http 2.2.2 → 3.0.0.pre

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 (75) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +46 -13
  3. data/.travis.yml +17 -12
  4. data/CHANGES.md +25 -1
  5. data/Gemfile +11 -4
  6. data/Guardfile +2 -0
  7. data/README.md +4 -5
  8. data/Rakefile +14 -13
  9. data/http.gemspec +3 -1
  10. data/lib/http.rb +1 -0
  11. data/lib/http/chainable.rb +15 -14
  12. data/lib/http/client.rb +27 -24
  13. data/lib/http/connection.rb +6 -4
  14. data/lib/http/content_type.rb +1 -0
  15. data/lib/http/errors.rb +3 -2
  16. data/lib/http/feature.rb +2 -1
  17. data/lib/http/features/auto_deflate.rb +77 -20
  18. data/lib/http/features/auto_inflate.rb +2 -1
  19. data/lib/http/headers.rb +3 -2
  20. data/lib/http/headers/known.rb +23 -22
  21. data/lib/http/headers/mixin.rb +1 -0
  22. data/lib/http/mime_type.rb +1 -0
  23. data/lib/http/mime_type/adapter.rb +2 -1
  24. data/lib/http/mime_type/json.rb +2 -1
  25. data/lib/http/options.rb +15 -12
  26. data/lib/http/redirector.rb +4 -3
  27. data/lib/http/request.rb +25 -10
  28. data/lib/http/request/body.rb +67 -0
  29. data/lib/http/request/writer.rb +32 -37
  30. data/lib/http/response.rb +17 -2
  31. data/lib/http/response/body.rb +16 -12
  32. data/lib/http/response/parser.rb +1 -0
  33. data/lib/http/response/status.rb +1 -0
  34. data/lib/http/response/status/reasons.rb +1 -0
  35. data/lib/http/timeout/global.rb +1 -0
  36. data/lib/http/timeout/null.rb +2 -1
  37. data/lib/http/timeout/per_operation.rb +19 -6
  38. data/lib/http/uri.rb +8 -2
  39. data/lib/http/version.rb +1 -1
  40. data/spec/lib/http/client_spec.rb +104 -4
  41. data/spec/lib/http/content_type_spec.rb +1 -0
  42. data/spec/lib/http/features/auto_deflate_spec.rb +32 -64
  43. data/spec/lib/http/features/auto_inflate_spec.rb +1 -0
  44. data/spec/lib/http/headers/mixin_spec.rb +1 -0
  45. data/spec/lib/http/headers_spec.rb +36 -35
  46. data/spec/lib/http/options/body_spec.rb +1 -0
  47. data/spec/lib/http/options/features_spec.rb +1 -0
  48. data/spec/lib/http/options/form_spec.rb +1 -0
  49. data/spec/lib/http/options/headers_spec.rb +2 -1
  50. data/spec/lib/http/options/json_spec.rb +1 -0
  51. data/spec/lib/http/options/new_spec.rb +2 -1
  52. data/spec/lib/http/options/proxy_spec.rb +1 -0
  53. data/spec/lib/http/options_spec.rb +1 -0
  54. data/spec/lib/http/redirector_spec.rb +1 -0
  55. data/spec/lib/http/request/body_spec.rb +138 -0
  56. data/spec/lib/http/request/writer_spec.rb +44 -74
  57. data/spec/lib/http/request_spec.rb +14 -0
  58. data/spec/lib/http/response/body_spec.rb +20 -4
  59. data/spec/lib/http/response/status_spec.rb +27 -26
  60. data/spec/lib/http/response_spec.rb +10 -0
  61. data/spec/lib/http/uri_spec.rb +11 -0
  62. data/spec/lib/http_spec.rb +18 -6
  63. data/spec/regression_specs.rb +1 -0
  64. data/spec/spec_helper.rb +1 -0
  65. data/spec/support/black_hole.rb +9 -2
  66. data/spec/support/capture_warning.rb +1 -0
  67. data/spec/support/dummy_server.rb +2 -1
  68. data/spec/support/dummy_server/servlet.rb +1 -1
  69. data/spec/support/fakeio.rb +21 -0
  70. data/spec/support/http_handling_shared.rb +1 -0
  71. data/spec/support/proxy_server.rb +1 -0
  72. data/spec/support/servers/config.rb +1 -0
  73. data/spec/support/servers/runner.rb +1 -0
  74. data/spec/support/ssl_helper.rb +3 -2
  75. metadata +20 -9
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module HTTP
3
4
  class Response
