JS#

Позволяет задавать обработчики на njs — подмножестве языка JavaScript.

В наших репозиториях модуль собран динамически и доступен отдельным пакетом angie-module-njs или angie-pro-module-njs; подключить его можно с помощью директивы load_module.

Пример конфигурации#

http {
    js_import http.js;

    js_set $foo     http.foo;
    js_set $summary http.summary;
    js_set $hash    http.hash;

    resolver 127.0.0.53;

    server {
        listen 8000;

        location / {
            add_header X-Foo $foo;
            js_content http.baz;
        }

        location = /summary {
            return 200 $summary;
        }

        location = /hello {
            js_content http.hello;
        }

        location = /fetch {
            js_content                   http.fetch;
            js_fetch_trusted_certificate /path/to/ISRG_Root_X1.pem;
        }

        location = /crypto {
            add_header Hash $hash;
            return     200;
        }
    }
}

Файл http.js:

function foo(r) {
    r.log("hello from foo() handler");
    return "foo";
}

function summary(r) {
    var a, s, h;

    s = "JS summary\n\n";

    s += "Method: " + r.method + "\n";
    s += "HTTP version: " + r.httpVersion + "\n";
    s += "Host: " + r.headersIn.host + "\n";
    s += "Remote Address: " + r.remoteAddress + "\n";
    s += "URI: " + r.uri + "\n";

    s += "Headers:\n";
    for (h in r.headersIn) {
        s += "  header '" + h + "' is '" + r.headersIn[h] + "'\n";
    }

    s += "Args:\n";
    for (a in r.args) {
        s += "  arg '" + a + "' is '" + r.args[a] + "'\n";
    }

    return s;
}

function baz(r) {
    r.status = 200;
    r.headersOut.foo = 1234;
    r.headersOut['Content-Type'] = "text/plain; charset=utf-8";
    r.headersOut['Content-Length'] = 15;
    r.sendHeader();
    r.send("nginx");
    r.send("java");
    r.send("script");

    r.finish();
}

function hello(r) {
    r.return(200, "Hello world!");
}

async function fetch(r) {
    let results = await Promise.all([ngx.fetch('https://google.com/'),
                                     ngx.fetch('https://google.ru/')]);

    r.return(200, JSON.stringify(results, undefined, 4));
}

async function hash(r) {
    let hash = await crypto.subtle.digest('SHA-512', r.headersIn.host);
    r.setReturnValue(Buffer.from(hash).toString('hex'));
}

export default {foo, summary, baz, hello, fetch, hash};

Директивы#

js_body_filter#

Синтаксис

js_body_filter функция | модуль.функция [buffer_type=строка | буфер];

По умолчанию

Контекст

location, if in location, limit_except

Задает функцию njs в качестве фильтра тела ответа. Функция фильтра вызывается для каждого блока данных тела ответа со следующими аргументами:

r

объект HTTP request

data

входящий блок данных может быть строкой или буфером в зависимости от значения buffer_type, по умолчанию является строкой.

flags

объект со следующими свойствами:
last — логическое значение

true — если данные являются последним буфером.

Функция фильтра может передавать свою модифицированную версию входящего блока данных следующему фильтру тела ответа при помощи вызова r.sendBuffer(). Пример преобразования букв в нижний регистр в теле ответа:

function filter(r, data, flags) {
    r.sendBuffer(data.toLowerCase(), flags);
}

Для отмены фильтра (блоки данных будут передаваться клиенту без вызова js_body_filter), можно использовать r.done().

Если функция фильтра изменяет длину тела ответа, то необходимо очистить заголовок ответа "Content-Length" (если присутствует) в js_header_filter, чтобы применить поблочное кодирование.

Примечание

Так как обработчик js_body_filter должен сразу возвращать результат, то поддерживаются только синхронные операции, Таким образом, асинхронные операции, например r.subrequest() или setTimeout(), не поддерживаются.

js_content#

Синтаксис

js_content функция | модуль.функция;

По умолчанию

Контекст

location, if in location, limit_except

Задает функцию njs в качестве обработчика содержимого location. Можно ссылаться на функцию модуля.

js_fetch_buffer_size#

Синтаксис

js_fetch_buffer_size размер;

По умолчанию

js_fetch_buffer_size 16k;

Контекст

http, server, location

Задает размер буфера, который будет использоваться для чтения и записи для Fetch API.

js_fetch_ciphers#

Синтаксис

js_fetch_ciphers шифры;

По умолчанию

js_fetch_ciphers HIGH:!aNULL:!MD5;

Контекст

http, server, location

Описывает разрешенные шифры для HTTPS-соединений при помощи Fetch API. Шифры задаются в формате, поддерживаемом библиотекой OpenSSL.

Полный список можно посмотреть с помощью команды "openssl ciphers".

js_fetch_max_response_buffer_size#

Синтаксис

js_fetch_max_response_buffer_size размер;

По умолчанию

js_fetch_max_response_buffer_size 1m;

Контекст

http, server, location

Задает максимальный размер ответа, полученного при помощи Fetch API.

js_fetch_protocols#

Синтаксис

js_fetch_protocols [TLSv1] [TLSv1.1] [TLSv1.2] [TLSv1.3];

По умолчанию

js_fetch_protocols TLSv1 TLSv1.1 TLSv1.2;

Контекст

http, server, location

Разрешает указанные протоколы для HTTPS-соединений при помощи Fetch API.

js_fetch_timeout#

Синтаксис

js_fetch_timeout время;

По умолчанию

js_fetch_timeout 60s;

