From 6f01e3ff115c659f8c8e2a06c634dc11085e2178 Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Sat, 18 Apr 2020 22:36:14 +1200 Subject: [PATCH 1/6] Add specs for server-side timeout. --- lib/async/http/protocol/http1/server.rb | 4 +++- spec/async/http/protocol/shared_examples.rb | 15 +++++++++++++++ spec/async/http/server_context.rb | 2 +- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/lib/async/http/protocol/http1/server.rb b/lib/async/http/protocol/http1/server.rb index 902d4cbd..bbdf4922 100644 --- a/lib/async/http/protocol/http1/server.rb +++ b/lib/async/http/protocol/http1/server.rb @@ -45,7 +45,9 @@ def next_request return request rescue Async::TimeoutError - fail_request(408) + # For an interesting discussion about this behaviour, see https://trac.nginx.org/nginx/ticket/1005 + # If you enable this, you will see some spec failures... + # fail_request(408) raise rescue fail_request(400) diff --git a/spec/async/http/protocol/shared_examples.rb b/spec/async/http/protocol/shared_examples.rb index 8db26a0a..7e496647 100644 --- a/spec/async/http/protocol/shared_examples.rb +++ b/spec/async/http/protocol/shared_examples.rb @@ -167,6 +167,21 @@ expect(server.scheme).to be == "http" end + it "disconnects slow clients" do + response = client.get("/") + response.read + + # We expect this connection to be closed: + connection = response.connection + + reactor.sleep(1.0) + + response = client.get("/") + response.read + + expect(connection).to_not be_reusable + end + context 'using GET method' do let(:expected) {"GET #{protocol::VERSION}"} diff --git a/spec/async/http/server_context.rb b/spec/async/http/server_context.rb index 12e9e5d9..80251bd4 100644 --- a/spec/async/http/server_context.rb +++ b/spec/async/http/server_context.rb @@ -26,7 +26,7 @@ include_context Async::RSpec::Reactor let(:protocol) {described_class} - let(:endpoint) {Async::HTTP::Endpoint.parse('http://127.0.0.1:9294', reuse_port: true)} + let(:endpoint) {Async::HTTP::Endpoint.parse('http://127.0.0.1:9294', timeout: 0.8, reuse_port: true)} let(:retries) {1} let!(:client) {Async::HTTP::Client.new(endpoint, protocol, retries: retries)} From 5b6ed31ad7a2bedc63c75c93cd8cb7fe0638c4fe Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Sat, 18 Apr 2020 22:37:02 +1200 Subject: [PATCH 2/6] Patch version bump. --- lib/async/http/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/async/http/version.rb b/lib/async/http/version.rb index 3d208fb8..a20a9b1a 100644 --- a/lib/async/http/version.rb +++ b/lib/async/http/version.rb @@ -22,6 +22,6 @@ module Async module HTTP - VERSION = "0.51.2" + VERSION = "0.51.3" end end From db14af66a0c5aba6c92307586adfabfe4461f510 Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Sun, 19 Apr 2020 13:02:52 +1200 Subject: [PATCH 3/6] Annotate server loop. --- lib/async/http/protocol/http1/server.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/async/http/protocol/http1/server.rb b/lib/async/http/protocol/http1/server.rb index bbdf4922..bd16ffdf 100644 --- a/lib/async/http/protocol/http1/server.rb +++ b/lib/async/http/protocol/http1/server.rb @@ -56,6 +56,8 @@ def next_request # Server loop. def each(task: Task.current) + task.annotate("#{version} reading requests for #{self.class}.") + while request = next_request response = yield(request, self) From 67bfb751d9e0257b1518cc1e53e84739a6a14bf0 Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Sun, 19 Apr 2020 13:26:26 +1200 Subject: [PATCH 4/6] Patch version bump. --- lib/async/http/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/async/http/version.rb b/lib/async/http/version.rb index a20a9b1a..15080383 100644 --- a/lib/async/http/version.rb +++ b/lib/async/http/version.rb @@ -22,6 +22,6 @@ module Async module HTTP - VERSION = "0.51.3" + VERSION = "0.51.4" end end From 06db9a443c75728516ac1a50288ff9dbd5e213a7 Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Sun, 19 Apr 2020 21:18:34 +1200 Subject: [PATCH 5/6] Try to improve robustness of HTTP/2 request queue. --- lib/async/http/protocol/http2/server.rb | 13 ++++++++----- spec/async/http/protocol/shared_examples.rb | 4 ++++ 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/lib/async/http/protocol/http2/server.rb b/lib/async/http/protocol/http2/server.rb index f3b8ea7c..4caa877e 100644 --- a/lib/async/http/protocol/http2/server.rb +++ b/lib/async/http/protocol/http2/server.rb @@ -52,17 +52,18 @@ def accept_stream(stream_id) end def close(error = nil) - # This invokes Framer#close which closes the stream: - super - if @requests # Stop the request loop: - @requests.enqueue nil + @requests.enqueue(nil) @requests = nil end + + super end - def each + def each(task: Task.current) + task.annotate("#{version} reading requests for #{self.class}.") + # It's possible the connection has died before we get here... @requests&.async do |task, request| task.annotate("Incoming request: #{request.method} #{request.path.inspect}.") @@ -80,6 +81,8 @@ def each request.send_response(response) end end + + # Maybe we should add some synchronisation here - i.e. only exit once all requests are finished. end end end diff --git a/spec/async/http/protocol/shared_examples.rb b/spec/async/http/protocol/shared_examples.rb index 7e496647..6c7326dd 100644 --- a/spec/async/http/protocol/shared_examples.rb +++ b/spec/async/http/protocol/shared_examples.rb @@ -180,6 +180,10 @@ response.read expect(connection).to_not be_reusable + + # client.close + # reactor.sleep(0.1) + # reactor.print_hierarchy end context 'using GET method' do From fa048923ecddd0de10062040f491c65c79134cb7 Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Sun, 19 Apr 2020 22:28:36 +1200 Subject: [PATCH 6/6] Patch version bump. --- lib/async/http/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/async/http/version.rb b/lib/async/http/version.rb index 15080383..e500692c 100644 --- a/lib/async/http/version.rb +++ b/lib/async/http/version.rb @@ -22,6 +22,6 @@ module Async module HTTP - VERSION = "0.51.4" + VERSION = "0.51.5" end end