excon 0.112.0 → 1.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a595b595665aaaf9b650c664bca78bdc66ad05b80d7641333ce3acbeb6a80c46
4
- data.tar.gz: 7200b150fded0987a7c57fb1155580efd0825472f375523ce12e2b0dce2ebf25
3
+ metadata.gz: 8a1cbd57e645d8d22b4180e346b0fcf365165ffb9e95a69a83fe805f56d50e16
4
+ data.tar.gz: e073309fd959dded213203af46f07bccded5fc8c1fe4b879902a9b04e5d82046
5
5
  SHA512:
6
- metadata.gz: 27c315cb6b11bf5f09bdae2802d033cdee746d8d907c77324535452f03b42fe2f77375530761153062f5fc70f39972858e0dfe82d64edecc7e9ab83135ce93ba
7
- data.tar.gz: 9c37add84efa4abb2766310f27a4aa7a151e31cc9e53cfdbd321c3aade0909b88d6d9ee0c9dea3accba8761dbb356d6570eb099bf24a52a96ea0576c1217edd9
6
+ metadata.gz: 109ed16c1c5f49cc84261a0c271c0f8769aabc9258a1482842d1c2f48b96ff48ce558b7018b01a1054d57b0f15f4321430e6cfb5f2f8af354150b41dec976d24
7
+ data.tar.gz: cb8c80990fea59470508ada7fcff74f217229c2f8f973d5369ef7f7beb32e105fb10013164427f787bcc38ff77f601fe617f0644a9a70407eaf405409444edfc
data/README.md CHANGED
@@ -190,6 +190,9 @@ connection.request(:timeout => 0.1) # timeout if the entire request takes longer
190
190
  #
191
191
  connection = Excon.new('http://geemus.com/', :tcp_nodelay => true)
192
192
 
193
+ # opt-in to having Excon add a default port (http:80 and https:443)
194
+ connection = Excon.new('http://geemus.com/', :include_default_port => true)
195
+
193
196
  # set longer connect_timeout (default is 60 seconds)
194
197
  connection = Excon.new('http://geemus.com/', :connect_timeout => 360)
195
198
 
@@ -138,7 +138,7 @@ module Excon
138
138
 
139
139
  # The HTTP spec isn't clear on it, but specifically, GET requests don't usually send bodies;
140
140
  # if they don't, sending Content-Length:0 can cause issues.
141
- unless datum[:method].to_s.casecmp('GET') == 0 && body.nil?
141
+ unless datum[:method].to_s.casecmp?('GET') && body.nil?
142
142
  unless datum[:headers].has_key?('Content-Length')
143
143
  datum[:headers]['Content-Length'] = detect_content_length(body)
144
144
  end
@@ -250,7 +250,7 @@ module Excon
250
250
  datum[:headers]['Authorization'] ||= 'Basic ' + ["#{user}:#{pass}"].pack('m').delete(Excon::CR_NL)
251
251
  end
252
252
 
253
- host_key = datum[:headers].keys.detect {|k| k.casecmp('Host') == 0 } || 'Host'
253
+ host_key = datum[:headers].keys.detect {|k| k.casecmp?('Host') } || 'Host'
254
254
  if datum[:scheme] == UNIX
255
255
  datum[:headers][host_key] ||= ''
256
256
  else
@@ -298,8 +298,8 @@ module Excon
298
298
  @persistent_socket_reusable = true
299
299
 
300
300
  if datum[:persistent]
301
- if (key = datum[:response][:headers].keys.detect {|k| k.casecmp('Connection') == 0 })
302
- if datum[:response][:headers][key].casecmp('close') == 0
301
+ if (key = datum[:response][:headers].keys.detect {|k| k.casecmp?('Connection') })
302
+ if datum[:response][:headers][key].casecmp?('close')
303
303
  reset
304
304
  end
305
305
  end
@@ -338,8 +338,8 @@ module Excon
338
338
  end
339
339
 
340
340
  if @data[:persistent]
341
- if (key = responses.last[:headers].keys.detect {|k| k.casecmp('Connection') == 0 })
342
- if responses.last[:headers][key].casecmp('close') == 0
341
+ if (key = responses.last[:headers].keys.detect {|k| k.casecmp?('Connection') })
342
+ if responses.last[:headers][key].casecmp?('close')
343
343
  reset
344
344
  end
345
345
  end
@@ -446,6 +446,12 @@ module Excon
446
446
  raise ArgumentError.new("Invalid validation type '#{validation}'")
447
447
  end
448
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
+
449
455
  invalid_keys = params.keys - valid_keys
450
456
  unless invalid_keys.empty?
451
457
  Excon.display_warning("Invalid Excon #{validation} keys: #{invalid_keys.map(&:inspect).join(', ')}")
@@ -14,9 +14,11 @@ module Excon
14
14
  DEFAULT_RETRY_LIMIT = 4
15
15
 
16
16
  DEFAULT_RETRY_ERRORS = [
17
- Excon::Error::Timeout,
17
+ Excon::Error::RequestTimeout,
18
+ Excon::Error::Server,
18
19
  Excon::Error::Socket,
19
- Excon::Error::HTTPStatus
20
+ Excon::Error::Timeout,
21
+ Excon::Error::TooManyRequests
20
22
  ].freeze
21
23
 
22
24
  FORCE_ENC = CR_NL.respond_to?(:force_encoding)
@@ -84,6 +86,7 @@ module Excon
84
86
  keepalive
85
87
  host
86
88
  hostname
89
+ include_default_port
87
90
  omit_default_port
88
91
  nonblock
89
92
  reuseaddr
@@ -151,14 +154,16 @@ module Excon
151
154
  instrumentor_name: 'excon',
152
155
  middlewares: [
153
156
  Excon::Middleware::ResponseParser,
157
+ Excon::Middleware::Decompress,
154
158
  Excon::Middleware::Expects,
155
159
  Excon::Middleware::Idempotent,
156
160
  Excon::Middleware::Instrumentor,
157
161
  Excon::Middleware::Mock
158
162
  ],
159
163
  mock: false,
164
+ include_default_port: false,
160
165
  nonblock: true,
161
- omit_default_port: false,
166
+ omit_default_port: true,
162
167
  persistent: false,
163
168
  read_timeout: 60,
164
169
  resolv_resolver: nil,
@@ -9,7 +9,7 @@ module Excon
9
9
 
10
10
  def get_header(datum, header)
11
11
  _, header_value = datum[:response][:headers].detect do |key, _|
12
- key.casecmp(header) == 0
12
+ key.casecmp?(header)
13
13
  end
14
14
  header_value
15
15
  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
@@ -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) == 0
20
+ key.casecmp?(header)
21
21
  end
22
22
  header_value
23
23
  end
@@ -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') == 0 })
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') == 0
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') == 0 })
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') == 0
205
+ if key.casecmp?('Set-Cookie')
206
206
  datum[:response][:cookies] << value.strip
207
207
  end
208
208
  last_key = key
@@ -108,7 +108,7 @@ module Excon
108
108
  end
109
109
 
110
110
  if @data[:proxy]
111
- request = "CONNECT #{@data[:host]}#{port_string(@data.merge(:omit_default_port => false))}#{Excon::HTTP_1_1}" +
111
+ request = "CONNECT #{@data[:host]}#{Excon::HTTP_1_1}" \
112
112
  "Host: #{@data[:host]}#{port_string(@data)}#{Excon::CR_NL}"
113
113
 
114
114
  if @data[:proxy].has_key?(:user) || @data[:proxy].has_key?(:password)
data/lib/excon/utils.rb CHANGED
@@ -62,11 +62,34 @@ module Excon
62
62
  end
63
63
 
64
64
  def port_string(datum)
65
- if datum[:port].nil? || (datum[:omit_default_port] && ((datum[:scheme].casecmp('http') == 0 && datum[:port] == 80) || (datum[:scheme].casecmp('https') == 0 && datum[:port] == 443)))
66
- ''
65
+ if !default_port?(datum) || datum[:include_default_port] || !datum[:omit_default_port]
66
+ ":#{datum[:port]}"
67
67
  else
68
- ':' + datum[:port].to_s
68
+ ''
69
+ end
70
+ end
71
+
72
+ # Used to normalize queries for stubs, based on Rack::Utils.parse_query
73
+ def parse_query_string(string)
74
+ params = {}
75
+
76
+ string.split(/[&;] */n).each do |pair|
77
+ key, value = pair.split('=', 2).map { |x| CGI.unescape(x) }
78
+
79
+ params[key] = if params[key]
80
+ [params[key], value].flatten
81
+ else
82
+ value
83
+ end
69
84
  end
85
+
86
+ params
87
+ end
88
+
89
+ def default_port?(datum)
90
+ (!datum[:scheme]&.casecmp?('unix') && datum[:port].nil?) ||
91
+ (datum[:scheme]&.casecmp?('http') && datum[:port] == 80) ||
92
+ (datum[:scheme]&.casecmp?('https') && datum[:port] == 443)
70
93
  end
71
94
 
72
95
  def query_string(datum)
data/lib/excon/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Excon
4
- VERSION = '0.112.0'
4
+ VERSION = '1.1.0'
5
5
  end
data/lib/excon.rb CHANGED
@@ -19,6 +19,7 @@ require 'excon/version'
19
19
  require 'excon/extensions/uri'
20
20
 
21
21
  require 'excon/middlewares/base'
22
+ require 'excon/middlewares/decompress'
22
23
  require 'excon/middlewares/expects'
23
24
  require 'excon/middlewares/idempotent'
24
25
  require 'excon/middlewares/instrumentor'
@@ -32,7 +33,6 @@ require 'excon/utils'
32
33
  require 'excon/connection'
33
34
  require 'excon/headers'
34
35
  require 'excon/response'
35
- require 'excon/middlewares/decompress'
36
36
  require 'excon/middlewares/escape_path'
37
37
  require 'excon/middlewares/redirect_follower'
38
38
  require 'excon/middlewares/capture_cookies'
@@ -143,7 +143,7 @@ module Excon
143
143
  uri = URI.parse(url)
144
144
  request_params = {
145
145
  host: uri.host,
146
- path: uri.path,
146
+ path: uri.path.empty? ? '/' : uri.path,
147
147
  port: uri.port,
148
148
  query: uri.query,
149
149
  scheme: uri.scheme
@@ -162,6 +162,9 @@ module Excon
162
162
  end
163
163
  request_params[:headers] = headers
164
164
  end
165
+ if request_params.key?(:query) && request_params[:query].instance_of?(String)
166
+ request_params[:query] = Utils.parse_query_string(request_params[:query])
167
+ end
165
168
  if block_given?
166
169
  raise(ArgumentError, 'stub requires either response_params OR a block') if response_params
167
170
 
@@ -182,6 +185,9 @@ module Excon
182
185
  if (method = request_params.delete(:method))
183
186
  request_params[:method] = method.to_s.downcase.to_sym
184
187
  end
188
+ if request_params.key?(:query) && request_params[:query].instance_of?(String)
189
+ request_params[:query] = Utils.parse_query_string(request_params[:query])
190
+ end
185
191
  Excon.stubs.each do |stub, response_params|
186
192
  captures = { headers: {} }
187
193
  headers_match = !stub.key?(:headers) || stub[:headers].keys.all? do |key|
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.112.0
4
+ version: 1.1.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: 2024-10-01 00:00:00.000000000 Z
13
+ date: 2024-10-29 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rspec