diff options
author | sterni <sternenseemann@systemli.org> | 2021-04-04T23·53+0200 |
---|---|---|
committer | sterni <sternenseemann@systemli.org> | 2021-04-05T10·54+0000 |
commit | 1c0f89f4cadba4957e95fc80b25b1b8b6dd1a3b3 (patch) | |
tree | 3c0e209f13a3d8b3fb19c9bacde5b617458ae2d4 /web/bubblegum/default.nix | |
parent | cbd6f5bbae95c26b1e67993ac609e83fa4b117fb (diff) |
feat(web/bubblegum): report some errors to the user via HTTP r/2438
We can actually catch some errors that may be generated in bubblegum applications where we can report them to the user in a way that doesn't require curl -vv: * Type errors in the status argument: By removing yants completely we not only (presumably) gain some performance, but also the ability to return an internal server error on an unexpected type instead of throwing. * User generated evaluation errors: by using builtins.tryEval we can catch throws and asserts the user inserted when generating the body and report to the user that something went wrong. To do: also support for the headers. Change-Id: I8363b9825c6c730e624eb8016a5482d63cbc1890 Reviewed-on: https://cl.tvl.fyi/c/depot/+/2849 Tested-by: BuildkiteCI Reviewed-by: sterni <sternenseemann@systemli.org>
Diffstat (limited to '')
-rw-r--r-- | web/bubblegum/default.nix | 73 |
1 files changed, 39 insertions, 34 deletions
diff --git a/web/bubblegum/default.nix b/web/bubblegum/default.nix index 81e41cfbdfbd..393ac75d4803 100644 --- a/web/bubblegum/default.nix +++ b/web/bubblegum/default.nix @@ -2,23 +2,11 @@ let - inherit (depot.nix.yants) - defun - restrict - struct - string - int - attrs - enum - ; - inherit (depot.nix) runExecline getBins ; - headers = attrs string; - statusCodes = { # 1xx "Continue" = 100; @@ -90,9 +78,6 @@ let "Network Authentication Required" = 511; }; - status = enum "bubblegum.status" - (builtins.attrNames statusCodes); - /* Generate a CGI response. Takes three arguments: 1. Status of the response as a string which is @@ -104,21 +89,42 @@ let See the [README](./README.md) for an example. - Type: Status -> Headers -> Body -> string + Type: string -> attrs string -> string -> string */ - respond = defun [ status headers string string ] - (s: hs: body: - let - code = status.match s statusCodes; - renderedHeaders = lib.concatStrings - (lib.mapAttrsToList (n: v: "${n}: ${v}\r\n") hs); - in - lib.concatStrings [ - "Status: ${toString code} ${s}\r\n" - renderedHeaders - "\r\n" - body - ]); + respond = + # response status as the textual representation in the + # HTTP protocol. See `statusCodes` for a list of valid + # options. + statusArg: + # headers as an attribute set of strings + headers: + # response body as a string + bodyArg: + let + status = + if builtins.isString statusArg then { + code = statusCodes."${statusArg}" or null; + line = statusArg; + } else { + code = null; line = null; + }; + renderedHeaders = lib.concatStrings + (lib.mapAttrsToList (n: v: "${n}: ${toString v}\r\n") headers); + internalError = msg: respond 500 { + Content-type = "text/plain"; + } "bubblegum error: ${msg}"; + body = builtins.tryEval bodyArg; + in + if status.code == null || status.line == null + then internalError "Invalid status ${lib.generators.toPretty {} statusArg}." + else if !body.success + then internalError "Unknown evaluation error in user code" + else lib.concatStrings [ + "Status: ${toString status.code} ${status.line}\r\n" + renderedHeaders + "\r\n" + body.value + ]; /* Returns the value of the `SCRIPT_NAME` environment variable used by CGI. @@ -147,11 +153,10 @@ let Type: string -> string */ - absolutePath = defun [ string string ] - (path: - if builtins.substring 0 1 path == "/" - then "${scriptName}${path}" - else "${scriptName}/${path}"); + absolutePath = path: + if builtins.substring 0 1 path == "/" + then "${scriptName}${path}" + else "${scriptName}/${path}"; bins = getBins pkgs.coreutils [ "env" "tee" "cat" "printf" "chmod" ] // getBins depot.users.sterni.nint [ "nint" ]; |