ngx-export-tools-extra-0.2.1.0: More extra tools for Nginx haskell module

Copyright(c) Alexey Radkov 2020
LicenseBSD-style
Maintaineralexey.radkov@gmail.com
Stabilityexperimental
Portabilitynon-portable (requires Template Haskell)
Safe HaskellNone
LanguageHaskell98

NgxExport.Tools.EDE

Contents

Description

EDE templates for parsing JSON objects from the more extra tools collection for nginx-haskell-module.

Synopsis

Rendering JSON objects using EDE templates

This module allows for complex parsing of JSON objects with EDE templating language. In terms of module NgxExport.Tools, it exports a single-shot service compileEDETemplates to configure the list of templates parameterized by a simple key, and an asynchronous variable handler renderEDETemplate for parsing POSTed JSON objects and substitution of extracted data in the provided EDE template.

Below is a simple example.

File test_tools_extra_ede.hs

{-# OPTIONS_GHC -Wno-unused-imports #-}

module TestToolsExtraEDE where

import NgxExport.Tools.EDE

This file does not contain any significant declarations as soon as we do not require anything besides the two exporters. As soon as imported entities are not used, option -Wno-unused-imports was added on the top of the file.

File nginx.conf

user                    nobody;
worker_processes        2;

events {
    worker_connections  1024;
}

http {
    default_type        application/octet-stream;
    sendfile            on;

    haskell load /var/lib/nginx/test_tools_extra_ede.so;

    haskell_run_service simpleService_compileEDETemplates $hs_EDETemplates
            '("/var/lib/nginx/EDE",
              [("user",
                "{{user.id}}/{{user.ops|b64}}/{{resources.path|uenc}}")])';

    server {
        listen       8010;
        server_name  main;
        error_log    /tmp/nginx-test-haskell-error.log;
        access_log   /tmp/nginx-test-haskell-access.log;

        location / {
            haskell_run_async_on_request_body renderEDETemplate $hs_user user;
            rewrite ^ /internal/user/$hs_user last;
        }

        location ~ ^/internal/user/(EDE\ ERROR:.*) {
            internal;
            echo_status 404;
            echo "Bad input: $1";
        }

        location ~ ^/internal/user/([^/]+)/([^/]+)/([^/]+)$ {
            internal;
            echo "User id: $1, options: $2, path: $3";
        }

        location ~ ^/internal/user/(.*) {
            internal;
            echo_status 404;
            echo "Unexpected input: $1";
        }
    }
}

There is an EDE template declared by the argument of service simpleService_compileEDETemplates. The template will be accessed later in the asynchronous body handler renderEDETemplate with key user. Path /var/lib/nginx/EDE can be used in the templates to include more rules from files located inside it, but we do not actually use this here.

The rule inside template user says: with given JSON object,

  • print object id inside a top object user,
  • print slash,
  • print object ops inside the top object user filtered by function b64,
  • print slash,
  • print object path inside a top object resources filtered by function uenc.

Functions b64 and uenc are polymorphic filters in terms of EDE language. There are many filters shipped with EDE, but b64 and uenc were defined in this module.

  • b64 encodes a JSON object using base64url encoding
  • uenc encodes a JSON object using URL encoding rules

So, basically, we used renderEDETemplate to decompose POSTed JSON objects and then rewrite requests to other locations where extracted fields were encoded inside the location's URL path.

A simple test

$ curl -d '{"user": {"id" : "user1", "ops": ["op1", "op2"]}, "resources": {"path": "/opt/users"}}' 'http://localhost:8010/'
User id: user1, options: WyJvcDEiLCJvcDIiXQ==, path: %2Fopt%2Fusers

Let's try to send a broken (in any meaning) input value.

$ curl -d '{"user": {"id" : "user1", "ops": ["op1", "op2"]}, "resources": {"p": "/opt/users"}}' 'http://localhost:8010/'
Bad input: EDE ERROR: Text.EDE.parse:1:32 error: variable resources.path doesn't exist.

Now we got response with HTTP status 404 and a comprehensive description of what went wrong. To not mess rewrite logic and error responses, variable $hs_user can be listed inside directive haskell_var_empty_on_error in the Nginx configuration.

    haskell_var_empty_on_error $hs_user;

Now the variable will always be empty on errors, while the errors will still be logged by Nginx in the error log.

renderEDETemplate Source #

Arguments

:: ByteString

JSON object

-> ByteString

Key to find the EDE template

-> IO ByteString 

Renders an EDE template from a JSON object.

This is the core function of the renderEDETemplate exporter. Accepts a JSON object written in a ByteString and a key to find a compiled EDE template declared by the compileEDETemplates exporter.

renderEDETemplateWith Source #

Arguments

:: (ByteString -> Maybe Value)

Decoding function

-> ByteString

JSON object

-> ByteString

Key to find the EDE template

-> IO ByteString 

Renders an EDE template with a custom decoding function.

This function can be used for templating from any configuration language which is translatable to Aeson's Value.