mirror of
https://github.com/ruby/ruby.git
synced 2025-09-16 17:14:01 +02:00

webrick: use IO.copy_stream for multipart response Use the new Proc response body feature to generate a multipart range response dynamically. We use a flat array to minimize object overhead as much as possible; as many ranges may fit into an HTTP request header. * lib/webrick/httpservlet/filehandler.rb (multipart_body): new method (make_partial_content): use multipart_body ------------------------------------------------------------------------ r62960 | normal | 2018-03-28 17:06:23 +0900 (水, 28 3 2018) | 13 lines webrick/httprequest: limit request headers size We use the same 112 KB limit started (AFAIK) by Mongrel, Thin, and Puma to prevent malicious users from using up all the memory with a single request. This also limits the damage done by excessive ranges in multipart Range: requests. Due to the way we rely on IO#gets and the desire to keep the code simple, the actual maximum header may be 4093 bytes larger than 112 KB, but we're splitting hairs at that point. * lib/webrick/httprequest.rb: define MAX_HEADER_LENGTH (read_header): raise when headers exceed max length ------------------------------------------------------------------------ r62961 | normal | 2018-03-28 17:06:28 +0900 (水, 28 3 2018) | 9 lines webrick/httpservlet/cgihandler: reduce memory use WEBrick::HTTPRequest#body can be passed a block to process the body in chunks. Use this feature to avoid building a giant string in memory. * lib/webrick/httpservlet/cgihandler.rb (do_GET): avoid reading entire request body into memory (do_POST is aliased to do_GET, so it handles bodies) ------------------------------------------------------------------------ r62962 | normal | 2018-03-28 17:06:34 +0900 (水, 28 3 2018) | 7 lines webrick/httprequest: raise correct exception "BadRequest" alone does not resolve correctly, it is in the HTTPStatus namespace. * lib/webrick/httprequest.rb (read_chunked): use correct exception * test/webrick/test_httpserver.rb (test_eof_in_chunk): new test ------------------------------------------------------------------------ r62963 | normal | 2018-03-28 17:06:39 +0900 (水, 28 3 2018) | 9 lines 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 ------------------------------------------------------------------------ r62964 | normal | 2018-03-28 17:06:44 +0900 (水, 28 3 2018) | 9 lines webrick: add test for Digest auth-int No changes to the actual code, this is a new test for a feature for which no tests existed. I don't understand the Digest authentication code well at all, but this is necessary for the subsequent change. * test/webrick/test_httpauth.rb (test_digest_auth_int): new test (credentials_for_request): support bodies with POST ------------------------------------------------------------------------ r62965 | normal | 2018-03-28 17:06:49 +0900 (水, 28 3 2018) | 18 lines webrick/httpauth/digestauth: stream req.body WARNING! WARNING! WARNING! LIKELY BROKEN CHANGE Pass a proc to WEBrick::HTTPRequest#body to avoid reading a potentially large request body into memory during authentication. WARNING! this will break apps completely which want to do something with the body besides calculating the MD5 digest of it. Also, keep in mind that probably nobody uses "auth-int". Servers such as Apache, lighttpd, nginx don't seem to support it; nor does curl when using POST/PUT bodies; and we didn't have tests for it until now... * lib/webrick/httpauth/digestauth.rb (_authenticate): stream req.body git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/branches/ruby_2_2@63021 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
479 lines
18 KiB
Ruby
479 lines
18 KiB
Ruby
require "test/unit"
|
|
require "net/http"
|
|
require "webrick"
|
|
require_relative "utils"
|
|
|
|
class TestWEBrickHTTPServer < Test::Unit::TestCase
|
|
empty_log = Object.new
|
|
def empty_log.<<(str)
|
|
assert_equal('', str)
|
|
self
|
|
end
|
|
NoLog = WEBrick::Log.new(empty_log, WEBrick::BasicLog::WARN)
|
|
|
|
def test_mount
|
|
httpd = WEBrick::HTTPServer.new(
|
|
:Logger => NoLog,
|
|
:DoNotListen=>true
|
|
)
|
|
httpd.mount("/", :Root)
|
|
httpd.mount("/foo", :Foo)
|
|
httpd.mount("/foo/bar", :Bar, :bar1)
|
|
httpd.mount("/foo/bar/baz", :Baz, :baz1, :baz2)
|
|
|
|
serv, opts, script_name, path_info = httpd.search_servlet("/")
|
|
assert_equal(:Root, serv)
|
|
assert_equal([], opts)
|
|
assert_equal("", script_name)
|
|
assert_equal("/", path_info)
|
|
|
|
serv, opts, script_name, path_info = httpd.search_servlet("/sub")
|
|
assert_equal(:Root, serv)
|
|
assert_equal([], opts)
|
|
assert_equal("", script_name)
|
|
assert_equal("/sub", path_info)
|
|
|
|
serv, opts, script_name, path_info = httpd.search_servlet("/sub/")
|
|
assert_equal(:Root, serv)
|
|
assert_equal([], opts)
|
|
assert_equal("", script_name)
|
|
assert_equal("/sub/", path_info)
|
|
|
|
serv, opts, script_name, path_info = httpd.search_servlet("/foo")
|
|
assert_equal(:Foo, serv)
|
|
assert_equal([], opts)
|
|
assert_equal("/foo", script_name)
|
|
assert_equal("", path_info)
|
|
|
|
serv, opts, script_name, path_info = httpd.search_servlet("/foo/")
|
|
assert_equal(:Foo, serv)
|
|
assert_equal([], opts)
|
|
assert_equal("/foo", script_name)
|
|
assert_equal("/", path_info)
|
|
|
|
serv, opts, script_name, path_info = httpd.search_servlet("/foo/sub")
|
|
assert_equal(:Foo, serv)
|
|
assert_equal([], opts)
|
|
assert_equal("/foo", script_name)
|
|
assert_equal("/sub", path_info)
|
|
|
|
serv, opts, script_name, path_info = httpd.search_servlet("/foo/bar")
|
|
assert_equal(:Bar, serv)
|
|
assert_equal([:bar1], opts)
|
|
assert_equal("/foo/bar", script_name)
|
|
assert_equal("", path_info)
|
|
|
|
serv, opts, script_name, path_info = httpd.search_servlet("/foo/bar/baz")
|
|
assert_equal(:Baz, serv)
|
|
assert_equal([:baz1, :baz2], opts)
|
|
assert_equal("/foo/bar/baz", script_name)
|
|
assert_equal("", path_info)
|
|
end
|
|
|
|
class Req
|
|
attr_reader :port, :host
|
|
def initialize(addr, port, host)
|
|
@addr, @port, @host = addr, port, host
|
|
end
|
|
def addr
|
|
[0,0,0,@addr]
|
|
end
|
|
end
|
|
|
|
def httpd(addr, port, host, ali)
|
|
config ={
|
|
:Logger => NoLog,
|
|
:DoNotListen => true,
|
|
:BindAddress => addr,
|
|
:Port => port,
|
|
:ServerName => host,
|
|
:ServerAlias => ali,
|
|
}
|
|
return WEBrick::HTTPServer.new(config)
|
|
end
|
|
|
|
def assert_eql?(v1, v2)
|
|
assert_equal(v1.object_id, v2.object_id)
|
|
end
|
|
|
|
def test_lookup_server
|
|
addr1 = "192.168.100.1"
|
|
addr2 = "192.168.100.2"
|
|
addrz = "192.168.100.254"
|
|
local = "127.0.0.1"
|
|
port1 = 80
|
|
port2 = 8080
|
|
port3 = 10080
|
|
portz = 32767
|
|
name1 = "www.example.com"
|
|
name2 = "www2.example.com"
|
|
name3 = "www3.example.com"
|
|
namea = "www.example.co.jp"
|
|
nameb = "www.example.jp"
|
|
namec = "www2.example.co.jp"
|
|
named = "www2.example.jp"
|
|
namez = "foobar.example.com"
|
|
alias1 = [namea, nameb]
|
|
alias2 = [namec, named]
|
|
|
|
host1 = httpd(nil, port1, name1, nil)
|
|
hosts = [
|
|
host2 = httpd(addr1, port1, name1, nil),
|
|
host3 = httpd(addr1, port1, name2, alias1),
|
|
host4 = httpd(addr1, port2, name1, nil),
|
|
host5 = httpd(addr1, port2, name2, alias1),
|
|
httpd(addr1, port2, name3, alias2),
|
|
host7 = httpd(addr2, nil, name1, nil),
|
|
host8 = httpd(addr2, nil, name2, alias1),
|
|
httpd(addr2, nil, name3, alias2),
|
|
host10 = httpd(local, nil, nil, nil),
|
|
host11 = httpd(nil, port3, nil, nil),
|
|
].sort_by{ rand }
|
|
hosts.each{|h| host1.virtual_host(h) }
|
|
|
|
# connect to addr1
|
|
assert_eql?(host2, host1.lookup_server(Req.new(addr1, port1, name1)))
|
|
assert_eql?(host3, host1.lookup_server(Req.new(addr1, port1, name2)))
|
|
assert_eql?(host3, host1.lookup_server(Req.new(addr1, port1, namea)))
|
|
assert_eql?(host3, host1.lookup_server(Req.new(addr1, port1, nameb)))
|
|
assert_eql?(nil, host1.lookup_server(Req.new(addr1, port1, namez)))
|
|
assert_eql?(host4, host1.lookup_server(Req.new(addr1, port2, name1)))
|
|
assert_eql?(host5, host1.lookup_server(Req.new(addr1, port2, name2)))
|
|
assert_eql?(host5, host1.lookup_server(Req.new(addr1, port2, namea)))
|
|
assert_eql?(host5, host1.lookup_server(Req.new(addr1, port2, nameb)))
|
|
assert_eql?(nil, host1.lookup_server(Req.new(addr1, port2, namez)))
|
|
assert_eql?(host11, host1.lookup_server(Req.new(addr1, port3, name1)))
|
|
assert_eql?(host11, host1.lookup_server(Req.new(addr1, port3, name2)))
|
|
assert_eql?(host11, host1.lookup_server(Req.new(addr1, port3, namea)))
|
|
assert_eql?(host11, host1.lookup_server(Req.new(addr1, port3, nameb)))
|
|
assert_eql?(host11, host1.lookup_server(Req.new(addr1, port3, namez)))
|
|
assert_eql?(nil, host1.lookup_server(Req.new(addr1, portz, name1)))
|
|
assert_eql?(nil, host1.lookup_server(Req.new(addr1, portz, name2)))
|
|
assert_eql?(nil, host1.lookup_server(Req.new(addr1, portz, namea)))
|
|
assert_eql?(nil, host1.lookup_server(Req.new(addr1, portz, nameb)))
|
|
assert_eql?(nil, host1.lookup_server(Req.new(addr1, portz, namez)))
|
|
|
|
# connect to addr2
|
|
assert_eql?(host7, host1.lookup_server(Req.new(addr2, port1, name1)))
|
|
assert_eql?(host8, host1.lookup_server(Req.new(addr2, port1, name2)))
|
|
assert_eql?(host8, host1.lookup_server(Req.new(addr2, port1, namea)))
|
|
assert_eql?(host8, host1.lookup_server(Req.new(addr2, port1, nameb)))
|
|
assert_eql?(nil, host1.lookup_server(Req.new(addr2, port1, namez)))
|
|
assert_eql?(host7, host1.lookup_server(Req.new(addr2, port2, name1)))
|
|
assert_eql?(host8, host1.lookup_server(Req.new(addr2, port2, name2)))
|
|
assert_eql?(host8, host1.lookup_server(Req.new(addr2, port2, namea)))
|
|
assert_eql?(host8, host1.lookup_server(Req.new(addr2, port2, nameb)))
|
|
assert_eql?(nil, host1.lookup_server(Req.new(addr2, port2, namez)))
|
|
assert_eql?(host7, host1.lookup_server(Req.new(addr2, port3, name1)))
|
|
assert_eql?(host8, host1.lookup_server(Req.new(addr2, port3, name2)))
|
|
assert_eql?(host8, host1.lookup_server(Req.new(addr2, port3, namea)))
|
|
assert_eql?(host8, host1.lookup_server(Req.new(addr2, port3, nameb)))
|
|
assert_eql?(host11, host1.lookup_server(Req.new(addr2, port3, namez)))
|
|
assert_eql?(host7, host1.lookup_server(Req.new(addr2, portz, name1)))
|
|
assert_eql?(host8, host1.lookup_server(Req.new(addr2, portz, name2)))
|
|
assert_eql?(host8, host1.lookup_server(Req.new(addr2, portz, namea)))
|
|
assert_eql?(host8, host1.lookup_server(Req.new(addr2, portz, nameb)))
|
|
assert_eql?(nil, host1.lookup_server(Req.new(addr2, portz, namez)))
|
|
|
|
# connect to addrz
|
|
assert_eql?(nil, host1.lookup_server(Req.new(addrz, port1, name1)))
|
|
assert_eql?(nil, host1.lookup_server(Req.new(addrz, port1, name2)))
|
|
assert_eql?(nil, host1.lookup_server(Req.new(addrz, port1, namea)))
|
|
assert_eql?(nil, host1.lookup_server(Req.new(addrz, port1, nameb)))
|
|
assert_eql?(nil, host1.lookup_server(Req.new(addrz, port1, namez)))
|
|
assert_eql?(nil, host1.lookup_server(Req.new(addrz, port2, name1)))
|
|
assert_eql?(nil, host1.lookup_server(Req.new(addrz, port2, name2)))
|
|
assert_eql?(nil, host1.lookup_server(Req.new(addrz, port2, namea)))
|
|
assert_eql?(nil, host1.lookup_server(Req.new(addrz, port2, nameb)))
|
|
assert_eql?(nil, host1.lookup_server(Req.new(addrz, port2, namez)))
|
|
assert_eql?(host11, host1.lookup_server(Req.new(addrz, port3, name1)))
|
|
assert_eql?(host11, host1.lookup_server(Req.new(addrz, port3, name2)))
|
|
assert_eql?(host11, host1.lookup_server(Req.new(addrz, port3, namea)))
|
|
assert_eql?(host11, host1.lookup_server(Req.new(addrz, port3, nameb)))
|
|
assert_eql?(host11, host1.lookup_server(Req.new(addrz, port3, namez)))
|
|
assert_eql?(nil, host1.lookup_server(Req.new(addrz, portz, name1)))
|
|
assert_eql?(nil, host1.lookup_server(Req.new(addrz, portz, name2)))
|
|
assert_eql?(nil, host1.lookup_server(Req.new(addrz, portz, namea)))
|
|
assert_eql?(nil, host1.lookup_server(Req.new(addrz, portz, nameb)))
|
|
assert_eql?(nil, host1.lookup_server(Req.new(addrz, portz, namez)))
|
|
|
|
# connect to localhost
|
|
assert_eql?(host10, host1.lookup_server(Req.new(local, port1, name1)))
|
|
assert_eql?(host10, host1.lookup_server(Req.new(local, port1, name2)))
|
|
assert_eql?(host10, host1.lookup_server(Req.new(local, port1, namea)))
|
|
assert_eql?(host10, host1.lookup_server(Req.new(local, port1, nameb)))
|
|
assert_eql?(host10, host1.lookup_server(Req.new(local, port1, namez)))
|
|
assert_eql?(host10, host1.lookup_server(Req.new(local, port2, name1)))
|
|
assert_eql?(host10, host1.lookup_server(Req.new(local, port2, name2)))
|
|
assert_eql?(host10, host1.lookup_server(Req.new(local, port2, namea)))
|
|
assert_eql?(host10, host1.lookup_server(Req.new(local, port2, nameb)))
|
|
assert_eql?(host10, host1.lookup_server(Req.new(local, port2, namez)))
|
|
assert_eql?(host10, host1.lookup_server(Req.new(local, port3, name1)))
|
|
assert_eql?(host10, host1.lookup_server(Req.new(local, port3, name2)))
|
|
assert_eql?(host10, host1.lookup_server(Req.new(local, port3, namea)))
|
|
assert_eql?(host10, host1.lookup_server(Req.new(local, port3, nameb)))
|
|
assert_eql?(host10, host1.lookup_server(Req.new(local, port3, namez)))
|
|
assert_eql?(host10, host1.lookup_server(Req.new(local, portz, name1)))
|
|
assert_eql?(host10, host1.lookup_server(Req.new(local, portz, name2)))
|
|
assert_eql?(host10, host1.lookup_server(Req.new(local, portz, namea)))
|
|
assert_eql?(host10, host1.lookup_server(Req.new(local, portz, nameb)))
|
|
assert_eql?(host10, host1.lookup_server(Req.new(local, portz, namez)))
|
|
end
|
|
|
|
def test_callbacks
|
|
accepted = started = stopped = 0
|
|
requested0 = requested1 = 0
|
|
config = {
|
|
:ServerName => "localhost",
|
|
:AcceptCallback => Proc.new{ accepted += 1 },
|
|
:StartCallback => Proc.new{ started += 1 },
|
|
:StopCallback => Proc.new{ stopped += 1 },
|
|
:RequestCallback => Proc.new{|req, res| requested0 += 1 },
|
|
}
|
|
log_tester = lambda {|log, access_log|
|
|
assert(log.find {|s| %r{ERROR `/' not found\.} =~ s })
|
|
assert_equal([], log.reject {|s| %r{ERROR `/' not found\.} =~ s })
|
|
}
|
|
TestWEBrick.start_httpserver(config, log_tester){|server, addr, port, log|
|
|
vhost_config = {
|
|
:ServerName => "myhostname",
|
|
:BindAddress => addr,
|
|
:Port => port,
|
|
:DoNotListen => true,
|
|
:Logger => NoLog,
|
|
:AccessLog => [],
|
|
:RequestCallback => Proc.new{|req, res| requested1 += 1 },
|
|
}
|
|
server.virtual_host(WEBrick::HTTPServer.new(vhost_config))
|
|
|
|
Thread.pass while server.status != :Running
|
|
assert_equal(1, started, log.call)
|
|
assert_equal(0, stopped, log.call)
|
|
assert_equal(0, accepted, log.call)
|
|
|
|
http = Net::HTTP.new(addr, port)
|
|
req = Net::HTTP::Get.new("/")
|
|
req["Host"] = "myhostname:#{port}"
|
|
http.request(req){|res| assert_equal("404", res.code, log.call)}
|
|
http.request(req){|res| assert_equal("404", res.code, log.call)}
|
|
http.request(req){|res| assert_equal("404", res.code, log.call)}
|
|
req["Host"] = "localhost:#{port}"
|
|
http.request(req){|res| assert_equal("404", res.code, log.call)}
|
|
http.request(req){|res| assert_equal("404", res.code, log.call)}
|
|
http.request(req){|res| assert_equal("404", res.code, log.call)}
|
|
assert_equal(6, accepted, log.call)
|
|
assert_equal(3, requested0, log.call)
|
|
assert_equal(3, requested1, log.call)
|
|
}
|
|
assert_equal(started, 1)
|
|
assert_equal(stopped, 1)
|
|
end
|
|
|
|
# This class is needed by test_response_io_with_chunked_set method
|
|
class EventManagerForChunkedResponseTest
|
|
def initialize
|
|
@listeners = []
|
|
end
|
|
def add_listener( &block )
|
|
@listeners << block
|
|
end
|
|
def raise_str_event( str )
|
|
@listeners.each{ |e| e.call( :str, str ) }
|
|
end
|
|
def raise_close_event()
|
|
@listeners.each{ |e| e.call( :cls ) }
|
|
end
|
|
end
|
|
def test_response_io_with_chunked_set
|
|
evt_man = EventManagerForChunkedResponseTest.new
|
|
t = Thread.new do
|
|
begin
|
|
config = {
|
|
:ServerName => "localhost"
|
|
}
|
|
TestWEBrick.start_httpserver(config) do |server, addr, port, log|
|
|
body_strs = [ 'aaaaaa', 'bb', 'cccc' ]
|
|
server.mount_proc( "/", ->( req, res ){
|
|
# Test for setting chunked...
|
|
res.chunked = true
|
|
r,w = IO.pipe
|
|
evt_man.add_listener do |type,str|
|
|
type == :cls ? ( w.close ) : ( w << str )
|
|
end
|
|
res.body = r
|
|
} )
|
|
Thread.pass while server.status != :Running
|
|
http = Net::HTTP.new(addr, port)
|
|
req = Net::HTTP::Get.new("/")
|
|
http.request(req) do |res|
|
|
i = 0
|
|
evt_man.raise_str_event( body_strs[i] )
|
|
res.read_body do |s|
|
|
assert_equal( body_strs[i], s )
|
|
i += 1
|
|
if i < body_strs.length
|
|
evt_man.raise_str_event( body_strs[i] )
|
|
else
|
|
evt_man.raise_close_event()
|
|
end
|
|
end
|
|
assert_equal( body_strs.length, i )
|
|
end
|
|
end
|
|
rescue => err
|
|
flunk( 'exception raised in thread: ' + err.to_s )
|
|
end
|
|
end
|
|
if t.join( 3 ).nil?
|
|
evt_man.raise_close_event()
|
|
flunk( 'timeout' )
|
|
if t.join( 1 ).nil?
|
|
Thread.kill t
|
|
end
|
|
end
|
|
end
|
|
|
|
def test_response_io_without_chunked_set
|
|
config = {
|
|
:ServerName => "localhost"
|
|
}
|
|
log_tester = lambda {|log, access_log|
|
|
assert_equal(1, log.length)
|
|
assert_match(/WARN Could not determine content-length of response body./, log[0])
|
|
}
|
|
TestWEBrick.start_httpserver(config, log_tester){|server, addr, port, log|
|
|
server.mount_proc("/", lambda { |req, res|
|
|
r,w = IO.pipe
|
|
# Test for not setting chunked...
|
|
# res.chunked = true
|
|
res.body = r
|
|
w << "foo"
|
|
w.close
|
|
})
|
|
Thread.pass while server.status != :Running
|
|
http = Net::HTTP.new(addr, port)
|
|
req = Net::HTTP::Get.new("/")
|
|
req['Connection'] = 'Keep-Alive'
|
|
begin
|
|
timeout(2) do
|
|
http.request(req){|res| assert_equal("foo", res.body) }
|
|
end
|
|
rescue Timeout::Error
|
|
flunk('corrupted response')
|
|
end
|
|
}
|
|
end
|
|
|
|
def test_request_handler_callback_is_deprecated
|
|
requested = 0
|
|
config = {
|
|
:ServerName => "localhost",
|
|
:RequestHandler => Proc.new{|req, res| requested += 1 },
|
|
}
|
|
log_tester = lambda {|log, access_log|
|
|
assert_equal(2, log.length)
|
|
assert_match(/WARN :RequestHandler is deprecated, please use :RequestCallback/, log[0])
|
|
assert_match(%r{ERROR `/' not found\.}, log[1])
|
|
}
|
|
TestWEBrick.start_httpserver(config, log_tester){|server, addr, port, log|
|
|
Thread.pass while server.status != :Running
|
|
|
|
http = Net::HTTP.new(addr, port)
|
|
req = Net::HTTP::Get.new("/")
|
|
req["Host"] = "localhost:#{port}"
|
|
http.request(req){|res| assert_equal("404", res.code, log.call)}
|
|
assert_match(%r{:RequestHandler is deprecated, please use :RequestCallback$}, log.call, log.call)
|
|
}
|
|
assert_equal(1, requested)
|
|
end
|
|
|
|
def test_shutdown_with_busy_keepalive_connection
|
|
requested = 0
|
|
config = {
|
|
:ServerName => "localhost",
|
|
}
|
|
TestWEBrick.start_httpserver(config){|server, addr, port, log|
|
|
server.mount_proc("/", lambda {|req, res| res.body = "heffalump" })
|
|
Thread.pass while server.status != :Running
|
|
|
|
Net::HTTP.start(addr, port) do |http|
|
|
req = Net::HTTP::Get.new("/")
|
|
http.request(req){|res| assert_equal('Keep-Alive', res['Connection'], log.call) }
|
|
server.shutdown
|
|
begin
|
|
10.times {|n| http.request(req); requested += 1 }
|
|
rescue
|
|
# Errno::ECONNREFUSED or similar
|
|
end
|
|
end
|
|
}
|
|
assert_equal(0, requested, "Server responded to #{requested} requests after shutdown")
|
|
end
|
|
|
|
def test_gigantic_request_header
|
|
log_tester = lambda {|log, access_log|
|
|
assert_equal 1, log.size
|
|
assert log[0].include?('ERROR headers too large')
|
|
}
|
|
TestWEBrick.start_httpserver({}, log_tester){|server, addr, port, log|
|
|
server.mount('/', WEBrick::HTTPServlet::FileHandler, __FILE__)
|
|
TCPSocket.open(addr, port) do |c|
|
|
c.write("GET / HTTP/1.0\r\n")
|
|
junk = "X-Junk: #{' ' * 1024}\r\n"
|
|
assert_raise(Errno::ECONNRESET, Errno::EPIPE) do
|
|
loop { c.write(junk) }
|
|
end
|
|
end
|
|
}
|
|
end
|
|
|
|
def test_eof_in_chunk
|
|
log_tester = lambda do |log, access_log|
|
|
assert_equal 1, log.size
|
|
assert log[0].include?('ERROR bad chunk data size')
|
|
end
|
|
TestWEBrick.start_httpserver({}, log_tester){|server, addr, port, log|
|
|
server.mount_proc('/', ->(req, res) { res.body = req.body })
|
|
TCPSocket.open(addr, port) do |c|
|
|
c.write("POST / HTTP/1.1\r\nHost: example.com\r\n" \
|
|
"Transfer-Encoding: chunked\r\n\r\n5\r\na")
|
|
c.shutdown(Socket::SHUT_WR) # trigger EOF in server
|
|
res = c.read
|
|
assert_match %r{\AHTTP/1\.1 400 }, res
|
|
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
|