excon 0.16.10 → 0.17.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.

@@ -21,7 +21,7 @@ module Excon
21
21
 
22
22
  HTTP_1_1 = " HTTP/1.1\r\n"
23
23
 
24
- HTTP_VERBS = %w{connect delete get head options post put trace}
24
+ HTTP_VERBS = %w{connect delete get head options post put trace patch}
25
25
 
26
26
  HTTPS = 'https'
27
27
 
@@ -29,7 +29,7 @@ module Excon
29
29
 
30
30
  REDACTED = 'REDACTED'
31
31
 
32
- VERSION = '0.16.10'
32
+ VERSION = '0.17.0'
33
33
 
34
34
  unless ::IO.const_defined?(:WaitReadable)
35
35
  class ::IO
@@ -121,6 +121,13 @@ module Excon
121
121
  504 => [Excon::Errors::GatewayTimeout, 'Gateway Timeout']
122
122
  }
123
123
  error, message = @errors[response.status] || [Excon::Errors::HTTPStatusError, 'Unknown']
124
+
125
+ # scrub authorization
126
+ if request[:headers].has_key?('Authorization')
127
+ request = request.dup
128
+ request[:headers] = request[:headers].dup
129
+ request[:headers]['Authorization'] = REDACTED
130
+ end
124
131
  error.new("Expected(#{request[:expects].inspect}) <=> Actual(#{response.status} #{message})\n request => #{request.inspect}\n response => #{response.inspect}", request, response)
125
132
  end
126
133
 
@@ -0,0 +1,19 @@
1
+ module Excon
2
+ module Middleware
3
+ class Expects
4
+ def initialize(app)
5
+ @app = app
6
+ end
7
+
8
+ def call(datum)
9
+ response_datum = @app.call(datum)
10
+
11
+ if datum.has_key?(:expects) && ![*datum[:expects]].include?(response_datum[:status])
12
+ raise(Excon::Errors.status_error(datum, Excon::Response.new(response_datum)))
13
+ else
14
+ response_datum
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,26 @@
1
+ module Excon
2
+ module Middleware
3
+ class Instrumentor
4
+ def initialize(app)
5
+ @app = app
6
+ end
7
+
8
+ def call(datum)
9
+ if datum.has_key?(:instrumentor)
10
+ if datum[:retries_remaining] < datum[:retry_limit]
11
+ event_name = "#{datum[:instrumentor_name]}.retry"
12
+ else
13
+ event_name = "#{datum[:instrumentor_name]}.request"
14
+ end
15
+ response_datum = datum[:instrumentor].instrument(event_name, datum) do
16
+ @app.call(datum)
17
+ end
18
+ datum[:instrumentor].instrument("#{datum[:instrumentor_name]}.response", response_datum)
19
+ response_datum
20
+ else
21
+ @app.call(datum)
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -1,29 +1,41 @@
1
1
  module Excon
2
2
  class Response
3
3
 
4
- attr_accessor :body, :headers, :status
4
+ attr_accessor :body, :headers, :status, :remote_ip
5
5
 
6
- def attributes
6
+ def data
7
7
  {
8
- :body => body,
9
- :headers => headers,
10
- :status => status
8
+ :body => body,
9
+ :headers => headers,
10
+ :status => status,
11
+ :remote_ip => remote_ip
11
12
  }
12
13
  end
13
14
 
14
- def initialize(attrs={})
15
- @body = attrs[:body] || ''
16
- @headers = attrs[:headers] || {}
17
- @status = attrs[:status]
15
+ def params
16
+ $stderr.puts("Excon::Response#params is deprecated use Excon::Response#data instead (#{caller.first})")
17
+ data
18
18
  end
19
19
 
20
- def self.parse(socket, params={})
21
- response = new(:status => socket.read(12)[9, 11].to_i)
20
+ def initialize(params={})
21
+ @body = params[:body] || ''
22
+ @headers = params[:headers] || {}
23
+ @status = params[:status]
24
+ @remote_ip = params[:remote_ip]
25
+ end
26
+
27
+ def self.parse(socket, datum={})
28
+ response_datum = {
29
+ :body => '',
30
+ :headers => {},
31
+ :status => socket.read(12)[9, 11].to_i,
32
+ :remote_ip => socket.remote_ip
33
+ }
22
34
  socket.readline # read the rest of the status line and CRLF
