async-http 0.10.0 → 0.12.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c74bbff1b6216d57e8d60e5a54700f9461cef5aff2436db4abbf07c300a1af09
4
- data.tar.gz: 1354a61e69f69706688f112fef4c958af285bec43172f00d7993bff7fcb9dc7b
3
+ metadata.gz: 9f76d14d623f95c9f57dc60a00e2200e05214149eeb2625ff632d05492981183
4
+ data.tar.gz: e9b9191121ca54563cae6090a28725bf00029eec28e914972f6e36d24b67b082
5
5
  SHA512:
6
- metadata.gz: d62959c37bc323c82d57ca4e745ef4d2a715f3ec0e580e0d4a1e5cbdb65766f6b4c671efab4f8e85fb0fabe6a5cac99be1de1aa2cc92c30bc39e5e51f9f40d51
7
- data.tar.gz: 75b83edd5bd767e638ebeacbd3446e317b24b49eba17628af95e32e78690cc0b26c8c998ed1aaed7248c8e796c7e77fd43a8c80f391fcc5876a8cd84533bdd1d
6
+ metadata.gz: bcdeb04bb6666ed1ccbd04cf40ffa01b37e2c8e1899c109fdb654e9bb8eb5b88a150ecd1ddbd53500eff5915e505e2e54bff6f68566a4f15324a5d6cecfa1179
7
+ data.tar.gz: 287f239b0e75d139c1e93f701365e7062d7e90a6c21912dd4b9d27b5d8c1211a12efcb349c0cf32de15ce04bee890a98efaf0d90d9e125415ec2eb1d6991d255
@@ -11,6 +11,7 @@ addons:
11
11
 
12
12
  before_script:
13
13
  - gem update --system
14
+ - gem install bundler
14
15
 
15
16
  matrix:
16
17
  include:
@@ -16,7 +16,7 @@ Gem::Specification.new do |spec|
16
16
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
17
  spec.require_paths = ["lib"]
18
18
 
19
- spec.add_dependency("async", "~> 1.4")
19
+ spec.add_dependency("async", "~> 1.5")
20
20
  spec.add_dependency("async-io", "~> 1.6")
21
21
 
22
22
  spec.add_dependency("http-2", "~> 0.8")
@@ -0,0 +1,139 @@
1
+ # Copyright, 2018, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ require 'async/queue'
22
+
23
+ module Async
24
+ module HTTP
25
+ class Body < Async::Queue
26
+ def initialize
27
+ super
28
+
29
+ @closed = false
30
+ end
31
+
32
+ def closed?
33
+ @closed
34
+ end
35
+
36
+ def each
37
+ return if @closed
38
+
39
+ while chunk = self.dequeue
40
+ yield chunk
41
+ end
42
+
43
+ @closed = true
44
+ end
45
+
46
+ def read
47
+ buffer = Async::IO::BinaryString.new
48
+
49
+ self.each do |chunk|
50
+ buffer << chunk
51
+ end
52
+
53
+ return buffer
54
+ end
55
+
56
+ alias join read
57
+
58
+ def write(chunk)
59
+ self.enqueue(chunk)
60
+ end
61
+
62
+ def close
63
+ self.enqueue(nil)
64
+ end
65
+ end
66
+
67
+ class BufferedBody
68
+ def initialize(body)
69
+ @chunks = []
70
+
71
+ body.each do |chunk|
72
+ @chunks << chunk
73
+ end
74
+ end
75
+
76
+ def each(&block)
77
+ @chunks.each(&block)
78
+ end
79
+
80
+ def read
81
+ @buffer ||= @chunks.join
82
+ end
83
+
84
+ alias join read
85
+
86
+ def closed?
87
+ true
88
+ end
89
+
90
+ module Reader
91
+ def read
92
+ self.body ? self.body.read : nil
93
+ end
94
+
95
+ def close
96
+ return if self.body.nil? or self.body.closed?
97
+
98
+ unless self.body.is_a? BufferedBody
99
+ self.body = BufferedBody.new(self.body)
100
+ end
101
+ end
102
+ end
103
+ end
104
+
105
+ class FixedBody
106
+ CHUNK_LENGTH = 1024*1024
107
+
108
+ def initialize(length, stream)
109
+ @length = length
110
+ @remaining = length
111
+ @stream = stream
112
+ end
113
+
114
+ def closed?
115
+ @remaining == 0
116
+ end
117
+
118
+ def each
119
+ while @remaining > 0
120
+ if chunk = @stream.read(CHUNK_LENGTH)
121
+ @remaining -= chunk.bytesize
122
+
123
+ yield chunk
124
+ end
125
+ end
126
+ end
127
+
128
+ def read
129
+ buffer = @stream.read(@remaining)
130
+
131
+ @remaining = 0
132
+
133
+ return buffer
134
+ end
135
+
136
+ alias join read
137
+ end
138
+ end
139
+ end
@@ -57,14 +57,23 @@ module Async
57
57
  VERBS = ['GET', 'HEAD', 'POST', 'PUT', 'PATCH', 'DELETE']
