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 +4 -4
- data/README.md +3 -0
- data/lib/excon/connection.rb +12 -6
- data/lib/excon/constants.rb +8 -3
- data/lib/excon/middlewares/capture_cookies.rb +1 -1
- data/lib/excon/middlewares/decompress.rb +17 -22
- data/lib/excon/middlewares/redirect_follower.rb +1 -1
- data/lib/excon/response.rb +4 -4
- data/lib/excon/ssl_socket.rb +1 -1
- data/lib/excon/utils.rb +26 -3
- data/lib/excon/version.rb +1 -1
- data/lib/excon.rb +8 -2
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8a1cbd57e645d8d22b4180e346b0fcf365165ffb9e95a69a83fe805f56d50e16
|
4
|
+
data.tar.gz: e073309fd959dded213203af46f07bccded5fc8c1fe4b879902a9b04e5d82046
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
|
data/lib/excon/connection.rb
CHANGED
@@ -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')
|
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')
|
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')
|
302
|
-
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')
|
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')
|
342
|
-
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')
|
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(', ')}")
|
data/lib/excon/constants.rb
CHANGED
@@ -14,9 +14,11 @@ module Excon
|
|
14
14
|
DEFAULT_RETRY_LIMIT = 4
|
15
15
|
|
16
16
|
DEFAULT_RETRY_ERRORS = [
|
17
|
-
Excon::Error::
|
17
|
+
Excon::Error::RequestTimeout,
|
18
|
+
Excon::Error::Server,
|
18
19
|
Excon::Error::Socket,
|
19
|
-
Excon::Error::
|
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:
|
166
|
+
omit_default_port: true,
|
162
167
|
persistent: false,
|
163
168
|
read_timeout: 60,
|
164
169
|
resolv_resolver: nil,
|
@@ -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
|
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
|
data/lib/excon/ssl_socket.rb
CHANGED
@@ -108,7 +108,7 @@ module Excon
|
|
108
108
|
end
|
109
109
|
|
110
110
|
if @data[:proxy]
|
111
|
-
request = "CONNECT #{@data[:host]}#{
|
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
|
66
|
-
|
65
|
+
if !default_port?(datum) || datum[:include_default_port] || !datum[:omit_default_port]
|
66
|
+
":#{datum[:port]}"
|
67
67
|
else
|
68
|
-
'
|
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
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:
|
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-
|
13
|
+
date: 2024-10-29 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: rspec
|