http 4.2.0 → 5.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 (69) 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 +168 -0
  11. data/Gemfile +18 -10
  12. data/LICENSE.txt +1 -1
  13. data/README.md +17 -20
  14. data/Rakefile +2 -10
  15. data/http.gemspec +5 -5
  16. data/lib/http/chainable.rb +23 -17
  17. data/lib/http/client.rb +52 -35
  18. data/lib/http/connection.rb +12 -8
  19. data/lib/http/content_type.rb +12 -7
  20. data/lib/http/feature.rb +3 -1
  21. data/lib/http/features/auto_deflate.rb +7 -7
  22. data/lib/http/features/auto_inflate.rb +6 -7
  23. data/lib/http/features/instrumentation.rb +1 -1
  24. data/lib/http/features/logging.rb +19 -21
  25. data/lib/http/headers.rb +50 -13
  26. data/lib/http/mime_type/adapter.rb +3 -1
  27. data/lib/http/mime_type/json.rb +1 -0
  28. data/lib/http/options.rb +6 -9
  29. data/lib/http/redirector.rb +4 -2
  30. data/lib/http/request/body.rb +1 -0
  31. data/lib/http/request/writer.rb +8 -3
  32. data/lib/http/request.rb +28 -11
  33. data/lib/http/response/body.rb +6 -4
  34. data/lib/http/response/inflater.rb +1 -1
  35. data/lib/http/response/parser.rb +75 -49
  36. data/lib/http/response/status.rb +4 -3
  37. data/lib/http/response.rb +35 -15
  38. data/lib/http/timeout/global.rb +42 -38
  39. data/lib/http/timeout/null.rb +2 -1
  40. data/lib/http/timeout/per_operation.rb +56 -58
  41. data/lib/http/uri.rb +5 -5
  42. data/lib/http/version.rb +1 -1
  43. data/spec/lib/http/client_spec.rb +173 -35
  44. data/spec/lib/http/connection_spec.rb +8 -5
  45. data/spec/lib/http/features/auto_inflate_spec.rb +3 -2
  46. data/spec/lib/http/features/instrumentation_spec.rb +27 -21
  47. data/spec/lib/http/features/logging_spec.rb +8 -10
  48. data/spec/lib/http/headers_spec.rb +53 -18
  49. data/spec/lib/http/options/headers_spec.rb +1 -1
  50. data/spec/lib/http/options/merge_spec.rb +16 -16
  51. data/spec/lib/http/redirector_spec.rb +59 -1
  52. data/spec/lib/http/request/writer_spec.rb +25 -2
  53. data/spec/lib/http/request_spec.rb +5 -5
  54. data/spec/lib/http/response/body_spec.rb +5 -5
  55. data/spec/lib/http/response/parser_spec.rb +74 -0
  56. data/spec/lib/http/response/status_spec.rb +3 -3
  57. data/spec/lib/http/response_spec.rb +44 -3
  58. data/spec/lib/http_spec.rb +30 -3
  59. data/spec/spec_helper.rb +21 -21
  60. data/spec/support/black_hole.rb +1 -1
  61. data/spec/support/dummy_server/servlet.rb +17 -6
  62. data/spec/support/dummy_server.rb +7 -7
  63. data/spec/support/fuubar.rb +21 -0
  64. data/spec/support/http_handling_shared.rb +4 -4
  65. data/spec/support/simplecov.rb +19 -0
  66. data/spec/support/ssl_helper.rb +4 -4
  67. metadata +24 -16
  68. data/.coveralls.yml +0 -1
  69. data/.travis.yml +0 -37
@@ -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
 
@@ -75,6 +76,19 @@ RSpec.describe HTTP::Redirector do
75
76
  expect(res.to_s).to eq "foo"
76
77
  end
77
78
 