58
58
 
59
59
  VERBS.each do |verb|
60
- define_method(verb.downcase) do |*args|
61
- self.request(verb, *args)
60
+ define_method(verb.downcase) do |*args, &block|
61
+ self.request(verb, *args, &block)
62
62
  end
63
63
  end
64
64
 
65
- def request(*args)
65
+ def request(*args, &block)
66
66
  @connections.acquire do |connection|
67
- connection.send_request(@authority, *args)
67
+ response = connection.send_request(@authority, *args)
68
+
69
+ begin
70
+ yield response if block_given?
71
+ ensure
72
+ # This forces the stream to complete reading.
73
+ response.close
74
+ end
75
+
76
+ return response
68
77
  end
69
78
  end
70
79
 
@@ -20,9 +20,6 @@
20
20
 
21
21
  require_relative 'http11'
22
22
 
23
- require_relative 'request'
24
- require_relative 'response'
25
-
26
23
  module Async
27
24
  module HTTP
28
25
  module Protocol
@@ -35,7 +32,7 @@ module Async
35
32
  end
36
33
 
37
34
  def keep_alive?(headers)
38
- headers[:connection] == KEEP_ALIVE
35
+ headers['connection'] == KEEP_ALIVE
39
36
  end
40
37
 
41
38
  # Server loop.
@@ -64,7 +61,7 @@ module Async
64
61
  end
65
62
 
66
63
  def read_body(headers)
67
- if content_length = headers[:content_length]
64
+ if content_length = headers['content-length']
68
65
  return @stream.read(Integer(content_length))
69
66
  # elsif !keep_alive?(headers)
70
67
  # return @stream.read
@@ -20,18 +20,14 @@
20
20
 
21
21
  require 'async/io/protocol/line'
22
22
 
23
- require_relative 'request'
24
- require_relative 'response'
25
- require_relative '../headers'
23
+ require_relative '../request'
24
+ require_relative '../response'
26
25
 
27
26
  module Async
28
27
  module HTTP
29
28
  module Protocol
30
29
  # Implements basic HTTP/1.1 request/response.
31
30
  class HTTP11 < Async::IO::Protocol::Line
32
- CONTENT_LENGTH = Headers['Content-Length']
33
- TRANSFER_ENCODING = Headers['Transfer-Encoding']
34
-
35
31
  CRLF = "\r\n".freeze
36
32
 
37
33
  def initialize(stream)
@@ -64,7 +60,7 @@ module Async
64
60
  end
65
61
 
66
62
  def keep_alive?(headers)
67
- headers[:connection] != CLOSE
63
+ headers['connection'] != CLOSE
68
64
  end
69
65
 
70
66
  # Server loop.
@@ -125,7 +121,7 @@ module Async
125
121
  headers = read_headers
126
122
  body = read_body(headers)
127
123
 
128
- return headers.delete(:host), method, path, version, headers, body
124
+ return headers.delete('host'), method, path, version, headers, body
129
125
  end
130
126
 
131
127
  def write_response(version, status, headers, body)
@@ -146,11 +142,11 @@ module Async
146
142
  end
147
143
  end
148
144
 
149
- def read_headers(headers = Headers.new)
145
+ def read_headers(headers = {})
150
146
  # Parsing headers:
151
147
  each_line do |line|
152
148
  if line =~ /^([a-zA-Z\-]+):\s*(.+?)\s*$/
153
- headers[$1] = $2
149
+ headers[$1.downcase] = $2
154
150
  else
155
151
  break
156
152
  end
@@ -169,6 +165,7 @@ module Async
169
165
  @stream.write("#{chunk.bytesize.to_s(16).upcase}\r\n")
170
166
  @stream.write(chunk)
171
167
  @stream.write(CRLF)
168
+ @stream.flush
172
169
  end
173
170
 
174
171
  @stream.write("0\r\n\r\n")
@@ -176,31 +173,56 @@ module Async
176
173
  buffer = String.new
177
174
  body.each{|chunk| buffer << chunk}
