Модуль ngx_http_js_module

Этот перевод может быть устаревшим. Смотрите английскую версию для ознакомления с последними изменениями.
Пример конфигурации
Директивы
     js_body_filter
     js_content
     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_header_filter
     js_import
     js_include
     js_path
     js_periodic
     js_preload_object
     js_set
     js_shared_dict_zone
     js_var
Аргумент запроса

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

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

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

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

http {
    js_import http.js;

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

    resolver 10.0.0.1;

    server {
        listen 8000;

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

        location = /summary {
            return 200 $summary;
        }

        location = /hello {
            js_content http.hello;
        }

        # начиная с версии 0.7.0
        location = /fetch {
            js_content                   http.fetch;
            js_fetch_trusted_certificate /path/to/ISRG_Root_X1.pem;
        }

        # начиная с версии 0.7.0
        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!");
}

// начиная с версии 0.7.0
async function fetch(r) {
    let results = await Promise.all([ngx.fetch('https://nginx.org/'),
                                     ngx.fetch('https://nginx.org/en/')]);

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

// начиная с версии 0.7.0
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 функция | модуль.функция [buffer_type=string | buffer];
Умолчание:
Контекст: location, if in location, limit_except

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

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

r
объект HTTP request
data
входящий блок данных может быть строкой или буфером в зависимости от значения buffer_type, по умолчанию является строкой. Начиная с 0.8.5, по умолчанию значение data неявно преобразуется в валидную строку UTF-8. Для бинарных данных параметр buffer_type необходимо установить в buffer.
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(), не поддерживаются.

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

Синтаксис: js_content функция | модуль.функция;
Умолчание:
Контекст: location, if in location, limit_except

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Пример:

location /fetch {
    js_fetch_keepalive 32;
    js_fetch_trusted_certificate /path/to/ISRG_Root_X1.pem;
    js_content main.fetch_handler;
}

Синтаксис: js_fetch_keepalive_requests число;
Умолчание:
js_fetch_keepalive_requests 1000;
Контекст: http, server, location

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

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

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

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

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

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

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

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

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

Синтаксис: js_header_filter функция | модуль.функция;
Умолчание:
Контекст: location, if in location, limit_except

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

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

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

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

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

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

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

js_import http.js;

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

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

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

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

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

nginx.conf:
js_include http.js;
location   /version {
    js_content version;
}

http.js:
function version(r) {
    r.return(200, njs.version);
}

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

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

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

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

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

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

Эта директива появилась в версии 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_preload_object имя.json | имя from файл.json;
Умолчание:
Контекст: http, server, location

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

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

js_preload_object map.json;

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

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

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

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

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

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

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

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

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

Эта директива появилась в версии 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 $переменная [значение];
Умолчание:
Контекст: http, server, location

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

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

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

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

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