glebtv-httpclient 3.1.1 → 3.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.coveralls.yml +1 -0
  3. data/.gitignore +8 -0
  4. data/.ruby-gemset +1 -0
  5. data/.ruby-version +1 -0
  6. data/.travis.yml +6 -0
  7. data/CHANGELOG.rdoc +653 -0
  8. data/Gemfile +12 -0
  9. data/Gemfile.lock +284 -0
  10. data/README.md +7 -2
  11. data/Rakefile +22 -0
  12. data/bin/httpclient +1 -1
  13. data/dist_key/cacerts.pem +1808 -0
  14. data/dist_key/cert.pem +24 -0
  15. data/dist_key/gen_dist_cert.rb +29 -0
  16. data/httpclient.gemspec +33 -0
  17. data/lib/httpclient.rb +59 -15
  18. data/lib/httpclient/lru_cache.rb +171 -0
  19. data/lib/httpclient/lru_threadsafe.rb +29 -0
  20. data/lib/httpclient/session.rb +52 -13
  21. data/lib/httpclient/ssl_config.rb +4 -3
  22. data/lib/httpclient/version.rb +1 -1
  23. data/sample/ssl/trust_certs/.keep_me +0 -0
  24. data/spec/http_message_spec.rb +124 -0
  25. data/spec/httpclient_spec.rb +322 -0
  26. data/spec/keepalive_spec.rb +129 -0
  27. data/spec/spec_helper.rb +40 -0
  28. data/spec/support/1024x768.gif +0 -0
  29. data/spec/support/1x1.png +0 -0
  30. data/spec/support/base_server.rb +36 -0
  31. data/spec/support/file.txt +1 -0
  32. data/spec/support/ht_helpers.rb +10 -0
  33. data/spec/support/main_server.rb +155 -0
  34. data/spec/support/proxy_server.rb +14 -0
  35. data/spec/support/test_servlet.rb +73 -0
  36. data/stress-test/Gemfile +4 -0
  37. data/stress-test/Gemfile.lock +16 -0
  38. data/stress-test/client.rb +72 -0
  39. data/stress-test/config.ru +4 -0
  40. data/stress-test/unicorn.conf +2 -0
  41. data/test.rb +19 -0
  42. data/test/helper.rb +4 -3
  43. data/test/test_httpclient.rb +19 -677
  44. metadata +226 -38
  45. data/lib/httpclient/timeout.rb +0 -140
  46. data/test/runner.rb +0 -2
@@ -83,7 +83,7 @@ class HTTPClient
83
83
  @verify_callback = nil
84
84
  @dest = nil
85
85
  @timeout = nil
86
- @ssl_version = "SSLv3"
86
+ @ssl_version = "SSLv23"
87
87
  @options = defined?(SSL::OP_ALL) ? SSL::OP_ALL | SSL::OP_NO_SSLv2 : nil
88
88
  # OpenSSL 0.9.8 default: "ALL:!ADH:!LOW:!EXP:!MD5:+SSLv2:@STRENGTH"
89
89
  @ciphers = "ALL:!aNULL:!eNULL:!SSLv2" # OpenSSL >1.0.0 default
@@ -269,8 +269,9 @@ class HTTPClient
269
269
 
270
270
  # interfaces for SSLSocketWrap.
271
271
  def set_context(ctx) # :nodoc:
272
- load_trust_ca unless @cacerts_loaded
273
- @cacerts_loaded = true
272
+ # load_trust_ca unless @cacerts_loaded
273
+ # @cacerts_loaded = true
274
+ set_default_paths unless @cacerts_loaded
274
275
  # Verification: Use Store#verify_callback instead of SSLContext#verify*?
275
276
  ctx.cert_store = @cert_store
276
277
  ctx.verify_mode = @verify_mode
@@ -1,3 +1,3 @@
1
1
  class HTTPClient
2
- VERSION = '3.1.1' unless defined?(VERSION)
2
+ VERSION = '3.2.0' unless defined?(VERSION)
3
3
  end