178
175
 
179
- @stream.write("Content-Length: #{chunk.bytesize}\r\n\r\n")
180
- @stream.write(chunk)
176
+ @stream.write("Content-Length: #{buffer.bytesize}\r\n\r\n")
177
+ @stream.write(buffer)
181
178
  end
182
179
  end
183
180
 
184
- def read_body(headers)
185
- if headers[:transfer_encoding] == 'chunked'
186
- buffer = Async::IO::BinaryString.new
181
+ class ChunkedBody
182
+ def initialize(protocol)
183
+ @protocol = protocol
184
+ @closed = false
185
+ end
186
+
187
+ def closed?
188
+ @closed
189
+ end
190
+
191
+ def each
192
+ return if @closed
187
193
 
188
194
  while true
189
- size = read_line.to_i(16)
195
+ size = @protocol.read_line.to_i(16)
190
196
 
191
197
  if size == 0
192
- read_line
193
- break
198
+ @closed = true
199
+ @protocol.read_line
200
+
201
+ return
194
202
  end
195
203
 
196
- buffer << @stream.read(size)
204
+ yield @protocol.stream.read(size)
197
205
 
198
- read_line # Consume the trailing CRLF
206
+ @protocol.read_line # Consume the trailing CRLF
207
+ end
208
+ end
209
+
210
+ def read
211
+ buffer = Async::IO::BinaryString.new
212
+
213
+ self.each do |chunk|
214
+ buffer << chunk
199
215
  end
200
216
 
201
217
  return buffer
202
- elsif content_length = headers[:content_length]
203
- return @stream.read(Integer(content_length))
218
+ end
219
+ end
220
+
221
+ def read_body(headers)
222
+ if headers['transfer-encoding'] == 'chunked'
223
+ return ChunkedBody.new(self)
224
+ elsif content_length = headers['content-length']
225
+ return FixedBody.new(Integer(content_length), @stream)
204
226
  end
205
227
  end
206
228
  end
@@ -18,9 +18,8 @@
18
18
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
19
  # THE SOFTWARE.
20
20
 
21
- require_relative 'request'
22
- require_relative 'response'
23
- require_relative '../headers'
21
+ require_relative '../request'
22
+ require_relative '../response'
24
23
 
25
24
  require 'async/notification'
26
25
 
@@ -46,6 +45,7 @@ module Async
46
45
  AUTHORITY = ':authority'.freeze
47
46
  REASON = ':reason'.freeze
48
47
  STATUS = ':status'.freeze
48
+ VERSION = 'HTTP/2.0'.freeze
49
49
 
50
50
  def initialize(controller, stream)
51
51
  @controller = controller
@@ -56,18 +56,19 @@ module Async
56
56
  @stream.flush
57
57
  end
58
58
 
59
- # @controller.on(:frame_sent) do |frame|
60
- # Async.logger.debug(self) {"Sent frame: #{frame.inspect}"}
61
- # end
62
- #
63
- # @controller.on(:frame_received) do |frame|
64
- # Async.logger.debug(self) {"Received frame: #{frame.inspect}"}
65
- # end
59
+ @controller.on(:frame_sent) do |frame|
60
+ Async.logger.debug(self) {"Sent frame: #{frame.inspect}"}
61
+ end
62
+
63
+ @controller.on(:frame_received) do |frame|
64
+ Async.logger.debug(self) {"Received frame: #{frame.inspect}"}
65
+ end
66
66
 
67
67
  if @controller.is_a? ::HTTP2::Client
68
68
  @controller.send_connection_preface
69
- @reader = read_in_background
70
69
  end
70
+
71
+ @reader = read_in_background
71
72
  end
72
73
 
73
74
  # Multiple requests can be processed at the same time.
@@ -97,15 +98,13 @@ module Async
97
98
  @stream.close
98
99
  end
99
100
 
100
- def receive_requests(&block)
101
+ def receive_requests(task: Task.current, &block)
101
102
  # emits new streams opened by the client
102
103
  @controller.on(:stream) do |stream|
103
104
  request = Request.new
104
- request.version = "HTTP/2.0"
105
- request.headers = Headers.new
106
-
107
- # stream.on(:active) { } # fires when stream transitions to open state
108
- # stream.on(:close) { } # stream is closed by client and server
105
+ request.version = VERSION
106
+ request.headers = {}
107
+ request.body = Body.new
109
108
 
110
109
  stream.on(:headers) do |headers|
111
110
  headers.each do |key, value|
@@ -121,17 +120,20 @@ module Async
121
120
  end
