excon 0.28.0 → 0.29.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.

@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- excon (0.28.0)
4
+ excon (0.29.0)
5
5
 
6
6
  GEM
7
7
  remote: http://rubygems.org/
data/README.md CHANGED
@@ -8,6 +8,7 @@ Excon was designed to be simple, fast and performant. It works great as a genera
8
8
  [![Build Status](https://secure.travis-ci.org/geemus/excon.png)](http://travis-ci.org/geemus/excon)
9
9
  [![Dependency Status](https://gemnasium.com/geemus/excon.png)](https://gemnasium.com/geemus/excon)
10
10
  [![Gem Version](https://fury-badge.herokuapp.com/rb/excon.png)](http://badge.fury.io/rb/excon)
11
+ [![Gittip](http://img.shields.io/gittip/geemus.png)](https://www.gittip.com/geemus/)
11
12
 
12
13
  Getting Started
13
14
  ---------------
@@ -83,9 +84,6 @@ connection.request(:idempotent => true)
83
84
  # this request can be repeated safely, retry up to 6 times
84
85
  connection.request(:idempotent => true, :retry_limit => 6)
85
86
 
86
- # opt-out of nonblocking operations for performance and/or as a workaround
87
- connection.request(:nonblock => false)
88
-
89
87
  # set longer read_timeout (default is 60 seconds)
90
88
  connection.request(:read_timeout => 360)
91
89
 
@@ -104,6 +102,9 @@ connection = Excon.new('http://geemus.com/', :omit_default_port => true)
104
102
 
105
103
  # set longer connect_timeout (default is 60 seconds)
106
104
  connection = Excon.new('http://geemus.com/', :connect_timeout => 360)
105
+
106
+ # opt-out of nonblocking operations for performance and/or as a workaround
107
+ connection = Excon.new('http://geemus.com/', :nonblock => false)
107
108
  ```
108
109
 
109
110
  Chunked Requests
@@ -1,3 +1,16 @@
1
+ 0.29.0 11/07/2013
2
+ =================
3
+
4
+ make nonblock invalid as request key
5
+ add backtrace to all warnings
6
+ do not allow idempotent + pipeline
7
+ close socket after pipeline if needed
8
+ fix Socket#read to match IO.read EOF behavior
9
+ use Socket#read for non-blocking readline
10
+ respect read_timeout for status read
11
+ read response until status line, discard chunked trailer
12
+ fix redirect follower to properly change host
13
+
1
14
  0.28.0 10/28/2013
2
15
  =================
3
16
 
@@ -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.28.0'
17
- s.date = '2013-10-28'
16
+ s.version = '0.29.0'
17
+ s.date = '2013-11-07'
18
18
  s.rubyforge_project = 'excon'
19
19
 
20
20
  ## Make sure your summary is short. The description may be as long
@@ -80,13 +80,13 @@ module Excon
80
80
  def display_warning(warning)
81
81
  # Respect Ruby's $VERBOSE setting, unless EXCON_DEBUG is set
82
82
  if !$VERBOSE.nil? || ENV['EXCON_DEBUG']
83
- $stderr.puts "[excon][WARNING] #{ warning }"
83
+ $stderr.puts '[excon][WARNING] ' << warning << "\n#{ caller.join("\n") }"
84
84
  end
85
85
  end
86
86
 
87
87
  # Status of mocking
88
88
  def mock
89
- display_warning("Excon#mock is deprecated, use Excon.defaults[:mock] instead (#{caller.first})")
89
+ display_warning('Excon#mock is deprecated, use Excon.defaults[:mock] instead.')
90
90
  self.defaults[:mock]
91
91
  end
92
92
 
@@ -94,33 +94,33 @@ module Excon
94
94
  # false is the default and works as expected
95
95
  # true returns a value from stubs or raises
96
96
  def mock=(new_mock)
97
- display_warning("Excon#mock is deprecated, use Excon.defaults[:mock]= instead (#{caller.first})")
97
+ display_warning('Excon#mock is deprecated, use Excon.defaults[:mock]= instead.')
98
98
  self.defaults[:mock] = new_mock
99
99
  end
100
100
 
101
101
  # @return [String] The filesystem path to the SSL Certificate Authority
102
102
  def ssl_ca_path
103
- display_warning("Excon#ssl_ca_path is deprecated, use Excon.defaults[:ssl_ca_path] instead (#{caller.first})")
103
+ display_warning('Excon#ssl_ca_path is deprecated, use Excon.defaults[:ssl_ca_path] instead.')
104
104
  self.defaults[:ssl_ca_path]
105
105
  end
106
106
 
107
107
  # Change path to the SSL Certificate Authority
108
108
  # @return [String] The filesystem path to the SSL Certificate Authority
109
109
  def ssl_ca_path=(new_ssl_ca_path)
110
- display_warning("Excon#ssl_ca_path= is deprecated, use Excon.defaults[:ssl_ca_path]= instead (#{caller.first})")
110
+ display_warning('Excon#ssl_ca_path= is deprecated, use Excon.defaults[:ssl_ca_path]= instead.')
111
111
  self.defaults[:ssl_ca_path] = new_ssl_ca_path
112
112
  end
113
113
 
114
114
  # @return [true, false] Whether or not to verify the peer's SSL certificate / chain
115
115
  def ssl_verify_peer
116
- display_warning("Excon#ssl_verify_peer is deprecated, use Excon.defaults[:ssl_verify_peer] instead (#{caller.first})")
116
+ display_warning('Excon#ssl_verify_peer is deprecated, use Excon.defaults[:ssl_verify_peer] instead.')
117
117
  self.defaults[:ssl_verify_peer]
118
118
  end
119
119
 
120
120
  # Change the status of ssl peer verification
121
121
  # @see Excon#ssl_verify_peer (attr_reader)
122
122
  def ssl_verify_peer=(new_ssl_verify_peer)
123
- display_warning("Excon#ssl_verify_peer= is deprecated, use Excon.defaults[:ssl_verify_peer]= instead (#{caller.first})")
123
+ display_warning('Excon#ssl_verify_peer= is deprecated, use Excon.defaults[:ssl_verify_peer]= instead.')
124
124
  self.defaults[:ssl_verify_peer] = new_ssl_verify_peer
125
125
  end
126
126
 
@@ -5,29 +5,29 @@ module Excon
5
5
  attr_reader :data
6
6
 
7
7
  def connection
8
- Excon.display_warning("Excon::Connection#connection is deprecated use Excon::Connection#data instead (#{caller.first})")
8
+ Excon.display_warning('Excon::Connection#connection is deprecated use Excon::Connection#data instead.')
9
9
  @data
10
10
  end
11
11
  def connection=(new_params)
12
- Excon.display_warning("Excon::Connection#connection= is deprecated. Use of this method may cause unexpected results. (#{caller.first})")
12
+ Excon.display_warning('Excon::Connection#connection= is deprecated. Use of this method may cause unexpected results.')
13
13
  @data = new_params
14
14
  end
15
15
 
16
16
  def params
17
- Excon.display_warning("Excon::Connection#params is deprecated use Excon::Connection#data instead (#{caller.first})")
17
+ Excon.display_warning('Excon::Connection#params is deprecated use Excon::Connection#data instead.')
18
18
  @data
19
19
  end
20
20
  def params=(new_params)
21
- Excon.display_warning("Excon::Connection#params= is deprecated. Use of this method may cause unexpected results. (#{caller.first})")
21
+ Excon.display_warning('Excon::Connection#params= is deprecated. Use of this method may cause unexpected results.')
22
22
  @data = new_params
23
23
  end
24
24
 
25
25
  def proxy
26
- Excon.display_warning("Excon::Connection#proxy is deprecated use Excon::Connection#data[:proxy] instead (#{caller.first})")
26
+ Excon.display_warning('Excon::Connection#proxy is deprecated use Excon::Connection#data[:proxy] instead.')
27
27
  @data[:proxy]
28
28
  end
29
29
  def proxy=(new_proxy)
30
- Excon.display_warning("Excon::Connection#proxy= is deprecated. Use of this method may cause unexpected results. (#{caller.first})")
30
+ Excon.display_warning('Excon::Connection#proxy= is deprecated. Use of this method may cause unexpected results.')
31
31
  @data[:proxy] = new_proxy
32
32
  end
33
33
 
@@ -232,13 +232,19 @@ module Excon
232
232
  end
233
233
 
234
234
  if block_given?
235
- Excon.display_warning("Excon requests with a block are deprecated, pass :response_block instead (#{caller.first})")
235
+ Excon.display_warning('Excon requests with a block are deprecated, pass :response_block instead.')
236
236
  datum[:response_block] = Proc.new
237
237
  end
238
238
 
239
- if datum[:request_block] && datum[:idempotent]
240
- Excon.display_warning("Excon requests with a :request_block can not be :idempotent (#{caller.first})")
241
- datum[:idempotent] = false
239
+ if datum[:idempotent]
240
+ if datum[:request_block]
241
+ Excon.display_warning('Excon requests with a :request_block can not be :idempotent.')
242
+ datum[:idempotent] = false
243
+ end
244
+ if datum[:pipeline]
245
+ Excon.display_warning("Excon requests can not be :idempotent when pipelining.")
246
+ datum[:idempotent] = false
247
+ end
242
248
  end
243
249
 
244
250
  datum[:connection] = self
@@ -274,11 +280,17 @@ module Excon
274
280
  # Sends the supplied requests to the destination host using pipelining.
275
281
  # @pipeline_params [Array<Hash>] pipeline_params An array of one or more optional params, override defaults set in Connection.new, see #request for details
276
282
  def requests(pipeline_params)
277
- pipeline_params.map do |params|
283
+ responses = pipeline_params.map do |params|
278
284
  request(params.merge!(:pipeline => true))
279
285
  end.map do |datum|
280
286
  Excon::Response.new(response(datum)[:response])
281
287
  end
288
+
289
+ if responses.last[:headers]['Connection'] == 'close'
290
+ reset
291
+ end
292
+
293
+ responses
282
294
  end
283
295
 
284
296
  def reset
@@ -295,12 +307,12 @@ module Excon
295
307
  end
296
308
 
297
309
  def retry_limit=(new_retry_limit)
298
- Excon.display_warning("Excon::Connection#retry_limit= is deprecated, pass :retry_limit to the initializer (#{caller.first})")
310
+ Excon.display_warning('Excon::Connection#retry_limit= is deprecated, pass :retry_limit to the initializer.')
299
311
  @data[:retry_limit] = new_retry_limit
300
312
  end
301
313
 
302
314
  def retry_limit
303
- Excon.display_warning("Excon::Connection#retry_limit is deprecated, use Excon::Connection#data[:retry_limit]. (#{caller.first})")
315
+ Excon.display_warning('Excon::Connection#retry_limit is deprecated, use Excon::Connection#data[:retry_limit].')
304
316
  @data[:retry_limit] ||= DEFAULT_RETRY_LIMIT
305
317
  end
306
318
 
@@ -349,7 +361,7 @@ module Excon
349
361
  end
350
362
  invalid_keys = params.keys - valid_keys
351
363
  unless invalid_keys.empty?
352
- Excon.display_warning("Invalid Excon #{validation} keys: #{invalid_keys.map(&:inspect).join(', ')}\n#{ caller.join("\n") }")
364
+ Excon.display_warning("Invalid Excon #{validation} keys: #{invalid_keys.map(&:inspect).join(', ')}")
353
365
  # FIXME: for now, just warn, don't mutate, give things (ie fog) a chance to catch up
354
366
  #params = params.dup
355
367
  #invalid_keys.each {|key| params.delete(key) }
@@ -1,6 +1,6 @@
1
1
  module Excon
2
2
 
3
- VERSION = '0.28.0'
3
+ VERSION = '0.29.0'
4
4
 
5
5
  CR_NL = "\r\n"
6
6
 
@@ -49,7 +49,6 @@ module Excon
49
49
  :method,
50
50
  :middlewares,
51
51
  :mock,
52
- :nonblock,
53
52
  :path,
54
53
  :pipeline,
55
54
  :query,
@@ -73,6 +72,7 @@ module Excon
73
72
  :family,
74
73
  :host,
75
74
  :omit_default_port,
75
+ :nonblock,
76
76
  :password,
77
77
  :port,
78
78
  :proxy,
@@ -21,6 +21,7 @@ module Excon
21
21
  params[:headers].delete('Authorization')
22
22
  params[:headers].delete('Proxy-Connection')
23
23
  params[:headers].delete('Proxy-Authorization')
24
+ params[:headers].delete('Host')
24
25
  params.merge!(
25
26
  :scheme => uri.scheme,
26
27
  :host => uri.host,
@@ -30,13 +30,16 @@ module Excon
30
30
  end
31
31
 
32
32
  def self.parse(socket, datum)
33
+ # this will discard any trailing lines from the previous response if any.
34
+ until match = /^HTTP\/\d+\.\d+\s(\d{3})\s/.match(socket.readline); end
35
+ status = match[1].to_i
36
+
33
37
  datum[:response] = {
34
38
  :body => '',
35
39
  :headers => {},
36
- :status => socket.read(12)[9, 11].to_i,
40
+ :status => status,
37
41
  :remote_ip => socket.respond_to?(:remote_ip) && socket.remote_ip
38
42
  }
39
- socket.readline # read the rest of the status line and CRLF
40
43
 
41
44
  until ((data = socket.readline).chop!).empty?
42
45
  key, value = data.split(/:\s*/, 2)
@@ -56,11 +59,11 @@ module Excon
56
59
  # if expects matched and there is a block, use it
57
60
  if expected_status && datum.has_key?(:response_block)
58
61
  if transfer_encoding_chunked
59
- # 2 == "/r/n".length
62
+ # 2 == "\r\n".length
60
63
  while (chunk_size = socket.readline.chop!.to_i(16)) > 0
61
64
  datum[:response_block].call(socket.read(chunk_size + 2).chop!, nil, nil)
62
65
  end
63
- socket.read(2)
66
+ socket.read(2) # empty chunk-body
64
67
  elsif remaining = content_length
65
68
  while remaining > 0
66
69
  datum[:response_block].call(socket.read([datum[:chunk_size], remaining].min), [remaining - datum[:chunk_size], 0].max, content_length)
@@ -73,10 +76,11 @@ module Excon
73
76
  end
74
77
  else # no block or unexpected status
75
78
  if transfer_encoding_chunked
79
+ # 2 == "\r\n".length
76
80
  while (chunk_size = socket.readline.chop!.to_i(16)) > 0
77
- datum[:response][:body] << socket.read(chunk_size + 2).chop! # 2 == "/r/n".length
81
+ datum[:response][:body] << socket.read(chunk_size + 2).chop!
78
82
  end
79
- socket.read(2) # 2 == "/r/n".length
83
+ socket.read(2) # empty chunk-body
80
84
  elsif remaining = content_length
81
85
  while remaining > 0
82
86
  datum[:response][:body] << socket.read([datum[:chunk_size], remaining].min)
@@ -106,7 +110,7 @@ module Excon
106
110
  end
107
111
 
108
112
  def params
109
- Excon.display_warning("Excon::Response#params is deprecated use Excon::Response#data instead (#{caller.first})")
113
+ Excon.display_warning('Excon::Response#params is deprecated use Excon::Response#data instead.')
110
114
  data
111
115
  end
112
116
 
@@ -7,21 +7,21 @@ module Excon
7
7
  attr_accessor :data
8
8
 
9
9
  def params
10
- Excon.display_warning("Excon::Socket#params is deprecated use Excon::Socket#data instead (#{caller.first})")
10
+ Excon.display_warning('Excon::Socket#params is deprecated use Excon::Socket#data instead.')
11
11
  @data
12
12
  end
13
13
  def params=(new_params)
14
- Excon.display_warning("Excon::Socket#params= is deprecated use Excon::Socket#data= instead (#{caller.first})")
14
+ Excon.display_warning('Excon::Socket#params= is deprecated use Excon::Socket#data= instead.')
15
15
  @data = new_params
16
16
  end
17
17
 
18
18
  attr_reader :remote_ip
19
19
 
20
20
  def_delegators(:@socket, :close, :close)
21
- def_delegators(:@socket, :readline, :readline)
22
21
 
23
22
  def initialize(data = {})
24
23
  @data = data
24
+ @nonblock = data[:nonblock]
25
25
  @read_buffer = ''
26
26
  @eof = false
27
27
 
@@ -30,8 +30,8 @@ module Excon
30
30
 
31
31
  def read(max_length=nil)
32
32
  if @eof
33
- return nil
34
- elsif @data[:nonblock]
33
+ return max_length ? nil : ''
34
+ elsif @nonblock
35
35
  begin
36
36
  if max_length
37
37
  until @read_buffer.length >= max_length
@@ -61,8 +61,13 @@ module Excon
61
61
  rescue EOFError
62
62
  @eof = true
63
63
  end
64
+
64
65
  if max_length
65
- @read_buffer.slice!(0, max_length)
66
+ if @read_buffer.empty?
67
+ nil # EOF met at beginning
68
+ else
69
+ @read_buffer.slice!(0, max_length)
70
+ end
66
71
  else
67
72
  # read until EOFError, so return everything
68
73
  @read_buffer.slice!(0, @read_buffer.length)
@@ -78,8 +83,32 @@ module Excon
78
83
  end
79
84
  end
80
85
 
86
+ def readline
87
+ if @eof
88
+ raise EOFError, 'end of file reached'
89
+ else
90
+ line = ''
91
+ if @nonblock
92
+ while char = read(1)
93
+ line << char
94
+ break if char == $/
95
+ end
96
+ raise EOFError, 'end of file reached' if line.empty?
97
+ else
98
+ begin
99
+ Timeout.timeout(@data[:read_timeout]) do
100
+ line = @socket.readline
101
+ end
102
+ rescue Timeout::Error
103
+ raise Excon::Errors::Timeout.new('read timeout reached')
104
+ end
105
+ end
106
+ line
107
+ end
108
+ end
109
+
81
110
  def write(data)
82
- if @data[:nonblock]
111
+ if @nonblock
83
112
  if FORCE_ENC
84
113
  data.force_encoding('BINARY')
85
114
  end
@@ -144,7 +173,7 @@ module Excon
144
173
 
145
174
  socket = ::Socket.new(a_family, s_type, 0)
146
175
 
147
- if @data[:nonblock]
176
+ if @nonblock
148
177
  socket.connect_nonblock(sockaddr)
149
178
  else
150
179
  begin
@@ -2,9 +2,6 @@ module Excon
2
2
  class SSLSocket < Socket
3
3
 
4
4
  def initialize(data = {})
5
- @data = data
6
- check_nonblock_support
7
-
8
5
  super
9
6
 
10
7
  # create ssl context
@@ -30,7 +27,7 @@ module Excon
30
27
  begin
31
28
  ssl_context.cert_store.add_file(ca_file)
32
29
  rescue => e
33
- Excon.display_warning("Excon unable to add file to cert store, ignoring: #{ca_file}\n[#{e.class}] #{e.message}\n#{e.backtrace.join("\n")}")
30
+ Excon.display_warning("Excon unable to add file to cert store, ignoring: #{ca_file}\n[#{e.class}] #{e.message}")
34
31
  end
35
32
  end
36
33
  else
@@ -88,22 +85,12 @@ module Excon
88
85
  @socket
89
86
  end
90
87
 
91
- def read(max_length=nil)
92
- check_nonblock_support
93
- super
94
- end
95
-
96
- def write(data)
97
- check_nonblock_support
98
- super
99
- end
100
-
101
88
  private
102
89
 
103
90
  def check_nonblock_support
104
91
  # backwards compatability for things lacking nonblock
105
- if !DEFAULT_NONBLOCK && @data[:nonblock]
106
- @data[:nonblock] = false
92
+ if !DEFAULT_NONBLOCK && @nonblock
93
+ @nonblock = false
107
94
  end
108
95
  end
109
96
 
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.28.0
4
+ version: 0.29.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2013-10-28 00:00:00.000000000 Z
14
+ date: 2013-11-07 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: activesupport
@@ -245,7 +245,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
245
245
  version: '0'
246
246
  segments:
247
247
  - 0
248
- hash: 527099065426297719
248
+ hash: -2194157728593914934
249
249
  required_rubygems_version: !ruby/object:Gem::Requirement
250
250
  none: false
251
251
  requirements: