excon 0.112.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
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