PageRenderTime 33ms CodeModel.GetById 1ms RepoModel.GetById 0ms app.codeStats 0ms

/test/net/http/test_http.rb

http://github.com/ruby/ruby
Ruby | 1228 lines | 1020 code | 191 blank | 17 comment | 8 complexity | fa7589071d706eed75dd3d66f699668b MD5 | raw file
Possible License(s): GPL-2.0, BSD-3-Clause, AGPL-3.0
  1. # coding: US-ASCII
  2. # frozen_string_literal: false
  3. require 'test/unit'
  4. require 'net/http'
  5. require 'stringio'
  6. require_relative 'utils'
  7. class TestNetHTTP < Test::Unit::TestCase
  8. def test_class_Proxy
  9. no_proxy_class = Net::HTTP.Proxy nil
  10. assert_equal Net::HTTP, no_proxy_class
  11. proxy_class = Net::HTTP.Proxy 'proxy.example', 8000, 'user', 'pass'
  12. assert_not_equal Net::HTTP, proxy_class
  13. assert_operator proxy_class, :<, Net::HTTP
  14. assert_equal 'proxy.example', proxy_class.proxy_address
  15. assert_equal 8000, proxy_class.proxy_port
  16. assert_equal 'user', proxy_class.proxy_user
  17. assert_equal 'pass', proxy_class.proxy_pass
  18. http = proxy_class.new 'hostname.example'
  19. assert_not_predicate http, :proxy_from_env?
  20. proxy_class = Net::HTTP.Proxy 'proxy.example'
  21. assert_equal 'proxy.example', proxy_class.proxy_address
  22. assert_equal 80, proxy_class.proxy_port
  23. end
  24. def test_class_Proxy_from_ENV
  25. TestNetHTTPUtils.clean_http_proxy_env do
  26. ENV['http_proxy'] = 'http://proxy.example:8000'
  27. # These are ignored on purpose. See Bug 4388 and Feature 6546
  28. ENV['http_proxy_user'] = 'user'
  29. ENV['http_proxy_pass'] = 'pass'
  30. proxy_class = Net::HTTP.Proxy :ENV
  31. assert_not_equal Net::HTTP, proxy_class
  32. assert_operator proxy_class, :<, Net::HTTP
  33. assert_nil proxy_class.proxy_address
  34. assert_nil proxy_class.proxy_user
  35. assert_nil proxy_class.proxy_pass
  36. assert_not_equal 8000, proxy_class.proxy_port
  37. http = proxy_class.new 'hostname.example'
  38. assert http.proxy_from_env?
  39. end
  40. end
  41. def test_addr_port
  42. http = Net::HTTP.new 'hostname.example', nil, nil
  43. addr_port = http.__send__ :addr_port
  44. assert_equal 'hostname.example', addr_port
  45. http.use_ssl = true
  46. addr_port = http.__send__ :addr_port
  47. assert_equal 'hostname.example:80', addr_port
  48. http = Net::HTTP.new '203.0.113.1', nil, nil
  49. addr_port = http.__send__ :addr_port
  50. assert_equal '203.0.113.1', addr_port
  51. http.use_ssl = true
  52. addr_port = http.__send__ :addr_port
  53. assert_equal '203.0.113.1:80', addr_port
  54. http = Net::HTTP.new '2001:db8::1', nil, nil
  55. addr_port = http.__send__ :addr_port
  56. assert_equal '[2001:db8::1]', addr_port
  57. http.use_ssl = true
  58. addr_port = http.__send__ :addr_port
  59. assert_equal '[2001:db8::1]:80', addr_port
  60. end
  61. def test_edit_path
  62. http = Net::HTTP.new 'hostname.example', nil, nil
  63. edited = http.send :edit_path, '/path'
  64. assert_equal '/path', edited
  65. http.use_ssl = true
  66. edited = http.send :edit_path, '/path'
  67. assert_equal '/path', edited
  68. end
  69. def test_edit_path_proxy
  70. http = Net::HTTP.new 'hostname.example', nil, 'proxy.example'
  71. edited = http.send :edit_path, '/path'
  72. assert_equal 'http://hostname.example/path', edited
  73. http.use_ssl = true
  74. edited = http.send :edit_path, '/path'
  75. assert_equal '/path', edited
  76. end
  77. def test_proxy_address
  78. TestNetHTTPUtils.clean_http_proxy_env do
  79. http = Net::HTTP.new 'hostname.example', nil, 'proxy.example'
  80. assert_equal 'proxy.example', http.proxy_address
  81. http = Net::HTTP.new 'hostname.example', nil
  82. assert_equal nil, http.proxy_address
  83. end
  84. end
  85. def test_proxy_address_no_proxy
  86. TestNetHTTPUtils.clean_http_proxy_env do
  87. http = Net::HTTP.new 'hostname.example', nil, 'proxy.example', nil, nil, nil, 'example'
  88. assert_nil http.proxy_address
  89. http = Net::HTTP.new '10.224.1.1', nil, 'proxy.example', nil, nil, nil, 'example,10.224.0.0/22'
  90. assert_nil http.proxy_address
  91. end
  92. end
  93. def test_proxy_from_env_ENV
  94. TestNetHTTPUtils.clean_http_proxy_env do
  95. ENV['http_proxy'] = 'http://proxy.example:8000'
  96. assert_equal false, Net::HTTP.proxy_class?
  97. http = Net::HTTP.new 'hostname.example'
  98. assert_equal true, http.proxy_from_env?
  99. end
  100. end
  101. def test_proxy_address_ENV
  102. TestNetHTTPUtils.clean_http_proxy_env do
  103. ENV['http_proxy'] = 'http://proxy.example:8000'
  104. http = Net::HTTP.new 'hostname.example'
  105. assert_equal 'proxy.example', http.proxy_address
  106. end
  107. end
  108. def test_proxy_eh_no_proxy
  109. TestNetHTTPUtils.clean_http_proxy_env do
  110. assert_equal false, Net::HTTP.new('hostname.example', nil, nil).proxy?
  111. end
  112. end
  113. def test_proxy_eh_ENV
  114. TestNetHTTPUtils.clean_http_proxy_env do
  115. ENV['http_proxy'] = 'http://proxy.example:8000'
  116. http = Net::HTTP.new 'hostname.example'
  117. assert_equal true, http.proxy?
  118. end
  119. end
  120. def test_proxy_eh_ENV_with_user
  121. TestNetHTTPUtils.clean_http_proxy_env do
  122. ENV['http_proxy'] = 'http://foo:bar@proxy.example:8000'
  123. http = Net::HTTP.new 'hostname.example'
  124. assert_equal true, http.proxy?
  125. if Net::HTTP::ENVIRONMENT_VARIABLE_IS_MULTIUSER_SAFE
  126. assert_equal 'foo', http.proxy_user
  127. assert_equal 'bar', http.proxy_pass
  128. else
  129. assert_nil http.proxy_user
  130. assert_nil http.proxy_pass
  131. end
  132. end
  133. end
  134. def test_proxy_eh_ENV_none_set
  135. TestNetHTTPUtils.clean_http_proxy_env do
  136. assert_equal false, Net::HTTP.new('hostname.example').proxy?
  137. end
  138. end
  139. def test_proxy_eh_ENV_no_proxy
  140. TestNetHTTPUtils.clean_http_proxy_env do
  141. ENV['http_proxy'] = 'http://proxy.example:8000'
  142. ENV['no_proxy'] = 'hostname.example'
  143. assert_equal false, Net::HTTP.new('hostname.example').proxy?
  144. end
  145. end
  146. def test_proxy_port
  147. TestNetHTTPUtils.clean_http_proxy_env do
  148. http = Net::HTTP.new 'example', nil, 'proxy.example'
  149. assert_equal 'proxy.example', http.proxy_address
  150. assert_equal 80, http.proxy_port
  151. http = Net::HTTP.new 'example', nil, 'proxy.example', 8000
  152. assert_equal 8000, http.proxy_port
  153. http = Net::HTTP.new 'example', nil
  154. assert_equal nil, http.proxy_port
  155. end
  156. end
  157. def test_proxy_port_ENV
  158. TestNetHTTPUtils.clean_http_proxy_env do
  159. ENV['http_proxy'] = 'http://proxy.example:8000'
  160. http = Net::HTTP.new 'hostname.example'
  161. assert_equal 8000, http.proxy_port
  162. end
  163. end
  164. def test_newobj
  165. TestNetHTTPUtils.clean_http_proxy_env do
  166. ENV['http_proxy'] = 'http://proxy.example:8000'
  167. http = Net::HTTP.newobj 'hostname.example'
  168. assert_equal false, http.proxy?
  169. end
  170. end
  171. def test_failure_message_includes_failed_domain_and_port
  172. # hostname to be included in the error message
  173. host = Struct.new(:to_s).new("<example>")
  174. port = 2119
  175. # hack to let TCPSocket.open fail
  176. def host.to_str; raise SocketError, "open failure"; end
  177. uri = Struct.new(:scheme, :hostname, :port).new("http", host, port)
  178. assert_raise_with_message(SocketError, /#{host}:#{port}/) do
  179. TestNetHTTPUtils.clean_http_proxy_env{ Net::HTTP.get(uri) }
  180. end
  181. end
  182. end
  183. module TestNetHTTP_version_1_1_methods
  184. def test_s_start
  185. begin
  186. h = Net::HTTP.start(config('host'), config('port'))
  187. ensure
  188. h&.finish
  189. end
  190. assert_equal config('host'), h.address
  191. assert_equal config('port'), h.port
  192. assert_equal true, h.instance_variable_get(:@proxy_from_env)
  193. begin
  194. h = Net::HTTP.start(config('host'), config('port'), :ENV)
  195. ensure
  196. h&.finish
  197. end
  198. assert_equal config('host'), h.address
  199. assert_equal config('port'), h.port
  200. assert_equal true, h.instance_variable_get(:@proxy_from_env)
  201. begin
  202. h = Net::HTTP.start(config('host'), config('port'), nil)
  203. ensure
  204. h&.finish
  205. end
  206. assert_equal config('host'), h.address
  207. assert_equal config('port'), h.port
  208. assert_equal false, h.instance_variable_get(:@proxy_from_env)
  209. end
  210. def test_s_get
  211. assert_equal $test_net_http_data,
  212. Net::HTTP.get(config('host'), '/', config('port'))
  213. assert_equal $test_net_http_data, Net::HTTP.get(
  214. URI.parse("http://#{config('host')}:#{config('port')}")
  215. )
  216. assert_equal $test_net_http_data, Net::HTTP.get(
  217. URI.parse("http://#{config('host')}:#{config('port')}"), "Accept" => "text/plain"
  218. )
  219. end
  220. def test_s_get_response
  221. res = Net::HTTP.get_response(
  222. URI.parse("http://#{config('host')}:#{config('port')}")
  223. )
  224. assert_equal "application/octet-stream", res["Content-Type"]
  225. assert_equal $test_net_http_data, res.body
  226. res = Net::HTTP.get_response(
  227. URI.parse("http://#{config('host')}:#{config('port')}"), "Accept" => "text/plain"
  228. )
  229. assert_equal "text/plain", res["Content-Type"]
  230. assert_equal $test_net_http_data, res.body
  231. end
  232. def test_head
  233. start {|http|
  234. res = http.head('/')
  235. assert_kind_of Net::HTTPResponse, res
  236. assert_equal $test_net_http_data_type, res['Content-Type']
  237. unless self.is_a?(TestNetHTTP_v1_2_chunked)
  238. assert_equal $test_net_http_data.size, res['Content-Length'].to_i
  239. end
  240. }
  241. end
  242. def test_get
  243. start {|http|
  244. _test_get__get http
  245. _test_get__iter http
  246. _test_get__chunked http
  247. }
  248. end
  249. def _test_get__get(http)
  250. res = http.get('/')
  251. assert_kind_of Net::HTTPResponse, res
  252. assert_kind_of String, res.body
  253. unless self.is_a?(TestNetHTTP_v1_2_chunked)
  254. assert_not_nil res['content-length']
  255. assert_equal $test_net_http_data.size, res['content-length'].to_i
  256. end
  257. assert_equal $test_net_http_data_type, res['Content-Type']
  258. assert_equal $test_net_http_data.size, res.body.size
  259. assert_equal $test_net_http_data, res.body
  260. assert_nothing_raised {
  261. http.get('/', { 'User-Agent' => 'test' }.freeze)
  262. }
  263. assert res.decode_content, '[Bug #7924]' if Net::HTTP::HAVE_ZLIB
  264. end
  265. def _test_get__iter(http)
  266. buf = ''
  267. res = http.get('/') {|s| buf << s }
  268. assert_kind_of Net::HTTPResponse, res
  269. # assert_kind_of String, res.body
  270. unless self.is_a?(TestNetHTTP_v1_2_chunked)
  271. assert_not_nil res['content-length']
  272. assert_equal $test_net_http_data.size, res['content-length'].to_i
  273. end
  274. assert_equal $test_net_http_data_type, res['Content-Type']
  275. assert_equal $test_net_http_data.size, buf.size
  276. assert_equal $test_net_http_data, buf
  277. # assert_equal $test_net_http_data.size, res.body.size
  278. # assert_equal $test_net_http_data, res.body
  279. end
  280. def _test_get__chunked(http)
  281. buf = ''
  282. res = http.get('/') {|s| buf << s }
  283. assert_kind_of Net::HTTPResponse, res
  284. # assert_kind_of String, res.body
  285. unless self.is_a?(TestNetHTTP_v1_2_chunked)
  286. assert_not_nil res['content-length']
  287. assert_equal $test_net_http_data.size, res['content-length'].to_i
  288. end
  289. assert_equal $test_net_http_data_type, res['Content-Type']
  290. assert_equal $test_net_http_data.size, buf.size
  291. assert_equal $test_net_http_data, buf
  292. # assert_equal $test_net_http_data.size, res.body.size
  293. # assert_equal $test_net_http_data, res.body
  294. end
  295. def test_get__break
  296. i = 0
  297. start {|http|
  298. http.get('/') do |str|
  299. i += 1
  300. break
  301. end
  302. }
  303. assert_equal 1, i
  304. @log_tester = nil # server may encount ECONNRESET
  305. end
  306. def test_get__implicit_start
  307. res = new().get('/')
  308. assert_kind_of Net::HTTPResponse, res
  309. assert_kind_of String, res.body
  310. unless self.is_a?(TestNetHTTP_v1_2_chunked)
  311. assert_not_nil res['content-length']
  312. end
  313. assert_equal $test_net_http_data_type, res['Content-Type']
  314. assert_equal $test_net_http_data.size, res.body.size
  315. assert_equal $test_net_http_data, res.body
  316. end
  317. def test_get__crlf
  318. start {|http|
  319. assert_raise(ArgumentError) do
  320. http.get("\r")
  321. end
  322. assert_raise(ArgumentError) do
  323. http.get("\n")
  324. end
  325. }
  326. end
  327. def test_get2
  328. start {|http|
  329. http.get2('/') {|res|
  330. EnvUtil.suppress_warning do
  331. assert_kind_of Net::HTTPResponse, res
  332. assert_kind_of Net::HTTPResponse, res.header
  333. end
  334. unless self.is_a?(TestNetHTTP_v1_2_chunked)
  335. assert_not_nil res['content-length']
  336. end
  337. assert_equal $test_net_http_data_type, res['Content-Type']
  338. assert_kind_of String, res.body
  339. assert_kind_of String, res.entity
  340. assert_equal $test_net_http_data.size, res.body.size
  341. assert_equal $test_net_http_data, res.body
  342. assert_equal $test_net_http_data, res.entity
  343. }
  344. }
  345. end
  346. def test_post
  347. start {|http|
  348. _test_post__base http
  349. _test_post__file http
  350. _test_post__no_data http
  351. }
  352. end
  353. def _test_post__base(http)
  354. uheader = {}
  355. uheader['Accept'] = 'application/octet-stream'
  356. uheader['Content-Type'] = 'application/x-www-form-urlencoded'
  357. data = 'post data'
  358. res = http.post('/', data, uheader)
  359. assert_kind_of Net::HTTPResponse, res
  360. assert_kind_of String, res.body
  361. assert_equal data, res.body
  362. assert_equal data, res.entity
  363. end
  364. def _test_post__file(http)
  365. data = 'post data'
  366. f = StringIO.new
  367. http.post('/', data, {'content-type' => 'application/x-www-form-urlencoded'}, f)
  368. assert_equal data, f.string
  369. end
  370. def _test_post__no_data(http)
  371. unless self.is_a?(TestNetHTTP_v1_2_chunked)
  372. EnvUtil.suppress_warning do
  373. data = nil
  374. res = http.post('/', data)
  375. assert_not_equal '411', res.code
  376. end
  377. end
  378. end
  379. def test_s_post
  380. url = "http://#{config('host')}:#{config('port')}/?q=a"
  381. res = assert_warning(/Content-Type did not set/) do
  382. Net::HTTP.post(
  383. URI.parse(url),
  384. "a=x")
  385. end
  386. assert_equal "application/x-www-form-urlencoded", res["Content-Type"]
  387. assert_equal "a=x", res.body
  388. assert_equal url, res["X-request-uri"]
  389. res = Net::HTTP.post(
  390. URI.parse(url),
  391. "hello world",
  392. "Content-Type" => "text/plain; charset=US-ASCII")
  393. assert_equal "text/plain; charset=US-ASCII", res["Content-Type"]
  394. assert_equal "hello world", res.body
  395. end
  396. def test_s_post_form
  397. url = "http://#{config('host')}:#{config('port')}/"
  398. res = Net::HTTP.post_form(
  399. URI.parse(url),
  400. "a" => "x")
  401. assert_equal ["a=x"], res.body.split(/[;&]/).sort
  402. res = Net::HTTP.post_form(
  403. URI.parse(url),
  404. "a" => "x",
  405. "b" => "y")
  406. assert_equal ["a=x", "b=y"], res.body.split(/[;&]/).sort
  407. res = Net::HTTP.post_form(
  408. URI.parse(url),
  409. "a" => ["x1", "x2"],
  410. "b" => "y")
  411. assert_equal url, res['X-request-uri']
  412. assert_equal ["a=x1", "a=x2", "b=y"], res.body.split(/[;&]/).sort
  413. res = Net::HTTP.post_form(
  414. URI.parse(url + '?a=x'),
  415. "b" => "y")
  416. assert_equal url + '?a=x', res['X-request-uri']
  417. assert_equal ["b=y"], res.body.split(/[;&]/).sort
  418. end
  419. def test_patch
  420. start {|http|
  421. _test_patch__base http
  422. }
  423. end
  424. def _test_patch__base(http)
  425. uheader = {}
  426. uheader['Accept'] = 'application/octet-stream'
  427. uheader['Content-Type'] = 'application/x-www-form-urlencoded'
  428. data = 'patch data'
  429. res = http.patch('/', data, uheader)
  430. assert_kind_of Net::HTTPResponse, res
  431. assert_kind_of String, res.body
  432. assert_equal data, res.body
  433. assert_equal data, res.entity
  434. end
  435. def test_timeout_during_HTTP_session_write
  436. th = nil
  437. # listen for connections... but deliberately do not read
  438. TCPServer.open('localhost', 0) {|server|
  439. port = server.addr[1]
  440. conn = Net::HTTP.new('localhost', port)
  441. conn.write_timeout = EnvUtil.apply_timeout_scale(0.01)
  442. conn.read_timeout = EnvUtil.apply_timeout_scale(0.01) if windows?
  443. conn.open_timeout = EnvUtil.apply_timeout_scale(0.1)
  444. th = Thread.new do
  445. err = !windows? ? Net::WriteTimeout : Net::ReadTimeout
  446. assert_raise(err) do
  447. assert_warning(/Content-Type did not set/) do
  448. conn.post('/', "a"*50_000_000)
  449. end
  450. end
  451. end
  452. assert th.join(EnvUtil.apply_timeout_scale(10))
  453. }
  454. ensure
  455. th&.kill
  456. th&.join
  457. end
  458. def test_timeout_during_HTTP_session
  459. bug4246 = "expected the HTTP session to have timed out but have not. c.f. [ruby-core:34203]"
  460. th = nil
  461. # listen for connections... but deliberately do not read
  462. TCPServer.open('localhost', 0) {|server|
  463. port = server.addr[1]
  464. conn = Net::HTTP.new('localhost', port)
  465. conn.read_timeout = EnvUtil.apply_timeout_scale(0.01)
  466. conn.open_timeout = EnvUtil.apply_timeout_scale(1)
  467. th = Thread.new do
  468. assert_raise(Net::ReadTimeout) {
  469. conn.get('/')
  470. }
  471. end
  472. assert th.join(EnvUtil.apply_timeout_scale(10)), bug4246
  473. }
  474. ensure
  475. th.kill
  476. th.join
  477. end
  478. end
  479. module TestNetHTTP_version_1_2_methods
  480. def test_request
  481. start {|http|
  482. _test_request__GET http
  483. _test_request__accept_encoding http
  484. _test_request__file http
  485. # _test_request__range http # WEBrick does not support Range: header.
  486. _test_request__HEAD http
  487. _test_request__POST http
  488. _test_request__stream_body http
  489. _test_request__uri http
  490. _test_request__uri_host http
  491. }
  492. end
  493. def _test_request__GET(http)
  494. req = Net::HTTP::Get.new('/')
  495. http.request(req) {|res|
  496. assert_kind_of Net::HTTPResponse, res
  497. assert_kind_of String, res.body
  498. unless self.is_a?(TestNetHTTP_v1_2_chunked)
  499. assert_not_nil res['content-length']
  500. assert_equal $test_net_http_data.size, res['content-length'].to_i
  501. end
  502. assert_equal $test_net_http_data.size, res.body.size
  503. assert_equal $test_net_http_data, res.body
  504. assert res.decode_content, 'Bug #7831' if Net::HTTP::HAVE_ZLIB
  505. }
  506. end
  507. def _test_request__accept_encoding(http)
  508. req = Net::HTTP::Get.new('/', 'accept-encoding' => 'deflate')
  509. http.request(req) {|res|
  510. assert_kind_of Net::HTTPResponse, res
  511. assert_kind_of String, res.body
  512. unless self.is_a?(TestNetHTTP_v1_2_chunked)
  513. assert_not_nil res['content-length']
  514. assert_equal $test_net_http_data.size, res['content-length'].to_i
  515. end
  516. assert_equal $test_net_http_data.size, res.body.size
  517. assert_equal $test_net_http_data, res.body
  518. assert_not_predicate res, :decode_content, 'Bug #7831' if Net::HTTP::HAVE_ZLIB
  519. }
  520. end
  521. def _test_request__file(http)
  522. req = Net::HTTP::Get.new('/')
  523. http.request(req) {|res|
  524. assert_kind_of Net::HTTPResponse, res
  525. unless self.is_a?(TestNetHTTP_v1_2_chunked)
  526. assert_not_nil res['content-length']
  527. assert_equal $test_net_http_data.size, res['content-length'].to_i
  528. end
  529. f = StringIO.new("".force_encoding("ASCII-8BIT"))
  530. res.read_body f
  531. assert_equal $test_net_http_data.bytesize, f.string.bytesize
  532. assert_equal $test_net_http_data.encoding, f.string.encoding
  533. assert_equal $test_net_http_data, f.string
  534. }
  535. end
  536. def _test_request__range(http)
  537. req = Net::HTTP::Get.new('/')
  538. req['range'] = 'bytes=0-5'
  539. assert_equal $test_net_http_data[0,6], http.request(req).body
  540. end
  541. def _test_request__HEAD(http)
  542. req = Net::HTTP::Head.new('/')
  543. http.request(req) {|res|
  544. assert_kind_of Net::HTTPResponse, res
  545. unless self.is_a?(TestNetHTTP_v1_2_chunked)
  546. assert_not_nil res['content-length']
  547. assert_equal $test_net_http_data.size, res['content-length'].to_i
  548. end
  549. assert_nil res.body
  550. }
  551. end
  552. def _test_request__POST(http)
  553. data = 'post data'
  554. req = Net::HTTP::Post.new('/')
  555. req['Accept'] = $test_net_http_data_type
  556. req['Content-Type'] = 'application/x-www-form-urlencoded'
  557. http.request(req, data) {|res|
  558. assert_kind_of Net::HTTPResponse, res
  559. unless self.is_a?(TestNetHTTP_v1_2_chunked)
  560. assert_equal data.size, res['content-length'].to_i
  561. end
  562. assert_kind_of String, res.body
  563. assert_equal data, res.body
  564. }
  565. end
  566. def _test_request__stream_body(http)
  567. req = Net::HTTP::Post.new('/')
  568. data = $test_net_http_data
  569. req.content_length = data.size
  570. req['Content-Type'] = 'application/x-www-form-urlencoded'
  571. req.body_stream = StringIO.new(data)
  572. res = http.request(req)
  573. assert_kind_of Net::HTTPResponse, res
  574. assert_kind_of String, res.body
  575. assert_equal data.size, res.body.size
  576. assert_equal data, res.body
  577. end
  578. def _test_request__path(http)
  579. uri = URI 'https://hostname.example/'
  580. req = Net::HTTP::Get.new('/')
  581. res = http.request(req)
  582. assert_kind_of URI::Generic, req.uri
  583. assert_not_equal uri, req.uri
  584. assert_equal uri, res.uri
  585. assert_not_same uri, req.uri
  586. assert_not_same req.uri, res.uri
  587. end
  588. def _test_request__uri(http)
  589. uri = URI 'https://hostname.example/'
  590. req = Net::HTTP::Get.new(uri)
  591. res = http.request(req)
  592. assert_kind_of URI::Generic, req.uri
  593. assert_not_equal uri, req.uri
  594. assert_equal req.uri, res.uri
  595. assert_not_same uri, req.uri
  596. assert_not_same req.uri, res.uri
  597. end
  598. def _test_request__uri_host(http)
  599. uri = URI 'http://other.example/'
  600. req = Net::HTTP::Get.new(uri)
  601. req['host'] = 'hostname.example'
  602. res = http.request(req)
  603. assert_kind_of URI::Generic, req.uri
  604. assert_equal URI("http://hostname.example:#{http.port}"), res.uri
  605. end
  606. def test_send_request
  607. start {|http|
  608. _test_send_request__GET http
  609. _test_send_request__HEAD http
  610. _test_send_request__POST http
  611. }
  612. end
  613. def _test_send_request__GET(http)
  614. res = http.send_request('GET', '/')
  615. assert_kind_of Net::HTTPResponse, res
  616. unless self.is_a?(TestNetHTTP_v1_2_chunked)
  617. assert_equal $test_net_http_data.size, res['content-length'].to_i
  618. end
  619. assert_kind_of String, res.body
  620. assert_equal $test_net_http_data, res.body
  621. end
  622. def _test_send_request__HEAD(http)
  623. res = http.send_request('HEAD', '/')
  624. assert_kind_of Net::HTTPResponse, res
  625. unless self.is_a?(TestNetHTTP_v1_2_chunked)
  626. assert_not_nil res['content-length']
  627. assert_equal $test_net_http_data.size, res['content-length'].to_i
  628. end
  629. assert_nil res.body
  630. end
  631. def _test_send_request__POST(http)
  632. data = 'aaabbb cc ddddddddddd lkjoiu4j3qlkuoa'
  633. res = http.send_request('POST', '/', data, 'content-type' => 'application/x-www-form-urlencoded')
  634. assert_kind_of Net::HTTPResponse, res
  635. assert_kind_of String, res.body
  636. assert_equal data.size, res.body.size
  637. assert_equal data, res.body
  638. end
  639. def test_set_form
  640. require 'tempfile'
  641. Tempfile.create('ruby-test') {|file|
  642. file << "\u{30c7}\u{30fc}\u{30bf}"
  643. data = [
  644. ['name', 'Gonbei Nanashi'],
  645. ['name', "\u{540d}\u{7121}\u{3057}\u{306e}\u{6a29}\u{5175}\u{885b}"],
  646. ['s"i\o', StringIO.new("\u{3042 3044 4e9c 925b}")],
  647. ["file", file, filename: "ruby-test"]
  648. ]
  649. expected = <<"__EOM__".gsub(/\n/, "\r\n")
  650. --<boundary>
  651. Content-Disposition: form-data; name="name"
  652. Gonbei Nanashi
  653. --<boundary>
  654. Content-Disposition: form-data; name="name"
  655. \xE5\x90\x8D\xE7\x84\xA1\xE3\x81\x97\xE3\x81\xAE\xE6\xA8\xA9\xE5\x85\xB5\xE8\xA1\x9B
  656. --<boundary>
  657. Content-Disposition: form-data; name="s\\"i\\\\o"
  658. \xE3\x81\x82\xE3\x81\x84\xE4\xBA\x9C\xE9\x89\x9B
  659. --<boundary>
  660. Content-Disposition: form-data; name="file"; filename="ruby-test"
  661. Content-Type: application/octet-stream
  662. \xE3\x83\x87\xE3\x83\xBC\xE3\x82\xBF
  663. --<boundary>--
  664. __EOM__
  665. start {|http|
  666. _test_set_form_urlencoded(http, data.reject{|k,v|!v.is_a?(String)})
  667. _test_set_form_multipart(http, false, data, expected)
  668. _test_set_form_multipart(http, true, data, expected)
  669. }
  670. }
  671. end
  672. def _test_set_form_urlencoded(http, data)
  673. req = Net::HTTP::Post.new('/')
  674. req.set_form(data)
  675. res = http.request req
  676. assert_equal "name=Gonbei+Nanashi&name=%E5%90%8D%E7%84%A1%E3%81%97%E3%81%AE%E6%A8%A9%E5%85%B5%E8%A1%9B", res.body
  677. end
  678. def _test_set_form_multipart(http, chunked_p, data, expected)
  679. data.each{|k,v|v.rewind rescue nil}
  680. req = Net::HTTP::Post.new('/')
  681. req.set_form(data, 'multipart/form-data')
  682. req['Transfer-Encoding'] = 'chunked' if chunked_p
  683. res = http.request req
  684. body = res.body
  685. assert_match(/\A--(?<boundary>\S+)/, body)
  686. /\A--(?<boundary>\S+)/ =~ body
  687. expected = expected.gsub(/<boundary>/, boundary)
  688. assert_equal(expected, body)
  689. end
  690. def test_set_form_with_file
  691. require 'tempfile'
  692. Tempfile.create('ruby-test') {|file|
  693. file.binmode
  694. file << $test_net_http_data
  695. filename = File.basename(file.to_path)
  696. data = [['file', file]]
  697. expected = <<"__EOM__".gsub(/\n/, "\r\n")
  698. --<boundary>
  699. Content-Disposition: form-data; name="file"; filename="<filename>"
  700. Content-Type: application/octet-stream
  701. <data>
  702. --<boundary>--
  703. __EOM__
  704. expected.sub!(/<filename>/, filename)
  705. expected.sub!(/<data>/, $test_net_http_data)
  706. start {|http|
  707. data.each{|k,v|v.rewind rescue nil}
  708. req = Net::HTTP::Post.new('/')
  709. req.set_form(data, 'multipart/form-data')
  710. res = http.request req
  711. body = res.body
  712. header, _ = body.split(/\r\n\r\n/, 2)
  713. assert_match(/\A--(?<boundary>\S+)/, body)
  714. /\A--(?<boundary>\S+)/ =~ body
  715. expected = expected.gsub(/<boundary>/, boundary)
  716. assert_match(/^--(?<boundary>\S+)\r\n/, header)
  717. assert_match(
  718. /^Content-Disposition: form-data; name="file"; filename="#{filename}"\r\n/,
  719. header)
  720. assert_equal(expected, body)
  721. data.each{|k,v|v.rewind rescue nil}
  722. req['Transfer-Encoding'] = 'chunked'
  723. res = http.request req
  724. #assert_equal(expected, res.body)
  725. }
  726. }
  727. end
  728. end
  729. class TestNetHTTP_v1_2 < Test::Unit::TestCase
  730. CONFIG = {
  731. 'host' => '127.0.0.1',
  732. 'proxy_host' => nil,
  733. 'proxy_port' => nil,
  734. }
  735. include TestNetHTTPUtils
  736. include TestNetHTTP_version_1_1_methods
  737. include TestNetHTTP_version_1_2_methods
  738. def new
  739. Net::HTTP.version_1_2
  740. super
  741. end
  742. def test_send_large_POST_request
  743. start {|http|
  744. data = ' '*6000000
  745. res = http.send_request('POST', '/', data, 'content-type' => 'application/x-www-form-urlencoded')
  746. assert_kind_of Net::HTTPResponse, res
  747. assert_kind_of String, res.body
  748. assert_equal data.size, res.body.size
  749. assert_equal data, res.body
  750. }
  751. end
  752. end
  753. class TestNetHTTP_v1_2_chunked < Test::Unit::TestCase
  754. CONFIG = {
  755. 'host' => '127.0.0.1',
  756. 'proxy_host' => nil,
  757. 'proxy_port' => nil,
  758. 'chunked' => true,
  759. }
  760. include TestNetHTTPUtils
  761. include TestNetHTTP_version_1_1_methods
  762. include TestNetHTTP_version_1_2_methods
  763. def new
  764. Net::HTTP.version_1_2
  765. super
  766. end
  767. def test_chunked_break
  768. assert_nothing_raised("[ruby-core:29229]") {
  769. start {|http|
  770. http.request_get('/') {|res|
  771. res.read_body {|chunk|
  772. break
  773. }
  774. }
  775. }
  776. }
  777. end
  778. end
  779. class TestNetHTTPContinue < Test::Unit::TestCase
  780. CONFIG = {
  781. 'host' => '127.0.0.1',
  782. 'proxy_host' => nil,
  783. 'proxy_port' => nil,
  784. 'chunked' => true,
  785. }
  786. include TestNetHTTPUtils
  787. def logfile
  788. @debug = StringIO.new('')
  789. end
  790. def mount_proc(&block)
  791. @server.mount('/continue', WEBrick::HTTPServlet::ProcHandler.new(block.to_proc))
  792. end
  793. def test_expect_continue
  794. mount_proc {|req, res|
  795. req.continue
  796. res.body = req.query['body']
  797. }
  798. start {|http|
  799. uheader = {'content-type' => 'application/x-www-form-urlencoded', 'expect' => '100-continue'}
  800. http.continue_timeout = 0.2
  801. http.request_post('/continue', 'body=BODY', uheader) {|res|
  802. assert_equal('BODY', res.read_body)
  803. }
  804. }
  805. assert_match(/Expect: 100-continue/, @debug.string)
  806. assert_match(/HTTP\/1.1 100 continue/, @debug.string)
  807. end
  808. def test_expect_continue_timeout
  809. mount_proc {|req, res|
  810. sleep 0.2
  811. req.continue # just ignored because it's '100'
  812. res.body = req.query['body']
  813. }
  814. start {|http|
  815. uheader = {'content-type' => 'application/x-www-form-urlencoded', 'expect' => '100-continue'}
  816. http.continue_timeout = 0
  817. http.request_post('/continue', 'body=BODY', uheader) {|res|
  818. assert_equal('BODY', res.read_body)
  819. }
  820. }
  821. assert_match(/Expect: 100-continue/, @debug.string)
  822. assert_match(/HTTP\/1.1 100 continue/, @debug.string)
  823. end
  824. def test_expect_continue_error
  825. mount_proc {|req, res|
  826. res.status = 501
  827. res.body = req.query['body']
  828. }
  829. start {|http|
  830. uheader = {'content-type' => 'application/x-www-form-urlencoded', 'expect' => '100-continue'}
  831. http.continue_timeout = 0
  832. http.request_post('/continue', 'body=ERROR', uheader) {|res|
  833. assert_equal('ERROR', res.read_body)
  834. }
  835. }
  836. assert_match(/Expect: 100-continue/, @debug.string)
  837. assert_not_match(/HTTP\/1.1 100 continue/, @debug.string)
  838. end
  839. def test_expect_continue_error_before_body
  840. @log_tester = nil
  841. mount_proc {|req, res|
  842. raise WEBrick::HTTPStatus::Forbidden
  843. }
  844. start {|http|
  845. uheader = {'content-type' => 'application/x-www-form-urlencoded', 'content-length' => '5', 'expect' => '100-continue'}
  846. http.continue_timeout = 1 # allow the server to respond before sending
  847. http.request_post('/continue', 'data', uheader) {|res|
  848. assert_equal(res.code, '403')
  849. }
  850. }
  851. assert_match(/Expect: 100-continue/, @debug.string)
  852. assert_not_match(/HTTP\/1.1 100 continue/, @debug.string)
  853. end
  854. def test_expect_continue_error_while_waiting
  855. mount_proc {|req, res|
  856. res.status = 501
  857. res.body = req.query['body']
  858. }
  859. start {|http|
  860. uheader = {'content-type' => 'application/x-www-form-urlencoded', 'expect' => '100-continue'}
  861. http.continue_timeout = 0.5
  862. http.request_post('/continue', 'body=ERROR', uheader) {|res|
  863. assert_equal('ERROR', res.read_body)
  864. }
  865. }
  866. assert_match(/Expect: 100-continue/, @debug.string)
  867. assert_not_match(/HTTP\/1.1 100 continue/, @debug.string)
  868. end
  869. end
  870. class TestNetHTTPSwitchingProtocols < Test::Unit::TestCase
  871. CONFIG = {
  872. 'host' => '127.0.0.1',
  873. 'proxy_host' => nil,
  874. 'proxy_port' => nil,
  875. 'chunked' => true,
  876. }
  877. include TestNetHTTPUtils
  878. def logfile
  879. @debug = StringIO.new('')
  880. end
  881. def mount_proc(&block)
  882. @server.mount('/continue', WEBrick::HTTPServlet::ProcHandler.new(block.to_proc))
  883. end
  884. def test_info
  885. mount_proc {|req, res|
  886. req.instance_variable_get(:@socket) << "HTTP/1.1 101 Switching Protocols\r\n\r\n"
  887. res.body = req.query['body']
  888. }
  889. start {|http|
  890. http.continue_timeout = 0.2
  891. http.request_post('/continue', 'body=BODY',
  892. 'content-type' => 'application/x-www-form-urlencoded') {|res|
  893. assert_equal('BODY', res.read_body)
  894. }
  895. }
  896. assert_match(/HTTP\/1.1 101 Switching Protocols/, @debug.string)
  897. end
  898. end
  899. class TestNetHTTPKeepAlive < Test::Unit::TestCase
  900. CONFIG = {
  901. 'host' => '127.0.0.1',
  902. 'proxy_host' => nil,
  903. 'proxy_port' => nil,
  904. 'RequestTimeout' => 1,
  905. }
  906. include TestNetHTTPUtils
  907. def test_keep_alive_get_auto_reconnect
  908. start {|http|
  909. res = http.get('/')
  910. http.keep_alive_timeout = 1
  911. assert_kind_of Net::HTTPResponse, res
  912. assert_kind_of String, res.body
  913. sleep 1.5
  914. assert_nothing_raised {
  915. res = http.get('/')
  916. }
  917. assert_kind_of Net::HTTPResponse, res
  918. assert_kind_of String, res.body
  919. }
  920. end
  921. def test_server_closed_connection_auto_reconnect
  922. start {|http|
  923. res = http.get('/')
  924. http.keep_alive_timeout = 5
  925. assert_kind_of Net::HTTPResponse, res
  926. assert_kind_of String, res.body
  927. sleep 1.5
  928. assert_nothing_raised {
  929. # Net::HTTP should detect the closed connection before attempting the
  930. # request, since post requests cannot be retried.
  931. res = http.post('/', 'query=foo', 'content-type' => 'application/x-www-form-urlencoded')
  932. }
  933. assert_kind_of Net::HTTPResponse, res
  934. assert_kind_of String, res.body
  935. }
  936. end
  937. def test_keep_alive_get_auto_retry
  938. start {|http|
  939. res = http.get('/')
  940. http.keep_alive_timeout = 5
  941. assert_kind_of Net::HTTPResponse, res
  942. assert_kind_of String, res.body
  943. sleep 1.5
  944. res = http.get('/')
  945. assert_kind_of Net::HTTPResponse, res
  946. assert_kind_of String, res.body
  947. }
  948. end
  949. class MockSocket
  950. attr_reader :count
  951. def initialize(success_after: nil)
  952. @success_after = success_after
  953. @count = 0
  954. end
  955. def close
  956. end
  957. def closed?
  958. end
  959. def write(_)
  960. end
  961. def readline
  962. @count += 1
  963. if @success_after && @success_after <= @count
  964. "HTTP/1.1 200 OK"
  965. else
  966. raise Errno::ECONNRESET
  967. end
  968. end
  969. def readuntil(*_)
  970. ""
  971. end
  972. def read_all(_)
  973. end
  974. end
  975. def test_http_retry_success
  976. start {|http|
  977. socket = MockSocket.new(success_after: 10)
  978. http.instance_variable_get(:@socket).close
  979. http.instance_variable_set(:@socket, socket)
  980. assert_equal 0, socket.count
  981. http.max_retries = 10
  982. res = http.get('/')
  983. assert_equal 10, socket.count
  984. assert_kind_of Net::HTTPResponse, res
  985. assert_kind_of String, res.body
  986. }
  987. end
  988. def test_http_retry_failed
  989. start {|http|
  990. socket = MockSocket.new
  991. http.instance_variable_get(:@socket).close
  992. http.instance_variable_set(:@socket, socket)
  993. http.max_retries = 10
  994. assert_raise(Errno::ECONNRESET){ http.get('/') }
  995. assert_equal 11, socket.count
  996. }
  997. end
  998. def test_keep_alive_server_close
  999. def @server.run(sock)
  1000. sock.close
  1001. end
  1002. start {|http|
  1003. assert_raise(EOFError, Errno::ECONNRESET, IOError) {
  1004. http.get('/')
  1005. }
  1006. }
  1007. end
  1008. end
  1009. class TestNetHTTPLocalBind < Test::Unit::TestCase
  1010. CONFIG = {
  1011. 'host' => 'localhost',
  1012. 'proxy_host' => nil,
  1013. 'proxy_port' => nil,
  1014. }
  1015. include TestNetHTTPUtils
  1016. def test_bind_to_local_host
  1017. @server.mount_proc('/show_ip') { |req, res| res.body = req.remote_ip }
  1018. http = Net::HTTP.new(config('host'), config('port'))
  1019. http.local_host = Addrinfo.tcp(config('host'), config('port')).ip_address
  1020. assert_not_nil(http.local_host)
  1021. assert_nil(http.local_port)
  1022. res = http.get('/show_ip')
  1023. assert_equal(http.local_host, res.body)
  1024. end
  1025. def test_bind_to_local_port
  1026. @server.mount_proc('/show_port') { |req, res| res.body = req.peeraddr[1].to_s }
  1027. http = Net::HTTP.new(config('host'), config('port'))
  1028. http.local_host = Addrinfo.tcp(config('host'), config('port')).ip_address
  1029. http.local_port = Addrinfo.tcp(config('host'), 0).bind {|s|
  1030. s.local_address.ip_port.to_s
  1031. }
  1032. assert_not_nil(http.local_host)
  1033. assert_not_nil(http.local_port)
  1034. res = http.get('/show_port')
  1035. assert_equal(http.local_port, res.body)
  1036. end
  1037. end