excon 0.38.0 → 0.39.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 +8 -8
- data/Gemfile.lock +1 -1
- data/README.md +3 -0
- data/changelog.txt +23 -0
- data/excon.gemspec +3 -2
- data/lib/excon.rb +41 -59
- data/lib/excon/connection.rb +51 -64
- data/lib/excon/constants.rb +35 -1
- data/lib/excon/errors.rb +12 -13
- data/lib/excon/headers.rb +15 -62
- data/lib/excon/middlewares/idempotent.rb +12 -2
- data/lib/excon/middlewares/response_parser.rb +0 -18
- data/lib/excon/pretty_printer.rb +45 -0
- data/lib/excon/response.rb +45 -32
- data/lib/excon/socket.rb +17 -27
- data/lib/excon/standard_instrumentor.rb +1 -26
- data/lib/excon/utils.rb +5 -13
- data/tests/basic_tests.rb +1 -1
- data/tests/errors_tests.rb +16 -16
- data/tests/rackups/streaming.ru +1 -1
- data/tests/request_tests.rb +0 -57
- data/tests/response_tests.rb +1 -68
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
YzFlMDNlOTJjZTE5MGExNDI4OWNiNWIyZDNkZTZmNWQ4MjA0M2RkOQ==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
ZmZkMzdhZTA0MWNiZWU3MWU0MTMyMzU1Y2QzYWMzOGE2NzcyNjdjNg==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
MDc2MTgzZWEwMzIwNDA0MzM1Mjk4NTM5MGNlODU1YmYwNmUyNGRlM2EyZmY5
|
10
|
+
YWZmNTI1YzhmZjI2MjViOWE2NGVjNjY4YWRjOTY1YzI5YmZjMTM3NGZiNTY1
|
11
|
+
YjM2NjM5ZDUyY2EyMDRjMjgxMWYxMWE2YjNjZWFlZTY0YzgzZWI=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
NDk1YTIzM2RhZjJmY2ViYzBhMGY2Y2FkYTViNzRmMzBhOTZjMjgxNDEyMjMw
|
14
|
+
MTgwMGNiYzcyYmVlY2U0NGRlZTE4OTA1ZWVjNGMyNGVhOTgxYmMxNjJlMGYz
|
15
|
+
ZTAwYjg5MjgxYzYxYTA2MmNmNTMzMzQxMzk1MDNjOTkzNTU4Zjg=
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -85,6 +85,9 @@ Both one-off and persistent connections support many other options. The final op
|
|
85
85
|
Here are a few common examples:
|
86
86
|
|
87
87
|
```ruby
|
88
|
+
# Output debug info, similar to ENV['EXCON_DEBUG']
|
89
|
+
connection = Excon.new('http://geemus.com/', :debug => true)
|
90
|
+
|
88
91
|
# Custom headers
|
89
92
|
Excon.get('http://geemus.com', :headers => {'Authorization' => 'Basic 0123456789ABCDEF'})
|
90
93
|
connection.get(:headers => {'Authorization' => 'Basic 0123456789ABCDEF'})
|
data/changelog.txt
CHANGED
@@ -1,3 +1,26 @@
|
|
1
|
+
0.39.0 08/01/2014
|
2
|
+
=================
|
3
|
+
|
4
|
+
revert to a blocking readline, for performance
|
5
|
+
simplify status lookup
|
6
|
+
consolidate proxy code
|
7
|
+
store defaults as a constant
|
8
|
+
avoid setting nil user/pass vs just no setting keys
|
9
|
+
move idempotent warnings in to middleware
|
10
|
+
simplify validations
|
11
|
+
use constants in utils
|
12
|
+
group non-chunk response paring
|
13
|
+
optimize/simplify socket local lookup
|
14
|
+
simplify to pro-actively build downcased headers instead of lazily do so
|
15
|
+
add version to options (so it will appear in debug)
|
16
|
+
add OS/Ruby version info to options/version for debugging
|
17
|
+
more consistent output styling for errors
|
18
|
+
remove TE stuff to simplify
|
19
|
+
shorten timeout/sleep in streaming tests
|
20
|
+
remove transfer-encoding altogether if it only includes chunked
|
21
|
+
only rescue http status errors in relevant tests
|
22
|
+
use case-insensitive headers in stubs also
|
23
|
+
|
1
24
|
0.38.0 07/09/2014
|
2
25
|
=================
|
3
26
|
|
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 = '2014-
|
16
|
+
s.version = '0.39.0'
|
17
|
+
s.date = '2014-08-01'
|
18
18
|
s.rubyforge_project = 'excon'
|
19
19
|
|
20
20
|
## Make sure your summary is short. The description may be as long
|
@@ -110,6 +110,7 @@ Gem::Specification.new do |s|
|
|
110
110
|
lib/excon/middlewares/mock.rb
|
111
111
|
lib/excon/middlewares/redirect_follower.rb
|
112
112
|
lib/excon/middlewares/response_parser.rb
|
113
|
+
lib/excon/pretty_printer.rb
|
113
114
|
lib/excon/response.rb
|
114
115
|
lib/excon/socket.rb
|
115
116
|
lib/excon/ssl_socket.rb
|
data/lib/excon.rb
CHANGED
@@ -11,75 +11,44 @@ require 'uri'
|
|
11
11
|
require 'zlib'
|
12
12
|
require 'stringio'
|
13
13
|
|
14
|
-
# Define defaults first so they will be available to other files
|
15
|
-
module Excon
|
16
|
-
class << self
|
17
|
-
|
18
|
-
# @return [Hash] defaults for Excon connections
|
19
|
-
def defaults
|
20
|
-
@defaults ||= {
|
21
|
-
:chunk_size => CHUNK_SIZE || DEFAULT_CHUNK_SIZE,
|
22
|
-
:ciphers => 'HIGH:!SSLv2:!aNULL:!eNULL:!3DES',
|
23
|
-
:connect_timeout => 60,
|
24
|
-
:debug_request => false,
|
25
|
-
:debug_response => false,
|
26
|
-
:headers => {
|
27
|
-
'User-Agent' => USER_AGENT
|
28
|
-
},
|
29
|
-
:idempotent => false,
|
30
|
-
:instrumentor_name => 'excon',
|
31
|
-
:middlewares => [
|
32
|
-
Excon::Middleware::ResponseParser,
|
33
|
-
Excon::Middleware::Expects,
|
34
|
-
Excon::Middleware::Idempotent,
|
35
|
-
Excon::Middleware::Instrumentor,
|
36
|
-
Excon::Middleware::Mock
|
37
|
-
],
|
38
|
-
:mock => false,
|
39
|
-
:nonblock => true,
|
40
|
-
:omit_default_port => false,
|
41
|
-
:persistent => false,
|
42
|
-
:read_timeout => 60,
|
43
|
-
:retry_limit => DEFAULT_RETRY_LIMIT,
|
44
|
-
:ssl_verify_peer => true,
|
45
|
-
:tcp_nodelay => false,
|
46
|
-
:uri_parser => URI,
|
47
|
-
:write_timeout => 60
|
48
|
-
}
|
49
|
-
end
|
50
|
-
|
51
|
-
# Change defaults for Excon connections
|
52
|
-
# @return [Hash] defaults for Excon connections
|
53
|
-
def defaults=(new_defaults)
|
54
|
-
@defaults = new_defaults
|
55
|
-
end
|
56
|
-
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
require 'excon/utils'
|
61
|
-
require 'excon/constants'
|
62
|
-
require 'excon/connection'
|
63
|
-
require 'excon/errors'
|
64
14
|
require 'excon/middlewares/base'
|
65
|
-
require 'excon/middlewares/decompress'
|
66
|
-
require 'excon/middlewares/escape_path'
|
67
15
|
require 'excon/middlewares/expects'
|
68
16
|
require 'excon/middlewares/idempotent'
|
69
17
|
require 'excon/middlewares/instrumentor'
|
70
18
|
require 'excon/middlewares/mock'
|
71
|
-
require 'excon/middlewares/redirect_follower'
|
72
19
|
require 'excon/middlewares/response_parser'
|
73
|
-
|
20
|
+
|
21
|
+
require 'excon/constants'
|
22
|
+
require 'excon/utils'
|
23
|
+
|
24
|
+
require 'excon/connection'
|
25
|
+
require 'excon/errors'
|
74
26
|
require 'excon/headers'
|
27
|
+
require 'excon/response'
|
28
|
+
require 'excon/middlewares/decompress'
|
29
|
+
require 'excon/middlewares/escape_path'
|
30
|
+
require 'excon/middlewares/redirect_follower'
|
31
|
+
require 'excon/pretty_printer'
|
75
32
|
require 'excon/socket'
|
76
33
|
require 'excon/ssl_socket'
|
77
|
-
require 'excon/unix_socket'
|
78
34
|
require 'excon/standard_instrumentor'
|
35
|
+
require 'excon/unix_socket'
|
79
36
|
|
37
|
+
# Define defaults first so they will be available to other files
|
80
38
|
module Excon
|
81
39
|
class << self
|
82
40
|
|
41
|
+
# @return [Hash] defaults for Excon connections
|
42
|
+
def defaults
|
43
|
+
@defaults ||= DEFAULTS
|
44
|
+
end
|
45
|
+
|
46
|
+
# Change defaults for Excon connections
|
47
|
+
# @return [Hash] defaults for Excon connections
|
48
|
+
def defaults=(new_defaults)
|
49
|
+
@defaults = new_defaults
|
50
|
+
end
|
51
|
+
|
83
52
|
def display_warning(warning)
|
84
53
|
# Respect Ruby's $VERBOSE setting, unless EXCON_DEBUG is set
|
85
54
|
if !$VERBOSE.nil? || ENV['EXCON_DEBUG']
|
@@ -135,16 +104,22 @@ module Excon
|
|
135
104
|
def new(url, params = {})
|
136
105
|
uri_parser = params[:uri_parser] || Excon.defaults[:uri_parser]
|
137
106
|
uri = uri_parser.parse(url)
|
138
|
-
|
107
|
+
unless uri.scheme
|
108
|
+
raise ArgumentError.new("Invalid URI: #{uri}")
|
109
|
+
end
|
139
110
|
params = {
|
140
111
|
:host => uri.host,
|
141
112
|
:path => uri.path,
|
142
113
|
:port => uri.port,
|
143
114
|
:query => uri.query,
|
144
|
-
:scheme => uri.scheme
|
145
|
-
:user => (Utils.unescape_uri(uri.user) if uri.user),
|
146
|
-
:password => (Utils.unescape_uri(uri.password) if uri.password)
|
115
|
+
:scheme => uri.scheme
|
147
116
|
}.merge!(params)
|
117
|
+
if uri.password
|
118
|
+
params[:password] = Utils.unescape_uri(uri.password)
|
119
|
+
end
|
120
|
+
if uri.user
|
121
|
+
params[:user] = Utils.unescape_uri(uri.user)
|
122
|
+
end
|
148
123
|
Excon::Connection.new(params)
|
149
124
|
end
|
150
125
|
|
@@ -170,6 +145,13 @@ module Excon
|
|
170
145
|
request_params[:headers]['Authorization'] ||= 'Basic ' << ['' << user << ':' << pass].pack('m').delete(Excon::CR_NL)
|
171
146
|
end
|
172
147
|
end
|
148
|
+
if request_params.has_key?(:headers)
|
149
|
+
headers = Excon::Headers.new
|
150
|
+
request_params[:headers].each do |key, value|
|
151
|
+
headers[key] = value
|
152
|
+
end
|
153
|
+
request_params[:headers] = headers
|
154
|
+
end
|
173
155
|
if block_given?
|
174
156
|
if response_params
|
175
157
|
raise(ArgumentError.new("stub requires either response_params OR a block"))
|
data/lib/excon/connection.rb
CHANGED
@@ -57,40 +57,19 @@ module Excon
|
|
57
57
|
params = validate_params(:connection, params)
|
58
58
|
@data.merge!(params)
|
59
59
|
|
60
|
-
|
61
|
-
no_proxy_env = ENV["no_proxy"] || ENV["NO_PROXY"] || ""
|
62
|
-
no_proxy_list = no_proxy_env.scan(/\*?\.?([^\s,:]+)(?::(\d+))?/i).map { |s| [s[0], s[1]] }
|
63
|
-
unless no_proxy_list.index { |h| /(^|\.)#{h[0]}$/.match(@data[:host]) && (h[1].nil? || h[1].to_i == @data[:port]) }
|
64
|
-
if @data[:scheme] == HTTPS && (ENV.has_key?('https_proxy') || ENV.has_key?('HTTPS_PROXY'))
|
65
|
-
@data[:proxy] = setup_proxy(ENV['https_proxy'] || ENV['HTTPS_PROXY'])
|
66
|
-
elsif (ENV.has_key?('http_proxy') || ENV.has_key?('HTTP_PROXY'))
|
67
|
-
@data[:proxy] = setup_proxy(ENV['http_proxy'] || ENV['HTTP_PROXY'])
|
68
|
-
elsif @data.has_key?(:proxy)
|
69
|
-
@data[:proxy] = setup_proxy(@data[:proxy])
|
70
|
-
end
|
71
|
-
end
|
72
|
-
end
|
60
|
+
setup_proxy
|
73
61
|
|
74
|
-
if
|
75
|
-
@data[:
|
76
|
-
# https credentials happen in handshake
|
77
|
-
if @data[:proxy][:user] || @data[:proxy][:password]
|
78
|
-
user, pass = Utils.unescape_form(@data[:proxy][:user].to_s), Utils.unescape_form(@data[:proxy][:password].to_s)
|
79
|
-
auth = ['' << user.to_s << ':' << pass.to_s].pack('m').delete(Excon::CR_NL)
|
80
|
-
@data[:headers]['Proxy-Authorization'] = 'Basic ' << auth
|
81
|
-
end
|
62
|
+
if ENV.has_key?('EXCON_STANDARD_INSTRUMENTOR')
|
63
|
+
@data[:instrumentor] = Excon::StandardInstrumentor
|
82
64
|
end
|
83
65
|
|
84
|
-
if
|
66
|
+
if @data[:debug] != false && @data[:debug] || ENV.has_key?('EXCON_DEBUG')
|
67
|
+
@data[:debug_request] = @data[:debug_response] = true
|
85
68
|
@data[:instrumentor] = Excon::StandardInstrumentor
|
86
|
-
|
87
|
-
if ENV.has_key?('EXCON_DEBUG')
|
88
|
-
@data[:debug_request] = @data[:debug_response] = true
|
89
|
-
end
|
90
69
|
end
|
91
70
|
|
92
71
|
# Use Basic Auth if url contains a login
|
93
|
-
if @data
|
72
|
+
if @data.has_key?(:user) || @data.has_key?(:password)
|
94
73
|
user, pass = Utils.unescape_form(@data[:user].to_s), Utils.unescape_form(@data[:password].to_s)
|
95
74
|
@data[:headers]['Authorization'] ||= 'Basic ' << ['' << user.to_s << ':' << pass.to_s].pack('m').delete(Excon::CR_NL)
|
96
75
|
end
|
@@ -151,13 +130,6 @@ module Excon
|
|
151
130
|
end
|
152
131
|
end
|
153
132
|
|
154
|
-
if datum[:response_block]
|
155
|
-
datum[:headers]['TE'] = 'trailers'
|
156
|
-
else
|
157
|
-
datum[:headers]['TE'] = 'trailers, deflate, gzip'
|
158
|
-
end
|
159
|
-
datum[:headers]['Connection'] = datum[:persistent] ? 'TE' : 'TE, close'
|
160
|
-
|
161
133
|
# add headers to request
|
162
134
|
datum[:headers].each do |key, values|
|
163
135
|
[values].flatten.each do |value|
|
@@ -248,17 +220,6 @@ module Excon
|
|
248
220
|
datum[:response_block] = Proc.new
|
249
221
|
end
|
250
222
|
|
251
|
-
if datum[:idempotent]
|
252
|
-
if datum[:request_block]
|
253
|
-
Excon.display_warning('Excon requests with a :request_block can not be :idempotent.')
|
254
|
-
datum[:idempotent] = false
|
255
|
-
end
|
256
|
-
if datum[:pipeline]
|
257
|
-
Excon.display_warning("Excon requests can not be :idempotent when pipelining.")
|
258
|
-
datum[:idempotent] = false
|
259
|
-
end
|
260
|
-
end
|
261
|
-
|
262
223
|
datum[:connection] = self
|
263
224
|
|
264
225
|
datum[:stack] = datum[:middlewares].map do |middleware|
|
@@ -273,7 +234,7 @@ module Excon
|
|
273
234
|
|
274
235
|
if datum[:persistent]
|
275
236
|
if key = datum[:response][:headers].keys.detect {|k| k.casecmp('Connection') == 0 }
|
276
|
-
if
|
237
|
+
if datum[:response][:headers][key].casecmp('close') == 0
|
277
238
|
reset
|
278
239
|
end
|
279
240
|
end
|
@@ -309,7 +270,7 @@ module Excon
|
|
309
270
|
|
310
271
|
if @data[:persistent]
|
311
272
|
if key = responses.last[:headers].keys.detect {|k| k.casecmp('Connection') == 0 }
|
312
|
-
if
|
273
|
+
if responses.last[:headers][key].casecmp('close') == 0
|
313
274
|
reset
|
314
275
|
end
|
315
276
|
end
|
@@ -384,9 +345,9 @@ module Excon
|
|
384
345
|
def validate_params(validation, params)
|
385
346
|
valid_keys = case validation
|
386
347
|
when :connection
|
387
|
-
|
348
|
+
Excon::VALID_CONNECTION_KEYS
|
388
349
|
when :request
|
389
|
-
|
350
|
+
Excon::VALID_REQUEST_KEYS
|
390
351
|
end
|
391
352
|
invalid_keys = params.keys - valid_keys
|
392
353
|
unless invalid_keys.empty?
|
@@ -423,22 +384,48 @@ module Excon
|
|
423
384
|
Thread.current[:_excon_sockets] ||= {}
|
424
385
|
end
|
425
386
|
|
426
|
-
def setup_proxy
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
387
|
+
def setup_proxy
|
388
|
+
unless @data[:scheme] == UNIX
|
389
|
+
if no_proxy_env = ENV["no_proxy"] || ENV["NO_PROXY"]
|
390
|
+
no_proxy_list = no_proxy_env.scan(/\*?\.?([^\s,:]+)(?::(\d+))?/i).map { |s| [s[0], s[1]] }
|
391
|
+
end
|
392
|
+
|
393
|
+
unless no_proxy_env && no_proxy_list.index { |h| /(^|\.)#{h[0]}$/.match(@data[:host]) && (h[1].nil? || h[1].to_i == @data[:port]) }
|
394
|
+
if @data[:scheme] == HTTPS && (ENV.has_key?('https_proxy') || ENV.has_key?('HTTPS_PROXY'))
|
395
|
+
@data[:proxy] = ENV['https_proxy'] || ENV['HTTPS_PROXY']
|
396
|
+
elsif (ENV.has_key?('http_proxy') || ENV.has_key?('HTTP_PROXY'))
|
397
|
+
@data[:proxy] = ENV['http_proxy'] || ENV['HTTP_PROXY']
|
398
|
+
end
|
399
|
+
end
|
400
|
+
|
401
|
+
case @data[:proxy]
|
402
|
+
when String
|
403
|
+
uri = URI.parse(@data[:proxy])
|
404
|
+
unless uri.host && uri.port && uri.scheme
|
405
|
+
raise Excon::Errors::ProxyParseError, "Proxy is invalid"
|
406
|
+
end
|
407
|
+
@data[:proxy] = {
|
408
|
+
:host => uri.host,
|
409
|
+
:port => uri.port,
|
410
|
+
:scheme => uri.scheme,
|
411
|
+
}
|
412
|
+
if uri.password
|
413
|
+
@data[:proxy][:password] = uri.password
|
414
|
+
end
|
415
|
+
if uri.user
|
416
|
+
@data[:proxy][:user] = uri.user
|
417
|
+
end
|
418
|
+
end
|
419
|
+
|
420
|
+
if @data.has_key?(:proxy) && @data[:scheme] == 'http'
|
421
|
+
@data[:headers]['Proxy-Connection'] ||= 'Keep-Alive'
|
422
|
+
# https credentials happen in handshake
|
423
|
+
if @data[:proxy].has_key?(:user) || @data[:proxy].has_key?(:password)
|
424
|
+
user, pass = Utils.unescape_form(@data[:proxy][:user].to_s), Utils.unescape_form(@data[:proxy][:password].to_s)
|
425
|
+
auth = ['' << user << ':' << pass].pack('m').delete(Excon::CR_NL)
|
426
|
+
@data[:headers]['Proxy-Authorization'] = 'Basic ' << auth
|
427
|
+
end
|
432
428
|
end
|
433
|
-
{
|
434
|
-
:host => uri.host,
|
435
|
-
:password => uri.password,
|
436
|
-
:port => uri.port,
|
437
|
-
:scheme => uri.scheme,
|
438
|
-
:user => uri.user
|
439
|
-
}
|
440
|
-
else
|
441
|
-
proxy
|
442
429
|
end
|
443
430
|
end
|
444
431
|
end
|
data/lib/excon/constants.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
module Excon
|
2
2
|
|
3
|
-
VERSION = '0.
|
3
|
+
VERSION = '0.39.0'
|
4
4
|
|
5
5
|
CR_NL = "\r\n"
|
6
6
|
|
@@ -31,6 +31,8 @@ module Excon
|
|
31
31
|
|
32
32
|
USER_AGENT = 'excon/' << VERSION
|
33
33
|
|
34
|
+
VERSIONS = USER_AGENT + ' (' << RUBY_PLATFORM << ') ruby/' << RUBY_VERSION
|
35
|
+
|
34
36
|
VALID_REQUEST_KEYS = [
|
35
37
|
:body,
|
36
38
|
:captures,
|
@@ -54,6 +56,7 @@ module Excon
|
|
54
56
|
:response_block,
|
55
57
|
:retries_remaining, # used internally
|
56
58
|
:retry_limit,
|
59
|
+
:versions,
|
57
60
|
:write_timeout
|
58
61
|
]
|
59
62
|
|
@@ -96,5 +99,36 @@ module Excon
|
|
96
99
|
module WaitWritable; end
|
97
100
|
end
|
98
101
|
end
|
102
|
+
# these come last as they rely on the above
|
103
|
+
DEFAULTS = {
|
104
|
+
:chunk_size => CHUNK_SIZE || DEFAULT_CHUNK_SIZE,
|
105
|
+
:ciphers => 'HIGH:!SSLv2:!aNULL:!eNULL:!3DES',
|
106
|
+
:connect_timeout => 60,
|
107
|
+
:debug_request => false,
|
108
|
+
:debug_response => false,
|
109
|
+
:headers => {
|
110
|
+
'User-Agent' => USER_AGENT
|
111
|
+
},
|
112
|
+
:idempotent => false,
|
113
|
+
:instrumentor_name => 'excon',
|
114
|
+
:middlewares => [
|
115
|
+
Excon::Middleware::ResponseParser,
|
116
|
+
Excon::Middleware::Expects,
|
117
|
+
Excon::Middleware::Idempotent,
|
118
|
+
Excon::Middleware::Instrumentor,
|
119
|
+
Excon::Middleware::Mock
|
120
|
+
],
|
121
|
+
:mock => false,
|
122
|
+
:nonblock => true,
|
123
|
+
:omit_default_port => false,
|
124
|
+
:persistent => false,
|
125
|
+
:read_timeout => 60,
|
126
|
+
:retry_limit => DEFAULT_RETRY_LIMIT,
|
127
|
+
:ssl_verify_peer => true,
|
128
|
+
:tcp_nodelay => false,
|
129
|
+
:uri_parser => URI,
|
130
|
+
:versions => VERSIONS,
|
131
|
+
:write_timeout => 60
|
132
|
+
}
|
99
133
|
|
100
134
|
end
|
data/lib/excon/errors.rb
CHANGED
@@ -36,7 +36,7 @@ module Excon
|
|
36
36
|
@response = response
|
37
37
|
end
|
38
38
|
end
|
39
|
-
|
39
|
+
|
40
40
|
# HTTP Error classes
|
41
41
|
class Informational < HTTPStatusError; end
|
42
42
|
class Success < HTTPStatusError; end
|
@@ -130,24 +130,23 @@ module Excon
|
|
130
130
|
504 => [Excon::Errors::GatewayTimeout, 'Gateway Timeout']
|
131
131
|
}
|
132
132
|
|
133
|
-
|
133
|
+
error_class, error_message = @errors[response[:status]] || [Excon::Errors::HTTPStatusError, 'Unknown']
|
134
134
|
|
135
|
-
message =
|
135
|
+
message = StringIO.new
|
136
|
+
message.puts("Expected(#{request[:expects].inspect}) <=> Actual(#{response[:status]} #{error_message})")
|
136
137
|
|
137
138
|
if request[:debug_request]
|
138
|
-
|
139
|
-
|
140
|
-
req.reject! {|key, value| [:connection, :stack].include?(key)}
|
141
|
-
if req.has_key?(:headers) && req[:headers].has_key?('Authorization')
|
142
|
-
req[:headers] = req[:headers].dup
|
143
|
-
req[:headers]['Authorization'] = REDACTED
|
144
|
-
end
|
145
|
-
message << "\n request => #{req.inspect}"
|
139
|
+
message.puts('excon.error.request')
|
140
|
+
Excon::PrettyPrinter.pp(message, request)
|
146
141
|
end
|
147
142
|
|
148
|
-
|
143
|
+
if request[:debug_response]
|
144
|
+
message.puts('excon.error.response')
|
145
|
+
Excon::PrettyPrinter.pp(message, response.data)
|
146
|
+
end
|
149
147
|
|
150
|
-
|
148
|
+
message.rewind
|
149
|
+
error_class.new(message.read, request, response)
|
151
150
|
end
|
152
151
|
|
153
152
|
end
|
data/lib/excon/headers.rb
CHANGED
@@ -18,99 +18,52 @@ module Excon
|
|
18
18
|
alias_method :raw_store, :store
|
19
19
|
alias_method :raw_values_at, :values_at
|
20
20
|
|
21
|
+
def initialize
|
22
|
+
@downcased = {}
|
23
|
+
end
|
24
|
+
|
21
25
|
def [](key)
|
22
|
-
|
23
|
-
@downcased[key.downcase]
|
24
|
-
else
|
25
|
-
raw_reader(key)
|
26
|
-
end
|
26
|
+
@downcased[key.downcase]
|
27
27
|
end
|
28
28
|
|
29
29
|
alias_method :[]=, :store
|
30
30
|
def []=(key, value)
|
31
31
|
raw_writer(key, value)
|
32
|
-
|
33
|
-
@downcased[key.downcase] = value
|
34
|
-
end
|
32
|
+
@downcased[key.downcase] = value
|
35
33
|
end
|
36
34
|
|
37
35
|
if SENTINEL.respond_to? :assoc
|
38
36
|
def assoc(obj)
|
39
|
-
|
40
|
-
@downcased.assoc(obj.downcase)
|
41
|
-
else
|
42
|
-
raw_assoc(obj)
|
43
|
-
end
|
37
|
+
@downcased.assoc(obj.downcase)
|
44
38
|
end
|
45
39
|
end
|
46
40
|
|
47
41
|
def delete(key, &proc)
|
48
|
-
|
49
|
-
|
50
|
-
else
|
51
|
-
raw_delete(key, &proc)
|
52
|
-
end
|
42
|
+
raw_delete(key, &proc)
|
43
|
+
@downcased.delete(key.downcase, &proc)
|
53
44
|
end
|
54
45
|
|
55
46
|
def fetch(key, default = nil, &proc)
|
56
|
-
if
|
57
|
-
|
58
|
-
@downcased.fetch(key.downcase, &proc)
|
59
|
-
else
|
60
|
-
@downcased.fetch(key.downcase, default)
|
61
|
-
end
|
47
|
+
if proc
|
48
|
+
@downcased.fetch(key.downcase, &proc)
|
62
49
|
else
|
63
|
-
|
64
|
-
raw_fetch(key, &proc)
|
65
|
-
else
|
66
|
-
raw_fetch(key, default)
|
67
|
-
end
|
50
|
+
@downcased.fetch(key.downcase, default)
|
68
51
|
end
|
69
52
|
end
|
70
53
|
|
71
54
|
alias_method :has_key?, :key?
|
72
55
|
alias_method :has_key?, :member?
|
73
56
|
def has_key?(key)
|
74
|
-
|
75
|
-
index_case_insensitive
|
76
|
-
@downcased.has_key?(key.downcase)
|
77
|
-
end
|
57
|
+
raw_key?(key) || @downcased.has_key?(key.downcase)
|
78
58
|
end
|
79
59
|
|
80
60
|
def rehash
|
81
61
|
raw_rehash
|
82
|
-
|
83
|
-
@downcased.rehash
|
84
|
-
end
|
62
|
+
@downcased.rehash
|
85
63
|
end
|
86
64
|
|
87
65
|
def values_at(*keys)
|
88
|
-
|
89
|
-
if v.nil?
|
90
|
-
index_case_insensitive
|
91
|
-
@downcased[k.downcase]
|
92
|
-
end
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
96
|
-
private
|
97
|
-
|
98
|
-
def should_delegate?(key)
|
99
|
-
if raw_has_key?(key)
|
100
|
-
false
|
101
|
-
else
|
102
|
-
index_case_insensitive
|
103
|
-
true
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
def index_case_insensitive
|
108
|
-
if @downcased.nil?
|
109
|
-
@downcased = {}
|
110
|
-
each_pair do |key, value|
|
111
|
-
@downcased[key.downcase] = value
|
112
|
-
end
|
113
|
-
end
|
66
|
+
@downcased.values_at(*keys.map {|key| key.downcase})
|
114
67
|
end
|
115
68
|
|
116
69
|
end
|
@@ -2,13 +2,23 @@ module Excon
|
|
2
2
|
module Middleware
|
3
3
|
class Idempotent < Excon::Middleware::Base
|
4
4
|
def error_call(datum)
|
5
|
+
if datum[:idempotent]
|
6
|
+
if datum.has_key?(:request_block)
|
7
|
+
Excon.display_warning('Excon requests with a :request_block can not be :idempotent.')
|
8
|
+
datum[:idempotent] = false
|
9
|
+
end
|
10
|
+
if datum.has_key?(:pipeline)
|
11
|
+
Excon.display_warning("Excon requests can not be :idempotent when pipelining.")
|
12
|
+
datum[:idempotent] = false
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
5
16
|
if datum[:idempotent] && [Excon::Errors::Timeout, Excon::Errors::SocketError,
|
6
17
|
Excon::Errors::HTTPStatusError].any? {|ex| datum[:error].kind_of?(ex) } && datum[:retries_remaining] > 1
|
7
18
|
# reduces remaining retries, reset connection, and restart request_call
|
8
19
|
datum[:retries_remaining] -= 1
|
9
20
|
connection = datum.delete(:connection)
|
10
|
-
|
11
|
-
datum.reject! {|key, _| !request_keys.include?(key) }
|
21
|
+
datum.reject! {|key, _| !Excon::VALID_REQUEST_KEYS.include?(key) }
|
12
22
|
connection.request(datum)
|
13
23
|
else
|
14
24
|
@stack.error_call(datum)
|
@@ -4,24 +4,6 @@ module Excon
|
|
4
4
|
def response_call(datum)
|
5
5
|
unless datum.has_key?(:response)
|
6
6
|
datum = Excon::Response.parse(datum[:connection].send(:socket), datum)
|
7
|
-
|
8
|
-
# only requests without a :response_block add 'deflate, gzip' to the TE header.
|
9
|
-
unless datum[:response_block]
|
10
|
-
if key = datum[:response][:headers].keys.detect {|k| k.casecmp('Transfer-Encoding') == 0 }
|
11
|
-
encodings = Utils.split_header_value(datum[:response][:headers][key])
|
12
|
-
if encoding = encodings.last
|
13
|
-
if encoding.casecmp('deflate') == 0
|
14
|
-
# assume inflate omits header
|
15
|
-
datum[:response][:body] = Zlib::Inflate.new(-Zlib::MAX_WBITS).inflate(datum[:response][:body])
|
16
|
-
encodings.pop
|
17
|
-
elsif encoding.casecmp('gzip') == 0 || encoding.casecmp('x-gzip') == 0
|
18
|
-
datum[:response][:body] = Zlib::GzipReader.new(StringIO.new(datum[:response][:body])).read
|
19
|
-
encodings.pop
|
20
|
-
end
|
21
|
-
datum[:response][:headers][key] = encodings.join(', ')
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
7
|
end
|
26
8
|
@stack.response_call(datum)
|
27
9
|
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Excon
|
2
|
+
class PrettyPrinter
|
3
|
+
def self.pp(io, datum, indent=0)
|
4
|
+
datum = datum.dup
|
5
|
+
|
6
|
+
# reduce duplication/noise of output
|
7
|
+
unless datum.is_a?(Excon::Headers)
|
8
|
+
datum.delete(:connection)
|
9
|
+
datum.delete(:stack)
|
10
|
+
|
11
|
+
if datum.has_key?(:headers) && datum[:headers].has_key?('Authorization')
|
12
|
+
datum[:headers] = datum[:headers].dup
|
13
|
+
datum[:headers]['Authorization'] = REDACTED
|
14
|
+
end
|
15
|
+
|
16
|
+
if datum.has_key?(:password)
|
17
|
+
datum[:password] = REDACTED
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
indent += 2
|
22
|
+
max_key_length = datum.keys.map {|key| key.inspect.length}.max
|
23
|
+
datum.keys.sort_by {|key| key.to_s}.each do |key|
|
24
|
+
value = datum[key]
|
25
|
+
io.write("#{' ' * indent}#{key.inspect.ljust(max_key_length)} => ")
|
26
|
+
case value
|
27
|
+
when Array
|
28
|
+
io.puts("[")
|
29
|
+
value.each do |v|
|
30
|
+
io.puts("#{' ' * indent} #{v.inspect}")
|
31
|
+
end
|
32
|
+
io.write("#{' ' * indent}]")
|
33
|
+
when Hash
|
34
|
+
io.puts("{")
|
35
|
+
self.pp(io, value, indent)
|
36
|
+
io.write("#{' ' * indent}}")
|
37
|
+
else
|
38
|
+
io.write("#{value.inspect}")
|
39
|
+
end
|
40
|
+
io.puts
|
41
|
+
end
|
42
|
+
indent -= 2
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/lib/excon/response.rb
CHANGED
@@ -37,18 +37,23 @@ module Excon
|
|
37
37
|
|
38
38
|
def self.parse(socket, datum)
|
39
39
|
# this will discard any trailing lines from the previous response if any.
|
40
|
-
until
|
41
|
-
|
40
|
+
until status = socket.readline[9,11].to_i
|
41
|
+
end
|
42
42
|
|
43
43
|
datum[:response] = {
|
44
44
|
:body => '',
|
45
45
|
:headers => Excon::Headers.new,
|
46
|
-
:status => status
|
47
|
-
:remote_ip => socket.respond_to?(:remote_ip) && socket.remote_ip,
|
48
|
-
:local_port => socket.respond_to?(:local_port) && socket.local_port,
|
49
|
-
:local_address => socket.respond_to?(:local_address) && socket.local_address
|
46
|
+
:status => status
|
50
47
|
}
|
51
48
|
|
49
|
+
unless datum[:scheme] == UNIX
|
50
|
+
datum[:response].merge!(
|
51
|
+
:remote_ip => socket.remote_ip,
|
52
|
+
:local_port => socket.local_port,
|
53
|
+
:local_address => socket.local_address
|
54
|
+
)
|
55
|
+
end
|
56
|
+
|
52
57
|
parse_headers(socket, datum)
|
53
58
|
|
54
59
|
unless (['HEAD', 'CONNECT'].include?(datum[:method].to_s.upcase)) || NO_ENTITY.include?(datum[:response][:status])
|
@@ -57,13 +62,11 @@ module Excon
|
|
57
62
|
encodings = Utils.split_header_value(datum[:response][:headers][key])
|
58
63
|
if (encoding = encodings.last) && encoding.casecmp('chunked') == 0
|
59
64
|
transfer_encoding_chunked = true
|
60
|
-
encodings.
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
if key = datum[:response][:headers].keys.detect {|k| k.casecmp('Content-Length') == 0 }
|
66
|
-
content_length = datum[:response][:headers][key].to_i
|
65
|
+
if encodings.length == 1
|
66
|
+
datum[:response][:headers].delete(key)
|
67
|
+
else
|
68
|
+
datum[:response][:headers][key] = encodings[0...-1].join(', ')
|
69
|
+
end
|
67
70
|
end
|
68
71
|
end
|
69
72
|
|
@@ -102,28 +105,34 @@ module Excon
|
|
102
105
|
end
|
103
106
|
end
|
104
107
|
parse_headers(socket, datum) # merge trailers into headers
|
105
|
-
elsif remaining = content_length
|
106
|
-
if response_block
|
107
|
-
while remaining > 0
|
108
|
-
chunk = socket.read([datum[:chunk_size], remaining].min)
|
109
|
-
response_block.call(chunk, [remaining - chunk.bytesize, 0].max, content_length)
|
110
|
-
remaining -= chunk.bytesize
|
111
|
-
end
|
112
|
-
else
|
113
|
-
while remaining > 0
|
114
|
-
chunk = socket.read([datum[:chunk_size], remaining].min)
|
115
|
-
datum[:response][:body] << chunk
|
116
|
-
remaining -= chunk.bytesize
|
117
|
-
end
|
118
|
-
end
|
119
108
|
else
|
120
|
-
if
|
121
|
-
|
122
|
-
|
109
|
+
if key = datum[:response][:headers].keys.detect {|k| k.casecmp('Content-Length') == 0 }
|
110
|
+
content_length = datum[:response][:headers][key].to_i
|
111
|
+
end
|
112
|
+
|
113
|
+
if remaining = content_length
|
114
|
+
if response_block
|
115
|
+
while remaining > 0
|
116
|
+
chunk = socket.read([datum[:chunk_size], remaining].min)
|
117
|
+
response_block.call(chunk, [remaining - chunk.bytesize, 0].max, content_length)
|
118
|
+
remaining -= chunk.bytesize
|
119
|
+
end
|
120
|
+
else
|
121
|
+
while remaining > 0
|
122
|
+
chunk = socket.read([datum[:chunk_size], remaining].min)
|
123
|
+
datum[:response][:body] << chunk
|
124
|
+
remaining -= chunk.bytesize
|
125
|
+
end
|
123
126
|
end
|
124
127
|
else
|
125
|
-
|
126
|
-
datum[:
|
128
|
+
if response_block
|
129
|
+
while chunk = socket.read(datum[:chunk_size])
|
130
|
+
response_block.call(chunk, nil, nil)
|
131
|
+
end
|
132
|
+
else
|
133
|
+
while chunk = socket.read(datum[:chunk_size])
|
134
|
+
datum[:response][:body] << chunk
|
135
|
+
end
|
127
136
|
end
|
128
137
|
end
|
129
138
|
end
|
@@ -170,6 +179,10 @@ module Excon
|
|
170
179
|
data
|
171
180
|
end
|
172
181
|
|
182
|
+
def pp
|
183
|
+
Excon::PrettyPrinter.pp($stdout, @data)
|
184
|
+
end
|
185
|
+
|
173
186
|
# Retrieve a specific header value. Header names are treated case-insensitively.
|
174
187
|
# @param [String] name Header name
|
175
188
|
def get_header(name)
|
data/lib/excon/socket.rb
CHANGED
@@ -87,26 +87,12 @@ module Excon
|
|
87
87
|
end
|
88
88
|
|
89
89
|
def readline
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
line = ''
|
94
|
-
if @nonblock
|
95
|
-
while char = read(1)
|
96
|
-
line << char
|
97
|
-
break if char == $/
|
98
|
-
end
|
99
|
-
raise EOFError, 'end of file reached' if line.empty?
|
100
|
-
else
|
101
|
-
begin
|
102
|
-
Timeout.timeout(@data[:read_timeout]) do
|
103
|
-
line = @socket.readline
|
104
|
-
end
|
105
|
-
rescue Timeout::Error
|
106
|
-
raise Excon::Errors::Timeout.new('read timeout reached')
|
107
|
-
end
|
90
|
+
begin
|
91
|
+
Timeout.timeout(@data[:read_timeout]) do
|
92
|
+
@socket.readline
|
108
93
|
end
|
109
|
-
|
94
|
+
rescue Timeout::Error
|
95
|
+
raise Excon::Errors::Timeout.new('read timeout reached')
|
110
96
|
end
|
111
97
|
end
|
112
98
|
|
@@ -153,16 +139,12 @@ module Excon
|
|
153
139
|
end
|
154
140
|
end
|
155
141
|
|
156
|
-
def
|
157
|
-
|
158
|
-
rescue ArgumentError => e
|
159
|
-
raise unless e.message == 'not an AF_INET/AF_INET6 sockaddr'
|
142
|
+
def local_address
|
143
|
+
unpacked_sockaddr[1]
|
160
144
|
end
|
161
145
|
|
162
|
-
def
|
163
|
-
|
164
|
-
rescue ArgumentError => e
|
165
|
-
raise unless e.message == 'not an AF_INET/AF_INET6 sockaddr'
|
146
|
+
def local_port
|
147
|
+
unpacked_sockaddr[0]
|
166
148
|
end
|
167
149
|
|
168
150
|
private
|
@@ -247,5 +229,13 @@ module Excon
|
|
247
229
|
end
|
248
230
|
end
|
249
231
|
|
232
|
+
def unpacked_sockaddr
|
233
|
+
@unpacked_sockaddr ||= ::Socket.unpack_sockaddr_in(@socket.to_io.getsockname)
|
234
|
+
rescue ArgumentError => e
|
235
|
+
unless e.message == 'not an AF_INET/AF_INET6 sockaddr'
|
236
|
+
raise
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
250
240
|
end
|
251
241
|
end
|
@@ -17,32 +17,7 @@ module Excon
|
|
17
17
|
end
|
18
18
|
|
19
19
|
$stderr.puts(name)
|
20
|
-
|
21
|
-
pretty_printer = lambda do |hash|
|
22
|
-
indent += 2
|
23
|
-
max_key_length = hash.keys.map {|key| key.inspect.length}.max
|
24
|
-
hash.keys.sort_by {|key| key.to_s}.each do |key|
|
25
|
-
value = hash[key]
|
26
|
-
$stderr.write("#{' ' * indent}#{key.inspect.ljust(max_key_length)} => ")
|
27
|
-
case value
|
28
|
-
when Array
|
29
|
-
$stderr.puts("[")
|
30
|
-
value.each do |v|
|
31
|
-
$stderr.puts("#{' ' * indent} #{v.inspect}")
|
32
|
-
end
|
33
|
-
$stderr.write("#{' ' * indent}]")
|
34
|
-
when Hash
|
35
|
-
$stderr.puts("{")
|
36
|
-
pretty_printer.call(value)
|
37
|
-
$stderr.write("#{' ' * indent}}")
|
38
|
-
else
|
39
|
-
$stderr.write("#{value.inspect}")
|
40
|
-
end
|
41
|
-
$stderr.puts
|
42
|
-
end
|
43
|
-
indent -= 2
|
44
|
-
end
|
45
|
-
pretty_printer.call(params)
|
20
|
+
Excon::PrettyPrinter.pp($stderr, params)
|
46
21
|
|
47
22
|
if block_given?
|
48
23
|
yield
|
data/lib/excon/utils.rb
CHANGED
@@ -2,21 +2,13 @@ module Excon
|
|
2
2
|
module Utils
|
3
3
|
extend self
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
UNESCAPED = /([#{ Regexp.escape(
|
5
|
+
CONTROL = (0x0..0x1f).map {|c| c.chr }.join + "\x7f"
|
6
|
+
DELIMS = '<>#%"'
|
7
|
+
UNWISE = '{}|\\^[]`'
|
8
|
+
NONASCII = (0x80..0xff).map {|c| c.chr }.join
|
9
|
+
UNESCAPED = /([#{ Regexp.escape(CONTROL + ' ' + DELIMS + UNWISE + NONASCII) }])/
|
10
10
|
ESCAPED = /%([0-9a-fA-F]{2})/
|
11
11
|
|
12
|
-
def valid_connection_keys(params = {})
|
13
|
-
Excon::VALID_CONNECTION_KEYS
|
14
|
-
end
|
15
|
-
|
16
|
-
def valid_request_keys(params = {})
|
17
|
-
Excon::VALID_REQUEST_KEYS
|
18
|
-
end
|
19
|
-
|
20
12
|
def connection_uri(datum = @data)
|
21
13
|
unless datum
|
22
14
|
raise ArgumentError, '`datum` must be given unless called on a Connection'
|
data/tests/basic_tests.rb
CHANGED
@@ -23,7 +23,7 @@ Shindo.tests('Excon streaming basics') do
|
|
23
23
|
with_unicorn('streaming.ru') do
|
24
24
|
# expected values: the response, in pieces, and a timeout after each piece
|
25
25
|
res = %w{Hello streamy world}
|
26
|
-
timeout = 1
|
26
|
+
timeout = 0.1
|
27
27
|
|
28
28
|
# expect the full response as a string
|
29
29
|
# and expect it to take a (timeout * pieces) seconds
|
data/tests/errors_tests.rb
CHANGED
@@ -14,32 +14,32 @@ Shindo.tests('HTTPStatusError request/response debugging') do
|
|
14
14
|
tests('message does not include response or response info').returns(true) do
|
15
15
|
begin
|
16
16
|
Excon.get('http://127.0.0.1:9292/error/not_found', :expects => 200)
|
17
|
-
rescue => err
|
17
|
+
rescue Excon::Errors::HTTPStatusError => err
|
18
18
|
err.message.include?('Expected(200) <=> Actual(404 Not Found)') &&
|
19
|
-
|
20
|
-
|
19
|
+
!err.message.include?('excon.error.request') &&
|
20
|
+
!err.message.include?('excon.error.response')
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
|
-
tests('message includes only
|
24
|
+
tests('message includes only request info').returns(true) do
|
25
25
|
begin
|
26
26
|
Excon.get('http://127.0.0.1:9292/error/not_found', :expects => 200,
|
27
|
-
:
|
28
|
-
rescue => err
|
27
|
+
:debug_request => true)
|
28
|
+
rescue Excon::Errors::HTTPStatusError => err
|
29
29
|
err.message.include?('Expected(200) <=> Actual(404 Not Found)') &&
|
30
|
-
|
31
|
-
|
30
|
+
err.message.include?('excon.error.request') &&
|
31
|
+
!err.message.include?('excon.error.response')
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
35
|
-
tests('message includes only
|
35
|
+
tests('message includes only response info').returns(true) do
|
36
36
|
begin
|
37
37
|
Excon.get('http://127.0.0.1:9292/error/not_found', :expects => 200,
|
38
|
-
:
|
39
|
-
rescue => err
|
38
|
+
:debug_response => true)
|
39
|
+
rescue Excon::Errors::HTTPStatusError => err
|
40
40
|
err.message.include?('Expected(200) <=> Actual(404 Not Found)') &&
|
41
|
-
|
42
|
-
|
41
|
+
!err.message.include?('excon.error.request') &&
|
42
|
+
err.message.include?('excon.error.response')
|
43
43
|
end
|
44
44
|
end
|
45
45
|
|
@@ -47,10 +47,10 @@ Shindo.tests('HTTPStatusError request/response debugging') do
|
|
47
47
|
begin
|
48
48
|
Excon.get('http://127.0.0.1:9292/error/not_found', :expects => 200,
|
49
49
|
:debug_request => true, :debug_response => true)
|
50
|
-
rescue => err
|
50
|
+
rescue Excon::Errors::HTTPStatusError => err
|
51
51
|
err.message.include?('Expected(200) <=> Actual(404 Not Found)') &&
|
52
|
-
|
53
|
-
|
52
|
+
err.message.include?('excon.error.request') &&
|
53
|
+
err.message.include?('excon.error.response')
|
54
54
|
end
|
55
55
|
end
|
56
56
|
|
data/tests/rackups/streaming.ru
CHANGED
data/tests/request_tests.rb
CHANGED
@@ -1,45 +1,6 @@
|
|
1
1
|
Shindo.tests('Request Tests') do
|
2
2
|
with_server('good') do
|
3
3
|
|
4
|
-
tests('sets transfer-coding and connection options') do
|
5
|
-
|
6
|
-
tests('without a :response_block') do
|
7
|
-
request = nil
|
8
|
-
|
9
|
-
returns('trailers, deflate, gzip', 'sets encoding options') do
|
10
|
-
request = Marshal.load(
|
11
|
-
Excon.get('http://127.0.0.1:9292/echo/request').body
|
12
|
-
)
|
13
|
-
|
14
|
-
request[:headers]['TE']
|
15
|
-
end
|
16
|
-
|
17
|
-
returns(true, 'TE added to Connection header') do
|
18
|
-
request[:headers]['Connection'].include?('TE')
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
tests('with a :response_block') do
|
23
|
-
request = nil
|
24
|
-
|
25
|
-
returns('trailers', 'does not set encoding options') do
|
26
|
-
captures = capture_response_block do |block|
|
27
|
-
Excon.get('http://127.0.0.1:9292/echo/request',
|
28
|
-
:response_block => block)
|
29
|
-
end
|
30
|
-
data = captures.map {|capture| capture[0] }.join
|
31
|
-
request = Marshal.load(data)
|
32
|
-
|
33
|
-
request[:headers]['TE']
|
34
|
-
end
|
35
|
-
|
36
|
-
returns(true, 'TE added to Connection header') do
|
37
|
-
request[:headers]['Connection'].include?('TE')
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
end
|
42
|
-
|
43
4
|
tests('persistent connections') do
|
44
5
|
|
45
6
|
tests('with default :persistent => true') do
|
@@ -89,24 +50,6 @@ Shindo.tests('Request Tests') do
|
|
89
50
|
end
|
90
51
|
end
|
91
52
|
|
92
|
-
tests('sends `Connection: close`') do
|
93
|
-
returns(true, 'when :persistent => false') do
|
94
|
-
request = Marshal.load(
|
95
|
-
Excon.get('http://127.0.0.1:9292/echo/request',
|
96
|
-
:persistent => false).body
|
97
|
-
)
|
98
|
-
request[:headers]['Connection'].include?('close')
|
99
|
-
end
|
100
|
-
|
101
|
-
returns(false, 'not when :persistent => true') do
|
102
|
-
request = Marshal.load(
|
103
|
-
Excon.get('http://127.0.0.1:9292/echo/request',
|
104
|
-
:persistent => true).body
|
105
|
-
)
|
106
|
-
request[:headers]['Connection'].include?('close')
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
53
|
end
|
111
54
|
|
112
55
|
end
|
data/tests/response_tests.rb
CHANGED
@@ -46,7 +46,7 @@ Shindo.tests('Excon Response Parsing') do
|
|
46
46
|
Excon.get('http://127.0.0.1:9292/chunked/trailers').headers['Test-Header']
|
47
47
|
end
|
48
48
|
|
49
|
-
tests("removes 'chunked' from Transfer-Encoding").returns(
|
49
|
+
tests("removes 'chunked' from Transfer-Encoding").returns(nil) do
|
50
50
|
Excon.get('http://127.0.0.1:9292/chunked/simple').headers['Transfer-Encoding']
|
51
51
|
end
|
52
52
|
|
@@ -153,73 +153,6 @@ Shindo.tests('Excon Response Parsing') do
|
|
153
153
|
|
154
154
|
end
|
155
155
|
|
156
|
-
tests('Transfer-Encoding') do
|
157
|
-
|
158
|
-
tests('used with chunked response') do
|
159
|
-
resp = nil
|
160
|
-
|
161
|
-
tests('server sent transfer-encoding').returns('gzip, chunked') do
|
162
|
-
resp = Excon.post(
|
163
|
-
'http://127.0.0.1:9292/echo/transfer-encoded/chunked',
|
164
|
-
:body => 'hello world'
|
165
|
-
)
|
166
|
-
|
167
|
-
resp[:headers]['Transfer-Encoding-Sent']
|
168
|
-
end
|
169
|
-
|
170
|
-
tests('processed encodings removed from header').returns('') do
|
171
|
-
resp[:headers]['Transfer-Encoding']
|
172
|
-
end
|
173
|
-
|
174
|
-
tests('response body decompressed').returns('hello world') do
|
175
|
-
resp[:body]
|
176
|
-
end
|
177
|
-
end
|
178
|
-
|
179
|
-
tests('used with non-chunked response') do
|
180
|
-
resp = nil
|
181
|
-
|
182
|
-
tests('server sent transfer-encoding').returns('gzip') do
|
183
|
-
resp = Excon.post(
|
184
|
-
'http://127.0.0.1:9292/echo/transfer-encoded',
|
185
|
-
:body => 'hello world'
|
186
|
-
)
|
187
|
-
|
188
|
-
resp[:headers]['Transfer-Encoding-Sent']
|
189
|
-
end
|
190
|
-
|
191
|
-
tests('processed encoding removed from header').returns('') do
|
192
|
-
resp[:headers]['Transfer-Encoding']
|
193
|
-
end
|
194
|
-
|
195
|
-
tests('response body decompressed').returns('hello world') do
|
196
|
-
resp[:body]
|
197
|
-
end
|
198
|
-
end
|
199
|
-
|
200
|
-
# sends TE header without gzip/deflate accepted (see requests_tests)
|
201
|
-
tests('with a :response_block') do
|
202
|
-
captures = nil
|
203
|
-
|
204
|
-
tests('server does not compress').returns('chunked') do
|
205
|
-
resp = nil
|
206
|
-
captures = capture_response_block do |block|
|
207
|
-
resp = Excon.post('http://127.0.0.1:9292/echo/transfer-encoded/chunked',
|
208
|
-
:body => 'hello world',
|
209
|
-
:response_block => block)
|
210
|
-
end
|
211
|
-
|
212
|
-
resp[:headers]['Transfer-Encoding-Sent']
|
213
|
-
end
|
214
|
-
|
215
|
-
tests('block receives uncompressed response').returns('hello world') do
|
216
|
-
captures.map {|capture| capture[0] }.join
|
217
|
-
end
|
218
|
-
|
219
|
-
end
|
220
|
-
|
221
|
-
end
|
222
|
-
|
223
156
|
end
|
224
157
|
|
225
158
|
env_restore
|
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.39.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: 2014-
|
13
|
+
date: 2014-08-01 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: activesupport
|
@@ -173,6 +173,7 @@ files:
|
|
173
173
|
- lib/excon/middlewares/mock.rb
|
174
174
|
- lib/excon/middlewares/redirect_follower.rb
|
175
175
|
- lib/excon/middlewares/response_parser.rb
|
176
|
+
- lib/excon/pretty_printer.rb
|
176
177
|
- lib/excon/response.rb
|
177
178
|
- lib/excon/socket.rb
|
178
179
|
- lib/excon/ssl_socket.rb
|
@@ -245,7 +246,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
245
246
|
version: '0'
|
246
247
|
requirements: []
|
247
248
|
rubyforge_project: excon
|
248
|
-
rubygems_version: 2.
|
249
|
+
rubygems_version: 2.3.0
|
249
250
|
signing_key:
|
250
251
|
specification_version: 2
|
251
252
|
summary: speed, persistence, http(s)
|