http 5.2.0 → 6.0.2

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 (128) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +110 -13
  4. data/http.gemspec +38 -35
  5. data/lib/http/base64.rb +22 -0
  6. data/lib/http/chainable/helpers.rb +62 -0
  7. data/lib/http/chainable/verbs.rb +136 -0
  8. data/lib/http/chainable.rb +249 -129
  9. data/lib/http/client.rb +158 -127
  10. data/lib/http/connection/internals.rb +141 -0
  11. data/lib/http/connection.rb +128 -97
  12. data/lib/http/content_type.rb +61 -6
  13. data/lib/http/errors.rb +41 -1
  14. data/lib/http/feature.rb +67 -6
  15. data/lib/http/features/auto_deflate.rb +124 -17
  16. data/lib/http/features/auto_inflate.rb +38 -15
  17. data/lib/http/features/caching/entry.rb +178 -0
  18. data/lib/http/features/caching/in_memory_store.rb +63 -0
  19. data/lib/http/features/caching.rb +216 -0
  20. data/lib/http/features/digest_auth.rb +234 -0
  21. data/lib/http/features/instrumentation.rb +97 -17
  22. data/lib/http/features/logging.rb +183 -5
  23. data/lib/http/features/normalize_uri.rb +17 -0
  24. data/lib/http/features/raise_error.rb +37 -0
  25. data/lib/http/form_data/composite_io.rb +106 -0
  26. data/lib/http/form_data/file.rb +95 -0
  27. data/lib/http/form_data/multipart/param.rb +62 -0
  28. data/lib/http/form_data/multipart.rb +106 -0
  29. data/lib/http/form_data/part.rb +52 -0
  30. data/lib/http/form_data/readable.rb +58 -0
  31. data/lib/http/form_data/urlencoded.rb +175 -0
  32. data/lib/http/form_data/version.rb +8 -0
  33. data/lib/http/form_data.rb +102 -0
  34. data/lib/http/headers/known.rb +3 -0
  35. data/lib/http/headers/normalizer.rb +50 -0
  36. data/lib/http/headers.rb +185 -92
  37. data/lib/http/mime_type/adapter.rb +24 -9
  38. data/lib/http/mime_type/json.rb +19 -4
  39. data/lib/http/mime_type.rb +21 -3
  40. data/lib/http/options/definitions.rb +189 -0
  41. data/lib/http/options.rb +172 -125
  42. data/lib/http/redirector.rb +80 -75
  43. data/lib/http/request/body.rb +87 -6
  44. data/lib/http/request/builder.rb +184 -0
  45. data/lib/http/request/proxy.rb +83 -0
  46. data/lib/http/request/writer.rb +78 -17
  47. data/lib/http/request.rb +216 -99
  48. data/lib/http/response/body.rb +103 -18
  49. data/lib/http/response/inflater.rb +35 -7
  50. data/lib/http/response/parser.rb +98 -4
  51. data/lib/http/response/status/reasons.rb +2 -4
  52. data/lib/http/response/status.rb +141 -31
  53. data/lib/http/response.rb +219 -61
  54. data/lib/http/retriable/delay_calculator.rb +91 -0
  55. data/lib/http/retriable/errors.rb +35 -0
  56. data/lib/http/retriable/performer.rb +197 -0
  57. data/lib/http/session.rb +280 -0
  58. data/lib/http/timeout/global.rb +147 -34
  59. data/lib/http/timeout/null.rb +155 -9
  60. data/lib/http/timeout/per_operation.rb +139 -18
  61. data/lib/http/uri/normalizer.rb +82 -0
  62. data/lib/http/uri/parsing.rb +182 -0
  63. data/lib/http/uri.rb +289 -124
  64. data/lib/http/version.rb +2 -1
  65. data/lib/http.rb +11 -1
  66. data/sig/http.rbs +1619 -0
  67. metadata +42 -175
  68. data/.github/workflows/ci.yml +0 -67
  69. data/.gitignore +0 -15
  70. data/.rspec +0 -1
  71. data/.rubocop/layout.yml +0 -8
  72. data/.rubocop/metrics.yml +0 -4
  73. data/.rubocop/style.yml +0 -32
  74. data/.rubocop.yml +0 -11
  75. data/.rubocop_todo.yml +0 -206
  76. data/.yardopts +0 -2
  77. data/CHANGELOG.md +0 -41
  78. data/CHANGES_OLD.md +0 -1002
  79. data/CONTRIBUTING.md +0 -26
  80. data/Gemfile +0 -50
  81. data/Guardfile +0 -18
  82. data/Rakefile +0 -64
  83. data/SECURITY.md +0 -17
  84. data/lib/http/headers/mixin.rb +0 -34
  85. data/logo.png +0 -0
  86. data/spec/lib/http/client_spec.rb +0 -556
  87. data/spec/lib/http/connection_spec.rb +0 -88
  88. data/spec/lib/http/content_type_spec.rb +0 -47
  89. data/spec/lib/http/features/auto_deflate_spec.rb +0 -77
  90. data/spec/lib/http/features/auto_inflate_spec.rb +0 -86
  91. data/spec/lib/http/features/instrumentation_spec.rb +0 -81
  92. data/spec/lib/http/features/logging_spec.rb +0 -65
  93. data/spec/lib/http/headers/mixin_spec.rb +0 -36
  94. data/spec/lib/http/headers_spec.rb +0 -527
  95. data/spec/lib/http/options/body_spec.rb +0 -15
  96. data/spec/lib/http/options/features_spec.rb +0 -33
  97. data/spec/lib/http/options/form_spec.rb +0 -15
  98. data/spec/lib/http/options/headers_spec.rb +0 -24
  99. data/spec/lib/http/options/json_spec.rb +0 -15
  100. data/spec/lib/http/options/merge_spec.rb +0 -68
  101. data/spec/lib/http/options/new_spec.rb +0 -30
  102. data/spec/lib/http/options/proxy_spec.rb +0 -20
  103. data/spec/lib/http/options_spec.rb +0 -13
  104. data/spec/lib/http/redirector_spec.rb +0 -529
  105. data/spec/lib/http/request/body_spec.rb +0 -211
  106. data/spec/lib/http/request/writer_spec.rb +0 -121
  107. data/spec/lib/http/request_spec.rb +0 -234
  108. data/spec/lib/http/response/body_spec.rb +0 -85
  109. data/spec/lib/http/response/parser_spec.rb +0 -74
  110. data/spec/lib/http/response/status_spec.rb +0 -253
  111. data/spec/lib/http/response_spec.rb +0 -262
  112. data/spec/lib/http/uri/normalizer_spec.rb +0 -95
  113. data/spec/lib/http/uri_spec.rb +0 -71
  114. data/spec/lib/http_spec.rb +0 -506
  115. data/spec/regression_specs.rb +0 -24
  116. data/spec/spec_helper.rb +0 -88
  117. data/spec/support/black_hole.rb +0 -13
  118. data/spec/support/capture_warning.rb +0 -10
  119. data/spec/support/dummy_server/servlet.rb +0 -190
  120. data/spec/support/dummy_server.rb +0 -43
  121. data/spec/support/fakeio.rb +0 -21
  122. data/spec/support/fuubar.rb +0 -21
  123. data/spec/support/http_handling_shared.rb +0 -190
  124. data/spec/support/proxy_server.rb +0 -39
  125. data/spec/support/servers/config.rb +0 -11
  126. data/spec/support/servers/runner.rb +0 -19
  127. data/spec/support/simplecov.rb +0 -19
  128. data/spec/support/ssl_helper.rb +0 -104
