excon 0.90.0 → 0.99.0

Sign up to get free protection for your applications and to get access to all the features.
data/excon.gemspec CHANGED
@@ -26,7 +26,6 @@ Gem::Specification.new do |s|
26
26
  s.add_development_dependency('eventmachine', '>= 1.0.4')
27
27
  s.add_development_dependency('open4')
28
28
  s.add_development_dependency('rake')
29
- s.add_development_dependency('rdoc')
30
29
  s.add_development_dependency('shindo')
31
30
  s.add_development_dependency('sinatra')
32
31
  s.add_development_dependency('sinatra-contrib')
@@ -62,6 +62,7 @@ module Excon
62
62
  # @option params [Class] :instrumentor Responds to #instrument as in ActiveSupport::Notifications
63
63
  # @option params [String] :instrumentor_name Name prefix for #instrument events. Defaults to 'excon'
64
64
  def initialize(params = {})
65
+ @pid = Process.pid
65
66
  @data = Excon.defaults.dup
66
67
  # merge does not deep-dup, so make sure headers is not the original
67
68
  @data[:headers] = @data[:headers].dup
@@ -89,7 +90,9 @@ module Excon
89
90
  end
90
91
 
91
92
  if @data[:scheme] == UNIX
92
- if @data[:host]
93
+ # 'uri' >= v0.12.0 returns an empty string instead of nil for no host.
94
+ # So treat the parameter as present if and only if it is both non-nill and non-empty.
95
+ if @data[:host] && !@data[:host].empty?
93
96
  raise ArgumentError, "The `:host` parameter should not be set for `unix://` connections.\n" +
94
97
  "When supplying a `unix://` URI, it should start with `unix:/` or `unix:///`."
95
98
  elsif !@data[:socket]
@@ -253,6 +256,11 @@ module Excon
253
256
  datum[:headers] = { 'Host' => host }.merge(datum[:headers])
254
257
  end
255
258
 
259
+ # default to GET if no method specified
260
+ unless datum[:method]
261
+ datum[:method] = :get
262
+ end
263
+
256
264
  # if path is empty or doesn't start with '/', insert one
257
265
  unless datum[:path][0, 1] == '/'
258
266
  datum[:path] = datum[:path].dup.insert(0, '/')
@@ -473,6 +481,11 @@ module Excon
473
481
  @_excon_sockets ||= {}
474
482
  @_excon_sockets.compare_by_identity
475
483
 
484
+ if @pid != Process.pid
485
+ @_excon_sockets.clear # GC will take care of closing sockets
486
+ @pid = Process.pid
487
+ end
488
+
476
489
  if @data[:thread_safe_sockets]
477
490
  # In a multi-threaded world, if the same connection is used by multiple
478
491
  # threads at the same time to connect to the same destination, they may
@@ -572,6 +585,8 @@ module Excon
572
585
  raise ArgumentError, "The `:ssl_proxy_headers` parameter should only be used with HTTPS requests."
573
586
  end
574
587
  if @data[:proxy][:scheme] == UNIX
588
+ # URI.parse might return empty string for security reasons.
589
+ @data[:proxy][:host] = nil if @data[:proxy][:host] == ""
575
590
  if @data[:proxy][:host]
576
591
  raise ArgumentError, "The `:host` parameter should not be set for `unix://` proxies.\n" +
577
592
  "When supplying a `unix://` URI, it should start with `unix:/` or `unix:///`."
@@ -72,6 +72,8 @@ module Excon
72
72
  :client_key_pass,
73
73
  :client_cert,
74
74
  :client_cert_data,
75
+ :client_chain,
76
+ :client_chain_data,
75
77
  :certificate,
76
78
  :certificate_path,
77
79
  :disable_proxy,
@@ -20,15 +20,24 @@ module Excon
20
20
  def host
21
21
  @data[:host]
22
22
  end
23
+ def scheme
24
+ @data[:scheme]
25
+ end
23
26
  def local_address
24
27
  @data[:local_address]
25
28
  end
26
29
  def local_port
27
30
  @data[:local_port]
28
31
  end
32
+ def http_method # can't be named "method"
33
+ @data[:method]
34
+ end
29
35
  def path
30
36
  @data[:path]
31
37
  end
38
+ def query
39
+ @data[:query]
40
+ end
32
41
  def port
33
42
  @data[:port]
34
43
  end
@@ -72,9 +81,13 @@ module Excon
72
81
  :body => String.new,
73
82
  :cookies => [],
74
83
  :host => datum[:host],
84
+ :scheme => datum[:scheme],
85
+ :method => datum[:method],
75
86
  :headers => Excon::Headers.new,
76
87
  :path => datum[:path],
88
+ :query => datum[:query],
77
89
  :port => datum[:port],
90
+ :omit_default_port => datum[:omit_default_port],
78
91
  :status => status,
