Packages
bandit
1.5.3
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/http2/handler.ex
defmodule Bandit.HTTP2.Handler do
@moduledoc false
# An HTTP/2 handler, this module comprises the primary interface between Thousand Island and an
# HTTP connection. It is responsible for:
#
# * All socket-level sending and receiving from the client
# * Coordinating the parsing of frames & attendant error handling
# * Tracking connection state as represented by a `Bandit.HTTP2.Connection` struct
use ThousandIsland.Handler
@impl ThousandIsland.Handler
def handle_connection(socket, state) do
connection = Bandit.HTTP2.Connection.init(socket, state.plug, state.opts)
{:continue, Map.merge(state, %{buffer: <<>>, connection: connection})}
end
@impl ThousandIsland.Handler
def handle_data(data, socket, state) do
(state.buffer <> data)
|> Stream.unfold(
&Bandit.HTTP2.Frame.deserialize(&1, state.connection.local_settings.max_frame_size)
)
|> Enum.reduce_while(state, fn
{:ok, frame}, state ->
connection = Bandit.HTTP2.Connection.handle_frame(frame, socket, state.connection)
{:cont, %{state | connection: connection, buffer: <<>>}}
{:more, rest}, state ->
{:halt, %{state | buffer: rest}}
{:error, error_code, message}, _state ->
# We encountered an error while deserializing the frame. Let the connection figure out
# how to respond to it
raise Bandit.HTTP2.Errors.ConnectionError, message: message, error_code: error_code
end)
|> then(&{:continue, &1})
end
@impl ThousandIsland.Handler
def handle_shutdown(socket, state) do
Bandit.HTTP2.Connection.close_connection(
Bandit.HTTP2.Errors.no_error(),
"Server shutdown",
socket,
state.connection
)
end
@impl ThousandIsland.Handler
def handle_timeout(socket, state) do
Bandit.HTTP2.Connection.close_connection(
Bandit.HTTP2.Errors.no_error(),
"Client timeout",
socket,
state.connection
)
end
@impl ThousandIsland.Handler
def handle_error({%Bandit.HTTP2.Errors.ConnectionError{} = error, _stacktrace}, socket, state) do
Bandit.HTTP2.Connection.close_connection(
error.error_code,
error.message,
socket,
state.connection
)
end
def handle_error(_error, _socket, _state), do: :ok
def handle_call({{:send_data, data, end_stream}, stream_id}, from, {socket, state}) do
# In 'normal' cases where there is sufficient space in the send windows for this message to be
# sent, Connection will call `unblock` synchronously in the `Connection.send_data` call below.
# In cases where there is not enough space in the connection window, Connection will call
# `unblock` at some point in the future once space opens up in the window. This
# keeps this code simple in that we can blindly send noreply here and let Connection handle
# the separate cases. This ensures that we have backpressure all the way back to the
# stream's handler process in the event of window overruns.
#
# Note that the above only applies to the connection-level send window; stream-level windows
# are managed internally by the stream and are not considered here at all. If the stream has
# managed to send this message, it is because there was enough room in the stream's send
# window to do so.
unblock = fn -> GenServer.reply(from, :ok) end
connection =
Bandit.HTTP2.Connection.send_data(
stream_id,
data,
end_stream,
unblock,
socket,
state.connection
)
{:noreply, {socket, %{state | connection: connection}}, socket.read_timeout}
end
def handle_info({{:send_headers, headers, end_stream}, stream_id}, {socket, state}) do
connection =
Bandit.HTTP2.Connection.send_headers(
stream_id,
headers,
end_stream,
socket,
state.connection
)
{:noreply, {socket, %{state | connection: connection}}, socket.read_timeout}
end
def handle_info({{:send_recv_window_update, size_increment}, stream_id}, {socket, state}) do
Bandit.HTTP2.Connection.send_recv_window_update(
stream_id,
size_increment,
socket,
state.connection
)
{:noreply, {socket, state}, socket.read_timeout}
end
def handle_info({{:send_rst_stream, error_code}, stream_id}, {socket, state}) do
Bandit.HTTP2.Connection.send_rst_stream(stream_id, error_code, socket, state.connection)
{:noreply, {socket, state}, socket.read_timeout}
end
def handle_info({{:close_connection, error_code, msg}, _stream_id}, {socket, state}) do
{:error, reason, connection} =
Bandit.HTTP2.Connection.close_connection(error_code, msg, socket, state.connection)
{:stop, reason, {socket, %{state | connection: connection}}}
end
def handle_info({:EXIT, pid, _reason}, {socket, state}) do
connection = Bandit.HTTP2.Connection.stream_terminated(pid, state.connection)
{:noreply, {socket, %{state | connection: connection}}, socket.read_timeout}
end
end