webrick/httprequest: use InputBufferSize for chunked requests

While WEBrick::HTTPRequest#body provides a Proc interface
for streaming large request bodies, clients must not force
the server to use an excessively large chunk size.

* lib/webrick/httprequest.rb (read_chunk_size): limit each
  read and block.call to :InputBufferSize in config.
* test/webrick/test_httpserver.rb (test_big_chunks): new test

git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@62963 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
normal 2018-03-28 08:06:39 +00:00
parent 6db6eb572b
commit 2d41d88c4d
2 changed files with 42 additions and 5 deletions

View File

@ -509,12 +509,16 @@ module WEBrick
def read_chunked(socket, block)
chunk_size, = read_chunk_size(socket)
while chunk_size > 0
data = read_data(socket, chunk_size) # read chunk-data
if data.nil? || data.bytesize != chunk_size
raise HTTPStatus::BadRequest, "bad chunk data size."
end
begin
sz = [ chunk_size, @buffer_size ].min
data = read_data(socket, sz) # read chunk-data
if data.nil? || data.bytesize != sz
raise HTTPStatus::BadRequest, "bad chunk data size."
end
block.call(data)
end while (chunk_size -= sz) > 0
read_line(socket) # skip CRLF
block.call(data)
chunk_size, = read_chunk_size(socket)
end
read_header(socket) # trailer + CRLF

View File

@ -475,4 +475,37 @@ class TestWEBrickHTTPServer < Test::Unit::TestCase
end
}
end
def test_big_chunks
nr_out = 3
buf = 'big' # 3 bytes is bigger than 2!
config = { :InputBufferSize => 2 }.freeze
total = 0
all = ''
TestWEBrick.start_httpserver(config){|server, addr, port, log|
server.mount_proc('/', ->(req, res) {
err = []
ret = req.body do |chunk|
n = chunk.bytesize
n > config[:InputBufferSize] and err << "#{n} > :InputBufferSize"
total += n
all << chunk
end
ret.nil? or err << 'req.body should return nil'
(buf * nr_out) == all or err << 'input body does not match expected'
res.header['connection'] = 'close'
res.body = err.join("\n")
})
TCPSocket.open(addr, port) do |c|
c.write("POST / HTTP/1.1\r\nHost: example.com\r\n" \
"Transfer-Encoding: chunked\r\n\r\n")
chunk = "#{buf.bytesize.to_s(16)}\r\n#{buf}\r\n"
nr_out.times { c.write(chunk) }
c.write("0\r\n\r\n")
head, body = c.read.split("\r\n\r\n")
assert_match %r{\AHTTP/1\.1 200 OK}, head
assert_nil body
end
}
end
end