http.rb 0.18.3 → 0.20.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4bfdcd1317baede9f978ecc2090b816da19fd76531ab759ff685ffc42e237297
4
- data.tar.gz: a4ea5421285e8a5b1b702b33be58de8be2e8984963b238b81679fdbca118eeec
3
+ metadata.gz: f8926fd82491288941f05e4ff3d38c0c000e31f214bd35f55462befa0ece73a3
4
+ data.tar.gz: ad696135489b778872649c052fb1f23eb05e151e7a04c29237510e997dba7d71
5
5
  SHA512:
6
- metadata.gz: 5dbf7b38407e2bcc6932efdee8b36ee4994cdfc420edcb6d2c7f39d9a7c407af2c2b786424d0b33123e4538b7aa094e7ed0ea5597d1afbe392aad913b29e890c
7
- data.tar.gz: ad271ead133a0f244d4f1538ee78aa2f6595c0a0c7be47bbf5d965f8768136f579354a1d4015754e0d60e922e64ade6cbc9043213d63d006c50e140e3c196553
6
+ metadata.gz: eb7f2d8945396de4151166fb3a2fc62a0bb47ac2636a9f157b04c67318f0c9c20d302aba00b8a0db21278fbeed559230b236fea674cb2cf4de3880fe22b09dce
7
+ data.tar.gz: bea5d01ffdab8e4a8fd876ec1b7353776b933d5e1259ea8c57aa4cbf4a8a6b3ba35950fa7bdc907b18bff84e5e7cbc972abbd1cc71398f37934834f176e9d86c
data/CHANGELOG CHANGED
@@ -1,5 +1,26 @@
1
1
  # CHANGELOG
2
2
 
3
+ # 20260522
4
+ # 0.20.0: Add opt-in retry logic with exponential backoff.
5
+ 1. + lib/HTTP/RETRY.rb: Retry helpers (with_retries, backoff_delay, retry_after) and constants (HTTP::RETRY::EXCEPTIONS, STATUS_CODES, VERBS).
6
+ 2. ~ HTTP.request: Retry on transient network exceptions and retry-worthy HTTP status codes (429, 502, 503, 504) when enabled. Exponential backoff with jitter. Honours Retry-After when present. Disabled by default; opt in via the retries option. Configurable via new options: retries (default 0), retry_delay (default 1.0), retry_status_codes, retry_exceptions, retry_verbs. Only idempotent verbs (get, head, options, put, delete, trace) retry by default; opt in to POST/PATCH retries via retry_verbs.
7
+ 3. + spec/HTTP/RETRY_spec.rb: Specs for retry behaviour and the helpers.
8
+ 4. ~ README.md: + Retries section.
9
+ 5. ~ TODO: Annotate "Add retry logic to HTTP.request" as (Done as of 0.20.0); also annotate three previously-completed items as (Done as of 0.18.2), (Done as of 0.18.3), and (Done as of 0.19.0).
10
+ 6. ~ HTTP::VERSION: /0.19.0/0.20.0/
11
+ 7. ~ CHANGELOG: + 0.20.0 entry
12
+
13
+ # 20260522
14
+ # 0.19.0: Change default verify_mode to VERIFY_PEER.
15
+ 1. ~ HTTP.request: Default verify_mode changed from OpenSSL::SSL::VERIFY_NONE to OpenSSL::SSL::VERIFY_PEER. Callers needing the old behaviour can pass verify_mode: OpenSSL::SSL::VERIFY_NONE explicitly through the options hash.
16
+ 2. ~ spec/HTTP/get_spec.rb: + specs for default verify_mode and explicit VERIFY_NONE override; /verify_mode: 0/verify_mode: OpenSSL::SSL::VERIFY_PEER/ in redirect specs.
17
+ 3. ~ spec/HTTP/post_spec.rb: /verify_mode: 0/verify_mode: OpenSSL::SSL::VERIFY_PEER/ in redirect specs.
18
+ 4. ~ spec/HTTP/put_spec.rb: /verify_mode: 0/verify_mode: OpenSSL::SSL::VERIFY_PEER/ in redirect specs.
19
+ 5. ~ spec/HTTP/delete_spec.rb: /verify_mode: 0/verify_mode: OpenSSL::SSL::VERIFY_PEER/ in redirect specs.
20
+ 6. ~ README.md: Note the new verify_mode default and how to opt back into VERIFY_NONE.
21
+ 7. ~ HTTP::VERSION: /0.18.3/0.19.0/
22
+ 8. ~ CHANGELOG: + 0.19.0 entry
23
+
3
24
  # 20260521
4
25
  # 0.18.3: Fix verb preservation on 307/308 redirects.
5
26
  1. ~ HTTP.request: Use original verb when following 307 or 308 redirects, per RFC 7231 §6.4.7 and RFC 7538. 301/302/303 keep legacy GET-on-redirect behaviour.
data/README.md CHANGED
@@ -84,6 +84,34 @@ HTTP.get('http://example.com', {}, {}, {no_redirect: true})
84
84
  # => #<Net::HTTPResponse @code=3xx>
