excon 0.109.0 → 1.5.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.
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 = "speed, persistence, http(s)"
8
- s.description = "EXtended http(s) CONnections"
9
- s.authors = ["dpiddy (Dan Peterson)", "geemus (Wesley Beary)", "nextmat (Matt Sanders)"]
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 = ["--charset=UTF-8"]
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
- "CONTRIBUTING.md",
17
- "CONTRIBUTORS.md",
18
- "LICENSE.md",
19
- "README.md",
20
- "excon.gemspec"
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
- 'homepage_uri' => 'https://github.com/excon/excon',
38
- 'bug_tracker_uri' => 'https://github.com/excon/excon/issues',
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
- 'source_code_uri' => 'https://github.com/excon/excon',
42
- 'wiki_uri' => 'https://github.com/excon/excon/wiki'
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
@@ -1,5 +1,4 @@
1
1
  # frozen_string_literal: true
2
- require 'ipaddr'
3
2
 
4
3
  module Excon
5
4
  class Connection
@@ -61,6 +60,8 @@ module Excon
61
60
  # @option params [Fixnum] :retry_interval Set how long to wait between retries. (Default 0)
62
61
  # @option params [Class] :instrumentor Responds to #instrument as in ActiveSupport::Notifications
63
62
  # @option params [String] :instrumentor_name Name prefix for #instrument events. Defaults to 'excon'
63
+ # @option params [Resolv, nil] :resolv_resolver A ready to use +Resolv+ resolver instance.
64
+ # @option params [Class] :resolver_factory Const of the resolver factory. Defaults to 'Excon::ResolverFactory'
64
65
  def initialize(params = {})
65
66
  @pid = Process.pid
66
67
  @data = Excon.defaults.dup
@@ -101,6 +102,10 @@ module Excon
101
102
  @socket_key = "#{@data[:scheme]}://#{@data[:socket]}"
102
103
  end
103
104
  else
105
+ if @data.key?(:host) && @data[:host].nil?
106
+ raise Excon::Error::InvalidParameter, "host is required for non-Unix connections; " \
107
+ "the URL may be malformed (e.g. missing \"//\" after the scheme)"
108
+ end
104
109
  @socket_key = "#{@data[:scheme]}://#{@data[:host]}#{port_string(@data)}"
105
110
  end
106
111
  reset
@@ -139,7 +144,7 @@ module Excon
139
144
 
140
145
  # The HTTP spec isn't clear on it, but specifically, GET requests don't usually send bodies;
141
146
  # if they don't, sending Content-Length:0 can cause issues.
142
- unless datum[:method].to_s.casecmp('GET') == 0 && body.nil?
147
+ unless datum[:method].to_s.casecmp?('GET') && body.nil?
143
148
  unless datum[:headers].has_key?('Content-Length')
144
149
  datum[:headers]['Content-Length'] = detect_content_length(body)
145
150
  end
@@ -160,7 +165,7 @@ module Excon
160
165
  if chunk.length > 0
161
166
  socket(datum).write(chunk.length.to_s(16) << CR_NL << chunk << CR_NL)
162
167
  else
163
- socket(datum).write(String.new("0#{CR_NL}#{CR_NL}"))
168
+ socket(datum).write("0#{CR_NL}#{CR_NL}")
164
169
  break
165
170
  end
166
171
  end
@@ -195,7 +200,7 @@ module Excon
195
200
  raise(error)
196
201
  when Errno::EPIPE
197
202
  # Read whatever remains in the pipe to aid in debugging
198
- response = socket.read
203
+ response = socket.read rescue ""
199
204
  error = Excon::Error.new(response + error.message)
200
205
  raise_socket_error(error)
201
206
  else
@@ -251,10 +256,14 @@ module Excon
251
256
  datum[:headers]['Authorization'] ||= 'Basic ' + ["#{user}:#{pass}"].pack('m').delete(Excon::CR_NL)
252
257
  end
253
258
 
254
- host_key = datum[:headers].keys.detect {|k| k.casecmp('Host') == 0 } || 'Host'
259
+ host_key = datum[:headers].keys.detect {|k| k.casecmp?('Host') } || 'Host'
255
260
  if datum[:scheme] == UNIX
