async-http 0.52.4 → 0.54.1
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/bake/async/http/h2spec.rb +1 -1
- data/lib/async/http/body/delayed.rb +2 -2
- data/lib/async/http/body/hijack.rb +5 -0
- data/lib/async/http/body/pipe.rb +15 -7
- data/lib/async/http/body/stream.rb +1 -1
- data/lib/async/http/client.rb +3 -3
- data/lib/async/http/protocol/http1.rb +2 -2
- data/lib/async/http/protocol/http1/connection.rb +0 -5
- data/lib/async/http/protocol/http1/server.rb +1 -1
- data/lib/async/http/protocol/http10.rb +2 -2
- data/lib/async/http/protocol/http11.rb +2 -2
- data/lib/async/http/protocol/http2.rb +0 -18
- data/lib/async/http/protocol/http2/connection.rb +7 -0
- data/lib/async/http/protocol/http2/output.rb +1 -1
- data/lib/async/http/protocol/http2/request.rb +0 -38
- data/lib/async/http/protocol/http2/response.rb +6 -13
- data/lib/async/http/protocol/request.rb +0 -4
- data/lib/async/http/proxy.rb +24 -8
- data/lib/async/http/server.rb +3 -3
- data/lib/async/http/version.rb +1 -1
- metadata +22 -66
- data/.editorconfig +0 -6
- data/.github/workflows/development.yml +0 -52
- data/.gitignore +0 -15
- data/.rspec +0 -3
- data/.travis.yml +0 -35
- data/README.md +0 -365
- data/async-http.gemspec +0 -39
- data/bake.rb +0 -0
- data/examples/compare/Gemfile +0 -9
- data/examples/compare/benchmark.rb +0 -78
- data/examples/download/chunked.rb +0 -86
- data/examples/fetch/Gemfile +0 -3
- data/examples/fetch/Gemfile.lock +0 -74
- data/examples/fetch/README.md +0 -3
- data/examples/fetch/config.ru +0 -28
- data/examples/fetch/public/index.html +0 -23
- data/examples/fetch/public/stream.js +0 -56
- data/examples/google/search.rb +0 -47
- data/examples/licenses/gemspect.rb +0 -71
- data/examples/licenses/list.rb +0 -90
- data/examples/request.rb +0 -38
- data/examples/stream/stop.rb +0 -28
- data/examples/trenni/Gemfile +0 -5
- data/examples/trenni/streaming.rb +0 -35
- data/examples/upload/client.rb +0 -39
- data/examples/upload/data.txt +0 -41
- data/examples/upload/server.rb +0 -19
- data/examples/upload/upload.rb +0 -26
- data/gems.rb +0 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 82df1313e46a49ebe4ba6c345c911dcf0c899e59361f09abaedc5f7c7979e61f
|
4
|
+
data.tar.gz: 838d76ab9e99d661ac7be82169d6931325e64d22fab02ca93089d5da2ed13b31
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0d78d6636a59967ab8de9c76eddad387d6a607c04224563bf6599ab4b637d5fccceffa462d700c7f4d19c4ce1bee6b6f72562dd91cc6f9532802dbe1769f4315
|
7
|
+
data.tar.gz: bcdc8bc22dfeb30ed425ad4117c682aca370acfab125a478cba7af03ec83b29eafabd5f8b0194adaf060dbdf03d7c926f348658cd9b89d4b5b12b583e33a9dc6
|
data/bake/async/http/h2spec.rb
CHANGED
@@ -29,7 +29,7 @@ def server
|
|
29
29
|
Async.logger.info(self){"Starting server..."}
|
30
30
|
|
31
31
|
container.run(count: 1) do
|
32
|
-
server = Async::HTTP::Server.for(endpoint, Async::HTTP::Protocol::HTTP2, "https") do |request|
|
32
|
+
server = Async::HTTP::Server.for(endpoint, protocol: Async::HTTP::Protocol::HTTP2, scheme: "https") do |request|
|
33
33
|
Protocol::HTTP::Response[200, {'content-type' => 'text/plain'}, ["Hello World"]]
|
34
34
|
end
|
35
35
|
|
@@ -20,12 +20,12 @@
|
|
20
20
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
21
|
# THE SOFTWARE.
|
22
22
|
|
23
|
-
|
23
|
+
require 'protocol/http/body/wrapper'
|
24
24
|
|
25
25
|
module Async
|
26
26
|
module HTTP
|
27
27
|
module Body
|
28
|
-
class Delayed <
|
28
|
+
class Delayed < Protocol::HTTP::Body::Wrapper
|
29
29
|
def initialize(body, delay = 0.01)
|
30
30
|
super(body)
|
31
31
|
|
data/lib/async/http/body/pipe.rb
CHANGED
@@ -20,6 +20,9 @@
|
|
20
20
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
21
|
# THE SOFTWARE.
|
22
22
|
|
23
|
+
require 'async/io/socket'
|
24
|
+
require 'async/io/stream'
|
25
|
+
|
23
26
|
require_relative 'writable'
|
24
27
|
|
25
28
|
module Async
|
@@ -39,8 +42,8 @@ module Async
|
|
39
42
|
@reader = nil
|
40
43
|
@writer = nil
|
41
44
|
|
42
|
-
task.async(&self.method(:reader))
|
43
|
-
task.async(&self.method(:writer))
|
45
|
+
task.async(transient: true, &self.method(:reader))
|
46
|
+
task.async(transient: true, &self.method(:writer))
|
44
47
|
end
|
45
48
|
|
46
49
|
def to_io
|
@@ -69,10 +72,9 @@ module Async
|
|
69
72
|
|
70
73
|
@head.close_write
|
71
74
|
ensure
|
72
|
-
@reader = nil
|
73
75
|
@input.close($!)
|
74
76
|
|
75
|
-
|
77
|
+
close_head if @writer&.finished?
|
76
78
|
end
|
77
79
|
|
78
80
|
# Read from the head of the pipe and write to the @output stream.
|
@@ -86,11 +88,17 @@ module Async
|
|
86
88
|
@output.write(chunk)
|
87
89
|
end
|
88
90
|
ensure
|
89
|
-
@writer = nil
|
90
|
-
|
91
91
|
@output.close($!)
|
92
92
|
|
93
|
-
|
93
|
+
close_head if @reader&.finished?
|
94
|
+
end
|
95
|
+
|
96
|
+
def close_head
|
97
|
+
@head.close
|
98
|
+
|
99
|
+
# Both tasks are done, don't keep references:
|
100
|
+
@reader = nil
|
101
|
+
@writer = nil
|
94
102
|
end
|
95
103
|
end
|
96
104
|
end
|
data/lib/async/http/client.rb
CHANGED
@@ -25,7 +25,7 @@ require 'async/io/stream'
|
|
25
25
|
|
26
26
|
require 'async/pool/controller'
|
27
27
|
|
28
|
-
require 'protocol/http/body/
|
28
|
+
require 'protocol/http/body/completable'
|
29
29
|
require 'protocol/http/methods'
|
30
30
|
|
31
31
|
require_relative 'protocol'
|
@@ -45,7 +45,7 @@ module Async
|
|
45
45
|
# @param protocol [Protocol::HTTP1 | Protocol::HTTP2 | Protocol::HTTPS] the protocol to use.
|
46
46
|
# @param scheme [String] The default scheme to set to requests.
|
47
47
|
# @param authority [String] The default authority to set to requests.
|
48
|
-
def initialize(endpoint, protocol
|
48
|
+
def initialize(endpoint, protocol: endpoint.protocol, scheme: endpoint.scheme, authority: endpoint.authority, retries: DEFAULT_RETRIES, connection_limit: DEFAULT_CONNECTION_LIMIT)
|
49
49
|
@endpoint = endpoint
|
50
50
|
@protocol = protocol
|
51
51
|
|
@@ -143,7 +143,7 @@ module Async
|
|
143
143
|
response = request.call(connection)
|
144
144
|
|
145
145
|
# The connection won't be released until the body is completely read/released.
|
146
|
-
::Protocol::HTTP::Body::
|
146
|
+
::Protocol::HTTP::Body::Completable.wrap(response) do
|
147
147
|
@pool.release(connection)
|
148
148
|
end
|
149
149
|
|
@@ -38,13 +38,13 @@ module Async
|
|
38
38
|
end
|
39
39
|
|
40
40
|
def self.client(peer)
|
41
|
-
stream = IO::Stream.new(peer, sync:
|
41
|
+
stream = IO::Stream.new(peer, sync: true)
|
42
42
|
|
43
43
|
return HTTP1::Client.new(stream, VERSION)
|
44
44
|
end
|
45
45
|
|
46
46
|
def self.server(peer)
|
47
|
-
stream = IO::Stream.new(peer, sync:
|
47
|
+
stream = IO::Stream.new(peer, sync: true)
|
48
48
|
|
49
49
|
return HTTP1::Server.new(stream, VERSION)
|
50
50
|
end
|
@@ -37,13 +37,13 @@ module Async
|
|
37
37
|
end
|
38
38
|
|
39
39
|
def self.client(peer)
|
40
|
-
stream = IO::Stream.new(peer, sync:
|
40
|
+
stream = IO::Stream.new(peer, sync: true)
|
41
41
|
|
42
42
|
return HTTP1::Client.new(stream, VERSION)
|
43
43
|
end
|
44
44
|
|
45
45
|
def self.server(peer)
|
46
|
-
stream = IO::Stream.new(peer, sync:
|
46
|
+
stream = IO::Stream.new(peer, sync: true)
|
47
47
|
|
48
48
|
return HTTP1::Server.new(stream, VERSION)
|
49
49
|
end
|
@@ -37,13 +37,13 @@ module Async
|
|
37
37
|
end
|
38
38
|
|
39
39
|
def self.client(peer)
|
40
|
-
stream = IO::Stream.new(peer, sync:
|
40
|
+
stream = IO::Stream.new(peer, sync: true)
|
41
41
|
|
42
42
|
return HTTP1::Client.new(stream, VERSION)
|
43
43
|
end
|
44
44
|
|
45
45
|
def self.server(peer)
|
46
|
-
stream = IO::Stream.new(peer, sync:
|
46
|
+
stream = IO::Stream.new(peer, sync: true)
|
47
47
|
|
48
48
|
return HTTP1::Server.new(stream, VERSION)
|
49
49
|
end
|
@@ -76,24 +76,6 @@ module Async
|
|
76
76
|
def self.names
|
77
77
|
["h2"]
|
78
78
|
end
|
79
|
-
|
80
|
-
module WithPush
|
81
|
-
CLIENT_SETTINGS = HTTP2::CLIENT_SETTINGS.merge(
|
82
|
-
::Protocol::HTTP2::Settings::ENABLE_PUSH => 1,
|
83
|
-
)
|
84
|
-
|
85
|
-
def self.client(peer, settings = CLIENT_SETTINGS)
|
86
|
-
HTTP2.client(peer, settings)
|
87
|
-
end
|
88
|
-
|
89
|
-
def self.server(peer, settings = SERVER_SETTINGS)
|
90
|
-
HTTP2.server(peer, settings)
|
91
|
-
end
|
92
|
-
|
93
|
-
def self.names
|
94
|
-
HTTP2.names
|
95
|
-
end
|
96
|
-
end
|
97
79
|
end
|
98
80
|
end
|
99
81
|
end
|
@@ -107,6 +107,13 @@ module Async
|
|
107
107
|
end
|
108
108
|
rescue SocketError, IOError, EOFError, Errno::ECONNRESET, Errno::EPIPE, Async::Wrapper::Cancelled
|
109
109
|
# Ignore.
|
110
|
+
rescue ::Protocol::HTTP2::GoawayError => error
|
111
|
+
# Error is raised if a response is actively reading from the
|
112
|
+
# connection. The connection is silently closed if GOAWAY is
|
113
|
+
# received outside the request/response cycle.
|
114
|
+
if @reader
|
115
|
+
self.close(error)
|
116
|
+
end
|
110
117
|
ensure
|
111
118
|
# Don't call #close twice.
|
112
119
|
if @reader
|
@@ -39,18 +39,6 @@ module Async
|
|
39
39
|
|
40
40
|
attr :request
|
41
41
|
|
42
|
-
# Create a fake request on the server, with the given headers.
|
43
|
-
def create_push_promise_stream(headers)
|
44
|
-
stream = @connection.create_push_promise_stream(&Stream.method(:create))
|
45
|
-
|
46
|
-
stream.headers = ::Protocol::HTTP::Headers.new
|
47
|
-
|
48
|
-
# This will ultimately enqueue the request to be processed by the server:
|
49
|
-
stream.receive_initial_headers(headers, false)
|
50
|
-
|
51
|
-
return stream
|
52
|
-
end
|
53
|
-
|
54
42
|
def receive_initial_headers(headers, end_stream)
|
55
43
|
headers.each do |key, value|
|
56
44
|
if key == SCHEME
|
@@ -133,32 +121,6 @@ module Async
|
|
133
121
|
false
|
134
122
|
end
|
135
123
|
|
136
|
-
def push?
|
137
|
-
@stream.connection.enable_push?
|
138
|
-
end
|
139
|
-
|
140
|
-
# @return [Stream] the promised stream, on which to send data.
|
141
|
-
def push(path, headers = nil, scheme = @scheme, authority = @authority)
|
142
|
-
raise ArgumentError, "Missing scheme!" unless scheme
|
143
|
-
raise ArgumentError, "Missing authority!" unless authority
|
144
|
-
|
145
|
-
push_headers = [
|
146
|
-
[SCHEME, scheme],
|
147
|
-
[METHOD, ::Protocol::HTTP::Methods::GET],
|
148
|
-
[PATH, path],
|
149
|
-
[AUTHORITY, authority]
|
150
|
-
]
|
151
|
-
|
152
|
-
if headers
|
153
|
-
push_headers = Headers::Merged.new(
|
154
|
-
push_headers,
|
155
|
-
headers
|
156
|
-
)
|
157
|
-
end
|
158
|
-
|
159
|
-
@stream.send_push_promise(push_headers)
|
160
|
-
end
|
161
|
-
|
162
124
|
NO_RESPONSE = [
|
163
125
|
[STATUS, '500'],
|
164
126
|
]
|
@@ -50,13 +50,7 @@ module Async
|
|
50
50
|
end
|
51
51
|
|
52
52
|
def accept_push_promise_stream(promised_stream_id, headers)
|
53
|
-
|
54
|
-
|
55
|
-
stream.response.build_request(headers)
|
56
|
-
|
57
|
-
@response.promises.enqueue(stream.response)
|
58
|
-
|
59
|
-
return stream
|
53
|
+
raise ProtocolError, "Cannot accept push promise stream!"
|
60
54
|
end
|
61
55
|
|
62
56
|
# This should be invoked from the background reader, and notifies the task waiting for the headers that we are done.
|
@@ -113,7 +107,6 @@ module Async
|
|
113
107
|
super
|
114
108
|
|
115
109
|
if @response
|
116
|
-
@response.promises.enqueue nil
|
117
110
|
@response = nil
|
118
111
|
end
|
119
112
|
|
@@ -128,7 +121,6 @@ module Async
|
|
128
121
|
|
129
122
|
@stream = stream
|
130
123
|
@request = nil
|
131
|
-
@promises = nil
|
132
124
|
end
|
133
125
|
|
134
126
|
attr :stream
|
@@ -150,10 +142,6 @@ module Async
|
|
150
142
|
!!@status
|
151
143
|
end
|
152
144
|
|
153
|
-
def promises
|
154
|
-
@promises ||= Async::Queue.new
|
155
|
-
end
|
156
|
-
|
157
145
|
def build_request(headers)
|
158
146
|
request = ::Protocol::HTTP::Request.new
|
159
147
|
request.headers = ::Protocol::HTTP::Headers.new
|
@@ -215,6 +203,11 @@ module Async
|
|
215
203
|
if request.body.nil?
|
216
204
|
@stream.send_headers(nil, headers, ::Protocol::HTTP2::END_STREAM)
|
217
205
|
else
|
206
|
+
if length = request.body.length
|
207
|
+
# This puts it at the end of the pseudo-headers:
|
208
|
+
pseudo_headers << [CONTENT_LENGTH, length]
|
209
|
+
end
|
210
|
+
|
218
211
|
# This function informs the headers object that any subsequent headers are going to be trailers. Therefore, it must be called *before* sending the headers, to avoid any race conditions.
|
219
212
|
trailers = request.headers.trailers!
|
220
213
|
|
data/lib/async/http/proxy.rb
CHANGED
@@ -30,6 +30,15 @@ module Async
|
|
30
30
|
# Wraps a client, address and headers required to initiate a connectio to a remote host using the CONNECT verb.
|
31
31
|
# Behaves like a TCP endpoint for the purposes of connecting to a remote host.
|
32
32
|
class Proxy
|
33
|
+
class ConnectFailure < StandardError
|
34
|
+
def initialize(response)
|
35
|
+
super "Failed to connect: #{response.status}"
|
36
|
+
@response = response
|
37
|
+
end
|
38
|
+
|
39
|
+
attr :response
|
40
|
+
end
|
41
|
+
|
33
42
|
module Client
|
34
43
|
def proxy(endpoint, headers = nil)
|
35
44
|
Proxy.new(self, endpoint.authority(false), headers)
|
@@ -92,14 +101,21 @@ module Async
|
|
92
101
|
|
93
102
|
response = @client.connect(@address.to_s, @headers, input)
|
94
103
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
104
|
+
if response.success?
|
105
|
+
pipe = Body::Pipe.new(response.body, input)
|
106
|
+
|
107
|
+
return pipe.to_io unless block_given?
|
108
|
+
|
109
|
+
begin
|
110
|
+
yield pipe.to_io
|
111
|
+
ensure
|
112
|
+
pipe.close
|
113
|
+
end
|
114
|
+
else
|
115
|
+
# This ensures we don't leave a response dangling:
|
116
|
+
response.close
|
117
|
+
|
118
|
+
raise ConnectFailure, response
|
103
119
|
end
|
104
120
|
end
|
105
121
|
|
data/lib/async/http/server.rb
CHANGED
@@ -29,11 +29,11 @@ require 'protocol/http/middleware'
|
|
29
29
|
module Async
|
30
30
|
module HTTP
|
31
31
|
class Server < ::Protocol::HTTP::Middleware
|
32
|
-
def self.for(*arguments, &block)
|
33
|
-
self.new(block, *arguments)
|
32
|
+
def self.for(*arguments, **options, &block)
|
33
|
+
self.new(block, *arguments, **options)
|
34
34
|
end
|
35
35
|
|
36
|
-
def initialize(app, endpoint, protocol
|
36
|
+
def initialize(app, endpoint, protocol: endpoint.protocol, scheme: endpoint.scheme)
|
37
37
|
super(app)
|
38
38
|
|
39
39
|
@endpoint = endpoint
|