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