256
261
  datum[:headers][host_key] ||= ''
257
262
  else
263
+ if datum[:host].nil?
264
+ raise Excon::Error::InvalidParameter, "host is required for non-Unix connections; " \
265
+ "the URL may be malformed (e.g. missing \"//\" after the scheme)"
266
+ end
258
267
  datum[:headers][host_key] ||= datum[:host] + port_string(datum)
259
268
  end
260
269
 
@@ -299,8 +308,8 @@ module Excon
299
308
  @persistent_socket_reusable = true
300
309
 
301
310
  if datum[:persistent]
302
- if (key = datum[:response][:headers].keys.detect {|k| k.casecmp('Connection') == 0 })
303
- if datum[:response][:headers][key].casecmp('close') == 0
311
+ if (key = datum[:response][:headers].keys.detect {|k| k.casecmp?('Connection') })
312
+ if datum[:response][:headers][key].casecmp?('close')
304
313
  reset
305
314
  end
306
315
  end
@@ -330,7 +339,7 @@ module Excon
330
339
  # @param pipeline_params [Array<Hash>] An array of one or more optional params, override defaults set in Connection.new, see #request for details
331
340
  def requests(pipeline_params)
332
341
  pipeline_params.each {|params| params.merge!(:pipeline => true, :persistent => true) }
333
- pipeline_params.last.merge!(:persistent => @data[:persistent])
342
+ pipeline_params.last[:persistent] = @data[:persistent]
334
343
 
335
344
  responses = pipeline_params.map do |params|
336
345
  request(params)
@@ -339,8 +348,8 @@ module Excon
339
348
  end
340
349
 
341
350
  if @data[:persistent]
342
- if (key = responses.last[:headers].keys.detect {|k| k.casecmp('Connection') == 0 })
343
- if responses.last[:headers][key].casecmp('close') == 0
351
+ if (key = responses.last[:headers].keys.detect {|k| k.casecmp?('Connection') })
352
+ if responses.last[:headers][key].casecmp?('close')
344
353
  reset
345
354
  end
346
355
  end
@@ -447,6 +456,12 @@ module Excon
447
456
  raise ArgumentError.new("Invalid validation type '#{validation}'")
448
457
  end
449
458
 
459
+ if validation == :connection && params[:omit_default_port] != true
460
+ Excon.display_warning(
461
+ 'The `omit_default_port` connection option is deprecated, please use `include_default_port` instead.'
462
+ )
463
+ end
464
+
450
465
  invalid_keys = params.keys - valid_keys
451
466
  unless invalid_keys.empty?
452
467
  Excon.display_warning("Invalid Excon #{validation} keys: #{invalid_keys.map(&:inspect).join(', ')}")
@@ -478,6 +493,13 @@ module Excon
478
493
  unix_proxy = datum[:proxy] ? datum[:proxy][:scheme] == UNIX : false
479
494
  sockets[@socket_key] ||= if datum[:scheme] == UNIX || unix_proxy
480
495
  Excon::UnixSocket.new(datum)
496
+ elsif datum[:socks5_proxy]
497
+ # SOCKS5 proxy - use appropriate socket based on target scheme
498
+ if datum[:ssl_uri_schemes].include?(datum[:scheme])
499
+ Excon::SOCKS5SSLSocket.new(datum)
500
+ else
501
+ Excon::SOCKS5Socket.new(datum)
502
+ end
481
503
  elsif datum[:ssl_uri_schemes].include?(datum[:scheme])
482
504
  Excon::SSLSocket.new(datum)
483
505
  else
@@ -506,7 +528,7 @@ module Excon
506
528
  end
507
529
 
508
530
  def raise_socket_error(error)
509
- if error.message =~ /certificate verify failed/
531
+ if error.message.include?('certificate verify failed')
510
532
  raise(Excon::Errors::CertificateError.new(error))
511
533
  else
512
534
  raise(Excon::Errors::SocketError.new(error))
@@ -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__), "..", "..", "data", "cacert.pem"))
5
+ DEFAULT_CA_FILE = File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'data', 'cacert.pem'))
7
6
 