23
35
 
24
36
  until ((data = socket.readline).chop!).empty?
25
37
  key, value = data.split(/:\s*/, 2)
26
- response.headers[key] = ([*response.headers[key]] << value).compact.join(', ')
38
+ response_datum[:headers][key] = ([*response_datum[:headers][key]] << value).compact.join(', ')
27
39
  if key.casecmp('Content-Length') == 0
28
40
  content_length = value.to_i
29
41
  elsif (key.casecmp('Transfer-Encoding') == 0) && (value.casecmp('chunked') == 0)
@@ -31,47 +43,47 @@ module Excon
31
43
  end
32
44
  end
33
45
 
34
- unless (['HEAD', 'CONNECT'].include?(params[:method].to_s.upcase)) || NO_ENTITY.include?(response.status)
46
+ unless (['HEAD', 'CONNECT'].include?(datum[:method].to_s.upcase)) || NO_ENTITY.include?(response_datum[:status])
35
47
 
36
48
  # check to see if expects was set and matched
37
- expected_status = !params.has_key?(:expects) || [*params[:expects]].include?(response.status)
49
+ expected_status = !datum.has_key?(:expects) || [*datum[:expects]].include?(response_datum[:status])
38
50
 
39
51
  # if expects matched and there is a block, use it
40
- if expected_status && params.has_key?(:response_block)
52
+ if expected_status && datum.has_key?(:response_block)
41
53
  if transfer_encoding_chunked
42
54
  # 2 == "/r/n".length
43
55
  while (chunk_size = socket.readline.chop!.to_i(16)) > 0
44
- params[:response_block].call(socket.read(chunk_size + 2).chop!, nil, nil)
56
+ datum[:response_block].call(socket.read(chunk_size + 2).chop!, nil, nil)
45
57
  end
46
58
  socket.read(2)
47
59
  elsif remaining = content_length
48
60
  while remaining > 0
49
- params[:response_block].call(socket.read([params[:chunk_size], remaining].min), [remaining - params[:chunk_size], 0].max, content_length)
50
- remaining -= params[:chunk_size]
61
+ datum[:response_block].call(socket.read([datum[:chunk_size], remaining].min), [remaining - datum[:chunk_size], 0].max, content_length)
62
+ remaining -= datum[:chunk_size]
51
63
  end
52
64
  else
53
- while remaining = socket.read(params[:chunk_size])
54
- params[:response_block].call(remaining, remaining.length, content_length)
65
+ while remaining = socket.read(datum[:chunk_size])
66
+ datum[:response_block].call(remaining, remaining.length, content_length)
55
67
  end
56
68
  end
57
69
  else # no block or unexpected status
58
70
  if transfer_encoding_chunked
59
71
  while (chunk_size = socket.readline.chop!.to_i(16)) > 0
60
- response.body << socket.read(chunk_size + 2).chop! # 2 == "/r/n".length
72
+ response_datum[:body] << socket.read(chunk_size + 2).chop! # 2 == "/r/n".length
61
73
  end
62
74
  socket.read(2) # 2 == "/r/n".length
63
75
  elsif remaining = content_length
64
76
  while remaining > 0
65
- response.body << socket.read([params[:chunk_size], remaining].min)
66
- remaining -= params[:chunk_size]
77
+ response_datum[:body] << socket.read([datum[:chunk_size], remaining].min)
78
+ remaining -= datum[:chunk_size]
67
79
  end
68
80
  else
69
- response.body << socket.read
81
+ response_datum[:body] << socket.read
70
82
  end
71
83
  end
72
84
  end
73
85
 
74
- response
86
+ response_datum
75
87
  end
76
88
 
77
89
  # Retrieve a specific header value. Header names are treated case-insensitively.
