async-http 0.46.4 → 0.46.5
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/hijack.rb +9 -1
- data/lib/async/http/protocol/http1/client.rb +6 -2
- data/lib/async/http/protocol/http2/connection.rb +1 -1
- data/lib/async/http/protocol/http2/server.rb +2 -0
- data/lib/async/http/protocol/http2/stream.rb +73 -45
- data/lib/async/http/version.rb +1 -1
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b9fb9d8fd91de95c63e2a7fc133902457a812394c80bb37773ddd04a74592cda
|
4
|
+
data.tar.gz: 460f4a0505a7fe53ee91246728ed601464f86c85eb01db3bc1f3c1ee322b7834
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f356561f16a0829428ca4733cb9af6cde0f871ce2df662871e0c78b299d16d4a25be5f562719e752037f64dbaba3ab2328a1deb7579e66c06c4452a9269462aa
|
7
|
+
data.tar.gz: 914b845234e8c98eac84ee45dbc9f5e745bc07e61f23bbaf6017cab898b8455e70b978e31735a5b898cb22b7e4918fb3705d51e258ac5632c7a4f2f5e1cb8cc3
|
data/async-http.gemspec
CHANGED
@@ -19,7 +19,7 @@ Gem::Specification.new do |spec|
|
|
19
19
|
spec.add_dependency("async", "~> 1.19")
|
20
20
|
spec.add_dependency("async-io", "~> 1.24")
|
21
21
|
|
22
|
-
spec.add_dependency("protocol-http", "~> 0.
|
22
|
+
spec.add_dependency("protocol-http", "~> 0.11.0")
|
23
23
|
spec.add_dependency("protocol-http1", "~> 0.8.0")
|
24
24
|
spec.add_dependency("protocol-http2", "~> 0.9.0")
|
25
25
|
|
@@ -46,6 +46,8 @@ module Async
|
|
46
46
|
return @block.call(stream)
|
47
47
|
end
|
48
48
|
|
49
|
+
attr :input
|
50
|
+
|
49
51
|
# Has the producer called #finish and has the reader consumed the nil token?
|
50
52
|
def empty?
|
51
53
|
if @stream
|
@@ -60,7 +62,9 @@ module Async
|
|
60
62
|
unless @task
|
61
63
|
@stream = Stream.new(@input)
|
62
64
|
|
63
|
-
@task = Task.current.async do
|
65
|
+
@task = Task.current.async do |task|
|
66
|
+
task.annotate "Streaming hijacked body."
|
67
|
+
|
64
68
|
@block.call(@stream)
|
65
69
|
end
|
66
70
|
end
|
@@ -71,6 +75,10 @@ module Async
|
|
71
75
|
def inspect
|
72
76
|
"\#<#{self.class} #{@block.inspect}>"
|
73
77
|
end
|
78
|
+
|
79
|
+
def to_s
|
80
|
+
"<Hijack #{@block.class}>"
|
81
|
+
end
|
74
82
|
end
|
75
83
|
end
|
76
84
|
end
|
@@ -42,12 +42,16 @@ module Async
|
|
42
42
|
|
43
43
|
if protocol = request.protocol
|
44
44
|
# This is a very tricky apect of handling HTTP/1 upgrade connections. In theory, this approach is a bit inefficient, because we spin up a task just to handle writing to the underlying stream when we could be writing to the stream directly. But we need to maintain some level of compatibility with HTTP/2. Additionally, we don't know if the upgrade request will be accepted, so starting to write the body at this point needs to be handled with care.
|
45
|
-
task.async do
|
45
|
+
task.async do |subtask|
|
46
|
+
subtask.annotate("Upgrading request.")
|
47
|
+
|
46
48
|
# If this fails, this connection will be closed.
|
47
49
|
write_upgrade_body(protocol, body)
|
48
50
|
end
|
49
51
|
else
|
50
|
-
task.async do
|
52
|
+
task.async do |subtask|
|
53
|
+
subtask.annotate("Streaming body.")
|
54
|
+
|
51
55
|
# 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.
|
52
56
|
write_body(@version, body)
|
53
57
|
end
|
@@ -84,7 +84,7 @@ module Async
|
|
84
84
|
|
85
85
|
def read_in_background(task: Task.current)
|
86
86
|
task.async do |nested_task|
|
87
|
-
nested_task.annotate("#{version} reading data for #{self.class}")
|
87
|
+
nested_task.annotate("#{version} reading data for #{self.class}.")
|
88
88
|
|
89
89
|
begin
|
90
90
|
while !self.closed?
|
@@ -44,20 +44,38 @@ module Async
|
|
44
44
|
end
|
45
45
|
|
46
46
|
class Output
|
47
|
-
def
|
48
|
-
|
47
|
+
def self.for(stream, body)
|
48
|
+
output = self.new(stream, body)
|
49
|
+
|
50
|
+
output.start
|
49
51
|
|
52
|
+
return output
|
53
|
+
end
|
54
|
+
|
55
|
+
def initialize(stream, body)
|
56
|
+
@stream = stream
|
50
57
|
@body = body
|
51
|
-
@remainder = nil
|
52
58
|
|
53
59
|
@window_updated = Async::Condition.new
|
60
|
+
end
|
61
|
+
|
62
|
+
def start(parent: Task.current)
|
63
|
+
if @body.respond_to?(:call)
|
64
|
+
@task = parent.async(&self.method(:stream))
|
65
|
+
else
|
66
|
+
@task = parent.async(&self.method(:passthrough))
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def stop(error)
|
71
|
+
# Ensure that invoking #close doesn't try to close the stream.
|
72
|
+
@stream = nil
|
54
73
|
|
55
|
-
@task
|
74
|
+
@task&.stop
|
56
75
|
end
|
57
76
|
|
58
|
-
|
59
|
-
|
60
|
-
while chunk = self.read
|
77
|
+
def write(chunk)
|
78
|
+
until chunk.empty?
|
61
79
|
maximum_size = @stream.available_frame_size
|
62
80
|
|
63
81
|
while maximum_size <= 0
|
@@ -66,35 +84,60 @@ module Async
|
|
66
84
|
maximum_size = @stream.available_frame_size
|
67
85
|
end
|
68
86
|
|
69
|
-
|
87
|
+
break unless chunk = send_data(chunk, maximum_size)
|
70
88
|
end
|
71
|
-
|
72
|
-
self.end_stream
|
73
|
-
rescue Async::Stop
|
74
|
-
# Ignore.
|
75
|
-
ensure
|
76
|
-
@body&.close($!)
|
77
|
-
@body = nil
|
78
89
|
end
|
79
90
|
|
80
|
-
def
|
81
|
-
|
82
|
-
|
83
|
-
|
91
|
+
def window_updated(size)
|
92
|
+
@window_updated.signal
|
93
|
+
end
|
94
|
+
|
95
|
+
def close(error = nil)
|
96
|
+
if @stream
|
97
|
+
if error
|
98
|
+
@stream.close(error)
|
99
|
+
else
|
100
|
+
self.close_write
|
101
|
+
end
|
84
102
|
|
85
|
-
|
86
|
-
else
|
87
|
-
@body&.read
|
103
|
+
@stream = nil
|
88
104
|
end
|
89
105
|
end
|
90
106
|
|
91
|
-
def
|
92
|
-
@
|
107
|
+
def close_write
|
108
|
+
@stream.send_data(nil, ::Protocol::HTTP2::END_STREAM)
|
109
|
+
end
|
110
|
+
|
111
|
+
private
|
112
|
+
|
113
|
+
def stream(task)
|
114
|
+
task.annotate("Streaming #{@body} to #{@stream}.")
|
115
|
+
|
116
|
+
@body.call(Body::Stream.new(@stream.input, self))
|
117
|
+
rescue Async::Stop
|
118
|
+
# Ignore.
|
119
|
+
end
|
120
|
+
|
121
|
+
# Reads chunks from the given body and writes them to the stream as fast as possible.
|
122
|
+
def passthrough(task)
|
123
|
+
task.annotate("Writing #{@body} to #{@stream}.")
|
124
|
+
|
125
|
+
while chunk = @body&.read
|
126
|
+
self.write(chunk)
|
127
|
+
end
|
128
|
+
|
129
|
+
self.close_write
|
130
|
+
rescue Async::Stop
|
131
|
+
# Ignore.
|
132
|
+
ensure
|
133
|
+
@body&.close($!)
|
134
|
+
@body = nil
|
93
135
|
end
|
94
136
|
|
95
137
|
# Send `maximum_size` bytes of data using the specified `stream`. If the buffer has no more chunks, `END_STREAM` will be sent on the final chunk.
|
96
138
|
# @param maximum_size [Integer] send up to this many bytes of data.
|
97
139
|
# @param stream [Stream] the stream to use for sending data frames.
|
140
|
+
# @return [String, nil] any data that could not be written.
|
98
141
|
def send_data(chunk, maximum_size)
|
99
142
|
if chunk.bytesize <= maximum_size
|
100
143
|
@stream.send_data(chunk, maximum_size: maximum_size)
|
@@ -102,27 +145,10 @@ module Async
|
|
102
145
|
@stream.send_data(chunk.byteslice(0, maximum_size), maximum_size: maximum_size)
|
103
146
|
|
104
147
|
# The window was not big enough to send all the data, so we save it for next time:
|
105
|
-
|
106
|
-
chunk.byteslice(maximum_size, chunk.bytesize - maximum_size)
|
107
|
-
)
|
108
|
-
end
|
109
|
-
end
|
110
|
-
|
111
|
-
def end_stream
|
112
|
-
@stream.send_data(nil, ::Protocol::HTTP2::END_STREAM)
|
113
|
-
end
|
114
|
-
|
115
|
-
def window_updated(size)
|
116
|
-
@window_updated.signal
|
117
|
-
end
|
118
|
-
|
119
|
-
def close(error)
|
120
|
-
if @body
|
121
|
-
@body.close(error)
|
122
|
-
@body = nil
|
148
|
+
return chunk.byteslice(maximum_size, chunk.bytesize - maximum_size)
|
123
149
|
end
|
124
150
|
|
125
|
-
|
151
|
+
return nil
|
126
152
|
end
|
127
153
|
end
|
128
154
|
|
@@ -142,6 +168,8 @@ module Async
|
|
142
168
|
|
143
169
|
attr_accessor :headers
|
144
170
|
|
171
|
+
attr :input
|
172
|
+
|
145
173
|
def add_header(key, value)
|
146
174
|
if key == CONNECTION
|
147
175
|
raise ::Protocol::HTTP2::HeaderError, "Connection header is not allowed!"
|
@@ -222,7 +250,7 @@ module Async
|
|
222
250
|
|
223
251
|
# Set the body and begin sending it.
|
224
252
|
def send_body(body)
|
225
|
-
@output = Output.
|
253
|
+
@output = Output.for(self, body)
|
226
254
|
end
|
227
255
|
|
228
256
|
def window_updated(size)
|
@@ -240,7 +268,7 @@ module Async
|
|
240
268
|
end
|
241
269
|
|
242
270
|
if @output
|
243
|
-
@output.
|
271
|
+
@output.stop(error)
|
244
272
|
@output = nil
|
245
273
|
end
|
246
274
|
end
|
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.46.
|
4
|
+
version: 0.46.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Samuel Williams
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-07-
|
11
|
+
date: 2019-07-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: async
|
@@ -44,14 +44,14 @@ dependencies:
|
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: 0.
|
47
|
+
version: 0.11.0
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: 0.
|
54
|
+
version: 0.11.0
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: protocol-http1
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|