@@ -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,95 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.describe HTTP::URI::NORMALIZER do
4
- describe "scheme" do
5
- it "lower-cases scheme" do
6
- expect(HTTP::URI::NORMALIZER.call("HttP://example.com").scheme).to eq "http"
7
- end
8
- end
9
-
10
- describe "hostname" do
11
- it "lower-cases hostname" do
12
- expect(HTTP::URI::NORMALIZER.call("http://EXAMPLE.com").host).to eq "example.com"
13
- end
14
-
15
- it "decodes percent-encoded hostname" do
16
- expect(HTTP::URI::NORMALIZER.call("http://ex%61mple.com").host).to eq "example.com"
17
- end
18
-
19
- it "removes trailing period in hostname" do
20
- expect(HTTP::URI::NORMALIZER.call("http://example.com.").host).to eq "example.com"
21
- end
22
-
23
- it "IDN-encodes non-ASCII hostname" do
24
- expect(HTTP::URI::NORMALIZER.call("http://exämple.com").host).to eq "xn--exmple-cua.com"
25
- end
26
- end
27
-
28
- describe "path" do
29
- it "ensures path is not empty" do
30
- expect(HTTP::URI::NORMALIZER.call("http://example.com").path).to eq "/"
31
- end
32
-
33
- it "preserves double slashes in path" do
34
- expect(HTTP::URI::NORMALIZER.call("http://example.com//a///b").path).to eq "//a///b"
35
- end
36
-
37
- it "resolves single-dot segments in path" do
38
- expect(HTTP::URI::NORMALIZER.call("http://example.com/a/./b").path).to eq "/a/b"
39
- end
40
-
41
- it "resolves double-dot segments in path" do
42
- expect(HTTP::URI::NORMALIZER.call("http://example.com/a/b/../c").path).to eq "/a/c"
43
- end
44
-
45
- it "resolves leading double-dot segments in path" do
46
- expect(HTTP::URI::NORMALIZER.call("http://example.com/../a/b").path).to eq "/a/b"
47
- end
48
-
49
- it "percent-encodes control characters in path" do
50
- expect(HTTP::URI::NORMALIZER.call("http://example.com/\x00\x7F\n").path).to eq "/%00%7F%0A"
51
- end
52
-
53
- it "percent-encodes space in path" do
54
- expect(HTTP::URI::NORMALIZER.call("http://example.com/a b").path).to eq "/a%20b"
55
- end
56
-
57
- it "percent-encodes non-ASCII characters in path" do
58
- expect(HTTP::URI::NORMALIZER.call("http://example.com/キョ").path).to eq "/%E3%82%AD%E3%83%A7"
59
- end
60
-
61
- it "does not percent-encode non-special characters in path" do
62
- expect(HTTP::URI::NORMALIZER.call("http://example.com/~.-_!$&()*,;=:@{}").path).to eq "/~.-_!$&()*,;=:@{}"
63
- end
64
-
65
- it "preserves escape sequences in path" do
66
- expect(HTTP::URI::NORMALIZER.call("http://example.com/%41").path).to eq "/%41"
67
- end
68
- end
69
-
70
- describe "query" do
71
- it "allows no query" do
72
- expect(HTTP::URI::NORMALIZER.call("http://example.com").query).to be_nil
73
- end
74
-
75
- it "percent-encodes control characters in query" do
76
- expect(HTTP::URI::NORMALIZER.call("http://example.com/?\x00\x7F\n").query).to eq "%00%7F%0A"
77
- end
78
-
79
- it "percent-encodes space in query" do
80
- expect(HTTP::URI::NORMALIZER.call("http://example.com/?a b").query).to eq "a%20b"
81
- end
82
-
83
- it "percent-encodes non-ASCII characters in query" do
84
- expect(HTTP::URI::NORMALIZER.call("http://example.com?キョ").query).to eq "%E3%82%AD%E3%83%A7"
85
- end
86
-
87
- it "does not percent-encode non-special characters in query" do
88
- expect(HTTP::URI::NORMALIZER.call("http://example.com/?~.-_!$&()*,;=:@{}?").query).to eq "~.-_!$&()*,;=:@{}?"
89
- end
90
-
91
- it "preserves escape sequences in query" do
92
- expect(HTTP::URI::NORMALIZER.call("http://example.com/?%41").query).to eq "%41"
93
- end
94
- end
95
- end