8
- DEFAULT_CHUNK_SIZE = 1048576 # 1 megabyte
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::Timeout,
17
+ Excon::Error::RequestTimeout,
18
+ Excon::Error::Server,
21
19
  Excon::Error::Socket,
22
- Excon::Error::HTTPStatus
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{connect delete get head options patch post put trace}
28
+ HTTP_VERBS = %w[connect delete get head options patch post put trace].freeze
30
29
 
31
30
  HTTPS = 'https'
32
31
 
@@ -36,93 +35,99 @@ 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
- VALID_REQUEST_KEYS = [
44
- :allow_unstubbed_requests,
45
- :body,
46
- :chunk_size,
47
- :debug_request,
48
- :debug_response,
49
- :dns_timeouts,
50
- :headers,
51
- :instrumentor, # Used for setting logging within Connection
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
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
+ resolver_factory
64
+ response_block
65
+ stubs
66
+ timeout
67
+ user
68
+ versions
69
+ write_timeout
69
70
  ]
70
71
 
71
- VALID_CONNECTION_KEYS = VALID_REQUEST_KEYS + [
72
- :ciphers,
73
- :client_key,
74
- :client_key_data,
75
- :client_key_pass,
76
- :client_cert,
77
- :client_cert_data,
78
- :client_chain,
79
- :client_chain_data,
80
- :certificate,
81
- :certificate_path,
82
- :disable_proxy,
83
- :private_key,
84
- :private_key_path,
85
- :connect_timeout,
86
- :family,
87
- :keepalive,
88
- :host,
89
- :hostname,
90
- :omit_default_port,
91
- :nonblock,
92
- :reuseaddr,
93
- :port,
94
- :proxy,
95
- :scheme,
96
- :socket,
97
- :ssl_ca_file,
98
- :ssl_ca_path,
99
- :ssl_cert_store,
100
- :ssl_verify_callback,
101
- :ssl_verify_peer,
102
- :ssl_verify_peer_host,
103
- :ssl_verify_hostname,
104
- :ssl_version,
105
- :ssl_min_version,
106
- :ssl_max_version,
107
- :ssl_security_level,
108
- :ssl_proxy_headers,
109
- :ssl_uri_schemes,
110
- :tcp_nodelay,
111
- :thread_safe_sockets,
112
- :uri_parser,
113
- ]
72
+ # FIXME: should be frozen, but with a way to change them, similar to defaults
73
+ VALID_CONNECTION_KEYS = (VALID_REQUEST_KEYS + %i[
74
+ ciphers
75
+ client_key
76
+ client_key_data
77
+ client_key_pass
78
+ client_cert
79
+ client_cert_data
80
+ client_chain
81
+ client_chain_data
82
+ certificate
83
+ certificate_path
84
+ disable_proxy
85
+ private_key
86
+ private_key_path
87
+ connect_timeout
88
+ family
89
+ keepalive
90
+ host
91
+ hostname
92
+ ignore_unexpected_eof
93
+ include_default_port
94
+ omit_default_port
95
+ nonblock
96
+ reuseaddr
97
+ port
98
+ proxy
99
+ scheme
100
+ socket
101
+ socks5_proxy
102
+ ssl_ca_file
103
+ ssl_ca_path
104
+ ssl_cert_store
105
+ ssl_verify_callback
106
+ ssl_verify_peer
107
+ ssl_verify_peer_host
108
+ ssl_verify_hostname
109
+ ssl_version
110
+ ssl_min_version
111
+ ssl_max_version
112
+ ssl_security_level
113
+ ssl_proxy_headers
114
+ ssl_uri_schemes
115
+ tcp_nodelay
116
+ thread_safe_sockets
117
+ uri_parser
118
+ ])
114
119
 
