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 +4 -4
- data/.travis.yml +1 -0
- data/async-http.gemspec +1 -1
- data/lib/async/http/body.rb +139 -0
- data/lib/async/http/client.rb +13 -4
- data/lib/async/http/protocol/http10.rb +2 -5
- data/lib/async/http/protocol/http11.rb +44 -22
- data/lib/async/http/protocol/http2.rb +44 -42
- data/lib/async/http/{protocol/request.rb → request.rb} +4 -6
- data/lib/async/http/{protocol/response.rb → response.rb} +19 -17
- data/lib/async/http/server.rb +6 -2
- data/lib/async/http/version.rb +1 -1
- metadata +7 -7
- data/lib/async/http/headers.rb +0 -85
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9f76d14d623f95c9f57dc60a00e2200e05214149eeb2625ff632d05492981183
|
4
|
+
data.tar.gz: e9b9191121ca54563cae6090a28725bf00029eec28e914972f6e36d24b67b082
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bcdeb04bb6666ed1ccbd04cf40ffa01b37e2c8e1899c109fdb654e9bb8eb5b88a150ecd1ddbd53500eff5915e505e2e54bff6f68566a4f15324a5d6cecfa1179
|
7
|
+
data.tar.gz: 287f239b0e75d139c1e93f701365e7062d7e90a6c21912dd4b9d27b5d8c1211a12efcb349c0cf32de15ce04bee890a98efaf0d90d9e125415ec2eb1d6991d255
|
data/.travis.yml
CHANGED
data/async-http.gemspec
CHANGED
@@ -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.
|
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
|
data/lib/async/http/client.rb
CHANGED
@@ -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[
|
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[
|
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[
|
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(
|
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 =
|
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: #{
|
180
|
-
@stream.write(
|
176
|
+
@stream.write("Content-Length: #{buffer.bytesize}\r\n\r\n")
|
177
|
+
@stream.write(buffer)
|
181
178
|
end
|
182
179
|
end
|
183
180
|
|
184
|
-
|
185
|
-
|
186
|
-
|
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
|
-
|
193
|
-
|
198
|
+
@closed = true
|
199
|
+
@protocol.read_line
|
200
|
+
|
201
|
+
return
|
194
202
|
end
|
195
203
|
|
196
|
-
|
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
|
-
|
203
|
-
|
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
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
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 =
|
105
|
-
request.headers =
|
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 |
|
125
|
-
request.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
|
-
|
133
|
+
headers = {STATUS => response[0].to_s}
|
134
|
+
headers.update(response[1])
|
133
135
|
|
134
|
-
stream.headers(
|
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
|
-
|
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:
|
161
|
+
stream.headers(internal_headers, end_stream: body.nil?)
|
162
162
|
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
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 =
|
174
|
-
response.body =
|
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 |
|
191
|
-
|
192
|
-
response.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
|
-
|
200
|
+
Async.logger.debug(self) {"Stream half-closed."}
|
199
201
|
end
|
200
202
|
|
201
203
|
stream.on(:close) do
|
202
|
-
|
203
|
-
|
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
|
-
|
24
|
-
|
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
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
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
|
data/lib/async/http/server.rb
CHANGED
@@ -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)
|
data/lib/async/http/version.rb
CHANGED
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.
|
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-
|
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.
|
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.
|
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/
|
137
|
-
- lib/async/http/
|
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
|
data/lib/async/http/headers.rb
DELETED
@@ -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
|