79
+ it "concatenates multiple Location headers" do
80
+ req = HTTP::Request.new :verb => :head, :uri => "http://example.com"
81
+ headers = HTTP::Headers.new
82
+
83
+ %w[http://example.com /123].each { |loc| headers.add("Location", loc) }
84
+
85
+ res = redirector.perform(req, simple_response(301, "", headers)) do |redirect|
86
+ simple_response(200, redirect.uri.to_s)
87
+ end
88
+
89
+ expect(res.to_s).to eq "http://example.com/123"
90
+ end
91
+
78
92
  context "following 300 redirect" do
79
93
  context "with strict mode" do
80
94
  let(:options) { {:strict => true} }
@@ -382,5 +396,49 @@ RSpec.describe HTTP::Redirector do
382
396
  end
383
397
  end
384
398
  end
399
+
400
+ describe "changing verbs during redirects" do
401
+ let(:options) { {:strict => false} }
402
+ let(:post_body) { HTTP::Request::Body.new("i might be way longer in real life") }
403
+ let(:cookie) { "dont eat my cookies" }
404
+
405
+ def a_dangerous_request(verb)
406
+ HTTP::Request.new(
407
+ :verb => verb, :uri => "http://example.com",
408
+ :body => post_body, :headers => {
409
+ "Content-Type" => "meme",
410
+ "Cookie" => cookie
411
+ }
412
+ )
413
+ end
414
+
415
+ def empty_body
416
+ HTTP::Request::Body.new(nil)
417
+ end
418
+
419
+ it "follows without body/content type if it has to change verb" do
420
+ req = a_dangerous_request(:post)
421
+ res = redirect_response 302, "http://example.com/1"
422
+
423
+ redirector.perform(req, res) do |prev_req, _|
424
+ expect(prev_req.body).to eq(empty_body)
425
+ expect(prev_req.headers["Cookie"]).to eq(cookie)
426
+ expect(prev_req.headers["Content-Type"]).to eq(nil)
427
+ simple_response 200
428
+ end
429
+ end
430
+
431
+ it "leaves body/content-type intact if it does not have to change verb" do
432
+ req = a_dangerous_request(:post)
433
+ res = redirect_response 307, "http://example.com/1"
434
+
435
+ redirector.perform(req, res) do |prev_req, _|
436
+ expect(prev_req.body).to eq(post_body)
437
+ expect(prev_req.headers["Cookie"]).to eq(cookie)
438
+ expect(prev_req.headers["Content-Type"]).to eq("meme")
439
+ simple_response 200
440
+ end
441
+ end
442
+ end
385
443
  end
386
444
  end
@@ -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
 
@@ -35,9 +47,20 @@ RSpec.describe HTTP::Request::Writer do
35
47
  end
36
48
  end
37
49
 
38
- context "when body is empty" do
50
+ context "when body is not set" do
39
51
  let(:body) { HTTP::Request::Body.new(nil) }
40
52
 
53
+ it "doesn't write anything to the socket and doesn't set Content-Length" do
54
+ writer.stream
55
+ expect(io.string).to eq [
56
+ "#{headerstart}\r\n\r\n"
57
+ ].join
58
+ end
59
+ end
60
+
61
+ context "when body is empty" do
62
+ let(:body) { HTTP::Request::Body.new("") }
63
+
41
64
  it "doesn't write anything to the socket and sets Content-Length" do
42
65
  writer.stream
43
66
  expect(io.string).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 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
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  RSpec.describe HTTP::Response::Body do
4
4
  let(:connection) { double(:sequence_id => 0) }
5
- let(:chunks) { [String.new("Hello, "), String.new("World!")] }
5
+ let(:chunks) { ["Hello, ", "World!"] }
6
6
 
7
7
  before do
8
8
  allow(connection).to receive(:readpartial) { chunks.shift }
@@ -16,7 +16,7 @@ RSpec.describe HTTP::Response::Body do
16
16
  end
17
17
 
18
18
  context "when body empty" do
19
- let(:chunks) { [String.new("")] }
19
+ let(:chunks) { [""] }
20
20
 
21
21
  it "returns responds to empty? with true" do
22
22
  expect(subject).to be_empty
@@ -45,12 +45,12 @@ RSpec.describe HTTP::Response::Body do
45
45
  it "returns content in specified encoding" do
46
46
  body = described_class.new(connection)
47
47
  expect(connection).to receive(:readpartial).
48
- and_return(String.new("content").force_encoding(Encoding::UTF_8))
48
+ and_return(String.new("content", :encoding => Encoding::UTF_8))
49
49
  expect(body.readpartial.encoding).to eq Encoding::BINARY
50
50
 
51
51
  body = described_class.new(connection, :encoding => Encoding::UTF_8)
52
52
  expect(connection).to receive(:readpartial).
53
- and_return(String.new("content").force_encoding(Encoding::BINARY))
53
+ and_return(String.new("content", :encoding => Encoding::BINARY))
54
54
  expect(body.readpartial.encoding).to eq Encoding::UTF_8
55
55
  end
56
56
  end
@@ -59,7 +59,7 @@ RSpec.describe HTTP::Response::Body do
59
59
  let(:chunks) do
60
60
  body = Zlib::Deflate.deflate("Hi, HTTP here ☺")
61
61
  len = body.length
62
- [String.new(body[0, len / 2]), String.new(body[(len / 2)..-1])]
62
+ [body[0, len / 2], body[(len / 2)..-1]]
63
63
  end
64
64
  subject(:body) do
65
65
  inflater = HTTP::Response::Inflater.new(connection)
@@ -0,0 +1,74 @@
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
@@ -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
 
@@ -4,6 +4,7 @@ RSpec.describe HTTP::Response do
4
4
  let(:body) { "Hello world!" }
5
5
  let(:uri) { "http://example.com/" }
6
6
  let(:headers) { {} }
7
+ let(:request) { HTTP::Request.new(:verb => :get, :uri => uri) }
7
8
 
8
9
  subject(:response) do
9
10
  HTTP::Response.new(
@@ -11,7 +12,7 @@ RSpec.describe HTTP::Response do
11
12
  :version => "1.1",
12
13
  :headers => headers,
13
14
  :body => body,
14
- :uri => uri
15
+ :request => request
15
16
  )
16
17
  end
17
18
 
@@ -109,7 +110,7 @@ RSpec.describe HTTP::Response do
109
110
  expect(response.parse("application/json")).to eq "foo" => "bar"
110
111
  end
111
112
 
112
- it "supports MIME type aliases" do
113
+ it "supports mime type aliases" do
113
114
  expect(response.parse(:json)).to eq "foo" => "bar"
114
115
  end
115
116
  end
@@ -165,7 +166,8 @@ RSpec.describe HTTP::Response do
165
166
  HTTP::Response.new(
166
167
  :version => "1.1",
167
168
  :status => 200,
168
- :connection => connection
169
+ :connection => connection,
170
+ :request => request
169
171
  )
170
172
  end
171
173
 
@@ -182,4 +184,43 @@ RSpec.describe HTTP::Response do
182
184
  end
183
185
  it { is_expected.not_to be_chunked }
184
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
185
226
  end
@@ -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
@@ -14,11 +14,11 @@ RSpec.shared_context "HTTP handling" do
14
14
 
15
15
  let(:options) do
16
16
  {
17
- :timeout_class => HTTP::Timeout::PerOperation,
17
+ :timeout_class => HTTP::Timeout::PerOperation,
18
18
  :timeout_options => {
19
19
  :connect_timeout => conn_timeout,
20
- :read_timeout => read_timeout,
21
- :write_timeout => write_timeout
20
+ :read_timeout => read_timeout,
21
+ :write_timeout => write_timeout
22
22
  }
23
23
  }
24
24
  end
@@ -62,7 +62,7 @@ RSpec.shared_context "HTTP handling" do
62
62
  context "with a global timeout" do
63
63
  let(:options) do
64
64
  {
65
- :timeout_class => HTTP::Timeout::Global,
65
+ :timeout_class => HTTP::Timeout::Global,
66
66
  :timeout_options => {
67
67
  :global_timeout => global_timeout
68
68
  }
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "simplecov"
4
+
5
+ if ENV["CI"]
6
+ require "simplecov-lcov"
7
+
8
+ SimpleCov::Formatter::LcovFormatter.config do |config|
9
+ config.report_with_single_file = true
10
+ config.lcov_file_name = "lcov.info"
11
+ end
12
+
13
+ SimpleCov.formatter = SimpleCov::Formatter::LcovFormatter
14
+ end
15
+
16
+ SimpleCov.start do
17
+ add_filter "/spec/"
18
+ minimum_coverage 80
19
+ end