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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0a113b8685408552c9b24fd5abbb0d9ba844c91105b1c6e1f96e4a516c8223ac
4
- data.tar.gz: bbf73351618744ce86a1dc7bf1ea884c8221dd483b13ad111d9a5e79ab5cf8a9
3
+ metadata.gz: b9fb9d8fd91de95c63e2a7fc133902457a812394c80bb37773ddd04a74592cda
4
+ data.tar.gz: 460f4a0505a7fe53ee91246728ed601464f86c85eb01db3bc1f3c1ee322b7834
5
5
  SHA512:
6
- metadata.gz: a508faf1a1431687767c3256c9b740e7c7baa5b009bb7cfd7c887c79a324f06d6bfce4ce2809af82419a42d38f8ed4a5b840f2363620bb276d65885ac85f37da
7
- data.tar.gz: 696a1dcabdbb82549f5efa24ab2795f3086f677ef325ae7f5c25c59f14e41888baaccc8d62a1b5b8fef6a9e08441744b89828041a1ab0eeffb2043d875b472cf
6
+ metadata.gz: f356561f16a0829428ca4733cb9af6cde0f871ce2df662871e0c78b299d16d4a25be5f562719e752037f64dbaba3ab2328a1deb7579e66c06c4452a9269462aa
7
+ data.tar.gz: 914b845234e8c98eac84ee45dbc9f5e745bc07e61f23bbaf6017cab898b8455e70b978e31735a5b898cb22b7e4918fb3705d51e258ac5632c7a4f2f5e1cb8cc3
@@ -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.10.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?
@@ -60,6 +60,8 @@ module Async
60
60
  def each
61
61
  # It's possible the connection has died before we get here...
62
62
  @requests&.async do |task, request|
63
+ task.annotate("Incoming request: #{request.method} #{request.path.inspect}.")
64
+
63
65
  @count += 1
64
66
 
65
67
  begin
@@ -44,20 +44,38 @@ module Async
44
44
  end
45
45
 
46
46
  class Output
47
- def initialize(stream, body, task: Task.current)
48
- @stream = stream
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 = task.async(&self.method(:passthrough))
74
+ @task&.stop
56
75
  end
57
76
 
58
- # Reads chunks from the given body and writes them to the stream as fast as possible.
59
- def passthrough(task)
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
- self.send_data(chunk, maximum_size)
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 read
81
- if @remainder
82
- remainder = @remainder
83
- @remainder = nil
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
- return remainder
86
- else
87
- @body&.read
103
+ @stream = nil
88
104
  end
89
105
  end
90
106
 
91
- def push(chunk)
92
- @remainder = chunk
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
- self.push(
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
- @task&.stop
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.new(self, body)
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.close(error)
271
+ @output.stop(error)
244
272
  @output = nil
245
273
  end
246
274
  end
@@ -20,6 +20,6 @@
20
20
 
21
21
  module Async
22
22
  module HTTP
23
- VERSION = "0.46.4"
23
+ VERSION = "0.46.5"
24
24
  end
25
25
  end
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
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-13 00:00:00.000000000 Z
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.10.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.10.0
54
+ version: 0.11.0
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: protocol-http1
57
57
  requirement: !ruby/object:Gem::Requirement