Skip to content

Commit 10ef11e

Browse files
committed
Introduce new HTTP/1 option for status reason-phrase
1 parent 8f95bc7 commit 10ef11e

2 files changed

Lines changed: 58 additions & 4 deletions

File tree

lib/mint/http1.ex

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,8 @@ defmodule Mint.HTTP1 do
9999
buffer: "",
100100
proxy_headers: [],
101101
private: %{},
102-
log: false
102+
log: false,
103+
optional_responses: []
103104
]
104105

105106
defmacrop log(conn, level, message) do
@@ -128,6 +129,11 @@ defmodule Mint.HTTP1 do
128129
will not be validated. You might want this if you deal with non standard-
129130
conforming URIs but need to preserve them. The default is to validate the request
130131
target. *Available since v1.7.0*.
132+
* `:optional_responses` - (list of atoms) a list of optional responses to return.
133+
The possible values in the list are `:status_reason` which will return the
134+
[reason-phrase](https://datatracker.ietf.org/doc/html/rfc9112#name-status-line)
135+
for the status code, if it is returned by the server in status-line.
136+
This is only available for HTTP/1.1 connections. *Available since v1.7.2*.
131137
132138
"""
133139
@spec connect(Types.scheme(), Types.address(), :inet.port_number(), keyword()) ::
@@ -206,7 +212,8 @@ defmodule Mint.HTTP1 do
206212
state: :open,
207213
log: log?,
208214
case_sensitive_headers: Keyword.get(opts, :case_sensitive_headers, false),
209-
skip_target_validation: Keyword.get(opts, :skip_target_validation, false)
215+
skip_target_validation: Keyword.get(opts, :skip_target_validation, false),
216+
optional_responses: Keyword.get(opts, :optional_responses, [])
210217
}
211218

212219
{:ok, conn}
@@ -646,10 +653,10 @@ defmodule Mint.HTTP1 do
646653

647654
defp decode(:status, %{request: request} = conn, data, responses) do
648655
case Response.decode_status_line(data) do
649-
{:ok, {version, status, _reason}, rest} ->
656+
{:ok, {version, status, _reason} = status_line, rest} ->
650657
request = %{request | version: version, status: status, state: :headers}
651658
conn = %{conn | request: request}
652-
responses = [{:status, request.ref, status} | responses]
659+
responses = put_status_responses(conn, status_line, responses)
653660
decode(:headers, conn, rest, responses)
654661

655662
:more ->
@@ -872,6 +879,20 @@ defmodule Mint.HTTP1 do
872879
end
873880
end
874881

882+
defp put_status_responses(
883+
%{request: request, optional_responses: optional_responses},
884+
{_version, status, reason},
885+
responses
886+
) do
887+
responses = [{:status, request.ref, status} | responses]
888+
889+
if Enum.member?(optional_responses, :status_reason) do
890+
[{:status_reason, request.ref, reason} | responses]
891+
else
892+
responses
893+
end
894+
end
895+
875896
defp store_header(%{content_length: nil} = request, "content-length", value) do
876897
with {:ok, content_length} <- Parse.content_length_header(value),
877898
do: {:ok, %{request | content_length: content_length}}

test/mint/http1/conn_test.exs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -581,6 +581,39 @@ defmodule Mint.HTTP1Test do
581581
end
582582
end
583583

584+
describe "status reason" do
585+
setup %{port: port} do
586+
assert {:ok, conn} =
587+
HTTP1.connect(:http, "localhost", port, optional_responses: [:status_reason])
588+
589+
[conn: conn]
590+
end
591+
592+
test "returns with 200 OK", %{conn: conn} do
593+
assert {:ok, conn, ref} = HTTP1.request(conn, "GET", "/", [], nil)
594+
595+
assert {:ok, _conn, [{:status, ^ref, 200}, {:status_reason, ^ref, "OK"}]} =
596+
HTTP1.stream(conn, {:tcp, conn.socket, "HTTP/1.1 200 OK\r\n"})
597+
end
598+
599+
test "returns with 404 Not Found", %{conn: conn} do
600+
assert {:ok, conn, ref} = HTTP1.request(conn, "GET", "/", [], nil)
601+
602+
assert {:ok, _conn, [{:status, ^ref, 404}, {:status_reason, ^ref, "Not Found"}]} =
603+
HTTP1.stream(conn, {:tcp, conn.socket, "HTTP/1.1 404 Not Found\r\n"})
604+
end
605+
606+
test "returns empty string when reason is not provided", %{conn: conn} do
607+
assert {:ok, conn, ref} = HTTP1.request(conn, "GET", "/", [], nil)
608+
609+
assert {:ok, _conn, [{:status, ^ref, 200}, {:status_reason, ^ref, ""}]} =
610+
HTTP1.stream(
611+
conn,
612+
{:tcp, conn.socket, "HTTP/1.1 200\r\n"}
613+
)
614+
end
615+
end
616+
584617
describe "non-streaming requests" do
585618
test "content-length header is added if not present",
586619
%{conn: conn, server_socket: server_socket, port: port} do

0 commit comments

Comments
 (0)