PlugCaisson behaviour (plug_caisson v0.2.0)
Body reader for supporting compressed Plug
requests.
Implementing algorithm
In addition to the built in algorithms (see read_body/2
) it is possible to
implement custom algorithms by implementing behaviour defined in this module.
Example
defmodule BobbyCompression do
@behaviour PlugCaisson
# Initialise decompression state with whatever is needed
@impl true
def init(opts), do: {:ok, Bobby.open()}
# Gracefully close the state
@impl true
def deinit(state), do: Bobby.finish(state)
# Process read data to decompress them
@impl true
def process(state, data, _opts) do
case Bobby.decompress(state, data) do
{:finished, decompressed, new_state} ->
# All data was decompressed and there is no more data to be
# decompressed in stream
{:ok, decompressed, new_state}
{:more, decompressed, new_state} ->
# It can happen that `decompressed == ""` and this is perfectly fine
# as long as there is possibility that there will be more data in
# future
{:more, decompressed, new_state}
{:error, _} = error -> error
end
end
end
Summary
Callbacks
Cleanup for the state. This will be called in
Plug.Conn.register_before_send/2
callback, so the same conditions as with
these apply. It is guaranteed that it will be called only once for each
connection.
Initialise state for the decompression algorithm.
Process chunk of data.
Callbacks
deinit(state)
Cleanup for the state. This will be called in
Plug.Conn.register_before_send/2
callback, so the same conditions as with
these apply. It is guaranteed that it will be called only once for each
connection.
init(opts)
Initialise state for the decompression algorithm.
This callback will be called if and only if given algorithm was picked as a
suitable option for decompression. The returned state will be stored in the
Plug.Conn.t()
. It is guaranteed that it will be called only on first call to
read_body/2
and all subsequent calls will not call this function again.
It will receive data passed as a second value in tuple declared in the algorithm map.
process(state, data, opts)
@callback process(state :: term(), data :: binary(), opts :: keyword()) :: {:ok, binary(), new_state :: term()} | {:more, binary(), new_state :: term()} | {:error, term()}
Process chunk of data.
It receives current state, binary read from the request and list of options
passed to the read_body/2
as a 2nd argument.
Return value
In case of success it should return 3-ary tuple:
{:ok, binary(), new_state :: term()}
- wich mean that all data was read and there is no more data left in the internal buffer.{:more, binary(), new_state :: term()}
- which mean that data was processed, but there is more data left to be read in future calls.
If error occured during processing {:error, term()}
tuple should be returned.
Functions
read_body(conn, opts \\ [])
Read Plug.Conn
request body and decompress it if needed.
Options
Accepts the same set of options as Plug.Conn.read_body/2
with one option
extra: :algorithms
which is map containing algorithm identifier as key and
tuple containing module name for module that implements PlugCaisson
behaviour and value that will be passed as 2nd argument to the init/1
callback.
By default the value is set to:
%{
"br" => {PlugCaisson.Brotli, []},
"deflate" => {PlugCaisson.Zlib, [type: :deflate]},
"gzip" => {PlugCaisson.Zlib, [type: :gzip]},
"zstd" => {PlugCaisson.Zstandard, []}
}
Supported algorithms
gzip
deflate
br
(Brotli) - only if:brotli
dependency is availablezstd
(Zstandard) - only if:ezstd
dependency is available
Options
All passed opts will be passed to Plug.Conn.read_body/2
and to used
decompression handlers. Decompressors by default will use :length
to limit
amount of returned data to prevent zipbombs. Returned data can be longer than
:length
if the internal decompression buffer was larger. As it is described
in Plug.Conn.read_body/2
docs. By default length: 8_000_000
.