excon 0.62.0 → 0.92.3
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 +1220 -1828
- 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 +59 -42
- 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
|
|
@@ -41,29 +60,18 @@ module Excon
|
|
|
41
60
|
end
|
|
42
61
|
|
|
43
62
|
def readline
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
buffer << @socket.read_nonblock(1) while buffer[-1] != "\n"
|
|
63
|
+
if @nonblock && RUBY_VERSION.to_f > 1.8_7
|
|
64
|
+
buffer = String.new
|
|
65
|
+
buffer << (read_nonblock(1) || raise(EOFError)) while buffer[-1] != "\n"
|
|
48
66
|
buffer
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
raise(
|
|
56
|
-
end
|
|
57
|
-
end
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
def legacy_readline
|
|
61
|
-
begin
|
|
62
|
-
Timeout.timeout(@data[:read_timeout]) do
|
|
63
|
-
@socket.readline
|
|
67
|
+
else # nonblock/legacy
|
|
68
|
+
begin
|
|
69
|
+
Timeout.timeout(@data[:read_timeout]) do
|
|
70
|
+
@socket.readline
|
|
71
|
+
end
|
|
72
|
+
rescue Timeout::Error
|
|
73
|
+
raise Excon::Errors::Timeout.new('read timeout reached')
|
|
64
74
|
end
|
|
65
|
-
rescue Timeout::Error
|
|
66
|
-
raise Excon::Errors::Timeout.new('read timeout reached')
|
|
67
75
|
end
|
|
68
76
|
end
|
|
69
77
|
|
|
@@ -88,32 +96,30 @@ module Excon
|
|
|
88
96
|
def connect
|
|
89
97
|
@socket = nil
|
|
90
98
|
exception = nil
|
|
99
|
+
hostname = @data[:hostname]
|
|
100
|
+
port = @port
|
|
101
|
+
family = @data[:family]
|
|
91
102
|
|
|
92
103
|
if @data[:proxy]
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
family = @data[:family] || ::Socket::Constants::AF_UNSPEC
|
|
97
|
-
args = [@data[:hostname], @port, family, ::Socket::Constants::SOCK_STREAM]
|
|
98
|
-
end
|
|
99
|
-
if RUBY_VERSION >= '1.9.2' && defined?(RUBY_ENGINE) && RUBY_ENGINE == 'ruby'
|
|
100
|
-
args << nil << nil << false # no reverse lookup
|
|
104
|
+
hostname = @data[:proxy][:hostname]
|
|
105
|
+
port = @data[:proxy][:port]
|
|
106
|
+
family = @data[:proxy][:family]
|
|
101
107
|
end
|
|
102
|
-
addrinfo = ::Socket.getaddrinfo(*args)
|
|
103
108
|
|
|
104
|
-
|
|
109
|
+
Resolv.each_address(hostname) do |ip|
|
|
105
110
|
# already succeeded on previous addrinfo
|
|
106
111
|
if @socket
|
|
107
112
|
break
|
|
108
113
|
end
|
|
109
|
-
|
|
114
|
+
|
|
110
115
|
@remote_ip = ip
|
|
116
|
+
@data[:remote_ip] = ip
|
|
111
117
|
|
|
112
118
|
# nonblocking connect
|
|
113
119
|
begin
|
|
114
120
|
sockaddr = ::Socket.sockaddr_in(port, ip)
|
|
115
|
-
|
|
116
|
-
socket = ::Socket.new(
|
|
121
|
+
addrinfo = Addrinfo.getaddrinfo(ip, port, family, :STREAM).first
|
|
122
|
+
socket = ::Socket.new(addrinfo.pfamily, addrinfo.socktype, addrinfo.protocol)
|
|
117
123
|
|
|
118
124
|
if @data[:reuseaddr]
|
|
119
125
|
socket.setsockopt(::Socket::Constants::SOL_SOCKET, ::Socket::Constants::SO_REUSEADDR, true)
|
|
@@ -128,7 +134,7 @@ module Excon
|
|
|
128
134
|
socket.connect(sockaddr)
|
|
129
135
|
end
|
|
130
136
|
@socket = socket
|
|
131
|
-
rescue
|
|
137
|
+
rescue *CONNECT_RETRY_EXCEPTION_CLASSES
|
|
132
138
|
select_with_timeout(socket, :connect_write)
|
|
133
139
|
begin
|
|
134
140
|
socket.connect_nonblock(sockaddr)
|
|
@@ -143,6 +149,8 @@ module Excon
|
|
|
143
149
|
end
|
|
144
150
|
end
|
|
145
151
|
|
|
152
|
+
exception ||= Resolv::ResolvError.new("no address for #{hostname}")
|
|
153
|
+
|
|
146
154
|
# this will be our last encountered exception
|
|
147
155
|
fail exception unless @socket
|
|
148
156
|
|
|
@@ -151,6 +159,17 @@ module Excon
|
|
|
151
159
|
::Socket::TCP_NODELAY,
|
|
152
160
|
true)
|
|
153
161
|
end
|
|
162
|
+
|
|
163
|
+
if @data[:keepalive]
|
|
164
|
+
if [:SOL_SOCKET, :SO_KEEPALIVE, :SOL_TCP, :TCP_KEEPIDLE, :TCP_KEEPINTVL, :TCP_KEEPCNT].all?{|c| ::Socket.const_defined? c}
|
|
165
|
+
@socket.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_KEEPALIVE, true)
|
|
166
|
+
@socket.setsockopt(::Socket::SOL_TCP, ::Socket::TCP_KEEPIDLE, @data[:keepalive][:time])
|
|
167
|
+
@socket.setsockopt(::Socket::SOL_TCP, ::Socket::TCP_KEEPINTVL, @data[:keepalive][:intvl])
|
|
168
|
+
@socket.setsockopt(::Socket::SOL_TCP, ::Socket::TCP_KEEPCNT, @data[:keepalive][:probes])
|
|
169
|
+
else
|
|
170
|
+
Excon.display_warning('Excon::Socket keepalive was set, but is not supported by Ruby version.')
|
|
171
|
+
end
|
|
172
|
+
end
|
|
154
173
|
end
|
|
155
174
|
|
|
156
175
|
def read_nonblock(max_length)
|
|
@@ -170,7 +189,7 @@ module Excon
|
|
|
170
189
|
else
|
|
171
190
|
raise(error)
|
|
172
191
|
end
|
|
173
|
-
rescue
|
|
192
|
+
rescue *READ_RETRY_EXCEPTION_CLASSES
|
|
174
193
|
if @read_buffer.empty?
|
|
175
194
|
# if we didn't read anything, try again...
|
|
176
195
|
select_with_timeout(@socket, :read) && retry
|
|
@@ -199,7 +218,7 @@ module Excon
|
|
|
199
218
|
else
|
|
200
219
|
raise(error)
|
|
201
220
|
end
|
|
202
|
-
rescue
|
|
221
|
+
rescue *READ_RETRY_EXCEPTION_CLASSES
|
|
203
222
|
if @read_buffer.empty?
|
|
204
223
|
select_with_timeout(@socket, :read) && retry
|
|
205
224
|
end
|
|
@@ -208,9 +227,7 @@ module Excon
|
|
|
208
227
|
end
|
|
209
228
|
|
|
210
229
|
def write_nonblock(data)
|
|
211
|
-
|
|
212
|
-
data.force_encoding('BINARY')
|
|
213
|
-
end
|
|
230
|
+
data = binary_encode(data)
|
|
214
231
|
loop do
|
|
215
232
|
written = nil
|
|
216
233
|
begin
|
|
@@ -225,7 +242,7 @@ module Excon
|
|
|
225
242
|
else
|
|
226
243
|
raise error
|
|
227
244
|
end
|
|
228
|
-
rescue OpenSSL::SSL::SSLError,
|
|
245
|
+
rescue OpenSSL::SSL::SSLError, *WRITE_RETRY_EXCEPTION_CLASSES => error
|
|
229
246
|
if error.is_a?(OpenSSL::SSL::SSLError) && error.message != 'write would block'
|
|
230
247
|
raise error
|
|
231
248
|
else
|
|
@@ -246,7 +263,7 @@ module Excon
|
|
|
246
263
|
|
|
247
264
|
def write_block(data)
|
|
248
265
|
@socket.write(data)
|
|
249
|
-
rescue OpenSSL::SSL::SSLError,
|
|
266
|
+
rescue OpenSSL::SSL::SSLError, *WRITE_RETRY_EXCEPTION_CLASSES => error
|
|
250
267
|
if error.is_a?(OpenSSL::SSL::SSLError) && error.message != 'write would block'
|
|
251
268
|
raise error
|
|
252
269
|
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))
|