115
120
  DEPRECATED_VALID_REQUEST_KEYS = {
116
- :captures => 'Mock',
117
- :expects => 'Expects',
118
- :idempotent => 'Idempotent',
119
- :instrumentor_name => 'Instrumentor',
120
- :mock => 'Mock',
121
- :retries_remaining => 'Idempotent', # referenced in Instrumentor, but only relevant with Idempotent
122
- :retry_errors => 'Idempotent',
123
- :retry_interval => 'Idempotent',
124
- :retry_limit => 'Idempotent' # referenced in Instrumentor, but only relevant with Idempotent
125
- }
121
+ captures: 'Mock',
122
+ expects: 'Expects',
123
+ idempotent: 'Idempotent',
124
+ instrumentor_name: 'Instrumentor',
125
+ mock: 'Mock',
126
+ retries_remaining: 'Idempotent', # referenced in Instrumentor, but only relevant with Idempotent
127
+ retry_errors: 'Idempotent',
128
+ retry_interval: 'Idempotent',
129
+ retry_limit: 'Idempotent' # referenced in Instrumentor, but only relevant with Idempotent
130
+ }.freeze
126
131
 
127
132
  unless ::IO.const_defined?(:WaitReadable)
128
133
  class ::IO
@@ -135,47 +140,55 @@ module Excon
135
140
  module WaitWritable; end
136
141
  end
137
142
  end
143
+
138
144
  # these come last as they rely on the above
139
- DEFAULTS = {
140
- :chunk_size => CHUNK_SIZE || DEFAULT_CHUNK_SIZE,
145
+ base_defaults = {
146
+ chunk_size: CHUNK_SIZE || DEFAULT_CHUNK_SIZE,
141
147
  # see https://wiki.mozilla.org/Security/Server_Side_TLS#Intermediate_compatibility_.28default.29
142
148
  # list provided then had DES related things sorted to the end
143
- :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',
144
- :connect_timeout => 60,
145
- :debug_request => false,
146
- :debug_response => false,
147
- :dns_timeouts => nil,
148
- # nil allows Resolv::DNS to set its default timeouts value (see https://ruby-doc.org/3.2.2/stdlibs/resolv/Resolv/DNS.html#method-i-timeouts-3D)
149
- :headers => {
149
+ 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',
150
+ connect_timeout: 60,
151
+ debug_request: false,
152
+ debug_response: false,
153
+ 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)
154
+ headers: {
150
155
  'User-Agent' => USER_AGENT,
151
- 'Accept' => '*/*'
152
- },
153
- :idempotent => false,
154
- :instrumentor_name => 'excon',
155
- :middlewares => [
156
+ 'Accept' => '*/*'
157
+ }.freeze,
158
+ idempotent: false,
159
+ instrumentor_name: 'excon',
160
+ middlewares: [
156
161
  Excon::Middleware::ResponseParser,
162
+ Excon::Middleware::Decompress,
157
163
  Excon::Middleware::Expects,
158
164
  Excon::Middleware::Idempotent,
159
165
  Excon::Middleware::Instrumentor,
160
166
  Excon::Middleware::Mock
161
167
  ],
162
- :mock => false,
163
- :nonblock => true,
164
- :omit_default_port => false,
165
- :persistent => false,
166
- :read_timeout => 60,
167
- :resolv_resolver => nil,
168
- :retry_errors => DEFAULT_RETRY_ERRORS,
169
- :retry_limit => DEFAULT_RETRY_LIMIT,
170
- :ssl_verify_peer => true,
171
- :ssl_uri_schemes => [HTTPS],
172
- :stubs => :global,
173
- :tcp_nodelay => false,
174
- :thread_safe_sockets => true,
175
- :timeout => nil,
176
- :uri_parser => URI,
177
- :versions => VERSIONS,
178
- :write_timeout => 60
179
- }
180
-
168
+ mock: false,
169
+ include_default_port: false,
170
+ nonblock: true,
171
+ omit_default_port: true,
172
+ persistent: false,
173
+ read_timeout: 60,
174
+ resolv_resolver: nil,
175
+ resolver_factory: Excon::ResolverFactory,
176
+ retry_errors: DEFAULT_RETRY_ERRORS,
177
+ retry_limit: DEFAULT_RETRY_LIMIT,
178
+ ssl_verify_peer: true,
179
+ ssl_uri_schemes: [HTTPS].freeze,
180
+ stubs: :global,
181
+ tcp_nodelay: false,
182
+ thread_safe_sockets: true,
183
+ timeout: nil,
184
+ uri_parser: URI,
185
+ versions: VERSIONS,
186
+ write_timeout: 60
187
+ }.freeze
188
+
189
+ DEFAULTS = if defined? TEST_SUITE_DEFAULTS
190
+ base_defaults.merge(TEST_SUITE_DEFAULTS).freeze
191
+ else
192
+ base_defaults.freeze
193
+ end
181
194
  end
