To Err is Human; to Handle, Divine

Vinland has received a significant update in regard to error handling and debugging.

Executive summary:

New libraries

print-hash

Source

The print-hash library does exactly what it says on the tin: function PRINT-HASH prints a hash-table, using a literal syntax inspired by the make-hash reader syntax. Nested hash-tables are supported, and the spacing can be modified by overriding special variable *INDENTATION-SPACES*.

This library was developed in conjunction with redact and lack-middleware-debug to enable easy inspection of any hash tables contained in the Clack ENV, such as request headers and the Lack session.

#{
  foo bar
  baaz #{
    quux baar
    bar foo
    baaz #{
      foo foo
    }
  }
}

http-response

Source

Extracted from Vinland, the foo.lisp.http-response ASDF system provides types for HTTP response status codes, functions to generate text/plain responses and text representations from these codes, and functions to translate between HTTP response codes and their representative keywords; also defines condition types CLIENT-ERROR and SERVER-ERROR (and the parent mixin, HTTP-ERROR).

redact

Source

The Redact repository contains two ASDF systems, foo.lisp.redact and foo.lisp.lack-middleware-redact.

Package LACK/MIDDLEWARE/REDACT, defined in system foo.lisp.lack-middleware-redact, is used to configure redaction policies used by downstream middlewares. The middleware is configured with a list of parameter names to redact, a list of headers to redact, and a list of cookie names for which to skip redaction (cookies are redacted by default). When a value is redacted, it is replaced with the string "[REDACTED]". Function PRINT-ENV is used to print the entire Clack ENV with all relevant values redacted: it uses the print-hash library to format any hash-tables contained in the ENV, so that headers, the Lack session, or any other hash-tables are easily inspectable.

Package FOO.LISP.REDACT provides functions for redacting the Clack ENV based on policies specified by the middleware. FOO.LISP.REDACT/BACKTRACE allows for redacting backtraces.

Example:

(lack:builder
 (:redact :parameters '("token" "code" "password")
          :headers '("x-access-token")
          :preserve-cookies '("lack.session"))
 *app*)

lack-middleware-errors

Source

The errors middleware defines a HANDLER-BIND for all application errors and allows either rendering a static file or calling a provided function in response to an error. The INTERCEPT option specifies a function called to translate an error to an HTTP response code, while the APP option specifies a Clack application that should handle the request.

(lack/builder:
 (:errors :app (foo.lisp.vinland/errors-app/simple/dynamic-override:make-app
                :root todo-app/config:*static-errors-directory*
                :static-file-types todo-app/http-error:*static-file-types*
                :handlers todo-app/http-error:*http-errors*
                :dynamic-override-p (lambda (env)
                                      (declare (ignore env))
                                      lack/middleware/user:*current-user*))
          :intercept (lambda (condition)
                       (declare (type error condition))
                       (typecase condition
                         (foo.lisp.http-response:http-error (slot-value
                                                             condition
                                                             'foo.lisp.http-response:status-code))
                         (foo.lisp.raven:no-route-error 404)
                         (lack/middleware/session/store/redis-pool:redis-pool-timeout-error 503))))
 todo-app/web:*web*)

lack-middleware-debug

Source

This middleware prints the redacted ENV and backtrace to the console and renders a debug page whenever an error is signalled; it is meant to be included in the middleware pipeline in development and removed in production. The debug page includes the type of error, the error message, the redacted Clack ENV, specified special variables, the redacted backtrace, and system information. By default the debug page is unstyled, but an option is provided for specifying an external stylesheet.

This middleware should be included immediately after the errors middleware.

Example (open in a new tab to view in detail):

Lack Middleware Debug
(lack/builder:
  (:redact :parameters '("token" "code" "password")
           :preserve-cookies '("lack.session"))
  (:debug :special-variables '(foo.lisp.vinland:*route*
                               foo.lisp.vinland:*binding*))
  *app*)

Updates

lack-request

Source

foo.lisp.lack-request, a fork of the official lack/request library, has been updated to add a package related to content negotiation. The new package FOO.LISP.LACK/REQUEST/CONTENT-NEGOTIATION exports function NEGOTIATE-MEDIA-TYPE.

raven

Source

Raven, the URL dispatcher library on which Vinland is based, has been updated to signal error NO-ROUTE-ERROR when a route cannot be matched to a request path on dispatch. The previous behavior was to return a text/plain 404 Clack response list. Also, routes/controllers defined with DEFINE-ROUTE are now functions (sets the FDEFINITION for the route symbol), meaning that you can call TRACE with any route name, or access documentation for any route in a uniform way.

vinland

Source

The web and api skeletons have been updated:

vinland-todo-app

Source

The Vinland To Do App has been updated to reflect the latest additions: