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