JS#

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

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

Примечание

Также доступна упрощенная версия пакета под названием ...-njs-light; однако она несовместима с обычной версией и не может использоваться одновременно с ней.

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

stream {
    js_import stream.js;

    js_set $bar stream.bar;
    js_set $req_line stream.req_line;

    server {
        listen 12345;

        js_preread stream.preread;
        return     $req_line;
    }

    server {
        listen 12346;

        js_access  stream.access;
        proxy_pass 127.0.0.1:8000;
        js_filter  stream.header_inject;
    }
}

http {
    server {
        listen 8000;
        location / {
            return 200 $http_foo\n;
        }
    }
}

Файл stream.js:

var line = '';

function bar(s) {
    var v = s.variables;
    s.log("hello from bar() handler!");
    return "bar-var" + v.remote_port + "; pid=" + v.pid;
}

function preread(s) {
    s.on('upload', function (data, flags) {
        var n = data.indexOf('\n');
        if (n != -1) {
            line = data.substr(0, n);
            s.done();
        }
    });
}

function req_line(s) {
    return line;
}

// Чтение строки HTTP-запроса.
// Получение байт в 'req' до того как
// будет прочитана строка запроса.
// Добавление HTTP-заголовка в запрос клиента

var my_header =  'Foo: foo';
function header_inject(s) {
    var req = '';
    s.on('upload', function(data, flags) {
        req += data;
        var n = req.search('\n');
        if (n != -1) {
            var rest = req.substr(n + 1);
            req = req.substr(0, n + 1);
            s.send(req + my_header + '\r\n' + rest, flags);
            s.off('upload');
        }
    });
}

function access(s) {
    if (s.remoteAddress.match('^192.*')) {
        s.deny();
        return;
    }

    s.allow();
}

export default {bar, preread, req_line, header_inject, access};

Директивы#

js_access#

Синтаксис

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

По умолчанию

Контекст

stream, server

Задает функцию njs, которая будет вызываться в access-фазе. Можно ссылаться на функцию модуля.

Функция вызывается однократно при первом достижении сессией access-фазе. Функция вызывается со следующими аргументами:

s

объект stream-сессии

В этой фазе может происходить инициализация, также при помощи метода s.on() может регистрироваться вызов для каждого входящего блока данных пока не будет вызван один из методов: s.done(), s.decline(), s.allow(). При вызове любого из этих методов обработка сессии переходит на следующую фазу и все текущие вызовы s.on() сбрасываются.

js_context_reuse#

Синтаксис

js_context_reuse число;

По умолчанию

js_context_reuse 128;

Контекст

stream, server

Устанавливает максимальное количество контекстов JS для повторного использования движком QuickJS. Каждый контекст используется для одной потоковой сессии. Завершенный контекст помещается в пул повторно используемых контекстов. Если пул заполнен, контекст уничтожается.

js_engine#

Синтаксис

js_engine njs | qjs;

По умолчанию

js_engine njs;

Контекст

stream, server

Устанавливает движок JavaScript для использования в скриптах njs. Параметр njs задает движок njs, также используемый по умолчанию. Параметр qjs задает движок QuickJS.

js_fetch_buffer_size#

Синтаксис

js_fetch_buffer_size размер;

По умолчанию

js_fetch_buffer_size 16k;

Контекст

stream, server

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

js_fetch_ciphers#

Синтаксис

js_fetch_ciphers шифры;

По умолчанию

js_fetch_ciphers HIGH:!aNULL:!MD5;

Контекст

stream, server

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

Список шифров зависит от установленной версии OpenSSL. Полный список можно посмотреть с помощью команды openssl ciphers.

js_fetch_max_response_buffer_size#

Синтаксис

js_fetch_max_response_buffer_size размер;

По умолчанию

js_fetch_max_response_buffer_size 1m;

Контекст

stream, server

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

js_fetch_protocols#

Синтаксис

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

По умолчанию

js_fetch_protocols TLSv1 TLSv1.1 TLSv1.2;

Контекст

stream, server

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

js_fetch_timeout#

Синтаксис

js_fetch_timeout время;

По умолчанию

js_fetch_timeout 60s;

Контекст

stream, server

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

js_fetch_trusted_certificate#

Синтаксис

js_fetch_trusted_certificate файл;

По умолчанию

Контекст

stream, server

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

js_fetch_verify#

Синтаксис

js_fetch_verify on | off;

По умолчанию

js_fetch_verify on;

Контекст

stream, server

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

js_fetch_verify_depth#

Синтаксис

js_fetch_verify_depth число;

По умолчанию

js_fetch_verify_depth 100;

Контекст

stream, server

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

js_fetch_keepalive#

Синтаксис

js_fetch_keepalive соединения;

По умолчанию

js_fetch_keepalive 0;

Контекст

stream, server

Активирует кэш соединений к серверам назначения. Когда значение больше 0, включает keepalive-соединения для Fetch API.

Параметр соединения задает максимальное количество неактивных keepalive-соединений к серверам назначения, которые сохраняются в кэше каждого рабочего процесса. Когда это число превышено, закрываются наименее недавно использованные соединения.

Пример:

server {
    listen 12345;
    js_fetch_keepalive 32;
    js_fetch_trusted_certificate /path/to/ISRG_Root_X1.pem;
    js_preread main.fetch_handler;
}

