messua/mware
Some basic middlware layers.
See the documentation for
Layer
and even more so the
Stack example
for demonstrations of how to put these together.
Types
Functions
pub fn make_blacklist_layer(
addrs: List(IpAddr),
) -> fn(
MRequest(a),
fn(MRequest(a)) -> Result(Response(ResponseData), Err),
) -> Result(Response(ResponseData), Err)
Return a layer that rejects requests from all listed addresses with an unceremonious 404.
You can use handle.parse_ip()
to help read
a list of strings (like from a configuration file).
Plans for a future version include a way to specify a range of IP addresses.
Examples
import gleam/list
import gleam/result
import messua/handle
import messua/mware
let assert Ok(blacklist) = [
"173.194.219.100",
"173.194.219.101",
"173.194.219.102",
"173.194.219.113",
"173.194.219.138",
"173.194.219.139",
]
|> list.map(handle.parse_ip)
|> result.all()
let blacklist_layer = mware.make_blacklist_layer(blacklist)
pub fn make_compression_layer(
min_size: Int,
) -> fn(
MRequest(a),
fn(MRequest(a)) -> Result(Response(ResponseData), Err),
) -> Result(Response(ResponseData), Err)
Return a Layer
that compresses outgoing responses if they are at least
min_size
bytes and the incoming request contained an
Accept-Encoding: deflate
header.
Like ok.compress_body()
, this does not do
anything to responses generated with
ok.serve_file()
or
ok.serve_dir()
.
pub fn make_log_layer(
target: LogTarget,
) -> fn(
MRequest(a),
fn(MRequest(a)) -> Result(Response(ResponseData), Err),
) -> Result(Response(ResponseData), Err)
Return a Layer
function that logs to the specified target.
Currently, logging to a file opens the file for append each time. This is clearly not the most efficient method, but I don’t know enough Erlang yet to do better.
TODO: Learn some Erlang and do better.
Safety
If you specify a path that cannot be written to, the server will crash upon startup.
pub fn make_rate_limit_layer(
requests: Int,
over_seconds: Int,
cull_expired_counters_every: Int,
) -> fn(
MRequest(a),
fn(MRequest(a)) -> Result(Response(ResponseData), Err),
) -> Result(Response(ResponseData), Err)
Return a layer that limits requests from any given IP address to
requests
requests every over_seconds
seconds. Excess requests will
be rejected with a a 429.
10 requests every two seconds and 20 requests every four seconds both limit to the same average rate (one request every 200 ms), but the latter setting allows for larger “bursts” of requests (with, of course, a correspondingly longer cooldown).
The cull_expired_counters_every
parameter controls how often the
layer will crawl through and throw away records from clients that
haven’t send a request in over_seconds
seconds or more.
Examples
import messua
import messua/mware
import messua/rr
fn inner_handler(req: rr.MRequest) -> rr.MResponse {
// Your routing and business logic goes here.
}
// 2 requests/second, with bursts up to 12 requests.
let limit_layer = mware.make_rate_limit_layer(12, 6, 1024)
// Wrap our `inner_handler` in our `limit_layer` to make our
// composite handler.
let handler = limit_layer(_, inner_handler)
messua.default()
|> messua.start(handler)
pub fn make_static_header_layer(
headers: List(#(String, String)),
) -> fn(
MRequest(a),
fn(MRequest(a)) -> Result(Response(ResponseData), Err),
) -> Result(Response(ResponseData), Err)
Adds the given list of #(name, value)
headers to each response.