122
121
  end
123
122
 
124
- stream.on(:data) do |body|
125
- request.body = body
123
+ stream.on(:data) do |chunk|
124
+ request.body.write(chunk.to_s) unless chunk.empty?
126
125
  end
127
126
 
128
127
  stream.on(:half_close) do
129
128
  response = yield request
130
129
 
130
+ request.body.close
131
+
131
132
  # send response
132
- stream.headers(STATUS => response[0].to_s)
133
+ headers = {STATUS => response[0].to_s}
134
+ headers.update(response[1])
133
135
 
134
- stream.headers(response[1]) unless response[1].empty?
136
+ stream.headers(headers, end_stream: false)
135
137
 
136
138
  response[2].each do |chunk|
137
139
  stream.data(chunk, end_stream: false)
@@ -141,9 +143,7 @@ module Async
141
143
  end
142
144
  end
143
145
 
144
- while data = @stream.io.read(1024)
145
- @controller << data
146
- end
146
+ @reader.wait
147
147
  end
148
148
 
149
149
  RESPONSE_VERSION = 'HTTP/2'.freeze
@@ -158,20 +158,22 @@ module Async
158
158
  AUTHORITY => authority,
159
159
  }.merge(headers)
160
160
 
161
- stream.headers(internal_headers, end_stream: true)
161
+ stream.headers(internal_headers, end_stream: body.nil?)
162
162
 
163
- # if body
164
- # body.each do |chunk|
165
- # stream.data(chunk, end_stream: false)
166
- # end
167
- #
168
- # stream.data("", end_stream: true)
169
- # end
163
+ if body
164
+ body.each do |chunk|
165
+ stream.data(chunk, end_stream: false)
166
+ end
167
+
168
+ stream.data("", end_stream: true)
169
+ end
170
+
171
+ finished = Async::Notification.new
170
172
 
171
173
  response = Response.new
172
174
  response.version = RESPONSE_VERSION
173
- response.headers = Headers.new
174
- response.body = Async::IO::BinaryString.new
175
+ response.headers = {}
176
+ response.body = Body.new
175
177
 
176
178
  stream.on(:headers) do |headers|
177
179
  # Async.logger.debug(self) {"Stream headers: #{headers.inspect}"}
@@ -185,22 +187,22 @@ module Async
185
187
  response.headers[key] = value
186
188
  end
187
189
  end
190
+
191
+ finished.signal
188
192
  end
189
193
 
190
- stream.on(:data) do |body|
191
- # Async.logger.debug(self) {"Stream data: #{body.size} bytes"}
192
- response.body << body
194
+ stream.on(:data) do |chunk|
195
+ Async.logger.debug(self) {"Stream data: #{chunk.inspect}"}
196
+ response.body.write(chunk.to_s) unless chunk.empty?
193
197
  end
194
198
 
195
- finished = Async::Notification.new
196
-
197
199
  stream.on(:half_close) do
198
- # Async.logger.debug(self) {"Stream half-closed."}
200
+ Async.logger.debug(self) {"Stream half-closed."}
199
201
  end
200
202
 
201
203
  stream.on(:close) do
202
- # Async.logger.debug(self) {"Stream closed, sending signal."}
203
- finished.signal
204
+ Async.logger.debug(self) {"Stream closed, sending signal."}
205
+ response.body.close
204
206
  end
205
207
 
206
208
  @stream.flush
@@ -18,14 +18,12 @@
18
18
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
19
  # THE SOFTWARE.
20
20
 
21
+ require_relative 'body'
22
+
21
23
  module Async
22
24
  module HTTP
23
- module Protocol
24
- class Request < Struct.new(:authority, :method, :path, :version, :headers, :body)
25
- def read
26
- @body
27
- end
28
- end
25
+ class Request < Struct.new(:authority, :method, :path, :version, :headers, :body)
26
+ include BufferedBody::Reader
29
27
  end
30
28
  end
31
29
  end
@@ -18,26 +18,28 @@
18
18
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
19
  # THE SOFTWARE.
20
20
 
21
+ require_relative 'body'
22
+
21
23
  module Async
22
24
  module HTTP
23
- module Protocol
24
- class Response < Struct.new(:version, :status, :reason, :headers, :body)
25
- def continue?
26
- status == 100
27
- end
28
-
29
- def success?
30
- status >= 200 && status < 300
31
- end
32
-
33
- def redirection?
34
- status >= 300 && status < 400
35
- end
36
-
37
- def failure?
38
- status >= 400 && status < 600
39
- end
25
+ class Response < Struct.new(:version, :status, :reason, :headers, :body)
26
+ def continue?
27
+ status == 100
28
+ end
29
+
30
+ def success?
31
+ status >= 200 && status < 300
32
+ end
33
+
34
+ def redirection?
35
+ status >= 300 && status < 400
36
+ end
37
+
38
+ def failure?
39
+ status >= 400 && status < 600
40
40
  end
