Skip to content

Commit 30f502c

Browse files
committed
Better handling of state transitions.
1 parent b5292d6 commit 30f502c

File tree

4 files changed

+78
-62
lines changed

4 files changed

+78
-62
lines changed

lib/async/http/body/finishable.rb

-56
This file was deleted.

lib/async/http/client.rb

-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
require "traces/provider"
1515

1616
require_relative "protocol"
17-
require_relative "body/finishable"
1817

1918
module Async
2019
module HTTP
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
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+
unless @closed.resolved?
36+
@error = error
37+
@closed.value = true
38+
end
39+
40+
super
41+
end
42+
43+
def wait
44+
if @reading
45+
@closed.wait
46+
else
47+
self.discard
48+
end
49+
end
50+
51+
def inspect
52+
"#<#{self.class} closed=#{@closed} error=#{@error}> | #{super}"
53+
end
54+
end
55+
end
56+
end
57+
end
58+
end

lib/async/http/protocol/http1/server.rb

+20-5
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
# Copyright, 2024, by Anton Zhuravsky.
88

99
require_relative "connection"
10-
require_relative "../../body/finishable"
10+
require_relative "finishable"
1111

1212
require "console/event/failure"
1313

@@ -16,6 +16,18 @@ module HTTP
1616
module Protocol
1717
module HTTP1
1818
class Server < Connection
19+
def initialize(...)
20+
super
21+
22+
@ready = Async::Notification.new
23+
end
24+
25+
def closed!
26+
super
27+
28+
@ready.signal
29+
end
30+
1931
def fail_request(status)
2032
@persistent = false
2133
write_response(@version, status, {})
@@ -26,6 +38,11 @@ def fail_request(status)
2638
end
2739

2840
def next_request
41+
# Wait for the connection to become idle before reading the next request:
42+
unless idle?
43+
@ready.wait
44+
end
45+
2946
# The default is true.
3047
return unless @persistent
3148

@@ -49,7 +66,7 @@ def each(task: Task.current)
4966

5067
while request = next_request
5168
if body = request.body
52-
finishable = Body::Finishable.new(body)
69+
finishable = Finishable.new(body)
5370
request.body = finishable
5471
end
5572

@@ -126,10 +143,8 @@ def each(task: Task.current)
126143
request&.finish
127144
end
128145

146+
# Discard or wait for the input body to be consumed:
129147
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
133148
rescue => error
134149
raise
135150
ensure

0 commit comments

Comments
 (0)