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.
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