Current section

Files

Jump to
bandit lib bandit http1 handler.ex
Raw

lib/bandit/http1/handler.ex

defmodule Bandit.HTTP1.Handler do
@moduledoc false
# An HTTP 1.0 & 1.1 Thousand Island Handler
use ThousandIsland.Handler
# credo:disable-for-this-file Credo.Check.Design.AliasUsage
@impl ThousandIsland.Handler
def handle_data(data, socket, state) do
{_, _, _, connection_span} = transport_info = build_transport_info(socket)
span =
Bandit.Telemetry.start_span(:request, %{}, %{
connection_telemetry_span_context: connection_span.telemetry_span_context
})
req = %Bandit.HTTP1.Adapter{socket: socket, buffer: data, opts: state.opts}
try do
with {:ok, headers, method, request_target, req} <-
Bandit.HTTP1.Adapter.read_headers(req),
{:ok, %Plug.Conn{adapter: {Bandit.HTTP1.Adapter, req}} = conn} <-
Bandit.Pipeline.run(
{Bandit.HTTP1.Adapter, req},
transport_info,
method,
request_target,
headers,
state.plug
) do
Bandit.Telemetry.stop_span(span, Map.put(req.metrics, :conn, conn))
maybe_keepalive(req, state)
else
{:error, reason} ->
attempt_to_send_fallback(req, code_for_reason(reason))
Bandit.Telemetry.stop_span(span, %{}, %{error: reason})
{:error, reason, state}
{:ok, :websocket, %Plug.Conn{adapter: {Bandit.HTTP1.Adapter, req}} = conn, upgrade_opts} ->
Bandit.Telemetry.stop_span(span, Map.put(req.metrics, :conn, conn))
state =
state
|> Map.put(:upgrade_opts, upgrade_opts)
|> Map.put(:origin_telemetry_span_context, span.telemetry_span_context)
{:switch, Bandit.WebSocket.Handler, state}
end
rescue
exception ->
# Raise here so that users can see useful stacktraces
attempt_to_send_fallback(req, 500)
Bandit.Telemetry.span_exception(span, :exit, exception, __STACKTRACE__)
reraise(exception, __STACKTRACE__)
end
end
defp build_transport_info(socket) do
{ThousandIsland.Socket.secure?(socket), ThousandIsland.Socket.local_info(socket),
ThousandIsland.Socket.peer_info(socket), ThousandIsland.Socket.telemetry_span(socket)}
end
defp code_for_reason(:timeout), do: 408
defp code_for_reason(:request_uri_too_long), do: 414
defp code_for_reason(:header_too_long), do: 431
defp code_for_reason(:too_many_headers), do: 431
defp code_for_reason(_), do: 400
defp attempt_to_send_fallback(req, code) do
Bandit.HTTP1.Adapter.send_resp(req, code, [], <<>>)
rescue
_ -> :ok
end
defp maybe_keepalive(req, state) do
requests_processed = Map.get(state, :requests_processed, 0) + 1
request_limit = Keyword.get(state.opts.http_1, :max_requests, 0)
under_limit = request_limit == 0 || requests_processed < request_limit
if under_limit && Bandit.HTTP1.Adapter.keepalive?(req) do
{:continue, Map.put(state, :requests_processed, requests_processed)}
else
{:close, state}
end
end
def handle_info({:plug_conn, :sent}, state), do: {:noreply, state}
def handle_info({:EXIT, _pid, :normal}, state), do: {:noreply, state}
end