Packages
bandit
1.4.0
1.12.0
1.11.1
1.11.0
1.10.4
1.10.3
1.10.2
1.10.1
1.10.0
retired
1.9.0
1.8.0
1.7.0
1.6.11
1.6.10
1.6.9
1.6.8
1.6.7
1.6.6
1.6.5
1.6.4
1.6.3
1.6.2
1.6.1
1.6.0
1.5.7
1.5.6
1.5.5
1.5.4
1.5.3
1.5.2
1.5.1
1.5.0
1.4.2
1.4.1
1.4.0
1.3.0
1.2.3
1.2.2
1.2.1
1.2.0
1.1.3
1.1.2
1.1.1
1.1.0
1.0.0
1.0.0-pre.18
1.0.0-pre.17
1.0.0-pre.16
1.0.0-pre.15
1.0.0-pre.14
1.0.0-pre.13
1.0.0-pre.12
1.0.0-pre.11
1.0.0-pre.10
1.0.0-pre.9
1.0.0-pre.8
1.0.0-pre.7
1.0.0-pre.6
1.0.0-pre.5
1.0.0-pre.4
1.0.0-pre.3
1.0.0-pre.2
1.0.0-pre.1
0.7.7
0.7.6
0.7.5
0.7.4
0.7.3
0.7.2
0.7.1
0.7.0
0.6.11
0.6.10
0.6.9
0.6.8
0.6.7
0.6.6
0.6.5
0.6.4
0.6.3
0.6.2
0.6.1
0.6.0
0.5.11
0.5.10
0.5.9
0.5.8
0.5.7
0.5.6
0.5.5
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.10
0.4.9
0.4.8
0.4.7
0.4.6
0.4.5
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.3.9
0.3.8
0.3.7
0.3.6
0.3.5
0.3.4
0.3.3
0.3.2
0.2.3
0.2.2
0.2.1
0.2.0
0.1.1
0.1.0
A pure-Elixir HTTP server built for Plug & WebSock apps
Security advisory:
This version has known vulnerabilities.
View advisories
Current section
Files
Jump to
Current section
Files
lib/bandit/http1/handler.ex
defmodule Bandit.HTTP1.Handler do
@moduledoc false
# An HTTP 1.0 & 1.1 Thousand Island Handler
use ThousandIsland.Handler
@already_sent {:plug_conn, :sent}
@impl ThousandIsland.Handler
def handle_data(data, socket, state) do
connection_span = ThousandIsland.Socket.telemetry_span(socket)
span =
Bandit.Telemetry.start_span(:request, %{}, %{
connection_telemetry_span_context: connection_span.telemetry_span_context
})
transport = %Bandit.HTTP1.Socket{socket: socket, buffer: data, opts: state.opts}
try do
case Bandit.Pipeline.run(transport, state.plug, state.opts) do
{:ok, %Plug.Conn{adapter: {_mod, adapter}} = conn} ->
Bandit.Telemetry.stop_span(span, adapter.metrics, %{conn: conn})
maybe_keepalive(adapter, state)
{:error, reason} ->
attempt_to_send_fallback(transport, 400)
Bandit.Telemetry.stop_span(span, %{}, %{error: reason, status: 400})
if Keyword.get(state.opts.http_1, :log_protocol_errors, true),
do: {:error, reason, state},
else: {:close, state}
{:ok, :websocket, %Plug.Conn{adapter: {Bandit.Adapter, adapter}} = conn, upgrade_opts} ->
Bandit.Telemetry.stop_span(span, adapter.metrics, %{conn: conn})
state =
state
|> Map.put(:upgrade_opts, upgrade_opts)
|> Map.put(
:origin_telemetry_span_context,
Bandit.Telemetry.telemetry_span_context(span)
)
{:switch, Bandit.WebSocket.Handler, state}
end
rescue
error in Bandit.HTTP1.Error ->
_ = attempt_to_send_fallback(transport, error.status)
Bandit.Telemetry.stop_span(span, %{}, %{error: error.message, status: error.status})
if Keyword.get(state.opts.http_1, :log_protocol_errors, true),
do: {:error, error.message, state},
else: {:close, state}
error ->
_ = attempt_to_send_fallback(transport, 500)
Bandit.Telemetry.span_exception(span, :exit, error, __STACKTRACE__)
reraise error, __STACKTRACE__
end
end
defp attempt_to_send_fallback(transport, status) do
receive do
@already_sent ->
send(self(), @already_sent)
after
0 ->
try do
Bandit.HTTP1.Socket.send_error(transport, status)
rescue
_ -> :ok
end
end
end
defp maybe_keepalive(adapter, 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 && adapter.transport.keepalive do
try do
_ = Bandit.HTTPTransport.ensure_completed(adapter.transport)
gc_every_n_requests = Keyword.get(state.opts.http_1, :gc_every_n_keepalive_requests, 5)
if rem(requests_processed, gc_every_n_requests) == 0, do: :erlang.garbage_collect()
{:continue, Map.put(state, :requests_processed, requests_processed)}
rescue
_error in Bandit.HTTP1.Error -> {:close, state}
end
else
{:close, state}
end
end
def handle_info({:plug_conn, :sent}, {socket, state}),
do: {:noreply, {socket, state}, socket.read_timeout}
def handle_info({:EXIT, _pid, :normal}, {socket, state}),
do: {:noreply, {socket, state}, socket.read_timeout}
def handle_info(msg, {socket, state}) do
if Keyword.get(state.opts.http_1, :log_unknown_messages, false), do: log_no_handle_info(msg)
{:noreply, {socket, state}, socket.read_timeout}
end
def handle_info(msg, state) do
log_no_handle_info(msg)
{:noreply, state}
end
defp log_no_handle_info(msg) do
# Copied verbatim from lib/elixir/lib/gen_server.ex
proc =
case Process.info(self(), :registered_name) do
{_, []} -> self()
{_, name} -> name
end
:logger.error(
%{
label: {GenServer, :no_handle_info},
report: %{
module: __MODULE__,
message: msg,
name: proc
}
},
%{
domain: [:otp, :elixir],
error_logger: %{tag: :error_msg},
report_cb: &GenServer.format_report/1
}
)
end
end