So here is what has been keeping me up at night: At some point I
realized that nix actually made a somewhat passable language for CGI
programming:
* That `builtins.getEnv` exists as one of the impurities of Nix is
perfect as environment variables are the main way of communication
from the web server to the CGI application.
* We can actually read from the filesystem via builtins.readDir and
builtins.readFile with bearable overhead if we avoid importing the
used paths into the nix store.
* Templating and routing are convenient to implement via indented strings
and attribute sets respectively.
Of course there are obvious limitation:
* The overhead of derivations is probably much to great for them to be
useful via IfD.
* Even without derivations, nix evaluation is very slow to the point
were a trivial application takes between 100ms and 400ms to produce a
response.
* We can't really cause effects other than producing a response which
makes it not viable for a lot of applications. There are some ways
around this:
* With a custom interpreter we could have streaming and multiplexed
I/O (using lazy lists emulated via attrsets) to cause such effects,
but it would probably perform terribly.
* We can use builtins.fetchurl to call other HTTP-based microservices,
but only in very limited constraints, i. e. only GET, no headers,
and only if the tarball ttl is set to 0 in the global nix.conf.
* Terrible error handling capabilities because builtins.tryEval actually
doesn't catch a lot of errors.
To prove that it actually works, there are some demo applications,
which I invite you to run and potentially break horribly:
nix-build -A web.bubblegum.examples && ./result
# navigate to http://localhost:9000
The setup uses thttpd and executes the nix CGI scripts using
users.sterni.nint which automatically passed `depot`, so they can
import the cgi library.
Change-Id: I3a22a749612211627e5f8301c31ec2e7a872812c
Reviewed-on: https://cl.tvl.fyi/c/depot/+/2746
Tested-by: BuildkiteCI
Reviewed-by: tazjin <mail@tazj.in>
2 KiB
//web/bubblegum
bubblegum is a CGI programming library for the Nix expression language.
It provides a few helpers to make writing CGI scripts which are executable
using //users/sterni/nint convenient.
An example nix.cgi script looks like this (don't worry about the shebang
too much, you can use web.bubblegum.writeCGI to set this up without
thinking twice):
#!/usr/bin/env nint --arg depot '(import /path/to/depot {})'
{ depot, ... }:
let
inherit (depot.web.bubblegum)
respond
;
in
respond "OK" {
"Content-type" = "text/html";
# further headers…
} ''
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>hello world</title>
</head>
<body>
hello world!
</body>
</html>
''
As you can see, the core component of bubblegum is the respond
function which takes three arguments:
-
The response status as the textual representation which is also returned to the client in the HTTP protocol, e. g.
"OK","Not Found","Bad Request", … -
An attribute set mapping header names to header values to be sent.
-
The response body as a string.
Additionally it exposes a few helpers for working with the CGI
environment like pathInfo which is a wrapper around
builtins.getEnv "PATH_INFO". The documentation for all exposed
helpers is inlined in default.nix (you should be
able to use nixdoc to render it).
For deployment purposes it is recommended to use writeCGI which
takes a nix CGI script in the form of a derivation, path or string
and builds an executable nix CGI script which has the correct shebang
set and is automatically passed a version of depot from the nix store,
so the script has access to the bubblegum library.
For example nix CGI scripts and a working deployment using thttpd
see the examples directory. You can also start a local
server running the examples like this:
$ nix-build -A web.bubblegum.examples && ./result
# navigate to http://localhost:9000