@@ -3,16 +3,32 @@ module Excon
3
3
 
4
4
  extend Forwardable
5
5
 
6
- attr_accessor :params
6
+ attr_accessor :data
7
+
8
+ def params
9
+ $stderr.puts("Excon::Socket#params is deprecated use Excon::Socket#data instead (#{caller.first})")
10
+ @data
11
+ end
12
+ def params=(new_params)
13
+ $stderr.puts("Excon::Socket#params= is deprecated use Excon::Socket#data= instead (#{caller.first})")
14
+ @data = new_params
15
+ end
16
+
17
+ attr_reader :remote_ip
7
18
 
8
19
  def_delegators(:@socket, :close, :close)
9
20
  def_delegators(:@socket, :readline, :readline)
10
21
 
11
- def initialize(params = {}, proxy = nil)
12
- @params, @proxy = params, proxy
22
+ def initialize(data = {})
23
+ @data = data
13
24
  @read_buffer = ''
14
25
  @eof = false
15
26
 
27
+ @data[:family] ||= ::Socket::Constants::AF_UNSPEC
28
+ if @data[:proxy]
29
+ @data[:proxy][:family] ||= ::Socket::Constants::AF_UNSPEC
30
+ end
31
+
16
32
  connect
17
33
  end
18
34
 
@@ -20,24 +36,26 @@ module Excon
20
36
  @socket = nil
21
37
  exception = nil
22
38
 
23
- addrinfo = if @proxy
24
- ::Socket.getaddrinfo(@proxy[:host], @proxy[:port].to_i, ::Socket::Constants::AF_UNSPEC, ::Socket::Constants::SOCK_STREAM)
39
+ addrinfo = if @data[:proxy]
40
+ ::Socket.getaddrinfo(@data[:proxy][:host], @data[:proxy][:port], @data[:proxy][:family], ::Socket::Constants::SOCK_STREAM)
25
41
  else
26
- ::Socket.getaddrinfo(@params[:host], @params[:port].to_i, ::Socket::Constants::AF_UNSPEC, ::Socket::Constants::SOCK_STREAM)
42
+ ::Socket.getaddrinfo(@data[:host], @data[:port], @data[:family], ::Socket::Constants::SOCK_STREAM)
27
43
  end
28
44
 
29
45
  addrinfo.each do |_, port, _, ip, a_family, s_type|
46
+ @remote_ip = ip
47
+
30
48
  # nonblocking connect
31
49
  begin
32
50
  sockaddr = ::Socket.sockaddr_in(port, ip)
33
51
 
34
52
  socket = ::Socket.new(a_family, s_type, 0)
35
53
 
36
- if @params[:nonblock]
54
+ if @data[:nonblock]
37
55
  socket.connect_nonblock(sockaddr)
38
56
  else
39
57
  begin
40
- Timeout.timeout(@params[:connect_timeout]) do
58
+ Timeout.timeout(@data[:connect_timeout]) do
41
59
  socket.connect(sockaddr)
42
60
  end
43
61
  rescue Timeout::Error
@@ -48,7 +66,7 @@ module Excon
48
66
  @socket = socket
49
67
  break
50
68
  rescue Errno::EINPROGRESS
51
- unless IO.select(nil, [socket], nil, @params[:connect_timeout])
69
+ unless IO.select(nil, [socket], nil, @data[:connect_timeout])
52
70
  raise(Excon::Errors::Timeout.new("connect timeout reached"))
53
71
  end
54
72
  begin
@@ -76,10 +94,9 @@ module Excon
76
94
  end
77
95
 
78
96
  def read(max_length=nil)
79
- return nil if @eof
80
97
  if @eof
81
- ''
82
- elsif @params[:nonblock]
98
+ return nil
99
+ elsif @data[:nonblock]
83
100
  begin
84
101
  if max_length
85
102
  until @read_buffer.length >= max_length
@@ -87,12 +104,12 @@ module Excon
87
104
  end
88
105
  else
89
106
  while true