85
85
  ```
86
86
 
87
+ ### Retries
88
+
89
+ Retries are disabled by default. Enable them by passing `retries:` in the options hash.
90
+
91
+ ```ruby
92
+ HTTP.get('http://example.com', {}, {}, {retries: 3})
93
+ ```
94
+
95
+ When enabled, transient network exceptions and retry-worthy HTTP status codes (429, 502, 503, 504) are retried with exponential backoff and jitter. If the response carries a `Retry-After` header, it is honoured in place of the calculated delay.
96
+
97
+ Only idempotent verbs (`get`, `head`, `options`, `put`, `delete`, `trace`) are retried by default. POST and PATCH are not — retrying a non-idempotent write can create duplicate resources against APIs that don't deduplicate. Opt in per-call via `retry_verbs:`.
98
+
99
+ ```ruby
100
+ HTTP.post('http://example.com', {a: 1}, {}, {retries: 3, retry_verbs: %i{get post}})
101
+ ```
102
+
103
+ Configurable options:
104
+
105
+ ```ruby
106
+ options = {
107
+ retries: 3, # max retry attempts; 0 disables
108
+ retry_delay: 1.0, # base delay (seconds) for exponential backoff
109
+ retry_status_codes: [429, 502, 503, 504], # HTTP status codes to retry
110
+ retry_exceptions: HTTP::RETRY::EXCEPTIONS, # exceptions to retry
111
+ retry_verbs: HTTP::RETRY::VERBS # verbs that retry by default
112
+ }
113
+ ```
114
+
87
115
  ### Response status predicate methods
88
116
 
89
117
  ```ruby
@@ -241,6 +269,10 @@ verify_mode
241
269
  # SSL/TLS session.
242
270
  #
243
271
  # OpenSSL::SSL::VERIFY_NONE or OpenSSL::SSL::VERIFY_PEER are acceptable.
