async-http 0.77.0 → 0.79.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/lib/async/http/client.rb +0 -1
- data/lib/async/http/protocol/http1/client.rb +12 -9
- data/lib/async/http/protocol/http1/connection.rb +2 -0
- data/lib/async/http/protocol/http1/finishable.rb +62 -0
- data/lib/async/http/protocol/http1/server.rb +40 -20
- data/lib/async/http/protocol/http2/stream.rb +8 -6
- data/lib/async/http/protocol/request.rb +4 -0
- data/lib/async/http/protocol/response.rb +4 -0
- data/lib/async/http/version.rb +1 -1
- data.tar.gz.sig +0 -0
- metadata +7 -7
- metadata.gz.sig +0 -0
- data/lib/async/http/body/finishable.rb +0 -56
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 93d3d9d22a84537ba522b417586a7bc02d243d56defe26fb9a3937470675b244
|
4
|
+
data.tar.gz: 8c16add5b2c301948235c739d076f92c400cdd745cc36881fac49125a41c5bc4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cf362c7394967d2746e6d37984f25f5fc02e8e1fa89b7d5f6afcf31d87e665d75877cd5a9cd920f1ae87b6ecbc73e7d9b7cedc72467a69d95b5d24a04b897a8e
|
7
|
+
data.tar.gz: e922cf9cfd745423d9350f633d1a152dd8f6e06ae23abd3c8b1e403f49abb7089181aa68a1ded1a329cf2017b7bf98feb6a60fed45e1f8582eb3b01ca401ce4d
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data/lib/async/http/client.rb
CHANGED
@@ -18,11 +18,12 @@ module Async
|
|
18
18
|
|
19
19
|
attr_accessor :pool
|
20
20
|
|
21
|
-
def closed
|
21
|
+
def closed(error = nil)
|
22
22
|
super
|
23
23
|
|
24
24
|
if pool = @pool
|
25
25
|
@pool = nil
|
26
|
+
# If the connection is not reusable, this will retire it from the connection pool and invoke `#close`.
|
26
27
|
pool.release(self)
|
27
28
|
end
|
28
29
|
end
|
@@ -50,30 +51,32 @@ module Async
|
|
50
51
|
task.async(annotation: "Upgrading request...") do
|
51
52
|
# If this fails, this connection will be closed.
|
52
53
|
write_upgrade_body(protocol, body)
|
54
|
+
rescue => error
|
55
|
+
self.close(error)
|
53
56
|
end
|
54
57
|
elsif request.connect?
|
55
58
|
task.async(annotation: "Tunnneling request...") do
|
56
59
|
write_tunnel_body(@version, body)
|
60
|
+
rescue => error
|
61
|
+
self.close(error)
|
57
62
|
end
|
58
63
|
else
|
59
64
|
task.async(annotation: "Streaming request...") do
|
60
65
|
# 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.
|
61
66
|
write_body(@version, body, false, trailer)
|
67
|
+
rescue => error
|
68
|
+
self.close(error)
|
62
69
|
end
|
63
70
|
end
|
64
71
|
elsif protocol = request.protocol
|
65
72
|
write_upgrade_body(protocol)
|
66
73
|
else
|
67
|
-
write_body(@version, body, false, trailer)
|
74
|
+
write_body(@version, request.body, false, trailer)
|
68
75
|
end
|
69
76
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
rescue
|
74
|
-
# This will ensure that #reusable? returns false.
|
75
|
-
self.close
|
76
|
-
|
77
|
+
return Response.read(self, request)
|
78
|
+
rescue => error
|
79
|
+
self.close(error)
|
77
80
|
raise
|
78
81
|
end
|
79
82
|
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Released under the MIT License.
|
4
|
+
# Copyright, 2024, by Samuel Williams.
|
5
|
+
|
6
|
+
require "protocol/http/body/wrapper"
|
7
|
+
require "async/variable"
|
8
|
+
|
9
|
+
module Async
|
10
|
+
module HTTP
|
11
|
+
module Protocol
|
12
|
+
module HTTP1
|
13
|
+
# Keeps track of whether a body is being read, and if so, waits for it to be closed.
|
14
|
+
class Finishable < ::Protocol::HTTP::Body::Wrapper
|
15
|
+
def initialize(body)
|
16
|
+
super(body)
|
17
|
+
|
18
|
+
@closed = Async::Variable.new
|
19
|
+
@error = nil
|
20
|
+
|
21
|
+
@reading = false
|
22
|
+
end
|
23
|
+
|
24
|
+
def reading?
|
25
|
+
@reading
|
26
|
+
end
|
27
|
+
|
28
|
+
def read
|
29
|
+
@reading = true
|
30
|
+
|
31
|
+
super
|
32
|
+
end
|
33
|
+
|
34
|
+
def close(error = nil)
|
35
|
+
super
|
36
|
+
|
37
|
+
unless @closed.resolved?
|
38
|
+
@error = error
|
39
|
+
@closed.value = true
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def wait(persistent = true)
|
44
|
+
if @reading
|
45
|
+
@closed.wait
|
46
|
+
elsif persistent
|
47
|
+
# If the connection can be reused, let's gracefully discard the body:
|
48
|
+
self.discard
|
49
|
+
else
|
50
|
+
# Else, we don't care about the body, so we can close it immediately:
|
51
|
+
self.close
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def inspect
|
56
|
+
"#<#{self.class} closed=#{@closed} error=#{@error}> | #{super}"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -7,7 +7,7 @@
|
|
7
7
|
# Copyright, 2024, by Anton Zhuravsky.
|
8
8
|
|
9
9
|
require_relative "connection"
|
10
|
-
require_relative "
|
10
|
+
require_relative "finishable"
|
11
11
|
|
12
12
|
require "console/event/failure"
|
13
13
|
|
@@ -16,6 +16,18 @@ module Async
|
|
16
16
|
module Protocol
|
17
17
|
module HTTP1
|
18
18
|
class Server < Connection
|
19
|
+
def initialize(...)
|
20
|
+
super
|
21
|
+
|
22
|
+
@ready = Async::Notification.new
|
23
|
+
end
|
24
|
+
|
25
|
+
def closed(error = nil)
|
26
|
+
super
|
27
|
+
|
28
|
+
@ready.signal
|
29
|
+
end
|
30
|
+
|
19
31
|
def fail_request(status)
|
20
32
|
@persistent = false
|
21
33
|
write_response(@version, status, {})
|
@@ -26,8 +38,11 @@ module Async
|
|
26
38
|
end
|
27
39
|
|
28
40
|
def next_request
|
29
|
-
|
30
|
-
|
41
|
+
if closed?
|
42
|
+
return nil
|
43
|
+
elsif !idle?
|
44
|
+
@ready.wait
|
45
|
+
end
|
31
46
|
|
32
47
|
# Read an incoming request:
|
33
48
|
return unless request = Request.read(self)
|
@@ -49,7 +64,7 @@ module Async
|
|
49
64
|
|
50
65
|
while request = next_request
|
51
66
|
if body = request.body
|
52
|
-
finishable =
|
67
|
+
finishable = Finishable.new(body)
|
53
68
|
request.body = finishable
|
54
69
|
end
|
55
70
|
|
@@ -73,38 +88,41 @@ module Async
|
|
73
88
|
# We force a 101 response if the protocol is upgraded - HTTP/2 CONNECT will return 200 for success, but this won't be understood by HTTP/1 clients:
|
74
89
|
write_response(@version, 101, response.headers)
|
75
90
|
|
76
|
-
stream = write_upgrade_body(protocol)
|
77
|
-
|
78
91
|
# At this point, the request body is hijacked, so we don't want to call #finish below.
|
79
92
|
request = nil
|
80
93
|
response = nil
|
81
94
|
|
82
|
-
|
83
|
-
|
95
|
+
if body.stream?
|
96
|
+
return body.call(write_upgrade_body(protocol))
|
97
|
+
else
|
98
|
+
write_upgrade_body(protocol, body)
|
99
|
+
end
|
84
100
|
elsif response.status == 101
|
85
101
|
# This code path is to support legacy behavior where the response status is set to 101, but the protocol is not upgraded. This may not be a valid use case, but it is supported for compatibility. We expect the response headers to contain the `upgrade` header.
|
86
102
|
write_response(@version, response.status, response.headers)
|
87
103
|
|
88
|
-
stream = write_tunnel_body(version)
|
89
|
-
|
90
104
|
# Same as above:
|
91
105
|
request = nil
|
92
106
|
response = nil
|
93
107
|
|
94
|
-
|
95
|
-
|
108
|
+
if body.stream?
|
109
|
+
return body.call(write_tunnel_body(version))
|
110
|
+
else
|
111
|
+
write_tunnel_body(version, body)
|
112
|
+
end
|
96
113
|
else
|
97
114
|
write_response(@version, response.status, response.headers)
|
98
115
|
|
99
116
|
if request.connect? and response.success?
|
100
|
-
stream = write_tunnel_body(version)
|
101
|
-
|
102
117
|
# Same as above:
|
103
118
|
request = nil
|
104
119
|
response = nil
|
105
120
|
|
106
|
-
|
107
|
-
|
121
|
+
if body.stream?
|
122
|
+
return body.call(write_tunnel_body(version))
|
123
|
+
else
|
124
|
+
write_tunnel_body(version, body)
|
125
|
+
end
|
108
126
|
else
|
109
127
|
head = request.head?
|
110
128
|
|
@@ -126,10 +144,12 @@ module Async
|
|
126
144
|
request&.finish
|
127
145
|
end
|
128
146
|
|
129
|
-
finishable
|
130
|
-
|
131
|
-
|
132
|
-
|
147
|
+
if finishable
|
148
|
+
finishable.wait(@persistent)
|
149
|
+
else
|
150
|
+
# Do not remove this line or you will unleash the gods of concurrency hell.
|
151
|
+
task.yield
|
152
|
+
end
|
133
153
|
rescue => error
|
134
154
|
raise
|
135
155
|
ensure
|
@@ -62,9 +62,9 @@ module Async
|
|
62
62
|
end
|
63
63
|
|
64
64
|
# TODO this might need to be in an ensure block:
|
65
|
-
if @input and frame.end_stream?
|
66
|
-
@input.close_write
|
65
|
+
if input = @input and frame.end_stream?
|
67
66
|
@input = nil
|
67
|
+
input.close_write
|
68
68
|
end
|
69
69
|
rescue ::Protocol::HTTP2::HeaderError => error
|
70
70
|
Console.logger.debug(self, error)
|
@@ -123,6 +123,8 @@ module Async
|
|
123
123
|
|
124
124
|
# Called when the output terminates normally.
|
125
125
|
def finish_output(error = nil)
|
126
|
+
return if self.closed?
|
127
|
+
|
126
128
|
trailer = @output&.trailer
|
127
129
|
|
128
130
|
@output = nil
|
@@ -152,14 +154,14 @@ module Async
|
|
152
154
|
def closed(error)
|
153
155
|
super
|
154
156
|
|
155
|
-
if @input
|
156
|
-
@input.close_write(error)
|
157
|
+
if input = @input
|
157
158
|
@input = nil
|
159
|
+
input.close_write(error)
|
158
160
|
end
|
159
161
|
|
160
|
-
if @output
|
161
|
-
@output.stop(error)
|
162
|
+
if output = @output
|
162
163
|
@output = nil
|
164
|
+
output.stop(error)
|
163
165
|
end
|
164
166
|
|
165
167
|
if pool = @pool and @connection
|
data/lib/async/http/version.rb
CHANGED
data.tar.gz.sig
CHANGED
Binary file
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: async-http
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.79.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Samuel Williams
|
@@ -58,7 +58,7 @@ cert_chain:
|
|
58
58
|
Q2K9NVun/S785AP05vKkXZEFYxqG6EW012U4oLcFl5MySFajYXRYbuUpH6AY+HP8
|
59
59
|
voD0MPg1DssDLKwXyt1eKD/+Fq0bFWhwVM/1XiAXL7lyYUyOq24KHgQ2Csg=
|
60
60
|
-----END CERTIFICATE-----
|
61
|
-
date: 2024-09-
|
61
|
+
date: 2024-09-24 00:00:00.000000000 Z
|
62
62
|
dependencies:
|
63
63
|
- !ruby/object:Gem::Dependency
|
64
64
|
name: async
|
@@ -136,28 +136,28 @@ dependencies:
|
|
136
136
|
requirements:
|
137
137
|
- - "~>"
|
138
138
|
- !ruby/object:Gem::Version
|
139
|
-
version: '0.
|
139
|
+
version: '0.27'
|
140
140
|
type: :runtime
|
141
141
|
prerelease: false
|
142
142
|
version_requirements: !ruby/object:Gem::Requirement
|
143
143
|
requirements:
|
144
144
|
- - "~>"
|
145
145
|
- !ruby/object:Gem::Version
|
146
|
-
version: '0.
|
146
|
+
version: '0.27'
|
147
147
|
- !ruby/object:Gem::Dependency
|
148
148
|
name: protocol-http2
|
149
149
|
requirement: !ruby/object:Gem::Requirement
|
150
150
|
requirements:
|
151
151
|
- - "~>"
|
152
152
|
- !ruby/object:Gem::Version
|
153
|
-
version: '0.
|
153
|
+
version: '0.19'
|
154
154
|
type: :runtime
|
155
155
|
prerelease: false
|
156
156
|
version_requirements: !ruby/object:Gem::Requirement
|
157
157
|
requirements:
|
158
158
|
- - "~>"
|
159
159
|
- !ruby/object:Gem::Version
|
160
|
-
version: '0.
|
160
|
+
version: '0.19'
|
161
161
|
- !ruby/object:Gem::Dependency
|
162
162
|
name: traces
|
163
163
|
requirement: !ruby/object:Gem::Requirement
|
@@ -182,7 +182,6 @@ files:
|
|
182
182
|
- bake/async/http/h2spec.rb
|
183
183
|
- lib/async/http.rb
|
184
184
|
- lib/async/http/body.rb
|
185
|
-
- lib/async/http/body/finishable.rb
|
186
185
|
- lib/async/http/body/hijack.rb
|
187
186
|
- lib/async/http/body/pipe.rb
|
188
187
|
- lib/async/http/body/writable.rb
|
@@ -198,6 +197,7 @@ files:
|
|
198
197
|
- lib/async/http/protocol/http1.rb
|
199
198
|
- lib/async/http/protocol/http1/client.rb
|
200
199
|
- lib/async/http/protocol/http1/connection.rb
|
200
|
+
- lib/async/http/protocol/http1/finishable.rb
|
201
201
|
- lib/async/http/protocol/http1/request.rb
|
202
202
|
- lib/async/http/protocol/http1/response.rb
|
203
203
|
- lib/async/http/protocol/http1/server.rb
|
metadata.gz.sig
CHANGED
Binary file
|
@@ -1,56 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# Released under the MIT License.
|
4
|
-
# Copyright, 2024, by Samuel Williams.
|
5
|
-
|
6
|
-
require "protocol/http/body/wrapper"
|
7
|
-
require "async/variable"
|
8
|
-
|
9
|
-
module Async
|
10
|
-
module HTTP
|
11
|
-
module Body
|
12
|
-
# Keeps track of whether a body is being read, and if so, waits for it to be closed.
|
13
|
-
class Finishable < ::Protocol::HTTP::Body::Wrapper
|
14
|
-
def initialize(body)
|
15
|
-
super(body)
|
16
|
-
|
17
|
-
@closed = Async::Variable.new
|
18
|
-
@error = nil
|
19
|
-
|
20
|
-
@reading = false
|
21
|
-
end
|
22
|
-
|
23
|
-
def reading?
|
24
|
-
@reading
|
25
|
-
end
|
26
|
-
|
27
|
-
def read
|
28
|
-
@reading = true
|
29
|
-
|
30
|
-
super
|
31
|
-
end
|
32
|
-
|
33
|
-
def close(error = nil)
|
34
|
-
unless @closed.resolved?
|
35
|
-
@error = error
|
36
|
-
@closed.value = true
|
37
|
-
end
|
38
|
-
|
39
|
-
super
|
40
|
-
end
|
41
|
-
|
42
|
-
def wait
|
43
|
-
if @reading
|
44
|
-
@closed.wait
|
45
|
-
else
|
46
|
-
self.discard
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
def inspect
|
51
|
-
"#<#{self.class} closed=#{@closed} error=#{@error}> | #{super}"
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|