diff --git a/lib/mint/http2.ex b/lib/mint/http2.ex index b3aaea74..670a16a1 100644 --- a/lib/mint/http2.ex +++ b/lib/mint/http2.ex @@ -2340,12 +2340,13 @@ defmodule Mint.HTTP2 do # RFC 9113 8.1.1: a response with content is malformed if the sum of the DATA # frame payload lengths doesn't equal the content-length header value. Responses # to HEAD and 204 and 304 responses must not have content, whatever their - # content-length header says, and 2xx responses to CONNECT carry tunnel data. + # content-length header says. Any 2xx response to CONNECT, including 204, + # establishes a tunnel (RFC 9110 9.3.6, RFC 9113 8.5) and carries tunnel data. defp response_content_length(%{method: method}, status, headers) do cond do method == "HEAD" -> {:ok, 0} - status in [204, 304] -> {:ok, 0} method == "CONNECT" and status in 200..299 -> {:ok, nil} + status in [204, 304] -> {:ok, 0} true -> content_length(headers) end end diff --git a/test/mint/http2/conn_test.exs b/test/mint/http2/conn_test.exs index 60ae7542..60495c24 100644 --- a/test/mint/http2/conn_test.exs +++ b/test/mint/http2/conn_test.exs @@ -4306,6 +4306,33 @@ defmodule Mint.HTTP2Test do assert HTTP2.open?(conn) end + test "a 204 response establishes the tunnel", %{conn: conn} do + assert {:ok, conn, ref} = HTTP2.request(conn, "CONNECT", "example.com:443", [], :stream) + + assert_recv_frames [headers(stream_id: stream_id)] + + hbf = server_encode_headers([{":status", "204"}]) + + assert {:ok, %HTTP2{} = conn, responses} = + stream_frames(conn, [ + headers( + stream_id: stream_id, + hbf: hbf, + flags: set_flags(:headers, [:end_headers]) + ), + data(stream_id: stream_id, data: "hello", flags: set_flags(:data, [:end_stream])) + ]) + + assert responses == [ + {:status, ref, 204}, + {:headers, ref, []}, + {:data, ref, "hello"}, + {:done, ref} + ] + + assert HTTP2.open?(conn) + end + test "an END_STREAM from the server closes the whole tunnel", %{conn: conn} do assert {:ok, conn, ref} = HTTP2.request(conn, "CONNECT", "example.com:443", [], :stream)