excon 0.62.0 → 0.91.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/CONTRIBUTING.md +0 -1
- data/LICENSE.md +1 -1
- data/README.md +7 -6
- data/data/cacert.pem +1154 -1798
- data/excon.gemspec +19 -3
- data/lib/excon/connection.rb +216 -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 +5 -16
- 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 +11 -4
- 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 +37 -15
- data/lib/excon/test/plugin/server/exec.rb +5 -2
- data/lib/excon/test/plugin/server/puma.rb +4 -1
- data/lib/excon/test/plugin/server/unicorn.rb +5 -0
- data/lib/excon/test/plugin/server/webrick.rb +4 -1
- data/lib/excon/test/server.rb +1 -1
- data/lib/excon/unix_socket.rb +1 -0
- data/lib/excon/utils.rb +59 -5
- data/lib/excon/version.rb +1 -1
- data/lib/excon.rb +25 -17
- 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/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,26 +32,35 @@ 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
|
|
|
45
|
-
|
|
46
|
-
unless ca_file || ca_path || cert_store
|
|
57
|
+
if cert_store.nil?
|
|
47
58
|
ssl_context.cert_store = OpenSSL::X509::Store.new
|
|
48
59
|
ssl_context.cert_store.set_default_paths
|
|
60
|
+
end
|
|
49
61
|
|
|
62
|
+
# no defaults, fallback to bundled
|
|
63
|
+
unless ca_file || ca_path || cert_store
|
|
50
64
|
# workaround issue #257 (JRUBY-6970)
|
|
51
65
|
ca_file = DEFAULT_CA_FILE
|
|
52
66
|
ca_file = ca_file.gsub(/^jar:/, '') if ca_file =~ /^jar:file:\//
|
|
@@ -58,7 +72,7 @@ module Excon
|
|
|
58
72
|
end
|
|
59
73
|
end
|
|
60
74
|
|
|
61
|
-
if verify_callback = @data[:ssl_verify_callback]
|
|
75
|
+
if (verify_callback = @data[:ssl_verify_callback])
|
|
62
76
|
ssl_context.verify_callback = verify_callback
|
|
63
77
|
end
|
|
64
78
|
else
|
|
@@ -66,6 +80,9 @@ module Excon
|
|
|
66
80
|
ssl_context.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
|
67
81
|
end
|
|
68
82
|
|
|
83
|
+
# Verify certificate hostname if supported (ruby >= 2.4.0)
|
|
84
|
+
ssl_context.verify_hostname = @data[:ssl_verify_hostname] if ssl_context.respond_to?(:verify_hostname=)
|
|
85
|
+
|
|
69
86
|
if client_cert_data && client_key_data
|
|
70
87
|
ssl_context.cert = OpenSSL::X509::Certificate.new client_cert_data
|
|
71
88
|
if OpenSSL::PKey.respond_to? :read
|
|
@@ -94,13 +111,20 @@ module Excon
|
|
|
94
111
|
|
|
95
112
|
request += "Proxy-Connection: Keep-Alive#{Excon::CR_NL}"
|
|
96
113
|
|
|
114
|
+
if @data[:ssl_proxy_headers]
|
|
115
|
+
request << Utils.headers_hash_to_s(@data[:ssl_proxy_headers])
|
|
116
|
+
end
|
|
117
|
+
|
|
97
118
|
request += Excon::CR_NL
|
|
98
119
|
|
|
99
120
|
# write out the proxy setup request
|
|
100
121
|
@socket.write(request)
|
|
101
122
|
|
|
102
123
|
# eat the proxy's connection response
|
|
103
|
-
Excon::Response.parse(self, :expects => 200, :method => 'CONNECT')
|
|
124
|
+
response = Excon::Response.parse(self, :expects => 200, :method => 'CONNECT')
|
|
125
|
+
if response[:response][:status] != 200
|
|
126
|
+
raise(Excon::Errors::ProxyConnectionError.new("proxy connection could not be established", request, response))
|
|
127
|
+
end
|
|
104
128
|
end
|
|
105
129
|
|
|
106
130
|
# convert Socket to OpenSSL::SSL::SSLSocket
|
|
@@ -132,18 +156,16 @@ module Excon
|
|
|
132
156
|
if @data[:ssl_verify_peer]
|
|
133
157
|
@socket.post_connection_check(@data[:ssl_verify_peer_host] || @data[:host])
|
|
134
158
|
end
|
|
135
|
-
|
|
136
|
-
@socket
|
|
137
159
|
end
|
|
138
160
|
|
|
139
161
|
private
|
|
140
162
|
|
|
141
163
|
def client_cert_data
|
|
142
|
-
@client_cert_data ||= if ccd = @data[:client_cert_data]
|
|
164
|
+
@client_cert_data ||= if (ccd = @data[:client_cert_data])
|
|
143
165
|
ccd
|
|
144
|
-
elsif path = @data[:client_cert]
|
|
166
|
+
elsif (path = @data[:client_cert])
|
|
145
167
|
File.read path
|
|
146
|
-
elsif path = @data[:certificate_path]
|
|
168
|
+
elsif (path = @data[:certificate_path])
|
|
147
169
|
warn ":certificate_path is no longer supported and will be deprecated. Please use :client_cert or :client_cert_data"
|
|
148
170
|
File.read path
|
|
149
171
|
end
|
|
@@ -156,11 +178,11 @@ module Excon
|
|
|
156
178
|
end
|
|
157
179
|
|
|
158
180
|
def client_key_data
|
|
159
|
-
@client_key_data ||= if ckd = @data[:client_key_data]
|
|
181
|
+
@client_key_data ||= if (ckd = @data[:client_key_data])
|
|
160
182
|
ckd
|
|
161
|
-
elsif path = @data[:client_key]
|
|
183
|
+
elsif (path = @data[:client_key])
|
|
162
184
|
File.read path
|
|
163
|
-
elsif path = @data[:private_key_path]
|
|
185
|
+
elsif (path = @data[:private_key_path])
|
|
164
186
|
warn ":private_key_path is no longer supported and will be deprecated. Please use :client_key or :client_key_data"
|
|
165
187
|
File.read path
|
|
166
188
|
end
|
|
@@ -4,13 +4,16 @@ module Excon
|
|
|
4
4
|
module Server
|
|
5
5
|
module Exec
|
|
6
6
|
def start(app_str = app)
|
|
7
|
+
open_process(app_str)
|
|
8
|
+
process_stderr = ""
|
|
7
9
|
line = ''
|
|
8
|
-
open_process(app)
|
|
9
10
|
until line =~ /\Aready\Z/
|
|
10
11
|
line = error.gets
|
|
12
|
+
raise process_stderr if line.nil?
|
|
13
|
+
process_stderr << line
|
|
11
14
|
fatal_time = elapsed_time > timeout
|
|
12
15
|
if fatal_time
|
|
13
|
-
msg = "executable #{
|
|
16
|
+
msg = "executable #{app_str} has taken too long to start"
|
|
14
17
|
raise msg
|
|
15
18
|
end
|
|
16
19
|
end
|
|
@@ -4,10 +4,13 @@ module Excon
|
|
|
4
4
|
module Server
|
|
5
5
|
module Puma
|
|
6
6
|
def start(app_str = app, bind_uri = bind)
|
|
7
|
-
open_process('puma', '-b', bind_uri.to_s, app_str)
|
|
7
|
+
open_process(RbConfig.ruby, '-S', 'puma', '-b', bind_uri.to_s, app_str)
|
|
8
|
+
process_stderr = ""
|
|
8
9
|
line = ''
|
|
9
10
|
until line =~ /Use Ctrl-C to stop/
|
|
10
11
|
line = read.gets
|
|
12
|
+
raise process_stderr if line.nil?
|
|
13
|
+
process_stderr << line
|
|
11
14
|
fatal_time = elapsed_time > timeout
|
|
12
15
|
raise 'puma server has taken too long to start' if fatal_time
|
|
13
16
|
end
|
|
@@ -13,6 +13,8 @@ module Excon
|
|
|
13
13
|
bind_str = "#{host}:#{bind_uri.port}"
|
|
14
14
|
end
|
|
15
15
|
args = [
|
|
16
|
+
RbConfig.ruby,
|
|
17
|
+
'-S',
|
|
16
18
|
'unicorn',
|
|
17
19
|
'--no-default-middleware',
|
|
18
20
|
'-l',
|
|
@@ -20,9 +22,12 @@ module Excon
|
|
|
20
22
|
app_str
|
|
21
23
|
]
|
|
22
24
|
open_process(*args)
|
|
25
|
+
process_stderr = ''
|
|
23
26
|
line = ''
|
|
24
27
|
until line =~ /worker\=0 ready/
|
|
25
28
|
line = error.gets
|
|
29
|
+
raise process_stderr if line.nil?
|
|
30
|
+
process_stderr << line
|
|
26
31
|
fatal_time = elapsed_time > timeout
|
|
27
32
|
raise 'unicorn server has taken too long to start' if fatal_time
|
|
28
33
|
end
|
|
@@ -7,10 +7,13 @@ module Excon
|
|
|
7
7
|
bind_uri = URI.parse(bind_uri) unless bind_uri.is_a? URI::Generic
|
|
8
8
|
host = bind_uri.host.gsub(/[\[\]]/, '')
|
|
9
9
|
port = bind_uri.port.to_s
|
|
10
|
-
open_process('rackup', '-s', 'webrick', '--host', host, '--port', port, app_str)
|
|
10
|
+
open_process(RbConfig.ruby, '-S', 'rackup', '-s', 'webrick', '--host', host, '--port', port, app_str)
|
|
11
|
+
process_stderr = ""
|
|
11
12
|
line = ''
|
|
12
13
|
until line =~ /HTTPServer#start/
|
|
13
14
|
line = error.gets
|
|
15
|
+
raise process_stderr if line.nil?
|
|
16
|
+
process_stderr << line
|
|
14
17
|
fatal_time = elapsed_time > timeout
|
|
15
18
|
raise 'webrick server has taken too long to start' if fatal_time
|
|
16
19
|
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,49 @@ 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
|
+
# Don't include the potentially sensitive header value (i.e. authorization token) in the message
|
|
135
|
+
raise Excon::Errors::InvalidHeaderValue.new(key.to_s + ' header value contains forbidden "\r" or "\n"')
|
|
136
|
+
end
|
|
137
|
+
headers_str << key.to_s << ': ' << value.to_s << CR_NL
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
headers_str
|
|
141
|
+
end
|
|
88
142
|
end
|
|
89
143
|
end
|
data/lib/excon/version.rb
CHANGED
data/lib/excon.rb
CHANGED
|
@@ -23,11 +23,11 @@ require 'excon/middlewares/instrumentor'
|
|
|
23
23
|
require 'excon/middlewares/mock'
|
|
24
24
|
require 'excon/middlewares/response_parser'
|
|
25
25
|
|
|
26
|
+
require 'excon/error'
|
|
26
27
|
require 'excon/constants'
|
|
27
28
|
require 'excon/utils'
|
|
28
29
|
|
|
29
30
|
require 'excon/connection'
|
|
30
|
-
require 'excon/error'
|
|
31
31
|
require 'excon/headers'
|
|
32
32
|
require 'excon/response'
|
|
33
33
|
require 'excon/middlewares/decompress'
|
|
@@ -61,6 +61,14 @@ module Excon
|
|
|
61
61
|
if $VERBOSE || ENV['EXCON_DEBUG']
|
|
62
62
|
$stderr.puts "[excon][WARNING] #{warning}\n#{ caller.join("\n") }"
|
|
63
63
|
end
|
|
64
|
+
|
|
65
|
+
if @raise_on_warnings
|
|
66
|
+
raise Error::Warning.new(warning)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def set_raise_on_warnings!(should_raise)
|
|
71
|
+
@raise_on_warnings = should_raise
|
|
64
72
|
end
|
|
65
73
|
|
|
66
74
|
# Status of mocking
|
|
@@ -105,9 +113,9 @@ module Excon
|
|
|
105
113
|
|
|
106
114
|
# @see Connection#initialize
|
|
107
115
|
# Initializes a new keep-alive session for a given remote host
|
|
108
|
-
#
|
|
109
|
-
#
|
|
110
|
-
#
|
|
116
|
+
# @param [String] url The destination URL
|
|
117
|
+
# @param [Hash<Symbol, >] params One or more option params to set on the Connection instance
|
|
118
|
+
# @return [Connection] A new Excon::Connection instance
|
|
111
119
|
def new(url, params = {})
|
|
112
120
|
uri_parser = params[:uri_parser] || defaults[:uri_parser]
|
|
113
121
|
uri = uri_parser.parse(url)
|
|
@@ -135,13 +143,13 @@ module Excon
|
|
|
135
143
|
end
|
|
136
144
|
|
|
137
145
|
# push an additional stub onto the list to check for mock requests
|
|
138
|
-
#
|
|
139
|
-
#
|
|
140
|
-
def stub(request_params = {}, response_params = nil)
|
|
141
|
-
if method = request_params.delete(:method)
|
|
146
|
+
# @param request_params [Hash<Symbol, >] request params to match against, omitted params match all
|
|
147
|
+
# @param response_params [Hash<Symbol, >] response params to return from matched request or block to call with params
|
|
148
|
+
def stub(request_params = {}, response_params = nil, &block)
|
|
149
|
+
if (method = request_params.delete(:method))
|
|
142
150
|
request_params[:method] = method.to_s.downcase.to_sym
|
|
143
151
|
end
|
|
144
|
-
if url = request_params.delete(:url)
|
|
152
|
+
if (url = request_params.delete(:url))
|
|
145
153
|
uri = URI.parse(url)
|
|
146
154
|
request_params = {
|
|
147
155
|
:host => uri.host,
|
|
@@ -167,7 +175,7 @@ module Excon
|
|
|
167
175
|
if response_params
|
|
168
176
|
raise(ArgumentError.new("stub requires either response_params OR a block"))
|
|
169
177
|
else
|
|
170
|
-
stub = [request_params,
|
|
178
|
+
stub = [request_params, block]
|
|
171
179
|
end
|
|
172
180
|
elsif response_params
|
|
173
181
|
stub = [request_params, response_params]
|
|
@@ -179,10 +187,10 @@ module Excon
|
|
|
179
187
|
end
|
|
180
188
|
|
|
181
189
|
# get a stub matching params or nil
|
|
182
|
-
#
|
|
183
|
-
#
|
|
190
|
+
# @param request_params [Hash<Symbol, >] request params to match against, omitted params match all
|
|
191
|
+
# @return [Hash<Symbol, >] response params to return from matched request or block to call with params
|
|
184
192
|
def stub_for(request_params={})
|
|
185
|
-
if method = request_params.delete(:method)
|
|
193
|
+
if (method = request_params.delete(:method))
|
|
186
194
|
request_params[:method] = method.to_s.downcase.to_sym
|
|
187
195
|
end
|
|
188
196
|
Excon.stubs.each do |stub, response_params|
|
|
@@ -190,7 +198,7 @@ module Excon
|
|
|
190
198
|
headers_match = !stub.has_key?(:headers) || stub[:headers].keys.all? do |key|
|
|
191
199
|
case value = stub[:headers][key]
|
|
192
200
|
when Regexp
|
|
193
|
-
if match = value.match(request_params[:headers][key])
|
|
201
|
+
if (match = value.match(request_params[:headers][key]))
|
|
194
202
|
captures[:headers][key] = match.captures
|
|
195
203
|
end
|
|
196
204
|
match
|
|
@@ -201,7 +209,7 @@ module Excon
|
|
|
201
209
|
non_headers_match = (stub.keys - [:headers]).all? do |key|
|
|
202
210
|
case value = stub[key]
|
|
203
211
|
when Regexp
|
|
204
|
-
if match = value.match(request_params[key])
|
|
212
|
+
if (match = value.match(request_params[key]))
|
|
205
213
|
captures[key] = match.captures
|
|
206
214
|
end
|
|
207
215
|
match
|
|
@@ -228,8 +236,8 @@ module Excon
|
|
|
228
236
|
end
|
|
229
237
|
|
|
230
238
|
# remove first/oldest stub matching request_params
|
|
231
|
-
#
|
|
232
|
-
#
|
|
239
|
+
# @param request_params [Hash<Symbol, >] request params to match against, omitted params match all
|
|
240
|
+
# @return [Hash<Symbol, >] response params from deleted stub
|
|
233
241
|
def unstub(request_params = {})
|
|
234
242
|
stub = stub_for(request_params)
|
|
235
243
|
Excon.stubs.delete_at(Excon.stubs.index(stub))
|