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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +241 -41
- data/LICENSE.txt +1 -1
- data/README.md +110 -13
- data/UPGRADING.md +491 -0
- data/http.gemspec +32 -29
- data/lib/http/base64.rb +11 -1
- data/lib/http/chainable/helpers.rb +62 -0
- data/lib/http/chainable/verbs.rb +136 -0
- data/lib/http/chainable.rb +232 -136
- data/lib/http/client.rb +158 -127
- data/lib/http/connection/internals.rb +141 -0
- data/lib/http/connection.rb +126 -97
- data/lib/http/content_type.rb +61 -6
- data/lib/http/errors.rb +25 -1
- data/lib/http/feature.rb +65 -5
- data/lib/http/features/auto_deflate.rb +124 -17
- data/lib/http/features/auto_inflate.rb +38 -15
- data/lib/http/features/caching/entry.rb +178 -0
- data/lib/http/features/caching/in_memory_store.rb +63 -0
- data/lib/http/features/caching.rb +216 -0
- data/lib/http/features/digest_auth.rb +234 -0
- data/lib/http/features/instrumentation.rb +97 -17
- data/lib/http/features/logging.rb +183 -5
- data/lib/http/features/normalize_uri.rb +17 -0
- data/lib/http/features/raise_error.rb +18 -3
- data/lib/http/form_data/composite_io.rb +106 -0
- data/lib/http/form_data/file.rb +95 -0
- data/lib/http/form_data/multipart/param.rb +62 -0
- data/lib/http/form_data/multipart.rb +106 -0
- data/lib/http/form_data/part.rb +52 -0
- data/lib/http/form_data/readable.rb +58 -0
- data/lib/http/form_data/urlencoded.rb +175 -0
- data/lib/http/form_data/version.rb +8 -0
- data/lib/http/form_data.rb +102 -0
- data/lib/http/headers/known.rb +3 -0
- data/lib/http/headers/normalizer.rb +17 -36
- data/lib/http/headers.rb +172 -65
- data/lib/http/mime_type/adapter.rb +24 -9
- data/lib/http/mime_type/json.rb +19 -4
- data/lib/http/mime_type.rb +21 -3
- data/lib/http/options/definitions.rb +189 -0
- data/lib/http/options.rb +172 -125
- data/lib/http/redirector.rb +80 -75
- data/lib/http/request/body.rb +87 -6
- data/lib/http/request/builder.rb +184 -0
- data/lib/http/request/proxy.rb +83 -0
- data/lib/http/request/writer.rb +76 -16
- data/lib/http/request.rb +214 -98
- data/lib/http/response/body.rb +103 -18
- data/lib/http/response/inflater.rb +35 -7
- data/lib/http/response/parser.rb +98 -4
- data/lib/http/response/status/reasons.rb +2 -4
- data/lib/http/response/status.rb +141 -31
- data/lib/http/response.rb +219 -61
- data/lib/http/retriable/delay_calculator.rb +38 -11
- data/lib/http/retriable/errors.rb +21 -0
- data/lib/http/retriable/performer.rb +82 -38
- data/lib/http/session.rb +280 -0
- data/lib/http/timeout/global.rb +147 -34
- data/lib/http/timeout/null.rb +155 -9
- data/lib/http/timeout/per_operation.rb +139 -18
- data/lib/http/uri/normalizer.rb +82 -0
- data/lib/http/uri/parsing.rb +182 -0
- data/lib/http/uri.rb +289 -124
- data/lib/http/version.rb +2 -1
- data/lib/http.rb +11 -2
- data/sig/deps.rbs +122 -0
- data/sig/http.rbs +1619 -0
- data/test/http/base64_test.rb +28 -0
- data/test/http/client_test.rb +739 -0
- data/test/http/connection_test.rb +1533 -0
- data/test/http/content_type_test.rb +190 -0
- data/test/http/errors_test.rb +28 -0
- data/test/http/feature_test.rb +49 -0
- data/test/http/features/auto_deflate_test.rb +317 -0
- data/test/http/features/auto_inflate_test.rb +213 -0
- data/test/http/features/caching_test.rb +942 -0
- data/test/http/features/digest_auth_test.rb +996 -0
- data/test/http/features/instrumentation_test.rb +246 -0
- data/test/http/features/logging_test.rb +654 -0
- data/test/http/features/normalize_uri_test.rb +41 -0
- data/test/http/features/raise_error_test.rb +77 -0
- data/test/http/form_data/composite_io_test.rb +215 -0
- data/test/http/form_data/file_test.rb +255 -0
- data/test/http/form_data/fixtures/the-http-gem.info +1 -0
- data/test/http/form_data/multipart_test.rb +303 -0
- data/test/http/form_data/part_test.rb +90 -0
- data/test/http/form_data/urlencoded_test.rb +164 -0
- data/test/http/form_data_test.rb +232 -0
- data/test/http/headers/normalizer_test.rb +93 -0
- data/test/http/headers_test.rb +888 -0
- data/test/http/mime_type/json_test.rb +39 -0
- data/test/http/mime_type_test.rb +150 -0
- data/test/http/options/base_uri_test.rb +148 -0
- data/test/http/options/body_test.rb +21 -0
- data/test/http/options/features_test.rb +38 -0
- data/test/http/options/form_test.rb +21 -0
- data/test/http/options/headers_test.rb +32 -0
- data/test/http/options/json_test.rb +21 -0
- data/test/http/options/merge_test.rb +78 -0
- data/test/http/options/new_test.rb +37 -0
- data/test/http/options/proxy_test.rb +32 -0
- data/test/http/options_test.rb +575 -0
- data/test/http/redirector_test.rb +639 -0
- data/test/http/request/body_test.rb +318 -0
- data/test/http/request/builder_test.rb +623 -0
- data/test/http/request/writer_test.rb +391 -0
- data/test/http/request_test.rb +1733 -0
- data/test/http/response/body_test.rb +292 -0
- data/test/http/response/parser_test.rb +105 -0
- data/test/http/response/status_test.rb +322 -0
- data/test/http/response_test.rb +502 -0
- data/test/http/retriable/delay_calculator_test.rb +194 -0
- data/test/http/retriable/errors_test.rb +71 -0
- data/test/http/retriable/performer_test.rb +551 -0
- data/test/http/session_test.rb +424 -0
- data/test/http/timeout/global_test.rb +239 -0
- data/test/http/timeout/null_test.rb +218 -0
- data/test/http/timeout/per_operation_test.rb +220 -0
- data/test/http/uri/normalizer_test.rb +89 -0
- data/test/http/uri_test.rb +1140 -0
- data/test/http/version_test.rb +15 -0
- data/test/http_test.rb +818 -0
- data/test/regression_tests.rb +27 -0
- data/test/support/dummy_server/encoding_routes.rb +47 -0
- data/test/support/dummy_server/routes.rb +201 -0
- data/test/support/dummy_server/servlet.rb +81 -0
- data/test/support/dummy_server.rb +200 -0
- data/{spec → test}/support/fakeio.rb +2 -2
- data/test/support/http_handling_shared/connection_reuse_tests.rb +97 -0
- data/test/support/http_handling_shared/timeout_tests.rb +134 -0
- data/test/support/http_handling_shared.rb +11 -0
- data/test/support/proxy_server.rb +207 -0
- data/test/support/servers/runner.rb +67 -0
- data/{spec → test}/support/simplecov.rb +11 -2
- data/test/support/ssl_helper.rb +108 -0
- data/test/test_helper.rb +38 -0
- metadata +108 -168
- data/.github/workflows/ci.yml +0 -67
- data/.gitignore +0 -15
- data/.rspec +0 -1
- data/.rubocop/layout.yml +0 -8
- data/.rubocop/metrics.yml +0 -4
- data/.rubocop/rspec.yml +0 -9
- data/.rubocop/style.yml +0 -32
- data/.rubocop.yml +0 -11
- data/.rubocop_todo.yml +0 -219
- data/.yardopts +0 -2
- data/CHANGES_OLD.md +0 -1002
- data/Gemfile +0 -51
- data/Guardfile +0 -18
- data/Rakefile +0 -64
- data/lib/http/headers/mixin.rb +0 -34
- data/lib/http/retriable/client.rb +0 -37
- data/logo.png +0 -0
- data/spec/lib/http/client_spec.rb +0 -556
- data/spec/lib/http/connection_spec.rb +0 -88
- data/spec/lib/http/content_type_spec.rb +0 -47
- data/spec/lib/http/features/auto_deflate_spec.rb +0 -77
- data/spec/lib/http/features/auto_inflate_spec.rb +0 -86
- data/spec/lib/http/features/instrumentation_spec.rb +0 -81
- data/spec/lib/http/features/logging_spec.rb +0 -65
- data/spec/lib/http/features/raise_error_spec.rb +0 -62
- data/spec/lib/http/headers/mixin_spec.rb +0 -36
- data/spec/lib/http/headers/normalizer_spec.rb +0 -52
- data/spec/lib/http/headers_spec.rb +0 -527
- data/spec/lib/http/options/body_spec.rb +0 -15
- data/spec/lib/http/options/features_spec.rb +0 -33
- data/spec/lib/http/options/form_spec.rb +0 -15
- data/spec/lib/http/options/headers_spec.rb +0 -24
- data/spec/lib/http/options/json_spec.rb +0 -15
- data/spec/lib/http/options/merge_spec.rb +0 -68
- data/spec/lib/http/options/new_spec.rb +0 -30
- data/spec/lib/http/options/proxy_spec.rb +0 -20
- data/spec/lib/http/options_spec.rb +0 -13
- data/spec/lib/http/redirector_spec.rb +0 -530
- data/spec/lib/http/request/body_spec.rb +0 -211
- data/spec/lib/http/request/writer_spec.rb +0 -121
- data/spec/lib/http/request_spec.rb +0 -234
- data/spec/lib/http/response/body_spec.rb +0 -85
- data/spec/lib/http/response/parser_spec.rb +0 -74
- data/spec/lib/http/response/status_spec.rb +0 -253
- data/spec/lib/http/response_spec.rb +0 -262
- data/spec/lib/http/retriable/delay_calculator_spec.rb +0 -69
- data/spec/lib/http/retriable/performer_spec.rb +0 -302
- data/spec/lib/http/uri/normalizer_spec.rb +0 -95
- data/spec/lib/http/uri_spec.rb +0 -71
- data/spec/lib/http_spec.rb +0 -535
- data/spec/regression_specs.rb +0 -24
- data/spec/spec_helper.rb +0 -89
- data/spec/support/black_hole.rb +0 -13
- data/spec/support/dummy_server/servlet.rb +0 -203
- data/spec/support/dummy_server.rb +0 -44
- data/spec/support/fuubar.rb +0 -21
- data/spec/support/http_handling_shared.rb +0 -190
- data/spec/support/proxy_server.rb +0 -39
- data/spec/support/servers/config.rb +0 -11
- data/spec/support/servers/runner.rb +0 -19
- data/spec/support/ssl_helper.rb +0 -104
- /data/{spec → test}/support/capture_warning.rb +0 -0
|
@@ -1,211 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
RSpec.describe HTTP::Request::Body do
|
|
4
|
-
let(:body) { "" }
|
|
5
|
-
subject { HTTP::Request::Body.new(body) }
|
|
6
|
-
|
|
7
|
-
describe "#initialize" do
|
|
8
|
-
context "when body is nil" do
|
|
9
|
-
let(:body) { nil }
|
|
10
|
-
|
|
11
|
-
it "does not raise an error" do
|
|
12
|
-
expect { subject }.not_to raise_error
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
context "when body is a string" do
|
|
17
|
-
let(:body) { "string body" }
|
|
18
|
-
|
|
19
|
-
it "does not raise an error" do
|
|
20
|
-
expect { subject }.not_to raise_error
|
|
21
|
-
end
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
context "when body is an IO" do
|
|
25
|
-
let(:body) { FakeIO.new("IO body") }
|
|
26
|
-
|
|
27
|
-
it "does not raise an error" do
|
|
28
|
-
expect { subject }.not_to raise_error
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
context "when body is an Enumerable" do
|
|
33
|
-
let(:body) { %w[bees cows] }
|
|
34
|
-
|
|
35
|
-
it "does not raise an error" do
|
|
36
|
-
expect { subject }.not_to raise_error
|
|
37
|
-
end
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
context "when body is of unrecognized type" do
|
|
41
|
-
let(:body) { 123 }
|
|
42
|
-
|
|
43
|
-
it "raises an error" do
|
|
44
|
-
expect { subject }.to raise_error(HTTP::RequestError)
|
|
45
|
-
end
|
|
46
|
-
end
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
describe "#source" do
|
|
50
|
-
it "returns the original object" do
|
|
51
|
-
expect(subject.source).to eq ""
|
|
52
|
-
end
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
describe "#size" do
|
|
56
|
-
context "when body is nil" do
|
|
57
|
-
let(:body) { nil }
|
|
58
|
-
|
|
59
|
-
it "returns zero" do
|
|
60
|
-
expect(subject.size).to eq 0
|
|
61
|
-
end
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
context "when body is a string" do
|
|
65
|
-
let(:body) { "Привет, мир!" }
|
|
66
|
-
|
|
67
|
-
it "returns string bytesize" do
|
|
68
|
-
expect(subject.size).to eq 21
|
|
69
|
-
end
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
context "when body is an IO with size" do
|
|
73
|
-
let(:body) { FakeIO.new("content") }
|
|
74
|
-
|
|
75
|
-
it "returns IO size" do
|
|
76
|
-
expect(subject.size).to eq 7
|
|
77
|
-
end
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
context "when body is an IO without size" do
|
|
81
|
-
let(:body) { IO.pipe[0] }
|
|
82
|
-
|
|
83
|
-
it "raises a RequestError" do
|
|
84
|
-
expect { subject.size }.to raise_error(HTTP::RequestError)
|
|
85
|
-
end
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
context "when body is an Enumerable" do
|
|
89
|
-
let(:body) { %w[bees cows] }
|
|
90
|
-
|
|
91
|
-
it "raises a RequestError" do
|
|
92
|
-
expect { subject.size }.to raise_error(HTTP::RequestError)
|
|
93
|
-
end
|
|
94
|
-
end
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
describe "#each" do
|
|
98
|
-
let(:chunks) do
|
|
99
|
-
chunks = []
|
|
100
|
-
subject.each { |chunk| chunks << chunk.dup }
|
|
101
|
-
chunks
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
context "when body is nil" do
|
|
105
|
-
let(:body) { nil }
|
|
106
|
-
|
|
107
|
-
it "yields nothing" do
|
|
108
|
-
expect(chunks).to eq []
|
|
109
|
-
end
|
|
110
|
-
end
|
|
111
|
-
|
|
112
|
-
context "when body is a string" do
|
|
113
|
-
let(:body) { "content" }
|
|
114
|
-
|
|
115
|
-
it "yields the string" do
|
|
116
|
-
expect(chunks).to eq %w[content]
|
|
117
|
-
end
|
|
118
|
-
end
|
|
119
|
-
|
|
120
|
-
context "when body is a non-Enumerable IO" do
|
|
121
|
-
let(:body) { FakeIO.new(("a" * 16 * 1024) + ("b" * 10 * 1024)) }
|
|
122
|
-
|
|
123
|
-
it "yields chunks of content" do
|
|
124
|
-
expect(chunks.inject("", :+)).to eq ("a" * 16 * 1024) + ("b" * 10 * 1024)
|
|
125
|
-
end
|
|
126
|
-
end
|
|
127
|
-
|
|
128
|
-
context "when body is a pipe" do
|
|
129
|
-
let(:ios) { IO.pipe }
|
|
130
|
-
let(:body) { ios[0] }
|
|
131
|
-
|
|
132
|
-
around do |example|
|
|
133
|
-
writer = Thread.new(ios[1]) do |io|
|
|
134
|
-
io << "abcdef"
|
|
135
|
-
io.close
|
|
136
|
-
end
|
|
137
|
-
|
|
138
|
-
begin
|
|
139
|
-
example.run
|
|
140
|
-
ensure
|
|
141
|
-
writer.join
|
|
142
|
-
end
|
|
143
|
-
end
|
|
144
|
-
|
|
145
|
-
it "yields chunks of content" do
|
|
146
|
-
expect(chunks.inject("", :+)).to eq("abcdef")
|
|
147
|
-
end
|
|
148
|
-
end
|
|
149
|
-
|
|
150
|
-
context "when body is an Enumerable IO" do
|
|
151
|
-
let(:data) { ("a" * 16 * 1024) + ("b" * 10 * 1024) }
|
|
152
|
-
let(:body) { StringIO.new data }
|
|
153
|
-
|
|
154
|
-
it "yields chunks of content" do
|
|
155
|
-
expect(chunks.inject("", :+)).to eq data
|
|
156
|
-
end
|
|
157
|
-
|
|
158
|
-
it "allows to enumerate multiple times" do
|
|
159
|
-
results = []
|
|
160
|
-
|
|
161
|
-
2.times do
|
|
162
|
-
result = ""
|
|
163
|
-
subject.each { |chunk| result += chunk }
|
|
164
|
-
results << result
|
|
165
|
-
end
|
|
166
|
-
|
|
167
|
-
aggregate_failures do
|
|
168
|
-
expect(results.count).to eq 2
|
|
169
|
-
expect(results).to all eq data
|
|
170
|
-
end
|
|
171
|
-
end
|
|
172
|
-
end
|
|
173
|
-
|
|
174
|
-
context "when body is an Enumerable" do
|
|
175
|
-
let(:body) { %w[bees cows] }
|
|
176
|
-
|
|
177
|
-
it "yields elements" do
|
|
178
|
-
expect(chunks).to eq %w[bees cows]
|
|
179
|
-
end
|
|
180
|
-
end
|
|
181
|
-
end
|
|
182
|
-
|
|
183
|
-
describe "#==" do
|
|
184
|
-
context "when sources are equivalent" do
|
|
185
|
-
let(:body1) { HTTP::Request::Body.new("content") }
|
|
186
|
-
let(:body2) { HTTP::Request::Body.new("content") }
|
|
187
|
-
|
|
188
|
-
it "returns true" do
|
|
189
|
-
expect(body1).to eq body2
|
|
190
|
-
end
|
|
191
|
-
end
|
|
192
|
-
|
|
193
|
-
context "when sources are not equivalent" do
|
|
194
|
-
let(:body1) { HTTP::Request::Body.new("content") }
|
|
195
|
-
let(:body2) { HTTP::Request::Body.new(nil) }
|
|
196
|
-
|
|
197
|
-
it "returns false" do
|
|
198
|
-
expect(body1).not_to eq body2
|
|
199
|
-
end
|
|
200
|
-
end
|
|
201
|
-
|
|
202
|
-
context "when objects are not of the same class" do
|
|
203
|
-
let(:body1) { HTTP::Request::Body.new("content") }
|
|
204
|
-
let(:body2) { "content" }
|
|
205
|
-
|
|
206
|
-
it "returns false" do
|
|
207
|
-
expect(body1).not_to eq body2
|
|
208
|
-
end
|
|
209
|
-
end
|
|
210
|
-
end
|
|
211
|
-
end
|
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
# coding: utf-8
|
|
2
|
-
# frozen_string_literal: true
|
|
3
|
-
|
|
4
|
-
RSpec.describe HTTP::Request::Writer do
|
|
5
|
-
let(:io) { StringIO.new }
|
|
6
|
-
let(:body) { HTTP::Request::Body.new("") }
|
|
7
|
-
let(:headers) { HTTP::Headers.new }
|
|
8
|
-
let(:headerstart) { "GET /test HTTP/1.1" }
|
|
9
|
-
|
|
10
|
-
subject(:writer) { described_class.new(io, body, headers, headerstart) }
|
|
11
|
-
|
|
12
|
-
describe "#stream" do
|
|
13
|
-
context "when multiple headers are set" do
|
|
14
|
-
let(:headers) { HTTP::Headers.coerce "Host" => "example.org" }
|
|
15
|
-
|
|
16
|
-
it "separates headers with carriage return and line feed" do
|
|
17
|
-
writer.stream
|
|
18
|
-
expect(io.string).to eq [
|
|
19
|
-
"#{headerstart}\r\n",
|
|
20
|
-
"Host: example.org\r\nContent-Length: 0\r\n\r\n"
|
|
21
|
-
].join
|
|
22
|
-
end
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
context "when headers are specified as strings with mixed case" do
|
|
26
|
-
let(:headers) { HTTP::Headers.coerce "content-Type" => "text", "X_MAX" => "200" }
|
|
27
|
-
|
|
28
|
-
it "writes the headers with the same casing" do
|
|
29
|
-
writer.stream
|
|
30
|
-
expect(io.string).to eq [
|
|
31
|
-
"#{headerstart}\r\n",
|
|
32
|
-
"content-Type: text\r\nX_MAX: 200\r\nContent-Length: 0\r\n\r\n"
|
|
33
|
-
].join
|
|
34
|
-
end
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
context "when body is nonempty" do
|
|
38
|
-
let(:body) { HTTP::Request::Body.new("content") }
|
|
39
|
-
|
|
40
|
-
it "writes it to the socket and sets Content-Length" do
|
|
41
|
-
writer.stream
|
|
42
|
-
expect(io.string).to eq [
|
|
43
|
-
"#{headerstart}\r\n",
|
|
44
|
-
"Content-Length: 7\r\n\r\n",
|
|
45
|
-
"content"
|
|
46
|
-
].join
|
|
47
|
-
end
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
context "when body is not set" do
|
|
51
|
-
let(:body) { HTTP::Request::Body.new(nil) }
|
|
52
|
-
|
|
53
|
-
it "doesn't write anything to the socket and doesn't set Content-Length" do
|
|
54
|
-
writer.stream
|
|
55
|
-
expect(io.string).to eq [
|
|
56
|
-
"#{headerstart}\r\n\r\n"
|
|
57
|
-
].join
|
|
58
|
-
end
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
context "when body is empty" do
|
|
62
|
-
let(:body) { HTTP::Request::Body.new("") }
|
|
63
|
-
|
|
64
|
-
it "doesn't write anything to the socket and sets Content-Length" do
|
|
65
|
-
writer.stream
|
|
66
|
-
expect(io.string).to eq [
|
|
67
|
-
"#{headerstart}\r\n",
|
|
68
|
-
"Content-Length: 0\r\n\r\n"
|
|
69
|
-
].join
|
|
70
|
-
end
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
context "when Content-Length header is set" do
|
|
74
|
-
let(:headers) { HTTP::Headers.coerce "Content-Length" => "12" }
|
|
75
|
-
let(:body) { HTTP::Request::Body.new("content") }
|
|
76
|
-
|
|
77
|
-
it "keeps the given value" do
|
|
78
|
-
writer.stream
|
|
79
|
-
expect(io.string).to eq [
|
|
80
|
-
"#{headerstart}\r\n",
|
|
81
|
-
"Content-Length: 12\r\n\r\n",
|
|
82
|
-
"content"
|
|
83
|
-
].join
|
|
84
|
-
end
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
context "when Transfer-Encoding is chunked" do
|
|
88
|
-
let(:headers) { HTTP::Headers.coerce "Transfer-Encoding" => "chunked" }
|
|
89
|
-
let(:body) { HTTP::Request::Body.new(%w[request body]) }
|
|
90
|
-
|
|
91
|
-
it "writes encoded content and omits Content-Length" do
|
|
92
|
-
writer.stream
|
|
93
|
-
expect(io.string).to eq [
|
|
94
|
-
"#{headerstart}\r\n",
|
|
95
|
-
"Transfer-Encoding: chunked\r\n\r\n",
|
|
96
|
-
"7\r\nrequest\r\n4\r\nbody\r\n0\r\n\r\n"
|
|
97
|
-
].join
|
|
98
|
-
end
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
context "when server won't accept any more data" do
|
|
102
|
-
before do
|
|
103
|
-
expect(io).to receive(:write).and_raise(Errno::EPIPE)
|
|
104
|
-
end
|
|
105
|
-
|
|
106
|
-
it "aborts silently" do
|
|
107
|
-
writer.stream
|
|
108
|
-
end
|
|
109
|
-
end
|
|
110
|
-
|
|
111
|
-
context "when writing to socket raises an exception" do
|
|
112
|
-
before do
|
|
113
|
-
expect(io).to receive(:write).and_raise(Errno::ECONNRESET)
|
|
114
|
-
end
|
|
115
|
-
|
|
116
|
-
it "raises a ConnectionError" do
|
|
117
|
-
expect { writer.stream }.to raise_error(HTTP::ConnectionError)
|
|
118
|
-
end
|
|
119
|
-
end
|
|
120
|
-
end
|
|
121
|
-
end
|
|
@@ -1,234 +0,0 @@
|
|
|
1
|
-
# coding: utf-8
|
|
2
|
-
# frozen_string_literal: true
|
|
3
|
-
|
|
4
|
-
RSpec.describe HTTP::Request do
|
|
5
|
-
let(:proxy) { {} }
|
|
6
|
-
let(:headers) { {:accept => "text/html"} }
|
|
7
|
-
let(:request_uri) { "http://example.com/foo?bar=baz" }
|
|
8
|
-
|
|
9
|
-
subject :request do
|
|
10
|
-
HTTP::Request.new(
|
|
11
|
-
:verb => :get,
|
|
12
|
-
:uri => request_uri,
|
|
13
|
-
:headers => headers,
|
|
14
|
-
:proxy => proxy
|
|
15
|
-
)
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
it "includes HTTP::Headers::Mixin" do
|
|
19
|
-
expect(described_class).to include HTTP::Headers::Mixin
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
it "requires URI to have scheme part" do
|
|
23
|
-
expect { HTTP::Request.new(:verb => :get, :uri => "example.com/") }.to \
|
|
24
|
-
raise_error(HTTP::Request::UnsupportedSchemeError)
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
it "provides a #scheme accessor" do
|
|
28
|
-
expect(request.scheme).to eq(:http)
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
it "provides a #verb accessor" do
|
|
32
|
-
expect(subject.verb).to eq(:get)
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
it "sets given headers" do
|
|
36
|
-
expect(subject["Accept"]).to eq("text/html")
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
describe "Host header" do
|
|
40
|
-
subject { request["Host"] }
|
|
41
|
-
|
|
42
|
-
context "was not given" do
|
|
43
|
-
it { is_expected.to eq "example.com" }
|
|
44
|
-
|
|
45
|
-
context "and request URI has non-standard port" do
|
|
46
|
-
let(:request_uri) { "http://example.com:3000/" }
|
|
47
|
-
it { is_expected.to eq "example.com:3000" }
|
|
48
|
-
end
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
context "was explicitly given" do
|
|
52
|
-
before { headers[:host] = "github.com" }
|
|
53
|
-
it { is_expected.to eq "github.com" }
|
|
54
|
-
end
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
describe "User-Agent header" do
|
|
58
|
-
subject { request["User-Agent"] }
|
|
59
|
-
|
|
60
|
-
context "was not given" do
|
|
61
|
-
it { is_expected.to eq HTTP::Request::USER_AGENT }
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
context "was explicitly given" do
|
|
65
|
-
before { headers[:user_agent] = "MrCrawly/123" }
|
|
66
|
-
it { is_expected.to eq "MrCrawly/123" }
|
|
67
|
-
end
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
describe "#redirect" do
|
|
71
|
-
let(:headers) { {:accept => "text/html"} }
|
|
72
|
-
let(:proxy) { {:proxy_username => "douglas", :proxy_password => "adams"} }
|
|
73
|
-
let(:body) { "The Ultimate Question" }
|
|
74
|
-
|
|
75
|
-
let :request do
|
|
76
|
-
HTTP::Request.new(
|
|
77
|
-
:verb => :post,
|
|
78
|
-
:uri => "http://example.com/",
|
|
79
|
-
:headers => headers,
|
|
80
|
-
:proxy => proxy,
|
|
81
|
-
:body => body
|
|
82
|
-
)
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
subject(:redirected) { request.redirect "http://blog.example.com/" }
|
|
86
|
-
|
|
87
|
-
its(:uri) { is_expected.to eq HTTP::URI.parse "http://blog.example.com/" }
|
|
88
|
-
|
|
89
|
-
its(:verb) { is_expected.to eq request.verb }
|
|
90
|
-
its(:body) { is_expected.to eq request.body }
|
|
91
|
-
its(:proxy) { is_expected.to eq request.proxy }
|
|
92
|
-
|
|
93
|
-
it "presets new Host header" do
|
|
94
|
-
expect(redirected["Host"]).to eq "blog.example.com"
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
context "with URL with non-standard port given" do
|
|
98
|
-
subject(:redirected) { request.redirect "http://example.com:8080" }
|
|
99
|
-
|
|
100
|
-
its(:uri) { is_expected.to eq HTTP::URI.parse "http://example.com:8080" }
|
|
101
|
-
|
|
102
|
-
its(:verb) { is_expected.to eq request.verb }
|
|
103
|
-
its(:body) { is_expected.to eq request.body }
|
|
104
|
-
its(:proxy) { is_expected.to eq request.proxy }
|
|
105
|
-
|
|
106
|
-
it "presets new Host header" do
|
|
107
|
-
expect(redirected["Host"]).to eq "example.com:8080"
|
|
108
|
-
end
|
|
109
|
-
end
|
|
110
|
-
|
|
111
|
-
context "with schema-less absolute URL given" do
|
|
112
|
-
subject(:redirected) { request.redirect "//another.example.com/blog" }
|
|
113
|
-
|
|
114
|
-
its(:uri) { is_expected.to eq HTTP::URI.parse "http://another.example.com/blog" }
|
|
115
|
-
|
|
116
|
-
its(:verb) { is_expected.to eq request.verb }
|
|
117
|
-
its(:body) { is_expected.to eq request.body }
|
|
118
|
-
its(:proxy) { is_expected.to eq request.proxy }
|
|
119
|
-
|
|
120
|
-
it "presets new Host header" do
|
|
121
|
-
expect(redirected["Host"]).to eq "another.example.com"
|
|
122
|
-
end
|
|
123
|
-
end
|
|
124
|
-
|
|
125
|
-
context "with relative URL given" do
|
|
126
|
-
subject(:redirected) { request.redirect "/blog" }
|
|
127
|
-
|
|
128
|
-
its(:uri) { is_expected.to eq HTTP::URI.parse "http://example.com/blog" }
|
|
129
|
-
|
|
130
|
-
its(:verb) { is_expected.to eq request.verb }
|
|
131
|
-
its(:body) { is_expected.to eq request.body }
|
|
132
|
-
its(:proxy) { is_expected.to eq request.proxy }
|
|
133
|
-
|
|
134
|
-
it "keeps Host header" do
|
|
135
|
-
expect(redirected["Host"]).to eq "example.com"
|
|
136
|
-
end
|
|
137
|
-
|
|
138
|
-
context "with original URI having non-standard port" do
|
|
139
|
-
let :request do
|
|
140
|
-
HTTP::Request.new(
|
|
141
|
-
:verb => :post,
|
|
142
|
-
:uri => "http://example.com:8080/",
|
|
143
|
-
:headers => headers,
|
|
144
|
-
:proxy => proxy,
|
|
145
|
-
:body => body
|
|
146
|
-
)
|
|
147
|
-
end
|
|
148
|
-
|
|
149
|
-
its(:uri) { is_expected.to eq HTTP::URI.parse "http://example.com:8080/blog" }
|
|
150
|
-
end
|
|
151
|
-
end
|
|
152
|
-
|
|
153
|
-
context "with relative URL that misses leading slash given" do
|
|
154
|
-
subject(:redirected) { request.redirect "blog" }
|
|
155
|
-
|
|
156
|
-
its(:uri) { is_expected.to eq HTTP::URI.parse "http://example.com/blog" }
|
|
157
|
-
|
|
158
|
-
its(:verb) { is_expected.to eq request.verb }
|
|
159
|
-
its(:body) { is_expected.to eq request.body }
|
|
160
|
-
its(:proxy) { is_expected.to eq request.proxy }
|
|
161
|
-
|
|
162
|
-
it "keeps Host header" do
|
|
163
|
-
expect(redirected["Host"]).to eq "example.com"
|
|
164
|
-
end
|
|
165
|
-
|
|
166
|
-
context "with original URI having non-standard port" do
|
|
167
|
-
let :request do
|
|
168
|
-
HTTP::Request.new(
|
|
169
|
-
:verb => :post,
|
|
170
|
-
:uri => "http://example.com:8080/",
|
|
171
|
-
:headers => headers,
|
|
172
|
-
:proxy => proxy,
|
|
173
|
-
:body => body
|
|
174
|
-
)
|
|
175
|
-
end
|
|
176
|
-
|
|
177
|
-
its(:uri) { is_expected.to eq HTTP::URI.parse "http://example.com:8080/blog" }
|
|
178
|
-
end
|
|
179
|
-
end
|
|
180
|
-
|
|
181
|
-
context "with new verb given" do
|
|
182
|
-
subject { request.redirect "http://blog.example.com/", :get }
|
|
183
|
-
its(:verb) { is_expected.to be :get }
|
|
184
|
-
end
|
|
185
|
-
end
|
|
186
|
-
|
|
187
|
-
describe "#headline" do
|
|
188
|
-
subject(:headline) { request.headline }
|
|
189
|
-
|
|
190
|
-
it { is_expected.to eq "GET /foo?bar=baz HTTP/1.1" }
|
|
191
|
-
|
|
192
|
-
context "when URI contains encoded query" do
|
|
193
|
-
let(:encoded_query) { "t=1970-01-01T01%3A00%3A00%2B01%3A00" }
|
|
194
|
-
let(:request_uri) { "http://example.com/foo/?#{encoded_query}" }
|
|
195
|
-
|
|
196
|
-
it "does not unencodes query part" do
|
|
197
|
-
expect(headline).to eq "GET /foo/?#{encoded_query} HTTP/1.1"
|
|
198
|
-
end
|
|
199
|
-
end
|
|
200
|
-
|
|
201
|
-
context "when URI contains non-ASCII path" do
|
|
202
|
-
let(:request_uri) { "http://example.com/キョ" }
|
|
203
|
-
|
|
204
|
-
it "encodes non-ASCII path part" do
|
|
205
|
-
expect(headline).to eq "GET /%E3%82%AD%E3%83%A7 HTTP/1.1"
|
|
206
|
-
end
|
|
207
|
-
end
|
|
208
|
-
|
|
209
|
-
context "when URI contains fragment" do
|
|
210
|
-
let(:request_uri) { "http://example.com/foo#bar" }
|
|
211
|
-
|
|
212
|
-
it "omits fragment part" do
|
|
213
|
-
expect(headline).to eq "GET /foo HTTP/1.1"
|
|
214
|
-
end
|
|
215
|
-
end
|
|
216
|
-
|
|
217
|
-
context "with proxy" do
|
|
218
|
-
let(:proxy) { {:user => "user", :pass => "pass"} }
|
|
219
|
-
it { is_expected.to eq "GET http://example.com/foo?bar=baz HTTP/1.1" }
|
|
220
|
-
|
|
221
|
-
context "and HTTPS uri" do
|
|
222
|
-
let(:request_uri) { "https://example.com/foo?bar=baz" }
|
|
223
|
-
|
|
224
|
-
it { is_expected.to eq "GET /foo?bar=baz HTTP/1.1" }
|
|
225
|
-
end
|
|
226
|
-
end
|
|
227
|
-
end
|
|
228
|
-
|
|
229
|
-
describe "#inspect" do
|
|
230
|
-
subject { request.inspect }
|
|
231
|
-
|
|
232
|
-
it { is_expected.to eq "#<HTTP::Request/1.1 GET #{request_uri}>" }
|
|
233
|
-
end
|
|
234
|
-
end
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
RSpec.describe HTTP::Response::Body do
|
|
4
|
-
let(:connection) { double(:sequence_id => 0) }
|
|
5
|
-
let(:chunks) { ["Hello, ", "World!"] }
|
|
6
|
-
|
|
7
|
-
before do
|
|
8
|
-
allow(connection).to receive(:readpartial) { chunks.shift }
|
|
9
|
-
allow(connection).to receive(:body_completed?) { chunks.empty? }
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
subject(:body) { described_class.new(connection, :encoding => Encoding::UTF_8) }
|
|
13
|
-
|
|
14
|
-
it "streams bodies from responses" do
|
|
15
|
-
expect(subject.to_s).to eq("Hello, World!")
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
context "when body empty" do
|
|
19
|
-
let(:chunks) { [""] }
|
|
20
|
-
|
|
21
|
-
it "returns responds to empty? with true" do
|
|
22
|
-
expect(subject).to be_empty
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
describe "#readpartial" do
|
|
27
|
-
context "with size given" do
|
|
28
|
-
it "passes value to underlying connection" do
|
|
29
|
-
expect(connection).to receive(:readpartial).with(42)
|
|
30
|
-
body.readpartial 42
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
context "without size given" do
|
|
35
|
-
it "does not blows up" do
|
|
36
|
-
expect { body.readpartial }.to_not raise_error
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
it "calls underlying connection readpartial without specific size" do
|
|
40
|
-
expect(connection).to receive(:readpartial).with no_args
|
|
41
|
-
body.readpartial
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
it "returns content in specified encoding" do
|
|
46
|
-
body = described_class.new(connection)
|
|
47
|
-
expect(connection).to receive(:readpartial).
|
|
48
|
-
and_return(String.new("content", :encoding => Encoding::UTF_8))
|
|
49
|
-
expect(body.readpartial.encoding).to eq Encoding::BINARY
|
|
50
|
-
|
|
51
|
-
body = described_class.new(connection, :encoding => Encoding::UTF_8)
|
|
52
|
-
expect(connection).to receive(:readpartial).
|
|
53
|
-
and_return(String.new("content", :encoding => Encoding::BINARY))
|
|
54
|
-
expect(body.readpartial.encoding).to eq Encoding::UTF_8
|
|
55
|
-
end
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
context "when body is gzipped" do
|
|
59
|
-
let(:chunks) do
|
|
60
|
-
body = Zlib::Deflate.deflate("Hi, HTTP here ☺")
|
|
61
|
-
len = body.length
|
|
62
|
-
[body[0, len / 2], body[(len / 2)..]]
|
|
63
|
-
end
|
|
64
|
-
subject(:body) do
|
|
65
|
-
inflater = HTTP::Response::Inflater.new(connection)
|
|
66
|
-
described_class.new(inflater, :encoding => Encoding::UTF_8)
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
it "decodes body" do
|
|
70
|
-
expect(subject.to_s).to eq("Hi, HTTP here ☺")
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
describe "#readpartial" do
|
|
74
|
-
it "streams decoded body" do
|
|
75
|
-
[
|
|
76
|
-
"Hi, HTTP ",
|
|
77
|
-
"here ☺",
|
|
78
|
-
nil
|
|
79
|
-
].each do |part|
|
|
80
|
-
expect(subject.readpartial).to eq(part)
|
|
81
|
-
end
|
|
82
|
-
end
|
|
83
|
-
end
|
|
84
|
-
end
|
|
85
|
-
end
|