File without changes
@@ -0,0 +1,124 @@
1
+ # coding: utf-8
2
+ require 'spec_helper'
3
+
4
+ describe HTTP::Message do
5
+ it 'has sane defaults for mime handlers' do
6
+ HTTP::Message.get_mime_type_func.should be_nil
7
+ HTTP::Message.mime_type_handler.should be_nil
8
+ end
9
+
10
+ context 'reset' do
11
+ before :each do
12
+ HTTP::Message.set_mime_type_func(nil)
13
+ HTTP::Message.mime_type_handler = nil
14
+ end
15
+ it 'mime type' do
16
+ HTTP::Message.mime_type('foo.txt').should eq 'text/plain'
17
+ HTTP::Message.mime_type('foo.html').should eq 'text/html'
18
+ HTTP::Message.mime_type('foo.htm').should eq 'text/html'
19
+ HTTP::Message.mime_type('foo.doc').should eq 'application/msword'
20
+ HTTP::Message.mime_type('foo.png').should eq 'image/png'
21
+ HTTP::Message.mime_type('foo.gif').should eq 'image/gif'
22
+ HTTP::Message.mime_type('foo.jpg').should eq 'image/jpeg'
23
+ HTTP::Message.mime_type('foo.jpeg').should eq 'image/jpeg'
24
+ HTTP::Message.mime_type('foo.unknown').should eq 'application/octet-stream'
25
+ end
26
+
27
+ it 'mime handler' do
28
+ handler = lambda { |path| 'hello/world' }
29
+ HTTP::Message.mime_type_handler = handler
30
+ HTTP::Message.mime_type_handler.should_not be_nil
31
+
32
+ HTTP::Message.get_mime_type_func.should_not be_nil
33
+
34
+ HTTP::Message.mime_type('foo.txt').should eq 'hello/world'
35
+
36
+ HTTP::Message.mime_type_handler = nil
37
+ HTTP::Message.mime_type('foo.txt').should eq 'text/plain'
38
+ HTTP::Message.set_mime_type_func(nil)
39
+ HTTP::Message.mime_type('foo.txt').should eq 'text/plain'
40
+
41
+ handler = lambda { |path| nil }
42
+ HTTP::Message.mime_type_handler = handler
43
+ HTTP::Message.mime_type('foo.txt').should eq 'application/octet-stream'
44
+ end
45
+ end
46
+
47
+
48
+ it 'connect request' do
49
+ req = HTTP::Message.new_connect_request(urify('https://foo/bar'))
50
+ req.dump.should eq "CONNECT foo:443 HTTP/1.0\r\n\r\n"
51
+ req = HTTP::Message.new_connect_request(urify('https://example.com/'))
52
+ req.dump.should eq "CONNECT example.com:443 HTTP/1.0\r\n\r\n"
53
+ end
54
+
55
+ it 'response' do
56
+ res = HTTP::Message.new_response('response')
57
+ res.contenttype = 'text/plain'
58
+ res.header.body_date = Time.at(946652400)
59
+ [
60
+ "",
61
+ "Content-Length: 8",
62
+ "Content-Type: text/plain",
63
+ "Last-Modified: Fri, 31 Dec 1999 15:00:00 GMT",
64
+ "Status: 200 OK",
65
+ "response"
66
+ ].should eq res.dump.split(/\r\n/).sort
67
+
68
+ res.header['Content-Length'].should eq ['8']
69
+ res.headers['Content-Length'].should eq '8'
70
+ res.header.set('foo', 'bar')
71
+ [
72
+ "",
73
+ "Content-Length: 8",
74
+ "Content-Type: text/plain",
75
+ "Last-Modified: Fri, 31 Dec 1999 15:00:00 GMT",
76
+ "Status: 200 OK",
77
+ "foo: bar",
78
+ "response"
79
+ ].should eq res.dump.split(/\r\n/).sort
80
+
81
+ res = HTTP::Message.new_response(nil)
82
+ [
83
+ "Content-Length: 0",
84
+ "Content-Type: text/html; charset=us-ascii",
85
+ "Status: 200 OK"
86
+ ].should eq res.dump.split(/\r\n/).sort
87
+ end
88
+
89
+ it 'response cookies' do
90
+ res = HTTP::Message.new_response('response')
91
+ res.contenttype = 'text/plain'
92
+ res.header.body_date = Time.at(946652400)
93
+ res.cookies.should be_nil
94
+ res.header['Set-Cookie'] = [
95
+ 'CUSTOMER=WILE_E_COYOTE; path=/; expires=Wednesday, 09-Nov-99 23:12:40 GMT',
96
+ 'PART_NUMBER=ROCKET_LAUNCHER_0001; path=/'
97
+ ]
98
+ [
99
+ "",
100
+ "Content-Length: 8",
101
+ "Content-Type: text/plain",
102
+ "Last-Modified: Fri, 31 Dec 1999 15:00:00 GMT",
103
+ "Set-Cookie: CUSTOMER=WILE_E_COYOTE; path=/; expires=Wednesday, 09-Nov-99 23:12:40 GMT",
104
+ "Set-Cookie: PART_NUMBER=ROCKET_LAUNCHER_0001; path=/",
105
+ "Status: 200 OK",
106
+ "response"
107
+ ].should eq res.dump.split(/\r\n/).sort
108
+
109
+ res.cookies.size.should eq 2
110
+ res.cookies[0].name.should eq 'CUSTOMER'
111
+ res.cookies[1].name.should eq 'PART_NUMBER'
112
+ end
113
+
114
+ it '#ok?' do
115
+ res = HTTP::Message.new_response('response')
116
+ res.ok?.should eq true
117
+ res.status = 404
118
+ res.ok?.should eq false
119
+ res.status = 500
120
+ res.ok?.should eq false
121
+ res.status = 302
122
+ res.ok?.should eq false
123
+ end
124
+ end
@@ -0,0 +1,322 @@
1
+ # coding: utf-8
2
+ require 'spec_helper'
3
+
4
+ describe HTTPClient do
5
+ before :each do
6
+ @client = HTTPClient.new
7
+ end
8
+
9
+ describe 'GET' do
10
+ it 'performs normal GET' do
11
+ HTTPClient.new.get(@srv.u('servlet')) do |s|
12
+ s.should eq 'get'
13
+ end
14
+ end
15
+
16
+ describe '#download_file' do
17
+ it 'writes to file' do
18
+ file = Tempfile.new('httpcl')
19
+ HTTPClient.new.download_file(@srv.u('largebody'), file.path)
20
+ file.read.length.should eq 1_000_000
21
+ end
22
+
23
+ it 'compressed' do
24
+ file = Tempfile.new('httpcl')
25
+ @client.download_file(@srv.u('compressed?enc=gzip'), file.path)
26
+ file.read.length.should eq 25
27
+ end
28
+
29
+ it 'compressed transparent' do
30
+ @client.transparent_gzip_decompression = true
31
+
32
+ file = Tempfile.new('httpcl')
33
+ @client.download_file(@srv.u('compressed?enc=gzip'), file.path)
34
+ cnt = file.read
35
+ cnt.length.should eq 5
36
+ cnt.should eq 'hello'
37
+
38
+ @client.download_file(@srv.u('compressed?enc=deflate'), file.path)
39
+ cnt = file.read
40
+ cnt.length.should eq 5
41
+ cnt.should eq 'hello'
42
+ end
43
+
44
+ it 'compressed large' do
45
+ file = Tempfile.new('httpcl')
46
+ @client.transparent_gzip_decompression = true
47
+
48
+ content = @client.download_file(@srv.u('compressed_large?enc=gzip'), file.path)
49
+ file.read.should eq LARGE_STR
50
+
51
+ content = @client.download_file(@srv.u('compressed_large?enc=deflate'), file.path)
52
+ file.read.should eq LARGE_STR
53
+ end
54
+ end
55
+
56
+ describe '#get_content' do
57
+ it 'normal' do
58
+ @client.get_content(@srv.u('hello')).should eq 'hello'
59
+ @client.get_content(@srv.u('redirect1')).should eq 'hello'
60
+ @client.get_content(@srv.u('redirect2')).should eq 'hello'
61
+ end
62
+
63
+ it '127.0.0.1' do
64
+ url = @srv.u.sub(/localhost/, '127.0.0.1')
65
+ @client.get_content(url + 'hello').should eq 'hello'
66
+ @client.get_content(url + 'redirect1').should eq 'hello'
67
+ @client.get_content(url + 'redirect2').should eq 'hello'
68
+ end
69
+
70
+ it 'redirect callback' do
71
+ called = false
72
+ @client.redirect_uri_callback = lambda { |uri, res|
73
+ newuri = res.header['location'][0]
74
+ called = true
75
+ newuri
76
+ }
77
+
78
+ @client.get_content(@srv.u('relative_redirect')).should eq 'hello'
79
+ called.should be_true
80
+ end
81
+
82
+ it 'errors' do
83
+ expect {
84
+ @client.get_content(@srv.u 'notfound')
85
+ }.to raise_error(HTTPClient::BadResponseError)
86
+
87
+ expect {
88
+ @client.get_content(@srv.u 'redirect_self')
89
+ }.to raise_error(HTTPClient::BadResponseError)
90
+ end
91
+
92
+ it 'with block' do
93
+ @client.get_content(@srv.u 'hello') do |str|
94
+ str.should eq 'hello'
95
+ end
96
+ @client.get_content(@srv.u + 'redirect1') do |str|
97
+ str.should eq 'hello'
98
+ end
99
+ @client.get_content(@srv.u + 'redirect2') do |str|
100
+ str.should eq 'hello'
101
+ end
102
+ end
103
+
104
+ it 'compressed' do
105
+ @client.transparent_gzip_decompression = false
106
+
107
+ content = @client.get_content(@srv.u 'compressed?enc=gzip')
108
+ content.should_not eq 'hello'
109
+ content.should eq GZIP_CONTENT
110
+ @client.transparent_gzip_decompression = true
111
+
112
+ content = @client.get_content(@srv.u 'compressed?enc=gzip')
113
+ content.should eq 'hello'
114
+
115
+ content = @client.get_content(@srv.u 'compressed?enc=deflate')
116
+ content.should eq 'hello'
117
+ end
118
+
119
+ it 'compressed large' do
120
+ @client.transparent_gzip_decompression = true
121
+
122
+ content = @client.get_content(@srv.u 'compressed_large?enc=gzip')
123
+ content.should eq LARGE_STR
124
+
125
+ content = @client.get_content(@srv.u 'compressed_large?enc=deflate')
126
+ content.should eq LARGE_STR
127
+ end
128
+ end
129
+ end
130
+ describe 'request' do
131
+ describe 'get with block' do
132
+ it 'works with filter_block: true' do
133
+ @client.request(:get, @srv.u('hello')) do |str|
134
+ str.should eq 'hello'
135
+ end
136
+ end
137
+ it 'works with filter_block: false' do
138
+ @client.request(:get, @srv.u('hello'), filter_block: false) do |req, str|
139
+ req.class.name.should eq 'HTTP::Message'
140
+ str.should eq 'hello'
141
+ end
142
+ end
143
+ end
144
+ end
145
+ describe 'agent name' do
146
+ it 'default' do
147
+ str = ""
148
+ @client.debug_dev = str
149
+ @client.get(@srv.u)
150
+ lines = str.split(/(?:\r?\n)+/)
151
+ lines[0].should eq '= Request'
152
+ lines[4].should eq "User-Agent: HTTPClient #{HTTPClient::VERSION}"
153
+ end
154
+
155
+ it 'custom' do
156
+ client = HTTPClient.new(nil, "agent_name_foo")
157
+ str = ""
158
+ client.debug_dev = str
159
+ client.get(@srv.u)
160
+ lines = str.split(/(?:\r?\n)+/)
161
+ lines[0].should eq '= Request'
162
+ lines[4].should eq 'User-Agent: agent_name_foo'
163
+ end
164
+ end
165
+
166
+ describe 'protocol versions' do
167
+ it '0.9' do
168
+ @client.protocol_version = 'HTTP/0.9'
169
+ @client.debug_dev = str = ''
170
+ @client.test_loopback_http_response << "hello\nworld\n"
171
+ res = @client.get(@srv.u 'hello')
172
+ res.http_version.should eq '0.9'
173
+ res.status.should be_nil
174
+ res.reason.should be_nil
175
+ res.content.should eq "hello\nworld\n"
176
+ lines = str.split(/(?:\r?\n)+/)
177
+ lines[0].should eq "= Request"
178
+ lines[2].should eq "! CONNECTION ESTABLISHED"
179
+ lines[3].should eq "GET /hello HTTP/0.9"
180
+ lines[7].should eq "Connection: close"
181
+ lines[8].should eq "= Response"
182
+ lines[9].should match /^hello$/
183
+ lines[10].should match /^world$/
184
+ end
185
+
186
+ it '1.0' do
187
+ @client.protocol_version.should be_nil
188
+ @client.protocol_version = 'HTTP/1.0'
189
+ @client.protocol_version.should eq 'HTTP/1.0'
190
+ str = ""
191
+ @client.debug_dev = str
192
+ @client.get(@srv.u 'hello')
193
+ lines = str.split(/(?:\r?\n)+/)
194
+ lines[0].should eq "= Request"
195
+ lines[2].should eq "! CONNECTION ESTABLISHED"
196
+ lines[3].should eq "GET /hello HTTP/1.0"
197
+ lines[7].should eq "Connection: close"
198
+ lines[8].should eq "= Response"
199
+ end
200
+
201
+ it '1.1' do
202
+ @client.protocol_version.should be_nil
203
+ str = ""
204
+ @client.debug_dev = str
205
+ @client.get(@srv.u)
206
+ lines = str.split(/(?:\r?\n)+/)
207
+ lines[0].should eq "= Request"
208
+ lines[2].should eq "! CONNECTION ESTABLISHED"
209
+ lines[3].should eq "GET / HTTP/1.1"
210
+ lines[7].should eq "Host: localhost:#{@srv.port}"
211
+ @client.protocol_version = 'HTTP/1.1'
212
+ @client.protocol_version.should eq 'HTTP/1.1'
213
+ str = ""
214
+ @client.debug_dev = str
215
+ @client.get(@srv.u)
216
+ lines = str.split(/(?:\r?\n)+/)
217
+ lines[0].should eq "= Request"
218
+ lines[2].should eq "! CONNECTION ESTABLISHED"
219
+ lines[3].should eq "GET / HTTP/1.1"
220
+ @client.protocol_version = 'HTTP/1.0'
221
+ str = ""
222
+ @client.debug_dev = str
223
+ @client.get(@srv.u)
224
+ lines = str.split(/(?:\r?\n)+/)
225
+ lines[0].should eq "= Request"
226
+ lines[2].should eq "! CONNECTION ESTABLISHED"
227
+ lines[3].should eq "GET / HTTP/1.0"
228
+ end
229
+ end
230
+
231
+ describe 'accept' do
232
+ it '*/* by default' do
233
+ str = ""
234
+ @client.debug_dev = str
235
+ @client.get(@srv.u)
236
+ lines = str.split(/(?:\r?\n)+/)
237
+ lines[5].should eq "Accept: */*"
238
+ end
239
+
240
+ it 'sets properly' do
241
+ str = ""
242
+ @client.debug_dev = str
243
+ @client.get(@srv.u, :header => {:Accept => 'text/html'})
244
+ lines = str.split(/(?:\r?\n)+/)
245
+ lines[4].should eq "Accept: text/html"
246
+ lines.each do |line|
247
+ line.should_not eq "Accept: */*"
248
+ end
249
+ end
250
+ end
251
+
252
+ describe 'POST' do
253
+ describe '#post_content' do
254
+ it 'works' do
255
+ @client.post_content(@srv.u('hello')).should eq 'hello'
256
+ @client.post_content(@srv.u("redirect1")).should eq 'hello'
257
+ @client.post_content(@srv.u("redirect2")).should eq 'hello'
258
+ end
259
+ end
260
+
261
+ it 'redirect callback' do
262
+ called = false
263
+ @client.redirect_uri_callback = lambda { |uri, res|
264
+ newuri = res.header['location'][0]
265
+ called = true
266
+ newuri
267
+ }
268
+ @client.post_content(@srv.u("relative_redirect")).should eq 'hello'
269
+ called.should be_true
270
+ end
271
+
272
+ it 'errors' do
273
+ expect {
274
+ @client.post_content(@srv.u 'notfound')
275
+ }.to raise_error(HTTPClient::BadResponseError)
276
+
277
+ expect {
278
+ @client.post_content(@srv.u 'redirect_self')
279
+ }.to raise_error(HTTPClient::BadResponseError)
280
+ end
281
+
282
+
283
+ describe 'string io' do
284
+ it do
285
+ post_body = StringIO.new("1234567890")
286
+ @client.post_content(@srv.u("servlet"), post_body).should eq 'post,1234567890'
287
+
288
+ # all browsers use GET for 302
289
+ post_body = StringIO.new("1234567890")
290
+ @client.post_content(@srv.u("servlet_413"), post_body).should eq '1234567890'
291
+
292
+ @client.get_content(@srv.u("servlet_redirect_413")).should eq ''
293
+
294
+ post_body = StringIO.new("1234567890")
295
+ @client.post_content(@srv.u("servlet_redirect_413"), post_body).should eq ''
296
+
297
+ post_body = StringIO.new("1234567890")
298
+ @client.post_content(@srv.u("servlet_temporary_redirect"), post_body).should eq 'post,1234567890'
299
+
300
+ post_body = StringIO.new("1234567890")
301
+ @client.post_content(@srv.u("servlet_see_other"), post_body).should eq 'get'
302
+ end
303
+
304
+ it 'doesnt rewind' do
305
+ post_body = StringIO.new("1234567890")
306
+ post_body.read(5)
307
+ @client.post_content(@srv.u("servlet_temporary_redirect"), post_body).should eq 'post,67890'
308
+ end
309
+ end
310
+ end
311
+
312
+ describe 'util' do
313
+ it '#urify' do
314
+ urify(nil).should be_nil
315
+ uri = 'http://foo'
316
+ urify(uri).class.name.should eq 'HTTPClient::Util::AddressableURI'
317
+ urify(uri).should eq urify(uri)
318
+ urify(uri).to_s.should eq uri
319
+ urify(urify(uri)).should eq urify(uri)
320
+ end
321
+ end
322
+ end