4
5
  class Parser
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require "delegate"
3
4
 
4
5
  require "http/response/status/reasons"
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  # AUTO-GENERATED FILE, DO NOT CHANGE IT MANUALLY
3
4
 
4
5
  require "delegate"
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require "timeout"
3
4
  require "io/wait"
4
5
 
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require "forwardable"
3
4
  require "io/wait"
4
5
 
@@ -11,7 +12,7 @@ module HTTP
11
12
 
12
13
  attr_reader :options, :socket
13
14
 
14
- def initialize(options = {})
15
+ def initialize(options = {}) # rubocop:disable Style/OptionHash
15
16
  @options = options
16
17
  end
17
18
 
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require "timeout"
3
4
 
4
5
  require "http/timeout/null"
@@ -59,29 +60,41 @@ module HTTP
59
60
  else
60
61
  # Read data from the socket
61
62
  def readpartial(size)
63
+ timeout = false
62
64
  loop do
63
65
  result = @socket.read_nonblock(size, :exception => false)
64
66
 
65
67
  return :eof if result.nil?
66
68
  return result if result != :wait_readable
67
69
 
68
- unless IO.select([@socket], nil, nil, read_timeout)
69
- raise TimeoutError, "Read timed out after #{read_timeout} seconds"
70
- end
70
+ raise TimeoutError, "Read timed out after #{read_timeout} seconds" if timeout
71
+ # marking the socket for timeout. Why is this not being raised immediately?
72
+ # it seems there is some race-condition on the network level between calling
73
+ # #read_nonblock and #wait_readable, in which #read_nonblock signalizes waiting
74
+ # for reads, and when waiting for x seconds, it returns nil suddenly without completing
75
+ # the x seconds. In a normal case this would be a timeout on wait/read, but it can
76
+ # also mean that the socket has been closed by the server. Therefore we "mark" the
77
+ # socket for timeout and try to read more bytes. If it returns :eof, it's all good, no
78
+ # timeout. Else, the first timeout was a proper timeout.
79
+ # This hack has to be done because io/wait#wait_readable doesn't provide a value for when
80
+ # the socket is closed by the server, and HTTP::Parser doesn't provide the limit for the chunks.
81
+ timeout = true unless @socket.to_io.wait_readable(read_timeout)
71
82
  end
72
83
  end
73
84
 
74
85
  # Write data to the socket
75
86
  def write(data)
87
+ timeout = false
76
88
  loop do
77
89
  result = @socket.write_nonblock(data, :exception => false)
78
90
  return result unless result == :wait_writable
79
91
 
80
- unless IO.select(nil, [@socket], nil, write_timeout)
81
- raise TimeoutError, "Write timed out after #{write_timeout} seconds"
82
- end
92
+ raise TimeoutError, "Write timed out after #{write_timeout} seconds" if timeout
93
+
94
+ timeout = true unless @socket.to_io.wait_writable(write_timeout)
83
95
  end
84
96
  end
97
+
85
98
  end
86
99
  end
87
100
  end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require "addressable/uri"
3
4
 
4
5
  module HTTP
@@ -19,10 +20,10 @@ module HTTP
19
20
  def_delegators :@uri, :omit, :join, :normalize
20
21
 
21
22
  # @private
22
- HTTP_SCHEME = "http".freeze
23
+ HTTP_SCHEME = "http"
23
24
 
24
25
  # @private
