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 +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
|