async-http 0.52.5 → 0.55.0

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: 5f3f0a32ca906a1af82161acfe4d4256748c1d2e7a8792e9973abf7f203df7d9
4
- data.tar.gz: 0f2e158de8e076bb7af74b3ac268e1a0260169ae2fee56925b2fd244f668f904
3
+ metadata.gz: 2c6ded2cff5f70f5f967912da2a390e878ef43ba759e9044b4131ea433a55d7b
4
+ data.tar.gz: eae09a23793d39af77a72d4cefddf7a9d5b353c887ab3acdb654c2ab7911c661
5
5
  SHA512:
6
- metadata.gz: 53a44e49b1502fb8cb2e6d2234b7ea94831b4b6f391f9ee48c842aeea0a80f517143894fb10c98499fdbb2f1bb23994ad26d82735c9912f4333975acfdc6b1af
7
- data.tar.gz: 53052f74772e143a831df936aad2f4687b045ae1d2ccf7bcf598d6e8b105ec3c5e47ffb20bc72ecc7fe07b6701d8f3155ba717aa6cebd065caf0506cb5ed1c70
6
+ metadata.gz: 43a53999b1931ace79bef33c196983aceef0f407dcfdadce076a9faf3b91de9b66bed3b54b87033d90d0c95d59b374b81891ccfac0684489e0cc17d0fc0b8e01
7
+ data.tar.gz: e2d7f407498eb891cc24a3fc147f031b6f46e2017e92da11c8c3f3496de2612277f63c60a80a800bfe2f8310b82d75b5073735b9b7901c037a1eafdeb171fe3d
@@ -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
 
@@ -32,18 +32,23 @@ module Async
32
32
  ::Protocol::HTTP::Response[status, headers, self.wrap(request, &block)]
33
33
  end
34
34
 
35
- def self.wrap(request, &block)
36
- self.new(request.body, &block)
35
+ def self.wrap(request = nil, &block)
36
+ self.new(block, request&.body)
37
37
  end
38
38
 
39
- def initialize(input = nil, &block)
40
- @input = input
39
+ def initialize(block, input = nil)
41
40
  @block = block
41
+ @input = input
42
42
 
43
43
  @task = nil
44
44
  @stream = nil
45
45
  end
46
46
 
47
+ # We prefer streaming directly as it's the lowest overhead.
48
+ def stream?
49
+ true
50
+ end
51
+
47
52
  def call(stream)
48
53
  return @block.call(stream)
49
54
  end
@@ -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
- @head.close if @writer.nil?
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
- @head.close if @reader.nil?
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
@@ -139,7 +139,7 @@ module Async
139
139
  end
140
140
 
141
141
  # Close the input and output bodies.
142
- def close
142
+ def close(error = nil)
143
143
  self.close_read
144
144
  self.close_write
145
145
  ensure
@@ -25,7 +25,7 @@ require 'async/io/stream'
25
25
 
26
26
  require 'async/pool/controller'
27
27
 
28
- require 'protocol/http/body/streamable'
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 = endpoint.protocol, scheme = endpoint.scheme, authority = endpoint.authority, retries: DEFAULT_RETRIES, connection_limit: DEFAULT_CONNECTION_LIMIT)
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::Streamable.wrap(response) do
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: false)
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: false)
47
+ stream = IO::Stream.new(peer, sync: true)
48
48
 
49
49
  return HTTP1::Server.new(stream, VERSION)
50
50
  end
@@ -74,11 +74,6 @@ module Async
74
74
  def reusable?
75
75
  @persistent && @stream && !@stream.closed?
76
76
  end
77
-
78
- def close
79
- Async.logger.debug(self) {"Closing connection"}
80
- super
81
- end
82
77
  end
83
78
  end
84
79
  end
@@ -80,7 +80,7 @@ module Async
80
80
  response = nil
81
81
 
82
82
  body.call(stream)
83
- elsif body and request.connect?
83
+ elsif request.connect? and response.success?
84
84
  stream = write_tunnel_body(request.version)
85
85
 
86
86
  # Same as above:
@@ -37,13 +37,13 @@ module Async
37
37
  end
38
38
 
39
39
  def self.client(peer)
40
- stream = IO::Stream.new(peer, sync: false)
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: false)
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: false)
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: false)
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
@@ -42,7 +42,7 @@ module Async
42
42
  def start(parent: Task.current)
43
43
  raise "Task already started!" if @task
44
44
 
45
- if @body.respond_to?(:call)
45
+ if @body.stream?
46
46
  @task = parent.async(&self.method(:stream))
47
47
  else
48
48
  @task = parent.async(&self.method(:passthrough))
@@ -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
- stream = @connection.accept_push_promise_stream(promised_stream_id, &Stream.method(:create))
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
 
@@ -42,10 +42,6 @@ module Async
42
42
  false
43
43
  end
44
44
 
45
- def push?
46
- false
47
- end
48
-
49
45
  def peer
50
46
  if connection = self.connection
51
47
  connection.peer
@@ -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
- pipe = Body::Pipe.new(response.body, input)
96
-
97
- return pipe.to_io unless block_given?
98
-
99
- begin
100
- yield pipe.to_io
101
- ensure
102
- pipe.close
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
 
@@ -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 = endpoint.protocol, scheme = endpoint.scheme)
36
+ def initialize(app, endpoint, protocol: endpoint.protocol, scheme: endpoint.scheme)
37
37
  super(app)
38
38
 
39
39
  @endpoint = endpoint
@@ -22,6 +22,6 @@
22
22
 
23
23
  module Async
24
24
  module HTTP
25
- VERSION = "0.52.5"
25
+ VERSION = "0.55.0"
26
26
  end
27
27
  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.52.5
4
+ version: 0.55.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-09-19 00:00:00.000000000 Z
11
+ date: 2021-04-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: async
@@ -58,14 +58,14 @@ dependencies:
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: 0.20.0
61
+ version: 0.21.0
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: 0.20.0
68
+ version: 0.21.0
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: protocol-http1
71
71
  requirement: !ruby/object:Gem::Requirement
@@ -164,6 +164,20 @@ dependencies:
164
164
  - - "~>"
165
165
  - !ruby/object:Gem::Version
166
166
  version: '3.6'
167
+ - !ruby/object:Gem::Dependency
168
+ name: localhost
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - ">="
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ type: :development
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - ">="
179
+ - !ruby/object:Gem::Version
180
+ version: '0'
167
181
  description:
168
182
  email:
169
183
  executables: []
@@ -229,7 +243,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
229
243
  - !ruby/object:Gem::Version
230
244
  version: '0'
231
245
  requirements: []
232
- rubygems_version: 3.0.3
246
+ rubygems_version: 3.2.3
233
247
  signing_key:
234
248
  specification_version: 4
235
249
  summary: A HTTP client and server library.