Skip to content

Commit

Permalink
Merge pull request #991 from rzane/bufferedio
Browse files Browse the repository at this point in the history
Avoid replacing Net::BufferedIO
  • Loading branch information
bblimke committed Aug 17, 2022
2 parents d1de530 + 93d6c20 commit 102dd56
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 74 deletions.
66 changes: 0 additions & 66 deletions lib/webmock/http_lib_adapters/net_http.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,19 @@ class NetHttpAdapter < HttpLibAdapter
adapter_for :net_http

OriginalNetHTTP = Net::HTTP unless const_defined?(:OriginalNetHTTP)
OriginalNetBufferedIO = Net::BufferedIO unless const_defined?(:OriginalNetBufferedIO)

def self.enable!
Net.send(:remove_const, :BufferedIO)
Net.send(:remove_const, :HTTP)
Net.send(:remove_const, :HTTPSession)
Net.send(:const_set, :HTTP, @webMockNetHTTP)
Net.send(:const_set, :HTTPSession, @webMockNetHTTP)
Net.send(:const_set, :BufferedIO, Net::WebMockNetBufferedIO)
end

def self.disable!
Net.send(:remove_const, :BufferedIO)
Net.send(:remove_const, :HTTP)
Net.send(:remove_const, :HTTPSession)
Net.send(:const_set, :HTTP, OriginalNetHTTP)
Net.send(:const_set, :HTTPSession, OriginalNetHTTP)
Net.send(:const_set, :BufferedIO, OriginalNetBufferedIO)

#copy all constants from @webMockNetHTTP to original Net::HTTP
#in case any constants were added to @webMockNetHTTP instead of Net::HTTP
Expand Down Expand Up @@ -208,19 +203,6 @@ def check_right_http_connection
end
end

# patch for StringIO behavior in Ruby 2.2.3
# https://github.com/bblimke/webmock/issues/558
class PatchedStringIO < StringIO #:nodoc:

alias_method :orig_read_nonblock, :read_nonblock

def read_nonblock(size, *args, **kwargs)
args.reject! {|arg| !arg.is_a?(Hash)}
orig_read_nonblock(size, *args, **kwargs)
end

end

class StubSocket #:nodoc:

attr_accessor :read_timeout, :continue_timeout, :write_timeout
Expand Down Expand Up @@ -254,54 +236,6 @@ def cipher; ["TLS_AES_128_GCM_SHA256", "TLSv1.3", 128, 128] end
end
end

module Net #:nodoc: all

class WebMockNetBufferedIO < BufferedIO
def initialize(io, *args, **kwargs)
io = case io
when Socket, OpenSSL::SSL::SSLSocket, IO
io
when StringIO
PatchedStringIO.new(io.string)
when String
PatchedStringIO.new(io)
end
raise "Unable to create local socket" unless io

# Prior to 2.4.0 `BufferedIO` only takes a single argument (`io`) with no
# options. Here we pass through our full set of arguments only if we're
# on 2.4.0 or later, and use a simplified invocation otherwise.
if RUBY_VERSION >= '2.4.0'
super
else
super(io)
end
end

if RUBY_VERSION >= '2.6.0'
# https://github.com/ruby/ruby/blob/7d02441f0d6e5c9d0a73a024519eba4f69e36dce/lib/net/protocol.rb#L208
# Modified version of method from ruby, so that nil is always passed into orig_read_nonblock to avoid timeout
def rbuf_fill
case rv = @io.read_nonblock(BUFSIZE, nil, exception: false)
when String
return if rv.nil?
@rbuf << rv
rv.clear
return
when :wait_readable
@io.to_io.wait_readable(@read_timeout) or raise Net::ReadTimeout
when :wait_writable
@io.to_io.wait_writable(@read_timeout) or raise Net::ReadTimeout
when nil
raise EOFError, 'end of file reached'
end while true
end
end
end

end


module WebMock
module NetHTTPUtility

Expand Down
16 changes: 8 additions & 8 deletions lib/webmock/response.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,11 @@ def self.response_for(options)

class Response
def initialize(options = {})
if options.is_a?(IO) || options.is_a?(String)
case options
when IO, StringIO
self.options = read_raw_response(options)
when String
self.options = read_raw_response(StringIO.new(options))
else
self.options = options
end
Expand Down Expand Up @@ -120,13 +123,8 @@ def assert_valid_body!
end
end

def read_raw_response(raw_response)
if raw_response.is_a?(IO)
string = raw_response.read
raw_response.close
raw_response = string
end
socket = ::Net::BufferedIO.new(raw_response)
def read_raw_response(io)
socket = ::Net::BufferedIO.new(io)
response = ::Net::HTTPResponse.read_new(socket)
transfer_encoding = response.delete('transfer-encoding') #chunks were already read by curl
response.reading_body(socket, true) {}
Expand All @@ -138,6 +136,8 @@ def read_raw_response(raw_response)
options[:body] = response.read_body
options[:status] = [response.code.to_i, response.message]
options
ensure
socket.close
end

InvalidBody = Class.new(StandardError)
Expand Down
28 changes: 28 additions & 0 deletions spec/unit/response_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,34 @@
end

describe "from raw response" do
describe "when input is a StringIO" do
before(:each) do
@io = StringIO.new(File.read(CURL_EXAMPLE_OUTPUT_PATH))
@response = WebMock::Response.new(@io)
end

it "should read status" do
expect(@response.status).to eq([202, "OK"])
end

it "should read headers" do
expect(@response.headers).to eq(
"Date"=>"Sat, 23 Jan 2010 01:01:05 GMT",
"Content-Type"=>"text/html; charset=UTF-8",
"Content-Length"=>"419",
"Connection"=>"Keep-Alive",
"Accept"=>"image/jpeg, image/png"
)
end

it "should read body" do
expect(@response.body.size).to eq(419)
end

it "should close IO" do
expect(@io).to be_closed
end
end

describe "when input is IO" do
before(:each) do
Expand Down

0 comments on commit 102dd56

Please sign in to comment.