90
- @read_buffer << @socket.read_nonblock(@params[:chunk_size])
107
+ @read_buffer << @socket.read_nonblock(@data[:chunk_size])
91
108
  end
92
109
  end
93
110
  rescue OpenSSL::SSL::SSLError => error
94
111
  if error.message == 'read would block'
95
- if IO.select([@socket], nil, nil, @params[:read_timeout])
112
+ if IO.select([@socket], nil, nil, @data[:read_timeout])
96
113
  retry
97
114
  else
98
115
  raise(Excon::Errors::Timeout.new("read timeout reached"))
@@ -101,7 +118,7 @@ module Excon
101
118
  raise(error)
102
119
  end
103
120
  rescue Errno::EAGAIN, Errno::EWOULDBLOCK, IO::WaitReadable
104
- if IO.select([@socket], nil, nil, @params[:read_timeout])
121
+ if IO.select([@socket], nil, nil, @data[:read_timeout])
105
122
  retry
106
123
  else
107
124
  raise(Excon::Errors::Timeout.new("read timeout reached"))
@@ -117,7 +134,7 @@ module Excon
117
134
  end
118
135
  else
119
136
  begin
120
- Timeout.timeout(@params[:read_timeout]) do
137
+ Timeout.timeout(@data[:read_timeout]) do
121
138
  @socket.read(max_length)
122
139
  end
123
140
  rescue Timeout::Error
@@ -127,7 +144,7 @@ module Excon
127
144
  end
128
145
 
129
146
  def write(data)
130
- if @params[:nonblock]
147
+ if @data[:nonblock]
131
148
  # We normally return from the return in the else block below, but
132
149
  # we guard that data is still something in case we get weird
133
150
  # values and String#[] returns nil. (This behavior has been observed
@@ -139,7 +156,7 @@ module Excon
139
156
  written = @socket.write_nonblock(data)
140
157
  rescue OpenSSL::SSL::SSLError => error
141
158
  if error.message == 'write would block'
142
- if IO.select(nil, [@socket], nil, @params[:write_timeout])
159
+ if IO.select(nil, [@socket], nil, @data[:write_timeout])
143
160
  retry
144
161
  else
145
162
  raise(Excon::Errors::Timeout.new("write timeout reached"))
@@ -148,7 +165,7 @@ module Excon
148
165
  raise(error)
149
166
  end
150
167
  rescue Errno::EAGAIN, Errno::EWOULDBLOCK, IO::WaitWritable
151
- if IO.select(nil, [@socket], nil, @params[:write_timeout])
168
+ if IO.select(nil, [@socket], nil, @data[:write_timeout])
152
169
  retry
153
170
  else
154
171
  raise(Excon::Errors::Timeout.new("write timeout reached"))
@@ -172,7 +189,7 @@ module Excon
172
189
  end
173
190
  else
174
191
  begin
175
- Timeout.timeout(@params[:write_timeout]) do
192
+ Timeout.timeout(@data[:write_timeout]) do
176
193
  @socket.write(data)
177
194
  end
178
195
  rescue Timeout::Error
@@ -1,8 +1,8 @@
1
1
  module Excon
2
2
  class SSLSocket < Socket
3
3
 
4
- def initialize(params = {}, proxy = nil)
5
- @params, @proxy = params, proxy
4
+ def initialize(data = {})
5
+ @data = data
6
6
  check_nonblock_support
7
7
 
8
8
  super
@@ -10,36 +10,31 @@ module Excon
10
10
  # create ssl context
11
11
  ssl_context = OpenSSL::SSL::SSLContext.new
12
12
 
13
- if params[:ssl_verify_peer]
13
+ if @data[:ssl_verify_peer]
14
14
  # turn verification on
15
15
  ssl_context.verify_mode = OpenSSL::SSL::VERIFY_PEER
16
16
 