79
92
  :status_line => line,
80
93
  :reason_phrase => reason_phrase
data/lib/excon/socket.rb CHANGED
@@ -40,12 +40,14 @@ module Excon
40
40
 
41
41
  def_delegators(:@socket, :close)
42
42
 
43
+
43
44
  def initialize(data = {})
44
45
  @data = data
45
46
  @nonblock = data[:nonblock]
46
47
  @port ||= @data[:port] || 80
47
48
  @read_buffer = String.new
48
49
  @eof = false
50
+ @backend_eof = false
49
51
  connect
50
52
  end
51
53
 
@@ -60,19 +62,31 @@ module Excon
60
62
  end
61
63
 
62
64
  def readline
63
- return legacy_readline if RUBY_VERSION.to_f <= 1.8_7
64
- buffer = String.new
65
- buffer << (read_nonblock(1) || raise(EOFError)) while buffer[-1] != "\n"
66
- buffer
67
- end
68
-
69
- def legacy_readline
70
- begin
71
- Timeout.timeout(@data[:read_timeout]) do
72
- @socket.readline
65
+ if @nonblock && RUBY_VERSION.to_f > 1.8_7
66
+ result = String.new
67
+ block = @read_buffer
68
+ @read_buffer = String.new
69
+
70
+ loop do
71
+ idx = block.index("\n")
72
+ if idx.nil?
73
+ result << block
74
+ else
75
+ result << block.slice!(0, idx+1)
76
+ add_to_read_buffer(block)
77
+ break
78
+ end
79
+ block = read_nonblock(@data[:chunk_size]) || raise(EOFError)
80
+ end
81
+ result
82
+ else # nonblock/legacy
83
+ begin
84
+ Timeout.timeout(@data[:read_timeout]) do
85
+ @socket.readline
86
+ end
87
+ rescue Timeout::Error
88
+ raise Excon::Errors::Timeout.new('read timeout reached')
73
89
  end
74
- rescue Timeout::Error
75
- raise Excon::Errors::Timeout.new('read timeout reached')
76
90
  end
77
91
  end
78
92
 
@@ -173,20 +187,27 @@ module Excon
173
187
  end
174
188
  end
175
189
 
190
+ def add_to_read_buffer(str)
191
+ @read_buffer << str
192
+ @eof = false
193
+ end
194
+
176
195
  def read_nonblock(max_length)
177
196
  begin
178
197
  if max_length
179
- until @read_buffer.length >= max_length
198
+ until @backend_eof || @read_buffer.length >= max_length
180
199
  @read_buffer << @socket.read_nonblock(max_length - @read_buffer.length)
181
200
  end
182
201
  else
183
- loop do
202
+ while !@backend_eof
184
203
  @read_buffer << @socket.read_nonblock(@data[:chunk_size])
185
204
  end
186
205
  end
187
206
  rescue OpenSSL::SSL::SSLError => error
188
207
  if error.message == 'read would block'
189
- select_with_timeout(@socket, :read) && retry
208
+ if @read_buffer.empty?
209
+ select_with_timeout(@socket, :read) && retry
210
+ end
190
211
  else
191
212
  raise(error)
192
213
  end
@@ -196,10 +217,10 @@ module Excon
196
217
  select_with_timeout(@socket, :read) && retry
197
218
  end
198
219
  rescue EOFError
199
- @eof = true
220
+ @backend_eof = true
200
221
  end
201
222
 
202
- if max_length
223
+ ret = if max_length
203
224
  if @read_buffer.empty?
204
225
  nil # EOF met at beginning
205
226
  else
@@ -209,6 +230,8 @@ module Excon
209
230
  # read until EOFError, so return everything
210
231
  @read_buffer.slice!(0, @read_buffer.length)
211
232
  end
233
+ @eof = @backend_eof && @read_buffer.empty?
234
+ ret
212
235
  end
213
236
 
214
237
  def read_block(max_length)
@@ -220,9 +243,7 @@ module Excon
220
243
  raise(error)
221
244
  end
222
245
  rescue *READ_RETRY_EXCEPTION_CLASSES
223
- if @read_buffer.empty?
224
- select_with_timeout(@socket, :read) && retry
225
- end
246
+ select_with_timeout(@socket, :read) && retry
226
247
  rescue EOFError
227
248
  @eof = true
228
249
  end
@@ -90,6 +90,14 @@ module Excon
90
90
  else
91
91
  ssl_context.key = OpenSSL::PKey::RSA.new(client_key_data, client_key_pass)
92
92
  end
