http 5.3.1 → 6.0.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.
Files changed (201) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +241 -41
  3. data/LICENSE.txt +1 -1
  4. data/README.md +110 -13
  5. data/UPGRADING.md +491 -0
  6. data/http.gemspec +32 -29
  7. data/lib/http/base64.rb +11 -1
  8. data/lib/http/chainable/helpers.rb +62 -0
  9. data/lib/http/chainable/verbs.rb +136 -0
  10. data/lib/http/chainable.rb +232 -136
  11. data/lib/http/client.rb +158 -127
  12. data/lib/http/connection/internals.rb +141 -0
  13. data/lib/http/connection.rb +126 -97
  14. data/lib/http/content_type.rb +61 -6
  15. data/lib/http/errors.rb +25 -1
  16. data/lib/http/feature.rb +65 -5
  17. data/lib/http/features/auto_deflate.rb +124 -17
  18. data/lib/http/features/auto_inflate.rb +38 -15
  19. data/lib/http/features/caching/entry.rb +178 -0
  20. data/lib/http/features/caching/in_memory_store.rb +63 -0
  21. data/lib/http/features/caching.rb +216 -0
  22. data/lib/http/features/digest_auth.rb +234 -0
  23. data/lib/http/features/instrumentation.rb +97 -17
  24. data/lib/http/features/logging.rb +183 -5
  25. data/lib/http/features/normalize_uri.rb +17 -0
  26. data/lib/http/features/raise_error.rb +18 -3
  27. data/lib/http/form_data/composite_io.rb +106 -0
  28. data/lib/http/form_data/file.rb +95 -0
  29. data/lib/http/form_data/multipart/param.rb +62 -0
  30. data/lib/http/form_data/multipart.rb +106 -0
  31. data/lib/http/form_data/part.rb +52 -0
  32. data/lib/http/form_data/readable.rb +58 -0
  33. data/lib/http/form_data/urlencoded.rb +175 -0
  34. data/lib/http/form_data/version.rb +8 -0
  35. data/lib/http/form_data.rb +102 -0
  36. data/lib/http/headers/known.rb +3 -0
  37. data/lib/http/headers/normalizer.rb +17 -36
  38. data/lib/http/headers.rb +172 -65
  39. data/lib/http/mime_type/adapter.rb +24 -9
  40. data/lib/http/mime_type/json.rb +19 -4
  41. data/lib/http/mime_type.rb +21 -3
  42. data/lib/http/options/definitions.rb +189 -0
  43. data/lib/http/options.rb +172 -125
  44. data/lib/http/redirector.rb +80 -75
  45. data/lib/http/request/body.rb +87 -6
  46. data/lib/http/request/builder.rb +184 -0
  47. data/lib/http/request/proxy.rb +83 -0
  48. data/lib/http/request/writer.rb +76 -16
  49. data/lib/http/request.rb +214 -98
  50. data/lib/http/response/body.rb +103 -18
  51. data/lib/http/response/inflater.rb +35 -7
  52. data/lib/http/response/parser.rb +98 -4
  53. data/lib/http/response/status/reasons.rb +2 -4
  54. data/lib/http/response/status.rb +141 -31
  55. data/lib/http/response.rb +219 -61
  56. data/lib/http/retriable/delay_calculator.rb +38 -11
  57. data/lib/http/retriable/errors.rb +21 -0
  58. data/lib/http/retriable/performer.rb +82 -38
  59. data/lib/http/session.rb +280 -0
  60. data/lib/http/timeout/global.rb +147 -34
  61. data/lib/http/timeout/null.rb +155 -9
  62. data/lib/http/timeout/per_operation.rb +139 -18
  63. data/lib/http/uri/normalizer.rb +82 -0
  64. data/lib/http/uri/parsing.rb +182 -0
  65. data/lib/http/uri.rb +289 -124
  66. data/lib/http/version.rb +2 -1
  67. data/lib/http.rb +11 -2
  68. data/sig/deps.rbs +122 -0
  69. data/sig/http.rbs +1619 -0
  70. data/test/http/base64_test.rb +28 -0
  71. data/test/http/client_test.rb +739 -0
  72. data/test/http/connection_test.rb +1533 -0
  73. data/test/http/content_type_test.rb +190 -0
  74. data/test/http/errors_test.rb +28 -0
  75. data/test/http/feature_test.rb +49 -0
  76. data/test/http/features/auto_deflate_test.rb +317 -0
  77. data/test/http/features/auto_inflate_test.rb +213 -0
  78. data/test/http/features/caching_test.rb +942 -0
  79. data/test/http/features/digest_auth_test.rb +996 -0
  80. data/test/http/features/instrumentation_test.rb +246 -0
  81. data/test/http/features/logging_test.rb +654 -0
  82. data/test/http/features/normalize_uri_test.rb +41 -0
  83. data/test/http/features/raise_error_test.rb +77 -0
  84. data/test/http/form_data/composite_io_test.rb +215 -0
  85. data/test/http/form_data/file_test.rb +255 -0
  86. data/test/http/form_data/fixtures/the-http-gem.info +1 -0
  87. data/test/http/form_data/multipart_test.rb +303 -0
  88. data/test/http/form_data/part_test.rb +90 -0
  89. data/test/http/form_data/urlencoded_test.rb +164 -0
  90. data/test/http/form_data_test.rb +232 -0
  91. data/test/http/headers/normalizer_test.rb +93 -0
  92. data/test/http/headers_test.rb +888 -0
  93. data/test/http/mime_type/json_test.rb +39 -0
  94. data/test/http/mime_type_test.rb +150 -0
  95. data/test/http/options/base_uri_test.rb +148 -0
  96. data/test/http/options/body_test.rb +21 -0
  97. data/test/http/options/features_test.rb +38 -0
  98. data/test/http/options/form_test.rb +21 -0
  99. data/test/http/options/headers_test.rb +32 -0
  100. data/test/http/options/json_test.rb +21 -0
  101. data/test/http/options/merge_test.rb +78 -0
  102. data/test/http/options/new_test.rb +37 -0
  103. data/test/http/options/proxy_test.rb +32 -0
  104. data/test/http/options_test.rb +575 -0
  105. data/test/http/redirector_test.rb +639 -0
  106. data/test/http/request/body_test.rb +318 -0
  107. data/test/http/request/builder_test.rb +623 -0
  108. data/test/http/request/writer_test.rb +391 -0
  109. data/test/http/request_test.rb +1733 -0
  110. data/test/http/response/body_test.rb +292 -0
  111. data/test/http/response/parser_test.rb +105 -0
  112. data/test/http/response/status_test.rb +322 -0
  113. data/test/http/response_test.rb +502 -0
  114. data/test/http/retriable/delay_calculator_test.rb +194 -0
  115. data/test/http/retriable/errors_test.rb +71 -0
  116. data/test/http/retriable/performer_test.rb +551 -0
  117. data/test/http/session_test.rb +424 -0
  118. data/test/http/timeout/global_test.rb +239 -0
  119. data/test/http/timeout/null_test.rb +218 -0
  120. data/test/http/timeout/per_operation_test.rb +220 -0
  121. data/test/http/uri/normalizer_test.rb +89 -0
  122. data/test/http/uri_test.rb +1140 -0
  123. data/test/http/version_test.rb +15 -0
  124. data/test/http_test.rb +818 -0
  125. data/test/regression_tests.rb +27 -0
  126. data/test/support/dummy_server/encoding_routes.rb +47 -0
  127. data/test/support/dummy_server/routes.rb +201 -0
  128. data/test/support/dummy_server/servlet.rb +81 -0
  129. data/test/support/dummy_server.rb +200 -0
  130. data/{spec → test}/support/fakeio.rb +2 -2
  131. data/test/support/http_handling_shared/connection_reuse_tests.rb +97 -0
  132. data/test/support/http_handling_shared/timeout_tests.rb +134 -0
  133. data/test/support/http_handling_shared.rb +11 -0
  134. data/test/support/proxy_server.rb +207 -0
  135. data/test/support/servers/runner.rb +67 -0
  136. data/{spec → test}/support/simplecov.rb +11 -2
  137. data/test/support/ssl_helper.rb +108 -0
  138. data/test/test_helper.rb +38 -0
  139. metadata +108 -168
  140. data/.github/workflows/ci.yml +0 -67
  141. data/.gitignore +0 -15
  142. data/.rspec +0 -1
  143. data/.rubocop/layout.yml +0 -8
  144. data/.rubocop/metrics.yml +0 -4
  145. data/.rubocop/rspec.yml +0 -9
  146. data/.rubocop/style.yml +0 -32
  147. data/.rubocop.yml +0 -11
  148. data/.rubocop_todo.yml +0 -219
  149. data/.yardopts +0 -2
  150. data/CHANGES_OLD.md +0 -1002
  151. data/Gemfile +0 -51
  152. data/Guardfile +0 -18
  153. data/Rakefile +0 -64
  154. data/lib/http/headers/mixin.rb +0 -34
  155. data/lib/http/retriable/client.rb +0 -37
  156. data/logo.png +0 -0
  157. data/spec/lib/http/client_spec.rb +0 -556
  158. data/spec/lib/http/connection_spec.rb +0 -88
  159. data/spec/lib/http/content_type_spec.rb +0 -47
  160. data/spec/lib/http/features/auto_deflate_spec.rb +0 -77
  161. data/spec/lib/http/features/auto_inflate_spec.rb +0 -86
  162. data/spec/lib/http/features/instrumentation_spec.rb +0 -81
  163. data/spec/lib/http/features/logging_spec.rb +0 -65
  164. data/spec/lib/http/features/raise_error_spec.rb +0 -62
  165. data/spec/lib/http/headers/mixin_spec.rb +0 -36
  166. data/spec/lib/http/headers/normalizer_spec.rb +0 -52
  167. data/spec/lib/http/headers_spec.rb +0 -527
  168. data/spec/lib/http/options/body_spec.rb +0 -15
  169. data/spec/lib/http/options/features_spec.rb +0 -33
  170. data/spec/lib/http/options/form_spec.rb +0 -15
  171. data/spec/lib/http/options/headers_spec.rb +0 -24
  172. data/spec/lib/http/options/json_spec.rb +0 -15
  173. data/spec/lib/http/options/merge_spec.rb +0 -68
  174. data/spec/lib/http/options/new_spec.rb +0 -30
  175. data/spec/lib/http/options/proxy_spec.rb +0 -20
  176. data/spec/lib/http/options_spec.rb +0 -13
  177. data/spec/lib/http/redirector_spec.rb +0 -530
  178. data/spec/lib/http/request/body_spec.rb +0 -211
  179. data/spec/lib/http/request/writer_spec.rb +0 -121
  180. data/spec/lib/http/request_spec.rb +0 -234
  181. data/spec/lib/http/response/body_spec.rb +0 -85
  182. data/spec/lib/http/response/parser_spec.rb +0 -74
  183. data/spec/lib/http/response/status_spec.rb +0 -253
  184. data/spec/lib/http/response_spec.rb +0 -262
  185. data/spec/lib/http/retriable/delay_calculator_spec.rb +0 -69
  186. data/spec/lib/http/retriable/performer_spec.rb +0 -302
  187. data/spec/lib/http/uri/normalizer_spec.rb +0 -95
  188. data/spec/lib/http/uri_spec.rb +0 -71
  189. data/spec/lib/http_spec.rb +0 -535
  190. data/spec/regression_specs.rb +0 -24
  191. data/spec/spec_helper.rb +0 -89
  192. data/spec/support/black_hole.rb +0 -13
  193. data/spec/support/dummy_server/servlet.rb +0 -203
  194. data/spec/support/dummy_server.rb +0 -44
  195. data/spec/support/fuubar.rb +0 -21
  196. data/spec/support/http_handling_shared.rb +0 -190
  197. data/spec/support/proxy_server.rb +0 -39
  198. data/spec/support/servers/config.rb +0 -11
  199. data/spec/support/servers/runner.rb +0 -19
  200. data/spec/support/ssl_helper.rb +0 -104
  201. /data/{spec → test}/support/capture_warning.rb +0 -0
