From 1c040cad61e057860fa1ae31238dfaf5a1050150 Mon Sep 17 00:00:00 2001 From: Jeremy Evans Date: Mon, 1 Mar 2021 12:59:21 -0800 Subject: [PATCH] Add ignore_eof access to HTTP and HTTPResponse The ignore_eof setting on HTTPResponse makes it so an EOFError is raised when reading bodies with a defined Content-Length, if the body read was truncated due to the socket be closed. The ignore_eof setting on HTTP sets the values used in responses that are created by the object. For backwards compatibility, the default is for both settings is true. However, unless you are specifically tested for and handling truncated responses, it's a good idea to set ignore_eof to false so that errors are raised for truncated responses, instead of those errors silently being ignored. Fixes [Bug #14972] --- lib/net/http.rb | 6 ++++++ lib/net/http/response.rb | 7 ++++++- test/net/http/test_http.rb | 30 ++++++++++++++++++++++++++++++ 3 files changed, 42 insertions(+), 1 deletion(-) diff --git a/lib/net/http.rb b/lib/net/http.rb index 16582f10..db8e2dc7 100644 --- a/lib/net/http.rb +++ b/lib/net/http.rb @@ -699,6 +699,7 @@ def initialize(address, port = nil) @max_retries = 1 @debug_output = nil @response_body_encoding = false + @ignore_eof = true @proxy_from_env = false @proxy_uri = nil @@ -839,6 +840,10 @@ def continue_timeout=(sec) # The default value is 2 seconds. attr_accessor :keep_alive_timeout + # Whether to ignore EOF when reading response bodies with defined + # Content-Length headers. For backwards compatibility, the default is true. + attr_accessor :ignore_eof + # Returns true if the HTTP session has been started. def started? @started @@ -1606,6 +1611,7 @@ def transport_request(req) res = HTTPResponse.read_new(@socket) res.decode_content = req.decode_content res.body_encoding = @response_body_encoding + res.ignore_eof = @ignore_eof end while res.kind_of?(HTTPInformation) res.uri = req.uri diff --git a/lib/net/http/response.rb b/lib/net/http/response.rb index 9cedbdbb..f8b522f1 100644 --- a/lib/net/http/response.rb +++ b/lib/net/http/response.rb @@ -85,6 +85,7 @@ def initialize(httpv, code, msg) #:nodoc: internal use only @uri = nil @decode_content = false @body_encoding = false + @ignore_eof = true end # The HTTP version supported by the server. @@ -119,6 +120,10 @@ def body_encoding=(value) @body_encoding = value end + # Whether to ignore EOF when reading bodies with a specified Content-Length + # header. + attr_accessor :ignore_eof + def inspect "#<#{self.class} #{@code} #{@message} readbody=#{@read}>" end @@ -459,7 +464,7 @@ def read_body_0(dest) clen = content_length() if clen - @socket.read clen, dest, true # ignore EOF + @socket.read clen, dest, @ignore_eof return end clen = range_length() diff --git a/test/net/http/test_http.rb b/test/net/http/test_http.rb index 4725a791..e9471273 100644 --- a/test/net/http/test_http.rb +++ b/test/net/http/test_http.rb @@ -1348,3 +1348,33 @@ def test_response_body_encoding_encoding_without_content_type assert_equal(Encoding::UTF_8, res.body.encoding) end end + +class TestNetHTTPPartialResponse < Test::Unit::TestCase + CONFIG = { + 'host' => '127.0.0.1', + 'proxy_host' => nil, + 'proxy_port' => nil, + } + + include TestNetHTTPUtils + + def test_partial_response + str = "0123456789" + @server.mount_proc('/') do |req, res| + res.status = 200 + res['Content-Type'] = 'text/plain' + + res.body = str + res['Content-Length'] = str.length + 1 + end + @server.mount_proc('/show_ip') { |req, res| res.body = req.remote_ip } + + http = Net::HTTP.new(config('host'), config('port')) + res = http.get('/') + assert_equal(str, res.body) + + http = Net::HTTP.new(config('host'), config('port')) + http.ignore_eof = false + assert_raise(EOFError) {http.get('/')} + end +end