async-http 0.22.0 → 0.23.0
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.
- checksums.yaml +4 -4
- data/async-http.gemspec +1 -1
- data/lib/async/http/body/writable.rb +7 -0
- data/lib/async/http/client.rb +31 -12
- data/lib/async/http/protocol/http10.rb +18 -12
- data/lib/async/http/protocol/http11.rb +43 -27
- data/lib/async/http/protocol/http2.rb +69 -34
- data/lib/async/http/protocol/request_failed.rb +29 -0
- data/lib/async/http/request.rb +4 -0
- data/lib/async/http/server.rb +2 -2
- data/lib/async/http/version.rb +1 -1
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4977069f03e3535e44a463522d754edc6c625e24af2e520f7707f8684467145c
|
4
|
+
data.tar.gz: 85ec5b67a4ac4aef7c0c04c8226ebb91fd986c1174e34fe90cf65b684eed967d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ce1d3da79cb8f4ded9270f3a48bbde841a4770c43636ef0d6336b61e587c80c93ce3ebe58711c912ffbe2c158ce2ac2c7a20d50203f17d1304bb406eafb41b98
|
7
|
+
data.tar.gz: 8f46167cd368f92067805a9d828a3ece309c5ecde97edf4966b26a41d60eb9ee21e09ec5b01a39aea415bb48ce1f42df66bcd9f3d232b7736610b52089939967
|
data/async-http.gemspec
CHANGED
@@ -17,7 +17,7 @@ Gem::Specification.new do |spec|
|
|
17
17
|
spec.require_paths = ["lib"]
|
18
18
|
|
19
19
|
spec.add_dependency("async", "~> 1.6")
|
20
|
-
spec.add_dependency("async-io", "~> 1.
|
20
|
+
spec.add_dependency("async-io", "~> 1.10")
|
21
21
|
|
22
22
|
spec.add_dependency("http-2", "~> 0.9.0")
|
23
23
|
# spec.add_dependency("openssl")
|
@@ -42,6 +42,11 @@ module Async
|
|
42
42
|
|
43
43
|
# Read the next available chunk.
|
44
44
|
def read
|
45
|
+
# I'm not sure if this is a good idea (*).
|
46
|
+
# if @stopped
|
47
|
+
# raise @stopped
|
48
|
+
# end
|
49
|
+
|
45
50
|
return if @finished
|
46
51
|
|
47
52
|
unless chunk = @queue.dequeue
|
@@ -58,6 +63,8 @@ module Async
|
|
58
63
|
|
59
64
|
# Write a single chunk to the body. Signal completion by calling `#finish`.
|
60
65
|
def write(chunk)
|
66
|
+
# If the reader breaks, the writer will break.
|
67
|
+
# The inverse of this is less obvious (*)
|
61
68
|
if @stopped
|
62
69
|
raise @stopped
|
63
70
|
end
|
data/lib/async/http/client.rb
CHANGED
@@ -27,12 +27,13 @@ require_relative 'middleware'
|
|
27
27
|
module Async
|
28
28
|
module HTTP
|
29
29
|
class Client
|
30
|
-
def initialize(endpoint, protocol = nil, authority = nil, **options)
|
30
|
+
def initialize(endpoint, protocol = nil, authority = nil, retries: 3, **options)
|
31
31
|
@endpoint = endpoint
|
32
32
|
|
33
33
|
@protocol = protocol || endpoint.protocol
|
34
34
|
@authority = authority || endpoint.hostname
|
35
35
|
|
36
|
+
@retries = retries
|
36
37
|
@connections = connect(**options)
|
37
38
|
end
|
38
39
|
|
@@ -60,21 +61,39 @@ module Async
|
|
60
61
|
|
61
62
|
def call(request)
|
62
63
|
request.authority ||= @authority
|
64
|
+
attempt = 0
|
63
65
|
|
64
|
-
|
65
|
-
|
66
|
+
begin
|
67
|
+
attempt += 1
|
68
|
+
|
69
|
+
# As we cache connections, it's possible these connections go bad (e.g. closed by remote host). In this case, we need to try again. It's up to the caller to impose a timeout on this.
|
66
70
|
connection = @connections.acquire
|
67
71
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
72
|
+
response = connection.call(request)
|
73
|
+
|
74
|
+
# The connection won't be released until the body is completely read/released.
|
75
|
+
Body::Streamable.wrap(response) do
|
76
|
+
@connections.release(connection)
|
77
|
+
end
|
78
|
+
|
79
|
+
return response
|
80
|
+
rescue Protocol::RequestFailed
|
81
|
+
# This is a specific case where the entire request wasn't sent before a failure occurred. So, we can even resend non-idempotent requests.
|
82
|
+
@connections.release(connection)
|
83
|
+
|
84
|
+
attempt += 1
|
85
|
+
if attempt < @retries
|
86
|
+
retry
|
87
|
+
else
|
88
|
+
raise
|
89
|
+
end
|
90
|
+
rescue
|
91
|
+
@connections.release(connection)
|
92
|
+
|
93
|
+
if request.idempotent? and attempt < @retries
|
94
|
+
retry
|
75
95
|
else
|
76
|
-
|
77
|
-
connection.close
|
96
|
+
raise
|
78
97
|
end
|
79
98
|
end
|
80
99
|
end
|
@@ -25,34 +25,40 @@ module Async
|
|
25
25
|
module Protocol
|
26
26
|
# Implements basic HTTP/1.1 request/response.
|
27
27
|
class HTTP10 < HTTP11
|
28
|
-
VERSION = "HTTP/1.0".freeze
|
29
28
|
KEEP_ALIVE = 'keep-alive'.freeze
|
30
29
|
|
30
|
+
VERSION = "HTTP/1.0".freeze
|
31
|
+
|
31
32
|
def version
|
32
33
|
VERSION
|
33
34
|
end
|
34
35
|
|
35
36
|
def persistent?(headers)
|
36
|
-
headers
|
37
|
+
headers.delete(CONNECTION) == KEEP_ALIVE
|
37
38
|
end
|
38
39
|
|
39
40
|
# Server loop.
|
40
|
-
def receive_requests
|
41
|
-
while
|
41
|
+
def receive_requests(task: Task.current)
|
42
|
+
while @persistent
|
43
|
+
request = Request.new(*self.read_request)
|
44
|
+
|
45
|
+
unless persistent?(request.headers)
|
46
|
+
@persistent = false
|
47
|
+
end
|
48
|
+
|
42
49
|
response = yield request
|
43
50
|
|
44
51
|
response.version ||= request.version
|
45
52
|
|
46
53
|
write_response(response.version, response.status, response.headers, response.body)
|
47
54
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
break
|
52
|
-
end
|
55
|
+
# This ensures we yield at least once every iteration of the loop and allow other fibers to execute.
|
56
|
+
task.yield
|
53
57
|
end
|
54
|
-
|
55
|
-
|
58
|
+
end
|
59
|
+
|
60
|
+
def write_persistent_header
|
61
|
+
@stream.write("Connection: keep-alive\r\n") if @persistent
|
56
62
|
end
|
57
63
|
|
58
64
|
def write_body(body, chunked = false)
|
@@ -66,7 +72,7 @@ module Async
|
|
66
72
|
end
|
67
73
|
|
68
74
|
# Technically, with HTTP/1.0, if no content-length is specified, we just need to read everything until the connection is closed.
|
69
|
-
|
75
|
+
unless @persistent
|
70
76
|
return Body::Remainder.new(@stream)
|
71
77
|
end
|
72
78
|
end
|
@@ -20,6 +20,8 @@
|
|
20
20
|
|
21
21
|
require 'async/io/protocol/line'
|
22
22
|
|
23
|
+
require_relative 'request_failed'
|
24
|
+
|
23
25
|
require_relative '../request'
|
24
26
|
require_relative '../response'
|
25
27
|
require_relative '../headers'
|
@@ -33,6 +35,11 @@ module Async
|
|
33
35
|
# Implements basic HTTP/1.1 request/response.
|
34
36
|
class HTTP11 < Async::IO::Protocol::Line
|
35
37
|
CRLF = "\r\n".freeze
|
38
|
+
CONNECTION = 'connection'.freeze
|
39
|
+
HOST = 'host'.freeze
|
40
|
+
CLOSE = 'close'.freeze
|
41
|
+
|
42
|
+
VERSION = "HTTP/1.1".freeze
|
36
43
|
|
37
44
|
def initialize(stream)
|
38
45
|
super(stream, CRLF)
|
@@ -49,7 +56,7 @@ module Async
|
|
49
56
|
end
|
50
57
|
|
51
58
|
def reusable?
|
52
|
-
@persistent
|
59
|
+
@persistent && !@stream.closed?
|
53
60
|
end
|
54
61
|
|
55
62
|
class << self
|
@@ -57,23 +64,22 @@ module Async
|
|
57
64
|
alias client new
|
58
65
|
end
|
59
66
|
|
60
|
-
CLOSE = 'close'.freeze
|
61
|
-
|
62
|
-
VERSION = "HTTP/1.1".freeze
|
63
|
-
|
64
67
|
def version
|
65
68
|
VERSION
|
66
69
|
end
|
67
70
|
|
68
71
|
def persistent?(headers)
|
69
|
-
headers
|
72
|
+
headers.delete(CONNECTION) != CLOSE
|
70
73
|
end
|
71
74
|
|
72
75
|
# Server loop.
|
73
76
|
def receive_requests(task: Task.current)
|
74
|
-
while
|
77
|
+
while @persistent
|
75
78
|
request = Request.new(*read_request)
|
76
|
-
|
79
|
+
|
80
|
+
unless persistent?(request.headers)
|
81
|
+
@persistent = false
|
82
|
+
end
|
77
83
|
|
78
84
|
response = yield request
|
79
85
|
|
@@ -83,41 +89,41 @@ module Async
|
|
83
89
|
|
84
90
|
request.finish
|
85
91
|
|
86
|
-
unless persistent?(request.headers) and persistent?(response.headers)
|
87
|
-
@persistent = false
|
88
|
-
|
89
|
-
break
|
90
|
-
end
|
91
|
-
|
92
92
|
# This ensures we yield at least once every iteration of the loop and allow other fibers to execute.
|
93
93
|
task.yield
|
94
94
|
end
|
95
95
|
end
|
96
96
|
|
97
97
|
def call(request)
|
98
|
-
@count += 1
|
99
|
-
|
100
98
|
request.version ||= self.version
|
101
99
|
|
102
100
|
Async.logger.debug(self) {"#{request.method} #{request.path} #{request.headers.inspect}"}
|
103
|
-
|
101
|
+
|
102
|
+
# We carefully interpret https://tools.ietf.org/html/rfc7230#section-6.3.1 to implement this correctly.
|
103
|
+
begin
|
104
|
+
write_request(request.authority, request.method, request.path, request.version, request.headers)
|
105
|
+
rescue
|
106
|
+
# If we fail to fully write the request and body, we can retry this request.
|
107
|
+
raise RequestFailed.new
|
108
|
+
end
|
109
|
+
|
110
|
+
# Once we start writing the body, we can't recover if the request fails. That's because the body might be generated dynamically, streaming, etc.
|
111
|
+
write_body(request.body)
|
104
112
|
|
105
113
|
return Response.new(*read_response)
|
106
114
|
rescue EOFError
|
107
|
-
|
108
|
-
|
115
|
+
# This will ensure that #reusable? returns false.
|
116
|
+
@stream.close
|
117
|
+
|
118
|
+
raise
|
109
119
|
end
|
110
120
|
|
111
|
-
def write_request(authority, method, path, version, headers
|
121
|
+
def write_request(authority, method, path, version, headers)
|
112
122
|
@stream.write("#{method} #{path} #{version}\r\n")
|
113
123
|
@stream.write("Host: #{authority}\r\n")
|
114
|
-
|
115
124
|
write_headers(headers)
|
116
|
-
write_body(body)
|
117
125
|
|
118
126
|
@stream.flush
|
119
|
-
|
120
|
-
return true
|
121
127
|
end
|
122
128
|
|
123
129
|
def read_response
|
@@ -125,6 +131,8 @@ module Async
|
|
125
131
|
headers = read_headers
|
126
132
|
body = read_body(headers)
|
127
133
|
|
134
|
+
@count += 1
|
135
|
+
|
128
136
|
@persistent = persistent?(headers)
|
129
137
|
|
130
138
|
return version, Integer(status), reason, headers, body
|
@@ -135,7 +143,9 @@ module Async
|
|
135
143
|
headers = read_headers
|
136
144
|
body = read_body(headers)
|
137
145
|
|
138
|
-
|
146
|
+
@count += 1
|
147
|
+
|
148
|
+
return headers.delete(HOST), method, path, version, headers, body
|
139
149
|
end
|
140
150
|
|
141
151
|
def write_response(version, status, headers, body)
|
@@ -144,16 +154,20 @@ module Async
|
|
144
154
|
write_body(body)
|
145
155
|
|
146
156
|
@stream.flush
|
147
|
-
|
148
|
-
return true
|
149
157
|
end
|
150
158
|
|
151
159
|
protected
|
152
160
|
|
161
|
+
def write_persistent_header
|
162
|
+
@stream.write("Connection: close\r\n") unless @persistent
|
163
|
+
end
|
164
|
+
|
153
165
|
def write_headers(headers)
|
154
166
|
headers.each do |name, value|
|
155
167
|
@stream.write("#{name}: #{value}\r\n")
|
156
168
|
end
|
169
|
+
|
170
|
+
write_persistent_header
|
157
171
|
end
|
158
172
|
|
159
173
|
def read_headers
|
@@ -196,6 +210,8 @@ module Async
|
|
196
210
|
@stream.write(chunk)
|
197
211
|
end
|
198
212
|
end
|
213
|
+
|
214
|
+
@stream.flush
|
199
215
|
end
|
200
216
|
|
201
217
|
def read_body(headers)
|
@@ -66,6 +66,13 @@ module Async
|
|
66
66
|
Async.logger.debug(self) {"Received frame: #{frame.inspect}"}
|
67
67
|
end
|
68
68
|
|
69
|
+
@controller.on(:goaway) do |payload|
|
70
|
+
Async.logger.error(self) {"goaway: #{payload.inspect}"}
|
71
|
+
|
72
|
+
@reader.stop
|
73
|
+
@stream.io.close
|
74
|
+
end
|
75
|
+
|
69
76
|
@count = 0
|
70
77
|
end
|
71
78
|
|
@@ -77,7 +84,7 @@ module Async
|
|
77
84
|
end
|
78
85
|
|
79
86
|
def reusable?
|
80
|
-
|
87
|
+
!@stream.closed?
|
81
88
|
end
|
82
89
|
|
83
90
|
def version
|
@@ -102,15 +109,14 @@ module Async
|
|
102
109
|
|
103
110
|
def close
|
104
111
|
Async.logger.debug(self) {"Closing connection"}
|
105
|
-
|
112
|
+
|
113
|
+
@reader.stop if @reader
|
106
114
|
@stream.close
|
107
115
|
end
|
108
116
|
|
109
117
|
def receive_requests(task: Task.current, &block)
|
110
118
|
# emits new streams opened by the client
|
111
119
|
@controller.on(:stream) do |stream|
|
112
|
-
@count += 1
|
113
|
-
|
114
120
|
request = Request.new
|
115
121
|
request.version = self.version
|
116
122
|
request.headers = Headers.new
|
@@ -136,33 +142,40 @@ module Async
|
|
136
142
|
body.write(chunk.to_s) unless chunk.empty?
|
137
143
|
end
|
138
144
|
|
145
|
+
stream.on(:close) do |error|
|
146
|
+
if error
|
147
|
+
body.stop(EOFError.new(error))
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
139
151
|
stream.on(:half_close) do
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
# puts "Sending response..."
|
147
|
-
# send response
|
148
|
-
headers = {STATUS => response.status.to_s}
|
149
|
-
headers.update(response.headers)
|
150
|
-
|
151
|
-
# puts "Sending headers #{headers}"
|
152
|
-
if response.body.nil? or response.body.empty?
|
153
|
-
stream.headers(headers, end_stream: true)
|
154
|
-
response.body.read if response.body
|
155
|
-
else
|
156
|
-
stream.headers(headers, end_stream: false)
|
152
|
+
begin
|
153
|
+
# We are no longer receiving any more data frames:
|
154
|
+
body.finish
|
155
|
+
|
156
|
+
# Generate the response:
|
157
|
+
response = yield request
|
157
158
|
|
158
|
-
|
159
|
-
response.
|
160
|
-
|
161
|
-
|
159
|
+
headers = {STATUS => response.status.to_s}
|
160
|
+
headers.update(response.headers)
|
161
|
+
|
162
|
+
if response.body.nil? or response.body.empty?
|
163
|
+
stream.headers(headers, end_stream: true)
|
164
|
+
response.body.read if response.body
|
165
|
+
else
|
166
|
+
stream.headers(headers, end_stream: false)
|
167
|
+
|
168
|
+
response.body.each do |chunk|
|
169
|
+
stream.data(chunk, end_stream: false)
|
170
|
+
end
|
171
|
+
|
172
|
+
stream.data("", end_stream: true)
|
162
173
|
end
|
174
|
+
rescue
|
175
|
+
Async.logger.error(self) {$!}
|
163
176
|
|
164
|
-
#
|
165
|
-
stream.
|
177
|
+
# Generating the response failed.
|
178
|
+
stream.close(:internal_error)
|
166
179
|
end
|
167
180
|
end
|
168
181
|
end
|
@@ -172,11 +185,10 @@ module Async
|
|
172
185
|
end
|
173
186
|
|
174
187
|
def call(request)
|
175
|
-
@count += 1
|
176
|
-
|
177
188
|
request.version ||= self.version
|
178
189
|
|
179
190
|
stream = @controller.new_stream
|
191
|
+
@count += 1
|
180
192
|
|
181
193
|
headers = {
|
182
194
|
SCHEME => HTTPS,
|
@@ -187,6 +199,7 @@ module Async
|
|
187
199
|
|
188
200
|
finished = Async::Notification.new
|
189
201
|
|
202
|
+
exception = nil
|
190
203
|
response = Response.new
|
191
204
|
response.version = self.version
|
192
205
|
response.headers = {}
|
@@ -204,6 +217,16 @@ module Async
|
|
204
217
|
end
|
205
218
|
end
|
206
219
|
|
220
|
+
# At this point, we are now expecting two events: data and close.
|
221
|
+
stream.on(:close) do |error|
|
222
|
+
# If we receive close after this point, it's not a request error, but a failure we need to signal to the body.
|
223
|
+
if error
|
224
|
+
body.stop(EOFError.new(error))
|
225
|
+
else
|
226
|
+
body.finish
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
207
230
|
finished.signal
|
208
231
|
end
|
209
232
|
|
@@ -211,15 +234,23 @@ module Async
|
|
211
234
|
body.write(chunk.to_s) unless chunk.empty?
|
212
235
|
end
|
213
236
|
|
214
|
-
stream.on(:close) do
|
215
|
-
|
237
|
+
stream.on(:close) do |error|
|
238
|
+
# The remote server has closed the connection while we were sending the request.
|
239
|
+
if error
|
240
|
+
exception = EOFError.new(error)
|
241
|
+
finished.signal
|
242
|
+
end
|
216
243
|
end
|
217
244
|
|
218
245
|
if request.body.nil? or request.body.empty?
|
219
246
|
stream.headers(headers, end_stream: true)
|
220
247
|
request.body.read if request.body
|
221
248
|
else
|
222
|
-
|
249
|
+
begin
|
250
|
+
stream.headers(headers, end_stream: false)
|
251
|
+
rescue
|
252
|
+
raise RequestFailed.new
|
253
|
+
end
|
223
254
|
|
224
255
|
request.body.each do |chunk|
|
225
256
|
stream.data(chunk, end_stream: false)
|
@@ -231,10 +262,14 @@ module Async
|
|
231
262
|
start_connection
|
232
263
|
@stream.flush
|
233
264
|
|
234
|
-
|
265
|
+
Async.logger.debug(self) {"Stream flushed, waiting for signal."}
|
235
266
|
finished.wait
|
236
267
|
|
237
|
-
|
268
|
+
if exception
|
269
|
+
raise exception
|
270
|
+
end
|
271
|
+
|
272
|
+
Async.logger.debug(self) {"Stream finished: #{response.inspect}"}
|
238
273
|
return response
|
239
274
|
end
|
240
275
|
end
|
@@ -0,0 +1,29 @@
|
|
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
|
+
module Protocol
|
24
|
+
# Failed to send the request. The request body has NOT been consumed (i.e. #read) and you should retry the request.
|
25
|
+
class RequestFailed < StandardError
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/async/http/request.rb
CHANGED
data/lib/async/http/server.rb
CHANGED
@@ -46,7 +46,7 @@ module Async
|
|
46
46
|
stream = Async::IO::Stream.new(peer)
|
47
47
|
protocol = @protocol_class.server(stream)
|
48
48
|
|
49
|
-
|
49
|
+
Async.logger.debug(self) {"Incoming connnection from #{address.inspect} to #{protocol}"}
|
50
50
|
|
51
51
|
hijack = catch(:hijack) do
|
52
52
|
protocol.receive_requests do |request|
|
@@ -62,7 +62,7 @@ module Async
|
|
62
62
|
hijack.call(peer)
|
63
63
|
end
|
64
64
|
rescue EOFError, Errno::ECONNRESET, Errno::EPIPE
|
65
|
-
# Sometimes client will disconnect without completing a result or reading the entire buffer.
|
65
|
+
# Sometimes client will disconnect without completing a result or reading the entire buffer. That means we are done.
|
66
66
|
end
|
67
67
|
|
68
68
|
def run
|
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.23.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-05-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: async
|
@@ -30,14 +30,14 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '1.
|
33
|
+
version: '1.10'
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '1.
|
40
|
+
version: '1.10'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: http-2
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -146,6 +146,7 @@ files:
|
|
146
146
|
- lib/async/http/protocol/http11.rb
|
147
147
|
- lib/async/http/protocol/http2.rb
|
148
148
|
- lib/async/http/protocol/https.rb
|
149
|
+
- lib/async/http/protocol/request_failed.rb
|
149
150
|
- lib/async/http/reference.rb
|
150
151
|
- lib/async/http/relative_location.rb
|
151
152
|
- lib/async/http/request.rb
|