excon 0.99.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +25 -2
- data/data/cacert.pem +622 -438
- data/excon.gemspec +24 -17
- data/lib/excon/connection.rb +27 -14
- data/lib/excon/constants.rb +136 -119
- data/lib/excon/error.rb +2 -2
- data/lib/excon/middlewares/capture_cookies.rb +1 -1
- data/lib/excon/middlewares/decompress.rb +17 -22
- data/lib/excon/middlewares/redirect_follower.rb +3 -3
- data/lib/excon/response.rb +4 -4
- data/lib/excon/socket.rb +121 -31
- data/lib/excon/ssl_socket.rb +4 -3
- data/lib/excon/test/plugin/server/puma.rb +1 -1
- data/lib/excon/test/plugin/server/unicorn.rb +5 -5
- data/lib/excon/test/plugin/server/webrick.rb +1 -1
- data/lib/excon/test/server.rb +2 -9
- data/lib/excon/unix_socket.rb +1 -1
- data/lib/excon/utils.rb +39 -36
- data/lib/excon/version.rb +2 -1
- data/lib/excon.rb +40 -48
- metadata +48 -22
data/excon.gemspec
CHANGED
@@ -1,44 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
$LOAD_PATH.unshift File.join(File.dirname(__FILE__), 'lib')
|
2
4
|
require 'excon/version'
|
3
5
|
|
4
6
|
Gem::Specification.new do |s|
|
5
7
|
s.name = 'excon'
|
6
8
|
s.version = Excon::VERSION
|
7
|
-
s.summary =
|
8
|
-
s.description =
|
9
|
-
s.authors = [
|
9
|
+
s.summary = 'speed, persistence, http(s)'
|
10
|
+
s.description = 'EXtended http(s) CONnections'
|
11
|
+
s.authors = ['dpiddy (Dan Peterson)', 'geemus (Wesley Beary)', 'nextmat (Matt Sanders)']
|
10
12
|
s.email = 'geemus@gmail.com'
|
11
13
|
s.homepage = 'https://github.com/excon/excon'
|
12
14
|
s.license = 'MIT'
|
13
|
-
s.rdoc_options = [
|
15
|
+
s.rdoc_options = ['--charset=UTF-8']
|
14
16
|
s.extra_rdoc_files = %w[README.md CONTRIBUTORS.md CONTRIBUTING.md]
|
15
17
|
s.files = `git ls-files -- data/* lib/*`.split("\n") + [
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
18
|
+
'CONTRIBUTING.md',
|
19
|
+
'CONTRIBUTORS.md',
|
20
|
+
'LICENSE.md',
|
21
|
+
'README.md',
|
22
|
+
'excon.gemspec'
|
21
23
|
]
|
24
|
+
s.required_ruby_version = '>= 3.1.0'
|
25
|
+
|
26
|
+
s.add_dependency 'logger'
|
22
27
|
|
23
|
-
s.add_development_dependency('rspec', '>= 3.5.0')
|
24
28
|
s.add_development_dependency('activesupport')
|
25
29
|
s.add_development_dependency('delorean')
|
26
30
|
s.add_development_dependency('eventmachine', '>= 1.0.4')
|
31
|
+
s.add_development_dependency('json', '>= 1.8.5')
|
27
32
|
s.add_development_dependency('open4')
|
33
|
+
s.add_development_dependency('puma')
|
28
34
|
s.add_development_dependency('rake')
|
35
|
+
s.add_development_dependency('rdoc')
|
36
|
+
s.add_development_dependency('rspec', '>= 3.5.0')
|
29
37
|
s.add_development_dependency('shindo')
|
30
38
|
s.add_development_dependency('sinatra')
|
31
39
|
s.add_development_dependency('sinatra-contrib')
|
32
|
-
s.add_development_dependency('json', '>= 1.8.5')
|
33
|
-
s.add_development_dependency('puma')
|
34
40
|
s.add_development_dependency('webrick')
|
35
41
|
|
36
42
|
s.metadata = {
|
37
|
-
'
|
38
|
-
'
|
39
|
-
'changelog_uri' => 'https://github.com/excon/excon/blob/master/changelog.txt',
|
43
|
+
'bug_tracker_uri' => 'https://github.com/excon/excon/issues',
|
44
|
+
'changelog_uri' => 'https://github.com/excon/excon/blob/master/changelog.txt',
|
40
45
|
'documentation_uri' => 'https://github.com/excon/excon/blob/master/README.md',
|
41
|
-
'
|
42
|
-
'
|
46
|
+
'funding_uri' => 'https://github.com/sponsors/geemus',
|
47
|
+
'homepage_uri' => 'https://github.com/excon/excon',
|
48
|
+
'source_code_uri' => 'https://github.com/excon/excon',
|
49
|
+
'wiki_uri' => 'https://github.com/excon/excon/wiki'
|
43
50
|
}
|
44
51
|
end
|
data/lib/excon/connection.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
require 'ipaddr'
|
3
2
|
|
4
3
|
module Excon
|
5
4
|
class Connection
|
@@ -139,7 +138,7 @@ module Excon
|
|
139
138
|
|
140
139
|
# The HTTP spec isn't clear on it, but specifically, GET requests don't usually send bodies;
|
141
140
|
# if they don't, sending Content-Length:0 can cause issues.
|
142
|
-
unless datum[:method].to_s.casecmp('GET')
|
141
|
+
unless datum[:method].to_s.casecmp?('GET') && body.nil?
|
143
142
|
unless datum[:headers].has_key?('Content-Length')
|
144
143
|
datum[:headers]['Content-Length'] = detect_content_length(body)
|
145
144
|
end
|
@@ -160,7 +159,7 @@ module Excon
|
|
160
159
|
if chunk.length > 0
|
161
160
|
socket(datum).write(chunk.length.to_s(16) << CR_NL << chunk << CR_NL)
|
162
161
|
else
|
163
|
-
socket(datum).write(
|
162
|
+
socket(datum).write("0#{CR_NL}#{CR_NL}")
|
164
163
|
break
|
165
164
|
end
|
166
165
|
end
|
@@ -195,7 +194,7 @@ module Excon
|
|
195
194
|
raise(error)
|
196
195
|
when Errno::EPIPE
|
197
196
|
# Read whatever remains in the pipe to aid in debugging
|
198
|
-
response = socket.read
|
197
|
+
response = socket.read rescue ""
|
199
198
|
error = Excon::Error.new(response + error.message)
|
200
199
|
raise_socket_error(error)
|
201
200
|
else
|
@@ -230,6 +229,13 @@ module Excon
|
|
230
229
|
def request(params={}, &block)
|
231
230
|
# @data has defaults, merge in new params to override
|
232
231
|
datum = @data.merge(params)
|
232
|
+
|
233
|
+
# Set the deadline for the current request in order to determine when we have run out of time.
|
234
|
+
# Only set when a request timeout has been defined.
|
235
|
+
if datum[:timeout]
|
236
|
+
datum[:deadline] = Process.clock_gettime(Process::CLOCK_MONOTONIC) + datum[:timeout]
|
237
|
+
end
|
238
|
+
|
233
239
|
datum[:headers] = @data[:headers].merge(datum[:headers] || {})
|
234
240
|
|
235
241
|
validate_params(:request, params, datum[:middlewares])
|
@@ -244,16 +250,17 @@ module Excon
|
|
244
250
|
datum[:headers]['Authorization'] ||= 'Basic ' + ["#{user}:#{pass}"].pack('m').delete(Excon::CR_NL)
|
245
251
|
end
|
246
252
|
|
253
|
+
host_key = datum[:headers].keys.detect {|k| k.casecmp?('Host') } || 'Host'
|
247
254
|
if datum[:scheme] == UNIX
|
248
|
-
datum[:headers][
|
255
|
+
datum[:headers][host_key] ||= ''
|
249
256
|
else
|
250
|
-
datum[:headers][
|
257
|
+
datum[:headers][host_key] ||= datum[:host] + port_string(datum)
|
251
258
|
end
|
252
259
|
|
253
260
|
# RFC 7230, section 5.4, states that the Host header SHOULD be the first one # to be present.
|
254
261
|
# Some web servers will reject the request if it comes too late, so let's hoist it to the top.
|
255
|
-
if (host = datum[:headers].delete(
|
256
|
-
datum[:headers] = {
|
262
|
+
if (host = datum[:headers].delete(host_key))
|
263
|
+
datum[:headers] = { host_key => host }.merge(datum[:headers])
|
257
264
|
end
|
258
265
|
|
259
266
|
# default to GET if no method specified
|
@@ -291,8 +298,8 @@ module Excon
|
|
291
298
|
@persistent_socket_reusable = true
|
292
299
|
|
293
300
|
if datum[:persistent]
|
294
|
-
if (key = datum[:response][:headers].keys.detect {|k| k.casecmp('Connection')
|
295
|
-
if datum[:response][:headers][key].casecmp('close')
|
301
|
+
if (key = datum[:response][:headers].keys.detect {|k| k.casecmp?('Connection') })
|
302
|
+
if datum[:response][:headers][key].casecmp?('close')
|
296
303
|
reset
|
297
304
|
end
|
298
305
|
end
|
@@ -322,7 +329,7 @@ module Excon
|
|
322
329
|
# @param pipeline_params [Array<Hash>] An array of one or more optional params, override defaults set in Connection.new, see #request for details
|
323
330
|
def requests(pipeline_params)
|
324
331
|
pipeline_params.each {|params| params.merge!(:pipeline => true, :persistent => true) }
|
325
|
-
pipeline_params.last
|
332
|
+
pipeline_params.last[:persistent] = @data[:persistent]
|
326
333
|
|
327
334
|
responses = pipeline_params.map do |params|
|
328
335
|
request(params)
|
@@ -331,8 +338,8 @@ module Excon
|
|
331
338
|
end
|
332
339
|
|
333
340
|
if @data[:persistent]
|
334
|
-
if (key = responses.last[:headers].keys.detect {|k| k.casecmp('Connection')
|
335
|
-
if responses.last[:headers][key].casecmp('close')
|
341
|
+
if (key = responses.last[:headers].keys.detect {|k| k.casecmp?('Connection') })
|
342
|
+
if responses.last[:headers][key].casecmp?('close')
|
336
343
|
reset
|
337
344
|
end
|
338
345
|
end
|
@@ -439,6 +446,12 @@ module Excon
|
|
439
446
|
raise ArgumentError.new("Invalid validation type '#{validation}'")
|
440
447
|
end
|
441
448
|
|
449
|
+
if validation == :connection && params[:omit_default_port] != true
|
450
|
+
Excon.display_warning(
|
451
|
+
'The `omit_default_port` connection option is deprecated, please use `include_default_port` instead.'
|
452
|
+
)
|
453
|
+
end
|
454
|
+
|
442
455
|
invalid_keys = params.keys - valid_keys
|
443
456
|
unless invalid_keys.empty?
|
444
457
|
Excon.display_warning("Invalid Excon #{validation} keys: #{invalid_keys.map(&:inspect).join(', ')}")
|
@@ -498,7 +511,7 @@ module Excon
|
|
498
511
|
end
|
499
512
|
|
500
513
|
def raise_socket_error(error)
|
501
|
-
if error.message
|
514
|
+
if error.message.include?('certificate verify failed')
|
502
515
|
raise(Excon::Errors::CertificateError.new(error))
|
503
516
|
else
|
504
517
|
raise(Excon::Errors::SocketError.new(error))
|
data/lib/excon/constants.rb
CHANGED
@@ -1,32 +1,31 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
module Excon
|
3
|
-
|
4
3
|
CR_NL = "\r\n"
|
5
4
|
|
6
|
-
DEFAULT_CA_FILE = File.expand_path(File.join(File.dirname(__FILE__),
|
5
|
+
DEFAULT_CA_FILE = File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'data', 'cacert.pem'))
|
7
6
|
|
8
|
-
DEFAULT_CHUNK_SIZE =
|
7
|
+
DEFAULT_CHUNK_SIZE = 1_048_576 # 1 megabyte
|
9
8
|
|
10
9
|
# avoid overwrite if somebody has redefined
|
11
|
-
unless const_defined?(:CHUNK_SIZE)
|
12
|
-
CHUNK_SIZE = DEFAULT_CHUNK_SIZE
|
13
|
-
end
|
10
|
+
CHUNK_SIZE = DEFAULT_CHUNK_SIZE unless const_defined?(:CHUNK_SIZE)
|
14
11
|
|
15
12
|
DEFAULT_REDIRECT_LIMIT = 10
|
16
13
|
|
17
14
|
DEFAULT_RETRY_LIMIT = 4
|
18
15
|
|
19
16
|
DEFAULT_RETRY_ERRORS = [
|
20
|
-
Excon::Error::
|
17
|
+
Excon::Error::RequestTimeout,
|
18
|
+
Excon::Error::Server,
|
21
19
|
Excon::Error::Socket,
|
22
|
-
Excon::Error::
|
23
|
-
|
20
|
+
Excon::Error::Timeout,
|
21
|
+
Excon::Error::TooManyRequests
|
22
|
+
].freeze
|
24
23
|
|
25
24
|
FORCE_ENC = CR_NL.respond_to?(:force_encoding)
|
26
25
|
|
27
26
|
HTTP_1_1 = " HTTP/1.1\r\n"
|
28
27
|
|
29
|
-
HTTP_VERBS = %w
|
28
|
+
HTTP_VERBS = %w[connect delete get head options patch post put trace].freeze
|
30
29
|
|
31
30
|
HTTPS = 'https'
|
32
31
|
|
@@ -36,90 +35,97 @@ module Excon
|
|
36
35
|
|
37
36
|
UNIX = 'unix'
|
38
37
|
|
39
|
-
USER_AGENT = "excon/#{VERSION}"
|
40
|
-
|
41
|
-
VERSIONS = "#{USER_AGENT} (#{RUBY_PLATFORM}) ruby/#{RUBY_VERSION}"
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
38
|
+
USER_AGENT = "excon/#{VERSION}".freeze
|
39
|
+
|
40
|
+
VERSIONS = "#{USER_AGENT} (#{RUBY_PLATFORM}) ruby/#{RUBY_VERSION}".freeze
|
41
|
+
|
42
|
+
# FIXME: should be frozen, but with a way to change them, similar to defaults
|
43
|
+
VALID_REQUEST_KEYS = %i[
|
44
|
+
allow_unstubbed_requests
|
45
|
+
body
|
46
|
+
chunk_size
|
47
|
+
debug_request
|
48
|
+
debug_response
|
49
|
+
dns_timeouts
|
50
|
+
headers
|
51
|
+
instrumentor
|
52
|
+
logger
|
53
|
+
method
|
54
|
+
middlewares
|
55
|
+
password
|
56
|
+
path
|
57
|
+
persistent
|
58
|
+
pipeline
|
59
|
+
query
|
60
|
+
read_timeout
|
61
|
+
request_block
|
62
|
+
resolv_resolver
|
63
|
+
response_block
|
64
|
+
stubs
|
65
|
+
timeout
|
66
|
+
user
|
67
|
+
versions
|
68
|
+
write_timeout
|
66
69
|
]
|
67
70
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
71
|
+
# FIXME: should be frozen, but with a way to change them, similar to defaults
|
72
|
+
VALID_CONNECTION_KEYS = (VALID_REQUEST_KEYS + %i[
|
73
|
+
ciphers
|
74
|
+
client_key
|
75
|
+
client_key_data
|
76
|
+
client_key_pass
|
77
|
+
client_cert
|
78
|
+
client_cert_data
|
79
|
+
client_chain
|
80
|
+
client_chain_data
|
81
|
+
certificate
|
82
|
+
certificate_path
|
83
|
+
disable_proxy
|
84
|
+
private_key
|
85
|
+
private_key_path
|
86
|
+
connect_timeout
|
87
|
+
family
|
88
|
+
keepalive
|
89
|
+
host
|
90
|
+
hostname
|
91
|
+
ignore_unexpected_eof
|
92
|
+
include_default_port
|
93
|
+
omit_default_port
|
94
|
+
nonblock
|
95
|
+
reuseaddr
|
96
|
+
port
|
97
|
+
proxy
|
98
|
+
scheme
|
99
|
+
socket
|
100
|
+
ssl_ca_file
|
101
|
+
ssl_ca_path
|
102
|
+
ssl_cert_store
|
103
|
+
ssl_verify_callback
|
104
|
+
ssl_verify_peer
|
105
|
+
ssl_verify_peer_host
|
106
|
+
ssl_verify_hostname
|
107
|
+
ssl_version
|
108
|
+
ssl_min_version
|
109
|
+
ssl_max_version
|
110
|
+
ssl_security_level
|
111
|
+
ssl_proxy_headers
|
112
|
+
ssl_uri_schemes
|
113
|
+
tcp_nodelay
|
114
|
+
thread_safe_sockets
|
115
|
+
uri_parser
|
116
|
+
])
|
111
117
|
|
112
118
|
DEPRECATED_VALID_REQUEST_KEYS = {
|
113
|
-
:
|
114
|
-
:
|
115
|
-
:
|
116
|
-
:
|
117
|
-
:
|
118
|
-
:
|
119
|
-
:
|
120
|
-
:
|
121
|
-
:
|
122
|
-
}
|
119
|
+
captures: 'Mock',
|
120
|
+
expects: 'Expects',
|
121
|
+
idempotent: 'Idempotent',
|
122
|
+
instrumentor_name: 'Instrumentor',
|
123
|
+
mock: 'Mock',
|
124
|
+
retries_remaining: 'Idempotent', # referenced in Instrumentor, but only relevant with Idempotent
|
125
|
+
retry_errors: 'Idempotent',
|
126
|
+
retry_interval: 'Idempotent',
|
127
|
+
retry_limit: 'Idempotent' # referenced in Instrumentor, but only relevant with Idempotent
|
128
|
+
}.freeze
|
123
129
|
|
124
130
|
unless ::IO.const_defined?(:WaitReadable)
|
125
131
|
class ::IO
|
@@ -132,43 +138,54 @@ module Excon
|
|
132
138
|
module WaitWritable; end
|
133
139
|
end
|
134
140
|
end
|
141
|
+
|
135
142
|
# these come last as they rely on the above
|
136
|
-
|
137
|
-
:
|
143
|
+
base_defaults = {
|
144
|
+
chunk_size: CHUNK_SIZE || DEFAULT_CHUNK_SIZE,
|
138
145
|
# see https://wiki.mozilla.org/Security/Server_Side_TLS#Intermediate_compatibility_.28default.29
|
139
146
|
# list provided then had DES related things sorted to the end
|
140
|
-
:
|
141
|
-
:
|
142
|
-
:
|
143
|
-
:
|
144
|
-
:
|
147
|
+
ciphers: 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:DES-CBC3-SHA:!DSS',
|
148
|
+
connect_timeout: 60,
|
149
|
+
debug_request: false,
|
150
|
+
debug_response: false,
|
151
|
+
dns_timeouts: nil, # nil allows Resolv::DNS default timeout value (see https://ruby-doc.org/3.2.2/stdlibs/resolv/Resolv/DNS.html#method-i-timeouts-3D)
|
152
|
+
headers: {
|
145
153
|
'User-Agent' => USER_AGENT,
|
146
|
-
'Accept'
|
147
|
-
},
|
148
|
-
:
|
149
|
-
:
|
150
|
-
:
|
154
|
+
'Accept' => '*/*'
|
155
|
+
}.freeze,
|
156
|
+
idempotent: false,
|
157
|
+
instrumentor_name: 'excon',
|
158
|
+
middlewares: [
|
151
159
|
Excon::Middleware::ResponseParser,
|
160
|
+
Excon::Middleware::Decompress,
|
152
161
|
Excon::Middleware::Expects,
|
153
162
|
Excon::Middleware::Idempotent,
|
154
163
|
Excon::Middleware::Instrumentor,
|
155
164
|
Excon::Middleware::Mock
|
156
165
|
],
|
157
|
-
:
|
158
|
-
:
|
159
|
-
:
|
160
|
-
:
|
161
|
-
:
|
162
|
-
:
|
163
|
-
:
|
164
|
-
:
|
165
|
-
:
|
166
|
-
:
|
167
|
-
:
|
168
|
-
:
|
169
|
-
:
|
170
|
-
:
|
171
|
-
:
|
172
|
-
|
173
|
-
|
166
|
+
mock: false,
|
167
|
+
include_default_port: false,
|
168
|
+
nonblock: true,
|
169
|
+
omit_default_port: true,
|
170
|
+
persistent: false,
|
171
|
+
read_timeout: 60,
|
172
|
+
resolv_resolver: nil,
|
173
|
+
retry_errors: DEFAULT_RETRY_ERRORS,
|
174
|
+
retry_limit: DEFAULT_RETRY_LIMIT,
|
175
|
+
ssl_verify_peer: true,
|
176
|
+
ssl_uri_schemes: [HTTPS].freeze,
|
177
|
+
stubs: :global,
|
178
|
+
tcp_nodelay: false,
|
179
|
+
thread_safe_sockets: true,
|
180
|
+
timeout: nil,
|
181
|
+
uri_parser: URI,
|
182
|
+
versions: VERSIONS,
|
183
|
+
write_timeout: 60
|
184
|
+
}.freeze
|
185
|
+
|
186
|
+
DEFAULTS = if defined? TEST_SUITE_DEFAULTS
|
187
|
+
base_defaults.merge(TEST_SUITE_DEFAULTS).freeze
|
188
|
+
else
|
189
|
+
base_defaults.freeze
|
190
|
+
end
|
174
191
|
end
|
data/lib/excon/error.rb
CHANGED
@@ -216,8 +216,8 @@ or:
|
|
216
216
|
|
217
217
|
klasses.each do |klass|
|
218
218
|
class_name = klass.to_s
|
219
|
-
unless class_name
|
220
|
-
class_name = klass.to_s + 'Error' if class_name
|
219
|
+
unless class_name.match?(/Error\Z/)
|
220
|
+
class_name = klass.to_s + 'Error' if class_name.match?(legacy_re)
|
221
221
|
end
|
222
222
|
Excon::Errors.const_set(class_name, Excon::Error.const_get(klass))
|
223
223
|
end
|
@@ -1,41 +1,36 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module Excon
|
3
4
|
module Middleware
|
4
5
|
class Decompress < Excon::Middleware::Base
|
5
|
-
|
6
6
|
INFLATE_ZLIB_OR_GZIP = 47 # Zlib::MAX_WBITS + 32
|
7
7
|
INFLATE_RAW = -15 # Zlib::MAX_WBITS * -1
|
8
8
|
|
9
9
|
def request_call(datum)
|
10
|
-
unless datum.
|
11
|
-
key = datum[:headers].keys.detect {|k| k.to_s.casecmp('Accept-Encoding')
|
12
|
-
if datum[:headers][key].to_s.empty?
|
13
|
-
datum[:headers][key] = 'deflate, gzip'
|
14
|
-
end
|
10
|
+
unless datum.key?(:response_block)
|
11
|
+
key = datum[:headers].keys.detect { |k| k.to_s.casecmp?('Accept-Encoding') } || 'Accept-Encoding'
|
12
|
+
datum[:headers][key] = 'deflate, gzip' if datum[:headers][key].to_s.empty?
|
15
13
|
end
|
16
14
|
@stack.request_call(datum)
|
17
15
|
end
|
18
16
|
|
19
17
|
def response_call(datum)
|
20
18
|
body = datum[:response][:body]
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
Zlib::Inflate.new(INFLATE_RAW).inflate(body)
|
30
|
-
end
|
31
|
-
encodings.pop
|
32
|
-
elsif encoding.casecmp('gzip') == 0 || encoding.casecmp('x-gzip') == 0
|
33
|
-
datum[:response][:body] = Zlib::GzipReader.new(StringIO.new(body)).read
|
34
|
-
encodings.pop
|
35
|
-
end
|
36
|
-
datum[:response][:headers][key] = encodings.join(', ')
|
19
|
+
if !(datum.key?(:response_block) || body.nil? || body.empty?) &&
|
20
|
+
(key = datum[:response][:headers].keys.detect { |k| k.casecmp?('Content-Encoding') })
|
21
|
+
encodings = Utils.split_header_value(datum[:response][:headers][key])
|
22
|
+
if encodings&.last&.casecmp?('deflate')
|
23
|
+
datum[:response][:body] = begin
|
24
|
+
Zlib::Inflate.new(INFLATE_ZLIB_OR_GZIP).inflate(body)
|
25
|
+
rescue Zlib::DataError # fallback to raw on error
|
26
|
+
Zlib::Inflate.new(INFLATE_RAW).inflate(body)
|
37
27
|
end
|
28
|
+
encodings.pop
|
29
|
+
elsif encodings&.last&.casecmp?('gzip') || encodings&.last&.casecmp?('x-gzip')
|
30
|
+
datum[:response][:body] = Zlib::GzipReader.new(StringIO.new(body)).read
|
31
|
+
encodings.pop
|
38
32
|
end
|
33
|
+
datum[:response][:headers][key] = encodings.join(', ')
|
39
34
|
end
|
40
35
|
@stack.response_call(datum)
|
41
36
|
end
|
@@ -17,7 +17,7 @@ module Excon
|
|
17
17
|
|
18
18
|
def get_header(datum, header)
|
19
19
|
_, header_value = datum[:response][:headers].detect do |key, _|
|
20
|
-
key.casecmp(header)
|
20
|
+
key.casecmp?(header)
|
21
21
|
end
|
22
22
|
header_value
|
23
23
|
end
|
@@ -67,8 +67,8 @@ module Excon
|
|
67
67
|
:query => uri.query
|
68
68
|
)
|
69
69
|
|
70
|
-
params
|
71
|
-
params
|
70
|
+
params[:user] = Utils.unescape_uri(uri.user) if uri.user
|
71
|
+
params[:password] = Utils.unescape_uri(uri.password) if uri.password
|
72
72
|
|
73
73
|
response = Excon::Connection.new(params).request
|
74
74
|
datum.merge!({
|
data/lib/excon/response.rb
CHANGED
@@ -106,9 +106,9 @@ module Excon
|
|
106
106
|
|
107
107
|
unless (['HEAD', 'CONNECT'].include?(datum[:method].to_s.upcase)) || NO_ENTITY.include?(datum[:response][:status])
|
108
108
|
|
109
|
-
if (key = datum[:response][:headers].keys.detect {|k| k.casecmp('Transfer-Encoding')
|
109
|
+
if (key = datum[:response][:headers].keys.detect {|k| k.casecmp?('Transfer-Encoding') })
|
110
110
|
encodings = Utils.split_header_value(datum[:response][:headers][key])
|
111
|
-
if (encoding = encodings.last) && encoding.casecmp('chunked')
|
111
|
+
if (encoding = encodings.last) && encoding.casecmp?('chunked')
|
112
112
|
transfer_encoding_chunked = true
|
113
113
|
if encodings.length == 1
|
114
114
|
datum[:response][:headers].delete(key)
|
@@ -156,7 +156,7 @@ module Excon
|
|
156
156
|
end
|
157
157
|
parse_headers(socket, datum) # merge trailers into headers
|
158
158
|
else
|
159
|
-
if (key = datum[:response][:headers].keys.detect {|k| k.casecmp('Content-Length')
|
159
|
+
if (key = datum[:response][:headers].keys.detect {|k| k.casecmp?('Content-Length') })
|
160
160
|
content_length = datum[:response][:headers][key].to_i
|
161
161
|
end
|
162
162
|
|
@@ -202,7 +202,7 @@ module Excon
|
|
202
202
|
raise Excon::Error::ResponseParse, 'malformed header' unless value
|
203
203
|
# add key/value or append value to existing values
|
204
204
|
datum[:response][:headers][key] = ([datum[:response][:headers][key]] << value.strip).compact.join(', ')
|
205
|
-
if key.casecmp('Set-Cookie')
|
205
|
+
if key.casecmp?('Set-Cookie')
|
206
206
|
datum[:response][:cookies] << value.strip
|
207
207
|
end
|
208
208
|
last_key = key
|