http 4.4.1 → 5.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|