async-http 0.46.4 → 0.46.5

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