272
+ #
273
+ # Defaults to OpenSSL::SSL::VERIFY_PEER as of 0.19.0. To opt back into the
274
+ # previous behaviour, pass verify_mode: OpenSSL::SSL::VERIFY_NONE through
275
+ # the options hash.
244
276
  ```
245
277
 
246
278
  ## Contributing
data/lib/HTTP/RETRY.rb ADDED
@@ -0,0 +1,76 @@
1
+ # HTTP/RETRY.rb
2
+ # HTTP::RETRY (retry helpers)
3
+
4
+ require 'net/http'
5
+ require 'socket'
6
+ require 'time'
7
+
8
+ module HTTP
9
+ module RETRY
10
+ EXCEPTIONS = [
11
+ Errno::ECONNREFUSED,
12
+ Errno::ECONNRESET,
13
+ Errno::ETIMEDOUT,
14
+ Errno::EHOSTUNREACH,
15
+ Errno::ENETUNREACH,
16
+ Net::OpenTimeout,
17
+ Net::ReadTimeout,
18
+ SocketError,
19
+ EOFError
20
+ ].freeze
21
+ STATUS_CODES = [429, 502, 503, 504].freeze
22
+ VERBS = %i{get head options put delete trace}.freeze
23
+
24
+ def self.sleep(seconds)
25
+ Kernel.sleep(seconds)
26
+ end
27
+ end
28
+
29
+ def retry_config(options)
30
+ {
31
+ retries: options.delete(:retries) || 0,
32
+ delay: options.delete(:retry_delay) || 1.0,
33
+ status_codes: options.delete(:retry_status_codes) || RETRY::STATUS_CODES,
34
+ exceptions: options.delete(:retry_exceptions) || RETRY::EXCEPTIONS,
35
+ verbs: options.delete(:retry_verbs) || RETRY::VERBS
36
+ }
37
+ end
38
+ module_function :retry_config
39
+
40
+ def with_retries(http, request_object, config)
41
+ attempt = 0
42
+ loop do
43
+ begin
44
+ response = http.request(request_object)
45
+ if config[:status_codes].include?(response.code.to_i) && attempt < config[:retries]
46
+ attempt += 1
47
+ RETRY.sleep(retry_after(response) || backoff_delay(config[:delay], attempt))
48
+ next
49
+ end
50
+ return response
51
+ rescue *config[:exceptions]
52
+ raise unless attempt < config[:retries]
53
+ attempt += 1
54
+ RETRY.sleep(backoff_delay(config[:delay], attempt))
55
+ end
56
+ end
57
+ end
58
+ module_function :with_retries
59
+
60
+ def backoff_delay(base, attempt)
61
+ base * (2 ** (attempt - 1)) * (1 + (rand - 0.5) * 0.4)
62
+ end
63
+ module_function :backoff_delay
64
+
65
+ def retry_after(response)
66
+ header = response['Retry-After']
67
+ return nil unless header
68
+ if header =~ /\A\d+\z/
69
+ header.to_i
70
+ else
71
+ # Malformed HTTP-date — fall through to caller's backoff.
72
+ Time.httpdate(header) - Time.now rescue nil
73
+ end
74
+ end
75
+ module_function :retry_after
76
+ end
data/lib/HTTP/VERSION.rb CHANGED
@@ -2,5 +2,5 @@
2
2
  # HTTP::VERSION
3
3
 
4
4
  module HTTP
5
- VERSION = '0.18.3'
5
+ VERSION = '0.20.0'
6
6
  end
data/lib/HTTP/request.rb CHANGED
@@ -9,18 +9,27 @@ require_relative '../Net/HTTP/set_options'
9
9
  require_relative '../Net/HTTPRequest/set_headers'
10
10
  require_relative '../Net/HTTPResponse/StatusPredicates'
11
11
  require_relative '../URI/Generic/use_sslQ'
12
+ require_relative './RETRY'
12
13
 
13
14
  module HTTP
14
15
  def request(uri, request_object, headers = {}, options = {}, &block)
15
16
  uri = uri.is_a?(URI) ? uri : URI.parse(uri)
16
17
  http = Net::HTTP.new(uri.host, uri.port)
17
18
  no_redirect = options.delete(:no_redirect)
19
+ config = retry_config(options)
18
20
  options[:use_ssl] ||= uri.use_ssl?
19
- options[:verify_mode] ||= OpenSSL::SSL::VERIFY_NONE
21
+ options[:verify_mode] ||= OpenSSL::SSL::VERIFY_PEER
20
22
  http.options = options
21
23
  request_object.headers = headers
22
24
  request_object.basic_auth(uri.user, uri.password) if uri.user
23
- response = http.request(request_object)
25
+ verb = request_object.method.downcase.to_sym
26
+ response = (
27
+ if config[:retries] > 0 && config[:verbs].include?(verb)
28
+ with_retries(http, request_object, config)
29
+ else
30
+ http.request(request_object)
31
+ end
32
+ )
24
33
  if response.code =~ /^3/
25
34
  if block_given? && no_redirect
26
35
  yield response
@@ -29,7 +38,6 @@ module HTTP
29
38
  end
30
39
  redirect_uri = uri.merge(response.header['location'])
31
40
  if response.code =~ /^30[78]$/
32
- verb = request_object.method.downcase.to_sym
33
41
  data = VERBS::WITH_BODY.include?(verb) ? request_object.body : {}
34
42
  response = send(verb, redirect_uri.to_s, data, headers, options, &block)
35
43
  else
@@ -0,0 +1,251 @@
1
+ # spec/HTTP/RETRY_spec.rb
2
+
3
+ require_relative '../spec_helper'
4
+ require 'http'
5
+
6
+ describe "retry behaviour" do
7
+ let(:uri){'http://example.com/path'}
8
+
9
+ before do
10
+ allow(HTTP::RETRY).to receive(:sleep)
11
+ end
12
+
13
+ describe "defaults" do
14
+ it "does not retry by default" do
15
+ stub_request(:get, uri).to_return(status: 503)
16
+ response = HTTP.get(uri)
17
+ expect(response.code.to_i).to eq(503)
18
+ expect(WebMock).to have_requested(:get, uri).times(1)
19
+ end
20
+
21
+ it "does not retry on a transient exception by default" do
22
+ stub_request(:get, uri).to_raise(Errno::ECONNRESET)
23
+ expect{HTTP.get(uri)}.to raise_error(Errno::ECONNRESET)
24
+ expect(WebMock).to have_requested(:get, uri).times(1)
25
+ end
26
+ end
27
+
28
+ describe "retry on transient exception" do
29
+ it "retries and succeeds when the failure is transient" do
30
+ stub_request(:get, uri).
31
+ to_raise(Errno::ECONNRESET).then.
32
+ to_raise(Errno::ECONNRESET).then.
33
+ to_return(status: 200, body: '')
34
+ response = HTTP.get(uri, {}, {}, {retries: 3})
35
+ expect(response.success?).to eq(true)
36
+ expect(WebMock).to have_requested(:get, uri).times(3)
37
+ end
38
+
39
+ it "re-raises the exception after retries are exhausted" do
40
+ stub_request(:get, uri).to_raise(Errno::ECONNRESET)
41
+ expect{HTTP.get(uri, {}, {}, {retries: 2})}.to raise_error(Errno::ECONNRESET)
42
+ expect(WebMock).to have_requested(:get, uri).times(3)
43
+ end
44
+
45
+ it "retries on SocketError (DNS failure)" do
46
+ stub_request(:get, uri).
47
+ to_raise(SocketError).then.
48
+ to_return(status: 200, body: '')
49
+ response = HTTP.get(uri, {}, {}, {retries: 3})
50
+ expect(response.success?).to eq(true)
51
+ expect(WebMock).to have_requested(:get, uri).times(2)
52
+ end
53
+
54
+ it "does not retry on a non-listed exception" do
55
+ stub_request(:get, uri).to_raise(OpenSSL::SSL::SSLError)
56
+ expect{HTTP.get(uri, {}, {}, {retries: 3})}.to raise_error(OpenSSL::SSL::SSLError)
57
+ expect(WebMock).to have_requested(:get, uri).times(1)
58
+ end
59
+ end
60
+
61
+ describe "retry on status code" do
62
+ it "retries on 503 then succeeds" do
63
+ stub_request(:get, uri).
64
+ to_return({status: 503}, {status: 503}, {status: 200, body: ''})
65
+ response = HTTP.get(uri, {}, {}, {retries: 3})
66
+ expect(response.success?).to eq(true)
67
+ expect(WebMock).to have_requested(:get, uri).times(3)
68
+ end
69
+
70
+ it "retries on 502" do
71
+ stub_request(:get, uri).to_return({status: 502}, {status: 200, body: ''})
72
+ response = HTTP.get(uri, {}, {}, {retries: 3})
73
+ expect(response.success?).to eq(true)
74
+ expect(WebMock).to have_requested(:get, uri).times(2)
75
+ end
76
+
77
+ it "retries on 504" do
78
+ stub_request(:get, uri).to_return({status: 504}, {status: 200, body: ''})
79
+ response = HTTP.get(uri, {}, {}, {retries: 3})
80
+ expect(response.success?).to eq(true)
81
+ expect(WebMock).to have_requested(:get, uri).times(2)
82
+ end
83
+
84
+ it "does not retry on 500 by default" do
85
+ stub_request(:get, uri).to_return(status: 500)
86
+ response = HTTP.get(uri, {}, {}, {retries: 3})
87
+ expect(response.code.to_i).to eq(500)
88
+ expect(WebMock).to have_requested(:get, uri).times(1)
89
+ end
90
+
91
+ it "does not retry on 404" do
92
+ stub_request(:get, uri).to_return(status: 404)
93
+ response = HTTP.get(uri, {}, {}, {retries: 3})
94
+ expect(response.code.to_i).to eq(404)
95
+ expect(WebMock).to have_requested(:get, uri).times(1)
96
+ end
97
+
98
+ it "returns the last response when retries are exhausted" do
99
+ stub_request(:get, uri).to_return(status: 503)
100
+ response = HTTP.get(uri, {}, {}, {retries: 2})
101
+ expect(response.code.to_i).to eq(503)
102
+ expect(WebMock).to have_requested(:get, uri).times(3)
103
+ end
104
+ end
105
+
106
+ describe "Retry-After header" do
107
+ it "honours integer Retry-After on 429" do
108
+ stub_request(:get, uri).
109
+ to_return({status: 429, headers: {'Retry-After' => '2'}}, {status: 200, body: ''})
110
+ expect(HTTP::RETRY).to receive(:sleep).with(2)
111
+ response = HTTP.get(uri, {}, {}, {retries: 3})
112
+ expect(response.success?).to eq(true)
113
+ end
114
+
115
+ it "honours integer Retry-After on 503" do
116
+ stub_request(:get, uri).
117
+ to_return({status: 503, headers: {'Retry-After' => '5'}}, {status: 200, body: ''})
118
+ expect(HTTP::RETRY).to receive(:sleep).with(5)
119
+ response = HTTP.get(uri, {}, {}, {retries: 3})
120
+ expect(response.success?).to eq(true)
121
+ end
122
+ end
123
+
124
+ describe "configuration" do
125
+ it "treats retries: 0 as no retries" do
126
+ stub_request(:get, uri).to_return(status: 503)
127
+ response = HTTP.get(uri, {}, {}, {retries: 0})
128
+ expect(response.code.to_i).to eq(503)
129
+ expect(WebMock).to have_requested(:get, uri).times(1)
130
+ end
131
+
132
+ it "respects a custom retry_status_codes list" do
133
+ stub_request(:get, uri).to_return({status: 500}, {status: 200, body: ''})
134
+ response = HTTP.get(uri, {}, {}, {retries: 3, retry_status_codes: [500]})
135
+ expect(response.success?).to eq(true)
136
+ expect(WebMock).to have_requested(:get, uri).times(2)
137
+ end
138
+
139
+ it "respects a custom retry_exceptions list" do
140
+ stub_request(:get, uri).
141
+ to_raise(OpenSSL::SSL::SSLError).then.
142
+ to_return(status: 200, body: '')
143
+ response = HTTP.get(uri, {}, {}, {retries: 3, retry_exceptions: [OpenSSL::SSL::SSLError]})
144
+ expect(response.success?).to eq(true)
145
+ expect(WebMock).to have_requested(:get, uri).times(2)
146
+ end
147
+
148
+ it "does not pass retry options through to Net::HTTP" do
149
+ stub_request(:get, uri).to_return(status: 200, body: '')
150
+ net_http_object = Net::HTTP.new(URI.parse(uri).host, URI.parse(uri).port)
151
+ allow(Net::HTTP).to receive(:new).and_return(net_http_object)
152
+ expect(net_http_object).to receive(:options=) do |opts|
153
+ expect(opts).not_to include(:retries, :retry_delay, :retry_status_codes, :retry_exceptions, :retry_verbs)
154
+ end
155
+ HTTP.get(uri, {}, {}, {
156
+ retries: 3,
157
+ retry_delay: 0.1,
158
+ retry_status_codes: [500],
159
+ retry_exceptions: [Errno::ECONNRESET],
160
+ retry_verbs: %i{get}
161
+ })
162
+ end
163
+ end
164
+
165
+ describe "backoff timing" do
166
+ it "increases the delay between successive retries" do
167
+ delays = []
168
+ allow(HTTP::RETRY).to receive(:sleep){|d| delays << d}
169
+ stub_request(:get, uri).to_return(status: 503)
170
+ HTTP.get(uri, {}, {}, {retries: 3, retry_delay: 1.0})
171
+ expect(delays.length).to eq(3)
172
+ expect(delays[1]).to be > delays[0] * 0.8
173
+ expect(delays[2]).to be > delays[1] * 0.8
174
+ end
175
+ end
176
+
177
+ describe "verb-based retry default" do
178
+ it "does not retry POST by default even when retries are enabled" do
179
+ stub_request(:post, uri).to_return(status: 503)
180
+ HTTP.post(uri, {}, {}, {retries: 3})
181
+ expect(WebMock).to have_requested(:post, uri).times(1)
182
+ end
183
+
184
+ it "does not retry PATCH by default" do
185
+ stub_request(:patch, uri).to_return(status: 503)
186
+ HTTP.patch(uri, {}, {}, {retries: 3})
187
+ expect(WebMock).to have_requested(:patch, uri).times(1)
188
+ end
189
+
190
+ it "retries PUT by default (idempotent)" do
191
+ stub_request(:put, uri).to_return({status: 503}, {status: 200, body: ''})
192
+ response = HTTP.put(uri, {}, {}, {retries: 3})
193
+ expect(response.success?).to eq(true)
194
+ expect(WebMock).to have_requested(:put, uri).times(2)
195
+ end
196
+
197
+ it "retries DELETE by default (idempotent)" do
198
+ stub_request(:delete, uri).to_return({status: 503}, {status: 200, body: ''})
199
+ response = HTTP.delete(uri, {}, {}, {retries: 3})
200
+ expect(response.success?).to eq(true)
201
+ expect(WebMock).to have_requested(:delete, uri).times(2)
202
+ end
203
+
204
+ it "retries POST when opted in via retry_verbs" do
205
+ stub_request(:post, uri).to_return({status: 503}, {status: 200, body: ''})
206
+ response = HTTP.post(uri, {}, {}, {retries: 3, retry_verbs: %i{get post}})
207
+ expect(response.success?).to eq(true)
208
+ expect(WebMock).to have_requested(:post, uri).times(2)
209
+ end
210
+ end
211
+ end
212
+
213
+ describe HTTP, ".retry_after" do
214
+ it "returns integer seconds for a delta-seconds Retry-After header" do
215
+ response = instance_double(Net::HTTPResponse)
216
+ allow(response).to receive(:[]).with('Retry-After').and_return('5')
217
+ expect(HTTP.retry_after(response)).to eq(5)
218
+ end
219
+
220
+ it "parses an HTTP-date Retry-After header" do
221
+ base = Time.utc(2026, 5, 22, 12, 0, 0)
222
+ retry_at_header = (base + 5).httpdate
223
+ response = instance_double(Net::HTTPResponse)
224
+ allow(response).to receive(:[]).with('Retry-After').and_return(retry_at_header)
225
+ allow(Time).to receive(:now).and_return(base)
226
+ expect(HTTP.retry_after(response)).to be_within(0.001).of(5.0)
227
+ end
228
+
229
+ it "returns nil when Retry-After is absent" do
230
+ response = instance_double(Net::HTTPResponse)
231
+ allow(response).to receive(:[]).with('Retry-After').and_return(nil)
232
+ expect(HTTP.retry_after(response)).to be_nil
233
+ end
234
+
235
+ it "returns nil when Retry-After is malformed" do
236
+ response = instance_double(Net::HTTPResponse)
237
+ allow(response).to receive(:[]).with('Retry-After').and_return('not a date')
238
+ expect(HTTP.retry_after(response)).to be_nil
239
+ end
240
+ end
241
+
242
+ describe HTTP, ".backoff_delay" do
243
+ it "grows exponentially with attempt number" do
244
+ base = 1.0
245
+ delays = (1..4).map{|attempt| HTTP.backoff_delay(base, attempt)}
246
+ expect(delays[0]).to be_within(0.2).of(1.0)
247
+ expect(delays[1]).to be_within(0.4).of(2.0)
248
+ expect(delays[2]).to be_within(0.8).of(4.0)
249
+ expect(delays[3]).to be_within(1.6).of(8.0)
250
+ end
251
+ end
@@ -148,7 +148,7 @@ describe ".delete" do
148
148
 
149
149
  it "does a redirect" do
150
150
  expect(HTTP).to receive(:delete).once.with(request_uri).and_call_original
151
- expect(HTTP).to receive(:get).once.with(redirect_uri, {}, {}, {use_ssl: false, verify_mode: 0}).and_call_original
151
+ expect(HTTP).to receive(:get).once.with(redirect_uri, {}, {}, {use_ssl: false, verify_mode: OpenSSL::SSL::VERIFY_PEER}).and_call_original
152
152
  response = HTTP.delete(request_uri)
153
153
  expect(response.success?).to eq(true)
154
154
  end
@@ -163,7 +163,7 @@ describe ".delete" do
163
163
 
164
164
  it "does a redirect" do
165
165
  expect(HTTP).to receive(:delete).once.with(request_uri).and_call_original
166
- expect(HTTP).to receive(:get).once.with(redirect_uri, {}, {}, {use_ssl: false, verify_mode: 0}).and_call_original
166
+ expect(HTTP).to receive(:get).once.with(redirect_uri, {}, {}, {use_ssl: false, verify_mode: OpenSSL::SSL::VERIFY_PEER}).and_call_original
167
167
  response = HTTP.delete(request_uri)
168
168
  expect(response.success?).to eq(true)
169
169
  end
@@ -190,7 +190,7 @@ describe ".delete" do
190
190
 
191
191
  it "does a redirect" do
192
192
  expect(HTTP).to receive(:delete).once.with(request_uri).and_call_original
193
- expect(HTTP).to receive(:get).once.with(redirect_uri, {}, {}, {use_ssl: false, verify_mode: 0}).and_call_original
193
+ expect(HTTP).to receive(:get).once.with(redirect_uri, {}, {}, {use_ssl: false, verify_mode: OpenSSL::SSL::VERIFY_PEER}).and_call_original
194
194
  response = HTTP.delete(request_uri)
195
195
  expect(response.success?).to eq(true)
196
196
  end
@@ -205,7 +205,7 @@ describe ".delete" do
205
205
 
206
206
  it "does a redirect" do
207
207
  expect(HTTP).to receive(:delete).once.with(request_uri).and_call_original
208
- expect(HTTP).to receive(:get).once.with(redirect_uri, {}, {}, {use_ssl: false, verify_mode: 0}).and_call_original
208
+ expect(HTTP).to receive(:get).once.with(redirect_uri, {}, {}, {use_ssl: false, verify_mode: OpenSSL::SSL::VERIFY_PEER}).and_call_original
209
209
  response = HTTP.delete(request_uri)
210
210
  expect(response.success?).to eq(true)
211
211
  end
@@ -227,7 +227,7 @@ describe ".delete" do
227
227
 
228
228
  it "preserves the verb" do
229
229
  expect(HTTP).to receive(:delete).with(request_uri).and_call_original.ordered
230
- expect(HTTP).to receive(:delete).with(redirect_uri, {}, {}, {use_ssl: false, verify_mode: 0}).and_call_original.ordered
230
+ expect(HTTP).to receive(:delete).with(redirect_uri, {}, {}, {use_ssl: false, verify_mode: OpenSSL::SSL::VERIFY_PEER}).and_call_original.ordered
231
231
  response = HTTP.delete(request_uri)
232
232
  expect(response.success?).to eq(true)
233
233
  end
@@ -115,6 +115,32 @@ describe ".get" do
115
115
  end
116
116
  end
117
117
 
118
+ context "with default verify_mode" do
119
+ let(:uri){'http://example.com/path'}
120
+ let(:parsed_uri){URI.parse(uri)}
121
+ let(:net_http_object){Net::HTTP.new(parsed_uri.host, parsed_uri.port)}
122
+
123
+ before do
124
+ stub_request(:get, uri).
125
+ with(headers: {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'User-Agent'=>'Ruby'}).
126
+ to_return(status: 200, body: '', headers: {})
127
+ end
128
+
129
+ it "defaults verify_mode to OpenSSL::SSL::VERIFY_PEER" do
130
+ allow(Net::HTTP).to receive(:new).with(parsed_uri.host, parsed_uri.port).and_return(net_http_object)
131
+ response = HTTP.get(uri)
132
+ expect(net_http_object.verify_mode).to eq(OpenSSL::SSL::VERIFY_PEER)
133
+ expect(response.success?).to eq(true)
134
+ end
135
+
136
+ it "allows opting back into VERIFY_NONE via options" do
137
+ allow(Net::HTTP).to receive(:new).with(parsed_uri.host, parsed_uri.port).and_return(net_http_object)
138
+ response = HTTP.get(uri, {}, {}, {verify_mode: OpenSSL::SSL::VERIFY_NONE})
139
+ expect(net_http_object.verify_mode).to eq(OpenSSL::SSL::VERIFY_NONE)
140
+ expect(response.success?).to eq(true)
141
+ end
142
+ end
143
+
118
144
  context "with block supplied" do
119
145
  let(:uri){'http://example.com/path'}
120
146
 
@@ -148,7 +174,7 @@ describe ".get" do
148
174
 
149
175
  it "does a redirect" do
150
176
  expect(HTTP).to receive(:get).once.with(request_uri).and_call_original
151
- expect(HTTP).to receive(:get).once.with(redirect_uri, {}, {}, {use_ssl: false, verify_mode: 0}).and_call_original
177
+ expect(HTTP).to receive(:get).once.with(redirect_uri, {}, {}, {use_ssl: false, verify_mode: OpenSSL::SSL::VERIFY_PEER}).and_call_original
152
178
  response = HTTP.get(request_uri)
153
179
  expect(response.success?).to eq(true)
154
180
  end
@@ -163,7 +189,7 @@ describe ".get" do
163
189
 
164
190
  it "does a redirect" do
165
191
  expect(HTTP).to receive(:get).once.with(request_uri).and_call_original
166
- expect(HTTP).to receive(:get).once.with(redirect_uri, {}, {}, {use_ssl: false, verify_mode: 0}).and_call_original
192
+ expect(HTTP).to receive(:get).once.with(redirect_uri, {}, {}, {use_ssl: false, verify_mode: OpenSSL::SSL::VERIFY_PEER}).and_call_original
167
193
  response = HTTP.get(request_uri)
168
194
  expect(response.success?).to eq(true)
169
195
  end
@@ -190,7 +216,7 @@ describe ".get" do
190
216
 
191
217
  it "does a redirect" do
192
218
  expect(HTTP).to receive(:get).once.with(request_uri).and_call_original
193
- expect(HTTP).to receive(:get).once.with(redirect_uri, {}, {}, {use_ssl: false, verify_mode: 0}).and_call_original
219
+ expect(HTTP).to receive(:get).once.with(redirect_uri, {}, {}, {use_ssl: false, verify_mode: OpenSSL::SSL::VERIFY_PEER}).and_call_original
194
220
  response = HTTP.get(request_uri)
195
221
  expect(response.success?).to eq(true)
196
222
  end
@@ -205,7 +231,7 @@ describe ".get" do
205
231
 
206
232
  it "does a redirect" do
207
233
  expect(HTTP).to receive(:get).once.with(request_uri).and_call_original
208
- expect(HTTP).to receive(:get).once.with(redirect_uri, {}, {}, {use_ssl: false, verify_mode: 0}).and_call_original
234
+ expect(HTTP).to receive(:get).once.with(redirect_uri, {}, {}, {use_ssl: false, verify_mode: OpenSSL::SSL::VERIFY_PEER}).and_call_original
209
235
  response = HTTP.get(request_uri)
210
236
  expect(response.success?).to eq(true)
211
237
  end
@@ -228,7 +254,7 @@ describe ".get" do
228
254
 
229
255
  it "preserves the HTTPS scheme on a relative redirect" do
230
256
  expect(HTTP).to receive(:get).once.with(request_uri).and_call_original
231
- expect(HTTP).to receive(:get).once.with(redirect_uri, {}, {}, {use_ssl: true, verify_mode: 0}).and_call_original
257
+ expect(HTTP).to receive(:get).once.with(redirect_uri, {}, {}, {use_ssl: true, verify_mode: OpenSSL::SSL::VERIFY_PEER}).and_call_original
232
258
  response = HTTP.get(request_uri)
233
259
  expect(response.success?).to eq(true)
234
260
  end
@@ -286,7 +286,7 @@ describe ".post" do
286
286
 
287
287
  it "does a redirect" do
288
288
  expect(HTTP).to receive(:post).once.with(request_uri).and_call_original
289
- expect(HTTP).to receive(:get).once.with(redirect_uri, {}, {}, {use_ssl: false, verify_mode: 0}).and_call_original
289
+ expect(HTTP).to receive(:get).once.with(redirect_uri, {}, {}, {use_ssl: false, verify_mode: OpenSSL::SSL::VERIFY_PEER}).and_call_original
290
290
  response = HTTP.post(request_uri)
291
291
  expect(response.success?).to eq(true)
292
292
  end
@@ -301,7 +301,7 @@ describe ".post" do
301
301
 
302
302
  it "does a redirect" do
303
303
  expect(HTTP).to receive(:post).once.with(request_uri).and_call_original
304
- expect(HTTP).to receive(:get).once.with(redirect_uri, {}, {}, {use_ssl: false, verify_mode: 0}).and_call_original
304
+ expect(HTTP).to receive(:get).once.with(redirect_uri, {}, {}, {use_ssl: false, verify_mode: OpenSSL::SSL::VERIFY_PEER}).and_call_original
305
305
  response = HTTP.post(request_uri)
306
306
  expect(response.success?).to eq(true)
307
307
  end
@@ -328,7 +328,7 @@ describe ".post" do
328
328
 
329
329
  it "does a redirect" do
330
330
  expect(HTTP).to receive(:post).once.with(request_uri).and_call_original
331
- expect(HTTP).to receive(:get).once.with(redirect_uri, {}, {}, {use_ssl: false, verify_mode: 0}).and_call_original
331
+ expect(HTTP).to receive(:get).once.with(redirect_uri, {}, {}, {use_ssl: false, verify_mode: OpenSSL::SSL::VERIFY_PEER}).and_call_original
332
332
  response = HTTP.post(request_uri)
333
333
  expect(response.success?).to eq(true)
334
334
  end
@@ -343,7 +343,7 @@ describe ".post" do
343
343
 
344
344
  it "does a redirect" do
345
345
  expect(HTTP).to receive(:post).once.with(request_uri).and_call_original
346
- expect(HTTP).to receive(:get).once.with(redirect_uri, {}, {}, {use_ssl: false, verify_mode: 0}).and_call_original
346
+ expect(HTTP).to receive(:get).once.with(redirect_uri, {}, {}, {use_ssl: false, verify_mode: OpenSSL::SSL::VERIFY_PEER}).and_call_original
347
347
  response = HTTP.post(request_uri)
348
348
  expect(response.success?).to eq(true)
349
349
  end
@@ -369,7 +369,7 @@ describe ".post" do
369
369
 
370
370
  it "preserves the verb" do
371
371
  expect(HTTP).to receive(:post).with(request_uri).and_call_original.ordered
372
- expect(HTTP).to receive(:post).with(redirect_uri, '', {}, {use_ssl: false, verify_mode: 0}).and_call_original.ordered
372
+ expect(HTTP).to receive(:post).with(redirect_uri, '', {}, {use_ssl: false, verify_mode: OpenSSL::SSL::VERIFY_PEER}).and_call_original.ordered
373
373
  response = HTTP.post(request_uri)
374
374
  expect(response.success?).to eq(true)
375
375
  end
@@ -384,7 +384,7 @@ describe ".post" do
384
384
 
385
385
  it "preserves the verb" do
386
386
  expect(HTTP).to receive(:post).with(request_uri).and_call_original.ordered
387
- expect(HTTP).to receive(:post).with(redirect_uri, '', {}, {use_ssl: false, verify_mode: 0}).and_call_original.ordered
387
+ expect(HTTP).to receive(:post).with(redirect_uri, '', {}, {use_ssl: false, verify_mode: OpenSSL::SSL::VERIFY_PEER}).and_call_original.ordered
388
388
  response = HTTP.post(request_uri)
389
389
  expect(response.success?).to eq(true)
390
390
  end
@@ -286,7 +286,7 @@ describe ".put" do
286
286
 
287
287
  it "does a redirect" do
288
288
  expect(HTTP).to receive(:put).once.with(request_uri).and_call_original
289
- expect(HTTP).to receive(:get).once.with(redirect_uri, {}, {}, {use_ssl: false, verify_mode: 0}).and_call_original
289
+ expect(HTTP).to receive(:get).once.with(redirect_uri, {}, {}, {use_ssl: false, verify_mode: OpenSSL::SSL::VERIFY_PEER}).and_call_original
290
290
  response = HTTP.put(request_uri)
291
291
  expect(response.success?).to eq(true)
292
292
  end
@@ -301,7 +301,7 @@ describe ".put" do
301
301
 
302
302
  it "does a redirect" do
303
303
  expect(HTTP).to receive(:put).once.with(request_uri).and_call_original
304
- expect(HTTP).to receive(:get).once.with(redirect_uri, {}, {}, {use_ssl: false, verify_mode: 0}).and_call_original
304
+ expect(HTTP).to receive(:get).once.with(redirect_uri, {}, {}, {use_ssl: false, verify_mode: OpenSSL::SSL::VERIFY_PEER}).and_call_original
305
305
  response = HTTP.put(request_uri)
306
306
  expect(response.success?).to eq(true)
307
307
  end
@@ -328,7 +328,7 @@ describe ".put" do
328
328
 
329
329
  it "does a redirect" do
330
330
  expect(HTTP).to receive(:put).once.with(request_uri).and_call_original
331
- expect(HTTP).to receive(:get).once.with(redirect_uri, {}, {}, {use_ssl: false, verify_mode: 0}).and_call_original
331
+ expect(HTTP).to receive(:get).once.with(redirect_uri, {}, {}, {use_ssl: false, verify_mode: OpenSSL::SSL::VERIFY_PEER}).and_call_original
332
332
  response = HTTP.put(request_uri)
333
333
  expect(response.success?).to eq(true)
334
334
  end
@@ -343,7 +343,7 @@ describe ".put" do
343
343
 
344
344
  it "does a redirect" do
345
345
  expect(HTTP).to receive(:put).once.with(request_uri).and_call_original
346
- expect(HTTP).to receive(:get).once.with(redirect_uri, {}, {}, {use_ssl: false, verify_mode: 0}).and_call_original
346
+ expect(HTTP).to receive(:get).once.with(redirect_uri, {}, {}, {use_ssl: false, verify_mode: OpenSSL::SSL::VERIFY_PEER}).and_call_original
347
347
  response = HTTP.put(request_uri)
348
348
  expect(response.success?).to eq(true)
349
349
  end
@@ -365,7 +365,7 @@ describe ".put" do
365
365
 
366
366
  it "preserves the verb" do
367
367
  expect(HTTP).to receive(:put).with(request_uri).and_call_original.ordered
368
- expect(HTTP).to receive(:put).with(redirect_uri, '', {}, {use_ssl: false, verify_mode: 0}).and_call_original.ordered
368
+ expect(HTTP).to receive(:put).with(redirect_uri, '', {}, {use_ssl: false, verify_mode: OpenSSL::SSL::VERIFY_PEER}).and_call_original.ordered
369
369
  response = HTTP.put(request_uri)
370
370
  expect(response.success?).to eq(true)
371
371
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: http.rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.18.3
4
+ version: 0.20.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - thoran
@@ -80,6 +80,7 @@ files:
80
80
  - Rakefile
81
81
  - http.rb.gemspec
82
82
  - lib/HTTP.rb
83
+ - lib/HTTP/RETRY.rb
83
84
  - lib/HTTP/VERSION.rb
84
85
  - lib/HTTP/request.rb
85
86
  - lib/HTTP/verbs.rb
@@ -93,6 +94,7 @@ files:
93
94
  - lib/Thoran/Array/FirstX/firstX.rb
94
95
  - lib/Thoran/String/ToConst/to_const.rb
95
96
  - lib/URI/Generic/use_sslQ.rb
97
+ - spec/HTTP/RETRY_spec.rb
96
98
  - spec/HTTP/delete_spec.rb
97
99
  - spec/HTTP/get_spec.rb
98
100
  - spec/HTTP/head_spec.rb