25
- HTTPS_SCHEME = "https".freeze
26
+ HTTPS_SCHEME = "https"
26
27
 
27
28
  # Parse the given URI string, returning an HTTP::URI object
28
29
  #
@@ -114,6 +115,11 @@ module HTTP
114
115
  HTTPS_SCHEME == scheme
115
116
  end
116
117
 
118
+ # @return [Object] duplicated URI
119
+ def dup
120
+ self.class.new @uri.dup
121
+ end
122
+
117
123
  # Convert an HTTP::URI to a String
118
124
  #
119
125
  # @return [String] URI serialized as a String
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module HTTP
4
- VERSION = "2.2.2".freeze
4
+ VERSION = "3.0.0.pre"
5
5
  end
@@ -111,7 +111,7 @@ RSpec.describe HTTP::Client do
111
111
 
112
112
  it "accepts params within the provided URL" do
113
113
  expect(HTTP::Request).to receive(:new) do |opts|
114
- expect(CGI.parse(opts[:uri].query)).to eq("foo" => %w(bar))
114
+ expect(CGI.parse(opts[:uri].query)).to eq("foo" => %w[bar])
115
115
  end
116
116
 
117
117
  client.get("http://example.com/?foo=bar")
@@ -119,7 +119,7 @@ RSpec.describe HTTP::Client do
119
119
 
120
120
  it "combines GET params from the URI with the passed in params" do
121
121
  expect(HTTP::Request).to receive(:new) do |opts|
122
- expect(CGI.parse(opts[:uri].query)).to eq("foo" => %w(bar), "baz" => %w(quux))
122
+ expect(CGI.parse(opts[:uri].query)).to eq("foo" => %w[bar], "baz" => %w[quux])
123
123
  end
124
124
 
125
125
  client.get("http://example.com/?foo=bar", :params => {:baz => "quux"})
@@ -143,7 +143,7 @@ RSpec.describe HTTP::Client do
143
143
 
144
144
  it "does not corrupts index-less arrays" do
145
145
  expect(HTTP::Request).to receive(:new) do |opts|
146
- expect(CGI.parse(opts[:uri].query)).to eq "a[]" => %w(b c), "d" => %w(e)
146
+ expect(CGI.parse(opts[:uri].query)).to eq "a[]" => %w[b c], "d" => %w[e]
147
147
  end
148
148
 
149
149
  client.get("http://example.com/?a[]=b&a[]=c", :params => {:d => "e"})
@@ -158,6 +158,32 @@ RSpec.describe HTTP::Client do
158
158
  end
159
159
  end
160
160
 
161
+ describe "passing multipart form data" do
162
+ it "creates url encoded form data object" do
163
+ client = HTTP::Client.new
164
+ allow(client).to receive(:perform)
165
+
166
+ expect(HTTP::Request).to receive(:new) do |opts|
167
+ expect(opts[:body]).to be_a(HTTP::FormData::Urlencoded)
168
+ expect(opts[:body].to_s).to eq "foo=bar"
169
+ end
170
+
171
+ client.get("http://example.com/", :form => {:foo => "bar"})
172
+ end
173
+
174
+ it "creates multipart form data object" do
175
+ client = HTTP::Client.new
176
+ allow(client).to receive(:perform)
177
+
178
+ expect(HTTP::Request).to receive(:new) do |opts|
179
+ expect(opts[:body]).to be_a(HTTP::FormData::Multipart)
180
+ expect(opts[:body].to_s).to include("content")
181
+ end
182
+
183
+ client.get("http://example.com/", :form => {:foo => HTTP::FormData::Part.new("content")})
184
+ end
185
+ end
186
+
161
187
  describe "passing json" do
162
188
  it "encodes given object" do
163
189
  client = HTTP::Client.new
@@ -197,6 +223,27 @@ RSpec.describe HTTP::Client do
197
223
  client.request(:get, "http://example.com/")
198
224
  end
199
225
  end
