excon 0.62.0 → 0.85.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 +5 -5
- data/LICENSE.md +1 -1
- data/README.md +6 -5
- data/data/cacert.pem +939 -1720
- data/excon.gemspec +17 -2
- data/lib/excon.rb +25 -17
- data/lib/excon/connection.rb +206 -139
- data/lib/excon/constants.rb +38 -13
- data/lib/excon/error.rb +15 -0
- data/lib/excon/headers.rb +4 -3
- data/lib/excon/instrumentors/logging_instrumentor.rb +4 -15
- data/lib/excon/instrumentors/standard_instrumentor.rb +2 -9
- data/lib/excon/middlewares/base.rb +6 -0
- data/lib/excon/middlewares/capture_cookies.rb +1 -1
- data/lib/excon/middlewares/decompress.rb +2 -2
- data/lib/excon/middlewares/expects.rb +7 -1
- data/lib/excon/middlewares/idempotent.rb +20 -3
- data/lib/excon/middlewares/instrumentor.rb +8 -0
- data/lib/excon/middlewares/mock.rb +12 -3
- data/lib/excon/middlewares/redirect_follower.rb +25 -3
- data/lib/excon/middlewares/response_parser.rb +3 -0
- data/lib/excon/pretty_printer.rb +1 -8
- data/lib/excon/response.rb +12 -9
- data/lib/excon/socket.rb +51 -33
- data/lib/excon/ssl_socket.rb +33 -13
- data/lib/excon/test/plugin/server/exec.rb +2 -2
- data/lib/excon/test/server.rb +1 -1
- data/lib/excon/unix_socket.rb +1 -0
- data/lib/excon/utils.rb +58 -5
- data/lib/excon/version.rb +1 -1
- metadata +27 -98
- data/.document +0 -5
- data/.gitignore +0 -13
- data/.rspec +0 -3
- data/.travis.yml +0 -29
- data/Gemfile +0 -19
- data/Rakefile +0 -41
- data/benchmarks/class_vs_lambda.rb +0 -50
- data/benchmarks/concat_vs_insert.rb +0 -21
- data/benchmarks/concat_vs_interpolate.rb +0 -22
- data/benchmarks/cr_lf.rb +0 -21
- data/benchmarks/downcase-eq-eq_vs_casecmp.rb +0 -169
- data/benchmarks/excon.rb +0 -69
- data/benchmarks/excon_vs.rb +0 -165
- data/benchmarks/for_vs_array_each.rb +0 -27
- data/benchmarks/for_vs_hash_each.rb +0 -27
- data/benchmarks/has_key-vs-lookup.rb +0 -177
- data/benchmarks/headers_case_sensitivity.rb +0 -83
- data/benchmarks/headers_split_vs_match.rb +0 -34
- data/benchmarks/implicit_block-vs-explicit_block.rb +0 -98
- data/benchmarks/merging.rb +0 -21
- data/benchmarks/single_vs_double_quotes.rb +0 -21
- data/benchmarks/string_ranged_index.rb +0 -87
- data/benchmarks/strip_newline.rb +0 -115
- data/benchmarks/vs_stdlib.rb +0 -82
- data/changelog.txt +0 -1083
- data/spec/excon/error_spec.rb +0 -139
- data/spec/excon/test/server_spec.rb +0 -28
- data/spec/excon_spec.rb +0 -7
- data/spec/helpers/file_path_helpers.rb +0 -22
- data/spec/requests/basic_spec.rb +0 -40
- data/spec/requests/eof_requests_spec.rb +0 -36
- data/spec/requests/unix_socket_spec.rb +0 -46
- data/spec/spec_helper.rb +0 -24
- data/spec/support/shared_contexts/test_server_context.rb +0 -83
- data/spec/support/shared_examples/shared_example_for_clients.rb +0 -218
- data/spec/support/shared_examples/shared_example_for_streaming_clients.rb +0 -20
- data/spec/support/shared_examples/shared_example_for_test_servers.rb +0 -16
- data/tests/authorization_header_tests.rb +0 -29
- data/tests/bad_tests.rb +0 -47
- data/tests/basic_tests.rb +0 -351
- data/tests/batch_requests.rb +0 -133
- data/tests/complete_responses.rb +0 -31
- data/tests/data/127.0.0.1.cert.crt +0 -20
- data/tests/data/127.0.0.1.cert.key +0 -27
- data/tests/data/excon.cert.crt +0 -20
- data/tests/data/excon.cert.key +0 -27
- data/tests/data/xs +0 -1
- data/tests/error_tests.rb +0 -145
- data/tests/header_tests.rb +0 -119
- data/tests/middlewares/canned_response_tests.rb +0 -34
- data/tests/middlewares/capture_cookies_tests.rb +0 -34
- data/tests/middlewares/decompress_tests.rb +0 -157
- data/tests/middlewares/escape_path_tests.rb +0 -36
- data/tests/middlewares/idempotent_tests.rb +0 -206
- data/tests/middlewares/instrumentation_tests.rb +0 -315
- data/tests/middlewares/mock_tests.rb +0 -304
- data/tests/middlewares/redirect_follower_tests.rb +0 -112
- data/tests/pipeline_tests.rb +0 -40
- data/tests/proxy_tests.rb +0 -306
- data/tests/query_string_tests.rb +0 -87
- data/tests/rackups/basic.rb +0 -41
- data/tests/rackups/basic.ru +0 -3
- data/tests/rackups/basic_auth.ru +0 -14
- data/tests/rackups/deflater.ru +0 -4
- data/tests/rackups/proxy.ru +0 -18
- data/tests/rackups/query_string.ru +0 -13
- data/tests/rackups/redirecting.ru +0 -23
- data/tests/rackups/redirecting_with_cookie.ru +0 -40
- data/tests/rackups/request_headers.ru +0 -15
- data/tests/rackups/request_methods.ru +0 -21
- data/tests/rackups/response_header.ru +0 -18
- data/tests/rackups/ssl.ru +0 -16
- data/tests/rackups/ssl_mismatched_cn.ru +0 -15
- data/tests/rackups/ssl_verify_peer.ru +0 -16
- data/tests/rackups/streaming.ru +0 -30
- data/tests/rackups/thread_safety.ru +0 -17
- data/tests/rackups/timeout.ru +0 -14
- data/tests/rackups/webrick_patch.rb +0 -34
- data/tests/request_headers_tests.rb +0 -21
- data/tests/request_method_tests.rb +0 -47
- data/tests/request_tests.rb +0 -59
- data/tests/response_tests.rb +0 -197
- data/tests/servers/bad.rb +0 -20
- data/tests/servers/eof.rb +0 -17
- data/tests/servers/error.rb +0 -20
- data/tests/servers/good.rb +0 -350
- data/tests/test_helper.rb +0 -306
- data/tests/thread_safety_tests.rb +0 -39
- data/tests/timeout_tests.rb +0 -12
- data/tests/utils_tests.rb +0 -81
data/lib/excon/pretty_printer.rb
CHANGED
|
@@ -9,14 +9,7 @@ module Excon
|
|
|
9
9
|
datum.delete(:connection)
|
|
10
10
|
datum.delete(:stack)
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
datum[:headers] = datum[:headers].dup
|
|
14
|
-
datum[:headers]['Authorization'] = REDACTED
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
if datum.has_key?(:password)
|
|
18
|
-
datum[:password] = REDACTED
|
|
19
|
-
end
|
|
12
|
+
datum = Utils.redact(datum)
|
|
20
13
|
end
|
|
21
14
|
|
|
22
15
|
indent += 2
|
data/lib/excon/response.rb
CHANGED
|
@@ -59,10 +59,13 @@ module Excon
|
|
|
59
59
|
|
|
60
60
|
def self.parse(socket, datum)
|
|
61
61
|
# this will discard any trailing lines from the previous response if any.
|
|
62
|
-
|
|
62
|
+
line = nil
|
|
63
|
+
loop do
|
|
63
64
|
line = socket.readline
|
|
64
|
-
|
|
65
|
+
break if line[9,3].to_i != 0
|
|
66
|
+
end
|
|
65
67
|
|
|
68
|
+
status = line[9, 3].to_i
|
|
66
69
|
reason_phrase = line[13..-3] # -3 strips the trailing "\r\n"
|
|
67
70
|
|
|
68
71
|
datum[:response] = {
|
|
@@ -90,7 +93,7 @@ module Excon
|
|
|
90
93
|
|
|
91
94
|
unless (['HEAD', 'CONNECT'].include?(datum[:method].to_s.upcase)) || NO_ENTITY.include?(datum[:response][:status])
|
|
92
95
|
|
|
93
|
-
if key = datum[:response][:headers].keys.detect {|k| k.casecmp('Transfer-Encoding') == 0 }
|
|
96
|
+
if (key = datum[:response][:headers].keys.detect {|k| k.casecmp('Transfer-Encoding') == 0 })
|
|
94
97
|
encodings = Utils.split_header_value(datum[:response][:headers][key])
|
|
95
98
|
if (encoding = encodings.last) && encoding.casecmp('chunked') == 0
|
|
96
99
|
transfer_encoding_chunked = true
|
|
@@ -103,7 +106,7 @@ module Excon
|
|
|
103
106
|
end
|
|
104
107
|
|
|
105
108
|
# use :response_block unless :expects would fail
|
|
106
|
-
if response_block = datum[:response_block]
|
|
109
|
+
if (response_block = datum[:response_block])
|
|
107
110
|
if datum[:middlewares].include?(Excon::Middleware::Expects) && datum[:expects] &&
|
|
108
111
|
!Array(datum[:expects]).include?(datum[:response][:status])
|
|
109
112
|
response_block = nil
|
|
@@ -140,11 +143,11 @@ module Excon
|
|
|
140
143
|
end
|
|
141
144
|
parse_headers(socket, datum) # merge trailers into headers
|
|
142
145
|
else
|
|
143
|
-
if key = datum[:response][:headers].keys.detect {|k| k.casecmp('Content-Length') == 0 }
|
|
146
|
+
if (key = datum[:response][:headers].keys.detect {|k| k.casecmp('Content-Length') == 0 })
|
|
144
147
|
content_length = datum[:response][:headers][key].to_i
|
|
145
148
|
end
|
|
146
149
|
|
|
147
|
-
if remaining = content_length
|
|
150
|
+
if (remaining = content_length)
|
|
148
151
|
if response_block
|
|
149
152
|
while remaining > 0
|
|
150
153
|
chunk = socket.read([datum[:chunk_size], remaining].min) || raise(EOFError)
|
|
@@ -160,11 +163,11 @@ module Excon
|
|
|
160
163
|
end
|
|
161
164
|
else
|
|
162
165
|
if response_block
|
|
163
|
-
while chunk = socket.read(datum[:chunk_size])
|
|
166
|
+
while (chunk = socket.read(datum[:chunk_size]))
|
|
164
167
|
response_block.call(chunk, nil, nil)
|
|
165
168
|
end
|
|
166
169
|
else
|
|
167
|
-
while chunk = socket.read(datum[:chunk_size])
|
|
170
|
+
while (chunk = socket.read(datum[:chunk_size]))
|
|
168
171
|
datum[:response][:body] << chunk
|
|
169
172
|
end
|
|
170
173
|
end
|
|
@@ -222,7 +225,7 @@ module Excon
|
|
|
222
225
|
end
|
|
223
226
|
|
|
224
227
|
# Retrieve a specific header value. Header names are treated case-insensitively.
|
|
225
|
-
#
|
|
228
|
+
# @param [String] name Header name
|
|
226
229
|
def get_header(name)
|
|
227
230
|
headers[name]
|
|
228
231
|
end
|
data/lib/excon/socket.rb
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
require 'resolv'
|
|
3
|
+
|
|
2
4
|
module Excon
|
|
3
5
|
class Socket
|
|
4
6
|
include Utils
|
|
@@ -7,6 +9,23 @@ module Excon
|
|
|
7
9
|
|
|
8
10
|
attr_accessor :data
|
|
9
11
|
|
|
12
|
+
# read/write drawn from https://github.com/ruby-amqp/bunny/commit/75d9dd79551b31a5dd3d1254c537bad471f108cf
|
|
13
|
+
CONNECT_RETRY_EXCEPTION_CLASSES = if defined?(IO::EINPROGRESSWaitWritable) # Ruby >= 2.1
|
|
14
|
+
[Errno::EINPROGRESS, IO::EINPROGRESSWaitWritable]
|
|
15
|
+
else # Ruby <= 2.0
|
|
16
|
+
[Errno::EINPROGRESS]
|
|
17
|
+
end
|
|
18
|
+
READ_RETRY_EXCEPTION_CLASSES = if defined?(IO::EAGAINWaitReadable) # Ruby >= 2.1
|
|
19
|
+
[Errno::EAGAIN, Errno::EWOULDBLOCK, IO::WaitReadable, IO::EAGAINWaitReadable, IO::EWOULDBLOCKWaitReadable]
|
|
20
|
+
else # Ruby <= 2.0
|
|
21
|
+
[Errno::EAGAIN, Errno::EWOULDBLOCK, IO::WaitReadable]
|
|
22
|
+
end
|
|
23
|
+
WRITE_RETRY_EXCEPTION_CLASSES = if defined?(IO::EAGAINWaitWritable) # Ruby >= 2.1
|
|
24
|
+
[Errno::EAGAIN, Errno::EWOULDBLOCK, IO::WaitWritable, IO::EAGAINWaitWritable, IO::EWOULDBLOCKWaitWritable]
|
|
25
|
+
else # Ruby <= 2.0
|
|
26
|
+
[Errno::EAGAIN, Errno::EWOULDBLOCK, IO::WaitWritable]
|
|
27
|
+
end
|
|
28
|
+
|
|
10
29
|
def params
|
|
11
30
|
Excon.display_warning('Excon::Socket#params is deprecated use Excon::Socket#data instead.')
|
|
12
31
|
@data
|
|
@@ -43,18 +62,8 @@ module Excon
|
|
|
43
62
|
def readline
|
|
44
63
|
return legacy_readline if RUBY_VERSION.to_f <= 1.8_7
|
|
45
64
|
buffer = String.new
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
buffer
|
|
49
|
-
rescue Errno::EAGAIN, Errno::EWOULDBLOCK, IO::WaitReadable
|
|
50
|
-
select_with_timeout(@socket, :read) && retry
|
|
51
|
-
rescue OpenSSL::SSL::SSLError => error
|
|
52
|
-
if error.message == 'read would block'
|
|
53
|
-
select_with_timeout(@socket, :read) && retry
|
|
54
|
-
else
|
|
55
|
-
raise(error)
|
|
56
|
-
end
|
|
57
|
-
end
|
|
65
|
+
buffer << (read_nonblock(1) || raise(EOFError)) while buffer[-1] != "\n"
|
|
66
|
+
buffer
|
|
58
67
|
end
|
|
59
68
|
|
|
60
69
|
def legacy_readline
|
|
@@ -88,32 +97,30 @@ module Excon
|
|
|
88
97
|
def connect
|
|
89
98
|
@socket = nil
|
|
90
99
|
exception = nil
|
|
100
|
+
hostname = @data[:hostname]
|
|
101
|
+
port = @port
|
|
102
|
+
family = @data[:family]
|
|
91
103
|
|
|
92
104
|
if @data[:proxy]
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
family = @data[:family] || ::Socket::Constants::AF_UNSPEC
|
|
97
|
-
args = [@data[:hostname], @port, family, ::Socket::Constants::SOCK_STREAM]
|
|
105
|
+
hostname = @data[:proxy][:hostname]
|
|
106
|
+
port = @data[:proxy][:port]
|
|
107
|
+
family = @data[:proxy][:family]
|
|
98
108
|
end
|
|
99
|
-
if RUBY_VERSION >= '1.9.2' && defined?(RUBY_ENGINE) && RUBY_ENGINE == 'ruby'
|
|
100
|
-
args << nil << nil << false # no reverse lookup
|
|
101
|
-
end
|
|
102
|
-
addrinfo = ::Socket.getaddrinfo(*args)
|
|
103
109
|
|
|
104
|
-
|
|
110
|
+
Resolv.each_address(hostname) do |ip|
|
|
105
111
|
# already succeeded on previous addrinfo
|
|
106
112
|
if @socket
|
|
107
113
|
break
|
|
108
114
|
end
|
|
109
|
-
|
|
115
|
+
|
|
110
116
|
@remote_ip = ip
|
|
117
|
+
@data[:remote_ip] = ip
|
|
111
118
|
|
|
112
119
|
# nonblocking connect
|
|
113
120
|
begin
|
|
114
121
|
sockaddr = ::Socket.sockaddr_in(port, ip)
|
|
115
|
-
|
|
116
|
-
socket = ::Socket.new(
|
|
122
|
+
addrinfo = Addrinfo.getaddrinfo(ip, port, family, :STREAM).first
|
|
123
|
+
socket = ::Socket.new(addrinfo.pfamily, addrinfo.socktype, addrinfo.protocol)
|
|
117
124
|
|
|
118
125
|
if @data[:reuseaddr]
|
|
119
126
|
socket.setsockopt(::Socket::Constants::SOL_SOCKET, ::Socket::Constants::SO_REUSEADDR, true)
|
|
@@ -128,7 +135,7 @@ module Excon
|
|
|
128
135
|
socket.connect(sockaddr)
|
|
129
136
|
end
|
|
130
137
|
@socket = socket
|
|
131
|
-
rescue
|
|
138
|
+
rescue *CONNECT_RETRY_EXCEPTION_CLASSES
|
|
132
139
|
select_with_timeout(socket, :connect_write)
|
|
133
140
|
begin
|
|
134
141
|
socket.connect_nonblock(sockaddr)
|
|
@@ -143,6 +150,8 @@ module Excon
|
|
|
143
150
|
end
|
|
144
151
|
end
|
|
145
152
|
|
|
153
|
+
exception ||= Resolv::ResolvError.new("no address for #{hostname}")
|
|
154
|
+
|
|
146
155
|
# this will be our last encountered exception
|
|
147
156
|
fail exception unless @socket
|
|
148
157
|
|
|
@@ -151,6 +160,17 @@ module Excon
|
|
|
151
160
|
::Socket::TCP_NODELAY,
|
|
152
161
|
true)
|
|
153
162
|
end
|
|
163
|
+
|
|
164
|
+
if @data[:keepalive]
|
|
165
|
+
if [:SOL_SOCKET, :SO_KEEPALIVE, :SOL_TCP, :TCP_KEEPIDLE, :TCP_KEEPINTVL, :TCP_KEEPCNT].all?{|c| ::Socket.const_defined? c}
|
|
166
|
+
@socket.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_KEEPALIVE, true)
|
|
167
|
+
@socket.setsockopt(::Socket::SOL_TCP, ::Socket::TCP_KEEPIDLE, @data[:keepalive][:time])
|
|
168
|
+
@socket.setsockopt(::Socket::SOL_TCP, ::Socket::TCP_KEEPINTVL, @data[:keepalive][:intvl])
|
|
169
|
+
@socket.setsockopt(::Socket::SOL_TCP, ::Socket::TCP_KEEPCNT, @data[:keepalive][:probes])
|
|
170
|
+
else
|
|
171
|
+
Excon.display_warning('Excon::Socket keepalive was set, but is not supported by Ruby version.')
|
|
172
|
+
end
|
|
173
|
+
end
|
|
154
174
|
end
|
|
155
175
|
|
|
156
176
|
def read_nonblock(max_length)
|
|
@@ -170,7 +190,7 @@ module Excon
|
|
|
170
190
|
else
|
|
171
191
|
raise(error)
|
|
172
192
|
end
|
|
173
|
-
rescue
|
|
193
|
+
rescue *READ_RETRY_EXCEPTION_CLASSES
|
|
174
194
|
if @read_buffer.empty?
|
|
175
195
|
# if we didn't read anything, try again...
|
|
176
196
|
select_with_timeout(@socket, :read) && retry
|
|
@@ -199,7 +219,7 @@ module Excon
|
|
|
199
219
|
else
|
|
200
220
|
raise(error)
|
|
201
221
|
end
|
|
202
|
-
rescue
|
|
222
|
+
rescue *READ_RETRY_EXCEPTION_CLASSES
|
|
203
223
|
if @read_buffer.empty?
|
|
204
224
|
select_with_timeout(@socket, :read) && retry
|
|
205
225
|
end
|
|
@@ -208,9 +228,7 @@ module Excon
|
|
|
208
228
|
end
|
|
209
229
|
|
|
210
230
|
def write_nonblock(data)
|
|
211
|
-
|
|
212
|
-
data.force_encoding('BINARY')
|
|
213
|
-
end
|
|
231
|
+
data = binary_encode(data)
|
|
214
232
|
loop do
|
|
215
233
|
written = nil
|
|
216
234
|
begin
|
|
@@ -225,7 +243,7 @@ module Excon
|
|
|
225
243
|
else
|
|
226
244
|
raise error
|
|
227
245
|
end
|
|
228
|
-
rescue OpenSSL::SSL::SSLError,
|
|
246
|
+
rescue OpenSSL::SSL::SSLError, *WRITE_RETRY_EXCEPTION_CLASSES => error
|
|
229
247
|
if error.is_a?(OpenSSL::SSL::SSLError) && error.message != 'write would block'
|
|
230
248
|
raise error
|
|
231
249
|
else
|
|
@@ -246,7 +264,7 @@ module Excon
|
|
|
246
264
|
|
|
247
265
|
def write_block(data)
|
|
248
266
|
@socket.write(data)
|
|
249
|
-
rescue OpenSSL::SSL::SSLError,
|
|
267
|
+
rescue OpenSSL::SSL::SSLError, *WRITE_RETRY_EXCEPTION_CLASSES => error
|
|
250
268
|
if error.is_a?(OpenSSL::SSL::SSLError) && error.message != 'write would block'
|
|
251
269
|
raise error
|
|
252
270
|
else
|
data/lib/excon/ssl_socket.rb
CHANGED
|
@@ -12,6 +12,11 @@ module Excon
|
|
|
12
12
|
# create ssl context
|
|
13
13
|
ssl_context = OpenSSL::SSL::SSLContext.new
|
|
14
14
|
|
|
15
|
+
# set the security level before setting other parameters affected by it
|
|
16
|
+
if @data[:ssl_security_level]
|
|
17
|
+
ssl_context.security_level = @data[:ssl_security_level]
|
|
18
|
+
end
|
|
19
|
+
|
|
15
20
|
# disable less secure options, when supported
|
|
16
21
|
ssl_context_options = OpenSSL::SSL::SSLContext::DEFAULT_PARAMS[:options]
|
|
17
22
|
if defined?(OpenSSL::SSL::OP_DONT_INSERT_EMPTY_FRAGMENTS)
|
|
@@ -27,18 +32,25 @@ module Excon
|
|
|
27
32
|
if @data[:ssl_version]
|
|
28
33
|
ssl_context.ssl_version = @data[:ssl_version]
|
|
29
34
|
end
|
|
35
|
+
if @data[:ssl_min_version]
|
|
36
|
+
ssl_context.min_version = @data[:ssl_min_version]
|
|
37
|
+
end
|
|
38
|
+
if @data[:ssl_max_version]
|
|
39
|
+
ssl_context.max_version = @data[:ssl_max_version]
|
|
40
|
+
end
|
|
41
|
+
|
|
30
42
|
|
|
31
43
|
if @data[:ssl_verify_peer]
|
|
32
44
|
# turn verification on
|
|
33
45
|
ssl_context.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
|
34
46
|
|
|
35
|
-
if ca_file = @data[:ssl_ca_file] || ENV['SSL_CERT_FILE']
|
|
47
|
+
if (ca_file = @data[:ssl_ca_file] || ENV['SSL_CERT_FILE'])
|
|
36
48
|
ssl_context.ca_file = ca_file
|
|
37
49
|
end
|
|
38
|
-
if ca_path = @data[:ssl_ca_path] || ENV['SSL_CERT_DIR']
|
|
50
|
+
if (ca_path = @data[:ssl_ca_path] || ENV['SSL_CERT_DIR'])
|
|
39
51
|
ssl_context.ca_path = ca_path
|
|
40
52
|
end
|
|
41
|
-
if cert_store = @data[:ssl_cert_store]
|
|
53
|
+
if (cert_store = @data[:ssl_cert_store])
|
|
42
54
|
ssl_context.cert_store = cert_store
|
|
43
55
|
end
|
|
44
56
|
|
|
@@ -58,7 +70,7 @@ module Excon
|
|
|
58
70
|
end
|
|
59
71
|
end
|
|
60
72
|
|
|
61
|
-
if verify_callback = @data[:ssl_verify_callback]
|
|
73
|
+
if (verify_callback = @data[:ssl_verify_callback])
|
|
62
74
|
ssl_context.verify_callback = verify_callback
|
|
63
75
|
end
|
|
64
76
|
else
|
|
@@ -66,6 +78,9 @@ module Excon
|
|
|
66
78
|
ssl_context.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
|
67
79
|
end
|
|
68
80
|
|
|
81
|
+
# Verify certificate hostname if supported (ruby >= 2.4.0)
|
|
82
|
+
ssl_context.verify_hostname = @data[:ssl_verify_hostname] if ssl_context.respond_to?(:verify_hostname=)
|
|
83
|
+
|
|
69
84
|
if client_cert_data && client_key_data
|
|
70
85
|
ssl_context.cert = OpenSSL::X509::Certificate.new client_cert_data
|
|
71
86
|
if OpenSSL::PKey.respond_to? :read
|
|
@@ -94,13 +109,20 @@ module Excon
|
|
|
94
109
|
|
|
95
110
|
request += "Proxy-Connection: Keep-Alive#{Excon::CR_NL}"
|
|
96
111
|
|
|
112
|
+
if @data[:ssl_proxy_headers]
|
|
113
|
+
request << Utils.headers_hash_to_s(@data[:ssl_proxy_headers])
|
|
114
|
+
end
|
|
115
|
+
|
|
97
116
|
request += Excon::CR_NL
|
|
98
117
|
|
|
99
118
|
# write out the proxy setup request
|
|
100
119
|
@socket.write(request)
|
|
101
120
|
|
|
102
121
|
# eat the proxy's connection response
|
|
103
|
-
Excon::Response.parse(self, :expects => 200, :method => 'CONNECT')
|
|
122
|
+
response = Excon::Response.parse(self, :expects => 200, :method => 'CONNECT')
|
|
123
|
+
if response[:response][:status] != 200
|
|
124
|
+
raise(Excon::Errors::ProxyConnectionError.new("proxy connection could not be established", request, response))
|
|
125
|
+
end
|
|
104
126
|
end
|
|
105
127
|
|
|
106
128
|
# convert Socket to OpenSSL::SSL::SSLSocket
|
|
@@ -132,18 +154,16 @@ module Excon
|
|
|
132
154
|
if @data[:ssl_verify_peer]
|
|
133
155
|
@socket.post_connection_check(@data[:ssl_verify_peer_host] || @data[:host])
|
|
134
156
|
end
|
|
135
|
-
|
|
136
|
-
@socket
|
|
137
157
|
end
|
|
138
158
|
|
|
139
159
|
private
|
|
140
160
|
|
|
141
161
|
def client_cert_data
|
|
142
|
-
@client_cert_data ||= if ccd = @data[:client_cert_data]
|
|
162
|
+
@client_cert_data ||= if (ccd = @data[:client_cert_data])
|
|
143
163
|
ccd
|
|
144
|
-
elsif path = @data[:client_cert]
|
|
164
|
+
elsif (path = @data[:client_cert])
|
|
145
165
|
File.read path
|
|
146
|
-
elsif path = @data[:certificate_path]
|
|
166
|
+
elsif (path = @data[:certificate_path])
|
|
147
167
|
warn ":certificate_path is no longer supported and will be deprecated. Please use :client_cert or :client_cert_data"
|
|
148
168
|
File.read path
|
|
149
169
|
end
|
|
@@ -156,11 +176,11 @@ module Excon
|
|
|
156
176
|
end
|
|
157
177
|
|
|
158
178
|
def client_key_data
|
|
159
|
-
@client_key_data ||= if ckd = @data[:client_key_data]
|
|
179
|
+
@client_key_data ||= if (ckd = @data[:client_key_data])
|
|
160
180
|
ckd
|
|
161
|
-
elsif path = @data[:client_key]
|
|
181
|
+
elsif (path = @data[:client_key])
|
|
162
182
|
File.read path
|
|
163
|
-
elsif path = @data[:private_key_path]
|
|
183
|
+
elsif (path = @data[:private_key_path])
|
|
164
184
|
warn ":private_key_path is no longer supported and will be deprecated. Please use :client_key or :client_key_data"
|
|
165
185
|
File.read path
|
|
166
186
|
end
|
|
@@ -5,12 +5,12 @@ module Excon
|
|
|
5
5
|
module Exec
|
|
6
6
|
def start(app_str = app)
|
|
7
7
|
line = ''
|
|
8
|
-
open_process(
|
|
8
|
+
open_process(app_str)
|
|
9
9
|
until line =~ /\Aready\Z/
|
|
10
10
|
line = error.gets
|
|
11
11
|
fatal_time = elapsed_time > timeout
|
|
12
12
|
if fatal_time
|
|
13
|
-
msg = "executable #{
|
|
13
|
+
msg = "executable #{app_str} has taken too long to start"
|
|
14
14
|
raise msg
|
|
15
15
|
end
|
|
16
16
|
end
|
data/lib/excon/test/server.rb
CHANGED
data/lib/excon/unix_socket.rb
CHANGED
data/lib/excon/utils.rb
CHANGED
|
@@ -10,6 +10,18 @@ module Excon
|
|
|
10
10
|
UNESCAPED = /([#{ Regexp.escape(CONTROL + ' ' + DELIMS + UNWISE + NONASCII) }])/
|
|
11
11
|
ESCAPED = /%([0-9a-fA-F]{2})/
|
|
12
12
|
|
|
13
|
+
def binary_encode(string)
|
|
14
|
+
if FORCE_ENC && string.encoding != Encoding::ASCII_8BIT
|
|
15
|
+
if string.frozen?
|
|
16
|
+
string.dup.force_encoding('BINARY')
|
|
17
|
+
else
|
|
18
|
+
string.force_encoding('BINARY')
|
|
19
|
+
end
|
|
20
|
+
else
|
|
21
|
+
string
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
13
25
|
def connection_uri(datum = @data)
|
|
14
26
|
unless datum
|
|
15
27
|
raise ArgumentError, '`datum` must be given unless called on a Connection'
|
|
@@ -21,6 +33,30 @@ module Excon
|
|
|
21
33
|
end
|
|
22
34
|
end
|
|
23
35
|
|
|
36
|
+
# Redact sensitive info from provided data
|
|
37
|
+
def redact(datum)
|
|
38
|
+
datum = datum.dup
|
|
39
|
+
if datum.has_key?(:headers)
|
|
40
|
+
if datum[:headers].has_key?('Authorization') || datum[:headers].has_key?('Proxy-Authorization')
|
|
41
|
+
datum[:headers] = datum[:headers].dup
|
|
42
|
+
end
|
|
43
|
+
if datum[:headers].has_key?('Authorization')
|
|
44
|
+
datum[:headers]['Authorization'] = REDACTED
|
|
45
|
+
end
|
|
46
|
+
if datum[:headers].has_key?('Proxy-Authorization')
|
|
47
|
+
datum[:headers]['Proxy-Authorization'] = REDACTED
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
if datum.has_key?(:password)
|
|
51
|
+
datum[:password] = REDACTED
|
|
52
|
+
end
|
|
53
|
+
if datum.has_key?(:proxy) && datum[:proxy] && datum[:proxy].has_key?(:password)
|
|
54
|
+
datum[:proxy] = datum[:proxy].dup
|
|
55
|
+
datum[:proxy][:password] = REDACTED
|
|
56
|
+
end
|
|
57
|
+
datum
|
|
58
|
+
end
|
|
59
|
+
|
|
24
60
|
def request_uri(datum)
|
|
25
61
|
connection_uri(datum) + datum[:path] + query_string(datum)
|
|
26
62
|
end
|
|
@@ -59,31 +95,48 @@ module Excon
|
|
|
59
95
|
def split_header_value(str)
|
|
60
96
|
return [] if str.nil?
|
|
61
97
|
str = str.dup.strip
|
|
62
|
-
str
|
|
63
|
-
str.scan(%r'\G((?:"(?:\\.|[^"])+?"|[^",]
|
|
98
|
+
str = binary_encode(str)
|
|
99
|
+
str.scan(%r'\G((?:"(?:\\.|[^"])+?"|[^",])+)
|
|
64
100
|
(?:,\s*|\Z)'xn).flatten
|
|
65
101
|
end
|
|
66
102
|
|
|
67
103
|
# Escapes HTTP reserved and unwise characters in +str+
|
|
68
104
|
def escape_uri(str)
|
|
69
105
|
str = str.dup
|
|
70
|
-
str
|
|
106
|
+
str = binary_encode(str)
|
|
71
107
|
str.gsub(UNESCAPED) { "%%%02X" % $1[0].ord }
|
|
72
108
|
end
|
|
73
109
|
|
|
74
110
|
# Unescapes HTTP reserved and unwise characters in +str+
|
|
75
111
|
def unescape_uri(str)
|
|
76
112
|
str = str.dup
|
|
77
|
-
str
|
|
113
|
+
str = binary_encode(str)
|
|
78
114
|
str.gsub(ESCAPED) { $1.hex.chr }
|
|
79
115
|
end
|
|
80
116
|
|
|
81
117
|
# Unescape form encoded values in +str+
|
|
82
118
|
def unescape_form(str)
|
|
83
119
|
str = str.dup
|
|
84
|
-
str
|
|
120
|
+
str = binary_encode(str)
|
|
85
121
|
str.gsub!(/\+/, ' ')
|
|
86
122
|
str.gsub(ESCAPED) { $1.hex.chr }
|
|
87
123
|
end
|
|
124
|
+
|
|
125
|
+
# Performs validation on the passed header hash and returns a string representation of the headers
|
|
126
|
+
def headers_hash_to_s(headers)
|
|
127
|
+
headers_str = String.new
|
|
128
|
+
headers.each do |key, values|
|
|
129
|
+
if key.to_s.match(/[\r\n]/)
|
|
130
|
+
raise Excon::Errors::InvalidHeaderKey.new(key.to_s.inspect + ' contains forbidden "\r" or "\n"')
|
|
131
|
+
end
|
|
132
|
+
[values].flatten.each do |value|
|
|
133
|
+
if value.to_s.match(/[\r\n]/)
|
|
134
|
+
raise Excon::Errors::InvalidHeaderValue.new(value.to_s.inspect + ' contains forbidden "\r" or "\n"')
|
|
135
|
+
end
|
|
136
|
+
headers_str << key.to_s << ': ' << value.to_s << CR_NL
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
headers_str
|
|
140
|
+
end
|
|
88
141
|
end
|
|
89
142
|
end
|