http 4.4.1 → 5.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/.github/workflows/ci.yml +65 -0
- data/.gitignore +6 -10
- data/.rspec +0 -4
- data/.rubocop/layout.yml +8 -0
- data/.rubocop/style.yml +32 -0
- data/.rubocop.yml +8 -110
- data/.rubocop_todo.yml +192 -0
- data/.yardopts +1 -1
- data/CHANGES.md +87 -3
- data/Gemfile +18 -10
- data/README.md +17 -20
- data/Rakefile +2 -10
- data/http.gemspec +3 -3
- data/lib/http/chainable.rb +23 -17
- data/lib/http/client.rb +36 -30
- data/lib/http/connection.rb +11 -7
- data/lib/http/content_type.rb +12 -7
- data/lib/http/feature.rb +3 -1
- data/lib/http/features/auto_deflate.rb +6 -6
- data/lib/http/features/auto_inflate.rb +6 -5
- data/lib/http/features/instrumentation.rb +1 -1
- data/lib/http/features/logging.rb +19 -21
- data/lib/http/headers.rb +50 -13
- data/lib/http/mime_type/adapter.rb +3 -1
- data/lib/http/mime_type/json.rb +1 -0
- data/lib/http/options.rb +5 -8
- data/lib/http/redirector.rb +2 -1
- data/lib/http/request/body.rb +1 -0
- data/lib/http/request/writer.rb +3 -2
- data/lib/http/request.rb +13 -10
- data/lib/http/response/body.rb +2 -2
- data/lib/http/response/inflater.rb +1 -1
- data/lib/http/response/parser.rb +74 -62
- data/lib/http/response/status.rb +4 -3
- data/lib/http/response.rb +17 -15
- data/lib/http/timeout/global.rb +17 -31
- data/lib/http/timeout/null.rb +2 -1
- data/lib/http/timeout/per_operation.rb +31 -54
- data/lib/http/uri.rb +5 -5
- data/lib/http/version.rb +1 -1
- data/spec/lib/http/client_spec.rb +119 -30
- data/spec/lib/http/connection_spec.rb +8 -5
- data/spec/lib/http/features/auto_inflate_spec.rb +4 -2
- data/spec/lib/http/features/instrumentation_spec.rb +28 -21
- data/spec/lib/http/features/logging_spec.rb +8 -9
- data/spec/lib/http/headers_spec.rb +53 -18
- data/spec/lib/http/options/headers_spec.rb +1 -1
- data/spec/lib/http/options/merge_spec.rb +16 -16
- data/spec/lib/http/redirector_spec.rb +2 -1
- data/spec/lib/http/request/writer_spec.rb +13 -1
- data/spec/lib/http/request_spec.rb +5 -5
- data/spec/lib/http/response/parser_spec.rb +33 -4
- data/spec/lib/http/response/status_spec.rb +3 -3
- data/spec/lib/http/response_spec.rb +11 -22
- data/spec/lib/http_spec.rb +30 -3
- data/spec/spec_helper.rb +21 -21
- data/spec/support/black_hole.rb +1 -1
- data/spec/support/dummy_server/servlet.rb +17 -6
- data/spec/support/dummy_server.rb +7 -7
- data/spec/support/fuubar.rb +21 -0
- data/spec/support/http_handling_shared.rb +4 -4
- data/spec/support/simplecov.rb +19 -0
- data/spec/support/ssl_helper.rb +4 -4
- metadata +18 -12
- data/.coveralls.yml +0 -1
- data/.travis.yml +0 -39
@@ -13,7 +13,7 @@ RSpec.describe HTTP::Headers do
|
|
13
13
|
expect(headers["Accept"]).to eq "application/json"
|
14
14
|
end
|
15
15
|
|
16
|
-
it "
|
16
|
+
it "allows retrieval via normalized header name" do
|
17
17
|
headers.set :content_type, "application/json"
|
18
18
|
expect(headers["Content-Type"]).to eq "application/json"
|
19
19
|
end
|
@@ -35,8 +35,15 @@ RSpec.describe HTTP::Headers do
|
|
35
35
|
to raise_error HTTP::HeaderError
|
36
36
|
end
|
37
37
|
|
38
|
-
|
39
|
-
|
38
|
+
["foo bar", "foo bar: ok\nfoo", "evil-header: evil-value\nfoo"].each do |name|
|
39
|
+
it "fails with invalid header name (#{name.inspect})" do
|
40
|
+
expect { headers.set name, "baz" }.
|
41
|
+
to raise_error HTTP::HeaderError
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
it "fails with invalid header value" do
|
46
|
+
expect { headers.set "foo", "bar\nEvil-Header: evil-value" }.
|
40
47
|
to raise_error HTTP::HeaderError
|
41
48
|
end
|
42
49
|
end
|
@@ -47,7 +54,7 @@ RSpec.describe HTTP::Headers do
|
|
47
54
|
expect(headers["Accept"]).to eq "application/json"
|
48
55
|
end
|
49
56
|
|
50
|
-
it "
|
57
|
+
it "allows retrieval via normalized header name" do
|
51
58
|
headers[:content_type] = "application/json"
|
52
59
|
expect(headers["Content-Type"]).to eq "application/json"
|
53
60
|
end
|
@@ -73,7 +80,7 @@ RSpec.describe HTTP::Headers do
|
|
73
80
|
expect(headers["Content-Type"]).to be_nil
|
74
81
|
end
|
75
82
|
|
76
|
-
it "
|
83
|
+
it "removes header that matches normalized version of specified name" do
|
77
84
|
headers.delete :content_type
|
78
85
|
expect(headers["Content-Type"]).to be_nil
|
79
86
|
end
|
@@ -83,9 +90,11 @@ RSpec.describe HTTP::Headers do
|
|
83
90
|
to raise_error HTTP::HeaderError
|
84
91
|
end
|
85
92
|
|
86
|
-
|
87
|
-
|
88
|
-
|
93
|
+
["foo bar", "foo bar: ok\nfoo"].each do |name|
|
94
|
+
it "fails with invalid header name (#{name.inspect})" do
|
95
|
+
expect { headers.delete name }.
|
96
|
+
to raise_error HTTP::HeaderError
|
97
|
+
end
|
89
98
|
end
|
90
99
|
end
|
91
100
|
|
@@ -95,13 +104,13 @@ RSpec.describe HTTP::Headers do
|
|
95
104
|
expect(headers["Accept"]).to eq "application/json"
|
96
105
|
end
|
97
106
|
|
98
|
-
it "
|
107
|
+
it "allows retrieval via normalized header name" do
|
99
108
|
headers.add :content_type, "application/json"
|
100
109
|
expect(headers["Content-Type"]).to eq "application/json"
|
101
110
|
end
|
102
111
|
|
103
112
|
it "appends new value if header exists" do
|
104
|
-
headers.add
|
113
|
+
headers.add "Set-Cookie", "hoo=ray"
|
105
114
|
headers.add :set_cookie, "woo=hoo"
|
106
115
|
expect(headers["Set-Cookie"]).to eq %w[hoo=ray woo=hoo]
|
107
116
|
end
|
@@ -117,8 +126,20 @@ RSpec.describe HTTP::Headers do
|
|
117
126
|
to raise_error HTTP::HeaderError
|
118
127
|
end
|
119
128
|
|
120
|
-
|
121
|
-
|
129
|
+
["foo bar", "foo bar: ok\nfoo"].each do |name|
|
130
|
+
it "fails with invalid header name (#{name.inspect})" do
|
131
|
+
expect { headers.add name, "baz" }.
|
132
|
+
to raise_error HTTP::HeaderError
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
it "fails with invalid header value" do
|
137
|
+
expect { headers.add "foo", "bar\nEvil-Header: evil-value" }.
|
138
|
+
to raise_error HTTP::HeaderError
|
139
|
+
end
|
140
|
+
|
141
|
+
it "fails when header name is not a String or Symbol" do
|
142
|
+
expect { headers.add 2, "foo" }.
|
122
143
|
to raise_error HTTP::HeaderError
|
123
144
|
end
|
124
145
|
end
|
@@ -145,9 +166,11 @@ RSpec.describe HTTP::Headers do
|
|
145
166
|
to raise_error HTTP::HeaderError
|
146
167
|
end
|
147
168
|
|
148
|
-
|
149
|
-
|
150
|
-
|
169
|
+
["foo bar", "foo bar: ok\nfoo"].each do |name|
|
170
|
+
it "fails with invalid header name (#{name.inspect})" do
|
171
|
+
expect { headers.get name }.
|
172
|
+
to raise_error HTTP::HeaderError
|
173
|
+
end
|
151
174
|
end
|
152
175
|
end
|
153
176
|
|
@@ -296,8 +319,19 @@ RSpec.describe HTTP::Headers do
|
|
296
319
|
)
|
297
320
|
end
|
298
321
|
|
322
|
+
it "yields header keys specified as symbols in normalized form" do
|
323
|
+
keys = headers.each.map(&:first)
|
324
|
+
expect(keys).to eq(%w[Set-Cookie Content-Type Set-Cookie])
|
325
|
+
end
|
326
|
+
|
327
|
+
it "yields headers specified as strings without conversion" do
|
328
|
+
headers.add "X_kEy", "value"
|
329
|
+
keys = headers.each.map(&:first)
|
330
|
+
expect(keys).to eq(%w[Set-Cookie Content-Type Set-Cookie X_kEy])
|
331
|
+
end
|
332
|
+
|
299
333
|
it "returns self instance if block given" do
|
300
|
-
expect(headers.each { |*| }).to be headers
|
334
|
+
expect(headers.each { |*| }).to be headers # rubocop:disable Lint/EmptyBlock
|
301
335
|
end
|
302
336
|
|
303
337
|
it "returns Enumerator if no block given" do
|
@@ -472,14 +506,15 @@ RSpec.describe HTTP::Headers do
|
|
472
506
|
end
|
473
507
|
|
474
508
|
context "with duplicate header keys (mixed case)" do
|
475
|
-
let(:headers) { {"Set-Cookie" => "hoo=ray", "
|
509
|
+
let(:headers) { {"Set-Cookie" => "hoo=ray", "set_cookie" => "woo=hoo", :set_cookie => "ta=da"} }
|
476
510
|
|
477
511
|
it "adds all headers" do
|
478
512
|
expect(described_class.coerce(headers).to_a).
|
479
513
|
to match_array(
|
480
514
|
[
|
481
515
|
%w[Set-Cookie hoo=ray],
|
482
|
-
%w[
|
516
|
+
%w[set_cookie woo=hoo],
|
517
|
+
%w[Set-Cookie ta=da]
|
483
518
|
]
|
484
519
|
)
|
485
520
|
end
|
@@ -8,7 +8,7 @@ RSpec.describe HTTP::Options, "headers" do
|
|
8
8
|
end
|
9
9
|
|
10
10
|
it "may be specified with with_headers" do
|
11
|
-
opts2 = opts.with_headers(
|
11
|
+
opts2 = opts.with_headers(:accept => "json")
|
12
12
|
expect(opts.headers).to be_empty
|
13
13
|
expect(opts2.headers).to eq([%w[Accept json]])
|
14
14
|
end
|
@@ -18,28 +18,28 @@ RSpec.describe HTTP::Options, "merge" do
|
|
18
18
|
# FIXME: yuck :(
|
19
19
|
|
20
20
|
foo = HTTP::Options.new(
|
21
|
-
:response
|
22
|
-
:params
|
23
|
-
:form
|
24
|
-
:body
|
25
|
-
:json
|
26
|
-
:headers
|
27
|
-
:proxy
|
28
|
-
:features
|
21
|
+
:response => :body,
|
22
|
+
:params => {:baz => "bar"},
|
23
|
+
:form => {:foo => "foo"},
|
24
|
+
:body => "body-foo",
|
25
|
+
:json => {:foo => "foo"},
|
26
|
+
:headers => {:accept => "json", :foo => "foo"},
|
27
|
+
:proxy => {},
|
28
|
+
:features => {}
|
29
29
|
)
|
30
30
|
|
31
31
|
bar = HTTP::Options.new(
|
32
|
-
:response
|
33
|
-
:persistent
|
34
|
-
:params
|
35
|
-
:form
|
36
|
-
:body
|
37
|
-
:json
|
32
|
+
:response => :parsed_body,
|
33
|
+
:persistent => "https://www.googe.com",
|
34
|
+
:params => {:plop => "plip"},
|
35
|
+
:form => {:bar => "bar"},
|
36
|
+
:body => "body-bar",
|
37
|
+
:json => {:bar => "bar"},
|
38
38
|
:keep_alive_timeout => 10,
|
39
39
|
:headers => {:accept => "xml", :bar => "bar"},
|
40
40
|
:timeout_options => {:foo => :bar},
|
41
|
-
:ssl
|
42
|
-
:proxy
|
41
|
+
:ssl => {:foo => "bar"},
|
42
|
+
:proxy => {:proxy_address => "127.0.0.1", :proxy_port => 8080}
|
43
43
|
)
|
44
44
|
|
45
45
|
expect(foo.merge(bar).to_hash).to eq(
|
@@ -1,5 +1,5 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
1
|
# coding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
3
|
|
4
4
|
RSpec.describe HTTP::Request::Writer do
|
5
5
|
let(:io) { StringIO.new }
|
@@ -22,6 +22,18 @@ RSpec.describe HTTP::Request::Writer do
|
|
22
22
|
end
|
23
23
|
end
|
24
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
|
+
|
25
37
|
context "when body is nonempty" do
|
26
38
|
let(:body) { HTTP::Request::Body.new("content") }
|
27
39
|
|
@@ -1,5 +1,5 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
1
|
# coding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
3
|
|
4
4
|
RSpec.describe HTTP::Request do
|
5
5
|
let(:proxy) { {} }
|
@@ -8,10 +8,10 @@ RSpec.describe HTTP::Request do
|
|
8
8
|
|
9
9
|
subject :request do
|
10
10
|
HTTP::Request.new(
|
11
|
-
:verb
|
12
|
-
:uri
|
13
|
-
:headers
|
14
|
-
:proxy
|
11
|
+
:verb => :get,
|
12
|
+
:uri => request_uri,
|
13
|
+
:headers => headers,
|
14
|
+
:proxy => proxy
|
15
15
|
)
|
16
16
|
end
|
17
17
|
|
@@ -3,14 +3,14 @@
|
|
3
3
|
RSpec.describe HTTP::Response::Parser do
|
4
4
|
subject(:parser) { described_class.new }
|
5
5
|
let(:raw_response) do
|
6
|
-
"HTTP/1.1 200 OK\r\nContent-Length: 2\r\nContent-Type: application/json\r\
|
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
7
|
end
|
8
8
|
let(:expected_headers) do
|
9
9
|
{
|
10
10
|
"Content-Length" => "2",
|
11
11
|
"Content-Type" => "application/json",
|
12
|
-
"
|
13
|
-
"
|
12
|
+
"MyHeader" => "val",
|
13
|
+
"EmptyHeader" => ""
|
14
14
|
}
|
15
15
|
end
|
16
16
|
let(:expected_body) { "{}" }
|
@@ -32,7 +32,7 @@ RSpec.describe HTTP::Response::Parser do
|
|
32
32
|
end
|
33
33
|
|
34
34
|
context "response in many parts" do
|
35
|
-
let(:parts) { raw_response.
|
35
|
+
let(:parts) { raw_response.chars }
|
36
36
|
|
37
37
|
it "parses headers" do
|
38
38
|
expect(subject.headers.to_h).to eq(expected_headers)
|
@@ -42,4 +42,33 @@ RSpec.describe HTTP::Response::Parser do
|
|
42
42
|
expect(subject.read(expected_body.size)).to eq(expected_body)
|
43
43
|
end
|
44
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
|
45
74
|
end
|
@@ -26,7 +26,7 @@ RSpec.describe HTTP::Response::Status do
|
|
26
26
|
end
|
27
27
|
|
28
28
|
described_class::REASONS.each do |code, reason|
|
29
|
-
class_eval <<-RUBY
|
29
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
30
30
|
context 'with well-known code: #{code}' do
|
31
31
|
let(:code) { #{code} }
|
32
32
|
it { is_expected.to eq #{reason.inspect} }
|
@@ -165,7 +165,7 @@ RSpec.describe HTTP::Response::Status do
|
|
165
165
|
end
|
166
166
|
|
167
167
|
described_class::SYMBOLS.each do |code, symbol|
|
168
|
-
class_eval <<-RUBY
|
168
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
169
169
|
context 'with well-known code: #{code}' do
|
170
170
|
let(:code) { #{code} }
|
171
171
|
it { is_expected.to be #{symbol.inspect} }
|
@@ -193,7 +193,7 @@ RSpec.describe HTTP::Response::Status do
|
|
193
193
|
end
|
194
194
|
|
195
195
|
described_class::SYMBOLS.each do |code, symbol|
|
196
|
-
class_eval <<-RUBY
|
196
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
197
197
|
describe '##{symbol}?' do
|
198
198
|
subject { status.#{symbol}? }
|
199
199
|
|
@@ -11,7 +11,8 @@ RSpec.describe HTTP::Response do
|
|
11
11
|
:version => "1.1",
|
12
12
|
:headers => headers,
|
13
13
|
:body => body,
|
14
|
-
:uri => uri
|
14
|
+
:uri => uri,
|
15
|
+
:request => HTTP::Request.new(:verb => :get, :uri => "http://example.com")
|
15
16
|
)
|
16
17
|
end
|
17
18
|
|
@@ -86,32 +87,19 @@ RSpec.describe HTTP::Response do
|
|
86
87
|
end
|
87
88
|
|
88
89
|
describe "#parse" do
|
89
|
-
let(:headers) { {"Content-Type" =>
|
90
|
+
let(:headers) { {"Content-Type" => "application/json"} }
|
90
91
|
let(:body) { '{"foo":"bar"}' }
|
91
92
|
|
92
|
-
|
93
|
-
|
94
|
-
it "returns parsed body" do
|
95
|
-
expect(response.parse).to eq "foo" => "bar"
|
96
|
-
end
|
93
|
+
it "fails if MIME type decoder is not found" do
|
94
|
+
expect { response.parse "text/html" }.to raise_error(HTTP::Error)
|
97
95
|
end
|
98
96
|
|
99
|
-
|
100
|
-
|
101
|
-
it "raises HTTP::Error" do
|
102
|
-
expect { response.parse }.to raise_error HTTP::Error
|
103
|
-
end
|
97
|
+
it "uses decoder found by given MIME type" do
|
98
|
+
expect(response.parse("application/json")).to eq("foo" => "bar")
|
104
99
|
end
|
105
100
|
|
106
|
-
|
107
|
-
|
108
|
-
it "ignores mime_type of response" do
|
109
|
-
expect(response.parse("application/json")).to eq "foo" => "bar"
|
110
|
-
end
|
111
|
-
|
112
|
-
it "supports MIME type aliases" do
|
113
|
-
expect(response.parse(:json)).to eq "foo" => "bar"
|
114
|
-
end
|
101
|
+
it "uses decoder found by given MIME type alias" do
|
102
|
+
expect(response.parse(:json)).to eq("foo" => "bar")
|
115
103
|
end
|
116
104
|
end
|
117
105
|
|
@@ -165,7 +153,8 @@ RSpec.describe HTTP::Response do
|
|
165
153
|
HTTP::Response.new(
|
166
154
|
:version => "1.1",
|
167
155
|
:status => 200,
|
168
|
-
:connection => connection
|
156
|
+
:connection => connection,
|
157
|
+
:request => HTTP::Request.new(:verb => :get, :uri => "http://example.com")
|
169
158
|
)
|
170
159
|
end
|
171
160
|
|
data/spec/lib/http_spec.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
1
|
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require "json"
|
5
5
|
|
@@ -95,7 +95,8 @@ RSpec.describe HTTP do
|
|
95
95
|
expect(response.to_s).to match(/<!doctype html>/)
|
96
96
|
end
|
97
97
|
|
98
|
-
|
98
|
+
# TODO: htt:s://github.com/httprb/http/issues/627
|
99
|
+
xcontext "ssl" do
|
99
100
|
it "responds with the endpoint's body" do
|
100
101
|
response = ssl_client.via(proxy.addr, proxy.port).get dummy_ssl.endpoint
|
101
102
|
expect(response.to_s).to match(/<!doctype html>/)
|
@@ -131,7 +132,8 @@ RSpec.describe HTTP do
|
|
131
132
|
expect(response.status).to eq(407)
|
132
133
|
end
|
133
134
|
|
134
|
-
|
135
|
+
# TODO: htt:s://github.com/httprb/http/issues/627
|
136
|
+
xcontext "ssl" do
|
135
137
|
it "responds with the endpoint's body" do
|
136
138
|
response = ssl_client.via(proxy.addr, proxy.port, "username", "password").get dummy_ssl.endpoint
|
137
139
|
expect(response.to_s).to match(/<!doctype html>/)
|
@@ -307,6 +309,15 @@ RSpec.describe HTTP do
|
|
307
309
|
end
|
308
310
|
end
|
309
311
|
|
312
|
+
context "specifying per operation timeouts as frozen hash" do
|
313
|
+
let(:frozen_options) { {:read => 123}.freeze }
|
314
|
+
subject(:client) { HTTP.timeout(frozen_options) }
|
315
|
+
|
316
|
+
it "does not raise an error" do
|
317
|
+
expect { client }.not_to raise_error
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
310
321
|
context "specifying a global timeout" do
|
311
322
|
subject(:client) { HTTP.timeout 123 }
|
312
323
|
|
@@ -429,6 +440,22 @@ RSpec.describe HTTP do
|
|
429
440
|
|
430
441
|
expect(response.to_s).to eq("#{body}-deflated")
|
431
442
|
end
|
443
|
+
|
444
|
+
it "returns empty body for no content response where Content-Encoding is gzip" do
|
445
|
+
client = HTTP.use(:auto_inflate).headers("Accept-Encoding" => "gzip")
|
446
|
+
body = "Hello!"
|
447
|
+
response = client.post("#{dummy.endpoint}/no-content-204", :body => body)
|
448
|
+
|
449
|
+
expect(response.to_s).to eq("")
|
450
|
+
end
|
451
|
+
|
452
|
+
it "returns empty body for no content response where Content-Encoding is deflate" do
|
453
|
+
client = HTTP.use(:auto_inflate).headers("Accept-Encoding" => "deflate")
|
454
|
+
body = "Hello!"
|
455
|
+
response = client.post("#{dummy.endpoint}/no-content-204", :body => body)
|
456
|
+
|
457
|
+
expect(response.to_s).to eq("")
|
458
|
+
end
|
432
459
|
end
|
433
460
|
|
434
461
|
context "with :normalize_uri" do
|
data/spec/spec_helper.rb
CHANGED
@@ -1,19 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new(
|
7
|
-
[
|
8
|
-
SimpleCov::Formatter::HTMLFormatter,
|
9
|
-
Coveralls::SimpleCov::Formatter
|
10
|
-
]
|
11
|
-
)
|
12
|
-
|
13
|
-
SimpleCov.start do
|
14
|
-
add_filter "/spec/"
|
15
|
-
minimum_coverage 80
|
16
|
-
end
|
3
|
+
require_relative "./support/simplecov"
|
4
|
+
require_relative "./support/fuubar" unless ENV["CI"]
|
17
5
|
|
18
6
|
require "http"
|
19
7
|
require "rspec/its"
|
@@ -40,6 +28,13 @@ RSpec.configure do |config|
|
|
40
28
|
mocks.verify_partial_doubles = true
|
41
29
|
end
|
42
30
|
|
31
|
+
# This option will default to `:apply_to_host_groups` in RSpec 4 (and will
|
32
|
+
# have no way to turn it off -- the option exists only for backwards
|
33
|
+
# compatibility in RSpec 3). It causes shared context metadata to be
|
34
|
+
# inherited by the metadata hash of host groups and examples, rather than
|
35
|
+
# triggering implicit auto-inclusion in groups with matching metadata.
|
36
|
+
config.shared_context_metadata_behavior = :apply_to_host_groups
|
37
|
+
|
43
38
|
# These two settings work together to allow you to limit a spec run
|
44
39
|
# to individual examples or groups you care about by tagging them with
|
45
40
|
# `:focus` metadata. When nothing is tagged with `:focus`, all examples
|
@@ -48,17 +43,22 @@ RSpec.configure do |config|
|
|
48
43
|
config.filter_run_excluding :flaky if defined?(JRUBY_VERSION) && ENV["CI"]
|
49
44
|
config.run_all_when_everything_filtered = true
|
50
45
|
|
51
|
-
# Limits the available syntax to the non-monkey patched syntax that is recommended.
|
52
|
-
# For more details, see:
|
53
|
-
# - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
|
54
|
-
# - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
|
55
|
-
# - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching
|
56
|
-
config.disable_monkey_patching!
|
57
|
-
|
58
46
|
# This setting enables warnings. It's recommended, but in some cases may
|
59
47
|
# be too noisy due to issues in dependencies.
|
60
48
|
config.warnings = 0 == ENV["GUARD_RSPEC"].to_i
|
61
49
|
|
50
|
+
# Allows RSpec to persist some state between runs in order to support
|
51
|
+
# the `--only-failures` and `--next-failure` CLI options. We recommend
|
52
|
+
# you configure your source control system to ignore this file.
|
53
|
+
config.example_status_persistence_file_path = "spec/examples.txt"
|
54
|
+
|
55
|
+
# Limits the available syntax to the non-monkey patched syntax that is
|
56
|
+
# recommended. For more details, see:
|
57
|
+
# - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/
|
58
|
+
# - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
|
59
|
+
# - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode
|
60
|
+
config.disable_monkey_patching!
|
61
|
+
|
62
62
|
# Many RSpec users commonly either run the entire suite or an individual
|
63
63
|
# file, and it's useful to allow more verbose output when running an
|
64
64
|
# individual spec file.
|
data/spec/support/black_hole.rb
CHANGED
@@ -1,9 +1,8 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
1
|
# encoding: UTF-8
|
2
|
+
# frozen_string_literal: true
|
3
3
|
|
4
4
|
class DummyServer < WEBrick::HTTPServer
|
5
|
-
# rubocop:disable Metrics/ClassLength
|
6
|
-
class Servlet < WEBrick::HTTPServlet::AbstractServlet
|
5
|
+
class Servlet < WEBrick::HTTPServlet::AbstractServlet # rubocop:disable Metrics/ClassLength
|
7
6
|
def self.sockets
|
8
7
|
@sockets ||= []
|
9
8
|
end
|
@@ -18,7 +17,7 @@ class DummyServer < WEBrick::HTTPServer
|
|
18
17
|
end
|
19
18
|
|
20
19
|
%w[get post head].each do |method|
|
21
|
-
class_eval <<-RUBY, __FILE__, __LINE__
|
20
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
22
21
|
def self.#{method}(path, &block)
|
23
22
|
handlers["#{method}:\#{path}"] = block
|
24
23
|
end
|
@@ -157,7 +156,7 @@ class DummyServer < WEBrick::HTTPServer
|
|
157
156
|
res.status = 200
|
158
157
|
|
159
158
|
res.body = case req["Accept-Encoding"]
|
160
|
-
when "gzip"
|
159
|
+
when "gzip"
|
161
160
|
res["Content-Encoding"] = "gzip"
|
162
161
|
StringIO.open do |out|
|
163
162
|
Zlib::GzipWriter.wrap(out) do |gz|
|
@@ -166,12 +165,24 @@ class DummyServer < WEBrick::HTTPServer
|
|
166
165
|
out.tap(&:rewind).read
|
167
166
|
end
|
168
167
|
end
|
169
|
-
when "deflate"
|
168
|
+
when "deflate"
|
170
169
|
res["Content-Encoding"] = "deflate"
|
171
170
|
Zlib::Deflate.deflate("#{req.body}-deflated")
|
172
171
|
else
|
173
172
|
"#{req.body}-raw"
|
174
173
|
end
|
175
174
|
end
|
175
|
+
|
176
|
+
post "/no-content-204" do |req, res|
|
177
|
+
res.status = 204
|
178
|
+
res.body = ""
|
179
|
+
|
180
|
+
case req["Accept-Encoding"]
|
181
|
+
when "gzip"
|
182
|
+
res["Content-Encoding"] = "gzip"
|
183
|
+
when "deflate"
|
184
|
+
res["Content-Encoding"] = "deflate"
|
185
|
+
end
|
186
|
+
end
|
176
187
|
end
|
177
188
|
end
|
@@ -13,18 +13,18 @@ class DummyServer < WEBrick::HTTPServer
|
|
13
13
|
include ServerConfig
|
14
14
|
|
15
15
|
CONFIG = {
|
16
|
-
:BindAddress
|
17
|
-
:Port
|
18
|
-
:AccessLog
|
19
|
-
:Logger
|
16
|
+
:BindAddress => "127.0.0.1",
|
17
|
+
:Port => 0,
|
18
|
+
:AccessLog => BlackHole,
|
19
|
+
:Logger => BlackHole
|
20
20
|
}.freeze
|
21
21
|
|
22
22
|
SSL_CONFIG = CONFIG.merge(
|
23
|
-
:SSLEnable
|
24
|
-
:SSLStartImmediately
|
23
|
+
:SSLEnable => true,
|
24
|
+
:SSLStartImmediately => true
|
25
25
|
).freeze
|
26
26
|
|
27
|
-
def initialize(options = {})
|
27
|
+
def initialize(options = {})
|
28
28
|
super(options[:ssl] ? SSL_CONFIG : CONFIG)
|
29
29
|
mount("/", Servlet)
|
30
30
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "fuubar"
|
4
|
+
|
5
|
+
RSpec.configure do |config|
|
6
|
+
# Use Fuubar instafail-alike formatter, unless a formatter has already been
|
7
|
+
# configured (e.g. via a command-line flag).
|
8
|
+
config.default_formatter = "Fuubar"
|
9
|
+
|
10
|
+
# Disable auto-refresh of the fuubar progress bar to avoid surprises during
|
11
|
+
# debugiing. And simply because there's next to absolutely no point in having
|
12
|
+
# this turned on.
|
13
|
+
#
|
14
|
+
# > By default fuubar will automatically refresh the bar (and therefore
|
15
|
+
# > the ETA) every second. Unfortunately this doesn't play well with things
|
16
|
+
# > like debuggers. When you're debugging, having a bar show up every second
|
17
|
+
# > is undesireable.
|
18
|
+
#
|
19
|
+
# See: https://github.com/thekompanee/fuubar#disabling-auto-refresh
|
20
|
+
config.fuubar_auto_refresh = false
|
21
|
+
end
|