From aa6d0fd481d2db053260ee25cd8dc45cb33bd2d6 Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Thu, 30 Jul 2026 13:41:18 +1200 Subject: [PATCH 1/2] Map HTTP/2 body internal errors --- lib/async/http/protocol/http2/input.rb | 8 ++++++ .../connection_close_with_active_streams.rb | 25 +++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/lib/async/http/protocol/http2/input.rb b/lib/async/http/protocol/http2/input.rb index 7a50ba82..fb8fb3d8 100644 --- a/lib/async/http/protocol/http2/input.rb +++ b/lib/async/http/protocol/http2/input.rb @@ -4,6 +4,8 @@ # Copyright, 2020-2026, by Samuel Williams. require "protocol/http/body/writable" +require "protocol/http/error" +require "protocol/http2/error" module Async module HTTP @@ -41,6 +43,12 @@ def read end return chunk + rescue ::Protocol::HTTP2::StreamError => error + if error.code == ::Protocol::HTTP2::Error::INTERNAL_ERROR + raise ::Protocol::HTTP::RemoteError, error.message + end + + raise end # Close the application-facing input body and notify the stream that incoming data is no longer being consumed. While local output is active, the HTTP/2 stream remains open. Once output also closes, the remaining wire stream is terminated without an error. diff --git a/test/async/http/protocol/http2/connection_close_with_active_streams.rb b/test/async/http/protocol/http2/connection_close_with_active_streams.rb index ed8c1596..59cb75b7 100644 --- a/test/async/http/protocol/http2/connection_close_with_active_streams.rb +++ b/test/async/http/protocol/http2/connection_close_with_active_streams.rb @@ -83,6 +83,31 @@ end.wait end + it "maps a remote internal error while reading the response body" do + Async do |task| + client_connection = Async::HTTP::Protocol::HTTP2::Client.new(client_stream) + client_connection.open! + + response = client_connection.create_response + request = Protocol::HTTP::Request.new("https", "example.com", "GET", "/") + client_connection.write_request(response, request) + response.stream.receive_initial_headers([[":status", "200"]], false) + + client_connection.read_response(response) + response.stream.close!(Protocol::HTTP2::INTERNAL_ERROR) + + expect do + response.body.read + end.to raise_exception(Protocol::HTTP::RemoteError).and( + have_attributes( + cause: be_a(Protocol::HTTP2::StreamError).and( + have_attributes(code: be == Protocol::HTTP2::INTERNAL_ERROR) + ) + ) + ) + end.wait + end + it "does not raise error when connection closes without active streams" do Async do |task| # Create client connection From 451b353ba9d44c18eabda576c92cc627258c2876 Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Fri, 18 Sep 2026 21:57:46 +1200 Subject: [PATCH 2/2] Describe the body read failure in RemoteError --- lib/async/http/protocol/http2/input.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/async/http/protocol/http2/input.rb b/lib/async/http/protocol/http2/input.rb index fb8fb3d8..ede70d3f 100644 --- a/lib/async/http/protocol/http2/input.rb +++ b/lib/async/http/protocol/http2/input.rb @@ -45,7 +45,7 @@ def read return chunk rescue ::Protocol::HTTP2::StreamError => error if error.code == ::Protocol::HTTP2::Error::INTERNAL_ERROR - raise ::Protocol::HTTP::RemoteError, error.message + raise ::Protocol::HTTP::RemoteError, "Remote endpoint failure while reading the HTTP/2 message body!" end raise