Модуль ngx_stream_js_module

Этот перевод может быть устаревшим. Смотрите английскую версию для ознакомления с последними изменениями.
Пример конфигурации
Директивы
     js_access
     js_context_reuse
     js_engine
     js_fetch_buffer_size
     js_fetch_ciphers
     js_fetch_max_response_buffer_size
     js_fetch_protocols
     js_fetch_timeout
     js_fetch_trusted_certificate
     js_fetch_verify
     js_fetch_verify_depth
     js_fetch_keepalive
     js_fetch_keepalive_requests
     js_fetch_keepalive_time
     js_fetch_keepalive_timeout
     js_filter
     js_import
     js_include
     js_preload_object
     js_preread
     js_path
     js_periodic
     js_set
     js_shared_dict_zone
     js_var
Свойства объекта сессии

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

Инструкция по сборке и установке доступны здесь.

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

Пример работает начиная с версии 0.4.0.

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 функция | модуль.функция;
Умолчание:
Контекст: stream, server

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

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

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

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

Синтаксис: js_context_reuse число;
Умолчание:
js_context_reuse 128;
Контекст: stream, server

Эта директива появилась в версии 0.8.6.

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

Синтаксис: js_engine njs | qjs;
Умолчание:
js_engine njs;
Контекст: stream, server

Эта директива появилась в версии 0.8.6.

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

Синтаксис: js_fetch_buffer_size размер;
Умолчание:
js_fetch_buffer_size 16k;
Контекст: stream, server

Эта директива появилась в версии 0.7.4.

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

Синтаксис: js_fetch_ciphers шифры;
Умолчание:
js_fetch_ciphers HIGH:!aNULL:!MD5;
Контекст: stream, server

Эта директива появилась в версии 0.7.0.

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

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

Синтаксис: js_fetch_max_response_buffer_size размер;
Умолчание:
js_fetch_max_response_buffer_size 1m;
Контекст: stream, server

Эта директива появилась в версии 0.7.4.

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

Синтаксис: js_fetch_protocols [TLSv1] [TLSv1.1] [TLSv1.2] [TLSv1.3];
Умолчание:
js_fetch_protocols TLSv1 TLSv1.1 TLSv1.2;
Контекст: stream, server

Эта директива появилась в версии 0.7.0.

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

Синтаксис: js_fetch_timeout время;
Умолчание:
js_fetch_timeout 60s;
Контекст: stream, server

Эта директива появилась в версии 0.7.4.

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

Синтаксис: js_fetch_trusted_certificate файл;
Умолчание:
Контекст: stream, server

Эта директива появилась в версии 0.7.0.

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

Синтаксис: js_fetch_verify on | off;
Умолчание:
js_fetch_verify on;
Контекст: stream, server

Эта директива появилась в версии 0.7.4.

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

Синтаксис: js_fetch_verify_depth число;
Умолчание:
js_fetch_verify_depth 100;
Контекст: stream, server

Эта директива появилась в версии 0.7.0.

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

Синтаксис: js_fetch_keepalive соединения;
Умолчание:
js_fetch_keepalive 0;
Контекст: stream, server

Эта директива появилась в версии 0.9.2.

Активирует кэш для соединений с серверами назначения. Если значение больше 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 1000;
Контекст: stream, server

Эта директива появилась в версии 0.9.2.

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

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

Синтаксис: js_fetch_keepalive_time время;
Умолчание:
js_fetch_keepalive_time 1h;
Контекст: stream, server

Эта директива появилась в версии 0.9.2.

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

Синтаксис: js_fetch_keepalive_timeout время;
Умолчание:
js_fetch_keepalive_timeout 60s;
Контекст: stream, server

Эта директива появилась в версии 0.9.2.

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

Синтаксис: js_filter функция | модуль.функция;
Умолчание:
Контекст: stream, server

Задаёт фильтр данных. Начиная с 0.4.0 можно ссылаться на функцию модуля. Функция фильтра вызывается однократно при первом достижении сессией content-фазы.

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

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

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

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

Синтаксис: js_import модуль.js | имя_экспорта from модуль.js;
Умолчание:
Контекст: stream, server

Эта директива появилась в версии 0.4.0.

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

js_import stream.js;

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

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

Директива может быть указана на уровне server начиная с 0.7.7.

Синтаксис: js_include файл;
Умолчание:
Контекст: stream

Задаёт файл, который позволяет задавать обработчики server и переменных на njs:

nginx.conf:
js_include stream.js;
js_set     $js_addr address;
server {
    listen 127.0.0.1:12345;
    return $js_addr;
}

stream.js:
function address(s) {
    return s.remoteAddress;
}

Директива устарела в версии 0.4.0 и была удалена в версии 0.7.1. Вместо неё следует использовать директиву js_import.

Синтаксис: js_preload_object имя.json | имя from файл.json;
Умолчание:
Контекст: stream, server

Эта директива появилась в версии 0.7.8.

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

js_preload_object map.json;

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

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

Синтаксис: js_preread функция | модуль.функция;
Умолчание:
Контекст: stream, server

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

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

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

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

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

Синтаксис: js_path путь;
Умолчание:
Контекст: stream, server

Эта директива появилась в версии 0.3.0.

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

Директива может быть указана на уровне server начиная с 0.7.7.

Синтаксис: js_periodic функция | модуль.функция [interval=время] [jitter=число] [worker_affinity=маска];
Умолчание:
Контекст: server

Эта директива появилась в версии 0.8.1.

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

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

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

По умолчанию 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://nginx.org/en/docs/njs/');
    let body = await reply.text();

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

Синтаксис: js_set $переменная функция | модуль.функция [nocache];
Умолчание:
Контекст: stream, server

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

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

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

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

Директива может быть указана на уровне server начиная с 0.7.7.

Синтаксис: js_shared_dict_zone zone=имя:размер [timeout=время] [type=строка|число] [evict];
Умолчание:
Контекст: stream

Эта директива появилась в версии 0.8.0.

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

По умолчанию в качестве ключа и значения используется строка. Необязательный параметр type позволяет изменить тип значения на число.

Необязательный параметр timeout задаёт время в миллисекундах, по завершении которого все записи в словаре удаляются из зоны. Если для части записей требуется другое время удаления, его можно задать при помощи аргумента timeout методов add, incr и set (0.8.5).

Необязательный параметр 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 del(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 $переменная [значение];
Умолчание:
Контекст: stream, server

Эта директива появилась в версии 0.5.3.

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

Директива может быть указана на уровне server начиная с 0.7.7.

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

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