Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion lib/mint/http1.ex
Original file line number Diff line number Diff line change
Expand Up @@ -873,6 +873,8 @@ defmodule Mint.HTTP1 do
# Informational (1xx) responses have no body and must not finalize the
# request; the final response follows on the same request ref. Reset the
# request's response-side fields and continue parsing without popping it.
# A "close" option is kept, so the connection is closed after the final
# response (RFC 9112 9.6).
defp decode_body(:informational, conn, data, _request_ref, responses) do
request = %{
conn.request
Expand All @@ -883,7 +885,7 @@ defmodule Mint.HTTP1 do
headers_size: 0,
data_buffer: [],
content_length: nil,
connection: [],
connection: if("close" in conn.request.connection, do: ["close"], else: []),
transfer_encoding: nil,
body: nil
}
Expand Down
72 changes: 72 additions & 0 deletions test/mint/http1/conn_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -1148,6 +1148,78 @@
)
end

for {name, final_response} <- [
{"a final", "HTTP/1.1 200 OK\r\ncontent-length: 0\r\n\r\n"},
{"a 101", "HTTP/1.1 101 Switching Protocols\r\nupgrade: foo\r\n\r\n"},
{"an HTTP/1.0 keep-alive", "HTTP/1.0 200 OK\r\nconnection: keep-alive\r\n\r\n"}
] do
test "connection: close in an informational response closes after #{name} response",
%{conn: conn} do
{:ok, conn, ref1} = HTTP1.request(conn, "HEAD", "/", [], nil)
{:ok, conn, ref2} = HTTP1.request(conn, "GET", "/", [], nil)

response =
"HTTP/1.1 103 Early Hints\r\nconnection: close\r\n\r\n" <> unquote(final_response)

assert {:ok, conn, responses} = HTTP1.stream(conn, {:tcp, conn.socket, response})

assert [
{:status, ^ref1, 103},
{:headers, ^ref1, [{"connection", "close"}]},
{:status, ^ref1, _},
{:headers, ^ref1, _},
{:done, ^ref1},
{:error, ^ref2, %HTTPError{reason: :unprocessed}}
] = responses

refute HTTP1.open?(conn)
end
end

test "connection: close in an informational response is ignored after a 2xx response to CONNECT",
%{conn: conn} do
{:ok, conn, ref} = HTTP1.request(conn, "CONNECT", "example.com:443", [], nil)

response =
"HTTP/1.1 103 Early Hints\r\nconnection: close\r\n\r\n" <>
"HTTP/1.1 200 Connection established\r\n\r\n"

assert {:ok, conn,
[
{:status, ^ref, 103},
{:headers, ^ref, _},
{:status, ^ref, 200},
{:headers, ^ref, []},
{:done, ^ref}
]} = HTTP1.stream(conn, {:tcp, conn.socket, response})

assert HTTP1.open?(conn)
end

test "connection: keep-alive in an informational response doesn't apply to the final response",
%{conn: conn} do
{:ok, conn, ref1} = HTTP1.request(conn, "GET", "/", [], nil)
{:ok, conn, ref2} = HTTP1.request(conn, "GET", "/", [], nil)

response =
"HTTP/1.1 103 Early Hints\r\nconnection: keep-alive\r\n\r\n" <>
"HTTP/1.0 200 OK\r\ncontent-length: 2\r\n\r\nok"

assert {:ok, conn, responses} = HTTP1.stream(conn, {:tcp, conn.socket, response})

assert [
{:status, ^ref1, 103},
{:headers, ^ref1, _},
{:status, ^ref1, 200},
{:headers, ^ref1, _},
{:data, ^ref1, "ok"},
{:done, ^ref1},
{:error, ^ref2, %TransportError{reason: :closed}}
] = responses

refute HTTP1.open?(conn)
end

test "body following a 101 switching-protocols", %{conn: conn} do
{:ok, conn, ref} = HTTP1.request(conn, "GET", "/socket/websocket", [], nil)

Expand Down Expand Up @@ -2036,7 +2108,7 @@
\
""")

{:ok, conn} = HTTP1.stream_request_body(conn, ref, "hello")

Check warning on line 2111 in test/mint/http1/conn_test.exs

View workflow job for this annotation

GitHub Actions / Test (Elixir 1.15.8, OTP 24.3)

variable "conn" is unused (there is a variable with the same name in the context, use the pin operator (^) to match on it or prefix this variable with underscore if it is not meant to be used)
assert receive_request_string(server_socket) == "5\r\nhello\r\n"
end

Expand All @@ -2055,7 +2127,7 @@
\
""")

{:ok, conn} = HTTP1.stream_request_body(conn, ref, "hello")

Check warning on line 2130 in test/mint/http1/conn_test.exs

View workflow job for this annotation

GitHub Actions / Test (Elixir 1.15.8, OTP 24.3)

variable "conn" is unused (there is a variable with the same name in the context, use the pin operator (^) to match on it or prefix this variable with underscore if it is not meant to be used)
assert receive_request_string(server_socket) == "5\r\nhello\r\n"
end

Expand All @@ -2075,7 +2147,7 @@
\
""")

{:ok, conn} = HTTP1.stream_request_body(conn, ref, "hello")

Check warning on line 2150 in test/mint/http1/conn_test.exs

View workflow job for this annotation

GitHub Actions / Test (Elixir 1.15.8, OTP 24.3)

variable "conn" is unused (there is a variable with the same name in the context, use the pin operator (^) to match on it or prefix this variable with underscore if it is not meant to be used)
assert receive_request_string(server_socket) == "5\r\nhello\r\n"
end

Expand All @@ -2095,7 +2167,7 @@
\
""")

{:ok, conn} = HTTP1.stream_request_body(conn, ref, "hello")

Check warning on line 2170 in test/mint/http1/conn_test.exs

View workflow job for this annotation

GitHub Actions / Test (Elixir 1.15.8, OTP 24.3)

variable "conn" is unused (there is a variable with the same name in the context, use the pin operator (^) to match on it or prefix this variable with underscore if it is not meant to be used)
assert receive_request_string(server_socket) == "hello"
end

Expand Down
Loading