@@ -1,74 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.describe HTTP::Response::Parser do
4
- subject(:parser) { described_class.new }
5
- let(:raw_response) do
6
- "HTTP/1.1 200 OK\r\nContent-Length: 2\r\nContent-Type: application/json\r\nMyHeader: val\r\nEmptyHeader: \r\n\r\n{}"
7
- end
8
- let(:expected_headers) do
9
- {
10
- "Content-Length" => "2",
11
- "Content-Type" => "application/json",
12
- "MyHeader" => "val",
13
- "EmptyHeader" => ""
14
- }
15
- end
16
- let(:expected_body) { "{}" }
17
-
18
- before do
19
- parts.each { |part| subject.add(part) }
20
- end
21
-
22
- context "whole response in one part" do
23
- let(:parts) { [raw_response] }
24
-
25
- it "parses headers" do
26
- expect(subject.headers.to_h).to eq(expected_headers)
27
- end
28
-
29
- it "parses body" do
30
- expect(subject.read(expected_body.size)).to eq(expected_body)
31
- end
32
- end
33
-
34
- context "response in many parts" do
35
- let(:parts) { raw_response.chars }
36
-
37
- it "parses headers" do
38
- expect(subject.headers.to_h).to eq(expected_headers)
39
- end
40
-
41
- it "parses body" do
42
- expect(subject.read(expected_body.size)).to eq(expected_body)
43
- end
44
- end
45
-
46
- context "when got 100 Continue response" do
47
- let :raw_response do
48
- "HTTP/1.1 100 Continue\r\n\r\n" \
49
- "HTTP/1.1 200 OK\r\n" \
50
- "Content-Length: 12\r\n\r\n" \
51
- "Hello World!"
52
- end
53
-
54
- context "when response is feeded in one part" do
55
- let(:parts) { [raw_response] }
56
-
57
- it "skips to next non-info response" do
58
- expect(subject.status_code).to eq(200)
59
- expect(subject.headers).to eq("Content-Length" => "12")
60
- expect(subject.read(12)).to eq("Hello World!")
61
- end
62
- end
63
-
64
- context "when response is feeded in many parts" do
65
- let(:parts) { raw_response.chars }
66
-
67
- it "skips to next non-info response" do
68
- expect(subject.status_code).to eq(200)
69
- expect(subject.headers).to eq("Content-Length" => "12")
70
- expect(subject.read(12)).to eq("Hello World!")
71
- end
72
- end
73
- end
74
- end
@@ -1,253 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.describe HTTP::Response::Status do
4
- describe ".new" do
5
- it "fails if given value does not respond to #to_i" do
6
- expect { described_class.new double }.to raise_error TypeError
7
- end
8
-
9
- it "accepts any object that responds to #to_i" do
10
- expect { described_class.new double :to_i => 200 }.to_not raise_error
11
- end
12
- end
13
-
14
- describe "#code" do
15
- subject { described_class.new("200.0").code }
16
- it { is_expected.to eq 200 }
17
- it { is_expected.to be_a Integer }
18
- end
19
-
20
- describe "#reason" do
21
- subject { described_class.new(code).reason }
22
-
23
- context "with unknown code" do
24
- let(:code) { 1024 }
25
- it { is_expected.to be_nil }
26
- end
27
-
28
- described_class::REASONS.each do |code, reason|
29
- class_eval <<-RUBY, __FILE__, __LINE__ + 1
30
- context 'with well-known code: #{code}' do
31
- let(:code) { #{code} }
32
- it { is_expected.to eq #{reason.inspect} }
33
- it { is_expected.to be_frozen }
34
- end
35
- RUBY
36
- end
37
- end
38
-
39
- context "with 1xx codes" do
40
- subject { (100...200).map { |code| described_class.new code } }
41
-
42
- it "is #informational?" do
43
- expect(subject).to all(satisfy(&:informational?))
44
- end
45
-
46
- it "is not #success?" do
47
- expect(subject).to all(satisfy { |status| !status.success? })
48
- end
49
-
50
- it "is not #redirect?" do
51
- expect(subject).to all(satisfy { |status| !status.redirect? })
52
- end
53
-
54
- it "is not #client_error?" do
55
- expect(subject).to all(satisfy { |status| !status.client_error? })
56
- end
57
-
58
- it "is not #server_error?" do
59
- expect(subject).to all(satisfy { |status| !status.server_error? })
60
- end
61
- end
62
-
63
- context "with 2xx codes" do
64
- subject { (200...300).map { |code| described_class.new code } }
65
-
66
- it "is not #informational?" do
67
- expect(subject).to all(satisfy { |status| !status.informational? })
68
- end
69
-
70
- it "is #success?" do
71
- expect(subject).to all(satisfy(&:success?))
72
- end
73
-
74
- it "is not #redirect?" do
75
- expect(subject).to all(satisfy { |status| !status.redirect? })
76
- end
77
-
78
- it "is not #client_error?" do
79
- expect(subject).to all(satisfy { |status| !status.client_error? })
80
- end
81
-
82
- it "is not #server_error?" do
83
- expect(subject).to all(satisfy { |status| !status.server_error? })
84
- end
85
- end
86
-
87
- context "with 3xx codes" do
88
- subject { (300...400).map { |code| described_class.new code } }
89
-
90
- it "is not #informational?" do
91
- expect(subject).to all(satisfy { |status| !status.informational? })
92
- end
93
-
94
- it "is not #success?" do
95
- expect(subject).to all(satisfy { |status| !status.success? })
96
- end
97
-
98
- it "is #redirect?" do
99
- expect(subject).to all(satisfy(&:redirect?))
100
- end
101
-
102
- it "is not #client_error?" do
103
- expect(subject).to all(satisfy { |status| !status.client_error? })
104
- end
105
-
106
- it "is not #server_error?" do
107
- expect(subject).to all(satisfy { |status| !status.server_error? })
108
- end
109
- end
110
-
111
- context "with 4xx codes" do
112
- subject { (400...500).map { |code| described_class.new code } }
113
-
114
- it "is not #informational?" do
115
- expect(subject).to all(satisfy { |status| !status.informational? })
116
- end
117
-
118
- it "is not #success?" do
119
- expect(subject).to all(satisfy { |status| !status.success? })
120
- end
121
-
122
- it "is not #redirect?" do
123
- expect(subject).to all(satisfy { |status| !status.redirect? })
124
- end
125
-
126
- it "is #client_error?" do
127
- expect(subject).to all(satisfy(&:client_error?))
128
- end
129
-
130
- it "is not #server_error?" do
131
- expect(subject).to all(satisfy { |status| !status.server_error? })
132
- end
133
- end
134
-
135
- context "with 5xx codes" do
136
- subject { (500...600).map { |code| described_class.new code } }
137
-
138
- it "is not #informational?" do
139
- expect(subject).to all(satisfy { |status| !status.informational? })
140
- end
141
-
142
- it "is not #success?" do
143
- expect(subject).to all(satisfy { |status| !status.success? })
144
- end
145
-
146
- it "is not #redirect?" do
147
- expect(subject).to all(satisfy { |status| !status.redirect? })
148
- end
149
-
150
- it "is not #client_error?" do
151
- expect(subject).to all(satisfy { |status| !status.client_error? })
152
- end
153
-
154
- it "is #server_error?" do
155
- expect(subject).to all(satisfy(&:server_error?))
156
- end
157
- end
158
-
159
- describe "#to_sym" do
160
- subject { described_class.new(code).to_sym }
161
-
162
- context "with unknown code" do
163
- let(:code) { 1024 }
164
- it { is_expected.to be_nil }
165
- end
166
-
167
- described_class::SYMBOLS.each do |code, symbol|
168
- class_eval <<-RUBY, __FILE__, __LINE__ + 1
169
- context 'with well-known code: #{code}' do
170
- let(:code) { #{code} }
171
- it { is_expected.to be #{symbol.inspect} }
172
- end
173
- RUBY
174
- end
175
- end
176
-
177
- describe "#inspect" do
178
- it "returns quoted code and reason phrase" do
179
- status = described_class.new 200
180
- expect(status.inspect).to eq "#<HTTP::Response::Status 200 OK>"
181
- end
182
- end
183
-
184
- # testing edge cases only
185
- describe "::SYMBOLS" do
186
- subject { described_class::SYMBOLS }
187
-
188
- # "OK"
189
- its([200]) { is_expected.to be :ok }
190
-
191
- # "Bad Request"
192
- its([400]) { is_expected.to be :bad_request }
193
- end
194
-
195
- described_class::SYMBOLS.each do |code, symbol|
196
- class_eval <<-RUBY, __FILE__, __LINE__ + 1
197
- describe '##{symbol}?' do
198
- subject { status.#{symbol}? }
199
-
200
- context 'when code is #{code}' do
201
- let(:status) { described_class.new #{code} }
202
- it { is_expected.to be true }
203
- end
204
-
205
- context 'when code is higher than #{code}' do
206
- let(:status) { described_class.new #{code + 1} }
207
- it { is_expected.to be false }
208
- end
209
-
210
- context 'when code is lower than #{code}' do
211
- let(:status) { described_class.new #{code - 1} }
212
- it { is_expected.to be false }
213
- end
214
- end
215
- RUBY
216
- end
217
-
218
- describe ".coerce" do
219
- context "with String" do
220
- it "coerces reasons" do
221
- expect(described_class.coerce("Bad request")).to eq described_class.new 400
222
- end
223
-
224
- it "fails when reason is unknown" do
225
- expect { described_class.coerce "foobar" }.to raise_error HTTP::Error
226
- end
227
- end
228
-
229
- context "with Symbol" do
230
- it "coerces symbolized reasons" do
231
- expect(described_class.coerce(:bad_request)).to eq described_class.new 400
232
- end
233
-
234
- it "fails when symbolized reason is unknown" do
235
- expect { described_class.coerce(:foobar) }.to raise_error HTTP::Error
236
- end
237
- end
238
-
239
- context "with Numeric" do
240
- it "coerces as Fixnum code" do
241
- expect(described_class.coerce(200.1)).to eq described_class.new 200
242
- end
243
- end
244
-
245
- it "fails if coercion failed" do
246
- expect { described_class.coerce(true) }.to raise_error HTTP::Error
247
- end
248
-
249
- it "is aliased as `.[]`" do
250
- expect(described_class.method(:coerce)).to eq described_class.method :[]
251
- end
252
- end
253
- end
@@ -1,262 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.describe HTTP::Response do
4
- let(:body) { "Hello world!" }
5
- let(:uri) { "http://example.com/" }
6
- let(:headers) { {} }
7
- let(:request) { HTTP::Request.new(:verb => :get, :uri => uri) }
8
-
9
- subject(:response) do
10
- HTTP::Response.new(
11
- :status => 200,
12
- :version => "1.1",
13
- :headers => headers,
14
- :body => body,
15
- :request => request
16
- )
17
- end
18
-
19
- it "includes HTTP::Headers::Mixin" do
20
- expect(described_class).to include HTTP::Headers::Mixin
21
- end
22
-
23
- describe "to_a" do
24
- let(:body) { "Hello world" }
25
- let(:content_type) { "text/plain" }
26
- let(:headers) { {"Content-Type" => content_type} }
27
-
28
- it "returns a Rack-like array" do
29
- expect(subject.to_a).to eq([200, headers, body])
30
- end
31
- end
32
-
33
- describe "#content_length" do
34
- subject { response.content_length }
35
-
36
- context "without Content-Length header" do
37
- it { is_expected.to be_nil }
38
- end
39
-
40
- context "with Content-Length: 5" do
41
- let(:headers) { {"Content-Length" => "5"} }
42
- it { is_expected.to eq 5 }
43
- end
44
-
45
- context "with invalid Content-Length" do
46
- let(:headers) { {"Content-Length" => "foo"} }
47
- it { is_expected.to be_nil }
48
- end
49
- end
50
-
51
- describe "mime_type" do
52
- subject { response.mime_type }
53
-
54
- context "without Content-Type header" do
55
- let(:headers) { {} }
56
- it { is_expected.to be_nil }
57
- end
58
-
59
- context "with Content-Type: text/html" do
60
- let(:headers) { {"Content-Type" => "text/html"} }
61
- it { is_expected.to eq "text/html" }
62
- end
63
-
64
- context "with Content-Type: text/html; charset=utf-8" do
65
- let(:headers) { {"Content-Type" => "text/html; charset=utf-8"} }
66
- it { is_expected.to eq "text/html" }
67
- end
68
- end
69
-
70
- describe "charset" do
71
- subject { response.charset }
72
-
73
- context "without Content-Type header" do
74
- let(:headers) { {} }
75
- it { is_expected.to be_nil }
76
- end
77
-
78
- context "with Content-Type: text/html" do
79
- let(:headers) { {"Content-Type" => "text/html"} }
80
- it { is_expected.to be_nil }
81
- end
82
-
83
- context "with Content-Type: text/html; charset=utf-8" do
84
- let(:headers) { {"Content-Type" => "text/html; charset=utf-8"} }
85
- it { is_expected.to eq "utf-8" }
86
- end
87
- end
88
-
89
- describe "#parse" do
90
- let(:headers) { {"Content-Type" => content_type} }
91
- let(:body) { '{"foo":"bar"}' }
92
-
93
- context "with known content type" do
94
- let(:content_type) { "application/json" }
95
- it "returns parsed body" do
96
- expect(response.parse).to eq "foo" => "bar"
97
- end
98
- end
99
-
100
- context "with unknown content type" do
101
- let(:content_type) { "application/deadbeef" }
102
- it "raises HTTP::Error" do
103
- expect { response.parse }.to raise_error HTTP::Error
104
- end
105
- end
106
-
107
- context "with explicitly given mime type" do
108
- let(:content_type) { "application/deadbeef" }
109
- it "ignores mime_type of response" do
110
- expect(response.parse("application/json")).to eq "foo" => "bar"
111
- end
112
-
113
- it "supports mime type aliases" do
114
- expect(response.parse(:json)).to eq "foo" => "bar"
115
- end
116
- end
117
- end
118
-
119
- describe "#flush" do
120
- let(:body) { double :to_s => "" }
121
-
122
- it "returns response self-reference" do
123
- expect(response.flush).to be response
124
- end
125
-
126
- it "flushes body" do
127
- expect(body).to receive :to_s
128
- response.flush
129
- end
130
- end
131
-
132
- describe "#inspect" do
133
- subject { response.inspect }
134
-
135
- let(:headers) { {:content_type => "text/plain"} }
136
- let(:body) { double :to_s => "foobar" }
137
-
138
- it { is_expected.to eq '#<HTTP::Response/1.1 200 OK {"Content-Type"=>"text/plain"}>' }
139
- end
140
-
141
- describe "#cookies" do
142
- let(:cookies) { ["a=1", "b=2; domain=example.com", "c=3; domain=bad.org"] }
143
- let(:headers) { {"Set-Cookie" => cookies} }
144
-
145
- subject(:jar) { response.cookies }
146
-
147
- it { is_expected.to be_an HTTP::CookieJar }
148
-
149
- it "contains cookies without domain restriction" do
150
- expect(jar.count { |c| "a" == c.name }).to eq 1
151
- end
152
-
153
- it "contains cookies limited to domain of request uri" do
154
- expect(jar.count { |c| "b" == c.name }).to eq 1
155
- end
156
-
157
- it "does not contains cookies limited to non-requeted uri" do
158
- expect(jar.count { |c| "c" == c.name }).to eq 0
159
- end
160
- end
161
-
162
- describe "#connection" do
163
- let(:connection) { double }
164
-
165
- subject(:response) do
166
- HTTP::Response.new(
167
- :version => "1.1",
168
- :status => 200,
169
- :connection => connection,
170
- :request => request
171
- )
172
- end
173
-
174
- it "returns the connection object used to instantiate the response" do
175
- expect(response.connection).to eq connection
176
- end
177
- end
178
-
179
- describe "#chunked?" do
180
- subject { response }
181
- context "when encoding is set to chunked" do
182
- let(:headers) { {"Transfer-Encoding" => "chunked"} }
183
- it { is_expected.to be_chunked }
184
- end
185
- it { is_expected.not_to be_chunked }
186
- end
187
-
188
- describe "backwards compatibilty with :uri" do
189
- context "with no :verb" do
190
- subject(:response) do
191
- HTTP::Response.new(
192
- :status => 200,
193
- :version => "1.1",
194
- :headers => headers,
195
- :body => body,
196
- :uri => uri
197
- )
198
- end
199
-
200
- it "defaults the uri to :uri" do
201
- expect(response.request.uri.to_s).to eq uri
202
- end
203
-
204
- it "defaults to the verb to :get" do
205
- expect(response.request.verb).to eq :get
206
- end
207
- end
208
-
209
- context "with both a :request and :uri" do
210
- subject(:response) do
211
- HTTP::Response.new(
212
- :status => 200,
213
- :version => "1.1",
214
- :headers => headers,
215
- :body => body,
216
- :uri => uri,
217
- :request => request
218
- )
219
- end
220
-
221
- it "raises ArgumentError" do
222
- expect { response }.to raise_error(ArgumentError)
223
- end
224
- end
225
- end
226
-
227
- describe "#body" do
228
- let(:connection) { double(:sequence_id => 0) }
229
- let(:chunks) { ["Hello, ", "World!"] }
230
-
231
- subject(:response) do
232
- HTTP::Response.new(
233
- :status => 200,
234
- :version => "1.1",
235
- :headers => headers,
236
- :request => request,
237
- :connection => connection
238
- )
239
- end
240
-
241
- before do
242
- allow(connection).to receive(:readpartial) { chunks.shift }
243
- allow(connection).to receive(:body_completed?) { chunks.empty? }
244
- end
245
-
246
- context "with no Content-Type" do
247
- let(:headers) { {} }
248
-
249
- it "returns a body with default binary encoding" do
250
- expect(response.body.to_s.encoding).to eq Encoding::BINARY
251
- end
252
- end
253
-
254
- context "with Content-Type: application/json" do
255
- let(:headers) { {"Content-Type" => "application/json"} }
256
-
257
- it "returns a body with a default UTF_8 encoding" do
258
- expect(response.body.to_s.encoding).to eq Encoding::UTF_8
259
- end
260
- end
261
- end
262
- end
@@ -1,69 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.describe HTTP::Retriable::DelayCalculator do
4
- let(:response) do
5
- HTTP::Response.new(
6
- status: 200,
7
- version: "1.1",
8
- headers: {},
9
- body: "Hello world!",
10
- request: HTTP::Request.new(verb: :get, uri: "http://example.com")
11
- )
12
- end
13
-
14
- def call_delay(iterations, **options)
15
- described_class.new(options).call(iterations, response)
16
- end
17
-
18
- def call_retry_header(value, **options)
19
- response.headers["Retry-After"] = value
20
- described_class.new(options).call(rand(1...100), response)
21
- end
22
-
23
- it "prevents negative sleep time" do
24
- expect(call_delay(20, delay: -20)).to eq 0
25
- end
26
-
27
- it "backs off exponentially" do
28
- expect(call_delay(1)).to be_between 0, 1
29
- expect(call_delay(2)).to be_between 1, 2
30
- expect(call_delay(3)).to be_between 3, 4
31
- expect(call_delay(4)).to be_between 7, 8
32
- expect(call_delay(5)).to be_between 15, 16
33
- end
34
-
35
- it "can have a maximum wait time" do
36
- expect(call_delay(1, max_delay: 5)).to be_between 0, 1
37
- expect(call_delay(5, max_delay: 5)).to eq 5
38
- end
39
-
40
- it "respects Retry-After headers as integer" do
41
- delay_time = rand(6...2500)
42
- header_value = delay_time.to_s
43
- expect(call_retry_header(header_value)).to eq delay_time
44
- expect(call_retry_header(header_value, max_delay: 5)).to eq 5
45
- end
46
-
47
- it "respects Retry-After headers as rfc2822 timestamp" do
48
- delay_time = rand(6...2500)
49
- header_value = (Time.now.gmtime + delay_time).to_datetime.rfc2822.sub("+0000", "GMT")
50
- expect(call_retry_header(header_value)).to be_within(1).of(delay_time)
51
- expect(call_retry_header(header_value, max_delay: 5)).to eq 5
52
- end
53
-
54
- it "respects Retry-After headers as rfc2822 timestamp in the past" do
55
- delay_time = rand(6...2500)
56
- header_value = (Time.now.gmtime - delay_time).to_datetime.rfc2822.sub("+0000", "GMT")
57
- expect(call_retry_header(header_value)).to eq 0
58
- end
59
-
60
- it "does not error on invalid Retry-After header" do
61
- [ # invalid strings
62
- "This is a string with a number 5 in it",
63
- "8 Eight is the first digit in this string",
64
- "This is a string with a #{Time.now.gmtime.to_datetime.rfc2822} timestamp in it"
65
- ].each do |header_value|
66
- expect(call_retry_header(header_value)).to eq 0
67
- end
68
- end
69
- end