Middleware

Middleware is a function of the form f(stream::HTTP.Stream, next), where next is the following handler/middleware in the list. It’s called in the order it was added, with the matching handler (if any) called last. By default app.middleware will run on all request methods however you can specify a specific method with app.middleware.get Bellow are examples of common use cases for middleware

Logging

Log each request method and URL.

function log_request(stream, next)
    @info "request" stream.message.method stream.message.url
    next(stream)
end

# don't forget to register the middleware
app.middleware["**"] = [log_request]

Time

The handler below logs the time taken to call the next handlers in the chain.

function time_taken(stream, next)
    x = now()
    next(stream)
    elapsed = now() - x
    @info "$(stream.message.target) took $elapsed" 
end

# make sure the middleware is first so
# we measure the time of all the  downstream handlers
app.middleware["**"] = [time_taken, ...other_middleware]

Authentication

For example, authentication might look something like this

function authentication(stream, next)
    headers = Bonsai.read(
        stream,
        Header(x_password=String)
    )

    if headers.x_password != "secret_password"
        return Bonsai.write(stream, Status(403), Body("Forbidden"))
    else
        next(stream)
    end
end

function protected_route(stream, next)
    return Bonsai.write(stream, Body("Welcome to the club"))
end

function unproteced_route(stream, next)
    return Bonsai.write(stream, Body("Welcome to the club"))
end

function register_middleware!(app)
    app.middleware["/protected/**"] = [authentication]
    app.get["/protected/"] = protected_route
    app.get["/"] = unproteced_route
end

TODO: a proper example using JWTs.

CORS

The below middleware will accept CORS requests from any domain and HTTP method. You can restrict it to specific domains by changing the relevant header e.g "Access-Control-Allow_Origin" => "https://my-website.com". For more information on CORS see https://cors-errors.info/

function cors(stream::Stream, next)
    res = stream.message.response

    if stream.message.method == "OPTIONS"
        headers = [
            "Access-Control-Allow-Origin" => "*",
            "Access-Control-Allow-Headers" => "*",
            "Access-Control-Allow-Methods" => "*",
        ]
        for i in headers
            HTTP.setheader(res, i)
        end
    else
        headers = [
            "Access-Control-Allow-Origin" => "*",
        ]
        for i in headers
            HTTP.setheader(res, i)
        end
        next(stream)
    end
end

function register_middleware!(app)
    # runs on all routes
    app.middleware["**"] = [authentication]
end

Note you probably don’t want to use a wildcard * for your origin but rather something more specific.