Packages
ankh
0.15.0
0.17.0
0.16.0
0.15.0
0.14.5
0.14.4
0.14.3
0.14.2
0.14.1
0.14.0
0.13.0
0.12.1
0.12.0
0.11.0
0.10.0
0.9.0
0.8.11
0.8.10
0.8.9
0.8.8
0.8.7
0.8.6
0.8.5
0.8.4
0.8.3
0.8.2
0.8.1
0.8.0
0.7.2
0.7.1
0.7.0
0.6.0
0.5.3
0.5.2
0.5.1
0.5.0
0.4.6
0.4.5
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.3.1
0.3.0
0.1.1
0.1.0
0.0.5
0.0.4
0.0.3
0.0.2
0.0.1
Pure Elixir HTTP/2 implementation
Current section
Files
Jump to
Current section
Files
lib/ankh/http/response.ex
defmodule Ankh.HTTP.Response do
@moduledoc """
Ankh HTTP Response
"""
alias Ankh.HTTP
@typedoc "Response status"
@type status :: pos_integer()
@type t() :: %__MODULE__{
status: status(),
body: HTTP.body(),
headers: HTTP.headers(),
trailers: HTTP.headers(),
body_fetched: boolean(),
complete: boolean()
}
defstruct status: 200,
body: [],
headers: [],
trailers: [],
body_fetched: false,
complete: false
@spec new(Enum.t()) :: t()
def new(attrs \\ []), do: struct(__MODULE__, attrs)
@spec set_status(t(), status()) :: t()
def set_status(response, status), do: %{response | status: status}
@spec set_body(t(), iodata()) :: t()
def set_body(response, body), do: %{response | body: body}
@spec fetch_body(t()) :: t()
def fetch_body(%__MODULE__{body_fetched: false, body: body} = response) do
fetched_body =
body
|> Enum.reverse()
|> IO.iodata_to_binary()
%{response | body_fetched: true, body: fetched_body}
end
def fetch_body(%__MODULE__{body_fetched: true} = response), do: response
@spec fetch_header_values(t(), HTTP.header_name()) :: [HTTP.header_value()]
defdelegate fetch_header_values(request, header), to: HTTP
@spec fetch_trailer_values(t(), HTTP.header_name()) :: [HTTP.header_value()]
defdelegate fetch_trailer_values(request, trailer), to: HTTP
@spec put_header(t(), HTTP.header_name(), HTTP.header_value()) :: t()
defdelegate put_header(request, name, value), to: HTTP
@spec put_headers(t(), HTTP.headers()) :: t()
defdelegate put_headers(request, headers), to: HTTP
@spec put_trailer(t(), HTTP.header_name(), HTTP.header_value()) :: t()
defdelegate put_trailer(request, name, value), to: HTTP
@spec put_trailers(t(), HTTP.headers()) :: t()
defdelegate put_trailers(request, trailers), to: HTTP
@spec validate_body(t()) :: {:ok, t()} | :error
defdelegate validate_body(response), to: HTTP
@spec validate_headers(HTTP.headers(), boolean(), [HTTP.header_name()]) ::
:ok | {:error, :protocol_error}
def validate_headers(headers, strict, forbidden \\ []),
do: do_validate_headers(headers, strict, forbidden, false, false)
defp do_validate_headers(
[],
_strict,
_forbidden,
true = _status,
_end_pseudo
),
do: :ok
defp do_validate_headers([], _strict, _forbidden, _status, _end_pseudo),
do: {:error, :protocol_error}
defp do_validate_headers(
[{":status", value} | rest],
strict,
forbidden,
false = _status,
false = _end_pseudo
) do
case Integer.parse(value) do
{status, ""} when status in 100..599 ->
do_validate_headers(rest, strict, forbidden, true, false)
_ ->
{:error, :protocol_error}
end
end
defp do_validate_headers(
[{":" <> _pseaudo_header, _value} | _rest],
_strict,
_forbidden,
_stats,
_end_pseudo
),
do: {:error, :protocol_error}
defp do_validate_headers([{header, value} | rest], strict, forbidden, stats, _end_pseudo) do
case {String.downcase(header), value} do
{"te", value} when value != "trailers" ->
{:error, :protocol_error}
{name, _value} ->
if name not in forbidden and HTTP.header_name_valid?(header, strict) do
do_validate_headers(rest, strict, forbidden, stats, true)
else
{:error, :protocol_error}
end
end
end
end