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.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +65 -0
  3. data/.gitignore +6 -10
  4. data/.rspec +0 -4
  5. data/.rubocop/layout.yml +8 -0
  6. data/.rubocop/style.yml +32 -0
  7. data/.rubocop.yml +8 -110
  8. data/.rubocop_todo.yml +192 -0
  9. data/.yardopts +1 -1
  10. data/CHANGES.md +87 -3
  11. data/Gemfile +18 -10
  12. data/README.md +17 -20
  13. data/Rakefile +2 -10
  14. data/http.gemspec +3 -3
  15. data/lib/http/chainable.rb +23 -17
  16. data/lib/http/client.rb +36 -30
  17. data/lib/http/connection.rb +11 -7
  18. data/lib/http/content_type.rb +12 -7
  19. data/lib/http/feature.rb +3 -1
  20. data/lib/http/features/auto_deflate.rb +6 -6
  21. data/lib/http/features/auto_inflate.rb +6 -5
  22. data/lib/http/features/instrumentation.rb +1 -1
  23. data/lib/http/features/logging.rb +19 -21
  24. data/lib/http/headers.rb +50 -13
  25. data/lib/http/mime_type/adapter.rb +3 -1
  26. data/lib/http/mime_type/json.rb +1 -0
  27. data/lib/http/options.rb +5 -8
  28. data/lib/http/redirector.rb +2 -1
  29. data/lib/http/request/body.rb +1 -0
  30. data/lib/http/request/writer.rb +3 -2
  31. data/lib/http/request.rb +13 -10
  32. data/lib/http/response/body.rb +2 -2
  33. data/lib/http/response/inflater.rb +1 -1
  34. data/lib/http/response/parser.rb +74 -62
  35. data/lib/http/response/status.rb +4 -3
  36. data/lib/http/response.rb +17 -15
  37. data/lib/http/timeout/global.rb +17 -31
  38. data/lib/http/timeout/null.rb +2 -1
  39. data/lib/http/timeout/per_operation.rb +31 -54
  40. data/lib/http/uri.rb +5 -5
  41. data/lib/http/version.rb +1 -1
  42. data/spec/lib/http/client_spec.rb +119 -30
  43. data/spec/lib/http/connection_spec.rb +8 -5
  44. data/spec/lib/http/features/auto_inflate_spec.rb +4 -2
  45. data/spec/lib/http/features/instrumentation_spec.rb +28 -21
  46. data/spec/lib/http/features/logging_spec.rb +8 -9
  47. data/spec/lib/http/headers_spec.rb +53 -18
  48. data/spec/lib/http/options/headers_spec.rb +1 -1
  49. data/spec/lib/http/options/merge_spec.rb +16 -16
  50. data/spec/lib/http/redirector_spec.rb +2 -1
  51. data/spec/lib/http/request/writer_spec.rb +13 -1
  52. data/spec/lib/http/request_spec.rb +5 -5
  53. data/spec/lib/http/response/parser_spec.rb +33 -4
  54. data/spec/lib/http/response/status_spec.rb +3 -3
  55. data/spec/lib/http/response_spec.rb +11 -22
  56. data/spec/lib/http_spec.rb +30 -3
  57. data/spec/spec_helper.rb +21 -21
  58. data/spec/support/black_hole.rb +1 -1
  59. data/spec/support/dummy_server/servlet.rb +17 -6
  60. data/spec/support/dummy_server.rb +7 -7
  61. data/spec/support/fuubar.rb +21 -0
  62. data/spec/support/http_handling_shared.rb +4 -4
  63. data/spec/support/simplecov.rb +19 -0
  64. data/spec/support/ssl_helper.rb +4 -4
  65. metadata +18 -12
  66. data/.coveralls.yml +0 -1
  67. 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 "normalizes header name" do
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
- it "fails with invalid header name" do
39
- expect { headers.set "foo bar", "baz" }.
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 "normalizes header name" do
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 "normalizes header name" do
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
- it "fails with invalid header name" do
87
- expect { headers.delete "foo bar" }.
88
- to raise_error HTTP::HeaderError
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 "normalizes header name" do
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 :set_cookie, "hoo=ray"
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
- it "fails with invalid header name" do
121
- expect { headers.add "foo bar", "baz" }.
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
- it "fails with invalid header name" do
149
- expect { headers.get("foo bar") }.
150
- to raise_error HTTP::HeaderError
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", "set-cookie" => "woo=hoo"} }
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[Set-Cookie woo=hoo]
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("accept" => "json")
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 => :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 => {}
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 => :parsed_body,
33
- :persistent => "https://www.googe.com",
34
- :params => {:plop => "plip"},
35
- :form => {:bar => "bar"},
36
- :body => "body-bar",
37
- :json => {:bar => "bar"},
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 => {:foo => "bar"},
42
- :proxy => {:proxy_address => "127.0.0.1", :proxy_port => 8080}
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(
@@ -6,7 +6,8 @@ RSpec.describe HTTP::Redirector do
6
6
  :status => status,
7
7
  :version => "1.1",
8
8
  :headers => headers,
9
- :body => body
9
+ :body => body,
10
+ :request => HTTP::Request.new(:verb => :get, :uri => "http://example.com")
10
11
  )
11
12
  end
12
13
 
@@ -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 => :get,
12
- :uri => request_uri,
13
- :headers => headers,
14
- :proxy => 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\nMy-Header: val\r\nEmpty-Header: \r\n\r\n{}"
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
- "My-Header" => "val",
13
- "Empty-Header" => ""
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.split(//) }
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" => content_type} }
90
+ let(:headers) { {"Content-Type" => "application/json"} }
90
91
  let(:body) { '{"foo":"bar"}' }
91
92
 
92
- context "with known content type" do
93
- let(:content_type) { "application/json" }
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
- context "with unknown content type" do
100
- let(:content_type) { "application/deadbeef" }
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
- context "with explicitly given mime type" do
107
- let(:content_type) { "application/deadbeef" }
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
 
@@ -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
- context "ssl" do
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
- context "ssl" do
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
- require "simplecov"
4
- require "coveralls"
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.
@@ -2,7 +2,7 @@
2
2
 
3
3
  module BlackHole
4
4
  class << self
5
- def method_missing(*) # rubocop: disable Style/MethodMissing
5
+ def method_missing(*)
6
6
  self
7
7
  end
8
8
 
@@ -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" then
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" then
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 => "127.0.0.1",
17
- :Port => 0,
18
- :AccessLog => BlackHole,
19
- :Logger => BlackHole
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 => true,
24
- :SSLStartImmediately => true
23
+ :SSLEnable => true,
24
+ :SSLStartImmediately => true
25
25
  ).freeze
26
26
 
27
- def initialize(options = {}) # rubocop:disable Style/OptionHash
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