lib/httpclient.rb in httpclient-2.5.3.3 vs lib/httpclient.rb in httpclient-2.6.0

- old
+ new

@@ -306,11 +306,11 @@ end end # HTTPClient::SSLConfig:: SSL configurator. attr_reader :ssl_config - # WebAgent::CookieManager:: Cookies configurator. + # HTTPClient::CookieManager:: Cookies configurator. attr_accessor :cookie_manager # An array of response HTTP message body String which is used for loop-back # test. See test/* to see how to use it. If you want to do loop-back test # of HTTP header, use test_loopback_http_response instead. attr_reader :test_loopback_response @@ -413,11 +413,11 @@ @test_loopback_response = [] @session_manager = SessionManager.new(self) @session_manager.agent_name = agent_name || DEFAULT_AGENT_NAME @session_manager.from = from @session_manager.ssl_config = @ssl_config = SSLConfig.new(self) - @cookie_manager = WebAgent::CookieManager.new + @cookie_manager = CookieManager.new @follow_redirect_count = 10 load_environment self.proxy = proxy if proxy keep_webmock_compat end @@ -574,11 +574,10 @@ reset_all end # Try to save Cookies to the file specified in set_cookie_store. Unexpected # error will be raised if you don't call set_cookie_store first. - # (interface mismatch between WebAgent::CookieManager implementation) def save_cookie_store @cookie_manager.save_cookies end # Returns stored cookies. @@ -925,10 +924,15 @@ end private class RetryableResponse < StandardError # :nodoc: + attr_reader :res + + def initialize(res = nil) + @res = res + end end class KeepAliveDisconnected < StandardError # :nodoc: attr_reader :sess attr_reader :cause @@ -945,28 +949,41 @@ # it has any one of the key key.all? { |e| args[0].key?(e) } end def do_request(method, uri, query, body, header, &block) - conn = Connection.new res = nil if HTTP::Message.file?(body) pos = body.pos rescue nil end retry_count = @session_manager.protocol_retry_count proxy = no_proxy?(uri) ? nil : @proxy + previous_request = previous_response = nil while retry_count > 0 body.pos = pos if pos req = create_request(method, uri, query, body, header) + if previous_request + # to remember IO positions to read + req.http_body.positions = previous_request.http_body.positions + end begin protect_keep_alive_disconnected do - do_get_block(req, proxy, conn, &block) + # TODO: remove Connection.new + # We want to delete Connection usage in do_get_block but Newrelic gem depends on it. + # https://github.com/newrelic/rpm/blob/master/lib/new_relic/agent/instrumentation/httpclient.rb#L34-L36 + conn = Connection.new + res = do_get_block(req, proxy, conn, &block) + # Webmock's do_get_block returns ConditionVariable + if !res.respond_to?(:previous) + res = conn.pop + end end - res = conn.pop + res.previous = previous_response break - rescue RetryableResponse - res = conn.pop + rescue RetryableResponse => e + previous_request = req + previous_response = res = e.res retry_count -= 1 end end res end @@ -1027,19 +1044,25 @@ end if HTTP::Message.file?(body) pos = body.pos rescue nil end retry_number = 0 + previous = nil + request_query = query while retry_number < @follow_redirect_count body.pos = pos if pos - res = do_request(method, uri, query, body, header, &filtered_block) + res = do_request(method, uri, request_query, body, header, &filtered_block) + res.previous = previous if res.redirect? if res.header['location'].empty? raise BadResponseError.new("Missing Location header for redirect", res) end method = :get if res.see_other? # See RFC2616 10.3.4 uri = urify(@redirect_uri_callback.call(uri, res)) + # To avoid duped query parameter. 'location' must include query part. + request_query = nil + previous = res retry_number += 1 else return res end end @@ -1056,14 +1079,17 @@ def protect_keep_alive_disconnected begin yield rescue KeepAliveDisconnected => e - if e.sess - @session_manager.invalidate(e.sess.dest) + # Force to create new connection + Thread.current[:HTTPClient_AcquireNewConnection] = true + begin + yield + ensure + Thread.current[:HTTPClient_AcquireNewConnection] = false end - yield end end def create_request(method, uri, query, body, header) method = method.to_s.upcase @@ -1099,12 +1125,15 @@ end req = HTTP::Message.new_request(method, uri, query, body, boundary) header.each do |key, value| req.header.add(key.to_s, value) end - if @cookie_manager && cookie = @cookie_manager.find(uri) - req.header.add('Cookie', cookie) + if @cookie_manager + cookie_value = @cookie_manager.cookie_value(uri) + if cookie_value + req.header.add('Cookie', cookie_value) + end end req end def create_boundary @@ -1150,12 +1179,13 @@ @request_filter.each do |filter| filter.filter_request(req) end if str = @test_loopback_response.shift dump_dummy_request_response(req.http_body.dump, str) if @debug_dev - conn.push(HTTP::Message.new_response(str, req.header)) - return + res = HTTP::Message.new_response(str, req.header) + conn.push(res) + return res end content = block ? nil : '' res = HTTP::Message.new_response(content, req.header) @debug_dev << "= Request\n\n" if @debug_dev sess = @session_manager.query(req, proxy) @@ -1176,12 +1206,13 @@ @session_manager.keep(sess) unless sess.closed? commands = @request_filter.collect { |filter| filter.filter_response(req, res) } if commands.find { |command| command == :retry } - raise RetryableResponse.new + raise RetryableResponse.new(res) end + res end def do_get_stream(req, proxy, conn) @request_filter.each do |filter| filter.filter_request(req) @@ -1207,9 +1238,10 @@ @session_manager.keep(sess) unless sess.closed? _ = @request_filter.collect { |filter| filter.filter_response(req, res) } # ignore commands (not retryable in async mode) + res end def do_get_header(req, res, sess) res.http_version, res.status, res.reason, headers = sess.get_header res.header.set_headers(headers)