93
+ if client_chain_data && OpenSSL::X509::Certificate.respond_to?(:load)
94
+ ssl_context.extra_chain_cert = OpenSSL::X509::Certificate.load(client_chain_data)
95
+ elsif client_chain_data
96
+ certs = client_chain_data.scan(/-----BEGIN CERTIFICATE-----(?:.|\n)+?-----END CERTIFICATE-----/)
97
+ ssl_context.extra_chain_cert = certs.map do |cert|
98
+ OpenSSL::X509::Certificate.new(cert)
99
+ end
100
+ end
93
101
  elsif @data.key?(:certificate) && @data.key?(:private_key)
94
102
  ssl_context.cert = OpenSSL::X509::Certificate.new(@data[:certificate])
95
103
  if OpenSSL::PKey.respond_to? :read
@@ -133,7 +141,7 @@ module Excon
133
141
 
134
142
  # Server Name Indication (SNI) RFC 3546
135
143
  if @socket.respond_to?(:hostname=)
136
- @socket.hostname = @data[:host]
144
+ @socket.hostname = @data[:ssl_verify_peer_host] || @data[:host]
137
145
  end
138
146
 
139
147
  begin
@@ -171,6 +179,14 @@ module Excon
171
179
  end
172
180
  end
173
181
 
182
+ def client_chain_data
183
+ @client_chain_data ||= if (ccd = @data[:client_chain_data])
184
+ ccd
185
+ elsif (path = @data[:client_chain])
186
+ File.read path
187
+ end
188
+ end
189
+
174
190
  def connect
175
191
  # backwards compatability for things lacking nonblock
176
192
  @nonblock = HAVE_NONBLOCK && @nonblock
data/lib/excon/version.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module Excon
3
- VERSION = '0.90.0'
3
+ VERSION = '0.99.0'
4
4
  end
data/lib/excon.rb CHANGED
@@ -198,8 +198,13 @@ module Excon
198
198
  headers_match = !stub.has_key?(:headers) || stub[:headers].keys.all? do |key|
199
199
  case value = stub[:headers][key]
200
200
  when Regexp
201
- if (match = value.match(request_params[:headers][key]))
202
- captures[:headers][key] = match.captures
201
+ case request_params[:headers][key]
202
+ when String
203
+ if (match = value.match(request_params[:headers][key]))
204
+ captures[:headers][key] = match.captures
205
+ end
206
+ when Regexp # for unstub on regex params
207
+ match = (value == request_params[:headers][key])
203
208
  end
204
209
  match
205
210
  else
@@ -209,8 +214,13 @@ module Excon
209
214
  non_headers_match = (stub.keys - [:headers]).all? do |key|
210
215
  case value = stub[key]
211
216
  when Regexp
212
- if (match = value.match(request_params[key]))
213
- captures[key] = match.captures
217
+ case request_params[key]
218
+ when String
219
+ if (match = value.match(request_params[key]))
220
+ captures[key] = match.captures
221
+ end
222
+ when Regexp # for unstub on regex params
223
+ match = (value == request_params[key])
214
224
  end
215
225
  match
216
226
  else
metadata CHANGED
@@ -1,16 +1,16 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: excon
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.90.0
4
+ version: 0.99.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - dpiddy (Dan Peterson)
8
8
  - geemus (Wesley Beary)
9
9
  - nextmat (Matt Sanders)
10
- autorequire:
10
+ autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2022-01-12 00:00:00.000000000 Z
13
+ date: 2023-02-03 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rspec
@@ -96,20 +96,6 @@ dependencies:
96
96
  - - ">="
97
97
  - !ruby/object:Gem::Version
98
98
  version: '0'
99
- - !ruby/object:Gem::Dependency
100
- name: rdoc
101
- requirement: !ruby/object:Gem::Requirement
102
- requirements:
103
- - - ">="
104
- - !ruby/object:Gem::Version
105
- version: '0'
106
- type: :development
107
- prerelease: false
108
- version_requirements: !ruby/object:Gem::Requirement
109
- requirements:
110
- - - ">="
111
- - !ruby/object:Gem::Version
112
- version: '0'
113
99
  - !ruby/object:Gem::Dependency
114
100
  name: shindo
115
101
  requirement: !ruby/object:Gem::Requirement
@@ -249,7 +235,7 @@ metadata:
249
235
  documentation_uri: https://github.com/excon/excon/blob/master/README.md
250
236
  source_code_uri: https://github.com/excon/excon
251
237
  wiki_uri: https://github.com/excon/excon/wiki
252
- post_install_message:
238
+ post_install_message:
253
239
  rdoc_options:
254
240
  - "--charset=UTF-8"
255
241
  require_paths:
@@ -265,8 +251,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
265
251
  - !ruby/object:Gem::Version
266
252
  version: '0'
267
253
  requirements: []
268
- rubygems_version: 3.2.15
269
- signing_key:
254
+ rubygems_version: 3.4.1
255
+ signing_key:
270
256
  specification_version: 4
271
257
  summary: speed, persistence, http(s)
272
258
  test_files: []