226
+
227
+ context "when :auto_deflate was specified" do
228
+ let(:headers) { {"Content-Length" => "12"} }
229
+ let(:client) { described_class.new :headers => headers, :features => {:auto_deflate => {}} }
230
+
231
+ it "deletes Content-Length header" do
232
+ expect(client).to receive(:perform) do |req, _|
233
+ expect(req["Content-Length"]).to eq nil
234
+ end
235
+
236
+ client.request(:get, "http://example.com/")
237
+ end
238
+
239
+ it "sets Content-Encoding header" do
240
+ expect(client).to receive(:perform) do |req, _|
241
+ expect(req["Content-Encoding"]).to eq "gzip"
242
+ end
243
+
244
+ client.request(:get, "http://example.com/")
245
+ end
246
+ end
200
247
  end
201
248
 
202
249
  include_context "HTTP handling" do
@@ -280,7 +327,7 @@ RSpec.describe HTTP::Client do
280
327
 
281
328
  allow(socket_spy).to receive(:close) { nil }
282
329
  allow(socket_spy).to receive(:closed?) { true }
283
- allow(socket_spy).to receive(:readpartial) { chunks[0] }
330
+ allow(socket_spy).to receive(:readpartial) { chunks.shift || :eof }
284
331
  allow(socket_spy).to receive(:write) { chunks[0].length }
285
332
 
286
333
  allow(TCPSocket).to receive(:open) { socket_spy }
@@ -291,5 +338,58 @@ RSpec.describe HTTP::Client do
291
338
  expect(body).to eq "<!doctype html>"
292
339
  end
293
340
  end
