http 4.2.0 → 5.0.2

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