1# coding: US-ASCII
2require 'test/unit'
3require 'net/http'
4require 'stringio'
5require_relative 'utils'
6require_relative '../../ruby/envutil'
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    refute_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 'example'
27
28    refute 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      refute_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      refute_equal 8000, proxy_class.proxy_port
55
56      http = proxy_class.new 'example'
57
58      assert http.proxy_from_env?
59    end
60  end
61
62  def test_edit_path
63    http = Net::HTTP.new 'example', nil, nil
64
65    edited = http.send :edit_path, '/path'
66
67    assert_equal '/path', edited
68
69    http.use_ssl = true
70
71    edited = http.send :edit_path, '/path'
72
73    assert_equal '/path', edited
74  end
75
76  def test_edit_path_proxy
77    http = Net::HTTP.new 'example', nil, 'proxy.example'
78
79    edited = http.send :edit_path, '/path'
80
81    assert_equal 'http://example/path', edited
82
83    http.use_ssl = true
84
85    edited = http.send :edit_path, '/path'
86
87    assert_equal '/path', edited
88  end
89
90  def test_proxy_address
91    clean_http_proxy_env do
92      http = Net::HTTP.new 'example', nil, 'proxy.example'
93      assert_equal 'proxy.example', http.proxy_address
94
95      http = Net::HTTP.new 'example', nil
96      assert_equal nil, http.proxy_address
97    end
98  end
99
100  def test_proxy_address_ENV
101    clean_http_proxy_env do
102      ENV['http_proxy'] = 'http://proxy.example:8000'
103
104      http = Net::HTTP.new 'example'
105
106      assert_equal 'proxy.example', http.proxy_address
107    end
108  end
109
110  def test_proxy_eh_no_proxy
111    clean_http_proxy_env do
112      assert_equal false, Net::HTTP.new('example', nil, nil).proxy?
113    end
114  end
115
116  def test_proxy_eh_ENV
117    clean_http_proxy_env do
118      ENV['http_proxy'] = 'http://proxy.example:8000'
119
120      http = Net::HTTP.new 'example'
121
122      assert_equal true, http.proxy?
123    end
124  end
125
126  def test_proxy_eh_ENV_none_set
127    clean_http_proxy_env do
128      assert_equal false, Net::HTTP.new('example').proxy?
129    end
130  end
131
132  def test_proxy_eh_ENV_no_proxy
133    clean_http_proxy_env do
134      ENV['http_proxy'] = 'http://proxy.example:8000'
135      ENV['no_proxy']   = 'example'
136
137      assert_equal false, Net::HTTP.new('example').proxy?
138    end
139  end
140
141  def test_proxy_port
142    clean_http_proxy_env do
143      http = Net::HTTP.new 'exmaple', nil, 'proxy.example'
144      assert_equal 'proxy.example', http.proxy_address
145      assert_equal 80, http.proxy_port
146      http = Net::HTTP.new 'exmaple', nil, 'proxy.example', 8000
147      assert_equal 8000, http.proxy_port
148      http = Net::HTTP.new 'exmaple', nil
149      assert_equal nil, http.proxy_port
150    end
151  end
152
153  def test_proxy_port_ENV
154    clean_http_proxy_env do
155      ENV['http_proxy'] = 'http://proxy.example:8000'
156
157      http = Net::HTTP.new 'example'
158
159      assert_equal 8000, http.proxy_port
160    end
161  end
162
163  def test_newobj
164    clean_http_proxy_env do
165      ENV['http_proxy'] = 'http://proxy.example:8000'
166
167      http = Net::HTTP.newobj 'example'
168
169      assert_equal false, http.proxy?
170    end
171  end
172
173  def clean_http_proxy_env
174    orig = {
175      'http_proxy'      => ENV['http_proxy'],
176      'http_proxy_user' => ENV['http_proxy_user'],
177      'http_proxy_pass' => ENV['http_proxy_pass'],
178      'no_proxy'        => ENV['no_proxy'],
179    }
180
181    orig.each_key do |key|
182      ENV.delete key
183    end
184
185    yield
186  ensure
187    orig.each do |key, value|
188      ENV[key] = value
189    end
190  end
191
192end
193
194module TestNetHTTP_version_1_1_methods
195
196  def test_s_get
197    assert_equal $test_net_http_data,
198        Net::HTTP.get(config('host'), '/', config('port'))
199  end
200
201  def test_head
202    start {|http|
203      res = http.head('/')
204      assert_kind_of Net::HTTPResponse, res
205      assert_equal $test_net_http_data_type, res['Content-Type']
206      unless self.is_a?(TestNetHTTP_v1_2_chunked)
207        assert_equal $test_net_http_data.size, res['Content-Length'].to_i
208      end
209    }
210  end
211
212  def test_get
213    start {|http|
214      _test_get__get http
215      _test_get__iter http
216      _test_get__chunked http
217    }
218  end
219
220  def _test_get__get(http)
221    res = http.get('/')
222    assert_kind_of Net::HTTPResponse, res
223    assert_kind_of String, res.body
224    unless self.is_a?(TestNetHTTP_v1_2_chunked)
225      assert_not_nil res['content-length']
226      assert_equal $test_net_http_data.size, res['content-length'].to_i
227    end
228    assert_equal $test_net_http_data_type, res['Content-Type']
229    assert_equal $test_net_http_data.size, res.body.size
230    assert_equal $test_net_http_data, res.body
231
232    assert_nothing_raised {
233      http.get('/', { 'User-Agent' => 'test' }.freeze)
234    }
235
236    assert res.decode_content, '[Bug #7924]' if Net::HTTP::HAVE_ZLIB
237  end
238
239  def _test_get__iter(http)
240    buf = ''
241    res = http.get('/') {|s| buf << s }
242    assert_kind_of Net::HTTPResponse, res
243    # assert_kind_of String, res.body
244    unless self.is_a?(TestNetHTTP_v1_2_chunked)
245      assert_not_nil res['content-length']
246      assert_equal $test_net_http_data.size, res['content-length'].to_i
247    end
248    assert_equal $test_net_http_data_type, res['Content-Type']
249    assert_equal $test_net_http_data.size, buf.size
250    assert_equal $test_net_http_data, buf
251    # assert_equal $test_net_http_data.size, res.body.size
252    # assert_equal $test_net_http_data, res.body
253  end
254
255  def _test_get__chunked(http)
256    buf = ''
257    res = http.get('/') {|s| buf << s }
258    assert_kind_of Net::HTTPResponse, res
259    # assert_kind_of String, res.body
260    unless self.is_a?(TestNetHTTP_v1_2_chunked)
261      assert_not_nil res['content-length']
262      assert_equal $test_net_http_data.size, res['content-length'].to_i
263    end
264    assert_equal $test_net_http_data_type, res['Content-Type']
265    assert_equal $test_net_http_data.size, buf.size
266    assert_equal $test_net_http_data, buf
267    # assert_equal $test_net_http_data.size, res.body.size
268    # assert_equal $test_net_http_data, res.body
269  end
270
271  def test_get__break
272    i = 0
273    start {|http|
274      http.get('/') do |str|
275        i += 1
276        break
277      end
278    }
279    assert_equal 1, i
280  end
281
282  def test_get__implicit_start
283    res = new().get('/')
284    assert_kind_of Net::HTTPResponse, res
285    assert_kind_of String, res.body
286    unless self.is_a?(TestNetHTTP_v1_2_chunked)
287      assert_not_nil res['content-length']
288    end
289    assert_equal $test_net_http_data_type, res['Content-Type']
290    assert_equal $test_net_http_data.size, res.body.size
291    assert_equal $test_net_http_data, res.body
292  end
293
294  def test_get2
295    start {|http|
296      http.get2('/') {|res|
297        EnvUtil.suppress_warning do
298          assert_kind_of Net::HTTPResponse, res
299          assert_kind_of Net::HTTPResponse, res.header
300        end
301
302        unless self.is_a?(TestNetHTTP_v1_2_chunked)
303          assert_not_nil res['content-length']
304        end
305        assert_equal $test_net_http_data_type, res['Content-Type']
306        assert_kind_of String, res.body
307        assert_kind_of String, res.entity
308        assert_equal $test_net_http_data.size, res.body.size
309        assert_equal $test_net_http_data, res.body
310        assert_equal $test_net_http_data, res.entity
311      }
312    }
313  end
314
315  def test_post
316    start {|http|
317      _test_post__base http
318      _test_post__file http
319      _test_post__no_data http
320    }
321  end
322
323  def _test_post__base(http)
324    uheader = {}
325    uheader['Accept'] = 'application/octet-stream'
326    uheader['Content-Type'] = 'application/x-www-form-urlencoded'
327    data = 'post data'
328    res = http.post('/', data, uheader)
329    assert_kind_of Net::HTTPResponse, res
330    assert_kind_of String, res.body
331    assert_equal data, res.body
332    assert_equal data, res.entity
333  end
334
335  def _test_post__file(http)
336    data = 'post data'
337    f = StringIO.new
338    http.post('/', data, {'content-type' => 'application/x-www-form-urlencoded'}, f)
339    assert_equal data, f.string
340  end
341
342  def _test_post__no_data(http)
343    unless self.is_a?(TestNetHTTP_v1_2_chunked)
344      EnvUtil.suppress_warning do
345        data = nil
346        res = http.post('/', data)
347        assert_not_equal '411', res.code
348      end
349    end
350  end
351
352  def test_s_post_form
353    url = "http://#{config('host')}:#{config('port')}/"
354    res = Net::HTTP.post_form(
355              URI.parse(url),
356              "a" => "x")
357    assert_equal ["a=x"], res.body.split(/[;&]/).sort
358
359    res = Net::HTTP.post_form(
360              URI.parse(url),
361              "a" => "x",
362              "b" => "y")
363    assert_equal ["a=x", "b=y"], res.body.split(/[;&]/).sort
364
365    res = Net::HTTP.post_form(
366              URI.parse(url),
367              "a" => ["x1", "x2"],
368              "b" => "y")
369    assert_equal url, res['X-request-uri']
370    assert_equal ["a=x1", "a=x2", "b=y"], res.body.split(/[;&]/).sort
371
372    res = Net::HTTP.post_form(
373              URI.parse(url + '?a=x'),
374              "b" => "y")
375    assert_equal url + '?a=x', res['X-request-uri']
376    assert_equal ["b=y"], res.body.split(/[;&]/).sort
377  end
378
379  def test_patch
380    start {|http|
381      _test_patch__base http
382    }
383  end
384
385  def _test_patch__base(http)
386    uheader = {}
387    uheader['Accept'] = 'application/octet-stream'
388    uheader['Content-Type'] = 'application/x-www-form-urlencoded'
389    data = 'patch data'
390    res = http.patch('/', data, uheader)
391    assert_kind_of Net::HTTPResponse, res
392    assert_kind_of String, res.body
393    assert_equal data, res.body
394    assert_equal data, res.entity
395  end
396
397  def test_timeout_during_HTTP_session
398    bug4246 = "expected the HTTP session to have timed out but have not. c.f. [ruby-core:34203]"
399
400    # listen for connections... but deliberately do not read
401    TCPServer.open('localhost', 0) {|server|
402      port = server.addr[1]
403
404      conn = Net::HTTP.new('localhost', port)
405      conn.read_timeout = 0.01
406      conn.open_timeout = 0.01
407
408      th = Thread.new do
409        assert_raise(Net::ReadTimeout) {
410          conn.get('/')
411        }
412      end
413      assert th.join(10), bug4246
414    }
415  end
416end
417
418
419module TestNetHTTP_version_1_2_methods
420
421  def test_request
422    start {|http|
423      _test_request__GET http
424      _test_request__accept_encoding http
425      _test_request__file http
426      # _test_request__range http   # WEBrick does not support Range: header.
427      _test_request__HEAD http
428      _test_request__POST http
429      _test_request__stream_body http
430      _test_request__uri http
431      _test_request__uri_host http
432    }
433  end
434
435  def _test_request__GET(http)
436    req = Net::HTTP::Get.new('/')
437    http.request(req) {|res|
438      assert_kind_of Net::HTTPResponse, res
439      assert_kind_of String, res.body
440      unless self.is_a?(TestNetHTTP_v1_2_chunked)
441        assert_not_nil res['content-length']
442        assert_equal $test_net_http_data.size, res['content-length'].to_i
443      end
444      assert_equal $test_net_http_data.size, res.body.size
445      assert_equal $test_net_http_data, res.body
446
447      assert res.decode_content, 'Bug #7831' if Net::HTTP::HAVE_ZLIB
448    }
449  end
450
451  def _test_request__accept_encoding(http)
452    req = Net::HTTP::Get.new('/', 'accept-encoding' => 'deflate')
453    http.request(req) {|res|
454      assert_kind_of Net::HTTPResponse, res
455      assert_kind_of String, res.body
456      unless self.is_a?(TestNetHTTP_v1_2_chunked)
457        assert_not_nil res['content-length']
458        assert_equal $test_net_http_data.size, res['content-length'].to_i
459      end
460      assert_equal $test_net_http_data.size, res.body.size
461      assert_equal $test_net_http_data, res.body
462
463      refute res.decode_content, 'Bug #7831' if Net::HTTP::HAVE_ZLIB
464    }
465  end
466
467  def _test_request__file(http)
468    req = Net::HTTP::Get.new('/')
469    http.request(req) {|res|
470      assert_kind_of Net::HTTPResponse, res
471      unless self.is_a?(TestNetHTTP_v1_2_chunked)
472        assert_not_nil res['content-length']
473        assert_equal $test_net_http_data.size, res['content-length'].to_i
474      end
475      f = StringIO.new("".force_encoding("ASCII-8BIT"))
476      res.read_body f
477      assert_equal $test_net_http_data.bytesize, f.string.bytesize
478      assert_equal $test_net_http_data.encoding, f.string.encoding
479      assert_equal $test_net_http_data, f.string
480    }
481  end
482
483  def _test_request__range(http)
484    req = Net::HTTP::Get.new('/')
485    req['range'] = 'bytes=0-5'
486    assert_equal $test_net_http_data[0,6], http.request(req).body
487  end
488
489  def _test_request__HEAD(http)
490    req = Net::HTTP::Head.new('/')
491    http.request(req) {|res|
492      assert_kind_of Net::HTTPResponse, res
493      unless self.is_a?(TestNetHTTP_v1_2_chunked)
494        assert_not_nil res['content-length']
495        assert_equal $test_net_http_data.size, res['content-length'].to_i
496      end
497      assert_nil res.body
498    }
499  end
500
501  def _test_request__POST(http)
502    data = 'post data'
503    req = Net::HTTP::Post.new('/')
504    req['Accept'] = $test_net_http_data_type
505    req['Content-Type'] = 'application/x-www-form-urlencoded'
506    http.request(req, data) {|res|
507      assert_kind_of Net::HTTPResponse, res
508      unless self.is_a?(TestNetHTTP_v1_2_chunked)
509        assert_equal data.size, res['content-length'].to_i
510      end
511      assert_kind_of String, res.body
512      assert_equal data, res.body
513    }
514  end
515
516  def _test_request__stream_body(http)
517    req = Net::HTTP::Post.new('/')
518    data = $test_net_http_data
519    req.content_length = data.size
520    req['Content-Type'] = 'application/x-www-form-urlencoded'
521    req.body_stream = StringIO.new(data)
522    res = http.request(req)
523    assert_kind_of Net::HTTPResponse, res
524    assert_kind_of String, res.body
525    assert_equal data.size, res.body.size
526    assert_equal data, res.body
527  end
528
529  def _test_request__path(http)
530    uri = URI 'https://example/'
531    req = Net::HTTP::Get.new('/')
532
533    res = http.request(req)
534
535    assert_kind_of URI::Generic, req.uri
536
537    refute_equal uri, req.uri
538
539    assert_equal uri, res.uri
540
541    refute_same uri,     req.uri
542    refute_same req.uri, res.uri
543  end
544
545  def _test_request__uri(http)
546    uri = URI 'https://example/'
547    req = Net::HTTP::Get.new(uri)
548
549    res = http.request(req)
550
551    assert_kind_of URI::Generic, req.uri
552
553    refute_equal uri, req.uri
554
555    assert_equal req.uri, res.uri
556
557    refute_same uri,     req.uri
558    refute_same req.uri, res.uri
559  end
560
561  def _test_request__uri_host(http)
562    uri = URI 'http://example/'
563
564    req = Net::HTTP::Get.new(uri)
565    req['host'] = 'other.example'
566
567    res = http.request(req)
568
569    assert_kind_of URI::Generic, req.uri
570
571    assert_equal URI("http://example:#{http.port}"), res.uri
572  end
573
574  def test_send_request
575    start {|http|
576      _test_send_request__GET http
577      _test_send_request__POST http
578    }
579  end
580
581  def _test_send_request__GET(http)
582    res = http.send_request('GET', '/')
583    assert_kind_of Net::HTTPResponse, res
584    unless self.is_a?(TestNetHTTP_v1_2_chunked)
585      assert_equal $test_net_http_data.size, res['content-length'].to_i
586    end
587    assert_kind_of String, res.body
588    assert_equal $test_net_http_data, res.body
589  end
590
591  def _test_send_request__POST(http)
592    data = 'aaabbb cc ddddddddddd lkjoiu4j3qlkuoa'
593    res = http.send_request('POST', '/', data, 'content-type' => 'application/x-www-form-urlencoded')
594    assert_kind_of Net::HTTPResponse, res
595    assert_kind_of String, res.body
596    assert_equal data.size, res.body.size
597    assert_equal data, res.body
598  end
599
600  def test_set_form
601    require 'tempfile'
602    file = Tempfile.new('ruby-test')
603    file << "\u{30c7}\u{30fc}\u{30bf}"
604    data = [
605      ['name', 'Gonbei Nanashi'],
606      ['name', "\u{540d}\u{7121}\u{3057}\u{306e}\u{6a29}\u{5175}\u{885b}"],
607      ['s"i\o', StringIO.new("\u{3042 3044 4e9c 925b}")],
608      ["file", file, filename: "ruby-test"]
609    ]
610    expected = <<"__EOM__".gsub(/\n/, "\r\n")
611--<boundary>
612Content-Disposition: form-data; name="name"
613
614Gonbei Nanashi
615--<boundary>
616Content-Disposition: form-data; name="name"
617
618\xE5\x90\x8D\xE7\x84\xA1\xE3\x81\x97\xE3\x81\xAE\xE6\xA8\xA9\xE5\x85\xB5\xE8\xA1\x9B
619--<boundary>
620Content-Disposition: form-data; name="s\\"i\\\\o"
621
622\xE3\x81\x82\xE3\x81\x84\xE4\xBA\x9C\xE9\x89\x9B
623--<boundary>
624Content-Disposition: form-data; name="file"; filename="ruby-test"
625Content-Type: application/octet-stream
626
627\xE3\x83\x87\xE3\x83\xBC\xE3\x82\xBF
628--<boundary>--
629__EOM__
630    start {|http|
631      _test_set_form_urlencoded(http, data.reject{|k,v|!v.is_a?(String)})
632      _test_set_form_multipart(http, false, data, expected)
633      _test_set_form_multipart(http, true, data, expected)
634    }
635  ensure
636    file.close! if file
637  end
638
639  def _test_set_form_urlencoded(http, data)
640    req = Net::HTTP::Post.new('/')
641    req.set_form(data)
642    res = http.request req
643    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
644  end
645
646  def _test_set_form_multipart(http, chunked_p, data, expected)
647    data.each{|k,v|v.rewind rescue nil}
648    req = Net::HTTP::Post.new('/')
649    req.set_form(data, 'multipart/form-data')
650    req['Transfer-Encoding'] = 'chunked' if chunked_p
651    res = http.request req
652    body = res.body
653    assert_match(/\A--(?<boundary>\S+)/, body)
654    /\A--(?<boundary>\S+)/ =~ body
655    expected = expected.gsub(/<boundary>/, boundary)
656    assert_equal(expected, body)
657  end
658
659  def test_set_form_with_file
660    require 'tempfile'
661    file = Tempfile.new('ruby-test')
662    file.binmode
663    file << $test_net_http_data
664    filename = File.basename(file.to_path)
665    data = [['file', file]]
666    expected = <<"__EOM__".gsub(/\n/, "\r\n")
667--<boundary>
668Content-Disposition: form-data; name="file"; filename="<filename>"
669Content-Type: application/octet-stream
670
671<data>
672--<boundary>--
673__EOM__
674    expected.sub!(/<filename>/, filename)
675    expected.sub!(/<data>/, $test_net_http_data)
676    start {|http|
677      data.each{|k,v|v.rewind rescue nil}
678      req = Net::HTTP::Post.new('/')
679      req.set_form(data, 'multipart/form-data')
680      res = http.request req
681      body = res.body
682      header, _ = body.split(/\r\n\r\n/, 2)
683      assert_match(/\A--(?<boundary>\S+)/, body)
684      /\A--(?<boundary>\S+)/ =~ body
685      expected = expected.gsub(/<boundary>/, boundary)
686      assert_match(/^--(?<boundary>\S+)\r\n/, header)
687      assert_match(
688        /^Content-Disposition: form-data; name="file"; filename="#{filename}"\r\n/,
689        header)
690      assert_equal(expected, body)
691
692      data.each{|k,v|v.rewind rescue nil}
693      req['Transfer-Encoding'] = 'chunked'
694      res = http.request req
695      #assert_equal(expected, res.body)
696    }
697  ensure
698    file.close! if file
699  end
700end
701
702class TestNetHTTP_v1_2 < Test::Unit::TestCase
703  CONFIG = {
704    'host' => '127.0.0.1',
705    'port' => 0,
706    'proxy_host' => nil,
707    'proxy_port' => nil,
708  }
709
710  include TestNetHTTPUtils
711  include TestNetHTTP_version_1_1_methods
712  include TestNetHTTP_version_1_2_methods
713
714  def new
715    Net::HTTP.version_1_2
716    super
717  end
718end
719
720class TestNetHTTP_v1_2_chunked < Test::Unit::TestCase
721  CONFIG = {
722    'host' => '127.0.0.1',
723    'port' => 0,
724    'proxy_host' => nil,
725    'proxy_port' => nil,
726    'chunked' => true,
727  }
728
729  include TestNetHTTPUtils
730  include TestNetHTTP_version_1_1_methods
731  include TestNetHTTP_version_1_2_methods
732
733  def new
734    Net::HTTP.version_1_2
735    super
736  end
737
738  def test_chunked_break
739    assert_nothing_raised("[ruby-core:29229]") {
740      start {|http|
741        http.request_get('/') {|res|
742          res.read_body {|chunk|
743            break
744          }
745        }
746      }
747    }
748  end
749end
750
751class TestNetHTTPContinue < Test::Unit::TestCase
752  CONFIG = {
753    'host' => '127.0.0.1',
754    'port' => 0,
755    'proxy_host' => nil,
756    'proxy_port' => nil,
757    'chunked' => true,
758  }
759
760  include TestNetHTTPUtils
761
762  def logfile
763    @debug = StringIO.new('')
764  end
765
766  def mount_proc(&block)
767    @server.mount('/continue', WEBrick::HTTPServlet::ProcHandler.new(block.to_proc))
768  end
769
770  def test_expect_continue
771    mount_proc {|req, res|
772      req.continue
773      res.body = req.query['body']
774    }
775    start {|http|
776      uheader = {'content-type' => 'application/x-www-form-urlencoded', 'expect' => '100-continue'}
777      http.continue_timeout = 0.2
778      http.request_post('/continue', 'body=BODY', uheader) {|res|
779        assert_equal('BODY', res.read_body)
780      }
781    }
782    assert_match(/Expect: 100-continue/, @debug.string)
783    assert_match(/HTTP\/1.1 100 continue/, @debug.string)
784  end
785
786  def test_expect_continue_timeout
787    mount_proc {|req, res|
788      sleep 0.2
789      req.continue # just ignored because it's '100'
790      res.body = req.query['body']
791    }
792    start {|http|
793      uheader = {'content-type' => 'application/x-www-form-urlencoded', 'expect' => '100-continue'}
794      http.continue_timeout = 0
795      http.request_post('/continue', 'body=BODY', uheader) {|res|
796        assert_equal('BODY', res.read_body)
797      }
798    }
799    assert_match(/Expect: 100-continue/, @debug.string)
800    assert_match(/HTTP\/1.1 100 continue/, @debug.string)
801  end
802
803  def test_expect_continue_error
804    mount_proc {|req, res|
805      res.status = 501
806      res.body = req.query['body']
807    }
808    start {|http|
809      uheader = {'content-type' => 'application/x-www-form-urlencoded', 'expect' => '100-continue'}
810      http.continue_timeout = 0
811      http.request_post('/continue', 'body=ERROR', uheader) {|res|
812        assert_equal('ERROR', res.read_body)
813      }
814    }
815    assert_match(/Expect: 100-continue/, @debug.string)
816    assert_not_match(/HTTP\/1.1 100 continue/, @debug.string)
817  end
818
819  def test_expect_continue_error_while_waiting
820    mount_proc {|req, res|
821      res.status = 501
822      res.body = req.query['body']
823    }
824    start {|http|
825      uheader = {'content-type' => 'application/x-www-form-urlencoded', 'expect' => '100-continue'}
826      http.continue_timeout = 0.5
827      http.request_post('/continue', 'body=ERROR', uheader) {|res|
828        assert_equal('ERROR', res.read_body)
829      }
830    }
831    assert_match(/Expect: 100-continue/, @debug.string)
832    assert_not_match(/HTTP\/1.1 100 continue/, @debug.string)
833  end
834end
835
836class TestNetHTTPKeepAlive < Test::Unit::TestCase
837  CONFIG = {
838    'host' => '127.0.0.1',
839    'port' => 0,
840    'proxy_host' => nil,
841    'proxy_port' => nil,
842    'RequestTimeout' => 1,
843  }
844
845  include TestNetHTTPUtils
846
847  def test_keep_alive_get_auto_reconnect
848    start {|http|
849      res = http.get('/')
850      http.keep_alive_timeout = 1
851      assert_kind_of Net::HTTPResponse, res
852      assert_kind_of String, res.body
853      sleep 1.5
854      assert_nothing_raised {
855        res = http.get('/')
856      }
857      assert_kind_of Net::HTTPResponse, res
858      assert_kind_of String, res.body
859    }
860  end
861
862  def test_keep_alive_get_auto_retry
863    start {|http|
864      res = http.get('/')
865      http.keep_alive_timeout = 5
866      assert_kind_of Net::HTTPResponse, res
867      assert_kind_of String, res.body
868      sleep 1.5
869      res = http.get('/')
870      assert_kind_of Net::HTTPResponse, res
871      assert_kind_of String, res.body
872    }
873  end
874
875  def test_keep_alive_server_close
876    def @server.run(sock)
877      sock.close
878    end
879
880    start {|http|
881      assert_raises(EOFError, Errno::ECONNRESET, IOError) {
882        http.get('/')
883      }
884    }
885  end
886end
887
888class TestNetHTTPLocalBind < Test::Unit::TestCase
889  CONFIG = {
890    'host' => 'localhost',
891    'port' => 0,
892    'proxy_host' => nil,
893    'proxy_port' => nil,
894  }
895
896  include TestNetHTTPUtils
897
898  def test_bind_to_local_host
899    @server.mount_proc('/show_ip') { |req, res| res.body = req.remote_ip }
900
901    http = Net::HTTP.new(config('host'), config('port'))
902    http.local_host = Addrinfo.tcp(config('host'), config('port')).ip_address
903    assert_not_nil(http.local_host)
904    assert_nil(http.local_port)
905
906    res = http.get('/show_ip')
907    assert_equal(http.local_host, res.body)
908  end
909
910  def test_bind_to_local_port
911    @server.mount_proc('/show_port') { |req, res| res.body = req.peeraddr[1].to_s }
912
913    http = Net::HTTP.new(config('host'), config('port'))
914    http.local_host = Addrinfo.tcp(config('host'), config('port')).ip_address
915    http.local_port = [*10000..20000].shuffle.first.to_s
916    assert_not_nil(http.local_host)
917    assert_not_nil(http.local_port)
918
919    res = http.get('/show_port')
920    assert_equal(http.local_port, res.body)
921  end
922end
923
924