341
+
342
+ context "when uses chunked transfer encoding" do
343
+ let(:chunks) do
344
+ [
345
+ <<-RESPONSE.gsub(/^\s*\| */, "").gsub(/\n/, "\r\n") << body
346
+ | HTTP/1.1 200 OK
347
+ | Content-Type: application/json
348
+ | Transfer-Encoding: chunked
349
+ | Connection: close
350
+ |
351
+ RESPONSE
352
+ ]
353
+ end
354
+ let(:body) do
355
+ <<-BODY.gsub(/^\s*\| */, "").gsub(/\n/, "\r\n")
356
+ | 9
357
+ | {"state":
358
+ | 5
359
+ | "ok"}
360
+ | 0
361
+ |
362
+ BODY
363
+ end
364
+
365
+ before do
366
+ socket_spy = double
367
+
368
+ allow(socket_spy).to receive(:close) { nil }
369
+ allow(socket_spy).to receive(:closed?) { true }
370
+ allow(socket_spy).to receive(:readpartial) { chunks.shift || :eof }
371
+ allow(socket_spy).to receive(:write) { chunks[0].length }
372
+
373
+ allow(TCPSocket).to receive(:open) { socket_spy }
374
+ end
375
+
376
+ it "properly reads body" do
377
+ body = client.get(dummy.endpoint).to_s
378
+ expect(body).to eq '{"state":"ok"}'
379
+ end
380
+
381
+ context "with broken body (too early closed connection)" do
382
+ let(:body) do
383
+ <<-BODY.gsub(/^\s*\| */, "").gsub(/\n/, "\r\n")
384
+ | 9
385
+ | {"state":
386
+ BODY
387
+ end
388
+
389
+ it "raises HTTP::ConnectionError" do
390
+ expect { client.get(dummy.endpoint).to_s }.to raise_error(HTTP::ConnectionError)
391
+ end
392
+ end
393
+ end
294
394
  end
295
395
  end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  RSpec.describe HTTP::ContentType do
3
4
  describe ".parse" do
4
5
  context "with text/plain" do
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  RSpec.describe HTTP::Features::AutoDeflate do
3
4
  subject { HTTP::Features::AutoDeflate.new }
4
5
 
@@ -25,84 +26,51 @@ RSpec.describe HTTP::Features::AutoDeflate do
25
26
  expect(subject.method).to eq("gzip")
26
27
  end
27
28
 
28
- describe "#deflate" do
29
- let(:headers) { HTTP::Headers.coerce("Content-Length" => "10") }
30
-
31
- context "when body is nil" do
32
- let(:body) { nil }
33
-
34
- it "returns nil" do
35
- expect(subject.deflate(headers, body)).to be_nil
36
- end
37
-
38
- it "does not remove Content-Length header" do
39
- subject.deflate(headers, body)
40
- expect(headers["Content-Length"]).to eq "10"
41
- end
29
+ describe "#deflated_body" do
30
+ let(:body) { %w[bees cows] }
31
+ let(:deflated_body) { subject.deflated_body(body) }
42
32
 
43
- it "does not set Content-Encoding header" do
44
- subject.deflate(headers, body)
45
- expect(headers.include?("Content-Encoding")).to eq false
46
- end
47
- end
33
+ context "when method is gzip" do
34
+ subject { HTTP::Features::AutoDeflate.new(:method => :gzip) }
48
35
 
49
- context "when body is not a string" do
50
- let(:body) { {} }
36
+ it "returns object which yields gzipped content of the given body" do
37
+ io = StringIO.new
38
+ io.set_encoding(Encoding::BINARY)
39
+ gzip = Zlib::GzipWriter.new(io)
40
+ gzip.write("beescows")
41
+ gzip.close
42
+ gzipped = io.string
51
43
 
52
- it "returns given body" do
53
- expect(subject.deflate(headers, body).object_id).to eq(body.object_id)
44
+ expect(deflated_body.each.to_a.join).to eq gzipped
54
45
  end
55
46
 
56
- it "does not remove Content-Length header" do
57
- subject.deflate(headers, body)
58
- expect(headers["Content-Length"]).to eq "10"
59
- end
47
+ it "caches compressed content when size is called" do
48
+ io = StringIO.new
49
+ io.set_encoding(Encoding::BINARY)
50
+ gzip = Zlib::GzipWriter.new(io)
51
+ gzip.write("beescows")
52
+ gzip.close
53
+ gzipped = io.string
60
54
 
61
- it "does not set Content-Encoding header" do
62
- subject.deflate(headers, body)
63
- expect(headers.include?("Content-Encoding")).to eq false
55
+ expect(deflated_body.size).to eq gzipped.bytesize
56
+ expect(deflated_body.each.to_a.join).to eq gzipped
64
57
  end
65
58
  end
66
59
 
67
- context "when body is a string" do
68
- let(:body) { "Hello HTTP!" }
69
-
70
- it "encodes body" do
71
- encoded = subject.deflate(headers, body)
72
- decoded = Zlib::GzipReader.new(StringIO.new(encoded)).read
73
-
74
- expect(decoded).to eq(body)
75
- end
60
+ context "when method is deflate" do
61
+ subject { HTTP::Features::AutoDeflate.new(:method => :deflate) }
76
62
 
77
- it "removes Content-Length header" do
78
- subject.deflate(headers, body)
79
- expect(headers.include?("Content-Length")).to eq false
80
- end
63
+ it "returns object which yields deflated content of the given body" do
64
+ deflated = Zlib::Deflate.deflate("beescows")
81
65
 
82
- it "sets Content-Encoding header" do
83
- subject.deflate(headers, body)
84
- expect(headers["Content-Encoding"]).to eq "gzip"
66
+ expect(deflated_body.each.to_a.join).to eq deflated
85
67
  end
86
68
 
87
- context "as deflate method" do
88
- subject { HTTP::Features::AutoDeflate.new(:method => :deflate) }
89
-
90
- it "encodes body" do
91
- encoded = subject.deflate(headers, body)
92
- decoded = Zlib::Inflate.inflate(encoded)
93
-
94
- expect(decoded).to eq(body)
95
- end
96
-
97
- it "removes Content-Length header" do
98
- subject.deflate(headers, body)
99
- expect(headers.include?("Content-Length")).to eq false
100
- end
69
+ it "caches compressed content when size is called" do
70
+ deflated = Zlib::Deflate.deflate("beescows")
101
71
 
102
- it "sets Content-Encoding header" do
103
- subject.deflate(headers, body)
104
- expect(headers["Content-Encoding"]).to eq "deflate"
105
- end
72
+ expect(deflated_body.size).to eq deflated.bytesize
73
+ expect(deflated_body.each.to_a.join).to eq deflated
106
74
  end
107
75
  end
108
76
  end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  RSpec.describe HTTP::Features::AutoInflate do
3
4
  subject { HTTP::Features::AutoInflate.new }
4
5
  let(:connection) { double }
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  RSpec.describe HTTP::Headers::Mixin do
3
4
  let :dummy_class do
4
5
  Class.new do
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  RSpec.describe HTTP::Headers do
3
4
  subject(:headers) { described_class.new }
4
5
 
@@ -25,18 +26,18 @@ RSpec.describe HTTP::Headers do
25
26
 
26
27
  it "allows set multiple values" do
27
28
  headers.set :set_cookie, "hoo=ray"
28
- headers.set :set_cookie, %w(hoo=ray woo=hoo)
29
- expect(headers["Set-Cookie"]).to eq %w(hoo=ray woo=hoo)
29
+ headers.set :set_cookie, %w[hoo=ray woo=hoo]
30
+ expect(headers["Set-Cookie"]).to eq %w[hoo=ray woo=hoo]
30
31
  end
31
32
 
32
33
  it "fails with empty header name" do
33
34
  expect { headers.set "", "foo bar" }.
34
- to raise_error HTTP::InvalidHeaderNameError
35
+ to raise_error HTTP::HeaderError
35
36
  end
36
37
 
37
38
  it "fails with invalid header name" do
38
39
  expect { headers.set "foo bar", "baz" }.
39
- to raise_error HTTP::InvalidHeaderNameError
40
+ to raise_error HTTP::HeaderError
40
41
  end
41
42
  end
42
43
 
@@ -59,8 +60,8 @@ RSpec.describe HTTP::Headers do
59
60
 
60
61
  it "allows set multiple values" do
61
62
  headers[:set_cookie] = "hoo=ray"
62
- headers[:set_cookie] = %w(hoo=ray woo=hoo)
63
- expect(headers["Set-Cookie"]).to eq %w(hoo=ray woo=hoo)
63
+ headers[:set_cookie] = %w[hoo=ray woo=hoo]
64
+ expect(headers["Set-Cookie"]).to eq %w[hoo=ray woo=hoo]
64
65
  end
65
66
  end
66
67
 
@@ -79,12 +80,12 @@ RSpec.describe HTTP::Headers do
79
80
 
80
81
  it "fails with empty header name" do
81
82
  expect { headers.delete "" }.
82
- to raise_error HTTP::InvalidHeaderNameError
83
+ to raise_error HTTP::HeaderError
83
84
  end
84
85
 
85
86
  it "fails with invalid header name" do
86
87
  expect { headers.delete "foo bar" }.
87
- to raise_error HTTP::InvalidHeaderNameError
88
+ to raise_error HTTP::HeaderError
88
89
  end
89
90
  end
90
91
 
@@ -102,23 +103,23 @@ RSpec.describe HTTP::Headers do
102
103
  it "appends new value if header exists" do
103
104
  headers.add :set_cookie, "hoo=ray"
104
105
  headers.add :set_cookie, "woo=hoo"
105
- expect(headers["Set-Cookie"]).to eq %w(hoo=ray woo=hoo)
106
+ expect(headers["Set-Cookie"]).to eq %w[hoo=ray woo=hoo]
106
107
  end
107
108
 
108
109
  it "allows append multiple values" do
109
110
  headers.add :set_cookie, "hoo=ray"
110
- headers.add :set_cookie, %w(woo=hoo yup=pie)
111
- expect(headers["Set-Cookie"]).to eq %w(hoo=ray woo=hoo yup=pie)
111
+ headers.add :set_cookie, %w[woo=hoo yup=pie]
112
+ expect(headers["Set-Cookie"]).to eq %w[hoo=ray woo=hoo yup=pie]
112
113
  end
113
114
 
114
115
  it "fails with empty header name" do
115
116
  expect { headers.add("", "foobar") }.
116
- to raise_error HTTP::InvalidHeaderNameError
117
+ to raise_error HTTP::HeaderError
117
118
  end
118
119
 
119
120
  it "fails with invalid header name" do
120
121
  expect { headers.add "foo bar", "baz" }.
121
- to raise_error HTTP::InvalidHeaderNameError
122
+ to raise_error HTTP::HeaderError
122
123
  end
123
124
  end
124
125
 
@@ -126,11 +127,11 @@ RSpec.describe HTTP::Headers do
126
127
  before { headers.set("Content-Type", "application/json") }
127
128
 
128
129
  it "returns array of associated values" do
129
- expect(headers.get("Content-Type")).to eq %w(application/json)
130
+ expect(headers.get("Content-Type")).to eq %w[application/json]
130
131
  end
131
132
 
132
133
  it "normalizes header name" do
133
- expect(headers.get(:content_type)).to eq %w(application/json)
134
+ expect(headers.get(:content_type)).to eq %w[application/json]
134
135
  end
135
136
 
136
137
  context "when header does not exists" do
@@ -141,12 +142,12 @@ RSpec.describe HTTP::Headers do
141
142
 
142
143
  it "fails with empty header name" do
143
144
  expect { headers.get("") }.
144
- to raise_error HTTP::InvalidHeaderNameError
145
+ to raise_error HTTP::HeaderError
145
146
  end
146
147
 
147
148
  it "fails with invalid header name" do
148
149
  expect { headers.get("foo bar") }.
149
- to raise_error HTTP::InvalidHeaderNameError
150
+ to raise_error HTTP::HeaderError
150
151
  end
151
152
  end
152
153
 
@@ -180,7 +181,7 @@ RSpec.describe HTTP::Headers do
180
181
  end
181
182
 
182
183
  it "returns array of associated values" do
183
- expect(headers[:set_cookie]).to eq %w(hoo=ray woo=hoo)
184
+ expect(headers[:set_cookie]).to eq %w[hoo=ray woo=hoo]
184
185
  end
185
186
  end
186
187
  end
@@ -217,7 +218,7 @@ RSpec.describe HTTP::Headers do
217
218
  end
218
219
 
219
220
  it "returns Hash with normalized keys" do
220
- expect(headers.to_h.keys).to match_array %w(Content-Type Set-Cookie)
221
+ expect(headers.to_h.keys).to match_array %w[Content-Type Set-Cookie]
221
222
  end
222
223
 
223
224
  context "for a header with single value" do
@@ -228,7 +229,7 @@ RSpec.describe HTTP::Headers do
228
229
 
229
230
  context "for a header with multiple values" do
230
231
  it "provides an array of values" do
231
- expect(headers.to_h["Set-Cookie"]).to eq %w(hoo=ray woo=hoo)
232
+ expect(headers.to_h["Set-Cookie"]).to eq %w[hoo=ray woo=hoo]
232
233
  end
233
234
  end
234
235
  end
@@ -246,15 +247,15 @@ RSpec.describe HTTP::Headers do
246
247
 
247
248
  it "returns Array of key/value pairs with normalized keys" do
248
249
  expect(headers.to_a).to eq [
249
- %w(Content-Type application/json),
250
- %w(Set-Cookie hoo=ray),
251
- %w(Set-Cookie woo=hoo)
250
+ %w[Content-Type application/json],
251
+ %w[Set-Cookie hoo=ray],
252
+ %w[Set-Cookie woo=hoo]
252
253
  ]
253
254
  end
254
255
  end
255
256
 
256
257
  describe "#inspect" do
257
- before { headers.set :set_cookie, %w(hoo=ray woo=hoo) }
258
+ before { headers.set :set_cookie, %w[hoo=ray woo=hoo] }
258
259
  subject { headers.inspect }
259
260
 
260
261
  it { is_expected.to eq '#<HTTP::Headers {"Set-Cookie"=>["hoo=ray", "woo=hoo"]}>' }
@@ -289,9 +290,9 @@ RSpec.describe HTTP::Headers do
289
290
 
290
291
  it "yields headers in the same order they were added" do
291
292
  expect { |b| headers.each(&b) }.to yield_successive_args(
292
- %w(Set-Cookie hoo=ray),
293
- %w(Content-Type application/json),
294
- %w(Set-Cookie woo=hoo)
293
+ %w[Set-Cookie hoo=ray],
294
+ %w[Content-Type application/json],
295
+ %w[Set-Cookie woo=hoo]
295
296
  )
296
297
  end
297
298
 
@@ -351,7 +352,7 @@ RSpec.describe HTTP::Headers do
351
352
 
352
353
  it "allows comparison with Array of key/value pairs" do
353
354
  left.add :accept, "text/plain"
354
- expect(left).to eq [%w(Accept text/plain)]
355
+ expect(left).to eq [%w[Accept text/plain]]
355
356
  end
356
357
 
357
358
  it "sensitive to headers order" do
@@ -402,7 +403,7 @@ RSpec.describe HTTP::Headers do
402
403
  before do
403
404
  headers.set :host, "example.com"
404
405
  headers.set :accept, "application/json"
405
- headers.merge! :accept => "plain/text", :cookie => %w(hoo=ray woo=hoo)
406
+ headers.merge! :accept => "plain/text", :cookie => %w[hoo=ray woo=hoo]
406
407
  end
407
408
 
408
409
  it "leaves headers not presented in other as is" do
@@ -414,7 +415,7 @@ RSpec.describe HTTP::Headers do
414
415
  end
415
416
 
416
417
  it "appends other headers, not presented in base" do
417
- expect(headers[:cookie]).to eq %w(hoo=ray woo=hoo)
418
+ expect(headers[:cookie]).to eq %w[hoo=ray woo=hoo]
418
419
  end
419
420
  end
420
421
 
@@ -425,7 +426,7 @@ RSpec.describe HTTP::Headers do
425
426
  end
426
427
 
427
428
  subject(:merged) do
428
- headers.merge :accept => "plain/text", :cookie => %w(hoo=ray woo=hoo)
429
+ headers.merge :accept => "plain/text", :cookie => %w[hoo=ray woo=hoo]
429
430
  end
430
431
 
431
432
  it { is_expected.to be_a described_class }
@@ -444,7 +445,7 @@ RSpec.describe HTTP::Headers do
444
445
  end
445
446
 
446
447
  it "appends other headers, not presented in base" do
447
- expect(merged[:cookie]).to eq %w(hoo=ray woo=hoo)
448
+ expect(merged[:cookie]).to eq %w[hoo=ray woo=hoo]
448
449
  end
449
450
  end
450
451
 
@@ -462,7 +463,7 @@ RSpec.describe HTTP::Headers do
462
463
  end
463
464
 
464
465
  it "accepts any object that respond to #to_a" do
465
- hashie = double :to_a => [%w(accept json)]
466
+ hashie = double :to_a => [%w[accept json]]
466
467
  expect(described_class.coerce(hashie)["accept"]).to eq "json"
467
468
  end
468
469
 
@@ -477,8 +478,8 @@ RSpec.describe HTTP::Headers do
477
478
  expect(described_class.coerce(headers).to_a).
478
479
  to match_array(
479
480
  [
480
- %w(Set-Cookie hoo=ray),
481
- %w(Set-Cookie woo=hoo)
481
+ %w[Set-Cookie hoo=ray],
482
+ %w[Set-Cookie woo=hoo]
482
483
  ]
483
484
  )
484
485
  end