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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3f65ecaae53f54ed501b78702355c4519f457a71
4
- data.tar.gz: 6b442dbb19c7925645c4b99ed3316891e8a9996f
3
+ metadata.gz: 89343aee2679e6e6b180e22e1ae3c16f5680bd4c
4
+ data.tar.gz: 354d695485716081f2b7732bbbb2b15964f8e738
5
5
  SHA512:
6
- metadata.gz: 121d8f9a2fec2ea187f8986fbff9bf4b7098fae3af6331b5f35feb330bdc34f3324a83fad280125163c96c0bc4bc7365edeb2d5022a41098420f163edd0ad8c2
7
- data.tar.gz: f187be053a500304f64140c4059c16eb60ff5d8aeb3ed6d9783acfd1015e46d3706572d30304dd9b58121d47eec3f17ed0e375f478660f1b4e3c992bba9f1de9
6
+ metadata.gz: 35185514c9f9460e3409282f8da653832110c6e13813e964d1bc53424fccd4d9dfce972a4811bddae2c9929e19b81860c50326620675b4a23f9c71c59bda24bf
7
+ data.tar.gz: 75d1f86fb2d5d66576d50b183c32d358c43408129ade13adb653a7333b1a2825083d4e66c61dfb476d78729fc4fcca087f14d24fbfa5d4d8dea02815e1efb7ef
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- excon (0.48.0)
4
+ excon (0.49.0)
5
5
 
6
6
  GEM
7
7
  remote: http://rubygems.org/
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/', :debug => true)
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 SSL for HTTPS. Unfortunately on some operating systems the defaults will not work. This will likely manifest itself as something like `Excon::Errors::SocketError: SSL_connect returned=1 ...`
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
 
@@ -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
 
@@ -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.48.0'
17
- s.date = '2016-03-07'
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
@@ -210,9 +210,14 @@ module Excon
210
210
  nil
211
211
  end
212
212
 
213
- # get a list of defined stubs for the current thread
213
+ # get a list of defined stubs
214
214
  def stubs
215
- Thread.current[:_excon_stubs] ||= []
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
@@ -157,7 +157,7 @@ module Excon
157
157
  end
158
158
  elsif body.nil?
159
159
  socket.write(request) # write out request + headers
160
- elsif !body.nil? # write out body
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
- raise(Excon::Errors::SocketError.new(error))
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
- raise(Excon::Errors::SocketError.new(error))
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]
@@ -1,6 +1,6 @@
1
1
  module Excon
2
2
 
3
- VERSION = '0.48.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,
@@ -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 socket_error.message =~ /certificate verify failed/
13
- super("Unable to verify certificate, please set `Excon.defaults[:ssl_ca_path] = path_to_certs`, `ENV['SSL_CERT_DIR'] = path_to_certs`, `Excon.defaults[:ssl_ca_file] = path_to_file`, `ENV['SSL_CERT_FILE'] = path_to_file`, `Excon.defaults[:ssl_verify_callback] = callback` (see OpenSSL::SSL::SSLContext#verify_callback), or `Excon.defaults[:ssl_verify_peer] = false` (less secure).")
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
@@ -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
@@ -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
- if timeout_reached('read')
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
- if timeout_reached('read')
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
- unless IO.select(nil, [socket], nil, @data[:connect_timeout])
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
- if timeout_reached('read')
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
- if timeout_reached('read')
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
- if timeout_reached('read')
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
- if timeout_reached('read')
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
- if timeout_reached('write')
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
- if timeout_reached('write')
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 timeout_reached(type)
290
- if type == 'read'
291
- args = [[@socket], nil, nil, @data[:read_timeout]]
292
- else
293
- args = [nil, [@socket], nil, @data[:write_timeout]]
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
- IO.select(*args) ? nil : true
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
@@ -119,11 +119,9 @@ module Excon
119
119
  begin
120
120
  @socket.connect_nonblock
121
121
  rescue Errno::EAGAIN, Errno::EWOULDBLOCK, IO::WaitReadable
122
- IO.select([@socket])
123
- retry
122
+ select_with_timeout(@socket, :connect_read) && retry
124
123
  rescue IO::WaitWritable
125
- IO.select(nil, [@socket])
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("thread-local stubs") do
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
- q1.pop
266
- tests("get on a different thread").returns('1') do
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
@@ -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
@@ -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.48.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-07 00:00:00.000000000 Z
13
+ date: 2016-03-28 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activesupport