Контекст

http, server, location

Задает таймаут при чтении и записи при помощи Fetch API. Таймаут устанавливается не на всю передачу ответа, а только между двумя операциями чтения. Если по истечении этого времени данные не передавались, соединение закрывается.

js_fetch_trusted_certificate#

Синтаксис

js_fetch_trusted_certificate файл;

По умолчанию

Контекст

http, server, location

Задает файл с доверенными сертификатами CA в формате PEM, используемыми при проверке HTTPS-сертификата при помощи Fetch API.

js_fetch_verify#

Синтаксис

js_fetch_verify on | off;

По умолчанию

js_fetch_verify on;

Контекст

http, server, location

Разрешает или запрещает проверку сертификата HTTPS-сервера при помощи Fetch API.

js_fetch_verify_depth#

Синтаксис

js_fetch_verify_depth число;

По умолчанию

js_fetch_verify_depth 100;

Контекст

http, server, location

Устанавливает глубину проверки в цепочке HTTPS-сертификатов при помощи Fetch API.

js_header_filter#

Синтаксис

js_header_filter функция | модуль.функция;

По умолчанию

Контекст

location, if in location, limit_except

Задает функцию njs в качестве фильтра заголовка ответа. Директива позволяет менять произвольные поля заголовка ответа.

Примечание

Так как обработчик js_header_filter должен сразу возвращать результат, то поддерживаются только синхронные операции. Таким образом, асинхронные операции, например r.subrequest() или setTimeout(), не поддерживаются.

js_import#

Синтаксис

js_import модуль.js | имя_экспорта from модуль.js;

По умолчанию

Контекст

http, server, location

Импортирует модуль, позволяющий задавать обработчики location и переменных на njs. Имя_экспорта является пространством имен при доступе к функциям модуля. Если имя_экспорта не задано, то пространством имен будет являться имя модуля.

js_import http.js;

В примере при доступе к экспорту в качестве пространства имен используется имя модуля http. Если импортируемый модуль экспортирует foo(), то для доступа используется http.foo.

Директив js_import может быть несколько.

js_path#

Синтаксис

js_path путь;

По умолчанию

Контекст

http, server, location

Задает дополнительный путь для модулей njs.

js_preload_object#

Синтаксис

js_preload_object имя.json | имя from файл.json;

По умолчанию

Контекст

http, server, location

Предварительно загружает неизменяемый объект во время конфигурации. Имя используется в качестве имени глобальной переменной, через которую объект доступен в коде njs. Если имя не указано, то будет использоваться имя файла.

js_preload_object map.json;

В примере map используется в качестве имени во время доступа к предварительно загруженному объекту.

Директив js_preload_object может быть несколько.

js_set#

Синтаксис

js_set $переменная функция | модуль.функция;

По умолчанию

Контекст

http, server, location

Задает функцию njs для указанной переменной. Можно ссылаться на функцию модуля.

Функция вызывается в момент первого обращения к переменной для данного запроса. Точный момент вызова функции зависит от фазы, в которой происходит обращение к переменной. Это можно использовать для реализации дополнительной логики, не относящейся к вычислению переменной. Например, если переменная указана в директиве log_format, то ее обработчик не будет выполняться до фазы записи в лог. Этот обработчик также может использоваться для выполнения процедур непосредственно перед освобождением запроса.

Примечание

Так как обработчик js_set должен сразу возвращать результат, то поддерживаются только синхронные операции. Таким образом, асинхронные операции, например r.subrequest() или setTimeout(), не поддерживаются.

js_shared_dict_zone#

Синтаксис

js_shared_dict_zone zone=имя:размер [timeout=время] [type=string | number] [evict];

По умолчанию

Контекст

http

Задает имя и размер зоны разделяемой памяти, в которой хранится словарь ключей и значений, разделяемый между рабочими процессами.

type

необязательный параметр, позволяет изменить тип значения на числовой (number),
по умолчанию в качестве ключа и значения используется строка (string).

timeout

необязательный параметр, задает время, по завершении которого все записи в словаре удаляются из зоны

evict

необязательный параметр, удаляет самую старую пару ключ-значение при переполнении зоны

Пример:

example.conf:
    # Создается словарь размером 1Мб со строковыми значениями,
    # пары ключ-значение удаляются при отсутствии активности в течение 60 секунд:
    js_shared_dict_zone zone=foo:1M timeout=60s;

    # Создается словарь размером 512Кб со строковыми значениями,
    # удаляется самая старая пара ключ-значение при переполнении зоны:
    js_shared_dict_zone zone=bar:512K timeout=30s evict;

    # Создается постоянный словарь размером 32Кб с числовыми значениями:
    js_shared_dict_zone zone=num:32k type=number;
example.js:
    function get(r) {
        r.return(200, ngx.shared.foo.get(r.args.key));
    }

    function set(r) {
        r.return(200, ngx.shared.foo.set(r.args.key, r.args.value));
    }

    function delete(r) {
        r.return(200, ngx.shared.bar.delete(r.args.key));
    }

    function increment(r) {
        r.return(200, ngx.shared.num.incr(r.args.key, 2));
    }

js_var#

Синтаксис

js_var $переменная [значение];

По умолчанию

Контекст

http, server, location

Объявляет перезаписываемую переменную. В качестве значения можно использовать текст, переменные и их комбинации. Переменная не перезаписывается после перенаправления, в отличие от переменных, созданных при помощи директивы set.

Аргумент запроса#

Каждый HTTP-обработчик njs получает один аргумент, объект запроса.