41
+
42
+ include BufferedBody::Reader
41
43
  end
42
44
  end
43
45
  end
@@ -25,9 +25,13 @@ require_relative 'protocol'
25
25
  module Async
26
26
  module HTTP
27
27
  class Server
28
- def initialize(endpoint, protocol_class = Protocol::HTTP1)
28
+ def initialize(endpoint, protocol_class = Protocol::HTTP1, &block)
29
29
  @endpoint = endpoint
30
- @protocol_class = protocol_class
30
+ @protocol_class = protocol_class || endpoint.protocol
31
+
32
+ if block_given?
33
+ self.define_singleton_method(:handle_request, block)
34
+ end
31
35
  end
32
36
 
33
37
  def handle_request(request, peer, address)
@@ -20,6 +20,6 @@
20
20
 
21
21
  module Async
22
22
  module HTTP
23
- VERSION = "0.10.0"
23
+ VERSION = "0.12.0"
24
24
  end
25
25
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: async-http
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.10.0
4
+ version: 0.12.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-03-28 00:00:00.000000000 Z
11
+ date: 2018-04-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: async
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '1.4'
19
+ version: '1.5'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '1.4'
26
+ version: '1.5'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: async-io
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -124,8 +124,8 @@ files:
124
124
  - Rakefile
125
125
  - async-http.gemspec
126
126
  - lib/async/http.rb
127
+ - lib/async/http/body.rb
127
128
  - lib/async/http/client.rb
128
- - lib/async/http/headers.rb
129
129
  - lib/async/http/pool.rb
130
130
  - lib/async/http/protocol.rb
131
131
  - lib/async/http/protocol/http1.rb
@@ -133,8 +133,8 @@ files:
133
133
  - lib/async/http/protocol/http11.rb
134
134
  - lib/async/http/protocol/http2.rb
135
135
  - lib/async/http/protocol/https.rb
136
- - lib/async/http/protocol/request.rb
137
- - lib/async/http/protocol/response.rb
136
+ - lib/async/http/request.rb
137
+ - lib/async/http/response.rb
138
138
  - lib/async/http/server.rb
139
139
  - lib/async/http/url_endpoint.rb
140
140
  - lib/async/http/version.rb
@@ -1,85 +0,0 @@
1
- # Copyright, 2017, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
- #
3
- # Permission is hereby granted, free of charge, to any person obtaining a copy
4
- # of this software and associated documentation files (the "Software"), to deal
5
- # in the Software without restriction, including without limitation the rights
6
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
- # copies of the Software, and to permit persons to whom the Software is
8
- # furnished to do so, subject to the following conditions:
9
- #
10
- # The above copyright notice and this permission notice shall be included in
11
- # all copies or substantial portions of the Software.
12
- #
13
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
- # THE SOFTWARE.
20
-
21
- module Async
22
- module HTTP
23
- class Headers
24
- def initialize
25
- @hash = {}
26
- end
27
-
28
- def freeze
29
- return unless frozen?
30
-
31
- @hash.freeze
32
-
33
- super
34
- end
35
-
36
- def inspect
37
- @hash.inspect
38
- end
39
-
40
- def []= key, value
41
- @hash[symbolize(key)] = value
42
- end
43
-
44
- def [] key
45
- @hash[key]
46
- end
47
-
48
- def == other
49
- @hash == other.to_hash
50
- end
51
-
52
- def delete(key)
53
- @hash.delete(key)
54
- end
55
-
56
- def each
57
- return to_enum unless block_given?
58
-
59
- @hash.each do |key, value|
60
- yield stringify(key), value
61
- end
62
- end
63
-
64
- def symbolize(value)
65
- Headers[value]
66
- end
67
-
68
- def stringify(key)
69
- key.to_s.tr('_', '-')
70
- end
71
-
72
- def to_hash
73
- @hash
74
- end
75
-
76
- def to_http_hash
77
- Hash[@hash.map{|key, value| ["HTTP_#{key.to_s.upcase}", value]}]
78
- end
79
-
80
- def self.[] value
81
- value.downcase.tr('-', '_').to_sym
82
- end
83
- end
84
- end
85
- end