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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5a6e20407a7969b8eebeaf3f3a2dac09bdb3fe48130454ae49f46251a03b5c98
4
- data.tar.gz: a827ae19dc5c7f60ed338357091f556d1e48b183fe8ab0c119d75547fce4a6cd
3
+ metadata.gz: 93d3d9d22a84537ba522b417586a7bc02d243d56defe26fb9a3937470675b244
4
+ data.tar.gz: 8c16add5b2c301948235c739d076f92c400cdd745cc36881fac49125a41c5bc4
5
5
  SHA512:
6
- metadata.gz: 116770838b19e96fdc9ed4e1db8af834f0ed8676542d1090ed88c90d02ac0b978bd94378b41edc151108d86e61c39d2fbb9680d217790e1b1cb3f2ceed2aa64e
7
- data.tar.gz: 2b05b93835b83052a89127f12db03fdcd79007b6b0f396134d9818352e48ffaf012057c0b818a56633f3e8b510e7861438e8016d2519bd4d0dec425dfd1113f7
6
+ metadata.gz: cf362c7394967d2746e6d37984f25f5fc02e8e1fa89b7d5f6afcf31d87e665d75877cd5a9cd920f1ae87b6ecbc73e7d9b7cedc72467a69d95b5d24a04b897a8e
7
+ data.tar.gz: e922cf9cfd745423d9350f633d1a152dd8f6e06ae23abd3c8b1e403f49abb7089181aa68a1ded1a329cf2017b7bf98feb6a60fed45e1f8582eb3b01ca401ce4d
checksums.yaml.gz.sig CHANGED
Binary file
@@ -14,7 +14,6 @@ require "protocol/http/methods"
14
14
  require "traces/provider"
15
15
 
16
16
  require_relative "protocol"
17
- require_relative "body/finishable"
18
17
 
19
18
  module Async
20
19
  module HTTP
@@ -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
- response = Response.read(self, request)
71
-
72
- return response
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
@@ -43,6 +43,8 @@ module Async
43
43
 
44
44
  def read_line?
45
45
  @stream.read_until(CRLF)
46
+ rescue Errno::ECONNRESET
47
+ return nil
46
48
  end
47
49
 
48
50
  def read_line
@@ -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 "../../body/finishable"
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
- # The default is true.
30
- return unless @persistent
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 = Body::Finishable.new(body)
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
- # We must return here as no further request processing can be done:
83
- return body.call(stream)
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
- # We must return here as no further request processing can be done:
95
- return body&.call(stream)
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
- # We must return here as no further request processing can be done:
107
- return body.call(stream)
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&.wait
130
-
131
- # This ensures we yield at least once every iteration of the loop and allow other fibers to execute.
132
- task.yield
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
@@ -41,6 +41,10 @@ module Async
41
41
  def remote_address= value
42
42
  @remote_address = value
43
43
  end
44
+
45
+ def inspect
46
+ "#<#{self.class}:0x#{self.object_id.to_s(16)} method=#{method} path=#{path} version=#{version}>"
47
+ end
44
48
  end
45
49
  end
46
50
  end
@@ -33,6 +33,10 @@ module Async
33
33
  def remote_address= value
34
34
  @remote_address = value
35
35
  end
36
+
37
+ def inspect
38
+ "#<#{self.class}:0x#{self.object_id.to_s(16)} status=#{status}>"
39
+ end
36
40
  end
37
41
  end
38
42
  end
@@ -5,6 +5,6 @@
5
5
 
6
6
  module Async
7
7
  module HTTP
8
- VERSION = "0.77.0"
8
+ VERSION = "0.79.0"
9
9
  end
10
10
  end
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.77.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-19 00:00:00.000000000 Z
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.25'
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.25'
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.18'
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.18'
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