17
- if params[:ssl_ca_path]
18
- ssl_context.ca_path = params[:ssl_ca_path]
19
- elsif params[:ssl_ca_file]
20
- ssl_context.ca_file = params[:ssl_ca_file]
21
- else
22
- # use default cert store
23
- store = OpenSSL::X509::Store.new
24
- store.set_default_paths
25
- ssl_context.cert_store = store
17
+ if @data[:ssl_ca_path]
18
+ ssl_context.ca_path = @data[:ssl_ca_path]
19
+ elsif @data[:ssl_ca_file]
20
+ ssl_context.ca_file = @data[:ssl_ca_file]
26
21
  end
27
22
  else
28
23
  # turn verification off
29
24
  ssl_context.verify_mode = OpenSSL::SSL::VERIFY_NONE
30
25
  end
31
26
 
32
- if @params.has_key?(:client_cert) && @params.has_key?(:client_key)
33
- ssl_context.cert = OpenSSL::X509::Certificate.new(File.read(@params[:client_cert]))
34
- ssl_context.key = OpenSSL::PKey::RSA.new(File.read(@params[:client_key]))
27
+ if @data.has_key?(:client_cert) && @data.has_key?(:client_key)
28
+ ssl_context.cert = OpenSSL::X509::Certificate.new(File.read(@data[:client_cert]))
29
+ ssl_context.key = OpenSSL::PKey::RSA.new(File.read(@data[:client_key]))
35
30
  end
36
31
 
37
- if @proxy
38
- request = 'CONNECT ' << @params[:host_port] << Excon::HTTP_1_1
39
- request << 'Host: ' << @params[:host_port] << Excon::CR_NL
32
+ if @data[:proxy]
33
+ request = 'CONNECT ' << @data[:host_port] << Excon::HTTP_1_1
34
+ request << 'Host: ' << @data[:host_port] << Excon::CR_NL
40
35
 
41
- if @proxy[:password] || @proxy[:user]
42
- auth = ['' << @proxy[:user].to_s << ':' << @proxy[:password].to_s].pack('m').delete(Excon::CR_NL)
36
+ if @data[:proxy][:password] || @data[:proxy][:user]
37
+ auth = ['' << @data[:proxy][:user].to_s << ':' << @data[:proxy][:password].to_s].pack('m').delete(Excon::CR_NL)
43
38
  request << "Proxy-Authorization: Basic " << auth << Excon::CR_NL
44
39
  end
45
40
 
@@ -61,12 +56,12 @@ module Excon
61
56
 
62
57
  # Server Name Indication (SNI) RFC 3546
63
58
  if @socket.respond_to?(:hostname=)
64
- @socket.hostname = @params[:host]
59
+ @socket.hostname = @data[:host]
65
60
  end
66
61
 
67
62
  # verify connection
68
- if params[:ssl_verify_peer]
69
- @socket.post_connection_check(@params[:host])
63
+ if @data[:ssl_verify_peer]
64
+ @socket.post_connection_check(@data[:host])
70
65
  end
71
66
 
72
67
  @socket
@@ -91,9 +86,9 @@ module Excon
91
86
 
92
87
  def check_nonblock_support
93
88
  # backwards compatability for things lacking nonblock
94
- if !DEFAULT_NONBLOCK && params[:nonblock]
89
+ if !DEFAULT_NONBLOCK && @data[:nonblock]
95
90
  $stderr.puts("Excon nonblock is not supported by your OpenSSL::SSL::SSLSocket")
96
- params[:nonblock] = false
91
+ @data[:nonblock] = false
97
92
  end
98
93
  end
99
94
 
@@ -6,7 +6,7 @@ module Excon
6
6
  params[:headers] = params[:headers].dup
7
7
  params[:headers]['Authorization'] = REDACTED
8
8
  end
9
- $stderr.puts("#{name} #{params}")
9
+ $stderr.puts("#{name} #{params.inspect}")
10
10
  if block_given?
11
11
  yield
12
12
  end
@@ -12,7 +12,7 @@ with_rackup('basic_auth.ru') do
12
12
  end
13
13
 
14
14
  test("authorization header remains correct for #{desc}") do
15
- conn.connection[:headers]['Authorization'] == auth_header
15
+ conn.data[:headers]['Authorization'] == auth_header
16
16
  end
17
17
 
18
18
  end