http 2.2.2 → 3.0.0.pre

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