js_fetch_keepalive_requests#

Синтаксис

js_fetch_keepalive_requests число;

По умолчанию

js_fetch_keepalive_requests 1000;

Контекст

stream, server

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

Периодическое закрытие соединений необходимо для освобождения выделенной памяти для каждого соединения. Поэтому использование слишком большого максимального количества запросов может привести к чрезмерному использованию памяти и не рекомендуется.

js_fetch_keepalive_time#

Синтаксис

js_fetch_keepalive_time время;

По умолчанию

js_fetch_keepalive_time 1h;

Контекст

stream, server

Ограничивает максимальное время, в течение которого запросы могут обрабатываться через одно keepalive-соединение с Fetch API. По достижении этого времени соединение закрывается после обработки следующего запроса.

js_fetch_keepalive_timeout#

Синтаксис

js_fetch_keepalive_timeout время;

По умолчанию

js_fetch_keepalive_timeout 60s;

Контекст

stream, server

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

js_filter#

Синтаксис

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

По умолчанию

Контекст

stream, server

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

Функция фильтра вызывается однократно при первом достижении сессией content-фазы. Функция фильтра вызывается со следующими аргументами:

s

объект stream-сессии

В этой фазе может происходить инициализация, также при помощи метода s.on() может регистрироваться вызов для каждого входящего блока данных. Для отмены регистрации вызова и отмены фильтра можно использовать метод s.off().

Примечание

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

js_import#

Синтаксис

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

По умолчанию

Контекст

stream, server

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

js_import stream.js;

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

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

js_path#

Синтаксис

js_path путь;

По умолчанию

Контекст

stream, server

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

js_periodic#

Синтаксис

js_periodic модуль.функция [interval=\ время] [jitter=\ число] [worker_affinity=\ маска];

По умолчанию

Контекст

server

Задает обработчик содержимого для запуска через регулярные интервалы. Обработчик получает объект сеанса в качестве первого аргумента, он также имеет доступ к глобальным объектам, таким как ngx.

Необязательный параметр interval задает интервал между двумя последовательными запусками, по умолчанию — 5 секунд.

Необязательный параметр jitter задает время, в течение которого обработчик содержимого location будет случайно задержан, по умолчанию задержки нет.

По умолчанию js_handler выполняется в рабочем процессе 0. Необязательный параметр worker_affinity позволяет указать определенные рабочие процессы, в которых должен выполняться обработчик содержимого location. Каждый набор рабочих процессов представлен битовой маской разрешенных рабочих процессов. Маска all позволяет выполнять обработчик во всех рабочих процессах.

Пример:

example.conf:

location @periodics {
    # запуск с интервалом в 1 минуту в рабочем процессе 0
    js_periodic main.handler interval=60s;

    # запуск с интервалом в 1 минуту во всех рабочих процессах
    js_periodic main.handler interval=60s worker_affinity=all;

    # запуск с интервалом в 1 минуту в рабочих процессах 1 и 3
    js_periodic main.handler interval=60s worker_affinity=0101;

    resolver 10.0.0.1;
    js_fetch_trusted_certificate /path/to/ISRG_Root_X1.pem;
}
example.js:

async function handler(s) {
    let reply = await ngx.fetch('https://example.com/');
    let body = await reply.text();

    ngx.log(ngx.INFO, body);
}

js_preload_object#

Синтаксис

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

По умолчанию

Контекст

stream, server

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

js_preload_object map.json;

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

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

js_preread#

Синтаксис

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

По умолчанию

Контекст

stream, server

Задает функцию njs, которая будет вызываться в preread-фазе. Можно ссылаться на функцию модуля.

Функция вызывается однократно при первом достижении сессией preread-фазы. Функция вызывается со следующими аргументами:

s

объект stream-сессии

В этой фазе может происходить инициализация, также при помощи метода s.on() может регистрироваться вызов для каждого входящего блока данных пока не будет вызван один из методов: s.done(), s.decline(), s.allow(). При вызове любого из этих методов обработка сессии переходит на следующую фазу и все текущие вызовы s.on() сбрасываются.

Примечание

Так как обработчик js_preread должен сразу возвращать результат, то поддерживаются только синхронные операции. Таким образом, асинхронные операции, например ngx.fetch() или setTimeout(), не поддерживаются. Тем не менее асинхронные операции поддерживаются в вызовах s.on() в preread-фазе.

js_set#

Синтаксис

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

По умолчанию

Контекст

stream, server

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

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

Начиная с njs 0.8.6, если указан необязательный аргумент nocache, обработчик вызывается каждый раз при обращении к нему. Из-за текущих ограничений модуля rewrite, когда переменная nocache используется директивой set, ее обработчик должен всегда возвращать значение фиксированной длины.

Примечание

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

js_shared_dict_zone#

Синтаксис

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

По умолчанию

Контекст

stream

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

type

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

timeout

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

evict

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

state

необязательный параметр, указывает файл, который хранит состояние разделяемого словаря в формате JSON и делает его постоянным при перезапусках nginx

Пример:

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;

# Создается словарь размером 1Мб со строковыми значениями и постоянным состоянием:
js_shared_dict_zone zone=persistent:1M state=/tmp/dict.json;
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 $переменная [значение];

По умолчанию

Контекст

stream, server

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

Свойства объекта сессии#

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