glebtv-httpclient 3.1.1 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.coveralls.yml +1 -0
- data/.gitignore +8 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +6 -0
- data/CHANGELOG.rdoc +653 -0
- data/Gemfile +12 -0
- data/Gemfile.lock +284 -0
- data/README.md +7 -2
- data/Rakefile +22 -0
- data/bin/httpclient +1 -1
- data/dist_key/cacerts.pem +1808 -0
- data/dist_key/cert.pem +24 -0
- data/dist_key/gen_dist_cert.rb +29 -0
- data/httpclient.gemspec +33 -0
- data/lib/httpclient.rb +59 -15
- data/lib/httpclient/lru_cache.rb +171 -0
- data/lib/httpclient/lru_threadsafe.rb +29 -0
- data/lib/httpclient/session.rb +52 -13
- data/lib/httpclient/ssl_config.rb +4 -3
- data/lib/httpclient/version.rb +1 -1
- data/sample/ssl/trust_certs/.keep_me +0 -0
- data/spec/http_message_spec.rb +124 -0
- data/spec/httpclient_spec.rb +322 -0
- data/spec/keepalive_spec.rb +129 -0
- data/spec/spec_helper.rb +40 -0
- data/spec/support/1024x768.gif +0 -0
- data/spec/support/1x1.png +0 -0
- data/spec/support/base_server.rb +36 -0
- data/spec/support/file.txt +1 -0
- data/spec/support/ht_helpers.rb +10 -0
- data/spec/support/main_server.rb +155 -0
- data/spec/support/proxy_server.rb +14 -0
- data/spec/support/test_servlet.rb +73 -0
- data/stress-test/Gemfile +4 -0
- data/stress-test/Gemfile.lock +16 -0
- data/stress-test/client.rb +72 -0
- data/stress-test/config.ru +4 -0
- data/stress-test/unicorn.conf +2 -0
- data/test.rb +19 -0
- data/test/helper.rb +4 -3
- data/test/test_httpclient.rb +19 -677
- metadata +226 -38
- data/lib/httpclient/timeout.rb +0 -140
- data/test/runner.rb +0 -2
data/stress-test/Gemfile
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
lib = File.expand_path('../../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'httpclient'
|
5
|
+
|
6
|
+
require 'thread'
|
7
|
+
|
8
|
+
threads = []
|
9
|
+
# clnt = HTTPClient.new()
|
10
|
+
|
11
|
+
# 20.times do
|
12
|
+
# threads << Thread.new do
|
13
|
+
# loop do
|
14
|
+
# clnt.get("http://localhost:7000/#{rand(1000..10000).to_s}.html") do |str|
|
15
|
+
# puts str.length if str.length != 1300000
|
16
|
+
# end
|
17
|
+
# end
|
18
|
+
# end
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# clnt = HTTPClient.new()
|
22
|
+
# 10.times do
|
23
|
+
# threads << Thread.new do
|
24
|
+
# loop do
|
25
|
+
# begin
|
26
|
+
# clnt.get("http://#{rand(10000...200000)}.test.local/test.htm") do |str|
|
27
|
+
# puts str.length if str.length != 1300000
|
28
|
+
# end
|
29
|
+
# rescue
|
30
|
+
# end
|
31
|
+
# end
|
32
|
+
# end
|
33
|
+
# end
|
34
|
+
|
35
|
+
20.times do
|
36
|
+
threads << Thread.new do
|
37
|
+
loop do
|
38
|
+
clnt = HTTPClient.new()
|
39
|
+
clnt.get("http://localhost:7000/#{rand(1000..10000).to_s}.html") do |str|
|
40
|
+
puts str.length if str.length != 1300000
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def ostats(last_stat = nil)
|
47
|
+
stats = Hash.new(0)
|
48
|
+
ObjectSpace.each_object {|o| stats[o.class] += 1}
|
49
|
+
|
50
|
+
stats.sort {|(k1,v1),(k2,v2)| v2 <=> v1}.each do |k,v|
|
51
|
+
next if v < 25
|
52
|
+
printf "%-30s %10d", k, v
|
53
|
+
printf " | delta %10d", (v - last_stat[k]) if last_stat
|
54
|
+
puts
|
55
|
+
end
|
56
|
+
|
57
|
+
stats
|
58
|
+
end
|
59
|
+
|
60
|
+
mstat = nil
|
61
|
+
|
62
|
+
threads << Thread.new do
|
63
|
+
loop do
|
64
|
+
mstat = ostats(mstat)
|
65
|
+
puts '-' * 80
|
66
|
+
sleep 1
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
threads.each do |t|
|
71
|
+
t.join
|
72
|
+
end
|
data/test.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$:.unshift(File.join('.', 'lib'))
|
4
|
+
require 'httpclient'
|
5
|
+
|
6
|
+
clnt = HTTPClient.new()
|
7
|
+
site = ARGV.shift
|
8
|
+
threads = []
|
9
|
+
15.times do
|
10
|
+
threads << Thread.new do
|
11
|
+
loop do
|
12
|
+
puts "GET #{site}"
|
13
|
+
cnt = clnt.get_content site
|
14
|
+
sleep 1
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
threads.map(&:join)
|
data/test/helper.rb
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
|
-
require
|
2
|
+
require "test/unit"
|
3
|
+
|
3
4
|
begin
|
4
5
|
require 'simplecov'
|
5
|
-
require 'simplecov-rcov'
|
6
|
-
SimpleCov.formatter = SimpleCov::Formatter::RcovFormatter
|
6
|
+
# require 'simplecov-rcov'
|
7
|
+
# SimpleCov.formatter = SimpleCov::Formatter::RcovFormatter
|
7
8
|
SimpleCov.start
|
8
9
|
rescue LoadError
|
9
10
|
end
|
data/test/test_httpclient.rb
CHANGED
@@ -64,56 +64,6 @@ class TestHTTPClient < Test::Unit::TestCase
|
|
64
64
|
assert(!str.empty?)
|
65
65
|
end
|
66
66
|
|
67
|
-
def test_protocol_version_http09
|
68
|
-
@client.protocol_version = 'HTTP/0.9'
|
69
|
-
@client.debug_dev = str = ''
|
70
|
-
@client.test_loopback_http_response << "hello\nworld\n"
|
71
|
-
res = @client.get(serverurl + 'hello')
|
72
|
-
assert_equal('0.9', res.http_version)
|
73
|
-
assert_equal(nil, res.status)
|
74
|
-
assert_equal(nil, res.reason)
|
75
|
-
assert_equal("hello\nworld\n", res.content)
|
76
|
-
lines = str.split(/(?:\r?\n)+/)
|
77
|
-
assert_equal("= Request", lines[0])
|
78
|
-
assert_equal("! CONNECTION ESTABLISHED", lines[2])
|
79
|
-
assert_equal("GET /hello HTTP/0.9", lines[3])
|
80
|
-
assert_equal("Connection: close", lines[7])
|
81
|
-
assert_equal("= Response", lines[8])
|
82
|
-
assert_match(/^hello$/, lines[9])
|
83
|
-
assert_match(/^world$/, lines[10])
|
84
|
-
end
|
85
|
-
|
86
|
-
def test_protocol_version_http10
|
87
|
-
assert_equal(nil, @client.protocol_version)
|
88
|
-
@client.protocol_version = 'HTTP/1.0'
|
89
|
-
assert_equal('HTTP/1.0', @client.protocol_version)
|
90
|
-
str = ""
|
91
|
-
@client.debug_dev = str
|
92
|
-
@client.get(serverurl + 'hello')
|
93
|
-
lines = str.split(/(?:\r?\n)+/)
|
94
|
-
assert_equal("= Request", lines[0])
|
95
|
-
assert_equal("! CONNECTION ESTABLISHED", lines[2])
|
96
|
-
assert_equal("GET /hello HTTP/1.0", lines[3])
|
97
|
-
assert_equal("Connection: close", lines[7])
|
98
|
-
assert_equal("= Response", lines[8])
|
99
|
-
end
|
100
|
-
|
101
|
-
def test_header_accept_by_default
|
102
|
-
str = ""
|
103
|
-
@client.debug_dev = str
|
104
|
-
@client.get(serverurl)
|
105
|
-
lines = str.split(/(?:\r?\n)+/)
|
106
|
-
assert_equal("Accept: */*", lines[5])
|
107
|
-
end
|
108
|
-
|
109
|
-
def test_header_accept
|
110
|
-
str = ""
|
111
|
-
@client.debug_dev = str
|
112
|
-
@client.get(serverurl, :header => {:Accept => 'text/html'})
|
113
|
-
lines = str.split(/(?:\r?\n)+/)
|
114
|
-
assert_equal("Accept: text/html", lines[4])
|
115
|
-
end
|
116
|
-
|
117
67
|
def test_host_given
|
118
68
|
str = ""
|
119
69
|
@client.debug_dev = str
|
@@ -143,35 +93,6 @@ class TestHTTPClient < Test::Unit::TestCase
|
|
143
93
|
end
|
144
94
|
end
|
145
95
|
|
146
|
-
def test_protocol_version_http11
|
147
|
-
assert_equal(nil, @client.protocol_version)
|
148
|
-
str = ""
|
149
|
-
@client.debug_dev = str
|
150
|
-
@client.get(serverurl)
|
151
|
-
lines = str.split(/(?:\r?\n)+/)
|
152
|
-
assert_equal("= Request", lines[0])
|
153
|
-
assert_equal("! CONNECTION ESTABLISHED", lines[2])
|
154
|
-
assert_equal("GET / HTTP/1.1", lines[3])
|
155
|
-
assert_equal("Host: localhost:#{serverport}", lines[7])
|
156
|
-
@client.protocol_version = 'HTTP/1.1'
|
157
|
-
assert_equal('HTTP/1.1', @client.protocol_version)
|
158
|
-
str = ""
|
159
|
-
@client.debug_dev = str
|
160
|
-
@client.get(serverurl)
|
161
|
-
lines = str.split(/(?:\r?\n)+/)
|
162
|
-
assert_equal("= Request", lines[0])
|
163
|
-
assert_equal("! CONNECTION ESTABLISHED", lines[2])
|
164
|
-
assert_equal("GET / HTTP/1.1", lines[3])
|
165
|
-
@client.protocol_version = 'HTTP/1.0'
|
166
|
-
str = ""
|
167
|
-
@client.debug_dev = str
|
168
|
-
@client.get(serverurl)
|
169
|
-
lines = str.split(/(?:\r?\n)+/)
|
170
|
-
assert_equal("= Request", lines[0])
|
171
|
-
assert_equal("! CONNECTION ESTABLISHED", lines[2])
|
172
|
-
assert_equal("GET / HTTP/1.0", lines[3])
|
173
|
-
end
|
174
|
-
|
175
96
|
def test_proxy
|
176
97
|
setup_proxyserver
|
177
98
|
escape_noproxy do
|
@@ -526,104 +447,6 @@ EOS
|
|
526
447
|
end
|
527
448
|
end
|
528
449
|
|
529
|
-
def test_get_content
|
530
|
-
assert_equal('hello', @client.get_content(serverurl + 'hello'))
|
531
|
-
assert_equal('hello', @client.get_content(serverurl + 'redirect1'))
|
532
|
-
assert_equal('hello', @client.get_content(serverurl + 'redirect2'))
|
533
|
-
url = serverurl.sub(/localhost/, '127.0.0.1')
|
534
|
-
assert_equal('hello', @client.get_content(url + 'hello'))
|
535
|
-
assert_equal('hello', @client.get_content(url + 'redirect1'))
|
536
|
-
assert_equal('hello', @client.get_content(url + 'redirect2'))
|
537
|
-
@client.reset(serverurl)
|
538
|
-
@client.reset(url)
|
539
|
-
@client.reset(serverurl)
|
540
|
-
@client.reset(url)
|
541
|
-
assert_raises(HTTPClient::BadResponseError) do
|
542
|
-
@client.get_content(serverurl + 'notfound')
|
543
|
-
end
|
544
|
-
assert_raises(HTTPClient::BadResponseError) do
|
545
|
-
@client.get_content(serverurl + 'redirect_self')
|
546
|
-
end
|
547
|
-
called = false
|
548
|
-
@client.redirect_uri_callback = lambda { |uri, res|
|
549
|
-
newuri = res.header['location'][0]
|
550
|
-
called = true
|
551
|
-
newuri
|
552
|
-
}
|
553
|
-
assert_equal('hello', @client.get_content(serverurl + 'relative_redirect'))
|
554
|
-
assert(called)
|
555
|
-
end
|
556
|
-
|
557
|
-
GZIP_CONTENT = "\x1f\x8b\x08\x00\x1a\x96\xe0\x4c\x00\x03\xcb\x48\xcd\xc9\xc9\x07\x00\x86\xa6\x10\x36\x05\x00\x00\x00"
|
558
|
-
DEFLATE_CONTENT = "\x78\x9c\xcb\x48\xcd\xc9\xc9\x07\x00\x06\x2c\x02\x15"
|
559
|
-
GZIP_CONTENT.force_encoding('BINARY') if GZIP_CONTENT.respond_to?(:force_encoding)
|
560
|
-
DEFLATE_CONTENT.force_encoding('BINARY') if DEFLATE_CONTENT.respond_to?(:force_encoding)
|
561
|
-
def test_get_gzipped_content
|
562
|
-
@client.transparent_gzip_decompression = false
|
563
|
-
content = @client.get_content(serverurl + 'compressed?enc=gzip')
|
564
|
-
assert_not_equal('hello', content)
|
565
|
-
assert_equal(GZIP_CONTENT, content)
|
566
|
-
@client.transparent_gzip_decompression = true
|
567
|
-
assert_equal('hello', @client.get_content(serverurl + 'compressed?enc=gzip'))
|
568
|
-
assert_equal('hello', @client.get_content(serverurl + 'compressed?enc=deflate'))
|
569
|
-
@client.transparent_gzip_decompression = false
|
570
|
-
end
|
571
|
-
|
572
|
-
def test_get_content_with_block
|
573
|
-
@client.get_content(serverurl + 'hello') do |str|
|
574
|
-
assert_equal('hello', str)
|
575
|
-
end
|
576
|
-
@client.get_content(serverurl + 'redirect1') do |str|
|
577
|
-
assert_equal('hello', str)
|
578
|
-
end
|
579
|
-
@client.get_content(serverurl + 'redirect2') do |str|
|
580
|
-
assert_equal('hello', str)
|
581
|
-
end
|
582
|
-
end
|
583
|
-
|
584
|
-
def test_post_content
|
585
|
-
assert_equal('hello', @client.post_content(serverurl + 'hello'))
|
586
|
-
assert_equal('hello', @client.post_content(serverurl + 'redirect1'))
|
587
|
-
assert_equal('hello', @client.post_content(serverurl + 'redirect2'))
|
588
|
-
assert_raises(HTTPClient::BadResponseError) do
|
589
|
-
@client.post_content(serverurl + 'notfound')
|
590
|
-
end
|
591
|
-
assert_raises(HTTPClient::BadResponseError) do
|
592
|
-
@client.post_content(serverurl + 'redirect_self')
|
593
|
-
end
|
594
|
-
called = false
|
595
|
-
@client.redirect_uri_callback = lambda { |uri, res|
|
596
|
-
newuri = res.header['location'][0]
|
597
|
-
called = true
|
598
|
-
newuri
|
599
|
-
}
|
600
|
-
assert_equal('hello', @client.post_content(serverurl + 'relative_redirect'))
|
601
|
-
assert(called)
|
602
|
-
end
|
603
|
-
|
604
|
-
def test_post_content_io
|
605
|
-
post_body = StringIO.new("1234567890")
|
606
|
-
assert_equal('post,1234567890', @client.post_content(serverurl + 'servlet', post_body))
|
607
|
-
|
608
|
-
# all browsers use GET for 302
|
609
|
-
post_body = StringIO.new("1234567890")
|
610
|
-
assert_equal('1234567890', @client.post_content(serverurl + 'servlet_413', post_body))
|
611
|
-
|
612
|
-
assert_equal('', @client.get_content(serverurl + 'servlet_redirect_413'))
|
613
|
-
post_body = StringIO.new("1234567890")
|
614
|
-
assert_equal('', @client.post_content(serverurl + 'servlet_redirect_413', post_body))
|
615
|
-
|
616
|
-
|
617
|
-
post_body = StringIO.new("1234567890")
|
618
|
-
assert_equal('post,1234567890', @client.post_content(serverurl + 'servlet_temporary_redirect', post_body))
|
619
|
-
post_body = StringIO.new("1234567890")
|
620
|
-
assert_equal('get', @client.post_content(serverurl + 'servlet_see_other', post_body))
|
621
|
-
#
|
622
|
-
post_body = StringIO.new("1234567890")
|
623
|
-
post_body.read(5)
|
624
|
-
assert_equal('post,67890', @client.post_content(serverurl + 'servlet_temporary_redirect', post_body))
|
625
|
-
end
|
626
|
-
|
627
450
|
def test_head
|
628
451
|
assert_equal("head", @client.head(serverurl + 'servlet').header["x-head"][0])
|
629
452
|
param = {'1'=>'2', '3'=>'4'}
|
@@ -888,7 +711,24 @@ EOS
|
|
888
711
|
assert_equal(param, params(res.header["x-query"][0]))
|
889
712
|
end
|
890
713
|
|
891
|
-
def
|
714
|
+
def test_patch
|
715
|
+
assert_equal("patch", @client.patch(serverurl + 'servlet').content)
|
716
|
+
param = {'1'=>'2', '3'=>'4'}
|
717
|
+
@client.debug_dev = str = ''
|
718
|
+
res = @client.patch(serverurl + 'servlet', param)
|
719
|
+
assert_equal(param, params(res.header["x-query"][0]))
|
720
|
+
assert_equal('Content-Type: application/x-www-form-urlencoded', str.split(/\r?\n/)[5])
|
721
|
+
end
|
722
|
+
|
723
|
+
def test_patch_async
|
724
|
+
param = {'1'=>'2', '3'=>'4'}
|
725
|
+
conn = @client.patch_async(serverurl + 'servlet', param)
|
726
|
+
Thread.pass while !conn.finished?
|
727
|
+
res = conn.pop
|
728
|
+
assert_equal(param, params(res.header["x-query"][0]))
|
729
|
+
end
|
730
|
+
|
731
|
+
def test_delete
|
892
732
|
assert_equal("delete", @client.delete(serverurl + 'servlet').content)
|
893
733
|
end
|
894
734
|
|
@@ -963,6 +803,7 @@ EOS
|
|
963
803
|
|
964
804
|
def test_chunked
|
965
805
|
assert_equal('chunked', @client.get_content(serverurl + 'chunked', { 'msg' => 'chunked' }))
|
806
|
+
assert_equal('あいうえお', @client.get_content(serverurl + 'chunked', { 'msg' => 'あいうえお' }))
|
966
807
|
end
|
967
808
|
|
968
809
|
def test_chunked_empty
|
@@ -1086,14 +927,6 @@ EOS
|
|
1086
927
|
assert_equal('hello', @client.post(serverurl + 'sleep', :sec => 2).content)
|
1087
928
|
end
|
1088
929
|
|
1089
|
-
# disable for now -- does not work
|
1090
|
-
#def test_async_error
|
1091
|
-
# assert_raise( SocketError ) do
|
1092
|
-
# conn = @client.get_async("http://non-existing-host/")
|
1093
|
-
# conn.pop
|
1094
|
-
# end
|
1095
|
-
#end
|
1096
|
-
|
1097
930
|
def test_reset
|
1098
931
|
url = serverurl + 'servlet'
|
1099
932
|
assert_nothing_raised do
|
@@ -1188,14 +1021,6 @@ EOS
|
|
1188
1021
|
end
|
1189
1022
|
end
|
1190
1023
|
|
1191
|
-
def test_urify
|
1192
|
-
extend HTTPClient::Util
|
1193
|
-
assert_nil(urify(nil))
|
1194
|
-
uri = 'http://foo'
|
1195
|
-
assert_equal(urify(uri), urify(uri))
|
1196
|
-
assert_equal(urify(uri), urify(urify(uri)))
|
1197
|
-
end
|
1198
|
-
|
1199
1024
|
def test_connection
|
1200
1025
|
c = HTTPClient::Connection.new
|
1201
1026
|
assert(c.finished?)
|
@@ -1249,156 +1074,6 @@ EOS
|
|
1249
1074
|
assert_equal([['foo', 'bar'], ['foo', 'bar2']], res.header.get('foo'))
|
1250
1075
|
end
|
1251
1076
|
|
1252
|
-
def test_mime_type
|
1253
|
-
assert_equal('text/plain', HTTP::Message.mime_type('foo.txt'))
|
1254
|
-
assert_equal('text/html', HTTP::Message.mime_type('foo.html'))
|
1255
|
-
assert_equal('text/html', HTTP::Message.mime_type('foo.htm'))
|
1256
|
-
assert_equal('application/msword', HTTP::Message.mime_type('foo.doc'))
|
1257
|
-
assert_equal('image/png', HTTP::Message.mime_type('foo.png'))
|
1258
|
-
assert_equal('image/gif', HTTP::Message.mime_type('foo.gif'))
|
1259
|
-
assert_equal('image/jpeg', HTTP::Message.mime_type('foo.jpg'))
|
1260
|
-
assert_equal('image/jpeg', HTTP::Message.mime_type('foo.jpeg'))
|
1261
|
-
assert_equal('application/octet-stream', HTTP::Message.mime_type('foo.unknown'))
|
1262
|
-
#
|
1263
|
-
handler = lambda { |path| 'hello/world' }
|
1264
|
-
assert_nil(HTTP::Message.mime_type_handler)
|
1265
|
-
assert_nil(HTTP::Message.get_mime_type_func)
|
1266
|
-
HTTP::Message.mime_type_handler = handler
|
1267
|
-
assert_not_nil(HTTP::Message.mime_type_handler)
|
1268
|
-
assert_not_nil(HTTP::Message.get_mime_type_func)
|
1269
|
-
assert_equal('hello/world', HTTP::Message.mime_type('foo.txt'))
|
1270
|
-
HTTP::Message.mime_type_handler = nil
|
1271
|
-
assert_equal('text/plain', HTTP::Message.mime_type('foo.txt'))
|
1272
|
-
HTTP::Message.set_mime_type_func(nil)
|
1273
|
-
assert_equal('text/plain', HTTP::Message.mime_type('foo.txt'))
|
1274
|
-
#
|
1275
|
-
handler = lambda { |path| nil }
|
1276
|
-
HTTP::Message.mime_type_handler = handler
|
1277
|
-
assert_equal('application/octet-stream', HTTP::Message.mime_type('foo.txt'))
|
1278
|
-
end
|
1279
|
-
|
1280
|
-
def test_connect_request
|
1281
|
-
req = HTTP::Message.new_connect_request(urify('https://foo/bar'))
|
1282
|
-
assert_equal("CONNECT foo:443 HTTP/1.0\r\n\r\n", req.dump)
|
1283
|
-
req = HTTP::Message.new_connect_request(urify('https://example.com/'))
|
1284
|
-
assert_equal("CONNECT example.com:443 HTTP/1.0\r\n\r\n", req.dump)
|
1285
|
-
end
|
1286
|
-
|
1287
|
-
def test_response
|
1288
|
-
res = HTTP::Message.new_response('response')
|
1289
|
-
res.contenttype = 'text/plain'
|
1290
|
-
res.header.body_date = Time.at(946652400)
|
1291
|
-
assert_equal(
|
1292
|
-
[
|
1293
|
-
"",
|
1294
|
-
"Content-Length: 8",
|
1295
|
-
"Content-Type: text/plain",
|
1296
|
-
"Last-Modified: Fri, 31 Dec 1999 15:00:00 GMT",
|
1297
|
-
"Status: 200 OK",
|
1298
|
-
"response"
|
1299
|
-
],
|
1300
|
-
res.dump.split(/\r\n/).sort
|
1301
|
-
)
|
1302
|
-
assert_equal(['8'], res.header['Content-Length'])
|
1303
|
-
assert_equal('8', res.headers['Content-Length'])
|
1304
|
-
res.header.set('foo', 'bar')
|
1305
|
-
assert_equal(
|
1306
|
-
[
|
1307
|
-
"",
|
1308
|
-
"Content-Length: 8",
|
1309
|
-
"Content-Type: text/plain",
|
1310
|
-
"Last-Modified: Fri, 31 Dec 1999 15:00:00 GMT",
|
1311
|
-
"Status: 200 OK",
|
1312
|
-
"foo: bar",
|
1313
|
-
"response"
|
1314
|
-
],
|
1315
|
-
res.dump.split(/\r\n/).sort
|
1316
|
-
)
|
1317
|
-
# nil body
|
1318
|
-
res = HTTP::Message.new_response(nil)
|
1319
|
-
assert_equal(
|
1320
|
-
[
|
1321
|
-
"Content-Length: 0",
|
1322
|
-
"Content-Type: text/html; charset=us-ascii",
|
1323
|
-
"Status: 200 OK"
|
1324
|
-
],
|
1325
|
-
res.dump.split(/\r\n/).sort
|
1326
|
-
)
|
1327
|
-
# for mod_ruby env
|
1328
|
-
Object.const_set('Apache', nil)
|
1329
|
-
begin
|
1330
|
-
res = HTTP::Message.new_response('response')
|
1331
|
-
assert(res.dump.split(/\r\n/).any? { |line| /^Date/ =~ line })
|
1332
|
-
#
|
1333
|
-
res = HTTP::Message.new_response('response')
|
1334
|
-
res.contenttype = 'text/plain'
|
1335
|
-
res.header.body_date = Time.at(946652400)
|
1336
|
-
res.header['Date'] = Time.at(946652400).httpdate
|
1337
|
-
assert_equal(
|
1338
|
-
[
|
1339
|
-
"",
|
1340
|
-
"Content-Length: 8",
|
1341
|
-
"Content-Type: text/plain",
|
1342
|
-
"Date: Fri, 31 Dec 1999 15:00:00 GMT",
|
1343
|
-
"HTTP/1.1 200 OK",
|
1344
|
-
"Last-Modified: Fri, 31 Dec 1999 15:00:00 GMT",
|
1345
|
-
"response"
|
1346
|
-
],
|
1347
|
-
res.dump.split(/\r\n/).sort
|
1348
|
-
)
|
1349
|
-
ensure
|
1350
|
-
Object.instance_eval { remove_const('Apache') }
|
1351
|
-
end
|
1352
|
-
end
|
1353
|
-
|
1354
|
-
def test_response_cookies
|
1355
|
-
res = HTTP::Message.new_response('response')
|
1356
|
-
res.contenttype = 'text/plain'
|
1357
|
-
res.header.body_date = Time.at(946652400)
|
1358
|
-
assert_nil(res.cookies)
|
1359
|
-
#
|
1360
|
-
res.header['Set-Cookie'] = [
|
1361
|
-
'CUSTOMER=WILE_E_COYOTE; path=/; expires=Wednesday, 09-Nov-99 23:12:40 GMT',
|
1362
|
-
'PART_NUMBER=ROCKET_LAUNCHER_0001; path=/'
|
1363
|
-
]
|
1364
|
-
assert_equal(
|
1365
|
-
[
|
1366
|
-
"",
|
1367
|
-
"Content-Length: 8",
|
1368
|
-
"Content-Type: text/plain",
|
1369
|
-
"Last-Modified: Fri, 31 Dec 1999 15:00:00 GMT",
|
1370
|
-
"Set-Cookie: CUSTOMER=WILE_E_COYOTE; path=/; expires=Wednesday, 09-Nov-99 23:12:40 GMT",
|
1371
|
-
"Set-Cookie: PART_NUMBER=ROCKET_LAUNCHER_0001; path=/",
|
1372
|
-
"Status: 200 OK",
|
1373
|
-
"response"
|
1374
|
-
],
|
1375
|
-
res.dump.split(/\r\n/).sort
|
1376
|
-
)
|
1377
|
-
assert_equal(2, res.cookies.size)
|
1378
|
-
assert_equal('CUSTOMER', res.cookies[0].name)
|
1379
|
-
assert_equal('PART_NUMBER', res.cookies[1].name)
|
1380
|
-
end
|
1381
|
-
|
1382
|
-
def test_ok_response_success
|
1383
|
-
res = HTTP::Message.new_response('response')
|
1384
|
-
assert_equal(true, res.ok?)
|
1385
|
-
res.status = 404
|
1386
|
-
assert_equal(false, res.ok?)
|
1387
|
-
res.status = 500
|
1388
|
-
assert_equal(false, res.ok?)
|
1389
|
-
res.status = 302
|
1390
|
-
assert_equal(false, res.ok?)
|
1391
|
-
end
|
1392
|
-
|
1393
|
-
if !defined?(JRUBY_VERSION) and RUBY_VERSION < '1.9'
|
1394
|
-
def test_timeout_scheduler
|
1395
|
-
assert_equal('hello', @client.get_content(serverurl + 'hello'))
|
1396
|
-
status = HTTPClient.timeout_scheduler.instance_eval { @thread.kill; @thread.join; @thread.status }
|
1397
|
-
assert(!status) # dead
|
1398
|
-
assert_equal('hello', @client.get_content(serverurl + 'hello'))
|
1399
|
-
end
|
1400
|
-
end
|
1401
|
-
|
1402
1077
|
def test_session_manager
|
1403
1078
|
mgr = HTTPClient::SessionManager.new(@client)
|
1404
1079
|
assert_nil(mgr.instance_eval { @proxy })
|
@@ -1410,131 +1085,6 @@ EOS
|
|
1410
1085
|
assert_equal(@client.debug_dev, mgr.debug_dev)
|
1411
1086
|
end
|
1412
1087
|
|
1413
|
-
def create_keepalive_disconnected_thread(idx, sock)
|
1414
|
-
Thread.new {
|
1415
|
-
# return "12345" for the first connection
|
1416
|
-
sock.gets
|
1417
|
-
sock.gets
|
1418
|
-
sock.write("HTTP/1.1 200 OK\r\n")
|
1419
|
-
sock.write("Content-Length: 5\r\n")
|
1420
|
-
sock.write("\r\n")
|
1421
|
-
sock.write("12345")
|
1422
|
-
# for the next connection, close while reading the request for emulating
|
1423
|
-
# KeepAliveDisconnected
|
1424
|
-
sock.gets
|
1425
|
-
sock.close
|
1426
|
-
}
|
1427
|
-
end
|
1428
|
-
|
1429
|
-
def test_keepalive_disconnected
|
1430
|
-
client = HTTPClient.new
|
1431
|
-
server = TCPServer.open('127.0.0.1', 0)
|
1432
|
-
server.listen(30) # set enough backlogs
|
1433
|
-
endpoint = "http://127.0.0.1:#{server.addr[1]}/"
|
1434
|
-
Thread.new {
|
1435
|
-
Thread.abort_on_exception = true
|
1436
|
-
# emulate 10 keep-alive connections
|
1437
|
-
10.times do |idx|
|
1438
|
-
sock = server.accept
|
1439
|
-
create_keepalive_disconnected_thread(idx, sock)
|
1440
|
-
end
|
1441
|
-
# return "23456" for the request which gets KeepAliveDisconnected
|
1442
|
-
5.times do
|
1443
|
-
sock = server.accept
|
1444
|
-
sock.gets
|
1445
|
-
sock.gets
|
1446
|
-
sock.write("HTTP/1.1 200 OK\r\n")
|
1447
|
-
sock.write("\r\n")
|
1448
|
-
sock.write("23456")
|
1449
|
-
sock.close
|
1450
|
-
end
|
1451
|
-
# return "34567" for the rest requests
|
1452
|
-
while true
|
1453
|
-
sock = server.accept
|
1454
|
-
sock.gets
|
1455
|
-
sock.gets
|
1456
|
-
sock.write("HTTP/1.1 200 OK\r\n")
|
1457
|
-
sock.write("Connection: close\r\n")
|
1458
|
-
sock.write("Content-Length: 5\r\n")
|
1459
|
-
sock.write("\r\n")
|
1460
|
-
sock.write("34567")
|
1461
|
-
sock.close
|
1462
|
-
end
|
1463
|
-
}
|
1464
|
-
# allocate 10 keep-alive connections
|
1465
|
-
(0...10).to_a.map {
|
1466
|
-
Thread.new {
|
1467
|
-
assert_equal("12345", client.get(endpoint).content)
|
1468
|
-
}
|
1469
|
-
}.each { |th| th.join }
|
1470
|
-
# send 5 requests, which should get KeepAliveDesconnected.
|
1471
|
-
# doing these requests, rest keep-alive connections are invalidated.
|
1472
|
-
(0...5).to_a.map {
|
1473
|
-
Thread.new {
|
1474
|
-
assert_equal("23456", client.get(endpoint).content)
|
1475
|
-
}
|
1476
|
-
}.each { |th| th.join }
|
1477
|
-
# rest requests won't get KeepAliveDisconnected; how can I check this?
|
1478
|
-
(0...10).to_a.map {
|
1479
|
-
Thread.new {
|
1480
|
-
assert_equal("34567", client.get(endpoint).content)
|
1481
|
-
}
|
1482
|
-
}.each { |th| th.join }
|
1483
|
-
end
|
1484
|
-
|
1485
|
-
def create_keepalive_thread(count, sock)
|
1486
|
-
Thread.new {
|
1487
|
-
Thread.abort_on_exception = true
|
1488
|
-
count.times do
|
1489
|
-
req = sock.gets
|
1490
|
-
while line = sock.gets
|
1491
|
-
break if line.chomp.empty?
|
1492
|
-
end
|
1493
|
-
case req
|
1494
|
-
when /chunked/
|
1495
|
-
sock.write("HTTP/1.1 200 OK\r\n")
|
1496
|
-
sock.write("Transfer-Encoding: chunked\r\n")
|
1497
|
-
sock.write("\r\n")
|
1498
|
-
sock.write("1a\r\n")
|
1499
|
-
sock.write("abcdefghijklmnopqrstuvwxyz\r\n")
|
1500
|
-
sock.write("10\r\n")
|
1501
|
-
sock.write("1234567890abcdef\r\n")
|
1502
|
-
sock.write("0\r\n")
|
1503
|
-
sock.write("\r\n")
|
1504
|
-
else
|
1505
|
-
sock.write("HTTP/1.1 200 OK\r\n")
|
1506
|
-
sock.write("Content-Length: 5\r\n")
|
1507
|
-
sock.write("\r\n")
|
1508
|
-
sock.write("12345")
|
1509
|
-
end
|
1510
|
-
end
|
1511
|
-
sock.close
|
1512
|
-
}
|
1513
|
-
end
|
1514
|
-
|
1515
|
-
def test_keepalive
|
1516
|
-
server = TCPServer.open('localhost', 0)
|
1517
|
-
server_thread = Thread.new {
|
1518
|
-
Thread.abort_on_exception = true
|
1519
|
-
sock = server.accept
|
1520
|
-
create_keepalive_thread(10, sock)
|
1521
|
-
}
|
1522
|
-
url = "http://localhost:#{server.addr[1]}/"
|
1523
|
-
begin
|
1524
|
-
# content-length
|
1525
|
-
5.times do
|
1526
|
-
assert_equal('12345', @client.get(url).body)
|
1527
|
-
end
|
1528
|
-
# chunked
|
1529
|
-
5.times do
|
1530
|
-
assert_equal('abcdefghijklmnopqrstuvwxyz1234567890abcdef', @client.get(url + 'chunked').body)
|
1531
|
-
end
|
1532
|
-
ensure
|
1533
|
-
server.close
|
1534
|
-
server_thread.join
|
1535
|
-
end
|
1536
|
-
end
|
1537
|
-
|
1538
1088
|
def test_socket_local
|
1539
1089
|
@client.socket_local.host = '127.0.0.1'
|
1540
1090
|
assert_equal('hello', @client.get_content(serverurl + 'hello'))
|
@@ -1608,212 +1158,4 @@ private
|
|
1608
1158
|
)
|
1609
1159
|
end
|
1610
1160
|
|
1611
|
-
def setup_server
|
1612
|
-
@server = WEBrick::HTTPServer.new(
|
1613
|
-
:BindAddress => "localhost",
|
1614
|
-
:Logger => @logger,
|
1615
|
-
:Port => 0,
|
1616
|
-
:AccessLog => [],
|
1617
|
-
:DocumentRoot => File.dirname(File.expand_path(__FILE__))
|
1618
|
-
)
|
1619
|
-
@serverport = @server.config[:Port]
|
1620
|
-
[
|
1621
|
-
:hello, :sleep, :servlet_redirect, :servlet_temporary_redirect, :servlet_see_other,
|
1622
|
-
:redirect1, :redirect2, :redirect3,
|
1623
|
-
:redirect_self, :relative_redirect, :redirect_see_other, :chunked,
|
1624
|
-
:largebody, :status, :compressed, :charset, :continue,
|
1625
|
-
:servlet_redirect_413, :servlet_413
|
1626
|
-
].each do |sym|
|
1627
|
-
@server.mount(
|
1628
|
-
"/#{sym}",
|
1629
|
-
WEBrick::HTTPServlet::ProcHandler.new(method("do_#{sym}").to_proc)
|
1630
|
-
)
|
1631
|
-
end
|
1632
|
-
@server.mount('/servlet', TestServlet.new(@server))
|
1633
|
-
@server_thread = start_server_thread(@server)
|
1634
|
-
end
|
1635
|
-
|
1636
|
-
def escape_noproxy
|
1637
|
-
backup = HTTPClient::NO_PROXY_HOSTS.dup
|
1638
|
-
HTTPClient::NO_PROXY_HOSTS.clear
|
1639
|
-
yield
|
1640
|
-
ensure
|
1641
|
-
HTTPClient::NO_PROXY_HOSTS.replace(backup)
|
1642
|
-
end
|
1643
|
-
|
1644
|
-
def do_hello(req, res)
|
1645
|
-
res['content-type'] = 'text/html'
|
1646
|
-
res.body = "hello"
|
1647
|
-
end
|
1648
|
-
|
1649
|
-
def do_sleep(req, res)
|
1650
|
-
sec = req.query['sec'].to_i
|
1651
|
-
sleep sec
|
1652
|
-
res['content-type'] = 'text/html'
|
1653
|
-
res.body = "hello"
|
1654
|
-
end
|
1655
|
-
|
1656
|
-
def do_servlet_redirect(req, res)
|
1657
|
-
res.set_redirect(WEBrick::HTTPStatus::Found, serverurl + "servlet")
|
1658
|
-
end
|
1659
|
-
|
1660
|
-
def do_servlet_redirect_413(req, res)
|
1661
|
-
res.set_redirect(WEBrick::HTTPStatus::Found, serverurl + "servlet_413")
|
1662
|
-
end
|
1663
|
-
|
1664
|
-
def do_servlet_413(req, res)
|
1665
|
-
res.body = req.body.to_s
|
1666
|
-
end
|
1667
|
-
|
1668
|
-
def do_servlet_temporary_redirect(req, res)
|
1669
|
-
res.set_redirect(WEBrick::HTTPStatus::TemporaryRedirect, serverurl + "servlet")
|
1670
|
-
end
|
1671
|
-
|
1672
|
-
def do_servlet_see_other(req, res)
|
1673
|
-
res.set_redirect(WEBrick::HTTPStatus::SeeOther, serverurl + "servlet")
|
1674
|
-
end
|
1675
|
-
|
1676
|
-
def do_redirect1(req, res)
|
1677
|
-
res.set_redirect(WEBrick::HTTPStatus::MovedPermanently, serverurl + "hello")
|
1678
|
-
end
|
1679
|
-
|
1680
|
-
def do_redirect2(req, res)
|
1681
|
-
res.set_redirect(WEBrick::HTTPStatus::TemporaryRedirect, serverurl + "redirect3")
|
1682
|
-
end
|
1683
|
-
|
1684
|
-
def do_redirect3(req, res)
|
1685
|
-
res.set_redirect(WEBrick::HTTPStatus::Found, serverurl + "hello")
|
1686
|
-
end
|
1687
|
-
|
1688
|
-
def do_redirect_self(req, res)
|
1689
|
-
res.set_redirect(WEBrick::HTTPStatus::Found, serverurl + "redirect_self")
|
1690
|
-
end
|
1691
|
-
|
1692
|
-
def do_relative_redirect(req, res)
|
1693
|
-
res.set_redirect(WEBrick::HTTPStatus::Found, "hello")
|
1694
|
-
end
|
1695
|
-
|
1696
|
-
def do_redirect_see_other(req, res)
|
1697
|
-
if req.request_method == 'POST'
|
1698
|
-
res.set_redirect(WEBrick::HTTPStatus::SeeOther, serverurl + "redirect_see_other") # self
|
1699
|
-
else
|
1700
|
-
res.body = 'hello'
|
1701
|
-
end
|
1702
|
-
end
|
1703
|
-
|
1704
|
-
def do_chunked(req, res)
|
1705
|
-
res.chunked = true
|
1706
|
-
piper, pipew = IO.pipe
|
1707
|
-
res.body = piper
|
1708
|
-
pipew << req.query['msg']
|
1709
|
-
pipew.close
|
1710
|
-
end
|
1711
|
-
|
1712
|
-
def do_largebody(req, res)
|
1713
|
-
res['content-type'] = 'text/html'
|
1714
|
-
res.body = "a" * 1000 * 1000
|
1715
|
-
end
|
1716
|
-
|
1717
|
-
def do_compressed(req, res)
|
1718
|
-
res['content-type'] = 'application/octet-stream'
|
1719
|
-
if req.query['enc'] == 'gzip'
|
1720
|
-
res['content-encoding'] = 'gzip'
|
1721
|
-
res.body = GZIP_CONTENT
|
1722
|
-
elsif req.query['enc'] == 'deflate'
|
1723
|
-
res['content-encoding'] = 'deflate'
|
1724
|
-
res.body = DEFLATE_CONTENT
|
1725
|
-
end
|
1726
|
-
end
|
1727
|
-
|
1728
|
-
def do_charset(req, res)
|
1729
|
-
if RUBY_VERSION > "1.9"
|
1730
|
-
res.body = 'あいうえお'.encode("euc-jp")
|
1731
|
-
res['Content-Type'] = 'text/plain; charset=euc-jp'
|
1732
|
-
else
|
1733
|
-
res.body = 'this endpoint is for 1.9 or later'
|
1734
|
-
end
|
1735
|
-
end
|
1736
|
-
|
1737
|
-
def do_status(req, res)
|
1738
|
-
res.status = req.query['status'].to_i
|
1739
|
-
end
|
1740
|
-
|
1741
|
-
def do_continue(req, res)
|
1742
|
-
req.continue
|
1743
|
-
res.body = 'done!'
|
1744
|
-
end
|
1745
|
-
|
1746
|
-
class TestServlet < WEBrick::HTTPServlet::AbstractServlet
|
1747
|
-
def get_instance(*arg)
|
1748
|
-
self
|
1749
|
-
end
|
1750
|
-
|
1751
|
-
def do_HEAD(req, res)
|
1752
|
-
res["x-head"] = 'head' # use this for test purpose only.
|
1753
|
-
res["x-query"] = query_response(req)
|
1754
|
-
end
|
1755
|
-
|
1756
|
-
def do_GET(req, res)
|
1757
|
-
res.body = 'get'
|
1758
|
-
res["x-query"] = query_response(req)
|
1759
|
-
end
|
1760
|
-
|
1761
|
-
def do_POST(req, res)
|
1762
|
-
res["content-type"] = "text/plain" # iso-8859-1, not US-ASCII
|
1763
|
-
res.body = 'post,' + req.body.to_s
|
1764
|
-
res["x-query"] = body_response(req)
|
1765
|
-
end
|
1766
|
-
|
1767
|
-
def do_PUT(req, res)
|
1768
|
-
res["x-query"] = body_response(req)
|
1769
|
-
param = WEBrick::HTTPUtils.parse_query(req.body) || {}
|
1770
|
-
res["x-size"] = (param['txt'] || '').size
|
1771
|
-
res.body = param['txt'] || 'put'
|
1772
|
-
end
|
1773
|
-
|
1774
|
-
def do_DELETE(req, res)
|
1775
|
-
res.body = 'delete'
|
1776
|
-
end
|
1777
|
-
|
1778
|
-
def do_OPTIONS(req, res)
|
1779
|
-
# check RFC for legal response.
|
1780
|
-
res.body = 'options'
|
1781
|
-
end
|
1782
|
-
|
1783
|
-
def do_PROPFIND(req, res)
|
1784
|
-
res.body = 'propfind'
|
1785
|
-
end
|
1786
|
-
|
1787
|
-
def do_PROPPATCH(req, res)
|
1788
|
-
res.body = 'proppatch'
|
1789
|
-
res["x-query"] = body_response(req)
|
1790
|
-
end
|
1791
|
-
|
1792
|
-
def do_TRACE(req, res)
|
1793
|
-
# client SHOULD reflect the message received back to the client as the
|
1794
|
-
# entity-body of a 200 (OK) response. [RFC2616]
|
1795
|
-
res.body = 'trace'
|
1796
|
-
res["x-query"] = query_response(req)
|
1797
|
-
end
|
1798
|
-
|
1799
|
-
private
|
1800
|
-
|
1801
|
-
def query_response(req)
|
1802
|
-
query_escape(WEBrick::HTTPUtils.parse_query(req.query_string))
|
1803
|
-
end
|
1804
|
-
|
1805
|
-
def body_response(req)
|
1806
|
-
query_escape(WEBrick::HTTPUtils.parse_query(req.body))
|
1807
|
-
end
|
1808
|
-
|
1809
|
-
def query_escape(query)
|
1810
|
-
escaped = []
|
1811
|
-
query.sort_by { |k, v| k }.collect do |k, v|
|
1812
|
-
v.to_ary.each do |ve|
|
1813
|
-
escaped << CGI.escape(k) + '=' + CGI.escape(ve)
|
1814
|
-
end
|
1815
|
-
end
|
1816
|
-
escaped.join('&')
|
1817
|
-
end
|
1818
|
-
end
|
1819
1161
|
end
|