data/lib/excon/error.rb CHANGED
@@ -48,6 +48,7 @@ or:
48
48
 
49
49
  class InvalidHeaderKey < Error; end
50
50
  class InvalidHeaderValue < Error; end
51
+ class InvalidParameter < Error; end
51
52
  class Timeout < Error; end
52
53
  class ResponseParse < Error; end
53
54
 
@@ -216,8 +217,8 @@ or:
216
217
 
217
218
  klasses.each do |klass|
218
219
  class_name = klass.to_s
219
- unless class_name =~ /Error\Z/
220
- class_name = klass.to_s + 'Error' if class_name =~ legacy_re
220
+ unless class_name.match?(/Error\Z/)
221
+ class_name = klass.to_s + 'Error' if class_name.match?(legacy_re)
221
222
  end
222
223
  Excon::Errors.const_set(class_name, Excon::Error.const_get(klass))
223
224
  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.has_key?(:response_block)
11
- key = datum[:headers].keys.detect {|k| k.to_s.casecmp('Accept-Encoding') == 0 } || '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
- unless datum.has_key?(:response_block) || body.nil? || body.empty?
22
- if (key = datum[:response][:headers].keys.detect {|k| k.casecmp('Content-Encoding') == 0 })
23
- encodings = Utils.split_header_value(datum[:response][:headers][key])
24
- if (encoding = encodings.last)
25
- if encoding.casecmp('deflate') == 0
26
- datum[:response][:body] = begin
27
- Zlib::Inflate.new(INFLATE_ZLIB_OR_GZIP).inflate(body)
28
- rescue Zlib::DataError # fallback to raw on error
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
@@ -2,6 +2,19 @@
2
2
  module Excon
3
3
  module Middleware
4
4
  class RedirectFollower < Excon::Middleware::Base
5
+ REDACTION_HEADERS = %w[
6
+ Authorization
7
+ Cookie
8
+ Cookie2
9
+ Host
10
+ Proxy-Connection
11
+ Proxy-Authorization
12
+ X-API-Key
13
+ X-Auth-Token
14
+ X-CSRF-Token
15
+ X-Session-ID
16
+ ]
17
+
5
18
  def self.valid_parameter_keys
6
19
  [
7
20
  :redirects_remaining,
@@ -17,7 +30,7 @@ module Excon
17
30
 
18
31
  def get_header(datum, header)
19
32
  _, header_value = datum[:response][:headers].detect do |key, _|
20
- key.casecmp(header) == 0
33
+ key.casecmp?(header)
21
34
  end
22
35
  header_value
23
36
  end
@@ -54,10 +67,9 @@ module Excon
54
67
  params[:headers].delete('Content-Length')
55
68
  end
56
69
  params[:headers] = datum[:headers].dup
57
- params[:headers].delete('Authorization')
58
- params[:headers].delete('Proxy-Connection')
59
- params[:headers].delete('Proxy-Authorization')
60
- params[:headers].delete('Host')
70
+ REDACTION_HEADERS.each do |header|
71
+ params[:headers].delete(header)
72
+ end
61
73
  params.merge!(
62
74
  :scheme => uri.scheme || datum[:scheme],
63
75
  :host => uri.host || datum[:host],
@@ -67,8 +79,8 @@ module Excon
67
79
  :query => uri.query
68
80
  )
69
81
 
70
- params.merge!(:user => Utils.unescape_uri(uri.user)) if uri.user
71
- params.merge!(:password => Utils.unescape_uri(uri.password)) if uri.password
82
+ params[:user] = Utils.unescape_uri(uri.user) if uri.user
83
+ params[:password] = Utils.unescape_uri(uri.password) if uri.password
72
84
 
73
85
  response = Excon::Connection.new(params).request
74
86
  datum.merge!({