glebtv-httpclient 3.2.7 → 3.2.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.ruby-version +1 -1
- data/.travis.yml +2 -2
- data/Gemfile +0 -9
- data/Gemfile.lock +34 -258
- data/README.md +1 -0
- data/httpclient.gemspec +1 -2
- data/lib/httpclient/auth.rb +241 -157
- data/lib/httpclient/cacert.p7s +3865 -1911
- data/lib/httpclient/lru_cache.rb +27 -24
- data/lib/httpclient/ssl_config.rb +3 -2
- data/lib/httpclient/version.rb +2 -1
- data/spec/basic_spec.rb +82 -82
- data/spec/cookie_spec.rb +132 -132
- data/spec/hexdump_spec.rb +1 -1
- data/spec/http_message_spec.rb +37 -37
- data/spec/httpclient_spec.rb +267 -263
- data/spec/keepalive_spec.rb +5 -5
- data/spec/lru_spec.rb +5 -11
- data/test/test_auth.rb +29 -2
- metadata +2 -30
data/lib/httpclient/lru_cache.rb
CHANGED
@@ -4,58 +4,61 @@
|
|
4
4
|
# (MIT Licensed). Thanks!
|
5
5
|
|
6
6
|
require "lru_redux"
|
7
|
+
require 'thread'
|
7
8
|
|
8
|
-
# Not thread-safe!
|
9
9
|
class HTTPClient
|
10
10
|
class LRUCache
|
11
11
|
|
12
12
|
attr_reader :max_size, :ttl, :soft_ttl, :retry_delay
|
13
13
|
|
14
14
|
def initialize(opts={})
|
15
|
+
@mutex = Mutex.new
|
16
|
+
|
15
17
|
@max_size = Integer(opts[:max_size] || 100)
|
16
18
|
@ttl = Float(opts[:ttl] || 0)
|
17
19
|
@soft_ttl = Float(opts[:soft_ttl] || 0)
|
18
20
|
@retry_delay = Float(opts[:retry_delay] || 0)
|
21
|
+
|
19
22
|
raise "max_size must not be negative" if @max_size < 0
|
20
23
|
raise "ttl must not be negative" if @ttl < 0
|
21
24
|
raise "soft_ttl must not be negative" if @soft_ttl < 0
|
22
25
|
raise "retry_delay must not be negative" if @retry_delay < 0
|
23
26
|
|
27
|
+
# we don't use thread safe variant, as we're doing thread safety ourselves
|
24
28
|
@data = LruRedux::Cache.new(@max_size)
|
25
29
|
end
|
26
30
|
|
27
|
-
def store(key, value)
|
28
|
-
expiration = Time.now + @ttl
|
29
|
-
soft_expiration = Time.now + @soft_ttl
|
30
|
-
@data[key] = Datum.new(value, expiration, soft_expiration)
|
31
|
-
value
|
32
|
-
end
|
33
|
-
|
34
|
-
alias :[]= :store
|
35
|
-
|
36
31
|
def fetch(key)
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
@data.delete(key)
|
42
|
-
store(key, value = yield)
|
43
|
-
elsif datum.soft_expired?
|
44
|
-
begin
|
32
|
+
# for now, this means that DNS resolving is single-threaded
|
33
|
+
@mutex.synchronize do
|
34
|
+
datum = @data[key]
|
35
|
+
if datum.nil?
|
45
36
|
store(key, value = yield)
|
46
|
-
|
47
|
-
|
37
|
+
elsif datum.expired?
|
38
|
+
@data.delete(key)
|
39
|
+
store(key, value = yield)
|
40
|
+
elsif datum.soft_expired?
|
41
|
+
begin
|
42
|
+
store(key, value = yield)
|
43
|
+
rescue RuntimeError => e
|
44
|
+
datum.soft_expiration = (Time.now + retry_delay) if retry_delay > 0
|
45
|
+
datum.value
|
46
|
+
end
|
47
|
+
else
|
48
48
|
datum.value
|
49
49
|
end
|
50
|
-
else
|
51
|
-
datum.value
|
52
50
|
end
|
53
51
|
end
|
54
52
|
|
55
|
-
alias :[] :fetch
|
56
|
-
|
57
53
|
private
|
58
54
|
|
55
|
+
def store(key, value)
|
56
|
+
expiration = Time.now + @ttl
|
57
|
+
soft_expiration = Time.now + @soft_ttl
|
58
|
+
@data[key] = Datum.new(value, expiration, soft_expiration)
|
59
|
+
value
|
60
|
+
end
|
61
|
+
|
59
62
|
class Datum
|
60
63
|
attr_reader :value, :expiration, :soft_expiration
|
61
64
|
attr_writer :soft_expiration
|
@@ -123,9 +123,9 @@ class HTTPClient
|
|
123
123
|
# use client_key=.
|
124
124
|
#
|
125
125
|
# Calling this method resets all existing sessions.
|
126
|
-
def set_client_cert_file(cert_file, key_file)
|
126
|
+
def set_client_cert_file(cert_file, key_file, pass = nil)
|
127
127
|
@client_cert = X509::Certificate.new(File.open(cert_file) { |f| f.read })
|
128
|
-
@client_key = PKey::RSA.new(File.open(key_file) { |f| f.read })
|
128
|
+
@client_key = PKey::RSA.new(File.open(key_file) { |f| f.read }, pass)
|
129
129
|
change_notify
|
130
130
|
end
|
131
131
|
|
@@ -392,6 +392,7 @@ class HTTPClient
|
|
392
392
|
|
393
393
|
def change_notify
|
394
394
|
@client.reset_all
|
395
|
+
nil
|
395
396
|
end
|
396
397
|
|
397
398
|
def load_cacerts(cert_store)
|
data/lib/httpclient/version.rb
CHANGED
data/spec/basic_spec.rb
CHANGED
@@ -9,7 +9,7 @@ describe HTTPClient do
|
|
9
9
|
describe 'GET' do
|
10
10
|
it 'performs normal GET' do
|
11
11
|
HTTPClient.new.get(@srv.u('servlet')) do |s|
|
12
|
-
s.
|
12
|
+
expect(s).to eq 'get'
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
@@ -21,13 +21,13 @@ describe HTTPClient do
|
|
21
21
|
it 'writes to file' do
|
22
22
|
file = Tempfile.new('httpcl')
|
23
23
|
HTTPClient.new.download_file(@srv.u('largebody'), file.path)
|
24
|
-
file.read.length.
|
24
|
+
expect(file.read.length).to eq 1_000_000
|
25
25
|
end
|
26
26
|
|
27
27
|
it 'compressed' do
|
28
28
|
file = Tempfile.new('httpcl')
|
29
29
|
@client.download_file(@srv.u('compressed?enc=gzip'), file.path)
|
30
|
-
file.read.length.
|
30
|
+
expect(file.read.length).to eq 25
|
31
31
|
end
|
32
32
|
|
33
33
|
it 'compressed transparent' do
|
@@ -36,13 +36,13 @@ describe HTTPClient do
|
|
36
36
|
file = Tempfile.new('httpcl')
|
37
37
|
@client.download_file(@srv.u('compressed?enc=gzip'), file.path)
|
38
38
|
cnt = file.read
|
39
|
-
cnt.length.
|
40
|
-
cnt.
|
39
|
+
expect(cnt.length).to eq 5
|
40
|
+
expect(cnt).to eq 'hello'
|
41
41
|
|
42
42
|
@client.download_file(@srv.u('compressed?enc=deflate'), file.path)
|
43
43
|
cnt = file.read
|
44
|
-
cnt.length.
|
45
|
-
cnt.
|
44
|
+
expect(cnt.length).to eq 5
|
45
|
+
expect(cnt).to eq 'hello'
|
46
46
|
end
|
47
47
|
|
48
48
|
it 'compressed large' do
|
@@ -50,25 +50,25 @@ describe HTTPClient do
|
|
50
50
|
@client.transparent_gzip_decompression = true
|
51
51
|
|
52
52
|
content = @client.download_file(@srv.u('compressed_large?enc=gzip'), file.path)
|
53
|
-
file.read.
|
53
|
+
expect(file.read).to eq LARGE_STR
|
54
54
|
|
55
55
|
content = @client.download_file(@srv.u('compressed_large?enc=deflate'), file.path)
|
56
|
-
file.read.
|
56
|
+
expect(file.read).to eq LARGE_STR
|
57
57
|
end
|
58
58
|
end
|
59
59
|
|
60
60
|
describe '#get_content' do
|
61
61
|
it 'normal' do
|
62
|
-
@client.get_content(@srv.u('hello')).
|
63
|
-
@client.get_content(@srv.u('redirect1')).
|
64
|
-
@client.get_content(@srv.u('redirect2')).
|
62
|
+
expect(@client.get_content(@srv.u('hello'))).to eq 'hello'
|
63
|
+
expect(@client.get_content(@srv.u('redirect1'))).to eq 'hello'
|
64
|
+
expect(@client.get_content(@srv.u('redirect2'))).to eq 'hello'
|
65
65
|
end
|
66
66
|
|
67
67
|
it '127.0.0.1' do
|
68
68
|
url = @srv.u.sub(/localhost/, '127.0.0.1')
|
69
|
-
@client.get_content(url + 'hello').
|
70
|
-
@client.get_content(url + 'redirect1').
|
71
|
-
@client.get_content(url + 'redirect2').
|
69
|
+
expect(@client.get_content(url + 'hello')).to eq 'hello'
|
70
|
+
expect(@client.get_content(url + 'redirect1')).to eq 'hello'
|
71
|
+
expect(@client.get_content(url + 'redirect2')).to eq 'hello'
|
72
72
|
end
|
73
73
|
|
74
74
|
it 'redirect callback' do
|
@@ -79,8 +79,8 @@ describe HTTPClient do
|
|
79
79
|
newuri
|
80
80
|
}
|
81
81
|
|
82
|
-
@client.get_content(@srv.u('relative_redirect')).
|
83
|
-
called.
|
82
|
+
expect(@client.get_content(@srv.u('relative_redirect'))).to eq 'hello'
|
83
|
+
expect(called).to be_truthy
|
84
84
|
end
|
85
85
|
|
86
86
|
it 'errors' do
|
@@ -95,13 +95,13 @@ describe HTTPClient do
|
|
95
95
|
|
96
96
|
it 'with block' do
|
97
97
|
@client.get_content(@srv.u 'hello') do |str|
|
98
|
-
str.
|
98
|
+
expect(str).to eq 'hello'
|
99
99
|
end
|
100
100
|
@client.get_content(@srv.u + 'redirect1') do |str|
|
101
|
-
str.
|
101
|
+
expect(str).to eq 'hello'
|
102
102
|
end
|
103
103
|
@client.get_content(@srv.u + 'redirect2') do |str|
|
104
|
-
str.
|
104
|
+
expect(str).to eq 'hello'
|
105
105
|
end
|
106
106
|
end
|
107
107
|
|
@@ -109,25 +109,25 @@ describe HTTPClient do
|
|
109
109
|
@client.transparent_gzip_decompression = false
|
110
110
|
|
111
111
|
content = @client.get_content(@srv.u 'compressed?enc=gzip')
|
112
|
-
content.
|
113
|
-
content.
|
112
|
+
expect(content).not_to eq 'hello'
|
113
|
+
expect(content).to eq GZIP_CONTENT
|
114
114
|
@client.transparent_gzip_decompression = true
|
115
115
|
|
116
116
|
content = @client.get_content(@srv.u 'compressed?enc=gzip')
|
117
|
-
content.
|
117
|
+
expect(content).to eq 'hello'
|
118
118
|
|
119
119
|
content = @client.get_content(@srv.u 'compressed?enc=deflate')
|
120
|
-
content.
|
120
|
+
expect(content).to eq 'hello'
|
121
121
|
end
|
122
122
|
|
123
123
|
it 'compressed large' do
|
124
124
|
@client.transparent_gzip_decompression = true
|
125
125
|
|
126
126
|
content = @client.get_content(@srv.u 'compressed_large?enc=gzip')
|
127
|
-
content.
|
127
|
+
expect(content).to eq LARGE_STR
|
128
128
|
|
129
129
|
content = @client.get_content(@srv.u 'compressed_large?enc=deflate')
|
130
|
-
content.
|
130
|
+
expect(content).to eq LARGE_STR
|
131
131
|
end
|
132
132
|
end
|
133
133
|
end
|
@@ -135,13 +135,13 @@ describe HTTPClient do
|
|
135
135
|
describe 'get with block' do
|
136
136
|
it 'works with filter_block: true' do
|
137
137
|
@client.request(:get, @srv.u('hello')) do |str|
|
138
|
-
str.
|
138
|
+
expect(str).to eq 'hello'
|
139
139
|
end
|
140
140
|
end
|
141
141
|
it 'works with filter_block: false' do
|
142
142
|
@client.request(:get, @srv.u('hello'), filter_block: false) do |req, str|
|
143
|
-
req.class.name.
|
144
|
-
str.
|
143
|
+
expect(req.class.name).to eq 'HTTP::Message'
|
144
|
+
expect(str).to eq 'hello'
|
145
145
|
end
|
146
146
|
end
|
147
147
|
end
|
@@ -152,8 +152,8 @@ describe HTTPClient do
|
|
152
152
|
@client.debug_dev = str
|
153
153
|
@client.get(@srv.u)
|
154
154
|
lines = str.split(/(?:\r?\n)+/)
|
155
|
-
lines[0].
|
156
|
-
lines[4].
|
155
|
+
expect(lines[0]).to eq '= Request'
|
156
|
+
expect(lines[4]).to eq "User-Agent: HTTPClient #{HTTPClient::VERSION}"
|
157
157
|
end
|
158
158
|
|
159
159
|
it 'custom' do
|
@@ -162,8 +162,8 @@ describe HTTPClient do
|
|
162
162
|
client.debug_dev = str
|
163
163
|
client.get(@srv.u)
|
164
164
|
lines = str.split(/(?:\r?\n)+/)
|
165
|
-
lines[0].
|
166
|
-
lines[4].
|
165
|
+
expect(lines[0]).to eq '= Request'
|
166
|
+
expect(lines[4]).to eq 'User-Agent: agent_name_foo'
|
167
167
|
end
|
168
168
|
end
|
169
169
|
|
@@ -173,62 +173,62 @@ describe HTTPClient do
|
|
173
173
|
@client.debug_dev = str = ''
|
174
174
|
@client.test_loopback_http_response << "hello\nworld\n"
|
175
175
|
res = @client.get(@srv.u 'hello')
|
176
|
-
res.http_version.
|
177
|
-
res.status.
|
178
|
-
res.reason.
|
179
|
-
res.content.
|
176
|
+
expect(res.http_version).to eq '0.9'
|
177
|
+
expect(res.status).to be_nil
|
178
|
+
expect(res.reason).to be_nil
|
179
|
+
expect(res.content).to eq "hello\nworld\n"
|
180
180
|
lines = str.split(/(?:\r?\n)+/)
|
181
|
-
lines[0].
|
182
|
-
lines[2].
|
183
|
-
lines[3].
|
184
|
-
lines[7].
|
185
|
-
lines[8].
|
186
|
-
lines[9].
|
187
|
-
lines[10].
|
181
|
+
expect(lines[0]).to eq "= Request"
|
182
|
+
expect(lines[2]).to eq "! CONNECTION ESTABLISHED"
|
183
|
+
expect(lines[3]).to eq "GET /hello HTTP/0.9"
|
184
|
+
expect(lines[7]).to eq "Connection: close"
|
185
|
+
expect(lines[8]).to eq "= Response"
|
186
|
+
expect(lines[9]).to match /^hello$/
|
187
|
+
expect(lines[10]).to match /^world$/
|
188
188
|
end
|
189
189
|
|
190
190
|
it '1.0' do
|
191
|
-
@client.protocol_version.
|
191
|
+
expect(@client.protocol_version).to be_nil
|
192
192
|
@client.protocol_version = 'HTTP/1.0'
|
193
|
-
@client.protocol_version.
|
193
|
+
expect(@client.protocol_version).to eq 'HTTP/1.0'
|
194
194
|
str = ""
|
195
195
|
@client.debug_dev = str
|
196
196
|
@client.get(@srv.u 'hello')
|
197
197
|
lines = str.split(/(?:\r?\n)+/)
|
198
|
-
lines[0].
|
199
|
-
lines[2].
|
200
|
-
lines[3].
|
201
|
-
lines[7].
|
202
|
-
lines[8].
|
198
|
+
expect(lines[0]).to eq "= Request"
|
199
|
+
expect(lines[2]).to eq "! CONNECTION ESTABLISHED"
|
200
|
+
expect(lines[3]).to eq "GET /hello HTTP/1.0"
|
201
|
+
expect(lines[7]).to eq "Connection: close"
|
202
|
+
expect(lines[8]).to eq "= Response"
|
203
203
|
end
|
204
204
|
|
205
205
|
it '1.1' do
|
206
|
-
@client.protocol_version.
|
206
|
+
expect(@client.protocol_version).to be_nil
|
207
207
|
str = ""
|
208
208
|
@client.debug_dev = str
|
209
209
|
@client.get(@srv.u)
|
210
210
|
lines = str.split(/(?:\r?\n)+/)
|
211
|
-
lines[0].
|
212
|
-
lines[2].
|
213
|
-
lines[3].
|
214
|
-
lines[7].
|
211
|
+
expect(lines[0]).to eq "= Request"
|
212
|
+
expect(lines[2]).to eq "! CONNECTION ESTABLISHED"
|
213
|
+
expect(lines[3]).to eq "GET / HTTP/1.1"
|
214
|
+
expect(lines[7]).to eq "Host: localhost:#{@srv.port}"
|
215
215
|
@client.protocol_version = 'HTTP/1.1'
|
216
|
-
@client.protocol_version.
|
216
|
+
expect(@client.protocol_version).to eq 'HTTP/1.1'
|
217
217
|
str = ""
|
218
218
|
@client.debug_dev = str
|
219
219
|
@client.get(@srv.u)
|
220
220
|
lines = str.split(/(?:\r?\n)+/)
|
221
|
-
lines[0].
|
222
|
-
lines[2].
|
223
|
-
lines[3].
|
221
|
+
expect(lines[0]).to eq "= Request"
|
222
|
+
expect(lines[2]).to eq "! CONNECTION ESTABLISHED"
|
223
|
+
expect(lines[3]).to eq "GET / HTTP/1.1"
|
224
224
|
@client.protocol_version = 'HTTP/1.0'
|
225
225
|
str = ""
|
226
226
|
@client.debug_dev = str
|
227
227
|
@client.get(@srv.u)
|
228
228
|
lines = str.split(/(?:\r?\n)+/)
|
229
|
-
lines[0].
|
230
|
-
lines[2].
|
231
|
-
lines[3].
|
229
|
+
expect(lines[0]).to eq "= Request"
|
230
|
+
expect(lines[2]).to eq "! CONNECTION ESTABLISHED"
|
231
|
+
expect(lines[3]).to eq "GET / HTTP/1.0"
|
232
232
|
end
|
233
233
|
end
|
234
234
|
|
@@ -238,7 +238,7 @@ describe HTTPClient do
|
|
238
238
|
@client.debug_dev = str
|
239
239
|
@client.get(@srv.u)
|
240
240
|
lines = str.split(/(?:\r?\n)+/)
|
241
|
-
lines[5].
|
241
|
+
expect(lines[5]).to eq "Accept: */*"
|
242
242
|
end
|
243
243
|
|
244
244
|
it 'sets properly' do
|
@@ -246,9 +246,9 @@ describe HTTPClient do
|
|
246
246
|
@client.debug_dev = str
|
247
247
|
@client.get(@srv.u, :header => {:Accept => 'text/html'})
|
248
248
|
lines = str.split(/(?:\r?\n)+/)
|
249
|
-
lines[4].
|
249
|
+
expect(lines[4]).to eq "Accept: text/html"
|
250
250
|
lines.each do |line|
|
251
|
-
line.
|
251
|
+
expect(line).not_to eq "Accept: */*"
|
252
252
|
end
|
253
253
|
end
|
254
254
|
end
|
@@ -256,9 +256,9 @@ describe HTTPClient do
|
|
256
256
|
describe 'POST' do
|
257
257
|
describe '#post_content' do
|
258
258
|
it 'works' do
|
259
|
-
@client.post_content(@srv.u('hello')).
|
260
|
-
@client.post_content(@srv.u("redirect1")).
|
261
|
-
@client.post_content(@srv.u("redirect2")).
|
259
|
+
expect(@client.post_content(@srv.u('hello'))).to eq 'hello'
|
260
|
+
expect(@client.post_content(@srv.u("redirect1"))).to eq 'hello'
|
261
|
+
expect(@client.post_content(@srv.u("redirect2"))).to eq 'hello'
|
262
262
|
end
|
263
263
|
end
|
264
264
|
|
@@ -269,8 +269,8 @@ describe HTTPClient do
|
|
269
269
|
called = true
|
270
270
|
newuri
|
271
271
|
}
|
272
|
-
@client.post_content(@srv.u("relative_redirect")).
|
273
|
-
called.
|
272
|
+
expect(@client.post_content(@srv.u("relative_redirect"))).to eq 'hello'
|
273
|
+
expect(called).to be_truthy
|
274
274
|
end
|
275
275
|
|
276
276
|
it 'errors' do
|
@@ -287,40 +287,40 @@ describe HTTPClient do
|
|
287
287
|
describe 'string io' do
|
288
288
|
it do
|
289
289
|
post_body = StringIO.new("1234567890")
|
290
|
-
@client.post_content(@srv.u("servlet"), post_body).
|
290
|
+
expect(@client.post_content(@srv.u("servlet"), post_body)).to eq 'post,1234567890'
|
291
291
|
|
292
292
|
# all browsers use GET for 302
|
293
293
|
post_body = StringIO.new("1234567890")
|
294
|
-
@client.post_content(@srv.u("servlet_413"), post_body).
|
294
|
+
expect(@client.post_content(@srv.u("servlet_413"), post_body)).to eq '1234567890'
|
295
295
|
|
296
|
-
@client.get_content(@srv.u("servlet_redirect_413")).
|
296
|
+
expect(@client.get_content(@srv.u("servlet_redirect_413"))).to eq ''
|
297
297
|
|
298
298
|
post_body = StringIO.new("1234567890")
|
299
|
-
@client.post_content(@srv.u("servlet_redirect_413"), post_body).
|
299
|
+
expect(@client.post_content(@srv.u("servlet_redirect_413"), post_body)).to eq ''
|
300
300
|
|
301
301
|
post_body = StringIO.new("1234567890")
|
302
|
-
@client.post_content(@srv.u("servlet_temporary_redirect"), post_body).
|
302
|
+
expect(@client.post_content(@srv.u("servlet_temporary_redirect"), post_body)).to eq 'post,1234567890'
|
303
303
|
|
304
304
|
post_body = StringIO.new("1234567890")
|
305
|
-
@client.post_content(@srv.u("servlet_see_other"), post_body).
|
305
|
+
expect(@client.post_content(@srv.u("servlet_see_other"), post_body)).to eq 'get'
|
306
306
|
end
|
307
307
|
|
308
308
|
it 'doesnt rewind' do
|
309
309
|
post_body = StringIO.new("1234567890")
|
310
310
|
post_body.read(5)
|
311
|
-
@client.post_content(@srv.u("servlet_temporary_redirect"), post_body).
|
311
|
+
expect(@client.post_content(@srv.u("servlet_temporary_redirect"), post_body)).to eq 'post,67890'
|
312
312
|
end
|
313
313
|
end
|
314
314
|
end
|
315
315
|
|
316
316
|
describe 'util' do
|
317
317
|
it '#urify' do
|
318
|
-
urify(nil).
|
318
|
+
expect(urify(nil)).to be_nil
|
319
319
|
uri = 'http://foo'
|
320
320
|
# urify(uri).class.name.should eq 'HTTPClient::Util::AddressableURI'
|
321
|
-
urify(uri).
|
322
|
-
urify(uri).to_s.
|
323
|
-
urify(urify(uri)).
|
321
|
+
expect(urify(uri)).to eq urify(uri)
|
322
|
+
expect(urify(uri).to_s).to eq uri
|
323
|
+
expect(urify(urify(uri))).to eq urify(uri)
|
324
324
|
end
|
325
325
|
end
|
326
326
|
end
|