Malachi. Console. HeaderDeadline
(malachi v0.22.7)
View Source
One deadline for the request line and the whole header block of every request on a console
connection, the guarantee Malachi.HTTP.Limits.recv_timeout_ms/0 describes and the dashboard keeps.
Bandit only has a per-read timeout, restarted on every chunk it reads, so a client that sends one header byte just inside it holds a connection for as long as the header limits allow, which is weeks (slowloris). This module closes that gap without changing Bandit:
- It is the Thousand Island handler of the console endpoint, delegating every callback to
Bandit's own delegating handler. When a connection starts, it spawns a watchdog process for it and
registers the watchdog under the connection's pid in
registry/0. - The watchdog is armed from the start: if the deadline passes, it closes the socket. A
connection blocked reading headers sees its read fail and ends; one waiting between keep alive
requests receives the exit of its own socket and stops with
{:shutdown, :header_deadline}. Malachi.Console.Routerdisarms it when a request reaches the plug, which is when every header has been read, and arms it again once the response has been sent, so the idle wait for the next request and that request's headers share one deadline, whether the next request arrives later or was already pipelined behind this one.
The watchdog is registered in a Registry rather than the process dictionary because Bandit clears
the dictionary between keep alive requests. It monitors its connection and exits with it.
Only HTTP/1 is served (see Malachi.Console.Endpoint): under HTTP/2 a request runs in a process of
its own, so the plug could not disarm the connection's watchdog.
Summary
Functions
Starts the calling connection's deadline again, if it has one. A response has been sent.
Returns a specification to start this module under a supervisor.
Stops the calling connection's deadline, if it has one. A request has finished its headers.
The name of the Registry the watchdogs are registered in, keyed by connection pid.
Functions
@spec arm() :: :ok
Starts the calling connection's deadline again, if it has one. A response has been sent.
Returns a specification to start this module under a supervisor.
See Supervisor.
@spec disarm() :: :ok
Stops the calling connection's deadline, if it has one. A request has finished its headers.
@spec registry() :: atom()
The name of the Registry the watchdogs are registered in, keyed by connection pid.
@spec start_link({handler_options :: term(), GenServer.options()}) :: GenServer.on_start()