excon 0.48.0 → 0.49.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of excon might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/README.md +4 -2
- data/changelog.txt +10 -0
- data/excon.gemspec +2 -2
- data/lib/excon.rb +7 -2
- data/lib/excon/connection.rb +11 -3
- data/lib/excon/constants.rb +2 -1
- data/lib/excon/errors.rb +20 -3
- data/lib/excon/response.rb +4 -0
- data/lib/excon/socket.rb +21 -54
- data/lib/excon/ssl_socket.rb +2 -4
- data/tests/middlewares/mock_tests.rb +23 -7
- data/tests/response_tests.rb +9 -0
- data/tests/servers/good.rb +7 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 89343aee2679e6e6b180e22e1ae3c16f5680bd4c
|
4
|
+
data.tar.gz: 354d695485716081f2b7732bbbb2b15964f8e738
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 35185514c9f9460e3409282f8da653832110c6e13813e964d1bc53424fccd4d9dfce972a4811bddae2c9929e19b81860c50326620675b4a23f9c71c59bda24bf
|
7
|
+
data.tar.gz: 75d1f86fb2d5d66576d50b183c32d358c43408129ade13adb653a7333b1a2825083d4e66c61dfb476d78729fc4fcca087f14d24fbfa5d4d8dea02815e1efb7ef
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -86,7 +86,7 @@ Here are a few common examples:
|
|
86
86
|
|
87
87
|
```ruby
|
88
88
|
# Output debug info, similar to ENV['EXCON_DEBUG']
|
89
|
-
connection = Excon.new('http://geemus.com/', :
|
89
|
+
connection = Excon.new('http://geemus.com/', :debug_request => true, :debug_response => true)
|
90
90
|
|
91
91
|
# Custom headers
|
92
92
|
Excon.get('http://geemus.com', :headers => {'Authorization' => 'Basic 0123456789ABCDEF'})
|
@@ -308,6 +308,8 @@ config.before(:all) do
|
|
308
308
|
end
|
309
309
|
```
|
310
310
|
|
311
|
+
By default stubs are shared globally, to make stubs unique to each thread, use `Excon.defaults[:stubs] = :local`.
|
312
|
+
|
311
313
|
## Instrumentation
|
312
314
|
|
313
315
|
Excon calls can be timed using the [ActiveSupport::Notifications](http://api.rubyonrails.org/classes/ActiveSupport/Notifications.html) API.
|
@@ -390,7 +392,7 @@ connection = Excon.new('https://example.com',
|
|
390
392
|
|
391
393
|
## HTTPS/SSL Issues
|
392
394
|
|
393
|
-
By default excon will try to verify peer certificates when using
|
395
|
+
By default excon will try to verify peer certificates when using HTTPS. Unfortunately on some operating systems the defaults will not work. This will likely manifest itself as something like `Excon::Errors::CertificateError: SSL_connect returned=1 ...`
|
394
396
|
|
395
397
|
If you have the misfortune of running into this problem you have a couple options. If you have certificates but they aren't being auto-discovered, you can specify the path to your certificates:
|
396
398
|
|
data/changelog.txt
CHANGED
@@ -1,3 +1,13 @@
|
|
1
|
+
0.49.0 03/28/2016
|
2
|
+
=================
|
3
|
+
|
4
|
+
fix nonblock ssl socket connect timeout handling
|
5
|
+
fix README debug example
|
6
|
+
make unique class for certificate errors
|
7
|
+
connection logic cleanup
|
8
|
+
change stubs back to global (with local option via defaults)
|
9
|
+
specific handling for set-cookie header exceptions
|
10
|
+
|
1
11
|
0.48.0 03/07/2016
|
2
12
|
=================
|
3
13
|
|
data/excon.gemspec
CHANGED
@@ -13,8 +13,8 @@ Gem::Specification.new do |s|
|
|
13
13
|
## If your rubyforge_project name is different, then edit it and comment out
|
14
14
|
## the sub! line in the Rakefile
|
15
15
|
s.name = 'excon'
|
16
|
-
s.version = '0.
|
17
|
-
s.date = '2016-03-
|
16
|
+
s.version = '0.49.0'
|
17
|
+
s.date = '2016-03-28'
|
18
18
|
s.rubyforge_project = 'excon'
|
19
19
|
|
20
20
|
## Make sure your summary is short. The description may be as long
|
data/lib/excon.rb
CHANGED
@@ -210,9 +210,14 @@ module Excon
|
|
210
210
|
nil
|
211
211
|
end
|
212
212
|
|
213
|
-
# get a list of defined stubs
|
213
|
+
# get a list of defined stubs
|
214
214
|
def stubs
|
215
|
-
|
215
|
+
case Excon.defaults[:stubs]
|
216
|
+
when :global
|
217
|
+
@stubs ||= []
|
218
|
+
when :local
|
219
|
+
Thread.current[:_excon_stubs] ||= []
|
220
|
+
end
|
216
221
|
end
|
217
222
|
|
218
223
|
# remove first/oldest stub matching request_params
|
data/lib/excon/connection.rb
CHANGED
@@ -157,7 +157,7 @@ module Excon
|
|
157
157
|
end
|
158
158
|
elsif body.nil?
|
159
159
|
socket.write(request) # write out request + headers
|
160
|
-
|
160
|
+
else # write out body
|
161
161
|
if body.respond_to?(:binmode)
|
162
162
|
body.binmode
|
163
163
|
end
|
@@ -189,7 +189,7 @@ module Excon
|
|
189
189
|
when Excon::Errors::StubNotFound, Excon::Errors::Timeout
|
190
190
|
raise(error)
|
191
191
|
else
|
192
|
-
|
192
|
+
raise_socket_error(error)
|
193
193
|
end
|
194
194
|
end
|
195
195
|
|
@@ -392,7 +392,7 @@ module Excon
|
|
392
392
|
when Excon::Errors::HTTPStatusError, Excon::Errors::Timeout
|
393
393
|
raise(error)
|
394
394
|
else
|
395
|
-
|
395
|
+
raise_socket_error(error)
|
396
396
|
end
|
397
397
|
end
|
398
398
|
|
@@ -415,6 +415,14 @@ module Excon
|
|
415
415
|
end
|
416
416
|
end
|
417
417
|
|
418
|
+
def raise_socket_error(error)
|
419
|
+
if error.message =~ /certificate verify failed/
|
420
|
+
raise(Excon::Errors::CertificateError.new(error))
|
421
|
+
else
|
422
|
+
raise(Excon::Errors::SocketError.new(error))
|
423
|
+
end
|
424
|
+
end
|
425
|
+
|
418
426
|
def setup_proxy
|
419
427
|
if @data[:disable_proxy]
|
420
428
|
if @data[:proxy]
|
data/lib/excon/constants.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
module Excon
|
2
2
|
|
3
|
-
VERSION = '0.
|
3
|
+
VERSION = '0.49.0'
|
4
4
|
|
5
5
|
CR_NL = "\r\n"
|
6
6
|
|
@@ -133,6 +133,7 @@ module Excon
|
|
133
133
|
:retry_limit => DEFAULT_RETRY_LIMIT,
|
134
134
|
:ssl_verify_peer => true,
|
135
135
|
:ssl_uri_schemes => [HTTPS],
|
136
|
+
:stubs => :global,
|
136
137
|
:tcp_nodelay => false,
|
137
138
|
:thread_safe_sockets => true,
|
138
139
|
:uri_parser => URI,
|
data/lib/excon/errors.rb
CHANGED
@@ -8,12 +8,29 @@ module Excon
|
|
8
8
|
class SocketError < Error
|
9
9
|
attr_reader :socket_error
|
10
10
|
|
11
|
-
def initialize(socket_error=Excon::Errors::Error.new)
|
12
|
-
if
|
13
|
-
super
|
11
|
+
def initialize(socket_error = Excon::Errors::Error.new)
|
12
|
+
if is_a?(CertificateError)
|
13
|
+
super
|
14
14
|
else
|
15
15
|
super("#{socket_error.message} (#{socket_error.class})")
|
16
|
+
set_backtrace(socket_error.backtrace)
|
17
|
+
@socket_error = socket_error
|
16
18
|
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class CertificateError < SocketError
|
23
|
+
def initialize(socket_error = Excon::Errors::Error.new)
|
24
|
+
msg = "Unable to verify certificate. This may be an issue with the remote host or with Excon." +
|
25
|
+
"Excon has certificates bundled, but these can be customized." +
|
26
|
+
"`Excon.defaults[:ssl_ca_path] = path_to_certs`, " +
|
27
|
+
"`ENV['SSL_CERT_DIR'] = path_to_certs`, " +
|
28
|
+
"`Excon.defaults[:ssl_ca_file] = path_to_file`, " +
|
29
|
+
"`ENV['SSL_CERT_FILE'] = path_to_file`, " +
|
30
|
+
"`Excon.defaults[:ssl_verify_callback] = callback` (see OpenSSL::SSL::SSLContext#verify_callback), or " +
|
31
|
+
"`Excon.defaults[:ssl_verify_peer] = false` (less secure)."
|
32
|
+
full_message = "#{socket_error.message} (#{socket_error.class})" + ' ' + msg
|
33
|
+
super(full_message)
|
17
34
|
set_backtrace(socket_error.backtrace)
|
18
35
|
@socket_error = socket_error
|
19
36
|
end
|
data/lib/excon/response.rb
CHANGED
@@ -66,6 +66,7 @@ module Excon
|
|
66
66
|
|
67
67
|
datum[:response] = {
|
68
68
|
:body => '',
|
69
|
+
:cookies => [],
|
69
70
|
:host => datum[:host],
|
70
71
|
:headers => Excon::Headers.new,
|
71
72
|
:path => datum[:path],
|
@@ -184,6 +185,9 @@ module Excon
|
|
184
185
|
raise Excon::Errors::ResponseParseError, 'malformed header' unless value
|
185
186
|
# add key/value or append value to existing values
|
186
187
|
datum[:response][:headers][key] = ([datum[:response][:headers][key]] << value.strip).compact.join(', ')
|
188
|
+
if key.casecmp('Set-Cookie') == 0
|
189
|
+
datum[:response][:cookies] << value.strip
|
190
|
+
end
|
187
191
|
last_key = key
|
188
192
|
end
|
189
193
|
end
|
data/lib/excon/socket.rb
CHANGED
@@ -44,19 +44,11 @@ module Excon
|
|
44
44
|
begin
|
45
45
|
buffer << @socket.read_nonblock(1) while buffer[-1] != "\n"
|
46
46
|
buffer
|
47
|
-
rescue Errno::EAGAIN, Errno::EWOULDBLOCK, IO::WaitReadable
|
48
|
-
|
49
|
-
raise_timeout_error('read')
|
50
|
-
else
|
51
|
-
retry
|
52
|
-
end
|
47
|
+
rescue Errno::EAGAIN, Errno::EWOULDBLOCK, IO::WaitReadable
|
48
|
+
select_with_timeout(@socket, :read) && retry
|
53
49
|
rescue OpenSSL::SSL::SSLError => error
|
54
50
|
if error.message == 'read would block'
|
55
|
-
|
56
|
-
raise_timeout_error('read')
|
57
|
-
else
|
58
|
-
retry
|
59
|
-
end
|
51
|
+
select_with_timeout(@socket, :read) && retry
|
60
52
|
else
|
61
53
|
raise(error)
|
62
54
|
end
|
@@ -135,9 +127,7 @@ module Excon
|
|
135
127
|
end
|
136
128
|
@socket = socket
|
137
129
|
rescue Errno::EINPROGRESS
|
138
|
-
|
139
|
-
raise(Excon::Errors::Timeout.new('connect timeout reached'))
|
140
|
-
end
|
130
|
+
select_with_timeout(socket, :connect_write)
|
141
131
|
begin
|
142
132
|
socket.connect_nonblock(sockaddr)
|
143
133
|
@socket = socket
|
@@ -174,22 +164,14 @@ module Excon
|
|
174
164
|
end
|
175
165
|
rescue OpenSSL::SSL::SSLError => error
|
176
166
|
if error.message == 'read would block'
|
177
|
-
|
178
|
-
raise_timeout_error('read')
|
179
|
-
else
|
180
|
-
retry
|
181
|
-
end
|
167
|
+
select_with_timeout(@socket, :read) && retry
|
182
168
|
else
|
183
169
|
raise(error)
|
184
170
|
end
|
185
171
|
rescue Errno::EAGAIN, Errno::EWOULDBLOCK, IO::WaitReadable
|
186
172
|
if @read_buffer.empty?
|
187
173
|
# if we didn't read anything, try again...
|
188
|
-
|
189
|
-
raise_timeout_error('read')
|
190
|
-
else
|
191
|
-
retry
|
192
|
-
end
|
174
|
+
select_with_timeout(@socket, :read) && retry
|
193
175
|
end
|
194
176
|
rescue EOFError
|
195
177
|
@eof = true
|
@@ -211,21 +193,13 @@ module Excon
|
|
211
193
|
@socket.read(max_length)
|
212
194
|
rescue OpenSSL::SSL::SSLError => error
|
213
195
|
if error.message == 'read would block'
|
214
|
-
|
215
|
-
raise_timeout_error('read')
|
216
|
-
else
|
217
|
-
retry
|
218
|
-
end
|
196
|
+
select_with_timeout(@socket, :read) && retry
|
219
197
|
else
|
220
198
|
raise(error)
|
221
199
|
end
|
222
200
|
rescue Errno::EAGAIN, Errno::EWOULDBLOCK, IO::WaitReadable
|
223
201
|
if @read_buffer.empty?
|
224
|
-
|
225
|
-
raise_timeout_error('read')
|
226
|
-
else
|
227
|
-
retry
|
228
|
-
end
|
202
|
+
select_with_timeout(@socket, :read) && retry
|
229
203
|
end
|
230
204
|
rescue EOFError
|
231
205
|
@eof = true
|
@@ -253,11 +227,7 @@ module Excon
|
|
253
227
|
if error.is_a?(OpenSSL::SSL::SSLError) && error.message != 'write would block'
|
254
228
|
raise error
|
255
229
|
else
|
256
|
-
|
257
|
-
raise_timeout_error('write')
|
258
|
-
else
|
259
|
-
retry
|
260
|
-
end
|
230
|
+
select_with_timeout(@socket, :write) && retry
|
261
231
|
end
|
262
232
|
end
|
263
233
|
|
@@ -278,25 +248,22 @@ module Excon
|
|
278
248
|
if error.is_a?(OpenSSL::SSL::SSLError) && error.message != 'write would block'
|
279
249
|
raise error
|
280
250
|
else
|
281
|
-
|
282
|
-
raise_timeout_error('write')
|
283
|
-
else
|
284
|
-
retry
|
285
|
-
end
|
251
|
+
select_with_timeout(@socket, :write) && retry
|
286
252
|
end
|
287
253
|
end
|
288
254
|
|
289
|
-
def
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
255
|
+
def select_with_timeout(socket, type)
|
256
|
+
select = case type
|
257
|
+
when :connect_read
|
258
|
+
IO.select([socket], nil, nil, @data[:connect_timeout])
|
259
|
+
when :connect_write
|
260
|
+
IO.select(nil, [socket], nil, @data[:connect_timeout])
|
261
|
+
when :read
|
262
|
+
IO.select([socket], nil, nil, @data[:read_timeout])
|
263
|
+
when :write
|
264
|
+
IO.select(nil, [socket], nil, @data[:write_timeout])
|
294
265
|
end
|
295
|
-
|
296
|
-
end
|
297
|
-
|
298
|
-
def raise_timeout_error(type)
|
299
|
-
fail Excon::Errors::Timeout.new("#{type} timeout reached")
|
266
|
+
select || raise(Excon::Errors::Timeout.new("#{type} timeout reached"))
|
300
267
|
end
|
301
268
|
|
302
269
|
def unpacked_sockaddr
|
data/lib/excon/ssl_socket.rb
CHANGED
@@ -119,11 +119,9 @@ module Excon
|
|
119
119
|
begin
|
120
120
|
@socket.connect_nonblock
|
121
121
|
rescue Errno::EAGAIN, Errno::EWOULDBLOCK, IO::WaitReadable
|
122
|
-
|
123
|
-
retry
|
122
|
+
select_with_timeout(@socket, :connect_read) && retry
|
124
123
|
rescue IO::WaitWritable
|
125
|
-
|
126
|
-
retry
|
124
|
+
select_with_timeout(@socket, :connect_write) && retry
|
127
125
|
end
|
128
126
|
else
|
129
127
|
@socket.connect
|
@@ -252,25 +252,41 @@ Shindo.tests('Excon stubs') do
|
|
252
252
|
Excon.stubs.clear
|
253
253
|
end
|
254
254
|
|
255
|
-
tests("
|
256
|
-
q1, q2 = Queue.new, Queue.new
|
255
|
+
tests("global stubs") do
|
257
256
|
connection = Excon.new('http://127.0.0.1:9292', :mock => true)
|
258
257
|
Excon.stub({}, {:body => '1'})
|
259
258
|
t = Thread.new do
|
260
259
|
Excon.stub({}, {:body => '2'})
|
261
|
-
q1.push nil
|
262
|
-
q2.pop
|
263
260
|
connection.request(:method => :get).body
|
264
261
|
end
|
265
|
-
|
266
|
-
|
262
|
+
tests("get on a different thread").returns('2') do
|
263
|
+
t.join.value
|
264
|
+
end
|
265
|
+
tests("get on main thread").returns('2') do
|
266
|
+
connection.request(:method => :get).body
|
267
|
+
end
|
268
|
+
Excon.stubs.clear
|
269
|
+
end
|
270
|
+
|
271
|
+
tests("thread-local stubs") do
|
272
|
+
original_stubs_value = Excon.defaults[:stubs]
|
273
|
+
Excon.defaults[:stubs] = :local
|
274
|
+
|
275
|
+
connection = Excon.new('http://127.0.0.1:9292', :mock => true)
|
276
|
+
Excon.stub({}, {:body => '1'})
|
277
|
+
t = Thread.new do
|
278
|
+
Excon.stub({}, {:body => '2'})
|
267
279
|
connection.request(:method => :get).body
|
268
280
|
end
|
269
|
-
q2.push nil
|
270
281
|
tests("get on a different thread").returns('2') do
|
271
282
|
t.join.value
|
272
283
|
end
|
284
|
+
tests("get on main thread").returns('1') do
|
285
|
+
connection.request(:method => :get).body
|
286
|
+
end
|
273
287
|
Excon.stubs.clear
|
288
|
+
|
289
|
+
Excon.defaults[:stubs] = original_stubs_value
|
274
290
|
end
|
275
291
|
|
276
292
|
env_restore
|
data/tests/response_tests.rb
CHANGED
@@ -151,6 +151,15 @@ Shindo.tests('Excon Response Parsing') do
|
|
151
151
|
|
152
152
|
end
|
153
153
|
|
154
|
+
tests('cookies') do
|
155
|
+
|
156
|
+
tests('parses cookies into array').returns(['one, two', 'three, four']) do
|
157
|
+
resp = Excon.get('http://127.0.0.1:9292/unknown/cookies')
|
158
|
+
resp[:cookies]
|
159
|
+
end
|
160
|
+
|
161
|
+
end
|
162
|
+
|
154
163
|
tests('header continuation') do
|
155
164
|
|
156
165
|
tests('proper continuation').returns('one, two, three, four, five, six') do
|
data/tests/servers/good.rb
CHANGED
@@ -139,6 +139,13 @@ module GoodServer
|
|
139
139
|
|
140
140
|
when 'unknown'
|
141
141
|
case path
|
142
|
+
when 'cookies'
|
143
|
+
start_response(:persistent => false)
|
144
|
+
send_data "Set-Cookie: one, two\r\n"
|
145
|
+
send_data "Set-Cookie: three, four\r\n"
|
146
|
+
send_data "\r\n"
|
147
|
+
send_data "hello world"
|
148
|
+
|
142
149
|
when 'simple'
|
143
150
|
start_response(:persistent => false)
|
144
151
|
send_data "\r\n"
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: excon
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.49.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- dpiddy (Dan Peterson)
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2016-03-
|
13
|
+
date: 2016-03-28 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: activesupport
|