PageRenderTime 46ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/test/webrick/test_httpserver.rb

http://github.com/ruby/ruby
Ruby | 543 lines | 501 code | 33 blank | 9 comment | 15 complexity | 58455b42c0a3539fc73e0ad8f6504c83 MD5 | raw file
Possible License(s): GPL-2.0, BSD-3-Clause, AGPL-3.0
  1. # frozen_string_literal: false
  2. require "test/unit"
  3. require "net/http"
  4. require "webrick"
  5. require_relative "utils"
  6. class TestWEBrickHTTPServer < Test::Unit::TestCase
  7. empty_log = Object.new
  8. def empty_log.<<(str)
  9. assert_equal('', str)
  10. self
  11. end
  12. NoLog = WEBrick::Log.new(empty_log, WEBrick::BasicLog::WARN)
  13. def teardown
  14. WEBrick::Utils::TimeoutHandler.terminate
  15. super
  16. end
  17. def test_mount
  18. httpd = WEBrick::HTTPServer.new(
  19. :Logger => NoLog,
  20. :DoNotListen=>true
  21. )
  22. httpd.mount("/", :Root)
  23. httpd.mount("/foo", :Foo)
  24. httpd.mount("/foo/bar", :Bar, :bar1)
  25. httpd.mount("/foo/bar/baz", :Baz, :baz1, :baz2)
  26. serv, opts, script_name, path_info = httpd.search_servlet("/")
  27. assert_equal(:Root, serv)
  28. assert_equal([], opts)
  29. assert_equal("", script_name)
  30. assert_equal("/", path_info)
  31. serv, opts, script_name, path_info = httpd.search_servlet("/sub")
  32. assert_equal(:Root, serv)
  33. assert_equal([], opts)
  34. assert_equal("", script_name)
  35. assert_equal("/sub", path_info)
  36. serv, opts, script_name, path_info = httpd.search_servlet("/sub/")
  37. assert_equal(:Root, serv)
  38. assert_equal([], opts)
  39. assert_equal("", script_name)
  40. assert_equal("/sub/", path_info)
  41. serv, opts, script_name, path_info = httpd.search_servlet("/foo")
  42. assert_equal(:Foo, serv)
  43. assert_equal([], opts)
  44. assert_equal("/foo", script_name)
  45. assert_equal("", path_info)
  46. serv, opts, script_name, path_info = httpd.search_servlet("/foo/")
  47. assert_equal(:Foo, serv)
  48. assert_equal([], opts)
  49. assert_equal("/foo", script_name)
  50. assert_equal("/", path_info)
  51. serv, opts, script_name, path_info = httpd.search_servlet("/foo/sub")
  52. assert_equal(:Foo, serv)
  53. assert_equal([], opts)
  54. assert_equal("/foo", script_name)
  55. assert_equal("/sub", path_info)
  56. serv, opts, script_name, path_info = httpd.search_servlet("/foo/bar")
  57. assert_equal(:Bar, serv)
  58. assert_equal([:bar1], opts)
  59. assert_equal("/foo/bar", script_name)
  60. assert_equal("", path_info)
  61. serv, opts, script_name, path_info = httpd.search_servlet("/foo/bar/baz")
  62. assert_equal(:Baz, serv)
  63. assert_equal([:baz1, :baz2], opts)
  64. assert_equal("/foo/bar/baz", script_name)
  65. assert_equal("", path_info)
  66. end
  67. class Req
  68. attr_reader :port, :host
  69. def initialize(addr, port, host)
  70. @addr, @port, @host = addr, port, host
  71. end
  72. def addr
  73. [0,0,0,@addr]
  74. end
  75. end
  76. def httpd(addr, port, host, ali)
  77. config ={
  78. :Logger => NoLog,
  79. :DoNotListen => true,
  80. :BindAddress => addr,
  81. :Port => port,
  82. :ServerName => host,
  83. :ServerAlias => ali,
  84. }
  85. return WEBrick::HTTPServer.new(config)
  86. end
  87. def assert_eql?(v1, v2)
  88. assert_equal(v1.object_id, v2.object_id)
  89. end
  90. def test_lookup_server
  91. addr1 = "192.168.100.1"
  92. addr2 = "192.168.100.2"
  93. addrz = "192.168.100.254"
  94. local = "127.0.0.1"
  95. port1 = 80
  96. port2 = 8080
  97. port3 = 10080
  98. portz = 32767
  99. name1 = "www.example.com"
  100. name2 = "www2.example.com"
  101. name3 = "www3.example.com"
  102. namea = "www.example.co.jp"
  103. nameb = "www.example.jp"
  104. namec = "www2.example.co.jp"
  105. named = "www2.example.jp"
  106. namez = "foobar.example.com"
  107. alias1 = [namea, nameb]
  108. alias2 = [namec, named]
  109. host1 = httpd(nil, port1, name1, nil)
  110. hosts = [
  111. host2 = httpd(addr1, port1, name1, nil),
  112. host3 = httpd(addr1, port1, name2, alias1),
  113. host4 = httpd(addr1, port2, name1, nil),
  114. host5 = httpd(addr1, port2, name2, alias1),
  115. httpd(addr1, port2, name3, alias2),
  116. host7 = httpd(addr2, nil, name1, nil),
  117. host8 = httpd(addr2, nil, name2, alias1),
  118. httpd(addr2, nil, name3, alias2),
  119. host10 = httpd(local, nil, nil, nil),
  120. host11 = httpd(nil, port3, nil, nil),
  121. ].sort_by{ rand }
  122. hosts.each{|h| host1.virtual_host(h) }
  123. # connect to addr1
  124. assert_eql?(host2, host1.lookup_server(Req.new(addr1, port1, name1)))
  125. assert_eql?(host3, host1.lookup_server(Req.new(addr1, port1, name2)))
  126. assert_eql?(host3, host1.lookup_server(Req.new(addr1, port1, namea)))
  127. assert_eql?(host3, host1.lookup_server(Req.new(addr1, port1, nameb)))
  128. assert_eql?(nil, host1.lookup_server(Req.new(addr1, port1, namez)))
  129. assert_eql?(host4, host1.lookup_server(Req.new(addr1, port2, name1)))
  130. assert_eql?(host5, host1.lookup_server(Req.new(addr1, port2, name2)))
  131. assert_eql?(host5, host1.lookup_server(Req.new(addr1, port2, namea)))
  132. assert_eql?(host5, host1.lookup_server(Req.new(addr1, port2, nameb)))
  133. assert_eql?(nil, host1.lookup_server(Req.new(addr1, port2, namez)))
  134. assert_eql?(host11, host1.lookup_server(Req.new(addr1, port3, name1)))
  135. assert_eql?(host11, host1.lookup_server(Req.new(addr1, port3, name2)))
  136. assert_eql?(host11, host1.lookup_server(Req.new(addr1, port3, namea)))
  137. assert_eql?(host11, host1.lookup_server(Req.new(addr1, port3, nameb)))
  138. assert_eql?(host11, host1.lookup_server(Req.new(addr1, port3, namez)))
  139. assert_eql?(nil, host1.lookup_server(Req.new(addr1, portz, name1)))
  140. assert_eql?(nil, host1.lookup_server(Req.new(addr1, portz, name2)))
  141. assert_eql?(nil, host1.lookup_server(Req.new(addr1, portz, namea)))
  142. assert_eql?(nil, host1.lookup_server(Req.new(addr1, portz, nameb)))
  143. assert_eql?(nil, host1.lookup_server(Req.new(addr1, portz, namez)))
  144. # connect to addr2
  145. assert_eql?(host7, host1.lookup_server(Req.new(addr2, port1, name1)))
  146. assert_eql?(host8, host1.lookup_server(Req.new(addr2, port1, name2)))
  147. assert_eql?(host8, host1.lookup_server(Req.new(addr2, port1, namea)))
  148. assert_eql?(host8, host1.lookup_server(Req.new(addr2, port1, nameb)))
  149. assert_eql?(nil, host1.lookup_server(Req.new(addr2, port1, namez)))
  150. assert_eql?(host7, host1.lookup_server(Req.new(addr2, port2, name1)))
  151. assert_eql?(host8, host1.lookup_server(Req.new(addr2, port2, name2)))
  152. assert_eql?(host8, host1.lookup_server(Req.new(addr2, port2, namea)))
  153. assert_eql?(host8, host1.lookup_server(Req.new(addr2, port2, nameb)))
  154. assert_eql?(nil, host1.lookup_server(Req.new(addr2, port2, namez)))
  155. assert_eql?(host7, host1.lookup_server(Req.new(addr2, port3, name1)))
  156. assert_eql?(host8, host1.lookup_server(Req.new(addr2, port3, name2)))
  157. assert_eql?(host8, host1.lookup_server(Req.new(addr2, port3, namea)))
  158. assert_eql?(host8, host1.lookup_server(Req.new(addr2, port3, nameb)))
  159. assert_eql?(host11, host1.lookup_server(Req.new(addr2, port3, namez)))
  160. assert_eql?(host7, host1.lookup_server(Req.new(addr2, portz, name1)))
  161. assert_eql?(host8, host1.lookup_server(Req.new(addr2, portz, name2)))
  162. assert_eql?(host8, host1.lookup_server(Req.new(addr2, portz, namea)))
  163. assert_eql?(host8, host1.lookup_server(Req.new(addr2, portz, nameb)))
  164. assert_eql?(nil, host1.lookup_server(Req.new(addr2, portz, namez)))
  165. # connect to addrz
  166. assert_eql?(nil, host1.lookup_server(Req.new(addrz, port1, name1)))
  167. assert_eql?(nil, host1.lookup_server(Req.new(addrz, port1, name2)))
  168. assert_eql?(nil, host1.lookup_server(Req.new(addrz, port1, namea)))
  169. assert_eql?(nil, host1.lookup_server(Req.new(addrz, port1, nameb)))
  170. assert_eql?(nil, host1.lookup_server(Req.new(addrz, port1, namez)))
  171. assert_eql?(nil, host1.lookup_server(Req.new(addrz, port2, name1)))
  172. assert_eql?(nil, host1.lookup_server(Req.new(addrz, port2, name2)))
  173. assert_eql?(nil, host1.lookup_server(Req.new(addrz, port2, namea)))
  174. assert_eql?(nil, host1.lookup_server(Req.new(addrz, port2, nameb)))
  175. assert_eql?(nil, host1.lookup_server(Req.new(addrz, port2, namez)))
  176. assert_eql?(host11, host1.lookup_server(Req.new(addrz, port3, name1)))
  177. assert_eql?(host11, host1.lookup_server(Req.new(addrz, port3, name2)))
  178. assert_eql?(host11, host1.lookup_server(Req.new(addrz, port3, namea)))
  179. assert_eql?(host11, host1.lookup_server(Req.new(addrz, port3, nameb)))
  180. assert_eql?(host11, host1.lookup_server(Req.new(addrz, port3, namez)))
  181. assert_eql?(nil, host1.lookup_server(Req.new(addrz, portz, name1)))
  182. assert_eql?(nil, host1.lookup_server(Req.new(addrz, portz, name2)))
  183. assert_eql?(nil, host1.lookup_server(Req.new(addrz, portz, namea)))
  184. assert_eql?(nil, host1.lookup_server(Req.new(addrz, portz, nameb)))
  185. assert_eql?(nil, host1.lookup_server(Req.new(addrz, portz, namez)))
  186. # connect to localhost
  187. assert_eql?(host10, host1.lookup_server(Req.new(local, port1, name1)))
  188. assert_eql?(host10, host1.lookup_server(Req.new(local, port1, name2)))
  189. assert_eql?(host10, host1.lookup_server(Req.new(local, port1, namea)))
  190. assert_eql?(host10, host1.lookup_server(Req.new(local, port1, nameb)))
  191. assert_eql?(host10, host1.lookup_server(Req.new(local, port1, namez)))
  192. assert_eql?(host10, host1.lookup_server(Req.new(local, port2, name1)))
  193. assert_eql?(host10, host1.lookup_server(Req.new(local, port2, name2)))
  194. assert_eql?(host10, host1.lookup_server(Req.new(local, port2, namea)))
  195. assert_eql?(host10, host1.lookup_server(Req.new(local, port2, nameb)))
  196. assert_eql?(host10, host1.lookup_server(Req.new(local, port2, namez)))
  197. assert_eql?(host10, host1.lookup_server(Req.new(local, port3, name1)))
  198. assert_eql?(host10, host1.lookup_server(Req.new(local, port3, name2)))
  199. assert_eql?(host10, host1.lookup_server(Req.new(local, port3, namea)))
  200. assert_eql?(host10, host1.lookup_server(Req.new(local, port3, nameb)))
  201. assert_eql?(host10, host1.lookup_server(Req.new(local, port3, namez)))
  202. assert_eql?(host10, host1.lookup_server(Req.new(local, portz, name1)))
  203. assert_eql?(host10, host1.lookup_server(Req.new(local, portz, name2)))
  204. assert_eql?(host10, host1.lookup_server(Req.new(local, portz, namea)))
  205. assert_eql?(host10, host1.lookup_server(Req.new(local, portz, nameb)))
  206. assert_eql?(host10, host1.lookup_server(Req.new(local, portz, namez)))
  207. end
  208. def test_callbacks
  209. accepted = started = stopped = 0
  210. requested0 = requested1 = 0
  211. config = {
  212. :ServerName => "localhost",
  213. :AcceptCallback => Proc.new{ accepted += 1 },
  214. :StartCallback => Proc.new{ started += 1 },
  215. :StopCallback => Proc.new{ stopped += 1 },
  216. :RequestCallback => Proc.new{|req, res| requested0 += 1 },
  217. }
  218. log_tester = lambda {|log, access_log|
  219. assert(log.find {|s| %r{ERROR `/' not found\.} =~ s })
  220. assert_equal([], log.reject {|s| %r{ERROR `/' not found\.} =~ s })
  221. }
  222. TestWEBrick.start_httpserver(config, log_tester){|server, addr, port, log|
  223. vhost_config = {
  224. :ServerName => "myhostname",
  225. :BindAddress => addr,
  226. :Port => port,
  227. :DoNotListen => true,
  228. :Logger => NoLog,
  229. :AccessLog => [],
  230. :RequestCallback => Proc.new{|req, res| requested1 += 1 },
  231. }
  232. server.virtual_host(WEBrick::HTTPServer.new(vhost_config))
  233. Thread.pass while server.status != :Running
  234. sleep 1 if defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled? # server.status behaves unexpectedly with --jit-wait
  235. assert_equal(1, started, log.call)
  236. assert_equal(0, stopped, log.call)
  237. assert_equal(0, accepted, log.call)
  238. http = Net::HTTP.new(addr, port)
  239. req = Net::HTTP::Get.new("/")
  240. req["Host"] = "myhostname:#{port}"
  241. http.request(req){|res| assert_equal("404", res.code, log.call)}
  242. http.request(req){|res| assert_equal("404", res.code, log.call)}
  243. http.request(req){|res| assert_equal("404", res.code, log.call)}
  244. req["Host"] = "localhost:#{port}"
  245. http.request(req){|res| assert_equal("404", res.code, log.call)}
  246. http.request(req){|res| assert_equal("404", res.code, log.call)}
  247. http.request(req){|res| assert_equal("404", res.code, log.call)}
  248. assert_equal(6, accepted, log.call)
  249. assert_equal(3, requested0, log.call)
  250. assert_equal(3, requested1, log.call)
  251. }
  252. assert_equal(started, 1)
  253. assert_equal(stopped, 1)
  254. end
  255. class CustomRequest < ::WEBrick::HTTPRequest; end
  256. class CustomResponse < ::WEBrick::HTTPResponse; end
  257. class CustomServer < ::WEBrick::HTTPServer
  258. def create_request(config)
  259. CustomRequest.new(config)
  260. end
  261. def create_response(config)
  262. CustomResponse.new(config)
  263. end
  264. end
  265. def test_custom_server_request_and_response
  266. config = { :ServerName => "localhost" }
  267. TestWEBrick.start_server(CustomServer, config){|server, addr, port, log|
  268. server.mount_proc("/", lambda {|req, res|
  269. assert_kind_of(CustomRequest, req)
  270. assert_kind_of(CustomResponse, res)
  271. res.body = "via custom response"
  272. })
  273. Thread.pass while server.status != :Running
  274. Net::HTTP.start(addr, port) do |http|
  275. req = Net::HTTP::Get.new("/")
  276. http.request(req){|res|
  277. assert_equal("via custom response", res.body)
  278. }
  279. server.shutdown
  280. end
  281. }
  282. end
  283. # This class is needed by test_response_io_with_chunked_set method
  284. class EventManagerForChunkedResponseTest
  285. def initialize
  286. @listeners = []
  287. end
  288. def add_listener( &block )
  289. @listeners << block
  290. end
  291. def raise_str_event( str )
  292. @listeners.each{ |e| e.call( :str, str ) }
  293. end
  294. def raise_close_event()
  295. @listeners.each{ |e| e.call( :cls ) }
  296. end
  297. end
  298. def test_response_io_with_chunked_set
  299. evt_man = EventManagerForChunkedResponseTest.new
  300. t = Thread.new do
  301. begin
  302. config = {
  303. :ServerName => "localhost"
  304. }
  305. TestWEBrick.start_httpserver(config) do |server, addr, port, log|
  306. body_strs = [ 'aaaaaa', 'bb', 'cccc' ]
  307. server.mount_proc( "/", ->( req, res ){
  308. # Test for setting chunked...
  309. res.chunked = true
  310. r,w = IO.pipe
  311. evt_man.add_listener do |type,str|
  312. type == :cls ? ( w.close ) : ( w << str )
  313. end
  314. res.body = r
  315. } )
  316. Thread.pass while server.status != :Running
  317. http = Net::HTTP.new(addr, port)
  318. req = Net::HTTP::Get.new("/")
  319. http.request(req) do |res|
  320. i = 0
  321. evt_man.raise_str_event( body_strs[i] )
  322. res.read_body do |s|
  323. assert_equal( body_strs[i], s )
  324. i += 1
  325. if i < body_strs.length
  326. evt_man.raise_str_event( body_strs[i] )
  327. else
  328. evt_man.raise_close_event()
  329. end
  330. end
  331. assert_equal( body_strs.length, i )
  332. end
  333. end
  334. rescue => err
  335. flunk( 'exception raised in thread: ' + err.to_s )
  336. end
  337. end
  338. if t.join( 3 ).nil?
  339. evt_man.raise_close_event()
  340. flunk( 'timeout' )
  341. if t.join( 1 ).nil?
  342. Thread.kill t
  343. end
  344. end
  345. end
  346. def test_response_io_without_chunked_set
  347. config = {
  348. :ServerName => "localhost"
  349. }
  350. log_tester = lambda {|log, access_log|
  351. assert_equal(1, log.length)
  352. assert_match(/WARN Could not determine content-length of response body./, log[0])
  353. }
  354. TestWEBrick.start_httpserver(config, log_tester){|server, addr, port, log|
  355. server.mount_proc("/", lambda { |req, res|
  356. r,w = IO.pipe
  357. # Test for not setting chunked...
  358. # res.chunked = true
  359. res.body = r
  360. w << "foo"
  361. w.close
  362. })
  363. Thread.pass while server.status != :Running
  364. http = Net::HTTP.new(addr, port)
  365. req = Net::HTTP::Get.new("/")
  366. req['Connection'] = 'Keep-Alive'
  367. begin
  368. Timeout.timeout(2) do
  369. http.request(req){|res| assert_equal("foo", res.body) }
  370. end
  371. rescue Timeout::Error
  372. flunk('corrupted response')
  373. end
  374. }
  375. end
  376. def test_request_handler_callback_is_deprecated
  377. requested = 0
  378. config = {
  379. :ServerName => "localhost",
  380. :RequestHandler => Proc.new{|req, res| requested += 1 },
  381. }
  382. log_tester = lambda {|log, access_log|
  383. assert_equal(2, log.length)
  384. assert_match(/WARN :RequestHandler is deprecated, please use :RequestCallback/, log[0])
  385. assert_match(%r{ERROR `/' not found\.}, log[1])
  386. }
  387. TestWEBrick.start_httpserver(config, log_tester){|server, addr, port, log|
  388. Thread.pass while server.status != :Running
  389. http = Net::HTTP.new(addr, port)
  390. req = Net::HTTP::Get.new("/")
  391. req["Host"] = "localhost:#{port}"
  392. http.request(req){|res| assert_equal("404", res.code, log.call)}
  393. assert_match(%r{:RequestHandler is deprecated, please use :RequestCallback$}, log.call, log.call)
  394. }
  395. assert_equal(1, requested)
  396. end
  397. def test_shutdown_with_busy_keepalive_connection
  398. requested = 0
  399. config = {
  400. :ServerName => "localhost",
  401. }
  402. TestWEBrick.start_httpserver(config){|server, addr, port, log|
  403. server.mount_proc("/", lambda {|req, res| res.body = "heffalump" })
  404. Thread.pass while server.status != :Running
  405. Net::HTTP.start(addr, port) do |http|
  406. req = Net::HTTP::Get.new("/")
  407. http.request(req){|res| assert_equal('Keep-Alive', res['Connection'], log.call) }
  408. server.shutdown
  409. begin
  410. 10.times {|n| http.request(req); requested += 1 }
  411. rescue
  412. # Errno::ECONNREFUSED or similar
  413. end
  414. end
  415. }
  416. assert_equal(0, requested, "Server responded to #{requested} requests after shutdown")
  417. end
  418. def test_cntrl_in_path
  419. log_ary = []
  420. access_log_ary = []
  421. config = {
  422. :Port => 0,
  423. :BindAddress => '127.0.0.1',
  424. :Logger => WEBrick::Log.new(log_ary, WEBrick::BasicLog::WARN),
  425. :AccessLog => [[access_log_ary, '']],
  426. }
  427. s = WEBrick::HTTPServer.new(config)
  428. s.mount('/foo', WEBrick::HTTPServlet::FileHandler, __FILE__)
  429. th = Thread.new { s.start }
  430. addr = s.listeners[0].addr
  431. http = Net::HTTP.new(addr[3], addr[1])
  432. req = Net::HTTP::Get.new('/notexist%0a/foo')
  433. http.request(req) { |res| assert_equal('404', res.code) }
  434. exp = %Q(ERROR `/notexist\\n/foo' not found.\n)
  435. assert_equal 1, log_ary.size
  436. assert_include log_ary[0], exp
  437. ensure
  438. s&.shutdown
  439. th&.join
  440. end
  441. def test_gigantic_request_header
  442. log_tester = lambda {|log, access_log|
  443. assert_equal 1, log.size
  444. assert_include log[0], 'ERROR headers too large'
  445. }
  446. TestWEBrick.start_httpserver({}, log_tester){|server, addr, port, log|
  447. server.mount('/', WEBrick::HTTPServlet::FileHandler, __FILE__)
  448. TCPSocket.open(addr, port) do |c|
  449. c.write("GET / HTTP/1.0\r\n")
  450. junk = -"X-Junk: #{' ' * 1024}\r\n"
  451. assert_raise(Errno::ECONNRESET, Errno::EPIPE) do
  452. loop { c.write(junk) }
  453. end
  454. end
  455. }
  456. end
  457. def test_eof_in_chunk
  458. log_tester = lambda do |log, access_log|
  459. assert_equal 1, log.size
  460. assert_include log[0], 'ERROR bad chunk data size'
  461. end
  462. TestWEBrick.start_httpserver({}, log_tester){|server, addr, port, log|
  463. server.mount_proc('/', ->(req, res) { res.body = req.body })
  464. TCPSocket.open(addr, port) do |c|
  465. c.write("POST / HTTP/1.1\r\nHost: example.com\r\n" \
  466. "Transfer-Encoding: chunked\r\n\r\n5\r\na")
  467. c.shutdown(Socket::SHUT_WR) # trigger EOF in server
  468. res = c.read
  469. assert_match %r{\AHTTP/1\.1 400 }, res
  470. end
  471. }
  472. end
  473. def test_big_chunks
  474. nr_out = 3
  475. buf = 'big' # 3 bytes is bigger than 2!
  476. config = { :InputBufferSize => 2 }.freeze
  477. total = 0
  478. all = ''
  479. TestWEBrick.start_httpserver(config){|server, addr, port, log|
  480. server.mount_proc('/', ->(req, res) {
  481. err = []
  482. ret = req.body do |chunk|
  483. n = chunk.bytesize
  484. n > config[:InputBufferSize] and err << "#{n} > :InputBufferSize"
  485. total += n
  486. all << chunk
  487. end
  488. ret.nil? or err << 'req.body should return nil'
  489. (buf * nr_out) == all or err << 'input body does not match expected'
  490. res.header['connection'] = 'close'
  491. res.body = err.join("\n")
  492. })
  493. TCPSocket.open(addr, port) do |c|
  494. c.write("POST / HTTP/1.1\r\nHost: example.com\r\n" \
  495. "Transfer-Encoding: chunked\r\n\r\n")
  496. chunk = "#{buf.bytesize.to_s(16)}\r\n#{buf}\r\n"
  497. nr_out.times { c.write(chunk) }
  498. c.write("0\r\n\r\n")
  499. head, body = c.read.split("\r\n\r\n")
  500. assert_match %r{\AHTTP/1\.1 200 OK}, head
  501. assert_nil body
  502. end
  503. }
  504. end
  505. end