pax_global_header00006660000000000000000000000064117421175500014515gustar00rootroot0000000000000052 comment=d39c428ad16a23955b1e2e9a9664d88c66dc4865 net-http-pipeline-1.0.1/000077500000000000000000000000001174211755000150625ustar00rootroot00000000000000net-http-pipeline-1.0.1/.autotest000066400000000000000000000001711174211755000167320ustar00rootroot00000000000000# -*- ruby -*- require 'autotest/restart' Autotest.add_hook :initialize do |at| at.testlib = 'minitest/autorun' end net-http-pipeline-1.0.1/.gitignore000066400000000000000000000000261174211755000170500ustar00rootroot00000000000000/TAGS /pkg /doc *.swp net-http-pipeline-1.0.1/.travis.yml000066400000000000000000000003701174211755000171730ustar00rootroot00000000000000--- after_script: - rake travis:after -t before_script: - gem install hoe-travis --no-rdoc --no-ri - rake travis:before -t language: ruby notifications: email: - drbrain@segment7.net rvm: - 1.8.7 - 1.9.2 - 1.9.3 - ruby-head script: rake travis net-http-pipeline-1.0.1/History.txt000066400000000000000000000013511174211755000172640ustar00rootroot00000000000000=== 1.0.1 / 2012-04-13 * Bug fixes * IOError is now handled in pipeline_check and pipeline_receive in case you pipeline to a closed socket. Partial patch by Eric Wong. Issue #2 === 1.0 / 2011-03-29 * API change * Net::HTTP::Pipeline#pipeline requires an Array of Net::HTTPRequests now. * Major enhancement * If a sequence of requests contains a non-idempotent request #pipeline now waits for a response to the previous request. * Minor enhancements * Check for HTTP/1.1 and persistent connections before attempting pipelining * Added Net::HTTP#persistent= to avoid pipelining-capability check. === 0.1 / 2010-12-07 * Minor enhancement * Add 1.8.7 support === 0.0 / 2010-12-07 * Major enhancement * Birthday! net-http-pipeline-1.0.1/Manifest.txt000066400000000000000000000002021174211755000173630ustar00rootroot00000000000000.autotest History.txt Manifest.txt README.txt Rakefile lib/net/http/pipeline.rb sample/pipeline.rb test/test_net_http_pipeline.rb net-http-pipeline-1.0.1/README.txt000066400000000000000000000034401174211755000165610ustar00rootroot00000000000000= net-http-pipeline * http://docs.seattlerb.org/net-http-pipeline * http://github.com/drbrain/net-http-pipeline == DESCRIPTION: An HTTP/1.1 pipelining implementation atop Net::HTTP. A pipelined connection sends multiple requests to the HTTP server without waiting for the responses. The server will respond in-order. == FEATURES/PROBLEMS: * Provides HTTP/1.1 pipelining == SYNOPSIS: require 'net/http/pipeline' Net::HTTP.start 'localhost' do |http| req1 = Net::HTTP::Get.new '/' req2 = Net::HTTP::Get.new '/' req3 = Net::HTTP::Get.new '/' http.pipeline [req1, req2, req3] do |res| puts res.code puts res.body[0..60].inspect puts end end == INSTALL: gem install net-http-pipeline == LICENSE: (The MIT License) Copyright (c) 2010 Eric Hodel Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. net-http-pipeline-1.0.1/Rakefile000066400000000000000000000006201174211755000165250ustar00rootroot00000000000000# -*- ruby -*- require 'rubygems' require 'hoe' Hoe.plugin :git Hoe.plugin :minitest Hoe.plugin :travis Hoe.spec 'net-http-pipeline' do developer 'Eric Hodel', 'drbrain@segment7.net' rdoc_locations << 'docs.seattlerb.org:/data/www/docs.seattlerb.org/net-http-pipeline/' rdoc_locations << 'rubyforge.org:/var/www/gforge-projects/seattlerb/net-http-pipeline/' end # vim: syntax=Ruby net-http-pipeline-1.0.1/lib/000077500000000000000000000000001174211755000156305ustar00rootroot00000000000000net-http-pipeline-1.0.1/lib/net/000077500000000000000000000000001174211755000164165ustar00rootroot00000000000000net-http-pipeline-1.0.1/lib/net/http/000077500000000000000000000000001174211755000173755ustar00rootroot00000000000000net-http-pipeline-1.0.1/lib/net/http/pipeline.rb000066400000000000000000000223011174211755000215250ustar00rootroot00000000000000require 'net/http' ## # An HTTP/1.1 pipelining implementation atop Net::HTTP. This library is # compliant with RFC 2616 8.1.2.2. # # Pipeline allows you to create a bunch of requests then send them all to an # HTTP/1.1 server without waiting for responses. The server will return HTTP # responses in-order. # # Net::HTTP::Pipeline does not assume the server supports pipelining. If you # know the server supports pipelining you can set Net::HTTP#pipelining to # true. # # = Example # # require 'net/http/pipeline' # # Net::HTTP.start 'localhost' do |http| # requests = [] # requests << Net::HTTP::Get.new('/') # requests << Net::HTTP::Get.new('/') # requests << Net::HTTP::Get.new('/') # # http.pipeline requests do |res| # puts res.code # puts res.body[0..60].inspect # puts # end # end module Net::HTTP::Pipeline ## # The version of net-http-pipeline you are using VERSION = '1.0.1' ## # Pipeline error class class Error < RuntimeError ## # Remaining requests that have not been sent to the HTTP server attr_reader :requests ## # Retrieved responses up to the error point attr_reader :responses ## # Creates a new Error with +message+, a list of +requests+ that have not # been sent to the server and a list of +responses+ that have been # retrieved from the server. def initialize message, requests, responses super message @requests = requests @responses = responses end end ## # Raised when an invalid version is given class VersionError < Error ## # Creates a new VersionError with a list of +requests+ that have not been # sent to the server and a list of +responses+ that have been retrieved # from the server. def initialize requests, responses super 'HTTP/1.1 or newer required', requests, responses end end ## # Raised when the server appears to not support persistent connections class PersistenceError < Error ## # Creates a new PersistenceError with a list of +requests+ that have not # been sent to the server and a list of +responses+ that have been # retrieved from the server. def initialize requests, responses super 'persistent connections required', requests, responses end end ## # Raised when the server appears to not support pipelining connections class PipelineError < Error ## # Creates a new PipelineError with a list of +requests+ that have not been # sent to the server and a list of +responses+ that have been retrieved # from the server. def initialize requests, responses super 'pipeline connections are not supported', requests, responses end end ## # Raised if an error occurs while reading responses. class ResponseError < Error ## # The original exception attr_accessor :original ## # Creates a new ResponseError with an original +exception+, a list of # +requests+ that were in-flight and a list of +responses+ that have been # retrieved from the server. def initialize exception, requests, responses @original = exception message = "error handling responses: #{original} (#{original.class})" super message, requests, responses end end ## # Pipelining capability accessor. # # Pipeline assumes servers do not support pipelining by default. The first # request is not pipelined while Pipeline ensures that the server is # HTTP/1.1 or newer and defaults to persistent connections. # # If you know the server is HTTP/1.1 and defaults to persistent # connections you can set this to true when you create the Net::HTTP object. attr_accessor :pipelining ## # Is +req+ idempotent according to RFC 2616? def idempotent? req case req when Net::HTTP::Delete, Net::HTTP::Get, Net::HTTP::Head, Net::HTTP::Options, Net::HTTP::Put, Net::HTTP::Trace then true end end ## # Pipelines +requests+ to the HTTP server yielding responses if a block is # given. Returns all responses recieved. # # The Net::HTTP connection must be started before calling #pipeline. # # Raises an exception if the connection is not pipeline-capable or if the # HTTP session has not been started. def pipeline requests, &block # :yields: response responses = [] raise Error.new('Net::HTTP not started', requests, responses) unless started? raise VersionError.new(requests, responses) if '1.1' > @curr_http_version pipeline_check requests, responses, &block retried = responses.length until requests.empty? do begin in_flight = pipeline_send requests pipeline_receive in_flight, responses, &block rescue Net::HTTP::Pipeline::ResponseError => e e.requests.reverse_each do |request| requests.unshift request end raise if responses.length == retried or not idempotent? requests.first retried = responses.length pipeline_reset requests, responses retry end end responses end ## # Ensures the connection supports pipelining. # # If the server has not been tested for pipelining support one of the # +requests+ will be consumed and placed in +responses+. # # A VersionError will be raised if the server is not HTTP/1.1 or newer. # # A PersistenceError will be raised if the server does not support # persistent connections. # # A PipelineError will be raised if the it was previously determined that # the server does not support pipelining. def pipeline_check requests, responses if instance_variable_defined? :@pipelining then return if @pipelining raise PipelineError.new(requests, responses) unless @pipelining else @pipelining = false end req = requests.shift retried = false begin res = request req rescue Timeout::Error, EOFError, Errno::ECONNABORTED, Errno::ECONNRESET, Errno::EPIPE, Net::HTTPBadResponse, IOError => e if retried then requests.unshift req raise ResponseError.new(e, requests, responses) end retried = true pipeline_reset requests, responses retry end responses << res yield res if block_given? @pipelining = pipeline_keep_alive? res if '1.1' > @curr_http_version then @pipelining = false raise VersionError.new(requests, responses) elsif not @pipelining then raise PersistenceError.new(requests, responses) end end ## # Updates the HTTP version and ensures the connection has keep-alive. def pipeline_end_transport res @curr_http_version = res.http_version if @socket.closed? then D 'Conn socket closed on pipeline' elsif pipeline_keep_alive? res then D 'Conn pipeline keep-alive' else D 'Conn close on pipeline' @socket.close end end ## # Closes the connection and rescues any IOErrors this may cause def pipeline_finish finish rescue IOError end if Net::HTTPResponse.allocate.respond_to? :connection_close? then ## # Checks for an connection close header def pipeline_keep_alive? res not res.connection_close? end else def pipeline_keep_alive? res not res['connection'].to_s =~ /close/i end end ## # Receives HTTP responses for +in_flight+ requests and adds them to # +responses+ def pipeline_receive in_flight, responses while req = in_flight.shift do begin begin res = Net::HTTPResponse.read_new @socket end while res.kind_of? Net::HTTPContinue res.reading_body @socket, req.response_body_permitted? do responses << res yield res if block_given? end pipeline_end_transport res rescue StandardError, Timeout::Error in_flight.unshift req raise end end responses rescue Timeout::Error, EOFError, Errno::ECONNABORTED, Errno::ECONNRESET, Errno::EPIPE, Net::HTTPBadResponse, IOError => e pipeline_finish raise ResponseError.new(e, in_flight, responses) end ## # Resets this connection def pipeline_reset requests, responses pipeline_finish start rescue Errno::ECONNREFUSED raise Error.new("connection refused: #{address}:#{port}", requests, responses) rescue Errno::EHOSTDOWN raise Error.new("host down: #{address}:#{port}", requests, responses) end ## # Sends +requests+ to the HTTP server and removes them from the +requests+ # list. Returns the requests that have been pipelined and are in-flight. # # If a non-idempotent request is first in +requests+ it will be sent and no # further requests will be pipelined. # # If a non-idempotent request is encountered after an idempotent request it # will not be sent. def pipeline_send requests in_flight = [] while req = requests.shift do idempotent = idempotent? req unless idempotent or in_flight.empty? then requests.unshift req break end begin_transport req req.exec @socket, @curr_http_version, edit_path(req.path) in_flight << req break unless idempotent end in_flight end end class Net::HTTP ## # Adds pipeline support to Net::HTTP include Pipeline end net-http-pipeline-1.0.1/sample/000077500000000000000000000000001174211755000163435ustar00rootroot00000000000000net-http-pipeline-1.0.1/sample/pipeline.rb000066400000000000000000000012271174211755000204770ustar00rootroot00000000000000require 'net/http/pipeline' http = Net::HTTP.new 'localhost' http.set_debug_output $stderr # so you can see what is happening # http.pipelining = true # set this when localhost:80 is an HTTP/1.1 server http.start do |http| reqs = [] reqs << Net::HTTP::Get.new('/?a') # this request will be non-pipelined # to check if localhost:80 is HTTP/1.1 # unless http.pipelining == true reqs << Net::HTTP::Get.new('/?b') # these requests will be pipelined reqs << Net::HTTP::Get.new('/?c') http.pipeline reqs do |res| puts res.code puts res.body[0..60].inspect puts end end net-http-pipeline-1.0.1/test/000077500000000000000000000000001174211755000160415ustar00rootroot00000000000000net-http-pipeline-1.0.1/test/test_net_http_pipeline.rb000066400000000000000000000362031174211755000231430ustar00rootroot00000000000000require 'minitest/autorun' require 'net/http/pipeline' require 'stringio' class TestNetHttpPipeline < MiniTest::Unit::TestCase include Net::HTTP::Pipeline def setup @curr_http_version = '1.1' @started = true @get1 = Net::HTTP::Get.new '/' @get2 = Net::HTTP::Get.new '/' @get3 = Net::HTTP::Get.new '/' @post = Net::HTTP::Post.new '/' remove_start def start end end def remove_start class << self alias_method :old_start, :start if method_defined? :start end end ## # Net::BufferedIO stub class Buffer attr_accessor :read_io, :write_io def initialize exception = nil, immediate = false @readline = immediate @exception = exception @read_io = StringIO.new @write_io = StringIO.new @closed = false end def close @closed = true end def closed? @closed end def finish @write_io.rewind end def read bytes, dest = '', ignored = nil @read_io.read bytes, dest dest end def readline raise @exception if @exception and @readline @readline = true @read_io.readline.chomp "\r\n" end def readuntil terminator, ignored @read_io.gets terminator end def start @read_io.rewind end def write data @write_io.write data end end attr_writer :started def D(*) end def begin_transport req end def edit_path path path end def finish @socket.close end def http_get get = [] get << 'GET / HTTP/1.1' get << 'Accept: */*' get << 'User-Agent: Ruby' if RUBY_VERSION > '1.9' get.push nil, nil get.join "\r\n" end def http_post get = [] get << 'POST / HTTP/1.1' get << 'Accept: */*' get << 'User-Agent: Ruby' if RUBY_VERSION > '1.9' get.push nil, nil get.join "\r\n" end def http_response body, *extra_header http_response = [] http_response << 'HTTP/1.1 200 OK' http_response << "Content-Length: #{body.bytesize}" http_response.push(*extra_header) http_response.push nil, nil # Array chomps on #join http_response.join("\r\n") << body end def http_bad_response http_response = [] http_response << 'HTP/1.1 200 OK' http_response << 'Content-Length: 0' http_response.push nil, nil # Array chomps on #join http_response.join("\r\n") end def request req req.exec @socket, @curr_http_version, edit_path(req.path) res = Net::HTTPResponse.read_new @socket res.reading_body @socket, req.response_body_permitted? do yield res if block_given? end @curr_http_version = res.http_version @socket.close unless pipeline_keep_alive? res res end def response r = Net::HTTPResponse.allocate def r.http_version() Net::HTTP::HTTPVersion end def r.read_body() true end r.instance_variable_set :@header, {} def r.header() @header end r end def started?() @started end # tests start def test_idempotent_eh http = Net::HTTP.new 'localhost' assert http.idempotent? Net::HTTP::Delete.new '/' assert http.idempotent? Net::HTTP::Get.new '/' assert http.idempotent? Net::HTTP::Head.new '/' assert http.idempotent? Net::HTTP::Options.new '/' assert http.idempotent? Net::HTTP::Put.new '/' assert http.idempotent? Net::HTTP::Trace.new '/' refute http.idempotent? Net::HTTP::Post.new '/' end def test_pipeline @socket = Buffer.new @socket.read_io.write http_response('Worked 1!') @socket.read_io.write http_response('Worked 2!') @socket.start requests = [@get1, @get2] responses = pipeline requests @socket.finish expected = http_get * 2 assert_equal expected, @socket.write_io.read refute @socket.closed? assert_equal 'Worked 1!', responses.first.body assert_equal 'Worked 2!', responses.last.body assert_empty requests end def test_pipeline_block @socket = Buffer.new @socket.read_io.write http_response('Worked 1!') @socket.read_io.write http_response('Worked 2!') @socket.start requests = [@get1, @get2] responses = [] pipeline requests do |response| responses << response end @socket.finish assert_equal 'Worked 1!', responses.first.body assert_equal 'Worked 2!', responses.last.body end def test_pipeline_http_1_0 @curr_http_version = '1.0' @socket = Buffer.new @socket.read_io.write http_response('Worked 1!', 'Connection: close') @socket.start e = assert_raises Net::HTTP::Pipeline::VersionError do pipeline [@get1, @get2] end assert_equal [@get1, @get2], e.requests assert_empty e.responses end def test_pipeline_non_idempotent @socket = Buffer.new @socket.read_io.write http_response('Worked 1!') @socket.read_io.write http_response('Worked 2!') @socket.read_io.write http_response('Worked 3!') @socket.read_io.write http_response('Worked 4!') @socket.start responses = pipeline [@get1, @get2, @post, @get3] @socket.finish expected = '' expected << http_get * 2 expected << http_post expected << http_get assert_equal expected, @socket.write_io.read refute @socket.closed? assert_equal 'Worked 1!', responses.shift.body assert_equal 'Worked 2!', responses.shift.body assert_equal 'Worked 3!', responses.shift.body assert_equal 'Worked 4!', responses.shift.body assert responses.empty? end def test_pipeline_not_started @started = false e = assert_raises Net::HTTP::Pipeline::Error do pipeline [] end assert_equal 'Net::HTTP not started', e.message end def test_pipeline_retry self.pipelining = true @error_socket = Buffer.new Errno::ECONNRESET @error_socket.read_io.write http_response('Worked 1!') @error_socket.start @good_socket = Buffer.new @good_socket.read_io.write http_response('Worked 2!') @good_socket.read_io.write http_response('Worked 3!') @good_socket.start @socket = @error_socket remove_start def start @socket = @good_socket end requests = [@get1, @get2, @get3] responses = pipeline requests @error_socket.finish @good_socket.finish assert_equal http_get * 3, @error_socket.write_io.read assert @error_socket.closed? assert_equal http_get * 2, @good_socket.write_io.read refute @good_socket.closed? assert_equal 'Worked 1!', responses.shift.body assert_equal 'Worked 2!', responses.shift.body assert_equal 'Worked 3!', responses.shift.body assert_empty responses assert_empty requests end def test_pipeline_retry_fail_post self.pipelining = true @socket = Buffer.new Errno::ECONNRESET, true @socket.start requests = [@post] e = assert_raises Net::HTTP::Pipeline::ResponseError do pipeline requests end @socket.finish assert_equal http_post, @socket.write_io.read assert_empty e.responses assert_equal [@post], e.requests end def test_pipeline_retry_fail_different self.pipelining = true @error_socket = Buffer.new Errno::ECONNRESET @error_socket.read_io.write http_response('Worked 1!') @error_socket.start @error_socket2 = Buffer.new Errno::ECONNRESET @error_socket2.read_io.write http_response('Worked 2!') @error_socket2.start @good_socket = Buffer.new @good_socket.read_io.write http_response('Worked 3!') @good_socket.start @socket = @error_socket @sockets = [@error_socket2, @good_socket] remove_start def start @socket = @sockets.shift end requests = [@get1, @get2, @get3] responses = pipeline requests @error_socket.finish @error_socket2.finish @good_socket.finish assert_equal http_get * 3, @error_socket.write_io.read assert @error_socket.closed? assert_equal http_get * 2, @error_socket2.write_io.read assert @error_socket2.closed? assert_equal http_get, @good_socket.write_io.read refute @good_socket.closed? assert_equal 'Worked 1!', responses.shift.body assert_equal 'Worked 2!', responses.shift.body assert_equal 'Worked 3!', responses.shift.body assert_empty responses assert_empty requests end def test_pipeline_retry_fail_same self.pipelining = true @error_socket = Buffer.new Errno::ECONNRESET @error_socket.read_io.write http_response('Worked 1!') @error_socket.start @error_socket2 = Buffer.new Errno::ECONNRESET, true @error_socket2.start @socket = @error_socket remove_start def start @socket = @error_socket2 end requests = [@get1, @get2, @get3] e = assert_raises Net::HTTP::Pipeline::ResponseError do pipeline requests end @error_socket.finish @error_socket2.finish assert_equal http_get * 3, @error_socket.write_io.read assert @error_socket.closed? assert_equal http_get * 2, @error_socket2.write_io.read assert @error_socket2.closed? responses = e.responses assert_equal 'Worked 1!', responses.shift.body assert_empty responses assert_equal [@get2, @get3], e.requests end # end #pipeline tests def test_pipeline_check @socket = Buffer.new @socket.read_io.write http_response('Worked 1!') @socket.start requests = [@get1, @get2] responses = [] pipeline_check requests, responses assert_equal [@get2], requests assert_equal 1, responses.length assert_equal 'Worked 1!', responses.first.body assert pipelining end def test_pipeline_check_again self.pipelining = false @socket = Buffer.new @socket.start e = assert_raises Net::HTTP::Pipeline::PipelineError do pipeline_check [@get1, @get2], [] end assert_equal [@get1, @get2], e.requests assert_empty e.responses refute pipelining end def test_pipeline_check_bad_response @socket = Buffer.new @socket.read_io.write http_bad_response @socket.start @socket2 = Buffer.new @socket2.read_io.write http_response('Worked 1!') @socket2.start remove_start def start @socket = @socket2 end requests = [@get1, @get2] responses = [] pipeline_check requests, responses assert_equal [@get2], requests assert_equal 1, responses.length assert_equal 'Worked 1!', responses.first.body assert pipelining end def test_pipeline_check_http_1_0 @socket = Buffer.new @socket.read_io.write <<-HTTP_1_0 HTTP/1.0 200 OK\r Content-Length: 9\r \r Worked 1! HTTP_1_0 @socket.start e = assert_raises Net::HTTP::Pipeline::VersionError do pipeline_check [@get1, @get2], [] end assert_equal [@get2], e.requests assert_equal 1, e.responses.length assert_equal 'Worked 1!', e.responses.first.body refute pipelining end def test_pipeline_check_ioerror @socket = Buffer.new IOError, true @socket.read_io.write http_response('Worked 1!') @socket.start requests = [@get1, @get2] responses = [] assert_raises Net::HTTP::Pipeline::ResponseError do pipeline_check requests, responses end assert_equal [@get1, @get2], requests assert_equal 0, responses.length refute pipelining end def test_pipeline_check_non_persistent @socket = Buffer.new @socket.read_io.write http_response('Worked 1!', 'Connection: close') @socket.start e = assert_raises Net::HTTP::Pipeline::PersistenceError do pipeline_check [@get1, @get2], [] end assert_equal [@get2], e.requests assert_equal 1, e.responses.length assert_equal 'Worked 1!', e.responses.first.body refute pipelining end def test_pipeline_check_pipelining self.pipelining = true @socket = Buffer.new @socket.start requests = [@get1, @get2] responses = [] pipeline_check requests, responses assert_equal [@get1, @get2], requests assert_empty responses assert pipelining end def test_pipeline_end_transport @curr_http_version = nil res = response @socket = StringIO.new pipeline_end_transport res refute @socket.closed? assert_equal '1.1', @curr_http_version end def test_pipeline_end_transport_no_keep_alive @curr_http_version = nil res = response res.header['connection'] = ['close'] @socket = StringIO.new pipeline_end_transport res assert @socket.closed? assert_equal '1.1', @curr_http_version end def test_pipeline_keep_alive_eh res = response assert pipeline_keep_alive? res res.header['connection'] = ['close'] refute pipeline_keep_alive? res end def test_pipeline_receive @socket = Buffer.new @socket.read_io.write http_response('Worked 1!') @socket.read_io.write http_response('Worked 2!') @socket.start in_flight = [@get1, @get2] responses = [] r = pipeline_receive in_flight, responses @socket.finish refute @socket.closed? assert_equal 'Worked 1!', responses.first.body assert_equal 'Worked 2!', responses.last.body assert_same r, responses end def test_pipeline_receive_bad_response @socket = Buffer.new Errno::ECONNRESET @socket.read_io.write http_response('Worked 1!') @socket.start in_flight = [@get1, @get2] responses = [] e = assert_raises Net::HTTP::Pipeline::ResponseError do pipeline_receive in_flight, responses end @socket.finish assert @socket.closed? assert_equal [@get2], e.requests assert_equal 1, e.responses.length assert_equal 'Worked 1!', e.responses.first.body assert_kind_of Errno::ECONNRESET, e.original end def test_pipeline_receive_ioerror @socket = Buffer.new IOError @socket.read_io.write http_response('Worked 1!') @socket.start in_flight = [@get1, @get2] responses = [] e = assert_raises Net::HTTP::Pipeline::ResponseError do pipeline_receive in_flight, responses end @socket.finish assert @socket.closed? assert_equal [@get2], e.requests assert_equal 1, e.responses.length assert_equal 'Worked 1!', e.responses.first.body assert_kind_of IOError, e.original end def test_pipeline_receive_timeout @socket = Buffer.new Timeout::Error @socket.read_io.write http_response('Worked 1!') @socket.start in_flight = [@get1, @get2] responses = [] e = assert_raises Net::HTTP::Pipeline::ResponseError do pipeline_receive in_flight, responses end @socket.finish assert @socket.closed? assert_equal [@get2], e.requests assert_equal 1, e.responses.length assert_equal 'Worked 1!', e.responses.first.body assert_kind_of Timeout::Error, e.original end def test_pipeline_send @socket = Buffer.new @socket.start requests = [@get1, @get2, @post, @get3] in_flight = pipeline_send requests @socket.finish assert_equal [@get1, @get2], in_flight assert_equal [@post, @get3], requests expected = '' expected << http_get * 2 assert_equal expected, @socket.write_io.read end def test_pipeline_send_non_idempotent @socket = Buffer.new @socket.start requests = [@post, @get3] in_flight = pipeline_send requests @socket.finish assert_equal [@post], in_flight assert_equal [@get3], requests expected = http_post assert_equal expected, @socket.write_io.read end end