nginx-1.14.0/ 000755 001751 001751 00000000000 13265410502 014104 5 ustar 00mdounin mdounin 000000 000000 nginx-1.14.0/auto/ 000755 001751 001751 00000000000 13265410475 015065 5 ustar 00mdounin mdounin 000000 000000 nginx-1.14.0/conf/ 000755 001751 001751 00000000000 13265410474 015041 5 ustar 00mdounin mdounin 000000 000000 nginx-1.14.0/contrib/ 000755 001751 001751 00000000000 13265410474 015554 5 ustar 00mdounin mdounin 000000 000000 nginx-1.14.0/src/ 000755 001751 001751 00000000000 13265410475 014704 5 ustar 00mdounin mdounin 000000 000000 nginx-1.14.0/configure 000755 001751 001751 00000004706 13265410474 016032 0 ustar 00mdounin mdounin 000000 000000 #!/bin/sh
# Copyright (C) Igor Sysoev
# Copyright (C) Nginx, Inc.
LC_ALL=C
export LC_ALL
. auto/options
. auto/init
. auto/sources
test -d $NGX_OBJS || mkdir -p $NGX_OBJS
echo > $NGX_AUTO_HEADERS_H
echo > $NGX_AUTOCONF_ERR
echo "#define NGX_CONFIGURE \"$NGX_CONFIGURE\"" > $NGX_AUTO_CONFIG_H
if [ $NGX_DEBUG = YES ]; then
have=NGX_DEBUG . auto/have
fi
if test -z "$NGX_PLATFORM"; then
echo "checking for OS"
NGX_SYSTEM=`uname -s 2>/dev/null`
NGX_RELEASE=`uname -r 2>/dev/null`
NGX_MACHINE=`uname -m 2>/dev/null`
echo " + $NGX_SYSTEM $NGX_RELEASE $NGX_MACHINE"
NGX_PLATFORM="$NGX_SYSTEM:$NGX_RELEASE:$NGX_MACHINE";
case "$NGX_SYSTEM" in
MINGW32_* | MINGW64_* | MSYS_*)
NGX_PLATFORM=win32
;;
esac
else
echo "building for $NGX_PLATFORM"
NGX_SYSTEM=$NGX_PLATFORM
fi
. auto/cc/conf
if [ "$NGX_PLATFORM" != win32 ]; then
. auto/headers
fi
. auto/os/conf
if [ "$NGX_PLATFORM" != win32 ]; then
. auto/unix
fi
. auto/threads
. auto/modules
. auto/lib/conf
case ".$NGX_PREFIX" in
.)
NGX_PREFIX=${NGX_PREFIX:-/usr/local/nginx}
have=NGX_PREFIX value="\"$NGX_PREFIX/\"" . auto/define
;;
.!)
NGX_PREFIX=
;;
*)
have=NGX_PREFIX value="\"$NGX_PREFIX/\"" . auto/define
;;
esac
if [ ".$NGX_CONF_PREFIX" != "." ]; then
have=NGX_CONF_PREFIX value="\"$NGX_CONF_PREFIX/\"" . auto/define
fi
have=NGX_SBIN_PATH value="\"$NGX_SBIN_PATH\"" . auto/define
have=NGX_CONF_PATH value="\"$NGX_CONF_PATH\"" . auto/define
have=NGX_PID_PATH value="\"$NGX_PID_PATH\"" . auto/define
have=NGX_LOCK_PATH value="\"$NGX_LOCK_PATH\"" . auto/define
have=NGX_ERROR_LOG_PATH value="\"$NGX_ERROR_LOG_PATH\"" . auto/define
have=NGX_HTTP_LOG_PATH value="\"$NGX_HTTP_LOG_PATH\"" . auto/define
have=NGX_HTTP_CLIENT_TEMP_PATH value="\"$NGX_HTTP_CLIENT_TEMP_PATH\""
. auto/define
have=NGX_HTTP_PROXY_TEMP_PATH value="\"$NGX_HTTP_PROXY_TEMP_PATH\""
. auto/define
have=NGX_HTTP_FASTCGI_TEMP_PATH value="\"$NGX_HTTP_FASTCGI_TEMP_PATH\""
. auto/define
have=NGX_HTTP_UWSGI_TEMP_PATH value="\"$NGX_HTTP_UWSGI_TEMP_PATH\""
. auto/define
have=NGX_HTTP_SCGI_TEMP_PATH value="\"$NGX_HTTP_SCGI_TEMP_PATH\""
. auto/define
. auto/make
. auto/lib/make
. auto/install
# STUB
. auto/stubs
have=NGX_USER value="\"$NGX_USER\"" . auto/define
have=NGX_GROUP value="\"$NGX_GROUP\"" . auto/define
if [ ".$NGX_BUILD" != "." ]; then
have=NGX_BUILD value="\"$NGX_BUILD\"" . auto/define
fi
. auto/summary
nginx-1.14.0/LICENSE 000644 001751 001751 00000002565 13265410474 015131 0 ustar 00mdounin mdounin 000000 000000 /*
* Copyright (C) 2002-2018 Igor Sysoev
* Copyright (C) 2011-2018 Nginx, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
nginx-1.14.0/README 000644 001751 001751 00000000061 13265410474 014771 0 ustar 00mdounin mdounin 000000 000000
Documentation is available at http://nginx.org
nginx-1.14.0/html/ 000755 001751 001751 00000000000 13265410475 015061 5 ustar 00mdounin mdounin 000000 000000 nginx-1.14.0/man/ 000755 001751 001751 00000000000 13265410475 014670 5 ustar 00mdounin mdounin 000000 000000 nginx-1.14.0/CHANGES.ru 000644 001751 001751 00001526046 13265410500 015540 0 ustar 00mdounin mdounin 000000 000000
Изменения в nginx 1.14.0 17.04.2018
*) Стабильная ветка 1.14.x.
Изменения в nginx 1.13.12 10.04.2018
*) Исправление: при возврате большого ответа соединения с gRPC-бэкендами
могли неожиданно закрываться.
Изменения в nginx 1.13.11 03.04.2018
*) Добавление: параметр proxy_protocol директивы listen теперь
поддерживает протокол PROXY версии 2.
*) Исправление: nginx не собирался с OpenSSL 1.1.1 статически на Linux.
*) Исправление: в параметрах http_404, http_500 и им подобных директивы
proxy_next_upstream.
Изменения в nginx 1.13.10 20.03.2018
*) Добавление: теперь параметр set в SSI-директиве include позволяет
сохранять в переменную любые ответы; максимальный размер ответа
задаётся директивой subrequest_output_buffer_size.
*) Добавление: теперь nginx использует вызов
clock_gettime(CLOCK_MONOTONIC), если он доступен, что позволяет
избежать некорректного срабатывания таймаутов при изменениях
системного времени.
*) Добавление: параметр "escape=none" директивы log_format.
Спасибо Johannes Baiter и Calin Don.
*) Добавление: переменная $ssl_preread_alpn_protocols в модуле
ngx_stream_ssl_preread_module.
*) Добавление: модуль ngx_http_grpc_module.
*) Исправление: в обработке ошибок выделения памяти в директиве geo.
*) Исправление: при использовании переменных в директиве
auth_basic_user_file в лог мог выводиться символ '\0'.
Спасибо Вадиму Филимонову.
Изменения в nginx 1.13.9 20.02.2018
*) Добавление: поддержка HTTP/2 server push; директивы http2_push и
http2_push_preload.
*) Исправление: при использовании кэша в логах могли появляться
сообщения "header already sent"; ошибка появилась в 1.9.13.
*) Исправление: при использовании директивы ssl_verify_client в рабочем
процессе мог произойти segmentation fault, если в виртуальном сервере
не был указан SSL-сертификат.
*) Исправление: в модуле ngx_http_v2_module.
*) Исправление: в модуле ngx_http_dav_module.
Изменения в nginx 1.13.8 26.12.2017
*) Добавление: теперь при использовании параметра transparent директив
proxy_bind, fastcgi_bind, memcached_bind, scgi_bind и uwsgi_bind
nginx автоматически сохраняет capability CAP_NET_RAW в рабочих
процессах.
*) Добавление: улучшения в определении размера строки кэша процессора.
Спасибо Debayan Ghosh.
*) Добавление: новые директивы в скриптах подсветки синтаксиса для vim.
Спасибо Геннадию Махомеду.
*) Исправление: процедура обновления исполняемого файла не работала,
если после завершения родительского процесса новым родительским
процессом nginx'а становился процесс с PID, отличным от 1.
*) Исправление: модуль ngx_http_autoindex_module неправильно обрабатывал
запросы с телом.
*) Исправление: в директиве proxy_limit_rate при использовании с
директивой keepalive.
*) Исправление: при использовании "proxy_buffering off" часть ответа
могла буферизироваться, если клиентское соединение использовало SSL.
Спасибо Patryk Lesiewicz.
*) Исправление: в директиве proxy_cache_background_update.
*) Исправление: переменную вида "${name}" с именем в фигурных скобках
нельзя было использовать в начале параметра не заключив весь параметр
в кавычки.
Изменения в nginx 1.13.7 21.11.2017
*) Исправление: в переменной $upstream_status.
*) Исправление: в рабочем процессе мог произойти segmentation fault,
если бэкенд возвращал ответ "101 Switching Protocols" на подзапрос.
*) Исправление: если при переконфигурации изменялся размер зоны
разделяемой памяти и переконфигурация завершалась неудачно, то в
главном процессе происходил segmentation fault.
*) Исправление: в модуле ngx_http_fastcgi_module.
*) Исправление: nginx возвращал ошибку 500, если в директиве
xslt_stylesheet были заданы параметры без использования переменных.
*) Изменение: при использовании варианта библиотеки zlib от Intel в лог
писались сообщения "gzip filter failed to use preallocated memory".
*) Исправление: директива worker_shutdown_timeout не работала при
использовании почтового прокси-сервера и при проксировании
WebSocket-соединений.
Изменения в nginx 1.13.6 10.10.2017
*) Исправление: при использовании директивы ssl_preread в модуле stream
не работало переключение на следующий бэкенд.
*) Исправление: в модуле ngx_http_v2_module.
Спасибо Piotr Sikora.
*) Исправление: nginx не поддерживал даты после 2038 года на 32-битных
платформах с 64-битным time_t.
*) Исправление: в обработке дат до 1970 года и после 10000 года.
*) Исправление: в модуле stream таймауты ожидания UDP-пакетов от
бэкендов не логгировались или логгировались на уровне info вместо
error.
*) Исправление: при использовании HTTP/2 nginx мог вернуть ошибку 400,
не указав в логе причину.
*) Исправление: в обработке повреждённых файлов кэша.
*) Исправление: при кэшировании ошибок, перехваченных error_page, не
учитывались заголовки управления кэшированием.
*) Исправление: при использовании HTTP/2 тело запроса могло быть
повреждено.
*) Исправление: в обработке адресов клиентов при использовании unix
domain сокетов.
*) Исправление: при использовании директивы "hash ... consistent" в
блоке upstream nginx нагружал процессор, если использовались большие
веса и все или почти все бэкенды были недоступны.
Изменения в nginx 1.13.5 05.09.2017
*) Добавление: переменная $ssl_client_escaped_cert.
*) Исправление: директива ssl_session_ticket_key и параметр include
директивы geo не работали на Windows.
*) Исправление: на 32-битных платформах при запросе более 4 гигабайт с
помощью нескольких диапазонов возвращалась некорректная длина ответа.
*) Исправление: директива "expires modified" и обработка строки If-Range
заголовка запроса не учитывали время последнего изменения ответа,
если использовалось проксирование без кэширования.
Изменения в nginx 1.13.4 08.08.2017
*) Добавление: модуль ngx_http_mirror_module.
*) Исправление: клиентские соединения могли сбрасываться при
тестировании конфигурации, если использовался параметр reuseport
директивы listen на Linux.
*) Исправление: тело запроса могло быть недоступно в подзапросах, если
оно было сохранено в файл и использовалось проксирование.
*) Исправление: очистка кэша по max_size не работала на Windows.
*) Исправление: любое выделение разделяемой памяти на Windows требовало
4096 байт памяти.
*) Исправление: при использовании директивы zone в блоке upstream на
Windows рабочий процесс мог завершаться аварийно.
Изменения в nginx 1.13.3 11.07.2017
*) Безопасность: специально созданный запрос мог вызвать целочисленное
переполнение в range-фильтре и последующую некорректную обработку
запрошенных диапазонов, что потенциально могло привести к утечке
конфиденциальной информации (CVE-2017-7529).
Изменения в nginx 1.13.2 27.06.2017
*) Изменение: теперь при запросе диапазона, начинающегося с 0, из
пустого файла nginx возвращает ответ 200 вместо 416.
*) Добавление: директива add_trailer.
Спасибо Piotr Sikora.
*) Исправление: nginx не собирался под Cygwin и NetBSD; ошибка появилась
в 1.13.0.
*) Исправление: nginx не собирался под MSYS2 / MinGW 64-bit.
Спасибо Orgad Shaneh.
*) Исправление: при использовании SSI с большим количеством подзапросов
и proxy_pass с переменными в рабочем процессе мог произойти
segmentation fault.
*) Исправление: в модуле ngx_http_v2_module.
Спасибо Piotr Sikora.
Изменения в nginx 1.13.1 30.05.2017
*) Добавление: теперь в качестве параметра директивы set_real_ip_from
можно указывать имя хоста.
*) Добавление: улучшения в скриптах подсветки синтаксиса для vim.
*) Добавление: директива worker_cpu_affinity теперь работает на
DragonFly BSD.
Спасибо Sepherosa Ziehau.
*) Исправление: SSL renegotiation в соединениях к бэкендам не работал
при использовании OpenSSL до 1.1.0.
*) Изменение: nginx не собирался с Oracle Developer Studio 12.5.
*) Изменение: теперь cache manager пропускает заблокированные записи при
очистке кэша по max_size.
*) Исправление: клиентские SSL-соединения сразу закрывались, если
использовался отложенный accept и параметр proxy_protocol директивы
listen.
*) Исправление: в директиве proxy_cache_background_update.
*) Изменение: теперь директива tcp_nodelay устанавливает опцию
TCP_NODELAY перед SSL handshake.
Изменения в nginx 1.13.0 25.04.2017
*) Изменение: теперь SSL renegotiation допускается в соединениях к
бэкендам.
*) Добавление: параметры rcvbuf и sndbuf директив listen в почтовом
прокси-сервере и модуле stream.
*) Добавление: директивы return и error_page теперь могут использоваться
для возврата перенаправлений с кодом 308.
Спасибо Simon Leblanc.
*) Добавление: параметр TLSv1.3 в директиве ssl_protocols.
*) Добавление: при логгировании сигналов теперь указывается PID
отправившего сигнал процесса.
*) Исправление: в обработке ошибок выделения памяти.
*) Исправление: если сервер в модуле stream слушал на wildcard-адресе,
исходящий адрес ответного UDP-пакета мог отличаться от адреса
назначения исходного пакета.
Изменения в nginx 1.11.13 04.04.2017
*) Добавление: параметр http_429 в директивах proxy_next_upstream,
fastcgi_next_upstream, scgi_next_upstream и uwsgi_next_upstream.
Спасибо Piotr Sikora.
*) Исправление: в обработке ошибок выделения памяти.
*) Исправление: при использовании директив sendfile и timer_resolution
на Linux запросы могли зависать.
*) Исправление: при использовании с подзапросами директив sendfile и
aio_write запросы могли зависать.
*) Исправление: в модуле ngx_http_v2_module.
Спасибо Piotr Sikora.
*) Исправление: при использовании HTTP/2 в рабочем процессе мог
произойти segmentation fault.
*) Исправление: запросы могли зависать при использовании с подзапросами
директив limit_rate, sendfile_max_chunk, limit_req или метода
$r->sleep() встроенного перла.
*) Исправление: в модуле ngx_http_slice_module.
Изменения в nginx 1.11.12 24.03.2017
*) Исправление: nginx мог нагружать процессор; ошибка появилась в
1.11.11.
Изменения в nginx 1.11.11 21.03.2017
*) Добавление: директива worker_shutdown_timeout.
*) Добавление: улучшения в скриптах подсветки синтаксиса для vim.
Спасибо Wei-Ko Kao.
*) Исправление: при попытке установить переменную $limit_rate в пустую
строку в рабочем процессе мог произойти segmentation fault.
*) Исправление: директивы proxy_cache_background_update,
fastcgi_cache_background_update, scgi_cache_background_update и
uwsgi_cache_background_update могли работать некорректно, если
использовалась директива if.
*) Исправление: в рабочем процессе мог произойти segmentation fault,
если количество large_client_header_buffers в виртуальном сервере
отличалось от такового в сервере по умолчанию.
*) Исправление: в почтовом прокси-сервере.
Изменения в nginx 1.11.10 14.02.2017
*) Изменение: формат заголовка кэша был изменен, ранее закэшированные
ответы будут загружены заново.
*) Добавление: поддержка расширений stale-while-revalidate и
stale-if-error в строке "Cache-Control" в заголовке ответа бэкенда.
*) Добавление: директивы proxy_cache_background_update,
fastcgi_cache_background_update, scgi_cache_background_update и
uwsgi_cache_background_update.
*) Добавление: теперь nginx может кэшировать ответы со строкой Vary
заголовка длиной до 128 символов (вместо 42 символов в предыдущих
версиях).
*) Добавление: параметр build директивы server_tokens.
Спасибо Tom Thorogood.
*) Исправление: при обработке запросов со строкой "Expect: 100-continue"
в заголовке запроса в логах могли появляться сообщения "[crit]
SSL_write() failed".
*) Исправление: модуль ngx_http_slice_module не работал в именованных
location'ах.
*) Исправление: при использовании AIO после перенаправления запроса с
помощью X-Accel-Redirect в рабочем процессе мог произойти
segmentation fault.
*) Исправление: уменьшено потребление памяти для долгоживущих запросов,
использующих сжатие.
Изменения в nginx 1.11.9 24.01.2017
*) Исправление: при использовании модуля stream nginx мог нагружать
процессор; ошибка появилась в 1.11.5.
*) Исправление: метод аутентификации EXTERNAL в почтовом прокси-сервере
можно было использовать, даже если он не был разрешён в конфигурации.
*) Исправление: при использовании директивы ssl_verify_client модуля
stream в рабочем процессе мог произойти segmentation fault.
*) Исправление: директива ssl_verify_client модуля stream могла не
работать.
*) Исправление: при исчерпании рабочим процессом свободных соединений
keepalive-соединения могли закрываться излишне агрессивно.
Спасибо Joel Cunningham.
*) Исправление: при использовании директивы sendfile на FreeBSD и macOS
мог возвращаться некорректный ответ; ошибка появилась в 1.7.8.
*) Исправление: при использовании директивы aio_write ответ мог
сохраняться в кэш не полностью.
*) Исправление: при использовании директивы aio_write могла происходить
утечка сокетов.
Изменения в nginx 1.11.8 27.12.2016
*) Добавление: директива absolute_redirect.
*) Добавление: параметр escape директивы log_format.
*) Добавление: проверка клиентских SSL-сертификатов в модуле stream.
*) Добавление: директива ssl_session_ticket_key поддерживает шифрование
TLS session tickets с помощью AES256 при использовании с 80-байтными
ключами.
*) Добавление: поддержка vim-commentary в скриптах для vim.
Спасибо Armin Grodon.
*) Исправление: рекурсия при получении значений переменных не
ограничивалась.
*) Исправление: в модуле ngx_stream_ssl_preread_module.
*) Исправление: если сервер, описанный в блоке upstream в модуле stream,
был признан неработающим, то после истечения fail_timeout он
признавался работающим только после завершения тестового соединения;
теперь достаточно, чтобы соединение было успешно установлено.
*) Исправление: nginx/Windows не собирался с 64-битным Visual Studio.
*) Исправление: nginx/Windows не собирался с OpenSSL 1.1.0.
Изменения в nginx 1.11.7 13.12.2016
*) Изменение: переменная $ssl_client_verify теперь в случае ошибки
проверки клиентского сертификата содержит строку с описанием ошибки,
например, "FAILED:certificate has expired".
*) Добавление: переменные $ssl_ciphers, $ssl_curves,
$ssl_client_v_start, $ssl_client_v_end и $ssl_client_v_remain.
*) Добавление: параметр volatile директивы map.
*) Исправление: при сборке динамических модулей не учитывались заданные
для модуля зависимости.
*) Исправление: при использовании HTTP/2 и директив limit_req или
auth_request тело запроса могло быть повреждено; ошибка появилась в
1.11.0.
*) Исправление: при использовании HTTP/2 в рабочем процессе мог
произойти segmentation fault; ошибка появилась в 1.11.3.
*) Исправление: в модуле ngx_http_mp4_module.
Спасибо Congcong Hu.
*) Исправление: в модуле ngx_http_perl_module.
Изменения в nginx 1.11.6 15.11.2016
*) Изменение: формат переменных $ssl_client_s_dn и $ssl_client_i_dn
изменён на соответствующий RFC 2253 (RFC 4514); значения в старом
формате доступны через переменные $ssl_client_s_dn_legacy и
$ssl_client_i_dn_legacy.
*) Изменение: при сохранении временных файлов в каталоге кэша они теперь
располагаются не в отдельном подкаталоге для временных файлов, а в
том же подкаталоге, что и соответствующие файлы в кэше.
*) Добавление: поддержка метода аутентификации EXTERNAL в почтовом
прокси-сервере.
Спасибо Robert Norris.
*) Добавление: поддержка WebP в модуле ngx_http_image_filter_module.
*) Добавление: директива proxy_method поддерживает переменные.
Спасибо Дмитрию Лазуркину.
*) Добавление: директива http2_max_requests в модуле ngx_http_v2_module.
*) Добавление: директивы proxy_cache_max_range_offset,
fastcgi_cache_max_range_offset, scgi_cache_max_range_offset и
uwsgi_cache_max_range_offset.
*) Исправление: плавное завершение старых рабочих процессов могло
занимать бесконечное время при использовании HTTP/2.
*) Исправление: в модуле ngx_http_mp4_module.
*) Исправление: при проксировании WebSocket-соединений и включённом
кэшировании в логах могли появляться сообщения "ignore long locked
inactive cache entry".
*) Исправление: если во время SSL handshake с бэкендом происходил
таймаут, nginx ничего не писал в лог и возвращал ответ с кодом 502
вместо 504.
Изменения в nginx 1.11.5 11.10.2016
*) Изменение: параметр configure --with-ipv6 упразднён, поддержка IPv6
теперь собирается автоматически.
*) Изменение: теперь, если в блоке upstream не оказалось доступных
серверов, nginx не сбрасывает статистику ошибок всех серверов, как
делал ранее, а ожидает истечения fail_timeout.
*) Добавление: модуль ngx_stream_ssl_preread_module.
*) Добавление: директива server в блоке upstream поддерживает параметр
max_conns.
*) Добавление: параметр configure --with-compat.
*) Добавление: параметры manager_files, manager_threshold и
manager_sleep директив proxy_cache_path, fastcgi_cache_path,
scgi_cache_path и uwsgi_cache_path.
*) Исправление: при сборке perl-модуля не использовались флаги, заданные
с помощью параметра configure --with-ld-opt.
*) Исправление: в директиве add_after_body при использовании совместно с
директивой sub_filter.
*) Исправление: в переменной $realip_remote_addr.
*) Исправление: директивы dav_access, proxy_store_access,
fastcgi_store_access, scgi_store_access и uwsgi_store_access
игнорировали права, заданные для пользователя.
*) Исправление: unix domain listen-сокеты могли не наследоваться при
обновлении исполняемого файла на Linux.
*) Исправление: nginx возвращал ошибку 400 на запросы с символом "-" в
HTTP-методе.
Изменения в nginx 1.11.4 13.09.2016
*) Добавление: переменная $upstream_bytes_received.
*) Добавление: переменные $bytes_received, $session_time, $protocol,
$status, $upstream_addr, $upstream_bytes_sent,
$upstream_bytes_received, $upstream_connect_time,
$upstream_first_byte_time и $upstream_session_time в модуле stream.
*) Добавление: модуль ngx_stream_log_module.
*) Добавление: параметр proxy_protocol в директиве listen, переменные
$proxy_protocol_addr и $proxy_protocol_port в модуле stream.
*) Добавление: модуль ngx_stream_realip_module.
*) Исправление: nginx не собирался с модулем stream и модулем
ngx_http_ssl_module, но без модуля ngx_stream_ssl_module; ошибка
появилась в 1.11.3.
*) Добавление: опция сокета IP_BIND_ADDRESS_NO_PORT не использовалась;
ошибка появилась в 1.11.2.
*) Исправление: в параметре ranges директивы geo.
*) Исправление: при использовании директив "aio threads" и sendfile мог
возвращаться некорректный ответ; ошибка появилась в 1.9.13.
Изменения в nginx 1.11.3 26.07.2016
*) Изменение: теперь accept_mutex по умолчанию выключен.
*) Добавление: теперь nginx использует EPOLLEXCLUSIVE на Linux.
*) Добавление: модуль ngx_stream_geo_module.
*) Добавление: модуль ngx_stream_geoip_module.
*) Добавление: модуль ngx_stream_split_clients_module.
*) Добавление: директивы proxy_pass и proxy_ssl_name в модуле stream
поддерживают переменные.
*) Исправление: утечки сокетов при использовании HTTP/2.
*) Исправление: в configure.
Спасибо Piotr Sikora.
Изменения в nginx 1.11.2 05.07.2016
*) Изменение: теперь nginx всегда использует внутренние реализации MD5 и
SHA1; параметры configure --with-md5 и --with-sha1 упразднены.
*) Добавление: поддержка переменных в модуле stream.
*) Добавление: модуль ngx_stream_map_module.
*) Добавление: модуль ngx_stream_return_module.
*) Добавление: в директивах proxy_bind, fastcgi_bind, memcached_bind,
scgi_bind и uwsgi_bind теперь можно указывать порт.
*) Добавление: теперь nginx использует опцию сокета
IP_BIND_ADDRESS_NO_PORT, если она доступна.
*) Исправление: при использовании HTTP/2 и директивы
proxy_request_buffering в рабочем процессе мог произойти segmentation
fault.
*) Исправление: при использовании HTTP/2 к запросам, передаваемым на
бэкенд, всегда добавлялась строка заголовка "Content-Length", даже
если у запроса не было тела.
*) Исправление: при использовании HTTP/2 в логах могли появляться
сообщения "http request count is zero".
*) Исправление: при использовании директивы sub_filter могло
буферизироваться больше данных, чем это необходимо; проблема
появилась в 1.9.4.
Изменения в nginx 1.11.1 31.05.2016
*) Безопасность: при записи тела специально созданного запроса во
временный файл в рабочем процессе мог происходить segmentation fault
(CVE-2016-4450); ошибка появилась в 1.3.9.
Изменения в nginx 1.11.0 24.05.2016
*) Добавление: параметр transparent директив proxy_bind, fastcgi_bind,
memcached_bind, scgi_bind и uwsgi_bind.
*) Добавление: переменная $request_id.
*) Добавление: директива map поддерживает комбинации нескольких
переменных в качестве результирующих значений.
*) Добавление: теперь при использовании метода epoll nginx проверяет,
поддерживает ли ядро события EPOLLRDHUP, и соответственно
оптимизирует обработку соединений.
*) Добавление: директивы ssl_certificate и ssl_certificate_key теперь
можно указывать несколько раз для загрузки сертификатов разных типов
(например, RSA и ECDSA).
*) Добавление: при использовании OpenSSL 1.0.2 и новее с помощью
директивы ssl_ecdh_curve теперь можно задать список кривых; по
умолчанию используется встроенный в OpenSSL список кривых.
*) Изменение: для использования DHE-шифров теперь надо явно задавать
файл параметров с помощью директивы ssl_dhparam.
*) Добавление: переменная $proxy_protocol_port.
*) Добавление: переменная $realip_remote_port в модуле
ngx_http_realip_module.
*) Добавление: модуль ngx_http_realip_module теперь позволяет
устанавливать не только адрес, но и порт клиента.
*) Изменение: при попытке запросить виртуальный сервер, отличающийся от
согласованного в процессе SSL handshake, теперь возвращается ответ
"421 Misdirected Request"; это улучшает совместимость с некоторыми
HTTP/2-клиентами в случае использования клиентских сертификатов.
*) Изменение: HTTP/2-клиенты теперь могут сразу присылать тело запроса;
директива http2_body_preread_size позволяет указать размер буфера,
который будет использоваться до того, как nginx начнёт читать тело.
*) Исправление: при использовании директивы proxy_cache_bypass не
обновлялись закэшированные ошибочные ответы.
Изменения в nginx 1.9.15 19.04.2016
*) Исправление: при использовании HHVM в качестве FastCGI-сервера могли
возникать ошибки "recv() failed".
*) Исправление: при использовании HTTP/2 и директив limit_req или
auth_request при чтении тела запроса мог произойти таймаут или ошибка
"client violated flow control"; ошибка появилась в 1.9.14.
*) Изменение: при использовании HTTP/2 ответ мог не показываться
некоторыми браузерами, если тело запроса было прочитано не целиком;
ошибка появилась в 1.9.14.
*) Исправление: при использовании директивы "aio threads" соединения
могли зависать.
Спасибо Mindaugas Rasiukevicius.
Изменения в nginx 1.9.14 05.04.2016
*) Добавление: совместимость с OpenSSL 1.1.0.
*) Добавление: директивы proxy_request_buffering,
fastcgi_request_buffering, scgi_request_buffering и
uwsgi_request_buffering теперь работают при использовании HTTP/2.
*) Исправление: при использовании HTTP/2 в логах могли появляться
сообщения "zero size buf in output".
*) Исправление: при использовании HTTP/2 директива client_max_body_size
могла работать неверно.
*) Исправление: незначительных ошибок логгирования.
Изменения в nginx 1.9.13 29.03.2016
*) Изменение: неидемпотентные запросы (POST, LOCK, PATCH) теперь по
умолчанию не передаются на другой сервер, если запрос уже был
отправлен на бэкенд; параметр non_idempotent директивы
proxy_next_upstream явно разрешает повторять такие запросы.
*) Добавление: модуль ngx_http_perl_module теперь можно собрать
динамически.
*) Добавление: поддержка UDP в модуле stream.
*) Добавление: директива aio_write.
*) Добавление: теперь cache manager следит за количеством элементов в
кэше и старается не допускать переполнений зоны разделяемой памяти.
*) Исправление: при использовании директив sendfile и aio с подзапросами
в логах могли появляться сообщения "task already active" и "second
aio post".
*) Исправление: при использовании кэширования в логах могли появляться
сообщения "zero size buf in output", если клиент закрывал соединение
преждевременно.
*) Исправление: при использовании кэширования соединения с клиентами
могли закрываться без необходимости.
Спасибо Justin Li.
*) Исправление: nginx мог нагружать процессор при использовании
директивы sendfile на Linux и Solaris, если отправляемый файл был
изменён в процессе отправки.
*) Исправление: при использовании директив sendfile и "aio threads"
соединения могли зависать.
*) Исправление: в директивах proxy_pass, fastcgi_pass, scgi_pass и
uwsgi_pass при использовании переменных.
Спасибо Piotr Sikora.
*) Исправление: в модуле ngx_http_sub_filter_module.
*) Исправление: если в закэшированном соединении к бэкенду происходила
ошибка, запрос передавался на другой сервер без учёта директивы
proxy_next_upstream.
*) Исправление: ошибки "CreateFile() failed" при создании временных
файлов на Windows.
Изменения в nginx 1.9.12 24.02.2016
*) Добавление: кодирование Хаффмана заголовков ответов в HTTP/2.
Спасибо Владу Краснову.
*) Добавление: директива worker_cpu_affinity теперь поддерживает более
64 процессоров.
*) Исправление: совместимость со сторонними модулями на C++; ошибка
появилась в 1.9.11.
Спасибо Piotr Sikora.
*) Исправление: nginx не собирался статически с OpenSSL на Linux; ошибка
появилась в 1.9.11.
*) Исправление: директива "add_header ... always" с пустым значением не
удаляла из заголовков ошибочных ответов строки Last-Modified и ETag.
*) Изменение: при использовании OpenSSL 1.0.2f в логах могли появляться
сообщения "called a function you should not call" и "shutdown while
in init".
*) Исправление: ошибочные заголовки могли логгироваться некорректно.
*) Исправление: утечки сокетов при использовании HTTP/2.
*) Исправление: в модуле ngx_http_v2_module.
Изменения в nginx 1.9.11 09.02.2016
*) Добавление: теперь resolver поддерживает TCP.
*) Добавление: динамические модули.
*) Исправление: при использовании HTTP/2 переменная $request_length не
учитывала размер заголовков запроса.
*) Исправление: в модуле ngx_http_v2_module.
Изменения в nginx 1.9.10 26.01.2016
*) Безопасность: при использовании директивы resolver во время обработки
ответов DNS-сервера могло происходить разыменование некорректного
адреса, что позволяло атакующему, имеющему возможность подделывать
UDP-пакеты от DNS-сервера, вызвать segmentation fault в рабочем
процессе (CVE-2016-0742).
*) Безопасность: при использовании директивы resolver во время обработки
CNAME-записей могло произойти обращение к ранее освобождённой памяти,
что позволяло атакующему, имеющему возможность инициировать
преобразование произвольных имён в адреса, вызвать segmentation fault
в рабочем процессе, а также потенциально могло иметь другие
последствия (CVE-2016-0746).
*) Безопасность: при использовании директивы resolver во время обработки
CNAME-записей не во всех случаях проверялось ограничение на
максимальное количество записей в цепочке, что позволяло атакующему,
имеющему возможность инициировать преобразование произвольных имён в
адреса, вызвать чрезмерное потребление ресурсов рабочими процессами
(CVE-2016-0747).
*) Добавление: параметр auto директивы worker_cpu_affinity.
*) Исправление: параметр proxy_protocol директивы listen не работал с
IPv6 listen-сокетами.
*) Исправление: при использовании директивы keepalive соединения к
бэкендам могли кэшироваться некорректно.
*) Исправление: после перенаправления запроса с помощью X-Accel-Redirect
при проксировании использовался HTTP-метод оригинального запроса.
Изменения в nginx 1.9.9 09.12.2015
*) Исправление: проксирование в unix domain сокеты не работало при
использовании переменных; ошибка появилась в 1.9.8.
Изменения в nginx 1.9.8 08.12.2015
*) Добавление: поддержка pwritev().
*) Добавление: директива include в блоке upstream.
*) Добавление: модуль ngx_http_slice_module.
*) Исправление: при использовании LibreSSL в рабочем процессе мог
произойти segmentation fault; ошибка появилась в 1.9.6.
*) Исправление: nginx мог не собираться на OS X.
Изменения в nginx 1.9.7 17.11.2015
*) Добавление: параметр nohostname логгирования в syslog.
*) Добавление: директива proxy_cache_convert_head.
*) Добавление: переменная $realip_remote_addr в модуле
ngx_http_realip_module.
*) Исправление: директива expires могла не срабатывать при использовании
переменных.
*) Исправление: при использовании HTTP/2 в рабочем процессе мог
произойти segmentation fault; ошибка появилась в 1.9.6.
*) Исправление: если nginx был собран с модулем ngx_http_v2_module,
протокол HTTP/2 мог быть использован клиентом, даже если не был
указан параметр http2 директивы listen.
*) Исправление: в модуле ngx_http_v2_module.
Изменения в nginx 1.9.6 27.10.2015
*) Исправление: при использовании HTTP/2 в рабочем процессе мог
произойти segmentation fault.
Спасибо Piotr Sikora и Denis Andzakovic.
*) Исправление: при использовании HTTP/2 переменная $server_protocol
была пустой.
*) Исправление: SSL-соединения к бэкендам в модуле stream могли
неожиданно завершаться по таймауту.
*) Исправление: при использовании различных настроек ssl_session_cache в
разных виртуальных серверах в рабочем процессе мог произойти
segmentation fault.
*) Исправление: nginx/Windows не собирался с MinGW gcc; ошибка появилась
в 1.9.4.
Спасибо Kouhei Sutou.
*) Исправление: при использовании директивы timer_resolution на Windows
время не обновлялось.
*) Незначительные исправления и улучшения.
Спасибо Markus Linnala, Kurtis Nusbaum и Piotr Sikora.
Изменения в nginx 1.9.5 22.09.2015
*) Добавление: модуль ngx_http_v2_module (заменяет модуль
ngx_http_spdy_module).
Спасибо Dropbox и Automattic за спонсирование разработки.
*) Изменение: теперь по умолчанию директива output_buffers использует
два буфера.
*) Изменение: теперь nginx ограничивает максимальную вложенность
подзапросов, а не количество одновременных подзапросов.
*) Изменение: теперь при возврате ответов из кэша nginx проверяет ключ
полностью.
Спасибо Геннадию Махомеду и Сергею Брестеру.
*) Исправление: при использовании кэша в логах могли появляться
сообщения "header already sent"; ошибка появилась в 1.7.5.
*) Исправление: при использовании CephFS и директивы timer_resolution на
Linux в логах могли появляться сообщения "writev() failed (4:
Interrupted system call)".
*) Исправление: в обработке ошибок конфигурации.
Спасибо Markus Linnala.
*) Исправление: при использовании директивы sub_filter на уровне http в
рабочем процессе происходил segmentation fault; ошибка появилась в
1.9.4.
Изменения в nginx 1.9.4 18.08.2015
*) Изменение: директивы proxy_downstream_buffer и proxy_upstream_buffer
в модуле stream заменены директивой proxy_buffer_size.
*) Добавление: директива tcp_nodelay в модуле stream.
*) Добавление: теперь можно указать несколько директив sub_filter
одновременно.
*) Добавление: директива sub_filter поддерживает переменные в строке
поиска.
*) Изменение: тестирование конфигурации могло не работать под Linux
OpenVZ.
Спасибо Геннадию Махомеду.
*) Исправление: после переконфигурации старые рабочие процессы могли
сильно нагружать процессор при больших значениях worker_connections.
*) Исправление: при совместном использовании директив try_files и alias
внутри location'а, заданного регулярным выражением, в рабочем
процессе мог произойти segmentation fault; ошибка появилась в 1.7.1.
*) Исправление: директива try_files внутри вложенного location'а,
заданного регулярным выражением, работала неправильно, если во
внешнем location'е использовалась директива alias.
*) Исправление: в обработке ошибок при построении хэш-таблиц.
*) Исправление: nginx не собирался с Visual Studio 2015.
Изменения в nginx 1.9.3 14.07.2015
*) Изменение: дублирующиеся блоки http, mail и stream теперь запрещены.
*) Добавление: ограничение количества соединений в модуле stream.
*) Добавление: ограничение скорости в модуле stream.
*) Исправление: директива zone в блоке upstream не работала на Windows.
*) Исправление: совместимость с LibreSSL в модуле stream.
Спасибо Piotr Sikora.
*) Исправление: в параметре --builddir в configure.
Спасибо Piotr Sikora.
*) Исправление: директива ssl_stapling_file не работала; ошибка
появилась в 1.9.2.
Спасибо Faidon Liambotis и Brandon Black.
*) Исправление: при использовании директивы ssl_stapling в рабочем
процессе мог произойти segmentation fault; ошибка появилась в 1.9.2.
Спасибо Matthew Baldwin.
Изменения в nginx 1.9.2 16.06.2015
*) Добавление: параметр backlog директивы listen в почтовом
прокси-сервере и модуле stream.
*) Добавление: директивы allow и deny в модуле stream.
*) Добавление: директива proxy_bind в модуле stream.
*) Добавление: директива proxy_protocol в модуле stream.
*) Добавление: ключ -T.
*) Добавление: параметр REQUEST_SCHEME добавлен в стандартные
конфигурационные файлы fastcgi.conf, fastcgi_params, scgi_params и
uwsgi_params.
*) Исправление: параметр reuseport директивы listen в модуле stream не
работал.
*) Исправление: OCSP stapling в некоторых случаях мог вернуть устаревший
OCSP-ответ.
Изменения в nginx 1.9.1 26.05.2015
*) Изменение: теперь протокол SSLv3 по умолчанию запрещён.
*) Изменение: некоторые давно устаревшие директивы больше не
поддерживаются.
*) Добавление: параметр reuseport директивы listen.
Спасибо Yingqi Lu из Intel и Sepherosa Ziehau.
*) Добавление: переменная $upstream_connect_time.
*) Исправление: в директиве hash на big-endian платформах.
*) Исправление: nginx мог не запускаться на некоторых старых версиях
Linux; ошибка появилась в 1.7.11.
*) Исправление: в парсинге IP-адресов.
Спасибо Сергею Половко.
Изменения в nginx 1.9.0 28.04.2015
*) Изменение: устаревшие методы обработки соединений aio и rtsig больше
не поддерживаются.
*) Добавление: директива zone в блоке upstream.
*) Добавление: модуль stream.
*) Добавление: поддержка byte ranges для ответов модуля
ngx_http_memcached_module.
Спасибо Martin Mlynář.
*) Добавление: разделяемую память теперь можно использовать на версиях
Windows с рандомизацией адресного пространства.
Спасибо Сергею Брестеру.
*) Добавление: директиву error_log теперь можно использовать на уровнях
mail и server в почтовом прокси-сервере.
*) Исправление: параметр proxy_protocol директивы listen не работал,
если не был указан в первой директиве listen для данного
listen-сокета.
Изменения в nginx 1.7.12 07.04.2015
*) Добавление: теперь директива tcp_nodelay работает для SSL-соединений
с бэкендами.
*) Добавление: теперь потоки могут использоваться для чтения заголовков
файлов в кэше.
*) Исправление: в директиве proxy_request_buffering.
*) Исправление: при использовании потоков на Linux в рабочем процессе
мог произойти segmentation fault.
*) Исправление: в обработке ошибок при использовании директивы
ssl_stapling.
Спасибо Filipe da Silva.
*) Исправление: в модуле ngx_http_spdy_module.
Изменения в nginx 1.7.11 24.03.2015
*) Изменение: параметр sendfile директивы aio более не нужен; теперь
nginx автоматически использует AIO для подгрузки данных для sendfile,
если одновременно используются директивы aio и sendfile.
*) Добавление: экспериментальная поддержка потоков.
*) Добавление: директивы proxy_request_buffering,
fastcgi_request_buffering, scgi_request_buffering и
uwsgi_request_buffering.
*) Добавление: экспериментальное API для обработки тела запроса.
*) Добавление: проверка клиентских SSL-сертификатов в почтовом
прокси-сервере.
Спасибо Sven Peter, Franck Levionnois и Filipe Da Silva.
*) Добавление: уменьшение времени запуска при использовании директивы
"hash ... consistent" в блоке upstream.
Спасибо Wai Keen Woon.
*) Добавление: отладочное логгирование в кольцевой буфер в памяти.
*) Исправление: в обработке хэш-таблиц.
Спасибо Chris West.
*) Исправление: в директиве proxy_cache_revalidate.
*) Исправление: SSL-соединения могли зависать, если использовался
отложенный accept или параметр proxy_protocol директивы listen.
Спасибо James Hamlin.
*) Исправление: переменная $upstream_response_time могла содержать
неверное значение при использовании директивы image_filter.
*) Исправление: в обработке целочисленных переполнений.
Спасибо Régis Leroy.
*) Исправление: при использовании LibreSSL было невозможно включить
поддержку SSLv3.
*) Исправление: при использовании LibreSSL в логах появлялись сообщения
"ignoring stale global SSL error ... called a function you should not
call".
*) Исправление: сертификаты, указанные в директивах
ssl_client_certificate и ssl_trusted_certificate, использовались для
автоматического построения цепочек сертификатов.
Изменения в nginx 1.7.10 10.02.2015
*) Добавление: параметр use_temp_path директив proxy_cache_path,
fastcgi_cache_path, scgi_cache_path и uwsgi_cache_path.
*) Добавление: переменная $upstream_header_time.
*) Изменение: теперь при переполнении диска nginx пытается писать
error_log'и только раз в секунду.
*) Исправление: директива try_files при тестировании каталогов не
игнорировала обычные файлы.
Спасибо Damien Tournoud.
*) Исправление: при использовании директивы sendfile на OS X возникали
ошибки "sendfile() failed"; ошибка появилась в nginx 1.7.8.
*) Исправление: в лог могли писаться сообщения "sem_post() failed".
*) Исправление: nginx не собирался с musl libc.
Спасибо James Taylor.
*) Исправление: nginx не собирался на Tru64 UNIX.
Спасибо Goetz T. Fischer.
Изменения в nginx 1.7.9 23.12.2014
*) Добавление: директивы proxy_cache, fastcgi_cache, scgi_cache и
uwsgi_cache поддерживают переменные.
*) Добавление: директива expires поддерживает переменные.
*) Добавление: возможность загрузки секретных ключей с аппаратных
устройств с помощью OpenSSL engines.
Спасибо Дмитрию Пичулину.
*) Добавление: директива autoindex_format.
*) Исправление: ревалидация элементов кэша теперь используется только
для ответов с кодами 200 и 206.
Спасибо Piotr Sikora.
*) Исправление: строка "TE" заголовка запроса клиента передавалась на
бэкенд при проксировании.
*) Исправление: директивы proxy_pass, fastcgi_pass, scgi_pass и
uwsgi_pass могли неправильно работать внутри блоков if и
limit_except.
*) Исправление: директива proxy_store с параметром "on" игнорировалась,
если на предыдущем уровне использовалась директива proxy_store с явно
заданным путём к файлам.
*) Исправление: nginx не собирался с BoringSSL.
Спасибо Lukas Tribus.
Изменения в nginx 1.7.8 02.12.2014
*) Изменение: теперь строки "If-Modified-Since", "If-Range" и им
подобные в заголовке запроса клиента передаются бэкенду при
включённом кэшировании, если nginx заранее знает, что не будет
кэшировать ответ (например, при использовании proxy_cache_min_uses).
*) Изменение: теперь после истечения proxy_cache_lock_timeout nginx
отправляет запрос на бэкенд без кэширования; новые директивы
proxy_cache_lock_age, fastcgi_cache_lock_age, scgi_cache_lock_age и
uwsgi_cache_lock_age позволяют указать, через какое время блокировка
будет принудительно снята и будет сделана ещё одна попытка
закэшировать ответ.
*) Изменение: директива log_format теперь может использоваться только на
уровне http.
*) Добавление: директивы proxy_ssl_certificate,
proxy_ssl_certificate_key, proxy_ssl_password_file,
uwsgi_ssl_certificate, uwsgi_ssl_certificate_key и
uwsgi_ssl_password_file.
Спасибо Piotr Sikora.
*) Добавление: теперь с помощью X-Accel-Redirect можно перейти в
именованный location.
Спасибо Toshikuni Fukaya.
*) Добавление: теперь директива tcp_nodelay работает для
SPDY-соединений.
*) Добавление: новые директивы в скриптах подсветки синтаксиса для vim.
Спасибо Peter Wu.
*) Исправление: nginx игнорировал значение "s-maxage" в строке
"Cache-Control" в заголовке ответа бэкенда.
Спасибо Piotr Sikora.
*) Исправление: в модуле ngx_http_spdy_module.
Спасибо Piotr Sikora.
*) Исправление: в директиве ssl_password_file при использовании OpenSSL
0.9.8zc, 1.0.0o, 1.0.1j.
*) Исправление: при использовании директивы post_action в лог писались
сообщения "header already sent"; ошибка появилась в nginx 1.5.4.
*) Исправление: при использовании директивы "postpone_output 0" с
SSI-подзапросами в лог могли писаться сообщения "the http output
chain is empty".
*) Исправление: в директиве proxy_cache_lock при использовании
SSI-подзапросов.
Спасибо Yichun Zhang.
Изменения в nginx 1.7.7 28.10.2014
*) Изменение: теперь nginx учитывает при кэшировании строку "Vary" в
заголовке ответа бэкенда.
*) Добавление: директивы proxy_force_ranges, fastcgi_force_ranges,
scgi_force_ranges и uwsgi_force_ranges.
*) Добавление: директивы proxy_limit_rate, fastcgi_limit_rate,
scgi_limit_rate и uwsgi_limit_rate.
*) Добавление: параметр Vary директив proxy_ignore_headers,
fastcgi_ignore_headers, scgi_ignore_headers и uwsgi_ignore_headers.
*) Исправление: последняя часть ответа, полученного от бэкенда при
небуферизированном проксировании, могла не отправляться клиенту, если
использовались директивы gzip или gunzip.
*) Исправление: в директиве proxy_cache_revalidate.
Спасибо Piotr Sikora.
*) Исправление: в обработке ошибок.
Спасибо Yichun Zhang и Даниилу Бондареву.
*) Исправление: в директивах proxy_next_upstream_tries и
proxy_next_upstream_timeout.
Спасибо Feng Gu.
*) Исправление: nginx/Windows не собирался с MinGW-w64 gcc.
Спасибо Kouhei Sutou.
Изменения в nginx 1.7.6 30.09.2014
*) Изменение: устаревшая директива limit_zone больше не поддерживается.
*) Добавление: в директивах limit_conn_zone и limit_req_zone теперь
можно использовать комбинации нескольких переменных.
*) Исправление: при повторной отправке FastCGI-запроса на бэкенд тело
запроса могло передаваться неправильно.
*) Исправление: в логгировании в syslog.
Изменения в nginx 1.7.5 16.09.2014
*) Безопасность: при использовании общего для нескольких блоков server
разделяемого кэша SSL-сессий или общего ключа для шифрования TLS
session tickets было возможно повторно использовать SSL-сессию в
контексте другого блока server (CVE-2014-3616).
Спасибо Antoine Delignat-Lavaud.
*) Изменение: директиву stub_status теперь можно указывать без
параметров.
*) Добавление: параметр always директивы add_header.
*) Добавление: директивы proxy_next_upstream_tries,
proxy_next_upstream_timeout, fastcgi_next_upstream_tries,
fastcgi_next_upstream_timeout, memcached_next_upstream_tries,
memcached_next_upstream_timeout, scgi_next_upstream_tries,
scgi_next_upstream_timeout, uwsgi_next_upstream_tries и
uwsgi_next_upstream_timeout.
*) Исправление: в параметре if директивы access_log.
*) Исправление: в модуле ngx_http_perl_module.
Спасибо Piotr Sikora.
*) Исправление: директива listen почтового прокси-сервера не позволяла
указать более двух параметров.
*) Исправление: директива sub_filter не работала с заменяемой строкой из
одного символа.
*) Исправление: запросы могли зависать, если использовался resolver и в
процессе обращения к DNS-серверу происходил таймаут.
*) Исправление: в модуле ngx_http_spdy_module при использовании
совместно с AIO.
*) Исправление: в рабочем процессе мог произойти segmentation fault,
если с помощью директивы set изменялись переменные "$http_...",
"$sent_http_..." или "$upstream_http_...".
*) Исправление: в обработке ошибок выделения памяти.
Спасибо Markus Linnala и Feng Gu.
Изменения в nginx 1.7.4 05.08.2014
*) Безопасность: pipelined-команды не отбрасывались после команды
STARTTLS в SMTP прокси-сервере (CVE-2014-3556); ошибка появилась в
1.5.6.
Спасибо Chris Boulton.
*) Изменение: экранирование символов в URI теперь использует
шестнадцатеричные цифры в верхнем регистре.
Спасибо Piotr Sikora.
*) Добавление: теперь nginx можно собрать с BoringSSL и LibreSSL.
Спасибо Piotr Sikora.
*) Исправление: запросы могли зависать, если использовался resolver и
DNS-сервер возвращал некорректный ответ; ошибка появилась в 1.5.8.
*) Исправление: в модуле ngx_http_spdy_module.
Спасибо Piotr Sikora.
*) Исправление: переменная $uri могла содержать мусор при возврате
ошибок с кодом 400.
Спасибо Сергею Боброву.
*) Исправление: в обработке ошибок в директиве proxy_store и в модуле
ngx_http_dav_module.
Спасибо Feng Gu.
*) Исправление: при логгировании ошибок в syslog мог происходить
segmentation fault; ошибка появилась в 1.7.1.
*) Исправление: переменные $geoip_latitude, $geoip_longitude,
$geoip_dma_code и $geoip_area_code могли не работать.
Спасибо Yichun Zhang.
*) Исправление: в обработке ошибок выделения памяти.
Спасибо Tatsuhiko Kubo и Piotr Sikora.
Изменения в nginx 1.7.3 08.07.2014
*) Добавление: weak entity tags теперь не удаляются при изменениях
ответа, а strong entity tags преобразуются в weak.
*) Добавление: ревалидация элементов кэша теперь, если это возможно,
использует заголовок If-None-Match.
*) Добавление: директива ssl_password_file.
*) Исправление: при возврате ответа из кэша заголовок запроса
If-None-Match игнорировался, если в ответе не было заголовка
Last-Modified.
*) Исправление: сообщения "peer closed connection in SSL handshake" при
соединении с бэкендами логгировались на уровне info вместо error.
*) Исправление: в модуле ngx_http_dav_module в nginx/Windows.
*) Исправление: SPDY-соединения могли неожиданно закрываться, если
использовалось кэширование.
Изменения в nginx 1.7.2 17.06.2014
*) Добавление: директива hash в блоке upstream.
*) Добавление: дефрагментация свободных блоков разделяемой памяти.
Спасибо Wandenberg Peixoto и Yichun Zhang.
*) Исправление: в рабочем процессе мог произойти segmentation fault,
если использовалось значение access_log по умолчанию; ошибка
появилась в 1.7.0.
Спасибо Piotr Sikora.
*) Исправление: завершающий слэш ошибочно удалялся из последнего
параметра директивы try_files.
*) Исправление: nginx мог не собираться на OS X.
*) Исправление: в модуле ngx_http_spdy_module.
Изменения в nginx 1.7.1 27.05.2014
*) Добавление: переменные "$upstream_cookie_...".
*) Добавление: переменная $ssl_client_fingerprint.
*) Добавление: директивы error_log и access_log теперь поддерживают
логгирование в syslog.
*) Добавление: почтовый прокси-сервер теперь логгирует порт клиента при
соединении.
*) Исправление: утечки памяти при использовании директивы
"ssl_stapling".
Спасибо Filipe da Silva.
*) Исправление: директива alias внутри location'а, заданного регулярным
выражением, работала неправильно, если использовались директивы if
или limit_except.
*) Исправление: директива charset не ставила кодировку для сжатых
ответов бэкендов.
*) Исправление: директива proxy_pass без URI могла использовать
оригинальный запрос после установки переменной $args.
Спасибо Yichun Zhang.
*) Исправление: в работе параметра none директивы smtp_auth; ошибка
появилась в 1.5.6.
Спасибо Святославу Никольскому.
*) Исправление: при совместном использовании sub_filter и SSI ответы
могли передаваться неверно.
*) Исправление: nginx не собирался с параметром --with-file-aio на
Linux/aarch64.
Изменения в nginx 1.7.0 24.04.2014
*) Добавление: проверка SSL-сертификатов бэкендов.
*) Добавление: поддержка SNI при работе с бэкендами по SSL.
*) Добавление: переменная $ssl_server_name.
*) Добавление: параметр if директивы access_log.
Изменения в nginx 1.5.13 08.04.2014
*) Изменение: улучшена обработка хэш-таблиц; в директивах
variables_hash_max_size и types_hash_bucket_size значения по
умолчанию изменены на 1024 и 64 соответственно.
*) Добавление: модуль ngx_http_mp4_module теперь понимает аргумент end.
*) Добавление: поддержка byte ranges модулем ngx_http_mp4_module и при
сохранении ответов в кэш.
*) Исправление: теперь nginx не пишет в лог сообщения "ngx_slab_alloc()
failed: no memory" при использовании разделяемой памяти в
ssl_session_cache и в модуле ngx_http_limit_req_module.
*) Исправление: директива underscores_in_headers не разрешала
подчёркивание в первом символе заголовка.
Спасибо Piotr Sikora.
*) Исправление: cache manager мог нагружать процессор при выходе в
nginx/Windows.
*) Исправление: при использовании ssl_session_cache с параметром shared
рабочий процесс nginx/Windows завершался аварийно.
*) Исправление: в модуле ngx_http_spdy_module.
Изменения в nginx 1.5.12 18.03.2014
*) Безопасность: при обработке специально созданного запроса модулем
ngx_http_spdy_module могло происходить переполнение буфера в рабочем
процессе, что потенциально могло приводить к выполнению произвольного
кода (CVE-2014-0133).
Спасибо Lucas Molas из Programa STIC, Fundación Dr. Manuel Sadosky,
Buenos Aires, Argentina.
*) Добавление: параметр proxy_protocol в директивах listen и
real_ip_header, переменная $proxy_protocol_addr.
*) Исправление: в директиве fastcgi_next_upstream.
Спасибо Lucas Molas.
Изменения в nginx 1.5.11 04.03.2014
*) Безопасность: при обработке специально созданного запроса модулем
ngx_http_spdy_module на 32-битных платформах могла повреждаться
память рабочего процесса, что потенциально могло приводить к
выполнению произвольного кода (CVE-2014-0088); ошибка появилась в
1.5.10.
Спасибо Lucas Molas из Programa STIC, Fundación Dr. Manuel Sadosky,
Buenos Aires, Argentina.
*) Добавление: переменная $ssl_session_reused.
*) Исправление: директива client_max_body_size могла не работать при
чтении тела запроса с использованием chunked transfer encoding;
ошибка появилась в 1.3.9.
Спасибо Lucas Molas.
*) Исправление: при проксировании WebSocket-соединений в рабочем
процессе мог произойти segmentation fault.
*) Исправление: в рабочем процессе мог произойти segmentation fault,
если использовался модуль ngx_http_spdy_module на 32-битных
платформах; ошибка появилась в 1.5.10.
*) Исправление: значение переменной $upstream_status могло быть
неверным, если использовались директивы proxy_cache_use_stale или
proxy_cache_revalidate.
Спасибо Piotr Sikora.
*) Исправление: в рабочем процессе мог произойти segmentation fault,
если ошибки с кодом 400 с помощью директивы error_page
перенаправлялись в именованный location.
*) Исправление: nginx/Windows не собирался с Visual Studio 2013.
Изменения в nginx 1.5.10 04.02.2014
*) Добавление: модуль ngx_http_spdy_module теперь использует протокол
SPDY 3.1.
Спасибо Automattic и MaxCDN за спонсирование разработки.
*) Добавление: модуль ngx_http_mp4_module теперь пропускает дорожки,
имеющие меньшую длину, чем запрошенная перемотка.
*) Исправление: в рабочем процессе мог произойти segmentation fault,
если переменная $ssl_session_id использовалась при логгировании;
ошибка появилась в 1.5.9.
*) Исправление: переменные $date_local и $date_gmt использовали неверный
формат вне модуля ngx_http_ssi_filter_module.
*) Исправление: клиентские соединения могли сразу закрываться, если
использовался отложенный accept; ошибка появилась в 1.3.15.
*) Исправление: сообщения "getsockopt(TCP_FASTOPEN) ... failed"
записывались в лог в процессе обновления исполняемого файла на Linux;
ошибка появилась в 1.5.8.
Спасибо Piotr Sikora.
Изменения в nginx 1.5.9 22.01.2014
*) Изменение: теперь в заголовке X-Accel-Redirect nginx ожидает
закодированный URI.
*) Добавление: директива ssl_buffer_size.
*) Добавление: директиву limit_rate теперь можно использовать для
ограничения скорости передачи ответов клиенту в SPDY-соединениях.
*) Добавление: директива spdy_chunk_size.
*) Добавление: директива ssl_session_tickets.
Спасибо Dirkjan Bussink.
*) Исправление: переменная $ssl_session_id содержала всю сессию в
сериализованном виде вместо её идентификатора.
Спасибо Ivan Ristić.
*) Исправление: nginx неправильно обрабатывал закодированный символ "?"
в команде SSI include.
*) Исправление: модуль ngx_http_dav_module не раскодировал целевой URI
при обработке методов COPY и MOVE.
*) Исправление: resolver не понимал доменные имена с точкой в конце.
Спасибо Yichun Zhang.
*) Исправление: при проксировании в логах могли появляться сообщения
"zero size buf in output"; ошибка появилась в 1.3.9.
*) Исправление: в рабочем процессе мог произойти segmentation fault,
если использовался модуль ngx_http_spdy_module.
*) Исправление: при использовании методов обработки соединений select,
poll и /dev/poll проксируемые WebSocket-соединения могли зависать
сразу после открытия.
*) Исправление: директива xclient почтового прокси-сервера некорректно
передавала IPv6-адреса.
Изменения в nginx 1.5.8 17.12.2013
*) Добавление: теперь resolver поддерживает IPv6.
*) Добавление: директива listen поддерживает параметр fastopen.
Спасибо Mathew Rodley.
*) Добавление: поддержка SSL в модуле ngx_http_uwsgi_module.
Спасибо Roberto De Ioris.
*) Добавление: скрипты подсветки синтаксиса для vim добавлены в contrib.
Спасибо Evan Miller.
*) Исправление: при чтении тела запроса с использованием chunked
transfer encoding по SSL-соединению мог произойти таймаут.
*) Исправление: директива master_process работала неправильно в
nginx/Windows.
*) Исправление: параметр setfib директивы listen мог не работать.
*) Исправление: в модуле ngx_http_spdy_module.
Изменения в nginx 1.5.7 19.11.2013
*) Безопасность: символ, следующий за незакодированным пробелом в строке
запроса, обрабатывался неправильно (CVE-2013-4547); ошибка появилась
в 0.8.41.
Спасибо Ivan Fratric из Google Security Team.
*) Изменение: уровень логгирования ошибок auth_basic об отсутствии
пароля понижен с уровня error до info.
*) Добавление: директивы proxy_cache_revalidate,
fastcgi_cache_revalidate, scgi_cache_revalidate и
uwsgi_cache_revalidate.
*) Добавление: директива ssl_session_ticket_key.
Спасибо Piotr Sikora.
*) Исправление: директива "add_header Cache-Control ''" добавляла строку
заголовка ответа "Cache-Control" с пустым значением.
*) Исправление: директива "satisfy any" могла вернуть ошибку 403 вместо
401 при использовании директив auth_request и auth_basic.
Спасибо Jan Marc Hoffmann.
*) Исправление: параметры accept_filter и deferred директивы listen
игнорировались для listen-сокетов, создаваемых в процессе обновления
исполняемого файла.
Спасибо Piotr Sikora.
*) Исправление: часть данных, полученных от бэкенда при
небуферизированном проксировании, могла не отправляться клиенту
сразу, если использовались директивы gzip или gunzip.
Спасибо Yichun Zhang.
*) Исправление: в обработке ошибок в модуле
ngx_http_gunzip_filter_module.
*) Исправление: ответы могли зависать, если использовался модуль
ngx_http_spdy_module и директива auth_request.
*) Исправление: утечки памяти в nginx/Windows.
Изменения в nginx 1.5.6 01.10.2013
*) Добавление: директива fastcgi_buffering.
*) Добавление: директивы proxy_ssl_protocols и proxy_ssl_ciphers.
Спасибо Piotr Sikora.
*) Добавление: оптимизация SSL handshake при использовании длинных
цепочек сертификатов.
*) Добавление: почтовый прокси-сервер поддерживает SMTP pipelining.
*) Исправление: в модуле ngx_http_auth_basic_module при использовании
метода шифрования паролей "$apr1$".
Спасибо Markus Linnala.
*) Исправление: на MacOSX, Cygwin и nginx/Windows для обработки запроса
мог использоваться неверный location, если для задания location'ов
использовались символы разных регистров.
*) Исправление: автоматическое перенаправление с добавлением
завершающего слэша для проксированных location'ов могло не работать.
*) Исправление: в почтовом прокси-сервере.
*) Исправление: в модуле ngx_http_spdy_module.
Изменения в nginx 1.5.5 17.09.2013
*) Изменение: теперь nginx по умолчанию использует HTTP/1.0, если точно
определить протокол не удалось.
*) Добавление: директива disable_symlinks теперь использует O_PATH на
Linux.
*) Добавление: для определения того, что клиент закрыл соединение, при
использовании метода epoll теперь используются события EPOLLRDHUP.
*) Исправление: в директиве valid_referers при использовании параметра
server_names.
*) Исправление: переменная $request_time не работала в nginx/Windows.
*) Исправление: в директиве image_filter.
Спасибо Lanshun Zhou.
*) Исправление: совместимость с OpenSSL 1.0.1f.
Спасибо Piotr Sikora.
Изменения в nginx 1.5.4 27.08.2013
*) Изменение: MIME-тип для расширения js изменён на
"application/javascript"; значение по умолчанию директивы
charset_types изменено соответственно.
*) Изменение: теперь директива image_filter с параметром size возвращает
ответ с MIME-типом "application/json".
*) Добавление: модуль ngx_http_auth_request_module.
*) Исправление: на старте или во время переконфигурации мог произойти
segmentation fault, если использовалась директива try_files с пустым
параметром.
*) Исправление: утечки памяти при использовании в директивах root и
auth_basic_user_file относительных путей, заданных с помощью
переменных.
*) Исправление: директива valid_referers неправильно выполняла
регулярные выражения, если заголовок Referer начинался с "https://".
Спасибо Liangbin Li.
*) Исправление: ответы могли зависать, если использовались подзапросы и
при обработке подзапроса происходила ошибка во время SSL handshake с
бэкендом.
Спасибо Aviram Cohen.
*) Исправление: в модуле ngx_http_autoindex_module.
*) Исправление: в модуле ngx_http_spdy_module.
Изменения в nginx 1.5.3 30.07.2013
*) Изменение во внутреннем API: теперь при небуферизированной работе с
бэкендами u->length по умолчанию устанавливается в -1.
*) Изменение: теперь при получении неполного ответа от бэкенда nginx
отправляет полученную часть ответа, после чего закрывает соединение с
клиентом.
*) Исправление: в рабочем процессе мог произойти segmentation fault,
если использовался модуль ngx_http_spdy_module и директива
client_body_in_file_only.
*) Исправление: параметр so_keepalive директивы listen мог работать
некорректно на DragonFlyBSD.
Спасибо Sepherosa Ziehau.
*) Исправление: в модуле ngx_http_xslt_filter_module.
*) Исправление: в модуле ngx_http_sub_filter_module.
Изменения в nginx 1.5.2 02.07.2013
*) Добавление: теперь можно использовать несколько директив error_log.
*) Исправление: метод $r->header_in() встроенного перла не возвращал
значения строк "Cookie" и "X-Forwarded-For" из заголовка запроса;
ошибка появилась в 1.3.14.
*) Исправление: в модуле ngx_http_spdy_module.
Спасибо Jim Radford.
*) Исправление: nginx не собирался на Linux при использовании x32 ABI.
Спасибо Сергею Иванцову.
Изменения в nginx 1.5.1 04.06.2013
*) Добавление: директивы ssi_last_modified, sub_filter_last_modified и
xslt_last_modified.
Спасибо Алексею Колпакову.
*) Добавление: параметр http_403 в директивах proxy_next_upstream,
fastcgi_next_upstream, scgi_next_upstream и uwsgi_next_upstream.
*) Добавление: директивы allow и deny теперь поддерживают unix domain
сокеты.
*) Исправление: nginx не собирался с модулем ngx_mail_ssl_module, но без
модуля ngx_http_ssl_module; ошибка появилась в 1.3.14.
*) Исправление: в директиве proxy_set_body.
Спасибо Lanshun Zhou.
*) Исправление: в директиве lingering_time.
Спасибо Lanshun Zhou.
*) Исправление: параметр fail_timeout директивы server в блоке upstream
мог не работать, если использовался параметр max_fails; ошибка
появилась в 1.3.0.
*) Исправление: в рабочем процессе мог произойти segmentation fault,
если использовалась директива ssl_stapling.
Спасибо Piotr Sikora.
*) Исправление: в почтовом прокси-сервере.
Спасибо Filipe Da Silva.
*) Исправление: nginx/Windows мог перестать принимать соединения, если
использовалось несколько рабочих процессов.
Изменения в nginx 1.5.0 07.05.2013
*) Безопасность: при обработке специально созданного запроса мог
перезаписываться стек рабочего процесса, что могло приводить к
выполнению произвольного кода (CVE-2013-2028); ошибка появилась в
1.3.9.
Спасибо Greg MacManus, iSIGHT Partners Labs.
Изменения в nginx 1.4.0 24.04.2013
*) Исправление: nginx не собирался с модулем ngx_http_perl_module, если
использовался параметр --with-openssl; ошибка появилась в 1.3.16.
*) Исправление: в работе с телом запроса из модуля ngx_http_perl_module;
ошибка появилась в 1.3.9.
Изменения в nginx 1.3.16 16.04.2013
*) Исправление: в рабочем процессе мог произойти segmentation fault,
если использовались подзапросы; ошибка появилась в 1.3.9.
*) Исправление: директива tcp_nodelay вызывала ошибку при проксировании
WebSocket-соединений в unix domain сокет.
*) Исправление: переменная $upstream_response_length возвращала значение
"0", если не использовалась буферизация.
Спасибо Piotr Sikora.
*) Исправление: в методах обработки соединений eventport и /dev/poll.
Изменения в nginx 1.3.15 26.03.2013
*) Изменение: открытие и закрытие соединения без отправки в нём
каких-либо данных больше не записывается в access_log с кодом ошибки
400.
*) Добавление: модуль ngx_http_spdy_module.
Спасибо Automattic за спонсирование разработки.
*) Добавление: директивы limit_req_status и limit_conn_status.
Спасибо Nick Marden.
*) Добавление: директива image_filter_interlace.
Спасибо Ивану Боброву.
*) Добавление: переменная $connections_waiting в модуле
ngx_http_stub_status_module.
*) Добавление: теперь почтовый прокси-сервер поддерживает IPv6-бэкенды.
*) Исправление: при повторной отправке запроса на бэкенд тело запроса
могло передаваться неправильно; ошибка появилась в 1.3.9.
Спасибо Piotr Sikora.
*) Исправление: в директиве client_body_in_file_only; ошибка появилась в
1.3.9.
*) Исправление: ответы могли зависать, если использовались подзапросы и
при обработке подзапроса происходила DNS-ошибка.
Спасибо Lanshun Zhou.
*) Исправление: в процедуре учёта использования бэкендов.
Изменения в nginx 1.3.14 05.03.2013
*) Добавление: переменные $connections_active, $connections_reading и
$connections_writing в модуле ngx_http_stub_status_module.
*) Добавление: поддержка WebSocket-соединений в модулях
ngx_http_uwsgi_module и ngx_http_scgi_module.
*) Исправление: в обработке виртуальных серверов при использовании SNI.
*) Исправление: при использовании директивы "ssl_session_cache shared"
новые сессии могли не сохраняться, если заканчивалось место в
разделяемой памяти.
Спасибо Piotr Sikora.
*) Исправление: несколько заголовков X-Forwarded-For обрабатывались
неправильно.
Спасибо Neal Poole за спонсирование разработки.
*) Исправление: в модуле ngx_http_mp4_module.
Спасибо Gernot Vormayr.
Изменения в nginx 1.3.13 19.02.2013
*) Изменение: теперь для сборки по умолчанию используется компилятор с
именем "cc".
*) Добавление: поддержка проксирования WebSocket-соединений.
Спасибо Apcera и CloudBees за спонсирование разработки.
*) Добавление: директива auth_basic_user_file поддерживает шифрование
паролей методом "{SHA}".
Спасибо Louis Opter.
Изменения в nginx 1.3.12 05.02.2013
*) Добавление: директивы proxy_bind, fastcgi_bind, memcached_bind,
scgi_bind и uwsgi_bind поддерживают переменные.
*) Добавление: переменные $pipe, $request_length, $time_iso8601 и
$time_local теперь можно использовать не только в директиве
log_format.
Спасибо Kiril Kalchev.
*) Добавление: поддержка IPv6 в модуле ngx_http_geoip_module.
Спасибо Gregor Kališnik.
*) Исправление: директива proxy_method работала неверно, если была
указана на уровне http.
*) Исправление: в рабочем процессе мог произойти segmentation fault,
если использовался resolver и метод poll.
*) Исправление: nginx мог нагружать процессор во время SSL handshake с
бэкендом при использовании методов обработки соединений select, poll
и /dev/poll.
*) Исправление: ошибка "[crit] SSL_write() failed (SSL:)".
*) Исправление: в директиве client_body_in_file_only; ошибка появилась в
1.3.9.
*) Исправление: в директиве fastcgi_keep_conn.
Изменения в nginx 1.3.11 10.01.2013
*) Исправление: при записи в лог мог происходить segmentation fault;
ошибка появилась в 1.3.10.
*) Исправление: директива proxy_pass не работала с IP-адресами без
явного указания порта; ошибка появилась в 1.3.10.
*) Исправление: на старте или во время переконфигурации происходил
segmentation fault, если директива keepalive была указана несколько
раз в одном блоке upstream.
*) Исправление: параметр default директивы geo не определял значение по
умолчанию для IPv6-адресов.
Изменения в nginx 1.3.10 25.12.2012
*) Изменение: для указанных в конфигурационном файле доменных имён
теперь используются не только IPv4, но и IPv6 адреса.
*) Изменение: теперь при использовании директивы include с маской на
Unix-системах включаемые файлы сортируются в алфавитном порядке.
*) Изменение: директива add_header добавляет строки в ответы с кодом
201.
*) Добавление: директива geo теперь поддерживает IPv6 адреса в формате
CIDR.
*) Добавление: параметры flush и gzip в директиве access_log.
*) Добавление: директива auth_basic поддерживает переменные.
*) Исправление: nginx в некоторых случаях не собирался с модулем
ngx_http_perl_module.
*) Исправление: в рабочем процессе мог произойти segmentation fault,
если использовался модуль ngx_http_xslt_module.
*) Исправление: nginx мог не собираться на MacOSX.
Спасибо Piotr Sikora.
*) Исправление: при использовании директивы limit_rate с большими
значениями скорости на 32-битных системах ответ мог возвращаться не
целиком.
Спасибо Алексею Антропову.
*) Исправление: в рабочем процессе мог произойти segmentation fault,
если использовалась директива if.
Спасибо Piotr Sikora.
*) Исправление: ответ "100 Continue" выдавался вместе с ответом "413
Request Entity Too Large".
*) Исправление: директивы image_filter, image_filter_jpeg_quality и
image_filter_sharpen могли наследоваться некорректно.
Спасибо Ивану Боброву.
*) Исправление: при использовании директивы auth_basic под Linux могли
возникать ошибки "crypt_r() failed".
*) Исправление: в обработке backup-серверов.
Спасибо Thomas Chen.
*) Исправление: при проксировании HEAD-запросов мог возвращаться
некорректный ответ, если использовалась директива gzip.
Изменения в nginx 1.3.9 27.11.2012
*) Добавление: поддержка chunked transfer encoding при получении тела
запроса.
*) Добавление: переменные $request_time и $msec теперь можно
использовать не только в директиве log_format.
*) Исправление: cache manager и cache loader могли не запускаться, если
использовалось более 512 listen-сокетов.
*) Исправление: в модуле ngx_http_dav_module.
Изменения в nginx 1.3.8 30.10.2012
*) Добавление: параметр optional_no_ca директивы ssl_verify_client.
Спасибо Михаилу Казанцеву и Eric O'Connor.
*) Добавление: переменные $bytes_sent, $connection и
$connection_requests теперь можно использовать не только в директиве
log_format.
Спасибо Benjamin Grössing.
*) Добавление: параметр auto директивы worker_processes.
*) Исправление: сообщения "cache file ... has md5 collision".
*) Исправление: в модуле ngx_http_gunzip_filter_module.
*) Исправление: в директиве ssl_stapling.
Изменения в nginx 1.3.7 02.10.2012
*) Добавление: поддержка OCSP stapling.
Спасибо Comodo, DigiCert и GlobalSign за спонсирование разработки.
*) Добавление: директива ssl_trusted_certificate.
*) Добавление: теперь resolver случайным образом меняет порядок
возвращаемых закэшированных адресов.
Спасибо Антону Жулину.
*) Исправление: совместимость с OpenSSL 0.9.7.
Изменения в nginx 1.3.6 12.09.2012
*) Добавление: модуль ngx_http_gunzip_filter_module.
*) Добавление: директива memcached_gzip_flag.
*) Добавление: параметр always директивы gzip_static.
*) Исправление: в директиве "limit_req"; ошибка появилась в 1.1.14.
Спасибо Charles Chen.
*) Исправление: nginx не собирался gcc 4.7 с оптимизацией -O2 если
использовался параметр --with-ipv6.
Изменения в nginx 1.3.5 21.08.2012
*) Изменение: модуль ngx_http_mp4_module больше не отфильтровывает
дорожки в форматах, отличных от H.264 и AAC.
*) Исправление: в рабочем процессе мог произойти segmentation fault,
если в директиве map в качестве значений использовались переменные.
*) Исправление: в рабочем процессе мог произойти segmentation fault при
использовании директивы geo с параметром ranges, но без параметра
default; ошибка появилась в 0.8.43.
Спасибо Zhen Chen и Weibin Yao.
*) Исправление: в обработке параметра командной строки -p.
*) Исправление: в почтовом прокси-сервере.
*) Исправление: незначительных потенциальных ошибок.
Спасибо Coverity.
*) Исправление: nginx/Windows не собирался с Visual Studio 2005 Express.
Спасибо HAYASHI Kentaro.
Изменения в nginx 1.3.4 31.07.2012
*) Изменение: теперь на слушающих IPv6-сокетах параметр ipv6only включён
по умолчанию.
*) Добавление: поддержка компилятора Clang.
*) Исправление: могли создаваться лишние слушающие сокеты.
Спасибо Роману Одайскому.
*) Исправление: nginx/Windows мог нагружать процессор, если при запуске
рабочего процесса происходила ошибка.
Спасибо Ricardo Villalobos Guevara.
*) Исправление: директивы proxy_pass_header, fastcgi_pass_header,
scgi_pass_header, uwsgi_pass_header, proxy_hide_header,
fastcgi_hide_header, scgi_hide_header и uwsgi_hide_header могли
наследоваться некорректно.
Изменения в nginx 1.3.3 10.07.2012
*) Добавление: поддержка entity tags и директива etag.
*) Исправление: при использовании директивы map с параметром hostnames
не игнорировалась конечная точка в исходном значении.
*) Исправление: для обработки запроса мог использоваться неверный
location, если переход в именованный location происходил после
изменения URI с помощью директивы rewrite.
Изменения в nginx 1.3.2 26.06.2012
*) Изменение: параметр single директивы keepalive теперь игнорируется.
*) Изменение: сжатие SSL теперь отключено в том числе при использовании
OpenSSL старее 1.0.0.
*) Добавление: директиву "ip_hash" теперь можно использовать для
балансировки IPv6 клиентов.
*) Добавление: переменную $status теперь можно использовать не только в
директиве log_format.
*) Исправление: при завершении рабочего процесса мог произойти
segmentation fault, если использовалась директива resolver.
*) Исправление: в рабочем процессе мог произойти segmentation fault,
если использовался модуль ngx_http_mp4_module.
*) Исправление: в модуле ngx_http_mp4_module.
*) Исправление: в рабочем процессе мог произойти segmentation fault,
если использовались конфликтующие имена серверов с масками.
*) Исправление: на платформе ARM nginx мог аварийно завершаться по
сигналу SIGBUS.
*) Исправление: во время переконфигурации на HP-UX в лог записывался
alert "sendmsg() failed (9: Bad file number)".
Изменения в nginx 1.3.1 05.06.2012
*) Безопасность: теперь nginx/Windows игнорирует точку в конце
компонента URI и не разрешает URI, содержащие последовательность
":$".
Спасибо Владимиру Кочеткову, Positive Research Center.
*) Добавление: директивы proxy_pass, fastcgi_pass, scgi_pass, uwsgi_pass
и директива server в блоке upstream теперь поддерживают IPv6-адреса.
*) Добавление: в директиве resolver теперь можно указывать порт и
задавать IPv6-адреса DNS-серверов.
*) Добавление: директива least_conn в блоке upstream.
*) Добавление: при использовании директивы ip_hash теперь можно задавать
веса серверов.
*) Исправление: в рабочем процессе мог произойти segmentation fault,
если использовалась директива image_filter; ошибка появилась в 1.3.0.
*) Исправление: nginx не собирался с модулем ngx_cpp_test_module; ошибка
появилась в 1.1.12.
*) Исправление: доступ к переменным из SSI и встроенного перла мог не
работать после переконфигурации.
Спасибо Yichun Zhang.
*) Исправление: в модуле ngx_http_xslt_filter_module.
Спасибо Kuramoto Eiji.
*) Исправление: утечки памяти при использовании переменной $geoip_org.
Спасибо Денису Латыпову.
*) Исправление: в директивах proxy_cookie_domain и proxy_cookie_path.
Изменения в nginx 1.3.0 15.05.2012
*) Добавление: директива debug_connection теперь поддерживает
IPv6-адреса и параметр "unix:".
*) Добавление: директива set_real_ip_from и параметр proxy директивы geo
теперь поддерживают IPv6-адреса.
*) Добавление: директивы real_ip_recursive, geoip_proxy и
geoip_proxy_recursive.
*) Добавление: параметр proxy_recursive директивы geo.
*) Исправление: в рабочем процессе мог произойти segmentation fault,
если использовалась директива resolver.
*) Исправление: в рабочем процессе мог произойти segmentation fault,
если использовались директивы fastcgi_pass, scgi_pass или uwsgi_pass
и бэкенд возвращал некорректный ответ.
*) Исправление: в рабочем процессе мог произойти segmentation fault,
если использовалась директива rewrite и в новых аргументах запроса в
строке замены использовались переменные.
*) Исправление: nginx мог нагружать процессор, если было достигнуто
ограничение на количество открытых файлов.
*) Исправление: при использовании директивы proxy_next_upstream с
параметром http_404 nginx мог бесконечно перебирать бэкенды, если в
блоке upstream был хотя бы один сервер с флагом backup.
*) Исправление: при использовании директивы ip_hash установка параметра
down директивы server могла приводить к ненужному перераспределению
клиентов между бэкендами.
*) Исправление: утечки сокетов.
Спасибо Yichun Zhang.
*) Исправление: в модуле ngx_http_fastcgi_module.
Изменения в nginx 1.2.0 23.04.2012
*) Исправление: в рабочем процессе мог произойти segmentation fault,
если использовалась директива try_files; ошибка появилась в 1.1.19.
*) Исправление: ответ мог быть передан не полностью, если использовалось
больше IOV_MAX буферов.
*) Исправление: в работе параметра crop директивы image_filter.
Спасибо Maxim Bublis.
Изменения в nginx 1.1.19 12.04.2012
*) Безопасность: при обработке специально созданного mp4 файла модулем
ngx_http_mp4_module могли перезаписываться области памяти рабочего
процесса, что могло приводить к выполнению произвольного кода
(CVE-2012-2089).
Спасибо Matthew Daley.
*) Исправление: nginx/Windows мог завершаться аварийно.
Спасибо Vincent Lee.
*) Исправление: nginx нагружал процессор, если все серверы в upstream'е
были помечены флагом backup.
*) Исправление: директивы allow и deny могли наследоваться некорректно,
если в них использовались IPv6 адреса.
*) Исправление: директивы modern_browser и ancient_browser могли
наследоваться некорректно.
*) Исправление: таймауты могли работать некорректно на Solaris/SPARC.
*) Исправление: в модуле ngx_http_mp4_module.
Изменения в nginx 1.1.18 28.03.2012
*) Изменение: теперь keepalive соединения не запрещены для Safari по
умолчанию.
*) Добавление: переменная $connection_requests.
*) Добавление: переменные $tcpinfo_rtt, $tcpinfo_rttvar,
$tcpinfo_snd_cwnd и $tcpinfo_rcv_space.
*) Добавление: директива worker_cpu_affinity теперь работает на FreeBSD.
*) Добавление: директивы xslt_param и xslt_string_param.
Спасибо Samuel Behan.
*) Исправление: в configure.
Спасибо Piotr Sikora.
*) Исправление: в модуле ngx_http_xslt_filter_module.
*) Исправление: nginx не собирался на Debian GNU/Hurd.
Изменения в nginx 1.1.17 15.03.2012
*) Безопасность: содержимое ранее освобождённой памяти могло быть
отправлено клиенту, если бэкенд возвращал специально созданный ответ.
Спасибо Matthew Daley.
*) Исправление: при использовании встроенного перла из SSI.
Спасибо Matthew Daley.
*) Исправление: в модуле ngx_http_uwsgi_module.
Изменения в nginx 1.1.16 29.02.2012
*) Изменение: ограничение на количество одновременных подзапросов
поднято до 200.
*) Добавление: параметр from в директиве disable_symlinks.
*) Добавление: директивы return и error_page теперь могут использоваться
для возврата перенаправлений с кодом 307.
*) Исправление: в рабочем процессе мог произойти segmentation fault,
если использовалась директива resolver и на глобальном уровне не была
задана директива error_log.
Спасибо Роману Арутюняну.
*) Исправление: в рабочем процессе мог произойти segmentation fault,
если использовались директивы "proxy_http_version 1.1" или
"fastcgi_keep_conn on".
*) Исправление: утечек памяти.
Спасибо Lanshun Zhou.
*) Исправление: в директиве disable_symlinks.
*) Исправление: при использовании ZFS размер кэша на диске мог считаться
некорректно; ошибка появилась в 1.0.1.
*) Исправление: nginx не собирался компилятором icc 12.1.
*) Исправление: nginx не собирался gcc на Solaris; ошибка появилась в
1.1.15.
Изменения в nginx 1.1.15 15.02.2012
*) Добавление: директива disable_symlinks.
*) Добавление: директивы proxy_cookie_domain и proxy_cookie_path.
*) Исправление: nginx мог некорректно сообщать об ошибке "upstream
prematurely closed connection" вместо "upstream sent too big header".
Спасибо Feibo Li.
*) Исправление: nginx не собирался с модулем ngx_http_perl_module, если
использовался параметр --with-openssl.
*) Исправление: количество внутренних перенаправлений в именованные
location'ы не ограничивалось.
*) Исправление: вызов $r->flush() несколько раз подряд мог приводить к
ошибкам в модуле ngx_http_gzip_filter_module.
*) Исправление: при использовании директивы proxy_store с
SSI-подзапросами временные файлы могли не удаляться.
*) Исправление: в некоторых случаях некэшируемые переменные (такие, как
$args) возвращали старое пустое закэшированное значение.
*) Исправление: в рабочем процессе мог произойти segmentation fault,
если одновременно создавалось слишком много SSI-подзапросов; ошибка
появилась в 0.7.25.
Изменения в nginx 1.1.14 30.01.2012
*) Добавление: теперь можно указать несколько ограничений limit_req
одновременно.
*) Исправление: в обработке ошибок при соединении с бэкендом.
Спасибо Piotr Sikora.
*) Исправление: в обработке ошибок при использовании AIO на FreeBSD.
*) Исправление: в инициализации библиотеки OpenSSL.
*) Исправление: директивы proxy_redirect могли наследоваться
некорректно.
*) Исправление: утечки памяти при переконфигурации, если использовалась
директива pcre_jit.
Изменения в nginx 1.1.13 16.01.2012
*) Добавление: параметры TLSv1.1 и TLSv1.2 в директиве ssl_protocols.
*) Исправление: параметры директивы limit_req наследовались некорректно;
ошибка появилась в 1.1.12.
*) Исправление: директива proxy_redirect некорректно обрабатывала
заголовок Refresh при использовании регулярных выражений.
*) Исправление: директива proxy_cache_use_stale с параметром error не
возвращала ответ из кэша, если все бэкенды были признаны
неработающими.
*) Исправление: директива worker_cpu_affinity могла не работать.
*) Исправление: nginx не собирался на Solaris; ошибка появилась в
1.1.12.
*) Исправление: в модуле ngx_http_mp4_module.
Изменения в nginx 1.1.12 26.12.2011
*) Изменение: после перенаправления запроса с помощью директивы
error_page директива proxy_pass без URI теперь использует изменённый
URI.
Спасибо Lanshun Zhou.
*) Добавление: директивы proxy/fastcgi/scgi/uwsgi_cache_lock,
proxy/fastcgi/scgi/uwsgi_cache_lock_timeout.
*) Добавление: директива pcre_jit.
*) Добавление: SSI команда if поддерживает выделения в регулярных
выражениях.
*) Исправление: SSI команда if не работала внутри команды block.
*) Исправление: директивы limit_conn_log_level и limit_req_log_level
могли не работать.
*) Исправление: директива limit_rate не позволяла передавать на полной
скорости, даже если был указан очень большой лимит.
*) Исправление: директива sendfile_max_chunk не работала, если
использовалась директива limit_rate.
*) Исправление: если в директиве proxy_pass использовались переменные и
не был указан URI, всегда использовался URI исходного запроса.
*) Исправление: после перенаправления запроса с помощью директивы
try_files директива proxy_pass без URI могла использовать URI
исходного запроса.
Спасибо Lanshun Zhou.
*) Исправление: в модуле ngx_http_scgi_module.
*) Исправление: в модуле ngx_http_mp4_module.
*) Исправление: nginx не собирался на Solaris; ошибка появилась в 1.1.9.
Изменения в nginx 1.1.11 12.12.2011
*) Добавление: параметр so_keepalive в директиве listen.
Спасибо Всеволоду Стахову.
*) Добавление: параметр if_not_empty в директивах
fastcgi/scgi/uwsgi_param.
*) Добавление: переменная $https.
*) Добавление: директива proxy_redirect поддерживает переменные в первом
параметре.
*) Добавление: директива proxy_redirect поддерживает регулярные
выражения.
*) Исправление: переменная $sent_http_cache_control могла содержать
неверное значение при использовании директивы expires.
Спасибо Yichun Zhang.
*) Исправление: директива read_ahead могла не работать при использовании
совместно с try_files и open_file_cache.
*) Исправление: если в параметре inactive директивы proxy_cache_path
было указано малое время, в рабочем процессе мог произойти
segmentation fault.
*) Исправление: ответы из кэша могли зависать.
Изменения в nginx 1.1.10 30.11.2011
*) Исправление: при использовании AIO на Linux в рабочем процессе
происходил segmentation fault; ошибка появилась в 1.1.9.
Изменения в nginx 1.1.9 28.11.2011
*) Изменение: теперь двойные кавычки экранируется при выводе
SSI-командой echo.
Спасибо Зауру Абасмирзоеву.
*) Добавление: параметр valid в директиве resolver. По умолчанию теперь
используется TTL, возвращённый DNS-сервером.
Спасибо Кириллу Коринскому.
*) Исправление: nginx мог перестать отвечать, если рабочий процесс
завершался аварийно.
*) Исправление: в рабочем процессе мог произойти segmentation fault,
если использовалось SNI; ошибка появилась в 1.1.2.
*) Исправление: в директиве keepalive_disable; ошибка появилась в 1.1.8.
Спасибо Александру Усову.
*) Исправление: сигнал SIGWINCH переставал работать после первого
обновления исполняемого файла; ошибка появилась в 1.1.1.
*) Исправление: теперь ответы бэкендов, длина которых не соответствует
заголовку Content-Length, не кэширутся.
*) Исправление: в директиве scgi_param при использовании составных
параметров.
*) Исправление: в методе epoll.
Спасибо Yichun Zhang.
*) Исправление: в модуле ngx_http_flv_module.
Спасибо Piotr Sikora.
*) Исправление: в модуле ngx_http_mp4_module.
*) Исправление: теперь nginx понимает IPv6-адреса в строке запроса и в
заголовке Host.
*) Исправление: директивы add_header и expires не работали для ответов с
кодом 206, если запрос проксировался.
*) Исправление: nginx не собирался на FreeBSD 10.
*) Исправление: nginx не собирался на AIX.
Изменения в nginx 1.1.8 14.11.2011
*) Изменение: модуль ngx_http_limit_zone_module переименован в
ngx_http_limit_conn_module.
*) Изменение: директива limit_zone заменена директивой limit_conn_zone с
новым синтаксисом.
*) Добавление: поддержка ограничения по нескольким limit_conn на одном
уровне.
*) Добавление: директива image_filter_sharpen.
*) Исправление: в рабочем процессе мог произойти segmentation fault,
если resolver получил большой DNS-ответ.
Спасибо Ben Hawkes.
*) Исправление: в вычислении ключа для кэширования, если использовалась
внутренняя реализация MD5; ошибка появилась в 1.0.4.
*) Исправление: строки "If-Modified-Since", "If-Range" и им подобные в
заголовке запроса клиента могли передаваться бэкенду при кэшировании;
или не передаваться при выключенном кэшировании, если кэширование
было включено в другой части конфигурации.
*) Исправление: модуль ngx_http_mp4_module выдавал неверную строку
"Content-Length" в заголовке ответа, использовался аргумент start.
Спасибо Piotr Sikora.
Изменения в nginx 1.1.7 31.10.2011
*) Добавление: поддержка нескольких DNS серверов в директиве "resolver".
Спасибо Кириллу Коринскому.
*) Исправление: на старте или во время переконфигурации происходил
segmentation fault, если директива ssl использовалась на уровне http
и не был указан ssl_certificate.
*) Исправление: уменьшено потребление памяти при проксировании больших
файлов, если они буферизировались на диск.
*) Исправление: в рабочем процессе мог произойти segmentation fault,
если использовалась директива "proxy_http_version 1.1".
*) Исправление: в директиве "expires @time".
Изменения в nginx 1.1.6 17.10.2011
*) Изменение во внутреннем API: теперь при внутреннем редиректе в
именованный location контексты модулей очищаются.
По запросу Yichun Zhang.
*) Изменение: теперь если сервер, описанный в блоке upstream, был
признан неработающим, то после истечения fail_timeout на него будет
отправлен только один запрос; сервер будет считаться работающим, если
успешно ответит на этот запрос.
*) Изменение: теперь символы 0x7F-0xFF в access_log записываются в виде
\xXX.
*) Добавление: директивы "proxy/fastcgi/scgi/uwsgi_ignore_headers"
теперь поддерживают значения X-Accel-Limit-Rate, X-Accel-Buffering и
X-Accel-Charset.
*) Добавление: уменьшение потребления памяти при использовании SSL.
*) Исправление: некоторые UTF-8 символы обрабатывались неправильно.
Спасибо Алексею Куцу.
*) Исправление: директивы модуля ngx_http_rewrite_module, заданные на
уровне server, применялись повторно, если для запроса не находилось
ни одного location'а.
*) Исправление: при использовании "aio sendfile" могла происходить
утечка сокетов.
*) Исправление: при использовании файлового AIO соединения с быстрыми
клиентами могли быть закрыты по истечению send_timeout.
*) Исправление: в модуле ngx_http_autoindex_module.
*) Исправление: модуль ngx_http_mp4_module не поддерживал перемотку на
32-битных платформах.
Изменения в nginx 1.1.5 05.10.2011
*) Добавление: директивы uwsgi_buffering и scgi_buffering.
Спасибо Peter Smit.
*) Исправление: при использовании proxy_cache_bypass могли быть
закэшированы некэшируемые ответы.
Спасибо John Ferlito.
*) Исправление: в модуле ngx_http_proxy_module при работе с бэкендами по
HTTP/1.1.
*) Исправление: закэшированные ответы с пустым телом возвращались
некорректно; ошибка появилась в 0.8.31.
*) Исправление: ответы с кодом 201 модуля ngx_http_dav_module были
некорректны; ошибка появилась в 0.8.32.
*) Исправление: в директиве return.
*) Исправление: при использовании директивы "ssl_session_cache builtin"
происходил segmentation fault; ошибка появилась в 1.1.1.
Изменения в nginx 1.1.4 20.09.2011
*) Добавление: модуль ngx_http_upstream_keepalive.
*) Добавление: директива proxy_http_version.
*) Добавление: директива fastcgi_keep_conn.
*) Добавление: директива worker_aio_requests.
*) Исправление: если nginx был собран с файловым AIO, он не мог
запускаться на Linux без поддержки AIO.
*) Исправление: в обработке ошибок при работе с Linux AIO.
Спасибо Hagai Avrahami.
*) Исправление: уменьшено потребление памяти для долгоживущих запросов.
*) Исправление: модуль ngx_http_mp4_module не поддерживал 64-битный
MP4-атом co64.
Изменения в nginx 1.1.3 14.09.2011
*) Добавление: модуль ngx_http_mp4_module.
*) Исправление: в Linux AIO, используемым совместно с open_file_cache.
*) Исправление: open_file_cache не обновлял информацию о файле, если
файл был изменён не атомарно.
*) Исправление: nginx не собирался на MacOSX 10.7.
Изменения в nginx 1.1.2 05.09.2011
*) Изменение: теперь, если суммарный размер всех диапазонов больше
размера исходного ответа, то nginx возвращает только исходный ответ,
не обрабатывая диапазоны.
*) Добавление: директива max_ranges.
*) Исправление: директивы ssl_verify_client, ssl_verify_depth и
ssl_prefer_server_cipher могли работать некорректно, если
использовался SNI.
*) Исправление: в директивах proxy/fastcgi/scgi/
uwsgi_ignore_client_abort.
Изменения в nginx 1.1.1 22.08.2011
*) Изменение: теперь загрузчик кэша за каждую итерацию либо обрабатывает
число файлов, указанное в параметре load_files, либо работает не
дольше времени, указанного в параметре loader_threshold.
*) Изменение: SIGWINCH сигнал теперь работает только в режиме демона.
*) Добавление: теперь разделяемые зоны и кэши используют семафоры POSIX
на Solaris.
Спасибо Денису Иванову.
*) Добавление: теперь на NetBSD поддерживаются accept фильтры.
*) Исправление: nginx не собирался на Linux 3.0.
*) Исправление: в некоторых случаях nginx не использовал сжатие; ошибка
появилась в 1.1.0.
*) Исправление: обработка тела запроса могла быть неверной, если клиент
использовал pipelining.
*) Исправление: в директиве request_body_in_single_buf.
*) Исправление: в директивах proxy_set_body и proxy_pass_request_body
при использовании SSL-соединения с бэкендом.
*) Исправление: nginx нагружал процессор, если все серверы в upstream'е
были помечены флагом down.
*) Исправление: при переконфигурации мог произойти segmentation fault,
если в предыдущей конфигурации был определён, но не использовался
ssl_session_cache.
*) Исправление: при использовании большого количества backup-серверов в
рабочем процессе мог произойти segmentation fault.
*) Исправление: при использовании директив fastcgi/scgi/uwsgi_param со
значениями, начинающимися со строки "HTTP_", в рабочем процессе мог
произойти segmentation fault; ошибка появилась в 0.8.40.
Изменения в nginx 1.1.0 01.08.2011
*) Добавление: уменьшение времени работы загрузчика кэша.
*) Добавление: параметры loader_files, loader_sleep и loader_threshold
директив proxy/fastcgi/scgi/uwsgi_cache_path.
*) Добавление: уменьшение времени загрузки конфигураций с большим
количеством HTTPS серверов.
*) Добавление: теперь nginx поддерживает шифры с обменом ECDHE-ключами.
Спасибо Adrian Kotelba.
*) Добавление: директива lingering_close.
Спасибо Максиму Дунину.
*) Исправление: закрытия соединения для pipelined-запросов.
Спасибо Максиму Дунину.
*) Исправление: nginx не запрещал сжатие при получении значения
"gzip;q=0" в строке "Accept-Encoding" в заголовке запроса клиента.
*) Исправление: таймаута при небуферизированном проксировании.
Спасибо Максиму Дунину.
*) Исправление: утечки памяти при использовании переменных в директиве
proxy_pass при работе с бэкендом по HTTPS.
Спасибо Максиму Дунину.
*) Исправление: в проверке параметра директивы proxy_pass, заданного
переменными.
Спасибо Lanshun Zhou.
*) Исправление: SSL не работал на QNX.
Спасибо Максиму Дунину.
*) Исправление: SSL модули не собирались gcc 4.6 без параметра
--with-debug.
Изменения в nginx 1.0.5 19.07.2011
*) Изменение: теперь по умолчанию используются следующие шифры SSL:
"HIGH:!aNULL:!MD5".
Спасибо Rob Stradling.
*) Добавление: директивы referer_hash_max_size и
referer_hash_bucket_size.
Спасибо Witold Filipczyk.
*) Добавление: переменная $uid_reset.
*) Исправление: при использовании кэширования в рабочем процессе мог
произойти segmentation fault.
Спасибо Lanshun Zhou.
*) Исправление: при использовании кэширования рабочие процессы могли
зациклиться во время переконфигурации; ошибка появилась в 0.8.48.
Спасибо Максиму Дунину.
*) Исправление: сообщения "stalled cache updating".
Спасибо Максиму Дунину.
Изменения в nginx 1.0.4 01.06.2011
*) Изменение: теперь в регулярных выражениях в директиве map можно
задать чувствительность к регистру с помощью префиксов "~" и "~*".
*) Добавление: теперь разделяемые зоны и кэши используют семафоры POSIX
на Linux.
Спасибо Денису Латыпову.
*) Исправление: сообщения "stalled cache updating".
*) Исправление: nginx не собирался с параметром
--without-http_auth_basic_module; ошибка появилась в 1.0.3.
Изменения в nginx 1.0.3 25.05.2011
*) Добавление: директива auth_basic_user_file поддерживает шифрование
пароля методами "$apr1", "{PLAIN}" и "{SSHA}".
Спасибо Максиму Дунину.
*) Добавление: директива geoip_org и переменная $geoip_org.
Спасибо Александру Ускову, Arnaud Granal и Денису Латыпову.
*) Добавление: модули ngx_http_geo_module и ngx_http_geoip_module
поддерживают адреса IPv4, отображённые на IPv6 адреса.
*) Исправление: при проверке адреса IPv4, отображённого на адрес IPv6, в
рабочем процессе происходил segmentation fault, если директивы access
или deny были определены только для адресов IPv6; ошибка появилась в
0.8.22.
*) Исправление: закэшированный ответ мог быть испорчен, если значения
директив proxy/fastcgi/scgi/uwsgi_cache_bypass и proxy/fastcgi/scgi/
uwsgi_no_cache были разными; ошибка появилась в 0.8.46.
Изменения в nginx 1.0.2 10.05.2011
*) Добавление: теперь разделяемые зоны и кэши используют семафоры POSIX.
*) Исправление: в работе параметра rotate директивы image_filter.
Спасибо Adam Bocim.
*) Исправление: nginx не собирался на Solaris; ошибка появилась в 1.0.1.
Изменения в nginx 1.0.1 03.05.2011
*) Изменение: теперь директива split_clients использует алгоритм
MurmurHash2 из-за лучшего распределения.
Спасибо Олегу Мамонтову.
*) Изменение: теперь длинные строки, начинающиеся с нуля, не считаются
ложными значениями.
Спасибо Максиму Дунину.
*) Изменение: теперь по умолчанию nginx использует значение 511 для
listen backlog на Linux.
*) Добавление: переменные $upstream_... можно использовать в SSI и
перловом модулях.
*) Исправление: теперь nginx лучше ограничивает размер кэша на диске.
Спасибо Олегу Мамонтову.
*) Исправление: при парсинге неправильного IPv4 адреса мог произойти
segmentation fault; ошибка появилась в 0.8.22.
Спасибо Максиму Дунину.
*) Исправление: nginx не собирался gcc 4.6 без параметра --with-debug.
*) Исправление: nginx не собирался на Solaris 9 и более ранних; ошибка
появилась в 0.9.3.
Спасибо Dagobert Michelsen.
*) Исправление: переменная $request_time имела неверные значения, если
использовались подзапросы; ошибка появилась в 0.8.47.
Спасибо Игорю А. Валькову.
Изменения в nginx 1.0.0 12.04.2011
*) Исправление: cache manager мог нагружать процессор после
переконфигурации.
Спасибо Максиму Дунину.
*) Исправление: директива "image_filter crop" неправильно работала в
сочетании с "image_filter rotate 180".
*) Исправление: директива "satisfy any" запрещала выдачу
пользовательской страницы для 401 кода.
Изменения в nginx 0.9.7 04.04.2011
*) Добавление: теперь соединения в состоянии keepalive могут быть
закрыты преждевременно, если у воркера нет свободных соединений.
Спасибо Максиму Дунину.
*) Добавление: параметр rotate директивы image_filter.
Спасибо Adam Bocim.
*) Исправление: ситуации, когда бэкенд в директивах fastcgi_pass,
scgi_pass или uwsgi_pass задан выражением и ссылается на описанный
upstream.
Изменения в nginx 0.9.6 21.03.2011
*) Добавление: директива map поддерживает регулярные выражения в
качестве значения первого параметра.
*) Добавление: переменная $time_iso8601 для access_log.
Спасибо Michael Lustfield.
Изменения в nginx 0.9.5 21.02.2011
*) Изменение: теперь по умолчанию nginx использует значение -1 для
listen backlog на Linux.
Спасибо Андрею Нигматулину.
*) Добавление: параметр utf8 в директивах geoip_country и geoip_city.
Спасибо Денису Латыпову.
*) Исправление: исправление в умолчательной директиве proxy_redirect,
если в директиве proxy_pass не был описан URI.
Спасибо Максиму Дунину.
*) Исправление: директива error_page не работала с нестандартными кодами
ошибок; ошибка появилась в 0.8.53.
Спасибо Максиму Дунину.
Изменения в nginx 0.9.4 21.01.2011
*) Добавление: директива server_name поддерживает переменную $hostname.
*) Добавление: 494 код для ошибки "Request Header Too Large".
Изменения в nginx 0.9.3 13.12.2010
*) Исправление: если для пары IPv6-адрес:порт описан только один сервер,
то выделения в регулярных выражениях в директиве server_name не
работали.
*) Исправление: nginx не собирался под Solaris; ошибка появилась в
0.9.0.
Изменения в nginx 0.9.2 06.12.2010
*) Добавление: поддержка строки "If-Unmodified-Since" в заголовке
запроса клиента.
*) Изменение: использование accept(), если accept4() не реализован;
ошибка появилась в 0.9.0.
*) Исправление: nginx не собирался под Cygwin; ошибка появилась в 0.9.0.
*) Исправление: уязвимости в OpenSSL CVE-2010-4180.
Спасибо Максиму Дунину.
Изменения в nginx 0.9.1 30.11.2010
*) Исправление: директивы вида "return CODE message" не работали; ошибка
появилась в 0.9.0.
Изменения в nginx 0.9.0 29.11.2010
*) Добавление: директива keepalive_disable.
*) Добавление: директива map поддерживает переменные в качестве значения
определяемой переменной.
*) Добавление: директива map поддерживает пустые строки в качестве
значения первого параметра.
*) Добавление: директива map поддерживает выражения в первом параметре.
*) Добавление: страница руководства nginx(8).
Спасибо Сергею Осокину.
*) Добавление: поддержка accept4() в Linux.
Спасибо Simon Liu.
*) Изменение: устранение предупреждения линкера о "sys_errlist" и
"sys_nerr" под Linux; предупреждение появилось в 0.8.35.
*) Исправление: при использовании директивы auth_basic в рабочем
процессе мог произойти segmentation fault.
Спасибо Михаилу Лалетину.
*) Исправление: совместимость с модулем ngx_http_eval_module; ошибка
появилась в 0.8.42.
Изменения в nginx 0.8.53 18.10.2010
*) Добавление: теперь директива error_page позволяет менять код статуса
у редиректа.
*) Добавление: директива gzip_disable поддерживает специальную маску
degradation.
*) Исправление: при использовании файлового AIO могла происходить утечка
сокетов.
Спасибо Максиму Дунину.
*) Исправление: если в первом сервере не была описана директива listen и
нигде явно не описан сервер по умолчанию, то сервером по умолчанию
становился следующий сервер с директивой listen; ошибка появилась в
0.8.21.
Изменения в nginx 0.8.52 28.09.2010
*) Исправление: nginx использовал режим SSL для listen сокета, если для
него был установлен любой listen-параметр; ошибка появилась в 0.8.51.
Изменения в nginx 0.8.51 27.09.2010
*) Изменение: директива secure_link_expires упразднена.
*) Изменение: уровень логгирования ошибок resolver'а понижен с уровня
alert на error.
*) Добавление: теперь параметр "ssl" listen-сокета можно устанавливать
несколько раз.
Изменения в nginx 0.8.50 02.09.2010
*) Добавление: директивы secure_link, secure_link_md5 и
secure_link_expires модуля ngx_http_secure_link_module.
*) Добавление: ключ -q.
Спасибо Геннадию Махомеду.
*) Исправление: при использовании кэширования рабочие процессы и могли
зациклиться во время переконфигурации; ошибка появилась в 0.8.48.
*) Исправление: в директиве gzip_disable.
Спасибо Derrick Petzold.
*) Исправление: nginx/Windows не мог посылать сигналы stop, quit,
reopen, reload процессу, запущенному в другой сессии.
Изменения в nginx 0.8.49 09.08.2010
*) Добавление: директива image_filter_jpeg_quality поддерживает
переменные.
*) Исправление: при использовании переменной $geoip_region_name в
рабочем процессе мог произойти segmentation fault; ошибка появилась в
0.8.48.
*) Исправление: ошибки, перехваченные error_page, кэшировались только до
следующего запроса; ошибка появилась в 0.8.48.
Изменения в nginx 0.8.48 03.08.2010
*) Изменение: теперь по умолчанию директива server_name имеет значение
пустое имя "".
Спасибо Геннадию Махомеду.
*) Изменение: теперь по умолчанию директива server_name_in_redirect
имеет значение off.
*) Добавление: переменные $geoip_dma_code, $geoip_area_code и
$geoip_region_name.
Спасибо Christine McGonagle.
*) Исправление: директивы proxy_pass, fastcgi_pass, uwsgi_pass и
scgi_pass не наследовались в блоки limit_except.
*) Исправление: директивы proxy_cache_min_uses, fastcgi_cache_min_uses
uwsgi_cache_min_uses и scgi_cache_min_uses не работали; ошибка
появилась в 0.8.46.
*) Исправление: директива fastcgi_split_path_info неверно использовала
выделения, если в выделения попадала только часть URI.
Спасибо Юрию Тарадаю и Frank Enderle.
*) Исправление: директива rewrite не экранировала символ ";" при
копировании из URI в аргументы.
Спасибо Daisuke Murase.
*) Исправление: модуль ngx_http_image_filter_module закрывал соединение,
если изображение было больше размера image_filter_buffer.
Изменения в nginx 0.8.47 28.07.2010
*) Исправление: переменная $request_time имела неверные значения для
подзапросов.
*) Исправление: ошибки, перехваченные error_page, не кэшировались.
*) Исправление: если использовался параметр max_size, то cache manager
мог зациклиться; ошибка появилась в 0.8.46.
Изменения в nginx 0.8.46 19.07.2010
*) Изменение: директивы proxy_no_cache, fastcgi_no_cache, uwsgi_no_cache
и scgi_no_cache теперь влияют только на сохранение закэшированного
ответа.
*) Добавление: директивы proxy_cache_bypass, fastcgi_cache_bypass,
uwsgi_cache_bypass и scgi_cache_bypass.
*) Исправление: nginx не освобождал память в keys_zone кэшей в случае
ошибки работы с бэкендом: память освобождалась только по истечении
времени неактивности или при недостатке памяти.
Изменения в nginx 0.8.45 13.07.2010
*) Добавление: улучшения в модуле ngx_http_xslt_filter.
Спасибо Laurence Rowe.
*) Исправление: ответ SSI модуля мог передаваться не полностью после
команды include с параметром wait="yes"; ошибка появилась в 0.7.25.
Спасибо Максиму Дунину.
*) Исправление: директива listen не поддерживала параметр setfib=0.
Изменения в nginx 0.8.44 05.07.2010
*) Изменение: теперь nginx по умолчанию не кэширует ответы бэкендов, в
заголовке которых есть строка "Set-Cookie".
*) Добавление: директива listen поддерживает параметр setfib.
Спасибо Андрею Филонову.
*) Исправление: директива sub_filter могла изменять регистр букв при
частичном совпадении.
*) Исправление: совместимость с HP/UX.
*) Исправление: совместимость с компилятором AIX xlC_r.
*) Исправление: nginx считал большие пакеты SSLv2 как обычные текстовые
запросы.
Спасибо Miroslaw Jaworski.
Изменения в nginx 0.8.43 30.06.2010
*) Добавление: ускорение загрузки больших баз geo-диапазонов.
*) Исправление: перенаправление ошибки в "location /zero {return 204;}"
без изменения кода ответа оставляло тело ошибки; ошибка появилась в
0.8.42.
*) Исправление: nginx мог закрывать IPv6 listen сокет во время
переконфигурации.
Спасибо Максиму Дунину.
*) Исправление: переменную $uid_set можно использовать на любой стадии
обработки запроса.
Изменения в nginx 0.8.42 21.06.2010
*) Изменение: теперь nginx проверяет location'ы, заданные регулярными
выражениями, если запрос полностью совпал с location'ом, заданным
строкой префикса. Предыдущее поведение появилось в 0.7.1.
*) Добавление: модуль ngx_http_scgi_module.
Спасибо Manlio Perillo.
*) Добавление: в директиве return можно добавлять текст ответа.
Изменения в nginx 0.8.41 15.06.2010
*) Безопасность: рабочий процесс nginx/Windows мог завершаться аварийно
при запросе файла с неверной кодировкой UTF-8.
*) Изменение: теперь nginx разрешает использовать пробелы в строке
запроса.
*) Исправление: директива proxy_redirect неправильно изменяла строку
"Refresh" в заголовке ответа бэкенда.
Спасибо Андрею Андрееву и Максиму Согину.
*) Исправление: nginx не поддерживал путь без имени хоста в строке
"Destination" в заголовке запроса.
Изменения в nginx 0.8.40 07.06.2010
*) Безопасность: теперь nginx/Windows игнорирует имя потока файла по
умолчанию.
Спасибо Jose Antonio Vazquez Gonzalez.
*) Добавление: модуль ngx_http_uwsgi_module.
Спасибо Roberto De Ioris.
*) Добавление: директива fastcgi_param со значением, начинающимся со
строки "HTTP_", изменяет строку заголовка в запросе клиента.
*) Исправление: строки "If-Modified-Since", "If-Range" и им подобные в
заголовке запроса клиента передавались FastCGI-серверу при
кэшировании.
*) Исправление: listen unix domain сокет нельзя было изменить во время
переконфигурации.
Спасибо Максиму Дунину.
Изменения в nginx 0.8.39 31.05.2010
*) Исправление: наследуемая директива alias неправильно работала во
вложенном location'е.
*) Исправление: в комбинации директив alias с переменными и try_files;
*) Исправление: listen unix domain и IPv6 сокеты не наследовались во
время обновления без перерыва.
Спасибо Максиму Дунину.
Изменения в nginx 0.8.38 24.05.2010
*) Добавление: директивы proxy_no_cache и fastcgi_no_cache.
*) Добавление: теперь при использовании переменной $scheme в директиве
rewrite автоматически делается редирект.
Спасибо Piotr Sikora.
*) Исправление: теперь задержки в директиве limit_req соответствует
описанному алгоритму.
Спасибо Максиму Дунину.
*) Исправление: переменную $uid_got нельзя было использовать в SSI и
перловом модулях.
Изменения в nginx 0.8.37 17.05.2010
*) Добавление: модуль ngx_http_split_clients_module.
*) Добавление: директива map поддерживает ключи больше 255 символов.
*) Исправление: nginx игнорировал значения "private" и "no-store" в
строке "Cache-Control" в заголовке ответа бэкенда.
*) Исправление: параметр stub в SSI-директиве include не использовался,
если пустой ответ имел код 200.
*) Исправление: если проксированный или FastCGI запрос внутренне
перенаправлялся в другой проксированный или FastCGI location, то в
рабочем процессе мог произойти segmentation fault; ошибка появилась в
0.8.33.
Спасибо Yichun Zhang.
*) Исправление: соединения IMAP к серверу Zimbra могло зависнуть до
таймаута.
Спасибо Alan Batie.
Изменения в nginx 0.8.36 22.04.2010
*) Исправление: модуль ngx_http_dav_module неправильно обрабатывал
методы DELETE, COPY и MOVE для симлинков.
*) Исправление: модуль SSI в подзапросах использовал закэшированные в
основном запросе значения переменных $query_string, $arg_... и им
подобных.
*) Исправление: значение переменной повторно экранировалось после
каждого вывода SSI-команды echo; ошибка появилась в 0.6.14.
*) Исправление: рабочий процесс зависал при запросе файла FIFO.
Спасибо Vicente Aguilar и Максиму Дунину.
*) Исправление: совместимость с OpenSSL-1.0.0 на 64-битном Linux.
Спасибо Максиму Дунину.
*) Исправление: nginx не собирался с параметром --without-http-cache;
ошибка появилась в 0.8.35.
Изменения в nginx 0.8.35 01.04.2010
*) Изменение: теперь charset-фильтр работает до SSI-фильтра.
*) Добавление: директива chunked_transfer_encoding.
*) Исправление: символ "&" при копировании в аргументы в правилах
rewrite не экранировался.
*) Исправление: nginx мог завершаться аварийно во время обработки
сигнала или при использовании директивы timer_resolution на
платформах, не поддерживающих методы kqueue или eventport.
Спасибо George Xie и Максиму Дунину.
*) Исправление: если временные файлы и постоянное место хранения
располагались на разных файловых системах, то у постоянных файлов
время изменения было неверным.
Спасибо Максиму Дунину.
*) Исправление: модуль ngx_http_memcached_module мог выдавать ошибку
"memcached sent invalid trailer".
Спасибо Максиму Дунину.
*) Исправление: nginx не мог собрать библиотеку zlib-1.2.4 из исходных
текстов.
Спасибо Максиму Дунину.
*) Исправление: в рабочем процессе происходил segmentation fault, если
перед ответом FastCGI-сервера было много вывода в stderr; ошибка
появилась в 0.8.34.
Спасибо Максиму Дунину.
Изменения в nginx 0.8.34 03.03.2010
*) Исправление: nginx не поддерживал все шифры, используемые в
клиентских сертификатах.
Спасибо Иннокентию Еникееву.
*) Исправление: nginx неправильно кэшировал FastCGI-ответы, если перед
ответом было много вывода в stderr.
*) Исправление: nginx не поддерживал HTTPS-рефереры.
*) Исправление: nginx/Windows мог не находить файлы, если путь в
конфигурации был задан в другом регистре; ошибка появилась в 0.8.33.
*) Исправление: переменная $date_local выдавала неверное время, если
использовался формат "%s".
Спасибо Максиму Дунину.
*) Исправление: если ssl_session_cache не был установлен или установлен
в none, то при проверке клиентского сертификаты могла происходить
ошибка "session id context uninitialized"; ошибка появилась в 0.7.1.
*) Исправление: geo-диапазон возвращал значение по умолчанию, если
диапазон включал в себя одну и более сетей размером /16 и не
начинался на границе сети размером /16.
*) Исправление: блок, используемый в параметре stub в SSI-директиве
include, выводился с MIME-типом "text/plain".
*) Исправление: $r->sleep() не работал; ошибка появилась в 0.8.11.
Изменения в nginx 0.8.33 01.02.2010
*) Безопасность: теперь nginx/Windows игнорирует пробелы в конце URI.
Спасибо Dan Crowley, Core Security Technologies.
*) Безопасность: теперь nginx/Windows игнорирует короткие имена файлов.
Спасибо Dan Crowley, Core Security Technologies.
*) Изменение: теперь keepalive соединения после запросов POST не
запрещаются для MSIE 7.0+.
Спасибо Adam Lounds.
*) Изменение: теперь keepalive соединения запрещены для Safari.
Спасибо Joshua Sierles.
*) Исправление: если проксированный или FastCGI запрос внутренне
перенаправлялся в другой проксированный или FastCGI location, то
переменная $upstream_response_time могла иметь ненормально большое
значение; ошибка появилась в 0.8.7.
*) Исправление: в рабочем процессе мог произойти segmentation fault при
отбрасывания тела запроса; ошибка появилась в 0.8.11.
Изменения в nginx 0.8.32 11.01.2010
*) Исправление: ошибки при использовании кодировки UTF-8 в
ngx_http_autoindex_module.
Спасибо Максиму Дунину.
*) Исправление: именованные выделения в регулярных выражениях работали
только для двух переменных.
Спасибо Максиму Дунину.
*) Исправление: теперь в строке заголовка запроса "Host" используется
имя "localhost", если в директиве auth_http указан unix domain сокет.
Спасибо Максиму Дунину.
*) Исправление: nginx не поддерживал передачу chunk'ами для 201-ых
ответов.
Спасибо Julian Reich.
*) Исправление: если директива "expires modified" выставляла дату в
прошлом, то в строке заголовка ответа "Cache-Control" выдавалось
отрицательное число.
Спасибо Алексею Капранову.
Изменения в nginx 0.8.31 23.12.2009
*) Добавление: теперь директива error_page может перенаправлять ответы
со статусом 301 и 302.
*) Добавление: переменные $geoip_city_continent_code, $geoip_latitude и
$geoip_longitude.
Спасибо Arvind Sundararajan.
*) Добавление: модуль ngx_http_image_filter_module теперь всегда удаляет
EXIF и другие данные, если они занимают больше 5% в JPEG-файле.
*) Исправление: nginx закрывал соединение при запросе закэшированного
ответа с пустым телом.
Спасибо Piotr Sikora.
*) Исправление: nginx мог не собираться gcc 4.x при использовании
оптимизации -O2 и выше.
Спасибо Максиму Дунину и Денису Латыпову.
*) Исправление: регулярные выражения в location всегда тестировались с
учётом регистра; ошибка появилась в 0.8.25.
*) Исправление: nginx кэшировал 304 ответ, если в заголовке
проксируемого запроса была строка "If-None-Match".
Спасибо Tim Dettrick и David Kostal.
*) Исправление: nginx/Windows пытался дважды удалить временный файл при
перезаписи уже существующего файла.
Изменения в nginx 0.8.30 15.12.2009
*) Изменение: теперь по умолчанию размер буфера директивы
large_client_header_buffers равен 8K.
Спасибо Andrew Cholakian.
*) Добавление: файл conf/fastcgi.conf для простых конфигураций FastCGI.
*) Исправление: nginx/Windows пытался дважды переименовать временный
файл при перезаписи уже существующего файла.
*) Исправление: ошибки double free or corruption, возникающей, если имя
хоста не было найдено; ошибка появилась в 0.8.22.
Спасибо Константину Свисту.
*) Исправление: в использовании libatomic на некоторых платформах.
Спасибо W-Mark Kubacki.
Изменения в nginx 0.8.29 30.11.2009
*) Изменение: теперь для проксируемых ответов HTTP/0.9 в лог пишется код
ответа "009".
*) Добавление: директивы addition_types, charset_types, gzip_types,
ssi_types, sub_filter_types и xslt_types поддерживают параметр "*".
*) Добавление: использование встроенных атомарных операций GCC 4.1+.
Спасибо W-Mark Kubacki.
*) Добавление: параметр --with-libatomic[=DIR] в configure.
Спасибо W-Mark Kubacki.
*) Исправление: listen unix domain сокет имели ограниченные права
доступа.
*) Исправление: закэшированные ответы ответов HTTP/0.9 неправильно
обрабатывались.
*) Исправление: именованные выделения в регулярных выражениях, заданные
как "?P<...>", не работали в директиве server_name.
Спасибо Максиму Дунину.
Изменения в nginx 0.8.28 23.11.2009
*) Исправление: nginx не собирался с параметром --without-pcre; ошибка
появилась в 0.8.25.
Изменения в nginx 0.8.27 17.11.2009
*) Исправление: регулярные выражения не работали в nginx/Windows; ошибка
появилась в 0.8.25.
Изменения в nginx 0.8.26 16.11.2009
*) Исправление: ошибки при использовании выделений в директиве rewrite;
ошибка появилась в 0.8.25.
*) Исправление: nginx не собирался без параметра --with-debug; ошибка
появилась в 0.8.25.
Изменения в nginx 0.8.25 16.11.2009
*) Изменение: теперь в лог ошибок не пишется сообщение, если переменная
не найдена с помощью метода $r->variable().
*) Добавление: модуль ngx_http_degradation_module.
*) Добавление: именованные выделения в регулярных выражениях.
*) Добавление: теперь при использовании переменных в директиве
proxy_pass не требуется задавать URI.
*) Добавление: теперь директива msie_padding работает и для Chrome.
*) Исправление: в рабочем процессе происходил segmentation fault при
недостатке памяти; ошибка появилась в 0.8.18.
*) Исправление: nginx передавал сжатые ответы клиентам, не
поддерживающим сжатие, при настройках gzip_static on и gzip_vary off;
ошибка появилась в 0.8.16.
Изменения в nginx 0.8.24 11.11.2009
*) Исправление: nginx всегда добавлял строку "Content-Encoding: gzip" в
заголовок 304-ых ответов модуля ngx_http_gzip_static_module.
*) Исправление: nginx не собирался без параметра --with-debug; ошибка
появилась в 0.8.23.
*) Исправление: параметр "unix:" в директиве set_real_ip_from
неправильно наследовался с предыдущего уровня.
*) Исправление: в resolver'е при определении пустого имени.
Изменения в nginx 0.8.23 11.11.2009
*) Безопасность: теперь SSL/TLS renegotiation запрещён.
Спасибо Максиму Дунину.
*) Исправление: listen unix domain сокет не наследовался во время
обновления без перерыва.
*) Исправление: параметр "unix:" в директиве set_real_ip_from не работал
без ещё одной директивы с любым IP-адресом.
*) Исправление: segmentation fault и зацикливания в resolver'е.
*) Исправление: в resolver'е.
Спасибо Артёму Бохану.
Изменения в nginx 0.8.22 03.11.2009
*) Добавление: директивы proxy_bind, fastcgi_bind и memcached_bind.
*) Добавление: директивы access и deny поддерживают IPv6.
*) Добавление: директива set_real_ip_from поддерживает IPv6 адреса в
заголовках запроса.
*) Добавление: параметр "unix:" в директиве set_real_ip_from.
*) Исправление: nginx не удалял unix domain сокет после тестирования
конфигурации.
*) Исправление: nginx удалял unix domain сокет во время обновления без
перерыва.
*) Исправление: оператор "!-x" не работал.
Спасибо Максиму Дунину.
*) Исправление: в рабочем процессе мог произойти segmentation fault при
использовании limit_rate в HTTPS сервере.
Спасибо Максиму Дунину.
*) Исправление: при записи в лог переменной $limit_rate в рабочем
процессе происходил segmentation fault.
Спасибо Максиму Дунину.
*) Исправление: в рабочем процессе мог произойти segmentation fault,
если внутри блока server не было директивы listen; ошибка появилась в
0.8.21.
Изменения в nginx 0.8.21 26.10.2009
*) Добавление: теперь ключ -V показывает статус поддержки TLS SNI.
*) Добавление: директива listen модуля HTTP поддерживает unix domain
сокеты.
Спасибо Hongli Lai.
*) Добавление: параметр "default_server" в директиве listen.
*) Добавление: теперь параметр "default" не обязателен для установки
параметров listen-сокета.
*) Исправление: nginx не поддерживал даты в 2038 году на 32-битных
платформах;
*) Исправление: утечки сокетов; ошибка появилась в 0.8.11.
Изменения в nginx 0.8.20 14.10.2009
*) Изменение: теперь по умолчанию используются следующие шифры SSL:
"HIGH:!ADH:!MD5".
*) Исправление: модуль ngx_http_autoindex_module не показывал последний
слэш для линков на каталоги; ошибка появилась в 0.7.15.
*) Исправление: nginx не закрывал лог, заданный параметром конфигурации
--error-log-path; ошибка появилась в 0.7.53.
*) Исправление: nginx не считал запятую разделителем в строке
"Cache-Control" в заголовке ответа бэкенда.
*) Исправление: nginx/Windows мог не создать временный файл, файл в кэше
или файл с помощью директив proxy/fastcgi_store, если рабочий процесс
не имел достаточно прав для работы с каталогами верхнего уровня.
*) Исправление: строки "Set-Cookie" и "P3P" в заголовке ответа
FastCGI-сервера не скрывались при кэшировании, если не использовались
директивы fastcgi_hide_header с любыми параметрами.
*) Исправление: nginx неверно считал размер кэша на диске.
Изменения в nginx 0.8.19 06.10.2009
*) Изменение: теперь протокол SSLv2 по умолчанию запрещён.
*) Изменение: теперь по умолчанию используются следующие шифры SSL:
"ALL:!ADH:RC4+RSA:+HIGH:+MEDIUM".
*) Исправление: директива limit_req не работала; ошибка появилась в
0.8.18.
Изменения в nginx 0.8.18 06.10.2009
*) Добавление: директива read_ahead.
*) Добавление: теперь можно использовать несколько директив
perl_modules.
*) Добавление: директивы limit_req_log_level и limit_conn_log_level.
*) Исправление: теперь директива limit_req соответствует алгоритму leaky
bucket.
Спасибо Максиму Дунину.
*) Исправление: nginx не работал на Linux/sparc.
Спасибо Marcus Ramberg.
*) Исправление: nginx слал символ '\0' в строке "Location" в заголовке в
ответе на запрос MKCOL.
Спасибо Xie Zhenye.
*) Исправление: вместо кода ответа 499 в лог записывался код 0; ошибка
появилась в 0.8.11.
*) Исправление: утечки сокетов; ошибка появилась в 0.8.11.
Изменения в nginx 0.8.17 28.09.2009
*) Безопасность: теперь символы "/../" запрещены в строке "Destination"
в заголовке запроса.
*) Изменение: теперь значение переменной $host всегда в нижнем регистре.
*) Добавление: переменная $ssl_session_id.
*) Исправление: утечки сокетов; ошибка появилась в 0.8.11.
Изменения в nginx 0.8.16 22.09.2009
*) Добавление: директива image_filter_transparency.
*) Исправление: директива "addition_types" была неверно названа
"addtion_types".
*) Исправление: порчи кэша resolver'а.
Спасибо Matthew Dempsky.
*) Исправление: утечки памяти в resolver'е.
Спасибо Matthew Dempsky.
*) Исправление: неверная строка запроса в переменной $request
записывалась в access_log только при использовании error_log на
уровне info или debug.
*) Исправление: в поддержке альфа-канала PNG в модуле
ngx_http_image_filter_module.
*) Исправление: nginx всегда добавлял строку "Vary: Accept-Encoding" в
заголовок ответа, если обе директивы gzip_static и gzip_vary были
включены.
*) Исправление: в поддержке кодировки UTF-8 директивой try_files в
nginx/Windows.
*) Исправление: ошибки при использовании post_action; ошибка появилась в
0.8.11.
Спасибо Игорю Артемьеву.
Изменения в nginx 0.8.15 14.09.2009
*) Безопасность: при обработке специально созданного запроса в рабочем
процессе мог произойти segmentation fault.
Спасибо Chris Ries.
*) Исправление: если были описаны имена .domain.tld, .sub.domain.tld и
.domain-some.tld, то имя .sub.domain.tld попадало под маску
.domain.tld.
*) Исправление: в поддержке прозрачности в модуле
ngx_http_image_filter_module.
*) Исправление: в файловом AIO.
*) Исправление: ошибки при использовании X-Accel-Redirect; ошибка
появилась в 0.8.11.
*) Исправление: ошибки при использовании встроенного перла; ошибка
появилась в 0.8.11.
Изменения в nginx 0.8.14 07.09.2009
*) Исправление: устаревший закэшированный запрос мог залипнуть в
состоянии "UPDATING".
*) Исправление: при использовании error_log на уровне info или debug в
рабочем процессе мог произойти segmentation fault.
Спасибо Сергею Боченкову.
*) Исправление: ошибки при использовании встроенного перла; ошибка
появилась в 0.8.11.
*) Исправление: директива error_page не перенаправляла ошибку 413;
ошибка появилась в 0.6.10.
Изменения в nginx 0.8.13 31.08.2009
*) Исправление: в директиве "aio sendfile"; ошибка появилась в 0.8.12.
*) Исправление: nginx не собирался без параметра --with-file-aio на
FreeBSD; ошибка появилась в 0.8.12.
Изменения в nginx 0.8.12 31.08.2009
*) Добавление: параметр sendfile в директиве aio во FreeBSD.
*) Исправление: ошибки при использовании try_files; ошибка появилась в
0.8.11.
*) Исправление: ошибки при использовании memcached; ошибка появилась в
0.8.11.
Изменения в nginx 0.8.11 28.08.2009
*) Изменение: теперь директива "gzip_disable msie6" не запрещает сжатие
для MSIE 6.0 SV1.
*) Добавление: поддержка файлового AIO во FreeBSD и Linux.
*) Добавление: директива directio_alignment.
Изменения в nginx 0.8.10 24.08.2009
*) Исправление: утечек памяти при использовании базы GeoIP City.
*) Исправление: ошибки при копировании временных файлов в постоянное
место хранения; ошибка появилась в 0.8.9.
Изменения в nginx 0.8.9 17.08.2009
*) Добавление: теперь стартовый загрузчик кэша работает в отдельном
процесс; это должно улучшить обработку больших кэшей.
*) Добавление: теперь временные файлы и постоянное место хранения могут
располагаться на разных файловых системах.
Изменения в nginx 0.8.8 10.08.2009
*) Исправление: в обработке заголовков ответа, разделённых в
FastCGI-записях.
*) Исправление: если запрос обрабатывался в двух проксированных или
FastCGI location'ах и в первом из них использовалось кэширование, то
в рабочем процессе происходил segmentation fault; ошибка появилась в
0.8.7.
Изменения в nginx 0.8.7 27.07.2009
*) Изменение: минимальная поддерживаемая версия OpenSSL - 0.9.7.
*) Изменение: параметр ask директивы ssl_verify_client изменён на
параметр optional и теперь он проверяет клиентский сертификат, если
он был предложен.
Спасибо Brice Figureau.
*) Добавление: переменная $ssl_client_verify.
Спасибо Brice Figureau.
*) Добавление: директива ssl_crl.
Спасибо Brice Figureau.
*) Добавление: параметр proxy директивы geo.
*) Добавление: директива image_filter поддерживает переменные для
задания размеров.
*) Исправление: использование переменной $ssl_client_cert портило
память; ошибка появилась в 0.7.7.
Спасибо Сергею Журавлёву.
*) Исправление: директивы proxy_pass_header и fastcgi_pass_header" не
передавали клиенту строки "X-Accel-Redirect", "X-Accel-Limit-Rate",
"X-Accel-Buffering" и "X-Accel-Charset" из заголовка ответа бэкенда.
Спасибо Максиму Дунину.
*) Исправление: в обработке строк "Last-Modified" и "Accept-Ranges" в
заголовке ответа бэкенда; ошибка появилась в 0.7.44.
Спасибо Максиму Дунину.
*) Исправление: ошибки "[alert] zero size buf" при получении пустых
ответы в подзапросах; ошибка появилась в 0.8.5.
Изменения в nginx 0.8.6 20.07.2009
*) Добавление: модуль ngx_http_geoip_module.
*) Исправление: XSLT-фильтр мог выдавать ошибку "not well formed XML
document" для правильного документа.
Спасибо Kuramoto Eiji.
*) Исправление: в MacOSX, Cygwin и nginx/Windows при проверке
location'ов, заданных регулярным выражением, теперь всегда делается
сравнение без учёта регистра символов.
*) Исправление: теперь nginx/Windows игнорирует точки в конце URI.
Спасибо Hugo Leisink.
*) Исправление: имя файла указанного в --conf-path игнорировалось при
установке; ошибка появилась в 0.6.6.
Спасибо Максиму Дунину.
Изменения в nginx 0.8.5 13.07.2009
*) Исправление: теперь nginx разрешает подчёркивания в методе запроса.
*) Исправление: при использовании HTTP Basic-аутентификации на Windows
для неверных имени/пароля возвращалась 500-ая ошибка.
*) Исправление: ответы модуля ngx_http_perl_module не работали в
подзапросах.
*) Исправление: в модуле ngx_http_limit_req_module.
Спасибо Максиму Дунину.
Изменения в nginx 0.8.4 22.06.2009
*) Исправление: nginx не собирался с параметром --without-http-cache;
ошибка появилась в 0.8.3.
Изменения в nginx 0.8.3 19.06.2009
*) Добавление: переменная $upstream_cache_status.
*) Исправление: nginx не собирался на MacOSX 10.6.
*) Исправление: nginx не собирался с параметром --without-http-cache;
ошибка появилась в 0.8.2.
*) Исправление: если использовался перехват 401 ошибки от бэкенда и
бэкенд не возвращал строку "WWW-Authenticate" в заголовке ответа, то
в рабочем процессе происходил segmentation fault.
Спасибо Евгению Мычло.
Изменения в nginx 0.8.2 15.06.2009
*) Исправление: во взаимодействии open_file_cache и proxy/fastcgi кэша
на старте.
*) Исправление: open_file_cache мог кэшировать открытые файлы очень
долго; ошибка появилась в 0.7.4.
Изменения в nginx 0.8.1 08.06.2009
*) Добавление: параметр updating в директивах proxy_cache_use_stale и
fastcgi_cache_use_stale.
*) Исправление: строки "If-Modified-Since", "If-Range" и им подобные в
заголовке запроса клиента передавались бэкенду при кэшировании, если
не использовалась директива proxy_set_header с любыми параметрами.
*) Исправление: строки "Set-Cookie" и "P3P" в заголовке ответа бэкенда
не скрывались при кэшировании, если не использовались директивы
proxy_hide_header/fastcgi_hide_header с любыми параметрами.
*) Исправление: модуль ngx_http_image_filter_module не понимал формат
GIF87a.
Спасибо Денису Ильиных.
*) Исправление: nginx не собирался на Solaris 10 и более ранних; ошибка
появилась в 0.7.56.
Изменения в nginx 0.8.0 02.06.2009
*) Добавление: директива keepalive_requests.
*) Добавление: директива limit_rate_after.
Спасибо Ivan Debnar.
*) Исправление: XSLT-фильтр не работал в подзапросах.
*) Исправление: обработке относительных путей в nginx/Windows.
*) Исправление: в proxy_store, fastcgi_store, proxy_cache и
fastcgi_cache в nginx/Windows.
*) Исправление: в обработке ошибок выделения памяти.
Спасибо Максиму Дунину и Кириллу Коринскому.
Изменения в nginx 0.7.59 25.05.2009
*) Добавление: директивы proxy_cache_methods и fastcgi_cache_methods.
*) Исправление: утечки сокетов; ошибка появилась в 0.7.25.
Спасибо Максиму Дунину.
*) Исправление: при использовании переменной $request_body в рабочем
процессе происходил segmentation fault, если в запросе не было тела;
ошибка появилась в 0.7.58.
*) Исправление: SSL-модули могли не собираться на Solaris и Linux;
ошибка появилась в 0.7.56.
*) Исправление: ответы модуля ngx_http_xslt_filter_module не
обрабатывались SSI-, charset- и gzip-фильтрами.
*) Исправление: директива charset не ставила кодировку для ответов
модуля ngx_http_gzip_static_module.
Изменения в nginx 0.7.58 18.05.2009
*) Добавление: директива listen почтового прокси-сервера поддерживает
IPv6.
*) Добавление: директива image_filter_jpeg_quality.
*) Добавление: директива client_body_in_single_buffer.
*) Добавление: переменная $request_body.
*) Исправление: в модуле ngx_http_autoindex_module в ссылках на имена
файлов, содержащих символ ":".
*) Исправление: процедура "make upgrade" не работала; ошибка появилась в
0.7.53.
Спасибо Денису Латыпову.
Изменения в nginx 0.7.57 12.05.2009
*) Исправление: при перенаправлении ошибок модуля
ngx_http_image_filter_module в именованный location в рабочем
процессе происходил floating-point fault; ошибка появилась в 0.7.56.
Изменения в nginx 0.7.56 11.05.2009
*) Добавление: nginx/Windows поддерживает IPv6 в директиве listen модуля
HTTP.
*) Исправление: в модуле ngx_http_image_filter_module.
Изменения в nginx 0.7.55 06.05.2009
*) Исправление: параметры http_XXX в директивах proxy_cache_use_stale и
fastcgi_cache_use_stale не работали.
*) Исправление: fastcgi кэш не кэшировал ответы, состоящие только из
заголовка.
*) Исправление: ошибки "select() failed (9: Bad file descriptor)" в
nginx/Unix и "select() failed (10038: ...)" в nginx/Windows.
*) Исправление: при использовании директивы debug_connection в рабочем
процессе мог произойти segmentation fault; ошибка появилась в 0.7.54.
*) Исправление: в сборке модуля ngx_http_image_filter_module.
*) Исправление: файлы больше 2G не передавались с использованием
$r->sendfile.
Спасибо Максиму Дунину.
Изменения в nginx 0.7.54 01.05.2009
*) Добавление: модуль ngx_http_image_filter_module.
*) Добавление: директивы proxy_ignore_headers и fastcgi_ignore_headers.
*) Исправление: при использовании переменных "open_file_cache_errors on"
в рабочем процессе мог произойти segmentation fault; ошибка появилась
в 0.7.53.
*) Исправление: директива "port_in_redirect off" не работала; ошибка
появилась в 0.7.39.
*) Исправление: улучшение обработки ошибок метода select.
*) Исправление: ошибки "select() failed (10022: ...)" в nginx/Windows.
*) Исправление: в текстовых сообщениях об ошибках в nginx/Windows;
ошибка появилась в 0.7.53.
Изменения в nginx 0.7.53 27.04.2009
*) Изменение: теперь лог, указанный в --error-log-path, создаётся с
самого начала работы.
*) Добавление: теперь ошибки и предупреждения при старте записываются в
error_log и выводятся на stderr.
*) Добавление: при сборке с пустым параметром --prefix= nginx использует
как префикс каталог, в котором он был запущен.
*) Добавление: ключ -p.
*) Добавление: ключ -s на Unix-платформах.
*) Добавление: ключи -? и -h.
Спасибо Jerome Loyet.
*) Добавление: теперь ключи можно задавать в сжатой форме.
*) Исправление: nginx/Windows не работал, если файл конфигурации был
задан ключом -c.
*) Исправление: при использовании директив proxy_store, fastcgi_store,
proxy_cache или fastcgi_cache временные файлы могли не удаляться.
Спасибо Максиму Дунину.
*) Исправление: в заголовке Auth-Method запроса серверу аутентификации
почтового прокси-сервера передавалось неверное значение; ошибка
появилась в 0.7.34.
Спасибо Simon Lecaille.
*) Исправление: при логгировании на Linux не писались текстовые описания
системных ошибок; ошибка появилась в 0.7.45.
*) Исправление: директива fastcgi_cache_min_uses не работала.
Спасибо Андрею Воробьёву.
Изменения в nginx 0.7.52 20.04.2009
*) Добавление: первая бинарная версия под Windows.
*) Исправление: корректная обработка метода HEAD при кэшировании.
*) Исправление: корректная обработка строк "If-Modified-Since",
"If-Range" и им подобных в заголовке запроса клиента при кэшировании.
*) Исправление: теперь строки "Set-Cookie" и "P3P" скрываются в
заголовке ответа для закэшированных ответов.
*) Исправление: если nginx был собран с модулем ngx_http_perl_module и
perl поддерживал потоки, то при выходе основного процесса могла
выдаваться ошибка "panic: MUTEX_LOCK".
*) Исправление: nginx не собирался с параметром --without-http-cache;
ошибка появилась в 0.7.48.
*) Исправление: nginx не собирался на платформах, отличных от i386,
amd64, sparc и ppc; ошибка появилась в 0.7.42.
Изменения в nginx 0.7.51 12.04.2009
*) Добавление: директива try_files поддерживает код ответа в последнем
параметре.
*) Добавление: теперь в директиве return можно использовать любой код
ответа.
*) Исправление: директива error_page делала внешний редирект без строки
запроса; ошибка появилась в 0.7.44.
*) Исправление: если сервера слушали на нескольких явно описанных
адресах, то виртуальные сервера могли не работать; ошибка появилась в
0.7.39.
Изменения в nginx 0.7.50 06.04.2009
*) Исправление: переменные $arg_... не работали; ошибка появилась в
0.7.49.
Изменения в nginx 0.7.49 06.04.2009
*) Исправление: при использовании переменных $arg_... в рабочем процессе
мог произойти segmentation fault; ошибка появилась в 0.7.48.
Изменения в nginx 0.7.48 06.04.2009
*) Добавление: директива proxy_cache_key.
*) Исправление: теперь nginx учитывает при кэшировании строки
"X-Accel-Expires", "Expires" и "Cache-Control" в заголовке ответа
бэкенда.
*) Исправление: теперь nginx кэширует только ответы на запросы GET.
*) Исправление: директива fastcgi_cache_key не наследовалась.
*) Исправление: переменные $arg_... не работали с SSI-подзапросами.
Спасибо Максиму Дунину.
*) Исправление: nginx не собирался с библиотекой uclibc.
Спасибо Timothy Redaelli.
*) Исправление: nginx не собирался на OpenBSD; ошибка появилась
в 0.7.46.
Изменения в nginx 0.7.47 01.04.2009
*) Исправление: nginx не собирался на FreeBSD 6 и более ранних версиях;
ошибка появилась в 0.7.46.
*) Исправление: nginx не собирался на MacOSX; ошибка появилась в 0.7.46.
*) Исправление: если использовался параметр max_size, то cache manager
мог удалить весь кэш; ошибка появилась в 0.7.46.
*) Изменение: в рабочем процессе мог произойти segmentation fault, если
директивы proxy_cache/fastcgi_cache и proxy_cache_valid/
fastcgi_cache_valid не были заданы на одном уровне; ошибка появилась
в 0.7.46.
*) Исправление: в рабочем процессе мог произойти segmentation fault при
перенаправлении запроса проксированному или FastCGI-серверу с помощью
error_page или try_files; ошибка появилась в 0.7.44.
Изменения в nginx 0.7.46 30.03.2009
*) Исправление: архив предыдущего релиза был неверным.
Изменения в nginx 0.7.45 30.03.2009
*) Изменение: теперь директивы proxy_cache и proxy_cache_valid можно
задавать на разных уровнях.
*) Изменение: параметр clean_time в директиве proxy_cache_path удалён.
*) Добавление: параметр max_size в директиве proxy_cache_path.
*) Добавление: предварительная поддержка кэширования в модуле
ngx_http_fastcgi_module.
*) Добавление: теперь при ошибках выделения в разделяемой памяти в логе
указываются названия директивы и зоны.
*) Исправление: директива "add_header last-modified ''" не удаляла в
заголовке ответа строку "Last-Modified"; ошибка появилась в 0.7.44.
*) Исправление: в директиве auth_basic_user_file не работал
относительный путь, заданный строкой без переменных; ошибка появилась
в 0.7.44.
Спасибо Jerome Loyet.
*) Исправление: в директиве alias, заданной переменными без ссылок на
выделения в регулярных выражениях; ошибка появилась в 0.7.42.
Изменения в nginx 0.7.44 23.03.2009
*) Добавление: предварительная поддержка кэширования в модуле
ngx_http_proxy_module.
*) Добавление: параметр --with-pcre в configure.
*) Добавление: теперь директива try_files может быть использована на
уровне server.
*) Исправление: директива try_files неправильно обрабатывала строку
запроса в последнем параметре.
*) Исправление: директива try_files могла неверно тестировать каталоги.
*) Исправление: если для пары адрес:порт описан только один сервер, то
выделения в регулярных выражениях в директиве server_name не
работали.
Изменения в nginx 0.7.43 18.03.2009
*) Исправление: запрос обрабатывался неверно, если директива root
использовала переменные; ошибка появилась в 0.7.42.
*) Исправление: если сервер слушал на адресах типа "*", то значение
переменной $server_addr было "0.0.0.0"; ошибка появилась в 0.7.36.
Изменения в nginx 0.7.42 16.03.2009
*) Изменение: ошибка "Invalid argument", возвращаемая
setsockopt(TCP_NODELAY) на Solaris, теперь игнорируется.
*) Изменение: при отсутствии файла, указанного в директиве
auth_basic_user_file, теперь возвращается ошибка 403 вместо 500.
*) Добавление: директива auth_basic_user_file поддерживает переменные.
Спасибо Кириллу Коринскому.
*) Добавление: директива listen поддерживает параметр ipv6only.
Спасибо Zhang Hua.
*) Исправление: в директиве alias со ссылками на выделения в регулярных
выражениях; ошибка появилась в 0.7.40.
*) Исправление: совместимость с Tru64 UNIX.
Спасибо Dustin Marquess.
*) Исправление: nginx не собирался без библиотеки PCRE; ошибка появилась
в 0.7.41.
Изменения в nginx 0.7.41 11.03.2009
*) Исправление: в рабочем процессе мог произойти segmentation fault,
если в server_name или location были выделения в регулярных
выражениях; ошибка появилась в 0.7.40.
Спасибо Владимиру Сопоту.
Изменения в nginx 0.7.40 09.03.2009
*) Добавление: директива location поддерживает выделения в регулярных
выражениях.
*) Добавление: директиву alias с ссылками на выделения в регулярных
выражениях можно использовать внутри location'а, заданного регулярным
выражением с выделениями.
*) Добавление: директива server_name поддерживает выделения в регулярных
выражениях.
*) Изменение: модуль ngx_http_autoindex_module не показывал последний
слэш для каталогов на файловой системе XFS; ошибка появилась в
0.7.15.
Спасибо Дмитрию Кузьменко.
Изменения в nginx 0.7.39 02.03.2009
*) Исправление: при включённом сжатии большие ответы с использованием
SSI могли зависать; ошибка появилась в 0.7.28.
Спасибо Артёму Бохану.
*) Исправление: при использовании коротких статических вариантов в
директиве try_files в рабочем процессе мог произойти segmentation
fault.
Изменения в nginx 0.7.38 23.02.2009
*) Добавление: логгирование ошибок аутентификации.
*) Исправление: имя/пароль, заданные в auth_basic_user_file,
игнорировались после нечётного числа пустых строк.
Спасибо Александру Загребину.
*) Исправление: при использовании длинного пути в unix domain сокете в
главном процессе происходил segmentation fault; ошибка появилась в
0.7.36.
Изменения в nginx 0.7.37 21.02.2009
*) Исправление: директивы, использующие upstream'ы, не работали; ошибка
появилась в 0.7.36.
Изменения в nginx 0.7.36 21.02.2009
*) Добавление: предварительная поддержка IPv6; директива listen модуля
HTTP поддерживает IPv6.
*) Исправление: переменная $ancient_browser не работала для браузеров,
заданных директивами modern_browser.
Изменения в nginx 0.7.35 16.02.2009
*) Исправление: директива ssl_engine не использовала SSL-акселератор для
асимметричных шифров.
Спасибо Marcin Gozdalik.
*) Исправление: директива try_files выставляла MIME-type, исходя из
расширения первоначального запроса.
*) Исправление: в директивах server_name, valid_referers и map
неправильно обрабатывались имена вида "*domain.tld", если
использовались маски вида ".domain.tld" и ".subdomain.domain.tld";
ошибка появилась в 0.7.9.
Изменения в nginx 0.7.34 10.02.2009
*) Добавление: параметр off в директиве if_modified_since.
*) Добавление: теперь после команды XCLIENT nginx посылает команду
HELO/EHLO.
Спасибо Максиму Дунину.
*) Добавление: поддержка Microsoft-специфичного режима
"AUTH LOGIN with User Name" в почтовом прокси-сервере.
Спасибо Максиму Дунину.
*) Исправление: в директиве rewrite, возвращающей редирект, старые
аргументы присоединялись к новым через символ "?" вместо "&";
ошибка появилась в 0.1.18.
Спасибо Максиму Дунину.
*) Исправление: nginx не собирался на AIX.
Изменения в nginx 0.7.33 02.02.2009
*) Исправление: если на запрос с телом возвращался редирект, то ответ
мог быть двойным при использовании методов epoll или rtsig.
Спасибо Eden Li.
*) Исправление: для некоторых типов редиректов в переменной
$sent_http_location было пустое значение.
*) Исправление: при использовании директивы resolver в SMTP
прокси-сервере в рабочем процессе мог произойти segmentation fault.
Изменения в nginx 0.7.32 26.01.2009
*) Добавление: теперь в директиве try_files можно явно указать проверку
каталога.
*) Исправление: fastcgi_store не всегда сохранял файлы.
*) Исправление: в гео-диапазонах.
*) Исправление: ошибки выделения больших блоков в разделяемой памяти,
если nginx был собран без отладки.
Спасибо Андрею Квасову.
Изменения в nginx 0.7.31 19.01.2009
*) Изменение: теперь директива try_files проверяет только файлы,
игнорируя каталоги.
*) Добавление: директива fastcgi_split_path_info.
*) Исправления в поддержке строки "Expect" в заголовке запроса.
*) Исправления в гео-диапазонах.
*) Исправление: при отсутствии ответа ngx_http_memcached_module
возвращал в теле ответа строку "END" вместо 404-ой страницы по
умолчанию; ошибка появилась в 0.7.18.
Спасибо Максиму Дунину.
*) Исправление: при проксировании SMTP nginx выдавал сообщение
"250 2.0.0 OK" вместо "235 2.0.0 OK"; ошибка появилась в 0.7.22.
Спасибо Максиму Дунину.
Изменения в nginx 0.7.30 24.12.2008
*) Исправление: в рабочем процессе происходил segmentation fault, если в
директивах fastcgi_pass или proxy_pass использовались переменные и
имя хоста должно было резолвиться; ошибка появилась в 0.7.29.
Изменения в nginx 0.7.29 24.12.2008
*) Исправление: директивы fastcgi_pass и proxy_pass не поддерживали
переменные при использовании unix domain сокетов.
*) Исправления в обработке подзапросов; ошибки появились в 0.7.25.
*) Исправление: ответ "100 Continue" выдавался для запросов версии
HTTP/1.0;
Спасибо Максиму Дунину.
*) Исправление: в выделении памяти в модуле ngx_http_gzip_filter_module
под Cygwin.
Изменения в nginx 0.7.28 22.12.2008
*) Изменение: в выделении памяти в модуле ngx_http_gzip_filter_module.
*) Изменение: значения по умолчанию для директивы gzip_buffers изменены
с 4 4k/8k на 32 4k или 16 8k.
Изменения в nginx 0.7.27 15.12.2008
*) Добавление: директива try_files.
*) Добавление: директива fastcgi_pass поддерживает переменные.
*) Добавление: теперь директива geo может брать адрес из переменной.
Спасибо Андрею Нигматулину.
*) Добавление: теперь модификатор location'а можно указывать без пробела
перед названием.
*) Добавление: переменная $upstream_response_length.
*) Исправление: теперь директива add_header не добавляет пустое
значение.
*) Исправление: при запросе файла нулевой длины nginx закрывал
соединение, ничего не передав; ошибка появилась в 0.7.25.
*) Исправление: метод MOVE не мог перемещать файл в несуществующий
каталог.
*) Исправление: если в сервере не был описан ни один именованный
location, но такой location использовался в директиве error_page, то
в рабочем процессе происходил segmentation fault.
Спасибо Сергею Боченкову.
Изменения в nginx 0.7.26 08.12.2008
*) Исправление: в обработке подзапросов; ошибка появилась в 0.7.25.
Изменения в nginx 0.7.25 08.12.2008
*) Изменение: в обработке подзапросов.
*) Изменение: теперь разрешаются POST'ы без строки "Content-Length" в
заголовке запроса.
*) Исправление: теперь директивы limit_req и limit_conn указывают
причину запрета запроса.
*) Исправление: в параметре delete директивы geo.
Изменения в nginx 0.7.24 01.12.2008
*) Добавление: директива if_modified_since.
*) Исправление: nginx не обрабатывал ответ FastCGI-сервера, если перед
ответом сервер передавал много сообщений в stderr.
*) Исправление: переменные "$cookie_..." не работали в SSI and в
перловом модуле.
Изменения в nginx 0.7.23 27.11.2008
*) Добавление: параметры delete и ranges в директиве geo.
*) Добавление: ускорение загрузки geo-базы с большим числом значений.
*) Добавление: уменьшение памяти, необходимой для загрузки geo-базы.
Изменения в nginx 0.7.22 20.11.2008
*) Добавление: параметр none в директиве smtp_auth.
Спасибо Максиму Дунину.
*) Добавление: переменные "$cookie_...".
*) Исправление: директива directio не работала с файловой системой XFS.
*) Исправление: resolver не понимал большие DNS-ответы.
Спасибо Zyb.
Изменения в nginx 0.7.21 11.11.2008
*) Изменения в модуле ngx_http_limit_req_module.
*) Добавление: поддержка EXSLT в модуле ngx_http_xslt_module.
Спасибо Денису Латыпову.
*) Изменение: совместимость с glibc 2.3.
Спасибо Eric Benson и Максиму Дунину.
*) Исправление: nginx не запускался на MacOSX 10.4 и более ранних;
ошибка появилась в 0.7.6.
Изменения в nginx 0.7.20 10.11.2008
*) Изменения в модуле ngx_http_gzip_filter_module.
*) Добавление: модуль ngx_http_limit_req_module.
*) Исправление: на платформах sparc и ppc рабочие процессы могли
выходить по сигналу SIGBUS; ошибка появилась в 0.7.3.
Спасибо Максиму Дунину.
*) Исправление: директивы вида "proxy_pass http://host/some:uri" не
работали; ошибка появилась в 0.7.12.
*) Исправление: при использовании HTTPS запросы могли завершаться с
ошибкой "bad write retry".
*) Исправление: модуль ngx_http_secure_link_module не работал внутри
location'ов с именами меньше 3 символов.
*) Исправление: переменная $server_addr могла не иметь значения.
Изменения в nginx 0.7.19 13.10.2008
*) Исправление: обновление номера версии.
Изменения в nginx 0.7.18 13.10.2008
*) Изменение: директива underscores_in_headers; теперь nginx по
умолчанию не разрешает подчёркивания в именах строк в заголовке
запроса клиента.
*) Добавление: модуль ngx_http_secure_link_module.
*) Добавление: директива real_ip_header поддерживает любой заголовок.
*) Добавление: директива log_subrequest.
*) Добавление: переменная $realpath_root.
*) Добавление: параметры http_502 и http_504 в директиве
proxy_next_upstream.
*) Исправление: параметр http_503 в директивах proxy_next_upstream или
fastcgi_next_upstream не работал.
*) Исправление: nginx мог выдавать строку "Transfer-Encoding: chunked"
для запросов HEAD.
*) Исправление: теперь accept-лимит зависит от числа worker_connections.
Изменения в nginx 0.7.17 15.09.2008
*) Добавление: директива directio теперь работает на Linux.
*) Добавление: переменная $pid.
*) Исправление: оптимизация directio, появившаяся в 0.7.15, не работала
при использовании open_file_cache.
*) Исправление: access_log с переменными не работал на Linux; ошибка
появилась в 0.7.7.
*) Исправление: модуль ngx_http_charset_module не понимал название
кодировки в кавычках, полученное от бэкенда.
Изменения в nginx 0.7.16 08.09.2008
*) Исправление: nginx не собирался на 64-битных платформах; ошибка
появилась в 0.7.15.
Изменения в nginx 0.7.15 08.09.2008
*) Добавление: модуль ngx_http_random_index_module.
*) Добавление: директива directio оптимизирована для запросов файлов,
начинающихся с произвольной позиции.
*) Добавление: директива directio при необходимости запрещает
использование sendfile.
*) Добавление: теперь nginx разрешает подчёркивания в именах строк в
заголовке запроса клиента.
Изменения в nginx 0.7.14 01.09.2008
*) Изменение: теперь директивы ssl_certificate и ssl_certificate_key не
имеют значений по умолчанию.
*) Добавление: директива listen поддерживает параметр ssl.
*) Добавление: теперь при переконфигурации nginx учитывает изменение
временной зоны на FreeBSD и Linux.
*) Исправление: параметры директивы listen, такие как backlog, rcvbuf и
прочие, не устанавливались, если сервером по умолчанию был не первый
сервер.
*) Исправление: при использовании в качестве аргументов части URI,
выделенного с помощью директивы rewrite, эти аргументы не
экранировались.
*) Исправление: улучшения тестирования правильности конфигурационного
файла.
Изменения в nginx 0.7.13 26.08.2008
*) Исправление: nginx не собирался на Linux и Solaris; ошибка появилась
в 0.7.12.
Изменения в nginx 0.7.12 26.08.2008
*) Добавление: директива server_name поддерживает пустое имя "".
*) Добавление: директива gzip_disable поддерживает специальную маску
msie6.
*) Исправление: при использовании параметра max_fails=0 в upstream'е с
несколькими серверами рабочий процесс выходил по сигналу SIGFPE.
Спасибо Максиму Дунину.
*) Исправление: при перенаправлении запроса с помощью директивы
error_page терялось тело запроса.
*) Исправление: при перенаправлении запроса с методом HEAD с помощью
директивы error_page возвращался полный ответ.
*) Исправление: метод $r->header_in() не возвращал значения строк
"Host", "User-Agent", и "Connection" из заголовка запроса; ошибка
появилась в 0.7.0.
Изменения в nginx 0.7.11 18.08.2008
*) Изменение: теперь ngx_http_charset_module по умолчанию не работает
MIME-типом text/css.
*) Добавление: теперь nginx возвращает код 405 для метода POST при
запросе статического файла, только если файл существует.
*) Добавление: директива proxy_ssl_session_reuse.
*) Исправление: после перенаправления запроса с помощью
"X-Accel-Redirect" директива proxy_pass без URI могла использовать
оригинальный запрос.
*) Исправление: если у каталога были права доступа только на поиск
файлов и первый индексный файл отсутствовал, то nginx возвращал
ошибку 500.
*) Исправление: ошибок во вложенных location'ах; ошибки появились в
0.7.1.
Изменения в nginx 0.7.10 13.08.2008
*) Исправление: ошибок в директивах addition_types, charset_types,
gzip_types, ssi_types, sub_filter_types и xslt_types; ошибки
появились в 0.7.9.
*) Исправление: рекурсивной error_page для 500 ошибки.
*) Исправление: теперь модуль ngx_http_realip_module устанавливает адрес
не для всего keepalive соединения, а для каждого запроса по этому
соединению.
Изменения в nginx 0.7.9 12.08.2008
*) Изменение: теперь ngx_http_charset_module по умолчанию работает со
следующими MIME-типами: text/html, text/css, text/xml, text/plain,
text/vnd.wap.wml, application/x-javascript и application/rss+xml.
*) Добавление: директивы charset_types и addition_types.
*) Добавление: теперь директивы gzip_types, ssi_types и sub_filter_types
используют хэш.
*) Добавление: модуль ngx_cpp_test_module.
*) Добавление: директива expires поддерживает суточное время.
*) Добавление: улучшения и исправления в модуле ngx_http_xslt_module.
Спасибо Денису Латыпову и Максиму Дунину.
*) Исправление: директива log_not_found не работала при поиске индексных
файлов.
*) Исправление: HTTPS-соединения могли зависнуть, если использовались
методы kqueue, epoll, rtsig или eventport; ошибка появилась в 0.7.7.
*) Исправление: если в директивах server_name, valid_referers и map
использовалась маска вида "*.domain.tld" и при этом полное имя вида
"domain.tld" не было описано, то это имя попадало под маску; ошибка
появилась в 0.3.18.
Изменения в nginx 0.7.8 04.08.2008
*) Добавление: модуль ngx_http_xslt_module.
*) Добавление: переменные "$arg_...".
*) Добавление: поддержка directio в Solaris.
Спасибо Ivan Debnar.
*) Исправление: теперь, если FastCGI-сервер присылает строку "Location"
в заголовке ответа без строки статуса, то nginx использует код
статуса 302.
Спасибо Максиму Дунину.
Изменения в nginx 0.7.7 30.07.2008
*) Изменение: теперь ошибка EAGAIN при вызове connect() не считается
временной.
*) Изменение: значением переменной $ssl_client_cert теперь является
сертификат, перед каждой строкой которого, кроме первой, вставляется
символ табуляции; неизменённый сертификат доступен через переменную
$ssl_client_raw_cert.
*) Добавление: параметр ask директивы ssl_verify_client.
*) Добавление: улучшения в обработке byte-range.
Спасибо Максиму Дунину.
*) Добавление: директива directio.
Спасибо Jiang Hong.
*) Добавление: поддержка sendfile() в MacOSX 10.5.
*) Исправление: в MacOSX и Cygwin при проверке location'ов теперь
делается сравнение без учёта регистра символов; однако, сравнение
ограничено только однобайтными locale'ями.
*) Исправление: соединения почтового прокси-сервера зависали в режиме
SSL, если использовались методы select, poll или /dev/poll.
*) Исправление: ошибки при использовании кодировки UTF-8 в
ngx_http_autoindex_module.
Изменения в nginx 0.7.6 07.07.2008
*) Исправление: теперь при использовании переменных в директиве
access_log всегда проверяется существовании root'а для запроса.
*) Исправление: модуль ngx_http_flv_module не поддерживал несколько
значений в аргументах запроса.
Изменения в nginx 0.7.5 01.07.2008
*) Исправления в поддержке переменных в директиве access_log; ошибки
появились в 0.7.4.
*) Исправление: nginx не собирался с параметром
--without-http_gzip_module; ошибка появилась в 0.7.3.
Спасибо Кириллу Коринскому.
*) Исправление: при совместном использовании sub_filter и SSI ответы
могли передаваться неверно.
Изменения в nginx 0.7.4 30.06.2008
*) Добавление: директива access_log поддерживает переменные.
*) Добавление: директива open_log_file_cache.
*) Добавление: ключ -g.
*) Добавление: поддержка строки "Expect" в заголовке запроса.
*) Исправление: большие включения в SSI могли передавались не полностью.
Изменения в nginx 0.7.3 23.06.2008
*) Изменение: MIME-тип для расширения rss изменён на
"application/rss+xml".
*) Изменение: теперь директива "gzip_vary on" выдаёт строку
"Vary: Accept-Encoding" в заголовке ответа и для несжатых ответов.
*) Добавление: теперь при использовании протокола "https://" в директиве
rewrite автоматически делается редирект.
*) Исправление: директива proxy_pass не работала с протоколом HTTPS;
ошибка появилась в 0.6.9.
Изменения в nginx 0.7.2 16.06.2008
*) Добавление: теперь nginx поддерживает шифры с обменом EDH-ключами.
*) Добавление: директива ssl_dhparam.
*) Добавление: переменная $ssl_client_cert.
Спасибо Manlio Perillo.
*) Исправление: после изменения URI с помощью директивы rewrite nginx не
искал новый location; ошибка появилась в 0.7.1.
Спасибо Максиму Дунину.
*) Исправление: nginx не собирался без библиотеки PCRE; ошибка появилась
в 0.7.1.
*) Исправление: при редиректе запроса к каталогу с добавлением слэша
nginx не добавлял аргументы из оригинального запроса.
Изменения в nginx 0.7.1 26.05.2008
*) Изменение: теперь поиск location'а делается с помощью дерева.
*) Изменение: директива optimize_server_names упразднена в связи с
появлением директивы server_name_in_redirect.
*) Изменение: некоторые давно устаревшие директивы больше не
поддерживаются.
*) Изменение: параметр "none" в директиве ssl_session_cache; теперь этот
параметр используется по умолчанию.
Спасибо Rob Mueller.
*) Исправление: рабочие процессы могли не реагировать на сигналы
переконфигурации и ротации логов.
*) Исправление: nginx не собирался на последних Fedora 9 Linux.
Спасибо Roxis.
Изменения в nginx 0.7.0 19.05.2008
*) Изменение: теперь символы 0x00-0x1F, '"' и '\' в access_log
записываются в виде \xXX.
Спасибо Максиму Дунину.
*) Изменение: теперь nginx разрешает несколько строк "Host" в заголовке
запроса.
*) Добавление: директива expires поддерживает флаг modified.
*) Добавление: переменные $uid_got и $uid_set можно использовать на
любой стадии обработки запроса.
*) Добавление: переменная $hostname.
Спасибо Андрею Нигматулину.
*) Добавление: поддержка DESTDIR.
Спасибо Todd A. Fisher и Andras Voroskoi.
*) Исправление: при использовании keepalive на Linux в рабочем процессе
мог произойти segmentation fault.
Изменения в nginx 0.6.31 12.05.2008
*) Исправление: nginx не обрабатывал ответ FastCGI-сервера, если строка
заголовка ответа была в конце записи FastCGI; ошибка появилась в
0.6.2.
Спасибо Сергею Серову.
*) Исправление: при удалении файла и использовании директивы
open_file_cache_errors off в рабочем процессе мог произойти
segmentation fault.
Изменения в nginx 0.6.30 29.04.2008
*) Изменение: теперь, если маске, заданной в директиве include, не
соответствует ни один файл, то nginx не выдаёт ошибку.
*) Добавление: теперь время в директивах можно задавать без пробела,
например, "1h50m".
*) Исправление: утечек памяти, если директива ssl_verify_client имела
значение on.
Спасибо Chavelle Vincent.
*) Исправление: директива sub_filter могла вставлять заменяемый текст в
вывод.
*) Исправление: директива error_page не воспринимала параметры в
перенаправляемом URI.
*) Исправление: теперь при сборке с Cygwin nginx всегда открывает файлы
в бинарном режиме.
*) Исправление: nginx не собирался под OpenBSD; ошибка появилась в
0.6.15.
Изменения в nginx 0.6.29 18.03.2008
*) Добавление: модуль ngx_google_perftools_module.
*) Исправление: модуль ngx_http_perl_module не собирался на 64-битных
платформах; ошибка появилась в 0.6.27.
Изменения в nginx 0.6.28 13.03.2008
*) Исправление: метод rtsig не собирался; ошибка появилась в 0.6.27.
Изменения в nginx 0.6.27 12.03.2008
*) Изменение: теперь на Linux 2.6.18+ по умолчанию не собирается метод
rtsig.
*) Изменение: теперь при перенаправлении запроса в именованный location
с помощью директивы error_page метод запроса не изменяется.
*) Добавление: директивы resolver и resolver_timeout в SMTP
прокси-сервере.
*) Добавление: директива post_action поддерживает именованные
location'ы.
*) Исправление: при перенаправлении запроса из location'а c обработчиком
proxy, FastCGI или memcached в именованный location со статическим
обработчиком в рабочем процессе происходил segmentation fault.
*) Исправление: браузеры не повторяли SSL handshake, если при первом
handshake не оказалось правильного клиентского сертификата.
Спасибо Александру Инюхину.
*) Исправление: при перенаправлении ошибок 495-497 с помощью директивы
error_page без изменения кода ошибки nginx пытался выделить очень
много памяти.
*) Исправление: утечки памяти в долгоживущих небуфферизированных
соединениях.
*) Исправление: утечки памяти в resolver'е.
*) Исправление: при перенаправлении запроса из location'а c обработчиком
proxy в другой location с обработчиком proxy в рабочем процессе
происходил segmentation fault.
*) Исправление: ошибки в кэшировании переменных $proxy_host и
$proxy_port.
Спасибо Сергею Боченкову.
*) Исправление: директива proxy_pass с переменными использовала порт,
описанной в другой директиве proxy_pass без переменных, но с таким же
именем хоста.
Спасибо Сергею Боченкову.
*) Исправление: во время переконфигурации на некоторых 64-битном
платформах в лог записывался alert "sendmsg() failed (9: Bad file
descriptor)".
*) Исправление: при повторном использовании в SSI пустого block'а в
качестве заглушки в рабочем процессе происходил segmentation fault.
*) Исправление: ошибки при копировании части URI, содержащего
экранированные символы, в аргументы.
Изменения в nginx 0.6.26 11.02.2008
*) Исправление: директивы proxy_store и fastcgi_store не проверяли длину
ответа.
*) Исправление: при использовании большого значения в директиве expires
в рабочем процессе происходил segmentation fault.
Спасибо Joaquin Cuenca Abela.
*) Исправление: nginx неверно определял длину строки кэша на Pentium 4.
Спасибо Геннадию Махомеду.
*) Исправление: в проксированных подзапросах и подзапросах к
FastCGI-серверу вместо метода GET использовался оригинальный метод
клиента.
*) Исправление: утечки сокетов в режиме HTTPS при использовании
отложенного accept'а.
Спасибо Ben Maurer.
*) Исправление: nginx выдавал ошибочное сообщение "SSL_shutdown() failed
(SSL: )"; ошибка появилась в 0.6.23.
*) Исправление: при использовании HTTPS запросы могли завершаться с
ошибкой "bad write retry"; ошибка появилась в 0.6.23.
Изменения в nginx 0.6.25 08.01.2008
*) Изменение: вместо специального параметра "*" в директиве server_name
теперь используется директива server_name_in_redirect.
*) Изменение: в качестве основного имени в директиве server_name теперь
можно использовать имена с масками и регулярными выражениями.
*) Изменение: директива satisfy_any заменена директивой satisfy.
*) Изменение: после переконфигурации старые рабочие процесс могли сильно
нагружать процессор при запуске под Linux OpenVZ.
*) Добавление: директива min_delete_depth.
*) Исправление: методы COPY и MOVE не работали с одиночными файлами.
*) Исправление: модуль ngx_http_gzip_static_module не позволял работать
модулю ngx_http_dav_module; ошибка появилась в 0.6.23.
*) Исправление: утечки сокетов в режиме HTTPS при использовании
отложенного accept'а.
Спасибо Ben Maurer.
*) Исправление: nginx не собирался без библиотеки PCRE; ошибка появилась
в 0.6.23.
Изменения в nginx 0.6.24 27.12.2007
*) Исправление: при использовании HTTPS в рабочем процессе мог произойти
segmentation fault; ошибка появилась в 0.6.23.
Изменения в nginx 0.6.23 27.12.2007
*) Изменение: параметр "off" в директиве ssl_session_cache; теперь этот
параметр используется по умолчанию.
*) Изменение: директива open_file_cache_retest переименована в
open_file_cache_valid.
*) Добавление: директива open_file_cache_min_uses.
*) Добавление: модуль ngx_http_gzip_static_module.
*) Добавление: директива gzip_disable.
*) Добавление: директиву memcached_pass можно использовать внутри блока
if.
*) Исправление: если внутри одного location'а использовались директивы
"memcached_pass" и "if", то в рабочем процессе происходил
segmentation fault.
*) Исправление: если при использовании директивы satisfy_any on" были
заданы директивы не всех модулей доступа, то заданные директивы не
проверялись.
*) Исправление: параметры, заданные регулярным выражением в директиве
valid_referers, не наследовалась с предыдущего уровня.
*) Исправление: директива post_action не работала, если запрос
завершался с кодом 499.
*) Исправление: оптимизация использования 16K буфера для SSL-соединения.
Спасибо Ben Maurer.
*) Исправление: STARTTLS в режиме SMTP не работал.
Спасибо Олегу Мотиенко.
*) Исправление: при использовании HTTPS запросы могли завершаться с
ошибкой "bad write retry"; ошибка появилась в 0.5.13.
Изменения в nginx 0.6.22 19.12.2007
*) Изменение: теперь все методы модуля ngx_http_perl_module возвращают
значения, скопированные в память, выделенную perl'ом.
*) Исправление: если nginx был собран с модулем ngx_http_perl_module,
использовался perl до версии 5.8.6 и perl поддерживал потоки, то во
время переконфигурации основной процесс аварийно выходил; ошибка
появилась в 0.5.9.
Спасибо Борису Жмурову.
*) Исправление: в методы модуля ngx_http_perl_module могли передаваться
неверные результаты выделения в регулярных выражениях.
*) Исправление: если метод $r->has_request_body() вызывался для запроса,
у которого небольшое тело запроса было уже полностью получено, то в
рабочем процессе происходил segmentation fault.
*) Исправление: large_client_header_buffers не освобождались перед
переходом в состояние keep-alive.
Спасибо Олександру Штепе.
*) Исправление: в переменной $upstream_addr не записывался последний
адрес; ошибка появилась в 0.6.18.
*) Исправление: директива fastcgi_catch_stderr не возвращала ошибку;
теперь она возвращает ошибку 502, которую можно направить на
следующий сервер с помощью "fastcgi_next_upstream invalid_header".
*) Исправление: при использовании директивы fastcgi_catch_stderr в
основном процессе происходил segmentation fault; ошибка появилась в
0.6.10.
Спасибо Manlio Perillo.
Изменения в nginx 0.6.21 03.12.2007
*) Изменение: если в значениях переменных директивы proxy_pass
используются только IP-адреса, то указывать resolver не нужно.
*) Исправление: при использовании директивы proxy_pass c URI-частью в
рабочем процессе мог произойти segmentation fault; ошибка появилась в
0.6.19.
*) Исправление: если resolver использовался на платформах, не
поддерживающих метод kqueue, то nginx выдавал alert "name is out of
response".
Спасибо Андрею Нигматулину.
*) Исправление: При использовании переменной $server_protocol в
FastCGI-параметрах и запросе, длина которого была близка к значению
директивы client_header_buffer_size, nginx выдавал alert "fastcgi:
the request record is too big".
*) Исправление: при обычном запросе версии HTTP/0.9 к HTTPS серверу
nginx возвращал обычный ответ.
Изменения в nginx 0.6.20 28.11.2007
*) Исправление: при использовании директивы proxy_pass c URI-частью в
рабочем процессе мог произойти segmentation fault; ошибка появилась в
0.6.19.
Изменения в nginx 0.6.19 27.11.2007
*) Исправление: версия 0.6.18 не собиралась.
Изменения в nginx 0.6.18 27.11.2007
*) Изменение: теперь модуль ngx_http_userid_module в поле куки с номером
процесса добавляет микросекунды на время старта.
*) Изменение: в error_log теперь записывается полная строка запроса
вместо только URI.
*) Добавление: директива proxy_pass поддерживает переменные.
*) Добавление: директивы resolver и resolver_timeout.
*) Добавление: теперь директива "add_header last-modified ''" удаляет в
заголовке ответа строку "Last-Modified".
*) Исправление: директива limit_rate не позволяла передавать на полной
скорости, даже если был указан очень большой лимит.
Изменения в nginx 0.6.17 15.11.2007
*) Добавление: поддержка строки "If-Range" в заголовке запроса.
Спасибо Александру Инюхину.
*) Исправление: при использовании директивы msie_refresh повторно
экранировались уже экранированные символы; ошибка появилась в 0.6.4.
*) Исправление: директива autoindex не работала при использовании "alias
/".
*) Исправление: при использовании подзапросов в рабочем процессе мог
произойти segmentation fault.
*) Исправление: при использовании SSL и gzip большие ответы могли
передаваться не полностью.
*) Исправление: если ответ проксированного сервера был версии HTTP/0.9,
то переменная $status была равна 0.
Изменения в nginx 0.6.16 29.10.2007
*) Изменение: теперь на Linux используется uname(2) вместо procfs.
Спасибо Илье Новикову.
*) Исправление: если в директиве error_page использовался символ "?", то
он экранировался при проксировании запроса; ошибка появилась в
0.6.11.
*) Исправление: совместимость с mget.
Изменения в nginx 0.6.15 22.10.2007
*) Добавление: совместимость с Cygwin.
Спасибо Владимиру Кутакову.
*) Добавление: директива merge_slashes.
*) Добавление: директива gzip_vary.
*) Добавление: директива server_tokens.
*) Исправление: nginx не раскодировал URI в команде SSI include.
*) Исправление: при использовании переменной в директивах charset или
source_charset на старте или во время переконфигурации происходил
segmentation fault,
*) Исправление: nginx возвращал ошибку 400 на запросы вида
"GET http://www.domain.com HTTP/1.0".
Спасибо James Oakley.
*) Исправление: после перенаправления запроса с телом запроса с помощью
директивы error_page nginx пытался снова прочитать тело запроса;
ошибка появилась в 0.6.7.
*) Исправление: в рабочем процессе происходил segmentation fault, если у
сервера, обрабатывающему запрос, не был явно определён server_name;
ошибка появилась в 0.6.7.
Изменения в nginx 0.6.14 15.10.2007
*) Изменение: теперь по умолчанию команда SSI echo использует
кодирование entity.
*) Добавление: параметр encoding в команде SSI echo.
*) Добавление: директиву access_log можно использовать внутри блока
limit_except.
*) Исправление: если все сервера апстрима оказывались недоступными, то
до восстановления работоспособности у всех серверов вес становился
равным одному; ошибка появилась в 0.6.6.
*) Исправление: при использовании переменных $date_local и $date_gmt вне
модуля ngx_http_ssi_filter_module в рабочем процессе происходил
segmentation fault.
*) Исправление: при использовании включённом отладочном логе в рабочем
процессе мог произойти segmentation fault.
Спасибо Андрею Нигматулину.
*) Исправление: ngx_http_memcached_module не устанавливал
$upstream_response_time.
Спасибо Максиму Дунину.
*) Исправление: рабочий процесс мог зациклиться при использовании
memcached.
*) Исправление: nginx распознавал параметры "close" и "keep-alive" в
строке "Connection" в заголовке запроса только, если они были в
нижнем регистре; ошибка появилась в 0.6.11.
*) Исправление: sub_filter не работал с пустой строкой замены.
*) Исправление: в парсинге sub_filter.
Изменения в nginx 0.6.13 24.09.2007
*) Исправление: nginx не закрывал файл каталога для запроса HEAD, если
использовался autoindex
Спасибо Arkadiusz Patyk.
Изменения в nginx 0.6.12 21.09.2007
*) Изменение: почтовый прокси-сервер разделён на три модуля: pop3, imap
и smtp.
*) Добавление: параметры конфигурации --without-mail_pop3_module,
--without-mail_imap_module и --without-mail_smtp_module.
*) Добавление: директивы smtp_greeting_delay и smtp_client_buffer модуля
ngx_mail_smtp_module.
*) Исправление: wildcard в конце имени сервера не работали; ошибка
появилась в 0.6.9.
*) Исправление: при использовании разделяемой библиотеки PCRE,
расположенной в нестандартном месте, nginx не запускался на Solaris.
*) Исправление: директивы proxy_hide_header и fastcgi_hide_header не
скрывали строки заголовка ответа с именем больше 32 символов.
Спасибо Manlio Perillo.
Изменения в nginx 0.6.11 11.09.2007
*) Исправление: счётчик активных соединений всегда рос при использовании
почтового прокси-сервера.
*) Исправление: если бэкенд возвращал только заголовок ответа при
небуферизированном проксировании, то nginx закрывал соединение с
бэкендом по таймауту.
*) Исправление: nginx не поддерживал несколько строк "Connection" в
заголовке запроса.
*) Исправление: если в сервере апстрима был задан max_fails, то после
первой же неудачной попытки вес сервера навсегда становился равным
одному; ошибка появилась в 0.6.6.
Изменения в nginx 0.6.10 03.09.2007
*) Добавление: директивы open_file_cache, open_file_cache_retest и
open_file_cache_errors.
*) Исправление: утечки сокетов; ошибка появилась в 0.6.7.
*) Исправление: В строку заголовка ответа "Content-Type", указанную в
методе $r->send_http_header(), не добавлялась кодировка, указанная в
директиве charset.
*) Исправление: при использовании метода /dev/poll в рабочем процессе
мог произойти segmentation fault.
Изменения в nginx 0.6.9 28.08.2007
*) Исправление: рабочий процесс мог зациклиться при использовании
протокола HTTPS; ошибка появилась в 0.6.7.
*) Исправление: если сервер слушал на двух адресах или портах, то nginx
не запускался при использовании wildcard в конце имени сервера.
*) Исправление: директива ip_hash могла неверно помечать сервера как
нерабочие.
*) Исправление: nginx не собирался на amd64; ошибка появилась в 0.6.8.
Изменения в nginx 0.6.8 20.08.2007
*) Изменение: теперь nginx пытается установить директивы
worker_priority, worker_rlimit_nofile, worker_rlimit_core,
worker_rlimit_sigpending без привилегий root'а.
*) Изменение: теперь nginx экранирует символы пробела и "%" при передаче
запроса серверу аутентификации почтового прокси-сервера.
*) Изменение: теперь nginx экранирует символ "%" в переменной
$memcached_key.
*) Исправление: при указании относительного пути к конфигурационному
файлу в качестве параметра ключа -c nginx определял путь относительно
конфигурационного префикса; ошибка появилась в 0.6.6.
*) Исправление: nginx не работал на FreeBSD/sparc64.
Изменения в nginx 0.6.7 15.08.2007
*) Изменение: теперь пути, указанные в директивах include,
auth_basic_user_file, perl_modules, ssl_certificate,
ssl_certificate_key и ssl_client_certificate, определяются
относительно каталога конфигурационного файла nginx.conf, а не
относительно префикса.
*) Изменение: параметр --sysconfdir=PATH в configure упразднён.
*) Изменение: для обновления на лету версий 0.1.x создан специальный
сценарий make upgrade1.
*) Добавление: директивы server_name и valid_referers поддерживают
регулярные выражения.
*) Добавление: директива server в блоке upstream поддерживает параметр
backup.
*) Добавление: модуль ngx_http_perl_module поддерживает метод
$r->discard_request_body.
*) Добавление: директива "add_header Last-Modified ..." меняет строку
"Last-Modified" в заголовке ответа.
*) Исправление: если на запрос с телом возвращался ответ с кодом HTTP
отличным от 200, и после этого запроса соединение переходило в
состояние keep-alive, то на следующий запрос nginx возвращал 400.
*) Исправление: если в директиве auth_http был задан неправильный адрес,
то в рабочем процессе происходил segmentation fault.
*) Исправление: теперь по умолчанию nginx использует значение 511 для
listen backlog на всех платформах, кроме FreeBSD.
Спасибо Jiang Hong.
*) Исправление: рабочий процесс мог зациклиться, если server в блоке
upstream был помечен как down; ошибка появилась в 0.6.6.
*) Исправление: sendfilev() в Solaris теперь не используется при
передаче тела запроса FastCGI-серверу через unix domain сокет.
Изменения в nginx 0.6.6 30.07.2007
*) Добавление: параметр --sysconfdir=PATH в configure.
*) Добавление: именованные location'ы.
*) Добавление: переменную $args можно устанавливать с помощью set.
*) Добавление: переменная $is_args.
*) Исправление: равномерное распределение запросов к апстримам с
большими весами.
*) Исправление: если клиент в почтовом прокси-сервере закрывал
соединение, то nginx мог не закрывать соединение с бэкендом.
*) Исправление: при использовании одного хоста в качестве бэкендов для
протоколов HTTP и HTTPS без явного указания портов, nginx использовал
только один порт - 80 или 443.
*) Исправление: nginx не собирался на Solaris/amd64 Sun Studio 11 и
более ранними версиями; ошибка появилась в 0.6.4.
Изменения в nginx 0.6.5 23.07.2007
*) Добавление: переменная $nginx_version.
Спасибо Николаю Гречуху.
*) Добавление: почтовый прокси-сервер поддерживает AUTHENTICATE в режиме
IMAP.
Спасибо Максиму Дунину.
*) Добавление: почтовый прокси-сервер поддерживает STARTTLS в режиме
SMTP.
Спасибо Максиму Дунину.
*) Исправление: теперь nginx экранирует пробел в переменной
$memcached_key.
*) Исправление: nginx неправильно собирался Sun Studio на Solaris/amd64.
Спасибо Jiang Hong.
*) Исправление: незначительных потенциальных ошибок.
Спасибо Coverity's Scan.
Изменения в nginx 0.6.4 17.07.2007
*) Безопасность: при использовании директивы msie_refresh был возможен
XSS.
Спасибо Максиму Богуку.
*) Изменение: директивы proxy_store и fastcgi_store изменены.
*) Добавление: директивы proxy_store_access и fastcgi_store_access.
*) Исправление: nginx не работал на Solaris/sparc64, если был собран Sun
Studio.
Спасибо Андрею Нигматулину.
*) Изменение: обход ошибки в Sun Studio 12.
Спасибо Jiang Hong.
Изменения в nginx 0.6.3 12.07.2007
*) Добавление: директивы proxy_store и fastcgi_store.
*) Исправление: при использовании директивы auth_http_header в рабочем
процессе мог произойти segmentation fault.
Спасибо Максиму Дунину.
*) Исправление: если использовался метод аутентификации CRAM-MD5, но он
не был разрешён, то в рабочем процессе происходил segmentation fault.
*) Исправление: при использовании протокола HTTPS в директиве proxy_pass
в рабочем процессе мог произойти segmentation fault.
*) Исправление: в рабочем процессе мог произойти segmentation fault,
если использовался метод eventport.
*) Исправление: директивы proxy_ignore_client_abort и
fastcgi_ignore_client_abort не работали; ошибка появилась в 0.5.13.
Изменения в nginx 0.6.2 09.07.2007
*) Исправление: если заголовок ответа был разделён в FastCGI-записях, то
nginx передавал клиенту мусор в таких заголовках.
Изменения в nginx 0.6.1 17.06.2007
*) Исправление: в парсинге SSI.
*) Исправление: при использовании удалённого подзапроса в SSI
последующий подзапрос локального файла мог отдаваться клиенту в
неверном порядке.
*) Исправление: большие включения в SSI, сохранённые во временные файлы,
передавались не полностью.
*) Исправление: значение perl'овой переменной $$ модуля
ngx_http_perl_module было равно номеру главного процесса.
Изменения в nginx 0.6.0 14.06.2007
*) Добавление: директивы "server_name", "map", and "valid_referers"
поддерживают маски вида "www.example.*".
Изменения в nginx 0.5.25 11.06.2007
*) Исправление: nginx не собирался с параметром
--without-http_rewrite_module; ошибка появилась в 0.5.24.
Изменения в nginx 0.5.24 06.06.2007
*) Безопасность: директива ssl_verify_client не работала, если запрос
выполнялся по протоколу HTTP/0.9.
*) Исправление: при использовании сжатия часть ответа могла передаваться
несжатой; ошибка появилась в 0.5.23.
Изменения в nginx 0.5.23 04.06.2007
*) Добавление: модуль ngx_http_ssl_module поддерживает расширение TLS
Server Name Indication.
*) Добавление: директива fastcgi_catch_stderr.
Спасибо Николаю Гречуху, проект OWOX.
*) Исправление: на Линуксе в основном процессе происходил segmentation
fault, если два виртуальных сервера должны bind()ится к
пересекающимся портам.
*) Исправление: если nginx был собран с модулем ngx_http_perl_module и
perl поддерживал потоки, то во время второй переконфигурации
выдавались ошибки "panic: MUTEX_LOCK" и "perl_parse() failed".
*) Исправление: в использовании протокола HTTPS в директиве proxy_pass.
Изменения в nginx 0.5.22 29.05.2007
*) Исправление: большое тело запроса могло не передаваться бэкенду;
ошибка появилась в 0.5.21.
Изменения в nginx 0.5.21 28.05.2007
*) Исправление: если внутри сервера описано больше примерно десяти
location'ов, то location'ы, заданные с помощью регулярного выражения,
могли выполняться не в том, порядке, в каком они описаны.
*) Исправление: на 64-битной платформе рабочий процесс мог зациклиться,
если 33-тий по счёту или последующий бэкенд упал.
Спасибо Антону Поварову.
*) Исправление: при использовании библиотеки PCRE на Solaris/sparc64 мог
произойти bus error.
Спасибо Андрею Нигматулину.
*) Исправление: в использовании протокола HTTPS в директиве proxy_pass.
Изменения в nginx 0.5.20 07.05.2007
*) Добавление: директива sendfile_max_chunk.
*) Добавление: переменные "$http_...", "$sent_http_..." и
"$upstream_http_..." можно менять директивой set.
*) Исправление: при использовании SSI-команды 'if expr="$var = /"' в
рабочем процессе мог произойти segmentation fault.
*) Исправление: завершающая строка multipart range ответа передавалась
неверно.
Спасибо Evan Miller.
*) Исправление: nginx не работал на Solaris/sparc64, если был собран Sun
Studio.
Спасибо Андрею Нигматулину.
*) Исправление: модуль ngx_http_perl_module не собирался make в Solaris.
Спасибо Андрею Нигматулину.
Изменения в nginx 0.5.19 24.04.2007
*) Изменение: значение переменной $request_time теперь записывается с
точностью до миллисекунд.
*) Изменение: метод $r->rflush в модуле ngx_http_perl_module
переименован в $r->flush.
*) Добавление: переменная $upstream_addr.
*) Добавление: директивы proxy_headers_hash_max_size и
proxy_headers_hash_bucket_size.
Спасибо Володымыру Костырко.
*) Исправление: при использовании sendfile и limit_rate на 64-битных
платформах нельзя было передавать файлы больше 2G.
*) Исправление: при использовании sendfile на 64-битном Linux нельзя
было передавать файлы больше 2G.
Изменения в nginx 0.5.18 19.04.2007
*) Добавление: модуль ngx_http_sub_filter_module.
*) Добавление: переменные "$upstream_http_...".
*) Добавление: теперь переменные $upstream_status и
$upstream_response_time содержат данные о всех обращениях к
апстримам, сделанным до X-Accel-Redirect.
*) Исправление: если nginx был собран с модулем ngx_http_perl_module и
perl не поддерживал multiplicity, то после первой переконфигурации и
после получения любого сигнала в основном процессе происходил
segmentation fault; ошибка появилась в 0.5.9.
*) Исправление: если perl не поддерживал multiplicity, то после
переконфигурации перловый код не работал; ошибка появилась в 0.3.38.
Изменения в nginx 0.5.17 02.04.2007
*) Изменение: теперь nginx для метода TRACE всегда возвращает код 405.
*) Добавление: теперь nginx поддерживает директиву include внутри блока
types.
*) Исправление: использование переменной $document_root в директиве root
и alias запрещено: оно вызывало рекурсивное переполнение стека.
*) Исправление: в использовании протокола HTTPS в директиве proxy_pass.
*) Исправление: в некоторых случаях некэшируемые переменные (такие, как
$uri) возвращали старое закэшированное значение.
Изменения в nginx 0.5.16 26.03.2007
*) Исправление: в качестве ключа для хэша в директиве ip_hash не
использовалась сеть класса С.
Спасибо Павлу Ярковому.
*) Исправление: если в строке "Content-Type" в заголовке ответа бэкенда
был указан charset и строка завершалась символом ";", то в рабочем
процессе мог произойти segmentation fault; ошибка появилась в 0.3.50.
*) Исправление: ошибки "[alert] zero size buf" при работе с
FastCGI-сервером, если тело запроса, записанное во временный файл,
было кратно 32K.
*) Исправление: nginx не собирался на Solaris без параметра
--with-debug; ошибка появилась в 0.5.15.
Изменения в nginx 0.5.15 19.03.2007
*) Добавление: почтовый прокси-сервер поддерживает аутентифицированное
SMTP-проксирование и директивы smtp_auth, smtp_capabilities и
xclient.
Спасибо Антону Южанинову и Максиму Дунину.
*) Добавление: теперь keep-alive соединения закрываются сразу же по
получении сигнала переконфигурации.
*) Изменение: директивы imap и auth переименованы соответственно в mail
и pop3_auth.
*) Исправление: если использовался метод аутентификации CRAM-MD5 и не
был разрешён метод APOP, то в рабочем процессе происходил
segmentation fault.
*) Исправление: при использовании директивы starttls only в протоколе
POP3 nginx разрешал аутентификацию без перехода в режим SSL.
*) Исправление: рабочие процессы не выходили после переконфигурации и не
переоткрывали логи, если использовался метод eventport.
*) Исправление: при использовании директивы ip_hash рабочий процесс мог
зациклиться.
*) Исправление: теперь nginx не пишет в лог некоторые alert'ы, если
используются методы eventport или /dev/poll.
Изменения в nginx 0.5.14 23.02.2007
*) Исправление: nginx игнорировал лишние закрывающие скобки "}" в конце
конфигурационного файла.
Изменения в nginx 0.5.13 19.02.2007
*) Добавление: методы COPY и MOVE.
*) Исправление: модуль ngx_http_realip_module устанавливал мусор для
запросов, переданных по keep-alive соединению.
*) Исправление: nginx не работал на 64-битном big-endian Linux.
Спасибо Андрею Нигматулину.
*) Исправление: при получении слишком длинной команды IMAP/POP3-прокси
теперь сразу закрывает соединение, а не по таймауту.
*) Исправление: если при использовании метода epoll клиент закрывал
преждевременно соединение со своей стороны, то nginx закрывал это
соединение только по истечении таймаута на передачу.
*) Исправление: nginx не собирался на платформах, отличных от i386,
amd64, sparc и ppc; ошибка появилась в 0.5.8.
Изменения в nginx 0.5.12 12.02.2007
*) Исправление: nginx не собирался на платформах, отличных от i386,
amd64, sparc и ppc; ошибка появилась в 0.5.8.
*) Исправление: при использовании временных файлов в время работы с
FastCGI-сервером в рабочем процессе мог произойти segmentation fault;
ошибка появилась в 0.5.8.
*) Исправление: если переменная $fastcgi_script_name записывалась в лог,
то в рабочем процессе мог произойти segmentation fault.
*) Исправление: ngx_http_perl_module не собирался на Solaris.
Изменения в nginx 0.5.11 05.02.2007
*) Добавление: теперь configure определяет библиотеку PCRE в MacPorts.
Спасибо Chris McGrath.
*) Исправление: ответ был неверным, если запрашивалось несколько
диапазонов; ошибка появилась в 0.5.6.
*) Исправление: директива create_full_put_path не могла создавать
промежуточные каталоги, если не была установлена директива
dav_access.
Спасибо Evan Miller.
*) Исправление: вместо кодов ошибок "400" и "408" в access_log мог
записываться код "0".
*) Исправление: при сборке с оптимизацией -O2 в рабочем процессе мог
произойти segmentation fault.
Изменения в nginx 0.5.10 26.01.2007
*) Исправление: во время обновления исполняемого файла новый процесс не
наследовал слушающие сокеты; ошибка появилась в 0.5.9.
*) Исправление: при сборке с оптимизацией -O2 в рабочем процессе мог
произойти segmentation fault; ошибка появилась в 0.5.1.
Изменения в nginx 0.5.9 25.01.2007
*) Изменение: модуль ngx_http_memcached_module теперь в качестве ключа
использует значение переменной $memcached_key.
*) Добавление: переменная $memcached_key.
*) Добавление: параметр clean в директиве client_body_in_file_only.
*) Добавление: директива env.
*) Добавление: директива sendfile работает внутри блока if.
*) Добавление: теперь при ошибке записи в access_log nginx записывает
сообщение в error_log, но не чаще одного раза в минуту.
*) Исправление: директива "access_log off" не всегда запрещала запись в
лог.
Изменения в nginx 0.5.8 19.01.2007
*) Исправление: если использовалась директива
"client_body_in_file_only on" и тело запроса было небольшое, то мог
произойти segmentation fault.
*) Исправление: происходил segmentation fault, если использовались
директивы "client_body_in_file_only on" и
"proxy_pass_request_body off" или "fastcgi_pass_request_body off", и
делался переход к следующему бэкенду.
*) Исправление: если при использовании директивы "proxy_buffering off"
соединение с клиентом было неактивно, то оно закрывалось по таймауту,
заданному директивой send_timeout; ошибка появилась в 0.4.7.
*) Исправление: если при использовании метода epoll клиент закрывал
преждевременно соединение со своей стороны, то nginx закрывал это
соединение только по истечении таймаута на передачу.
*) Исправление: ошибки "[alert] zero size buf" при работе с
FastCGI-сервером.
*) Исправление ошибок в директиве limit_zone.
Изменения в nginx 0.5.7 15.01.2007
*) Добавление: оптимизация использования памяти в ssl_session_cache.
*) Исправление ошибок в директивах ssl_session_cache и limit_zone.
*) Исправление: на старте или во время переконфигурации происходил
segmentation fault, если директивы ssl_session_cache или limit_zone
использовались на 64-битных платформах.
*) Исправление: при использовании директив add_before_body или
add_after_body происходил segmentation fault, если в заголовке ответа
нет строки "Content-Type".
*) Исправление: библиотека OpenSSL всегда собиралась с поддержкой
потоков.
Спасибо Дену Иванову.
*) Исправление: совместимость библиотеки PCRE-6.5+ и компилятора icc.
Изменения в nginx 0.5.6 09.01.2007
*) Изменение: теперь модуль ngx_http_index_module игнорирует все методы,
кроме GET, HEAD и POST.
*) Добавление: модуль ngx_http_limit_zone_module.
*) Добавление: переменная $binary_remote_addr.
*) Добавление: директивы ssl_session_cache модулей ngx_http_ssl_module и
ngx_imap_ssl_module.
*) Добавление: метод DELETE поддерживает рекурсивное удаление.
*) Исправление: при использовании $r->sendfile() byte-ranges
передавались неверно.
Изменения в nginx 0.5.5 24.12.2006
*) Изменение: ключ -v больше не выводит информацию о компиляторе.
*) Добавление: ключ -V.
*) Добавление: директива worker_rlimit_core поддерживает указание
размера в K, M и G.
*) Исправление: модуль nginx.pm теперь может устанавливаться
непривилегированным пользователем.
*) Исправление: при использовании методов $r->request_body или
$r->request_body_file мог произойти segmentation fault.
*) Исправление: ошибок, специфичных для платформы ppc.
Изменения в nginx 0.5.4 15.12.2006
*) Добавление: директиву perl можно использовать внутри блока
limit_except.
*) Исправление: модуль ngx_http_dav_module требовал строку "Date" в
заголовке запроса для метода DELETE.
*) Исправление: при использовании одного параметра в директиве
dav_access nginx мог сообщить об ошибке в конфигурации.
*) Исправление: при использовании переменной $host мог произойти
segmentation fault; ошибка появилась в 0.4.14.
Изменения в nginx 0.5.3 13.12.2006
*) Добавление: модуль ngx_http_perl_module поддерживает методы
$r->status, $r->log_error и $r->sleep.
*) Добавление: метод $r->variable поддерживает переменные, неописанные в
конфигурации nginx'а.
*) Исправление: метод $r->has_request_body не работал.
Изменения в nginx 0.5.2 11.12.2006
*) Исправление: если в директивах proxy_pass использовалось имя,
указанное в upstream, то nginx пытался найти IP-адрес этого имени;
ошибка появилась в 0.5.1.
Изменения в nginx 0.5.1 11.12.2006
*) Исправление: директива post_action могла не работать после неудачного
завершения запроса.
*) Изменение: обход ошибки в Eudora для Mac; ошибка появилась в 0.4.11.
Спасибо Bron Gondwana.
*) Исправление: при указании в директиве fastcgi_pass имени описанного
upstream'а выдавалось сообщение "no port in upstream"; ошибка
появилась в 0.5.0.
*) Исправление: если в директивах proxy_pass и fastcgi_pass
использовались одинаковых имена серверов, но с разными портами, то
эти директивы использовали первый описанный порт; ошибка появилась в
0.5.0.
*) Исправление: если в директивах proxy_pass и fastcgi_pass
использовались unix domain сокеты, то эти директивы использовали
первый описанный сокет; ошибка появилась в 0.5.0.
*) Исправление: ngx_http_auth_basic_module игнорировал пользователя,
если он был указан в последней строке файла паролей и после пароля не
было перевода строки, возврата каретки или символа ":".
*) Исправление: переменная $upstream_response_time могла быть равна
"0.000", хотя время обработки было больше 1 миллисекунды.
Изменения в nginx 0.5.0 04.12.2006
*) Изменение: параметры в виде "%name" в директиве log_format больше не
поддерживаются.
*) Изменение: директивы proxy_upstream_max_fails,
proxy_upstream_fail_timeout, fastcgi_upstream_max_fails, и
fastcgi_upstream_fail_timeout, memcached_upstream_max_fails и
memcached_upstream_fail_timeout больше не поддерживаются.
*) Добавление: директива server в блоке upstream поддерживает параметры
max_fails, fail_timeout и down.
*) Добавление: директива ip_hash в блоке upstream.
*) Добавление: статус WAIT в строке "Auth-Status" в заголовке ответа
сервера аутентификации IMAP/POP3 прокси.
*) Исправление: nginx не собирался на 64-битных платформах; ошибка
появилась в 0.4.14.
Изменения в nginx 0.4.14 27.11.2006
*) Добавление: директива proxy_pass_error_message в IMAP/POP3 прокси.
*) Добавление: теперь configure определяет библиотеку PCRE на FreeBSD,
Linux и NetBSD.
*) Исправление: ngx_http_perl_module не работал с перлом, собранным с
поддержкой потоков; ошибка появилась в 0.3.38.
*) Исправление: ngx_http_perl_module не работал корректно, если перл
вызывался рекурсивно.
*) Исправление: nginx игнорировал имя сервера в строке запроса.
*) Исправление: если FastCGI сервер передавал много в stderr, то рабочий
процесс мог зациклиться.
*) Исправление: при изменении системного времени переменная
$upstream_response_time могла быть отрицательной.
*) Исправление: при использовании POP3 серверу аутентификации IMAP/POP3
прокси не передавался параметр Auth-Login-Attempt.
*) Исправление: при ошибке соединения с сервером аутентификации
IMAP/POP3 прокси мог произойти segmentation fault.
Изменения в nginx 0.4.13 15.11.2006
*) Добавление: директиву proxy_pass можно использовать внутри блока
limit_except.
*) Добавление: директива limit_except поддерживает все WebDAV методы.
*) Исправление: при использовании директивы add_before_body без
директивы add_after_body ответ передавался не полностью.
*) Исправление: большое тело запроса не принималось, если использовались
метод epoll и deferred accept().
*) Исправление: для ответов модуля ngx_http_autoindex_module не
выставлялась кодировка; ошибка появилась в 0.3.50.
*) Исправление: ошибки "[alert] zero size buf" при работе с
FastCGI-сервером;
*) Исправление: параметр конфигурации --group= игнорировался.
Спасибо Thomas Moschny.
*) Исправление: 50-й подзапрос в SSI ответе не работал; ошибка появилась
в 0.3.50.
Изменения в nginx 0.4.12 31.10.2006
*) Добавление: модуль ngx_http_perl_module поддерживает метод
$r->variable.
*) Исправление: при включении в ответ большого статического файла с
помощью SSI ответ мог передаваться не полностью.
*) Исправление: nginx не убирал "#fragment" в URI.
Изменения в nginx 0.4.11 25.10.2006
*) Добавление: POP3 прокси поддерживает AUTH LOGIN PLAIN и CRAM-MD5.
*) Добавление: модуль ngx_http_perl_module поддерживает метод
$r->allow_ranges.
*) Исправление: при включённой поддержке команды APOP в POP3 прокси
могли не работать команды USER/PASS; ошибка появилась в 0.4.10.
Изменения в nginx 0.4.10 23.10.2006
*) Добавление: POP3 прокси поддерживает APOP.
*) Исправление: при использовании методов select, poll и /dev/poll во
время ожидания ответа от сервера аутентификации IMAP/POP3 прокси
нагружал процессор.
*) Исправление: при использовании переменной $server_addr в директиве
map мог произойти segmentation fault.
*) Исправление: модуль ngx_http_flv_module не поддерживал byte ranges
для полных ответов; ошибка появилась в 0.4.7.
*) Исправление: nginx не собирался на Debian amd64; ошибка появилась в
0.4.9.
Изменения в nginx 0.4.9 13.10.2006
*) Добавление: параметр set в команде SSI include.
*) Добавление: модуль ngx_http_perl_module теперь проверяет версию
модуля nginx.pm.
Изменения в nginx 0.4.8 11.10.2006
*) Исправление: если до команды SSI include с параметром wait
выполнялась ещё одна команда SSI include, то параметр wait мог не
работать.
*) Исправление: модуль ngx_http_flv_module добавлял FLV-заголовок для
полных ответов.
Спасибо Алексею Ковырину.
Изменения в nginx 0.4.7 10.10.2006
*) Добавление: модуль ngx_http_flv_module.
*) Добавление: переменная $request_body_file.
*) Добавление: директивы charset и source_charset поддерживают
переменные.
*) Исправление: если до команды SSI include с параметром wait
выполнялась ещё одна команда SSI include, то параметр wait мог не
работать.
*) Исправление: при использовании директивы "proxy_buffering off" или
при работе с memcached соединения могли не закрываться по таймауту.
*) Исправление: nginx не запускался на 64-битных платформах, отличных от
amd64, sparc64 и ppc64.
Изменения в nginx 0.4.6 06.10.2006
*) Исправление: nginx не запускался на 64-битных платформах, отличных от
amd64, sparc64 и ppc64.
*) Исправление: при запросе версии HTTP/1.1 nginx передавал ответ
chunk'ами, если длина ответа в методе
$r->headers_out("Content-Length", ...) была задана текстовой строкой.
*) Исправление: после перенаправления ошибки с помощью директивы
error_page любая директива модуля ngx_http_rewrite_module возвращала
эту ошибку; ошибка появилась в 0.4.4.
Изменения в nginx 0.4.5 02.10.2006
*) Исправление: nginx не собирался на Linux и Solaris; ошибка появилась
в 0.4.4.
Изменения в nginx 0.4.4 02.10.2006
*) Добавление: переменная $scheme.
*) Добавление: директива expires поддерживает параметр max.
*) Добавление: директива include поддерживает маску "*".
Спасибо Jonathan Dance.
*) Исправление: директива return всегда изменяла код ответа,
перенаправленного директивой error_page.
*) Исправление: происходил segmentation fault, если в методе PUT
передавалось тело нулевой длины.
*) Исправление: при использовании переменных в директиве proxy_redirect
редирект изменялся неверно.
Изменения в nginx 0.4.3 26.09.2006
*) Изменение: ошибку 499 теперь нельзя перенаправить с помощью директивы
error_page.
*) Добавление: поддержка Solaris 10 event ports.
*) Добавление: модуль ngx_http_browser_module.
*) Исправление: при перенаправлении ошибки 400 проксированному серверу
помощью директивы error_page мог произойти segmentation fault.
*) Исправление: происходил segmentation fault, если в директиве
proxy_pass использовался unix domain сокет; ошибка появилась в
0.3.47.
*) Исправление: SSI не работал с ответами memcached и
небуферизированными проксированными ответами.
*) Изменение: обход ошибки PAUSE hardware capability в Sun Studio.
Изменения в nginx 0.4.2 14.09.2006
*) Исправление: убрана поддержка флага O_NOATIME на Linux; ошибка
появилась в 0.4.1.
Изменения в nginx 0.4.1 14.09.2006
*) Исправление: совместимость с DragonFlyBSD.
Спасибо Павлу Назарову.
*) Изменение: обход ошибки в sendfile() в 64-битном Linux при передаче
файлов больше 2G.
*) Добавление: теперь на Linux nginx для статических запросов использует
флаг O_NOATIME.
Спасибо Yusuf Goolamabbas.
Изменения в nginx 0.4.0 30.08.2006
*) Изменение во внутреннем API: инициализация модулей HTTP перенесена из
фазы init module в фазу HTTP postconfiguration.
*) Изменение: теперь тело запроса в модуле ngx_http_perl_module не
считывается заранее: нужно явно инициировать чтение с помощью метода
$r->has_request_body.
*) Добавление: модуль ngx_http_perl_module поддерживает код возврата
DECLINED.
*) Добавление: модуль ngx_http_dav_module поддерживает входящую строку
заголовка "Date" для метода PUT.
*) Добавление: директива ssi работает внутри блока if.
*) Исправление: происходил segmentation fault, если в директиве index
использовалась переменные и при этом первое имя индексного файла было
без переменных; ошибка появилась в 0.1.29.
Изменения в nginx 0.3.61 28.08.2006
*) Изменение: директива tcp_nodelay теперь по умолчанию включена.
*) Добавление: директива msie_refresh.
*) Добавление: директива recursive_error_pages.
*) Исправление: директива rewrite возвращала неправильный редирект, если
редирект включал в себя выделенные закодированные символы из
оригинального URI.
Изменения в nginx 0.3.60 18.08.2006
*) Исправление: во время перенаправления ошибки рабочий процесс мог
зациклиться; ошибка появилась в 0.3.59.
Изменения в nginx 0.3.59 16.08.2006
*) Добавление: теперь можно делать несколько перенаправлений через
директиву error_page.
*) Исправление: директива dav_access не поддерживала три параметра.
*) Исправление: директива error_page не изменяла строку "Content-Type"
после перенаправления с помощью "X-Accel-Redirect"; ошибка появилась
в 0.3.58.
Изменения в nginx 0.3.58 14.08.2006
*) Добавление: директива error_page поддерживает переменные.
*) Изменение: теперь на Linux используется интерфейс procfs вместо
sysctl.
*) Изменение: теперь при использовании "X-Accel-Redirect" строка
"Content-Type" наследуется из первоначального ответа.
*) Исправление: директива error_page не перенаправляла ошибку 413.
*) Исправление: завершающий "?" не удалял старые аргументы, если в
переписанном URI не было новых аргументов.
*) Исправление: nginx не запускался на 64-битной FreeBSD 7.0-CURRENT.
Изменения в nginx 0.3.57 09.08.2006
*) Добавление: переменная $ssl_client_serial.
*) Исправление: в операторе "!-e" в директиве if.
Спасибо Андриану Буданцову.
*) Исправление: при проверке клиентского сертификата nginx не передавал
клиенту информацию о требуемых сертификатах.
*) Исправление: переменная $document_root не поддерживала переменные в
директиве root.
Изменения в nginx 0.3.56 04.08.2006
*) Добавление: директива dav_access.
*) Добавление: директива if поддерживает операторы "-d", "!-d", "-e",
"!-e", "-x" и "!-x".
*) Исправление: при записи в access_log некоторых передаваемых клиенту
строк заголовков происходил segmentation fault, если запрос возвращал
редирект.
Изменения в nginx 0.3.55 28.07.2006
*) Добавление: параметр stub в команде SSI include.
*) Добавление: команда SSI block.
*) Добавление: скрипт unicode2nginx добавлен в contrib.
*) Исправление: если root был задан только переменной, то корень
задавался относительно префикса сервера.
*) Исправление: если в запросе был "//" или "/.", и после этого
закодированные символы в виде "%XX", то проксируемый запрос
передавался незакодированным.
*) Исправление: метод $r->header_in("Cookie") модуля
ngx_http_perl_module теперь возвращает все строки "Cookie" в
заголовке запроса.
*) Исправление: происходил segmentation fault, если использовался
"client_body_in_file_only on" и делался переход к следующему бэкенду.
*) Исправление: при некоторых условиях во время переконфигурации коды
символов внутри директивы charset_map могли считаться неверными;
ошибка появилась в 0.3.50.
Изменения в nginx 0.3.54 11.07.2006
*) Добавление: nginx теперь записывает в лог информацию о подзапросах.
*) Добавление: директивы proxy_next_upstream, fastcgi_next_upstream и
memcached_next_upstream поддерживают параметр off.
*) Добавление: директива debug_connection поддерживает запись адресов в
формате CIDR.
*) Исправление: при перекодировании ответа проксированного сервера или
сервера FastCGI в UTF-8 или наоборот ответ мог передаваться не
полностью.
*) Исправление: переменная $upstream_response_time содержала время
только первого обращения к бэкенду.
*) Исправление: nginx не собирался на платформе amd64; ошибка появилась
в 0.3.53.
Изменения в nginx 0.3.53 07.07.2006
*) Изменение: директива add_header добавляет строки в ответы с кодом
204, 301 и 302.
*) Добавление: директива server в блоке upstream поддерживает параметр
weight.
*) Добавление: директива server_name поддерживает маску "*".
*) Добавление: nginx поддерживает тело запроса больше 2G.
*) Исправление: если при использовании "satisfy_any on" клиент успешно
проходил аутентификацию, в лог всё равно записалоcь сообщение "access
forbidden by rule".
*) Исправление: метод PUT мог ошибочно не создать файл и вернуть код
409.
*) Исправление: если во время аутентификации IMAP/POP3 бэкенд возвращал
ошибку, nginx продолжал проксирование.
Изменения в nginx 0.3.52 03.07.2006
*) Изменение: восстановлено поведение модуля ngx_http_index_module для
запросов "POST /": как в версии до 0.3.40, модуль теперь не выдаёт
ошибку 405.
*) Исправление: при использовании ограничения скорости рабочий процесс
мог зациклиться; ошибка появилась в 0.3.37.
*) Исправление: модуль ngx_http_charset_module записывал в лог ошибку
"unknown charset", даже если перекодировка не требовалась; ошибка
появилась в 0.3.50.
*) Исправление: если в результате запроса PUT возвращался код 409, то
временный файл не удалялся.
Изменения в nginx 0.3.51 30.06.2006
*) Исправление: при некоторых условиях в SSI мог пропадать символы "<";
ошибка появилась в 0.3.50.
Изменения в nginx 0.3.50 28.06.2006
*) Изменение: директивы proxy_redirect_errors и fastcgi_redirect_errors
переименованы соответственно в proxy_intercept_errors и
fastcgi_intercept_errors.
*) Добавление: модуль ngx_http_charset_module поддерживает
перекодирование из однобайтных кодировок в UTF-8 и обратно.
*) Добавление: в режиме прокси и FastCGI поддерживается строка заголовка
"X-Accel-Charset" в ответе бэкенда.
*) Исправление: символ "\" в парах "\"" и "\'" в SSI командах убирался,
только если также использовался символ "$".
*) Исправление: при некоторых условиях в SSI после вставки могла быть
добавлена строка "" CRLF
"" CRLF
"" CRLF
"" CRLF
"" CRLF
"" CRLF
;
static u_char ngx_http_msie_refresh_head[] =
"
" CRLF;
static char ngx_http_error_301_page[] =
"" CRLF
"301 Moved Permanently" CRLF
"" CRLF
"301 Moved Permanently
" CRLF
;
static char ngx_http_error_302_page[] =
"" CRLF
"302 Found" CRLF
"" CRLF
"302 Found
" CRLF
;
static char ngx_http_error_303_page[] =
"" CRLF
"303 See Other" CRLF
"" CRLF
"303 See Other
" CRLF
;
static char ngx_http_error_307_page[] =
"" CRLF
"307 Temporary Redirect" CRLF
"" CRLF
"307 Temporary Redirect
" CRLF
;
static char ngx_http_error_308_page[] =
"" CRLF
"308 Permanent Redirect" CRLF
"" CRLF
"308 Permanent Redirect
" CRLF
;
static char ngx_http_error_400_page[] =
"" CRLF
"400 Bad Request" CRLF
"" CRLF
"400 Bad Request
" CRLF
;
static char ngx_http_error_401_page[] =
"" CRLF
"401 Authorization Required" CRLF
"" CRLF
"401 Authorization Required
" CRLF
;
static char ngx_http_error_402_page[] =
"" CRLF
"402 Payment Required" CRLF
"" CRLF
"402 Payment Required
" CRLF
;
static char ngx_http_error_403_page[] =
"" CRLF
"403 Forbidden" CRLF
"" CRLF
"403 Forbidden
" CRLF
;
static char ngx_http_error_404_page[] =
"" CRLF
"404 Not Found" CRLF
"" CRLF
"404 Not Found
" CRLF
;
static char ngx_http_error_405_page[] =
"" CRLF
"405 Not Allowed" CRLF
"" CRLF
"405 Not Allowed
" CRLF
;
static char ngx_http_error_406_page[] =
"" CRLF
"406 Not Acceptable" CRLF
"" CRLF
"406 Not Acceptable
" CRLF
;
static char ngx_http_error_408_page[] =
"" CRLF
"408 Request Time-out" CRLF
"" CRLF
"408 Request Time-out
" CRLF
;
static char ngx_http_error_409_page[] =
"" CRLF
"409 Conflict" CRLF
"" CRLF
"409 Conflict
" CRLF
;
static char ngx_http_error_410_page[] =
"" CRLF
"410 Gone" CRLF
"" CRLF
"410 Gone
" CRLF
;
static char ngx_http_error_411_page[] =
"" CRLF
"411 Length Required" CRLF
"" CRLF
"411 Length Required
" CRLF
;
static char ngx_http_error_412_page[] =
"" CRLF
"412 Precondition Failed" CRLF
"" CRLF
"412 Precondition Failed
" CRLF
;
static char ngx_http_error_413_page[] =
"" CRLF
"413 Request Entity Too Large" CRLF
"" CRLF
"413 Request Entity Too Large
" CRLF
;
static char ngx_http_error_414_page[] =
"" CRLF
"414 Request-URI Too Large" CRLF
"" CRLF
"414 Request-URI Too Large
" CRLF
;
static char ngx_http_error_415_page[] =
"" CRLF
"415 Unsupported Media Type" CRLF
"" CRLF
"415 Unsupported Media Type
" CRLF
;
static char ngx_http_error_416_page[] =
"" CRLF
"416 Requested Range Not Satisfiable" CRLF
"" CRLF
"416 Requested Range Not Satisfiable
" CRLF
;
static char ngx_http_error_421_page[] =
"" CRLF
"421 Misdirected Request" CRLF
"" CRLF
"421 Misdirected Request
" CRLF
;
static char ngx_http_error_429_page[] =
"" CRLF
"429 Too Many Requests" CRLF
"" CRLF
"429 Too Many Requests
" CRLF
;
static char ngx_http_error_494_page[] =
"" CRLF
"400 Request Header Or Cookie Too Large"
CRLF
"" CRLF
"400 Bad Request
" CRLF
"Request Header Or Cookie Too Large" CRLF
;
static char ngx_http_error_495_page[] =
"" CRLF
"400 The SSL certificate error"
CRLF
"" CRLF
"400 Bad Request
" CRLF
"The SSL certificate error" CRLF
;
static char ngx_http_error_496_page[] =
"" CRLF
"400 No required SSL certificate was sent"
CRLF
"" CRLF
"400 Bad Request
" CRLF
"No required SSL certificate was sent" CRLF
;
static char ngx_http_error_497_page[] =
"" CRLF
"400 The plain HTTP request was sent to HTTPS port"
CRLF
"" CRLF
"400 Bad Request
" CRLF
"The plain HTTP request was sent to HTTPS port" CRLF
;
static char ngx_http_error_500_page[] =
"" CRLF
"500 Internal Server Error" CRLF
"" CRLF
"500 Internal Server Error
" CRLF
;
static char ngx_http_error_501_page[] =
"" CRLF
"501 Not Implemented" CRLF
"" CRLF
"501 Not Implemented
" CRLF
;
static char ngx_http_error_502_page[] =
"" CRLF
"502 Bad Gateway" CRLF
"" CRLF
"502 Bad Gateway
" CRLF
;
static char ngx_http_error_503_page[] =
"" CRLF
"503 Service Temporarily Unavailable" CRLF
"" CRLF
"503 Service Temporarily Unavailable
" CRLF
;
static char ngx_http_error_504_page[] =
"" CRLF
"504 Gateway Time-out" CRLF
"" CRLF
"504 Gateway Time-out
" CRLF
;
static char ngx_http_error_505_page[] =
"" CRLF
"505 HTTP Version Not Supported" CRLF
"" CRLF
"505 HTTP Version Not Supported
" CRLF
;
static char ngx_http_error_507_page[] =
"" CRLF
"507 Insufficient Storage" CRLF
"" CRLF
"507 Insufficient Storage
" CRLF
;
static ngx_str_t ngx_http_error_pages[] = {
ngx_null_string, /* 201, 204 */
#define NGX_HTTP_LAST_2XX 202
#define NGX_HTTP_OFF_3XX (NGX_HTTP_LAST_2XX - 201)
/* ngx_null_string, */ /* 300 */
ngx_string(ngx_http_error_301_page),
ngx_string(ngx_http_error_302_page),
ngx_string(ngx_http_error_303_page),
ngx_null_string, /* 304 */
ngx_null_string, /* 305 */
ngx_null_string, /* 306 */
ngx_string(ngx_http_error_307_page),
ngx_string(ngx_http_error_308_page),
#define NGX_HTTP_LAST_3XX 309
#define NGX_HTTP_OFF_4XX (NGX_HTTP_LAST_3XX - 301 + NGX_HTTP_OFF_3XX)
ngx_string(ngx_http_error_400_page),
ngx_string(ngx_http_error_401_page),
ngx_string(ngx_http_error_402_page),
ngx_string(ngx_http_error_403_page),
ngx_string(ngx_http_error_404_page),
ngx_string(ngx_http_error_405_page),
ngx_string(ngx_http_error_406_page),
ngx_null_string, /* 407 */
ngx_string(ngx_http_error_408_page),
ngx_string(ngx_http_error_409_page),
ngx_string(ngx_http_error_410_page),
ngx_string(ngx_http_error_411_page),
ngx_string(ngx_http_error_412_page),
ngx_string(ngx_http_error_413_page),
ngx_string(ngx_http_error_414_page),
ngx_string(ngx_http_error_415_page),
ngx_string(ngx_http_error_416_page),
ngx_null_string, /* 417 */
ngx_null_string, /* 418 */
ngx_null_string, /* 419 */
ngx_null_string, /* 420 */
ngx_string(ngx_http_error_421_page),
ngx_null_string, /* 422 */
ngx_null_string, /* 423 */
ngx_null_string, /* 424 */
ngx_null_string, /* 425 */
ngx_null_string, /* 426 */
ngx_null_string, /* 427 */
ngx_null_string, /* 428 */
ngx_string(ngx_http_error_429_page),
#define NGX_HTTP_LAST_4XX 430
#define NGX_HTTP_OFF_5XX (NGX_HTTP_LAST_4XX - 400 + NGX_HTTP_OFF_4XX)
ngx_string(ngx_http_error_494_page), /* 494, request header too large */
ngx_string(ngx_http_error_495_page), /* 495, https certificate error */
ngx_string(ngx_http_error_496_page), /* 496, https no certificate */
ngx_string(ngx_http_error_497_page), /* 497, http to https */
ngx_string(ngx_http_error_404_page), /* 498, canceled */
ngx_null_string, /* 499, client has closed connection */
ngx_string(ngx_http_error_500_page),
ngx_string(ngx_http_error_501_page),
ngx_string(ngx_http_error_502_page),
ngx_string(ngx_http_error_503_page),
ngx_string(ngx_http_error_504_page),
ngx_string(ngx_http_error_505_page),
ngx_null_string, /* 506 */
ngx_string(ngx_http_error_507_page)
#define NGX_HTTP_LAST_5XX 508
};
ngx_int_t
ngx_http_special_response_handler(ngx_http_request_t *r, ngx_int_t error)
{
ngx_uint_t i, err;
ngx_http_err_page_t *err_page;
ngx_http_core_loc_conf_t *clcf;
ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http special response: %i, \"%V?%V\"",
error, &r->uri, &r->args);
r->err_status = error;
if (r->keepalive) {
switch (error) {
case NGX_HTTP_BAD_REQUEST:
case NGX_HTTP_REQUEST_ENTITY_TOO_LARGE:
case NGX_HTTP_REQUEST_URI_TOO_LARGE:
case NGX_HTTP_TO_HTTPS:
case NGX_HTTPS_CERT_ERROR:
case NGX_HTTPS_NO_CERT:
case NGX_HTTP_INTERNAL_SERVER_ERROR:
case NGX_HTTP_NOT_IMPLEMENTED:
r->keepalive = 0;
}
}
if (r->lingering_close) {
switch (error) {
case NGX_HTTP_BAD_REQUEST:
case NGX_HTTP_TO_HTTPS:
case NGX_HTTPS_CERT_ERROR:
case NGX_HTTPS_NO_CERT:
r->lingering_close = 0;
}
}
r->headers_out.content_type.len = 0;
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
if (!r->error_page && clcf->error_pages && r->uri_changes != 0) {
if (clcf->recursive_error_pages == 0) {
r->error_page = 1;
}
err_page = clcf->error_pages->elts;
for (i = 0; i < clcf->error_pages->nelts; i++) {
if (err_page[i].status == error) {
return ngx_http_send_error_page(r, &err_page[i]);
}
}
}
r->expect_tested = 1;
if (ngx_http_discard_request_body(r) != NGX_OK) {
r->keepalive = 0;
}
if (clcf->msie_refresh
&& r->headers_in.msie
&& (error == NGX_HTTP_MOVED_PERMANENTLY
|| error == NGX_HTTP_MOVED_TEMPORARILY))
{
return ngx_http_send_refresh(r);
}
if (error == NGX_HTTP_CREATED) {
/* 201 */
err = 0;
} else if (error == NGX_HTTP_NO_CONTENT) {
/* 204 */
err = 0;
} else if (error >= NGX_HTTP_MOVED_PERMANENTLY
&& error < NGX_HTTP_LAST_3XX)
{
/* 3XX */
err = error - NGX_HTTP_MOVED_PERMANENTLY + NGX_HTTP_OFF_3XX;
} else if (error >= NGX_HTTP_BAD_REQUEST
&& error < NGX_HTTP_LAST_4XX)
{
/* 4XX */
err = error - NGX_HTTP_BAD_REQUEST + NGX_HTTP_OFF_4XX;
} else if (error >= NGX_HTTP_NGINX_CODES
&& error < NGX_HTTP_LAST_5XX)
{
/* 49X, 5XX */
err = error - NGX_HTTP_NGINX_CODES + NGX_HTTP_OFF_5XX;
switch (error) {
case NGX_HTTP_TO_HTTPS:
case NGX_HTTPS_CERT_ERROR:
case NGX_HTTPS_NO_CERT:
case NGX_HTTP_REQUEST_HEADER_TOO_LARGE:
r->err_status = NGX_HTTP_BAD_REQUEST;
}
} else {
/* unknown code, zero body */
err = 0;
}
return ngx_http_send_special_response(r, clcf, err);
}
ngx_int_t
ngx_http_filter_finalize_request(ngx_http_request_t *r, ngx_module_t *m,
ngx_int_t error)
{
void *ctx;
ngx_int_t rc;
ngx_http_clean_header(r);
ctx = NULL;
if (m) {
ctx = r->ctx[m->ctx_index];
}
/* clear the modules contexts */
ngx_memzero(r->ctx, sizeof(void *) * ngx_http_max_module);
if (m) {
r->ctx[m->ctx_index] = ctx;
}
r->filter_finalize = 1;
rc = ngx_http_special_response_handler(r, error);
/* NGX_ERROR resets any pending data */
switch (rc) {
case NGX_OK:
case NGX_DONE:
return NGX_ERROR;
default:
return rc;
}
}
void
ngx_http_clean_header(ngx_http_request_t *r)
{
ngx_memzero(&r->headers_out.status,
sizeof(ngx_http_headers_out_t)
- offsetof(ngx_http_headers_out_t, status));
r->headers_out.headers.part.nelts = 0;
r->headers_out.headers.part.next = NULL;
r->headers_out.headers.last = &r->headers_out.headers.part;
r->headers_out.content_length_n = -1;
r->headers_out.last_modified_time = -1;
}
static ngx_int_t
ngx_http_send_error_page(ngx_http_request_t *r, ngx_http_err_page_t *err_page)
{
ngx_int_t overwrite;
ngx_str_t uri, args;
ngx_table_elt_t *location;
ngx_http_core_loc_conf_t *clcf;
overwrite = err_page->overwrite;
if (overwrite && overwrite != NGX_HTTP_OK) {
r->expect_tested = 1;
}
if (overwrite >= 0) {
r->err_status = overwrite;
}
if (ngx_http_complex_value(r, &err_page->value, &uri) != NGX_OK) {
return NGX_ERROR;
}
if (uri.len && uri.data[0] == '/') {
if (err_page->value.lengths) {
ngx_http_split_args(r, &uri, &args);
} else {
args = err_page->args;
}
if (r->method != NGX_HTTP_HEAD) {
r->method = NGX_HTTP_GET;
r->method_name = ngx_http_core_get_method;
}
return ngx_http_internal_redirect(r, &uri, &args);
}
if (uri.len && uri.data[0] == '@') {
return ngx_http_named_location(r, &uri);
}
location = ngx_list_push(&r->headers_out.headers);
if (location == NULL) {
return NGX_ERROR;
}
if (overwrite != NGX_HTTP_MOVED_PERMANENTLY
&& overwrite != NGX_HTTP_MOVED_TEMPORARILY
&& overwrite != NGX_HTTP_SEE_OTHER
&& overwrite != NGX_HTTP_TEMPORARY_REDIRECT
&& overwrite != NGX_HTTP_PERMANENT_REDIRECT)
{
r->err_status = NGX_HTTP_MOVED_TEMPORARILY;
}
location->hash = 1;
ngx_str_set(&location->key, "Location");
location->value = uri;
ngx_http_clear_location(r);
r->headers_out.location = location;
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
if (clcf->msie_refresh && r->headers_in.msie) {
return ngx_http_send_refresh(r);
}
return ngx_http_send_special_response(r, clcf, r->err_status
- NGX_HTTP_MOVED_PERMANENTLY
+ NGX_HTTP_OFF_3XX);
}
static ngx_int_t
ngx_http_send_special_response(ngx_http_request_t *r,
ngx_http_core_loc_conf_t *clcf, ngx_uint_t err)
{
u_char *tail;
size_t len;
ngx_int_t rc;
ngx_buf_t *b;
ngx_uint_t msie_padding;
ngx_chain_t out[3];
if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_ON) {
len = sizeof(ngx_http_error_full_tail) - 1;
tail = ngx_http_error_full_tail;
} else if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_BUILD) {
len = sizeof(ngx_http_error_build_tail) - 1;
tail = ngx_http_error_build_tail;
} else {
len = sizeof(ngx_http_error_tail) - 1;
tail = ngx_http_error_tail;
}
msie_padding = 0;
if (ngx_http_error_pages[err].len) {
r->headers_out.content_length_n = ngx_http_error_pages[err].len + len;
if (clcf->msie_padding
&& (r->headers_in.msie || r->headers_in.chrome)
&& r->http_version >= NGX_HTTP_VERSION_10
&& err >= NGX_HTTP_OFF_4XX)
{
r->headers_out.content_length_n +=
sizeof(ngx_http_msie_padding) - 1;
msie_padding = 1;
}
r->headers_out.content_type_len = sizeof("text/html") - 1;
ngx_str_set(&r->headers_out.content_type, "text/html");
r->headers_out.content_type_lowcase = NULL;
} else {
r->headers_out.content_length_n = 0;
}
if (r->headers_out.content_length) {
r->headers_out.content_length->hash = 0;
r->headers_out.content_length = NULL;
}
ngx_http_clear_accept_ranges(r);
ngx_http_clear_last_modified(r);
ngx_http_clear_etag(r);
rc = ngx_http_send_header(r);
if (rc == NGX_ERROR || r->header_only) {
return rc;
}
if (ngx_http_error_pages[err].len == 0) {
return ngx_http_send_special(r, NGX_HTTP_LAST);
}
b = ngx_calloc_buf(r->pool);
if (b == NULL) {
return NGX_ERROR;
}
b->memory = 1;
b->pos = ngx_http_error_pages[err].data;
b->last = ngx_http_error_pages[err].data + ngx_http_error_pages[err].len;
out[0].buf = b;
out[0].next = &out[1];
b = ngx_calloc_buf(r->pool);
if (b == NULL) {
return NGX_ERROR;
}
b->memory = 1;
b->pos = tail;
b->last = tail + len;
out[1].buf = b;
out[1].next = NULL;
if (msie_padding) {
b = ngx_calloc_buf(r->pool);
if (b == NULL) {
return NGX_ERROR;
}
b->memory = 1;
b->pos = ngx_http_msie_padding;
b->last = ngx_http_msie_padding + sizeof(ngx_http_msie_padding) - 1;
out[1].next = &out[2];
out[2].buf = b;
out[2].next = NULL;
}
if (r == r->main) {
b->last_buf = 1;
}
b->last_in_chain = 1;
return ngx_http_output_filter(r, &out[0]);
}
static ngx_int_t
ngx_http_send_refresh(ngx_http_request_t *r)
{
u_char *p, *location;
size_t len, size;
uintptr_t escape;
ngx_int_t rc;
ngx_buf_t *b;
ngx_chain_t out;
len = r->headers_out.location->value.len;
location = r->headers_out.location->value.data;
escape = 2 * ngx_escape_uri(NULL, location, len, NGX_ESCAPE_REFRESH);
size = sizeof(ngx_http_msie_refresh_head) - 1
+ escape + len
+ sizeof(ngx_http_msie_refresh_tail) - 1;
r->err_status = NGX_HTTP_OK;
r->headers_out.content_type_len = sizeof("text/html") - 1;
ngx_str_set(&r->headers_out.content_type, "text/html");
r->headers_out.content_type_lowcase = NULL;
r->headers_out.location->hash = 0;
r->headers_out.location = NULL;
r->headers_out.content_length_n = size;
if (r->headers_out.content_length) {
r->headers_out.content_length->hash = 0;
r->headers_out.content_length = NULL;
}
ngx_http_clear_accept_ranges(r);
ngx_http_clear_last_modified(r);
ngx_http_clear_etag(r);
rc = ngx_http_send_header(r);
if (rc == NGX_ERROR || r->header_only) {
return rc;
}
b = ngx_create_temp_buf(r->pool, size);
if (b == NULL) {
return NGX_ERROR;
}
p = ngx_cpymem(b->pos, ngx_http_msie_refresh_head,
sizeof(ngx_http_msie_refresh_head) - 1);
if (escape == 0) {
p = ngx_cpymem(p, location, len);
} else {
p = (u_char *) ngx_escape_uri(p, location, len, NGX_ESCAPE_REFRESH);
}
b->last = ngx_cpymem(p, ngx_http_msie_refresh_tail,
sizeof(ngx_http_msie_refresh_tail) - 1);
b->last_buf = (r == r->main) ? 1 : 0;
b->last_in_chain = 1;
out.buf = b;
out.next = NULL;
return ngx_http_output_filter(r, &out);
}
nginx-1.14.0/src/http/ngx_http_request.c 000644 001751 001751 00000267374 13265410474 021454 0 ustar 00mdounin mdounin 000000 000000
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#include
#include
#include
static void ngx_http_wait_request_handler(ngx_event_t *ev);
static void ngx_http_process_request_line(ngx_event_t *rev);
static void ngx_http_process_request_headers(ngx_event_t *rev);
static ssize_t ngx_http_read_request_header(ngx_http_request_t *r);
static ngx_int_t ngx_http_alloc_large_header_buffer(ngx_http_request_t *r,
ngx_uint_t request_line);
static ngx_int_t ngx_http_process_header_line(ngx_http_request_t *r,
ngx_table_elt_t *h, ngx_uint_t offset);
static ngx_int_t ngx_http_process_unique_header_line(ngx_http_request_t *r,
ngx_table_elt_t *h, ngx_uint_t offset);
static ngx_int_t ngx_http_process_multi_header_lines(ngx_http_request_t *r,
ngx_table_elt_t *h, ngx_uint_t offset);
static ngx_int_t ngx_http_process_host(ngx_http_request_t *r,
ngx_table_elt_t *h, ngx_uint_t offset);
static ngx_int_t ngx_http_process_connection(ngx_http_request_t *r,
ngx_table_elt_t *h, ngx_uint_t offset);
static ngx_int_t ngx_http_process_user_agent(ngx_http_request_t *r,
ngx_table_elt_t *h, ngx_uint_t offset);
static ngx_int_t ngx_http_validate_host(ngx_str_t *host, ngx_pool_t *pool,
ngx_uint_t alloc);
static ngx_int_t ngx_http_set_virtual_server(ngx_http_request_t *r,
ngx_str_t *host);
static ngx_int_t ngx_http_find_virtual_server(ngx_connection_t *c,
ngx_http_virtual_names_t *virtual_names, ngx_str_t *host,
ngx_http_request_t *r, ngx_http_core_srv_conf_t **cscfp);
static void ngx_http_request_handler(ngx_event_t *ev);
static void ngx_http_terminate_request(ngx_http_request_t *r, ngx_int_t rc);
static void ngx_http_terminate_handler(ngx_http_request_t *r);
static void ngx_http_finalize_connection(ngx_http_request_t *r);
static ngx_int_t ngx_http_set_write_handler(ngx_http_request_t *r);
static void ngx_http_writer(ngx_http_request_t *r);
static void ngx_http_request_finalizer(ngx_http_request_t *r);
static void ngx_http_set_keepalive(ngx_http_request_t *r);
static void ngx_http_keepalive_handler(ngx_event_t *ev);
static void ngx_http_set_lingering_close(ngx_http_request_t *r);
static void ngx_http_lingering_close_handler(ngx_event_t *ev);
static ngx_int_t ngx_http_post_action(ngx_http_request_t *r);
static void ngx_http_close_request(ngx_http_request_t *r, ngx_int_t error);
static void ngx_http_log_request(ngx_http_request_t *r);
static u_char *ngx_http_log_error(ngx_log_t *log, u_char *buf, size_t len);
static u_char *ngx_http_log_error_handler(ngx_http_request_t *r,
ngx_http_request_t *sr, u_char *buf, size_t len);
#if (NGX_HTTP_SSL)
static void ngx_http_ssl_handshake(ngx_event_t *rev);
static void ngx_http_ssl_handshake_handler(ngx_connection_t *c);
#endif
static char *ngx_http_client_errors[] = {
/* NGX_HTTP_PARSE_INVALID_METHOD */
"client sent invalid method",
/* NGX_HTTP_PARSE_INVALID_REQUEST */
"client sent invalid request",
/* NGX_HTTP_PARSE_INVALID_VERSION */
"client sent invalid version",
/* NGX_HTTP_PARSE_INVALID_09_METHOD */
"client sent invalid method in HTTP/0.9 request"
};
ngx_http_header_t ngx_http_headers_in[] = {
{ ngx_string("Host"), offsetof(ngx_http_headers_in_t, host),
ngx_http_process_host },
{ ngx_string("Connection"), offsetof(ngx_http_headers_in_t, connection),
ngx_http_process_connection },
{ ngx_string("If-Modified-Since"),
offsetof(ngx_http_headers_in_t, if_modified_since),
ngx_http_process_unique_header_line },
{ ngx_string("If-Unmodified-Since"),
offsetof(ngx_http_headers_in_t, if_unmodified_since),
ngx_http_process_unique_header_line },
{ ngx_string("If-Match"),
offsetof(ngx_http_headers_in_t, if_match),
ngx_http_process_unique_header_line },
{ ngx_string("If-None-Match"),
offsetof(ngx_http_headers_in_t, if_none_match),
ngx_http_process_unique_header_line },
{ ngx_string("User-Agent"), offsetof(ngx_http_headers_in_t, user_agent),
ngx_http_process_user_agent },
{ ngx_string("Referer"), offsetof(ngx_http_headers_in_t, referer),
ngx_http_process_header_line },
{ ngx_string("Content-Length"),
offsetof(ngx_http_headers_in_t, content_length),
ngx_http_process_unique_header_line },
{ ngx_string("Content-Range"),
offsetof(ngx_http_headers_in_t, content_range),
ngx_http_process_unique_header_line },
{ ngx_string("Content-Type"),
offsetof(ngx_http_headers_in_t, content_type),
ngx_http_process_header_line },
{ ngx_string("Range"), offsetof(ngx_http_headers_in_t, range),
ngx_http_process_header_line },
{ ngx_string("If-Range"),
offsetof(ngx_http_headers_in_t, if_range),
ngx_http_process_unique_header_line },
{ ngx_string("Transfer-Encoding"),
offsetof(ngx_http_headers_in_t, transfer_encoding),
ngx_http_process_header_line },
{ ngx_string("TE"),
offsetof(ngx_http_headers_in_t, te),
ngx_http_process_header_line },
{ ngx_string("Expect"),
offsetof(ngx_http_headers_in_t, expect),
ngx_http_process_unique_header_line },
{ ngx_string("Upgrade"),
offsetof(ngx_http_headers_in_t, upgrade),
ngx_http_process_header_line },
#if (NGX_HTTP_GZIP || NGX_HTTP_HEADERS)
{ ngx_string("Accept-Encoding"),
offsetof(ngx_http_headers_in_t, accept_encoding),
ngx_http_process_header_line },
{ ngx_string("Via"), offsetof(ngx_http_headers_in_t, via),
ngx_http_process_header_line },
#endif
{ ngx_string("Authorization"),
offsetof(ngx_http_headers_in_t, authorization),
ngx_http_process_unique_header_line },
{ ngx_string("Keep-Alive"), offsetof(ngx_http_headers_in_t, keep_alive),
ngx_http_process_header_line },
#if (NGX_HTTP_X_FORWARDED_FOR)
{ ngx_string("X-Forwarded-For"),
offsetof(ngx_http_headers_in_t, x_forwarded_for),
ngx_http_process_multi_header_lines },
#endif
#if (NGX_HTTP_REALIP)
{ ngx_string("X-Real-IP"),
offsetof(ngx_http_headers_in_t, x_real_ip),
ngx_http_process_header_line },
#endif
#if (NGX_HTTP_HEADERS)
{ ngx_string("Accept"), offsetof(ngx_http_headers_in_t, accept),
ngx_http_process_header_line },
{ ngx_string("Accept-Language"),
offsetof(ngx_http_headers_in_t, accept_language),
ngx_http_process_header_line },
#endif
#if (NGX_HTTP_DAV)
{ ngx_string("Depth"), offsetof(ngx_http_headers_in_t, depth),
ngx_http_process_header_line },
{ ngx_string("Destination"), offsetof(ngx_http_headers_in_t, destination),
ngx_http_process_header_line },
{ ngx_string("Overwrite"), offsetof(ngx_http_headers_in_t, overwrite),
ngx_http_process_header_line },
{ ngx_string("Date"), offsetof(ngx_http_headers_in_t, date),
ngx_http_process_header_line },
#endif
{ ngx_string("Cookie"), offsetof(ngx_http_headers_in_t, cookies),
ngx_http_process_multi_header_lines },
{ ngx_null_string, 0, NULL }
};
void
ngx_http_init_connection(ngx_connection_t *c)
{
ngx_uint_t i;
ngx_event_t *rev;
struct sockaddr_in *sin;
ngx_http_port_t *port;
ngx_http_in_addr_t *addr;
ngx_http_log_ctx_t *ctx;
ngx_http_connection_t *hc;
#if (NGX_HAVE_INET6)
struct sockaddr_in6 *sin6;
ngx_http_in6_addr_t *addr6;
#endif
hc = ngx_pcalloc(c->pool, sizeof(ngx_http_connection_t));
if (hc == NULL) {
ngx_http_close_connection(c);
return;
}
c->data = hc;
/* find the server configuration for the address:port */
port = c->listening->servers;
if (port->naddrs > 1) {
/*
* there are several addresses on this port and one of them
* is an "*:port" wildcard so getsockname() in ngx_http_server_addr()
* is required to determine a server address
*/
if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) {
ngx_http_close_connection(c);
return;
}
switch (c->local_sockaddr->sa_family) {
#if (NGX_HAVE_INET6)
case AF_INET6:
sin6 = (struct sockaddr_in6 *) c->local_sockaddr;
addr6 = port->addrs;
/* the last address is "*" */
for (i = 0; i < port->naddrs - 1; i++) {
if (ngx_memcmp(&addr6[i].addr6, &sin6->sin6_addr, 16) == 0) {
break;
}
}
hc->addr_conf = &addr6[i].conf;
break;
#endif
default: /* AF_INET */
sin = (struct sockaddr_in *) c->local_sockaddr;
addr = port->addrs;
/* the last address is "*" */
for (i = 0; i < port->naddrs - 1; i++) {
if (addr[i].addr == sin->sin_addr.s_addr) {
break;
}
}
hc->addr_conf = &addr[i].conf;
break;
}
} else {
switch (c->local_sockaddr->sa_family) {
#if (NGX_HAVE_INET6)
case AF_INET6:
addr6 = port->addrs;
hc->addr_conf = &addr6[0].conf;
break;
#endif
default: /* AF_INET */
addr = port->addrs;
hc->addr_conf = &addr[0].conf;
break;
}
}
/* the default server configuration for the address:port */
hc->conf_ctx = hc->addr_conf->default_server->ctx;
ctx = ngx_palloc(c->pool, sizeof(ngx_http_log_ctx_t));
if (ctx == NULL) {
ngx_http_close_connection(c);
return;
}
ctx->connection = c;
ctx->request = NULL;
ctx->current_request = NULL;
c->log->connection = c->number;
c->log->handler = ngx_http_log_error;
c->log->data = ctx;
c->log->action = "waiting for request";
c->log_error = NGX_ERROR_INFO;
rev = c->read;
rev->handler = ngx_http_wait_request_handler;
c->write->handler = ngx_http_empty_handler;
#if (NGX_HTTP_V2)
if (hc->addr_conf->http2) {
rev->handler = ngx_http_v2_init;
}
#endif
#if (NGX_HTTP_SSL)
{
ngx_http_ssl_srv_conf_t *sscf;
sscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_ssl_module);
if (sscf->enable || hc->addr_conf->ssl) {
c->log->action = "SSL handshaking";
if (hc->addr_conf->ssl && sscf->ssl.ctx == NULL) {
ngx_log_error(NGX_LOG_ERR, c->log, 0,
"no \"ssl_certificate\" is defined "
"in server listening on SSL port");
ngx_http_close_connection(c);
return;
}
hc->ssl = 1;
rev->handler = ngx_http_ssl_handshake;
}
}
#endif
if (hc->addr_conf->proxy_protocol) {
hc->proxy_protocol = 1;
c->log->action = "reading PROXY protocol";
}
if (rev->ready) {
/* the deferred accept(), iocp */
if (ngx_use_accept_mutex) {
ngx_post_event(rev, &ngx_posted_events);
return;
}
rev->handler(rev);
return;
}
ngx_add_timer(rev, c->listening->post_accept_timeout);
ngx_reusable_connection(c, 1);
if (ngx_handle_read_event(rev, 0) != NGX_OK) {
ngx_http_close_connection(c);
return;
}
}
static void
ngx_http_wait_request_handler(ngx_event_t *rev)
{
u_char *p;
size_t size;
ssize_t n;
ngx_buf_t *b;
ngx_connection_t *c;
ngx_http_connection_t *hc;
ngx_http_core_srv_conf_t *cscf;
c = rev->data;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http wait request handler");
if (rev->timedout) {
ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
ngx_http_close_connection(c);
return;
}
if (c->close) {
ngx_http_close_connection(c);
return;
}
hc = c->data;
cscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_core_module);
size = cscf->client_header_buffer_size;
b = c->buffer;
if (b == NULL) {
b = ngx_create_temp_buf(c->pool, size);
if (b == NULL) {
ngx_http_close_connection(c);
return;
}
c->buffer = b;
} else if (b->start == NULL) {
b->start = ngx_palloc(c->pool, size);
if (b->start == NULL) {
ngx_http_close_connection(c);
return;
}
b->pos = b->start;
b->last = b->start;
b->end = b->last + size;
}
n = c->recv(c, b->last, size);
if (n == NGX_AGAIN) {
if (!rev->timer_set) {
ngx_add_timer(rev, c->listening->post_accept_timeout);
ngx_reusable_connection(c, 1);
}
if (ngx_handle_read_event(rev, 0) != NGX_OK) {
ngx_http_close_connection(c);
return;
}
/*
* We are trying to not hold c->buffer's memory for an idle connection.
*/
if (ngx_pfree(c->pool, b->start) == NGX_OK) {
b->start = NULL;
}
return;
}
if (n == NGX_ERROR) {
ngx_http_close_connection(c);
return;
}
if (n == 0) {
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"client closed connection");
ngx_http_close_connection(c);
return;
}
b->last += n;
if (hc->proxy_protocol) {
hc->proxy_protocol = 0;
p = ngx_proxy_protocol_read(c, b->pos, b->last);
if (p == NULL) {
ngx_http_close_connection(c);
return;
}
b->pos = p;
if (b->pos == b->last) {
c->log->action = "waiting for request";
b->pos = b->start;
b->last = b->start;
ngx_post_event(rev, &ngx_posted_events);
return;
}
}
c->log->action = "reading client request line";
ngx_reusable_connection(c, 0);
c->data = ngx_http_create_request(c);
if (c->data == NULL) {
ngx_http_close_connection(c);
return;
}
rev->handler = ngx_http_process_request_line;
ngx_http_process_request_line(rev);
}
ngx_http_request_t *
ngx_http_create_request(ngx_connection_t *c)
{
ngx_pool_t *pool;
ngx_time_t *tp;
ngx_http_request_t *r;
ngx_http_log_ctx_t *ctx;
ngx_http_connection_t *hc;
ngx_http_core_srv_conf_t *cscf;
ngx_http_core_loc_conf_t *clcf;
ngx_http_core_main_conf_t *cmcf;
c->requests++;
hc = c->data;
cscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_core_module);
pool = ngx_create_pool(cscf->request_pool_size, c->log);
if (pool == NULL) {
return NULL;
}
r = ngx_pcalloc(pool, sizeof(ngx_http_request_t));
if (r == NULL) {
ngx_destroy_pool(pool);
return NULL;
}
r->pool = pool;
r->http_connection = hc;
r->signature = NGX_HTTP_MODULE;
r->connection = c;
r->main_conf = hc->conf_ctx->main_conf;
r->srv_conf = hc->conf_ctx->srv_conf;
r->loc_conf = hc->conf_ctx->loc_conf;
r->read_event_handler = ngx_http_block_reading;
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
ngx_set_connection_log(r->connection, clcf->error_log);
r->header_in = hc->busy ? hc->busy->buf : c->buffer;
if (ngx_list_init(&r->headers_out.headers, r->pool, 20,
sizeof(ngx_table_elt_t))
!= NGX_OK)
{
ngx_destroy_pool(r->pool);
return NULL;
}
if (ngx_list_init(&r->headers_out.trailers, r->pool, 4,
sizeof(ngx_table_elt_t))
!= NGX_OK)
{
ngx_destroy_pool(r->pool);
return NULL;
}
r->ctx = ngx_pcalloc(r->pool, sizeof(void *) * ngx_http_max_module);
if (r->ctx == NULL) {
ngx_destroy_pool(r->pool);
return NULL;
}
cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
r->variables = ngx_pcalloc(r->pool, cmcf->variables.nelts
* sizeof(ngx_http_variable_value_t));
if (r->variables == NULL) {
ngx_destroy_pool(r->pool);
return NULL;
}
#if (NGX_HTTP_SSL)
if (c->ssl) {
r->main_filter_need_in_memory = 1;
}
#endif
r->main = r;
r->count = 1;
tp = ngx_timeofday();
r->start_sec = tp->sec;
r->start_msec = tp->msec;
r->method = NGX_HTTP_UNKNOWN;
r->http_version = NGX_HTTP_VERSION_10;
r->headers_in.content_length_n = -1;
r->headers_in.keep_alive_n = -1;
r->headers_out.content_length_n = -1;
r->headers_out.last_modified_time = -1;
r->uri_changes = NGX_HTTP_MAX_URI_CHANGES + 1;
r->subrequests = NGX_HTTP_MAX_SUBREQUESTS + 1;
r->http_state = NGX_HTTP_READING_REQUEST_STATE;
ctx = c->log->data;
ctx->request = r;
ctx->current_request = r;
r->log_handler = ngx_http_log_error_handler;
#if (NGX_STAT_STUB)
(void) ngx_atomic_fetch_add(ngx_stat_reading, 1);
r->stat_reading = 1;
(void) ngx_atomic_fetch_add(ngx_stat_requests, 1);
#endif
return r;
}
#if (NGX_HTTP_SSL)
static void
ngx_http_ssl_handshake(ngx_event_t *rev)
{
u_char *p, buf[NGX_PROXY_PROTOCOL_MAX_HEADER + 1];
size_t size;
ssize_t n;
ngx_err_t err;
ngx_int_t rc;
ngx_connection_t *c;
ngx_http_connection_t *hc;
ngx_http_ssl_srv_conf_t *sscf;
ngx_http_core_loc_conf_t *clcf;
c = rev->data;
hc = c->data;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0,
"http check ssl handshake");
if (rev->timedout) {
ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
ngx_http_close_connection(c);
return;
}
if (c->close) {
ngx_http_close_connection(c);
return;
}
size = hc->proxy_protocol ? sizeof(buf) : 1;
n = recv(c->fd, (char *) buf, size, MSG_PEEK);
err = ngx_socket_errno;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, rev->log, 0, "http recv(): %z", n);
if (n == -1) {
if (err == NGX_EAGAIN) {
rev->ready = 0;
if (!rev->timer_set) {
ngx_add_timer(rev, c->listening->post_accept_timeout);
ngx_reusable_connection(c, 1);
}
if (ngx_handle_read_event(rev, 0) != NGX_OK) {
ngx_http_close_connection(c);
}
return;
}
ngx_connection_error(c, err, "recv() failed");
ngx_http_close_connection(c);
return;
}
if (hc->proxy_protocol) {
hc->proxy_protocol = 0;
p = ngx_proxy_protocol_read(c, buf, buf + n);
if (p == NULL) {
ngx_http_close_connection(c);
return;
}
size = p - buf;
if (c->recv(c, buf, size) != (ssize_t) size) {
ngx_http_close_connection(c);
return;
}
c->log->action = "SSL handshaking";
if (n == (ssize_t) size) {
ngx_post_event(rev, &ngx_posted_events);
return;
}
n = 1;
buf[0] = *p;
}
if (n == 1) {
if (buf[0] & 0x80 /* SSLv2 */ || buf[0] == 0x16 /* SSLv3/TLSv1 */) {
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, rev->log, 0,
"https ssl handshake: 0x%02Xd", buf[0]);
clcf = ngx_http_get_module_loc_conf(hc->conf_ctx,
ngx_http_core_module);
if (clcf->tcp_nodelay && ngx_tcp_nodelay(c) != NGX_OK) {
ngx_http_close_connection(c);
return;
}
sscf = ngx_http_get_module_srv_conf(hc->conf_ctx,
ngx_http_ssl_module);
if (ngx_ssl_create_connection(&sscf->ssl, c, NGX_SSL_BUFFER)
!= NGX_OK)
{
ngx_http_close_connection(c);
return;
}
rc = ngx_ssl_handshake(c);
if (rc == NGX_AGAIN) {
if (!rev->timer_set) {
ngx_add_timer(rev, c->listening->post_accept_timeout);
}
ngx_reusable_connection(c, 0);
c->ssl->handler = ngx_http_ssl_handshake_handler;
return;
}
ngx_http_ssl_handshake_handler(c);
return;
}
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "plain http");
c->log->action = "waiting for request";
rev->handler = ngx_http_wait_request_handler;
ngx_http_wait_request_handler(rev);
return;
}
ngx_log_error(NGX_LOG_INFO, c->log, 0, "client closed connection");
ngx_http_close_connection(c);
}
static void
ngx_http_ssl_handshake_handler(ngx_connection_t *c)
{
if (c->ssl->handshaked) {
/*
* The majority of browsers do not send the "close notify" alert.
* Among them are MSIE, old Mozilla, Netscape 4, Konqueror,
* and Links. And what is more, MSIE ignores the server's alert.
*
* Opera and recent Mozilla send the alert.
*/
c->ssl->no_wait_shutdown = 1;
#if (NGX_HTTP_V2 \
&& (defined TLSEXT_TYPE_application_layer_protocol_negotiation \
|| defined TLSEXT_TYPE_next_proto_neg))
{
unsigned int len;
const unsigned char *data;
ngx_http_connection_t *hc;
hc = c->data;
if (hc->addr_conf->http2) {
#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
SSL_get0_alpn_selected(c->ssl->connection, &data, &len);
#ifdef TLSEXT_TYPE_next_proto_neg
if (len == 0) {
SSL_get0_next_proto_negotiated(c->ssl->connection, &data, &len);
}
#endif
#else /* TLSEXT_TYPE_next_proto_neg */
SSL_get0_next_proto_negotiated(c->ssl->connection, &data, &len);
#endif
if (len == 2 && data[0] == 'h' && data[1] == '2') {
ngx_http_v2_init(c->read);
return;
}
}
}
#endif
c->log->action = "waiting for request";
c->read->handler = ngx_http_wait_request_handler;
/* STUB: epoll edge */ c->write->handler = ngx_http_empty_handler;
ngx_reusable_connection(c, 1);
ngx_http_wait_request_handler(c->read);
return;
}
if (c->read->timedout) {
ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
}
ngx_http_close_connection(c);
}
#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
int
ngx_http_ssl_servername(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg)
{
ngx_str_t host;
const char *servername;
ngx_connection_t *c;
ngx_http_connection_t *hc;
ngx_http_ssl_srv_conf_t *sscf;
ngx_http_core_loc_conf_t *clcf;
ngx_http_core_srv_conf_t *cscf;
servername = SSL_get_servername(ssl_conn, TLSEXT_NAMETYPE_host_name);
if (servername == NULL) {
return SSL_TLSEXT_ERR_NOACK;
}
c = ngx_ssl_get_connection(ssl_conn);
if (c->ssl->renegotiation) {
return SSL_TLSEXT_ERR_NOACK;
}
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
"SSL server name: \"%s\"", servername);
host.len = ngx_strlen(servername);
if (host.len == 0) {
return SSL_TLSEXT_ERR_NOACK;
}
host.data = (u_char *) servername;
if (ngx_http_validate_host(&host, c->pool, 1) != NGX_OK) {
return SSL_TLSEXT_ERR_NOACK;
}
hc = c->data;
if (ngx_http_find_virtual_server(c, hc->addr_conf->virtual_names, &host,
NULL, &cscf)
!= NGX_OK)
{
return SSL_TLSEXT_ERR_NOACK;
}
hc->ssl_servername = ngx_palloc(c->pool, sizeof(ngx_str_t));
if (hc->ssl_servername == NULL) {
return SSL_TLSEXT_ERR_NOACK;
}
*hc->ssl_servername = host;
hc->conf_ctx = cscf->ctx;
clcf = ngx_http_get_module_loc_conf(hc->conf_ctx, ngx_http_core_module);
ngx_set_connection_log(c, clcf->error_log);
sscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_ssl_module);
c->ssl->buffer_size = sscf->buffer_size;
if (sscf->ssl.ctx) {
SSL_set_SSL_CTX(ssl_conn, sscf->ssl.ctx);
/*
* SSL_set_SSL_CTX() only changes certs as of 1.0.0d
* adjust other things we care about
*/
SSL_set_verify(ssl_conn, SSL_CTX_get_verify_mode(sscf->ssl.ctx),
SSL_CTX_get_verify_callback(sscf->ssl.ctx));
SSL_set_verify_depth(ssl_conn, SSL_CTX_get_verify_depth(sscf->ssl.ctx));
#ifdef SSL_CTRL_CLEAR_OPTIONS
/* only in 0.9.8m+ */
SSL_clear_options(ssl_conn, SSL_get_options(ssl_conn) &
~SSL_CTX_get_options(sscf->ssl.ctx));
#endif
SSL_set_options(ssl_conn, SSL_CTX_get_options(sscf->ssl.ctx));
}
return SSL_TLSEXT_ERR_OK;
}
#endif
#endif
static void
ngx_http_process_request_line(ngx_event_t *rev)
{
ssize_t n;
ngx_int_t rc, rv;
ngx_str_t host;
ngx_connection_t *c;
ngx_http_request_t *r;
c = rev->data;
r = c->data;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0,
"http process request line");
if (rev->timedout) {
ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
c->timedout = 1;
ngx_http_close_request(r, NGX_HTTP_REQUEST_TIME_OUT);
return;
}
rc = NGX_AGAIN;
for ( ;; ) {
if (rc == NGX_AGAIN) {
n = ngx_http_read_request_header(r);
if (n == NGX_AGAIN || n == NGX_ERROR) {
return;
}
}
rc = ngx_http_parse_request_line(r, r->header_in);
if (rc == NGX_OK) {
/* the request line has been parsed successfully */
r->request_line.len = r->request_end - r->request_start;
r->request_line.data = r->request_start;
r->request_length = r->header_in->pos - r->request_start;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
"http request line: \"%V\"", &r->request_line);
r->method_name.len = r->method_end - r->request_start + 1;
r->method_name.data = r->request_line.data;
if (r->http_protocol.data) {
r->http_protocol.len = r->request_end - r->http_protocol.data;
}
if (ngx_http_process_request_uri(r) != NGX_OK) {
return;
}
if (r->host_start && r->host_end) {
host.len = r->host_end - r->host_start;
host.data = r->host_start;
rc = ngx_http_validate_host(&host, r->pool, 0);
if (rc == NGX_DECLINED) {
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"client sent invalid host in request line");
ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
return;
}
if (rc == NGX_ERROR) {
ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
}
if (ngx_http_set_virtual_server(r, &host) == NGX_ERROR) {
return;
}
r->headers_in.server = host;
}
if (r->http_version < NGX_HTTP_VERSION_10) {
if (r->headers_in.server.len == 0
&& ngx_http_set_virtual_server(r, &r->headers_in.server)
== NGX_ERROR)
{
return;
}
ngx_http_process_request(r);
return;
}
if (ngx_list_init(&r->headers_in.headers, r->pool, 20,
sizeof(ngx_table_elt_t))
!= NGX_OK)
{
ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
}
c->log->action = "reading client request headers";
rev->handler = ngx_http_process_request_headers;
ngx_http_process_request_headers(rev);
return;
}
if (rc != NGX_AGAIN) {
/* there was error while a request line parsing */
ngx_log_error(NGX_LOG_INFO, c->log, 0,
ngx_http_client_errors[rc - NGX_HTTP_CLIENT_ERROR]);
if (rc == NGX_HTTP_PARSE_INVALID_VERSION) {
ngx_http_finalize_request(r, NGX_HTTP_VERSION_NOT_SUPPORTED);
} else {
ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
}
return;
}
/* NGX_AGAIN: a request line parsing is still incomplete */
if (r->header_in->pos == r->header_in->end) {
rv = ngx_http_alloc_large_header_buffer(r, 1);
if (rv == NGX_ERROR) {
ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
}
if (rv == NGX_DECLINED) {
r->request_line.len = r->header_in->end - r->request_start;
r->request_line.data = r->request_start;
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"client sent too long URI");
ngx_http_finalize_request(r, NGX_HTTP_REQUEST_URI_TOO_LARGE);
return;
}
}
}
}
ngx_int_t
ngx_http_process_request_uri(ngx_http_request_t *r)
{
ngx_http_core_srv_conf_t *cscf;
if (r->args_start) {
r->uri.len = r->args_start - 1 - r->uri_start;
} else {
r->uri.len = r->uri_end - r->uri_start;
}
if (r->complex_uri || r->quoted_uri) {
r->uri.data = ngx_pnalloc(r->pool, r->uri.len + 1);
if (r->uri.data == NULL) {
ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
return NGX_ERROR;
}
cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
if (ngx_http_parse_complex_uri(r, cscf->merge_slashes) != NGX_OK) {
r->uri.len = 0;
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client sent invalid request");
ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
return NGX_ERROR;
}
} else {
r->uri.data = r->uri_start;
}
r->unparsed_uri.len = r->uri_end - r->uri_start;
r->unparsed_uri.data = r->uri_start;
r->valid_unparsed_uri = r->space_in_uri ? 0 : 1;
if (r->uri_ext) {
if (r->args_start) {
r->exten.len = r->args_start - 1 - r->uri_ext;
} else {
r->exten.len = r->uri_end - r->uri_ext;
}
r->exten.data = r->uri_ext;
}
if (r->args_start && r->uri_end > r->args_start) {
r->args.len = r->uri_end - r->args_start;
r->args.data = r->args_start;
}
#if (NGX_WIN32)
{
u_char *p, *last;
p = r->uri.data;
last = r->uri.data + r->uri.len;
while (p < last) {
if (*p++ == ':') {
/*
* this check covers "::$data", "::$index_allocation" and
* ":$i30:$index_allocation"
*/
if (p < last && *p == '$') {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client sent unsafe win32 URI");
ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
return NGX_ERROR;
}
}
}
p = r->uri.data + r->uri.len - 1;
while (p > r->uri.data) {
if (*p == ' ') {
p--;
continue;
}
if (*p == '.') {
p--;
continue;
}
break;
}
if (p != r->uri.data + r->uri.len - 1) {
r->uri.len = p + 1 - r->uri.data;
ngx_http_set_exten(r);
}
}
#endif
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http uri: \"%V\"", &r->uri);
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http args: \"%V\"", &r->args);
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http exten: \"%V\"", &r->exten);
return NGX_OK;
}
static void
ngx_http_process_request_headers(ngx_event_t *rev)
{
u_char *p;
size_t len;
ssize_t n;
ngx_int_t rc, rv;
ngx_table_elt_t *h;
ngx_connection_t *c;
ngx_http_header_t *hh;
ngx_http_request_t *r;
ngx_http_core_srv_conf_t *cscf;
ngx_http_core_main_conf_t *cmcf;
c = rev->data;
r = c->data;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0,
"http process request header line");
if (rev->timedout) {
ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
c->timedout = 1;
ngx_http_close_request(r, NGX_HTTP_REQUEST_TIME_OUT);
return;
}
cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
rc = NGX_AGAIN;
for ( ;; ) {
if (rc == NGX_AGAIN) {
if (r->header_in->pos == r->header_in->end) {
rv = ngx_http_alloc_large_header_buffer(r, 0);
if (rv == NGX_ERROR) {
ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
}
if (rv == NGX_DECLINED) {
p = r->header_name_start;
r->lingering_close = 1;
if (p == NULL) {
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"client sent too large request");
ngx_http_finalize_request(r,
NGX_HTTP_REQUEST_HEADER_TOO_LARGE);
return;
}
len = r->header_in->end - p;
if (len > NGX_MAX_ERROR_STR - 300) {
len = NGX_MAX_ERROR_STR - 300;
}
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"client sent too long header line: \"%*s...\"",
len, r->header_name_start);
ngx_http_finalize_request(r,
NGX_HTTP_REQUEST_HEADER_TOO_LARGE);
return;
}
}
n = ngx_http_read_request_header(r);
if (n == NGX_AGAIN || n == NGX_ERROR) {
return;
}
}
/* the host header could change the server configuration context */
cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
rc = ngx_http_parse_header_line(r, r->header_in,
cscf->underscores_in_headers);
if (rc == NGX_OK) {
r->request_length += r->header_in->pos - r->header_name_start;
if (r->invalid_header && cscf->ignore_invalid_headers) {
/* there was error while a header line parsing */
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"client sent invalid header line: \"%*s\"",
r->header_end - r->header_name_start,
r->header_name_start);
continue;
}
/* a header line has been parsed successfully */
h = ngx_list_push(&r->headers_in.headers);
if (h == NULL) {
ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
}
h->hash = r->header_hash;
h->key.len = r->header_name_end - r->header_name_start;
h->key.data = r->header_name_start;
h->key.data[h->key.len] = '\0';
h->value.len = r->header_end - r->header_start;
h->value.data = r->header_start;
h->value.data[h->value.len] = '\0';
h->lowcase_key = ngx_pnalloc(r->pool, h->key.len);
if (h->lowcase_key == NULL) {
ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
}
if (h->key.len == r->lowcase_index) {
ngx_memcpy(h->lowcase_key, r->lowcase_header, h->key.len);
} else {
ngx_strlow(h->lowcase_key, h->key.data, h->key.len);
}
hh = ngx_hash_find(&cmcf->headers_in_hash, h->hash,
h->lowcase_key, h->key.len);
if (hh && hh->handler(r, h, hh->offset) != NGX_OK) {
return;
}
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http header: \"%V: %V\"",
&h->key, &h->value);
continue;
}
if (rc == NGX_HTTP_PARSE_HEADER_DONE) {
/* a whole header has been parsed successfully */
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http header done");
r->request_length += r->header_in->pos - r->header_name_start;
r->http_state = NGX_HTTP_PROCESS_REQUEST_STATE;
rc = ngx_http_process_request_header(r);
if (rc != NGX_OK) {
return;
}
ngx_http_process_request(r);
return;
}
if (rc == NGX_AGAIN) {
/* a header line parsing is still not complete */
continue;
}
/* rc == NGX_HTTP_PARSE_INVALID_HEADER */
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"client sent invalid header line");
ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
return;
}
}
static ssize_t
ngx_http_read_request_header(ngx_http_request_t *r)
{
ssize_t n;
ngx_event_t *rev;
ngx_connection_t *c;
ngx_http_core_srv_conf_t *cscf;
c = r->connection;
rev = c->read;
n = r->header_in->last - r->header_in->pos;
if (n > 0) {
return n;
}
if (rev->ready) {
n = c->recv(c, r->header_in->last,
r->header_in->end - r->header_in->last);
} else {
n = NGX_AGAIN;
}
if (n == NGX_AGAIN) {
if (!rev->timer_set) {
cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
ngx_add_timer(rev, cscf->client_header_timeout);
}
if (ngx_handle_read_event(rev, 0) != NGX_OK) {
ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
return NGX_ERROR;
}
return NGX_AGAIN;
}
if (n == 0) {
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"client prematurely closed connection");
}
if (n == 0 || n == NGX_ERROR) {
c->error = 1;
c->log->action = "reading client request headers";
ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
return NGX_ERROR;
}
r->header_in->last += n;
return n;
}
static ngx_int_t
ngx_http_alloc_large_header_buffer(ngx_http_request_t *r,
ngx_uint_t request_line)
{
u_char *old, *new;
ngx_buf_t *b;
ngx_chain_t *cl;
ngx_http_connection_t *hc;
ngx_http_core_srv_conf_t *cscf;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http alloc large header buffer");
if (request_line && r->state == 0) {
/* the client fills up the buffer with "\r\n" */
r->header_in->pos = r->header_in->start;
r->header_in->last = r->header_in->start;
return NGX_OK;
}
old = request_line ? r->request_start : r->header_name_start;
cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
if (r->state != 0
&& (size_t) (r->header_in->pos - old)
>= cscf->large_client_header_buffers.size)
{
return NGX_DECLINED;
}
hc = r->http_connection;
if (hc->free) {
cl = hc->free;
hc->free = cl->next;
b = cl->buf;
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http large header free: %p %uz",
b->pos, b->end - b->last);
} else if (hc->nbusy < cscf->large_client_header_buffers.num) {
b = ngx_create_temp_buf(r->connection->pool,
cscf->large_client_header_buffers.size);
if (b == NULL) {
return NGX_ERROR;
}
cl = ngx_alloc_chain_link(r->connection->pool);
if (cl == NULL) {
return NGX_ERROR;
}
cl->buf = b;
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http large header alloc: %p %uz",
b->pos, b->end - b->last);
} else {
return NGX_DECLINED;
}
cl->next = hc->busy;
hc->busy = cl;
hc->nbusy++;
if (r->state == 0) {
/*
* r->state == 0 means that a header line was parsed successfully
* and we do not need to copy incomplete header line and
* to relocate the parser header pointers
*/
r->header_in = b;
return NGX_OK;
}
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http large header copy: %uz", r->header_in->pos - old);
new = b->start;
ngx_memcpy(new, old, r->header_in->pos - old);
b->pos = new + (r->header_in->pos - old);
b->last = new + (r->header_in->pos - old);
if (request_line) {
r->request_start = new;
if (r->request_end) {
r->request_end = new + (r->request_end - old);
}
r->method_end = new + (r->method_end - old);
r->uri_start = new + (r->uri_start - old);
r->uri_end = new + (r->uri_end - old);
if (r->schema_start) {
r->schema_start = new + (r->schema_start - old);
r->schema_end = new + (r->schema_end - old);
}
if (r->host_start) {
r->host_start = new + (r->host_start - old);
if (r->host_end) {
r->host_end = new + (r->host_end - old);
}
}
if (r->port_start) {
r->port_start = new + (r->port_start - old);
r->port_end = new + (r->port_end - old);
}
if (r->uri_ext) {
r->uri_ext = new + (r->uri_ext - old);
}
if (r->args_start) {
r->args_start = new + (r->args_start - old);
}
if (r->http_protocol.data) {
r->http_protocol.data = new + (r->http_protocol.data - old);
}
} else {
r->header_name_start = new;
r->header_name_end = new + (r->header_name_end - old);
r->header_start = new + (r->header_start - old);
r->header_end = new + (r->header_end - old);
}
r->header_in = b;
return NGX_OK;
}
static ngx_int_t
ngx_http_process_header_line(ngx_http_request_t *r, ngx_table_elt_t *h,
ngx_uint_t offset)
{
ngx_table_elt_t **ph;
ph = (ngx_table_elt_t **) ((char *) &r->headers_in + offset);
if (*ph == NULL) {
*ph = h;
}
return NGX_OK;
}
static ngx_int_t
ngx_http_process_unique_header_line(ngx_http_request_t *r, ngx_table_elt_t *h,
ngx_uint_t offset)
{
ngx_table_elt_t **ph;
ph = (ngx_table_elt_t **) ((char *) &r->headers_in + offset);
if (*ph == NULL) {
*ph = h;
return NGX_OK;
}
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client sent duplicate header line: \"%V: %V\", "
"previous value: \"%V: %V\"",
&h->key, &h->value, &(*ph)->key, &(*ph)->value);
ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
return NGX_ERROR;
}
static ngx_int_t
ngx_http_process_host(ngx_http_request_t *r, ngx_table_elt_t *h,
ngx_uint_t offset)
{
ngx_int_t rc;
ngx_str_t host;
if (r->headers_in.host == NULL) {
r->headers_in.host = h;
}
host = h->value;
rc = ngx_http_validate_host(&host, r->pool, 0);
if (rc == NGX_DECLINED) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client sent invalid host header");
ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
return NGX_ERROR;
}
if (rc == NGX_ERROR) {
ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
return NGX_ERROR;
}
if (r->headers_in.server.len) {
return NGX_OK;
}
if (ngx_http_set_virtual_server(r, &host) == NGX_ERROR) {
return NGX_ERROR;
}
r->headers_in.server = host;
return NGX_OK;
}
static ngx_int_t
ngx_http_process_connection(ngx_http_request_t *r, ngx_table_elt_t *h,
ngx_uint_t offset)
{
if (ngx_strcasestrn(h->value.data, "close", 5 - 1)) {
r->headers_in.connection_type = NGX_HTTP_CONNECTION_CLOSE;
} else if (ngx_strcasestrn(h->value.data, "keep-alive", 10 - 1)) {
r->headers_in.connection_type = NGX_HTTP_CONNECTION_KEEP_ALIVE;
}
return NGX_OK;
}
static ngx_int_t
ngx_http_process_user_agent(ngx_http_request_t *r, ngx_table_elt_t *h,
ngx_uint_t offset)
{
u_char *user_agent, *msie;
if (r->headers_in.user_agent) {
return NGX_OK;
}
r->headers_in.user_agent = h;
/* check some widespread browsers while the header is in CPU cache */
user_agent = h->value.data;
msie = ngx_strstrn(user_agent, "MSIE ", 5 - 1);
if (msie && msie + 7 < user_agent + h->value.len) {
r->headers_in.msie = 1;
if (msie[6] == '.') {
switch (msie[5]) {
case '4':
case '5':
r->headers_in.msie6 = 1;
break;
case '6':
if (ngx_strstrn(msie + 8, "SV1", 3 - 1) == NULL) {
r->headers_in.msie6 = 1;
}
break;
}
}
#if 0
/* MSIE ignores the SSL "close notify" alert */
if (c->ssl) {
c->ssl->no_send_shutdown = 1;
}
#endif
}
if (ngx_strstrn(user_agent, "Opera", 5 - 1)) {
r->headers_in.opera = 1;
r->headers_in.msie = 0;
r->headers_in.msie6 = 0;
}
if (!r->headers_in.msie && !r->headers_in.opera) {
if (ngx_strstrn(user_agent, "Gecko/", 6 - 1)) {
r->headers_in.gecko = 1;
} else if (ngx_strstrn(user_agent, "Chrome/", 7 - 1)) {
r->headers_in.chrome = 1;
} else if (ngx_strstrn(user_agent, "Safari/", 7 - 1)
&& ngx_strstrn(user_agent, "Mac OS X", 8 - 1))
{
r->headers_in.safari = 1;
} else if (ngx_strstrn(user_agent, "Konqueror", 9 - 1)) {
r->headers_in.konqueror = 1;
}
}
return NGX_OK;
}
static ngx_int_t
ngx_http_process_multi_header_lines(ngx_http_request_t *r, ngx_table_elt_t *h,
ngx_uint_t offset)
{
ngx_array_t *headers;
ngx_table_elt_t **ph;
headers = (ngx_array_t *) ((char *) &r->headers_in + offset);
if (headers->elts == NULL) {
if (ngx_array_init(headers, r->pool, 1, sizeof(ngx_table_elt_t *))
!= NGX_OK)
{
ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
return NGX_ERROR;
}
}
ph = ngx_array_push(headers);
if (ph == NULL) {
ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
return NGX_ERROR;
}
*ph = h;
return NGX_OK;
}
ngx_int_t
ngx_http_process_request_header(ngx_http_request_t *r)
{
if (r->headers_in.server.len == 0
&& ngx_http_set_virtual_server(r, &r->headers_in.server)
== NGX_ERROR)
{
return NGX_ERROR;
}
if (r->headers_in.host == NULL && r->http_version > NGX_HTTP_VERSION_10) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client sent HTTP/1.1 request without \"Host\" header");
ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
return NGX_ERROR;
}
if (r->headers_in.content_length) {
r->headers_in.content_length_n =
ngx_atoof(r->headers_in.content_length->value.data,
r->headers_in.content_length->value.len);
if (r->headers_in.content_length_n == NGX_ERROR) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client sent invalid \"Content-Length\" header");
ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
return NGX_ERROR;
}
}
if (r->method == NGX_HTTP_TRACE) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client sent TRACE method");
ngx_http_finalize_request(r, NGX_HTTP_NOT_ALLOWED);
return NGX_ERROR;
}
if (r->headers_in.transfer_encoding) {
if (r->headers_in.transfer_encoding->value.len == 7
&& ngx_strncasecmp(r->headers_in.transfer_encoding->value.data,
(u_char *) "chunked", 7) == 0)
{
r->headers_in.content_length = NULL;
r->headers_in.content_length_n = -1;
r->headers_in.chunked = 1;
} else if (r->headers_in.transfer_encoding->value.len != 8
|| ngx_strncasecmp(r->headers_in.transfer_encoding->value.data,
(u_char *) "identity", 8) != 0)
{
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client sent unknown \"Transfer-Encoding\": \"%V\"",
&r->headers_in.transfer_encoding->value);
ngx_http_finalize_request(r, NGX_HTTP_NOT_IMPLEMENTED);
return NGX_ERROR;
}
}
if (r->headers_in.connection_type == NGX_HTTP_CONNECTION_KEEP_ALIVE) {
if (r->headers_in.keep_alive) {
r->headers_in.keep_alive_n =
ngx_atotm(r->headers_in.keep_alive->value.data,
r->headers_in.keep_alive->value.len);
}
}
return NGX_OK;
}
void
ngx_http_process_request(ngx_http_request_t *r)
{
ngx_connection_t *c;
c = r->connection;
#if (NGX_HTTP_SSL)
if (r->http_connection->ssl) {
long rc;
X509 *cert;
ngx_http_ssl_srv_conf_t *sscf;
if (c->ssl == NULL) {
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"client sent plain HTTP request to HTTPS port");
ngx_http_finalize_request(r, NGX_HTTP_TO_HTTPS);
return;
}
sscf = ngx_http_get_module_srv_conf(r, ngx_http_ssl_module);
if (sscf->verify) {
rc = SSL_get_verify_result(c->ssl->connection);
if (rc != X509_V_OK
&& (sscf->verify != 3 || !ngx_ssl_verify_error_optional(rc)))
{
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"client SSL certificate verify error: (%l:%s)",
rc, X509_verify_cert_error_string(rc));
ngx_ssl_remove_cached_session(c->ssl->session_ctx,
(SSL_get0_session(c->ssl->connection)));
ngx_http_finalize_request(r, NGX_HTTPS_CERT_ERROR);
return;
}
if (sscf->verify == 1) {
cert = SSL_get_peer_certificate(c->ssl->connection);
if (cert == NULL) {
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"client sent no required SSL certificate");
ngx_ssl_remove_cached_session(c->ssl->session_ctx,
(SSL_get0_session(c->ssl->connection)));
ngx_http_finalize_request(r, NGX_HTTPS_NO_CERT);
return;
}
X509_free(cert);
}
}
}
#endif
if (c->read->timer_set) {
ngx_del_timer(c->read);
}
#if (NGX_STAT_STUB)
(void) ngx_atomic_fetch_add(ngx_stat_reading, -1);
r->stat_reading = 0;
(void) ngx_atomic_fetch_add(ngx_stat_writing, 1);
r->stat_writing = 1;
#endif
c->read->handler = ngx_http_request_handler;
c->write->handler = ngx_http_request_handler;
r->read_event_handler = ngx_http_block_reading;
ngx_http_handler(r);
ngx_http_run_posted_requests(c);
}
static ngx_int_t
ngx_http_validate_host(ngx_str_t *host, ngx_pool_t *pool, ngx_uint_t alloc)
{
u_char *h, ch;
size_t i, dot_pos, host_len;
enum {
sw_usual = 0,
sw_literal,
sw_rest
} state;
dot_pos = host->len;
host_len = host->len;
h = host->data;
state = sw_usual;
for (i = 0; i < host->len; i++) {
ch = h[i];
switch (ch) {
case '.':
if (dot_pos == i - 1) {
return NGX_DECLINED;
}
dot_pos = i;
break;
case ':':
if (state == sw_usual) {
host_len = i;
state = sw_rest;
}
break;
case '[':
if (i == 0) {
state = sw_literal;
}
break;
case ']':
if (state == sw_literal) {
host_len = i + 1;
state = sw_rest;
}
break;
case '\0':
return NGX_DECLINED;
default:
if (ngx_path_separator(ch)) {
return NGX_DECLINED;
}
if (ch >= 'A' && ch <= 'Z') {
alloc = 1;
}
break;
}
}
if (dot_pos == host_len - 1) {
host_len--;
}
if (host_len == 0) {
return NGX_DECLINED;
}
if (alloc) {
host->data = ngx_pnalloc(pool, host_len);
if (host->data == NULL) {
return NGX_ERROR;
}
ngx_strlow(host->data, h, host_len);
}
host->len = host_len;
return NGX_OK;
}
static ngx_int_t
ngx_http_set_virtual_server(ngx_http_request_t *r, ngx_str_t *host)
{
ngx_int_t rc;
ngx_http_connection_t *hc;
ngx_http_core_loc_conf_t *clcf;
ngx_http_core_srv_conf_t *cscf;
#if (NGX_SUPPRESS_WARN)
cscf = NULL;
#endif
hc = r->http_connection;
#if (NGX_HTTP_SSL && defined SSL_CTRL_SET_TLSEXT_HOSTNAME)
if (hc->ssl_servername) {
if (hc->ssl_servername->len == host->len
&& ngx_strncmp(hc->ssl_servername->data,
host->data, host->len) == 0)
{
#if (NGX_PCRE)
if (hc->ssl_servername_regex
&& ngx_http_regex_exec(r, hc->ssl_servername_regex,
hc->ssl_servername) != NGX_OK)
{
ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
return NGX_ERROR;
}
#endif
return NGX_OK;
}
}
#endif
rc = ngx_http_find_virtual_server(r->connection,
hc->addr_conf->virtual_names,
host, r, &cscf);
if (rc == NGX_ERROR) {
ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
return NGX_ERROR;
}
#if (NGX_HTTP_SSL && defined SSL_CTRL_SET_TLSEXT_HOSTNAME)
if (hc->ssl_servername) {
ngx_http_ssl_srv_conf_t *sscf;
if (rc == NGX_DECLINED) {
cscf = hc->addr_conf->default_server;
rc = NGX_OK;
}
sscf = ngx_http_get_module_srv_conf(cscf->ctx, ngx_http_ssl_module);
if (sscf->verify) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client attempted to request the server name "
"different from the one that was negotiated");
ngx_http_finalize_request(r, NGX_HTTP_MISDIRECTED_REQUEST);
return NGX_ERROR;
}
}
#endif
if (rc == NGX_DECLINED) {
return NGX_OK;
}
r->srv_conf = cscf->ctx->srv_conf;
r->loc_conf = cscf->ctx->loc_conf;
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
ngx_set_connection_log(r->connection, clcf->error_log);
return NGX_OK;
}
static ngx_int_t
ngx_http_find_virtual_server(ngx_connection_t *c,
ngx_http_virtual_names_t *virtual_names, ngx_str_t *host,
ngx_http_request_t *r, ngx_http_core_srv_conf_t **cscfp)
{
ngx_http_core_srv_conf_t *cscf;
if (virtual_names == NULL) {
return NGX_DECLINED;
}
cscf = ngx_hash_find_combined(&virtual_names->names,
ngx_hash_key(host->data, host->len),
host->data, host->len);
if (cscf) {
*cscfp = cscf;
return NGX_OK;
}
#if (NGX_PCRE)
if (host->len && virtual_names->nregex) {
ngx_int_t n;
ngx_uint_t i;
ngx_http_server_name_t *sn;
sn = virtual_names->regex;
#if (NGX_HTTP_SSL && defined SSL_CTRL_SET_TLSEXT_HOSTNAME)
if (r == NULL) {
ngx_http_connection_t *hc;
for (i = 0; i < virtual_names->nregex; i++) {
n = ngx_regex_exec(sn[i].regex->regex, host, NULL, 0);
if (n == NGX_REGEX_NO_MATCHED) {
continue;
}
if (n >= 0) {
hc = c->data;
hc->ssl_servername_regex = sn[i].regex;
*cscfp = sn[i].server;
return NGX_OK;
}
ngx_log_error(NGX_LOG_ALERT, c->log, 0,
ngx_regex_exec_n " failed: %i "
"on \"%V\" using \"%V\"",
n, host, &sn[i].regex->name);
return NGX_ERROR;
}
return NGX_DECLINED;
}
#endif /* NGX_HTTP_SSL && defined SSL_CTRL_SET_TLSEXT_HOSTNAME */
for (i = 0; i < virtual_names->nregex; i++) {
n = ngx_http_regex_exec(r, sn[i].regex, host);
if (n == NGX_DECLINED) {
continue;
}
if (n == NGX_OK) {
*cscfp = sn[i].server;
return NGX_OK;
}
return NGX_ERROR;
}
}
#endif /* NGX_PCRE */
return NGX_DECLINED;
}
static void
ngx_http_request_handler(ngx_event_t *ev)
{
ngx_connection_t *c;
ngx_http_request_t *r;
c = ev->data;
r = c->data;
ngx_http_set_log_request(c->log, r);
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
"http run request: \"%V?%V\"", &r->uri, &r->args);
if (c->close) {
r->main->count++;
ngx_http_terminate_request(r, 0);
ngx_http_run_posted_requests(c);
return;
}
if (ev->delayed && ev->timedout) {
ev->delayed = 0;
ev->timedout = 0;
}
if (ev->write) {
r->write_event_handler(r);
} else {
r->read_event_handler(r);
}
ngx_http_run_posted_requests(c);
}
void
ngx_http_run_posted_requests(ngx_connection_t *c)
{
ngx_http_request_t *r;
ngx_http_posted_request_t *pr;
for ( ;; ) {
if (c->destroyed) {
return;
}
r = c->data;
pr = r->main->posted_requests;
if (pr == NULL) {
return;
}
r->main->posted_requests = pr->next;
r = pr->request;
ngx_http_set_log_request(c->log, r);
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
"http posted request: \"%V?%V\"", &r->uri, &r->args);
r->write_event_handler(r);
}
}
ngx_int_t
ngx_http_post_request(ngx_http_request_t *r, ngx_http_posted_request_t *pr)
{
ngx_http_posted_request_t **p;
if (pr == NULL) {
pr = ngx_palloc(r->pool, sizeof(ngx_http_posted_request_t));
if (pr == NULL) {
return NGX_ERROR;
}
}
pr->request = r;
pr->next = NULL;
for (p = &r->main->posted_requests; *p; p = &(*p)->next) { /* void */ }
*p = pr;
return NGX_OK;
}
void
ngx_http_finalize_request(ngx_http_request_t *r, ngx_int_t rc)
{
ngx_connection_t *c;
ngx_http_request_t *pr;
ngx_http_core_loc_conf_t *clcf;
c = r->connection;
ngx_log_debug5(NGX_LOG_DEBUG_HTTP, c->log, 0,
"http finalize request: %i, \"%V?%V\" a:%d, c:%d",
rc, &r->uri, &r->args, r == c->data, r->main->count);
if (rc == NGX_DONE) {
ngx_http_finalize_connection(r);
return;
}
if (rc == NGX_OK && r->filter_finalize) {
c->error = 1;
}
if (rc == NGX_DECLINED) {
r->content_handler = NULL;
r->write_event_handler = ngx_http_core_run_phases;
ngx_http_core_run_phases(r);
return;
}
if (r != r->main && r->post_subrequest) {
rc = r->post_subrequest->handler(r, r->post_subrequest->data, rc);
}
if (rc == NGX_ERROR
|| rc == NGX_HTTP_REQUEST_TIME_OUT
|| rc == NGX_HTTP_CLIENT_CLOSED_REQUEST
|| c->error)
{
if (ngx_http_post_action(r) == NGX_OK) {
return;
}
ngx_http_terminate_request(r, rc);
return;
}
if (rc >= NGX_HTTP_SPECIAL_RESPONSE
|| rc == NGX_HTTP_CREATED
|| rc == NGX_HTTP_NO_CONTENT)
{
if (rc == NGX_HTTP_CLOSE) {
ngx_http_terminate_request(r, rc);
return;
}
if (r == r->main) {
if (c->read->timer_set) {
ngx_del_timer(c->read);
}
if (c->write->timer_set) {
ngx_del_timer(c->write);
}
}
c->read->handler = ngx_http_request_handler;
c->write->handler = ngx_http_request_handler;
ngx_http_finalize_request(r, ngx_http_special_response_handler(r, rc));
return;
}
if (r != r->main) {
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
if (r->background) {
if (!r->logged) {
if (clcf->log_subrequest) {
ngx_http_log_request(r);
}
r->logged = 1;
} else {
ngx_log_error(NGX_LOG_ALERT, c->log, 0,
"subrequest: \"%V?%V\" logged again",
&r->uri, &r->args);
}
r->done = 1;
ngx_http_finalize_connection(r);
return;
}
if (r->buffered || r->postponed) {
if (ngx_http_set_write_handler(r) != NGX_OK) {
ngx_http_terminate_request(r, 0);
}
return;
}
pr = r->parent;
if (r == c->data) {
r->main->count--;
if (!r->logged) {
if (clcf->log_subrequest) {
ngx_http_log_request(r);
}
r->logged = 1;
} else {
ngx_log_error(NGX_LOG_ALERT, c->log, 0,
"subrequest: \"%V?%V\" logged again",
&r->uri, &r->args);
}
r->done = 1;
if (pr->postponed && pr->postponed->request == r) {
pr->postponed = pr->postponed->next;
}
c->data = pr;
} else {
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
"http finalize non-active request: \"%V?%V\"",
&r->uri, &r->args);
r->write_event_handler = ngx_http_request_finalizer;
if (r->waited) {
r->done = 1;
}
}
if (ngx_http_post_request(pr, NULL) != NGX_OK) {
r->main->count++;
ngx_http_terminate_request(r, 0);
return;
}
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
"http wake parent request: \"%V?%V\"",
&pr->uri, &pr->args);
return;
}
if (r->buffered || c->buffered || r->postponed) {
if (ngx_http_set_write_handler(r) != NGX_OK) {
ngx_http_terminate_request(r, 0);
}
return;
}
if (r != c->data) {
ngx_log_error(NGX_LOG_ALERT, c->log, 0,
"http finalize non-active request: \"%V?%V\"",
&r->uri, &r->args);
return;
}
r->done = 1;
r->read_event_handler = ngx_http_block_reading;
r->write_event_handler = ngx_http_request_empty_handler;
if (!r->post_action) {
r->request_complete = 1;
}
if (ngx_http_post_action(r) == NGX_OK) {
return;
}
if (c->read->timer_set) {
ngx_del_timer(c->read);
}
if (c->write->timer_set) {
c->write->delayed = 0;
ngx_del_timer(c->write);
}
if (c->read->eof) {
ngx_http_close_request(r, 0);
return;
}
ngx_http_finalize_connection(r);
}
static void
ngx_http_terminate_request(ngx_http_request_t *r, ngx_int_t rc)
{
ngx_http_cleanup_t *cln;
ngx_http_request_t *mr;
ngx_http_ephemeral_t *e;
mr = r->main;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http terminate request count:%d", mr->count);
if (rc > 0 && (mr->headers_out.status == 0 || mr->connection->sent == 0)) {
mr->headers_out.status = rc;
}
cln = mr->cleanup;
mr->cleanup = NULL;
while (cln) {
if (cln->handler) {
cln->handler(cln->data);
}
cln = cln->next;
}
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http terminate cleanup count:%d blk:%d",
mr->count, mr->blocked);
if (mr->write_event_handler) {
if (mr->blocked) {
r->connection->error = 1;
r->write_event_handler = ngx_http_request_finalizer;
return;
}
e = ngx_http_ephemeral(mr);
mr->posted_requests = NULL;
mr->write_event_handler = ngx_http_terminate_handler;
(void) ngx_http_post_request(mr, &e->terminal_posted_request);
return;
}
ngx_http_close_request(mr, rc);
}
static void
ngx_http_terminate_handler(ngx_http_request_t *r)
{
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http terminate handler count:%d", r->count);
r->count = 1;
ngx_http_close_request(r, 0);
}
static void
ngx_http_finalize_connection(ngx_http_request_t *r)
{
ngx_http_core_loc_conf_t *clcf;
#if (NGX_HTTP_V2)
if (r->stream) {
ngx_http_close_request(r, 0);
return;
}
#endif
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
if (r->main->count != 1) {
if (r->discard_body) {
r->read_event_handler = ngx_http_discarded_request_body_handler;
ngx_add_timer(r->connection->read, clcf->lingering_timeout);
if (r->lingering_time == 0) {
r->lingering_time = ngx_time()
+ (time_t) (clcf->lingering_time / 1000);
}
}
ngx_http_close_request(r, 0);
return;
}
r = r->main;
if (r->reading_body) {
r->keepalive = 0;
r->lingering_close = 1;
}
if (!ngx_terminate
&& !ngx_exiting
&& r->keepalive
&& clcf->keepalive_timeout > 0)
{
ngx_http_set_keepalive(r);
return;
}
if (clcf->lingering_close == NGX_HTTP_LINGERING_ALWAYS
|| (clcf->lingering_close == NGX_HTTP_LINGERING_ON
&& (r->lingering_close
|| r->header_in->pos < r->header_in->last
|| r->connection->read->ready)))
{
ngx_http_set_lingering_close(r);
return;
}
ngx_http_close_request(r, 0);
}
static ngx_int_t
ngx_http_set_write_handler(ngx_http_request_t *r)
{
ngx_event_t *wev;
ngx_http_core_loc_conf_t *clcf;
r->http_state = NGX_HTTP_WRITING_REQUEST_STATE;
r->read_event_handler = r->discard_body ?
ngx_http_discarded_request_body_handler:
ngx_http_test_reading;
r->write_event_handler = ngx_http_writer;
wev = r->connection->write;
if (wev->ready && wev->delayed) {
return NGX_OK;
}
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
if (!wev->delayed) {
ngx_add_timer(wev, clcf->send_timeout);
}
if (ngx_handle_write_event(wev, clcf->send_lowat) != NGX_OK) {
ngx_http_close_request(r, 0);
return NGX_ERROR;
}
return NGX_OK;
}
static void
ngx_http_writer(ngx_http_request_t *r)
{
ngx_int_t rc;
ngx_event_t *wev;
ngx_connection_t *c;
ngx_http_core_loc_conf_t *clcf;
c = r->connection;
wev = c->write;
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, wev->log, 0,
"http writer handler: \"%V?%V\"", &r->uri, &r->args);
clcf = ngx_http_get_module_loc_conf(r->main, ngx_http_core_module);
if (wev->timedout) {
ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT,
"client timed out");
c->timedout = 1;
ngx_http_finalize_request(r, NGX_HTTP_REQUEST_TIME_OUT);
return;
}
if (wev->delayed || r->aio) {
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, wev->log, 0,
"http writer delayed");
if (!wev->delayed) {
ngx_add_timer(wev, clcf->send_timeout);
}
if (ngx_handle_write_event(wev, clcf->send_lowat) != NGX_OK) {
ngx_http_close_request(r, 0);
}
return;
}
rc = ngx_http_output_filter(r, NULL);
ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0,
"http writer output filter: %i, \"%V?%V\"",
rc, &r->uri, &r->args);
if (rc == NGX_ERROR) {
ngx_http_finalize_request(r, rc);
return;
}
if (r->buffered || r->postponed || (r == r->main && c->buffered)) {
if (!wev->delayed) {
ngx_add_timer(wev, clcf->send_timeout);
}
if (ngx_handle_write_event(wev, clcf->send_lowat) != NGX_OK) {
ngx_http_close_request(r, 0);
}
return;
}
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, wev->log, 0,
"http writer done: \"%V?%V\"", &r->uri, &r->args);
r->write_event_handler = ngx_http_request_empty_handler;
ngx_http_finalize_request(r, rc);
}
static void
ngx_http_request_finalizer(ngx_http_request_t *r)
{
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http finalizer done: \"%V?%V\"", &r->uri, &r->args);
ngx_http_finalize_request(r, 0);
}
void
ngx_http_block_reading(ngx_http_request_t *r)
{
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http reading blocked");
/* aio does not call this handler */
if ((ngx_event_flags & NGX_USE_LEVEL_EVENT)
&& r->connection->read->active)
{
if (ngx_del_event(r->connection->read, NGX_READ_EVENT, 0) != NGX_OK) {
ngx_http_close_request(r, 0);
}
}
}
void
ngx_http_test_reading(ngx_http_request_t *r)
{
int n;
char buf[1];
ngx_err_t err;
ngx_event_t *rev;
ngx_connection_t *c;
c = r->connection;
rev = c->read;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http test reading");
#if (NGX_HTTP_V2)
if (r->stream) {
if (c->error) {
err = 0;
goto closed;
}
return;
}
#endif
#if (NGX_HAVE_KQUEUE)
if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {
if (!rev->pending_eof) {
return;
}
rev->eof = 1;
c->error = 1;
err = rev->kq_errno;
goto closed;
}
#endif
#if (NGX_HAVE_EPOLLRDHUP)
if ((ngx_event_flags & NGX_USE_EPOLL_EVENT) && ngx_use_epoll_rdhup) {
socklen_t len;
if (!rev->pending_eof) {
return;
}
rev->eof = 1;
c->error = 1;
err = 0;
len = sizeof(ngx_err_t);
/*
* BSDs and Linux return 0 and set a pending error in err
* Solaris returns -1 and sets errno
*/
if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR, (void *) &err, &len)
== -1)
{
err = ngx_socket_errno;
}
goto closed;
}
#endif
n = recv(c->fd, buf, 1, MSG_PEEK);
if (n == 0) {
rev->eof = 1;
c->error = 1;
err = 0;
goto closed;
} else if (n == -1) {
err = ngx_socket_errno;
if (err != NGX_EAGAIN) {
rev->eof = 1;
c->error = 1;
goto closed;
}
}
/* aio does not call this handler */
if ((ngx_event_flags & NGX_USE_LEVEL_EVENT) && rev->active) {
if (ngx_del_event(rev, NGX_READ_EVENT, 0) != NGX_OK) {
ngx_http_close_request(r, 0);
}
}
return;
closed:
if (err) {
rev->error = 1;
}
ngx_log_error(NGX_LOG_INFO, c->log, err,
"client prematurely closed connection");
ngx_http_finalize_request(r, NGX_HTTP_CLIENT_CLOSED_REQUEST);
}
static void
ngx_http_set_keepalive(ngx_http_request_t *r)
{
int tcp_nodelay;
ngx_buf_t *b, *f;
ngx_chain_t *cl, *ln;
ngx_event_t *rev, *wev;
ngx_connection_t *c;
ngx_http_connection_t *hc;
ngx_http_core_loc_conf_t *clcf;
c = r->connection;
rev = c->read;
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "set http keepalive handler");
if (r->discard_body) {
r->write_event_handler = ngx_http_request_empty_handler;
r->lingering_time = ngx_time() + (time_t) (clcf->lingering_time / 1000);
ngx_add_timer(rev, clcf->lingering_timeout);
return;
}
c->log->action = "closing request";
hc = r->http_connection;
b = r->header_in;
if (b->pos < b->last) {
/* the pipelined request */
if (b != c->buffer) {
/*
* If the large header buffers were allocated while the previous
* request processing then we do not use c->buffer for
* the pipelined request (see ngx_http_create_request()).
*
* Now we would move the large header buffers to the free list.
*/
for (cl = hc->busy; cl; /* void */) {
ln = cl;
cl = cl->next;
if (ln->buf == b) {
ngx_free_chain(c->pool, ln);
continue;
}
f = ln->buf;
f->pos = f->start;
f->last = f->start;
ln->next = hc->free;
hc->free = ln;
}
cl = ngx_alloc_chain_link(c->pool);
if (cl == NULL) {
ngx_http_close_request(r, 0);
return;
}
cl->buf = b;
cl->next = NULL;
hc->busy = cl;
hc->nbusy = 1;
}
}
/* guard against recursive call from ngx_http_finalize_connection() */
r->keepalive = 0;
ngx_http_free_request(r, 0);
c->data = hc;
if (ngx_handle_read_event(rev, 0) != NGX_OK) {
ngx_http_close_connection(c);
return;
}
wev = c->write;
wev->handler = ngx_http_empty_handler;
if (b->pos < b->last) {
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "pipelined request");
c->log->action = "reading client pipelined request line";
r = ngx_http_create_request(c);
if (r == NULL) {
ngx_http_close_connection(c);
return;
}
r->pipeline = 1;
c->data = r;
c->sent = 0;
c->destroyed = 0;
if (rev->timer_set) {
ngx_del_timer(rev);
}
rev->handler = ngx_http_process_request_line;
ngx_post_event(rev, &ngx_posted_events);
return;
}
/*
* To keep a memory footprint as small as possible for an idle keepalive
* connection we try to free c->buffer's memory if it was allocated outside
* the c->pool. The large header buffers are always allocated outside the
* c->pool and are freed too.
*/
b = c->buffer;
if (ngx_pfree(c->pool, b->start) == NGX_OK) {
/*
* the special note for ngx_http_keepalive_handler() that
* c->buffer's memory was freed
*/
b->pos = NULL;
} else {
b->pos = b->start;
b->last = b->start;
}
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "hc free: %p",
hc->free);
if (hc->free) {
for (cl = hc->free; cl; /* void */) {
ln = cl;
cl = cl->next;
ngx_pfree(c->pool, ln->buf->start);
ngx_free_chain(c->pool, ln);
}
hc->free = NULL;
}
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "hc busy: %p %i",
hc->busy, hc->nbusy);
if (hc->busy) {
for (cl = hc->busy; cl; /* void */) {
ln = cl;
cl = cl->next;
ngx_pfree(c->pool, ln->buf->start);
ngx_free_chain(c->pool, ln);
}
hc->busy = NULL;
hc->nbusy = 0;
}
#if (NGX_HTTP_SSL)
if (c->ssl) {
ngx_ssl_free_buffer(c);
}
#endif
rev->handler = ngx_http_keepalive_handler;
if (wev->active && (ngx_event_flags & NGX_USE_LEVEL_EVENT)) {
if (ngx_del_event(wev, NGX_WRITE_EVENT, 0) != NGX_OK) {
ngx_http_close_connection(c);
return;
}
}
c->log->action = "keepalive";
if (c->tcp_nopush == NGX_TCP_NOPUSH_SET) {
if (ngx_tcp_push(c->fd) == -1) {
ngx_connection_error(c, ngx_socket_errno, ngx_tcp_push_n " failed");
ngx_http_close_connection(c);
return;
}
c->tcp_nopush = NGX_TCP_NOPUSH_UNSET;
tcp_nodelay = ngx_tcp_nodelay_and_tcp_nopush ? 1 : 0;
} else {
tcp_nodelay = 1;
}
if (tcp_nodelay && clcf->tcp_nodelay && ngx_tcp_nodelay(c) != NGX_OK) {
ngx_http_close_connection(c);
return;
}
#if 0
/* if ngx_http_request_t was freed then we need some other place */
r->http_state = NGX_HTTP_KEEPALIVE_STATE;
#endif
c->idle = 1;
ngx_reusable_connection(c, 1);
ngx_add_timer(rev, clcf->keepalive_timeout);
if (rev->ready) {
ngx_post_event(rev, &ngx_posted_events);
}
}
static void
ngx_http_keepalive_handler(ngx_event_t *rev)
{
size_t size;
ssize_t n;
ngx_buf_t *b;
ngx_connection_t *c;
c = rev->data;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http keepalive handler");
if (rev->timedout || c->close) {
ngx_http_close_connection(c);
return;
}
#if (NGX_HAVE_KQUEUE)
if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {
if (rev->pending_eof) {
c->log->handler = NULL;
ngx_log_error(NGX_LOG_INFO, c->log, rev->kq_errno,
"kevent() reported that client %V closed "
"keepalive connection", &c->addr_text);
#if (NGX_HTTP_SSL)
if (c->ssl) {
c->ssl->no_send_shutdown = 1;
}
#endif
ngx_http_close_connection(c);
return;
}
}
#endif
b = c->buffer;
size = b->end - b->start;
if (b->pos == NULL) {
/*
* The c->buffer's memory was freed by ngx_http_set_keepalive().
* However, the c->buffer->start and c->buffer->end were not changed
* to keep the buffer size.
*/
b->pos = ngx_palloc(c->pool, size);
if (b->pos == NULL) {
ngx_http_close_connection(c);
return;
}
b->start = b->pos;
b->last = b->pos;
b->end = b->pos + size;
}
/*
* MSIE closes a keepalive connection with RST flag
* so we ignore ECONNRESET here.
*/
c->log_error = NGX_ERROR_IGNORE_ECONNRESET;
ngx_set_socket_errno(0);
n = c->recv(c, b->last, size);
c->log_error = NGX_ERROR_INFO;
if (n == NGX_AGAIN) {
if (ngx_handle_read_event(rev, 0) != NGX_OK) {
ngx_http_close_connection(c);
return;
}
/*
* Like ngx_http_set_keepalive() we are trying to not hold
* c->buffer's memory for a keepalive connection.
*/
if (ngx_pfree(c->pool, b->start) == NGX_OK) {
/*
* the special note that c->buffer's memory was freed
*/
b->pos = NULL;
}
return;
}
if (n == NGX_ERROR) {
ngx_http_close_connection(c);
return;
}
c->log->handler = NULL;
if (n == 0) {
ngx_log_error(NGX_LOG_INFO, c->log, ngx_socket_errno,
"client %V closed keepalive connection", &c->addr_text);
ngx_http_close_connection(c);
return;
}
b->last += n;
c->log->handler = ngx_http_log_error;
c->log->action = "reading client request line";
c->idle = 0;
ngx_reusable_connection(c, 0);
c->data = ngx_http_create_request(c);
if (c->data == NULL) {
ngx_http_close_connection(c);
return;
}
c->sent = 0;
c->destroyed = 0;
ngx_del_timer(rev);
rev->handler = ngx_http_process_request_line;
ngx_http_process_request_line(rev);
}
static void
ngx_http_set_lingering_close(ngx_http_request_t *r)
{
ngx_event_t *rev, *wev;
ngx_connection_t *c;
ngx_http_core_loc_conf_t *clcf;
c = r->connection;
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
rev = c->read;
rev->handler = ngx_http_lingering_close_handler;
r->lingering_time = ngx_time() + (time_t) (clcf->lingering_time / 1000);
ngx_add_timer(rev, clcf->lingering_timeout);
if (ngx_handle_read_event(rev, 0) != NGX_OK) {
ngx_http_close_request(r, 0);
return;
}
wev = c->write;
wev->handler = ngx_http_empty_handler;
if (wev->active && (ngx_event_flags & NGX_USE_LEVEL_EVENT)) {
if (ngx_del_event(wev, NGX_WRITE_EVENT, 0) != NGX_OK) {
ngx_http_close_request(r, 0);
return;
}
}
if (ngx_shutdown_socket(c->fd, NGX_WRITE_SHUTDOWN) == -1) {
ngx_connection_error(c, ngx_socket_errno,
ngx_shutdown_socket_n " failed");
ngx_http_close_request(r, 0);
return;
}
if (rev->ready) {
ngx_http_lingering_close_handler(rev);
}
}
static void
ngx_http_lingering_close_handler(ngx_event_t *rev)
{
ssize_t n;
ngx_msec_t timer;
ngx_connection_t *c;
ngx_http_request_t *r;
ngx_http_core_loc_conf_t *clcf;
u_char buffer[NGX_HTTP_LINGERING_BUFFER_SIZE];
c = rev->data;
r = c->data;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
"http lingering close handler");
if (rev->timedout) {
ngx_http_close_request(r, 0);
return;
}
timer = (ngx_msec_t) r->lingering_time - (ngx_msec_t) ngx_time();
if ((ngx_msec_int_t) timer <= 0) {
ngx_http_close_request(r, 0);
return;
}
do {
n = c->recv(c, buffer, NGX_HTTP_LINGERING_BUFFER_SIZE);
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "lingering read: %z", n);
if (n == NGX_ERROR || n == 0) {
ngx_http_close_request(r, 0);
return;
}
} while (rev->ready);
if (ngx_handle_read_event(rev, 0) != NGX_OK) {
ngx_http_close_request(r, 0);
return;
}
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
timer *= 1000;
if (timer > clcf->lingering_timeout) {
timer = clcf->lingering_timeout;
}
ngx_add_timer(rev, timer);
}
void
ngx_http_empty_handler(ngx_event_t *wev)
{
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, wev->log, 0, "http empty handler");
return;
}
void
ngx_http_request_empty_handler(ngx_http_request_t *r)
{
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http request empty handler");
return;
}
ngx_int_t
ngx_http_send_special(ngx_http_request_t *r, ngx_uint_t flags)
{
ngx_buf_t *b;
ngx_chain_t out;
b = ngx_calloc_buf(r->pool);
if (b == NULL) {
return NGX_ERROR;
}
if (flags & NGX_HTTP_LAST) {
if (r == r->main && !r->post_action) {
b->last_buf = 1;
} else {
b->sync = 1;
b->last_in_chain = 1;
}
}
if (flags & NGX_HTTP_FLUSH) {
b->flush = 1;
}
out.buf = b;
out.next = NULL;
return ngx_http_output_filter(r, &out);
}
static ngx_int_t
ngx_http_post_action(ngx_http_request_t *r)
{
ngx_http_core_loc_conf_t *clcf;
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
if (clcf->post_action.data == NULL) {
return NGX_DECLINED;
}
if (r->post_action && r->uri_changes == 0) {
return NGX_DECLINED;
}
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"post action: \"%V\"", &clcf->post_action);
r->main->count--;
r->http_version = NGX_HTTP_VERSION_9;
r->header_only = 1;
r->post_action = 1;
r->read_event_handler = ngx_http_block_reading;
if (clcf->post_action.data[0] == '/') {
ngx_http_internal_redirect(r, &clcf->post_action, NULL);
} else {
ngx_http_named_location(r, &clcf->post_action);
}
return NGX_OK;
}
static void
ngx_http_close_request(ngx_http_request_t *r, ngx_int_t rc)
{
ngx_connection_t *c;
r = r->main;
c = r->connection;
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
"http request count:%d blk:%d", r->count, r->blocked);
if (r->count == 0) {
ngx_log_error(NGX_LOG_ALERT, c->log, 0, "http request count is zero");
}
r->count--;
if (r->count || r->blocked) {
return;
}
#if (NGX_HTTP_V2)
if (r->stream) {
ngx_http_v2_close_stream(r->stream, rc);
return;
}
#endif
ngx_http_free_request(r, rc);
ngx_http_close_connection(c);
}
void
ngx_http_free_request(ngx_http_request_t *r, ngx_int_t rc)
{
ngx_log_t *log;
ngx_pool_t *pool;
struct linger linger;
ngx_http_cleanup_t *cln;
ngx_http_log_ctx_t *ctx;
ngx_http_core_loc_conf_t *clcf;
log = r->connection->log;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, log, 0, "http close request");
if (r->pool == NULL) {
ngx_log_error(NGX_LOG_ALERT, log, 0, "http request already closed");
return;
}
cln = r->cleanup;
r->cleanup = NULL;
while (cln) {
if (cln->handler) {
cln->handler(cln->data);
}
cln = cln->next;
}
#if (NGX_STAT_STUB)
if (r->stat_reading) {
(void) ngx_atomic_fetch_add(ngx_stat_reading, -1);
}
if (r->stat_writing) {
(void) ngx_atomic_fetch_add(ngx_stat_writing, -1);
}
#endif
if (rc > 0 && (r->headers_out.status == 0 || r->connection->sent == 0)) {
r->headers_out.status = rc;
}
log->action = "logging request";
ngx_http_log_request(r);
log->action = "closing request";
if (r->connection->timedout) {
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
if (clcf->reset_timedout_connection) {
linger.l_onoff = 1;
linger.l_linger = 0;
if (setsockopt(r->connection->fd, SOL_SOCKET, SO_LINGER,
(const void *) &linger, sizeof(struct linger)) == -1)
{
ngx_log_error(NGX_LOG_ALERT, log, ngx_socket_errno,
"setsockopt(SO_LINGER) failed");
}
}
}
/* the various request strings were allocated from r->pool */
ctx = log->data;
ctx->request = NULL;
r->request_line.len = 0;
r->connection->destroyed = 1;
/*
* Setting r->pool to NULL will increase probability to catch double close
* of request since the request object is allocated from its own pool.
*/
pool = r->pool;
r->pool = NULL;
ngx_destroy_pool(pool);
}
static void
ngx_http_log_request(ngx_http_request_t *r)
{
ngx_uint_t i, n;
ngx_http_handler_pt *log_handler;
ngx_http_core_main_conf_t *cmcf;
cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
log_handler = cmcf->phases[NGX_HTTP_LOG_PHASE].handlers.elts;
n = cmcf->phases[NGX_HTTP_LOG_PHASE].handlers.nelts;
for (i = 0; i < n; i++) {
log_handler[i](r);
}
}
void
ngx_http_close_connection(ngx_connection_t *c)
{
ngx_pool_t *pool;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
"close http connection: %d", c->fd);
#if (NGX_HTTP_SSL)
if (c->ssl) {
if (ngx_ssl_shutdown(c) == NGX_AGAIN) {
c->ssl->handler = ngx_http_close_connection;
return;
}
}
#endif
#if (NGX_STAT_STUB)
(void) ngx_atomic_fetch_add(ngx_stat_active, -1);
#endif
c->destroyed = 1;
pool = c->pool;
ngx_close_connection(c);
ngx_destroy_pool(pool);
}
static u_char *
ngx_http_log_error(ngx_log_t *log, u_char *buf, size_t len)
{
u_char *p;
ngx_http_request_t *r;
ngx_http_log_ctx_t *ctx;
if (log->action) {
p = ngx_snprintf(buf, len, " while %s", log->action);
len -= p - buf;
buf = p;
}
ctx = log->data;
p = ngx_snprintf(buf, len, ", client: %V", &ctx->connection->addr_text);
len -= p - buf;
r = ctx->request;
if (r) {
return r->log_handler(r, ctx->current_request, p, len);
} else {
p = ngx_snprintf(p, len, ", server: %V",
&ctx->connection->listening->addr_text);
}
return p;
}
static u_char *
ngx_http_log_error_handler(ngx_http_request_t *r, ngx_http_request_t *sr,
u_char *buf, size_t len)
{
char *uri_separator;
u_char *p;
ngx_http_upstream_t *u;
ngx_http_core_srv_conf_t *cscf;
cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
p = ngx_snprintf(buf, len, ", server: %V", &cscf->server_name);
len -= p - buf;
buf = p;
if (r->request_line.data == NULL && r->request_start) {
for (p = r->request_start; p < r->header_in->last; p++) {
if (*p == CR || *p == LF) {
break;
}
}
r->request_line.len = p - r->request_start;
r->request_line.data = r->request_start;
}
if (r->request_line.len) {
p = ngx_snprintf(buf, len, ", request: \"%V\"", &r->request_line);
len -= p - buf;
buf = p;
}
if (r != sr) {
p = ngx_snprintf(buf, len, ", subrequest: \"%V\"", &sr->uri);
len -= p - buf;
buf = p;
}
u = sr->upstream;
if (u && u->peer.name) {
uri_separator = "";
#if (NGX_HAVE_UNIX_DOMAIN)
if (u->peer.sockaddr && u->peer.sockaddr->sa_family == AF_UNIX) {
uri_separator = ":";
}
#endif
p = ngx_snprintf(buf, len, ", upstream: \"%V%V%s%V\"",
&u->schema, u->peer.name,
uri_separator, &u->uri);
len -= p - buf;
buf = p;
}
if (r->headers_in.host) {
p = ngx_snprintf(buf, len, ", host: \"%V\"",
&r->headers_in.host->value);
len -= p - buf;
buf = p;
}
if (r->headers_in.referer) {
p = ngx_snprintf(buf, len, ", referrer: \"%V\"",
&r->headers_in.referer->value);
buf = p;
}
return buf;
}
nginx-1.14.0/src/http/ngx_http_request.h 000644 001751 001751 00000050241 13265410474 021440 0 ustar 00mdounin mdounin 000000 000000
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#ifndef _NGX_HTTP_REQUEST_H_INCLUDED_
#define _NGX_HTTP_REQUEST_H_INCLUDED_
#define NGX_HTTP_MAX_URI_CHANGES 10
#define NGX_HTTP_MAX_SUBREQUESTS 50
/* must be 2^n */
#define NGX_HTTP_LC_HEADER_LEN 32
#define NGX_HTTP_DISCARD_BUFFER_SIZE 4096
#define NGX_HTTP_LINGERING_BUFFER_SIZE 4096
#define NGX_HTTP_VERSION_9 9
#define NGX_HTTP_VERSION_10 1000
#define NGX_HTTP_VERSION_11 1001
#define NGX_HTTP_VERSION_20 2000
#define NGX_HTTP_UNKNOWN 0x0001
#define NGX_HTTP_GET 0x0002
#define NGX_HTTP_HEAD 0x0004
#define NGX_HTTP_POST 0x0008
#define NGX_HTTP_PUT 0x0010
#define NGX_HTTP_DELETE 0x0020
#define NGX_HTTP_MKCOL 0x0040
#define NGX_HTTP_COPY 0x0080
#define NGX_HTTP_MOVE 0x0100
#define NGX_HTTP_OPTIONS 0x0200
#define NGX_HTTP_PROPFIND 0x0400
#define NGX_HTTP_PROPPATCH 0x0800
#define NGX_HTTP_LOCK 0x1000
#define NGX_HTTP_UNLOCK 0x2000
#define NGX_HTTP_PATCH 0x4000
#define NGX_HTTP_TRACE 0x8000
#define NGX_HTTP_CONNECTION_CLOSE 1
#define NGX_HTTP_CONNECTION_KEEP_ALIVE 2
#define NGX_NONE 1
#define NGX_HTTP_PARSE_HEADER_DONE 1
#define NGX_HTTP_CLIENT_ERROR 10
#define NGX_HTTP_PARSE_INVALID_METHOD 10
#define NGX_HTTP_PARSE_INVALID_REQUEST 11
#define NGX_HTTP_PARSE_INVALID_VERSION 12
#define NGX_HTTP_PARSE_INVALID_09_METHOD 13
#define NGX_HTTP_PARSE_INVALID_HEADER 14
/* unused 1 */
#define NGX_HTTP_SUBREQUEST_IN_MEMORY 2
#define NGX_HTTP_SUBREQUEST_WAITED 4
#define NGX_HTTP_SUBREQUEST_CLONE 8
#define NGX_HTTP_SUBREQUEST_BACKGROUND 16
#define NGX_HTTP_LOG_UNSAFE 1
#define NGX_HTTP_CONTINUE 100
#define NGX_HTTP_SWITCHING_PROTOCOLS 101
#define NGX_HTTP_PROCESSING 102
#define NGX_HTTP_OK 200
#define NGX_HTTP_CREATED 201
#define NGX_HTTP_ACCEPTED 202
#define NGX_HTTP_NO_CONTENT 204
#define NGX_HTTP_PARTIAL_CONTENT 206
#define NGX_HTTP_SPECIAL_RESPONSE 300
#define NGX_HTTP_MOVED_PERMANENTLY 301
#define NGX_HTTP_MOVED_TEMPORARILY 302
#define NGX_HTTP_SEE_OTHER 303
#define NGX_HTTP_NOT_MODIFIED 304
#define NGX_HTTP_TEMPORARY_REDIRECT 307
#define NGX_HTTP_PERMANENT_REDIRECT 308
#define NGX_HTTP_BAD_REQUEST 400
#define NGX_HTTP_UNAUTHORIZED 401
#define NGX_HTTP_FORBIDDEN 403
#define NGX_HTTP_NOT_FOUND 404
#define NGX_HTTP_NOT_ALLOWED 405
#define NGX_HTTP_REQUEST_TIME_OUT 408
#define NGX_HTTP_CONFLICT 409
#define NGX_HTTP_LENGTH_REQUIRED 411
#define NGX_HTTP_PRECONDITION_FAILED 412
#define NGX_HTTP_REQUEST_ENTITY_TOO_LARGE 413
#define NGX_HTTP_REQUEST_URI_TOO_LARGE 414
#define NGX_HTTP_UNSUPPORTED_MEDIA_TYPE 415
#define NGX_HTTP_RANGE_NOT_SATISFIABLE 416
#define NGX_HTTP_MISDIRECTED_REQUEST 421
#define NGX_HTTP_TOO_MANY_REQUESTS 429
/* Our own HTTP codes */
/* The special code to close connection without any response */
#define NGX_HTTP_CLOSE 444
#define NGX_HTTP_NGINX_CODES 494
#define NGX_HTTP_REQUEST_HEADER_TOO_LARGE 494
#define NGX_HTTPS_CERT_ERROR 495
#define NGX_HTTPS_NO_CERT 496
/*
* We use the special code for the plain HTTP requests that are sent to
* HTTPS port to distinguish it from 4XX in an error page redirection
*/
#define NGX_HTTP_TO_HTTPS 497
/* 498 is the canceled code for the requests with invalid host name */
/*
* HTTP does not define the code for the case when a client closed
* the connection while we are processing its request so we introduce
* own code to log such situation when a client has closed the connection
* before we even try to send the HTTP header to it
*/
#define NGX_HTTP_CLIENT_CLOSED_REQUEST 499
#define NGX_HTTP_INTERNAL_SERVER_ERROR 500
#define NGX_HTTP_NOT_IMPLEMENTED 501
#define NGX_HTTP_BAD_GATEWAY 502
#define NGX_HTTP_SERVICE_UNAVAILABLE 503
#define NGX_HTTP_GATEWAY_TIME_OUT 504
#define NGX_HTTP_VERSION_NOT_SUPPORTED 505
#define NGX_HTTP_INSUFFICIENT_STORAGE 507
#define NGX_HTTP_LOWLEVEL_BUFFERED 0xf0
#define NGX_HTTP_WRITE_BUFFERED 0x10
#define NGX_HTTP_GZIP_BUFFERED 0x20
#define NGX_HTTP_SSI_BUFFERED 0x01
#define NGX_HTTP_SUB_BUFFERED 0x02
#define NGX_HTTP_COPY_BUFFERED 0x04
typedef enum {
NGX_HTTP_INITING_REQUEST_STATE = 0,
NGX_HTTP_READING_REQUEST_STATE,
NGX_HTTP_PROCESS_REQUEST_STATE,
NGX_HTTP_CONNECT_UPSTREAM_STATE,
NGX_HTTP_WRITING_UPSTREAM_STATE,
NGX_HTTP_READING_UPSTREAM_STATE,
NGX_HTTP_WRITING_REQUEST_STATE,
NGX_HTTP_LINGERING_CLOSE_STATE,
NGX_HTTP_KEEPALIVE_STATE
} ngx_http_state_e;
typedef struct {
ngx_str_t name;
ngx_uint_t offset;
ngx_http_header_handler_pt handler;
} ngx_http_header_t;
typedef struct {
ngx_str_t name;
ngx_uint_t offset;
} ngx_http_header_out_t;
typedef struct {
ngx_list_t headers;
ngx_table_elt_t *host;
ngx_table_elt_t *connection;
ngx_table_elt_t *if_modified_since;
ngx_table_elt_t *if_unmodified_since;
ngx_table_elt_t *if_match;
ngx_table_elt_t *if_none_match;
ngx_table_elt_t *user_agent;
ngx_table_elt_t *referer;
ngx_table_elt_t *content_length;
ngx_table_elt_t *content_range;
ngx_table_elt_t *content_type;
ngx_table_elt_t *range;
ngx_table_elt_t *if_range;
ngx_table_elt_t *transfer_encoding;
ngx_table_elt_t *te;
ngx_table_elt_t *expect;
ngx_table_elt_t *upgrade;
#if (NGX_HTTP_GZIP || NGX_HTTP_HEADERS)
ngx_table_elt_t *accept_encoding;
ngx_table_elt_t *via;
#endif
ngx_table_elt_t *authorization;
ngx_table_elt_t *keep_alive;
#if (NGX_HTTP_X_FORWARDED_FOR)
ngx_array_t x_forwarded_for;
#endif
#if (NGX_HTTP_REALIP)
ngx_table_elt_t *x_real_ip;
#endif
#if (NGX_HTTP_HEADERS)
ngx_table_elt_t *accept;
ngx_table_elt_t *accept_language;
#endif
#if (NGX_HTTP_DAV)
ngx_table_elt_t *depth;
ngx_table_elt_t *destination;
ngx_table_elt_t *overwrite;
ngx_table_elt_t *date;
#endif
ngx_str_t user;
ngx_str_t passwd;
ngx_array_t cookies;
ngx_str_t server;
off_t content_length_n;
time_t keep_alive_n;
unsigned connection_type:2;
unsigned chunked:1;
unsigned msie:1;
unsigned msie6:1;
unsigned opera:1;
unsigned gecko:1;
unsigned chrome:1;
unsigned safari:1;
unsigned konqueror:1;
} ngx_http_headers_in_t;
typedef struct {
ngx_list_t headers;
ngx_list_t trailers;
ngx_uint_t status;
ngx_str_t status_line;
ngx_table_elt_t *server;
ngx_table_elt_t *date;
ngx_table_elt_t *content_length;
ngx_table_elt_t *content_encoding;
ngx_table_elt_t *location;
ngx_table_elt_t *refresh;
ngx_table_elt_t *last_modified;
ngx_table_elt_t *content_range;
ngx_table_elt_t *accept_ranges;
ngx_table_elt_t *www_authenticate;
ngx_table_elt_t *expires;
ngx_table_elt_t *etag;
ngx_str_t *override_charset;
size_t content_type_len;
ngx_str_t content_type;
ngx_str_t charset;
u_char *content_type_lowcase;
ngx_uint_t content_type_hash;
ngx_array_t cache_control;
ngx_array_t link;
off_t content_length_n;
off_t content_offset;
time_t date_time;
time_t last_modified_time;
} ngx_http_headers_out_t;
typedef void (*ngx_http_client_body_handler_pt)(ngx_http_request_t *r);
typedef struct {
ngx_temp_file_t *temp_file;
ngx_chain_t *bufs;
ngx_buf_t *buf;
off_t rest;
off_t received;
ngx_chain_t *free;
ngx_chain_t *busy;
ngx_http_chunked_t *chunked;
ngx_http_client_body_handler_pt post_handler;
} ngx_http_request_body_t;
typedef struct ngx_http_addr_conf_s ngx_http_addr_conf_t;
typedef struct {
ngx_http_addr_conf_t *addr_conf;
ngx_http_conf_ctx_t *conf_ctx;
#if (NGX_HTTP_SSL || NGX_COMPAT)
ngx_str_t *ssl_servername;
#if (NGX_PCRE)
ngx_http_regex_t *ssl_servername_regex;
#endif
#endif
ngx_chain_t *busy;
ngx_int_t nbusy;
ngx_chain_t *free;
unsigned ssl:1;
unsigned proxy_protocol:1;
} ngx_http_connection_t;
typedef void (*ngx_http_cleanup_pt)(void *data);
typedef struct ngx_http_cleanup_s ngx_http_cleanup_t;
struct ngx_http_cleanup_s {
ngx_http_cleanup_pt handler;
void *data;
ngx_http_cleanup_t *next;
};
typedef ngx_int_t (*ngx_http_post_subrequest_pt)(ngx_http_request_t *r,
void *data, ngx_int_t rc);
typedef struct {
ngx_http_post_subrequest_pt handler;
void *data;
} ngx_http_post_subrequest_t;
typedef struct ngx_http_postponed_request_s ngx_http_postponed_request_t;
struct ngx_http_postponed_request_s {
ngx_http_request_t *request;
ngx_chain_t *out;
ngx_http_postponed_request_t *next;
};
typedef struct ngx_http_posted_request_s ngx_http_posted_request_t;
struct ngx_http_posted_request_s {
ngx_http_request_t *request;
ngx_http_posted_request_t *next;
};
typedef ngx_int_t (*ngx_http_handler_pt)(ngx_http_request_t *r);
typedef void (*ngx_http_event_handler_pt)(ngx_http_request_t *r);
struct ngx_http_request_s {
uint32_t signature; /* "HTTP" */
ngx_connection_t *connection;
void **ctx;
void **main_conf;
void **srv_conf;
void **loc_conf;
ngx_http_event_handler_pt read_event_handler;
ngx_http_event_handler_pt write_event_handler;
#if (NGX_HTTP_CACHE)
ngx_http_cache_t *cache;
#endif
ngx_http_upstream_t *upstream;
ngx_array_t *upstream_states;
/* of ngx_http_upstream_state_t */
ngx_pool_t *pool;
ngx_buf_t *header_in;
ngx_http_headers_in_t headers_in;
ngx_http_headers_out_t headers_out;
ngx_http_request_body_t *request_body;
time_t lingering_time;
time_t start_sec;
ngx_msec_t start_msec;
ngx_uint_t method;
ngx_uint_t http_version;
ngx_str_t request_line;
ngx_str_t uri;
ngx_str_t args;
ngx_str_t exten;
ngx_str_t unparsed_uri;
ngx_str_t method_name;
ngx_str_t http_protocol;
ngx_chain_t *out;
ngx_http_request_t *main;
ngx_http_request_t *parent;
ngx_http_postponed_request_t *postponed;
ngx_http_post_subrequest_t *post_subrequest;
ngx_http_posted_request_t *posted_requests;
ngx_int_t phase_handler;
ngx_http_handler_pt content_handler;
ngx_uint_t access_code;
ngx_http_variable_value_t *variables;
#if (NGX_PCRE)
ngx_uint_t ncaptures;
int *captures;
u_char *captures_data;
#endif
size_t limit_rate;
size_t limit_rate_after;
/* used to learn the Apache compatible response length without a header */
size_t header_size;
off_t request_length;
ngx_uint_t err_status;
ngx_http_connection_t *http_connection;
ngx_http_v2_stream_t *stream;
ngx_http_log_handler_pt log_handler;
ngx_http_cleanup_t *cleanup;
unsigned count:16;
unsigned subrequests:8;
unsigned blocked:8;
unsigned aio:1;
unsigned http_state:4;
/* URI with "/." and on Win32 with "//" */
unsigned complex_uri:1;
/* URI with "%" */
unsigned quoted_uri:1;
/* URI with "+" */
unsigned plus_in_uri:1;
/* URI with " " */
unsigned space_in_uri:1;
unsigned invalid_header:1;
unsigned add_uri_to_alias:1;
unsigned valid_location:1;
unsigned valid_unparsed_uri:1;
unsigned uri_changed:1;
unsigned uri_changes:4;
unsigned request_body_in_single_buf:1;
unsigned request_body_in_file_only:1;
unsigned request_body_in_persistent_file:1;
unsigned request_body_in_clean_file:1;
unsigned request_body_file_group_access:1;
unsigned request_body_file_log_level:3;
unsigned request_body_no_buffering:1;
unsigned subrequest_in_memory:1;
unsigned waited:1;
#if (NGX_HTTP_CACHE)
unsigned cached:1;
#endif
#if (NGX_HTTP_GZIP)
unsigned gzip_tested:1;
unsigned gzip_ok:1;
unsigned gzip_vary:1;
#endif
unsigned proxy:1;
unsigned bypass_cache:1;
unsigned no_cache:1;
/*
* instead of using the request context data in
* ngx_http_limit_conn_module and ngx_http_limit_req_module
* we use the single bits in the request structure
*/
unsigned limit_conn_set:1;
unsigned limit_req_set:1;
#if 0
unsigned cacheable:1;
#endif
unsigned pipeline:1;
unsigned chunked:1;
unsigned header_only:1;
unsigned expect_trailers:1;
unsigned keepalive:1;
unsigned lingering_close:1;
unsigned discard_body:1;
unsigned reading_body:1;
unsigned internal:1;
unsigned error_page:1;
unsigned filter_finalize:1;
unsigned post_action:1;
unsigned request_complete:1;
unsigned request_output:1;
unsigned header_sent:1;
unsigned expect_tested:1;
unsigned root_tested:1;
unsigned done:1;
unsigned logged:1;
unsigned buffered:4;
unsigned main_filter_need_in_memory:1;
unsigned filter_need_in_memory:1;
unsigned filter_need_temporary:1;
unsigned preserve_body:1;
unsigned allow_ranges:1;
unsigned subrequest_ranges:1;
unsigned single_range:1;
unsigned disable_not_modified:1;
unsigned stat_reading:1;
unsigned stat_writing:1;
unsigned stat_processing:1;
unsigned background:1;
unsigned health_check:1;
/* used to parse HTTP headers */
ngx_uint_t state;
ngx_uint_t header_hash;
ngx_uint_t lowcase_index;
u_char lowcase_header[NGX_HTTP_LC_HEADER_LEN];
u_char *header_name_start;
u_char *header_name_end;
u_char *header_start;
u_char *header_end;
/*
* a memory that can be reused after parsing a request line
* via ngx_http_ephemeral_t
*/
u_char *uri_start;
u_char *uri_end;
u_char *uri_ext;
u_char *args_start;
u_char *request_start;
u_char *request_end;
u_char *method_end;
u_char *schema_start;
u_char *schema_end;
u_char *host_start;
u_char *host_end;
u_char *port_start;
u_char *port_end;
unsigned http_minor:16;
unsigned http_major:16;
};
typedef struct {
ngx_http_posted_request_t terminal_posted_request;
} ngx_http_ephemeral_t;
#define ngx_http_ephemeral(r) (void *) (&r->uri_start)
extern ngx_http_header_t ngx_http_headers_in[];
extern ngx_http_header_out_t ngx_http_headers_out[];
#define ngx_http_set_log_request(log, r) \
((ngx_http_log_ctx_t *) log->data)->current_request = r
#endif /* _NGX_HTTP_REQUEST_H_INCLUDED_ */
nginx-1.14.0/src/http/ngx_http_request_body.c 000644 001751 001751 00000071452 13265410474 022457 0 ustar 00mdounin mdounin 000000 000000
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#include
#include
#include
static void ngx_http_read_client_request_body_handler(ngx_http_request_t *r);
static ngx_int_t ngx_http_do_read_client_request_body(ngx_http_request_t *r);
static ngx_int_t ngx_http_write_request_body(ngx_http_request_t *r);
static ngx_int_t ngx_http_read_discarded_request_body(ngx_http_request_t *r);
static ngx_int_t ngx_http_discard_request_body_filter(ngx_http_request_t *r,
ngx_buf_t *b);
static ngx_int_t ngx_http_test_expect(ngx_http_request_t *r);
static ngx_int_t ngx_http_request_body_filter(ngx_http_request_t *r,
ngx_chain_t *in);
static ngx_int_t ngx_http_request_body_length_filter(ngx_http_request_t *r,
ngx_chain_t *in);
static ngx_int_t ngx_http_request_body_chunked_filter(ngx_http_request_t *r,
ngx_chain_t *in);
ngx_int_t
ngx_http_read_client_request_body(ngx_http_request_t *r,
ngx_http_client_body_handler_pt post_handler)
{
size_t preread;
ssize_t size;
ngx_int_t rc;
ngx_buf_t *b;
ngx_chain_t out;
ngx_http_request_body_t *rb;
ngx_http_core_loc_conf_t *clcf;
r->main->count++;
if (r != r->main || r->request_body || r->discard_body) {
r->request_body_no_buffering = 0;
post_handler(r);
return NGX_OK;
}
if (ngx_http_test_expect(r) != NGX_OK) {
rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
goto done;
}
rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t));
if (rb == NULL) {
rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
goto done;
}
/*
* set by ngx_pcalloc():
*
* rb->bufs = NULL;
* rb->buf = NULL;
* rb->free = NULL;
* rb->busy = NULL;
* rb->chunked = NULL;
*/
rb->rest = -1;
rb->post_handler = post_handler;
r->request_body = rb;
if (r->headers_in.content_length_n < 0 && !r->headers_in.chunked) {
r->request_body_no_buffering = 0;
post_handler(r);
return NGX_OK;
}
#if (NGX_HTTP_V2)
if (r->stream) {
rc = ngx_http_v2_read_request_body(r);
goto done;
}
#endif
preread = r->header_in->last - r->header_in->pos;
if (preread) {
/* there is the pre-read part of the request body */
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http client request body preread %uz", preread);
out.buf = r->header_in;
out.next = NULL;
rc = ngx_http_request_body_filter(r, &out);
if (rc != NGX_OK) {
goto done;
}
r->request_length += preread - (r->header_in->last - r->header_in->pos);
if (!r->headers_in.chunked
&& rb->rest > 0
&& rb->rest <= (off_t) (r->header_in->end - r->header_in->last))
{
/* the whole request body may be placed in r->header_in */
b = ngx_calloc_buf(r->pool);
if (b == NULL) {
rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
goto done;
}
b->temporary = 1;
b->start = r->header_in->pos;
b->pos = r->header_in->pos;
b->last = r->header_in->last;
b->end = r->header_in->end;
rb->buf = b;
r->read_event_handler = ngx_http_read_client_request_body_handler;
r->write_event_handler = ngx_http_request_empty_handler;
rc = ngx_http_do_read_client_request_body(r);
goto done;
}
} else {
/* set rb->rest */
if (ngx_http_request_body_filter(r, NULL) != NGX_OK) {
rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
goto done;
}
}
if (rb->rest == 0) {
/* the whole request body was pre-read */
r->request_body_no_buffering = 0;
post_handler(r);
return NGX_OK;
}
if (rb->rest < 0) {
ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
"negative request body rest");
rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
goto done;
}
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
size = clcf->client_body_buffer_size;
size += size >> 2;
/* TODO: honor r->request_body_in_single_buf */
if (!r->headers_in.chunked && rb->rest < size) {
size = (ssize_t) rb->rest;
if (r->request_body_in_single_buf) {
size += preread;
}
} else {
size = clcf->client_body_buffer_size;
}
rb->buf = ngx_create_temp_buf(r->pool, size);
if (rb->buf == NULL) {
rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
goto done;
}
r->read_event_handler = ngx_http_read_client_request_body_handler;
r->write_event_handler = ngx_http_request_empty_handler;
rc = ngx_http_do_read_client_request_body(r);
done:
if (r->request_body_no_buffering
&& (rc == NGX_OK || rc == NGX_AGAIN))
{
if (rc == NGX_OK) {
r->request_body_no_buffering = 0;
} else {
/* rc == NGX_AGAIN */
r->reading_body = 1;
}
r->read_event_handler = ngx_http_block_reading;
post_handler(r);
}
if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
r->main->count--;
}
return rc;
}
ngx_int_t
ngx_http_read_unbuffered_request_body(ngx_http_request_t *r)
{
ngx_int_t rc;
#if (NGX_HTTP_V2)
if (r->stream) {
rc = ngx_http_v2_read_unbuffered_request_body(r);
if (rc == NGX_OK) {
r->reading_body = 0;
}
return rc;
}
#endif
if (r->connection->read->timedout) {
r->connection->timedout = 1;
return NGX_HTTP_REQUEST_TIME_OUT;
}
rc = ngx_http_do_read_client_request_body(r);
if (rc == NGX_OK) {
r->reading_body = 0;
}
return rc;
}
static void
ngx_http_read_client_request_body_handler(ngx_http_request_t *r)
{
ngx_int_t rc;
if (r->connection->read->timedout) {
r->connection->timedout = 1;
ngx_http_finalize_request(r, NGX_HTTP_REQUEST_TIME_OUT);
return;
}
rc = ngx_http_do_read_client_request_body(r);
if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
ngx_http_finalize_request(r, rc);
}
}
static ngx_int_t
ngx_http_do_read_client_request_body(ngx_http_request_t *r)
{
off_t rest;
size_t size;
ssize_t n;
ngx_int_t rc;
ngx_chain_t out;
ngx_connection_t *c;
ngx_http_request_body_t *rb;
ngx_http_core_loc_conf_t *clcf;
c = r->connection;
rb = r->request_body;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
"http read client request body");
for ( ;; ) {
for ( ;; ) {
if (rb->buf->last == rb->buf->end) {
if (rb->buf->pos != rb->buf->last) {
/* pass buffer to request body filter chain */
out.buf = rb->buf;
out.next = NULL;
rc = ngx_http_request_body_filter(r, &out);
if (rc != NGX_OK) {
return rc;
}
} else {
/* update chains */
rc = ngx_http_request_body_filter(r, NULL);
if (rc != NGX_OK) {
return rc;
}
}
if (rb->busy != NULL) {
if (r->request_body_no_buffering) {
if (c->read->timer_set) {
ngx_del_timer(c->read);
}
if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
return NGX_AGAIN;
}
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
rb->buf->pos = rb->buf->start;
rb->buf->last = rb->buf->start;
}
size = rb->buf->end - rb->buf->last;
rest = rb->rest - (rb->buf->last - rb->buf->pos);
if ((off_t) size > rest) {
size = (size_t) rest;
}
n = c->recv(c, rb->buf->last, size);
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
"http client request body recv %z", n);
if (n == NGX_AGAIN) {
break;
}
if (n == 0) {
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"client prematurely closed connection");
}
if (n == 0 || n == NGX_ERROR) {
c->error = 1;
return NGX_HTTP_BAD_REQUEST;
}
rb->buf->last += n;
r->request_length += n;
if (n == rest) {
/* pass buffer to request body filter chain */
out.buf = rb->buf;
out.next = NULL;
rc = ngx_http_request_body_filter(r, &out);
if (rc != NGX_OK) {
return rc;
}
}
if (rb->rest == 0) {
break;
}
if (rb->buf->last < rb->buf->end) {
break;
}
}
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
"http client request body rest %O", rb->rest);
if (rb->rest == 0) {
break;
}
if (!c->read->ready) {
if (r->request_body_no_buffering
&& rb->buf->pos != rb->buf->last)
{
/* pass buffer to request body filter chain */
out.buf = rb->buf;
out.next = NULL;
rc = ngx_http_request_body_filter(r, &out);
if (rc != NGX_OK) {
return rc;
}
}
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
ngx_add_timer(c->read, clcf->client_body_timeout);
if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
return NGX_AGAIN;
}
}
if (c->read->timer_set) {
ngx_del_timer(c->read);
}
if (!r->request_body_no_buffering) {
r->read_event_handler = ngx_http_block_reading;
rb->post_handler(r);
}
return NGX_OK;
}
static ngx_int_t
ngx_http_write_request_body(ngx_http_request_t *r)
{
ssize_t n;
ngx_chain_t *cl, *ln;
ngx_temp_file_t *tf;
ngx_http_request_body_t *rb;
ngx_http_core_loc_conf_t *clcf;
rb = r->request_body;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http write client request body, bufs %p", rb->bufs);
if (rb->temp_file == NULL) {
tf = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t));
if (tf == NULL) {
return NGX_ERROR;
}
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
tf->file.fd = NGX_INVALID_FILE;
tf->file.log = r->connection->log;
tf->path = clcf->client_body_temp_path;
tf->pool = r->pool;
tf->warn = "a client request body is buffered to a temporary file";
tf->log_level = r->request_body_file_log_level;
tf->persistent = r->request_body_in_persistent_file;
tf->clean = r->request_body_in_clean_file;
if (r->request_body_file_group_access) {
tf->access = 0660;
}
rb->temp_file = tf;
if (rb->bufs == NULL) {
/* empty body with r->request_body_in_file_only */
if (ngx_create_temp_file(&tf->file, tf->path, tf->pool,
tf->persistent, tf->clean, tf->access)
!= NGX_OK)
{
return NGX_ERROR;
}
return NGX_OK;
}
}
if (rb->bufs == NULL) {
return NGX_OK;
}
n = ngx_write_chain_to_temp_file(rb->temp_file, rb->bufs);
/* TODO: n == 0 or not complete and level event */
if (n == NGX_ERROR) {
return NGX_ERROR;
}
rb->temp_file->offset += n;
/* mark all buffers as written */
for (cl = rb->bufs; cl; /* void */) {
cl->buf->pos = cl->buf->last;
ln = cl;
cl = cl->next;
ngx_free_chain(r->pool, ln);
}
rb->bufs = NULL;
return NGX_OK;
}
ngx_int_t
ngx_http_discard_request_body(ngx_http_request_t *r)
{
ssize_t size;
ngx_int_t rc;
ngx_event_t *rev;
if (r != r->main || r->discard_body || r->request_body) {
return NGX_OK;
}
#if (NGX_HTTP_V2)
if (r->stream) {
r->stream->skip_data = 1;
return NGX_OK;
}
#endif
if (ngx_http_test_expect(r) != NGX_OK) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
rev = r->connection->read;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "http set discard body");
if (rev->timer_set) {
ngx_del_timer(rev);
}
if (r->headers_in.content_length_n <= 0 && !r->headers_in.chunked) {
return NGX_OK;
}
size = r->header_in->last - r->header_in->pos;
if (size || r->headers_in.chunked) {
rc = ngx_http_discard_request_body_filter(r, r->header_in);
if (rc != NGX_OK) {
return rc;
}
if (r->headers_in.content_length_n == 0) {
return NGX_OK;
}
}
rc = ngx_http_read_discarded_request_body(r);
if (rc == NGX_OK) {
r->lingering_close = 0;
return NGX_OK;
}
if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
return rc;
}
/* rc == NGX_AGAIN */
r->read_event_handler = ngx_http_discarded_request_body_handler;
if (ngx_handle_read_event(rev, 0) != NGX_OK) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
r->count++;
r->discard_body = 1;
return NGX_OK;
}
void
ngx_http_discarded_request_body_handler(ngx_http_request_t *r)
{
ngx_int_t rc;
ngx_msec_t timer;
ngx_event_t *rev;
ngx_connection_t *c;
ngx_http_core_loc_conf_t *clcf;
c = r->connection;
rev = c->read;
if (rev->timedout) {
c->timedout = 1;
c->error = 1;
ngx_http_finalize_request(r, NGX_ERROR);
return;
}
if (r->lingering_time) {
timer = (ngx_msec_t) r->lingering_time - (ngx_msec_t) ngx_time();
if ((ngx_msec_int_t) timer <= 0) {
r->discard_body = 0;
r->lingering_close = 0;
ngx_http_finalize_request(r, NGX_ERROR);
return;
}
} else {
timer = 0;
}
rc = ngx_http_read_discarded_request_body(r);
if (rc == NGX_OK) {
r->discard_body = 0;
r->lingering_close = 0;
ngx_http_finalize_request(r, NGX_DONE);
return;
}
if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
c->error = 1;
ngx_http_finalize_request(r, NGX_ERROR);
return;
}
/* rc == NGX_AGAIN */
if (ngx_handle_read_event(rev, 0) != NGX_OK) {
c->error = 1;
ngx_http_finalize_request(r, NGX_ERROR);
return;
}
if (timer) {
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
timer *= 1000;
if (timer > clcf->lingering_timeout) {
timer = clcf->lingering_timeout;
}
ngx_add_timer(rev, timer);
}
}
static ngx_int_t
ngx_http_read_discarded_request_body(ngx_http_request_t *r)
{
size_t size;
ssize_t n;
ngx_int_t rc;
ngx_buf_t b;
u_char buffer[NGX_HTTP_DISCARD_BUFFER_SIZE];
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http read discarded body");
ngx_memzero(&b, sizeof(ngx_buf_t));
b.temporary = 1;
for ( ;; ) {
if (r->headers_in.content_length_n == 0) {
r->read_event_handler = ngx_http_block_reading;
return NGX_OK;
}
if (!r->connection->read->ready) {
return NGX_AGAIN;
}
size = (size_t) ngx_min(r->headers_in.content_length_n,
NGX_HTTP_DISCARD_BUFFER_SIZE);
n = r->connection->recv(r->connection, buffer, size);
if (n == NGX_ERROR) {
r->connection->error = 1;
return NGX_OK;
}
if (n == NGX_AGAIN) {
return NGX_AGAIN;
}
if (n == 0) {
return NGX_OK;
}
b.pos = buffer;
b.last = buffer + n;
rc = ngx_http_discard_request_body_filter(r, &b);
if (rc != NGX_OK) {
return rc;
}
}
}
static ngx_int_t
ngx_http_discard_request_body_filter(ngx_http_request_t *r, ngx_buf_t *b)
{
size_t size;
ngx_int_t rc;
ngx_http_request_body_t *rb;
if (r->headers_in.chunked) {
rb = r->request_body;
if (rb == NULL) {
rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t));
if (rb == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
rb->chunked = ngx_pcalloc(r->pool, sizeof(ngx_http_chunked_t));
if (rb->chunked == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
r->request_body = rb;
}
for ( ;; ) {
rc = ngx_http_parse_chunked(r, b, rb->chunked);
if (rc == NGX_OK) {
/* a chunk has been parsed successfully */
size = b->last - b->pos;
if ((off_t) size > rb->chunked->size) {
b->pos += (size_t) rb->chunked->size;
rb->chunked->size = 0;
} else {
rb->chunked->size -= size;
b->pos = b->last;
}
continue;
}
if (rc == NGX_DONE) {
/* a whole response has been parsed successfully */
r->headers_in.content_length_n = 0;
break;
}
if (rc == NGX_AGAIN) {
/* set amount of data we want to see next time */
r->headers_in.content_length_n = rb->chunked->length;
break;
}
/* invalid */
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"client sent invalid chunked body");
return NGX_HTTP_BAD_REQUEST;
}
} else {
size = b->last - b->pos;
if ((off_t) size > r->headers_in.content_length_n) {
b->pos += (size_t) r->headers_in.content_length_n;
r->headers_in.content_length_n = 0;
} else {
b->pos = b->last;
r->headers_in.content_length_n -= size;
}
}
return NGX_OK;
}
static ngx_int_t
ngx_http_test_expect(ngx_http_request_t *r)
{
ngx_int_t n;
ngx_str_t *expect;
if (r->expect_tested
|| r->headers_in.expect == NULL
|| r->http_version < NGX_HTTP_VERSION_11
#if (NGX_HTTP_V2)
|| r->stream != NULL
#endif
)
{
return NGX_OK;
}
r->expect_tested = 1;
expect = &r->headers_in.expect->value;
if (expect->len != sizeof("100-continue") - 1
|| ngx_strncasecmp(expect->data, (u_char *) "100-continue",
sizeof("100-continue") - 1)
!= 0)
{
return NGX_OK;
}
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"send 100 Continue");
n = r->connection->send(r->connection,
(u_char *) "HTTP/1.1 100 Continue" CRLF CRLF,
sizeof("HTTP/1.1 100 Continue" CRLF CRLF) - 1);
if (n == sizeof("HTTP/1.1 100 Continue" CRLF CRLF) - 1) {
return NGX_OK;
}
/* we assume that such small packet should be send successfully */
r->connection->error = 1;
return NGX_ERROR;
}
static ngx_int_t
ngx_http_request_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
if (r->headers_in.chunked) {
return ngx_http_request_body_chunked_filter(r, in);
} else {
return ngx_http_request_body_length_filter(r, in);
}
}
static ngx_int_t
ngx_http_request_body_length_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
size_t size;
ngx_int_t rc;
ngx_buf_t *b;
ngx_chain_t *cl, *tl, *out, **ll;
ngx_http_request_body_t *rb;
rb = r->request_body;
if (rb->rest == -1) {
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http request body content length filter");
rb->rest = r->headers_in.content_length_n;
}
out = NULL;
ll = &out;
for (cl = in; cl; cl = cl->next) {
if (rb->rest == 0) {
break;
}
tl = ngx_chain_get_free_buf(r->pool, &rb->free);
if (tl == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
b = tl->buf;
ngx_memzero(b, sizeof(ngx_buf_t));
b->temporary = 1;
b->tag = (ngx_buf_tag_t) &ngx_http_read_client_request_body;
b->start = cl->buf->pos;
b->pos = cl->buf->pos;
b->last = cl->buf->last;
b->end = cl->buf->end;
b->flush = r->request_body_no_buffering;
size = cl->buf->last - cl->buf->pos;
if ((off_t) size < rb->rest) {
cl->buf->pos = cl->buf->last;
rb->rest -= size;
} else {
cl->buf->pos += (size_t) rb->rest;
rb->rest = 0;
b->last = cl->buf->pos;
b->last_buf = 1;
}
*ll = tl;
ll = &tl->next;
}
rc = ngx_http_top_request_body_filter(r, out);
ngx_chain_update_chains(r->pool, &rb->free, &rb->busy, &out,
(ngx_buf_tag_t) &ngx_http_read_client_request_body);
return rc;
}
static ngx_int_t
ngx_http_request_body_chunked_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
size_t size;
ngx_int_t rc;
ngx_buf_t *b;
ngx_chain_t *cl, *out, *tl, **ll;
ngx_http_request_body_t *rb;
ngx_http_core_loc_conf_t *clcf;
rb = r->request_body;
if (rb->rest == -1) {
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http request body chunked filter");
rb->chunked = ngx_pcalloc(r->pool, sizeof(ngx_http_chunked_t));
if (rb->chunked == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
r->headers_in.content_length_n = 0;
rb->rest = 3;
}
out = NULL;
ll = &out;
for (cl = in; cl; cl = cl->next) {
for ( ;; ) {
ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0,
"http body chunked buf "
"t:%d f:%d %p, pos %p, size: %z file: %O, size: %O",
cl->buf->temporary, cl->buf->in_file,
cl->buf->start, cl->buf->pos,
cl->buf->last - cl->buf->pos,
cl->buf->file_pos,
cl->buf->file_last - cl->buf->file_pos);
rc = ngx_http_parse_chunked(r, cl->buf, rb->chunked);
if (rc == NGX_OK) {
/* a chunk has been parsed successfully */
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
if (clcf->client_max_body_size
&& clcf->client_max_body_size
- r->headers_in.content_length_n < rb->chunked->size)
{
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"client intended to send too large chunked "
"body: %O+%O bytes",
r->headers_in.content_length_n,
rb->chunked->size);
r->lingering_close = 1;
return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE;
}
tl = ngx_chain_get_free_buf(r->pool, &rb->free);
if (tl == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
b = tl->buf;
ngx_memzero(b, sizeof(ngx_buf_t));
b->temporary = 1;
b->tag = (ngx_buf_tag_t) &ngx_http_read_client_request_body;
b->start = cl->buf->pos;
b->pos = cl->buf->pos;
b->last = cl->buf->last;
b->end = cl->buf->end;
b->flush = r->request_body_no_buffering;
*ll = tl;
ll = &tl->next;
size = cl->buf->last - cl->buf->pos;
if ((off_t) size > rb->chunked->size) {
cl->buf->pos += (size_t) rb->chunked->size;
r->headers_in.content_length_n += rb->chunked->size;
rb->chunked->size = 0;
} else {
rb->chunked->size -= size;
r->headers_in.content_length_n += size;
cl->buf->pos = cl->buf->last;
}
b->last = cl->buf->pos;
continue;
}
if (rc == NGX_DONE) {
/* a whole response has been parsed successfully */
rb->rest = 0;
tl = ngx_chain_get_free_buf(r->pool, &rb->free);
if (tl == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
b = tl->buf;
ngx_memzero(b, sizeof(ngx_buf_t));
b->last_buf = 1;
*ll = tl;
ll = &tl->next;
break;
}
if (rc == NGX_AGAIN) {
/* set rb->rest, amount of data we want to see next time */
rb->rest = rb->chunked->length;
break;
}
/* invalid */
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"client sent invalid chunked body");
return NGX_HTTP_BAD_REQUEST;
}
}
rc = ngx_http_top_request_body_filter(r, out);
ngx_chain_update_chains(r->pool, &rb->free, &rb->busy, &out,
(ngx_buf_tag_t) &ngx_http_read_client_request_body);
return rc;
}
ngx_int_t
ngx_http_request_body_save_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
ngx_buf_t *b;
ngx_chain_t *cl;
ngx_http_request_body_t *rb;
rb = r->request_body;
#if (NGX_DEBUG)
#if 0
for (cl = rb->bufs; cl; cl = cl->next) {
ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0,
"http body old buf t:%d f:%d %p, pos %p, size: %z "
"file: %O, size: %O",
cl->buf->temporary, cl->buf->in_file,
cl->buf->start, cl->buf->pos,
cl->buf->last - cl->buf->pos,
cl->buf->file_pos,
cl->buf->file_last - cl->buf->file_pos);
}
#endif
for (cl = in; cl; cl = cl->next) {
ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0,
"http body new buf t:%d f:%d %p, pos %p, size: %z "
"file: %O, size: %O",
cl->buf->temporary, cl->buf->in_file,
cl->buf->start, cl->buf->pos,
cl->buf->last - cl->buf->pos,
cl->buf->file_pos,
cl->buf->file_last - cl->buf->file_pos);
}
#endif
/* TODO: coalesce neighbouring buffers */
if (ngx_chain_add_copy(r->pool, &rb->bufs, in) != NGX_OK) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
if (r->request_body_no_buffering) {
return NGX_OK;
}
if (rb->rest > 0) {
if (rb->buf && rb->buf->last == rb->buf->end
&& ngx_http_write_request_body(r) != NGX_OK)
{
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
return NGX_OK;
}
/* rb->rest == 0 */
if (rb->temp_file || r->request_body_in_file_only) {
if (ngx_http_write_request_body(r) != NGX_OK) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
if (rb->temp_file->file.offset != 0) {
cl = ngx_chain_get_free_buf(r->pool, &rb->free);
if (cl == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
b = cl->buf;
ngx_memzero(b, sizeof(ngx_buf_t));
b->in_file = 1;
b->file_last = rb->temp_file->file.offset;
b->file = &rb->temp_file->file;
rb->bufs = cl;
}
}
return NGX_OK;
}
nginx-1.14.0/src/http/ngx_http_script.c 000644 001751 001751 00000123331 13265410474 021250 0 ustar 00mdounin mdounin 000000 000000
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#include
#include
#include
static ngx_int_t ngx_http_script_init_arrays(ngx_http_script_compile_t *sc);
static ngx_int_t ngx_http_script_done(ngx_http_script_compile_t *sc);
static ngx_int_t ngx_http_script_add_copy_code(ngx_http_script_compile_t *sc,
ngx_str_t *value, ngx_uint_t last);
static ngx_int_t ngx_http_script_add_var_code(ngx_http_script_compile_t *sc,
ngx_str_t *name);
static ngx_int_t ngx_http_script_add_args_code(ngx_http_script_compile_t *sc);
#if (NGX_PCRE)
static ngx_int_t ngx_http_script_add_capture_code(ngx_http_script_compile_t *sc,
ngx_uint_t n);
#endif
static ngx_int_t
ngx_http_script_add_full_name_code(ngx_http_script_compile_t *sc);
static size_t ngx_http_script_full_name_len_code(ngx_http_script_engine_t *e);
static void ngx_http_script_full_name_code(ngx_http_script_engine_t *e);
#define ngx_http_script_exit (u_char *) &ngx_http_script_exit_code
static uintptr_t ngx_http_script_exit_code = (uintptr_t) NULL;
void
ngx_http_script_flush_complex_value(ngx_http_request_t *r,
ngx_http_complex_value_t *val)
{
ngx_uint_t *index;
index = val->flushes;
if (index) {
while (*index != (ngx_uint_t) -1) {
if (r->variables[*index].no_cacheable) {
r->variables[*index].valid = 0;
r->variables[*index].not_found = 0;
}
index++;
}
}
}
ngx_int_t
ngx_http_complex_value(ngx_http_request_t *r, ngx_http_complex_value_t *val,
ngx_str_t *value)
{
size_t len;
ngx_http_script_code_pt code;
ngx_http_script_len_code_pt lcode;
ngx_http_script_engine_t e;
if (val->lengths == NULL) {
*value = val->value;
return NGX_OK;
}
ngx_http_script_flush_complex_value(r, val);
ngx_memzero(&e, sizeof(ngx_http_script_engine_t));
e.ip = val->lengths;
e.request = r;
e.flushed = 1;
len = 0;
while (*(uintptr_t *) e.ip) {
lcode = *(ngx_http_script_len_code_pt *) e.ip;
len += lcode(&e);
}
value->len = len;
value->data = ngx_pnalloc(r->pool, len);
if (value->data == NULL) {
return NGX_ERROR;
}
e.ip = val->values;
e.pos = value->data;
e.buf = *value;
while (*(uintptr_t *) e.ip) {
code = *(ngx_http_script_code_pt *) e.ip;
code((ngx_http_script_engine_t *) &e);
}
*value = e.buf;
return NGX_OK;
}
ngx_int_t
ngx_http_compile_complex_value(ngx_http_compile_complex_value_t *ccv)
{
ngx_str_t *v;
ngx_uint_t i, n, nv, nc;
ngx_array_t flushes, lengths, values, *pf, *pl, *pv;
ngx_http_script_compile_t sc;
v = ccv->value;
nv = 0;
nc = 0;
for (i = 0; i < v->len; i++) {
if (v->data[i] == '$') {
if (v->data[i + 1] >= '1' && v->data[i + 1] <= '9') {
nc++;
} else {
nv++;
}
}
}
if ((v->len == 0 || v->data[0] != '$')
&& (ccv->conf_prefix || ccv->root_prefix))
{
if (ngx_conf_full_name(ccv->cf->cycle, v, ccv->conf_prefix) != NGX_OK) {
return NGX_ERROR;
}
ccv->conf_prefix = 0;
ccv->root_prefix = 0;
}
ccv->complex_value->value = *v;
ccv->complex_value->flushes = NULL;
ccv->complex_value->lengths = NULL;
ccv->complex_value->values = NULL;
if (nv == 0 && nc == 0) {
return NGX_OK;
}
n = nv + 1;
if (ngx_array_init(&flushes, ccv->cf->pool, n, sizeof(ngx_uint_t))
!= NGX_OK)
{
return NGX_ERROR;
}
n = nv * (2 * sizeof(ngx_http_script_copy_code_t)
+ sizeof(ngx_http_script_var_code_t))
+ sizeof(uintptr_t);
if (ngx_array_init(&lengths, ccv->cf->pool, n, 1) != NGX_OK) {
return NGX_ERROR;
}
n = (nv * (2 * sizeof(ngx_http_script_copy_code_t)
+ sizeof(ngx_http_script_var_code_t))
+ sizeof(uintptr_t)
+ v->len
+ sizeof(uintptr_t) - 1)
& ~(sizeof(uintptr_t) - 1);
if (ngx_array_init(&values, ccv->cf->pool, n, 1) != NGX_OK) {
return NGX_ERROR;
}
pf = &flushes;
pl = &lengths;
pv = &values;
ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
sc.cf = ccv->cf;
sc.source = v;
sc.flushes = &pf;
sc.lengths = &pl;
sc.values = &pv;
sc.complete_lengths = 1;
sc.complete_values = 1;
sc.zero = ccv->zero;
sc.conf_prefix = ccv->conf_prefix;
sc.root_prefix = ccv->root_prefix;
if (ngx_http_script_compile(&sc) != NGX_OK) {
return NGX_ERROR;
}
if (flushes.nelts) {
ccv->complex_value->flushes = flushes.elts;
ccv->complex_value->flushes[flushes.nelts] = (ngx_uint_t) -1;
}
ccv->complex_value->lengths = lengths.elts;
ccv->complex_value->values = values.elts;
return NGX_OK;
}
char *
ngx_http_set_complex_value_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
char *p = conf;
ngx_str_t *value;
ngx_http_complex_value_t **cv;
ngx_http_compile_complex_value_t ccv;
cv = (ngx_http_complex_value_t **) (p + cmd->offset);
if (*cv != NULL) {
return "is duplicate";
}
*cv = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t));
if (*cv == NULL) {
return NGX_CONF_ERROR;
}
value = cf->args->elts;
ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
ccv.cf = cf;
ccv.value = &value[1];
ccv.complex_value = *cv;
if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
return NGX_CONF_ERROR;
}
return NGX_CONF_OK;
}
ngx_int_t
ngx_http_test_predicates(ngx_http_request_t *r, ngx_array_t *predicates)
{
ngx_str_t val;
ngx_uint_t i;
ngx_http_complex_value_t *cv;
if (predicates == NULL) {
return NGX_OK;
}
cv = predicates->elts;
for (i = 0; i < predicates->nelts; i++) {
if (ngx_http_complex_value(r, &cv[i], &val) != NGX_OK) {
return NGX_ERROR;
}
if (val.len && (val.len != 1 || val.data[0] != '0')) {
return NGX_DECLINED;
}
}
return NGX_OK;
}
char *
ngx_http_set_predicate_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
char *p = conf;
ngx_str_t *value;
ngx_uint_t i;
ngx_array_t **a;
ngx_http_complex_value_t *cv;
ngx_http_compile_complex_value_t ccv;
a = (ngx_array_t **) (p + cmd->offset);
if (*a == NGX_CONF_UNSET_PTR) {
*a = ngx_array_create(cf->pool, 1, sizeof(ngx_http_complex_value_t));
if (*a == NULL) {
return NGX_CONF_ERROR;
}
}
value = cf->args->elts;
for (i = 1; i < cf->args->nelts; i++) {
cv = ngx_array_push(*a);
if (cv == NULL) {
return NGX_CONF_ERROR;
}
ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
ccv.cf = cf;
ccv.value = &value[i];
ccv.complex_value = cv;
if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
return NGX_CONF_ERROR;
}
}
return NGX_CONF_OK;
}
ngx_uint_t
ngx_http_script_variables_count(ngx_str_t *value)
{
ngx_uint_t i, n;
for (n = 0, i = 0; i < value->len; i++) {
if (value->data[i] == '$') {
n++;
}
}
return n;
}
ngx_int_t
ngx_http_script_compile(ngx_http_script_compile_t *sc)
{
u_char ch;
ngx_str_t name;
ngx_uint_t i, bracket;
if (ngx_http_script_init_arrays(sc) != NGX_OK) {
return NGX_ERROR;
}
for (i = 0; i < sc->source->len; /* void */ ) {
name.len = 0;
if (sc->source->data[i] == '$') {
if (++i == sc->source->len) {
goto invalid_variable;
}
if (sc->source->data[i] >= '1' && sc->source->data[i] <= '9') {
#if (NGX_PCRE)
ngx_uint_t n;
n = sc->source->data[i] - '0';
if (sc->captures_mask & ((ngx_uint_t) 1 << n)) {
sc->dup_capture = 1;
}
sc->captures_mask |= (ngx_uint_t) 1 << n;
if (ngx_http_script_add_capture_code(sc, n) != NGX_OK) {
return NGX_ERROR;
}
i++;
continue;
#else
ngx_conf_log_error(NGX_LOG_EMERG, sc->cf, 0,
"using variable \"$%c\" requires "
"PCRE library", sc->source->data[i]);
return NGX_ERROR;
#endif
}
if (sc->source->data[i] == '{') {
bracket = 1;
if (++i == sc->source->len) {
goto invalid_variable;
}
name.data = &sc->source->data[i];
} else {
bracket = 0;
name.data = &sc->source->data[i];
}
for ( /* void */ ; i < sc->source->len; i++, name.len++) {
ch = sc->source->data[i];
if (ch == '}' && bracket) {
i++;
bracket = 0;
break;
}
if ((ch >= 'A' && ch <= 'Z')
|| (ch >= 'a' && ch <= 'z')
|| (ch >= '0' && ch <= '9')
|| ch == '_')
{
continue;
}
break;
}
if (bracket) {
ngx_conf_log_error(NGX_LOG_EMERG, sc->cf, 0,
"the closing bracket in \"%V\" "
"variable is missing", &name);
return NGX_ERROR;
}
if (name.len == 0) {
goto invalid_variable;
}
sc->variables++;
if (ngx_http_script_add_var_code(sc, &name) != NGX_OK) {
return NGX_ERROR;
}
continue;
}
if (sc->source->data[i] == '?' && sc->compile_args) {
sc->args = 1;
sc->compile_args = 0;
if (ngx_http_script_add_args_code(sc) != NGX_OK) {
return NGX_ERROR;
}
i++;
continue;
}
name.data = &sc->source->data[i];
while (i < sc->source->len) {
if (sc->source->data[i] == '$') {
break;
}
if (sc->source->data[i] == '?') {
sc->args = 1;
if (sc->compile_args) {
break;
}
}
i++;
name.len++;
}
sc->size += name.len;
if (ngx_http_script_add_copy_code(sc, &name, (i == sc->source->len))
!= NGX_OK)
{
return NGX_ERROR;
}
}
return ngx_http_script_done(sc);
invalid_variable:
ngx_conf_log_error(NGX_LOG_EMERG, sc->cf, 0, "invalid variable name");
return NGX_ERROR;
}
u_char *
ngx_http_script_run(ngx_http_request_t *r, ngx_str_t *value,
void *code_lengths, size_t len, void *code_values)
{
ngx_uint_t i;
ngx_http_script_code_pt code;
ngx_http_script_len_code_pt lcode;
ngx_http_script_engine_t e;
ngx_http_core_main_conf_t *cmcf;
cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
for (i = 0; i < cmcf->variables.nelts; i++) {
if (r->variables[i].no_cacheable) {
r->variables[i].valid = 0;
r->variables[i].not_found = 0;
}
}
ngx_memzero(&e, sizeof(ngx_http_script_engine_t));
e.ip = code_lengths;
e.request = r;
e.flushed = 1;
while (*(uintptr_t *) e.ip) {
lcode = *(ngx_http_script_len_code_pt *) e.ip;
len += lcode(&e);
}
value->len = len;
value->data = ngx_pnalloc(r->pool, len);
if (value->data == NULL) {
return NULL;
}
e.ip = code_values;
e.pos = value->data;
while (*(uintptr_t *) e.ip) {
code = *(ngx_http_script_code_pt *) e.ip;
code((ngx_http_script_engine_t *) &e);
}
return e.pos;
}
void
ngx_http_script_flush_no_cacheable_variables(ngx_http_request_t *r,
ngx_array_t *indices)
{
ngx_uint_t n, *index;
if (indices) {
index = indices->elts;
for (n = 0; n < indices->nelts; n++) {
if (r->variables[index[n]].no_cacheable) {
r->variables[index[n]].valid = 0;
r->variables[index[n]].not_found = 0;
}
}
}
}
static ngx_int_t
ngx_http_script_init_arrays(ngx_http_script_compile_t *sc)
{
ngx_uint_t n;
if (sc->flushes && *sc->flushes == NULL) {
n = sc->variables ? sc->variables : 1;
*sc->flushes = ngx_array_create(sc->cf->pool, n, sizeof(ngx_uint_t));
if (*sc->flushes == NULL) {
return NGX_ERROR;
}
}
if (*sc->lengths == NULL) {
n = sc->variables * (2 * sizeof(ngx_http_script_copy_code_t)
+ sizeof(ngx_http_script_var_code_t))
+ sizeof(uintptr_t);
*sc->lengths = ngx_array_create(sc->cf->pool, n, 1);
if (*sc->lengths == NULL) {
return NGX_ERROR;
}
}
if (*sc->values == NULL) {
n = (sc->variables * (2 * sizeof(ngx_http_script_copy_code_t)
+ sizeof(ngx_http_script_var_code_t))
+ sizeof(uintptr_t)
+ sc->source->len
+ sizeof(uintptr_t) - 1)
& ~(sizeof(uintptr_t) - 1);
*sc->values = ngx_array_create(sc->cf->pool, n, 1);
if (*sc->values == NULL) {
return NGX_ERROR;
}
}
sc->variables = 0;
return NGX_OK;
}
static ngx_int_t
ngx_http_script_done(ngx_http_script_compile_t *sc)
{
ngx_str_t zero;
uintptr_t *code;
if (sc->zero) {
zero.len = 1;
zero.data = (u_char *) "\0";
if (ngx_http_script_add_copy_code(sc, &zero, 0) != NGX_OK) {
return NGX_ERROR;
}
}
if (sc->conf_prefix || sc->root_prefix) {
if (ngx_http_script_add_full_name_code(sc) != NGX_OK) {
return NGX_ERROR;
}
}
if (sc->complete_lengths) {
code = ngx_http_script_add_code(*sc->lengths, sizeof(uintptr_t), NULL);
if (code == NULL) {
return NGX_ERROR;
}
*code = (uintptr_t) NULL;
}
if (sc->complete_values) {
code = ngx_http_script_add_code(*sc->values, sizeof(uintptr_t),
&sc->main);
if (code == NULL) {
return NGX_ERROR;
}
*code = (uintptr_t) NULL;
}
return NGX_OK;
}
void *
ngx_http_script_start_code(ngx_pool_t *pool, ngx_array_t **codes, size_t size)
{
if (*codes == NULL) {
*codes = ngx_array_create(pool, 256, 1);
if (*codes == NULL) {
return NULL;
}
}
return ngx_array_push_n(*codes, size);
}
void *
ngx_http_script_add_code(ngx_array_t *codes, size_t size, void *code)
{
u_char *elts, **p;
void *new;
elts = codes->elts;
new = ngx_array_push_n(codes, size);
if (new == NULL) {
return NULL;
}
if (code) {
if (elts != codes->elts) {
p = code;
*p += (u_char *) codes->elts - elts;
}
}
return new;
}
static ngx_int_t
ngx_http_script_add_copy_code(ngx_http_script_compile_t *sc, ngx_str_t *value,
ngx_uint_t last)
{
u_char *p;
size_t size, len, zero;
ngx_http_script_copy_code_t *code;
zero = (sc->zero && last);
len = value->len + zero;
code = ngx_http_script_add_code(*sc->lengths,
sizeof(ngx_http_script_copy_code_t), NULL);
if (code == NULL) {
return NGX_ERROR;
}
code->code = (ngx_http_script_code_pt) ngx_http_script_copy_len_code;
code->len = len;
size = (sizeof(ngx_http_script_copy_code_t) + len + sizeof(uintptr_t) - 1)
& ~(sizeof(uintptr_t) - 1);
code = ngx_http_script_add_code(*sc->values, size, &sc->main);
if (code == NULL) {
return NGX_ERROR;
}
code->code = ngx_http_script_copy_code;
code->len = len;
p = ngx_cpymem((u_char *) code + sizeof(ngx_http_script_copy_code_t),
value->data, value->len);
if (zero) {
*p = '\0';
sc->zero = 0;
}
return NGX_OK;
}
size_t
ngx_http_script_copy_len_code(ngx_http_script_engine_t *e)
{
ngx_http_script_copy_code_t *code;
code = (ngx_http_script_copy_code_t *) e->ip;
e->ip += sizeof(ngx_http_script_copy_code_t);
return code->len;
}
void
ngx_http_script_copy_code(ngx_http_script_engine_t *e)
{
u_char *p;
ngx_http_script_copy_code_t *code;
code = (ngx_http_script_copy_code_t *) e->ip;
p = e->pos;
if (!e->skip) {
e->pos = ngx_copy(p, e->ip + sizeof(ngx_http_script_copy_code_t),
code->len);
}
e->ip += sizeof(ngx_http_script_copy_code_t)
+ ((code->len + sizeof(uintptr_t) - 1) & ~(sizeof(uintptr_t) - 1));
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
"http script copy: \"%*s\"", e->pos - p, p);
}
static ngx_int_t
ngx_http_script_add_var_code(ngx_http_script_compile_t *sc, ngx_str_t *name)
{
ngx_int_t index, *p;
ngx_http_script_var_code_t *code;
index = ngx_http_get_variable_index(sc->cf, name);
if (index == NGX_ERROR) {
return NGX_ERROR;
}
if (sc->flushes) {
p = ngx_array_push(*sc->flushes);
if (p == NULL) {
return NGX_ERROR;
}
*p = index;
}
code = ngx_http_script_add_code(*sc->lengths,
sizeof(ngx_http_script_var_code_t), NULL);
if (code == NULL) {
return NGX_ERROR;
}
code->code = (ngx_http_script_code_pt) ngx_http_script_copy_var_len_code;
code->index = (uintptr_t) index;
code = ngx_http_script_add_code(*sc->values,
sizeof(ngx_http_script_var_code_t),
&sc->main);
if (code == NULL) {
return NGX_ERROR;
}
code->code = ngx_http_script_copy_var_code;
code->index = (uintptr_t) index;
return NGX_OK;
}
size_t
ngx_http_script_copy_var_len_code(ngx_http_script_engine_t *e)
{
ngx_http_variable_value_t *value;
ngx_http_script_var_code_t *code;
code = (ngx_http_script_var_code_t *) e->ip;
e->ip += sizeof(ngx_http_script_var_code_t);
if (e->flushed) {
value = ngx_http_get_indexed_variable(e->request, code->index);
} else {
value = ngx_http_get_flushed_variable(e->request, code->index);
}
if (value && !value->not_found) {
return value->len;
}
return 0;
}
void
ngx_http_script_copy_var_code(ngx_http_script_engine_t *e)
{
u_char *p;
ngx_http_variable_value_t *value;
ngx_http_script_var_code_t *code;
code = (ngx_http_script_var_code_t *) e->ip;
e->ip += sizeof(ngx_http_script_var_code_t);
if (!e->skip) {
if (e->flushed) {
value = ngx_http_get_indexed_variable(e->request, code->index);
} else {
value = ngx_http_get_flushed_variable(e->request, code->index);
}
if (value && !value->not_found) {
p = e->pos;
e->pos = ngx_copy(p, value->data, value->len);
ngx_log_debug2(NGX_LOG_DEBUG_HTTP,
e->request->connection->log, 0,
"http script var: \"%*s\"", e->pos - p, p);
}
}
}
static ngx_int_t
ngx_http_script_add_args_code(ngx_http_script_compile_t *sc)
{
uintptr_t *code;
code = ngx_http_script_add_code(*sc->lengths, sizeof(uintptr_t), NULL);
if (code == NULL) {
return NGX_ERROR;
}
*code = (uintptr_t) ngx_http_script_mark_args_code;
code = ngx_http_script_add_code(*sc->values, sizeof(uintptr_t), &sc->main);
if (code == NULL) {
return NGX_ERROR;
}
*code = (uintptr_t) ngx_http_script_start_args_code;
return NGX_OK;
}
size_t
ngx_http_script_mark_args_code(ngx_http_script_engine_t *e)
{
e->is_args = 1;
e->ip += sizeof(uintptr_t);
return 1;
}
void
ngx_http_script_start_args_code(ngx_http_script_engine_t *e)
{
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
"http script args");
e->is_args = 1;
e->args = e->pos;
e->ip += sizeof(uintptr_t);
}
#if (NGX_PCRE)
void
ngx_http_script_regex_start_code(ngx_http_script_engine_t *e)
{
size_t len;
ngx_int_t rc;
ngx_uint_t n;
ngx_http_request_t *r;
ngx_http_script_engine_t le;
ngx_http_script_len_code_pt lcode;
ngx_http_script_regex_code_t *code;
code = (ngx_http_script_regex_code_t *) e->ip;
r = e->request;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http script regex: \"%V\"", &code->name);
if (code->uri) {
e->line = r->uri;
} else {
e->sp--;
e->line.len = e->sp->len;
e->line.data = e->sp->data;
}
rc = ngx_http_regex_exec(r, code->regex, &e->line);
if (rc == NGX_DECLINED) {
if (e->log || (r->connection->log->log_level & NGX_LOG_DEBUG_HTTP)) {
ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0,
"\"%V\" does not match \"%V\"",
&code->name, &e->line);
}
r->ncaptures = 0;
if (code->test) {
if (code->negative_test) {
e->sp->len = 1;
e->sp->data = (u_char *) "1";
} else {
e->sp->len = 0;
e->sp->data = (u_char *) "";
}
e->sp++;
e->ip += sizeof(ngx_http_script_regex_code_t);
return;
}
e->ip += code->next;
return;
}
if (rc == NGX_ERROR) {
e->ip = ngx_http_script_exit;
e->status = NGX_HTTP_INTERNAL_SERVER_ERROR;
return;
}
if (e->log || (r->connection->log->log_level & NGX_LOG_DEBUG_HTTP)) {
ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0,
"\"%V\" matches \"%V\"", &code->name, &e->line);
}
if (code->test) {
if (code->negative_test) {
e->sp->len = 0;
e->sp->data = (u_char *) "";
} else {
e->sp->len = 1;
e->sp->data = (u_char *) "1";
}
e->sp++;
e->ip += sizeof(ngx_http_script_regex_code_t);
return;
}
if (code->status) {
e->status = code->status;
if (!code->redirect) {
e->ip = ngx_http_script_exit;
return;
}
}
if (code->uri) {
r->internal = 1;
r->valid_unparsed_uri = 0;
if (code->break_cycle) {
r->valid_location = 0;
r->uri_changed = 0;
} else {
r->uri_changed = 1;
}
}
if (code->lengths == NULL) {
e->buf.len = code->size;
if (code->uri) {
if (r->ncaptures && (r->quoted_uri || r->plus_in_uri)) {
e->buf.len += 2 * ngx_escape_uri(NULL, r->uri.data, r->uri.len,
NGX_ESCAPE_ARGS);
}
}
for (n = 2; n < r->ncaptures; n += 2) {
e->buf.len += r->captures[n + 1] - r->captures[n];
}
} else {
ngx_memzero(&le, sizeof(ngx_http_script_engine_t));
le.ip = code->lengths->elts;
le.line = e->line;
le.request = r;
le.quote = code->redirect;
len = 0;
while (*(uintptr_t *) le.ip) {
lcode = *(ngx_http_script_len_code_pt *) le.ip;
len += lcode(&le);
}
e->buf.len = len;
}
if (code->add_args && r->args.len) {
e->buf.len += r->args.len + 1;
}
e->buf.data = ngx_pnalloc(r->pool, e->buf.len);
if (e->buf.data == NULL) {
e->ip = ngx_http_script_exit;
e->status = NGX_HTTP_INTERNAL_SERVER_ERROR;
return;
}
e->quote = code->redirect;
e->pos = e->buf.data;
e->ip += sizeof(ngx_http_script_regex_code_t);
}
void
ngx_http_script_regex_end_code(ngx_http_script_engine_t *e)
{
u_char *dst, *src;
ngx_http_request_t *r;
ngx_http_script_regex_end_code_t *code;
code = (ngx_http_script_regex_end_code_t *) e->ip;
r = e->request;
e->quote = 0;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http script regex end");
if (code->redirect) {
dst = e->buf.data;
src = e->buf.data;
ngx_unescape_uri(&dst, &src, e->pos - e->buf.data,
NGX_UNESCAPE_REDIRECT);
if (src < e->pos) {
dst = ngx_movemem(dst, src, e->pos - src);
}
e->pos = dst;
if (code->add_args && r->args.len) {
*e->pos++ = (u_char) (code->args ? '&' : '?');
e->pos = ngx_copy(e->pos, r->args.data, r->args.len);
}
e->buf.len = e->pos - e->buf.data;
if (e->log || (r->connection->log->log_level & NGX_LOG_DEBUG_HTTP)) {
ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0,
"rewritten redirect: \"%V\"", &e->buf);
}
ngx_http_clear_location(r);
r->headers_out.location = ngx_list_push(&r->headers_out.headers);
if (r->headers_out.location == NULL) {
e->ip = ngx_http_script_exit;
e->status = NGX_HTTP_INTERNAL_SERVER_ERROR;
return;
}
r->headers_out.location->hash = 1;
ngx_str_set(&r->headers_out.location->key, "Location");
r->headers_out.location->value = e->buf;
e->ip += sizeof(ngx_http_script_regex_end_code_t);
return;
}
if (e->args) {
e->buf.len = e->args - e->buf.data;
if (code->add_args && r->args.len) {
*e->pos++ = '&';
e->pos = ngx_copy(e->pos, r->args.data, r->args.len);
}
r->args.len = e->pos - e->args;
r->args.data = e->args;
e->args = NULL;
} else {
e->buf.len = e->pos - e->buf.data;
if (!code->add_args) {
r->args.len = 0;
}
}
if (e->log || (r->connection->log->log_level & NGX_LOG_DEBUG_HTTP)) {
ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0,
"rewritten data: \"%V\", args: \"%V\"",
&e->buf, &r->args);
}
if (code->uri) {
r->uri = e->buf;
if (r->uri.len == 0) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"the rewritten URI has a zero length");
e->ip = ngx_http_script_exit;
e->status = NGX_HTTP_INTERNAL_SERVER_ERROR;
return;
}
ngx_http_set_exten(r);
}
e->ip += sizeof(ngx_http_script_regex_end_code_t);
}
static ngx_int_t
ngx_http_script_add_capture_code(ngx_http_script_compile_t *sc, ngx_uint_t n)
{
ngx_http_script_copy_capture_code_t *code;
code = ngx_http_script_add_code(*sc->lengths,
sizeof(ngx_http_script_copy_capture_code_t),
NULL);
if (code == NULL) {
return NGX_ERROR;
}
code->code = (ngx_http_script_code_pt)
ngx_http_script_copy_capture_len_code;
code->n = 2 * n;
code = ngx_http_script_add_code(*sc->values,
sizeof(ngx_http_script_copy_capture_code_t),
&sc->main);
if (code == NULL) {
return NGX_ERROR;
}
code->code = ngx_http_script_copy_capture_code;
code->n = 2 * n;
if (sc->ncaptures < n) {
sc->ncaptures = n;
}
return NGX_OK;
}
size_t
ngx_http_script_copy_capture_len_code(ngx_http_script_engine_t *e)
{
int *cap;
u_char *p;
ngx_uint_t n;
ngx_http_request_t *r;
ngx_http_script_copy_capture_code_t *code;
r = e->request;
code = (ngx_http_script_copy_capture_code_t *) e->ip;
e->ip += sizeof(ngx_http_script_copy_capture_code_t);
n = code->n;
if (n < r->ncaptures) {
cap = r->captures;
if ((e->is_args || e->quote)
&& (e->request->quoted_uri || e->request->plus_in_uri))
{
p = r->captures_data;
return cap[n + 1] - cap[n]
+ 2 * ngx_escape_uri(NULL, &p[cap[n]], cap[n + 1] - cap[n],
NGX_ESCAPE_ARGS);
} else {
return cap[n + 1] - cap[n];
}
}
return 0;
}
void
ngx_http_script_copy_capture_code(ngx_http_script_engine_t *e)
{
int *cap;
u_char *p, *pos;
ngx_uint_t n;
ngx_http_request_t *r;
ngx_http_script_copy_capture_code_t *code;
r = e->request;
code = (ngx_http_script_copy_capture_code_t *) e->ip;
e->ip += sizeof(ngx_http_script_copy_capture_code_t);
n = code->n;
pos = e->pos;
if (n < r->ncaptures) {
cap = r->captures;
p = r->captures_data;
if ((e->is_args || e->quote)
&& (e->request->quoted_uri || e->request->plus_in_uri))
{
e->pos = (u_char *) ngx_escape_uri(pos, &p[cap[n]],
cap[n + 1] - cap[n],
NGX_ESCAPE_ARGS);
} else {
e->pos = ngx_copy(pos, &p[cap[n]], cap[n + 1] - cap[n]);
}
}
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
"http script capture: \"%*s\"", e->pos - pos, pos);
}
#endif
static ngx_int_t
ngx_http_script_add_full_name_code(ngx_http_script_compile_t *sc)
{
ngx_http_script_full_name_code_t *code;
code = ngx_http_script_add_code(*sc->lengths,
sizeof(ngx_http_script_full_name_code_t),
NULL);
if (code == NULL) {
return NGX_ERROR;
}
code->code = (ngx_http_script_code_pt) ngx_http_script_full_name_len_code;
code->conf_prefix = sc->conf_prefix;
code = ngx_http_script_add_code(*sc->values,
sizeof(ngx_http_script_full_name_code_t),
&sc->main);
if (code == NULL) {
return NGX_ERROR;
}
code->code = ngx_http_script_full_name_code;
code->conf_prefix = sc->conf_prefix;
return NGX_OK;
}
static size_t
ngx_http_script_full_name_len_code(ngx_http_script_engine_t *e)
{
ngx_http_script_full_name_code_t *code;
code = (ngx_http_script_full_name_code_t *) e->ip;
e->ip += sizeof(ngx_http_script_full_name_code_t);
return code->conf_prefix ? ngx_cycle->conf_prefix.len:
ngx_cycle->prefix.len;
}
static void
ngx_http_script_full_name_code(ngx_http_script_engine_t *e)
{
ngx_http_script_full_name_code_t *code;
ngx_str_t value, *prefix;
code = (ngx_http_script_full_name_code_t *) e->ip;
value.data = e->buf.data;
value.len = e->pos - e->buf.data;
prefix = code->conf_prefix ? (ngx_str_t *) &ngx_cycle->conf_prefix:
(ngx_str_t *) &ngx_cycle->prefix;
if (ngx_get_full_name(e->request->pool, prefix, &value) != NGX_OK) {
e->ip = ngx_http_script_exit;
e->status = NGX_HTTP_INTERNAL_SERVER_ERROR;
return;
}
e->buf = value;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
"http script fullname: \"%V\"", &value);
e->ip += sizeof(ngx_http_script_full_name_code_t);
}
void
ngx_http_script_return_code(ngx_http_script_engine_t *e)
{
ngx_http_script_return_code_t *code;
code = (ngx_http_script_return_code_t *) e->ip;
if (code->status < NGX_HTTP_BAD_REQUEST
|| code->text.value.len
|| code->text.lengths)
{
e->status = ngx_http_send_response(e->request, code->status, NULL,
&code->text);
} else {
e->status = code->status;
}
e->ip = ngx_http_script_exit;
}
void
ngx_http_script_break_code(ngx_http_script_engine_t *e)
{
e->request->uri_changed = 0;
e->ip = ngx_http_script_exit;
}
void
ngx_http_script_if_code(ngx_http_script_engine_t *e)
{
ngx_http_script_if_code_t *code;
code = (ngx_http_script_if_code_t *) e->ip;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
"http script if");
e->sp--;
if (e->sp->len && (e->sp->len != 1 || e->sp->data[0] != '0')) {
if (code->loc_conf) {
e->request->loc_conf = code->loc_conf;
ngx_http_update_location_config(e->request);
}
e->ip += sizeof(ngx_http_script_if_code_t);
return;
}
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
"http script if: false");
e->ip += code->next;
}
void
ngx_http_script_equal_code(ngx_http_script_engine_t *e)
{
ngx_http_variable_value_t *val, *res;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
"http script equal");
e->sp--;
val = e->sp;
res = e->sp - 1;
e->ip += sizeof(uintptr_t);
if (val->len == res->len
&& ngx_strncmp(val->data, res->data, res->len) == 0)
{
*res = ngx_http_variable_true_value;
return;
}
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
"http script equal: no");
*res = ngx_http_variable_null_value;
}
void
ngx_http_script_not_equal_code(ngx_http_script_engine_t *e)
{
ngx_http_variable_value_t *val, *res;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
"http script not equal");
e->sp--;
val = e->sp;
res = e->sp - 1;
e->ip += sizeof(uintptr_t);
if (val->len == res->len
&& ngx_strncmp(val->data, res->data, res->len) == 0)
{
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
"http script not equal: no");
*res = ngx_http_variable_null_value;
return;
}
*res = ngx_http_variable_true_value;
}
void
ngx_http_script_file_code(ngx_http_script_engine_t *e)
{
ngx_str_t path;
ngx_http_request_t *r;
ngx_open_file_info_t of;
ngx_http_core_loc_conf_t *clcf;
ngx_http_variable_value_t *value;
ngx_http_script_file_code_t *code;
value = e->sp - 1;
code = (ngx_http_script_file_code_t *) e->ip;
e->ip += sizeof(ngx_http_script_file_code_t);
path.len = value->len - 1;
path.data = value->data;
r = e->request;
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http script file op %p \"%V\"", (void *) code->op, &path);
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
ngx_memzero(&of, sizeof(ngx_open_file_info_t));
of.read_ahead = clcf->read_ahead;
of.directio = clcf->directio;
of.valid = clcf->open_file_cache_valid;
of.min_uses = clcf->open_file_cache_min_uses;
of.test_only = 1;
of.errors = clcf->open_file_cache_errors;
of.events = clcf->open_file_cache_events;
if (ngx_http_set_disable_symlinks(r, clcf, &path, &of) != NGX_OK) {
e->ip = ngx_http_script_exit;
e->status = NGX_HTTP_INTERNAL_SERVER_ERROR;
return;
}
if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)
!= NGX_OK)
{
if (of.err == 0) {
e->ip = ngx_http_script_exit;
e->status = NGX_HTTP_INTERNAL_SERVER_ERROR;
return;
}
if (of.err != NGX_ENOENT
&& of.err != NGX_ENOTDIR
&& of.err != NGX_ENAMETOOLONG)
{
ngx_log_error(NGX_LOG_CRIT, r->connection->log, of.err,
"%s \"%s\" failed", of.failed, value->data);
}
switch (code->op) {
case ngx_http_script_file_plain:
case ngx_http_script_file_dir:
case ngx_http_script_file_exists:
case ngx_http_script_file_exec:
goto false_value;
case ngx_http_script_file_not_plain:
case ngx_http_script_file_not_dir:
case ngx_http_script_file_not_exists:
case ngx_http_script_file_not_exec:
goto true_value;
}
goto false_value;
}
switch (code->op) {
case ngx_http_script_file_plain:
if (of.is_file) {
goto true_value;
}
goto false_value;
case ngx_http_script_file_not_plain:
if (of.is_file) {
goto false_value;
}
goto true_value;
case ngx_http_script_file_dir:
if (of.is_dir) {
goto true_value;
}
goto false_value;
case ngx_http_script_file_not_dir:
if (of.is_dir) {
goto false_value;
}
goto true_value;
case ngx_http_script_file_exists:
if (of.is_file || of.is_dir || of.is_link) {
goto true_value;
}
goto false_value;
case ngx_http_script_file_not_exists:
if (of.is_file || of.is_dir || of.is_link) {
goto false_value;
}
goto true_value;
case ngx_http_script_file_exec:
if (of.is_exec) {
goto true_value;
}
goto false_value;
case ngx_http_script_file_not_exec:
if (of.is_exec) {
goto false_value;
}
goto true_value;
}
false_value:
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http script file op false");
*value = ngx_http_variable_null_value;
return;
true_value:
*value = ngx_http_variable_true_value;
return;
}
void
ngx_http_script_complex_value_code(ngx_http_script_engine_t *e)
{
size_t len;
ngx_http_script_engine_t le;
ngx_http_script_len_code_pt lcode;
ngx_http_script_complex_value_code_t *code;
code = (ngx_http_script_complex_value_code_t *) e->ip;
e->ip += sizeof(ngx_http_script_complex_value_code_t);
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
"http script complex value");
ngx_memzero(&le, sizeof(ngx_http_script_engine_t));
le.ip = code->lengths->elts;
le.line = e->line;
le.request = e->request;
le.quote = e->quote;
for (len = 0; *(uintptr_t *) le.ip; len += lcode(&le)) {
lcode = *(ngx_http_script_len_code_pt *) le.ip;
}
e->buf.len = len;
e->buf.data = ngx_pnalloc(e->request->pool, len);
if (e->buf.data == NULL) {
e->ip = ngx_http_script_exit;
e->status = NGX_HTTP_INTERNAL_SERVER_ERROR;
return;
}
e->pos = e->buf.data;
e->sp->len = e->buf.len;
e->sp->data = e->buf.data;
e->sp++;
}
void
ngx_http_script_value_code(ngx_http_script_engine_t *e)
{
ngx_http_script_value_code_t *code;
code = (ngx_http_script_value_code_t *) e->ip;
e->ip += sizeof(ngx_http_script_value_code_t);
e->sp->len = code->text_len;
e->sp->data = (u_char *) code->text_data;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
"http script value: \"%v\"", e->sp);
e->sp++;
}
void
ngx_http_script_set_var_code(ngx_http_script_engine_t *e)
{
ngx_http_request_t *r;
ngx_http_script_var_code_t *code;
code = (ngx_http_script_var_code_t *) e->ip;
e->ip += sizeof(ngx_http_script_var_code_t);
r = e->request;
e->sp--;
r->variables[code->index].len = e->sp->len;
r->variables[code->index].valid = 1;
r->variables[code->index].no_cacheable = 0;
r->variables[code->index].not_found = 0;
r->variables[code->index].data = e->sp->data;
#if (NGX_DEBUG)
{
ngx_http_variable_t *v;
ngx_http_core_main_conf_t *cmcf;
cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
v = cmcf->variables.elts;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
"http script set $%V", &v[code->index].name);
}
#endif
}
void
ngx_http_script_var_set_handler_code(ngx_http_script_engine_t *e)
{
ngx_http_script_var_handler_code_t *code;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
"http script set var handler");
code = (ngx_http_script_var_handler_code_t *) e->ip;
e->ip += sizeof(ngx_http_script_var_handler_code_t);
e->sp--;
code->handler(e->request, e->sp, code->data);
}
void
ngx_http_script_var_code(ngx_http_script_engine_t *e)
{
ngx_http_variable_value_t *value;
ngx_http_script_var_code_t *code;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
"http script var");
code = (ngx_http_script_var_code_t *) e->ip;
e->ip += sizeof(ngx_http_script_var_code_t);
value = ngx_http_get_flushed_variable(e->request, code->index);
if (value && !value->not_found) {
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
"http script var: \"%v\"", value);
*e->sp = *value;
e->sp++;
return;
}
*e->sp = ngx_http_variable_null_value;
e->sp++;
}
void
ngx_http_script_nop_code(ngx_http_script_engine_t *e)
{
e->ip += sizeof(uintptr_t);
}
nginx-1.14.0/src/http/ngx_http_script.h 000644 001751 001751 00000017101 13265410474 021252 0 ustar 00mdounin mdounin 000000 000000
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#ifndef _NGX_HTTP_SCRIPT_H_INCLUDED_
#define _NGX_HTTP_SCRIPT_H_INCLUDED_
#include
#include
#include
typedef struct {
u_char *ip;
u_char *pos;
ngx_http_variable_value_t *sp;
ngx_str_t buf;
ngx_str_t line;
/* the start of the rewritten arguments */
u_char *args;
unsigned flushed:1;
unsigned skip:1;
unsigned quote:1;
unsigned is_args:1;
unsigned log:1;
ngx_int_t status;
ngx_http_request_t *request;
} ngx_http_script_engine_t;
typedef struct {
ngx_conf_t *cf;
ngx_str_t *source;
ngx_array_t **flushes;
ngx_array_t **lengths;
ngx_array_t **values;
ngx_uint_t variables;
ngx_uint_t ncaptures;
ngx_uint_t captures_mask;
ngx_uint_t size;
void *main;
unsigned compile_args:1;
unsigned complete_lengths:1;
unsigned complete_values:1;
unsigned zero:1;
unsigned conf_prefix:1;
unsigned root_prefix:1;
unsigned dup_capture:1;
unsigned args:1;
} ngx_http_script_compile_t;
typedef struct {
ngx_str_t value;
ngx_uint_t *flushes;
void *lengths;
void *values;
} ngx_http_complex_value_t;
typedef struct {
ngx_conf_t *cf;
ngx_str_t *value;
ngx_http_complex_value_t *complex_value;
unsigned zero:1;
unsigned conf_prefix:1;
unsigned root_prefix:1;
} ngx_http_compile_complex_value_t;
typedef void (*ngx_http_script_code_pt) (ngx_http_script_engine_t *e);
typedef size_t (*ngx_http_script_len_code_pt) (ngx_http_script_engine_t *e);
typedef struct {
ngx_http_script_code_pt code;
uintptr_t len;
} ngx_http_script_copy_code_t;
typedef struct {
ngx_http_script_code_pt code;
uintptr_t index;
} ngx_http_script_var_code_t;
typedef struct {
ngx_http_script_code_pt code;
ngx_http_set_variable_pt handler;
uintptr_t data;
} ngx_http_script_var_handler_code_t;
typedef struct {
ngx_http_script_code_pt code;
uintptr_t n;
} ngx_http_script_copy_capture_code_t;
#if (NGX_PCRE)
typedef struct {
ngx_http_script_code_pt code;
ngx_http_regex_t *regex;
ngx_array_t *lengths;
uintptr_t size;
uintptr_t status;
uintptr_t next;
unsigned test:1;
unsigned negative_test:1;
unsigned uri:1;
unsigned args:1;
/* add the r->args to the new arguments */
unsigned add_args:1;
unsigned redirect:1;
unsigned break_cycle:1;
ngx_str_t name;
} ngx_http_script_regex_code_t;
typedef struct {
ngx_http_script_code_pt code;
unsigned uri:1;
unsigned args:1;
/* add the r->args to the new arguments */
unsigned add_args:1;
unsigned redirect:1;
} ngx_http_script_regex_end_code_t;
#endif
typedef struct {
ngx_http_script_code_pt code;
uintptr_t conf_prefix;
} ngx_http_script_full_name_code_t;
typedef struct {
ngx_http_script_code_pt code;
uintptr_t status;
ngx_http_complex_value_t text;
} ngx_http_script_return_code_t;
typedef enum {
ngx_http_script_file_plain = 0,
ngx_http_script_file_not_plain,
ngx_http_script_file_dir,
ngx_http_script_file_not_dir,
ngx_http_script_file_exists,
ngx_http_script_file_not_exists,
ngx_http_script_file_exec,
ngx_http_script_file_not_exec
} ngx_http_script_file_op_e;
typedef struct {
ngx_http_script_code_pt code;
uintptr_t op;
} ngx_http_script_file_code_t;
typedef struct {
ngx_http_script_code_pt code;
uintptr_t next;
void **loc_conf;
} ngx_http_script_if_code_t;
typedef struct {
ngx_http_script_code_pt code;
ngx_array_t *lengths;
} ngx_http_script_complex_value_code_t;
typedef struct {
ngx_http_script_code_pt code;
uintptr_t value;
uintptr_t text_len;
uintptr_t text_data;
} ngx_http_script_value_code_t;
void ngx_http_script_flush_complex_value(ngx_http_request_t *r,
ngx_http_complex_value_t *val);
ngx_int_t ngx_http_complex_value(ngx_http_request_t *r,
ngx_http_complex_value_t *val, ngx_str_t *value);
ngx_int_t ngx_http_compile_complex_value(ngx_http_compile_complex_value_t *ccv);
char *ngx_http_set_complex_value_slot(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
ngx_int_t ngx_http_test_predicates(ngx_http_request_t *r,
ngx_array_t *predicates);
char *ngx_http_set_predicate_slot(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
ngx_uint_t ngx_http_script_variables_count(ngx_str_t *value);
ngx_int_t ngx_http_script_compile(ngx_http_script_compile_t *sc);
u_char *ngx_http_script_run(ngx_http_request_t *r, ngx_str_t *value,
void *code_lengths, size_t reserved, void *code_values);
void ngx_http_script_flush_no_cacheable_variables(ngx_http_request_t *r,
ngx_array_t *indices);
void *ngx_http_script_start_code(ngx_pool_t *pool, ngx_array_t **codes,
size_t size);
void *ngx_http_script_add_code(ngx_array_t *codes, size_t size, void *code);
size_t ngx_http_script_copy_len_code(ngx_http_script_engine_t *e);
void ngx_http_script_copy_code(ngx_http_script_engine_t *e);
size_t ngx_http_script_copy_var_len_code(ngx_http_script_engine_t *e);
void ngx_http_script_copy_var_code(ngx_http_script_engine_t *e);
size_t ngx_http_script_copy_capture_len_code(ngx_http_script_engine_t *e);
void ngx_http_script_copy_capture_code(ngx_http_script_engine_t *e);
size_t ngx_http_script_mark_args_code(ngx_http_script_engine_t *e);
void ngx_http_script_start_args_code(ngx_http_script_engine_t *e);
#if (NGX_PCRE)
void ngx_http_script_regex_start_code(ngx_http_script_engine_t *e);
void ngx_http_script_regex_end_code(ngx_http_script_engine_t *e);
#endif
void ngx_http_script_return_code(ngx_http_script_engine_t *e);
void ngx_http_script_break_code(ngx_http_script_engine_t *e);
void ngx_http_script_if_code(ngx_http_script_engine_t *e);
void ngx_http_script_equal_code(ngx_http_script_engine_t *e);
void ngx_http_script_not_equal_code(ngx_http_script_engine_t *e);
void ngx_http_script_file_code(ngx_http_script_engine_t *e);
void ngx_http_script_complex_value_code(ngx_http_script_engine_t *e);
void ngx_http_script_value_code(ngx_http_script_engine_t *e);
void ngx_http_script_set_var_code(ngx_http_script_engine_t *e);
void ngx_http_script_var_set_handler_code(ngx_http_script_engine_t *e);
void ngx_http_script_var_code(ngx_http_script_engine_t *e);
void ngx_http_script_nop_code(ngx_http_script_engine_t *e);
#endif /* _NGX_HTTP_SCRIPT_H_INCLUDED_ */
nginx-1.14.0/src/http/ngx_http_variables.c 000644 001751 001751 00000200656 13265410474 021722 0 ustar 00mdounin mdounin 000000 000000
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#include
#include
#include
#include
static ngx_http_variable_t *ngx_http_add_prefix_variable(ngx_conf_t *cf,
ngx_str_t *name, ngx_uint_t flags);
static ngx_int_t ngx_http_variable_request(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
#if 0
static void ngx_http_variable_request_set(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
#endif
static ngx_int_t ngx_http_variable_request_get_size(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static void ngx_http_variable_request_set_size(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_variable_header(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_variable_cookies(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_variable_headers(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_variable_headers_internal(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data, u_char sep);
static ngx_int_t ngx_http_variable_unknown_header_in(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_variable_unknown_header_out(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_variable_unknown_trailer_out(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_variable_request_line(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_variable_cookie(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_variable_argument(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
#if (NGX_HAVE_TCP_INFO)
static ngx_int_t ngx_http_variable_tcpinfo(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
#endif
static ngx_int_t ngx_http_variable_content_length(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_variable_host(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_variable_binary_remote_addr(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_variable_remote_addr(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_variable_remote_port(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_variable_proxy_protocol_addr(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_variable_proxy_protocol_port(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_variable_server_addr(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_variable_server_port(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_variable_scheme(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_variable_https(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static void ngx_http_variable_set_args(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_variable_is_args(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_variable_document_root(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_variable_realpath_root(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_variable_request_filename(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_variable_server_name(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_variable_request_method(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_variable_remote_user(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_variable_bytes_sent(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_variable_body_bytes_sent(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_variable_pipe(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_variable_request_completion(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_variable_request_body(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_variable_request_body_file(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_variable_request_length(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_variable_request_time(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_variable_request_id(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_variable_status(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_variable_sent_content_type(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_variable_sent_content_length(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_variable_sent_location(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_variable_sent_last_modified(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_variable_sent_connection(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_variable_sent_keep_alive(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_variable_sent_transfer_encoding(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_variable_connection(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_variable_connection_requests(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_variable_nginx_version(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_variable_hostname(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_variable_pid(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_variable_msec(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_variable_time_iso8601(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_variable_time_local(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
/*
* TODO:
* Apache CGI: AUTH_TYPE, PATH_INFO (null), PATH_TRANSLATED
* REMOTE_HOST (null), REMOTE_IDENT (null),
* SERVER_SOFTWARE
*
* Apache SSI: DOCUMENT_NAME, LAST_MODIFIED, USER_NAME (file owner)
*/
/*
* the $http_host, $http_user_agent, $http_referer, and $http_via
* variables may be handled by generic
* ngx_http_variable_unknown_header_in(), but for performance reasons
* they are handled using dedicated entries
*/
static ngx_http_variable_t ngx_http_core_variables[] = {
{ ngx_string("http_host"), NULL, ngx_http_variable_header,
offsetof(ngx_http_request_t, headers_in.host), 0, 0 },
{ ngx_string("http_user_agent"), NULL, ngx_http_variable_header,
offsetof(ngx_http_request_t, headers_in.user_agent), 0, 0 },
{ ngx_string("http_referer"), NULL, ngx_http_variable_header,
offsetof(ngx_http_request_t, headers_in.referer), 0, 0 },
#if (NGX_HTTP_GZIP)
{ ngx_string("http_via"), NULL, ngx_http_variable_header,
offsetof(ngx_http_request_t, headers_in.via), 0, 0 },
#endif
#if (NGX_HTTP_X_FORWARDED_FOR)
{ ngx_string("http_x_forwarded_for"), NULL, ngx_http_variable_headers,
offsetof(ngx_http_request_t, headers_in.x_forwarded_for), 0, 0 },
#endif
{ ngx_string("http_cookie"), NULL, ngx_http_variable_cookies,
offsetof(ngx_http_request_t, headers_in.cookies), 0, 0 },
{ ngx_string("content_length"), NULL, ngx_http_variable_content_length,
0, 0, 0 },
{ ngx_string("content_type"), NULL, ngx_http_variable_header,
offsetof(ngx_http_request_t, headers_in.content_type), 0, 0 },
{ ngx_string("host"), NULL, ngx_http_variable_host, 0, 0, 0 },
{ ngx_string("binary_remote_addr"), NULL,
ngx_http_variable_binary_remote_addr, 0, 0, 0 },
{ ngx_string("remote_addr"), NULL, ngx_http_variable_remote_addr, 0, 0, 0 },
{ ngx_string("remote_port"), NULL, ngx_http_variable_remote_port, 0, 0, 0 },
{ ngx_string("proxy_protocol_addr"), NULL,
ngx_http_variable_proxy_protocol_addr, 0, 0, 0 },
{ ngx_string("proxy_protocol_port"), NULL,
ngx_http_variable_proxy_protocol_port, 0, 0, 0 },
{ ngx_string("server_addr"), NULL, ngx_http_variable_server_addr, 0, 0, 0 },
{ ngx_string("server_port"), NULL, ngx_http_variable_server_port, 0, 0, 0 },
{ ngx_string("server_protocol"), NULL, ngx_http_variable_request,
offsetof(ngx_http_request_t, http_protocol), 0, 0 },
{ ngx_string("scheme"), NULL, ngx_http_variable_scheme, 0, 0, 0 },
{ ngx_string("https"), NULL, ngx_http_variable_https, 0, 0, 0 },
{ ngx_string("request_uri"), NULL, ngx_http_variable_request,
offsetof(ngx_http_request_t, unparsed_uri), 0, 0 },
{ ngx_string("uri"), NULL, ngx_http_variable_request,
offsetof(ngx_http_request_t, uri),
NGX_HTTP_VAR_NOCACHEABLE, 0 },
{ ngx_string("document_uri"), NULL, ngx_http_variable_request,
offsetof(ngx_http_request_t, uri),
NGX_HTTP_VAR_NOCACHEABLE, 0 },
{ ngx_string("request"), NULL, ngx_http_variable_request_line, 0, 0, 0 },
{ ngx_string("document_root"), NULL,
ngx_http_variable_document_root, 0, NGX_HTTP_VAR_NOCACHEABLE, 0 },
{ ngx_string("realpath_root"), NULL,
ngx_http_variable_realpath_root, 0, NGX_HTTP_VAR_NOCACHEABLE, 0 },
{ ngx_string("query_string"), NULL, ngx_http_variable_request,
offsetof(ngx_http_request_t, args),
NGX_HTTP_VAR_NOCACHEABLE, 0 },
{ ngx_string("args"),
ngx_http_variable_set_args,
ngx_http_variable_request,
offsetof(ngx_http_request_t, args),
NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE, 0 },
{ ngx_string("is_args"), NULL, ngx_http_variable_is_args,
0, NGX_HTTP_VAR_NOCACHEABLE, 0 },
{ ngx_string("request_filename"), NULL,
ngx_http_variable_request_filename, 0,
NGX_HTTP_VAR_NOCACHEABLE, 0 },
{ ngx_string("server_name"), NULL, ngx_http_variable_server_name, 0, 0, 0 },
{ ngx_string("request_method"), NULL,
ngx_http_variable_request_method, 0,
NGX_HTTP_VAR_NOCACHEABLE, 0 },
{ ngx_string("remote_user"), NULL, ngx_http_variable_remote_user, 0, 0, 0 },
{ ngx_string("bytes_sent"), NULL, ngx_http_variable_bytes_sent,
0, 0, 0 },
{ ngx_string("body_bytes_sent"), NULL, ngx_http_variable_body_bytes_sent,
0, 0, 0 },
{ ngx_string("pipe"), NULL, ngx_http_variable_pipe,
0, 0, 0 },
{ ngx_string("request_completion"), NULL,
ngx_http_variable_request_completion,
0, 0, 0 },
{ ngx_string("request_body"), NULL,
ngx_http_variable_request_body,
0, 0, 0 },
{ ngx_string("request_body_file"), NULL,
ngx_http_variable_request_body_file,
0, 0, 0 },
{ ngx_string("request_length"), NULL, ngx_http_variable_request_length,
0, NGX_HTTP_VAR_NOCACHEABLE, 0 },
{ ngx_string("request_time"), NULL, ngx_http_variable_request_time,
0, NGX_HTTP_VAR_NOCACHEABLE, 0 },
{ ngx_string("request_id"), NULL,
ngx_http_variable_request_id,
0, 0, 0 },
{ ngx_string("status"), NULL,
ngx_http_variable_status, 0,
NGX_HTTP_VAR_NOCACHEABLE, 0 },
{ ngx_string("sent_http_content_type"), NULL,
ngx_http_variable_sent_content_type, 0, 0, 0 },
{ ngx_string("sent_http_content_length"), NULL,
ngx_http_variable_sent_content_length, 0, 0, 0 },
{ ngx_string("sent_http_location"), NULL,
ngx_http_variable_sent_location, 0, 0, 0 },
{ ngx_string("sent_http_last_modified"), NULL,
ngx_http_variable_sent_last_modified, 0, 0, 0 },
{ ngx_string("sent_http_connection"), NULL,
ngx_http_variable_sent_connection, 0, 0, 0 },
{ ngx_string("sent_http_keep_alive"), NULL,
ngx_http_variable_sent_keep_alive, 0, 0, 0 },
{ ngx_string("sent_http_transfer_encoding"), NULL,
ngx_http_variable_sent_transfer_encoding, 0, 0, 0 },
{ ngx_string("sent_http_cache_control"), NULL, ngx_http_variable_headers,
offsetof(ngx_http_request_t, headers_out.cache_control), 0, 0 },
{ ngx_string("sent_http_link"), NULL, ngx_http_variable_headers,
offsetof(ngx_http_request_t, headers_out.link), 0, 0 },
{ ngx_string("limit_rate"), ngx_http_variable_request_set_size,
ngx_http_variable_request_get_size,
offsetof(ngx_http_request_t, limit_rate),
NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE, 0 },
{ ngx_string("connection"), NULL,
ngx_http_variable_connection, 0, 0, 0 },
{ ngx_string("connection_requests"), NULL,
ngx_http_variable_connection_requests, 0, 0, 0 },
{ ngx_string("nginx_version"), NULL, ngx_http_variable_nginx_version,
0, 0, 0 },
{ ngx_string("hostname"), NULL, ngx_http_variable_hostname,
0, 0, 0 },
{ ngx_string("pid"), NULL, ngx_http_variable_pid,
0, 0, 0 },
{ ngx_string("msec"), NULL, ngx_http_variable_msec,
0, NGX_HTTP_VAR_NOCACHEABLE, 0 },
{ ngx_string("time_iso8601"), NULL, ngx_http_variable_time_iso8601,
0, NGX_HTTP_VAR_NOCACHEABLE, 0 },
{ ngx_string("time_local"), NULL, ngx_http_variable_time_local,
0, NGX_HTTP_VAR_NOCACHEABLE, 0 },
#if (NGX_HAVE_TCP_INFO)
{ ngx_string("tcpinfo_rtt"), NULL, ngx_http_variable_tcpinfo,
0, NGX_HTTP_VAR_NOCACHEABLE, 0 },
{ ngx_string("tcpinfo_rttvar"), NULL, ngx_http_variable_tcpinfo,
1, NGX_HTTP_VAR_NOCACHEABLE, 0 },
{ ngx_string("tcpinfo_snd_cwnd"), NULL, ngx_http_variable_tcpinfo,
2, NGX_HTTP_VAR_NOCACHEABLE, 0 },
{ ngx_string("tcpinfo_rcv_space"), NULL, ngx_http_variable_tcpinfo,
3, NGX_HTTP_VAR_NOCACHEABLE, 0 },
#endif
{ ngx_string("http_"), NULL, ngx_http_variable_unknown_header_in,
0, NGX_HTTP_VAR_PREFIX, 0 },
{ ngx_string("sent_http_"), NULL, ngx_http_variable_unknown_header_out,
0, NGX_HTTP_VAR_PREFIX, 0 },
{ ngx_string("sent_trailer_"), NULL, ngx_http_variable_unknown_trailer_out,
0, NGX_HTTP_VAR_PREFIX, 0 },
{ ngx_string("cookie_"), NULL, ngx_http_variable_cookie,
0, NGX_HTTP_VAR_PREFIX, 0 },
{ ngx_string("arg_"), NULL, ngx_http_variable_argument,
0, NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_PREFIX, 0 },
ngx_http_null_variable
};
ngx_http_variable_value_t ngx_http_variable_null_value =
ngx_http_variable("");
ngx_http_variable_value_t ngx_http_variable_true_value =
ngx_http_variable("1");
static ngx_uint_t ngx_http_variable_depth = 100;
ngx_http_variable_t *
ngx_http_add_variable(ngx_conf_t *cf, ngx_str_t *name, ngx_uint_t flags)
{
ngx_int_t rc;
ngx_uint_t i;
ngx_hash_key_t *key;
ngx_http_variable_t *v;
ngx_http_core_main_conf_t *cmcf;
if (name->len == 0) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid variable name \"$\"");
return NULL;
}
if (flags & NGX_HTTP_VAR_PREFIX) {
return ngx_http_add_prefix_variable(cf, name, flags);
}
cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
key = cmcf->variables_keys->keys.elts;
for (i = 0; i < cmcf->variables_keys->keys.nelts; i++) {
if (name->len != key[i].key.len
|| ngx_strncasecmp(name->data, key[i].key.data, name->len) != 0)
{
continue;
}
v = key[i].value;
if (!(v->flags & NGX_HTTP_VAR_CHANGEABLE)) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"the duplicate \"%V\" variable", name);
return NULL;
}
if (!(flags & NGX_HTTP_VAR_WEAK)) {
v->flags &= ~NGX_HTTP_VAR_WEAK;
}
return v;
}
v = ngx_palloc(cf->pool, sizeof(ngx_http_variable_t));
if (v == NULL) {
return NULL;
}
v->name.len = name->len;
v->name.data = ngx_pnalloc(cf->pool, name->len);
if (v->name.data == NULL) {
return NULL;
}
ngx_strlow(v->name.data, name->data, name->len);
v->set_handler = NULL;
v->get_handler = NULL;
v->data = 0;
v->flags = flags;
v->index = 0;
rc = ngx_hash_add_key(cmcf->variables_keys, &v->name, v, 0);
if (rc == NGX_ERROR) {
return NULL;
}
if (rc == NGX_BUSY) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"conflicting variable name \"%V\"", name);
return NULL;
}
return v;
}
static ngx_http_variable_t *
ngx_http_add_prefix_variable(ngx_conf_t *cf, ngx_str_t *name, ngx_uint_t flags)
{
ngx_uint_t i;
ngx_http_variable_t *v;
ngx_http_core_main_conf_t *cmcf;
cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
v = cmcf->prefix_variables.elts;
for (i = 0; i < cmcf->prefix_variables.nelts; i++) {
if (name->len != v[i].name.len
|| ngx_strncasecmp(name->data, v[i].name.data, name->len) != 0)
{
continue;
}
v = &v[i];
if (!(v->flags & NGX_HTTP_VAR_CHANGEABLE)) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"the duplicate \"%V\" variable", name);
return NULL;
}
if (!(flags & NGX_HTTP_VAR_WEAK)) {
v->flags &= ~NGX_HTTP_VAR_WEAK;
}
return v;
}
v = ngx_array_push(&cmcf->prefix_variables);
if (v == NULL) {
return NULL;
}
v->name.len = name->len;
v->name.data = ngx_pnalloc(cf->pool, name->len);
if (v->name.data == NULL) {
return NULL;
}
ngx_strlow(v->name.data, name->data, name->len);
v->set_handler = NULL;
v->get_handler = NULL;
v->data = 0;
v->flags = flags;
v->index = 0;
return v;
}
ngx_int_t
ngx_http_get_variable_index(ngx_conf_t *cf, ngx_str_t *name)
{
ngx_uint_t i;
ngx_http_variable_t *v;
ngx_http_core_main_conf_t *cmcf;
if (name->len == 0) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid variable name \"$\"");
return NGX_ERROR;
}
cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
v = cmcf->variables.elts;
if (v == NULL) {
if (ngx_array_init(&cmcf->variables, cf->pool, 4,
sizeof(ngx_http_variable_t))
!= NGX_OK)
{
return NGX_ERROR;
}
} else {
for (i = 0; i < cmcf->variables.nelts; i++) {
if (name->len != v[i].name.len
|| ngx_strncasecmp(name->data, v[i].name.data, name->len) != 0)
{
continue;
}
return i;
}
}
v = ngx_array_push(&cmcf->variables);
if (v == NULL) {
return NGX_ERROR;
}
v->name.len = name->len;
v->name.data = ngx_pnalloc(cf->pool, name->len);
if (v->name.data == NULL) {
return NGX_ERROR;
}
ngx_strlow(v->name.data, name->data, name->len);
v->set_handler = NULL;
v->get_handler = NULL;
v->data = 0;
v->flags = 0;
v->index = cmcf->variables.nelts - 1;
return v->index;
}
ngx_http_variable_value_t *
ngx_http_get_indexed_variable(ngx_http_request_t *r, ngx_uint_t index)
{
ngx_http_variable_t *v;
ngx_http_core_main_conf_t *cmcf;
cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
if (cmcf->variables.nelts <= index) {
ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
"unknown variable index: %ui", index);
return NULL;
}
if (r->variables[index].not_found || r->variables[index].valid) {
return &r->variables[index];
}
v = cmcf->variables.elts;
if (ngx_http_variable_depth == 0) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"cycle while evaluating variable \"%V\"",
&v[index].name);
return NULL;
}
ngx_http_variable_depth--;
if (v[index].get_handler(r, &r->variables[index], v[index].data)
== NGX_OK)
{
ngx_http_variable_depth++;
if (v[index].flags & NGX_HTTP_VAR_NOCACHEABLE) {
r->variables[index].no_cacheable = 1;
}
return &r->variables[index];
}
ngx_http_variable_depth++;
r->variables[index].valid = 0;
r->variables[index].not_found = 1;
return NULL;
}
ngx_http_variable_value_t *
ngx_http_get_flushed_variable(ngx_http_request_t *r, ngx_uint_t index)
{
ngx_http_variable_value_t *v;
v = &r->variables[index];
if (v->valid || v->not_found) {
if (!v->no_cacheable) {
return v;
}
v->valid = 0;
v->not_found = 0;
}
return ngx_http_get_indexed_variable(r, index);
}
ngx_http_variable_value_t *
ngx_http_get_variable(ngx_http_request_t *r, ngx_str_t *name, ngx_uint_t key)
{
size_t len;
ngx_uint_t i, n;
ngx_http_variable_t *v;
ngx_http_variable_value_t *vv;
ngx_http_core_main_conf_t *cmcf;
cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
v = ngx_hash_find(&cmcf->variables_hash, key, name->data, name->len);
if (v) {
if (v->flags & NGX_HTTP_VAR_INDEXED) {
return ngx_http_get_flushed_variable(r, v->index);
}
if (ngx_http_variable_depth == 0) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"cycle while evaluating variable \"%V\"", name);
return NULL;
}
ngx_http_variable_depth--;
vv = ngx_palloc(r->pool, sizeof(ngx_http_variable_value_t));
if (vv && v->get_handler(r, vv, v->data) == NGX_OK) {
ngx_http_variable_depth++;
return vv;
}
ngx_http_variable_depth++;
return NULL;
}
vv = ngx_palloc(r->pool, sizeof(ngx_http_variable_value_t));
if (vv == NULL) {
return NULL;
}
len = 0;
v = cmcf->prefix_variables.elts;
n = cmcf->prefix_variables.nelts;
for (i = 0; i < cmcf->prefix_variables.nelts; i++) {
if (name->len >= v[i].name.len && name->len > len
&& ngx_strncmp(name->data, v[i].name.data, v[i].name.len) == 0)
{
len = v[i].name.len;
n = i;
}
}
if (n != cmcf->prefix_variables.nelts) {
if (v[n].get_handler(r, vv, (uintptr_t) name) == NGX_OK) {
return vv;
}
return NULL;
}
vv->not_found = 1;
return vv;
}
static ngx_int_t
ngx_http_variable_request(ngx_http_request_t *r, ngx_http_variable_value_t *v,
uintptr_t data)
{
ngx_str_t *s;
s = (ngx_str_t *) ((char *) r + data);
if (s->data) {
v->len = s->len;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = s->data;
} else {
v->not_found = 1;
}
return NGX_OK;
}
#if 0
static void
ngx_http_variable_request_set(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
ngx_str_t *s;
s = (ngx_str_t *) ((char *) r + data);
s->len = v->len;
s->data = v->data;
}
#endif
static ngx_int_t
ngx_http_variable_request_get_size(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
size_t *sp;
sp = (size_t *) ((char *) r + data);
v->data = ngx_pnalloc(r->pool, NGX_SIZE_T_LEN);
if (v->data == NULL) {
return NGX_ERROR;
}
v->len = ngx_sprintf(v->data, "%uz", *sp) - v->data;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
return NGX_OK;
}
static void
ngx_http_variable_request_set_size(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
ssize_t s, *sp;
ngx_str_t val;
val.len = v->len;
val.data = v->data;
s = ngx_parse_size(&val);
if (s == NGX_ERROR) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"invalid size \"%V\"", &val);
return;
}
sp = (ssize_t *) ((char *) r + data);
*sp = s;
return;
}
static ngx_int_t
ngx_http_variable_header(ngx_http_request_t *r, ngx_http_variable_value_t *v,
uintptr_t data)
{
ngx_table_elt_t *h;
h = *(ngx_table_elt_t **) ((char *) r + data);
if (h) {
v->len = h->value.len;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = h->value.data;
} else {
v->not_found = 1;
}
return NGX_OK;
}
static ngx_int_t
ngx_http_variable_cookies(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
return ngx_http_variable_headers_internal(r, v, data, ';');
}
static ngx_int_t
ngx_http_variable_headers(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
return ngx_http_variable_headers_internal(r, v, data, ',');
}
static ngx_int_t
ngx_http_variable_headers_internal(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data, u_char sep)
{
size_t len;
u_char *p, *end;
ngx_uint_t i, n;
ngx_array_t *a;
ngx_table_elt_t **h;
a = (ngx_array_t *) ((char *) r + data);
n = a->nelts;
h = a->elts;
len = 0;
for (i = 0; i < n; i++) {
if (h[i]->hash == 0) {
continue;
}
len += h[i]->value.len + 2;
}
if (len == 0) {
v->not_found = 1;
return NGX_OK;
}
len -= 2;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
if (n == 1) {
v->len = (*h)->value.len;
v->data = (*h)->value.data;
return NGX_OK;
}
p = ngx_pnalloc(r->pool, len);
if (p == NULL) {
return NGX_ERROR;
}
v->len = len;
v->data = p;
end = p + len;
for (i = 0; /* void */ ; i++) {
if (h[i]->hash == 0) {
continue;
}
p = ngx_copy(p, h[i]->value.data, h[i]->value.len);
if (p == end) {
break;
}
*p++ = sep; *p++ = ' ';
}
return NGX_OK;
}
static ngx_int_t
ngx_http_variable_unknown_header_in(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
return ngx_http_variable_unknown_header(v, (ngx_str_t *) data,
&r->headers_in.headers.part,
sizeof("http_") - 1);
}
static ngx_int_t
ngx_http_variable_unknown_header_out(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
return ngx_http_variable_unknown_header(v, (ngx_str_t *) data,
&r->headers_out.headers.part,
sizeof("sent_http_") - 1);
}
static ngx_int_t
ngx_http_variable_unknown_trailer_out(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
return ngx_http_variable_unknown_header(v, (ngx_str_t *) data,
&r->headers_out.trailers.part,
sizeof("sent_trailer_") - 1);
}
ngx_int_t
ngx_http_variable_unknown_header(ngx_http_variable_value_t *v, ngx_str_t *var,
ngx_list_part_t *part, size_t prefix)
{
u_char ch;
ngx_uint_t i, n;
ngx_table_elt_t *header;
header = part->elts;
for (i = 0; /* void */ ; i++) {
if (i >= part->nelts) {
if (part->next == NULL) {
break;
}
part = part->next;
header = part->elts;
i = 0;
}
if (header[i].hash == 0) {
continue;
}
for (n = 0; n + prefix < var->len && n < header[i].key.len; n++) {
ch = header[i].key.data[n];
if (ch >= 'A' && ch <= 'Z') {
ch |= 0x20;
} else if (ch == '-') {
ch = '_';
}
if (var->data[n + prefix] != ch) {
break;
}
}
if (n + prefix == var->len && n == header[i].key.len) {
v->len = header[i].value.len;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = header[i].value.data;
return NGX_OK;
}
}
v->not_found = 1;
return NGX_OK;
}
static ngx_int_t
ngx_http_variable_request_line(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
u_char *p, *s;
s = r->request_line.data;
if (s == NULL) {
s = r->request_start;
if (s == NULL) {
v->not_found = 1;
return NGX_OK;
}
for (p = s; p < r->header_in->last; p++) {
if (*p == CR || *p == LF) {
break;
}
}
r->request_line.len = p - s;
r->request_line.data = s;
}
v->len = r->request_line.len;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = s;
return NGX_OK;
}
static ngx_int_t
ngx_http_variable_cookie(ngx_http_request_t *r, ngx_http_variable_value_t *v,
uintptr_t data)
{
ngx_str_t *name = (ngx_str_t *) data;
ngx_str_t cookie, s;
s.len = name->len - (sizeof("cookie_") - 1);
s.data = name->data + sizeof("cookie_") - 1;
if (ngx_http_parse_multi_header_lines(&r->headers_in.cookies, &s, &cookie)
== NGX_DECLINED)
{
v->not_found = 1;
return NGX_OK;
}
v->len = cookie.len;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = cookie.data;
return NGX_OK;
}
static ngx_int_t
ngx_http_variable_argument(ngx_http_request_t *r, ngx_http_variable_value_t *v,
uintptr_t data)
{
ngx_str_t *name = (ngx_str_t *) data;
u_char *arg;
size_t len;
ngx_str_t value;
len = name->len - (sizeof("arg_") - 1);
arg = name->data + sizeof("arg_") - 1;
if (ngx_http_arg(r, arg, len, &value) != NGX_OK) {
v->not_found = 1;
return NGX_OK;
}
v->data = value.data;
v->len = value.len;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
return NGX_OK;
}
#if (NGX_HAVE_TCP_INFO)
static ngx_int_t
ngx_http_variable_tcpinfo(ngx_http_request_t *r, ngx_http_variable_value_t *v,
uintptr_t data)
{
struct tcp_info ti;
socklen_t len;
uint32_t value;
len = sizeof(struct tcp_info);
if (getsockopt(r->connection->fd, IPPROTO_TCP, TCP_INFO, &ti, &len) == -1) {
v->not_found = 1;
return NGX_OK;
}
v->data = ngx_pnalloc(r->pool, NGX_INT32_LEN);
if (v->data == NULL) {
return NGX_ERROR;
}
switch (data) {
case 0:
value = ti.tcpi_rtt;
break;
case 1:
value = ti.tcpi_rttvar;
break;
case 2:
value = ti.tcpi_snd_cwnd;
break;
case 3:
value = ti.tcpi_rcv_space;
break;
/* suppress warning */
default:
value = 0;
break;
}
v->len = ngx_sprintf(v->data, "%uD", value) - v->data;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
return NGX_OK;
}
#endif
static ngx_int_t
ngx_http_variable_content_length(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
u_char *p;
if (r->headers_in.content_length) {
v->len = r->headers_in.content_length->value.len;
v->data = r->headers_in.content_length->value.data;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
} else if (r->reading_body) {
v->not_found = 1;
v->no_cacheable = 1;
} else if (r->headers_in.content_length_n >= 0) {
p = ngx_pnalloc(r->pool, NGX_OFF_T_LEN);
if (p == NULL) {
return NGX_ERROR;
}
v->len = ngx_sprintf(p, "%O", r->headers_in.content_length_n) - p;
v->data = p;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
} else {
v->not_found = 1;
}
return NGX_OK;
}
static ngx_int_t
ngx_http_variable_host(ngx_http_request_t *r, ngx_http_variable_value_t *v,
uintptr_t data)
{
ngx_http_core_srv_conf_t *cscf;
if (r->headers_in.server.len) {
v->len = r->headers_in.server.len;
v->data = r->headers_in.server.data;
} else {
cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
v->len = cscf->server_name.len;
v->data = cscf->server_name.data;
}
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
return NGX_OK;
}
static ngx_int_t
ngx_http_variable_binary_remote_addr(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
struct sockaddr_in *sin;
#if (NGX_HAVE_INET6)
struct sockaddr_in6 *sin6;
#endif
switch (r->connection->sockaddr->sa_family) {
#if (NGX_HAVE_INET6)
case AF_INET6:
sin6 = (struct sockaddr_in6 *) r->connection->sockaddr;
v->len = sizeof(struct in6_addr);
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = sin6->sin6_addr.s6_addr;
break;
#endif
#if (NGX_HAVE_UNIX_DOMAIN)
case AF_UNIX:
v->len = r->connection->addr_text.len;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = r->connection->addr_text.data;
break;
#endif
default: /* AF_INET */
sin = (struct sockaddr_in *) r->connection->sockaddr;
v->len = sizeof(in_addr_t);
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = (u_char *) &sin->sin_addr;
break;
}
return NGX_OK;
}
static ngx_int_t
ngx_http_variable_remote_addr(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
v->len = r->connection->addr_text.len;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = r->connection->addr_text.data;
return NGX_OK;
}
static ngx_int_t
ngx_http_variable_remote_port(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
ngx_uint_t port;
v->len = 0;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = ngx_pnalloc(r->pool, sizeof("65535") - 1);
if (v->data == NULL) {
return NGX_ERROR;
}
port = ngx_inet_get_port(r->connection->sockaddr);
if (port > 0 && port < 65536) {
v->len = ngx_sprintf(v->data, "%ui", port) - v->data;
}
return NGX_OK;
}
static ngx_int_t
ngx_http_variable_proxy_protocol_addr(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
v->len = r->connection->proxy_protocol_addr.len;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = r->connection->proxy_protocol_addr.data;
return NGX_OK;
}
static ngx_int_t
ngx_http_variable_proxy_protocol_port(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
ngx_uint_t port;
v->len = 0;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = ngx_pnalloc(r->pool, sizeof("65535") - 1);
if (v->data == NULL) {
return NGX_ERROR;
}
port = r->connection->proxy_protocol_port;
if (port > 0 && port < 65536) {
v->len = ngx_sprintf(v->data, "%ui", port) - v->data;
}
return NGX_OK;
}
static ngx_int_t
ngx_http_variable_server_addr(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
ngx_str_t s;
u_char addr[NGX_SOCKADDR_STRLEN];
s.len = NGX_SOCKADDR_STRLEN;
s.data = addr;
if (ngx_connection_local_sockaddr(r->connection, &s, 0) != NGX_OK) {
return NGX_ERROR;
}
s.data = ngx_pnalloc(r->pool, s.len);
if (s.data == NULL) {
return NGX_ERROR;
}
ngx_memcpy(s.data, addr, s.len);
v->len = s.len;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = s.data;
return NGX_OK;
}
static ngx_int_t
ngx_http_variable_server_port(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
ngx_uint_t port;
v->len = 0;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
if (ngx_connection_local_sockaddr(r->connection, NULL, 0) != NGX_OK) {
return NGX_ERROR;
}
v->data = ngx_pnalloc(r->pool, sizeof("65535") - 1);
if (v->data == NULL) {
return NGX_ERROR;
}
port = ngx_inet_get_port(r->connection->local_sockaddr);
if (port > 0 && port < 65536) {
v->len = ngx_sprintf(v->data, "%ui", port) - v->data;
}
return NGX_OK;
}
static ngx_int_t
ngx_http_variable_scheme(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
#if (NGX_HTTP_SSL)
if (r->connection->ssl) {
v->len = sizeof("https") - 1;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = (u_char *) "https";
return NGX_OK;
}
#endif
v->len = sizeof("http") - 1;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = (u_char *) "http";
return NGX_OK;
}
static ngx_int_t
ngx_http_variable_https(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
#if (NGX_HTTP_SSL)
if (r->connection->ssl) {
v->len = sizeof("on") - 1;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = (u_char *) "on";
return NGX_OK;
}
#endif
*v = ngx_http_variable_null_value;
return NGX_OK;
}
static void
ngx_http_variable_set_args(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
r->args.len = v->len;
r->args.data = v->data;
r->valid_unparsed_uri = 0;
}
static ngx_int_t
ngx_http_variable_is_args(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
if (r->args.len == 0) {
*v = ngx_http_variable_null_value;
return NGX_OK;
}
v->len = 1;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = (u_char *) "?";
return NGX_OK;
}
static ngx_int_t
ngx_http_variable_document_root(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
ngx_str_t path;
ngx_http_core_loc_conf_t *clcf;
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
if (clcf->root_lengths == NULL) {
v->len = clcf->root.len;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = clcf->root.data;
} else {
if (ngx_http_script_run(r, &path, clcf->root_lengths->elts, 0,
clcf->root_values->elts)
== NULL)
{
return NGX_ERROR;
}
if (ngx_get_full_name(r->pool, (ngx_str_t *) &ngx_cycle->prefix, &path)
!= NGX_OK)
{
return NGX_ERROR;
}
v->len = path.len;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = path.data;
}
return NGX_OK;
}
static ngx_int_t
ngx_http_variable_realpath_root(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
u_char *real;
size_t len;
ngx_str_t path;
ngx_http_core_loc_conf_t *clcf;
#if (NGX_HAVE_MAX_PATH)
u_char buffer[NGX_MAX_PATH];
#endif
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
if (clcf->root_lengths == NULL) {
path = clcf->root;
} else {
if (ngx_http_script_run(r, &path, clcf->root_lengths->elts, 1,
clcf->root_values->elts)
== NULL)
{
return NGX_ERROR;
}
path.data[path.len - 1] = '\0';
if (ngx_get_full_name(r->pool, (ngx_str_t *) &ngx_cycle->prefix, &path)
!= NGX_OK)
{
return NGX_ERROR;
}
}
#if (NGX_HAVE_MAX_PATH)
real = buffer;
#else
real = NULL;
#endif
real = ngx_realpath(path.data, real);
if (real == NULL) {
ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,
ngx_realpath_n " \"%s\" failed", path.data);
return NGX_ERROR;
}
len = ngx_strlen(real);
v->data = ngx_pnalloc(r->pool, len);
if (v->data == NULL) {
#if !(NGX_HAVE_MAX_PATH)
ngx_free(real);
#endif
return NGX_ERROR;
}
v->len = len;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
ngx_memcpy(v->data, real, len);
#if !(NGX_HAVE_MAX_PATH)
ngx_free(real);
#endif
return NGX_OK;
}
static ngx_int_t
ngx_http_variable_request_filename(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
size_t root;
ngx_str_t path;
if (ngx_http_map_uri_to_path(r, &path, &root, 0) == NULL) {
return NGX_ERROR;
}
/* ngx_http_map_uri_to_path() allocates memory for terminating '\0' */
v->len = path.len - 1;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = path.data;
return NGX_OK;
}
static ngx_int_t
ngx_http_variable_server_name(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
ngx_http_core_srv_conf_t *cscf;
cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
v->len = cscf->server_name.len;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = cscf->server_name.data;
return NGX_OK;
}
static ngx_int_t
ngx_http_variable_request_method(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
if (r->main->method_name.data) {
v->len = r->main->method_name.len;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = r->main->method_name.data;
} else {
v->not_found = 1;
}
return NGX_OK;
}
static ngx_int_t
ngx_http_variable_remote_user(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
ngx_int_t rc;
rc = ngx_http_auth_basic_user(r);
if (rc == NGX_DECLINED) {
v->not_found = 1;
return NGX_OK;
}
if (rc == NGX_ERROR) {
return NGX_ERROR;
}
v->len = r->headers_in.user.len;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = r->headers_in.user.data;
return NGX_OK;
}
static ngx_int_t
ngx_http_variable_bytes_sent(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
u_char *p;
p = ngx_pnalloc(r->pool, NGX_OFF_T_LEN);
if (p == NULL) {
return NGX_ERROR;
}
v->len = ngx_sprintf(p, "%O", r->connection->sent) - p;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = p;
return NGX_OK;
}
static ngx_int_t
ngx_http_variable_body_bytes_sent(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
off_t sent;
u_char *p;
sent = r->connection->sent - r->header_size;
if (sent < 0) {
sent = 0;
}
p = ngx_pnalloc(r->pool, NGX_OFF_T_LEN);
if (p == NULL) {
return NGX_ERROR;
}
v->len = ngx_sprintf(p, "%O", sent) - p;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = p;
return NGX_OK;
}
static ngx_int_t
ngx_http_variable_pipe(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
v->data = (u_char *) (r->pipeline ? "p" : ".");
v->len = 1;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
return NGX_OK;
}
static ngx_int_t
ngx_http_variable_status(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
ngx_uint_t status;
v->data = ngx_pnalloc(r->pool, NGX_INT_T_LEN);
if (v->data == NULL) {
return NGX_ERROR;
}
if (r->err_status) {
status = r->err_status;
} else if (r->headers_out.status) {
status = r->headers_out.status;
} else if (r->http_version == NGX_HTTP_VERSION_9) {
status = 9;
} else {
status = 0;
}
v->len = ngx_sprintf(v->data, "%03ui", status) - v->data;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
return NGX_OK;
}
static ngx_int_t
ngx_http_variable_sent_content_type(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
if (r->headers_out.content_type.len) {
v->len = r->headers_out.content_type.len;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = r->headers_out.content_type.data;
} else {
v->not_found = 1;
}
return NGX_OK;
}
static ngx_int_t
ngx_http_variable_sent_content_length(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
u_char *p;
if (r->headers_out.content_length) {
v->len = r->headers_out.content_length->value.len;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = r->headers_out.content_length->value.data;
return NGX_OK;
}
if (r->headers_out.content_length_n >= 0) {
p = ngx_pnalloc(r->pool, NGX_OFF_T_LEN);
if (p == NULL) {
return NGX_ERROR;
}
v->len = ngx_sprintf(p, "%O", r->headers_out.content_length_n) - p;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = p;
return NGX_OK;
}
v->not_found = 1;
return NGX_OK;
}
static ngx_int_t
ngx_http_variable_sent_location(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
ngx_str_t name;
if (r->headers_out.location) {
v->len = r->headers_out.location->value.len;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = r->headers_out.location->value.data;
return NGX_OK;
}
ngx_str_set(&name, "sent_http_location");
return ngx_http_variable_unknown_header(v, &name,
&r->headers_out.headers.part,
sizeof("sent_http_") - 1);
}
static ngx_int_t
ngx_http_variable_sent_last_modified(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
u_char *p;
if (r->headers_out.last_modified) {
v->len = r->headers_out.last_modified->value.len;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = r->headers_out.last_modified->value.data;
return NGX_OK;
}
if (r->headers_out.last_modified_time >= 0) {
p = ngx_pnalloc(r->pool, sizeof("Mon, 28 Sep 1970 06:00:00 GMT") - 1);
if (p == NULL) {
return NGX_ERROR;
}
v->len = ngx_http_time(p, r->headers_out.last_modified_time) - p;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = p;
return NGX_OK;
}
v->not_found = 1;
return NGX_OK;
}
static ngx_int_t
ngx_http_variable_sent_connection(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
size_t len;
char *p;
if (r->headers_out.status == NGX_HTTP_SWITCHING_PROTOCOLS) {
len = sizeof("upgrade") - 1;
p = "upgrade";
} else if (r->keepalive) {
len = sizeof("keep-alive") - 1;
p = "keep-alive";
} else {
len = sizeof("close") - 1;
p = "close";
}
v->len = len;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = (u_char *) p;
return NGX_OK;
}
static ngx_int_t
ngx_http_variable_sent_keep_alive(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
u_char *p;
ngx_http_core_loc_conf_t *clcf;
if (r->keepalive) {
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
if (clcf->keepalive_header) {
p = ngx_pnalloc(r->pool, sizeof("timeout=") - 1 + NGX_TIME_T_LEN);
if (p == NULL) {
return NGX_ERROR;
}
v->len = ngx_sprintf(p, "timeout=%T", clcf->keepalive_header) - p;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = p;
return NGX_OK;
}
}
v->not_found = 1;
return NGX_OK;
}
static ngx_int_t
ngx_http_variable_sent_transfer_encoding(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
if (r->chunked) {
v->len = sizeof("chunked") - 1;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = (u_char *) "chunked";
} else {
v->not_found = 1;
}
return NGX_OK;
}
static ngx_int_t
ngx_http_variable_request_completion(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
if (r->request_complete) {
v->len = 2;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = (u_char *) "OK";
return NGX_OK;
}
*v = ngx_http_variable_null_value;
return NGX_OK;
}
static ngx_int_t
ngx_http_variable_request_body(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
u_char *p;
size_t len;
ngx_buf_t *buf;
ngx_chain_t *cl;
if (r->request_body == NULL
|| r->request_body->bufs == NULL
|| r->request_body->temp_file)
{
v->not_found = 1;
return NGX_OK;
}
cl = r->request_body->bufs;
buf = cl->buf;
if (cl->next == NULL) {
v->len = buf->last - buf->pos;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = buf->pos;
return NGX_OK;
}
len = buf->last - buf->pos;
cl = cl->next;
for ( /* void */ ; cl; cl = cl->next) {
buf = cl->buf;
len += buf->last - buf->pos;
}
p = ngx_pnalloc(r->pool, len);
if (p == NULL) {
return NGX_ERROR;
}
v->data = p;
cl = r->request_body->bufs;
for ( /* void */ ; cl; cl = cl->next) {
buf = cl->buf;
p = ngx_cpymem(p, buf->pos, buf->last - buf->pos);
}
v->len = len;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
return NGX_OK;
}
static ngx_int_t
ngx_http_variable_request_body_file(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
if (r->request_body == NULL || r->request_body->temp_file == NULL) {
v->not_found = 1;
return NGX_OK;
}
v->len = r->request_body->temp_file->file.name.len;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = r->request_body->temp_file->file.name.data;
return NGX_OK;
}
static ngx_int_t
ngx_http_variable_request_length(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
u_char *p;
p = ngx_pnalloc(r->pool, NGX_OFF_T_LEN);
if (p == NULL) {
return NGX_ERROR;
}
v->len = ngx_sprintf(p, "%O", r->request_length) - p;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = p;
return NGX_OK;
}
static ngx_int_t
ngx_http_variable_request_time(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
u_char *p;
ngx_time_t *tp;
ngx_msec_int_t ms;
p = ngx_pnalloc(r->pool, NGX_TIME_T_LEN + 4);
if (p == NULL) {
return NGX_ERROR;
}
tp = ngx_timeofday();
ms = (ngx_msec_int_t)
((tp->sec - r->start_sec) * 1000 + (tp->msec - r->start_msec));
ms = ngx_max(ms, 0);
v->len = ngx_sprintf(p, "%T.%03M", (time_t) ms / 1000, ms % 1000) - p;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = p;
return NGX_OK;
}
static ngx_int_t
ngx_http_variable_request_id(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
u_char *id;
#if (NGX_OPENSSL)
u_char random_bytes[16];
#endif
id = ngx_pnalloc(r->pool, 32);
if (id == NULL) {
return NGX_ERROR;
}
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->len = 32;
v->data = id;
#if (NGX_OPENSSL)
if (RAND_bytes(random_bytes, 16) == 1) {
ngx_hex_dump(id, random_bytes, 16);
return NGX_OK;
}
ngx_ssl_error(NGX_LOG_ERR, r->connection->log, 0, "RAND_bytes() failed");
#endif
ngx_sprintf(id, "%08xD%08xD%08xD%08xD",
(uint32_t) ngx_random(), (uint32_t) ngx_random(),
(uint32_t) ngx_random(), (uint32_t) ngx_random());
return NGX_OK;
}
static ngx_int_t
ngx_http_variable_connection(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
u_char *p;
p = ngx_pnalloc(r->pool, NGX_ATOMIC_T_LEN);
if (p == NULL) {
return NGX_ERROR;
}
v->len = ngx_sprintf(p, "%uA", r->connection->number) - p;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = p;
return NGX_OK;
}
static ngx_int_t
ngx_http_variable_connection_requests(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
u_char *p;
p = ngx_pnalloc(r->pool, NGX_INT_T_LEN);
if (p == NULL) {
return NGX_ERROR;
}
v->len = ngx_sprintf(p, "%ui", r->connection->requests) - p;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = p;
return NGX_OK;
}
static ngx_int_t
ngx_http_variable_nginx_version(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
v->len = sizeof(NGINX_VERSION) - 1;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = (u_char *) NGINX_VERSION;
return NGX_OK;
}
static ngx_int_t
ngx_http_variable_hostname(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
v->len = ngx_cycle->hostname.len;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = ngx_cycle->hostname.data;
return NGX_OK;
}
static ngx_int_t
ngx_http_variable_pid(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
u_char *p;
p = ngx_pnalloc(r->pool, NGX_INT64_LEN);
if (p == NULL) {
return NGX_ERROR;
}
v->len = ngx_sprintf(p, "%P", ngx_pid) - p;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = p;
return NGX_OK;
}
static ngx_int_t
ngx_http_variable_msec(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
u_char *p;
ngx_time_t *tp;
p = ngx_pnalloc(r->pool, NGX_TIME_T_LEN + 4);
if (p == NULL) {
return NGX_ERROR;
}
tp = ngx_timeofday();
v->len = ngx_sprintf(p, "%T.%03M", tp->sec, tp->msec) - p;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = p;
return NGX_OK;
}
static ngx_int_t
ngx_http_variable_time_iso8601(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
u_char *p;
p = ngx_pnalloc(r->pool, ngx_cached_http_log_iso8601.len);
if (p == NULL) {
return NGX_ERROR;
}
ngx_memcpy(p, ngx_cached_http_log_iso8601.data,
ngx_cached_http_log_iso8601.len);
v->len = ngx_cached_http_log_iso8601.len;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = p;
return NGX_OK;
}
static ngx_int_t
ngx_http_variable_time_local(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
u_char *p;
p = ngx_pnalloc(r->pool, ngx_cached_http_log_time.len);
if (p == NULL) {
return NGX_ERROR;
}
ngx_memcpy(p, ngx_cached_http_log_time.data, ngx_cached_http_log_time.len);
v->len = ngx_cached_http_log_time.len;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = p;
return NGX_OK;
}
void *
ngx_http_map_find(ngx_http_request_t *r, ngx_http_map_t *map, ngx_str_t *match)
{
void *value;
u_char *low;
size_t len;
ngx_uint_t key;
len = match->len;
if (len) {
low = ngx_pnalloc(r->pool, len);
if (low == NULL) {
return NULL;
}
} else {
low = NULL;
}
key = ngx_hash_strlow(low, match->data, len);
value = ngx_hash_find_combined(&map->hash, key, low, len);
if (value) {
return value;
}
#if (NGX_PCRE)
if (len && map->nregex) {
ngx_int_t n;
ngx_uint_t i;
ngx_http_map_regex_t *reg;
reg = map->regex;
for (i = 0; i < map->nregex; i++) {
n = ngx_http_regex_exec(r, reg[i].regex, match);
if (n == NGX_OK) {
return reg[i].value;
}
if (n == NGX_DECLINED) {
continue;
}
/* NGX_ERROR */
return NULL;
}
}
#endif
return NULL;
}
#if (NGX_PCRE)
static ngx_int_t
ngx_http_variable_not_found(ngx_http_request_t *r, ngx_http_variable_value_t *v,
uintptr_t data)
{
v->not_found = 1;
return NGX_OK;
}
ngx_http_regex_t *
ngx_http_regex_compile(ngx_conf_t *cf, ngx_regex_compile_t *rc)
{
u_char *p;
size_t size;
ngx_str_t name;
ngx_uint_t i, n;
ngx_http_variable_t *v;
ngx_http_regex_t *re;
ngx_http_regex_variable_t *rv;
ngx_http_core_main_conf_t *cmcf;
rc->pool = cf->pool;
if (ngx_regex_compile(rc) != NGX_OK) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%V", &rc->err);
return NULL;
}
re = ngx_pcalloc(cf->pool, sizeof(ngx_http_regex_t));
if (re == NULL) {
return NULL;
}
re->regex = rc->regex;
re->ncaptures = rc->captures;
re->name = rc->pattern;
cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
cmcf->ncaptures = ngx_max(cmcf->ncaptures, re->ncaptures);
n = (ngx_uint_t) rc->named_captures;
if (n == 0) {
return re;
}
rv = ngx_palloc(rc->pool, n * sizeof(ngx_http_regex_variable_t));
if (rv == NULL) {
return NULL;
}
re->variables = rv;
re->nvariables = n;
size = rc->name_size;
p = rc->names;
for (i = 0; i < n; i++) {
rv[i].capture = 2 * ((p[0] << 8) + p[1]);
name.data = &p[2];
name.len = ngx_strlen(name.data);
v = ngx_http_add_variable(cf, &name, NGX_HTTP_VAR_CHANGEABLE);
if (v == NULL) {
return NULL;
}
rv[i].index = ngx_http_get_variable_index(cf, &name);
if (rv[i].index == NGX_ERROR) {
return NULL;
}
v->get_handler = ngx_http_variable_not_found;
p += size;
}
return re;
}
ngx_int_t
ngx_http_regex_exec(ngx_http_request_t *r, ngx_http_regex_t *re, ngx_str_t *s)
{
ngx_int_t rc, index;
ngx_uint_t i, n, len;
ngx_http_variable_value_t *vv;
ngx_http_core_main_conf_t *cmcf;
cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
if (re->ncaptures) {
len = cmcf->ncaptures;
if (r->captures == NULL) {
r->captures = ngx_palloc(r->pool, len * sizeof(int));
if (r->captures == NULL) {
return NGX_ERROR;
}
}
} else {
len = 0;
}
rc = ngx_regex_exec(re->regex, s, r->captures, len);
if (rc == NGX_REGEX_NO_MATCHED) {
return NGX_DECLINED;
}
if (rc < 0) {
ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
ngx_regex_exec_n " failed: %i on \"%V\" using \"%V\"",
rc, s, &re->name);
return NGX_ERROR;
}
for (i = 0; i < re->nvariables; i++) {
n = re->variables[i].capture;
index = re->variables[i].index;
vv = &r->variables[index];
vv->len = r->captures[n + 1] - r->captures[n];
vv->valid = 1;
vv->no_cacheable = 0;
vv->not_found = 0;
vv->data = &s->data[r->captures[n]];
#if (NGX_DEBUG)
{
ngx_http_variable_t *v;
v = cmcf->variables.elts;
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http regex set $%V to \"%v\"", &v[index].name, vv);
}
#endif
}
r->ncaptures = rc * 2;
r->captures_data = s->data;
return NGX_OK;
}
#endif
ngx_int_t
ngx_http_variables_add_core_vars(ngx_conf_t *cf)
{
ngx_http_variable_t *cv, *v;
ngx_http_core_main_conf_t *cmcf;
cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
cmcf->variables_keys = ngx_pcalloc(cf->temp_pool,
sizeof(ngx_hash_keys_arrays_t));
if (cmcf->variables_keys == NULL) {
return NGX_ERROR;
}
cmcf->variables_keys->pool = cf->pool;
cmcf->variables_keys->temp_pool = cf->pool;
if (ngx_hash_keys_array_init(cmcf->variables_keys, NGX_HASH_SMALL)
!= NGX_OK)
{
return NGX_ERROR;
}
if (ngx_array_init(&cmcf->prefix_variables, cf->pool, 8,
sizeof(ngx_http_variable_t))
!= NGX_OK)
{
return NGX_ERROR;
}
for (cv = ngx_http_core_variables; cv->name.len; cv++) {
v = ngx_http_add_variable(cf, &cv->name, cv->flags);
if (v == NULL) {
return NGX_ERROR;
}
*v = *cv;
}
return NGX_OK;
}
ngx_int_t
ngx_http_variables_init_vars(ngx_conf_t *cf)
{
size_t len;
ngx_uint_t i, n;
ngx_hash_key_t *key;
ngx_hash_init_t hash;
ngx_http_variable_t *v, *av, *pv;
ngx_http_core_main_conf_t *cmcf;
/* set the handlers for the indexed http variables */
cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
v = cmcf->variables.elts;
pv = cmcf->prefix_variables.elts;
key = cmcf->variables_keys->keys.elts;
for (i = 0; i < cmcf->variables.nelts; i++) {
for (n = 0; n < cmcf->variables_keys->keys.nelts; n++) {
av = key[n].value;
if (v[i].name.len == key[n].key.len
&& ngx_strncmp(v[i].name.data, key[n].key.data, v[i].name.len)
== 0)
{
v[i].get_handler = av->get_handler;
v[i].data = av->data;
av->flags |= NGX_HTTP_VAR_INDEXED;
v[i].flags = av->flags;
av->index = i;
if (av->get_handler == NULL
|| (av->flags & NGX_HTTP_VAR_WEAK))
{
break;
}
goto next;
}
}
len = 0;
av = NULL;
for (n = 0; n < cmcf->prefix_variables.nelts; n++) {
if (v[i].name.len >= pv[n].name.len && v[i].name.len > len
&& ngx_strncmp(v[i].name.data, pv[n].name.data, pv[n].name.len)
== 0)
{
av = &pv[n];
len = pv[n].name.len;
}
}
if (av) {
v[i].get_handler = av->get_handler;
v[i].data = (uintptr_t) &v[i].name;
v[i].flags = av->flags;
goto next;
}
if (v[i].get_handler == NULL) {
ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
"unknown \"%V\" variable", &v[i].name);
return NGX_ERROR;
}
next:
continue;
}
for (n = 0; n < cmcf->variables_keys->keys.nelts; n++) {
av = key[n].value;
if (av->flags & NGX_HTTP_VAR_NOHASH) {
key[n].key.data = NULL;
}
}
hash.hash = &cmcf->variables_hash;
hash.key = ngx_hash_key;
hash.max_size = cmcf->variables_hash_max_size;
hash.bucket_size = cmcf->variables_hash_bucket_size;
hash.name = "variables_hash";
hash.pool = cf->pool;
hash.temp_pool = NULL;
if (ngx_hash_init(&hash, cmcf->variables_keys->keys.elts,
cmcf->variables_keys->keys.nelts)
!= NGX_OK)
{
return NGX_ERROR;
}
cmcf->variables_keys = NULL;
return NGX_OK;
}
nginx-1.14.0/src/http/ngx_http_upstream.c 000644 001751 001751 00000500360 13265410474 021605 0 ustar 00mdounin mdounin 000000 000000
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#include
#include
#include
#if (NGX_HTTP_CACHE)
static ngx_int_t ngx_http_upstream_cache(ngx_http_request_t *r,
ngx_http_upstream_t *u);
static ngx_int_t ngx_http_upstream_cache_get(ngx_http_request_t *r,
ngx_http_upstream_t *u, ngx_http_file_cache_t **cache);
static ngx_int_t ngx_http_upstream_cache_send(ngx_http_request_t *r,
ngx_http_upstream_t *u);
static ngx_int_t ngx_http_upstream_cache_background_update(
ngx_http_request_t *r, ngx_http_upstream_t *u);
static ngx_int_t ngx_http_upstream_cache_check_range(ngx_http_request_t *r,
ngx_http_upstream_t *u);
static ngx_int_t ngx_http_upstream_cache_status(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_upstream_cache_last_modified(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_upstream_cache_etag(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
#endif
static void ngx_http_upstream_init_request(ngx_http_request_t *r);
static void ngx_http_upstream_resolve_handler(ngx_resolver_ctx_t *ctx);
static void ngx_http_upstream_rd_check_broken_connection(ngx_http_request_t *r);
static void ngx_http_upstream_wr_check_broken_connection(ngx_http_request_t *r);
static void ngx_http_upstream_check_broken_connection(ngx_http_request_t *r,
ngx_event_t *ev);
static void ngx_http_upstream_connect(ngx_http_request_t *r,
ngx_http_upstream_t *u);
static ngx_int_t ngx_http_upstream_reinit(ngx_http_request_t *r,
ngx_http_upstream_t *u);
static void ngx_http_upstream_send_request(ngx_http_request_t *r,
ngx_http_upstream_t *u, ngx_uint_t do_write);
static ngx_int_t ngx_http_upstream_send_request_body(ngx_http_request_t *r,
ngx_http_upstream_t *u, ngx_uint_t do_write);
static void ngx_http_upstream_send_request_handler(ngx_http_request_t *r,
ngx_http_upstream_t *u);
static void ngx_http_upstream_read_request_handler(ngx_http_request_t *r);
static void ngx_http_upstream_process_header(ngx_http_request_t *r,
ngx_http_upstream_t *u);
static ngx_int_t ngx_http_upstream_test_next(ngx_http_request_t *r,
ngx_http_upstream_t *u);
static ngx_int_t ngx_http_upstream_intercept_errors(ngx_http_request_t *r,
ngx_http_upstream_t *u);
static ngx_int_t ngx_http_upstream_test_connect(ngx_connection_t *c);
static ngx_int_t ngx_http_upstream_process_headers(ngx_http_request_t *r,
ngx_http_upstream_t *u);
static ngx_int_t ngx_http_upstream_process_trailers(ngx_http_request_t *r,
ngx_http_upstream_t *u);
static void ngx_http_upstream_send_response(ngx_http_request_t *r,
ngx_http_upstream_t *u);
static void ngx_http_upstream_upgrade(ngx_http_request_t *r,
ngx_http_upstream_t *u);
static void ngx_http_upstream_upgraded_read_downstream(ngx_http_request_t *r);
static void ngx_http_upstream_upgraded_write_downstream(ngx_http_request_t *r);
static void ngx_http_upstream_upgraded_read_upstream(ngx_http_request_t *r,
ngx_http_upstream_t *u);
static void ngx_http_upstream_upgraded_write_upstream(ngx_http_request_t *r,
ngx_http_upstream_t *u);
static void ngx_http_upstream_process_upgraded(ngx_http_request_t *r,
ngx_uint_t from_upstream, ngx_uint_t do_write);
static void
ngx_http_upstream_process_non_buffered_downstream(ngx_http_request_t *r);
static void
ngx_http_upstream_process_non_buffered_upstream(ngx_http_request_t *r,
ngx_http_upstream_t *u);
static void
ngx_http_upstream_process_non_buffered_request(ngx_http_request_t *r,
ngx_uint_t do_write);
static ngx_int_t ngx_http_upstream_non_buffered_filter_init(void *data);
static ngx_int_t ngx_http_upstream_non_buffered_filter(void *data,
ssize_t bytes);
#if (NGX_THREADS)
static ngx_int_t ngx_http_upstream_thread_handler(ngx_thread_task_t *task,
ngx_file_t *file);
static void ngx_http_upstream_thread_event_handler(ngx_event_t *ev);
#endif
static ngx_int_t ngx_http_upstream_output_filter(void *data,
ngx_chain_t *chain);
static void ngx_http_upstream_process_downstream(ngx_http_request_t *r);
static void ngx_http_upstream_process_upstream(ngx_http_request_t *r,
ngx_http_upstream_t *u);
static void ngx_http_upstream_process_request(ngx_http_request_t *r,
ngx_http_upstream_t *u);
static void ngx_http_upstream_store(ngx_http_request_t *r,
ngx_http_upstream_t *u);
static void ngx_http_upstream_dummy_handler(ngx_http_request_t *r,
ngx_http_upstream_t *u);
static void ngx_http_upstream_next(ngx_http_request_t *r,
ngx_http_upstream_t *u, ngx_uint_t ft_type);
static void ngx_http_upstream_cleanup(void *data);
static void ngx_http_upstream_finalize_request(ngx_http_request_t *r,
ngx_http_upstream_t *u, ngx_int_t rc);
static ngx_int_t ngx_http_upstream_process_header_line(ngx_http_request_t *r,
ngx_table_elt_t *h, ngx_uint_t offset);
static ngx_int_t ngx_http_upstream_process_content_length(ngx_http_request_t *r,
ngx_table_elt_t *h, ngx_uint_t offset);
static ngx_int_t ngx_http_upstream_process_last_modified(ngx_http_request_t *r,
ngx_table_elt_t *h, ngx_uint_t offset);
static ngx_int_t ngx_http_upstream_process_set_cookie(ngx_http_request_t *r,
ngx_table_elt_t *h, ngx_uint_t offset);
static ngx_int_t
ngx_http_upstream_process_cache_control(ngx_http_request_t *r,
ngx_table_elt_t *h, ngx_uint_t offset);
static ngx_int_t ngx_http_upstream_ignore_header_line(ngx_http_request_t *r,
ngx_table_elt_t *h, ngx_uint_t offset);
static ngx_int_t ngx_http_upstream_process_expires(ngx_http_request_t *r,
ngx_table_elt_t *h, ngx_uint_t offset);
static ngx_int_t ngx_http_upstream_process_accel_expires(ngx_http_request_t *r,
ngx_table_elt_t *h, ngx_uint_t offset);
static ngx_int_t ngx_http_upstream_process_limit_rate(ngx_http_request_t *r,
ngx_table_elt_t *h, ngx_uint_t offset);
static ngx_int_t ngx_http_upstream_process_buffering(ngx_http_request_t *r,
ngx_table_elt_t *h, ngx_uint_t offset);
static ngx_int_t ngx_http_upstream_process_charset(ngx_http_request_t *r,
ngx_table_elt_t *h, ngx_uint_t offset);
static ngx_int_t ngx_http_upstream_process_connection(ngx_http_request_t *r,
ngx_table_elt_t *h, ngx_uint_t offset);
static ngx_int_t
ngx_http_upstream_process_transfer_encoding(ngx_http_request_t *r,
ngx_table_elt_t *h, ngx_uint_t offset);
static ngx_int_t ngx_http_upstream_process_vary(ngx_http_request_t *r,
ngx_table_elt_t *h, ngx_uint_t offset);
static ngx_int_t ngx_http_upstream_copy_header_line(ngx_http_request_t *r,
ngx_table_elt_t *h, ngx_uint_t offset);
static ngx_int_t
ngx_http_upstream_copy_multi_header_lines(ngx_http_request_t *r,
ngx_table_elt_t *h, ngx_uint_t offset);
static ngx_int_t ngx_http_upstream_copy_content_type(ngx_http_request_t *r,
ngx_table_elt_t *h, ngx_uint_t offset);
static ngx_int_t ngx_http_upstream_copy_last_modified(ngx_http_request_t *r,
ngx_table_elt_t *h, ngx_uint_t offset);
static ngx_int_t ngx_http_upstream_rewrite_location(ngx_http_request_t *r,
ngx_table_elt_t *h, ngx_uint_t offset);
static ngx_int_t ngx_http_upstream_rewrite_refresh(ngx_http_request_t *r,
ngx_table_elt_t *h, ngx_uint_t offset);
static ngx_int_t ngx_http_upstream_rewrite_set_cookie(ngx_http_request_t *r,
ngx_table_elt_t *h, ngx_uint_t offset);
static ngx_int_t ngx_http_upstream_copy_allow_ranges(ngx_http_request_t *r,
ngx_table_elt_t *h, ngx_uint_t offset);
#if (NGX_HTTP_GZIP)
static ngx_int_t ngx_http_upstream_copy_content_encoding(ngx_http_request_t *r,
ngx_table_elt_t *h, ngx_uint_t offset);
#endif
static ngx_int_t ngx_http_upstream_add_variables(ngx_conf_t *cf);
static ngx_int_t ngx_http_upstream_addr_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_upstream_status_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_upstream_response_time_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_upstream_response_length_variable(
ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_upstream_header_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_upstream_trailer_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_upstream_cookie_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static char *ngx_http_upstream(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy);
static char *ngx_http_upstream_server(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static ngx_int_t ngx_http_upstream_set_local(ngx_http_request_t *r,
ngx_http_upstream_t *u, ngx_http_upstream_local_t *local);
static void *ngx_http_upstream_create_main_conf(ngx_conf_t *cf);
static char *ngx_http_upstream_init_main_conf(ngx_conf_t *cf, void *conf);
#if (NGX_HTTP_SSL)
static void ngx_http_upstream_ssl_init_connection(ngx_http_request_t *,
ngx_http_upstream_t *u, ngx_connection_t *c);
static void ngx_http_upstream_ssl_handshake_handler(ngx_connection_t *c);
static void ngx_http_upstream_ssl_handshake(ngx_http_request_t *,
ngx_http_upstream_t *u, ngx_connection_t *c);
static ngx_int_t ngx_http_upstream_ssl_name(ngx_http_request_t *r,
ngx_http_upstream_t *u, ngx_connection_t *c);
#endif
static ngx_http_upstream_header_t ngx_http_upstream_headers_in[] = {
{ ngx_string("Status"),
ngx_http_upstream_process_header_line,
offsetof(ngx_http_upstream_headers_in_t, status),
ngx_http_upstream_copy_header_line, 0, 0 },
{ ngx_string("Content-Type"),
ngx_http_upstream_process_header_line,
offsetof(ngx_http_upstream_headers_in_t, content_type),
ngx_http_upstream_copy_content_type, 0, 1 },
{ ngx_string("Content-Length"),
ngx_http_upstream_process_content_length, 0,
ngx_http_upstream_ignore_header_line, 0, 0 },
{ ngx_string("Date"),
ngx_http_upstream_process_header_line,
offsetof(ngx_http_upstream_headers_in_t, date),
ngx_http_upstream_copy_header_line,
offsetof(ngx_http_headers_out_t, date), 0 },
{ ngx_string("Last-Modified"),
ngx_http_upstream_process_last_modified, 0,
ngx_http_upstream_copy_last_modified, 0, 0 },
{ ngx_string("ETag"),
ngx_http_upstream_process_header_line,
offsetof(ngx_http_upstream_headers_in_t, etag),
ngx_http_upstream_copy_header_line,
offsetof(ngx_http_headers_out_t, etag), 0 },
{ ngx_string("Server"),
ngx_http_upstream_process_header_line,
offsetof(ngx_http_upstream_headers_in_t, server),
ngx_http_upstream_copy_header_line,
offsetof(ngx_http_headers_out_t, server), 0 },
{ ngx_string("WWW-Authenticate"),
ngx_http_upstream_process_header_line,
offsetof(ngx_http_upstream_headers_in_t, www_authenticate),
ngx_http_upstream_copy_header_line, 0, 0 },
{ ngx_string("Location"),
ngx_http_upstream_process_header_line,
offsetof(ngx_http_upstream_headers_in_t, location),
ngx_http_upstream_rewrite_location, 0, 0 },
{ ngx_string("Refresh"),
ngx_http_upstream_ignore_header_line, 0,
ngx_http_upstream_rewrite_refresh, 0, 0 },
{ ngx_string("Set-Cookie"),
ngx_http_upstream_process_set_cookie,
offsetof(ngx_http_upstream_headers_in_t, cookies),
ngx_http_upstream_rewrite_set_cookie, 0, 1 },
{ ngx_string("Content-Disposition"),
ngx_http_upstream_ignore_header_line, 0,
ngx_http_upstream_copy_header_line, 0, 1 },
{ ngx_string("Cache-Control"),
ngx_http_upstream_process_cache_control, 0,
ngx_http_upstream_copy_multi_header_lines,
offsetof(ngx_http_headers_out_t, cache_control), 1 },
{ ngx_string("Expires"),
ngx_http_upstream_process_expires, 0,
ngx_http_upstream_copy_header_line,
offsetof(ngx_http_headers_out_t, expires), 1 },
{ ngx_string("Accept-Ranges"),
ngx_http_upstream_process_header_line,
offsetof(ngx_http_upstream_headers_in_t, accept_ranges),
ngx_http_upstream_copy_allow_ranges,
offsetof(ngx_http_headers_out_t, accept_ranges), 1 },
{ ngx_string("Content-Range"),
ngx_http_upstream_ignore_header_line, 0,
ngx_http_upstream_copy_header_line,
offsetof(ngx_http_headers_out_t, content_range), 0 },
{ ngx_string("Connection"),
ngx_http_upstream_process_connection, 0,
ngx_http_upstream_ignore_header_line, 0, 0 },
{ ngx_string("Keep-Alive"),
ngx_http_upstream_ignore_header_line, 0,
ngx_http_upstream_ignore_header_line, 0, 0 },
{ ngx_string("Vary"),
ngx_http_upstream_process_vary, 0,
ngx_http_upstream_copy_header_line, 0, 0 },
{ ngx_string("Link"),
ngx_http_upstream_ignore_header_line, 0,
ngx_http_upstream_copy_multi_header_lines,
offsetof(ngx_http_headers_out_t, link), 0 },
{ ngx_string("X-Accel-Expires"),
ngx_http_upstream_process_accel_expires, 0,
ngx_http_upstream_copy_header_line, 0, 0 },
{ ngx_string("X-Accel-Redirect"),
ngx_http_upstream_process_header_line,
offsetof(ngx_http_upstream_headers_in_t, x_accel_redirect),
ngx_http_upstream_copy_header_line, 0, 0 },
{ ngx_string("X-Accel-Limit-Rate"),
ngx_http_upstream_process_limit_rate, 0,
ngx_http_upstream_copy_header_line, 0, 0 },
{ ngx_string("X-Accel-Buffering"),
ngx_http_upstream_process_buffering, 0,
ngx_http_upstream_copy_header_line, 0, 0 },
{ ngx_string("X-Accel-Charset"),
ngx_http_upstream_process_charset, 0,
ngx_http_upstream_copy_header_line, 0, 0 },
{ ngx_string("Transfer-Encoding"),
ngx_http_upstream_process_transfer_encoding, 0,
ngx_http_upstream_ignore_header_line, 0, 0 },
#if (NGX_HTTP_GZIP)
{ ngx_string("Content-Encoding"),
ngx_http_upstream_process_header_line,
offsetof(ngx_http_upstream_headers_in_t, content_encoding),
ngx_http_upstream_copy_content_encoding, 0, 0 },
#endif
{ ngx_null_string, NULL, 0, NULL, 0, 0 }
};
static ngx_command_t ngx_http_upstream_commands[] = {
{ ngx_string("upstream"),
NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE1,
ngx_http_upstream,
0,
0,
NULL },
{ ngx_string("server"),
NGX_HTTP_UPS_CONF|NGX_CONF_1MORE,
ngx_http_upstream_server,
NGX_HTTP_SRV_CONF_OFFSET,
0,
NULL },
ngx_null_command
};
static ngx_http_module_t ngx_http_upstream_module_ctx = {
ngx_http_upstream_add_variables, /* preconfiguration */
NULL, /* postconfiguration */
ngx_http_upstream_create_main_conf, /* create main configuration */
ngx_http_upstream_init_main_conf, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
NULL, /* create location configuration */
NULL /* merge location configuration */
};
ngx_module_t ngx_http_upstream_module = {
NGX_MODULE_V1,
&ngx_http_upstream_module_ctx, /* module context */
ngx_http_upstream_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static ngx_http_variable_t ngx_http_upstream_vars[] = {
{ ngx_string("upstream_addr"), NULL,
ngx_http_upstream_addr_variable, 0,
NGX_HTTP_VAR_NOCACHEABLE, 0 },
{ ngx_string("upstream_status"), NULL,
ngx_http_upstream_status_variable, 0,
NGX_HTTP_VAR_NOCACHEABLE, 0 },
{ ngx_string("upstream_connect_time"), NULL,
ngx_http_upstream_response_time_variable, 2,
NGX_HTTP_VAR_NOCACHEABLE, 0 },
{ ngx_string("upstream_header_time"), NULL,
ngx_http_upstream_response_time_variable, 1,
NGX_HTTP_VAR_NOCACHEABLE, 0 },
{ ngx_string("upstream_response_time"), NULL,
ngx_http_upstream_response_time_variable, 0,
NGX_HTTP_VAR_NOCACHEABLE, 0 },
{ ngx_string("upstream_response_length"), NULL,
ngx_http_upstream_response_length_variable, 0,
NGX_HTTP_VAR_NOCACHEABLE, 0 },
{ ngx_string("upstream_bytes_received"), NULL,
ngx_http_upstream_response_length_variable, 1,
NGX_HTTP_VAR_NOCACHEABLE, 0 },
#if (NGX_HTTP_CACHE)
{ ngx_string("upstream_cache_status"), NULL,
ngx_http_upstream_cache_status, 0,
NGX_HTTP_VAR_NOCACHEABLE, 0 },
{ ngx_string("upstream_cache_last_modified"), NULL,
ngx_http_upstream_cache_last_modified, 0,
NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
{ ngx_string("upstream_cache_etag"), NULL,
ngx_http_upstream_cache_etag, 0,
NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
#endif
{ ngx_string("upstream_http_"), NULL, ngx_http_upstream_header_variable,
0, NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_PREFIX, 0 },
{ ngx_string("upstream_trailer_"), NULL, ngx_http_upstream_trailer_variable,
0, NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_PREFIX, 0 },
{ ngx_string("upstream_cookie_"), NULL, ngx_http_upstream_cookie_variable,
0, NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_PREFIX, 0 },
ngx_http_null_variable
};
static ngx_http_upstream_next_t ngx_http_upstream_next_errors[] = {
{ 500, NGX_HTTP_UPSTREAM_FT_HTTP_500 },
{ 502, NGX_HTTP_UPSTREAM_FT_HTTP_502 },
{ 503, NGX_HTTP_UPSTREAM_FT_HTTP_503 },
{ 504, NGX_HTTP_UPSTREAM_FT_HTTP_504 },
{ 403, NGX_HTTP_UPSTREAM_FT_HTTP_403 },
{ 404, NGX_HTTP_UPSTREAM_FT_HTTP_404 },
{ 429, NGX_HTTP_UPSTREAM_FT_HTTP_429 },
{ 0, 0 }
};
ngx_conf_bitmask_t ngx_http_upstream_cache_method_mask[] = {
{ ngx_string("GET"), NGX_HTTP_GET },
{ ngx_string("HEAD"), NGX_HTTP_HEAD },
{ ngx_string("POST"), NGX_HTTP_POST },
{ ngx_null_string, 0 }
};
ngx_conf_bitmask_t ngx_http_upstream_ignore_headers_masks[] = {
{ ngx_string("X-Accel-Redirect"), NGX_HTTP_UPSTREAM_IGN_XA_REDIRECT },
{ ngx_string("X-Accel-Expires"), NGX_HTTP_UPSTREAM_IGN_XA_EXPIRES },
{ ngx_string("X-Accel-Limit-Rate"), NGX_HTTP_UPSTREAM_IGN_XA_LIMIT_RATE },
{ ngx_string("X-Accel-Buffering"), NGX_HTTP_UPSTREAM_IGN_XA_BUFFERING },
{ ngx_string("X-Accel-Charset"), NGX_HTTP_UPSTREAM_IGN_XA_CHARSET },
{ ngx_string("Expires"), NGX_HTTP_UPSTREAM_IGN_EXPIRES },
{ ngx_string("Cache-Control"), NGX_HTTP_UPSTREAM_IGN_CACHE_CONTROL },
{ ngx_string("Set-Cookie"), NGX_HTTP_UPSTREAM_IGN_SET_COOKIE },
{ ngx_string("Vary"), NGX_HTTP_UPSTREAM_IGN_VARY },
{ ngx_null_string, 0 }
};
ngx_int_t
ngx_http_upstream_create(ngx_http_request_t *r)
{
ngx_http_upstream_t *u;
u = r->upstream;
if (u && u->cleanup) {
r->main->count++;
ngx_http_upstream_cleanup(r);
}
u = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_t));
if (u == NULL) {
return NGX_ERROR;
}
r->upstream = u;
u->peer.log = r->connection->log;
u->peer.log_error = NGX_ERROR_ERR;
#if (NGX_HTTP_CACHE)
r->cache = NULL;
#endif
u->headers_in.content_length_n = -1;
u->headers_in.last_modified_time = -1;
return NGX_OK;
}
void
ngx_http_upstream_init(ngx_http_request_t *r)
{
ngx_connection_t *c;
c = r->connection;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
"http init upstream, client timer: %d", c->read->timer_set);
#if (NGX_HTTP_V2)
if (r->stream) {
ngx_http_upstream_init_request(r);
return;
}
#endif
if (c->read->timer_set) {
ngx_del_timer(c->read);
}
if (ngx_event_flags & NGX_USE_CLEAR_EVENT) {
if (!c->write->active) {
if (ngx_add_event(c->write, NGX_WRITE_EVENT, NGX_CLEAR_EVENT)
== NGX_ERROR)
{
ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
}
}
}
ngx_http_upstream_init_request(r);
}
static void
ngx_http_upstream_init_request(ngx_http_request_t *r)
{
ngx_str_t *host;
ngx_uint_t i;
ngx_resolver_ctx_t *ctx, temp;
ngx_http_cleanup_t *cln;
ngx_http_upstream_t *u;
ngx_http_core_loc_conf_t *clcf;
ngx_http_upstream_srv_conf_t *uscf, **uscfp;
ngx_http_upstream_main_conf_t *umcf;
if (r->aio) {
return;
}
u = r->upstream;
#if (NGX_HTTP_CACHE)
if (u->conf->cache) {
ngx_int_t rc;
rc = ngx_http_upstream_cache(r, u);
if (rc == NGX_BUSY) {
r->write_event_handler = ngx_http_upstream_init_request;
return;
}
r->write_event_handler = ngx_http_request_empty_handler;
if (rc == NGX_ERROR) {
ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
}
if (rc == NGX_OK) {
rc = ngx_http_upstream_cache_send(r, u);
if (rc == NGX_DONE) {
return;
}
if (rc == NGX_HTTP_UPSTREAM_INVALID_HEADER) {
rc = NGX_DECLINED;
r->cached = 0;
u->buffer.start = NULL;
u->cache_status = NGX_HTTP_CACHE_MISS;
u->request_sent = 1;
}
if (ngx_http_upstream_cache_background_update(r, u) != NGX_OK) {
rc = NGX_ERROR;
}
}
if (rc != NGX_DECLINED) {
ngx_http_finalize_request(r, rc);
return;
}
}
#endif
u->store = u->conf->store;
if (!u->store && !r->post_action && !u->conf->ignore_client_abort) {
r->read_event_handler = ngx_http_upstream_rd_check_broken_connection;
r->write_event_handler = ngx_http_upstream_wr_check_broken_connection;
}
if (r->request_body) {
u->request_bufs = r->request_body->bufs;
}
if (u->create_request(r) != NGX_OK) {
ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
}
if (ngx_http_upstream_set_local(r, u, u->conf->local) != NGX_OK) {
ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
}
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
u->output.alignment = clcf->directio_alignment;
u->output.pool = r->pool;
u->output.bufs.num = 1;
u->output.bufs.size = clcf->client_body_buffer_size;
if (u->output.output_filter == NULL) {
u->output.output_filter = ngx_chain_writer;
u->output.filter_ctx = &u->writer;
}
u->writer.pool = r->pool;
if (r->upstream_states == NULL) {
r->upstream_states = ngx_array_create(r->pool, 1,
sizeof(ngx_http_upstream_state_t));
if (r->upstream_states == NULL) {
ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
}
} else {
u->state = ngx_array_push(r->upstream_states);
if (u->state == NULL) {
ngx_http_upstream_finalize_request(r, u,
NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
}
ngx_memzero(u->state, sizeof(ngx_http_upstream_state_t));
}
cln = ngx_http_cleanup_add(r, 0);
if (cln == NULL) {
ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
}
cln->handler = ngx_http_upstream_cleanup;
cln->data = r;
u->cleanup = &cln->handler;
if (u->resolved == NULL) {
uscf = u->conf->upstream;
} else {
#if (NGX_HTTP_SSL)
u->ssl_name = u->resolved->host;
#endif
host = &u->resolved->host;
umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module);
uscfp = umcf->upstreams.elts;
for (i = 0; i < umcf->upstreams.nelts; i++) {
uscf = uscfp[i];
if (uscf->host.len == host->len
&& ((uscf->port == 0 && u->resolved->no_port)
|| uscf->port == u->resolved->port)
&& ngx_strncasecmp(uscf->host.data, host->data, host->len) == 0)
{
goto found;
}
}
if (u->resolved->sockaddr) {
if (u->resolved->port == 0
&& u->resolved->sockaddr->sa_family != AF_UNIX)
{
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"no port in upstream \"%V\"", host);
ngx_http_upstream_finalize_request(r, u,
NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
}
if (ngx_http_upstream_create_round_robin_peer(r, u->resolved)
!= NGX_OK)
{
ngx_http_upstream_finalize_request(r, u,
NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
}
ngx_http_upstream_connect(r, u);
return;
}
if (u->resolved->port == 0) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"no port in upstream \"%V\"", host);
ngx_http_upstream_finalize_request(r, u,
NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
}
temp.name = *host;
ctx = ngx_resolve_start(clcf->resolver, &temp);
if (ctx == NULL) {
ngx_http_upstream_finalize_request(r, u,
NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
}
if (ctx == NGX_NO_RESOLVER) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"no resolver defined to resolve %V", host);
ngx_http_upstream_finalize_request(r, u, NGX_HTTP_BAD_GATEWAY);
return;
}
ctx->name = *host;
ctx->handler = ngx_http_upstream_resolve_handler;
ctx->data = r;
ctx->timeout = clcf->resolver_timeout;
u->resolved->ctx = ctx;
if (ngx_resolve_name(ctx) != NGX_OK) {
u->resolved->ctx = NULL;
ngx_http_upstream_finalize_request(r, u,
NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
}
return;
}
found:
if (uscf == NULL) {
ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
"no upstream configuration");
ngx_http_upstream_finalize_request(r, u,
NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
}
u->upstream = uscf;
#if (NGX_HTTP_SSL)
u->ssl_name = uscf->host;
#endif
if (uscf->peer.init(r, uscf) != NGX_OK) {
ngx_http_upstream_finalize_request(r, u,
NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
}
u->peer.start_time = ngx_current_msec;
if (u->conf->next_upstream_tries
&& u->peer.tries > u->conf->next_upstream_tries)
{
u->peer.tries = u->conf->next_upstream_tries;
}
ngx_http_upstream_connect(r, u);
}
#if (NGX_HTTP_CACHE)
static ngx_int_t
ngx_http_upstream_cache(ngx_http_request_t *r, ngx_http_upstream_t *u)
{
ngx_int_t rc;
ngx_http_cache_t *c;
ngx_http_file_cache_t *cache;
c = r->cache;
if (c == NULL) {
if (!(r->method & u->conf->cache_methods)) {
return NGX_DECLINED;
}
rc = ngx_http_upstream_cache_get(r, u, &cache);
if (rc != NGX_OK) {
return rc;
}
if (r->method == NGX_HTTP_HEAD && u->conf->cache_convert_head) {
u->method = ngx_http_core_get_method;
}
if (ngx_http_file_cache_new(r) != NGX_OK) {
return NGX_ERROR;
}
if (u->create_key(r) != NGX_OK) {
return NGX_ERROR;
}
/* TODO: add keys */
ngx_http_file_cache_create_key(r);
if (r->cache->header_start + 256 >= u->conf->buffer_size) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"%V_buffer_size %uz is not enough for cache key, "
"it should be increased to at least %uz",
&u->conf->module, u->conf->buffer_size,
ngx_align(r->cache->header_start + 256, 1024));
r->cache = NULL;
return NGX_DECLINED;
}
u->cacheable = 1;
c = r->cache;
c->body_start = u->conf->buffer_size;
c->min_uses = u->conf->cache_min_uses;
c->file_cache = cache;
switch (ngx_http_test_predicates(r, u->conf->cache_bypass)) {
case NGX_ERROR:
return NGX_ERROR;
case NGX_DECLINED:
u->cache_status = NGX_HTTP_CACHE_BYPASS;
return NGX_DECLINED;
default: /* NGX_OK */
break;
}
c->lock = u->conf->cache_lock;
c->lock_timeout = u->conf->cache_lock_timeout;
c->lock_age = u->conf->cache_lock_age;
u->cache_status = NGX_HTTP_CACHE_MISS;
}
rc = ngx_http_file_cache_open(r);
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http upstream cache: %i", rc);
switch (rc) {
case NGX_HTTP_CACHE_STALE:
if (((u->conf->cache_use_stale & NGX_HTTP_UPSTREAM_FT_UPDATING)
|| c->stale_updating) && !r->background
&& u->conf->cache_background_update)
{
r->cache->background = 1;
u->cache_status = rc;
rc = NGX_OK;
}
break;
case NGX_HTTP_CACHE_UPDATING:
if (((u->conf->cache_use_stale & NGX_HTTP_UPSTREAM_FT_UPDATING)
|| c->stale_updating) && !r->background)
{
u->cache_status = rc;
rc = NGX_OK;
} else {
rc = NGX_HTTP_CACHE_STALE;
}
break;
case NGX_OK:
u->cache_status = NGX_HTTP_CACHE_HIT;
}
switch (rc) {
case NGX_OK:
return NGX_OK;
case NGX_HTTP_CACHE_STALE:
c->valid_sec = 0;
c->updating_sec = 0;
c->error_sec = 0;
u->buffer.start = NULL;
u->cache_status = NGX_HTTP_CACHE_EXPIRED;
break;
case NGX_DECLINED:
if ((size_t) (u->buffer.end - u->buffer.start) < u->conf->buffer_size) {
u->buffer.start = NULL;
} else {
u->buffer.pos = u->buffer.start + c->header_start;
u->buffer.last = u->buffer.pos;
}
break;
case NGX_HTTP_CACHE_SCARCE:
u->cacheable = 0;
break;
case NGX_AGAIN:
return NGX_BUSY;
case NGX_ERROR:
return NGX_ERROR;
default:
/* cached NGX_HTTP_BAD_GATEWAY, NGX_HTTP_GATEWAY_TIME_OUT, etc. */
u->cache_status = NGX_HTTP_CACHE_HIT;
return rc;
}
if (ngx_http_upstream_cache_check_range(r, u) == NGX_DECLINED) {
u->cacheable = 0;
}
r->cached = 0;
return NGX_DECLINED;
}
static ngx_int_t
ngx_http_upstream_cache_get(ngx_http_request_t *r, ngx_http_upstream_t *u,
ngx_http_file_cache_t **cache)
{
ngx_str_t *name, val;
ngx_uint_t i;
ngx_http_file_cache_t **caches;
if (u->conf->cache_zone) {
*cache = u->conf->cache_zone->data;
return NGX_OK;
}
if (ngx_http_complex_value(r, u->conf->cache_value, &val) != NGX_OK) {
return NGX_ERROR;
}
if (val.len == 0
|| (val.len == 3 && ngx_strncmp(val.data, "off", 3) == 0))
{
return NGX_DECLINED;
}
caches = u->caches->elts;
for (i = 0; i < u->caches->nelts; i++) {
name = &caches[i]->shm_zone->shm.name;
if (name->len == val.len
&& ngx_strncmp(name->data, val.data, val.len) == 0)
{
*cache = caches[i];
return NGX_OK;
}
}
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"cache \"%V\" not found", &val);
return NGX_ERROR;
}
static ngx_int_t
ngx_http_upstream_cache_send(ngx_http_request_t *r, ngx_http_upstream_t *u)
{
ngx_int_t rc;
ngx_http_cache_t *c;
r->cached = 1;
c = r->cache;
if (c->header_start == c->body_start) {
r->http_version = NGX_HTTP_VERSION_9;
return ngx_http_cache_send(r);
}
/* TODO: cache stack */
u->buffer = *c->buf;
u->buffer.pos += c->header_start;
ngx_memzero(&u->headers_in, sizeof(ngx_http_upstream_headers_in_t));
u->headers_in.content_length_n = -1;
u->headers_in.last_modified_time = -1;
if (ngx_list_init(&u->headers_in.headers, r->pool, 8,
sizeof(ngx_table_elt_t))
!= NGX_OK)
{
return NGX_ERROR;
}
if (ngx_list_init(&u->headers_in.trailers, r->pool, 2,
sizeof(ngx_table_elt_t))
!= NGX_OK)
{
return NGX_ERROR;
}
rc = u->process_header(r);
if (rc == NGX_OK) {
if (ngx_http_upstream_process_headers(r, u) != NGX_OK) {
return NGX_DONE;
}
return ngx_http_cache_send(r);
}
if (rc == NGX_ERROR) {
return NGX_ERROR;
}
if (rc == NGX_AGAIN) {
rc = NGX_HTTP_UPSTREAM_INVALID_HEADER;
}
/* rc == NGX_HTTP_UPSTREAM_INVALID_HEADER */
ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,
"cache file \"%s\" contains invalid header",
c->file.name.data);
/* TODO: delete file */
return rc;
}
static ngx_int_t
ngx_http_upstream_cache_background_update(ngx_http_request_t *r,
ngx_http_upstream_t *u)
{
ngx_http_request_t *sr;
if (!r->cached || !r->cache->background) {
return NGX_OK;
}
if (r == r->main) {
r->preserve_body = 1;
}
if (ngx_http_subrequest(r, &r->uri, &r->args, &sr, NULL,
NGX_HTTP_SUBREQUEST_CLONE
|NGX_HTTP_SUBREQUEST_BACKGROUND)
!= NGX_OK)
{
return NGX_ERROR;
}
sr->header_only = 1;
return NGX_OK;
}
static ngx_int_t
ngx_http_upstream_cache_check_range(ngx_http_request_t *r,
ngx_http_upstream_t *u)
{
off_t offset;
u_char *p, *start;
ngx_table_elt_t *h;
h = r->headers_in.range;
if (h == NULL
|| !u->cacheable
|| u->conf->cache_max_range_offset == NGX_MAX_OFF_T_VALUE)
{
return NGX_OK;
}
if (u->conf->cache_max_range_offset == 0) {
return NGX_DECLINED;
}
if (h->value.len < 7
|| ngx_strncasecmp(h->value.data, (u_char *) "bytes=", 6) != 0)
{
return NGX_OK;
}
p = h->value.data + 6;
while (*p == ' ') { p++; }
if (*p == '-') {
return NGX_DECLINED;
}
start = p;
while (*p >= '0' && *p <= '9') { p++; }
offset = ngx_atoof(start, p - start);
if (offset >= u->conf->cache_max_range_offset) {
return NGX_DECLINED;
}
return NGX_OK;
}
#endif
static void
ngx_http_upstream_resolve_handler(ngx_resolver_ctx_t *ctx)
{
ngx_uint_t run_posted;
ngx_connection_t *c;
ngx_http_request_t *r;
ngx_http_upstream_t *u;
ngx_http_upstream_resolved_t *ur;
run_posted = ctx->async;
r = ctx->data;
c = r->connection;
u = r->upstream;
ur = u->resolved;
ngx_http_set_log_request(c->log, r);
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
"http upstream resolve: \"%V?%V\"", &r->uri, &r->args);
if (ctx->state) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"%V could not be resolved (%i: %s)",
&ctx->name, ctx->state,
ngx_resolver_strerror(ctx->state));
ngx_http_upstream_finalize_request(r, u, NGX_HTTP_BAD_GATEWAY);
goto failed;
}
ur->naddrs = ctx->naddrs;
ur->addrs = ctx->addrs;
#if (NGX_DEBUG)
{
u_char text[NGX_SOCKADDR_STRLEN];
ngx_str_t addr;
ngx_uint_t i;
addr.data = text;
for (i = 0; i < ctx->naddrs; i++) {
addr.len = ngx_sock_ntop(ur->addrs[i].sockaddr, ur->addrs[i].socklen,
text, NGX_SOCKADDR_STRLEN, 0);
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"name was resolved to %V", &addr);
}
}
#endif
if (ngx_http_upstream_create_round_robin_peer(r, ur) != NGX_OK) {
ngx_http_upstream_finalize_request(r, u,
NGX_HTTP_INTERNAL_SERVER_ERROR);
goto failed;
}
ngx_resolve_name_done(ctx);
ur->ctx = NULL;
u->peer.start_time = ngx_current_msec;
if (u->conf->next_upstream_tries
&& u->peer.tries > u->conf->next_upstream_tries)
{
u->peer.tries = u->conf->next_upstream_tries;
}
ngx_http_upstream_connect(r, u);
failed:
if (run_posted) {
ngx_http_run_posted_requests(c);
}
}
static void
ngx_http_upstream_handler(ngx_event_t *ev)
{
ngx_connection_t *c;
ngx_http_request_t *r;
ngx_http_upstream_t *u;
c = ev->data;
r = c->data;
u = r->upstream;
c = r->connection;
ngx_http_set_log_request(c->log, r);
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
"http upstream request: \"%V?%V\"", &r->uri, &r->args);
if (ev->delayed && ev->timedout) {
ev->delayed = 0;
ev->timedout = 0;
}
if (ev->write) {
u->write_event_handler(r, u);
} else {
u->read_event_handler(r, u);
}
ngx_http_run_posted_requests(c);
}
static void
ngx_http_upstream_rd_check_broken_connection(ngx_http_request_t *r)
{
ngx_http_upstream_check_broken_connection(r, r->connection->read);
}
static void
ngx_http_upstream_wr_check_broken_connection(ngx_http_request_t *r)
{
ngx_http_upstream_check_broken_connection(r, r->connection->write);
}
static void
ngx_http_upstream_check_broken_connection(ngx_http_request_t *r,
ngx_event_t *ev)
{
int n;
char buf[1];
ngx_err_t err;
ngx_int_t event;
ngx_connection_t *c;
ngx_http_upstream_t *u;
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ev->log, 0,
"http upstream check client, write event:%d, \"%V\"",
ev->write, &r->uri);
c = r->connection;
u = r->upstream;
if (c->error) {
if ((ngx_event_flags & NGX_USE_LEVEL_EVENT) && ev->active) {
event = ev->write ? NGX_WRITE_EVENT : NGX_READ_EVENT;
if (ngx_del_event(ev, event, 0) != NGX_OK) {
ngx_http_upstream_finalize_request(r, u,
NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
}
}
if (!u->cacheable) {
ngx_http_upstream_finalize_request(r, u,
NGX_HTTP_CLIENT_CLOSED_REQUEST);
}
return;
}
#if (NGX_HTTP_V2)
if (r->stream) {
return;
}
#endif
#if (NGX_HAVE_KQUEUE)
if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {
if (!ev->pending_eof) {
return;
}
ev->eof = 1;
c->error = 1;
if (ev->kq_errno) {
ev->error = 1;
}
if (!u->cacheable && u->peer.connection) {
ngx_log_error(NGX_LOG_INFO, ev->log, ev->kq_errno,
"kevent() reported that client prematurely closed "
"connection, so upstream connection is closed too");
ngx_http_upstream_finalize_request(r, u,
NGX_HTTP_CLIENT_CLOSED_REQUEST);
return;
}
ngx_log_error(NGX_LOG_INFO, ev->log, ev->kq_errno,
"kevent() reported that client prematurely closed "
"connection");
if (u->peer.connection == NULL) {
ngx_http_upstream_finalize_request(r, u,
NGX_HTTP_CLIENT_CLOSED_REQUEST);
}
return;
}
#endif
#if (NGX_HAVE_EPOLLRDHUP)
if ((ngx_event_flags & NGX_USE_EPOLL_EVENT) && ngx_use_epoll_rdhup) {
socklen_t len;
if (!ev->pending_eof) {
return;
}
ev->eof = 1;
c->error = 1;
err = 0;
len = sizeof(ngx_err_t);
/*
* BSDs and Linux return 0 and set a pending error in err
* Solaris returns -1 and sets errno
*/
if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR, (void *) &err, &len)
== -1)
{
err = ngx_socket_errno;
}
if (err) {
ev->error = 1;
}
if (!u->cacheable && u->peer.connection) {
ngx_log_error(NGX_LOG_INFO, ev->log, err,
"epoll_wait() reported that client prematurely closed "
"connection, so upstream connection is closed too");
ngx_http_upstream_finalize_request(r, u,
NGX_HTTP_CLIENT_CLOSED_REQUEST);
return;
}
ngx_log_error(NGX_LOG_INFO, ev->log, err,
"epoll_wait() reported that client prematurely closed "
"connection");
if (u->peer.connection == NULL) {
ngx_http_upstream_finalize_request(r, u,
NGX_HTTP_CLIENT_CLOSED_REQUEST);
}
return;
}
#endif
n = recv(c->fd, buf, 1, MSG_PEEK);
err = ngx_socket_errno;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ev->log, err,
"http upstream recv(): %d", n);
if (ev->write && (n >= 0 || err == NGX_EAGAIN)) {
return;
}
if ((ngx_event_flags & NGX_USE_LEVEL_EVENT) && ev->active) {
event = ev->write ? NGX_WRITE_EVENT : NGX_READ_EVENT;
if (ngx_del_event(ev, event, 0) != NGX_OK) {
ngx_http_upstream_finalize_request(r, u,
NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
}
}
if (n > 0) {
return;
}
if (n == -1) {
if (err == NGX_EAGAIN) {
return;
}
ev->error = 1;
} else { /* n == 0 */
err = 0;
}
ev->eof = 1;
c->error = 1;
if (!u->cacheable && u->peer.connection) {
ngx_log_error(NGX_LOG_INFO, ev->log, err,
"client prematurely closed connection, "
"so upstream connection is closed too");
ngx_http_upstream_finalize_request(r, u,
NGX_HTTP_CLIENT_CLOSED_REQUEST);
return;
}
ngx_log_error(NGX_LOG_INFO, ev->log, err,
"client prematurely closed connection");
if (u->peer.connection == NULL) {
ngx_http_upstream_finalize_request(r, u,
NGX_HTTP_CLIENT_CLOSED_REQUEST);
}
}
static void
ngx_http_upstream_connect(ngx_http_request_t *r, ngx_http_upstream_t *u)
{
ngx_int_t rc;
ngx_connection_t *c;
r->connection->log->action = "connecting to upstream";
if (u->state && u->state->response_time) {
u->state->response_time = ngx_current_msec - u->state->response_time;
}
u->state = ngx_array_push(r->upstream_states);
if (u->state == NULL) {
ngx_http_upstream_finalize_request(r, u,
NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
}
ngx_memzero(u->state, sizeof(ngx_http_upstream_state_t));
u->state->response_time = ngx_current_msec;
u->state->connect_time = (ngx_msec_t) -1;
u->state->header_time = (ngx_msec_t) -1;
rc = ngx_event_connect_peer(&u->peer);
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http upstream connect: %i", rc);
if (rc == NGX_ERROR) {
ngx_http_upstream_finalize_request(r, u,
NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
}
u->state->peer = u->peer.name;
if (rc == NGX_BUSY) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "no live upstreams");
ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_NOLIVE);
return;
}
if (rc == NGX_DECLINED) {
ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR);
return;
}
/* rc == NGX_OK || rc == NGX_AGAIN || rc == NGX_DONE */
c = u->peer.connection;
c->data = r;
c->write->handler = ngx_http_upstream_handler;
c->read->handler = ngx_http_upstream_handler;
u->write_event_handler = ngx_http_upstream_send_request_handler;
u->read_event_handler = ngx_http_upstream_process_header;
c->sendfile &= r->connection->sendfile;
u->output.sendfile = c->sendfile;
if (c->pool == NULL) {
/* we need separate pool here to be able to cache SSL connections */
c->pool = ngx_create_pool(128, r->connection->log);
if (c->pool == NULL) {
ngx_http_upstream_finalize_request(r, u,
NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
}
}
c->log = r->connection->log;
c->pool->log = c->log;
c->read->log = c->log;
c->write->log = c->log;
/* init or reinit the ngx_output_chain() and ngx_chain_writer() contexts */
u->writer.out = NULL;
u->writer.last = &u->writer.out;
u->writer.connection = c;
u->writer.limit = 0;
if (u->request_sent) {
if (ngx_http_upstream_reinit(r, u) != NGX_OK) {
ngx_http_upstream_finalize_request(r, u,
NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
}
}
if (r->request_body
&& r->request_body->buf
&& r->request_body->temp_file
&& r == r->main)
{
/*
* the r->request_body->buf can be reused for one request only,
* the subrequests should allocate their own temporary bufs
*/
u->output.free = ngx_alloc_chain_link(r->pool);
if (u->output.free == NULL) {
ngx_http_upstream_finalize_request(r, u,
NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
}
u->output.free->buf = r->request_body->buf;
u->output.free->next = NULL;
u->output.allocated = 1;
r->request_body->buf->pos = r->request_body->buf->start;
r->request_body->buf->last = r->request_body->buf->start;
r->request_body->buf->tag = u->output.tag;
}
u->request_sent = 0;
u->request_body_sent = 0;
u->request_body_blocked = 0;
if (rc == NGX_AGAIN) {
ngx_add_timer(c->write, u->conf->connect_timeout);
return;
}
#if (NGX_HTTP_SSL)
if (u->ssl && c->ssl == NULL) {
ngx_http_upstream_ssl_init_connection(r, u, c);
return;
}
#endif
ngx_http_upstream_send_request(r, u, 1);
}
#if (NGX_HTTP_SSL)
static void
ngx_http_upstream_ssl_init_connection(ngx_http_request_t *r,
ngx_http_upstream_t *u, ngx_connection_t *c)
{
ngx_int_t rc;
ngx_http_core_loc_conf_t *clcf;
if (ngx_http_upstream_test_connect(c) != NGX_OK) {
ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR);
return;
}
if (ngx_ssl_create_connection(u->conf->ssl, c,
NGX_SSL_BUFFER|NGX_SSL_CLIENT)
!= NGX_OK)
{
ngx_http_upstream_finalize_request(r, u,
NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
}
c->sendfile = 0;
u->output.sendfile = 0;
if (u->conf->ssl_server_name || u->conf->ssl_verify) {
if (ngx_http_upstream_ssl_name(r, u, c) != NGX_OK) {
ngx_http_upstream_finalize_request(r, u,
NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
}
}
if (u->conf->ssl_session_reuse) {
if (u->peer.set_session(&u->peer, u->peer.data) != NGX_OK) {
ngx_http_upstream_finalize_request(r, u,
NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
}
/* abbreviated SSL handshake may interact badly with Nagle */
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
if (clcf->tcp_nodelay && ngx_tcp_nodelay(c) != NGX_OK) {
ngx_http_upstream_finalize_request(r, u,
NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
}
}
r->connection->log->action = "SSL handshaking to upstream";
rc = ngx_ssl_handshake(c);
if (rc == NGX_AGAIN) {
if (!c->write->timer_set) {
ngx_add_timer(c->write, u->conf->connect_timeout);
}
c->ssl->handler = ngx_http_upstream_ssl_handshake_handler;
return;
}
ngx_http_upstream_ssl_handshake(r, u, c);
}
static void
ngx_http_upstream_ssl_handshake_handler(ngx_connection_t *c)
{
ngx_http_request_t *r;
ngx_http_upstream_t *u;
r = c->data;
u = r->upstream;
c = r->connection;
ngx_http_set_log_request(c->log, r);
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
"http upstream ssl handshake: \"%V?%V\"",
&r->uri, &r->args);
ngx_http_upstream_ssl_handshake(r, u, u->peer.connection);
ngx_http_run_posted_requests(c);
}
static void
ngx_http_upstream_ssl_handshake(ngx_http_request_t *r, ngx_http_upstream_t *u,
ngx_connection_t *c)
{
long rc;
if (c->ssl->handshaked) {
if (u->conf->ssl_verify) {
rc = SSL_get_verify_result(c->ssl->connection);
if (rc != X509_V_OK) {
ngx_log_error(NGX_LOG_ERR, c->log, 0,
"upstream SSL certificate verify error: (%l:%s)",
rc, X509_verify_cert_error_string(rc));
goto failed;
}
if (ngx_ssl_check_host(c, &u->ssl_name) != NGX_OK) {
ngx_log_error(NGX_LOG_ERR, c->log, 0,
"upstream SSL certificate does not match \"%V\"",
&u->ssl_name);
goto failed;
}
}
if (u->conf->ssl_session_reuse) {
u->peer.save_session(&u->peer, u->peer.data);
}
c->write->handler = ngx_http_upstream_handler;
c->read->handler = ngx_http_upstream_handler;
ngx_http_upstream_send_request(r, u, 1);
return;
}
if (c->write->timedout) {
ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_TIMEOUT);
return;
}
failed:
ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR);
}
static ngx_int_t
ngx_http_upstream_ssl_name(ngx_http_request_t *r, ngx_http_upstream_t *u,
ngx_connection_t *c)
{
u_char *p, *last;
ngx_str_t name;
if (u->conf->ssl_name) {
if (ngx_http_complex_value(r, u->conf->ssl_name, &name) != NGX_OK) {
return NGX_ERROR;
}
} else {
name = u->ssl_name;
}
if (name.len == 0) {
goto done;
}
/*
* ssl name here may contain port, notably if derived from $proxy_host
* or $http_host; we have to strip it
*/
p = name.data;
last = name.data + name.len;
if (*p == '[') {
p = ngx_strlchr(p, last, ']');
if (p == NULL) {
p = name.data;
}
}
p = ngx_strlchr(p, last, ':');
if (p != NULL) {
name.len = p - name.data;
}
if (!u->conf->ssl_server_name) {
goto done;
}
#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
/* as per RFC 6066, literal IPv4 and IPv6 addresses are not permitted */
if (name.len == 0 || *name.data == '[') {
goto done;
}
if (ngx_inet_addr(name.data, name.len) != INADDR_NONE) {
goto done;
}
/*
* SSL_set_tlsext_host_name() needs a null-terminated string,
* hence we explicitly null-terminate name here
*/
p = ngx_pnalloc(r->pool, name.len + 1);
if (p == NULL) {
return NGX_ERROR;
}
(void) ngx_cpystrn(p, name.data, name.len + 1);
name.data = p;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"upstream SSL server name: \"%s\"", name.data);
if (SSL_set_tlsext_host_name(c->ssl->connection,
(char *) name.data)
== 0)
{
ngx_ssl_error(NGX_LOG_ERR, r->connection->log, 0,
"SSL_set_tlsext_host_name(\"%s\") failed", name.data);
return NGX_ERROR;
}
#endif
done:
u->ssl_name = name;
return NGX_OK;
}
#endif
static ngx_int_t
ngx_http_upstream_reinit(ngx_http_request_t *r, ngx_http_upstream_t *u)
{
off_t file_pos;
ngx_chain_t *cl;
if (u->reinit_request(r) != NGX_OK) {
return NGX_ERROR;
}
u->keepalive = 0;
u->upgrade = 0;
ngx_memzero(&u->headers_in, sizeof(ngx_http_upstream_headers_in_t));
u->headers_in.content_length_n = -1;
u->headers_in.last_modified_time = -1;
if (ngx_list_init(&u->headers_in.headers, r->pool, 8,
sizeof(ngx_table_elt_t))
!= NGX_OK)
{
return NGX_ERROR;
}
if (ngx_list_init(&u->headers_in.trailers, r->pool, 2,
sizeof(ngx_table_elt_t))
!= NGX_OK)
{
return NGX_ERROR;
}
/* reinit the request chain */
file_pos = 0;
for (cl = u->request_bufs; cl; cl = cl->next) {
cl->buf->pos = cl->buf->start;
/* there is at most one file */
if (cl->buf->in_file) {
cl->buf->file_pos = file_pos;
file_pos = cl->buf->file_last;
}
}
/* reinit the subrequest's ngx_output_chain() context */
if (r->request_body && r->request_body->temp_file
&& r != r->main && u->output.buf)
{
u->output.free = ngx_alloc_chain_link(r->pool);
if (u->output.free == NULL) {
return NGX_ERROR;
}
u->output.free->buf = u->output.buf;
u->output.free->next = NULL;
u->output.buf->pos = u->output.buf->start;
u->output.buf->last = u->output.buf->start;
}
u->output.buf = NULL;
u->output.in = NULL;
u->output.busy = NULL;
/* reinit u->buffer */
u->buffer.pos = u->buffer.start;
#if (NGX_HTTP_CACHE)
if (r->cache) {
u->buffer.pos += r->cache->header_start;
}
#endif
u->buffer.last = u->buffer.pos;
return NGX_OK;
}
static void
ngx_http_upstream_send_request(ngx_http_request_t *r, ngx_http_upstream_t *u,
ngx_uint_t do_write)
{
ngx_int_t rc;
ngx_connection_t *c;
c = u->peer.connection;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
"http upstream send request");
if (u->state->connect_time == (ngx_msec_t) -1) {
u->state->connect_time = ngx_current_msec - u->state->response_time;
}
if (!u->request_sent && ngx_http_upstream_test_connect(c) != NGX_OK) {
ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR);
return;
}
c->log->action = "sending request to upstream";
rc = ngx_http_upstream_send_request_body(r, u, do_write);
if (rc == NGX_ERROR) {
ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR);
return;
}
if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
ngx_http_upstream_finalize_request(r, u, rc);
return;
}
if (rc == NGX_AGAIN) {
if (!c->write->ready || u->request_body_blocked) {
ngx_add_timer(c->write, u->conf->send_timeout);
} else if (c->write->timer_set) {
ngx_del_timer(c->write);
}
if (ngx_handle_write_event(c->write, u->conf->send_lowat) != NGX_OK) {
ngx_http_upstream_finalize_request(r, u,
NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
}
return;
}
/* rc == NGX_OK */
if (c->write->timer_set) {
ngx_del_timer(c->write);
}
if (c->tcp_nopush == NGX_TCP_NOPUSH_SET) {
if (ngx_tcp_push(c->fd) == -1) {
ngx_log_error(NGX_LOG_CRIT, c->log, ngx_socket_errno,
ngx_tcp_push_n " failed");
ngx_http_upstream_finalize_request(r, u,
NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
}
c->tcp_nopush = NGX_TCP_NOPUSH_UNSET;
}
if (!u->conf->preserve_output) {
u->write_event_handler = ngx_http_upstream_dummy_handler;
}
if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
ngx_http_upstream_finalize_request(r, u,
NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
}
if (!u->request_body_sent) {
u->request_body_sent = 1;
if (u->header_sent) {
return;
}
ngx_add_timer(c->read, u->conf->read_timeout);
if (c->read->ready) {
ngx_http_upstream_process_header(r, u);
return;
}
}
}
static ngx_int_t
ngx_http_upstream_send_request_body(ngx_http_request_t *r,
ngx_http_upstream_t *u, ngx_uint_t do_write)
{
ngx_int_t rc;
ngx_chain_t *out, *cl, *ln;
ngx_connection_t *c;
ngx_http_core_loc_conf_t *clcf;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http upstream send request body");
if (!r->request_body_no_buffering) {
/* buffered request body */
if (!u->request_sent) {
u->request_sent = 1;
out = u->request_bufs;
} else {
out = NULL;
}
rc = ngx_output_chain(&u->output, out);
if (rc == NGX_AGAIN) {
u->request_body_blocked = 1;
} else {
u->request_body_blocked = 0;
}
return rc;
}
if (!u->request_sent) {
u->request_sent = 1;
out = u->request_bufs;
if (r->request_body->bufs) {
for (cl = out; cl->next; cl = out->next) { /* void */ }
cl->next = r->request_body->bufs;
r->request_body->bufs = NULL;
}
c = u->peer.connection;
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
if (clcf->tcp_nodelay && ngx_tcp_nodelay(c) != NGX_OK) {
return NGX_ERROR;
}
r->read_event_handler = ngx_http_upstream_read_request_handler;
} else {
out = NULL;
}
for ( ;; ) {
if (do_write) {
rc = ngx_output_chain(&u->output, out);
if (rc == NGX_ERROR) {
return NGX_ERROR;
}
while (out) {
ln = out;
out = out->next;
ngx_free_chain(r->pool, ln);
}
if (rc == NGX_AGAIN) {
u->request_body_blocked = 1;
} else {
u->request_body_blocked = 0;
}
if (rc == NGX_OK && !r->reading_body) {
break;
}
}
if (r->reading_body) {
/* read client request body */
rc = ngx_http_read_unbuffered_request_body(r);
if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
return rc;
}
out = r->request_body->bufs;
r->request_body->bufs = NULL;
}
/* stop if there is nothing to send */
if (out == NULL) {
rc = NGX_AGAIN;
break;
}
do_write = 1;
}
if (!r->reading_body) {
if (!u->store && !r->post_action && !u->conf->ignore_client_abort) {
r->read_event_handler =
ngx_http_upstream_rd_check_broken_connection;
}
}
return rc;
}
static void
ngx_http_upstream_send_request_handler(ngx_http_request_t *r,
ngx_http_upstream_t *u)
{
ngx_connection_t *c;
c = u->peer.connection;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http upstream send request handler");
if (c->write->timedout) {
ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_TIMEOUT);
return;
}
#if (NGX_HTTP_SSL)
if (u->ssl && c->ssl == NULL) {
ngx_http_upstream_ssl_init_connection(r, u, c);
return;
}
#endif
if (u->header_sent && !u->conf->preserve_output) {
u->write_event_handler = ngx_http_upstream_dummy_handler;
(void) ngx_handle_write_event(c->write, 0);
return;
}
ngx_http_upstream_send_request(r, u, 1);
}
static void
ngx_http_upstream_read_request_handler(ngx_http_request_t *r)
{
ngx_connection_t *c;
ngx_http_upstream_t *u;
c = r->connection;
u = r->upstream;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http upstream read request handler");
if (c->read->timedout) {
c->timedout = 1;
ngx_http_upstream_finalize_request(r, u, NGX_HTTP_REQUEST_TIME_OUT);
return;
}
ngx_http_upstream_send_request(r, u, 0);
}
static void
ngx_http_upstream_process_header(ngx_http_request_t *r, ngx_http_upstream_t *u)
{
ssize_t n;
ngx_int_t rc;
ngx_connection_t *c;
c = u->peer.connection;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
"http upstream process header");
c->log->action = "reading response header from upstream";
if (c->read->timedout) {
ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_TIMEOUT);
return;
}
if (!u->request_sent && ngx_http_upstream_test_connect(c) != NGX_OK) {
ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR);
return;
}
if (u->buffer.start == NULL) {
u->buffer.start = ngx_palloc(r->pool, u->conf->buffer_size);
if (u->buffer.start == NULL) {
ngx_http_upstream_finalize_request(r, u,
NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
}
u->buffer.pos = u->buffer.start;
u->buffer.last = u->buffer.start;
u->buffer.end = u->buffer.start + u->conf->buffer_size;
u->buffer.temporary = 1;
u->buffer.tag = u->output.tag;
if (ngx_list_init(&u->headers_in.headers, r->pool, 8,
sizeof(ngx_table_elt_t))
!= NGX_OK)
{
ngx_http_upstream_finalize_request(r, u,
NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
}
if (ngx_list_init(&u->headers_in.trailers, r->pool, 2,
sizeof(ngx_table_elt_t))
!= NGX_OK)
{
ngx_http_upstream_finalize_request(r, u,
NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
}
#if (NGX_HTTP_CACHE)
if (r->cache) {
u->buffer.pos += r->cache->header_start;
u->buffer.last = u->buffer.pos;
}
#endif
}
for ( ;; ) {
n = c->recv(c, u->buffer.last, u->buffer.end - u->buffer.last);
if (n == NGX_AGAIN) {
#if 0
ngx_add_timer(rev, u->read_timeout);
#endif
if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
ngx_http_upstream_finalize_request(r, u,
NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
}
return;
}
if (n == 0) {
ngx_log_error(NGX_LOG_ERR, c->log, 0,
"upstream prematurely closed connection");
}
if (n == NGX_ERROR || n == 0) {
ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR);
return;
}
u->state->bytes_received += n;
u->buffer.last += n;
#if 0
u->valid_header_in = 0;
u->peer.cached = 0;
#endif
rc = u->process_header(r);
if (rc == NGX_AGAIN) {
if (u->buffer.last == u->buffer.end) {
ngx_log_error(NGX_LOG_ERR, c->log, 0,
"upstream sent too big header");
ngx_http_upstream_next(r, u,
NGX_HTTP_UPSTREAM_FT_INVALID_HEADER);
return;
}
continue;
}
break;
}
if (rc == NGX_HTTP_UPSTREAM_INVALID_HEADER) {
ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_INVALID_HEADER);
return;
}
if (rc == NGX_ERROR) {
ngx_http_upstream_finalize_request(r, u,
NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
}
/* rc == NGX_OK */
u->state->header_time = ngx_current_msec - u->state->response_time;
if (u->headers_in.status_n >= NGX_HTTP_SPECIAL_RESPONSE) {
if (ngx_http_upstream_test_next(r, u) == NGX_OK) {
return;
}
if (ngx_http_upstream_intercept_errors(r, u) == NGX_OK) {
return;
}
}
if (ngx_http_upstream_process_headers(r, u) != NGX_OK) {
return;
}
ngx_http_upstream_send_response(r, u);
}
static ngx_int_t
ngx_http_upstream_test_next(ngx_http_request_t *r, ngx_http_upstream_t *u)
{
ngx_msec_t timeout;
ngx_uint_t status, mask;
ngx_http_upstream_next_t *un;
status = u->headers_in.status_n;
for (un = ngx_http_upstream_next_errors; un->status; un++) {
if (status != un->status) {
continue;
}
timeout = u->conf->next_upstream_timeout;
if (u->request_sent
&& (r->method & (NGX_HTTP_POST|NGX_HTTP_LOCK|NGX_HTTP_PATCH)))
{
mask = un->mask | NGX_HTTP_UPSTREAM_FT_NON_IDEMPOTENT;
} else {
mask = un->mask;
}
if (u->peer.tries > 1
&& ((u->conf->next_upstream & mask) == mask)
&& !(u->request_sent && r->request_body_no_buffering)
&& !(timeout && ngx_current_msec - u->peer.start_time >= timeout))
{
ngx_http_upstream_next(r, u, un->mask);
return NGX_OK;
}
#if (NGX_HTTP_CACHE)
if (u->cache_status == NGX_HTTP_CACHE_EXPIRED
&& ((u->conf->cache_use_stale & un->mask) || r->cache->stale_error))
{
ngx_int_t rc;
rc = u->reinit_request(r);
if (rc != NGX_OK) {
ngx_http_upstream_finalize_request(r, u, rc);
return NGX_OK;
}
u->cache_status = NGX_HTTP_CACHE_STALE;
rc = ngx_http_upstream_cache_send(r, u);
if (rc == NGX_DONE) {
return NGX_OK;
}
if (rc == NGX_HTTP_UPSTREAM_INVALID_HEADER) {
rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
}
ngx_http_upstream_finalize_request(r, u, rc);
return NGX_OK;
}
#endif
}
#if (NGX_HTTP_CACHE)
if (status == NGX_HTTP_NOT_MODIFIED
&& u->cache_status == NGX_HTTP_CACHE_EXPIRED
&& u->conf->cache_revalidate)
{
time_t now, valid, updating, error;
ngx_int_t rc;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http upstream not modified");
now = ngx_time();
valid = r->cache->valid_sec;
updating = r->cache->updating_sec;
error = r->cache->error_sec;
rc = u->reinit_request(r);
if (rc != NGX_OK) {
ngx_http_upstream_finalize_request(r, u, rc);
return NGX_OK;
}
u->cache_status = NGX_HTTP_CACHE_REVALIDATED;
rc = ngx_http_upstream_cache_send(r, u);
if (rc == NGX_DONE) {
return NGX_OK;
}
if (rc == NGX_HTTP_UPSTREAM_INVALID_HEADER) {
rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
}
if (valid == 0) {
valid = r->cache->valid_sec;
updating = r->cache->updating_sec;
error = r->cache->error_sec;
}
if (valid == 0) {
valid = ngx_http_file_cache_valid(u->conf->cache_valid,
u->headers_in.status_n);
if (valid) {
valid = now + valid;
}
}
if (valid) {
r->cache->valid_sec = valid;
r->cache->updating_sec = updating;
r->cache->error_sec = error;
r->cache->date = now;
ngx_http_file_cache_update_header(r);
}
ngx_http_upstream_finalize_request(r, u, rc);
return NGX_OK;
}
#endif
return NGX_DECLINED;
}
static ngx_int_t
ngx_http_upstream_intercept_errors(ngx_http_request_t *r,
ngx_http_upstream_t *u)
{
ngx_int_t status;
ngx_uint_t i;
ngx_table_elt_t *h;
ngx_http_err_page_t *err_page;
ngx_http_core_loc_conf_t *clcf;
status = u->headers_in.status_n;
if (status == NGX_HTTP_NOT_FOUND && u->conf->intercept_404) {
ngx_http_upstream_finalize_request(r, u, NGX_HTTP_NOT_FOUND);
return NGX_OK;
}
if (!u->conf->intercept_errors) {
return NGX_DECLINED;
}
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
if (clcf->error_pages == NULL) {
return NGX_DECLINED;
}
err_page = clcf->error_pages->elts;
for (i = 0; i < clcf->error_pages->nelts; i++) {
if (err_page[i].status == status) {
if (status == NGX_HTTP_UNAUTHORIZED
&& u->headers_in.www_authenticate)
{
h = ngx_list_push(&r->headers_out.headers);
if (h == NULL) {
ngx_http_upstream_finalize_request(r, u,
NGX_HTTP_INTERNAL_SERVER_ERROR);
return NGX_OK;
}
*h = *u->headers_in.www_authenticate;
r->headers_out.www_authenticate = h;
}
#if (NGX_HTTP_CACHE)
if (r->cache) {
if (u->cacheable) {
time_t valid;
valid = r->cache->valid_sec;
if (valid == 0) {
valid = ngx_http_file_cache_valid(u->conf->cache_valid,
status);
if (valid) {
r->cache->valid_sec = ngx_time() + valid;
}
}
if (valid) {
r->cache->error = status;
}
}
ngx_http_file_cache_free(r->cache, u->pipe->temp_file);
}
#endif
ngx_http_upstream_finalize_request(r, u, status);
return NGX_OK;
}
}
return NGX_DECLINED;
}
static ngx_int_t
ngx_http_upstream_test_connect(ngx_connection_t *c)
{
int err;
socklen_t len;
#if (NGX_HAVE_KQUEUE)
if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {
if (c->write->pending_eof || c->read->pending_eof) {
if (c->write->pending_eof) {
err = c->write->kq_errno;
} else {
err = c->read->kq_errno;
}
c->log->action = "connecting to upstream";
(void) ngx_connection_error(c, err,
"kevent() reported that connect() failed");
return NGX_ERROR;
}
} else
#endif
{
err = 0;
len = sizeof(int);
/*
* BSDs and Linux return 0 and set a pending error in err
* Solaris returns -1 and sets errno
*/
if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR, (void *) &err, &len)
== -1)
{
err = ngx_socket_errno;
}
if (err) {
c->log->action = "connecting to upstream";
(void) ngx_connection_error(c, err, "connect() failed");
return NGX_ERROR;
}
}
return NGX_OK;
}
static ngx_int_t
ngx_http_upstream_process_headers(ngx_http_request_t *r, ngx_http_upstream_t *u)
{
ngx_str_t uri, args;
ngx_uint_t i, flags;
ngx_list_part_t *part;
ngx_table_elt_t *h;
ngx_http_upstream_header_t *hh;
ngx_http_upstream_main_conf_t *umcf;
umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module);
if (u->headers_in.x_accel_redirect
&& !(u->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_XA_REDIRECT))
{
ngx_http_upstream_finalize_request(r, u, NGX_DECLINED);
part = &u->headers_in.headers.part;
h = part->elts;
for (i = 0; /* void */; i++) {
if (i >= part->nelts) {
if (part->next == NULL) {
break;
}
part = part->next;
h = part->elts;
i = 0;
}
hh = ngx_hash_find(&umcf->headers_in_hash, h[i].hash,
h[i].lowcase_key, h[i].key.len);
if (hh && hh->redirect) {
if (hh->copy_handler(r, &h[i], hh->conf) != NGX_OK) {
ngx_http_finalize_request(r,
NGX_HTTP_INTERNAL_SERVER_ERROR);
return NGX_DONE;
}
}
}
uri = u->headers_in.x_accel_redirect->value;
if (uri.data[0] == '@') {
ngx_http_named_location(r, &uri);
} else {
ngx_str_null(&args);
flags = NGX_HTTP_LOG_UNSAFE;
if (ngx_http_parse_unsafe_uri(r, &uri, &args, &flags) != NGX_OK) {
ngx_http_finalize_request(r, NGX_HTTP_NOT_FOUND);
return NGX_DONE;
}
if (r->method != NGX_HTTP_HEAD) {
r->method = NGX_HTTP_GET;
r->method_name = ngx_http_core_get_method;
}
ngx_http_internal_redirect(r, &uri, &args);
}
ngx_http_finalize_request(r, NGX_DONE);
return NGX_DONE;
}
part = &u->headers_in.headers.part;
h = part->elts;
for (i = 0; /* void */; i++) {
if (i >= part->nelts) {
if (part->next == NULL) {
break;
}
part = part->next;
h = part->elts;
i = 0;
}
if (ngx_hash_find(&u->conf->hide_headers_hash, h[i].hash,
h[i].lowcase_key, h[i].key.len))
{
continue;
}
hh = ngx_hash_find(&umcf->headers_in_hash, h[i].hash,
h[i].lowcase_key, h[i].key.len);
if (hh) {
if (hh->copy_handler(r, &h[i], hh->conf) != NGX_OK) {
ngx_http_upstream_finalize_request(r, u,
NGX_HTTP_INTERNAL_SERVER_ERROR);
return NGX_DONE;
}
continue;
}
if (ngx_http_upstream_copy_header_line(r, &h[i], 0) != NGX_OK) {
ngx_http_upstream_finalize_request(r, u,
NGX_HTTP_INTERNAL_SERVER_ERROR);
return NGX_DONE;
}
}
if (r->headers_out.server && r->headers_out.server->value.data == NULL) {
r->headers_out.server->hash = 0;
}
if (r->headers_out.date && r->headers_out.date->value.data == NULL) {
r->headers_out.date->hash = 0;
}
r->headers_out.status = u->headers_in.status_n;
r->headers_out.status_line = u->headers_in.status_line;
r->headers_out.content_length_n = u->headers_in.content_length_n;
r->disable_not_modified = !u->cacheable;
if (u->conf->force_ranges) {
r->allow_ranges = 1;
r->single_range = 1;
#if (NGX_HTTP_CACHE)
if (r->cached) {
r->single_range = 0;
}
#endif
}
u->length = -1;
return NGX_OK;
}
static ngx_int_t
ngx_http_upstream_process_trailers(ngx_http_request_t *r,
ngx_http_upstream_t *u)
{
ngx_uint_t i;
ngx_list_part_t *part;
ngx_table_elt_t *h, *ho;
if (!u->conf->pass_trailers) {
return NGX_OK;
}
part = &u->headers_in.trailers.part;
h = part->elts;
for (i = 0; /* void */; i++) {
if (i >= part->nelts) {
if (part->next == NULL) {
break;
}
part = part->next;
h = part->elts;
i = 0;
}
if (ngx_hash_find(&u->conf->hide_headers_hash, h[i].hash,
h[i].lowcase_key, h[i].key.len))
{
continue;
}
ho = ngx_list_push(&r->headers_out.trailers);
if (ho == NULL) {
return NGX_ERROR;
}
*ho = h[i];
}
return NGX_OK;
}
static void
ngx_http_upstream_send_response(ngx_http_request_t *r, ngx_http_upstream_t *u)
{
ssize_t n;
ngx_int_t rc;
ngx_event_pipe_t *p;
ngx_connection_t *c;
ngx_http_core_loc_conf_t *clcf;
rc = ngx_http_send_header(r);
if (rc == NGX_ERROR || rc > NGX_OK || r->post_action) {
ngx_http_upstream_finalize_request(r, u, rc);
return;
}
u->header_sent = 1;
if (u->upgrade) {
#if (NGX_HTTP_CACHE)
if (r->cache) {
ngx_http_file_cache_free(r->cache, u->pipe->temp_file);
}
#endif
ngx_http_upstream_upgrade(r, u);
return;
}
c = r->connection;
if (r->header_only) {
if (!u->buffering) {
ngx_http_upstream_finalize_request(r, u, rc);
return;
}
if (!u->cacheable && !u->store) {
ngx_http_upstream_finalize_request(r, u, rc);
return;
}
u->pipe->downstream_error = 1;
}
if (r->request_body && r->request_body->temp_file
&& r == r->main && !r->preserve_body)
{
ngx_pool_run_cleanup_file(r->pool, r->request_body->temp_file->file.fd);
r->request_body->temp_file->file.fd = NGX_INVALID_FILE;
}
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
if (!u->buffering) {
#if (NGX_HTTP_CACHE)
if (r->cache) {
ngx_http_file_cache_free(r->cache, u->pipe->temp_file);
}
#endif
if (u->input_filter == NULL) {
u->input_filter_init = ngx_http_upstream_non_buffered_filter_init;
u->input_filter = ngx_http_upstream_non_buffered_filter;
u->input_filter_ctx = r;
}
u->read_event_handler = ngx_http_upstream_process_non_buffered_upstream;
r->write_event_handler =
ngx_http_upstream_process_non_buffered_downstream;
r->limit_rate = 0;
if (u->input_filter_init(u->input_filter_ctx) == NGX_ERROR) {
ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
return;
}
if (clcf->tcp_nodelay && ngx_tcp_nodelay(c) != NGX_OK) {
ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
return;
}
n = u->buffer.last - u->buffer.pos;
if (n) {
u->buffer.last = u->buffer.pos;
u->state->response_length += n;
if (u->input_filter(u->input_filter_ctx, n) == NGX_ERROR) {
ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
return;
}
ngx_http_upstream_process_non_buffered_downstream(r);
} else {
u->buffer.pos = u->buffer.start;
u->buffer.last = u->buffer.start;
if (ngx_http_send_special(r, NGX_HTTP_FLUSH) == NGX_ERROR) {
ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
return;
}
if (u->peer.connection->read->ready || u->length == 0) {
ngx_http_upstream_process_non_buffered_upstream(r, u);
}
}
return;
}
/* TODO: preallocate event_pipe bufs, look "Content-Length" */
#if (NGX_HTTP_CACHE)
if (r->cache && r->cache->file.fd != NGX_INVALID_FILE) {
ngx_pool_run_cleanup_file(r->pool, r->cache->file.fd);
r->cache->file.fd = NGX_INVALID_FILE;
}
switch (ngx_http_test_predicates(r, u->conf->no_cache)) {
case NGX_ERROR:
ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
return;
case NGX_DECLINED:
u->cacheable = 0;
break;
default: /* NGX_OK */
if (u->cache_status == NGX_HTTP_CACHE_BYPASS) {
/* create cache if previously bypassed */
if (ngx_http_file_cache_create(r) != NGX_OK) {
ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
return;
}
}
break;
}
if (u->cacheable) {
time_t now, valid;
now = ngx_time();
valid = r->cache->valid_sec;
if (valid == 0) {
valid = ngx_http_file_cache_valid(u->conf->cache_valid,
u->headers_in.status_n);
if (valid) {
r->cache->valid_sec = now + valid;
}
}
if (valid) {
r->cache->date = now;
r->cache->body_start = (u_short) (u->buffer.pos - u->buffer.start);
if (u->headers_in.status_n == NGX_HTTP_OK
|| u->headers_in.status_n == NGX_HTTP_PARTIAL_CONTENT)
{
r->cache->last_modified = u->headers_in.last_modified_time;
if (u->headers_in.etag) {
r->cache->etag = u->headers_in.etag->value;
} else {
ngx_str_null(&r->cache->etag);
}
} else {
r->cache->last_modified = -1;
ngx_str_null(&r->cache->etag);
}
if (ngx_http_file_cache_set_header(r, u->buffer.start) != NGX_OK) {
ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
return;
}
} else {
u->cacheable = 0;
}
}
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
"http cacheable: %d", u->cacheable);
if (u->cacheable == 0 && r->cache) {
ngx_http_file_cache_free(r->cache, u->pipe->temp_file);
}
if (r->header_only && !u->cacheable && !u->store) {
ngx_http_upstream_finalize_request(r, u, 0);
return;
}
#endif
p = u->pipe;
p->output_filter = ngx_http_upstream_output_filter;
p->output_ctx = r;
p->tag = u->output.tag;
p->bufs = u->conf->bufs;
p->busy_size = u->conf->busy_buffers_size;
p->upstream = u->peer.connection;
p->downstream = c;
p->pool = r->pool;
p->log = c->log;
p->limit_rate = u->conf->limit_rate;
p->start_sec = ngx_time();
p->cacheable = u->cacheable || u->store;
p->temp_file = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t));
if (p->temp_file == NULL) {
ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
return;
}
p->temp_file->file.fd = NGX_INVALID_FILE;
p->temp_file->file.log = c->log;
p->temp_file->path = u->conf->temp_path;
p->temp_file->pool = r->pool;
if (p->cacheable) {
p->temp_file->persistent = 1;
#if (NGX_HTTP_CACHE)
if (r->cache && !r->cache->file_cache->use_temp_path) {
p->temp_file->path = r->cache->file_cache->path;
p->temp_file->file.name = r->cache->file.name;
}
#endif
} else {
p->temp_file->log_level = NGX_LOG_WARN;
p->temp_file->warn = "an upstream response is buffered "
"to a temporary file";
}
p->max_temp_file_size = u->conf->max_temp_file_size;
p->temp_file_write_size = u->conf->temp_file_write_size;
#if (NGX_THREADS)
if (clcf->aio == NGX_HTTP_AIO_THREADS && clcf->aio_write) {
p->thread_handler = ngx_http_upstream_thread_handler;
p->thread_ctx = r;
}
#endif
p->preread_bufs = ngx_alloc_chain_link(r->pool);
if (p->preread_bufs == NULL) {
ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
return;
}
p->preread_bufs->buf = &u->buffer;
p->preread_bufs->next = NULL;
u->buffer.recycled = 1;
p->preread_size = u->buffer.last - u->buffer.pos;
if (u->cacheable) {
p->buf_to_file = ngx_calloc_buf(r->pool);
if (p->buf_to_file == NULL) {
ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
return;
}
p->buf_to_file->start = u->buffer.start;
p->buf_to_file->pos = u->buffer.start;
p->buf_to_file->last = u->buffer.pos;
p->buf_to_file->temporary = 1;
}
if (ngx_event_flags & NGX_USE_IOCP_EVENT) {
/* the posted aio operation may corrupt a shadow buffer */
p->single_buf = 1;
}
/* TODO: p->free_bufs = 0 if use ngx_create_chain_of_bufs() */
p->free_bufs = 1;
/*
* event_pipe would do u->buffer.last += p->preread_size
* as though these bytes were read
*/
u->buffer.last = u->buffer.pos;
if (u->conf->cyclic_temp_file) {
/*
* we need to disable the use of sendfile() if we use cyclic temp file
* because the writing a new data may interfere with sendfile()
* that uses the same kernel file pages (at least on FreeBSD)
*/
p->cyclic_temp_file = 1;
c->sendfile = 0;
} else {
p->cyclic_temp_file = 0;
}
p->read_timeout = u->conf->read_timeout;
p->send_timeout = clcf->send_timeout;
p->send_lowat = clcf->send_lowat;
p->length = -1;
if (u->input_filter_init
&& u->input_filter_init(p->input_ctx) != NGX_OK)
{
ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
return;
}
u->read_event_handler = ngx_http_upstream_process_upstream;
r->write_event_handler = ngx_http_upstream_process_downstream;
ngx_http_upstream_process_upstream(r, u);
}
static void
ngx_http_upstream_upgrade(ngx_http_request_t *r, ngx_http_upstream_t *u)
{
ngx_connection_t *c;
ngx_http_core_loc_conf_t *clcf;
c = r->connection;
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
/* TODO: prevent upgrade if not requested or not possible */
if (r != r->main) {
ngx_log_error(NGX_LOG_ERR, c->log, 0,
"connection upgrade in subrequest");
ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
return;
}
r->keepalive = 0;
c->log->action = "proxying upgraded connection";
u->read_event_handler = ngx_http_upstream_upgraded_read_upstream;
u->write_event_handler = ngx_http_upstream_upgraded_write_upstream;
r->read_event_handler = ngx_http_upstream_upgraded_read_downstream;
r->write_event_handler = ngx_http_upstream_upgraded_write_downstream;
if (clcf->tcp_nodelay) {
if (ngx_tcp_nodelay(c) != NGX_OK) {
ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
return;
}
if (ngx_tcp_nodelay(u->peer.connection) != NGX_OK) {
ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
return;
}
}
if (ngx_http_send_special(r, NGX_HTTP_FLUSH) == NGX_ERROR) {
ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
return;
}
if (u->peer.connection->read->ready
|| u->buffer.pos != u->buffer.last)
{
ngx_post_event(c->read, &ngx_posted_events);
ngx_http_upstream_process_upgraded(r, 1, 1);
return;
}
ngx_http_upstream_process_upgraded(r, 0, 1);
}
static void
ngx_http_upstream_upgraded_read_downstream(ngx_http_request_t *r)
{
ngx_http_upstream_process_upgraded(r, 0, 0);
}
static void
ngx_http_upstream_upgraded_write_downstream(ngx_http_request_t *r)
{
ngx_http_upstream_process_upgraded(r, 1, 1);
}
static void
ngx_http_upstream_upgraded_read_upstream(ngx_http_request_t *r,
ngx_http_upstream_t *u)
{
ngx_http_upstream_process_upgraded(r, 1, 0);
}
static void
ngx_http_upstream_upgraded_write_upstream(ngx_http_request_t *r,
ngx_http_upstream_t *u)
{
ngx_http_upstream_process_upgraded(r, 0, 1);
}
static void
ngx_http_upstream_process_upgraded(ngx_http_request_t *r,
ngx_uint_t from_upstream, ngx_uint_t do_write)
{
size_t size;
ssize_t n;
ngx_buf_t *b;
ngx_connection_t *c, *downstream, *upstream, *dst, *src;
ngx_http_upstream_t *u;
ngx_http_core_loc_conf_t *clcf;
c = r->connection;
u = r->upstream;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
"http upstream process upgraded, fu:%ui", from_upstream);
downstream = c;
upstream = u->peer.connection;
if (downstream->write->timedout) {
c->timedout = 1;
ngx_connection_error(c, NGX_ETIMEDOUT, "client timed out");
ngx_http_upstream_finalize_request(r, u, NGX_HTTP_REQUEST_TIME_OUT);
return;
}
if (upstream->read->timedout || upstream->write->timedout) {
ngx_connection_error(c, NGX_ETIMEDOUT, "upstream timed out");
ngx_http_upstream_finalize_request(r, u, NGX_HTTP_GATEWAY_TIME_OUT);
return;
}
if (from_upstream) {
src = upstream;
dst = downstream;
b = &u->buffer;
} else {
src = downstream;
dst = upstream;
b = &u->from_client;
if (r->header_in->last > r->header_in->pos) {
b = r->header_in;
b->end = b->last;
do_write = 1;
}
if (b->start == NULL) {
b->start = ngx_palloc(r->pool, u->conf->buffer_size);
if (b->start == NULL) {
ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
return;
}
b->pos = b->start;
b->last = b->start;
b->end = b->start + u->conf->buffer_size;
b->temporary = 1;
b->tag = u->output.tag;
}
}
for ( ;; ) {
if (do_write) {
size = b->last - b->pos;
if (size && dst->write->ready) {
n = dst->send(dst, b->pos, size);
if (n == NGX_ERROR) {
ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
return;
}
if (n > 0) {
b->pos += n;
if (b->pos == b->last) {
b->pos = b->start;
b->last = b->start;
}
}
}
}
size = b->end - b->last;
if (size && src->read->ready) {
n = src->recv(src, b->last, size);
if (n == NGX_AGAIN || n == 0) {
break;
}
if (n > 0) {
do_write = 1;
b->last += n;
if (from_upstream) {
u->state->bytes_received += n;
}
continue;
}
if (n == NGX_ERROR) {
src->read->eof = 1;
}
}
break;
}
if ((upstream->read->eof && u->buffer.pos == u->buffer.last)
|| (downstream->read->eof && u->from_client.pos == u->from_client.last)
|| (downstream->read->eof && upstream->read->eof))
{
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
"http upstream upgraded done");
ngx_http_upstream_finalize_request(r, u, 0);
return;
}
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
if (ngx_handle_write_event(upstream->write, u->conf->send_lowat)
!= NGX_OK)
{
ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
return;
}
if (upstream->write->active && !upstream->write->ready) {
ngx_add_timer(upstream->write, u->conf->send_timeout);
} else if (upstream->write->timer_set) {
ngx_del_timer(upstream->write);
}
if (ngx_handle_read_event(upstream->read, 0) != NGX_OK) {
ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
return;
}
if (upstream->read->active && !upstream->read->ready) {
ngx_add_timer(upstream->read, u->conf->read_timeout);
} else if (upstream->read->timer_set) {
ngx_del_timer(upstream->read);
}
if (ngx_handle_write_event(downstream->write, clcf->send_lowat)
!= NGX_OK)
{
ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
return;
}
if (ngx_handle_read_event(downstream->read, 0) != NGX_OK) {
ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
return;
}
if (downstream->write->active && !downstream->write->ready) {
ngx_add_timer(downstream->write, clcf->send_timeout);
} else if (downstream->write->timer_set) {
ngx_del_timer(downstream->write);
}
}
static void
ngx_http_upstream_process_non_buffered_downstream(ngx_http_request_t *r)
{
ngx_event_t *wev;
ngx_connection_t *c;
ngx_http_upstream_t *u;
c = r->connection;
u = r->upstream;
wev = c->write;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
"http upstream process non buffered downstream");
c->log->action = "sending to client";
if (wev->timedout) {
c->timedout = 1;
ngx_connection_error(c, NGX_ETIMEDOUT, "client timed out");
ngx_http_upstream_finalize_request(r, u, NGX_HTTP_REQUEST_TIME_OUT);
return;
}
ngx_http_upstream_process_non_buffered_request(r, 1);
}
static void
ngx_http_upstream_process_non_buffered_upstream(ngx_http_request_t *r,
ngx_http_upstream_t *u)
{
ngx_connection_t *c;
c = u->peer.connection;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
"http upstream process non buffered upstream");
c->log->action = "reading upstream";
if (c->read->timedout) {
ngx_connection_error(c, NGX_ETIMEDOUT, "upstream timed out");
ngx_http_upstream_finalize_request(r, u, NGX_HTTP_GATEWAY_TIME_OUT);
return;
}
ngx_http_upstream_process_non_buffered_request(r, 0);
}
static void
ngx_http_upstream_process_non_buffered_request(ngx_http_request_t *r,
ngx_uint_t do_write)
{
size_t size;
ssize_t n;
ngx_buf_t *b;
ngx_int_t rc;
ngx_connection_t *downstream, *upstream;
ngx_http_upstream_t *u;
ngx_http_core_loc_conf_t *clcf;
u = r->upstream;
downstream = r->connection;
upstream = u->peer.connection;
b = &u->buffer;
do_write = do_write || u->length == 0;
for ( ;; ) {
if (do_write) {
if (u->out_bufs || u->busy_bufs || downstream->buffered) {
rc = ngx_http_output_filter(r, u->out_bufs);
if (rc == NGX_ERROR) {
ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
return;
}
ngx_chain_update_chains(r->pool, &u->free_bufs, &u->busy_bufs,
&u->out_bufs, u->output.tag);
}
if (u->busy_bufs == NULL) {
if (u->length == 0
|| (upstream->read->eof && u->length == -1))
{
ngx_http_upstream_finalize_request(r, u, 0);
return;
}
if (upstream->read->eof) {
ngx_log_error(NGX_LOG_ERR, upstream->log, 0,
"upstream prematurely closed connection");
ngx_http_upstream_finalize_request(r, u,
NGX_HTTP_BAD_GATEWAY);
return;
}
if (upstream->read->error) {
ngx_http_upstream_finalize_request(r, u,
NGX_HTTP_BAD_GATEWAY);
return;
}
b->pos = b->start;
b->last = b->start;
}
}
size = b->end - b->last;
if (size && upstream->read->ready) {
n = upstream->recv(upstream, b->last, size);
if (n == NGX_AGAIN) {
break;
}
if (n > 0) {
u->state->bytes_received += n;
u->state->response_length += n;
if (u->input_filter(u->input_filter_ctx, n) == NGX_ERROR) {
ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
return;
}
}
do_write = 1;
continue;
}
break;
}
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
if (downstream->data == r) {
if (ngx_handle_write_event(downstream->write, clcf->send_lowat)
!= NGX_OK)
{
ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
return;
}
}
if (downstream->write->active && !downstream->write->ready) {
ngx_add_timer(downstream->write, clcf->send_timeout);
} else if (downstream->write->timer_set) {
ngx_del_timer(downstream->write);
}
if (ngx_handle_read_event(upstream->read, 0) != NGX_OK) {
ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
return;
}
if (upstream->read->active && !upstream->read->ready) {
ngx_add_timer(upstream->read, u->conf->read_timeout);
} else if (upstream->read->timer_set) {
ngx_del_timer(upstream->read);
}
}
static ngx_int_t
ngx_http_upstream_non_buffered_filter_init(void *data)
{
return NGX_OK;
}
static ngx_int_t
ngx_http_upstream_non_buffered_filter(void *data, ssize_t bytes)
{
ngx_http_request_t *r = data;
ngx_buf_t *b;
ngx_chain_t *cl, **ll;
ngx_http_upstream_t *u;
u = r->upstream;
for (cl = u->out_bufs, ll = &u->out_bufs; cl; cl = cl->next) {
ll = &cl->next;
}
cl = ngx_chain_get_free_buf(r->pool, &u->free_bufs);
if (cl == NULL) {
return NGX_ERROR;
}
*ll = cl;
cl->buf->flush = 1;
cl->buf->memory = 1;
b = &u->buffer;
cl->buf->pos = b->last;
b->last += bytes;
cl->buf->last = b->last;
cl->buf->tag = u->output.tag;
if (u->length == -1) {
return NGX_OK;
}
u->length -= bytes;
return NGX_OK;
}
#if (NGX_THREADS)
static ngx_int_t
ngx_http_upstream_thread_handler(ngx_thread_task_t *task, ngx_file_t *file)
{
ngx_str_t name;
ngx_event_pipe_t *p;
ngx_thread_pool_t *tp;
ngx_http_request_t *r;
ngx_http_core_loc_conf_t *clcf;
r = file->thread_ctx;
p = r->upstream->pipe;
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
tp = clcf->thread_pool;
if (tp == NULL) {
if (ngx_http_complex_value(r, clcf->thread_pool_value, &name)
!= NGX_OK)
{
return NGX_ERROR;
}
tp = ngx_thread_pool_get((ngx_cycle_t *) ngx_cycle, &name);
if (tp == NULL) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"thread pool \"%V\" not found", &name);
return NGX_ERROR;
}
}
task->event.data = r;
task->event.handler = ngx_http_upstream_thread_event_handler;
if (ngx_thread_task_post(tp, task) != NGX_OK) {
return NGX_ERROR;
}
r->main->blocked++;
r->aio = 1;
p->aio = 1;
return NGX_OK;
}
static void
ngx_http_upstream_thread_event_handler(ngx_event_t *ev)
{
ngx_connection_t *c;
ngx_http_request_t *r;
r = ev->data;
c = r->connection;
ngx_http_set_log_request(c->log, r);
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
"http upstream thread: \"%V?%V\"", &r->uri, &r->args);
r->main->blocked--;
r->aio = 0;
if (r->done) {
/*
* trigger connection event handler if the subrequest was
* already finalized; this can happen if the handler is used
* for sendfile() in threads
*/
c->write->handler(c->write);
} else {
r->write_event_handler(r);
ngx_http_run_posted_requests(c);
}
}
#endif
static ngx_int_t
ngx_http_upstream_output_filter(void *data, ngx_chain_t *chain)
{
ngx_int_t rc;
ngx_event_pipe_t *p;
ngx_http_request_t *r;
r = data;
p = r->upstream->pipe;
rc = ngx_http_output_filter(r, chain);
p->aio = r->aio;
return rc;
}
static void
ngx_http_upstream_process_downstream(ngx_http_request_t *r)
{
ngx_event_t *wev;
ngx_connection_t *c;
ngx_event_pipe_t *p;
ngx_http_upstream_t *u;
c = r->connection;
u = r->upstream;
p = u->pipe;
wev = c->write;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
"http upstream process downstream");
c->log->action = "sending to client";
#if (NGX_THREADS)
p->aio = r->aio;
#endif
if (wev->timedout) {
p->downstream_error = 1;
c->timedout = 1;
ngx_connection_error(c, NGX_ETIMEDOUT, "client timed out");
} else {
if (wev->delayed) {
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
"http downstream delayed");
if (ngx_handle_write_event(wev, p->send_lowat) != NGX_OK) {
ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
}
return;
}
if (ngx_event_pipe(p, 1) == NGX_ABORT) {
ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
return;
}
}
ngx_http_upstream_process_request(r, u);
}
static void
ngx_http_upstream_process_upstream(ngx_http_request_t *r,
ngx_http_upstream_t *u)
{
ngx_event_t *rev;
ngx_event_pipe_t *p;
ngx_connection_t *c;
c = u->peer.connection;
p = u->pipe;
rev = c->read;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
"http upstream process upstream");
c->log->action = "reading upstream";
if (rev->timedout) {
p->upstream_error = 1;
ngx_connection_error(c, NGX_ETIMEDOUT, "upstream timed out");
} else {
if (rev->delayed) {
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
"http upstream delayed");
if (ngx_handle_read_event(rev, 0) != NGX_OK) {
ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
}
return;
}
if (ngx_event_pipe(p, 0) == NGX_ABORT) {
ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
return;
}
}
ngx_http_upstream_process_request(r, u);
}
static void
ngx_http_upstream_process_request(ngx_http_request_t *r,
ngx_http_upstream_t *u)
{
ngx_temp_file_t *tf;
ngx_event_pipe_t *p;
p = u->pipe;
#if (NGX_THREADS)
if (p->writing && !p->aio) {
/*
* make sure to call ngx_event_pipe()
* if there is an incomplete aio write
*/
if (ngx_event_pipe(p, 1) == NGX_ABORT) {
ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
return;
}
}
if (p->writing) {
return;
}
#endif
if (u->peer.connection) {
if (u->store) {
if (p->upstream_eof || p->upstream_done) {
tf = p->temp_file;
if (u->headers_in.status_n == NGX_HTTP_OK
&& (p->upstream_done || p->length == -1)
&& (u->headers_in.content_length_n == -1
|| u->headers_in.content_length_n == tf->offset))
{
ngx_http_upstream_store(r, u);
}
}
}
#if (NGX_HTTP_CACHE)
if (u->cacheable) {
if (p->upstream_done) {
ngx_http_file_cache_update(r, p->temp_file);
} else if (p->upstream_eof) {
tf = p->temp_file;
if (p->length == -1
&& (u->headers_in.content_length_n == -1
|| u->headers_in.content_length_n
== tf->offset - (off_t) r->cache->body_start))
{
ngx_http_file_cache_update(r, tf);
} else {
ngx_http_file_cache_free(r->cache, tf);
}
} else if (p->upstream_error) {
ngx_http_file_cache_free(r->cache, p->temp_file);
}
}
#endif
if (p->upstream_done || p->upstream_eof || p->upstream_error) {
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http upstream exit: %p", p->out);
if (p->upstream_done
|| (p->upstream_eof && p->length == -1))
{
ngx_http_upstream_finalize_request(r, u, 0);
return;
}
if (p->upstream_eof) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream prematurely closed connection");
}
ngx_http_upstream_finalize_request(r, u, NGX_HTTP_BAD_GATEWAY);
return;
}
}
if (p->downstream_error) {
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http upstream downstream error");
if (!u->cacheable && !u->store && u->peer.connection) {
ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
}
}
}
static void
ngx_http_upstream_store(ngx_http_request_t *r, ngx_http_upstream_t *u)
{
size_t root;
time_t lm;
ngx_str_t path;
ngx_temp_file_t *tf;
ngx_ext_rename_file_t ext;
tf = u->pipe->temp_file;
if (tf->file.fd == NGX_INVALID_FILE) {
/* create file for empty 200 response */
tf = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t));
if (tf == NULL) {
return;
}
tf->file.fd = NGX_INVALID_FILE;
tf->file.log = r->connection->log;
tf->path = u->conf->temp_path;
tf->pool = r->pool;
tf->persistent = 1;
if (ngx_create_temp_file(&tf->file, tf->path, tf->pool,
tf->persistent, tf->clean, tf->access)
!= NGX_OK)
{
return;
}
u->pipe->temp_file = tf;
}
ext.access = u->conf->store_access;
ext.path_access = u->conf->store_access;
ext.time = -1;
ext.create_path = 1;
ext.delete_file = 1;
ext.log = r->connection->log;
if (u->headers_in.last_modified) {
lm = ngx_parse_http_time(u->headers_in.last_modified->value.data,
u->headers_in.last_modified->value.len);
if (lm != NGX_ERROR) {
ext.time = lm;
ext.fd = tf->file.fd;
}
}
if (u->conf->store_lengths == NULL) {
if (ngx_http_map_uri_to_path(r, &path, &root, 0) == NULL) {
return;
}
} else {
if (ngx_http_script_run(r, &path, u->conf->store_lengths->elts, 0,
u->conf->store_values->elts)
== NULL)
{
return;
}
}
path.len--;
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"upstream stores \"%s\" to \"%s\"",
tf->file.name.data, path.data);
(void) ngx_ext_rename_file(&tf->file.name, &path, &ext);
u->store = 0;
}
static void
ngx_http_upstream_dummy_handler(ngx_http_request_t *r, ngx_http_upstream_t *u)
{
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http upstream dummy handler");
}
static void
ngx_http_upstream_next(ngx_http_request_t *r, ngx_http_upstream_t *u,
ngx_uint_t ft_type)
{
ngx_msec_t timeout;
ngx_uint_t status, state;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http next upstream, %xi", ft_type);
if (u->peer.sockaddr) {
if (ft_type == NGX_HTTP_UPSTREAM_FT_HTTP_403
|| ft_type == NGX_HTTP_UPSTREAM_FT_HTTP_404)
{
state = NGX_PEER_NEXT;
} else {
state = NGX_PEER_FAILED;
}
u->peer.free(&u->peer, u->peer.data, state);
u->peer.sockaddr = NULL;
}
if (ft_type == NGX_HTTP_UPSTREAM_FT_TIMEOUT) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, NGX_ETIMEDOUT,
"upstream timed out");
}
if (u->peer.cached && ft_type == NGX_HTTP_UPSTREAM_FT_ERROR) {
/* TODO: inform balancer instead */
u->peer.tries++;
}
switch (ft_type) {
case NGX_HTTP_UPSTREAM_FT_TIMEOUT:
case NGX_HTTP_UPSTREAM_FT_HTTP_504:
status = NGX_HTTP_GATEWAY_TIME_OUT;
break;
case NGX_HTTP_UPSTREAM_FT_HTTP_500:
status = NGX_HTTP_INTERNAL_SERVER_ERROR;
break;
case NGX_HTTP_UPSTREAM_FT_HTTP_503:
status = NGX_HTTP_SERVICE_UNAVAILABLE;
break;
case NGX_HTTP_UPSTREAM_FT_HTTP_403:
status = NGX_HTTP_FORBIDDEN;
break;
case NGX_HTTP_UPSTREAM_FT_HTTP_404:
status = NGX_HTTP_NOT_FOUND;
break;
case NGX_HTTP_UPSTREAM_FT_HTTP_429:
status = NGX_HTTP_TOO_MANY_REQUESTS;
break;
/*
* NGX_HTTP_UPSTREAM_FT_BUSY_LOCK and NGX_HTTP_UPSTREAM_FT_MAX_WAITING
* never reach here
*/
default:
status = NGX_HTTP_BAD_GATEWAY;
}
if (r->connection->error) {
ngx_http_upstream_finalize_request(r, u,
NGX_HTTP_CLIENT_CLOSED_REQUEST);
return;
}
u->state->status = status;
timeout = u->conf->next_upstream_timeout;
if (u->request_sent
&& (r->method & (NGX_HTTP_POST|NGX_HTTP_LOCK|NGX_HTTP_PATCH)))
{
ft_type |= NGX_HTTP_UPSTREAM_FT_NON_IDEMPOTENT;
}
if (u->peer.tries == 0
|| ((u->conf->next_upstream & ft_type) != ft_type)
|| (u->request_sent && r->request_body_no_buffering)
|| (timeout && ngx_current_msec - u->peer.start_time >= timeout))
{
#if (NGX_HTTP_CACHE)
if (u->cache_status == NGX_HTTP_CACHE_EXPIRED
&& ((u->conf->cache_use_stale & ft_type) || r->cache->stale_error))
{
ngx_int_t rc;
rc = u->reinit_request(r);
if (rc != NGX_OK) {
ngx_http_upstream_finalize_request(r, u, rc);
return;
}
u->cache_status = NGX_HTTP_CACHE_STALE;
rc = ngx_http_upstream_cache_send(r, u);
if (rc == NGX_DONE) {
return;
}
if (rc == NGX_HTTP_UPSTREAM_INVALID_HEADER) {
rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
}
ngx_http_upstream_finalize_request(r, u, rc);
return;
}
#endif
ngx_http_upstream_finalize_request(r, u, status);
return;
}
if (u->peer.connection) {
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"close http upstream connection: %d",
u->peer.connection->fd);
#if (NGX_HTTP_SSL)
if (u->peer.connection->ssl) {
u->peer.connection->ssl->no_wait_shutdown = 1;
u->peer.connection->ssl->no_send_shutdown = 1;
(void) ngx_ssl_shutdown(u->peer.connection);
}
#endif
if (u->peer.connection->pool) {
ngx_destroy_pool(u->peer.connection->pool);
}
ngx_close_connection(u->peer.connection);
u->peer.connection = NULL;
}
ngx_http_upstream_connect(r, u);
}
static void
ngx_http_upstream_cleanup(void *data)
{
ngx_http_request_t *r = data;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"cleanup http upstream request: \"%V\"", &r->uri);
ngx_http_upstream_finalize_request(r, r->upstream, NGX_DONE);
}
static void
ngx_http_upstream_finalize_request(ngx_http_request_t *r,
ngx_http_upstream_t *u, ngx_int_t rc)
{
ngx_uint_t flush;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"finalize http upstream request: %i", rc);
if (u->cleanup == NULL) {
/* the request was already finalized */
ngx_http_finalize_request(r, NGX_DONE);
return;
}
*u->cleanup = NULL;
u->cleanup = NULL;
if (u->resolved && u->resolved->ctx) {
ngx_resolve_name_done(u->resolved->ctx);
u->resolved->ctx = NULL;
}
if (u->state && u->state->response_time) {
u->state->response_time = ngx_current_msec - u->state->response_time;
if (u->pipe && u->pipe->read_length) {
u->state->bytes_received += u->pipe->read_length
- u->pipe->preread_size;
u->state->response_length = u->pipe->read_length;
}
}
u->finalize_request(r, rc);
if (u->peer.free && u->peer.sockaddr) {
u->peer.free(&u->peer, u->peer.data, 0);
u->peer.sockaddr = NULL;
}
if (u->peer.connection) {
#if (NGX_HTTP_SSL)
/* TODO: do not shutdown persistent connection */
if (u->peer.connection->ssl) {
/*
* We send the "close notify" shutdown alert to the upstream only
* and do not wait its "close notify" shutdown alert.
* It is acceptable according to the TLS standard.
*/
u->peer.connection->ssl->no_wait_shutdown = 1;
(void) ngx_ssl_shutdown(u->peer.connection);
}
#endif
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"close http upstream connection: %d",
u->peer.connection->fd);
if (u->peer.connection->pool) {
ngx_destroy_pool(u->peer.connection->pool);
}
ngx_close_connection(u->peer.connection);
}
u->peer.connection = NULL;
if (u->pipe && u->pipe->temp_file) {
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http upstream temp fd: %d",
u->pipe->temp_file->file.fd);
}
if (u->store && u->pipe && u->pipe->temp_file
&& u->pipe->temp_file->file.fd != NGX_INVALID_FILE)
{
if (ngx_delete_file(u->pipe->temp_file->file.name.data)
== NGX_FILE_ERROR)
{
ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,
ngx_delete_file_n " \"%s\" failed",
u->pipe->temp_file->file.name.data);
}
}
#if (NGX_HTTP_CACHE)
if (r->cache) {
if (u->cacheable) {
if (rc == NGX_HTTP_BAD_GATEWAY || rc == NGX_HTTP_GATEWAY_TIME_OUT) {
time_t valid;
valid = ngx_http_file_cache_valid(u->conf->cache_valid, rc);
if (valid) {
r->cache->valid_sec = ngx_time() + valid;
r->cache->error = rc;
}
}
}
ngx_http_file_cache_free(r->cache, u->pipe->temp_file);
}
#endif
r->read_event_handler = ngx_http_block_reading;
if (rc == NGX_DECLINED) {
return;
}
r->connection->log->action = "sending to client";
if (!u->header_sent
|| rc == NGX_HTTP_REQUEST_TIME_OUT
|| rc == NGX_HTTP_CLIENT_CLOSED_REQUEST)
{
ngx_http_finalize_request(r, rc);
return;
}
flush = 0;
if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
rc = NGX_ERROR;
flush = 1;
}
if (r->header_only
|| (u->pipe && u->pipe->downstream_error))
{
ngx_http_finalize_request(r, rc);
return;
}
if (rc == 0) {
if (ngx_http_upstream_process_trailers(r, u) != NGX_OK) {
ngx_http_finalize_request(r, NGX_ERROR);
return;
}
rc = ngx_http_send_special(r, NGX_HTTP_LAST);
} else if (flush) {
r->keepalive = 0;
rc = ngx_http_send_special(r, NGX_HTTP_FLUSH);
}
ngx_http_finalize_request(r, rc);
}
static ngx_int_t
ngx_http_upstream_process_header_line(ngx_http_request_t *r, ngx_table_elt_t *h,
ngx_uint_t offset)
{
ngx_table_elt_t **ph;
ph = (ngx_table_elt_t **) ((char *) &r->upstream->headers_in + offset);
if (*ph == NULL) {
*ph = h;
}
return NGX_OK;
}
static ngx_int_t
ngx_http_upstream_ignore_header_line(ngx_http_request_t *r, ngx_table_elt_t *h,
ngx_uint_t offset)
{
return NGX_OK;
}
static ngx_int_t
ngx_http_upstream_process_content_length(ngx_http_request_t *r,
ngx_table_elt_t *h, ngx_uint_t offset)
{
ngx_http_upstream_t *u;
u = r->upstream;
u->headers_in.content_length = h;
u->headers_in.content_length_n = ngx_atoof(h->value.data, h->value.len);
return NGX_OK;
}
static ngx_int_t
ngx_http_upstream_process_last_modified(ngx_http_request_t *r,
ngx_table_elt_t *h, ngx_uint_t offset)
{
ngx_http_upstream_t *u;
u = r->upstream;
u->headers_in.last_modified = h;
u->headers_in.last_modified_time = ngx_parse_http_time(h->value.data,
h->value.len);
return NGX_OK;
}
static ngx_int_t
ngx_http_upstream_process_set_cookie(ngx_http_request_t *r, ngx_table_elt_t *h,
ngx_uint_t offset)
{
ngx_array_t *pa;
ngx_table_elt_t **ph;
ngx_http_upstream_t *u;
u = r->upstream;
pa = &u->headers_in.cookies;
if (pa->elts == NULL) {
if (ngx_array_init(pa, r->pool, 1, sizeof(ngx_table_elt_t *)) != NGX_OK)
{
return NGX_ERROR;
}
}
ph = ngx_array_push(pa);
if (ph == NULL) {
return NGX_ERROR;
}
*ph = h;
#if (NGX_HTTP_CACHE)
if (!(u->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_SET_COOKIE)) {
u->cacheable = 0;
}
#endif
return NGX_OK;
}
static ngx_int_t
ngx_http_upstream_process_cache_control(ngx_http_request_t *r,
ngx_table_elt_t *h, ngx_uint_t offset)
{
ngx_array_t *pa;
ngx_table_elt_t **ph;
ngx_http_upstream_t *u;
u = r->upstream;
pa = &u->headers_in.cache_control;
if (pa->elts == NULL) {
if (ngx_array_init(pa, r->pool, 2, sizeof(ngx_table_elt_t *)) != NGX_OK)
{
return NGX_ERROR;
}
}
ph = ngx_array_push(pa);
if (ph == NULL) {
return NGX_ERROR;
}
*ph = h;
#if (NGX_HTTP_CACHE)
{
u_char *p, *start, *last;
ngx_int_t n;
if (u->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_CACHE_CONTROL) {
return NGX_OK;
}
if (r->cache == NULL) {
return NGX_OK;
}
if (r->cache->valid_sec != 0 && u->headers_in.x_accel_expires != NULL) {
return NGX_OK;
}
start = h->value.data;
last = start + h->value.len;
if (ngx_strlcasestrn(start, last, (u_char *) "no-cache", 8 - 1) != NULL
|| ngx_strlcasestrn(start, last, (u_char *) "no-store", 8 - 1) != NULL
|| ngx_strlcasestrn(start, last, (u_char *) "private", 7 - 1) != NULL)
{
u->cacheable = 0;
return NGX_OK;
}
p = ngx_strlcasestrn(start, last, (u_char *) "s-maxage=", 9 - 1);
offset = 9;
if (p == NULL) {
p = ngx_strlcasestrn(start, last, (u_char *) "max-age=", 8 - 1);
offset = 8;
}
if (p) {
n = 0;
for (p += offset; p < last; p++) {
if (*p == ',' || *p == ';' || *p == ' ') {
break;
}
if (*p >= '0' && *p <= '9') {
n = n * 10 + (*p - '0');
continue;
}
u->cacheable = 0;
return NGX_OK;
}
if (n == 0) {
u->cacheable = 0;
return NGX_OK;
}
r->cache->valid_sec = ngx_time() + n;
}
p = ngx_strlcasestrn(start, last, (u_char *) "stale-while-revalidate=",
23 - 1);
if (p) {
n = 0;
for (p += 23; p < last; p++) {
if (*p == ',' || *p == ';' || *p == ' ') {
break;
}
if (*p >= '0' && *p <= '9') {
n = n * 10 + (*p - '0');
continue;
}
u->cacheable = 0;
return NGX_OK;
}
r->cache->updating_sec = n;
r->cache->error_sec = n;
}
p = ngx_strlcasestrn(start, last, (u_char *) "stale-if-error=", 15 - 1);
if (p) {
n = 0;
for (p += 15; p < last; p++) {
if (*p == ',' || *p == ';' || *p == ' ') {
break;
}
if (*p >= '0' && *p <= '9') {
n = n * 10 + (*p - '0');
continue;
}
u->cacheable = 0;
return NGX_OK;
}
r->cache->error_sec = n;
}
}
#endif
return NGX_OK;
}
static ngx_int_t
ngx_http_upstream_process_expires(ngx_http_request_t *r, ngx_table_elt_t *h,
ngx_uint_t offset)
{
ngx_http_upstream_t *u;
u = r->upstream;
u->headers_in.expires = h;
#if (NGX_HTTP_CACHE)
{
time_t expires;
if (u->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_EXPIRES) {
return NGX_OK;
}
if (r->cache == NULL) {
return NGX_OK;
}
if (r->cache->valid_sec != 0) {
return NGX_OK;
}
expires = ngx_parse_http_time(h->value.data, h->value.len);
if (expires == NGX_ERROR || expires < ngx_time()) {
u->cacheable = 0;
return NGX_OK;
}
r->cache->valid_sec = expires;
}
#endif
return NGX_OK;
}
static ngx_int_t
ngx_http_upstream_process_accel_expires(ngx_http_request_t *r,
ngx_table_elt_t *h, ngx_uint_t offset)
{
ngx_http_upstream_t *u;
u = r->upstream;
u->headers_in.x_accel_expires = h;
#if (NGX_HTTP_CACHE)
{
u_char *p;
size_t len;
ngx_int_t n;
if (u->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_XA_EXPIRES) {
return NGX_OK;
}
if (r->cache == NULL) {
return NGX_OK;
}
len = h->value.len;
p = h->value.data;
if (p[0] != '@') {
n = ngx_atoi(p, len);
switch (n) {
case 0:
u->cacheable = 0;
/* fall through */
case NGX_ERROR:
return NGX_OK;
default:
r->cache->valid_sec = ngx_time() + n;
return NGX_OK;
}
}
p++;
len--;
n = ngx_atoi(p, len);
if (n != NGX_ERROR) {
r->cache->valid_sec = n;
}
}
#endif
return NGX_OK;
}
static ngx_int_t
ngx_http_upstream_process_limit_rate(ngx_http_request_t *r, ngx_table_elt_t *h,
ngx_uint_t offset)
{
ngx_int_t n;
ngx_http_upstream_t *u;
u = r->upstream;
u->headers_in.x_accel_limit_rate = h;
if (u->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_XA_LIMIT_RATE) {
return NGX_OK;
}
n = ngx_atoi(h->value.data, h->value.len);
if (n != NGX_ERROR) {
r->limit_rate = (size_t) n;
}
return NGX_OK;
}
static ngx_int_t
ngx_http_upstream_process_buffering(ngx_http_request_t *r, ngx_table_elt_t *h,
ngx_uint_t offset)
{
u_char c0, c1, c2;
ngx_http_upstream_t *u;
u = r->upstream;
if (u->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_XA_BUFFERING) {
return NGX_OK;
}
if (u->conf->change_buffering) {
if (h->value.len == 2) {
c0 = ngx_tolower(h->value.data[0]);
c1 = ngx_tolower(h->value.data[1]);
if (c0 == 'n' && c1 == 'o') {
u->buffering = 0;
}
} else if (h->value.len == 3) {
c0 = ngx_tolower(h->value.data[0]);
c1 = ngx_tolower(h->value.data[1]);
c2 = ngx_tolower(h->value.data[2]);
if (c0 == 'y' && c1 == 'e' && c2 == 's') {
u->buffering = 1;
}
}
}
return NGX_OK;
}
static ngx_int_t
ngx_http_upstream_process_charset(ngx_http_request_t *r, ngx_table_elt_t *h,
ngx_uint_t offset)
{
if (r->upstream->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_XA_CHARSET) {
return NGX_OK;
}
r->headers_out.override_charset = &h->value;
return NGX_OK;
}
static ngx_int_t
ngx_http_upstream_process_connection(ngx_http_request_t *r, ngx_table_elt_t *h,
ngx_uint_t offset)
{
r->upstream->headers_in.connection = h;
if (ngx_strlcasestrn(h->value.data, h->value.data + h->value.len,
(u_char *) "close", 5 - 1)
!= NULL)
{
r->upstream->headers_in.connection_close = 1;
}
return NGX_OK;
}
static ngx_int_t
ngx_http_upstream_process_transfer_encoding(ngx_http_request_t *r,
ngx_table_elt_t *h, ngx_uint_t offset)
{
r->upstream->headers_in.transfer_encoding = h;
if (ngx_strlcasestrn(h->value.data, h->value.data + h->value.len,
(u_char *) "chunked", 7 - 1)
!= NULL)
{
r->upstream->headers_in.chunked = 1;
}
return NGX_OK;
}
static ngx_int_t
ngx_http_upstream_process_vary(ngx_http_request_t *r,
ngx_table_elt_t *h, ngx_uint_t offset)
{
ngx_http_upstream_t *u;
u = r->upstream;
u->headers_in.vary = h;
#if (NGX_HTTP_CACHE)
if (u->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_VARY) {
return NGX_OK;
}
if (r->cache == NULL) {
return NGX_OK;
}
if (h->value.len > NGX_HTTP_CACHE_VARY_LEN
|| (h->value.len == 1 && h->value.data[0] == '*'))
{
u->cacheable = 0;
}
r->cache->vary = h->value;
#endif
return NGX_OK;
}
static ngx_int_t
ngx_http_upstream_copy_header_line(ngx_http_request_t *r, ngx_table_elt_t *h,
ngx_uint_t offset)
{
ngx_table_elt_t *ho, **ph;
ho = ngx_list_push(&r->headers_out.headers);
if (ho == NULL) {
return NGX_ERROR;
}
*ho = *h;
if (offset) {
ph = (ngx_table_elt_t **) ((char *) &r->headers_out + offset);
*ph = ho;
}
return NGX_OK;
}
static ngx_int_t
ngx_http_upstream_copy_multi_header_lines(ngx_http_request_t *r,
ngx_table_elt_t *h, ngx_uint_t offset)
{
ngx_array_t *pa;
ngx_table_elt_t *ho, **ph;
pa = (ngx_array_t *) ((char *) &r->headers_out + offset);
if (pa->elts == NULL) {
if (ngx_array_init(pa, r->pool, 2, sizeof(ngx_table_elt_t *)) != NGX_OK)
{
return NGX_ERROR;
}
}
ho = ngx_list_push(&r->headers_out.headers);
if (ho == NULL) {
return NGX_ERROR;
}
*ho = *h;
ph = ngx_array_push(pa);
if (ph == NULL) {
return NGX_ERROR;
}
*ph = ho;
return NGX_OK;
}
static ngx_int_t
ngx_http_upstream_copy_content_type(ngx_http_request_t *r, ngx_table_elt_t *h,
ngx_uint_t offset)
{
u_char *p, *last;
r->headers_out.content_type_len = h->value.len;
r->headers_out.content_type = h->value;
r->headers_out.content_type_lowcase = NULL;
for (p = h->value.data; *p; p++) {
if (*p != ';') {
continue;
}
last = p;
while (*++p == ' ') { /* void */ }
if (*p == '\0') {
return NGX_OK;
}
if (ngx_strncasecmp(p, (u_char *) "charset=", 8) != 0) {
continue;
}
p += 8;
r->headers_out.content_type_len = last - h->value.data;
if (*p == '"') {
p++;
}
last = h->value.data + h->value.len;
if (*(last - 1) == '"') {
last--;
}
r->headers_out.charset.len = last - p;
r->headers_out.charset.data = p;
return NGX_OK;
}
return NGX_OK;
}
static ngx_int_t
ngx_http_upstream_copy_last_modified(ngx_http_request_t *r, ngx_table_elt_t *h,
ngx_uint_t offset)
{
ngx_table_elt_t *ho;
ho = ngx_list_push(&r->headers_out.headers);
if (ho == NULL) {
return NGX_ERROR;
}
*ho = *h;
r->headers_out.last_modified = ho;
r->headers_out.last_modified_time =
r->upstream->headers_in.last_modified_time;
return NGX_OK;
}
static ngx_int_t
ngx_http_upstream_rewrite_location(ngx_http_request_t *r, ngx_table_elt_t *h,
ngx_uint_t offset)
{
ngx_int_t rc;
ngx_table_elt_t *ho;
ho = ngx_list_push(&r->headers_out.headers);
if (ho == NULL) {
return NGX_ERROR;
}
*ho = *h;
if (r->upstream->rewrite_redirect) {
rc = r->upstream->rewrite_redirect(r, ho, 0);
if (rc == NGX_DECLINED) {
return NGX_OK;
}
if (rc == NGX_OK) {
r->headers_out.location = ho;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"rewritten location: \"%V\"", &ho->value);
}
return rc;
}
if (ho->value.data[0] != '/') {
r->headers_out.location = ho;
}
/*
* we do not set r->headers_out.location here to avoid handling
* relative redirects in ngx_http_header_filter()
*/
return NGX_OK;
}
static ngx_int_t
ngx_http_upstream_rewrite_refresh(ngx_http_request_t *r, ngx_table_elt_t *h,
ngx_uint_t offset)
{
u_char *p;
ngx_int_t rc;
ngx_table_elt_t *ho;
ho = ngx_list_push(&r->headers_out.headers);
if (ho == NULL) {
return NGX_ERROR;
}
*ho = *h;
if (r->upstream->rewrite_redirect) {
p = ngx_strcasestrn(ho->value.data, "url=", 4 - 1);
if (p) {
rc = r->upstream->rewrite_redirect(r, ho, p + 4 - ho->value.data);
} else {
return NGX_OK;
}
if (rc == NGX_DECLINED) {
return NGX_OK;
}
if (rc == NGX_OK) {
r->headers_out.refresh = ho;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"rewritten refresh: \"%V\"", &ho->value);
}
return rc;
}
r->headers_out.refresh = ho;
return NGX_OK;
}
static ngx_int_t
ngx_http_upstream_rewrite_set_cookie(ngx_http_request_t *r, ngx_table_elt_t *h,
ngx_uint_t offset)
{
ngx_int_t rc;
ngx_table_elt_t *ho;
ho = ngx_list_push(&r->headers_out.headers);
if (ho == NULL) {
return NGX_ERROR;
}
*ho = *h;
if (r->upstream->rewrite_cookie) {
rc = r->upstream->rewrite_cookie(r, ho);
if (rc == NGX_DECLINED) {
return NGX_OK;
}
#if (NGX_DEBUG)
if (rc == NGX_OK) {
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"rewritten cookie: \"%V\"", &ho->value);
}
#endif
return rc;
}
return NGX_OK;
}
static ngx_int_t
ngx_http_upstream_copy_allow_ranges(ngx_http_request_t *r,
ngx_table_elt_t *h, ngx_uint_t offset)
{
ngx_table_elt_t *ho;
if (r->upstream->conf->force_ranges) {
return NGX_OK;
}
#if (NGX_HTTP_CACHE)
if (r->cached) {
r->allow_ranges = 1;
return NGX_OK;
}
if (r->upstream->cacheable) {
r->allow_ranges = 1;
r->single_range = 1;
return NGX_OK;
}
#endif
ho = ngx_list_push(&r->headers_out.headers);
if (ho == NULL) {
return NGX_ERROR;
}
*ho = *h;
r->headers_out.accept_ranges = ho;
return NGX_OK;
}
#if (NGX_HTTP_GZIP)
static ngx_int_t
ngx_http_upstream_copy_content_encoding(ngx_http_request_t *r,
ngx_table_elt_t *h, ngx_uint_t offset)
{
ngx_table_elt_t *ho;
ho = ngx_list_push(&r->headers_out.headers);
if (ho == NULL) {
return NGX_ERROR;
}
*ho = *h;
r->headers_out.content_encoding = ho;
return NGX_OK;
}
#endif
static ngx_int_t
ngx_http_upstream_add_variables(ngx_conf_t *cf)
{
ngx_http_variable_t *var, *v;
for (v = ngx_http_upstream_vars; v->name.len; v++) {
var = ngx_http_add_variable(cf, &v->name, v->flags);
if (var == NULL) {
return NGX_ERROR;
}
var->get_handler = v->get_handler;
var->data = v->data;
}
return NGX_OK;
}
static ngx_int_t
ngx_http_upstream_addr_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
u_char *p;
size_t len;
ngx_uint_t i;
ngx_http_upstream_state_t *state;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
if (r->upstream_states == NULL || r->upstream_states->nelts == 0) {
v->not_found = 1;
return NGX_OK;
}
len = 0;
state = r->upstream_states->elts;
for (i = 0; i < r->upstream_states->nelts; i++) {
if (state[i].peer) {
len += state[i].peer->len + 2;
} else {
len += 3;
}
}
p = ngx_pnalloc(r->pool, len);
if (p == NULL) {
return NGX_ERROR;
}
v->data = p;
i = 0;
for ( ;; ) {
if (state[i].peer) {
p = ngx_cpymem(p, state[i].peer->data, state[i].peer->len);
}
if (++i == r->upstream_states->nelts) {
break;
}
if (state[i].peer) {
*p++ = ',';
*p++ = ' ';
} else {
*p++ = ' ';
*p++ = ':';
*p++ = ' ';
if (++i == r->upstream_states->nelts) {
break;
}
continue;
}
}
v->len = p - v->data;
return NGX_OK;
}
static ngx_int_t
ngx_http_upstream_status_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
u_char *p;
size_t len;
ngx_uint_t i;
ngx_http_upstream_state_t *state;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
if (r->upstream_states == NULL || r->upstream_states->nelts == 0) {
v->not_found = 1;
return NGX_OK;
}
len = r->upstream_states->nelts * (3 + 2);
p = ngx_pnalloc(r->pool, len);
if (p == NULL) {
return NGX_ERROR;
}
v->data = p;
i = 0;
state = r->upstream_states->elts;
for ( ;; ) {
if (state[i].status) {
p = ngx_sprintf(p, "%ui", state[i].status);
} else {
*p++ = '-';
}
if (++i == r->upstream_states->nelts) {
break;
}
if (state[i].peer) {
*p++ = ',';
*p++ = ' ';
} else {
*p++ = ' ';
*p++ = ':';
*p++ = ' ';
if (++i == r->upstream_states->nelts) {
break;
}
continue;
}
}
v->len = p - v->data;
return NGX_OK;
}
static ngx_int_t
ngx_http_upstream_response_time_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
u_char *p;
size_t len;
ngx_uint_t i;
ngx_msec_int_t ms;
ngx_http_upstream_state_t *state;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
if (r->upstream_states == NULL || r->upstream_states->nelts == 0) {
v->not_found = 1;
return NGX_OK;
}
len = r->upstream_states->nelts * (NGX_TIME_T_LEN + 4 + 2);
p = ngx_pnalloc(r->pool, len);
if (p == NULL) {
return NGX_ERROR;
}
v->data = p;
i = 0;
state = r->upstream_states->elts;
for ( ;; ) {
if (state[i].status) {
if (data == 1 && state[i].header_time != (ngx_msec_t) -1) {
ms = state[i].header_time;
} else if (data == 2 && state[i].connect_time != (ngx_msec_t) -1) {
ms = state[i].connect_time;
} else {
ms = state[i].response_time;
}
ms = ngx_max(ms, 0);
p = ngx_sprintf(p, "%T.%03M", (time_t) ms / 1000, ms % 1000);
} else {
*p++ = '-';
}
if (++i == r->upstream_states->nelts) {
break;
}
if (state[i].peer) {
*p++ = ',';
*p++ = ' ';
} else {
*p++ = ' ';
*p++ = ':';
*p++ = ' ';
if (++i == r->upstream_states->nelts) {
break;
}
continue;
}
}
v->len = p - v->data;
return NGX_OK;
}
static ngx_int_t
ngx_http_upstream_response_length_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
u_char *p;
size_t len;
ngx_uint_t i;
ngx_http_upstream_state_t *state;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
if (r->upstream_states == NULL || r->upstream_states->nelts == 0) {
v->not_found = 1;
return NGX_OK;
}
len = r->upstream_states->nelts * (NGX_OFF_T_LEN + 2);
p = ngx_pnalloc(r->pool, len);
if (p == NULL) {
return NGX_ERROR;
}
v->data = p;
i = 0;
state = r->upstream_states->elts;
for ( ;; ) {
if (data == 1) {
p = ngx_sprintf(p, "%O", state[i].bytes_received);
} else {
p = ngx_sprintf(p, "%O", state[i].response_length);
}
if (++i == r->upstream_states->nelts) {
break;
}
if (state[i].peer) {
*p++ = ',';
*p++ = ' ';
} else {
*p++ = ' ';
*p++ = ':';
*p++ = ' ';
if (++i == r->upstream_states->nelts) {
break;
}
continue;
}
}
v->len = p - v->data;
return NGX_OK;
}
static ngx_int_t
ngx_http_upstream_header_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
if (r->upstream == NULL) {
v->not_found = 1;
return NGX_OK;
}
return ngx_http_variable_unknown_header(v, (ngx_str_t *) data,
&r->upstream->headers_in.headers.part,
sizeof("upstream_http_") - 1);
}
static ngx_int_t
ngx_http_upstream_trailer_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
if (r->upstream == NULL) {
v->not_found = 1;
return NGX_OK;
}
return ngx_http_variable_unknown_header(v, (ngx_str_t *) data,
&r->upstream->headers_in.trailers.part,
sizeof("upstream_trailer_") - 1);
}
static ngx_int_t
ngx_http_upstream_cookie_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
ngx_str_t *name = (ngx_str_t *) data;
ngx_str_t cookie, s;
if (r->upstream == NULL) {
v->not_found = 1;
return NGX_OK;
}
s.len = name->len - (sizeof("upstream_cookie_") - 1);
s.data = name->data + sizeof("upstream_cookie_") - 1;
if (ngx_http_parse_set_cookie_lines(&r->upstream->headers_in.cookies,
&s, &cookie)
== NGX_DECLINED)
{
v->not_found = 1;
return NGX_OK;
}
v->len = cookie.len;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = cookie.data;
return NGX_OK;
}
#if (NGX_HTTP_CACHE)
static ngx_int_t
ngx_http_upstream_cache_status(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
ngx_uint_t n;
if (r->upstream == NULL || r->upstream->cache_status == 0) {
v->not_found = 1;
return NGX_OK;
}
n = r->upstream->cache_status - 1;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->len = ngx_http_cache_status[n].len;
v->data = ngx_http_cache_status[n].data;
return NGX_OK;
}
static ngx_int_t
ngx_http_upstream_cache_last_modified(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
u_char *p;
if (r->upstream == NULL
|| !r->upstream->conf->cache_revalidate
|| r->upstream->cache_status != NGX_HTTP_CACHE_EXPIRED
|| r->cache->last_modified == -1)
{
v->not_found = 1;
return NGX_OK;
}
p = ngx_pnalloc(r->pool, sizeof("Mon, 28 Sep 1970 06:00:00 GMT") - 1);
if (p == NULL) {
return NGX_ERROR;
}
v->len = ngx_http_time(p, r->cache->last_modified) - p;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = p;
return NGX_OK;
}
static ngx_int_t
ngx_http_upstream_cache_etag(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
if (r->upstream == NULL
|| !r->upstream->conf->cache_revalidate
|| r->upstream->cache_status != NGX_HTTP_CACHE_EXPIRED
|| r->cache->etag.len == 0)
{
v->not_found = 1;
return NGX_OK;
}
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->len = r->cache->etag.len;
v->data = r->cache->etag.data;
return NGX_OK;
}
#endif
static char *
ngx_http_upstream(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy)
{
char *rv;
void *mconf;
ngx_str_t *value;
ngx_url_t u;
ngx_uint_t m;
ngx_conf_t pcf;
ngx_http_module_t *module;
ngx_http_conf_ctx_t *ctx, *http_ctx;
ngx_http_upstream_srv_conf_t *uscf;
ngx_memzero(&u, sizeof(ngx_url_t));
value = cf->args->elts;
u.host = value[1];
u.no_resolve = 1;
u.no_port = 1;
uscf = ngx_http_upstream_add(cf, &u, NGX_HTTP_UPSTREAM_CREATE
|NGX_HTTP_UPSTREAM_WEIGHT
|NGX_HTTP_UPSTREAM_MAX_CONNS
|NGX_HTTP_UPSTREAM_MAX_FAILS
|NGX_HTTP_UPSTREAM_FAIL_TIMEOUT
|NGX_HTTP_UPSTREAM_DOWN
|NGX_HTTP_UPSTREAM_BACKUP);
if (uscf == NULL) {
return NGX_CONF_ERROR;
}
ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));
if (ctx == NULL) {
return NGX_CONF_ERROR;
}
http_ctx = cf->ctx;
ctx->main_conf = http_ctx->main_conf;
/* the upstream{}'s srv_conf */
ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
if (ctx->srv_conf == NULL) {
return NGX_CONF_ERROR;
}
ctx->srv_conf[ngx_http_upstream_module.ctx_index] = uscf;
uscf->srv_conf = ctx->srv_conf;
/* the upstream{}'s loc_conf */
ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
if (ctx->loc_conf == NULL) {
return NGX_CONF_ERROR;
}
for (m = 0; cf->cycle->modules[m]; m++) {
if (cf->cycle->modules[m]->type != NGX_HTTP_MODULE) {
continue;
}
module = cf->cycle->modules[m]->ctx;
if (module->create_srv_conf) {
mconf = module->create_srv_conf(cf);
if (mconf == NULL) {
return NGX_CONF_ERROR;
}
ctx->srv_conf[cf->cycle->modules[m]->ctx_index] = mconf;
}
if (module->create_loc_conf) {
mconf = module->create_loc_conf(cf);
if (mconf == NULL) {
return NGX_CONF_ERROR;
}
ctx->loc_conf[cf->cycle->modules[m]->ctx_index] = mconf;
}
}
uscf->servers = ngx_array_create(cf->pool, 4,
sizeof(ngx_http_upstream_server_t));
if (uscf->servers == NULL) {
return NGX_CONF_ERROR;
}
/* parse inside upstream{} */
pcf = *cf;
cf->ctx = ctx;
cf->cmd_type = NGX_HTTP_UPS_CONF;
rv = ngx_conf_parse(cf, NULL);
*cf = pcf;
if (rv != NGX_CONF_OK) {
return rv;
}
if (uscf->servers->nelts == 0) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"no servers are inside upstream");
return NGX_CONF_ERROR;
}
return rv;
}
static char *
ngx_http_upstream_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_upstream_srv_conf_t *uscf = conf;
time_t fail_timeout;
ngx_str_t *value, s;
ngx_url_t u;
ngx_int_t weight, max_conns, max_fails;
ngx_uint_t i;
ngx_http_upstream_server_t *us;
us = ngx_array_push(uscf->servers);
if (us == NULL) {
return NGX_CONF_ERROR;
}
ngx_memzero(us, sizeof(ngx_http_upstream_server_t));
value = cf->args->elts;
weight = 1;
max_conns = 0;
max_fails = 1;
fail_timeout = 10;
for (i = 2; i < cf->args->nelts; i++) {
if (ngx_strncmp(value[i].data, "weight=", 7) == 0) {
if (!(uscf->flags & NGX_HTTP_UPSTREAM_WEIGHT)) {
goto not_supported;
}
weight = ngx_atoi(&value[i].data[7], value[i].len - 7);
if (weight == NGX_ERROR || weight == 0) {
goto invalid;
}
continue;
}
if (ngx_strncmp(value[i].data, "max_conns=", 10) == 0) {
if (!(uscf->flags & NGX_HTTP_UPSTREAM_MAX_CONNS)) {
goto not_supported;
}
max_conns = ngx_atoi(&value[i].data[10], value[i].len - 10);
if (max_conns == NGX_ERROR) {
goto invalid;
}
continue;
}
if (ngx_strncmp(value[i].data, "max_fails=", 10) == 0) {
if (!(uscf->flags & NGX_HTTP_UPSTREAM_MAX_FAILS)) {
goto not_supported;
}
max_fails = ngx_atoi(&value[i].data[10], value[i].len - 10);
if (max_fails == NGX_ERROR) {
goto invalid;
}
continue;
}
if (ngx_strncmp(value[i].data, "fail_timeout=", 13) == 0) {
if (!(uscf->flags & NGX_HTTP_UPSTREAM_FAIL_TIMEOUT)) {
goto not_supported;
}
s.len = value[i].len - 13;
s.data = &value[i].data[13];
fail_timeout = ngx_parse_time(&s, 1);
if (fail_timeout == (time_t) NGX_ERROR) {
goto invalid;
}
continue;
}
if (ngx_strcmp(value[i].data, "backup") == 0) {
if (!(uscf->flags & NGX_HTTP_UPSTREAM_BACKUP)) {
goto not_supported;
}
us->backup = 1;
continue;
}
if (ngx_strcmp(value[i].data, "down") == 0) {
if (!(uscf->flags & NGX_HTTP_UPSTREAM_DOWN)) {
goto not_supported;
}
us->down = 1;
continue;
}
goto invalid;
}
ngx_memzero(&u, sizeof(ngx_url_t));
u.url = value[1];
u.default_port = 80;
if (ngx_parse_url(cf->pool, &u) != NGX_OK) {
if (u.err) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"%s in upstream \"%V\"", u.err, &u.url);
}
return NGX_CONF_ERROR;
}
us->name = u.url;
us->addrs = u.addrs;
us->naddrs = u.naddrs;
us->weight = weight;
us->max_conns = max_conns;
us->max_fails = max_fails;
us->fail_timeout = fail_timeout;
return NGX_CONF_OK;
invalid:
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid parameter \"%V\"", &value[i]);
return NGX_CONF_ERROR;
not_supported:
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"balancing method does not support parameter \"%V\"",
&value[i]);
return NGX_CONF_ERROR;
}
ngx_http_upstream_srv_conf_t *
ngx_http_upstream_add(ngx_conf_t *cf, ngx_url_t *u, ngx_uint_t flags)
{
ngx_uint_t i;
ngx_http_upstream_server_t *us;
ngx_http_upstream_srv_conf_t *uscf, **uscfp;
ngx_http_upstream_main_conf_t *umcf;
if (!(flags & NGX_HTTP_UPSTREAM_CREATE)) {
if (ngx_parse_url(cf->pool, u) != NGX_OK) {
if (u->err) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"%s in upstream \"%V\"", u->err, &u->url);
}
return NULL;
}
}
umcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_upstream_module);
uscfp = umcf->upstreams.elts;
for (i = 0; i < umcf->upstreams.nelts; i++) {
if (uscfp[i]->host.len != u->host.len
|| ngx_strncasecmp(uscfp[i]->host.data, u->host.data, u->host.len)
!= 0)
{
continue;
}
if ((flags & NGX_HTTP_UPSTREAM_CREATE)
&& (uscfp[i]->flags & NGX_HTTP_UPSTREAM_CREATE))
{
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"duplicate upstream \"%V\"", &u->host);
return NULL;
}
if ((uscfp[i]->flags & NGX_HTTP_UPSTREAM_CREATE) && !u->no_port) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"upstream \"%V\" may not have port %d",
&u->host, u->port);
return NULL;
}
if ((flags & NGX_HTTP_UPSTREAM_CREATE) && !uscfp[i]->no_port) {
ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
"upstream \"%V\" may not have port %d in %s:%ui",
&u->host, uscfp[i]->port,
uscfp[i]->file_name, uscfp[i]->line);
return NULL;
}
if (uscfp[i]->port && u->port
&& uscfp[i]->port != u->port)
{
continue;
}
if (flags & NGX_HTTP_UPSTREAM_CREATE) {
uscfp[i]->flags = flags;
uscfp[i]->port = 0;
}
return uscfp[i];
}
uscf = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_srv_conf_t));
if (uscf == NULL) {
return NULL;
}
uscf->flags = flags;
uscf->host = u->host;
uscf->file_name = cf->conf_file->file.name.data;
uscf->line = cf->conf_file->line;
uscf->port = u->port;
uscf->no_port = u->no_port;
if (u->naddrs == 1 && (u->port || u->family == AF_UNIX)) {
uscf->servers = ngx_array_create(cf->pool, 1,
sizeof(ngx_http_upstream_server_t));
if (uscf->servers == NULL) {
return NULL;
}
us = ngx_array_push(uscf->servers);
if (us == NULL) {
return NULL;
}
ngx_memzero(us, sizeof(ngx_http_upstream_server_t));
us->addrs = u->addrs;
us->naddrs = 1;
}
uscfp = ngx_array_push(&umcf->upstreams);
if (uscfp == NULL) {
return NULL;
}
*uscfp = uscf;
return uscf;
}
char *
ngx_http_upstream_bind_set_slot(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf)
{
char *p = conf;
ngx_int_t rc;
ngx_str_t *value;
ngx_http_complex_value_t cv;
ngx_http_upstream_local_t **plocal, *local;
ngx_http_compile_complex_value_t ccv;
plocal = (ngx_http_upstream_local_t **) (p + cmd->offset);
if (*plocal != NGX_CONF_UNSET_PTR) {
return "is duplicate";
}
value = cf->args->elts;
if (cf->args->nelts == 2 && ngx_strcmp(value[1].data, "off") == 0) {
*plocal = NULL;
return NGX_CONF_OK;
}
ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
ccv.cf = cf;
ccv.value = &value[1];
ccv.complex_value = &cv;
if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
return NGX_CONF_ERROR;
}
local = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_local_t));
if (local == NULL) {
return NGX_CONF_ERROR;
}
*plocal = local;
if (cv.lengths) {
local->value = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t));
if (local->value == NULL) {
return NGX_CONF_ERROR;
}
*local->value = cv;
} else {
local->addr = ngx_palloc(cf->pool, sizeof(ngx_addr_t));
if (local->addr == NULL) {
return NGX_CONF_ERROR;
}
rc = ngx_parse_addr_port(cf->pool, local->addr, value[1].data,
value[1].len);
switch (rc) {
case NGX_OK:
local->addr->name = value[1];
break;
case NGX_DECLINED:
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid address \"%V\"", &value[1]);
/* fall through */
default:
return NGX_CONF_ERROR;
}
}
if (cf->args->nelts > 2) {
if (ngx_strcmp(value[2].data, "transparent") == 0) {
#if (NGX_HAVE_TRANSPARENT_PROXY)
ngx_core_conf_t *ccf;
ccf = (ngx_core_conf_t *) ngx_get_conf(cf->cycle->conf_ctx,
ngx_core_module);
ccf->transparent = 1;
local->transparent = 1;
#else
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"transparent proxying is not supported "
"on this platform, ignored");
#endif
} else {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid parameter \"%V\"", &value[2]);
return NGX_CONF_ERROR;
}
}
return NGX_CONF_OK;
}
static ngx_int_t
ngx_http_upstream_set_local(ngx_http_request_t *r, ngx_http_upstream_t *u,
ngx_http_upstream_local_t *local)
{
ngx_int_t rc;
ngx_str_t val;
ngx_addr_t *addr;
if (local == NULL) {
u->peer.local = NULL;
return NGX_OK;
}
#if (NGX_HAVE_TRANSPARENT_PROXY)
u->peer.transparent = local->transparent;
#endif
if (local->value == NULL) {
u->peer.local = local->addr;
return NGX_OK;
}
if (ngx_http_complex_value(r, local->value, &val) != NGX_OK) {
return NGX_ERROR;
}
if (val.len == 0) {
return NGX_OK;
}
addr = ngx_palloc(r->pool, sizeof(ngx_addr_t));
if (addr == NULL) {
return NGX_ERROR;
}
rc = ngx_parse_addr_port(r->pool, addr, val.data, val.len);
if (rc == NGX_ERROR) {
return NGX_ERROR;
}
if (rc != NGX_OK) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"invalid local address \"%V\"", &val);
return NGX_OK;
}
addr->name = val;
u->peer.local = addr;
return NGX_OK;
}
char *
ngx_http_upstream_param_set_slot(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf)
{
char *p = conf;
ngx_str_t *value;
ngx_array_t **a;
ngx_http_upstream_param_t *param;
a = (ngx_array_t **) (p + cmd->offset);
if (*a == NULL) {
*a = ngx_array_create(cf->pool, 4, sizeof(ngx_http_upstream_param_t));
if (*a == NULL) {
return NGX_CONF_ERROR;
}
}
param = ngx_array_push(*a);
if (param == NULL) {
return NGX_CONF_ERROR;
}
value = cf->args->elts;
param->key = value[1];
param->value = value[2];
param->skip_empty = 0;
if (cf->args->nelts == 4) {
if (ngx_strcmp(value[3].data, "if_not_empty") != 0) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid parameter \"%V\"", &value[3]);
return NGX_CONF_ERROR;
}
param->skip_empty = 1;
}
return NGX_CONF_OK;
}
ngx_int_t
ngx_http_upstream_hide_headers_hash(ngx_conf_t *cf,
ngx_http_upstream_conf_t *conf, ngx_http_upstream_conf_t *prev,
ngx_str_t *default_hide_headers, ngx_hash_init_t *hash)
{
ngx_str_t *h;
ngx_uint_t i, j;
ngx_array_t hide_headers;
ngx_hash_key_t *hk;
if (conf->hide_headers == NGX_CONF_UNSET_PTR
&& conf->pass_headers == NGX_CONF_UNSET_PTR)
{
conf->hide_headers = prev->hide_headers;
conf->pass_headers = prev->pass_headers;
conf->hide_headers_hash = prev->hide_headers_hash;
if (conf->hide_headers_hash.buckets) {
return NGX_OK;
}
} else {
if (conf->hide_headers == NGX_CONF_UNSET_PTR) {
conf->hide_headers = prev->hide_headers;
}
if (conf->pass_headers == NGX_CONF_UNSET_PTR) {
conf->pass_headers = prev->pass_headers;
}
}
if (ngx_array_init(&hide_headers, cf->temp_pool, 4, sizeof(ngx_hash_key_t))
!= NGX_OK)
{
return NGX_ERROR;
}
for (h = default_hide_headers; h->len; h++) {
hk = ngx_array_push(&hide_headers);
if (hk == NULL) {
return NGX_ERROR;
}
hk->key = *h;
hk->key_hash = ngx_hash_key_lc(h->data, h->len);
hk->value = (void *) 1;
}
if (conf->hide_headers != NGX_CONF_UNSET_PTR) {
h = conf->hide_headers->elts;
for (i = 0; i < conf->hide_headers->nelts; i++) {
hk = hide_headers.elts;
for (j = 0; j < hide_headers.nelts; j++) {
if (ngx_strcasecmp(h[i].data, hk[j].key.data) == 0) {
goto exist;
}
}
hk = ngx_array_push(&hide_headers);
if (hk == NULL) {
return NGX_ERROR;
}
hk->key = h[i];
hk->key_hash = ngx_hash_key_lc(h[i].data, h[i].len);
hk->value = (void *) 1;
exist:
continue;
}
}
if (conf->pass_headers != NGX_CONF_UNSET_PTR) {
h = conf->pass_headers->elts;
hk = hide_headers.elts;
for (i = 0; i < conf->pass_headers->nelts; i++) {
for (j = 0; j < hide_headers.nelts; j++) {
if (hk[j].key.data == NULL) {
continue;
}
if (ngx_strcasecmp(h[i].data, hk[j].key.data) == 0) {
hk[j].key.data = NULL;
break;
}
}
}
}
hash->hash = &conf->hide_headers_hash;
hash->key = ngx_hash_key_lc;
hash->pool = cf->pool;
hash->temp_pool = NULL;
if (ngx_hash_init(hash, hide_headers.elts, hide_headers.nelts) != NGX_OK) {
return NGX_ERROR;
}
/*
* special handling to preserve conf->hide_headers_hash
* in the "http" section to inherit it to all servers
*/
if (prev->hide_headers_hash.buckets == NULL
&& conf->hide_headers == prev->hide_headers
&& conf->pass_headers == prev->pass_headers)
{
prev->hide_headers_hash = conf->hide_headers_hash;
}
return NGX_OK;
}
static void *
ngx_http_upstream_create_main_conf(ngx_conf_t *cf)
{
ngx_http_upstream_main_conf_t *umcf;
umcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_main_conf_t));
if (umcf == NULL) {
return NULL;
}
if (ngx_array_init(&umcf->upstreams, cf->pool, 4,
sizeof(ngx_http_upstream_srv_conf_t *))
!= NGX_OK)
{
return NULL;
}
return umcf;
}
static char *
ngx_http_upstream_init_main_conf(ngx_conf_t *cf, void *conf)
{
ngx_http_upstream_main_conf_t *umcf = conf;
ngx_uint_t i;
ngx_array_t headers_in;
ngx_hash_key_t *hk;
ngx_hash_init_t hash;
ngx_http_upstream_init_pt init;
ngx_http_upstream_header_t *header;
ngx_http_upstream_srv_conf_t **uscfp;
uscfp = umcf->upstreams.elts;
for (i = 0; i < umcf->upstreams.nelts; i++) {
init = uscfp[i]->peer.init_upstream ? uscfp[i]->peer.init_upstream:
ngx_http_upstream_init_round_robin;
if (init(cf, uscfp[i]) != NGX_OK) {
return NGX_CONF_ERROR;
}
}
/* upstream_headers_in_hash */
if (ngx_array_init(&headers_in, cf->temp_pool, 32, sizeof(ngx_hash_key_t))
!= NGX_OK)
{
return NGX_CONF_ERROR;
}
for (header = ngx_http_upstream_headers_in; header->name.len; header++) {
hk = ngx_array_push(&headers_in);
if (hk == NULL) {
return NGX_CONF_ERROR;
}
hk->key = header->name;
hk->key_hash = ngx_hash_key_lc(header->name.data, header->name.len);
hk->value = header;
}
hash.hash = &umcf->headers_in_hash;
hash.key = ngx_hash_key_lc;
hash.max_size = 512;
hash.bucket_size = ngx_align(64, ngx_cacheline_size);
hash.name = "upstream_headers_in_hash";
hash.pool = cf->pool;
hash.temp_pool = NULL;
if (ngx_hash_init(&hash, headers_in.elts, headers_in.nelts) != NGX_OK) {
return NGX_CONF_ERROR;
}
return NGX_CONF_OK;
}
nginx-1.14.0/src/http/ngx_http_upstream.h 000644 001751 001751 00000035653 13265410474 021622 0 ustar 00mdounin mdounin 000000 000000
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#ifndef _NGX_HTTP_UPSTREAM_H_INCLUDED_
#define _NGX_HTTP_UPSTREAM_H_INCLUDED_
#include
#include
#include
#include
#include
#include
#define NGX_HTTP_UPSTREAM_FT_ERROR 0x00000002
#define NGX_HTTP_UPSTREAM_FT_TIMEOUT 0x00000004
#define NGX_HTTP_UPSTREAM_FT_INVALID_HEADER 0x00000008
#define NGX_HTTP_UPSTREAM_FT_HTTP_500 0x00000010
#define NGX_HTTP_UPSTREAM_FT_HTTP_502 0x00000020
#define NGX_HTTP_UPSTREAM_FT_HTTP_503 0x00000040
#define NGX_HTTP_UPSTREAM_FT_HTTP_504 0x00000080
#define NGX_HTTP_UPSTREAM_FT_HTTP_403 0x00000100
#define NGX_HTTP_UPSTREAM_FT_HTTP_404 0x00000200
#define NGX_HTTP_UPSTREAM_FT_HTTP_429 0x00000400
#define NGX_HTTP_UPSTREAM_FT_UPDATING 0x00000800
#define NGX_HTTP_UPSTREAM_FT_BUSY_LOCK 0x00001000
#define NGX_HTTP_UPSTREAM_FT_MAX_WAITING 0x00002000
#define NGX_HTTP_UPSTREAM_FT_NON_IDEMPOTENT 0x00004000
#define NGX_HTTP_UPSTREAM_FT_NOLIVE 0x40000000
#define NGX_HTTP_UPSTREAM_FT_OFF 0x80000000
#define NGX_HTTP_UPSTREAM_FT_STATUS (NGX_HTTP_UPSTREAM_FT_HTTP_500 \
|NGX_HTTP_UPSTREAM_FT_HTTP_502 \
|NGX_HTTP_UPSTREAM_FT_HTTP_503 \
|NGX_HTTP_UPSTREAM_FT_HTTP_504 \
|NGX_HTTP_UPSTREAM_FT_HTTP_403 \
|NGX_HTTP_UPSTREAM_FT_HTTP_404 \
|NGX_HTTP_UPSTREAM_FT_HTTP_429)
#define NGX_HTTP_UPSTREAM_INVALID_HEADER 40
#define NGX_HTTP_UPSTREAM_IGN_XA_REDIRECT 0x00000002
#define NGX_HTTP_UPSTREAM_IGN_XA_EXPIRES 0x00000004
#define NGX_HTTP_UPSTREAM_IGN_EXPIRES 0x00000008
#define NGX_HTTP_UPSTREAM_IGN_CACHE_CONTROL 0x00000010
#define NGX_HTTP_UPSTREAM_IGN_SET_COOKIE 0x00000020
#define NGX_HTTP_UPSTREAM_IGN_XA_LIMIT_RATE 0x00000040
#define NGX_HTTP_UPSTREAM_IGN_XA_BUFFERING 0x00000080
#define NGX_HTTP_UPSTREAM_IGN_XA_CHARSET 0x00000100
#define NGX_HTTP_UPSTREAM_IGN_VARY 0x00000200
typedef struct {
ngx_uint_t status;
ngx_msec_t response_time;
ngx_msec_t connect_time;
ngx_msec_t header_time;
ngx_msec_t queue_time;
off_t response_length;
off_t bytes_received;
ngx_str_t *peer;
} ngx_http_upstream_state_t;
typedef struct {
ngx_hash_t headers_in_hash;
ngx_array_t upstreams;
/* ngx_http_upstream_srv_conf_t */
} ngx_http_upstream_main_conf_t;
typedef struct ngx_http_upstream_srv_conf_s ngx_http_upstream_srv_conf_t;
typedef ngx_int_t (*ngx_http_upstream_init_pt)(ngx_conf_t *cf,
ngx_http_upstream_srv_conf_t *us);
typedef ngx_int_t (*ngx_http_upstream_init_peer_pt)(ngx_http_request_t *r,
ngx_http_upstream_srv_conf_t *us);
typedef struct {
ngx_http_upstream_init_pt init_upstream;
ngx_http_upstream_init_peer_pt init;
void *data;
} ngx_http_upstream_peer_t;
typedef struct {
ngx_str_t name;
ngx_addr_t *addrs;
ngx_uint_t naddrs;
ngx_uint_t weight;
ngx_uint_t max_conns;
ngx_uint_t max_fails;
time_t fail_timeout;
ngx_msec_t slow_start;
ngx_uint_t down;
unsigned backup:1;
NGX_COMPAT_BEGIN(6)
NGX_COMPAT_END
} ngx_http_upstream_server_t;
#define NGX_HTTP_UPSTREAM_CREATE 0x0001
#define NGX_HTTP_UPSTREAM_WEIGHT 0x0002
#define NGX_HTTP_UPSTREAM_MAX_FAILS 0x0004
#define NGX_HTTP_UPSTREAM_FAIL_TIMEOUT 0x0008
#define NGX_HTTP_UPSTREAM_DOWN 0x0010
#define NGX_HTTP_UPSTREAM_BACKUP 0x0020
#define NGX_HTTP_UPSTREAM_MAX_CONNS 0x0100
struct ngx_http_upstream_srv_conf_s {
ngx_http_upstream_peer_t peer;
void **srv_conf;
ngx_array_t *servers; /* ngx_http_upstream_server_t */
ngx_uint_t flags;
ngx_str_t host;
u_char *file_name;
ngx_uint_t line;
in_port_t port;
ngx_uint_t no_port; /* unsigned no_port:1 */
#if (NGX_HTTP_UPSTREAM_ZONE)
ngx_shm_zone_t *shm_zone;
#endif
};
typedef struct {
ngx_addr_t *addr;
ngx_http_complex_value_t *value;
#if (NGX_HAVE_TRANSPARENT_PROXY)
ngx_uint_t transparent; /* unsigned transparent:1; */
#endif
} ngx_http_upstream_local_t;
typedef struct {
ngx_http_upstream_srv_conf_t *upstream;
ngx_msec_t connect_timeout;
ngx_msec_t send_timeout;
ngx_msec_t read_timeout;
ngx_msec_t next_upstream_timeout;
size_t send_lowat;
size_t buffer_size;
size_t limit_rate;
size_t busy_buffers_size;
size_t max_temp_file_size;
size_t temp_file_write_size;
size_t busy_buffers_size_conf;
size_t max_temp_file_size_conf;
size_t temp_file_write_size_conf;
ngx_bufs_t bufs;
ngx_uint_t ignore_headers;
ngx_uint_t next_upstream;
ngx_uint_t store_access;
ngx_uint_t next_upstream_tries;
ngx_flag_t buffering;
ngx_flag_t request_buffering;
ngx_flag_t pass_request_headers;
ngx_flag_t pass_request_body;
ngx_flag_t ignore_client_abort;
ngx_flag_t intercept_errors;
ngx_flag_t cyclic_temp_file;
ngx_flag_t force_ranges;
ngx_path_t *temp_path;
ngx_hash_t hide_headers_hash;
ngx_array_t *hide_headers;
ngx_array_t *pass_headers;
ngx_http_upstream_local_t *local;
#if (NGX_HTTP_CACHE)
ngx_shm_zone_t *cache_zone;
ngx_http_complex_value_t *cache_value;
ngx_uint_t cache_min_uses;
ngx_uint_t cache_use_stale;
ngx_uint_t cache_methods;
off_t cache_max_range_offset;
ngx_flag_t cache_lock;
ngx_msec_t cache_lock_timeout;
ngx_msec_t cache_lock_age;
ngx_flag_t cache_revalidate;
ngx_flag_t cache_convert_head;
ngx_flag_t cache_background_update;
ngx_array_t *cache_valid;
ngx_array_t *cache_bypass;
ngx_array_t *cache_purge;
ngx_array_t *no_cache;
#endif
ngx_array_t *store_lengths;
ngx_array_t *store_values;
#if (NGX_HTTP_CACHE)
signed cache:2;
#endif
signed store:2;
unsigned intercept_404:1;
unsigned change_buffering:1;
unsigned pass_trailers:1;
unsigned preserve_output:1;
#if (NGX_HTTP_SSL || NGX_COMPAT)
ngx_ssl_t *ssl;
ngx_flag_t ssl_session_reuse;
ngx_http_complex_value_t *ssl_name;
ngx_flag_t ssl_server_name;
ngx_flag_t ssl_verify;
#endif
ngx_str_t module;
NGX_COMPAT_BEGIN(2)
NGX_COMPAT_END
} ngx_http_upstream_conf_t;
typedef struct {
ngx_str_t name;
ngx_http_header_handler_pt handler;
ngx_uint_t offset;
ngx_http_header_handler_pt copy_handler;
ngx_uint_t conf;
ngx_uint_t redirect; /* unsigned redirect:1; */
} ngx_http_upstream_header_t;
typedef struct {
ngx_list_t headers;
ngx_list_t trailers;
ngx_uint_t status_n;
ngx_str_t status_line;
ngx_table_elt_t *status;
ngx_table_elt_t *date;
ngx_table_elt_t *server;
ngx_table_elt_t *connection;
ngx_table_elt_t *expires;
ngx_table_elt_t *etag;
ngx_table_elt_t *x_accel_expires;
ngx_table_elt_t *x_accel_redirect;
ngx_table_elt_t *x_accel_limit_rate;
ngx_table_elt_t *content_type;
ngx_table_elt_t *content_length;
ngx_table_elt_t *last_modified;
ngx_table_elt_t *location;
ngx_table_elt_t *accept_ranges;
ngx_table_elt_t *www_authenticate;
ngx_table_elt_t *transfer_encoding;
ngx_table_elt_t *vary;
#if (NGX_HTTP_GZIP)
ngx_table_elt_t *content_encoding;
#endif
ngx_array_t cache_control;
ngx_array_t cookies;
off_t content_length_n;
time_t last_modified_time;
unsigned connection_close:1;
unsigned chunked:1;
} ngx_http_upstream_headers_in_t;
typedef struct {
ngx_str_t host;
in_port_t port;
ngx_uint_t no_port; /* unsigned no_port:1 */
ngx_uint_t naddrs;
ngx_resolver_addr_t *addrs;
struct sockaddr *sockaddr;
socklen_t socklen;
ngx_str_t name;
ngx_resolver_ctx_t *ctx;
} ngx_http_upstream_resolved_t;
typedef void (*ngx_http_upstream_handler_pt)(ngx_http_request_t *r,
ngx_http_upstream_t *u);
struct ngx_http_upstream_s {
ngx_http_upstream_handler_pt read_event_handler;
ngx_http_upstream_handler_pt write_event_handler;
ngx_peer_connection_t peer;
ngx_event_pipe_t *pipe;
ngx_chain_t *request_bufs;
ngx_output_chain_ctx_t output;
ngx_chain_writer_ctx_t writer;
ngx_http_upstream_conf_t *conf;
ngx_http_upstream_srv_conf_t *upstream;
#if (NGX_HTTP_CACHE)
ngx_array_t *caches;
#endif
ngx_http_upstream_headers_in_t headers_in;
ngx_http_upstream_resolved_t *resolved;
ngx_buf_t from_client;
ngx_buf_t buffer;
off_t length;
ngx_chain_t *out_bufs;
ngx_chain_t *busy_bufs;
ngx_chain_t *free_bufs;
ngx_int_t (*input_filter_init)(void *data);
ngx_int_t (*input_filter)(void *data, ssize_t bytes);
void *input_filter_ctx;
#if (NGX_HTTP_CACHE)
ngx_int_t (*create_key)(ngx_http_request_t *r);
#endif
ngx_int_t (*create_request)(ngx_http_request_t *r);
ngx_int_t (*reinit_request)(ngx_http_request_t *r);
ngx_int_t (*process_header)(ngx_http_request_t *r);
void (*abort_request)(ngx_http_request_t *r);
void (*finalize_request)(ngx_http_request_t *r,
ngx_int_t rc);
ngx_int_t (*rewrite_redirect)(ngx_http_request_t *r,
ngx_table_elt_t *h, size_t prefix);
ngx_int_t (*rewrite_cookie)(ngx_http_request_t *r,
ngx_table_elt_t *h);
ngx_msec_t timeout;
ngx_http_upstream_state_t *state;
ngx_str_t method;
ngx_str_t schema;
ngx_str_t uri;
#if (NGX_HTTP_SSL || NGX_COMPAT)
ngx_str_t ssl_name;
#endif
ngx_http_cleanup_pt *cleanup;
unsigned store:1;
unsigned cacheable:1;
unsigned accel:1;
unsigned ssl:1;
#if (NGX_HTTP_CACHE)
unsigned cache_status:3;
#endif
unsigned buffering:1;
unsigned keepalive:1;
unsigned upgrade:1;
unsigned request_sent:1;
unsigned request_body_sent:1;
unsigned request_body_blocked:1;
unsigned header_sent:1;
};
typedef struct {
ngx_uint_t status;
ngx_uint_t mask;
} ngx_http_upstream_next_t;
typedef struct {
ngx_str_t key;
ngx_str_t value;
ngx_uint_t skip_empty;
} ngx_http_upstream_param_t;
ngx_int_t ngx_http_upstream_create(ngx_http_request_t *r);
void ngx_http_upstream_init(ngx_http_request_t *r);
ngx_http_upstream_srv_conf_t *ngx_http_upstream_add(ngx_conf_t *cf,
ngx_url_t *u, ngx_uint_t flags);
char *ngx_http_upstream_bind_set_slot(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
char *ngx_http_upstream_param_set_slot(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
ngx_int_t ngx_http_upstream_hide_headers_hash(ngx_conf_t *cf,
ngx_http_upstream_conf_t *conf, ngx_http_upstream_conf_t *prev,
ngx_str_t *default_hide_headers, ngx_hash_init_t *hash);
#define ngx_http_conf_upstream_srv_conf(uscf, module) \
uscf->srv_conf[module.ctx_index]
extern ngx_module_t ngx_http_upstream_module;
extern ngx_conf_bitmask_t ngx_http_upstream_cache_method_mask[];
extern ngx_conf_bitmask_t ngx_http_upstream_ignore_headers_masks[];
#endif /* _NGX_HTTP_UPSTREAM_H_INCLUDED_ */
nginx-1.14.0/src/http/v2/ 000755 001751 001751 00000000000 13265410474 016211 5 ustar 00mdounin mdounin 000000 000000 nginx-1.14.0/src/http/ngx_http_upstream_round_robin.c 000644 001751 001751 00000051671 13265410474 024213 0 ustar 00mdounin mdounin 000000 000000
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#include
#include
#include
#define ngx_http_upstream_tries(p) ((p)->number \
+ ((p)->next ? (p)->next->number : 0))
static ngx_http_upstream_rr_peer_t *ngx_http_upstream_get_peer(
ngx_http_upstream_rr_peer_data_t *rrp);
#if (NGX_HTTP_SSL)
static ngx_int_t ngx_http_upstream_empty_set_session(ngx_peer_connection_t *pc,
void *data);
static void ngx_http_upstream_empty_save_session(ngx_peer_connection_t *pc,
void *data);
#endif
ngx_int_t
ngx_http_upstream_init_round_robin(ngx_conf_t *cf,
ngx_http_upstream_srv_conf_t *us)
{
ngx_url_t u;
ngx_uint_t i, j, n, w;
ngx_http_upstream_server_t *server;
ngx_http_upstream_rr_peer_t *peer, **peerp;
ngx_http_upstream_rr_peers_t *peers, *backup;
us->peer.init = ngx_http_upstream_init_round_robin_peer;
if (us->servers) {
server = us->servers->elts;
n = 0;
w = 0;
for (i = 0; i < us->servers->nelts; i++) {
if (server[i].backup) {
continue;
}
n += server[i].naddrs;
w += server[i].naddrs * server[i].weight;
}
if (n == 0) {
ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
"no servers in upstream \"%V\" in %s:%ui",
&us->host, us->file_name, us->line);
return NGX_ERROR;
}
peers = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_rr_peers_t));
if (peers == NULL) {
return NGX_ERROR;
}
peer = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_rr_peer_t) * n);
if (peer == NULL) {
return NGX_ERROR;
}
peers->single = (n == 1);
peers->number = n;
peers->weighted = (w != n);
peers->total_weight = w;
peers->name = &us->host;
n = 0;
peerp = &peers->peer;
for (i = 0; i < us->servers->nelts; i++) {
if (server[i].backup) {
continue;
}
for (j = 0; j < server[i].naddrs; j++) {
peer[n].sockaddr = server[i].addrs[j].sockaddr;
peer[n].socklen = server[i].addrs[j].socklen;
peer[n].name = server[i].addrs[j].name;
peer[n].weight = server[i].weight;
peer[n].effective_weight = server[i].weight;
peer[n].current_weight = 0;
peer[n].max_conns = server[i].max_conns;
peer[n].max_fails = server[i].max_fails;
peer[n].fail_timeout = server[i].fail_timeout;
peer[n].down = server[i].down;
peer[n].server = server[i].name;
*peerp = &peer[n];
peerp = &peer[n].next;
n++;
}
}
us->peer.data = peers;
/* backup servers */
n = 0;
w = 0;
for (i = 0; i < us->servers->nelts; i++) {
if (!server[i].backup) {
continue;
}
n += server[i].naddrs;
w += server[i].naddrs * server[i].weight;
}
if (n == 0) {
return NGX_OK;
}
backup = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_rr_peers_t));
if (backup == NULL) {
return NGX_ERROR;
}
peer = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_rr_peer_t) * n);
if (peer == NULL) {
return NGX_ERROR;
}
peers->single = 0;
backup->single = 0;
backup->number = n;
backup->weighted = (w != n);
backup->total_weight = w;
backup->name = &us->host;
n = 0;
peerp = &backup->peer;
for (i = 0; i < us->servers->nelts; i++) {
if (!server[i].backup) {
continue;
}
for (j = 0; j < server[i].naddrs; j++) {
peer[n].sockaddr = server[i].addrs[j].sockaddr;
peer[n].socklen = server[i].addrs[j].socklen;
peer[n].name = server[i].addrs[j].name;
peer[n].weight = server[i].weight;
peer[n].effective_weight = server[i].weight;
peer[n].current_weight = 0;
peer[n].max_conns = server[i].max_conns;
peer[n].max_fails = server[i].max_fails;
peer[n].fail_timeout = server[i].fail_timeout;
peer[n].down = server[i].down;
peer[n].server = server[i].name;
*peerp = &peer[n];
peerp = &peer[n].next;
n++;
}
}
peers->next = backup;
return NGX_OK;
}
/* an upstream implicitly defined by proxy_pass, etc. */
if (us->port == 0) {
ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
"no port in upstream \"%V\" in %s:%ui",
&us->host, us->file_name, us->line);
return NGX_ERROR;
}
ngx_memzero(&u, sizeof(ngx_url_t));
u.host = us->host;
u.port = us->port;
if (ngx_inet_resolve_host(cf->pool, &u) != NGX_OK) {
if (u.err) {
ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
"%s in upstream \"%V\" in %s:%ui",
u.err, &us->host, us->file_name, us->line);
}
return NGX_ERROR;
}
n = u.naddrs;
peers = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_rr_peers_t));
if (peers == NULL) {
return NGX_ERROR;
}
peer = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_rr_peer_t) * n);
if (peer == NULL) {
return NGX_ERROR;
}
peers->single = (n == 1);
peers->number = n;
peers->weighted = 0;
peers->total_weight = n;
peers->name = &us->host;
peerp = &peers->peer;
for (i = 0; i < u.naddrs; i++) {
peer[i].sockaddr = u.addrs[i].sockaddr;
peer[i].socklen = u.addrs[i].socklen;
peer[i].name = u.addrs[i].name;
peer[i].weight = 1;
peer[i].effective_weight = 1;
peer[i].current_weight = 0;
peer[i].max_conns = 0;
peer[i].max_fails = 1;
peer[i].fail_timeout = 10;
*peerp = &peer[i];
peerp = &peer[i].next;
}
us->peer.data = peers;
/* implicitly defined upstream has no backup servers */
return NGX_OK;
}
ngx_int_t
ngx_http_upstream_init_round_robin_peer(ngx_http_request_t *r,
ngx_http_upstream_srv_conf_t *us)
{
ngx_uint_t n;
ngx_http_upstream_rr_peer_data_t *rrp;
rrp = r->upstream->peer.data;
if (rrp == NULL) {
rrp = ngx_palloc(r->pool, sizeof(ngx_http_upstream_rr_peer_data_t));
if (rrp == NULL) {
return NGX_ERROR;
}
r->upstream->peer.data = rrp;
}
rrp->peers = us->peer.data;
rrp->current = NULL;
rrp->config = 0;
n = rrp->peers->number;
if (rrp->peers->next && rrp->peers->next->number > n) {
n = rrp->peers->next->number;
}
if (n <= 8 * sizeof(uintptr_t)) {
rrp->tried = &rrp->data;
rrp->data = 0;
} else {
n = (n + (8 * sizeof(uintptr_t) - 1)) / (8 * sizeof(uintptr_t));
rrp->tried = ngx_pcalloc(r->pool, n * sizeof(uintptr_t));
if (rrp->tried == NULL) {
return NGX_ERROR;
}
}
r->upstream->peer.get = ngx_http_upstream_get_round_robin_peer;
r->upstream->peer.free = ngx_http_upstream_free_round_robin_peer;
r->upstream->peer.tries = ngx_http_upstream_tries(rrp->peers);
#if (NGX_HTTP_SSL)
r->upstream->peer.set_session =
ngx_http_upstream_set_round_robin_peer_session;
r->upstream->peer.save_session =
ngx_http_upstream_save_round_robin_peer_session;
#endif
return NGX_OK;
}
ngx_int_t
ngx_http_upstream_create_round_robin_peer(ngx_http_request_t *r,
ngx_http_upstream_resolved_t *ur)
{
u_char *p;
size_t len;
socklen_t socklen;
ngx_uint_t i, n;
struct sockaddr *sockaddr;
ngx_http_upstream_rr_peer_t *peer, **peerp;
ngx_http_upstream_rr_peers_t *peers;
ngx_http_upstream_rr_peer_data_t *rrp;
rrp = r->upstream->peer.data;
if (rrp == NULL) {
rrp = ngx_palloc(r->pool, sizeof(ngx_http_upstream_rr_peer_data_t));
if (rrp == NULL) {
return NGX_ERROR;
}
r->upstream->peer.data = rrp;
}
peers = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_rr_peers_t));
if (peers == NULL) {
return NGX_ERROR;
}
peer = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_rr_peer_t)
* ur->naddrs);
if (peer == NULL) {
return NGX_ERROR;
}
peers->single = (ur->naddrs == 1);
peers->number = ur->naddrs;
peers->name = &ur->host;
if (ur->sockaddr) {
peer[0].sockaddr = ur->sockaddr;
peer[0].socklen = ur->socklen;
peer[0].name = ur->name.data ? ur->name : ur->host;
peer[0].weight = 1;
peer[0].effective_weight = 1;
peer[0].current_weight = 0;
peer[0].max_conns = 0;
peer[0].max_fails = 1;
peer[0].fail_timeout = 10;
peers->peer = peer;
} else {
peerp = &peers->peer;
for (i = 0; i < ur->naddrs; i++) {
socklen = ur->addrs[i].socklen;
sockaddr = ngx_palloc(r->pool, socklen);
if (sockaddr == NULL) {
return NGX_ERROR;
}
ngx_memcpy(sockaddr, ur->addrs[i].sockaddr, socklen);
ngx_inet_set_port(sockaddr, ur->port);
p = ngx_pnalloc(r->pool, NGX_SOCKADDR_STRLEN);
if (p == NULL) {
return NGX_ERROR;
}
len = ngx_sock_ntop(sockaddr, socklen, p, NGX_SOCKADDR_STRLEN, 1);
peer[i].sockaddr = sockaddr;
peer[i].socklen = socklen;
peer[i].name.len = len;
peer[i].name.data = p;
peer[i].weight = 1;
peer[i].effective_weight = 1;
peer[i].current_weight = 0;
peer[i].max_conns = 0;
peer[i].max_fails = 1;
peer[i].fail_timeout = 10;
*peerp = &peer[i];
peerp = &peer[i].next;
}
}
rrp->peers = peers;
rrp->current = NULL;
rrp->config = 0;
if (rrp->peers->number <= 8 * sizeof(uintptr_t)) {
rrp->tried = &rrp->data;
rrp->data = 0;
} else {
n = (rrp->peers->number + (8 * sizeof(uintptr_t) - 1))
/ (8 * sizeof(uintptr_t));
rrp->tried = ngx_pcalloc(r->pool, n * sizeof(uintptr_t));
if (rrp->tried == NULL) {
return NGX_ERROR;
}
}
r->upstream->peer.get = ngx_http_upstream_get_round_robin_peer;
r->upstream->peer.free = ngx_http_upstream_free_round_robin_peer;
r->upstream->peer.tries = ngx_http_upstream_tries(rrp->peers);
#if (NGX_HTTP_SSL)
r->upstream->peer.set_session = ngx_http_upstream_empty_set_session;
r->upstream->peer.save_session = ngx_http_upstream_empty_save_session;
#endif
return NGX_OK;
}
ngx_int_t
ngx_http_upstream_get_round_robin_peer(ngx_peer_connection_t *pc, void *data)
{
ngx_http_upstream_rr_peer_data_t *rrp = data;
ngx_int_t rc;
ngx_uint_t i, n;
ngx_http_upstream_rr_peer_t *peer;
ngx_http_upstream_rr_peers_t *peers;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
"get rr peer, try: %ui", pc->tries);
pc->cached = 0;
pc->connection = NULL;
peers = rrp->peers;
ngx_http_upstream_rr_peers_wlock(peers);
if (peers->single) {
peer = peers->peer;
if (peer->down) {
goto failed;
}
if (peer->max_conns && peer->conns >= peer->max_conns) {
goto failed;
}
rrp->current = peer;
} else {
/* there are several peers */
peer = ngx_http_upstream_get_peer(rrp);
if (peer == NULL) {
goto failed;
}
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,
"get rr peer, current: %p %i",
peer, peer->current_weight);
}
pc->sockaddr = peer->sockaddr;
pc->socklen = peer->socklen;
pc->name = &peer->name;
peer->conns++;
ngx_http_upstream_rr_peers_unlock(peers);
return NGX_OK;
failed:
if (peers->next) {
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0, "backup servers");
rrp->peers = peers->next;
n = (rrp->peers->number + (8 * sizeof(uintptr_t) - 1))
/ (8 * sizeof(uintptr_t));
for (i = 0; i < n; i++) {
rrp->tried[i] = 0;
}
ngx_http_upstream_rr_peers_unlock(peers);
rc = ngx_http_upstream_get_round_robin_peer(pc, rrp);
if (rc != NGX_BUSY) {
return rc;
}
ngx_http_upstream_rr_peers_wlock(peers);
}
ngx_http_upstream_rr_peers_unlock(peers);
pc->name = peers->name;
return NGX_BUSY;
}
static ngx_http_upstream_rr_peer_t *
ngx_http_upstream_get_peer(ngx_http_upstream_rr_peer_data_t *rrp)
{
time_t now;
uintptr_t m;
ngx_int_t total;
ngx_uint_t i, n, p;
ngx_http_upstream_rr_peer_t *peer, *best;
now = ngx_time();
best = NULL;
total = 0;
#if (NGX_SUPPRESS_WARN)
p = 0;
#endif
for (peer = rrp->peers->peer, i = 0;
peer;
peer = peer->next, i++)
{
n = i / (8 * sizeof(uintptr_t));
m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t));
if (rrp->tried[n] & m) {
continue;
}
if (peer->down) {
continue;
}
if (peer->max_fails
&& peer->fails >= peer->max_fails
&& now - peer->checked <= peer->fail_timeout)
{
continue;
}
if (peer->max_conns && peer->conns >= peer->max_conns) {
continue;
}
peer->current_weight += peer->effective_weight;
total += peer->effective_weight;
if (peer->effective_weight < peer->weight) {
peer->effective_weight++;
}
if (best == NULL || peer->current_weight > best->current_weight) {
best = peer;
p = i;
}
}
if (best == NULL) {
return NULL;
}
rrp->current = best;
n = p / (8 * sizeof(uintptr_t));
m = (uintptr_t) 1 << p % (8 * sizeof(uintptr_t));
rrp->tried[n] |= m;
best->current_weight -= total;
if (now - best->checked > best->fail_timeout) {
best->checked = now;
}
return best;
}
void
ngx_http_upstream_free_round_robin_peer(ngx_peer_connection_t *pc, void *data,
ngx_uint_t state)
{
ngx_http_upstream_rr_peer_data_t *rrp = data;
time_t now;
ngx_http_upstream_rr_peer_t *peer;
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,
"free rr peer %ui %ui", pc->tries, state);
/* TODO: NGX_PEER_KEEPALIVE */
peer = rrp->current;
ngx_http_upstream_rr_peers_rlock(rrp->peers);
ngx_http_upstream_rr_peer_lock(rrp->peers, peer);
if (rrp->peers->single) {
peer->conns--;
ngx_http_upstream_rr_peer_unlock(rrp->peers, peer);
ngx_http_upstream_rr_peers_unlock(rrp->peers);
pc->tries = 0;
return;
}
if (state & NGX_PEER_FAILED) {
now = ngx_time();
peer->fails++;
peer->accessed = now;
peer->checked = now;
if (peer->max_fails) {
peer->effective_weight -= peer->weight / peer->max_fails;
if (peer->fails >= peer->max_fails) {
ngx_log_error(NGX_LOG_WARN, pc->log, 0,
"upstream server temporarily disabled");
}
}
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,
"free rr peer failed: %p %i",
peer, peer->effective_weight);
if (peer->effective_weight < 0) {
peer->effective_weight = 0;
}
} else {
/* mark peer live if check passed */
if (peer->accessed < peer->checked) {
peer->fails = 0;
}
}
peer->conns--;
ngx_http_upstream_rr_peer_unlock(rrp->peers, peer);
ngx_http_upstream_rr_peers_unlock(rrp->peers);
if (pc->tries) {
pc->tries--;
}
}
#if (NGX_HTTP_SSL)
ngx_int_t
ngx_http_upstream_set_round_robin_peer_session(ngx_peer_connection_t *pc,
void *data)
{
ngx_http_upstream_rr_peer_data_t *rrp = data;
ngx_int_t rc;
ngx_ssl_session_t *ssl_session;
ngx_http_upstream_rr_peer_t *peer;
#if (NGX_HTTP_UPSTREAM_ZONE)
int len;
#if OPENSSL_VERSION_NUMBER >= 0x0090707fL
const
#endif
u_char *p;
ngx_http_upstream_rr_peers_t *peers;
u_char buf[NGX_SSL_MAX_SESSION_SIZE];
#endif
peer = rrp->current;
#if (NGX_HTTP_UPSTREAM_ZONE)
peers = rrp->peers;
if (peers->shpool) {
ngx_http_upstream_rr_peers_rlock(peers);
ngx_http_upstream_rr_peer_lock(peers, peer);
if (peer->ssl_session == NULL) {
ngx_http_upstream_rr_peer_unlock(peers, peer);
ngx_http_upstream_rr_peers_unlock(peers);
return NGX_OK;
}
len = peer->ssl_session_len;
ngx_memcpy(buf, peer->ssl_session, len);
ngx_http_upstream_rr_peer_unlock(peers, peer);
ngx_http_upstream_rr_peers_unlock(peers);
p = buf;
ssl_session = d2i_SSL_SESSION(NULL, &p, len);
rc = ngx_ssl_set_session(pc->connection, ssl_session);
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
"set session: %p", ssl_session);
ngx_ssl_free_session(ssl_session);
return rc;
}
#endif
ssl_session = peer->ssl_session;
rc = ngx_ssl_set_session(pc->connection, ssl_session);
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
"set session: %p", ssl_session);
return rc;
}
void
ngx_http_upstream_save_round_robin_peer_session(ngx_peer_connection_t *pc,
void *data)
{
ngx_http_upstream_rr_peer_data_t *rrp = data;
ngx_ssl_session_t *old_ssl_session, *ssl_session;
ngx_http_upstream_rr_peer_t *peer;
#if (NGX_HTTP_UPSTREAM_ZONE)
int len;
u_char *p;
ngx_http_upstream_rr_peers_t *peers;
u_char buf[NGX_SSL_MAX_SESSION_SIZE];
#endif
#if (NGX_HTTP_UPSTREAM_ZONE)
peers = rrp->peers;
if (peers->shpool) {
ssl_session = SSL_get0_session(pc->connection->ssl->connection);
if (ssl_session == NULL) {
return;
}
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
"save session: %p", ssl_session);
len = i2d_SSL_SESSION(ssl_session, NULL);
/* do not cache too big session */
if (len > NGX_SSL_MAX_SESSION_SIZE) {
return;
}
p = buf;
(void) i2d_SSL_SESSION(ssl_session, &p);
peer = rrp->current;
ngx_http_upstream_rr_peers_rlock(peers);
ngx_http_upstream_rr_peer_lock(peers, peer);
if (len > peer->ssl_session_len) {
ngx_shmtx_lock(&peers->shpool->mutex);
if (peer->ssl_session) {
ngx_slab_free_locked(peers->shpool, peer->ssl_session);
}
peer->ssl_session = ngx_slab_alloc_locked(peers->shpool, len);
ngx_shmtx_unlock(&peers->shpool->mutex);
if (peer->ssl_session == NULL) {
peer->ssl_session_len = 0;
ngx_http_upstream_rr_peer_unlock(peers, peer);
ngx_http_upstream_rr_peers_unlock(peers);
return;
}
peer->ssl_session_len = len;
}
ngx_memcpy(peer->ssl_session, buf, len);
ngx_http_upstream_rr_peer_unlock(peers, peer);
ngx_http_upstream_rr_peers_unlock(peers);
return;
}
#endif
ssl_session = ngx_ssl_get_session(pc->connection);
if (ssl_session == NULL) {
return;
}
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
"save session: %p", ssl_session);
peer = rrp->current;
old_ssl_session = peer->ssl_session;
peer->ssl_session = ssl_session;
if (old_ssl_session) {
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
"old session: %p", old_ssl_session);
/* TODO: may block */
ngx_ssl_free_session(old_ssl_session);
}
}
static ngx_int_t
ngx_http_upstream_empty_set_session(ngx_peer_connection_t *pc, void *data)
{
return NGX_OK;
}
static void
ngx_http_upstream_empty_save_session(ngx_peer_connection_t *pc, void *data)
{
return;
}
#endif
nginx-1.14.0/src/http/ngx_http_upstream_round_robin.h 000644 001751 001751 00000011741 13265410474 024212 0 ustar 00mdounin mdounin 000000 000000
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#ifndef _NGX_HTTP_UPSTREAM_ROUND_ROBIN_H_INCLUDED_
#define _NGX_HTTP_UPSTREAM_ROUND_ROBIN_H_INCLUDED_
#include
#include
#include
typedef struct ngx_http_upstream_rr_peer_s ngx_http_upstream_rr_peer_t;
struct ngx_http_upstream_rr_peer_s {
struct sockaddr *sockaddr;
socklen_t socklen;
ngx_str_t name;
ngx_str_t server;
ngx_int_t current_weight;
ngx_int_t effective_weight;
ngx_int_t weight;
ngx_uint_t conns;
ngx_uint_t max_conns;
ngx_uint_t fails;
time_t accessed;
time_t checked;
ngx_uint_t max_fails;
time_t fail_timeout;
ngx_msec_t slow_start;
ngx_msec_t start_time;
ngx_uint_t down;
#if (NGX_HTTP_SSL || NGX_COMPAT)
void *ssl_session;
int ssl_session_len;
#endif
#if (NGX_HTTP_UPSTREAM_ZONE)
ngx_atomic_t lock;
#endif
ngx_http_upstream_rr_peer_t *next;
NGX_COMPAT_BEGIN(32)
NGX_COMPAT_END
};
typedef struct ngx_http_upstream_rr_peers_s ngx_http_upstream_rr_peers_t;
struct ngx_http_upstream_rr_peers_s {
ngx_uint_t number;
#if (NGX_HTTP_UPSTREAM_ZONE)
ngx_slab_pool_t *shpool;
ngx_atomic_t rwlock;
ngx_http_upstream_rr_peers_t *zone_next;
#endif
ngx_uint_t total_weight;
unsigned single:1;
unsigned weighted:1;
ngx_str_t *name;
ngx_http_upstream_rr_peers_t *next;
ngx_http_upstream_rr_peer_t *peer;
};
#if (NGX_HTTP_UPSTREAM_ZONE)
#define ngx_http_upstream_rr_peers_rlock(peers) \
\
if (peers->shpool) { \
ngx_rwlock_rlock(&peers->rwlock); \
}
#define ngx_http_upstream_rr_peers_wlock(peers) \
\
if (peers->shpool) { \
ngx_rwlock_wlock(&peers->rwlock); \
}
#define ngx_http_upstream_rr_peers_unlock(peers) \
\
if (peers->shpool) { \
ngx_rwlock_unlock(&peers->rwlock); \
}
#define ngx_http_upstream_rr_peer_lock(peers, peer) \
\
if (peers->shpool) { \
ngx_rwlock_wlock(&peer->lock); \
}
#define ngx_http_upstream_rr_peer_unlock(peers, peer) \
\
if (peers->shpool) { \
ngx_rwlock_unlock(&peer->lock); \
}
#else
#define ngx_http_upstream_rr_peers_rlock(peers)
#define ngx_http_upstream_rr_peers_wlock(peers)
#define ngx_http_upstream_rr_peers_unlock(peers)
#define ngx_http_upstream_rr_peer_lock(peers, peer)
#define ngx_http_upstream_rr_peer_unlock(peers, peer)
#endif
typedef struct {
ngx_uint_t config;
ngx_http_upstream_rr_peers_t *peers;
ngx_http_upstream_rr_peer_t *current;
uintptr_t *tried;
uintptr_t data;
} ngx_http_upstream_rr_peer_data_t;
ngx_int_t ngx_http_upstream_init_round_robin(ngx_conf_t *cf,
ngx_http_upstream_srv_conf_t *us);
ngx_int_t ngx_http_upstream_init_round_robin_peer(ngx_http_request_t *r,
ngx_http_upstream_srv_conf_t *us);
ngx_int_t ngx_http_upstream_create_round_robin_peer(ngx_http_request_t *r,
ngx_http_upstream_resolved_t *ur);
ngx_int_t ngx_http_upstream_get_round_robin_peer(ngx_peer_connection_t *pc,
void *data);
void ngx_http_upstream_free_round_robin_peer(ngx_peer_connection_t *pc,
void *data, ngx_uint_t state);
#if (NGX_HTTP_SSL)
ngx_int_t
ngx_http_upstream_set_round_robin_peer_session(ngx_peer_connection_t *pc,
void *data);
void ngx_http_upstream_save_round_robin_peer_session(ngx_peer_connection_t *pc,
void *data);
#endif
#endif /* _NGX_HTTP_UPSTREAM_ROUND_ROBIN_H_INCLUDED_ */
nginx-1.14.0/src/http/ngx_http_variables.h 000644 001751 001751 00000006164 13265410474 021725 0 ustar 00mdounin mdounin 000000 000000
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#ifndef _NGX_HTTP_VARIABLES_H_INCLUDED_
#define _NGX_HTTP_VARIABLES_H_INCLUDED_
#include
#include
#include
typedef ngx_variable_value_t ngx_http_variable_value_t;
#define ngx_http_variable(v) { sizeof(v) - 1, 1, 0, 0, 0, (u_char *) v }
typedef struct ngx_http_variable_s ngx_http_variable_t;
typedef void (*ngx_http_set_variable_pt) (ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
typedef ngx_int_t (*ngx_http_get_variable_pt) (ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
#define NGX_HTTP_VAR_CHANGEABLE 1
#define NGX_HTTP_VAR_NOCACHEABLE 2
#define NGX_HTTP_VAR_INDEXED 4
#define NGX_HTTP_VAR_NOHASH 8
#define NGX_HTTP_VAR_WEAK 16
#define NGX_HTTP_VAR_PREFIX 32
struct ngx_http_variable_s {
ngx_str_t name; /* must be first to build the hash */
ngx_http_set_variable_pt set_handler;
ngx_http_get_variable_pt get_handler;
uintptr_t data;
ngx_uint_t flags;
ngx_uint_t index;
};
#define ngx_http_null_variable { ngx_null_string, NULL, NULL, 0, 0, 0 }
ngx_http_variable_t *ngx_http_add_variable(ngx_conf_t *cf, ngx_str_t *name,
ngx_uint_t flags);
ngx_int_t ngx_http_get_variable_index(ngx_conf_t *cf, ngx_str_t *name);
ngx_http_variable_value_t *ngx_http_get_indexed_variable(ngx_http_request_t *r,
ngx_uint_t index);
ngx_http_variable_value_t *ngx_http_get_flushed_variable(ngx_http_request_t *r,
ngx_uint_t index);
ngx_http_variable_value_t *ngx_http_get_variable(ngx_http_request_t *r,
ngx_str_t *name, ngx_uint_t key);
ngx_int_t ngx_http_variable_unknown_header(ngx_http_variable_value_t *v,
ngx_str_t *var, ngx_list_part_t *part, size_t prefix);
#if (NGX_PCRE)
typedef struct {
ngx_uint_t capture;
ngx_int_t index;
} ngx_http_regex_variable_t;
typedef struct {
ngx_regex_t *regex;
ngx_uint_t ncaptures;
ngx_http_regex_variable_t *variables;
ngx_uint_t nvariables;
ngx_str_t name;
} ngx_http_regex_t;
typedef struct {
ngx_http_regex_t *regex;
void *value;
} ngx_http_map_regex_t;
ngx_http_regex_t *ngx_http_regex_compile(ngx_conf_t *cf,
ngx_regex_compile_t *rc);
ngx_int_t ngx_http_regex_exec(ngx_http_request_t *r, ngx_http_regex_t *re,
ngx_str_t *s);
#endif
typedef struct {
ngx_hash_combined_t hash;
#if (NGX_PCRE)
ngx_http_map_regex_t *regex;
ngx_uint_t nregex;
#endif
} ngx_http_map_t;
void *ngx_http_map_find(ngx_http_request_t *r, ngx_http_map_t *map,
ngx_str_t *match);
ngx_int_t ngx_http_variables_add_core_vars(ngx_conf_t *cf);
ngx_int_t ngx_http_variables_init_vars(ngx_conf_t *cf);
extern ngx_http_variable_value_t ngx_http_variable_null_value;
extern ngx_http_variable_value_t ngx_http_variable_true_value;
#endif /* _NGX_HTTP_VARIABLES_H_INCLUDED_ */
nginx-1.14.0/src/http/ngx_http_write_filter_module.c 000644 001751 001751 00000021406 13265410474 024010 0 ustar 00mdounin mdounin 000000 000000
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#include
#include
#include
static ngx_int_t ngx_http_write_filter_init(ngx_conf_t *cf);
static ngx_http_module_t ngx_http_write_filter_module_ctx = {
NULL, /* preconfiguration */
ngx_http_write_filter_init, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
NULL, /* create location configuration */
NULL, /* merge location configuration */
};
ngx_module_t ngx_http_write_filter_module = {
NGX_MODULE_V1,
&ngx_http_write_filter_module_ctx, /* module context */
NULL, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
ngx_int_t
ngx_http_write_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
off_t size, sent, nsent, limit;
ngx_uint_t last, flush, sync;
ngx_msec_t delay;
ngx_chain_t *cl, *ln, **ll, *chain;
ngx_connection_t *c;
ngx_http_core_loc_conf_t *clcf;
c = r->connection;
if (c->error) {
return NGX_ERROR;
}
size = 0;
flush = 0;
sync = 0;
last = 0;
ll = &r->out;
/* find the size, the flush point and the last link of the saved chain */
for (cl = r->out; cl; cl = cl->next) {
ll = &cl->next;
ngx_log_debug7(NGX_LOG_DEBUG_EVENT, c->log, 0,
"write old buf t:%d f:%d %p, pos %p, size: %z "
"file: %O, size: %O",
cl->buf->temporary, cl->buf->in_file,
cl->buf->start, cl->buf->pos,
cl->buf->last - cl->buf->pos,
cl->buf->file_pos,
cl->buf->file_last - cl->buf->file_pos);
#if 1
if (ngx_buf_size(cl->buf) == 0 && !ngx_buf_special(cl->buf)) {
ngx_log_error(NGX_LOG_ALERT, c->log, 0,
"zero size buf in writer "
"t:%d r:%d f:%d %p %p-%p %p %O-%O",
cl->buf->temporary,
cl->buf->recycled,
cl->buf->in_file,
cl->buf->start,
cl->buf->pos,
cl->buf->last,
cl->buf->file,
cl->buf->file_pos,
cl->buf->file_last);
ngx_debug_point();
return NGX_ERROR;
}
#endif
size += ngx_buf_size(cl->buf);
if (cl->buf->flush || cl->buf->recycled) {
flush = 1;
}
if (cl->buf->sync) {
sync = 1;
}
if (cl->buf->last_buf) {
last = 1;
}
}
/* add the new chain to the existent one */
for (ln = in; ln; ln = ln->next) {
cl = ngx_alloc_chain_link(r->pool);
if (cl == NULL) {
return NGX_ERROR;
}
cl->buf = ln->buf;
*ll = cl;
ll = &cl->next;
ngx_log_debug7(NGX_LOG_DEBUG_EVENT, c->log, 0,
"write new buf t:%d f:%d %p, pos %p, size: %z "
"file: %O, size: %O",
cl->buf->temporary, cl->buf->in_file,
cl->buf->start, cl->buf->pos,
cl->buf->last - cl->buf->pos,
cl->buf->file_pos,
cl->buf->file_last - cl->buf->file_pos);
#if 1
if (ngx_buf_size(cl->buf) == 0 && !ngx_buf_special(cl->buf)) {
ngx_log_error(NGX_LOG_ALERT, c->log, 0,
"zero size buf in writer "
"t:%d r:%d f:%d %p %p-%p %p %O-%O",
cl->buf->temporary,
cl->buf->recycled,
cl->buf->in_file,
cl->buf->start,
cl->buf->pos,
cl->buf->last,
cl->buf->file,
cl->buf->file_pos,
cl->buf->file_last);
ngx_debug_point();
return NGX_ERROR;
}
#endif
size += ngx_buf_size(cl->buf);
if (cl->buf->flush || cl->buf->recycled) {
flush = 1;
}
if (cl->buf->sync) {
sync = 1;
}
if (cl->buf->last_buf) {
last = 1;
}
}
*ll = NULL;
ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0,
"http write filter: l:%ui f:%ui s:%O", last, flush, size);
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
/*
* avoid the output if there are no last buf, no flush point,
* there are the incoming bufs and the size of all bufs
* is smaller than "postpone_output" directive
*/
if (!last && !flush && in && size < (off_t) clcf->postpone_output) {
return NGX_OK;
}
if (c->write->delayed) {
c->buffered |= NGX_HTTP_WRITE_BUFFERED;
return NGX_AGAIN;
}
if (size == 0
&& !(c->buffered & NGX_LOWLEVEL_BUFFERED)
&& !(last && c->need_last_buf))
{
if (last || flush || sync) {
for (cl = r->out; cl; /* void */) {
ln = cl;
cl = cl->next;
ngx_free_chain(r->pool, ln);
}
r->out = NULL;
c->buffered &= ~NGX_HTTP_WRITE_BUFFERED;
return NGX_OK;
}
ngx_log_error(NGX_LOG_ALERT, c->log, 0,
"the http output chain is empty");
ngx_debug_point();
return NGX_ERROR;
}
if (r->limit_rate) {
if (r->limit_rate_after == 0) {
r->limit_rate_after = clcf->limit_rate_after;
}
limit = (off_t) r->limit_rate * (ngx_time() - r->start_sec + 1)
- (c->sent - r->limit_rate_after);
if (limit <= 0) {
c->write->delayed = 1;
delay = (ngx_msec_t) (- limit * 1000 / r->limit_rate + 1);
ngx_add_timer(c->write, delay);
c->buffered |= NGX_HTTP_WRITE_BUFFERED;
return NGX_AGAIN;
}
if (clcf->sendfile_max_chunk
&& (off_t) clcf->sendfile_max_chunk < limit)
{
limit = clcf->sendfile_max_chunk;
}
} else {
limit = clcf->sendfile_max_chunk;
}
sent = c->sent;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
"http write filter limit %O", limit);
chain = c->send_chain(c, r->out, limit);
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
"http write filter %p", chain);
if (chain == NGX_CHAIN_ERROR) {
c->error = 1;
return NGX_ERROR;
}
if (r->limit_rate) {
nsent = c->sent;
if (r->limit_rate_after) {
sent -= r->limit_rate_after;
if (sent < 0) {
sent = 0;
}
nsent -= r->limit_rate_after;
if (nsent < 0) {
nsent = 0;
}
}
delay = (ngx_msec_t) ((nsent - sent) * 1000 / r->limit_rate);
if (delay > 0) {
limit = 0;
c->write->delayed = 1;
ngx_add_timer(c->write, delay);
}
}
if (limit
&& c->write->ready
&& c->sent - sent >= limit - (off_t) (2 * ngx_pagesize))
{
c->write->delayed = 1;
ngx_add_timer(c->write, 1);
}
for (cl = r->out; cl && cl != chain; /* void */) {
ln = cl;
cl = cl->next;
ngx_free_chain(r->pool, ln);
}
r->out = chain;
if (chain) {
c->buffered |= NGX_HTTP_WRITE_BUFFERED;
return NGX_AGAIN;
}
c->buffered &= ~NGX_HTTP_WRITE_BUFFERED;
if ((c->buffered & NGX_LOWLEVEL_BUFFERED) && r->postponed == NULL) {
return NGX_AGAIN;
}
return NGX_OK;
}
static ngx_int_t
ngx_http_write_filter_init(ngx_conf_t *cf)
{
ngx_http_top_body_filter = ngx_http_write_filter;
return NGX_OK;
}
nginx-1.14.0/src/http/v2/ngx_http_v2_encode.c 000644 001751 001751 00000002247 13265410474 022141 0 ustar 00mdounin mdounin 000000 000000
/*
* Copyright (C) Nginx, Inc.
* Copyright (C) Valentin V. Bartenev
*/
#include
#include
#include
static u_char *ngx_http_v2_write_int(u_char *pos, ngx_uint_t prefix,
ngx_uint_t value);
u_char *
ngx_http_v2_string_encode(u_char *dst, u_char *src, size_t len, u_char *tmp,
ngx_uint_t lower)
{
size_t hlen;
hlen = ngx_http_v2_huff_encode(src, len, tmp, lower);
if (hlen > 0) {
*dst = NGX_HTTP_V2_ENCODE_HUFF;
dst = ngx_http_v2_write_int(dst, ngx_http_v2_prefix(7), hlen);
return ngx_cpymem(dst, tmp, hlen);
}
*dst = NGX_HTTP_V2_ENCODE_RAW;
dst = ngx_http_v2_write_int(dst, ngx_http_v2_prefix(7), len);
if (lower) {
ngx_strlow(dst, src, len);
return dst + len;
}
return ngx_cpymem(dst, src, len);
}
static u_char *
ngx_http_v2_write_int(u_char *pos, ngx_uint_t prefix, ngx_uint_t value)
{
if (value < prefix) {
*pos++ |= value;
return pos;
}
*pos++ |= prefix;
value -= prefix;
while (value >= 128) {
*pos++ = value % 128 + 128;
value /= 128;
}
*pos++ = (u_char) value;
return pos;
}
nginx-1.14.0/src/http/v2/ngx_http_v2.c 000644 001751 001751 00000401036 13265410474 020623 0 ustar 00mdounin mdounin 000000 000000
/*
* Copyright (C) Nginx, Inc.
* Copyright (C) Valentin V. Bartenev
*/
#include
#include
#include
#include
typedef struct {
ngx_str_t name;
ngx_uint_t offset;
ngx_uint_t hash;
ngx_http_header_t *hh;
} ngx_http_v2_parse_header_t;
/* errors */
#define NGX_HTTP_V2_NO_ERROR 0x0
#define NGX_HTTP_V2_PROTOCOL_ERROR 0x1
#define NGX_HTTP_V2_INTERNAL_ERROR 0x2
#define NGX_HTTP_V2_FLOW_CTRL_ERROR 0x3
#define NGX_HTTP_V2_SETTINGS_TIMEOUT 0x4
#define NGX_HTTP_V2_STREAM_CLOSED 0x5
#define NGX_HTTP_V2_SIZE_ERROR 0x6
#define NGX_HTTP_V2_REFUSED_STREAM 0x7
#define NGX_HTTP_V2_CANCEL 0x8
#define NGX_HTTP_V2_COMP_ERROR 0x9
#define NGX_HTTP_V2_CONNECT_ERROR 0xa
#define NGX_HTTP_V2_ENHANCE_YOUR_CALM 0xb
#define NGX_HTTP_V2_INADEQUATE_SECURITY 0xc
#define NGX_HTTP_V2_HTTP_1_1_REQUIRED 0xd
/* frame sizes */
#define NGX_HTTP_V2_SETTINGS_ACK_SIZE 0
#define NGX_HTTP_V2_RST_STREAM_SIZE 4
#define NGX_HTTP_V2_PRIORITY_SIZE 5
#define NGX_HTTP_V2_PING_SIZE 8
#define NGX_HTTP_V2_GOAWAY_SIZE 8
#define NGX_HTTP_V2_WINDOW_UPDATE_SIZE 4
#define NGX_HTTP_V2_SETTINGS_PARAM_SIZE 6
/* settings fields */
#define NGX_HTTP_V2_HEADER_TABLE_SIZE_SETTING 0x1
#define NGX_HTTP_V2_ENABLE_PUSH_SETTING 0x2
#define NGX_HTTP_V2_MAX_STREAMS_SETTING 0x3
#define NGX_HTTP_V2_INIT_WINDOW_SIZE_SETTING 0x4
#define NGX_HTTP_V2_MAX_FRAME_SIZE_SETTING 0x5
#define NGX_HTTP_V2_FRAME_BUFFER_SIZE 24
#define NGX_HTTP_V2_ROOT (void *) -1
static void ngx_http_v2_read_handler(ngx_event_t *rev);
static void ngx_http_v2_write_handler(ngx_event_t *wev);
static void ngx_http_v2_handle_connection(ngx_http_v2_connection_t *h2c);
static u_char *ngx_http_v2_state_proxy_protocol(ngx_http_v2_connection_t *h2c,
u_char *pos, u_char *end);
static u_char *ngx_http_v2_state_preface(ngx_http_v2_connection_t *h2c,
u_char *pos, u_char *end);
static u_char *ngx_http_v2_state_preface_end(ngx_http_v2_connection_t *h2c,
u_char *pos, u_char *end);
static u_char *ngx_http_v2_state_head(ngx_http_v2_connection_t *h2c,
u_char *pos, u_char *end);
static u_char *ngx_http_v2_state_data(ngx_http_v2_connection_t *h2c,
u_char *pos, u_char *end);
static u_char *ngx_http_v2_state_read_data(ngx_http_v2_connection_t *h2c,
u_char *pos, u_char *end);
static u_char *ngx_http_v2_state_headers(ngx_http_v2_connection_t *h2c,
u_char *pos, u_char *end);
static u_char *ngx_http_v2_state_header_block(ngx_http_v2_connection_t *h2c,
u_char *pos, u_char *end);
static u_char *ngx_http_v2_state_field_len(ngx_http_v2_connection_t *h2c,
u_char *pos, u_char *end);
static u_char *ngx_http_v2_state_field_huff(ngx_http_v2_connection_t *h2c,
u_char *pos, u_char *end);
static u_char *ngx_http_v2_state_field_raw(ngx_http_v2_connection_t *h2c,
u_char *pos, u_char *end);
static u_char *ngx_http_v2_state_field_skip(ngx_http_v2_connection_t *h2c,
u_char *pos, u_char *end);
static u_char *ngx_http_v2_state_process_header(ngx_http_v2_connection_t *h2c,
u_char *pos, u_char *end);
static u_char *ngx_http_v2_state_header_complete(ngx_http_v2_connection_t *h2c,
u_char *pos, u_char *end);
static u_char *ngx_http_v2_handle_continuation(ngx_http_v2_connection_t *h2c,
u_char *pos, u_char *end, ngx_http_v2_handler_pt handler);
static u_char *ngx_http_v2_state_priority(ngx_http_v2_connection_t *h2c,
u_char *pos, u_char *end);
static u_char *ngx_http_v2_state_rst_stream(ngx_http_v2_connection_t *h2c,
u_char *pos, u_char *end);
static u_char *ngx_http_v2_state_settings(ngx_http_v2_connection_t *h2c,
u_char *pos, u_char *end);
static u_char *ngx_http_v2_state_settings_params(ngx_http_v2_connection_t *h2c,
u_char *pos, u_char *end);
static u_char *ngx_http_v2_state_push_promise(ngx_http_v2_connection_t *h2c,
u_char *pos, u_char *end);
static u_char *ngx_http_v2_state_ping(ngx_http_v2_connection_t *h2c,
u_char *pos, u_char *end);
static u_char *ngx_http_v2_state_goaway(ngx_http_v2_connection_t *h2c,
u_char *pos, u_char *end);
static u_char *ngx_http_v2_state_window_update(ngx_http_v2_connection_t *h2c,
u_char *pos, u_char *end);
static u_char *ngx_http_v2_state_continuation(ngx_http_v2_connection_t *h2c,
u_char *pos, u_char *end);
static u_char *ngx_http_v2_state_complete(ngx_http_v2_connection_t *h2c,
u_char *pos, u_char *end);
static u_char *ngx_http_v2_state_skip_padded(ngx_http_v2_connection_t *h2c,
u_char *pos, u_char *end);
static u_char *ngx_http_v2_state_skip(ngx_http_v2_connection_t *h2c,
u_char *pos, u_char *end);
static u_char *ngx_http_v2_state_save(ngx_http_v2_connection_t *h2c,
u_char *pos, u_char *end, ngx_http_v2_handler_pt handler);
static u_char *ngx_http_v2_state_headers_save(ngx_http_v2_connection_t *h2c,
u_char *pos, u_char *end, ngx_http_v2_handler_pt handler);
static u_char *ngx_http_v2_connection_error(ngx_http_v2_connection_t *h2c,
ngx_uint_t err);
static ngx_int_t ngx_http_v2_parse_int(ngx_http_v2_connection_t *h2c,
u_char **pos, u_char *end, ngx_uint_t prefix);
static ngx_http_v2_stream_t *ngx_http_v2_create_stream(
ngx_http_v2_connection_t *h2c, ngx_uint_t push);
static ngx_http_v2_node_t *ngx_http_v2_get_node_by_id(
ngx_http_v2_connection_t *h2c, ngx_uint_t sid, ngx_uint_t alloc);
static ngx_http_v2_node_t *ngx_http_v2_get_closed_node(
ngx_http_v2_connection_t *h2c);
#define ngx_http_v2_index_size(h2scf) (h2scf->streams_index_mask + 1)
#define ngx_http_v2_index(h2scf, sid) ((sid >> 1) & h2scf->streams_index_mask)
static ngx_int_t ngx_http_v2_send_settings(ngx_http_v2_connection_t *h2c);
static ngx_int_t ngx_http_v2_settings_frame_handler(
ngx_http_v2_connection_t *h2c, ngx_http_v2_out_frame_t *frame);
static ngx_int_t ngx_http_v2_send_window_update(ngx_http_v2_connection_t *h2c,
ngx_uint_t sid, size_t window);
static ngx_int_t ngx_http_v2_send_rst_stream(ngx_http_v2_connection_t *h2c,
ngx_uint_t sid, ngx_uint_t status);
static ngx_int_t ngx_http_v2_send_goaway(ngx_http_v2_connection_t *h2c,
ngx_uint_t status);
static ngx_http_v2_out_frame_t *ngx_http_v2_get_frame(
ngx_http_v2_connection_t *h2c, size_t length, ngx_uint_t type,
u_char flags, ngx_uint_t sid);
static ngx_int_t ngx_http_v2_frame_handler(ngx_http_v2_connection_t *h2c,
ngx_http_v2_out_frame_t *frame);
static ngx_int_t ngx_http_v2_validate_header(ngx_http_request_t *r,
ngx_http_v2_header_t *header);
static ngx_int_t ngx_http_v2_pseudo_header(ngx_http_request_t *r,
ngx_http_v2_header_t *header);
static ngx_int_t ngx_http_v2_parse_path(ngx_http_request_t *r,
ngx_str_t *value);
static ngx_int_t ngx_http_v2_parse_method(ngx_http_request_t *r,
ngx_str_t *value);
static ngx_int_t ngx_http_v2_parse_scheme(ngx_http_request_t *r,
ngx_str_t *value);
static ngx_int_t ngx_http_v2_parse_authority(ngx_http_request_t *r,
ngx_str_t *value);
static ngx_int_t ngx_http_v2_parse_header(ngx_http_request_t *r,
ngx_http_v2_parse_header_t *header, ngx_str_t *value);
static ngx_int_t ngx_http_v2_construct_request_line(ngx_http_request_t *r);
static ngx_int_t ngx_http_v2_cookie(ngx_http_request_t *r,
ngx_http_v2_header_t *header);
static ngx_int_t ngx_http_v2_construct_cookie_header(ngx_http_request_t *r);
static void ngx_http_v2_run_request(ngx_http_request_t *r);
static void ngx_http_v2_run_request_handler(ngx_event_t *ev);
static ngx_int_t ngx_http_v2_process_request_body(ngx_http_request_t *r,
u_char *pos, size_t size, ngx_uint_t last);
static ngx_int_t ngx_http_v2_filter_request_body(ngx_http_request_t *r);
static void ngx_http_v2_read_client_request_body_handler(ngx_http_request_t *r);
static ngx_int_t ngx_http_v2_terminate_stream(ngx_http_v2_connection_t *h2c,
ngx_http_v2_stream_t *stream, ngx_uint_t status);
static void ngx_http_v2_close_stream_handler(ngx_event_t *ev);
static void ngx_http_v2_handle_connection_handler(ngx_event_t *rev);
static void ngx_http_v2_idle_handler(ngx_event_t *rev);
static void ngx_http_v2_finalize_connection(ngx_http_v2_connection_t *h2c,
ngx_uint_t status);
static ngx_int_t ngx_http_v2_adjust_windows(ngx_http_v2_connection_t *h2c,
ssize_t delta);
static void ngx_http_v2_set_dependency(ngx_http_v2_connection_t *h2c,
ngx_http_v2_node_t *node, ngx_uint_t depend, ngx_uint_t exclusive);
static void ngx_http_v2_node_children_update(ngx_http_v2_node_t *node);
static void ngx_http_v2_pool_cleanup(void *data);
static ngx_http_v2_handler_pt ngx_http_v2_frame_states[] = {
ngx_http_v2_state_data, /* NGX_HTTP_V2_DATA_FRAME */
ngx_http_v2_state_headers, /* NGX_HTTP_V2_HEADERS_FRAME */
ngx_http_v2_state_priority, /* NGX_HTTP_V2_PRIORITY_FRAME */
ngx_http_v2_state_rst_stream, /* NGX_HTTP_V2_RST_STREAM_FRAME */
ngx_http_v2_state_settings, /* NGX_HTTP_V2_SETTINGS_FRAME */
ngx_http_v2_state_push_promise, /* NGX_HTTP_V2_PUSH_PROMISE_FRAME */
ngx_http_v2_state_ping, /* NGX_HTTP_V2_PING_FRAME */
ngx_http_v2_state_goaway, /* NGX_HTTP_V2_GOAWAY_FRAME */
ngx_http_v2_state_window_update, /* NGX_HTTP_V2_WINDOW_UPDATE_FRAME */
ngx_http_v2_state_continuation /* NGX_HTTP_V2_CONTINUATION_FRAME */
};
#define NGX_HTTP_V2_FRAME_STATES \
(sizeof(ngx_http_v2_frame_states) / sizeof(ngx_http_v2_handler_pt))
static ngx_http_v2_parse_header_t ngx_http_v2_parse_headers[] = {
{ ngx_string("host"),
offsetof(ngx_http_headers_in_t, host), 0, NULL },
{ ngx_string("accept-encoding"),
offsetof(ngx_http_headers_in_t, accept_encoding), 0, NULL },
{ ngx_string("accept-language"),
offsetof(ngx_http_headers_in_t, accept_language), 0, NULL },
{ ngx_string("user-agent"),
offsetof(ngx_http_headers_in_t, user_agent), 0, NULL },
{ ngx_null_string, 0, 0, NULL }
};
void
ngx_http_v2_init(ngx_event_t *rev)
{
ngx_connection_t *c;
ngx_pool_cleanup_t *cln;
ngx_http_connection_t *hc;
ngx_http_v2_srv_conf_t *h2scf;
ngx_http_v2_main_conf_t *h2mcf;
ngx_http_v2_connection_t *h2c;
c = rev->data;
hc = c->data;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "init http2 connection");
c->log->action = "processing HTTP/2 connection";
h2mcf = ngx_http_get_module_main_conf(hc->conf_ctx, ngx_http_v2_module);
if (h2mcf->recv_buffer == NULL) {
h2mcf->recv_buffer = ngx_palloc(ngx_cycle->pool,
h2mcf->recv_buffer_size);
if (h2mcf->recv_buffer == NULL) {
ngx_http_close_connection(c);
return;
}
}
h2c = ngx_pcalloc(c->pool, sizeof(ngx_http_v2_connection_t));
if (h2c == NULL) {
ngx_http_close_connection(c);
return;
}
h2c->connection = c;
h2c->http_connection = hc;
h2c->send_window = NGX_HTTP_V2_DEFAULT_WINDOW;
h2c->recv_window = NGX_HTTP_V2_MAX_WINDOW;
h2c->init_window = NGX_HTTP_V2_DEFAULT_WINDOW;
h2c->frame_size = NGX_HTTP_V2_DEFAULT_FRAME_SIZE;
h2c->table_update = 1;
h2scf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_v2_module);
h2c->concurrent_pushes = h2scf->concurrent_pushes;
h2c->pool = ngx_create_pool(h2scf->pool_size, h2c->connection->log);
if (h2c->pool == NULL) {
ngx_http_close_connection(c);
return;
}
cln = ngx_pool_cleanup_add(c->pool, 0);
if (cln == NULL) {
ngx_http_close_connection(c);
return;
}
cln->handler = ngx_http_v2_pool_cleanup;
cln->data = h2c;
h2c->streams_index = ngx_pcalloc(c->pool, ngx_http_v2_index_size(h2scf)
* sizeof(ngx_http_v2_node_t *));
if (h2c->streams_index == NULL) {
ngx_http_close_connection(c);
return;
}
if (ngx_http_v2_send_settings(h2c) == NGX_ERROR) {
ngx_http_close_connection(c);
return;
}
if (ngx_http_v2_send_window_update(h2c, 0, NGX_HTTP_V2_MAX_WINDOW
- NGX_HTTP_V2_DEFAULT_WINDOW)
== NGX_ERROR)
{
ngx_http_close_connection(c);
return;
}
h2c->state.handler = hc->proxy_protocol ? ngx_http_v2_state_proxy_protocol
: ngx_http_v2_state_preface;
ngx_queue_init(&h2c->waiting);
ngx_queue_init(&h2c->dependencies);
ngx_queue_init(&h2c->closed);
c->data = h2c;
rev->handler = ngx_http_v2_read_handler;
c->write->handler = ngx_http_v2_write_handler;
c->idle = 1;
ngx_http_v2_read_handler(rev);
}
static void
ngx_http_v2_read_handler(ngx_event_t *rev)
{
u_char *p, *end;
size_t available;
ssize_t n;
ngx_connection_t *c;
ngx_http_v2_main_conf_t *h2mcf;
ngx_http_v2_connection_t *h2c;
c = rev->data;
h2c = c->data;
if (rev->timedout) {
ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
ngx_http_v2_finalize_connection(h2c, NGX_HTTP_V2_PROTOCOL_ERROR);
return;
}
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http2 read handler");
h2c->blocked = 1;
if (c->close) {
c->close = 0;
if (!h2c->goaway) {
h2c->goaway = 1;
if (ngx_http_v2_send_goaway(h2c, NGX_HTTP_V2_NO_ERROR)
== NGX_ERROR)
{
ngx_http_v2_finalize_connection(h2c, 0);
return;
}
if (ngx_http_v2_send_output_queue(h2c) == NGX_ERROR) {
ngx_http_v2_finalize_connection(h2c, 0);
return;
}
}
h2c->blocked = 0;
return;
}
h2mcf = ngx_http_get_module_main_conf(h2c->http_connection->conf_ctx,
ngx_http_v2_module);
available = h2mcf->recv_buffer_size - 2 * NGX_HTTP_V2_STATE_BUFFER_SIZE;
do {
p = h2mcf->recv_buffer;
ngx_memcpy(p, h2c->state.buffer, NGX_HTTP_V2_STATE_BUFFER_SIZE);
end = p + h2c->state.buffer_used;
n = c->recv(c, end, available);
if (n == NGX_AGAIN) {
break;
}
if (n == 0
&& (h2c->state.incomplete || h2c->processing || h2c->pushing))
{
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"client prematurely closed connection");
}
if (n == 0 || n == NGX_ERROR) {
c->error = 1;
ngx_http_v2_finalize_connection(h2c, 0);
return;
}
end += n;
h2c->state.buffer_used = 0;
h2c->state.incomplete = 0;
do {
p = h2c->state.handler(h2c, p, end);
if (p == NULL) {
return;
}
} while (p != end);
} while (rev->ready);
if (ngx_handle_read_event(rev, 0) != NGX_OK) {
ngx_http_v2_finalize_connection(h2c, NGX_HTTP_V2_INTERNAL_ERROR);
return;
}
if (h2c->last_out && ngx_http_v2_send_output_queue(h2c) == NGX_ERROR) {
ngx_http_v2_finalize_connection(h2c, 0);
return;
}
h2c->blocked = 0;
if (h2c->processing || h2c->pushing) {
if (rev->timer_set) {
ngx_del_timer(rev);
}
return;
}
ngx_http_v2_handle_connection(h2c);
}
static void
ngx_http_v2_write_handler(ngx_event_t *wev)
{
ngx_int_t rc;
ngx_connection_t *c;
ngx_http_v2_connection_t *h2c;
c = wev->data;
h2c = c->data;
if (wev->timedout) {
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
"http2 write event timed out");
c->error = 1;
ngx_http_v2_finalize_connection(h2c, 0);
return;
}
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http2 write handler");
if (h2c->last_out == NULL && !c->buffered) {
if (wev->timer_set) {
ngx_del_timer(wev);
}
ngx_http_v2_handle_connection(h2c);
return;
}
h2c->blocked = 1;
rc = ngx_http_v2_send_output_queue(h2c);
if (rc == NGX_ERROR) {
ngx_http_v2_finalize_connection(h2c, 0);
return;
}
h2c->blocked = 0;
if (rc == NGX_AGAIN) {
return;
}
ngx_http_v2_handle_connection(h2c);
}
ngx_int_t
ngx_http_v2_send_output_queue(ngx_http_v2_connection_t *h2c)
{
int tcp_nodelay;
ngx_chain_t *cl;
ngx_event_t *wev;
ngx_connection_t *c;
ngx_http_v2_out_frame_t *out, *frame, *fn;
ngx_http_core_loc_conf_t *clcf;
c = h2c->connection;
if (c->error) {
return NGX_ERROR;
}
wev = c->write;
if (!wev->ready) {
return NGX_AGAIN;
}
cl = NULL;
out = NULL;
for (frame = h2c->last_out; frame; frame = fn) {
frame->last->next = cl;
cl = frame->first;
fn = frame->next;
frame->next = out;
out = frame;
ngx_log_debug4(NGX_LOG_DEBUG_HTTP, c->log, 0,
"http2 frame out: %p sid:%ui bl:%d len:%uz",
out, out->stream ? out->stream->node->id : 0,
out->blocked, out->length);
}
cl = c->send_chain(c, cl, 0);
if (cl == NGX_CHAIN_ERROR) {
goto error;
}
clcf = ngx_http_get_module_loc_conf(h2c->http_connection->conf_ctx,
ngx_http_core_module);
if (ngx_handle_write_event(wev, clcf->send_lowat) != NGX_OK) {
goto error;
}
if (c->tcp_nopush == NGX_TCP_NOPUSH_SET) {
if (ngx_tcp_push(c->fd) == -1) {
ngx_connection_error(c, ngx_socket_errno, ngx_tcp_push_n " failed");
goto error;
}
c->tcp_nopush = NGX_TCP_NOPUSH_UNSET;
tcp_nodelay = ngx_tcp_nodelay_and_tcp_nopush ? 1 : 0;
} else {
tcp_nodelay = 1;
}
if (tcp_nodelay && clcf->tcp_nodelay && ngx_tcp_nodelay(c) != NGX_OK) {
goto error;
}
for ( /* void */ ; out; out = fn) {
fn = out->next;
if (out->handler(h2c, out) != NGX_OK) {
out->blocked = 1;
break;
}
ngx_log_debug4(NGX_LOG_DEBUG_HTTP, c->log, 0,
"http2 frame sent: %p sid:%ui bl:%d len:%uz",
out, out->stream ? out->stream->node->id : 0,
out->blocked, out->length);
}
frame = NULL;
for ( /* void */ ; out; out = fn) {
fn = out->next;
out->next = frame;
frame = out;
}
h2c->last_out = frame;
if (!wev->ready) {
ngx_add_timer(wev, clcf->send_timeout);
return NGX_AGAIN;
}
if (wev->timer_set) {
ngx_del_timer(wev);
}
return NGX_OK;
error:
c->error = 1;
if (!h2c->blocked) {
ngx_post_event(wev, &ngx_posted_events);
}
return NGX_ERROR;
}
static void
ngx_http_v2_handle_connection(ngx_http_v2_connection_t *h2c)
{
ngx_int_t rc;
ngx_connection_t *c;
ngx_http_v2_srv_conf_t *h2scf;
if (h2c->last_out || h2c->processing || h2c->pushing) {
return;
}
c = h2c->connection;
if (c->error) {
ngx_http_close_connection(c);
return;
}
if (c->buffered) {
h2c->blocked = 1;
rc = ngx_http_v2_send_output_queue(h2c);
h2c->blocked = 0;
if (rc == NGX_ERROR) {
ngx_http_close_connection(c);
return;
}
if (rc == NGX_AGAIN) {
return;
}
/* rc == NGX_OK */
}
if (h2c->goaway) {
ngx_http_close_connection(c);
return;
}
h2scf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx,
ngx_http_v2_module);
if (h2c->state.incomplete) {
ngx_add_timer(c->read, h2scf->recv_timeout);
return;
}
ngx_destroy_pool(h2c->pool);
h2c->pool = NULL;
h2c->free_frames = NULL;
h2c->free_fake_connections = NULL;
#if (NGX_HTTP_SSL)
if (c->ssl) {
ngx_ssl_free_buffer(c);
}
#endif
c->destroyed = 1;
ngx_reusable_connection(c, 1);
c->write->handler = ngx_http_empty_handler;
c->read->handler = ngx_http_v2_idle_handler;
if (c->write->timer_set) {
ngx_del_timer(c->write);
}
ngx_add_timer(c->read, h2scf->idle_timeout);
}
static u_char *
ngx_http_v2_state_proxy_protocol(ngx_http_v2_connection_t *h2c, u_char *pos,
u_char *end)
{
ngx_log_t *log;
log = h2c->connection->log;
log->action = "reading PROXY protocol";
pos = ngx_proxy_protocol_read(h2c->connection, pos, end);
log->action = "processing HTTP/2 connection";
if (pos == NULL) {
return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR);
}
return ngx_http_v2_state_preface(h2c, pos, end);
}
static u_char *
ngx_http_v2_state_preface(ngx_http_v2_connection_t *h2c, u_char *pos,
u_char *end)
{
static const u_char preface[] = "PRI * HTTP/2.0\r\n";
if ((size_t) (end - pos) < sizeof(preface) - 1) {
return ngx_http_v2_state_save(h2c, pos, end, ngx_http_v2_state_preface);
}
if (ngx_memcmp(pos, preface, sizeof(preface) - 1) != 0) {
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
"invalid http2 connection preface \"%*s\"",
sizeof(preface) - 1, pos);
return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR);
}
return ngx_http_v2_state_preface_end(h2c, pos + sizeof(preface) - 1, end);
}
static u_char *
ngx_http_v2_state_preface_end(ngx_http_v2_connection_t *h2c, u_char *pos,
u_char *end)
{
static const u_char preface[] = "\r\nSM\r\n\r\n";
if ((size_t) (end - pos) < sizeof(preface) - 1) {
return ngx_http_v2_state_save(h2c, pos, end,
ngx_http_v2_state_preface_end);
}
if (ngx_memcmp(pos, preface, sizeof(preface) - 1) != 0) {
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
"invalid http2 connection preface \"%*s\"",
sizeof(preface) - 1, pos);
return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR);
}
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
"http2 preface verified");
return ngx_http_v2_state_head(h2c, pos + sizeof(preface) - 1, end);
}
static u_char *
ngx_http_v2_state_head(ngx_http_v2_connection_t *h2c, u_char *pos, u_char *end)
{
uint32_t head;
ngx_uint_t type;
if (end - pos < NGX_HTTP_V2_FRAME_HEADER_SIZE) {
return ngx_http_v2_state_save(h2c, pos, end, ngx_http_v2_state_head);
}
head = ngx_http_v2_parse_uint32(pos);
h2c->state.length = ngx_http_v2_parse_length(head);
h2c->state.flags = pos[4];
h2c->state.sid = ngx_http_v2_parse_sid(&pos[5]);
pos += NGX_HTTP_V2_FRAME_HEADER_SIZE;
type = ngx_http_v2_parse_type(head);
ngx_log_debug4(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
"http2 frame type:%ui f:%Xd l:%uz sid:%ui",
type, h2c->state.flags, h2c->state.length, h2c->state.sid);
if (type >= NGX_HTTP_V2_FRAME_STATES) {
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
"client sent frame with unknown type %ui", type);
return ngx_http_v2_state_skip(h2c, pos, end);
}
return ngx_http_v2_frame_states[type](h2c, pos, end);
}
static u_char *
ngx_http_v2_state_data(ngx_http_v2_connection_t *h2c, u_char *pos, u_char *end)
{
size_t size;
ngx_http_v2_node_t *node;
ngx_http_v2_stream_t *stream;
size = h2c->state.length;
if (h2c->state.flags & NGX_HTTP_V2_PADDED_FLAG) {
if (h2c->state.length == 0) {
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
"client sent padded DATA frame "
"with incorrect length: 0");
return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR);
}
if (end - pos == 0) {
return ngx_http_v2_state_save(h2c, pos, end,
ngx_http_v2_state_data);
}
h2c->state.padding = *pos++;
if (h2c->state.padding >= size) {
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
"client sent padded DATA frame "
"with incorrect length: %uz, padding: %uz",
size, h2c->state.padding);
return ngx_http_v2_connection_error(h2c,
NGX_HTTP_V2_PROTOCOL_ERROR);
}
h2c->state.length -= 1 + h2c->state.padding;
}
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
"http2 DATA frame");
if (size > h2c->recv_window) {
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
"client violated connection flow control: "
"received DATA frame length %uz, available window %uz",
size, h2c->recv_window);
return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_FLOW_CTRL_ERROR);
}
h2c->recv_window -= size;
if (h2c->recv_window < NGX_HTTP_V2_MAX_WINDOW / 4) {
if (ngx_http_v2_send_window_update(h2c, 0, NGX_HTTP_V2_MAX_WINDOW
- h2c->recv_window)
== NGX_ERROR)
{
return ngx_http_v2_connection_error(h2c,
NGX_HTTP_V2_INTERNAL_ERROR);
}
h2c->recv_window = NGX_HTTP_V2_MAX_WINDOW;
}
node = ngx_http_v2_get_node_by_id(h2c, h2c->state.sid, 0);
if (node == NULL || node->stream == NULL) {
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
"unknown http2 stream");
return ngx_http_v2_state_skip_padded(h2c, pos, end);
}
stream = node->stream;
if (size > stream->recv_window) {
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
"client violated flow control for stream %ui: "
"received DATA frame length %uz, available window %uz",
node->id, size, stream->recv_window);
if (ngx_http_v2_terminate_stream(h2c, stream,
NGX_HTTP_V2_FLOW_CTRL_ERROR)
== NGX_ERROR)
{
return ngx_http_v2_connection_error(h2c,
NGX_HTTP_V2_INTERNAL_ERROR);
}
return ngx_http_v2_state_skip_padded(h2c, pos, end);
}
stream->recv_window -= size;
if (stream->no_flow_control
&& stream->recv_window < NGX_HTTP_V2_MAX_WINDOW / 4)
{
if (ngx_http_v2_send_window_update(h2c, node->id,
NGX_HTTP_V2_MAX_WINDOW
- stream->recv_window)
== NGX_ERROR)
{
return ngx_http_v2_connection_error(h2c,
NGX_HTTP_V2_INTERNAL_ERROR);
}
stream->recv_window = NGX_HTTP_V2_MAX_WINDOW;
}
if (stream->in_closed) {
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
"client sent DATA frame for half-closed stream %ui",
node->id);
if (ngx_http_v2_terminate_stream(h2c, stream,
NGX_HTTP_V2_STREAM_CLOSED)
== NGX_ERROR)
{
return ngx_http_v2_connection_error(h2c,
NGX_HTTP_V2_INTERNAL_ERROR);
}
return ngx_http_v2_state_skip_padded(h2c, pos, end);
}
h2c->state.stream = stream;
return ngx_http_v2_state_read_data(h2c, pos, end);
}
static u_char *
ngx_http_v2_state_read_data(ngx_http_v2_connection_t *h2c, u_char *pos,
u_char *end)
{
size_t size;
ngx_buf_t *buf;
ngx_int_t rc;
ngx_http_request_t *r;
ngx_http_v2_stream_t *stream;
ngx_http_v2_srv_conf_t *h2scf;
stream = h2c->state.stream;
if (stream == NULL) {
return ngx_http_v2_state_skip_padded(h2c, pos, end);
}
if (stream->skip_data) {
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
"skipping http2 DATA frame");
return ngx_http_v2_state_skip_padded(h2c, pos, end);
}
size = end - pos;
if (size >= h2c->state.length) {
size = h2c->state.length;
stream->in_closed = h2c->state.flags & NGX_HTTP_V2_END_STREAM_FLAG;
}
r = stream->request;
if (r->request_body) {
rc = ngx_http_v2_process_request_body(r, pos, size, stream->in_closed);
if (rc != NGX_OK) {
stream->skip_data = 1;
ngx_http_finalize_request(r, rc);
}
} else if (size) {
buf = stream->preread;
if (buf == NULL) {
h2scf = ngx_http_get_module_srv_conf(r, ngx_http_v2_module);
buf = ngx_create_temp_buf(r->pool, h2scf->preread_size);
if (buf == NULL) {
return ngx_http_v2_connection_error(h2c,
NGX_HTTP_V2_INTERNAL_ERROR);
}
stream->preread = buf;
}
if (size > (size_t) (buf->end - buf->last)) {
ngx_log_error(NGX_LOG_ALERT, h2c->connection->log, 0,
"http2 preread buffer overflow");
return ngx_http_v2_connection_error(h2c,
NGX_HTTP_V2_INTERNAL_ERROR);
}
buf->last = ngx_cpymem(buf->last, pos, size);
}
pos += size;
h2c->state.length -= size;
if (h2c->state.length) {
return ngx_http_v2_state_save(h2c, pos, end,
ngx_http_v2_state_read_data);
}
if (h2c->state.padding) {
return ngx_http_v2_state_skip_padded(h2c, pos, end);
}
return ngx_http_v2_state_complete(h2c, pos, end);
}
static u_char *
ngx_http_v2_state_headers(ngx_http_v2_connection_t *h2c, u_char *pos,
u_char *end)
{
size_t size;
ngx_uint_t padded, priority, depend, dependency, excl, weight;
ngx_uint_t status;
ngx_http_v2_node_t *node;
ngx_http_v2_stream_t *stream;
ngx_http_v2_srv_conf_t *h2scf;
padded = h2c->state.flags & NGX_HTTP_V2_PADDED_FLAG;
priority = h2c->state.flags & NGX_HTTP_V2_PRIORITY_FLAG;
size = 0;
if (padded) {
size++;
}
if (priority) {
size += sizeof(uint32_t) + 1;
}
if (h2c->state.length < size) {
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
"client sent HEADERS frame with incorrect length %uz",
h2c->state.length);
return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR);
}
if (h2c->state.length == size) {
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
"client sent HEADERS frame with empty header block");
return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR);
}
if (h2c->goaway) {
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
"skipping http2 HEADERS frame");
return ngx_http_v2_state_skip(h2c, pos, end);
}
if ((size_t) (end - pos) < size) {
return ngx_http_v2_state_save(h2c, pos, end,
ngx_http_v2_state_headers);
}
h2c->state.length -= size;
if (padded) {
h2c->state.padding = *pos++;
if (h2c->state.padding > h2c->state.length) {
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
"client sent padded HEADERS frame "
"with incorrect length: %uz, padding: %uz",
h2c->state.length, h2c->state.padding);
return ngx_http_v2_connection_error(h2c,
NGX_HTTP_V2_PROTOCOL_ERROR);
}
h2c->state.length -= h2c->state.padding;
}
depend = 0;
excl = 0;
weight = NGX_HTTP_V2_DEFAULT_WEIGHT;
if (priority) {
dependency = ngx_http_v2_parse_uint32(pos);
depend = dependency & 0x7fffffff;
excl = dependency >> 31;
weight = pos[4] + 1;
pos += sizeof(uint32_t) + 1;
}
ngx_log_debug4(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
"http2 HEADERS frame sid:%ui "
"depends on %ui excl:%ui weight:%ui",
h2c->state.sid, depend, excl, weight);
if (h2c->state.sid % 2 == 0 || h2c->state.sid <= h2c->last_sid) {
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
"client sent HEADERS frame with incorrect identifier "
"%ui, the last was %ui", h2c->state.sid, h2c->last_sid);
return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR);
}
h2c->last_sid = h2c->state.sid;
h2c->state.pool = ngx_create_pool(1024, h2c->connection->log);
if (h2c->state.pool == NULL) {
return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_INTERNAL_ERROR);
}
if (depend == h2c->state.sid) {
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
"client sent HEADERS frame for stream %ui "
"with incorrect dependency", h2c->state.sid);
status = NGX_HTTP_V2_PROTOCOL_ERROR;
goto rst_stream;
}
h2scf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx,
ngx_http_v2_module);
h2c->state.header_limit = h2scf->max_header_size;
if (h2c->processing >= h2scf->concurrent_streams) {
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
"concurrent streams exceeded %ui", h2c->processing);
status = NGX_HTTP_V2_REFUSED_STREAM;
goto rst_stream;
}
if (!h2c->settings_ack
&& !(h2c->state.flags & NGX_HTTP_V2_END_STREAM_FLAG)
&& h2scf->preread_size < NGX_HTTP_V2_DEFAULT_WINDOW)
{
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
"client sent stream with data "
"before settings were acknowledged");
status = NGX_HTTP_V2_REFUSED_STREAM;
goto rst_stream;
}
node = ngx_http_v2_get_node_by_id(h2c, h2c->state.sid, 1);
if (node == NULL) {
return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_INTERNAL_ERROR);
}
if (node->parent) {
ngx_queue_remove(&node->reuse);
h2c->closed_nodes--;
}
stream = ngx_http_v2_create_stream(h2c, 0);
if (stream == NULL) {
return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_INTERNAL_ERROR);
}
h2c->state.stream = stream;
stream->pool = h2c->state.pool;
h2c->state.keep_pool = 1;
stream->request->request_length = h2c->state.length;
stream->in_closed = h2c->state.flags & NGX_HTTP_V2_END_STREAM_FLAG;
stream->node = node;
node->stream = stream;
if (priority || node->parent == NULL) {
node->weight = weight;
ngx_http_v2_set_dependency(h2c, node, depend, excl);
}
if (h2c->connection->requests >= h2scf->max_requests) {
h2c->goaway = 1;
if (ngx_http_v2_send_goaway(h2c, NGX_HTTP_V2_NO_ERROR) == NGX_ERROR) {
return ngx_http_v2_connection_error(h2c,
NGX_HTTP_V2_INTERNAL_ERROR);
}
}
return ngx_http_v2_state_header_block(h2c, pos, end);
rst_stream:
if (ngx_http_v2_send_rst_stream(h2c, h2c->state.sid, status) != NGX_OK) {
return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_INTERNAL_ERROR);
}
return ngx_http_v2_state_header_block(h2c, pos, end);
}
static u_char *
ngx_http_v2_state_header_block(ngx_http_v2_connection_t *h2c, u_char *pos,
u_char *end)
{
u_char ch;
ngx_int_t value;
ngx_uint_t indexed, size_update, prefix;
if (end - pos < 1) {
return ngx_http_v2_state_headers_save(h2c, pos, end,
ngx_http_v2_state_header_block);
}
if (!(h2c->state.flags & NGX_HTTP_V2_END_HEADERS_FLAG)
&& h2c->state.length < NGX_HTTP_V2_INT_OCTETS)
{
return ngx_http_v2_handle_continuation(h2c, pos, end,
ngx_http_v2_state_header_block);
}
size_update = 0;
indexed = 0;
ch = *pos;
if (ch >= (1 << 7)) {
/* indexed header field */
indexed = 1;
prefix = ngx_http_v2_prefix(7);
} else if (ch >= (1 << 6)) {
/* literal header field with incremental indexing */
h2c->state.index = 1;
prefix = ngx_http_v2_prefix(6);
} else if (ch >= (1 << 5)) {
/* dynamic table size update */
size_update = 1;
prefix = ngx_http_v2_prefix(5);
} else if (ch >= (1 << 4)) {
/* literal header field never indexed */
prefix = ngx_http_v2_prefix(4);
} else {
/* literal header field without indexing */
prefix = ngx_http_v2_prefix(4);
}
value = ngx_http_v2_parse_int(h2c, &pos, end, prefix);
if (value < 0) {
if (value == NGX_AGAIN) {
return ngx_http_v2_state_headers_save(h2c, pos, end,
ngx_http_v2_state_header_block);
}
if (value == NGX_DECLINED) {
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
"client sent header block with too long %s value",
size_update ? "size update" : "header index");
return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_COMP_ERROR);
}
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
"client sent header block with incorrect length");
return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR);
}
if (indexed) {
if (ngx_http_v2_get_indexed_header(h2c, value, 0) != NGX_OK) {
return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_COMP_ERROR);
}
return ngx_http_v2_state_process_header(h2c, pos, end);
}
if (size_update) {
if (ngx_http_v2_table_size(h2c, value) != NGX_OK) {
return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_COMP_ERROR);
}
return ngx_http_v2_state_header_complete(h2c, pos, end);
}
if (value == 0) {
h2c->state.parse_name = 1;
} else if (ngx_http_v2_get_indexed_header(h2c, value, 1) != NGX_OK) {
return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_COMP_ERROR);
}
h2c->state.parse_value = 1;
return ngx_http_v2_state_field_len(h2c, pos, end);
}
static u_char *
ngx_http_v2_state_field_len(ngx_http_v2_connection_t *h2c, u_char *pos,
u_char *end)
{
size_t alloc;
ngx_int_t len;
ngx_uint_t huff;
ngx_http_v2_srv_conf_t *h2scf;
if (!(h2c->state.flags & NGX_HTTP_V2_END_HEADERS_FLAG)
&& h2c->state.length < NGX_HTTP_V2_INT_OCTETS)
{
return ngx_http_v2_handle_continuation(h2c, pos, end,
ngx_http_v2_state_field_len);
}
if (h2c->state.length < 1) {
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
"client sent header block with incorrect length");
return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR);
}
if (end - pos < 1) {
return ngx_http_v2_state_headers_save(h2c, pos, end,
ngx_http_v2_state_field_len);
}
huff = *pos >> 7;
len = ngx_http_v2_parse_int(h2c, &pos, end, ngx_http_v2_prefix(7));
if (len < 0) {
if (len == NGX_AGAIN) {
return ngx_http_v2_state_headers_save(h2c, pos, end,
ngx_http_v2_state_field_len);
}
if (len == NGX_DECLINED) {
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
"client sent header field with too long length value");
return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_COMP_ERROR);
}
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
"client sent header block with incorrect length");
return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR);
}
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
"http2 %s string, len:%i",
huff ? "encoded" : "raw", len);
h2scf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx,
ngx_http_v2_module);
if ((size_t) len > h2scf->max_field_size) {
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
"client exceeded http2_max_field_size limit");
return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_ENHANCE_YOUR_CALM);
}
h2c->state.field_rest = len;
if (h2c->state.stream == NULL && !h2c->state.index) {
return ngx_http_v2_state_field_skip(h2c, pos, end);
}
alloc = (huff ? len * 8 / 5 : len) + 1;
h2c->state.field_start = ngx_pnalloc(h2c->state.pool, alloc);
if (h2c->state.field_start == NULL) {
return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_INTERNAL_ERROR);
}
h2c->state.field_end = h2c->state.field_start;
if (huff) {
return ngx_http_v2_state_field_huff(h2c, pos, end);
}
return ngx_http_v2_state_field_raw(h2c, pos, end);
}
static u_char *
ngx_http_v2_state_field_huff(ngx_http_v2_connection_t *h2c, u_char *pos,
u_char *end)
{
size_t size;
size = end - pos;
if (size > h2c->state.field_rest) {
size = h2c->state.field_rest;
}
if (size > h2c->state.length) {
size = h2c->state.length;
}
h2c->state.length -= size;
h2c->state.field_rest -= size;
if (ngx_http_v2_huff_decode(&h2c->state.field_state, pos, size,
&h2c->state.field_end,
h2c->state.field_rest == 0,
h2c->connection->log)
!= NGX_OK)
{
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
"client sent invalid encoded header field");
return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_COMP_ERROR);
}
pos += size;
if (h2c->state.field_rest == 0) {
*h2c->state.field_end = '\0';
return ngx_http_v2_state_process_header(h2c, pos, end);
}
if (h2c->state.length) {
return ngx_http_v2_state_headers_save(h2c, pos, end,
ngx_http_v2_state_field_huff);
}
if (h2c->state.flags & NGX_HTTP_V2_END_HEADERS_FLAG) {
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
"client sent header field with incorrect length");
return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR);
}
return ngx_http_v2_handle_continuation(h2c, pos, end,
ngx_http_v2_state_field_huff);
}
static u_char *
ngx_http_v2_state_field_raw(ngx_http_v2_connection_t *h2c, u_char *pos,
u_char *end)
{
size_t size;
size = end - pos;
if (size > h2c->state.field_rest) {
size = h2c->state.field_rest;
}
if (size > h2c->state.length) {
size = h2c->state.length;
}
h2c->state.length -= size;
h2c->state.field_rest -= size;
h2c->state.field_end = ngx_cpymem(h2c->state.field_end, pos, size);
pos += size;
if (h2c->state.field_rest == 0) {
*h2c->state.field_end = '\0';
return ngx_http_v2_state_process_header(h2c, pos, end);
}
if (h2c->state.length) {
return ngx_http_v2_state_headers_save(h2c, pos, end,
ngx_http_v2_state_field_raw);
}
if (h2c->state.flags & NGX_HTTP_V2_END_HEADERS_FLAG) {
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
"client sent header field with incorrect length");
return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR);
}
return ngx_http_v2_handle_continuation(h2c, pos, end,
ngx_http_v2_state_field_raw);
}
static u_char *
ngx_http_v2_state_field_skip(ngx_http_v2_connection_t *h2c, u_char *pos,
u_char *end)
{
size_t size;
size = end - pos;
if (size > h2c->state.field_rest) {
size = h2c->state.field_rest;
}
if (size > h2c->state.length) {
size = h2c->state.length;
}
h2c->state.length -= size;
h2c->state.field_rest -= size;
pos += size;
if (h2c->state.field_rest == 0) {
return ngx_http_v2_state_process_header(h2c, pos, end);
}
if (h2c->state.length) {
return ngx_http_v2_state_save(h2c, pos, end,
ngx_http_v2_state_field_skip);
}
if (h2c->state.flags & NGX_HTTP_V2_END_HEADERS_FLAG) {
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
"client sent header field with incorrect length");
return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR);
}
return ngx_http_v2_handle_continuation(h2c, pos, end,
ngx_http_v2_state_field_skip);
}
static u_char *
ngx_http_v2_state_process_header(ngx_http_v2_connection_t *h2c, u_char *pos,
u_char *end)
{
size_t len;
ngx_int_t rc;
ngx_table_elt_t *h;
ngx_http_header_t *hh;
ngx_http_request_t *r;
ngx_http_v2_header_t *header;
ngx_http_core_srv_conf_t *cscf;
ngx_http_core_main_conf_t *cmcf;
static ngx_str_t cookie = ngx_string("cookie");
header = &h2c->state.header;
if (h2c->state.parse_name) {
h2c->state.parse_name = 0;
header->name.len = h2c->state.field_end - h2c->state.field_start;
header->name.data = h2c->state.field_start;
return ngx_http_v2_state_field_len(h2c, pos, end);
}
if (h2c->state.parse_value) {
h2c->state.parse_value = 0;
header->value.len = h2c->state.field_end - h2c->state.field_start;
header->value.data = h2c->state.field_start;
}
len = header->name.len + header->value.len;
if (len > h2c->state.header_limit) {
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
"client exceeded http2_max_header_size limit");
return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_ENHANCE_YOUR_CALM);
}
h2c->state.header_limit -= len;
if (h2c->state.index) {
if (ngx_http_v2_add_header(h2c, header) != NGX_OK) {
return ngx_http_v2_connection_error(h2c,
NGX_HTTP_V2_INTERNAL_ERROR);
}
h2c->state.index = 0;
}
if (h2c->state.stream == NULL) {
return ngx_http_v2_state_header_complete(h2c, pos, end);
}
r = h2c->state.stream->request;
/* TODO Optimization: validate headers while parsing. */
if (ngx_http_v2_validate_header(r, header) != NGX_OK) {
if (ngx_http_v2_terminate_stream(h2c, h2c->state.stream,
NGX_HTTP_V2_PROTOCOL_ERROR)
== NGX_ERROR)
{
return ngx_http_v2_connection_error(h2c,
NGX_HTTP_V2_INTERNAL_ERROR);
}
goto error;
}
if (header->name.data[0] == ':') {
rc = ngx_http_v2_pseudo_header(r, header);
if (rc == NGX_OK) {
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http2 header: \":%V: %V\"",
&header->name, &header->value);
return ngx_http_v2_state_header_complete(h2c, pos, end);
}
if (rc == NGX_ABORT) {
goto error;
}
if (rc == NGX_DECLINED) {
ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
goto error;
}
return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_INTERNAL_ERROR);
}
if (r->invalid_header) {
cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
if (cscf->ignore_invalid_headers) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client sent invalid header: \"%V\"", &header->name);
return ngx_http_v2_state_header_complete(h2c, pos, end);
}
}
if (header->name.len == cookie.len
&& ngx_memcmp(header->name.data, cookie.data, cookie.len) == 0)
{
if (ngx_http_v2_cookie(r, header) != NGX_OK) {
return ngx_http_v2_connection_error(h2c,
NGX_HTTP_V2_INTERNAL_ERROR);
}
} else {
h = ngx_list_push(&r->headers_in.headers);
if (h == NULL) {
return ngx_http_v2_connection_error(h2c,
NGX_HTTP_V2_INTERNAL_ERROR);
}
h->key.len = header->name.len;
h->key.data = header->name.data;
/*
* TODO Optimization: precalculate hash
* and handler for indexed headers.
*/
h->hash = ngx_hash_key(h->key.data, h->key.len);
h->value.len = header->value.len;
h->value.data = header->value.data;
h->lowcase_key = h->key.data;
cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
hh = ngx_hash_find(&cmcf->headers_in_hash, h->hash,
h->lowcase_key, h->key.len);
if (hh && hh->handler(r, h, hh->offset) != NGX_OK) {
goto error;
}
}
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http2 header: \"%V: %V\"",
&header->name, &header->value);
return ngx_http_v2_state_header_complete(h2c, pos, end);
error:
h2c->state.stream = NULL;
return ngx_http_v2_state_header_complete(h2c, pos, end);
}
static u_char *
ngx_http_v2_state_header_complete(ngx_http_v2_connection_t *h2c, u_char *pos,
u_char *end)
{
ngx_http_v2_stream_t *stream;
if (h2c->state.length) {
h2c->state.handler = ngx_http_v2_state_header_block;
return pos;
}
if (!(h2c->state.flags & NGX_HTTP_V2_END_HEADERS_FLAG)) {
return ngx_http_v2_handle_continuation(h2c, pos, end,
ngx_http_v2_state_header_complete);
}
stream = h2c->state.stream;
if (stream) {
ngx_http_v2_run_request(stream->request);
}
if (!h2c->state.keep_pool) {
ngx_destroy_pool(h2c->state.pool);
}
h2c->state.pool = NULL;
h2c->state.keep_pool = 0;
if (h2c->state.padding) {
return ngx_http_v2_state_skip_padded(h2c, pos, end);
}
return ngx_http_v2_state_complete(h2c, pos, end);
}
static u_char *
ngx_http_v2_handle_continuation(ngx_http_v2_connection_t *h2c, u_char *pos,
u_char *end, ngx_http_v2_handler_pt handler)
{
u_char *p;
size_t len, skip;
uint32_t head;
len = h2c->state.length;
if (h2c->state.padding && (size_t) (end - pos) > len) {
skip = ngx_min(h2c->state.padding, (end - pos) - len);
h2c->state.padding -= skip;
p = pos;
pos += skip;
ngx_memmove(pos, p, len);
}
if ((size_t) (end - pos) < len + NGX_HTTP_V2_FRAME_HEADER_SIZE) {
return ngx_http_v2_state_headers_save(h2c, pos, end, handler);
}
p = pos + len;
head = ngx_http_v2_parse_uint32(p);
if (ngx_http_v2_parse_type(head) != NGX_HTTP_V2_CONTINUATION_FRAME) {
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
"client sent inappropriate frame while CONTINUATION was expected");
return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR);
}
h2c->state.flags |= p[4];
if (h2c->state.sid != ngx_http_v2_parse_sid(&p[5])) {
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
"client sent CONTINUATION frame with incorrect identifier");
return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR);
}
p = pos;
pos += NGX_HTTP_V2_FRAME_HEADER_SIZE;
ngx_memcpy(pos, p, len);
len = ngx_http_v2_parse_length(head);
h2c->state.length += len;
if (h2c->state.stream) {
h2c->state.stream->request->request_length += len;
}
h2c->state.handler = handler;
return pos;
}
static u_char *
ngx_http_v2_state_priority(ngx_http_v2_connection_t *h2c, u_char *pos,
u_char *end)
{
ngx_uint_t depend, dependency, excl, weight;
ngx_http_v2_node_t *node;
if (h2c->state.length != NGX_HTTP_V2_PRIORITY_SIZE) {
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
"client sent PRIORITY frame with incorrect length %uz",
h2c->state.length);
return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR);
}
if (end - pos < NGX_HTTP_V2_PRIORITY_SIZE) {
return ngx_http_v2_state_save(h2c, pos, end,
ngx_http_v2_state_priority);
}
dependency = ngx_http_v2_parse_uint32(pos);
depend = dependency & 0x7fffffff;
excl = dependency >> 31;
weight = pos[4] + 1;
pos += NGX_HTTP_V2_PRIORITY_SIZE;
ngx_log_debug4(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
"http2 PRIORITY frame sid:%ui "
"depends on %ui excl:%ui weight:%ui",
h2c->state.sid, depend, excl, weight);
if (h2c->state.sid == 0) {
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
"client sent PRIORITY frame with incorrect identifier");
return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR);
}
if (depend == h2c->state.sid) {
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
"client sent PRIORITY frame for stream %ui "
"with incorrect dependency", h2c->state.sid);
node = ngx_http_v2_get_node_by_id(h2c, h2c->state.sid, 0);
if (node && node->stream) {
if (ngx_http_v2_terminate_stream(h2c, node->stream,
NGX_HTTP_V2_PROTOCOL_ERROR)
== NGX_ERROR)
{
return ngx_http_v2_connection_error(h2c,
NGX_HTTP_V2_INTERNAL_ERROR);
}
} else {
if (ngx_http_v2_send_rst_stream(h2c, h2c->state.sid,
NGX_HTTP_V2_PROTOCOL_ERROR)
== NGX_ERROR)
{
return ngx_http_v2_connection_error(h2c,
NGX_HTTP_V2_INTERNAL_ERROR);
}
}
return ngx_http_v2_state_complete(h2c, pos, end);
}
node = ngx_http_v2_get_node_by_id(h2c, h2c->state.sid, 1);
if (node == NULL) {
return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_INTERNAL_ERROR);
}
node->weight = weight;
if (node->stream == NULL) {
if (node->parent == NULL) {
h2c->closed_nodes++;
} else {
ngx_queue_remove(&node->reuse);
}
ngx_queue_insert_tail(&h2c->closed, &node->reuse);
}
ngx_http_v2_set_dependency(h2c, node, depend, excl);
return ngx_http_v2_state_complete(h2c, pos, end);
}
static u_char *
ngx_http_v2_state_rst_stream(ngx_http_v2_connection_t *h2c, u_char *pos,
u_char *end)
{
ngx_uint_t status;
ngx_event_t *ev;
ngx_connection_t *fc;
ngx_http_v2_node_t *node;
ngx_http_v2_stream_t *stream;
if (h2c->state.length != NGX_HTTP_V2_RST_STREAM_SIZE) {
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
"client sent RST_STREAM frame with incorrect length %uz",
h2c->state.length);
return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR);
}
if (end - pos < NGX_HTTP_V2_RST_STREAM_SIZE) {
return ngx_http_v2_state_save(h2c, pos, end,
ngx_http_v2_state_rst_stream);
}
status = ngx_http_v2_parse_uint32(pos);
pos += NGX_HTTP_V2_RST_STREAM_SIZE;
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
"http2 RST_STREAM frame, sid:%ui status:%ui",
h2c->state.sid, status);
if (h2c->state.sid == 0) {
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
"client sent RST_STREAM frame with incorrect identifier");
return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR);
}
node = ngx_http_v2_get_node_by_id(h2c, h2c->state.sid, 0);
if (node == NULL || node->stream == NULL) {
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
"unknown http2 stream");
return ngx_http_v2_state_complete(h2c, pos, end);
}
stream = node->stream;
stream->in_closed = 1;
stream->out_closed = 1;
fc = stream->request->connection;
fc->error = 1;
switch (status) {
case NGX_HTTP_V2_CANCEL:
ngx_log_error(NGX_LOG_INFO, fc->log, 0,
"client canceled stream %ui", h2c->state.sid);
break;
case NGX_HTTP_V2_REFUSED_STREAM:
ngx_log_error(NGX_LOG_INFO, fc->log, 0,
"client refused stream %ui", h2c->state.sid);
break;
case NGX_HTTP_V2_INTERNAL_ERROR:
ngx_log_error(NGX_LOG_INFO, fc->log, 0,
"client terminated stream %ui due to internal error",
h2c->state.sid);
break;
default:
ngx_log_error(NGX_LOG_INFO, fc->log, 0,
"client terminated stream %ui with status %ui",
h2c->state.sid, status);
break;
}
ev = fc->read;
ev->handler(ev);
return ngx_http_v2_state_complete(h2c, pos, end);
}
static u_char *
ngx_http_v2_state_settings(ngx_http_v2_connection_t *h2c, u_char *pos,
u_char *end)
{
if (h2c->state.flags == NGX_HTTP_V2_ACK_FLAG) {
if (h2c->state.length != 0) {
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
"client sent SETTINGS frame with the ACK flag "
"and nonzero length");
return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR);
}
h2c->settings_ack = 1;
return ngx_http_v2_state_complete(h2c, pos, end);
}
if (h2c->state.length % NGX_HTTP_V2_SETTINGS_PARAM_SIZE) {
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
"client sent SETTINGS frame with incorrect length %uz",
h2c->state.length);
return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR);
}
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
"http2 SETTINGS frame");
return ngx_http_v2_state_settings_params(h2c, pos, end);
}
static u_char *
ngx_http_v2_state_settings_params(ngx_http_v2_connection_t *h2c, u_char *pos,
u_char *end)
{
ssize_t window_delta;
ngx_uint_t id, value;
ngx_http_v2_srv_conf_t *h2scf;
ngx_http_v2_out_frame_t *frame;
window_delta = 0;
while (h2c->state.length) {
if (end - pos < NGX_HTTP_V2_SETTINGS_PARAM_SIZE) {
return ngx_http_v2_state_save(h2c, pos, end,
ngx_http_v2_state_settings_params);
}
h2c->state.length -= NGX_HTTP_V2_SETTINGS_PARAM_SIZE;
id = ngx_http_v2_parse_uint16(pos);
value = ngx_http_v2_parse_uint32(&pos[2]);
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
"http2 setting %ui:%ui", id, value);
switch (id) {
case NGX_HTTP_V2_INIT_WINDOW_SIZE_SETTING:
if (value > NGX_HTTP_V2_MAX_WINDOW) {
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
"client sent SETTINGS frame with incorrect "
"INITIAL_WINDOW_SIZE value %ui", value);
return ngx_http_v2_connection_error(h2c,
NGX_HTTP_V2_FLOW_CTRL_ERROR);
}
window_delta = value - h2c->init_window;
break;
case NGX_HTTP_V2_MAX_FRAME_SIZE_SETTING:
if (value > NGX_HTTP_V2_MAX_FRAME_SIZE
|| value < NGX_HTTP_V2_DEFAULT_FRAME_SIZE)
{
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
"client sent SETTINGS frame with incorrect "
"MAX_FRAME_SIZE value %ui", value);
return ngx_http_v2_connection_error(h2c,
NGX_HTTP_V2_PROTOCOL_ERROR);
}
h2c->frame_size = value;
break;
case NGX_HTTP_V2_ENABLE_PUSH_SETTING:
if (value > 1) {
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
"client sent SETTINGS frame with incorrect "
"ENABLE_PUSH value %ui", value);
return ngx_http_v2_connection_error(h2c,
NGX_HTTP_V2_PROTOCOL_ERROR);
}
h2c->push_disabled = !value;
break;
case NGX_HTTP_V2_MAX_STREAMS_SETTING:
h2scf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx,
ngx_http_v2_module);
h2c->concurrent_pushes = ngx_min(value, h2scf->concurrent_pushes);
break;
default:
break;
}
pos += NGX_HTTP_V2_SETTINGS_PARAM_SIZE;
}
frame = ngx_http_v2_get_frame(h2c, NGX_HTTP_V2_SETTINGS_ACK_SIZE,
NGX_HTTP_V2_SETTINGS_FRAME,
NGX_HTTP_V2_ACK_FLAG, 0);
if (frame == NULL) {
return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_INTERNAL_ERROR);
}
ngx_http_v2_queue_ordered_frame(h2c, frame);
if (window_delta) {
h2c->init_window += window_delta;
if (ngx_http_v2_adjust_windows(h2c, window_delta) != NGX_OK) {
return ngx_http_v2_connection_error(h2c,
NGX_HTTP_V2_INTERNAL_ERROR);
}
}
return ngx_http_v2_state_complete(h2c, pos, end);
}
static u_char *
ngx_http_v2_state_push_promise(ngx_http_v2_connection_t *h2c, u_char *pos,
u_char *end)
{
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
"client sent PUSH_PROMISE frame");
return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR);
}
static u_char *
ngx_http_v2_state_ping(ngx_http_v2_connection_t *h2c, u_char *pos, u_char *end)
{
ngx_buf_t *buf;
ngx_http_v2_out_frame_t *frame;
if (h2c->state.length != NGX_HTTP_V2_PING_SIZE) {
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
"client sent PING frame with incorrect length %uz",
h2c->state.length);
return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR);
}
if (end - pos < NGX_HTTP_V2_PING_SIZE) {
return ngx_http_v2_state_save(h2c, pos, end, ngx_http_v2_state_ping);
}
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
"http2 PING frame");
if (h2c->state.flags & NGX_HTTP_V2_ACK_FLAG) {
return ngx_http_v2_state_skip(h2c, pos, end);
}
frame = ngx_http_v2_get_frame(h2c, NGX_HTTP_V2_PING_SIZE,
NGX_HTTP_V2_PING_FRAME,
NGX_HTTP_V2_ACK_FLAG, 0);
if (frame == NULL) {
return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_INTERNAL_ERROR);
}
buf = frame->first->buf;
buf->last = ngx_cpymem(buf->last, pos, NGX_HTTP_V2_PING_SIZE);
ngx_http_v2_queue_blocked_frame(h2c, frame);
return ngx_http_v2_state_complete(h2c, pos + NGX_HTTP_V2_PING_SIZE, end);
}
static u_char *
ngx_http_v2_state_goaway(ngx_http_v2_connection_t *h2c, u_char *pos,
u_char *end)
{
#if (NGX_DEBUG)
ngx_uint_t last_sid, error;
#endif
if (h2c->state.length < NGX_HTTP_V2_GOAWAY_SIZE) {
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
"client sent GOAWAY frame "
"with incorrect length %uz", h2c->state.length);
return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR);
}
if (end - pos < NGX_HTTP_V2_GOAWAY_SIZE) {
return ngx_http_v2_state_save(h2c, pos, end, ngx_http_v2_state_goaway);
}
#if (NGX_DEBUG)
h2c->state.length -= NGX_HTTP_V2_GOAWAY_SIZE;
last_sid = ngx_http_v2_parse_sid(pos);
error = ngx_http_v2_parse_uint32(&pos[4]);
pos += NGX_HTTP_V2_GOAWAY_SIZE;
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
"http2 GOAWAY frame: last sid %ui, error %ui",
last_sid, error);
#endif
return ngx_http_v2_state_skip(h2c, pos, end);
}
static u_char *
ngx_http_v2_state_window_update(ngx_http_v2_connection_t *h2c, u_char *pos,
u_char *end)
{
size_t window;
ngx_event_t *wev;
ngx_queue_t *q;
ngx_http_v2_node_t *node;
ngx_http_v2_stream_t *stream;
if (h2c->state.length != NGX_HTTP_V2_WINDOW_UPDATE_SIZE) {
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
"client sent WINDOW_UPDATE frame "
"with incorrect length %uz", h2c->state.length);
return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR);
}
if (end - pos < NGX_HTTP_V2_WINDOW_UPDATE_SIZE) {
return ngx_http_v2_state_save(h2c, pos, end,
ngx_http_v2_state_window_update);
}
window = ngx_http_v2_parse_window(pos);
pos += NGX_HTTP_V2_WINDOW_UPDATE_SIZE;
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
"http2 WINDOW_UPDATE frame sid:%ui window:%uz",
h2c->state.sid, window);
if (window == 0) {
if (h2c->state.sid == 0) {
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
"client sent WINDOW_UPDATE frame "
"with incorrect window increment 0");
return ngx_http_v2_connection_error(h2c,
NGX_HTTP_V2_PROTOCOL_ERROR);
}
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
"client sent WINDOW_UPDATE frame for stream %ui "
"with incorrect window increment 0", h2c->state.sid);
node = ngx_http_v2_get_node_by_id(h2c, h2c->state.sid, 0);
if (node && node->stream) {
if (ngx_http_v2_terminate_stream(h2c, node->stream,
NGX_HTTP_V2_PROTOCOL_ERROR)
== NGX_ERROR)
{
return ngx_http_v2_connection_error(h2c,
NGX_HTTP_V2_INTERNAL_ERROR);
}
} else {
if (ngx_http_v2_send_rst_stream(h2c, h2c->state.sid,
NGX_HTTP_V2_PROTOCOL_ERROR)
== NGX_ERROR)
{
return ngx_http_v2_connection_error(h2c,
NGX_HTTP_V2_INTERNAL_ERROR);
}
}
return ngx_http_v2_state_complete(h2c, pos, end);
}
if (h2c->state.sid) {
node = ngx_http_v2_get_node_by_id(h2c, h2c->state.sid, 0);
if (node == NULL || node->stream == NULL) {
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
"unknown http2 stream");
return ngx_http_v2_state_complete(h2c, pos, end);
}
stream = node->stream;
if (window > (size_t) (NGX_HTTP_V2_MAX_WINDOW - stream->send_window)) {
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
"client violated flow control for stream %ui: "
"received WINDOW_UPDATE frame "
"with window increment %uz "
"not allowed for window %z",
h2c->state.sid, window, stream->send_window);
if (ngx_http_v2_terminate_stream(h2c, stream,
NGX_HTTP_V2_FLOW_CTRL_ERROR)
== NGX_ERROR)
{
return ngx_http_v2_connection_error(h2c,
NGX_HTTP_V2_INTERNAL_ERROR);
}
return ngx_http_v2_state_complete(h2c, pos, end);
}
stream->send_window += window;
if (stream->exhausted) {
stream->exhausted = 0;
wev = stream->request->connection->write;
wev->active = 0;
wev->ready = 1;
if (!wev->delayed) {
wev->handler(wev);
}
}
return ngx_http_v2_state_complete(h2c, pos, end);
}
if (window > NGX_HTTP_V2_MAX_WINDOW - h2c->send_window) {
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
"client violated connection flow control: "
"received WINDOW_UPDATE frame "
"with window increment %uz "
"not allowed for window %uz",
window, h2c->send_window);
return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_FLOW_CTRL_ERROR);
}
h2c->send_window += window;
while (!ngx_queue_empty(&h2c->waiting)) {
q = ngx_queue_head(&h2c->waiting);
ngx_queue_remove(q);
stream = ngx_queue_data(q, ngx_http_v2_stream_t, queue);
stream->waiting = 0;
wev = stream->request->connection->write;
wev->active = 0;
wev->ready = 1;
if (!wev->delayed) {
wev->handler(wev);
if (h2c->send_window == 0) {
break;
}
}
}
return ngx_http_v2_state_complete(h2c, pos, end);
}
static u_char *
ngx_http_v2_state_continuation(ngx_http_v2_connection_t *h2c, u_char *pos,
u_char *end)
{
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
"client sent unexpected CONTINUATION frame");
return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR);
}
static u_char *
ngx_http_v2_state_complete(ngx_http_v2_connection_t *h2c, u_char *pos,
u_char *end)
{
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
"http2 frame complete pos:%p end:%p", pos, end);
if (pos > end) {
ngx_log_error(NGX_LOG_ALERT, h2c->connection->log, 0,
"receive buffer overrun");
return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_INTERNAL_ERROR);
}
h2c->state.stream = NULL;
h2c->state.handler = ngx_http_v2_state_head;
return pos;
}
static u_char *
ngx_http_v2_state_skip_padded(ngx_http_v2_connection_t *h2c, u_char *pos,
u_char *end)
{
h2c->state.length += h2c->state.padding;
h2c->state.padding = 0;
return ngx_http_v2_state_skip(h2c, pos, end);
}
static u_char *
ngx_http_v2_state_skip(ngx_http_v2_connection_t *h2c, u_char *pos, u_char *end)
{
size_t size;
size = end - pos;
if (size < h2c->state.length) {
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
"http2 frame skip %uz of %uz", size, h2c->state.length);
h2c->state.length -= size;
return ngx_http_v2_state_save(h2c, end, end, ngx_http_v2_state_skip);
}
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
"http2 frame skip %uz", h2c->state.length);
return ngx_http_v2_state_complete(h2c, pos + h2c->state.length, end);
}
static u_char *
ngx_http_v2_state_save(ngx_http_v2_connection_t *h2c, u_char *pos, u_char *end,
ngx_http_v2_handler_pt handler)
{
size_t size;
ngx_log_debug3(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
"http2 frame state save pos:%p end:%p handler:%p",
pos, end, handler);
size = end - pos;
if (size > NGX_HTTP_V2_STATE_BUFFER_SIZE) {
ngx_log_error(NGX_LOG_ALERT, h2c->connection->log, 0,
"state buffer overflow: %uz bytes required", size);
return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_INTERNAL_ERROR);
}
ngx_memcpy(h2c->state.buffer, pos, NGX_HTTP_V2_STATE_BUFFER_SIZE);
h2c->state.buffer_used = size;
h2c->state.handler = handler;
h2c->state.incomplete = 1;
return end;
}
static u_char *
ngx_http_v2_state_headers_save(ngx_http_v2_connection_t *h2c, u_char *pos,
u_char *end, ngx_http_v2_handler_pt handler)
{
ngx_event_t *rev;
ngx_http_request_t *r;
ngx_http_core_srv_conf_t *cscf;
if (h2c->state.stream) {
r = h2c->state.stream->request;
rev = r->connection->read;
if (!rev->timer_set) {
cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
ngx_add_timer(rev, cscf->client_header_timeout);
}
}
return ngx_http_v2_state_save(h2c, pos, end, handler);
}
static u_char *
ngx_http_v2_connection_error(ngx_http_v2_connection_t *h2c,
ngx_uint_t err)
{
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
"http2 state connection error");
if (err == NGX_HTTP_V2_INTERNAL_ERROR) {
ngx_debug_point();
}
ngx_http_v2_finalize_connection(h2c, err);
return NULL;
}
static ngx_int_t
ngx_http_v2_parse_int(ngx_http_v2_connection_t *h2c, u_char **pos, u_char *end,
ngx_uint_t prefix)
{
u_char *start, *p;
ngx_uint_t value, octet, shift;
start = *pos;
p = start;
value = *p++ & prefix;
if (value != prefix) {
if (h2c->state.length == 0) {
return NGX_ERROR;
}
h2c->state.length--;
*pos = p;
return value;
}
if (end - start > NGX_HTTP_V2_INT_OCTETS) {
end = start + NGX_HTTP_V2_INT_OCTETS;
}
for (shift = 0; p != end; shift += 7) {
octet = *p++;
value += (octet & 0x7f) << shift;
if (octet < 128) {
if ((size_t) (p - start) > h2c->state.length) {
return NGX_ERROR;
}
h2c->state.length -= p - start;
*pos = p;
return value;
}
}
if ((size_t) (end - start) >= h2c->state.length) {
return NGX_ERROR;
}
if (end == start + NGX_HTTP_V2_INT_OCTETS) {
return NGX_DECLINED;
}
return NGX_AGAIN;
}
ngx_http_v2_stream_t *
ngx_http_v2_push_stream(ngx_http_v2_stream_t *parent, ngx_str_t *path)
{
ngx_int_t rc;
ngx_str_t value;
ngx_pool_t *pool;
ngx_uint_t index;
ngx_table_elt_t **h;
ngx_connection_t *fc;
ngx_http_request_t *r;
ngx_http_v2_node_t *node;
ngx_http_v2_stream_t *stream;
ngx_http_v2_srv_conf_t *h2scf;
ngx_http_v2_connection_t *h2c;
ngx_http_v2_parse_header_t *header;
h2c = parent->connection;
pool = ngx_create_pool(1024, h2c->connection->log);
if (pool == NULL) {
goto rst_stream;
}
node = ngx_http_v2_get_node_by_id(h2c, h2c->last_push, 1);
if (node == NULL) {
ngx_destroy_pool(pool);
goto rst_stream;
}
stream = ngx_http_v2_create_stream(h2c, 1);
if (stream == NULL) {
if (node->parent == NULL) {
h2scf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx,
ngx_http_v2_module);
index = ngx_http_v2_index(h2scf, h2c->last_push);
h2c->streams_index[index] = node->index;
ngx_queue_insert_tail(&h2c->closed, &node->reuse);
h2c->closed_nodes++;
}
ngx_destroy_pool(pool);
goto rst_stream;
}
if (node->parent) {
ngx_queue_remove(&node->reuse);
h2c->closed_nodes--;
}
stream->pool = pool;
r = stream->request;
fc = r->connection;
stream->in_closed = 1;
stream->node = node;
node->stream = stream;
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
"http2 push stream sid:%ui "
"depends on %ui excl:0 weight:16",
h2c->last_push, parent->node->id);
node->weight = NGX_HTTP_V2_DEFAULT_WEIGHT;
ngx_http_v2_set_dependency(h2c, node, parent->node->id, 0);
r->method_name = ngx_http_core_get_method;
r->method = NGX_HTTP_GET;
r->schema_start = (u_char *) "https";
#if (NGX_HTTP_SSL)
if (fc->ssl) {
r->schema_end = r->schema_start + 5;
} else
#endif
{
r->schema_end = r->schema_start + 4;
}
value.data = ngx_pstrdup(pool, path);
if (value.data == NULL) {
goto close;
}
value.len = path->len;
rc = ngx_http_v2_parse_path(r, &value);
if (rc != NGX_OK) {
goto error;
}
for (header = ngx_http_v2_parse_headers; header->name.len; header++) {
h = (ngx_table_elt_t **)
((char *) &parent->request->headers_in + header->offset);
if (*h == NULL) {
continue;
}
value.len = (*h)->value.len;
value.data = ngx_pnalloc(pool, value.len + 1);
if (value.data == NULL) {
goto close;
}
ngx_memcpy(value.data, (*h)->value.data, value.len);
value.data[value.len] = '\0';
rc = ngx_http_v2_parse_header(r, header, &value);
if (rc != NGX_OK) {
goto error;
}
}
fc->write->handler = ngx_http_v2_run_request_handler;
ngx_post_event(fc->write, &ngx_posted_events);
return stream;
error:
if (rc == NGX_ABORT) {
/* header handler has already finalized request */
return NULL;
}
if (rc == NGX_DECLINED) {
ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
return NULL;
}
close:
ngx_http_v2_close_stream(stream, NGX_HTTP_INTERNAL_SERVER_ERROR);
return NULL;
rst_stream:
if (ngx_http_v2_send_rst_stream(h2c, h2c->last_push,
NGX_HTTP_INTERNAL_SERVER_ERROR)
!= NGX_OK)
{
h2c->connection->error = 1;
}
return NULL;
}
static ngx_int_t
ngx_http_v2_send_settings(ngx_http_v2_connection_t *h2c)
{
size_t len;
ngx_buf_t *buf;
ngx_chain_t *cl;
ngx_http_v2_srv_conf_t *h2scf;
ngx_http_v2_out_frame_t *frame;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
"http2 send SETTINGS frame");
frame = ngx_palloc(h2c->pool, sizeof(ngx_http_v2_out_frame_t));
if (frame == NULL) {
return NGX_ERROR;
}
cl = ngx_alloc_chain_link(h2c->pool);
if (cl == NULL) {
return NGX_ERROR;
}
len = NGX_HTTP_V2_SETTINGS_PARAM_SIZE * 3;
buf = ngx_create_temp_buf(h2c->pool, NGX_HTTP_V2_FRAME_HEADER_SIZE + len);
if (buf == NULL) {
return NGX_ERROR;
}
buf->last_buf = 1;
cl->buf = buf;
cl->next = NULL;
frame->first = cl;
frame->last = cl;
frame->handler = ngx_http_v2_settings_frame_handler;
frame->stream = NULL;
#if (NGX_DEBUG)
frame->length = len;
#endif
frame->blocked = 0;
buf->last = ngx_http_v2_write_len_and_type(buf->last, len,
NGX_HTTP_V2_SETTINGS_FRAME);
*buf->last++ = NGX_HTTP_V2_NO_FLAG;
buf->last = ngx_http_v2_write_sid(buf->last, 0);
h2scf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx,
ngx_http_v2_module);
buf->last = ngx_http_v2_write_uint16(buf->last,
NGX_HTTP_V2_MAX_STREAMS_SETTING);
buf->last = ngx_http_v2_write_uint32(buf->last,
h2scf->concurrent_streams);
buf->last = ngx_http_v2_write_uint16(buf->last,
NGX_HTTP_V2_INIT_WINDOW_SIZE_SETTING);
buf->last = ngx_http_v2_write_uint32(buf->last, h2scf->preread_size);
buf->last = ngx_http_v2_write_uint16(buf->last,
NGX_HTTP_V2_MAX_FRAME_SIZE_SETTING);
buf->last = ngx_http_v2_write_uint32(buf->last,
NGX_HTTP_V2_MAX_FRAME_SIZE);
ngx_http_v2_queue_blocked_frame(h2c, frame);
return NGX_OK;
}
static ngx_int_t
ngx_http_v2_settings_frame_handler(ngx_http_v2_connection_t *h2c,
ngx_http_v2_out_frame_t *frame)
{
ngx_buf_t *buf;
buf = frame->first->buf;
if (buf->pos != buf->last) {
return NGX_AGAIN;
}
ngx_free_chain(h2c->pool, frame->first);
return NGX_OK;
}
static ngx_int_t
ngx_http_v2_send_window_update(ngx_http_v2_connection_t *h2c, ngx_uint_t sid,
size_t window)
{
ngx_buf_t *buf;
ngx_http_v2_out_frame_t *frame;
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
"http2 send WINDOW_UPDATE frame sid:%ui, window:%uz",
sid, window);
frame = ngx_http_v2_get_frame(h2c, NGX_HTTP_V2_WINDOW_UPDATE_SIZE,
NGX_HTTP_V2_WINDOW_UPDATE_FRAME,
NGX_HTTP_V2_NO_FLAG, sid);
if (frame == NULL) {
return NGX_ERROR;
}
buf = frame->first->buf;
buf->last = ngx_http_v2_write_uint32(buf->last, window);
ngx_http_v2_queue_blocked_frame(h2c, frame);
return NGX_OK;
}
static ngx_int_t
ngx_http_v2_send_rst_stream(ngx_http_v2_connection_t *h2c, ngx_uint_t sid,
ngx_uint_t status)
{
ngx_buf_t *buf;
ngx_http_v2_out_frame_t *frame;
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
"http2 send RST_STREAM frame sid:%ui, status:%ui",
sid, status);
frame = ngx_http_v2_get_frame(h2c, NGX_HTTP_V2_RST_STREAM_SIZE,
NGX_HTTP_V2_RST_STREAM_FRAME,
NGX_HTTP_V2_NO_FLAG, sid);
if (frame == NULL) {
return NGX_ERROR;
}
buf = frame->first->buf;
buf->last = ngx_http_v2_write_uint32(buf->last, status);
ngx_http_v2_queue_blocked_frame(h2c, frame);
return NGX_OK;
}
static ngx_int_t
ngx_http_v2_send_goaway(ngx_http_v2_connection_t *h2c, ngx_uint_t status)
{
ngx_buf_t *buf;
ngx_http_v2_out_frame_t *frame;
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
"http2 send GOAWAY frame: last sid %ui, error %ui",
h2c->last_sid, status);
frame = ngx_http_v2_get_frame(h2c, NGX_HTTP_V2_GOAWAY_SIZE,
NGX_HTTP_V2_GOAWAY_FRAME,
NGX_HTTP_V2_NO_FLAG, 0);
if (frame == NULL) {
return NGX_ERROR;
}
buf = frame->first->buf;
buf->last = ngx_http_v2_write_sid(buf->last, h2c->last_sid);
buf->last = ngx_http_v2_write_uint32(buf->last, status);
ngx_http_v2_queue_blocked_frame(h2c, frame);
return NGX_OK;
}
static ngx_http_v2_out_frame_t *
ngx_http_v2_get_frame(ngx_http_v2_connection_t *h2c, size_t length,
ngx_uint_t type, u_char flags, ngx_uint_t sid)
{
ngx_buf_t *buf;
ngx_pool_t *pool;
ngx_http_v2_out_frame_t *frame;
frame = h2c->free_frames;
if (frame) {
h2c->free_frames = frame->next;
buf = frame->first->buf;
buf->pos = buf->start;
frame->blocked = 0;
} else {
pool = h2c->pool ? h2c->pool : h2c->connection->pool;
frame = ngx_pcalloc(pool, sizeof(ngx_http_v2_out_frame_t));
if (frame == NULL) {
return NULL;
}
frame->first = ngx_alloc_chain_link(pool);
if (frame->first == NULL) {
return NULL;
}
buf = ngx_create_temp_buf(pool, NGX_HTTP_V2_FRAME_BUFFER_SIZE);
if (buf == NULL) {
return NULL;
}
buf->last_buf = 1;
frame->first->buf = buf;
frame->last = frame->first;
frame->handler = ngx_http_v2_frame_handler;
}
#if (NGX_DEBUG)
if (length > NGX_HTTP_V2_FRAME_BUFFER_SIZE - NGX_HTTP_V2_FRAME_HEADER_SIZE)
{
ngx_log_error(NGX_LOG_ALERT, h2c->connection->log, 0,
"requested control frame is too large: %uz", length);
return NULL;
}
frame->length = length;
#endif
buf->last = ngx_http_v2_write_len_and_type(buf->pos, length, type);
*buf->last++ = flags;
buf->last = ngx_http_v2_write_sid(buf->last, sid);
return frame;
}
static ngx_int_t
ngx_http_v2_frame_handler(ngx_http_v2_connection_t *h2c,
ngx_http_v2_out_frame_t *frame)
{
ngx_buf_t *buf;
buf = frame->first->buf;
if (buf->pos != buf->last) {
return NGX_AGAIN;
}
frame->next = h2c->free_frames;
h2c->free_frames = frame;
return NGX_OK;
}
static ngx_http_v2_stream_t *
ngx_http_v2_create_stream(ngx_http_v2_connection_t *h2c, ngx_uint_t push)
{
ngx_log_t *log;
ngx_event_t *rev, *wev;
ngx_connection_t *fc;
ngx_http_log_ctx_t *ctx;
ngx_http_request_t *r;
ngx_http_v2_stream_t *stream;
ngx_http_v2_srv_conf_t *h2scf;
ngx_http_core_srv_conf_t *cscf;
fc = h2c->free_fake_connections;
if (fc) {
h2c->free_fake_connections = fc->data;
rev = fc->read;
wev = fc->write;
log = fc->log;
ctx = log->data;
} else {
fc = ngx_palloc(h2c->pool, sizeof(ngx_connection_t));
if (fc == NULL) {
return NULL;
}
rev = ngx_palloc(h2c->pool, sizeof(ngx_event_t));
if (rev == NULL) {
return NULL;
}
wev = ngx_palloc(h2c->pool, sizeof(ngx_event_t));
if (wev == NULL) {
return NULL;
}
log = ngx_palloc(h2c->pool, sizeof(ngx_log_t));
if (log == NULL) {
return NULL;
}
ctx = ngx_palloc(h2c->pool, sizeof(ngx_http_log_ctx_t));
if (ctx == NULL) {
return NULL;
}
ctx->connection = fc;
ctx->request = NULL;
ctx->current_request = NULL;
}
ngx_memcpy(log, h2c->connection->log, sizeof(ngx_log_t));
log->data = ctx;
if (push) {
log->action = "processing pushed request headers";
} else {
log->action = "reading client request headers";
}
ngx_memzero(rev, sizeof(ngx_event_t));
rev->data = fc;
rev->ready = 1;
rev->handler = ngx_http_v2_close_stream_handler;
rev->log = log;
ngx_memcpy(wev, rev, sizeof(ngx_event_t));
wev->write = 1;
ngx_memcpy(fc, h2c->connection, sizeof(ngx_connection_t));
fc->data = h2c->http_connection;
fc->read = rev;
fc->write = wev;
fc->sent = 0;
fc->log = log;
fc->buffered = 0;
fc->sndlowat = 1;
fc->tcp_nodelay = NGX_TCP_NODELAY_DISABLED;
r = ngx_http_create_request(fc);
if (r == NULL) {
return NULL;
}
ngx_str_set(&r->http_protocol, "HTTP/2.0");
r->http_version = NGX_HTTP_VERSION_20;
r->valid_location = 1;
fc->data = r;
h2c->connection->requests++;
cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
r->header_in = ngx_create_temp_buf(r->pool,
cscf->client_header_buffer_size);
if (r->header_in == NULL) {
ngx_http_free_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
return NULL;
}
if (ngx_list_init(&r->headers_in.headers, r->pool, 20,
sizeof(ngx_table_elt_t))
!= NGX_OK)
{
ngx_http_free_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
return NULL;
}
r->headers_in.connection_type = NGX_HTTP_CONNECTION_CLOSE;
stream = ngx_pcalloc(r->pool, sizeof(ngx_http_v2_stream_t));
if (stream == NULL) {
ngx_http_free_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
return NULL;
}
r->stream = stream;
stream->request = r;
stream->connection = h2c;
h2scf = ngx_http_get_module_srv_conf(r, ngx_http_v2_module);
stream->send_window = h2c->init_window;
stream->recv_window = h2scf->preread_size;
if (push) {
h2c->pushing++;
} else {
h2c->processing++;
}
return stream;
}
static ngx_http_v2_node_t *
ngx_http_v2_get_node_by_id(ngx_http_v2_connection_t *h2c, ngx_uint_t sid,
ngx_uint_t alloc)
{
ngx_uint_t index;
ngx_http_v2_node_t *node;
ngx_http_v2_srv_conf_t *h2scf;
h2scf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx,
ngx_http_v2_module);
index = ngx_http_v2_index(h2scf, sid);
for (node = h2c->streams_index[index]; node; node = node->index) {
if (node->id == sid) {
return node;
}
}
if (!alloc) {
return NULL;
}
if (h2c->closed_nodes < 32) {
node = ngx_pcalloc(h2c->connection->pool, sizeof(ngx_http_v2_node_t));
if (node == NULL) {
return NULL;
}
} else {
node = ngx_http_v2_get_closed_node(h2c);
}
node->id = sid;
ngx_queue_init(&node->children);
node->index = h2c->streams_index[index];
h2c->streams_index[index] = node;
return node;
}
static ngx_http_v2_node_t *
ngx_http_v2_get_closed_node(ngx_http_v2_connection_t *h2c)
{
ngx_uint_t weight;
ngx_queue_t *q, *children;
ngx_http_v2_node_t *node, **next, *n, *parent, *child;
ngx_http_v2_srv_conf_t *h2scf;
h2scf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx,
ngx_http_v2_module);
h2c->closed_nodes--;
q = ngx_queue_head(&h2c->closed);
ngx_queue_remove(q);
node = ngx_queue_data(q, ngx_http_v2_node_t, reuse);
next = &h2c->streams_index[ngx_http_v2_index(h2scf, node->id)];
for ( ;; ) {
n = *next;
if (n == node) {
*next = n->index;
break;
}
next = &n->index;
}
ngx_queue_remove(&node->queue);
weight = 0;
for (q = ngx_queue_head(&node->children);
q != ngx_queue_sentinel(&node->children);
q = ngx_queue_next(q))
{
child = ngx_queue_data(q, ngx_http_v2_node_t, queue);
weight += child->weight;
}
parent = node->parent;
for (q = ngx_queue_head(&node->children);
q != ngx_queue_sentinel(&node->children);
q = ngx_queue_next(q))
{
child = ngx_queue_data(q, ngx_http_v2_node_t, queue);
child->parent = parent;
child->weight = node->weight * child->weight / weight;
if (child->weight == 0) {
child->weight = 1;
}
}
if (parent == NGX_HTTP_V2_ROOT) {
node->rank = 0;
node->rel_weight = 1.0;
children = &h2c->dependencies;
} else {
node->rank = parent->rank;
node->rel_weight = parent->rel_weight;
children = &parent->children;
}
ngx_http_v2_node_children_update(node);
ngx_queue_add(children, &node->children);
ngx_memzero(node, sizeof(ngx_http_v2_node_t));
return node;
}
static ngx_int_t
ngx_http_v2_validate_header(ngx_http_request_t *r, ngx_http_v2_header_t *header)
{
u_char ch;
ngx_uint_t i;
ngx_http_core_srv_conf_t *cscf;
if (header->name.len == 0) {
return NGX_ERROR;
}
r->invalid_header = 0;
cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
for (i = (header->name.data[0] == ':'); i != header->name.len; i++) {
ch = header->name.data[i];
if ((ch >= 'a' && ch <= 'z')
|| (ch == '-')
|| (ch >= '0' && ch <= '9')
|| (ch == '_' && cscf->underscores_in_headers))
{
continue;
}
if (ch == '\0' || ch == LF || ch == CR || ch == ':'
|| (ch >= 'A' && ch <= 'Z'))
{
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client sent invalid header name: \"%V\"",
&header->name);
return NGX_ERROR;
}
r->invalid_header = 1;
}
for (i = 0; i != header->value.len; i++) {
ch = header->value.data[i];
if (ch == '\0' || ch == LF || ch == CR) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client sent header \"%V\" with "
"invalid value: \"%V\"",
&header->name, &header->value);
return NGX_ERROR;
}
}
return NGX_OK;
}
static ngx_int_t
ngx_http_v2_pseudo_header(ngx_http_request_t *r, ngx_http_v2_header_t *header)
{
header->name.len--;
header->name.data++;
switch (header->name.len) {
case 4:
if (ngx_memcmp(header->name.data, "path", sizeof("path") - 1)
== 0)
{
return ngx_http_v2_parse_path(r, &header->value);
}
break;
case 6:
if (ngx_memcmp(header->name.data, "method", sizeof("method") - 1)
== 0)
{
return ngx_http_v2_parse_method(r, &header->value);
}
if (ngx_memcmp(header->name.data, "scheme", sizeof("scheme") - 1)
== 0)
{
return ngx_http_v2_parse_scheme(r, &header->value);
}
break;
case 9:
if (ngx_memcmp(header->name.data, "authority", sizeof("authority") - 1)
== 0)
{
return ngx_http_v2_parse_authority(r, &header->value);
}
break;
}
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client sent unknown pseudo-header \":%V\"",
&header->name);
return NGX_DECLINED;
}
static ngx_int_t
ngx_http_v2_parse_path(ngx_http_request_t *r, ngx_str_t *value)
{
if (r->unparsed_uri.len) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client sent duplicate :path header");
return NGX_DECLINED;
}
if (value->len == 0) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client sent empty :path header");
return NGX_DECLINED;
}
r->uri_start = value->data;
r->uri_end = value->data + value->len;
if (ngx_http_parse_uri(r) != NGX_OK) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client sent invalid :path header: \"%V\"", value);
return NGX_DECLINED;
}
if (ngx_http_process_request_uri(r) != NGX_OK) {
/*
* request has been finalized already
* in ngx_http_process_request_uri()
*/
return NGX_ABORT;
}
return NGX_OK;
}
static ngx_int_t
ngx_http_v2_parse_method(ngx_http_request_t *r, ngx_str_t *value)
{
size_t k, len;
ngx_uint_t n;
const u_char *p, *m;
/*
* This array takes less than 256 sequential bytes,
* and if typical CPU cache line size is 64 bytes,
* it is prefetched for 4 load operations.
*/
static const struct {
u_char len;
const u_char method[11];
uint32_t value;
} tests[] = {
{ 3, "GET", NGX_HTTP_GET },
{ 4, "POST", NGX_HTTP_POST },
{ 4, "HEAD", NGX_HTTP_HEAD },
{ 7, "OPTIONS", NGX_HTTP_OPTIONS },
{ 8, "PROPFIND", NGX_HTTP_PROPFIND },
{ 3, "PUT", NGX_HTTP_PUT },
{ 5, "MKCOL", NGX_HTTP_MKCOL },
{ 6, "DELETE", NGX_HTTP_DELETE },
{ 4, "COPY", NGX_HTTP_COPY },
{ 4, "MOVE", NGX_HTTP_MOVE },
{ 9, "PROPPATCH", NGX_HTTP_PROPPATCH },
{ 4, "LOCK", NGX_HTTP_LOCK },
{ 6, "UNLOCK", NGX_HTTP_UNLOCK },
{ 5, "PATCH", NGX_HTTP_PATCH },
{ 5, "TRACE", NGX_HTTP_TRACE }
}, *test;
if (r->method_name.len) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client sent duplicate :method header");
return NGX_DECLINED;
}
if (value->len == 0) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client sent empty :method header");
return NGX_DECLINED;
}
r->method_name.len = value->len;
r->method_name.data = value->data;
len = r->method_name.len;
n = sizeof(tests) / sizeof(tests[0]);
test = tests;
do {
if (len == test->len) {
p = r->method_name.data;
m = test->method;
k = len;
do {
if (*p++ != *m++) {
goto next;
}
} while (--k);
r->method = test->value;
return NGX_OK;
}
next:
test++;
} while (--n);
p = r->method_name.data;
do {
if ((*p < 'A' || *p > 'Z') && *p != '_' && *p != '-') {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client sent invalid method: \"%V\"",
&r->method_name);
return NGX_DECLINED;
}
p++;
} while (--len);
return NGX_OK;
}
static ngx_int_t
ngx_http_v2_parse_scheme(ngx_http_request_t *r, ngx_str_t *value)
{
if (r->schema_start) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client sent duplicate :scheme header");
return NGX_DECLINED;
}
if (value->len == 0) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client sent empty :scheme header");
return NGX_DECLINED;
}
r->schema_start = value->data;
r->schema_end = value->data + value->len;
return NGX_OK;
}
static ngx_int_t
ngx_http_v2_parse_authority(ngx_http_request_t *r, ngx_str_t *value)
{
return ngx_http_v2_parse_header(r, &ngx_http_v2_parse_headers[0], value);
}
static ngx_int_t
ngx_http_v2_parse_header(ngx_http_request_t *r,
ngx_http_v2_parse_header_t *header, ngx_str_t *value)
{
ngx_table_elt_t *h;
ngx_http_core_main_conf_t *cmcf;
h = ngx_list_push(&r->headers_in.headers);
if (h == NULL) {
return NGX_ERROR;
}
h->key.len = header->name.len;
h->key.data = header->name.data;
h->lowcase_key = header->name.data;
if (header->hh == NULL) {
header->hash = ngx_hash_key(header->name.data, header->name.len);
cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
header->hh = ngx_hash_find(&cmcf->headers_in_hash, header->hash,
h->lowcase_key, h->key.len);
if (header->hh == NULL) {
return NGX_ERROR;
}
}
h->hash = header->hash;
h->value.len = value->len;
h->value.data = value->data;
if (header->hh->handler(r, h, header->hh->offset) != NGX_OK) {
/* header handler has already finalized request */
return NGX_ABORT;
}
return NGX_OK;
}
static ngx_int_t
ngx_http_v2_construct_request_line(ngx_http_request_t *r)
{
u_char *p;
static const u_char ending[] = " HTTP/2.0";
if (r->method_name.len == 0
|| r->schema_start == NULL
|| r->unparsed_uri.len == 0)
{
if (r->method_name.len == 0) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client sent no :method header");
} else if (r->schema_start == NULL) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client sent no :scheme header");
} else {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client sent no :path header");
}
ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
return NGX_ERROR;
}
r->request_line.len = r->method_name.len + 1
+ r->unparsed_uri.len
+ sizeof(ending) - 1;
p = ngx_pnalloc(r->pool, r->request_line.len + 1);
if (p == NULL) {
ngx_http_v2_close_stream(r->stream, NGX_HTTP_INTERNAL_SERVER_ERROR);
return NGX_ERROR;
}
r->request_line.data = p;
p = ngx_cpymem(p, r->method_name.data, r->method_name.len);
*p++ = ' ';
p = ngx_cpymem(p, r->unparsed_uri.data, r->unparsed_uri.len);
ngx_memcpy(p, ending, sizeof(ending));
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http2 request line: \"%V\"", &r->request_line);
return NGX_OK;
}
static ngx_int_t
ngx_http_v2_cookie(ngx_http_request_t *r, ngx_http_v2_header_t *header)
{
ngx_str_t *val;
ngx_array_t *cookies;
cookies = r->stream->cookies;
if (cookies == NULL) {
cookies = ngx_array_create(r->pool, 2, sizeof(ngx_str_t));
if (cookies == NULL) {
return NGX_ERROR;
}
r->stream->cookies = cookies;
}
val = ngx_array_push(cookies);
if (val == NULL) {
return NGX_ERROR;
}
val->len = header->value.len;
val->data = header->value.data;
return NGX_OK;
}
static ngx_int_t
ngx_http_v2_construct_cookie_header(ngx_http_request_t *r)
{
u_char *buf, *p, *end;
size_t len;
ngx_str_t *vals;
ngx_uint_t i;
ngx_array_t *cookies;
ngx_table_elt_t *h;
ngx_http_header_t *hh;
ngx_http_core_main_conf_t *cmcf;
static ngx_str_t cookie = ngx_string("cookie");
cookies = r->stream->cookies;
if (cookies == NULL) {
return NGX_OK;
}
vals = cookies->elts;
i = 0;
len = 0;
do {
len += vals[i].len + 2;
} while (++i != cookies->nelts);
len -= 2;
buf = ngx_pnalloc(r->pool, len + 1);
if (buf == NULL) {
ngx_http_v2_close_stream(r->stream, NGX_HTTP_INTERNAL_SERVER_ERROR);
return NGX_ERROR;
}
p = buf;
end = buf + len;
for (i = 0; /* void */ ; i++) {
p = ngx_cpymem(p, vals[i].data, vals[i].len);
if (p == end) {
*p = '\0';
break;
}
*p++ = ';'; *p++ = ' ';
}
h = ngx_list_push(&r->headers_in.headers);
if (h == NULL) {
ngx_http_v2_close_stream(r->stream, NGX_HTTP_INTERNAL_SERVER_ERROR);
return NGX_ERROR;
}
h->hash = ngx_hash(ngx_hash(ngx_hash(ngx_hash(
ngx_hash('c', 'o'), 'o'), 'k'), 'i'), 'e');
h->key.len = cookie.len;
h->key.data = cookie.data;
h->value.len = len;
h->value.data = buf;
h->lowcase_key = cookie.data;
cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
hh = ngx_hash_find(&cmcf->headers_in_hash, h->hash,
h->lowcase_key, h->key.len);
if (hh == NULL) {
ngx_http_v2_close_stream(r->stream, NGX_HTTP_INTERNAL_SERVER_ERROR);
return NGX_ERROR;
}
if (hh->handler(r, h, hh->offset) != NGX_OK) {
/*
* request has been finalized already
* in ngx_http_process_multi_header_lines()
*/
return NGX_ERROR;
}
return NGX_OK;
}
static void
ngx_http_v2_run_request(ngx_http_request_t *r)
{
if (ngx_http_v2_construct_request_line(r) != NGX_OK) {
return;
}
if (ngx_http_v2_construct_cookie_header(r) != NGX_OK) {
return;
}
r->http_state = NGX_HTTP_PROCESS_REQUEST_STATE;
if (ngx_http_process_request_header(r) != NGX_OK) {
return;
}
if (r->headers_in.content_length_n > 0 && r->stream->in_closed) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client prematurely closed stream");
r->stream->skip_data = 1;
ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
return;
}
if (r->headers_in.content_length_n == -1 && !r->stream->in_closed) {
r->headers_in.chunked = 1;
}
ngx_http_process_request(r);
}
static void
ngx_http_v2_run_request_handler(ngx_event_t *ev)
{
ngx_connection_t *fc;
ngx_http_request_t *r;
fc = ev->data;
r = fc->data;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,
"http2 run request handler");
ngx_http_v2_run_request(r);
}
ngx_int_t
ngx_http_v2_read_request_body(ngx_http_request_t *r)
{
off_t len;
size_t size;
ngx_buf_t *buf;
ngx_int_t rc;
ngx_http_v2_stream_t *stream;
ngx_http_v2_srv_conf_t *h2scf;
ngx_http_request_body_t *rb;
ngx_http_core_loc_conf_t *clcf;
ngx_http_v2_connection_t *h2c;
stream = r->stream;
rb = r->request_body;
if (stream->skip_data) {
r->request_body_no_buffering = 0;
rb->post_handler(r);
return NGX_OK;
}
h2scf = ngx_http_get_module_srv_conf(r, ngx_http_v2_module);
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
len = r->headers_in.content_length_n;
if (r->request_body_no_buffering && !stream->in_closed) {
if (len < 0 || len > (off_t) clcf->client_body_buffer_size) {
len = clcf->client_body_buffer_size;
}
/*
* We need a room to store data up to the stream's initial window size,
* at least until this window will be exhausted.
*/
if (len < (off_t) h2scf->preread_size) {
len = h2scf->preread_size;
}
if (len > NGX_HTTP_V2_MAX_WINDOW) {
len = NGX_HTTP_V2_MAX_WINDOW;
}
rb->buf = ngx_create_temp_buf(r->pool, (size_t) len);
} else if (len >= 0 && len <= (off_t) clcf->client_body_buffer_size
&& !r->request_body_in_file_only)
{
rb->buf = ngx_create_temp_buf(r->pool, (size_t) len);
} else {
rb->buf = ngx_calloc_buf(r->pool);
if (rb->buf != NULL) {
rb->buf->sync = 1;
}
}
if (rb->buf == NULL) {
stream->skip_data = 1;
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
rb->rest = 1;
buf = stream->preread;
if (stream->in_closed) {
r->request_body_no_buffering = 0;
if (buf) {
rc = ngx_http_v2_process_request_body(r, buf->pos,
buf->last - buf->pos, 1);
ngx_pfree(r->pool, buf->start);
return rc;
}
return ngx_http_v2_process_request_body(r, NULL, 0, 1);
}
if (buf) {
rc = ngx_http_v2_process_request_body(r, buf->pos,
buf->last - buf->pos, 0);
ngx_pfree(r->pool, buf->start);
if (rc != NGX_OK) {
stream->skip_data = 1;
return rc;
}
}
if (r->request_body_no_buffering) {
size = (size_t) len - h2scf->preread_size;
} else {
stream->no_flow_control = 1;
size = NGX_HTTP_V2_MAX_WINDOW - stream->recv_window;
}
if (size) {
if (ngx_http_v2_send_window_update(stream->connection,
stream->node->id, size)
== NGX_ERROR)
{
stream->skip_data = 1;
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
h2c = stream->connection;
if (!h2c->blocked) {
if (ngx_http_v2_send_output_queue(h2c) == NGX_ERROR) {
stream->skip_data = 1;
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
}
stream->recv_window += size;
}
if (!buf) {
ngx_add_timer(r->connection->read, clcf->client_body_timeout);
}
r->read_event_handler = ngx_http_v2_read_client_request_body_handler;
r->write_event_handler = ngx_http_request_empty_handler;
return NGX_AGAIN;
}
static ngx_int_t
ngx_http_v2_process_request_body(ngx_http_request_t *r, u_char *pos,
size_t size, ngx_uint_t last)
{
ngx_buf_t *buf;
ngx_int_t rc;
ngx_connection_t *fc;
ngx_http_request_body_t *rb;
ngx_http_core_loc_conf_t *clcf;
fc = r->connection;
rb = r->request_body;
buf = rb->buf;
if (size) {
if (buf->sync) {
buf->pos = buf->start = pos;
buf->last = buf->end = pos + size;
r->request_body_in_file_only = 1;
} else {
if (size > (size_t) (buf->end - buf->last)) {
ngx_log_error(NGX_LOG_INFO, fc->log, 0,
"client intended to send body data "
"larger than declared");
return NGX_HTTP_BAD_REQUEST;
}
buf->last = ngx_cpymem(buf->last, pos, size);
}
}
if (last) {
rb->rest = 0;
if (fc->read->timer_set) {
ngx_del_timer(fc->read);
}
if (r->request_body_no_buffering) {
ngx_post_event(fc->read, &ngx_posted_events);
return NGX_OK;
}
rc = ngx_http_v2_filter_request_body(r);
if (rc != NGX_OK) {
return rc;
}
if (buf->sync) {
/* prevent reusing this buffer in the upstream module */
rb->buf = NULL;
}
if (r->headers_in.chunked) {
r->headers_in.content_length_n = rb->received;
}
r->read_event_handler = ngx_http_block_reading;
rb->post_handler(r);
return NGX_OK;
}
if (size == 0) {
return NGX_OK;
}
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
ngx_add_timer(fc->read, clcf->client_body_timeout);
if (r->request_body_no_buffering) {
ngx_post_event(fc->read, &ngx_posted_events);
return NGX_OK;
}
if (buf->sync) {
return ngx_http_v2_filter_request_body(r);
}
return NGX_OK;
}
static ngx_int_t
ngx_http_v2_filter_request_body(ngx_http_request_t *r)
{
ngx_buf_t *b, *buf;
ngx_int_t rc;
ngx_chain_t *cl;
ngx_http_request_body_t *rb;
ngx_http_core_loc_conf_t *clcf;
rb = r->request_body;
buf = rb->buf;
if (buf->pos == buf->last && rb->rest) {
cl = NULL;
goto update;
}
cl = ngx_chain_get_free_buf(r->pool, &rb->free);
if (cl == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
b = cl->buf;
ngx_memzero(b, sizeof(ngx_buf_t));
if (buf->pos != buf->last) {
r->request_length += buf->last - buf->pos;
rb->received += buf->last - buf->pos;
if (r->headers_in.content_length_n != -1) {
if (rb->received > r->headers_in.content_length_n) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client intended to send body data "
"larger than declared");
return NGX_HTTP_BAD_REQUEST;
}
} else {
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
if (clcf->client_max_body_size
&& rb->received > clcf->client_max_body_size)
{
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"client intended to send too large chunked body: "
"%O bytes", rb->received);
return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE;
}
}
b->temporary = 1;
b->pos = buf->pos;
b->last = buf->last;
b->start = b->pos;
b->end = b->last;
buf->pos = buf->last;
}
if (!rb->rest) {
if (r->headers_in.content_length_n != -1
&& r->headers_in.content_length_n != rb->received)
{
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client prematurely closed stream: "
"only %O out of %O bytes of request body received",
rb->received, r->headers_in.content_length_n);
return NGX_HTTP_BAD_REQUEST;
}
b->last_buf = 1;
}
b->tag = (ngx_buf_tag_t) &ngx_http_v2_filter_request_body;
b->flush = r->request_body_no_buffering;
update:
rc = ngx_http_top_request_body_filter(r, cl);
ngx_chain_update_chains(r->pool, &rb->free, &rb->busy, &cl,
(ngx_buf_tag_t) &ngx_http_v2_filter_request_body);
return rc;
}
static void
ngx_http_v2_read_client_request_body_handler(ngx_http_request_t *r)
{
ngx_connection_t *fc;
fc = r->connection;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,
"http2 read client request body handler");
if (fc->read->timedout) {
ngx_log_error(NGX_LOG_INFO, fc->log, NGX_ETIMEDOUT, "client timed out");
fc->timedout = 1;
r->stream->skip_data = 1;
ngx_http_finalize_request(r, NGX_HTTP_REQUEST_TIME_OUT);
return;
}
if (fc->error) {
ngx_log_error(NGX_LOG_INFO, fc->log, 0,
"client prematurely closed stream");
r->stream->skip_data = 1;
ngx_http_finalize_request(r, NGX_HTTP_CLIENT_CLOSED_REQUEST);
return;
}
}
ngx_int_t
ngx_http_v2_read_unbuffered_request_body(ngx_http_request_t *r)
{
size_t window;
ngx_buf_t *buf;
ngx_int_t rc;
ngx_connection_t *fc;
ngx_http_v2_stream_t *stream;
ngx_http_v2_connection_t *h2c;
ngx_http_core_loc_conf_t *clcf;
stream = r->stream;
fc = r->connection;
if (fc->read->timedout) {
if (stream->recv_window) {
stream->skip_data = 1;
fc->timedout = 1;
return NGX_HTTP_REQUEST_TIME_OUT;
}
fc->read->timedout = 0;
}
if (fc->error) {
stream->skip_data = 1;
return NGX_HTTP_BAD_REQUEST;
}
rc = ngx_http_v2_filter_request_body(r);
if (rc != NGX_OK) {
stream->skip_data = 1;
return rc;
}
if (!r->request_body->rest) {
return NGX_OK;
}
if (r->request_body->busy != NULL) {
return NGX_AGAIN;
}
buf = r->request_body->buf;
buf->pos = buf->start;
buf->last = buf->start;
window = buf->end - buf->start;
h2c = stream->connection;
if (h2c->state.stream == stream) {
window -= h2c->state.length;
}
if (window <= stream->recv_window) {
if (window < stream->recv_window) {
ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
"http2 negative window update");
stream->skip_data = 1;
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
return NGX_AGAIN;
}
if (ngx_http_v2_send_window_update(h2c, stream->node->id,
window - stream->recv_window)
== NGX_ERROR)
{
stream->skip_data = 1;
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
if (ngx_http_v2_send_output_queue(h2c) == NGX_ERROR) {
stream->skip_data = 1;
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
if (stream->recv_window == 0) {
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
ngx_add_timer(fc->read, clcf->client_body_timeout);
}
stream->recv_window = window;
return NGX_AGAIN;
}
static ngx_int_t
ngx_http_v2_terminate_stream(ngx_http_v2_connection_t *h2c,
ngx_http_v2_stream_t *stream, ngx_uint_t status)
{
ngx_event_t *rev;
ngx_connection_t *fc;
if (stream->rst_sent) {
return NGX_OK;
}
if (ngx_http_v2_send_rst_stream(h2c, stream->node->id, status)
== NGX_ERROR)
{
return NGX_ERROR;
}
stream->rst_sent = 1;
stream->skip_data = 1;
fc = stream->request->connection;
fc->error = 1;
rev = fc->read;
rev->handler(rev);
return NGX_OK;
}
void
ngx_http_v2_close_stream(ngx_http_v2_stream_t *stream, ngx_int_t rc)
{
ngx_pool_t *pool;
ngx_uint_t push;
ngx_event_t *ev;
ngx_connection_t *fc;
ngx_http_v2_node_t *node;
ngx_http_v2_connection_t *h2c;
h2c = stream->connection;
node = stream->node;
ngx_log_debug4(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
"http2 close stream %ui, queued %ui, "
"processing %ui, pushing %ui",
node->id, stream->queued, h2c->processing, h2c->pushing);
fc = stream->request->connection;
if (stream->queued) {
fc->write->handler = ngx_http_v2_close_stream_handler;
fc->read->handler = ngx_http_empty_handler;
return;
}
if (!stream->rst_sent && !h2c->connection->error) {
if (!stream->out_closed) {
if (ngx_http_v2_send_rst_stream(h2c, node->id,
fc->timedout ? NGX_HTTP_V2_PROTOCOL_ERROR
: NGX_HTTP_V2_INTERNAL_ERROR)
!= NGX_OK)
{
h2c->connection->error = 1;
}
} else if (!stream->in_closed) {
#if 0
if (ngx_http_v2_send_rst_stream(h2c, node->id, NGX_HTTP_V2_NO_ERROR)
!= NGX_OK)
{
h2c->connection->error = 1;
}
#else
/*
* At the time of writing at least the latest versions of Chrome
* do not properly handle RST_STREAM with NO_ERROR status.
*
* See: https://bugs.chromium.org/p/chromium/issues/detail?id=603182
*
* As a workaround, the stream window is maximized before closing
* the stream. This allows a client to send up to 2 GB of data
* before getting blocked on flow control.
*/
if (stream->recv_window < NGX_HTTP_V2_MAX_WINDOW
&& ngx_http_v2_send_window_update(h2c, node->id,
NGX_HTTP_V2_MAX_WINDOW
- stream->recv_window)
!= NGX_OK)
{
h2c->connection->error = 1;
}
#endif
}
}
if (h2c->state.stream == stream) {
h2c->state.stream = NULL;
}
push = stream->node->id % 2 == 0;
node->stream = NULL;
ngx_queue_insert_tail(&h2c->closed, &node->reuse);
h2c->closed_nodes++;
/*
* This pool keeps decoded request headers which can be used by log phase
* handlers in ngx_http_free_request().
*
* The pointer is stored into local variable because the stream object
* will be destroyed after a call to ngx_http_free_request().
*/
pool = stream->pool;
ngx_http_free_request(stream->request, rc);
if (pool != h2c->state.pool) {
ngx_destroy_pool(pool);
} else {
/* pool will be destroyed when the complete header is parsed */
h2c->state.keep_pool = 0;
}
ev = fc->read;
if (ev->timer_set) {
ngx_del_timer(ev);
}
if (ev->posted) {
ngx_delete_posted_event(ev);
}
ev = fc->write;
if (ev->timer_set) {
ngx_del_timer(ev);
}
if (ev->posted) {
ngx_delete_posted_event(ev);
}
fc->data = h2c->free_fake_connections;
h2c->free_fake_connections = fc;
if (push) {
h2c->pushing--;
} else {
h2c->processing--;
}
if (h2c->processing || h2c->pushing || h2c->blocked) {
return;
}
ev = h2c->connection->read;
ev->handler = ngx_http_v2_handle_connection_handler;
ngx_post_event(ev, &ngx_posted_events);
}
static void
ngx_http_v2_close_stream_handler(ngx_event_t *ev)
{
ngx_connection_t *fc;
ngx_http_request_t *r;
fc = ev->data;
r = fc->data;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,
"http2 close stream handler");
if (ev->timedout) {
ngx_log_error(NGX_LOG_INFO, fc->log, NGX_ETIMEDOUT, "client timed out");
fc->timedout = 1;
ngx_http_v2_close_stream(r->stream, NGX_HTTP_REQUEST_TIME_OUT);
return;
}
ngx_http_v2_close_stream(r->stream, 0);
}
static void
ngx_http_v2_handle_connection_handler(ngx_event_t *rev)
{
ngx_connection_t *c;
ngx_http_v2_connection_t *h2c;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0,
"http2 handle connection handler");
c = rev->data;
h2c = c->data;
if (c->error) {
ngx_http_v2_finalize_connection(h2c, 0);
return;
}
rev->handler = ngx_http_v2_read_handler;
if (rev->ready) {
ngx_http_v2_read_handler(rev);
return;
}
if (h2c->last_out && ngx_http_v2_send_output_queue(h2c) == NGX_ERROR) {
ngx_http_v2_finalize_connection(h2c, 0);
return;
}
ngx_http_v2_handle_connection(c->data);
}
static void
ngx_http_v2_idle_handler(ngx_event_t *rev)
{
ngx_connection_t *c;
ngx_http_v2_srv_conf_t *h2scf;
ngx_http_v2_connection_t *h2c;
c = rev->data;
h2c = c->data;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http2 idle handler");
if (rev->timedout || c->close) {
ngx_http_v2_finalize_connection(h2c, NGX_HTTP_V2_NO_ERROR);
return;
}
#if (NGX_HAVE_KQUEUE)
if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {
if (rev->pending_eof) {
c->log->handler = NULL;
ngx_log_error(NGX_LOG_INFO, c->log, rev->kq_errno,
"kevent() reported that client %V closed "
"idle connection", &c->addr_text);
#if (NGX_HTTP_SSL)
if (c->ssl) {
c->ssl->no_send_shutdown = 1;
}
#endif
ngx_http_close_connection(c);
return;
}
}
#endif
c->destroyed = 0;
ngx_reusable_connection(c, 0);
h2scf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx,
ngx_http_v2_module);
h2c->pool = ngx_create_pool(h2scf->pool_size, h2c->connection->log);
if (h2c->pool == NULL) {
ngx_http_v2_finalize_connection(h2c, NGX_HTTP_V2_INTERNAL_ERROR);
return;
}
c->write->handler = ngx_http_v2_write_handler;
rev->handler = ngx_http_v2_read_handler;
ngx_http_v2_read_handler(rev);
}
static void
ngx_http_v2_finalize_connection(ngx_http_v2_connection_t *h2c,
ngx_uint_t status)
{
ngx_uint_t i, size;
ngx_event_t *ev;
ngx_connection_t *c, *fc;
ngx_http_request_t *r;
ngx_http_v2_node_t *node;
ngx_http_v2_stream_t *stream;
ngx_http_v2_srv_conf_t *h2scf;
c = h2c->connection;
h2c->blocked = 1;
if (!c->error && !h2c->goaway) {
if (ngx_http_v2_send_goaway(h2c, status) != NGX_ERROR) {
(void) ngx_http_v2_send_output_queue(h2c);
}
}
c->error = 1;
if (!h2c->processing && !h2c->pushing) {
ngx_http_close_connection(c);
return;
}
c->read->handler = ngx_http_empty_handler;
c->write->handler = ngx_http_empty_handler;
h2c->last_out = NULL;
h2scf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx,
ngx_http_v2_module);
size = ngx_http_v2_index_size(h2scf);
for (i = 0; i < size; i++) {
for (node = h2c->streams_index[i]; node; node = node->index) {
stream = node->stream;
if (stream == NULL) {
continue;
}
stream->waiting = 0;
r = stream->request;
fc = r->connection;
fc->error = 1;
if (stream->queued) {
stream->queued = 0;
ev = fc->write;
ev->active = 0;
ev->ready = 1;
} else {
ev = fc->read;
}
ev->eof = 1;
ev->handler(ev);
}
}
h2c->blocked = 0;
if (h2c->processing || h2c->pushing) {
return;
}
ngx_http_close_connection(c);
}
static ngx_int_t
ngx_http_v2_adjust_windows(ngx_http_v2_connection_t *h2c, ssize_t delta)
{
ngx_uint_t i, size;
ngx_event_t *wev;
ngx_http_v2_node_t *node;
ngx_http_v2_stream_t *stream;
ngx_http_v2_srv_conf_t *h2scf;
h2scf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx,
ngx_http_v2_module);
size = ngx_http_v2_index_size(h2scf);
for (i = 0; i < size; i++) {
for (node = h2c->streams_index[i]; node; node = node->index) {
stream = node->stream;
if (stream == NULL) {
continue;
}
if (delta > 0
&& stream->send_window
> (ssize_t) (NGX_HTTP_V2_MAX_WINDOW - delta))
{
if (ngx_http_v2_terminate_stream(h2c, stream,
NGX_HTTP_V2_FLOW_CTRL_ERROR)
== NGX_ERROR)
{
return NGX_ERROR;
}
continue;
}
stream->send_window += delta;
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
"http2:%ui adjusted window: %z",
node->id, stream->send_window);
if (stream->send_window > 0 && stream->exhausted) {
stream->exhausted = 0;
wev = stream->request->connection->write;
wev->active = 0;
wev->ready = 1;
if (!wev->delayed) {
wev->handler(wev);
}
}
}
}
return NGX_OK;
}
static void
ngx_http_v2_set_dependency(ngx_http_v2_connection_t *h2c,
ngx_http_v2_node_t *node, ngx_uint_t depend, ngx_uint_t exclusive)
{
ngx_queue_t *children, *q;
ngx_http_v2_node_t *parent, *child, *next;
parent = depend ? ngx_http_v2_get_node_by_id(h2c, depend, 0) : NULL;
if (parent == NULL) {
parent = NGX_HTTP_V2_ROOT;
if (depend != 0) {
exclusive = 0;
}
node->rank = 1;
node->rel_weight = (1.0 / 256) * node->weight;
children = &h2c->dependencies;
} else {
if (node->parent != NULL) {
for (next = parent->parent;
next != NGX_HTTP_V2_ROOT && next->rank >= node->rank;
next = next->parent)
{
if (next != node) {
continue;
}
ngx_queue_remove(&parent->queue);
ngx_queue_insert_after(&node->queue, &parent->queue);
parent->parent = node->parent;
if (node->parent == NGX_HTTP_V2_ROOT) {
parent->rank = 1;
parent->rel_weight = (1.0 / 256) * parent->weight;
} else {
parent->rank = node->parent->rank + 1;
parent->rel_weight = (node->parent->rel_weight / 256)
* parent->weight;
}
if (!exclusive) {
ngx_http_v2_node_children_update(parent);
}
break;
}
}
node->rank = parent->rank + 1;
node->rel_weight = (parent->rel_weight / 256) * node->weight;
if (parent->stream == NULL) {
ngx_queue_remove(&parent->reuse);
ngx_queue_insert_tail(&h2c->closed, &parent->reuse);
}
children = &parent->children;
}
if (exclusive) {
for (q = ngx_queue_head(children);
q != ngx_queue_sentinel(children);
q = ngx_queue_next(q))
{
child = ngx_queue_data(q, ngx_http_v2_node_t, queue);
child->parent = node;
}
ngx_queue_add(&node->children, children);
ngx_queue_init(children);
}
if (node->parent != NULL) {
ngx_queue_remove(&node->queue);
}
ngx_queue_insert_tail(children, &node->queue);
node->parent = parent;
ngx_http_v2_node_children_update(node);
}
static void
ngx_http_v2_node_children_update(ngx_http_v2_node_t *node)
{
ngx_queue_t *q;
ngx_http_v2_node_t *child;
for (q = ngx_queue_head(&node->children);
q != ngx_queue_sentinel(&node->children);
q = ngx_queue_next(q))
{
child = ngx_queue_data(q, ngx_http_v2_node_t, queue);
child->rank = node->rank + 1;
child->rel_weight = (node->rel_weight / 256) * child->weight;
ngx_http_v2_node_children_update(child);
}
}
static void
ngx_http_v2_pool_cleanup(void *data)
{
ngx_http_v2_connection_t *h2c = data;
if (h2c->state.pool) {
ngx_destroy_pool(h2c->state.pool);
}
if (h2c->pool) {
ngx_destroy_pool(h2c->pool);
}
}
nginx-1.14.0/src/http/v2/ngx_http_v2.h 000644 001751 001751 00000031717 13265410474 020635 0 ustar 00mdounin mdounin 000000 000000 /*
* Copyright (C) Nginx, Inc.
* Copyright (C) Valentin V. Bartenev
*/
#ifndef _NGX_HTTP_V2_H_INCLUDED_
#define _NGX_HTTP_V2_H_INCLUDED_
#include
#include
#include
#define NGX_HTTP_V2_ALPN_ADVERTISE "\x02h2"
#define NGX_HTTP_V2_NPN_ADVERTISE NGX_HTTP_V2_ALPN_ADVERTISE
#define NGX_HTTP_V2_STATE_BUFFER_SIZE 16
#define NGX_HTTP_V2_DEFAULT_FRAME_SIZE (1 << 14)
#define NGX_HTTP_V2_MAX_FRAME_SIZE ((1 << 24) - 1)
#define NGX_HTTP_V2_INT_OCTETS 4
#define NGX_HTTP_V2_MAX_FIELD \
(127 + (1 << (NGX_HTTP_V2_INT_OCTETS - 1) * 7) - 1)
#define NGX_HTTP_V2_STREAM_ID_SIZE 4
#define NGX_HTTP_V2_FRAME_HEADER_SIZE 9
/* frame types */
#define NGX_HTTP_V2_DATA_FRAME 0x0
#define NGX_HTTP_V2_HEADERS_FRAME 0x1
#define NGX_HTTP_V2_PRIORITY_FRAME 0x2
#define NGX_HTTP_V2_RST_STREAM_FRAME 0x3
#define NGX_HTTP_V2_SETTINGS_FRAME 0x4
#define NGX_HTTP_V2_PUSH_PROMISE_FRAME 0x5
#define NGX_HTTP_V2_PING_FRAME 0x6
#define NGX_HTTP_V2_GOAWAY_FRAME 0x7
#define NGX_HTTP_V2_WINDOW_UPDATE_FRAME 0x8
#define NGX_HTTP_V2_CONTINUATION_FRAME 0x9
/* frame flags */
#define NGX_HTTP_V2_NO_FLAG 0x00
#define NGX_HTTP_V2_ACK_FLAG 0x01
#define NGX_HTTP_V2_END_STREAM_FLAG 0x01
#define NGX_HTTP_V2_END_HEADERS_FLAG 0x04
#define NGX_HTTP_V2_PADDED_FLAG 0x08
#define NGX_HTTP_V2_PRIORITY_FLAG 0x20
#define NGX_HTTP_V2_MAX_WINDOW ((1U << 31) - 1)
#define NGX_HTTP_V2_DEFAULT_WINDOW 65535
#define NGX_HTTP_V2_DEFAULT_WEIGHT 16
typedef struct ngx_http_v2_connection_s ngx_http_v2_connection_t;
typedef struct ngx_http_v2_node_s ngx_http_v2_node_t;
typedef struct ngx_http_v2_out_frame_s ngx_http_v2_out_frame_t;
typedef u_char *(*ngx_http_v2_handler_pt) (ngx_http_v2_connection_t *h2c,
u_char *pos, u_char *end);
typedef struct {
ngx_str_t name;
ngx_str_t value;
} ngx_http_v2_header_t;
typedef struct {
ngx_uint_t sid;
size_t length;
size_t padding;
unsigned flags:8;
unsigned incomplete:1;
unsigned keep_pool:1;
/* HPACK */
unsigned parse_name:1;
unsigned parse_value:1;
unsigned index:1;
ngx_http_v2_header_t header;
size_t header_limit;
u_char field_state;
u_char *field_start;
u_char *field_end;
size_t field_rest;
ngx_pool_t *pool;
ngx_http_v2_stream_t *stream;
u_char buffer[NGX_HTTP_V2_STATE_BUFFER_SIZE];
size_t buffer_used;
ngx_http_v2_handler_pt handler;
} ngx_http_v2_state_t;
typedef struct {
ngx_http_v2_header_t **entries;
ngx_uint_t added;
ngx_uint_t deleted;
ngx_uint_t reused;
ngx_uint_t allocated;
size_t size;
size_t free;
u_char *storage;
u_char *pos;
} ngx_http_v2_hpack_t;
struct ngx_http_v2_connection_s {
ngx_connection_t *connection;
ngx_http_connection_t *http_connection;
ngx_uint_t processing;
ngx_uint_t pushing;
ngx_uint_t concurrent_pushes;
size_t send_window;
size_t recv_window;
size_t init_window;
size_t frame_size;
ngx_queue_t waiting;
ngx_http_v2_state_t state;
ngx_http_v2_hpack_t hpack;
ngx_pool_t *pool;
ngx_http_v2_out_frame_t *free_frames;
ngx_connection_t *free_fake_connections;
ngx_http_v2_node_t **streams_index;
ngx_http_v2_out_frame_t *last_out;
ngx_queue_t dependencies;
ngx_queue_t closed;
ngx_uint_t last_sid;
ngx_uint_t last_push;
unsigned closed_nodes:8;
unsigned settings_ack:1;
unsigned table_update:1;
unsigned blocked:1;
unsigned goaway:1;
unsigned push_disabled:1;
};
struct ngx_http_v2_node_s {
ngx_uint_t id;
ngx_http_v2_node_t *index;
ngx_http_v2_node_t *parent;
ngx_queue_t queue;
ngx_queue_t children;
ngx_queue_t reuse;
ngx_uint_t rank;
ngx_uint_t weight;
double rel_weight;
ngx_http_v2_stream_t *stream;
};
struct ngx_http_v2_stream_s {
ngx_http_request_t *request;
ngx_http_v2_connection_t *connection;
ngx_http_v2_node_t *node;
ngx_uint_t queued;
/*
* A change to SETTINGS_INITIAL_WINDOW_SIZE could cause the
* send_window to become negative, hence it's signed.
*/
ssize_t send_window;
size_t recv_window;
ngx_buf_t *preread;
ngx_http_v2_out_frame_t *free_frames;
ngx_chain_t *free_frame_headers;
ngx_chain_t *free_bufs;
ngx_queue_t queue;
ngx_array_t *cookies;
ngx_pool_t *pool;
unsigned waiting:1;
unsigned blocked:1;
unsigned exhausted:1;
unsigned in_closed:1;
unsigned out_closed:1;
unsigned rst_sent:1;
unsigned no_flow_control:1;
unsigned skip_data:1;
};
struct ngx_http_v2_out_frame_s {
ngx_http_v2_out_frame_t *next;
ngx_chain_t *first;
ngx_chain_t *last;
ngx_int_t (*handler)(ngx_http_v2_connection_t *h2c,
ngx_http_v2_out_frame_t *frame);
ngx_http_v2_stream_t *stream;
size_t length;
unsigned blocked:1;
unsigned fin:1;
};
static ngx_inline void
ngx_http_v2_queue_frame(ngx_http_v2_connection_t *h2c,
ngx_http_v2_out_frame_t *frame)
{
ngx_http_v2_out_frame_t **out;
for (out = &h2c->last_out; *out; out = &(*out)->next) {
if ((*out)->blocked || (*out)->stream == NULL) {
break;
}
if ((*out)->stream->node->rank < frame->stream->node->rank
|| ((*out)->stream->node->rank == frame->stream->node->rank
&& (*out)->stream->node->rel_weight
>= frame->stream->node->rel_weight))
{
break;
}
}
frame->next = *out;
*out = frame;
}
static ngx_inline void
ngx_http_v2_queue_blocked_frame(ngx_http_v2_connection_t *h2c,
ngx_http_v2_out_frame_t *frame)
{
ngx_http_v2_out_frame_t **out;
for (out = &h2c->last_out; *out; out = &(*out)->next) {
if ((*out)->blocked || (*out)->stream == NULL) {
break;
}
}
frame->next = *out;
*out = frame;
}
static ngx_inline void
ngx_http_v2_queue_ordered_frame(ngx_http_v2_connection_t *h2c,
ngx_http_v2_out_frame_t *frame)
{
frame->next = h2c->last_out;
h2c->last_out = frame;
}
void ngx_http_v2_init(ngx_event_t *rev);
ngx_int_t ngx_http_v2_read_request_body(ngx_http_request_t *r);
ngx_int_t ngx_http_v2_read_unbuffered_request_body(ngx_http_request_t *r);
ngx_http_v2_stream_t *ngx_http_v2_push_stream(ngx_http_v2_stream_t *parent,
ngx_str_t *path);
void ngx_http_v2_close_stream(ngx_http_v2_stream_t *stream, ngx_int_t rc);
ngx_int_t ngx_http_v2_send_output_queue(ngx_http_v2_connection_t *h2c);
ngx_str_t *ngx_http_v2_get_static_name(ngx_uint_t index);
ngx_str_t *ngx_http_v2_get_static_value(ngx_uint_t index);
ngx_int_t ngx_http_v2_get_indexed_header(ngx_http_v2_connection_t *h2c,
ngx_uint_t index, ngx_uint_t name_only);
ngx_int_t ngx_http_v2_add_header(ngx_http_v2_connection_t *h2c,
ngx_http_v2_header_t *header);
ngx_int_t ngx_http_v2_table_size(ngx_http_v2_connection_t *h2c, size_t size);
ngx_int_t ngx_http_v2_huff_decode(u_char *state, u_char *src, size_t len,
u_char **dst, ngx_uint_t last, ngx_log_t *log);
size_t ngx_http_v2_huff_encode(u_char *src, size_t len, u_char *dst,
ngx_uint_t lower);
#define ngx_http_v2_prefix(bits) ((1 << (bits)) - 1)
#if (NGX_HAVE_NONALIGNED)
#define ngx_http_v2_parse_uint16(p) ntohs(*(uint16_t *) (p))
#define ngx_http_v2_parse_uint32(p) ntohl(*(uint32_t *) (p))
#else
#define ngx_http_v2_parse_uint16(p) ((p)[0] << 8 | (p)[1])
#define ngx_http_v2_parse_uint32(p) \
((uint32_t) (p)[0] << 24 | (p)[1] << 16 | (p)[2] << 8 | (p)[3])
#endif
#define ngx_http_v2_parse_length(p) ((p) >> 8)
#define ngx_http_v2_parse_type(p) ((p) & 0xff)
#define ngx_http_v2_parse_sid(p) (ngx_http_v2_parse_uint32(p) & 0x7fffffff)
#define ngx_http_v2_parse_window(p) (ngx_http_v2_parse_uint32(p) & 0x7fffffff)
#define ngx_http_v2_write_uint16_aligned(p, s) \
(*(uint16_t *) (p) = htons((uint16_t) (s)), (p) + sizeof(uint16_t))
#define ngx_http_v2_write_uint32_aligned(p, s) \
(*(uint32_t *) (p) = htonl((uint32_t) (s)), (p) + sizeof(uint32_t))
#if (NGX_HAVE_NONALIGNED)
#define ngx_http_v2_write_uint16 ngx_http_v2_write_uint16_aligned
#define ngx_http_v2_write_uint32 ngx_http_v2_write_uint32_aligned
#else
#define ngx_http_v2_write_uint16(p, s) \
((p)[0] = (u_char) ((s) >> 8), \
(p)[1] = (u_char) (s), \
(p) + sizeof(uint16_t))
#define ngx_http_v2_write_uint32(p, s) \
((p)[0] = (u_char) ((s) >> 24), \
(p)[1] = (u_char) ((s) >> 16), \
(p)[2] = (u_char) ((s) >> 8), \
(p)[3] = (u_char) (s), \
(p) + sizeof(uint32_t))
#endif
#define ngx_http_v2_write_len_and_type(p, l, t) \
ngx_http_v2_write_uint32_aligned(p, (l) << 8 | (t))
#define ngx_http_v2_write_sid ngx_http_v2_write_uint32
#define ngx_http_v2_indexed(i) (128 + (i))
#define ngx_http_v2_inc_indexed(i) (64 + (i))
#define ngx_http_v2_write_name(dst, src, len, tmp) \
ngx_http_v2_string_encode(dst, src, len, tmp, 1)
#define ngx_http_v2_write_value(dst, src, len, tmp) \
ngx_http_v2_string_encode(dst, src, len, tmp, 0)
#define NGX_HTTP_V2_ENCODE_RAW 0
#define NGX_HTTP_V2_ENCODE_HUFF 0x80
#define NGX_HTTP_V2_AUTHORITY_INDEX 1
#define NGX_HTTP_V2_METHOD_INDEX 2
#define NGX_HTTP_V2_METHOD_GET_INDEX 2
#define NGX_HTTP_V2_METHOD_POST_INDEX 3
#define NGX_HTTP_V2_PATH_INDEX 4
#define NGX_HTTP_V2_PATH_ROOT_INDEX 4
#define NGX_HTTP_V2_SCHEME_HTTP_INDEX 6
#define NGX_HTTP_V2_SCHEME_HTTPS_INDEX 7
#define NGX_HTTP_V2_STATUS_INDEX 8
#define NGX_HTTP_V2_STATUS_200_INDEX 8
#define NGX_HTTP_V2_STATUS_204_INDEX 9
#define NGX_HTTP_V2_STATUS_206_INDEX 10
#define NGX_HTTP_V2_STATUS_304_INDEX 11
#define NGX_HTTP_V2_STATUS_400_INDEX 12
#define NGX_HTTP_V2_STATUS_404_INDEX 13
#define NGX_HTTP_V2_STATUS_500_INDEX 14
#define NGX_HTTP_V2_ACCEPT_ENCODING_INDEX 16
#define NGX_HTTP_V2_ACCEPT_LANGUAGE_INDEX 17
#define NGX_HTTP_V2_CONTENT_LENGTH_INDEX 28
#define NGX_HTTP_V2_CONTENT_TYPE_INDEX 31
#define NGX_HTTP_V2_DATE_INDEX 33
#define NGX_HTTP_V2_LAST_MODIFIED_INDEX 44
#define NGX_HTTP_V2_LOCATION_INDEX 46
#define NGX_HTTP_V2_SERVER_INDEX 54
#define NGX_HTTP_V2_USER_AGENT_INDEX 58
#define NGX_HTTP_V2_VARY_INDEX 59
u_char *ngx_http_v2_string_encode(u_char *dst, u_char *src, size_t len,
u_char *tmp, ngx_uint_t lower);
#endif /* _NGX_HTTP_V2_H_INCLUDED_ */
nginx-1.14.0/src/http/v2/ngx_http_v2_filter_module.c 000644 001751 001751 00000157260 13265410474 023544 0 ustar 00mdounin mdounin 000000 000000
/*
* Copyright (C) Nginx, Inc.
* Copyright (C) Valentin V. Bartenev
* Copyright (C) Ruslan Ermilov
*/
#include
#include
#include
#include
#include
/*
* This returns precise number of octets for values in range 0..253
* and estimate number for the rest, but not smaller than required.
*/
#define ngx_http_v2_integer_octets(v) (1 + (v) / 127)
#define ngx_http_v2_literal_size(h) \
(ngx_http_v2_integer_octets(sizeof(h) - 1) + sizeof(h) - 1)
#define NGX_HTTP_V2_NO_TRAILERS (ngx_http_v2_out_frame_t *) -1
typedef struct {
ngx_str_t name;
u_char index;
ngx_uint_t offset;
} ngx_http_v2_push_header_t;
static ngx_http_v2_push_header_t ngx_http_v2_push_headers[] = {
{ ngx_string(":authority"), NGX_HTTP_V2_AUTHORITY_INDEX,
offsetof(ngx_http_headers_in_t, host) },
{ ngx_string("accept-encoding"), NGX_HTTP_V2_ACCEPT_ENCODING_INDEX,
offsetof(ngx_http_headers_in_t, accept_encoding) },
{ ngx_string("accept-language"), NGX_HTTP_V2_ACCEPT_LANGUAGE_INDEX,
offsetof(ngx_http_headers_in_t, accept_language) },
{ ngx_string("user-agent"), NGX_HTTP_V2_USER_AGENT_INDEX,
offsetof(ngx_http_headers_in_t, user_agent) },
};
#define NGX_HTTP_V2_PUSH_HEADERS \
(sizeof(ngx_http_v2_push_headers) / sizeof(ngx_http_v2_push_header_t))
static ngx_int_t ngx_http_v2_push_resources(ngx_http_request_t *r);
static ngx_int_t ngx_http_v2_push_resource(ngx_http_request_t *r,
ngx_str_t *path, ngx_str_t *binary);
static ngx_http_v2_out_frame_t *ngx_http_v2_create_headers_frame(
ngx_http_request_t *r, u_char *pos, u_char *end, ngx_uint_t fin);
static ngx_http_v2_out_frame_t *ngx_http_v2_create_push_frame(
ngx_http_request_t *r, u_char *pos, u_char *end);
static ngx_http_v2_out_frame_t *ngx_http_v2_create_trailers_frame(
ngx_http_request_t *r);
static ngx_chain_t *ngx_http_v2_send_chain(ngx_connection_t *fc,
ngx_chain_t *in, off_t limit);
static ngx_chain_t *ngx_http_v2_filter_get_shadow(
ngx_http_v2_stream_t *stream, ngx_buf_t *buf, off_t offset, off_t size);
static ngx_http_v2_out_frame_t *ngx_http_v2_filter_get_data_frame(
ngx_http_v2_stream_t *stream, size_t len, ngx_chain_t *first,
ngx_chain_t *last);
static ngx_inline ngx_int_t ngx_http_v2_flow_control(
ngx_http_v2_connection_t *h2c, ngx_http_v2_stream_t *stream);
static void ngx_http_v2_waiting_queue(ngx_http_v2_connection_t *h2c,
ngx_http_v2_stream_t *stream);
static ngx_inline ngx_int_t ngx_http_v2_filter_send(
ngx_connection_t *fc, ngx_http_v2_stream_t *stream);
static ngx_int_t ngx_http_v2_headers_frame_handler(
ngx_http_v2_connection_t *h2c, ngx_http_v2_out_frame_t *frame);
static ngx_int_t ngx_http_v2_push_frame_handler(
ngx_http_v2_connection_t *h2c, ngx_http_v2_out_frame_t *frame);
static ngx_int_t ngx_http_v2_data_frame_handler(
ngx_http_v2_connection_t *h2c, ngx_http_v2_out_frame_t *frame);
static ngx_inline void ngx_http_v2_handle_frame(
ngx_http_v2_stream_t *stream, ngx_http_v2_out_frame_t *frame);
static ngx_inline void ngx_http_v2_handle_stream(
ngx_http_v2_connection_t *h2c, ngx_http_v2_stream_t *stream);
static void ngx_http_v2_filter_cleanup(void *data);
static ngx_int_t ngx_http_v2_filter_init(ngx_conf_t *cf);
static ngx_http_module_t ngx_http_v2_filter_module_ctx = {
NULL, /* preconfiguration */
ngx_http_v2_filter_init, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
NULL, /* create location configuration */
NULL /* merge location configuration */
};
ngx_module_t ngx_http_v2_filter_module = {
NGX_MODULE_V1,
&ngx_http_v2_filter_module_ctx, /* module context */
NULL, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static ngx_http_output_header_filter_pt ngx_http_next_header_filter;
static ngx_int_t
ngx_http_v2_header_filter(ngx_http_request_t *r)
{
u_char status, *pos, *start, *p, *tmp;
size_t len, tmp_len;
ngx_str_t host, location;
ngx_uint_t i, port, fin;
ngx_list_part_t *part;
ngx_table_elt_t *header;
ngx_connection_t *fc;
ngx_http_cleanup_t *cln;
ngx_http_v2_stream_t *stream;
ngx_http_v2_out_frame_t *frame;
ngx_http_v2_connection_t *h2c;
ngx_http_core_loc_conf_t *clcf;
ngx_http_core_srv_conf_t *cscf;
u_char addr[NGX_SOCKADDR_STRLEN];
static const u_char nginx[5] = "\x84\xaa\x63\x55\xe7";
#if (NGX_HTTP_GZIP)
static const u_char accept_encoding[12] =
"\x8b\x84\x84\x2d\x69\x5b\x05\x44\x3c\x86\xaa\x6f";
#endif
static size_t nginx_ver_len = ngx_http_v2_literal_size(NGINX_VER);
static u_char nginx_ver[ngx_http_v2_literal_size(NGINX_VER)];
static size_t nginx_ver_build_len =
ngx_http_v2_literal_size(NGINX_VER_BUILD);
static u_char nginx_ver_build[ngx_http_v2_literal_size(NGINX_VER_BUILD)];
stream = r->stream;
if (!stream) {
return ngx_http_next_header_filter(r);
}
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http2 header filter");
if (r->header_sent) {
return NGX_OK;
}
r->header_sent = 1;
if (r != r->main) {
return NGX_OK;
}
fc = r->connection;
if (fc->error) {
return NGX_ERROR;
}
if (r->method == NGX_HTTP_HEAD) {
r->header_only = 1;
}
switch (r->headers_out.status) {
case NGX_HTTP_OK:
status = ngx_http_v2_indexed(NGX_HTTP_V2_STATUS_200_INDEX);
break;
case NGX_HTTP_NO_CONTENT:
r->header_only = 1;
ngx_str_null(&r->headers_out.content_type);
r->headers_out.content_length = NULL;
r->headers_out.content_length_n = -1;
r->headers_out.last_modified_time = -1;
r->headers_out.last_modified = NULL;
status = ngx_http_v2_indexed(NGX_HTTP_V2_STATUS_204_INDEX);
break;
case NGX_HTTP_PARTIAL_CONTENT:
status = ngx_http_v2_indexed(NGX_HTTP_V2_STATUS_206_INDEX);
break;
case NGX_HTTP_NOT_MODIFIED:
r->header_only = 1;
status = ngx_http_v2_indexed(NGX_HTTP_V2_STATUS_304_INDEX);
break;
default:
r->headers_out.last_modified_time = -1;
r->headers_out.last_modified = NULL;
switch (r->headers_out.status) {
case NGX_HTTP_BAD_REQUEST:
status = ngx_http_v2_indexed(NGX_HTTP_V2_STATUS_400_INDEX);
break;
case NGX_HTTP_NOT_FOUND:
status = ngx_http_v2_indexed(NGX_HTTP_V2_STATUS_404_INDEX);
break;
case NGX_HTTP_INTERNAL_SERVER_ERROR:
status = ngx_http_v2_indexed(NGX_HTTP_V2_STATUS_500_INDEX);
break;
default:
status = 0;
}
}
h2c = stream->connection;
if (!h2c->push_disabled && !h2c->goaway
&& stream->node->id % 2 == 1
&& r->method != NGX_HTTP_HEAD)
{
if (ngx_http_v2_push_resources(r) != NGX_OK) {
return NGX_ERROR;
}
}
len = h2c->table_update ? 1 : 0;
len += status ? 1 : 1 + ngx_http_v2_literal_size("418");
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
if (r->headers_out.server == NULL) {
if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_ON) {
len += 1 + nginx_ver_len;
} else if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_BUILD) {
len += 1 + nginx_ver_build_len;
} else {
len += 1 + sizeof(nginx);
}
}
if (r->headers_out.date == NULL) {
len += 1 + ngx_http_v2_literal_size("Wed, 31 Dec 1986 18:00:00 GMT");
}
if (r->headers_out.content_type.len) {
len += 1 + NGX_HTTP_V2_INT_OCTETS + r->headers_out.content_type.len;
if (r->headers_out.content_type_len == r->headers_out.content_type.len
&& r->headers_out.charset.len)
{
len += sizeof("; charset=") - 1 + r->headers_out.charset.len;
}
}
if (r->headers_out.content_length == NULL
&& r->headers_out.content_length_n >= 0)
{
len += 1 + ngx_http_v2_integer_octets(NGX_OFF_T_LEN) + NGX_OFF_T_LEN;
}
if (r->headers_out.last_modified == NULL
&& r->headers_out.last_modified_time != -1)
{
len += 1 + ngx_http_v2_literal_size("Wed, 31 Dec 1986 18:00:00 GMT");
}
if (r->headers_out.location && r->headers_out.location->value.len) {
if (r->headers_out.location->value.data[0] == '/'
&& clcf->absolute_redirect)
{
if (clcf->server_name_in_redirect) {
cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
host = cscf->server_name;
} else if (r->headers_in.server.len) {
host = r->headers_in.server;
} else {
host.len = NGX_SOCKADDR_STRLEN;
host.data = addr;
if (ngx_connection_local_sockaddr(fc, &host, 0) != NGX_OK) {
return NGX_ERROR;
}
}
port = ngx_inet_get_port(fc->local_sockaddr);
location.len = sizeof("https://") - 1 + host.len
+ r->headers_out.location->value.len;
if (clcf->port_in_redirect) {
#if (NGX_HTTP_SSL)
if (fc->ssl)
port = (port == 443) ? 0 : port;
else
#endif
port = (port == 80) ? 0 : port;
} else {
port = 0;
}
if (port) {
location.len += sizeof(":65535") - 1;
}
location.data = ngx_pnalloc(r->pool, location.len);
if (location.data == NULL) {
return NGX_ERROR;
}
p = ngx_cpymem(location.data, "http", sizeof("http") - 1);
#if (NGX_HTTP_SSL)
if (fc->ssl) {
*p++ = 's';
}
#endif
*p++ = ':'; *p++ = '/'; *p++ = '/';
p = ngx_cpymem(p, host.data, host.len);
if (port) {
p = ngx_sprintf(p, ":%ui", port);
}
p = ngx_cpymem(p, r->headers_out.location->value.data,
r->headers_out.location->value.len);
/* update r->headers_out.location->value for possible logging */
r->headers_out.location->value.len = p - location.data;
r->headers_out.location->value.data = location.data;
ngx_str_set(&r->headers_out.location->key, "Location");
}
r->headers_out.location->hash = 0;
len += 1 + NGX_HTTP_V2_INT_OCTETS + r->headers_out.location->value.len;
}
tmp_len = len;
#if (NGX_HTTP_GZIP)
if (r->gzip_vary) {
if (clcf->gzip_vary) {
len += 1 + sizeof(accept_encoding);
} else {
r->gzip_vary = 0;
}
}
#endif
part = &r->headers_out.headers.part;
header = part->elts;
for (i = 0; /* void */; i++) {
if (i >= part->nelts) {
if (part->next == NULL) {
break;
}
part = part->next;
header = part->elts;
i = 0;
}
if (header[i].hash == 0) {
continue;
}
if (header[i].key.len > NGX_HTTP_V2_MAX_FIELD) {
ngx_log_error(NGX_LOG_CRIT, fc->log, 0,
"too long response header name: \"%V\"",
&header[i].key);
return NGX_ERROR;
}
if (header[i].value.len > NGX_HTTP_V2_MAX_FIELD) {
ngx_log_error(NGX_LOG_CRIT, fc->log, 0,
"too long response header value: \"%V: %V\"",
&header[i].key, &header[i].value);
return NGX_ERROR;
}
len += 1 + NGX_HTTP_V2_INT_OCTETS + header[i].key.len
+ NGX_HTTP_V2_INT_OCTETS + header[i].value.len;
if (header[i].key.len > tmp_len) {
tmp_len = header[i].key.len;
}
if (header[i].value.len > tmp_len) {
tmp_len = header[i].value.len;
}
}
tmp = ngx_palloc(r->pool, tmp_len);
pos = ngx_pnalloc(r->pool, len);
if (pos == NULL || tmp == NULL) {
return NGX_ERROR;
}
start = pos;
if (h2c->table_update) {
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,
"http2 table size update: 0");
*pos++ = (1 << 5) | 0;
h2c->table_update = 0;
}
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,
"http2 output header: \":status: %03ui\"",
r->headers_out.status);
if (status) {
*pos++ = status;
} else {
*pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_STATUS_INDEX);
*pos++ = NGX_HTTP_V2_ENCODE_RAW | 3;
pos = ngx_sprintf(pos, "%03ui", r->headers_out.status);
}
if (r->headers_out.server == NULL) {
if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_ON) {
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,
"http2 output header: \"server: %s\"",
NGINX_VER);
} else if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_BUILD) {
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,
"http2 output header: \"server: %s\"",
NGINX_VER_BUILD);
} else {
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,
"http2 output header: \"server: nginx\"");
}
*pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_SERVER_INDEX);
if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_ON) {
if (nginx_ver[0] == '\0') {
p = ngx_http_v2_write_value(nginx_ver, (u_char *) NGINX_VER,
sizeof(NGINX_VER) - 1, tmp);
nginx_ver_len = p - nginx_ver;
}
pos = ngx_cpymem(pos, nginx_ver, nginx_ver_len);
} else if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_BUILD) {
if (nginx_ver_build[0] == '\0') {
p = ngx_http_v2_write_value(nginx_ver_build,
(u_char *) NGINX_VER_BUILD,
sizeof(NGINX_VER_BUILD) - 1, tmp);
nginx_ver_build_len = p - nginx_ver_build;
}
pos = ngx_cpymem(pos, nginx_ver_build, nginx_ver_build_len);
} else {
pos = ngx_cpymem(pos, nginx, sizeof(nginx));
}
}
if (r->headers_out.date == NULL) {
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,
"http2 output header: \"date: %V\"",
&ngx_cached_http_time);
*pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_DATE_INDEX);
pos = ngx_http_v2_write_value(pos, ngx_cached_http_time.data,
ngx_cached_http_time.len, tmp);
}
if (r->headers_out.content_type.len) {
*pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_CONTENT_TYPE_INDEX);
if (r->headers_out.content_type_len == r->headers_out.content_type.len
&& r->headers_out.charset.len)
{
len = r->headers_out.content_type.len + sizeof("; charset=") - 1
+ r->headers_out.charset.len;
p = ngx_pnalloc(r->pool, len);
if (p == NULL) {
return NGX_ERROR;
}
p = ngx_cpymem(p, r->headers_out.content_type.data,
r->headers_out.content_type.len);
p = ngx_cpymem(p, "; charset=", sizeof("; charset=") - 1);
p = ngx_cpymem(p, r->headers_out.charset.data,
r->headers_out.charset.len);
/* updated r->headers_out.content_type is also needed for logging */
r->headers_out.content_type.len = len;
r->headers_out.content_type.data = p - len;
}
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,
"http2 output header: \"content-type: %V\"",
&r->headers_out.content_type);
pos = ngx_http_v2_write_value(pos, r->headers_out.content_type.data,
r->headers_out.content_type.len, tmp);
}
if (r->headers_out.content_length == NULL
&& r->headers_out.content_length_n >= 0)
{
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,
"http2 output header: \"content-length: %O\"",
r->headers_out.content_length_n);
*pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_CONTENT_LENGTH_INDEX);
p = pos;
pos = ngx_sprintf(pos + 1, "%O", r->headers_out.content_length_n);
*p = NGX_HTTP_V2_ENCODE_RAW | (u_char) (pos - p - 1);
}
if (r->headers_out.last_modified == NULL
&& r->headers_out.last_modified_time != -1)
{
*pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_LAST_MODIFIED_INDEX);
ngx_http_time(pos, r->headers_out.last_modified_time);
len = sizeof("Wed, 31 Dec 1986 18:00:00 GMT") - 1;
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, fc->log, 0,
"http2 output header: \"last-modified: %*s\"",
len, pos);
/*
* Date will always be encoded using huffman in the temporary buffer,
* so it's safe here to use src and dst pointing to the same address.
*/
pos = ngx_http_v2_write_value(pos, pos, len, tmp);
}
if (r->headers_out.location && r->headers_out.location->value.len) {
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,
"http2 output header: \"location: %V\"",
&r->headers_out.location->value);
*pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_LOCATION_INDEX);
pos = ngx_http_v2_write_value(pos, r->headers_out.location->value.data,
r->headers_out.location->value.len, tmp);
}
#if (NGX_HTTP_GZIP)
if (r->gzip_vary) {
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,
"http2 output header: \"vary: Accept-Encoding\"");
*pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_VARY_INDEX);
pos = ngx_cpymem(pos, accept_encoding, sizeof(accept_encoding));
}
#endif
part = &r->headers_out.headers.part;
header = part->elts;
for (i = 0; /* void */; i++) {
if (i >= part->nelts) {
if (part->next == NULL) {
break;
}
part = part->next;
header = part->elts;
i = 0;
}
if (header[i].hash == 0) {
continue;
}
#if (NGX_DEBUG)
if (fc->log->log_level & NGX_LOG_DEBUG_HTTP) {
ngx_strlow(tmp, header[i].key.data, header[i].key.len);
ngx_log_debug3(NGX_LOG_DEBUG_HTTP, fc->log, 0,
"http2 output header: \"%*s: %V\"",
header[i].key.len, tmp, &header[i].value);
}
#endif
*pos++ = 0;
pos = ngx_http_v2_write_name(pos, header[i].key.data,
header[i].key.len, tmp);
pos = ngx_http_v2_write_value(pos, header[i].value.data,
header[i].value.len, tmp);
}
fin = r->header_only
|| (r->headers_out.content_length_n == 0 && !r->expect_trailers);
frame = ngx_http_v2_create_headers_frame(r, start, pos, fin);
if (frame == NULL) {
return NGX_ERROR;
}
ngx_http_v2_queue_blocked_frame(h2c, frame);
stream->queued++;
cln = ngx_http_cleanup_add(r, 0);
if (cln == NULL) {
return NGX_ERROR;
}
cln->handler = ngx_http_v2_filter_cleanup;
cln->data = stream;
fc->send_chain = ngx_http_v2_send_chain;
fc->need_last_buf = 1;
return ngx_http_v2_filter_send(fc, stream);
}
static ngx_int_t
ngx_http_v2_push_resources(ngx_http_request_t *r)
{
u_char *start, *end, *last;
ngx_int_t rc;
ngx_str_t path;
ngx_uint_t i, push;
ngx_table_elt_t **h;
ngx_http_v2_loc_conf_t *h2lcf;
ngx_http_complex_value_t *pushes;
ngx_str_t binary[NGX_HTTP_V2_PUSH_HEADERS];
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http2 push resources");
ngx_memzero(binary, NGX_HTTP_V2_PUSH_HEADERS * sizeof(ngx_str_t));
h2lcf = ngx_http_get_module_loc_conf(r, ngx_http_v2_module);
if (h2lcf->pushes) {
pushes = h2lcf->pushes->elts;
for (i = 0; i < h2lcf->pushes->nelts; i++) {
if (ngx_http_complex_value(r, &pushes[i], &path) != NGX_OK) {
return NGX_ERROR;
}
if (path.len == 0) {
continue;
}
if (path.len == 3 && ngx_strncmp(path.data, "off", 3) == 0) {
continue;
}
rc = ngx_http_v2_push_resource(r, &path, binary);
if (rc == NGX_ERROR) {
return NGX_ERROR;
}
if (rc == NGX_ABORT) {
return NGX_OK;
}
/* NGX_OK, NGX_DECLINED */
}
}
if (!h2lcf->push_preload) {
return NGX_OK;
}
h = r->headers_out.link.elts;
for (i = 0; i < r->headers_out.link.nelts; i++) {
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http2 parse link: \"%V\"", &h[i]->value);
start = h[i]->value.data;
end = h[i]->value.data + h[i]->value.len;
next_link:
while (start < end && *start == ' ') { start++; }
if (start == end || *start++ != '<') {
continue;
}
while (start < end && *start == ' ') { start++; }
for (last = start; last < end && *last != '>'; last++) {
/* void */
}
if (last == start || last == end) {
continue;
}
path.len = last - start;
path.data = start;
start = last + 1;
while (start < end && *start == ' ') { start++; }
if (start == end) {
continue;
}
if (*start == ',') {
start++;
goto next_link;
}
if (*start++ != ';') {
continue;
}
last = ngx_strlchr(start, end, ',');
if (last == NULL) {
last = end;
}
push = 0;
for ( ;; ) {
while (start < last && *start == ' ') { start++; }
if (last - start >= 6
&& ngx_strncasecmp(start, (u_char *) "nopush", 6) == 0)
{
start += 6;
if (start == last || *start == ' ' || *start == ';') {
push = 0;
break;
}
goto next_param;
}
if (last - start >= 11
&& ngx_strncasecmp(start, (u_char *) "rel=preload", 11) == 0)
{
start += 11;
if (start == last || *start == ' ' || *start == ';') {
push = 1;
}
goto next_param;
}
if (last - start >= 4
&& ngx_strncasecmp(start, (u_char *) "rel=", 4) == 0)
{
start += 4;
while (start < last && *start == ' ') { start++; }
if (start == last || *start++ != '"') {
goto next_param;
}
for ( ;; ) {
while (start < last && *start == ' ') { start++; }
if (last - start >= 7
&& ngx_strncasecmp(start, (u_char *) "preload", 7) == 0)
{
start += 7;
if (start < last && (*start == ' ' || *start == '"')) {
push = 1;
break;
}
}
while (start < last && *start != ' ' && *start != '"') {
start++;
}
if (start == last) {
break;
}
if (*start == '"') {
break;
}
start++;
}
}
next_param:
start = ngx_strlchr(start, last, ';');
if (start == NULL) {
break;
}
start++;
}
if (push) {
while (path.len && path.data[path.len - 1] == ' ') {
path.len--;
}
}
if (push && path.len
&& !(path.len > 1 && path.data[0] == '/' && path.data[1] == '/'))
{
rc = ngx_http_v2_push_resource(r, &path, binary);
if (rc == NGX_ERROR) {
return NGX_ERROR;
}
if (rc == NGX_ABORT) {
return NGX_OK;
}
/* NGX_OK, NGX_DECLINED */
}
if (last < end) {
start = last + 1;
goto next_link;
}
}
return NGX_OK;
}
static ngx_int_t
ngx_http_v2_push_resource(ngx_http_request_t *r, ngx_str_t *path,
ngx_str_t *binary)
{
u_char *start, *pos, *tmp;
size_t len;
ngx_str_t *value;
ngx_uint_t i;
ngx_table_elt_t **h;
ngx_connection_t *fc;
ngx_http_v2_stream_t *stream;
ngx_http_v2_out_frame_t *frame;
ngx_http_v2_connection_t *h2c;
ngx_http_v2_push_header_t *ph;
fc = r->connection;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0, "http2 push resource");
stream = r->stream;
h2c = stream->connection;
if (!ngx_path_separator(path->data[0])) {
ngx_log_error(NGX_LOG_WARN, fc->log, 0,
"non-absolute path \"%V\" not pushed", path);
return NGX_DECLINED;
}
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
"http2 pushing:%ui limit:%ui",
h2c->pushing, h2c->concurrent_pushes);
if (h2c->pushing >= h2c->concurrent_pushes) {
return NGX_ABORT;
}
if (h2c->last_push == 0x7ffffffe) {
return NGX_ABORT;
}
if (path->len > NGX_HTTP_V2_MAX_FIELD) {
return NGX_DECLINED;
}
if (r->headers_in.host == NULL) {
return NGX_ABORT;
}
ph = ngx_http_v2_push_headers;
if (binary[0].len) {
tmp = ngx_palloc(r->pool, path->len);
if (tmp == NULL) {
return NGX_ERROR;
}
} else {
len = path->len;
for (i = 0; i < NGX_HTTP_V2_PUSH_HEADERS; i++) {
h = (ngx_table_elt_t **) ((char *) &r->headers_in + ph[i].offset);
if (*h) {
len = ngx_max(len, (*h)->value.len);
}
}
tmp = ngx_palloc(r->pool, len);
if (tmp == NULL) {
return NGX_ERROR;
}
for (i = 0; i < NGX_HTTP_V2_PUSH_HEADERS; i++) {
h = (ngx_table_elt_t **) ((char *) &r->headers_in + ph[i].offset);
if (*h == NULL) {
continue;
}
value = &(*h)->value;
len = 1 + NGX_HTTP_V2_INT_OCTETS + value->len;
pos = ngx_pnalloc(r->pool, len);
if (pos == NULL) {
return NGX_ERROR;
}
binary[i].data = pos;
*pos++ = ngx_http_v2_inc_indexed(ph[i].index);
pos = ngx_http_v2_write_value(pos, value->data, value->len, tmp);
binary[i].len = pos - binary[i].data;
}
}
len = (h2c->table_update ? 1 : 0)
+ 1
+ 1 + NGX_HTTP_V2_INT_OCTETS + path->len
+ 1;
for (i = 0; i < NGX_HTTP_V2_PUSH_HEADERS; i++) {
len += binary[i].len;
}
pos = ngx_pnalloc(r->pool, len);
if (pos == NULL) {
return NGX_ERROR;
}
start = pos;
if (h2c->table_update) {
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,
"http2 table size update: 0");
*pos++ = (1 << 5) | 0;
h2c->table_update = 0;
}
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,
"http2 push header: \":method: GET\"");
*pos++ = ngx_http_v2_indexed(NGX_HTTP_V2_METHOD_GET_INDEX);
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,
"http2 push header: \":path: %V\"", path);
*pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_PATH_INDEX);
pos = ngx_http_v2_write_value(pos, path->data, path->len, tmp);
#if (NGX_HTTP_SSL)
if (fc->ssl) {
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,
"http2 push header: \":scheme: https\"");
*pos++ = ngx_http_v2_indexed(NGX_HTTP_V2_SCHEME_HTTPS_INDEX);
} else
#endif
{
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,
"http2 push header: \":scheme: http\"");
*pos++ = ngx_http_v2_indexed(NGX_HTTP_V2_SCHEME_HTTP_INDEX);
}
for (i = 0; i < NGX_HTTP_V2_PUSH_HEADERS; i++) {
h = (ngx_table_elt_t **) ((char *) &r->headers_in + ph[i].offset);
if (*h == NULL) {
continue;
}
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, fc->log, 0,
"http2 push header: \"%V: %V\"",
&ph[i].name, &(*h)->value);
pos = ngx_cpymem(pos, binary[i].data, binary[i].len);
}
frame = ngx_http_v2_create_push_frame(r, start, pos);
if (frame == NULL) {
return NGX_ERROR;
}
ngx_http_v2_queue_blocked_frame(h2c, frame);
stream->queued++;
stream = ngx_http_v2_push_stream(stream, path);
if (stream) {
stream->request->request_length = pos - start;
return NGX_OK;
}
return NGX_ERROR;
}
static ngx_http_v2_out_frame_t *
ngx_http_v2_create_headers_frame(ngx_http_request_t *r, u_char *pos,
u_char *end, ngx_uint_t fin)
{
u_char type, flags;
size_t rest, frame_size;
ngx_buf_t *b;
ngx_chain_t *cl, **ll;
ngx_http_v2_stream_t *stream;
ngx_http_v2_out_frame_t *frame;
stream = r->stream;
rest = end - pos;
frame = ngx_palloc(r->pool, sizeof(ngx_http_v2_out_frame_t));
if (frame == NULL) {
return NULL;
}
frame->handler = ngx_http_v2_headers_frame_handler;
frame->stream = stream;
frame->length = rest;
frame->blocked = 1;
frame->fin = fin;
ll = &frame->first;
type = NGX_HTTP_V2_HEADERS_FRAME;
flags = fin ? NGX_HTTP_V2_END_STREAM_FLAG : NGX_HTTP_V2_NO_FLAG;
frame_size = stream->connection->frame_size;
for ( ;; ) {
if (rest <= frame_size) {
frame_size = rest;
flags |= NGX_HTTP_V2_END_HEADERS_FLAG;
}
b = ngx_create_temp_buf(r->pool, NGX_HTTP_V2_FRAME_HEADER_SIZE);
if (b == NULL) {
return NULL;
}
b->last = ngx_http_v2_write_len_and_type(b->last, frame_size, type);
*b->last++ = flags;
b->last = ngx_http_v2_write_sid(b->last, stream->node->id);
b->tag = (ngx_buf_tag_t) &ngx_http_v2_module;
cl = ngx_alloc_chain_link(r->pool);
if (cl == NULL) {
return NULL;
}
cl->buf = b;
*ll = cl;
ll = &cl->next;
b = ngx_calloc_buf(r->pool);
if (b == NULL) {
return NULL;
}
b->pos = pos;
pos += frame_size;
b->last = pos;
b->start = b->pos;
b->end = b->last;
b->temporary = 1;
cl = ngx_alloc_chain_link(r->pool);
if (cl == NULL) {
return NULL;
}
cl->buf = b;
*ll = cl;
ll = &cl->next;
rest -= frame_size;
if (rest) {
frame->length += NGX_HTTP_V2_FRAME_HEADER_SIZE;
type = NGX_HTTP_V2_CONTINUATION_FRAME;
flags = NGX_HTTP_V2_NO_FLAG;
continue;
}
b->last_buf = fin;
cl->next = NULL;
frame->last = cl;
ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http2:%ui create HEADERS frame %p: len:%uz fin:%ui",
stream->node->id, frame, frame->length, fin);
return frame;
}
}
static ngx_http_v2_out_frame_t *
ngx_http_v2_create_push_frame(ngx_http_request_t *r, u_char *pos, u_char *end)
{
u_char type, flags;
size_t rest, frame_size, len;
ngx_buf_t *b;
ngx_chain_t *cl, **ll;
ngx_http_v2_stream_t *stream;
ngx_http_v2_out_frame_t *frame;
ngx_http_v2_connection_t *h2c;
stream = r->stream;
h2c = stream->connection;
rest = NGX_HTTP_V2_STREAM_ID_SIZE + (end - pos);
frame = ngx_palloc(r->pool, sizeof(ngx_http_v2_out_frame_t));
if (frame == NULL) {
return NULL;
}
frame->handler = ngx_http_v2_push_frame_handler;
frame->stream = stream;
frame->length = rest;
frame->blocked = 1;
frame->fin = 0;
ll = &frame->first;
type = NGX_HTTP_V2_PUSH_PROMISE_FRAME;
flags = NGX_HTTP_V2_NO_FLAG;
frame_size = h2c->frame_size;
for ( ;; ) {
if (rest <= frame_size) {
frame_size = rest;
flags |= NGX_HTTP_V2_END_HEADERS_FLAG;
}
b = ngx_create_temp_buf(r->pool,
NGX_HTTP_V2_FRAME_HEADER_SIZE
+ ((type == NGX_HTTP_V2_PUSH_PROMISE_FRAME)
? NGX_HTTP_V2_STREAM_ID_SIZE : 0));
if (b == NULL) {
return NULL;
}
b->last = ngx_http_v2_write_len_and_type(b->last, frame_size, type);
*b->last++ = flags;
b->last = ngx_http_v2_write_sid(b->last, stream->node->id);
b->tag = (ngx_buf_tag_t) &ngx_http_v2_module;
if (type == NGX_HTTP_V2_PUSH_PROMISE_FRAME) {
h2c->last_push += 2;
b->last = ngx_http_v2_write_sid(b->last, h2c->last_push);
len = frame_size - NGX_HTTP_V2_STREAM_ID_SIZE;
} else {
len = frame_size;
}
cl = ngx_alloc_chain_link(r->pool);
if (cl == NULL) {
return NULL;
}
cl->buf = b;
*ll = cl;
ll = &cl->next;
b = ngx_calloc_buf(r->pool);
if (b == NULL) {
return NULL;
}
b->pos = pos;
pos += len;
b->last = pos;
b->start = b->pos;
b->end = b->last;
b->temporary = 1;
cl = ngx_alloc_chain_link(r->pool);
if (cl == NULL) {
return NULL;
}
cl->buf = b;
*ll = cl;
ll = &cl->next;
rest -= frame_size;
if (rest) {
frame->length += NGX_HTTP_V2_FRAME_HEADER_SIZE;
type = NGX_HTTP_V2_CONTINUATION_FRAME;
continue;
}
cl->next = NULL;
frame->last = cl;
ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http2:%ui create PUSH_PROMISE frame %p: "
"sid:%ui len:%uz",
stream->node->id, frame, h2c->last_push,
frame->length);
return frame;
}
}
static ngx_http_v2_out_frame_t *
ngx_http_v2_create_trailers_frame(ngx_http_request_t *r)
{
u_char *pos, *start, *tmp;
size_t len, tmp_len;
ngx_uint_t i;
ngx_list_part_t *part;
ngx_table_elt_t *header;
ngx_connection_t *fc;
fc = r->connection;
len = 0;
tmp_len = 0;
part = &r->headers_out.trailers.part;
header = part->elts;
for (i = 0; /* void */; i++) {
if (i >= part->nelts) {
if (part->next == NULL) {
break;
}
part = part->next;
header = part->elts;
i = 0;
}
if (header[i].hash == 0) {
continue;
}
if (header[i].key.len > NGX_HTTP_V2_MAX_FIELD) {
ngx_log_error(NGX_LOG_CRIT, fc->log, 0,
"too long response trailer name: \"%V\"",
&header[i].key);
return NULL;
}
if (header[i].value.len > NGX_HTTP_V2_MAX_FIELD) {
ngx_log_error(NGX_LOG_CRIT, fc->log, 0,
"too long response trailer value: \"%V: %V\"",
&header[i].key, &header[i].value);
return NULL;
}
len += 1 + NGX_HTTP_V2_INT_OCTETS + header[i].key.len
+ NGX_HTTP_V2_INT_OCTETS + header[i].value.len;
if (header[i].key.len > tmp_len) {
tmp_len = header[i].key.len;
}
if (header[i].value.len > tmp_len) {
tmp_len = header[i].value.len;
}
}
if (len == 0) {
return NGX_HTTP_V2_NO_TRAILERS;
}
tmp = ngx_palloc(r->pool, tmp_len);
pos = ngx_pnalloc(r->pool, len);
if (pos == NULL || tmp == NULL) {
return NULL;
}
start = pos;
part = &r->headers_out.trailers.part;
header = part->elts;
for (i = 0; /* void */; i++) {
if (i >= part->nelts) {
if (part->next == NULL) {
break;
}
part = part->next;
header = part->elts;
i = 0;
}
if (header[i].hash == 0) {
continue;
}
#if (NGX_DEBUG)
if (fc->log->log_level & NGX_LOG_DEBUG_HTTP) {
ngx_strlow(tmp, header[i].key.data, header[i].key.len);
ngx_log_debug3(NGX_LOG_DEBUG_HTTP, fc->log, 0,
"http2 output trailer: \"%*s: %V\"",
header[i].key.len, tmp, &header[i].value);
}
#endif
*pos++ = 0;
pos = ngx_http_v2_write_name(pos, header[i].key.data,
header[i].key.len, tmp);
pos = ngx_http_v2_write_value(pos, header[i].value.data,
header[i].value.len, tmp);
}
return ngx_http_v2_create_headers_frame(r, start, pos, 1);
}
static ngx_chain_t *
ngx_http_v2_send_chain(ngx_connection_t *fc, ngx_chain_t *in, off_t limit)
{
off_t size, offset;
size_t rest, frame_size;
ngx_chain_t *cl, *out, **ln;
ngx_http_request_t *r;
ngx_http_v2_stream_t *stream;
ngx_http_v2_loc_conf_t *h2lcf;
ngx_http_v2_out_frame_t *frame, *trailers;
ngx_http_v2_connection_t *h2c;
r = fc->data;
stream = r->stream;
#if (NGX_SUPPRESS_WARN)
size = 0;
#endif
while (in) {
size = ngx_buf_size(in->buf);
if (size || in->buf->last_buf) {
break;
}
in = in->next;
}
if (in == NULL || stream->out_closed) {
if (stream->queued) {
fc->write->active = 1;
fc->write->ready = 0;
} else {
fc->buffered &= ~NGX_HTTP_V2_BUFFERED;
}
return NULL;
}
h2c = stream->connection;
if (size && ngx_http_v2_flow_control(h2c, stream) == NGX_DECLINED) {
fc->write->active = 1;
fc->write->ready = 0;
return in;
}
if (in->buf->tag == (ngx_buf_tag_t) &ngx_http_v2_filter_get_shadow) {
cl = ngx_alloc_chain_link(r->pool);
if (cl == NULL) {
return NGX_CHAIN_ERROR;
}
cl->buf = in->buf;
in->buf = cl->buf->shadow;
offset = ngx_buf_in_memory(in->buf)
? (cl->buf->pos - in->buf->pos)
: (cl->buf->file_pos - in->buf->file_pos);
cl->next = stream->free_bufs;
stream->free_bufs = cl;
} else {
offset = 0;
}
if (limit == 0 || limit > (off_t) h2c->send_window) {
limit = h2c->send_window;
}
if (limit > stream->send_window) {
limit = (stream->send_window > 0) ? stream->send_window : 0;
}
h2lcf = ngx_http_get_module_loc_conf(r, ngx_http_v2_module);
frame_size = (h2lcf->chunk_size < h2c->frame_size)
? h2lcf->chunk_size : h2c->frame_size;
trailers = NGX_HTTP_V2_NO_TRAILERS;
#if (NGX_SUPPRESS_WARN)
cl = NULL;
#endif
for ( ;; ) {
if ((off_t) frame_size > limit) {
frame_size = (size_t) limit;
}
ln = &out;
rest = frame_size;
while ((off_t) rest >= size) {
if (offset) {
cl = ngx_http_v2_filter_get_shadow(stream, in->buf,
offset, size);
if (cl == NULL) {
return NGX_CHAIN_ERROR;
}
offset = 0;
} else {
cl = ngx_alloc_chain_link(r->pool);
if (cl == NULL) {
return NGX_CHAIN_ERROR;
}
cl->buf = in->buf;
}
*ln = cl;
ln = &cl->next;
rest -= (size_t) size;
in = in->next;
if (in == NULL) {
frame_size -= rest;
rest = 0;
break;
}
size = ngx_buf_size(in->buf);
}
if (rest) {
cl = ngx_http_v2_filter_get_shadow(stream, in->buf, offset, rest);
if (cl == NULL) {
return NGX_CHAIN_ERROR;
}
cl->buf->flush = 0;
cl->buf->last_buf = 0;
*ln = cl;
offset += rest;
size -= rest;
}
if (cl->buf->last_buf) {
trailers = ngx_http_v2_create_trailers_frame(r);
if (trailers == NULL) {
return NGX_CHAIN_ERROR;
}
if (trailers != NGX_HTTP_V2_NO_TRAILERS) {
cl->buf->last_buf = 0;
}
}
if (frame_size || cl->buf->last_buf) {
frame = ngx_http_v2_filter_get_data_frame(stream, frame_size,
out, cl);
if (frame == NULL) {
return NGX_CHAIN_ERROR;
}
ngx_http_v2_queue_frame(h2c, frame);
h2c->send_window -= frame_size;
stream->send_window -= frame_size;
stream->queued++;
}
if (in == NULL) {
if (trailers != NGX_HTTP_V2_NO_TRAILERS) {
ngx_http_v2_queue_frame(h2c, trailers);
stream->queued++;
}
break;
}
limit -= frame_size;
if (limit == 0) {
break;
}
}
if (offset) {
cl = ngx_http_v2_filter_get_shadow(stream, in->buf, offset, size);
if (cl == NULL) {
return NGX_CHAIN_ERROR;
}
in->buf = cl->buf;
ngx_free_chain(r->pool, cl);
}
if (ngx_http_v2_filter_send(fc, stream) == NGX_ERROR) {
return NGX_CHAIN_ERROR;
}
if (in && ngx_http_v2_flow_control(h2c, stream) == NGX_DECLINED) {
fc->write->active = 1;
fc->write->ready = 0;
}
return in;
}
static ngx_chain_t *
ngx_http_v2_filter_get_shadow(ngx_http_v2_stream_t *stream, ngx_buf_t *buf,
off_t offset, off_t size)
{
ngx_buf_t *chunk;
ngx_chain_t *cl;
cl = ngx_chain_get_free_buf(stream->request->pool, &stream->free_bufs);
if (cl == NULL) {
return NULL;
}
chunk = cl->buf;
ngx_memcpy(chunk, buf, sizeof(ngx_buf_t));
chunk->tag = (ngx_buf_tag_t) &ngx_http_v2_filter_get_shadow;
chunk->shadow = buf;
if (ngx_buf_in_memory(chunk)) {
chunk->pos += offset;
chunk->last = chunk->pos + size;
}
if (chunk->in_file) {
chunk->file_pos += offset;
chunk->file_last = chunk->file_pos + size;
}
return cl;
}
static ngx_http_v2_out_frame_t *
ngx_http_v2_filter_get_data_frame(ngx_http_v2_stream_t *stream,
size_t len, ngx_chain_t *first, ngx_chain_t *last)
{
u_char flags;
ngx_buf_t *buf;
ngx_chain_t *cl;
ngx_http_v2_out_frame_t *frame;
frame = stream->free_frames;
if (frame) {
stream->free_frames = frame->next;
} else {
frame = ngx_palloc(stream->request->pool,
sizeof(ngx_http_v2_out_frame_t));
if (frame == NULL) {
return NULL;
}
}
flags = last->buf->last_buf ? NGX_HTTP_V2_END_STREAM_FLAG : 0;
ngx_log_debug4(NGX_LOG_DEBUG_HTTP, stream->request->connection->log, 0,
"http2:%ui create DATA frame %p: len:%uz flags:%ui",
stream->node->id, frame, len, (ngx_uint_t) flags);
cl = ngx_chain_get_free_buf(stream->request->pool,
&stream->free_frame_headers);
if (cl == NULL) {
return NULL;
}
buf = cl->buf;
if (buf->start == NULL) {
buf->start = ngx_palloc(stream->request->pool,
NGX_HTTP_V2_FRAME_HEADER_SIZE);
if (buf->start == NULL) {
return NULL;
}
buf->end = buf->start + NGX_HTTP_V2_FRAME_HEADER_SIZE;
buf->last = buf->end;
buf->tag = (ngx_buf_tag_t) &ngx_http_v2_module;
buf->memory = 1;
}
buf->pos = buf->start;
buf->last = buf->pos;
buf->last = ngx_http_v2_write_len_and_type(buf->last, len,
NGX_HTTP_V2_DATA_FRAME);
*buf->last++ = flags;
buf->last = ngx_http_v2_write_sid(buf->last, stream->node->id);
cl->next = first;
first = cl;
last->buf->flush = 1;
frame->first = first;
frame->last = last;
frame->handler = ngx_http_v2_data_frame_handler;
frame->stream = stream;
frame->length = len;
frame->blocked = 0;
frame->fin = last->buf->last_buf;
return frame;
}
static ngx_inline ngx_int_t
ngx_http_v2_flow_control(ngx_http_v2_connection_t *h2c,
ngx_http_v2_stream_t *stream)
{
ngx_log_debug3(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
"http2:%ui windows: conn:%uz stream:%z",
stream->node->id, h2c->send_window, stream->send_window);
if (stream->send_window <= 0) {
stream->exhausted = 1;
return NGX_DECLINED;
}
if (h2c->send_window == 0) {
ngx_http_v2_waiting_queue(h2c, stream);
return NGX_DECLINED;
}
return NGX_OK;
}
static void
ngx_http_v2_waiting_queue(ngx_http_v2_connection_t *h2c,
ngx_http_v2_stream_t *stream)
{
ngx_queue_t *q;
ngx_http_v2_stream_t *s;
if (stream->waiting) {
return;
}
stream->waiting = 1;
for (q = ngx_queue_last(&h2c->waiting);
q != ngx_queue_sentinel(&h2c->waiting);
q = ngx_queue_prev(q))
{
s = ngx_queue_data(q, ngx_http_v2_stream_t, queue);
if (s->node->rank < stream->node->rank
|| (s->node->rank == stream->node->rank
&& s->node->rel_weight >= stream->node->rel_weight))
{
break;
}
}
ngx_queue_insert_after(q, &stream->queue);
}
static ngx_inline ngx_int_t
ngx_http_v2_filter_send(ngx_connection_t *fc, ngx_http_v2_stream_t *stream)
{
stream->blocked = 1;
if (ngx_http_v2_send_output_queue(stream->connection) == NGX_ERROR) {
fc->error = 1;
return NGX_ERROR;
}
stream->blocked = 0;
if (stream->queued) {
fc->buffered |= NGX_HTTP_V2_BUFFERED;
fc->write->active = 1;
fc->write->ready = 0;
return NGX_AGAIN;
}
fc->buffered &= ~NGX_HTTP_V2_BUFFERED;
return NGX_OK;
}
static ngx_int_t
ngx_http_v2_headers_frame_handler(ngx_http_v2_connection_t *h2c,
ngx_http_v2_out_frame_t *frame)
{
ngx_chain_t *cl, *ln;
ngx_http_v2_stream_t *stream;
stream = frame->stream;
cl = frame->first;
for ( ;; ) {
if (cl->buf->pos != cl->buf->last) {
frame->first = cl;
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
"http2:%ui HEADERS frame %p was sent partially",
stream->node->id, frame);
return NGX_AGAIN;
}
ln = cl->next;
if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_v2_module) {
cl->next = stream->free_frame_headers;
stream->free_frame_headers = cl;
} else {
cl->next = stream->free_bufs;
stream->free_bufs = cl;
}
if (cl == frame->last) {
break;
}
cl = ln;
}
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
"http2:%ui HEADERS frame %p was sent",
stream->node->id, frame);
stream->request->header_size += NGX_HTTP_V2_FRAME_HEADER_SIZE
+ frame->length;
ngx_http_v2_handle_frame(stream, frame);
ngx_http_v2_handle_stream(h2c, stream);
return NGX_OK;
}
static ngx_int_t
ngx_http_v2_push_frame_handler(ngx_http_v2_connection_t *h2c,
ngx_http_v2_out_frame_t *frame)
{
ngx_chain_t *cl, *ln;
ngx_http_v2_stream_t *stream;
stream = frame->stream;
cl = frame->first;
for ( ;; ) {
if (cl->buf->pos != cl->buf->last) {
frame->first = cl;
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
"http2:%ui PUSH_PROMISE frame %p was sent partially",
stream->node->id, frame);
return NGX_AGAIN;
}
ln = cl->next;
if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_v2_module) {
cl->next = stream->free_frame_headers;
stream->free_frame_headers = cl;
} else {
cl->next = stream->free_bufs;
stream->free_bufs = cl;
}
if (cl == frame->last) {
break;
}
cl = ln;
}
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
"http2:%ui PUSH_PROMISE frame %p was sent",
stream->node->id, frame);
stream->request->header_size += NGX_HTTP_V2_FRAME_HEADER_SIZE
+ frame->length;
ngx_http_v2_handle_frame(stream, frame);
ngx_http_v2_handle_stream(h2c, stream);
return NGX_OK;
}
static ngx_int_t
ngx_http_v2_data_frame_handler(ngx_http_v2_connection_t *h2c,
ngx_http_v2_out_frame_t *frame)
{
ngx_buf_t *buf;
ngx_chain_t *cl, *ln;
ngx_http_v2_stream_t *stream;
stream = frame->stream;
cl = frame->first;
if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_v2_module) {
if (cl->buf->pos != cl->buf->last) {
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
"http2:%ui DATA frame %p was sent partially",
stream->node->id, frame);
return NGX_AGAIN;
}
ln = cl->next;
cl->next = stream->free_frame_headers;
stream->free_frame_headers = cl;
if (cl == frame->last) {
goto done;
}
cl = ln;
}
for ( ;; ) {
if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_v2_filter_get_shadow) {
buf = cl->buf->shadow;
if (ngx_buf_in_memory(buf)) {
buf->pos = cl->buf->pos;
}
if (buf->in_file) {
buf->file_pos = cl->buf->file_pos;
}
}
if (ngx_buf_size(cl->buf) != 0) {
if (cl != frame->first) {
frame->first = cl;
ngx_http_v2_handle_stream(h2c, stream);
}
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
"http2:%ui DATA frame %p was sent partially",
stream->node->id, frame);
return NGX_AGAIN;
}
ln = cl->next;
if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_v2_filter_get_shadow) {
cl->next = stream->free_bufs;
stream->free_bufs = cl;
} else {
ngx_free_chain(stream->request->pool, cl);
}
if (cl == frame->last) {
goto done;
}
cl = ln;
}
done:
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
"http2:%ui DATA frame %p was sent",
stream->node->id, frame);
stream->request->header_size += NGX_HTTP_V2_FRAME_HEADER_SIZE;
ngx_http_v2_handle_frame(stream, frame);
ngx_http_v2_handle_stream(h2c, stream);
return NGX_OK;
}
static ngx_inline void
ngx_http_v2_handle_frame(ngx_http_v2_stream_t *stream,
ngx_http_v2_out_frame_t *frame)
{
ngx_http_request_t *r;
r = stream->request;
r->connection->sent += NGX_HTTP_V2_FRAME_HEADER_SIZE + frame->length;
if (frame->fin) {
stream->out_closed = 1;
}
frame->next = stream->free_frames;
stream->free_frames = frame;
stream->queued--;
}
static ngx_inline void
ngx_http_v2_handle_stream(ngx_http_v2_connection_t *h2c,
ngx_http_v2_stream_t *stream)
{
ngx_event_t *wev;
ngx_connection_t *fc;
if (stream->waiting || stream->blocked) {
return;
}
fc = stream->request->connection;
if (!fc->error && stream->exhausted) {
return;
}
wev = fc->write;
wev->active = 0;
wev->ready = 1;
if (!fc->error && wev->delayed) {
return;
}
ngx_post_event(wev, &ngx_posted_events);
}
static void
ngx_http_v2_filter_cleanup(void *data)
{
ngx_http_v2_stream_t *stream = data;
size_t window;
ngx_event_t *wev;
ngx_queue_t *q;
ngx_http_v2_out_frame_t *frame, **fn;
ngx_http_v2_connection_t *h2c;
if (stream->waiting) {
stream->waiting = 0;
ngx_queue_remove(&stream->queue);
}
if (stream->queued == 0) {
return;
}
window = 0;
h2c = stream->connection;
fn = &h2c->last_out;
for ( ;; ) {
frame = *fn;
if (frame == NULL) {
break;
}
if (frame->stream == stream && !frame->blocked) {
*fn = frame->next;
window += frame->length;
if (--stream->queued == 0) {
break;
}
continue;
}
fn = &frame->next;
}
if (h2c->send_window == 0 && window) {
while (!ngx_queue_empty(&h2c->waiting)) {
q = ngx_queue_head(&h2c->waiting);
ngx_queue_remove(q);
stream = ngx_queue_data(q, ngx_http_v2_stream_t, queue);
stream->waiting = 0;
wev = stream->request->connection->write;
wev->active = 0;
wev->ready = 1;
if (!wev->delayed) {
ngx_post_event(wev, &ngx_posted_events);
}
}
}
h2c->send_window += window;
}
static ngx_int_t
ngx_http_v2_filter_init(ngx_conf_t *cf)
{
ngx_http_next_header_filter = ngx_http_top_header_filter;
ngx_http_top_header_filter = ngx_http_v2_header_filter;
return NGX_OK;
}
nginx-1.14.0/src/http/v2/ngx_http_v2_huff_decode.c 000644 001751 001751 00000373443 13265410474 023150 0 ustar 00mdounin mdounin 000000 000000
/*
* Copyright (C) Nginx, Inc.
* Copyright (C) Valentin V. Bartenev
*/
#include
#include
#include
typedef struct {
u_char next;
u_char emit;
u_char sym;
u_char ending;
} ngx_http_v2_huff_decode_code_t;
static ngx_inline ngx_int_t ngx_http_v2_huff_decode_bits(u_char *state,
u_char *ending, ngx_uint_t bits, u_char **dst);
static ngx_http_v2_huff_decode_code_t ngx_http_v2_huff_decode_codes[256][16] =
{
/* 0 */
{
{0x04, 0x00, 0x00, 0x00}, {0x05, 0x00, 0x00, 0x00},
{0x07, 0x00, 0x00, 0x00}, {0x08, 0x00, 0x00, 0x00},
{0x0b, 0x00, 0x00, 0x00}, {0x0c, 0x00, 0x00, 0x00},
{0x10, 0x00, 0x00, 0x00}, {0x13, 0x00, 0x00, 0x00},
{0x19, 0x00, 0x00, 0x00}, {0x1c, 0x00, 0x00, 0x00},
{0x20, 0x00, 0x00, 0x00}, {0x23, 0x00, 0x00, 0x00},
{0x2a, 0x00, 0x00, 0x00}, {0x31, 0x00, 0x00, 0x00},
{0x39, 0x00, 0x00, 0x00}, {0x40, 0x00, 0x00, 0x01}
},
{
{0x00, 0x01, 0x30, 0x01}, {0x00, 0x01, 0x31, 0x01},
{0x00, 0x01, 0x32, 0x01}, {0x00, 0x01, 0x61, 0x01},
{0x00, 0x01, 0x63, 0x01}, {0x00, 0x01, 0x65, 0x01},
{0x00, 0x01, 0x69, 0x01}, {0x00, 0x01, 0x6f, 0x01},
{0x00, 0x01, 0x73, 0x01}, {0x00, 0x01, 0x74, 0x01},
{0x0d, 0x00, 0x00, 0x00}, {0x0e, 0x00, 0x00, 0x00},
{0x11, 0x00, 0x00, 0x00}, {0x12, 0x00, 0x00, 0x00},
{0x14, 0x00, 0x00, 0x00}, {0x15, 0x00, 0x00, 0x00}
},
{
{0x01, 0x01, 0x30, 0x00}, {0x16, 0x01, 0x30, 0x01},
{0x01, 0x01, 0x31, 0x00}, {0x16, 0x01, 0x31, 0x01},
{0x01, 0x01, 0x32, 0x00}, {0x16, 0x01, 0x32, 0x01},
{0x01, 0x01, 0x61, 0x00}, {0x16, 0x01, 0x61, 0x01},
{0x01, 0x01, 0x63, 0x00}, {0x16, 0x01, 0x63, 0x01},
{0x01, 0x01, 0x65, 0x00}, {0x16, 0x01, 0x65, 0x01},
{0x01, 0x01, 0x69, 0x00}, {0x16, 0x01, 0x69, 0x01},
{0x01, 0x01, 0x6f, 0x00}, {0x16, 0x01, 0x6f, 0x01}
},
{
{0x02, 0x01, 0x30, 0x00}, {0x09, 0x01, 0x30, 0x00},
{0x17, 0x01, 0x30, 0x00}, {0x28, 0x01, 0x30, 0x01},
{0x02, 0x01, 0x31, 0x00}, {0x09, 0x01, 0x31, 0x00},
{0x17, 0x01, 0x31, 0x00}, {0x28, 0x01, 0x31, 0x01},
{0x02, 0x01, 0x32, 0x00}, {0x09, 0x01, 0x32, 0x00},
{0x17, 0x01, 0x32, 0x00}, {0x28, 0x01, 0x32, 0x01},
{0x02, 0x01, 0x61, 0x00}, {0x09, 0x01, 0x61, 0x00},
{0x17, 0x01, 0x61, 0x00}, {0x28, 0x01, 0x61, 0x01}
},
{
{0x03, 0x01, 0x30, 0x00}, {0x06, 0x01, 0x30, 0x00},
{0x0a, 0x01, 0x30, 0x00}, {0x0f, 0x01, 0x30, 0x00},
{0x18, 0x01, 0x30, 0x00}, {0x1f, 0x01, 0x30, 0x00},
{0x29, 0x01, 0x30, 0x00}, {0x38, 0x01, 0x30, 0x01},
{0x03, 0x01, 0x31, 0x00}, {0x06, 0x01, 0x31, 0x00},
{0x0a, 0x01, 0x31, 0x00}, {0x0f, 0x01, 0x31, 0x00},
{0x18, 0x01, 0x31, 0x00}, {0x1f, 0x01, 0x31, 0x00},
{0x29, 0x01, 0x31, 0x00}, {0x38, 0x01, 0x31, 0x01}
},
/* 5 */
{
{0x03, 0x01, 0x32, 0x00}, {0x06, 0x01, 0x32, 0x00},
{0x0a, 0x01, 0x32, 0x00}, {0x0f, 0x01, 0x32, 0x00},
{0x18, 0x01, 0x32, 0x00}, {0x1f, 0x01, 0x32, 0x00},
{0x29, 0x01, 0x32, 0x00}, {0x38, 0x01, 0x32, 0x01},
{0x03, 0x01, 0x61, 0x00}, {0x06, 0x01, 0x61, 0x00},
{0x0a, 0x01, 0x61, 0x00}, {0x0f, 0x01, 0x61, 0x00},
{0x18, 0x01, 0x61, 0x00}, {0x1f, 0x01, 0x61, 0x00},
{0x29, 0x01, 0x61, 0x00}, {0x38, 0x01, 0x61, 0x01}
},
{
{0x02, 0x01, 0x63, 0x00}, {0x09, 0x01, 0x63, 0x00},
{0x17, 0x01, 0x63, 0x00}, {0x28, 0x01, 0x63, 0x01},
{0x02, 0x01, 0x65, 0x00}, {0x09, 0x01, 0x65, 0x00},
{0x17, 0x01, 0x65, 0x00}, {0x28, 0x01, 0x65, 0x01},
{0x02, 0x01, 0x69, 0x00}, {0x09, 0x01, 0x69, 0x00},
{0x17, 0x01, 0x69, 0x00}, {0x28, 0x01, 0x69, 0x01},
{0x02, 0x01, 0x6f, 0x00}, {0x09, 0x01, 0x6f, 0x00},
{0x17, 0x01, 0x6f, 0x00}, {0x28, 0x01, 0x6f, 0x01}
},
{
{0x03, 0x01, 0x63, 0x00}, {0x06, 0x01, 0x63, 0x00},
{0x0a, 0x01, 0x63, 0x00}, {0x0f, 0x01, 0x63, 0x00},
{0x18, 0x01, 0x63, 0x00}, {0x1f, 0x01, 0x63, 0x00},
{0x29, 0x01, 0x63, 0x00}, {0x38, 0x01, 0x63, 0x01},
{0x03, 0x01, 0x65, 0x00}, {0x06, 0x01, 0x65, 0x00},
{0x0a, 0x01, 0x65, 0x00}, {0x0f, 0x01, 0x65, 0x00},
{0x18, 0x01, 0x65, 0x00}, {0x1f, 0x01, 0x65, 0x00},
{0x29, 0x01, 0x65, 0x00}, {0x38, 0x01, 0x65, 0x01}
},
{
{0x03, 0x01, 0x69, 0x00}, {0x06, 0x01, 0x69, 0x00},
{0x0a, 0x01, 0x69, 0x00}, {0x0f, 0x01, 0x69, 0x00},
{0x18, 0x01, 0x69, 0x00}, {0x1f, 0x01, 0x69, 0x00},
{0x29, 0x01, 0x69, 0x00}, {0x38, 0x01, 0x69, 0x01},
{0x03, 0x01, 0x6f, 0x00}, {0x06, 0x01, 0x6f, 0x00},
{0x0a, 0x01, 0x6f, 0x00}, {0x0f, 0x01, 0x6f, 0x00},
{0x18, 0x01, 0x6f, 0x00}, {0x1f, 0x01, 0x6f, 0x00},
{0x29, 0x01, 0x6f, 0x00}, {0x38, 0x01, 0x6f, 0x01}
},
{
{0x01, 0x01, 0x73, 0x00}, {0x16, 0x01, 0x73, 0x01},
{0x01, 0x01, 0x74, 0x00}, {0x16, 0x01, 0x74, 0x01},
{0x00, 0x01, 0x20, 0x01}, {0x00, 0x01, 0x25, 0x01},
{0x00, 0x01, 0x2d, 0x01}, {0x00, 0x01, 0x2e, 0x01},
{0x00, 0x01, 0x2f, 0x01}, {0x00, 0x01, 0x33, 0x01},
{0x00, 0x01, 0x34, 0x01}, {0x00, 0x01, 0x35, 0x01},
{0x00, 0x01, 0x36, 0x01}, {0x00, 0x01, 0x37, 0x01},
{0x00, 0x01, 0x38, 0x01}, {0x00, 0x01, 0x39, 0x01}
},
/* 10 */
{
{0x02, 0x01, 0x73, 0x00}, {0x09, 0x01, 0x73, 0x00},
{0x17, 0x01, 0x73, 0x00}, {0x28, 0x01, 0x73, 0x01},
{0x02, 0x01, 0x74, 0x00}, {0x09, 0x01, 0x74, 0x00},
{0x17, 0x01, 0x74, 0x00}, {0x28, 0x01, 0x74, 0x01},
{0x01, 0x01, 0x20, 0x00}, {0x16, 0x01, 0x20, 0x01},
{0x01, 0x01, 0x25, 0x00}, {0x16, 0x01, 0x25, 0x01},
{0x01, 0x01, 0x2d, 0x00}, {0x16, 0x01, 0x2d, 0x01},
{0x01, 0x01, 0x2e, 0x00}, {0x16, 0x01, 0x2e, 0x01}
},
{
{0x03, 0x01, 0x73, 0x00}, {0x06, 0x01, 0x73, 0x00},
{0x0a, 0x01, 0x73, 0x00}, {0x0f, 0x01, 0x73, 0x00},
{0x18, 0x01, 0x73, 0x00}, {0x1f, 0x01, 0x73, 0x00},
{0x29, 0x01, 0x73, 0x00}, {0x38, 0x01, 0x73, 0x01},
{0x03, 0x01, 0x74, 0x00}, {0x06, 0x01, 0x74, 0x00},
{0x0a, 0x01, 0x74, 0x00}, {0x0f, 0x01, 0x74, 0x00},
{0x18, 0x01, 0x74, 0x00}, {0x1f, 0x01, 0x74, 0x00},
{0x29, 0x01, 0x74, 0x00}, {0x38, 0x01, 0x74, 0x01}
},
{
{0x02, 0x01, 0x20, 0x00}, {0x09, 0x01, 0x20, 0x00},
{0x17, 0x01, 0x20, 0x00}, {0x28, 0x01, 0x20, 0x01},
{0x02, 0x01, 0x25, 0x00}, {0x09, 0x01, 0x25, 0x00},
{0x17, 0x01, 0x25, 0x00}, {0x28, 0x01, 0x25, 0x01},
{0x02, 0x01, 0x2d, 0x00}, {0x09, 0x01, 0x2d, 0x00},
{0x17, 0x01, 0x2d, 0x00}, {0x28, 0x01, 0x2d, 0x01},
{0x02, 0x01, 0x2e, 0x00}, {0x09, 0x01, 0x2e, 0x00},
{0x17, 0x01, 0x2e, 0x00}, {0x28, 0x01, 0x2e, 0x01}
},
{
{0x03, 0x01, 0x20, 0x00}, {0x06, 0x01, 0x20, 0x00},
{0x0a, 0x01, 0x20, 0x00}, {0x0f, 0x01, 0x20, 0x00},
{0x18, 0x01, 0x20, 0x00}, {0x1f, 0x01, 0x20, 0x00},
{0x29, 0x01, 0x20, 0x00}, {0x38, 0x01, 0x20, 0x01},
{0x03, 0x01, 0x25, 0x00}, {0x06, 0x01, 0x25, 0x00},
{0x0a, 0x01, 0x25, 0x00}, {0x0f, 0x01, 0x25, 0x00},
{0x18, 0x01, 0x25, 0x00}, {0x1f, 0x01, 0x25, 0x00},
{0x29, 0x01, 0x25, 0x00}, {0x38, 0x01, 0x25, 0x01}
},
{
{0x03, 0x01, 0x2d, 0x00}, {0x06, 0x01, 0x2d, 0x00},
{0x0a, 0x01, 0x2d, 0x00}, {0x0f, 0x01, 0x2d, 0x00},
{0x18, 0x01, 0x2d, 0x00}, {0x1f, 0x01, 0x2d, 0x00},
{0x29, 0x01, 0x2d, 0x00}, {0x38, 0x01, 0x2d, 0x01},
{0x03, 0x01, 0x2e, 0x00}, {0x06, 0x01, 0x2e, 0x00},
{0x0a, 0x01, 0x2e, 0x00}, {0x0f, 0x01, 0x2e, 0x00},
{0x18, 0x01, 0x2e, 0x00}, {0x1f, 0x01, 0x2e, 0x00},
{0x29, 0x01, 0x2e, 0x00}, {0x38, 0x01, 0x2e, 0x01}
},
/* 15 */
{
{0x01, 0x01, 0x2f, 0x00}, {0x16, 0x01, 0x2f, 0x01},
{0x01, 0x01, 0x33, 0x00}, {0x16, 0x01, 0x33, 0x01},
{0x01, 0x01, 0x34, 0x00}, {0x16, 0x01, 0x34, 0x01},
{0x01, 0x01, 0x35, 0x00}, {0x16, 0x01, 0x35, 0x01},
{0x01, 0x01, 0x36, 0x00}, {0x16, 0x01, 0x36, 0x01},
{0x01, 0x01, 0x37, 0x00}, {0x16, 0x01, 0x37, 0x01},
{0x01, 0x01, 0x38, 0x00}, {0x16, 0x01, 0x38, 0x01},
{0x01, 0x01, 0x39, 0x00}, {0x16, 0x01, 0x39, 0x01}
},
{
{0x02, 0x01, 0x2f, 0x00}, {0x09, 0x01, 0x2f, 0x00},
{0x17, 0x01, 0x2f, 0x00}, {0x28, 0x01, 0x2f, 0x01},
{0x02, 0x01, 0x33, 0x00}, {0x09, 0x01, 0x33, 0x00},
{0x17, 0x01, 0x33, 0x00}, {0x28, 0x01, 0x33, 0x01},
{0x02, 0x01, 0x34, 0x00}, {0x09, 0x01, 0x34, 0x00},
{0x17, 0x01, 0x34, 0x00}, {0x28, 0x01, 0x34, 0x01},
{0x02, 0x01, 0x35, 0x00}, {0x09, 0x01, 0x35, 0x00},
{0x17, 0x01, 0x35, 0x00}, {0x28, 0x01, 0x35, 0x01}
},
{
{0x03, 0x01, 0x2f, 0x00}, {0x06, 0x01, 0x2f, 0x00},
{0x0a, 0x01, 0x2f, 0x00}, {0x0f, 0x01, 0x2f, 0x00},
{0x18, 0x01, 0x2f, 0x00}, {0x1f, 0x01, 0x2f, 0x00},
{0x29, 0x01, 0x2f, 0x00}, {0x38, 0x01, 0x2f, 0x01},
{0x03, 0x01, 0x33, 0x00}, {0x06, 0x01, 0x33, 0x00},
{0x0a, 0x01, 0x33, 0x00}, {0x0f, 0x01, 0x33, 0x00},
{0x18, 0x01, 0x33, 0x00}, {0x1f, 0x01, 0x33, 0x00},
{0x29, 0x01, 0x33, 0x00}, {0x38, 0x01, 0x33, 0x01}
},
{
{0x03, 0x01, 0x34, 0x00}, {0x06, 0x01, 0x34, 0x00},
{0x0a, 0x01, 0x34, 0x00}, {0x0f, 0x01, 0x34, 0x00},
{0x18, 0x01, 0x34, 0x00}, {0x1f, 0x01, 0x34, 0x00},
{0x29, 0x01, 0x34, 0x00}, {0x38, 0x01, 0x34, 0x01},
{0x03, 0x01, 0x35, 0x00}, {0x06, 0x01, 0x35, 0x00},
{0x0a, 0x01, 0x35, 0x00}, {0x0f, 0x01, 0x35, 0x00},
{0x18, 0x01, 0x35, 0x00}, {0x1f, 0x01, 0x35, 0x00},
{0x29, 0x01, 0x35, 0x00}, {0x38, 0x01, 0x35, 0x01}
},
{
{0x02, 0x01, 0x36, 0x00}, {0x09, 0x01, 0x36, 0x00},
{0x17, 0x01, 0x36, 0x00}, {0x28, 0x01, 0x36, 0x01},
{0x02, 0x01, 0x37, 0x00}, {0x09, 0x01, 0x37, 0x00},
{0x17, 0x01, 0x37, 0x00}, {0x28, 0x01, 0x37, 0x01},
{0x02, 0x01, 0x38, 0x00}, {0x09, 0x01, 0x38, 0x00},
{0x17, 0x01, 0x38, 0x00}, {0x28, 0x01, 0x38, 0x01},
{0x02, 0x01, 0x39, 0x00}, {0x09, 0x01, 0x39, 0x00},
{0x17, 0x01, 0x39, 0x00}, {0x28, 0x01, 0x39, 0x01}
},
/* 20 */
{
{0x03, 0x01, 0x36, 0x00}, {0x06, 0x01, 0x36, 0x00},
{0x0a, 0x01, 0x36, 0x00}, {0x0f, 0x01, 0x36, 0x00},
{0x18, 0x01, 0x36, 0x00}, {0x1f, 0x01, 0x36, 0x00},
{0x29, 0x01, 0x36, 0x00}, {0x38, 0x01, 0x36, 0x01},
{0x03, 0x01, 0x37, 0x00}, {0x06, 0x01, 0x37, 0x00},
{0x0a, 0x01, 0x37, 0x00}, {0x0f, 0x01, 0x37, 0x00},
{0x18, 0x01, 0x37, 0x00}, {0x1f, 0x01, 0x37, 0x00},
{0x29, 0x01, 0x37, 0x00}, {0x38, 0x01, 0x37, 0x01}
},
{
{0x03, 0x01, 0x38, 0x00}, {0x06, 0x01, 0x38, 0x00},
{0x0a, 0x01, 0x38, 0x00}, {0x0f, 0x01, 0x38, 0x00},
{0x18, 0x01, 0x38, 0x00}, {0x1f, 0x01, 0x38, 0x00},
{0x29, 0x01, 0x38, 0x00}, {0x38, 0x01, 0x38, 0x01},
{0x03, 0x01, 0x39, 0x00}, {0x06, 0x01, 0x39, 0x00},
{0x0a, 0x01, 0x39, 0x00}, {0x0f, 0x01, 0x39, 0x00},
{0x18, 0x01, 0x39, 0x00}, {0x1f, 0x01, 0x39, 0x00},
{0x29, 0x01, 0x39, 0x00}, {0x38, 0x01, 0x39, 0x01}
},
{
{0x1a, 0x00, 0x00, 0x00}, {0x1b, 0x00, 0x00, 0x00},
{0x1d, 0x00, 0x00, 0x00}, {0x1e, 0x00, 0x00, 0x00},
{0x21, 0x00, 0x00, 0x00}, {0x22, 0x00, 0x00, 0x00},
{0x24, 0x00, 0x00, 0x00}, {0x25, 0x00, 0x00, 0x00},
{0x2b, 0x00, 0x00, 0x00}, {0x2e, 0x00, 0x00, 0x00},
{0x32, 0x00, 0x00, 0x00}, {0x35, 0x00, 0x00, 0x00},
{0x3a, 0x00, 0x00, 0x00}, {0x3d, 0x00, 0x00, 0x00},
{0x41, 0x00, 0x00, 0x00}, {0x44, 0x00, 0x00, 0x01}
},
{
{0x00, 0x01, 0x3d, 0x01}, {0x00, 0x01, 0x41, 0x01},
{0x00, 0x01, 0x5f, 0x01}, {0x00, 0x01, 0x62, 0x01},
{0x00, 0x01, 0x64, 0x01}, {0x00, 0x01, 0x66, 0x01},
{0x00, 0x01, 0x67, 0x01}, {0x00, 0x01, 0x68, 0x01},
{0x00, 0x01, 0x6c, 0x01}, {0x00, 0x01, 0x6d, 0x01},
{0x00, 0x01, 0x6e, 0x01}, {0x00, 0x01, 0x70, 0x01},
{0x00, 0x01, 0x72, 0x01}, {0x00, 0x01, 0x75, 0x01},
{0x26, 0x00, 0x00, 0x00}, {0x27, 0x00, 0x00, 0x00}
},
{
{0x01, 0x01, 0x3d, 0x00}, {0x16, 0x01, 0x3d, 0x01},
{0x01, 0x01, 0x41, 0x00}, {0x16, 0x01, 0x41, 0x01},
{0x01, 0x01, 0x5f, 0x00}, {0x16, 0x01, 0x5f, 0x01},
{0x01, 0x01, 0x62, 0x00}, {0x16, 0x01, 0x62, 0x01},
{0x01, 0x01, 0x64, 0x00}, {0x16, 0x01, 0x64, 0x01},
{0x01, 0x01, 0x66, 0x00}, {0x16, 0x01, 0x66, 0x01},
{0x01, 0x01, 0x67, 0x00}, {0x16, 0x01, 0x67, 0x01},
{0x01, 0x01, 0x68, 0x00}, {0x16, 0x01, 0x68, 0x01}
},
/* 25 */
{
{0x02, 0x01, 0x3d, 0x00}, {0x09, 0x01, 0x3d, 0x00},
{0x17, 0x01, 0x3d, 0x00}, {0x28, 0x01, 0x3d, 0x01},
{0x02, 0x01, 0x41, 0x00}, {0x09, 0x01, 0x41, 0x00},
{0x17, 0x01, 0x41, 0x00}, {0x28, 0x01, 0x41, 0x01},
{0x02, 0x01, 0x5f, 0x00}, {0x09, 0x01, 0x5f, 0x00},
{0x17, 0x01, 0x5f, 0x00}, {0x28, 0x01, 0x5f, 0x01},
{0x02, 0x01, 0x62, 0x00}, {0x09, 0x01, 0x62, 0x00},
{0x17, 0x01, 0x62, 0x00}, {0x28, 0x01, 0x62, 0x01}
},
{
{0x03, 0x01, 0x3d, 0x00}, {0x06, 0x01, 0x3d, 0x00},
{0x0a, 0x01, 0x3d, 0x00}, {0x0f, 0x01, 0x3d, 0x00},
{0x18, 0x01, 0x3d, 0x00}, {0x1f, 0x01, 0x3d, 0x00},
{0x29, 0x01, 0x3d, 0x00}, {0x38, 0x01, 0x3d, 0x01},
{0x03, 0x01, 0x41, 0x00}, {0x06, 0x01, 0x41, 0x00},
{0x0a, 0x01, 0x41, 0x00}, {0x0f, 0x01, 0x41, 0x00},
{0x18, 0x01, 0x41, 0x00}, {0x1f, 0x01, 0x41, 0x00},
{0x29, 0x01, 0x41, 0x00}, {0x38, 0x01, 0x41, 0x01}
},
{
{0x03, 0x01, 0x5f, 0x00}, {0x06, 0x01, 0x5f, 0x00},
{0x0a, 0x01, 0x5f, 0x00}, {0x0f, 0x01, 0x5f, 0x00},
{0x18, 0x01, 0x5f, 0x00}, {0x1f, 0x01, 0x5f, 0x00},
{0x29, 0x01, 0x5f, 0x00}, {0x38, 0x01, 0x5f, 0x01},
{0x03, 0x01, 0x62, 0x00}, {0x06, 0x01, 0x62, 0x00},
{0x0a, 0x01, 0x62, 0x00}, {0x0f, 0x01, 0x62, 0x00},
{0x18, 0x01, 0x62, 0x00}, {0x1f, 0x01, 0x62, 0x00},
{0x29, 0x01, 0x62, 0x00}, {0x38, 0x01, 0x62, 0x01}
},
{
{0x02, 0x01, 0x64, 0x00}, {0x09, 0x01, 0x64, 0x00},
{0x17, 0x01, 0x64, 0x00}, {0x28, 0x01, 0x64, 0x01},
{0x02, 0x01, 0x66, 0x00}, {0x09, 0x01, 0x66, 0x00},
{0x17, 0x01, 0x66, 0x00}, {0x28, 0x01, 0x66, 0x01},
{0x02, 0x01, 0x67, 0x00}, {0x09, 0x01, 0x67, 0x00},
{0x17, 0x01, 0x67, 0x00}, {0x28, 0x01, 0x67, 0x01},
{0x02, 0x01, 0x68, 0x00}, {0x09, 0x01, 0x68, 0x00},
{0x17, 0x01, 0x68, 0x00}, {0x28, 0x01, 0x68, 0x01}
},
{
{0x03, 0x01, 0x64, 0x00}, {0x06, 0x01, 0x64, 0x00},
{0x0a, 0x01, 0x64, 0x00}, {0x0f, 0x01, 0x64, 0x00},
{0x18, 0x01, 0x64, 0x00}, {0x1f, 0x01, 0x64, 0x00},
{0x29, 0x01, 0x64, 0x00}, {0x38, 0x01, 0x64, 0x01},
{0x03, 0x01, 0x66, 0x00}, {0x06, 0x01, 0x66, 0x00},
{0x0a, 0x01, 0x66, 0x00}, {0x0f, 0x01, 0x66, 0x00},
{0x18, 0x01, 0x66, 0x00}, {0x1f, 0x01, 0x66, 0x00},
{0x29, 0x01, 0x66, 0x00}, {0x38, 0x01, 0x66, 0x01}
},
/* 30 */
{
{0x03, 0x01, 0x67, 0x00}, {0x06, 0x01, 0x67, 0x00},
{0x0a, 0x01, 0x67, 0x00}, {0x0f, 0x01, 0x67, 0x00},
{0x18, 0x01, 0x67, 0x00}, {0x1f, 0x01, 0x67, 0x00},
{0x29, 0x01, 0x67, 0x00}, {0x38, 0x01, 0x67, 0x01},
{0x03, 0x01, 0x68, 0x00}, {0x06, 0x01, 0x68, 0x00},
{0x0a, 0x01, 0x68, 0x00}, {0x0f, 0x01, 0x68, 0x00},
{0x18, 0x01, 0x68, 0x00}, {0x1f, 0x01, 0x68, 0x00},
{0x29, 0x01, 0x68, 0x00}, {0x38, 0x01, 0x68, 0x01}
},
{
{0x01, 0x01, 0x6c, 0x00}, {0x16, 0x01, 0x6c, 0x01},
{0x01, 0x01, 0x6d, 0x00}, {0x16, 0x01, 0x6d, 0x01},
{0x01, 0x01, 0x6e, 0x00}, {0x16, 0x01, 0x6e, 0x01},
{0x01, 0x01, 0x70, 0x00}, {0x16, 0x01, 0x70, 0x01},
{0x01, 0x01, 0x72, 0x00}, {0x16, 0x01, 0x72, 0x01},
{0x01, 0x01, 0x75, 0x00}, {0x16, 0x01, 0x75, 0x01},
{0x00, 0x01, 0x3a, 0x01}, {0x00, 0x01, 0x42, 0x01},
{0x00, 0x01, 0x43, 0x01}, {0x00, 0x01, 0x44, 0x01}
},
{
{0x02, 0x01, 0x6c, 0x00}, {0x09, 0x01, 0x6c, 0x00},
{0x17, 0x01, 0x6c, 0x00}, {0x28, 0x01, 0x6c, 0x01},
{0x02, 0x01, 0x6d, 0x00}, {0x09, 0x01, 0x6d, 0x00},
{0x17, 0x01, 0x6d, 0x00}, {0x28, 0x01, 0x6d, 0x01},
{0x02, 0x01, 0x6e, 0x00}, {0x09, 0x01, 0x6e, 0x00},
{0x17, 0x01, 0x6e, 0x00}, {0x28, 0x01, 0x6e, 0x01},
{0x02, 0x01, 0x70, 0x00}, {0x09, 0x01, 0x70, 0x00},
{0x17, 0x01, 0x70, 0x00}, {0x28, 0x01, 0x70, 0x01}
},
{
{0x03, 0x01, 0x6c, 0x00}, {0x06, 0x01, 0x6c, 0x00},
{0x0a, 0x01, 0x6c, 0x00}, {0x0f, 0x01, 0x6c, 0x00},
{0x18, 0x01, 0x6c, 0x00}, {0x1f, 0x01, 0x6c, 0x00},
{0x29, 0x01, 0x6c, 0x00}, {0x38, 0x01, 0x6c, 0x01},
{0x03, 0x01, 0x6d, 0x00}, {0x06, 0x01, 0x6d, 0x00},
{0x0a, 0x01, 0x6d, 0x00}, {0x0f, 0x01, 0x6d, 0x00},
{0x18, 0x01, 0x6d, 0x00}, {0x1f, 0x01, 0x6d, 0x00},
{0x29, 0x01, 0x6d, 0x00}, {0x38, 0x01, 0x6d, 0x01}
},
{
{0x03, 0x01, 0x6e, 0x00}, {0x06, 0x01, 0x6e, 0x00},
{0x0a, 0x01, 0x6e, 0x00}, {0x0f, 0x01, 0x6e, 0x00},
{0x18, 0x01, 0x6e, 0x00}, {0x1f, 0x01, 0x6e, 0x00},
{0x29, 0x01, 0x6e, 0x00}, {0x38, 0x01, 0x6e, 0x01},
{0x03, 0x01, 0x70, 0x00}, {0x06, 0x01, 0x70, 0x00},
{0x0a, 0x01, 0x70, 0x00}, {0x0f, 0x01, 0x70, 0x00},
{0x18, 0x01, 0x70, 0x00}, {0x1f, 0x01, 0x70, 0x00},
{0x29, 0x01, 0x70, 0x00}, {0x38, 0x01, 0x70, 0x01}
},
/* 35 */
{
{0x02, 0x01, 0x72, 0x00}, {0x09, 0x01, 0x72, 0x00},
{0x17, 0x01, 0x72, 0x00}, {0x28, 0x01, 0x72, 0x01},
{0x02, 0x01, 0x75, 0x00}, {0x09, 0x01, 0x75, 0x00},
{0x17, 0x01, 0x75, 0x00}, {0x28, 0x01, 0x75, 0x01},
{0x01, 0x01, 0x3a, 0x00}, {0x16, 0x01, 0x3a, 0x01},
{0x01, 0x01, 0x42, 0x00}, {0x16, 0x01, 0x42, 0x01},
{0x01, 0x01, 0x43, 0x00}, {0x16, 0x01, 0x43, 0x01},
{0x01, 0x01, 0x44, 0x00}, {0x16, 0x01, 0x44, 0x01}
},
{
{0x03, 0x01, 0x72, 0x00}, {0x06, 0x01, 0x72, 0x00},
{0x0a, 0x01, 0x72, 0x00}, {0x0f, 0x01, 0x72, 0x00},
{0x18, 0x01, 0x72, 0x00}, {0x1f, 0x01, 0x72, 0x00},
{0x29, 0x01, 0x72, 0x00}, {0x38, 0x01, 0x72, 0x01},
{0x03, 0x01, 0x75, 0x00}, {0x06, 0x01, 0x75, 0x00},
{0x0a, 0x01, 0x75, 0x00}, {0x0f, 0x01, 0x75, 0x00},
{0x18, 0x01, 0x75, 0x00}, {0x1f, 0x01, 0x75, 0x00},
{0x29, 0x01, 0x75, 0x00}, {0x38, 0x01, 0x75, 0x01}
},
{
{0x02, 0x01, 0x3a, 0x00}, {0x09, 0x01, 0x3a, 0x00},
{0x17, 0x01, 0x3a, 0x00}, {0x28, 0x01, 0x3a, 0x01},
{0x02, 0x01, 0x42, 0x00}, {0x09, 0x01, 0x42, 0x00},
{0x17, 0x01, 0x42, 0x00}, {0x28, 0x01, 0x42, 0x01},
{0x02, 0x01, 0x43, 0x00}, {0x09, 0x01, 0x43, 0x00},
{0x17, 0x01, 0x43, 0x00}, {0x28, 0x01, 0x43, 0x01},
{0x02, 0x01, 0x44, 0x00}, {0x09, 0x01, 0x44, 0x00},
{0x17, 0x01, 0x44, 0x00}, {0x28, 0x01, 0x44, 0x01}
},
{
{0x03, 0x01, 0x3a, 0x00}, {0x06, 0x01, 0x3a, 0x00},
{0x0a, 0x01, 0x3a, 0x00}, {0x0f, 0x01, 0x3a, 0x00},
{0x18, 0x01, 0x3a, 0x00}, {0x1f, 0x01, 0x3a, 0x00},
{0x29, 0x01, 0x3a, 0x00}, {0x38, 0x01, 0x3a, 0x01},
{0x03, 0x01, 0x42, 0x00}, {0x06, 0x01, 0x42, 0x00},
{0x0a, 0x01, 0x42, 0x00}, {0x0f, 0x01, 0x42, 0x00},
{0x18, 0x01, 0x42, 0x00}, {0x1f, 0x01, 0x42, 0x00},
{0x29, 0x01, 0x42, 0x00}, {0x38, 0x01, 0x42, 0x01}
},
{
{0x03, 0x01, 0x43, 0x00}, {0x06, 0x01, 0x43, 0x00},
{0x0a, 0x01, 0x43, 0x00}, {0x0f, 0x01, 0x43, 0x00},
{0x18, 0x01, 0x43, 0x00}, {0x1f, 0x01, 0x43, 0x00},
{0x29, 0x01, 0x43, 0x00}, {0x38, 0x01, 0x43, 0x01},
{0x03, 0x01, 0x44, 0x00}, {0x06, 0x01, 0x44, 0x00},
{0x0a, 0x01, 0x44, 0x00}, {0x0f, 0x01, 0x44, 0x00},
{0x18, 0x01, 0x44, 0x00}, {0x1f, 0x01, 0x44, 0x00},
{0x29, 0x01, 0x44, 0x00}, {0x38, 0x01, 0x44, 0x01}
},
/* 40 */
{
{0x2c, 0x00, 0x00, 0x00}, {0x2d, 0x00, 0x00, 0x00},
{0x2f, 0x00, 0x00, 0x00}, {0x30, 0x00, 0x00, 0x00},
{0x33, 0x00, 0x00, 0x00}, {0x34, 0x00, 0x00, 0x00},
{0x36, 0x00, 0x00, 0x00}, {0x37, 0x00, 0x00, 0x00},
{0x3b, 0x00, 0x00, 0x00}, {0x3c, 0x00, 0x00, 0x00},
{0x3e, 0x00, 0x00, 0x00}, {0x3f, 0x00, 0x00, 0x00},
{0x42, 0x00, 0x00, 0x00}, {0x43, 0x00, 0x00, 0x00},
{0x45, 0x00, 0x00, 0x00}, {0x48, 0x00, 0x00, 0x01}
},
{
{0x00, 0x01, 0x45, 0x01}, {0x00, 0x01, 0x46, 0x01},
{0x00, 0x01, 0x47, 0x01}, {0x00, 0x01, 0x48, 0x01},
{0x00, 0x01, 0x49, 0x01}, {0x00, 0x01, 0x4a, 0x01},
{0x00, 0x01, 0x4b, 0x01}, {0x00, 0x01, 0x4c, 0x01},
{0x00, 0x01, 0x4d, 0x01}, {0x00, 0x01, 0x4e, 0x01},
{0x00, 0x01, 0x4f, 0x01}, {0x00, 0x01, 0x50, 0x01},
{0x00, 0x01, 0x51, 0x01}, {0x00, 0x01, 0x52, 0x01},
{0x00, 0x01, 0x53, 0x01}, {0x00, 0x01, 0x54, 0x01}
},
{
{0x01, 0x01, 0x45, 0x00}, {0x16, 0x01, 0x45, 0x01},
{0x01, 0x01, 0x46, 0x00}, {0x16, 0x01, 0x46, 0x01},
{0x01, 0x01, 0x47, 0x00}, {0x16, 0x01, 0x47, 0x01},
{0x01, 0x01, 0x48, 0x00}, {0x16, 0x01, 0x48, 0x01},
{0x01, 0x01, 0x49, 0x00}, {0x16, 0x01, 0x49, 0x01},
{0x01, 0x01, 0x4a, 0x00}, {0x16, 0x01, 0x4a, 0x01},
{0x01, 0x01, 0x4b, 0x00}, {0x16, 0x01, 0x4b, 0x01},
{0x01, 0x01, 0x4c, 0x00}, {0x16, 0x01, 0x4c, 0x01}
},
{
{0x02, 0x01, 0x45, 0x00}, {0x09, 0x01, 0x45, 0x00},
{0x17, 0x01, 0x45, 0x00}, {0x28, 0x01, 0x45, 0x01},
{0x02, 0x01, 0x46, 0x00}, {0x09, 0x01, 0x46, 0x00},
{0x17, 0x01, 0x46, 0x00}, {0x28, 0x01, 0x46, 0x01},
{0x02, 0x01, 0x47, 0x00}, {0x09, 0x01, 0x47, 0x00},
{0x17, 0x01, 0x47, 0x00}, {0x28, 0x01, 0x47, 0x01},
{0x02, 0x01, 0x48, 0x00}, {0x09, 0x01, 0x48, 0x00},
{0x17, 0x01, 0x48, 0x00}, {0x28, 0x01, 0x48, 0x01}
},
{
{0x03, 0x01, 0x45, 0x00}, {0x06, 0x01, 0x45, 0x00},
{0x0a, 0x01, 0x45, 0x00}, {0x0f, 0x01, 0x45, 0x00},
{0x18, 0x01, 0x45, 0x00}, {0x1f, 0x01, 0x45, 0x00},
{0x29, 0x01, 0x45, 0x00}, {0x38, 0x01, 0x45, 0x01},
{0x03, 0x01, 0x46, 0x00}, {0x06, 0x01, 0x46, 0x00},
{0x0a, 0x01, 0x46, 0x00}, {0x0f, 0x01, 0x46, 0x00},
{0x18, 0x01, 0x46, 0x00}, {0x1f, 0x01, 0x46, 0x00},
{0x29, 0x01, 0x46, 0x00}, {0x38, 0x01, 0x46, 0x01}
},
/* 45 */
{
{0x03, 0x01, 0x47, 0x00}, {0x06, 0x01, 0x47, 0x00},
{0x0a, 0x01, 0x47, 0x00}, {0x0f, 0x01, 0x47, 0x00},
{0x18, 0x01, 0x47, 0x00}, {0x1f, 0x01, 0x47, 0x00},
{0x29, 0x01, 0x47, 0x00}, {0x38, 0x01, 0x47, 0x01},
{0x03, 0x01, 0x48, 0x00}, {0x06, 0x01, 0x48, 0x00},
{0x0a, 0x01, 0x48, 0x00}, {0x0f, 0x01, 0x48, 0x00},
{0x18, 0x01, 0x48, 0x00}, {0x1f, 0x01, 0x48, 0x00},
{0x29, 0x01, 0x48, 0x00}, {0x38, 0x01, 0x48, 0x01}
},
{
{0x02, 0x01, 0x49, 0x00}, {0x09, 0x01, 0x49, 0x00},
{0x17, 0x01, 0x49, 0x00}, {0x28, 0x01, 0x49, 0x01},
{0x02, 0x01, 0x4a, 0x00}, {0x09, 0x01, 0x4a, 0x00},
{0x17, 0x01, 0x4a, 0x00}, {0x28, 0x01, 0x4a, 0x01},
{0x02, 0x01, 0x4b, 0x00}, {0x09, 0x01, 0x4b, 0x00},
{0x17, 0x01, 0x4b, 0x00}, {0x28, 0x01, 0x4b, 0x01},
{0x02, 0x01, 0x4c, 0x00}, {0x09, 0x01, 0x4c, 0x00},
{0x17, 0x01, 0x4c, 0x00}, {0x28, 0x01, 0x4c, 0x01}
},
{
{0x03, 0x01, 0x49, 0x00}, {0x06, 0x01, 0x49, 0x00},
{0x0a, 0x01, 0x49, 0x00}, {0x0f, 0x01, 0x49, 0x00},
{0x18, 0x01, 0x49, 0x00}, {0x1f, 0x01, 0x49, 0x00},
{0x29, 0x01, 0x49, 0x00}, {0x38, 0x01, 0x49, 0x01},
{0x03, 0x01, 0x4a, 0x00}, {0x06, 0x01, 0x4a, 0x00},
{0x0a, 0x01, 0x4a, 0x00}, {0x0f, 0x01, 0x4a, 0x00},
{0x18, 0x01, 0x4a, 0x00}, {0x1f, 0x01, 0x4a, 0x00},
{0x29, 0x01, 0x4a, 0x00}, {0x38, 0x01, 0x4a, 0x01}
},
{
{0x03, 0x01, 0x4b, 0x00}, {0x06, 0x01, 0x4b, 0x00},
{0x0a, 0x01, 0x4b, 0x00}, {0x0f, 0x01, 0x4b, 0x00},
{0x18, 0x01, 0x4b, 0x00}, {0x1f, 0x01, 0x4b, 0x00},
{0x29, 0x01, 0x4b, 0x00}, {0x38, 0x01, 0x4b, 0x01},
{0x03, 0x01, 0x4c, 0x00}, {0x06, 0x01, 0x4c, 0x00},
{0x0a, 0x01, 0x4c, 0x00}, {0x0f, 0x01, 0x4c, 0x00},
{0x18, 0x01, 0x4c, 0x00}, {0x1f, 0x01, 0x4c, 0x00},
{0x29, 0x01, 0x4c, 0x00}, {0x38, 0x01, 0x4c, 0x01}
},
{
{0x01, 0x01, 0x4d, 0x00}, {0x16, 0x01, 0x4d, 0x01},
{0x01, 0x01, 0x4e, 0x00}, {0x16, 0x01, 0x4e, 0x01},
{0x01, 0x01, 0x4f, 0x00}, {0x16, 0x01, 0x4f, 0x01},
{0x01, 0x01, 0x50, 0x00}, {0x16, 0x01, 0x50, 0x01},
{0x01, 0x01, 0x51, 0x00}, {0x16, 0x01, 0x51, 0x01},
{0x01, 0x01, 0x52, 0x00}, {0x16, 0x01, 0x52, 0x01},
{0x01, 0x01, 0x53, 0x00}, {0x16, 0x01, 0x53, 0x01},
{0x01, 0x01, 0x54, 0x00}, {0x16, 0x01, 0x54, 0x01}
},
/* 50 */
{
{0x02, 0x01, 0x4d, 0x00}, {0x09, 0x01, 0x4d, 0x00},
{0x17, 0x01, 0x4d, 0x00}, {0x28, 0x01, 0x4d, 0x01},
{0x02, 0x01, 0x4e, 0x00}, {0x09, 0x01, 0x4e, 0x00},
{0x17, 0x01, 0x4e, 0x00}, {0x28, 0x01, 0x4e, 0x01},
{0x02, 0x01, 0x4f, 0x00}, {0x09, 0x01, 0x4f, 0x00},
{0x17, 0x01, 0x4f, 0x00}, {0x28, 0x01, 0x4f, 0x01},
{0x02, 0x01, 0x50, 0x00}, {0x09, 0x01, 0x50, 0x00},
{0x17, 0x01, 0x50, 0x00}, {0x28, 0x01, 0x50, 0x01}
},
{
{0x03, 0x01, 0x4d, 0x00}, {0x06, 0x01, 0x4d, 0x00},
{0x0a, 0x01, 0x4d, 0x00}, {0x0f, 0x01, 0x4d, 0x00},
{0x18, 0x01, 0x4d, 0x00}, {0x1f, 0x01, 0x4d, 0x00},
{0x29, 0x01, 0x4d, 0x00}, {0x38, 0x01, 0x4d, 0x01},
{0x03, 0x01, 0x4e, 0x00}, {0x06, 0x01, 0x4e, 0x00},
{0x0a, 0x01, 0x4e, 0x00}, {0x0f, 0x01, 0x4e, 0x00},
{0x18, 0x01, 0x4e, 0x00}, {0x1f, 0x01, 0x4e, 0x00},
{0x29, 0x01, 0x4e, 0x00}, {0x38, 0x01, 0x4e, 0x01}
},
{
{0x03, 0x01, 0x4f, 0x00}, {0x06, 0x01, 0x4f, 0x00},
{0x0a, 0x01, 0x4f, 0x00}, {0x0f, 0x01, 0x4f, 0x00},
{0x18, 0x01, 0x4f, 0x00}, {0x1f, 0x01, 0x4f, 0x00},
{0x29, 0x01, 0x4f, 0x00}, {0x38, 0x01, 0x4f, 0x01},
{0x03, 0x01, 0x50, 0x00}, {0x06, 0x01, 0x50, 0x00},
{0x0a, 0x01, 0x50, 0x00}, {0x0f, 0x01, 0x50, 0x00},
{0x18, 0x01, 0x50, 0x00}, {0x1f, 0x01, 0x50, 0x00},
{0x29, 0x01, 0x50, 0x00}, {0x38, 0x01, 0x50, 0x01}
},
{
{0x02, 0x01, 0x51, 0x00}, {0x09, 0x01, 0x51, 0x00},
{0x17, 0x01, 0x51, 0x00}, {0x28, 0x01, 0x51, 0x01},
{0x02, 0x01, 0x52, 0x00}, {0x09, 0x01, 0x52, 0x00},
{0x17, 0x01, 0x52, 0x00}, {0x28, 0x01, 0x52, 0x01},
{0x02, 0x01, 0x53, 0x00}, {0x09, 0x01, 0x53, 0x00},
{0x17, 0x01, 0x53, 0x00}, {0x28, 0x01, 0x53, 0x01},
{0x02, 0x01, 0x54, 0x00}, {0x09, 0x01, 0x54, 0x00},
{0x17, 0x01, 0x54, 0x00}, {0x28, 0x01, 0x54, 0x01}
},
{
{0x03, 0x01, 0x51, 0x00}, {0x06, 0x01, 0x51, 0x00},
{0x0a, 0x01, 0x51, 0x00}, {0x0f, 0x01, 0x51, 0x00},
{0x18, 0x01, 0x51, 0x00}, {0x1f, 0x01, 0x51, 0x00},
{0x29, 0x01, 0x51, 0x00}, {0x38, 0x01, 0x51, 0x01},
{0x03, 0x01, 0x52, 0x00}, {0x06, 0x01, 0x52, 0x00},
{0x0a, 0x01, 0x52, 0x00}, {0x0f, 0x01, 0x52, 0x00},
{0x18, 0x01, 0x52, 0x00}, {0x1f, 0x01, 0x52, 0x00},
{0x29, 0x01, 0x52, 0x00}, {0x38, 0x01, 0x52, 0x01}
},
/* 55 */
{
{0x03, 0x01, 0x53, 0x00}, {0x06, 0x01, 0x53, 0x00},
{0x0a, 0x01, 0x53, 0x00}, {0x0f, 0x01, 0x53, 0x00},
{0x18, 0x01, 0x53, 0x00}, {0x1f, 0x01, 0x53, 0x00},
{0x29, 0x01, 0x53, 0x00}, {0x38, 0x01, 0x53, 0x01},
{0x03, 0x01, 0x54, 0x00}, {0x06, 0x01, 0x54, 0x00},
{0x0a, 0x01, 0x54, 0x00}, {0x0f, 0x01, 0x54, 0x00},
{0x18, 0x01, 0x54, 0x00}, {0x1f, 0x01, 0x54, 0x00},
{0x29, 0x01, 0x54, 0x00}, {0x38, 0x01, 0x54, 0x01}
},
{
{0x00, 0x01, 0x55, 0x01}, {0x00, 0x01, 0x56, 0x01},
{0x00, 0x01, 0x57, 0x01}, {0x00, 0x01, 0x59, 0x01},
{0x00, 0x01, 0x6a, 0x01}, {0x00, 0x01, 0x6b, 0x01},
{0x00, 0x01, 0x71, 0x01}, {0x00, 0x01, 0x76, 0x01},
{0x00, 0x01, 0x77, 0x01}, {0x00, 0x01, 0x78, 0x01},
{0x00, 0x01, 0x79, 0x01}, {0x00, 0x01, 0x7a, 0x01},
{0x46, 0x00, 0x00, 0x00}, {0x47, 0x00, 0x00, 0x00},
{0x49, 0x00, 0x00, 0x00}, {0x4a, 0x00, 0x00, 0x01}
},
{
{0x01, 0x01, 0x55, 0x00}, {0x16, 0x01, 0x55, 0x01},
{0x01, 0x01, 0x56, 0x00}, {0x16, 0x01, 0x56, 0x01},
{0x01, 0x01, 0x57, 0x00}, {0x16, 0x01, 0x57, 0x01},
{0x01, 0x01, 0x59, 0x00}, {0x16, 0x01, 0x59, 0x01},
{0x01, 0x01, 0x6a, 0x00}, {0x16, 0x01, 0x6a, 0x01},
{0x01, 0x01, 0x6b, 0x00}, {0x16, 0x01, 0x6b, 0x01},
{0x01, 0x01, 0x71, 0x00}, {0x16, 0x01, 0x71, 0x01},
{0x01, 0x01, 0x76, 0x00}, {0x16, 0x01, 0x76, 0x01}
},
{
{0x02, 0x01, 0x55, 0x00}, {0x09, 0x01, 0x55, 0x00},
{0x17, 0x01, 0x55, 0x00}, {0x28, 0x01, 0x55, 0x01},
{0x02, 0x01, 0x56, 0x00}, {0x09, 0x01, 0x56, 0x00},
{0x17, 0x01, 0x56, 0x00}, {0x28, 0x01, 0x56, 0x01},
{0x02, 0x01, 0x57, 0x00}, {0x09, 0x01, 0x57, 0x00},
{0x17, 0x01, 0x57, 0x00}, {0x28, 0x01, 0x57, 0x01},
{0x02, 0x01, 0x59, 0x00}, {0x09, 0x01, 0x59, 0x00},
{0x17, 0x01, 0x59, 0x00}, {0x28, 0x01, 0x59, 0x01}
},
{
{0x03, 0x01, 0x55, 0x00}, {0x06, 0x01, 0x55, 0x00},
{0x0a, 0x01, 0x55, 0x00}, {0x0f, 0x01, 0x55, 0x00},
{0x18, 0x01, 0x55, 0x00}, {0x1f, 0x01, 0x55, 0x00},
{0x29, 0x01, 0x55, 0x00}, {0x38, 0x01, 0x55, 0x01},
{0x03, 0x01, 0x56, 0x00}, {0x06, 0x01, 0x56, 0x00},
{0x0a, 0x01, 0x56, 0x00}, {0x0f, 0x01, 0x56, 0x00},
{0x18, 0x01, 0x56, 0x00}, {0x1f, 0x01, 0x56, 0x00},
{0x29, 0x01, 0x56, 0x00}, {0x38, 0x01, 0x56, 0x01}
},
/* 60 */
{
{0x03, 0x01, 0x57, 0x00}, {0x06, 0x01, 0x57, 0x00},
{0x0a, 0x01, 0x57, 0x00}, {0x0f, 0x01, 0x57, 0x00},
{0x18, 0x01, 0x57, 0x00}, {0x1f, 0x01, 0x57, 0x00},
{0x29, 0x01, 0x57, 0x00}, {0x38, 0x01, 0x57, 0x01},
{0x03, 0x01, 0x59, 0x00}, {0x06, 0x01, 0x59, 0x00},
{0x0a, 0x01, 0x59, 0x00}, {0x0f, 0x01, 0x59, 0x00},
{0x18, 0x01, 0x59, 0x00}, {0x1f, 0x01, 0x59, 0x00},
{0x29, 0x01, 0x59, 0x00}, {0x38, 0x01, 0x59, 0x01}
},
{
{0x02, 0x01, 0x6a, 0x00}, {0x09, 0x01, 0x6a, 0x00},
{0x17, 0x01, 0x6a, 0x00}, {0x28, 0x01, 0x6a, 0x01},
{0x02, 0x01, 0x6b, 0x00}, {0x09, 0x01, 0x6b, 0x00},
{0x17, 0x01, 0x6b, 0x00}, {0x28, 0x01, 0x6b, 0x01},
{0x02, 0x01, 0x71, 0x00}, {0x09, 0x01, 0x71, 0x00},
{0x17, 0x01, 0x71, 0x00}, {0x28, 0x01, 0x71, 0x01},
{0x02, 0x01, 0x76, 0x00}, {0x09, 0x01, 0x76, 0x00},
{0x17, 0x01, 0x76, 0x00}, {0x28, 0x01, 0x76, 0x01}
},
{
{0x03, 0x01, 0x6a, 0x00}, {0x06, 0x01, 0x6a, 0x00},
{0x0a, 0x01, 0x6a, 0x00}, {0x0f, 0x01, 0x6a, 0x00},
{0x18, 0x01, 0x6a, 0x00}, {0x1f, 0x01, 0x6a, 0x00},
{0x29, 0x01, 0x6a, 0x00}, {0x38, 0x01, 0x6a, 0x01},
{0x03, 0x01, 0x6b, 0x00}, {0x06, 0x01, 0x6b, 0x00},
{0x0a, 0x01, 0x6b, 0x00}, {0x0f, 0x01, 0x6b, 0x00},
{0x18, 0x01, 0x6b, 0x00}, {0x1f, 0x01, 0x6b, 0x00},
{0x29, 0x01, 0x6b, 0x00}, {0x38, 0x01, 0x6b, 0x01}
},
{
{0x03, 0x01, 0x71, 0x00}, {0x06, 0x01, 0x71, 0x00},
{0x0a, 0x01, 0x71, 0x00}, {0x0f, 0x01, 0x71, 0x00},
{0x18, 0x01, 0x71, 0x00}, {0x1f, 0x01, 0x71, 0x00},
{0x29, 0x01, 0x71, 0x00}, {0x38, 0x01, 0x71, 0x01},
{0x03, 0x01, 0x76, 0x00}, {0x06, 0x01, 0x76, 0x00},
{0x0a, 0x01, 0x76, 0x00}, {0x0f, 0x01, 0x76, 0x00},
{0x18, 0x01, 0x76, 0x00}, {0x1f, 0x01, 0x76, 0x00},
{0x29, 0x01, 0x76, 0x00}, {0x38, 0x01, 0x76, 0x01}
},
{
{0x01, 0x01, 0x77, 0x00}, {0x16, 0x01, 0x77, 0x01},
{0x01, 0x01, 0x78, 0x00}, {0x16, 0x01, 0x78, 0x01},
{0x01, 0x01, 0x79, 0x00}, {0x16, 0x01, 0x79, 0x01},
{0x01, 0x01, 0x7a, 0x00}, {0x16, 0x01, 0x7a, 0x01},
{0x00, 0x01, 0x26, 0x01}, {0x00, 0x01, 0x2a, 0x01},
{0x00, 0x01, 0x2c, 0x01}, {0x00, 0x01, 0x3b, 0x01},
{0x00, 0x01, 0x58, 0x01}, {0x00, 0x01, 0x5a, 0x01},
{0x4b, 0x00, 0x00, 0x00}, {0x4e, 0x00, 0x00, 0x01}
},
/* 65 */
{
{0x02, 0x01, 0x77, 0x00}, {0x09, 0x01, 0x77, 0x00},
{0x17, 0x01, 0x77, 0x00}, {0x28, 0x01, 0x77, 0x01},
{0x02, 0x01, 0x78, 0x00}, {0x09, 0x01, 0x78, 0x00},
{0x17, 0x01, 0x78, 0x00}, {0x28, 0x01, 0x78, 0x01},
{0x02, 0x01, 0x79, 0x00}, {0x09, 0x01, 0x79, 0x00},
{0x17, 0x01, 0x79, 0x00}, {0x28, 0x01, 0x79, 0x01},
{0x02, 0x01, 0x7a, 0x00}, {0x09, 0x01, 0x7a, 0x00},
{0x17, 0x01, 0x7a, 0x00}, {0x28, 0x01, 0x7a, 0x01}
},
{
{0x03, 0x01, 0x77, 0x00}, {0x06, 0x01, 0x77, 0x00},
{0x0a, 0x01, 0x77, 0x00}, {0x0f, 0x01, 0x77, 0x00},
{0x18, 0x01, 0x77, 0x00}, {0x1f, 0x01, 0x77, 0x00},
{0x29, 0x01, 0x77, 0x00}, {0x38, 0x01, 0x77, 0x01},
{0x03, 0x01, 0x78, 0x00}, {0x06, 0x01, 0x78, 0x00},
{0x0a, 0x01, 0x78, 0x00}, {0x0f, 0x01, 0x78, 0x00},
{0x18, 0x01, 0x78, 0x00}, {0x1f, 0x01, 0x78, 0x00},
{0x29, 0x01, 0x78, 0x00}, {0x38, 0x01, 0x78, 0x01}
},
{
{0x03, 0x01, 0x79, 0x00}, {0x06, 0x01, 0x79, 0x00},
{0x0a, 0x01, 0x79, 0x00}, {0x0f, 0x01, 0x79, 0x00},
{0x18, 0x01, 0x79, 0x00}, {0x1f, 0x01, 0x79, 0x00},
{0x29, 0x01, 0x79, 0x00}, {0x38, 0x01, 0x79, 0x01},
{0x03, 0x01, 0x7a, 0x00}, {0x06, 0x01, 0x7a, 0x00},
{0x0a, 0x01, 0x7a, 0x00}, {0x0f, 0x01, 0x7a, 0x00},
{0x18, 0x01, 0x7a, 0x00}, {0x1f, 0x01, 0x7a, 0x00},
{0x29, 0x01, 0x7a, 0x00}, {0x38, 0x01, 0x7a, 0x01}
},
{
{0x01, 0x01, 0x26, 0x00}, {0x16, 0x01, 0x26, 0x01},
{0x01, 0x01, 0x2a, 0x00}, {0x16, 0x01, 0x2a, 0x01},
{0x01, 0x01, 0x2c, 0x00}, {0x16, 0x01, 0x2c, 0x01},
{0x01, 0x01, 0x3b, 0x00}, {0x16, 0x01, 0x3b, 0x01},
{0x01, 0x01, 0x58, 0x00}, {0x16, 0x01, 0x58, 0x01},
{0x01, 0x01, 0x5a, 0x00}, {0x16, 0x01, 0x5a, 0x01},
{0x4c, 0x00, 0x00, 0x00}, {0x4d, 0x00, 0x00, 0x00},
{0x4f, 0x00, 0x00, 0x00}, {0x51, 0x00, 0x00, 0x01}
},
{
{0x02, 0x01, 0x26, 0x00}, {0x09, 0x01, 0x26, 0x00},
{0x17, 0x01, 0x26, 0x00}, {0x28, 0x01, 0x26, 0x01},
{0x02, 0x01, 0x2a, 0x00}, {0x09, 0x01, 0x2a, 0x00},
{0x17, 0x01, 0x2a, 0x00}, {0x28, 0x01, 0x2a, 0x01},
{0x02, 0x01, 0x2c, 0x00}, {0x09, 0x01, 0x2c, 0x00},
{0x17, 0x01, 0x2c, 0x00}, {0x28, 0x01, 0x2c, 0x01},
{0x02, 0x01, 0x3b, 0x00}, {0x09, 0x01, 0x3b, 0x00},
{0x17, 0x01, 0x3b, 0x00}, {0x28, 0x01, 0x3b, 0x01}
},
/* 70 */
{
{0x03, 0x01, 0x26, 0x00}, {0x06, 0x01, 0x26, 0x00},
{0x0a, 0x01, 0x26, 0x00}, {0x0f, 0x01, 0x26, 0x00},
{0x18, 0x01, 0x26, 0x00}, {0x1f, 0x01, 0x26, 0x00},
{0x29, 0x01, 0x26, 0x00}, {0x38, 0x01, 0x26, 0x01},
{0x03, 0x01, 0x2a, 0x00}, {0x06, 0x01, 0x2a, 0x00},
{0x0a, 0x01, 0x2a, 0x00}, {0x0f, 0x01, 0x2a, 0x00},
{0x18, 0x01, 0x2a, 0x00}, {0x1f, 0x01, 0x2a, 0x00},
{0x29, 0x01, 0x2a, 0x00}, {0x38, 0x01, 0x2a, 0x01}
},
{
{0x03, 0x01, 0x2c, 0x00}, {0x06, 0x01, 0x2c, 0x00},
{0x0a, 0x01, 0x2c, 0x00}, {0x0f, 0x01, 0x2c, 0x00},
{0x18, 0x01, 0x2c, 0x00}, {0x1f, 0x01, 0x2c, 0x00},
{0x29, 0x01, 0x2c, 0x00}, {0x38, 0x01, 0x2c, 0x01},
{0x03, 0x01, 0x3b, 0x00}, {0x06, 0x01, 0x3b, 0x00},
{0x0a, 0x01, 0x3b, 0x00}, {0x0f, 0x01, 0x3b, 0x00},
{0x18, 0x01, 0x3b, 0x00}, {0x1f, 0x01, 0x3b, 0x00},
{0x29, 0x01, 0x3b, 0x00}, {0x38, 0x01, 0x3b, 0x01}
},
{
{0x02, 0x01, 0x58, 0x00}, {0x09, 0x01, 0x58, 0x00},
{0x17, 0x01, 0x58, 0x00}, {0x28, 0x01, 0x58, 0x01},
{0x02, 0x01, 0x5a, 0x00}, {0x09, 0x01, 0x5a, 0x00},
{0x17, 0x01, 0x5a, 0x00}, {0x28, 0x01, 0x5a, 0x01},
{0x00, 0x01, 0x21, 0x01}, {0x00, 0x01, 0x22, 0x01},
{0x00, 0x01, 0x28, 0x01}, {0x00, 0x01, 0x29, 0x01},
{0x00, 0x01, 0x3f, 0x01}, {0x50, 0x00, 0x00, 0x00},
{0x52, 0x00, 0x00, 0x00}, {0x54, 0x00, 0x00, 0x01}
},
{
{0x03, 0x01, 0x58, 0x00}, {0x06, 0x01, 0x58, 0x00},
{0x0a, 0x01, 0x58, 0x00}, {0x0f, 0x01, 0x58, 0x00},
{0x18, 0x01, 0x58, 0x00}, {0x1f, 0x01, 0x58, 0x00},
{0x29, 0x01, 0x58, 0x00}, {0x38, 0x01, 0x58, 0x01},
{0x03, 0x01, 0x5a, 0x00}, {0x06, 0x01, 0x5a, 0x00},
{0x0a, 0x01, 0x5a, 0x00}, {0x0f, 0x01, 0x5a, 0x00},
{0x18, 0x01, 0x5a, 0x00}, {0x1f, 0x01, 0x5a, 0x00},
{0x29, 0x01, 0x5a, 0x00}, {0x38, 0x01, 0x5a, 0x01}
},
{
{0x01, 0x01, 0x21, 0x00}, {0x16, 0x01, 0x21, 0x01},
{0x01, 0x01, 0x22, 0x00}, {0x16, 0x01, 0x22, 0x01},
{0x01, 0x01, 0x28, 0x00}, {0x16, 0x01, 0x28, 0x01},
{0x01, 0x01, 0x29, 0x00}, {0x16, 0x01, 0x29, 0x01},
{0x01, 0x01, 0x3f, 0x00}, {0x16, 0x01, 0x3f, 0x01},
{0x00, 0x01, 0x27, 0x01}, {0x00, 0x01, 0x2b, 0x01},
{0x00, 0x01, 0x7c, 0x01}, {0x53, 0x00, 0x00, 0x00},
{0x55, 0x00, 0x00, 0x00}, {0x58, 0x00, 0x00, 0x01}
},
/* 75 */
{
{0x02, 0x01, 0x21, 0x00}, {0x09, 0x01, 0x21, 0x00},
{0x17, 0x01, 0x21, 0x00}, {0x28, 0x01, 0x21, 0x01},
{0x02, 0x01, 0x22, 0x00}, {0x09, 0x01, 0x22, 0x00},
{0x17, 0x01, 0x22, 0x00}, {0x28, 0x01, 0x22, 0x01},
{0x02, 0x01, 0x28, 0x00}, {0x09, 0x01, 0x28, 0x00},
{0x17, 0x01, 0x28, 0x00}, {0x28, 0x01, 0x28, 0x01},
{0x02, 0x01, 0x29, 0x00}, {0x09, 0x01, 0x29, 0x00},
{0x17, 0x01, 0x29, 0x00}, {0x28, 0x01, 0x29, 0x01}
},
{
{0x03, 0x01, 0x21, 0x00}, {0x06, 0x01, 0x21, 0x00},
{0x0a, 0x01, 0x21, 0x00}, {0x0f, 0x01, 0x21, 0x00},
{0x18, 0x01, 0x21, 0x00}, {0x1f, 0x01, 0x21, 0x00},
{0x29, 0x01, 0x21, 0x00}, {0x38, 0x01, 0x21, 0x01},
{0x03, 0x01, 0x22, 0x00}, {0x06, 0x01, 0x22, 0x00},
{0x0a, 0x01, 0x22, 0x00}, {0x0f, 0x01, 0x22, 0x00},
{0x18, 0x01, 0x22, 0x00}, {0x1f, 0x01, 0x22, 0x00},
{0x29, 0x01, 0x22, 0x00}, {0x38, 0x01, 0x22, 0x01}
},
{
{0x03, 0x01, 0x28, 0x00}, {0x06, 0x01, 0x28, 0x00},
{0x0a, 0x01, 0x28, 0x00}, {0x0f, 0x01, 0x28, 0x00},
{0x18, 0x01, 0x28, 0x00}, {0x1f, 0x01, 0x28, 0x00},
{0x29, 0x01, 0x28, 0x00}, {0x38, 0x01, 0x28, 0x01},
{0x03, 0x01, 0x29, 0x00}, {0x06, 0x01, 0x29, 0x00},
{0x0a, 0x01, 0x29, 0x00}, {0x0f, 0x01, 0x29, 0x00},
{0x18, 0x01, 0x29, 0x00}, {0x1f, 0x01, 0x29, 0x00},
{0x29, 0x01, 0x29, 0x00}, {0x38, 0x01, 0x29, 0x01}
},
{
{0x02, 0x01, 0x3f, 0x00}, {0x09, 0x01, 0x3f, 0x00},
{0x17, 0x01, 0x3f, 0x00}, {0x28, 0x01, 0x3f, 0x01},
{0x01, 0x01, 0x27, 0x00}, {0x16, 0x01, 0x27, 0x01},
{0x01, 0x01, 0x2b, 0x00}, {0x16, 0x01, 0x2b, 0x01},
{0x01, 0x01, 0x7c, 0x00}, {0x16, 0x01, 0x7c, 0x01},
{0x00, 0x01, 0x23, 0x01}, {0x00, 0x01, 0x3e, 0x01},
{0x56, 0x00, 0x00, 0x00}, {0x57, 0x00, 0x00, 0x00},
{0x59, 0x00, 0x00, 0x00}, {0x5a, 0x00, 0x00, 0x01}
},
{
{0x03, 0x01, 0x3f, 0x00}, {0x06, 0x01, 0x3f, 0x00},
{0x0a, 0x01, 0x3f, 0x00}, {0x0f, 0x01, 0x3f, 0x00},
{0x18, 0x01, 0x3f, 0x00}, {0x1f, 0x01, 0x3f, 0x00},
{0x29, 0x01, 0x3f, 0x00}, {0x38, 0x01, 0x3f, 0x01},
{0x02, 0x01, 0x27, 0x00}, {0x09, 0x01, 0x27, 0x00},
{0x17, 0x01, 0x27, 0x00}, {0x28, 0x01, 0x27, 0x01},
{0x02, 0x01, 0x2b, 0x00}, {0x09, 0x01, 0x2b, 0x00},
{0x17, 0x01, 0x2b, 0x00}, {0x28, 0x01, 0x2b, 0x01}
},
/* 80 */
{
{0x03, 0x01, 0x27, 0x00}, {0x06, 0x01, 0x27, 0x00},
{0x0a, 0x01, 0x27, 0x00}, {0x0f, 0x01, 0x27, 0x00},
{0x18, 0x01, 0x27, 0x00}, {0x1f, 0x01, 0x27, 0x00},
{0x29, 0x01, 0x27, 0x00}, {0x38, 0x01, 0x27, 0x01},
{0x03, 0x01, 0x2b, 0x00}, {0x06, 0x01, 0x2b, 0x00},
{0x0a, 0x01, 0x2b, 0x00}, {0x0f, 0x01, 0x2b, 0x00},
{0x18, 0x01, 0x2b, 0x00}, {0x1f, 0x01, 0x2b, 0x00},
{0x29, 0x01, 0x2b, 0x00}, {0x38, 0x01, 0x2b, 0x01}
},
{
{0x02, 0x01, 0x7c, 0x00}, {0x09, 0x01, 0x7c, 0x00},
{0x17, 0x01, 0x7c, 0x00}, {0x28, 0x01, 0x7c, 0x01},
{0x01, 0x01, 0x23, 0x00}, {0x16, 0x01, 0x23, 0x01},
{0x01, 0x01, 0x3e, 0x00}, {0x16, 0x01, 0x3e, 0x01},
{0x00, 0x01, 0x00, 0x01}, {0x00, 0x01, 0x24, 0x01},
{0x00, 0x01, 0x40, 0x01}, {0x00, 0x01, 0x5b, 0x01},
{0x00, 0x01, 0x5d, 0x01}, {0x00, 0x01, 0x7e, 0x01},
{0x5b, 0x00, 0x00, 0x00}, {0x5c, 0x00, 0x00, 0x01}
},
{
{0x03, 0x01, 0x7c, 0x00}, {0x06, 0x01, 0x7c, 0x00},
{0x0a, 0x01, 0x7c, 0x00}, {0x0f, 0x01, 0x7c, 0x00},
{0x18, 0x01, 0x7c, 0x00}, {0x1f, 0x01, 0x7c, 0x00},
{0x29, 0x01, 0x7c, 0x00}, {0x38, 0x01, 0x7c, 0x01},
{0x02, 0x01, 0x23, 0x00}, {0x09, 0x01, 0x23, 0x00},
{0x17, 0x01, 0x23, 0x00}, {0x28, 0x01, 0x23, 0x01},
{0x02, 0x01, 0x3e, 0x00}, {0x09, 0x01, 0x3e, 0x00},
{0x17, 0x01, 0x3e, 0x00}, {0x28, 0x01, 0x3e, 0x01}
},
{
{0x03, 0x01, 0x23, 0x00}, {0x06, 0x01, 0x23, 0x00},
{0x0a, 0x01, 0x23, 0x00}, {0x0f, 0x01, 0x23, 0x00},
{0x18, 0x01, 0x23, 0x00}, {0x1f, 0x01, 0x23, 0x00},
{0x29, 0x01, 0x23, 0x00}, {0x38, 0x01, 0x23, 0x01},
{0x03, 0x01, 0x3e, 0x00}, {0x06, 0x01, 0x3e, 0x00},
{0x0a, 0x01, 0x3e, 0x00}, {0x0f, 0x01, 0x3e, 0x00},
{0x18, 0x01, 0x3e, 0x00}, {0x1f, 0x01, 0x3e, 0x00},
{0x29, 0x01, 0x3e, 0x00}, {0x38, 0x01, 0x3e, 0x01}
},
{
{0x01, 0x01, 0x00, 0x00}, {0x16, 0x01, 0x00, 0x01},
{0x01, 0x01, 0x24, 0x00}, {0x16, 0x01, 0x24, 0x01},
{0x01, 0x01, 0x40, 0x00}, {0x16, 0x01, 0x40, 0x01},
{0x01, 0x01, 0x5b, 0x00}, {0x16, 0x01, 0x5b, 0x01},
{0x01, 0x01, 0x5d, 0x00}, {0x16, 0x01, 0x5d, 0x01},
{0x01, 0x01, 0x7e, 0x00}, {0x16, 0x01, 0x7e, 0x01},
{0x00, 0x01, 0x5e, 0x01}, {0x00, 0x01, 0x7d, 0x01},
{0x5d, 0x00, 0x00, 0x00}, {0x5e, 0x00, 0x00, 0x01}
},
/* 85 */
{
{0x02, 0x01, 0x00, 0x00}, {0x09, 0x01, 0x00, 0x00},
{0x17, 0x01, 0x00, 0x00}, {0x28, 0x01, 0x00, 0x01},
{0x02, 0x01, 0x24, 0x00}, {0x09, 0x01, 0x24, 0x00},
{0x17, 0x01, 0x24, 0x00}, {0x28, 0x01, 0x24, 0x01},
{0x02, 0x01, 0x40, 0x00}, {0x09, 0x01, 0x40, 0x00},
{0x17, 0x01, 0x40, 0x00}, {0x28, 0x01, 0x40, 0x01},
{0x02, 0x01, 0x5b, 0x00}, {0x09, 0x01, 0x5b, 0x00},
{0x17, 0x01, 0x5b, 0x00}, {0x28, 0x01, 0x5b, 0x01}
},
{
{0x03, 0x01, 0x00, 0x00}, {0x06, 0x01, 0x00, 0x00},
{0x0a, 0x01, 0x00, 0x00}, {0x0f, 0x01, 0x00, 0x00},
{0x18, 0x01, 0x00, 0x00}, {0x1f, 0x01, 0x00, 0x00},
{0x29, 0x01, 0x00, 0x00}, {0x38, 0x01, 0x00, 0x01},
{0x03, 0x01, 0x24, 0x00}, {0x06, 0x01, 0x24, 0x00},
{0x0a, 0x01, 0x24, 0x00}, {0x0f, 0x01, 0x24, 0x00},
{0x18, 0x01, 0x24, 0x00}, {0x1f, 0x01, 0x24, 0x00},
{0x29, 0x01, 0x24, 0x00}, {0x38, 0x01, 0x24, 0x01}
},
{
{0x03, 0x01, 0x40, 0x00}, {0x06, 0x01, 0x40, 0x00},
{0x0a, 0x01, 0x40, 0x00}, {0x0f, 0x01, 0x40, 0x00},
{0x18, 0x01, 0x40, 0x00}, {0x1f, 0x01, 0x40, 0x00},
{0x29, 0x01, 0x40, 0x00}, {0x38, 0x01, 0x40, 0x01},
{0x03, 0x01, 0x5b, 0x00}, {0x06, 0x01, 0x5b, 0x00},
{0x0a, 0x01, 0x5b, 0x00}, {0x0f, 0x01, 0x5b, 0x00},
{0x18, 0x01, 0x5b, 0x00}, {0x1f, 0x01, 0x5b, 0x00},
{0x29, 0x01, 0x5b, 0x00}, {0x38, 0x01, 0x5b, 0x01}
},
{
{0x02, 0x01, 0x5d, 0x00}, {0x09, 0x01, 0x5d, 0x00},
{0x17, 0x01, 0x5d, 0x00}, {0x28, 0x01, 0x5d, 0x01},
{0x02, 0x01, 0x7e, 0x00}, {0x09, 0x01, 0x7e, 0x00},
{0x17, 0x01, 0x7e, 0x00}, {0x28, 0x01, 0x7e, 0x01},
{0x01, 0x01, 0x5e, 0x00}, {0x16, 0x01, 0x5e, 0x01},
{0x01, 0x01, 0x7d, 0x00}, {0x16, 0x01, 0x7d, 0x01},
{0x00, 0x01, 0x3c, 0x01}, {0x00, 0x01, 0x60, 0x01},
{0x00, 0x01, 0x7b, 0x01}, {0x5f, 0x00, 0x00, 0x01}
},
{
{0x03, 0x01, 0x5d, 0x00}, {0x06, 0x01, 0x5d, 0x00},
{0x0a, 0x01, 0x5d, 0x00}, {0x0f, 0x01, 0x5d, 0x00},
{0x18, 0x01, 0x5d, 0x00}, {0x1f, 0x01, 0x5d, 0x00},
{0x29, 0x01, 0x5d, 0x00}, {0x38, 0x01, 0x5d, 0x01},
{0x03, 0x01, 0x7e, 0x00}, {0x06, 0x01, 0x7e, 0x00},
{0x0a, 0x01, 0x7e, 0x00}, {0x0f, 0x01, 0x7e, 0x00},
{0x18, 0x01, 0x7e, 0x00}, {0x1f, 0x01, 0x7e, 0x00},
{0x29, 0x01, 0x7e, 0x00}, {0x38, 0x01, 0x7e, 0x01}
},
/* 90 */
{
{0x02, 0x01, 0x5e, 0x00}, {0x09, 0x01, 0x5e, 0x00},
{0x17, 0x01, 0x5e, 0x00}, {0x28, 0x01, 0x5e, 0x01},
{0x02, 0x01, 0x7d, 0x00}, {0x09, 0x01, 0x7d, 0x00},
{0x17, 0x01, 0x7d, 0x00}, {0x28, 0x01, 0x7d, 0x01},
{0x01, 0x01, 0x3c, 0x00}, {0x16, 0x01, 0x3c, 0x01},
{0x01, 0x01, 0x60, 0x00}, {0x16, 0x01, 0x60, 0x01},
{0x01, 0x01, 0x7b, 0x00}, {0x16, 0x01, 0x7b, 0x01},
{0x60, 0x00, 0x00, 0x00}, {0x6e, 0x00, 0x00, 0x01}
},
{
{0x03, 0x01, 0x5e, 0x00}, {0x06, 0x01, 0x5e, 0x00},
{0x0a, 0x01, 0x5e, 0x00}, {0x0f, 0x01, 0x5e, 0x00},
{0x18, 0x01, 0x5e, 0x00}, {0x1f, 0x01, 0x5e, 0x00},
{0x29, 0x01, 0x5e, 0x00}, {0x38, 0x01, 0x5e, 0x01},
{0x03, 0x01, 0x7d, 0x00}, {0x06, 0x01, 0x7d, 0x00},
{0x0a, 0x01, 0x7d, 0x00}, {0x0f, 0x01, 0x7d, 0x00},
{0x18, 0x01, 0x7d, 0x00}, {0x1f, 0x01, 0x7d, 0x00},
{0x29, 0x01, 0x7d, 0x00}, {0x38, 0x01, 0x7d, 0x01}
},
{
{0x02, 0x01, 0x3c, 0x00}, {0x09, 0x01, 0x3c, 0x00},
{0x17, 0x01, 0x3c, 0x00}, {0x28, 0x01, 0x3c, 0x01},
{0x02, 0x01, 0x60, 0x00}, {0x09, 0x01, 0x60, 0x00},
{0x17, 0x01, 0x60, 0x00}, {0x28, 0x01, 0x60, 0x01},
{0x02, 0x01, 0x7b, 0x00}, {0x09, 0x01, 0x7b, 0x00},
{0x17, 0x01, 0x7b, 0x00}, {0x28, 0x01, 0x7b, 0x01},
{0x61, 0x00, 0x00, 0x00}, {0x65, 0x00, 0x00, 0x00},
{0x6f, 0x00, 0x00, 0x00}, {0x85, 0x00, 0x00, 0x01}
},
{
{0x03, 0x01, 0x3c, 0x00}, {0x06, 0x01, 0x3c, 0x00},
{0x0a, 0x01, 0x3c, 0x00}, {0x0f, 0x01, 0x3c, 0x00},
{0x18, 0x01, 0x3c, 0x00}, {0x1f, 0x01, 0x3c, 0x00},
{0x29, 0x01, 0x3c, 0x00}, {0x38, 0x01, 0x3c, 0x01},
{0x03, 0x01, 0x60, 0x00}, {0x06, 0x01, 0x60, 0x00},
{0x0a, 0x01, 0x60, 0x00}, {0x0f, 0x01, 0x60, 0x00},
{0x18, 0x01, 0x60, 0x00}, {0x1f, 0x01, 0x60, 0x00},
{0x29, 0x01, 0x60, 0x00}, {0x38, 0x01, 0x60, 0x01}
},
{
{0x03, 0x01, 0x7b, 0x00}, {0x06, 0x01, 0x7b, 0x00},
{0x0a, 0x01, 0x7b, 0x00}, {0x0f, 0x01, 0x7b, 0x00},
{0x18, 0x01, 0x7b, 0x00}, {0x1f, 0x01, 0x7b, 0x00},
{0x29, 0x01, 0x7b, 0x00}, {0x38, 0x01, 0x7b, 0x01},
{0x62, 0x00, 0x00, 0x00}, {0x63, 0x00, 0x00, 0x00},
{0x66, 0x00, 0x00, 0x00}, {0x69, 0x00, 0x00, 0x00},
{0x70, 0x00, 0x00, 0x00}, {0x77, 0x00, 0x00, 0x00},
{0x86, 0x00, 0x00, 0x00}, {0x99, 0x00, 0x00, 0x01}
},
/* 95 */
{
{0x00, 0x01, 0x5c, 0x01}, {0x00, 0x01, 0xc3, 0x01},
{0x00, 0x01, 0xd0, 0x01}, {0x64, 0x00, 0x00, 0x00},
{0x67, 0x00, 0x00, 0x00}, {0x68, 0x00, 0x00, 0x00},
{0x6a, 0x00, 0x00, 0x00}, {0x6b, 0x00, 0x00, 0x00},
{0x71, 0x00, 0x00, 0x00}, {0x74, 0x00, 0x00, 0x00},
{0x78, 0x00, 0x00, 0x00}, {0x7e, 0x00, 0x00, 0x00},
{0x87, 0x00, 0x00, 0x00}, {0x8e, 0x00, 0x00, 0x00},
{0x9a, 0x00, 0x00, 0x00}, {0xa9, 0x00, 0x00, 0x01}
},
{
{0x01, 0x01, 0x5c, 0x00}, {0x16, 0x01, 0x5c, 0x01},
{0x01, 0x01, 0xc3, 0x00}, {0x16, 0x01, 0xc3, 0x01},
{0x01, 0x01, 0xd0, 0x00}, {0x16, 0x01, 0xd0, 0x01},
{0x00, 0x01, 0x80, 0x01}, {0x00, 0x01, 0x82, 0x01},
{0x00, 0x01, 0x83, 0x01}, {0x00, 0x01, 0xa2, 0x01},
{0x00, 0x01, 0xb8, 0x01}, {0x00, 0x01, 0xc2, 0x01},
{0x00, 0x01, 0xe0, 0x01}, {0x00, 0x01, 0xe2, 0x01},
{0x6c, 0x00, 0x00, 0x00}, {0x6d, 0x00, 0x00, 0x00}
},
{
{0x02, 0x01, 0x5c, 0x00}, {0x09, 0x01, 0x5c, 0x00},
{0x17, 0x01, 0x5c, 0x00}, {0x28, 0x01, 0x5c, 0x01},
{0x02, 0x01, 0xc3, 0x00}, {0x09, 0x01, 0xc3, 0x00},
{0x17, 0x01, 0xc3, 0x00}, {0x28, 0x01, 0xc3, 0x01},
{0x02, 0x01, 0xd0, 0x00}, {0x09, 0x01, 0xd0, 0x00},
{0x17, 0x01, 0xd0, 0x00}, {0x28, 0x01, 0xd0, 0x01},
{0x01, 0x01, 0x80, 0x00}, {0x16, 0x01, 0x80, 0x01},
{0x01, 0x01, 0x82, 0x00}, {0x16, 0x01, 0x82, 0x01}
},
{
{0x03, 0x01, 0x5c, 0x00}, {0x06, 0x01, 0x5c, 0x00},
{0x0a, 0x01, 0x5c, 0x00}, {0x0f, 0x01, 0x5c, 0x00},
{0x18, 0x01, 0x5c, 0x00}, {0x1f, 0x01, 0x5c, 0x00},
{0x29, 0x01, 0x5c, 0x00}, {0x38, 0x01, 0x5c, 0x01},
{0x03, 0x01, 0xc3, 0x00}, {0x06, 0x01, 0xc3, 0x00},
{0x0a, 0x01, 0xc3, 0x00}, {0x0f, 0x01, 0xc3, 0x00},
{0x18, 0x01, 0xc3, 0x00}, {0x1f, 0x01, 0xc3, 0x00},
{0x29, 0x01, 0xc3, 0x00}, {0x38, 0x01, 0xc3, 0x01}
},
{
{0x03, 0x01, 0xd0, 0x00}, {0x06, 0x01, 0xd0, 0x00},
{0x0a, 0x01, 0xd0, 0x00}, {0x0f, 0x01, 0xd0, 0x00},
{0x18, 0x01, 0xd0, 0x00}, {0x1f, 0x01, 0xd0, 0x00},
{0x29, 0x01, 0xd0, 0x00}, {0x38, 0x01, 0xd0, 0x01},
{0x02, 0x01, 0x80, 0x00}, {0x09, 0x01, 0x80, 0x00},
{0x17, 0x01, 0x80, 0x00}, {0x28, 0x01, 0x80, 0x01},
{0x02, 0x01, 0x82, 0x00}, {0x09, 0x01, 0x82, 0x00},
{0x17, 0x01, 0x82, 0x00}, {0x28, 0x01, 0x82, 0x01}
},
/* 100 */
{
{0x03, 0x01, 0x80, 0x00}, {0x06, 0x01, 0x80, 0x00},
{0x0a, 0x01, 0x80, 0x00}, {0x0f, 0x01, 0x80, 0x00},
{0x18, 0x01, 0x80, 0x00}, {0x1f, 0x01, 0x80, 0x00},
{0x29, 0x01, 0x80, 0x00}, {0x38, 0x01, 0x80, 0x01},
{0x03, 0x01, 0x82, 0x00}, {0x06, 0x01, 0x82, 0x00},
{0x0a, 0x01, 0x82, 0x00}, {0x0f, 0x01, 0x82, 0x00},
{0x18, 0x01, 0x82, 0x00}, {0x1f, 0x01, 0x82, 0x00},
{0x29, 0x01, 0x82, 0x00}, {0x38, 0x01, 0x82, 0x01}
},
{
{0x01, 0x01, 0x83, 0x00}, {0x16, 0x01, 0x83, 0x01},
{0x01, 0x01, 0xa2, 0x00}, {0x16, 0x01, 0xa2, 0x01},
{0x01, 0x01, 0xb8, 0x00}, {0x16, 0x01, 0xb8, 0x01},
{0x01, 0x01, 0xc2, 0x00}, {0x16, 0x01, 0xc2, 0x01},
{0x01, 0x01, 0xe0, 0x00}, {0x16, 0x01, 0xe0, 0x01},
{0x01, 0x01, 0xe2, 0x00}, {0x16, 0x01, 0xe2, 0x01},
{0x00, 0x01, 0x99, 0x01}, {0x00, 0x01, 0xa1, 0x01},
{0x00, 0x01, 0xa7, 0x01}, {0x00, 0x01, 0xac, 0x01}
},
{
{0x02, 0x01, 0x83, 0x00}, {0x09, 0x01, 0x83, 0x00},
{0x17, 0x01, 0x83, 0x00}, {0x28, 0x01, 0x83, 0x01},
{0x02, 0x01, 0xa2, 0x00}, {0x09, 0x01, 0xa2, 0x00},
{0x17, 0x01, 0xa2, 0x00}, {0x28, 0x01, 0xa2, 0x01},
{0x02, 0x01, 0xb8, 0x00}, {0x09, 0x01, 0xb8, 0x00},
{0x17, 0x01, 0xb8, 0x00}, {0x28, 0x01, 0xb8, 0x01},
{0x02, 0x01, 0xc2, 0x00}, {0x09, 0x01, 0xc2, 0x00},
{0x17, 0x01, 0xc2, 0x00}, {0x28, 0x01, 0xc2, 0x01}
},
{
{0x03, 0x01, 0x83, 0x00}, {0x06, 0x01, 0x83, 0x00},
{0x0a, 0x01, 0x83, 0x00}, {0x0f, 0x01, 0x83, 0x00},
{0x18, 0x01, 0x83, 0x00}, {0x1f, 0x01, 0x83, 0x00},
{0x29, 0x01, 0x83, 0x00}, {0x38, 0x01, 0x83, 0x01},
{0x03, 0x01, 0xa2, 0x00}, {0x06, 0x01, 0xa2, 0x00},
{0x0a, 0x01, 0xa2, 0x00}, {0x0f, 0x01, 0xa2, 0x00},
{0x18, 0x01, 0xa2, 0x00}, {0x1f, 0x01, 0xa2, 0x00},
{0x29, 0x01, 0xa2, 0x00}, {0x38, 0x01, 0xa2, 0x01}
},
{
{0x03, 0x01, 0xb8, 0x00}, {0x06, 0x01, 0xb8, 0x00},
{0x0a, 0x01, 0xb8, 0x00}, {0x0f, 0x01, 0xb8, 0x00},
{0x18, 0x01, 0xb8, 0x00}, {0x1f, 0x01, 0xb8, 0x00},
{0x29, 0x01, 0xb8, 0x00}, {0x38, 0x01, 0xb8, 0x01},
{0x03, 0x01, 0xc2, 0x00}, {0x06, 0x01, 0xc2, 0x00},
{0x0a, 0x01, 0xc2, 0x00}, {0x0f, 0x01, 0xc2, 0x00},
{0x18, 0x01, 0xc2, 0x00}, {0x1f, 0x01, 0xc2, 0x00},
{0x29, 0x01, 0xc2, 0x00}, {0x38, 0x01, 0xc2, 0x01}
},
/* 105 */
{
{0x02, 0x01, 0xe0, 0x00}, {0x09, 0x01, 0xe0, 0x00},
{0x17, 0x01, 0xe0, 0x00}, {0x28, 0x01, 0xe0, 0x01},
{0x02, 0x01, 0xe2, 0x00}, {0x09, 0x01, 0xe2, 0x00},
{0x17, 0x01, 0xe2, 0x00}, {0x28, 0x01, 0xe2, 0x01},
{0x01, 0x01, 0x99, 0x00}, {0x16, 0x01, 0x99, 0x01},
{0x01, 0x01, 0xa1, 0x00}, {0x16, 0x01, 0xa1, 0x01},
{0x01, 0x01, 0xa7, 0x00}, {0x16, 0x01, 0xa7, 0x01},
{0x01, 0x01, 0xac, 0x00}, {0x16, 0x01, 0xac, 0x01}
},
{
{0x03, 0x01, 0xe0, 0x00}, {0x06, 0x01, 0xe0, 0x00},
{0x0a, 0x01, 0xe0, 0x00}, {0x0f, 0x01, 0xe0, 0x00},
{0x18, 0x01, 0xe0, 0x00}, {0x1f, 0x01, 0xe0, 0x00},
{0x29, 0x01, 0xe0, 0x00}, {0x38, 0x01, 0xe0, 0x01},
{0x03, 0x01, 0xe2, 0x00}, {0x06, 0x01, 0xe2, 0x00},
{0x0a, 0x01, 0xe2, 0x00}, {0x0f, 0x01, 0xe2, 0x00},
{0x18, 0x01, 0xe2, 0x00}, {0x1f, 0x01, 0xe2, 0x00},
{0x29, 0x01, 0xe2, 0x00}, {0x38, 0x01, 0xe2, 0x01}
},
{
{0x02, 0x01, 0x99, 0x00}, {0x09, 0x01, 0x99, 0x00},
{0x17, 0x01, 0x99, 0x00}, {0x28, 0x01, 0x99, 0x01},
{0x02, 0x01, 0xa1, 0x00}, {0x09, 0x01, 0xa1, 0x00},
{0x17, 0x01, 0xa1, 0x00}, {0x28, 0x01, 0xa1, 0x01},
{0x02, 0x01, 0xa7, 0x00}, {0x09, 0x01, 0xa7, 0x00},
{0x17, 0x01, 0xa7, 0x00}, {0x28, 0x01, 0xa7, 0x01},
{0x02, 0x01, 0xac, 0x00}, {0x09, 0x01, 0xac, 0x00},
{0x17, 0x01, 0xac, 0x00}, {0x28, 0x01, 0xac, 0x01}
},
{
{0x03, 0x01, 0x99, 0x00}, {0x06, 0x01, 0x99, 0x00},
{0x0a, 0x01, 0x99, 0x00}, {0x0f, 0x01, 0x99, 0x00},
{0x18, 0x01, 0x99, 0x00}, {0x1f, 0x01, 0x99, 0x00},
{0x29, 0x01, 0x99, 0x00}, {0x38, 0x01, 0x99, 0x01},
{0x03, 0x01, 0xa1, 0x00}, {0x06, 0x01, 0xa1, 0x00},
{0x0a, 0x01, 0xa1, 0x00}, {0x0f, 0x01, 0xa1, 0x00},
{0x18, 0x01, 0xa1, 0x00}, {0x1f, 0x01, 0xa1, 0x00},
{0x29, 0x01, 0xa1, 0x00}, {0x38, 0x01, 0xa1, 0x01}
},
{
{0x03, 0x01, 0xa7, 0x00}, {0x06, 0x01, 0xa7, 0x00},
{0x0a, 0x01, 0xa7, 0x00}, {0x0f, 0x01, 0xa7, 0x00},
{0x18, 0x01, 0xa7, 0x00}, {0x1f, 0x01, 0xa7, 0x00},
{0x29, 0x01, 0xa7, 0x00}, {0x38, 0x01, 0xa7, 0x01},
{0x03, 0x01, 0xac, 0x00}, {0x06, 0x01, 0xac, 0x00},
{0x0a, 0x01, 0xac, 0x00}, {0x0f, 0x01, 0xac, 0x00},
{0x18, 0x01, 0xac, 0x00}, {0x1f, 0x01, 0xac, 0x00},
{0x29, 0x01, 0xac, 0x00}, {0x38, 0x01, 0xac, 0x01}
},
/* 110 */
{
{0x72, 0x00, 0x00, 0x00}, {0x73, 0x00, 0x00, 0x00},
{0x75, 0x00, 0x00, 0x00}, {0x76, 0x00, 0x00, 0x00},
{0x79, 0x00, 0x00, 0x00}, {0x7b, 0x00, 0x00, 0x00},
{0x7f, 0x00, 0x00, 0x00}, {0x82, 0x00, 0x00, 0x00},
{0x88, 0x00, 0x00, 0x00}, {0x8b, 0x00, 0x00, 0x00},
{0x8f, 0x00, 0x00, 0x00}, {0x92, 0x00, 0x00, 0x00},
{0x9b, 0x00, 0x00, 0x00}, {0xa2, 0x00, 0x00, 0x00},
{0xaa, 0x00, 0x00, 0x00}, {0xb4, 0x00, 0x00, 0x01}
},
{
{0x00, 0x01, 0xb0, 0x01}, {0x00, 0x01, 0xb1, 0x01},
{0x00, 0x01, 0xb3, 0x01}, {0x00, 0x01, 0xd1, 0x01},
{0x00, 0x01, 0xd8, 0x01}, {0x00, 0x01, 0xd9, 0x01},
{0x00, 0x01, 0xe3, 0x01}, {0x00, 0x01, 0xe5, 0x01},
{0x00, 0x01, 0xe6, 0x01}, {0x7a, 0x00, 0x00, 0x00},
{0x7c, 0x00, 0x00, 0x00}, {0x7d, 0x00, 0x00, 0x00},
{0x80, 0x00, 0x00, 0x00}, {0x81, 0x00, 0x00, 0x00},
{0x83, 0x00, 0x00, 0x00}, {0x84, 0x00, 0x00, 0x00}
},
{
{0x01, 0x01, 0xb0, 0x00}, {0x16, 0x01, 0xb0, 0x01},
{0x01, 0x01, 0xb1, 0x00}, {0x16, 0x01, 0xb1, 0x01},
{0x01, 0x01, 0xb3, 0x00}, {0x16, 0x01, 0xb3, 0x01},
{0x01, 0x01, 0xd1, 0x00}, {0x16, 0x01, 0xd1, 0x01},
{0x01, 0x01, 0xd8, 0x00}, {0x16, 0x01, 0xd8, 0x01},
{0x01, 0x01, 0xd9, 0x00}, {0x16, 0x01, 0xd9, 0x01},
{0x01, 0x01, 0xe3, 0x00}, {0x16, 0x01, 0xe3, 0x01},
{0x01, 0x01, 0xe5, 0x00}, {0x16, 0x01, 0xe5, 0x01}
},
{
{0x02, 0x01, 0xb0, 0x00}, {0x09, 0x01, 0xb0, 0x00},
{0x17, 0x01, 0xb0, 0x00}, {0x28, 0x01, 0xb0, 0x01},
{0x02, 0x01, 0xb1, 0x00}, {0x09, 0x01, 0xb1, 0x00},
{0x17, 0x01, 0xb1, 0x00}, {0x28, 0x01, 0xb1, 0x01},
{0x02, 0x01, 0xb3, 0x00}, {0x09, 0x01, 0xb3, 0x00},
{0x17, 0x01, 0xb3, 0x00}, {0x28, 0x01, 0xb3, 0x01},
{0x02, 0x01, 0xd1, 0x00}, {0x09, 0x01, 0xd1, 0x00},
{0x17, 0x01, 0xd1, 0x00}, {0x28, 0x01, 0xd1, 0x01}
},
{
{0x03, 0x01, 0xb0, 0x00}, {0x06, 0x01, 0xb0, 0x00},
{0x0a, 0x01, 0xb0, 0x00}, {0x0f, 0x01, 0xb0, 0x00},
{0x18, 0x01, 0xb0, 0x00}, {0x1f, 0x01, 0xb0, 0x00},
{0x29, 0x01, 0xb0, 0x00}, {0x38, 0x01, 0xb0, 0x01},
{0x03, 0x01, 0xb1, 0x00}, {0x06, 0x01, 0xb1, 0x00},
{0x0a, 0x01, 0xb1, 0x00}, {0x0f, 0x01, 0xb1, 0x00},
{0x18, 0x01, 0xb1, 0x00}, {0x1f, 0x01, 0xb1, 0x00},
{0x29, 0x01, 0xb1, 0x00}, {0x38, 0x01, 0xb1, 0x01}
},
/* 115 */
{
{0x03, 0x01, 0xb3, 0x00}, {0x06, 0x01, 0xb3, 0x00},
{0x0a, 0x01, 0xb3, 0x00}, {0x0f, 0x01, 0xb3, 0x00},
{0x18, 0x01, 0xb3, 0x00}, {0x1f, 0x01, 0xb3, 0x00},
{0x29, 0x01, 0xb3, 0x00}, {0x38, 0x01, 0xb3, 0x01},
{0x03, 0x01, 0xd1, 0x00}, {0x06, 0x01, 0xd1, 0x00},
{0x0a, 0x01, 0xd1, 0x00}, {0x0f, 0x01, 0xd1, 0x00},
{0x18, 0x01, 0xd1, 0x00}, {0x1f, 0x01, 0xd1, 0x00},
{0x29, 0x01, 0xd1, 0x00}, {0x38, 0x01, 0xd1, 0x01}
},
{
{0x02, 0x01, 0xd8, 0x00}, {0x09, 0x01, 0xd8, 0x00},
{0x17, 0x01, 0xd8, 0x00}, {0x28, 0x01, 0xd8, 0x01},
{0x02, 0x01, 0xd9, 0x00}, {0x09, 0x01, 0xd9, 0x00},
{0x17, 0x01, 0xd9, 0x00}, {0x28, 0x01, 0xd9, 0x01},
{0x02, 0x01, 0xe3, 0x00}, {0x09, 0x01, 0xe3, 0x00},
{0x17, 0x01, 0xe3, 0x00}, {0x28, 0x01, 0xe3, 0x01},
{0x02, 0x01, 0xe5, 0x00}, {0x09, 0x01, 0xe5, 0x00},
{0x17, 0x01, 0xe5, 0x00}, {0x28, 0x01, 0xe5, 0x01}
},
{
{0x03, 0x01, 0xd8, 0x00}, {0x06, 0x01, 0xd8, 0x00},
{0x0a, 0x01, 0xd8, 0x00}, {0x0f, 0x01, 0xd8, 0x00},
{0x18, 0x01, 0xd8, 0x00}, {0x1f, 0x01, 0xd8, 0x00},
{0x29, 0x01, 0xd8, 0x00}, {0x38, 0x01, 0xd8, 0x01},
{0x03, 0x01, 0xd9, 0x00}, {0x06, 0x01, 0xd9, 0x00},
{0x0a, 0x01, 0xd9, 0x00}, {0x0f, 0x01, 0xd9, 0x00},
{0x18, 0x01, 0xd9, 0x00}, {0x1f, 0x01, 0xd9, 0x00},
{0x29, 0x01, 0xd9, 0x00}, {0x38, 0x01, 0xd9, 0x01}
},
{
{0x03, 0x01, 0xe3, 0x00}, {0x06, 0x01, 0xe3, 0x00},
{0x0a, 0x01, 0xe3, 0x00}, {0x0f, 0x01, 0xe3, 0x00},
{0x18, 0x01, 0xe3, 0x00}, {0x1f, 0x01, 0xe3, 0x00},
{0x29, 0x01, 0xe3, 0x00}, {0x38, 0x01, 0xe3, 0x01},
{0x03, 0x01, 0xe5, 0x00}, {0x06, 0x01, 0xe5, 0x00},
{0x0a, 0x01, 0xe5, 0x00}, {0x0f, 0x01, 0xe5, 0x00},
{0x18, 0x01, 0xe5, 0x00}, {0x1f, 0x01, 0xe5, 0x00},
{0x29, 0x01, 0xe5, 0x00}, {0x38, 0x01, 0xe5, 0x01}
},
{
{0x01, 0x01, 0xe6, 0x00}, {0x16, 0x01, 0xe6, 0x01},
{0x00, 0x01, 0x81, 0x01}, {0x00, 0x01, 0x84, 0x01},
{0x00, 0x01, 0x85, 0x01}, {0x00, 0x01, 0x86, 0x01},
{0x00, 0x01, 0x88, 0x01}, {0x00, 0x01, 0x92, 0x01},
{0x00, 0x01, 0x9a, 0x01}, {0x00, 0x01, 0x9c, 0x01},
{0x00, 0x01, 0xa0, 0x01}, {0x00, 0x01, 0xa3, 0x01},
{0x00, 0x01, 0xa4, 0x01}, {0x00, 0x01, 0xa9, 0x01},
{0x00, 0x01, 0xaa, 0x01}, {0x00, 0x01, 0xad, 0x01}
},
/* 120 */
{
{0x02, 0x01, 0xe6, 0x00}, {0x09, 0x01, 0xe6, 0x00},
{0x17, 0x01, 0xe6, 0x00}, {0x28, 0x01, 0xe6, 0x01},
{0x01, 0x01, 0x81, 0x00}, {0x16, 0x01, 0x81, 0x01},
{0x01, 0x01, 0x84, 0x00}, {0x16, 0x01, 0x84, 0x01},
{0x01, 0x01, 0x85, 0x00}, {0x16, 0x01, 0x85, 0x01},
{0x01, 0x01, 0x86, 0x00}, {0x16, 0x01, 0x86, 0x01},
{0x01, 0x01, 0x88, 0x00}, {0x16, 0x01, 0x88, 0x01},
{0x01, 0x01, 0x92, 0x00}, {0x16, 0x01, 0x92, 0x01}
},
{
{0x03, 0x01, 0xe6, 0x00}, {0x06, 0x01, 0xe6, 0x00},
{0x0a, 0x01, 0xe6, 0x00}, {0x0f, 0x01, 0xe6, 0x00},
{0x18, 0x01, 0xe6, 0x00}, {0x1f, 0x01, 0xe6, 0x00},
{0x29, 0x01, 0xe6, 0x00}, {0x38, 0x01, 0xe6, 0x01},
{0x02, 0x01, 0x81, 0x00}, {0x09, 0x01, 0x81, 0x00},
{0x17, 0x01, 0x81, 0x00}, {0x28, 0x01, 0x81, 0x01},
{0x02, 0x01, 0x84, 0x00}, {0x09, 0x01, 0x84, 0x00},
{0x17, 0x01, 0x84, 0x00}, {0x28, 0x01, 0x84, 0x01}
},
{
{0x03, 0x01, 0x81, 0x00}, {0x06, 0x01, 0x81, 0x00},
{0x0a, 0x01, 0x81, 0x00}, {0x0f, 0x01, 0x81, 0x00},
{0x18, 0x01, 0x81, 0x00}, {0x1f, 0x01, 0x81, 0x00},
{0x29, 0x01, 0x81, 0x00}, {0x38, 0x01, 0x81, 0x01},
{0x03, 0x01, 0x84, 0x00}, {0x06, 0x01, 0x84, 0x00},
{0x0a, 0x01, 0x84, 0x00}, {0x0f, 0x01, 0x84, 0x00},
{0x18, 0x01, 0x84, 0x00}, {0x1f, 0x01, 0x84, 0x00},
{0x29, 0x01, 0x84, 0x00}, {0x38, 0x01, 0x84, 0x01}
},
{
{0x02, 0x01, 0x85, 0x00}, {0x09, 0x01, 0x85, 0x00},
{0x17, 0x01, 0x85, 0x00}, {0x28, 0x01, 0x85, 0x01},
{0x02, 0x01, 0x86, 0x00}, {0x09, 0x01, 0x86, 0x00},
{0x17, 0x01, 0x86, 0x00}, {0x28, 0x01, 0x86, 0x01},
{0x02, 0x01, 0x88, 0x00}, {0x09, 0x01, 0x88, 0x00},
{0x17, 0x01, 0x88, 0x00}, {0x28, 0x01, 0x88, 0x01},
{0x02, 0x01, 0x92, 0x00}, {0x09, 0x01, 0x92, 0x00},
{0x17, 0x01, 0x92, 0x00}, {0x28, 0x01, 0x92, 0x01}
},
{
{0x03, 0x01, 0x85, 0x00}, {0x06, 0x01, 0x85, 0x00},
{0x0a, 0x01, 0x85, 0x00}, {0x0f, 0x01, 0x85, 0x00},
{0x18, 0x01, 0x85, 0x00}, {0x1f, 0x01, 0x85, 0x00},
{0x29, 0x01, 0x85, 0x00}, {0x38, 0x01, 0x85, 0x01},
{0x03, 0x01, 0x86, 0x00}, {0x06, 0x01, 0x86, 0x00},
{0x0a, 0x01, 0x86, 0x00}, {0x0f, 0x01, 0x86, 0x00},
{0x18, 0x01, 0x86, 0x00}, {0x1f, 0x01, 0x86, 0x00},
{0x29, 0x01, 0x86, 0x00}, {0x38, 0x01, 0x86, 0x01}
},
/* 125 */
{
{0x03, 0x01, 0x88, 0x00}, {0x06, 0x01, 0x88, 0x00},
{0x0a, 0x01, 0x88, 0x00}, {0x0f, 0x01, 0x88, 0x00},
{0x18, 0x01, 0x88, 0x00}, {0x1f, 0x01, 0x88, 0x00},
{0x29, 0x01, 0x88, 0x00}, {0x38, 0x01, 0x88, 0x01},
{0x03, 0x01, 0x92, 0x00}, {0x06, 0x01, 0x92, 0x00},
{0x0a, 0x01, 0x92, 0x00}, {0x0f, 0x01, 0x92, 0x00},
{0x18, 0x01, 0x92, 0x00}, {0x1f, 0x01, 0x92, 0x00},
{0x29, 0x01, 0x92, 0x00}, {0x38, 0x01, 0x92, 0x01}
},
{
{0x01, 0x01, 0x9a, 0x00}, {0x16, 0x01, 0x9a, 0x01},
{0x01, 0x01, 0x9c, 0x00}, {0x16, 0x01, 0x9c, 0x01},
{0x01, 0x01, 0xa0, 0x00}, {0x16, 0x01, 0xa0, 0x01},
{0x01, 0x01, 0xa3, 0x00}, {0x16, 0x01, 0xa3, 0x01},
{0x01, 0x01, 0xa4, 0x00}, {0x16, 0x01, 0xa4, 0x01},
{0x01, 0x01, 0xa9, 0x00}, {0x16, 0x01, 0xa9, 0x01},
{0x01, 0x01, 0xaa, 0x00}, {0x16, 0x01, 0xaa, 0x01},
{0x01, 0x01, 0xad, 0x00}, {0x16, 0x01, 0xad, 0x01}
},
{
{0x02, 0x01, 0x9a, 0x00}, {0x09, 0x01, 0x9a, 0x00},
{0x17, 0x01, 0x9a, 0x00}, {0x28, 0x01, 0x9a, 0x01},
{0x02, 0x01, 0x9c, 0x00}, {0x09, 0x01, 0x9c, 0x00},
{0x17, 0x01, 0x9c, 0x00}, {0x28, 0x01, 0x9c, 0x01},
{0x02, 0x01, 0xa0, 0x00}, {0x09, 0x01, 0xa0, 0x00},
{0x17, 0x01, 0xa0, 0x00}, {0x28, 0x01, 0xa0, 0x01},
{0x02, 0x01, 0xa3, 0x00}, {0x09, 0x01, 0xa3, 0x00},
{0x17, 0x01, 0xa3, 0x00}, {0x28, 0x01, 0xa3, 0x01}
},
{
{0x03, 0x01, 0x9a, 0x00}, {0x06, 0x01, 0x9a, 0x00},
{0x0a, 0x01, 0x9a, 0x00}, {0x0f, 0x01, 0x9a, 0x00},
{0x18, 0x01, 0x9a, 0x00}, {0x1f, 0x01, 0x9a, 0x00},
{0x29, 0x01, 0x9a, 0x00}, {0x38, 0x01, 0x9a, 0x01},
{0x03, 0x01, 0x9c, 0x00}, {0x06, 0x01, 0x9c, 0x00},
{0x0a, 0x01, 0x9c, 0x00}, {0x0f, 0x01, 0x9c, 0x00},
{0x18, 0x01, 0x9c, 0x00}, {0x1f, 0x01, 0x9c, 0x00},
{0x29, 0x01, 0x9c, 0x00}, {0x38, 0x01, 0x9c, 0x01}
},
{
{0x03, 0x01, 0xa0, 0x00}, {0x06, 0x01, 0xa0, 0x00},
{0x0a, 0x01, 0xa0, 0x00}, {0x0f, 0x01, 0xa0, 0x00},
{0x18, 0x01, 0xa0, 0x00}, {0x1f, 0x01, 0xa0, 0x00},
{0x29, 0x01, 0xa0, 0x00}, {0x38, 0x01, 0xa0, 0x01},
{0x03, 0x01, 0xa3, 0x00}, {0x06, 0x01, 0xa3, 0x00},
{0x0a, 0x01, 0xa3, 0x00}, {0x0f, 0x01, 0xa3, 0x00},
{0x18, 0x01, 0xa3, 0x00}, {0x1f, 0x01, 0xa3, 0x00},
{0x29, 0x01, 0xa3, 0x00}, {0x38, 0x01, 0xa3, 0x01}
},
/* 130 */
{
{0x02, 0x01, 0xa4, 0x00}, {0x09, 0x01, 0xa4, 0x00},
{0x17, 0x01, 0xa4, 0x00}, {0x28, 0x01, 0xa4, 0x01},
{0x02, 0x01, 0xa9, 0x00}, {0x09, 0x01, 0xa9, 0x00},
{0x17, 0x01, 0xa9, 0x00}, {0x28, 0x01, 0xa9, 0x01},
{0x02, 0x01, 0xaa, 0x00}, {0x09, 0x01, 0xaa, 0x00},
{0x17, 0x01, 0xaa, 0x00}, {0x28, 0x01, 0xaa, 0x01},
{0x02, 0x01, 0xad, 0x00}, {0x09, 0x01, 0xad, 0x00},
{0x17, 0x01, 0xad, 0x00}, {0x28, 0x01, 0xad, 0x01}
},
{
{0x03, 0x01, 0xa4, 0x00}, {0x06, 0x01, 0xa4, 0x00},
{0x0a, 0x01, 0xa4, 0x00}, {0x0f, 0x01, 0xa4, 0x00},
{0x18, 0x01, 0xa4, 0x00}, {0x1f, 0x01, 0xa4, 0x00},
{0x29, 0x01, 0xa4, 0x00}, {0x38, 0x01, 0xa4, 0x01},
{0x03, 0x01, 0xa9, 0x00}, {0x06, 0x01, 0xa9, 0x00},
{0x0a, 0x01, 0xa9, 0x00}, {0x0f, 0x01, 0xa9, 0x00},
{0x18, 0x01, 0xa9, 0x00}, {0x1f, 0x01, 0xa9, 0x00},
{0x29, 0x01, 0xa9, 0x00}, {0x38, 0x01, 0xa9, 0x01}
},
{
{0x03, 0x01, 0xaa, 0x00}, {0x06, 0x01, 0xaa, 0x00},
{0x0a, 0x01, 0xaa, 0x00}, {0x0f, 0x01, 0xaa, 0x00},
{0x18, 0x01, 0xaa, 0x00}, {0x1f, 0x01, 0xaa, 0x00},
{0x29, 0x01, 0xaa, 0x00}, {0x38, 0x01, 0xaa, 0x01},
{0x03, 0x01, 0xad, 0x00}, {0x06, 0x01, 0xad, 0x00},
{0x0a, 0x01, 0xad, 0x00}, {0x0f, 0x01, 0xad, 0x00},
{0x18, 0x01, 0xad, 0x00}, {0x1f, 0x01, 0xad, 0x00},
{0x29, 0x01, 0xad, 0x00}, {0x38, 0x01, 0xad, 0x01}
},
{
{0x89, 0x00, 0x00, 0x00}, {0x8a, 0x00, 0x00, 0x00},
{0x8c, 0x00, 0x00, 0x00}, {0x8d, 0x00, 0x00, 0x00},
{0x90, 0x00, 0x00, 0x00}, {0x91, 0x00, 0x00, 0x00},
{0x93, 0x00, 0x00, 0x00}, {0x96, 0x00, 0x00, 0x00},
{0x9c, 0x00, 0x00, 0x00}, {0x9f, 0x00, 0x00, 0x00},
{0xa3, 0x00, 0x00, 0x00}, {0xa6, 0x00, 0x00, 0x00},
{0xab, 0x00, 0x00, 0x00}, {0xae, 0x00, 0x00, 0x00},
{0xb5, 0x00, 0x00, 0x00}, {0xbe, 0x00, 0x00, 0x01}
},
{
{0x00, 0x01, 0xb2, 0x01}, {0x00, 0x01, 0xb5, 0x01},
{0x00, 0x01, 0xb9, 0x01}, {0x00, 0x01, 0xba, 0x01},
{0x00, 0x01, 0xbb, 0x01}, {0x00, 0x01, 0xbd, 0x01},
{0x00, 0x01, 0xbe, 0x01}, {0x00, 0x01, 0xc4, 0x01},
{0x00, 0x01, 0xc6, 0x01}, {0x00, 0x01, 0xe4, 0x01},
{0x00, 0x01, 0xe8, 0x01}, {0x00, 0x01, 0xe9, 0x01},
{0x94, 0x00, 0x00, 0x00}, {0x95, 0x00, 0x00, 0x00},
{0x97, 0x00, 0x00, 0x00}, {0x98, 0x00, 0x00, 0x00}
},
/* 135 */
{
{0x01, 0x01, 0xb2, 0x00}, {0x16, 0x01, 0xb2, 0x01},
{0x01, 0x01, 0xb5, 0x00}, {0x16, 0x01, 0xb5, 0x01},
{0x01, 0x01, 0xb9, 0x00}, {0x16, 0x01, 0xb9, 0x01},
{0x01, 0x01, 0xba, 0x00}, {0x16, 0x01, 0xba, 0x01},
{0x01, 0x01, 0xbb, 0x00}, {0x16, 0x01, 0xbb, 0x01},
{0x01, 0x01, 0xbd, 0x00}, {0x16, 0x01, 0xbd, 0x01},
{0x01, 0x01, 0xbe, 0x00}, {0x16, 0x01, 0xbe, 0x01},
{0x01, 0x01, 0xc4, 0x00}, {0x16, 0x01, 0xc4, 0x01}
},
{
{0x02, 0x01, 0xb2, 0x00}, {0x09, 0x01, 0xb2, 0x00},
{0x17, 0x01, 0xb2, 0x00}, {0x28, 0x01, 0xb2, 0x01},
{0x02, 0x01, 0xb5, 0x00}, {0x09, 0x01, 0xb5, 0x00},
{0x17, 0x01, 0xb5, 0x00}, {0x28, 0x01, 0xb5, 0x01},
{0x02, 0x01, 0xb9, 0x00}, {0x09, 0x01, 0xb9, 0x00},
{0x17, 0x01, 0xb9, 0x00}, {0x28, 0x01, 0xb9, 0x01},
{0x02, 0x01, 0xba, 0x00}, {0x09, 0x01, 0xba, 0x00},
{0x17, 0x01, 0xba, 0x00}, {0x28, 0x01, 0xba, 0x01}
},
{
{0x03, 0x01, 0xb2, 0x00}, {0x06, 0x01, 0xb2, 0x00},
{0x0a, 0x01, 0xb2, 0x00}, {0x0f, 0x01, 0xb2, 0x00},
{0x18, 0x01, 0xb2, 0x00}, {0x1f, 0x01, 0xb2, 0x00},
{0x29, 0x01, 0xb2, 0x00}, {0x38, 0x01, 0xb2, 0x01},
{0x03, 0x01, 0xb5, 0x00}, {0x06, 0x01, 0xb5, 0x00},
{0x0a, 0x01, 0xb5, 0x00}, {0x0f, 0x01, 0xb5, 0x00},
{0x18, 0x01, 0xb5, 0x00}, {0x1f, 0x01, 0xb5, 0x00},
{0x29, 0x01, 0xb5, 0x00}, {0x38, 0x01, 0xb5, 0x01}
},
{
{0x03, 0x01, 0xb9, 0x00}, {0x06, 0x01, 0xb9, 0x00},
{0x0a, 0x01, 0xb9, 0x00}, {0x0f, 0x01, 0xb9, 0x00},
{0x18, 0x01, 0xb9, 0x00}, {0x1f, 0x01, 0xb9, 0x00},
{0x29, 0x01, 0xb9, 0x00}, {0x38, 0x01, 0xb9, 0x01},
{0x03, 0x01, 0xba, 0x00}, {0x06, 0x01, 0xba, 0x00},
{0x0a, 0x01, 0xba, 0x00}, {0x0f, 0x01, 0xba, 0x00},
{0x18, 0x01, 0xba, 0x00}, {0x1f, 0x01, 0xba, 0x00},
{0x29, 0x01, 0xba, 0x00}, {0x38, 0x01, 0xba, 0x01}
},
{
{0x02, 0x01, 0xbb, 0x00}, {0x09, 0x01, 0xbb, 0x00},
{0x17, 0x01, 0xbb, 0x00}, {0x28, 0x01, 0xbb, 0x01},
{0x02, 0x01, 0xbd, 0x00}, {0x09, 0x01, 0xbd, 0x00},
{0x17, 0x01, 0xbd, 0x00}, {0x28, 0x01, 0xbd, 0x01},
{0x02, 0x01, 0xbe, 0x00}, {0x09, 0x01, 0xbe, 0x00},
{0x17, 0x01, 0xbe, 0x00}, {0x28, 0x01, 0xbe, 0x01},
{0x02, 0x01, 0xc4, 0x00}, {0x09, 0x01, 0xc4, 0x00},
{0x17, 0x01, 0xc4, 0x00}, {0x28, 0x01, 0xc4, 0x01}
},
/* 140 */
{
{0x03, 0x01, 0xbb, 0x00}, {0x06, 0x01, 0xbb, 0x00},
{0x0a, 0x01, 0xbb, 0x00}, {0x0f, 0x01, 0xbb, 0x00},
{0x18, 0x01, 0xbb, 0x00}, {0x1f, 0x01, 0xbb, 0x00},
{0x29, 0x01, 0xbb, 0x00}, {0x38, 0x01, 0xbb, 0x01},
{0x03, 0x01, 0xbd, 0x00}, {0x06, 0x01, 0xbd, 0x00},
{0x0a, 0x01, 0xbd, 0x00}, {0x0f, 0x01, 0xbd, 0x00},
{0x18, 0x01, 0xbd, 0x00}, {0x1f, 0x01, 0xbd, 0x00},
{0x29, 0x01, 0xbd, 0x00}, {0x38, 0x01, 0xbd, 0x01}
},
{
{0x03, 0x01, 0xbe, 0x00}, {0x06, 0x01, 0xbe, 0x00},
{0x0a, 0x01, 0xbe, 0x00}, {0x0f, 0x01, 0xbe, 0x00},
{0x18, 0x01, 0xbe, 0x00}, {0x1f, 0x01, 0xbe, 0x00},
{0x29, 0x01, 0xbe, 0x00}, {0x38, 0x01, 0xbe, 0x01},
{0x03, 0x01, 0xc4, 0x00}, {0x06, 0x01, 0xc4, 0x00},
{0x0a, 0x01, 0xc4, 0x00}, {0x0f, 0x01, 0xc4, 0x00},
{0x18, 0x01, 0xc4, 0x00}, {0x1f, 0x01, 0xc4, 0x00},
{0x29, 0x01, 0xc4, 0x00}, {0x38, 0x01, 0xc4, 0x01}
},
{
{0x01, 0x01, 0xc6, 0x00}, {0x16, 0x01, 0xc6, 0x01},
{0x01, 0x01, 0xe4, 0x00}, {0x16, 0x01, 0xe4, 0x01},
{0x01, 0x01, 0xe8, 0x00}, {0x16, 0x01, 0xe8, 0x01},
{0x01, 0x01, 0xe9, 0x00}, {0x16, 0x01, 0xe9, 0x01},
{0x00, 0x01, 0x01, 0x01}, {0x00, 0x01, 0x87, 0x01},
{0x00, 0x01, 0x89, 0x01}, {0x00, 0x01, 0x8a, 0x01},
{0x00, 0x01, 0x8b, 0x01}, {0x00, 0x01, 0x8c, 0x01},
{0x00, 0x01, 0x8d, 0x01}, {0x00, 0x01, 0x8f, 0x01}
},
{
{0x02, 0x01, 0xc6, 0x00}, {0x09, 0x01, 0xc6, 0x00},
{0x17, 0x01, 0xc6, 0x00}, {0x28, 0x01, 0xc6, 0x01},
{0x02, 0x01, 0xe4, 0x00}, {0x09, 0x01, 0xe4, 0x00},
{0x17, 0x01, 0xe4, 0x00}, {0x28, 0x01, 0xe4, 0x01},
{0x02, 0x01, 0xe8, 0x00}, {0x09, 0x01, 0xe8, 0x00},
{0x17, 0x01, 0xe8, 0x00}, {0x28, 0x01, 0xe8, 0x01},
{0x02, 0x01, 0xe9, 0x00}, {0x09, 0x01, 0xe9, 0x00},
{0x17, 0x01, 0xe9, 0x00}, {0x28, 0x01, 0xe9, 0x01}
},
{
{0x03, 0x01, 0xc6, 0x00}, {0x06, 0x01, 0xc6, 0x00},
{0x0a, 0x01, 0xc6, 0x00}, {0x0f, 0x01, 0xc6, 0x00},
{0x18, 0x01, 0xc6, 0x00}, {0x1f, 0x01, 0xc6, 0x00},
{0x29, 0x01, 0xc6, 0x00}, {0x38, 0x01, 0xc6, 0x01},
{0x03, 0x01, 0xe4, 0x00}, {0x06, 0x01, 0xe4, 0x00},
{0x0a, 0x01, 0xe4, 0x00}, {0x0f, 0x01, 0xe4, 0x00},
{0x18, 0x01, 0xe4, 0x00}, {0x1f, 0x01, 0xe4, 0x00},
{0x29, 0x01, 0xe4, 0x00}, {0x38, 0x01, 0xe4, 0x01}
},
/* 145 */
{
{0x03, 0x01, 0xe8, 0x00}, {0x06, 0x01, 0xe8, 0x00},
{0x0a, 0x01, 0xe8, 0x00}, {0x0f, 0x01, 0xe8, 0x00},
{0x18, 0x01, 0xe8, 0x00}, {0x1f, 0x01, 0xe8, 0x00},
{0x29, 0x01, 0xe8, 0x00}, {0x38, 0x01, 0xe8, 0x01},
{0x03, 0x01, 0xe9, 0x00}, {0x06, 0x01, 0xe9, 0x00},
{0x0a, 0x01, 0xe9, 0x00}, {0x0f, 0x01, 0xe9, 0x00},
{0x18, 0x01, 0xe9, 0x00}, {0x1f, 0x01, 0xe9, 0x00},
{0x29, 0x01, 0xe9, 0x00}, {0x38, 0x01, 0xe9, 0x01}
},
{
{0x01, 0x01, 0x01, 0x00}, {0x16, 0x01, 0x01, 0x01},
{0x01, 0x01, 0x87, 0x00}, {0x16, 0x01, 0x87, 0x01},
{0x01, 0x01, 0x89, 0x00}, {0x16, 0x01, 0x89, 0x01},
{0x01, 0x01, 0x8a, 0x00}, {0x16, 0x01, 0x8a, 0x01},
{0x01, 0x01, 0x8b, 0x00}, {0x16, 0x01, 0x8b, 0x01},
{0x01, 0x01, 0x8c, 0x00}, {0x16, 0x01, 0x8c, 0x01},
{0x01, 0x01, 0x8d, 0x00}, {0x16, 0x01, 0x8d, 0x01},
{0x01, 0x01, 0x8f, 0x00}, {0x16, 0x01, 0x8f, 0x01}
},
{
{0x02, 0x01, 0x01, 0x00}, {0x09, 0x01, 0x01, 0x00},
{0x17, 0x01, 0x01, 0x00}, {0x28, 0x01, 0x01, 0x01},
{0x02, 0x01, 0x87, 0x00}, {0x09, 0x01, 0x87, 0x00},
{0x17, 0x01, 0x87, 0x00}, {0x28, 0x01, 0x87, 0x01},
{0x02, 0x01, 0x89, 0x00}, {0x09, 0x01, 0x89, 0x00},
{0x17, 0x01, 0x89, 0x00}, {0x28, 0x01, 0x89, 0x01},
{0x02, 0x01, 0x8a, 0x00}, {0x09, 0x01, 0x8a, 0x00},
{0x17, 0x01, 0x8a, 0x00}, {0x28, 0x01, 0x8a, 0x01}
},
{
{0x03, 0x01, 0x01, 0x00}, {0x06, 0x01, 0x01, 0x00},
{0x0a, 0x01, 0x01, 0x00}, {0x0f, 0x01, 0x01, 0x00},
{0x18, 0x01, 0x01, 0x00}, {0x1f, 0x01, 0x01, 0x00},
{0x29, 0x01, 0x01, 0x00}, {0x38, 0x01, 0x01, 0x01},
{0x03, 0x01, 0x87, 0x00}, {0x06, 0x01, 0x87, 0x00},
{0x0a, 0x01, 0x87, 0x00}, {0x0f, 0x01, 0x87, 0x00},
{0x18, 0x01, 0x87, 0x00}, {0x1f, 0x01, 0x87, 0x00},
{0x29, 0x01, 0x87, 0x00}, {0x38, 0x01, 0x87, 0x01}
},
{
{0x03, 0x01, 0x89, 0x00}, {0x06, 0x01, 0x89, 0x00},
{0x0a, 0x01, 0x89, 0x00}, {0x0f, 0x01, 0x89, 0x00},
{0x18, 0x01, 0x89, 0x00}, {0x1f, 0x01, 0x89, 0x00},
{0x29, 0x01, 0x89, 0x00}, {0x38, 0x01, 0x89, 0x01},
{0x03, 0x01, 0x8a, 0x00}, {0x06, 0x01, 0x8a, 0x00},
{0x0a, 0x01, 0x8a, 0x00}, {0x0f, 0x01, 0x8a, 0x00},
{0x18, 0x01, 0x8a, 0x00}, {0x1f, 0x01, 0x8a, 0x00},
{0x29, 0x01, 0x8a, 0x00}, {0x38, 0x01, 0x8a, 0x01}
},
/* 150 */
{
{0x02, 0x01, 0x8b, 0x00}, {0x09, 0x01, 0x8b, 0x00},
{0x17, 0x01, 0x8b, 0x00}, {0x28, 0x01, 0x8b, 0x01},
{0x02, 0x01, 0x8c, 0x00}, {0x09, 0x01, 0x8c, 0x00},
{0x17, 0x01, 0x8c, 0x00}, {0x28, 0x01, 0x8c, 0x01},
{0x02, 0x01, 0x8d, 0x00}, {0x09, 0x01, 0x8d, 0x00},
{0x17, 0x01, 0x8d, 0x00}, {0x28, 0x01, 0x8d, 0x01},
{0x02, 0x01, 0x8f, 0x00}, {0x09, 0x01, 0x8f, 0x00},
{0x17, 0x01, 0x8f, 0x00}, {0x28, 0x01, 0x8f, 0x01}
},
{
{0x03, 0x01, 0x8b, 0x00}, {0x06, 0x01, 0x8b, 0x00},
{0x0a, 0x01, 0x8b, 0x00}, {0x0f, 0x01, 0x8b, 0x00},
{0x18, 0x01, 0x8b, 0x00}, {0x1f, 0x01, 0x8b, 0x00},
{0x29, 0x01, 0x8b, 0x00}, {0x38, 0x01, 0x8b, 0x01},
{0x03, 0x01, 0x8c, 0x00}, {0x06, 0x01, 0x8c, 0x00},
{0x0a, 0x01, 0x8c, 0x00}, {0x0f, 0x01, 0x8c, 0x00},
{0x18, 0x01, 0x8c, 0x00}, {0x1f, 0x01, 0x8c, 0x00},
{0x29, 0x01, 0x8c, 0x00}, {0x38, 0x01, 0x8c, 0x01}
},
{
{0x03, 0x01, 0x8d, 0x00}, {0x06, 0x01, 0x8d, 0x00},
{0x0a, 0x01, 0x8d, 0x00}, {0x0f, 0x01, 0x8d, 0x00},
{0x18, 0x01, 0x8d, 0x00}, {0x1f, 0x01, 0x8d, 0x00},
{0x29, 0x01, 0x8d, 0x00}, {0x38, 0x01, 0x8d, 0x01},
{0x03, 0x01, 0x8f, 0x00}, {0x06, 0x01, 0x8f, 0x00},
{0x0a, 0x01, 0x8f, 0x00}, {0x0f, 0x01, 0x8f, 0x00},
{0x18, 0x01, 0x8f, 0x00}, {0x1f, 0x01, 0x8f, 0x00},
{0x29, 0x01, 0x8f, 0x00}, {0x38, 0x01, 0x8f, 0x01}
},
{
{0x9d, 0x00, 0x00, 0x00}, {0x9e, 0x00, 0x00, 0x00},
{0xa0, 0x00, 0x00, 0x00}, {0xa1, 0x00, 0x00, 0x00},
{0xa4, 0x00, 0x00, 0x00}, {0xa5, 0x00, 0x00, 0x00},
{0xa7, 0x00, 0x00, 0x00}, {0xa8, 0x00, 0x00, 0x00},
{0xac, 0x00, 0x00, 0x00}, {0xad, 0x00, 0x00, 0x00},
{0xaf, 0x00, 0x00, 0x00}, {0xb1, 0x00, 0x00, 0x00},
{0xb6, 0x00, 0x00, 0x00}, {0xb9, 0x00, 0x00, 0x00},
{0xbf, 0x00, 0x00, 0x00}, {0xcf, 0x00, 0x00, 0x01}
},
{
{0x00, 0x01, 0x93, 0x01}, {0x00, 0x01, 0x95, 0x01},
{0x00, 0x01, 0x96, 0x01}, {0x00, 0x01, 0x97, 0x01},
{0x00, 0x01, 0x98, 0x01}, {0x00, 0x01, 0x9b, 0x01},
{0x00, 0x01, 0x9d, 0x01}, {0x00, 0x01, 0x9e, 0x01},
{0x00, 0x01, 0xa5, 0x01}, {0x00, 0x01, 0xa6, 0x01},
{0x00, 0x01, 0xa8, 0x01}, {0x00, 0x01, 0xae, 0x01},
{0x00, 0x01, 0xaf, 0x01}, {0x00, 0x01, 0xb4, 0x01},
{0x00, 0x01, 0xb6, 0x01}, {0x00, 0x01, 0xb7, 0x01}
},
/* 155 */
{
{0x01, 0x01, 0x93, 0x00}, {0x16, 0x01, 0x93, 0x01},
{0x01, 0x01, 0x95, 0x00}, {0x16, 0x01, 0x95, 0x01},
{0x01, 0x01, 0x96, 0x00}, {0x16, 0x01, 0x96, 0x01},
{0x01, 0x01, 0x97, 0x00}, {0x16, 0x01, 0x97, 0x01},
{0x01, 0x01, 0x98, 0x00}, {0x16, 0x01, 0x98, 0x01},
{0x01, 0x01, 0x9b, 0x00}, {0x16, 0x01, 0x9b, 0x01},
{0x01, 0x01, 0x9d, 0x00}, {0x16, 0x01, 0x9d, 0x01},
{0x01, 0x01, 0x9e, 0x00}, {0x16, 0x01, 0x9e, 0x01}
},
{
{0x02, 0x01, 0x93, 0x00}, {0x09, 0x01, 0x93, 0x00},
{0x17, 0x01, 0x93, 0x00}, {0x28, 0x01, 0x93, 0x01},
{0x02, 0x01, 0x95, 0x00}, {0x09, 0x01, 0x95, 0x00},
{0x17, 0x01, 0x95, 0x00}, {0x28, 0x01, 0x95, 0x01},
{0x02, 0x01, 0x96, 0x00}, {0x09, 0x01, 0x96, 0x00},
{0x17, 0x01, 0x96, 0x00}, {0x28, 0x01, 0x96, 0x01},
{0x02, 0x01, 0x97, 0x00}, {0x09, 0x01, 0x97, 0x00},
{0x17, 0x01, 0x97, 0x00}, {0x28, 0x01, 0x97, 0x01}
},
{
{0x03, 0x01, 0x93, 0x00}, {0x06, 0x01, 0x93, 0x00},
{0x0a, 0x01, 0x93, 0x00}, {0x0f, 0x01, 0x93, 0x00},
{0x18, 0x01, 0x93, 0x00}, {0x1f, 0x01, 0x93, 0x00},
{0x29, 0x01, 0x93, 0x00}, {0x38, 0x01, 0x93, 0x01},
{0x03, 0x01, 0x95, 0x00}, {0x06, 0x01, 0x95, 0x00},
{0x0a, 0x01, 0x95, 0x00}, {0x0f, 0x01, 0x95, 0x00},
{0x18, 0x01, 0x95, 0x00}, {0x1f, 0x01, 0x95, 0x00},
{0x29, 0x01, 0x95, 0x00}, {0x38, 0x01, 0x95, 0x01}
},
{
{0x03, 0x01, 0x96, 0x00}, {0x06, 0x01, 0x96, 0x00},
{0x0a, 0x01, 0x96, 0x00}, {0x0f, 0x01, 0x96, 0x00},
{0x18, 0x01, 0x96, 0x00}, {0x1f, 0x01, 0x96, 0x00},
{0x29, 0x01, 0x96, 0x00}, {0x38, 0x01, 0x96, 0x01},
{0x03, 0x01, 0x97, 0x00}, {0x06, 0x01, 0x97, 0x00},
{0x0a, 0x01, 0x97, 0x00}, {0x0f, 0x01, 0x97, 0x00},
{0x18, 0x01, 0x97, 0x00}, {0x1f, 0x01, 0x97, 0x00},
{0x29, 0x01, 0x97, 0x00}, {0x38, 0x01, 0x97, 0x01}
},
{
{0x02, 0x01, 0x98, 0x00}, {0x09, 0x01, 0x98, 0x00},
{0x17, 0x01, 0x98, 0x00}, {0x28, 0x01, 0x98, 0x01},
{0x02, 0x01, 0x9b, 0x00}, {0x09, 0x01, 0x9b, 0x00},
{0x17, 0x01, 0x9b, 0x00}, {0x28, 0x01, 0x9b, 0x01},
{0x02, 0x01, 0x9d, 0x00}, {0x09, 0x01, 0x9d, 0x00},
{0x17, 0x01, 0x9d, 0x00}, {0x28, 0x01, 0x9d, 0x01},
{0x02, 0x01, 0x9e, 0x00}, {0x09, 0x01, 0x9e, 0x00},
{0x17, 0x01, 0x9e, 0x00}, {0x28, 0x01, 0x9e, 0x01}
},
/* 160 */
{
{0x03, 0x01, 0x98, 0x00}, {0x06, 0x01, 0x98, 0x00},
{0x0a, 0x01, 0x98, 0x00}, {0x0f, 0x01, 0x98, 0x00},
{0x18, 0x01, 0x98, 0x00}, {0x1f, 0x01, 0x98, 0x00},
{0x29, 0x01, 0x98, 0x00}, {0x38, 0x01, 0x98, 0x01},
{0x03, 0x01, 0x9b, 0x00}, {0x06, 0x01, 0x9b, 0x00},
{0x0a, 0x01, 0x9b, 0x00}, {0x0f, 0x01, 0x9b, 0x00},
{0x18, 0x01, 0x9b, 0x00}, {0x1f, 0x01, 0x9b, 0x00},
{0x29, 0x01, 0x9b, 0x00}, {0x38, 0x01, 0x9b, 0x01}
},
{
{0x03, 0x01, 0x9d, 0x00}, {0x06, 0x01, 0x9d, 0x00},
{0x0a, 0x01, 0x9d, 0x00}, {0x0f, 0x01, 0x9d, 0x00},
{0x18, 0x01, 0x9d, 0x00}, {0x1f, 0x01, 0x9d, 0x00},
{0x29, 0x01, 0x9d, 0x00}, {0x38, 0x01, 0x9d, 0x01},
{0x03, 0x01, 0x9e, 0x00}, {0x06, 0x01, 0x9e, 0x00},
{0x0a, 0x01, 0x9e, 0x00}, {0x0f, 0x01, 0x9e, 0x00},
{0x18, 0x01, 0x9e, 0x00}, {0x1f, 0x01, 0x9e, 0x00},
{0x29, 0x01, 0x9e, 0x00}, {0x38, 0x01, 0x9e, 0x01}
},
{
{0x01, 0x01, 0xa5, 0x00}, {0x16, 0x01, 0xa5, 0x01},
{0x01, 0x01, 0xa6, 0x00}, {0x16, 0x01, 0xa6, 0x01},
{0x01, 0x01, 0xa8, 0x00}, {0x16, 0x01, 0xa8, 0x01},
{0x01, 0x01, 0xae, 0x00}, {0x16, 0x01, 0xae, 0x01},
{0x01, 0x01, 0xaf, 0x00}, {0x16, 0x01, 0xaf, 0x01},
{0x01, 0x01, 0xb4, 0x00}, {0x16, 0x01, 0xb4, 0x01},
{0x01, 0x01, 0xb6, 0x00}, {0x16, 0x01, 0xb6, 0x01},
{0x01, 0x01, 0xb7, 0x00}, {0x16, 0x01, 0xb7, 0x01}
},
{
{0x02, 0x01, 0xa5, 0x00}, {0x09, 0x01, 0xa5, 0x00},
{0x17, 0x01, 0xa5, 0x00}, {0x28, 0x01, 0xa5, 0x01},
{0x02, 0x01, 0xa6, 0x00}, {0x09, 0x01, 0xa6, 0x00},
{0x17, 0x01, 0xa6, 0x00}, {0x28, 0x01, 0xa6, 0x01},
{0x02, 0x01, 0xa8, 0x00}, {0x09, 0x01, 0xa8, 0x00},
{0x17, 0x01, 0xa8, 0x00}, {0x28, 0x01, 0xa8, 0x01},
{0x02, 0x01, 0xae, 0x00}, {0x09, 0x01, 0xae, 0x00},
{0x17, 0x01, 0xae, 0x00}, {0x28, 0x01, 0xae, 0x01}
},
{
{0x03, 0x01, 0xa5, 0x00}, {0x06, 0x01, 0xa5, 0x00},
{0x0a, 0x01, 0xa5, 0x00}, {0x0f, 0x01, 0xa5, 0x00},
{0x18, 0x01, 0xa5, 0x00}, {0x1f, 0x01, 0xa5, 0x00},
{0x29, 0x01, 0xa5, 0x00}, {0x38, 0x01, 0xa5, 0x01},
{0x03, 0x01, 0xa6, 0x00}, {0x06, 0x01, 0xa6, 0x00},
{0x0a, 0x01, 0xa6, 0x00}, {0x0f, 0x01, 0xa6, 0x00},
{0x18, 0x01, 0xa6, 0x00}, {0x1f, 0x01, 0xa6, 0x00},
{0x29, 0x01, 0xa6, 0x00}, {0x38, 0x01, 0xa6, 0x01}
},
/* 165 */
{
{0x03, 0x01, 0xa8, 0x00}, {0x06, 0x01, 0xa8, 0x00},
{0x0a, 0x01, 0xa8, 0x00}, {0x0f, 0x01, 0xa8, 0x00},
{0x18, 0x01, 0xa8, 0x00}, {0x1f, 0x01, 0xa8, 0x00},
{0x29, 0x01, 0xa8, 0x00}, {0x38, 0x01, 0xa8, 0x01},
{0x03, 0x01, 0xae, 0x00}, {0x06, 0x01, 0xae, 0x00},
{0x0a, 0x01, 0xae, 0x00}, {0x0f, 0x01, 0xae, 0x00},
{0x18, 0x01, 0xae, 0x00}, {0x1f, 0x01, 0xae, 0x00},
{0x29, 0x01, 0xae, 0x00}, {0x38, 0x01, 0xae, 0x01}
},
{
{0x02, 0x01, 0xaf, 0x00}, {0x09, 0x01, 0xaf, 0x00},
{0x17, 0x01, 0xaf, 0x00}, {0x28, 0x01, 0xaf, 0x01},
{0x02, 0x01, 0xb4, 0x00}, {0x09, 0x01, 0xb4, 0x00},
{0x17, 0x01, 0xb4, 0x00}, {0x28, 0x01, 0xb4, 0x01},
{0x02, 0x01, 0xb6, 0x00}, {0x09, 0x01, 0xb6, 0x00},
{0x17, 0x01, 0xb6, 0x00}, {0x28, 0x01, 0xb6, 0x01},
{0x02, 0x01, 0xb7, 0x00}, {0x09, 0x01, 0xb7, 0x00},
{0x17, 0x01, 0xb7, 0x00}, {0x28, 0x01, 0xb7, 0x01}
},
{
{0x03, 0x01, 0xaf, 0x00}, {0x06, 0x01, 0xaf, 0x00},
{0x0a, 0x01, 0xaf, 0x00}, {0x0f, 0x01, 0xaf, 0x00},
{0x18, 0x01, 0xaf, 0x00}, {0x1f, 0x01, 0xaf, 0x00},
{0x29, 0x01, 0xaf, 0x00}, {0x38, 0x01, 0xaf, 0x01},
{0x03, 0x01, 0xb4, 0x00}, {0x06, 0x01, 0xb4, 0x00},
{0x0a, 0x01, 0xb4, 0x00}, {0x0f, 0x01, 0xb4, 0x00},
{0x18, 0x01, 0xb4, 0x00}, {0x1f, 0x01, 0xb4, 0x00},
{0x29, 0x01, 0xb4, 0x00}, {0x38, 0x01, 0xb4, 0x01}
},
{
{0x03, 0x01, 0xb6, 0x00}, {0x06, 0x01, 0xb6, 0x00},
{0x0a, 0x01, 0xb6, 0x00}, {0x0f, 0x01, 0xb6, 0x00},
{0x18, 0x01, 0xb6, 0x00}, {0x1f, 0x01, 0xb6, 0x00},
{0x29, 0x01, 0xb6, 0x00}, {0x38, 0x01, 0xb6, 0x01},
{0x03, 0x01, 0xb7, 0x00}, {0x06, 0x01, 0xb7, 0x00},
{0x0a, 0x01, 0xb7, 0x00}, {0x0f, 0x01, 0xb7, 0x00},
{0x18, 0x01, 0xb7, 0x00}, {0x1f, 0x01, 0xb7, 0x00},
{0x29, 0x01, 0xb7, 0x00}, {0x38, 0x01, 0xb7, 0x01}
},
{
{0x00, 0x01, 0xbc, 0x01}, {0x00, 0x01, 0xbf, 0x01},
{0x00, 0x01, 0xc5, 0x01}, {0x00, 0x01, 0xe7, 0x01},
{0x00, 0x01, 0xef, 0x01}, {0xb0, 0x00, 0x00, 0x00},
{0xb2, 0x00, 0x00, 0x00}, {0xb3, 0x00, 0x00, 0x00},
{0xb7, 0x00, 0x00, 0x00}, {0xb8, 0x00, 0x00, 0x00},
{0xba, 0x00, 0x00, 0x00}, {0xbb, 0x00, 0x00, 0x00},
{0xc0, 0x00, 0x00, 0x00}, {0xc7, 0x00, 0x00, 0x00},
{0xd0, 0x00, 0x00, 0x00}, {0xdf, 0x00, 0x00, 0x01}
},
/* 170 */
{
{0x01, 0x01, 0xbc, 0x00}, {0x16, 0x01, 0xbc, 0x01},
{0x01, 0x01, 0xbf, 0x00}, {0x16, 0x01, 0xbf, 0x01},
{0x01, 0x01, 0xc5, 0x00}, {0x16, 0x01, 0xc5, 0x01},
{0x01, 0x01, 0xe7, 0x00}, {0x16, 0x01, 0xe7, 0x01},
{0x01, 0x01, 0xef, 0x00}, {0x16, 0x01, 0xef, 0x01},
{0x00, 0x01, 0x09, 0x01}, {0x00, 0x01, 0x8e, 0x01},
{0x00, 0x01, 0x90, 0x01}, {0x00, 0x01, 0x91, 0x01},
{0x00, 0x01, 0x94, 0x01}, {0x00, 0x01, 0x9f, 0x01}
},
{
{0x02, 0x01, 0xbc, 0x00}, {0x09, 0x01, 0xbc, 0x00},
{0x17, 0x01, 0xbc, 0x00}, {0x28, 0x01, 0xbc, 0x01},
{0x02, 0x01, 0xbf, 0x00}, {0x09, 0x01, 0xbf, 0x00},
{0x17, 0x01, 0xbf, 0x00}, {0x28, 0x01, 0xbf, 0x01},
{0x02, 0x01, 0xc5, 0x00}, {0x09, 0x01, 0xc5, 0x00},
{0x17, 0x01, 0xc5, 0x00}, {0x28, 0x01, 0xc5, 0x01},
{0x02, 0x01, 0xe7, 0x00}, {0x09, 0x01, 0xe7, 0x00},
{0x17, 0x01, 0xe7, 0x00}, {0x28, 0x01, 0xe7, 0x01}
},
{
{0x03, 0x01, 0xbc, 0x00}, {0x06, 0x01, 0xbc, 0x00},
{0x0a, 0x01, 0xbc, 0x00}, {0x0f, 0x01, 0xbc, 0x00},
{0x18, 0x01, 0xbc, 0x00}, {0x1f, 0x01, 0xbc, 0x00},
{0x29, 0x01, 0xbc, 0x00}, {0x38, 0x01, 0xbc, 0x01},
{0x03, 0x01, 0xbf, 0x00}, {0x06, 0x01, 0xbf, 0x00},
{0x0a, 0x01, 0xbf, 0x00}, {0x0f, 0x01, 0xbf, 0x00},
{0x18, 0x01, 0xbf, 0x00}, {0x1f, 0x01, 0xbf, 0x00},
{0x29, 0x01, 0xbf, 0x00}, {0x38, 0x01, 0xbf, 0x01}
},
{
{0x03, 0x01, 0xc5, 0x00}, {0x06, 0x01, 0xc5, 0x00},
{0x0a, 0x01, 0xc5, 0x00}, {0x0f, 0x01, 0xc5, 0x00},
{0x18, 0x01, 0xc5, 0x00}, {0x1f, 0x01, 0xc5, 0x00},
{0x29, 0x01, 0xc5, 0x00}, {0x38, 0x01, 0xc5, 0x01},
{0x03, 0x01, 0xe7, 0x00}, {0x06, 0x01, 0xe7, 0x00},
{0x0a, 0x01, 0xe7, 0x00}, {0x0f, 0x01, 0xe7, 0x00},
{0x18, 0x01, 0xe7, 0x00}, {0x1f, 0x01, 0xe7, 0x00},
{0x29, 0x01, 0xe7, 0x00}, {0x38, 0x01, 0xe7, 0x01}
},
{
{0x02, 0x01, 0xef, 0x00}, {0x09, 0x01, 0xef, 0x00},
{0x17, 0x01, 0xef, 0x00}, {0x28, 0x01, 0xef, 0x01},
{0x01, 0x01, 0x09, 0x00}, {0x16, 0x01, 0x09, 0x01},
{0x01, 0x01, 0x8e, 0x00}, {0x16, 0x01, 0x8e, 0x01},
{0x01, 0x01, 0x90, 0x00}, {0x16, 0x01, 0x90, 0x01},
{0x01, 0x01, 0x91, 0x00}, {0x16, 0x01, 0x91, 0x01},
{0x01, 0x01, 0x94, 0x00}, {0x16, 0x01, 0x94, 0x01},
{0x01, 0x01, 0x9f, 0x00}, {0x16, 0x01, 0x9f, 0x01}
},
/* 175 */
{
{0x03, 0x01, 0xef, 0x00}, {0x06, 0x01, 0xef, 0x00},
{0x0a, 0x01, 0xef, 0x00}, {0x0f, 0x01, 0xef, 0x00},
{0x18, 0x01, 0xef, 0x00}, {0x1f, 0x01, 0xef, 0x00},
{0x29, 0x01, 0xef, 0x00}, {0x38, 0x01, 0xef, 0x01},
{0x02, 0x01, 0x09, 0x00}, {0x09, 0x01, 0x09, 0x00},
{0x17, 0x01, 0x09, 0x00}, {0x28, 0x01, 0x09, 0x01},
{0x02, 0x01, 0x8e, 0x00}, {0x09, 0x01, 0x8e, 0x00},
{0x17, 0x01, 0x8e, 0x00}, {0x28, 0x01, 0x8e, 0x01}
},
{
{0x03, 0x01, 0x09, 0x00}, {0x06, 0x01, 0x09, 0x00},
{0x0a, 0x01, 0x09, 0x00}, {0x0f, 0x01, 0x09, 0x00},
{0x18, 0x01, 0x09, 0x00}, {0x1f, 0x01, 0x09, 0x00},
{0x29, 0x01, 0x09, 0x00}, {0x38, 0x01, 0x09, 0x01},
{0x03, 0x01, 0x8e, 0x00}, {0x06, 0x01, 0x8e, 0x00},
{0x0a, 0x01, 0x8e, 0x00}, {0x0f, 0x01, 0x8e, 0x00},
{0x18, 0x01, 0x8e, 0x00}, {0x1f, 0x01, 0x8e, 0x00},
{0x29, 0x01, 0x8e, 0x00}, {0x38, 0x01, 0x8e, 0x01}
},
{
{0x02, 0x01, 0x90, 0x00}, {0x09, 0x01, 0x90, 0x00},
{0x17, 0x01, 0x90, 0x00}, {0x28, 0x01, 0x90, 0x01},
{0x02, 0x01, 0x91, 0x00}, {0x09, 0x01, 0x91, 0x00},
{0x17, 0x01, 0x91, 0x00}, {0x28, 0x01, 0x91, 0x01},
{0x02, 0x01, 0x94, 0x00}, {0x09, 0x01, 0x94, 0x00},
{0x17, 0x01, 0x94, 0x00}, {0x28, 0x01, 0x94, 0x01},
{0x02, 0x01, 0x9f, 0x00}, {0x09, 0x01, 0x9f, 0x00},
{0x17, 0x01, 0x9f, 0x00}, {0x28, 0x01, 0x9f, 0x01}
},
{
{0x03, 0x01, 0x90, 0x00}, {0x06, 0x01, 0x90, 0x00},
{0x0a, 0x01, 0x90, 0x00}, {0x0f, 0x01, 0x90, 0x00},
{0x18, 0x01, 0x90, 0x00}, {0x1f, 0x01, 0x90, 0x00},
{0x29, 0x01, 0x90, 0x00}, {0x38, 0x01, 0x90, 0x01},
{0x03, 0x01, 0x91, 0x00}, {0x06, 0x01, 0x91, 0x00},
{0x0a, 0x01, 0x91, 0x00}, {0x0f, 0x01, 0x91, 0x00},
{0x18, 0x01, 0x91, 0x00}, {0x1f, 0x01, 0x91, 0x00},
{0x29, 0x01, 0x91, 0x00}, {0x38, 0x01, 0x91, 0x01}
},
{
{0x03, 0x01, 0x94, 0x00}, {0x06, 0x01, 0x94, 0x00},
{0x0a, 0x01, 0x94, 0x00}, {0x0f, 0x01, 0x94, 0x00},
{0x18, 0x01, 0x94, 0x00}, {0x1f, 0x01, 0x94, 0x00},
{0x29, 0x01, 0x94, 0x00}, {0x38, 0x01, 0x94, 0x01},
{0x03, 0x01, 0x9f, 0x00}, {0x06, 0x01, 0x9f, 0x00},
{0x0a, 0x01, 0x9f, 0x00}, {0x0f, 0x01, 0x9f, 0x00},
{0x18, 0x01, 0x9f, 0x00}, {0x1f, 0x01, 0x9f, 0x00},
{0x29, 0x01, 0x9f, 0x00}, {0x38, 0x01, 0x9f, 0x01}
},
/* 180 */
{
{0x00, 0x01, 0xab, 0x01}, {0x00, 0x01, 0xce, 0x01},
{0x00, 0x01, 0xd7, 0x01}, {0x00, 0x01, 0xe1, 0x01},
{0x00, 0x01, 0xec, 0x01}, {0x00, 0x01, 0xed, 0x01},
{0xbc, 0x00, 0x00, 0x00}, {0xbd, 0x00, 0x00, 0x00},
{0xc1, 0x00, 0x00, 0x00}, {0xc4, 0x00, 0x00, 0x00},
{0xc8, 0x00, 0x00, 0x00}, {0xcb, 0x00, 0x00, 0x00},
{0xd1, 0x00, 0x00, 0x00}, {0xd8, 0x00, 0x00, 0x00},
{0xe0, 0x00, 0x00, 0x00}, {0xee, 0x00, 0x00, 0x01}
},
{
{0x01, 0x01, 0xab, 0x00}, {0x16, 0x01, 0xab, 0x01},
{0x01, 0x01, 0xce, 0x00}, {0x16, 0x01, 0xce, 0x01},
{0x01, 0x01, 0xd7, 0x00}, {0x16, 0x01, 0xd7, 0x01},
{0x01, 0x01, 0xe1, 0x00}, {0x16, 0x01, 0xe1, 0x01},
{0x01, 0x01, 0xec, 0x00}, {0x16, 0x01, 0xec, 0x01},
{0x01, 0x01, 0xed, 0x00}, {0x16, 0x01, 0xed, 0x01},
{0x00, 0x01, 0xc7, 0x01}, {0x00, 0x01, 0xcf, 0x01},
{0x00, 0x01, 0xea, 0x01}, {0x00, 0x01, 0xeb, 0x01}
},
{
{0x02, 0x01, 0xab, 0x00}, {0x09, 0x01, 0xab, 0x00},
{0x17, 0x01, 0xab, 0x00}, {0x28, 0x01, 0xab, 0x01},
{0x02, 0x01, 0xce, 0x00}, {0x09, 0x01, 0xce, 0x00},
{0x17, 0x01, 0xce, 0x00}, {0x28, 0x01, 0xce, 0x01},
{0x02, 0x01, 0xd7, 0x00}, {0x09, 0x01, 0xd7, 0x00},
{0x17, 0x01, 0xd7, 0x00}, {0x28, 0x01, 0xd7, 0x01},
{0x02, 0x01, 0xe1, 0x00}, {0x09, 0x01, 0xe1, 0x00},
{0x17, 0x01, 0xe1, 0x00}, {0x28, 0x01, 0xe1, 0x01}
},
{
{0x03, 0x01, 0xab, 0x00}, {0x06, 0x01, 0xab, 0x00},
{0x0a, 0x01, 0xab, 0x00}, {0x0f, 0x01, 0xab, 0x00},
{0x18, 0x01, 0xab, 0x00}, {0x1f, 0x01, 0xab, 0x00},
{0x29, 0x01, 0xab, 0x00}, {0x38, 0x01, 0xab, 0x01},
{0x03, 0x01, 0xce, 0x00}, {0x06, 0x01, 0xce, 0x00},
{0x0a, 0x01, 0xce, 0x00}, {0x0f, 0x01, 0xce, 0x00},
{0x18, 0x01, 0xce, 0x00}, {0x1f, 0x01, 0xce, 0x00},
{0x29, 0x01, 0xce, 0x00}, {0x38, 0x01, 0xce, 0x01}
},
{
{0x03, 0x01, 0xd7, 0x00}, {0x06, 0x01, 0xd7, 0x00},
{0x0a, 0x01, 0xd7, 0x00}, {0x0f, 0x01, 0xd7, 0x00},
{0x18, 0x01, 0xd7, 0x00}, {0x1f, 0x01, 0xd7, 0x00},
{0x29, 0x01, 0xd7, 0x00}, {0x38, 0x01, 0xd7, 0x01},
{0x03, 0x01, 0xe1, 0x00}, {0x06, 0x01, 0xe1, 0x00},
{0x0a, 0x01, 0xe1, 0x00}, {0x0f, 0x01, 0xe1, 0x00},
{0x18, 0x01, 0xe1, 0x00}, {0x1f, 0x01, 0xe1, 0x00},
{0x29, 0x01, 0xe1, 0x00}, {0x38, 0x01, 0xe1, 0x01}
},
/* 185 */
{
{0x02, 0x01, 0xec, 0x00}, {0x09, 0x01, 0xec, 0x00},
{0x17, 0x01, 0xec, 0x00}, {0x28, 0x01, 0xec, 0x01},
{0x02, 0x01, 0xed, 0x00}, {0x09, 0x01, 0xed, 0x00},
{0x17, 0x01, 0xed, 0x00}, {0x28, 0x01, 0xed, 0x01},
{0x01, 0x01, 0xc7, 0x00}, {0x16, 0x01, 0xc7, 0x01},
{0x01, 0x01, 0xcf, 0x00}, {0x16, 0x01, 0xcf, 0x01},
{0x01, 0x01, 0xea, 0x00}, {0x16, 0x01, 0xea, 0x01},
{0x01, 0x01, 0xeb, 0x00}, {0x16, 0x01, 0xeb, 0x01}
},
{
{0x03, 0x01, 0xec, 0x00}, {0x06, 0x01, 0xec, 0x00},
{0x0a, 0x01, 0xec, 0x00}, {0x0f, 0x01, 0xec, 0x00},
{0x18, 0x01, 0xec, 0x00}, {0x1f, 0x01, 0xec, 0x00},
{0x29, 0x01, 0xec, 0x00}, {0x38, 0x01, 0xec, 0x01},
{0x03, 0x01, 0xed, 0x00}, {0x06, 0x01, 0xed, 0x00},
{0x0a, 0x01, 0xed, 0x00}, {0x0f, 0x01, 0xed, 0x00},
{0x18, 0x01, 0xed, 0x00}, {0x1f, 0x01, 0xed, 0x00},
{0x29, 0x01, 0xed, 0x00}, {0x38, 0x01, 0xed, 0x01}
},
{
{0x02, 0x01, 0xc7, 0x00}, {0x09, 0x01, 0xc7, 0x00},
{0x17, 0x01, 0xc7, 0x00}, {0x28, 0x01, 0xc7, 0x01},
{0x02, 0x01, 0xcf, 0x00}, {0x09, 0x01, 0xcf, 0x00},
{0x17, 0x01, 0xcf, 0x00}, {0x28, 0x01, 0xcf, 0x01},
{0x02, 0x01, 0xea, 0x00}, {0x09, 0x01, 0xea, 0x00},
{0x17, 0x01, 0xea, 0x00}, {0x28, 0x01, 0xea, 0x01},
{0x02, 0x01, 0xeb, 0x00}, {0x09, 0x01, 0xeb, 0x00},
{0x17, 0x01, 0xeb, 0x00}, {0x28, 0x01, 0xeb, 0x01}
},
{
{0x03, 0x01, 0xc7, 0x00}, {0x06, 0x01, 0xc7, 0x00},
{0x0a, 0x01, 0xc7, 0x00}, {0x0f, 0x01, 0xc7, 0x00},
{0x18, 0x01, 0xc7, 0x00}, {0x1f, 0x01, 0xc7, 0x00},
{0x29, 0x01, 0xc7, 0x00}, {0x38, 0x01, 0xc7, 0x01},
{0x03, 0x01, 0xcf, 0x00}, {0x06, 0x01, 0xcf, 0x00},
{0x0a, 0x01, 0xcf, 0x00}, {0x0f, 0x01, 0xcf, 0x00},
{0x18, 0x01, 0xcf, 0x00}, {0x1f, 0x01, 0xcf, 0x00},
{0x29, 0x01, 0xcf, 0x00}, {0x38, 0x01, 0xcf, 0x01}
},
{
{0x03, 0x01, 0xea, 0x00}, {0x06, 0x01, 0xea, 0x00},
{0x0a, 0x01, 0xea, 0x00}, {0x0f, 0x01, 0xea, 0x00},
{0x18, 0x01, 0xea, 0x00}, {0x1f, 0x01, 0xea, 0x00},
{0x29, 0x01, 0xea, 0x00}, {0x38, 0x01, 0xea, 0x01},
{0x03, 0x01, 0xeb, 0x00}, {0x06, 0x01, 0xeb, 0x00},
{0x0a, 0x01, 0xeb, 0x00}, {0x0f, 0x01, 0xeb, 0x00},
{0x18, 0x01, 0xeb, 0x00}, {0x1f, 0x01, 0xeb, 0x00},
{0x29, 0x01, 0xeb, 0x00}, {0x38, 0x01, 0xeb, 0x01}
},
/* 190 */
{
{0xc2, 0x00, 0x00, 0x00}, {0xc3, 0x00, 0x00, 0x00},
{0xc5, 0x00, 0x00, 0x00}, {0xc6, 0x00, 0x00, 0x00},
{0xc9, 0x00, 0x00, 0x00}, {0xca, 0x00, 0x00, 0x00},
{0xcc, 0x00, 0x00, 0x00}, {0xcd, 0x00, 0x00, 0x00},
{0xd2, 0x00, 0x00, 0x00}, {0xd5, 0x00, 0x00, 0x00},
{0xd9, 0x00, 0x00, 0x00}, {0xdc, 0x00, 0x00, 0x00},
{0xe1, 0x00, 0x00, 0x00}, {0xe7, 0x00, 0x00, 0x00},
{0xef, 0x00, 0x00, 0x00}, {0xf6, 0x00, 0x00, 0x01}
},
{
{0x00, 0x01, 0xc0, 0x01}, {0x00, 0x01, 0xc1, 0x01},
{0x00, 0x01, 0xc8, 0x01}, {0x00, 0x01, 0xc9, 0x01},
{0x00, 0x01, 0xca, 0x01}, {0x00, 0x01, 0xcd, 0x01},
{0x00, 0x01, 0xd2, 0x01}, {0x00, 0x01, 0xd5, 0x01},
{0x00, 0x01, 0xda, 0x01}, {0x00, 0x01, 0xdb, 0x01},
{0x00, 0x01, 0xee, 0x01}, {0x00, 0x01, 0xf0, 0x01},
{0x00, 0x01, 0xf2, 0x01}, {0x00, 0x01, 0xf3, 0x01},
{0x00, 0x01, 0xff, 0x01}, {0xce, 0x00, 0x00, 0x00}
},
{
{0x01, 0x01, 0xc0, 0x00}, {0x16, 0x01, 0xc0, 0x01},
{0x01, 0x01, 0xc1, 0x00}, {0x16, 0x01, 0xc1, 0x01},
{0x01, 0x01, 0xc8, 0x00}, {0x16, 0x01, 0xc8, 0x01},
{0x01, 0x01, 0xc9, 0x00}, {0x16, 0x01, 0xc9, 0x01},
{0x01, 0x01, 0xca, 0x00}, {0x16, 0x01, 0xca, 0x01},
{0x01, 0x01, 0xcd, 0x00}, {0x16, 0x01, 0xcd, 0x01},
{0x01, 0x01, 0xd2, 0x00}, {0x16, 0x01, 0xd2, 0x01},
{0x01, 0x01, 0xd5, 0x00}, {0x16, 0x01, 0xd5, 0x01}
},
{
{0x02, 0x01, 0xc0, 0x00}, {0x09, 0x01, 0xc0, 0x00},
{0x17, 0x01, 0xc0, 0x00}, {0x28, 0x01, 0xc0, 0x01},
{0x02, 0x01, 0xc1, 0x00}, {0x09, 0x01, 0xc1, 0x00},
{0x17, 0x01, 0xc1, 0x00}, {0x28, 0x01, 0xc1, 0x01},
{0x02, 0x01, 0xc8, 0x00}, {0x09, 0x01, 0xc8, 0x00},
{0x17, 0x01, 0xc8, 0x00}, {0x28, 0x01, 0xc8, 0x01},
{0x02, 0x01, 0xc9, 0x00}, {0x09, 0x01, 0xc9, 0x00},
{0x17, 0x01, 0xc9, 0x00}, {0x28, 0x01, 0xc9, 0x01}
},
{
{0x03, 0x01, 0xc0, 0x00}, {0x06, 0x01, 0xc0, 0x00},
{0x0a, 0x01, 0xc0, 0x00}, {0x0f, 0x01, 0xc0, 0x00},
{0x18, 0x01, 0xc0, 0x00}, {0x1f, 0x01, 0xc0, 0x00},
{0x29, 0x01, 0xc0, 0x00}, {0x38, 0x01, 0xc0, 0x01},
{0x03, 0x01, 0xc1, 0x00}, {0x06, 0x01, 0xc1, 0x00},
{0x0a, 0x01, 0xc1, 0x00}, {0x0f, 0x01, 0xc1, 0x00},
{0x18, 0x01, 0xc1, 0x00}, {0x1f, 0x01, 0xc1, 0x00},
{0x29, 0x01, 0xc1, 0x00}, {0x38, 0x01, 0xc1, 0x01}
},
/* 195 */
{
{0x03, 0x01, 0xc8, 0x00}, {0x06, 0x01, 0xc8, 0x00},
{0x0a, 0x01, 0xc8, 0x00}, {0x0f, 0x01, 0xc8, 0x00},
{0x18, 0x01, 0xc8, 0x00}, {0x1f, 0x01, 0xc8, 0x00},
{0x29, 0x01, 0xc8, 0x00}, {0x38, 0x01, 0xc8, 0x01},
{0x03, 0x01, 0xc9, 0x00}, {0x06, 0x01, 0xc9, 0x00},
{0x0a, 0x01, 0xc9, 0x00}, {0x0f, 0x01, 0xc9, 0x00},
{0x18, 0x01, 0xc9, 0x00}, {0x1f, 0x01, 0xc9, 0x00},
{0x29, 0x01, 0xc9, 0x00}, {0x38, 0x01, 0xc9, 0x01}
},
{
{0x02, 0x01, 0xca, 0x00}, {0x09, 0x01, 0xca, 0x00},
{0x17, 0x01, 0xca, 0x00}, {0x28, 0x01, 0xca, 0x01},
{0x02, 0x01, 0xcd, 0x00}, {0x09, 0x01, 0xcd, 0x00},
{0x17, 0x01, 0xcd, 0x00}, {0x28, 0x01, 0xcd, 0x01},
{0x02, 0x01, 0xd2, 0x00}, {0x09, 0x01, 0xd2, 0x00},
{0x17, 0x01, 0xd2, 0x00}, {0x28, 0x01, 0xd2, 0x01},
{0x02, 0x01, 0xd5, 0x00}, {0x09, 0x01, 0xd5, 0x00},
{0x17, 0x01, 0xd5, 0x00}, {0x28, 0x01, 0xd5, 0x01}
},
{
{0x03, 0x01, 0xca, 0x00}, {0x06, 0x01, 0xca, 0x00},
{0x0a, 0x01, 0xca, 0x00}, {0x0f, 0x01, 0xca, 0x00},
{0x18, 0x01, 0xca, 0x00}, {0x1f, 0x01, 0xca, 0x00},
{0x29, 0x01, 0xca, 0x00}, {0x38, 0x01, 0xca, 0x01},
{0x03, 0x01, 0xcd, 0x00}, {0x06, 0x01, 0xcd, 0x00},
{0x0a, 0x01, 0xcd, 0x00}, {0x0f, 0x01, 0xcd, 0x00},
{0x18, 0x01, 0xcd, 0x00}, {0x1f, 0x01, 0xcd, 0x00},
{0x29, 0x01, 0xcd, 0x00}, {0x38, 0x01, 0xcd, 0x01}
},
{
{0x03, 0x01, 0xd2, 0x00}, {0x06, 0x01, 0xd2, 0x00},
{0x0a, 0x01, 0xd2, 0x00}, {0x0f, 0x01, 0xd2, 0x00},
{0x18, 0x01, 0xd2, 0x00}, {0x1f, 0x01, 0xd2, 0x00},
{0x29, 0x01, 0xd2, 0x00}, {0x38, 0x01, 0xd2, 0x01},
{0x03, 0x01, 0xd5, 0x00}, {0x06, 0x01, 0xd5, 0x00},
{0x0a, 0x01, 0xd5, 0x00}, {0x0f, 0x01, 0xd5, 0x00},
{0x18, 0x01, 0xd5, 0x00}, {0x1f, 0x01, 0xd5, 0x00},
{0x29, 0x01, 0xd5, 0x00}, {0x38, 0x01, 0xd5, 0x01}
},
{
{0x01, 0x01, 0xda, 0x00}, {0x16, 0x01, 0xda, 0x01},
{0x01, 0x01, 0xdb, 0x00}, {0x16, 0x01, 0xdb, 0x01},
{0x01, 0x01, 0xee, 0x00}, {0x16, 0x01, 0xee, 0x01},
{0x01, 0x01, 0xf0, 0x00}, {0x16, 0x01, 0xf0, 0x01},
{0x01, 0x01, 0xf2, 0x00}, {0x16, 0x01, 0xf2, 0x01},
{0x01, 0x01, 0xf3, 0x00}, {0x16, 0x01, 0xf3, 0x01},
{0x01, 0x01, 0xff, 0x00}, {0x16, 0x01, 0xff, 0x01},
{0x00, 0x01, 0xcb, 0x01}, {0x00, 0x01, 0xcc, 0x01}
},
/* 200 */
{
{0x02, 0x01, 0xda, 0x00}, {0x09, 0x01, 0xda, 0x00},
{0x17, 0x01, 0xda, 0x00}, {0x28, 0x01, 0xda, 0x01},
{0x02, 0x01, 0xdb, 0x00}, {0x09, 0x01, 0xdb, 0x00},
{0x17, 0x01, 0xdb, 0x00}, {0x28, 0x01, 0xdb, 0x01},
{0x02, 0x01, 0xee, 0x00}, {0x09, 0x01, 0xee, 0x00},
{0x17, 0x01, 0xee, 0x00}, {0x28, 0x01, 0xee, 0x01},
{0x02, 0x01, 0xf0, 0x00}, {0x09, 0x01, 0xf0, 0x00},
{0x17, 0x01, 0xf0, 0x00}, {0x28, 0x01, 0xf0, 0x01}
},
{
{0x03, 0x01, 0xda, 0x00}, {0x06, 0x01, 0xda, 0x00},
{0x0a, 0x01, 0xda, 0x00}, {0x0f, 0x01, 0xda, 0x00},
{0x18, 0x01, 0xda, 0x00}, {0x1f, 0x01, 0xda, 0x00},
{0x29, 0x01, 0xda, 0x00}, {0x38, 0x01, 0xda, 0x01},
{0x03, 0x01, 0xdb, 0x00}, {0x06, 0x01, 0xdb, 0x00},
{0x0a, 0x01, 0xdb, 0x00}, {0x0f, 0x01, 0xdb, 0x00},
{0x18, 0x01, 0xdb, 0x00}, {0x1f, 0x01, 0xdb, 0x00},
{0x29, 0x01, 0xdb, 0x00}, {0x38, 0x01, 0xdb, 0x01}
},
{
{0x03, 0x01, 0xee, 0x00}, {0x06, 0x01, 0xee, 0x00},
{0x0a, 0x01, 0xee, 0x00}, {0x0f, 0x01, 0xee, 0x00},
{0x18, 0x01, 0xee, 0x00}, {0x1f, 0x01, 0xee, 0x00},
{0x29, 0x01, 0xee, 0x00}, {0x38, 0x01, 0xee, 0x01},
{0x03, 0x01, 0xf0, 0x00}, {0x06, 0x01, 0xf0, 0x00},
{0x0a, 0x01, 0xf0, 0x00}, {0x0f, 0x01, 0xf0, 0x00},
{0x18, 0x01, 0xf0, 0x00}, {0x1f, 0x01, 0xf0, 0x00},
{0x29, 0x01, 0xf0, 0x00}, {0x38, 0x01, 0xf0, 0x01}
},
{
{0x02, 0x01, 0xf2, 0x00}, {0x09, 0x01, 0xf2, 0x00},
{0x17, 0x01, 0xf2, 0x00}, {0x28, 0x01, 0xf2, 0x01},
{0x02, 0x01, 0xf3, 0x00}, {0x09, 0x01, 0xf3, 0x00},
{0x17, 0x01, 0xf3, 0x00}, {0x28, 0x01, 0xf3, 0x01},
{0x02, 0x01, 0xff, 0x00}, {0x09, 0x01, 0xff, 0x00},
{0x17, 0x01, 0xff, 0x00}, {0x28, 0x01, 0xff, 0x01},
{0x01, 0x01, 0xcb, 0x00}, {0x16, 0x01, 0xcb, 0x01},
{0x01, 0x01, 0xcc, 0x00}, {0x16, 0x01, 0xcc, 0x01}
},
{
{0x03, 0x01, 0xf2, 0x00}, {0x06, 0x01, 0xf2, 0x00},
{0x0a, 0x01, 0xf2, 0x00}, {0x0f, 0x01, 0xf2, 0x00},
{0x18, 0x01, 0xf2, 0x00}, {0x1f, 0x01, 0xf2, 0x00},
{0x29, 0x01, 0xf2, 0x00}, {0x38, 0x01, 0xf2, 0x01},
{0x03, 0x01, 0xf3, 0x00}, {0x06, 0x01, 0xf3, 0x00},
{0x0a, 0x01, 0xf3, 0x00}, {0x0f, 0x01, 0xf3, 0x00},
{0x18, 0x01, 0xf3, 0x00}, {0x1f, 0x01, 0xf3, 0x00},
{0x29, 0x01, 0xf3, 0x00}, {0x38, 0x01, 0xf3, 0x01}
},
/* 205 */
{
{0x03, 0x01, 0xff, 0x00}, {0x06, 0x01, 0xff, 0x00},
{0x0a, 0x01, 0xff, 0x00}, {0x0f, 0x01, 0xff, 0x00},
{0x18, 0x01, 0xff, 0x00}, {0x1f, 0x01, 0xff, 0x00},
{0x29, 0x01, 0xff, 0x00}, {0x38, 0x01, 0xff, 0x01},
{0x02, 0x01, 0xcb, 0x00}, {0x09, 0x01, 0xcb, 0x00},
{0x17, 0x01, 0xcb, 0x00}, {0x28, 0x01, 0xcb, 0x01},
{0x02, 0x01, 0xcc, 0x00}, {0x09, 0x01, 0xcc, 0x00},
{0x17, 0x01, 0xcc, 0x00}, {0x28, 0x01, 0xcc, 0x01}
},
{
{0x03, 0x01, 0xcb, 0x00}, {0x06, 0x01, 0xcb, 0x00},
{0x0a, 0x01, 0xcb, 0x00}, {0x0f, 0x01, 0xcb, 0x00},
{0x18, 0x01, 0xcb, 0x00}, {0x1f, 0x01, 0xcb, 0x00},
{0x29, 0x01, 0xcb, 0x00}, {0x38, 0x01, 0xcb, 0x01},
{0x03, 0x01, 0xcc, 0x00}, {0x06, 0x01, 0xcc, 0x00},
{0x0a, 0x01, 0xcc, 0x00}, {0x0f, 0x01, 0xcc, 0x00},
{0x18, 0x01, 0xcc, 0x00}, {0x1f, 0x01, 0xcc, 0x00},
{0x29, 0x01, 0xcc, 0x00}, {0x38, 0x01, 0xcc, 0x01}
},
{
{0xd3, 0x00, 0x00, 0x00}, {0xd4, 0x00, 0x00, 0x00},
{0xd6, 0x00, 0x00, 0x00}, {0xd7, 0x00, 0x00, 0x00},
{0xda, 0x00, 0x00, 0x00}, {0xdb, 0x00, 0x00, 0x00},
{0xdd, 0x00, 0x00, 0x00}, {0xde, 0x00, 0x00, 0x00},
{0xe2, 0x00, 0x00, 0x00}, {0xe4, 0x00, 0x00, 0x00},
{0xe8, 0x00, 0x00, 0x00}, {0xeb, 0x00, 0x00, 0x00},
{0xf0, 0x00, 0x00, 0x00}, {0xf3, 0x00, 0x00, 0x00},
{0xf7, 0x00, 0x00, 0x00}, {0xfa, 0x00, 0x00, 0x01}
},
{
{0x00, 0x01, 0xd3, 0x01}, {0x00, 0x01, 0xd4, 0x01},
{0x00, 0x01, 0xd6, 0x01}, {0x00, 0x01, 0xdd, 0x01},
{0x00, 0x01, 0xde, 0x01}, {0x00, 0x01, 0xdf, 0x01},
{0x00, 0x01, 0xf1, 0x01}, {0x00, 0x01, 0xf4, 0x01},
{0x00, 0x01, 0xf5, 0x01}, {0x00, 0x01, 0xf6, 0x01},
{0x00, 0x01, 0xf7, 0x01}, {0x00, 0x01, 0xf8, 0x01},
{0x00, 0x01, 0xfa, 0x01}, {0x00, 0x01, 0xfb, 0x01},
{0x00, 0x01, 0xfc, 0x01}, {0x00, 0x01, 0xfd, 0x01}
},
{
{0x01, 0x01, 0xd3, 0x00}, {0x16, 0x01, 0xd3, 0x01},
{0x01, 0x01, 0xd4, 0x00}, {0x16, 0x01, 0xd4, 0x01},
{0x01, 0x01, 0xd6, 0x00}, {0x16, 0x01, 0xd6, 0x01},
{0x01, 0x01, 0xdd, 0x00}, {0x16, 0x01, 0xdd, 0x01},
{0x01, 0x01, 0xde, 0x00}, {0x16, 0x01, 0xde, 0x01},
{0x01, 0x01, 0xdf, 0x00}, {0x16, 0x01, 0xdf, 0x01},
{0x01, 0x01, 0xf1, 0x00}, {0x16, 0x01, 0xf1, 0x01},
{0x01, 0x01, 0xf4, 0x00}, {0x16, 0x01, 0xf4, 0x01}
},
/* 210 */
{
{0x02, 0x01, 0xd3, 0x00}, {0x09, 0x01, 0xd3, 0x00},
{0x17, 0x01, 0xd3, 0x00}, {0x28, 0x01, 0xd3, 0x01},
{0x02, 0x01, 0xd4, 0x00}, {0x09, 0x01, 0xd4, 0x00},
{0x17, 0x01, 0xd4, 0x00}, {0x28, 0x01, 0xd4, 0x01},
{0x02, 0x01, 0xd6, 0x00}, {0x09, 0x01, 0xd6, 0x00},
{0x17, 0x01, 0xd6, 0x00}, {0x28, 0x01, 0xd6, 0x01},
{0x02, 0x01, 0xdd, 0x00}, {0x09, 0x01, 0xdd, 0x00},
{0x17, 0x01, 0xdd, 0x00}, {0x28, 0x01, 0xdd, 0x01}
},
{
{0x03, 0x01, 0xd3, 0x00}, {0x06, 0x01, 0xd3, 0x00},
{0x0a, 0x01, 0xd3, 0x00}, {0x0f, 0x01, 0xd3, 0x00},
{0x18, 0x01, 0xd3, 0x00}, {0x1f, 0x01, 0xd3, 0x00},
{0x29, 0x01, 0xd3, 0x00}, {0x38, 0x01, 0xd3, 0x01},
{0x03, 0x01, 0xd4, 0x00}, {0x06, 0x01, 0xd4, 0x00},
{0x0a, 0x01, 0xd4, 0x00}, {0x0f, 0x01, 0xd4, 0x00},
{0x18, 0x01, 0xd4, 0x00}, {0x1f, 0x01, 0xd4, 0x00},
{0x29, 0x01, 0xd4, 0x00}, {0x38, 0x01, 0xd4, 0x01}
},
{
{0x03, 0x01, 0xd6, 0x00}, {0x06, 0x01, 0xd6, 0x00},
{0x0a, 0x01, 0xd6, 0x00}, {0x0f, 0x01, 0xd6, 0x00},
{0x18, 0x01, 0xd6, 0x00}, {0x1f, 0x01, 0xd6, 0x00},
{0x29, 0x01, 0xd6, 0x00}, {0x38, 0x01, 0xd6, 0x01},
{0x03, 0x01, 0xdd, 0x00}, {0x06, 0x01, 0xdd, 0x00},
{0x0a, 0x01, 0xdd, 0x00}, {0x0f, 0x01, 0xdd, 0x00},
{0x18, 0x01, 0xdd, 0x00}, {0x1f, 0x01, 0xdd, 0x00},
{0x29, 0x01, 0xdd, 0x00}, {0x38, 0x01, 0xdd, 0x01}
},
{
{0x02, 0x01, 0xde, 0x00}, {0x09, 0x01, 0xde, 0x00},
{0x17, 0x01, 0xde, 0x00}, {0x28, 0x01, 0xde, 0x01},
{0x02, 0x01, 0xdf, 0x00}, {0x09, 0x01, 0xdf, 0x00},
{0x17, 0x01, 0xdf, 0x00}, {0x28, 0x01, 0xdf, 0x01},
{0x02, 0x01, 0xf1, 0x00}, {0x09, 0x01, 0xf1, 0x00},
{0x17, 0x01, 0xf1, 0x00}, {0x28, 0x01, 0xf1, 0x01},
{0x02, 0x01, 0xf4, 0x00}, {0x09, 0x01, 0xf4, 0x00},
{0x17, 0x01, 0xf4, 0x00}, {0x28, 0x01, 0xf4, 0x01}
},
{
{0x03, 0x01, 0xde, 0x00}, {0x06, 0x01, 0xde, 0x00},
{0x0a, 0x01, 0xde, 0x00}, {0x0f, 0x01, 0xde, 0x00},
{0x18, 0x01, 0xde, 0x00}, {0x1f, 0x01, 0xde, 0x00},
{0x29, 0x01, 0xde, 0x00}, {0x38, 0x01, 0xde, 0x01},
{0x03, 0x01, 0xdf, 0x00}, {0x06, 0x01, 0xdf, 0x00},
{0x0a, 0x01, 0xdf, 0x00}, {0x0f, 0x01, 0xdf, 0x00},
{0x18, 0x01, 0xdf, 0x00}, {0x1f, 0x01, 0xdf, 0x00},
{0x29, 0x01, 0xdf, 0x00}, {0x38, 0x01, 0xdf, 0x01}
},
/* 215 */
{
{0x03, 0x01, 0xf1, 0x00}, {0x06, 0x01, 0xf1, 0x00},
{0x0a, 0x01, 0xf1, 0x00}, {0x0f, 0x01, 0xf1, 0x00},
{0x18, 0x01, 0xf1, 0x00}, {0x1f, 0x01, 0xf1, 0x00},
{0x29, 0x01, 0xf1, 0x00}, {0x38, 0x01, 0xf1, 0x01},
{0x03, 0x01, 0xf4, 0x00}, {0x06, 0x01, 0xf4, 0x00},
{0x0a, 0x01, 0xf4, 0x00}, {0x0f, 0x01, 0xf4, 0x00},
{0x18, 0x01, 0xf4, 0x00}, {0x1f, 0x01, 0xf4, 0x00},
{0x29, 0x01, 0xf4, 0x00}, {0x38, 0x01, 0xf4, 0x01}
},
{
{0x01, 0x01, 0xf5, 0x00}, {0x16, 0x01, 0xf5, 0x01},
{0x01, 0x01, 0xf6, 0x00}, {0x16, 0x01, 0xf6, 0x01},
{0x01, 0x01, 0xf7, 0x00}, {0x16, 0x01, 0xf7, 0x01},
{0x01, 0x01, 0xf8, 0x00}, {0x16, 0x01, 0xf8, 0x01},
{0x01, 0x01, 0xfa, 0x00}, {0x16, 0x01, 0xfa, 0x01},
{0x01, 0x01, 0xfb, 0x00}, {0x16, 0x01, 0xfb, 0x01},
{0x01, 0x01, 0xfc, 0x00}, {0x16, 0x01, 0xfc, 0x01},
{0x01, 0x01, 0xfd, 0x00}, {0x16, 0x01, 0xfd, 0x01}
},
{
{0x02, 0x01, 0xf5, 0x00}, {0x09, 0x01, 0xf5, 0x00},
{0x17, 0x01, 0xf5, 0x00}, {0x28, 0x01, 0xf5, 0x01},
{0x02, 0x01, 0xf6, 0x00}, {0x09, 0x01, 0xf6, 0x00},
{0x17, 0x01, 0xf6, 0x00}, {0x28, 0x01, 0xf6, 0x01},
{0x02, 0x01, 0xf7, 0x00}, {0x09, 0x01, 0xf7, 0x00},
{0x17, 0x01, 0xf7, 0x00}, {0x28, 0x01, 0xf7, 0x01},
{0x02, 0x01, 0xf8, 0x00}, {0x09, 0x01, 0xf8, 0x00},
{0x17, 0x01, 0xf8, 0x00}, {0x28, 0x01, 0xf8, 0x01}
},
{
{0x03, 0x01, 0xf5, 0x00}, {0x06, 0x01, 0xf5, 0x00},
{0x0a, 0x01, 0xf5, 0x00}, {0x0f, 0x01, 0xf5, 0x00},
{0x18, 0x01, 0xf5, 0x00}, {0x1f, 0x01, 0xf5, 0x00},
{0x29, 0x01, 0xf5, 0x00}, {0x38, 0x01, 0xf5, 0x01},
{0x03, 0x01, 0xf6, 0x00}, {0x06, 0x01, 0xf6, 0x00},
{0x0a, 0x01, 0xf6, 0x00}, {0x0f, 0x01, 0xf6, 0x00},
{0x18, 0x01, 0xf6, 0x00}, {0x1f, 0x01, 0xf6, 0x00},
{0x29, 0x01, 0xf6, 0x00}, {0x38, 0x01, 0xf6, 0x01}
},
{
{0x03, 0x01, 0xf7, 0x00}, {0x06, 0x01, 0xf7, 0x00},
{0x0a, 0x01, 0xf7, 0x00}, {0x0f, 0x01, 0xf7, 0x00},
{0x18, 0x01, 0xf7, 0x00}, {0x1f, 0x01, 0xf7, 0x00},
{0x29, 0x01, 0xf7, 0x00}, {0x38, 0x01, 0xf7, 0x01},
{0x03, 0x01, 0xf8, 0x00}, {0x06, 0x01, 0xf8, 0x00},
{0x0a, 0x01, 0xf8, 0x00}, {0x0f, 0x01, 0xf8, 0x00},
{0x18, 0x01, 0xf8, 0x00}, {0x1f, 0x01, 0xf8, 0x00},
{0x29, 0x01, 0xf8, 0x00}, {0x38, 0x01, 0xf8, 0x01}
},
/* 220 */
{
{0x02, 0x01, 0xfa, 0x00}, {0x09, 0x01, 0xfa, 0x00},
{0x17, 0x01, 0xfa, 0x00}, {0x28, 0x01, 0xfa, 0x01},
{0x02, 0x01, 0xfb, 0x00}, {0x09, 0x01, 0xfb, 0x00},
{0x17, 0x01, 0xfb, 0x00}, {0x28, 0x01, 0xfb, 0x01},
{0x02, 0x01, 0xfc, 0x00}, {0x09, 0x01, 0xfc, 0x00},
{0x17, 0x01, 0xfc, 0x00}, {0x28, 0x01, 0xfc, 0x01},
{0x02, 0x01, 0xfd, 0x00}, {0x09, 0x01, 0xfd, 0x00},
{0x17, 0x01, 0xfd, 0x00}, {0x28, 0x01, 0xfd, 0x01}
},
{
{0x03, 0x01, 0xfa, 0x00}, {0x06, 0x01, 0xfa, 0x00},
{0x0a, 0x01, 0xfa, 0x00}, {0x0f, 0x01, 0xfa, 0x00},
{0x18, 0x01, 0xfa, 0x00}, {0x1f, 0x01, 0xfa, 0x00},
{0x29, 0x01, 0xfa, 0x00}, {0x38, 0x01, 0xfa, 0x01},
{0x03, 0x01, 0xfb, 0x00}, {0x06, 0x01, 0xfb, 0x00},
{0x0a, 0x01, 0xfb, 0x00}, {0x0f, 0x01, 0xfb, 0x00},
{0x18, 0x01, 0xfb, 0x00}, {0x1f, 0x01, 0xfb, 0x00},
{0x29, 0x01, 0xfb, 0x00}, {0x38, 0x01, 0xfb, 0x01}
},
{
{0x03, 0x01, 0xfc, 0x00}, {0x06, 0x01, 0xfc, 0x00},
{0x0a, 0x01, 0xfc, 0x00}, {0x0f, 0x01, 0xfc, 0x00},
{0x18, 0x01, 0xfc, 0x00}, {0x1f, 0x01, 0xfc, 0x00},
{0x29, 0x01, 0xfc, 0x00}, {0x38, 0x01, 0xfc, 0x01},
{0x03, 0x01, 0xfd, 0x00}, {0x06, 0x01, 0xfd, 0x00},
{0x0a, 0x01, 0xfd, 0x00}, {0x0f, 0x01, 0xfd, 0x00},
{0x18, 0x01, 0xfd, 0x00}, {0x1f, 0x01, 0xfd, 0x00},
{0x29, 0x01, 0xfd, 0x00}, {0x38, 0x01, 0xfd, 0x01}
},
{
{0x00, 0x01, 0xfe, 0x01}, {0xe3, 0x00, 0x00, 0x00},
{0xe5, 0x00, 0x00, 0x00}, {0xe6, 0x00, 0x00, 0x00},
{0xe9, 0x00, 0x00, 0x00}, {0xea, 0x00, 0x00, 0x00},
{0xec, 0x00, 0x00, 0x00}, {0xed, 0x00, 0x00, 0x00},
{0xf1, 0x00, 0x00, 0x00}, {0xf2, 0x00, 0x00, 0x00},
{0xf4, 0x00, 0x00, 0x00}, {0xf5, 0x00, 0x00, 0x00},
{0xf8, 0x00, 0x00, 0x00}, {0xf9, 0x00, 0x00, 0x00},
{0xfb, 0x00, 0x00, 0x00}, {0xfc, 0x00, 0x00, 0x01}
},
{
{0x01, 0x01, 0xfe, 0x00}, {0x16, 0x01, 0xfe, 0x01},
{0x00, 0x01, 0x02, 0x01}, {0x00, 0x01, 0x03, 0x01},
{0x00, 0x01, 0x04, 0x01}, {0x00, 0x01, 0x05, 0x01},
{0x00, 0x01, 0x06, 0x01}, {0x00, 0x01, 0x07, 0x01},
{0x00, 0x01, 0x08, 0x01}, {0x00, 0x01, 0x0b, 0x01},
{0x00, 0x01, 0x0c, 0x01}, {0x00, 0x01, 0x0e, 0x01},
{0x00, 0x01, 0x0f, 0x01}, {0x00, 0x01, 0x10, 0x01},
{0x00, 0x01, 0x11, 0x01}, {0x00, 0x01, 0x12, 0x01}
},
/* 225 */
{
{0x02, 0x01, 0xfe, 0x00}, {0x09, 0x01, 0xfe, 0x00},
{0x17, 0x01, 0xfe, 0x00}, {0x28, 0x01, 0xfe, 0x01},
{0x01, 0x01, 0x02, 0x00}, {0x16, 0x01, 0x02, 0x01},
{0x01, 0x01, 0x03, 0x00}, {0x16, 0x01, 0x03, 0x01},
{0x01, 0x01, 0x04, 0x00}, {0x16, 0x01, 0x04, 0x01},
{0x01, 0x01, 0x05, 0x00}, {0x16, 0x01, 0x05, 0x01},
{0x01, 0x01, 0x06, 0x00}, {0x16, 0x01, 0x06, 0x01},
{0x01, 0x01, 0x07, 0x00}, {0x16, 0x01, 0x07, 0x01}
},
{
{0x03, 0x01, 0xfe, 0x00}, {0x06, 0x01, 0xfe, 0x00},
{0x0a, 0x01, 0xfe, 0x00}, {0x0f, 0x01, 0xfe, 0x00},
{0x18, 0x01, 0xfe, 0x00}, {0x1f, 0x01, 0xfe, 0x00},
{0x29, 0x01, 0xfe, 0x00}, {0x38, 0x01, 0xfe, 0x01},
{0x02, 0x01, 0x02, 0x00}, {0x09, 0x01, 0x02, 0x00},
{0x17, 0x01, 0x02, 0x00}, {0x28, 0x01, 0x02, 0x01},
{0x02, 0x01, 0x03, 0x00}, {0x09, 0x01, 0x03, 0x00},
{0x17, 0x01, 0x03, 0x00}, {0x28, 0x01, 0x03, 0x01}
},
{
{0x03, 0x01, 0x02, 0x00}, {0x06, 0x01, 0x02, 0x00},
{0x0a, 0x01, 0x02, 0x00}, {0x0f, 0x01, 0x02, 0x00},
{0x18, 0x01, 0x02, 0x00}, {0x1f, 0x01, 0x02, 0x00},
{0x29, 0x01, 0x02, 0x00}, {0x38, 0x01, 0x02, 0x01},
{0x03, 0x01, 0x03, 0x00}, {0x06, 0x01, 0x03, 0x00},
{0x0a, 0x01, 0x03, 0x00}, {0x0f, 0x01, 0x03, 0x00},
{0x18, 0x01, 0x03, 0x00}, {0x1f, 0x01, 0x03, 0x00},
{0x29, 0x01, 0x03, 0x00}, {0x38, 0x01, 0x03, 0x01}
},
{
{0x02, 0x01, 0x04, 0x00}, {0x09, 0x01, 0x04, 0x00},
{0x17, 0x01, 0x04, 0x00}, {0x28, 0x01, 0x04, 0x01},
{0x02, 0x01, 0x05, 0x00}, {0x09, 0x01, 0x05, 0x00},
{0x17, 0x01, 0x05, 0x00}, {0x28, 0x01, 0x05, 0x01},
{0x02, 0x01, 0x06, 0x00}, {0x09, 0x01, 0x06, 0x00},
{0x17, 0x01, 0x06, 0x00}, {0x28, 0x01, 0x06, 0x01},
{0x02, 0x01, 0x07, 0x00}, {0x09, 0x01, 0x07, 0x00},
{0x17, 0x01, 0x07, 0x00}, {0x28, 0x01, 0x07, 0x01}
},
{
{0x03, 0x01, 0x04, 0x00}, {0x06, 0x01, 0x04, 0x00},
{0x0a, 0x01, 0x04, 0x00}, {0x0f, 0x01, 0x04, 0x00},
{0x18, 0x01, 0x04, 0x00}, {0x1f, 0x01, 0x04, 0x00},
{0x29, 0x01, 0x04, 0x00}, {0x38, 0x01, 0x04, 0x01},
{0x03, 0x01, 0x05, 0x00}, {0x06, 0x01, 0x05, 0x00},
{0x0a, 0x01, 0x05, 0x00}, {0x0f, 0x01, 0x05, 0x00},
{0x18, 0x01, 0x05, 0x00}, {0x1f, 0x01, 0x05, 0x00},
{0x29, 0x01, 0x05, 0x00}, {0x38, 0x01, 0x05, 0x01}
},
/* 230 */
{
{0x03, 0x01, 0x06, 0x00}, {0x06, 0x01, 0x06, 0x00},
{0x0a, 0x01, 0x06, 0x00}, {0x0f, 0x01, 0x06, 0x00},
{0x18, 0x01, 0x06, 0x00}, {0x1f, 0x01, 0x06, 0x00},
{0x29, 0x01, 0x06, 0x00}, {0x38, 0x01, 0x06, 0x01},
{0x03, 0x01, 0x07, 0x00}, {0x06, 0x01, 0x07, 0x00},
{0x0a, 0x01, 0x07, 0x00}, {0x0f, 0x01, 0x07, 0x00},
{0x18, 0x01, 0x07, 0x00}, {0x1f, 0x01, 0x07, 0x00},
{0x29, 0x01, 0x07, 0x00}, {0x38, 0x01, 0x07, 0x01}
},
{
{0x01, 0x01, 0x08, 0x00}, {0x16, 0x01, 0x08, 0x01},
{0x01, 0x01, 0x0b, 0x00}, {0x16, 0x01, 0x0b, 0x01},
{0x01, 0x01, 0x0c, 0x00}, {0x16, 0x01, 0x0c, 0x01},
{0x01, 0x01, 0x0e, 0x00}, {0x16, 0x01, 0x0e, 0x01},
{0x01, 0x01, 0x0f, 0x00}, {0x16, 0x01, 0x0f, 0x01},
{0x01, 0x01, 0x10, 0x00}, {0x16, 0x01, 0x10, 0x01},
{0x01, 0x01, 0x11, 0x00}, {0x16, 0x01, 0x11, 0x01},
{0x01, 0x01, 0x12, 0x00}, {0x16, 0x01, 0x12, 0x01}
},
{
{0x02, 0x01, 0x08, 0x00}, {0x09, 0x01, 0x08, 0x00},
{0x17, 0x01, 0x08, 0x00}, {0x28, 0x01, 0x08, 0x01},
{0x02, 0x01, 0x0b, 0x00}, {0x09, 0x01, 0x0b, 0x00},
{0x17, 0x01, 0x0b, 0x00}, {0x28, 0x01, 0x0b, 0x01},
{0x02, 0x01, 0x0c, 0x00}, {0x09, 0x01, 0x0c, 0x00},
{0x17, 0x01, 0x0c, 0x00}, {0x28, 0x01, 0x0c, 0x01},
{0x02, 0x01, 0x0e, 0x00}, {0x09, 0x01, 0x0e, 0x00},
{0x17, 0x01, 0x0e, 0x00}, {0x28, 0x01, 0x0e, 0x01}
},
{
{0x03, 0x01, 0x08, 0x00}, {0x06, 0x01, 0x08, 0x00},
{0x0a, 0x01, 0x08, 0x00}, {0x0f, 0x01, 0x08, 0x00},
{0x18, 0x01, 0x08, 0x00}, {0x1f, 0x01, 0x08, 0x00},
{0x29, 0x01, 0x08, 0x00}, {0x38, 0x01, 0x08, 0x01},
{0x03, 0x01, 0x0b, 0x00}, {0x06, 0x01, 0x0b, 0x00},
{0x0a, 0x01, 0x0b, 0x00}, {0x0f, 0x01, 0x0b, 0x00},
{0x18, 0x01, 0x0b, 0x00}, {0x1f, 0x01, 0x0b, 0x00},
{0x29, 0x01, 0x0b, 0x00}, {0x38, 0x01, 0x0b, 0x01}
},
{
{0x03, 0x01, 0x0c, 0x00}, {0x06, 0x01, 0x0c, 0x00},
{0x0a, 0x01, 0x0c, 0x00}, {0x0f, 0x01, 0x0c, 0x00},
{0x18, 0x01, 0x0c, 0x00}, {0x1f, 0x01, 0x0c, 0x00},
{0x29, 0x01, 0x0c, 0x00}, {0x38, 0x01, 0x0c, 0x01},
{0x03, 0x01, 0x0e, 0x00}, {0x06, 0x01, 0x0e, 0x00},
{0x0a, 0x01, 0x0e, 0x00}, {0x0f, 0x01, 0x0e, 0x00},
{0x18, 0x01, 0x0e, 0x00}, {0x1f, 0x01, 0x0e, 0x00},
{0x29, 0x01, 0x0e, 0x00}, {0x38, 0x01, 0x0e, 0x01}
},
/* 235 */
{
{0x02, 0x01, 0x0f, 0x00}, {0x09, 0x01, 0x0f, 0x00},
{0x17, 0x01, 0x0f, 0x00}, {0x28, 0x01, 0x0f, 0x01},
{0x02, 0x01, 0x10, 0x00}, {0x09, 0x01, 0x10, 0x00},
{0x17, 0x01, 0x10, 0x00}, {0x28, 0x01, 0x10, 0x01},
{0x02, 0x01, 0x11, 0x00}, {0x09, 0x01, 0x11, 0x00},
{0x17, 0x01, 0x11, 0x00}, {0x28, 0x01, 0x11, 0x01},
{0x02, 0x01, 0x12, 0x00}, {0x09, 0x01, 0x12, 0x00},
{0x17, 0x01, 0x12, 0x00}, {0x28, 0x01, 0x12, 0x01}
},
{
{0x03, 0x01, 0x0f, 0x00}, {0x06, 0x01, 0x0f, 0x00},
{0x0a, 0x01, 0x0f, 0x00}, {0x0f, 0x01, 0x0f, 0x00},
{0x18, 0x01, 0x0f, 0x00}, {0x1f, 0x01, 0x0f, 0x00},
{0x29, 0x01, 0x0f, 0x00}, {0x38, 0x01, 0x0f, 0x01},
{0x03, 0x01, 0x10, 0x00}, {0x06, 0x01, 0x10, 0x00},
{0x0a, 0x01, 0x10, 0x00}, {0x0f, 0x01, 0x10, 0x00},
{0x18, 0x01, 0x10, 0x00}, {0x1f, 0x01, 0x10, 0x00},
{0x29, 0x01, 0x10, 0x00}, {0x38, 0x01, 0x10, 0x01}
},
{
{0x03, 0x01, 0x11, 0x00}, {0x06, 0x01, 0x11, 0x00},
{0x0a, 0x01, 0x11, 0x00}, {0x0f, 0x01, 0x11, 0x00},
{0x18, 0x01, 0x11, 0x00}, {0x1f, 0x01, 0x11, 0x00},
{0x29, 0x01, 0x11, 0x00}, {0x38, 0x01, 0x11, 0x01},
{0x03, 0x01, 0x12, 0x00}, {0x06, 0x01, 0x12, 0x00},
{0x0a, 0x01, 0x12, 0x00}, {0x0f, 0x01, 0x12, 0x00},
{0x18, 0x01, 0x12, 0x00}, {0x1f, 0x01, 0x12, 0x00},
{0x29, 0x01, 0x12, 0x00}, {0x38, 0x01, 0x12, 0x01}
},
{
{0x00, 0x01, 0x13, 0x01}, {0x00, 0x01, 0x14, 0x01},
{0x00, 0x01, 0x15, 0x01}, {0x00, 0x01, 0x17, 0x01},
{0x00, 0x01, 0x18, 0x01}, {0x00, 0x01, 0x19, 0x01},
{0x00, 0x01, 0x1a, 0x01}, {0x00, 0x01, 0x1b, 0x01},
{0x00, 0x01, 0x1c, 0x01}, {0x00, 0x01, 0x1d, 0x01},
{0x00, 0x01, 0x1e, 0x01}, {0x00, 0x01, 0x1f, 0x01},
{0x00, 0x01, 0x7f, 0x01}, {0x00, 0x01, 0xdc, 0x01},
{0x00, 0x01, 0xf9, 0x01}, {0xfd, 0x00, 0x00, 0x01}
},
{
{0x01, 0x01, 0x13, 0x00}, {0x16, 0x01, 0x13, 0x01},
{0x01, 0x01, 0x14, 0x00}, {0x16, 0x01, 0x14, 0x01},
{0x01, 0x01, 0x15, 0x00}, {0x16, 0x01, 0x15, 0x01},
{0x01, 0x01, 0x17, 0x00}, {0x16, 0x01, 0x17, 0x01},
{0x01, 0x01, 0x18, 0x00}, {0x16, 0x01, 0x18, 0x01},
{0x01, 0x01, 0x19, 0x00}, {0x16, 0x01, 0x19, 0x01},
{0x01, 0x01, 0x1a, 0x00}, {0x16, 0x01, 0x1a, 0x01},
{0x01, 0x01, 0x1b, 0x00}, {0x16, 0x01, 0x1b, 0x01}
},
/* 240 */
{
{0x02, 0x01, 0x13, 0x00}, {0x09, 0x01, 0x13, 0x00},
{0x17, 0x01, 0x13, 0x00}, {0x28, 0x01, 0x13, 0x01},
{0x02, 0x01, 0x14, 0x00}, {0x09, 0x01, 0x14, 0x00},
{0x17, 0x01, 0x14, 0x00}, {0x28, 0x01, 0x14, 0x01},
{0x02, 0x01, 0x15, 0x00}, {0x09, 0x01, 0x15, 0x00},
{0x17, 0x01, 0x15, 0x00}, {0x28, 0x01, 0x15, 0x01},
{0x02, 0x01, 0x17, 0x00}, {0x09, 0x01, 0x17, 0x00},
{0x17, 0x01, 0x17, 0x00}, {0x28, 0x01, 0x17, 0x01}
},
{
{0x03, 0x01, 0x13, 0x00}, {0x06, 0x01, 0x13, 0x00},
{0x0a, 0x01, 0x13, 0x00}, {0x0f, 0x01, 0x13, 0x00},
{0x18, 0x01, 0x13, 0x00}, {0x1f, 0x01, 0x13, 0x00},
{0x29, 0x01, 0x13, 0x00}, {0x38, 0x01, 0x13, 0x01},
{0x03, 0x01, 0x14, 0x00}, {0x06, 0x01, 0x14, 0x00},
{0x0a, 0x01, 0x14, 0x00}, {0x0f, 0x01, 0x14, 0x00},
{0x18, 0x01, 0x14, 0x00}, {0x1f, 0x01, 0x14, 0x00},
{0x29, 0x01, 0x14, 0x00}, {0x38, 0x01, 0x14, 0x01}
},
{
{0x03, 0x01, 0x15, 0x00}, {0x06, 0x01, 0x15, 0x00},
{0x0a, 0x01, 0x15, 0x00}, {0x0f, 0x01, 0x15, 0x00},
{0x18, 0x01, 0x15, 0x00}, {0x1f, 0x01, 0x15, 0x00},
{0x29, 0x01, 0x15, 0x00}, {0x38, 0x01, 0x15, 0x01},
{0x03, 0x01, 0x17, 0x00}, {0x06, 0x01, 0x17, 0x00},
{0x0a, 0x01, 0x17, 0x00}, {0x0f, 0x01, 0x17, 0x00},
{0x18, 0x01, 0x17, 0x00}, {0x1f, 0x01, 0x17, 0x00},
{0x29, 0x01, 0x17, 0x00}, {0x38, 0x01, 0x17, 0x01}
},
{
{0x02, 0x01, 0x18, 0x00}, {0x09, 0x01, 0x18, 0x00},
{0x17, 0x01, 0x18, 0x00}, {0x28, 0x01, 0x18, 0x01},
{0x02, 0x01, 0x19, 0x00}, {0x09, 0x01, 0x19, 0x00},
{0x17, 0x01, 0x19, 0x00}, {0x28, 0x01, 0x19, 0x01},
{0x02, 0x01, 0x1a, 0x00}, {0x09, 0x01, 0x1a, 0x00},
{0x17, 0x01, 0x1a, 0x00}, {0x28, 0x01, 0x1a, 0x01},
{0x02, 0x01, 0x1b, 0x00}, {0x09, 0x01, 0x1b, 0x00},
{0x17, 0x01, 0x1b, 0x00}, {0x28, 0x01, 0x1b, 0x01}
},
{
{0x03, 0x01, 0x18, 0x00}, {0x06, 0x01, 0x18, 0x00},
{0x0a, 0x01, 0x18, 0x00}, {0x0f, 0x01, 0x18, 0x00},
{0x18, 0x01, 0x18, 0x00}, {0x1f, 0x01, 0x18, 0x00},
{0x29, 0x01, 0x18, 0x00}, {0x38, 0x01, 0x18, 0x01},
{0x03, 0x01, 0x19, 0x00}, {0x06, 0x01, 0x19, 0x00},
{0x0a, 0x01, 0x19, 0x00}, {0x0f, 0x01, 0x19, 0x00},
{0x18, 0x01, 0x19, 0x00}, {0x1f, 0x01, 0x19, 0x00},
{0x29, 0x01, 0x19, 0x00}, {0x38, 0x01, 0x19, 0x01}
},
/* 245 */
{
{0x03, 0x01, 0x1a, 0x00}, {0x06, 0x01, 0x1a, 0x00},
{0x0a, 0x01, 0x1a, 0x00}, {0x0f, 0x01, 0x1a, 0x00},
{0x18, 0x01, 0x1a, 0x00}, {0x1f, 0x01, 0x1a, 0x00},
{0x29, 0x01, 0x1a, 0x00}, {0x38, 0x01, 0x1a, 0x01},
{0x03, 0x01, 0x1b, 0x00}, {0x06, 0x01, 0x1b, 0x00},
{0x0a, 0x01, 0x1b, 0x00}, {0x0f, 0x01, 0x1b, 0x00},
{0x18, 0x01, 0x1b, 0x00}, {0x1f, 0x01, 0x1b, 0x00},
{0x29, 0x01, 0x1b, 0x00}, {0x38, 0x01, 0x1b, 0x01}
},
{
{0x01, 0x01, 0x1c, 0x00}, {0x16, 0x01, 0x1c, 0x01},
{0x01, 0x01, 0x1d, 0x00}, {0x16, 0x01, 0x1d, 0x01},
{0x01, 0x01, 0x1e, 0x00}, {0x16, 0x01, 0x1e, 0x01},
{0x01, 0x01, 0x1f, 0x00}, {0x16, 0x01, 0x1f, 0x01},
{0x01, 0x01, 0x7f, 0x00}, {0x16, 0x01, 0x7f, 0x01},
{0x01, 0x01, 0xdc, 0x00}, {0x16, 0x01, 0xdc, 0x01},
{0x01, 0x01, 0xf9, 0x00}, {0x16, 0x01, 0xf9, 0x01},
{0xfe, 0x00, 0x00, 0x00}, {0xff, 0x00, 0x00, 0x01}
},
{
{0x02, 0x01, 0x1c, 0x00}, {0x09, 0x01, 0x1c, 0x00},
{0x17, 0x01, 0x1c, 0x00}, {0x28, 0x01, 0x1c, 0x01},
{0x02, 0x01, 0x1d, 0x00}, {0x09, 0x01, 0x1d, 0x00},
{0x17, 0x01, 0x1d, 0x00}, {0x28, 0x01, 0x1d, 0x01},
{0x02, 0x01, 0x1e, 0x00}, {0x09, 0x01, 0x1e, 0x00},
{0x17, 0x01, 0x1e, 0x00}, {0x28, 0x01, 0x1e, 0x01},
{0x02, 0x01, 0x1f, 0x00}, {0x09, 0x01, 0x1f, 0x00},
{0x17, 0x01, 0x1f, 0x00}, {0x28, 0x01, 0x1f, 0x01}
},
{
{0x03, 0x01, 0x1c, 0x00}, {0x06, 0x01, 0x1c, 0x00},
{0x0a, 0x01, 0x1c, 0x00}, {0x0f, 0x01, 0x1c, 0x00},
{0x18, 0x01, 0x1c, 0x00}, {0x1f, 0x01, 0x1c, 0x00},
{0x29, 0x01, 0x1c, 0x00}, {0x38, 0x01, 0x1c, 0x01},
{0x03, 0x01, 0x1d, 0x00}, {0x06, 0x01, 0x1d, 0x00},
{0x0a, 0x01, 0x1d, 0x00}, {0x0f, 0x01, 0x1d, 0x00},
{0x18, 0x01, 0x1d, 0x00}, {0x1f, 0x01, 0x1d, 0x00},
{0x29, 0x01, 0x1d, 0x00}, {0x38, 0x01, 0x1d, 0x01}
},
{
{0x03, 0x01, 0x1e, 0x00}, {0x06, 0x01, 0x1e, 0x00},
{0x0a, 0x01, 0x1e, 0x00}, {0x0f, 0x01, 0x1e, 0x00},
{0x18, 0x01, 0x1e, 0x00}, {0x1f, 0x01, 0x1e, 0x00},
{0x29, 0x01, 0x1e, 0x00}, {0x38, 0x01, 0x1e, 0x01},
{0x03, 0x01, 0x1f, 0x00}, {0x06, 0x01, 0x1f, 0x00},
{0x0a, 0x01, 0x1f, 0x00}, {0x0f, 0x01, 0x1f, 0x00},
{0x18, 0x01, 0x1f, 0x00}, {0x1f, 0x01, 0x1f, 0x00},
{0x29, 0x01, 0x1f, 0x00}, {0x38, 0x01, 0x1f, 0x01}
},
/* 250 */
{
{0x02, 0x01, 0x7f, 0x00}, {0x09, 0x01, 0x7f, 0x00},
{0x17, 0x01, 0x7f, 0x00}, {0x28, 0x01, 0x7f, 0x01},
{0x02, 0x01, 0xdc, 0x00}, {0x09, 0x01, 0xdc, 0x00},
{0x17, 0x01, 0xdc, 0x00}, {0x28, 0x01, 0xdc, 0x01},
{0x02, 0x01, 0xf9, 0x00}, {0x09, 0x01, 0xf9, 0x00},
{0x17, 0x01, 0xf9, 0x00}, {0x28, 0x01, 0xf9, 0x01},
{0x00, 0x01, 0x0a, 0x01}, {0x00, 0x01, 0x0d, 0x01},
{0x00, 0x01, 0x16, 0x01}, {0xfa, 0x00, 0x00, 0x00}
},
{
{0x03, 0x01, 0x7f, 0x00}, {0x06, 0x01, 0x7f, 0x00},
{0x0a, 0x01, 0x7f, 0x00}, {0x0f, 0x01, 0x7f, 0x00},
{0x18, 0x01, 0x7f, 0x00}, {0x1f, 0x01, 0x7f, 0x00},
{0x29, 0x01, 0x7f, 0x00}, {0x38, 0x01, 0x7f, 0x01},
{0x03, 0x01, 0xdc, 0x00}, {0x06, 0x01, 0xdc, 0x00},
{0x0a, 0x01, 0xdc, 0x00}, {0x0f, 0x01, 0xdc, 0x00},
{0x18, 0x01, 0xdc, 0x00}, {0x1f, 0x01, 0xdc, 0x00},
{0x29, 0x01, 0xdc, 0x00}, {0x38, 0x01, 0xdc, 0x01}
},
{
{0x03, 0x01, 0xf9, 0x00}, {0x06, 0x01, 0xf9, 0x00},
{0x0a, 0x01, 0xf9, 0x00}, {0x0f, 0x01, 0xf9, 0x00},
{0x18, 0x01, 0xf9, 0x00}, {0x1f, 0x01, 0xf9, 0x00},
{0x29, 0x01, 0xf9, 0x00}, {0x38, 0x01, 0xf9, 0x01},
{0x01, 0x01, 0x0a, 0x00}, {0x16, 0x01, 0x0a, 0x01},
{0x01, 0x01, 0x0d, 0x00}, {0x16, 0x01, 0x0d, 0x01},
{0x01, 0x01, 0x16, 0x00}, {0x16, 0x01, 0x16, 0x01},
{0xfc, 0x00, 0x00, 0x00}, {0xfc, 0x00, 0x00, 0x00}
},
{
{0x02, 0x01, 0x0a, 0x00}, {0x09, 0x01, 0x0a, 0x00},
{0x17, 0x01, 0x0a, 0x00}, {0x28, 0x01, 0x0a, 0x01},
{0x02, 0x01, 0x0d, 0x00}, {0x09, 0x01, 0x0d, 0x00},
{0x17, 0x01, 0x0d, 0x00}, {0x28, 0x01, 0x0d, 0x01},
{0x02, 0x01, 0x16, 0x00}, {0x09, 0x01, 0x16, 0x00},
{0x17, 0x01, 0x16, 0x00}, {0x28, 0x01, 0x16, 0x01},
{0xfd, 0x00, 0x00, 0x00}, {0xfd, 0x00, 0x00, 0x00},
{0xfd, 0x00, 0x00, 0x00}, {0xfd, 0x00, 0x00, 0x00}
},
{
{0x03, 0x01, 0x0a, 0x00}, {0x06, 0x01, 0x0a, 0x00},
{0x0a, 0x01, 0x0a, 0x00}, {0x0f, 0x01, 0x0a, 0x00},
{0x18, 0x01, 0x0a, 0x00}, {0x1f, 0x01, 0x0a, 0x00},
{0x29, 0x01, 0x0a, 0x00}, {0x38, 0x01, 0x0a, 0x01},
{0x03, 0x01, 0x0d, 0x00}, {0x06, 0x01, 0x0d, 0x00},
{0x0a, 0x01, 0x0d, 0x00}, {0x0f, 0x01, 0x0d, 0x00},
{0x18, 0x01, 0x0d, 0x00}, {0x1f, 0x01, 0x0d, 0x00},
{0x29, 0x01, 0x0d, 0x00}, {0x38, 0x01, 0x0d, 0x01}
},
/* 255 */
{
{0x03, 0x01, 0x16, 0x00}, {0x06, 0x01, 0x16, 0x00},
{0x0a, 0x01, 0x16, 0x00}, {0x0f, 0x01, 0x16, 0x00},
{0x18, 0x01, 0x16, 0x00}, {0x1f, 0x01, 0x16, 0x00},
{0x29, 0x01, 0x16, 0x00}, {0x38, 0x01, 0x16, 0x01},
{0xff, 0x00, 0x00, 0x00}, {0xff, 0x00, 0x00, 0x00},
{0xff, 0x00, 0x00, 0x00}, {0xff, 0x00, 0x00, 0x00},
{0xff, 0x00, 0x00, 0x00}, {0xff, 0x00, 0x00, 0x00},
{0xff, 0x00, 0x00, 0x00}, {0xff, 0x00, 0x00, 0x00}
}
};
ngx_int_t
ngx_http_v2_huff_decode(u_char *state, u_char *src, size_t len, u_char **dst,
ngx_uint_t last, ngx_log_t *log)
{
u_char *end, ch, ending;
ch = 0;
ending = 1;
end = src + len;
while (src != end) {
ch = *src++;
if (ngx_http_v2_huff_decode_bits(state, &ending, ch >> 4, dst)
!= NGX_OK)
{
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, log, 0,
"http2 huffman decoding error at state %d: "
"bad code 0x%Xd", *state, ch >> 4);
return NGX_ERROR;
}
if (ngx_http_v2_huff_decode_bits(state, &ending, ch & 0xf, dst)
!= NGX_OK)
{
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, log, 0,
"http2 huffman decoding error at state %d: "
"bad code 0x%Xd", *state, ch & 0xf);
return NGX_ERROR;
}
}
if (last) {
if (!ending) {
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0,
"http2 huffman decoding error: "
"incomplete code 0x%Xd", ch);
return NGX_ERROR;
}
*state = 0;
}
return NGX_OK;
}
static ngx_inline ngx_int_t
ngx_http_v2_huff_decode_bits(u_char *state, u_char *ending, ngx_uint_t bits,
u_char **dst)
{
ngx_http_v2_huff_decode_code_t code;
code = ngx_http_v2_huff_decode_codes[*state][bits];
if (code.next == *state) {
return NGX_ERROR;
}
if (code.emit) {
*(*dst)++ = code.sym;
}
*ending = code.ending;
*state = code.next;
return NGX_OK;
}
nginx-1.14.0/src/http/v2/ngx_http_v2_huff_encode.c 000644 001751 001751 00000031035 13265410474 023146 0 ustar 00mdounin mdounin 000000 000000
/*
* Copyright (C) Nginx, Inc.
* Copyright (C) Valentin V. Bartenev
* Copyright (C) 2015 Vlad Krasnov
*/
#include
#include
#include
typedef struct {
uint32_t code;
uint32_t len;
} ngx_http_v2_huff_encode_code_t;
static ngx_http_v2_huff_encode_code_t ngx_http_v2_huff_encode_table[256] =
{
{0x00001ff8, 13}, {0x007fffd8, 23}, {0x0fffffe2, 28}, {0x0fffffe3, 28},
{0x0fffffe4, 28}, {0x0fffffe5, 28}, {0x0fffffe6, 28}, {0x0fffffe7, 28},
{0x0fffffe8, 28}, {0x00ffffea, 24}, {0x3ffffffc, 30}, {0x0fffffe9, 28},
{0x0fffffea, 28}, {0x3ffffffd, 30}, {0x0fffffeb, 28}, {0x0fffffec, 28},
{0x0fffffed, 28}, {0x0fffffee, 28}, {0x0fffffef, 28}, {0x0ffffff0, 28},
{0x0ffffff1, 28}, {0x0ffffff2, 28}, {0x3ffffffe, 30}, {0x0ffffff3, 28},
{0x0ffffff4, 28}, {0x0ffffff5, 28}, {0x0ffffff6, 28}, {0x0ffffff7, 28},
{0x0ffffff8, 28}, {0x0ffffff9, 28}, {0x0ffffffa, 28}, {0x0ffffffb, 28},
{0x00000014, 6}, {0x000003f8, 10}, {0x000003f9, 10}, {0x00000ffa, 12},
{0x00001ff9, 13}, {0x00000015, 6}, {0x000000f8, 8}, {0x000007fa, 11},
{0x000003fa, 10}, {0x000003fb, 10}, {0x000000f9, 8}, {0x000007fb, 11},
{0x000000fa, 8}, {0x00000016, 6}, {0x00000017, 6}, {0x00000018, 6},
{0x00000000, 5}, {0x00000001, 5}, {0x00000002, 5}, {0x00000019, 6},
{0x0000001a, 6}, {0x0000001b, 6}, {0x0000001c, 6}, {0x0000001d, 6},
{0x0000001e, 6}, {0x0000001f, 6}, {0x0000005c, 7}, {0x000000fb, 8},
{0x00007ffc, 15}, {0x00000020, 6}, {0x00000ffb, 12}, {0x000003fc, 10},
{0x00001ffa, 13}, {0x00000021, 6}, {0x0000005d, 7}, {0x0000005e, 7},
{0x0000005f, 7}, {0x00000060, 7}, {0x00000061, 7}, {0x00000062, 7},
{0x00000063, 7}, {0x00000064, 7}, {0x00000065, 7}, {0x00000066, 7},
{0x00000067, 7}, {0x00000068, 7}, {0x00000069, 7}, {0x0000006a, 7},
{0x0000006b, 7}, {0x0000006c, 7}, {0x0000006d, 7}, {0x0000006e, 7},
{0x0000006f, 7}, {0x00000070, 7}, {0x00000071, 7}, {0x00000072, 7},
{0x000000fc, 8}, {0x00000073, 7}, {0x000000fd, 8}, {0x00001ffb, 13},
{0x0007fff0, 19}, {0x00001ffc, 13}, {0x00003ffc, 14}, {0x00000022, 6},
{0x00007ffd, 15}, {0x00000003, 5}, {0x00000023, 6}, {0x00000004, 5},
{0x00000024, 6}, {0x00000005, 5}, {0x00000025, 6}, {0x00000026, 6},
{0x00000027, 6}, {0x00000006, 5}, {0x00000074, 7}, {0x00000075, 7},
{0x00000028, 6}, {0x00000029, 6}, {0x0000002a, 6}, {0x00000007, 5},
{0x0000002b, 6}, {0x00000076, 7}, {0x0000002c, 6}, {0x00000008, 5},
{0x00000009, 5}, {0x0000002d, 6}, {0x00000077, 7}, {0x00000078, 7},
{0x00000079, 7}, {0x0000007a, 7}, {0x0000007b, 7}, {0x00007ffe, 15},
{0x000007fc, 11}, {0x00003ffd, 14}, {0x00001ffd, 13}, {0x0ffffffc, 28},
{0x000fffe6, 20}, {0x003fffd2, 22}, {0x000fffe7, 20}, {0x000fffe8, 20},
{0x003fffd3, 22}, {0x003fffd4, 22}, {0x003fffd5, 22}, {0x007fffd9, 23},
{0x003fffd6, 22}, {0x007fffda, 23}, {0x007fffdb, 23}, {0x007fffdc, 23},
{0x007fffdd, 23}, {0x007fffde, 23}, {0x00ffffeb, 24}, {0x007fffdf, 23},
{0x00ffffec, 24}, {0x00ffffed, 24}, {0x003fffd7, 22}, {0x007fffe0, 23},
{0x00ffffee, 24}, {0x007fffe1, 23}, {0x007fffe2, 23}, {0x007fffe3, 23},
{0x007fffe4, 23}, {0x001fffdc, 21}, {0x003fffd8, 22}, {0x007fffe5, 23},
{0x003fffd9, 22}, {0x007fffe6, 23}, {0x007fffe7, 23}, {0x00ffffef, 24},
{0x003fffda, 22}, {0x001fffdd, 21}, {0x000fffe9, 20}, {0x003fffdb, 22},
{0x003fffdc, 22}, {0x007fffe8, 23}, {0x007fffe9, 23}, {0x001fffde, 21},
{0x007fffea, 23}, {0x003fffdd, 22}, {0x003fffde, 22}, {0x00fffff0, 24},
{0x001fffdf, 21}, {0x003fffdf, 22}, {0x007fffeb, 23}, {0x007fffec, 23},
{0x001fffe0, 21}, {0x001fffe1, 21}, {0x003fffe0, 22}, {0x001fffe2, 21},
{0x007fffed, 23}, {0x003fffe1, 22}, {0x007fffee, 23}, {0x007fffef, 23},
{0x000fffea, 20}, {0x003fffe2, 22}, {0x003fffe3, 22}, {0x003fffe4, 22},
{0x007ffff0, 23}, {0x003fffe5, 22}, {0x003fffe6, 22}, {0x007ffff1, 23},
{0x03ffffe0, 26}, {0x03ffffe1, 26}, {0x000fffeb, 20}, {0x0007fff1, 19},
{0x003fffe7, 22}, {0x007ffff2, 23}, {0x003fffe8, 22}, {0x01ffffec, 25},
{0x03ffffe2, 26}, {0x03ffffe3, 26}, {0x03ffffe4, 26}, {0x07ffffde, 27},
{0x07ffffdf, 27}, {0x03ffffe5, 26}, {0x00fffff1, 24}, {0x01ffffed, 25},
{0x0007fff2, 19}, {0x001fffe3, 21}, {0x03ffffe6, 26}, {0x07ffffe0, 27},
{0x07ffffe1, 27}, {0x03ffffe7, 26}, {0x07ffffe2, 27}, {0x00fffff2, 24},
{0x001fffe4, 21}, {0x001fffe5, 21}, {0x03ffffe8, 26}, {0x03ffffe9, 26},
{0x0ffffffd, 28}, {0x07ffffe3, 27}, {0x07ffffe4, 27}, {0x07ffffe5, 27},
{0x000fffec, 20}, {0x00fffff3, 24}, {0x000fffed, 20}, {0x001fffe6, 21},
{0x003fffe9, 22}, {0x001fffe7, 21}, {0x001fffe8, 21}, {0x007ffff3, 23},
{0x003fffea, 22}, {0x003fffeb, 22}, {0x01ffffee, 25}, {0x01ffffef, 25},
{0x00fffff4, 24}, {0x00fffff5, 24}, {0x03ffffea, 26}, {0x007ffff4, 23},
{0x03ffffeb, 26}, {0x07ffffe6, 27}, {0x03ffffec, 26}, {0x03ffffed, 26},
{0x07ffffe7, 27}, {0x07ffffe8, 27}, {0x07ffffe9, 27}, {0x07ffffea, 27},
{0x07ffffeb, 27}, {0x0ffffffe, 28}, {0x07ffffec, 27}, {0x07ffffed, 27},
{0x07ffffee, 27}, {0x07ffffef, 27}, {0x07fffff0, 27}, {0x03ffffee, 26}
};
/* same as above, but embeds lowercase transformation */
static ngx_http_v2_huff_encode_code_t ngx_http_v2_huff_encode_table_lc[256] =
{
{0x00001ff8, 13}, {0x007fffd8, 23}, {0x0fffffe2, 28}, {0x0fffffe3, 28},
{0x0fffffe4, 28}, {0x0fffffe5, 28}, {0x0fffffe6, 28}, {0x0fffffe7, 28},
{0x0fffffe8, 28}, {0x00ffffea, 24}, {0x3ffffffc, 30}, {0x0fffffe9, 28},
{0x0fffffea, 28}, {0x3ffffffd, 30}, {0x0fffffeb, 28}, {0x0fffffec, 28},
{0x0fffffed, 28}, {0x0fffffee, 28}, {0x0fffffef, 28}, {0x0ffffff0, 28},
{0x0ffffff1, 28}, {0x0ffffff2, 28}, {0x3ffffffe, 30}, {0x0ffffff3, 28},
{0x0ffffff4, 28}, {0x0ffffff5, 28}, {0x0ffffff6, 28}, {0x0ffffff7, 28},
{0x0ffffff8, 28}, {0x0ffffff9, 28}, {0x0ffffffa, 28}, {0x0ffffffb, 28},
{0x00000014, 6}, {0x000003f8, 10}, {0x000003f9, 10}, {0x00000ffa, 12},
{0x00001ff9, 13}, {0x00000015, 6}, {0x000000f8, 8}, {0x000007fa, 11},
{0x000003fa, 10}, {0x000003fb, 10}, {0x000000f9, 8}, {0x000007fb, 11},
{0x000000fa, 8}, {0x00000016, 6}, {0x00000017, 6}, {0x00000018, 6},
{0x00000000, 5}, {0x00000001, 5}, {0x00000002, 5}, {0x00000019, 6},
{0x0000001a, 6}, {0x0000001b, 6}, {0x0000001c, 6}, {0x0000001d, 6},
{0x0000001e, 6}, {0x0000001f, 6}, {0x0000005c, 7}, {0x000000fb, 8},
{0x00007ffc, 15}, {0x00000020, 6}, {0x00000ffb, 12}, {0x000003fc, 10},
{0x00001ffa, 13}, {0x00000003, 5}, {0x00000023, 6}, {0x00000004, 5},
{0x00000024, 6}, {0x00000005, 5}, {0x00000025, 6}, {0x00000026, 6},
{0x00000027, 6}, {0x00000006, 5}, {0x00000074, 7}, {0x00000075, 7},
{0x00000028, 6}, {0x00000029, 6}, {0x0000002a, 6}, {0x00000007, 5},
{0x0000002b, 6}, {0x00000076, 7}, {0x0000002c, 6}, {0x00000008, 5},
{0x00000009, 5}, {0x0000002d, 6}, {0x00000077, 7}, {0x00000078, 7},
{0x00000079, 7}, {0x0000007a, 7}, {0x0000007b, 7}, {0x00001ffb, 13},
{0x0007fff0, 19}, {0x00001ffc, 13}, {0x00003ffc, 14}, {0x00000022, 6},
{0x00007ffd, 15}, {0x00000003, 5}, {0x00000023, 6}, {0x00000004, 5},
{0x00000024, 6}, {0x00000005, 5}, {0x00000025, 6}, {0x00000026, 6},
{0x00000027, 6}, {0x00000006, 5}, {0x00000074, 7}, {0x00000075, 7},
{0x00000028, 6}, {0x00000029, 6}, {0x0000002a, 6}, {0x00000007, 5},
{0x0000002b, 6}, {0x00000076, 7}, {0x0000002c, 6}, {0x00000008, 5},
{0x00000009, 5}, {0x0000002d, 6}, {0x00000077, 7}, {0x00000078, 7},
{0x00000079, 7}, {0x0000007a, 7}, {0x0000007b, 7}, {0x00007ffe, 15},
{0x000007fc, 11}, {0x00003ffd, 14}, {0x00001ffd, 13}, {0x0ffffffc, 28},
{0x000fffe6, 20}, {0x003fffd2, 22}, {0x000fffe7, 20}, {0x000fffe8, 20},
{0x003fffd3, 22}, {0x003fffd4, 22}, {0x003fffd5, 22}, {0x007fffd9, 23},
{0x003fffd6, 22}, {0x007fffda, 23}, {0x007fffdb, 23}, {0x007fffdc, 23},
{0x007fffdd, 23}, {0x007fffde, 23}, {0x00ffffeb, 24}, {0x007fffdf, 23},
{0x00ffffec, 24}, {0x00ffffed, 24}, {0x003fffd7, 22}, {0x007fffe0, 23},
{0x00ffffee, 24}, {0x007fffe1, 23}, {0x007fffe2, 23}, {0x007fffe3, 23},
{0x007fffe4, 23}, {0x001fffdc, 21}, {0x003fffd8, 22}, {0x007fffe5, 23},
{0x003fffd9, 22}, {0x007fffe6, 23}, {0x007fffe7, 23}, {0x00ffffef, 24},
{0x003fffda, 22}, {0x001fffdd, 21}, {0x000fffe9, 20}, {0x003fffdb, 22},
{0x003fffdc, 22}, {0x007fffe8, 23}, {0x007fffe9, 23}, {0x001fffde, 21},
{0x007fffea, 23}, {0x003fffdd, 22}, {0x003fffde, 22}, {0x00fffff0, 24},
{0x001fffdf, 21}, {0x003fffdf, 22}, {0x007fffeb, 23}, {0x007fffec, 23},
{0x001fffe0, 21}, {0x001fffe1, 21}, {0x003fffe0, 22}, {0x001fffe2, 21},
{0x007fffed, 23}, {0x003fffe1, 22}, {0x007fffee, 23}, {0x007fffef, 23},
{0x000fffea, 20}, {0x003fffe2, 22}, {0x003fffe3, 22}, {0x003fffe4, 22},
{0x007ffff0, 23}, {0x003fffe5, 22}, {0x003fffe6, 22}, {0x007ffff1, 23},
{0x03ffffe0, 26}, {0x03ffffe1, 26}, {0x000fffeb, 20}, {0x0007fff1, 19},
{0x003fffe7, 22}, {0x007ffff2, 23}, {0x003fffe8, 22}, {0x01ffffec, 25},
{0x03ffffe2, 26}, {0x03ffffe3, 26}, {0x03ffffe4, 26}, {0x07ffffde, 27},
{0x07ffffdf, 27}, {0x03ffffe5, 26}, {0x00fffff1, 24}, {0x01ffffed, 25},
{0x0007fff2, 19}, {0x001fffe3, 21}, {0x03ffffe6, 26}, {0x07ffffe0, 27},
{0x07ffffe1, 27}, {0x03ffffe7, 26}, {0x07ffffe2, 27}, {0x00fffff2, 24},
{0x001fffe4, 21}, {0x001fffe5, 21}, {0x03ffffe8, 26}, {0x03ffffe9, 26},
{0x0ffffffd, 28}, {0x07ffffe3, 27}, {0x07ffffe4, 27}, {0x07ffffe5, 27},
{0x000fffec, 20}, {0x00fffff3, 24}, {0x000fffed, 20}, {0x001fffe6, 21},
{0x003fffe9, 22}, {0x001fffe7, 21}, {0x001fffe8, 21}, {0x007ffff3, 23},
{0x003fffea, 22}, {0x003fffeb, 22}, {0x01ffffee, 25}, {0x01ffffef, 25},
{0x00fffff4, 24}, {0x00fffff5, 24}, {0x03ffffea, 26}, {0x007ffff4, 23},
{0x03ffffeb, 26}, {0x07ffffe6, 27}, {0x03ffffec, 26}, {0x03ffffed, 26},
{0x07ffffe7, 27}, {0x07ffffe8, 27}, {0x07ffffe9, 27}, {0x07ffffea, 27},
{0x07ffffeb, 27}, {0x0ffffffe, 28}, {0x07ffffec, 27}, {0x07ffffed, 27},
{0x07ffffee, 27}, {0x07ffffef, 27}, {0x07fffff0, 27}, {0x03ffffee, 26}
};
#if (NGX_PTR_SIZE == 8)
#if (NGX_HAVE_LITTLE_ENDIAN)
#if (NGX_HAVE_GCC_BSWAP64)
#define ngx_http_v2_huff_encode_buf(dst, buf) \
(*(uint64_t *) (dst) = __builtin_bswap64(buf))
#else
#define ngx_http_v2_huff_encode_buf(dst, buf) \
((dst)[0] = (u_char) ((buf) >> 56), \
(dst)[1] = (u_char) ((buf) >> 48), \
(dst)[2] = (u_char) ((buf) >> 40), \
(dst)[3] = (u_char) ((buf) >> 32), \
(dst)[4] = (u_char) ((buf) >> 24), \
(dst)[5] = (u_char) ((buf) >> 16), \
(dst)[6] = (u_char) ((buf) >> 8), \
(dst)[7] = (u_char) (buf))
#endif
#else /* !NGX_HAVE_LITTLE_ENDIAN */
#define ngx_http_v2_huff_encode_buf(dst, buf) \
(*(uint64_t *) (dst) = (buf))
#endif
#else /* NGX_PTR_SIZE == 4 */
#define ngx_http_v2_huff_encode_buf(dst, buf) \
(*(uint32_t *) (dst) = htonl(buf))
#endif
size_t
ngx_http_v2_huff_encode(u_char *src, size_t len, u_char *dst, ngx_uint_t lower)
{
u_char *end;
size_t hlen;
ngx_uint_t buf, pending, code;
ngx_http_v2_huff_encode_code_t *table, *next;
table = lower ? ngx_http_v2_huff_encode_table_lc
: ngx_http_v2_huff_encode_table;
hlen = 0;
buf = 0;
pending = 0;
end = src + len;
while (src != end) {
next = &table[*src++];
code = next->code;
pending += next->len;
/* accumulate bits */
if (pending < sizeof(buf) * 8) {
buf |= code << (sizeof(buf) * 8 - pending);
continue;
}
if (hlen + sizeof(buf) >= len) {
return 0;
}
pending -= sizeof(buf) * 8;
buf |= code >> pending;
ngx_http_v2_huff_encode_buf(&dst[hlen], buf);
hlen += sizeof(buf);
buf = pending ? code << (sizeof(buf) * 8 - pending) : 0;
}
if (pending == 0) {
return hlen;
}
buf |= (ngx_uint_t) -1 >> pending;
pending = ngx_align(pending, 8);
if (hlen + pending / 8 >= len) {
return 0;
}
buf >>= sizeof(buf) * 8 - pending;
do {
pending -= 8;
dst[hlen++] = (u_char) (buf >> pending);
} while (pending);
return hlen;
}
nginx-1.14.0/src/http/v2/ngx_http_v2_module.c 000644 001751 001751 00000040106 13265410474 022165 0 ustar 00mdounin mdounin 000000 000000
/*
* Copyright (C) Nginx, Inc.
* Copyright (C) Valentin V. Bartenev
*/
#include
#include
#include
#include
static ngx_int_t ngx_http_v2_add_variables(ngx_conf_t *cf);
static ngx_int_t ngx_http_v2_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_v2_module_init(ngx_cycle_t *cycle);
static void *ngx_http_v2_create_main_conf(ngx_conf_t *cf);
static char *ngx_http_v2_init_main_conf(ngx_conf_t *cf, void *conf);
static void *ngx_http_v2_create_srv_conf(ngx_conf_t *cf);
static char *ngx_http_v2_merge_srv_conf(ngx_conf_t *cf, void *parent,
void *child);
static void *ngx_http_v2_create_loc_conf(ngx_conf_t *cf);
static char *ngx_http_v2_merge_loc_conf(ngx_conf_t *cf, void *parent,
void *child);
static char *ngx_http_v2_push(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static char *ngx_http_v2_recv_buffer_size(ngx_conf_t *cf, void *post,
void *data);
static char *ngx_http_v2_pool_size(ngx_conf_t *cf, void *post, void *data);
static char *ngx_http_v2_preread_size(ngx_conf_t *cf, void *post, void *data);
static char *ngx_http_v2_streams_index_mask(ngx_conf_t *cf, void *post,
void *data);
static char *ngx_http_v2_chunk_size(ngx_conf_t *cf, void *post, void *data);
static char *ngx_http_v2_spdy_deprecated(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static ngx_conf_post_t ngx_http_v2_recv_buffer_size_post =
{ ngx_http_v2_recv_buffer_size };
static ngx_conf_post_t ngx_http_v2_pool_size_post =
{ ngx_http_v2_pool_size };
static ngx_conf_post_t ngx_http_v2_preread_size_post =
{ ngx_http_v2_preread_size };
static ngx_conf_post_t ngx_http_v2_streams_index_mask_post =
{ ngx_http_v2_streams_index_mask };
static ngx_conf_post_t ngx_http_v2_chunk_size_post =
{ ngx_http_v2_chunk_size };
static ngx_command_t ngx_http_v2_commands[] = {
{ ngx_string("http2_recv_buffer_size"),
NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,
ngx_conf_set_size_slot,
NGX_HTTP_MAIN_CONF_OFFSET,
offsetof(ngx_http_v2_main_conf_t, recv_buffer_size),
&ngx_http_v2_recv_buffer_size_post },
{ ngx_string("http2_pool_size"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
ngx_conf_set_size_slot,
NGX_HTTP_SRV_CONF_OFFSET,
offsetof(ngx_http_v2_srv_conf_t, pool_size),
&ngx_http_v2_pool_size_post },
{ ngx_string("http2_max_concurrent_streams"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
ngx_conf_set_num_slot,
NGX_HTTP_SRV_CONF_OFFSET,
offsetof(ngx_http_v2_srv_conf_t, concurrent_streams),
NULL },
{ ngx_string("http2_max_concurrent_pushes"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
ngx_conf_set_num_slot,
NGX_HTTP_SRV_CONF_OFFSET,
offsetof(ngx_http_v2_srv_conf_t, concurrent_pushes),
NULL },
{ ngx_string("http2_max_requests"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
ngx_conf_set_num_slot,
NGX_HTTP_SRV_CONF_OFFSET,
offsetof(ngx_http_v2_srv_conf_t, max_requests),
NULL },
{ ngx_string("http2_max_field_size"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
ngx_conf_set_size_slot,
NGX_HTTP_SRV_CONF_OFFSET,
offsetof(ngx_http_v2_srv_conf_t, max_field_size),
NULL },
{ ngx_string("http2_max_header_size"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
ngx_conf_set_size_slot,
NGX_HTTP_SRV_CONF_OFFSET,
offsetof(ngx_http_v2_srv_conf_t, max_header_size),
NULL },
{ ngx_string("http2_body_preread_size"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
ngx_conf_set_size_slot,
NGX_HTTP_SRV_CONF_OFFSET,
offsetof(ngx_http_v2_srv_conf_t, preread_size),
&ngx_http_v2_preread_size_post },
{ ngx_string("http2_streams_index_size"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
ngx_conf_set_num_slot,
NGX_HTTP_SRV_CONF_OFFSET,
offsetof(ngx_http_v2_srv_conf_t, streams_index_mask),
&ngx_http_v2_streams_index_mask_post },
{ ngx_string("http2_recv_timeout"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
ngx_conf_set_msec_slot,
NGX_HTTP_SRV_CONF_OFFSET,
offsetof(ngx_http_v2_srv_conf_t, recv_timeout),
NULL },
{ ngx_string("http2_idle_timeout"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
ngx_conf_set_msec_slot,
NGX_HTTP_SRV_CONF_OFFSET,
offsetof(ngx_http_v2_srv_conf_t, idle_timeout),
NULL },
{ ngx_string("http2_chunk_size"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_size_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_v2_loc_conf_t, chunk_size),
&ngx_http_v2_chunk_size_post },
{ ngx_string("http2_push_preload"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_v2_loc_conf_t, push_preload),
NULL },
{ ngx_string("http2_push"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_http_v2_push,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
{ ngx_string("spdy_recv_buffer_size"),
NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,
ngx_http_v2_spdy_deprecated,
NGX_HTTP_MAIN_CONF_OFFSET,
0,
NULL },
{ ngx_string("spdy_pool_size"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
ngx_http_v2_spdy_deprecated,
NGX_HTTP_SRV_CONF_OFFSET,
0,
NULL },
{ ngx_string("spdy_max_concurrent_streams"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
ngx_http_v2_spdy_deprecated,
NGX_HTTP_SRV_CONF_OFFSET,
0,
NULL },
{ ngx_string("spdy_streams_index_size"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
ngx_http_v2_spdy_deprecated,
NGX_HTTP_SRV_CONF_OFFSET,
0,
NULL },
{ ngx_string("spdy_recv_timeout"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
ngx_http_v2_spdy_deprecated,
NGX_HTTP_SRV_CONF_OFFSET,
0,
NULL },
{ ngx_string("spdy_keepalive_timeout"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
ngx_http_v2_spdy_deprecated,
NGX_HTTP_SRV_CONF_OFFSET,
0,
NULL },
{ ngx_string("spdy_headers_comp"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
ngx_http_v2_spdy_deprecated,
NGX_HTTP_SRV_CONF_OFFSET,
0,
NULL },
{ ngx_string("spdy_chunk_size"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_http_v2_spdy_deprecated,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
ngx_null_command
};
static ngx_http_module_t ngx_http_v2_module_ctx = {
ngx_http_v2_add_variables, /* preconfiguration */
NULL, /* postconfiguration */
ngx_http_v2_create_main_conf, /* create main configuration */
ngx_http_v2_init_main_conf, /* init main configuration */
ngx_http_v2_create_srv_conf, /* create server configuration */
ngx_http_v2_merge_srv_conf, /* merge server configuration */
ngx_http_v2_create_loc_conf, /* create location configuration */
ngx_http_v2_merge_loc_conf /* merge location configuration */
};
ngx_module_t ngx_http_v2_module = {
NGX_MODULE_V1,
&ngx_http_v2_module_ctx, /* module context */
ngx_http_v2_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
ngx_http_v2_module_init, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static ngx_http_variable_t ngx_http_v2_vars[] = {
{ ngx_string("http2"), NULL,
ngx_http_v2_variable, 0, 0, 0 },
ngx_http_null_variable
};
static ngx_int_t
ngx_http_v2_add_variables(ngx_conf_t *cf)
{
ngx_http_variable_t *var, *v;
for (v = ngx_http_v2_vars; v->name.len; v++) {
var = ngx_http_add_variable(cf, &v->name, v->flags);
if (var == NULL) {
return NGX_ERROR;
}
var->get_handler = v->get_handler;
var->data = v->data;
}
return NGX_OK;
}
static ngx_int_t
ngx_http_v2_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
if (r->stream) {
#if (NGX_HTTP_SSL)
if (r->connection->ssl) {
v->len = sizeof("h2") - 1;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = (u_char *) "h2";
return NGX_OK;
}
#endif
v->len = sizeof("h2c") - 1;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = (u_char *) "h2c";
return NGX_OK;
}
*v = ngx_http_variable_null_value;
return NGX_OK;
}
static ngx_int_t
ngx_http_v2_module_init(ngx_cycle_t *cycle)
{
return NGX_OK;
}
static void *
ngx_http_v2_create_main_conf(ngx_conf_t *cf)
{
ngx_http_v2_main_conf_t *h2mcf;
h2mcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_v2_main_conf_t));
if (h2mcf == NULL) {
return NULL;
}
h2mcf->recv_buffer_size = NGX_CONF_UNSET_SIZE;
return h2mcf;
}
static char *
ngx_http_v2_init_main_conf(ngx_conf_t *cf, void *conf)
{
ngx_http_v2_main_conf_t *h2mcf = conf;
ngx_conf_init_size_value(h2mcf->recv_buffer_size, 256 * 1024);
return NGX_CONF_OK;
}
static void *
ngx_http_v2_create_srv_conf(ngx_conf_t *cf)
{
ngx_http_v2_srv_conf_t *h2scf;
h2scf = ngx_pcalloc(cf->pool, sizeof(ngx_http_v2_srv_conf_t));
if (h2scf == NULL) {
return NULL;
}
h2scf->pool_size = NGX_CONF_UNSET_SIZE;
h2scf->concurrent_streams = NGX_CONF_UNSET_UINT;
h2scf->concurrent_pushes = NGX_CONF_UNSET_UINT;
h2scf->max_requests = NGX_CONF_UNSET_UINT;
h2scf->max_field_size = NGX_CONF_UNSET_SIZE;
h2scf->max_header_size = NGX_CONF_UNSET_SIZE;
h2scf->preread_size = NGX_CONF_UNSET_SIZE;
h2scf->streams_index_mask = NGX_CONF_UNSET_UINT;
h2scf->recv_timeout = NGX_CONF_UNSET_MSEC;
h2scf->idle_timeout = NGX_CONF_UNSET_MSEC;
return h2scf;
}
static char *
ngx_http_v2_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
{
ngx_http_v2_srv_conf_t *prev = parent;
ngx_http_v2_srv_conf_t *conf = child;
ngx_conf_merge_size_value(conf->pool_size, prev->pool_size, 4096);
ngx_conf_merge_uint_value(conf->concurrent_streams,
prev->concurrent_streams, 128);
ngx_conf_merge_uint_value(conf->concurrent_pushes,
prev->concurrent_pushes, 10);
ngx_conf_merge_uint_value(conf->max_requests, prev->max_requests, 1000);
ngx_conf_merge_size_value(conf->max_field_size, prev->max_field_size,
4096);
ngx_conf_merge_size_value(conf->max_header_size, prev->max_header_size,
16384);
ngx_conf_merge_size_value(conf->preread_size, prev->preread_size, 65536);
ngx_conf_merge_uint_value(conf->streams_index_mask,
prev->streams_index_mask, 32 - 1);
ngx_conf_merge_msec_value(conf->recv_timeout,
prev->recv_timeout, 30000);
ngx_conf_merge_msec_value(conf->idle_timeout,
prev->idle_timeout, 180000);
return NGX_CONF_OK;
}
static void *
ngx_http_v2_create_loc_conf(ngx_conf_t *cf)
{
ngx_http_v2_loc_conf_t *h2lcf;
h2lcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_v2_loc_conf_t));
if (h2lcf == NULL) {
return NULL;
}
/*
* set by ngx_pcalloc():
*
* h2lcf->pushes = NULL;
*/
h2lcf->chunk_size = NGX_CONF_UNSET_SIZE;
h2lcf->push_preload = NGX_CONF_UNSET;
h2lcf->push = NGX_CONF_UNSET;
return h2lcf;
}
static char *
ngx_http_v2_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
{
ngx_http_v2_loc_conf_t *prev = parent;
ngx_http_v2_loc_conf_t *conf = child;
ngx_conf_merge_size_value(conf->chunk_size, prev->chunk_size, 8 * 1024);
ngx_conf_merge_value(conf->push, prev->push, 1);
if (conf->push && conf->pushes == NULL) {
conf->pushes = prev->pushes;
}
ngx_conf_merge_value(conf->push_preload, prev->push_preload, 0);
return NGX_CONF_OK;
}
static char *
ngx_http_v2_push(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_v2_loc_conf_t *h2lcf = conf;
ngx_str_t *value;
ngx_http_complex_value_t *cv;
ngx_http_compile_complex_value_t ccv;
value = cf->args->elts;
if (ngx_strcmp(value[1].data, "off") == 0) {
if (h2lcf->pushes) {
return "\"off\" parameter cannot be used with URI";
}
if (h2lcf->push == 0) {
return "is duplicate";
}
h2lcf->push = 0;
return NGX_CONF_OK;
}
if (h2lcf->push == 0) {
return "URI cannot be used with \"off\" parameter";
}
h2lcf->push = 1;
if (h2lcf->pushes == NULL) {
h2lcf->pushes = ngx_array_create(cf->pool, 1,
sizeof(ngx_http_complex_value_t));
if (h2lcf->pushes == NULL) {
return NGX_CONF_ERROR;
}
}
cv = ngx_array_push(h2lcf->pushes);
if (cv == NULL) {
return NGX_CONF_ERROR;
}
ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
ccv.cf = cf;
ccv.value = &value[1];
ccv.complex_value = cv;
if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
return NGX_CONF_ERROR;
}
return NGX_CONF_OK;
}
static char *
ngx_http_v2_recv_buffer_size(ngx_conf_t *cf, void *post, void *data)
{
size_t *sp = data;
if (*sp <= 2 * NGX_HTTP_V2_STATE_BUFFER_SIZE) {
return "value is too small";
}
return NGX_CONF_OK;
}
static char *
ngx_http_v2_pool_size(ngx_conf_t *cf, void *post, void *data)
{
size_t *sp = data;
if (*sp < NGX_MIN_POOL_SIZE) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"the pool size must be no less than %uz",
NGX_MIN_POOL_SIZE);
return NGX_CONF_ERROR;
}
if (*sp % NGX_POOL_ALIGNMENT) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"the pool size must be a multiple of %uz",
NGX_POOL_ALIGNMENT);
return NGX_CONF_ERROR;
}
return NGX_CONF_OK;
}
static char *
ngx_http_v2_preread_size(ngx_conf_t *cf, void *post, void *data)
{
size_t *sp = data;
if (*sp > NGX_HTTP_V2_MAX_WINDOW) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"the maximum body preread buffer size is %uz",
NGX_HTTP_V2_MAX_WINDOW);
return NGX_CONF_ERROR;
}
return NGX_CONF_OK;
}
static char *
ngx_http_v2_streams_index_mask(ngx_conf_t *cf, void *post, void *data)
{
ngx_uint_t *np = data;
ngx_uint_t mask;
mask = *np - 1;
if (*np == 0 || (*np & mask)) {
return "must be a power of two";
}
*np = mask;
return NGX_CONF_OK;
}
static char *
ngx_http_v2_chunk_size(ngx_conf_t *cf, void *post, void *data)
{
size_t *sp = data;
if (*sp == 0) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"the http2 chunk size cannot be zero");
return NGX_CONF_ERROR;
}
if (*sp > NGX_HTTP_V2_MAX_FRAME_SIZE) {
*sp = NGX_HTTP_V2_MAX_FRAME_SIZE;
}
return NGX_CONF_OK;
}
static char *
ngx_http_v2_spdy_deprecated(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
"invalid directive \"%V\": ngx_http_spdy_module "
"was superseded by ngx_http_v2_module", &cmd->name);
return NGX_CONF_OK;
}
nginx-1.14.0/src/http/v2/ngx_http_v2_module.h 000644 001751 001751 00000002355 13265410474 022176 0 ustar 00mdounin mdounin 000000 000000
/*
* Copyright (C) Nginx, Inc.
* Copyright (C) Valentin V. Bartenev
*/
#ifndef _NGX_HTTP_V2_MODULE_H_INCLUDED_
#define _NGX_HTTP_V2_MODULE_H_INCLUDED_
#include
#include
#include
typedef struct {
size_t recv_buffer_size;
u_char *recv_buffer;
} ngx_http_v2_main_conf_t;
typedef struct {
size_t pool_size;
ngx_uint_t concurrent_streams;
ngx_uint_t concurrent_pushes;
ngx_uint_t max_requests;
size_t max_field_size;
size_t max_header_size;
size_t preread_size;
ngx_uint_t streams_index_mask;
ngx_msec_t recv_timeout;
ngx_msec_t idle_timeout;
} ngx_http_v2_srv_conf_t;
typedef struct {
size_t chunk_size;
ngx_flag_t push_preload;
ngx_flag_t push;
ngx_array_t *pushes;
} ngx_http_v2_loc_conf_t;
extern ngx_module_t ngx_http_v2_module;
#endif /* _NGX_HTTP_V2_MODULE_H_INCLUDED_ */
nginx-1.14.0/src/http/v2/ngx_http_v2_table.c 000644 001751 001751 00000026661 13265410474 022001 0 ustar 00mdounin mdounin 000000 000000
/*
* Copyright (C) Nginx, Inc.
* Copyright (C) Valentin V. Bartenev
*/
#include
#include
#include
#define NGX_HTTP_V2_TABLE_SIZE 4096
static ngx_int_t ngx_http_v2_table_account(ngx_http_v2_connection_t *h2c,
size_t size);
static ngx_http_v2_header_t ngx_http_v2_static_table[] = {
{ ngx_string(":authority"), ngx_string("") },
{ ngx_string(":method"), ngx_string("GET") },
{ ngx_string(":method"), ngx_string("POST") },
{ ngx_string(":path"), ngx_string("/") },
{ ngx_string(":path"), ngx_string("/index.html") },
{ ngx_string(":scheme"), ngx_string("http") },
{ ngx_string(":scheme"), ngx_string("https") },
{ ngx_string(":status"), ngx_string("200") },
{ ngx_string(":status"), ngx_string("204") },
{ ngx_string(":status"), ngx_string("206") },
{ ngx_string(":status"), ngx_string("304") },
{ ngx_string(":status"), ngx_string("400") },
{ ngx_string(":status"), ngx_string("404") },
{ ngx_string(":status"), ngx_string("500") },
{ ngx_string("accept-charset"), ngx_string("") },
{ ngx_string("accept-encoding"), ngx_string("gzip, deflate") },
{ ngx_string("accept-language"), ngx_string("") },
{ ngx_string("accept-ranges"), ngx_string("") },
{ ngx_string("accept"), ngx_string("") },
{ ngx_string("access-control-allow-origin"), ngx_string("") },
{ ngx_string("age"), ngx_string("") },
{ ngx_string("allow"), ngx_string("") },
{ ngx_string("authorization"), ngx_string("") },
{ ngx_string("cache-control"), ngx_string("") },
{ ngx_string("content-disposition"), ngx_string("") },
{ ngx_string("content-encoding"), ngx_string("") },
{ ngx_string("content-language"), ngx_string("") },
{ ngx_string("content-length"), ngx_string("") },
{ ngx_string("content-location"), ngx_string("") },
{ ngx_string("content-range"), ngx_string("") },
{ ngx_string("content-type"), ngx_string("") },
{ ngx_string("cookie"), ngx_string("") },
{ ngx_string("date"), ngx_string("") },
{ ngx_string("etag"), ngx_string("") },
{ ngx_string("expect"), ngx_string("") },
{ ngx_string("expires"), ngx_string("") },
{ ngx_string("from"), ngx_string("") },
{ ngx_string("host"), ngx_string("") },
{ ngx_string("if-match"), ngx_string("") },
{ ngx_string("if-modified-since"), ngx_string("") },
{ ngx_string("if-none-match"), ngx_string("") },
{ ngx_string("if-range"), ngx_string("") },
{ ngx_string("if-unmodified-since"), ngx_string("") },
{ ngx_string("last-modified"), ngx_string("") },
{ ngx_string("link"), ngx_string("") },
{ ngx_string("location"), ngx_string("") },
{ ngx_string("max-forwards"), ngx_string("") },
{ ngx_string("proxy-authenticate"), ngx_string("") },
{ ngx_string("proxy-authorization"), ngx_string("") },
{ ngx_string("range"), ngx_string("") },
{ ngx_string("referer"), ngx_string("") },
{ ngx_string("refresh"), ngx_string("") },
{ ngx_string("retry-after"), ngx_string("") },
{ ngx_string("server"), ngx_string("") },
{ ngx_string("set-cookie"), ngx_string("") },
{ ngx_string("strict-transport-security"), ngx_string("") },
{ ngx_string("transfer-encoding"), ngx_string("") },
{ ngx_string("user-agent"), ngx_string("") },
{ ngx_string("vary"), ngx_string("") },
{ ngx_string("via"), ngx_string("") },
{ ngx_string("www-authenticate"), ngx_string("") },
};
#define NGX_HTTP_V2_STATIC_TABLE_ENTRIES \
(sizeof(ngx_http_v2_static_table) \
/ sizeof(ngx_http_v2_header_t))
ngx_str_t *
ngx_http_v2_get_static_name(ngx_uint_t index)
{
return &ngx_http_v2_static_table[index - 1].name;
}
ngx_str_t *
ngx_http_v2_get_static_value(ngx_uint_t index)
{
return &ngx_http_v2_static_table[index - 1].value;
}
ngx_int_t
ngx_http_v2_get_indexed_header(ngx_http_v2_connection_t *h2c, ngx_uint_t index,
ngx_uint_t name_only)
{
u_char *p;
size_t rest;
ngx_http_v2_header_t *entry;
if (index == 0) {
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
"client sent invalid hpack table index 0");
return NGX_ERROR;
}
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
"http2 get indexed %s: %ui",
name_only ? "name" : "header", index);
index--;
if (index < NGX_HTTP_V2_STATIC_TABLE_ENTRIES) {
h2c->state.header = ngx_http_v2_static_table[index];
return NGX_OK;
}
index -= NGX_HTTP_V2_STATIC_TABLE_ENTRIES;
if (index < h2c->hpack.added - h2c->hpack.deleted) {
index = (h2c->hpack.added - index - 1) % h2c->hpack.allocated;
entry = h2c->hpack.entries[index];
p = ngx_pnalloc(h2c->state.pool, entry->name.len + 1);
if (p == NULL) {
return NGX_ERROR;
}
h2c->state.header.name.len = entry->name.len;
h2c->state.header.name.data = p;
rest = h2c->hpack.storage + NGX_HTTP_V2_TABLE_SIZE - entry->name.data;
if (entry->name.len > rest) {
p = ngx_cpymem(p, entry->name.data, rest);
p = ngx_cpymem(p, h2c->hpack.storage, entry->name.len - rest);
} else {
p = ngx_cpymem(p, entry->name.data, entry->name.len);
}
*p = '\0';
if (name_only) {
return NGX_OK;
}
p = ngx_pnalloc(h2c->state.pool, entry->value.len + 1);
if (p == NULL) {
return NGX_ERROR;
}
h2c->state.header.value.len = entry->value.len;
h2c->state.header.value.data = p;
rest = h2c->hpack.storage + NGX_HTTP_V2_TABLE_SIZE - entry->value.data;
if (entry->value.len > rest) {
p = ngx_cpymem(p, entry->value.data, rest);
p = ngx_cpymem(p, h2c->hpack.storage, entry->value.len - rest);
} else {
p = ngx_cpymem(p, entry->value.data, entry->value.len);
}
*p = '\0';
return NGX_OK;
}
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
"client sent out of bound hpack table index: %ui", index);
return NGX_ERROR;
}
ngx_int_t
ngx_http_v2_add_header(ngx_http_v2_connection_t *h2c,
ngx_http_v2_header_t *header)
{
size_t avail;
ngx_uint_t index;
ngx_http_v2_header_t *entry, **entries;
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
"http2 table add: \"%V: %V\"",
&header->name, &header->value);
if (h2c->hpack.entries == NULL) {
h2c->hpack.allocated = 64;
h2c->hpack.size = NGX_HTTP_V2_TABLE_SIZE;
h2c->hpack.free = NGX_HTTP_V2_TABLE_SIZE;
h2c->hpack.entries = ngx_palloc(h2c->connection->pool,
sizeof(ngx_http_v2_header_t *)
* h2c->hpack.allocated);
if (h2c->hpack.entries == NULL) {
return NGX_ERROR;
}
h2c->hpack.storage = ngx_palloc(h2c->connection->pool,
h2c->hpack.free);
if (h2c->hpack.storage == NULL) {
return NGX_ERROR;
}
h2c->hpack.pos = h2c->hpack.storage;
}
if (ngx_http_v2_table_account(h2c, header->name.len + header->value.len)
!= NGX_OK)
{
return NGX_OK;
}
if (h2c->hpack.reused == h2c->hpack.deleted) {
entry = ngx_palloc(h2c->connection->pool, sizeof(ngx_http_v2_header_t));
if (entry == NULL) {
return NGX_ERROR;
}
} else {
entry = h2c->hpack.entries[h2c->hpack.reused++ % h2c->hpack.allocated];
}
avail = h2c->hpack.storage + NGX_HTTP_V2_TABLE_SIZE - h2c->hpack.pos;
entry->name.len = header->name.len;
entry->name.data = h2c->hpack.pos;
if (avail >= header->name.len) {
h2c->hpack.pos = ngx_cpymem(h2c->hpack.pos, header->name.data,
header->name.len);
} else {
ngx_memcpy(h2c->hpack.pos, header->name.data, avail);
h2c->hpack.pos = ngx_cpymem(h2c->hpack.storage,
header->name.data + avail,
header->name.len - avail);
avail = NGX_HTTP_V2_TABLE_SIZE;
}
avail -= header->name.len;
entry->value.len = header->value.len;
entry->value.data = h2c->hpack.pos;
if (avail >= header->value.len) {
h2c->hpack.pos = ngx_cpymem(h2c->hpack.pos, header->value.data,
header->value.len);
} else {
ngx_memcpy(h2c->hpack.pos, header->value.data, avail);
h2c->hpack.pos = ngx_cpymem(h2c->hpack.storage,
header->value.data + avail,
header->value.len - avail);
}
if (h2c->hpack.allocated == h2c->hpack.added - h2c->hpack.deleted) {
entries = ngx_palloc(h2c->connection->pool,
sizeof(ngx_http_v2_header_t *)
* (h2c->hpack.allocated + 64));
if (entries == NULL) {
return NGX_ERROR;
}
index = h2c->hpack.deleted % h2c->hpack.allocated;
ngx_memcpy(entries, &h2c->hpack.entries[index],
(h2c->hpack.allocated - index)
* sizeof(ngx_http_v2_header_t *));
ngx_memcpy(&entries[h2c->hpack.allocated - index], h2c->hpack.entries,
index * sizeof(ngx_http_v2_header_t *));
(void) ngx_pfree(h2c->connection->pool, h2c->hpack.entries);
h2c->hpack.entries = entries;
h2c->hpack.added = h2c->hpack.allocated;
h2c->hpack.deleted = 0;
h2c->hpack.reused = 0;
h2c->hpack.allocated += 64;
}
h2c->hpack.entries[h2c->hpack.added++ % h2c->hpack.allocated] = entry;
return NGX_OK;
}
static ngx_int_t
ngx_http_v2_table_account(ngx_http_v2_connection_t *h2c, size_t size)
{
ngx_http_v2_header_t *entry;
size += 32;
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
"http2 table account: %uz free:%uz",
size, h2c->hpack.free);
if (size <= h2c->hpack.free) {
h2c->hpack.free -= size;
return NGX_OK;
}
if (size > h2c->hpack.size) {
h2c->hpack.deleted = h2c->hpack.added;
h2c->hpack.free = h2c->hpack.size;
return NGX_DECLINED;
}
do {
entry = h2c->hpack.entries[h2c->hpack.deleted++ % h2c->hpack.allocated];
h2c->hpack.free += 32 + entry->name.len + entry->value.len;
} while (size > h2c->hpack.free);
h2c->hpack.free -= size;
return NGX_OK;
}
ngx_int_t
ngx_http_v2_table_size(ngx_http_v2_connection_t *h2c, size_t size)
{
ssize_t needed;
ngx_http_v2_header_t *entry;
if (size > NGX_HTTP_V2_TABLE_SIZE) {
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
"client sent invalid table size update: %uz", size);
return NGX_ERROR;
}
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
"http2 new hpack table size: %uz was:%uz",
size, h2c->hpack.size);
needed = h2c->hpack.size - size;
while (needed > (ssize_t) h2c->hpack.free) {
entry = h2c->hpack.entries[h2c->hpack.deleted++ % h2c->hpack.allocated];
h2c->hpack.free += 32 + entry->name.len + entry->value.len;
}
h2c->hpack.size = size;
h2c->hpack.free -= needed;
return NGX_OK;
}
nginx-1.14.0/src/http/modules/ngx_http_addition_filter_module.c 000644 001751 001751 00000015522 13265410474 026123 0 ustar 00mdounin mdounin 000000 000000
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#include
#include
#include
typedef struct {
ngx_str_t before_body;
ngx_str_t after_body;
ngx_hash_t types;
ngx_array_t *types_keys;
} ngx_http_addition_conf_t;
typedef struct {
ngx_uint_t before_body_sent;
} ngx_http_addition_ctx_t;
static void *ngx_http_addition_create_conf(ngx_conf_t *cf);
static char *ngx_http_addition_merge_conf(ngx_conf_t *cf, void *parent,
void *child);
static ngx_int_t ngx_http_addition_filter_init(ngx_conf_t *cf);
static ngx_command_t ngx_http_addition_commands[] = {
{ ngx_string("add_before_body"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_str_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_addition_conf_t, before_body),
NULL },
{ ngx_string("add_after_body"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_str_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_addition_conf_t, after_body),
NULL },
{ ngx_string("addition_types"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
ngx_http_types_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_addition_conf_t, types_keys),
&ngx_http_html_default_types[0] },
ngx_null_command
};
static ngx_http_module_t ngx_http_addition_filter_module_ctx = {
NULL, /* preconfiguration */
ngx_http_addition_filter_init, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
ngx_http_addition_create_conf, /* create location configuration */
ngx_http_addition_merge_conf /* merge location configuration */
};
ngx_module_t ngx_http_addition_filter_module = {
NGX_MODULE_V1,
&ngx_http_addition_filter_module_ctx, /* module context */
ngx_http_addition_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static ngx_http_output_header_filter_pt ngx_http_next_header_filter;
static ngx_http_output_body_filter_pt ngx_http_next_body_filter;
static ngx_int_t
ngx_http_addition_header_filter(ngx_http_request_t *r)
{
ngx_http_addition_ctx_t *ctx;
ngx_http_addition_conf_t *conf;
if (r->headers_out.status != NGX_HTTP_OK || r != r->main) {
return ngx_http_next_header_filter(r);
}
conf = ngx_http_get_module_loc_conf(r, ngx_http_addition_filter_module);
if (conf->before_body.len == 0 && conf->after_body.len == 0) {
return ngx_http_next_header_filter(r);
}
if (ngx_http_test_content_type(r, &conf->types) == NULL) {
return ngx_http_next_header_filter(r);
}
ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_addition_ctx_t));
if (ctx == NULL) {
return NGX_ERROR;
}
ngx_http_set_ctx(r, ctx, ngx_http_addition_filter_module);
ngx_http_clear_content_length(r);
ngx_http_clear_accept_ranges(r);
ngx_http_weak_etag(r);
r->preserve_body = 1;
return ngx_http_next_header_filter(r);
}
static ngx_int_t
ngx_http_addition_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
ngx_int_t rc;
ngx_uint_t last;
ngx_chain_t *cl;
ngx_http_request_t *sr;
ngx_http_addition_ctx_t *ctx;
ngx_http_addition_conf_t *conf;
if (in == NULL || r->header_only) {
return ngx_http_next_body_filter(r, in);
}
ctx = ngx_http_get_module_ctx(r, ngx_http_addition_filter_module);
if (ctx == NULL) {
return ngx_http_next_body_filter(r, in);
}
conf = ngx_http_get_module_loc_conf(r, ngx_http_addition_filter_module);
if (!ctx->before_body_sent) {
ctx->before_body_sent = 1;
if (conf->before_body.len) {
if (ngx_http_subrequest(r, &conf->before_body, NULL, &sr, NULL, 0)
!= NGX_OK)
{
return NGX_ERROR;
}
}
}
if (conf->after_body.len == 0) {
ngx_http_set_ctx(r, NULL, ngx_http_addition_filter_module);
return ngx_http_next_body_filter(r, in);
}
last = 0;
for (cl = in; cl; cl = cl->next) {
if (cl->buf->last_buf) {
cl->buf->last_buf = 0;
cl->buf->last_in_chain = 1;
cl->buf->sync = 1;
last = 1;
}
}
rc = ngx_http_next_body_filter(r, in);
if (rc == NGX_ERROR || !last || conf->after_body.len == 0) {
return rc;
}
if (ngx_http_subrequest(r, &conf->after_body, NULL, &sr, NULL, 0)
!= NGX_OK)
{
return NGX_ERROR;
}
ngx_http_set_ctx(r, NULL, ngx_http_addition_filter_module);
return ngx_http_send_special(r, NGX_HTTP_LAST);
}
static ngx_int_t
ngx_http_addition_filter_init(ngx_conf_t *cf)
{
ngx_http_next_header_filter = ngx_http_top_header_filter;
ngx_http_top_header_filter = ngx_http_addition_header_filter;
ngx_http_next_body_filter = ngx_http_top_body_filter;
ngx_http_top_body_filter = ngx_http_addition_body_filter;
return NGX_OK;
}
static void *
ngx_http_addition_create_conf(ngx_conf_t *cf)
{
ngx_http_addition_conf_t *conf;
conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_addition_conf_t));
if (conf == NULL) {
return NULL;
}
/*
* set by ngx_pcalloc():
*
* conf->before_body = { 0, NULL };
* conf->after_body = { 0, NULL };
* conf->types = { NULL };
* conf->types_keys = NULL;
*/
return conf;
}
static char *
ngx_http_addition_merge_conf(ngx_conf_t *cf, void *parent, void *child)
{
ngx_http_addition_conf_t *prev = parent;
ngx_http_addition_conf_t *conf = child;
ngx_conf_merge_str_value(conf->before_body, prev->before_body, "");
ngx_conf_merge_str_value(conf->after_body, prev->after_body, "");
if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types,
&prev->types_keys, &prev->types,
ngx_http_html_default_types)
!= NGX_OK)
{
return NGX_CONF_ERROR;
}
return NGX_CONF_OK;
}
nginx-1.14.0/src/http/modules/ngx_http_access_module.c 000644 001751 001751 00000027132 13265410474 024224 0 ustar 00mdounin mdounin 000000 000000
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#include
#include
#include
typedef struct {
in_addr_t mask;
in_addr_t addr;
ngx_uint_t deny; /* unsigned deny:1; */
} ngx_http_access_rule_t;
#if (NGX_HAVE_INET6)
typedef struct {
struct in6_addr addr;
struct in6_addr mask;
ngx_uint_t deny; /* unsigned deny:1; */
} ngx_http_access_rule6_t;
#endif
#if (NGX_HAVE_UNIX_DOMAIN)
typedef struct {
ngx_uint_t deny; /* unsigned deny:1; */
} ngx_http_access_rule_un_t;
#endif
typedef struct {
ngx_array_t *rules; /* array of ngx_http_access_rule_t */
#if (NGX_HAVE_INET6)
ngx_array_t *rules6; /* array of ngx_http_access_rule6_t */
#endif
#if (NGX_HAVE_UNIX_DOMAIN)
ngx_array_t *rules_un; /* array of ngx_http_access_rule_un_t */
#endif
} ngx_http_access_loc_conf_t;
static ngx_int_t ngx_http_access_handler(ngx_http_request_t *r);
static ngx_int_t ngx_http_access_inet(ngx_http_request_t *r,
ngx_http_access_loc_conf_t *alcf, in_addr_t addr);
#if (NGX_HAVE_INET6)
static ngx_int_t ngx_http_access_inet6(ngx_http_request_t *r,
ngx_http_access_loc_conf_t *alcf, u_char *p);
#endif
#if (NGX_HAVE_UNIX_DOMAIN)
static ngx_int_t ngx_http_access_unix(ngx_http_request_t *r,
ngx_http_access_loc_conf_t *alcf);
#endif
static ngx_int_t ngx_http_access_found(ngx_http_request_t *r, ngx_uint_t deny);
static char *ngx_http_access_rule(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static void *ngx_http_access_create_loc_conf(ngx_conf_t *cf);
static char *ngx_http_access_merge_loc_conf(ngx_conf_t *cf,
void *parent, void *child);
static ngx_int_t ngx_http_access_init(ngx_conf_t *cf);
static ngx_command_t ngx_http_access_commands[] = {
{ ngx_string("allow"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF
|NGX_CONF_TAKE1,
ngx_http_access_rule,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
{ ngx_string("deny"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF
|NGX_CONF_TAKE1,
ngx_http_access_rule,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
ngx_null_command
};
static ngx_http_module_t ngx_http_access_module_ctx = {
NULL, /* preconfiguration */
ngx_http_access_init, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
ngx_http_access_create_loc_conf, /* create location configuration */
ngx_http_access_merge_loc_conf /* merge location configuration */
};
ngx_module_t ngx_http_access_module = {
NGX_MODULE_V1,
&ngx_http_access_module_ctx, /* module context */
ngx_http_access_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static ngx_int_t
ngx_http_access_handler(ngx_http_request_t *r)
{
struct sockaddr_in *sin;
ngx_http_access_loc_conf_t *alcf;
#if (NGX_HAVE_INET6)
u_char *p;
in_addr_t addr;
struct sockaddr_in6 *sin6;
#endif
alcf = ngx_http_get_module_loc_conf(r, ngx_http_access_module);
switch (r->connection->sockaddr->sa_family) {
case AF_INET:
if (alcf->rules) {
sin = (struct sockaddr_in *) r->connection->sockaddr;
return ngx_http_access_inet(r, alcf, sin->sin_addr.s_addr);
}
break;
#if (NGX_HAVE_INET6)
case AF_INET6:
sin6 = (struct sockaddr_in6 *) r->connection->sockaddr;
p = sin6->sin6_addr.s6_addr;
if (alcf->rules && IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
addr = p[12] << 24;
addr += p[13] << 16;
addr += p[14] << 8;
addr += p[15];
return ngx_http_access_inet(r, alcf, htonl(addr));
}
if (alcf->rules6) {
return ngx_http_access_inet6(r, alcf, p);
}
break;
#endif
#if (NGX_HAVE_UNIX_DOMAIN)
case AF_UNIX:
if (alcf->rules_un) {
return ngx_http_access_unix(r, alcf);
}
break;
#endif
}
return NGX_DECLINED;
}
static ngx_int_t
ngx_http_access_inet(ngx_http_request_t *r, ngx_http_access_loc_conf_t *alcf,
in_addr_t addr)
{
ngx_uint_t i;
ngx_http_access_rule_t *rule;
rule = alcf->rules->elts;
for (i = 0; i < alcf->rules->nelts; i++) {
ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"access: %08XD %08XD %08XD",
addr, rule[i].mask, rule[i].addr);
if ((addr & rule[i].mask) == rule[i].addr) {
return ngx_http_access_found(r, rule[i].deny);
}
}
return NGX_DECLINED;
}
#if (NGX_HAVE_INET6)
static ngx_int_t
ngx_http_access_inet6(ngx_http_request_t *r, ngx_http_access_loc_conf_t *alcf,
u_char *p)
{
ngx_uint_t n;
ngx_uint_t i;
ngx_http_access_rule6_t *rule6;
rule6 = alcf->rules6->elts;
for (i = 0; i < alcf->rules6->nelts; i++) {
#if (NGX_DEBUG)
{
size_t cl, ml, al;
u_char ct[NGX_INET6_ADDRSTRLEN];
u_char mt[NGX_INET6_ADDRSTRLEN];
u_char at[NGX_INET6_ADDRSTRLEN];
cl = ngx_inet6_ntop(p, ct, NGX_INET6_ADDRSTRLEN);
ml = ngx_inet6_ntop(rule6[i].mask.s6_addr, mt, NGX_INET6_ADDRSTRLEN);
al = ngx_inet6_ntop(rule6[i].addr.s6_addr, at, NGX_INET6_ADDRSTRLEN);
ngx_log_debug6(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"access: %*s %*s %*s", cl, ct, ml, mt, al, at);
}
#endif
for (n = 0; n < 16; n++) {
if ((p[n] & rule6[i].mask.s6_addr[n]) != rule6[i].addr.s6_addr[n]) {
goto next;
}
}
return ngx_http_access_found(r, rule6[i].deny);
next:
continue;
}
return NGX_DECLINED;
}
#endif
#if (NGX_HAVE_UNIX_DOMAIN)
static ngx_int_t
ngx_http_access_unix(ngx_http_request_t *r, ngx_http_access_loc_conf_t *alcf)
{
ngx_uint_t i;
ngx_http_access_rule_un_t *rule_un;
rule_un = alcf->rules_un->elts;
for (i = 0; i < alcf->rules_un->nelts; i++) {
/* TODO: check path */
if (1) {
return ngx_http_access_found(r, rule_un[i].deny);
}
}
return NGX_DECLINED;
}
#endif
static ngx_int_t
ngx_http_access_found(ngx_http_request_t *r, ngx_uint_t deny)
{
ngx_http_core_loc_conf_t *clcf;
if (deny) {
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
if (clcf->satisfy == NGX_HTTP_SATISFY_ALL) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"access forbidden by rule");
}
return NGX_HTTP_FORBIDDEN;
}
return NGX_OK;
}
static char *
ngx_http_access_rule(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_access_loc_conf_t *alcf = conf;
ngx_int_t rc;
ngx_uint_t all;
ngx_str_t *value;
ngx_cidr_t cidr;
ngx_http_access_rule_t *rule;
#if (NGX_HAVE_INET6)
ngx_http_access_rule6_t *rule6;
#endif
#if (NGX_HAVE_UNIX_DOMAIN)
ngx_http_access_rule_un_t *rule_un;
#endif
all = 0;
ngx_memzero(&cidr, sizeof(ngx_cidr_t));
value = cf->args->elts;
if (value[1].len == 3 && ngx_strcmp(value[1].data, "all") == 0) {
all = 1;
#if (NGX_HAVE_UNIX_DOMAIN)
} else if (value[1].len == 5 && ngx_strcmp(value[1].data, "unix:") == 0) {
cidr.family = AF_UNIX;
#endif
} else {
rc = ngx_ptocidr(&value[1], &cidr);
if (rc == NGX_ERROR) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid parameter \"%V\"", &value[1]);
return NGX_CONF_ERROR;
}
if (rc == NGX_DONE) {
ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
"low address bits of %V are meaningless", &value[1]);
}
}
if (cidr.family == AF_INET || all) {
if (alcf->rules == NULL) {
alcf->rules = ngx_array_create(cf->pool, 4,
sizeof(ngx_http_access_rule_t));
if (alcf->rules == NULL) {
return NGX_CONF_ERROR;
}
}
rule = ngx_array_push(alcf->rules);
if (rule == NULL) {
return NGX_CONF_ERROR;
}
rule->mask = cidr.u.in.mask;
rule->addr = cidr.u.in.addr;
rule->deny = (value[0].data[0] == 'd') ? 1 : 0;
}
#if (NGX_HAVE_INET6)
if (cidr.family == AF_INET6 || all) {
if (alcf->rules6 == NULL) {
alcf->rules6 = ngx_array_create(cf->pool, 4,
sizeof(ngx_http_access_rule6_t));
if (alcf->rules6 == NULL) {
return NGX_CONF_ERROR;
}
}
rule6 = ngx_array_push(alcf->rules6);
if (rule6 == NULL) {
return NGX_CONF_ERROR;
}
rule6->mask = cidr.u.in6.mask;
rule6->addr = cidr.u.in6.addr;
rule6->deny = (value[0].data[0] == 'd') ? 1 : 0;
}
#endif
#if (NGX_HAVE_UNIX_DOMAIN)
if (cidr.family == AF_UNIX || all) {
if (alcf->rules_un == NULL) {
alcf->rules_un = ngx_array_create(cf->pool, 1,
sizeof(ngx_http_access_rule_un_t));
if (alcf->rules_un == NULL) {
return NGX_CONF_ERROR;
}
}
rule_un = ngx_array_push(alcf->rules_un);
if (rule_un == NULL) {
return NGX_CONF_ERROR;
}
rule_un->deny = (value[0].data[0] == 'd') ? 1 : 0;
}
#endif
return NGX_CONF_OK;
}
static void *
ngx_http_access_create_loc_conf(ngx_conf_t *cf)
{
ngx_http_access_loc_conf_t *conf;
conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_access_loc_conf_t));
if (conf == NULL) {
return NULL;
}
return conf;
}
static char *
ngx_http_access_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
{
ngx_http_access_loc_conf_t *prev = parent;
ngx_http_access_loc_conf_t *conf = child;
if (conf->rules == NULL
#if (NGX_HAVE_INET6)
&& conf->rules6 == NULL
#endif
#if (NGX_HAVE_UNIX_DOMAIN)
&& conf->rules_un == NULL
#endif
) {
conf->rules = prev->rules;
#if (NGX_HAVE_INET6)
conf->rules6 = prev->rules6;
#endif
#if (NGX_HAVE_UNIX_DOMAIN)
conf->rules_un = prev->rules_un;
#endif
}
return NGX_CONF_OK;
}
static ngx_int_t
ngx_http_access_init(ngx_conf_t *cf)
{
ngx_http_handler_pt *h;
ngx_http_core_main_conf_t *cmcf;
cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
h = ngx_array_push(&cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers);
if (h == NULL) {
return NGX_ERROR;
}
*h = ngx_http_access_handler;
return NGX_OK;
}
nginx-1.14.0/src/http/modules/ngx_http_charset_filter_module.c 000644 001751 001751 00000120166 13265410474 025762 0 ustar 00mdounin mdounin 000000 000000
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#include
#include
#include
#define NGX_HTTP_CHARSET_OFF -2
#define NGX_HTTP_NO_CHARSET -3
#define NGX_HTTP_CHARSET_VAR 0x10000
/* 1 byte length and up to 3 bytes for the UTF-8 encoding of the UCS-2 */
#define NGX_UTF_LEN 4
#define NGX_HTML_ENTITY_LEN (sizeof("") - 1)
typedef struct {
u_char **tables;
ngx_str_t name;
unsigned length:16;
unsigned utf8:1;
} ngx_http_charset_t;
typedef struct {
ngx_int_t src;
ngx_int_t dst;
} ngx_http_charset_recode_t;
typedef struct {
ngx_int_t src;
ngx_int_t dst;
u_char *src2dst;
u_char *dst2src;
} ngx_http_charset_tables_t;
typedef struct {
ngx_array_t charsets; /* ngx_http_charset_t */
ngx_array_t tables; /* ngx_http_charset_tables_t */
ngx_array_t recodes; /* ngx_http_charset_recode_t */
} ngx_http_charset_main_conf_t;
typedef struct {
ngx_int_t charset;
ngx_int_t source_charset;
ngx_flag_t override_charset;
ngx_hash_t types;
ngx_array_t *types_keys;
} ngx_http_charset_loc_conf_t;
typedef struct {
u_char *table;
ngx_int_t charset;
ngx_str_t charset_name;
ngx_chain_t *busy;
ngx_chain_t *free_bufs;
ngx_chain_t *free_buffers;
size_t saved_len;
u_char saved[NGX_UTF_LEN];
unsigned length:16;
unsigned from_utf8:1;
unsigned to_utf8:1;
} ngx_http_charset_ctx_t;
typedef struct {
ngx_http_charset_tables_t *table;
ngx_http_charset_t *charset;
ngx_uint_t characters;
} ngx_http_charset_conf_ctx_t;
static ngx_int_t ngx_http_destination_charset(ngx_http_request_t *r,
ngx_str_t *name);
static ngx_int_t ngx_http_main_request_charset(ngx_http_request_t *r,
ngx_str_t *name);
static ngx_int_t ngx_http_source_charset(ngx_http_request_t *r,
ngx_str_t *name);
static ngx_int_t ngx_http_get_charset(ngx_http_request_t *r, ngx_str_t *name);
static ngx_inline void ngx_http_set_charset(ngx_http_request_t *r,
ngx_str_t *charset);
static ngx_int_t ngx_http_charset_ctx(ngx_http_request_t *r,
ngx_http_charset_t *charsets, ngx_int_t charset, ngx_int_t source_charset);
static ngx_uint_t ngx_http_charset_recode(ngx_buf_t *b, u_char *table);
static ngx_chain_t *ngx_http_charset_recode_from_utf8(ngx_pool_t *pool,
ngx_buf_t *buf, ngx_http_charset_ctx_t *ctx);
static ngx_chain_t *ngx_http_charset_recode_to_utf8(ngx_pool_t *pool,
ngx_buf_t *buf, ngx_http_charset_ctx_t *ctx);
static ngx_chain_t *ngx_http_charset_get_buf(ngx_pool_t *pool,
ngx_http_charset_ctx_t *ctx);
static ngx_chain_t *ngx_http_charset_get_buffer(ngx_pool_t *pool,
ngx_http_charset_ctx_t *ctx, size_t size);
static char *ngx_http_charset_map_block(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static char *ngx_http_charset_map(ngx_conf_t *cf, ngx_command_t *dummy,
void *conf);
static char *ngx_http_set_charset_slot(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static ngx_int_t ngx_http_add_charset(ngx_array_t *charsets, ngx_str_t *name);
static void *ngx_http_charset_create_main_conf(ngx_conf_t *cf);
static void *ngx_http_charset_create_loc_conf(ngx_conf_t *cf);
static char *ngx_http_charset_merge_loc_conf(ngx_conf_t *cf,
void *parent, void *child);
static ngx_int_t ngx_http_charset_postconfiguration(ngx_conf_t *cf);
static ngx_str_t ngx_http_charset_default_types[] = {
ngx_string("text/html"),
ngx_string("text/xml"),
ngx_string("text/plain"),
ngx_string("text/vnd.wap.wml"),
ngx_string("application/javascript"),
ngx_string("application/rss+xml"),
ngx_null_string
};
static ngx_command_t ngx_http_charset_filter_commands[] = {
{ ngx_string("charset"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF
|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,
ngx_http_set_charset_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_charset_loc_conf_t, charset),
NULL },
{ ngx_string("source_charset"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF
|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,
ngx_http_set_charset_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_charset_loc_conf_t, source_charset),
NULL },
{ ngx_string("override_charset"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF
|NGX_HTTP_LIF_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_charset_loc_conf_t, override_charset),
NULL },
{ ngx_string("charset_types"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
ngx_http_types_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_charset_loc_conf_t, types_keys),
&ngx_http_charset_default_types[0] },
{ ngx_string("charset_map"),
NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE2,
ngx_http_charset_map_block,
NGX_HTTP_MAIN_CONF_OFFSET,
0,
NULL },
ngx_null_command
};
static ngx_http_module_t ngx_http_charset_filter_module_ctx = {
NULL, /* preconfiguration */
ngx_http_charset_postconfiguration, /* postconfiguration */
ngx_http_charset_create_main_conf, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
ngx_http_charset_create_loc_conf, /* create location configuration */
ngx_http_charset_merge_loc_conf /* merge location configuration */
};
ngx_module_t ngx_http_charset_filter_module = {
NGX_MODULE_V1,
&ngx_http_charset_filter_module_ctx, /* module context */
ngx_http_charset_filter_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static ngx_http_output_header_filter_pt ngx_http_next_header_filter;
static ngx_http_output_body_filter_pt ngx_http_next_body_filter;
static ngx_int_t
ngx_http_charset_header_filter(ngx_http_request_t *r)
{
ngx_int_t charset, source_charset;
ngx_str_t dst, src;
ngx_http_charset_t *charsets;
ngx_http_charset_main_conf_t *mcf;
if (r == r->main) {
charset = ngx_http_destination_charset(r, &dst);
} else {
charset = ngx_http_main_request_charset(r, &dst);
}
if (charset == NGX_ERROR) {
return NGX_ERROR;
}
if (charset == NGX_DECLINED) {
return ngx_http_next_header_filter(r);
}
/* charset: charset index or NGX_HTTP_NO_CHARSET */
source_charset = ngx_http_source_charset(r, &src);
if (source_charset == NGX_ERROR) {
return NGX_ERROR;
}
/*
* source_charset: charset index, NGX_HTTP_NO_CHARSET,
* or NGX_HTTP_CHARSET_OFF
*/
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"charset: \"%V\" > \"%V\"", &src, &dst);
if (source_charset == NGX_HTTP_CHARSET_OFF) {
ngx_http_set_charset(r, &dst);
return ngx_http_next_header_filter(r);
}
if (charset == NGX_HTTP_NO_CHARSET
|| source_charset == NGX_HTTP_NO_CHARSET)
{
if (source_charset != charset
|| ngx_strncasecmp(dst.data, src.data, dst.len) != 0)
{
goto no_charset_map;
}
ngx_http_set_charset(r, &dst);
return ngx_http_next_header_filter(r);
}
if (source_charset == charset) {
r->headers_out.content_type.len = r->headers_out.content_type_len;
ngx_http_set_charset(r, &dst);
return ngx_http_next_header_filter(r);
}
/* source_charset != charset */
if (r->headers_out.content_encoding
&& r->headers_out.content_encoding->value.len)
{
return ngx_http_next_header_filter(r);
}
mcf = ngx_http_get_module_main_conf(r, ngx_http_charset_filter_module);
charsets = mcf->charsets.elts;
if (charsets[source_charset].tables == NULL
|| charsets[source_charset].tables[charset] == NULL)
{
goto no_charset_map;
}
r->headers_out.content_type.len = r->headers_out.content_type_len;
ngx_http_set_charset(r, &dst);
return ngx_http_charset_ctx(r, charsets, charset, source_charset);
no_charset_map:
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"no \"charset_map\" between the charsets \"%V\" and \"%V\"",
&src, &dst);
return ngx_http_next_header_filter(r);
}
static ngx_int_t
ngx_http_destination_charset(ngx_http_request_t *r, ngx_str_t *name)
{
ngx_int_t charset;
ngx_http_charset_t *charsets;
ngx_http_variable_value_t *vv;
ngx_http_charset_loc_conf_t *mlcf;
ngx_http_charset_main_conf_t *mcf;
if (r->headers_out.content_type.len == 0) {
return NGX_DECLINED;
}
if (r->headers_out.override_charset
&& r->headers_out.override_charset->len)
{
*name = *r->headers_out.override_charset;
charset = ngx_http_get_charset(r, name);
if (charset != NGX_HTTP_NO_CHARSET) {
return charset;
}
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"unknown charset \"%V\" to override", name);
return NGX_DECLINED;
}
mlcf = ngx_http_get_module_loc_conf(r, ngx_http_charset_filter_module);
charset = mlcf->charset;
if (charset == NGX_HTTP_CHARSET_OFF) {
return NGX_DECLINED;
}
if (r->headers_out.charset.len) {
if (mlcf->override_charset == 0) {
return NGX_DECLINED;
}
} else {
if (ngx_http_test_content_type(r, &mlcf->types) == NULL) {
return NGX_DECLINED;
}
}
if (charset < NGX_HTTP_CHARSET_VAR) {
mcf = ngx_http_get_module_main_conf(r, ngx_http_charset_filter_module);
charsets = mcf->charsets.elts;
*name = charsets[charset].name;
return charset;
}
vv = ngx_http_get_indexed_variable(r, charset - NGX_HTTP_CHARSET_VAR);
if (vv == NULL || vv->not_found) {
return NGX_ERROR;
}
name->len = vv->len;
name->data = vv->data;
return ngx_http_get_charset(r, name);
}
static ngx_int_t
ngx_http_main_request_charset(ngx_http_request_t *r, ngx_str_t *src)
{
ngx_int_t charset;
ngx_str_t *main_charset;
ngx_http_charset_ctx_t *ctx;
ctx = ngx_http_get_module_ctx(r->main, ngx_http_charset_filter_module);
if (ctx) {
*src = ctx->charset_name;
return ctx->charset;
}
main_charset = &r->main->headers_out.charset;
if (main_charset->len == 0) {
return NGX_DECLINED;
}
ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_charset_ctx_t));
if (ctx == NULL) {
return NGX_ERROR;
}
ngx_http_set_ctx(r->main, ctx, ngx_http_charset_filter_module);
charset = ngx_http_get_charset(r, main_charset);
ctx->charset = charset;
ctx->charset_name = *main_charset;
*src = *main_charset;
return charset;
}
static ngx_int_t
ngx_http_source_charset(ngx_http_request_t *r, ngx_str_t *name)
{
ngx_int_t charset;
ngx_http_charset_t *charsets;
ngx_http_variable_value_t *vv;
ngx_http_charset_loc_conf_t *lcf;
ngx_http_charset_main_conf_t *mcf;
if (r->headers_out.charset.len) {
*name = r->headers_out.charset;
return ngx_http_get_charset(r, name);
}
lcf = ngx_http_get_module_loc_conf(r, ngx_http_charset_filter_module);
charset = lcf->source_charset;
if (charset == NGX_HTTP_CHARSET_OFF) {
name->len = 0;
return charset;
}
if (charset < NGX_HTTP_CHARSET_VAR) {
mcf = ngx_http_get_module_main_conf(r, ngx_http_charset_filter_module);
charsets = mcf->charsets.elts;
*name = charsets[charset].name;
return charset;
}
vv = ngx_http_get_indexed_variable(r, charset - NGX_HTTP_CHARSET_VAR);
if (vv == NULL || vv->not_found) {
return NGX_ERROR;
}
name->len = vv->len;
name->data = vv->data;
return ngx_http_get_charset(r, name);
}
static ngx_int_t
ngx_http_get_charset(ngx_http_request_t *r, ngx_str_t *name)
{
ngx_uint_t i, n;
ngx_http_charset_t *charset;
ngx_http_charset_main_conf_t *mcf;
mcf = ngx_http_get_module_main_conf(r, ngx_http_charset_filter_module);
charset = mcf->charsets.elts;
n = mcf->charsets.nelts;
for (i = 0; i < n; i++) {
if (charset[i].name.len != name->len) {
continue;
}
if (ngx_strncasecmp(charset[i].name.data, name->data, name->len) == 0) {
return i;
}
}
return NGX_HTTP_NO_CHARSET;
}
static ngx_inline void
ngx_http_set_charset(ngx_http_request_t *r, ngx_str_t *charset)
{
if (r != r->main) {
return;
}
if (r->headers_out.status == NGX_HTTP_MOVED_PERMANENTLY
|| r->headers_out.status == NGX_HTTP_MOVED_TEMPORARILY)
{
/*
* do not set charset for the redirect because NN 4.x
* use this charset instead of the next page charset
*/
r->headers_out.charset.len = 0;
return;
}
r->headers_out.charset = *charset;
}
static ngx_int_t
ngx_http_charset_ctx(ngx_http_request_t *r, ngx_http_charset_t *charsets,
ngx_int_t charset, ngx_int_t source_charset)
{
ngx_http_charset_ctx_t *ctx;
ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_charset_ctx_t));
if (ctx == NULL) {
return NGX_ERROR;
}
ngx_http_set_ctx(r, ctx, ngx_http_charset_filter_module);
ctx->table = charsets[source_charset].tables[charset];
ctx->charset = charset;
ctx->charset_name = charsets[charset].name;
ctx->length = charsets[charset].length;
ctx->from_utf8 = charsets[source_charset].utf8;
ctx->to_utf8 = charsets[charset].utf8;
r->filter_need_in_memory = 1;
if ((ctx->to_utf8 || ctx->from_utf8) && r == r->main) {
ngx_http_clear_content_length(r);
} else {
r->filter_need_temporary = 1;
}
return ngx_http_next_header_filter(r);
}
static ngx_int_t
ngx_http_charset_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
ngx_int_t rc;
ngx_buf_t *b;
ngx_chain_t *cl, *out, **ll;
ngx_http_charset_ctx_t *ctx;
ctx = ngx_http_get_module_ctx(r, ngx_http_charset_filter_module);
if (ctx == NULL || ctx->table == NULL) {
return ngx_http_next_body_filter(r, in);
}
if ((ctx->to_utf8 || ctx->from_utf8) || ctx->busy) {
out = NULL;
ll = &out;
for (cl = in; cl; cl = cl->next) {
b = cl->buf;
if (ngx_buf_size(b) == 0) {
*ll = ngx_alloc_chain_link(r->pool);
if (*ll == NULL) {
return NGX_ERROR;
}
(*ll)->buf = b;
(*ll)->next = NULL;
ll = &(*ll)->next;
continue;
}
if (ctx->to_utf8) {
*ll = ngx_http_charset_recode_to_utf8(r->pool, b, ctx);
} else {
*ll = ngx_http_charset_recode_from_utf8(r->pool, b, ctx);
}
if (*ll == NULL) {
return NGX_ERROR;
}
while (*ll) {
ll = &(*ll)->next;
}
}
rc = ngx_http_next_body_filter(r, out);
if (out) {
if (ctx->busy == NULL) {
ctx->busy = out;
} else {
for (cl = ctx->busy; cl->next; cl = cl->next) { /* void */ }
cl->next = out;
}
}
while (ctx->busy) {
cl = ctx->busy;
b = cl->buf;
if (ngx_buf_size(b) != 0) {
break;
}
ctx->busy = cl->next;
if (b->tag != (ngx_buf_tag_t) &ngx_http_charset_filter_module) {
continue;
}
if (b->shadow) {
b->shadow->pos = b->shadow->last;
}
if (b->pos) {
cl->next = ctx->free_buffers;
ctx->free_buffers = cl;
continue;
}
cl->next = ctx->free_bufs;
ctx->free_bufs = cl;
}
return rc;
}
for (cl = in; cl; cl = cl->next) {
(void) ngx_http_charset_recode(cl->buf, ctx->table);
}
return ngx_http_next_body_filter(r, in);
}
static ngx_uint_t
ngx_http_charset_recode(ngx_buf_t *b, u_char *table)
{
u_char *p, *last;
last = b->last;
for (p = b->pos; p < last; p++) {
if (*p != table[*p]) {
goto recode;
}
}
return 0;
recode:
do {
if (*p != table[*p]) {
*p = table[*p];
}
p++;
} while (p < last);
b->in_file = 0;
return 1;
}
static ngx_chain_t *
ngx_http_charset_recode_from_utf8(ngx_pool_t *pool, ngx_buf_t *buf,
ngx_http_charset_ctx_t *ctx)
{
size_t len, size;
u_char c, *p, *src, *dst, *saved, **table;
uint32_t n;
ngx_buf_t *b;
ngx_uint_t i;
ngx_chain_t *out, *cl, **ll;
src = buf->pos;
if (ctx->saved_len == 0) {
for ( /* void */ ; src < buf->last; src++) {
if (*src < 0x80) {
continue;
}
len = src - buf->pos;
if (len > 512) {
out = ngx_http_charset_get_buf(pool, ctx);
if (out == NULL) {
return NULL;
}
b = out->buf;
b->temporary = buf->temporary;
b->memory = buf->memory;
b->mmap = buf->mmap;
b->flush = buf->flush;
b->pos = buf->pos;
b->last = src;
out->buf = b;
out->next = NULL;
size = buf->last - src;
saved = src;
n = ngx_utf8_decode(&saved, size);
if (n == 0xfffffffe) {
/* incomplete UTF-8 symbol */
ngx_memcpy(ctx->saved, src, size);
ctx->saved_len = size;
b->shadow = buf;
return out;
}
} else {
out = NULL;
size = len + buf->last - src;
src = buf->pos;
}
if (size < NGX_HTML_ENTITY_LEN) {
size += NGX_HTML_ENTITY_LEN;
}
cl = ngx_http_charset_get_buffer(pool, ctx, size);
if (cl == NULL) {
return NULL;
}
if (out) {
out->next = cl;
} else {
out = cl;
}
b = cl->buf;
dst = b->pos;
goto recode;
}
out = ngx_alloc_chain_link(pool);
if (out == NULL) {
return NULL;
}
out->buf = buf;
out->next = NULL;
return out;
}
/* process incomplete UTF sequence from previous buffer */
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pool->log, 0,
"http charset utf saved: %z", ctx->saved_len);
p = src;
for (i = ctx->saved_len; i < NGX_UTF_LEN; i++) {
ctx->saved[i] = *p++;
if (p == buf->last) {
break;
}
}
saved = ctx->saved;
n = ngx_utf8_decode(&saved, i);
c = '\0';
if (n < 0x10000) {
table = (u_char **) ctx->table;
p = table[n >> 8];
if (p) {
c = p[n & 0xff];
}
} else if (n == 0xfffffffe) {
/* incomplete UTF-8 symbol */
if (i < NGX_UTF_LEN) {
out = ngx_http_charset_get_buf(pool, ctx);
if (out == NULL) {
return NULL;
}
b = out->buf;
b->pos = buf->pos;
b->last = buf->last;
b->sync = 1;
b->shadow = buf;
ngx_memcpy(&ctx->saved[ctx->saved_len], src, i);
ctx->saved_len += i;
return out;
}
}
size = buf->last - buf->pos;
if (size < NGX_HTML_ENTITY_LEN) {
size += NGX_HTML_ENTITY_LEN;
}
cl = ngx_http_charset_get_buffer(pool, ctx, size);
if (cl == NULL) {
return NULL;
}
out = cl;
b = cl->buf;
dst = b->pos;
if (c) {
*dst++ = c;
} else if (n == 0xfffffffe) {
*dst++ = '?';
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pool->log, 0,
"http charset invalid utf 0");
saved = &ctx->saved[NGX_UTF_LEN];
} else if (n > 0x10ffff) {
*dst++ = '?';
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pool->log, 0,
"http charset invalid utf 1");
} else {
dst = ngx_sprintf(dst, "%uD;", n);
}
src += (saved - ctx->saved) - ctx->saved_len;
ctx->saved_len = 0;
recode:
ll = &cl->next;
table = (u_char **) ctx->table;
while (src < buf->last) {
if ((size_t) (b->end - dst) < NGX_HTML_ENTITY_LEN) {
b->last = dst;
size = buf->last - src + NGX_HTML_ENTITY_LEN;
cl = ngx_http_charset_get_buffer(pool, ctx, size);
if (cl == NULL) {
return NULL;
}
*ll = cl;
ll = &cl->next;
b = cl->buf;
dst = b->pos;
}
if (*src < 0x80) {
*dst++ = *src++;
continue;
}
len = buf->last - src;
n = ngx_utf8_decode(&src, len);
if (n < 0x10000) {
p = table[n >> 8];
if (p) {
c = p[n & 0xff];
if (c) {
*dst++ = c;
continue;
}
}
dst = ngx_sprintf(dst, "%uD;", n);
continue;
}
if (n == 0xfffffffe) {
/* incomplete UTF-8 symbol */
ngx_memcpy(ctx->saved, src, len);
ctx->saved_len = len;
if (b->pos == dst) {
b->sync = 1;
b->temporary = 0;
}
break;
}
if (n > 0x10ffff) {
*dst++ = '?';
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pool->log, 0,
"http charset invalid utf 2");
continue;
}
/* n > 0xffff */
dst = ngx_sprintf(dst, "%uD;", n);
}
b->last = dst;
b->last_buf = buf->last_buf;
b->last_in_chain = buf->last_in_chain;
b->flush = buf->flush;
b->shadow = buf;
return out;
}
static ngx_chain_t *
ngx_http_charset_recode_to_utf8(ngx_pool_t *pool, ngx_buf_t *buf,
ngx_http_charset_ctx_t *ctx)
{
size_t len, size;
u_char *p, *src, *dst, *table;
ngx_buf_t *b;
ngx_chain_t *out, *cl, **ll;
table = ctx->table;
for (src = buf->pos; src < buf->last; src++) {
if (table[*src * NGX_UTF_LEN] == '\1') {
continue;
}
goto recode;
}
out = ngx_alloc_chain_link(pool);
if (out == NULL) {
return NULL;
}
out->buf = buf;
out->next = NULL;
return out;
recode:
/*
* we assume that there are about half of characters to be recoded,
* so we preallocate "size / 2 + size / 2 * ctx->length"
*/
len = src - buf->pos;
if (len > 512) {
out = ngx_http_charset_get_buf(pool, ctx);
if (out == NULL) {
return NULL;
}
b = out->buf;
b->temporary = buf->temporary;
b->memory = buf->memory;
b->mmap = buf->mmap;
b->flush = buf->flush;
b->pos = buf->pos;
b->last = src;
out->buf = b;
out->next = NULL;
size = buf->last - src;
size = size / 2 + size / 2 * ctx->length;
} else {
out = NULL;
size = buf->last - src;
size = len + size / 2 + size / 2 * ctx->length;
src = buf->pos;
}
cl = ngx_http_charset_get_buffer(pool, ctx, size);
if (cl == NULL) {
return NULL;
}
if (out) {
out->next = cl;
} else {
out = cl;
}
ll = &cl->next;
b = cl->buf;
dst = b->pos;
while (src < buf->last) {
p = &table[*src++ * NGX_UTF_LEN];
len = *p++;
if ((size_t) (b->end - dst) < len) {
b->last = dst;
size = buf->last - src;
size = len + size / 2 + size / 2 * ctx->length;
cl = ngx_http_charset_get_buffer(pool, ctx, size);
if (cl == NULL) {
return NULL;
}
*ll = cl;
ll = &cl->next;
b = cl->buf;
dst = b->pos;
}
while (len) {
*dst++ = *p++;
len--;
}
}
b->last = dst;
b->last_buf = buf->last_buf;
b->last_in_chain = buf->last_in_chain;
b->flush = buf->flush;
b->shadow = buf;
return out;
}
static ngx_chain_t *
ngx_http_charset_get_buf(ngx_pool_t *pool, ngx_http_charset_ctx_t *ctx)
{
ngx_chain_t *cl;
cl = ctx->free_bufs;
if (cl) {
ctx->free_bufs = cl->next;
cl->buf->shadow = NULL;
cl->next = NULL;
return cl;
}
cl = ngx_alloc_chain_link(pool);
if (cl == NULL) {
return NULL;
}
cl->buf = ngx_calloc_buf(pool);
if (cl->buf == NULL) {
return NULL;
}
cl->next = NULL;
cl->buf->tag = (ngx_buf_tag_t) &ngx_http_charset_filter_module;
return cl;
}
static ngx_chain_t *
ngx_http_charset_get_buffer(ngx_pool_t *pool, ngx_http_charset_ctx_t *ctx,
size_t size)
{
ngx_buf_t *b;
ngx_chain_t *cl, **ll;
for (ll = &ctx->free_buffers, cl = ctx->free_buffers;
cl;
ll = &cl->next, cl = cl->next)
{
b = cl->buf;
if ((size_t) (b->end - b->start) >= size) {
*ll = cl->next;
cl->next = NULL;
b->pos = b->start;
b->temporary = 1;
b->shadow = NULL;
return cl;
}
}
cl = ngx_alloc_chain_link(pool);
if (cl == NULL) {
return NULL;
}
cl->buf = ngx_create_temp_buf(pool, size);
if (cl->buf == NULL) {
return NULL;
}
cl->next = NULL;
cl->buf->temporary = 1;
cl->buf->tag = (ngx_buf_tag_t) &ngx_http_charset_filter_module;
return cl;
}
static char *
ngx_http_charset_map_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_charset_main_conf_t *mcf = conf;
char *rv;
u_char *p, *dst2src, **pp;
ngx_int_t src, dst;
ngx_uint_t i, n;
ngx_str_t *value;
ngx_conf_t pvcf;
ngx_http_charset_t *charset;
ngx_http_charset_tables_t *table;
ngx_http_charset_conf_ctx_t ctx;
value = cf->args->elts;
src = ngx_http_add_charset(&mcf->charsets, &value[1]);
if (src == NGX_ERROR) {
return NGX_CONF_ERROR;
}
dst = ngx_http_add_charset(&mcf->charsets, &value[2]);
if (dst == NGX_ERROR) {
return NGX_CONF_ERROR;
}
if (src == dst) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"\"charset_map\" between the same charsets "
"\"%V\" and \"%V\"", &value[1], &value[2]);
return NGX_CONF_ERROR;
}
table = mcf->tables.elts;
for (i = 0; i < mcf->tables.nelts; i++) {
if ((src == table->src && dst == table->dst)
|| (src == table->dst && dst == table->src))
{
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"duplicate \"charset_map\" between "
"\"%V\" and \"%V\"", &value[1], &value[2]);
return NGX_CONF_ERROR;
}
}
table = ngx_array_push(&mcf->tables);
if (table == NULL) {
return NGX_CONF_ERROR;
}
table->src = src;
table->dst = dst;
if (ngx_strcasecmp(value[2].data, (u_char *) "utf-8") == 0) {
table->src2dst = ngx_pcalloc(cf->pool, 256 * NGX_UTF_LEN);
if (table->src2dst == NULL) {
return NGX_CONF_ERROR;
}
table->dst2src = ngx_pcalloc(cf->pool, 256 * sizeof(void *));
if (table->dst2src == NULL) {
return NGX_CONF_ERROR;
}
dst2src = ngx_pcalloc(cf->pool, 256);
if (dst2src == NULL) {
return NGX_CONF_ERROR;
}
pp = (u_char **) &table->dst2src[0];
pp[0] = dst2src;
for (i = 0; i < 128; i++) {
p = &table->src2dst[i * NGX_UTF_LEN];
p[0] = '\1';
p[1] = (u_char) i;
dst2src[i] = (u_char) i;
}
for (/* void */; i < 256; i++) {
p = &table->src2dst[i * NGX_UTF_LEN];
p[0] = '\1';
p[1] = '?';
}
} else {
table->src2dst = ngx_palloc(cf->pool, 256);
if (table->src2dst == NULL) {
return NGX_CONF_ERROR;
}
table->dst2src = ngx_palloc(cf->pool, 256);
if (table->dst2src == NULL) {
return NGX_CONF_ERROR;
}
for (i = 0; i < 128; i++) {
table->src2dst[i] = (u_char) i;
table->dst2src[i] = (u_char) i;
}
for (/* void */; i < 256; i++) {
table->src2dst[i] = '?';
table->dst2src[i] = '?';
}
}
charset = mcf->charsets.elts;
ctx.table = table;
ctx.charset = &charset[dst];
ctx.characters = 0;
pvcf = *cf;
cf->ctx = &ctx;
cf->handler = ngx_http_charset_map;
cf->handler_conf = conf;
rv = ngx_conf_parse(cf, NULL);
*cf = pvcf;
if (ctx.characters) {
n = ctx.charset->length;
ctx.charset->length /= ctx.characters;
if (((n * 10) / ctx.characters) % 10 > 4) {
ctx.charset->length++;
}
}
return rv;
}
static char *
ngx_http_charset_map(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
{
u_char *p, *dst2src, **pp;
uint32_t n;
ngx_int_t src, dst;
ngx_str_t *value;
ngx_uint_t i;
ngx_http_charset_tables_t *table;
ngx_http_charset_conf_ctx_t *ctx;
if (cf->args->nelts != 2) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameters number");
return NGX_CONF_ERROR;
}
value = cf->args->elts;
src = ngx_hextoi(value[0].data, value[0].len);
if (src == NGX_ERROR || src > 255) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid value \"%V\"", &value[0]);
return NGX_CONF_ERROR;
}
ctx = cf->ctx;
table = ctx->table;
if (ctx->charset->utf8) {
p = &table->src2dst[src * NGX_UTF_LEN];
*p++ = (u_char) (value[1].len / 2);
for (i = 0; i < value[1].len; i += 2) {
dst = ngx_hextoi(&value[1].data[i], 2);
if (dst == NGX_ERROR || dst > 255) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid value \"%V\"", &value[1]);
return NGX_CONF_ERROR;
}
*p++ = (u_char) dst;
}
i /= 2;
ctx->charset->length += i;
ctx->characters++;
p = &table->src2dst[src * NGX_UTF_LEN] + 1;
n = ngx_utf8_decode(&p, i);
if (n > 0xffff) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid value \"%V\"", &value[1]);
return NGX_CONF_ERROR;
}
pp = (u_char **) &table->dst2src[0];
dst2src = pp[n >> 8];
if (dst2src == NULL) {
dst2src = ngx_pcalloc(cf->pool, 256);
if (dst2src == NULL) {
return NGX_CONF_ERROR;
}
pp[n >> 8] = dst2src;
}
dst2src[n & 0xff] = (u_char) src;
} else {
dst = ngx_hextoi(value[1].data, value[1].len);
if (dst == NGX_ERROR || dst > 255) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid value \"%V\"", &value[1]);
return NGX_CONF_ERROR;
}
table->src2dst[src] = (u_char) dst;
table->dst2src[dst] = (u_char) src;
}
return NGX_CONF_OK;
}
static char *
ngx_http_set_charset_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
char *p = conf;
ngx_int_t *cp;
ngx_str_t *value, var;
ngx_http_charset_main_conf_t *mcf;
cp = (ngx_int_t *) (p + cmd->offset);
if (*cp != NGX_CONF_UNSET) {
return "is duplicate";
}
value = cf->args->elts;
if (cmd->offset == offsetof(ngx_http_charset_loc_conf_t, charset)
&& ngx_strcmp(value[1].data, "off") == 0)
{
*cp = NGX_HTTP_CHARSET_OFF;
return NGX_CONF_OK;
}
if (value[1].data[0] == '$') {
var.len = value[1].len - 1;
var.data = value[1].data + 1;
*cp = ngx_http_get_variable_index(cf, &var);
if (*cp == NGX_ERROR) {
return NGX_CONF_ERROR;
}
*cp += NGX_HTTP_CHARSET_VAR;
return NGX_CONF_OK;
}
mcf = ngx_http_conf_get_module_main_conf(cf,
ngx_http_charset_filter_module);
*cp = ngx_http_add_charset(&mcf->charsets, &value[1]);
if (*cp == NGX_ERROR) {
return NGX_CONF_ERROR;
}
return NGX_CONF_OK;
}
static ngx_int_t
ngx_http_add_charset(ngx_array_t *charsets, ngx_str_t *name)
{
ngx_uint_t i;
ngx_http_charset_t *c;
c = charsets->elts;
for (i = 0; i < charsets->nelts; i++) {
if (name->len != c[i].name.len) {
continue;
}
if (ngx_strcasecmp(name->data, c[i].name.data) == 0) {
break;
}
}
if (i < charsets->nelts) {
return i;
}
c = ngx_array_push(charsets);
if (c == NULL) {
return NGX_ERROR;
}
c->tables = NULL;
c->name = *name;
c->length = 0;
if (ngx_strcasecmp(name->data, (u_char *) "utf-8") == 0) {
c->utf8 = 1;
} else {
c->utf8 = 0;
}
return i;
}
static void *
ngx_http_charset_create_main_conf(ngx_conf_t *cf)
{
ngx_http_charset_main_conf_t *mcf;
mcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_charset_main_conf_t));
if (mcf == NULL) {
return NULL;
}
if (ngx_array_init(&mcf->charsets, cf->pool, 2, sizeof(ngx_http_charset_t))
!= NGX_OK)
{
return NULL;
}
if (ngx_array_init(&mcf->tables, cf->pool, 1,
sizeof(ngx_http_charset_tables_t))
!= NGX_OK)
{
return NULL;
}
if (ngx_array_init(&mcf->recodes, cf->pool, 2,
sizeof(ngx_http_charset_recode_t))
!= NGX_OK)
{
return NULL;
}
return mcf;
}
static void *
ngx_http_charset_create_loc_conf(ngx_conf_t *cf)
{
ngx_http_charset_loc_conf_t *lcf;
lcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_charset_loc_conf_t));
if (lcf == NULL) {
return NULL;
}
/*
* set by ngx_pcalloc():
*
* lcf->types = { NULL };
* lcf->types_keys = NULL;
*/
lcf->charset = NGX_CONF_UNSET;
lcf->source_charset = NGX_CONF_UNSET;
lcf->override_charset = NGX_CONF_UNSET;
return lcf;
}
static char *
ngx_http_charset_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
{
ngx_http_charset_loc_conf_t *prev = parent;
ngx_http_charset_loc_conf_t *conf = child;
ngx_uint_t i;
ngx_http_charset_recode_t *recode;
ngx_http_charset_main_conf_t *mcf;
if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types,
&prev->types_keys, &prev->types,
ngx_http_charset_default_types)
!= NGX_OK)
{
return NGX_CONF_ERROR;
}
ngx_conf_merge_value(conf->override_charset, prev->override_charset, 0);
ngx_conf_merge_value(conf->charset, prev->charset, NGX_HTTP_CHARSET_OFF);
ngx_conf_merge_value(conf->source_charset, prev->source_charset,
NGX_HTTP_CHARSET_OFF);
if (conf->charset == NGX_HTTP_CHARSET_OFF
|| conf->source_charset == NGX_HTTP_CHARSET_OFF
|| conf->charset == conf->source_charset)
{
return NGX_CONF_OK;
}
if (conf->source_charset >= NGX_HTTP_CHARSET_VAR
|| conf->charset >= NGX_HTTP_CHARSET_VAR)
{
return NGX_CONF_OK;
}
mcf = ngx_http_conf_get_module_main_conf(cf,
ngx_http_charset_filter_module);
recode = mcf->recodes.elts;
for (i = 0; i < mcf->recodes.nelts; i++) {
if (conf->source_charset == recode[i].src
&& conf->charset == recode[i].dst)
{
return NGX_CONF_OK;
}
}
recode = ngx_array_push(&mcf->recodes);
if (recode == NULL) {
return NGX_CONF_ERROR;
}
recode->src = conf->source_charset;
recode->dst = conf->charset;
return NGX_CONF_OK;
}
static ngx_int_t
ngx_http_charset_postconfiguration(ngx_conf_t *cf)
{
u_char **src, **dst;
ngx_int_t c;
ngx_uint_t i, t;
ngx_http_charset_t *charset;
ngx_http_charset_recode_t *recode;
ngx_http_charset_tables_t *tables;
ngx_http_charset_main_conf_t *mcf;
mcf = ngx_http_conf_get_module_main_conf(cf,
ngx_http_charset_filter_module);
recode = mcf->recodes.elts;
tables = mcf->tables.elts;
charset = mcf->charsets.elts;
for (i = 0; i < mcf->recodes.nelts; i++) {
c = recode[i].src;
for (t = 0; t < mcf->tables.nelts; t++) {
if (c == tables[t].src && recode[i].dst == tables[t].dst) {
goto next;
}
if (c == tables[t].dst && recode[i].dst == tables[t].src) {
goto next;
}
}
ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
"no \"charset_map\" between the charsets \"%V\" and \"%V\"",
&charset[c].name, &charset[recode[i].dst].name);
return NGX_ERROR;
next:
continue;
}
for (t = 0; t < mcf->tables.nelts; t++) {
src = charset[tables[t].src].tables;
if (src == NULL) {
src = ngx_pcalloc(cf->pool, sizeof(u_char *) * mcf->charsets.nelts);
if (src == NULL) {
return NGX_ERROR;
}
charset[tables[t].src].tables = src;
}
dst = charset[tables[t].dst].tables;
if (dst == NULL) {
dst = ngx_pcalloc(cf->pool, sizeof(u_char *) * mcf->charsets.nelts);
if (dst == NULL) {
return NGX_ERROR;
}
charset[tables[t].dst].tables = dst;
}
src[tables[t].dst] = tables[t].src2dst;
dst[tables[t].src] = tables[t].dst2src;
}
ngx_http_next_header_filter = ngx_http_top_header_filter;
ngx_http_top_header_filter = ngx_http_charset_header_filter;
ngx_http_next_body_filter = ngx_http_top_body_filter;
ngx_http_top_body_filter = ngx_http_charset_body_filter;
return NGX_OK;
}
nginx-1.14.0/src/http/modules/ngx_http_auth_basic_module.c 000644 001751 001751 00000026572 13265410474 025074 0 ustar 00mdounin mdounin 000000 000000
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#include
#include
#include
#include
#define NGX_HTTP_AUTH_BUF_SIZE 2048
typedef struct {
ngx_http_complex_value_t *realm;
ngx_http_complex_value_t user_file;
} ngx_http_auth_basic_loc_conf_t;
static ngx_int_t ngx_http_auth_basic_handler(ngx_http_request_t *r);
static ngx_int_t ngx_http_auth_basic_crypt_handler(ngx_http_request_t *r,
ngx_str_t *passwd, ngx_str_t *realm);
static ngx_int_t ngx_http_auth_basic_set_realm(ngx_http_request_t *r,
ngx_str_t *realm);
static void ngx_http_auth_basic_close(ngx_file_t *file);
static void *ngx_http_auth_basic_create_loc_conf(ngx_conf_t *cf);
static char *ngx_http_auth_basic_merge_loc_conf(ngx_conf_t *cf,
void *parent, void *child);
static ngx_int_t ngx_http_auth_basic_init(ngx_conf_t *cf);
static char *ngx_http_auth_basic_user_file(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static ngx_command_t ngx_http_auth_basic_commands[] = {
{ ngx_string("auth_basic"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF
|NGX_CONF_TAKE1,
ngx_http_set_complex_value_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_auth_basic_loc_conf_t, realm),
NULL },
{ ngx_string("auth_basic_user_file"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF
|NGX_CONF_TAKE1,
ngx_http_auth_basic_user_file,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_auth_basic_loc_conf_t, user_file),
NULL },
ngx_null_command
};
static ngx_http_module_t ngx_http_auth_basic_module_ctx = {
NULL, /* preconfiguration */
ngx_http_auth_basic_init, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
ngx_http_auth_basic_create_loc_conf, /* create location configuration */
ngx_http_auth_basic_merge_loc_conf /* merge location configuration */
};
ngx_module_t ngx_http_auth_basic_module = {
NGX_MODULE_V1,
&ngx_http_auth_basic_module_ctx, /* module context */
ngx_http_auth_basic_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static ngx_int_t
ngx_http_auth_basic_handler(ngx_http_request_t *r)
{
off_t offset;
ssize_t n;
ngx_fd_t fd;
ngx_int_t rc;
ngx_err_t err;
ngx_str_t pwd, realm, user_file;
ngx_uint_t i, level, login, left, passwd;
ngx_file_t file;
ngx_http_auth_basic_loc_conf_t *alcf;
u_char buf[NGX_HTTP_AUTH_BUF_SIZE];
enum {
sw_login,
sw_passwd,
sw_skip
} state;
alcf = ngx_http_get_module_loc_conf(r, ngx_http_auth_basic_module);
if (alcf->realm == NULL || alcf->user_file.value.data == NULL) {
return NGX_DECLINED;
}
if (ngx_http_complex_value(r, alcf->realm, &realm) != NGX_OK) {
return NGX_ERROR;
}
if (realm.len == 3 && ngx_strncmp(realm.data, "off", 3) == 0) {
return NGX_DECLINED;
}
rc = ngx_http_auth_basic_user(r);
if (rc == NGX_DECLINED) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"no user/password was provided for basic authentication");
return ngx_http_auth_basic_set_realm(r, &realm);
}
if (rc == NGX_ERROR) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
if (ngx_http_complex_value(r, &alcf->user_file, &user_file) != NGX_OK) {
return NGX_ERROR;
}
fd = ngx_open_file(user_file.data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);
if (fd == NGX_INVALID_FILE) {
err = ngx_errno;
if (err == NGX_ENOENT) {
level = NGX_LOG_ERR;
rc = NGX_HTTP_FORBIDDEN;
} else {
level = NGX_LOG_CRIT;
rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
}
ngx_log_error(level, r->connection->log, err,
ngx_open_file_n " \"%s\" failed", user_file.data);
return rc;
}
ngx_memzero(&file, sizeof(ngx_file_t));
file.fd = fd;
file.name = user_file;
file.log = r->connection->log;
state = sw_login;
passwd = 0;
login = 0;
left = 0;
offset = 0;
for ( ;; ) {
i = left;
n = ngx_read_file(&file, buf + left, NGX_HTTP_AUTH_BUF_SIZE - left,
offset);
if (n == NGX_ERROR) {
ngx_http_auth_basic_close(&file);
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
if (n == 0) {
break;
}
for (i = left; i < left + n; i++) {
switch (state) {
case sw_login:
if (login == 0) {
if (buf[i] == '#' || buf[i] == CR) {
state = sw_skip;
break;
}
if (buf[i] == LF) {
break;
}
}
if (buf[i] != r->headers_in.user.data[login]) {
state = sw_skip;
break;
}
if (login == r->headers_in.user.len) {
state = sw_passwd;
passwd = i + 1;
}
login++;
break;
case sw_passwd:
if (buf[i] == LF || buf[i] == CR || buf[i] == ':') {
buf[i] = '\0';
ngx_http_auth_basic_close(&file);
pwd.len = i - passwd;
pwd.data = &buf[passwd];
return ngx_http_auth_basic_crypt_handler(r, &pwd, &realm);
}
break;
case sw_skip:
if (buf[i] == LF) {
state = sw_login;
login = 0;
}
break;
}
}
if (state == sw_passwd) {
left = left + n - passwd;
ngx_memmove(buf, &buf[passwd], left);
passwd = 0;
} else {
left = 0;
}
offset += n;
}
ngx_http_auth_basic_close(&file);
if (state == sw_passwd) {
pwd.len = i - passwd;
pwd.data = ngx_pnalloc(r->pool, pwd.len + 1);
if (pwd.data == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
ngx_cpystrn(pwd.data, &buf[passwd], pwd.len + 1);
return ngx_http_auth_basic_crypt_handler(r, &pwd, &realm);
}
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"user \"%V\" was not found in \"%s\"",
&r->headers_in.user, user_file.data);
return ngx_http_auth_basic_set_realm(r, &realm);
}
static ngx_int_t
ngx_http_auth_basic_crypt_handler(ngx_http_request_t *r, ngx_str_t *passwd,
ngx_str_t *realm)
{
ngx_int_t rc;
u_char *encrypted;
rc = ngx_crypt(r->pool, r->headers_in.passwd.data, passwd->data,
&encrypted);
ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"rc: %i user: \"%V\" salt: \"%s\"",
rc, &r->headers_in.user, passwd->data);
if (rc != NGX_OK) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
if (ngx_strcmp(encrypted, passwd->data) == 0) {
return NGX_OK;
}
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"encrypted: \"%s\"", encrypted);
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"user \"%V\": password mismatch",
&r->headers_in.user);
return ngx_http_auth_basic_set_realm(r, realm);
}
static ngx_int_t
ngx_http_auth_basic_set_realm(ngx_http_request_t *r, ngx_str_t *realm)
{
size_t len;
u_char *basic, *p;
r->headers_out.www_authenticate = ngx_list_push(&r->headers_out.headers);
if (r->headers_out.www_authenticate == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
len = sizeof("Basic realm=\"\"") - 1 + realm->len;
basic = ngx_pnalloc(r->pool, len);
if (basic == NULL) {
r->headers_out.www_authenticate->hash = 0;
r->headers_out.www_authenticate = NULL;
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
p = ngx_cpymem(basic, "Basic realm=\"", sizeof("Basic realm=\"") - 1);
p = ngx_cpymem(p, realm->data, realm->len);
*p = '"';
r->headers_out.www_authenticate->hash = 1;
ngx_str_set(&r->headers_out.www_authenticate->key, "WWW-Authenticate");
r->headers_out.www_authenticate->value.data = basic;
r->headers_out.www_authenticate->value.len = len;
return NGX_HTTP_UNAUTHORIZED;
}
static void
ngx_http_auth_basic_close(ngx_file_t *file)
{
if (ngx_close_file(file->fd) == NGX_FILE_ERROR) {
ngx_log_error(NGX_LOG_ALERT, file->log, ngx_errno,
ngx_close_file_n " \"%s\" failed", file->name.data);
}
}
static void *
ngx_http_auth_basic_create_loc_conf(ngx_conf_t *cf)
{
ngx_http_auth_basic_loc_conf_t *conf;
conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_auth_basic_loc_conf_t));
if (conf == NULL) {
return NULL;
}
return conf;
}
static char *
ngx_http_auth_basic_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
{
ngx_http_auth_basic_loc_conf_t *prev = parent;
ngx_http_auth_basic_loc_conf_t *conf = child;
if (conf->realm == NULL) {
conf->realm = prev->realm;
}
if (conf->user_file.value.data == NULL) {
conf->user_file = prev->user_file;
}
return NGX_CONF_OK;
}
static ngx_int_t
ngx_http_auth_basic_init(ngx_conf_t *cf)
{
ngx_http_handler_pt *h;
ngx_http_core_main_conf_t *cmcf;
cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
h = ngx_array_push(&cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers);
if (h == NULL) {
return NGX_ERROR;
}
*h = ngx_http_auth_basic_handler;
return NGX_OK;
}
static char *
ngx_http_auth_basic_user_file(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_auth_basic_loc_conf_t *alcf = conf;
ngx_str_t *value;
ngx_http_compile_complex_value_t ccv;
if (alcf->user_file.value.data) {
return "is duplicate";
}
value = cf->args->elts;
ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
ccv.cf = cf;
ccv.value = &value[1];
ccv.complex_value = &alcf->user_file;
ccv.zero = 1;
ccv.conf_prefix = 1;
if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
return NGX_CONF_ERROR;
}
return NGX_CONF_OK;
}
nginx-1.14.0/src/http/modules/ngx_http_auth_request_module.c 000644 001751 001751 00000026405 13265410474 025476 0 ustar 00mdounin mdounin 000000 000000
/*
* Copyright (C) Maxim Dounin
* Copyright (C) Nginx, Inc.
*/
#include
#include
#include
typedef struct {
ngx_str_t uri;
ngx_array_t *vars;
} ngx_http_auth_request_conf_t;
typedef struct {
ngx_uint_t done;
ngx_uint_t status;
ngx_http_request_t *subrequest;
} ngx_http_auth_request_ctx_t;
typedef struct {
ngx_int_t index;
ngx_http_complex_value_t value;
ngx_http_set_variable_pt set_handler;
} ngx_http_auth_request_variable_t;
static ngx_int_t ngx_http_auth_request_handler(ngx_http_request_t *r);
static ngx_int_t ngx_http_auth_request_done(ngx_http_request_t *r,
void *data, ngx_int_t rc);
static ngx_int_t ngx_http_auth_request_set_variables(ngx_http_request_t *r,
ngx_http_auth_request_conf_t *arcf, ngx_http_auth_request_ctx_t *ctx);
static ngx_int_t ngx_http_auth_request_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static void *ngx_http_auth_request_create_conf(ngx_conf_t *cf);
static char *ngx_http_auth_request_merge_conf(ngx_conf_t *cf,
void *parent, void *child);
static ngx_int_t ngx_http_auth_request_init(ngx_conf_t *cf);
static char *ngx_http_auth_request(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static char *ngx_http_auth_request_set(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static ngx_command_t ngx_http_auth_request_commands[] = {
{ ngx_string("auth_request"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_http_auth_request,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
{ ngx_string("auth_request_set"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
ngx_http_auth_request_set,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
ngx_null_command
};
static ngx_http_module_t ngx_http_auth_request_module_ctx = {
NULL, /* preconfiguration */
ngx_http_auth_request_init, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
ngx_http_auth_request_create_conf, /* create location configuration */
ngx_http_auth_request_merge_conf /* merge location configuration */
};
ngx_module_t ngx_http_auth_request_module = {
NGX_MODULE_V1,
&ngx_http_auth_request_module_ctx, /* module context */
ngx_http_auth_request_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static ngx_int_t
ngx_http_auth_request_handler(ngx_http_request_t *r)
{
ngx_table_elt_t *h, *ho;
ngx_http_request_t *sr;
ngx_http_post_subrequest_t *ps;
ngx_http_auth_request_ctx_t *ctx;
ngx_http_auth_request_conf_t *arcf;
arcf = ngx_http_get_module_loc_conf(r, ngx_http_auth_request_module);
if (arcf->uri.len == 0) {
return NGX_DECLINED;
}
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"auth request handler");
ctx = ngx_http_get_module_ctx(r, ngx_http_auth_request_module);
if (ctx != NULL) {
if (!ctx->done) {
return NGX_AGAIN;
}
/*
* as soon as we are done - explicitly set variables to make
* sure they will be available after internal redirects
*/
if (ngx_http_auth_request_set_variables(r, arcf, ctx) != NGX_OK) {
return NGX_ERROR;
}
/* return appropriate status */
if (ctx->status == NGX_HTTP_FORBIDDEN) {
return ctx->status;
}
if (ctx->status == NGX_HTTP_UNAUTHORIZED) {
sr = ctx->subrequest;
h = sr->headers_out.www_authenticate;
if (!h && sr->upstream) {
h = sr->upstream->headers_in.www_authenticate;
}
if (h) {
ho = ngx_list_push(&r->headers_out.headers);
if (ho == NULL) {
return NGX_ERROR;
}
*ho = *h;
r->headers_out.www_authenticate = ho;
}
return ctx->status;
}
if (ctx->status >= NGX_HTTP_OK
&& ctx->status < NGX_HTTP_SPECIAL_RESPONSE)
{
return NGX_OK;
}
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"auth request unexpected status: %ui", ctx->status);
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_auth_request_ctx_t));
if (ctx == NULL) {
return NGX_ERROR;
}
ps = ngx_palloc(r->pool, sizeof(ngx_http_post_subrequest_t));
if (ps == NULL) {
return NGX_ERROR;
}
ps->handler = ngx_http_auth_request_done;
ps->data = ctx;
if (ngx_http_subrequest(r, &arcf->uri, NULL, &sr, ps,
NGX_HTTP_SUBREQUEST_WAITED)
!= NGX_OK)
{
return NGX_ERROR;
}
/*
* allocate fake request body to avoid attempts to read it and to make
* sure real body file (if already read) won't be closed by upstream
*/
sr->request_body = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t));
if (sr->request_body == NULL) {
return NGX_ERROR;
}
sr->header_only = 1;
ctx->subrequest = sr;
ngx_http_set_ctx(r, ctx, ngx_http_auth_request_module);
return NGX_AGAIN;
}
static ngx_int_t
ngx_http_auth_request_done(ngx_http_request_t *r, void *data, ngx_int_t rc)
{
ngx_http_auth_request_ctx_t *ctx = data;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"auth request done s:%ui", r->headers_out.status);
ctx->done = 1;
ctx->status = r->headers_out.status;
return rc;
}
static ngx_int_t
ngx_http_auth_request_set_variables(ngx_http_request_t *r,
ngx_http_auth_request_conf_t *arcf, ngx_http_auth_request_ctx_t *ctx)
{
ngx_str_t val;
ngx_http_variable_t *v;
ngx_http_variable_value_t *vv;
ngx_http_auth_request_variable_t *av, *last;
ngx_http_core_main_conf_t *cmcf;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"auth request set variables");
if (arcf->vars == NULL) {
return NGX_OK;
}
cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
v = cmcf->variables.elts;
av = arcf->vars->elts;
last = av + arcf->vars->nelts;
while (av < last) {
/*
* explicitly set new value to make sure it will be available after
* internal redirects
*/
vv = &r->variables[av->index];
if (ngx_http_complex_value(ctx->subrequest, &av->value, &val)
!= NGX_OK)
{
return NGX_ERROR;
}
vv->valid = 1;
vv->not_found = 0;
vv->data = val.data;
vv->len = val.len;
if (av->set_handler) {
/*
* set_handler only available in cmcf->variables_keys, so we store
* it explicitly
*/
av->set_handler(r, vv, v[av->index].data);
}
av++;
}
return NGX_OK;
}
static ngx_int_t
ngx_http_auth_request_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"auth request variable");
v->not_found = 1;
return NGX_OK;
}
static void *
ngx_http_auth_request_create_conf(ngx_conf_t *cf)
{
ngx_http_auth_request_conf_t *conf;
conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_auth_request_conf_t));
if (conf == NULL) {
return NULL;
}
/*
* set by ngx_pcalloc():
*
* conf->uri = { 0, NULL };
*/
conf->vars = NGX_CONF_UNSET_PTR;
return conf;
}
static char *
ngx_http_auth_request_merge_conf(ngx_conf_t *cf, void *parent, void *child)
{
ngx_http_auth_request_conf_t *prev = parent;
ngx_http_auth_request_conf_t *conf = child;
ngx_conf_merge_str_value(conf->uri, prev->uri, "");
ngx_conf_merge_ptr_value(conf->vars, prev->vars, NULL);
return NGX_CONF_OK;
}
static ngx_int_t
ngx_http_auth_request_init(ngx_conf_t *cf)
{
ngx_http_handler_pt *h;
ngx_http_core_main_conf_t *cmcf;
cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
h = ngx_array_push(&cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers);
if (h == NULL) {
return NGX_ERROR;
}
*h = ngx_http_auth_request_handler;
return NGX_OK;
}
static char *
ngx_http_auth_request(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_auth_request_conf_t *arcf = conf;
ngx_str_t *value;
if (arcf->uri.data != NULL) {
return "is duplicate";
}
value = cf->args->elts;
if (ngx_strcmp(value[1].data, "off") == 0) {
arcf->uri.len = 0;
arcf->uri.data = (u_char *) "";
return NGX_CONF_OK;
}
arcf->uri = value[1];
return NGX_CONF_OK;
}
static char *
ngx_http_auth_request_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_auth_request_conf_t *arcf = conf;
ngx_str_t *value;
ngx_http_variable_t *v;
ngx_http_auth_request_variable_t *av;
ngx_http_compile_complex_value_t ccv;
value = cf->args->elts;
if (value[1].data[0] != '$') {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid variable name \"%V\"", &value[1]);
return NGX_CONF_ERROR;
}
value[1].len--;
value[1].data++;
if (arcf->vars == NGX_CONF_UNSET_PTR) {
arcf->vars = ngx_array_create(cf->pool, 1,
sizeof(ngx_http_auth_request_variable_t));
if (arcf->vars == NULL) {
return NGX_CONF_ERROR;
}
}
av = ngx_array_push(arcf->vars);
if (av == NULL) {
return NGX_CONF_ERROR;
}
v = ngx_http_add_variable(cf, &value[1], NGX_HTTP_VAR_CHANGEABLE);
if (v == NULL) {
return NGX_CONF_ERROR;
}
av->index = ngx_http_get_variable_index(cf, &value[1]);
if (av->index == NGX_ERROR) {
return NGX_CONF_ERROR;
}
if (v->get_handler == NULL) {
v->get_handler = ngx_http_auth_request_variable;
v->data = (uintptr_t) av;
}
av->set_handler = v->set_handler;
ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
ccv.cf = cf;
ccv.value = &value[2];
ccv.complex_value = &av->value;
if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
return NGX_CONF_ERROR;
}
return NGX_CONF_OK;
}
nginx-1.14.0/src/http/modules/ngx_http_autoindex_module.c 000644 001751 001751 00000073765 13265410474 025000 0 ustar 00mdounin mdounin 000000 000000
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#include
#include
#include
#if 0
typedef struct {
ngx_buf_t *buf;
size_t size;
ngx_pool_t *pool;
size_t alloc_size;
ngx_chain_t **last_out;
} ngx_http_autoindex_ctx_t;
#endif
typedef struct {
ngx_str_t name;
size_t utf_len;
size_t escape;
size_t escape_html;
unsigned dir:1;
unsigned file:1;
time_t mtime;
off_t size;
} ngx_http_autoindex_entry_t;
typedef struct {
ngx_flag_t enable;
ngx_uint_t format;
ngx_flag_t localtime;
ngx_flag_t exact_size;
} ngx_http_autoindex_loc_conf_t;
#define NGX_HTTP_AUTOINDEX_HTML 0
#define NGX_HTTP_AUTOINDEX_JSON 1
#define NGX_HTTP_AUTOINDEX_JSONP 2
#define NGX_HTTP_AUTOINDEX_XML 3
#define NGX_HTTP_AUTOINDEX_PREALLOCATE 50
#define NGX_HTTP_AUTOINDEX_NAME_LEN 50
static ngx_buf_t *ngx_http_autoindex_html(ngx_http_request_t *r,
ngx_array_t *entries);
static ngx_buf_t *ngx_http_autoindex_json(ngx_http_request_t *r,
ngx_array_t *entries, ngx_str_t *callback);
static ngx_int_t ngx_http_autoindex_jsonp_callback(ngx_http_request_t *r,
ngx_str_t *callback);
static ngx_buf_t *ngx_http_autoindex_xml(ngx_http_request_t *r,
ngx_array_t *entries);
static int ngx_libc_cdecl ngx_http_autoindex_cmp_entries(const void *one,
const void *two);
static ngx_int_t ngx_http_autoindex_error(ngx_http_request_t *r,
ngx_dir_t *dir, ngx_str_t *name);
static ngx_int_t ngx_http_autoindex_init(ngx_conf_t *cf);
static void *ngx_http_autoindex_create_loc_conf(ngx_conf_t *cf);
static char *ngx_http_autoindex_merge_loc_conf(ngx_conf_t *cf,
void *parent, void *child);
static ngx_conf_enum_t ngx_http_autoindex_format[] = {
{ ngx_string("html"), NGX_HTTP_AUTOINDEX_HTML },
{ ngx_string("json"), NGX_HTTP_AUTOINDEX_JSON },
{ ngx_string("jsonp"), NGX_HTTP_AUTOINDEX_JSONP },
{ ngx_string("xml"), NGX_HTTP_AUTOINDEX_XML },
{ ngx_null_string, 0 }
};
static ngx_command_t ngx_http_autoindex_commands[] = {
{ ngx_string("autoindex"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_autoindex_loc_conf_t, enable),
NULL },
{ ngx_string("autoindex_format"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_enum_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_autoindex_loc_conf_t, format),
&ngx_http_autoindex_format },
{ ngx_string("autoindex_localtime"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_autoindex_loc_conf_t, localtime),
NULL },
{ ngx_string("autoindex_exact_size"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_autoindex_loc_conf_t, exact_size),
NULL },
ngx_null_command
};
static ngx_http_module_t ngx_http_autoindex_module_ctx = {
NULL, /* preconfiguration */
ngx_http_autoindex_init, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
ngx_http_autoindex_create_loc_conf, /* create location configuration */
ngx_http_autoindex_merge_loc_conf /* merge location configuration */
};
ngx_module_t ngx_http_autoindex_module = {
NGX_MODULE_V1,
&ngx_http_autoindex_module_ctx, /* module context */
ngx_http_autoindex_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static ngx_int_t
ngx_http_autoindex_handler(ngx_http_request_t *r)
{
u_char *last, *filename;
size_t len, allocated, root;
ngx_err_t err;
ngx_buf_t *b;
ngx_int_t rc;
ngx_str_t path, callback;
ngx_dir_t dir;
ngx_uint_t level, format;
ngx_pool_t *pool;
ngx_chain_t out;
ngx_array_t entries;
ngx_http_autoindex_entry_t *entry;
ngx_http_autoindex_loc_conf_t *alcf;
if (r->uri.data[r->uri.len - 1] != '/') {
return NGX_DECLINED;
}
if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) {
return NGX_DECLINED;
}
alcf = ngx_http_get_module_loc_conf(r, ngx_http_autoindex_module);
if (!alcf->enable) {
return NGX_DECLINED;
}
rc = ngx_http_discard_request_body(r);
if (rc != NGX_OK) {
return rc;
}
/* NGX_DIR_MASK_LEN is lesser than NGX_HTTP_AUTOINDEX_PREALLOCATE */
last = ngx_http_map_uri_to_path(r, &path, &root,
NGX_HTTP_AUTOINDEX_PREALLOCATE);
if (last == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
allocated = path.len;
path.len = last - path.data;
if (path.len > 1) {
path.len--;
}
path.data[path.len] = '\0';
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http autoindex: \"%s\"", path.data);
format = alcf->format;
if (format == NGX_HTTP_AUTOINDEX_JSONP) {
if (ngx_http_autoindex_jsonp_callback(r, &callback) != NGX_OK) {
return NGX_HTTP_BAD_REQUEST;
}
if (callback.len == 0) {
format = NGX_HTTP_AUTOINDEX_JSON;
}
}
if (ngx_open_dir(&path, &dir) == NGX_ERROR) {
err = ngx_errno;
if (err == NGX_ENOENT
|| err == NGX_ENOTDIR
|| err == NGX_ENAMETOOLONG)
{
level = NGX_LOG_ERR;
rc = NGX_HTTP_NOT_FOUND;
} else if (err == NGX_EACCES) {
level = NGX_LOG_ERR;
rc = NGX_HTTP_FORBIDDEN;
} else {
level = NGX_LOG_CRIT;
rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
}
ngx_log_error(level, r->connection->log, err,
ngx_open_dir_n " \"%s\" failed", path.data);
return rc;
}
#if (NGX_SUPPRESS_WARN)
/* MSVC thinks 'entries' may be used without having been initialized */
ngx_memzero(&entries, sizeof(ngx_array_t));
#endif
/* TODO: pool should be temporary pool */
pool = r->pool;
if (ngx_array_init(&entries, pool, 40, sizeof(ngx_http_autoindex_entry_t))
!= NGX_OK)
{
return ngx_http_autoindex_error(r, &dir, &path);
}
r->headers_out.status = NGX_HTTP_OK;
switch (format) {
case NGX_HTTP_AUTOINDEX_JSON:
ngx_str_set(&r->headers_out.content_type, "application/json");
break;
case NGX_HTTP_AUTOINDEX_JSONP:
ngx_str_set(&r->headers_out.content_type, "application/javascript");
break;
case NGX_HTTP_AUTOINDEX_XML:
ngx_str_set(&r->headers_out.content_type, "text/xml");
ngx_str_set(&r->headers_out.charset, "utf-8");
break;
default: /* NGX_HTTP_AUTOINDEX_HTML */
ngx_str_set(&r->headers_out.content_type, "text/html");
break;
}
r->headers_out.content_type_len = r->headers_out.content_type.len;
r->headers_out.content_type_lowcase = NULL;
rc = ngx_http_send_header(r);
if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
if (ngx_close_dir(&dir) == NGX_ERROR) {
ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno,
ngx_close_dir_n " \"%V\" failed", &path);
}
return rc;
}
filename = path.data;
filename[path.len] = '/';
for ( ;; ) {
ngx_set_errno(0);
if (ngx_read_dir(&dir) == NGX_ERROR) {
err = ngx_errno;
if (err != NGX_ENOMOREFILES) {
ngx_log_error(NGX_LOG_CRIT, r->connection->log, err,
ngx_read_dir_n " \"%V\" failed", &path);
return ngx_http_autoindex_error(r, &dir, &path);
}
break;
}
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http autoindex file: \"%s\"", ngx_de_name(&dir));
len = ngx_de_namelen(&dir);
if (ngx_de_name(&dir)[0] == '.') {
continue;
}
if (!dir.valid_info) {
/* 1 byte for '/' and 1 byte for terminating '\0' */
if (path.len + 1 + len + 1 > allocated) {
allocated = path.len + 1 + len + 1
+ NGX_HTTP_AUTOINDEX_PREALLOCATE;
filename = ngx_pnalloc(pool, allocated);
if (filename == NULL) {
return ngx_http_autoindex_error(r, &dir, &path);
}
last = ngx_cpystrn(filename, path.data, path.len + 1);
*last++ = '/';
}
ngx_cpystrn(last, ngx_de_name(&dir), len + 1);
if (ngx_de_info(filename, &dir) == NGX_FILE_ERROR) {
err = ngx_errno;
if (err != NGX_ENOENT && err != NGX_ELOOP) {
ngx_log_error(NGX_LOG_CRIT, r->connection->log, err,
ngx_de_info_n " \"%s\" failed", filename);
if (err == NGX_EACCES) {
continue;
}
return ngx_http_autoindex_error(r, &dir, &path);
}
if (ngx_de_link_info(filename, &dir) == NGX_FILE_ERROR) {
ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,
ngx_de_link_info_n " \"%s\" failed",
filename);
return ngx_http_autoindex_error(r, &dir, &path);
}
}
}
entry = ngx_array_push(&entries);
if (entry == NULL) {
return ngx_http_autoindex_error(r, &dir, &path);
}
entry->name.len = len;
entry->name.data = ngx_pnalloc(pool, len + 1);
if (entry->name.data == NULL) {
return ngx_http_autoindex_error(r, &dir, &path);
}
ngx_cpystrn(entry->name.data, ngx_de_name(&dir), len + 1);
entry->dir = ngx_de_is_dir(&dir);
entry->file = ngx_de_is_file(&dir);
entry->mtime = ngx_de_mtime(&dir);
entry->size = ngx_de_size(&dir);
}
if (ngx_close_dir(&dir) == NGX_ERROR) {
ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno,
ngx_close_dir_n " \"%V\" failed", &path);
}
if (entries.nelts > 1) {
ngx_qsort(entries.elts, (size_t) entries.nelts,
sizeof(ngx_http_autoindex_entry_t),
ngx_http_autoindex_cmp_entries);
}
switch (format) {
case NGX_HTTP_AUTOINDEX_JSON:
b = ngx_http_autoindex_json(r, &entries, NULL);
break;
case NGX_HTTP_AUTOINDEX_JSONP:
b = ngx_http_autoindex_json(r, &entries, &callback);
break;
case NGX_HTTP_AUTOINDEX_XML:
b = ngx_http_autoindex_xml(r, &entries);
break;
default: /* NGX_HTTP_AUTOINDEX_HTML */
b = ngx_http_autoindex_html(r, &entries);
break;
}
if (b == NULL) {
return NGX_ERROR;
}
/* TODO: free temporary pool */
if (r == r->main) {
b->last_buf = 1;
}
b->last_in_chain = 1;
out.buf = b;
out.next = NULL;
return ngx_http_output_filter(r, &out);
}
static ngx_buf_t *
ngx_http_autoindex_html(ngx_http_request_t *r, ngx_array_t *entries)
{
u_char *last, scale;
off_t length;
size_t len, char_len, escape_html;
ngx_tm_t tm;
ngx_buf_t *b;
ngx_int_t size;
ngx_uint_t i, utf8;
ngx_time_t *tp;
ngx_http_autoindex_entry_t *entry;
ngx_http_autoindex_loc_conf_t *alcf;
static u_char title[] =
"" CRLF
"Index of "
;
static u_char header[] =
"" CRLF
"" CRLF
"Index of "
;
static u_char tail[] =
"" CRLF
"" CRLF
;
static char *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
if (r->headers_out.charset.len == 5
&& ngx_strncasecmp(r->headers_out.charset.data, (u_char *) "utf-8", 5)
== 0)
{
utf8 = 1;
} else {
utf8 = 0;
}
escape_html = ngx_escape_html(NULL, r->uri.data, r->uri.len);
len = sizeof(title) - 1
+ r->uri.len + escape_html
+ sizeof(header) - 1
+ r->uri.len + escape_html
+ sizeof("
") - 1
+ sizeof("
../" CRLF) - 1
+ sizeof("
") - 1
+ sizeof(tail) - 1;
entry = entries->elts;
for (i = 0; i < entries->nelts; i++) {
entry[i].escape = 2 * ngx_escape_uri(NULL, entry[i].name.data,
entry[i].name.len,
NGX_ESCAPE_URI_COMPONENT);
entry[i].escape_html = ngx_escape_html(NULL, entry[i].name.data,
entry[i].name.len);
if (utf8) {
entry[i].utf_len = ngx_utf8_length(entry[i].name.data,
entry[i].name.len);
} else {
entry[i].utf_len = entry[i].name.len;
}
len += sizeof("") - 1
+ entry[i].name.len - entry[i].utf_len
+ entry[i].escape_html
+ NGX_HTTP_AUTOINDEX_NAME_LEN + sizeof(">") - 2
+ sizeof("") - 1
+ sizeof(" 28-Sep-1970 12:00 ") - 1
+ 20 /* the file size */
+ 2;
}
b = ngx_create_temp_buf(r->pool, len);
if (b == NULL) {
return NULL;
}
b->last = ngx_cpymem(b->last, title, sizeof(title) - 1);
if (escape_html) {
b->last = (u_char *) ngx_escape_html(b->last, r->uri.data, r->uri.len);
b->last = ngx_cpymem(b->last, header, sizeof(header) - 1);
b->last = (u_char *) ngx_escape_html(b->last, r->uri.data, r->uri.len);
} else {
b->last = ngx_cpymem(b->last, r->uri.data, r->uri.len);
b->last = ngx_cpymem(b->last, header, sizeof(header) - 1);
b->last = ngx_cpymem(b->last, r->uri.data, r->uri.len);
}
b->last = ngx_cpymem(b->last, "", sizeof("") - 1);
b->last = ngx_cpymem(b->last, "
../" CRLF,
sizeof("
../" CRLF) - 1);
alcf = ngx_http_get_module_loc_conf(r, ngx_http_autoindex_module);
tp = ngx_timeofday();
for (i = 0; i < entries->nelts; i++) {
b->last = ngx_cpymem(b->last, "last, entry[i].name.data, entry[i].name.len,
NGX_ESCAPE_URI_COMPONENT);
b->last += entry[i].name.len + entry[i].escape;
} else {
b->last = ngx_cpymem(b->last, entry[i].name.data,
entry[i].name.len);
}
if (entry[i].dir) {
*b->last++ = '/';
}
*b->last++ = '"';
*b->last++ = '>';
len = entry[i].utf_len;
if (entry[i].name.len != len) {
if (len > NGX_HTTP_AUTOINDEX_NAME_LEN) {
char_len = NGX_HTTP_AUTOINDEX_NAME_LEN - 3 + 1;
} else {
char_len = NGX_HTTP_AUTOINDEX_NAME_LEN + 1;
}
last = b->last;
b->last = ngx_utf8_cpystrn(b->last, entry[i].name.data,
char_len, entry[i].name.len + 1);
if (entry[i].escape_html) {
b->last = (u_char *) ngx_escape_html(last, entry[i].name.data,
b->last - last);
}
last = b->last;
} else {
if (entry[i].escape_html) {
if (len > NGX_HTTP_AUTOINDEX_NAME_LEN) {
char_len = NGX_HTTP_AUTOINDEX_NAME_LEN - 3;
} else {
char_len = len;
}
b->last = (u_char *) ngx_escape_html(b->last,
entry[i].name.data, char_len);
last = b->last;
} else {
b->last = ngx_cpystrn(b->last, entry[i].name.data,
NGX_HTTP_AUTOINDEX_NAME_LEN + 1);
last = b->last - 3;
}
}
if (len > NGX_HTTP_AUTOINDEX_NAME_LEN) {
b->last = ngx_cpymem(last, "..>", sizeof("..>") - 1);
} else {
if (entry[i].dir && NGX_HTTP_AUTOINDEX_NAME_LEN - len > 0) {
*b->last++ = '/';
len++;
}
b->last = ngx_cpymem(b->last, "", sizeof("") - 1);
if (NGX_HTTP_AUTOINDEX_NAME_LEN - len > 0) {
ngx_memset(b->last, ' ', NGX_HTTP_AUTOINDEX_NAME_LEN - len);
b->last += NGX_HTTP_AUTOINDEX_NAME_LEN - len;
}
}
*b->last++ = ' ';
ngx_gmtime(entry[i].mtime + tp->gmtoff * 60 * alcf->localtime, &tm);
b->last = ngx_sprintf(b->last, "%02d-%s-%d %02d:%02d ",
tm.ngx_tm_mday,
months[tm.ngx_tm_mon - 1],
tm.ngx_tm_year,
tm.ngx_tm_hour,
tm.ngx_tm_min);
if (alcf->exact_size) {
if (entry[i].dir) {
b->last = ngx_cpymem(b->last, " -",
sizeof(" -") - 1);
} else {
b->last = ngx_sprintf(b->last, "%19O", entry[i].size);
}
} else {
if (entry[i].dir) {
b->last = ngx_cpymem(b->last, " -",
sizeof(" -") - 1);
} else {
length = entry[i].size;
if (length > 1024 * 1024 * 1024 - 1) {
size = (ngx_int_t) (length / (1024 * 1024 * 1024));
if ((length % (1024 * 1024 * 1024))
> (1024 * 1024 * 1024 / 2 - 1))
{
size++;
}
scale = 'G';
} else if (length > 1024 * 1024 - 1) {
size = (ngx_int_t) (length / (1024 * 1024));
if ((length % (1024 * 1024)) > (1024 * 1024 / 2 - 1)) {
size++;
}
scale = 'M';
} else if (length > 9999) {
size = (ngx_int_t) (length / 1024);
if (length % 1024 > 511) {
size++;
}
scale = 'K';
} else {
size = (ngx_int_t) length;
scale = '\0';
}
if (scale) {
b->last = ngx_sprintf(b->last, "%6i%c", size, scale);
} else {
b->last = ngx_sprintf(b->last, " %6i", size);
}
}
}
*b->last++ = CR;
*b->last++ = LF;
}
b->last = ngx_cpymem(b->last, "
", sizeof("
") - 1);
b->last = ngx_cpymem(b->last, tail, sizeof(tail) - 1);
return b;
}
static ngx_buf_t *
ngx_http_autoindex_json(ngx_http_request_t *r, ngx_array_t *entries,
ngx_str_t *callback)
{
size_t len;
ngx_buf_t *b;
ngx_uint_t i;
ngx_http_autoindex_entry_t *entry;
len = sizeof("[" CRLF CRLF "]") - 1;
if (callback) {
len += sizeof("/* callback */" CRLF "();") - 1 + callback->len;
}
entry = entries->elts;
for (i = 0; i < entries->nelts; i++) {
entry[i].escape = ngx_escape_json(NULL, entry[i].name.data,
entry[i].name.len);
len += sizeof("{ }," CRLF) - 1
+ sizeof("\"name\":\"\"") - 1
+ entry[i].name.len + entry[i].escape
+ sizeof(", \"type\":\"directory\"") - 1
+ sizeof(", \"mtime\":\"Wed, 31 Dec 1986 10:00:00 GMT\"") - 1;
if (entry[i].file) {
len += sizeof(", \"size\":") - 1 + NGX_OFF_T_LEN;
}
}
b = ngx_create_temp_buf(r->pool, len);
if (b == NULL) {
return NULL;
}
if (callback) {
b->last = ngx_cpymem(b->last, "/* callback */" CRLF,
sizeof("/* callback */" CRLF) - 1);
b->last = ngx_cpymem(b->last, callback->data, callback->len);
*b->last++ = '(';
}
*b->last++ = '[';
for (i = 0; i < entries->nelts; i++) {
b->last = ngx_cpymem(b->last, CRLF "{ \"name\":\"",
sizeof(CRLF "{ \"name\":\"") - 1);
if (entry[i].escape) {
b->last = (u_char *) ngx_escape_json(b->last, entry[i].name.data,
entry[i].name.len);
} else {
b->last = ngx_cpymem(b->last, entry[i].name.data,
entry[i].name.len);
}
b->last = ngx_cpymem(b->last, "\", \"type\":\"",
sizeof("\", \"type\":\"") - 1);
if (entry[i].dir) {
b->last = ngx_cpymem(b->last, "directory", sizeof("directory") - 1);
} else if (entry[i].file) {
b->last = ngx_cpymem(b->last, "file", sizeof("file") - 1);
} else {
b->last = ngx_cpymem(b->last, "other", sizeof("other") - 1);
}
b->last = ngx_cpymem(b->last, "\", \"mtime\":\"",
sizeof("\", \"mtime\":\"") - 1);
b->last = ngx_http_time(b->last, entry[i].mtime);
if (entry[i].file) {
b->last = ngx_cpymem(b->last, "\", \"size\":",
sizeof("\", \"size\":") - 1);
b->last = ngx_sprintf(b->last, "%O", entry[i].size);
} else {
*b->last++ = '"';
}
b->last = ngx_cpymem(b->last, " },", sizeof(" },") - 1);
}
if (i > 0) {
b->last--; /* strip last comma */
}
b->last = ngx_cpymem(b->last, CRLF "]", sizeof(CRLF "]") - 1);
if (callback) {
*b->last++ = ')'; *b->last++ = ';';
}
return b;
}
static ngx_int_t
ngx_http_autoindex_jsonp_callback(ngx_http_request_t *r, ngx_str_t *callback)
{
u_char *p, c, ch;
ngx_uint_t i;
if (ngx_http_arg(r, (u_char *) "callback", 8, callback) != NGX_OK) {
callback->len = 0;
return NGX_OK;
}
if (callback->len > 128) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client sent too long callback name: \"%V\"", callback);
return NGX_DECLINED;
}
p = callback->data;
for (i = 0; i < callback->len; i++) {
ch = p[i];
c = (u_char) (ch | 0x20);
if (c >= 'a' && c <= 'z') {
continue;
}
if ((ch >= '0' && ch <= '9') || ch == '_' || ch == '.') {
continue;
}
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client sent invalid callback name: \"%V\"", callback);
return NGX_DECLINED;
}
return NGX_OK;
}
static ngx_buf_t *
ngx_http_autoindex_xml(ngx_http_request_t *r, ngx_array_t *entries)
{
size_t len;
ngx_tm_t tm;
ngx_buf_t *b;
ngx_str_t type;
ngx_uint_t i;
ngx_http_autoindex_entry_t *entry;
static u_char head[] = "" CRLF "" CRLF;
static u_char tail[] = "
" CRLF;
len = sizeof(head) - 1 + sizeof(tail) - 1;
entry = entries->elts;
for (i = 0; i < entries->nelts; i++) {
entry[i].escape = ngx_escape_html(NULL, entry[i].name.data,
entry[i].name.len);
len += sizeof("" CRLF) - 1
+ entry[i].name.len + entry[i].escape
+ sizeof(" mtime=\"1986-12-31T10:00:00Z\"") - 1;
if (entry[i].file) {
len += sizeof(" size=\"\"") - 1 + NGX_OFF_T_LEN;
}
}
b = ngx_create_temp_buf(r->pool, len);
if (b == NULL) {
return NULL;
}
b->last = ngx_cpymem(b->last, head, sizeof(head) - 1);
for (i = 0; i < entries->nelts; i++) {
*b->last++ = '<';
if (entry[i].dir) {
ngx_str_set(&type, "directory");
} else if (entry[i].file) {
ngx_str_set(&type, "file");
} else {
ngx_str_set(&type, "other");
}
b->last = ngx_cpymem(b->last, type.data, type.len);
b->last = ngx_cpymem(b->last, " mtime=\"", sizeof(" mtime=\"") - 1);
ngx_gmtime(entry[i].mtime, &tm);
b->last = ngx_sprintf(b->last, "%4d-%02d-%02dT%02d:%02d:%02dZ",
tm.ngx_tm_year, tm.ngx_tm_mon,
tm.ngx_tm_mday, tm.ngx_tm_hour,
tm.ngx_tm_min, tm.ngx_tm_sec);
if (entry[i].file) {
b->last = ngx_cpymem(b->last, "\" size=\"",
sizeof("\" size=\"") - 1);
b->last = ngx_sprintf(b->last, "%O", entry[i].size);
}
*b->last++ = '"'; *b->last++ = '>';
if (entry[i].escape) {
b->last = (u_char *) ngx_escape_html(b->last, entry[i].name.data,
entry[i].name.len);
} else {
b->last = ngx_cpymem(b->last, entry[i].name.data,
entry[i].name.len);
}
*b->last++ = '<'; *b->last++ = '/';
b->last = ngx_cpymem(b->last, type.data, type.len);
*b->last++ = '>';
*b->last++ = CR; *b->last++ = LF;
}
b->last = ngx_cpymem(b->last, tail, sizeof(tail) - 1);
return b;
}
static int ngx_libc_cdecl
ngx_http_autoindex_cmp_entries(const void *one, const void *two)
{
ngx_http_autoindex_entry_t *first = (ngx_http_autoindex_entry_t *) one;
ngx_http_autoindex_entry_t *second = (ngx_http_autoindex_entry_t *) two;
if (first->dir && !second->dir) {
/* move the directories to the start */
return -1;
}
if (!first->dir && second->dir) {
/* move the directories to the start */
return 1;
}
return (int) ngx_strcmp(first->name.data, second->name.data);
}
#if 0
static ngx_buf_t *
ngx_http_autoindex_alloc(ngx_http_autoindex_ctx_t *ctx, size_t size)
{
ngx_chain_t *cl;
if (ctx->buf) {
if ((size_t) (ctx->buf->end - ctx->buf->last) >= size) {
return ctx->buf;
}
ctx->size += ctx->buf->last - ctx->buf->pos;
}
ctx->buf = ngx_create_temp_buf(ctx->pool, ctx->alloc_size);
if (ctx->buf == NULL) {
return NULL;
}
cl = ngx_alloc_chain_link(ctx->pool);
if (cl == NULL) {
return NULL;
}
cl->buf = ctx->buf;
cl->next = NULL;
*ctx->last_out = cl;
ctx->last_out = &cl->next;
return ctx->buf;
}
#endif
static ngx_int_t
ngx_http_autoindex_error(ngx_http_request_t *r, ngx_dir_t *dir, ngx_str_t *name)
{
if (ngx_close_dir(dir) == NGX_ERROR) {
ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno,
ngx_close_dir_n " \"%V\" failed", name);
}
return r->header_sent ? NGX_ERROR : NGX_HTTP_INTERNAL_SERVER_ERROR;
}
static void *
ngx_http_autoindex_create_loc_conf(ngx_conf_t *cf)
{
ngx_http_autoindex_loc_conf_t *conf;
conf = ngx_palloc(cf->pool, sizeof(ngx_http_autoindex_loc_conf_t));
if (conf == NULL) {
return NULL;
}
conf->enable = NGX_CONF_UNSET;
conf->format = NGX_CONF_UNSET_UINT;
conf->localtime = NGX_CONF_UNSET;
conf->exact_size = NGX_CONF_UNSET;
return conf;
}
static char *
ngx_http_autoindex_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
{
ngx_http_autoindex_loc_conf_t *prev = parent;
ngx_http_autoindex_loc_conf_t *conf = child;
ngx_conf_merge_value(conf->enable, prev->enable, 0);
ngx_conf_merge_uint_value(conf->format, prev->format,
NGX_HTTP_AUTOINDEX_HTML);
ngx_conf_merge_value(conf->localtime, prev->localtime, 0);
ngx_conf_merge_value(conf->exact_size, prev->exact_size, 1);
return NGX_CONF_OK;
}
static ngx_int_t
ngx_http_autoindex_init(ngx_conf_t *cf)
{
ngx_http_handler_pt *h;
ngx_http_core_main_conf_t *cmcf;
cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers);
if (h == NULL) {
return NGX_ERROR;
}
*h = ngx_http_autoindex_handler;
return NGX_OK;
}
nginx-1.14.0/src/http/modules/ngx_http_browser_module.c 000644 001751 001751 00000046721 13265410474 024453 0 ustar 00mdounin mdounin 000000 000000
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#include
#include
#include
/*
* The module can check browser versions conforming to the following formats:
* X, X.X, X.X.X, and X.X.X.X. The maximum values of each format may be
* 4000, 4000.99, 4000.99.99, and 4000.99.99.99.
*/
#define NGX_HTTP_MODERN_BROWSER 0
#define NGX_HTTP_ANCIENT_BROWSER 1
typedef struct {
u_char browser[12];
size_t skip;
size_t add;
u_char name[12];
} ngx_http_modern_browser_mask_t;
typedef struct {
ngx_uint_t version;
size_t skip;
size_t add;
u_char name[12];
} ngx_http_modern_browser_t;
typedef struct {
ngx_array_t *modern_browsers;
ngx_array_t *ancient_browsers;
ngx_http_variable_value_t *modern_browser_value;
ngx_http_variable_value_t *ancient_browser_value;
unsigned modern_unlisted_browsers:1;
unsigned netscape4:1;
} ngx_http_browser_conf_t;
static ngx_int_t ngx_http_msie_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_browser_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_uint_t ngx_http_browser(ngx_http_request_t *r,
ngx_http_browser_conf_t *cf);
static ngx_int_t ngx_http_browser_add_variables(ngx_conf_t *cf);
static void *ngx_http_browser_create_conf(ngx_conf_t *cf);
static char *ngx_http_browser_merge_conf(ngx_conf_t *cf, void *parent,
void *child);
static int ngx_libc_cdecl ngx_http_modern_browser_sort(const void *one,
const void *two);
static char *ngx_http_modern_browser(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static char *ngx_http_ancient_browser(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static char *ngx_http_modern_browser_value(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static char *ngx_http_ancient_browser_value(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static ngx_command_t ngx_http_browser_commands[] = {
{ ngx_string("modern_browser"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12,
ngx_http_modern_browser,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
{ ngx_string("ancient_browser"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
ngx_http_ancient_browser,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
{ ngx_string("modern_browser_value"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_http_modern_browser_value,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
{ ngx_string("ancient_browser_value"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_http_ancient_browser_value,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
ngx_null_command
};
static ngx_http_module_t ngx_http_browser_module_ctx = {
ngx_http_browser_add_variables, /* preconfiguration */
NULL, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
ngx_http_browser_create_conf, /* create location configuration */
ngx_http_browser_merge_conf /* merge location configuration */
};
ngx_module_t ngx_http_browser_module = {
NGX_MODULE_V1,
&ngx_http_browser_module_ctx, /* module context */
ngx_http_browser_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static ngx_http_modern_browser_mask_t ngx_http_modern_browser_masks[] = {
/* Opera must be the first browser to check */
/*
* "Opera/7.50 (X11; FreeBSD i386; U) [en]"
* "Mozilla/5.0 (X11; FreeBSD i386; U) Opera 7.50 [en]"
* "Mozilla/4.0 (compatible; MSIE 6.0; X11; FreeBSD i386) Opera 7.50 [en]"
* "Opera/8.0 (Windows NT 5.1; U; ru)"
* "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; en) Opera 8.0"
* "Opera/9.01 (X11; FreeBSD 6 i386; U; en)"
*/
{ "opera",
0,
sizeof("Opera ") - 1,
"Opera"},
/* "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)" */
{ "msie",
sizeof("Mozilla/4.0 (compatible; ") - 1,
sizeof("MSIE ") - 1,
"MSIE "},
/*
* "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.0.0) Gecko/20020610"
* "Mozilla/5.0 (Windows; U; Windows NT 5.1; ru-RU; rv:1.5) Gecko/20031006"
* "Mozilla/5.0 (Windows; U; Windows NT 5.1; ru-RU; rv:1.6) Gecko/20040206
* Firefox/0.8"
* "Mozilla/5.0 (Windows; U; Windows NT 5.1; ru-RU; rv:1.7.8)
* Gecko/20050511 Firefox/1.0.4"
* "Mozilla/5.0 (X11; U; FreeBSD i386; en-US; rv:1.8.0.5) Gecko/20060729
* Firefox/1.5.0.5"
*/
{ "gecko",
sizeof("Mozilla/5.0 (") - 1,
sizeof("rv:") - 1,
"rv:"},
/*
* "Mozilla/5.0 (Macintosh; U; PPC Mac OS X; ru-ru) AppleWebKit/125.2
* (KHTML, like Gecko) Safari/125.7"
* "Mozilla/5.0 (SymbianOS/9.1; U; en-us) AppleWebKit/413
* (KHTML, like Gecko) Safari/413"
* "Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/418
* (KHTML, like Gecko) Safari/417.9.3"
* "Mozilla/5.0 (Macintosh; U; PPC Mac OS X; ru-ru) AppleWebKit/418.8
* (KHTML, like Gecko) Safari/419.3"
*/
{ "safari",
sizeof("Mozilla/5.0 (") - 1,
sizeof("Safari/") - 1,
"Safari/"},
/*
* "Mozilla/5.0 (compatible; Konqueror/3.1; Linux)"
* "Mozilla/5.0 (compatible; Konqueror/3.4; Linux) KHTML/3.4.2 (like Gecko)"
* "Mozilla/5.0 (compatible; Konqueror/3.5; FreeBSD) KHTML/3.5.1
* (like Gecko)"
*/
{ "konqueror",
sizeof("Mozilla/5.0 (compatible; ") - 1,
sizeof("Konqueror/") - 1,
"Konqueror/"},
{ "", 0, 0, "" }
};
static ngx_http_variable_t ngx_http_browser_vars[] = {
{ ngx_string("msie"), NULL, ngx_http_msie_variable,
0, NGX_HTTP_VAR_CHANGEABLE, 0 },
{ ngx_string("modern_browser"), NULL, ngx_http_browser_variable,
NGX_HTTP_MODERN_BROWSER, NGX_HTTP_VAR_CHANGEABLE, 0 },
{ ngx_string("ancient_browser"), NULL, ngx_http_browser_variable,
NGX_HTTP_ANCIENT_BROWSER, NGX_HTTP_VAR_CHANGEABLE, 0 },
ngx_http_null_variable
};
static ngx_int_t
ngx_http_browser_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
uintptr_t data)
{
ngx_uint_t rc;
ngx_http_browser_conf_t *cf;
cf = ngx_http_get_module_loc_conf(r, ngx_http_browser_module);
rc = ngx_http_browser(r, cf);
if (data == NGX_HTTP_MODERN_BROWSER && rc == NGX_HTTP_MODERN_BROWSER) {
*v = *cf->modern_browser_value;
return NGX_OK;
}
if (data == NGX_HTTP_ANCIENT_BROWSER && rc == NGX_HTTP_ANCIENT_BROWSER) {
*v = *cf->ancient_browser_value;
return NGX_OK;
}
*v = ngx_http_variable_null_value;
return NGX_OK;
}
static ngx_uint_t
ngx_http_browser(ngx_http_request_t *r, ngx_http_browser_conf_t *cf)
{
size_t len;
u_char *name, *ua, *last, c;
ngx_str_t *ancient;
ngx_uint_t i, version, ver, scale;
ngx_http_modern_browser_t *modern;
if (r->headers_in.user_agent == NULL) {
if (cf->modern_unlisted_browsers) {
return NGX_HTTP_MODERN_BROWSER;
}
return NGX_HTTP_ANCIENT_BROWSER;
}
ua = r->headers_in.user_agent->value.data;
len = r->headers_in.user_agent->value.len;
last = ua + len;
if (cf->modern_browsers) {
modern = cf->modern_browsers->elts;
for (i = 0; i < cf->modern_browsers->nelts; i++) {
name = ua + modern[i].skip;
if (name >= last) {
continue;
}
name = (u_char *) ngx_strstr(name, modern[i].name);
if (name == NULL) {
continue;
}
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"browser: \"%s\"", name);
name += modern[i].add;
if (name >= last) {
continue;
}
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"version: \"%ui\" \"%s\"", modern[i].version, name);
version = 0;
ver = 0;
scale = 1000000;
while (name < last) {
c = *name++;
if (c >= '0' && c <= '9') {
ver = ver * 10 + (c - '0');
continue;
}
if (c == '.') {
version += ver * scale;
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"version: \"%ui\" \"%ui\"",
modern[i].version, version);
if (version > modern[i].version) {
return NGX_HTTP_MODERN_BROWSER;
}
ver = 0;
scale /= 100;
continue;
}
break;
}
version += ver * scale;
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"version: \"%ui\" \"%ui\"",
modern[i].version, version);
if (version >= modern[i].version) {
return NGX_HTTP_MODERN_BROWSER;
}
return NGX_HTTP_ANCIENT_BROWSER;
}
if (!cf->modern_unlisted_browsers) {
return NGX_HTTP_ANCIENT_BROWSER;
}
}
if (cf->netscape4) {
if (len > sizeof("Mozilla/4.72 ") - 1
&& ngx_strncmp(ua, "Mozilla/", sizeof("Mozilla/") - 1) == 0
&& ua[8] > '0' && ua[8] < '5')
{
return NGX_HTTP_ANCIENT_BROWSER;
}
}
if (cf->ancient_browsers) {
ancient = cf->ancient_browsers->elts;
for (i = 0; i < cf->ancient_browsers->nelts; i++) {
if (len >= ancient[i].len
&& ngx_strstr(ua, ancient[i].data) != NULL)
{
return NGX_HTTP_ANCIENT_BROWSER;
}
}
}
if (cf->modern_unlisted_browsers) {
return NGX_HTTP_MODERN_BROWSER;
}
return NGX_HTTP_ANCIENT_BROWSER;
}
static ngx_int_t
ngx_http_msie_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
uintptr_t data)
{
if (r->headers_in.msie) {
*v = ngx_http_variable_true_value;
return NGX_OK;
}
*v = ngx_http_variable_null_value;
return NGX_OK;
}
static ngx_int_t
ngx_http_browser_add_variables(ngx_conf_t *cf)
{
ngx_http_variable_t *var, *v;
for (v = ngx_http_browser_vars; v->name.len; v++) {
var = ngx_http_add_variable(cf, &v->name, v->flags);
if (var == NULL) {
return NGX_ERROR;
}
var->get_handler = v->get_handler;
var->data = v->data;
}
return NGX_OK;
}
static void *
ngx_http_browser_create_conf(ngx_conf_t *cf)
{
ngx_http_browser_conf_t *conf;
conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_browser_conf_t));
if (conf == NULL) {
return NULL;
}
/*
* set by ngx_pcalloc():
*
* conf->modern_browsers = NULL;
* conf->ancient_browsers = NULL;
* conf->modern_browser_value = NULL;
* conf->ancient_browser_value = NULL;
*
* conf->modern_unlisted_browsers = 0;
* conf->netscape4 = 0;
*/
return conf;
}
static char *
ngx_http_browser_merge_conf(ngx_conf_t *cf, void *parent, void *child)
{
ngx_http_browser_conf_t *prev = parent;
ngx_http_browser_conf_t *conf = child;
ngx_uint_t i, n;
ngx_http_modern_browser_t *browsers, *opera;
/*
* At the merge the skip field is used to store the browser slot,
* it will be used in sorting and then will overwritten
* with a real skip value. The zero value means Opera.
*/
if (conf->modern_browsers == NULL && conf->modern_unlisted_browsers == 0) {
conf->modern_browsers = prev->modern_browsers;
conf->modern_unlisted_browsers = prev->modern_unlisted_browsers;
} else if (conf->modern_browsers != NULL) {
browsers = conf->modern_browsers->elts;
for (i = 0; i < conf->modern_browsers->nelts; i++) {
if (browsers[i].skip == 0) {
goto found;
}
}
/*
* Opera may contain MSIE string, so if Opera was not enumerated
* as modern browsers, then add it and set a unreachable version
*/
opera = ngx_array_push(conf->modern_browsers);
if (opera == NULL) {
return NGX_CONF_ERROR;
}
opera->skip = 0;
opera->version = 4001000000U;
browsers = conf->modern_browsers->elts;
found:
ngx_qsort(browsers, (size_t) conf->modern_browsers->nelts,
sizeof(ngx_http_modern_browser_t),
ngx_http_modern_browser_sort);
for (i = 0; i < conf->modern_browsers->nelts; i++) {
n = browsers[i].skip;
browsers[i].skip = ngx_http_modern_browser_masks[n].skip;
browsers[i].add = ngx_http_modern_browser_masks[n].add;
(void) ngx_cpystrn(browsers[i].name,
ngx_http_modern_browser_masks[n].name, 12);
}
}
if (conf->ancient_browsers == NULL && conf->netscape4 == 0) {
conf->ancient_browsers = prev->ancient_browsers;
conf->netscape4 = prev->netscape4;
}
if (conf->modern_browser_value == NULL) {
conf->modern_browser_value = prev->modern_browser_value;
}
if (conf->modern_browser_value == NULL) {
conf->modern_browser_value = &ngx_http_variable_true_value;
}
if (conf->ancient_browser_value == NULL) {
conf->ancient_browser_value = prev->ancient_browser_value;
}
if (conf->ancient_browser_value == NULL) {
conf->ancient_browser_value = &ngx_http_variable_true_value;
}
return NGX_CONF_OK;
}
static int ngx_libc_cdecl
ngx_http_modern_browser_sort(const void *one, const void *two)
{
ngx_http_modern_browser_t *first = (ngx_http_modern_browser_t *) one;
ngx_http_modern_browser_t *second = (ngx_http_modern_browser_t *) two;
return (first->skip - second->skip);
}
static char *
ngx_http_modern_browser(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_browser_conf_t *bcf = conf;
u_char c;
ngx_str_t *value;
ngx_uint_t i, n, version, ver, scale;
ngx_http_modern_browser_t *browser;
ngx_http_modern_browser_mask_t *mask;
value = cf->args->elts;
if (cf->args->nelts == 2) {
if (ngx_strcmp(value[1].data, "unlisted") == 0) {
bcf->modern_unlisted_browsers = 1;
return NGX_CONF_OK;
}
return NGX_CONF_ERROR;
}
if (bcf->modern_browsers == NULL) {
bcf->modern_browsers = ngx_array_create(cf->pool, 5,
sizeof(ngx_http_modern_browser_t));
if (bcf->modern_browsers == NULL) {
return NGX_CONF_ERROR;
}
}
browser = ngx_array_push(bcf->modern_browsers);
if (browser == NULL) {
return NGX_CONF_ERROR;
}
mask = ngx_http_modern_browser_masks;
for (n = 0; mask[n].browser[0] != '\0'; n++) {
if (ngx_strcasecmp(mask[n].browser, value[1].data) == 0) {
goto found;
}
}
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"unknown browser name \"%V\"", &value[1]);
return NGX_CONF_ERROR;
found:
/*
* at this stage the skip field is used to store the browser slot,
* it will be used in sorting in merge stage and then will overwritten
* with a real value
*/
browser->skip = n;
version = 0;
ver = 0;
scale = 1000000;
for (i = 0; i < value[2].len; i++) {
c = value[2].data[i];
if (c >= '0' && c <= '9') {
ver = ver * 10 + (c - '0');
continue;
}
if (c == '.') {
version += ver * scale;
ver = 0;
scale /= 100;
continue;
}
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid browser version \"%V\"", &value[2]);
return NGX_CONF_ERROR;
}
version += ver * scale;
browser->version = version;
return NGX_CONF_OK;
}
static char *
ngx_http_ancient_browser(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_browser_conf_t *bcf = conf;
ngx_str_t *value, *browser;
ngx_uint_t i;
value = cf->args->elts;
for (i = 1; i < cf->args->nelts; i++) {
if (ngx_strcmp(value[i].data, "netscape4") == 0) {
bcf->netscape4 = 1;
continue;
}
if (bcf->ancient_browsers == NULL) {
bcf->ancient_browsers = ngx_array_create(cf->pool, 4,
sizeof(ngx_str_t));
if (bcf->ancient_browsers == NULL) {
return NGX_CONF_ERROR;
}
}
browser = ngx_array_push(bcf->ancient_browsers);
if (browser == NULL) {
return NGX_CONF_ERROR;
}
*browser = value[i];
}
return NGX_CONF_OK;
}
static char *
ngx_http_modern_browser_value(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_browser_conf_t *bcf = conf;
ngx_str_t *value;
bcf->modern_browser_value = ngx_palloc(cf->pool,
sizeof(ngx_http_variable_value_t));
if (bcf->modern_browser_value == NULL) {
return NGX_CONF_ERROR;
}
value = cf->args->elts;
bcf->modern_browser_value->len = value[1].len;
bcf->modern_browser_value->valid = 1;
bcf->modern_browser_value->no_cacheable = 0;
bcf->modern_browser_value->not_found = 0;
bcf->modern_browser_value->data = value[1].data;
return NGX_CONF_OK;
}
static char *
ngx_http_ancient_browser_value(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_browser_conf_t *bcf = conf;
ngx_str_t *value;
bcf->ancient_browser_value = ngx_palloc(cf->pool,
sizeof(ngx_http_variable_value_t));
if (bcf->ancient_browser_value == NULL) {
return NGX_CONF_ERROR;
}
value = cf->args->elts;
bcf->ancient_browser_value->len = value[1].len;
bcf->ancient_browser_value->valid = 1;
bcf->ancient_browser_value->no_cacheable = 0;
bcf->ancient_browser_value->not_found = 0;
bcf->ancient_browser_value->data = value[1].data;
return NGX_CONF_OK;
}
nginx-1.14.0/src/http/modules/ngx_http_not_modified_filter_module.c 000644 001751 001751 00000015500 13265410474 026764 0 ustar 00mdounin mdounin 000000 000000
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#include
#include
#include
static ngx_uint_t ngx_http_test_if_unmodified(ngx_http_request_t *r);
static ngx_uint_t ngx_http_test_if_modified(ngx_http_request_t *r);
static ngx_uint_t ngx_http_test_if_match(ngx_http_request_t *r,
ngx_table_elt_t *header, ngx_uint_t weak);
static ngx_int_t ngx_http_not_modified_filter_init(ngx_conf_t *cf);
static ngx_http_module_t ngx_http_not_modified_filter_module_ctx = {
NULL, /* preconfiguration */
ngx_http_not_modified_filter_init, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
NULL, /* create location configuration */
NULL /* merge location configuration */
};
ngx_module_t ngx_http_not_modified_filter_module = {
NGX_MODULE_V1,
&ngx_http_not_modified_filter_module_ctx, /* module context */
NULL, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static ngx_http_output_header_filter_pt ngx_http_next_header_filter;
static ngx_int_t
ngx_http_not_modified_header_filter(ngx_http_request_t *r)
{
if (r->headers_out.status != NGX_HTTP_OK
|| r != r->main
|| r->disable_not_modified)
{
return ngx_http_next_header_filter(r);
}
if (r->headers_in.if_unmodified_since
&& !ngx_http_test_if_unmodified(r))
{
return ngx_http_filter_finalize_request(r, NULL,
NGX_HTTP_PRECONDITION_FAILED);
}
if (r->headers_in.if_match
&& !ngx_http_test_if_match(r, r->headers_in.if_match, 0))
{
return ngx_http_filter_finalize_request(r, NULL,
NGX_HTTP_PRECONDITION_FAILED);
}
if (r->headers_in.if_modified_since || r->headers_in.if_none_match) {
if (r->headers_in.if_modified_since
&& ngx_http_test_if_modified(r))
{
return ngx_http_next_header_filter(r);
}
if (r->headers_in.if_none_match
&& !ngx_http_test_if_match(r, r->headers_in.if_none_match, 1))
{
return ngx_http_next_header_filter(r);
}
/* not modified */
r->headers_out.status = NGX_HTTP_NOT_MODIFIED;
r->headers_out.status_line.len = 0;
r->headers_out.content_type.len = 0;
ngx_http_clear_content_length(r);
ngx_http_clear_accept_ranges(r);
if (r->headers_out.content_encoding) {
r->headers_out.content_encoding->hash = 0;
r->headers_out.content_encoding = NULL;
}
return ngx_http_next_header_filter(r);
}
return ngx_http_next_header_filter(r);
}
static ngx_uint_t
ngx_http_test_if_unmodified(ngx_http_request_t *r)
{
time_t iums;
if (r->headers_out.last_modified_time == (time_t) -1) {
return 0;
}
iums = ngx_parse_http_time(r->headers_in.if_unmodified_since->value.data,
r->headers_in.if_unmodified_since->value.len);
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http iums:%T lm:%T", iums, r->headers_out.last_modified_time);
if (iums >= r->headers_out.last_modified_time) {
return 1;
}
return 0;
}
static ngx_uint_t
ngx_http_test_if_modified(ngx_http_request_t *r)
{
time_t ims;
ngx_http_core_loc_conf_t *clcf;
if (r->headers_out.last_modified_time == (time_t) -1) {
return 1;
}
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
if (clcf->if_modified_since == NGX_HTTP_IMS_OFF) {
return 1;
}
ims = ngx_parse_http_time(r->headers_in.if_modified_since->value.data,
r->headers_in.if_modified_since->value.len);
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http ims:%T lm:%T", ims, r->headers_out.last_modified_time);
if (ims == r->headers_out.last_modified_time) {
return 0;
}
if (clcf->if_modified_since == NGX_HTTP_IMS_EXACT
|| ims < r->headers_out.last_modified_time)
{
return 1;
}
return 0;
}
static ngx_uint_t
ngx_http_test_if_match(ngx_http_request_t *r, ngx_table_elt_t *header,
ngx_uint_t weak)
{
u_char *start, *end, ch;
ngx_str_t etag, *list;
list = &header->value;
if (list->len == 1 && list->data[0] == '*') {
return 1;
}
if (r->headers_out.etag == NULL) {
return 0;
}
etag = r->headers_out.etag->value;
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http im:\"%V\" etag:%V", list, &etag);
if (weak
&& etag.len > 2
&& etag.data[0] == 'W'
&& etag.data[1] == '/')
{
etag.len -= 2;
etag.data += 2;
}
start = list->data;
end = list->data + list->len;
while (start < end) {
if (weak
&& end - start > 2
&& start[0] == 'W'
&& start[1] == '/')
{
start += 2;
}
if (etag.len > (size_t) (end - start)) {
return 0;
}
if (ngx_strncmp(start, etag.data, etag.len) != 0) {
goto skip;
}
start += etag.len;
while (start < end) {
ch = *start;
if (ch == ' ' || ch == '\t') {
start++;
continue;
}
break;
}
if (start == end || *start == ',') {
return 1;
}
skip:
while (start < end && *start != ',') { start++; }
while (start < end) {
ch = *start;
if (ch == ' ' || ch == '\t' || ch == ',') {
start++;
continue;
}
break;
}
}
return 0;
}
static ngx_int_t
ngx_http_not_modified_filter_init(ngx_conf_t *cf)
{
ngx_http_next_header_filter = ngx_http_top_header_filter;
ngx_http_top_header_filter = ngx_http_not_modified_header_filter;
return NGX_OK;
}
nginx-1.14.0/src/http/modules/ngx_http_chunked_filter_module.c 000644 001751 001751 00000021041 13265410474 025742 0 ustar 00mdounin mdounin 000000 000000
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#include
#include
#include
typedef struct {
ngx_chain_t *free;
ngx_chain_t *busy;
} ngx_http_chunked_filter_ctx_t;
static ngx_int_t ngx_http_chunked_filter_init(ngx_conf_t *cf);
static ngx_chain_t *ngx_http_chunked_create_trailers(ngx_http_request_t *r,
ngx_http_chunked_filter_ctx_t *ctx);
static ngx_http_module_t ngx_http_chunked_filter_module_ctx = {
NULL, /* preconfiguration */
ngx_http_chunked_filter_init, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
NULL, /* create location configuration */
NULL /* merge location configuration */
};
ngx_module_t ngx_http_chunked_filter_module = {
NGX_MODULE_V1,
&ngx_http_chunked_filter_module_ctx, /* module context */
NULL, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static ngx_http_output_header_filter_pt ngx_http_next_header_filter;
static ngx_http_output_body_filter_pt ngx_http_next_body_filter;
static ngx_int_t
ngx_http_chunked_header_filter(ngx_http_request_t *r)
{
ngx_http_core_loc_conf_t *clcf;
ngx_http_chunked_filter_ctx_t *ctx;
if (r->headers_out.status == NGX_HTTP_NOT_MODIFIED
|| r->headers_out.status == NGX_HTTP_NO_CONTENT
|| r->headers_out.status < NGX_HTTP_OK
|| r != r->main
|| r->method == NGX_HTTP_HEAD)
{
return ngx_http_next_header_filter(r);
}
if (r->headers_out.content_length_n == -1
|| r->expect_trailers)
{
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
if (r->http_version >= NGX_HTTP_VERSION_11
&& clcf->chunked_transfer_encoding)
{
if (r->expect_trailers) {
ngx_http_clear_content_length(r);
}
r->chunked = 1;
ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_chunked_filter_ctx_t));
if (ctx == NULL) {
return NGX_ERROR;
}
ngx_http_set_ctx(r, ctx, ngx_http_chunked_filter_module);
} else if (r->headers_out.content_length_n == -1) {
r->keepalive = 0;
}
}
return ngx_http_next_header_filter(r);
}
static ngx_int_t
ngx_http_chunked_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
u_char *chunk;
off_t size;
ngx_int_t rc;
ngx_buf_t *b;
ngx_chain_t *out, *cl, *tl, **ll;
ngx_http_chunked_filter_ctx_t *ctx;
if (in == NULL || !r->chunked || r->header_only) {
return ngx_http_next_body_filter(r, in);
}
ctx = ngx_http_get_module_ctx(r, ngx_http_chunked_filter_module);
out = NULL;
ll = &out;
size = 0;
cl = in;
for ( ;; ) {
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http chunk: %O", ngx_buf_size(cl->buf));
size += ngx_buf_size(cl->buf);
if (cl->buf->flush
|| cl->buf->sync
|| ngx_buf_in_memory(cl->buf)
|| cl->buf->in_file)
{
tl = ngx_alloc_chain_link(r->pool);
if (tl == NULL) {
return NGX_ERROR;
}
tl->buf = cl->buf;
*ll = tl;
ll = &tl->next;
}
if (cl->next == NULL) {
break;
}
cl = cl->next;
}
if (size) {
tl = ngx_chain_get_free_buf(r->pool, &ctx->free);
if (tl == NULL) {
return NGX_ERROR;
}
b = tl->buf;
chunk = b->start;
if (chunk == NULL) {
/* the "0000000000000000" is 64-bit hexadecimal string */
chunk = ngx_palloc(r->pool, sizeof("0000000000000000" CRLF) - 1);
if (chunk == NULL) {
return NGX_ERROR;
}
b->start = chunk;
b->end = chunk + sizeof("0000000000000000" CRLF) - 1;
}
b->tag = (ngx_buf_tag_t) &ngx_http_chunked_filter_module;
b->memory = 0;
b->temporary = 1;
b->pos = chunk;
b->last = ngx_sprintf(chunk, "%xO" CRLF, size);
tl->next = out;
out = tl;
}
if (cl->buf->last_buf) {
tl = ngx_http_chunked_create_trailers(r, ctx);
if (tl == NULL) {
return NGX_ERROR;
}
cl->buf->last_buf = 0;
*ll = tl;
if (size == 0) {
tl->buf->pos += 2;
}
} else if (size > 0) {
tl = ngx_chain_get_free_buf(r->pool, &ctx->free);
if (tl == NULL) {
return NGX_ERROR;
}
b = tl->buf;
b->tag = (ngx_buf_tag_t) &ngx_http_chunked_filter_module;
b->temporary = 0;
b->memory = 1;
b->pos = (u_char *) CRLF;
b->last = b->pos + 2;
*ll = tl;
} else {
*ll = NULL;
}
rc = ngx_http_next_body_filter(r, out);
ngx_chain_update_chains(r->pool, &ctx->free, &ctx->busy, &out,
(ngx_buf_tag_t) &ngx_http_chunked_filter_module);
return rc;
}
static ngx_chain_t *
ngx_http_chunked_create_trailers(ngx_http_request_t *r,
ngx_http_chunked_filter_ctx_t *ctx)
{
size_t len;
ngx_buf_t *b;
ngx_uint_t i;
ngx_chain_t *cl;
ngx_list_part_t *part;
ngx_table_elt_t *header;
len = 0;
part = &r->headers_out.trailers.part;
header = part->elts;
for (i = 0; /* void */; i++) {
if (i >= part->nelts) {
if (part->next == NULL) {
break;
}
part = part->next;
header = part->elts;
i = 0;
}
if (header[i].hash == 0) {
continue;
}
len += header[i].key.len + sizeof(": ") - 1
+ header[i].value.len + sizeof(CRLF) - 1;
}
cl = ngx_chain_get_free_buf(r->pool, &ctx->free);
if (cl == NULL) {
return NULL;
}
b = cl->buf;
b->tag = (ngx_buf_tag_t) &ngx_http_chunked_filter_module;
b->temporary = 0;
b->memory = 1;
b->last_buf = 1;
if (len == 0) {
b->pos = (u_char *) CRLF "0" CRLF CRLF;
b->last = b->pos + sizeof(CRLF "0" CRLF CRLF) - 1;
return cl;
}
len += sizeof(CRLF "0" CRLF CRLF) - 1;
b->pos = ngx_palloc(r->pool, len);
if (b->pos == NULL) {
return NULL;
}
b->last = b->pos;
*b->last++ = CR; *b->last++ = LF;
*b->last++ = '0';
*b->last++ = CR; *b->last++ = LF;
part = &r->headers_out.trailers.part;
header = part->elts;
for (i = 0; /* void */; i++) {
if (i >= part->nelts) {
if (part->next == NULL) {
break;
}
part = part->next;
header = part->elts;
i = 0;
}
if (header[i].hash == 0) {
continue;
}
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http trailer: \"%V: %V\"",
&header[i].key, &header[i].value);
b->last = ngx_copy(b->last, header[i].key.data, header[i].key.len);
*b->last++ = ':'; *b->last++ = ' ';
b->last = ngx_copy(b->last, header[i].value.data, header[i].value.len);
*b->last++ = CR; *b->last++ = LF;
}
*b->last++ = CR; *b->last++ = LF;
return cl;
}
static ngx_int_t
ngx_http_chunked_filter_init(ngx_conf_t *cf)
{
ngx_http_next_header_filter = ngx_http_top_header_filter;
ngx_http_top_header_filter = ngx_http_chunked_header_filter;
ngx_http_next_body_filter = ngx_http_top_body_filter;
ngx_http_top_body_filter = ngx_http_chunked_body_filter;
return NGX_OK;
}
nginx-1.14.0/src/http/modules/ngx_http_dav_module.c 000644 001751 001751 00000076722 13265410474 023546 0 ustar 00mdounin mdounin 000000 000000
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#include
#include
#include
#define NGX_HTTP_DAV_OFF 2
#define NGX_HTTP_DAV_NO_DEPTH -3
#define NGX_HTTP_DAV_INVALID_DEPTH -2
#define NGX_HTTP_DAV_INFINITY_DEPTH -1
typedef struct {
ngx_uint_t methods;
ngx_uint_t access;
ngx_uint_t min_delete_depth;
ngx_flag_t create_full_put_path;
} ngx_http_dav_loc_conf_t;
typedef struct {
ngx_str_t path;
size_t len;
} ngx_http_dav_copy_ctx_t;
static ngx_int_t ngx_http_dav_handler(ngx_http_request_t *r);
static void ngx_http_dav_put_handler(ngx_http_request_t *r);
static ngx_int_t ngx_http_dav_delete_handler(ngx_http_request_t *r);
static ngx_int_t ngx_http_dav_delete_path(ngx_http_request_t *r,
ngx_str_t *path, ngx_uint_t dir);
static ngx_int_t ngx_http_dav_delete_dir(ngx_tree_ctx_t *ctx, ngx_str_t *path);
static ngx_int_t ngx_http_dav_delete_file(ngx_tree_ctx_t *ctx, ngx_str_t *path);
static ngx_int_t ngx_http_dav_noop(ngx_tree_ctx_t *ctx, ngx_str_t *path);
static ngx_int_t ngx_http_dav_mkcol_handler(ngx_http_request_t *r,
ngx_http_dav_loc_conf_t *dlcf);
static ngx_int_t ngx_http_dav_copy_move_handler(ngx_http_request_t *r);
static ngx_int_t ngx_http_dav_copy_dir(ngx_tree_ctx_t *ctx, ngx_str_t *path);
static ngx_int_t ngx_http_dav_copy_dir_time(ngx_tree_ctx_t *ctx,
ngx_str_t *path);
static ngx_int_t ngx_http_dav_copy_tree_file(ngx_tree_ctx_t *ctx,
ngx_str_t *path);
static ngx_int_t ngx_http_dav_depth(ngx_http_request_t *r, ngx_int_t dflt);
static ngx_int_t ngx_http_dav_error(ngx_log_t *log, ngx_err_t err,
ngx_int_t not_found, char *failed, u_char *path);
static ngx_int_t ngx_http_dav_location(ngx_http_request_t *r, u_char *path);
static void *ngx_http_dav_create_loc_conf(ngx_conf_t *cf);
static char *ngx_http_dav_merge_loc_conf(ngx_conf_t *cf,
void *parent, void *child);
static ngx_int_t ngx_http_dav_init(ngx_conf_t *cf);
static ngx_conf_bitmask_t ngx_http_dav_methods_mask[] = {
{ ngx_string("off"), NGX_HTTP_DAV_OFF },
{ ngx_string("put"), NGX_HTTP_PUT },
{ ngx_string("delete"), NGX_HTTP_DELETE },
{ ngx_string("mkcol"), NGX_HTTP_MKCOL },
{ ngx_string("copy"), NGX_HTTP_COPY },
{ ngx_string("move"), NGX_HTTP_MOVE },
{ ngx_null_string, 0 }
};
static ngx_command_t ngx_http_dav_commands[] = {
{ ngx_string("dav_methods"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
ngx_conf_set_bitmask_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_dav_loc_conf_t, methods),
&ngx_http_dav_methods_mask },
{ ngx_string("create_full_put_path"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_dav_loc_conf_t, create_full_put_path),
NULL },
{ ngx_string("min_delete_depth"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_num_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_dav_loc_conf_t, min_delete_depth),
NULL },
{ ngx_string("dav_access"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE123,
ngx_conf_set_access_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_dav_loc_conf_t, access),
NULL },
ngx_null_command
};
static ngx_http_module_t ngx_http_dav_module_ctx = {
NULL, /* preconfiguration */
ngx_http_dav_init, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
ngx_http_dav_create_loc_conf, /* create location configuration */
ngx_http_dav_merge_loc_conf /* merge location configuration */
};
ngx_module_t ngx_http_dav_module = {
NGX_MODULE_V1,
&ngx_http_dav_module_ctx, /* module context */
ngx_http_dav_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static ngx_int_t
ngx_http_dav_handler(ngx_http_request_t *r)
{
ngx_int_t rc;
ngx_http_dav_loc_conf_t *dlcf;
dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_module);
if (!(r->method & dlcf->methods)) {
return NGX_DECLINED;
}
switch (r->method) {
case NGX_HTTP_PUT:
if (r->uri.data[r->uri.len - 1] == '/') {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"cannot PUT to a collection");
return NGX_HTTP_CONFLICT;
}
if (r->headers_in.content_range) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"PUT with range is unsupported");
return NGX_HTTP_NOT_IMPLEMENTED;
}
r->request_body_in_file_only = 1;
r->request_body_in_persistent_file = 1;
r->request_body_in_clean_file = 1;
r->request_body_file_group_access = 1;
r->request_body_file_log_level = 0;
rc = ngx_http_read_client_request_body(r, ngx_http_dav_put_handler);
if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
return rc;
}
return NGX_DONE;
case NGX_HTTP_DELETE:
return ngx_http_dav_delete_handler(r);
case NGX_HTTP_MKCOL:
return ngx_http_dav_mkcol_handler(r, dlcf);
case NGX_HTTP_COPY:
return ngx_http_dav_copy_move_handler(r);
case NGX_HTTP_MOVE:
return ngx_http_dav_copy_move_handler(r);
}
return NGX_DECLINED;
}
static void
ngx_http_dav_put_handler(ngx_http_request_t *r)
{
size_t root;
time_t date;
ngx_str_t *temp, path;
ngx_uint_t status;
ngx_file_info_t fi;
ngx_ext_rename_file_t ext;
ngx_http_dav_loc_conf_t *dlcf;
if (r->request_body == NULL) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"PUT request body is unavailable");
ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
}
if (r->request_body->temp_file == NULL) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"PUT request body must be in a file");
ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
}
if (ngx_http_map_uri_to_path(r, &path, &root, 0) == NULL) {
ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
}
path.len--;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http put filename: \"%s\"", path.data);
temp = &r->request_body->temp_file->file.name;
if (ngx_file_info(path.data, &fi) == NGX_FILE_ERROR) {
status = NGX_HTTP_CREATED;
} else {
status = NGX_HTTP_NO_CONTENT;
if (ngx_is_dir(&fi)) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, NGX_EISDIR,
"\"%s\" could not be created", path.data);
if (ngx_delete_file(temp->data) == NGX_FILE_ERROR) {
ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,
ngx_delete_file_n " \"%s\" failed",
temp->data);
}
ngx_http_finalize_request(r, NGX_HTTP_CONFLICT);
return;
}
}
dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_module);
ext.access = dlcf->access;
ext.path_access = dlcf->access;
ext.time = -1;
ext.create_path = dlcf->create_full_put_path;
ext.delete_file = 1;
ext.log = r->connection->log;
if (r->headers_in.date) {
date = ngx_parse_http_time(r->headers_in.date->value.data,
r->headers_in.date->value.len);
if (date != NGX_ERROR) {
ext.time = date;
ext.fd = r->request_body->temp_file->file.fd;
}
}
if (ngx_ext_rename_file(temp, &path, &ext) != NGX_OK) {
ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
}
if (status == NGX_HTTP_CREATED) {
if (ngx_http_dav_location(r, path.data) != NGX_OK) {
ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
}
r->headers_out.content_length_n = 0;
}
r->headers_out.status = status;
r->header_only = 1;
ngx_http_finalize_request(r, ngx_http_send_header(r));
return;
}
static ngx_int_t
ngx_http_dav_delete_handler(ngx_http_request_t *r)
{
size_t root;
ngx_err_t err;
ngx_int_t rc, depth;
ngx_uint_t i, d, dir;
ngx_str_t path;
ngx_file_info_t fi;
ngx_http_dav_loc_conf_t *dlcf;
if (r->headers_in.content_length_n > 0) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"DELETE with body is unsupported");
return NGX_HTTP_UNSUPPORTED_MEDIA_TYPE;
}
dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_module);
if (dlcf->min_delete_depth) {
d = 0;
for (i = 0; i < r->uri.len; /* void */) {
if (r->uri.data[i++] == '/') {
if (++d >= dlcf->min_delete_depth && i < r->uri.len) {
goto ok;
}
}
}
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"insufficient URI depth:%i to DELETE", d);
return NGX_HTTP_CONFLICT;
}
ok:
if (ngx_http_map_uri_to_path(r, &path, &root, 0) == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http delete filename: \"%s\"", path.data);
if (ngx_link_info(path.data, &fi) == NGX_FILE_ERROR) {
err = ngx_errno;
rc = (err == NGX_ENOTDIR) ? NGX_HTTP_CONFLICT : NGX_HTTP_NOT_FOUND;
return ngx_http_dav_error(r->connection->log, err,
rc, ngx_link_info_n, path.data);
}
if (ngx_is_dir(&fi)) {
if (r->uri.data[r->uri.len - 1] != '/') {
ngx_log_error(NGX_LOG_ERR, r->connection->log, NGX_EISDIR,
"DELETE \"%s\" failed", path.data);
return NGX_HTTP_CONFLICT;
}
depth = ngx_http_dav_depth(r, NGX_HTTP_DAV_INFINITY_DEPTH);
if (depth != NGX_HTTP_DAV_INFINITY_DEPTH) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"\"Depth\" header must be infinity");
return NGX_HTTP_BAD_REQUEST;
}
path.len -= 2; /* omit "/\0" */
dir = 1;
} else {
/*
* we do not need to test (r->uri.data[r->uri.len - 1] == '/')
* because ngx_link_info("/file/") returned NGX_ENOTDIR above
*/
depth = ngx_http_dav_depth(r, 0);
if (depth != 0 && depth != NGX_HTTP_DAV_INFINITY_DEPTH) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"\"Depth\" header must be 0 or infinity");
return NGX_HTTP_BAD_REQUEST;
}
dir = 0;
}
rc = ngx_http_dav_delete_path(r, &path, dir);
if (rc == NGX_OK) {
return NGX_HTTP_NO_CONTENT;
}
return rc;
}
static ngx_int_t
ngx_http_dav_delete_path(ngx_http_request_t *r, ngx_str_t *path, ngx_uint_t dir)
{
char *failed;
ngx_tree_ctx_t tree;
if (dir) {
tree.init_handler = NULL;
tree.file_handler = ngx_http_dav_delete_file;
tree.pre_tree_handler = ngx_http_dav_noop;
tree.post_tree_handler = ngx_http_dav_delete_dir;
tree.spec_handler = ngx_http_dav_delete_file;
tree.data = NULL;
tree.alloc = 0;
tree.log = r->connection->log;
/* TODO: 207 */
if (ngx_walk_tree(&tree, path) != NGX_OK) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
if (ngx_delete_dir(path->data) != NGX_FILE_ERROR) {
return NGX_OK;
}
failed = ngx_delete_dir_n;
} else {
if (ngx_delete_file(path->data) != NGX_FILE_ERROR) {
return NGX_OK;
}
failed = ngx_delete_file_n;
}
return ngx_http_dav_error(r->connection->log, ngx_errno,
NGX_HTTP_NOT_FOUND, failed, path->data);
}
static ngx_int_t
ngx_http_dav_delete_dir(ngx_tree_ctx_t *ctx, ngx_str_t *path)
{
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
"http delete dir: \"%s\"", path->data);
if (ngx_delete_dir(path->data) == NGX_FILE_ERROR) {
/* TODO: add to 207 */
(void) ngx_http_dav_error(ctx->log, ngx_errno, 0, ngx_delete_dir_n,
path->data);
}
return NGX_OK;
}
static ngx_int_t
ngx_http_dav_delete_file(ngx_tree_ctx_t *ctx, ngx_str_t *path)
{
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
"http delete file: \"%s\"", path->data);
if (ngx_delete_file(path->data) == NGX_FILE_ERROR) {
/* TODO: add to 207 */
(void) ngx_http_dav_error(ctx->log, ngx_errno, 0, ngx_delete_file_n,
path->data);
}
return NGX_OK;
}
static ngx_int_t
ngx_http_dav_noop(ngx_tree_ctx_t *ctx, ngx_str_t *path)
{
return NGX_OK;
}
static ngx_int_t
ngx_http_dav_mkcol_handler(ngx_http_request_t *r, ngx_http_dav_loc_conf_t *dlcf)
{
u_char *p;
size_t root;
ngx_str_t path;
if (r->headers_in.content_length_n > 0) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"MKCOL with body is unsupported");
return NGX_HTTP_UNSUPPORTED_MEDIA_TYPE;
}
if (r->uri.data[r->uri.len - 1] != '/') {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"MKCOL can create a collection only");
return NGX_HTTP_CONFLICT;
}
p = ngx_http_map_uri_to_path(r, &path, &root, 0);
if (p == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
*(p - 1) = '\0';
r->uri.len--;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http mkcol path: \"%s\"", path.data);
if (ngx_create_dir(path.data, ngx_dir_access(dlcf->access))
!= NGX_FILE_ERROR)
{
if (ngx_http_dav_location(r, path.data) != NGX_OK) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
return NGX_HTTP_CREATED;
}
return ngx_http_dav_error(r->connection->log, ngx_errno,
NGX_HTTP_CONFLICT, ngx_create_dir_n, path.data);
}
static ngx_int_t
ngx_http_dav_copy_move_handler(ngx_http_request_t *r)
{
u_char *p, *host, *last, ch;
size_t len, root;
ngx_err_t err;
ngx_int_t rc, depth;
ngx_uint_t overwrite, slash, dir, flags;
ngx_str_t path, uri, duri, args;
ngx_tree_ctx_t tree;
ngx_copy_file_t cf;
ngx_file_info_t fi;
ngx_table_elt_t *dest, *over;
ngx_ext_rename_file_t ext;
ngx_http_dav_copy_ctx_t copy;
ngx_http_dav_loc_conf_t *dlcf;
if (r->headers_in.content_length_n > 0) {
return NGX_HTTP_UNSUPPORTED_MEDIA_TYPE;
}
dest = r->headers_in.destination;
if (dest == NULL) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"client sent no \"Destination\" header");
return NGX_HTTP_BAD_REQUEST;
}
p = dest->value.data;
/* there is always '\0' even after empty header value */
if (p[0] == '/') {
last = p + dest->value.len;
goto destination_done;
}
len = r->headers_in.server.len;
if (len == 0) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"client sent no \"Host\" header");
return NGX_HTTP_BAD_REQUEST;
}
#if (NGX_HTTP_SSL)
if (r->connection->ssl) {
if (ngx_strncmp(dest->value.data, "https://", sizeof("https://") - 1)
!= 0)
{
goto invalid_destination;
}
host = dest->value.data + sizeof("https://") - 1;
} else
#endif
{
if (ngx_strncmp(dest->value.data, "http://", sizeof("http://") - 1)
!= 0)
{
goto invalid_destination;
}
host = dest->value.data + sizeof("http://") - 1;
}
if (ngx_strncmp(host, r->headers_in.server.data, len) != 0) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"\"Destination\" URI \"%V\" is handled by "
"different repository than the source URI",
&dest->value);
return NGX_HTTP_BAD_REQUEST;
}
last = dest->value.data + dest->value.len;
for (p = host + len; p < last; p++) {
if (*p == '/') {
goto destination_done;
}
}
invalid_destination:
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"client sent invalid \"Destination\" header: \"%V\"",
&dest->value);
return NGX_HTTP_BAD_REQUEST;
destination_done:
duri.len = last - p;
duri.data = p;
flags = NGX_HTTP_LOG_UNSAFE;
if (ngx_http_parse_unsafe_uri(r, &duri, &args, &flags) != NGX_OK) {
goto invalid_destination;
}
if ((r->uri.data[r->uri.len - 1] == '/' && *(last - 1) != '/')
|| (r->uri.data[r->uri.len - 1] != '/' && *(last - 1) == '/'))
{
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"both URI \"%V\" and \"Destination\" URI \"%V\" "
"should be either collections or non-collections",
&r->uri, &dest->value);
return NGX_HTTP_CONFLICT;
}
depth = ngx_http_dav_depth(r, NGX_HTTP_DAV_INFINITY_DEPTH);
if (depth != NGX_HTTP_DAV_INFINITY_DEPTH) {
if (r->method == NGX_HTTP_COPY) {
if (depth != 0) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"\"Depth\" header must be 0 or infinity");
return NGX_HTTP_BAD_REQUEST;
}
} else {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"\"Depth\" header must be infinity");
return NGX_HTTP_BAD_REQUEST;
}
}
over = r->headers_in.overwrite;
if (over) {
if (over->value.len == 1) {
ch = over->value.data[0];
if (ch == 'T' || ch == 't') {
overwrite = 1;
goto overwrite_done;
}
if (ch == 'F' || ch == 'f') {
overwrite = 0;
goto overwrite_done;
}
}
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"client sent invalid \"Overwrite\" header: \"%V\"",
&over->value);
return NGX_HTTP_BAD_REQUEST;
}
overwrite = 1;
overwrite_done:
if (ngx_http_map_uri_to_path(r, &path, &root, 0) == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http copy from: \"%s\"", path.data);
uri = r->uri;
r->uri = duri;
if (ngx_http_map_uri_to_path(r, ©.path, &root, 0) == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
r->uri = uri;
copy.path.len--; /* omit "\0" */
if (copy.path.data[copy.path.len - 1] == '/') {
slash = 1;
copy.path.len--;
copy.path.data[copy.path.len] = '\0';
} else {
slash = 0;
}
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http copy to: \"%s\"", copy.path.data);
if (ngx_link_info(copy.path.data, &fi) == NGX_FILE_ERROR) {
err = ngx_errno;
if (err != NGX_ENOENT) {
return ngx_http_dav_error(r->connection->log, err,
NGX_HTTP_NOT_FOUND, ngx_link_info_n,
copy.path.data);
}
/* destination does not exist */
overwrite = 0;
dir = 0;
} else {
/* destination exists */
if (ngx_is_dir(&fi) && !slash) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"\"%V\" could not be %Ved to collection \"%V\"",
&r->uri, &r->method_name, &dest->value);
return NGX_HTTP_CONFLICT;
}
if (!overwrite) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, NGX_EEXIST,
"\"%s\" could not be created", copy.path.data);
return NGX_HTTP_PRECONDITION_FAILED;
}
dir = ngx_is_dir(&fi);
}
if (ngx_link_info(path.data, &fi) == NGX_FILE_ERROR) {
return ngx_http_dav_error(r->connection->log, ngx_errno,
NGX_HTTP_NOT_FOUND, ngx_link_info_n,
path.data);
}
if (ngx_is_dir(&fi)) {
if (r->uri.data[r->uri.len - 1] != '/') {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"\"%V\" is collection", &r->uri);
return NGX_HTTP_BAD_REQUEST;
}
if (overwrite) {
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http delete: \"%s\"", copy.path.data);
rc = ngx_http_dav_delete_path(r, ©.path, dir);
if (rc != NGX_OK) {
return rc;
}
}
}
if (ngx_is_dir(&fi)) {
path.len -= 2; /* omit "/\0" */
if (r->method == NGX_HTTP_MOVE) {
if (ngx_rename_file(path.data, copy.path.data) != NGX_FILE_ERROR) {
return NGX_HTTP_CREATED;
}
}
if (ngx_create_dir(copy.path.data, ngx_file_access(&fi))
== NGX_FILE_ERROR)
{
return ngx_http_dav_error(r->connection->log, ngx_errno,
NGX_HTTP_NOT_FOUND,
ngx_create_dir_n, copy.path.data);
}
copy.len = path.len;
tree.init_handler = NULL;
tree.file_handler = ngx_http_dav_copy_tree_file;
tree.pre_tree_handler = ngx_http_dav_copy_dir;
tree.post_tree_handler = ngx_http_dav_copy_dir_time;
tree.spec_handler = ngx_http_dav_noop;
tree.data = ©
tree.alloc = 0;
tree.log = r->connection->log;
if (ngx_walk_tree(&tree, &path) == NGX_OK) {
if (r->method == NGX_HTTP_MOVE) {
rc = ngx_http_dav_delete_path(r, &path, 1);
if (rc != NGX_OK) {
return rc;
}
}
return NGX_HTTP_CREATED;
}
} else {
if (r->method == NGX_HTTP_MOVE) {
dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_module);
ext.access = 0;
ext.path_access = dlcf->access;
ext.time = -1;
ext.create_path = 1;
ext.delete_file = 0;
ext.log = r->connection->log;
if (ngx_ext_rename_file(&path, ©.path, &ext) == NGX_OK) {
return NGX_HTTP_NO_CONTENT;
}
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_module);
cf.size = ngx_file_size(&fi);
cf.buf_size = 0;
cf.access = dlcf->access;
cf.time = ngx_file_mtime(&fi);
cf.log = r->connection->log;
if (ngx_copy_file(path.data, copy.path.data, &cf) == NGX_OK) {
return NGX_HTTP_NO_CONTENT;
}
}
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
static ngx_int_t
ngx_http_dav_copy_dir(ngx_tree_ctx_t *ctx, ngx_str_t *path)
{
u_char *p, *dir;
size_t len;
ngx_http_dav_copy_ctx_t *copy;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
"http copy dir: \"%s\"", path->data);
copy = ctx->data;
len = copy->path.len + path->len;
dir = ngx_alloc(len + 1, ctx->log);
if (dir == NULL) {
return NGX_ABORT;
}
p = ngx_cpymem(dir, copy->path.data, copy->path.len);
(void) ngx_cpystrn(p, path->data + copy->len, path->len - copy->len + 1);
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
"http copy dir to: \"%s\"", dir);
if (ngx_create_dir(dir, ngx_dir_access(ctx->access)) == NGX_FILE_ERROR) {
(void) ngx_http_dav_error(ctx->log, ngx_errno, 0, ngx_create_dir_n,
dir);
}
ngx_free(dir);
return NGX_OK;
}
static ngx_int_t
ngx_http_dav_copy_dir_time(ngx_tree_ctx_t *ctx, ngx_str_t *path)
{
u_char *p, *dir;
size_t len;
ngx_http_dav_copy_ctx_t *copy;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
"http copy dir time: \"%s\"", path->data);
copy = ctx->data;
len = copy->path.len + path->len;
dir = ngx_alloc(len + 1, ctx->log);
if (dir == NULL) {
return NGX_ABORT;
}
p = ngx_cpymem(dir, copy->path.data, copy->path.len);
(void) ngx_cpystrn(p, path->data + copy->len, path->len - copy->len + 1);
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
"http copy dir time to: \"%s\"", dir);
#if (NGX_WIN32)
{
ngx_fd_t fd;
fd = ngx_open_file(dir, NGX_FILE_RDWR, NGX_FILE_OPEN, 0);
if (fd == NGX_INVALID_FILE) {
(void) ngx_http_dav_error(ctx->log, ngx_errno, 0, ngx_open_file_n, dir);
goto failed;
}
if (ngx_set_file_time(NULL, fd, ctx->mtime) != NGX_OK) {
ngx_log_error(NGX_LOG_ALERT, ctx->log, ngx_errno,
ngx_set_file_time_n " \"%s\" failed", dir);
}
if (ngx_close_file(fd) == NGX_FILE_ERROR) {
ngx_log_error(NGX_LOG_ALERT, ctx->log, ngx_errno,
ngx_close_file_n " \"%s\" failed", dir);
}
}
failed:
#else
if (ngx_set_file_time(dir, 0, ctx->mtime) != NGX_OK) {
ngx_log_error(NGX_LOG_ALERT, ctx->log, ngx_errno,
ngx_set_file_time_n " \"%s\" failed", dir);
}
#endif
ngx_free(dir);
return NGX_OK;
}
static ngx_int_t
ngx_http_dav_copy_tree_file(ngx_tree_ctx_t *ctx, ngx_str_t *path)
{
u_char *p, *file;
size_t len;
ngx_copy_file_t cf;
ngx_http_dav_copy_ctx_t *copy;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
"http copy file: \"%s\"", path->data);
copy = ctx->data;
len = copy->path.len + path->len;
file = ngx_alloc(len + 1, ctx->log);
if (file == NULL) {
return NGX_ABORT;
}
p = ngx_cpymem(file, copy->path.data, copy->path.len);
(void) ngx_cpystrn(p, path->data + copy->len, path->len - copy->len + 1);
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
"http copy file to: \"%s\"", file);
cf.size = ctx->size;
cf.buf_size = 0;
cf.access = ctx->access;
cf.time = ctx->mtime;
cf.log = ctx->log;
(void) ngx_copy_file(path->data, file, &cf);
ngx_free(file);
return NGX_OK;
}
static ngx_int_t
ngx_http_dav_depth(ngx_http_request_t *r, ngx_int_t dflt)
{
ngx_table_elt_t *depth;
depth = r->headers_in.depth;
if (depth == NULL) {
return dflt;
}
if (depth->value.len == 1) {
if (depth->value.data[0] == '0') {
return 0;
}
if (depth->value.data[0] == '1') {
return 1;
}
} else {
if (depth->value.len == sizeof("infinity") - 1
&& ngx_strcmp(depth->value.data, "infinity") == 0)
{
return NGX_HTTP_DAV_INFINITY_DEPTH;
}
}
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"client sent invalid \"Depth\" header: \"%V\"",
&depth->value);
return NGX_HTTP_DAV_INVALID_DEPTH;
}
static ngx_int_t
ngx_http_dav_error(ngx_log_t *log, ngx_err_t err, ngx_int_t not_found,
char *failed, u_char *path)
{
ngx_int_t rc;
ngx_uint_t level;
if (err == NGX_ENOENT || err == NGX_ENOTDIR || err == NGX_ENAMETOOLONG) {
level = NGX_LOG_ERR;
rc = not_found;
} else if (err == NGX_EACCES || err == NGX_EPERM) {
level = NGX_LOG_ERR;
rc = NGX_HTTP_FORBIDDEN;
} else if (err == NGX_EEXIST) {
level = NGX_LOG_ERR;
rc = NGX_HTTP_NOT_ALLOWED;
} else if (err == NGX_ENOSPC) {
level = NGX_LOG_CRIT;
rc = NGX_HTTP_INSUFFICIENT_STORAGE;
} else {
level = NGX_LOG_CRIT;
rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
}
ngx_log_error(level, log, err, "%s \"%s\" failed", failed, path);
return rc;
}
static ngx_int_t
ngx_http_dav_location(ngx_http_request_t *r, u_char *path)
{
u_char *location;
ngx_http_core_loc_conf_t *clcf;
r->headers_out.location = ngx_list_push(&r->headers_out.headers);
if (r->headers_out.location == NULL) {
return NGX_ERROR;
}
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
if (!clcf->alias && clcf->root_lengths == NULL) {
location = path + clcf->root.len;
} else {
location = ngx_pnalloc(r->pool, r->uri.len);
if (location == NULL) {
ngx_http_clear_location(r);
return NGX_ERROR;
}
ngx_memcpy(location, r->uri.data, r->uri.len);
}
r->headers_out.location->hash = 1;
ngx_str_set(&r->headers_out.location->key, "Location");
r->headers_out.location->value.len = r->uri.len;
r->headers_out.location->value.data = location;
return NGX_OK;
}
static void *
ngx_http_dav_create_loc_conf(ngx_conf_t *cf)
{
ngx_http_dav_loc_conf_t *conf;
conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_dav_loc_conf_t));
if (conf == NULL) {
return NULL;
}
/*
* set by ngx_pcalloc():
*
* conf->methods = 0;
*/
conf->min_delete_depth = NGX_CONF_UNSET_UINT;
conf->access = NGX_CONF_UNSET_UINT;
conf->create_full_put_path = NGX_CONF_UNSET;
return conf;
}
static char *
ngx_http_dav_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
{
ngx_http_dav_loc_conf_t *prev = parent;
ngx_http_dav_loc_conf_t *conf = child;
ngx_conf_merge_bitmask_value(conf->methods, prev->methods,
(NGX_CONF_BITMASK_SET|NGX_HTTP_DAV_OFF));
ngx_conf_merge_uint_value(conf->min_delete_depth,
prev->min_delete_depth, 0);
ngx_conf_merge_uint_value(conf->access, prev->access, 0600);
ngx_conf_merge_value(conf->create_full_put_path,
prev->create_full_put_path, 0);
return NGX_CONF_OK;
}
static ngx_int_t
ngx_http_dav_init(ngx_conf_t *cf)
{
ngx_http_handler_pt *h;
ngx_http_core_main_conf_t *cmcf;
cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers);
if (h == NULL) {
return NGX_ERROR;
}
*h = ngx_http_dav_handler;
return NGX_OK;
}
nginx-1.14.0/src/http/modules/ngx_http_degradation_module.c 000644 001751 001751 00000014140 13265410474 025237 0 ustar 00mdounin mdounin 000000 000000
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#include
#include
#include
typedef struct {
size_t sbrk_size;
} ngx_http_degradation_main_conf_t;
typedef struct {
ngx_uint_t degrade;
} ngx_http_degradation_loc_conf_t;
static ngx_conf_enum_t ngx_http_degrade[] = {
{ ngx_string("204"), 204 },
{ ngx_string("444"), 444 },
{ ngx_null_string, 0 }
};
static void *ngx_http_degradation_create_main_conf(ngx_conf_t *cf);
static void *ngx_http_degradation_create_loc_conf(ngx_conf_t *cf);
static char *ngx_http_degradation_merge_loc_conf(ngx_conf_t *cf, void *parent,
void *child);
static char *ngx_http_degradation(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static ngx_int_t ngx_http_degradation_init(ngx_conf_t *cf);
static ngx_command_t ngx_http_degradation_commands[] = {
{ ngx_string("degradation"),
NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,
ngx_http_degradation,
NGX_HTTP_MAIN_CONF_OFFSET,
0,
NULL },
{ ngx_string("degrade"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_enum_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_degradation_loc_conf_t, degrade),
&ngx_http_degrade },
ngx_null_command
};
static ngx_http_module_t ngx_http_degradation_module_ctx = {
NULL, /* preconfiguration */
ngx_http_degradation_init, /* postconfiguration */
ngx_http_degradation_create_main_conf, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
ngx_http_degradation_create_loc_conf, /* create location configuration */
ngx_http_degradation_merge_loc_conf /* merge location configuration */
};
ngx_module_t ngx_http_degradation_module = {
NGX_MODULE_V1,
&ngx_http_degradation_module_ctx, /* module context */
ngx_http_degradation_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static ngx_int_t
ngx_http_degradation_handler(ngx_http_request_t *r)
{
ngx_http_degradation_loc_conf_t *dlcf;
dlcf = ngx_http_get_module_loc_conf(r, ngx_http_degradation_module);
if (dlcf->degrade && ngx_http_degraded(r)) {
return dlcf->degrade;
}
return NGX_DECLINED;
}
ngx_uint_t
ngx_http_degraded(ngx_http_request_t *r)
{
time_t now;
ngx_uint_t log;
static size_t sbrk_size;
static time_t sbrk_time;
ngx_http_degradation_main_conf_t *dmcf;
dmcf = ngx_http_get_module_main_conf(r, ngx_http_degradation_module);
if (dmcf->sbrk_size) {
log = 0;
now = ngx_time();
/* lock mutex */
if (now != sbrk_time) {
/*
* ELF/i386 is loaded at 0x08000000, 128M
* ELF/amd64 is loaded at 0x00400000, 4M
*
* use a function address to subtract the loading address
*/
sbrk_size = (size_t) sbrk(0) - ((uintptr_t) ngx_palloc & ~0x3FFFFF);
sbrk_time = now;
log = 1;
}
/* unlock mutex */
if (sbrk_size >= dmcf->sbrk_size) {
if (log) {
ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0,
"degradation sbrk:%uzM",
sbrk_size / (1024 * 1024));
}
return 1;
}
}
return 0;
}
static void *
ngx_http_degradation_create_main_conf(ngx_conf_t *cf)
{
ngx_http_degradation_main_conf_t *dmcf;
dmcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_degradation_main_conf_t));
if (dmcf == NULL) {
return NULL;
}
return dmcf;
}
static void *
ngx_http_degradation_create_loc_conf(ngx_conf_t *cf)
{
ngx_http_degradation_loc_conf_t *conf;
conf = ngx_palloc(cf->pool, sizeof(ngx_http_degradation_loc_conf_t));
if (conf == NULL) {
return NULL;
}
conf->degrade = NGX_CONF_UNSET_UINT;
return conf;
}
static char *
ngx_http_degradation_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
{
ngx_http_degradation_loc_conf_t *prev = parent;
ngx_http_degradation_loc_conf_t *conf = child;
ngx_conf_merge_uint_value(conf->degrade, prev->degrade, 0);
return NGX_CONF_OK;
}
static char *
ngx_http_degradation(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_degradation_main_conf_t *dmcf = conf;
ngx_str_t *value, s;
value = cf->args->elts;
if (ngx_strncmp(value[1].data, "sbrk=", 5) == 0) {
s.len = value[1].len - 5;
s.data = value[1].data + 5;
dmcf->sbrk_size = ngx_parse_size(&s);
if (dmcf->sbrk_size == (size_t) NGX_ERROR) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid sbrk size \"%V\"", &value[1]);
return NGX_CONF_ERROR;
}
return NGX_CONF_OK;
}
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid parameter \"%V\"", &value[1]);
return NGX_CONF_ERROR;
}
static ngx_int_t
ngx_http_degradation_init(ngx_conf_t *cf)
{
ngx_http_handler_pt *h;
ngx_http_core_main_conf_t *cmcf;
cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
h = ngx_array_push(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers);
if (h == NULL) {
return NGX_ERROR;
}
*h = ngx_http_degradation_handler;
return NGX_OK;
}
nginx-1.14.0/src/http/modules/ngx_http_empty_gif_module.c 000644 001751 001751 00000012640 13265410474 024744 0 ustar 00mdounin mdounin 000000 000000
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#include
#include
#include
static char *ngx_http_empty_gif(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static ngx_command_t ngx_http_empty_gif_commands[] = {
{ ngx_string("empty_gif"),
NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,
ngx_http_empty_gif,
0,
0,
NULL },
ngx_null_command
};
/* the minimal single pixel transparent GIF, 43 bytes */
static u_char ngx_empty_gif[] = {
'G', 'I', 'F', '8', '9', 'a', /* header */
/* logical screen descriptor */
0x01, 0x00, /* logical screen width */
0x01, 0x00, /* logical screen height */
0x80, /* global 1-bit color table */
0x01, /* background color #1 */
0x00, /* no aspect ratio */
/* global color table */
0x00, 0x00, 0x00, /* #0: black */
0xff, 0xff, 0xff, /* #1: white */
/* graphic control extension */
0x21, /* extension introducer */
0xf9, /* graphic control label */
0x04, /* block size */
0x01, /* transparent color is given, */
/* no disposal specified, */
/* user input is not expected */
0x00, 0x00, /* delay time */
0x01, /* transparent color #1 */
0x00, /* block terminator */
/* image descriptor */
0x2c, /* image separator */
0x00, 0x00, /* image left position */
0x00, 0x00, /* image top position */
0x01, 0x00, /* image width */
0x01, 0x00, /* image height */
0x00, /* no local color table, no interlaced */
/* table based image data */
0x02, /* LZW minimum code size, */
/* must be at least 2-bit */
0x02, /* block size */
0x4c, 0x01, /* compressed bytes 01_001_100, 0000000_1 */
/* 100: clear code */
/* 001: 1 */
/* 101: end of information code */
0x00, /* block terminator */
0x3B /* trailer */
};
static ngx_http_module_t ngx_http_empty_gif_module_ctx = {
NULL, /* preconfiguration */
NULL, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
NULL, /* create location configuration */
NULL /* merge location configuration */
};
ngx_module_t ngx_http_empty_gif_module = {
NGX_MODULE_V1,
&ngx_http_empty_gif_module_ctx, /* module context */
ngx_http_empty_gif_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static ngx_str_t ngx_http_gif_type = ngx_string("image/gif");
static ngx_int_t
ngx_http_empty_gif_handler(ngx_http_request_t *r)
{
ngx_http_complex_value_t cv;
if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) {
return NGX_HTTP_NOT_ALLOWED;
}
ngx_memzero(&cv, sizeof(ngx_http_complex_value_t));
cv.value.len = sizeof(ngx_empty_gif);
cv.value.data = ngx_empty_gif;
r->headers_out.last_modified_time = 23349600;
return ngx_http_send_response(r, NGX_HTTP_OK, &ngx_http_gif_type, &cv);
}
static char *
ngx_http_empty_gif(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_core_loc_conf_t *clcf;
clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
clcf->handler = ngx_http_empty_gif_handler;
return NGX_CONF_OK;
}
nginx-1.14.0/src/http/modules/ngx_http_fastcgi_module.c 000644 001751 001751 00000325323 13265410474 024406 0 ustar 00mdounin mdounin 000000 000000
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#include
#include
#include
typedef struct {
ngx_array_t caches; /* ngx_http_file_cache_t * */
} ngx_http_fastcgi_main_conf_t;
typedef struct {
ngx_array_t *flushes;
ngx_array_t *lengths;
ngx_array_t *values;
ngx_uint_t number;
ngx_hash_t hash;
} ngx_http_fastcgi_params_t;
typedef struct {
ngx_http_upstream_conf_t upstream;
ngx_str_t index;
ngx_http_fastcgi_params_t params;
#if (NGX_HTTP_CACHE)
ngx_http_fastcgi_params_t params_cache;
#endif
ngx_array_t *params_source;
ngx_array_t *catch_stderr;
ngx_array_t *fastcgi_lengths;
ngx_array_t *fastcgi_values;
ngx_flag_t keep_conn;
#if (NGX_HTTP_CACHE)
ngx_http_complex_value_t cache_key;
#endif
#if (NGX_PCRE)
ngx_regex_t *split_regex;
ngx_str_t split_name;
#endif
} ngx_http_fastcgi_loc_conf_t;
typedef enum {
ngx_http_fastcgi_st_version = 0,
ngx_http_fastcgi_st_type,
ngx_http_fastcgi_st_request_id_hi,
ngx_http_fastcgi_st_request_id_lo,
ngx_http_fastcgi_st_content_length_hi,
ngx_http_fastcgi_st_content_length_lo,
ngx_http_fastcgi_st_padding_length,
ngx_http_fastcgi_st_reserved,
ngx_http_fastcgi_st_data,
ngx_http_fastcgi_st_padding
} ngx_http_fastcgi_state_e;
typedef struct {
u_char *start;
u_char *end;
} ngx_http_fastcgi_split_part_t;
typedef struct {
ngx_http_fastcgi_state_e state;
u_char *pos;
u_char *last;
ngx_uint_t type;
size_t length;
size_t padding;
ngx_chain_t *free;
ngx_chain_t *busy;
unsigned fastcgi_stdout:1;
unsigned large_stderr:1;
unsigned header_sent:1;
ngx_array_t *split_parts;
ngx_str_t script_name;
ngx_str_t path_info;
} ngx_http_fastcgi_ctx_t;
#define NGX_HTTP_FASTCGI_RESPONDER 1
#define NGX_HTTP_FASTCGI_KEEP_CONN 1
#define NGX_HTTP_FASTCGI_BEGIN_REQUEST 1
#define NGX_HTTP_FASTCGI_ABORT_REQUEST 2
#define NGX_HTTP_FASTCGI_END_REQUEST 3
#define NGX_HTTP_FASTCGI_PARAMS 4
#define NGX_HTTP_FASTCGI_STDIN 5
#define NGX_HTTP_FASTCGI_STDOUT 6
#define NGX_HTTP_FASTCGI_STDERR 7
#define NGX_HTTP_FASTCGI_DATA 8
typedef struct {
u_char version;
u_char type;
u_char request_id_hi;
u_char request_id_lo;
u_char content_length_hi;
u_char content_length_lo;
u_char padding_length;
u_char reserved;
} ngx_http_fastcgi_header_t;
typedef struct {
u_char role_hi;
u_char role_lo;
u_char flags;
u_char reserved[5];
} ngx_http_fastcgi_begin_request_t;
typedef struct {
u_char version;
u_char type;
u_char request_id_hi;
u_char request_id_lo;
} ngx_http_fastcgi_header_small_t;
typedef struct {
ngx_http_fastcgi_header_t h0;
ngx_http_fastcgi_begin_request_t br;
ngx_http_fastcgi_header_small_t h1;
} ngx_http_fastcgi_request_start_t;
static ngx_int_t ngx_http_fastcgi_eval(ngx_http_request_t *r,
ngx_http_fastcgi_loc_conf_t *flcf);
#if (NGX_HTTP_CACHE)
static ngx_int_t ngx_http_fastcgi_create_key(ngx_http_request_t *r);
#endif
static ngx_int_t ngx_http_fastcgi_create_request(ngx_http_request_t *r);
static ngx_int_t ngx_http_fastcgi_reinit_request(ngx_http_request_t *r);
static ngx_int_t ngx_http_fastcgi_body_output_filter(void *data,
ngx_chain_t *in);
static ngx_int_t ngx_http_fastcgi_process_header(ngx_http_request_t *r);
static ngx_int_t ngx_http_fastcgi_input_filter_init(void *data);
static ngx_int_t ngx_http_fastcgi_input_filter(ngx_event_pipe_t *p,
ngx_buf_t *buf);
static ngx_int_t ngx_http_fastcgi_non_buffered_filter(void *data,
ssize_t bytes);
static ngx_int_t ngx_http_fastcgi_process_record(ngx_http_request_t *r,
ngx_http_fastcgi_ctx_t *f);
static void ngx_http_fastcgi_abort_request(ngx_http_request_t *r);
static void ngx_http_fastcgi_finalize_request(ngx_http_request_t *r,
ngx_int_t rc);
static ngx_int_t ngx_http_fastcgi_add_variables(ngx_conf_t *cf);
static void *ngx_http_fastcgi_create_main_conf(ngx_conf_t *cf);
static void *ngx_http_fastcgi_create_loc_conf(ngx_conf_t *cf);
static char *ngx_http_fastcgi_merge_loc_conf(ngx_conf_t *cf,
void *parent, void *child);
static ngx_int_t ngx_http_fastcgi_init_params(ngx_conf_t *cf,
ngx_http_fastcgi_loc_conf_t *conf, ngx_http_fastcgi_params_t *params,
ngx_keyval_t *default_params);
static ngx_int_t ngx_http_fastcgi_script_name_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_fastcgi_path_info_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_http_fastcgi_ctx_t *ngx_http_fastcgi_split(ngx_http_request_t *r,
ngx_http_fastcgi_loc_conf_t *flcf);
static char *ngx_http_fastcgi_pass(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static char *ngx_http_fastcgi_split_path_info(ngx_conf_t *cf,
ngx_command_t *cmd, void *conf);
static char *ngx_http_fastcgi_store(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
#if (NGX_HTTP_CACHE)
static char *ngx_http_fastcgi_cache(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static char *ngx_http_fastcgi_cache_key(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
#endif
static char *ngx_http_fastcgi_lowat_check(ngx_conf_t *cf, void *post,
void *data);
static ngx_conf_post_t ngx_http_fastcgi_lowat_post =
{ ngx_http_fastcgi_lowat_check };
static ngx_conf_bitmask_t ngx_http_fastcgi_next_upstream_masks[] = {
{ ngx_string("error"), NGX_HTTP_UPSTREAM_FT_ERROR },
{ ngx_string("timeout"), NGX_HTTP_UPSTREAM_FT_TIMEOUT },
{ ngx_string("invalid_header"), NGX_HTTP_UPSTREAM_FT_INVALID_HEADER },
{ ngx_string("non_idempotent"), NGX_HTTP_UPSTREAM_FT_NON_IDEMPOTENT },
{ ngx_string("http_500"), NGX_HTTP_UPSTREAM_FT_HTTP_500 },
{ ngx_string("http_503"), NGX_HTTP_UPSTREAM_FT_HTTP_503 },
{ ngx_string("http_403"), NGX_HTTP_UPSTREAM_FT_HTTP_403 },
{ ngx_string("http_404"), NGX_HTTP_UPSTREAM_FT_HTTP_404 },
{ ngx_string("http_429"), NGX_HTTP_UPSTREAM_FT_HTTP_429 },
{ ngx_string("updating"), NGX_HTTP_UPSTREAM_FT_UPDATING },
{ ngx_string("off"), NGX_HTTP_UPSTREAM_FT_OFF },
{ ngx_null_string, 0 }
};
ngx_module_t ngx_http_fastcgi_module;
static ngx_command_t ngx_http_fastcgi_commands[] = {
{ ngx_string("fastcgi_pass"),
NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,
ngx_http_fastcgi_pass,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
{ ngx_string("fastcgi_index"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_str_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_fastcgi_loc_conf_t, index),
NULL },
{ ngx_string("fastcgi_split_path_info"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_http_fastcgi_split_path_info,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
{ ngx_string("fastcgi_store"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_http_fastcgi_store,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
{ ngx_string("fastcgi_store_access"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE123,
ngx_conf_set_access_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_fastcgi_loc_conf_t, upstream.store_access),
NULL },
{ ngx_string("fastcgi_buffering"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_fastcgi_loc_conf_t, upstream.buffering),
NULL },
{ ngx_string("fastcgi_request_buffering"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_fastcgi_loc_conf_t, upstream.request_buffering),
NULL },
{ ngx_string("fastcgi_ignore_client_abort"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_fastcgi_loc_conf_t, upstream.ignore_client_abort),
NULL },
{ ngx_string("fastcgi_bind"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12,
ngx_http_upstream_bind_set_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_fastcgi_loc_conf_t, upstream.local),
NULL },
{ ngx_string("fastcgi_connect_timeout"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_msec_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_fastcgi_loc_conf_t, upstream.connect_timeout),
NULL },
{ ngx_string("fastcgi_send_timeout"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_msec_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_fastcgi_loc_conf_t, upstream.send_timeout),
NULL },
{ ngx_string("fastcgi_send_lowat"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_size_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_fastcgi_loc_conf_t, upstream.send_lowat),
&ngx_http_fastcgi_lowat_post },
{ ngx_string("fastcgi_buffer_size"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_size_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_fastcgi_loc_conf_t, upstream.buffer_size),
NULL },
{ ngx_string("fastcgi_pass_request_headers"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_fastcgi_loc_conf_t, upstream.pass_request_headers),
NULL },
{ ngx_string("fastcgi_pass_request_body"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_fastcgi_loc_conf_t, upstream.pass_request_body),
NULL },
{ ngx_string("fastcgi_intercept_errors"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_fastcgi_loc_conf_t, upstream.intercept_errors),
NULL },
{ ngx_string("fastcgi_read_timeout"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_msec_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_fastcgi_loc_conf_t, upstream.read_timeout),
NULL },
{ ngx_string("fastcgi_buffers"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
ngx_conf_set_bufs_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_fastcgi_loc_conf_t, upstream.bufs),
NULL },
{ ngx_string("fastcgi_busy_buffers_size"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_size_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_fastcgi_loc_conf_t, upstream.busy_buffers_size_conf),
NULL },
{ ngx_string("fastcgi_force_ranges"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_fastcgi_loc_conf_t, upstream.force_ranges),
NULL },
{ ngx_string("fastcgi_limit_rate"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_size_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_fastcgi_loc_conf_t, upstream.limit_rate),
NULL },
#if (NGX_HTTP_CACHE)
{ ngx_string("fastcgi_cache"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_http_fastcgi_cache,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
{ ngx_string("fastcgi_cache_key"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_http_fastcgi_cache_key,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
{ ngx_string("fastcgi_cache_path"),
NGX_HTTP_MAIN_CONF|NGX_CONF_2MORE,
ngx_http_file_cache_set_slot,
NGX_HTTP_MAIN_CONF_OFFSET,
offsetof(ngx_http_fastcgi_main_conf_t, caches),
&ngx_http_fastcgi_module },
{ ngx_string("fastcgi_cache_bypass"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
ngx_http_set_predicate_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_fastcgi_loc_conf_t, upstream.cache_bypass),
NULL },
{ ngx_string("fastcgi_no_cache"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
ngx_http_set_predicate_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_fastcgi_loc_conf_t, upstream.no_cache),
NULL },
{ ngx_string("fastcgi_cache_valid"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
ngx_http_file_cache_valid_set_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_fastcgi_loc_conf_t, upstream.cache_valid),
NULL },
{ ngx_string("fastcgi_cache_min_uses"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_num_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_fastcgi_loc_conf_t, upstream.cache_min_uses),
NULL },
{ ngx_string("fastcgi_cache_max_range_offset"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_off_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_fastcgi_loc_conf_t, upstream.cache_max_range_offset),
NULL },
{ ngx_string("fastcgi_cache_use_stale"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
ngx_conf_set_bitmask_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_fastcgi_loc_conf_t, upstream.cache_use_stale),
&ngx_http_fastcgi_next_upstream_masks },
{ ngx_string("fastcgi_cache_methods"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
ngx_conf_set_bitmask_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_fastcgi_loc_conf_t, upstream.cache_methods),
&ngx_http_upstream_cache_method_mask },
{ ngx_string("fastcgi_cache_lock"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_fastcgi_loc_conf_t, upstream.cache_lock),
NULL },
{ ngx_string("fastcgi_cache_lock_timeout"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_msec_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_fastcgi_loc_conf_t, upstream.cache_lock_timeout),
NULL },
{ ngx_string("fastcgi_cache_lock_age"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_msec_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_fastcgi_loc_conf_t, upstream.cache_lock_age),
NULL },
{ ngx_string("fastcgi_cache_revalidate"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_fastcgi_loc_conf_t, upstream.cache_revalidate),
NULL },
{ ngx_string("fastcgi_cache_background_update"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_fastcgi_loc_conf_t, upstream.cache_background_update),
NULL },
#endif
{ ngx_string("fastcgi_temp_path"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1234,
ngx_conf_set_path_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_fastcgi_loc_conf_t, upstream.temp_path),
NULL },
{ ngx_string("fastcgi_max_temp_file_size"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_size_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_fastcgi_loc_conf_t, upstream.max_temp_file_size_conf),
NULL },
{ ngx_string("fastcgi_temp_file_write_size"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_size_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_fastcgi_loc_conf_t, upstream.temp_file_write_size_conf),
NULL },
{ ngx_string("fastcgi_next_upstream"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
ngx_conf_set_bitmask_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_fastcgi_loc_conf_t, upstream.next_upstream),
&ngx_http_fastcgi_next_upstream_masks },
{ ngx_string("fastcgi_next_upstream_tries"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_num_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_fastcgi_loc_conf_t, upstream.next_upstream_tries),
NULL },
{ ngx_string("fastcgi_next_upstream_timeout"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_msec_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_fastcgi_loc_conf_t, upstream.next_upstream_timeout),
NULL },
{ ngx_string("fastcgi_param"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE23,
ngx_http_upstream_param_set_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_fastcgi_loc_conf_t, params_source),
NULL },
{ ngx_string("fastcgi_pass_header"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_str_array_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_fastcgi_loc_conf_t, upstream.pass_headers),
NULL },
{ ngx_string("fastcgi_hide_header"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_str_array_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_fastcgi_loc_conf_t, upstream.hide_headers),
NULL },
{ ngx_string("fastcgi_ignore_headers"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
ngx_conf_set_bitmask_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_fastcgi_loc_conf_t, upstream.ignore_headers),
&ngx_http_upstream_ignore_headers_masks },
{ ngx_string("fastcgi_catch_stderr"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_str_array_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_fastcgi_loc_conf_t, catch_stderr),
NULL },
{ ngx_string("fastcgi_keep_conn"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_fastcgi_loc_conf_t, keep_conn),
NULL },
ngx_null_command
};
static ngx_http_module_t ngx_http_fastcgi_module_ctx = {
ngx_http_fastcgi_add_variables, /* preconfiguration */
NULL, /* postconfiguration */
ngx_http_fastcgi_create_main_conf, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
ngx_http_fastcgi_create_loc_conf, /* create location configuration */
ngx_http_fastcgi_merge_loc_conf /* merge location configuration */
};
ngx_module_t ngx_http_fastcgi_module = {
NGX_MODULE_V1,
&ngx_http_fastcgi_module_ctx, /* module context */
ngx_http_fastcgi_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static ngx_http_fastcgi_request_start_t ngx_http_fastcgi_request_start = {
{ 1, /* version */
NGX_HTTP_FASTCGI_BEGIN_REQUEST, /* type */
0, /* request_id_hi */
1, /* request_id_lo */
0, /* content_length_hi */
sizeof(ngx_http_fastcgi_begin_request_t), /* content_length_lo */
0, /* padding_length */
0 }, /* reserved */
{ 0, /* role_hi */
NGX_HTTP_FASTCGI_RESPONDER, /* role_lo */
0, /* NGX_HTTP_FASTCGI_KEEP_CONN */ /* flags */
{ 0, 0, 0, 0, 0 } }, /* reserved[5] */
{ 1, /* version */
NGX_HTTP_FASTCGI_PARAMS, /* type */
0, /* request_id_hi */
1 }, /* request_id_lo */
};
static ngx_http_variable_t ngx_http_fastcgi_vars[] = {
{ ngx_string("fastcgi_script_name"), NULL,
ngx_http_fastcgi_script_name_variable, 0,
NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
{ ngx_string("fastcgi_path_info"), NULL,
ngx_http_fastcgi_path_info_variable, 0,
NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
ngx_http_null_variable
};
static ngx_str_t ngx_http_fastcgi_hide_headers[] = {
ngx_string("Status"),
ngx_string("X-Accel-Expires"),
ngx_string("X-Accel-Redirect"),
ngx_string("X-Accel-Limit-Rate"),
ngx_string("X-Accel-Buffering"),
ngx_string("X-Accel-Charset"),
ngx_null_string
};
#if (NGX_HTTP_CACHE)
static ngx_keyval_t ngx_http_fastcgi_cache_headers[] = {
{ ngx_string("HTTP_IF_MODIFIED_SINCE"),
ngx_string("$upstream_cache_last_modified") },
{ ngx_string("HTTP_IF_UNMODIFIED_SINCE"), ngx_string("") },
{ ngx_string("HTTP_IF_NONE_MATCH"), ngx_string("$upstream_cache_etag") },
{ ngx_string("HTTP_IF_MATCH"), ngx_string("") },
{ ngx_string("HTTP_RANGE"), ngx_string("") },
{ ngx_string("HTTP_IF_RANGE"), ngx_string("") },
{ ngx_null_string, ngx_null_string }
};
#endif
static ngx_path_init_t ngx_http_fastcgi_temp_path = {
ngx_string(NGX_HTTP_FASTCGI_TEMP_PATH), { 1, 2, 0 }
};
static ngx_int_t
ngx_http_fastcgi_handler(ngx_http_request_t *r)
{
ngx_int_t rc;
ngx_http_upstream_t *u;
ngx_http_fastcgi_ctx_t *f;
ngx_http_fastcgi_loc_conf_t *flcf;
#if (NGX_HTTP_CACHE)
ngx_http_fastcgi_main_conf_t *fmcf;
#endif
if (ngx_http_upstream_create(r) != NGX_OK) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
f = ngx_pcalloc(r->pool, sizeof(ngx_http_fastcgi_ctx_t));
if (f == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
ngx_http_set_ctx(r, f, ngx_http_fastcgi_module);
flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);
if (flcf->fastcgi_lengths) {
if (ngx_http_fastcgi_eval(r, flcf) != NGX_OK) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
}
u = r->upstream;
ngx_str_set(&u->schema, "fastcgi://");
u->output.tag = (ngx_buf_tag_t) &ngx_http_fastcgi_module;
u->conf = &flcf->upstream;
#if (NGX_HTTP_CACHE)
fmcf = ngx_http_get_module_main_conf(r, ngx_http_fastcgi_module);
u->caches = &fmcf->caches;
u->create_key = ngx_http_fastcgi_create_key;
#endif
u->create_request = ngx_http_fastcgi_create_request;
u->reinit_request = ngx_http_fastcgi_reinit_request;
u->process_header = ngx_http_fastcgi_process_header;
u->abort_request = ngx_http_fastcgi_abort_request;
u->finalize_request = ngx_http_fastcgi_finalize_request;
r->state = 0;
u->buffering = flcf->upstream.buffering;
u->pipe = ngx_pcalloc(r->pool, sizeof(ngx_event_pipe_t));
if (u->pipe == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
u->pipe->input_filter = ngx_http_fastcgi_input_filter;
u->pipe->input_ctx = r;
u->input_filter_init = ngx_http_fastcgi_input_filter_init;
u->input_filter = ngx_http_fastcgi_non_buffered_filter;
u->input_filter_ctx = r;
if (!flcf->upstream.request_buffering
&& flcf->upstream.pass_request_body)
{
r->request_body_no_buffering = 1;
}
rc = ngx_http_read_client_request_body(r, ngx_http_upstream_init);
if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
return rc;
}
return NGX_DONE;
}
static ngx_int_t
ngx_http_fastcgi_eval(ngx_http_request_t *r, ngx_http_fastcgi_loc_conf_t *flcf)
{
ngx_url_t url;
ngx_http_upstream_t *u;
ngx_memzero(&url, sizeof(ngx_url_t));
if (ngx_http_script_run(r, &url.url, flcf->fastcgi_lengths->elts, 0,
flcf->fastcgi_values->elts)
== NULL)
{
return NGX_ERROR;
}
url.no_resolve = 1;
if (ngx_parse_url(r->pool, &url) != NGX_OK) {
if (url.err) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"%s in upstream \"%V\"", url.err, &url.url);
}
return NGX_ERROR;
}
u = r->upstream;
u->resolved = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_resolved_t));
if (u->resolved == NULL) {
return NGX_ERROR;
}
if (url.addrs) {
u->resolved->sockaddr = url.addrs[0].sockaddr;
u->resolved->socklen = url.addrs[0].socklen;
u->resolved->name = url.addrs[0].name;
u->resolved->naddrs = 1;
}
u->resolved->host = url.host;
u->resolved->port = url.port;
u->resolved->no_port = url.no_port;
return NGX_OK;
}
#if (NGX_HTTP_CACHE)
static ngx_int_t
ngx_http_fastcgi_create_key(ngx_http_request_t *r)
{
ngx_str_t *key;
ngx_http_fastcgi_loc_conf_t *flcf;
key = ngx_array_push(&r->cache->keys);
if (key == NULL) {
return NGX_ERROR;
}
flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);
if (ngx_http_complex_value(r, &flcf->cache_key, key) != NGX_OK) {
return NGX_ERROR;
}
return NGX_OK;
}
#endif
static ngx_int_t
ngx_http_fastcgi_create_request(ngx_http_request_t *r)
{
off_t file_pos;
u_char ch, *pos, *lowcase_key;
size_t size, len, key_len, val_len, padding,
allocated;
ngx_uint_t i, n, next, hash, skip_empty, header_params;
ngx_buf_t *b;
ngx_chain_t *cl, *body;
ngx_list_part_t *part;
ngx_table_elt_t *header, **ignored;
ngx_http_upstream_t *u;
ngx_http_script_code_pt code;
ngx_http_script_engine_t e, le;
ngx_http_fastcgi_header_t *h;
ngx_http_fastcgi_params_t *params;
ngx_http_fastcgi_loc_conf_t *flcf;
ngx_http_script_len_code_pt lcode;
len = 0;
header_params = 0;
ignored = NULL;
u = r->upstream;
flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);
#if (NGX_HTTP_CACHE)
params = u->cacheable ? &flcf->params_cache : &flcf->params;
#else
params = &flcf->params;
#endif
if (params->lengths) {
ngx_memzero(&le, sizeof(ngx_http_script_engine_t));
ngx_http_script_flush_no_cacheable_variables(r, params->flushes);
le.flushed = 1;
le.ip = params->lengths->elts;
le.request = r;
while (*(uintptr_t *) le.ip) {
lcode = *(ngx_http_script_len_code_pt *) le.ip;
key_len = lcode(&le);
lcode = *(ngx_http_script_len_code_pt *) le.ip;
skip_empty = lcode(&le);
for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) {
lcode = *(ngx_http_script_len_code_pt *) le.ip;
}
le.ip += sizeof(uintptr_t);
if (skip_empty && val_len == 0) {
continue;
}
len += 1 + key_len + ((val_len > 127) ? 4 : 1) + val_len;
}
}
if (flcf->upstream.pass_request_headers) {
allocated = 0;
lowcase_key = NULL;
if (params->number) {
n = 0;
part = &r->headers_in.headers.part;
while (part) {
n += part->nelts;
part = part->next;
}
ignored = ngx_palloc(r->pool, n * sizeof(void *));
if (ignored == NULL) {
return NGX_ERROR;
}
}
part = &r->headers_in.headers.part;
header = part->elts;
for (i = 0; /* void */; i++) {
if (i >= part->nelts) {
if (part->next == NULL) {
break;
}
part = part->next;
header = part->elts;
i = 0;
}
if (params->number) {
if (allocated < header[i].key.len) {
allocated = header[i].key.len + 16;
lowcase_key = ngx_pnalloc(r->pool, allocated);
if (lowcase_key == NULL) {
return NGX_ERROR;
}
}
hash = 0;
for (n = 0; n < header[i].key.len; n++) {
ch = header[i].key.data[n];
if (ch >= 'A' && ch <= 'Z') {
ch |= 0x20;
} else if (ch == '-') {
ch = '_';
}
hash = ngx_hash(hash, ch);
lowcase_key[n] = ch;
}
if (ngx_hash_find(¶ms->hash, hash, lowcase_key, n)) {
ignored[header_params++] = &header[i];
continue;
}
n += sizeof("HTTP_") - 1;
} else {
n = sizeof("HTTP_") - 1 + header[i].key.len;
}
len += ((n > 127) ? 4 : 1) + ((header[i].value.len > 127) ? 4 : 1)
+ n + header[i].value.len;
}
}
if (len > 65535) {
ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
"fastcgi request record is too big: %uz", len);
return NGX_ERROR;
}
padding = 8 - len % 8;
padding = (padding == 8) ? 0 : padding;
size = sizeof(ngx_http_fastcgi_header_t)
+ sizeof(ngx_http_fastcgi_begin_request_t)
+ sizeof(ngx_http_fastcgi_header_t) /* NGX_HTTP_FASTCGI_PARAMS */
+ len + padding
+ sizeof(ngx_http_fastcgi_header_t) /* NGX_HTTP_FASTCGI_PARAMS */
+ sizeof(ngx_http_fastcgi_header_t); /* NGX_HTTP_FASTCGI_STDIN */
b = ngx_create_temp_buf(r->pool, size);
if (b == NULL) {
return NGX_ERROR;
}
cl = ngx_alloc_chain_link(r->pool);
if (cl == NULL) {
return NGX_ERROR;
}
cl->buf = b;
ngx_http_fastcgi_request_start.br.flags =
flcf->keep_conn ? NGX_HTTP_FASTCGI_KEEP_CONN : 0;
ngx_memcpy(b->pos, &ngx_http_fastcgi_request_start,
sizeof(ngx_http_fastcgi_request_start_t));
h = (ngx_http_fastcgi_header_t *)
(b->pos + sizeof(ngx_http_fastcgi_header_t)
+ sizeof(ngx_http_fastcgi_begin_request_t));
h->content_length_hi = (u_char) ((len >> 8) & 0xff);
h->content_length_lo = (u_char) (len & 0xff);
h->padding_length = (u_char) padding;
h->reserved = 0;
b->last = b->pos + sizeof(ngx_http_fastcgi_header_t)
+ sizeof(ngx_http_fastcgi_begin_request_t)
+ sizeof(ngx_http_fastcgi_header_t);
if (params->lengths) {
ngx_memzero(&e, sizeof(ngx_http_script_engine_t));
e.ip = params->values->elts;
e.pos = b->last;
e.request = r;
e.flushed = 1;
le.ip = params->lengths->elts;
while (*(uintptr_t *) le.ip) {
lcode = *(ngx_http_script_len_code_pt *) le.ip;
key_len = (u_char) lcode(&le);
lcode = *(ngx_http_script_len_code_pt *) le.ip;
skip_empty = lcode(&le);
for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) {
lcode = *(ngx_http_script_len_code_pt *) le.ip;
}
le.ip += sizeof(uintptr_t);
if (skip_empty && val_len == 0) {
e.skip = 1;
while (*(uintptr_t *) e.ip) {
code = *(ngx_http_script_code_pt *) e.ip;
code((ngx_http_script_engine_t *) &e);
}
e.ip += sizeof(uintptr_t);
e.skip = 0;
continue;
}
*e.pos++ = (u_char) key_len;
if (val_len > 127) {
*e.pos++ = (u_char) (((val_len >> 24) & 0x7f) | 0x80);
*e.pos++ = (u_char) ((val_len >> 16) & 0xff);
*e.pos++ = (u_char) ((val_len >> 8) & 0xff);
*e.pos++ = (u_char) (val_len & 0xff);
} else {
*e.pos++ = (u_char) val_len;
}
while (*(uintptr_t *) e.ip) {
code = *(ngx_http_script_code_pt *) e.ip;
code((ngx_http_script_engine_t *) &e);
}
e.ip += sizeof(uintptr_t);
ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"fastcgi param: \"%*s: %*s\"",
key_len, e.pos - (key_len + val_len),
val_len, e.pos - val_len);
}
b->last = e.pos;
}
if (flcf->upstream.pass_request_headers) {
part = &r->headers_in.headers.part;
header = part->elts;
for (i = 0; /* void */; i++) {
if (i >= part->nelts) {
if (part->next == NULL) {
break;
}
part = part->next;
header = part->elts;
i = 0;
}
for (n = 0; n < header_params; n++) {
if (&header[i] == ignored[n]) {
goto next;
}
}
key_len = sizeof("HTTP_") - 1 + header[i].key.len;
if (key_len > 127) {
*b->last++ = (u_char) (((key_len >> 24) & 0x7f) | 0x80);
*b->last++ = (u_char) ((key_len >> 16) & 0xff);
*b->last++ = (u_char) ((key_len >> 8) & 0xff);
*b->last++ = (u_char) (key_len & 0xff);
} else {
*b->last++ = (u_char) key_len;
}
val_len = header[i].value.len;
if (val_len > 127) {
*b->last++ = (u_char) (((val_len >> 24) & 0x7f) | 0x80);
*b->last++ = (u_char) ((val_len >> 16) & 0xff);
*b->last++ = (u_char) ((val_len >> 8) & 0xff);
*b->last++ = (u_char) (val_len & 0xff);
} else {
*b->last++ = (u_char) val_len;
}
b->last = ngx_cpymem(b->last, "HTTP_", sizeof("HTTP_") - 1);
for (n = 0; n < header[i].key.len; n++) {
ch = header[i].key.data[n];
if (ch >= 'a' && ch <= 'z') {
ch &= ~0x20;
} else if (ch == '-') {
ch = '_';
}
*b->last++ = ch;
}
b->last = ngx_copy(b->last, header[i].value.data, val_len);
ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"fastcgi param: \"%*s: %*s\"",
key_len, b->last - (key_len + val_len),
val_len, b->last - val_len);
next:
continue;
}
}
if (padding) {
ngx_memzero(b->last, padding);
b->last += padding;
}
h = (ngx_http_fastcgi_header_t *) b->last;
b->last += sizeof(ngx_http_fastcgi_header_t);
h->version = 1;
h->type = NGX_HTTP_FASTCGI_PARAMS;
h->request_id_hi = 0;
h->request_id_lo = 1;
h->content_length_hi = 0;
h->content_length_lo = 0;
h->padding_length = 0;
h->reserved = 0;
if (r->request_body_no_buffering) {
u->request_bufs = cl;
u->output.output_filter = ngx_http_fastcgi_body_output_filter;
u->output.filter_ctx = r;
} else if (flcf->upstream.pass_request_body) {
body = u->request_bufs;
u->request_bufs = cl;
#if (NGX_SUPPRESS_WARN)
file_pos = 0;
pos = NULL;
#endif
while (body) {
if (ngx_buf_special(body->buf)) {
body = body->next;
continue;
}
if (body->buf->in_file) {
file_pos = body->buf->file_pos;
} else {
pos = body->buf->pos;
}
next = 0;
do {
b = ngx_alloc_buf(r->pool);
if (b == NULL) {
return NGX_ERROR;
}
ngx_memcpy(b, body->buf, sizeof(ngx_buf_t));
if (body->buf->in_file) {
b->file_pos = file_pos;
file_pos += 32 * 1024;
if (file_pos >= body->buf->file_last) {
file_pos = body->buf->file_last;
next = 1;
}
b->file_last = file_pos;
len = (ngx_uint_t) (file_pos - b->file_pos);
} else {
b->pos = pos;
b->start = pos;
pos += 32 * 1024;
if (pos >= body->buf->last) {
pos = body->buf->last;
next = 1;
}
b->last = pos;
len = (ngx_uint_t) (pos - b->pos);
}
padding = 8 - len % 8;
padding = (padding == 8) ? 0 : padding;
h = (ngx_http_fastcgi_header_t *) cl->buf->last;
cl->buf->last += sizeof(ngx_http_fastcgi_header_t);
h->version = 1;
h->type = NGX_HTTP_FASTCGI_STDIN;
h->request_id_hi = 0;
h->request_id_lo = 1;
h->content_length_hi = (u_char) ((len >> 8) & 0xff);
h->content_length_lo = (u_char) (len & 0xff);
h->padding_length = (u_char) padding;
h->reserved = 0;
cl->next = ngx_alloc_chain_link(r->pool);
if (cl->next == NULL) {
return NGX_ERROR;
}
cl = cl->next;
cl->buf = b;
b = ngx_create_temp_buf(r->pool,
sizeof(ngx_http_fastcgi_header_t)
+ padding);
if (b == NULL) {
return NGX_ERROR;
}
if (padding) {
ngx_memzero(b->last, padding);
b->last += padding;
}
cl->next = ngx_alloc_chain_link(r->pool);
if (cl->next == NULL) {
return NGX_ERROR;
}
cl = cl->next;
cl->buf = b;
} while (!next);
body = body->next;
}
} else {
u->request_bufs = cl;
}
if (!r->request_body_no_buffering) {
h = (ngx_http_fastcgi_header_t *) cl->buf->last;
cl->buf->last += sizeof(ngx_http_fastcgi_header_t);
h->version = 1;
h->type = NGX_HTTP_FASTCGI_STDIN;
h->request_id_hi = 0;
h->request_id_lo = 1;
h->content_length_hi = 0;
h->content_length_lo = 0;
h->padding_length = 0;
h->reserved = 0;
}
cl->next = NULL;
return NGX_OK;
}
static ngx_int_t
ngx_http_fastcgi_reinit_request(ngx_http_request_t *r)
{
ngx_http_fastcgi_ctx_t *f;
f = ngx_http_get_module_ctx(r, ngx_http_fastcgi_module);
if (f == NULL) {
return NGX_OK;
}
f->state = ngx_http_fastcgi_st_version;
f->fastcgi_stdout = 0;
f->large_stderr = 0;
if (f->split_parts) {
f->split_parts->nelts = 0;
}
r->state = 0;
return NGX_OK;
}
static ngx_int_t
ngx_http_fastcgi_body_output_filter(void *data, ngx_chain_t *in)
{
ngx_http_request_t *r = data;
off_t file_pos;
u_char *pos, *start;
size_t len, padding;
ngx_buf_t *b;
ngx_int_t rc;
ngx_uint_t next, last;
ngx_chain_t *cl, *tl, *out, **ll;
ngx_http_fastcgi_ctx_t *f;
ngx_http_fastcgi_header_t *h;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"fastcgi output filter");
f = ngx_http_get_module_ctx(r, ngx_http_fastcgi_module);
if (in == NULL) {
out = in;
goto out;
}
out = NULL;
ll = &out;
if (!f->header_sent) {
/* first buffer contains headers, pass it unmodified */
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"fastcgi output header");
f->header_sent = 1;
tl = ngx_alloc_chain_link(r->pool);
if (tl == NULL) {
return NGX_ERROR;
}
tl->buf = in->buf;
*ll = tl;
ll = &tl->next;
in = in->next;
if (in == NULL) {
tl->next = NULL;
goto out;
}
}
cl = ngx_chain_get_free_buf(r->pool, &f->free);
if (cl == NULL) {
return NGX_ERROR;
}
b = cl->buf;
b->tag = (ngx_buf_tag_t) &ngx_http_fastcgi_body_output_filter;
b->temporary = 1;
if (b->start == NULL) {
/* reserve space for maximum possible padding, 7 bytes */
b->start = ngx_palloc(r->pool,
sizeof(ngx_http_fastcgi_header_t) + 7);
if (b->start == NULL) {
return NGX_ERROR;
}
b->pos = b->start;
b->last = b->start;
b->end = b->start + sizeof(ngx_http_fastcgi_header_t) + 7;
}
*ll = cl;
last = 0;
padding = 0;
#if (NGX_SUPPRESS_WARN)
file_pos = 0;
pos = NULL;
#endif
while (in) {
ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0,
"fastcgi output in l:%d f:%d %p, pos %p, size: %z "
"file: %O, size: %O",
in->buf->last_buf,
in->buf->in_file,
in->buf->start, in->buf->pos,
in->buf->last - in->buf->pos,
in->buf->file_pos,
in->buf->file_last - in->buf->file_pos);
if (in->buf->last_buf) {
last = 1;
}
if (ngx_buf_special(in->buf)) {
in = in->next;
continue;
}
if (in->buf->in_file) {
file_pos = in->buf->file_pos;
} else {
pos = in->buf->pos;
}
next = 0;
do {
tl = ngx_chain_get_free_buf(r->pool, &f->free);
if (tl == NULL) {
return NGX_ERROR;
}
b = tl->buf;
start = b->start;
ngx_memcpy(b, in->buf, sizeof(ngx_buf_t));
/*
* restore b->start to preserve memory allocated in the buffer,
* to reuse it later for headers and padding
*/
b->start = start;
if (in->buf->in_file) {
b->file_pos = file_pos;
file_pos += 32 * 1024;
if (file_pos >= in->buf->file_last) {
file_pos = in->buf->file_last;
next = 1;
}
b->file_last = file_pos;
len = (ngx_uint_t) (file_pos - b->file_pos);
} else {
b->pos = pos;
pos += 32 * 1024;
if (pos >= in->buf->last) {
pos = in->buf->last;
next = 1;
}
b->last = pos;
len = (ngx_uint_t) (pos - b->pos);
}
b->tag = (ngx_buf_tag_t) &ngx_http_fastcgi_body_output_filter;
b->shadow = in->buf;
b->last_shadow = next;
b->last_buf = 0;
b->last_in_chain = 0;
padding = 8 - len % 8;
padding = (padding == 8) ? 0 : padding;
h = (ngx_http_fastcgi_header_t *) cl->buf->last;
cl->buf->last += sizeof(ngx_http_fastcgi_header_t);
h->version = 1;
h->type = NGX_HTTP_FASTCGI_STDIN;
h->request_id_hi = 0;
h->request_id_lo = 1;
h->content_length_hi = (u_char) ((len >> 8) & 0xff);
h->content_length_lo = (u_char) (len & 0xff);
h->padding_length = (u_char) padding;
h->reserved = 0;
cl->next = tl;
cl = tl;
tl = ngx_chain_get_free_buf(r->pool, &f->free);
if (tl == NULL) {
return NGX_ERROR;
}
b = tl->buf;
b->tag = (ngx_buf_tag_t) &ngx_http_fastcgi_body_output_filter;
b->temporary = 1;
if (b->start == NULL) {
/* reserve space for maximum possible padding, 7 bytes */
b->start = ngx_palloc(r->pool,
sizeof(ngx_http_fastcgi_header_t) + 7);
if (b->start == NULL) {
return NGX_ERROR;
}
b->pos = b->start;
b->last = b->start;
b->end = b->start + sizeof(ngx_http_fastcgi_header_t) + 7;
}
if (padding) {
ngx_memzero(b->last, padding);
b->last += padding;
}
cl->next = tl;
cl = tl;
} while (!next);
in = in->next;
}
if (last) {
h = (ngx_http_fastcgi_header_t *) cl->buf->last;
cl->buf->last += sizeof(ngx_http_fastcgi_header_t);
h->version = 1;
h->type = NGX_HTTP_FASTCGI_STDIN;
h->request_id_hi = 0;
h->request_id_lo = 1;
h->content_length_hi = 0;
h->content_length_lo = 0;
h->padding_length = 0;
h->reserved = 0;
cl->buf->last_buf = 1;
} else if (padding == 0) {
/* TODO: do not allocate buffers instead */
cl->buf->temporary = 0;
cl->buf->sync = 1;
}
cl->next = NULL;
out:
#if (NGX_DEBUG)
for (cl = out; cl; cl = cl->next) {
ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0,
"fastcgi output out l:%d f:%d %p, pos %p, size: %z "
"file: %O, size: %O",
cl->buf->last_buf,
cl->buf->in_file,
cl->buf->start, cl->buf->pos,
cl->buf->last - cl->buf->pos,
cl->buf->file_pos,
cl->buf->file_last - cl->buf->file_pos);
}
#endif
rc = ngx_chain_writer(&r->upstream->writer, out);
ngx_chain_update_chains(r->pool, &f->free, &f->busy, &out,
(ngx_buf_tag_t) &ngx_http_fastcgi_body_output_filter);
for (cl = f->free; cl; cl = cl->next) {
/* mark original buffers as sent */
if (cl->buf->shadow) {
if (cl->buf->last_shadow) {
b = cl->buf->shadow;
b->pos = b->last;
}
cl->buf->shadow = NULL;
}
}
return rc;
}
static ngx_int_t
ngx_http_fastcgi_process_header(ngx_http_request_t *r)
{
u_char *p, *msg, *start, *last,
*part_start, *part_end;
size_t size;
ngx_str_t *status_line, *pattern;
ngx_int_t rc, status;
ngx_buf_t buf;
ngx_uint_t i;
ngx_table_elt_t *h;
ngx_http_upstream_t *u;
ngx_http_fastcgi_ctx_t *f;
ngx_http_upstream_header_t *hh;
ngx_http_fastcgi_loc_conf_t *flcf;
ngx_http_fastcgi_split_part_t *part;
ngx_http_upstream_main_conf_t *umcf;
f = ngx_http_get_module_ctx(r, ngx_http_fastcgi_module);
umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module);
u = r->upstream;
for ( ;; ) {
if (f->state < ngx_http_fastcgi_st_data) {
f->pos = u->buffer.pos;
f->last = u->buffer.last;
rc = ngx_http_fastcgi_process_record(r, f);
u->buffer.pos = f->pos;
u->buffer.last = f->last;
if (rc == NGX_AGAIN) {
return NGX_AGAIN;
}
if (rc == NGX_ERROR) {
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
}
if (f->type != NGX_HTTP_FASTCGI_STDOUT
&& f->type != NGX_HTTP_FASTCGI_STDERR)
{
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent unexpected FastCGI record: %ui",
f->type);
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
}
if (f->type == NGX_HTTP_FASTCGI_STDOUT && f->length == 0) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream prematurely closed FastCGI stdout");
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
}
}
if (f->state == ngx_http_fastcgi_st_padding) {
if (u->buffer.pos + f->padding < u->buffer.last) {
f->state = ngx_http_fastcgi_st_version;
u->buffer.pos += f->padding;
continue;
}
if (u->buffer.pos + f->padding == u->buffer.last) {
f->state = ngx_http_fastcgi_st_version;
u->buffer.pos = u->buffer.last;
return NGX_AGAIN;
}
f->padding -= u->buffer.last - u->buffer.pos;
u->buffer.pos = u->buffer.last;
return NGX_AGAIN;
}
/* f->state == ngx_http_fastcgi_st_data */
if (f->type == NGX_HTTP_FASTCGI_STDERR) {
if (f->length) {
msg = u->buffer.pos;
if (u->buffer.pos + f->length <= u->buffer.last) {
u->buffer.pos += f->length;
f->length = 0;
f->state = ngx_http_fastcgi_st_padding;
} else {
f->length -= u->buffer.last - u->buffer.pos;
u->buffer.pos = u->buffer.last;
}
for (p = u->buffer.pos - 1; msg < p; p--) {
if (*p != LF && *p != CR && *p != '.' && *p != ' ') {
break;
}
}
p++;
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"FastCGI sent in stderr: \"%*s\"", p - msg, msg);
flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);
if (flcf->catch_stderr) {
pattern = flcf->catch_stderr->elts;
for (i = 0; i < flcf->catch_stderr->nelts; i++) {
if (ngx_strnstr(msg, (char *) pattern[i].data,
p - msg)
!= NULL)
{
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
}
}
}
if (u->buffer.pos == u->buffer.last) {
if (!f->fastcgi_stdout) {
/*
* the special handling the large number
* of the PHP warnings to not allocate memory
*/
#if (NGX_HTTP_CACHE)
if (r->cache) {
u->buffer.pos = u->buffer.start
+ r->cache->header_start;
} else {
u->buffer.pos = u->buffer.start;
}
#else
u->buffer.pos = u->buffer.start;
#endif
u->buffer.last = u->buffer.pos;
f->large_stderr = 1;
}
return NGX_AGAIN;
}
} else {
f->state = ngx_http_fastcgi_st_padding;
}
continue;
}
/* f->type == NGX_HTTP_FASTCGI_STDOUT */
#if (NGX_HTTP_CACHE)
if (f->large_stderr && r->cache) {
ssize_t len;
ngx_http_fastcgi_header_t *fh;
start = u->buffer.start + r->cache->header_start;
len = u->buffer.pos - start - 2 * sizeof(ngx_http_fastcgi_header_t);
/*
* A tail of large stderr output before HTTP header is placed
* in a cache file without a FastCGI record header.
* To workaround it we put a dummy FastCGI record header at the
* start of the stderr output or update r->cache_header_start,
* if there is no enough place for the record header.
*/
if (len >= 0) {
fh = (ngx_http_fastcgi_header_t *) start;
fh->version = 1;
fh->type = NGX_HTTP_FASTCGI_STDERR;
fh->request_id_hi = 0;
fh->request_id_lo = 1;
fh->content_length_hi = (u_char) ((len >> 8) & 0xff);
fh->content_length_lo = (u_char) (len & 0xff);
fh->padding_length = 0;
fh->reserved = 0;
} else {
r->cache->header_start += u->buffer.pos - start
- sizeof(ngx_http_fastcgi_header_t);
}
f->large_stderr = 0;
}
#endif
f->fastcgi_stdout = 1;
start = u->buffer.pos;
if (u->buffer.pos + f->length < u->buffer.last) {
/*
* set u->buffer.last to the end of the FastCGI record data
* for ngx_http_parse_header_line()
*/
last = u->buffer.last;
u->buffer.last = u->buffer.pos + f->length;
} else {
last = NULL;
}
for ( ;; ) {
part_start = u->buffer.pos;
part_end = u->buffer.last;
rc = ngx_http_parse_header_line(r, &u->buffer, 1);
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http fastcgi parser: %i", rc);
if (rc == NGX_AGAIN) {
break;
}
if (rc == NGX_OK) {
/* a header line has been parsed successfully */
h = ngx_list_push(&u->headers_in.headers);
if (h == NULL) {
return NGX_ERROR;
}
if (f->split_parts && f->split_parts->nelts) {
part = f->split_parts->elts;
size = u->buffer.pos - part_start;
for (i = 0; i < f->split_parts->nelts; i++) {
size += part[i].end - part[i].start;
}
p = ngx_pnalloc(r->pool, size);
if (p == NULL) {
h->hash = 0;
return NGX_ERROR;
}
buf.pos = p;
for (i = 0; i < f->split_parts->nelts; i++) {
p = ngx_cpymem(p, part[i].start,
part[i].end - part[i].start);
}
p = ngx_cpymem(p, part_start, u->buffer.pos - part_start);
buf.last = p;
f->split_parts->nelts = 0;
rc = ngx_http_parse_header_line(r, &buf, 1);
if (rc != NGX_OK) {
ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
"invalid header after joining "
"FastCGI records");
h->hash = 0;
return NGX_ERROR;
}
h->key.len = r->header_name_end - r->header_name_start;
h->key.data = r->header_name_start;
h->key.data[h->key.len] = '\0';
h->value.len = r->header_end - r->header_start;
h->value.data = r->header_start;
h->value.data[h->value.len] = '\0';
h->lowcase_key = ngx_pnalloc(r->pool, h->key.len);
if (h->lowcase_key == NULL) {
return NGX_ERROR;
}
} else {
h->key.len = r->header_name_end - r->header_name_start;
h->value.len = r->header_end - r->header_start;
h->key.data = ngx_pnalloc(r->pool,
h->key.len + 1 + h->value.len + 1
+ h->key.len);
if (h->key.data == NULL) {
h->hash = 0;
return NGX_ERROR;
}
h->value.data = h->key.data + h->key.len + 1;
h->lowcase_key = h->key.data + h->key.len + 1
+ h->value.len + 1;
ngx_memcpy(h->key.data, r->header_name_start, h->key.len);
h->key.data[h->key.len] = '\0';
ngx_memcpy(h->value.data, r->header_start, h->value.len);
h->value.data[h->value.len] = '\0';
}
h->hash = r->header_hash;
if (h->key.len == r->lowcase_index) {
ngx_memcpy(h->lowcase_key, r->lowcase_header, h->key.len);
} else {
ngx_strlow(h->lowcase_key, h->key.data, h->key.len);
}
hh = ngx_hash_find(&umcf->headers_in_hash, h->hash,
h->lowcase_key, h->key.len);
if (hh && hh->handler(r, h, hh->offset) != NGX_OK) {
return NGX_ERROR;
}
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http fastcgi header: \"%V: %V\"",
&h->key, &h->value);
if (u->buffer.pos < u->buffer.last) {
continue;
}
/* the end of the FastCGI record */
break;
}
if (rc == NGX_HTTP_PARSE_HEADER_DONE) {
/* a whole header has been parsed successfully */
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http fastcgi header done");
if (u->headers_in.status) {
status_line = &u->headers_in.status->value;
status = ngx_atoi(status_line->data, 3);
if (status == NGX_ERROR) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent invalid status \"%V\"",
status_line);
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
}
u->headers_in.status_n = status;
u->headers_in.status_line = *status_line;
} else if (u->headers_in.location) {
u->headers_in.status_n = 302;
ngx_str_set(&u->headers_in.status_line,
"302 Moved Temporarily");
} else {
u->headers_in.status_n = 200;
ngx_str_set(&u->headers_in.status_line, "200 OK");
}
if (u->state && u->state->status == 0) {
u->state->status = u->headers_in.status_n;
}
break;
}
/* there was error while a header line parsing */
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent invalid header");
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
}
if (last) {
u->buffer.last = last;
}
f->length -= u->buffer.pos - start;
if (f->length == 0) {
f->state = ngx_http_fastcgi_st_padding;
}
if (rc == NGX_HTTP_PARSE_HEADER_DONE) {
return NGX_OK;
}
if (rc == NGX_OK) {
continue;
}
/* rc == NGX_AGAIN */
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"upstream split a header line in FastCGI records");
if (f->split_parts == NULL) {
f->split_parts = ngx_array_create(r->pool, 1,
sizeof(ngx_http_fastcgi_split_part_t));
if (f->split_parts == NULL) {
return NGX_ERROR;
}
}
part = ngx_array_push(f->split_parts);
if (part == NULL) {
return NGX_ERROR;
}
part->start = part_start;
part->end = part_end;
if (u->buffer.pos < u->buffer.last) {
continue;
}
return NGX_AGAIN;
}
}
static ngx_int_t
ngx_http_fastcgi_input_filter_init(void *data)
{
ngx_http_request_t *r = data;
ngx_http_fastcgi_loc_conf_t *flcf;
flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);
r->upstream->pipe->length = flcf->keep_conn ?
(off_t) sizeof(ngx_http_fastcgi_header_t) : -1;
return NGX_OK;
}
static ngx_int_t
ngx_http_fastcgi_input_filter(ngx_event_pipe_t *p, ngx_buf_t *buf)
{
u_char *m, *msg;
ngx_int_t rc;
ngx_buf_t *b, **prev;
ngx_chain_t *cl;
ngx_http_request_t *r;
ngx_http_fastcgi_ctx_t *f;
ngx_http_fastcgi_loc_conf_t *flcf;
if (buf->pos == buf->last) {
return NGX_OK;
}
r = p->input_ctx;
f = ngx_http_get_module_ctx(r, ngx_http_fastcgi_module);
flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);
b = NULL;
prev = &buf->shadow;
f->pos = buf->pos;
f->last = buf->last;
for ( ;; ) {
if (f->state < ngx_http_fastcgi_st_data) {
rc = ngx_http_fastcgi_process_record(r, f);
if (rc == NGX_AGAIN) {
break;
}
if (rc == NGX_ERROR) {
return NGX_ERROR;
}
if (f->type == NGX_HTTP_FASTCGI_STDOUT && f->length == 0) {
f->state = ngx_http_fastcgi_st_padding;
if (!flcf->keep_conn) {
p->upstream_done = 1;
}
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, p->log, 0,
"http fastcgi closed stdout");
continue;
}
if (f->type == NGX_HTTP_FASTCGI_END_REQUEST) {
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, p->log, 0,
"http fastcgi sent end request");
if (!flcf->keep_conn) {
p->upstream_done = 1;
break;
}
continue;
}
}
if (f->state == ngx_http_fastcgi_st_padding) {
if (f->type == NGX_HTTP_FASTCGI_END_REQUEST) {
if (f->pos + f->padding < f->last) {
p->upstream_done = 1;
break;
}
if (f->pos + f->padding == f->last) {
p->upstream_done = 1;
r->upstream->keepalive = 1;
break;
}
f->padding -= f->last - f->pos;
break;
}
if (f->pos + f->padding < f->last) {
f->state = ngx_http_fastcgi_st_version;
f->pos += f->padding;
continue;
}
if (f->pos + f->padding == f->last) {
f->state = ngx_http_fastcgi_st_version;
break;
}
f->padding -= f->last - f->pos;
break;
}
/* f->state == ngx_http_fastcgi_st_data */
if (f->type == NGX_HTTP_FASTCGI_STDERR) {
if (f->length) {
if (f->pos == f->last) {
break;
}
msg = f->pos;
if (f->pos + f->length <= f->last) {
f->pos += f->length;
f->length = 0;
f->state = ngx_http_fastcgi_st_padding;
} else {
f->length -= f->last - f->pos;
f->pos = f->last;
}
for (m = f->pos - 1; msg < m; m--) {
if (*m != LF && *m != CR && *m != '.' && *m != ' ') {
break;
}
}
ngx_log_error(NGX_LOG_ERR, p->log, 0,
"FastCGI sent in stderr: \"%*s\"",
m + 1 - msg, msg);
} else {
f->state = ngx_http_fastcgi_st_padding;
}
continue;
}
if (f->type == NGX_HTTP_FASTCGI_END_REQUEST) {
if (f->pos + f->length <= f->last) {
f->state = ngx_http_fastcgi_st_padding;
f->pos += f->length;
continue;
}
f->length -= f->last - f->pos;
break;
}
/* f->type == NGX_HTTP_FASTCGI_STDOUT */
if (f->pos == f->last) {
break;
}
cl = ngx_chain_get_free_buf(p->pool, &p->free);
if (cl == NULL) {
return NGX_ERROR;
}
b = cl->buf;
ngx_memzero(b, sizeof(ngx_buf_t));
b->pos = f->pos;
b->start = buf->start;
b->end = buf->end;
b->tag = p->tag;
b->temporary = 1;
b->recycled = 1;
*prev = b;
prev = &b->shadow;
if (p->in) {
*p->last_in = cl;
} else {
p->in = cl;
}
p->last_in = &cl->next;
/* STUB */ b->num = buf->num;
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, p->log, 0,
"input buf #%d %p", b->num, b->pos);
if (f->pos + f->length <= f->last) {
f->state = ngx_http_fastcgi_st_padding;
f->pos += f->length;
b->last = f->pos;
continue;
}
f->length -= f->last - f->pos;
b->last = f->last;
break;
}
if (flcf->keep_conn) {
/* set p->length, minimal amount of data we want to see */
if (f->state < ngx_http_fastcgi_st_data) {
p->length = 1;
} else if (f->state == ngx_http_fastcgi_st_padding) {
p->length = f->padding;
} else {
/* ngx_http_fastcgi_st_data */
p->length = f->length;
}
}
if (b) {
b->shadow = buf;
b->last_shadow = 1;
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, p->log, 0,
"input buf %p %z", b->pos, b->last - b->pos);
return NGX_OK;
}
/* there is no data record in the buf, add it to free chain */
if (ngx_event_pipe_add_free_buf(p, buf) != NGX_OK) {
return NGX_ERROR;
}
return NGX_OK;
}
static ngx_int_t
ngx_http_fastcgi_non_buffered_filter(void *data, ssize_t bytes)
{
u_char *m, *msg;
ngx_int_t rc;
ngx_buf_t *b, *buf;
ngx_chain_t *cl, **ll;
ngx_http_request_t *r;
ngx_http_upstream_t *u;
ngx_http_fastcgi_ctx_t *f;
r = data;
f = ngx_http_get_module_ctx(r, ngx_http_fastcgi_module);
u = r->upstream;
buf = &u->buffer;
buf->pos = buf->last;
buf->last += bytes;
for (cl = u->out_bufs, ll = &u->out_bufs; cl; cl = cl->next) {
ll = &cl->next;
}
f->pos = buf->pos;
f->last = buf->last;
for ( ;; ) {
if (f->state < ngx_http_fastcgi_st_data) {
rc = ngx_http_fastcgi_process_record(r, f);
if (rc == NGX_AGAIN) {
break;
}
if (rc == NGX_ERROR) {
return NGX_ERROR;
}
if (f->type == NGX_HTTP_FASTCGI_STDOUT && f->length == 0) {
f->state = ngx_http_fastcgi_st_padding;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http fastcgi closed stdout");
continue;
}
}
if (f->state == ngx_http_fastcgi_st_padding) {
if (f->type == NGX_HTTP_FASTCGI_END_REQUEST) {
if (f->pos + f->padding < f->last) {
u->length = 0;
break;
}
if (f->pos + f->padding == f->last) {
u->length = 0;
u->keepalive = 1;
break;
}
f->padding -= f->last - f->pos;
break;
}
if (f->pos + f->padding < f->last) {
f->state = ngx_http_fastcgi_st_version;
f->pos += f->padding;
continue;
}
if (f->pos + f->padding == f->last) {
f->state = ngx_http_fastcgi_st_version;
break;
}
f->padding -= f->last - f->pos;
break;
}
/* f->state == ngx_http_fastcgi_st_data */
if (f->type == NGX_HTTP_FASTCGI_STDERR) {
if (f->length) {
if (f->pos == f->last) {
break;
}
msg = f->pos;
if (f->pos + f->length <= f->last) {
f->pos += f->length;
f->length = 0;
f->state = ngx_http_fastcgi_st_padding;
} else {
f->length -= f->last - f->pos;
f->pos = f->last;
}
for (m = f->pos - 1; msg < m; m--) {
if (*m != LF && *m != CR && *m != '.' && *m != ' ') {
break;
}
}
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"FastCGI sent in stderr: \"%*s\"",
m + 1 - msg, msg);
} else {
f->state = ngx_http_fastcgi_st_padding;
}
continue;
}
if (f->type == NGX_HTTP_FASTCGI_END_REQUEST) {
if (f->pos + f->length <= f->last) {
f->state = ngx_http_fastcgi_st_padding;
f->pos += f->length;
continue;
}
f->length -= f->last - f->pos;
break;
}
/* f->type == NGX_HTTP_FASTCGI_STDOUT */
if (f->pos == f->last) {
break;
}
cl = ngx_chain_get_free_buf(r->pool, &u->free_bufs);
if (cl == NULL) {
return NGX_ERROR;
}
*ll = cl;
ll = &cl->next;
b = cl->buf;
b->flush = 1;
b->memory = 1;
b->pos = f->pos;
b->tag = u->output.tag;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http fastcgi output buf %p", b->pos);
if (f->pos + f->length <= f->last) {
f->state = ngx_http_fastcgi_st_padding;
f->pos += f->length;
b->last = f->pos;
continue;
}
f->length -= f->last - f->pos;
b->last = f->last;
break;
}
return NGX_OK;
}
static ngx_int_t
ngx_http_fastcgi_process_record(ngx_http_request_t *r,
ngx_http_fastcgi_ctx_t *f)
{
u_char ch, *p;
ngx_http_fastcgi_state_e state;
state = f->state;
for (p = f->pos; p < f->last; p++) {
ch = *p;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http fastcgi record byte: %02Xd", ch);
switch (state) {
case ngx_http_fastcgi_st_version:
if (ch != 1) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent unsupported FastCGI "
"protocol version: %d", ch);
return NGX_ERROR;
}
state = ngx_http_fastcgi_st_type;
break;
case ngx_http_fastcgi_st_type:
switch (ch) {
case NGX_HTTP_FASTCGI_STDOUT:
case NGX_HTTP_FASTCGI_STDERR:
case NGX_HTTP_FASTCGI_END_REQUEST:
f->type = (ngx_uint_t) ch;
break;
default:
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent invalid FastCGI "
"record type: %d", ch);
return NGX_ERROR;
}
state = ngx_http_fastcgi_st_request_id_hi;
break;
/* we support the single request per connection */
case ngx_http_fastcgi_st_request_id_hi:
if (ch != 0) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent unexpected FastCGI "
"request id high byte: %d", ch);
return NGX_ERROR;
}
state = ngx_http_fastcgi_st_request_id_lo;
break;
case ngx_http_fastcgi_st_request_id_lo:
if (ch != 1) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent unexpected FastCGI "
"request id low byte: %d", ch);
return NGX_ERROR;
}
state = ngx_http_fastcgi_st_content_length_hi;
break;
case ngx_http_fastcgi_st_content_length_hi:
f->length = ch << 8;
state = ngx_http_fastcgi_st_content_length_lo;
break;
case ngx_http_fastcgi_st_content_length_lo:
f->length |= (size_t) ch;
state = ngx_http_fastcgi_st_padding_length;
break;
case ngx_http_fastcgi_st_padding_length:
f->padding = (size_t) ch;
state = ngx_http_fastcgi_st_reserved;
break;
case ngx_http_fastcgi_st_reserved:
state = ngx_http_fastcgi_st_data;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http fastcgi record length: %z", f->length);
f->pos = p + 1;
f->state = state;
return NGX_OK;
/* suppress warning */
case ngx_http_fastcgi_st_data:
case ngx_http_fastcgi_st_padding:
break;
}
}
f->pos = p;
f->state = state;
return NGX_AGAIN;
}
static void
ngx_http_fastcgi_abort_request(ngx_http_request_t *r)
{
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"abort http fastcgi request");
return;
}
static void
ngx_http_fastcgi_finalize_request(ngx_http_request_t *r, ngx_int_t rc)
{
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"finalize http fastcgi request");
return;
}
static ngx_int_t
ngx_http_fastcgi_add_variables(ngx_conf_t *cf)
{
ngx_http_variable_t *var, *v;
for (v = ngx_http_fastcgi_vars; v->name.len; v++) {
var = ngx_http_add_variable(cf, &v->name, v->flags);
if (var == NULL) {
return NGX_ERROR;
}
var->get_handler = v->get_handler;
var->data = v->data;
}
return NGX_OK;
}
static void *
ngx_http_fastcgi_create_main_conf(ngx_conf_t *cf)
{
ngx_http_fastcgi_main_conf_t *conf;
conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_fastcgi_main_conf_t));
if (conf == NULL) {
return NULL;
}
#if (NGX_HTTP_CACHE)
if (ngx_array_init(&conf->caches, cf->pool, 4,
sizeof(ngx_http_file_cache_t *))
!= NGX_OK)
{
return NULL;
}
#endif
return conf;
}
static void *
ngx_http_fastcgi_create_loc_conf(ngx_conf_t *cf)
{
ngx_http_fastcgi_loc_conf_t *conf;
conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_fastcgi_loc_conf_t));
if (conf == NULL) {
return NULL;
}
/*
* set by ngx_pcalloc():
*
* conf->upstream.bufs.num = 0;
* conf->upstream.ignore_headers = 0;
* conf->upstream.next_upstream = 0;
* conf->upstream.cache_zone = NULL;
* conf->upstream.cache_use_stale = 0;
* conf->upstream.cache_methods = 0;
* conf->upstream.temp_path = NULL;
* conf->upstream.hide_headers_hash = { NULL, 0 };
* conf->upstream.store_lengths = NULL;
* conf->upstream.store_values = NULL;
*
* conf->index.len = { 0, NULL };
*/
conf->upstream.store = NGX_CONF_UNSET;
conf->upstream.store_access = NGX_CONF_UNSET_UINT;
conf->upstream.next_upstream_tries = NGX_CONF_UNSET_UINT;
conf->upstream.buffering = NGX_CONF_UNSET;
conf->upstream.request_buffering = NGX_CONF_UNSET;
conf->upstream.ignore_client_abort = NGX_CONF_UNSET;
conf->upstream.force_ranges = NGX_CONF_UNSET;
conf->upstream.local = NGX_CONF_UNSET_PTR;
conf->upstream.connect_timeout = NGX_CONF_UNSET_MSEC;
conf->upstream.send_timeout = NGX_CONF_UNSET_MSEC;
conf->upstream.read_timeout = NGX_CONF_UNSET_MSEC;
conf->upstream.next_upstream_timeout = NGX_CONF_UNSET_MSEC;
conf->upstream.send_lowat = NGX_CONF_UNSET_SIZE;
conf->upstream.buffer_size = NGX_CONF_UNSET_SIZE;
conf->upstream.limit_rate = NGX_CONF_UNSET_SIZE;
conf->upstream.busy_buffers_size_conf = NGX_CONF_UNSET_SIZE;
conf->upstream.max_temp_file_size_conf = NGX_CONF_UNSET_SIZE;
conf->upstream.temp_file_write_size_conf = NGX_CONF_UNSET_SIZE;
conf->upstream.pass_request_headers = NGX_CONF_UNSET;
conf->upstream.pass_request_body = NGX_CONF_UNSET;
#if (NGX_HTTP_CACHE)
conf->upstream.cache = NGX_CONF_UNSET;
conf->upstream.cache_min_uses = NGX_CONF_UNSET_UINT;
conf->upstream.cache_max_range_offset = NGX_CONF_UNSET;
conf->upstream.cache_bypass = NGX_CONF_UNSET_PTR;
conf->upstream.no_cache = NGX_CONF_UNSET_PTR;
conf->upstream.cache_valid = NGX_CONF_UNSET_PTR;
conf->upstream.cache_lock = NGX_CONF_UNSET;
conf->upstream.cache_lock_timeout = NGX_CONF_UNSET_MSEC;
conf->upstream.cache_lock_age = NGX_CONF_UNSET_MSEC;
conf->upstream.cache_revalidate = NGX_CONF_UNSET;
conf->upstream.cache_background_update = NGX_CONF_UNSET;
#endif
conf->upstream.hide_headers = NGX_CONF_UNSET_PTR;
conf->upstream.pass_headers = NGX_CONF_UNSET_PTR;
conf->upstream.intercept_errors = NGX_CONF_UNSET;
/* "fastcgi_cyclic_temp_file" is disabled */
conf->upstream.cyclic_temp_file = 0;
conf->upstream.change_buffering = 1;
conf->catch_stderr = NGX_CONF_UNSET_PTR;
conf->keep_conn = NGX_CONF_UNSET;
ngx_str_set(&conf->upstream.module, "fastcgi");
return conf;
}
static char *
ngx_http_fastcgi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
{
ngx_http_fastcgi_loc_conf_t *prev = parent;
ngx_http_fastcgi_loc_conf_t *conf = child;
size_t size;
ngx_int_t rc;
ngx_hash_init_t hash;
ngx_http_core_loc_conf_t *clcf;
#if (NGX_HTTP_CACHE)
if (conf->upstream.store > 0) {
conf->upstream.cache = 0;
}
if (conf->upstream.cache > 0) {
conf->upstream.store = 0;
}
#endif
if (conf->upstream.store == NGX_CONF_UNSET) {
ngx_conf_merge_value(conf->upstream.store,
prev->upstream.store, 0);
conf->upstream.store_lengths = prev->upstream.store_lengths;
conf->upstream.store_values = prev->upstream.store_values;
}
ngx_conf_merge_uint_value(conf->upstream.store_access,
prev->upstream.store_access, 0600);
ngx_conf_merge_uint_value(conf->upstream.next_upstream_tries,
prev->upstream.next_upstream_tries, 0);
ngx_conf_merge_value(conf->upstream.buffering,
prev->upstream.buffering, 1);
ngx_conf_merge_value(conf->upstream.request_buffering,
prev->upstream.request_buffering, 1);
ngx_conf_merge_value(conf->upstream.ignore_client_abort,
prev->upstream.ignore_client_abort, 0);
ngx_conf_merge_value(conf->upstream.force_ranges,
prev->upstream.force_ranges, 0);
ngx_conf_merge_ptr_value(conf->upstream.local,
prev->upstream.local, NULL);
ngx_conf_merge_msec_value(conf->upstream.connect_timeout,
prev->upstream.connect_timeout, 60000);
ngx_conf_merge_msec_value(conf->upstream.send_timeout,
prev->upstream.send_timeout, 60000);
ngx_conf_merge_msec_value(conf->upstream.read_timeout,
prev->upstream.read_timeout, 60000);
ngx_conf_merge_msec_value(conf->upstream.next_upstream_timeout,
prev->upstream.next_upstream_timeout, 0);
ngx_conf_merge_size_value(conf->upstream.send_lowat,
prev->upstream.send_lowat, 0);
ngx_conf_merge_size_value(conf->upstream.buffer_size,
prev->upstream.buffer_size,
(size_t) ngx_pagesize);
ngx_conf_merge_size_value(conf->upstream.limit_rate,
prev->upstream.limit_rate, 0);
ngx_conf_merge_bufs_value(conf->upstream.bufs, prev->upstream.bufs,
8, ngx_pagesize);
if (conf->upstream.bufs.num < 2) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"there must be at least 2 \"fastcgi_buffers\"");
return NGX_CONF_ERROR;
}
size = conf->upstream.buffer_size;
if (size < conf->upstream.bufs.size) {
size = conf->upstream.bufs.size;
}
ngx_conf_merge_size_value(conf->upstream.busy_buffers_size_conf,
prev->upstream.busy_buffers_size_conf,
NGX_CONF_UNSET_SIZE);
if (conf->upstream.busy_buffers_size_conf == NGX_CONF_UNSET_SIZE) {
conf->upstream.busy_buffers_size = 2 * size;
} else {
conf->upstream.busy_buffers_size =
conf->upstream.busy_buffers_size_conf;
}
if (conf->upstream.busy_buffers_size < size) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"\"fastcgi_busy_buffers_size\" must be equal to or greater than "
"the maximum of the value of \"fastcgi_buffer_size\" and "
"one of the \"fastcgi_buffers\"");
return NGX_CONF_ERROR;
}
if (conf->upstream.busy_buffers_size
> (conf->upstream.bufs.num - 1) * conf->upstream.bufs.size)
{
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"\"fastcgi_busy_buffers_size\" must be less than "
"the size of all \"fastcgi_buffers\" minus one buffer");
return NGX_CONF_ERROR;
}
ngx_conf_merge_size_value(conf->upstream.temp_file_write_size_conf,
prev->upstream.temp_file_write_size_conf,
NGX_CONF_UNSET_SIZE);
if (conf->upstream.temp_file_write_size_conf == NGX_CONF_UNSET_SIZE) {
conf->upstream.temp_file_write_size = 2 * size;
} else {
conf->upstream.temp_file_write_size =
conf->upstream.temp_file_write_size_conf;
}
if (conf->upstream.temp_file_write_size < size) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"\"fastcgi_temp_file_write_size\" must be equal to or greater "
"than the maximum of the value of \"fastcgi_buffer_size\" and "
"one of the \"fastcgi_buffers\"");
return NGX_CONF_ERROR;
}
ngx_conf_merge_size_value(conf->upstream.max_temp_file_size_conf,
prev->upstream.max_temp_file_size_conf,
NGX_CONF_UNSET_SIZE);
if (conf->upstream.max_temp_file_size_conf == NGX_CONF_UNSET_SIZE) {
conf->upstream.max_temp_file_size = 1024 * 1024 * 1024;
} else {
conf->upstream.max_temp_file_size =
conf->upstream.max_temp_file_size_conf;
}
if (conf->upstream.max_temp_file_size != 0
&& conf->upstream.max_temp_file_size < size)
{
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"\"fastcgi_max_temp_file_size\" must be equal to zero to disable "
"temporary files usage or must be equal to or greater than "
"the maximum of the value of \"fastcgi_buffer_size\" and "
"one of the \"fastcgi_buffers\"");
return NGX_CONF_ERROR;
}
ngx_conf_merge_bitmask_value(conf->upstream.ignore_headers,
prev->upstream.ignore_headers,
NGX_CONF_BITMASK_SET);
ngx_conf_merge_bitmask_value(conf->upstream.next_upstream,
prev->upstream.next_upstream,
(NGX_CONF_BITMASK_SET
|NGX_HTTP_UPSTREAM_FT_ERROR
|NGX_HTTP_UPSTREAM_FT_TIMEOUT));
if (conf->upstream.next_upstream & NGX_HTTP_UPSTREAM_FT_OFF) {
conf->upstream.next_upstream = NGX_CONF_BITMASK_SET
|NGX_HTTP_UPSTREAM_FT_OFF;
}
if (ngx_conf_merge_path_value(cf, &conf->upstream.temp_path,
prev->upstream.temp_path,
&ngx_http_fastcgi_temp_path)
!= NGX_OK)
{
return NGX_CONF_ERROR;
}
#if (NGX_HTTP_CACHE)
if (conf->upstream.cache == NGX_CONF_UNSET) {
ngx_conf_merge_value(conf->upstream.cache,
prev->upstream.cache, 0);
conf->upstream.cache_zone = prev->upstream.cache_zone;
conf->upstream.cache_value = prev->upstream.cache_value;
}
if (conf->upstream.cache_zone && conf->upstream.cache_zone->data == NULL) {
ngx_shm_zone_t *shm_zone;
shm_zone = conf->upstream.cache_zone;
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"\"fastcgi_cache\" zone \"%V\" is unknown",
&shm_zone->shm.name);
return NGX_CONF_ERROR;
}
ngx_conf_merge_uint_value(conf->upstream.cache_min_uses,
prev->upstream.cache_min_uses, 1);
ngx_conf_merge_off_value(conf->upstream.cache_max_range_offset,
prev->upstream.cache_max_range_offset,
NGX_MAX_OFF_T_VALUE);
ngx_conf_merge_bitmask_value(conf->upstream.cache_use_stale,
prev->upstream.cache_use_stale,
(NGX_CONF_BITMASK_SET
|NGX_HTTP_UPSTREAM_FT_OFF));
if (conf->upstream.cache_use_stale & NGX_HTTP_UPSTREAM_FT_OFF) {
conf->upstream.cache_use_stale = NGX_CONF_BITMASK_SET
|NGX_HTTP_UPSTREAM_FT_OFF;
}
if (conf->upstream.cache_use_stale & NGX_HTTP_UPSTREAM_FT_ERROR) {
conf->upstream.cache_use_stale |= NGX_HTTP_UPSTREAM_FT_NOLIVE;
}
if (conf->upstream.cache_methods == 0) {
conf->upstream.cache_methods = prev->upstream.cache_methods;
}
conf->upstream.cache_methods |= NGX_HTTP_GET|NGX_HTTP_HEAD;
ngx_conf_merge_ptr_value(conf->upstream.cache_bypass,
prev->upstream.cache_bypass, NULL);
ngx_conf_merge_ptr_value(conf->upstream.no_cache,
prev->upstream.no_cache, NULL);
ngx_conf_merge_ptr_value(conf->upstream.cache_valid,
prev->upstream.cache_valid, NULL);
if (conf->cache_key.value.data == NULL) {
conf->cache_key = prev->cache_key;
}
if (conf->upstream.cache && conf->cache_key.value.data == NULL) {
ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
"no \"fastcgi_cache_key\" for \"fastcgi_cache\"");
}
ngx_conf_merge_value(conf->upstream.cache_lock,
prev->upstream.cache_lock, 0);
ngx_conf_merge_msec_value(conf->upstream.cache_lock_timeout,
prev->upstream.cache_lock_timeout, 5000);
ngx_conf_merge_msec_value(conf->upstream.cache_lock_age,
prev->upstream.cache_lock_age, 5000);
ngx_conf_merge_value(conf->upstream.cache_revalidate,
prev->upstream.cache_revalidate, 0);
ngx_conf_merge_value(conf->upstream.cache_background_update,
prev->upstream.cache_background_update, 0);
#endif
ngx_conf_merge_value(conf->upstream.pass_request_headers,
prev->upstream.pass_request_headers, 1);
ngx_conf_merge_value(conf->upstream.pass_request_body,
prev->upstream.pass_request_body, 1);
ngx_conf_merge_value(conf->upstream.intercept_errors,
prev->upstream.intercept_errors, 0);
ngx_conf_merge_ptr_value(conf->catch_stderr, prev->catch_stderr, NULL);
ngx_conf_merge_value(conf->keep_conn, prev->keep_conn, 0);
ngx_conf_merge_str_value(conf->index, prev->index, "");
hash.max_size = 512;
hash.bucket_size = ngx_align(64, ngx_cacheline_size);
hash.name = "fastcgi_hide_headers_hash";
if (ngx_http_upstream_hide_headers_hash(cf, &conf->upstream,
&prev->upstream, ngx_http_fastcgi_hide_headers, &hash)
!= NGX_OK)
{
return NGX_CONF_ERROR;
}
clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
if (clcf->noname
&& conf->upstream.upstream == NULL && conf->fastcgi_lengths == NULL)
{
conf->upstream.upstream = prev->upstream.upstream;
conf->fastcgi_lengths = prev->fastcgi_lengths;
conf->fastcgi_values = prev->fastcgi_values;
}
if (clcf->lmt_excpt && clcf->handler == NULL
&& (conf->upstream.upstream || conf->fastcgi_lengths))
{
clcf->handler = ngx_http_fastcgi_handler;
}
#if (NGX_PCRE)
if (conf->split_regex == NULL) {
conf->split_regex = prev->split_regex;
conf->split_name = prev->split_name;
}
#endif
if (conf->params_source == NULL) {
conf->params = prev->params;
#if (NGX_HTTP_CACHE)
conf->params_cache = prev->params_cache;
#endif
conf->params_source = prev->params_source;
}
rc = ngx_http_fastcgi_init_params(cf, conf, &conf->params, NULL);
if (rc != NGX_OK) {
return NGX_CONF_ERROR;
}
#if (NGX_HTTP_CACHE)
if (conf->upstream.cache) {
rc = ngx_http_fastcgi_init_params(cf, conf, &conf->params_cache,
ngx_http_fastcgi_cache_headers);
if (rc != NGX_OK) {
return NGX_CONF_ERROR;
}
}
#endif
/*
* special handling to preserve conf->params in the "http" section
* to inherit it to all servers
*/
if (prev->params.hash.buckets == NULL
&& conf->params_source == prev->params_source)
{
prev->params = conf->params;
#if (NGX_HTTP_CACHE)
prev->params_cache = conf->params_cache;
#endif
}
return NGX_CONF_OK;
}
static ngx_int_t
ngx_http_fastcgi_init_params(ngx_conf_t *cf, ngx_http_fastcgi_loc_conf_t *conf,
ngx_http_fastcgi_params_t *params, ngx_keyval_t *default_params)
{
u_char *p;
size_t size;
uintptr_t *code;
ngx_uint_t i, nsrc;
ngx_array_t headers_names, params_merged;
ngx_keyval_t *h;
ngx_hash_key_t *hk;
ngx_hash_init_t hash;
ngx_http_upstream_param_t *src, *s;
ngx_http_script_compile_t sc;
ngx_http_script_copy_code_t *copy;
if (params->hash.buckets) {
return NGX_OK;
}
if (conf->params_source == NULL && default_params == NULL) {
params->hash.buckets = (void *) 1;
return NGX_OK;
}
params->lengths = ngx_array_create(cf->pool, 64, 1);
if (params->lengths == NULL) {
return NGX_ERROR;
}
params->values = ngx_array_create(cf->pool, 512, 1);
if (params->values == NULL) {
return NGX_ERROR;
}
if (ngx_array_init(&headers_names, cf->temp_pool, 4, sizeof(ngx_hash_key_t))
!= NGX_OK)
{
return NGX_ERROR;
}
if (conf->params_source) {
src = conf->params_source->elts;
nsrc = conf->params_source->nelts;
} else {
src = NULL;
nsrc = 0;
}
if (default_params) {
if (ngx_array_init(¶ms_merged, cf->temp_pool, 4,
sizeof(ngx_http_upstream_param_t))
!= NGX_OK)
{
return NGX_ERROR;
}
for (i = 0; i < nsrc; i++) {
s = ngx_array_push(¶ms_merged);
if (s == NULL) {
return NGX_ERROR;
}
*s = src[i];
}
h = default_params;
while (h->key.len) {
src = params_merged.elts;
nsrc = params_merged.nelts;
for (i = 0; i < nsrc; i++) {
if (ngx_strcasecmp(h->key.data, src[i].key.data) == 0) {
goto next;
}
}
s = ngx_array_push(¶ms_merged);
if (s == NULL) {
return NGX_ERROR;
}
s->key = h->key;
s->value = h->value;
s->skip_empty = 1;
next:
h++;
}
src = params_merged.elts;
nsrc = params_merged.nelts;
}
for (i = 0; i < nsrc; i++) {
if (src[i].key.len > sizeof("HTTP_") - 1
&& ngx_strncmp(src[i].key.data, "HTTP_", sizeof("HTTP_") - 1) == 0)
{
hk = ngx_array_push(&headers_names);
if (hk == NULL) {
return NGX_ERROR;
}
hk->key.len = src[i].key.len - 5;
hk->key.data = src[i].key.data + 5;
hk->key_hash = ngx_hash_key_lc(hk->key.data, hk->key.len);
hk->value = (void *) 1;
if (src[i].value.len == 0) {
continue;
}
}
copy = ngx_array_push_n(params->lengths,
sizeof(ngx_http_script_copy_code_t));
if (copy == NULL) {
return NGX_ERROR;
}
copy->code = (ngx_http_script_code_pt) ngx_http_script_copy_len_code;
copy->len = src[i].key.len;
copy = ngx_array_push_n(params->lengths,
sizeof(ngx_http_script_copy_code_t));
if (copy == NULL) {
return NGX_ERROR;
}
copy->code = (ngx_http_script_code_pt) ngx_http_script_copy_len_code;
copy->len = src[i].skip_empty;
size = (sizeof(ngx_http_script_copy_code_t)
+ src[i].key.len + sizeof(uintptr_t) - 1)
& ~(sizeof(uintptr_t) - 1);
copy = ngx_array_push_n(params->values, size);
if (copy == NULL) {
return NGX_ERROR;
}
copy->code = ngx_http_script_copy_code;
copy->len = src[i].key.len;
p = (u_char *) copy + sizeof(ngx_http_script_copy_code_t);
ngx_memcpy(p, src[i].key.data, src[i].key.len);
ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
sc.cf = cf;
sc.source = &src[i].value;
sc.flushes = ¶ms->flushes;
sc.lengths = ¶ms->lengths;
sc.values = ¶ms->values;
if (ngx_http_script_compile(&sc) != NGX_OK) {
return NGX_ERROR;
}
code = ngx_array_push_n(params->lengths, sizeof(uintptr_t));
if (code == NULL) {
return NGX_ERROR;
}
*code = (uintptr_t) NULL;
code = ngx_array_push_n(params->values, sizeof(uintptr_t));
if (code == NULL) {
return NGX_ERROR;
}
*code = (uintptr_t) NULL;
}
code = ngx_array_push_n(params->lengths, sizeof(uintptr_t));
if (code == NULL) {
return NGX_ERROR;
}
*code = (uintptr_t) NULL;
params->number = headers_names.nelts;
hash.hash = ¶ms->hash;
hash.key = ngx_hash_key_lc;
hash.max_size = 512;
hash.bucket_size = 64;
hash.name = "fastcgi_params_hash";
hash.pool = cf->pool;
hash.temp_pool = NULL;
return ngx_hash_init(&hash, headers_names.elts, headers_names.nelts);
}
static ngx_int_t
ngx_http_fastcgi_script_name_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
u_char *p;
ngx_http_fastcgi_ctx_t *f;
ngx_http_fastcgi_loc_conf_t *flcf;
flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);
f = ngx_http_fastcgi_split(r, flcf);
if (f == NULL) {
return NGX_ERROR;
}
if (f->script_name.len == 0
|| f->script_name.data[f->script_name.len - 1] != '/')
{
v->len = f->script_name.len;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = f->script_name.data;
return NGX_OK;
}
v->len = f->script_name.len + flcf->index.len;
v->data = ngx_pnalloc(r->pool, v->len);
if (v->data == NULL) {
return NGX_ERROR;
}
p = ngx_copy(v->data, f->script_name.data, f->script_name.len);
ngx_memcpy(p, flcf->index.data, flcf->index.len);
return NGX_OK;
}
static ngx_int_t
ngx_http_fastcgi_path_info_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
ngx_http_fastcgi_ctx_t *f;
ngx_http_fastcgi_loc_conf_t *flcf;
flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);
f = ngx_http_fastcgi_split(r, flcf);
if (f == NULL) {
return NGX_ERROR;
}
v->len = f->path_info.len;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = f->path_info.data;
return NGX_OK;
}
static ngx_http_fastcgi_ctx_t *
ngx_http_fastcgi_split(ngx_http_request_t *r, ngx_http_fastcgi_loc_conf_t *flcf)
{
ngx_http_fastcgi_ctx_t *f;
#if (NGX_PCRE)
ngx_int_t n;
int captures[(1 + 2) * 3];
f = ngx_http_get_module_ctx(r, ngx_http_fastcgi_module);
if (f == NULL) {
f = ngx_pcalloc(r->pool, sizeof(ngx_http_fastcgi_ctx_t));
if (f == NULL) {
return NULL;
}
ngx_http_set_ctx(r, f, ngx_http_fastcgi_module);
}
if (f->script_name.len) {
return f;
}
if (flcf->split_regex == NULL) {
f->script_name = r->uri;
return f;
}
n = ngx_regex_exec(flcf->split_regex, &r->uri, captures, (1 + 2) * 3);
if (n >= 0) { /* match */
f->script_name.len = captures[3] - captures[2];
f->script_name.data = r->uri.data + captures[2];
f->path_info.len = captures[5] - captures[4];
f->path_info.data = r->uri.data + captures[4];
return f;
}
if (n == NGX_REGEX_NO_MATCHED) {
f->script_name = r->uri;
return f;
}
ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
ngx_regex_exec_n " failed: %i on \"%V\" using \"%V\"",
n, &r->uri, &flcf->split_name);
return NULL;
#else
f = ngx_http_get_module_ctx(r, ngx_http_fastcgi_module);
if (f == NULL) {
f = ngx_pcalloc(r->pool, sizeof(ngx_http_fastcgi_ctx_t));
if (f == NULL) {
return NULL;
}
ngx_http_set_ctx(r, f, ngx_http_fastcgi_module);
}
f->script_name = r->uri;
return f;
#endif
}
static char *
ngx_http_fastcgi_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_fastcgi_loc_conf_t *flcf = conf;
ngx_url_t u;
ngx_str_t *value, *url;
ngx_uint_t n;
ngx_http_core_loc_conf_t *clcf;
ngx_http_script_compile_t sc;
if (flcf->upstream.upstream || flcf->fastcgi_lengths) {
return "is duplicate";
}
clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
clcf->handler = ngx_http_fastcgi_handler;
if (clcf->name.data[clcf->name.len - 1] == '/') {
clcf->auto_redirect = 1;
}
value = cf->args->elts;
url = &value[1];
n = ngx_http_script_variables_count(url);
if (n) {
ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
sc.cf = cf;
sc.source = url;
sc.lengths = &flcf->fastcgi_lengths;
sc.values = &flcf->fastcgi_values;
sc.variables = n;
sc.complete_lengths = 1;
sc.complete_values = 1;
if (ngx_http_script_compile(&sc) != NGX_OK) {
return NGX_CONF_ERROR;
}
return NGX_CONF_OK;
}
ngx_memzero(&u, sizeof(ngx_url_t));
u.url = value[1];
u.no_resolve = 1;
flcf->upstream.upstream = ngx_http_upstream_add(cf, &u, 0);
if (flcf->upstream.upstream == NULL) {
return NGX_CONF_ERROR;
}
return NGX_CONF_OK;
}
static char *
ngx_http_fastcgi_split_path_info(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
#if (NGX_PCRE)
ngx_http_fastcgi_loc_conf_t *flcf = conf;
ngx_str_t *value;
ngx_regex_compile_t rc;
u_char errstr[NGX_MAX_CONF_ERRSTR];
value = cf->args->elts;
flcf->split_name = value[1];
ngx_memzero(&rc, sizeof(ngx_regex_compile_t));
rc.pattern = value[1];
rc.pool = cf->pool;
rc.err.len = NGX_MAX_CONF_ERRSTR;
rc.err.data = errstr;
if (ngx_regex_compile(&rc) != NGX_OK) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%V", &rc.err);
return NGX_CONF_ERROR;
}
if (rc.captures != 2) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"pattern \"%V\" must have 2 captures", &value[1]);
return NGX_CONF_ERROR;
}
flcf->split_regex = rc.regex;
return NGX_CONF_OK;
#else
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"\"%V\" requires PCRE library", &cmd->name);
return NGX_CONF_ERROR;
#endif
}
static char *
ngx_http_fastcgi_store(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_fastcgi_loc_conf_t *flcf = conf;
ngx_str_t *value;
ngx_http_script_compile_t sc;
if (flcf->upstream.store != NGX_CONF_UNSET) {
return "is duplicate";
}
value = cf->args->elts;
if (ngx_strcmp(value[1].data, "off") == 0) {
flcf->upstream.store = 0;
return NGX_CONF_OK;
}
#if (NGX_HTTP_CACHE)
if (flcf->upstream.cache > 0) {
return "is incompatible with \"fastcgi_cache\"";
}
#endif
flcf->upstream.store = 1;
if (ngx_strcmp(value[1].data, "on") == 0) {
return NGX_CONF_OK;
}
/* include the terminating '\0' into script */
value[1].len++;
ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
sc.cf = cf;
sc.source = &value[1];
sc.lengths = &flcf->upstream.store_lengths;
sc.values = &flcf->upstream.store_values;
sc.variables = ngx_http_script_variables_count(&value[1]);
sc.complete_lengths = 1;
sc.complete_values = 1;
if (ngx_http_script_compile(&sc) != NGX_OK) {
return NGX_CONF_ERROR;
}
return NGX_CONF_OK;
}
#if (NGX_HTTP_CACHE)
static char *
ngx_http_fastcgi_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_fastcgi_loc_conf_t *flcf = conf;
ngx_str_t *value;
ngx_http_complex_value_t cv;
ngx_http_compile_complex_value_t ccv;
value = cf->args->elts;
if (flcf->upstream.cache != NGX_CONF_UNSET) {
return "is duplicate";
}
if (ngx_strcmp(value[1].data, "off") == 0) {
flcf->upstream.cache = 0;
return NGX_CONF_OK;
}
if (flcf->upstream.store > 0) {
return "is incompatible with \"fastcgi_store\"";
}
flcf->upstream.cache = 1;
ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
ccv.cf = cf;
ccv.value = &value[1];
ccv.complex_value = &cv;
if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
return NGX_CONF_ERROR;
}
if (cv.lengths != NULL) {
flcf->upstream.cache_value = ngx_palloc(cf->pool,
sizeof(ngx_http_complex_value_t));
if (flcf->upstream.cache_value == NULL) {
return NGX_CONF_ERROR;
}
*flcf->upstream.cache_value = cv;
return NGX_CONF_OK;
}
flcf->upstream.cache_zone = ngx_shared_memory_add(cf, &value[1], 0,
&ngx_http_fastcgi_module);
if (flcf->upstream.cache_zone == NULL) {
return NGX_CONF_ERROR;
}
return NGX_CONF_OK;
}
static char *
ngx_http_fastcgi_cache_key(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_fastcgi_loc_conf_t *flcf = conf;
ngx_str_t *value;
ngx_http_compile_complex_value_t ccv;
value = cf->args->elts;
if (flcf->cache_key.value.data) {
return "is duplicate";
}
ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
ccv.cf = cf;
ccv.value = &value[1];
ccv.complex_value = &flcf->cache_key;
if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
return NGX_CONF_ERROR;
}
return NGX_CONF_OK;
}
#endif
static char *
ngx_http_fastcgi_lowat_check(ngx_conf_t *cf, void *post, void *data)
{
#if (NGX_FREEBSD)
ssize_t *np = data;
if ((u_long) *np >= ngx_freebsd_net_inet_tcp_sendspace) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"\"fastcgi_send_lowat\" must be less than %d "
"(sysctl net.inet.tcp.sendspace)",
ngx_freebsd_net_inet_tcp_sendspace);
return NGX_CONF_ERROR;
}
#elif !(NGX_HAVE_SO_SNDLOWAT)
ssize_t *np = data;
ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
"\"fastcgi_send_lowat\" is not supported, ignored");
*np = 0;
#endif
return NGX_CONF_OK;
}
nginx-1.14.0/src/http/modules/ngx_http_flv_module.c 000644 001751 001751 00000014737 13265410474 023561 0 ustar 00mdounin mdounin 000000 000000
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#include
#include
#include
static char *ngx_http_flv(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static ngx_command_t ngx_http_flv_commands[] = {
{ ngx_string("flv"),
NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,
ngx_http_flv,
0,
0,
NULL },
ngx_null_command
};
static u_char ngx_flv_header[] = "FLV\x1\x5\0\0\0\x9\0\0\0\0";
static ngx_http_module_t ngx_http_flv_module_ctx = {
NULL, /* preconfiguration */
NULL, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
NULL, /* create location configuration */
NULL /* merge location configuration */
};
ngx_module_t ngx_http_flv_module = {
NGX_MODULE_V1,
&ngx_http_flv_module_ctx, /* module context */
ngx_http_flv_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static ngx_int_t
ngx_http_flv_handler(ngx_http_request_t *r)
{
u_char *last;
off_t start, len;
size_t root;
ngx_int_t rc;
ngx_uint_t level, i;
ngx_str_t path, value;
ngx_log_t *log;
ngx_buf_t *b;
ngx_chain_t out[2];
ngx_open_file_info_t of;
ngx_http_core_loc_conf_t *clcf;
if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) {
return NGX_HTTP_NOT_ALLOWED;
}
if (r->uri.data[r->uri.len - 1] == '/') {
return NGX_DECLINED;
}
rc = ngx_http_discard_request_body(r);
if (rc != NGX_OK) {
return rc;
}
last = ngx_http_map_uri_to_path(r, &path, &root, 0);
if (last == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
log = r->connection->log;
path.len = last - path.data;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0,
"http flv filename: \"%V\"", &path);
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
ngx_memzero(&of, sizeof(ngx_open_file_info_t));
of.read_ahead = clcf->read_ahead;
of.directio = clcf->directio;
of.valid = clcf->open_file_cache_valid;
of.min_uses = clcf->open_file_cache_min_uses;
of.errors = clcf->open_file_cache_errors;
of.events = clcf->open_file_cache_events;
if (ngx_http_set_disable_symlinks(r, clcf, &path, &of) != NGX_OK) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)
!= NGX_OK)
{
switch (of.err) {
case 0:
return NGX_HTTP_INTERNAL_SERVER_ERROR;
case NGX_ENOENT:
case NGX_ENOTDIR:
case NGX_ENAMETOOLONG:
level = NGX_LOG_ERR;
rc = NGX_HTTP_NOT_FOUND;
break;
case NGX_EACCES:
#if (NGX_HAVE_OPENAT)
case NGX_EMLINK:
case NGX_ELOOP:
#endif
level = NGX_LOG_ERR;
rc = NGX_HTTP_FORBIDDEN;
break;
default:
level = NGX_LOG_CRIT;
rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
break;
}
if (rc != NGX_HTTP_NOT_FOUND || clcf->log_not_found) {
ngx_log_error(level, log, of.err,
"%s \"%s\" failed", of.failed, path.data);
}
return rc;
}
if (!of.is_file) {
if (ngx_close_file(of.fd) == NGX_FILE_ERROR) {
ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
ngx_close_file_n " \"%s\" failed", path.data);
}
return NGX_DECLINED;
}
r->root_tested = !r->error_page;
start = 0;
len = of.size;
i = 1;
if (r->args.len) {
if (ngx_http_arg(r, (u_char *) "start", 5, &value) == NGX_OK) {
start = ngx_atoof(value.data, value.len);
if (start == NGX_ERROR || start >= len) {
start = 0;
}
if (start) {
len = sizeof(ngx_flv_header) - 1 + len - start;
i = 0;
}
}
}
log->action = "sending flv to client";
r->headers_out.status = NGX_HTTP_OK;
r->headers_out.content_length_n = len;
r->headers_out.last_modified_time = of.mtime;
if (ngx_http_set_etag(r) != NGX_OK) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
if (ngx_http_set_content_type(r) != NGX_OK) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
if (i == 0) {
b = ngx_calloc_buf(r->pool);
if (b == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
b->pos = ngx_flv_header;
b->last = ngx_flv_header + sizeof(ngx_flv_header) - 1;
b->memory = 1;
out[0].buf = b;
out[0].next = &out[1];
}
b = ngx_calloc_buf(r->pool);
if (b == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
b->file = ngx_pcalloc(r->pool, sizeof(ngx_file_t));
if (b->file == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
r->allow_ranges = 1;
rc = ngx_http_send_header(r);
if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
return rc;
}
b->file_pos = start;
b->file_last = of.size;
b->in_file = b->file_last ? 1: 0;
b->last_buf = (r == r->main) ? 1 : 0;
b->last_in_chain = 1;
b->file->fd = of.fd;
b->file->name = path;
b->file->log = log;
b->file->directio = of.is_directio;
out[1].buf = b;
out[1].next = NULL;
return ngx_http_output_filter(r, &out[i]);
}
static char *
ngx_http_flv(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_core_loc_conf_t *clcf;
clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
clcf->handler = ngx_http_flv_handler;
return NGX_CONF_OK;
}
nginx-1.14.0/src/http/modules/ngx_http_geo_module.c 000644 001751 001751 00000125401 13265410474 023533 0 ustar 00mdounin mdounin 000000 000000
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#include
#include
#include
typedef struct {
ngx_http_variable_value_t *value;
u_short start;
u_short end;
} ngx_http_geo_range_t;
typedef struct {
ngx_radix_tree_t *tree;
#if (NGX_HAVE_INET6)
ngx_radix_tree_t *tree6;
#endif
} ngx_http_geo_trees_t;
typedef struct {
ngx_http_geo_range_t **low;
ngx_http_variable_value_t *default_value;
} ngx_http_geo_high_ranges_t;
typedef struct {
ngx_str_node_t sn;
ngx_http_variable_value_t *value;
size_t offset;
} ngx_http_geo_variable_value_node_t;
typedef struct {
ngx_http_variable_value_t *value;
ngx_str_t *net;
ngx_http_geo_high_ranges_t high;
ngx_radix_tree_t *tree;
#if (NGX_HAVE_INET6)
ngx_radix_tree_t *tree6;
#endif
ngx_rbtree_t rbtree;
ngx_rbtree_node_t sentinel;
ngx_array_t *proxies;
ngx_pool_t *pool;
ngx_pool_t *temp_pool;
size_t data_size;
ngx_str_t include_name;
ngx_uint_t includes;
ngx_uint_t entries;
unsigned ranges:1;
unsigned outside_entries:1;
unsigned allow_binary_include:1;
unsigned binary_include:1;
unsigned proxy_recursive:1;
} ngx_http_geo_conf_ctx_t;
typedef struct {
union {
ngx_http_geo_trees_t trees;
ngx_http_geo_high_ranges_t high;
} u;
ngx_array_t *proxies;
unsigned proxy_recursive:1;
ngx_int_t index;
} ngx_http_geo_ctx_t;
static ngx_int_t ngx_http_geo_addr(ngx_http_request_t *r,
ngx_http_geo_ctx_t *ctx, ngx_addr_t *addr);
static ngx_int_t ngx_http_geo_real_addr(ngx_http_request_t *r,
ngx_http_geo_ctx_t *ctx, ngx_addr_t *addr);
static char *ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static char *ngx_http_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf);
static char *ngx_http_geo_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
ngx_str_t *value);
static char *ngx_http_geo_add_range(ngx_conf_t *cf,
ngx_http_geo_conf_ctx_t *ctx, in_addr_t start, in_addr_t end);
static ngx_uint_t ngx_http_geo_delete_range(ngx_conf_t *cf,
ngx_http_geo_conf_ctx_t *ctx, in_addr_t start, in_addr_t end);
static char *ngx_http_geo_cidr(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
ngx_str_t *value);
static char *ngx_http_geo_cidr_add(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
ngx_cidr_t *cidr, ngx_str_t *value, ngx_str_t *net);
static ngx_http_variable_value_t *ngx_http_geo_value(ngx_conf_t *cf,
ngx_http_geo_conf_ctx_t *ctx, ngx_str_t *value);
static char *ngx_http_geo_add_proxy(ngx_conf_t *cf,
ngx_http_geo_conf_ctx_t *ctx, ngx_cidr_t *cidr);
static ngx_int_t ngx_http_geo_cidr_value(ngx_conf_t *cf, ngx_str_t *net,
ngx_cidr_t *cidr);
static char *ngx_http_geo_include(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
ngx_str_t *name);
static ngx_int_t ngx_http_geo_include_binary_base(ngx_conf_t *cf,
ngx_http_geo_conf_ctx_t *ctx, ngx_str_t *name);
static void ngx_http_geo_create_binary_base(ngx_http_geo_conf_ctx_t *ctx);
static u_char *ngx_http_geo_copy_values(u_char *base, u_char *p,
ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);
static ngx_command_t ngx_http_geo_commands[] = {
{ ngx_string("geo"),
NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE12,
ngx_http_geo_block,
NGX_HTTP_MAIN_CONF_OFFSET,
0,
NULL },
ngx_null_command
};
static ngx_http_module_t ngx_http_geo_module_ctx = {
NULL, /* preconfiguration */
NULL, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
NULL, /* create location configuration */
NULL /* merge location configuration */
};
ngx_module_t ngx_http_geo_module = {
NGX_MODULE_V1,
&ngx_http_geo_module_ctx, /* module context */
ngx_http_geo_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
typedef struct {
u_char GEORNG[6];
u_char version;
u_char ptr_size;
uint32_t endianness;
uint32_t crc32;
} ngx_http_geo_header_t;
static ngx_http_geo_header_t ngx_http_geo_header = {
{ 'G', 'E', 'O', 'R', 'N', 'G' }, 0, sizeof(void *), 0x12345678, 0
};
/* geo range is AF_INET only */
static ngx_int_t
ngx_http_geo_cidr_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
uintptr_t data)
{
ngx_http_geo_ctx_t *ctx = (ngx_http_geo_ctx_t *) data;
in_addr_t inaddr;
ngx_addr_t addr;
struct sockaddr_in *sin;
ngx_http_variable_value_t *vv;
#if (NGX_HAVE_INET6)
u_char *p;
struct in6_addr *inaddr6;
#endif
if (ngx_http_geo_addr(r, ctx, &addr) != NGX_OK) {
vv = (ngx_http_variable_value_t *)
ngx_radix32tree_find(ctx->u.trees.tree, INADDR_NONE);
goto done;
}
switch (addr.sockaddr->sa_family) {
#if (NGX_HAVE_INET6)
case AF_INET6:
inaddr6 = &((struct sockaddr_in6 *) addr.sockaddr)->sin6_addr;
p = inaddr6->s6_addr;
if (IN6_IS_ADDR_V4MAPPED(inaddr6)) {
inaddr = p[12] << 24;
inaddr += p[13] << 16;
inaddr += p[14] << 8;
inaddr += p[15];
vv = (ngx_http_variable_value_t *)
ngx_radix32tree_find(ctx->u.trees.tree, inaddr);
} else {
vv = (ngx_http_variable_value_t *)
ngx_radix128tree_find(ctx->u.trees.tree6, p);
}
break;
#endif
default: /* AF_INET */
sin = (struct sockaddr_in *) addr.sockaddr;
inaddr = ntohl(sin->sin_addr.s_addr);
vv = (ngx_http_variable_value_t *)
ngx_radix32tree_find(ctx->u.trees.tree, inaddr);
break;
}
done:
*v = *vv;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http geo: %v", v);
return NGX_OK;
}
static ngx_int_t
ngx_http_geo_range_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
uintptr_t data)
{
ngx_http_geo_ctx_t *ctx = (ngx_http_geo_ctx_t *) data;
in_addr_t inaddr;
ngx_addr_t addr;
ngx_uint_t n;
struct sockaddr_in *sin;
ngx_http_geo_range_t *range;
#if (NGX_HAVE_INET6)
u_char *p;
struct in6_addr *inaddr6;
#endif
*v = *ctx->u.high.default_value;
if (ngx_http_geo_addr(r, ctx, &addr) == NGX_OK) {
switch (addr.sockaddr->sa_family) {
#if (NGX_HAVE_INET6)
case AF_INET6:
inaddr6 = &((struct sockaddr_in6 *) addr.sockaddr)->sin6_addr;
if (IN6_IS_ADDR_V4MAPPED(inaddr6)) {
p = inaddr6->s6_addr;
inaddr = p[12] << 24;
inaddr += p[13] << 16;
inaddr += p[14] << 8;
inaddr += p[15];
} else {
inaddr = INADDR_NONE;
}
break;
#endif
default: /* AF_INET */
sin = (struct sockaddr_in *) addr.sockaddr;
inaddr = ntohl(sin->sin_addr.s_addr);
break;
}
} else {
inaddr = INADDR_NONE;
}
if (ctx->u.high.low) {
range = ctx->u.high.low[inaddr >> 16];
if (range) {
n = inaddr & 0xffff;
do {
if (n >= (ngx_uint_t) range->start
&& n <= (ngx_uint_t) range->end)
{
*v = *range->value;
break;
}
} while ((++range)->value);
}
}
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http geo: %v", v);
return NGX_OK;
}
static ngx_int_t
ngx_http_geo_addr(ngx_http_request_t *r, ngx_http_geo_ctx_t *ctx,
ngx_addr_t *addr)
{
ngx_array_t *xfwd;
if (ngx_http_geo_real_addr(r, ctx, addr) != NGX_OK) {
return NGX_ERROR;
}
xfwd = &r->headers_in.x_forwarded_for;
if (xfwd->nelts > 0 && ctx->proxies != NULL) {
(void) ngx_http_get_forwarded_addr(r, addr, xfwd, NULL,
ctx->proxies, ctx->proxy_recursive);
}
return NGX_OK;
}
static ngx_int_t
ngx_http_geo_real_addr(ngx_http_request_t *r, ngx_http_geo_ctx_t *ctx,
ngx_addr_t *addr)
{
ngx_http_variable_value_t *v;
if (ctx->index == -1) {
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http geo started: %V", &r->connection->addr_text);
addr->sockaddr = r->connection->sockaddr;
addr->socklen = r->connection->socklen;
/* addr->name = r->connection->addr_text; */
return NGX_OK;
}
v = ngx_http_get_flushed_variable(r, ctx->index);
if (v == NULL || v->not_found) {
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http geo not found");
return NGX_ERROR;
}
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http geo started: %v", v);
if (ngx_parse_addr(r->pool, addr, v->data, v->len) == NGX_OK) {
return NGX_OK;
}
return NGX_ERROR;
}
static char *
ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
char *rv;
size_t len;
ngx_str_t *value, name;
ngx_uint_t i;
ngx_conf_t save;
ngx_pool_t *pool;
ngx_array_t *a;
ngx_http_variable_t *var;
ngx_http_geo_ctx_t *geo;
ngx_http_geo_conf_ctx_t ctx;
#if (NGX_HAVE_INET6)
static struct in6_addr zero;
#endif
value = cf->args->elts;
geo = ngx_palloc(cf->pool, sizeof(ngx_http_geo_ctx_t));
if (geo == NULL) {
return NGX_CONF_ERROR;
}
name = value[1];
if (name.data[0] != '$') {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid variable name \"%V\"", &name);
return NGX_CONF_ERROR;
}
name.len--;
name.data++;
if (cf->args->nelts == 3) {
geo->index = ngx_http_get_variable_index(cf, &name);
if (geo->index == NGX_ERROR) {
return NGX_CONF_ERROR;
}
name = value[2];
if (name.data[0] != '$') {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid variable name \"%V\"", &name);
return NGX_CONF_ERROR;
}
name.len--;
name.data++;
} else {
geo->index = -1;
}
var = ngx_http_add_variable(cf, &name, NGX_HTTP_VAR_CHANGEABLE);
if (var == NULL) {
return NGX_CONF_ERROR;
}
pool = ngx_create_pool(NGX_DEFAULT_POOL_SIZE, cf->log);
if (pool == NULL) {
return NGX_CONF_ERROR;
}
ngx_memzero(&ctx, sizeof(ngx_http_geo_conf_ctx_t));
ctx.temp_pool = ngx_create_pool(NGX_DEFAULT_POOL_SIZE, cf->log);
if (ctx.temp_pool == NULL) {
ngx_destroy_pool(pool);
return NGX_CONF_ERROR;
}
ngx_rbtree_init(&ctx.rbtree, &ctx.sentinel, ngx_str_rbtree_insert_value);
ctx.pool = cf->pool;
ctx.data_size = sizeof(ngx_http_geo_header_t)
+ sizeof(ngx_http_variable_value_t)
+ 0x10000 * sizeof(ngx_http_geo_range_t *);
ctx.allow_binary_include = 1;
save = *cf;
cf->pool = pool;
cf->ctx = &ctx;
cf->handler = ngx_http_geo;
cf->handler_conf = conf;
rv = ngx_conf_parse(cf, NULL);
*cf = save;
if (rv != NGX_CONF_OK) {
goto failed;
}
geo->proxies = ctx.proxies;
geo->proxy_recursive = ctx.proxy_recursive;
if (ctx.ranges) {
if (ctx.high.low && !ctx.binary_include) {
for (i = 0; i < 0x10000; i++) {
a = (ngx_array_t *) ctx.high.low[i];
if (a == NULL) {
continue;
}
if (a->nelts == 0) {
ctx.high.low[i] = NULL;
continue;
}
len = a->nelts * sizeof(ngx_http_geo_range_t);
ctx.high.low[i] = ngx_palloc(cf->pool, len + sizeof(void *));
if (ctx.high.low[i] == NULL) {
goto failed;
}
ngx_memcpy(ctx.high.low[i], a->elts, len);
ctx.high.low[i][a->nelts].value = NULL;
ctx.data_size += len + sizeof(void *);
}
if (ctx.allow_binary_include
&& !ctx.outside_entries
&& ctx.entries > 100000
&& ctx.includes == 1)
{
ngx_http_geo_create_binary_base(&ctx);
}
}
if (ctx.high.default_value == NULL) {
ctx.high.default_value = &ngx_http_variable_null_value;
}
geo->u.high = ctx.high;
var->get_handler = ngx_http_geo_range_variable;
var->data = (uintptr_t) geo;
} else {
if (ctx.tree == NULL) {
ctx.tree = ngx_radix_tree_create(cf->pool, -1);
if (ctx.tree == NULL) {
goto failed;
}
}
geo->u.trees.tree = ctx.tree;
#if (NGX_HAVE_INET6)
if (ctx.tree6 == NULL) {
ctx.tree6 = ngx_radix_tree_create(cf->pool, -1);
if (ctx.tree6 == NULL) {
goto failed;
}
}
geo->u.trees.tree6 = ctx.tree6;
#endif
var->get_handler = ngx_http_geo_cidr_variable;
var->data = (uintptr_t) geo;
if (ngx_radix32tree_insert(ctx.tree, 0, 0,
(uintptr_t) &ngx_http_variable_null_value)
== NGX_ERROR)
{
goto failed;
}
/* NGX_BUSY is okay (default was set explicitly) */
#if (NGX_HAVE_INET6)
if (ngx_radix128tree_insert(ctx.tree6, zero.s6_addr, zero.s6_addr,
(uintptr_t) &ngx_http_variable_null_value)
== NGX_ERROR)
{
goto failed;
}
#endif
}
ngx_destroy_pool(ctx.temp_pool);
ngx_destroy_pool(pool);
return NGX_CONF_OK;
failed:
ngx_destroy_pool(ctx.temp_pool);
ngx_destroy_pool(pool);
return NGX_CONF_ERROR;
}
static char *
ngx_http_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
{
char *rv;
ngx_str_t *value;
ngx_cidr_t cidr;
ngx_http_geo_conf_ctx_t *ctx;
ctx = cf->ctx;
value = cf->args->elts;
if (cf->args->nelts == 1) {
if (ngx_strcmp(value[0].data, "ranges") == 0) {
if (ctx->tree
#if (NGX_HAVE_INET6)
|| ctx->tree6
#endif
)
{
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"the \"ranges\" directive must be "
"the first directive inside \"geo\" block");
goto failed;
}
ctx->ranges = 1;
rv = NGX_CONF_OK;
goto done;
}
else if (ngx_strcmp(value[0].data, "proxy_recursive") == 0) {
ctx->proxy_recursive = 1;
rv = NGX_CONF_OK;
goto done;
}
}
if (cf->args->nelts != 2) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid number of the geo parameters");
goto failed;
}
if (ngx_strcmp(value[0].data, "include") == 0) {
rv = ngx_http_geo_include(cf, ctx, &value[1]);
goto done;
} else if (ngx_strcmp(value[0].data, "proxy") == 0) {
if (ngx_http_geo_cidr_value(cf, &value[1], &cidr) != NGX_OK) {
goto failed;
}
rv = ngx_http_geo_add_proxy(cf, ctx, &cidr);
goto done;
}
if (ctx->ranges) {
rv = ngx_http_geo_range(cf, ctx, value);
} else {
rv = ngx_http_geo_cidr(cf, ctx, value);
}
done:
ngx_reset_pool(cf->pool);
return rv;
failed:
ngx_reset_pool(cf->pool);
return NGX_CONF_ERROR;
}
static char *
ngx_http_geo_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
ngx_str_t *value)
{
u_char *p, *last;
in_addr_t start, end;
ngx_str_t *net;
ngx_uint_t del;
if (ngx_strcmp(value[0].data, "default") == 0) {
if (ctx->high.default_value) {
ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
"duplicate default geo range value: \"%V\", old value: \"%v\"",
&value[1], ctx->high.default_value);
}
ctx->high.default_value = ngx_http_geo_value(cf, ctx, &value[1]);
if (ctx->high.default_value == NULL) {
return NGX_CONF_ERROR;
}
return NGX_CONF_OK;
}
if (ctx->binary_include) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"binary geo range base \"%s\" cannot be mixed with usual entries",
ctx->include_name.data);
return NGX_CONF_ERROR;
}
if (ctx->high.low == NULL) {
ctx->high.low = ngx_pcalloc(ctx->pool,
0x10000 * sizeof(ngx_http_geo_range_t *));
if (ctx->high.low == NULL) {
return NGX_CONF_ERROR;
}
}
ctx->entries++;
ctx->outside_entries = 1;
if (ngx_strcmp(value[0].data, "delete") == 0) {
net = &value[1];
del = 1;
} else {
net = &value[0];
del = 0;
}
last = net->data + net->len;
p = ngx_strlchr(net->data, last, '-');
if (p == NULL) {
goto invalid;
}
start = ngx_inet_addr(net->data, p - net->data);
if (start == INADDR_NONE) {
goto invalid;
}
start = ntohl(start);
p++;
end = ngx_inet_addr(p, last - p);
if (end == INADDR_NONE) {
goto invalid;
}
end = ntohl(end);
if (start > end) {
goto invalid;
}
if (del) {
if (ngx_http_geo_delete_range(cf, ctx, start, end)) {
ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
"no address range \"%V\" to delete", net);
}
return NGX_CONF_OK;
}
ctx->value = ngx_http_geo_value(cf, ctx, &value[1]);
if (ctx->value == NULL) {
return NGX_CONF_ERROR;
}
ctx->net = net;
return ngx_http_geo_add_range(cf, ctx, start, end);
invalid:
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid range \"%V\"", net);
return NGX_CONF_ERROR;
}
/* the add procedure is optimized to add a growing up sequence */
static char *
ngx_http_geo_add_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
in_addr_t start, in_addr_t end)
{
in_addr_t n;
ngx_uint_t h, i, s, e;
ngx_array_t *a;
ngx_http_geo_range_t *range;
for (n = start; n <= end; n = (n + 0x10000) & 0xffff0000) {
h = n >> 16;
if (n == start) {
s = n & 0xffff;
} else {
s = 0;
}
if ((n | 0xffff) > end) {
e = end & 0xffff;
} else {
e = 0xffff;
}
a = (ngx_array_t *) ctx->high.low[h];
if (a == NULL) {
a = ngx_array_create(ctx->temp_pool, 64,
sizeof(ngx_http_geo_range_t));
if (a == NULL) {
return NGX_CONF_ERROR;
}
ctx->high.low[h] = (ngx_http_geo_range_t *) a;
}
i = a->nelts;
range = a->elts;
while (i) {
i--;
if (e < (ngx_uint_t) range[i].start) {
continue;
}
if (s > (ngx_uint_t) range[i].end) {
/* add after the range */
range = ngx_array_push(a);
if (range == NULL) {
return NGX_CONF_ERROR;
}
range = a->elts;
ngx_memmove(&range[i + 2], &range[i + 1],
(a->nelts - 2 - i) * sizeof(ngx_http_geo_range_t));
range[i + 1].start = (u_short) s;
range[i + 1].end = (u_short) e;
range[i + 1].value = ctx->value;
goto next;
}
if (s == (ngx_uint_t) range[i].start
&& e == (ngx_uint_t) range[i].end)
{
ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
"duplicate range \"%V\", value: \"%v\", old value: \"%v\"",
ctx->net, ctx->value, range[i].value);
range[i].value = ctx->value;
goto next;
}
if (s > (ngx_uint_t) range[i].start
&& e < (ngx_uint_t) range[i].end)
{
/* split the range and insert the new one */
range = ngx_array_push(a);
if (range == NULL) {
return NGX_CONF_ERROR;
}
range = ngx_array_push(a);
if (range == NULL) {
return NGX_CONF_ERROR;
}
range = a->elts;
ngx_memmove(&range[i + 3], &range[i + 1],
(a->nelts - 3 - i) * sizeof(ngx_http_geo_range_t));
range[i + 2].start = (u_short) (e + 1);
range[i + 2].end = range[i].end;
range[i + 2].value = range[i].value;
range[i + 1].start = (u_short) s;
range[i + 1].end = (u_short) e;
range[i + 1].value = ctx->value;
range[i].end = (u_short) (s - 1);
goto next;
}
if (s == (ngx_uint_t) range[i].start
&& e < (ngx_uint_t) range[i].end)
{
/* shift the range start and insert the new range */
range = ngx_array_push(a);
if (range == NULL) {
return NGX_CONF_ERROR;
}
range = a->elts;
ngx_memmove(&range[i + 1], &range[i],
(a->nelts - 1 - i) * sizeof(ngx_http_geo_range_t));
range[i + 1].start = (u_short) (e + 1);
range[i].start = (u_short) s;
range[i].end = (u_short) e;
range[i].value = ctx->value;
goto next;
}
if (s > (ngx_uint_t) range[i].start
&& e == (ngx_uint_t) range[i].end)
{
/* shift the range end and insert the new range */
range = ngx_array_push(a);
if (range == NULL) {
return NGX_CONF_ERROR;
}
range = a->elts;
ngx_memmove(&range[i + 2], &range[i + 1],
(a->nelts - 2 - i) * sizeof(ngx_http_geo_range_t));
range[i + 1].start = (u_short) s;
range[i + 1].end = (u_short) e;
range[i + 1].value = ctx->value;
range[i].end = (u_short) (s - 1);
goto next;
}
s = (ngx_uint_t) range[i].start;
e = (ngx_uint_t) range[i].end;
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"range \"%V\" overlaps \"%d.%d.%d.%d-%d.%d.%d.%d\"",
ctx->net,
h >> 8, h & 0xff, s >> 8, s & 0xff,
h >> 8, h & 0xff, e >> 8, e & 0xff);
return NGX_CONF_ERROR;
}
/* add the first range */
range = ngx_array_push(a);
if (range == NULL) {
return NGX_CONF_ERROR;
}
range = a->elts;
ngx_memmove(&range[1], &range[0],
(a->nelts - 1) * sizeof(ngx_http_geo_range_t));
range[0].start = (u_short) s;
range[0].end = (u_short) e;
range[0].value = ctx->value;
next:
if (h == 0xffff) {
break;
}
}
return NGX_CONF_OK;
}
static ngx_uint_t
ngx_http_geo_delete_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
in_addr_t start, in_addr_t end)
{
in_addr_t n;
ngx_uint_t h, i, s, e, warn;
ngx_array_t *a;
ngx_http_geo_range_t *range;
warn = 0;
for (n = start; n <= end; n = (n + 0x10000) & 0xffff0000) {
h = n >> 16;
if (n == start) {
s = n & 0xffff;
} else {
s = 0;
}
if ((n | 0xffff) > end) {
e = end & 0xffff;
} else {
e = 0xffff;
}
a = (ngx_array_t *) ctx->high.low[h];
if (a == NULL || a->nelts == 0) {
warn = 1;
goto next;
}
range = a->elts;
for (i = 0; i < a->nelts; i++) {
if (s == (ngx_uint_t) range[i].start
&& e == (ngx_uint_t) range[i].end)
{
ngx_memmove(&range[i], &range[i + 1],
(a->nelts - 1 - i) * sizeof(ngx_http_geo_range_t));
a->nelts--;
break;
}
if (i == a->nelts - 1) {
warn = 1;
}
}
next:
if (h == 0xffff) {
break;
}
}
return warn;
}
static char *
ngx_http_geo_cidr(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
ngx_str_t *value)
{
char *rv;
ngx_int_t rc, del;
ngx_str_t *net;
ngx_cidr_t cidr;
if (ctx->tree == NULL) {
ctx->tree = ngx_radix_tree_create(ctx->pool, -1);
if (ctx->tree == NULL) {
return NGX_CONF_ERROR;
}
}
#if (NGX_HAVE_INET6)
if (ctx->tree6 == NULL) {
ctx->tree6 = ngx_radix_tree_create(ctx->pool, -1);
if (ctx->tree6 == NULL) {
return NGX_CONF_ERROR;
}
}
#endif
if (ngx_strcmp(value[0].data, "default") == 0) {
cidr.family = AF_INET;
cidr.u.in.addr = 0;
cidr.u.in.mask = 0;
rv = ngx_http_geo_cidr_add(cf, ctx, &cidr, &value[1], &value[0]);
if (rv != NGX_CONF_OK) {
return rv;
}
#if (NGX_HAVE_INET6)
cidr.family = AF_INET6;
ngx_memzero(&cidr.u.in6, sizeof(ngx_in6_cidr_t));
rv = ngx_http_geo_cidr_add(cf, ctx, &cidr, &value[1], &value[0]);
if (rv != NGX_CONF_OK) {
return rv;
}
#endif
return NGX_CONF_OK;
}
if (ngx_strcmp(value[0].data, "delete") == 0) {
net = &value[1];
del = 1;
} else {
net = &value[0];
del = 0;
}
if (ngx_http_geo_cidr_value(cf, net, &cidr) != NGX_OK) {
return NGX_CONF_ERROR;
}
if (cidr.family == AF_INET) {
cidr.u.in.addr = ntohl(cidr.u.in.addr);
cidr.u.in.mask = ntohl(cidr.u.in.mask);
}
if (del) {
switch (cidr.family) {
#if (NGX_HAVE_INET6)
case AF_INET6:
rc = ngx_radix128tree_delete(ctx->tree6,
cidr.u.in6.addr.s6_addr,
cidr.u.in6.mask.s6_addr);
break;
#endif
default: /* AF_INET */
rc = ngx_radix32tree_delete(ctx->tree, cidr.u.in.addr,
cidr.u.in.mask);
break;
}
if (rc != NGX_OK) {
ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
"no network \"%V\" to delete", net);
}
return NGX_CONF_OK;
}
return ngx_http_geo_cidr_add(cf, ctx, &cidr, &value[1], net);
}
static char *
ngx_http_geo_cidr_add(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
ngx_cidr_t *cidr, ngx_str_t *value, ngx_str_t *net)
{
ngx_int_t rc;
ngx_http_variable_value_t *val, *old;
val = ngx_http_geo_value(cf, ctx, value);
if (val == NULL) {
return NGX_CONF_ERROR;
}
switch (cidr->family) {
#if (NGX_HAVE_INET6)
case AF_INET6:
rc = ngx_radix128tree_insert(ctx->tree6, cidr->u.in6.addr.s6_addr,
cidr->u.in6.mask.s6_addr,
(uintptr_t) val);
if (rc == NGX_OK) {
return NGX_CONF_OK;
}
if (rc == NGX_ERROR) {
return NGX_CONF_ERROR;
}
/* rc == NGX_BUSY */
old = (ngx_http_variable_value_t *)
ngx_radix128tree_find(ctx->tree6,
cidr->u.in6.addr.s6_addr);
ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
"duplicate network \"%V\", value: \"%v\", old value: \"%v\"",
net, val, old);
rc = ngx_radix128tree_delete(ctx->tree6,
cidr->u.in6.addr.s6_addr,
cidr->u.in6.mask.s6_addr);
if (rc == NGX_ERROR) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid radix tree");
return NGX_CONF_ERROR;
}
rc = ngx_radix128tree_insert(ctx->tree6, cidr->u.in6.addr.s6_addr,
cidr->u.in6.mask.s6_addr,
(uintptr_t) val);
break;
#endif
default: /* AF_INET */
rc = ngx_radix32tree_insert(ctx->tree, cidr->u.in.addr,
cidr->u.in.mask, (uintptr_t) val);
if (rc == NGX_OK) {
return NGX_CONF_OK;
}
if (rc == NGX_ERROR) {
return NGX_CONF_ERROR;
}
/* rc == NGX_BUSY */
old = (ngx_http_variable_value_t *)
ngx_radix32tree_find(ctx->tree, cidr->u.in.addr);
ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
"duplicate network \"%V\", value: \"%v\", old value: \"%v\"",
net, val, old);
rc = ngx_radix32tree_delete(ctx->tree,
cidr->u.in.addr, cidr->u.in.mask);
if (rc == NGX_ERROR) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid radix tree");
return NGX_CONF_ERROR;
}
rc = ngx_radix32tree_insert(ctx->tree, cidr->u.in.addr,
cidr->u.in.mask, (uintptr_t) val);
break;
}
if (rc == NGX_OK) {
return NGX_CONF_OK;
}
return NGX_CONF_ERROR;
}
static ngx_http_variable_value_t *
ngx_http_geo_value(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
ngx_str_t *value)
{
uint32_t hash;
ngx_http_variable_value_t *val;
ngx_http_geo_variable_value_node_t *gvvn;
hash = ngx_crc32_long(value->data, value->len);
gvvn = (ngx_http_geo_variable_value_node_t *)
ngx_str_rbtree_lookup(&ctx->rbtree, value, hash);
if (gvvn) {
return gvvn->value;
}
val = ngx_palloc(ctx->pool, sizeof(ngx_http_variable_value_t));
if (val == NULL) {
return NULL;
}
val->len = value->len;
val->data = ngx_pstrdup(ctx->pool, value);
if (val->data == NULL) {
return NULL;
}
val->valid = 1;
val->no_cacheable = 0;
val->not_found = 0;
gvvn = ngx_palloc(ctx->temp_pool,
sizeof(ngx_http_geo_variable_value_node_t));
if (gvvn == NULL) {
return NULL;
}
gvvn->sn.node.key = hash;
gvvn->sn.str.len = val->len;
gvvn->sn.str.data = val->data;
gvvn->value = val;
gvvn->offset = 0;
ngx_rbtree_insert(&ctx->rbtree, &gvvn->sn.node);
ctx->data_size += ngx_align(sizeof(ngx_http_variable_value_t) + value->len,
sizeof(void *));
return val;
}
static char *
ngx_http_geo_add_proxy(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
ngx_cidr_t *cidr)
{
ngx_cidr_t *c;
if (ctx->proxies == NULL) {
ctx->proxies = ngx_array_create(ctx->pool, 4, sizeof(ngx_cidr_t));
if (ctx->proxies == NULL) {
return NGX_CONF_ERROR;
}
}
c = ngx_array_push(ctx->proxies);
if (c == NULL) {
return NGX_CONF_ERROR;
}
*c = *cidr;
return NGX_CONF_OK;
}
static ngx_int_t
ngx_http_geo_cidr_value(ngx_conf_t *cf, ngx_str_t *net, ngx_cidr_t *cidr)
{
ngx_int_t rc;
if (ngx_strcmp(net->data, "255.255.255.255") == 0) {
cidr->family = AF_INET;
cidr->u.in.addr = 0xffffffff;
cidr->u.in.mask = 0xffffffff;
return NGX_OK;
}
rc = ngx_ptocidr(net, cidr);
if (rc == NGX_ERROR) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid network \"%V\"", net);
return NGX_ERROR;
}
if (rc == NGX_DONE) {
ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
"low address bits of %V are meaningless", net);
}
return NGX_OK;
}
static char *
ngx_http_geo_include(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
ngx_str_t *name)
{
char *rv;
ngx_str_t file;
file.len = name->len + 4;
file.data = ngx_pnalloc(ctx->temp_pool, name->len + 5);
if (file.data == NULL) {
return NGX_CONF_ERROR;
}
ngx_sprintf(file.data, "%V.bin%Z", name);
if (ngx_conf_full_name(cf->cycle, &file, 1) != NGX_OK) {
return NGX_CONF_ERROR;
}
if (ctx->ranges) {
ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data);
switch (ngx_http_geo_include_binary_base(cf, ctx, &file)) {
case NGX_OK:
return NGX_CONF_OK;
case NGX_ERROR:
return NGX_CONF_ERROR;
default:
break;
}
}
file.len -= 4;
file.data[file.len] = '\0';
ctx->include_name = file;
if (ctx->outside_entries) {
ctx->allow_binary_include = 0;
}
ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data);
rv = ngx_conf_parse(cf, &file);
ctx->includes++;
ctx->outside_entries = 0;
return rv;
}
static ngx_int_t
ngx_http_geo_include_binary_base(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
ngx_str_t *name)
{
u_char *base, ch;
time_t mtime;
size_t size, len;
ssize_t n;
uint32_t crc32;
ngx_err_t err;
ngx_int_t rc;
ngx_uint_t i;
ngx_file_t file;
ngx_file_info_t fi;
ngx_http_geo_range_t *range, **ranges;
ngx_http_geo_header_t *header;
ngx_http_variable_value_t *vv;
ngx_memzero(&file, sizeof(ngx_file_t));
file.name = *name;
file.log = cf->log;
file.fd = ngx_open_file(name->data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);
if (file.fd == NGX_INVALID_FILE) {
err = ngx_errno;
if (err != NGX_ENOENT) {
ngx_conf_log_error(NGX_LOG_CRIT, cf, err,
ngx_open_file_n " \"%s\" failed", name->data);
}
return NGX_DECLINED;
}
if (ctx->outside_entries) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"binary geo range base \"%s\" cannot be mixed with usual entries",
name->data);
rc = NGX_ERROR;
goto done;
}
if (ctx->binary_include) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"second binary geo range base \"%s\" cannot be mixed with \"%s\"",
name->data, ctx->include_name.data);
rc = NGX_ERROR;
goto done;
}
if (ngx_fd_info(file.fd, &fi) == NGX_FILE_ERROR) {
ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno,
ngx_fd_info_n " \"%s\" failed", name->data);
goto failed;
}
size = (size_t) ngx_file_size(&fi);
mtime = ngx_file_mtime(&fi);
ch = name->data[name->len - 4];
name->data[name->len - 4] = '\0';
if (ngx_file_info(name->data, &fi) == NGX_FILE_ERROR) {
ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno,
ngx_file_info_n " \"%s\" failed", name->data);
goto failed;
}
name->data[name->len - 4] = ch;
if (mtime < ngx_file_mtime(&fi)) {
ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
"stale binary geo range base \"%s\"", name->data);
goto failed;
}
base = ngx_palloc(ctx->pool, size);
if (base == NULL) {
goto failed;
}
n = ngx_read_file(&file, base, size, 0);
if (n == NGX_ERROR) {
ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno,
ngx_read_file_n " \"%s\" failed", name->data);
goto failed;
}
if ((size_t) n != size) {
ngx_conf_log_error(NGX_LOG_CRIT, cf, 0,
ngx_read_file_n " \"%s\" returned only %z bytes instead of %z",
name->data, n, size);
goto failed;
}
header = (ngx_http_geo_header_t *) base;
if (size < 16 || ngx_memcmp(&ngx_http_geo_header, header, 12) != 0) {
ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
"incompatible binary geo range base \"%s\"", name->data);
goto failed;
}
ngx_crc32_init(crc32);
vv = (ngx_http_variable_value_t *) (base + sizeof(ngx_http_geo_header_t));
while (vv->data) {
len = ngx_align(sizeof(ngx_http_variable_value_t) + vv->len,
sizeof(void *));
ngx_crc32_update(&crc32, (u_char *) vv, len);
vv->data += (size_t) base;
vv = (ngx_http_variable_value_t *) ((u_char *) vv + len);
}
ngx_crc32_update(&crc32, (u_char *) vv, sizeof(ngx_http_variable_value_t));
vv++;
ranges = (ngx_http_geo_range_t **) vv;
for (i = 0; i < 0x10000; i++) {
ngx_crc32_update(&crc32, (u_char *) &ranges[i], sizeof(void *));
if (ranges[i]) {
ranges[i] = (ngx_http_geo_range_t *)
((u_char *) ranges[i] + (size_t) base);
}
}
range = (ngx_http_geo_range_t *) &ranges[0x10000];
while ((u_char *) range < base + size) {
while (range->value) {
ngx_crc32_update(&crc32, (u_char *) range,
sizeof(ngx_http_geo_range_t));
range->value = (ngx_http_variable_value_t *)
((u_char *) range->value + (size_t) base);
range++;
}
ngx_crc32_update(&crc32, (u_char *) range, sizeof(void *));
range = (ngx_http_geo_range_t *) ((u_char *) range + sizeof(void *));
}
ngx_crc32_final(crc32);
if (crc32 != header->crc32) {
ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
"CRC32 mismatch in binary geo range base \"%s\"", name->data);
goto failed;
}
ngx_conf_log_error(NGX_LOG_NOTICE, cf, 0,
"using binary geo range base \"%s\"", name->data);
ctx->include_name = *name;
ctx->binary_include = 1;
ctx->high.low = ranges;
rc = NGX_OK;
goto done;
failed:
rc = NGX_DECLINED;
done:
if (ngx_close_file(file.fd) == NGX_FILE_ERROR) {
ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
ngx_close_file_n " \"%s\" failed", name->data);
}
return rc;
}
static void
ngx_http_geo_create_binary_base(ngx_http_geo_conf_ctx_t *ctx)
{
u_char *p;
uint32_t hash;
ngx_str_t s;
ngx_uint_t i;
ngx_file_mapping_t fm;
ngx_http_geo_range_t *r, *range, **ranges;
ngx_http_geo_header_t *header;
ngx_http_geo_variable_value_node_t *gvvn;
fm.name = ngx_pnalloc(ctx->temp_pool, ctx->include_name.len + 5);
if (fm.name == NULL) {
return;
}
ngx_sprintf(fm.name, "%V.bin%Z", &ctx->include_name);
fm.size = ctx->data_size;
fm.log = ctx->pool->log;
ngx_log_error(NGX_LOG_NOTICE, fm.log, 0,
"creating binary geo range base \"%s\"", fm.name);
if (ngx_create_file_mapping(&fm) != NGX_OK) {
return;
}
p = ngx_cpymem(fm.addr, &ngx_http_geo_header,
sizeof(ngx_http_geo_header_t));
p = ngx_http_geo_copy_values(fm.addr, p, ctx->rbtree.root,
ctx->rbtree.sentinel);
p += sizeof(ngx_http_variable_value_t);
ranges = (ngx_http_geo_range_t **) p;
p += 0x10000 * sizeof(ngx_http_geo_range_t *);
for (i = 0; i < 0x10000; i++) {
r = ctx->high.low[i];
if (r == NULL) {
continue;
}
range = (ngx_http_geo_range_t *) p;
ranges[i] = (ngx_http_geo_range_t *) (p - (u_char *) fm.addr);
do {
s.len = r->value->len;
s.data = r->value->data;
hash = ngx_crc32_long(s.data, s.len);
gvvn = (ngx_http_geo_variable_value_node_t *)
ngx_str_rbtree_lookup(&ctx->rbtree, &s, hash);
range->value = (ngx_http_variable_value_t *) gvvn->offset;
range->start = r->start;
range->end = r->end;
range++;
} while ((++r)->value);
range->value = NULL;
p = (u_char *) range + sizeof(void *);
}
header = fm.addr;
header->crc32 = ngx_crc32_long((u_char *) fm.addr
+ sizeof(ngx_http_geo_header_t),
fm.size - sizeof(ngx_http_geo_header_t));
ngx_close_file_mapping(&fm);
}
static u_char *
ngx_http_geo_copy_values(u_char *base, u_char *p, ngx_rbtree_node_t *node,
ngx_rbtree_node_t *sentinel)
{
ngx_http_variable_value_t *vv;
ngx_http_geo_variable_value_node_t *gvvn;
if (node == sentinel) {
return p;
}
gvvn = (ngx_http_geo_variable_value_node_t *) node;
gvvn->offset = p - base;
vv = (ngx_http_variable_value_t *) p;
*vv = *gvvn->value;
p += sizeof(ngx_http_variable_value_t);
vv->data = (u_char *) (p - base);
p = ngx_cpymem(p, gvvn->sn.str.data, gvvn->sn.str.len);
p = ngx_align_ptr(p, sizeof(void *));
p = ngx_http_geo_copy_values(base, p, node->left, sentinel);
return ngx_http_geo_copy_values(base, p, node->right, sentinel);
}
nginx-1.14.0/src/http/modules/ngx_http_geoip_module.c 000644 001751 001751 00000053543 13265410474 024073 0 ustar 00mdounin mdounin 000000 000000
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#include
#include
#include
#include
#include
#define NGX_GEOIP_COUNTRY_CODE 0
#define NGX_GEOIP_COUNTRY_CODE3 1
#define NGX_GEOIP_COUNTRY_NAME 2
typedef struct {
GeoIP *country;
GeoIP *org;
GeoIP *city;
ngx_array_t *proxies; /* array of ngx_cidr_t */
ngx_flag_t proxy_recursive;
#if (NGX_HAVE_GEOIP_V6)
unsigned country_v6:1;
unsigned org_v6:1;
unsigned city_v6:1;
#endif
} ngx_http_geoip_conf_t;
typedef struct {
ngx_str_t *name;
uintptr_t data;
} ngx_http_geoip_var_t;
typedef const char *(*ngx_http_geoip_variable_handler_pt)(GeoIP *,
u_long addr);
ngx_http_geoip_variable_handler_pt ngx_http_geoip_country_functions[] = {
GeoIP_country_code_by_ipnum,
GeoIP_country_code3_by_ipnum,
GeoIP_country_name_by_ipnum,
};
#if (NGX_HAVE_GEOIP_V6)
typedef const char *(*ngx_http_geoip_variable_handler_v6_pt)(GeoIP *,
geoipv6_t addr);
ngx_http_geoip_variable_handler_v6_pt ngx_http_geoip_country_v6_functions[] = {
GeoIP_country_code_by_ipnum_v6,
GeoIP_country_code3_by_ipnum_v6,
GeoIP_country_name_by_ipnum_v6,
};
#endif
static ngx_int_t ngx_http_geoip_country_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_geoip_org_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_geoip_city_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_geoip_region_name_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_geoip_city_float_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_geoip_city_int_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static GeoIPRecord *ngx_http_geoip_get_city_record(ngx_http_request_t *r);
static ngx_int_t ngx_http_geoip_add_variables(ngx_conf_t *cf);
static void *ngx_http_geoip_create_conf(ngx_conf_t *cf);
static char *ngx_http_geoip_init_conf(ngx_conf_t *cf, void *conf);
static char *ngx_http_geoip_country(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static char *ngx_http_geoip_org(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static char *ngx_http_geoip_city(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static char *ngx_http_geoip_proxy(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static ngx_int_t ngx_http_geoip_cidr_value(ngx_conf_t *cf, ngx_str_t *net,
ngx_cidr_t *cidr);
static void ngx_http_geoip_cleanup(void *data);
static ngx_command_t ngx_http_geoip_commands[] = {
{ ngx_string("geoip_country"),
NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE12,
ngx_http_geoip_country,
NGX_HTTP_MAIN_CONF_OFFSET,
0,
NULL },
{ ngx_string("geoip_org"),
NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE12,
ngx_http_geoip_org,
NGX_HTTP_MAIN_CONF_OFFSET,
0,
NULL },
{ ngx_string("geoip_city"),
NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE12,
ngx_http_geoip_city,
NGX_HTTP_MAIN_CONF_OFFSET,
0,
NULL },
{ ngx_string("geoip_proxy"),
NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,
ngx_http_geoip_proxy,
NGX_HTTP_MAIN_CONF_OFFSET,
0,
NULL },
{ ngx_string("geoip_proxy_recursive"),
NGX_HTTP_MAIN_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_HTTP_MAIN_CONF_OFFSET,
offsetof(ngx_http_geoip_conf_t, proxy_recursive),
NULL },
ngx_null_command
};
static ngx_http_module_t ngx_http_geoip_module_ctx = {
ngx_http_geoip_add_variables, /* preconfiguration */
NULL, /* postconfiguration */
ngx_http_geoip_create_conf, /* create main configuration */
ngx_http_geoip_init_conf, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
NULL, /* create location configuration */
NULL /* merge location configuration */
};
ngx_module_t ngx_http_geoip_module = {
NGX_MODULE_V1,
&ngx_http_geoip_module_ctx, /* module context */
ngx_http_geoip_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static ngx_http_variable_t ngx_http_geoip_vars[] = {
{ ngx_string("geoip_country_code"), NULL,
ngx_http_geoip_country_variable,
NGX_GEOIP_COUNTRY_CODE, 0, 0 },
{ ngx_string("geoip_country_code3"), NULL,
ngx_http_geoip_country_variable,
NGX_GEOIP_COUNTRY_CODE3, 0, 0 },
{ ngx_string("geoip_country_name"), NULL,
ngx_http_geoip_country_variable,
NGX_GEOIP_COUNTRY_NAME, 0, 0 },
{ ngx_string("geoip_org"), NULL,
ngx_http_geoip_org_variable,
0, 0, 0 },
{ ngx_string("geoip_city_continent_code"), NULL,
ngx_http_geoip_city_variable,
offsetof(GeoIPRecord, continent_code), 0, 0 },
{ ngx_string("geoip_city_country_code"), NULL,
ngx_http_geoip_city_variable,
offsetof(GeoIPRecord, country_code), 0, 0 },
{ ngx_string("geoip_city_country_code3"), NULL,
ngx_http_geoip_city_variable,
offsetof(GeoIPRecord, country_code3), 0, 0 },
{ ngx_string("geoip_city_country_name"), NULL,
ngx_http_geoip_city_variable,
offsetof(GeoIPRecord, country_name), 0, 0 },
{ ngx_string("geoip_region"), NULL,
ngx_http_geoip_city_variable,
offsetof(GeoIPRecord, region), 0, 0 },
{ ngx_string("geoip_region_name"), NULL,
ngx_http_geoip_region_name_variable,
0, 0, 0 },
{ ngx_string("geoip_city"), NULL,
ngx_http_geoip_city_variable,
offsetof(GeoIPRecord, city), 0, 0 },
{ ngx_string("geoip_postal_code"), NULL,
ngx_http_geoip_city_variable,
offsetof(GeoIPRecord, postal_code), 0, 0 },
{ ngx_string("geoip_latitude"), NULL,
ngx_http_geoip_city_float_variable,
offsetof(GeoIPRecord, latitude), 0, 0 },
{ ngx_string("geoip_longitude"), NULL,
ngx_http_geoip_city_float_variable,
offsetof(GeoIPRecord, longitude), 0, 0 },
{ ngx_string("geoip_dma_code"), NULL,
ngx_http_geoip_city_int_variable,
offsetof(GeoIPRecord, dma_code), 0, 0 },
{ ngx_string("geoip_area_code"), NULL,
ngx_http_geoip_city_int_variable,
offsetof(GeoIPRecord, area_code), 0, 0 },
ngx_http_null_variable
};
static u_long
ngx_http_geoip_addr(ngx_http_request_t *r, ngx_http_geoip_conf_t *gcf)
{
ngx_addr_t addr;
ngx_array_t *xfwd;
struct sockaddr_in *sin;
addr.sockaddr = r->connection->sockaddr;
addr.socklen = r->connection->socklen;
/* addr.name = r->connection->addr_text; */
xfwd = &r->headers_in.x_forwarded_for;
if (xfwd->nelts > 0 && gcf->proxies != NULL) {
(void) ngx_http_get_forwarded_addr(r, &addr, xfwd, NULL,
gcf->proxies, gcf->proxy_recursive);
}
#if (NGX_HAVE_INET6)
if (addr.sockaddr->sa_family == AF_INET6) {
u_char *p;
in_addr_t inaddr;
struct in6_addr *inaddr6;
inaddr6 = &((struct sockaddr_in6 *) addr.sockaddr)->sin6_addr;
if (IN6_IS_ADDR_V4MAPPED(inaddr6)) {
p = inaddr6->s6_addr;
inaddr = p[12] << 24;
inaddr += p[13] << 16;
inaddr += p[14] << 8;
inaddr += p[15];
return inaddr;
}
}
#endif
if (addr.sockaddr->sa_family != AF_INET) {
return INADDR_NONE;
}
sin = (struct sockaddr_in *) addr.sockaddr;
return ntohl(sin->sin_addr.s_addr);
}
#if (NGX_HAVE_GEOIP_V6)
static geoipv6_t
ngx_http_geoip_addr_v6(ngx_http_request_t *r, ngx_http_geoip_conf_t *gcf)
{
ngx_addr_t addr;
ngx_array_t *xfwd;
in_addr_t addr4;
struct in6_addr addr6;
struct sockaddr_in *sin;
struct sockaddr_in6 *sin6;
addr.sockaddr = r->connection->sockaddr;
addr.socklen = r->connection->socklen;
/* addr.name = r->connection->addr_text; */
xfwd = &r->headers_in.x_forwarded_for;
if (xfwd->nelts > 0 && gcf->proxies != NULL) {
(void) ngx_http_get_forwarded_addr(r, &addr, xfwd, NULL,
gcf->proxies, gcf->proxy_recursive);
}
switch (addr.sockaddr->sa_family) {
case AF_INET:
/* Produce IPv4-mapped IPv6 address. */
sin = (struct sockaddr_in *) addr.sockaddr;
addr4 = ntohl(sin->sin_addr.s_addr);
ngx_memzero(&addr6, sizeof(struct in6_addr));
addr6.s6_addr[10] = 0xff;
addr6.s6_addr[11] = 0xff;
addr6.s6_addr[12] = addr4 >> 24;
addr6.s6_addr[13] = addr4 >> 16;
addr6.s6_addr[14] = addr4 >> 8;
addr6.s6_addr[15] = addr4;
return addr6;
case AF_INET6:
sin6 = (struct sockaddr_in6 *) addr.sockaddr;
return sin6->sin6_addr;
default:
return in6addr_any;
}
}
#endif
static ngx_int_t
ngx_http_geoip_country_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
ngx_http_geoip_variable_handler_pt handler =
ngx_http_geoip_country_functions[data];
#if (NGX_HAVE_GEOIP_V6)
ngx_http_geoip_variable_handler_v6_pt handler_v6 =
ngx_http_geoip_country_v6_functions[data];
#endif
const char *val;
ngx_http_geoip_conf_t *gcf;
gcf = ngx_http_get_module_main_conf(r, ngx_http_geoip_module);
if (gcf->country == NULL) {
goto not_found;
}
#if (NGX_HAVE_GEOIP_V6)
val = gcf->country_v6
? handler_v6(gcf->country, ngx_http_geoip_addr_v6(r, gcf))
: handler(gcf->country, ngx_http_geoip_addr(r, gcf));
#else
val = handler(gcf->country, ngx_http_geoip_addr(r, gcf));
#endif
if (val == NULL) {
goto not_found;
}
v->len = ngx_strlen(val);
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = (u_char *) val;
return NGX_OK;
not_found:
v->not_found = 1;
return NGX_OK;
}
static ngx_int_t
ngx_http_geoip_org_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
size_t len;
char *val;
ngx_http_geoip_conf_t *gcf;
gcf = ngx_http_get_module_main_conf(r, ngx_http_geoip_module);
if (gcf->org == NULL) {
goto not_found;
}
#if (NGX_HAVE_GEOIP_V6)
val = gcf->org_v6
? GeoIP_name_by_ipnum_v6(gcf->org,
ngx_http_geoip_addr_v6(r, gcf))
: GeoIP_name_by_ipnum(gcf->org,
ngx_http_geoip_addr(r, gcf));
#else
val = GeoIP_name_by_ipnum(gcf->org, ngx_http_geoip_addr(r, gcf));
#endif
if (val == NULL) {
goto not_found;
}
len = ngx_strlen(val);
v->data = ngx_pnalloc(r->pool, len);
if (v->data == NULL) {
ngx_free(val);
return NGX_ERROR;
}
ngx_memcpy(v->data, val, len);
v->len = len;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
ngx_free(val);
return NGX_OK;
not_found:
v->not_found = 1;
return NGX_OK;
}
static ngx_int_t
ngx_http_geoip_city_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
char *val;
size_t len;
GeoIPRecord *gr;
gr = ngx_http_geoip_get_city_record(r);
if (gr == NULL) {
goto not_found;
}
val = *(char **) ((char *) gr + data);
if (val == NULL) {
goto no_value;
}
len = ngx_strlen(val);
v->data = ngx_pnalloc(r->pool, len);
if (v->data == NULL) {
GeoIPRecord_delete(gr);
return NGX_ERROR;
}
ngx_memcpy(v->data, val, len);
v->len = len;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
GeoIPRecord_delete(gr);
return NGX_OK;
no_value:
GeoIPRecord_delete(gr);
not_found:
v->not_found = 1;
return NGX_OK;
}
static ngx_int_t
ngx_http_geoip_region_name_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
size_t len;
const char *val;
GeoIPRecord *gr;
gr = ngx_http_geoip_get_city_record(r);
if (gr == NULL) {
goto not_found;
}
val = GeoIP_region_name_by_code(gr->country_code, gr->region);
GeoIPRecord_delete(gr);
if (val == NULL) {
goto not_found;
}
len = ngx_strlen(val);
v->data = ngx_pnalloc(r->pool, len);
if (v->data == NULL) {
return NGX_ERROR;
}
ngx_memcpy(v->data, val, len);
v->len = len;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
return NGX_OK;
not_found:
v->not_found = 1;
return NGX_OK;
}
static ngx_int_t
ngx_http_geoip_city_float_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
float val;
GeoIPRecord *gr;
gr = ngx_http_geoip_get_city_record(r);
if (gr == NULL) {
v->not_found = 1;
return NGX_OK;
}
v->data = ngx_pnalloc(r->pool, NGX_INT64_LEN + 5);
if (v->data == NULL) {
GeoIPRecord_delete(gr);
return NGX_ERROR;
}
val = *(float *) ((char *) gr + data);
v->len = ngx_sprintf(v->data, "%.4f", val) - v->data;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
GeoIPRecord_delete(gr);
return NGX_OK;
}
static ngx_int_t
ngx_http_geoip_city_int_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
int val;
GeoIPRecord *gr;
gr = ngx_http_geoip_get_city_record(r);
if (gr == NULL) {
v->not_found = 1;
return NGX_OK;
}
v->data = ngx_pnalloc(r->pool, NGX_INT64_LEN);
if (v->data == NULL) {
GeoIPRecord_delete(gr);
return NGX_ERROR;
}
val = *(int *) ((char *) gr + data);
v->len = ngx_sprintf(v->data, "%d", val) - v->data;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
GeoIPRecord_delete(gr);
return NGX_OK;
}
static GeoIPRecord *
ngx_http_geoip_get_city_record(ngx_http_request_t *r)
{
ngx_http_geoip_conf_t *gcf;
gcf = ngx_http_get_module_main_conf(r, ngx_http_geoip_module);
if (gcf->city) {
#if (NGX_HAVE_GEOIP_V6)
return gcf->city_v6
? GeoIP_record_by_ipnum_v6(gcf->city,
ngx_http_geoip_addr_v6(r, gcf))
: GeoIP_record_by_ipnum(gcf->city,
ngx_http_geoip_addr(r, gcf));
#else
return GeoIP_record_by_ipnum(gcf->city, ngx_http_geoip_addr(r, gcf));
#endif
}
return NULL;
}
static ngx_int_t
ngx_http_geoip_add_variables(ngx_conf_t *cf)
{
ngx_http_variable_t *var, *v;
for (v = ngx_http_geoip_vars; v->name.len; v++) {
var = ngx_http_add_variable(cf, &v->name, v->flags);
if (var == NULL) {
return NGX_ERROR;
}
var->get_handler = v->get_handler;
var->data = v->data;
}
return NGX_OK;
}
static void *
ngx_http_geoip_create_conf(ngx_conf_t *cf)
{
ngx_pool_cleanup_t *cln;
ngx_http_geoip_conf_t *conf;
conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_geoip_conf_t));
if (conf == NULL) {
return NULL;
}
conf->proxy_recursive = NGX_CONF_UNSET;
cln = ngx_pool_cleanup_add(cf->pool, 0);
if (cln == NULL) {
return NULL;
}
cln->handler = ngx_http_geoip_cleanup;
cln->data = conf;
return conf;
}
static char *
ngx_http_geoip_init_conf(ngx_conf_t *cf, void *conf)
{
ngx_http_geoip_conf_t *gcf = conf;
ngx_conf_init_value(gcf->proxy_recursive, 0);
return NGX_CONF_OK;
}
static char *
ngx_http_geoip_country(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_geoip_conf_t *gcf = conf;
ngx_str_t *value;
if (gcf->country) {
return "is duplicate";
}
value = cf->args->elts;
gcf->country = GeoIP_open((char *) value[1].data, GEOIP_MEMORY_CACHE);
if (gcf->country == NULL) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"GeoIP_open(\"%V\") failed", &value[1]);
return NGX_CONF_ERROR;
}
if (cf->args->nelts == 3) {
if (ngx_strcmp(value[2].data, "utf8") == 0) {
GeoIP_set_charset(gcf->country, GEOIP_CHARSET_UTF8);
} else {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid parameter \"%V\"", &value[2]);
return NGX_CONF_ERROR;
}
}
switch (gcf->country->databaseType) {
case GEOIP_COUNTRY_EDITION:
return NGX_CONF_OK;
#if (NGX_HAVE_GEOIP_V6)
case GEOIP_COUNTRY_EDITION_V6:
gcf->country_v6 = 1;
return NGX_CONF_OK;
#endif
default:
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid GeoIP database \"%V\" type:%d",
&value[1], gcf->country->databaseType);
return NGX_CONF_ERROR;
}
}
static char *
ngx_http_geoip_org(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_geoip_conf_t *gcf = conf;
ngx_str_t *value;
if (gcf->org) {
return "is duplicate";
}
value = cf->args->elts;
gcf->org = GeoIP_open((char *) value[1].data, GEOIP_MEMORY_CACHE);
if (gcf->org == NULL) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"GeoIP_open(\"%V\") failed", &value[1]);
return NGX_CONF_ERROR;
}
if (cf->args->nelts == 3) {
if (ngx_strcmp(value[2].data, "utf8") == 0) {
GeoIP_set_charset(gcf->org, GEOIP_CHARSET_UTF8);
} else {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid parameter \"%V\"", &value[2]);
return NGX_CONF_ERROR;
}
}
switch (gcf->org->databaseType) {
case GEOIP_ISP_EDITION:
case GEOIP_ORG_EDITION:
case GEOIP_DOMAIN_EDITION:
case GEOIP_ASNUM_EDITION:
return NGX_CONF_OK;
#if (NGX_HAVE_GEOIP_V6)
case GEOIP_ISP_EDITION_V6:
case GEOIP_ORG_EDITION_V6:
case GEOIP_DOMAIN_EDITION_V6:
case GEOIP_ASNUM_EDITION_V6:
gcf->org_v6 = 1;
return NGX_CONF_OK;
#endif
default:
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid GeoIP database \"%V\" type:%d",
&value[1], gcf->org->databaseType);
return NGX_CONF_ERROR;
}
}
static char *
ngx_http_geoip_city(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_geoip_conf_t *gcf = conf;
ngx_str_t *value;
if (gcf->city) {
return "is duplicate";
}
value = cf->args->elts;
gcf->city = GeoIP_open((char *) value[1].data, GEOIP_MEMORY_CACHE);
if (gcf->city == NULL) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"GeoIP_open(\"%V\") failed", &value[1]);
return NGX_CONF_ERROR;
}
if (cf->args->nelts == 3) {
if (ngx_strcmp(value[2].data, "utf8") == 0) {
GeoIP_set_charset(gcf->city, GEOIP_CHARSET_UTF8);
} else {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid parameter \"%V\"", &value[2]);
return NGX_CONF_ERROR;
}
}
switch (gcf->city->databaseType) {
case GEOIP_CITY_EDITION_REV0:
case GEOIP_CITY_EDITION_REV1:
return NGX_CONF_OK;
#if (NGX_HAVE_GEOIP_V6)
case GEOIP_CITY_EDITION_REV0_V6:
case GEOIP_CITY_EDITION_REV1_V6:
gcf->city_v6 = 1;
return NGX_CONF_OK;
#endif
default:
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid GeoIP City database \"%V\" type:%d",
&value[1], gcf->city->databaseType);
return NGX_CONF_ERROR;
}
}
static char *
ngx_http_geoip_proxy(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_geoip_conf_t *gcf = conf;
ngx_str_t *value;
ngx_cidr_t cidr, *c;
value = cf->args->elts;
if (ngx_http_geoip_cidr_value(cf, &value[1], &cidr) != NGX_OK) {
return NGX_CONF_ERROR;
}
if (gcf->proxies == NULL) {
gcf->proxies = ngx_array_create(cf->pool, 4, sizeof(ngx_cidr_t));
if (gcf->proxies == NULL) {
return NGX_CONF_ERROR;
}
}
c = ngx_array_push(gcf->proxies);
if (c == NULL) {
return NGX_CONF_ERROR;
}
*c = cidr;
return NGX_CONF_OK;
}
static ngx_int_t
ngx_http_geoip_cidr_value(ngx_conf_t *cf, ngx_str_t *net, ngx_cidr_t *cidr)
{
ngx_int_t rc;
if (ngx_strcmp(net->data, "255.255.255.255") == 0) {
cidr->family = AF_INET;
cidr->u.in.addr = 0xffffffff;
cidr->u.in.mask = 0xffffffff;
return NGX_OK;
}
rc = ngx_ptocidr(net, cidr);
if (rc == NGX_ERROR) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid network \"%V\"", net);
return NGX_ERROR;
}
if (rc == NGX_DONE) {
ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
"low address bits of %V are meaningless", net);
}
return NGX_OK;
}
static void
ngx_http_geoip_cleanup(void *data)
{
ngx_http_geoip_conf_t *gcf = data;
if (gcf->country) {
GeoIP_delete(gcf->country);
}
if (gcf->org) {
GeoIP_delete(gcf->org);
}
if (gcf->city) {
GeoIP_delete(gcf->city);
}
}
nginx-1.14.0/src/http/modules/ngx_http_grpc_module.c 000644 001751 001751 00000402722 13265410474 023720 0 ustar 00mdounin mdounin 000000 000000
/*
* Copyright (C) Maxim Dounin
* Copyright (C) Nginx, Inc.
*/
#include
#include
#include
typedef struct {
ngx_array_t *flushes;
ngx_array_t *lengths;
ngx_array_t *values;
ngx_hash_t hash;
} ngx_http_grpc_headers_t;
typedef struct {
ngx_http_upstream_conf_t upstream;
ngx_http_grpc_headers_t headers;
ngx_array_t *headers_source;
ngx_str_t host;
ngx_uint_t host_set;
#if (NGX_HTTP_SSL)
ngx_uint_t ssl;
ngx_uint_t ssl_protocols;
ngx_str_t ssl_ciphers;
ngx_uint_t ssl_verify_depth;
ngx_str_t ssl_trusted_certificate;
ngx_str_t ssl_crl;
ngx_str_t ssl_certificate;
ngx_str_t ssl_certificate_key;
ngx_array_t *ssl_passwords;
#endif
} ngx_http_grpc_loc_conf_t;
typedef enum {
ngx_http_grpc_st_start = 0,
ngx_http_grpc_st_length_2,
ngx_http_grpc_st_length_3,
ngx_http_grpc_st_type,
ngx_http_grpc_st_flags,
ngx_http_grpc_st_stream_id,
ngx_http_grpc_st_stream_id_2,
ngx_http_grpc_st_stream_id_3,
ngx_http_grpc_st_stream_id_4,
ngx_http_grpc_st_payload,
ngx_http_grpc_st_padding
} ngx_http_grpc_state_e;
typedef struct {
size_t init_window;
size_t send_window;
size_t recv_window;
ngx_uint_t last_stream_id;
} ngx_http_grpc_conn_t;
typedef struct {
ngx_http_grpc_state_e state;
ngx_uint_t frame_state;
ngx_uint_t fragment_state;
ngx_chain_t *in;
ngx_chain_t *out;
ngx_chain_t *free;
ngx_chain_t *busy;
ngx_http_grpc_conn_t *connection;
ngx_uint_t id;
ssize_t send_window;
size_t recv_window;
size_t rest;
ngx_uint_t stream_id;
u_char type;
u_char flags;
u_char padding;
ngx_uint_t error;
ngx_uint_t window_update;
ngx_uint_t setting_id;
ngx_uint_t setting_value;
u_char ping_data[8];
ngx_uint_t index;
ngx_str_t name;
ngx_str_t value;
u_char *field_end;
size_t field_length;
size_t field_rest;
u_char field_state;
unsigned literal:1;
unsigned field_huffman:1;
unsigned header_sent:1;
unsigned output_closed:1;
unsigned parsing_headers:1;
unsigned end_stream:1;
unsigned status:1;
ngx_http_request_t *request;
} ngx_http_grpc_ctx_t;
typedef struct {
u_char length_0;
u_char length_1;
u_char length_2;
u_char type;
u_char flags;
u_char stream_id_0;
u_char stream_id_1;
u_char stream_id_2;
u_char stream_id_3;
} ngx_http_grpc_frame_t;
static ngx_int_t ngx_http_grpc_create_request(ngx_http_request_t *r);
static ngx_int_t ngx_http_grpc_reinit_request(ngx_http_request_t *r);
static ngx_int_t ngx_http_grpc_body_output_filter(void *data, ngx_chain_t *in);
static ngx_int_t ngx_http_grpc_process_header(ngx_http_request_t *r);
static ngx_int_t ngx_http_grpc_filter_init(void *data);
static ngx_int_t ngx_http_grpc_filter(void *data, ssize_t bytes);
static ngx_int_t ngx_http_grpc_parse_frame(ngx_http_request_t *r,
ngx_http_grpc_ctx_t *ctx, ngx_buf_t *b);
static ngx_int_t ngx_http_grpc_parse_header(ngx_http_request_t *r,
ngx_http_grpc_ctx_t *ctx, ngx_buf_t *b);
static ngx_int_t ngx_http_grpc_parse_fragment(ngx_http_request_t *r,
ngx_http_grpc_ctx_t *ctx, ngx_buf_t *b);
static ngx_int_t ngx_http_grpc_validate_header_name(ngx_http_request_t *r,
ngx_str_t *s);
static ngx_int_t ngx_http_grpc_validate_header_value(ngx_http_request_t *r,
ngx_str_t *s);
static ngx_int_t ngx_http_grpc_parse_rst_stream(ngx_http_request_t *r,
ngx_http_grpc_ctx_t *ctx, ngx_buf_t *b);
static ngx_int_t ngx_http_grpc_parse_goaway(ngx_http_request_t *r,
ngx_http_grpc_ctx_t *ctx, ngx_buf_t *b);
static ngx_int_t ngx_http_grpc_parse_window_update(ngx_http_request_t *r,
ngx_http_grpc_ctx_t *ctx, ngx_buf_t *b);
static ngx_int_t ngx_http_grpc_parse_settings(ngx_http_request_t *r,
ngx_http_grpc_ctx_t *ctx, ngx_buf_t *b);
static ngx_int_t ngx_http_grpc_parse_ping(ngx_http_request_t *r,
ngx_http_grpc_ctx_t *ctx, ngx_buf_t *b);
static ngx_int_t ngx_http_grpc_send_settings_ack(ngx_http_request_t *r,
ngx_http_grpc_ctx_t *ctx);
static ngx_int_t ngx_http_grpc_send_ping_ack(ngx_http_request_t *r,
ngx_http_grpc_ctx_t *ctx);
static ngx_int_t ngx_http_grpc_send_window_update(ngx_http_request_t *r,
ngx_http_grpc_ctx_t *ctx);
static ngx_chain_t *ngx_http_grpc_get_buf(ngx_http_request_t *r,
ngx_http_grpc_ctx_t *ctx);
static ngx_http_grpc_ctx_t *ngx_http_grpc_get_ctx(ngx_http_request_t *r);
static ngx_int_t ngx_http_grpc_get_connection_data(ngx_http_request_t *r,
ngx_http_grpc_ctx_t *ctx, ngx_peer_connection_t *pc);
static void ngx_http_grpc_cleanup(void *data);
static void ngx_http_grpc_abort_request(ngx_http_request_t *r);
static void ngx_http_grpc_finalize_request(ngx_http_request_t *r,
ngx_int_t rc);
static ngx_int_t ngx_http_grpc_internal_trailers_variable(
ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_grpc_add_variables(ngx_conf_t *cf);
static void *ngx_http_grpc_create_loc_conf(ngx_conf_t *cf);
static char *ngx_http_grpc_merge_loc_conf(ngx_conf_t *cf,
void *parent, void *child);
static ngx_int_t ngx_http_grpc_init_headers(ngx_conf_t *cf,
ngx_http_grpc_loc_conf_t *conf, ngx_http_grpc_headers_t *headers,
ngx_keyval_t *default_headers);
static char *ngx_http_grpc_pass(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
#if (NGX_HTTP_SSL)
static char *ngx_http_grpc_ssl_password_file(ngx_conf_t *cf,
ngx_command_t *cmd, void *conf);
static ngx_int_t ngx_http_grpc_set_ssl(ngx_conf_t *cf,
ngx_http_grpc_loc_conf_t *glcf);
#endif
static ngx_conf_bitmask_t ngx_http_grpc_next_upstream_masks[] = {
{ ngx_string("error"), NGX_HTTP_UPSTREAM_FT_ERROR },
{ ngx_string("timeout"), NGX_HTTP_UPSTREAM_FT_TIMEOUT },
{ ngx_string("invalid_header"), NGX_HTTP_UPSTREAM_FT_INVALID_HEADER },
{ ngx_string("non_idempotent"), NGX_HTTP_UPSTREAM_FT_NON_IDEMPOTENT },
{ ngx_string("http_500"), NGX_HTTP_UPSTREAM_FT_HTTP_500 },
{ ngx_string("http_502"), NGX_HTTP_UPSTREAM_FT_HTTP_502 },
{ ngx_string("http_503"), NGX_HTTP_UPSTREAM_FT_HTTP_503 },
{ ngx_string("http_504"), NGX_HTTP_UPSTREAM_FT_HTTP_504 },
{ ngx_string("http_403"), NGX_HTTP_UPSTREAM_FT_HTTP_403 },
{ ngx_string("http_404"), NGX_HTTP_UPSTREAM_FT_HTTP_404 },
{ ngx_string("http_429"), NGX_HTTP_UPSTREAM_FT_HTTP_429 },
{ ngx_string("off"), NGX_HTTP_UPSTREAM_FT_OFF },
{ ngx_null_string, 0 }
};
#if (NGX_HTTP_SSL)
static ngx_conf_bitmask_t ngx_http_grpc_ssl_protocols[] = {
{ ngx_string("SSLv2"), NGX_SSL_SSLv2 },
{ ngx_string("SSLv3"), NGX_SSL_SSLv3 },
{ ngx_string("TLSv1"), NGX_SSL_TLSv1 },
{ ngx_string("TLSv1.1"), NGX_SSL_TLSv1_1 },
{ ngx_string("TLSv1.2"), NGX_SSL_TLSv1_2 },
{ ngx_string("TLSv1.3"), NGX_SSL_TLSv1_3 },
{ ngx_null_string, 0 }
};
#endif
static ngx_command_t ngx_http_grpc_commands[] = {
{ ngx_string("grpc_pass"),
NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,
ngx_http_grpc_pass,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
{ ngx_string("grpc_bind"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12,
ngx_http_upstream_bind_set_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_grpc_loc_conf_t, upstream.local),
NULL },
{ ngx_string("grpc_connect_timeout"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_msec_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_grpc_loc_conf_t, upstream.connect_timeout),
NULL },
{ ngx_string("grpc_send_timeout"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_msec_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_grpc_loc_conf_t, upstream.send_timeout),
NULL },
{ ngx_string("grpc_intercept_errors"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_grpc_loc_conf_t, upstream.intercept_errors),
NULL },
{ ngx_string("grpc_buffer_size"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_size_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_grpc_loc_conf_t, upstream.buffer_size),
NULL },
{ ngx_string("grpc_read_timeout"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_msec_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_grpc_loc_conf_t, upstream.read_timeout),
NULL },
{ ngx_string("grpc_next_upstream"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
ngx_conf_set_bitmask_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_grpc_loc_conf_t, upstream.next_upstream),
&ngx_http_grpc_next_upstream_masks },
{ ngx_string("grpc_next_upstream_tries"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_num_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_grpc_loc_conf_t, upstream.next_upstream_tries),
NULL },
{ ngx_string("grpc_next_upstream_timeout"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_msec_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_grpc_loc_conf_t, upstream.next_upstream_timeout),
NULL },
{ ngx_string("grpc_set_header"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
ngx_conf_set_keyval_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_grpc_loc_conf_t, headers_source),
NULL },
{ ngx_string("grpc_pass_header"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_str_array_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_grpc_loc_conf_t, upstream.pass_headers),
NULL },
{ ngx_string("grpc_hide_header"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_str_array_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_grpc_loc_conf_t, upstream.hide_headers),
NULL },
{ ngx_string("grpc_ignore_headers"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
ngx_conf_set_bitmask_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_grpc_loc_conf_t, upstream.ignore_headers),
&ngx_http_upstream_ignore_headers_masks },
#if (NGX_HTTP_SSL)
{ ngx_string("grpc_ssl_session_reuse"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_grpc_loc_conf_t, upstream.ssl_session_reuse),
NULL },
{ ngx_string("grpc_ssl_protocols"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
ngx_conf_set_bitmask_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_grpc_loc_conf_t, ssl_protocols),
&ngx_http_grpc_ssl_protocols },
{ ngx_string("grpc_ssl_ciphers"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_str_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_grpc_loc_conf_t, ssl_ciphers),
NULL },
{ ngx_string("grpc_ssl_name"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_http_set_complex_value_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_grpc_loc_conf_t, upstream.ssl_name),
NULL },
{ ngx_string("grpc_ssl_server_name"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_grpc_loc_conf_t, upstream.ssl_server_name),
NULL },
{ ngx_string("grpc_ssl_verify"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_grpc_loc_conf_t, upstream.ssl_verify),
NULL },
{ ngx_string("grpc_ssl_verify_depth"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_num_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_grpc_loc_conf_t, ssl_verify_depth),
NULL },
{ ngx_string("grpc_ssl_trusted_certificate"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_str_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_grpc_loc_conf_t, ssl_trusted_certificate),
NULL },
{ ngx_string("grpc_ssl_crl"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_str_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_grpc_loc_conf_t, ssl_crl),
NULL },
{ ngx_string("grpc_ssl_certificate"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_str_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_grpc_loc_conf_t, ssl_certificate),
NULL },
{ ngx_string("grpc_ssl_certificate_key"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_str_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_grpc_loc_conf_t, ssl_certificate_key),
NULL },
{ ngx_string("grpc_ssl_password_file"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_http_grpc_ssl_password_file,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
#endif
ngx_null_command
};
static ngx_http_module_t ngx_http_grpc_module_ctx = {
ngx_http_grpc_add_variables, /* preconfiguration */
NULL, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
ngx_http_grpc_create_loc_conf, /* create location configuration */
ngx_http_grpc_merge_loc_conf /* merge location configuration */
};
ngx_module_t ngx_http_grpc_module = {
NGX_MODULE_V1,
&ngx_http_grpc_module_ctx, /* module context */
ngx_http_grpc_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static u_char ngx_http_grpc_connection_start[] =
"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n" /* connection preface */
"\x00\x00\x12\x04\x00\x00\x00\x00\x00" /* settings frame */
"\x00\x01\x00\x00\x00\x00" /* header table size */
"\x00\x02\x00\x00\x00\x00" /* disable push */
"\x00\x04\x7f\xff\xff\xff" /* initial window */
"\x00\x00\x04\x08\x00\x00\x00\x00\x00" /* window update frame */
"\x7f\xff\x00\x00";
static ngx_keyval_t ngx_http_grpc_headers[] = {
{ ngx_string("Content-Length"), ngx_string("$content_length") },
{ ngx_string("TE"), ngx_string("$grpc_internal_trailers") },
{ ngx_string("Host"), ngx_string("") },
{ ngx_string("Connection"), ngx_string("") },
{ ngx_string("Transfer-Encoding"), ngx_string("") },
{ ngx_string("Keep-Alive"), ngx_string("") },
{ ngx_string("Expect"), ngx_string("") },
{ ngx_string("Upgrade"), ngx_string("") },
{ ngx_null_string, ngx_null_string }
};
static ngx_str_t ngx_http_grpc_hide_headers[] = {
ngx_string("Date"),
ngx_string("Server"),
ngx_string("X-Accel-Expires"),
ngx_string("X-Accel-Redirect"),
ngx_string("X-Accel-Limit-Rate"),
ngx_string("X-Accel-Buffering"),
ngx_string("X-Accel-Charset"),
ngx_null_string
};
static ngx_http_variable_t ngx_http_grpc_vars[] = {
{ ngx_string("grpc_internal_trailers"), NULL,
ngx_http_grpc_internal_trailers_variable, 0,
NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
ngx_http_null_variable
};
static ngx_int_t
ngx_http_grpc_handler(ngx_http_request_t *r)
{
ngx_int_t rc;
ngx_http_upstream_t *u;
ngx_http_grpc_ctx_t *ctx;
ngx_http_grpc_loc_conf_t *glcf;
if (ngx_http_upstream_create(r) != NGX_OK) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
glcf = ngx_http_get_module_loc_conf(r, ngx_http_grpc_module);
u = r->upstream;
#if (NGX_HTTP_SSL)
u->ssl = (glcf->upstream.ssl != NULL);
if (u->ssl) {
ngx_str_set(&u->schema, "grpcs://");
} else {
ngx_str_set(&u->schema, "grpc://");
}
#else
ngx_str_set(&u->schema, "grpc://");
#endif
u->output.tag = (ngx_buf_tag_t) &ngx_http_grpc_module;
u->conf = &glcf->upstream;
u->create_request = ngx_http_grpc_create_request;
u->reinit_request = ngx_http_grpc_reinit_request;
u->process_header = ngx_http_grpc_process_header;
u->abort_request = ngx_http_grpc_abort_request;
u->finalize_request = ngx_http_grpc_finalize_request;
ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_grpc_ctx_t));
if (ctx == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
ctx->request = r;
ngx_http_set_ctx(r, ctx, ngx_http_grpc_module);
u->input_filter_init = ngx_http_grpc_filter_init;
u->input_filter = ngx_http_grpc_filter;
u->input_filter_ctx = ctx;
r->request_body_no_buffering = 1;
rc = ngx_http_read_client_request_body(r, ngx_http_upstream_init);
if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
return rc;
}
return NGX_DONE;
}
static ngx_int_t
ngx_http_grpc_create_request(ngx_http_request_t *r)
{
u_char *p, *tmp, *key_tmp, *val_tmp, *headers_frame;
size_t len, tmp_len, key_len, val_len, uri_len;
uintptr_t escape;
ngx_buf_t *b;
ngx_uint_t i, next;
ngx_chain_t *cl, *body;
ngx_list_part_t *part;
ngx_table_elt_t *header;
ngx_http_upstream_t *u;
ngx_http_grpc_frame_t *f;
ngx_http_script_code_pt code;
ngx_http_grpc_loc_conf_t *glcf;
ngx_http_script_engine_t e, le;
ngx_http_script_len_code_pt lcode;
u = r->upstream;
glcf = ngx_http_get_module_loc_conf(r, ngx_http_grpc_module);
len = sizeof(ngx_http_grpc_connection_start) - 1
+ sizeof(ngx_http_grpc_frame_t); /* headers frame */
/* :method header */
if (r->method == NGX_HTTP_GET || r->method == NGX_HTTP_POST) {
len += 1;
tmp_len = 0;
} else {
len += 1 + NGX_HTTP_V2_INT_OCTETS + r->method_name.len;
tmp_len = r->method_name.len;
}
/* :scheme header */
len += 1;
/* :path header */
if (r->valid_unparsed_uri) {
escape = 0;
uri_len = r->unparsed_uri.len;
} else {
escape = 2 * ngx_escape_uri(NULL, r->uri.data, r->uri.len,
NGX_ESCAPE_URI);
uri_len = r->uri.len + escape + sizeof("?") - 1 + r->args.len;
}
len += 1 + NGX_HTTP_V2_INT_OCTETS + uri_len;
if (tmp_len < uri_len) {
tmp_len = uri_len;
}
/* :authority header */
if (!glcf->host_set) {
len += 1 + NGX_HTTP_V2_INT_OCTETS + glcf->host.len;
if (tmp_len < glcf->host.len) {
tmp_len = glcf->host.len;
}
}
/* other headers */
ngx_http_script_flush_no_cacheable_variables(r, glcf->headers.flushes);
ngx_memzero(&le, sizeof(ngx_http_script_engine_t));
le.ip = glcf->headers.lengths->elts;
le.request = r;
le.flushed = 1;
while (*(uintptr_t *) le.ip) {
lcode = *(ngx_http_script_len_code_pt *) le.ip;
key_len = lcode(&le);
for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) {
lcode = *(ngx_http_script_len_code_pt *) le.ip;
}
le.ip += sizeof(uintptr_t);
if (val_len == 0) {
continue;
}
len += 1 + NGX_HTTP_V2_INT_OCTETS + key_len
+ NGX_HTTP_V2_INT_OCTETS + val_len;
if (tmp_len < key_len) {
tmp_len = key_len;
}
if (tmp_len < val_len) {
tmp_len = val_len;
}
}
if (glcf->upstream.pass_request_headers) {
part = &r->headers_in.headers.part;
header = part->elts;
for (i = 0; /* void */; i++) {
if (i >= part->nelts) {
if (part->next == NULL) {
break;
}
part = part->next;
header = part->elts;
i = 0;
}
if (ngx_hash_find(&glcf->headers.hash, header[i].hash,
header[i].lowcase_key, header[i].key.len))
{
continue;
}
len += 1 + NGX_HTTP_V2_INT_OCTETS + header[i].key.len
+ NGX_HTTP_V2_INT_OCTETS + header[i].value.len;
if (tmp_len < header[i].key.len) {
tmp_len = header[i].key.len;
}
if (tmp_len < header[i].value.len) {
tmp_len = header[i].value.len;
}
}
}
/* continuation frames */
len += sizeof(ngx_http_grpc_frame_t)
* (len / NGX_HTTP_V2_DEFAULT_FRAME_SIZE);
b = ngx_create_temp_buf(r->pool, len);
if (b == NULL) {
return NGX_ERROR;
}
cl = ngx_alloc_chain_link(r->pool);
if (cl == NULL) {
return NGX_ERROR;
}
cl->buf = b;
cl->next = NULL;
tmp = ngx_palloc(r->pool, tmp_len * 3);
if (tmp == NULL) {
return NGX_ERROR;
}
key_tmp = tmp + tmp_len;
val_tmp = tmp + 2 * tmp_len;
/* connection preface */
b->last = ngx_copy(b->last, ngx_http_grpc_connection_start,
sizeof(ngx_http_grpc_connection_start) - 1);
/* headers frame */
headers_frame = b->last;
f = (ngx_http_grpc_frame_t *) b->last;
b->last += sizeof(ngx_http_grpc_frame_t);
f->length_0 = 0;
f->length_1 = 0;
f->length_2 = 0;
f->type = NGX_HTTP_V2_HEADERS_FRAME;
f->flags = 0;
f->stream_id_0 = 0;
f->stream_id_1 = 0;
f->stream_id_2 = 0;
f->stream_id_3 = 1;
if (r->method == NGX_HTTP_GET) {
*b->last++ = ngx_http_v2_indexed(NGX_HTTP_V2_METHOD_GET_INDEX);
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"grpc header: \":method: GET\"");
} else if (r->method == NGX_HTTP_POST) {
*b->last++ = ngx_http_v2_indexed(NGX_HTTP_V2_METHOD_POST_INDEX);
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"grpc header: \":method: POST\"");
} else {
*b->last++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_METHOD_INDEX);
b->last = ngx_http_v2_write_value(b->last, r->method_name.data,
r->method_name.len, tmp);
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"grpc header: \":method: %V\"", &r->method_name);
}
#if (NGX_HTTP_SSL)
if (glcf->ssl) {
*b->last++ = ngx_http_v2_indexed(NGX_HTTP_V2_SCHEME_HTTPS_INDEX);
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"grpc header: \":scheme: https\"");
} else
#endif
{
*b->last++ = ngx_http_v2_indexed(NGX_HTTP_V2_SCHEME_HTTP_INDEX);
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"grpc header: \":scheme: http\"");
}
if (r->valid_unparsed_uri) {
if (r->unparsed_uri.len == 1 && r->unparsed_uri.data[0] == '/') {
*b->last++ = ngx_http_v2_indexed(NGX_HTTP_V2_PATH_ROOT_INDEX);
} else {
*b->last++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_PATH_INDEX);
b->last = ngx_http_v2_write_value(b->last, r->unparsed_uri.data,
r->unparsed_uri.len, tmp);
}
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"grpc header: \":path: %V\"", &r->unparsed_uri);
} else if (escape || r->args.len > 0) {
p = val_tmp;
if (escape) {
p = (u_char *) ngx_escape_uri(p, r->uri.data, r->uri.len,
NGX_ESCAPE_URI);
} else {
p = ngx_copy(p, r->uri.data, r->uri.len);
}
if (r->args.len > 0) {
*p++ = '?';
p = ngx_copy(p, r->args.data, r->args.len);
}
*b->last++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_PATH_INDEX);
b->last = ngx_http_v2_write_value(b->last, val_tmp, p - val_tmp, tmp);
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"grpc header: \":path: %*s\"", p - val_tmp, val_tmp);
} else {
*b->last++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_PATH_INDEX);
b->last = ngx_http_v2_write_value(b->last, r->uri.data,
r->uri.len, tmp);
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"grpc header: \":path: %V\"", &r->uri);
}
if (!glcf->host_set) {
*b->last++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_AUTHORITY_INDEX);
b->last = ngx_http_v2_write_value(b->last, glcf->host.data,
glcf->host.len, tmp);
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"grpc header: \":authority: %V\"", &glcf->host);
}
ngx_memzero(&e, sizeof(ngx_http_script_engine_t));
e.ip = glcf->headers.values->elts;
e.request = r;
e.flushed = 1;
le.ip = glcf->headers.lengths->elts;
while (*(uintptr_t *) le.ip) {
lcode = *(ngx_http_script_len_code_pt *) le.ip;
key_len = lcode(&le);
for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) {
lcode = *(ngx_http_script_len_code_pt *) le.ip;
}
le.ip += sizeof(uintptr_t);
if (val_len == 0) {
e.skip = 1;
while (*(uintptr_t *) e.ip) {
code = *(ngx_http_script_code_pt *) e.ip;
code((ngx_http_script_engine_t *) &e);
}
e.ip += sizeof(uintptr_t);
e.skip = 0;
continue;
}
*b->last++ = 0;
e.pos = key_tmp;
code = *(ngx_http_script_code_pt *) e.ip;
code((ngx_http_script_engine_t *) &e);
b->last = ngx_http_v2_write_name(b->last, key_tmp, key_len, tmp);
e.pos = val_tmp;
while (*(uintptr_t *) e.ip) {
code = *(ngx_http_script_code_pt *) e.ip;
code((ngx_http_script_engine_t *) &e);
}
e.ip += sizeof(uintptr_t);
b->last = ngx_http_v2_write_value(b->last, val_tmp, val_len, tmp);
#if (NGX_DEBUG)
if (r->connection->log->log_level & NGX_LOG_DEBUG_HTTP) {
ngx_strlow(key_tmp, key_tmp, key_len);
ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"grpc header: \"%*s: %*s\"",
key_len, key_tmp, val_len, val_tmp);
}
#endif
}
if (glcf->upstream.pass_request_headers) {
part = &r->headers_in.headers.part;
header = part->elts;
for (i = 0; /* void */; i++) {
if (i >= part->nelts) {
if (part->next == NULL) {
break;
}
part = part->next;
header = part->elts;
i = 0;
}
if (ngx_hash_find(&glcf->headers.hash, header[i].hash,
header[i].lowcase_key, header[i].key.len))
{
continue;
}
*b->last++ = 0;
b->last = ngx_http_v2_write_name(b->last, header[i].key.data,
header[i].key.len, tmp);
b->last = ngx_http_v2_write_value(b->last, header[i].value.data,
header[i].value.len, tmp);
#if (NGX_DEBUG)
if (r->connection->log->log_level & NGX_LOG_DEBUG_HTTP) {
ngx_strlow(tmp, header[i].key.data, header[i].key.len);
ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"grpc header: \"%*s: %V\"",
header[i].key.len, tmp, &header[i].value);
}
#endif
}
}
/* update headers frame length */
len = b->last - headers_frame - sizeof(ngx_http_grpc_frame_t);
if (len > NGX_HTTP_V2_DEFAULT_FRAME_SIZE) {
len = NGX_HTTP_V2_DEFAULT_FRAME_SIZE;
next = 1;
} else {
next = 0;
}
f = (ngx_http_grpc_frame_t *) headers_frame;
f->length_0 = (u_char) ((len >> 16) & 0xff);
f->length_1 = (u_char) ((len >> 8) & 0xff);
f->length_2 = (u_char) (len & 0xff);
/* create additional continuation frames */
p = headers_frame;
while (next) {
p += sizeof(ngx_http_grpc_frame_t) + NGX_HTTP_V2_DEFAULT_FRAME_SIZE;
len = b->last - p;
ngx_memmove(p + sizeof(ngx_http_grpc_frame_t), p, len);
b->last += sizeof(ngx_http_grpc_frame_t);
if (len > NGX_HTTP_V2_DEFAULT_FRAME_SIZE) {
len = NGX_HTTP_V2_DEFAULT_FRAME_SIZE;
next = 1;
} else {
next = 0;
}
f = (ngx_http_grpc_frame_t *) p;
f->length_0 = (u_char) ((len >> 16) & 0xff);
f->length_1 = (u_char) ((len >> 8) & 0xff);
f->length_2 = (u_char) (len & 0xff);
f->type = NGX_HTTP_V2_CONTINUATION_FRAME;
f->flags = 0;
f->stream_id_0 = 0;
f->stream_id_1 = 0;
f->stream_id_2 = 0;
f->stream_id_3 = 1;
}
f->flags |= NGX_HTTP_V2_END_HEADERS_FLAG;
#if (NGX_DEBUG)
if (r->connection->log->log_level & NGX_LOG_DEBUG_HTTP) {
u_char buf[512];
size_t n, m;
n = ngx_min(b->last - b->pos, 256);
m = ngx_hex_dump(buf, b->pos, n) - buf;
ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"grpc header: %*s%s, len: %uz",
m, buf, b->last - b->pos > 256 ? "..." : "",
b->last - b->pos);
}
#endif
if (r->request_body_no_buffering) {
u->request_bufs = cl;
} else {
body = u->request_bufs;
u->request_bufs = cl;
if (body == NULL) {
f = (ngx_http_grpc_frame_t *) headers_frame;
f->flags |= NGX_HTTP_V2_END_STREAM_FLAG;
}
while (body) {
b = ngx_alloc_buf(r->pool);
if (b == NULL) {
return NGX_ERROR;
}
ngx_memcpy(b, body->buf, sizeof(ngx_buf_t));
cl->next = ngx_alloc_chain_link(r->pool);
if (cl->next == NULL) {
return NGX_ERROR;
}
cl = cl->next;
cl->buf = b;
body = body->next;
}
b->last_buf = 1;
}
u->output.output_filter = ngx_http_grpc_body_output_filter;
u->output.filter_ctx = r;
b->flush = 1;
cl->next = NULL;
return NGX_OK;
}
static ngx_int_t
ngx_http_grpc_reinit_request(ngx_http_request_t *r)
{
ngx_http_grpc_ctx_t *ctx;
ctx = ngx_http_get_module_ctx(r, ngx_http_grpc_module);
if (ctx == NULL) {
return NGX_OK;
}
ctx->state = 0;
ctx->header_sent = 0;
ctx->output_closed = 0;
ctx->parsing_headers = 0;
ctx->end_stream = 0;
ctx->status = 0;
ctx->connection = NULL;
return NGX_OK;
}
static ngx_int_t
ngx_http_grpc_body_output_filter(void *data, ngx_chain_t *in)
{
ngx_http_request_t *r = data;
off_t file_pos;
u_char *p, *pos, *start;
size_t len, limit;
ngx_buf_t *b;
ngx_int_t rc;
ngx_uint_t next, last;
ngx_chain_t *cl, *out, **ll;
ngx_http_grpc_ctx_t *ctx;
ngx_http_grpc_frame_t *f;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"grpc output filter");
ctx = ngx_http_grpc_get_ctx(r);
if (ctx == NULL) {
return NGX_ERROR;
}
if (in) {
if (ngx_chain_add_copy(r->pool, &ctx->in, in) != NGX_OK) {
return NGX_ERROR;
}
}
out = NULL;
ll = &out;
if (!ctx->header_sent) {
/* first buffer contains headers */
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"grpc output header");
ctx->header_sent = 1;
if (ctx->id != 1) {
/*
* keepalive connection: skip connection preface,
* update stream identifiers
*/
b = ctx->in->buf;
b->pos += sizeof(ngx_http_grpc_connection_start) - 1;
p = b->pos;
while (p < b->last) {
f = (ngx_http_grpc_frame_t *) p;
p += sizeof(ngx_http_grpc_frame_t);
f->stream_id_0 = (u_char) ((ctx->id >> 24) & 0xff);
f->stream_id_1 = (u_char) ((ctx->id >> 16) & 0xff);
f->stream_id_2 = (u_char) ((ctx->id >> 8) & 0xff);
f->stream_id_3 = (u_char) (ctx->id & 0xff);
p += (f->length_0 << 16) + (f->length_1 << 8) + f->length_2;
}
}
if (ctx->in->buf->last_buf) {
ctx->output_closed = 1;
}
*ll = ctx->in;
ll = &ctx->in->next;
ctx->in = ctx->in->next;
}
if (ctx->out) {
/* queued control frames */
*ll = ctx->out;
for (cl = ctx->out, ll = &cl->next; cl; cl = cl->next) {
ll = &cl->next;
}
ctx->out = NULL;
}
f = NULL;
last = 0;
limit = ngx_max(0, ctx->send_window);
if (limit > ctx->connection->send_window) {
limit = ctx->connection->send_window;
}
ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"grpc output limit: %uz w:%z:%uz",
limit, ctx->send_window, ctx->connection->send_window);
#if (NGX_SUPPRESS_WARN)
file_pos = 0;
pos = NULL;
cl = NULL;
#endif
in = ctx->in;
while (in && limit > 0) {
ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0,
"grpc output in l:%d f:%d %p, pos %p, size: %z "
"file: %O, size: %O",
in->buf->last_buf,
in->buf->in_file,
in->buf->start, in->buf->pos,
in->buf->last - in->buf->pos,
in->buf->file_pos,
in->buf->file_last - in->buf->file_pos);
if (ngx_buf_special(in->buf)) {
goto next;
}
if (in->buf->in_file) {
file_pos = in->buf->file_pos;
} else {
pos = in->buf->pos;
}
next = 0;
do {
cl = ngx_http_grpc_get_buf(r, ctx);
if (cl == NULL) {
return NGX_ERROR;
}
b = cl->buf;
f = (ngx_http_grpc_frame_t *) b->last;
b->last += sizeof(ngx_http_grpc_frame_t);
*ll = cl;
ll = &cl->next;
cl = ngx_chain_get_free_buf(r->pool, &ctx->free);
if (cl == NULL) {
return NGX_ERROR;
}
b = cl->buf;
start = b->start;
ngx_memcpy(b, in->buf, sizeof(ngx_buf_t));
/*
* restore b->start to preserve memory allocated in the buffer,
* to reuse it later for headers and control frames
*/
b->start = start;
if (in->buf->in_file) {
b->file_pos = file_pos;
file_pos += ngx_min(NGX_HTTP_V2_DEFAULT_FRAME_SIZE, limit);
if (file_pos >= in->buf->file_last) {
file_pos = in->buf->file_last;
next = 1;
}
b->file_last = file_pos;
len = (ngx_uint_t) (file_pos - b->file_pos);
} else {
b->pos = pos;
pos += ngx_min(NGX_HTTP_V2_DEFAULT_FRAME_SIZE, limit);
if (pos >= in->buf->last) {
pos = in->buf->last;
next = 1;
}
b->last = pos;
len = (ngx_uint_t) (pos - b->pos);
}
b->tag = (ngx_buf_tag_t) &ngx_http_grpc_body_output_filter;
b->shadow = in->buf;
b->last_shadow = next;
b->last_buf = 0;
b->last_in_chain = 0;
*ll = cl;
ll = &cl->next;
f->length_0 = (u_char) ((len >> 16) & 0xff);
f->length_1 = (u_char) ((len >> 8) & 0xff);
f->length_2 = (u_char) (len & 0xff);
f->type = NGX_HTTP_V2_DATA_FRAME;
f->flags = 0;
f->stream_id_0 = (u_char) ((ctx->id >> 24) & 0xff);
f->stream_id_1 = (u_char) ((ctx->id >> 16) & 0xff);
f->stream_id_2 = (u_char) ((ctx->id >> 8) & 0xff);
f->stream_id_3 = (u_char) (ctx->id & 0xff);
limit -= len;
ctx->send_window -= len;
ctx->connection->send_window -= len;
} while (!next && limit > 0);
if (!next) {
/*
* if the buffer wasn't fully sent due to flow control limits,
* preserve position for future use
*/
if (in->buf->in_file) {
in->buf->file_pos = file_pos;
} else {
in->buf->pos = pos;
}
break;
}
next:
if (in->buf->last_buf) {
last = 1;
}
in = in->next;
}
ctx->in = in;
if (last) {
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"grpc output last");
ctx->output_closed = 1;
if (f) {
f->flags |= NGX_HTTP_V2_END_STREAM_FLAG;
} else {
cl = ngx_http_grpc_get_buf(r, ctx);
if (cl == NULL) {
return NGX_ERROR;
}
b = cl->buf;
f = (ngx_http_grpc_frame_t *) b->last;
b->last += sizeof(ngx_http_grpc_frame_t);
f->length_0 = 0;
f->length_1 = 0;
f->length_2 = 0;
f->type = NGX_HTTP_V2_DATA_FRAME;
f->flags = NGX_HTTP_V2_END_STREAM_FLAG;
f->stream_id_0 = (u_char) ((ctx->id >> 24) & 0xff);
f->stream_id_1 = (u_char) ((ctx->id >> 16) & 0xff);
f->stream_id_2 = (u_char) ((ctx->id >> 8) & 0xff);
f->stream_id_3 = (u_char) (ctx->id & 0xff);
*ll = cl;
ll = &cl->next;
}
cl->buf->last_buf = 1;
}
*ll = NULL;
#if (NGX_DEBUG)
for (cl = out; cl; cl = cl->next) {
ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0,
"grpc output out l:%d f:%d %p, pos %p, size: %z "
"file: %O, size: %O",
cl->buf->last_buf,
cl->buf->in_file,
cl->buf->start, cl->buf->pos,
cl->buf->last - cl->buf->pos,
cl->buf->file_pos,
cl->buf->file_last - cl->buf->file_pos);
}
ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"grpc output limit: %uz w:%z:%uz",
limit, ctx->send_window, ctx->connection->send_window);
#endif
rc = ngx_chain_writer(&r->upstream->writer, out);
ngx_chain_update_chains(r->pool, &ctx->free, &ctx->busy, &out,
(ngx_buf_tag_t) &ngx_http_grpc_body_output_filter);
for (cl = ctx->free; cl; cl = cl->next) {
/* mark original buffers as sent */
if (cl->buf->shadow) {
if (cl->buf->last_shadow) {
b = cl->buf->shadow;
b->pos = b->last;
}
cl->buf->shadow = NULL;
}
}
if (rc == NGX_OK && ctx->in) {
rc = NGX_AGAIN;
}
return rc;
}
static ngx_int_t
ngx_http_grpc_process_header(ngx_http_request_t *r)
{
ngx_str_t *status_line;
ngx_int_t rc, status;
ngx_buf_t *b;
ngx_table_elt_t *h;
ngx_http_upstream_t *u;
ngx_http_grpc_ctx_t *ctx;
ngx_http_upstream_header_t *hh;
ngx_http_upstream_main_conf_t *umcf;
u = r->upstream;
b = &u->buffer;
#if (NGX_DEBUG)
if (r->connection->log->log_level & NGX_LOG_DEBUG_HTTP) {
u_char buf[512];
size_t n, m;
n = ngx_min(b->last - b->pos, 256);
m = ngx_hex_dump(buf, b->pos, n) - buf;
ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"grpc response: %*s%s, len: %uz",
m, buf, b->last - b->pos > 256 ? "..." : "",
b->last - b->pos);
}
#endif
ctx = ngx_http_grpc_get_ctx(r);
if (ctx == NULL) {
return NGX_ERROR;
}
umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module);
for ( ;; ) {
if (ctx->state < ngx_http_grpc_st_payload) {
rc = ngx_http_grpc_parse_frame(r, ctx, b);
if (rc == NGX_AGAIN) {
/*
* there can be a lot of window update frames,
* so we reset buffer if it is empty and we haven't
* started parsing headers yet
*/
if (!ctx->parsing_headers) {
b->pos = b->start;
b->last = b->pos;
}
return NGX_AGAIN;
}
if (rc == NGX_ERROR) {
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
}
/*
* RFC 7540 says that implementations MUST discard frames
* that have unknown or unsupported types. However, extension
* frames that appear in the middle of a header block are
* not permitted. Also, for obvious reasons CONTINUATION frames
* cannot appear before headers, and DATA frames are not expected
* to appear before all headers are parsed.
*/
if (ctx->type == NGX_HTTP_V2_DATA_FRAME
|| (ctx->type == NGX_HTTP_V2_CONTINUATION_FRAME
&& !ctx->parsing_headers)
|| (ctx->type != NGX_HTTP_V2_CONTINUATION_FRAME
&& ctx->parsing_headers))
{
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent unexpected http2 frame: %d",
ctx->type);
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
}
if (ctx->stream_id && ctx->stream_id != ctx->id) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent frame for unknown stream %ui",
ctx->stream_id);
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
}
}
/* frame payload */
if (ctx->type == NGX_HTTP_V2_RST_STREAM_FRAME) {
rc = ngx_http_grpc_parse_rst_stream(r, ctx, b);
if (rc == NGX_AGAIN) {
return NGX_AGAIN;
}
if (rc == NGX_ERROR) {
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
}
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream rejected request with error %ui",
ctx->error);
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
}
if (ctx->type == NGX_HTTP_V2_GOAWAY_FRAME) {
rc = ngx_http_grpc_parse_goaway(r, ctx, b);
if (rc == NGX_AGAIN) {
return NGX_AGAIN;
}
if (rc == NGX_ERROR) {
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
}
/*
* If stream_id is lower than one we use, our
* request won't be processed and needs to be retried.
* If stream_id is greater or equal to the one we use,
* we can continue normally (except we can't use this
* connection for additional requests). If there is
* a real error, the connection will be closed.
*/
if (ctx->stream_id < ctx->id) {
/* TODO: we can retry non-idempotent requests */
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent goaway with error %ui",
ctx->error);
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
}
continue;
}
if (ctx->type == NGX_HTTP_V2_WINDOW_UPDATE_FRAME) {
rc = ngx_http_grpc_parse_window_update(r, ctx, b);
if (rc == NGX_AGAIN) {
return NGX_AGAIN;
}
if (rc == NGX_ERROR) {
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
}
if (ctx->in) {
ngx_post_event(u->peer.connection->write, &ngx_posted_events);
}
continue;
}
if (ctx->type == NGX_HTTP_V2_SETTINGS_FRAME) {
rc = ngx_http_grpc_parse_settings(r, ctx, b);
if (rc == NGX_AGAIN) {
return NGX_AGAIN;
}
if (rc == NGX_ERROR) {
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
}
if (ctx->in) {
ngx_post_event(u->peer.connection->write, &ngx_posted_events);
}
continue;
}
if (ctx->type == NGX_HTTP_V2_PING_FRAME) {
rc = ngx_http_grpc_parse_ping(r, ctx, b);
if (rc == NGX_AGAIN) {
return NGX_AGAIN;
}
if (rc == NGX_ERROR) {
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
}
ngx_post_event(u->peer.connection->write, &ngx_posted_events);
continue;
}
if (ctx->type == NGX_HTTP_V2_PUSH_PROMISE_FRAME) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent unexpected push promise frame");
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
}
if (ctx->type != NGX_HTTP_V2_HEADERS_FRAME
&& ctx->type != NGX_HTTP_V2_CONTINUATION_FRAME)
{
/* priority, unknown frames */
if (b->last - b->pos < (ssize_t) ctx->rest) {
ctx->rest -= b->last - b->pos;
b->pos = b->last;
return NGX_AGAIN;
}
b->pos += ctx->rest;
ctx->rest = 0;
ctx->state = ngx_http_grpc_st_start;
continue;
}
/* headers */
for ( ;; ) {
rc = ngx_http_grpc_parse_header(r, ctx, b);
if (rc == NGX_AGAIN) {
break;
}
if (rc == NGX_OK) {
/* a header line has been parsed successfully */
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"grpc header: \"%V: %V\"",
&ctx->name, &ctx->value);
if (ctx->name.len && ctx->name.data[0] == ':') {
if (ctx->name.len != sizeof(":status") - 1
|| ngx_strncmp(ctx->name.data, ":status",
sizeof(":status") - 1)
!= 0)
{
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent invalid header \"%V: %V\"",
&ctx->name, &ctx->value);
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
}
if (ctx->status) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent duplicate :status header");
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
}
status_line = &ctx->value;
if (status_line->len != 3) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent invalid :status \"%V\"",
status_line);
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
}
status = ngx_atoi(status_line->data, 3);
if (status == NGX_ERROR) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent invalid :status \"%V\"",
status_line);
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
}
if (status < NGX_HTTP_OK) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent unexpected :status \"%V\"",
status_line);
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
}
u->headers_in.status_n = status;
if (u->state && u->state->status == 0) {
u->state->status = status;
}
ctx->status = 1;
continue;
} else if (!ctx->status) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent no :status header");
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
}
h = ngx_list_push(&u->headers_in.headers);
if (h == NULL) {
return NGX_ERROR;
}
h->key = ctx->name;
h->value = ctx->value;
h->lowcase_key = h->key.data;
h->hash = ngx_hash_key(h->key.data, h->key.len);
hh = ngx_hash_find(&umcf->headers_in_hash, h->hash,
h->lowcase_key, h->key.len);
if (hh && hh->handler(r, h, hh->offset) != NGX_OK) {
return NGX_ERROR;
}
continue;
}
if (rc == NGX_HTTP_PARSE_HEADER_DONE) {
/* a whole header has been parsed successfully */
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"grpc header done");
if (ctx->end_stream) {
u->headers_in.content_length_n = 0;
if (ctx->in == NULL
&& ctx->out == NULL
&& ctx->output_closed
&& b->last == b->pos)
{
u->keepalive = 1;
}
}
return NGX_OK;
}
/* there was error while a header line parsing */
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent invalid header");
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
}
/* rc == NGX_AGAIN */
if (ctx->rest == 0) {
ctx->state = ngx_http_grpc_st_start;
continue;
}
return NGX_AGAIN;
}
}
static ngx_int_t
ngx_http_grpc_filter_init(void *data)
{
ngx_http_grpc_ctx_t *ctx = data;
ngx_http_request_t *r;
ngx_http_upstream_t *u;
r = ctx->request;
u = r->upstream;
u->length = 1;
if (ctx->end_stream) {
u->length = 0;
}
return NGX_OK;
}
static ngx_int_t
ngx_http_grpc_filter(void *data, ssize_t bytes)
{
ngx_http_grpc_ctx_t *ctx = data;
ngx_int_t rc;
ngx_buf_t *b, *buf;
ngx_chain_t *cl, **ll;
ngx_table_elt_t *h;
ngx_http_request_t *r;
ngx_http_upstream_t *u;
r = ctx->request;
u = r->upstream;
b = &u->buffer;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"grpc filter bytes:%z", bytes);
b->pos = b->last;
b->last += bytes;
for (cl = u->out_bufs, ll = &u->out_bufs; cl; cl = cl->next) {
ll = &cl->next;
}
for ( ;; ) {
if (ctx->state < ngx_http_grpc_st_payload) {
rc = ngx_http_grpc_parse_frame(r, ctx, b);
if (rc == NGX_AGAIN) {
return NGX_AGAIN;
}
if (rc == NGX_ERROR) {
return NGX_ERROR;
}
if ((ctx->type == NGX_HTTP_V2_CONTINUATION_FRAME
&& !ctx->parsing_headers)
|| (ctx->type != NGX_HTTP_V2_CONTINUATION_FRAME
&& ctx->parsing_headers))
{
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent unexpected http2 frame: %d",
ctx->type);
return NGX_ERROR;
}
if (ctx->type == NGX_HTTP_V2_DATA_FRAME) {
if (ctx->stream_id != ctx->id) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent data frame "
"for unknown stream %ui",
ctx->stream_id);
return NGX_ERROR;
}
if (ctx->rest > ctx->recv_window) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream violated stream flow control, "
"received %uz data frame with window %uz",
ctx->rest, ctx->recv_window);
return NGX_ERROR;
}
if (ctx->rest > ctx->connection->recv_window) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream violated connection flow control, "
"received %uz data frame with window %uz",
ctx->rest, ctx->connection->recv_window);
return NGX_ERROR;
}
ctx->recv_window -= ctx->rest;
ctx->connection->recv_window -= ctx->rest;
if (ctx->connection->recv_window < NGX_HTTP_V2_MAX_WINDOW / 4
|| ctx->recv_window < NGX_HTTP_V2_MAX_WINDOW / 4)
{
if (ngx_http_grpc_send_window_update(r, ctx) != NGX_OK) {
return NGX_ERROR;
}
ngx_post_event(u->peer.connection->write,
&ngx_posted_events);
}
}
if (ctx->stream_id && ctx->stream_id != ctx->id) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent frame for unknown stream %ui",
ctx->stream_id);
return NGX_ERROR;
}
ctx->padding = 0;
}
if (ctx->state == ngx_http_grpc_st_padding) {
if (b->last - b->pos < (ssize_t) ctx->rest) {
ctx->rest -= b->last - b->pos;
b->pos = b->last;
return NGX_AGAIN;
}
b->pos += ctx->rest;
ctx->rest = 0;
ctx->state = ngx_http_grpc_st_start;
if (ctx->flags & NGX_HTTP_V2_END_STREAM_FLAG) {
u->length = 0;
if (ctx->in == NULL
&& ctx->out == NULL
&& ctx->output_closed
&& b->last == b->pos)
{
u->keepalive = 1;
}
break;
}
continue;
}
/* frame payload */
if (ctx->type == NGX_HTTP_V2_RST_STREAM_FRAME) {
rc = ngx_http_grpc_parse_rst_stream(r, ctx, b);
if (rc == NGX_AGAIN) {
return NGX_AGAIN;
}
if (rc == NGX_ERROR) {
return NGX_ERROR;
}
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream rejected request with error %ui",
ctx->error);
return NGX_ERROR;
}
if (ctx->type == NGX_HTTP_V2_GOAWAY_FRAME) {
rc = ngx_http_grpc_parse_goaway(r, ctx, b);
if (rc == NGX_AGAIN) {
return NGX_AGAIN;
}
if (rc == NGX_ERROR) {
return NGX_ERROR;
}
/*
* If stream_id is lower than one we use, our
* request won't be processed and needs to be retried.
* If stream_id is greater or equal to the one we use,
* we can continue normally (except we can't use this
* connection for additional requests). If there is
* a real error, the connection will be closed.
*/
if (ctx->stream_id < ctx->id) {
/* TODO: we can retry non-idempotent requests */
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent goaway with error %ui",
ctx->error);
return NGX_ERROR;
}
continue;
}
if (ctx->type == NGX_HTTP_V2_WINDOW_UPDATE_FRAME) {
rc = ngx_http_grpc_parse_window_update(r, ctx, b);
if (rc == NGX_AGAIN) {
return NGX_AGAIN;
}
if (rc == NGX_ERROR) {
return NGX_ERROR;
}
if (ctx->in) {
ngx_post_event(u->peer.connection->write, &ngx_posted_events);
}
continue;
}
if (ctx->type == NGX_HTTP_V2_SETTINGS_FRAME) {
rc = ngx_http_grpc_parse_settings(r, ctx, b);
if (rc == NGX_AGAIN) {
return NGX_AGAIN;
}
if (rc == NGX_ERROR) {
return NGX_ERROR;
}
if (ctx->in) {
ngx_post_event(u->peer.connection->write, &ngx_posted_events);
}
continue;
}
if (ctx->type == NGX_HTTP_V2_PING_FRAME) {
rc = ngx_http_grpc_parse_ping(r, ctx, b);
if (rc == NGX_AGAIN) {
return NGX_AGAIN;
}
if (rc == NGX_ERROR) {
return NGX_ERROR;
}
ngx_post_event(u->peer.connection->write, &ngx_posted_events);
continue;
}
if (ctx->type == NGX_HTTP_V2_PUSH_PROMISE_FRAME) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent unexpected push promise frame");
return NGX_ERROR;
}
if (ctx->type == NGX_HTTP_V2_HEADERS_FRAME
|| ctx->type == NGX_HTTP_V2_CONTINUATION_FRAME)
{
for ( ;; ) {
rc = ngx_http_grpc_parse_header(r, ctx, b);
if (rc == NGX_AGAIN) {
break;
}
if (rc == NGX_OK) {
/* a header line has been parsed successfully */
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"grpc trailer: \"%V: %V\"",
&ctx->name, &ctx->value);
if (ctx->name.len && ctx->name.data[0] == ':') {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent invalid "
"trailer \"%V: %V\"",
&ctx->name, &ctx->value);
return NGX_ERROR;
}
h = ngx_list_push(&u->headers_in.trailers);
if (h == NULL) {
return NGX_ERROR;
}
h->key = ctx->name;
h->value = ctx->value;
h->lowcase_key = h->key.data;
h->hash = ngx_hash_key(h->key.data, h->key.len);
continue;
}
if (rc == NGX_HTTP_PARSE_HEADER_DONE) {
/* a whole header has been parsed successfully */
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"grpc trailer done");
if (ctx->end_stream) {
u->length = 0;
if (ctx->in == NULL
&& ctx->out == NULL
&& ctx->output_closed
&& b->last == b->pos)
{
u->keepalive = 1;
}
return NGX_OK;
}
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent trailer without "
"end stream flag");
return NGX_ERROR;
}
/* there was error while a header line parsing */
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent invalid trailer");
return NGX_ERROR;
}
/* rc == NGX_AGAIN */
if (ctx->rest == 0) {
ctx->state = ngx_http_grpc_st_start;
continue;
}
return NGX_AGAIN;
}
if (ctx->type != NGX_HTTP_V2_DATA_FRAME) {
/* priority, unknown frames */
if (b->last - b->pos < (ssize_t) ctx->rest) {
ctx->rest -= b->last - b->pos;
b->pos = b->last;
return NGX_AGAIN;
}
b->pos += ctx->rest;
ctx->rest = 0;
ctx->state = ngx_http_grpc_st_start;
continue;
}
/*
* data frame:
*
* +---------------+
* |Pad Length? (8)|
* +---------------+-----------------------------------------------+
* | Data (*) ...
* +---------------------------------------------------------------+
* | Padding (*) ...
* +---------------------------------------------------------------+
*/
if (ctx->flags & NGX_HTTP_V2_PADDED_FLAG) {
if (ctx->rest == 0) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent too short http2 frame");
return NGX_ERROR;
}
if (b->pos == b->last) {
return NGX_AGAIN;
}
ctx->flags &= ~NGX_HTTP_V2_PADDED_FLAG;
ctx->padding = *b->pos++;
ctx->rest -= 1;
if (ctx->padding > ctx->rest) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent http2 frame with too long "
"padding: %d in frame %uz",
ctx->padding, ctx->rest);
return NGX_ERROR;
}
continue;
}
if (ctx->rest == ctx->padding) {
goto done;
}
if (b->pos == b->last) {
return NGX_AGAIN;
}
cl = ngx_chain_get_free_buf(r->pool, &u->free_bufs);
if (cl == NULL) {
return NGX_ERROR;
}
*ll = cl;
ll = &cl->next;
buf = cl->buf;
buf->flush = 1;
buf->memory = 1;
buf->pos = b->pos;
buf->tag = u->output.tag;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"grpc output buf %p", buf->pos);
if (b->last - b->pos < (ssize_t) ctx->rest - ctx->padding) {
ctx->rest -= b->last - b->pos;
b->pos = b->last;
buf->last = b->pos;
return NGX_AGAIN;
}
b->pos += ctx->rest - ctx->padding;
buf->last = b->pos;
ctx->rest = ctx->padding;
done:
if (ctx->padding) {
ctx->state = ngx_http_grpc_st_padding;
continue;
}
ctx->state = ngx_http_grpc_st_start;
if (ctx->flags & NGX_HTTP_V2_END_STREAM_FLAG) {
u->length = 0;
if (ctx->in == NULL
&& ctx->out == NULL
&& ctx->output_closed
&& b->last == b->pos)
{
u->keepalive = 1;
}
break;
}
}
return NGX_OK;
}
static ngx_int_t
ngx_http_grpc_parse_frame(ngx_http_request_t *r, ngx_http_grpc_ctx_t *ctx,
ngx_buf_t *b)
{
u_char ch, *p;
ngx_http_grpc_state_e state;
state = ctx->state;
for (p = b->pos; p < b->last; p++) {
ch = *p;
#if 0
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"grpc frame byte: %02Xd, s:%d", ch, state);
#endif
switch (state) {
case ngx_http_grpc_st_start:
ctx->rest = ch << 16;
state = ngx_http_grpc_st_length_2;
break;
case ngx_http_grpc_st_length_2:
ctx->rest |= ch << 8;
state = ngx_http_grpc_st_length_3;
break;
case ngx_http_grpc_st_length_3:
ctx->rest |= ch;
if (ctx->rest > NGX_HTTP_V2_DEFAULT_FRAME_SIZE) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent too large http2 frame: %uz",
ctx->rest);
return NGX_ERROR;
}
state = ngx_http_grpc_st_type;
break;
case ngx_http_grpc_st_type:
ctx->type = ch;
state = ngx_http_grpc_st_flags;
break;
case ngx_http_grpc_st_flags:
ctx->flags = ch;
state = ngx_http_grpc_st_stream_id;
break;
case ngx_http_grpc_st_stream_id:
ctx->stream_id = (ch & 0x7f) << 24;
state = ngx_http_grpc_st_stream_id_2;
break;
case ngx_http_grpc_st_stream_id_2:
ctx->stream_id |= ch << 16;
state = ngx_http_grpc_st_stream_id_3;
break;
case ngx_http_grpc_st_stream_id_3:
ctx->stream_id |= ch << 8;
state = ngx_http_grpc_st_stream_id_4;
break;
case ngx_http_grpc_st_stream_id_4:
ctx->stream_id |= ch;
ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"grpc frame: %d, len: %uz, f:%d, i:%ui",
ctx->type, ctx->rest, ctx->flags, ctx->stream_id);
b->pos = p + 1;
ctx->state = ngx_http_grpc_st_payload;
ctx->frame_state = 0;
return NGX_OK;
/* suppress warning */
case ngx_http_grpc_st_payload:
case ngx_http_grpc_st_padding:
break;
}
}
b->pos = p;
ctx->state = state;
return NGX_AGAIN;
}
static ngx_int_t
ngx_http_grpc_parse_header(ngx_http_request_t *r, ngx_http_grpc_ctx_t *ctx,
ngx_buf_t *b)
{
u_char ch, *p, *last;
size_t min;
ngx_int_t rc;
enum {
sw_start = 0,
sw_padding_length,
sw_dependency,
sw_dependency_2,
sw_dependency_3,
sw_dependency_4,
sw_weight,
sw_fragment,
sw_padding
} state;
state = ctx->frame_state;
if (state == sw_start) {
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"grpc parse header: start");
if (ctx->type == NGX_HTTP_V2_HEADERS_FRAME) {
ctx->parsing_headers = 1;
ctx->fragment_state = 0;
min = (ctx->flags & NGX_HTTP_V2_PADDED_FLAG ? 1 : 0)
+ (ctx->flags & NGX_HTTP_V2_PRIORITY_FLAG ? 5 : 0);
if (ctx->rest < min) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent headers frame "
"with invalid length: %uz",
ctx->rest);
return NGX_ERROR;
}
if (ctx->flags & NGX_HTTP_V2_END_STREAM_FLAG) {
ctx->end_stream = 1;
}
if (ctx->flags & NGX_HTTP_V2_PADDED_FLAG) {
state = sw_padding_length;
} else if (ctx->flags & NGX_HTTP_V2_PRIORITY_FLAG) {
state = sw_dependency;
} else {
state = sw_fragment;
}
} else if (ctx->type == NGX_HTTP_V2_CONTINUATION_FRAME) {
state = sw_fragment;
}
ctx->padding = 0;
ctx->frame_state = state;
}
if (state < sw_fragment) {
if (b->last - b->pos < (ssize_t) ctx->rest) {
last = b->last;
} else {
last = b->pos + ctx->rest;
}
for (p = b->pos; p < last; p++) {
ch = *p;
#if 0
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"grpc header byte: %02Xd s:%d", ch, state);
#endif
/*
* headers frame:
*
* +---------------+
* |Pad Length? (8)|
* +-+-------------+----------------------------------------------+
* |E| Stream Dependency? (31) |
* +-+-------------+----------------------------------------------+
* | Weight? (8) |
* +-+-------------+----------------------------------------------+
* | Header Block Fragment (*) ...
* +--------------------------------------------------------------+
* | Padding (*) ...
* +--------------------------------------------------------------+
*/
switch (state) {
case sw_padding_length:
ctx->padding = ch;
if (ctx->flags & NGX_HTTP_V2_PRIORITY_FLAG) {
state = sw_dependency;
break;
}
goto fragment;
case sw_dependency:
state = sw_dependency_2;
break;
case sw_dependency_2:
state = sw_dependency_3;
break;
case sw_dependency_3:
state = sw_dependency_4;
break;
case sw_dependency_4:
state = sw_weight;
break;
case sw_weight:
goto fragment;
/* suppress warning */
case sw_start:
case sw_fragment:
case sw_padding:
break;
}
}
ctx->rest -= p - b->pos;
b->pos = p;
ctx->frame_state = state;
return NGX_AGAIN;
fragment:
p++;
ctx->rest -= p - b->pos;
b->pos = p;
if (ctx->padding > ctx->rest) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent http2 frame with too long "
"padding: %d in frame %uz",
ctx->padding, ctx->rest);
return NGX_ERROR;
}
state = sw_fragment;
ctx->frame_state = state;
}
if (state == sw_fragment) {
rc = ngx_http_grpc_parse_fragment(r, ctx, b);
if (rc == NGX_AGAIN) {
return NGX_AGAIN;
}
if (rc == NGX_ERROR) {
return NGX_ERROR;
}
if (rc == NGX_OK) {
return NGX_OK;
}
/* rc == NGX_DONE */
state = sw_padding;
ctx->frame_state = state;
}
if (state == sw_padding) {
if (b->last - b->pos < (ssize_t) ctx->rest) {
ctx->rest -= b->last - b->pos;
b->pos = b->last;
return NGX_AGAIN;
}
b->pos += ctx->rest;
ctx->rest = 0;
ctx->state = ngx_http_grpc_st_start;
if (ctx->flags & NGX_HTTP_V2_END_HEADERS_FLAG) {
if (ctx->fragment_state) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent truncated http2 header");
return NGX_ERROR;
}
ctx->parsing_headers = 0;
return NGX_HTTP_PARSE_HEADER_DONE;
}
return NGX_AGAIN;
}
/* unreachable */
return NGX_ERROR;
}
static ngx_int_t
ngx_http_grpc_parse_fragment(ngx_http_request_t *r, ngx_http_grpc_ctx_t *ctx,
ngx_buf_t *b)
{
u_char ch, *p, *last;
size_t size;
ngx_uint_t index, size_update;
enum {
sw_start = 0,
sw_index,
sw_name_length,
sw_name_length_2,
sw_name_length_3,
sw_name_length_4,
sw_name,
sw_name_bytes,
sw_value_length,
sw_value_length_2,
sw_value_length_3,
sw_value_length_4,
sw_value,
sw_value_bytes
} state;
/* header block fragment */
#if 0
ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"grpc header fragment %p:%p rest:%uz",
b->pos, b->last, ctx->rest);
#endif
if (b->last - b->pos < (ssize_t) ctx->rest - ctx->padding) {
last = b->last;
} else {
last = b->pos + ctx->rest - ctx->padding;
}
state = ctx->fragment_state;
for (p = b->pos; p < last; p++) {
ch = *p;
#if 0
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"grpc header byte: %02Xd s:%d", ch, state);
#endif
switch (state) {
case sw_start:
ctx->index = 0;
if ((ch & 0x80) == 0x80) {
/*
* indexed header:
*
* 0 1 2 3 4 5 6 7
* +---+---+---+---+---+---+---+---+
* | 1 | Index (7+) |
* +---+---------------------------+
*/
index = ch & ~0x80;
if (index == 0 || index > 61) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent invalid http2 "
"table index: %ui", index);
return NGX_ERROR;
}
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"grpc indexed header: %ui", index);
ctx->index = index;
ctx->literal = 0;
goto done;
} else if ((ch & 0xc0) == 0x40) {
/*
* literal header with incremental indexing:
*
* 0 1 2 3 4 5 6 7
* +---+---+---+---+---+---+---+---+
* | 0 | 1 | Index (6+) |
* +---+---+-----------------------+
* | H | Value Length (7+) |
* +---+---------------------------+
* | Value String (Length octets) |
* +-------------------------------+
*
* 0 1 2 3 4 5 6 7
* +---+---+---+---+---+---+---+---+
* | 0 | 1 | 0 |
* +---+---+-----------------------+
* | H | Name Length (7+) |
* +---+---------------------------+
* | Name String (Length octets) |
* +---+---------------------------+
* | H | Value Length (7+) |
* +---+---------------------------+
* | Value String (Length octets) |
* +-------------------------------+
*/
index = ch & ~0xc0;
if (index > 61) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent invalid http2 "
"table index: %ui", index);
return NGX_ERROR;
}
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"grpc literal header: %ui", index);
if (index == 0) {
state = sw_name_length;
break;
}
ctx->index = index;
ctx->literal = 1;
state = sw_value_length;
break;
} else if ((ch & 0xe0) == 0x20) {
/*
* dynamic table size update:
*
* 0 1 2 3 4 5 6 7
* +---+---+---+---+---+---+---+---+
* | 0 | 0 | 1 | Max size (5+) |
* +---+---------------------------+
*/
size_update = ch & ~0xe0;
if (size_update > 0) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent invalid http2 "
"dynamic table size update: %ui",
size_update);
return NGX_ERROR;
}
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"grpc table size update: %ui", size_update);
break;
} else if ((ch & 0xf0) == 0x10) {
/*
* literal header field never indexed:
*
* 0 1 2 3 4 5 6 7
* +---+---+---+---+---+---+---+---+
* | 0 | 0 | 0 | 1 | Index (4+) |
* +---+---+-----------------------+
* | H | Value Length (7+) |
* +---+---------------------------+
* | Value String (Length octets) |
* +-------------------------------+
*
* 0 1 2 3 4 5 6 7
* +---+---+---+---+---+---+---+---+
* | 0 | 0 | 0 | 1 | 0 |
* +---+---+-----------------------+
* | H | Name Length (7+) |
* +---+---------------------------+
* | Name String (Length octets) |
* +---+---------------------------+
* | H | Value Length (7+) |
* +---+---------------------------+
* | Value String (Length octets) |
* +-------------------------------+
*/
index = ch & ~0xf0;
if (index == 0x0f) {
ctx->index = index;
ctx->literal = 1;
state = sw_index;
break;
}
if (index == 0) {
state = sw_name_length;
break;
}
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"grpc literal header never indexed: %ui",
index);
ctx->index = index;
ctx->literal = 1;
state = sw_value_length;
break;
} else if ((ch & 0xf0) == 0x00) {
/*
* literal header field without indexing:
*
* 0 1 2 3 4 5 6 7
* +---+---+---+---+---+---+---+---+
* | 0 | 0 | 0 | 0 | Index (4+) |
* +---+---+-----------------------+
* | H | Value Length (7+) |
* +---+---------------------------+
* | Value String (Length octets) |
* +-------------------------------+
*
* 0 1 2 3 4 5 6 7
* +---+---+---+---+---+---+---+---+
* | 0 | 0 | 0 | 0 | 0 |
* +---+---+-----------------------+
* | H | Name Length (7+) |
* +---+---------------------------+
* | Name String (Length octets) |
* +---+---------------------------+
* | H | Value Length (7+) |
* +---+---------------------------+
* | Value String (Length octets) |
* +-------------------------------+
*/
index = ch & ~0xf0;
if (index == 0x0f) {
ctx->index = index;
ctx->literal = 1;
state = sw_index;
break;
}
if (index == 0) {
state = sw_name_length;
break;
}
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"grpc literal header without indexing: %ui",
index);
ctx->index = index;
ctx->literal = 1;
state = sw_value_length;
break;
}
/* not reached */
return NGX_ERROR;
case sw_index:
ctx->index = ctx->index + (ch & ~0x80);
if (ch & 0x80) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent http2 table index "
"with continuation flag");
return NGX_ERROR;
}
if (ctx->index > 61) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent invalid http2 "
"table index: %ui", ctx->index);
return NGX_ERROR;
}
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"grpc header index: %ui", ctx->index);
state = sw_value_length;
break;
case sw_name_length:
ctx->field_huffman = ch & 0x80 ? 1 : 0;
ctx->field_length = ch & ~0x80;
if (ctx->field_length == 0x7f) {
state = sw_name_length_2;
break;
}
if (ctx->field_length == 0) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent zero http2 "
"header name length");
return NGX_ERROR;
}
state = sw_name;
break;
case sw_name_length_2:
ctx->field_length += ch & ~0x80;
if (ch & 0x80) {
state = sw_name_length_3;
break;
}
state = sw_name;
break;
case sw_name_length_3:
ctx->field_length += (ch & ~0x80) << 7;
if (ch & 0x80) {
state = sw_name_length_4;
break;
}
state = sw_name;
break;
case sw_name_length_4:
ctx->field_length += (ch & ~0x80) << 14;
if (ch & 0x80) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent too large http2 "
"header name length");
return NGX_ERROR;
}
state = sw_name;
break;
case sw_name:
ctx->name.len = ctx->field_huffman ?
ctx->field_length * 8 / 5 : ctx->field_length;
ctx->name.data = ngx_pnalloc(r->pool, ctx->name.len + 1);
if (ctx->name.data == NULL) {
return NGX_ERROR;
}
ctx->field_end = ctx->name.data;
ctx->field_rest = ctx->field_length;
ctx->field_state = 0;
state = sw_name_bytes;
/* fall through */
case sw_name_bytes:
ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"grpc name: len:%uz h:%d last:%uz, rest:%uz",
ctx->field_length,
ctx->field_huffman,
last - p,
ctx->rest - (p - b->pos));
size = ngx_min(last - p, (ssize_t) ctx->field_rest);
ctx->field_rest -= size;
if (ctx->field_huffman) {
if (ngx_http_v2_huff_decode(&ctx->field_state, p, size,
&ctx->field_end,
ctx->field_rest == 0,
r->connection->log)
!= NGX_OK)
{
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent invalid encoded header");
return NGX_ERROR;
}
ctx->name.len = ctx->field_end - ctx->name.data;
ctx->name.data[ctx->name.len] = '\0';
} else {
ctx->field_end = ngx_cpymem(ctx->field_end, p, size);
ctx->name.data[ctx->name.len] = '\0';
}
p += size - 1;
if (ctx->field_rest == 0) {
state = sw_value_length;
}
break;
case sw_value_length:
ctx->field_huffman = ch & 0x80 ? 1 : 0;
ctx->field_length = ch & ~0x80;
if (ctx->field_length == 0x7f) {
state = sw_value_length_2;
break;
}
if (ctx->field_length == 0) {
ngx_str_set(&ctx->value, "");
goto done;
}
state = sw_value;
break;
case sw_value_length_2:
ctx->field_length += ch & ~0x80;
if (ch & 0x80) {
state = sw_value_length_3;
break;
}
state = sw_value;
break;
case sw_value_length_3:
ctx->field_length += (ch & ~0x80) << 7;
if (ch & 0x80) {
state = sw_value_length_4;
break;
}
state = sw_value;
break;
case sw_value_length_4:
ctx->field_length += (ch & ~0x80) << 14;
if (ch & 0x80) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent too large http2 "
"header value length");
return NGX_ERROR;
}
state = sw_value;
break;
case sw_value:
ctx->value.len = ctx->field_huffman ?
ctx->field_length * 8 / 5 : ctx->field_length;
ctx->value.data = ngx_pnalloc(r->pool, ctx->value.len + 1);
if (ctx->value.data == NULL) {
return NGX_ERROR;
}
ctx->field_end = ctx->value.data;
ctx->field_rest = ctx->field_length;
ctx->field_state = 0;
state = sw_value_bytes;
/* fall through */
case sw_value_bytes:
ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"grpc value: len:%uz h:%d last:%uz, rest:%uz",
ctx->field_length,
ctx->field_huffman,
last - p,
ctx->rest - (p - b->pos));
size = ngx_min(last - p, (ssize_t) ctx->field_rest);
ctx->field_rest -= size;
if (ctx->field_huffman) {
if (ngx_http_v2_huff_decode(&ctx->field_state, p, size,
&ctx->field_end,
ctx->field_rest == 0,
r->connection->log)
!= NGX_OK)
{
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent invalid encoded header");
return NGX_ERROR;
}
ctx->value.len = ctx->field_end - ctx->value.data;
ctx->value.data[ctx->value.len] = '\0';
} else {
ctx->field_end = ngx_cpymem(ctx->field_end, p, size);
ctx->value.data[ctx->value.len] = '\0';
}
p += size - 1;
if (ctx->field_rest == 0) {
goto done;
}
break;
}
continue;
done:
p++;
ctx->rest -= p - b->pos;
ctx->fragment_state = sw_start;
b->pos = p;
if (ctx->index) {
ctx->name = *ngx_http_v2_get_static_name(ctx->index);
}
if (ctx->index && !ctx->literal) {
ctx->value = *ngx_http_v2_get_static_value(ctx->index);
}
if (!ctx->index) {
if (ngx_http_grpc_validate_header_name(r, &ctx->name) != NGX_OK) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent invalid header: \"%V: %V\"",
&ctx->name, &ctx->value);
return NGX_ERROR;
}
}
if (!ctx->index || ctx->literal) {
if (ngx_http_grpc_validate_header_value(r, &ctx->value) != NGX_OK) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent invalid header: \"%V: %V\"",
&ctx->name, &ctx->value);
return NGX_ERROR;
}
}
return NGX_OK;
}
ctx->rest -= p - b->pos;
ctx->fragment_state = state;
b->pos = p;
if (ctx->rest > ctx->padding) {
return NGX_AGAIN;
}
return NGX_DONE;
}
static ngx_int_t
ngx_http_grpc_validate_header_name(ngx_http_request_t *r, ngx_str_t *s)
{
u_char ch;
ngx_uint_t i;
for (i = 0; i < s->len; i++) {
ch = s->data[i];
if (ch == ':' && i > 0) {
return NGX_ERROR;
}
if (ch >= 'A' && ch <= 'Z') {
return NGX_ERROR;
}
if (ch == '\0' || ch == CR || ch == LF) {
return NGX_ERROR;
}
}
return NGX_OK;
}
static ngx_int_t
ngx_http_grpc_validate_header_value(ngx_http_request_t *r, ngx_str_t *s)
{
u_char ch;
ngx_uint_t i;
for (i = 0; i < s->len; i++) {
ch = s->data[i];
if (ch == '\0' || ch == CR || ch == LF) {
return NGX_ERROR;
}
}
return NGX_OK;
}
static ngx_int_t
ngx_http_grpc_parse_rst_stream(ngx_http_request_t *r, ngx_http_grpc_ctx_t *ctx,
ngx_buf_t *b)
{
u_char ch, *p, *last;
enum {
sw_start = 0,
sw_error_2,
sw_error_3,
sw_error_4
} state;
if (b->last - b->pos < (ssize_t) ctx->rest) {
last = b->last;
} else {
last = b->pos + ctx->rest;
}
state = ctx->frame_state;
if (state == sw_start) {
if (ctx->rest != 4) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent rst stream frame "
"with invalid length: %uz",
ctx->rest);
return NGX_ERROR;
}
}
for (p = b->pos; p < last; p++) {
ch = *p;
#if 0
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"grpc rst byte: %02Xd s:%d", ch, state);
#endif
switch (state) {
case sw_start:
ctx->error = (ngx_uint_t) ch << 24;
state = sw_error_2;
break;
case sw_error_2:
ctx->error |= ch << 16;
state = sw_error_3;
break;
case sw_error_3:
ctx->error |= ch << 8;
state = sw_error_4;
break;
case sw_error_4:
ctx->error |= ch;
state = sw_start;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"grpc error: %ui", ctx->error);
break;
}
}
ctx->rest -= p - b->pos;
ctx->frame_state = state;
b->pos = p;
if (ctx->rest > 0) {
return NGX_AGAIN;
}
return NGX_OK;
}
static ngx_int_t
ngx_http_grpc_parse_goaway(ngx_http_request_t *r, ngx_http_grpc_ctx_t *ctx,
ngx_buf_t *b)
{
u_char ch, *p, *last;
enum {
sw_start = 0,
sw_last_stream_id_2,
sw_last_stream_id_3,
sw_last_stream_id_4,
sw_error,
sw_error_2,
sw_error_3,
sw_error_4,
sw_debug
} state;
if (b->last - b->pos < (ssize_t) ctx->rest) {
last = b->last;
} else {
last = b->pos + ctx->rest;
}
state = ctx->frame_state;
if (state == sw_start) {
if (ctx->stream_id) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent goaway frame "
"with non-zero stream id: %ui",
ctx->stream_id);
return NGX_ERROR;
}
if (ctx->rest < 8) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent goaway frame "
"with invalid length: %uz",
ctx->rest);
return NGX_ERROR;
}
}
for (p = b->pos; p < last; p++) {
ch = *p;
#if 0
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"grpc goaway byte: %02Xd s:%d", ch, state);
#endif
switch (state) {
case sw_start:
ctx->stream_id = (ch & 0x7f) << 24;
state = sw_last_stream_id_2;
break;
case sw_last_stream_id_2:
ctx->stream_id |= ch << 16;
state = sw_last_stream_id_3;
break;
case sw_last_stream_id_3:
ctx->stream_id |= ch << 8;
state = sw_last_stream_id_4;
break;
case sw_last_stream_id_4:
ctx->stream_id |= ch;
state = sw_error;
break;
case sw_error:
ctx->error = (ngx_uint_t) ch << 24;
state = sw_error_2;
break;
case sw_error_2:
ctx->error |= ch << 16;
state = sw_error_3;
break;
case sw_error_3:
ctx->error |= ch << 8;
state = sw_error_4;
break;
case sw_error_4:
ctx->error |= ch;
state = sw_debug;
break;
case sw_debug:
break;
}
}
ctx->rest -= p - b->pos;
ctx->frame_state = state;
b->pos = p;
if (ctx->rest > 0) {
return NGX_AGAIN;
}
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"grpc goaway: %ui, stream %ui",
ctx->error, ctx->stream_id);
ctx->state = ngx_http_grpc_st_start;
return NGX_OK;
}
static ngx_int_t
ngx_http_grpc_parse_window_update(ngx_http_request_t *r,
ngx_http_grpc_ctx_t *ctx, ngx_buf_t *b)
{
u_char ch, *p, *last;
enum {
sw_start = 0,
sw_size_2,
sw_size_3,
sw_size_4
} state;
if (b->last - b->pos < (ssize_t) ctx->rest) {
last = b->last;
} else {
last = b->pos + ctx->rest;
}
state = ctx->frame_state;
if (state == sw_start) {
if (ctx->rest != 4) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent window update frame "
"with invalid length: %uz",
ctx->rest);
return NGX_ERROR;
}
}
for (p = b->pos; p < last; p++) {
ch = *p;
#if 0
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"grpc window update byte: %02Xd s:%d", ch, state);
#endif
switch (state) {
case sw_start:
ctx->window_update = (ch & 0x7f) << 24;
state = sw_size_2;
break;
case sw_size_2:
ctx->window_update |= ch << 16;
state = sw_size_3;
break;
case sw_size_3:
ctx->window_update |= ch << 8;
state = sw_size_4;
break;
case sw_size_4:
ctx->window_update |= ch;
state = sw_start;
break;
}
}
ctx->rest -= p - b->pos;
ctx->frame_state = state;
b->pos = p;
if (ctx->rest > 0) {
return NGX_AGAIN;
}
ctx->state = ngx_http_grpc_st_start;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"grpc window update: %ui", ctx->window_update);
if (ctx->stream_id) {
if (ctx->window_update > (size_t) NGX_HTTP_V2_MAX_WINDOW
- ctx->send_window)
{
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent too large window update");
return NGX_ERROR;
}
ctx->send_window += ctx->window_update;
} else {
if (ctx->window_update > NGX_HTTP_V2_MAX_WINDOW
- ctx->connection->send_window)
{
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent too large window update");
return NGX_ERROR;
}
ctx->connection->send_window += ctx->window_update;
}
return NGX_OK;
}
static ngx_int_t
ngx_http_grpc_parse_settings(ngx_http_request_t *r, ngx_http_grpc_ctx_t *ctx,
ngx_buf_t *b)
{
u_char ch, *p, *last;
ssize_t window_update;
enum {
sw_start = 0,
sw_id,
sw_id_2,
sw_value,
sw_value_2,
sw_value_3,
sw_value_4
} state;
if (b->last - b->pos < (ssize_t) ctx->rest) {
last = b->last;
} else {
last = b->pos + ctx->rest;
}
state = ctx->frame_state;
if (state == sw_start) {
if (ctx->stream_id) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent settings frame "
"with non-zero stream id: %ui",
ctx->stream_id);
return NGX_ERROR;
}
if (ctx->flags & NGX_HTTP_V2_ACK_FLAG) {
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"grpc settings ack");
if (ctx->rest != 0) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent settings frame "
"with ack flag and non-zero length: %uz",
ctx->rest);
return NGX_ERROR;
}
ctx->state = ngx_http_grpc_st_start;
return NGX_OK;
}
if (ctx->rest % 6 != 0) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent settings frame "
"with invalid length: %uz",
ctx->rest);
return NGX_ERROR;
}
}
for (p = b->pos; p < last; p++) {
ch = *p;
#if 0
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"grpc settings byte: %02Xd s:%d", ch, state);
#endif
switch (state) {
case sw_start:
case sw_id:
ctx->setting_id = ch << 8;
state = sw_id_2;
break;
case sw_id_2:
ctx->setting_id |= ch;
state = sw_value;
break;
case sw_value:
ctx->setting_value = (ngx_uint_t) ch << 24;
state = sw_value_2;
break;
case sw_value_2:
ctx->setting_value |= ch << 16;
state = sw_value_3;
break;
case sw_value_3:
ctx->setting_value |= ch << 8;
state = sw_value_4;
break;
case sw_value_4:
ctx->setting_value |= ch;
state = sw_id;
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"grpc setting: %ui %ui",
ctx->setting_id, ctx->setting_value);
/*
* The following settings are defined by the protocol:
*
* SETTINGS_HEADER_TABLE_SIZE, SETTINGS_ENABLE_PUSH,
* SETTINGS_MAX_CONCURRENT_STREAMS, SETTINGS_INITIAL_WINDOW_SIZE,
* SETTINGS_MAX_FRAME_SIZE, SETTINGS_MAX_HEADER_LIST_SIZE
*
* Only SETTINGS_INITIAL_WINDOW_SIZE seems to be needed in
* a simple client.
*/
if (ctx->setting_id == 0x04) {
/* SETTINGS_INITIAL_WINDOW_SIZE */
if (ctx->setting_value > NGX_HTTP_V2_MAX_WINDOW) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent settings frame "
"with too large initial window size: %ui",
ctx->setting_value);
return NGX_ERROR;
}
window_update = ctx->setting_value
- ctx->connection->init_window;
ctx->connection->init_window = ctx->setting_value;
if (ctx->send_window > 0
&& window_update > (ssize_t) NGX_HTTP_V2_MAX_WINDOW
- ctx->send_window)
{
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent settings frame "
"with too large initial window size: %ui",
ctx->setting_value);
return NGX_ERROR;
}
ctx->send_window += window_update;
}
break;
}
}
ctx->rest -= p - b->pos;
ctx->frame_state = state;
b->pos = p;
if (ctx->rest > 0) {
return NGX_AGAIN;
}
ctx->state = ngx_http_grpc_st_start;
return ngx_http_grpc_send_settings_ack(r, ctx);
}
static ngx_int_t
ngx_http_grpc_parse_ping(ngx_http_request_t *r,
ngx_http_grpc_ctx_t *ctx, ngx_buf_t *b)
{
u_char ch, *p, *last;
enum {
sw_start = 0,
sw_data_2,
sw_data_3,
sw_data_4,
sw_data_5,
sw_data_6,
sw_data_7,
sw_data_8
} state;
if (b->last - b->pos < (ssize_t) ctx->rest) {
last = b->last;
} else {
last = b->pos + ctx->rest;
}
state = ctx->frame_state;
if (state == sw_start) {
if (ctx->stream_id) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent ping frame "
"with non-zero stream id: %ui",
ctx->stream_id);
return NGX_ERROR;
}
if (ctx->rest != 8) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent ping frame "
"with invalid length: %uz",
ctx->rest);
return NGX_ERROR;
}
if (ctx->flags & NGX_HTTP_V2_ACK_FLAG) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent ping frame with ack flag");
return NGX_ERROR;
}
}
for (p = b->pos; p < last; p++) {
ch = *p;
#if 0
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"grpc ping byte: %02Xd s:%d", ch, state);
#endif
if (state < sw_data_8) {
ctx->ping_data[state] = ch;
state++;
} else {
ctx->ping_data[7] = ch;
state = sw_start;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"grpc ping");
}
}
ctx->rest -= p - b->pos;
ctx->frame_state = state;
b->pos = p;
if (ctx->rest > 0) {
return NGX_AGAIN;
}
ctx->state = ngx_http_grpc_st_start;
return ngx_http_grpc_send_ping_ack(r, ctx);
}
static ngx_int_t
ngx_http_grpc_send_settings_ack(ngx_http_request_t *r, ngx_http_grpc_ctx_t *ctx)
{
ngx_chain_t *cl, **ll;
ngx_http_grpc_frame_t *f;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"grpc send settings ack");
for (cl = ctx->out, ll = &ctx->out; cl; cl = cl->next) {
ll = &cl->next;
}
cl = ngx_http_grpc_get_buf(r, ctx);
if (cl == NULL) {
return NGX_ERROR;
}
f = (ngx_http_grpc_frame_t *) cl->buf->last;
cl->buf->last += sizeof(ngx_http_grpc_frame_t);
f->length_0 = 0;
f->length_1 = 0;
f->length_2 = 0;
f->type = NGX_HTTP_V2_SETTINGS_FRAME;
f->flags = NGX_HTTP_V2_ACK_FLAG;
f->stream_id_0 = 0;
f->stream_id_1 = 0;
f->stream_id_2 = 0;
f->stream_id_3 = 0;
*ll = cl;
return NGX_OK;
}
static ngx_int_t
ngx_http_grpc_send_ping_ack(ngx_http_request_t *r, ngx_http_grpc_ctx_t *ctx)
{
ngx_chain_t *cl, **ll;
ngx_http_grpc_frame_t *f;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"grpc send ping ack");
for (cl = ctx->out, ll = &ctx->out; cl; cl = cl->next) {
ll = &cl->next;
}
cl = ngx_http_grpc_get_buf(r, ctx);
if (cl == NULL) {
return NGX_ERROR;
}
f = (ngx_http_grpc_frame_t *) cl->buf->last;
cl->buf->last += sizeof(ngx_http_grpc_frame_t);
f->length_0 = 0;
f->length_1 = 0;
f->length_2 = 8;
f->type = NGX_HTTP_V2_PING_FRAME;
f->flags = NGX_HTTP_V2_ACK_FLAG;
f->stream_id_0 = 0;
f->stream_id_1 = 0;
f->stream_id_2 = 0;
f->stream_id_3 = 0;
cl->buf->last = ngx_copy(cl->buf->last, ctx->ping_data, 8);
*ll = cl;
return NGX_OK;
}
static ngx_int_t
ngx_http_grpc_send_window_update(ngx_http_request_t *r,
ngx_http_grpc_ctx_t *ctx)
{
size_t n;
ngx_chain_t *cl, **ll;
ngx_http_grpc_frame_t *f;
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"grpc send window update: %uz %uz",
ctx->connection->recv_window, ctx->recv_window);
for (cl = ctx->out, ll = &ctx->out; cl; cl = cl->next) {
ll = &cl->next;
}
cl = ngx_http_grpc_get_buf(r, ctx);
if (cl == NULL) {
return NGX_ERROR;
}
f = (ngx_http_grpc_frame_t *) cl->buf->last;
cl->buf->last += sizeof(ngx_http_grpc_frame_t);
f->length_0 = 0;
f->length_1 = 0;
f->length_2 = 4;
f->type = NGX_HTTP_V2_WINDOW_UPDATE_FRAME;
f->flags = 0;
f->stream_id_0 = 0;
f->stream_id_1 = 0;
f->stream_id_2 = 0;
f->stream_id_3 = 0;
n = NGX_HTTP_V2_MAX_WINDOW - ctx->connection->recv_window;
ctx->connection->recv_window = NGX_HTTP_V2_MAX_WINDOW;
*cl->buf->last++ = (u_char) ((n >> 24) & 0xff);
*cl->buf->last++ = (u_char) ((n >> 16) & 0xff);
*cl->buf->last++ = (u_char) ((n >> 8) & 0xff);
*cl->buf->last++ = (u_char) (n & 0xff);
f = (ngx_http_grpc_frame_t *) cl->buf->last;
cl->buf->last += sizeof(ngx_http_grpc_frame_t);
f->length_0 = 0;
f->length_1 = 0;
f->length_2 = 4;
f->type = NGX_HTTP_V2_WINDOW_UPDATE_FRAME;
f->flags = 0;
f->stream_id_0 = (u_char) ((ctx->id >> 24) & 0xff);
f->stream_id_1 = (u_char) ((ctx->id >> 16) & 0xff);
f->stream_id_2 = (u_char) ((ctx->id >> 8) & 0xff);
f->stream_id_3 = (u_char) (ctx->id & 0xff);
n = NGX_HTTP_V2_MAX_WINDOW - ctx->recv_window;
ctx->recv_window = NGX_HTTP_V2_MAX_WINDOW;
*cl->buf->last++ = (u_char) ((n >> 24) & 0xff);
*cl->buf->last++ = (u_char) ((n >> 16) & 0xff);
*cl->buf->last++ = (u_char) ((n >> 8) & 0xff);
*cl->buf->last++ = (u_char) (n & 0xff);
*ll = cl;
return NGX_OK;
}
static ngx_chain_t *
ngx_http_grpc_get_buf(ngx_http_request_t *r, ngx_http_grpc_ctx_t *ctx)
{
ngx_buf_t *b;
ngx_chain_t *cl;
cl = ngx_chain_get_free_buf(r->pool, &ctx->free);
if (cl == NULL) {
return NULL;
}
b = cl->buf;
b->tag = (ngx_buf_tag_t) &ngx_http_grpc_body_output_filter;
b->temporary = 1;
b->flush = 1;
if (b->start == NULL) {
/*
* each buffer is large enough to hold two window update
* frames in a row
*/
b->start = ngx_palloc(r->pool, 2 * sizeof(ngx_http_grpc_frame_t) + 8);
if (b->start == NULL) {
return NULL;
}
b->pos = b->start;
b->last = b->start;
b->end = b->start + 2 * sizeof(ngx_http_grpc_frame_t) + 8;
}
return cl;
}
static ngx_http_grpc_ctx_t *
ngx_http_grpc_get_ctx(ngx_http_request_t *r)
{
ngx_http_grpc_ctx_t *ctx;
ngx_http_upstream_t *u;
ctx = ngx_http_get_module_ctx(r, ngx_http_grpc_module);
if (ctx->connection == NULL) {
u = r->upstream;
if (ngx_http_grpc_get_connection_data(r, ctx, &u->peer) != NGX_OK) {
return NULL;
}
}
return ctx;
}
static ngx_int_t
ngx_http_grpc_get_connection_data(ngx_http_request_t *r,
ngx_http_grpc_ctx_t *ctx, ngx_peer_connection_t *pc)
{
ngx_connection_t *c;
ngx_pool_cleanup_t *cln;
c = pc->connection;
if (pc->cached) {
/*
* for cached connections, connection data can be found
* in the cleanup handler
*/
for (cln = c->pool->cleanup; cln; cln = cln->next) {
if (cln->handler == ngx_http_grpc_cleanup) {
ctx->connection = cln->data;
break;
}
}
if (ctx->connection == NULL) {
ngx_log_error(NGX_LOG_ERR, c->log, 0,
"no connection data found for "
"keepalive http2 connection");
return NGX_ERROR;
}
ctx->send_window = ctx->connection->init_window;
ctx->recv_window = NGX_HTTP_V2_MAX_WINDOW;
ctx->connection->last_stream_id += 2;
ctx->id = ctx->connection->last_stream_id;
return NGX_OK;
}
cln = ngx_pool_cleanup_add(c->pool, sizeof(ngx_http_grpc_conn_t));
if (cln == NULL) {
return NGX_ERROR;
}
cln->handler = ngx_http_grpc_cleanup;
ctx->connection = cln->data;
ctx->connection->init_window = NGX_HTTP_V2_DEFAULT_WINDOW;
ctx->connection->send_window = NGX_HTTP_V2_DEFAULT_WINDOW;
ctx->connection->recv_window = NGX_HTTP_V2_MAX_WINDOW;
ctx->send_window = NGX_HTTP_V2_DEFAULT_WINDOW;
ctx->recv_window = NGX_HTTP_V2_MAX_WINDOW;
ctx->id = 1;
ctx->connection->last_stream_id = 1;
return NGX_OK;
}
static void
ngx_http_grpc_cleanup(void *data)
{
#if 0
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
"grpc cleanup");
#endif
return;
}
static void
ngx_http_grpc_abort_request(ngx_http_request_t *r)
{
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"abort grpc request");
return;
}
static void
ngx_http_grpc_finalize_request(ngx_http_request_t *r, ngx_int_t rc)
{
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"finalize grpc request");
return;
}
static ngx_int_t
ngx_http_grpc_internal_trailers_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
ngx_table_elt_t *te;
te = r->headers_in.te;
if (te == NULL) {
v->not_found = 1;
return NGX_OK;
}
if (ngx_strlcasestrn(te->value.data, te->value.data + te->value.len,
(u_char *) "trailers", 8 - 1)
== NULL)
{
v->not_found = 1;
return NGX_OK;
}
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = (u_char *) "trailers";
v->len = sizeof("trailers") - 1;
return NGX_OK;
}
static ngx_int_t
ngx_http_grpc_add_variables(ngx_conf_t *cf)
{
ngx_http_variable_t *var, *v;
for (v = ngx_http_grpc_vars; v->name.len; v++) {
var = ngx_http_add_variable(cf, &v->name, v->flags);
if (var == NULL) {
return NGX_ERROR;
}
var->get_handler = v->get_handler;
var->data = v->data;
}
return NGX_OK;
}
static void *
ngx_http_grpc_create_loc_conf(ngx_conf_t *cf)
{
ngx_http_grpc_loc_conf_t *conf;
conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_grpc_loc_conf_t));
if (conf == NULL) {
return NULL;
}
/*
* set by ngx_pcalloc():
*
* conf->upstream.ignore_headers = 0;
* conf->upstream.next_upstream = 0;
* conf->upstream.hide_headers_hash = { NULL, 0 };
* conf->upstream.ssl_name = NULL;
*
* conf->headers_source = NULL;
* conf->headers.lengths = NULL;
* conf->headers.values = NULL;
* conf->headers.hash = { NULL, 0 };
* conf->host = { 0, NULL };
* conf->host_set = 0;
* conf->ssl = 0;
* conf->ssl_protocols = 0;
* conf->ssl_ciphers = { 0, NULL };
* conf->ssl_trusted_certificate = { 0, NULL };
* conf->ssl_crl = { 0, NULL };
* conf->ssl_certificate = { 0, NULL };
* conf->ssl_certificate_key = { 0, NULL };
*/
conf->upstream.local = NGX_CONF_UNSET_PTR;
conf->upstream.next_upstream_tries = NGX_CONF_UNSET_UINT;
conf->upstream.connect_timeout = NGX_CONF_UNSET_MSEC;
conf->upstream.send_timeout = NGX_CONF_UNSET_MSEC;
conf->upstream.read_timeout = NGX_CONF_UNSET_MSEC;
conf->upstream.next_upstream_timeout = NGX_CONF_UNSET_MSEC;
conf->upstream.buffer_size = NGX_CONF_UNSET_SIZE;
conf->upstream.hide_headers = NGX_CONF_UNSET_PTR;
conf->upstream.pass_headers = NGX_CONF_UNSET_PTR;
conf->upstream.intercept_errors = NGX_CONF_UNSET;
#if (NGX_HTTP_SSL)
conf->upstream.ssl_session_reuse = NGX_CONF_UNSET;
conf->upstream.ssl_server_name = NGX_CONF_UNSET;
conf->upstream.ssl_verify = NGX_CONF_UNSET;
conf->ssl_verify_depth = NGX_CONF_UNSET_UINT;
conf->ssl_passwords = NGX_CONF_UNSET_PTR;
#endif
/* the hardcoded values */
conf->upstream.cyclic_temp_file = 0;
conf->upstream.buffering = 0;
conf->upstream.ignore_client_abort = 0;
conf->upstream.send_lowat = 0;
conf->upstream.bufs.num = 0;
conf->upstream.busy_buffers_size = 0;
conf->upstream.max_temp_file_size = 0;
conf->upstream.temp_file_write_size = 0;
conf->upstream.pass_request_headers = 1;
conf->upstream.pass_request_body = 1;
conf->upstream.force_ranges = 0;
conf->upstream.pass_trailers = 1;
conf->upstream.preserve_output = 1;
ngx_str_set(&conf->upstream.module, "grpc");
return conf;
}
static char *
ngx_http_grpc_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
{
ngx_http_grpc_loc_conf_t *prev = parent;
ngx_http_grpc_loc_conf_t *conf = child;
ngx_int_t rc;
ngx_hash_init_t hash;
ngx_http_core_loc_conf_t *clcf;
ngx_conf_merge_ptr_value(conf->upstream.local,
prev->upstream.local, NULL);
ngx_conf_merge_uint_value(conf->upstream.next_upstream_tries,
prev->upstream.next_upstream_tries, 0);
ngx_conf_merge_msec_value(conf->upstream.connect_timeout,
prev->upstream.connect_timeout, 60000);
ngx_conf_merge_msec_value(conf->upstream.send_timeout,
prev->upstream.send_timeout, 60000);
ngx_conf_merge_msec_value(conf->upstream.read_timeout,
prev->upstream.read_timeout, 60000);
ngx_conf_merge_msec_value(conf->upstream.next_upstream_timeout,
prev->upstream.next_upstream_timeout, 0);
ngx_conf_merge_size_value(conf->upstream.buffer_size,
prev->upstream.buffer_size,
(size_t) ngx_pagesize);
ngx_conf_merge_bitmask_value(conf->upstream.ignore_headers,
prev->upstream.ignore_headers,
NGX_CONF_BITMASK_SET);
ngx_conf_merge_bitmask_value(conf->upstream.next_upstream,
prev->upstream.next_upstream,
(NGX_CONF_BITMASK_SET
|NGX_HTTP_UPSTREAM_FT_ERROR
|NGX_HTTP_UPSTREAM_FT_TIMEOUT));
if (conf->upstream.next_upstream & NGX_HTTP_UPSTREAM_FT_OFF) {
conf->upstream.next_upstream = NGX_CONF_BITMASK_SET
|NGX_HTTP_UPSTREAM_FT_OFF;
}
ngx_conf_merge_value(conf->upstream.intercept_errors,
prev->upstream.intercept_errors, 0);
#if (NGX_HTTP_SSL)
ngx_conf_merge_value(conf->upstream.ssl_session_reuse,
prev->upstream.ssl_session_reuse, 1);
ngx_conf_merge_bitmask_value(conf->ssl_protocols, prev->ssl_protocols,
(NGX_CONF_BITMASK_SET|NGX_SSL_TLSv1
|NGX_SSL_TLSv1_1|NGX_SSL_TLSv1_2));
ngx_conf_merge_str_value(conf->ssl_ciphers, prev->ssl_ciphers,
"DEFAULT");
if (conf->upstream.ssl_name == NULL) {
conf->upstream.ssl_name = prev->upstream.ssl_name;
}
ngx_conf_merge_value(conf->upstream.ssl_server_name,
prev->upstream.ssl_server_name, 0);
ngx_conf_merge_value(conf->upstream.ssl_verify,
prev->upstream.ssl_verify, 0);
ngx_conf_merge_uint_value(conf->ssl_verify_depth,
prev->ssl_verify_depth, 1);
ngx_conf_merge_str_value(conf->ssl_trusted_certificate,
prev->ssl_trusted_certificate, "");
ngx_conf_merge_str_value(conf->ssl_crl, prev->ssl_crl, "");
ngx_conf_merge_str_value(conf->ssl_certificate,
prev->ssl_certificate, "");
ngx_conf_merge_str_value(conf->ssl_certificate_key,
prev->ssl_certificate_key, "");
ngx_conf_merge_ptr_value(conf->ssl_passwords, prev->ssl_passwords, NULL);
if (conf->ssl && ngx_http_grpc_set_ssl(cf, conf) != NGX_OK) {
return NGX_CONF_ERROR;
}
#endif
hash.max_size = 512;
hash.bucket_size = ngx_align(64, ngx_cacheline_size);
hash.name = "grpc_headers_hash";
if (ngx_http_upstream_hide_headers_hash(cf, &conf->upstream,
&prev->upstream, ngx_http_grpc_hide_headers, &hash)
!= NGX_OK)
{
return NGX_CONF_ERROR;
}
clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
if (clcf->noname && conf->upstream.upstream == NULL) {
conf->upstream.upstream = prev->upstream.upstream;
conf->host = prev->host;
#if (NGX_HTTP_SSL)
conf->upstream.ssl = prev->upstream.ssl;
#endif
}
if (clcf->lmt_excpt && clcf->handler == NULL && conf->upstream.upstream) {
clcf->handler = ngx_http_grpc_handler;
}
if (conf->headers_source == NULL) {
conf->headers = prev->headers;
conf->headers_source = prev->headers_source;
conf->host_set = prev->host_set;
}
rc = ngx_http_grpc_init_headers(cf, conf, &conf->headers,
ngx_http_grpc_headers);
if (rc != NGX_OK) {
return NGX_CONF_ERROR;
}
/*
* special handling to preserve conf->headers in the "http" section
* to inherit it to all servers
*/
if (prev->headers.hash.buckets == NULL
&& conf->headers_source == prev->headers_source)
{
prev->headers = conf->headers;
prev->host_set = conf->host_set;
}
return NGX_CONF_OK;
}
static ngx_int_t
ngx_http_grpc_init_headers(ngx_conf_t *cf, ngx_http_grpc_loc_conf_t *conf,
ngx_http_grpc_headers_t *headers, ngx_keyval_t *default_headers)
{
u_char *p;
size_t size;
uintptr_t *code;
ngx_uint_t i;
ngx_array_t headers_names, headers_merged;
ngx_keyval_t *src, *s, *h;
ngx_hash_key_t *hk;
ngx_hash_init_t hash;
ngx_http_script_compile_t sc;
ngx_http_script_copy_code_t *copy;
if (headers->hash.buckets) {
return NGX_OK;
}
if (ngx_array_init(&headers_names, cf->temp_pool, 4, sizeof(ngx_hash_key_t))
!= NGX_OK)
{
return NGX_ERROR;
}
if (ngx_array_init(&headers_merged, cf->temp_pool, 4, sizeof(ngx_keyval_t))
!= NGX_OK)
{
return NGX_ERROR;
}
headers->lengths = ngx_array_create(cf->pool, 64, 1);
if (headers->lengths == NULL) {
return NGX_ERROR;
}
headers->values = ngx_array_create(cf->pool, 512, 1);
if (headers->values == NULL) {
return NGX_ERROR;
}
if (conf->headers_source) {
src = conf->headers_source->elts;
for (i = 0; i < conf->headers_source->nelts; i++) {
if (src[i].key.len == 4
&& ngx_strncasecmp(src[i].key.data, (u_char *) "Host", 4) == 0)
{
conf->host_set = 1;
}
s = ngx_array_push(&headers_merged);
if (s == NULL) {
return NGX_ERROR;
}
*s = src[i];
}
}
h = default_headers;
while (h->key.len) {
src = headers_merged.elts;
for (i = 0; i < headers_merged.nelts; i++) {
if (ngx_strcasecmp(h->key.data, src[i].key.data) == 0) {
goto next;
}
}
s = ngx_array_push(&headers_merged);
if (s == NULL) {
return NGX_ERROR;
}
*s = *h;
next:
h++;
}
src = headers_merged.elts;
for (i = 0; i < headers_merged.nelts; i++) {
hk = ngx_array_push(&headers_names);
if (hk == NULL) {
return NGX_ERROR;
}
hk->key = src[i].key;
hk->key_hash = ngx_hash_key_lc(src[i].key.data, src[i].key.len);
hk->value = (void *) 1;
if (src[i].value.len == 0) {
continue;
}
copy = ngx_array_push_n(headers->lengths,
sizeof(ngx_http_script_copy_code_t));
if (copy == NULL) {
return NGX_ERROR;
}
copy->code = (ngx_http_script_code_pt) ngx_http_script_copy_len_code;
copy->len = src[i].key.len;
size = (sizeof(ngx_http_script_copy_code_t)
+ src[i].key.len + sizeof(uintptr_t) - 1)
& ~(sizeof(uintptr_t) - 1);
copy = ngx_array_push_n(headers->values, size);
if (copy == NULL) {
return NGX_ERROR;
}
copy->code = ngx_http_script_copy_code;
copy->len = src[i].key.len;
p = (u_char *) copy + sizeof(ngx_http_script_copy_code_t);
ngx_memcpy(p, src[i].key.data, src[i].key.len);
ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
sc.cf = cf;
sc.source = &src[i].value;
sc.flushes = &headers->flushes;
sc.lengths = &headers->lengths;
sc.values = &headers->values;
if (ngx_http_script_compile(&sc) != NGX_OK) {
return NGX_ERROR;
}
code = ngx_array_push_n(headers->lengths, sizeof(uintptr_t));
if (code == NULL) {
return NGX_ERROR;
}
*code = (uintptr_t) NULL;
code = ngx_array_push_n(headers->values, sizeof(uintptr_t));
if (code == NULL) {
return NGX_ERROR;
}
*code = (uintptr_t) NULL;
}
code = ngx_array_push_n(headers->lengths, sizeof(uintptr_t));
if (code == NULL) {
return NGX_ERROR;
}
*code = (uintptr_t) NULL;
hash.hash = &headers->hash;
hash.key = ngx_hash_key_lc;
hash.max_size = 512;
hash.bucket_size = 64;
hash.name = "grpc_headers_hash";
hash.pool = cf->pool;
hash.temp_pool = NULL;
return ngx_hash_init(&hash, headers_names.elts, headers_names.nelts);
}
static char *
ngx_http_grpc_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_grpc_loc_conf_t *glcf = conf;
size_t add;
ngx_str_t *value, *url;
ngx_url_t u;
ngx_http_core_loc_conf_t *clcf;
if (glcf->upstream.upstream) {
return "is duplicate";
}
value = cf->args->elts;
url = &value[1];
if (ngx_strncasecmp(url->data, (u_char *) "grpc://", 7) == 0) {
add = 7;
} else if (ngx_strncasecmp(url->data, (u_char *) "grpcs://", 8) == 0) {
#if (NGX_HTTP_SSL)
glcf->ssl = 1;
add = 8;
#else
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"grpcs protocol requires SSL support");
return NGX_CONF_ERROR;
#endif
} else {
add = 0;
}
ngx_memzero(&u, sizeof(ngx_url_t));
u.url.len = url->len - add;
u.url.data = url->data + add;
u.no_resolve = 1;
glcf->upstream.upstream = ngx_http_upstream_add(cf, &u, 0);
if (glcf->upstream.upstream == NULL) {
return NGX_CONF_ERROR;
}
if (u.family != AF_UNIX) {
if (u.no_port) {
glcf->host = u.host;
} else {
glcf->host.len = u.host.len + 1 + u.port_text.len;
glcf->host.data = u.host.data;
}
} else {
ngx_str_set(&glcf->host, "localhost");
}
clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
clcf->handler = ngx_http_grpc_handler;
if (clcf->name.data[clcf->name.len - 1] == '/') {
clcf->auto_redirect = 1;
}
return NGX_CONF_OK;
}
#if (NGX_HTTP_SSL)
static char *
ngx_http_grpc_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_grpc_loc_conf_t *glcf = conf;
ngx_str_t *value;
if (glcf->ssl_passwords != NGX_CONF_UNSET_PTR) {
return "is duplicate";
}
value = cf->args->elts;
glcf->ssl_passwords = ngx_ssl_read_password_file(cf, &value[1]);
if (glcf->ssl_passwords == NULL) {
return NGX_CONF_ERROR;
}
return NGX_CONF_OK;
}
static ngx_int_t
ngx_http_grpc_set_ssl(ngx_conf_t *cf, ngx_http_grpc_loc_conf_t *glcf)
{
ngx_pool_cleanup_t *cln;
glcf->upstream.ssl = ngx_pcalloc(cf->pool, sizeof(ngx_ssl_t));
if (glcf->upstream.ssl == NULL) {
return NGX_ERROR;
}
glcf->upstream.ssl->log = cf->log;
if (ngx_ssl_create(glcf->upstream.ssl, glcf->ssl_protocols, NULL)
!= NGX_OK)
{
return NGX_ERROR;
}
cln = ngx_pool_cleanup_add(cf->pool, 0);
if (cln == NULL) {
return NGX_ERROR;
}
cln->handler = ngx_ssl_cleanup_ctx;
cln->data = glcf->upstream.ssl;
if (glcf->ssl_certificate.len) {
if (glcf->ssl_certificate_key.len == 0) {
ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
"no \"grpc_ssl_certificate_key\" is defined "
"for certificate \"%V\"", &glcf->ssl_certificate);
return NGX_ERROR;
}
if (ngx_ssl_certificate(cf, glcf->upstream.ssl, &glcf->ssl_certificate,
&glcf->ssl_certificate_key, glcf->ssl_passwords)
!= NGX_OK)
{
return NGX_ERROR;
}
}
if (ngx_ssl_ciphers(cf, glcf->upstream.ssl, &glcf->ssl_ciphers, 0)
!= NGX_OK)
{
return NGX_ERROR;
}
if (glcf->upstream.ssl_verify) {
if (glcf->ssl_trusted_certificate.len == 0) {
ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
"no grpc_ssl_trusted_certificate for grpc_ssl_verify");
return NGX_ERROR;
}
if (ngx_ssl_trusted_certificate(cf, glcf->upstream.ssl,
&glcf->ssl_trusted_certificate,
glcf->ssl_verify_depth)
!= NGX_OK)
{
return NGX_ERROR;
}
if (ngx_ssl_crl(cf, glcf->upstream.ssl, &glcf->ssl_crl) != NGX_OK) {
return NGX_ERROR;
}
}
#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
if (SSL_CTX_set_alpn_protos(glcf->upstream.ssl->ctx,
(u_char *) "\x02h2", 3)
!= 0)
{
ngx_ssl_error(NGX_LOG_EMERG, cf->log, 0,
"SSL_CTX_set_alpn_protos() failed");
return NGX_ERROR;
}
#endif
return NGX_OK;
}
#endif
nginx-1.14.0/src/http/modules/ngx_http_gunzip_filter_module.c 000644 001751 001751 00000041216 13265410474 025643 0 ustar 00mdounin mdounin 000000 000000
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Maxim Dounin
* Copyright (C) Nginx, Inc.
*/
#include
#include
#include
#include
typedef struct {
ngx_flag_t enable;
ngx_bufs_t bufs;
} ngx_http_gunzip_conf_t;
typedef struct {
ngx_chain_t *in;
ngx_chain_t *free;
ngx_chain_t *busy;
ngx_chain_t *out;
ngx_chain_t **last_out;
ngx_buf_t *in_buf;
ngx_buf_t *out_buf;
ngx_int_t bufs;
unsigned started:1;
unsigned flush:4;
unsigned redo:1;
unsigned done:1;
unsigned nomem:1;
z_stream zstream;
ngx_http_request_t *request;
} ngx_http_gunzip_ctx_t;
static ngx_int_t ngx_http_gunzip_filter_inflate_start(ngx_http_request_t *r,
ngx_http_gunzip_ctx_t *ctx);
static ngx_int_t ngx_http_gunzip_filter_add_data(ngx_http_request_t *r,
ngx_http_gunzip_ctx_t *ctx);
static ngx_int_t ngx_http_gunzip_filter_get_buf(ngx_http_request_t *r,
ngx_http_gunzip_ctx_t *ctx);
static ngx_int_t ngx_http_gunzip_filter_inflate(ngx_http_request_t *r,
ngx_http_gunzip_ctx_t *ctx);
static ngx_int_t ngx_http_gunzip_filter_inflate_end(ngx_http_request_t *r,
ngx_http_gunzip_ctx_t *ctx);
static void *ngx_http_gunzip_filter_alloc(void *opaque, u_int items,
u_int size);
static void ngx_http_gunzip_filter_free(void *opaque, void *address);
static ngx_int_t ngx_http_gunzip_filter_init(ngx_conf_t *cf);
static void *ngx_http_gunzip_create_conf(ngx_conf_t *cf);
static char *ngx_http_gunzip_merge_conf(ngx_conf_t *cf,
void *parent, void *child);
static ngx_command_t ngx_http_gunzip_filter_commands[] = {
{ ngx_string("gunzip"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_gunzip_conf_t, enable),
NULL },
{ ngx_string("gunzip_buffers"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
ngx_conf_set_bufs_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_gunzip_conf_t, bufs),
NULL },
ngx_null_command
};
static ngx_http_module_t ngx_http_gunzip_filter_module_ctx = {
NULL, /* preconfiguration */
ngx_http_gunzip_filter_init, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
ngx_http_gunzip_create_conf, /* create location configuration */
ngx_http_gunzip_merge_conf /* merge location configuration */
};
ngx_module_t ngx_http_gunzip_filter_module = {
NGX_MODULE_V1,
&ngx_http_gunzip_filter_module_ctx, /* module context */
ngx_http_gunzip_filter_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static ngx_http_output_header_filter_pt ngx_http_next_header_filter;
static ngx_http_output_body_filter_pt ngx_http_next_body_filter;
static ngx_int_t
ngx_http_gunzip_header_filter(ngx_http_request_t *r)
{
ngx_http_gunzip_ctx_t *ctx;
ngx_http_gunzip_conf_t *conf;
conf = ngx_http_get_module_loc_conf(r, ngx_http_gunzip_filter_module);
/* TODO support multiple content-codings */
/* TODO always gunzip - due to configuration or module request */
/* TODO ignore content encoding? */
if (!conf->enable
|| r->headers_out.content_encoding == NULL
|| r->headers_out.content_encoding->value.len != 4
|| ngx_strncasecmp(r->headers_out.content_encoding->value.data,
(u_char *) "gzip", 4) != 0)
{
return ngx_http_next_header_filter(r);
}
r->gzip_vary = 1;
if (!r->gzip_tested) {
if (ngx_http_gzip_ok(r) == NGX_OK) {
return ngx_http_next_header_filter(r);
}
} else if (r->gzip_ok) {
return ngx_http_next_header_filter(r);
}
ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_gunzip_ctx_t));
if (ctx == NULL) {
return NGX_ERROR;
}
ngx_http_set_ctx(r, ctx, ngx_http_gunzip_filter_module);
ctx->request = r;
r->filter_need_in_memory = 1;
r->headers_out.content_encoding->hash = 0;
r->headers_out.content_encoding = NULL;
ngx_http_clear_content_length(r);
ngx_http_clear_accept_ranges(r);
ngx_http_weak_etag(r);
return ngx_http_next_header_filter(r);
}
static ngx_int_t
ngx_http_gunzip_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
int rc;
ngx_uint_t flush;
ngx_chain_t *cl;
ngx_http_gunzip_ctx_t *ctx;
ctx = ngx_http_get_module_ctx(r, ngx_http_gunzip_filter_module);
if (ctx == NULL || ctx->done) {
return ngx_http_next_body_filter(r, in);
}
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http gunzip filter");
if (!ctx->started) {
if (ngx_http_gunzip_filter_inflate_start(r, ctx) != NGX_OK) {
goto failed;
}
}
if (in) {
if (ngx_chain_add_copy(r->pool, &ctx->in, in) != NGX_OK) {
goto failed;
}
}
if (ctx->nomem) {
/* flush busy buffers */
if (ngx_http_next_body_filter(r, NULL) == NGX_ERROR) {
goto failed;
}
cl = NULL;
ngx_chain_update_chains(r->pool, &ctx->free, &ctx->busy, &cl,
(ngx_buf_tag_t) &ngx_http_gunzip_filter_module);
ctx->nomem = 0;
flush = 0;
} else {
flush = ctx->busy ? 1 : 0;
}
for ( ;; ) {
/* cycle while we can write to a client */
for ( ;; ) {
/* cycle while there is data to feed zlib and ... */
rc = ngx_http_gunzip_filter_add_data(r, ctx);
if (rc == NGX_DECLINED) {
break;
}
if (rc == NGX_AGAIN) {
continue;
}
/* ... there are buffers to write zlib output */
rc = ngx_http_gunzip_filter_get_buf(r, ctx);
if (rc == NGX_DECLINED) {
break;
}
if (rc == NGX_ERROR) {
goto failed;
}
rc = ngx_http_gunzip_filter_inflate(r, ctx);
if (rc == NGX_OK) {
break;
}
if (rc == NGX_ERROR) {
goto failed;
}
/* rc == NGX_AGAIN */
}
if (ctx->out == NULL && !flush) {
return ctx->busy ? NGX_AGAIN : NGX_OK;
}
rc = ngx_http_next_body_filter(r, ctx->out);
if (rc == NGX_ERROR) {
goto failed;
}
ngx_chain_update_chains(r->pool, &ctx->free, &ctx->busy, &ctx->out,
(ngx_buf_tag_t) &ngx_http_gunzip_filter_module);
ctx->last_out = &ctx->out;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"gunzip out: %p", ctx->out);
ctx->nomem = 0;
flush = 0;
if (ctx->done) {
return rc;
}
}
/* unreachable */
failed:
ctx->done = 1;
return NGX_ERROR;
}
static ngx_int_t
ngx_http_gunzip_filter_inflate_start(ngx_http_request_t *r,
ngx_http_gunzip_ctx_t *ctx)
{
int rc;
ctx->zstream.next_in = Z_NULL;
ctx->zstream.avail_in = 0;
ctx->zstream.zalloc = ngx_http_gunzip_filter_alloc;
ctx->zstream.zfree = ngx_http_gunzip_filter_free;
ctx->zstream.opaque = ctx;
/* windowBits +16 to decode gzip, zlib 1.2.0.4+ */
rc = inflateInit2(&ctx->zstream, MAX_WBITS + 16);
if (rc != Z_OK) {
ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
"inflateInit2() failed: %d", rc);
return NGX_ERROR;
}
ctx->started = 1;
ctx->last_out = &ctx->out;
ctx->flush = Z_NO_FLUSH;
return NGX_OK;
}
static ngx_int_t
ngx_http_gunzip_filter_add_data(ngx_http_request_t *r,
ngx_http_gunzip_ctx_t *ctx)
{
if (ctx->zstream.avail_in || ctx->flush != Z_NO_FLUSH || ctx->redo) {
return NGX_OK;
}
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"gunzip in: %p", ctx->in);
if (ctx->in == NULL) {
return NGX_DECLINED;
}
ctx->in_buf = ctx->in->buf;
ctx->in = ctx->in->next;
ctx->zstream.next_in = ctx->in_buf->pos;
ctx->zstream.avail_in = ctx->in_buf->last - ctx->in_buf->pos;
ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"gunzip in_buf:%p ni:%p ai:%ud",
ctx->in_buf,
ctx->zstream.next_in, ctx->zstream.avail_in);
if (ctx->in_buf->last_buf || ctx->in_buf->last_in_chain) {
ctx->flush = Z_FINISH;
} else if (ctx->in_buf->flush) {
ctx->flush = Z_SYNC_FLUSH;
} else if (ctx->zstream.avail_in == 0) {
/* ctx->flush == Z_NO_FLUSH */
return NGX_AGAIN;
}
return NGX_OK;
}
static ngx_int_t
ngx_http_gunzip_filter_get_buf(ngx_http_request_t *r,
ngx_http_gunzip_ctx_t *ctx)
{
ngx_http_gunzip_conf_t *conf;
if (ctx->zstream.avail_out) {
return NGX_OK;
}
conf = ngx_http_get_module_loc_conf(r, ngx_http_gunzip_filter_module);
if (ctx->free) {
ctx->out_buf = ctx->free->buf;
ctx->free = ctx->free->next;
ctx->out_buf->flush = 0;
} else if (ctx->bufs < conf->bufs.num) {
ctx->out_buf = ngx_create_temp_buf(r->pool, conf->bufs.size);
if (ctx->out_buf == NULL) {
return NGX_ERROR;
}
ctx->out_buf->tag = (ngx_buf_tag_t) &ngx_http_gunzip_filter_module;
ctx->out_buf->recycled = 1;
ctx->bufs++;
} else {
ctx->nomem = 1;
return NGX_DECLINED;
}
ctx->zstream.next_out = ctx->out_buf->pos;
ctx->zstream.avail_out = conf->bufs.size;
return NGX_OK;
}
static ngx_int_t
ngx_http_gunzip_filter_inflate(ngx_http_request_t *r,
ngx_http_gunzip_ctx_t *ctx)
{
int rc;
ngx_buf_t *b;
ngx_chain_t *cl;
ngx_log_debug6(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"inflate in: ni:%p no:%p ai:%ud ao:%ud fl:%d redo:%d",
ctx->zstream.next_in, ctx->zstream.next_out,
ctx->zstream.avail_in, ctx->zstream.avail_out,
ctx->flush, ctx->redo);
rc = inflate(&ctx->zstream, ctx->flush);
if (rc != Z_OK && rc != Z_STREAM_END && rc != Z_BUF_ERROR) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"inflate() failed: %d, %d", ctx->flush, rc);
return NGX_ERROR;
}
ngx_log_debug5(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"inflate out: ni:%p no:%p ai:%ud ao:%ud rc:%d",
ctx->zstream.next_in, ctx->zstream.next_out,
ctx->zstream.avail_in, ctx->zstream.avail_out,
rc);
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"gunzip in_buf:%p pos:%p",
ctx->in_buf, ctx->in_buf->pos);
if (ctx->zstream.next_in) {
ctx->in_buf->pos = ctx->zstream.next_in;
if (ctx->zstream.avail_in == 0) {
ctx->zstream.next_in = NULL;
}
}
ctx->out_buf->last = ctx->zstream.next_out;
if (ctx->zstream.avail_out == 0) {
/* zlib wants to output some more data */
cl = ngx_alloc_chain_link(r->pool);
if (cl == NULL) {
return NGX_ERROR;
}
cl->buf = ctx->out_buf;
cl->next = NULL;
*ctx->last_out = cl;
ctx->last_out = &cl->next;
ctx->redo = 1;
return NGX_AGAIN;
}
ctx->redo = 0;
if (ctx->flush == Z_SYNC_FLUSH) {
ctx->flush = Z_NO_FLUSH;
cl = ngx_alloc_chain_link(r->pool);
if (cl == NULL) {
return NGX_ERROR;
}
b = ctx->out_buf;
if (ngx_buf_size(b) == 0) {
b = ngx_calloc_buf(ctx->request->pool);
if (b == NULL) {
return NGX_ERROR;
}
} else {
ctx->zstream.avail_out = 0;
}
b->flush = 1;
cl->buf = b;
cl->next = NULL;
*ctx->last_out = cl;
ctx->last_out = &cl->next;
return NGX_OK;
}
if (ctx->flush == Z_FINISH && ctx->zstream.avail_in == 0) {
if (rc != Z_STREAM_END) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"inflate() returned %d on response end", rc);
return NGX_ERROR;
}
if (ngx_http_gunzip_filter_inflate_end(r, ctx) != NGX_OK) {
return NGX_ERROR;
}
return NGX_OK;
}
if (rc == Z_STREAM_END && ctx->zstream.avail_in > 0) {
rc = inflateReset(&ctx->zstream);
if (rc != Z_OK) {
ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
"inflateReset() failed: %d", rc);
return NGX_ERROR;
}
ctx->redo = 1;
return NGX_AGAIN;
}
if (ctx->in == NULL) {
b = ctx->out_buf;
if (ngx_buf_size(b) == 0) {
return NGX_OK;
}
cl = ngx_alloc_chain_link(r->pool);
if (cl == NULL) {
return NGX_ERROR;
}
ctx->zstream.avail_out = 0;
cl->buf = b;
cl->next = NULL;
*ctx->last_out = cl;
ctx->last_out = &cl->next;
return NGX_OK;
}
return NGX_AGAIN;
}
static ngx_int_t
ngx_http_gunzip_filter_inflate_end(ngx_http_request_t *r,
ngx_http_gunzip_ctx_t *ctx)
{
int rc;
ngx_buf_t *b;
ngx_chain_t *cl;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"gunzip inflate end");
rc = inflateEnd(&ctx->zstream);
if (rc != Z_OK) {
ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
"inflateEnd() failed: %d", rc);
return NGX_ERROR;
}
b = ctx->out_buf;
if (ngx_buf_size(b) == 0) {
b = ngx_calloc_buf(ctx->request->pool);
if (b == NULL) {
return NGX_ERROR;
}
}
cl = ngx_alloc_chain_link(r->pool);
if (cl == NULL) {
return NGX_ERROR;
}
cl->buf = b;
cl->next = NULL;
*ctx->last_out = cl;
ctx->last_out = &cl->next;
b->last_buf = (r == r->main) ? 1 : 0;
b->last_in_chain = 1;
b->sync = 1;
ctx->done = 1;
return NGX_OK;
}
static void *
ngx_http_gunzip_filter_alloc(void *opaque, u_int items, u_int size)
{
ngx_http_gunzip_ctx_t *ctx = opaque;
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ctx->request->connection->log, 0,
"gunzip alloc: n:%ud s:%ud",
items, size);
return ngx_palloc(ctx->request->pool, items * size);
}
static void
ngx_http_gunzip_filter_free(void *opaque, void *address)
{
#if 0
ngx_http_gunzip_ctx_t *ctx = opaque;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->request->connection->log, 0,
"gunzip free: %p", address);
#endif
}
static void *
ngx_http_gunzip_create_conf(ngx_conf_t *cf)
{
ngx_http_gunzip_conf_t *conf;
conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_gunzip_conf_t));
if (conf == NULL) {
return NULL;
}
/*
* set by ngx_pcalloc():
*
* conf->bufs.num = 0;
*/
conf->enable = NGX_CONF_UNSET;
return conf;
}
static char *
ngx_http_gunzip_merge_conf(ngx_conf_t *cf, void *parent, void *child)
{
ngx_http_gunzip_conf_t *prev = parent;
ngx_http_gunzip_conf_t *conf = child;
ngx_conf_merge_value(conf->enable, prev->enable, 0);
ngx_conf_merge_bufs_value(conf->bufs, prev->bufs,
(128 * 1024) / ngx_pagesize, ngx_pagesize);
return NGX_CONF_OK;
}
static ngx_int_t
ngx_http_gunzip_filter_init(ngx_conf_t *cf)
{
ngx_http_next_header_filter = ngx_http_top_header_filter;
ngx_http_top_header_filter = ngx_http_gunzip_header_filter;
ngx_http_next_body_filter = ngx_http_top_body_filter;
ngx_http_top_body_filter = ngx_http_gunzip_body_filter;
return NGX_OK;
}
nginx-1.14.0/src/http/modules/ngx_http_gzip_filter_module.c 000644 001751 001751 00000077215 13265410474 025310 0 ustar 00mdounin mdounin 000000 000000
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#include
#include
#include
#include
typedef struct {
ngx_flag_t enable;
ngx_flag_t no_buffer;
ngx_hash_t types;
ngx_bufs_t bufs;
size_t postpone_gzipping;
ngx_int_t level;
size_t wbits;
size_t memlevel;
ssize_t min_length;
ngx_array_t *types_keys;
} ngx_http_gzip_conf_t;
typedef struct {
ngx_chain_t *in;
ngx_chain_t *free;
ngx_chain_t *busy;
ngx_chain_t *out;
ngx_chain_t **last_out;
ngx_chain_t *copied;
ngx_chain_t *copy_buf;
ngx_buf_t *in_buf;
ngx_buf_t *out_buf;
ngx_int_t bufs;
void *preallocated;
char *free_mem;
ngx_uint_t allocated;
int wbits;
int memlevel;
unsigned flush:4;
unsigned redo:1;
unsigned done:1;
unsigned nomem:1;
unsigned gzheader:1;
unsigned buffering:1;
unsigned intel:1;
size_t zin;
size_t zout;
uint32_t crc32;
z_stream zstream;
ngx_http_request_t *request;
} ngx_http_gzip_ctx_t;
#if (NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED)
struct gztrailer {
uint32_t crc32;
uint32_t zlen;
};
#else /* NGX_HAVE_BIG_ENDIAN || !NGX_HAVE_NONALIGNED */
struct gztrailer {
u_char crc32[4];
u_char zlen[4];
};
#endif
static void ngx_http_gzip_filter_memory(ngx_http_request_t *r,
ngx_http_gzip_ctx_t *ctx);
static ngx_int_t ngx_http_gzip_filter_buffer(ngx_http_gzip_ctx_t *ctx,
ngx_chain_t *in);
static ngx_int_t ngx_http_gzip_filter_deflate_start(ngx_http_request_t *r,
ngx_http_gzip_ctx_t *ctx);
static ngx_int_t ngx_http_gzip_filter_gzheader(ngx_http_request_t *r,
ngx_http_gzip_ctx_t *ctx);
static ngx_int_t ngx_http_gzip_filter_add_data(ngx_http_request_t *r,
ngx_http_gzip_ctx_t *ctx);
static ngx_int_t ngx_http_gzip_filter_get_buf(ngx_http_request_t *r,
ngx_http_gzip_ctx_t *ctx);
static ngx_int_t ngx_http_gzip_filter_deflate(ngx_http_request_t *r,
ngx_http_gzip_ctx_t *ctx);
static ngx_int_t ngx_http_gzip_filter_deflate_end(ngx_http_request_t *r,
ngx_http_gzip_ctx_t *ctx);
static void *ngx_http_gzip_filter_alloc(void *opaque, u_int items,
u_int size);
static void ngx_http_gzip_filter_free(void *opaque, void *address);
static void ngx_http_gzip_filter_free_copy_buf(ngx_http_request_t *r,
ngx_http_gzip_ctx_t *ctx);
static ngx_int_t ngx_http_gzip_add_variables(ngx_conf_t *cf);
static ngx_int_t ngx_http_gzip_ratio_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_gzip_filter_init(ngx_conf_t *cf);
static void *ngx_http_gzip_create_conf(ngx_conf_t *cf);
static char *ngx_http_gzip_merge_conf(ngx_conf_t *cf,
void *parent, void *child);
static char *ngx_http_gzip_window(ngx_conf_t *cf, void *post, void *data);
static char *ngx_http_gzip_hash(ngx_conf_t *cf, void *post, void *data);
static ngx_conf_num_bounds_t ngx_http_gzip_comp_level_bounds = {
ngx_conf_check_num_bounds, 1, 9
};
static ngx_conf_post_handler_pt ngx_http_gzip_window_p = ngx_http_gzip_window;
static ngx_conf_post_handler_pt ngx_http_gzip_hash_p = ngx_http_gzip_hash;
static ngx_command_t ngx_http_gzip_filter_commands[] = {
{ ngx_string("gzip"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_gzip_conf_t, enable),
NULL },
{ ngx_string("gzip_buffers"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
ngx_conf_set_bufs_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_gzip_conf_t, bufs),
NULL },
{ ngx_string("gzip_types"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
ngx_http_types_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_gzip_conf_t, types_keys),
&ngx_http_html_default_types[0] },
{ ngx_string("gzip_comp_level"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_num_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_gzip_conf_t, level),
&ngx_http_gzip_comp_level_bounds },
{ ngx_string("gzip_window"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_size_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_gzip_conf_t, wbits),
&ngx_http_gzip_window_p },
{ ngx_string("gzip_hash"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_size_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_gzip_conf_t, memlevel),
&ngx_http_gzip_hash_p },
{ ngx_string("postpone_gzipping"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_size_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_gzip_conf_t, postpone_gzipping),
NULL },
{ ngx_string("gzip_no_buffer"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_gzip_conf_t, no_buffer),
NULL },
{ ngx_string("gzip_min_length"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_size_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_gzip_conf_t, min_length),
NULL },
ngx_null_command
};
static ngx_http_module_t ngx_http_gzip_filter_module_ctx = {
ngx_http_gzip_add_variables, /* preconfiguration */
ngx_http_gzip_filter_init, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
ngx_http_gzip_create_conf, /* create location configuration */
ngx_http_gzip_merge_conf /* merge location configuration */
};
ngx_module_t ngx_http_gzip_filter_module = {
NGX_MODULE_V1,
&ngx_http_gzip_filter_module_ctx, /* module context */
ngx_http_gzip_filter_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static ngx_str_t ngx_http_gzip_ratio = ngx_string("gzip_ratio");
static ngx_http_output_header_filter_pt ngx_http_next_header_filter;
static ngx_http_output_body_filter_pt ngx_http_next_body_filter;
static ngx_uint_t ngx_http_gzip_assume_intel;
static ngx_int_t
ngx_http_gzip_header_filter(ngx_http_request_t *r)
{
ngx_table_elt_t *h;
ngx_http_gzip_ctx_t *ctx;
ngx_http_gzip_conf_t *conf;
conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module);
if (!conf->enable
|| (r->headers_out.status != NGX_HTTP_OK
&& r->headers_out.status != NGX_HTTP_FORBIDDEN
&& r->headers_out.status != NGX_HTTP_NOT_FOUND)
|| (r->headers_out.content_encoding
&& r->headers_out.content_encoding->value.len)
|| (r->headers_out.content_length_n != -1
&& r->headers_out.content_length_n < conf->min_length)
|| ngx_http_test_content_type(r, &conf->types) == NULL
|| r->header_only)
{
return ngx_http_next_header_filter(r);
}
r->gzip_vary = 1;
#if (NGX_HTTP_DEGRADATION)
{
ngx_http_core_loc_conf_t *clcf;
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
if (clcf->gzip_disable_degradation && ngx_http_degraded(r)) {
return ngx_http_next_header_filter(r);
}
}
#endif
if (!r->gzip_tested) {
if (ngx_http_gzip_ok(r) != NGX_OK) {
return ngx_http_next_header_filter(r);
}
} else if (!r->gzip_ok) {
return ngx_http_next_header_filter(r);
}
ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_gzip_ctx_t));
if (ctx == NULL) {
return NGX_ERROR;
}
ngx_http_set_ctx(r, ctx, ngx_http_gzip_filter_module);
ctx->request = r;
ctx->buffering = (conf->postpone_gzipping != 0);
ngx_http_gzip_filter_memory(r, ctx);
h = ngx_list_push(&r->headers_out.headers);
if (h == NULL) {
return NGX_ERROR;
}
h->hash = 1;
ngx_str_set(&h->key, "Content-Encoding");
ngx_str_set(&h->value, "gzip");
r->headers_out.content_encoding = h;
r->main_filter_need_in_memory = 1;
ngx_http_clear_content_length(r);
ngx_http_clear_accept_ranges(r);
ngx_http_weak_etag(r);
return ngx_http_next_header_filter(r);
}
static ngx_int_t
ngx_http_gzip_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
int rc;
ngx_uint_t flush;
ngx_chain_t *cl;
ngx_http_gzip_ctx_t *ctx;
ctx = ngx_http_get_module_ctx(r, ngx_http_gzip_filter_module);
if (ctx == NULL || ctx->done || r->header_only) {
return ngx_http_next_body_filter(r, in);
}
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http gzip filter");
if (ctx->buffering) {
/*
* With default memory settings zlib starts to output gzipped data
* only after it has got about 90K, so it makes sense to allocate
* zlib memory (200-400K) only after we have enough data to compress.
* Although we copy buffers, nevertheless for not big responses
* this allows to allocate zlib memory, to compress and to output
* the response in one step using hot CPU cache.
*/
if (in) {
switch (ngx_http_gzip_filter_buffer(ctx, in)) {
case NGX_OK:
return NGX_OK;
case NGX_DONE:
in = NULL;
break;
default: /* NGX_ERROR */
goto failed;
}
} else {
ctx->buffering = 0;
}
}
if (ctx->preallocated == NULL) {
if (ngx_http_gzip_filter_deflate_start(r, ctx) != NGX_OK) {
goto failed;
}
}
if (in) {
if (ngx_chain_add_copy(r->pool, &ctx->in, in) != NGX_OK) {
goto failed;
}
r->connection->buffered |= NGX_HTTP_GZIP_BUFFERED;
}
if (ctx->nomem) {
/* flush busy buffers */
if (ngx_http_next_body_filter(r, NULL) == NGX_ERROR) {
goto failed;
}
cl = NULL;
ngx_chain_update_chains(r->pool, &ctx->free, &ctx->busy, &cl,
(ngx_buf_tag_t) &ngx_http_gzip_filter_module);
ctx->nomem = 0;
flush = 0;
} else {
flush = ctx->busy ? 1 : 0;
}
for ( ;; ) {
/* cycle while we can write to a client */
for ( ;; ) {
/* cycle while there is data to feed zlib and ... */
rc = ngx_http_gzip_filter_add_data(r, ctx);
if (rc == NGX_DECLINED) {
break;
}
if (rc == NGX_AGAIN) {
continue;
}
/* ... there are buffers to write zlib output */
rc = ngx_http_gzip_filter_get_buf(r, ctx);
if (rc == NGX_DECLINED) {
break;
}
if (rc == NGX_ERROR) {
goto failed;
}
rc = ngx_http_gzip_filter_deflate(r, ctx);
if (rc == NGX_OK) {
break;
}
if (rc == NGX_ERROR) {
goto failed;
}
/* rc == NGX_AGAIN */
}
if (ctx->out == NULL && !flush) {
ngx_http_gzip_filter_free_copy_buf(r, ctx);
return ctx->busy ? NGX_AGAIN : NGX_OK;
}
if (!ctx->gzheader) {
if (ngx_http_gzip_filter_gzheader(r, ctx) != NGX_OK) {
goto failed;
}
}
rc = ngx_http_next_body_filter(r, ctx->out);
if (rc == NGX_ERROR) {
goto failed;
}
ngx_http_gzip_filter_free_copy_buf(r, ctx);
ngx_chain_update_chains(r->pool, &ctx->free, &ctx->busy, &ctx->out,
(ngx_buf_tag_t) &ngx_http_gzip_filter_module);
ctx->last_out = &ctx->out;
ctx->nomem = 0;
flush = 0;
if (ctx->done) {
return rc;
}
}
/* unreachable */
failed:
ctx->done = 1;
if (ctx->preallocated) {
deflateEnd(&ctx->zstream);
ngx_pfree(r->pool, ctx->preallocated);
}
ngx_http_gzip_filter_free_copy_buf(r, ctx);
return NGX_ERROR;
}
static void
ngx_http_gzip_filter_memory(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx)
{
int wbits, memlevel;
ngx_http_gzip_conf_t *conf;
conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module);
wbits = conf->wbits;
memlevel = conf->memlevel;
if (r->headers_out.content_length_n > 0) {
/* the actual zlib window size is smaller by 262 bytes */
while (r->headers_out.content_length_n < ((1 << (wbits - 1)) - 262)) {
wbits--;
memlevel--;
}
if (memlevel < 1) {
memlevel = 1;
}
}
ctx->wbits = wbits;
ctx->memlevel = memlevel;
/*
* We preallocate a memory for zlib in one buffer (200K-400K), this
* decreases a number of malloc() and free() calls and also probably
* decreases a number of syscalls (sbrk()/mmap() and so on).
* Besides we free the memory as soon as a gzipping will complete
* and do not wait while a whole response will be sent to a client.
*
* 8K is for zlib deflate_state, it takes
* *) 5816 bytes on i386 and sparc64 (32-bit mode)
* *) 5920 bytes on amd64 and sparc64
*/
if (!ngx_http_gzip_assume_intel) {
ctx->allocated = 8192 + (1 << (wbits + 2)) + (1 << (memlevel + 9));
} else {
/*
* A zlib variant from Intel, https://github.com/jtkukunas/zlib.
* It can force window bits to 13 for fast compression level,
* on processors with SSE 4.2 it uses 64K hash instead of scaling
* it from the specified memory level, and also introduces
* 16-byte padding in one out of the two window-sized buffers.
*/
if (conf->level == 1) {
wbits = ngx_max(wbits, 13);
}
ctx->allocated = 8192 + 16 + (1 << (wbits + 2))
+ (1 << (ngx_max(memlevel, 8) + 8))
+ (1 << (memlevel + 8));
ctx->intel = 1;
}
}
static ngx_int_t
ngx_http_gzip_filter_buffer(ngx_http_gzip_ctx_t *ctx, ngx_chain_t *in)
{
size_t size, buffered;
ngx_buf_t *b, *buf;
ngx_chain_t *cl, **ll;
ngx_http_request_t *r;
ngx_http_gzip_conf_t *conf;
r = ctx->request;
r->connection->buffered |= NGX_HTTP_GZIP_BUFFERED;
buffered = 0;
ll = &ctx->in;
for (cl = ctx->in; cl; cl = cl->next) {
buffered += cl->buf->last - cl->buf->pos;
ll = &cl->next;
}
conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module);
while (in) {
cl = ngx_alloc_chain_link(r->pool);
if (cl == NULL) {
return NGX_ERROR;
}
b = in->buf;
size = b->last - b->pos;
buffered += size;
if (b->flush || b->last_buf || buffered > conf->postpone_gzipping) {
ctx->buffering = 0;
}
if (ctx->buffering && size) {
buf = ngx_create_temp_buf(r->pool, size);
if (buf == NULL) {
return NGX_ERROR;
}
buf->last = ngx_cpymem(buf->pos, b->pos, size);
b->pos = b->last;
buf->last_buf = b->last_buf;
buf->tag = (ngx_buf_tag_t) &ngx_http_gzip_filter_module;
cl->buf = buf;
} else {
cl->buf = b;
}
*ll = cl;
ll = &cl->next;
in = in->next;
}
*ll = NULL;
return ctx->buffering ? NGX_OK : NGX_DONE;
}
static ngx_int_t
ngx_http_gzip_filter_deflate_start(ngx_http_request_t *r,
ngx_http_gzip_ctx_t *ctx)
{
int rc;
ngx_http_gzip_conf_t *conf;
conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module);
ctx->preallocated = ngx_palloc(r->pool, ctx->allocated);
if (ctx->preallocated == NULL) {
return NGX_ERROR;
}
ctx->free_mem = ctx->preallocated;
ctx->zstream.zalloc = ngx_http_gzip_filter_alloc;
ctx->zstream.zfree = ngx_http_gzip_filter_free;
ctx->zstream.opaque = ctx;
rc = deflateInit2(&ctx->zstream, (int) conf->level, Z_DEFLATED,
- ctx->wbits, ctx->memlevel, Z_DEFAULT_STRATEGY);
if (rc != Z_OK) {
ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
"deflateInit2() failed: %d", rc);
return NGX_ERROR;
}
ctx->last_out = &ctx->out;
ctx->crc32 = crc32(0L, Z_NULL, 0);
ctx->flush = Z_NO_FLUSH;
return NGX_OK;
}
static ngx_int_t
ngx_http_gzip_filter_gzheader(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx)
{
ngx_buf_t *b;
ngx_chain_t *cl;
static u_char gzheader[10] =
{ 0x1f, 0x8b, Z_DEFLATED, 0, 0, 0, 0, 0, 0, 3 };
b = ngx_calloc_buf(r->pool);
if (b == NULL) {
return NGX_ERROR;
}
b->memory = 1;
b->pos = gzheader;
b->last = b->pos + 10;
cl = ngx_alloc_chain_link(r->pool);
if (cl == NULL) {
return NGX_ERROR;
}
cl->buf = b;
cl->next = ctx->out;
ctx->out = cl;
ctx->gzheader = 1;
return NGX_OK;
}
static ngx_int_t
ngx_http_gzip_filter_add_data(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx)
{
ngx_chain_t *cl;
if (ctx->zstream.avail_in || ctx->flush != Z_NO_FLUSH || ctx->redo) {
return NGX_OK;
}
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"gzip in: %p", ctx->in);
if (ctx->in == NULL) {
return NGX_DECLINED;
}
if (ctx->copy_buf) {
/*
* to avoid CPU cache trashing we do not free() just quit buf,
* but postpone free()ing after zlib compressing and data output
*/
ctx->copy_buf->next = ctx->copied;
ctx->copied = ctx->copy_buf;
ctx->copy_buf = NULL;
}
cl = ctx->in;
ctx->in_buf = cl->buf;
ctx->in = cl->next;
if (ctx->in_buf->tag == (ngx_buf_tag_t) &ngx_http_gzip_filter_module) {
ctx->copy_buf = cl;
} else {
ngx_free_chain(r->pool, cl);
}
ctx->zstream.next_in = ctx->in_buf->pos;
ctx->zstream.avail_in = ctx->in_buf->last - ctx->in_buf->pos;
ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"gzip in_buf:%p ni:%p ai:%ud",
ctx->in_buf,
ctx->zstream.next_in, ctx->zstream.avail_in);
if (ctx->in_buf->last_buf) {
ctx->flush = Z_FINISH;
} else if (ctx->in_buf->flush) {
ctx->flush = Z_SYNC_FLUSH;
}
if (ctx->zstream.avail_in) {
ctx->crc32 = crc32(ctx->crc32, ctx->zstream.next_in,
ctx->zstream.avail_in);
} else if (ctx->flush == Z_NO_FLUSH) {
return NGX_AGAIN;
}
return NGX_OK;
}
static ngx_int_t
ngx_http_gzip_filter_get_buf(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx)
{
ngx_chain_t *cl;
ngx_http_gzip_conf_t *conf;
if (ctx->zstream.avail_out) {
return NGX_OK;
}
conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module);
if (ctx->free) {
cl = ctx->free;
ctx->out_buf = cl->buf;
ctx->free = cl->next;
ngx_free_chain(r->pool, cl);
} else if (ctx->bufs < conf->bufs.num) {
ctx->out_buf = ngx_create_temp_buf(r->pool, conf->bufs.size);
if (ctx->out_buf == NULL) {
return NGX_ERROR;
}
ctx->out_buf->tag = (ngx_buf_tag_t) &ngx_http_gzip_filter_module;
ctx->out_buf->recycled = 1;
ctx->bufs++;
} else {
ctx->nomem = 1;
return NGX_DECLINED;
}
ctx->zstream.next_out = ctx->out_buf->pos;
ctx->zstream.avail_out = conf->bufs.size;
return NGX_OK;
}
static ngx_int_t
ngx_http_gzip_filter_deflate(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx)
{
int rc;
ngx_buf_t *b;
ngx_chain_t *cl;
ngx_http_gzip_conf_t *conf;
ngx_log_debug6(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"deflate in: ni:%p no:%p ai:%ud ao:%ud fl:%d redo:%d",
ctx->zstream.next_in, ctx->zstream.next_out,
ctx->zstream.avail_in, ctx->zstream.avail_out,
ctx->flush, ctx->redo);
rc = deflate(&ctx->zstream, ctx->flush);
if (rc != Z_OK && rc != Z_STREAM_END && rc != Z_BUF_ERROR) {
ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
"deflate() failed: %d, %d", ctx->flush, rc);
return NGX_ERROR;
}
ngx_log_debug5(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"deflate out: ni:%p no:%p ai:%ud ao:%ud rc:%d",
ctx->zstream.next_in, ctx->zstream.next_out,
ctx->zstream.avail_in, ctx->zstream.avail_out,
rc);
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"gzip in_buf:%p pos:%p",
ctx->in_buf, ctx->in_buf->pos);
if (ctx->zstream.next_in) {
ctx->in_buf->pos = ctx->zstream.next_in;
if (ctx->zstream.avail_in == 0) {
ctx->zstream.next_in = NULL;
}
}
ctx->out_buf->last = ctx->zstream.next_out;
if (ctx->zstream.avail_out == 0) {
/* zlib wants to output some more gzipped data */
cl = ngx_alloc_chain_link(r->pool);
if (cl == NULL) {
return NGX_ERROR;
}
cl->buf = ctx->out_buf;
cl->next = NULL;
*ctx->last_out = cl;
ctx->last_out = &cl->next;
ctx->redo = 1;
return NGX_AGAIN;
}
ctx->redo = 0;
if (ctx->flush == Z_SYNC_FLUSH) {
ctx->flush = Z_NO_FLUSH;
cl = ngx_alloc_chain_link(r->pool);
if (cl == NULL) {
return NGX_ERROR;
}
b = ctx->out_buf;
if (ngx_buf_size(b) == 0) {
b = ngx_calloc_buf(ctx->request->pool);
if (b == NULL) {
return NGX_ERROR;
}
} else {
ctx->zstream.avail_out = 0;
}
b->flush = 1;
cl->buf = b;
cl->next = NULL;
*ctx->last_out = cl;
ctx->last_out = &cl->next;
r->connection->buffered &= ~NGX_HTTP_GZIP_BUFFERED;
return NGX_OK;
}
if (rc == Z_STREAM_END) {
if (ngx_http_gzip_filter_deflate_end(r, ctx) != NGX_OK) {
return NGX_ERROR;
}
return NGX_OK;
}
conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module);
if (conf->no_buffer && ctx->in == NULL) {
cl = ngx_alloc_chain_link(r->pool);
if (cl == NULL) {
return NGX_ERROR;
}
cl->buf = ctx->out_buf;
cl->next = NULL;
*ctx->last_out = cl;
ctx->last_out = &cl->next;
return NGX_OK;
}
return NGX_AGAIN;
}
static ngx_int_t
ngx_http_gzip_filter_deflate_end(ngx_http_request_t *r,
ngx_http_gzip_ctx_t *ctx)
{
int rc;
ngx_buf_t *b;
ngx_chain_t *cl;
struct gztrailer *trailer;
ctx->zin = ctx->zstream.total_in;
ctx->zout = 10 + ctx->zstream.total_out + 8;
rc = deflateEnd(&ctx->zstream);
if (rc != Z_OK) {
ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
"deflateEnd() failed: %d", rc);
return NGX_ERROR;
}
ngx_pfree(r->pool, ctx->preallocated);
cl = ngx_alloc_chain_link(r->pool);
if (cl == NULL) {
return NGX_ERROR;
}
cl->buf = ctx->out_buf;
cl->next = NULL;
*ctx->last_out = cl;
ctx->last_out = &cl->next;
if (ctx->zstream.avail_out >= 8) {
trailer = (struct gztrailer *) ctx->out_buf->last;
ctx->out_buf->last += 8;
ctx->out_buf->last_buf = 1;
} else {
b = ngx_create_temp_buf(r->pool, 8);
if (b == NULL) {
return NGX_ERROR;
}
b->last_buf = 1;
cl = ngx_alloc_chain_link(r->pool);
if (cl == NULL) {
return NGX_ERROR;
}
cl->buf = b;
cl->next = NULL;
*ctx->last_out = cl;
ctx->last_out = &cl->next;
trailer = (struct gztrailer *) b->pos;
b->last += 8;
}
#if (NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED)
trailer->crc32 = ctx->crc32;
trailer->zlen = ctx->zin;
#else
trailer->crc32[0] = (u_char) (ctx->crc32 & 0xff);
trailer->crc32[1] = (u_char) ((ctx->crc32 >> 8) & 0xff);
trailer->crc32[2] = (u_char) ((ctx->crc32 >> 16) & 0xff);
trailer->crc32[3] = (u_char) ((ctx->crc32 >> 24) & 0xff);
trailer->zlen[0] = (u_char) (ctx->zin & 0xff);
trailer->zlen[1] = (u_char) ((ctx->zin >> 8) & 0xff);
trailer->zlen[2] = (u_char) ((ctx->zin >> 16) & 0xff);
trailer->zlen[3] = (u_char) ((ctx->zin >> 24) & 0xff);
#endif
ctx->zstream.avail_in = 0;
ctx->zstream.avail_out = 0;
ctx->done = 1;
r->connection->buffered &= ~NGX_HTTP_GZIP_BUFFERED;
return NGX_OK;
}
static void *
ngx_http_gzip_filter_alloc(void *opaque, u_int items, u_int size)
{
ngx_http_gzip_ctx_t *ctx = opaque;
void *p;
ngx_uint_t alloc;
alloc = items * size;
if (items == 1 && alloc % 512 != 0 && alloc < 8192) {
/*
* The zlib deflate_state allocation, it takes about 6K,
* we allocate 8K. Other allocations are divisible by 512.
*/
alloc = 8192;
}
if (alloc <= ctx->allocated) {
p = ctx->free_mem;
ctx->free_mem += alloc;
ctx->allocated -= alloc;
ngx_log_debug4(NGX_LOG_DEBUG_HTTP, ctx->request->connection->log, 0,
"gzip alloc: n:%ud s:%ud a:%ui p:%p",
items, size, alloc, p);
return p;
}
if (ctx->intel) {
ngx_log_error(NGX_LOG_ALERT, ctx->request->connection->log, 0,
"gzip filter failed to use preallocated memory: "
"%ud of %ui", items * size, ctx->allocated);
} else {
ngx_http_gzip_assume_intel = 1;
}
p = ngx_palloc(ctx->request->pool, items * size);
return p;
}
static void
ngx_http_gzip_filter_free(void *opaque, void *address)
{
#if 0
ngx_http_gzip_ctx_t *ctx = opaque;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->request->connection->log, 0,
"gzip free: %p", address);
#endif
}
static void
ngx_http_gzip_filter_free_copy_buf(ngx_http_request_t *r,
ngx_http_gzip_ctx_t *ctx)
{
ngx_chain_t *cl;
for (cl = ctx->copied; cl; cl = cl->next) {
ngx_pfree(r->pool, cl->buf->start);
}
ctx->copied = NULL;
}
static ngx_int_t
ngx_http_gzip_add_variables(ngx_conf_t *cf)
{
ngx_http_variable_t *var;
var = ngx_http_add_variable(cf, &ngx_http_gzip_ratio, NGX_HTTP_VAR_NOHASH);
if (var == NULL) {
return NGX_ERROR;
}
var->get_handler = ngx_http_gzip_ratio_variable;
return NGX_OK;
}
static ngx_int_t
ngx_http_gzip_ratio_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
ngx_uint_t zint, zfrac;
ngx_http_gzip_ctx_t *ctx;
ctx = ngx_http_get_module_ctx(r, ngx_http_gzip_filter_module);
if (ctx == NULL || ctx->zout == 0) {
v->not_found = 1;
return NGX_OK;
}
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = ngx_pnalloc(r->pool, NGX_INT32_LEN + 3);
if (v->data == NULL) {
return NGX_ERROR;
}
zint = (ngx_uint_t) (ctx->zin / ctx->zout);
zfrac = (ngx_uint_t) ((ctx->zin * 100 / ctx->zout) % 100);
if ((ctx->zin * 1000 / ctx->zout) % 10 > 4) {
/* the rounding, e.g., 2.125 to 2.13 */
zfrac++;
if (zfrac > 99) {
zint++;
zfrac = 0;
}
}
v->len = ngx_sprintf(v->data, "%ui.%02ui", zint, zfrac) - v->data;
return NGX_OK;
}
static void *
ngx_http_gzip_create_conf(ngx_conf_t *cf)
{
ngx_http_gzip_conf_t *conf;
conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_gzip_conf_t));
if (conf == NULL) {
return NULL;
}
/*
* set by ngx_pcalloc():
*
* conf->bufs.num = 0;
* conf->types = { NULL };
* conf->types_keys = NULL;
*/
conf->enable = NGX_CONF_UNSET;
conf->no_buffer = NGX_CONF_UNSET;
conf->postpone_gzipping = NGX_CONF_UNSET_SIZE;
conf->level = NGX_CONF_UNSET;
conf->wbits = NGX_CONF_UNSET_SIZE;
conf->memlevel = NGX_CONF_UNSET_SIZE;
conf->min_length = NGX_CONF_UNSET;
return conf;
}
static char *
ngx_http_gzip_merge_conf(ngx_conf_t *cf, void *parent, void *child)
{
ngx_http_gzip_conf_t *prev = parent;
ngx_http_gzip_conf_t *conf = child;
ngx_conf_merge_value(conf->enable, prev->enable, 0);
ngx_conf_merge_value(conf->no_buffer, prev->no_buffer, 0);
ngx_conf_merge_bufs_value(conf->bufs, prev->bufs,
(128 * 1024) / ngx_pagesize, ngx_pagesize);
ngx_conf_merge_size_value(conf->postpone_gzipping, prev->postpone_gzipping,
0);
ngx_conf_merge_value(conf->level, prev->level, 1);
ngx_conf_merge_size_value(conf->wbits, prev->wbits, MAX_WBITS);
ngx_conf_merge_size_value(conf->memlevel, prev->memlevel,
MAX_MEM_LEVEL - 1);
ngx_conf_merge_value(conf->min_length, prev->min_length, 20);
if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types,
&prev->types_keys, &prev->types,
ngx_http_html_default_types)
!= NGX_OK)
{
return NGX_CONF_ERROR;
}
return NGX_CONF_OK;
}
static ngx_int_t
ngx_http_gzip_filter_init(ngx_conf_t *cf)
{
ngx_http_next_header_filter = ngx_http_top_header_filter;
ngx_http_top_header_filter = ngx_http_gzip_header_filter;
ngx_http_next_body_filter = ngx_http_top_body_filter;
ngx_http_top_body_filter = ngx_http_gzip_body_filter;
return NGX_OK;
}
static char *
ngx_http_gzip_window(ngx_conf_t *cf, void *post, void *data)
{
size_t *np = data;
size_t wbits, wsize;
wbits = 15;
for (wsize = 32 * 1024; wsize > 256; wsize >>= 1) {
if (wsize == *np) {
*np = wbits;
return NGX_CONF_OK;
}
wbits--;
}
return "must be 512, 1k, 2k, 4k, 8k, 16k, or 32k";
}
static char *
ngx_http_gzip_hash(ngx_conf_t *cf, void *post, void *data)
{
size_t *np = data;
size_t memlevel, hsize;
memlevel = 9;
for (hsize = 128 * 1024; hsize > 256; hsize >>= 1) {
if (hsize == *np) {
*np = memlevel;
return NGX_CONF_OK;
}
memlevel--;
}
return "must be 512, 1k, 2k, 4k, 8k, 16k, 32k, 64k, or 128k";
}
nginx-1.14.0/src/http/modules/ngx_http_gzip_static_module.c 000644 001751 001751 00000020424 13265410474 025300 0 ustar 00mdounin mdounin 000000 000000
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#include
#include
#include
#define NGX_HTTP_GZIP_STATIC_OFF 0
#define NGX_HTTP_GZIP_STATIC_ON 1
#define NGX_HTTP_GZIP_STATIC_ALWAYS 2
typedef struct {
ngx_uint_t enable;
} ngx_http_gzip_static_conf_t;
static ngx_int_t ngx_http_gzip_static_handler(ngx_http_request_t *r);
static void *ngx_http_gzip_static_create_conf(ngx_conf_t *cf);
static char *ngx_http_gzip_static_merge_conf(ngx_conf_t *cf, void *parent,
void *child);
static ngx_int_t ngx_http_gzip_static_init(ngx_conf_t *cf);
static ngx_conf_enum_t ngx_http_gzip_static[] = {
{ ngx_string("off"), NGX_HTTP_GZIP_STATIC_OFF },
{ ngx_string("on"), NGX_HTTP_GZIP_STATIC_ON },
{ ngx_string("always"), NGX_HTTP_GZIP_STATIC_ALWAYS },
{ ngx_null_string, 0 }
};
static ngx_command_t ngx_http_gzip_static_commands[] = {
{ ngx_string("gzip_static"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_enum_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_gzip_static_conf_t, enable),
&ngx_http_gzip_static },
ngx_null_command
};
static ngx_http_module_t ngx_http_gzip_static_module_ctx = {
NULL, /* preconfiguration */
ngx_http_gzip_static_init, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
ngx_http_gzip_static_create_conf, /* create location configuration */
ngx_http_gzip_static_merge_conf /* merge location configuration */
};
ngx_module_t ngx_http_gzip_static_module = {
NGX_MODULE_V1,
&ngx_http_gzip_static_module_ctx, /* module context */
ngx_http_gzip_static_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static ngx_int_t
ngx_http_gzip_static_handler(ngx_http_request_t *r)
{
u_char *p;
size_t root;
ngx_str_t path;
ngx_int_t rc;
ngx_uint_t level;
ngx_log_t *log;
ngx_buf_t *b;
ngx_chain_t out;
ngx_table_elt_t *h;
ngx_open_file_info_t of;
ngx_http_core_loc_conf_t *clcf;
ngx_http_gzip_static_conf_t *gzcf;
if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) {
return NGX_DECLINED;
}
if (r->uri.data[r->uri.len - 1] == '/') {
return NGX_DECLINED;
}
gzcf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_static_module);
if (gzcf->enable == NGX_HTTP_GZIP_STATIC_OFF) {
return NGX_DECLINED;
}
if (gzcf->enable == NGX_HTTP_GZIP_STATIC_ON) {
rc = ngx_http_gzip_ok(r);
} else {
/* always */
rc = NGX_OK;
}
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
if (!clcf->gzip_vary && rc != NGX_OK) {
return NGX_DECLINED;
}
log = r->connection->log;
p = ngx_http_map_uri_to_path(r, &path, &root, sizeof(".gz") - 1);
if (p == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
*p++ = '.';
*p++ = 'g';
*p++ = 'z';
*p = '\0';
path.len = p - path.data;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0,
"http filename: \"%s\"", path.data);
ngx_memzero(&of, sizeof(ngx_open_file_info_t));
of.read_ahead = clcf->read_ahead;
of.directio = clcf->directio;
of.valid = clcf->open_file_cache_valid;
of.min_uses = clcf->open_file_cache_min_uses;
of.errors = clcf->open_file_cache_errors;
of.events = clcf->open_file_cache_events;
if (ngx_http_set_disable_symlinks(r, clcf, &path, &of) != NGX_OK) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)
!= NGX_OK)
{
switch (of.err) {
case 0:
return NGX_HTTP_INTERNAL_SERVER_ERROR;
case NGX_ENOENT:
case NGX_ENOTDIR:
case NGX_ENAMETOOLONG:
return NGX_DECLINED;
case NGX_EACCES:
#if (NGX_HAVE_OPENAT)
case NGX_EMLINK:
case NGX_ELOOP:
#endif
level = NGX_LOG_ERR;
break;
default:
level = NGX_LOG_CRIT;
break;
}
ngx_log_error(level, log, of.err,
"%s \"%s\" failed", of.failed, path.data);
return NGX_DECLINED;
}
if (gzcf->enable == NGX_HTTP_GZIP_STATIC_ON) {
r->gzip_vary = 1;
if (rc != NGX_OK) {
return NGX_DECLINED;
}
}
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, "http static fd: %d", of.fd);
if (of.is_dir) {
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, log, 0, "http dir");
return NGX_DECLINED;
}
#if !(NGX_WIN32) /* the not regular files are probably Unix specific */
if (!of.is_file) {
ngx_log_error(NGX_LOG_CRIT, log, 0,
"\"%s\" is not a regular file", path.data);
return NGX_HTTP_NOT_FOUND;
}
#endif
r->root_tested = !r->error_page;
rc = ngx_http_discard_request_body(r);
if (rc != NGX_OK) {
return rc;
}
log->action = "sending response to client";
r->headers_out.status = NGX_HTTP_OK;
r->headers_out.content_length_n = of.size;
r->headers_out.last_modified_time = of.mtime;
if (ngx_http_set_etag(r) != NGX_OK) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
if (ngx_http_set_content_type(r) != NGX_OK) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
h = ngx_list_push(&r->headers_out.headers);
if (h == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
h->hash = 1;
ngx_str_set(&h->key, "Content-Encoding");
ngx_str_set(&h->value, "gzip");
r->headers_out.content_encoding = h;
/* we need to allocate all before the header would be sent */
b = ngx_calloc_buf(r->pool);
if (b == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
b->file = ngx_pcalloc(r->pool, sizeof(ngx_file_t));
if (b->file == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
rc = ngx_http_send_header(r);
if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
return rc;
}
b->file_pos = 0;
b->file_last = of.size;
b->in_file = b->file_last ? 1 : 0;
b->last_buf = (r == r->main) ? 1 : 0;
b->last_in_chain = 1;
b->file->fd = of.fd;
b->file->name = path;
b->file->log = log;
b->file->directio = of.is_directio;
out.buf = b;
out.next = NULL;
return ngx_http_output_filter(r, &out);
}
static void *
ngx_http_gzip_static_create_conf(ngx_conf_t *cf)
{
ngx_http_gzip_static_conf_t *conf;
conf = ngx_palloc(cf->pool, sizeof(ngx_http_gzip_static_conf_t));
if (conf == NULL) {
return NULL;
}
conf->enable = NGX_CONF_UNSET_UINT;
return conf;
}
static char *
ngx_http_gzip_static_merge_conf(ngx_conf_t *cf, void *parent, void *child)
{
ngx_http_gzip_static_conf_t *prev = parent;
ngx_http_gzip_static_conf_t *conf = child;
ngx_conf_merge_uint_value(conf->enable, prev->enable,
NGX_HTTP_GZIP_STATIC_OFF);
return NGX_CONF_OK;
}
static ngx_int_t
ngx_http_gzip_static_init(ngx_conf_t *cf)
{
ngx_http_handler_pt *h;
ngx_http_core_main_conf_t *cmcf;
cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers);
if (h == NULL) {
return NGX_ERROR;
}
*h = ngx_http_gzip_static_handler;
return NGX_OK;
}
nginx-1.14.0/src/http/modules/ngx_http_headers_filter_module.c 000644 001751 001751 00000052304 13265410474 025742 0 ustar 00mdounin mdounin 000000 000000
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#include
#include
#include
typedef struct ngx_http_header_val_s ngx_http_header_val_t;
typedef ngx_int_t (*ngx_http_set_header_pt)(ngx_http_request_t *r,
ngx_http_header_val_t *hv, ngx_str_t *value);
typedef struct {
ngx_str_t name;
ngx_uint_t offset;
ngx_http_set_header_pt handler;
} ngx_http_set_header_t;
struct ngx_http_header_val_s {
ngx_http_complex_value_t value;
ngx_str_t key;
ngx_http_set_header_pt handler;
ngx_uint_t offset;
ngx_uint_t always; /* unsigned always:1 */
};
typedef enum {
NGX_HTTP_EXPIRES_OFF,
NGX_HTTP_EXPIRES_EPOCH,
NGX_HTTP_EXPIRES_MAX,
NGX_HTTP_EXPIRES_ACCESS,
NGX_HTTP_EXPIRES_MODIFIED,
NGX_HTTP_EXPIRES_DAILY,
NGX_HTTP_EXPIRES_UNSET
} ngx_http_expires_t;
typedef struct {
ngx_http_expires_t expires;
time_t expires_time;
ngx_http_complex_value_t *expires_value;
ngx_array_t *headers;
ngx_array_t *trailers;
} ngx_http_headers_conf_t;
static ngx_int_t ngx_http_set_expires(ngx_http_request_t *r,
ngx_http_headers_conf_t *conf);
static ngx_int_t ngx_http_parse_expires(ngx_str_t *value,
ngx_http_expires_t *expires, time_t *expires_time, char **err);
static ngx_int_t ngx_http_add_multi_header_lines(ngx_http_request_t *r,
ngx_http_header_val_t *hv, ngx_str_t *value);
static ngx_int_t ngx_http_add_header(ngx_http_request_t *r,
ngx_http_header_val_t *hv, ngx_str_t *value);
static ngx_int_t ngx_http_set_last_modified(ngx_http_request_t *r,
ngx_http_header_val_t *hv, ngx_str_t *value);
static ngx_int_t ngx_http_set_response_header(ngx_http_request_t *r,
ngx_http_header_val_t *hv, ngx_str_t *value);
static void *ngx_http_headers_create_conf(ngx_conf_t *cf);
static char *ngx_http_headers_merge_conf(ngx_conf_t *cf,
void *parent, void *child);
static ngx_int_t ngx_http_headers_filter_init(ngx_conf_t *cf);
static char *ngx_http_headers_expires(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static char *ngx_http_headers_add(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static ngx_http_set_header_t ngx_http_set_headers[] = {
{ ngx_string("Cache-Control"),
offsetof(ngx_http_headers_out_t, cache_control),
ngx_http_add_multi_header_lines },
{ ngx_string("Link"),
offsetof(ngx_http_headers_out_t, link),
ngx_http_add_multi_header_lines },
{ ngx_string("Last-Modified"),
offsetof(ngx_http_headers_out_t, last_modified),
ngx_http_set_last_modified },
{ ngx_string("ETag"),
offsetof(ngx_http_headers_out_t, etag),
ngx_http_set_response_header },
{ ngx_null_string, 0, NULL }
};
static ngx_command_t ngx_http_headers_filter_commands[] = {
{ ngx_string("expires"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
|NGX_CONF_TAKE12,
ngx_http_headers_expires,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
{ ngx_string("add_header"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
|NGX_CONF_TAKE23,
ngx_http_headers_add,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_headers_conf_t, headers),
NULL },
{ ngx_string("add_trailer"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
|NGX_CONF_TAKE23,
ngx_http_headers_add,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_headers_conf_t, trailers),
NULL },
ngx_null_command
};
static ngx_http_module_t ngx_http_headers_filter_module_ctx = {
NULL, /* preconfiguration */
ngx_http_headers_filter_init, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
ngx_http_headers_create_conf, /* create location configuration */
ngx_http_headers_merge_conf /* merge location configuration */
};
ngx_module_t ngx_http_headers_filter_module = {
NGX_MODULE_V1,
&ngx_http_headers_filter_module_ctx, /* module context */
ngx_http_headers_filter_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static ngx_http_output_header_filter_pt ngx_http_next_header_filter;
static ngx_http_output_body_filter_pt ngx_http_next_body_filter;
static ngx_int_t
ngx_http_headers_filter(ngx_http_request_t *r)
{
ngx_str_t value;
ngx_uint_t i, safe_status;
ngx_http_header_val_t *h;
ngx_http_headers_conf_t *conf;
if (r != r->main) {
return ngx_http_next_header_filter(r);
}
conf = ngx_http_get_module_loc_conf(r, ngx_http_headers_filter_module);
if (conf->expires == NGX_HTTP_EXPIRES_OFF
&& conf->headers == NULL
&& conf->trailers == NULL)
{
return ngx_http_next_header_filter(r);
}
switch (r->headers_out.status) {
case NGX_HTTP_OK:
case NGX_HTTP_CREATED:
case NGX_HTTP_NO_CONTENT:
case NGX_HTTP_PARTIAL_CONTENT:
case NGX_HTTP_MOVED_PERMANENTLY:
case NGX_HTTP_MOVED_TEMPORARILY:
case NGX_HTTP_SEE_OTHER:
case NGX_HTTP_NOT_MODIFIED:
case NGX_HTTP_TEMPORARY_REDIRECT:
case NGX_HTTP_PERMANENT_REDIRECT:
safe_status = 1;
break;
default:
safe_status = 0;
break;
}
if (conf->expires != NGX_HTTP_EXPIRES_OFF && safe_status) {
if (ngx_http_set_expires(r, conf) != NGX_OK) {
return NGX_ERROR;
}
}
if (conf->headers) {
h = conf->headers->elts;
for (i = 0; i < conf->headers->nelts; i++) {
if (!safe_status && !h[i].always) {
continue;
}
if (ngx_http_complex_value(r, &h[i].value, &value) != NGX_OK) {
return NGX_ERROR;
}
if (h[i].handler(r, &h[i], &value) != NGX_OK) {
return NGX_ERROR;
}
}
}
if (conf->trailers) {
h = conf->trailers->elts;
for (i = 0; i < conf->trailers->nelts; i++) {
if (!safe_status && !h[i].always) {
continue;
}
r->expect_trailers = 1;
break;
}
}
return ngx_http_next_header_filter(r);
}
static ngx_int_t
ngx_http_trailers_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
ngx_str_t value;
ngx_uint_t i, safe_status;
ngx_chain_t *cl;
ngx_table_elt_t *t;
ngx_http_header_val_t *h;
ngx_http_headers_conf_t *conf;
conf = ngx_http_get_module_loc_conf(r, ngx_http_headers_filter_module);
if (in == NULL
|| conf->trailers == NULL
|| !r->expect_trailers
|| r->header_only)
{
return ngx_http_next_body_filter(r, in);
}
for (cl = in; cl; cl = cl->next) {
if (cl->buf->last_buf) {
break;
}
}
if (cl == NULL) {
return ngx_http_next_body_filter(r, in);
}
switch (r->headers_out.status) {
case NGX_HTTP_OK:
case NGX_HTTP_CREATED:
case NGX_HTTP_NO_CONTENT:
case NGX_HTTP_PARTIAL_CONTENT:
case NGX_HTTP_MOVED_PERMANENTLY:
case NGX_HTTP_MOVED_TEMPORARILY:
case NGX_HTTP_SEE_OTHER:
case NGX_HTTP_NOT_MODIFIED:
case NGX_HTTP_TEMPORARY_REDIRECT:
case NGX_HTTP_PERMANENT_REDIRECT:
safe_status = 1;
break;
default:
safe_status = 0;
break;
}
h = conf->trailers->elts;
for (i = 0; i < conf->trailers->nelts; i++) {
if (!safe_status && !h[i].always) {
continue;
}
if (ngx_http_complex_value(r, &h[i].value, &value) != NGX_OK) {
return NGX_ERROR;
}
if (value.len) {
t = ngx_list_push(&r->headers_out.trailers);
if (t == NULL) {
return NGX_ERROR;
}
t->key = h[i].key;
t->value = value;
t->hash = 1;
}
}
return ngx_http_next_body_filter(r, in);
}
static ngx_int_t
ngx_http_set_expires(ngx_http_request_t *r, ngx_http_headers_conf_t *conf)
{
char *err;
size_t len;
time_t now, expires_time, max_age;
ngx_str_t value;
ngx_int_t rc;
ngx_uint_t i;
ngx_table_elt_t *e, *cc, **ccp;
ngx_http_expires_t expires;
expires = conf->expires;
expires_time = conf->expires_time;
if (conf->expires_value != NULL) {
if (ngx_http_complex_value(r, conf->expires_value, &value) != NGX_OK) {
return NGX_ERROR;
}
rc = ngx_http_parse_expires(&value, &expires, &expires_time, &err);
if (rc != NGX_OK) {
return NGX_OK;
}
if (expires == NGX_HTTP_EXPIRES_OFF) {
return NGX_OK;
}
}
e = r->headers_out.expires;
if (e == NULL) {
e = ngx_list_push(&r->headers_out.headers);
if (e == NULL) {
return NGX_ERROR;
}
r->headers_out.expires = e;
e->hash = 1;
ngx_str_set(&e->key, "Expires");
}
len = sizeof("Mon, 28 Sep 1970 06:00:00 GMT");
e->value.len = len - 1;
ccp = r->headers_out.cache_control.elts;
if (ccp == NULL) {
if (ngx_array_init(&r->headers_out.cache_control, r->pool,
1, sizeof(ngx_table_elt_t *))
!= NGX_OK)
{
return NGX_ERROR;
}
cc = ngx_list_push(&r->headers_out.headers);
if (cc == NULL) {
return NGX_ERROR;
}
cc->hash = 1;
ngx_str_set(&cc->key, "Cache-Control");
ccp = ngx_array_push(&r->headers_out.cache_control);
if (ccp == NULL) {
return NGX_ERROR;
}
*ccp = cc;
} else {
for (i = 1; i < r->headers_out.cache_control.nelts; i++) {
ccp[i]->hash = 0;
}
cc = ccp[0];
}
if (expires == NGX_HTTP_EXPIRES_EPOCH) {
e->value.data = (u_char *) "Thu, 01 Jan 1970 00:00:01 GMT";
ngx_str_set(&cc->value, "no-cache");
return NGX_OK;
}
if (expires == NGX_HTTP_EXPIRES_MAX) {
e->value.data = (u_char *) "Thu, 31 Dec 2037 23:55:55 GMT";
/* 10 years */
ngx_str_set(&cc->value, "max-age=315360000");
return NGX_OK;
}
e->value.data = ngx_pnalloc(r->pool, len);
if (e->value.data == NULL) {
return NGX_ERROR;
}
if (expires_time == 0 && expires != NGX_HTTP_EXPIRES_DAILY) {
ngx_memcpy(e->value.data, ngx_cached_http_time.data,
ngx_cached_http_time.len + 1);
ngx_str_set(&cc->value, "max-age=0");
return NGX_OK;
}
now = ngx_time();
if (expires == NGX_HTTP_EXPIRES_DAILY) {
expires_time = ngx_next_time(expires_time);
max_age = expires_time - now;
} else if (expires == NGX_HTTP_EXPIRES_ACCESS
|| r->headers_out.last_modified_time == -1)
{
max_age = expires_time;
expires_time += now;
} else {
expires_time += r->headers_out.last_modified_time;
max_age = expires_time - now;
}
ngx_http_time(e->value.data, expires_time);
if (conf->expires_time < 0 || max_age < 0) {
ngx_str_set(&cc->value, "no-cache");
return NGX_OK;
}
cc->value.data = ngx_pnalloc(r->pool,
sizeof("max-age=") + NGX_TIME_T_LEN + 1);
if (cc->value.data == NULL) {
return NGX_ERROR;
}
cc->value.len = ngx_sprintf(cc->value.data, "max-age=%T", max_age)
- cc->value.data;
return NGX_OK;
}
static ngx_int_t
ngx_http_parse_expires(ngx_str_t *value, ngx_http_expires_t *expires,
time_t *expires_time, char **err)
{
ngx_uint_t minus;
if (*expires != NGX_HTTP_EXPIRES_MODIFIED) {
if (value->len == 5 && ngx_strncmp(value->data, "epoch", 5) == 0) {
*expires = NGX_HTTP_EXPIRES_EPOCH;
return NGX_OK;
}
if (value->len == 3 && ngx_strncmp(value->data, "max", 3) == 0) {
*expires = NGX_HTTP_EXPIRES_MAX;
return NGX_OK;
}
if (value->len == 3 && ngx_strncmp(value->data, "off", 3) == 0) {
*expires = NGX_HTTP_EXPIRES_OFF;
return NGX_OK;
}
}
if (value->len && value->data[0] == '@') {
value->data++;
value->len--;
minus = 0;
if (*expires == NGX_HTTP_EXPIRES_MODIFIED) {
*err = "daily time cannot be used with \"modified\" parameter";
return NGX_ERROR;
}
*expires = NGX_HTTP_EXPIRES_DAILY;
} else if (value->len && value->data[0] == '+') {
value->data++;
value->len--;
minus = 0;
} else if (value->len && value->data[0] == '-') {
value->data++;
value->len--;
minus = 1;
} else {
minus = 0;
}
*expires_time = ngx_parse_time(value, 1);
if (*expires_time == (time_t) NGX_ERROR) {
*err = "invalid value";
return NGX_ERROR;
}
if (*expires == NGX_HTTP_EXPIRES_DAILY
&& *expires_time > 24 * 60 * 60)
{
*err = "daily time value must be less than 24 hours";
return NGX_ERROR;
}
if (minus) {
*expires_time = - *expires_time;
}
return NGX_OK;
}
static ngx_int_t
ngx_http_add_header(ngx_http_request_t *r, ngx_http_header_val_t *hv,
ngx_str_t *value)
{
ngx_table_elt_t *h;
if (value->len) {
h = ngx_list_push(&r->headers_out.headers);
if (h == NULL) {
return NGX_ERROR;
}
h->hash = 1;
h->key = hv->key;
h->value = *value;
}
return NGX_OK;
}
static ngx_int_t
ngx_http_add_multi_header_lines(ngx_http_request_t *r,
ngx_http_header_val_t *hv, ngx_str_t *value)
{
ngx_array_t *pa;
ngx_table_elt_t *h, **ph;
if (value->len == 0) {
return NGX_OK;
}
pa = (ngx_array_t *) ((char *) &r->headers_out + hv->offset);
if (pa->elts == NULL) {
if (ngx_array_init(pa, r->pool, 1, sizeof(ngx_table_elt_t *)) != NGX_OK)
{
return NGX_ERROR;
}
}
h = ngx_list_push(&r->headers_out.headers);
if (h == NULL) {
return NGX_ERROR;
}
h->hash = 1;
h->key = hv->key;
h->value = *value;
ph = ngx_array_push(pa);
if (ph == NULL) {
return NGX_ERROR;
}
*ph = h;
return NGX_OK;
}
static ngx_int_t
ngx_http_set_last_modified(ngx_http_request_t *r, ngx_http_header_val_t *hv,
ngx_str_t *value)
{
if (ngx_http_set_response_header(r, hv, value) != NGX_OK) {
return NGX_ERROR;
}
r->headers_out.last_modified_time =
(value->len) ? ngx_parse_http_time(value->data, value->len) : -1;
return NGX_OK;
}
static ngx_int_t
ngx_http_set_response_header(ngx_http_request_t *r, ngx_http_header_val_t *hv,
ngx_str_t *value)
{
ngx_table_elt_t *h, **old;
old = (ngx_table_elt_t **) ((char *) &r->headers_out + hv->offset);
if (value->len == 0) {
if (*old) {
(*old)->hash = 0;
*old = NULL;
}
return NGX_OK;
}
if (*old) {
h = *old;
} else {
h = ngx_list_push(&r->headers_out.headers);
if (h == NULL) {
return NGX_ERROR;
}
*old = h;
}
h->hash = 1;
h->key = hv->key;
h->value = *value;
return NGX_OK;
}
static void *
ngx_http_headers_create_conf(ngx_conf_t *cf)
{
ngx_http_headers_conf_t *conf;
conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_headers_conf_t));
if (conf == NULL) {
return NULL;
}
/*
* set by ngx_pcalloc():
*
* conf->headers = NULL;
* conf->trailers = NULL;
* conf->expires_time = 0;
* conf->expires_value = NULL;
*/
conf->expires = NGX_HTTP_EXPIRES_UNSET;
return conf;
}
static char *
ngx_http_headers_merge_conf(ngx_conf_t *cf, void *parent, void *child)
{
ngx_http_headers_conf_t *prev = parent;
ngx_http_headers_conf_t *conf = child;
if (conf->expires == NGX_HTTP_EXPIRES_UNSET) {
conf->expires = prev->expires;
conf->expires_time = prev->expires_time;
conf->expires_value = prev->expires_value;
if (conf->expires == NGX_HTTP_EXPIRES_UNSET) {
conf->expires = NGX_HTTP_EXPIRES_OFF;
}
}
if (conf->headers == NULL) {
conf->headers = prev->headers;
}
if (conf->trailers == NULL) {
conf->trailers = prev->trailers;
}
return NGX_CONF_OK;
}
static ngx_int_t
ngx_http_headers_filter_init(ngx_conf_t *cf)
{
ngx_http_next_header_filter = ngx_http_top_header_filter;
ngx_http_top_header_filter = ngx_http_headers_filter;
ngx_http_next_body_filter = ngx_http_top_body_filter;
ngx_http_top_body_filter = ngx_http_trailers_filter;
return NGX_OK;
}
static char *
ngx_http_headers_expires(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_headers_conf_t *hcf = conf;
char *err;
ngx_str_t *value;
ngx_int_t rc;
ngx_uint_t n;
ngx_http_complex_value_t cv;
ngx_http_compile_complex_value_t ccv;
if (hcf->expires != NGX_HTTP_EXPIRES_UNSET) {
return "is duplicate";
}
value = cf->args->elts;
if (cf->args->nelts == 2) {
hcf->expires = NGX_HTTP_EXPIRES_ACCESS;
n = 1;
} else { /* cf->args->nelts == 3 */
if (ngx_strcmp(value[1].data, "modified") != 0) {
return "invalid value";
}
hcf->expires = NGX_HTTP_EXPIRES_MODIFIED;
n = 2;
}
ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
ccv.cf = cf;
ccv.value = &value[n];
ccv.complex_value = &cv;
if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
return NGX_CONF_ERROR;
}
if (cv.lengths != NULL) {
hcf->expires_value = ngx_palloc(cf->pool,
sizeof(ngx_http_complex_value_t));
if (hcf->expires_value == NULL) {
return NGX_CONF_ERROR;
}
*hcf->expires_value = cv;
return NGX_CONF_OK;
}
rc = ngx_http_parse_expires(&value[n], &hcf->expires, &hcf->expires_time,
&err);
if (rc != NGX_OK) {
return err;
}
return NGX_CONF_OK;
}
static char *
ngx_http_headers_add(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_headers_conf_t *hcf = conf;
ngx_str_t *value;
ngx_uint_t i;
ngx_array_t **headers;
ngx_http_header_val_t *hv;
ngx_http_set_header_t *set;
ngx_http_compile_complex_value_t ccv;
value = cf->args->elts;
headers = (ngx_array_t **) ((char *) hcf + cmd->offset);
if (*headers == NULL) {
*headers = ngx_array_create(cf->pool, 1,
sizeof(ngx_http_header_val_t));
if (*headers == NULL) {
return NGX_CONF_ERROR;
}
}
hv = ngx_array_push(*headers);
if (hv == NULL) {
return NGX_CONF_ERROR;
}
hv->key = value[1];
hv->handler = NULL;
hv->offset = 0;
hv->always = 0;
if (headers == &hcf->headers) {
hv->handler = ngx_http_add_header;
set = ngx_http_set_headers;
for (i = 0; set[i].name.len; i++) {
if (ngx_strcasecmp(value[1].data, set[i].name.data) != 0) {
continue;
}
hv->offset = set[i].offset;
hv->handler = set[i].handler;
break;
}
}
if (value[2].len == 0) {
ngx_memzero(&hv->value, sizeof(ngx_http_complex_value_t));
} else {
ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
ccv.cf = cf;
ccv.value = &value[2];
ccv.complex_value = &hv->value;
if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
return NGX_CONF_ERROR;
}
}
if (cf->args->nelts == 3) {
return NGX_CONF_OK;
}
if (ngx_strcmp(value[3].data, "always") != 0) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid parameter \"%V\"", &value[3]);
return NGX_CONF_ERROR;
}
hv->always = 1;
return NGX_CONF_OK;
}
nginx-1.14.0/src/http/modules/ngx_http_image_filter_module.c 000644 001751 001751 00000123111 13265410474 025404 0 ustar 00mdounin mdounin 000000 000000
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#include
#include
#include
#include
#define NGX_HTTP_IMAGE_OFF 0
#define NGX_HTTP_IMAGE_TEST 1
#define NGX_HTTP_IMAGE_SIZE 2
#define NGX_HTTP_IMAGE_RESIZE 3
#define NGX_HTTP_IMAGE_CROP 4
#define NGX_HTTP_IMAGE_ROTATE 5
#define NGX_HTTP_IMAGE_START 0
#define NGX_HTTP_IMAGE_READ 1
#define NGX_HTTP_IMAGE_PROCESS 2
#define NGX_HTTP_IMAGE_PASS 3
#define NGX_HTTP_IMAGE_DONE 4
#define NGX_HTTP_IMAGE_NONE 0
#define NGX_HTTP_IMAGE_JPEG 1
#define NGX_HTTP_IMAGE_GIF 2
#define NGX_HTTP_IMAGE_PNG 3
#define NGX_HTTP_IMAGE_WEBP 4
#define NGX_HTTP_IMAGE_BUFFERED 0x08
typedef struct {
ngx_uint_t filter;
ngx_uint_t width;
ngx_uint_t height;
ngx_uint_t angle;
ngx_uint_t jpeg_quality;
ngx_uint_t webp_quality;
ngx_uint_t sharpen;
ngx_flag_t transparency;
ngx_flag_t interlace;
ngx_http_complex_value_t *wcv;
ngx_http_complex_value_t *hcv;
ngx_http_complex_value_t *acv;
ngx_http_complex_value_t *jqcv;
ngx_http_complex_value_t *wqcv;
ngx_http_complex_value_t *shcv;
size_t buffer_size;
} ngx_http_image_filter_conf_t;
typedef struct {
u_char *image;
u_char *last;
size_t length;
ngx_uint_t width;
ngx_uint_t height;
ngx_uint_t max_width;
ngx_uint_t max_height;
ngx_uint_t angle;
ngx_uint_t phase;
ngx_uint_t type;
ngx_uint_t force;
} ngx_http_image_filter_ctx_t;
static ngx_int_t ngx_http_image_send(ngx_http_request_t *r,
ngx_http_image_filter_ctx_t *ctx, ngx_chain_t *in);
static ngx_uint_t ngx_http_image_test(ngx_http_request_t *r, ngx_chain_t *in);
static ngx_int_t ngx_http_image_read(ngx_http_request_t *r, ngx_chain_t *in);
static ngx_buf_t *ngx_http_image_process(ngx_http_request_t *r);
static ngx_buf_t *ngx_http_image_json(ngx_http_request_t *r,
ngx_http_image_filter_ctx_t *ctx);
static ngx_buf_t *ngx_http_image_asis(ngx_http_request_t *r,
ngx_http_image_filter_ctx_t *ctx);
static void ngx_http_image_length(ngx_http_request_t *r, ngx_buf_t *b);
static ngx_int_t ngx_http_image_size(ngx_http_request_t *r,
ngx_http_image_filter_ctx_t *ctx);
static ngx_buf_t *ngx_http_image_resize(ngx_http_request_t *r,
ngx_http_image_filter_ctx_t *ctx);
static gdImagePtr ngx_http_image_source(ngx_http_request_t *r,
ngx_http_image_filter_ctx_t *ctx);
static gdImagePtr ngx_http_image_new(ngx_http_request_t *r, int w, int h,
int colors);
static u_char *ngx_http_image_out(ngx_http_request_t *r, ngx_uint_t type,
gdImagePtr img, int *size);
static void ngx_http_image_cleanup(void *data);
static ngx_uint_t ngx_http_image_filter_get_value(ngx_http_request_t *r,
ngx_http_complex_value_t *cv, ngx_uint_t v);
static ngx_uint_t ngx_http_image_filter_value(ngx_str_t *value);
static void *ngx_http_image_filter_create_conf(ngx_conf_t *cf);
static char *ngx_http_image_filter_merge_conf(ngx_conf_t *cf, void *parent,
void *child);
static char *ngx_http_image_filter(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static char *ngx_http_image_filter_jpeg_quality(ngx_conf_t *cf,
ngx_command_t *cmd, void *conf);
static char *ngx_http_image_filter_webp_quality(ngx_conf_t *cf,
ngx_command_t *cmd, void *conf);
static char *ngx_http_image_filter_sharpen(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static ngx_int_t ngx_http_image_filter_init(ngx_conf_t *cf);
static ngx_command_t ngx_http_image_filter_commands[] = {
{ ngx_string("image_filter"),
NGX_HTTP_LOC_CONF|NGX_CONF_TAKE123,
ngx_http_image_filter,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
{ ngx_string("image_filter_jpeg_quality"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_http_image_filter_jpeg_quality,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
{ ngx_string("image_filter_webp_quality"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_http_image_filter_webp_quality,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
{ ngx_string("image_filter_sharpen"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_http_image_filter_sharpen,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
{ ngx_string("image_filter_transparency"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_image_filter_conf_t, transparency),
NULL },
{ ngx_string("image_filter_interlace"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_image_filter_conf_t, interlace),
NULL },
{ ngx_string("image_filter_buffer"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_size_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_image_filter_conf_t, buffer_size),
NULL },
ngx_null_command
};
static ngx_http_module_t ngx_http_image_filter_module_ctx = {
NULL, /* preconfiguration */
ngx_http_image_filter_init, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
ngx_http_image_filter_create_conf, /* create location configuration */
ngx_http_image_filter_merge_conf /* merge location configuration */
};
ngx_module_t ngx_http_image_filter_module = {
NGX_MODULE_V1,
&ngx_http_image_filter_module_ctx, /* module context */
ngx_http_image_filter_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static ngx_http_output_header_filter_pt ngx_http_next_header_filter;
static ngx_http_output_body_filter_pt ngx_http_next_body_filter;
static ngx_str_t ngx_http_image_types[] = {
ngx_string("image/jpeg"),
ngx_string("image/gif"),
ngx_string("image/png"),
ngx_string("image/webp")
};
static ngx_int_t
ngx_http_image_header_filter(ngx_http_request_t *r)
{
off_t len;
ngx_http_image_filter_ctx_t *ctx;
ngx_http_image_filter_conf_t *conf;
if (r->headers_out.status == NGX_HTTP_NOT_MODIFIED) {
return ngx_http_next_header_filter(r);
}
ctx = ngx_http_get_module_ctx(r, ngx_http_image_filter_module);
if (ctx) {
ngx_http_set_ctx(r, NULL, ngx_http_image_filter_module);
return ngx_http_next_header_filter(r);
}
conf = ngx_http_get_module_loc_conf(r, ngx_http_image_filter_module);
if (conf->filter == NGX_HTTP_IMAGE_OFF) {
return ngx_http_next_header_filter(r);
}
if (r->headers_out.content_type.len
>= sizeof("multipart/x-mixed-replace") - 1
&& ngx_strncasecmp(r->headers_out.content_type.data,
(u_char *) "multipart/x-mixed-replace",
sizeof("multipart/x-mixed-replace") - 1)
== 0)
{
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"image filter: multipart/x-mixed-replace response");
return NGX_ERROR;
}
ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_image_filter_ctx_t));
if (ctx == NULL) {
return NGX_ERROR;
}
ngx_http_set_ctx(r, ctx, ngx_http_image_filter_module);
len = r->headers_out.content_length_n;
if (len != -1 && len > (off_t) conf->buffer_size) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"image filter: too big response: %O", len);
return NGX_HTTP_UNSUPPORTED_MEDIA_TYPE;
}
if (len == -1) {
ctx->length = conf->buffer_size;
} else {
ctx->length = (size_t) len;
}
if (r->headers_out.refresh) {
r->headers_out.refresh->hash = 0;
}
r->main_filter_need_in_memory = 1;
r->allow_ranges = 0;
return NGX_OK;
}
static ngx_int_t
ngx_http_image_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
ngx_int_t rc;
ngx_str_t *ct;
ngx_chain_t out;
ngx_http_image_filter_ctx_t *ctx;
ngx_http_image_filter_conf_t *conf;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "image filter");
if (in == NULL) {
return ngx_http_next_body_filter(r, in);
}
ctx = ngx_http_get_module_ctx(r, ngx_http_image_filter_module);
if (ctx == NULL) {
return ngx_http_next_body_filter(r, in);
}
switch (ctx->phase) {
case NGX_HTTP_IMAGE_START:
ctx->type = ngx_http_image_test(r, in);
conf = ngx_http_get_module_loc_conf(r, ngx_http_image_filter_module);
if (ctx->type == NGX_HTTP_IMAGE_NONE) {
if (conf->filter == NGX_HTTP_IMAGE_SIZE) {
out.buf = ngx_http_image_json(r, NULL);
if (out.buf) {
out.next = NULL;
ctx->phase = NGX_HTTP_IMAGE_DONE;
return ngx_http_image_send(r, ctx, &out);
}
}
return ngx_http_filter_finalize_request(r,
&ngx_http_image_filter_module,
NGX_HTTP_UNSUPPORTED_MEDIA_TYPE);
}
/* override content type */
ct = &ngx_http_image_types[ctx->type - 1];
r->headers_out.content_type_len = ct->len;
r->headers_out.content_type = *ct;
r->headers_out.content_type_lowcase = NULL;
if (conf->filter == NGX_HTTP_IMAGE_TEST) {
ctx->phase = NGX_HTTP_IMAGE_PASS;
return ngx_http_image_send(r, ctx, in);
}
ctx->phase = NGX_HTTP_IMAGE_READ;
/* fall through */
case NGX_HTTP_IMAGE_READ:
rc = ngx_http_image_read(r, in);
if (rc == NGX_AGAIN) {
return NGX_OK;
}
if (rc == NGX_ERROR) {
return ngx_http_filter_finalize_request(r,
&ngx_http_image_filter_module,
NGX_HTTP_UNSUPPORTED_MEDIA_TYPE);
}
/* fall through */
case NGX_HTTP_IMAGE_PROCESS:
out.buf = ngx_http_image_process(r);
if (out.buf == NULL) {
return ngx_http_filter_finalize_request(r,
&ngx_http_image_filter_module,
NGX_HTTP_UNSUPPORTED_MEDIA_TYPE);
}
out.next = NULL;
ctx->phase = NGX_HTTP_IMAGE_PASS;
return ngx_http_image_send(r, ctx, &out);
case NGX_HTTP_IMAGE_PASS:
return ngx_http_next_body_filter(r, in);
default: /* NGX_HTTP_IMAGE_DONE */
rc = ngx_http_next_body_filter(r, NULL);
/* NGX_ERROR resets any pending data */
return (rc == NGX_OK) ? NGX_ERROR : rc;
}
}
static ngx_int_t
ngx_http_image_send(ngx_http_request_t *r, ngx_http_image_filter_ctx_t *ctx,
ngx_chain_t *in)
{
ngx_int_t rc;
rc = ngx_http_next_header_filter(r);
if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
return NGX_ERROR;
}
rc = ngx_http_next_body_filter(r, in);
if (ctx->phase == NGX_HTTP_IMAGE_DONE) {
/* NGX_ERROR resets any pending data */
return (rc == NGX_OK) ? NGX_ERROR : rc;
}
return rc;
}
static ngx_uint_t
ngx_http_image_test(ngx_http_request_t *r, ngx_chain_t *in)
{
u_char *p;
p = in->buf->pos;
if (in->buf->last - p < 16) {
return NGX_HTTP_IMAGE_NONE;
}
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"image filter: \"%c%c\"", p[0], p[1]);
if (p[0] == 0xff && p[1] == 0xd8) {
/* JPEG */
return NGX_HTTP_IMAGE_JPEG;
} else if (p[0] == 'G' && p[1] == 'I' && p[2] == 'F' && p[3] == '8'
&& p[5] == 'a')
{
if (p[4] == '9' || p[4] == '7') {
/* GIF */
return NGX_HTTP_IMAGE_GIF;
}
} else if (p[0] == 0x89 && p[1] == 'P' && p[2] == 'N' && p[3] == 'G'
&& p[4] == 0x0d && p[5] == 0x0a && p[6] == 0x1a && p[7] == 0x0a)
{
/* PNG */
return NGX_HTTP_IMAGE_PNG;
} else if (p[0] == 'R' && p[1] == 'I' && p[2] == 'F' && p[3] == 'F'
&& p[8] == 'W' && p[9] == 'E' && p[10] == 'B' && p[11] == 'P')
{
/* WebP */
return NGX_HTTP_IMAGE_WEBP;
}
return NGX_HTTP_IMAGE_NONE;
}
static ngx_int_t
ngx_http_image_read(ngx_http_request_t *r, ngx_chain_t *in)
{
u_char *p;
size_t size, rest;
ngx_buf_t *b;
ngx_chain_t *cl;
ngx_http_image_filter_ctx_t *ctx;
ctx = ngx_http_get_module_ctx(r, ngx_http_image_filter_module);
if (ctx->image == NULL) {
ctx->image = ngx_palloc(r->pool, ctx->length);
if (ctx->image == NULL) {
return NGX_ERROR;
}
ctx->last = ctx->image;
}
p = ctx->last;
for (cl = in; cl; cl = cl->next) {
b = cl->buf;
size = b->last - b->pos;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"image buf: %uz", size);
rest = ctx->image + ctx->length - p;
if (size > rest) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"image filter: too big response");
return NGX_ERROR;
}
p = ngx_cpymem(p, b->pos, size);
b->pos += size;
if (b->last_buf) {
ctx->last = p;
return NGX_OK;
}
}
ctx->last = p;
r->connection->buffered |= NGX_HTTP_IMAGE_BUFFERED;
return NGX_AGAIN;
}
static ngx_buf_t *
ngx_http_image_process(ngx_http_request_t *r)
{
ngx_int_t rc;
ngx_http_image_filter_ctx_t *ctx;
ngx_http_image_filter_conf_t *conf;
r->connection->buffered &= ~NGX_HTTP_IMAGE_BUFFERED;
ctx = ngx_http_get_module_ctx(r, ngx_http_image_filter_module);
rc = ngx_http_image_size(r, ctx);
conf = ngx_http_get_module_loc_conf(r, ngx_http_image_filter_module);
if (conf->filter == NGX_HTTP_IMAGE_SIZE) {
return ngx_http_image_json(r, rc == NGX_OK ? ctx : NULL);
}
ctx->angle = ngx_http_image_filter_get_value(r, conf->acv, conf->angle);
if (conf->filter == NGX_HTTP_IMAGE_ROTATE) {
if (ctx->angle != 90 && ctx->angle != 180 && ctx->angle != 270) {
return NULL;
}
return ngx_http_image_resize(r, ctx);
}
ctx->max_width = ngx_http_image_filter_get_value(r, conf->wcv, conf->width);
if (ctx->max_width == 0) {
return NULL;
}
ctx->max_height = ngx_http_image_filter_get_value(r, conf->hcv,
conf->height);
if (ctx->max_height == 0) {
return NULL;
}
if (rc == NGX_OK
&& ctx->width <= ctx->max_width
&& ctx->height <= ctx->max_height
&& ctx->angle == 0
&& !ctx->force)
{
return ngx_http_image_asis(r, ctx);
}
return ngx_http_image_resize(r, ctx);
}
static ngx_buf_t *
ngx_http_image_json(ngx_http_request_t *r, ngx_http_image_filter_ctx_t *ctx)
{
size_t len;
ngx_buf_t *b;
b = ngx_calloc_buf(r->pool);
if (b == NULL) {
return NULL;
}
b->memory = 1;
b->last_buf = 1;
ngx_http_clean_header(r);
r->headers_out.status = NGX_HTTP_OK;
r->headers_out.content_type_len = sizeof("application/json") - 1;
ngx_str_set(&r->headers_out.content_type, "application/json");
r->headers_out.content_type_lowcase = NULL;
if (ctx == NULL) {
b->pos = (u_char *) "{}" CRLF;
b->last = b->pos + sizeof("{}" CRLF) - 1;
ngx_http_image_length(r, b);
return b;
}
len = sizeof("{ \"img\" : "
"{ \"width\": , \"height\": , \"type\": \"jpeg\" } }" CRLF) - 1
+ 2 * NGX_SIZE_T_LEN;
b->pos = ngx_pnalloc(r->pool, len);
if (b->pos == NULL) {
return NULL;
}
b->last = ngx_sprintf(b->pos,
"{ \"img\" : "
"{ \"width\": %uz,"
" \"height\": %uz,"
" \"type\": \"%s\" } }" CRLF,
ctx->width, ctx->height,
ngx_http_image_types[ctx->type - 1].data + 6);
ngx_http_image_length(r, b);
return b;
}
static ngx_buf_t *
ngx_http_image_asis(ngx_http_request_t *r, ngx_http_image_filter_ctx_t *ctx)
{
ngx_buf_t *b;
b = ngx_calloc_buf(r->pool);
if (b == NULL) {
return NULL;
}
b->pos = ctx->image;
b->last = ctx->last;
b->memory = 1;
b->last_buf = 1;
ngx_http_image_length(r, b);
return b;
}
static void
ngx_http_image_length(ngx_http_request_t *r, ngx_buf_t *b)
{
r->headers_out.content_length_n = b->last - b->pos;
if (r->headers_out.content_length) {
r->headers_out.content_length->hash = 0;
}
r->headers_out.content_length = NULL;
}
static ngx_int_t
ngx_http_image_size(ngx_http_request_t *r, ngx_http_image_filter_ctx_t *ctx)
{
u_char *p, *last;
size_t len, app;
ngx_uint_t width, height;
p = ctx->image;
switch (ctx->type) {
case NGX_HTTP_IMAGE_JPEG:
p += 2;
last = ctx->image + ctx->length - 10;
width = 0;
height = 0;
app = 0;
while (p < last) {
if (p[0] == 0xff && p[1] != 0xff) {
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"JPEG: %02xd %02xd", p[0], p[1]);
p++;
if ((*p == 0xc0 || *p == 0xc1 || *p == 0xc2 || *p == 0xc3
|| *p == 0xc9 || *p == 0xca || *p == 0xcb)
&& (width == 0 || height == 0))
{
width = p[6] * 256 + p[7];
height = p[4] * 256 + p[5];
}
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"JPEG: %02xd %02xd", p[1], p[2]);
len = p[1] * 256 + p[2];
if (*p >= 0xe1 && *p <= 0xef) {
/* application data, e.g., EXIF, Adobe XMP, etc. */
app += len;
}
p += len;
continue;
}
p++;
}
if (width == 0 || height == 0) {
return NGX_DECLINED;
}
if (ctx->length / 20 < app) {
/* force conversion if application data consume more than 5% */
ctx->force = 1;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"app data size: %uz", app);
}
break;
case NGX_HTTP_IMAGE_GIF:
if (ctx->length < 10) {
return NGX_DECLINED;
}
width = p[7] * 256 + p[6];
height = p[9] * 256 + p[8];
break;
case NGX_HTTP_IMAGE_PNG:
if (ctx->length < 24) {
return NGX_DECLINED;
}
width = p[18] * 256 + p[19];
height = p[22] * 256 + p[23];
break;
case NGX_HTTP_IMAGE_WEBP:
if (ctx->length < 30) {
return NGX_DECLINED;
}
if (p[12] != 'V' || p[13] != 'P' || p[14] != '8') {
return NGX_DECLINED;
}
switch (p[15]) {
case ' ':
if (p[20] & 1) {
/* not a key frame */
return NGX_DECLINED;
}
if (p[23] != 0x9d || p[24] != 0x01 || p[25] != 0x2a) {
/* invalid start code */
return NGX_DECLINED;
}
width = (p[26] | p[27] << 8) & 0x3fff;
height = (p[28] | p[29] << 8) & 0x3fff;
break;
case 'L':
if (p[20] != 0x2f) {
/* invalid signature */
return NGX_DECLINED;
}
width = ((p[21] | p[22] << 8) & 0x3fff) + 1;
height = ((p[22] >> 6 | p[23] << 2 | p[24] << 10) & 0x3fff) + 1;
break;
case 'X':
width = (p[24] | p[25] << 8 | p[26] << 16) + 1;
height = (p[27] | p[28] << 8 | p[29] << 16) + 1;
break;
default:
return NGX_DECLINED;
}
break;
default:
return NGX_DECLINED;
}
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"image size: %d x %d", (int) width, (int) height);
ctx->width = width;
ctx->height = height;
return NGX_OK;
}
static ngx_buf_t *
ngx_http_image_resize(ngx_http_request_t *r, ngx_http_image_filter_ctx_t *ctx)
{
int sx, sy, dx, dy, ox, oy, ax, ay, size,
colors, palette, transparent, sharpen,
red, green, blue, t;
u_char *out;
ngx_buf_t *b;
ngx_uint_t resize;
gdImagePtr src, dst;
ngx_pool_cleanup_t *cln;
ngx_http_image_filter_conf_t *conf;
src = ngx_http_image_source(r, ctx);
if (src == NULL) {
return NULL;
}
sx = gdImageSX(src);
sy = gdImageSY(src);
conf = ngx_http_get_module_loc_conf(r, ngx_http_image_filter_module);
if (!ctx->force
&& ctx->angle == 0
&& (ngx_uint_t) sx <= ctx->max_width
&& (ngx_uint_t) sy <= ctx->max_height)
{
gdImageDestroy(src);
return ngx_http_image_asis(r, ctx);
}
colors = gdImageColorsTotal(src);
if (colors && conf->transparency) {
transparent = gdImageGetTransparent(src);
if (transparent != -1) {
palette = colors;
red = gdImageRed(src, transparent);
green = gdImageGreen(src, transparent);
blue = gdImageBlue(src, transparent);
goto transparent;
}
}
palette = 0;
transparent = -1;
red = 0;
green = 0;
blue = 0;
transparent:
gdImageColorTransparent(src, -1);
dx = sx;
dy = sy;
if (conf->filter == NGX_HTTP_IMAGE_RESIZE) {
if ((ngx_uint_t) dx > ctx->max_width) {
dy = dy * ctx->max_width / dx;
dy = dy ? dy : 1;
dx = ctx->max_width;
}
if ((ngx_uint_t) dy > ctx->max_height) {
dx = dx * ctx->max_height / dy;
dx = dx ? dx : 1;
dy = ctx->max_height;
}
resize = 1;
} else if (conf->filter == NGX_HTTP_IMAGE_ROTATE) {
resize = 0;
} else { /* NGX_HTTP_IMAGE_CROP */
resize = 0;
if ((double) dx / dy < (double) ctx->max_width / ctx->max_height) {
if ((ngx_uint_t) dx > ctx->max_width) {
dy = dy * ctx->max_width / dx;
dy = dy ? dy : 1;
dx = ctx->max_width;
resize = 1;
}
} else {
if ((ngx_uint_t) dy > ctx->max_height) {
dx = dx * ctx->max_height / dy;
dx = dx ? dx : 1;
dy = ctx->max_height;
resize = 1;
}
}
}
if (resize) {
dst = ngx_http_image_new(r, dx, dy, palette);
if (dst == NULL) {
gdImageDestroy(src);
return NULL;
}
if (colors == 0) {
gdImageSaveAlpha(dst, 1);
gdImageAlphaBlending(dst, 0);
}
gdImageCopyResampled(dst, src, 0, 0, 0, 0, dx, dy, sx, sy);
if (colors) {
gdImageTrueColorToPalette(dst, 1, 256);
}
gdImageDestroy(src);
} else {
dst = src;
}
if (ctx->angle) {
src = dst;
ax = (dx % 2 == 0) ? 1 : 0;
ay = (dy % 2 == 0) ? 1 : 0;
switch (ctx->angle) {
case 90:
case 270:
dst = ngx_http_image_new(r, dy, dx, palette);
if (dst == NULL) {
gdImageDestroy(src);
return NULL;
}
if (ctx->angle == 90) {
ox = dy / 2 + ay;
oy = dx / 2 - ax;
} else {
ox = dy / 2 - ay;
oy = dx / 2 + ax;
}
gdImageCopyRotated(dst, src, ox, oy, 0, 0,
dx + ax, dy + ay, ctx->angle);
gdImageDestroy(src);
t = dx;
dx = dy;
dy = t;
break;
case 180:
dst = ngx_http_image_new(r, dx, dy, palette);
if (dst == NULL) {
gdImageDestroy(src);
return NULL;
}
gdImageCopyRotated(dst, src, dx / 2 - ax, dy / 2 - ay, 0, 0,
dx + ax, dy + ay, ctx->angle);
gdImageDestroy(src);
break;
}
}
if (conf->filter == NGX_HTTP_IMAGE_CROP) {
src = dst;
if ((ngx_uint_t) dx > ctx->max_width) {
ox = dx - ctx->max_width;
} else {
ox = 0;
}
if ((ngx_uint_t) dy > ctx->max_height) {
oy = dy - ctx->max_height;
} else {
oy = 0;
}
if (ox || oy) {
dst = ngx_http_image_new(r, dx - ox, dy - oy, colors);
if (dst == NULL) {
gdImageDestroy(src);
return NULL;
}
ox /= 2;
oy /= 2;
ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"image crop: %d x %d @ %d x %d",
dx, dy, ox, oy);
if (colors == 0) {
gdImageSaveAlpha(dst, 1);
gdImageAlphaBlending(dst, 0);
}
gdImageCopy(dst, src, 0, 0, ox, oy, dx - ox, dy - oy);
if (colors) {
gdImageTrueColorToPalette(dst, 1, 256);
}
gdImageDestroy(src);
}
}
if (transparent != -1 && colors) {
gdImageColorTransparent(dst, gdImageColorExact(dst, red, green, blue));
}
sharpen = ngx_http_image_filter_get_value(r, conf->shcv, conf->sharpen);
if (sharpen > 0) {
gdImageSharpen(dst, sharpen);
}
gdImageInterlace(dst, (int) conf->interlace);
out = ngx_http_image_out(r, ctx->type, dst, &size);
ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"image: %d x %d %d", sx, sy, colors);
gdImageDestroy(dst);
ngx_pfree(r->pool, ctx->image);
if (out == NULL) {
return NULL;
}
cln = ngx_pool_cleanup_add(r->pool, 0);
if (cln == NULL) {
gdFree(out);
return NULL;
}
b = ngx_calloc_buf(r->pool);
if (b == NULL) {
gdFree(out);
return NULL;
}
cln->handler = ngx_http_image_cleanup;
cln->data = out;
b->pos = out;
b->last = out + size;
b->memory = 1;
b->last_buf = 1;
ngx_http_image_length(r, b);
ngx_http_weak_etag(r);
return b;
}
static gdImagePtr
ngx_http_image_source(ngx_http_request_t *r, ngx_http_image_filter_ctx_t *ctx)
{
char *failed;
gdImagePtr img;
img = NULL;
switch (ctx->type) {
case NGX_HTTP_IMAGE_JPEG:
img = gdImageCreateFromJpegPtr(ctx->length, ctx->image);
failed = "gdImageCreateFromJpegPtr() failed";
break;
case NGX_HTTP_IMAGE_GIF:
img = gdImageCreateFromGifPtr(ctx->length, ctx->image);
failed = "gdImageCreateFromGifPtr() failed";
break;
case NGX_HTTP_IMAGE_PNG:
img = gdImageCreateFromPngPtr(ctx->length, ctx->image);
failed = "gdImageCreateFromPngPtr() failed";
break;
case NGX_HTTP_IMAGE_WEBP:
#if (NGX_HAVE_GD_WEBP)
img = gdImageCreateFromWebpPtr(ctx->length, ctx->image);
failed = "gdImageCreateFromWebpPtr() failed";
#else
failed = "nginx was built without GD WebP support";
#endif
break;
default:
failed = "unknown image type";
break;
}
if (img == NULL) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, failed);
}
return img;
}
static gdImagePtr
ngx_http_image_new(ngx_http_request_t *r, int w, int h, int colors)
{
gdImagePtr img;
if (colors == 0) {
img = gdImageCreateTrueColor(w, h);
if (img == NULL) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"gdImageCreateTrueColor() failed");
return NULL;
}
} else {
img = gdImageCreate(w, h);
if (img == NULL) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"gdImageCreate() failed");
return NULL;
}
}
return img;
}
static u_char *
ngx_http_image_out(ngx_http_request_t *r, ngx_uint_t type, gdImagePtr img,
int *size)
{
char *failed;
u_char *out;
ngx_int_t q;
ngx_http_image_filter_conf_t *conf;
out = NULL;
switch (type) {
case NGX_HTTP_IMAGE_JPEG:
conf = ngx_http_get_module_loc_conf(r, ngx_http_image_filter_module);
q = ngx_http_image_filter_get_value(r, conf->jqcv, conf->jpeg_quality);
if (q <= 0) {
return NULL;
}
out = gdImageJpegPtr(img, size, q);
failed = "gdImageJpegPtr() failed";
break;
case NGX_HTTP_IMAGE_GIF:
out = gdImageGifPtr(img, size);
failed = "gdImageGifPtr() failed";
break;
case NGX_HTTP_IMAGE_PNG:
out = gdImagePngPtr(img, size);
failed = "gdImagePngPtr() failed";
break;
case NGX_HTTP_IMAGE_WEBP:
#if (NGX_HAVE_GD_WEBP)
conf = ngx_http_get_module_loc_conf(r, ngx_http_image_filter_module);
q = ngx_http_image_filter_get_value(r, conf->wqcv, conf->webp_quality);
if (q <= 0) {
return NULL;
}
out = gdImageWebpPtrEx(img, size, q);
failed = "gdImageWebpPtrEx() failed";
#else
failed = "nginx was built without GD WebP support";
#endif
break;
default:
failed = "unknown image type";
break;
}
if (out == NULL) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, failed);
}
return out;
}
static void
ngx_http_image_cleanup(void *data)
{
gdFree(data);
}
static ngx_uint_t
ngx_http_image_filter_get_value(ngx_http_request_t *r,
ngx_http_complex_value_t *cv, ngx_uint_t v)
{
ngx_str_t val;
if (cv == NULL) {
return v;
}
if (ngx_http_complex_value(r, cv, &val) != NGX_OK) {
return 0;
}
return ngx_http_image_filter_value(&val);
}
static ngx_uint_t
ngx_http_image_filter_value(ngx_str_t *value)
{
ngx_int_t n;
if (value->len == 1 && value->data[0] == '-') {
return (ngx_uint_t) -1;
}
n = ngx_atoi(value->data, value->len);
if (n > 0) {
return (ngx_uint_t) n;
}
return 0;
}
static void *
ngx_http_image_filter_create_conf(ngx_conf_t *cf)
{
ngx_http_image_filter_conf_t *conf;
conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_image_filter_conf_t));
if (conf == NULL) {
return NULL;
}
/*
* set by ngx_pcalloc():
*
* conf->width = 0;
* conf->height = 0;
* conf->angle = 0;
* conf->wcv = NULL;
* conf->hcv = NULL;
* conf->acv = NULL;
* conf->jqcv = NULL;
* conf->wqcv = NULL;
* conf->shcv = NULL;
*/
conf->filter = NGX_CONF_UNSET_UINT;
conf->jpeg_quality = NGX_CONF_UNSET_UINT;
conf->webp_quality = NGX_CONF_UNSET_UINT;
conf->sharpen = NGX_CONF_UNSET_UINT;
conf->transparency = NGX_CONF_UNSET;
conf->interlace = NGX_CONF_UNSET;
conf->buffer_size = NGX_CONF_UNSET_SIZE;
return conf;
}
static char *
ngx_http_image_filter_merge_conf(ngx_conf_t *cf, void *parent, void *child)
{
ngx_http_image_filter_conf_t *prev = parent;
ngx_http_image_filter_conf_t *conf = child;
if (conf->filter == NGX_CONF_UNSET_UINT) {
if (prev->filter == NGX_CONF_UNSET_UINT) {
conf->filter = NGX_HTTP_IMAGE_OFF;
} else {
conf->filter = prev->filter;
conf->width = prev->width;
conf->height = prev->height;
conf->angle = prev->angle;
conf->wcv = prev->wcv;
conf->hcv = prev->hcv;
conf->acv = prev->acv;
}
}
if (conf->jpeg_quality == NGX_CONF_UNSET_UINT) {
/* 75 is libjpeg default quality */
ngx_conf_merge_uint_value(conf->jpeg_quality, prev->jpeg_quality, 75);
if (conf->jqcv == NULL) {
conf->jqcv = prev->jqcv;
}
}
if (conf->webp_quality == NGX_CONF_UNSET_UINT) {
/* 80 is libwebp default quality */
ngx_conf_merge_uint_value(conf->webp_quality, prev->webp_quality, 80);
if (conf->wqcv == NULL) {
conf->wqcv = prev->wqcv;
}
}
if (conf->sharpen == NGX_CONF_UNSET_UINT) {
ngx_conf_merge_uint_value(conf->sharpen, prev->sharpen, 0);
if (conf->shcv == NULL) {
conf->shcv = prev->shcv;
}
}
ngx_conf_merge_value(conf->transparency, prev->transparency, 1);
ngx_conf_merge_value(conf->interlace, prev->interlace, 0);
ngx_conf_merge_size_value(conf->buffer_size, prev->buffer_size,
1 * 1024 * 1024);
return NGX_CONF_OK;
}
static char *
ngx_http_image_filter(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_image_filter_conf_t *imcf = conf;
ngx_str_t *value;
ngx_int_t n;
ngx_uint_t i;
ngx_http_complex_value_t cv;
ngx_http_compile_complex_value_t ccv;
value = cf->args->elts;
i = 1;
if (cf->args->nelts == 2) {
if (ngx_strcmp(value[i].data, "off") == 0) {
imcf->filter = NGX_HTTP_IMAGE_OFF;
} else if (ngx_strcmp(value[i].data, "test") == 0) {
imcf->filter = NGX_HTTP_IMAGE_TEST;
} else if (ngx_strcmp(value[i].data, "size") == 0) {
imcf->filter = NGX_HTTP_IMAGE_SIZE;
} else {
goto failed;
}
return NGX_CONF_OK;
} else if (cf->args->nelts == 3) {
if (ngx_strcmp(value[i].data, "rotate") == 0) {
if (imcf->filter != NGX_HTTP_IMAGE_RESIZE
&& imcf->filter != NGX_HTTP_IMAGE_CROP)
{
imcf->filter = NGX_HTTP_IMAGE_ROTATE;
}
ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
ccv.cf = cf;
ccv.value = &value[++i];
ccv.complex_value = &cv;
if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
return NGX_CONF_ERROR;
}
if (cv.lengths == NULL) {
n = ngx_http_image_filter_value(&value[i]);
if (n != 90 && n != 180 && n != 270) {
goto failed;
}
imcf->angle = (ngx_uint_t) n;
} else {
imcf->acv = ngx_palloc(cf->pool,
sizeof(ngx_http_complex_value_t));
if (imcf->acv == NULL) {
return NGX_CONF_ERROR;
}
*imcf->acv = cv;
}
return NGX_CONF_OK;
} else {
goto failed;
}
}
if (ngx_strcmp(value[i].data, "resize") == 0) {
imcf->filter = NGX_HTTP_IMAGE_RESIZE;
} else if (ngx_strcmp(value[i].data, "crop") == 0) {
imcf->filter = NGX_HTTP_IMAGE_CROP;
} else {
goto failed;
}
ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
ccv.cf = cf;
ccv.value = &value[++i];
ccv.complex_value = &cv;
if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
return NGX_CONF_ERROR;
}
if (cv.lengths == NULL) {
n = ngx_http_image_filter_value(&value[i]);
if (n == 0) {
goto failed;
}
imcf->width = (ngx_uint_t) n;
} else {
imcf->wcv = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t));
if (imcf->wcv == NULL) {
return NGX_CONF_ERROR;
}
*imcf->wcv = cv;
}
ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
ccv.cf = cf;
ccv.value = &value[++i];
ccv.complex_value = &cv;
if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
return NGX_CONF_ERROR;
}
if (cv.lengths == NULL) {
n = ngx_http_image_filter_value(&value[i]);
if (n == 0) {
goto failed;
}
imcf->height = (ngx_uint_t) n;
} else {
imcf->hcv = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t));
if (imcf->hcv == NULL) {
return NGX_CONF_ERROR;
}
*imcf->hcv = cv;
}
return NGX_CONF_OK;
failed:
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameter \"%V\"",
&value[i]);
return NGX_CONF_ERROR;
}
static char *
ngx_http_image_filter_jpeg_quality(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf)
{
ngx_http_image_filter_conf_t *imcf = conf;
ngx_str_t *value;
ngx_int_t n;
ngx_http_complex_value_t cv;
ngx_http_compile_complex_value_t ccv;
value = cf->args->elts;
ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
ccv.cf = cf;
ccv.value = &value[1];
ccv.complex_value = &cv;
if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
return NGX_CONF_ERROR;
}
if (cv.lengths == NULL) {
n = ngx_http_image_filter_value(&value[1]);
if (n <= 0) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid value \"%V\"", &value[1]);
return NGX_CONF_ERROR;
}
imcf->jpeg_quality = (ngx_uint_t) n;
} else {
imcf->jqcv = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t));
if (imcf->jqcv == NULL) {
return NGX_CONF_ERROR;
}
*imcf->jqcv = cv;
}
return NGX_CONF_OK;
}
static char *
ngx_http_image_filter_webp_quality(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf)
{
ngx_http_image_filter_conf_t *imcf = conf;
ngx_str_t *value;
ngx_int_t n;
ngx_http_complex_value_t cv;
ngx_http_compile_complex_value_t ccv;
value = cf->args->elts;
ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
ccv.cf = cf;
ccv.value = &value[1];
ccv.complex_value = &cv;
if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
return NGX_CONF_ERROR;
}
if (cv.lengths == NULL) {
n = ngx_http_image_filter_value(&value[1]);
if (n <= 0) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid value \"%V\"", &value[1]);
return NGX_CONF_ERROR;
}
imcf->webp_quality = (ngx_uint_t) n;
} else {
imcf->wqcv = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t));
if (imcf->wqcv == NULL) {
return NGX_CONF_ERROR;
}
*imcf->wqcv = cv;
}
return NGX_CONF_OK;
}
static char *
ngx_http_image_filter_sharpen(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf)
{
ngx_http_image_filter_conf_t *imcf = conf;
ngx_str_t *value;
ngx_int_t n;
ngx_http_complex_value_t cv;
ngx_http_compile_complex_value_t ccv;
value = cf->args->elts;
ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
ccv.cf = cf;
ccv.value = &value[1];
ccv.complex_value = &cv;
if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
return NGX_CONF_ERROR;
}
if (cv.lengths == NULL) {
n = ngx_http_image_filter_value(&value[1]);
if (n < 0) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid value \"%V\"", &value[1]);
return NGX_CONF_ERROR;
}
imcf->sharpen = (ngx_uint_t) n;
} else {
imcf->shcv = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t));
if (imcf->shcv == NULL) {
return NGX_CONF_ERROR;
}
*imcf->shcv = cv;
}
return NGX_CONF_OK;
}
static ngx_int_t
ngx_http_image_filter_init(ngx_conf_t *cf)
{
ngx_http_next_header_filter = ngx_http_top_header_filter;
ngx_http_top_header_filter = ngx_http_image_header_filter;
ngx_http_next_body_filter = ngx_http_top_body_filter;
ngx_http_top_body_filter = ngx_http_image_body_filter;
return NGX_OK;
}
nginx-1.14.0/src/http/modules/ngx_http_index_module.c 000644 001751 001751 00000035247 13265410474 024100 0 ustar 00mdounin mdounin 000000 000000
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#include
#include
#include
typedef struct {
ngx_str_t name;
ngx_array_t *lengths;
ngx_array_t *values;
} ngx_http_index_t;
typedef struct {
ngx_array_t *indices; /* array of ngx_http_index_t */
size_t max_index_len;
} ngx_http_index_loc_conf_t;
#define NGX_HTTP_DEFAULT_INDEX "index.html"
static ngx_int_t ngx_http_index_test_dir(ngx_http_request_t *r,
ngx_http_core_loc_conf_t *clcf, u_char *path, u_char *last);
static ngx_int_t ngx_http_index_error(ngx_http_request_t *r,
ngx_http_core_loc_conf_t *clcf, u_char *file, ngx_err_t err);
static ngx_int_t ngx_http_index_init(ngx_conf_t *cf);
static void *ngx_http_index_create_loc_conf(ngx_conf_t *cf);
static char *ngx_http_index_merge_loc_conf(ngx_conf_t *cf,
void *parent, void *child);
static char *ngx_http_index_set_index(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static ngx_command_t ngx_http_index_commands[] = {
{ ngx_string("index"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
ngx_http_index_set_index,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
ngx_null_command
};
static ngx_http_module_t ngx_http_index_module_ctx = {
NULL, /* preconfiguration */
ngx_http_index_init, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
ngx_http_index_create_loc_conf, /* create location configuration */
ngx_http_index_merge_loc_conf /* merge location configuration */
};
ngx_module_t ngx_http_index_module = {
NGX_MODULE_V1,
&ngx_http_index_module_ctx, /* module context */
ngx_http_index_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
/*
* Try to open/test the first index file before the test of directory
* existence because valid requests should prevail over invalid ones.
* If open()/stat() of a file will fail then stat() of a directory
* should be faster because kernel may have already cached some data.
* Besides, Win32 may return ERROR_PATH_NOT_FOUND (NGX_ENOTDIR) at once.
* Unix has ENOTDIR error; however, it's less helpful than Win32's one:
* it only indicates that path points to a regular file, not a directory.
*/
static ngx_int_t
ngx_http_index_handler(ngx_http_request_t *r)
{
u_char *p, *name;
size_t len, root, reserve, allocated;
ngx_int_t rc;
ngx_str_t path, uri;
ngx_uint_t i, dir_tested;
ngx_http_index_t *index;
ngx_open_file_info_t of;
ngx_http_script_code_pt code;
ngx_http_script_engine_t e;
ngx_http_core_loc_conf_t *clcf;
ngx_http_index_loc_conf_t *ilcf;
ngx_http_script_len_code_pt lcode;
if (r->uri.data[r->uri.len - 1] != '/') {
return NGX_DECLINED;
}
if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD|NGX_HTTP_POST))) {
return NGX_DECLINED;
}
ilcf = ngx_http_get_module_loc_conf(r, ngx_http_index_module);
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
allocated = 0;
root = 0;
dir_tested = 0;
name = NULL;
/* suppress MSVC warning */
path.data = NULL;
index = ilcf->indices->elts;
for (i = 0; i < ilcf->indices->nelts; i++) {
if (index[i].lengths == NULL) {
if (index[i].name.data[0] == '/') {
return ngx_http_internal_redirect(r, &index[i].name, &r->args);
}
reserve = ilcf->max_index_len;
len = index[i].name.len;
} else {
ngx_memzero(&e, sizeof(ngx_http_script_engine_t));
e.ip = index[i].lengths->elts;
e.request = r;
e.flushed = 1;
/* 1 is for terminating '\0' as in static names */
len = 1;
while (*(uintptr_t *) e.ip) {
lcode = *(ngx_http_script_len_code_pt *) e.ip;
len += lcode(&e);
}
/* 16 bytes are preallocation */
reserve = len + 16;
}
if (reserve > allocated) {
name = ngx_http_map_uri_to_path(r, &path, &root, reserve);
if (name == NULL) {
return NGX_ERROR;
}
allocated = path.data + path.len - name;
}
if (index[i].values == NULL) {
/* index[i].name.len includes the terminating '\0' */
ngx_memcpy(name, index[i].name.data, index[i].name.len);
path.len = (name + index[i].name.len - 1) - path.data;
} else {
e.ip = index[i].values->elts;
e.pos = name;
while (*(uintptr_t *) e.ip) {
code = *(ngx_http_script_code_pt *) e.ip;
code((ngx_http_script_engine_t *) &e);
}
if (*name == '/') {
uri.len = len - 1;
uri.data = name;
return ngx_http_internal_redirect(r, &uri, &r->args);
}
path.len = e.pos - path.data;
*e.pos = '\0';
}
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"open index \"%V\"", &path);
ngx_memzero(&of, sizeof(ngx_open_file_info_t));
of.read_ahead = clcf->read_ahead;
of.directio = clcf->directio;
of.valid = clcf->open_file_cache_valid;
of.min_uses = clcf->open_file_cache_min_uses;
of.test_only = 1;
of.errors = clcf->open_file_cache_errors;
of.events = clcf->open_file_cache_events;
if (ngx_http_set_disable_symlinks(r, clcf, &path, &of) != NGX_OK) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)
!= NGX_OK)
{
if (of.err == 0) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, of.err,
"%s \"%s\" failed", of.failed, path.data);
#if (NGX_HAVE_OPENAT)
if (of.err == NGX_EMLINK
|| of.err == NGX_ELOOP)
{
return NGX_HTTP_FORBIDDEN;
}
#endif
if (of.err == NGX_ENOTDIR
|| of.err == NGX_ENAMETOOLONG
|| of.err == NGX_EACCES)
{
return ngx_http_index_error(r, clcf, path.data, of.err);
}
if (!dir_tested) {
rc = ngx_http_index_test_dir(r, clcf, path.data, name - 1);
if (rc != NGX_OK) {
return rc;
}
dir_tested = 1;
}
if (of.err == NGX_ENOENT) {
continue;
}
ngx_log_error(NGX_LOG_CRIT, r->connection->log, of.err,
"%s \"%s\" failed", of.failed, path.data);
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
uri.len = r->uri.len + len - 1;
if (!clcf->alias) {
uri.data = path.data + root;
} else {
uri.data = ngx_pnalloc(r->pool, uri.len);
if (uri.data == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
p = ngx_copy(uri.data, r->uri.data, r->uri.len);
ngx_memcpy(p, name, len - 1);
}
return ngx_http_internal_redirect(r, &uri, &r->args);
}
return NGX_DECLINED;
}
static ngx_int_t
ngx_http_index_test_dir(ngx_http_request_t *r, ngx_http_core_loc_conf_t *clcf,
u_char *path, u_char *last)
{
u_char c;
ngx_str_t dir;
ngx_open_file_info_t of;
c = *last;
if (c != '/' || path == last) {
/* "alias" without trailing slash */
c = *(++last);
}
*last = '\0';
dir.len = last - path;
dir.data = path;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http index check dir: \"%V\"", &dir);
ngx_memzero(&of, sizeof(ngx_open_file_info_t));
of.test_dir = 1;
of.test_only = 1;
of.valid = clcf->open_file_cache_valid;
of.errors = clcf->open_file_cache_errors;
if (ngx_http_set_disable_symlinks(r, clcf, &dir, &of) != NGX_OK) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
if (ngx_open_cached_file(clcf->open_file_cache, &dir, &of, r->pool)
!= NGX_OK)
{
if (of.err) {
#if (NGX_HAVE_OPENAT)
if (of.err == NGX_EMLINK
|| of.err == NGX_ELOOP)
{
return NGX_HTTP_FORBIDDEN;
}
#endif
if (of.err == NGX_ENOENT) {
*last = c;
return ngx_http_index_error(r, clcf, dir.data, NGX_ENOENT);
}
if (of.err == NGX_EACCES) {
*last = c;
/*
* ngx_http_index_test_dir() is called after the first index
* file testing has returned an error distinct from NGX_EACCES.
* This means that directory searching is allowed.
*/
return NGX_OK;
}
ngx_log_error(NGX_LOG_CRIT, r->connection->log, of.err,
"%s \"%s\" failed", of.failed, dir.data);
}
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
*last = c;
if (of.is_dir) {
return NGX_OK;
}
ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
"\"%s\" is not a directory", dir.data);
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
static ngx_int_t
ngx_http_index_error(ngx_http_request_t *r, ngx_http_core_loc_conf_t *clcf,
u_char *file, ngx_err_t err)
{
if (err == NGX_EACCES) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, err,
"\"%s\" is forbidden", file);
return NGX_HTTP_FORBIDDEN;
}
if (clcf->log_not_found) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, err,
"\"%s\" is not found", file);
}
return NGX_HTTP_NOT_FOUND;
}
static void *
ngx_http_index_create_loc_conf(ngx_conf_t *cf)
{
ngx_http_index_loc_conf_t *conf;
conf = ngx_palloc(cf->pool, sizeof(ngx_http_index_loc_conf_t));
if (conf == NULL) {
return NULL;
}
conf->indices = NULL;
conf->max_index_len = 0;
return conf;
}
static char *
ngx_http_index_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
{
ngx_http_index_loc_conf_t *prev = parent;
ngx_http_index_loc_conf_t *conf = child;
ngx_http_index_t *index;
if (conf->indices == NULL) {
conf->indices = prev->indices;
conf->max_index_len = prev->max_index_len;
}
if (conf->indices == NULL) {
conf->indices = ngx_array_create(cf->pool, 1, sizeof(ngx_http_index_t));
if (conf->indices == NULL) {
return NGX_CONF_ERROR;
}
index = ngx_array_push(conf->indices);
if (index == NULL) {
return NGX_CONF_ERROR;
}
index->name.len = sizeof(NGX_HTTP_DEFAULT_INDEX);
index->name.data = (u_char *) NGX_HTTP_DEFAULT_INDEX;
index->lengths = NULL;
index->values = NULL;
conf->max_index_len = sizeof(NGX_HTTP_DEFAULT_INDEX);
return NGX_CONF_OK;
}
return NGX_CONF_OK;
}
static ngx_int_t
ngx_http_index_init(ngx_conf_t *cf)
{
ngx_http_handler_pt *h;
ngx_http_core_main_conf_t *cmcf;
cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers);
if (h == NULL) {
return NGX_ERROR;
}
*h = ngx_http_index_handler;
return NGX_OK;
}
/* TODO: warn about duplicate indices */
static char *
ngx_http_index_set_index(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_index_loc_conf_t *ilcf = conf;
ngx_str_t *value;
ngx_uint_t i, n;
ngx_http_index_t *index;
ngx_http_script_compile_t sc;
if (ilcf->indices == NULL) {
ilcf->indices = ngx_array_create(cf->pool, 2, sizeof(ngx_http_index_t));
if (ilcf->indices == NULL) {
return NGX_CONF_ERROR;
}
}
value = cf->args->elts;
for (i = 1; i < cf->args->nelts; i++) {
if (value[i].data[0] == '/' && i != cf->args->nelts - 1) {
ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
"only the last index in \"index\" directive "
"should be absolute");
}
if (value[i].len == 0) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"index \"%V\" in \"index\" directive is invalid",
&value[1]);
return NGX_CONF_ERROR;
}
index = ngx_array_push(ilcf->indices);
if (index == NULL) {
return NGX_CONF_ERROR;
}
index->name.len = value[i].len;
index->name.data = value[i].data;
index->lengths = NULL;
index->values = NULL;
n = ngx_http_script_variables_count(&value[i]);
if (n == 0) {
if (ilcf->max_index_len < index->name.len) {
ilcf->max_index_len = index->name.len;
}
if (index->name.data[0] == '/') {
continue;
}
/* include the terminating '\0' to the length to use ngx_memcpy() */
index->name.len++;
continue;
}
ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
sc.cf = cf;
sc.source = &value[i];
sc.lengths = &index->lengths;
sc.values = &index->values;
sc.variables = n;
sc.complete_lengths = 1;
sc.complete_values = 1;
if (ngx_http_script_compile(&sc) != NGX_OK) {
return NGX_CONF_ERROR;
}
}
return NGX_CONF_OK;
}
nginx-1.14.0/src/http/modules/ngx_http_limit_conn_module.c 000644 001751 001751 00000043116 13265410474 025116 0 ustar 00mdounin mdounin 000000 000000
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#include
#include
#include
typedef struct {
u_char color;
u_char len;
u_short conn;
u_char data[1];
} ngx_http_limit_conn_node_t;
typedef struct {
ngx_shm_zone_t *shm_zone;
ngx_rbtree_node_t *node;
} ngx_http_limit_conn_cleanup_t;
typedef struct {
ngx_rbtree_t *rbtree;
ngx_http_complex_value_t key;
} ngx_http_limit_conn_ctx_t;
typedef struct {
ngx_shm_zone_t *shm_zone;
ngx_uint_t conn;
} ngx_http_limit_conn_limit_t;
typedef struct {
ngx_array_t limits;
ngx_uint_t log_level;
ngx_uint_t status_code;
} ngx_http_limit_conn_conf_t;
static ngx_rbtree_node_t *ngx_http_limit_conn_lookup(ngx_rbtree_t *rbtree,
ngx_str_t *key, uint32_t hash);
static void ngx_http_limit_conn_cleanup(void *data);
static ngx_inline void ngx_http_limit_conn_cleanup_all(ngx_pool_t *pool);
static void *ngx_http_limit_conn_create_conf(ngx_conf_t *cf);
static char *ngx_http_limit_conn_merge_conf(ngx_conf_t *cf, void *parent,
void *child);
static char *ngx_http_limit_conn_zone(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static char *ngx_http_limit_conn(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static ngx_int_t ngx_http_limit_conn_init(ngx_conf_t *cf);
static ngx_conf_enum_t ngx_http_limit_conn_log_levels[] = {
{ ngx_string("info"), NGX_LOG_INFO },
{ ngx_string("notice"), NGX_LOG_NOTICE },
{ ngx_string("warn"), NGX_LOG_WARN },
{ ngx_string("error"), NGX_LOG_ERR },
{ ngx_null_string, 0 }
};
static ngx_conf_num_bounds_t ngx_http_limit_conn_status_bounds = {
ngx_conf_check_num_bounds, 400, 599
};
static ngx_command_t ngx_http_limit_conn_commands[] = {
{ ngx_string("limit_conn_zone"),
NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE2,
ngx_http_limit_conn_zone,
0,
0,
NULL },
{ ngx_string("limit_conn"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
ngx_http_limit_conn,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
{ ngx_string("limit_conn_log_level"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_enum_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_limit_conn_conf_t, log_level),
&ngx_http_limit_conn_log_levels },
{ ngx_string("limit_conn_status"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_num_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_limit_conn_conf_t, status_code),
&ngx_http_limit_conn_status_bounds },
ngx_null_command
};
static ngx_http_module_t ngx_http_limit_conn_module_ctx = {
NULL, /* preconfiguration */
ngx_http_limit_conn_init, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
ngx_http_limit_conn_create_conf, /* create location configuration */
ngx_http_limit_conn_merge_conf /* merge location configuration */
};
ngx_module_t ngx_http_limit_conn_module = {
NGX_MODULE_V1,
&ngx_http_limit_conn_module_ctx, /* module context */
ngx_http_limit_conn_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static ngx_int_t
ngx_http_limit_conn_handler(ngx_http_request_t *r)
{
size_t n;
uint32_t hash;
ngx_str_t key;
ngx_uint_t i;
ngx_slab_pool_t *shpool;
ngx_rbtree_node_t *node;
ngx_pool_cleanup_t *cln;
ngx_http_limit_conn_ctx_t *ctx;
ngx_http_limit_conn_node_t *lc;
ngx_http_limit_conn_conf_t *lccf;
ngx_http_limit_conn_limit_t *limits;
ngx_http_limit_conn_cleanup_t *lccln;
if (r->main->limit_conn_set) {
return NGX_DECLINED;
}
lccf = ngx_http_get_module_loc_conf(r, ngx_http_limit_conn_module);
limits = lccf->limits.elts;
for (i = 0; i < lccf->limits.nelts; i++) {
ctx = limits[i].shm_zone->data;
if (ngx_http_complex_value(r, &ctx->key, &key) != NGX_OK) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
if (key.len == 0) {
continue;
}
if (key.len > 255) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"the value of the \"%V\" key "
"is more than 255 bytes: \"%V\"",
&ctx->key.value, &key);
continue;
}
r->main->limit_conn_set = 1;
hash = ngx_crc32_short(key.data, key.len);
shpool = (ngx_slab_pool_t *) limits[i].shm_zone->shm.addr;
ngx_shmtx_lock(&shpool->mutex);
node = ngx_http_limit_conn_lookup(ctx->rbtree, &key, hash);
if (node == NULL) {
n = offsetof(ngx_rbtree_node_t, color)
+ offsetof(ngx_http_limit_conn_node_t, data)
+ key.len;
node = ngx_slab_alloc_locked(shpool, n);
if (node == NULL) {
ngx_shmtx_unlock(&shpool->mutex);
ngx_http_limit_conn_cleanup_all(r->pool);
return lccf->status_code;
}
lc = (ngx_http_limit_conn_node_t *) &node->color;
node->key = hash;
lc->len = (u_char) key.len;
lc->conn = 1;
ngx_memcpy(lc->data, key.data, key.len);
ngx_rbtree_insert(ctx->rbtree, node);
} else {
lc = (ngx_http_limit_conn_node_t *) &node->color;
if ((ngx_uint_t) lc->conn >= limits[i].conn) {
ngx_shmtx_unlock(&shpool->mutex);
ngx_log_error(lccf->log_level, r->connection->log, 0,
"limiting connections by zone \"%V\"",
&limits[i].shm_zone->shm.name);
ngx_http_limit_conn_cleanup_all(r->pool);
return lccf->status_code;
}
lc->conn++;
}
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"limit conn: %08Xi %d", node->key, lc->conn);
ngx_shmtx_unlock(&shpool->mutex);
cln = ngx_pool_cleanup_add(r->pool,
sizeof(ngx_http_limit_conn_cleanup_t));
if (cln == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
cln->handler = ngx_http_limit_conn_cleanup;
lccln = cln->data;
lccln->shm_zone = limits[i].shm_zone;
lccln->node = node;
}
return NGX_DECLINED;
}
static void
ngx_http_limit_conn_rbtree_insert_value(ngx_rbtree_node_t *temp,
ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
{
ngx_rbtree_node_t **p;
ngx_http_limit_conn_node_t *lcn, *lcnt;
for ( ;; ) {
if (node->key < temp->key) {
p = &temp->left;
} else if (node->key > temp->key) {
p = &temp->right;
} else { /* node->key == temp->key */
lcn = (ngx_http_limit_conn_node_t *) &node->color;
lcnt = (ngx_http_limit_conn_node_t *) &temp->color;
p = (ngx_memn2cmp(lcn->data, lcnt->data, lcn->len, lcnt->len) < 0)
? &temp->left : &temp->right;
}
if (*p == sentinel) {
break;
}
temp = *p;
}
*p = node;
node->parent = temp;
node->left = sentinel;
node->right = sentinel;
ngx_rbt_red(node);
}
static ngx_rbtree_node_t *
ngx_http_limit_conn_lookup(ngx_rbtree_t *rbtree, ngx_str_t *key, uint32_t hash)
{
ngx_int_t rc;
ngx_rbtree_node_t *node, *sentinel;
ngx_http_limit_conn_node_t *lcn;
node = rbtree->root;
sentinel = rbtree->sentinel;
while (node != sentinel) {
if (hash < node->key) {
node = node->left;
continue;
}
if (hash > node->key) {
node = node->right;
continue;
}
/* hash == node->key */
lcn = (ngx_http_limit_conn_node_t *) &node->color;
rc = ngx_memn2cmp(key->data, lcn->data, key->len, (size_t) lcn->len);
if (rc == 0) {
return node;
}
node = (rc < 0) ? node->left : node->right;
}
return NULL;
}
static void
ngx_http_limit_conn_cleanup(void *data)
{
ngx_http_limit_conn_cleanup_t *lccln = data;
ngx_slab_pool_t *shpool;
ngx_rbtree_node_t *node;
ngx_http_limit_conn_ctx_t *ctx;
ngx_http_limit_conn_node_t *lc;
ctx = lccln->shm_zone->data;
shpool = (ngx_slab_pool_t *) lccln->shm_zone->shm.addr;
node = lccln->node;
lc = (ngx_http_limit_conn_node_t *) &node->color;
ngx_shmtx_lock(&shpool->mutex);
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, lccln->shm_zone->shm.log, 0,
"limit conn cleanup: %08Xi %d", node->key, lc->conn);
lc->conn--;
if (lc->conn == 0) {
ngx_rbtree_delete(ctx->rbtree, node);
ngx_slab_free_locked(shpool, node);
}
ngx_shmtx_unlock(&shpool->mutex);
}
static ngx_inline void
ngx_http_limit_conn_cleanup_all(ngx_pool_t *pool)
{
ngx_pool_cleanup_t *cln;
cln = pool->cleanup;
while (cln && cln->handler == ngx_http_limit_conn_cleanup) {
ngx_http_limit_conn_cleanup(cln->data);
cln = cln->next;
}
pool->cleanup = cln;
}
static ngx_int_t
ngx_http_limit_conn_init_zone(ngx_shm_zone_t *shm_zone, void *data)
{
ngx_http_limit_conn_ctx_t *octx = data;
size_t len;
ngx_slab_pool_t *shpool;
ngx_rbtree_node_t *sentinel;
ngx_http_limit_conn_ctx_t *ctx;
ctx = shm_zone->data;
if (octx) {
if (ctx->key.value.len != octx->key.value.len
|| ngx_strncmp(ctx->key.value.data, octx->key.value.data,
ctx->key.value.len)
!= 0)
{
ngx_log_error(NGX_LOG_EMERG, shm_zone->shm.log, 0,
"limit_conn_zone \"%V\" uses the \"%V\" key "
"while previously it used the \"%V\" key",
&shm_zone->shm.name, &ctx->key.value,
&octx->key.value);
return NGX_ERROR;
}
ctx->rbtree = octx->rbtree;
return NGX_OK;
}
shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
if (shm_zone->shm.exists) {
ctx->rbtree = shpool->data;
return NGX_OK;
}
ctx->rbtree = ngx_slab_alloc(shpool, sizeof(ngx_rbtree_t));
if (ctx->rbtree == NULL) {
return NGX_ERROR;
}
shpool->data = ctx->rbtree;
sentinel = ngx_slab_alloc(shpool, sizeof(ngx_rbtree_node_t));
if (sentinel == NULL) {
return NGX_ERROR;
}
ngx_rbtree_init(ctx->rbtree, sentinel,
ngx_http_limit_conn_rbtree_insert_value);
len = sizeof(" in limit_conn_zone \"\"") + shm_zone->shm.name.len;
shpool->log_ctx = ngx_slab_alloc(shpool, len);
if (shpool->log_ctx == NULL) {
return NGX_ERROR;
}
ngx_sprintf(shpool->log_ctx, " in limit_conn_zone \"%V\"%Z",
&shm_zone->shm.name);
return NGX_OK;
}
static void *
ngx_http_limit_conn_create_conf(ngx_conf_t *cf)
{
ngx_http_limit_conn_conf_t *conf;
conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_limit_conn_conf_t));
if (conf == NULL) {
return NULL;
}
/*
* set by ngx_pcalloc():
*
* conf->limits.elts = NULL;
*/
conf->log_level = NGX_CONF_UNSET_UINT;
conf->status_code = NGX_CONF_UNSET_UINT;
return conf;
}
static char *
ngx_http_limit_conn_merge_conf(ngx_conf_t *cf, void *parent, void *child)
{
ngx_http_limit_conn_conf_t *prev = parent;
ngx_http_limit_conn_conf_t *conf = child;
if (conf->limits.elts == NULL) {
conf->limits = prev->limits;
}
ngx_conf_merge_uint_value(conf->log_level, prev->log_level, NGX_LOG_ERR);
ngx_conf_merge_uint_value(conf->status_code, prev->status_code,
NGX_HTTP_SERVICE_UNAVAILABLE);
return NGX_CONF_OK;
}
static char *
ngx_http_limit_conn_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
u_char *p;
ssize_t size;
ngx_str_t *value, name, s;
ngx_uint_t i;
ngx_shm_zone_t *shm_zone;
ngx_http_limit_conn_ctx_t *ctx;
ngx_http_compile_complex_value_t ccv;
value = cf->args->elts;
ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_limit_conn_ctx_t));
if (ctx == NULL) {
return NGX_CONF_ERROR;
}
ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
ccv.cf = cf;
ccv.value = &value[1];
ccv.complex_value = &ctx->key;
if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
return NGX_CONF_ERROR;
}
size = 0;
name.len = 0;
for (i = 2; i < cf->args->nelts; i++) {
if (ngx_strncmp(value[i].data, "zone=", 5) == 0) {
name.data = value[i].data + 5;
p = (u_char *) ngx_strchr(name.data, ':');
if (p == NULL) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid zone size \"%V\"", &value[i]);
return NGX_CONF_ERROR;
}
name.len = p - name.data;
s.data = p + 1;
s.len = value[i].data + value[i].len - s.data;
size = ngx_parse_size(&s);
if (size == NGX_ERROR) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid zone size \"%V\"", &value[i]);
return NGX_CONF_ERROR;
}
if (size < (ssize_t) (8 * ngx_pagesize)) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"zone \"%V\" is too small", &value[i]);
return NGX_CONF_ERROR;
}
continue;
}
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid parameter \"%V\"", &value[i]);
return NGX_CONF_ERROR;
}
if (name.len == 0) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"\"%V\" must have \"zone\" parameter",
&cmd->name);
return NGX_CONF_ERROR;
}
shm_zone = ngx_shared_memory_add(cf, &name, size,
&ngx_http_limit_conn_module);
if (shm_zone == NULL) {
return NGX_CONF_ERROR;
}
if (shm_zone->data) {
ctx = shm_zone->data;
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"%V \"%V\" is already bound to key \"%V\"",
&cmd->name, &name, &ctx->key.value);
return NGX_CONF_ERROR;
}
shm_zone->init = ngx_http_limit_conn_init_zone;
shm_zone->data = ctx;
return NGX_CONF_OK;
}
static char *
ngx_http_limit_conn(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_shm_zone_t *shm_zone;
ngx_http_limit_conn_conf_t *lccf = conf;
ngx_http_limit_conn_limit_t *limit, *limits;
ngx_str_t *value;
ngx_int_t n;
ngx_uint_t i;
value = cf->args->elts;
shm_zone = ngx_shared_memory_add(cf, &value[1], 0,
&ngx_http_limit_conn_module);
if (shm_zone == NULL) {
return NGX_CONF_ERROR;
}
limits = lccf->limits.elts;
if (limits == NULL) {
if (ngx_array_init(&lccf->limits, cf->pool, 1,
sizeof(ngx_http_limit_conn_limit_t))
!= NGX_OK)
{
return NGX_CONF_ERROR;
}
}
for (i = 0; i < lccf->limits.nelts; i++) {
if (shm_zone == limits[i].shm_zone) {
return "is duplicate";
}
}
n = ngx_atoi(value[2].data, value[2].len);
if (n <= 0) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid number of connections \"%V\"", &value[2]);
return NGX_CONF_ERROR;
}
if (n > 65535) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"connection limit must be less 65536");
return NGX_CONF_ERROR;
}
limit = ngx_array_push(&lccf->limits);
if (limit == NULL) {
return NGX_CONF_ERROR;
}
limit->conn = n;
limit->shm_zone = shm_zone;
return NGX_CONF_OK;
}
static ngx_int_t
ngx_http_limit_conn_init(ngx_conf_t *cf)
{
ngx_http_handler_pt *h;
ngx_http_core_main_conf_t *cmcf;
cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
h = ngx_array_push(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers);
if (h == NULL) {
return NGX_ERROR;
}
*h = ngx_http_limit_conn_handler;
return NGX_OK;
}
nginx-1.14.0/src/http/modules/ngx_http_limit_req_module.c 000644 001751 001751 00000060671 13265410474 024755 0 ustar 00mdounin mdounin 000000 000000
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#include
#include
#include
typedef struct {
u_char color;
u_char dummy;
u_short len;
ngx_queue_t queue;
ngx_msec_t last;
/* integer value, 1 corresponds to 0.001 r/s */
ngx_uint_t excess;
ngx_uint_t count;
u_char data[1];
} ngx_http_limit_req_node_t;
typedef struct {
ngx_rbtree_t rbtree;
ngx_rbtree_node_t sentinel;
ngx_queue_t queue;
} ngx_http_limit_req_shctx_t;
typedef struct {
ngx_http_limit_req_shctx_t *sh;
ngx_slab_pool_t *shpool;
/* integer value, 1 corresponds to 0.001 r/s */
ngx_uint_t rate;
ngx_http_complex_value_t key;
ngx_http_limit_req_node_t *node;
} ngx_http_limit_req_ctx_t;
typedef struct {
ngx_shm_zone_t *shm_zone;
/* integer value, 1 corresponds to 0.001 r/s */
ngx_uint_t burst;
ngx_uint_t nodelay; /* unsigned nodelay:1 */
} ngx_http_limit_req_limit_t;
typedef struct {
ngx_array_t limits;
ngx_uint_t limit_log_level;
ngx_uint_t delay_log_level;
ngx_uint_t status_code;
} ngx_http_limit_req_conf_t;
static void ngx_http_limit_req_delay(ngx_http_request_t *r);
static ngx_int_t ngx_http_limit_req_lookup(ngx_http_limit_req_limit_t *limit,
ngx_uint_t hash, ngx_str_t *key, ngx_uint_t *ep, ngx_uint_t account);
static ngx_msec_t ngx_http_limit_req_account(ngx_http_limit_req_limit_t *limits,
ngx_uint_t n, ngx_uint_t *ep, ngx_http_limit_req_limit_t **limit);
static void ngx_http_limit_req_expire(ngx_http_limit_req_ctx_t *ctx,
ngx_uint_t n);
static void *ngx_http_limit_req_create_conf(ngx_conf_t *cf);
static char *ngx_http_limit_req_merge_conf(ngx_conf_t *cf, void *parent,
void *child);
static char *ngx_http_limit_req_zone(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static char *ngx_http_limit_req(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static ngx_int_t ngx_http_limit_req_init(ngx_conf_t *cf);
static ngx_conf_enum_t ngx_http_limit_req_log_levels[] = {
{ ngx_string("info"), NGX_LOG_INFO },
{ ngx_string("notice"), NGX_LOG_NOTICE },
{ ngx_string("warn"), NGX_LOG_WARN },
{ ngx_string("error"), NGX_LOG_ERR },
{ ngx_null_string, 0 }
};
static ngx_conf_num_bounds_t ngx_http_limit_req_status_bounds = {
ngx_conf_check_num_bounds, 400, 599
};
static ngx_command_t ngx_http_limit_req_commands[] = {
{ ngx_string("limit_req_zone"),
NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE3,
ngx_http_limit_req_zone,
0,
0,
NULL },
{ ngx_string("limit_req"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE123,
ngx_http_limit_req,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
{ ngx_string("limit_req_log_level"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_enum_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_limit_req_conf_t, limit_log_level),
&ngx_http_limit_req_log_levels },
{ ngx_string("limit_req_status"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_num_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_limit_req_conf_t, status_code),
&ngx_http_limit_req_status_bounds },
ngx_null_command
};
static ngx_http_module_t ngx_http_limit_req_module_ctx = {
NULL, /* preconfiguration */
ngx_http_limit_req_init, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
ngx_http_limit_req_create_conf, /* create location configuration */
ngx_http_limit_req_merge_conf /* merge location configuration */
};
ngx_module_t ngx_http_limit_req_module = {
NGX_MODULE_V1,
&ngx_http_limit_req_module_ctx, /* module context */
ngx_http_limit_req_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static ngx_int_t
ngx_http_limit_req_handler(ngx_http_request_t *r)
{
uint32_t hash;
ngx_str_t key;
ngx_int_t rc;
ngx_uint_t n, excess;
ngx_msec_t delay;
ngx_http_limit_req_ctx_t *ctx;
ngx_http_limit_req_conf_t *lrcf;
ngx_http_limit_req_limit_t *limit, *limits;
if (r->main->limit_req_set) {
return NGX_DECLINED;
}
lrcf = ngx_http_get_module_loc_conf(r, ngx_http_limit_req_module);
limits = lrcf->limits.elts;
excess = 0;
rc = NGX_DECLINED;
#if (NGX_SUPPRESS_WARN)
limit = NULL;
#endif
for (n = 0; n < lrcf->limits.nelts; n++) {
limit = &limits[n];
ctx = limit->shm_zone->data;
if (ngx_http_complex_value(r, &ctx->key, &key) != NGX_OK) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
if (key.len == 0) {
continue;
}
if (key.len > 65535) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"the value of the \"%V\" key "
"is more than 65535 bytes: \"%V\"",
&ctx->key.value, &key);
continue;
}
hash = ngx_crc32_short(key.data, key.len);
ngx_shmtx_lock(&ctx->shpool->mutex);
rc = ngx_http_limit_req_lookup(limit, hash, &key, &excess,
(n == lrcf->limits.nelts - 1));
ngx_shmtx_unlock(&ctx->shpool->mutex);
ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"limit_req[%ui]: %i %ui.%03ui",
n, rc, excess / 1000, excess % 1000);
if (rc != NGX_AGAIN) {
break;
}
}
if (rc == NGX_DECLINED) {
return NGX_DECLINED;
}
r->main->limit_req_set = 1;
if (rc == NGX_BUSY || rc == NGX_ERROR) {
if (rc == NGX_BUSY) {
ngx_log_error(lrcf->limit_log_level, r->connection->log, 0,
"limiting requests, excess: %ui.%03ui by zone \"%V\"",
excess / 1000, excess % 1000,
&limit->shm_zone->shm.name);
}
while (n--) {
ctx = limits[n].shm_zone->data;
if (ctx->node == NULL) {
continue;
}
ngx_shmtx_lock(&ctx->shpool->mutex);
ctx->node->count--;
ngx_shmtx_unlock(&ctx->shpool->mutex);
ctx->node = NULL;
}
return lrcf->status_code;
}
/* rc == NGX_AGAIN || rc == NGX_OK */
if (rc == NGX_AGAIN) {
excess = 0;
}
delay = ngx_http_limit_req_account(limits, n, &excess, &limit);
if (!delay) {
return NGX_DECLINED;
}
ngx_log_error(lrcf->delay_log_level, r->connection->log, 0,
"delaying request, excess: %ui.%03ui, by zone \"%V\"",
excess / 1000, excess % 1000, &limit->shm_zone->shm.name);
if (ngx_handle_read_event(r->connection->read, 0) != NGX_OK) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
r->read_event_handler = ngx_http_test_reading;
r->write_event_handler = ngx_http_limit_req_delay;
r->connection->write->delayed = 1;
ngx_add_timer(r->connection->write, delay);
return NGX_AGAIN;
}
static void
ngx_http_limit_req_delay(ngx_http_request_t *r)
{
ngx_event_t *wev;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"limit_req delay");
wev = r->connection->write;
if (wev->delayed) {
if (ngx_handle_write_event(wev, 0) != NGX_OK) {
ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
}
return;
}
if (ngx_handle_read_event(r->connection->read, 0) != NGX_OK) {
ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
}
r->read_event_handler = ngx_http_block_reading;
r->write_event_handler = ngx_http_core_run_phases;
ngx_http_core_run_phases(r);
}
static void
ngx_http_limit_req_rbtree_insert_value(ngx_rbtree_node_t *temp,
ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
{
ngx_rbtree_node_t **p;
ngx_http_limit_req_node_t *lrn, *lrnt;
for ( ;; ) {
if (node->key < temp->key) {
p = &temp->left;
} else if (node->key > temp->key) {
p = &temp->right;
} else { /* node->key == temp->key */
lrn = (ngx_http_limit_req_node_t *) &node->color;
lrnt = (ngx_http_limit_req_node_t *) &temp->color;
p = (ngx_memn2cmp(lrn->data, lrnt->data, lrn->len, lrnt->len) < 0)
? &temp->left : &temp->right;
}
if (*p == sentinel) {
break;
}
temp = *p;
}
*p = node;
node->parent = temp;
node->left = sentinel;
node->right = sentinel;
ngx_rbt_red(node);
}
static ngx_int_t
ngx_http_limit_req_lookup(ngx_http_limit_req_limit_t *limit, ngx_uint_t hash,
ngx_str_t *key, ngx_uint_t *ep, ngx_uint_t account)
{
size_t size;
ngx_int_t rc, excess;
ngx_msec_t now;
ngx_msec_int_t ms;
ngx_rbtree_node_t *node, *sentinel;
ngx_http_limit_req_ctx_t *ctx;
ngx_http_limit_req_node_t *lr;
now = ngx_current_msec;
ctx = limit->shm_zone->data;
node = ctx->sh->rbtree.root;
sentinel = ctx->sh->rbtree.sentinel;
while (node != sentinel) {
if (hash < node->key) {
node = node->left;
continue;
}
if (hash > node->key) {
node = node->right;
continue;
}
/* hash == node->key */
lr = (ngx_http_limit_req_node_t *) &node->color;
rc = ngx_memn2cmp(key->data, lr->data, key->len, (size_t) lr->len);
if (rc == 0) {
ngx_queue_remove(&lr->queue);
ngx_queue_insert_head(&ctx->sh->queue, &lr->queue);
ms = (ngx_msec_int_t) (now - lr->last);
excess = lr->excess - ctx->rate * ngx_abs(ms) / 1000 + 1000;
if (excess < 0) {
excess = 0;
}
*ep = excess;
if ((ngx_uint_t) excess > limit->burst) {
return NGX_BUSY;
}
if (account) {
lr->excess = excess;
lr->last = now;
return NGX_OK;
}
lr->count++;
ctx->node = lr;
return NGX_AGAIN;
}
node = (rc < 0) ? node->left : node->right;
}
*ep = 0;
size = offsetof(ngx_rbtree_node_t, color)
+ offsetof(ngx_http_limit_req_node_t, data)
+ key->len;
ngx_http_limit_req_expire(ctx, 1);
node = ngx_slab_alloc_locked(ctx->shpool, size);
if (node == NULL) {
ngx_http_limit_req_expire(ctx, 0);
node = ngx_slab_alloc_locked(ctx->shpool, size);
if (node == NULL) {
ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
"could not allocate node%s", ctx->shpool->log_ctx);
return NGX_ERROR;
}
}
node->key = hash;
lr = (ngx_http_limit_req_node_t *) &node->color;
lr->len = (u_short) key->len;
lr->excess = 0;
ngx_memcpy(lr->data, key->data, key->len);
ngx_rbtree_insert(&ctx->sh->rbtree, node);
ngx_queue_insert_head(&ctx->sh->queue, &lr->queue);
if (account) {
lr->last = now;
lr->count = 0;
return NGX_OK;
}
lr->last = 0;
lr->count = 1;
ctx->node = lr;
return NGX_AGAIN;
}
static ngx_msec_t
ngx_http_limit_req_account(ngx_http_limit_req_limit_t *limits, ngx_uint_t n,
ngx_uint_t *ep, ngx_http_limit_req_limit_t **limit)
{
ngx_int_t excess;
ngx_msec_t now, delay, max_delay;
ngx_msec_int_t ms;
ngx_http_limit_req_ctx_t *ctx;
ngx_http_limit_req_node_t *lr;
excess = *ep;
if (excess == 0 || (*limit)->nodelay) {
max_delay = 0;
} else {
ctx = (*limit)->shm_zone->data;
max_delay = excess * 1000 / ctx->rate;
}
while (n--) {
ctx = limits[n].shm_zone->data;
lr = ctx->node;
if (lr == NULL) {
continue;
}
ngx_shmtx_lock(&ctx->shpool->mutex);
now = ngx_current_msec;
ms = (ngx_msec_int_t) (now - lr->last);
excess = lr->excess - ctx->rate * ngx_abs(ms) / 1000 + 1000;
if (excess < 0) {
excess = 0;
}
lr->last = now;
lr->excess = excess;
lr->count--;
ngx_shmtx_unlock(&ctx->shpool->mutex);
ctx->node = NULL;
if (limits[n].nodelay) {
continue;
}
delay = excess * 1000 / ctx->rate;
if (delay > max_delay) {
max_delay = delay;
*ep = excess;
*limit = &limits[n];
}
}
return max_delay;
}
static void
ngx_http_limit_req_expire(ngx_http_limit_req_ctx_t *ctx, ngx_uint_t n)
{
ngx_int_t excess;
ngx_msec_t now;
ngx_queue_t *q;
ngx_msec_int_t ms;
ngx_rbtree_node_t *node;
ngx_http_limit_req_node_t *lr;
now = ngx_current_msec;
/*
* n == 1 deletes one or two zero rate entries
* n == 0 deletes oldest entry by force
* and one or two zero rate entries
*/
while (n < 3) {
if (ngx_queue_empty(&ctx->sh->queue)) {
return;
}
q = ngx_queue_last(&ctx->sh->queue);
lr = ngx_queue_data(q, ngx_http_limit_req_node_t, queue);
if (lr->count) {
/*
* There is not much sense in looking further,
* because we bump nodes on the lookup stage.
*/
return;
}
if (n++ != 0) {
ms = (ngx_msec_int_t) (now - lr->last);
ms = ngx_abs(ms);
if (ms < 60000) {
return;
}
excess = lr->excess - ctx->rate * ms / 1000;
if (excess > 0) {
return;
}
}
ngx_queue_remove(q);
node = (ngx_rbtree_node_t *)
((u_char *) lr - offsetof(ngx_rbtree_node_t, color));
ngx_rbtree_delete(&ctx->sh->rbtree, node);
ngx_slab_free_locked(ctx->shpool, node);
}
}
static ngx_int_t
ngx_http_limit_req_init_zone(ngx_shm_zone_t *shm_zone, void *data)
{
ngx_http_limit_req_ctx_t *octx = data;
size_t len;
ngx_http_limit_req_ctx_t *ctx;
ctx = shm_zone->data;
if (octx) {
if (ctx->key.value.len != octx->key.value.len
|| ngx_strncmp(ctx->key.value.data, octx->key.value.data,
ctx->key.value.len)
!= 0)
{
ngx_log_error(NGX_LOG_EMERG, shm_zone->shm.log, 0,
"limit_req \"%V\" uses the \"%V\" key "
"while previously it used the \"%V\" key",
&shm_zone->shm.name, &ctx->key.value,
&octx->key.value);
return NGX_ERROR;
}
ctx->sh = octx->sh;
ctx->shpool = octx->shpool;
return NGX_OK;
}
ctx->shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
if (shm_zone->shm.exists) {
ctx->sh = ctx->shpool->data;
return NGX_OK;
}
ctx->sh = ngx_slab_alloc(ctx->shpool, sizeof(ngx_http_limit_req_shctx_t));
if (ctx->sh == NULL) {
return NGX_ERROR;
}
ctx->shpool->data = ctx->sh;
ngx_rbtree_init(&ctx->sh->rbtree, &ctx->sh->sentinel,
ngx_http_limit_req_rbtree_insert_value);
ngx_queue_init(&ctx->sh->queue);
len = sizeof(" in limit_req zone \"\"") + shm_zone->shm.name.len;
ctx->shpool->log_ctx = ngx_slab_alloc(ctx->shpool, len);
if (ctx->shpool->log_ctx == NULL) {
return NGX_ERROR;
}
ngx_sprintf(ctx->shpool->log_ctx, " in limit_req zone \"%V\"%Z",
&shm_zone->shm.name);
ctx->shpool->log_nomem = 0;
return NGX_OK;
}
static void *
ngx_http_limit_req_create_conf(ngx_conf_t *cf)
{
ngx_http_limit_req_conf_t *conf;
conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_limit_req_conf_t));
if (conf == NULL) {
return NULL;
}
/*
* set by ngx_pcalloc():
*
* conf->limits.elts = NULL;
*/
conf->limit_log_level = NGX_CONF_UNSET_UINT;
conf->status_code = NGX_CONF_UNSET_UINT;
return conf;
}
static char *
ngx_http_limit_req_merge_conf(ngx_conf_t *cf, void *parent, void *child)
{
ngx_http_limit_req_conf_t *prev = parent;
ngx_http_limit_req_conf_t *conf = child;
if (conf->limits.elts == NULL) {
conf->limits = prev->limits;
}
ngx_conf_merge_uint_value(conf->limit_log_level, prev->limit_log_level,
NGX_LOG_ERR);
conf->delay_log_level = (conf->limit_log_level == NGX_LOG_INFO) ?
NGX_LOG_INFO : conf->limit_log_level + 1;
ngx_conf_merge_uint_value(conf->status_code, prev->status_code,
NGX_HTTP_SERVICE_UNAVAILABLE);
return NGX_CONF_OK;
}
static char *
ngx_http_limit_req_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
u_char *p;
size_t len;
ssize_t size;
ngx_str_t *value, name, s;
ngx_int_t rate, scale;
ngx_uint_t i;
ngx_shm_zone_t *shm_zone;
ngx_http_limit_req_ctx_t *ctx;
ngx_http_compile_complex_value_t ccv;
value = cf->args->elts;
ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_limit_req_ctx_t));
if (ctx == NULL) {
return NGX_CONF_ERROR;
}
ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
ccv.cf = cf;
ccv.value = &value[1];
ccv.complex_value = &ctx->key;
if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
return NGX_CONF_ERROR;
}
size = 0;
rate = 1;
scale = 1;
name.len = 0;
for (i = 2; i < cf->args->nelts; i++) {
if (ngx_strncmp(value[i].data, "zone=", 5) == 0) {
name.data = value[i].data + 5;
p = (u_char *) ngx_strchr(name.data, ':');
if (p == NULL) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid zone size \"%V\"", &value[i]);
return NGX_CONF_ERROR;
}
name.len = p - name.data;
s.data = p + 1;
s.len = value[i].data + value[i].len - s.data;
size = ngx_parse_size(&s);
if (size == NGX_ERROR) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid zone size \"%V\"", &value[i]);
return NGX_CONF_ERROR;
}
if (size < (ssize_t) (8 * ngx_pagesize)) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"zone \"%V\" is too small", &value[i]);
return NGX_CONF_ERROR;
}
continue;
}
if (ngx_strncmp(value[i].data, "rate=", 5) == 0) {
len = value[i].len;
p = value[i].data + len - 3;
if (ngx_strncmp(p, "r/s", 3) == 0) {
scale = 1;
len -= 3;
} else if (ngx_strncmp(p, "r/m", 3) == 0) {
scale = 60;
len -= 3;
}
rate = ngx_atoi(value[i].data + 5, len - 5);
if (rate <= 0) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid rate \"%V\"", &value[i]);
return NGX_CONF_ERROR;
}
continue;
}
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid parameter \"%V\"", &value[i]);
return NGX_CONF_ERROR;
}
if (name.len == 0) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"\"%V\" must have \"zone\" parameter",
&cmd->name);
return NGX_CONF_ERROR;
}
ctx->rate = rate * 1000 / scale;
shm_zone = ngx_shared_memory_add(cf, &name, size,
&ngx_http_limit_req_module);
if (shm_zone == NULL) {
return NGX_CONF_ERROR;
}
if (shm_zone->data) {
ctx = shm_zone->data;
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"%V \"%V\" is already bound to key \"%V\"",
&cmd->name, &name, &ctx->key.value);
return NGX_CONF_ERROR;
}
shm_zone->init = ngx_http_limit_req_init_zone;
shm_zone->data = ctx;
return NGX_CONF_OK;
}
static char *
ngx_http_limit_req(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_limit_req_conf_t *lrcf = conf;
ngx_int_t burst;
ngx_str_t *value, s;
ngx_uint_t i, nodelay;
ngx_shm_zone_t *shm_zone;
ngx_http_limit_req_limit_t *limit, *limits;
value = cf->args->elts;
shm_zone = NULL;
burst = 0;
nodelay = 0;
for (i = 1; i < cf->args->nelts; i++) {
if (ngx_strncmp(value[i].data, "zone=", 5) == 0) {
s.len = value[i].len - 5;
s.data = value[i].data + 5;
shm_zone = ngx_shared_memory_add(cf, &s, 0,
&ngx_http_limit_req_module);
if (shm_zone == NULL) {
return NGX_CONF_ERROR;
}
continue;
}
if (ngx_strncmp(value[i].data, "burst=", 6) == 0) {
burst = ngx_atoi(value[i].data + 6, value[i].len - 6);
if (burst <= 0) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid burst rate \"%V\"", &value[i]);
return NGX_CONF_ERROR;
}
continue;
}
if (ngx_strcmp(value[i].data, "nodelay") == 0) {
nodelay = 1;
continue;
}
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid parameter \"%V\"", &value[i]);
return NGX_CONF_ERROR;
}
if (shm_zone == NULL) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"\"%V\" must have \"zone\" parameter",
&cmd->name);
return NGX_CONF_ERROR;
}
limits = lrcf->limits.elts;
if (limits == NULL) {
if (ngx_array_init(&lrcf->limits, cf->pool, 1,
sizeof(ngx_http_limit_req_limit_t))
!= NGX_OK)
{
return NGX_CONF_ERROR;
}
}
for (i = 0; i < lrcf->limits.nelts; i++) {
if (shm_zone == limits[i].shm_zone) {
return "is duplicate";
}
}
limit = ngx_array_push(&lrcf->limits);
if (limit == NULL) {
return NGX_CONF_ERROR;
}
limit->shm_zone = shm_zone;
limit->burst = burst * 1000;
limit->nodelay = nodelay;
return NGX_CONF_OK;
}
static ngx_int_t
ngx_http_limit_req_init(ngx_conf_t *cf)
{
ngx_http_handler_pt *h;
ngx_http_core_main_conf_t *cmcf;
cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
h = ngx_array_push(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers);
if (h == NULL) {
return NGX_ERROR;
}
*h = ngx_http_limit_req_handler;
return NGX_OK;
}
nginx-1.14.0/src/http/modules/ngx_http_log_module.c 000644 001751 001751 00000141155 13265410474 023546 0 ustar 00mdounin mdounin 000000 000000
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#include
#include
#include
#if (NGX_ZLIB)
#include
#endif
typedef struct ngx_http_log_op_s ngx_http_log_op_t;
typedef u_char *(*ngx_http_log_op_run_pt) (ngx_http_request_t *r, u_char *buf,
ngx_http_log_op_t *op);
typedef size_t (*ngx_http_log_op_getlen_pt) (ngx_http_request_t *r,
uintptr_t data);
struct ngx_http_log_op_s {
size_t len;
ngx_http_log_op_getlen_pt getlen;
ngx_http_log_op_run_pt run;
uintptr_t data;
};
typedef struct {
ngx_str_t name;
ngx_array_t *flushes;
ngx_array_t *ops; /* array of ngx_http_log_op_t */
} ngx_http_log_fmt_t;
typedef struct {
ngx_array_t formats; /* array of ngx_http_log_fmt_t */
ngx_uint_t combined_used; /* unsigned combined_used:1 */
} ngx_http_log_main_conf_t;
typedef struct {
u_char *start;
u_char *pos;
u_char *last;
ngx_event_t *event;
ngx_msec_t flush;
ngx_int_t gzip;
} ngx_http_log_buf_t;
typedef struct {
ngx_array_t *lengths;
ngx_array_t *values;
} ngx_http_log_script_t;
typedef struct {
ngx_open_file_t *file;
ngx_http_log_script_t *script;
time_t disk_full_time;
time_t error_log_time;
ngx_syslog_peer_t *syslog_peer;
ngx_http_log_fmt_t *format;
ngx_http_complex_value_t *filter;
} ngx_http_log_t;
typedef struct {
ngx_array_t *logs; /* array of ngx_http_log_t */
ngx_open_file_cache_t *open_file_cache;
time_t open_file_cache_valid;
ngx_uint_t open_file_cache_min_uses;
ngx_uint_t off; /* unsigned off:1 */
} ngx_http_log_loc_conf_t;
typedef struct {
ngx_str_t name;
size_t len;
ngx_http_log_op_run_pt run;
} ngx_http_log_var_t;
#define NGX_HTTP_LOG_ESCAPE_DEFAULT 0
#define NGX_HTTP_LOG_ESCAPE_JSON 1
#define NGX_HTTP_LOG_ESCAPE_NONE 2
static void ngx_http_log_write(ngx_http_request_t *r, ngx_http_log_t *log,
u_char *buf, size_t len);
static ssize_t ngx_http_log_script_write(ngx_http_request_t *r,
ngx_http_log_script_t *script, u_char **name, u_char *buf, size_t len);
#if (NGX_ZLIB)
static ssize_t ngx_http_log_gzip(ngx_fd_t fd, u_char *buf, size_t len,
ngx_int_t level, ngx_log_t *log);
static void *ngx_http_log_gzip_alloc(void *opaque, u_int items, u_int size);
static void ngx_http_log_gzip_free(void *opaque, void *address);
#endif
static void ngx_http_log_flush(ngx_open_file_t *file, ngx_log_t *log);
static void ngx_http_log_flush_handler(ngx_event_t *ev);
static u_char *ngx_http_log_pipe(ngx_http_request_t *r, u_char *buf,
ngx_http_log_op_t *op);
static u_char *ngx_http_log_time(ngx_http_request_t *r, u_char *buf,
ngx_http_log_op_t *op);
static u_char *ngx_http_log_iso8601(ngx_http_request_t *r, u_char *buf,
ngx_http_log_op_t *op);
static u_char *ngx_http_log_msec(ngx_http_request_t *r, u_char *buf,
ngx_http_log_op_t *op);
static u_char *ngx_http_log_request_time(ngx_http_request_t *r, u_char *buf,
ngx_http_log_op_t *op);
static u_char *ngx_http_log_status(ngx_http_request_t *r, u_char *buf,
ngx_http_log_op_t *op);
static u_char *ngx_http_log_bytes_sent(ngx_http_request_t *r, u_char *buf,
ngx_http_log_op_t *op);
static u_char *ngx_http_log_body_bytes_sent(ngx_http_request_t *r,
u_char *buf, ngx_http_log_op_t *op);
static u_char *ngx_http_log_request_length(ngx_http_request_t *r, u_char *buf,
ngx_http_log_op_t *op);
static ngx_int_t ngx_http_log_variable_compile(ngx_conf_t *cf,
ngx_http_log_op_t *op, ngx_str_t *value, ngx_uint_t escape);
static size_t ngx_http_log_variable_getlen(ngx_http_request_t *r,
uintptr_t data);
static u_char *ngx_http_log_variable(ngx_http_request_t *r, u_char *buf,
ngx_http_log_op_t *op);
static uintptr_t ngx_http_log_escape(u_char *dst, u_char *src, size_t size);
static size_t ngx_http_log_json_variable_getlen(ngx_http_request_t *r,
uintptr_t data);
static u_char *ngx_http_log_json_variable(ngx_http_request_t *r, u_char *buf,
ngx_http_log_op_t *op);
static size_t ngx_http_log_unescaped_variable_getlen(ngx_http_request_t *r,
uintptr_t data);
static u_char *ngx_http_log_unescaped_variable(ngx_http_request_t *r,
u_char *buf, ngx_http_log_op_t *op);
static void *ngx_http_log_create_main_conf(ngx_conf_t *cf);
static void *ngx_http_log_create_loc_conf(ngx_conf_t *cf);
static char *ngx_http_log_merge_loc_conf(ngx_conf_t *cf, void *parent,
void *child);
static char *ngx_http_log_set_log(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static char *ngx_http_log_set_format(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static char *ngx_http_log_compile_format(ngx_conf_t *cf,
ngx_array_t *flushes, ngx_array_t *ops, ngx_array_t *args, ngx_uint_t s);
static char *ngx_http_log_open_file_cache(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static ngx_int_t ngx_http_log_init(ngx_conf_t *cf);
static ngx_command_t ngx_http_log_commands[] = {
{ ngx_string("log_format"),
NGX_HTTP_MAIN_CONF|NGX_CONF_2MORE,
ngx_http_log_set_format,
NGX_HTTP_MAIN_CONF_OFFSET,
0,
NULL },
{ ngx_string("access_log"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
|NGX_HTTP_LMT_CONF|NGX_CONF_1MORE,
ngx_http_log_set_log,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
{ ngx_string("open_log_file_cache"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1234,
ngx_http_log_open_file_cache,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
ngx_null_command
};
static ngx_http_module_t ngx_http_log_module_ctx = {
NULL, /* preconfiguration */
ngx_http_log_init, /* postconfiguration */
ngx_http_log_create_main_conf, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
ngx_http_log_create_loc_conf, /* create location configuration */
ngx_http_log_merge_loc_conf /* merge location configuration */
};
ngx_module_t ngx_http_log_module = {
NGX_MODULE_V1,
&ngx_http_log_module_ctx, /* module context */
ngx_http_log_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static ngx_str_t ngx_http_access_log = ngx_string(NGX_HTTP_LOG_PATH);
static ngx_str_t ngx_http_combined_fmt =
ngx_string("$remote_addr - $remote_user [$time_local] "
"\"$request\" $status $body_bytes_sent "
"\"$http_referer\" \"$http_user_agent\"");
static ngx_http_log_var_t ngx_http_log_vars[] = {
{ ngx_string("pipe"), 1, ngx_http_log_pipe },
{ ngx_string("time_local"), sizeof("28/Sep/1970:12:00:00 +0600") - 1,
ngx_http_log_time },
{ ngx_string("time_iso8601"), sizeof("1970-09-28T12:00:00+06:00") - 1,
ngx_http_log_iso8601 },
{ ngx_string("msec"), NGX_TIME_T_LEN + 4, ngx_http_log_msec },
{ ngx_string("request_time"), NGX_TIME_T_LEN + 4,
ngx_http_log_request_time },
{ ngx_string("status"), NGX_INT_T_LEN, ngx_http_log_status },
{ ngx_string("bytes_sent"), NGX_OFF_T_LEN, ngx_http_log_bytes_sent },
{ ngx_string("body_bytes_sent"), NGX_OFF_T_LEN,
ngx_http_log_body_bytes_sent },
{ ngx_string("request_length"), NGX_SIZE_T_LEN,
ngx_http_log_request_length },
{ ngx_null_string, 0, NULL }
};
static ngx_int_t
ngx_http_log_handler(ngx_http_request_t *r)
{
u_char *line, *p;
size_t len, size;
ssize_t n;
ngx_str_t val;
ngx_uint_t i, l;
ngx_http_log_t *log;
ngx_http_log_op_t *op;
ngx_http_log_buf_t *buffer;
ngx_http_log_loc_conf_t *lcf;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http log handler");
lcf = ngx_http_get_module_loc_conf(r, ngx_http_log_module);
if (lcf->off) {
return NGX_OK;
}
log = lcf->logs->elts;
for (l = 0; l < lcf->logs->nelts; l++) {
if (log[l].filter) {
if (ngx_http_complex_value(r, log[l].filter, &val) != NGX_OK) {
return NGX_ERROR;
}
if (val.len == 0 || (val.len == 1 && val.data[0] == '0')) {
continue;
}
}
if (ngx_time() == log[l].disk_full_time) {
/*
* on FreeBSD writing to a full filesystem with enabled softupdates
* may block process for much longer time than writing to non-full
* filesystem, so we skip writing to a log for one second
*/
continue;
}
ngx_http_script_flush_no_cacheable_variables(r, log[l].format->flushes);
len = 0;
op = log[l].format->ops->elts;
for (i = 0; i < log[l].format->ops->nelts; i++) {
if (op[i].len == 0) {
len += op[i].getlen(r, op[i].data);
} else {
len += op[i].len;
}
}
if (log[l].syslog_peer) {
/* length of syslog's PRI and HEADER message parts */
len += sizeof("<255>Jan 01 00:00:00 ") - 1
+ ngx_cycle->hostname.len + 1
+ log[l].syslog_peer->tag.len + 2;
goto alloc_line;
}
len += NGX_LINEFEED_SIZE;
buffer = log[l].file ? log[l].file->data : NULL;
if (buffer) {
if (len > (size_t) (buffer->last - buffer->pos)) {
ngx_http_log_write(r, &log[l], buffer->start,
buffer->pos - buffer->start);
buffer->pos = buffer->start;
}
if (len <= (size_t) (buffer->last - buffer->pos)) {
p = buffer->pos;
if (buffer->event && p == buffer->start) {
ngx_add_timer(buffer->event, buffer->flush);
}
for (i = 0; i < log[l].format->ops->nelts; i++) {
p = op[i].run(r, p, &op[i]);
}
ngx_linefeed(p);
buffer->pos = p;
continue;
}
if (buffer->event && buffer->event->timer_set) {
ngx_del_timer(buffer->event);
}
}
alloc_line:
line = ngx_pnalloc(r->pool, len);
if (line == NULL) {
return NGX_ERROR;
}
p = line;
if (log[l].syslog_peer) {
p = ngx_syslog_add_header(log[l].syslog_peer, line);
}
for (i = 0; i < log[l].format->ops->nelts; i++) {
p = op[i].run(r, p, &op[i]);
}
if (log[l].syslog_peer) {
size = p - line;
n = ngx_syslog_send(log[l].syslog_peer, line, size);
if (n < 0) {
ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,
"send() to syslog failed");
} else if ((size_t) n != size) {
ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,
"send() to syslog has written only %z of %uz",
n, size);
}
continue;
}
ngx_linefeed(p);
ngx_http_log_write(r, &log[l], line, p - line);
}
return NGX_OK;
}
static void
ngx_http_log_write(ngx_http_request_t *r, ngx_http_log_t *log, u_char *buf,
size_t len)
{
u_char *name;
time_t now;
ssize_t n;
ngx_err_t err;
#if (NGX_ZLIB)
ngx_http_log_buf_t *buffer;
#endif
if (log->script == NULL) {
name = log->file->name.data;
#if (NGX_ZLIB)
buffer = log->file->data;
if (buffer && buffer->gzip) {
n = ngx_http_log_gzip(log->file->fd, buf, len, buffer->gzip,
r->connection->log);
} else {
n = ngx_write_fd(log->file->fd, buf, len);
}
#else
n = ngx_write_fd(log->file->fd, buf, len);
#endif
} else {
name = NULL;
n = ngx_http_log_script_write(r, log->script, &name, buf, len);
}
if (n == (ssize_t) len) {
return;
}
now = ngx_time();
if (n == -1) {
err = ngx_errno;
if (err == NGX_ENOSPC) {
log->disk_full_time = now;
}
if (now - log->error_log_time > 59) {
ngx_log_error(NGX_LOG_ALERT, r->connection->log, err,
ngx_write_fd_n " to \"%s\" failed", name);
log->error_log_time = now;
}
return;
}
if (now - log->error_log_time > 59) {
ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
ngx_write_fd_n " to \"%s\" was incomplete: %z of %uz",
name, n, len);
log->error_log_time = now;
}
}
static ssize_t
ngx_http_log_script_write(ngx_http_request_t *r, ngx_http_log_script_t *script,
u_char **name, u_char *buf, size_t len)
{
size_t root;
ssize_t n;
ngx_str_t log, path;
ngx_open_file_info_t of;
ngx_http_log_loc_conf_t *llcf;
ngx_http_core_loc_conf_t *clcf;
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
if (!r->root_tested) {
/* test root directory existence */
if (ngx_http_map_uri_to_path(r, &path, &root, 0) == NULL) {
/* simulate successful logging */
return len;
}
path.data[root] = '\0';
ngx_memzero(&of, sizeof(ngx_open_file_info_t));
of.valid = clcf->open_file_cache_valid;
of.min_uses = clcf->open_file_cache_min_uses;
of.test_dir = 1;
of.test_only = 1;
of.errors = clcf->open_file_cache_errors;
of.events = clcf->open_file_cache_events;
if (ngx_http_set_disable_symlinks(r, clcf, &path, &of) != NGX_OK) {
/* simulate successful logging */
return len;
}
if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)
!= NGX_OK)
{
if (of.err == 0) {
/* simulate successful logging */
return len;
}
ngx_log_error(NGX_LOG_ERR, r->connection->log, of.err,
"testing \"%s\" existence failed", path.data);
/* simulate successful logging */
return len;
}
if (!of.is_dir) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, NGX_ENOTDIR,
"testing \"%s\" existence failed", path.data);
/* simulate successful logging */
return len;
}
}
if (ngx_http_script_run(r, &log, script->lengths->elts, 1,
script->values->elts)
== NULL)
{
/* simulate successful logging */
return len;
}
log.data[log.len - 1] = '\0';
*name = log.data;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http log \"%s\"", log.data);
llcf = ngx_http_get_module_loc_conf(r, ngx_http_log_module);
ngx_memzero(&of, sizeof(ngx_open_file_info_t));
of.log = 1;
of.valid = llcf->open_file_cache_valid;
of.min_uses = llcf->open_file_cache_min_uses;
of.directio = NGX_OPEN_FILE_DIRECTIO_OFF;
if (ngx_http_set_disable_symlinks(r, clcf, &log, &of) != NGX_OK) {
/* simulate successful logging */
return len;
}
if (ngx_open_cached_file(llcf->open_file_cache, &log, &of, r->pool)
!= NGX_OK)
{
if (of.err == 0) {
/* simulate successful logging */
return len;
}
ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,
"%s \"%s\" failed", of.failed, log.data);
/* simulate successful logging */
return len;
}
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http log #%d", of.fd);
n = ngx_write_fd(of.fd, buf, len);
return n;
}
#if (NGX_ZLIB)
static ssize_t
ngx_http_log_gzip(ngx_fd_t fd, u_char *buf, size_t len, ngx_int_t level,
ngx_log_t *log)
{
int rc, wbits, memlevel;
u_char *out;
size_t size;
ssize_t n;
z_stream zstream;
ngx_err_t err;
ngx_pool_t *pool;
wbits = MAX_WBITS;
memlevel = MAX_MEM_LEVEL - 1;
while ((ssize_t) len < ((1 << (wbits - 1)) - 262)) {
wbits--;
memlevel--;
}
/*
* This is a formula from deflateBound() for conservative upper bound of
* compressed data plus 18 bytes of gzip wrapper.
*/
size = len + ((len + 7) >> 3) + ((len + 63) >> 6) + 5 + 18;
ngx_memzero(&zstream, sizeof(z_stream));
pool = ngx_create_pool(256, log);
if (pool == NULL) {
/* simulate successful logging */
return len;
}
pool->log = log;
zstream.zalloc = ngx_http_log_gzip_alloc;
zstream.zfree = ngx_http_log_gzip_free;
zstream.opaque = pool;
out = ngx_pnalloc(pool, size);
if (out == NULL) {
goto done;
}
zstream.next_in = buf;
zstream.avail_in = len;
zstream.next_out = out;
zstream.avail_out = size;
rc = deflateInit2(&zstream, (int) level, Z_DEFLATED, wbits + 16, memlevel,
Z_DEFAULT_STRATEGY);
if (rc != Z_OK) {
ngx_log_error(NGX_LOG_ALERT, log, 0, "deflateInit2() failed: %d", rc);
goto done;
}
ngx_log_debug4(NGX_LOG_DEBUG_HTTP, log, 0,
"deflate in: ni:%p no:%p ai:%ud ao:%ud",
zstream.next_in, zstream.next_out,
zstream.avail_in, zstream.avail_out);
rc = deflate(&zstream, Z_FINISH);
if (rc != Z_STREAM_END) {
ngx_log_error(NGX_LOG_ALERT, log, 0,
"deflate(Z_FINISH) failed: %d", rc);
goto done;
}
ngx_log_debug5(NGX_LOG_DEBUG_HTTP, log, 0,
"deflate out: ni:%p no:%p ai:%ud ao:%ud rc:%d",
zstream.next_in, zstream.next_out,
zstream.avail_in, zstream.avail_out,
rc);
size -= zstream.avail_out;
rc = deflateEnd(&zstream);
if (rc != Z_OK) {
ngx_log_error(NGX_LOG_ALERT, log, 0, "deflateEnd() failed: %d", rc);
goto done;
}
n = ngx_write_fd(fd, out, size);
if (n != (ssize_t) size) {
err = (n == -1) ? ngx_errno : 0;
ngx_destroy_pool(pool);
ngx_set_errno(err);
return -1;
}
done:
ngx_destroy_pool(pool);
/* simulate successful logging */
return len;
}
static void *
ngx_http_log_gzip_alloc(void *opaque, u_int items, u_int size)
{
ngx_pool_t *pool = opaque;
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pool->log, 0,
"gzip alloc: n:%ud s:%ud", items, size);
return ngx_palloc(pool, items * size);
}
static void
ngx_http_log_gzip_free(void *opaque, void *address)
{
#if 0
ngx_pool_t *pool = opaque;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pool->log, 0, "gzip free: %p", address);
#endif
}
#endif
static void
ngx_http_log_flush(ngx_open_file_t *file, ngx_log_t *log)
{
size_t len;
ssize_t n;
ngx_http_log_buf_t *buffer;
buffer = file->data;
len = buffer->pos - buffer->start;
if (len == 0) {
return;
}
#if (NGX_ZLIB)
if (buffer->gzip) {
n = ngx_http_log_gzip(file->fd, buffer->start, len, buffer->gzip, log);
} else {
n = ngx_write_fd(file->fd, buffer->start, len);
}
#else
n = ngx_write_fd(file->fd, buffer->start, len);
#endif
if (n == -1) {
ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
ngx_write_fd_n " to \"%s\" failed",
file->name.data);
} else if ((size_t) n != len) {
ngx_log_error(NGX_LOG_ALERT, log, 0,
ngx_write_fd_n " to \"%s\" was incomplete: %z of %uz",
file->name.data, n, len);
}
buffer->pos = buffer->start;
if (buffer->event && buffer->event->timer_set) {
ngx_del_timer(buffer->event);
}
}
static void
ngx_http_log_flush_handler(ngx_event_t *ev)
{
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0,
"http log buffer flush handler");
ngx_http_log_flush(ev->data, ev->log);
}
static u_char *
ngx_http_log_copy_short(ngx_http_request_t *r, u_char *buf,
ngx_http_log_op_t *op)
{
size_t len;
uintptr_t data;
len = op->len;
data = op->data;
while (len--) {
*buf++ = (u_char) (data & 0xff);
data >>= 8;
}
return buf;
}
static u_char *
ngx_http_log_copy_long(ngx_http_request_t *r, u_char *buf,
ngx_http_log_op_t *op)
{
return ngx_cpymem(buf, (u_char *) op->data, op->len);
}
static u_char *
ngx_http_log_pipe(ngx_http_request_t *r, u_char *buf, ngx_http_log_op_t *op)
{
if (r->pipeline) {
*buf = 'p';
} else {
*buf = '.';
}
return buf + 1;
}
static u_char *
ngx_http_log_time(ngx_http_request_t *r, u_char *buf, ngx_http_log_op_t *op)
{
return ngx_cpymem(buf, ngx_cached_http_log_time.data,
ngx_cached_http_log_time.len);
}
static u_char *
ngx_http_log_iso8601(ngx_http_request_t *r, u_char *buf, ngx_http_log_op_t *op)
{
return ngx_cpymem(buf, ngx_cached_http_log_iso8601.data,
ngx_cached_http_log_iso8601.len);
}
static u_char *
ngx_http_log_msec(ngx_http_request_t *r, u_char *buf, ngx_http_log_op_t *op)
{
ngx_time_t *tp;
tp = ngx_timeofday();
return ngx_sprintf(buf, "%T.%03M", tp->sec, tp->msec);
}
static u_char *
ngx_http_log_request_time(ngx_http_request_t *r, u_char *buf,
ngx_http_log_op_t *op)
{
ngx_time_t *tp;
ngx_msec_int_t ms;
tp = ngx_timeofday();
ms = (ngx_msec_int_t)
((tp->sec - r->start_sec) * 1000 + (tp->msec - r->start_msec));
ms = ngx_max(ms, 0);
return ngx_sprintf(buf, "%T.%03M", (time_t) ms / 1000, ms % 1000);
}
static u_char *
ngx_http_log_status(ngx_http_request_t *r, u_char *buf, ngx_http_log_op_t *op)
{
ngx_uint_t status;
if (r->err_status) {
status = r->err_status;
} else if (r->headers_out.status) {
status = r->headers_out.status;
} else if (r->http_version == NGX_HTTP_VERSION_9) {
status = 9;
} else {
status = 0;
}
return ngx_sprintf(buf, "%03ui", status);
}
static u_char *
ngx_http_log_bytes_sent(ngx_http_request_t *r, u_char *buf,
ngx_http_log_op_t *op)
{
return ngx_sprintf(buf, "%O", r->connection->sent);
}
/*
* although there is a real $body_bytes_sent variable,
* this log operation code function is more optimized for logging
*/
static u_char *
ngx_http_log_body_bytes_sent(ngx_http_request_t *r, u_char *buf,
ngx_http_log_op_t *op)
{
off_t length;
length = r->connection->sent - r->header_size;
if (length > 0) {
return ngx_sprintf(buf, "%O", length);
}
*buf = '0';
return buf + 1;
}
static u_char *
ngx_http_log_request_length(ngx_http_request_t *r, u_char *buf,
ngx_http_log_op_t *op)
{
return ngx_sprintf(buf, "%O", r->request_length);
}
static ngx_int_t
ngx_http_log_variable_compile(ngx_conf_t *cf, ngx_http_log_op_t *op,
ngx_str_t *value, ngx_uint_t escape)
{
ngx_int_t index;
index = ngx_http_get_variable_index(cf, value);
if (index == NGX_ERROR) {
return NGX_ERROR;
}
op->len = 0;
switch (escape) {
case NGX_HTTP_LOG_ESCAPE_JSON:
op->getlen = ngx_http_log_json_variable_getlen;
op->run = ngx_http_log_json_variable;
break;
case NGX_HTTP_LOG_ESCAPE_NONE:
op->getlen = ngx_http_log_unescaped_variable_getlen;
op->run = ngx_http_log_unescaped_variable;
break;
default: /* NGX_HTTP_LOG_ESCAPE_DEFAULT */
op->getlen = ngx_http_log_variable_getlen;
op->run = ngx_http_log_variable;
}
op->data = index;
return NGX_OK;
}
static size_t
ngx_http_log_variable_getlen(ngx_http_request_t *r, uintptr_t data)
{
uintptr_t len;
ngx_http_variable_value_t *value;
value = ngx_http_get_indexed_variable(r, data);
if (value == NULL || value->not_found) {
return 1;
}
len = ngx_http_log_escape(NULL, value->data, value->len);
value->escape = len ? 1 : 0;
return value->len + len * 3;
}
static u_char *
ngx_http_log_variable(ngx_http_request_t *r, u_char *buf, ngx_http_log_op_t *op)
{
ngx_http_variable_value_t *value;
value = ngx_http_get_indexed_variable(r, op->data);
if (value == NULL || value->not_found) {
*buf = '-';
return buf + 1;
}
if (value->escape == 0) {
return ngx_cpymem(buf, value->data, value->len);
} else {
return (u_char *) ngx_http_log_escape(buf, value->data, value->len);
}
}
static uintptr_t
ngx_http_log_escape(u_char *dst, u_char *src, size_t size)
{
ngx_uint_t n;
static u_char hex[] = "0123456789ABCDEF";
static uint32_t escape[] = {
0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
/* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */
0x00000004, /* 0000 0000 0000 0000 0000 0000 0000 0100 */
/* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */
0x10000000, /* 0001 0000 0000 0000 0000 0000 0000 0000 */
/* ~}| {zyx wvut srqp onml kjih gfed cba` */
0x80000000, /* 1000 0000 0000 0000 0000 0000 0000 0000 */
0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
};
if (dst == NULL) {
/* find the number of the characters to be escaped */
n = 0;
while (size) {
if (escape[*src >> 5] & (1U << (*src & 0x1f))) {
n++;
}
src++;
size--;
}
return (uintptr_t) n;
}
while (size) {
if (escape[*src >> 5] & (1U << (*src & 0x1f))) {
*dst++ = '\\';
*dst++ = 'x';
*dst++ = hex[*src >> 4];
*dst++ = hex[*src & 0xf];
src++;
} else {
*dst++ = *src++;
}
size--;
}
return (uintptr_t) dst;
}
static size_t
ngx_http_log_json_variable_getlen(ngx_http_request_t *r, uintptr_t data)
{
uintptr_t len;
ngx_http_variable_value_t *value;
value = ngx_http_get_indexed_variable(r, data);
if (value == NULL || value->not_found) {
return 0;
}
len = ngx_escape_json(NULL, value->data, value->len);
value->escape = len ? 1 : 0;
return value->len + len;
}
static u_char *
ngx_http_log_json_variable(ngx_http_request_t *r, u_char *buf,
ngx_http_log_op_t *op)
{
ngx_http_variable_value_t *value;
value = ngx_http_get_indexed_variable(r, op->data);
if (value == NULL || value->not_found) {
return buf;
}
if (value->escape == 0) {
return ngx_cpymem(buf, value->data, value->len);
} else {
return (u_char *) ngx_escape_json(buf, value->data, value->len);
}
}
static size_t
ngx_http_log_unescaped_variable_getlen(ngx_http_request_t *r, uintptr_t data)
{
ngx_http_variable_value_t *value;
value = ngx_http_get_indexed_variable(r, data);
if (value == NULL || value->not_found) {
return 0;
}
value->escape = 0;
return value->len;
}
static u_char *
ngx_http_log_unescaped_variable(ngx_http_request_t *r, u_char *buf,
ngx_http_log_op_t *op)
{
ngx_http_variable_value_t *value;
value = ngx_http_get_indexed_variable(r, op->data);
if (value == NULL || value->not_found) {
return buf;
}
return ngx_cpymem(buf, value->data, value->len);
}
static void *
ngx_http_log_create_main_conf(ngx_conf_t *cf)
{
ngx_http_log_main_conf_t *conf;
ngx_http_log_fmt_t *fmt;
conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_log_main_conf_t));
if (conf == NULL) {
return NULL;
}
if (ngx_array_init(&conf->formats, cf->pool, 4, sizeof(ngx_http_log_fmt_t))
!= NGX_OK)
{
return NULL;
}
fmt = ngx_array_push(&conf->formats);
if (fmt == NULL) {
return NULL;
}
ngx_str_set(&fmt->name, "combined");
fmt->flushes = NULL;
fmt->ops = ngx_array_create(cf->pool, 16, sizeof(ngx_http_log_op_t));
if (fmt->ops == NULL) {
return NULL;
}
return conf;
}
static void *
ngx_http_log_create_loc_conf(ngx_conf_t *cf)
{
ngx_http_log_loc_conf_t *conf;
conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_log_loc_conf_t));
if (conf == NULL) {
return NULL;
}
conf->open_file_cache = NGX_CONF_UNSET_PTR;
return conf;
}
static char *
ngx_http_log_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
{
ngx_http_log_loc_conf_t *prev = parent;
ngx_http_log_loc_conf_t *conf = child;
ngx_http_log_t *log;
ngx_http_log_fmt_t *fmt;
ngx_http_log_main_conf_t *lmcf;
if (conf->open_file_cache == NGX_CONF_UNSET_PTR) {
conf->open_file_cache = prev->open_file_cache;
conf->open_file_cache_valid = prev->open_file_cache_valid;
conf->open_file_cache_min_uses = prev->open_file_cache_min_uses;
if (conf->open_file_cache == NGX_CONF_UNSET_PTR) {
conf->open_file_cache = NULL;
}
}
if (conf->logs || conf->off) {
return NGX_CONF_OK;
}
conf->logs = prev->logs;
conf->off = prev->off;
if (conf->logs || conf->off) {
return NGX_CONF_OK;
}
conf->logs = ngx_array_create(cf->pool, 2, sizeof(ngx_http_log_t));
if (conf->logs == NULL) {
return NGX_CONF_ERROR;
}
log = ngx_array_push(conf->logs);
if (log == NULL) {
return NGX_CONF_ERROR;
}
ngx_memzero(log, sizeof(ngx_http_log_t));
log->file = ngx_conf_open_file(cf->cycle, &ngx_http_access_log);
if (log->file == NULL) {
return NGX_CONF_ERROR;
}
lmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_log_module);
fmt = lmcf->formats.elts;
/* the default "combined" format */
log->format = &fmt[0];
lmcf->combined_used = 1;
return NGX_CONF_OK;
}
static char *
ngx_http_log_set_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_log_loc_conf_t *llcf = conf;
ssize_t size;
ngx_int_t gzip;
ngx_uint_t i, n;
ngx_msec_t flush;
ngx_str_t *value, name, s;
ngx_http_log_t *log;
ngx_syslog_peer_t *peer;
ngx_http_log_buf_t *buffer;
ngx_http_log_fmt_t *fmt;
ngx_http_log_main_conf_t *lmcf;
ngx_http_script_compile_t sc;
ngx_http_compile_complex_value_t ccv;
value = cf->args->elts;
if (ngx_strcmp(value[1].data, "off") == 0) {
llcf->off = 1;
if (cf->args->nelts == 2) {
return NGX_CONF_OK;
}
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid parameter \"%V\"", &value[2]);
return NGX_CONF_ERROR;
}
if (llcf->logs == NULL) {
llcf->logs = ngx_array_create(cf->pool, 2, sizeof(ngx_http_log_t));
if (llcf->logs == NULL) {
return NGX_CONF_ERROR;
}
}
lmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_log_module);
log = ngx_array_push(llcf->logs);
if (log == NULL) {
return NGX_CONF_ERROR;
}
ngx_memzero(log, sizeof(ngx_http_log_t));
if (ngx_strncmp(value[1].data, "syslog:", 7) == 0) {
peer = ngx_pcalloc(cf->pool, sizeof(ngx_syslog_peer_t));
if (peer == NULL) {
return NGX_CONF_ERROR;
}
if (ngx_syslog_process_conf(cf, peer) != NGX_CONF_OK) {
return NGX_CONF_ERROR;
}
log->syslog_peer = peer;
goto process_formats;
}
n = ngx_http_script_variables_count(&value[1]);
if (n == 0) {
log->file = ngx_conf_open_file(cf->cycle, &value[1]);
if (log->file == NULL) {
return NGX_CONF_ERROR;
}
} else {
if (ngx_conf_full_name(cf->cycle, &value[1], 0) != NGX_OK) {
return NGX_CONF_ERROR;
}
log->script = ngx_pcalloc(cf->pool, sizeof(ngx_http_log_script_t));
if (log->script == NULL) {
return NGX_CONF_ERROR;
}
ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
sc.cf = cf;
sc.source = &value[1];
sc.lengths = &log->script->lengths;
sc.values = &log->script->values;
sc.variables = n;
sc.complete_lengths = 1;
sc.complete_values = 1;
if (ngx_http_script_compile(&sc) != NGX_OK) {
return NGX_CONF_ERROR;
}
}
process_formats:
if (cf->args->nelts >= 3) {
name = value[2];
if (ngx_strcmp(name.data, "combined") == 0) {
lmcf->combined_used = 1;
}
} else {
ngx_str_set(&name, "combined");
lmcf->combined_used = 1;
}
fmt = lmcf->formats.elts;
for (i = 0; i < lmcf->formats.nelts; i++) {
if (fmt[i].name.len == name.len
&& ngx_strcasecmp(fmt[i].name.data, name.data) == 0)
{
log->format = &fmt[i];
break;
}
}
if (log->format == NULL) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"unknown log format \"%V\"", &name);
return NGX_CONF_ERROR;
}
size = 0;
flush = 0;
gzip = 0;
for (i = 3; i < cf->args->nelts; i++) {
if (ngx_strncmp(value[i].data, "buffer=", 7) == 0) {
s.len = value[i].len - 7;
s.data = value[i].data + 7;
size = ngx_parse_size(&s);
if (size == NGX_ERROR || size == 0) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid buffer size \"%V\"", &s);
return NGX_CONF_ERROR;
}
continue;
}
if (ngx_strncmp(value[i].data, "flush=", 6) == 0) {
s.len = value[i].len - 6;
s.data = value[i].data + 6;
flush = ngx_parse_time(&s, 0);
if (flush == (ngx_msec_t) NGX_ERROR || flush == 0) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid flush time \"%V\"", &s);
return NGX_CONF_ERROR;
}
continue;
}
if (ngx_strncmp(value[i].data, "gzip", 4) == 0
&& (value[i].len == 4 || value[i].data[4] == '='))
{
#if (NGX_ZLIB)
if (size == 0) {
size = 64 * 1024;
}
if (value[i].len == 4) {
gzip = Z_BEST_SPEED;
continue;
}
s.len = value[i].len - 5;
s.data = value[i].data + 5;
gzip = ngx_atoi(s.data, s.len);
if (gzip < 1 || gzip > 9) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid compression level \"%V\"", &s);
return NGX_CONF_ERROR;
}
continue;
#else
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"nginx was built without zlib support");
return NGX_CONF_ERROR;
#endif
}
if (ngx_strncmp(value[i].data, "if=", 3) == 0) {
s.len = value[i].len - 3;
s.data = value[i].data + 3;
ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
ccv.cf = cf;
ccv.value = &s;
ccv.complex_value = ngx_palloc(cf->pool,
sizeof(ngx_http_complex_value_t));
if (ccv.complex_value == NULL) {
return NGX_CONF_ERROR;
}
if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
return NGX_CONF_ERROR;
}
log->filter = ccv.complex_value;
continue;
}
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid parameter \"%V\"", &value[i]);
return NGX_CONF_ERROR;
}
if (flush && size == 0) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"no buffer is defined for access_log \"%V\"",
&value[1]);
return NGX_CONF_ERROR;
}
if (size) {
if (log->script) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"buffered logs cannot have variables in name");
return NGX_CONF_ERROR;
}
if (log->syslog_peer) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"logs to syslog cannot be buffered");
return NGX_CONF_ERROR;
}
if (log->file->data) {
buffer = log->file->data;
if (buffer->last - buffer->start != size
|| buffer->flush != flush
|| buffer->gzip != gzip)
{
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"access_log \"%V\" already defined "
"with conflicting parameters",
&value[1]);
return NGX_CONF_ERROR;
}
return NGX_CONF_OK;
}
buffer = ngx_pcalloc(cf->pool, sizeof(ngx_http_log_buf_t));
if (buffer == NULL) {
return NGX_CONF_ERROR;
}
buffer->start = ngx_pnalloc(cf->pool, size);
if (buffer->start == NULL) {
return NGX_CONF_ERROR;
}
buffer->pos = buffer->start;
buffer->last = buffer->start + size;
if (flush) {
buffer->event = ngx_pcalloc(cf->pool, sizeof(ngx_event_t));
if (buffer->event == NULL) {
return NGX_CONF_ERROR;
}
buffer->event->data = log->file;
buffer->event->handler = ngx_http_log_flush_handler;
buffer->event->log = &cf->cycle->new_log;
buffer->event->cancelable = 1;
buffer->flush = flush;
}
buffer->gzip = gzip;
log->file->flush = ngx_http_log_flush;
log->file->data = buffer;
}
return NGX_CONF_OK;
}
static char *
ngx_http_log_set_format(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_log_main_conf_t *lmcf = conf;
ngx_str_t *value;
ngx_uint_t i;
ngx_http_log_fmt_t *fmt;
value = cf->args->elts;
fmt = lmcf->formats.elts;
for (i = 0; i < lmcf->formats.nelts; i++) {
if (fmt[i].name.len == value[1].len
&& ngx_strcmp(fmt[i].name.data, value[1].data) == 0)
{
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"duplicate \"log_format\" name \"%V\"",
&value[1]);
return NGX_CONF_ERROR;
}
}
fmt = ngx_array_push(&lmcf->formats);
if (fmt == NULL) {
return NGX_CONF_ERROR;
}
fmt->name = value[1];
fmt->flushes = ngx_array_create(cf->pool, 4, sizeof(ngx_int_t));
if (fmt->flushes == NULL) {
return NGX_CONF_ERROR;
}
fmt->ops = ngx_array_create(cf->pool, 16, sizeof(ngx_http_log_op_t));
if (fmt->ops == NULL) {
return NGX_CONF_ERROR;
}
return ngx_http_log_compile_format(cf, fmt->flushes, fmt->ops, cf->args, 2);
}
static char *
ngx_http_log_compile_format(ngx_conf_t *cf, ngx_array_t *flushes,
ngx_array_t *ops, ngx_array_t *args, ngx_uint_t s)
{
u_char *data, *p, ch;
size_t i, len;
ngx_str_t *value, var;
ngx_int_t *flush;
ngx_uint_t bracket, escape;
ngx_http_log_op_t *op;
ngx_http_log_var_t *v;
escape = NGX_HTTP_LOG_ESCAPE_DEFAULT;
value = args->elts;
if (s < args->nelts && ngx_strncmp(value[s].data, "escape=", 7) == 0) {
data = value[s].data + 7;
if (ngx_strcmp(data, "json") == 0) {
escape = NGX_HTTP_LOG_ESCAPE_JSON;
} else if (ngx_strcmp(data, "none") == 0) {
escape = NGX_HTTP_LOG_ESCAPE_NONE;
} else if (ngx_strcmp(data, "default") != 0) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"unknown log format escaping \"%s\"", data);
return NGX_CONF_ERROR;
}
s++;
}
for ( /* void */ ; s < args->nelts; s++) {
i = 0;
while (i < value[s].len) {
op = ngx_array_push(ops);
if (op == NULL) {
return NGX_CONF_ERROR;
}
data = &value[s].data[i];
if (value[s].data[i] == '$') {
if (++i == value[s].len) {
goto invalid;
}
if (value[s].data[i] == '{') {
bracket = 1;
if (++i == value[s].len) {
goto invalid;
}
var.data = &value[s].data[i];
} else {
bracket = 0;
var.data = &value[s].data[i];
}
for (var.len = 0; i < value[s].len; i++, var.len++) {
ch = value[s].data[i];
if (ch == '}' && bracket) {
i++;
bracket = 0;
break;
}
if ((ch >= 'A' && ch <= 'Z')
|| (ch >= 'a' && ch <= 'z')
|| (ch >= '0' && ch <= '9')
|| ch == '_')
{
continue;
}
break;
}
if (bracket) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"the closing bracket in \"%V\" "
"variable is missing", &var);
return NGX_CONF_ERROR;
}
if (var.len == 0) {
goto invalid;
}
for (v = ngx_http_log_vars; v->name.len; v++) {
if (v->name.len == var.len
&& ngx_strncmp(v->name.data, var.data, var.len) == 0)
{
op->len = v->len;
op->getlen = NULL;
op->run = v->run;
op->data = 0;
goto found;
}
}
if (ngx_http_log_variable_compile(cf, op, &var, escape)
!= NGX_OK)
{
return NGX_CONF_ERROR;
}
if (flushes) {
flush = ngx_array_push(flushes);
if (flush == NULL) {
return NGX_CONF_ERROR;
}
*flush = op->data; /* variable index */
}
found:
continue;
}
i++;
while (i < value[s].len && value[s].data[i] != '$') {
i++;
}
len = &value[s].data[i] - data;
if (len) {
op->len = len;
op->getlen = NULL;
if (len <= sizeof(uintptr_t)) {
op->run = ngx_http_log_copy_short;
op->data = 0;
while (len--) {
op->data <<= 8;
op->data |= data[len];
}
} else {
op->run = ngx_http_log_copy_long;
p = ngx_pnalloc(cf->pool, len);
if (p == NULL) {
return NGX_CONF_ERROR;
}
ngx_memcpy(p, data, len);
op->data = (uintptr_t) p;
}
}
}
}
return NGX_CONF_OK;
invalid:
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameter \"%s\"", data);
return NGX_CONF_ERROR;
}
static char *
ngx_http_log_open_file_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_log_loc_conf_t *llcf = conf;
time_t inactive, valid;
ngx_str_t *value, s;
ngx_int_t max, min_uses;
ngx_uint_t i;
if (llcf->open_file_cache != NGX_CONF_UNSET_PTR) {
return "is duplicate";
}
value = cf->args->elts;
max = 0;
inactive = 10;
valid = 60;
min_uses = 1;
for (i = 1; i < cf->args->nelts; i++) {
if (ngx_strncmp(value[i].data, "max=", 4) == 0) {
max = ngx_atoi(value[i].data + 4, value[i].len - 4);
if (max == NGX_ERROR) {
goto failed;
}
continue;
}
if (ngx_strncmp(value[i].data, "inactive=", 9) == 0) {
s.len = value[i].len - 9;
s.data = value[i].data + 9;
inactive = ngx_parse_time(&s, 1);
if (inactive == (time_t) NGX_ERROR) {
goto failed;
}
continue;
}
if (ngx_strncmp(value[i].data, "min_uses=", 9) == 0) {
min_uses = ngx_atoi(value[i].data + 9, value[i].len - 9);
if (min_uses == NGX_ERROR) {
goto failed;
}
continue;
}
if (ngx_strncmp(value[i].data, "valid=", 6) == 0) {
s.len = value[i].len - 6;
s.data = value[i].data + 6;
valid = ngx_parse_time(&s, 1);
if (valid == (time_t) NGX_ERROR) {
goto failed;
}
continue;
}
if (ngx_strcmp(value[i].data, "off") == 0) {
llcf->open_file_cache = NULL;
continue;
}
failed:
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid \"open_log_file_cache\" parameter \"%V\"",
&value[i]);
return NGX_CONF_ERROR;
}
if (llcf->open_file_cache == NULL) {
return NGX_CONF_OK;
}
if (max == 0) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"\"open_log_file_cache\" must have \"max\" parameter");
return NGX_CONF_ERROR;
}
llcf->open_file_cache = ngx_open_file_cache_init(cf->pool, max, inactive);
if (llcf->open_file_cache) {
llcf->open_file_cache_valid = valid;
llcf->open_file_cache_min_uses = min_uses;
return NGX_CONF_OK;
}
return NGX_CONF_ERROR;
}
static ngx_int_t
ngx_http_log_init(ngx_conf_t *cf)
{
ngx_str_t *value;
ngx_array_t a;
ngx_http_handler_pt *h;
ngx_http_log_fmt_t *fmt;
ngx_http_log_main_conf_t *lmcf;
ngx_http_core_main_conf_t *cmcf;
lmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_log_module);
if (lmcf->combined_used) {
if (ngx_array_init(&a, cf->pool, 1, sizeof(ngx_str_t)) != NGX_OK) {
return NGX_ERROR;
}
value = ngx_array_push(&a);
if (value == NULL) {
return NGX_ERROR;
}
*value = ngx_http_combined_fmt;
fmt = lmcf->formats.elts;
if (ngx_http_log_compile_format(cf, NULL, fmt->ops, &a, 0)
!= NGX_CONF_OK)
{
return NGX_ERROR;
}
}
cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
h = ngx_array_push(&cmcf->phases[NGX_HTTP_LOG_PHASE].handlers);
if (h == NULL) {
return NGX_ERROR;
}
*h = ngx_http_log_handler;
return NGX_OK;
}
nginx-1.14.0/src/http/modules/ngx_http_map_module.c 000644 001751 001751 00000035244 13265410474 023543 0 ustar 00mdounin mdounin 000000 000000
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#include
#include
#include
typedef struct {
ngx_uint_t hash_max_size;
ngx_uint_t hash_bucket_size;
} ngx_http_map_conf_t;
typedef struct {
ngx_hash_keys_arrays_t keys;
ngx_array_t *values_hash;
#if (NGX_PCRE)
ngx_array_t regexes;
#endif
ngx_http_variable_value_t *default_value;
ngx_conf_t *cf;
unsigned hostnames:1;
unsigned no_cacheable:1;
} ngx_http_map_conf_ctx_t;
typedef struct {
ngx_http_map_t map;
ngx_http_complex_value_t value;
ngx_http_variable_value_t *default_value;
ngx_uint_t hostnames; /* unsigned hostnames:1 */
} ngx_http_map_ctx_t;
static int ngx_libc_cdecl ngx_http_map_cmp_dns_wildcards(const void *one,
const void *two);
static void *ngx_http_map_create_conf(ngx_conf_t *cf);
static char *ngx_http_map_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static char *ngx_http_map(ngx_conf_t *cf, ngx_command_t *dummy, void *conf);
static ngx_command_t ngx_http_map_commands[] = {
{ ngx_string("map"),
NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE2,
ngx_http_map_block,
NGX_HTTP_MAIN_CONF_OFFSET,
0,
NULL },
{ ngx_string("map_hash_max_size"),
NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,
ngx_conf_set_num_slot,
NGX_HTTP_MAIN_CONF_OFFSET,
offsetof(ngx_http_map_conf_t, hash_max_size),
NULL },
{ ngx_string("map_hash_bucket_size"),
NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,
ngx_conf_set_num_slot,
NGX_HTTP_MAIN_CONF_OFFSET,
offsetof(ngx_http_map_conf_t, hash_bucket_size),
NULL },
ngx_null_command
};
static ngx_http_module_t ngx_http_map_module_ctx = {
NULL, /* preconfiguration */
NULL, /* postconfiguration */
ngx_http_map_create_conf, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
NULL, /* create location configuration */
NULL /* merge location configuration */
};
ngx_module_t ngx_http_map_module = {
NGX_MODULE_V1,
&ngx_http_map_module_ctx, /* module context */
ngx_http_map_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static ngx_int_t
ngx_http_map_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
uintptr_t data)
{
ngx_http_map_ctx_t *map = (ngx_http_map_ctx_t *) data;
ngx_str_t val, str;
ngx_http_complex_value_t *cv;
ngx_http_variable_value_t *value;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http map started");
if (ngx_http_complex_value(r, &map->value, &val) != NGX_OK) {
return NGX_ERROR;
}
if (map->hostnames && val.len > 0 && val.data[val.len - 1] == '.') {
val.len--;
}
value = ngx_http_map_find(r, &map->map, &val);
if (value == NULL) {
value = map->default_value;
}
if (!value->valid) {
cv = (ngx_http_complex_value_t *) value->data;
if (ngx_http_complex_value(r, cv, &str) != NGX_OK) {
return NGX_ERROR;
}
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->len = str.len;
v->data = str.data;
} else {
*v = *value;
}
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http map: \"%V\" \"%v\"", &val, v);
return NGX_OK;
}
static void *
ngx_http_map_create_conf(ngx_conf_t *cf)
{
ngx_http_map_conf_t *mcf;
mcf = ngx_palloc(cf->pool, sizeof(ngx_http_map_conf_t));
if (mcf == NULL) {
return NULL;
}
mcf->hash_max_size = NGX_CONF_UNSET_UINT;
mcf->hash_bucket_size = NGX_CONF_UNSET_UINT;
return mcf;
}
static char *
ngx_http_map_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_map_conf_t *mcf = conf;
char *rv;
ngx_str_t *value, name;
ngx_conf_t save;
ngx_pool_t *pool;
ngx_hash_init_t hash;
ngx_http_map_ctx_t *map;
ngx_http_variable_t *var;
ngx_http_map_conf_ctx_t ctx;
ngx_http_compile_complex_value_t ccv;
if (mcf->hash_max_size == NGX_CONF_UNSET_UINT) {
mcf->hash_max_size = 2048;
}
if (mcf->hash_bucket_size == NGX_CONF_UNSET_UINT) {
mcf->hash_bucket_size = ngx_cacheline_size;
} else {
mcf->hash_bucket_size = ngx_align(mcf->hash_bucket_size,
ngx_cacheline_size);
}
map = ngx_pcalloc(cf->pool, sizeof(ngx_http_map_ctx_t));
if (map == NULL) {
return NGX_CONF_ERROR;
}
value = cf->args->elts;
ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
ccv.cf = cf;
ccv.value = &value[1];
ccv.complex_value = &map->value;
if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
return NGX_CONF_ERROR;
}
name = value[2];
if (name.data[0] != '$') {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid variable name \"%V\"", &name);
return NGX_CONF_ERROR;
}
name.len--;
name.data++;
var = ngx_http_add_variable(cf, &name, NGX_HTTP_VAR_CHANGEABLE);
if (var == NULL) {
return NGX_CONF_ERROR;
}
var->get_handler = ngx_http_map_variable;
var->data = (uintptr_t) map;
pool = ngx_create_pool(NGX_DEFAULT_POOL_SIZE, cf->log);
if (pool == NULL) {
return NGX_CONF_ERROR;
}
ctx.keys.pool = cf->pool;
ctx.keys.temp_pool = pool;
if (ngx_hash_keys_array_init(&ctx.keys, NGX_HASH_LARGE) != NGX_OK) {
ngx_destroy_pool(pool);
return NGX_CONF_ERROR;
}
ctx.values_hash = ngx_pcalloc(pool, sizeof(ngx_array_t) * ctx.keys.hsize);
if (ctx.values_hash == NULL) {
ngx_destroy_pool(pool);
return NGX_CONF_ERROR;
}
#if (NGX_PCRE)
if (ngx_array_init(&ctx.regexes, cf->pool, 2, sizeof(ngx_http_map_regex_t))
!= NGX_OK)
{
ngx_destroy_pool(pool);
return NGX_CONF_ERROR;
}
#endif
ctx.default_value = NULL;
ctx.cf = &save;
ctx.hostnames = 0;
ctx.no_cacheable = 0;
save = *cf;
cf->pool = pool;
cf->ctx = &ctx;
cf->handler = ngx_http_map;
cf->handler_conf = conf;
rv = ngx_conf_parse(cf, NULL);
*cf = save;
if (rv != NGX_CONF_OK) {
ngx_destroy_pool(pool);
return rv;
}
if (ctx.no_cacheable) {
var->flags |= NGX_HTTP_VAR_NOCACHEABLE;
}
map->default_value = ctx.default_value ? ctx.default_value:
&ngx_http_variable_null_value;
map->hostnames = ctx.hostnames;
hash.key = ngx_hash_key_lc;
hash.max_size = mcf->hash_max_size;
hash.bucket_size = mcf->hash_bucket_size;
hash.name = "map_hash";
hash.pool = cf->pool;
if (ctx.keys.keys.nelts) {
hash.hash = &map->map.hash.hash;
hash.temp_pool = NULL;
if (ngx_hash_init(&hash, ctx.keys.keys.elts, ctx.keys.keys.nelts)
!= NGX_OK)
{
ngx_destroy_pool(pool);
return NGX_CONF_ERROR;
}
}
if (ctx.keys.dns_wc_head.nelts) {
ngx_qsort(ctx.keys.dns_wc_head.elts,
(size_t) ctx.keys.dns_wc_head.nelts,
sizeof(ngx_hash_key_t), ngx_http_map_cmp_dns_wildcards);
hash.hash = NULL;
hash.temp_pool = pool;
if (ngx_hash_wildcard_init(&hash, ctx.keys.dns_wc_head.elts,
ctx.keys.dns_wc_head.nelts)
!= NGX_OK)
{
ngx_destroy_pool(pool);
return NGX_CONF_ERROR;
}
map->map.hash.wc_head = (ngx_hash_wildcard_t *) hash.hash;
}
if (ctx.keys.dns_wc_tail.nelts) {
ngx_qsort(ctx.keys.dns_wc_tail.elts,
(size_t) ctx.keys.dns_wc_tail.nelts,
sizeof(ngx_hash_key_t), ngx_http_map_cmp_dns_wildcards);
hash.hash = NULL;
hash.temp_pool = pool;
if (ngx_hash_wildcard_init(&hash, ctx.keys.dns_wc_tail.elts,
ctx.keys.dns_wc_tail.nelts)
!= NGX_OK)
{
ngx_destroy_pool(pool);
return NGX_CONF_ERROR;
}
map->map.hash.wc_tail = (ngx_hash_wildcard_t *) hash.hash;
}
#if (NGX_PCRE)
if (ctx.regexes.nelts) {
map->map.regex = ctx.regexes.elts;
map->map.nregex = ctx.regexes.nelts;
}
#endif
ngx_destroy_pool(pool);
return rv;
}
static int ngx_libc_cdecl
ngx_http_map_cmp_dns_wildcards(const void *one, const void *two)
{
ngx_hash_key_t *first, *second;
first = (ngx_hash_key_t *) one;
second = (ngx_hash_key_t *) two;
return ngx_dns_strcmp(first->key.data, second->key.data);
}
static char *
ngx_http_map(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
{
u_char *data;
size_t len;
ngx_int_t rv;
ngx_str_t *value, v;
ngx_uint_t i, key;
ngx_http_map_conf_ctx_t *ctx;
ngx_http_complex_value_t cv, *cvp;
ngx_http_variable_value_t *var, **vp;
ngx_http_compile_complex_value_t ccv;
ctx = cf->ctx;
value = cf->args->elts;
if (cf->args->nelts == 1
&& ngx_strcmp(value[0].data, "hostnames") == 0)
{
ctx->hostnames = 1;
return NGX_CONF_OK;
}
if (cf->args->nelts == 1
&& ngx_strcmp(value[0].data, "volatile") == 0)
{
ctx->no_cacheable = 1;
return NGX_CONF_OK;
}
if (cf->args->nelts != 2) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid number of the map parameters");
return NGX_CONF_ERROR;
}
if (ngx_strcmp(value[0].data, "include") == 0) {
return ngx_conf_include(cf, dummy, conf);
}
key = 0;
for (i = 0; i < value[1].len; i++) {
key = ngx_hash(key, value[1].data[i]);
}
key %= ctx->keys.hsize;
vp = ctx->values_hash[key].elts;
if (vp) {
for (i = 0; i < ctx->values_hash[key].nelts; i++) {
if (vp[i]->valid) {
data = vp[i]->data;
len = vp[i]->len;
} else {
cvp = (ngx_http_complex_value_t *) vp[i]->data;
data = cvp->value.data;
len = cvp->value.len;
}
if (value[1].len != len) {
continue;
}
if (ngx_strncmp(value[1].data, data, len) == 0) {
var = vp[i];
goto found;
}
}
} else {
if (ngx_array_init(&ctx->values_hash[key], cf->pool, 4,
sizeof(ngx_http_variable_value_t *))
!= NGX_OK)
{
return NGX_CONF_ERROR;
}
}
var = ngx_palloc(ctx->keys.pool, sizeof(ngx_http_variable_value_t));
if (var == NULL) {
return NGX_CONF_ERROR;
}
v.len = value[1].len;
v.data = ngx_pstrdup(ctx->keys.pool, &value[1]);
if (v.data == NULL) {
return NGX_CONF_ERROR;
}
ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
ccv.cf = ctx->cf;
ccv.value = &v;
ccv.complex_value = &cv;
if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
return NGX_CONF_ERROR;
}
if (cv.lengths != NULL) {
cvp = ngx_palloc(ctx->keys.pool, sizeof(ngx_http_complex_value_t));
if (cvp == NULL) {
return NGX_CONF_ERROR;
}
*cvp = cv;
var->len = 0;
var->data = (u_char *) cvp;
var->valid = 0;
} else {
var->len = v.len;
var->data = v.data;
var->valid = 1;
}
var->no_cacheable = 0;
var->not_found = 0;
vp = ngx_array_push(&ctx->values_hash[key]);
if (vp == NULL) {
return NGX_CONF_ERROR;
}
*vp = var;
found:
if (ngx_strcmp(value[0].data, "default") == 0) {
if (ctx->default_value) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"duplicate default map parameter");
return NGX_CONF_ERROR;
}
ctx->default_value = var;
return NGX_CONF_OK;
}
#if (NGX_PCRE)
if (value[0].len && value[0].data[0] == '~') {
ngx_regex_compile_t rc;
ngx_http_map_regex_t *regex;
u_char errstr[NGX_MAX_CONF_ERRSTR];
regex = ngx_array_push(&ctx->regexes);
if (regex == NULL) {
return NGX_CONF_ERROR;
}
value[0].len--;
value[0].data++;
ngx_memzero(&rc, sizeof(ngx_regex_compile_t));
if (value[0].data[0] == '*') {
value[0].len--;
value[0].data++;
rc.options = NGX_REGEX_CASELESS;
}
rc.pattern = value[0];
rc.err.len = NGX_MAX_CONF_ERRSTR;
rc.err.data = errstr;
regex->regex = ngx_http_regex_compile(ctx->cf, &rc);
if (regex->regex == NULL) {
return NGX_CONF_ERROR;
}
regex->value = var;
return NGX_CONF_OK;
}
#endif
if (value[0].len && value[0].data[0] == '\\') {
value[0].len--;
value[0].data++;
}
rv = ngx_hash_add_key(&ctx->keys, &value[0], var,
(ctx->hostnames) ? NGX_HASH_WILDCARD_KEY : 0);
if (rv == NGX_OK) {
return NGX_CONF_OK;
}
if (rv == NGX_DECLINED) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid hostname or wildcard \"%V\"", &value[0]);
}
if (rv == NGX_BUSY) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"conflicting parameter \"%V\"", &value[0]);
}
return NGX_CONF_ERROR;
}
nginx-1.14.0/src/http/modules/ngx_http_memcached_module.c 000644 001751 001751 00000050267 13265410474 024676 0 ustar 00mdounin mdounin 000000 000000
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#include
#include
#include
typedef struct {
ngx_http_upstream_conf_t upstream;
ngx_int_t index;
ngx_uint_t gzip_flag;
} ngx_http_memcached_loc_conf_t;
typedef struct {
size_t rest;
ngx_http_request_t *request;
ngx_str_t key;
} ngx_http_memcached_ctx_t;
static ngx_int_t ngx_http_memcached_create_request(ngx_http_request_t *r);
static ngx_int_t ngx_http_memcached_reinit_request(ngx_http_request_t *r);
static ngx_int_t ngx_http_memcached_process_header(ngx_http_request_t *r);
static ngx_int_t ngx_http_memcached_filter_init(void *data);
static ngx_int_t ngx_http_memcached_filter(void *data, ssize_t bytes);
static void ngx_http_memcached_abort_request(ngx_http_request_t *r);
static void ngx_http_memcached_finalize_request(ngx_http_request_t *r,
ngx_int_t rc);
static void *ngx_http_memcached_create_loc_conf(ngx_conf_t *cf);
static char *ngx_http_memcached_merge_loc_conf(ngx_conf_t *cf,
void *parent, void *child);
static char *ngx_http_memcached_pass(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static ngx_conf_bitmask_t ngx_http_memcached_next_upstream_masks[] = {
{ ngx_string("error"), NGX_HTTP_UPSTREAM_FT_ERROR },
{ ngx_string("timeout"), NGX_HTTP_UPSTREAM_FT_TIMEOUT },
{ ngx_string("invalid_response"), NGX_HTTP_UPSTREAM_FT_INVALID_HEADER },
{ ngx_string("not_found"), NGX_HTTP_UPSTREAM_FT_HTTP_404 },
{ ngx_string("off"), NGX_HTTP_UPSTREAM_FT_OFF },
{ ngx_null_string, 0 }
};
static ngx_command_t ngx_http_memcached_commands[] = {
{ ngx_string("memcached_pass"),
NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,
ngx_http_memcached_pass,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
{ ngx_string("memcached_bind"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12,
ngx_http_upstream_bind_set_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_memcached_loc_conf_t, upstream.local),
NULL },
{ ngx_string("memcached_connect_timeout"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_msec_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_memcached_loc_conf_t, upstream.connect_timeout),
NULL },
{ ngx_string("memcached_send_timeout"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_msec_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_memcached_loc_conf_t, upstream.send_timeout),
NULL },
{ ngx_string("memcached_buffer_size"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_size_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_memcached_loc_conf_t, upstream.buffer_size),
NULL },
{ ngx_string("memcached_read_timeout"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_msec_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_memcached_loc_conf_t, upstream.read_timeout),
NULL },
{ ngx_string("memcached_next_upstream"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
ngx_conf_set_bitmask_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_memcached_loc_conf_t, upstream.next_upstream),
&ngx_http_memcached_next_upstream_masks },
{ ngx_string("memcached_next_upstream_tries"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_num_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_memcached_loc_conf_t, upstream.next_upstream_tries),
NULL },
{ ngx_string("memcached_next_upstream_timeout"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_msec_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_memcached_loc_conf_t, upstream.next_upstream_timeout),
NULL },
{ ngx_string("memcached_gzip_flag"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_num_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_memcached_loc_conf_t, gzip_flag),
NULL },
ngx_null_command
};
static ngx_http_module_t ngx_http_memcached_module_ctx = {
NULL, /* preconfiguration */
NULL, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
ngx_http_memcached_create_loc_conf, /* create location configuration */
ngx_http_memcached_merge_loc_conf /* merge location configuration */
};
ngx_module_t ngx_http_memcached_module = {
NGX_MODULE_V1,
&ngx_http_memcached_module_ctx, /* module context */
ngx_http_memcached_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static ngx_str_t ngx_http_memcached_key = ngx_string("memcached_key");
#define NGX_HTTP_MEMCACHED_END (sizeof(ngx_http_memcached_end) - 1)
static u_char ngx_http_memcached_end[] = CRLF "END" CRLF;
static ngx_int_t
ngx_http_memcached_handler(ngx_http_request_t *r)
{
ngx_int_t rc;
ngx_http_upstream_t *u;
ngx_http_memcached_ctx_t *ctx;
ngx_http_memcached_loc_conf_t *mlcf;
if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) {
return NGX_HTTP_NOT_ALLOWED;
}
rc = ngx_http_discard_request_body(r);
if (rc != NGX_OK) {
return rc;
}
if (ngx_http_set_content_type(r) != NGX_OK) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
if (ngx_http_upstream_create(r) != NGX_OK) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
u = r->upstream;
ngx_str_set(&u->schema, "memcached://");
u->output.tag = (ngx_buf_tag_t) &ngx_http_memcached_module;
mlcf = ngx_http_get_module_loc_conf(r, ngx_http_memcached_module);
u->conf = &mlcf->upstream;
u->create_request = ngx_http_memcached_create_request;
u->reinit_request = ngx_http_memcached_reinit_request;
u->process_header = ngx_http_memcached_process_header;
u->abort_request = ngx_http_memcached_abort_request;
u->finalize_request = ngx_http_memcached_finalize_request;
ctx = ngx_palloc(r->pool, sizeof(ngx_http_memcached_ctx_t));
if (ctx == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
ctx->request = r;
ngx_http_set_ctx(r, ctx, ngx_http_memcached_module);
u->input_filter_init = ngx_http_memcached_filter_init;
u->input_filter = ngx_http_memcached_filter;
u->input_filter_ctx = ctx;
r->main->count++;
ngx_http_upstream_init(r);
return NGX_DONE;
}
static ngx_int_t
ngx_http_memcached_create_request(ngx_http_request_t *r)
{
size_t len;
uintptr_t escape;
ngx_buf_t *b;
ngx_chain_t *cl;
ngx_http_memcached_ctx_t *ctx;
ngx_http_variable_value_t *vv;
ngx_http_memcached_loc_conf_t *mlcf;
mlcf = ngx_http_get_module_loc_conf(r, ngx_http_memcached_module);
vv = ngx_http_get_indexed_variable(r, mlcf->index);
if (vv == NULL || vv->not_found || vv->len == 0) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"the \"$memcached_key\" variable is not set");
return NGX_ERROR;
}
escape = 2 * ngx_escape_uri(NULL, vv->data, vv->len, NGX_ESCAPE_MEMCACHED);
len = sizeof("get ") - 1 + vv->len + escape + sizeof(CRLF) - 1;
b = ngx_create_temp_buf(r->pool, len);
if (b == NULL) {
return NGX_ERROR;
}
cl = ngx_alloc_chain_link(r->pool);
if (cl == NULL) {
return NGX_ERROR;
}
cl->buf = b;
cl->next = NULL;
r->upstream->request_bufs = cl;
*b->last++ = 'g'; *b->last++ = 'e'; *b->last++ = 't'; *b->last++ = ' ';
ctx = ngx_http_get_module_ctx(r, ngx_http_memcached_module);
ctx->key.data = b->last;
if (escape == 0) {
b->last = ngx_copy(b->last, vv->data, vv->len);
} else {
b->last = (u_char *) ngx_escape_uri(b->last, vv->data, vv->len,
NGX_ESCAPE_MEMCACHED);
}
ctx->key.len = b->last - ctx->key.data;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http memcached request: \"%V\"", &ctx->key);
*b->last++ = CR; *b->last++ = LF;
return NGX_OK;
}
static ngx_int_t
ngx_http_memcached_reinit_request(ngx_http_request_t *r)
{
return NGX_OK;
}
static ngx_int_t
ngx_http_memcached_process_header(ngx_http_request_t *r)
{
u_char *p, *start;
ngx_str_t line;
ngx_uint_t flags;
ngx_table_elt_t *h;
ngx_http_upstream_t *u;
ngx_http_memcached_ctx_t *ctx;
ngx_http_memcached_loc_conf_t *mlcf;
u = r->upstream;
for (p = u->buffer.pos; p < u->buffer.last; p++) {
if (*p == LF) {
goto found;
}
}
return NGX_AGAIN;
found:
line.data = u->buffer.pos;
line.len = p - u->buffer.pos;
if (line.len == 0 || *(p - 1) != CR) {
goto no_valid;
}
*p = '\0';
line.len--;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"memcached: \"%V\"", &line);
p = u->buffer.pos;
ctx = ngx_http_get_module_ctx(r, ngx_http_memcached_module);
mlcf = ngx_http_get_module_loc_conf(r, ngx_http_memcached_module);
if (ngx_strncmp(p, "VALUE ", sizeof("VALUE ") - 1) == 0) {
p += sizeof("VALUE ") - 1;
if (ngx_strncmp(p, ctx->key.data, ctx->key.len) != 0) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"memcached sent invalid key in response \"%V\" "
"for key \"%V\"",
&line, &ctx->key);
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
}
p += ctx->key.len;
if (*p++ != ' ') {
goto no_valid;
}
/* flags */
start = p;
while (*p) {
if (*p++ == ' ') {
if (mlcf->gzip_flag) {
goto flags;
} else {
goto length;
}
}
}
goto no_valid;
flags:
flags = ngx_atoi(start, p - start - 1);
if (flags == (ngx_uint_t) NGX_ERROR) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"memcached sent invalid flags in response \"%V\" "
"for key \"%V\"",
&line, &ctx->key);
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
}
if (flags & mlcf->gzip_flag) {
h = ngx_list_push(&r->headers_out.headers);
if (h == NULL) {
return NGX_ERROR;
}
h->hash = 1;
ngx_str_set(&h->key, "Content-Encoding");
ngx_str_set(&h->value, "gzip");
r->headers_out.content_encoding = h;
}
length:
start = p;
p = line.data + line.len;
u->headers_in.content_length_n = ngx_atoof(start, p - start);
if (u->headers_in.content_length_n == NGX_ERROR) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"memcached sent invalid length in response \"%V\" "
"for key \"%V\"",
&line, &ctx->key);
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
}
u->headers_in.status_n = 200;
u->state->status = 200;
u->buffer.pos = p + sizeof(CRLF) - 1;
return NGX_OK;
}
if (ngx_strcmp(p, "END\x0d") == 0) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"key: \"%V\" was not found by memcached", &ctx->key);
u->headers_in.content_length_n = 0;
u->headers_in.status_n = 404;
u->state->status = 404;
u->buffer.pos = p + sizeof("END" CRLF) - 1;
u->keepalive = 1;
return NGX_OK;
}
no_valid:
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"memcached sent invalid response: \"%V\"", &line);
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
}
static ngx_int_t
ngx_http_memcached_filter_init(void *data)
{
ngx_http_memcached_ctx_t *ctx = data;
ngx_http_upstream_t *u;
u = ctx->request->upstream;
if (u->headers_in.status_n != 404) {
u->length = u->headers_in.content_length_n + NGX_HTTP_MEMCACHED_END;
ctx->rest = NGX_HTTP_MEMCACHED_END;
} else {
u->length = 0;
}
return NGX_OK;
}
static ngx_int_t
ngx_http_memcached_filter(void *data, ssize_t bytes)
{
ngx_http_memcached_ctx_t *ctx = data;
u_char *last;
ngx_buf_t *b;
ngx_chain_t *cl, **ll;
ngx_http_upstream_t *u;
u = ctx->request->upstream;
b = &u->buffer;
if (u->length == (ssize_t) ctx->rest) {
if (ngx_strncmp(b->last,
ngx_http_memcached_end + NGX_HTTP_MEMCACHED_END - ctx->rest,
bytes)
!= 0)
{
ngx_log_error(NGX_LOG_ERR, ctx->request->connection->log, 0,
"memcached sent invalid trailer");
u->length = 0;
ctx->rest = 0;
return NGX_OK;
}
u->length -= bytes;
ctx->rest -= bytes;
if (u->length == 0) {
u->keepalive = 1;
}
return NGX_OK;
}
for (cl = u->out_bufs, ll = &u->out_bufs; cl; cl = cl->next) {
ll = &cl->next;
}
cl = ngx_chain_get_free_buf(ctx->request->pool, &u->free_bufs);
if (cl == NULL) {
return NGX_ERROR;
}
cl->buf->flush = 1;
cl->buf->memory = 1;
*ll = cl;
last = b->last;
cl->buf->pos = last;
b->last += bytes;
cl->buf->last = b->last;
cl->buf->tag = u->output.tag;
ngx_log_debug4(NGX_LOG_DEBUG_HTTP, ctx->request->connection->log, 0,
"memcached filter bytes:%z size:%z length:%O rest:%z",
bytes, b->last - b->pos, u->length, ctx->rest);
if (bytes <= (ssize_t) (u->length - NGX_HTTP_MEMCACHED_END)) {
u->length -= bytes;
return NGX_OK;
}
last += (size_t) (u->length - NGX_HTTP_MEMCACHED_END);
if (ngx_strncmp(last, ngx_http_memcached_end, b->last - last) != 0) {
ngx_log_error(NGX_LOG_ERR, ctx->request->connection->log, 0,
"memcached sent invalid trailer");
b->last = last;
cl->buf->last = last;
u->length = 0;
ctx->rest = 0;
return NGX_OK;
}
ctx->rest -= b->last - last;
b->last = last;
cl->buf->last = last;
u->length = ctx->rest;
if (u->length == 0) {
u->keepalive = 1;
}
return NGX_OK;
}
static void
ngx_http_memcached_abort_request(ngx_http_request_t *r)
{
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"abort http memcached request");
return;
}
static void
ngx_http_memcached_finalize_request(ngx_http_request_t *r, ngx_int_t rc)
{
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"finalize http memcached request");
return;
}
static void *
ngx_http_memcached_create_loc_conf(ngx_conf_t *cf)
{
ngx_http_memcached_loc_conf_t *conf;
conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_memcached_loc_conf_t));
if (conf == NULL) {
return NULL;
}
/*
* set by ngx_pcalloc():
*
* conf->upstream.bufs.num = 0;
* conf->upstream.next_upstream = 0;
* conf->upstream.temp_path = NULL;
*/
conf->upstream.local = NGX_CONF_UNSET_PTR;
conf->upstream.next_upstream_tries = NGX_CONF_UNSET_UINT;
conf->upstream.connect_timeout = NGX_CONF_UNSET_MSEC;
conf->upstream.send_timeout = NGX_CONF_UNSET_MSEC;
conf->upstream.read_timeout = NGX_CONF_UNSET_MSEC;
conf->upstream.next_upstream_timeout = NGX_CONF_UNSET_MSEC;
conf->upstream.buffer_size = NGX_CONF_UNSET_SIZE;
/* the hardcoded values */
conf->upstream.cyclic_temp_file = 0;
conf->upstream.buffering = 0;
conf->upstream.ignore_client_abort = 0;
conf->upstream.send_lowat = 0;
conf->upstream.bufs.num = 0;
conf->upstream.busy_buffers_size = 0;
conf->upstream.max_temp_file_size = 0;
conf->upstream.temp_file_write_size = 0;
conf->upstream.intercept_errors = 1;
conf->upstream.intercept_404 = 1;
conf->upstream.pass_request_headers = 0;
conf->upstream.pass_request_body = 0;
conf->upstream.force_ranges = 1;
conf->index = NGX_CONF_UNSET;
conf->gzip_flag = NGX_CONF_UNSET_UINT;
return conf;
}
static char *
ngx_http_memcached_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
{
ngx_http_memcached_loc_conf_t *prev = parent;
ngx_http_memcached_loc_conf_t *conf = child;
ngx_conf_merge_ptr_value(conf->upstream.local,
prev->upstream.local, NULL);
ngx_conf_merge_uint_value(conf->upstream.next_upstream_tries,
prev->upstream.next_upstream_tries, 0);
ngx_conf_merge_msec_value(conf->upstream.connect_timeout,
prev->upstream.connect_timeout, 60000);
ngx_conf_merge_msec_value(conf->upstream.send_timeout,
prev->upstream.send_timeout, 60000);
ngx_conf_merge_msec_value(conf->upstream.read_timeout,
prev->upstream.read_timeout, 60000);
ngx_conf_merge_msec_value(conf->upstream.next_upstream_timeout,
prev->upstream.next_upstream_timeout, 0);
ngx_conf_merge_size_value(conf->upstream.buffer_size,
prev->upstream.buffer_size,
(size_t) ngx_pagesize);
ngx_conf_merge_bitmask_value(conf->upstream.next_upstream,
prev->upstream.next_upstream,
(NGX_CONF_BITMASK_SET
|NGX_HTTP_UPSTREAM_FT_ERROR
|NGX_HTTP_UPSTREAM_FT_TIMEOUT));
if (conf->upstream.next_upstream & NGX_HTTP_UPSTREAM_FT_OFF) {
conf->upstream.next_upstream = NGX_CONF_BITMASK_SET
|NGX_HTTP_UPSTREAM_FT_OFF;
}
if (conf->upstream.upstream == NULL) {
conf->upstream.upstream = prev->upstream.upstream;
}
if (conf->index == NGX_CONF_UNSET) {
conf->index = prev->index;
}
ngx_conf_merge_uint_value(conf->gzip_flag, prev->gzip_flag, 0);
return NGX_CONF_OK;
}
static char *
ngx_http_memcached_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_memcached_loc_conf_t *mlcf = conf;
ngx_str_t *value;
ngx_url_t u;
ngx_http_core_loc_conf_t *clcf;
if (mlcf->upstream.upstream) {
return "is duplicate";
}
value = cf->args->elts;
ngx_memzero(&u, sizeof(ngx_url_t));
u.url = value[1];
u.no_resolve = 1;
mlcf->upstream.upstream = ngx_http_upstream_add(cf, &u, 0);
if (mlcf->upstream.upstream == NULL) {
return NGX_CONF_ERROR;
}
clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
clcf->handler = ngx_http_memcached_handler;
if (clcf->name.data[clcf->name.len - 1] == '/') {
clcf->auto_redirect = 1;
}
mlcf->index = ngx_http_get_variable_index(cf, &ngx_http_memcached_key);
if (mlcf->index == NGX_ERROR) {
return NGX_CONF_ERROR;
}
return NGX_CONF_OK;
}
nginx-1.14.0/src/http/modules/ngx_http_mirror_module.c 000644 001751 001751 00000015212 13265410474 024271 0 ustar 00mdounin mdounin 000000 000000
/*
* Copyright (C) Roman Arutyunyan
* Copyright (C) Nginx, Inc.
*/
#include
#include
#include
typedef struct {
ngx_array_t *mirror;
ngx_flag_t request_body;
} ngx_http_mirror_loc_conf_t;
typedef struct {
ngx_int_t status;
} ngx_http_mirror_ctx_t;
static ngx_int_t ngx_http_mirror_handler(ngx_http_request_t *r);
static void ngx_http_mirror_body_handler(ngx_http_request_t *r);
static ngx_int_t ngx_http_mirror_handler_internal(ngx_http_request_t *r);
static void *ngx_http_mirror_create_loc_conf(ngx_conf_t *cf);
static char *ngx_http_mirror_merge_loc_conf(ngx_conf_t *cf, void *parent,
void *child);
static char *ngx_http_mirror(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static ngx_int_t ngx_http_mirror_init(ngx_conf_t *cf);
static ngx_command_t ngx_http_mirror_commands[] = {
{ ngx_string("mirror"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_http_mirror,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
{ ngx_string("mirror_request_body"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_mirror_loc_conf_t, request_body),
NULL },
ngx_null_command
};
static ngx_http_module_t ngx_http_mirror_module_ctx = {
NULL, /* preconfiguration */
ngx_http_mirror_init, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
ngx_http_mirror_create_loc_conf, /* create location configuration */
ngx_http_mirror_merge_loc_conf /* merge location configuration */
};
ngx_module_t ngx_http_mirror_module = {
NGX_MODULE_V1,
&ngx_http_mirror_module_ctx, /* module context */
ngx_http_mirror_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static ngx_int_t
ngx_http_mirror_handler(ngx_http_request_t *r)
{
ngx_int_t rc;
ngx_http_mirror_ctx_t *ctx;
ngx_http_mirror_loc_conf_t *mlcf;
if (r != r->main) {
return NGX_DECLINED;
}
mlcf = ngx_http_get_module_loc_conf(r, ngx_http_mirror_module);
if (mlcf->mirror == NULL) {
return NGX_DECLINED;
}
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "mirror handler");
if (mlcf->request_body) {
ctx = ngx_http_get_module_ctx(r, ngx_http_mirror_module);
if (ctx) {
return ctx->status;
}
ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_mirror_ctx_t));
if (ctx == NULL) {
return NGX_ERROR;
}
ctx->status = NGX_DONE;
ngx_http_set_ctx(r, ctx, ngx_http_mirror_module);
rc = ngx_http_read_client_request_body(r, ngx_http_mirror_body_handler);
if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
return rc;
}
ngx_http_finalize_request(r, NGX_DONE);
return NGX_DONE;
}
return ngx_http_mirror_handler_internal(r);
}
static void
ngx_http_mirror_body_handler(ngx_http_request_t *r)
{
ngx_http_mirror_ctx_t *ctx;
ctx = ngx_http_get_module_ctx(r, ngx_http_mirror_module);
ctx->status = ngx_http_mirror_handler_internal(r);
r->preserve_body = 1;
r->write_event_handler = ngx_http_core_run_phases;
ngx_http_core_run_phases(r);
}
static ngx_int_t
ngx_http_mirror_handler_internal(ngx_http_request_t *r)
{
ngx_str_t *name;
ngx_uint_t i;
ngx_http_request_t *sr;
ngx_http_mirror_loc_conf_t *mlcf;
mlcf = ngx_http_get_module_loc_conf(r, ngx_http_mirror_module);
name = mlcf->mirror->elts;
for (i = 0; i < mlcf->mirror->nelts; i++) {
if (ngx_http_subrequest(r, &name[i], &r->args, &sr, NULL,
NGX_HTTP_SUBREQUEST_BACKGROUND)
!= NGX_OK)
{
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
sr->header_only = 1;
sr->method = r->method;
sr->method_name = r->method_name;
}
return NGX_DECLINED;
}
static void *
ngx_http_mirror_create_loc_conf(ngx_conf_t *cf)
{
ngx_http_mirror_loc_conf_t *mlcf;
mlcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_mirror_loc_conf_t));
if (mlcf == NULL) {
return NULL;
}
mlcf->mirror = NGX_CONF_UNSET_PTR;
mlcf->request_body = NGX_CONF_UNSET;
return mlcf;
}
static char *
ngx_http_mirror_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
{
ngx_http_mirror_loc_conf_t *prev = parent;
ngx_http_mirror_loc_conf_t *conf = child;
ngx_conf_merge_ptr_value(conf->mirror, prev->mirror, NULL);
ngx_conf_merge_value(conf->request_body, prev->request_body, 1);
return NGX_CONF_OK;
}
static char *
ngx_http_mirror(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_mirror_loc_conf_t *mlcf = conf;
ngx_str_t *value, *s;
value = cf->args->elts;
if (ngx_strcmp(value[1].data, "off") == 0) {
if (mlcf->mirror != NGX_CONF_UNSET_PTR) {
return "is duplicate";
}
mlcf->mirror = NULL;
return NGX_CONF_OK;
}
if (mlcf->mirror == NULL) {
return "is duplicate";
}
if (mlcf->mirror == NGX_CONF_UNSET_PTR) {
mlcf->mirror = ngx_array_create(cf->pool, 4, sizeof(ngx_str_t));
if (mlcf->mirror == NULL) {
return NGX_CONF_ERROR;
}
}
s = ngx_array_push(mlcf->mirror);
if (s == NULL) {
return NGX_CONF_ERROR;
}
*s = value[1];
return NGX_CONF_OK;
}
static ngx_int_t
ngx_http_mirror_init(ngx_conf_t *cf)
{
ngx_http_handler_pt *h;
ngx_http_core_main_conf_t *cmcf;
cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
h = ngx_array_push(&cmcf->phases[NGX_HTTP_PRECONTENT_PHASE].handlers);
if (h == NULL) {
return NGX_ERROR;
}
*h = ngx_http_mirror_handler;
return NGX_OK;
}
nginx-1.14.0/src/http/modules/ngx_http_mp4_module.c 000644 001751 001751 00000315101 13265410474 023457 0 ustar 00mdounin mdounin 000000 000000
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#include
#include
#include
#define NGX_HTTP_MP4_TRAK_ATOM 0
#define NGX_HTTP_MP4_TKHD_ATOM 1
#define NGX_HTTP_MP4_MDIA_ATOM 2
#define NGX_HTTP_MP4_MDHD_ATOM 3
#define NGX_HTTP_MP4_HDLR_ATOM 4
#define NGX_HTTP_MP4_MINF_ATOM 5
#define NGX_HTTP_MP4_VMHD_ATOM 6
#define NGX_HTTP_MP4_SMHD_ATOM 7
#define NGX_HTTP_MP4_DINF_ATOM 8
#define NGX_HTTP_MP4_STBL_ATOM 9
#define NGX_HTTP_MP4_STSD_ATOM 10
#define NGX_HTTP_MP4_STTS_ATOM 11
#define NGX_HTTP_MP4_STTS_DATA 12
#define NGX_HTTP_MP4_STSS_ATOM 13
#define NGX_HTTP_MP4_STSS_DATA 14
#define NGX_HTTP_MP4_CTTS_ATOM 15
#define NGX_HTTP_MP4_CTTS_DATA 16
#define NGX_HTTP_MP4_STSC_ATOM 17
#define NGX_HTTP_MP4_STSC_START 18
#define NGX_HTTP_MP4_STSC_DATA 19
#define NGX_HTTP_MP4_STSC_END 20
#define NGX_HTTP_MP4_STSZ_ATOM 21
#define NGX_HTTP_MP4_STSZ_DATA 22
#define NGX_HTTP_MP4_STCO_ATOM 23
#define NGX_HTTP_MP4_STCO_DATA 24
#define NGX_HTTP_MP4_CO64_ATOM 25
#define NGX_HTTP_MP4_CO64_DATA 26
#define NGX_HTTP_MP4_LAST_ATOM NGX_HTTP_MP4_CO64_DATA
typedef struct {
size_t buffer_size;
size_t max_buffer_size;
} ngx_http_mp4_conf_t;
typedef struct {
u_char chunk[4];
u_char samples[4];
u_char id[4];
} ngx_mp4_stsc_entry_t;
typedef struct {
uint32_t timescale;
uint32_t time_to_sample_entries;
uint32_t sample_to_chunk_entries;
uint32_t sync_samples_entries;
uint32_t composition_offset_entries;
uint32_t sample_sizes_entries;
uint32_t chunks;
ngx_uint_t start_sample;
ngx_uint_t end_sample;
ngx_uint_t start_chunk;
ngx_uint_t end_chunk;
ngx_uint_t start_chunk_samples;
ngx_uint_t end_chunk_samples;
uint64_t start_chunk_samples_size;
uint64_t end_chunk_samples_size;
off_t start_offset;
off_t end_offset;
size_t tkhd_size;
size_t mdhd_size;
size_t hdlr_size;
size_t vmhd_size;
size_t smhd_size;
size_t dinf_size;
size_t size;
ngx_chain_t out[NGX_HTTP_MP4_LAST_ATOM + 1];
ngx_buf_t trak_atom_buf;
ngx_buf_t tkhd_atom_buf;
ngx_buf_t mdia_atom_buf;
ngx_buf_t mdhd_atom_buf;
ngx_buf_t hdlr_atom_buf;
ngx_buf_t minf_atom_buf;
ngx_buf_t vmhd_atom_buf;
ngx_buf_t smhd_atom_buf;
ngx_buf_t dinf_atom_buf;
ngx_buf_t stbl_atom_buf;
ngx_buf_t stsd_atom_buf;
ngx_buf_t stts_atom_buf;
ngx_buf_t stts_data_buf;
ngx_buf_t stss_atom_buf;
ngx_buf_t stss_data_buf;
ngx_buf_t ctts_atom_buf;
ngx_buf_t ctts_data_buf;
ngx_buf_t stsc_atom_buf;
ngx_buf_t stsc_start_chunk_buf;
ngx_buf_t stsc_end_chunk_buf;
ngx_buf_t stsc_data_buf;
ngx_buf_t stsz_atom_buf;
ngx_buf_t stsz_data_buf;
ngx_buf_t stco_atom_buf;
ngx_buf_t stco_data_buf;
ngx_buf_t co64_atom_buf;
ngx_buf_t co64_data_buf;
ngx_mp4_stsc_entry_t stsc_start_chunk_entry;
ngx_mp4_stsc_entry_t stsc_end_chunk_entry;
} ngx_http_mp4_trak_t;
typedef struct {
ngx_file_t file;
u_char *buffer;
u_char *buffer_start;
u_char *buffer_pos;
u_char *buffer_end;
size_t buffer_size;
off_t offset;
off_t end;
off_t content_length;
ngx_uint_t start;
ngx_uint_t length;
uint32_t timescale;
ngx_http_request_t *request;
ngx_array_t trak;
ngx_http_mp4_trak_t traks[2];
size_t ftyp_size;
size_t moov_size;
ngx_chain_t *out;
ngx_chain_t ftyp_atom;
ngx_chain_t moov_atom;
ngx_chain_t mvhd_atom;
ngx_chain_t mdat_atom;
ngx_chain_t mdat_data;
ngx_buf_t ftyp_atom_buf;
ngx_buf_t moov_atom_buf;
ngx_buf_t mvhd_atom_buf;
ngx_buf_t mdat_atom_buf;
ngx_buf_t mdat_data_buf;
u_char moov_atom_header[8];
u_char mdat_atom_header[16];
} ngx_http_mp4_file_t;
typedef struct {
char *name;
ngx_int_t (*handler)(ngx_http_mp4_file_t *mp4,
uint64_t atom_data_size);
} ngx_http_mp4_atom_handler_t;
#define ngx_mp4_atom_header(mp4) (mp4->buffer_pos - 8)
#define ngx_mp4_atom_data(mp4) mp4->buffer_pos
#define ngx_mp4_atom_data_size(t) (uint64_t) (sizeof(t) - 8)
#define ngx_mp4_atom_next(mp4, n) \
mp4->buffer_pos += (size_t) n; \
mp4->offset += n
#define ngx_mp4_set_atom_name(p, n1, n2, n3, n4) \
((u_char *) (p))[4] = n1; \
((u_char *) (p))[5] = n2; \
((u_char *) (p))[6] = n3; \
((u_char *) (p))[7] = n4
#define ngx_mp4_get_32value(p) \
( ((uint32_t) ((u_char *) (p))[0] << 24) \
+ ( ((u_char *) (p))[1] << 16) \
+ ( ((u_char *) (p))[2] << 8) \
+ ( ((u_char *) (p))[3]) )
#define ngx_mp4_set_32value(p, n) \
((u_char *) (p))[0] = (u_char) ((n) >> 24); \
((u_char *) (p))[1] = (u_char) ((n) >> 16); \
((u_char *) (p))[2] = (u_char) ((n) >> 8); \
((u_char *) (p))[3] = (u_char) (n)
#define ngx_mp4_get_64value(p) \
( ((uint64_t) ((u_char *) (p))[0] << 56) \
+ ((uint64_t) ((u_char *) (p))[1] << 48) \
+ ((uint64_t) ((u_char *) (p))[2] << 40) \
+ ((uint64_t) ((u_char *) (p))[3] << 32) \
+ ((uint64_t) ((u_char *) (p))[4] << 24) \
+ ( ((u_char *) (p))[5] << 16) \
+ ( ((u_char *) (p))[6] << 8) \
+ ( ((u_char *) (p))[7]) )
#define ngx_mp4_set_64value(p, n) \
((u_char *) (p))[0] = (u_char) ((uint64_t) (n) >> 56); \
((u_char *) (p))[1] = (u_char) ((uint64_t) (n) >> 48); \
((u_char *) (p))[2] = (u_char) ((uint64_t) (n) >> 40); \
((u_char *) (p))[3] = (u_char) ((uint64_t) (n) >> 32); \
((u_char *) (p))[4] = (u_char) ( (n) >> 24); \
((u_char *) (p))[5] = (u_char) ( (n) >> 16); \
((u_char *) (p))[6] = (u_char) ( (n) >> 8); \
((u_char *) (p))[7] = (u_char) (n)
#define ngx_mp4_last_trak(mp4) \
&((ngx_http_mp4_trak_t *) mp4->trak.elts)[mp4->trak.nelts - 1]
static ngx_int_t ngx_http_mp4_handler(ngx_http_request_t *r);
static ngx_int_t ngx_http_mp4_atofp(u_char *line, size_t n, size_t point);
static ngx_int_t ngx_http_mp4_process(ngx_http_mp4_file_t *mp4);
static ngx_int_t ngx_http_mp4_read_atom(ngx_http_mp4_file_t *mp4,
ngx_http_mp4_atom_handler_t *atom, uint64_t atom_data_size);
static ngx_int_t ngx_http_mp4_read(ngx_http_mp4_file_t *mp4, size_t size);
static ngx_int_t ngx_http_mp4_read_ftyp_atom(ngx_http_mp4_file_t *mp4,
uint64_t atom_data_size);
static ngx_int_t ngx_http_mp4_read_moov_atom(ngx_http_mp4_file_t *mp4,
uint64_t atom_data_size);
static ngx_int_t ngx_http_mp4_read_mdat_atom(ngx_http_mp4_file_t *mp4,
uint64_t atom_data_size);
static size_t ngx_http_mp4_update_mdat_atom(ngx_http_mp4_file_t *mp4,
off_t start_offset, off_t end_offset);
static ngx_int_t ngx_http_mp4_read_mvhd_atom(ngx_http_mp4_file_t *mp4,
uint64_t atom_data_size);
static ngx_int_t ngx_http_mp4_read_trak_atom(ngx_http_mp4_file_t *mp4,
uint64_t atom_data_size);
static void ngx_http_mp4_update_trak_atom(ngx_http_mp4_file_t *mp4,
ngx_http_mp4_trak_t *trak);
static ngx_int_t ngx_http_mp4_read_cmov_atom(ngx_http_mp4_file_t *mp4,
uint64_t atom_data_size);
static ngx_int_t ngx_http_mp4_read_tkhd_atom(ngx_http_mp4_file_t *mp4,
uint64_t atom_data_size);
static ngx_int_t ngx_http_mp4_read_mdia_atom(ngx_http_mp4_file_t *mp4,
uint64_t atom_data_size);
static void ngx_http_mp4_update_mdia_atom(ngx_http_mp4_file_t *mp4,
ngx_http_mp4_trak_t *trak);
static ngx_int_t ngx_http_mp4_read_mdhd_atom(ngx_http_mp4_file_t *mp4,
uint64_t atom_data_size);
static ngx_int_t ngx_http_mp4_read_hdlr_atom(ngx_http_mp4_file_t *mp4,
uint64_t atom_data_size);
static ngx_int_t ngx_http_mp4_read_minf_atom(ngx_http_mp4_file_t *mp4,
uint64_t atom_data_size);
static void ngx_http_mp4_update_minf_atom(ngx_http_mp4_file_t *mp4,
ngx_http_mp4_trak_t *trak);
static ngx_int_t ngx_http_mp4_read_dinf_atom(ngx_http_mp4_file_t *mp4,
uint64_t atom_data_size);
static ngx_int_t ngx_http_mp4_read_vmhd_atom(ngx_http_mp4_file_t *mp4,
uint64_t atom_data_size);
static ngx_int_t ngx_http_mp4_read_smhd_atom(ngx_http_mp4_file_t *mp4,
uint64_t atom_data_size);
static ngx_int_t ngx_http_mp4_read_stbl_atom(ngx_http_mp4_file_t *mp4,
uint64_t atom_data_size);
static void ngx_http_mp4_update_stbl_atom(ngx_http_mp4_file_t *mp4,
ngx_http_mp4_trak_t *trak);
static ngx_int_t ngx_http_mp4_read_stsd_atom(ngx_http_mp4_file_t *mp4,
uint64_t atom_data_size);
static ngx_int_t ngx_http_mp4_read_stts_atom(ngx_http_mp4_file_t *mp4,
uint64_t atom_data_size);
static ngx_int_t ngx_http_mp4_update_stts_atom(ngx_http_mp4_file_t *mp4,
ngx_http_mp4_trak_t *trak);
static ngx_int_t ngx_http_mp4_crop_stts_data(ngx_http_mp4_file_t *mp4,
ngx_http_mp4_trak_t *trak, ngx_uint_t start);
static ngx_int_t ngx_http_mp4_read_stss_atom(ngx_http_mp4_file_t *mp4,
uint64_t atom_data_size);
static ngx_int_t ngx_http_mp4_update_stss_atom(ngx_http_mp4_file_t *mp4,
ngx_http_mp4_trak_t *trak);
static void ngx_http_mp4_crop_stss_data(ngx_http_mp4_file_t *mp4,
ngx_http_mp4_trak_t *trak, ngx_uint_t start);
static ngx_int_t ngx_http_mp4_read_ctts_atom(ngx_http_mp4_file_t *mp4,
uint64_t atom_data_size);
static void ngx_http_mp4_update_ctts_atom(ngx_http_mp4_file_t *mp4,
ngx_http_mp4_trak_t *trak);
static void ngx_http_mp4_crop_ctts_data(ngx_http_mp4_file_t *mp4,
ngx_http_mp4_trak_t *trak, ngx_uint_t start);
static ngx_int_t ngx_http_mp4_read_stsc_atom(ngx_http_mp4_file_t *mp4,
uint64_t atom_data_size);
static ngx_int_t ngx_http_mp4_update_stsc_atom(ngx_http_mp4_file_t *mp4,
ngx_http_mp4_trak_t *trak);
static ngx_int_t ngx_http_mp4_crop_stsc_data(ngx_http_mp4_file_t *mp4,
ngx_http_mp4_trak_t *trak, ngx_uint_t start);
static ngx_int_t ngx_http_mp4_read_stsz_atom(ngx_http_mp4_file_t *mp4,
uint64_t atom_data_size);
static ngx_int_t ngx_http_mp4_update_stsz_atom(ngx_http_mp4_file_t *mp4,
ngx_http_mp4_trak_t *trak);
static ngx_int_t ngx_http_mp4_read_stco_atom(ngx_http_mp4_file_t *mp4,
uint64_t atom_data_size);
static ngx_int_t ngx_http_mp4_update_stco_atom(ngx_http_mp4_file_t *mp4,
ngx_http_mp4_trak_t *trak);
static void ngx_http_mp4_adjust_stco_atom(ngx_http_mp4_file_t *mp4,
ngx_http_mp4_trak_t *trak, int32_t adjustment);
static ngx_int_t ngx_http_mp4_read_co64_atom(ngx_http_mp4_file_t *mp4,
uint64_t atom_data_size);
static ngx_int_t ngx_http_mp4_update_co64_atom(ngx_http_mp4_file_t *mp4,
ngx_http_mp4_trak_t *trak);
static void ngx_http_mp4_adjust_co64_atom(ngx_http_mp4_file_t *mp4,
ngx_http_mp4_trak_t *trak, off_t adjustment);
static char *ngx_http_mp4(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static void *ngx_http_mp4_create_conf(ngx_conf_t *cf);
static char *ngx_http_mp4_merge_conf(ngx_conf_t *cf, void *parent, void *child);
static ngx_command_t ngx_http_mp4_commands[] = {
{ ngx_string("mp4"),
NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,
ngx_http_mp4,
0,
0,
NULL },
{ ngx_string("mp4_buffer_size"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_size_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_mp4_conf_t, buffer_size),
NULL },
{ ngx_string("mp4_max_buffer_size"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_size_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_mp4_conf_t, max_buffer_size),
NULL },
ngx_null_command
};
static ngx_http_module_t ngx_http_mp4_module_ctx = {
NULL, /* preconfiguration */
NULL, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
ngx_http_mp4_create_conf, /* create location configuration */
ngx_http_mp4_merge_conf /* merge location configuration */
};
ngx_module_t ngx_http_mp4_module = {
NGX_MODULE_V1,
&ngx_http_mp4_module_ctx, /* module context */
ngx_http_mp4_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static ngx_http_mp4_atom_handler_t ngx_http_mp4_atoms[] = {
{ "ftyp", ngx_http_mp4_read_ftyp_atom },
{ "moov", ngx_http_mp4_read_moov_atom },
{ "mdat", ngx_http_mp4_read_mdat_atom },
{ NULL, NULL }
};
static ngx_http_mp4_atom_handler_t ngx_http_mp4_moov_atoms[] = {
{ "mvhd", ngx_http_mp4_read_mvhd_atom },
{ "trak", ngx_http_mp4_read_trak_atom },
{ "cmov", ngx_http_mp4_read_cmov_atom },
{ NULL, NULL }
};
static ngx_http_mp4_atom_handler_t ngx_http_mp4_trak_atoms[] = {
{ "tkhd", ngx_http_mp4_read_tkhd_atom },
{ "mdia", ngx_http_mp4_read_mdia_atom },
{ NULL, NULL }
};
static ngx_http_mp4_atom_handler_t ngx_http_mp4_mdia_atoms[] = {
{ "mdhd", ngx_http_mp4_read_mdhd_atom },
{ "hdlr", ngx_http_mp4_read_hdlr_atom },
{ "minf", ngx_http_mp4_read_minf_atom },
{ NULL, NULL }
};
static ngx_http_mp4_atom_handler_t ngx_http_mp4_minf_atoms[] = {
{ "vmhd", ngx_http_mp4_read_vmhd_atom },
{ "smhd", ngx_http_mp4_read_smhd_atom },
{ "dinf", ngx_http_mp4_read_dinf_atom },
{ "stbl", ngx_http_mp4_read_stbl_atom },
{ NULL, NULL }
};
static ngx_http_mp4_atom_handler_t ngx_http_mp4_stbl_atoms[] = {
{ "stsd", ngx_http_mp4_read_stsd_atom },
{ "stts", ngx_http_mp4_read_stts_atom },
{ "stss", ngx_http_mp4_read_stss_atom },
{ "ctts", ngx_http_mp4_read_ctts_atom },
{ "stsc", ngx_http_mp4_read_stsc_atom },
{ "stsz", ngx_http_mp4_read_stsz_atom },
{ "stco", ngx_http_mp4_read_stco_atom },
{ "co64", ngx_http_mp4_read_co64_atom },
{ NULL, NULL }
};
static ngx_int_t
ngx_http_mp4_handler(ngx_http_request_t *r)
{
u_char *last;
size_t root;
ngx_int_t rc, start, end;
ngx_uint_t level, length;
ngx_str_t path, value;
ngx_log_t *log;
ngx_buf_t *b;
ngx_chain_t out;
ngx_http_mp4_file_t *mp4;
ngx_open_file_info_t of;
ngx_http_core_loc_conf_t *clcf;
if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) {
return NGX_HTTP_NOT_ALLOWED;
}
if (r->uri.data[r->uri.len - 1] == '/') {
return NGX_DECLINED;
}
rc = ngx_http_discard_request_body(r);
if (rc != NGX_OK) {
return rc;
}
last = ngx_http_map_uri_to_path(r, &path, &root, 0);
if (last == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
log = r->connection->log;
path.len = last - path.data;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0,
"http mp4 filename: \"%V\"", &path);
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
ngx_memzero(&of, sizeof(ngx_open_file_info_t));
of.read_ahead = clcf->read_ahead;
of.directio = NGX_MAX_OFF_T_VALUE;
of.valid = clcf->open_file_cache_valid;
of.min_uses = clcf->open_file_cache_min_uses;
of.errors = clcf->open_file_cache_errors;
of.events = clcf->open_file_cache_events;
if (ngx_http_set_disable_symlinks(r, clcf, &path, &of) != NGX_OK) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)
!= NGX_OK)
{
switch (of.err) {
case 0:
return NGX_HTTP_INTERNAL_SERVER_ERROR;
case NGX_ENOENT:
case NGX_ENOTDIR:
case NGX_ENAMETOOLONG:
level = NGX_LOG_ERR;
rc = NGX_HTTP_NOT_FOUND;
break;
case NGX_EACCES:
#if (NGX_HAVE_OPENAT)
case NGX_EMLINK:
case NGX_ELOOP:
#endif
level = NGX_LOG_ERR;
rc = NGX_HTTP_FORBIDDEN;
break;
default:
level = NGX_LOG_CRIT;
rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
break;
}
if (rc != NGX_HTTP_NOT_FOUND || clcf->log_not_found) {
ngx_log_error(level, log, of.err,
"%s \"%s\" failed", of.failed, path.data);
}
return rc;
}
if (!of.is_file) {
if (ngx_close_file(of.fd) == NGX_FILE_ERROR) {
ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
ngx_close_file_n " \"%s\" failed", path.data);
}
return NGX_DECLINED;
}
r->root_tested = !r->error_page;
r->allow_ranges = 1;
start = -1;
length = 0;
r->headers_out.content_length_n = of.size;
mp4 = NULL;
b = NULL;
if (r->args.len) {
if (ngx_http_arg(r, (u_char *) "start", 5, &value) == NGX_OK) {
/*
* A Flash player may send start value with a lot of digits
* after dot so a custom function is used instead of ngx_atofp().
*/
start = ngx_http_mp4_atofp(value.data, value.len, 3);
}
if (ngx_http_arg(r, (u_char *) "end", 3, &value) == NGX_OK) {
end = ngx_http_mp4_atofp(value.data, value.len, 3);
if (end > 0) {
if (start < 0) {
start = 0;
}
if (end > start) {
length = end - start;
}
}
}
}
if (start >= 0) {
r->single_range = 1;
mp4 = ngx_pcalloc(r->pool, sizeof(ngx_http_mp4_file_t));
if (mp4 == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
mp4->file.fd = of.fd;
mp4->file.name = path;
mp4->file.log = r->connection->log;
mp4->end = of.size;
mp4->start = (ngx_uint_t) start;
mp4->length = length;
mp4->request = r;
switch (ngx_http_mp4_process(mp4)) {
case NGX_DECLINED:
if (mp4->buffer) {
ngx_pfree(r->pool, mp4->buffer);
}
ngx_pfree(r->pool, mp4);
mp4 = NULL;
break;
case NGX_OK:
r->headers_out.content_length_n = mp4->content_length;
break;
default: /* NGX_ERROR */
if (mp4->buffer) {
ngx_pfree(r->pool, mp4->buffer);
}
ngx_pfree(r->pool, mp4);
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
}
log->action = "sending mp4 to client";
if (clcf->directio <= of.size) {
/*
* DIRECTIO is set on transfer only
* to allow kernel to cache "moov" atom
*/
if (ngx_directio_on(of.fd) == NGX_FILE_ERROR) {
ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
ngx_directio_on_n " \"%s\" failed", path.data);
}
of.is_directio = 1;
if (mp4) {
mp4->file.directio = 1;
}
}
r->headers_out.status = NGX_HTTP_OK;
r->headers_out.last_modified_time = of.mtime;
if (ngx_http_set_etag(r) != NGX_OK) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
if (ngx_http_set_content_type(r) != NGX_OK) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
if (mp4 == NULL) {
b = ngx_calloc_buf(r->pool);
if (b == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
b->file = ngx_pcalloc(r->pool, sizeof(ngx_file_t));
if (b->file == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
}
rc = ngx_http_send_header(r);
if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
return rc;
}
if (mp4) {
return ngx_http_output_filter(r, mp4->out);
}
b->file_pos = 0;
b->file_last = of.size;
b->in_file = b->file_last ? 1 : 0;
b->last_buf = (r == r->main) ? 1 : 0;
b->last_in_chain = 1;
b->file->fd = of.fd;
b->file->name = path;
b->file->log = log;
b->file->directio = of.is_directio;
out.buf = b;
out.next = NULL;
return ngx_http_output_filter(r, &out);
}
static ngx_int_t
ngx_http_mp4_atofp(u_char *line, size_t n, size_t point)
{
ngx_int_t value, cutoff, cutlim;
ngx_uint_t dot;
/* same as ngx_atofp(), but allows additional digits */
if (n == 0) {
return NGX_ERROR;
}
cutoff = NGX_MAX_INT_T_VALUE / 10;
cutlim = NGX_MAX_INT_T_VALUE % 10;
dot = 0;
for (value = 0; n--; line++) {
if (*line == '.') {
if (dot) {
return NGX_ERROR;
}
dot = 1;
continue;
}
if (*line < '0' || *line > '9') {
return NGX_ERROR;
}
if (point == 0) {
continue;
}
if (value >= cutoff && (value > cutoff || *line - '0' > cutlim)) {
return NGX_ERROR;
}
value = value * 10 + (*line - '0');
point -= dot;
}
while (point--) {
if (value > cutoff) {
return NGX_ERROR;
}
value = value * 10;
}
return value;
}
static ngx_int_t
ngx_http_mp4_process(ngx_http_mp4_file_t *mp4)
{
off_t start_offset, end_offset, adjustment;
ngx_int_t rc;
ngx_uint_t i, j;
ngx_chain_t **prev;
ngx_http_mp4_trak_t *trak;
ngx_http_mp4_conf_t *conf;
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
"mp4 start:%ui, length:%ui", mp4->start, mp4->length);
conf = ngx_http_get_module_loc_conf(mp4->request, ngx_http_mp4_module);
mp4->buffer_size = conf->buffer_size;
rc = ngx_http_mp4_read_atom(mp4, ngx_http_mp4_atoms, mp4->end);
if (rc != NGX_OK) {
return rc;
}
if (mp4->trak.nelts == 0) {
ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
"no mp4 trak atoms were found in \"%s\"",
mp4->file.name.data);
return NGX_ERROR;
}
if (mp4->mdat_atom.buf == NULL) {
ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
"no mp4 mdat atom was found in \"%s\"",
mp4->file.name.data);
return NGX_ERROR;
}
prev = &mp4->out;
if (mp4->ftyp_atom.buf) {
*prev = &mp4->ftyp_atom;
prev = &mp4->ftyp_atom.next;
}
*prev = &mp4->moov_atom;
prev = &mp4->moov_atom.next;
if (mp4->mvhd_atom.buf) {
mp4->moov_size += mp4->mvhd_atom_buf.last - mp4->mvhd_atom_buf.pos;
*prev = &mp4->mvhd_atom;
prev = &mp4->mvhd_atom.next;
}
start_offset = mp4->end;
end_offset = 0;
trak = mp4->trak.elts;
for (i = 0; i < mp4->trak.nelts; i++) {
if (ngx_http_mp4_update_stts_atom(mp4, &trak[i]) != NGX_OK) {
return NGX_ERROR;
}
if (ngx_http_mp4_update_stss_atom(mp4, &trak[i]) != NGX_OK) {
return NGX_ERROR;
}
ngx_http_mp4_update_ctts_atom(mp4, &trak[i]);
if (ngx_http_mp4_update_stsc_atom(mp4, &trak[i]) != NGX_OK) {
return NGX_ERROR;
}
if (ngx_http_mp4_update_stsz_atom(mp4, &trak[i]) != NGX_OK) {
return NGX_ERROR;
}
if (trak[i].out[NGX_HTTP_MP4_CO64_DATA].buf) {
if (ngx_http_mp4_update_co64_atom(mp4, &trak[i]) != NGX_OK) {
return NGX_ERROR;
}
} else {
if (ngx_http_mp4_update_stco_atom(mp4, &trak[i]) != NGX_OK) {
return NGX_ERROR;
}
}
ngx_http_mp4_update_stbl_atom(mp4, &trak[i]);
ngx_http_mp4_update_minf_atom(mp4, &trak[i]);
trak[i].size += trak[i].mdhd_size;
trak[i].size += trak[i].hdlr_size;
ngx_http_mp4_update_mdia_atom(mp4, &trak[i]);
trak[i].size += trak[i].tkhd_size;
ngx_http_mp4_update_trak_atom(mp4, &trak[i]);
mp4->moov_size += trak[i].size;
if (start_offset > trak[i].start_offset) {
start_offset = trak[i].start_offset;
}
if (end_offset < trak[i].end_offset) {
end_offset = trak[i].end_offset;
}
*prev = &trak[i].out[NGX_HTTP_MP4_TRAK_ATOM];
prev = &trak[i].out[NGX_HTTP_MP4_TRAK_ATOM].next;
for (j = 0; j < NGX_HTTP_MP4_LAST_ATOM + 1; j++) {
if (trak[i].out[j].buf) {
*prev = &trak[i].out[j];
prev = &trak[i].out[j].next;
}
}
}
if (end_offset < start_offset) {
end_offset = start_offset;
}
mp4->moov_size += 8;
ngx_mp4_set_32value(mp4->moov_atom_header, mp4->moov_size);
ngx_mp4_set_atom_name(mp4->moov_atom_header, 'm', 'o', 'o', 'v');
mp4->content_length += mp4->moov_size;
*prev = &mp4->mdat_atom;
if (start_offset > mp4->mdat_data.buf->file_last) {
ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
"start time is out mp4 mdat atom in \"%s\"",
mp4->file.name.data);
return NGX_ERROR;
}
adjustment = mp4->ftyp_size + mp4->moov_size
+ ngx_http_mp4_update_mdat_atom(mp4, start_offset, end_offset)
- start_offset;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
"mp4 adjustment:%O", adjustment);
for (i = 0; i < mp4->trak.nelts; i++) {
if (trak[i].out[NGX_HTTP_MP4_CO64_DATA].buf) {
ngx_http_mp4_adjust_co64_atom(mp4, &trak[i], adjustment);
} else {
ngx_http_mp4_adjust_stco_atom(mp4, &trak[i], (int32_t) adjustment);
}
}
return NGX_OK;
}
typedef struct {
u_char size[4];
u_char name[4];
} ngx_mp4_atom_header_t;
typedef struct {
u_char size[4];
u_char name[4];
u_char size64[8];
} ngx_mp4_atom_header64_t;
static ngx_int_t
ngx_http_mp4_read_atom(ngx_http_mp4_file_t *mp4,
ngx_http_mp4_atom_handler_t *atom, uint64_t atom_data_size)
{
off_t end;
size_t atom_header_size;
u_char *atom_header, *atom_name;
uint64_t atom_size;
ngx_int_t rc;
ngx_uint_t n;
end = mp4->offset + atom_data_size;
while (mp4->offset < end) {
if (ngx_http_mp4_read(mp4, sizeof(uint32_t)) != NGX_OK) {
return NGX_ERROR;
}
atom_header = mp4->buffer_pos;
atom_size = ngx_mp4_get_32value(atom_header);
atom_header_size = sizeof(ngx_mp4_atom_header_t);
if (atom_size == 0) {
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
"mp4 atom end");
return NGX_OK;
}
if (atom_size < sizeof(ngx_mp4_atom_header_t)) {
if (atom_size == 1) {
if (ngx_http_mp4_read(mp4, sizeof(ngx_mp4_atom_header64_t))
!= NGX_OK)
{
return NGX_ERROR;
}
/* 64-bit atom size */
atom_header = mp4->buffer_pos;
atom_size = ngx_mp4_get_64value(atom_header + 8);
atom_header_size = sizeof(ngx_mp4_atom_header64_t);
} else {
ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
"\"%s\" mp4 atom is too small:%uL",
mp4->file.name.data, atom_size);
return NGX_ERROR;
}
}
if (ngx_http_mp4_read(mp4, sizeof(ngx_mp4_atom_header_t)) != NGX_OK) {
return NGX_ERROR;
}
atom_header = mp4->buffer_pos;
atom_name = atom_header + sizeof(uint32_t);
ngx_log_debug4(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
"mp4 atom: %*s @%O:%uL",
(size_t) 4, atom_name, mp4->offset, atom_size);
if (atom_size > (uint64_t) (NGX_MAX_OFF_T_VALUE - mp4->offset)
|| mp4->offset + (off_t) atom_size > end)
{
ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
"\"%s\" mp4 atom too large:%uL",
mp4->file.name.data, atom_size);
return NGX_ERROR;
}
for (n = 0; atom[n].name; n++) {
if (ngx_strncmp(atom_name, atom[n].name, 4) == 0) {
ngx_mp4_atom_next(mp4, atom_header_size);
rc = atom[n].handler(mp4, atom_size - atom_header_size);
if (rc != NGX_OK) {
return rc;
}
goto next;
}
}
ngx_mp4_atom_next(mp4, atom_size);
next:
continue;
}
return NGX_OK;
}
static ngx_int_t
ngx_http_mp4_read(ngx_http_mp4_file_t *mp4, size_t size)
{
ssize_t n;
if (mp4->buffer_pos + size <= mp4->buffer_end) {
return NGX_OK;
}
if (mp4->offset + (off_t) mp4->buffer_size > mp4->end) {
mp4->buffer_size = (size_t) (mp4->end - mp4->offset);
}
if (mp4->buffer_size < size) {
ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
"\"%s\" mp4 file truncated", mp4->file.name.data);
return NGX_ERROR;
}
if (mp4->buffer == NULL) {
mp4->buffer = ngx_palloc(mp4->request->pool, mp4->buffer_size);
if (mp4->buffer == NULL) {
return NGX_ERROR;
}
mp4->buffer_start = mp4->buffer;
}
n = ngx_read_file(&mp4->file, mp4->buffer_start, mp4->buffer_size,
mp4->offset);
if (n == NGX_ERROR) {
return NGX_ERROR;
}
if ((size_t) n != mp4->buffer_size) {
ngx_log_error(NGX_LOG_CRIT, mp4->file.log, 0,
ngx_read_file_n " read only %z of %z from \"%s\"",
n, mp4->buffer_size, mp4->file.name.data);
return NGX_ERROR;
}
mp4->buffer_pos = mp4->buffer_start;
mp4->buffer_end = mp4->buffer_start + mp4->buffer_size;
return NGX_OK;
}
static ngx_int_t
ngx_http_mp4_read_ftyp_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
{
u_char *ftyp_atom;
size_t atom_size;
ngx_buf_t *atom;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 ftyp atom");
if (atom_data_size > 1024
|| ngx_mp4_atom_data(mp4) + (size_t) atom_data_size > mp4->buffer_end)
{
ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
"\"%s\" mp4 ftyp atom is too large:%uL",
mp4->file.name.data, atom_data_size);
return NGX_ERROR;
}
atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size;
ftyp_atom = ngx_palloc(mp4->request->pool, atom_size);
if (ftyp_atom == NULL) {
return NGX_ERROR;
}
ngx_mp4_set_32value(ftyp_atom, atom_size);
ngx_mp4_set_atom_name(ftyp_atom, 'f', 't', 'y', 'p');
/*
* only moov atom content is guaranteed to be in mp4->buffer
* during sending response, so ftyp atom content should be copied
*/
ngx_memcpy(ftyp_atom + sizeof(ngx_mp4_atom_header_t),
ngx_mp4_atom_data(mp4), (size_t) atom_data_size);
atom = &mp4->ftyp_atom_buf;
atom->temporary = 1;
atom->pos = ftyp_atom;
atom->last = ftyp_atom + atom_size;
mp4->ftyp_atom.buf = atom;
mp4->ftyp_size = atom_size;
mp4->content_length = atom_size;
ngx_mp4_atom_next(mp4, atom_data_size);
return NGX_OK;
}
/*
* Small excess buffer to process atoms after moov atom, mp4->buffer_start
* will be set to this buffer part after moov atom processing.
*/
#define NGX_HTTP_MP4_MOOV_BUFFER_EXCESS (4 * 1024)
static ngx_int_t
ngx_http_mp4_read_moov_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
{
ngx_int_t rc;
ngx_uint_t no_mdat;
ngx_buf_t *atom;
ngx_http_mp4_conf_t *conf;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 moov atom");
no_mdat = (mp4->mdat_atom.buf == NULL);
if (no_mdat && mp4->start == 0 && mp4->length == 0) {
/*
* send original file if moov atom resides before
* mdat atom and client requests integral file
*/
return NGX_DECLINED;
}
conf = ngx_http_get_module_loc_conf(mp4->request, ngx_http_mp4_module);
if (atom_data_size > mp4->buffer_size) {
if (atom_data_size > conf->max_buffer_size) {
ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
"\"%s\" mp4 moov atom is too large:%uL, "
"you may want to increase mp4_max_buffer_size",
mp4->file.name.data, atom_data_size);
return NGX_ERROR;
}
ngx_pfree(mp4->request->pool, mp4->buffer);
mp4->buffer = NULL;
mp4->buffer_pos = NULL;
mp4->buffer_end = NULL;
mp4->buffer_size = (size_t) atom_data_size
+ NGX_HTTP_MP4_MOOV_BUFFER_EXCESS * no_mdat;
}
if (ngx_http_mp4_read(mp4, (size_t) atom_data_size) != NGX_OK) {
return NGX_ERROR;
}
mp4->trak.elts = &mp4->traks;
mp4->trak.size = sizeof(ngx_http_mp4_trak_t);
mp4->trak.nalloc = 2;
mp4->trak.pool = mp4->request->pool;
atom = &mp4->moov_atom_buf;
atom->temporary = 1;
atom->pos = mp4->moov_atom_header;
atom->last = mp4->moov_atom_header + 8;
mp4->moov_atom.buf = &mp4->moov_atom_buf;
rc = ngx_http_mp4_read_atom(mp4, ngx_http_mp4_moov_atoms, atom_data_size);
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 moov atom done");
if (no_mdat) {
mp4->buffer_start = mp4->buffer_pos;
mp4->buffer_size = NGX_HTTP_MP4_MOOV_BUFFER_EXCESS;
if (mp4->buffer_start + mp4->buffer_size > mp4->buffer_end) {
mp4->buffer = NULL;
mp4->buffer_pos = NULL;
mp4->buffer_end = NULL;
}
} else {
/* skip atoms after moov atom */
mp4->offset = mp4->end;
}
return rc;
}
static ngx_int_t
ngx_http_mp4_read_mdat_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
{
ngx_buf_t *data;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 mdat atom");
data = &mp4->mdat_data_buf;
data->file = &mp4->file;
data->in_file = 1;
data->last_buf = (mp4->request == mp4->request->main) ? 1 : 0;
data->last_in_chain = 1;
data->file_last = mp4->offset + atom_data_size;
mp4->mdat_atom.buf = &mp4->mdat_atom_buf;
mp4->mdat_atom.next = &mp4->mdat_data;
mp4->mdat_data.buf = data;
if (mp4->trak.nelts) {
/* skip atoms after mdat atom */
mp4->offset = mp4->end;
} else {
ngx_mp4_atom_next(mp4, atom_data_size);
}
return NGX_OK;
}
static size_t
ngx_http_mp4_update_mdat_atom(ngx_http_mp4_file_t *mp4, off_t start_offset,
off_t end_offset)
{
off_t atom_data_size;
u_char *atom_header;
uint32_t atom_header_size;
uint64_t atom_size;
ngx_buf_t *atom;
atom_data_size = end_offset - start_offset;
mp4->mdat_data.buf->file_pos = start_offset;
mp4->mdat_data.buf->file_last = end_offset;
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
"mdat new offset @%O:%O", start_offset, atom_data_size);
atom_header = mp4->mdat_atom_header;
if ((uint64_t) atom_data_size
> (uint64_t) 0xffffffff - sizeof(ngx_mp4_atom_header_t))
{
atom_size = 1;
atom_header_size = sizeof(ngx_mp4_atom_header64_t);
ngx_mp4_set_64value(atom_header + sizeof(ngx_mp4_atom_header_t),
sizeof(ngx_mp4_atom_header64_t) + atom_data_size);
} else {
atom_size = sizeof(ngx_mp4_atom_header_t) + atom_data_size;
atom_header_size = sizeof(ngx_mp4_atom_header_t);
}
mp4->content_length += atom_header_size + atom_data_size;
ngx_mp4_set_32value(atom_header, atom_size);
ngx_mp4_set_atom_name(atom_header, 'm', 'd', 'a', 't');
atom = &mp4->mdat_atom_buf;
atom->temporary = 1;
atom->pos = atom_header;
atom->last = atom_header + atom_header_size;
return atom_header_size;
}
typedef struct {
u_char size[4];
u_char name[4];
u_char version[1];
u_char flags[3];
u_char creation_time[4];
u_char modification_time[4];
u_char timescale[4];
u_char duration[4];
u_char rate[4];
u_char volume[2];
u_char reserved[10];
u_char matrix[36];
u_char preview_time[4];
u_char preview_duration[4];
u_char poster_time[4];
u_char selection_time[4];
u_char selection_duration[4];
u_char current_time[4];
u_char next_track_id[4];
} ngx_mp4_mvhd_atom_t;
typedef struct {
u_char size[4];
u_char name[4];
u_char version[1];
u_char flags[3];
u_char creation_time[8];
u_char modification_time[8];
u_char timescale[4];
u_char duration[8];
u_char rate[4];
u_char volume[2];
u_char reserved[10];
u_char matrix[36];
u_char preview_time[4];
u_char preview_duration[4];
u_char poster_time[4];
u_char selection_time[4];
u_char selection_duration[4];
u_char current_time[4];
u_char next_track_id[4];
} ngx_mp4_mvhd64_atom_t;
static ngx_int_t
ngx_http_mp4_read_mvhd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
{
u_char *atom_header;
size_t atom_size;
uint32_t timescale;
uint64_t duration, start_time, length_time;
ngx_buf_t *atom;
ngx_mp4_mvhd_atom_t *mvhd_atom;
ngx_mp4_mvhd64_atom_t *mvhd64_atom;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 mvhd atom");
atom_header = ngx_mp4_atom_header(mp4);
mvhd_atom = (ngx_mp4_mvhd_atom_t *) atom_header;
mvhd64_atom = (ngx_mp4_mvhd64_atom_t *) atom_header;
ngx_mp4_set_atom_name(atom_header, 'm', 'v', 'h', 'd');
if (ngx_mp4_atom_data_size(ngx_mp4_mvhd_atom_t) > atom_data_size) {
ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
"\"%s\" mp4 mvhd atom too small", mp4->file.name.data);
return NGX_ERROR;
}
if (mvhd_atom->version[0] == 0) {
/* version 0: 32-bit duration */
timescale = ngx_mp4_get_32value(mvhd_atom->timescale);
duration = ngx_mp4_get_32value(mvhd_atom->duration);
} else {
/* version 1: 64-bit duration */
if (ngx_mp4_atom_data_size(ngx_mp4_mvhd64_atom_t) > atom_data_size) {
ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
"\"%s\" mp4 mvhd atom too small",
mp4->file.name.data);
return NGX_ERROR;
}
timescale = ngx_mp4_get_32value(mvhd64_atom->timescale);
duration = ngx_mp4_get_64value(mvhd64_atom->duration);
}
mp4->timescale = timescale;
ngx_log_debug3(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
"mvhd timescale:%uD, duration:%uL, time:%.3fs",
timescale, duration, (double) duration / timescale);
start_time = (uint64_t) mp4->start * timescale / 1000;
if (duration < start_time) {
ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
"\"%s\" mp4 start time exceeds file duration",
mp4->file.name.data);
return NGX_ERROR;
}
duration -= start_time;
if (mp4->length) {
length_time = (uint64_t) mp4->length * timescale / 1000;
if (duration > length_time) {
duration = length_time;
}
}
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
"mvhd new duration:%uL, time:%.3fs",
duration, (double) duration / timescale);
atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size;
ngx_mp4_set_32value(mvhd_atom->size, atom_size);
if (mvhd_atom->version[0] == 0) {
ngx_mp4_set_32value(mvhd_atom->duration, duration);
} else {
ngx_mp4_set_64value(mvhd64_atom->duration, duration);
}
atom = &mp4->mvhd_atom_buf;
atom->temporary = 1;
atom->pos = atom_header;
atom->last = atom_header + atom_size;
mp4->mvhd_atom.buf = atom;
ngx_mp4_atom_next(mp4, atom_data_size);
return NGX_OK;
}
static ngx_int_t
ngx_http_mp4_read_trak_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
{
u_char *atom_header, *atom_end;
off_t atom_file_end;
ngx_int_t rc;
ngx_buf_t *atom;
ngx_http_mp4_trak_t *trak;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 trak atom");
trak = ngx_array_push(&mp4->trak);
if (trak == NULL) {
return NGX_ERROR;
}
ngx_memzero(trak, sizeof(ngx_http_mp4_trak_t));
atom_header = ngx_mp4_atom_header(mp4);
ngx_mp4_set_atom_name(atom_header, 't', 'r', 'a', 'k');
atom = &trak->trak_atom_buf;
atom->temporary = 1;
atom->pos = atom_header;
atom->last = atom_header + sizeof(ngx_mp4_atom_header_t);
trak->out[NGX_HTTP_MP4_TRAK_ATOM].buf = atom;
atom_end = mp4->buffer_pos + (size_t) atom_data_size;
atom_file_end = mp4->offset + atom_data_size;
rc = ngx_http_mp4_read_atom(mp4, ngx_http_mp4_trak_atoms, atom_data_size);
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
"mp4 trak atom: %i", rc);
if (rc == NGX_DECLINED) {
/* skip this trak */
ngx_memzero(trak, sizeof(ngx_http_mp4_trak_t));
mp4->trak.nelts--;
mp4->buffer_pos = atom_end;
mp4->offset = atom_file_end;
return NGX_OK;
}
return rc;
}
static void
ngx_http_mp4_update_trak_atom(ngx_http_mp4_file_t *mp4,
ngx_http_mp4_trak_t *trak)
{
ngx_buf_t *atom;
trak->size += sizeof(ngx_mp4_atom_header_t);
atom = &trak->trak_atom_buf;
ngx_mp4_set_32value(atom->pos, trak->size);
}
static ngx_int_t
ngx_http_mp4_read_cmov_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
{
ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
"\"%s\" mp4 compressed moov atom (cmov) is not supported",
mp4->file.name.data);
return NGX_ERROR;
}
typedef struct {
u_char size[4];
u_char name[4];
u_char version[1];
u_char flags[3];
u_char creation_time[4];
u_char modification_time[4];
u_char track_id[4];
u_char reserved1[4];
u_char duration[4];
u_char reserved2[8];
u_char layer[2];
u_char group[2];
u_char volume[2];
u_char reserved3[2];
u_char matrix[36];
u_char width[4];
u_char height[4];
} ngx_mp4_tkhd_atom_t;
typedef struct {
u_char size[4];
u_char name[4];
u_char version[1];
u_char flags[3];
u_char creation_time[8];
u_char modification_time[8];
u_char track_id[4];
u_char reserved1[4];
u_char duration[8];
u_char reserved2[8];
u_char layer[2];
u_char group[2];
u_char volume[2];
u_char reserved3[2];
u_char matrix[36];
u_char width[4];
u_char height[4];
} ngx_mp4_tkhd64_atom_t;
static ngx_int_t
ngx_http_mp4_read_tkhd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
{
u_char *atom_header;
size_t atom_size;
uint64_t duration, start_time, length_time;
ngx_buf_t *atom;
ngx_http_mp4_trak_t *trak;
ngx_mp4_tkhd_atom_t *tkhd_atom;
ngx_mp4_tkhd64_atom_t *tkhd64_atom;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 tkhd atom");
atom_header = ngx_mp4_atom_header(mp4);
tkhd_atom = (ngx_mp4_tkhd_atom_t *) atom_header;
tkhd64_atom = (ngx_mp4_tkhd64_atom_t *) atom_header;
ngx_mp4_set_atom_name(tkhd_atom, 't', 'k', 'h', 'd');
if (ngx_mp4_atom_data_size(ngx_mp4_tkhd_atom_t) > atom_data_size) {
ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
"\"%s\" mp4 tkhd atom too small", mp4->file.name.data);
return NGX_ERROR;
}
if (tkhd_atom->version[0] == 0) {
/* version 0: 32-bit duration */
duration = ngx_mp4_get_32value(tkhd_atom->duration);
} else {
/* version 1: 64-bit duration */
if (ngx_mp4_atom_data_size(ngx_mp4_tkhd64_atom_t) > atom_data_size) {
ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
"\"%s\" mp4 tkhd atom too small",
mp4->file.name.data);
return NGX_ERROR;
}
duration = ngx_mp4_get_64value(tkhd64_atom->duration);
}
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
"tkhd duration:%uL, time:%.3fs",
duration, (double) duration / mp4->timescale);
start_time = (uint64_t) mp4->start * mp4->timescale / 1000;
if (duration <= start_time) {
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
"tkhd duration is less than start time");
return NGX_DECLINED;
}
duration -= start_time;
if (mp4->length) {
length_time = (uint64_t) mp4->length * mp4->timescale / 1000;
if (duration > length_time) {
duration = length_time;
}
}
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
"tkhd new duration:%uL, time:%.3fs",
duration, (double) duration / mp4->timescale);
atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size;
trak = ngx_mp4_last_trak(mp4);
trak->tkhd_size = atom_size;
ngx_mp4_set_32value(tkhd_atom->size, atom_size);
if (tkhd_atom->version[0] == 0) {
ngx_mp4_set_32value(tkhd_atom->duration, duration);
} else {
ngx_mp4_set_64value(tkhd64_atom->duration, duration);
}
atom = &trak->tkhd_atom_buf;
atom->temporary = 1;
atom->pos = atom_header;
atom->last = atom_header + atom_size;
trak->out[NGX_HTTP_MP4_TKHD_ATOM].buf = atom;
ngx_mp4_atom_next(mp4, atom_data_size);
return NGX_OK;
}
static ngx_int_t
ngx_http_mp4_read_mdia_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
{
u_char *atom_header;
ngx_buf_t *atom;
ngx_http_mp4_trak_t *trak;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "process mdia atom");
atom_header = ngx_mp4_atom_header(mp4);
ngx_mp4_set_atom_name(atom_header, 'm', 'd', 'i', 'a');
trak = ngx_mp4_last_trak(mp4);
atom = &trak->mdia_atom_buf;
atom->temporary = 1;
atom->pos = atom_header;
atom->last = atom_header + sizeof(ngx_mp4_atom_header_t);
trak->out[NGX_HTTP_MP4_MDIA_ATOM].buf = atom;
return ngx_http_mp4_read_atom(mp4, ngx_http_mp4_mdia_atoms, atom_data_size);
}
static void
ngx_http_mp4_update_mdia_atom(ngx_http_mp4_file_t *mp4,
ngx_http_mp4_trak_t *trak)
{
ngx_buf_t *atom;
trak->size += sizeof(ngx_mp4_atom_header_t);
atom = &trak->mdia_atom_buf;
ngx_mp4_set_32value(atom->pos, trak->size);
}
typedef struct {
u_char size[4];
u_char name[4];
u_char version[1];
u_char flags[3];
u_char creation_time[4];
u_char modification_time[4];
u_char timescale[4];
u_char duration[4];
u_char language[2];
u_char quality[2];
} ngx_mp4_mdhd_atom_t;
typedef struct {
u_char size[4];
u_char name[4];
u_char version[1];
u_char flags[3];
u_char creation_time[8];
u_char modification_time[8];
u_char timescale[4];
u_char duration[8];
u_char language[2];
u_char quality[2];
} ngx_mp4_mdhd64_atom_t;
static ngx_int_t
ngx_http_mp4_read_mdhd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
{
u_char *atom_header;
size_t atom_size;
uint32_t timescale;
uint64_t duration, start_time, length_time;
ngx_buf_t *atom;
ngx_http_mp4_trak_t *trak;
ngx_mp4_mdhd_atom_t *mdhd_atom;
ngx_mp4_mdhd64_atom_t *mdhd64_atom;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 mdhd atom");
atom_header = ngx_mp4_atom_header(mp4);
mdhd_atom = (ngx_mp4_mdhd_atom_t *) atom_header;
mdhd64_atom = (ngx_mp4_mdhd64_atom_t *) atom_header;
ngx_mp4_set_atom_name(mdhd_atom, 'm', 'd', 'h', 'd');
if (ngx_mp4_atom_data_size(ngx_mp4_mdhd_atom_t) > atom_data_size) {
ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
"\"%s\" mp4 mdhd atom too small", mp4->file.name.data);
return NGX_ERROR;
}
if (mdhd_atom->version[0] == 0) {
/* version 0: everything is 32-bit */
timescale = ngx_mp4_get_32value(mdhd_atom->timescale);
duration = ngx_mp4_get_32value(mdhd_atom->duration);
} else {
/* version 1: 64-bit duration and 32-bit timescale */
if (ngx_mp4_atom_data_size(ngx_mp4_mdhd64_atom_t) > atom_data_size) {
ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
"\"%s\" mp4 mdhd atom too small",
mp4->file.name.data);
return NGX_ERROR;
}
timescale = ngx_mp4_get_32value(mdhd64_atom->timescale);
duration = ngx_mp4_get_64value(mdhd64_atom->duration);
}
ngx_log_debug3(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
"mdhd timescale:%uD, duration:%uL, time:%.3fs",
timescale, duration, (double) duration / timescale);
start_time = (uint64_t) mp4->start * timescale / 1000;
if (duration <= start_time) {
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
"mdhd duration is less than start time");
return NGX_DECLINED;
}
duration -= start_time;
if (mp4->length) {
length_time = (uint64_t) mp4->length * timescale / 1000;
if (duration > length_time) {
duration = length_time;
}
}
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
"mdhd new duration:%uL, time:%.3fs",
duration, (double) duration / timescale);
atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size;
trak = ngx_mp4_last_trak(mp4);
trak->mdhd_size = atom_size;
trak->timescale = timescale;
ngx_mp4_set_32value(mdhd_atom->size, atom_size);
if (mdhd_atom->version[0] == 0) {
ngx_mp4_set_32value(mdhd_atom->duration, duration);
} else {
ngx_mp4_set_64value(mdhd64_atom->duration, duration);
}
atom = &trak->mdhd_atom_buf;
atom->temporary = 1;
atom->pos = atom_header;
atom->last = atom_header + atom_size;
trak->out[NGX_HTTP_MP4_MDHD_ATOM].buf = atom;
ngx_mp4_atom_next(mp4, atom_data_size);
return NGX_OK;
}
static ngx_int_t
ngx_http_mp4_read_hdlr_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
{
u_char *atom_header;
size_t atom_size;
ngx_buf_t *atom;
ngx_http_mp4_trak_t *trak;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 hdlr atom");
atom_header = ngx_mp4_atom_header(mp4);
atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size;
ngx_mp4_set_32value(atom_header, atom_size);
ngx_mp4_set_atom_name(atom_header, 'h', 'd', 'l', 'r');
trak = ngx_mp4_last_trak(mp4);
atom = &trak->hdlr_atom_buf;
atom->temporary = 1;
atom->pos = atom_header;
atom->last = atom_header + atom_size;
trak->hdlr_size = atom_size;
trak->out[NGX_HTTP_MP4_HDLR_ATOM].buf = atom;
ngx_mp4_atom_next(mp4, atom_data_size);
return NGX_OK;
}
static ngx_int_t
ngx_http_mp4_read_minf_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
{
u_char *atom_header;
ngx_buf_t *atom;
ngx_http_mp4_trak_t *trak;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "process minf atom");
atom_header = ngx_mp4_atom_header(mp4);
ngx_mp4_set_atom_name(atom_header, 'm', 'i', 'n', 'f');
trak = ngx_mp4_last_trak(mp4);
atom = &trak->minf_atom_buf;
atom->temporary = 1;
atom->pos = atom_header;
atom->last = atom_header + sizeof(ngx_mp4_atom_header_t);
trak->out[NGX_HTTP_MP4_MINF_ATOM].buf = atom;
return ngx_http_mp4_read_atom(mp4, ngx_http_mp4_minf_atoms, atom_data_size);
}
static void
ngx_http_mp4_update_minf_atom(ngx_http_mp4_file_t *mp4,
ngx_http_mp4_trak_t *trak)
{
ngx_buf_t *atom;
trak->size += sizeof(ngx_mp4_atom_header_t)
+ trak->vmhd_size
+ trak->smhd_size
+ trak->dinf_size;
atom = &trak->minf_atom_buf;
ngx_mp4_set_32value(atom->pos, trak->size);
}
static ngx_int_t
ngx_http_mp4_read_vmhd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
{
u_char *atom_header;
size_t atom_size;
ngx_buf_t *atom;
ngx_http_mp4_trak_t *trak;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 vmhd atom");
atom_header = ngx_mp4_atom_header(mp4);
atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size;
ngx_mp4_set_32value(atom_header, atom_size);
ngx_mp4_set_atom_name(atom_header, 'v', 'm', 'h', 'd');
trak = ngx_mp4_last_trak(mp4);
atom = &trak->vmhd_atom_buf;
atom->temporary = 1;
atom->pos = atom_header;
atom->last = atom_header + atom_size;
trak->vmhd_size += atom_size;
trak->out[NGX_HTTP_MP4_VMHD_ATOM].buf = atom;
ngx_mp4_atom_next(mp4, atom_data_size);
return NGX_OK;
}
static ngx_int_t
ngx_http_mp4_read_smhd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
{
u_char *atom_header;
size_t atom_size;
ngx_buf_t *atom;
ngx_http_mp4_trak_t *trak;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 smhd atom");
atom_header = ngx_mp4_atom_header(mp4);
atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size;
ngx_mp4_set_32value(atom_header, atom_size);
ngx_mp4_set_atom_name(atom_header, 's', 'm', 'h', 'd');
trak = ngx_mp4_last_trak(mp4);
atom = &trak->smhd_atom_buf;
atom->temporary = 1;
atom->pos = atom_header;
atom->last = atom_header + atom_size;
trak->smhd_size += atom_size;
trak->out[NGX_HTTP_MP4_SMHD_ATOM].buf = atom;
ngx_mp4_atom_next(mp4, atom_data_size);
return NGX_OK;
}
static ngx_int_t
ngx_http_mp4_read_dinf_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
{
u_char *atom_header;
size_t atom_size;
ngx_buf_t *atom;
ngx_http_mp4_trak_t *trak;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 dinf atom");
atom_header = ngx_mp4_atom_header(mp4);
atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size;
ngx_mp4_set_32value(atom_header, atom_size);
ngx_mp4_set_atom_name(atom_header, 'd', 'i', 'n', 'f');
trak = ngx_mp4_last_trak(mp4);
atom = &trak->dinf_atom_buf;
atom->temporary = 1;
atom->pos = atom_header;
atom->last = atom_header + atom_size;
trak->dinf_size += atom_size;
trak->out[NGX_HTTP_MP4_DINF_ATOM].buf = atom;
ngx_mp4_atom_next(mp4, atom_data_size);
return NGX_OK;
}
static ngx_int_t
ngx_http_mp4_read_stbl_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
{
u_char *atom_header;
ngx_buf_t *atom;
ngx_http_mp4_trak_t *trak;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "process stbl atom");
atom_header = ngx_mp4_atom_header(mp4);
ngx_mp4_set_atom_name(atom_header, 's', 't', 'b', 'l');
trak = ngx_mp4_last_trak(mp4);
atom = &trak->stbl_atom_buf;
atom->temporary = 1;
atom->pos = atom_header;
atom->last = atom_header + sizeof(ngx_mp4_atom_header_t);
trak->out[NGX_HTTP_MP4_STBL_ATOM].buf = atom;
return ngx_http_mp4_read_atom(mp4, ngx_http_mp4_stbl_atoms, atom_data_size);
}
static void
ngx_http_mp4_update_stbl_atom(ngx_http_mp4_file_t *mp4,
ngx_http_mp4_trak_t *trak)
{
ngx_buf_t *atom;
trak->size += sizeof(ngx_mp4_atom_header_t);
atom = &trak->stbl_atom_buf;
ngx_mp4_set_32value(atom->pos, trak->size);
}
typedef struct {
u_char size[4];
u_char name[4];
u_char version[1];
u_char flags[3];
u_char entries[4];
u_char media_size[4];
u_char media_name[4];
} ngx_mp4_stsd_atom_t;
static ngx_int_t
ngx_http_mp4_read_stsd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
{
u_char *atom_header, *atom_table;
size_t atom_size;
ngx_buf_t *atom;
ngx_mp4_stsd_atom_t *stsd_atom;
ngx_http_mp4_trak_t *trak;
/* sample description atom */
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 stsd atom");
atom_header = ngx_mp4_atom_header(mp4);
stsd_atom = (ngx_mp4_stsd_atom_t *) atom_header;
atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size;
atom_table = atom_header + atom_size;
ngx_mp4_set_32value(stsd_atom->size, atom_size);
ngx_mp4_set_atom_name(stsd_atom, 's', 't', 's', 'd');
if (ngx_mp4_atom_data_size(ngx_mp4_stsd_atom_t) > atom_data_size) {
ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
"\"%s\" mp4 stsd atom too small", mp4->file.name.data);
return NGX_ERROR;
}
ngx_log_debug3(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
"stsd entries:%uD, media:%*s",
ngx_mp4_get_32value(stsd_atom->entries),
(size_t) 4, stsd_atom->media_name);
trak = ngx_mp4_last_trak(mp4);
atom = &trak->stsd_atom_buf;
atom->temporary = 1;
atom->pos = atom_header;
atom->last = atom_table;
trak->out[NGX_HTTP_MP4_STSD_ATOM].buf = atom;
trak->size += atom_size;
ngx_mp4_atom_next(mp4, atom_data_size);
return NGX_OK;
}
typedef struct {
u_char size[4];
u_char name[4];
u_char version[1];
u_char flags[3];
u_char entries[4];
} ngx_mp4_stts_atom_t;
typedef struct {
u_char count[4];
u_char duration[4];
} ngx_mp4_stts_entry_t;
static ngx_int_t
ngx_http_mp4_read_stts_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
{
u_char *atom_header, *atom_table, *atom_end;
uint32_t entries;
ngx_buf_t *atom, *data;
ngx_mp4_stts_atom_t *stts_atom;
ngx_http_mp4_trak_t *trak;
/* time-to-sample atom */
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 stts atom");
atom_header = ngx_mp4_atom_header(mp4);
stts_atom = (ngx_mp4_stts_atom_t *) atom_header;
ngx_mp4_set_atom_name(stts_atom, 's', 't', 't', 's');
if (ngx_mp4_atom_data_size(ngx_mp4_stts_atom_t) > atom_data_size) {
ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
"\"%s\" mp4 stts atom too small", mp4->file.name.data);
return NGX_ERROR;
}
entries = ngx_mp4_get_32value(stts_atom->entries);
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
"mp4 time-to-sample entries:%uD", entries);
if (ngx_mp4_atom_data_size(ngx_mp4_stts_atom_t)
+ entries * sizeof(ngx_mp4_stts_entry_t) > atom_data_size)
{
ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
"\"%s\" mp4 stts atom too small", mp4->file.name.data);
return NGX_ERROR;
}
atom_table = atom_header + sizeof(ngx_mp4_stts_atom_t);
atom_end = atom_table + entries * sizeof(ngx_mp4_stts_entry_t);
trak = ngx_mp4_last_trak(mp4);
trak->time_to_sample_entries = entries;
atom = &trak->stts_atom_buf;
atom->temporary = 1;
atom->pos = atom_header;
atom->last = atom_table;
data = &trak->stts_data_buf;
data->temporary = 1;
data->pos = atom_table;
data->last = atom_end;
trak->out[NGX_HTTP_MP4_STTS_ATOM].buf = atom;
trak->out[NGX_HTTP_MP4_STTS_DATA].buf = data;
ngx_mp4_atom_next(mp4, atom_data_size);
return NGX_OK;
}
static ngx_int_t
ngx_http_mp4_update_stts_atom(ngx_http_mp4_file_t *mp4,
ngx_http_mp4_trak_t *trak)
{
size_t atom_size;
ngx_buf_t *atom, *data;
ngx_mp4_stts_atom_t *stts_atom;
/*
* mdia.minf.stbl.stts updating requires trak->timescale
* from mdia.mdhd atom which may reside after mdia.minf
*/
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
"mp4 stts atom update");
data = trak->out[NGX_HTTP_MP4_STTS_DATA].buf;
if (data == NULL) {
ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
"no mp4 stts atoms were found in \"%s\"",
mp4->file.name.data);
return NGX_ERROR;
}
if (ngx_http_mp4_crop_stts_data(mp4, trak, 1) != NGX_OK) {
return NGX_ERROR;
}
if (ngx_http_mp4_crop_stts_data(mp4, trak, 0) != NGX_OK) {
return NGX_ERROR;
}
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
"time-to-sample entries:%uD", trak->time_to_sample_entries);
atom_size = sizeof(ngx_mp4_stts_atom_t) + (data->last - data->pos);
trak->size += atom_size;
atom = trak->out[NGX_HTTP_MP4_STTS_ATOM].buf;
stts_atom = (ngx_mp4_stts_atom_t *) atom->pos;
ngx_mp4_set_32value(stts_atom->size, atom_size);
ngx_mp4_set_32value(stts_atom->entries, trak->time_to_sample_entries);
return NGX_OK;
}
static ngx_int_t
ngx_http_mp4_crop_stts_data(ngx_http_mp4_file_t *mp4,
ngx_http_mp4_trak_t *trak, ngx_uint_t start)
{
uint32_t count, duration, rest;
uint64_t start_time;
ngx_buf_t *data;
ngx_uint_t start_sample, entries, start_sec;
ngx_mp4_stts_entry_t *entry, *end;
if (start) {
start_sec = mp4->start;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
"mp4 stts crop start_time:%ui", start_sec);
} else if (mp4->length) {
start_sec = mp4->length;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
"mp4 stts crop end_time:%ui", start_sec);
} else {
return NGX_OK;
}
data = trak->out[NGX_HTTP_MP4_STTS_DATA].buf;
start_time = (uint64_t) start_sec * trak->timescale / 1000;
entries = trak->time_to_sample_entries;
start_sample = 0;
entry = (ngx_mp4_stts_entry_t *) data->pos;
end = (ngx_mp4_stts_entry_t *) data->last;
while (entry < end) {
count = ngx_mp4_get_32value(entry->count);
duration = ngx_mp4_get_32value(entry->duration);
ngx_log_debug3(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
"time:%uL, count:%uD, duration:%uD",
start_time, count, duration);
if (start_time < (uint64_t) count * duration) {
start_sample += (ngx_uint_t) (start_time / duration);
rest = (uint32_t) (start_time / duration);
goto found;
}
start_sample += count;
start_time -= count * duration;
entries--;
entry++;
}
if (start) {
ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
"start time is out mp4 stts samples in \"%s\"",
mp4->file.name.data);
return NGX_ERROR;
} else {
trak->end_sample = trak->start_sample + start_sample;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
"end_sample:%ui", trak->end_sample);
return NGX_OK;
}
found:
if (start) {
ngx_mp4_set_32value(entry->count, count - rest);
data->pos = (u_char *) entry;
trak->time_to_sample_entries = entries;
trak->start_sample = start_sample;
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
"start_sample:%ui, new count:%uD",
trak->start_sample, count - rest);
} else {
ngx_mp4_set_32value(entry->count, rest);
data->last = (u_char *) (entry + 1);
trak->time_to_sample_entries -= entries - 1;
trak->end_sample = trak->start_sample + start_sample;
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
"end_sample:%ui, new count:%uD",
trak->end_sample, rest);
}
return NGX_OK;
}
typedef struct {
u_char size[4];
u_char name[4];
u_char version[1];
u_char flags[3];
u_char entries[4];
} ngx_http_mp4_stss_atom_t;
static ngx_int_t
ngx_http_mp4_read_stss_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
{
u_char *atom_header, *atom_table, *atom_end;
uint32_t entries;
ngx_buf_t *atom, *data;
ngx_http_mp4_trak_t *trak;
ngx_http_mp4_stss_atom_t *stss_atom;
/* sync samples atom */
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 stss atom");
atom_header = ngx_mp4_atom_header(mp4);
stss_atom = (ngx_http_mp4_stss_atom_t *) atom_header;
ngx_mp4_set_atom_name(stss_atom, 's', 't', 's', 's');
if (ngx_mp4_atom_data_size(ngx_http_mp4_stss_atom_t) > atom_data_size) {
ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
"\"%s\" mp4 stss atom too small", mp4->file.name.data);
return NGX_ERROR;
}
entries = ngx_mp4_get_32value(stss_atom->entries);
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
"sync sample entries:%uD", entries);
trak = ngx_mp4_last_trak(mp4);
trak->sync_samples_entries = entries;
atom_table = atom_header + sizeof(ngx_http_mp4_stss_atom_t);
atom = &trak->stss_atom_buf;
atom->temporary = 1;
atom->pos = atom_header;
atom->last = atom_table;
if (ngx_mp4_atom_data_size(ngx_http_mp4_stss_atom_t)
+ entries * sizeof(uint32_t) > atom_data_size)
{
ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
"\"%s\" mp4 stss atom too small", mp4->file.name.data);
return NGX_ERROR;
}
atom_end = atom_table + entries * sizeof(uint32_t);
data = &trak->stss_data_buf;
data->temporary = 1;
data->pos = atom_table;
data->last = atom_end;
trak->out[NGX_HTTP_MP4_STSS_ATOM].buf = atom;
trak->out[NGX_HTTP_MP4_STSS_DATA].buf = data;
ngx_mp4_atom_next(mp4, atom_data_size);
return NGX_OK;
}
static ngx_int_t
ngx_http_mp4_update_stss_atom(ngx_http_mp4_file_t *mp4,
ngx_http_mp4_trak_t *trak)
{
size_t atom_size;
uint32_t sample, start_sample, *entry, *end;
ngx_buf_t *atom, *data;
ngx_http_mp4_stss_atom_t *stss_atom;
/*
* mdia.minf.stbl.stss updating requires trak->start_sample
* from mdia.minf.stbl.stts which depends on value from mdia.mdhd
* atom which may reside after mdia.minf
*/
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
"mp4 stss atom update");
data = trak->out[NGX_HTTP_MP4_STSS_DATA].buf;
if (data == NULL) {
return NGX_OK;
}
ngx_http_mp4_crop_stss_data(mp4, trak, 1);
ngx_http_mp4_crop_stss_data(mp4, trak, 0);
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
"sync sample entries:%uD", trak->sync_samples_entries);
if (trak->sync_samples_entries) {
entry = (uint32_t *) data->pos;
end = (uint32_t *) data->last;
start_sample = trak->start_sample;
while (entry < end) {
sample = ngx_mp4_get_32value(entry);
sample -= start_sample;
ngx_mp4_set_32value(entry, sample);
entry++;
}
} else {
trak->out[NGX_HTTP_MP4_STSS_DATA].buf = NULL;
}
atom_size = sizeof(ngx_http_mp4_stss_atom_t) + (data->last - data->pos);
trak->size += atom_size;
atom = trak->out[NGX_HTTP_MP4_STSS_ATOM].buf;
stss_atom = (ngx_http_mp4_stss_atom_t *) atom->pos;
ngx_mp4_set_32value(stss_atom->size, atom_size);
ngx_mp4_set_32value(stss_atom->entries, trak->sync_samples_entries);
return NGX_OK;
}
static void
ngx_http_mp4_crop_stss_data(ngx_http_mp4_file_t *mp4,
ngx_http_mp4_trak_t *trak, ngx_uint_t start)
{
uint32_t sample, start_sample, *entry, *end;
ngx_buf_t *data;
ngx_uint_t entries;
/* sync samples starts from 1 */
if (start) {
start_sample = trak->start_sample + 1;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
"mp4 stss crop start_sample:%uD", start_sample);
} else if (mp4->length) {
start_sample = trak->end_sample + 1;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
"mp4 stss crop end_sample:%uD", start_sample);
} else {
return;
}
data = trak->out[NGX_HTTP_MP4_STSS_DATA].buf;
entries = trak->sync_samples_entries;
entry = (uint32_t *) data->pos;
end = (uint32_t *) data->last;
while (entry < end) {
sample = ngx_mp4_get_32value(entry);
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
"sync:%uD", sample);
if (sample >= start_sample) {
goto found;
}
entries--;
entry++;
}
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
"sample is out of mp4 stss atom");
found:
if (start) {
data->pos = (u_char *) entry;
trak->sync_samples_entries = entries;
} else {
data->last = (u_char *) entry;
trak->sync_samples_entries -= entries;
}
}
typedef struct {
u_char size[4];
u_char name[4];
u_char version[1];
u_char flags[3];
u_char entries[4];
} ngx_mp4_ctts_atom_t;
typedef struct {
u_char count[4];
u_char offset[4];
} ngx_mp4_ctts_entry_t;
static ngx_int_t
ngx_http_mp4_read_ctts_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
{
u_char *atom_header, *atom_table, *atom_end;
uint32_t entries;
ngx_buf_t *atom, *data;
ngx_mp4_ctts_atom_t *ctts_atom;
ngx_http_mp4_trak_t *trak;
/* composition offsets atom */
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 ctts atom");
atom_header = ngx_mp4_atom_header(mp4);
ctts_atom = (ngx_mp4_ctts_atom_t *) atom_header;
ngx_mp4_set_atom_name(ctts_atom, 'c', 't', 't', 's');
if (ngx_mp4_atom_data_size(ngx_mp4_ctts_atom_t) > atom_data_size) {
ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
"\"%s\" mp4 ctts atom too small", mp4->file.name.data);
return NGX_ERROR;
}
entries = ngx_mp4_get_32value(ctts_atom->entries);
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
"composition offset entries:%uD", entries);
trak = ngx_mp4_last_trak(mp4);
trak->composition_offset_entries = entries;
atom_table = atom_header + sizeof(ngx_mp4_ctts_atom_t);
atom = &trak->ctts_atom_buf;
atom->temporary = 1;
atom->pos = atom_header;
atom->last = atom_table;
if (ngx_mp4_atom_data_size(ngx_mp4_ctts_atom_t)
+ entries * sizeof(ngx_mp4_ctts_entry_t) > atom_data_size)
{
ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
"\"%s\" mp4 ctts atom too small", mp4->file.name.data);
return NGX_ERROR;
}
atom_end = atom_table + entries * sizeof(ngx_mp4_ctts_entry_t);
data = &trak->ctts_data_buf;
data->temporary = 1;
data->pos = atom_table;
data->last = atom_end;
trak->out[NGX_HTTP_MP4_CTTS_ATOM].buf = atom;
trak->out[NGX_HTTP_MP4_CTTS_DATA].buf = data;
ngx_mp4_atom_next(mp4, atom_data_size);
return NGX_OK;
}
static void
ngx_http_mp4_update_ctts_atom(ngx_http_mp4_file_t *mp4,
ngx_http_mp4_trak_t *trak)
{
size_t atom_size;
ngx_buf_t *atom, *data;
ngx_mp4_ctts_atom_t *ctts_atom;
/*
* mdia.minf.stbl.ctts updating requires trak->start_sample
* from mdia.minf.stbl.stts which depends on value from mdia.mdhd
* atom which may reside after mdia.minf
*/
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
"mp4 ctts atom update");
data = trak->out[NGX_HTTP_MP4_CTTS_DATA].buf;
if (data == NULL) {
return;
}
ngx_http_mp4_crop_ctts_data(mp4, trak, 1);
ngx_http_mp4_crop_ctts_data(mp4, trak, 0);
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
"composition offset entries:%uD",
trak->composition_offset_entries);
if (trak->composition_offset_entries == 0) {
trak->out[NGX_HTTP_MP4_CTTS_ATOM].buf = NULL;
trak->out[NGX_HTTP_MP4_CTTS_DATA].buf = NULL;
return;
}
atom_size = sizeof(ngx_mp4_ctts_atom_t) + (data->last - data->pos);
trak->size += atom_size;
atom = trak->out[NGX_HTTP_MP4_CTTS_ATOM].buf;
ctts_atom = (ngx_mp4_ctts_atom_t *) atom->pos;
ngx_mp4_set_32value(ctts_atom->size, atom_size);
ngx_mp4_set_32value(ctts_atom->entries, trak->composition_offset_entries);
return;
}
static void
ngx_http_mp4_crop_ctts_data(ngx_http_mp4_file_t *mp4,
ngx_http_mp4_trak_t *trak, ngx_uint_t start)
{
uint32_t count, start_sample, rest;
ngx_buf_t *data;
ngx_uint_t entries;
ngx_mp4_ctts_entry_t *entry, *end;
/* sync samples starts from 1 */
if (start) {
start_sample = trak->start_sample + 1;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
"mp4 ctts crop start_sample:%uD", start_sample);
} else if (mp4->length) {
start_sample = trak->end_sample - trak->start_sample + 1;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
"mp4 ctts crop end_sample:%uD", start_sample);
} else {
return;
}
data = trak->out[NGX_HTTP_MP4_CTTS_DATA].buf;
entries = trak->composition_offset_entries;
entry = (ngx_mp4_ctts_entry_t *) data->pos;
end = (ngx_mp4_ctts_entry_t *) data->last;
while (entry < end) {
count = ngx_mp4_get_32value(entry->count);
ngx_log_debug3(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
"sample:%uD, count:%uD, offset:%uD",
start_sample, count, ngx_mp4_get_32value(entry->offset));
if (start_sample <= count) {
rest = start_sample - 1;
goto found;
}
start_sample -= count;
entries--;
entry++;
}
if (start) {
data->pos = (u_char *) end;
trak->composition_offset_entries = 0;
}
return;
found:
if (start) {
ngx_mp4_set_32value(entry->count, count - rest);
data->pos = (u_char *) entry;
trak->composition_offset_entries = entries;
} else {
ngx_mp4_set_32value(entry->count, rest);
data->last = (u_char *) (entry + 1);
trak->composition_offset_entries -= entries - 1;
}
}
typedef struct {
u_char size[4];
u_char name[4];
u_char version[1];
u_char flags[3];
u_char entries[4];
} ngx_mp4_stsc_atom_t;
static ngx_int_t
ngx_http_mp4_read_stsc_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
{
u_char *atom_header, *atom_table, *atom_end;
uint32_t entries;
ngx_buf_t *atom, *data;
ngx_mp4_stsc_atom_t *stsc_atom;
ngx_http_mp4_trak_t *trak;
/* sample-to-chunk atom */
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 stsc atom");
atom_header = ngx_mp4_atom_header(mp4);
stsc_atom = (ngx_mp4_stsc_atom_t *) atom_header;
ngx_mp4_set_atom_name(stsc_atom, 's', 't', 's', 'c');
if (ngx_mp4_atom_data_size(ngx_mp4_stsc_atom_t) > atom_data_size) {
ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
"\"%s\" mp4 stsc atom too small", mp4->file.name.data);
return NGX_ERROR;
}
entries = ngx_mp4_get_32value(stsc_atom->entries);
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
"sample-to-chunk entries:%uD", entries);
if (ngx_mp4_atom_data_size(ngx_mp4_stsc_atom_t)
+ entries * sizeof(ngx_mp4_stsc_entry_t) > atom_data_size)
{
ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
"\"%s\" mp4 stsc atom too small", mp4->file.name.data);
return NGX_ERROR;
}
atom_table = atom_header + sizeof(ngx_mp4_stsc_atom_t);
atom_end = atom_table + entries * sizeof(ngx_mp4_stsc_entry_t);
trak = ngx_mp4_last_trak(mp4);
trak->sample_to_chunk_entries = entries;
atom = &trak->stsc_atom_buf;
atom->temporary = 1;
atom->pos = atom_header;
atom->last = atom_table;
data = &trak->stsc_data_buf;
data->temporary = 1;
data->pos = atom_table;
data->last = atom_end;
trak->out[NGX_HTTP_MP4_STSC_ATOM].buf = atom;
trak->out[NGX_HTTP_MP4_STSC_DATA].buf = data;
ngx_mp4_atom_next(mp4, atom_data_size);
return NGX_OK;
}
static ngx_int_t
ngx_http_mp4_update_stsc_atom(ngx_http_mp4_file_t *mp4,
ngx_http_mp4_trak_t *trak)
{
size_t atom_size;
uint32_t chunk;
ngx_buf_t *atom, *data;
ngx_mp4_stsc_atom_t *stsc_atom;
ngx_mp4_stsc_entry_t *entry, *end;
/*
* mdia.minf.stbl.stsc updating requires trak->start_sample
* from mdia.minf.stbl.stts which depends on value from mdia.mdhd
* atom which may reside after mdia.minf
*/
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
"mp4 stsc atom update");
data = trak->out[NGX_HTTP_MP4_STSC_DATA].buf;
if (data == NULL) {
ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
"no mp4 stsc atoms were found in \"%s\"",
mp4->file.name.data);
return NGX_ERROR;
}
if (trak->sample_to_chunk_entries == 0) {
ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
"zero number of entries in stsc atom in \"%s\"",
mp4->file.name.data);
return NGX_ERROR;
}
if (ngx_http_mp4_crop_stsc_data(mp4, trak, 1) != NGX_OK) {
return NGX_ERROR;
}
if (ngx_http_mp4_crop_stsc_data(mp4, trak, 0) != NGX_OK) {
return NGX_ERROR;
}
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
"sample-to-chunk entries:%uD",
trak->sample_to_chunk_entries);
entry = (ngx_mp4_stsc_entry_t *) data->pos;
end = (ngx_mp4_stsc_entry_t *) data->last;
while (entry < end) {
chunk = ngx_mp4_get_32value(entry->chunk);
chunk -= trak->start_chunk;
ngx_mp4_set_32value(entry->chunk, chunk);
entry++;
}
atom_size = sizeof(ngx_mp4_stsc_atom_t)
+ trak->sample_to_chunk_entries * sizeof(ngx_mp4_stsc_entry_t);
trak->size += atom_size;
atom = trak->out[NGX_HTTP_MP4_STSC_ATOM].buf;
stsc_atom = (ngx_mp4_stsc_atom_t *) atom->pos;
ngx_mp4_set_32value(stsc_atom->size, atom_size);
ngx_mp4_set_32value(stsc_atom->entries, trak->sample_to_chunk_entries);
return NGX_OK;
}
static ngx_int_t
ngx_http_mp4_crop_stsc_data(ngx_http_mp4_file_t *mp4,
ngx_http_mp4_trak_t *trak, ngx_uint_t start)
{
uint32_t start_sample, chunk, samples, id, next_chunk, n,
prev_samples;
ngx_buf_t *data, *buf;
ngx_uint_t entries, target_chunk, chunk_samples;
ngx_mp4_stsc_entry_t *entry, *end, *first;
entries = trak->sample_to_chunk_entries - 1;
if (start) {
start_sample = (uint32_t) trak->start_sample;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
"mp4 stsc crop start_sample:%uD", start_sample);
} else if (mp4->length) {
start_sample = (uint32_t) (trak->end_sample - trak->start_sample);
samples = 0;
data = trak->out[NGX_HTTP_MP4_STSC_START].buf;
if (data) {
entry = (ngx_mp4_stsc_entry_t *) data->pos;
samples = ngx_mp4_get_32value(entry->samples);
entries--;
if (samples > start_sample) {
samples = start_sample;
ngx_mp4_set_32value(entry->samples, samples);
}
start_sample -= samples;
}
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
"mp4 stsc crop end_sample:%uD, ext_samples:%uD",
start_sample, samples);
} else {
return NGX_OK;
}
data = trak->out[NGX_HTTP_MP4_STSC_DATA].buf;
entry = (ngx_mp4_stsc_entry_t *) data->pos;
end = (ngx_mp4_stsc_entry_t *) data->last;
chunk = ngx_mp4_get_32value(entry->chunk);
samples = ngx_mp4_get_32value(entry->samples);
id = ngx_mp4_get_32value(entry->id);
prev_samples = 0;
entry++;
while (entry < end) {
next_chunk = ngx_mp4_get_32value(entry->chunk);
ngx_log_debug5(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
"sample:%uD, chunk:%uD, chunks:%uD, "
"samples:%uD, id:%uD",
start_sample, chunk, next_chunk - chunk, samples, id);
n = (next_chunk - chunk) * samples;
if (start_sample < n) {
goto found;
}
start_sample -= n;
prev_samples = samples;
chunk = next_chunk;
samples = ngx_mp4_get_32value(entry->samples);
id = ngx_mp4_get_32value(entry->id);
entries--;
entry++;
}
next_chunk = trak->chunks + 1;
ngx_log_debug4(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
"sample:%uD, chunk:%uD, chunks:%uD, samples:%uD",
start_sample, chunk, next_chunk - chunk, samples);
n = (next_chunk - chunk) * samples;
if (start_sample > n) {
ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
"%s time is out mp4 stsc chunks in \"%s\"",
start ? "start" : "end", mp4->file.name.data);
return NGX_ERROR;
}
found:
entries++;
entry--;
if (samples == 0) {
ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
"zero number of samples in \"%s\"",
mp4->file.name.data);
return NGX_ERROR;
}
target_chunk = chunk - 1;
target_chunk += start_sample / samples;
chunk_samples = start_sample % samples;
if (start) {
data->pos = (u_char *) entry;
trak->sample_to_chunk_entries = entries;
trak->start_chunk = target_chunk;
trak->start_chunk_samples = chunk_samples;
ngx_mp4_set_32value(entry->chunk, trak->start_chunk + 1);
samples -= chunk_samples;
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
"start_chunk:%ui, start_chunk_samples:%ui",
trak->start_chunk, trak->start_chunk_samples);
} else {
if (start_sample) {
data->last = (u_char *) (entry + 1);
trak->sample_to_chunk_entries -= entries - 1;
trak->end_chunk_samples = samples;
} else {
data->last = (u_char *) entry;
trak->sample_to_chunk_entries -= entries;
trak->end_chunk_samples = prev_samples;
}
if (chunk_samples) {
trak->end_chunk = target_chunk + 1;
trak->end_chunk_samples = chunk_samples;
} else {
trak->end_chunk = target_chunk;
}
samples = chunk_samples;
next_chunk = chunk + 1;
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
"end_chunk:%ui, end_chunk_samples:%ui",
trak->end_chunk, trak->end_chunk_samples);
}
if (chunk_samples && next_chunk - target_chunk == 2) {
ngx_mp4_set_32value(entry->samples, samples);
} else if (chunk_samples && start) {
first = &trak->stsc_start_chunk_entry;
ngx_mp4_set_32value(first->chunk, 1);
ngx_mp4_set_32value(first->samples, samples);
ngx_mp4_set_32value(first->id, id);
buf = &trak->stsc_start_chunk_buf;
buf->temporary = 1;
buf->pos = (u_char *) first;
buf->last = (u_char *) first + sizeof(ngx_mp4_stsc_entry_t);
trak->out[NGX_HTTP_MP4_STSC_START].buf = buf;
ngx_mp4_set_32value(entry->chunk, trak->start_chunk + 2);
trak->sample_to_chunk_entries++;
} else if (chunk_samples) {
first = &trak->stsc_end_chunk_entry;
ngx_mp4_set_32value(first->chunk, trak->end_chunk - trak->start_chunk);
ngx_mp4_set_32value(first->samples, samples);
ngx_mp4_set_32value(first->id, id);
buf = &trak->stsc_end_chunk_buf;
buf->temporary = 1;
buf->pos = (u_char *) first;
buf->last = (u_char *) first + sizeof(ngx_mp4_stsc_entry_t);
trak->out[NGX_HTTP_MP4_STSC_END].buf = buf;
trak->sample_to_chunk_entries++;
}
return NGX_OK;
}
typedef struct {
u_char size[4];
u_char name[4];
u_char version[1];
u_char flags[3];
u_char uniform_size[4];
u_char entries[4];
} ngx_mp4_stsz_atom_t;
static ngx_int_t
ngx_http_mp4_read_stsz_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
{
u_char *atom_header, *atom_table, *atom_end;
size_t atom_size;
uint32_t entries, size;
ngx_buf_t *atom, *data;
ngx_mp4_stsz_atom_t *stsz_atom;
ngx_http_mp4_trak_t *trak;
/* sample sizes atom */
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 stsz atom");
atom_header = ngx_mp4_atom_header(mp4);
stsz_atom = (ngx_mp4_stsz_atom_t *) atom_header;
ngx_mp4_set_atom_name(stsz_atom, 's', 't', 's', 'z');
if (ngx_mp4_atom_data_size(ngx_mp4_stsz_atom_t) > atom_data_size) {
ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
"\"%s\" mp4 stsz atom too small", mp4->file.name.data);
return NGX_ERROR;
}
size = ngx_mp4_get_32value(stsz_atom->uniform_size);
entries = ngx_mp4_get_32value(stsz_atom->entries);
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
"sample uniform size:%uD, entries:%uD", size, entries);
trak = ngx_mp4_last_trak(mp4);
trak->sample_sizes_entries = entries;
atom_table = atom_header + sizeof(ngx_mp4_stsz_atom_t);
atom = &trak->stsz_atom_buf;
atom->temporary = 1;
atom->pos = atom_header;
atom->last = atom_table;
trak->out[NGX_HTTP_MP4_STSZ_ATOM].buf = atom;
if (size == 0) {
if (ngx_mp4_atom_data_size(ngx_mp4_stsz_atom_t)
+ entries * sizeof(uint32_t) > atom_data_size)
{
ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
"\"%s\" mp4 stsz atom too small",
mp4->file.name.data);
return NGX_ERROR;
}
atom_end = atom_table + entries * sizeof(uint32_t);
data = &trak->stsz_data_buf;
data->temporary = 1;
data->pos = atom_table;
data->last = atom_end;
trak->out[NGX_HTTP_MP4_STSZ_DATA].buf = data;
} else {
/* if size != 0 then all samples are the same size */
/* TODO : chunk samples */
atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size;
ngx_mp4_set_32value(atom_header, atom_size);
trak->size += atom_size;
}
ngx_mp4_atom_next(mp4, atom_data_size);
return NGX_OK;
}
static ngx_int_t
ngx_http_mp4_update_stsz_atom(ngx_http_mp4_file_t *mp4,
ngx_http_mp4_trak_t *trak)
{
size_t atom_size;
uint32_t *pos, *end, entries;
ngx_buf_t *atom, *data;
ngx_mp4_stsz_atom_t *stsz_atom;
/*
* mdia.minf.stbl.stsz updating requires trak->start_sample
* from mdia.minf.stbl.stts which depends on value from mdia.mdhd
* atom which may reside after mdia.minf
*/
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
"mp4 stsz atom update");
data = trak->out[NGX_HTTP_MP4_STSZ_DATA].buf;
if (data) {
entries = trak->sample_sizes_entries;
if (trak->start_sample > entries) {
ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
"start time is out mp4 stsz samples in \"%s\"",
mp4->file.name.data);
return NGX_ERROR;
}
entries -= trak->start_sample;
data->pos += trak->start_sample * sizeof(uint32_t);
end = (uint32_t *) data->pos;
for (pos = end - trak->start_chunk_samples; pos < end; pos++) {
trak->start_chunk_samples_size += ngx_mp4_get_32value(pos);
}
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
"chunk samples sizes:%uL",
trak->start_chunk_samples_size);
if (mp4->length) {
if (trak->end_sample - trak->start_sample > entries) {
ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
"end time is out mp4 stsz samples in \"%s\"",
mp4->file.name.data);
return NGX_ERROR;
}
entries = trak->end_sample - trak->start_sample;
data->last = data->pos + entries * sizeof(uint32_t);
end = (uint32_t *) data->last;
for (pos = end - trak->end_chunk_samples; pos < end; pos++) {
trak->end_chunk_samples_size += ngx_mp4_get_32value(pos);
}
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
"mp4 stsz end_chunk_samples_size:%uL",
trak->end_chunk_samples_size);
}
atom_size = sizeof(ngx_mp4_stsz_atom_t) + (data->last - data->pos);
trak->size += atom_size;
atom = trak->out[NGX_HTTP_MP4_STSZ_ATOM].buf;
stsz_atom = (ngx_mp4_stsz_atom_t *) atom->pos;
ngx_mp4_set_32value(stsz_atom->size, atom_size);
ngx_mp4_set_32value(stsz_atom->entries, entries);
}
return NGX_OK;
}
typedef struct {
u_char size[4];
u_char name[4];
u_char version[1];
u_char flags[3];
u_char entries[4];
} ngx_mp4_stco_atom_t;
static ngx_int_t
ngx_http_mp4_read_stco_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
{
u_char *atom_header, *atom_table, *atom_end;
uint32_t entries;
ngx_buf_t *atom, *data;
ngx_mp4_stco_atom_t *stco_atom;
ngx_http_mp4_trak_t *trak;
/* chunk offsets atom */
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 stco atom");
atom_header = ngx_mp4_atom_header(mp4);
stco_atom = (ngx_mp4_stco_atom_t *) atom_header;
ngx_mp4_set_atom_name(stco_atom, 's', 't', 'c', 'o');
if (ngx_mp4_atom_data_size(ngx_mp4_stco_atom_t) > atom_data_size) {
ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
"\"%s\" mp4 stco atom too small", mp4->file.name.data);
return NGX_ERROR;
}
entries = ngx_mp4_get_32value(stco_atom->entries);
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "chunks:%uD", entries);
if (ngx_mp4_atom_data_size(ngx_mp4_stco_atom_t)
+ entries * sizeof(uint32_t) > atom_data_size)
{
ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
"\"%s\" mp4 stco atom too small", mp4->file.name.data);
return NGX_ERROR;
}
atom_table = atom_header + sizeof(ngx_mp4_stco_atom_t);
atom_end = atom_table + entries * sizeof(uint32_t);
trak = ngx_mp4_last_trak(mp4);
trak->chunks = entries;
atom = &trak->stco_atom_buf;
atom->temporary = 1;
atom->pos = atom_header;
atom->last = atom_table;
data = &trak->stco_data_buf;
data->temporary = 1;
data->pos = atom_table;
data->last = atom_end;
trak->out[NGX_HTTP_MP4_STCO_ATOM].buf = atom;
trak->out[NGX_HTTP_MP4_STCO_DATA].buf = data;
ngx_mp4_atom_next(mp4, atom_data_size);
return NGX_OK;
}
static ngx_int_t
ngx_http_mp4_update_stco_atom(ngx_http_mp4_file_t *mp4,
ngx_http_mp4_trak_t *trak)
{
size_t atom_size;
uint32_t entries;
ngx_buf_t *atom, *data;
ngx_mp4_stco_atom_t *stco_atom;
/*
* mdia.minf.stbl.stco updating requires trak->start_chunk
* from mdia.minf.stbl.stsc which depends on value from mdia.mdhd
* atom which may reside after mdia.minf
*/
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
"mp4 stco atom update");
data = trak->out[NGX_HTTP_MP4_STCO_DATA].buf;
if (data == NULL) {
ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
"no mp4 stco atoms were found in \"%s\"",
mp4->file.name.data);
return NGX_ERROR;
}
if (trak->start_chunk > trak->chunks) {
ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
"start time is out mp4 stco chunks in \"%s\"",
mp4->file.name.data);
return NGX_ERROR;
}
data->pos += trak->start_chunk * sizeof(uint32_t);
trak->start_offset = ngx_mp4_get_32value(data->pos);
trak->start_offset += trak->start_chunk_samples_size;
ngx_mp4_set_32value(data->pos, trak->start_offset);
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
"start chunk offset:%O", trak->start_offset);
if (mp4->length) {
if (trak->end_chunk > trak->chunks) {
ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
"end time is out mp4 stco chunks in \"%s\"",
mp4->file.name.data);
return NGX_ERROR;
}
entries = trak->end_chunk - trak->start_chunk;
data->last = data->pos + entries * sizeof(uint32_t);
if (entries) {
trak->end_offset =
ngx_mp4_get_32value(data->last - sizeof(uint32_t));
trak->end_offset += trak->end_chunk_samples_size;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
"end chunk offset:%O", trak->end_offset);
}
} else {
entries = trak->chunks - trak->start_chunk;
trak->end_offset = mp4->mdat_data.buf->file_last;
}
if (entries == 0) {
trak->start_offset = mp4->end;
trak->end_offset = 0;
}
atom_size = sizeof(ngx_mp4_stco_atom_t) + (data->last - data->pos);
trak->size += atom_size;
atom = trak->out[NGX_HTTP_MP4_STCO_ATOM].buf;
stco_atom = (ngx_mp4_stco_atom_t *) atom->pos;
ngx_mp4_set_32value(stco_atom->size, atom_size);
ngx_mp4_set_32value(stco_atom->entries, entries);
return NGX_OK;
}
static void
ngx_http_mp4_adjust_stco_atom(ngx_http_mp4_file_t *mp4,
ngx_http_mp4_trak_t *trak, int32_t adjustment)
{
uint32_t offset, *entry, *end;
ngx_buf_t *data;
/*
* moov.trak.mdia.minf.stbl.stco adjustment requires
* minimal start offset of all traks and new moov atom size
*/
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
"mp4 stco atom adjustment");
data = trak->out[NGX_HTTP_MP4_STCO_DATA].buf;
entry = (uint32_t *) data->pos;
end = (uint32_t *) data->last;
while (entry < end) {
offset = ngx_mp4_get_32value(entry);
offset += adjustment;
ngx_mp4_set_32value(entry, offset);
entry++;
}
}
typedef struct {
u_char size[4];
u_char name[4];
u_char version[1];
u_char flags[3];
u_char entries[4];
} ngx_mp4_co64_atom_t;
static ngx_int_t
ngx_http_mp4_read_co64_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
{
u_char *atom_header, *atom_table, *atom_end;
uint32_t entries;
ngx_buf_t *atom, *data;
ngx_mp4_co64_atom_t *co64_atom;
ngx_http_mp4_trak_t *trak;
/* chunk offsets atom */
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 co64 atom");
atom_header = ngx_mp4_atom_header(mp4);
co64_atom = (ngx_mp4_co64_atom_t *) atom_header;
ngx_mp4_set_atom_name(co64_atom, 'c', 'o', '6', '4');
if (ngx_mp4_atom_data_size(ngx_mp4_co64_atom_t) > atom_data_size) {
ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
"\"%s\" mp4 co64 atom too small", mp4->file.name.data);
return NGX_ERROR;
}
entries = ngx_mp4_get_32value(co64_atom->entries);
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "chunks:%uD", entries);
if (ngx_mp4_atom_data_size(ngx_mp4_co64_atom_t)
+ entries * sizeof(uint64_t) > atom_data_size)
{
ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
"\"%s\" mp4 co64 atom too small", mp4->file.name.data);
return NGX_ERROR;
}
atom_table = atom_header + sizeof(ngx_mp4_co64_atom_t);
atom_end = atom_table + entries * sizeof(uint64_t);
trak = ngx_mp4_last_trak(mp4);
trak->chunks = entries;
atom = &trak->co64_atom_buf;
atom->temporary = 1;
atom->pos = atom_header;
atom->last = atom_table;
data = &trak->co64_data_buf;
data->temporary = 1;
data->pos = atom_table;
data->last = atom_end;
trak->out[NGX_HTTP_MP4_CO64_ATOM].buf = atom;
trak->out[NGX_HTTP_MP4_CO64_DATA].buf = data;
ngx_mp4_atom_next(mp4, atom_data_size);
return NGX_OK;
}
static ngx_int_t
ngx_http_mp4_update_co64_atom(ngx_http_mp4_file_t *mp4,
ngx_http_mp4_trak_t *trak)
{
size_t atom_size;
uint64_t entries;
ngx_buf_t *atom, *data;
ngx_mp4_co64_atom_t *co64_atom;
/*
* mdia.minf.stbl.co64 updating requires trak->start_chunk
* from mdia.minf.stbl.stsc which depends on value from mdia.mdhd
* atom which may reside after mdia.minf
*/
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
"mp4 co64 atom update");
data = trak->out[NGX_HTTP_MP4_CO64_DATA].buf;
if (data == NULL) {
ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
"no mp4 co64 atoms were found in \"%s\"",
mp4->file.name.data);
return NGX_ERROR;
}
if (trak->start_chunk > trak->chunks) {
ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
"start time is out mp4 co64 chunks in \"%s\"",
mp4->file.name.data);
return NGX_ERROR;
}
data->pos += trak->start_chunk * sizeof(uint64_t);
trak->start_offset = ngx_mp4_get_64value(data->pos);
trak->start_offset += trak->start_chunk_samples_size;
ngx_mp4_set_64value(data->pos, trak->start_offset);
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
"start chunk offset:%O", trak->start_offset);
if (mp4->length) {
if (trak->end_chunk > trak->chunks) {
ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
"end time is out mp4 co64 chunks in \"%s\"",
mp4->file.name.data);
return NGX_ERROR;
}
entries = trak->end_chunk - trak->start_chunk;
data->last = data->pos + entries * sizeof(uint64_t);
if (entries) {
trak->end_offset =
ngx_mp4_get_64value(data->last - sizeof(uint64_t));
trak->end_offset += trak->end_chunk_samples_size;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
"end chunk offset:%O", trak->end_offset);
}
} else {
entries = trak->chunks - trak->start_chunk;
trak->end_offset = mp4->mdat_data.buf->file_last;
}
if (entries == 0) {
trak->start_offset = mp4->end;
trak->end_offset = 0;
}
atom_size = sizeof(ngx_mp4_co64_atom_t) + (data->last - data->pos);
trak->size += atom_size;
atom = trak->out[NGX_HTTP_MP4_CO64_ATOM].buf;
co64_atom = (ngx_mp4_co64_atom_t *) atom->pos;
ngx_mp4_set_32value(co64_atom->size, atom_size);
ngx_mp4_set_32value(co64_atom->entries, entries);
return NGX_OK;
}
static void
ngx_http_mp4_adjust_co64_atom(ngx_http_mp4_file_t *mp4,
ngx_http_mp4_trak_t *trak, off_t adjustment)
{
uint64_t offset, *entry, *end;
ngx_buf_t *data;
/*
* moov.trak.mdia.minf.stbl.co64 adjustment requires
* minimal start offset of all traks and new moov atom size
*/
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
"mp4 co64 atom adjustment");
data = trak->out[NGX_HTTP_MP4_CO64_DATA].buf;
entry = (uint64_t *) data->pos;
end = (uint64_t *) data->last;
while (entry < end) {
offset = ngx_mp4_get_64value(entry);
offset += adjustment;
ngx_mp4_set_64value(entry, offset);
entry++;
}
}
static char *
ngx_http_mp4(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_core_loc_conf_t *clcf;
clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
clcf->handler = ngx_http_mp4_handler;
return NGX_CONF_OK;
}
static void *
ngx_http_mp4_create_conf(ngx_conf_t *cf)
{
ngx_http_mp4_conf_t *conf;
conf = ngx_palloc(cf->pool, sizeof(ngx_http_mp4_conf_t));
if (conf == NULL) {
return NULL;
}
conf->buffer_size = NGX_CONF_UNSET_SIZE;
conf->max_buffer_size = NGX_CONF_UNSET_SIZE;
return conf;
}
static char *
ngx_http_mp4_merge_conf(ngx_conf_t *cf, void *parent, void *child)
{
ngx_http_mp4_conf_t *prev = parent;
ngx_http_mp4_conf_t *conf = child;
ngx_conf_merge_size_value(conf->buffer_size, prev->buffer_size, 512 * 1024);
ngx_conf_merge_size_value(conf->max_buffer_size, prev->max_buffer_size,
10 * 1024 * 1024);
return NGX_CONF_OK;
}
nginx-1.14.0/src/http/modules/ngx_http_random_index_module.c 000644 001751 001751 00000021434 13265410474 025431 0 ustar 00mdounin mdounin 000000 000000
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#include
#include
#include
typedef struct {
ngx_flag_t enable;
} ngx_http_random_index_loc_conf_t;
#define NGX_HTTP_RANDOM_INDEX_PREALLOCATE 50
static ngx_int_t ngx_http_random_index_error(ngx_http_request_t *r,
ngx_dir_t *dir, ngx_str_t *name);
static ngx_int_t ngx_http_random_index_init(ngx_conf_t *cf);
static void *ngx_http_random_index_create_loc_conf(ngx_conf_t *cf);
static char *ngx_http_random_index_merge_loc_conf(ngx_conf_t *cf,
void *parent, void *child);
static ngx_command_t ngx_http_random_index_commands[] = {
{ ngx_string("random_index"),
NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_random_index_loc_conf_t, enable),
NULL },
ngx_null_command
};
static ngx_http_module_t ngx_http_random_index_module_ctx = {
NULL, /* preconfiguration */
ngx_http_random_index_init, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
ngx_http_random_index_create_loc_conf, /* create location configuration */
ngx_http_random_index_merge_loc_conf /* merge location configuration */
};
ngx_module_t ngx_http_random_index_module = {
NGX_MODULE_V1,
&ngx_http_random_index_module_ctx, /* module context */
ngx_http_random_index_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static ngx_int_t
ngx_http_random_index_handler(ngx_http_request_t *r)
{
u_char *last, *filename;
size_t len, allocated, root;
ngx_err_t err;
ngx_int_t rc;
ngx_str_t path, uri, *name;
ngx_dir_t dir;
ngx_uint_t n, level;
ngx_array_t names;
ngx_http_random_index_loc_conf_t *rlcf;
if (r->uri.data[r->uri.len - 1] != '/') {
return NGX_DECLINED;
}
if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD|NGX_HTTP_POST))) {
return NGX_DECLINED;
}
rlcf = ngx_http_get_module_loc_conf(r, ngx_http_random_index_module);
if (!rlcf->enable) {
return NGX_DECLINED;
}
#if (NGX_HAVE_D_TYPE)
len = NGX_DIR_MASK_LEN;
#else
len = NGX_HTTP_RANDOM_INDEX_PREALLOCATE;
#endif
last = ngx_http_map_uri_to_path(r, &path, &root, len);
if (last == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
allocated = path.len;
path.len = last - path.data - 1;
path.data[path.len] = '\0';
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http random index: \"%s\"", path.data);
if (ngx_open_dir(&path, &dir) == NGX_ERROR) {
err = ngx_errno;
if (err == NGX_ENOENT
|| err == NGX_ENOTDIR
|| err == NGX_ENAMETOOLONG)
{
level = NGX_LOG_ERR;
rc = NGX_HTTP_NOT_FOUND;
} else if (err == NGX_EACCES) {
level = NGX_LOG_ERR;
rc = NGX_HTTP_FORBIDDEN;
} else {
level = NGX_LOG_CRIT;
rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
}
ngx_log_error(level, r->connection->log, err,
ngx_open_dir_n " \"%s\" failed", path.data);
return rc;
}
if (ngx_array_init(&names, r->pool, 32, sizeof(ngx_str_t)) != NGX_OK) {
return ngx_http_random_index_error(r, &dir, &path);
}
filename = path.data;
filename[path.len] = '/';
for ( ;; ) {
ngx_set_errno(0);
if (ngx_read_dir(&dir) == NGX_ERROR) {
err = ngx_errno;
if (err != NGX_ENOMOREFILES) {
ngx_log_error(NGX_LOG_CRIT, r->connection->log, err,
ngx_read_dir_n " \"%V\" failed", &path);
return ngx_http_random_index_error(r, &dir, &path);
}
break;
}
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http random index file: \"%s\"", ngx_de_name(&dir));
if (ngx_de_name(&dir)[0] == '.') {
continue;
}
len = ngx_de_namelen(&dir);
if (dir.type == 0 || ngx_de_is_link(&dir)) {
/* 1 byte for '/' and 1 byte for terminating '\0' */
if (path.len + 1 + len + 1 > allocated) {
allocated = path.len + 1 + len + 1
+ NGX_HTTP_RANDOM_INDEX_PREALLOCATE;
filename = ngx_pnalloc(r->pool, allocated);
if (filename == NULL) {
return ngx_http_random_index_error(r, &dir, &path);
}
last = ngx_cpystrn(filename, path.data, path.len + 1);
*last++ = '/';
}
ngx_cpystrn(last, ngx_de_name(&dir), len + 1);
if (ngx_de_info(filename, &dir) == NGX_FILE_ERROR) {
err = ngx_errno;
if (err != NGX_ENOENT) {
ngx_log_error(NGX_LOG_CRIT, r->connection->log, err,
ngx_de_info_n " \"%s\" failed", filename);
return ngx_http_random_index_error(r, &dir, &path);
}
if (ngx_de_link_info(filename, &dir) == NGX_FILE_ERROR) {
ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,
ngx_de_link_info_n " \"%s\" failed",
filename);
return ngx_http_random_index_error(r, &dir, &path);
}
}
}
if (!ngx_de_is_file(&dir)) {
continue;
}
name = ngx_array_push(&names);
if (name == NULL) {
return ngx_http_random_index_error(r, &dir, &path);
}
name->len = len;
name->data = ngx_pnalloc(r->pool, len);
if (name->data == NULL) {
return ngx_http_random_index_error(r, &dir, &path);
}
ngx_memcpy(name->data, ngx_de_name(&dir), len);
}
if (ngx_close_dir(&dir) == NGX_ERROR) {
ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno,
ngx_close_dir_n " \"%V\" failed", &path);
}
n = names.nelts;
if (n == 0) {
return NGX_DECLINED;
}
name = names.elts;
n = (ngx_uint_t) (((uint64_t) ngx_random() * n) / 0x80000000);
uri.len = r->uri.len + name[n].len;
uri.data = ngx_pnalloc(r->pool, uri.len);
if (uri.data == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
last = ngx_copy(uri.data, r->uri.data, r->uri.len);
ngx_memcpy(last, name[n].data, name[n].len);
return ngx_http_internal_redirect(r, &uri, &r->args);
}
static ngx_int_t
ngx_http_random_index_error(ngx_http_request_t *r, ngx_dir_t *dir,
ngx_str_t *name)
{
if (ngx_close_dir(dir) == NGX_ERROR) {
ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno,
ngx_close_dir_n " \"%V\" failed", name);
}
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
static void *
ngx_http_random_index_create_loc_conf(ngx_conf_t *cf)
{
ngx_http_random_index_loc_conf_t *conf;
conf = ngx_palloc(cf->pool, sizeof(ngx_http_random_index_loc_conf_t));
if (conf == NULL) {
return NULL;
}
conf->enable = NGX_CONF_UNSET;
return conf;
}
static char *
ngx_http_random_index_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
{
ngx_http_random_index_loc_conf_t *prev = parent;
ngx_http_random_index_loc_conf_t *conf = child;
ngx_conf_merge_value(conf->enable, prev->enable, 0);
return NGX_CONF_OK;
}
static ngx_int_t
ngx_http_random_index_init(ngx_conf_t *cf)
{
ngx_http_handler_pt *h;
ngx_http_core_main_conf_t *cmcf;
cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers);
if (h == NULL) {
return NGX_ERROR;
}
*h = ngx_http_random_index_handler;
return NGX_OK;
}
nginx-1.14.0/src/http/modules/ngx_http_proxy_module.c 000644 001751 001751 00000360413 13265410474 024146 0 ustar 00mdounin mdounin 000000 000000
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#include
#include
#include
typedef struct {
ngx_array_t caches; /* ngx_http_file_cache_t * */
} ngx_http_proxy_main_conf_t;
typedef struct ngx_http_proxy_rewrite_s ngx_http_proxy_rewrite_t;
typedef ngx_int_t (*ngx_http_proxy_rewrite_pt)(ngx_http_request_t *r,
ngx_table_elt_t *h, size_t prefix, size_t len,
ngx_http_proxy_rewrite_t *pr);
struct ngx_http_proxy_rewrite_s {
ngx_http_proxy_rewrite_pt handler;
union {
ngx_http_complex_value_t complex;
#if (NGX_PCRE)
ngx_http_regex_t *regex;
#endif
} pattern;
ngx_http_complex_value_t replacement;
};
typedef struct {
ngx_str_t key_start;
ngx_str_t schema;
ngx_str_t host_header;
ngx_str_t port;
ngx_str_t uri;
} ngx_http_proxy_vars_t;
typedef struct {
ngx_array_t *flushes;
ngx_array_t *lengths;
ngx_array_t *values;
ngx_hash_t hash;
} ngx_http_proxy_headers_t;
typedef struct {
ngx_http_upstream_conf_t upstream;
ngx_array_t *body_flushes;
ngx_array_t *body_lengths;
ngx_array_t *body_values;
ngx_str_t body_source;
ngx_http_proxy_headers_t headers;
#if (NGX_HTTP_CACHE)
ngx_http_proxy_headers_t headers_cache;
#endif
ngx_array_t *headers_source;
ngx_array_t *proxy_lengths;
ngx_array_t *proxy_values;
ngx_array_t *redirects;
ngx_array_t *cookie_domains;
ngx_array_t *cookie_paths;
ngx_http_complex_value_t *method;
ngx_str_t location;
ngx_str_t url;
#if (NGX_HTTP_CACHE)
ngx_http_complex_value_t cache_key;
#endif
ngx_http_proxy_vars_t vars;
ngx_flag_t redirect;
ngx_uint_t http_version;
ngx_uint_t headers_hash_max_size;
ngx_uint_t headers_hash_bucket_size;
#if (NGX_HTTP_SSL)
ngx_uint_t ssl;
ngx_uint_t ssl_protocols;
ngx_str_t ssl_ciphers;
ngx_uint_t ssl_verify_depth;
ngx_str_t ssl_trusted_certificate;
ngx_str_t ssl_crl;
ngx_str_t ssl_certificate;
ngx_str_t ssl_certificate_key;
ngx_array_t *ssl_passwords;
#endif
} ngx_http_proxy_loc_conf_t;
typedef struct {
ngx_http_status_t status;
ngx_http_chunked_t chunked;
ngx_http_proxy_vars_t vars;
off_t internal_body_length;
ngx_chain_t *free;
ngx_chain_t *busy;
unsigned head:1;
unsigned internal_chunked:1;
unsigned header_sent:1;
} ngx_http_proxy_ctx_t;
static ngx_int_t ngx_http_proxy_eval(ngx_http_request_t *r,
ngx_http_proxy_ctx_t *ctx, ngx_http_proxy_loc_conf_t *plcf);
#if (NGX_HTTP_CACHE)
static ngx_int_t ngx_http_proxy_create_key(ngx_http_request_t *r);
#endif
static ngx_int_t ngx_http_proxy_create_request(ngx_http_request_t *r);
static ngx_int_t ngx_http_proxy_reinit_request(ngx_http_request_t *r);
static ngx_int_t ngx_http_proxy_body_output_filter(void *data, ngx_chain_t *in);
static ngx_int_t ngx_http_proxy_process_status_line(ngx_http_request_t *r);
static ngx_int_t ngx_http_proxy_process_header(ngx_http_request_t *r);
static ngx_int_t ngx_http_proxy_input_filter_init(void *data);
static ngx_int_t ngx_http_proxy_copy_filter(ngx_event_pipe_t *p,
ngx_buf_t *buf);
static ngx_int_t ngx_http_proxy_chunked_filter(ngx_event_pipe_t *p,
ngx_buf_t *buf);
static ngx_int_t ngx_http_proxy_non_buffered_copy_filter(void *data,
ssize_t bytes);
static ngx_int_t ngx_http_proxy_non_buffered_chunked_filter(void *data,
ssize_t bytes);
static void ngx_http_proxy_abort_request(ngx_http_request_t *r);
static void ngx_http_proxy_finalize_request(ngx_http_request_t *r,
ngx_int_t rc);
static ngx_int_t ngx_http_proxy_host_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_proxy_port_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t
ngx_http_proxy_add_x_forwarded_for_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t
ngx_http_proxy_internal_body_length_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_proxy_internal_chunked_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_proxy_rewrite_redirect(ngx_http_request_t *r,
ngx_table_elt_t *h, size_t prefix);
static ngx_int_t ngx_http_proxy_rewrite_cookie(ngx_http_request_t *r,
ngx_table_elt_t *h);
static ngx_int_t ngx_http_proxy_rewrite_cookie_value(ngx_http_request_t *r,
ngx_table_elt_t *h, u_char *value, ngx_array_t *rewrites);
static ngx_int_t ngx_http_proxy_rewrite(ngx_http_request_t *r,
ngx_table_elt_t *h, size_t prefix, size_t len, ngx_str_t *replacement);
static ngx_int_t ngx_http_proxy_add_variables(ngx_conf_t *cf);
static void *ngx_http_proxy_create_main_conf(ngx_conf_t *cf);
static void *ngx_http_proxy_create_loc_conf(ngx_conf_t *cf);
static char *ngx_http_proxy_merge_loc_conf(ngx_conf_t *cf,
void *parent, void *child);
static ngx_int_t ngx_http_proxy_init_headers(ngx_conf_t *cf,
ngx_http_proxy_loc_conf_t *conf, ngx_http_proxy_headers_t *headers,
ngx_keyval_t *default_headers);
static char *ngx_http_proxy_pass(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static char *ngx_http_proxy_redirect(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static char *ngx_http_proxy_cookie_domain(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static char *ngx_http_proxy_cookie_path(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static char *ngx_http_proxy_store(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
#if (NGX_HTTP_CACHE)
static char *ngx_http_proxy_cache(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static char *ngx_http_proxy_cache_key(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
#endif
#if (NGX_HTTP_SSL)
static char *ngx_http_proxy_ssl_password_file(ngx_conf_t *cf,
ngx_command_t *cmd, void *conf);
#endif
static char *ngx_http_proxy_lowat_check(ngx_conf_t *cf, void *post, void *data);
static ngx_int_t ngx_http_proxy_rewrite_regex(ngx_conf_t *cf,
ngx_http_proxy_rewrite_t *pr, ngx_str_t *regex, ngx_uint_t caseless);
#if (NGX_HTTP_SSL)
static ngx_int_t ngx_http_proxy_set_ssl(ngx_conf_t *cf,
ngx_http_proxy_loc_conf_t *plcf);
#endif
static void ngx_http_proxy_set_vars(ngx_url_t *u, ngx_http_proxy_vars_t *v);
static ngx_conf_post_t ngx_http_proxy_lowat_post =
{ ngx_http_proxy_lowat_check };
static ngx_conf_bitmask_t ngx_http_proxy_next_upstream_masks[] = {
{ ngx_string("error"), NGX_HTTP_UPSTREAM_FT_ERROR },
{ ngx_string("timeout"), NGX_HTTP_UPSTREAM_FT_TIMEOUT },
{ ngx_string("invalid_header"), NGX_HTTP_UPSTREAM_FT_INVALID_HEADER },
{ ngx_string("non_idempotent"), NGX_HTTP_UPSTREAM_FT_NON_IDEMPOTENT },
{ ngx_string("http_500"), NGX_HTTP_UPSTREAM_FT_HTTP_500 },
{ ngx_string("http_502"), NGX_HTTP_UPSTREAM_FT_HTTP_502 },
{ ngx_string("http_503"), NGX_HTTP_UPSTREAM_FT_HTTP_503 },
{ ngx_string("http_504"), NGX_HTTP_UPSTREAM_FT_HTTP_504 },
{ ngx_string("http_403"), NGX_HTTP_UPSTREAM_FT_HTTP_403 },
{ ngx_string("http_404"), NGX_HTTP_UPSTREAM_FT_HTTP_404 },
{ ngx_string("http_429"), NGX_HTTP_UPSTREAM_FT_HTTP_429 },
{ ngx_string("updating"), NGX_HTTP_UPSTREAM_FT_UPDATING },
{ ngx_string("off"), NGX_HTTP_UPSTREAM_FT_OFF },
{ ngx_null_string, 0 }
};
#if (NGX_HTTP_SSL)
static ngx_conf_bitmask_t ngx_http_proxy_ssl_protocols[] = {
{ ngx_string("SSLv2"), NGX_SSL_SSLv2 },
{ ngx_string("SSLv3"), NGX_SSL_SSLv3 },
{ ngx_string("TLSv1"), NGX_SSL_TLSv1 },
{ ngx_string("TLSv1.1"), NGX_SSL_TLSv1_1 },
{ ngx_string("TLSv1.2"), NGX_SSL_TLSv1_2 },
{ ngx_string("TLSv1.3"), NGX_SSL_TLSv1_3 },
{ ngx_null_string, 0 }
};
#endif
static ngx_conf_enum_t ngx_http_proxy_http_version[] = {
{ ngx_string("1.0"), NGX_HTTP_VERSION_10 },
{ ngx_string("1.1"), NGX_HTTP_VERSION_11 },
{ ngx_null_string, 0 }
};
ngx_module_t ngx_http_proxy_module;
static ngx_command_t ngx_http_proxy_commands[] = {
{ ngx_string("proxy_pass"),
NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_TAKE1,
ngx_http_proxy_pass,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
{ ngx_string("proxy_redirect"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12,
ngx_http_proxy_redirect,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
{ ngx_string("proxy_cookie_domain"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12,
ngx_http_proxy_cookie_domain,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
{ ngx_string("proxy_cookie_path"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12,
ngx_http_proxy_cookie_path,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
{ ngx_string("proxy_store"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_http_proxy_store,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
{ ngx_string("proxy_store_access"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE123,
ngx_conf_set_access_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_proxy_loc_conf_t, upstream.store_access),
NULL },
{ ngx_string("proxy_buffering"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_proxy_loc_conf_t, upstream.buffering),
NULL },
{ ngx_string("proxy_request_buffering"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_proxy_loc_conf_t, upstream.request_buffering),
NULL },
{ ngx_string("proxy_ignore_client_abort"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_proxy_loc_conf_t, upstream.ignore_client_abort),
NULL },
{ ngx_string("proxy_bind"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12,
ngx_http_upstream_bind_set_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_proxy_loc_conf_t, upstream.local),
NULL },
{ ngx_string("proxy_connect_timeout"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_msec_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_proxy_loc_conf_t, upstream.connect_timeout),
NULL },
{ ngx_string("proxy_send_timeout"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_msec_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_proxy_loc_conf_t, upstream.send_timeout),
NULL },
{ ngx_string("proxy_send_lowat"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_size_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_proxy_loc_conf_t, upstream.send_lowat),
&ngx_http_proxy_lowat_post },
{ ngx_string("proxy_intercept_errors"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_proxy_loc_conf_t, upstream.intercept_errors),
NULL },
{ ngx_string("proxy_set_header"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
ngx_conf_set_keyval_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_proxy_loc_conf_t, headers_source),
NULL },
{ ngx_string("proxy_headers_hash_max_size"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_num_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_proxy_loc_conf_t, headers_hash_max_size),
NULL },
{ ngx_string("proxy_headers_hash_bucket_size"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_num_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_proxy_loc_conf_t, headers_hash_bucket_size),
NULL },
{ ngx_string("proxy_set_body"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_str_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_proxy_loc_conf_t, body_source),
NULL },
{ ngx_string("proxy_method"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_http_set_complex_value_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_proxy_loc_conf_t, method),
NULL },
{ ngx_string("proxy_pass_request_headers"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_proxy_loc_conf_t, upstream.pass_request_headers),
NULL },
{ ngx_string("proxy_pass_request_body"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_proxy_loc_conf_t, upstream.pass_request_body),
NULL },
{ ngx_string("proxy_buffer_size"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_size_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_proxy_loc_conf_t, upstream.buffer_size),
NULL },
{ ngx_string("proxy_read_timeout"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_msec_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_proxy_loc_conf_t, upstream.read_timeout),
NULL },
{ ngx_string("proxy_buffers"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
ngx_conf_set_bufs_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_proxy_loc_conf_t, upstream.bufs),
NULL },
{ ngx_string("proxy_busy_buffers_size"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_size_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_proxy_loc_conf_t, upstream.busy_buffers_size_conf),
NULL },
{ ngx_string("proxy_force_ranges"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_proxy_loc_conf_t, upstream.force_ranges),
NULL },
{ ngx_string("proxy_limit_rate"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_size_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_proxy_loc_conf_t, upstream.limit_rate),
NULL },
#if (NGX_HTTP_CACHE)
{ ngx_string("proxy_cache"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_http_proxy_cache,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
{ ngx_string("proxy_cache_key"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_http_proxy_cache_key,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
{ ngx_string("proxy_cache_path"),
NGX_HTTP_MAIN_CONF|NGX_CONF_2MORE,
ngx_http_file_cache_set_slot,
NGX_HTTP_MAIN_CONF_OFFSET,
offsetof(ngx_http_proxy_main_conf_t, caches),
&ngx_http_proxy_module },
{ ngx_string("proxy_cache_bypass"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
ngx_http_set_predicate_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_proxy_loc_conf_t, upstream.cache_bypass),
NULL },
{ ngx_string("proxy_no_cache"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
ngx_http_set_predicate_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_proxy_loc_conf_t, upstream.no_cache),
NULL },
{ ngx_string("proxy_cache_valid"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
ngx_http_file_cache_valid_set_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_proxy_loc_conf_t, upstream.cache_valid),
NULL },
{ ngx_string("proxy_cache_min_uses"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_num_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_proxy_loc_conf_t, upstream.cache_min_uses),
NULL },
{ ngx_string("proxy_cache_max_range_offset"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_off_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_proxy_loc_conf_t, upstream.cache_max_range_offset),
NULL },
{ ngx_string("proxy_cache_use_stale"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
ngx_conf_set_bitmask_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_proxy_loc_conf_t, upstream.cache_use_stale),
&ngx_http_proxy_next_upstream_masks },
{ ngx_string("proxy_cache_methods"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
ngx_conf_set_bitmask_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_proxy_loc_conf_t, upstream.cache_methods),
&ngx_http_upstream_cache_method_mask },
{ ngx_string("proxy_cache_lock"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_proxy_loc_conf_t, upstream.cache_lock),
NULL },
{ ngx_string("proxy_cache_lock_timeout"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_msec_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_proxy_loc_conf_t, upstream.cache_lock_timeout),
NULL },
{ ngx_string("proxy_cache_lock_age"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_msec_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_proxy_loc_conf_t, upstream.cache_lock_age),
NULL },
{ ngx_string("proxy_cache_revalidate"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_proxy_loc_conf_t, upstream.cache_revalidate),
NULL },
{ ngx_string("proxy_cache_convert_head"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_proxy_loc_conf_t, upstream.cache_convert_head),
NULL },
{ ngx_string("proxy_cache_background_update"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_proxy_loc_conf_t, upstream.cache_background_update),
NULL },
#endif
{ ngx_string("proxy_temp_path"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1234,
ngx_conf_set_path_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_proxy_loc_conf_t, upstream.temp_path),
NULL },
{ ngx_string("proxy_max_temp_file_size"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_size_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_proxy_loc_conf_t, upstream.max_temp_file_size_conf),
NULL },
{ ngx_string("proxy_temp_file_write_size"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_size_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_proxy_loc_conf_t, upstream.temp_file_write_size_conf),
NULL },
{ ngx_string("proxy_next_upstream"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
ngx_conf_set_bitmask_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_proxy_loc_conf_t, upstream.next_upstream),
&ngx_http_proxy_next_upstream_masks },
{ ngx_string("proxy_next_upstream_tries"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_num_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_proxy_loc_conf_t, upstream.next_upstream_tries),
NULL },
{ ngx_string("proxy_next_upstream_timeout"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_msec_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_proxy_loc_conf_t, upstream.next_upstream_timeout),
NULL },
{ ngx_string("proxy_pass_header"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_str_array_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_proxy_loc_conf_t, upstream.pass_headers),
NULL },
{ ngx_string("proxy_hide_header"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_str_array_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_proxy_loc_conf_t, upstream.hide_headers),
NULL },
{ ngx_string("proxy_ignore_headers"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
ngx_conf_set_bitmask_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_proxy_loc_conf_t, upstream.ignore_headers),
&ngx_http_upstream_ignore_headers_masks },
{ ngx_string("proxy_http_version"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_enum_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_proxy_loc_conf_t, http_version),
&ngx_http_proxy_http_version },
#if (NGX_HTTP_SSL)
{ ngx_string("proxy_ssl_session_reuse"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_proxy_loc_conf_t, upstream.ssl_session_reuse),
NULL },
{ ngx_string("proxy_ssl_protocols"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
ngx_conf_set_bitmask_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_proxy_loc_conf_t, ssl_protocols),
&ngx_http_proxy_ssl_protocols },
{ ngx_string("proxy_ssl_ciphers"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_str_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_proxy_loc_conf_t, ssl_ciphers),
NULL },
{ ngx_string("proxy_ssl_name"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_http_set_complex_value_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_proxy_loc_conf_t, upstream.ssl_name),
NULL },
{ ngx_string("proxy_ssl_server_name"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_proxy_loc_conf_t, upstream.ssl_server_name),
NULL },
{ ngx_string("proxy_ssl_verify"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_proxy_loc_conf_t, upstream.ssl_verify),
NULL },
{ ngx_string("proxy_ssl_verify_depth"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_num_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_proxy_loc_conf_t, ssl_verify_depth),
NULL },
{ ngx_string("proxy_ssl_trusted_certificate"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_str_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_proxy_loc_conf_t, ssl_trusted_certificate),
NULL },
{ ngx_string("proxy_ssl_crl"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_str_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_proxy_loc_conf_t, ssl_crl),
NULL },
{ ngx_string("proxy_ssl_certificate"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_str_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_proxy_loc_conf_t, ssl_certificate),
NULL },
{ ngx_string("proxy_ssl_certificate_key"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_str_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_proxy_loc_conf_t, ssl_certificate_key),
NULL },
{ ngx_string("proxy_ssl_password_file"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_http_proxy_ssl_password_file,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
#endif
ngx_null_command
};
static ngx_http_module_t ngx_http_proxy_module_ctx = {
ngx_http_proxy_add_variables, /* preconfiguration */
NULL, /* postconfiguration */
ngx_http_proxy_create_main_conf, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
ngx_http_proxy_create_loc_conf, /* create location configuration */
ngx_http_proxy_merge_loc_conf /* merge location configuration */
};
ngx_module_t ngx_http_proxy_module = {
NGX_MODULE_V1,
&ngx_http_proxy_module_ctx, /* module context */
ngx_http_proxy_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static char ngx_http_proxy_version[] = " HTTP/1.0" CRLF;
static char ngx_http_proxy_version_11[] = " HTTP/1.1" CRLF;
static ngx_keyval_t ngx_http_proxy_headers[] = {
{ ngx_string("Host"), ngx_string("$proxy_host") },
{ ngx_string("Connection"), ngx_string("close") },
{ ngx_string("Content-Length"), ngx_string("$proxy_internal_body_length") },
{ ngx_string("Transfer-Encoding"), ngx_string("$proxy_internal_chunked") },
{ ngx_string("TE"), ngx_string("") },
{ ngx_string("Keep-Alive"), ngx_string("") },
{ ngx_string("Expect"), ngx_string("") },
{ ngx_string("Upgrade"), ngx_string("") },
{ ngx_null_string, ngx_null_string }
};
static ngx_str_t ngx_http_proxy_hide_headers[] = {
ngx_string("Date"),
ngx_string("Server"),
ngx_string("X-Pad"),
ngx_string("X-Accel-Expires"),
ngx_string("X-Accel-Redirect"),
ngx_string("X-Accel-Limit-Rate"),
ngx_string("X-Accel-Buffering"),
ngx_string("X-Accel-Charset"),
ngx_null_string
};
#if (NGX_HTTP_CACHE)
static ngx_keyval_t ngx_http_proxy_cache_headers[] = {
{ ngx_string("Host"), ngx_string("$proxy_host") },
{ ngx_string("Connection"), ngx_string("close") },
{ ngx_string("Content-Length"), ngx_string("$proxy_internal_body_length") },
{ ngx_string("Transfer-Encoding"), ngx_string("$proxy_internal_chunked") },
{ ngx_string("TE"), ngx_string("") },
{ ngx_string("Keep-Alive"), ngx_string("") },
{ ngx_string("Expect"), ngx_string("") },
{ ngx_string("Upgrade"), ngx_string("") },
{ ngx_string("If-Modified-Since"),
ngx_string("$upstream_cache_last_modified") },
{ ngx_string("If-Unmodified-Since"), ngx_string("") },
{ ngx_string("If-None-Match"), ngx_string("$upstream_cache_etag") },
{ ngx_string("If-Match"), ngx_string("") },
{ ngx_string("Range"), ngx_string("") },
{ ngx_string("If-Range"), ngx_string("") },
{ ngx_null_string, ngx_null_string }
};
#endif
static ngx_http_variable_t ngx_http_proxy_vars[] = {
{ ngx_string("proxy_host"), NULL, ngx_http_proxy_host_variable, 0,
NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
{ ngx_string("proxy_port"), NULL, ngx_http_proxy_port_variable, 0,
NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
{ ngx_string("proxy_add_x_forwarded_for"), NULL,
ngx_http_proxy_add_x_forwarded_for_variable, 0, NGX_HTTP_VAR_NOHASH, 0 },
#if 0
{ ngx_string("proxy_add_via"), NULL, NULL, 0, NGX_HTTP_VAR_NOHASH, 0 },
#endif
{ ngx_string("proxy_internal_body_length"), NULL,
ngx_http_proxy_internal_body_length_variable, 0,
NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
{ ngx_string("proxy_internal_chunked"), NULL,
ngx_http_proxy_internal_chunked_variable, 0,
NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
ngx_http_null_variable
};
static ngx_path_init_t ngx_http_proxy_temp_path = {
ngx_string(NGX_HTTP_PROXY_TEMP_PATH), { 1, 2, 0 }
};
static ngx_int_t
ngx_http_proxy_handler(ngx_http_request_t *r)
{
ngx_int_t rc;
ngx_http_upstream_t *u;
ngx_http_proxy_ctx_t *ctx;
ngx_http_proxy_loc_conf_t *plcf;
#if (NGX_HTTP_CACHE)
ngx_http_proxy_main_conf_t *pmcf;
#endif
if (ngx_http_upstream_create(r) != NGX_OK) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_proxy_ctx_t));
if (ctx == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
ngx_http_set_ctx(r, ctx, ngx_http_proxy_module);
plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module);
u = r->upstream;
if (plcf->proxy_lengths == NULL) {
ctx->vars = plcf->vars;
u->schema = plcf->vars.schema;
#if (NGX_HTTP_SSL)
u->ssl = (plcf->upstream.ssl != NULL);
#endif
} else {
if (ngx_http_proxy_eval(r, ctx, plcf) != NGX_OK) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
}
u->output.tag = (ngx_buf_tag_t) &ngx_http_proxy_module;
u->conf = &plcf->upstream;
#if (NGX_HTTP_CACHE)
pmcf = ngx_http_get_module_main_conf(r, ngx_http_proxy_module);
u->caches = &pmcf->caches;
u->create_key = ngx_http_proxy_create_key;
#endif
u->create_request = ngx_http_proxy_create_request;
u->reinit_request = ngx_http_proxy_reinit_request;
u->process_header = ngx_http_proxy_process_status_line;
u->abort_request = ngx_http_proxy_abort_request;
u->finalize_request = ngx_http_proxy_finalize_request;
r->state = 0;
if (plcf->redirects) {
u->rewrite_redirect = ngx_http_proxy_rewrite_redirect;
}
if (plcf->cookie_domains || plcf->cookie_paths) {
u->rewrite_cookie = ngx_http_proxy_rewrite_cookie;
}
u->buffering = plcf->upstream.buffering;
u->pipe = ngx_pcalloc(r->pool, sizeof(ngx_event_pipe_t));
if (u->pipe == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
u->pipe->input_filter = ngx_http_proxy_copy_filter;
u->pipe->input_ctx = r;
u->input_filter_init = ngx_http_proxy_input_filter_init;
u->input_filter = ngx_http_proxy_non_buffered_copy_filter;
u->input_filter_ctx = r;
u->accel = 1;
if (!plcf->upstream.request_buffering
&& plcf->body_values == NULL && plcf->upstream.pass_request_body
&& (!r->headers_in.chunked
|| plcf->http_version == NGX_HTTP_VERSION_11))
{
r->request_body_no_buffering = 1;
}
rc = ngx_http_read_client_request_body(r, ngx_http_upstream_init);
if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
return rc;
}
return NGX_DONE;
}
static ngx_int_t
ngx_http_proxy_eval(ngx_http_request_t *r, ngx_http_proxy_ctx_t *ctx,
ngx_http_proxy_loc_conf_t *plcf)
{
u_char *p;
size_t add;
u_short port;
ngx_str_t proxy;
ngx_url_t url;
ngx_http_upstream_t *u;
if (ngx_http_script_run(r, &proxy, plcf->proxy_lengths->elts, 0,
plcf->proxy_values->elts)
== NULL)
{
return NGX_ERROR;
}
if (proxy.len > 7
&& ngx_strncasecmp(proxy.data, (u_char *) "http://", 7) == 0)
{
add = 7;
port = 80;
#if (NGX_HTTP_SSL)
} else if (proxy.len > 8
&& ngx_strncasecmp(proxy.data, (u_char *) "https://", 8) == 0)
{
add = 8;
port = 443;
r->upstream->ssl = 1;
#endif
} else {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"invalid URL prefix in \"%V\"", &proxy);
return NGX_ERROR;
}
u = r->upstream;
u->schema.len = add;
u->schema.data = proxy.data;
ngx_memzero(&url, sizeof(ngx_url_t));
url.url.len = proxy.len - add;
url.url.data = proxy.data + add;
url.default_port = port;
url.uri_part = 1;
url.no_resolve = 1;
if (ngx_parse_url(r->pool, &url) != NGX_OK) {
if (url.err) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"%s in upstream \"%V\"", url.err, &url.url);
}
return NGX_ERROR;
}
if (url.uri.len) {
if (url.uri.data[0] == '?') {
p = ngx_pnalloc(r->pool, url.uri.len + 1);
if (p == NULL) {
return NGX_ERROR;
}
*p++ = '/';
ngx_memcpy(p, url.uri.data, url.uri.len);
url.uri.len++;
url.uri.data = p - 1;
}
}
ctx->vars.key_start = u->schema;
ngx_http_proxy_set_vars(&url, &ctx->vars);
u->resolved = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_resolved_t));
if (u->resolved == NULL) {
return NGX_ERROR;
}
if (url.addrs) {
u->resolved->sockaddr = url.addrs[0].sockaddr;
u->resolved->socklen = url.addrs[0].socklen;
u->resolved->name = url.addrs[0].name;
u->resolved->naddrs = 1;
}
u->resolved->host = url.host;
u->resolved->port = (in_port_t) (url.no_port ? port : url.port);
u->resolved->no_port = url.no_port;
return NGX_OK;
}
#if (NGX_HTTP_CACHE)
static ngx_int_t
ngx_http_proxy_create_key(ngx_http_request_t *r)
{
size_t len, loc_len;
u_char *p;
uintptr_t escape;
ngx_str_t *key;
ngx_http_upstream_t *u;
ngx_http_proxy_ctx_t *ctx;
ngx_http_proxy_loc_conf_t *plcf;
u = r->upstream;
plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module);
ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);
key = ngx_array_push(&r->cache->keys);
if (key == NULL) {
return NGX_ERROR;
}
if (plcf->cache_key.value.data) {
if (ngx_http_complex_value(r, &plcf->cache_key, key) != NGX_OK) {
return NGX_ERROR;
}
return NGX_OK;
}
*key = ctx->vars.key_start;
key = ngx_array_push(&r->cache->keys);
if (key == NULL) {
return NGX_ERROR;
}
if (plcf->proxy_lengths && ctx->vars.uri.len) {
*key = ctx->vars.uri;
u->uri = ctx->vars.uri;
return NGX_OK;
} else if (ctx->vars.uri.len == 0 && r->valid_unparsed_uri) {
*key = r->unparsed_uri;
u->uri = r->unparsed_uri;
return NGX_OK;
}
loc_len = (r->valid_location && ctx->vars.uri.len) ? plcf->location.len : 0;
if (r->quoted_uri || r->space_in_uri || r->internal) {
escape = 2 * ngx_escape_uri(NULL, r->uri.data + loc_len,
r->uri.len - loc_len, NGX_ESCAPE_URI);
} else {
escape = 0;
}
len = ctx->vars.uri.len + r->uri.len - loc_len + escape
+ sizeof("?") - 1 + r->args.len;
p = ngx_pnalloc(r->pool, len);
if (p == NULL) {
return NGX_ERROR;
}
key->data = p;
if (r->valid_location) {
p = ngx_copy(p, ctx->vars.uri.data, ctx->vars.uri.len);
}
if (escape) {
ngx_escape_uri(p, r->uri.data + loc_len,
r->uri.len - loc_len, NGX_ESCAPE_URI);
p += r->uri.len - loc_len + escape;
} else {
p = ngx_copy(p, r->uri.data + loc_len, r->uri.len - loc_len);
}
if (r->args.len > 0) {
*p++ = '?';
p = ngx_copy(p, r->args.data, r->args.len);
}
key->len = p - key->data;
u->uri = *key;
return NGX_OK;
}
#endif
static ngx_int_t
ngx_http_proxy_create_request(ngx_http_request_t *r)
{
size_t len, uri_len, loc_len, body_len,
key_len, val_len;
uintptr_t escape;
ngx_buf_t *b;
ngx_str_t method;
ngx_uint_t i, unparsed_uri;
ngx_chain_t *cl, *body;
ngx_list_part_t *part;
ngx_table_elt_t *header;
ngx_http_upstream_t *u;
ngx_http_proxy_ctx_t *ctx;
ngx_http_script_code_pt code;
ngx_http_proxy_headers_t *headers;
ngx_http_script_engine_t e, le;
ngx_http_proxy_loc_conf_t *plcf;
ngx_http_script_len_code_pt lcode;
u = r->upstream;
plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module);
#if (NGX_HTTP_CACHE)
headers = u->cacheable ? &plcf->headers_cache : &plcf->headers;
#else
headers = &plcf->headers;
#endif
if (u->method.len) {
/* HEAD was changed to GET to cache response */
method = u->method;
} else if (plcf->method) {
if (ngx_http_complex_value(r, plcf->method, &method) != NGX_OK) {
return NGX_ERROR;
}
} else {
method = r->method_name;
}
ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);
if (method.len == 4
&& ngx_strncasecmp(method.data, (u_char *) "HEAD", 4) == 0)
{
ctx->head = 1;
}
len = method.len + 1 + sizeof(ngx_http_proxy_version) - 1
+ sizeof(CRLF) - 1;
escape = 0;
loc_len = 0;
unparsed_uri = 0;
if (plcf->proxy_lengths && ctx->vars.uri.len) {
uri_len = ctx->vars.uri.len;
} else if (ctx->vars.uri.len == 0 && r->valid_unparsed_uri) {
unparsed_uri = 1;
uri_len = r->unparsed_uri.len;
} else {
loc_len = (r->valid_location && ctx->vars.uri.len) ?
plcf->location.len : 0;
if (r->quoted_uri || r->space_in_uri || r->internal) {
escape = 2 * ngx_escape_uri(NULL, r->uri.data + loc_len,
r->uri.len - loc_len, NGX_ESCAPE_URI);
}
uri_len = ctx->vars.uri.len + r->uri.len - loc_len + escape
+ sizeof("?") - 1 + r->args.len;
}
if (uri_len == 0) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"zero length URI to proxy");
return NGX_ERROR;
}
len += uri_len;
ngx_memzero(&le, sizeof(ngx_http_script_engine_t));
ngx_http_script_flush_no_cacheable_variables(r, plcf->body_flushes);
ngx_http_script_flush_no_cacheable_variables(r, headers->flushes);
if (plcf->body_lengths) {
le.ip = plcf->body_lengths->elts;
le.request = r;
le.flushed = 1;
body_len = 0;
while (*(uintptr_t *) le.ip) {
lcode = *(ngx_http_script_len_code_pt *) le.ip;
body_len += lcode(&le);
}
ctx->internal_body_length = body_len;
len += body_len;
} else if (r->headers_in.chunked && r->reading_body) {
ctx->internal_body_length = -1;
ctx->internal_chunked = 1;
} else {
ctx->internal_body_length = r->headers_in.content_length_n;
}
le.ip = headers->lengths->elts;
le.request = r;
le.flushed = 1;
while (*(uintptr_t *) le.ip) {
lcode = *(ngx_http_script_len_code_pt *) le.ip;
key_len = lcode(&le);
for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) {
lcode = *(ngx_http_script_len_code_pt *) le.ip;
}
le.ip += sizeof(uintptr_t);
if (val_len == 0) {
continue;
}
len += key_len + sizeof(": ") - 1 + val_len + sizeof(CRLF) - 1;
}
if (plcf->upstream.pass_request_headers) {
part = &r->headers_in.headers.part;
header = part->elts;
for (i = 0; /* void */; i++) {
if (i >= part->nelts) {
if (part->next == NULL) {
break;
}
part = part->next;
header = part->elts;
i = 0;
}
if (ngx_hash_find(&headers->hash, header[i].hash,
header[i].lowcase_key, header[i].key.len))
{
continue;
}
len += header[i].key.len + sizeof(": ") - 1
+ header[i].value.len + sizeof(CRLF) - 1;
}
}
b = ngx_create_temp_buf(r->pool, len);
if (b == NULL) {
return NGX_ERROR;
}
cl = ngx_alloc_chain_link(r->pool);
if (cl == NULL) {
return NGX_ERROR;
}
cl->buf = b;
/* the request line */
b->last = ngx_copy(b->last, method.data, method.len);
*b->last++ = ' ';
u->uri.data = b->last;
if (plcf->proxy_lengths && ctx->vars.uri.len) {
b->last = ngx_copy(b->last, ctx->vars.uri.data, ctx->vars.uri.len);
} else if (unparsed_uri) {
b->last = ngx_copy(b->last, r->unparsed_uri.data, r->unparsed_uri.len);
} else {
if (r->valid_location) {
b->last = ngx_copy(b->last, ctx->vars.uri.data, ctx->vars.uri.len);
}
if (escape) {
ngx_escape_uri(b->last, r->uri.data + loc_len,
r->uri.len - loc_len, NGX_ESCAPE_URI);
b->last += r->uri.len - loc_len + escape;
} else {
b->last = ngx_copy(b->last, r->uri.data + loc_len,
r->uri.len - loc_len);
}
if (r->args.len > 0) {
*b->last++ = '?';
b->last = ngx_copy(b->last, r->args.data, r->args.len);
}
}
u->uri.len = b->last - u->uri.data;
if (plcf->http_version == NGX_HTTP_VERSION_11) {
b->last = ngx_cpymem(b->last, ngx_http_proxy_version_11,
sizeof(ngx_http_proxy_version_11) - 1);
} else {
b->last = ngx_cpymem(b->last, ngx_http_proxy_version,
sizeof(ngx_http_proxy_version) - 1);
}
ngx_memzero(&e, sizeof(ngx_http_script_engine_t));
e.ip = headers->values->elts;
e.pos = b->last;
e.request = r;
e.flushed = 1;
le.ip = headers->lengths->elts;
while (*(uintptr_t *) le.ip) {
lcode = *(ngx_http_script_len_code_pt *) le.ip;
(void) lcode(&le);
for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) {
lcode = *(ngx_http_script_len_code_pt *) le.ip;
}
le.ip += sizeof(uintptr_t);
if (val_len == 0) {
e.skip = 1;
while (*(uintptr_t *) e.ip) {
code = *(ngx_http_script_code_pt *) e.ip;
code((ngx_http_script_engine_t *) &e);
}
e.ip += sizeof(uintptr_t);
e.skip = 0;
continue;
}
code = *(ngx_http_script_code_pt *) e.ip;
code((ngx_http_script_engine_t *) &e);
*e.pos++ = ':'; *e.pos++ = ' ';
while (*(uintptr_t *) e.ip) {
code = *(ngx_http_script_code_pt *) e.ip;
code((ngx_http_script_engine_t *) &e);
}
e.ip += sizeof(uintptr_t);
*e.pos++ = CR; *e.pos++ = LF;
}
b->last = e.pos;
if (plcf->upstream.pass_request_headers) {
part = &r->headers_in.headers.part;
header = part->elts;
for (i = 0; /* void */; i++) {
if (i >= part->nelts) {
if (part->next == NULL) {
break;
}
part = part->next;
header = part->elts;
i = 0;
}
if (ngx_hash_find(&headers->hash, header[i].hash,
header[i].lowcase_key, header[i].key.len))
{
continue;
}
b->last = ngx_copy(b->last, header[i].key.data, header[i].key.len);
*b->last++ = ':'; *b->last++ = ' ';
b->last = ngx_copy(b->last, header[i].value.data,
header[i].value.len);
*b->last++ = CR; *b->last++ = LF;
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http proxy header: \"%V: %V\"",
&header[i].key, &header[i].value);
}
}
/* add "\r\n" at the header end */
*b->last++ = CR; *b->last++ = LF;
if (plcf->body_values) {
e.ip = plcf->body_values->elts;
e.pos = b->last;
e.skip = 0;
while (*(uintptr_t *) e.ip) {
code = *(ngx_http_script_code_pt *) e.ip;
code((ngx_http_script_engine_t *) &e);
}
b->last = e.pos;
}
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http proxy header:%N\"%*s\"",
(size_t) (b->last - b->pos), b->pos);
if (r->request_body_no_buffering) {
u->request_bufs = cl;
if (ctx->internal_chunked) {
u->output.output_filter = ngx_http_proxy_body_output_filter;
u->output.filter_ctx = r;
}
} else if (plcf->body_values == NULL && plcf->upstream.pass_request_body) {
body = u->request_bufs;
u->request_bufs = cl;
while (body) {
b = ngx_alloc_buf(r->pool);
if (b == NULL) {
return NGX_ERROR;
}
ngx_memcpy(b, body->buf, sizeof(ngx_buf_t));
cl->next = ngx_alloc_chain_link(r->pool);
if (cl->next == NULL) {
return NGX_ERROR;
}
cl = cl->next;
cl->buf = b;
body = body->next;
}
} else {
u->request_bufs = cl;
}
b->flush = 1;
cl->next = NULL;
return NGX_OK;
}
static ngx_int_t
ngx_http_proxy_reinit_request(ngx_http_request_t *r)
{
ngx_http_proxy_ctx_t *ctx;
ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);
if (ctx == NULL) {
return NGX_OK;
}
ctx->status.code = 0;
ctx->status.count = 0;
ctx->status.start = NULL;
ctx->status.end = NULL;
ctx->chunked.state = 0;
r->upstream->process_header = ngx_http_proxy_process_status_line;
r->upstream->pipe->input_filter = ngx_http_proxy_copy_filter;
r->upstream->input_filter = ngx_http_proxy_non_buffered_copy_filter;
r->state = 0;
return NGX_OK;
}
static ngx_int_t
ngx_http_proxy_body_output_filter(void *data, ngx_chain_t *in)
{
ngx_http_request_t *r = data;
off_t size;
u_char *chunk;
ngx_int_t rc;
ngx_buf_t *b;
ngx_chain_t *out, *cl, *tl, **ll, **fl;
ngx_http_proxy_ctx_t *ctx;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"proxy output filter");
ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);
if (in == NULL) {
out = in;
goto out;
}
out = NULL;
ll = &out;
if (!ctx->header_sent) {
/* first buffer contains headers, pass it unmodified */
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"proxy output header");
ctx->header_sent = 1;
tl = ngx_alloc_chain_link(r->pool);
if (tl == NULL) {
return NGX_ERROR;
}
tl->buf = in->buf;
*ll = tl;
ll = &tl->next;
in = in->next;
if (in == NULL) {
tl->next = NULL;
goto out;
}
}
size = 0;
cl = in;
fl = ll;
for ( ;; ) {
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"proxy output chunk: %O", ngx_buf_size(cl->buf));
size += ngx_buf_size(cl->buf);
if (cl->buf->flush
|| cl->buf->sync
|| ngx_buf_in_memory(cl->buf)
|| cl->buf->in_file)
{
tl = ngx_alloc_chain_link(r->pool);
if (tl == NULL) {
return NGX_ERROR;
}
tl->buf = cl->buf;
*ll = tl;
ll = &tl->next;
}
if (cl->next == NULL) {
break;
}
cl = cl->next;
}
if (size) {
tl = ngx_chain_get_free_buf(r->pool, &ctx->free);
if (tl == NULL) {
return NGX_ERROR;
}
b = tl->buf;
chunk = b->start;
if (chunk == NULL) {
/* the "0000000000000000" is 64-bit hexadecimal string */
chunk = ngx_palloc(r->pool, sizeof("0000000000000000" CRLF) - 1);
if (chunk == NULL) {
return NGX_ERROR;
}
b->start = chunk;
b->end = chunk + sizeof("0000000000000000" CRLF) - 1;
}
b->tag = (ngx_buf_tag_t) &ngx_http_proxy_body_output_filter;
b->memory = 0;
b->temporary = 1;
b->pos = chunk;
b->last = ngx_sprintf(chunk, "%xO" CRLF, size);
tl->next = *fl;
*fl = tl;
}
if (cl->buf->last_buf) {
tl = ngx_chain_get_free_buf(r->pool, &ctx->free);
if (tl == NULL) {
return NGX_ERROR;
}
b = tl->buf;
b->tag = (ngx_buf_tag_t) &ngx_http_proxy_body_output_filter;
b->temporary = 0;
b->memory = 1;
b->last_buf = 1;
b->pos = (u_char *) CRLF "0" CRLF CRLF;
b->last = b->pos + 7;
cl->buf->last_buf = 0;
*ll = tl;
if (size == 0) {
b->pos += 2;
}
} else if (size > 0) {
tl = ngx_chain_get_free_buf(r->pool, &ctx->free);
if (tl == NULL) {
return NGX_ERROR;
}
b = tl->buf;
b->tag = (ngx_buf_tag_t) &ngx_http_proxy_body_output_filter;
b->temporary = 0;
b->memory = 1;
b->pos = (u_char *) CRLF;
b->last = b->pos + 2;
*ll = tl;
} else {
*ll = NULL;
}
out:
rc = ngx_chain_writer(&r->upstream->writer, out);
ngx_chain_update_chains(r->pool, &ctx->free, &ctx->busy, &out,
(ngx_buf_tag_t) &ngx_http_proxy_body_output_filter);
return rc;
}
static ngx_int_t
ngx_http_proxy_process_status_line(ngx_http_request_t *r)
{
size_t len;
ngx_int_t rc;
ngx_http_upstream_t *u;
ngx_http_proxy_ctx_t *ctx;
ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);
if (ctx == NULL) {
return NGX_ERROR;
}
u = r->upstream;
rc = ngx_http_parse_status_line(r, &u->buffer, &ctx->status);
if (rc == NGX_AGAIN) {
return rc;
}
if (rc == NGX_ERROR) {
#if (NGX_HTTP_CACHE)
if (r->cache) {
r->http_version = NGX_HTTP_VERSION_9;
return NGX_OK;
}
#endif
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent no valid HTTP/1.0 header");
#if 0
if (u->accel) {
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
}
#endif
r->http_version = NGX_HTTP_VERSION_9;
u->state->status = NGX_HTTP_OK;
u->headers_in.connection_close = 1;
return NGX_OK;
}
if (u->state && u->state->status == 0) {
u->state->status = ctx->status.code;
}
u->headers_in.status_n = ctx->status.code;
len = ctx->status.end - ctx->status.start;
u->headers_in.status_line.len = len;
u->headers_in.status_line.data = ngx_pnalloc(r->pool, len);
if (u->headers_in.status_line.data == NULL) {
return NGX_ERROR;
}
ngx_memcpy(u->headers_in.status_line.data, ctx->status.start, len);
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http proxy status %ui \"%V\"",
u->headers_in.status_n, &u->headers_in.status_line);
if (ctx->status.http_version < NGX_HTTP_VERSION_11) {
u->headers_in.connection_close = 1;
}
u->process_header = ngx_http_proxy_process_header;
return ngx_http_proxy_process_header(r);
}
static ngx_int_t
ngx_http_proxy_process_header(ngx_http_request_t *r)
{
ngx_int_t rc;
ngx_table_elt_t *h;
ngx_http_upstream_t *u;
ngx_http_proxy_ctx_t *ctx;
ngx_http_upstream_header_t *hh;
ngx_http_upstream_main_conf_t *umcf;
umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module);
for ( ;; ) {
rc = ngx_http_parse_header_line(r, &r->upstream->buffer, 1);
if (rc == NGX_OK) {
/* a header line has been parsed successfully */
h = ngx_list_push(&r->upstream->headers_in.headers);
if (h == NULL) {
return NGX_ERROR;
}
h->hash = r->header_hash;
h->key.len = r->header_name_end - r->header_name_start;
h->value.len = r->header_end - r->header_start;
h->key.data = ngx_pnalloc(r->pool,
h->key.len + 1 + h->value.len + 1 + h->key.len);
if (h->key.data == NULL) {
h->hash = 0;
return NGX_ERROR;
}
h->value.data = h->key.data + h->key.len + 1;
h->lowcase_key = h->key.data + h->key.len + 1 + h->value.len + 1;
ngx_memcpy(h->key.data, r->header_name_start, h->key.len);
h->key.data[h->key.len] = '\0';
ngx_memcpy(h->value.data, r->header_start, h->value.len);
h->value.data[h->value.len] = '\0';
if (h->key.len == r->lowcase_index) {
ngx_memcpy(h->lowcase_key, r->lowcase_header, h->key.len);
} else {
ngx_strlow(h->lowcase_key, h->key.data, h->key.len);
}
hh = ngx_hash_find(&umcf->headers_in_hash, h->hash,
h->lowcase_key, h->key.len);
if (hh && hh->handler(r, h, hh->offset) != NGX_OK) {
return NGX_ERROR;
}
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http proxy header: \"%V: %V\"",
&h->key, &h->value);
continue;
}
if (rc == NGX_HTTP_PARSE_HEADER_DONE) {
/* a whole header has been parsed successfully */
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http proxy header done");
/*
* if no "Server" and "Date" in header line,
* then add the special empty headers
*/
if (r->upstream->headers_in.server == NULL) {
h = ngx_list_push(&r->upstream->headers_in.headers);
if (h == NULL) {
return NGX_ERROR;
}
h->hash = ngx_hash(ngx_hash(ngx_hash(ngx_hash(
ngx_hash('s', 'e'), 'r'), 'v'), 'e'), 'r');
ngx_str_set(&h->key, "Server");
ngx_str_null(&h->value);
h->lowcase_key = (u_char *) "server";
}
if (r->upstream->headers_in.date == NULL) {
h = ngx_list_push(&r->upstream->headers_in.headers);
if (h == NULL) {
return NGX_ERROR;
}
h->hash = ngx_hash(ngx_hash(ngx_hash('d', 'a'), 't'), 'e');
ngx_str_set(&h->key, "Date");
ngx_str_null(&h->value);
h->lowcase_key = (u_char *) "date";
}
/* clear content length if response is chunked */
u = r->upstream;
if (u->headers_in.chunked) {
u->headers_in.content_length_n = -1;
}
/*
* set u->keepalive if response has no body; this allows to keep
* connections alive in case of r->header_only or X-Accel-Redirect
*/
ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);
if (u->headers_in.status_n == NGX_HTTP_NO_CONTENT
|| u->headers_in.status_n == NGX_HTTP_NOT_MODIFIED
|| ctx->head
|| (!u->headers_in.chunked
&& u->headers_in.content_length_n == 0))
{
u->keepalive = !u->headers_in.connection_close;
}
if (u->headers_in.status_n == NGX_HTTP_SWITCHING_PROTOCOLS) {
u->keepalive = 0;
if (r->headers_in.upgrade) {
u->upgrade = 1;
}
}
return NGX_OK;
}
if (rc == NGX_AGAIN) {
return NGX_AGAIN;
}
/* there was error while a header line parsing */
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent invalid header");
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
}
}
static ngx_int_t
ngx_http_proxy_input_filter_init(void *data)
{
ngx_http_request_t *r = data;
ngx_http_upstream_t *u;
ngx_http_proxy_ctx_t *ctx;
u = r->upstream;
ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);
if (ctx == NULL) {
return NGX_ERROR;
}
ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http proxy filter init s:%ui h:%d c:%d l:%O",
u->headers_in.status_n, ctx->head, u->headers_in.chunked,
u->headers_in.content_length_n);
/* as per RFC2616, 4.4 Message Length */
if (u->headers_in.status_n == NGX_HTTP_NO_CONTENT
|| u->headers_in.status_n == NGX_HTTP_NOT_MODIFIED
|| ctx->head)
{
/* 1xx, 204, and 304 and replies to HEAD requests */
/* no 1xx since we don't send Expect and Upgrade */
u->pipe->length = 0;
u->length = 0;
u->keepalive = !u->headers_in.connection_close;
} else if (u->headers_in.chunked) {
/* chunked */
u->pipe->input_filter = ngx_http_proxy_chunked_filter;
u->pipe->length = 3; /* "0" LF LF */
u->input_filter = ngx_http_proxy_non_buffered_chunked_filter;
u->length = 1;
} else if (u->headers_in.content_length_n == 0) {
/* empty body: special case as filter won't be called */
u->pipe->length = 0;
u->length = 0;
u->keepalive = !u->headers_in.connection_close;
} else {
/* content length or connection close */
u->pipe->length = u->headers_in.content_length_n;
u->length = u->headers_in.content_length_n;
}
return NGX_OK;
}
static ngx_int_t
ngx_http_proxy_copy_filter(ngx_event_pipe_t *p, ngx_buf_t *buf)
{
ngx_buf_t *b;
ngx_chain_t *cl;
ngx_http_request_t *r;
if (buf->pos == buf->last) {
return NGX_OK;
}
cl = ngx_chain_get_free_buf(p->pool, &p->free);
if (cl == NULL) {
return NGX_ERROR;
}
b = cl->buf;
ngx_memcpy(b, buf, sizeof(ngx_buf_t));
b->shadow = buf;
b->tag = p->tag;
b->last_shadow = 1;
b->recycled = 1;
buf->shadow = b;
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0, "input buf #%d", b->num);
if (p->in) {
*p->last_in = cl;
} else {
p->in = cl;
}
p->last_in = &cl->next;
if (p->length == -1) {
return NGX_OK;
}
p->length -= b->last - b->pos;
if (p->length == 0) {
r = p->input_ctx;
p->upstream_done = 1;
r->upstream->keepalive = !r->upstream->headers_in.connection_close;
} else if (p->length < 0) {
r = p->input_ctx;
p->upstream_done = 1;
ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,
"upstream sent more data than specified in "
"\"Content-Length\" header");
}
return NGX_OK;
}
static ngx_int_t
ngx_http_proxy_chunked_filter(ngx_event_pipe_t *p, ngx_buf_t *buf)
{
ngx_int_t rc;
ngx_buf_t *b, **prev;
ngx_chain_t *cl;
ngx_http_request_t *r;
ngx_http_proxy_ctx_t *ctx;
if (buf->pos == buf->last) {
return NGX_OK;
}
r = p->input_ctx;
ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);
if (ctx == NULL) {
return NGX_ERROR;
}
b = NULL;
prev = &buf->shadow;
for ( ;; ) {
rc = ngx_http_parse_chunked(r, buf, &ctx->chunked);
if (rc == NGX_OK) {
/* a chunk has been parsed successfully */
cl = ngx_chain_get_free_buf(p->pool, &p->free);
if (cl == NULL) {
return NGX_ERROR;
}
b = cl->buf;
ngx_memzero(b, sizeof(ngx_buf_t));
b->pos = buf->pos;
b->start = buf->start;
b->end = buf->end;
b->tag = p->tag;
b->temporary = 1;
b->recycled = 1;
*prev = b;
prev = &b->shadow;
if (p->in) {
*p->last_in = cl;
} else {
p->in = cl;
}
p->last_in = &cl->next;
/* STUB */ b->num = buf->num;
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, p->log, 0,
"input buf #%d %p", b->num, b->pos);
if (buf->last - buf->pos >= ctx->chunked.size) {
buf->pos += (size_t) ctx->chunked.size;
b->last = buf->pos;
ctx->chunked.size = 0;
continue;
}
ctx->chunked.size -= buf->last - buf->pos;
buf->pos = buf->last;
b->last = buf->last;
continue;
}
if (rc == NGX_DONE) {
/* a whole response has been parsed successfully */
p->upstream_done = 1;
r->upstream->keepalive = !r->upstream->headers_in.connection_close;
break;
}
if (rc == NGX_AGAIN) {
/* set p->length, minimal amount of data we want to see */
p->length = ctx->chunked.length;
break;
}
/* invalid response */
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent invalid chunked response");
return NGX_ERROR;
}
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http proxy chunked state %ui, length %O",
ctx->chunked.state, p->length);
if (b) {
b->shadow = buf;
b->last_shadow = 1;
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, p->log, 0,
"input buf %p %z", b->pos, b->last - b->pos);
return NGX_OK;
}
/* there is no data record in the buf, add it to free chain */
if (ngx_event_pipe_add_free_buf(p, buf) != NGX_OK) {
return NGX_ERROR;
}
return NGX_OK;
}
static ngx_int_t
ngx_http_proxy_non_buffered_copy_filter(void *data, ssize_t bytes)
{
ngx_http_request_t *r = data;
ngx_buf_t *b;
ngx_chain_t *cl, **ll;
ngx_http_upstream_t *u;
u = r->upstream;
for (cl = u->out_bufs, ll = &u->out_bufs; cl; cl = cl->next) {
ll = &cl->next;
}
cl = ngx_chain_get_free_buf(r->pool, &u->free_bufs);
if (cl == NULL) {
return NGX_ERROR;
}
*ll = cl;
cl->buf->flush = 1;
cl->buf->memory = 1;
b = &u->buffer;
cl->buf->pos = b->last;
b->last += bytes;
cl->buf->last = b->last;
cl->buf->tag = u->output.tag;
if (u->length == -1) {
return NGX_OK;
}
u->length -= bytes;
if (u->length == 0) {
u->keepalive = !u->headers_in.connection_close;
}
return NGX_OK;
}
static ngx_int_t
ngx_http_proxy_non_buffered_chunked_filter(void *data, ssize_t bytes)
{
ngx_http_request_t *r = data;
ngx_int_t rc;
ngx_buf_t *b, *buf;
ngx_chain_t *cl, **ll;
ngx_http_upstream_t *u;
ngx_http_proxy_ctx_t *ctx;
ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);
if (ctx == NULL) {
return NGX_ERROR;
}
u = r->upstream;
buf = &u->buffer;
buf->pos = buf->last;
buf->last += bytes;
for (cl = u->out_bufs, ll = &u->out_bufs; cl; cl = cl->next) {
ll = &cl->next;
}
for ( ;; ) {
rc = ngx_http_parse_chunked(r, buf, &ctx->chunked);
if (rc == NGX_OK) {
/* a chunk has been parsed successfully */
cl = ngx_chain_get_free_buf(r->pool, &u->free_bufs);
if (cl == NULL) {
return NGX_ERROR;
}
*ll = cl;
ll = &cl->next;
b = cl->buf;
b->flush = 1;
b->memory = 1;
b->pos = buf->pos;
b->tag = u->output.tag;
if (buf->last - buf->pos >= ctx->chunked.size) {
buf->pos += (size_t) ctx->chunked.size;
b->last = buf->pos;
ctx->chunked.size = 0;
} else {
ctx->chunked.size -= buf->last - buf->pos;
buf->pos = buf->last;
b->last = buf->last;
}
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http proxy out buf %p %z",
b->pos, b->last - b->pos);
continue;
}
if (rc == NGX_DONE) {
/* a whole response has been parsed successfully */
u->keepalive = !u->headers_in.connection_close;
u->length = 0;
break;
}
if (rc == NGX_AGAIN) {
break;
}
/* invalid response */
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent invalid chunked response");
return NGX_ERROR;
}
return NGX_OK;
}
static void
ngx_http_proxy_abort_request(ngx_http_request_t *r)
{
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"abort http proxy request");
return;
}
static void
ngx_http_proxy_finalize_request(ngx_http_request_t *r, ngx_int_t rc)
{
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"finalize http proxy request");
return;
}
static ngx_int_t
ngx_http_proxy_host_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
ngx_http_proxy_ctx_t *ctx;
ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);
if (ctx == NULL) {
v->not_found = 1;
return NGX_OK;
}
v->len = ctx->vars.host_header.len;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = ctx->vars.host_header.data;
return NGX_OK;
}
static ngx_int_t
ngx_http_proxy_port_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
ngx_http_proxy_ctx_t *ctx;
ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);
if (ctx == NULL) {
v->not_found = 1;
return NGX_OK;
}
v->len = ctx->vars.port.len;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = ctx->vars.port.data;
return NGX_OK;
}
static ngx_int_t
ngx_http_proxy_add_x_forwarded_for_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
size_t len;
u_char *p;
ngx_uint_t i, n;
ngx_table_elt_t **h;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
n = r->headers_in.x_forwarded_for.nelts;
h = r->headers_in.x_forwarded_for.elts;
len = 0;
for (i = 0; i < n; i++) {
len += h[i]->value.len + sizeof(", ") - 1;
}
if (len == 0) {
v->len = r->connection->addr_text.len;
v->data = r->connection->addr_text.data;
return NGX_OK;
}
len += r->connection->addr_text.len;
p = ngx_pnalloc(r->pool, len);
if (p == NULL) {
return NGX_ERROR;
}
v->len = len;
v->data = p;
for (i = 0; i < n; i++) {
p = ngx_copy(p, h[i]->value.data, h[i]->value.len);
*p++ = ','; *p++ = ' ';
}
ngx_memcpy(p, r->connection->addr_text.data, r->connection->addr_text.len);
return NGX_OK;
}
static ngx_int_t
ngx_http_proxy_internal_body_length_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
ngx_http_proxy_ctx_t *ctx;
ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);
if (ctx == NULL || ctx->internal_body_length < 0) {
v->not_found = 1;
return NGX_OK;
}
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = ngx_pnalloc(r->pool, NGX_OFF_T_LEN);
if (v->data == NULL) {
return NGX_ERROR;
}
v->len = ngx_sprintf(v->data, "%O", ctx->internal_body_length) - v->data;
return NGX_OK;
}
static ngx_int_t
ngx_http_proxy_internal_chunked_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
ngx_http_proxy_ctx_t *ctx;
ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);
if (ctx == NULL || !ctx->internal_chunked) {
v->not_found = 1;
return NGX_OK;
}
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = (u_char *) "chunked";
v->len = sizeof("chunked") - 1;
return NGX_OK;
}
static ngx_int_t
ngx_http_proxy_rewrite_redirect(ngx_http_request_t *r, ngx_table_elt_t *h,
size_t prefix)
{
size_t len;
ngx_int_t rc;
ngx_uint_t i;
ngx_http_proxy_rewrite_t *pr;
ngx_http_proxy_loc_conf_t *plcf;
plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module);
pr = plcf->redirects->elts;
if (pr == NULL) {
return NGX_DECLINED;
}
len = h->value.len - prefix;
for (i = 0; i < plcf->redirects->nelts; i++) {
rc = pr[i].handler(r, h, prefix, len, &pr[i]);
if (rc != NGX_DECLINED) {
return rc;
}
}
return NGX_DECLINED;
}
static ngx_int_t
ngx_http_proxy_rewrite_cookie(ngx_http_request_t *r, ngx_table_elt_t *h)
{
size_t prefix;
u_char *p;
ngx_int_t rc, rv;
ngx_http_proxy_loc_conf_t *plcf;
p = (u_char *) ngx_strchr(h->value.data, ';');
if (p == NULL) {
return NGX_DECLINED;
}
prefix = p + 1 - h->value.data;
rv = NGX_DECLINED;
plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module);
if (plcf->cookie_domains) {
p = ngx_strcasestrn(h->value.data + prefix, "domain=", 7 - 1);
if (p) {
rc = ngx_http_proxy_rewrite_cookie_value(r, h, p + 7,
plcf->cookie_domains);
if (rc == NGX_ERROR) {
return NGX_ERROR;
}
if (rc != NGX_DECLINED) {
rv = rc;
}
}
}
if (plcf->cookie_paths) {
p = ngx_strcasestrn(h->value.data + prefix, "path=", 5 - 1);
if (p) {
rc = ngx_http_proxy_rewrite_cookie_value(r, h, p + 5,
plcf->cookie_paths);
if (rc == NGX_ERROR) {
return NGX_ERROR;
}
if (rc != NGX_DECLINED) {
rv = rc;
}
}
}
return rv;
}
static ngx_int_t
ngx_http_proxy_rewrite_cookie_value(ngx_http_request_t *r, ngx_table_elt_t *h,
u_char *value, ngx_array_t *rewrites)
{
size_t len, prefix;
u_char *p;
ngx_int_t rc;
ngx_uint_t i;
ngx_http_proxy_rewrite_t *pr;
prefix = value - h->value.data;
p = (u_char *) ngx_strchr(value, ';');
len = p ? (size_t) (p - value) : (h->value.len - prefix);
pr = rewrites->elts;
for (i = 0; i < rewrites->nelts; i++) {
rc = pr[i].handler(r, h, prefix, len, &pr[i]);
if (rc != NGX_DECLINED) {
return rc;
}
}
return NGX_DECLINED;
}
static ngx_int_t
ngx_http_proxy_rewrite_complex_handler(ngx_http_request_t *r,
ngx_table_elt_t *h, size_t prefix, size_t len, ngx_http_proxy_rewrite_t *pr)
{
ngx_str_t pattern, replacement;
if (ngx_http_complex_value(r, &pr->pattern.complex, &pattern) != NGX_OK) {
return NGX_ERROR;
}
if (pattern.len > len
|| ngx_rstrncmp(h->value.data + prefix, pattern.data,
pattern.len) != 0)
{
return NGX_DECLINED;
}
if (ngx_http_complex_value(r, &pr->replacement, &replacement) != NGX_OK) {
return NGX_ERROR;
}
return ngx_http_proxy_rewrite(r, h, prefix, pattern.len, &replacement);
}
#if (NGX_PCRE)
static ngx_int_t
ngx_http_proxy_rewrite_regex_handler(ngx_http_request_t *r, ngx_table_elt_t *h,
size_t prefix, size_t len, ngx_http_proxy_rewrite_t *pr)
{
ngx_str_t pattern, replacement;
pattern.len = len;
pattern.data = h->value.data + prefix;
if (ngx_http_regex_exec(r, pr->pattern.regex, &pattern) != NGX_OK) {
return NGX_DECLINED;
}
if (ngx_http_complex_value(r, &pr->replacement, &replacement) != NGX_OK) {
return NGX_ERROR;
}
if (prefix == 0 && h->value.len == len) {
h->value = replacement;
return NGX_OK;
}
return ngx_http_proxy_rewrite(r, h, prefix, len, &replacement);
}
#endif
static ngx_int_t
ngx_http_proxy_rewrite_domain_handler(ngx_http_request_t *r,
ngx_table_elt_t *h, size_t prefix, size_t len, ngx_http_proxy_rewrite_t *pr)
{
u_char *p;
ngx_str_t pattern, replacement;
if (ngx_http_complex_value(r, &pr->pattern.complex, &pattern) != NGX_OK) {
return NGX_ERROR;
}
p = h->value.data + prefix;
if (p[0] == '.') {
p++;
prefix++;
len--;
}
if (pattern.len != len || ngx_rstrncasecmp(pattern.data, p, len) != 0) {
return NGX_DECLINED;
}
if (ngx_http_complex_value(r, &pr->replacement, &replacement) != NGX_OK) {
return NGX_ERROR;
}
return ngx_http_proxy_rewrite(r, h, prefix, len, &replacement);
}
static ngx_int_t
ngx_http_proxy_rewrite(ngx_http_request_t *r, ngx_table_elt_t *h, size_t prefix,
size_t len, ngx_str_t *replacement)
{
u_char *p, *data;
size_t new_len;
new_len = replacement->len + h->value.len - len;
if (replacement->len > len) {
data = ngx_pnalloc(r->pool, new_len + 1);
if (data == NULL) {
return NGX_ERROR;
}
p = ngx_copy(data, h->value.data, prefix);
p = ngx_copy(p, replacement->data, replacement->len);
ngx_memcpy(p, h->value.data + prefix + len,
h->value.len - len - prefix + 1);
h->value.data = data;
} else {
p = ngx_copy(h->value.data + prefix, replacement->data,
replacement->len);
ngx_memmove(p, h->value.data + prefix + len,
h->value.len - len - prefix + 1);
}
h->value.len = new_len;
return NGX_OK;
}
static ngx_int_t
ngx_http_proxy_add_variables(ngx_conf_t *cf)
{
ngx_http_variable_t *var, *v;
for (v = ngx_http_proxy_vars; v->name.len; v++) {
var = ngx_http_add_variable(cf, &v->name, v->flags);
if (var == NULL) {
return NGX_ERROR;
}
var->get_handler = v->get_handler;
var->data = v->data;
}
return NGX_OK;
}
static void *
ngx_http_proxy_create_main_conf(ngx_conf_t *cf)
{
ngx_http_proxy_main_conf_t *conf;
conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_proxy_main_conf_t));
if (conf == NULL) {
return NULL;
}
#if (NGX_HTTP_CACHE)
if (ngx_array_init(&conf->caches, cf->pool, 4,
sizeof(ngx_http_file_cache_t *))
!= NGX_OK)
{
return NULL;
}
#endif
return conf;
}
static void *
ngx_http_proxy_create_loc_conf(ngx_conf_t *cf)
{
ngx_http_proxy_loc_conf_t *conf;
conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_proxy_loc_conf_t));
if (conf == NULL) {
return NULL;
}
/*
* set by ngx_pcalloc():
*
* conf->upstream.bufs.num = 0;
* conf->upstream.ignore_headers = 0;
* conf->upstream.next_upstream = 0;
* conf->upstream.cache_zone = NULL;
* conf->upstream.cache_use_stale = 0;
* conf->upstream.cache_methods = 0;
* conf->upstream.temp_path = NULL;
* conf->upstream.hide_headers_hash = { NULL, 0 };
* conf->upstream.store_lengths = NULL;
* conf->upstream.store_values = NULL;
* conf->upstream.ssl_name = NULL;
*
* conf->method = NULL;
* conf->location = NULL;
* conf->url = { 0, NULL };
* conf->headers_source = NULL;
* conf->headers.lengths = NULL;
* conf->headers.values = NULL;
* conf->headers.hash = { NULL, 0 };
* conf->headers_cache.lengths = NULL;
* conf->headers_cache.values = NULL;
* conf->headers_cache.hash = { NULL, 0 };
* conf->body_lengths = NULL;
* conf->body_values = NULL;
* conf->body_source = { 0, NULL };
* conf->redirects = NULL;
* conf->ssl = 0;
* conf->ssl_protocols = 0;
* conf->ssl_ciphers = { 0, NULL };
* conf->ssl_trusted_certificate = { 0, NULL };
* conf->ssl_crl = { 0, NULL };
* conf->ssl_certificate = { 0, NULL };
* conf->ssl_certificate_key = { 0, NULL };
*/
conf->upstream.store = NGX_CONF_UNSET;
conf->upstream.store_access = NGX_CONF_UNSET_UINT;
conf->upstream.next_upstream_tries = NGX_CONF_UNSET_UINT;
conf->upstream.buffering = NGX_CONF_UNSET;
conf->upstream.request_buffering = NGX_CONF_UNSET;
conf->upstream.ignore_client_abort = NGX_CONF_UNSET;
conf->upstream.force_ranges = NGX_CONF_UNSET;
conf->upstream.local = NGX_CONF_UNSET_PTR;
conf->upstream.connect_timeout = NGX_CONF_UNSET_MSEC;
conf->upstream.send_timeout = NGX_CONF_UNSET_MSEC;
conf->upstream.read_timeout = NGX_CONF_UNSET_MSEC;
conf->upstream.next_upstream_timeout = NGX_CONF_UNSET_MSEC;
conf->upstream.send_lowat = NGX_CONF_UNSET_SIZE;
conf->upstream.buffer_size = NGX_CONF_UNSET_SIZE;
conf->upstream.limit_rate = NGX_CONF_UNSET_SIZE;
conf->upstream.busy_buffers_size_conf = NGX_CONF_UNSET_SIZE;
conf->upstream.max_temp_file_size_conf = NGX_CONF_UNSET_SIZE;
conf->upstream.temp_file_write_size_conf = NGX_CONF_UNSET_SIZE;
conf->upstream.pass_request_headers = NGX_CONF_UNSET;
conf->upstream.pass_request_body = NGX_CONF_UNSET;
#if (NGX_HTTP_CACHE)
conf->upstream.cache = NGX_CONF_UNSET;
conf->upstream.cache_min_uses = NGX_CONF_UNSET_UINT;
conf->upstream.cache_max_range_offset = NGX_CONF_UNSET;
conf->upstream.cache_bypass = NGX_CONF_UNSET_PTR;
conf->upstream.no_cache = NGX_CONF_UNSET_PTR;
conf->upstream.cache_valid = NGX_CONF_UNSET_PTR;
conf->upstream.cache_lock = NGX_CONF_UNSET;
conf->upstream.cache_lock_timeout = NGX_CONF_UNSET_MSEC;
conf->upstream.cache_lock_age = NGX_CONF_UNSET_MSEC;
conf->upstream.cache_revalidate = NGX_CONF_UNSET;
conf->upstream.cache_convert_head = NGX_CONF_UNSET;
conf->upstream.cache_background_update = NGX_CONF_UNSET;
#endif
conf->upstream.hide_headers = NGX_CONF_UNSET_PTR;
conf->upstream.pass_headers = NGX_CONF_UNSET_PTR;
conf->upstream.intercept_errors = NGX_CONF_UNSET;
#if (NGX_HTTP_SSL)
conf->upstream.ssl_session_reuse = NGX_CONF_UNSET;
conf->upstream.ssl_server_name = NGX_CONF_UNSET;
conf->upstream.ssl_verify = NGX_CONF_UNSET;
conf->ssl_verify_depth = NGX_CONF_UNSET_UINT;
conf->ssl_passwords = NGX_CONF_UNSET_PTR;
#endif
/* "proxy_cyclic_temp_file" is disabled */
conf->upstream.cyclic_temp_file = 0;
conf->redirect = NGX_CONF_UNSET;
conf->upstream.change_buffering = 1;
conf->cookie_domains = NGX_CONF_UNSET_PTR;
conf->cookie_paths = NGX_CONF_UNSET_PTR;
conf->http_version = NGX_CONF_UNSET_UINT;
conf->headers_hash_max_size = NGX_CONF_UNSET_UINT;
conf->headers_hash_bucket_size = NGX_CONF_UNSET_UINT;
ngx_str_set(&conf->upstream.module, "proxy");
return conf;
}
static char *
ngx_http_proxy_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
{
ngx_http_proxy_loc_conf_t *prev = parent;
ngx_http_proxy_loc_conf_t *conf = child;
u_char *p;
size_t size;
ngx_int_t rc;
ngx_hash_init_t hash;
ngx_http_core_loc_conf_t *clcf;
ngx_http_proxy_rewrite_t *pr;
ngx_http_script_compile_t sc;
#if (NGX_HTTP_CACHE)
if (conf->upstream.store > 0) {
conf->upstream.cache = 0;
}
if (conf->upstream.cache > 0) {
conf->upstream.store = 0;
}
#endif
if (conf->upstream.store == NGX_CONF_UNSET) {
ngx_conf_merge_value(conf->upstream.store,
prev->upstream.store, 0);
conf->upstream.store_lengths = prev->upstream.store_lengths;
conf->upstream.store_values = prev->upstream.store_values;
}
ngx_conf_merge_uint_value(conf->upstream.store_access,
prev->upstream.store_access, 0600);
ngx_conf_merge_uint_value(conf->upstream.next_upstream_tries,
prev->upstream.next_upstream_tries, 0);
ngx_conf_merge_value(conf->upstream.buffering,
prev->upstream.buffering, 1);
ngx_conf_merge_value(conf->upstream.request_buffering,
prev->upstream.request_buffering, 1);
ngx_conf_merge_value(conf->upstream.ignore_client_abort,
prev->upstream.ignore_client_abort, 0);
ngx_conf_merge_value(conf->upstream.force_ranges,
prev->upstream.force_ranges, 0);
ngx_conf_merge_ptr_value(conf->upstream.local,
prev->upstream.local, NULL);
ngx_conf_merge_msec_value(conf->upstream.connect_timeout,
prev->upstream.connect_timeout, 60000);
ngx_conf_merge_msec_value(conf->upstream.send_timeout,
prev->upstream.send_timeout, 60000);
ngx_conf_merge_msec_value(conf->upstream.read_timeout,
prev->upstream.read_timeout, 60000);
ngx_conf_merge_msec_value(conf->upstream.next_upstream_timeout,
prev->upstream.next_upstream_timeout, 0);
ngx_conf_merge_size_value(conf->upstream.send_lowat,
prev->upstream.send_lowat, 0);
ngx_conf_merge_size_value(conf->upstream.buffer_size,
prev->upstream.buffer_size,
(size_t) ngx_pagesize);
ngx_conf_merge_size_value(conf->upstream.limit_rate,
prev->upstream.limit_rate, 0);
ngx_conf_merge_bufs_value(conf->upstream.bufs, prev->upstream.bufs,
8, ngx_pagesize);
if (conf->upstream.bufs.num < 2) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"there must be at least 2 \"proxy_buffers\"");
return NGX_CONF_ERROR;
}
size = conf->upstream.buffer_size;
if (size < conf->upstream.bufs.size) {
size = conf->upstream.bufs.size;
}
ngx_conf_merge_size_value(conf->upstream.busy_buffers_size_conf,
prev->upstream.busy_buffers_size_conf,
NGX_CONF_UNSET_SIZE);
if (conf->upstream.busy_buffers_size_conf == NGX_CONF_UNSET_SIZE) {
conf->upstream.busy_buffers_size = 2 * size;
} else {
conf->upstream.busy_buffers_size =
conf->upstream.busy_buffers_size_conf;
}
if (conf->upstream.busy_buffers_size < size) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"\"proxy_busy_buffers_size\" must be equal to or greater than "
"the maximum of the value of \"proxy_buffer_size\" and "
"one of the \"proxy_buffers\"");
return NGX_CONF_ERROR;
}
if (conf->upstream.busy_buffers_size
> (conf->upstream.bufs.num - 1) * conf->upstream.bufs.size)
{
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"\"proxy_busy_buffers_size\" must be less than "
"the size of all \"proxy_buffers\" minus one buffer");
return NGX_CONF_ERROR;
}
ngx_conf_merge_size_value(conf->upstream.temp_file_write_size_conf,
prev->upstream.temp_file_write_size_conf,
NGX_CONF_UNSET_SIZE);
if (conf->upstream.temp_file_write_size_conf == NGX_CONF_UNSET_SIZE) {
conf->upstream.temp_file_write_size = 2 * size;
} else {
conf->upstream.temp_file_write_size =
conf->upstream.temp_file_write_size_conf;
}
if (conf->upstream.temp_file_write_size < size) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"\"proxy_temp_file_write_size\" must be equal to or greater "
"than the maximum of the value of \"proxy_buffer_size\" and "
"one of the \"proxy_buffers\"");
return NGX_CONF_ERROR;
}
ngx_conf_merge_size_value(conf->upstream.max_temp_file_size_conf,
prev->upstream.max_temp_file_size_conf,
NGX_CONF_UNSET_SIZE);
if (conf->upstream.max_temp_file_size_conf == NGX_CONF_UNSET_SIZE) {
conf->upstream.max_temp_file_size = 1024 * 1024 * 1024;
} else {
conf->upstream.max_temp_file_size =
conf->upstream.max_temp_file_size_conf;
}
if (conf->upstream.max_temp_file_size != 0
&& conf->upstream.max_temp_file_size < size)
{
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"\"proxy_max_temp_file_size\" must be equal to zero to disable "
"temporary files usage or must be equal to or greater than "
"the maximum of the value of \"proxy_buffer_size\" and "
"one of the \"proxy_buffers\"");
return NGX_CONF_ERROR;
}
ngx_conf_merge_bitmask_value(conf->upstream.ignore_headers,
prev->upstream.ignore_headers,
NGX_CONF_BITMASK_SET);
ngx_conf_merge_bitmask_value(conf->upstream.next_upstream,
prev->upstream.next_upstream,
(NGX_CONF_BITMASK_SET
|NGX_HTTP_UPSTREAM_FT_ERROR
|NGX_HTTP_UPSTREAM_FT_TIMEOUT));
if (conf->upstream.next_upstream & NGX_HTTP_UPSTREAM_FT_OFF) {
conf->upstream.next_upstream = NGX_CONF_BITMASK_SET
|NGX_HTTP_UPSTREAM_FT_OFF;
}
if (ngx_conf_merge_path_value(cf, &conf->upstream.temp_path,
prev->upstream.temp_path,
&ngx_http_proxy_temp_path)
!= NGX_OK)
{
return NGX_CONF_ERROR;
}
#if (NGX_HTTP_CACHE)
if (conf->upstream.cache == NGX_CONF_UNSET) {
ngx_conf_merge_value(conf->upstream.cache,
prev->upstream.cache, 0);
conf->upstream.cache_zone = prev->upstream.cache_zone;
conf->upstream.cache_value = prev->upstream.cache_value;
}
if (conf->upstream.cache_zone && conf->upstream.cache_zone->data == NULL) {
ngx_shm_zone_t *shm_zone;
shm_zone = conf->upstream.cache_zone;
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"\"proxy_cache\" zone \"%V\" is unknown",
&shm_zone->shm.name);
return NGX_CONF_ERROR;
}
ngx_conf_merge_uint_value(conf->upstream.cache_min_uses,
prev->upstream.cache_min_uses, 1);
ngx_conf_merge_off_value(conf->upstream.cache_max_range_offset,
prev->upstream.cache_max_range_offset,
NGX_MAX_OFF_T_VALUE);
ngx_conf_merge_bitmask_value(conf->upstream.cache_use_stale,
prev->upstream.cache_use_stale,
(NGX_CONF_BITMASK_SET
|NGX_HTTP_UPSTREAM_FT_OFF));
if (conf->upstream.cache_use_stale & NGX_HTTP_UPSTREAM_FT_OFF) {
conf->upstream.cache_use_stale = NGX_CONF_BITMASK_SET
|NGX_HTTP_UPSTREAM_FT_OFF;
}
if (conf->upstream.cache_use_stale & NGX_HTTP_UPSTREAM_FT_ERROR) {
conf->upstream.cache_use_stale |= NGX_HTTP_UPSTREAM_FT_NOLIVE;
}
if (conf->upstream.cache_methods == 0) {
conf->upstream.cache_methods = prev->upstream.cache_methods;
}
conf->upstream.cache_methods |= NGX_HTTP_GET|NGX_HTTP_HEAD;
ngx_conf_merge_ptr_value(conf->upstream.cache_bypass,
prev->upstream.cache_bypass, NULL);
ngx_conf_merge_ptr_value(conf->upstream.no_cache,
prev->upstream.no_cache, NULL);
ngx_conf_merge_ptr_value(conf->upstream.cache_valid,
prev->upstream.cache_valid, NULL);
if (conf->cache_key.value.data == NULL) {
conf->cache_key = prev->cache_key;
}
ngx_conf_merge_value(conf->upstream.cache_lock,
prev->upstream.cache_lock, 0);
ngx_conf_merge_msec_value(conf->upstream.cache_lock_timeout,
prev->upstream.cache_lock_timeout, 5000);
ngx_conf_merge_msec_value(conf->upstream.cache_lock_age,
prev->upstream.cache_lock_age, 5000);
ngx_conf_merge_value(conf->upstream.cache_revalidate,
prev->upstream.cache_revalidate, 0);
ngx_conf_merge_value(conf->upstream.cache_convert_head,
prev->upstream.cache_convert_head, 1);
ngx_conf_merge_value(conf->upstream.cache_background_update,
prev->upstream.cache_background_update, 0);
#endif
if (conf->method == NULL) {
conf->method = prev->method;
}
ngx_conf_merge_value(conf->upstream.pass_request_headers,
prev->upstream.pass_request_headers, 1);
ngx_conf_merge_value(conf->upstream.pass_request_body,
prev->upstream.pass_request_body, 1);
ngx_conf_merge_value(conf->upstream.intercept_errors,
prev->upstream.intercept_errors, 0);
#if (NGX_HTTP_SSL)
ngx_conf_merge_value(conf->upstream.ssl_session_reuse,
prev->upstream.ssl_session_reuse, 1);
ngx_conf_merge_bitmask_value(conf->ssl_protocols, prev->ssl_protocols,
(NGX_CONF_BITMASK_SET|NGX_SSL_TLSv1
|NGX_SSL_TLSv1_1|NGX_SSL_TLSv1_2));
ngx_conf_merge_str_value(conf->ssl_ciphers, prev->ssl_ciphers,
"DEFAULT");
if (conf->upstream.ssl_name == NULL) {
conf->upstream.ssl_name = prev->upstream.ssl_name;
}
ngx_conf_merge_value(conf->upstream.ssl_server_name,
prev->upstream.ssl_server_name, 0);
ngx_conf_merge_value(conf->upstream.ssl_verify,
prev->upstream.ssl_verify, 0);
ngx_conf_merge_uint_value(conf->ssl_verify_depth,
prev->ssl_verify_depth, 1);
ngx_conf_merge_str_value(conf->ssl_trusted_certificate,
prev->ssl_trusted_certificate, "");
ngx_conf_merge_str_value(conf->ssl_crl, prev->ssl_crl, "");
ngx_conf_merge_str_value(conf->ssl_certificate,
prev->ssl_certificate, "");
ngx_conf_merge_str_value(conf->ssl_certificate_key,
prev->ssl_certificate_key, "");
ngx_conf_merge_ptr_value(conf->ssl_passwords, prev->ssl_passwords, NULL);
if (conf->ssl && ngx_http_proxy_set_ssl(cf, conf) != NGX_OK) {
return NGX_CONF_ERROR;
}
#endif
ngx_conf_merge_value(conf->redirect, prev->redirect, 1);
if (conf->redirect) {
if (conf->redirects == NULL) {
conf->redirects = prev->redirects;
}
if (conf->redirects == NULL && conf->url.data) {
conf->redirects = ngx_array_create(cf->pool, 1,
sizeof(ngx_http_proxy_rewrite_t));
if (conf->redirects == NULL) {
return NGX_CONF_ERROR;
}
pr = ngx_array_push(conf->redirects);
if (pr == NULL) {
return NGX_CONF_ERROR;
}
ngx_memzero(&pr->pattern.complex,
sizeof(ngx_http_complex_value_t));
ngx_memzero(&pr->replacement, sizeof(ngx_http_complex_value_t));
pr->handler = ngx_http_proxy_rewrite_complex_handler;
if (conf->vars.uri.len) {
pr->pattern.complex.value = conf->url;
pr->replacement.value = conf->location;
} else {
pr->pattern.complex.value.len = conf->url.len
+ sizeof("/") - 1;
p = ngx_pnalloc(cf->pool, pr->pattern.complex.value.len);
if (p == NULL) {
return NGX_CONF_ERROR;
}
pr->pattern.complex.value.data = p;
p = ngx_cpymem(p, conf->url.data, conf->url.len);
*p = '/';
ngx_str_set(&pr->replacement.value, "/");
}
}
}
ngx_conf_merge_ptr_value(conf->cookie_domains, prev->cookie_domains, NULL);
ngx_conf_merge_ptr_value(conf->cookie_paths, prev->cookie_paths, NULL);
ngx_conf_merge_uint_value(conf->http_version, prev->http_version,
NGX_HTTP_VERSION_10);
ngx_conf_merge_uint_value(conf->headers_hash_max_size,
prev->headers_hash_max_size, 512);
ngx_conf_merge_uint_value(conf->headers_hash_bucket_size,
prev->headers_hash_bucket_size, 64);
conf->headers_hash_bucket_size = ngx_align(conf->headers_hash_bucket_size,
ngx_cacheline_size);
hash.max_size = conf->headers_hash_max_size;
hash.bucket_size = conf->headers_hash_bucket_size;
hash.name = "proxy_headers_hash";
if (ngx_http_upstream_hide_headers_hash(cf, &conf->upstream,
&prev->upstream, ngx_http_proxy_hide_headers, &hash)
!= NGX_OK)
{
return NGX_CONF_ERROR;
}
clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
if (clcf->noname
&& conf->upstream.upstream == NULL && conf->proxy_lengths == NULL)
{
conf->upstream.upstream = prev->upstream.upstream;
conf->location = prev->location;
conf->vars = prev->vars;
conf->proxy_lengths = prev->proxy_lengths;
conf->proxy_values = prev->proxy_values;
#if (NGX_HTTP_SSL)
conf->upstream.ssl = prev->upstream.ssl;
#endif
}
if (clcf->lmt_excpt && clcf->handler == NULL
&& (conf->upstream.upstream || conf->proxy_lengths))
{
clcf->handler = ngx_http_proxy_handler;
}
if (conf->body_source.data == NULL) {
conf->body_flushes = prev->body_flushes;
conf->body_source = prev->body_source;
conf->body_lengths = prev->body_lengths;
conf->body_values = prev->body_values;
}
if (conf->body_source.data && conf->body_lengths == NULL) {
ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
sc.cf = cf;
sc.source = &conf->body_source;
sc.flushes = &conf->body_flushes;
sc.lengths = &conf->body_lengths;
sc.values = &conf->body_values;
sc.complete_lengths = 1;
sc.complete_values = 1;
if (ngx_http_script_compile(&sc) != NGX_OK) {
return NGX_CONF_ERROR;
}
}
if (conf->headers_source == NULL) {
conf->headers = prev->headers;
#if (NGX_HTTP_CACHE)
conf->headers_cache = prev->headers_cache;
#endif
conf->headers_source = prev->headers_source;
}
rc = ngx_http_proxy_init_headers(cf, conf, &conf->headers,
ngx_http_proxy_headers);
if (rc != NGX_OK) {
return NGX_CONF_ERROR;
}
#if (NGX_HTTP_CACHE)
if (conf->upstream.cache) {
rc = ngx_http_proxy_init_headers(cf, conf, &conf->headers_cache,
ngx_http_proxy_cache_headers);
if (rc != NGX_OK) {
return NGX_CONF_ERROR;
}
}
#endif
/*
* special handling to preserve conf->headers in the "http" section
* to inherit it to all servers
*/
if (prev->headers.hash.buckets == NULL
&& conf->headers_source == prev->headers_source)
{
prev->headers = conf->headers;
#if (NGX_HTTP_CACHE)
prev->headers_cache = conf->headers_cache;
#endif
}
return NGX_CONF_OK;
}
static ngx_int_t
ngx_http_proxy_init_headers(ngx_conf_t *cf, ngx_http_proxy_loc_conf_t *conf,
ngx_http_proxy_headers_t *headers, ngx_keyval_t *default_headers)
{
u_char *p;
size_t size;
uintptr_t *code;
ngx_uint_t i;
ngx_array_t headers_names, headers_merged;
ngx_keyval_t *src, *s, *h;
ngx_hash_key_t *hk;
ngx_hash_init_t hash;
ngx_http_script_compile_t sc;
ngx_http_script_copy_code_t *copy;
if (headers->hash.buckets) {
return NGX_OK;
}
if (ngx_array_init(&headers_names, cf->temp_pool, 4, sizeof(ngx_hash_key_t))
!= NGX_OK)
{
return NGX_ERROR;
}
if (ngx_array_init(&headers_merged, cf->temp_pool, 4, sizeof(ngx_keyval_t))
!= NGX_OK)
{
return NGX_ERROR;
}
headers->lengths = ngx_array_create(cf->pool, 64, 1);
if (headers->lengths == NULL) {
return NGX_ERROR;
}
headers->values = ngx_array_create(cf->pool, 512, 1);
if (headers->values == NULL) {
return NGX_ERROR;
}
if (conf->headers_source) {
src = conf->headers_source->elts;
for (i = 0; i < conf->headers_source->nelts; i++) {
s = ngx_array_push(&headers_merged);
if (s == NULL) {
return NGX_ERROR;
}
*s = src[i];
}
}
h = default_headers;
while (h->key.len) {
src = headers_merged.elts;
for (i = 0; i < headers_merged.nelts; i++) {
if (ngx_strcasecmp(h->key.data, src[i].key.data) == 0) {
goto next;
}
}
s = ngx_array_push(&headers_merged);
if (s == NULL) {
return NGX_ERROR;
}
*s = *h;
next:
h++;
}
src = headers_merged.elts;
for (i = 0; i < headers_merged.nelts; i++) {
hk = ngx_array_push(&headers_names);
if (hk == NULL) {
return NGX_ERROR;
}
hk->key = src[i].key;
hk->key_hash = ngx_hash_key_lc(src[i].key.data, src[i].key.len);
hk->value = (void *) 1;
if (src[i].value.len == 0) {
continue;
}
copy = ngx_array_push_n(headers->lengths,
sizeof(ngx_http_script_copy_code_t));
if (copy == NULL) {
return NGX_ERROR;
}
copy->code = (ngx_http_script_code_pt) ngx_http_script_copy_len_code;
copy->len = src[i].key.len;
size = (sizeof(ngx_http_script_copy_code_t)
+ src[i].key.len + sizeof(uintptr_t) - 1)
& ~(sizeof(uintptr_t) - 1);
copy = ngx_array_push_n(headers->values, size);
if (copy == NULL) {
return NGX_ERROR;
}
copy->code = ngx_http_script_copy_code;
copy->len = src[i].key.len;
p = (u_char *) copy + sizeof(ngx_http_script_copy_code_t);
ngx_memcpy(p, src[i].key.data, src[i].key.len);
ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
sc.cf = cf;
sc.source = &src[i].value;
sc.flushes = &headers->flushes;
sc.lengths = &headers->lengths;
sc.values = &headers->values;
if (ngx_http_script_compile(&sc) != NGX_OK) {
return NGX_ERROR;
}
code = ngx_array_push_n(headers->lengths, sizeof(uintptr_t));
if (code == NULL) {
return NGX_ERROR;
}
*code = (uintptr_t) NULL;
code = ngx_array_push_n(headers->values, sizeof(uintptr_t));
if (code == NULL) {
return NGX_ERROR;
}
*code = (uintptr_t) NULL;
}
code = ngx_array_push_n(headers->lengths, sizeof(uintptr_t));
if (code == NULL) {
return NGX_ERROR;
}
*code = (uintptr_t) NULL;
hash.hash = &headers->hash;
hash.key = ngx_hash_key_lc;
hash.max_size = conf->headers_hash_max_size;
hash.bucket_size = conf->headers_hash_bucket_size;
hash.name = "proxy_headers_hash";
hash.pool = cf->pool;
hash.temp_pool = NULL;
return ngx_hash_init(&hash, headers_names.elts, headers_names.nelts);
}
static char *
ngx_http_proxy_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_proxy_loc_conf_t *plcf = conf;
size_t add;
u_short port;
ngx_str_t *value, *url;
ngx_url_t u;
ngx_uint_t n;
ngx_http_core_loc_conf_t *clcf;
ngx_http_script_compile_t sc;
if (plcf->upstream.upstream || plcf->proxy_lengths) {
return "is duplicate";
}
clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
clcf->handler = ngx_http_proxy_handler;
if (clcf->name.data[clcf->name.len - 1] == '/') {
clcf->auto_redirect = 1;
}
value = cf->args->elts;
url = &value[1];
n = ngx_http_script_variables_count(url);
if (n) {
ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
sc.cf = cf;
sc.source = url;
sc.lengths = &plcf->proxy_lengths;
sc.values = &plcf->proxy_values;
sc.variables = n;
sc.complete_lengths = 1;
sc.complete_values = 1;
if (ngx_http_script_compile(&sc) != NGX_OK) {
return NGX_CONF_ERROR;
}
#if (NGX_HTTP_SSL)
plcf->ssl = 1;
#endif
return NGX_CONF_OK;
}
if (ngx_strncasecmp(url->data, (u_char *) "http://", 7) == 0) {
add = 7;
port = 80;
} else if (ngx_strncasecmp(url->data, (u_char *) "https://", 8) == 0) {
#if (NGX_HTTP_SSL)
plcf->ssl = 1;
add = 8;
port = 443;
#else
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"https protocol requires SSL support");
return NGX_CONF_ERROR;
#endif
} else {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid URL prefix");
return NGX_CONF_ERROR;
}
ngx_memzero(&u, sizeof(ngx_url_t));
u.url.len = url->len - add;
u.url.data = url->data + add;
u.default_port = port;
u.uri_part = 1;
u.no_resolve = 1;
plcf->upstream.upstream = ngx_http_upstream_add(cf, &u, 0);
if (plcf->upstream.upstream == NULL) {
return NGX_CONF_ERROR;
}
plcf->vars.schema.len = add;
plcf->vars.schema.data = url->data;
plcf->vars.key_start = plcf->vars.schema;
ngx_http_proxy_set_vars(&u, &plcf->vars);
plcf->location = clcf->name;
if (clcf->named
#if (NGX_PCRE)
|| clcf->regex
#endif
|| clcf->noname)
{
if (plcf->vars.uri.len) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"\"proxy_pass\" cannot have URI part in "
"location given by regular expression, "
"or inside named location, "
"or inside \"if\" statement, "
"or inside \"limit_except\" block");
return NGX_CONF_ERROR;
}
plcf->location.len = 0;
}
plcf->url = *url;
return NGX_CONF_OK;
}
static char *
ngx_http_proxy_redirect(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_proxy_loc_conf_t *plcf = conf;
u_char *p;
ngx_str_t *value;
ngx_http_proxy_rewrite_t *pr;
ngx_http_compile_complex_value_t ccv;
if (plcf->redirect == 0) {
return NGX_CONF_OK;
}
plcf->redirect = 1;
value = cf->args->elts;
if (cf->args->nelts == 2) {
if (ngx_strcmp(value[1].data, "off") == 0) {
plcf->redirect = 0;
plcf->redirects = NULL;
return NGX_CONF_OK;
}
if (ngx_strcmp(value[1].data, "false") == 0) {
ngx_conf_log_error(NGX_LOG_ERR, cf, 0,
"invalid parameter \"false\", use \"off\" instead");
plcf->redirect = 0;
plcf->redirects = NULL;
return NGX_CONF_OK;
}
if (ngx_strcmp(value[1].data, "default") != 0) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid parameter \"%V\"", &value[1]);
return NGX_CONF_ERROR;
}
}
if (plcf->redirects == NULL) {
plcf->redirects = ngx_array_create(cf->pool, 1,
sizeof(ngx_http_proxy_rewrite_t));
if (plcf->redirects == NULL) {
return NGX_CONF_ERROR;
}
}
pr = ngx_array_push(plcf->redirects);
if (pr == NULL) {
return NGX_CONF_ERROR;
}
if (ngx_strcmp(value[1].data, "default") == 0) {
if (plcf->proxy_lengths) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"\"proxy_redirect default\" cannot be used "
"with \"proxy_pass\" directive with variables");
return NGX_CONF_ERROR;
}
if (plcf->url.data == NULL) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"\"proxy_redirect default\" should be placed "
"after the \"proxy_pass\" directive");
return NGX_CONF_ERROR;
}
pr->handler = ngx_http_proxy_rewrite_complex_handler;
ngx_memzero(&pr->pattern.complex, sizeof(ngx_http_complex_value_t));
ngx_memzero(&pr->replacement, sizeof(ngx_http_complex_value_t));
if (plcf->vars.uri.len) {
pr->pattern.complex.value = plcf->url;
pr->replacement.value = plcf->location;
} else {
pr->pattern.complex.value.len = plcf->url.len + sizeof("/") - 1;
p = ngx_pnalloc(cf->pool, pr->pattern.complex.value.len);
if (p == NULL) {
return NGX_CONF_ERROR;
}
pr->pattern.complex.value.data = p;
p = ngx_cpymem(p, plcf->url.data, plcf->url.len);
*p = '/';
ngx_str_set(&pr->replacement.value, "/");
}
return NGX_CONF_OK;
}
if (value[1].data[0] == '~') {
value[1].len--;
value[1].data++;
if (value[1].data[0] == '*') {
value[1].len--;
value[1].data++;
if (ngx_http_proxy_rewrite_regex(cf, pr, &value[1], 1) != NGX_OK) {
return NGX_CONF_ERROR;
}
} else {
if (ngx_http_proxy_rewrite_regex(cf, pr, &value[1], 0) != NGX_OK) {
return NGX_CONF_ERROR;
}
}
} else {
ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
ccv.cf = cf;
ccv.value = &value[1];
ccv.complex_value = &pr->pattern.complex;
if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
return NGX_CONF_ERROR;
}
pr->handler = ngx_http_proxy_rewrite_complex_handler;
}
ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
ccv.cf = cf;
ccv.value = &value[2];
ccv.complex_value = &pr->replacement;
if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
return NGX_CONF_ERROR;
}
return NGX_CONF_OK;
}
static char *
ngx_http_proxy_cookie_domain(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_proxy_loc_conf_t *plcf = conf;
ngx_str_t *value;
ngx_http_proxy_rewrite_t *pr;
ngx_http_compile_complex_value_t ccv;
if (plcf->cookie_domains == NULL) {
return NGX_CONF_OK;
}
value = cf->args->elts;
if (cf->args->nelts == 2) {
if (ngx_strcmp(value[1].data, "off") == 0) {
plcf->cookie_domains = NULL;
return NGX_CONF_OK;
}
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid parameter \"%V\"", &value[1]);
return NGX_CONF_ERROR;
}
if (plcf->cookie_domains == NGX_CONF_UNSET_PTR) {
plcf->cookie_domains = ngx_array_create(cf->pool, 1,
sizeof(ngx_http_proxy_rewrite_t));
if (plcf->cookie_domains == NULL) {
return NGX_CONF_ERROR;
}
}
pr = ngx_array_push(plcf->cookie_domains);
if (pr == NULL) {
return NGX_CONF_ERROR;
}
if (value[1].data[0] == '~') {
value[1].len--;
value[1].data++;
if (ngx_http_proxy_rewrite_regex(cf, pr, &value[1], 1) != NGX_OK) {
return NGX_CONF_ERROR;
}
} else {
if (value[1].data[0] == '.') {
value[1].len--;
value[1].data++;
}
ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
ccv.cf = cf;
ccv.value = &value[1];
ccv.complex_value = &pr->pattern.complex;
if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
return NGX_CONF_ERROR;
}
pr->handler = ngx_http_proxy_rewrite_domain_handler;
if (value[2].data[0] == '.') {
value[2].len--;
value[2].data++;
}
}
ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
ccv.cf = cf;
ccv.value = &value[2];
ccv.complex_value = &pr->replacement;
if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
return NGX_CONF_ERROR;
}
return NGX_CONF_OK;
}
static char *
ngx_http_proxy_cookie_path(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_proxy_loc_conf_t *plcf = conf;
ngx_str_t *value;
ngx_http_proxy_rewrite_t *pr;
ngx_http_compile_complex_value_t ccv;
if (plcf->cookie_paths == NULL) {
return NGX_CONF_OK;
}
value = cf->args->elts;
if (cf->args->nelts == 2) {
if (ngx_strcmp(value[1].data, "off") == 0) {
plcf->cookie_paths = NULL;
return NGX_CONF_OK;
}
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid parameter \"%V\"", &value[1]);
return NGX_CONF_ERROR;
}
if (plcf->cookie_paths == NGX_CONF_UNSET_PTR) {
plcf->cookie_paths = ngx_array_create(cf->pool, 1,
sizeof(ngx_http_proxy_rewrite_t));
if (plcf->cookie_paths == NULL) {
return NGX_CONF_ERROR;
}
}
pr = ngx_array_push(plcf->cookie_paths);
if (pr == NULL) {
return NGX_CONF_ERROR;
}
if (value[1].data[0] == '~') {
value[1].len--;
value[1].data++;
if (value[1].data[0] == '*') {
value[1].len--;
value[1].data++;
if (ngx_http_proxy_rewrite_regex(cf, pr, &value[1], 1) != NGX_OK) {
return NGX_CONF_ERROR;
}
} else {
if (ngx_http_proxy_rewrite_regex(cf, pr, &value[1], 0) != NGX_OK) {
return NGX_CONF_ERROR;
}
}
} else {
ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
ccv.cf = cf;
ccv.value = &value[1];
ccv.complex_value = &pr->pattern.complex;
if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
return NGX_CONF_ERROR;
}
pr->handler = ngx_http_proxy_rewrite_complex_handler;
}
ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
ccv.cf = cf;
ccv.value = &value[2];
ccv.complex_value = &pr->replacement;
if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
return NGX_CONF_ERROR;
}
return NGX_CONF_OK;
}
static ngx_int_t
ngx_http_proxy_rewrite_regex(ngx_conf_t *cf, ngx_http_proxy_rewrite_t *pr,
ngx_str_t *regex, ngx_uint_t caseless)
{
#if (NGX_PCRE)
u_char errstr[NGX_MAX_CONF_ERRSTR];
ngx_regex_compile_t rc;
ngx_memzero(&rc, sizeof(ngx_regex_compile_t));
rc.pattern = *regex;
rc.err.len = NGX_MAX_CONF_ERRSTR;
rc.err.data = errstr;
if (caseless) {
rc.options = NGX_REGEX_CASELESS;
}
pr->pattern.regex = ngx_http_regex_compile(cf, &rc);
if (pr->pattern.regex == NULL) {
return NGX_ERROR;
}
pr->handler = ngx_http_proxy_rewrite_regex_handler;
return NGX_OK;
#else
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"using regex \"%V\" requires PCRE library", regex);
return NGX_ERROR;
#endif
}
static char *
ngx_http_proxy_store(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_proxy_loc_conf_t *plcf = conf;
ngx_str_t *value;
ngx_http_script_compile_t sc;
if (plcf->upstream.store != NGX_CONF_UNSET) {
return "is duplicate";
}
value = cf->args->elts;
if (ngx_strcmp(value[1].data, "off") == 0) {
plcf->upstream.store = 0;
return NGX_CONF_OK;
}
#if (NGX_HTTP_CACHE)
if (plcf->upstream.cache > 0) {
return "is incompatible with \"proxy_cache\"";
}
#endif
plcf->upstream.store = 1;
if (ngx_strcmp(value[1].data, "on") == 0) {
return NGX_CONF_OK;
}
/* include the terminating '\0' into script */
value[1].len++;
ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
sc.cf = cf;
sc.source = &value[1];
sc.lengths = &plcf->upstream.store_lengths;
sc.values = &plcf->upstream.store_values;
sc.variables = ngx_http_script_variables_count(&value[1]);
sc.complete_lengths = 1;
sc.complete_values = 1;
if (ngx_http_script_compile(&sc) != NGX_OK) {
return NGX_CONF_ERROR;
}
return NGX_CONF_OK;
}
#if (NGX_HTTP_CACHE)
static char *
ngx_http_proxy_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_proxy_loc_conf_t *plcf = conf;
ngx_str_t *value;
ngx_http_complex_value_t cv;
ngx_http_compile_complex_value_t ccv;
value = cf->args->elts;
if (plcf->upstream.cache != NGX_CONF_UNSET) {
return "is duplicate";
}
if (ngx_strcmp(value[1].data, "off") == 0) {
plcf->upstream.cache = 0;
return NGX_CONF_OK;
}
if (plcf->upstream.store > 0) {
return "is incompatible with \"proxy_store\"";
}
plcf->upstream.cache = 1;
ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
ccv.cf = cf;
ccv.value = &value[1];
ccv.complex_value = &cv;
if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
return NGX_CONF_ERROR;
}
if (cv.lengths != NULL) {
plcf->upstream.cache_value = ngx_palloc(cf->pool,
sizeof(ngx_http_complex_value_t));
if (plcf->upstream.cache_value == NULL) {
return NGX_CONF_ERROR;
}
*plcf->upstream.cache_value = cv;
return NGX_CONF_OK;
}
plcf->upstream.cache_zone = ngx_shared_memory_add(cf, &value[1], 0,
&ngx_http_proxy_module);
if (plcf->upstream.cache_zone == NULL) {
return NGX_CONF_ERROR;
}
return NGX_CONF_OK;
}
static char *
ngx_http_proxy_cache_key(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_proxy_loc_conf_t *plcf = conf;
ngx_str_t *value;
ngx_http_compile_complex_value_t ccv;
value = cf->args->elts;
if (plcf->cache_key.value.data) {
return "is duplicate";
}
ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
ccv.cf = cf;
ccv.value = &value[1];
ccv.complex_value = &plcf->cache_key;
if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
return NGX_CONF_ERROR;
}
return NGX_CONF_OK;
}
#endif
#if (NGX_HTTP_SSL)
static char *
ngx_http_proxy_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_proxy_loc_conf_t *plcf = conf;
ngx_str_t *value;
if (plcf->ssl_passwords != NGX_CONF_UNSET_PTR) {
return "is duplicate";
}
value = cf->args->elts;
plcf->ssl_passwords = ngx_ssl_read_password_file(cf, &value[1]);
if (plcf->ssl_passwords == NULL) {
return NGX_CONF_ERROR;
}
return NGX_CONF_OK;
}
#endif
static char *
ngx_http_proxy_lowat_check(ngx_conf_t *cf, void *post, void *data)
{
#if (NGX_FREEBSD)
ssize_t *np = data;
if ((u_long) *np >= ngx_freebsd_net_inet_tcp_sendspace) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"\"proxy_send_lowat\" must be less than %d "
"(sysctl net.inet.tcp.sendspace)",
ngx_freebsd_net_inet_tcp_sendspace);
return NGX_CONF_ERROR;
}
#elif !(NGX_HAVE_SO_SNDLOWAT)
ssize_t *np = data;
ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
"\"proxy_send_lowat\" is not supported, ignored");
*np = 0;
#endif
return NGX_CONF_OK;
}
#if (NGX_HTTP_SSL)
static ngx_int_t
ngx_http_proxy_set_ssl(ngx_conf_t *cf, ngx_http_proxy_loc_conf_t *plcf)
{
ngx_pool_cleanup_t *cln;
plcf->upstream.ssl = ngx_pcalloc(cf->pool, sizeof(ngx_ssl_t));
if (plcf->upstream.ssl == NULL) {
return NGX_ERROR;
}
plcf->upstream.ssl->log = cf->log;
if (ngx_ssl_create(plcf->upstream.ssl, plcf->ssl_protocols, NULL)
!= NGX_OK)
{
return NGX_ERROR;
}
cln = ngx_pool_cleanup_add(cf->pool, 0);
if (cln == NULL) {
return NGX_ERROR;
}
cln->handler = ngx_ssl_cleanup_ctx;
cln->data = plcf->upstream.ssl;
if (plcf->ssl_certificate.len) {
if (plcf->ssl_certificate_key.len == 0) {
ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
"no \"proxy_ssl_certificate_key\" is defined "
"for certificate \"%V\"", &plcf->ssl_certificate);
return NGX_ERROR;
}
if (ngx_ssl_certificate(cf, plcf->upstream.ssl, &plcf->ssl_certificate,
&plcf->ssl_certificate_key, plcf->ssl_passwords)
!= NGX_OK)
{
return NGX_ERROR;
}
}
if (ngx_ssl_ciphers(cf, plcf->upstream.ssl, &plcf->ssl_ciphers, 0)
!= NGX_OK)
{
return NGX_ERROR;
}
if (plcf->upstream.ssl_verify) {
if (plcf->ssl_trusted_certificate.len == 0) {
ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
"no proxy_ssl_trusted_certificate for proxy_ssl_verify");
return NGX_ERROR;
}
if (ngx_ssl_trusted_certificate(cf, plcf->upstream.ssl,
&plcf->ssl_trusted_certificate,
plcf->ssl_verify_depth)
!= NGX_OK)
{
return NGX_ERROR;
}
if (ngx_ssl_crl(cf, plcf->upstream.ssl, &plcf->ssl_crl) != NGX_OK) {
return NGX_ERROR;
}
}
return NGX_OK;
}
#endif
static void
ngx_http_proxy_set_vars(ngx_url_t *u, ngx_http_proxy_vars_t *v)
{
if (u->family != AF_UNIX) {
if (u->no_port || u->port == u->default_port) {
v->host_header = u->host;
if (u->default_port == 80) {
ngx_str_set(&v->port, "80");
} else {
ngx_str_set(&v->port, "443");
}
} else {
v->host_header.len = u->host.len + 1 + u->port_text.len;
v->host_header.data = u->host.data;
v->port = u->port_text;
}
v->key_start.len += v->host_header.len;
} else {
ngx_str_set(&v->host_header, "localhost");
ngx_str_null(&v->port);
v->key_start.len += sizeof("unix:") - 1 + u->host.len + 1;
}
v->uri = u->uri;
}
nginx-1.14.0/src/http/modules/ngx_http_upstream_ip_hash_module.c 000644 001751 001751 00000016067 13265410474 026323 0 ustar 00mdounin mdounin 000000 000000
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#include
#include
#include
typedef struct {
/* the round robin data must be first */
ngx_http_upstream_rr_peer_data_t rrp;
ngx_uint_t hash;
u_char addrlen;
u_char *addr;
u_char tries;
ngx_event_get_peer_pt get_rr_peer;
} ngx_http_upstream_ip_hash_peer_data_t;
static ngx_int_t ngx_http_upstream_init_ip_hash_peer(ngx_http_request_t *r,
ngx_http_upstream_srv_conf_t *us);
static ngx_int_t ngx_http_upstream_get_ip_hash_peer(ngx_peer_connection_t *pc,
void *data);
static char *ngx_http_upstream_ip_hash(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static ngx_command_t ngx_http_upstream_ip_hash_commands[] = {
{ ngx_string("ip_hash"),
NGX_HTTP_UPS_CONF|NGX_CONF_NOARGS,
ngx_http_upstream_ip_hash,
0,
0,
NULL },
ngx_null_command
};
static ngx_http_module_t ngx_http_upstream_ip_hash_module_ctx = {
NULL, /* preconfiguration */
NULL, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
NULL, /* create location configuration */
NULL /* merge location configuration */
};
ngx_module_t ngx_http_upstream_ip_hash_module = {
NGX_MODULE_V1,
&ngx_http_upstream_ip_hash_module_ctx, /* module context */
ngx_http_upstream_ip_hash_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static u_char ngx_http_upstream_ip_hash_pseudo_addr[3];
static ngx_int_t
ngx_http_upstream_init_ip_hash(ngx_conf_t *cf, ngx_http_upstream_srv_conf_t *us)
{
if (ngx_http_upstream_init_round_robin(cf, us) != NGX_OK) {
return NGX_ERROR;
}
us->peer.init = ngx_http_upstream_init_ip_hash_peer;
return NGX_OK;
}
static ngx_int_t
ngx_http_upstream_init_ip_hash_peer(ngx_http_request_t *r,
ngx_http_upstream_srv_conf_t *us)
{
struct sockaddr_in *sin;
#if (NGX_HAVE_INET6)
struct sockaddr_in6 *sin6;
#endif
ngx_http_upstream_ip_hash_peer_data_t *iphp;
iphp = ngx_palloc(r->pool, sizeof(ngx_http_upstream_ip_hash_peer_data_t));
if (iphp == NULL) {
return NGX_ERROR;
}
r->upstream->peer.data = &iphp->rrp;
if (ngx_http_upstream_init_round_robin_peer(r, us) != NGX_OK) {
return NGX_ERROR;
}
r->upstream->peer.get = ngx_http_upstream_get_ip_hash_peer;
switch (r->connection->sockaddr->sa_family) {
case AF_INET:
sin = (struct sockaddr_in *) r->connection->sockaddr;
iphp->addr = (u_char *) &sin->sin_addr.s_addr;
iphp->addrlen = 3;
break;
#if (NGX_HAVE_INET6)
case AF_INET6:
sin6 = (struct sockaddr_in6 *) r->connection->sockaddr;
iphp->addr = (u_char *) &sin6->sin6_addr.s6_addr;
iphp->addrlen = 16;
break;
#endif
default:
iphp->addr = ngx_http_upstream_ip_hash_pseudo_addr;
iphp->addrlen = 3;
}
iphp->hash = 89;
iphp->tries = 0;
iphp->get_rr_peer = ngx_http_upstream_get_round_robin_peer;
return NGX_OK;
}
static ngx_int_t
ngx_http_upstream_get_ip_hash_peer(ngx_peer_connection_t *pc, void *data)
{
ngx_http_upstream_ip_hash_peer_data_t *iphp = data;
time_t now;
ngx_int_t w;
uintptr_t m;
ngx_uint_t i, n, p, hash;
ngx_http_upstream_rr_peer_t *peer;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
"get ip hash peer, try: %ui", pc->tries);
/* TODO: cached */
ngx_http_upstream_rr_peers_wlock(iphp->rrp.peers);
if (iphp->tries > 20 || iphp->rrp.peers->single) {
ngx_http_upstream_rr_peers_unlock(iphp->rrp.peers);
return iphp->get_rr_peer(pc, &iphp->rrp);
}
now = ngx_time();
pc->cached = 0;
pc->connection = NULL;
hash = iphp->hash;
for ( ;; ) {
for (i = 0; i < (ngx_uint_t) iphp->addrlen; i++) {
hash = (hash * 113 + iphp->addr[i]) % 6271;
}
w = hash % iphp->rrp.peers->total_weight;
peer = iphp->rrp.peers->peer;
p = 0;
while (w >= peer->weight) {
w -= peer->weight;
peer = peer->next;
p++;
}
n = p / (8 * sizeof(uintptr_t));
m = (uintptr_t) 1 << p % (8 * sizeof(uintptr_t));
if (iphp->rrp.tried[n] & m) {
goto next;
}
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,
"get ip hash peer, hash: %ui %04XL", p, (uint64_t) m);
if (peer->down) {
goto next;
}
if (peer->max_fails
&& peer->fails >= peer->max_fails
&& now - peer->checked <= peer->fail_timeout)
{
goto next;
}
if (peer->max_conns && peer->conns >= peer->max_conns) {
goto next;
}
break;
next:
if (++iphp->tries > 20) {
ngx_http_upstream_rr_peers_unlock(iphp->rrp.peers);
return iphp->get_rr_peer(pc, &iphp->rrp);
}
}
iphp->rrp.current = peer;
pc->sockaddr = peer->sockaddr;
pc->socklen = peer->socklen;
pc->name = &peer->name;
peer->conns++;
if (now - peer->checked > peer->fail_timeout) {
peer->checked = now;
}
ngx_http_upstream_rr_peers_unlock(iphp->rrp.peers);
iphp->rrp.tried[n] |= m;
iphp->hash = hash;
return NGX_OK;
}
static char *
ngx_http_upstream_ip_hash(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_upstream_srv_conf_t *uscf;
uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);
if (uscf->peer.init_upstream) {
ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
"load balancing method redefined");
}
uscf->peer.init_upstream = ngx_http_upstream_init_ip_hash;
uscf->flags = NGX_HTTP_UPSTREAM_CREATE
|NGX_HTTP_UPSTREAM_WEIGHT
|NGX_HTTP_UPSTREAM_MAX_CONNS
|NGX_HTTP_UPSTREAM_MAX_FAILS
|NGX_HTTP_UPSTREAM_FAIL_TIMEOUT
|NGX_HTTP_UPSTREAM_DOWN;
return NGX_CONF_OK;
}
nginx-1.14.0/src/http/modules/ngx_http_range_filter_module.c 000644 001751 001751 00000062606 13265410474 025431 0 ustar 00mdounin mdounin 000000 000000
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#include
#include
#include
/*
* the single part format:
*
* "HTTP/1.0 206 Partial Content" CRLF
* ... header ...
* "Content-Type: image/jpeg" CRLF
* "Content-Length: SIZE" CRLF
* "Content-Range: bytes START-END/SIZE" CRLF
* CRLF
* ... data ...
*
*
* the multipart format:
*
* "HTTP/1.0 206 Partial Content" CRLF
* ... header ...
* "Content-Type: multipart/byteranges; boundary=0123456789" CRLF
* CRLF
* CRLF
* "--0123456789" CRLF
* "Content-Type: image/jpeg" CRLF
* "Content-Range: bytes START0-END0/SIZE" CRLF
* CRLF
* ... data ...
* CRLF
* "--0123456789" CRLF
* "Content-Type: image/jpeg" CRLF
* "Content-Range: bytes START1-END1/SIZE" CRLF
* CRLF
* ... data ...
* CRLF
* "--0123456789--" CRLF
*/
typedef struct {
off_t start;
off_t end;
ngx_str_t content_range;
} ngx_http_range_t;
typedef struct {
off_t offset;
ngx_str_t boundary_header;
ngx_array_t ranges;
} ngx_http_range_filter_ctx_t;
static ngx_int_t ngx_http_range_parse(ngx_http_request_t *r,
ngx_http_range_filter_ctx_t *ctx, ngx_uint_t ranges);
static ngx_int_t ngx_http_range_singlepart_header(ngx_http_request_t *r,
ngx_http_range_filter_ctx_t *ctx);
static ngx_int_t ngx_http_range_multipart_header(ngx_http_request_t *r,
ngx_http_range_filter_ctx_t *ctx);
static ngx_int_t ngx_http_range_not_satisfiable(ngx_http_request_t *r);
static ngx_int_t ngx_http_range_test_overlapped(ngx_http_request_t *r,
ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in);
static ngx_int_t ngx_http_range_singlepart_body(ngx_http_request_t *r,
ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in);
static ngx_int_t ngx_http_range_multipart_body(ngx_http_request_t *r,
ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in);
static ngx_int_t ngx_http_range_header_filter_init(ngx_conf_t *cf);
static ngx_int_t ngx_http_range_body_filter_init(ngx_conf_t *cf);
static ngx_http_module_t ngx_http_range_header_filter_module_ctx = {
NULL, /* preconfiguration */
ngx_http_range_header_filter_init, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
NULL, /* create location configuration */
NULL, /* merge location configuration */
};
ngx_module_t ngx_http_range_header_filter_module = {
NGX_MODULE_V1,
&ngx_http_range_header_filter_module_ctx, /* module context */
NULL, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static ngx_http_module_t ngx_http_range_body_filter_module_ctx = {
NULL, /* preconfiguration */
ngx_http_range_body_filter_init, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
NULL, /* create location configuration */
NULL, /* merge location configuration */
};
ngx_module_t ngx_http_range_body_filter_module = {
NGX_MODULE_V1,
&ngx_http_range_body_filter_module_ctx, /* module context */
NULL, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static ngx_http_output_header_filter_pt ngx_http_next_header_filter;
static ngx_http_output_body_filter_pt ngx_http_next_body_filter;
static ngx_int_t
ngx_http_range_header_filter(ngx_http_request_t *r)
{
time_t if_range_time;
ngx_str_t *if_range, *etag;
ngx_uint_t ranges;
ngx_http_core_loc_conf_t *clcf;
ngx_http_range_filter_ctx_t *ctx;
if (r->http_version < NGX_HTTP_VERSION_10
|| r->headers_out.status != NGX_HTTP_OK
|| (r != r->main && !r->subrequest_ranges)
|| r->headers_out.content_length_n == -1
|| !r->allow_ranges)
{
return ngx_http_next_header_filter(r);
}
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
if (clcf->max_ranges == 0) {
return ngx_http_next_header_filter(r);
}
if (r->headers_in.range == NULL
|| r->headers_in.range->value.len < 7
|| ngx_strncasecmp(r->headers_in.range->value.data,
(u_char *) "bytes=", 6)
!= 0)
{
goto next_filter;
}
if (r->headers_in.if_range) {
if_range = &r->headers_in.if_range->value;
if (if_range->len >= 2 && if_range->data[if_range->len - 1] == '"') {
if (r->headers_out.etag == NULL) {
goto next_filter;
}
etag = &r->headers_out.etag->value;
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http ir:%V etag:%V", if_range, etag);
if (if_range->len != etag->len
|| ngx_strncmp(if_range->data, etag->data, etag->len) != 0)
{
goto next_filter;
}
goto parse;
}
if (r->headers_out.last_modified_time == (time_t) -1) {
goto next_filter;
}
if_range_time = ngx_parse_http_time(if_range->data, if_range->len);
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http ir:%T lm:%T",
if_range_time, r->headers_out.last_modified_time);
if (if_range_time != r->headers_out.last_modified_time) {
goto next_filter;
}
}
parse:
ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_range_filter_ctx_t));
if (ctx == NULL) {
return NGX_ERROR;
}
ctx->offset = r->headers_out.content_offset;
ranges = r->single_range ? 1 : clcf->max_ranges;
switch (ngx_http_range_parse(r, ctx, ranges)) {
case NGX_OK:
ngx_http_set_ctx(r, ctx, ngx_http_range_body_filter_module);
r->headers_out.status = NGX_HTTP_PARTIAL_CONTENT;
r->headers_out.status_line.len = 0;
if (ctx->ranges.nelts == 1) {
return ngx_http_range_singlepart_header(r, ctx);
}
return ngx_http_range_multipart_header(r, ctx);
case NGX_HTTP_RANGE_NOT_SATISFIABLE:
return ngx_http_range_not_satisfiable(r);
case NGX_ERROR:
return NGX_ERROR;
default: /* NGX_DECLINED */
break;
}
next_filter:
r->headers_out.accept_ranges = ngx_list_push(&r->headers_out.headers);
if (r->headers_out.accept_ranges == NULL) {
return NGX_ERROR;
}
r->headers_out.accept_ranges->hash = 1;
ngx_str_set(&r->headers_out.accept_ranges->key, "Accept-Ranges");
ngx_str_set(&r->headers_out.accept_ranges->value, "bytes");
return ngx_http_next_header_filter(r);
}
static ngx_int_t
ngx_http_range_parse(ngx_http_request_t *r, ngx_http_range_filter_ctx_t *ctx,
ngx_uint_t ranges)
{
u_char *p;
off_t start, end, size, content_length, cutoff,
cutlim;
ngx_uint_t suffix;
ngx_http_range_t *range;
ngx_http_range_filter_ctx_t *mctx;
if (r != r->main) {
mctx = ngx_http_get_module_ctx(r->main,
ngx_http_range_body_filter_module);
if (mctx) {
ctx->ranges = mctx->ranges;
return NGX_OK;
}
}
if (ngx_array_init(&ctx->ranges, r->pool, 1, sizeof(ngx_http_range_t))
!= NGX_OK)
{
return NGX_ERROR;
}
p = r->headers_in.range->value.data + 6;
size = 0;
content_length = r->headers_out.content_length_n;
cutoff = NGX_MAX_OFF_T_VALUE / 10;
cutlim = NGX_MAX_OFF_T_VALUE % 10;
for ( ;; ) {
start = 0;
end = 0;
suffix = 0;
while (*p == ' ') { p++; }
if (*p != '-') {
if (*p < '0' || *p > '9') {
return NGX_HTTP_RANGE_NOT_SATISFIABLE;
}
while (*p >= '0' && *p <= '9') {
if (start >= cutoff && (start > cutoff || *p - '0' > cutlim)) {
return NGX_HTTP_RANGE_NOT_SATISFIABLE;
}
start = start * 10 + (*p++ - '0');
}
while (*p == ' ') { p++; }
if (*p++ != '-') {
return NGX_HTTP_RANGE_NOT_SATISFIABLE;
}
while (*p == ' ') { p++; }
if (*p == ',' || *p == '\0') {
end = content_length;
goto found;
}
} else {
suffix = 1;
p++;
}
if (*p < '0' || *p > '9') {
return NGX_HTTP_RANGE_NOT_SATISFIABLE;
}
while (*p >= '0' && *p <= '9') {
if (end >= cutoff && (end > cutoff || *p - '0' > cutlim)) {
return NGX_HTTP_RANGE_NOT_SATISFIABLE;
}
end = end * 10 + (*p++ - '0');
}
while (*p == ' ') { p++; }
if (*p != ',' && *p != '\0') {
return NGX_HTTP_RANGE_NOT_SATISFIABLE;
}
if (suffix) {
start = (end < content_length) ? content_length - end : 0;
end = content_length - 1;
}
if (end >= content_length) {
end = content_length;
} else {
end++;
}
found:
if (start < end) {
range = ngx_array_push(&ctx->ranges);
if (range == NULL) {
return NGX_ERROR;
}
range->start = start;
range->end = end;
if (size > NGX_MAX_OFF_T_VALUE - (end - start)) {
return NGX_HTTP_RANGE_NOT_SATISFIABLE;
}
size += end - start;
if (ranges-- == 0) {
return NGX_DECLINED;
}
} else if (start == 0) {
return NGX_DECLINED;
}
if (*p++ != ',') {
break;
}
}
if (ctx->ranges.nelts == 0) {
return NGX_HTTP_RANGE_NOT_SATISFIABLE;
}
if (size > content_length) {
return NGX_DECLINED;
}
return NGX_OK;
}
static ngx_int_t
ngx_http_range_singlepart_header(ngx_http_request_t *r,
ngx_http_range_filter_ctx_t *ctx)
{
ngx_table_elt_t *content_range;
ngx_http_range_t *range;
if (r != r->main) {
return ngx_http_next_header_filter(r);
}
content_range = ngx_list_push(&r->headers_out.headers);
if (content_range == NULL) {
return NGX_ERROR;
}
r->headers_out.content_range = content_range;
content_range->hash = 1;
ngx_str_set(&content_range->key, "Content-Range");
content_range->value.data = ngx_pnalloc(r->pool,
sizeof("bytes -/") - 1 + 3 * NGX_OFF_T_LEN);
if (content_range->value.data == NULL) {
content_range->hash = 0;
r->headers_out.content_range = NULL;
return NGX_ERROR;
}
/* "Content-Range: bytes SSSS-EEEE/TTTT" header */
range = ctx->ranges.elts;
content_range->value.len = ngx_sprintf(content_range->value.data,
"bytes %O-%O/%O",
range->start, range->end - 1,
r->headers_out.content_length_n)
- content_range->value.data;
r->headers_out.content_length_n = range->end - range->start;
r->headers_out.content_offset = range->start;
if (r->headers_out.content_length) {
r->headers_out.content_length->hash = 0;
r->headers_out.content_length = NULL;
}
return ngx_http_next_header_filter(r);
}
static ngx_int_t
ngx_http_range_multipart_header(ngx_http_request_t *r,
ngx_http_range_filter_ctx_t *ctx)
{
off_t len;
size_t size;
ngx_uint_t i;
ngx_http_range_t *range;
ngx_atomic_uint_t boundary;
size = sizeof(CRLF "--") - 1 + NGX_ATOMIC_T_LEN
+ sizeof(CRLF "Content-Type: ") - 1
+ r->headers_out.content_type.len
+ sizeof(CRLF "Content-Range: bytes ") - 1;
if (r->headers_out.content_type_len == r->headers_out.content_type.len
&& r->headers_out.charset.len)
{
size += sizeof("; charset=") - 1 + r->headers_out.charset.len;
}
ctx->boundary_header.data = ngx_pnalloc(r->pool, size);
if (ctx->boundary_header.data == NULL) {
return NGX_ERROR;
}
boundary = ngx_next_temp_number(0);
/*
* The boundary header of the range:
* CRLF
* "--0123456789" CRLF
* "Content-Type: image/jpeg" CRLF
* "Content-Range: bytes "
*/
if (r->headers_out.content_type_len == r->headers_out.content_type.len
&& r->headers_out.charset.len)
{
ctx->boundary_header.len = ngx_sprintf(ctx->boundary_header.data,
CRLF "--%0muA" CRLF
"Content-Type: %V; charset=%V" CRLF
"Content-Range: bytes ",
boundary,
&r->headers_out.content_type,
&r->headers_out.charset)
- ctx->boundary_header.data;
} else if (r->headers_out.content_type.len) {
ctx->boundary_header.len = ngx_sprintf(ctx->boundary_header.data,
CRLF "--%0muA" CRLF
"Content-Type: %V" CRLF
"Content-Range: bytes ",
boundary,
&r->headers_out.content_type)
- ctx->boundary_header.data;
} else {
ctx->boundary_header.len = ngx_sprintf(ctx->boundary_header.data,
CRLF "--%0muA" CRLF
"Content-Range: bytes ",
boundary)
- ctx->boundary_header.data;
}
r->headers_out.content_type.data =
ngx_pnalloc(r->pool,
sizeof("Content-Type: multipart/byteranges; boundary=") - 1
+ NGX_ATOMIC_T_LEN);
if (r->headers_out.content_type.data == NULL) {
return NGX_ERROR;
}
r->headers_out.content_type_lowcase = NULL;
/* "Content-Type: multipart/byteranges; boundary=0123456789" */
r->headers_out.content_type.len =
ngx_sprintf(r->headers_out.content_type.data,
"multipart/byteranges; boundary=%0muA",
boundary)
- r->headers_out.content_type.data;
r->headers_out.content_type_len = r->headers_out.content_type.len;
r->headers_out.charset.len = 0;
/* the size of the last boundary CRLF "--0123456789--" CRLF */
len = sizeof(CRLF "--") - 1 + NGX_ATOMIC_T_LEN + sizeof("--" CRLF) - 1;
range = ctx->ranges.elts;
for (i = 0; i < ctx->ranges.nelts; i++) {
/* the size of the range: "SSSS-EEEE/TTTT" CRLF CRLF */
range[i].content_range.data =
ngx_pnalloc(r->pool, 3 * NGX_OFF_T_LEN + 2 + 4);
if (range[i].content_range.data == NULL) {
return NGX_ERROR;
}
range[i].content_range.len = ngx_sprintf(range[i].content_range.data,
"%O-%O/%O" CRLF CRLF,
range[i].start, range[i].end - 1,
r->headers_out.content_length_n)
- range[i].content_range.data;
len += ctx->boundary_header.len + range[i].content_range.len
+ (range[i].end - range[i].start);
}
r->headers_out.content_length_n = len;
if (r->headers_out.content_length) {
r->headers_out.content_length->hash = 0;
r->headers_out.content_length = NULL;
}
return ngx_http_next_header_filter(r);
}
static ngx_int_t
ngx_http_range_not_satisfiable(ngx_http_request_t *r)
{
ngx_table_elt_t *content_range;
r->headers_out.status = NGX_HTTP_RANGE_NOT_SATISFIABLE;
content_range = ngx_list_push(&r->headers_out.headers);
if (content_range == NULL) {
return NGX_ERROR;
}
r->headers_out.content_range = content_range;
content_range->hash = 1;
ngx_str_set(&content_range->key, "Content-Range");
content_range->value.data = ngx_pnalloc(r->pool,
sizeof("bytes */") - 1 + NGX_OFF_T_LEN);
if (content_range->value.data == NULL) {
content_range->hash = 0;
r->headers_out.content_range = NULL;
return NGX_ERROR;
}
content_range->value.len = ngx_sprintf(content_range->value.data,
"bytes */%O",
r->headers_out.content_length_n)
- content_range->value.data;
ngx_http_clear_content_length(r);
return NGX_HTTP_RANGE_NOT_SATISFIABLE;
}
static ngx_int_t
ngx_http_range_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
ngx_http_range_filter_ctx_t *ctx;
if (in == NULL) {
return ngx_http_next_body_filter(r, in);
}
ctx = ngx_http_get_module_ctx(r, ngx_http_range_body_filter_module);
if (ctx == NULL) {
return ngx_http_next_body_filter(r, in);
}
if (ctx->ranges.nelts == 1) {
return ngx_http_range_singlepart_body(r, ctx, in);
}
/*
* multipart ranges are supported only if whole body is in a single buffer
*/
if (ngx_buf_special(in->buf)) {
return ngx_http_next_body_filter(r, in);
}
if (ngx_http_range_test_overlapped(r, ctx, in) != NGX_OK) {
return NGX_ERROR;
}
return ngx_http_range_multipart_body(r, ctx, in);
}
static ngx_int_t
ngx_http_range_test_overlapped(ngx_http_request_t *r,
ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in)
{
off_t start, last;
ngx_buf_t *buf;
ngx_uint_t i;
ngx_http_range_t *range;
if (ctx->offset) {
goto overlapped;
}
buf = in->buf;
if (!buf->last_buf) {
start = ctx->offset;
last = ctx->offset + ngx_buf_size(buf);
range = ctx->ranges.elts;
for (i = 0; i < ctx->ranges.nelts; i++) {
if (start > range[i].start || last < range[i].end) {
goto overlapped;
}
}
}
ctx->offset = ngx_buf_size(buf);
return NGX_OK;
overlapped:
ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
"range in overlapped buffers");
return NGX_ERROR;
}
static ngx_int_t
ngx_http_range_singlepart_body(ngx_http_request_t *r,
ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in)
{
off_t start, last;
ngx_buf_t *buf;
ngx_chain_t *out, *cl, **ll;
ngx_http_range_t *range;
out = NULL;
ll = &out;
range = ctx->ranges.elts;
for (cl = in; cl; cl = cl->next) {
buf = cl->buf;
start = ctx->offset;
last = ctx->offset + ngx_buf_size(buf);
ctx->offset = last;
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http range body buf: %O-%O", start, last);
if (ngx_buf_special(buf)) {
*ll = cl;
ll = &cl->next;
continue;
}
if (range->end <= start || range->start >= last) {
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http range body skip");
if (buf->in_file) {
buf->file_pos = buf->file_last;
}
buf->pos = buf->last;
buf->sync = 1;
continue;
}
if (range->start > start) {
if (buf->in_file) {
buf->file_pos += range->start - start;
}
if (ngx_buf_in_memory(buf)) {
buf->pos += (size_t) (range->start - start);
}
}
if (range->end <= last) {
if (buf->in_file) {
buf->file_last -= last - range->end;
}
if (ngx_buf_in_memory(buf)) {
buf->last -= (size_t) (last - range->end);
}
buf->last_buf = (r == r->main) ? 1 : 0;
buf->last_in_chain = 1;
*ll = cl;
cl->next = NULL;
break;
}
*ll = cl;
ll = &cl->next;
}
if (out == NULL) {
return NGX_OK;
}
return ngx_http_next_body_filter(r, out);
}
static ngx_int_t
ngx_http_range_multipart_body(ngx_http_request_t *r,
ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in)
{
ngx_buf_t *b, *buf;
ngx_uint_t i;
ngx_chain_t *out, *hcl, *rcl, *dcl, **ll;
ngx_http_range_t *range;
ll = &out;
buf = in->buf;
range = ctx->ranges.elts;
for (i = 0; i < ctx->ranges.nelts; i++) {
/*
* The boundary header of the range:
* CRLF
* "--0123456789" CRLF
* "Content-Type: image/jpeg" CRLF
* "Content-Range: bytes "
*/
b = ngx_calloc_buf(r->pool);
if (b == NULL) {
return NGX_ERROR;
}
b->memory = 1;
b->pos = ctx->boundary_header.data;
b->last = ctx->boundary_header.data + ctx->boundary_header.len;
hcl = ngx_alloc_chain_link(r->pool);
if (hcl == NULL) {
return NGX_ERROR;
}
hcl->buf = b;
/* "SSSS-EEEE/TTTT" CRLF CRLF */
b = ngx_calloc_buf(r->pool);
if (b == NULL) {
return NGX_ERROR;
}
b->temporary = 1;
b->pos = range[i].content_range.data;
b->last = range[i].content_range.data + range[i].content_range.len;
rcl = ngx_alloc_chain_link(r->pool);
if (rcl == NULL) {
return NGX_ERROR;
}
rcl->buf = b;
/* the range data */
b = ngx_calloc_buf(r->pool);
if (b == NULL) {
return NGX_ERROR;
}
b->in_file = buf->in_file;
b->temporary = buf->temporary;
b->memory = buf->memory;
b->mmap = buf->mmap;
b->file = buf->file;
if (buf->in_file) {
b->file_pos = buf->file_pos + range[i].start;
b->file_last = buf->file_pos + range[i].end;
}
if (ngx_buf_in_memory(buf)) {
b->pos = buf->pos + (size_t) range[i].start;
b->last = buf->pos + (size_t) range[i].end;
}
dcl = ngx_alloc_chain_link(r->pool);
if (dcl == NULL) {
return NGX_ERROR;
}
dcl->buf = b;
*ll = hcl;
hcl->next = rcl;
rcl->next = dcl;
ll = &dcl->next;
}
/* the last boundary CRLF "--0123456789--" CRLF */
b = ngx_calloc_buf(r->pool);
if (b == NULL) {
return NGX_ERROR;
}
b->temporary = 1;
b->last_buf = 1;
b->pos = ngx_pnalloc(r->pool, sizeof(CRLF "--") - 1 + NGX_ATOMIC_T_LEN
+ sizeof("--" CRLF) - 1);
if (b->pos == NULL) {
return NGX_ERROR;
}
b->last = ngx_cpymem(b->pos, ctx->boundary_header.data,
sizeof(CRLF "--") - 1 + NGX_ATOMIC_T_LEN);
*b->last++ = '-'; *b->last++ = '-';
*b->last++ = CR; *b->last++ = LF;
hcl = ngx_alloc_chain_link(r->pool);
if (hcl == NULL) {
return NGX_ERROR;
}
hcl->buf = b;
hcl->next = NULL;
*ll = hcl;
return ngx_http_next_body_filter(r, out);
}
static ngx_int_t
ngx_http_range_header_filter_init(ngx_conf_t *cf)
{
ngx_http_next_header_filter = ngx_http_top_header_filter;
ngx_http_top_header_filter = ngx_http_range_header_filter;
return NGX_OK;
}
static ngx_int_t
ngx_http_range_body_filter_init(ngx_conf_t *cf)
{
ngx_http_next_body_filter = ngx_http_top_body_filter;
ngx_http_top_body_filter = ngx_http_range_body_filter;
return NGX_OK;
}
nginx-1.14.0/src/http/modules/ngx_http_realip_module.c 000644 001751 001751 00000035701 13265410474 024240 0 ustar 00mdounin mdounin 000000 000000
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#include
#include
#include
#define NGX_HTTP_REALIP_XREALIP 0
#define NGX_HTTP_REALIP_XFWD 1
#define NGX_HTTP_REALIP_HEADER 2
#define NGX_HTTP_REALIP_PROXY 3
typedef struct {
ngx_array_t *from; /* array of ngx_cidr_t */
ngx_uint_t type;
ngx_uint_t hash;
ngx_str_t header;
ngx_flag_t recursive;
} ngx_http_realip_loc_conf_t;
typedef struct {
ngx_connection_t *connection;
struct sockaddr *sockaddr;
socklen_t socklen;
ngx_str_t addr_text;
} ngx_http_realip_ctx_t;
static ngx_int_t ngx_http_realip_handler(ngx_http_request_t *r);
static ngx_int_t ngx_http_realip_set_addr(ngx_http_request_t *r,
ngx_addr_t *addr);
static void ngx_http_realip_cleanup(void *data);
static char *ngx_http_realip_from(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static char *ngx_http_realip(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static void *ngx_http_realip_create_loc_conf(ngx_conf_t *cf);
static char *ngx_http_realip_merge_loc_conf(ngx_conf_t *cf,
void *parent, void *child);
static ngx_int_t ngx_http_realip_add_variables(ngx_conf_t *cf);
static ngx_int_t ngx_http_realip_init(ngx_conf_t *cf);
static ngx_http_realip_ctx_t *ngx_http_realip_get_module_ctx(
ngx_http_request_t *r);
static ngx_int_t ngx_http_realip_remote_addr_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_realip_remote_port_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_command_t ngx_http_realip_commands[] = {
{ ngx_string("set_real_ip_from"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_http_realip_from,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
{ ngx_string("real_ip_header"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_http_realip,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
{ ngx_string("real_ip_recursive"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_realip_loc_conf_t, recursive),
NULL },
ngx_null_command
};
static ngx_http_module_t ngx_http_realip_module_ctx = {
ngx_http_realip_add_variables, /* preconfiguration */
ngx_http_realip_init, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
ngx_http_realip_create_loc_conf, /* create location configuration */
ngx_http_realip_merge_loc_conf /* merge location configuration */
};
ngx_module_t ngx_http_realip_module = {
NGX_MODULE_V1,
&ngx_http_realip_module_ctx, /* module context */
ngx_http_realip_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static ngx_http_variable_t ngx_http_realip_vars[] = {
{ ngx_string("realip_remote_addr"), NULL,
ngx_http_realip_remote_addr_variable, 0, 0, 0 },
{ ngx_string("realip_remote_port"), NULL,
ngx_http_realip_remote_port_variable, 0, 0, 0 },
ngx_http_null_variable
};
static ngx_int_t
ngx_http_realip_handler(ngx_http_request_t *r)
{
u_char *p;
size_t len;
ngx_str_t *value;
ngx_uint_t i, hash;
ngx_addr_t addr;
ngx_array_t *xfwd;
ngx_list_part_t *part;
ngx_table_elt_t *header;
ngx_connection_t *c;
ngx_http_realip_ctx_t *ctx;
ngx_http_realip_loc_conf_t *rlcf;
rlcf = ngx_http_get_module_loc_conf(r, ngx_http_realip_module);
if (rlcf->from == NULL) {
return NGX_DECLINED;
}
ctx = ngx_http_realip_get_module_ctx(r);
if (ctx) {
return NGX_DECLINED;
}
switch (rlcf->type) {
case NGX_HTTP_REALIP_XREALIP:
if (r->headers_in.x_real_ip == NULL) {
return NGX_DECLINED;
}
value = &r->headers_in.x_real_ip->value;
xfwd = NULL;
break;
case NGX_HTTP_REALIP_XFWD:
xfwd = &r->headers_in.x_forwarded_for;
if (xfwd->elts == NULL) {
return NGX_DECLINED;
}
value = NULL;
break;
case NGX_HTTP_REALIP_PROXY:
value = &r->connection->proxy_protocol_addr;
if (value->len == 0) {
return NGX_DECLINED;
}
xfwd = NULL;
break;
default: /* NGX_HTTP_REALIP_HEADER */
part = &r->headers_in.headers.part;
header = part->elts;
hash = rlcf->hash;
len = rlcf->header.len;
p = rlcf->header.data;
for (i = 0; /* void */ ; i++) {
if (i >= part->nelts) {
if (part->next == NULL) {
break;
}
part = part->next;
header = part->elts;
i = 0;
}
if (hash == header[i].hash
&& len == header[i].key.len
&& ngx_strncmp(p, header[i].lowcase_key, len) == 0)
{
value = &header[i].value;
xfwd = NULL;
goto found;
}
}
return NGX_DECLINED;
}
found:
c = r->connection;
addr.sockaddr = c->sockaddr;
addr.socklen = c->socklen;
/* addr.name = c->addr_text; */
if (ngx_http_get_forwarded_addr(r, &addr, xfwd, value, rlcf->from,
rlcf->recursive)
!= NGX_DECLINED)
{
if (rlcf->type == NGX_HTTP_REALIP_PROXY) {
ngx_inet_set_port(addr.sockaddr, c->proxy_protocol_port);
}
return ngx_http_realip_set_addr(r, &addr);
}
return NGX_DECLINED;
}
static ngx_int_t
ngx_http_realip_set_addr(ngx_http_request_t *r, ngx_addr_t *addr)
{
size_t len;
u_char *p;
u_char text[NGX_SOCKADDR_STRLEN];
ngx_connection_t *c;
ngx_pool_cleanup_t *cln;
ngx_http_realip_ctx_t *ctx;
cln = ngx_pool_cleanup_add(r->pool, sizeof(ngx_http_realip_ctx_t));
if (cln == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
ctx = cln->data;
c = r->connection;
len = ngx_sock_ntop(addr->sockaddr, addr->socklen, text,
NGX_SOCKADDR_STRLEN, 0);
if (len == 0) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
p = ngx_pnalloc(c->pool, len);
if (p == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
ngx_memcpy(p, text, len);
cln->handler = ngx_http_realip_cleanup;
ngx_http_set_ctx(r, ctx, ngx_http_realip_module);
ctx->connection = c;
ctx->sockaddr = c->sockaddr;
ctx->socklen = c->socklen;
ctx->addr_text = c->addr_text;
c->sockaddr = addr->sockaddr;
c->socklen = addr->socklen;
c->addr_text.len = len;
c->addr_text.data = p;
return NGX_DECLINED;
}
static void
ngx_http_realip_cleanup(void *data)
{
ngx_http_realip_ctx_t *ctx = data;
ngx_connection_t *c;
c = ctx->connection;
c->sockaddr = ctx->sockaddr;
c->socklen = ctx->socklen;
c->addr_text = ctx->addr_text;
}
static char *
ngx_http_realip_from(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_realip_loc_conf_t *rlcf = conf;
ngx_int_t rc;
ngx_str_t *value;
ngx_url_t u;
ngx_cidr_t c, *cidr;
ngx_uint_t i;
struct sockaddr_in *sin;
#if (NGX_HAVE_INET6)
struct sockaddr_in6 *sin6;
#endif
value = cf->args->elts;
if (rlcf->from == NULL) {
rlcf->from = ngx_array_create(cf->pool, 2,
sizeof(ngx_cidr_t));
if (rlcf->from == NULL) {
return NGX_CONF_ERROR;
}
}
#if (NGX_HAVE_UNIX_DOMAIN)
if (ngx_strcmp(value[1].data, "unix:") == 0) {
cidr = ngx_array_push(rlcf->from);
if (cidr == NULL) {
return NGX_CONF_ERROR;
}
cidr->family = AF_UNIX;
return NGX_CONF_OK;
}
#endif
rc = ngx_ptocidr(&value[1], &c);
if (rc != NGX_ERROR) {
if (rc == NGX_DONE) {
ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
"low address bits of %V are meaningless",
&value[1]);
}
cidr = ngx_array_push(rlcf->from);
if (cidr == NULL) {
return NGX_CONF_ERROR;
}
*cidr = c;
return NGX_CONF_OK;
}
ngx_memzero(&u, sizeof(ngx_url_t));
u.host = value[1];
if (ngx_inet_resolve_host(cf->pool, &u) != NGX_OK) {
if (u.err) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"%s in set_real_ip_from \"%V\"",
u.err, &u.host);
}
return NGX_CONF_ERROR;
}
cidr = ngx_array_push_n(rlcf->from, u.naddrs);
if (cidr == NULL) {
return NGX_CONF_ERROR;
}
ngx_memzero(cidr, u.naddrs * sizeof(ngx_cidr_t));
for (i = 0; i < u.naddrs; i++) {
cidr[i].family = u.addrs[i].sockaddr->sa_family;
switch (cidr[i].family) {
#if (NGX_HAVE_INET6)
case AF_INET6:
sin6 = (struct sockaddr_in6 *) u.addrs[i].sockaddr;
cidr[i].u.in6.addr = sin6->sin6_addr;
ngx_memset(cidr[i].u.in6.mask.s6_addr, 0xff, 16);
break;
#endif
default: /* AF_INET */
sin = (struct sockaddr_in *) u.addrs[i].sockaddr;
cidr[i].u.in.addr = sin->sin_addr.s_addr;
cidr[i].u.in.mask = 0xffffffff;
break;
}
}
return NGX_CONF_OK;
}
static char *
ngx_http_realip(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_realip_loc_conf_t *rlcf = conf;
ngx_str_t *value;
if (rlcf->type != NGX_CONF_UNSET_UINT) {
return "is duplicate";
}
value = cf->args->elts;
if (ngx_strcmp(value[1].data, "X-Real-IP") == 0) {
rlcf->type = NGX_HTTP_REALIP_XREALIP;
return NGX_CONF_OK;
}
if (ngx_strcmp(value[1].data, "X-Forwarded-For") == 0) {
rlcf->type = NGX_HTTP_REALIP_XFWD;
return NGX_CONF_OK;
}
if (ngx_strcmp(value[1].data, "proxy_protocol") == 0) {
rlcf->type = NGX_HTTP_REALIP_PROXY;
return NGX_CONF_OK;
}
rlcf->type = NGX_HTTP_REALIP_HEADER;
rlcf->hash = ngx_hash_strlow(value[1].data, value[1].data, value[1].len);
rlcf->header = value[1];
return NGX_CONF_OK;
}
static void *
ngx_http_realip_create_loc_conf(ngx_conf_t *cf)
{
ngx_http_realip_loc_conf_t *conf;
conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_realip_loc_conf_t));
if (conf == NULL) {
return NULL;
}
/*
* set by ngx_pcalloc():
*
* conf->from = NULL;
* conf->hash = 0;
* conf->header = { 0, NULL };
*/
conf->type = NGX_CONF_UNSET_UINT;
conf->recursive = NGX_CONF_UNSET;
return conf;
}
static char *
ngx_http_realip_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
{
ngx_http_realip_loc_conf_t *prev = parent;
ngx_http_realip_loc_conf_t *conf = child;
if (conf->from == NULL) {
conf->from = prev->from;
}
ngx_conf_merge_uint_value(conf->type, prev->type, NGX_HTTP_REALIP_XREALIP);
ngx_conf_merge_value(conf->recursive, prev->recursive, 0);
if (conf->header.len == 0) {
conf->hash = prev->hash;
conf->header = prev->header;
}
return NGX_CONF_OK;
}
static ngx_int_t
ngx_http_realip_add_variables(ngx_conf_t *cf)
{
ngx_http_variable_t *var, *v;
for (v = ngx_http_realip_vars; v->name.len; v++) {
var = ngx_http_add_variable(cf, &v->name, v->flags);
if (var == NULL) {
return NGX_ERROR;
}
var->get_handler = v->get_handler;
var->data = v->data;
}
return NGX_OK;
}
static ngx_int_t
ngx_http_realip_init(ngx_conf_t *cf)
{
ngx_http_handler_pt *h;
ngx_http_core_main_conf_t *cmcf;
cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
h = ngx_array_push(&cmcf->phases[NGX_HTTP_POST_READ_PHASE].handlers);
if (h == NULL) {
return NGX_ERROR;
}
*h = ngx_http_realip_handler;
h = ngx_array_push(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers);
if (h == NULL) {
return NGX_ERROR;
}
*h = ngx_http_realip_handler;
return NGX_OK;
}
static ngx_http_realip_ctx_t *
ngx_http_realip_get_module_ctx(ngx_http_request_t *r)
{
ngx_pool_cleanup_t *cln;
ngx_http_realip_ctx_t *ctx;
ctx = ngx_http_get_module_ctx(r, ngx_http_realip_module);
if (ctx == NULL && (r->internal || r->filter_finalize)) {
/*
* if module context was reset, the original address
* can still be found in the cleanup handler
*/
for (cln = r->pool->cleanup; cln; cln = cln->next) {
if (cln->handler == ngx_http_realip_cleanup) {
ctx = cln->data;
break;
}
}
}
return ctx;
}
static ngx_int_t
ngx_http_realip_remote_addr_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
ngx_str_t *addr_text;
ngx_http_realip_ctx_t *ctx;
ctx = ngx_http_realip_get_module_ctx(r);
addr_text = ctx ? &ctx->addr_text : &r->connection->addr_text;
v->len = addr_text->len;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = addr_text->data;
return NGX_OK;
}
static ngx_int_t
ngx_http_realip_remote_port_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
ngx_uint_t port;
struct sockaddr *sa;
ngx_http_realip_ctx_t *ctx;
ctx = ngx_http_realip_get_module_ctx(r);
sa = ctx ? ctx->sockaddr : r->connection->sockaddr;
v->len = 0;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = ngx_pnalloc(r->pool, sizeof("65535") - 1);
if (v->data == NULL) {
return NGX_ERROR;
}
port = ngx_inet_get_port(sa);
if (port > 0 && port < 65536) {
v->len = ngx_sprintf(v->data, "%ui", port) - v->data;
}
return NGX_OK;
}
nginx-1.14.0/src/http/modules/ngx_http_referer_module.c 000644 001751 001751 00000041751 13265410474 024420 0 ustar 00mdounin mdounin 000000 000000
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#include
#include
#include
#define NGX_HTTP_REFERER_NO_URI_PART ((void *) 4)
typedef struct {
ngx_hash_combined_t hash;
#if (NGX_PCRE)
ngx_array_t *regex;
ngx_array_t *server_name_regex;
#endif
ngx_flag_t no_referer;
ngx_flag_t blocked_referer;
ngx_flag_t server_names;
ngx_hash_keys_arrays_t *keys;
ngx_uint_t referer_hash_max_size;
ngx_uint_t referer_hash_bucket_size;
} ngx_http_referer_conf_t;
static ngx_int_t ngx_http_referer_add_variables(ngx_conf_t *cf);
static void * ngx_http_referer_create_conf(ngx_conf_t *cf);
static char * ngx_http_referer_merge_conf(ngx_conf_t *cf, void *parent,
void *child);
static char *ngx_http_valid_referers(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static ngx_int_t ngx_http_add_referer(ngx_conf_t *cf,
ngx_hash_keys_arrays_t *keys, ngx_str_t *value, ngx_str_t *uri);
static ngx_int_t ngx_http_add_regex_referer(ngx_conf_t *cf,
ngx_http_referer_conf_t *rlcf, ngx_str_t *name);
#if (NGX_PCRE)
static ngx_int_t ngx_http_add_regex_server_name(ngx_conf_t *cf,
ngx_http_referer_conf_t *rlcf, ngx_http_regex_t *regex);
#endif
static int ngx_libc_cdecl ngx_http_cmp_referer_wildcards(const void *one,
const void *two);
static ngx_command_t ngx_http_referer_commands[] = {
{ ngx_string("valid_referers"),
NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
ngx_http_valid_referers,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
{ ngx_string("referer_hash_max_size"),
NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_num_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_referer_conf_t, referer_hash_max_size),
NULL },
{ ngx_string("referer_hash_bucket_size"),
NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_num_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_referer_conf_t, referer_hash_bucket_size),
NULL },
ngx_null_command
};
static ngx_http_module_t ngx_http_referer_module_ctx = {
ngx_http_referer_add_variables, /* preconfiguration */
NULL, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
ngx_http_referer_create_conf, /* create location configuration */
ngx_http_referer_merge_conf /* merge location configuration */
};
ngx_module_t ngx_http_referer_module = {
NGX_MODULE_V1,
&ngx_http_referer_module_ctx, /* module context */
ngx_http_referer_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static ngx_str_t ngx_http_invalid_referer_name = ngx_string("invalid_referer");
static ngx_int_t
ngx_http_referer_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
uintptr_t data)
{
u_char *p, *ref, *last;
size_t len;
ngx_str_t *uri;
ngx_uint_t i, key;
ngx_http_referer_conf_t *rlcf;
u_char buf[256];
#if (NGX_PCRE)
ngx_int_t rc;
ngx_str_t referer;
#endif
rlcf = ngx_http_get_module_loc_conf(r, ngx_http_referer_module);
if (rlcf->hash.hash.buckets == NULL
&& rlcf->hash.wc_head == NULL
&& rlcf->hash.wc_tail == NULL
#if (NGX_PCRE)
&& rlcf->regex == NULL
&& rlcf->server_name_regex == NULL
#endif
)
{
goto valid;
}
if (r->headers_in.referer == NULL) {
if (rlcf->no_referer) {
goto valid;
}
goto invalid;
}
len = r->headers_in.referer->value.len;
ref = r->headers_in.referer->value.data;
if (len >= sizeof("http://i.ru") - 1) {
last = ref + len;
if (ngx_strncasecmp(ref, (u_char *) "http://", 7) == 0) {
ref += 7;
len -= 7;
goto valid_scheme;
} else if (ngx_strncasecmp(ref, (u_char *) "https://", 8) == 0) {
ref += 8;
len -= 8;
goto valid_scheme;
}
}
if (rlcf->blocked_referer) {
goto valid;
}
goto invalid;
valid_scheme:
i = 0;
key = 0;
for (p = ref; p < last; p++) {
if (*p == '/' || *p == ':') {
break;
}
if (i == 256) {
goto invalid;
}
buf[i] = ngx_tolower(*p);
key = ngx_hash(key, buf[i++]);
}
uri = ngx_hash_find_combined(&rlcf->hash, key, buf, p - ref);
if (uri) {
goto uri;
}
#if (NGX_PCRE)
if (rlcf->server_name_regex) {
referer.len = p - ref;
referer.data = buf;
rc = ngx_regex_exec_array(rlcf->server_name_regex, &referer,
r->connection->log);
if (rc == NGX_OK) {
goto valid;
}
if (rc == NGX_ERROR) {
return rc;
}
/* NGX_DECLINED */
}
if (rlcf->regex) {
referer.len = len;
referer.data = ref;
rc = ngx_regex_exec_array(rlcf->regex, &referer, r->connection->log);
if (rc == NGX_OK) {
goto valid;
}
if (rc == NGX_ERROR) {
return rc;
}
/* NGX_DECLINED */
}
#endif
invalid:
*v = ngx_http_variable_true_value;
return NGX_OK;
uri:
for ( /* void */ ; p < last; p++) {
if (*p == '/') {
break;
}
}
len = last - p;
if (uri == NGX_HTTP_REFERER_NO_URI_PART) {
goto valid;
}
if (len < uri->len || ngx_strncmp(uri->data, p, uri->len) != 0) {
goto invalid;
}
valid:
*v = ngx_http_variable_null_value;
return NGX_OK;
}
static ngx_int_t
ngx_http_referer_add_variables(ngx_conf_t *cf)
{
ngx_http_variable_t *var;
var = ngx_http_add_variable(cf, &ngx_http_invalid_referer_name,
NGX_HTTP_VAR_CHANGEABLE);
if (var == NULL) {
return NGX_ERROR;
}
var->get_handler = ngx_http_referer_variable;
return NGX_OK;
}
static void *
ngx_http_referer_create_conf(ngx_conf_t *cf)
{
ngx_http_referer_conf_t *conf;
conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_referer_conf_t));
if (conf == NULL) {
return NULL;
}
/*
* set by ngx_pcalloc():
*
* conf->hash = { NULL };
* conf->server_names = 0;
* conf->keys = NULL;
*/
#if (NGX_PCRE)
conf->regex = NGX_CONF_UNSET_PTR;
conf->server_name_regex = NGX_CONF_UNSET_PTR;
#endif
conf->no_referer = NGX_CONF_UNSET;
conf->blocked_referer = NGX_CONF_UNSET;
conf->referer_hash_max_size = NGX_CONF_UNSET_UINT;
conf->referer_hash_bucket_size = NGX_CONF_UNSET_UINT;
return conf;
}
static char *
ngx_http_referer_merge_conf(ngx_conf_t *cf, void *parent, void *child)
{
ngx_http_referer_conf_t *prev = parent;
ngx_http_referer_conf_t *conf = child;
ngx_uint_t n;
ngx_hash_init_t hash;
ngx_http_server_name_t *sn;
ngx_http_core_srv_conf_t *cscf;
if (conf->keys == NULL) {
conf->hash = prev->hash;
#if (NGX_PCRE)
ngx_conf_merge_ptr_value(conf->regex, prev->regex, NULL);
ngx_conf_merge_ptr_value(conf->server_name_regex,
prev->server_name_regex, NULL);
#endif
ngx_conf_merge_value(conf->no_referer, prev->no_referer, 0);
ngx_conf_merge_value(conf->blocked_referer, prev->blocked_referer, 0);
ngx_conf_merge_uint_value(conf->referer_hash_max_size,
prev->referer_hash_max_size, 2048);
ngx_conf_merge_uint_value(conf->referer_hash_bucket_size,
prev->referer_hash_bucket_size, 64);
return NGX_CONF_OK;
}
if (conf->server_names == 1) {
cscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_core_module);
sn = cscf->server_names.elts;
for (n = 0; n < cscf->server_names.nelts; n++) {
#if (NGX_PCRE)
if (sn[n].regex) {
if (ngx_http_add_regex_server_name(cf, conf, sn[n].regex)
!= NGX_OK)
{
return NGX_CONF_ERROR;
}
continue;
}
#endif
if (ngx_http_add_referer(cf, conf->keys, &sn[n].name, NULL)
!= NGX_OK)
{
return NGX_CONF_ERROR;
}
}
}
if ((conf->no_referer == 1 || conf->blocked_referer == 1)
&& conf->keys->keys.nelts == 0
&& conf->keys->dns_wc_head.nelts == 0
&& conf->keys->dns_wc_tail.nelts == 0)
{
ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
"the \"none\" or \"blocked\" referers are specified "
"in the \"valid_referers\" directive "
"without any valid referer");
return NGX_CONF_ERROR;
}
ngx_conf_merge_uint_value(conf->referer_hash_max_size,
prev->referer_hash_max_size, 2048);
ngx_conf_merge_uint_value(conf->referer_hash_bucket_size,
prev->referer_hash_bucket_size, 64);
conf->referer_hash_bucket_size = ngx_align(conf->referer_hash_bucket_size,
ngx_cacheline_size);
hash.key = ngx_hash_key_lc;
hash.max_size = conf->referer_hash_max_size;
hash.bucket_size = conf->referer_hash_bucket_size;
hash.name = "referer_hash";
hash.pool = cf->pool;
if (conf->keys->keys.nelts) {
hash.hash = &conf->hash.hash;
hash.temp_pool = NULL;
if (ngx_hash_init(&hash, conf->keys->keys.elts, conf->keys->keys.nelts)
!= NGX_OK)
{
return NGX_CONF_ERROR;
}
}
if (conf->keys->dns_wc_head.nelts) {
ngx_qsort(conf->keys->dns_wc_head.elts,
(size_t) conf->keys->dns_wc_head.nelts,
sizeof(ngx_hash_key_t),
ngx_http_cmp_referer_wildcards);
hash.hash = NULL;
hash.temp_pool = cf->temp_pool;
if (ngx_hash_wildcard_init(&hash, conf->keys->dns_wc_head.elts,
conf->keys->dns_wc_head.nelts)
!= NGX_OK)
{
return NGX_CONF_ERROR;
}
conf->hash.wc_head = (ngx_hash_wildcard_t *) hash.hash;
}
if (conf->keys->dns_wc_tail.nelts) {
ngx_qsort(conf->keys->dns_wc_tail.elts,
(size_t) conf->keys->dns_wc_tail.nelts,
sizeof(ngx_hash_key_t),
ngx_http_cmp_referer_wildcards);
hash.hash = NULL;
hash.temp_pool = cf->temp_pool;
if (ngx_hash_wildcard_init(&hash, conf->keys->dns_wc_tail.elts,
conf->keys->dns_wc_tail.nelts)
!= NGX_OK)
{
return NGX_CONF_ERROR;
}
conf->hash.wc_tail = (ngx_hash_wildcard_t *) hash.hash;
}
#if (NGX_PCRE)
ngx_conf_merge_ptr_value(conf->regex, prev->regex, NULL);
ngx_conf_merge_ptr_value(conf->server_name_regex, prev->server_name_regex,
NULL);
#endif
if (conf->no_referer == NGX_CONF_UNSET) {
conf->no_referer = 0;
}
if (conf->blocked_referer == NGX_CONF_UNSET) {
conf->blocked_referer = 0;
}
conf->keys = NULL;
return NGX_CONF_OK;
}
static char *
ngx_http_valid_referers(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_referer_conf_t *rlcf = conf;
u_char *p;
ngx_str_t *value, uri;
ngx_uint_t i;
if (rlcf->keys == NULL) {
rlcf->keys = ngx_pcalloc(cf->temp_pool, sizeof(ngx_hash_keys_arrays_t));
if (rlcf->keys == NULL) {
return NGX_CONF_ERROR;
}
rlcf->keys->pool = cf->pool;
rlcf->keys->temp_pool = cf->pool;
if (ngx_hash_keys_array_init(rlcf->keys, NGX_HASH_SMALL) != NGX_OK) {
return NGX_CONF_ERROR;
}
}
value = cf->args->elts;
for (i = 1; i < cf->args->nelts; i++) {
if (value[i].len == 0) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid referer \"%V\"", &value[i]);
return NGX_CONF_ERROR;
}
if (ngx_strcmp(value[i].data, "none") == 0) {
rlcf->no_referer = 1;
continue;
}
if (ngx_strcmp(value[i].data, "blocked") == 0) {
rlcf->blocked_referer = 1;
continue;
}
if (ngx_strcmp(value[i].data, "server_names") == 0) {
rlcf->server_names = 1;
continue;
}
if (value[i].data[0] == '~') {
if (ngx_http_add_regex_referer(cf, rlcf, &value[i]) != NGX_OK) {
return NGX_CONF_ERROR;
}
continue;
}
ngx_str_null(&uri);
p = (u_char *) ngx_strchr(value[i].data, '/');
if (p) {
uri.len = (value[i].data + value[i].len) - p;
uri.data = p;
value[i].len = p - value[i].data;
}
if (ngx_http_add_referer(cf, rlcf->keys, &value[i], &uri) != NGX_OK) {
return NGX_CONF_ERROR;
}
}
return NGX_CONF_OK;
}
static ngx_int_t
ngx_http_add_referer(ngx_conf_t *cf, ngx_hash_keys_arrays_t *keys,
ngx_str_t *value, ngx_str_t *uri)
{
ngx_int_t rc;
ngx_str_t *u;
if (uri == NULL || uri->len == 0) {
u = NGX_HTTP_REFERER_NO_URI_PART;
} else {
u = ngx_palloc(cf->pool, sizeof(ngx_str_t));
if (u == NULL) {
return NGX_ERROR;
}
*u = *uri;
}
rc = ngx_hash_add_key(keys, value, u, NGX_HASH_WILDCARD_KEY);
if (rc == NGX_OK) {
return NGX_OK;
}
if (rc == NGX_DECLINED) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid hostname or wildcard \"%V\"", value);
}
if (rc == NGX_BUSY) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"conflicting parameter \"%V\"", value);
}
return NGX_ERROR;
}
static ngx_int_t
ngx_http_add_regex_referer(ngx_conf_t *cf, ngx_http_referer_conf_t *rlcf,
ngx_str_t *name)
{
#if (NGX_PCRE)
ngx_regex_elt_t *re;
ngx_regex_compile_t rc;
u_char errstr[NGX_MAX_CONF_ERRSTR];
if (name->len == 1) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "empty regex in \"%V\"", name);
return NGX_ERROR;
}
if (rlcf->regex == NGX_CONF_UNSET_PTR) {
rlcf->regex = ngx_array_create(cf->pool, 2, sizeof(ngx_regex_elt_t));
if (rlcf->regex == NULL) {
return NGX_ERROR;
}
}
re = ngx_array_push(rlcf->regex);
if (re == NULL) {
return NGX_ERROR;
}
name->len--;
name->data++;
ngx_memzero(&rc, sizeof(ngx_regex_compile_t));
rc.pattern = *name;
rc.pool = cf->pool;
rc.options = NGX_REGEX_CASELESS;
rc.err.len = NGX_MAX_CONF_ERRSTR;
rc.err.data = errstr;
if (ngx_regex_compile(&rc) != NGX_OK) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%V", &rc.err);
return NGX_ERROR;
}
re->regex = rc.regex;
re->name = name->data;
return NGX_OK;
#else
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"the using of the regex \"%V\" requires PCRE library",
name);
return NGX_ERROR;
#endif
}
#if (NGX_PCRE)
static ngx_int_t
ngx_http_add_regex_server_name(ngx_conf_t *cf, ngx_http_referer_conf_t *rlcf,
ngx_http_regex_t *regex)
{
ngx_regex_elt_t *re;
if (rlcf->server_name_regex == NGX_CONF_UNSET_PTR) {
rlcf->server_name_regex = ngx_array_create(cf->pool, 2,
sizeof(ngx_regex_elt_t));
if (rlcf->server_name_regex == NULL) {
return NGX_ERROR;
}
}
re = ngx_array_push(rlcf->server_name_regex);
if (re == NULL) {
return NGX_ERROR;
}
re->regex = regex->regex;
re->name = regex->name.data;
return NGX_OK;
}
#endif
static int ngx_libc_cdecl
ngx_http_cmp_referer_wildcards(const void *one, const void *two)
{
ngx_hash_key_t *first, *second;
first = (ngx_hash_key_t *) one;
second = (ngx_hash_key_t *) two;
return ngx_dns_strcmp(first->key.data, second->key.data);
}
nginx-1.14.0/src/http/modules/ngx_http_rewrite_module.c 000644 001751 001751 00000067330 13265410474 024450 0 ustar 00mdounin mdounin 000000 000000
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#include
#include
#include
typedef struct {
ngx_array_t *codes; /* uintptr_t */
ngx_uint_t stack_size;
ngx_flag_t log;
ngx_flag_t uninitialized_variable_warn;
} ngx_http_rewrite_loc_conf_t;
static void *ngx_http_rewrite_create_loc_conf(ngx_conf_t *cf);
static char *ngx_http_rewrite_merge_loc_conf(ngx_conf_t *cf,
void *parent, void *child);
static ngx_int_t ngx_http_rewrite_init(ngx_conf_t *cf);
static char *ngx_http_rewrite(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static char *ngx_http_rewrite_return(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static char *ngx_http_rewrite_break(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static char *ngx_http_rewrite_if(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static char * ngx_http_rewrite_if_condition(ngx_conf_t *cf,
ngx_http_rewrite_loc_conf_t *lcf);
static char *ngx_http_rewrite_variable(ngx_conf_t *cf,
ngx_http_rewrite_loc_conf_t *lcf, ngx_str_t *value);
static char *ngx_http_rewrite_set(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static char * ngx_http_rewrite_value(ngx_conf_t *cf,
ngx_http_rewrite_loc_conf_t *lcf, ngx_str_t *value);
static ngx_command_t ngx_http_rewrite_commands[] = {
{ ngx_string("rewrite"),
NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
|NGX_CONF_TAKE23,
ngx_http_rewrite,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
{ ngx_string("return"),
NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
|NGX_CONF_TAKE12,
ngx_http_rewrite_return,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
{ ngx_string("break"),
NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
|NGX_CONF_NOARGS,
ngx_http_rewrite_break,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
{ ngx_string("if"),
NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_BLOCK|NGX_CONF_1MORE,
ngx_http_rewrite_if,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
{ ngx_string("set"),
NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
|NGX_CONF_TAKE2,
ngx_http_rewrite_set,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
{ ngx_string("rewrite_log"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF|NGX_HTTP_LOC_CONF
|NGX_HTTP_LIF_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_rewrite_loc_conf_t, log),
NULL },
{ ngx_string("uninitialized_variable_warn"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF|NGX_HTTP_LOC_CONF
|NGX_HTTP_LIF_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_rewrite_loc_conf_t, uninitialized_variable_warn),
NULL },
ngx_null_command
};
static ngx_http_module_t ngx_http_rewrite_module_ctx = {
NULL, /* preconfiguration */
ngx_http_rewrite_init, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
ngx_http_rewrite_create_loc_conf, /* create location configuration */
ngx_http_rewrite_merge_loc_conf /* merge location configuration */
};
ngx_module_t ngx_http_rewrite_module = {
NGX_MODULE_V1,
&ngx_http_rewrite_module_ctx, /* module context */
ngx_http_rewrite_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static ngx_int_t
ngx_http_rewrite_handler(ngx_http_request_t *r)
{
ngx_int_t index;
ngx_http_script_code_pt code;
ngx_http_script_engine_t *e;
ngx_http_core_srv_conf_t *cscf;
ngx_http_core_main_conf_t *cmcf;
ngx_http_rewrite_loc_conf_t *rlcf;
cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
index = cmcf->phase_engine.location_rewrite_index;
if (r->phase_handler == index && r->loc_conf == cscf->ctx->loc_conf) {
/* skipping location rewrite phase for server null location */
return NGX_DECLINED;
}
rlcf = ngx_http_get_module_loc_conf(r, ngx_http_rewrite_module);
if (rlcf->codes == NULL) {
return NGX_DECLINED;
}
e = ngx_pcalloc(r->pool, sizeof(ngx_http_script_engine_t));
if (e == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
e->sp = ngx_pcalloc(r->pool,
rlcf->stack_size * sizeof(ngx_http_variable_value_t));
if (e->sp == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
e->ip = rlcf->codes->elts;
e->request = r;
e->quote = 1;
e->log = rlcf->log;
e->status = NGX_DECLINED;
while (*(uintptr_t *) e->ip) {
code = *(ngx_http_script_code_pt *) e->ip;
code(e);
}
if (e->status < NGX_HTTP_BAD_REQUEST) {
return e->status;
}
if (r->err_status == 0) {
return e->status;
}
return r->err_status;
}
static ngx_int_t
ngx_http_rewrite_var(ngx_http_request_t *r, ngx_http_variable_value_t *v,
uintptr_t data)
{
ngx_http_variable_t *var;
ngx_http_core_main_conf_t *cmcf;
ngx_http_rewrite_loc_conf_t *rlcf;
rlcf = ngx_http_get_module_loc_conf(r, ngx_http_rewrite_module);
if (rlcf->uninitialized_variable_warn == 0) {
*v = ngx_http_variable_null_value;
return NGX_OK;
}
cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
var = cmcf->variables.elts;
/*
* the ngx_http_rewrite_module sets variables directly in r->variables,
* and they should be handled by ngx_http_get_indexed_variable(),
* so the handler is called only if the variable is not initialized
*/
ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,
"using uninitialized \"%V\" variable", &var[data].name);
*v = ngx_http_variable_null_value;
return NGX_OK;
}
static void *
ngx_http_rewrite_create_loc_conf(ngx_conf_t *cf)
{
ngx_http_rewrite_loc_conf_t *conf;
conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_rewrite_loc_conf_t));
if (conf == NULL) {
return NULL;
}
conf->stack_size = NGX_CONF_UNSET_UINT;
conf->log = NGX_CONF_UNSET;
conf->uninitialized_variable_warn = NGX_CONF_UNSET;
return conf;
}
static char *
ngx_http_rewrite_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
{
ngx_http_rewrite_loc_conf_t *prev = parent;
ngx_http_rewrite_loc_conf_t *conf = child;
uintptr_t *code;
ngx_conf_merge_value(conf->log, prev->log, 0);
ngx_conf_merge_value(conf->uninitialized_variable_warn,
prev->uninitialized_variable_warn, 1);
ngx_conf_merge_uint_value(conf->stack_size, prev->stack_size, 10);
if (conf->codes == NULL) {
return NGX_CONF_OK;
}
if (conf->codes == prev->codes) {
return NGX_CONF_OK;
}
code = ngx_array_push_n(conf->codes, sizeof(uintptr_t));
if (code == NULL) {
return NGX_CONF_ERROR;
}
*code = (uintptr_t) NULL;
return NGX_CONF_OK;
}
static ngx_int_t
ngx_http_rewrite_init(ngx_conf_t *cf)
{
ngx_http_handler_pt *h;
ngx_http_core_main_conf_t *cmcf;
cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
h = ngx_array_push(&cmcf->phases[NGX_HTTP_SERVER_REWRITE_PHASE].handlers);
if (h == NULL) {
return NGX_ERROR;
}
*h = ngx_http_rewrite_handler;
h = ngx_array_push(&cmcf->phases[NGX_HTTP_REWRITE_PHASE].handlers);
if (h == NULL) {
return NGX_ERROR;
}
*h = ngx_http_rewrite_handler;
return NGX_OK;
}
static char *
ngx_http_rewrite(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_rewrite_loc_conf_t *lcf = conf;
ngx_str_t *value;
ngx_uint_t last;
ngx_regex_compile_t rc;
ngx_http_script_code_pt *code;
ngx_http_script_compile_t sc;
ngx_http_script_regex_code_t *regex;
ngx_http_script_regex_end_code_t *regex_end;
u_char errstr[NGX_MAX_CONF_ERRSTR];
regex = ngx_http_script_start_code(cf->pool, &lcf->codes,
sizeof(ngx_http_script_regex_code_t));
if (regex == NULL) {
return NGX_CONF_ERROR;
}
ngx_memzero(regex, sizeof(ngx_http_script_regex_code_t));
value = cf->args->elts;
ngx_memzero(&rc, sizeof(ngx_regex_compile_t));
rc.pattern = value[1];
rc.err.len = NGX_MAX_CONF_ERRSTR;
rc.err.data = errstr;
/* TODO: NGX_REGEX_CASELESS */
regex->regex = ngx_http_regex_compile(cf, &rc);
if (regex->regex == NULL) {
return NGX_CONF_ERROR;
}
regex->code = ngx_http_script_regex_start_code;
regex->uri = 1;
regex->name = value[1];
if (value[2].data[value[2].len - 1] == '?') {
/* the last "?" drops the original arguments */
value[2].len--;
} else {
regex->add_args = 1;
}
last = 0;
if (ngx_strncmp(value[2].data, "http://", sizeof("http://") - 1) == 0
|| ngx_strncmp(value[2].data, "https://", sizeof("https://") - 1) == 0
|| ngx_strncmp(value[2].data, "$scheme", sizeof("$scheme") - 1) == 0)
{
regex->status = NGX_HTTP_MOVED_TEMPORARILY;
regex->redirect = 1;
last = 1;
}
if (cf->args->nelts == 4) {
if (ngx_strcmp(value[3].data, "last") == 0) {
last = 1;
} else if (ngx_strcmp(value[3].data, "break") == 0) {
regex->break_cycle = 1;
last = 1;
} else if (ngx_strcmp(value[3].data, "redirect") == 0) {
regex->status = NGX_HTTP_MOVED_TEMPORARILY;
regex->redirect = 1;
last = 1;
} else if (ngx_strcmp(value[3].data, "permanent") == 0) {
regex->status = NGX_HTTP_MOVED_PERMANENTLY;
regex->redirect = 1;
last = 1;
} else {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid parameter \"%V\"", &value[3]);
return NGX_CONF_ERROR;
}
}
ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
sc.cf = cf;
sc.source = &value[2];
sc.lengths = ®ex->lengths;
sc.values = &lcf->codes;
sc.variables = ngx_http_script_variables_count(&value[2]);
sc.main = regex;
sc.complete_lengths = 1;
sc.compile_args = !regex->redirect;
if (ngx_http_script_compile(&sc) != NGX_OK) {
return NGX_CONF_ERROR;
}
regex = sc.main;
regex->size = sc.size;
regex->args = sc.args;
if (sc.variables == 0 && !sc.dup_capture) {
regex->lengths = NULL;
}
regex_end = ngx_http_script_add_code(lcf->codes,
sizeof(ngx_http_script_regex_end_code_t),
®ex);
if (regex_end == NULL) {
return NGX_CONF_ERROR;
}
regex_end->code = ngx_http_script_regex_end_code;
regex_end->uri = regex->uri;
regex_end->args = regex->args;
regex_end->add_args = regex->add_args;
regex_end->redirect = regex->redirect;
if (last) {
code = ngx_http_script_add_code(lcf->codes, sizeof(uintptr_t), ®ex);
if (code == NULL) {
return NGX_CONF_ERROR;
}
*code = NULL;
}
regex->next = (u_char *) lcf->codes->elts + lcf->codes->nelts
- (u_char *) regex;
return NGX_CONF_OK;
}
static char *
ngx_http_rewrite_return(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_rewrite_loc_conf_t *lcf = conf;
u_char *p;
ngx_str_t *value, *v;
ngx_http_script_return_code_t *ret;
ngx_http_compile_complex_value_t ccv;
ret = ngx_http_script_start_code(cf->pool, &lcf->codes,
sizeof(ngx_http_script_return_code_t));
if (ret == NULL) {
return NGX_CONF_ERROR;
}
value = cf->args->elts;
ngx_memzero(ret, sizeof(ngx_http_script_return_code_t));
ret->code = ngx_http_script_return_code;
p = value[1].data;
ret->status = ngx_atoi(p, value[1].len);
if (ret->status == (uintptr_t) NGX_ERROR) {
if (cf->args->nelts == 2
&& (ngx_strncmp(p, "http://", sizeof("http://") - 1) == 0
|| ngx_strncmp(p, "https://", sizeof("https://") - 1) == 0
|| ngx_strncmp(p, "$scheme", sizeof("$scheme") - 1) == 0))
{
ret->status = NGX_HTTP_MOVED_TEMPORARILY;
v = &value[1];
} else {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid return code \"%V\"", &value[1]);
return NGX_CONF_ERROR;
}
} else {
if (ret->status > 999) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid return code \"%V\"", &value[1]);
return NGX_CONF_ERROR;
}
if (cf->args->nelts == 2) {
return NGX_CONF_OK;
}
v = &value[2];
}
ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
ccv.cf = cf;
ccv.value = v;
ccv.complex_value = &ret->text;
if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
return NGX_CONF_ERROR;
}
return NGX_CONF_OK;
}
static char *
ngx_http_rewrite_break(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_rewrite_loc_conf_t *lcf = conf;
ngx_http_script_code_pt *code;
code = ngx_http_script_start_code(cf->pool, &lcf->codes, sizeof(uintptr_t));
if (code == NULL) {
return NGX_CONF_ERROR;
}
*code = ngx_http_script_break_code;
return NGX_CONF_OK;
}
static char *
ngx_http_rewrite_if(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_rewrite_loc_conf_t *lcf = conf;
void *mconf;
char *rv;
u_char *elts;
ngx_uint_t i;
ngx_conf_t save;
ngx_http_module_t *module;
ngx_http_conf_ctx_t *ctx, *pctx;
ngx_http_core_loc_conf_t *clcf, *pclcf;
ngx_http_script_if_code_t *if_code;
ngx_http_rewrite_loc_conf_t *nlcf;
ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));
if (ctx == NULL) {
return NGX_CONF_ERROR;
}
pctx = cf->ctx;
ctx->main_conf = pctx->main_conf;
ctx->srv_conf = pctx->srv_conf;
ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
if (ctx->loc_conf == NULL) {
return NGX_CONF_ERROR;
}
for (i = 0; cf->cycle->modules[i]; i++) {
if (cf->cycle->modules[i]->type != NGX_HTTP_MODULE) {
continue;
}
module = cf->cycle->modules[i]->ctx;
if (module->create_loc_conf) {
mconf = module->create_loc_conf(cf);
if (mconf == NULL) {
return NGX_CONF_ERROR;
}
ctx->loc_conf[cf->cycle->modules[i]->ctx_index] = mconf;
}
}
pclcf = pctx->loc_conf[ngx_http_core_module.ctx_index];
clcf = ctx->loc_conf[ngx_http_core_module.ctx_index];
clcf->loc_conf = ctx->loc_conf;
clcf->name = pclcf->name;
clcf->noname = 1;
if (ngx_http_add_location(cf, &pclcf->locations, clcf) != NGX_OK) {
return NGX_CONF_ERROR;
}
if (ngx_http_rewrite_if_condition(cf, lcf) != NGX_CONF_OK) {
return NGX_CONF_ERROR;
}
if_code = ngx_array_push_n(lcf->codes, sizeof(ngx_http_script_if_code_t));
if (if_code == NULL) {
return NGX_CONF_ERROR;
}
if_code->code = ngx_http_script_if_code;
elts = lcf->codes->elts;
/* the inner directives must be compiled to the same code array */
nlcf = ctx->loc_conf[ngx_http_rewrite_module.ctx_index];
nlcf->codes = lcf->codes;
save = *cf;
cf->ctx = ctx;
if (cf->cmd_type == NGX_HTTP_SRV_CONF) {
if_code->loc_conf = NULL;
cf->cmd_type = NGX_HTTP_SIF_CONF;
} else {
if_code->loc_conf = ctx->loc_conf;
cf->cmd_type = NGX_HTTP_LIF_CONF;
}
rv = ngx_conf_parse(cf, NULL);
*cf = save;
if (rv != NGX_CONF_OK) {
return rv;
}
if (elts != lcf->codes->elts) {
if_code = (ngx_http_script_if_code_t *)
((u_char *) if_code + ((u_char *) lcf->codes->elts - elts));
}
if_code->next = (u_char *) lcf->codes->elts + lcf->codes->nelts
- (u_char *) if_code;
/* the code array belong to parent block */
nlcf->codes = NULL;
return NGX_CONF_OK;
}
static char *
ngx_http_rewrite_if_condition(ngx_conf_t *cf, ngx_http_rewrite_loc_conf_t *lcf)
{
u_char *p;
size_t len;
ngx_str_t *value;
ngx_uint_t cur, last;
ngx_regex_compile_t rc;
ngx_http_script_code_pt *code;
ngx_http_script_file_code_t *fop;
ngx_http_script_regex_code_t *regex;
u_char errstr[NGX_MAX_CONF_ERRSTR];
value = cf->args->elts;
last = cf->args->nelts - 1;
if (value[1].len < 1 || value[1].data[0] != '(') {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid condition \"%V\"", &value[1]);
return NGX_CONF_ERROR;
}
if (value[1].len == 1) {
cur = 2;
} else {
cur = 1;
value[1].len--;
value[1].data++;
}
if (value[last].len < 1 || value[last].data[value[last].len - 1] != ')') {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid condition \"%V\"", &value[last]);
return NGX_CONF_ERROR;
}
if (value[last].len == 1) {
last--;
} else {
value[last].len--;
value[last].data[value[last].len] = '\0';
}
len = value[cur].len;
p = value[cur].data;
if (len > 1 && p[0] == '$') {
if (cur != last && cur + 2 != last) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid condition \"%V\"", &value[cur]);
return NGX_CONF_ERROR;
}
if (ngx_http_rewrite_variable(cf, lcf, &value[cur]) != NGX_CONF_OK) {
return NGX_CONF_ERROR;
}
if (cur == last) {
return NGX_CONF_OK;
}
cur++;
len = value[cur].len;
p = value[cur].data;
if (len == 1 && p[0] == '=') {
if (ngx_http_rewrite_value(cf, lcf, &value[last]) != NGX_CONF_OK) {
return NGX_CONF_ERROR;
}
code = ngx_http_script_start_code(cf->pool, &lcf->codes,
sizeof(uintptr_t));
if (code == NULL) {
return NGX_CONF_ERROR;
}
*code = ngx_http_script_equal_code;
return NGX_CONF_OK;
}
if (len == 2 && p[0] == '!' && p[1] == '=') {
if (ngx_http_rewrite_value(cf, lcf, &value[last]) != NGX_CONF_OK) {
return NGX_CONF_ERROR;
}
code = ngx_http_script_start_code(cf->pool, &lcf->codes,
sizeof(uintptr_t));
if (code == NULL) {
return NGX_CONF_ERROR;
}
*code = ngx_http_script_not_equal_code;
return NGX_CONF_OK;
}
if ((len == 1 && p[0] == '~')
|| (len == 2 && p[0] == '~' && p[1] == '*')
|| (len == 2 && p[0] == '!' && p[1] == '~')
|| (len == 3 && p[0] == '!' && p[1] == '~' && p[2] == '*'))
{
regex = ngx_http_script_start_code(cf->pool, &lcf->codes,
sizeof(ngx_http_script_regex_code_t));
if (regex == NULL) {
return NGX_CONF_ERROR;
}
ngx_memzero(regex, sizeof(ngx_http_script_regex_code_t));
ngx_memzero(&rc, sizeof(ngx_regex_compile_t));
rc.pattern = value[last];
rc.options = (p[len - 1] == '*') ? NGX_REGEX_CASELESS : 0;
rc.err.len = NGX_MAX_CONF_ERRSTR;
rc.err.data = errstr;
regex->regex = ngx_http_regex_compile(cf, &rc);
if (regex->regex == NULL) {
return NGX_CONF_ERROR;
}
regex->code = ngx_http_script_regex_start_code;
regex->next = sizeof(ngx_http_script_regex_code_t);
regex->test = 1;
if (p[0] == '!') {
regex->negative_test = 1;
}
regex->name = value[last];
return NGX_CONF_OK;
}
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"unexpected \"%V\" in condition", &value[cur]);
return NGX_CONF_ERROR;
} else if ((len == 2 && p[0] == '-')
|| (len == 3 && p[0] == '!' && p[1] == '-'))
{
if (cur + 1 != last) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid condition \"%V\"", &value[cur]);
return NGX_CONF_ERROR;
}
value[last].data[value[last].len] = '\0';
value[last].len++;
if (ngx_http_rewrite_value(cf, lcf, &value[last]) != NGX_CONF_OK) {
return NGX_CONF_ERROR;
}
fop = ngx_http_script_start_code(cf->pool, &lcf->codes,
sizeof(ngx_http_script_file_code_t));
if (fop == NULL) {
return NGX_CONF_ERROR;
}
fop->code = ngx_http_script_file_code;
if (p[1] == 'f') {
fop->op = ngx_http_script_file_plain;
return NGX_CONF_OK;
}
if (p[1] == 'd') {
fop->op = ngx_http_script_file_dir;
return NGX_CONF_OK;
}
if (p[1] == 'e') {
fop->op = ngx_http_script_file_exists;
return NGX_CONF_OK;
}
if (p[1] == 'x') {
fop->op = ngx_http_script_file_exec;
return NGX_CONF_OK;
}
if (p[0] == '!') {
if (p[2] == 'f') {
fop->op = ngx_http_script_file_not_plain;
return NGX_CONF_OK;
}
if (p[2] == 'd') {
fop->op = ngx_http_script_file_not_dir;
return NGX_CONF_OK;
}
if (p[2] == 'e') {
fop->op = ngx_http_script_file_not_exists;
return NGX_CONF_OK;
}
if (p[2] == 'x') {
fop->op = ngx_http_script_file_not_exec;
return NGX_CONF_OK;
}
}
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid condition \"%V\"", &value[cur]);
return NGX_CONF_ERROR;
}
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid condition \"%V\"", &value[cur]);
return NGX_CONF_ERROR;
}
static char *
ngx_http_rewrite_variable(ngx_conf_t *cf, ngx_http_rewrite_loc_conf_t *lcf,
ngx_str_t *value)
{
ngx_int_t index;
ngx_http_script_var_code_t *var_code;
value->len--;
value->data++;
index = ngx_http_get_variable_index(cf, value);
if (index == NGX_ERROR) {
return NGX_CONF_ERROR;
}
var_code = ngx_http_script_start_code(cf->pool, &lcf->codes,
sizeof(ngx_http_script_var_code_t));
if (var_code == NULL) {
return NGX_CONF_ERROR;
}
var_code->code = ngx_http_script_var_code;
var_code->index = index;
return NGX_CONF_OK;
}
static char *
ngx_http_rewrite_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_rewrite_loc_conf_t *lcf = conf;
ngx_int_t index;
ngx_str_t *value;
ngx_http_variable_t *v;
ngx_http_script_var_code_t *vcode;
ngx_http_script_var_handler_code_t *vhcode;
value = cf->args->elts;
if (value[1].data[0] != '$') {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid variable name \"%V\"", &value[1]);
return NGX_CONF_ERROR;
}
value[1].len--;
value[1].data++;
v = ngx_http_add_variable(cf, &value[1],
NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_WEAK);
if (v == NULL) {
return NGX_CONF_ERROR;
}
index = ngx_http_get_variable_index(cf, &value[1]);
if (index == NGX_ERROR) {
return NGX_CONF_ERROR;
}
if (v->get_handler == NULL) {
v->get_handler = ngx_http_rewrite_var;
v->data = index;
}
if (ngx_http_rewrite_value(cf, lcf, &value[2]) != NGX_CONF_OK) {
return NGX_CONF_ERROR;
}
if (v->set_handler) {
vhcode = ngx_http_script_start_code(cf->pool, &lcf->codes,
sizeof(ngx_http_script_var_handler_code_t));
if (vhcode == NULL) {
return NGX_CONF_ERROR;
}
vhcode->code = ngx_http_script_var_set_handler_code;
vhcode->handler = v->set_handler;
vhcode->data = v->data;
return NGX_CONF_OK;
}
vcode = ngx_http_script_start_code(cf->pool, &lcf->codes,
sizeof(ngx_http_script_var_code_t));
if (vcode == NULL) {
return NGX_CONF_ERROR;
}
vcode->code = ngx_http_script_set_var_code;
vcode->index = (uintptr_t) index;
return NGX_CONF_OK;
}
static char *
ngx_http_rewrite_value(ngx_conf_t *cf, ngx_http_rewrite_loc_conf_t *lcf,
ngx_str_t *value)
{
ngx_int_t n;
ngx_http_script_compile_t sc;
ngx_http_script_value_code_t *val;
ngx_http_script_complex_value_code_t *complex;
n = ngx_http_script_variables_count(value);
if (n == 0) {
val = ngx_http_script_start_code(cf->pool, &lcf->codes,
sizeof(ngx_http_script_value_code_t));
if (val == NULL) {
return NGX_CONF_ERROR;
}
n = ngx_atoi(value->data, value->len);
if (n == NGX_ERROR) {
n = 0;
}
val->code = ngx_http_script_value_code;
val->value = (uintptr_t) n;
val->text_len = (uintptr_t) value->len;
val->text_data = (uintptr_t) value->data;
return NGX_CONF_OK;
}
complex = ngx_http_script_start_code(cf->pool, &lcf->codes,
sizeof(ngx_http_script_complex_value_code_t));
if (complex == NULL) {
return NGX_CONF_ERROR;
}
complex->code = ngx_http_script_complex_value_code;
complex->lengths = NULL;
ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
sc.cf = cf;
sc.source = value;
sc.lengths = &complex->lengths;
sc.values = &lcf->codes;
sc.variables = n;
sc.complete_lengths = 1;
if (ngx_http_script_compile(&sc) != NGX_OK) {
return NGX_CONF_ERROR;
}
return NGX_CONF_OK;
}
nginx-1.14.0/src/http/modules/ngx_http_scgi_module.c 000644 001751 001751 00000164542 13265410474 023717 0 ustar 00mdounin mdounin 000000 000000
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
* Copyright (C) Manlio Perillo (manlio.perillo@gmail.com)
*/
#include
#include
#include
typedef struct {
ngx_array_t caches; /* ngx_http_file_cache_t * */
} ngx_http_scgi_main_conf_t;
typedef struct {
ngx_array_t *flushes;
ngx_array_t *lengths;
ngx_array_t *values;
ngx_uint_t number;
ngx_hash_t hash;
} ngx_http_scgi_params_t;
typedef struct {
ngx_http_upstream_conf_t upstream;
ngx_http_scgi_params_t params;
#if (NGX_HTTP_CACHE)
ngx_http_scgi_params_t params_cache;
#endif
ngx_array_t *params_source;
ngx_array_t *scgi_lengths;
ngx_array_t *scgi_values;
#if (NGX_HTTP_CACHE)
ngx_http_complex_value_t cache_key;
#endif
} ngx_http_scgi_loc_conf_t;
static ngx_int_t ngx_http_scgi_eval(ngx_http_request_t *r,
ngx_http_scgi_loc_conf_t *scf);
static ngx_int_t ngx_http_scgi_create_request(ngx_http_request_t *r);
static ngx_int_t ngx_http_scgi_reinit_request(ngx_http_request_t *r);
static ngx_int_t ngx_http_scgi_process_status_line(ngx_http_request_t *r);
static ngx_int_t ngx_http_scgi_process_header(ngx_http_request_t *r);
static void ngx_http_scgi_abort_request(ngx_http_request_t *r);
static void ngx_http_scgi_finalize_request(ngx_http_request_t *r, ngx_int_t rc);
static void *ngx_http_scgi_create_main_conf(ngx_conf_t *cf);
static void *ngx_http_scgi_create_loc_conf(ngx_conf_t *cf);
static char *ngx_http_scgi_merge_loc_conf(ngx_conf_t *cf, void *parent,
void *child);
static ngx_int_t ngx_http_scgi_init_params(ngx_conf_t *cf,
ngx_http_scgi_loc_conf_t *conf, ngx_http_scgi_params_t *params,
ngx_keyval_t *default_params);
static char *ngx_http_scgi_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static char *ngx_http_scgi_store(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
#if (NGX_HTTP_CACHE)
static ngx_int_t ngx_http_scgi_create_key(ngx_http_request_t *r);
static char *ngx_http_scgi_cache(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static char *ngx_http_scgi_cache_key(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
#endif
static ngx_conf_bitmask_t ngx_http_scgi_next_upstream_masks[] = {
{ ngx_string("error"), NGX_HTTP_UPSTREAM_FT_ERROR },
{ ngx_string("timeout"), NGX_HTTP_UPSTREAM_FT_TIMEOUT },
{ ngx_string("invalid_header"), NGX_HTTP_UPSTREAM_FT_INVALID_HEADER },
{ ngx_string("non_idempotent"), NGX_HTTP_UPSTREAM_FT_NON_IDEMPOTENT },
{ ngx_string("http_500"), NGX_HTTP_UPSTREAM_FT_HTTP_500 },
{ ngx_string("http_503"), NGX_HTTP_UPSTREAM_FT_HTTP_503 },
{ ngx_string("http_403"), NGX_HTTP_UPSTREAM_FT_HTTP_403 },
{ ngx_string("http_404"), NGX_HTTP_UPSTREAM_FT_HTTP_404 },
{ ngx_string("http_429"), NGX_HTTP_UPSTREAM_FT_HTTP_429 },
{ ngx_string("updating"), NGX_HTTP_UPSTREAM_FT_UPDATING },
{ ngx_string("off"), NGX_HTTP_UPSTREAM_FT_OFF },
{ ngx_null_string, 0 }
};
ngx_module_t ngx_http_scgi_module;
static ngx_command_t ngx_http_scgi_commands[] = {
{ ngx_string("scgi_pass"),
NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,
ngx_http_scgi_pass,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
{ ngx_string("scgi_store"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_http_scgi_store,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
{ ngx_string("scgi_store_access"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE123,
ngx_conf_set_access_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_scgi_loc_conf_t, upstream.store_access),
NULL },
{ ngx_string("scgi_buffering"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_scgi_loc_conf_t, upstream.buffering),
NULL },
{ ngx_string("scgi_request_buffering"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_scgi_loc_conf_t, upstream.request_buffering),
NULL },
{ ngx_string("scgi_ignore_client_abort"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_scgi_loc_conf_t, upstream.ignore_client_abort),
NULL },
{ ngx_string("scgi_bind"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12,
ngx_http_upstream_bind_set_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_scgi_loc_conf_t, upstream.local),
NULL },
{ ngx_string("scgi_connect_timeout"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_msec_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_scgi_loc_conf_t, upstream.connect_timeout),
NULL },
{ ngx_string("scgi_send_timeout"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_msec_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_scgi_loc_conf_t, upstream.send_timeout),
NULL },
{ ngx_string("scgi_buffer_size"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_size_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_scgi_loc_conf_t, upstream.buffer_size),
NULL },
{ ngx_string("scgi_pass_request_headers"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_scgi_loc_conf_t, upstream.pass_request_headers),
NULL },
{ ngx_string("scgi_pass_request_body"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_scgi_loc_conf_t, upstream.pass_request_body),
NULL },
{ ngx_string("scgi_intercept_errors"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_scgi_loc_conf_t, upstream.intercept_errors),
NULL },
{ ngx_string("scgi_read_timeout"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_msec_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_scgi_loc_conf_t, upstream.read_timeout),
NULL },
{ ngx_string("scgi_buffers"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
ngx_conf_set_bufs_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_scgi_loc_conf_t, upstream.bufs),
NULL },
{ ngx_string("scgi_busy_buffers_size"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_size_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_scgi_loc_conf_t, upstream.busy_buffers_size_conf),
NULL },
{ ngx_string("scgi_force_ranges"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_scgi_loc_conf_t, upstream.force_ranges),
NULL },
{ ngx_string("scgi_limit_rate"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_size_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_scgi_loc_conf_t, upstream.limit_rate),
NULL },
#if (NGX_HTTP_CACHE)
{ ngx_string("scgi_cache"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_http_scgi_cache,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
{ ngx_string("scgi_cache_key"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_http_scgi_cache_key,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
{ ngx_string("scgi_cache_path"),
NGX_HTTP_MAIN_CONF|NGX_CONF_2MORE,
ngx_http_file_cache_set_slot,
NGX_HTTP_MAIN_CONF_OFFSET,
offsetof(ngx_http_scgi_main_conf_t, caches),
&ngx_http_scgi_module },
{ ngx_string("scgi_cache_bypass"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
ngx_http_set_predicate_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_scgi_loc_conf_t, upstream.cache_bypass),
NULL },
{ ngx_string("scgi_no_cache"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
ngx_http_set_predicate_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_scgi_loc_conf_t, upstream.no_cache),
NULL },
{ ngx_string("scgi_cache_valid"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
ngx_http_file_cache_valid_set_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_scgi_loc_conf_t, upstream.cache_valid),
NULL },
{ ngx_string("scgi_cache_min_uses"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_num_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_scgi_loc_conf_t, upstream.cache_min_uses),
NULL },
{ ngx_string("scgi_cache_max_range_offset"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_off_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_scgi_loc_conf_t, upstream.cache_max_range_offset),
NULL },
{ ngx_string("scgi_cache_use_stale"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
ngx_conf_set_bitmask_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_scgi_loc_conf_t, upstream.cache_use_stale),
&ngx_http_scgi_next_upstream_masks },
{ ngx_string("scgi_cache_methods"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
ngx_conf_set_bitmask_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_scgi_loc_conf_t, upstream.cache_methods),
&ngx_http_upstream_cache_method_mask },
{ ngx_string("scgi_cache_lock"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_scgi_loc_conf_t, upstream.cache_lock),
NULL },
{ ngx_string("scgi_cache_lock_timeout"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_msec_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_scgi_loc_conf_t, upstream.cache_lock_timeout),
NULL },
{ ngx_string("scgi_cache_lock_age"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_msec_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_scgi_loc_conf_t, upstream.cache_lock_age),
NULL },
{ ngx_string("scgi_cache_revalidate"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_scgi_loc_conf_t, upstream.cache_revalidate),
NULL },
{ ngx_string("scgi_cache_background_update"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_scgi_loc_conf_t, upstream.cache_background_update),
NULL },
#endif
{ ngx_string("scgi_temp_path"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1234,
ngx_conf_set_path_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_scgi_loc_conf_t, upstream.temp_path),
NULL },
{ ngx_string("scgi_max_temp_file_size"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_size_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_scgi_loc_conf_t, upstream.max_temp_file_size_conf),
NULL },
{ ngx_string("scgi_temp_file_write_size"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_size_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_scgi_loc_conf_t, upstream.temp_file_write_size_conf),
NULL },
{ ngx_string("scgi_next_upstream"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
ngx_conf_set_bitmask_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_scgi_loc_conf_t, upstream.next_upstream),
&ngx_http_scgi_next_upstream_masks },
{ ngx_string("scgi_next_upstream_tries"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_num_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_scgi_loc_conf_t, upstream.next_upstream_tries),
NULL },
{ ngx_string("scgi_next_upstream_timeout"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_msec_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_scgi_loc_conf_t, upstream.next_upstream_timeout),
NULL },
{ ngx_string("scgi_param"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE23,
ngx_http_upstream_param_set_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_scgi_loc_conf_t, params_source),
NULL },
{ ngx_string("scgi_pass_header"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_str_array_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_scgi_loc_conf_t, upstream.pass_headers),
NULL },
{ ngx_string("scgi_hide_header"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_str_array_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_scgi_loc_conf_t, upstream.hide_headers),
NULL },
{ ngx_string("scgi_ignore_headers"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
ngx_conf_set_bitmask_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_scgi_loc_conf_t, upstream.ignore_headers),
&ngx_http_upstream_ignore_headers_masks },
ngx_null_command
};
static ngx_http_module_t ngx_http_scgi_module_ctx = {
NULL, /* preconfiguration */
NULL, /* postconfiguration */
ngx_http_scgi_create_main_conf, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
ngx_http_scgi_create_loc_conf, /* create location configuration */
ngx_http_scgi_merge_loc_conf /* merge location configuration */
};
ngx_module_t ngx_http_scgi_module = {
NGX_MODULE_V1,
&ngx_http_scgi_module_ctx, /* module context */
ngx_http_scgi_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static ngx_str_t ngx_http_scgi_hide_headers[] = {
ngx_string("Status"),
ngx_string("X-Accel-Expires"),
ngx_string("X-Accel-Redirect"),
ngx_string("X-Accel-Limit-Rate"),
ngx_string("X-Accel-Buffering"),
ngx_string("X-Accel-Charset"),
ngx_null_string
};
#if (NGX_HTTP_CACHE)
static ngx_keyval_t ngx_http_scgi_cache_headers[] = {
{ ngx_string("HTTP_IF_MODIFIED_SINCE"),
ngx_string("$upstream_cache_last_modified") },
{ ngx_string("HTTP_IF_UNMODIFIED_SINCE"), ngx_string("") },
{ ngx_string("HTTP_IF_NONE_MATCH"), ngx_string("$upstream_cache_etag") },
{ ngx_string("HTTP_IF_MATCH"), ngx_string("") },
{ ngx_string("HTTP_RANGE"), ngx_string("") },
{ ngx_string("HTTP_IF_RANGE"), ngx_string("") },
{ ngx_null_string, ngx_null_string }
};
#endif
static ngx_path_init_t ngx_http_scgi_temp_path = {
ngx_string(NGX_HTTP_SCGI_TEMP_PATH), { 1, 2, 0 }
};
static ngx_int_t
ngx_http_scgi_handler(ngx_http_request_t *r)
{
ngx_int_t rc;
ngx_http_status_t *status;
ngx_http_upstream_t *u;
ngx_http_scgi_loc_conf_t *scf;
#if (NGX_HTTP_CACHE)
ngx_http_scgi_main_conf_t *smcf;
#endif
if (ngx_http_upstream_create(r) != NGX_OK) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
status = ngx_pcalloc(r->pool, sizeof(ngx_http_status_t));
if (status == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
ngx_http_set_ctx(r, status, ngx_http_scgi_module);
scf = ngx_http_get_module_loc_conf(r, ngx_http_scgi_module);
if (scf->scgi_lengths) {
if (ngx_http_scgi_eval(r, scf) != NGX_OK) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
}
u = r->upstream;
ngx_str_set(&u->schema, "scgi://");
u->output.tag = (ngx_buf_tag_t) &ngx_http_scgi_module;
u->conf = &scf->upstream;
#if (NGX_HTTP_CACHE)
smcf = ngx_http_get_module_main_conf(r, ngx_http_scgi_module);
u->caches = &smcf->caches;
u->create_key = ngx_http_scgi_create_key;
#endif
u->create_request = ngx_http_scgi_create_request;
u->reinit_request = ngx_http_scgi_reinit_request;
u->process_header = ngx_http_scgi_process_status_line;
u->abort_request = ngx_http_scgi_abort_request;
u->finalize_request = ngx_http_scgi_finalize_request;
r->state = 0;
u->buffering = scf->upstream.buffering;
u->pipe = ngx_pcalloc(r->pool, sizeof(ngx_event_pipe_t));
if (u->pipe == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
u->pipe->input_filter = ngx_event_pipe_copy_input_filter;
u->pipe->input_ctx = r;
if (!scf->upstream.request_buffering
&& scf->upstream.pass_request_body
&& !r->headers_in.chunked)
{
r->request_body_no_buffering = 1;
}
rc = ngx_http_read_client_request_body(r, ngx_http_upstream_init);
if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
return rc;
}
return NGX_DONE;
}
static ngx_int_t
ngx_http_scgi_eval(ngx_http_request_t *r, ngx_http_scgi_loc_conf_t * scf)
{
ngx_url_t url;
ngx_http_upstream_t *u;
ngx_memzero(&url, sizeof(ngx_url_t));
if (ngx_http_script_run(r, &url.url, scf->scgi_lengths->elts, 0,
scf->scgi_values->elts)
== NULL)
{
return NGX_ERROR;
}
url.no_resolve = 1;
if (ngx_parse_url(r->pool, &url) != NGX_OK) {
if (url.err) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"%s in upstream \"%V\"", url.err, &url.url);
}
return NGX_ERROR;
}
u = r->upstream;
u->resolved = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_resolved_t));
if (u->resolved == NULL) {
return NGX_ERROR;
}
if (url.addrs) {
u->resolved->sockaddr = url.addrs[0].sockaddr;
u->resolved->socklen = url.addrs[0].socklen;
u->resolved->name = url.addrs[0].name;
u->resolved->naddrs = 1;
}
u->resolved->host = url.host;
u->resolved->port = url.port;
u->resolved->no_port = url.no_port;
return NGX_OK;
}
#if (NGX_HTTP_CACHE)
static ngx_int_t
ngx_http_scgi_create_key(ngx_http_request_t *r)
{
ngx_str_t *key;
ngx_http_scgi_loc_conf_t *scf;
key = ngx_array_push(&r->cache->keys);
if (key == NULL) {
return NGX_ERROR;
}
scf = ngx_http_get_module_loc_conf(r, ngx_http_scgi_module);
if (ngx_http_complex_value(r, &scf->cache_key, key) != NGX_OK) {
return NGX_ERROR;
}
return NGX_OK;
}
#endif
static ngx_int_t
ngx_http_scgi_create_request(ngx_http_request_t *r)
{
off_t content_length_n;
u_char ch, *key, *val, *lowcase_key;
size_t len, key_len, val_len, allocated;
ngx_buf_t *b;
ngx_str_t content_length;
ngx_uint_t i, n, hash, skip_empty, header_params;
ngx_chain_t *cl, *body;
ngx_list_part_t *part;
ngx_table_elt_t *header, **ignored;
ngx_http_scgi_params_t *params;
ngx_http_script_code_pt code;
ngx_http_script_engine_t e, le;
ngx_http_scgi_loc_conf_t *scf;
ngx_http_script_len_code_pt lcode;
u_char buffer[NGX_OFF_T_LEN];
content_length_n = 0;
body = r->upstream->request_bufs;
while (body) {
content_length_n += ngx_buf_size(body->buf);
body = body->next;
}
content_length.data = buffer;
content_length.len = ngx_sprintf(buffer, "%O", content_length_n) - buffer;
len = sizeof("CONTENT_LENGTH") + content_length.len + 1;
header_params = 0;
ignored = NULL;
scf = ngx_http_get_module_loc_conf(r, ngx_http_scgi_module);
#if (NGX_HTTP_CACHE)
params = r->upstream->cacheable ? &scf->params_cache : &scf->params;
#else
params = &scf->params;
#endif
if (params->lengths) {
ngx_memzero(&le, sizeof(ngx_http_script_engine_t));
ngx_http_script_flush_no_cacheable_variables(r, params->flushes);
le.flushed = 1;
le.ip = params->lengths->elts;
le.request = r;
while (*(uintptr_t *) le.ip) {
lcode = *(ngx_http_script_len_code_pt *) le.ip;
key_len = lcode(&le);
lcode = *(ngx_http_script_len_code_pt *) le.ip;
skip_empty = lcode(&le);
for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) {
lcode = *(ngx_http_script_len_code_pt *) le.ip;
}
le.ip += sizeof(uintptr_t);
if (skip_empty && val_len == 0) {
continue;
}
len += key_len + val_len + 1;
}
}
if (scf->upstream.pass_request_headers) {
allocated = 0;
lowcase_key = NULL;
if (params->number) {
n = 0;
part = &r->headers_in.headers.part;
while (part) {
n += part->nelts;
part = part->next;
}
ignored = ngx_palloc(r->pool, n * sizeof(void *));
if (ignored == NULL) {
return NGX_ERROR;
}
}
part = &r->headers_in.headers.part;
header = part->elts;
for (i = 0; /* void */; i++) {
if (i >= part->nelts) {
if (part->next == NULL) {
break;
}
part = part->next;
header = part->elts;
i = 0;
}
if (params->number) {
if (allocated < header[i].key.len) {
allocated = header[i].key.len + 16;
lowcase_key = ngx_pnalloc(r->pool, allocated);
if (lowcase_key == NULL) {
return NGX_ERROR;
}
}
hash = 0;
for (n = 0; n < header[i].key.len; n++) {
ch = header[i].key.data[n];
if (ch >= 'A' && ch <= 'Z') {
ch |= 0x20;
} else if (ch == '-') {
ch = '_';
}
hash = ngx_hash(hash, ch);
lowcase_key[n] = ch;
}
if (ngx_hash_find(¶ms->hash, hash, lowcase_key, n)) {
ignored[header_params++] = &header[i];
continue;
}
}
len += sizeof("HTTP_") - 1 + header[i].key.len + 1
+ header[i].value.len + 1;
}
}
/* netstring: "length:" + packet + "," */
b = ngx_create_temp_buf(r->pool, NGX_SIZE_T_LEN + 1 + len + 1);
if (b == NULL) {
return NGX_ERROR;
}
cl = ngx_alloc_chain_link(r->pool);
if (cl == NULL) {
return NGX_ERROR;
}
cl->buf = b;
b->last = ngx_sprintf(b->last, "%ui:CONTENT_LENGTH%Z%V%Z",
len, &content_length);
if (params->lengths) {
ngx_memzero(&e, sizeof(ngx_http_script_engine_t));
e.ip = params->values->elts;
e.pos = b->last;
e.request = r;
e.flushed = 1;
le.ip = params->lengths->elts;
while (*(uintptr_t *) le.ip) {
lcode = *(ngx_http_script_len_code_pt *) le.ip;
lcode(&le); /* key length */
lcode = *(ngx_http_script_len_code_pt *) le.ip;
skip_empty = lcode(&le);
for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) {
lcode = *(ngx_http_script_len_code_pt *) le.ip;
}
le.ip += sizeof(uintptr_t);
if (skip_empty && val_len == 0) {
e.skip = 1;
while (*(uintptr_t *) e.ip) {
code = *(ngx_http_script_code_pt *) e.ip;
code((ngx_http_script_engine_t *) &e);
}
e.ip += sizeof(uintptr_t);
e.skip = 0;
continue;
}
#if (NGX_DEBUG)
key = e.pos;
#endif
code = *(ngx_http_script_code_pt *) e.ip;
code((ngx_http_script_engine_t *) &e);
#if (NGX_DEBUG)
val = e.pos;
#endif
while (*(uintptr_t *) e.ip) {
code = *(ngx_http_script_code_pt *) e.ip;
code((ngx_http_script_engine_t *) &e);
}
*e.pos++ = '\0';
e.ip += sizeof(uintptr_t);
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"scgi param: \"%s: %s\"", key, val);
}
b->last = e.pos;
}
if (scf->upstream.pass_request_headers) {
part = &r->headers_in.headers.part;
header = part->elts;
for (i = 0; /* void */; i++) {
if (i >= part->nelts) {
if (part->next == NULL) {
break;
}
part = part->next;
header = part->elts;
i = 0;
}
for (n = 0; n < header_params; n++) {
if (&header[i] == ignored[n]) {
goto next;
}
}
key = b->last;
b->last = ngx_cpymem(key, "HTTP_", sizeof("HTTP_") - 1);
for (n = 0; n < header[i].key.len; n++) {
ch = header[i].key.data[n];
if (ch >= 'a' && ch <= 'z') {
ch &= ~0x20;
} else if (ch == '-') {
ch = '_';
}
*b->last++ = ch;
}
*b->last++ = (u_char) 0;
val = b->last;
b->last = ngx_copy(val, header[i].value.data, header[i].value.len);
*b->last++ = (u_char) 0;
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"scgi param: \"%s: %s\"", key, val);
next:
continue;
}
}
*b->last++ = (u_char) ',';
if (r->request_body_no_buffering) {
r->upstream->request_bufs = cl;
} else if (scf->upstream.pass_request_body) {
body = r->upstream->request_bufs;
r->upstream->request_bufs = cl;
while (body) {
b = ngx_alloc_buf(r->pool);
if (b == NULL) {
return NGX_ERROR;
}
ngx_memcpy(b, body->buf, sizeof(ngx_buf_t));
cl->next = ngx_alloc_chain_link(r->pool);
if (cl->next == NULL) {
return NGX_ERROR;
}
cl = cl->next;
cl->buf = b;
body = body->next;
}
} else {
r->upstream->request_bufs = cl;
}
cl->next = NULL;
return NGX_OK;
}
static ngx_int_t
ngx_http_scgi_reinit_request(ngx_http_request_t *r)
{
ngx_http_status_t *status;
status = ngx_http_get_module_ctx(r, ngx_http_scgi_module);
if (status == NULL) {
return NGX_OK;
}
status->code = 0;
status->count = 0;
status->start = NULL;
status->end = NULL;
r->upstream->process_header = ngx_http_scgi_process_status_line;
r->state = 0;
return NGX_OK;
}
static ngx_int_t
ngx_http_scgi_process_status_line(ngx_http_request_t *r)
{
size_t len;
ngx_int_t rc;
ngx_http_status_t *status;
ngx_http_upstream_t *u;
status = ngx_http_get_module_ctx(r, ngx_http_scgi_module);
if (status == NULL) {
return NGX_ERROR;
}
u = r->upstream;
rc = ngx_http_parse_status_line(r, &u->buffer, status);
if (rc == NGX_AGAIN) {
return rc;
}
if (rc == NGX_ERROR) {
u->process_header = ngx_http_scgi_process_header;
return ngx_http_scgi_process_header(r);
}
if (u->state && u->state->status == 0) {
u->state->status = status->code;
}
u->headers_in.status_n = status->code;
len = status->end - status->start;
u->headers_in.status_line.len = len;
u->headers_in.status_line.data = ngx_pnalloc(r->pool, len);
if (u->headers_in.status_line.data == NULL) {
return NGX_ERROR;
}
ngx_memcpy(u->headers_in.status_line.data, status->start, len);
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http scgi status %ui \"%V\"",
u->headers_in.status_n, &u->headers_in.status_line);
u->process_header = ngx_http_scgi_process_header;
return ngx_http_scgi_process_header(r);
}
static ngx_int_t
ngx_http_scgi_process_header(ngx_http_request_t *r)
{
ngx_str_t *status_line;
ngx_int_t rc, status;
ngx_table_elt_t *h;
ngx_http_upstream_t *u;
ngx_http_upstream_header_t *hh;
ngx_http_upstream_main_conf_t *umcf;
umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module);
for ( ;; ) {
rc = ngx_http_parse_header_line(r, &r->upstream->buffer, 1);
if (rc == NGX_OK) {
/* a header line has been parsed successfully */
h = ngx_list_push(&r->upstream->headers_in.headers);
if (h == NULL) {
return NGX_ERROR;
}
h->hash = r->header_hash;
h->key.len = r->header_name_end - r->header_name_start;
h->value.len = r->header_end - r->header_start;
h->key.data = ngx_pnalloc(r->pool,
h->key.len + 1 + h->value.len + 1
+ h->key.len);
if (h->key.data == NULL) {
h->hash = 0;
return NGX_ERROR;
}
h->value.data = h->key.data + h->key.len + 1;
h->lowcase_key = h->key.data + h->key.len + 1 + h->value.len + 1;
ngx_memcpy(h->key.data, r->header_name_start, h->key.len);
h->key.data[h->key.len] = '\0';
ngx_memcpy(h->value.data, r->header_start, h->value.len);
h->value.data[h->value.len] = '\0';
if (h->key.len == r->lowcase_index) {
ngx_memcpy(h->lowcase_key, r->lowcase_header, h->key.len);
} else {
ngx_strlow(h->lowcase_key, h->key.data, h->key.len);
}
hh = ngx_hash_find(&umcf->headers_in_hash, h->hash,
h->lowcase_key, h->key.len);
if (hh && hh->handler(r, h, hh->offset) != NGX_OK) {
return NGX_ERROR;
}
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http scgi header: \"%V: %V\"", &h->key, &h->value);
continue;
}
if (rc == NGX_HTTP_PARSE_HEADER_DONE) {
/* a whole header has been parsed successfully */
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http scgi header done");
u = r->upstream;
if (u->headers_in.status_n) {
goto done;
}
if (u->headers_in.status) {
status_line = &u->headers_in.status->value;
status = ngx_atoi(status_line->data, 3);
if (status == NGX_ERROR) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent invalid status \"%V\"",
status_line);
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
}
u->headers_in.status_n = status;
u->headers_in.status_line = *status_line;
} else if (u->headers_in.location) {
u->headers_in.status_n = 302;
ngx_str_set(&u->headers_in.status_line,
"302 Moved Temporarily");
} else {
u->headers_in.status_n = 200;
ngx_str_set(&u->headers_in.status_line, "200 OK");
}
if (u->state && u->state->status == 0) {
u->state->status = u->headers_in.status_n;
}
done:
if (u->headers_in.status_n == NGX_HTTP_SWITCHING_PROTOCOLS
&& r->headers_in.upgrade)
{
u->upgrade = 1;
}
return NGX_OK;
}
if (rc == NGX_AGAIN) {
return NGX_AGAIN;
}
/* there was error while a header line parsing */
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent invalid header");
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
}
}
static void
ngx_http_scgi_abort_request(ngx_http_request_t *r)
{
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"abort http scgi request");
return;
}
static void
ngx_http_scgi_finalize_request(ngx_http_request_t *r, ngx_int_t rc)
{
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"finalize http scgi request");
return;
}
static void *
ngx_http_scgi_create_main_conf(ngx_conf_t *cf)
{
ngx_http_scgi_main_conf_t *conf;
conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_scgi_main_conf_t));
if (conf == NULL) {
return NULL;
}
#if (NGX_HTTP_CACHE)
if (ngx_array_init(&conf->caches, cf->pool, 4,
sizeof(ngx_http_file_cache_t *))
!= NGX_OK)
{
return NULL;
}
#endif
return conf;
}
static void *
ngx_http_scgi_create_loc_conf(ngx_conf_t *cf)
{
ngx_http_scgi_loc_conf_t *conf;
conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_scgi_loc_conf_t));
if (conf == NULL) {
return NULL;
}
conf->upstream.store = NGX_CONF_UNSET;
conf->upstream.store_access = NGX_CONF_UNSET_UINT;
conf->upstream.next_upstream_tries = NGX_CONF_UNSET_UINT;
conf->upstream.buffering = NGX_CONF_UNSET;
conf->upstream.request_buffering = NGX_CONF_UNSET;
conf->upstream.ignore_client_abort = NGX_CONF_UNSET;
conf->upstream.force_ranges = NGX_CONF_UNSET;
conf->upstream.local = NGX_CONF_UNSET_PTR;
conf->upstream.connect_timeout = NGX_CONF_UNSET_MSEC;
conf->upstream.send_timeout = NGX_CONF_UNSET_MSEC;
conf->upstream.read_timeout = NGX_CONF_UNSET_MSEC;
conf->upstream.next_upstream_timeout = NGX_CONF_UNSET_MSEC;
conf->upstream.send_lowat = NGX_CONF_UNSET_SIZE;
conf->upstream.buffer_size = NGX_CONF_UNSET_SIZE;
conf->upstream.limit_rate = NGX_CONF_UNSET_SIZE;
conf->upstream.busy_buffers_size_conf = NGX_CONF_UNSET_SIZE;
conf->upstream.max_temp_file_size_conf = NGX_CONF_UNSET_SIZE;
conf->upstream.temp_file_write_size_conf = NGX_CONF_UNSET_SIZE;
conf->upstream.pass_request_headers = NGX_CONF_UNSET;
conf->upstream.pass_request_body = NGX_CONF_UNSET;
#if (NGX_HTTP_CACHE)
conf->upstream.cache = NGX_CONF_UNSET;
conf->upstream.cache_min_uses = NGX_CONF_UNSET_UINT;
conf->upstream.cache_max_range_offset = NGX_CONF_UNSET;
conf->upstream.cache_bypass = NGX_CONF_UNSET_PTR;
conf->upstream.no_cache = NGX_CONF_UNSET_PTR;
conf->upstream.cache_valid = NGX_CONF_UNSET_PTR;
conf->upstream.cache_lock = NGX_CONF_UNSET;
conf->upstream.cache_lock_timeout = NGX_CONF_UNSET_MSEC;
conf->upstream.cache_lock_age = NGX_CONF_UNSET_MSEC;
conf->upstream.cache_revalidate = NGX_CONF_UNSET;
conf->upstream.cache_background_update = NGX_CONF_UNSET;
#endif
conf->upstream.hide_headers = NGX_CONF_UNSET_PTR;
conf->upstream.pass_headers = NGX_CONF_UNSET_PTR;
conf->upstream.intercept_errors = NGX_CONF_UNSET;
/* "scgi_cyclic_temp_file" is disabled */
conf->upstream.cyclic_temp_file = 0;
conf->upstream.change_buffering = 1;
ngx_str_set(&conf->upstream.module, "scgi");
return conf;
}
static char *
ngx_http_scgi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
{
ngx_http_scgi_loc_conf_t *prev = parent;
ngx_http_scgi_loc_conf_t *conf = child;
size_t size;
ngx_int_t rc;
ngx_hash_init_t hash;
ngx_http_core_loc_conf_t *clcf;
#if (NGX_HTTP_CACHE)
if (conf->upstream.store > 0) {
conf->upstream.cache = 0;
}
if (conf->upstream.cache > 0) {
conf->upstream.store = 0;
}
#endif
if (conf->upstream.store == NGX_CONF_UNSET) {
ngx_conf_merge_value(conf->upstream.store, prev->upstream.store, 0);
conf->upstream.store_lengths = prev->upstream.store_lengths;
conf->upstream.store_values = prev->upstream.store_values;
}
ngx_conf_merge_uint_value(conf->upstream.store_access,
prev->upstream.store_access, 0600);
ngx_conf_merge_uint_value(conf->upstream.next_upstream_tries,
prev->upstream.next_upstream_tries, 0);
ngx_conf_merge_value(conf->upstream.buffering,
prev->upstream.buffering, 1);
ngx_conf_merge_value(conf->upstream.request_buffering,
prev->upstream.request_buffering, 1);
ngx_conf_merge_value(conf->upstream.ignore_client_abort,
prev->upstream.ignore_client_abort, 0);
ngx_conf_merge_value(conf->upstream.force_ranges,
prev->upstream.force_ranges, 0);
ngx_conf_merge_ptr_value(conf->upstream.local,
prev->upstream.local, NULL);
ngx_conf_merge_msec_value(conf->upstream.connect_timeout,
prev->upstream.connect_timeout, 60000);
ngx_conf_merge_msec_value(conf->upstream.send_timeout,
prev->upstream.send_timeout, 60000);
ngx_conf_merge_msec_value(conf->upstream.read_timeout,
prev->upstream.read_timeout, 60000);
ngx_conf_merge_msec_value(conf->upstream.next_upstream_timeout,
prev->upstream.next_upstream_timeout, 0);
ngx_conf_merge_size_value(conf->upstream.send_lowat,
prev->upstream.send_lowat, 0);
ngx_conf_merge_size_value(conf->upstream.buffer_size,
prev->upstream.buffer_size,
(size_t) ngx_pagesize);
ngx_conf_merge_size_value(conf->upstream.limit_rate,
prev->upstream.limit_rate, 0);
ngx_conf_merge_bufs_value(conf->upstream.bufs, prev->upstream.bufs,
8, ngx_pagesize);
if (conf->upstream.bufs.num < 2) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"there must be at least 2 \"scgi_buffers\"");
return NGX_CONF_ERROR;
}
size = conf->upstream.buffer_size;
if (size < conf->upstream.bufs.size) {
size = conf->upstream.bufs.size;
}
ngx_conf_merge_size_value(conf->upstream.busy_buffers_size_conf,
prev->upstream.busy_buffers_size_conf,
NGX_CONF_UNSET_SIZE);
if (conf->upstream.busy_buffers_size_conf == NGX_CONF_UNSET_SIZE) {
conf->upstream.busy_buffers_size = 2 * size;
} else {
conf->upstream.busy_buffers_size =
conf->upstream.busy_buffers_size_conf;
}
if (conf->upstream.busy_buffers_size < size) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"\"scgi_busy_buffers_size\" must be equal to or greater "
"than the maximum of the value of \"scgi_buffer_size\" and "
"one of the \"scgi_buffers\"");
return NGX_CONF_ERROR;
}
if (conf->upstream.busy_buffers_size
> (conf->upstream.bufs.num - 1) * conf->upstream.bufs.size)
{
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"\"scgi_busy_buffers_size\" must be less than "
"the size of all \"scgi_buffers\" minus one buffer");
return NGX_CONF_ERROR;
}
ngx_conf_merge_size_value(conf->upstream.temp_file_write_size_conf,
prev->upstream.temp_file_write_size_conf,
NGX_CONF_UNSET_SIZE);
if (conf->upstream.temp_file_write_size_conf == NGX_CONF_UNSET_SIZE) {
conf->upstream.temp_file_write_size = 2 * size;
} else {
conf->upstream.temp_file_write_size =
conf->upstream.temp_file_write_size_conf;
}
if (conf->upstream.temp_file_write_size < size) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"\"scgi_temp_file_write_size\" must be equal to or greater than "
"the maximum of the value of \"scgi_buffer_size\" and "
"one of the \"scgi_buffers\"");
return NGX_CONF_ERROR;
}
ngx_conf_merge_size_value(conf->upstream.max_temp_file_size_conf,
prev->upstream.max_temp_file_size_conf,
NGX_CONF_UNSET_SIZE);
if (conf->upstream.max_temp_file_size_conf == NGX_CONF_UNSET_SIZE) {
conf->upstream.max_temp_file_size = 1024 * 1024 * 1024;
} else {
conf->upstream.max_temp_file_size =
conf->upstream.max_temp_file_size_conf;
}
if (conf->upstream.max_temp_file_size != 0
&& conf->upstream.max_temp_file_size < size)
{
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"\"scgi_max_temp_file_size\" must be equal to zero to disable "
"temporary files usage or must be equal to or greater than "
"the maximum of the value of \"scgi_buffer_size\" and "
"one of the \"scgi_buffers\"");
return NGX_CONF_ERROR;
}
ngx_conf_merge_bitmask_value(conf->upstream.ignore_headers,
prev->upstream.ignore_headers,
NGX_CONF_BITMASK_SET);
ngx_conf_merge_bitmask_value(conf->upstream.next_upstream,
prev->upstream.next_upstream,
(NGX_CONF_BITMASK_SET
|NGX_HTTP_UPSTREAM_FT_ERROR
|NGX_HTTP_UPSTREAM_FT_TIMEOUT));
if (conf->upstream.next_upstream & NGX_HTTP_UPSTREAM_FT_OFF) {
conf->upstream.next_upstream = NGX_CONF_BITMASK_SET
|NGX_HTTP_UPSTREAM_FT_OFF;
}
if (ngx_conf_merge_path_value(cf, &conf->upstream.temp_path,
prev->upstream.temp_path,
&ngx_http_scgi_temp_path)
!= NGX_OK)
{
return NGX_CONF_ERROR;
}
#if (NGX_HTTP_CACHE)
if (conf->upstream.cache == NGX_CONF_UNSET) {
ngx_conf_merge_value(conf->upstream.cache,
prev->upstream.cache, 0);
conf->upstream.cache_zone = prev->upstream.cache_zone;
conf->upstream.cache_value = prev->upstream.cache_value;
}
if (conf->upstream.cache_zone && conf->upstream.cache_zone->data == NULL) {
ngx_shm_zone_t *shm_zone;
shm_zone = conf->upstream.cache_zone;
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"\"scgi_cache\" zone \"%V\" is unknown",
&shm_zone->shm.name);
return NGX_CONF_ERROR;
}
ngx_conf_merge_uint_value(conf->upstream.cache_min_uses,
prev->upstream.cache_min_uses, 1);
ngx_conf_merge_off_value(conf->upstream.cache_max_range_offset,
prev->upstream.cache_max_range_offset,
NGX_MAX_OFF_T_VALUE);
ngx_conf_merge_bitmask_value(conf->upstream.cache_use_stale,
prev->upstream.cache_use_stale,
(NGX_CONF_BITMASK_SET
|NGX_HTTP_UPSTREAM_FT_OFF));
if (conf->upstream.cache_use_stale & NGX_HTTP_UPSTREAM_FT_OFF) {
conf->upstream.cache_use_stale = NGX_CONF_BITMASK_SET
|NGX_HTTP_UPSTREAM_FT_OFF;
}
if (conf->upstream.cache_use_stale & NGX_HTTP_UPSTREAM_FT_ERROR) {
conf->upstream.cache_use_stale |= NGX_HTTP_UPSTREAM_FT_NOLIVE;
}
if (conf->upstream.cache_methods == 0) {
conf->upstream.cache_methods = prev->upstream.cache_methods;
}
conf->upstream.cache_methods |= NGX_HTTP_GET|NGX_HTTP_HEAD;
ngx_conf_merge_ptr_value(conf->upstream.cache_bypass,
prev->upstream.cache_bypass, NULL);
ngx_conf_merge_ptr_value(conf->upstream.no_cache,
prev->upstream.no_cache, NULL);
ngx_conf_merge_ptr_value(conf->upstream.cache_valid,
prev->upstream.cache_valid, NULL);
if (conf->cache_key.value.data == NULL) {
conf->cache_key = prev->cache_key;
}
if (conf->upstream.cache && conf->cache_key.value.data == NULL) {
ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
"no \"scgi_cache_key\" for \"scgi_cache\"");
}
ngx_conf_merge_value(conf->upstream.cache_lock,
prev->upstream.cache_lock, 0);
ngx_conf_merge_msec_value(conf->upstream.cache_lock_timeout,
prev->upstream.cache_lock_timeout, 5000);
ngx_conf_merge_msec_value(conf->upstream.cache_lock_age,
prev->upstream.cache_lock_age, 5000);
ngx_conf_merge_value(conf->upstream.cache_revalidate,
prev->upstream.cache_revalidate, 0);
ngx_conf_merge_value(conf->upstream.cache_background_update,
prev->upstream.cache_background_update, 0);
#endif
ngx_conf_merge_value(conf->upstream.pass_request_headers,
prev->upstream.pass_request_headers, 1);
ngx_conf_merge_value(conf->upstream.pass_request_body,
prev->upstream.pass_request_body, 1);
ngx_conf_merge_value(conf->upstream.intercept_errors,
prev->upstream.intercept_errors, 0);
hash.max_size = 512;
hash.bucket_size = ngx_align(64, ngx_cacheline_size);
hash.name = "scgi_hide_headers_hash";
if (ngx_http_upstream_hide_headers_hash(cf, &conf->upstream,
&prev->upstream, ngx_http_scgi_hide_headers, &hash)
!= NGX_OK)
{
return NGX_CONF_ERROR;
}
clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
if (clcf->noname
&& conf->upstream.upstream == NULL && conf->scgi_lengths == NULL)
{
conf->upstream.upstream = prev->upstream.upstream;
conf->scgi_lengths = prev->scgi_lengths;
conf->scgi_values = prev->scgi_values;
}
if (clcf->lmt_excpt && clcf->handler == NULL
&& (conf->upstream.upstream || conf->scgi_lengths))
{
clcf->handler = ngx_http_scgi_handler;
}
if (conf->params_source == NULL) {
conf->params = prev->params;
#if (NGX_HTTP_CACHE)
conf->params_cache = prev->params_cache;
#endif
conf->params_source = prev->params_source;
}
rc = ngx_http_scgi_init_params(cf, conf, &conf->params, NULL);
if (rc != NGX_OK) {
return NGX_CONF_ERROR;
}
#if (NGX_HTTP_CACHE)
if (conf->upstream.cache) {
rc = ngx_http_scgi_init_params(cf, conf, &conf->params_cache,
ngx_http_scgi_cache_headers);
if (rc != NGX_OK) {
return NGX_CONF_ERROR;
}
}
#endif
/*
* special handling to preserve conf->params in the "http" section
* to inherit it to all servers
*/
if (prev->params.hash.buckets == NULL
&& conf->params_source == prev->params_source)
{
prev->params = conf->params;
#if (NGX_HTTP_CACHE)
prev->params_cache = conf->params_cache;
#endif
}
return NGX_CONF_OK;
}
static ngx_int_t
ngx_http_scgi_init_params(ngx_conf_t *cf, ngx_http_scgi_loc_conf_t *conf,
ngx_http_scgi_params_t *params, ngx_keyval_t *default_params)
{
u_char *p;
size_t size;
uintptr_t *code;
ngx_uint_t i, nsrc;
ngx_array_t headers_names, params_merged;
ngx_keyval_t *h;
ngx_hash_key_t *hk;
ngx_hash_init_t hash;
ngx_http_upstream_param_t *src, *s;
ngx_http_script_compile_t sc;
ngx_http_script_copy_code_t *copy;
if (params->hash.buckets) {
return NGX_OK;
}
if (conf->params_source == NULL && default_params == NULL) {
params->hash.buckets = (void *) 1;
return NGX_OK;
}
params->lengths = ngx_array_create(cf->pool, 64, 1);
if (params->lengths == NULL) {
return NGX_ERROR;
}
params->values = ngx_array_create(cf->pool, 512, 1);
if (params->values == NULL) {
return NGX_ERROR;
}
if (ngx_array_init(&headers_names, cf->temp_pool, 4, sizeof(ngx_hash_key_t))
!= NGX_OK)
{
return NGX_ERROR;
}
if (conf->params_source) {
src = conf->params_source->elts;
nsrc = conf->params_source->nelts;
} else {
src = NULL;
nsrc = 0;
}
if (default_params) {
if (ngx_array_init(¶ms_merged, cf->temp_pool, 4,
sizeof(ngx_http_upstream_param_t))
!= NGX_OK)
{
return NGX_ERROR;
}
for (i = 0; i < nsrc; i++) {
s = ngx_array_push(¶ms_merged);
if (s == NULL) {
return NGX_ERROR;
}
*s = src[i];
}
h = default_params;
while (h->key.len) {
src = params_merged.elts;
nsrc = params_merged.nelts;
for (i = 0; i < nsrc; i++) {
if (ngx_strcasecmp(h->key.data, src[i].key.data) == 0) {
goto next;
}
}
s = ngx_array_push(¶ms_merged);
if (s == NULL) {
return NGX_ERROR;
}
s->key = h->key;
s->value = h->value;
s->skip_empty = 1;
next:
h++;
}
src = params_merged.elts;
nsrc = params_merged.nelts;
}
for (i = 0; i < nsrc; i++) {
if (src[i].key.len > sizeof("HTTP_") - 1
&& ngx_strncmp(src[i].key.data, "HTTP_", sizeof("HTTP_") - 1) == 0)
{
hk = ngx_array_push(&headers_names);
if (hk == NULL) {
return NGX_ERROR;
}
hk->key.len = src[i].key.len - 5;
hk->key.data = src[i].key.data + 5;
hk->key_hash = ngx_hash_key_lc(hk->key.data, hk->key.len);
hk->value = (void *) 1;
if (src[i].value.len == 0) {
continue;
}
}
copy = ngx_array_push_n(params->lengths,
sizeof(ngx_http_script_copy_code_t));
if (copy == NULL) {
return NGX_ERROR;
}
copy->code = (ngx_http_script_code_pt) ngx_http_script_copy_len_code;
copy->len = src[i].key.len + 1;
copy = ngx_array_push_n(params->lengths,
sizeof(ngx_http_script_copy_code_t));
if (copy == NULL) {
return NGX_ERROR;
}
copy->code = (ngx_http_script_code_pt) ngx_http_script_copy_len_code;
copy->len = src[i].skip_empty;
size = (sizeof(ngx_http_script_copy_code_t)
+ src[i].key.len + 1 + sizeof(uintptr_t) - 1)
& ~(sizeof(uintptr_t) - 1);
copy = ngx_array_push_n(params->values, size);
if (copy == NULL) {
return NGX_ERROR;
}
copy->code = ngx_http_script_copy_code;
copy->len = src[i].key.len + 1;
p = (u_char *) copy + sizeof(ngx_http_script_copy_code_t);
(void) ngx_cpystrn(p, src[i].key.data, src[i].key.len + 1);
ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
sc.cf = cf;
sc.source = &src[i].value;
sc.flushes = ¶ms->flushes;
sc.lengths = ¶ms->lengths;
sc.values = ¶ms->values;
if (ngx_http_script_compile(&sc) != NGX_OK) {
return NGX_ERROR;
}
code = ngx_array_push_n(params->lengths, sizeof(uintptr_t));
if (code == NULL) {
return NGX_ERROR;
}
*code = (uintptr_t) NULL;
code = ngx_array_push_n(params->values, sizeof(uintptr_t));
if (code == NULL) {
return NGX_ERROR;
}
*code = (uintptr_t) NULL;
}
code = ngx_array_push_n(params->lengths, sizeof(uintptr_t));
if (code == NULL) {
return NGX_ERROR;
}
*code = (uintptr_t) NULL;
params->number = headers_names.nelts;
hash.hash = ¶ms->hash;
hash.key = ngx_hash_key_lc;
hash.max_size = 512;
hash.bucket_size = 64;
hash.name = "scgi_params_hash";
hash.pool = cf->pool;
hash.temp_pool = NULL;
return ngx_hash_init(&hash, headers_names.elts, headers_names.nelts);
}
static char *
ngx_http_scgi_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_scgi_loc_conf_t *scf = conf;
ngx_url_t u;
ngx_str_t *value, *url;
ngx_uint_t n;
ngx_http_core_loc_conf_t *clcf;
ngx_http_script_compile_t sc;
if (scf->upstream.upstream || scf->scgi_lengths) {
return "is duplicate";
}
clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
clcf->handler = ngx_http_scgi_handler;
value = cf->args->elts;
url = &value[1];
n = ngx_http_script_variables_count(url);
if (n) {
ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
sc.cf = cf;
sc.source = url;
sc.lengths = &scf->scgi_lengths;
sc.values = &scf->scgi_values;
sc.variables = n;
sc.complete_lengths = 1;
sc.complete_values = 1;
if (ngx_http_script_compile(&sc) != NGX_OK) {
return NGX_CONF_ERROR;
}
return NGX_CONF_OK;
}
ngx_memzero(&u, sizeof(ngx_url_t));
u.url = value[1];
u.no_resolve = 1;
scf->upstream.upstream = ngx_http_upstream_add(cf, &u, 0);
if (scf->upstream.upstream == NULL) {
return NGX_CONF_ERROR;
}
if (clcf->name.data[clcf->name.len - 1] == '/') {
clcf->auto_redirect = 1;
}
return NGX_CONF_OK;
}
static char *
ngx_http_scgi_store(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_scgi_loc_conf_t *scf = conf;
ngx_str_t *value;
ngx_http_script_compile_t sc;
if (scf->upstream.store != NGX_CONF_UNSET) {
return "is duplicate";
}
value = cf->args->elts;
if (ngx_strcmp(value[1].data, "off") == 0) {
scf->upstream.store = 0;
return NGX_CONF_OK;
}
#if (NGX_HTTP_CACHE)
if (scf->upstream.cache > 0) {
return "is incompatible with \"scgi_cache\"";
}
#endif
scf->upstream.store = 1;
if (ngx_strcmp(value[1].data, "on") == 0) {
return NGX_CONF_OK;
}
/* include the terminating '\0' into script */
value[1].len++;
ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
sc.cf = cf;
sc.source = &value[1];
sc.lengths = &scf->upstream.store_lengths;
sc.values = &scf->upstream.store_values;
sc.variables = ngx_http_script_variables_count(&value[1]);
sc.complete_lengths = 1;
sc.complete_values = 1;
if (ngx_http_script_compile(&sc) != NGX_OK) {
return NGX_CONF_ERROR;
}
return NGX_CONF_OK;
}
#if (NGX_HTTP_CACHE)
static char *
ngx_http_scgi_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_scgi_loc_conf_t *scf = conf;
ngx_str_t *value;
ngx_http_complex_value_t cv;
ngx_http_compile_complex_value_t ccv;
value = cf->args->elts;
if (scf->upstream.cache != NGX_CONF_UNSET) {
return "is duplicate";
}
if (ngx_strcmp(value[1].data, "off") == 0) {
scf->upstream.cache = 0;
return NGX_CONF_OK;
}
if (scf->upstream.store > 0) {
return "is incompatible with \"scgi_store\"";
}
scf->upstream.cache = 1;
ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
ccv.cf = cf;
ccv.value = &value[1];
ccv.complex_value = &cv;
if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
return NGX_CONF_ERROR;
}
if (cv.lengths != NULL) {
scf->upstream.cache_value = ngx_palloc(cf->pool,
sizeof(ngx_http_complex_value_t));
if (scf->upstream.cache_value == NULL) {
return NGX_CONF_ERROR;
}
*scf->upstream.cache_value = cv;
return NGX_CONF_OK;
}
scf->upstream.cache_zone = ngx_shared_memory_add(cf, &value[1], 0,
&ngx_http_scgi_module);
if (scf->upstream.cache_zone == NULL) {
return NGX_CONF_ERROR;
}
return NGX_CONF_OK;
}
static char *
ngx_http_scgi_cache_key(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_scgi_loc_conf_t *scf = conf;
ngx_str_t *value;
ngx_http_compile_complex_value_t ccv;
value = cf->args->elts;
if (scf->cache_key.value.data) {
return "is duplicate";
}
ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
ccv.cf = cf;
ccv.value = &value[1];
ccv.complex_value = &scf->cache_key;
if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
return NGX_CONF_ERROR;
}
return NGX_CONF_OK;
}
#endif
nginx-1.14.0/src/http/modules/ngx_http_secure_link_module.c 000644 001751 001751 00000021622 13265410474 025264 0 ustar 00mdounin mdounin 000000 000000
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#include
#include
#include
#include
typedef struct {
ngx_http_complex_value_t *variable;
ngx_http_complex_value_t *md5;
ngx_str_t secret;
} ngx_http_secure_link_conf_t;
typedef struct {
ngx_str_t expires;
} ngx_http_secure_link_ctx_t;
static ngx_int_t ngx_http_secure_link_old_variable(ngx_http_request_t *r,
ngx_http_secure_link_conf_t *conf, ngx_http_variable_value_t *v,
uintptr_t data);
static ngx_int_t ngx_http_secure_link_expires_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static void *ngx_http_secure_link_create_conf(ngx_conf_t *cf);
static char *ngx_http_secure_link_merge_conf(ngx_conf_t *cf, void *parent,
void *child);
static ngx_int_t ngx_http_secure_link_add_variables(ngx_conf_t *cf);
static ngx_command_t ngx_http_secure_link_commands[] = {
{ ngx_string("secure_link"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_http_set_complex_value_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_secure_link_conf_t, variable),
NULL },
{ ngx_string("secure_link_md5"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_http_set_complex_value_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_secure_link_conf_t, md5),
NULL },
{ ngx_string("secure_link_secret"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_str_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_secure_link_conf_t, secret),
NULL },
ngx_null_command
};
static ngx_http_module_t ngx_http_secure_link_module_ctx = {
ngx_http_secure_link_add_variables, /* preconfiguration */
NULL, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
ngx_http_secure_link_create_conf, /* create location configuration */
ngx_http_secure_link_merge_conf /* merge location configuration */
};
ngx_module_t ngx_http_secure_link_module = {
NGX_MODULE_V1,
&ngx_http_secure_link_module_ctx, /* module context */
ngx_http_secure_link_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static ngx_str_t ngx_http_secure_link_name = ngx_string("secure_link");
static ngx_str_t ngx_http_secure_link_expires_name =
ngx_string("secure_link_expires");
static ngx_int_t
ngx_http_secure_link_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
u_char *p, *last;
ngx_str_t val, hash;
time_t expires;
ngx_md5_t md5;
ngx_http_secure_link_ctx_t *ctx;
ngx_http_secure_link_conf_t *conf;
u_char hash_buf[18], md5_buf[16];
conf = ngx_http_get_module_loc_conf(r, ngx_http_secure_link_module);
if (conf->secret.data) {
return ngx_http_secure_link_old_variable(r, conf, v, data);
}
if (conf->variable == NULL || conf->md5 == NULL) {
goto not_found;
}
if (ngx_http_complex_value(r, conf->variable, &val) != NGX_OK) {
return NGX_ERROR;
}
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"secure link: \"%V\"", &val);
last = val.data + val.len;
p = ngx_strlchr(val.data, last, ',');
expires = 0;
if (p) {
val.len = p++ - val.data;
expires = ngx_atotm(p, last - p);
if (expires <= 0) {
goto not_found;
}
ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_secure_link_ctx_t));
if (ctx == NULL) {
return NGX_ERROR;
}
ngx_http_set_ctx(r, ctx, ngx_http_secure_link_module);
ctx->expires.len = last - p;
ctx->expires.data = p;
}
if (val.len > 24) {
goto not_found;
}
hash.data = hash_buf;
if (ngx_decode_base64url(&hash, &val) != NGX_OK) {
goto not_found;
}
if (hash.len != 16) {
goto not_found;
}
if (ngx_http_complex_value(r, conf->md5, &val) != NGX_OK) {
return NGX_ERROR;
}
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"secure link md5: \"%V\"", &val);
ngx_md5_init(&md5);
ngx_md5_update(&md5, val.data, val.len);
ngx_md5_final(md5_buf, &md5);
if (ngx_memcmp(hash_buf, md5_buf, 16) != 0) {
goto not_found;
}
v->data = (u_char *) ((expires && expires < ngx_time()) ? "0" : "1");
v->len = 1;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
return NGX_OK;
not_found:
v->not_found = 1;
return NGX_OK;
}
static ngx_int_t
ngx_http_secure_link_old_variable(ngx_http_request_t *r,
ngx_http_secure_link_conf_t *conf, ngx_http_variable_value_t *v,
uintptr_t data)
{
u_char *p, *start, *end, *last;
size_t len;
ngx_int_t n;
ngx_uint_t i;
ngx_md5_t md5;
u_char hash[16];
p = &r->unparsed_uri.data[1];
last = r->unparsed_uri.data + r->unparsed_uri.len;
while (p < last) {
if (*p++ == '/') {
start = p;
goto md5_start;
}
}
goto not_found;
md5_start:
while (p < last) {
if (*p++ == '/') {
end = p - 1;
goto url_start;
}
}
goto not_found;
url_start:
len = last - p;
if (end - start != 32 || len == 0) {
goto not_found;
}
ngx_md5_init(&md5);
ngx_md5_update(&md5, p, len);
ngx_md5_update(&md5, conf->secret.data, conf->secret.len);
ngx_md5_final(hash, &md5);
for (i = 0; i < 16; i++) {
n = ngx_hextoi(&start[2 * i], 2);
if (n == NGX_ERROR || n != hash[i]) {
goto not_found;
}
}
v->len = len;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = p;
return NGX_OK;
not_found:
v->not_found = 1;
return NGX_OK;
}
static ngx_int_t
ngx_http_secure_link_expires_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
ngx_http_secure_link_ctx_t *ctx;
ctx = ngx_http_get_module_ctx(r, ngx_http_secure_link_module);
if (ctx) {
v->len = ctx->expires.len;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = ctx->expires.data;
} else {
v->not_found = 1;
}
return NGX_OK;
}
static void *
ngx_http_secure_link_create_conf(ngx_conf_t *cf)
{
ngx_http_secure_link_conf_t *conf;
conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_secure_link_conf_t));
if (conf == NULL) {
return NULL;
}
/*
* set by ngx_pcalloc():
*
* conf->variable = NULL;
* conf->md5 = NULL;
* conf->secret = { 0, NULL };
*/
return conf;
}
static char *
ngx_http_secure_link_merge_conf(ngx_conf_t *cf, void *parent, void *child)
{
ngx_http_secure_link_conf_t *prev = parent;
ngx_http_secure_link_conf_t *conf = child;
if (conf->secret.data) {
if (conf->variable || conf->md5) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"\"secure_link_secret\" cannot be mixed with "
"\"secure_link\" and \"secure_link_md5\"");
return NGX_CONF_ERROR;
}
return NGX_CONF_OK;
}
if (conf->variable == NULL) {
conf->variable = prev->variable;
}
if (conf->md5 == NULL) {
conf->md5 = prev->md5;
}
if (conf->variable == NULL && conf->md5 == NULL) {
conf->secret = prev->secret;
}
return NGX_CONF_OK;
}
static ngx_int_t
ngx_http_secure_link_add_variables(ngx_conf_t *cf)
{
ngx_http_variable_t *var;
var = ngx_http_add_variable(cf, &ngx_http_secure_link_name, 0);
if (var == NULL) {
return NGX_ERROR;
}
var->get_handler = ngx_http_secure_link_variable;
var = ngx_http_add_variable(cf, &ngx_http_secure_link_expires_name, 0);
if (var == NULL) {
return NGX_ERROR;
}
var->get_handler = ngx_http_secure_link_expires_variable;
return NGX_OK;
}
nginx-1.14.0/src/http/modules/ngx_http_ssl_module.c 000644 001751 001751 00000071630 13265410474 023566 0 ustar 00mdounin mdounin 000000 000000
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#include
#include
#include
typedef ngx_int_t (*ngx_ssl_variable_handler_pt)(ngx_connection_t *c,
ngx_pool_t *pool, ngx_str_t *s);
#define NGX_DEFAULT_CIPHERS "HIGH:!aNULL:!MD5"
#define NGX_DEFAULT_ECDH_CURVE "auto"
#define NGX_HTTP_NPN_ADVERTISE "\x08http/1.1"
#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
static int ngx_http_ssl_alpn_select(ngx_ssl_conn_t *ssl_conn,
const unsigned char **out, unsigned char *outlen,
const unsigned char *in, unsigned int inlen, void *arg);
#endif
#ifdef TLSEXT_TYPE_next_proto_neg
static int ngx_http_ssl_npn_advertised(ngx_ssl_conn_t *ssl_conn,
const unsigned char **out, unsigned int *outlen, void *arg);
#endif
static ngx_int_t ngx_http_ssl_static_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_ssl_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_ssl_add_variables(ngx_conf_t *cf);
static void *ngx_http_ssl_create_srv_conf(ngx_conf_t *cf);
static char *ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf,
void *parent, void *child);
static char *ngx_http_ssl_enable(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static char *ngx_http_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static char *ngx_http_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static ngx_int_t ngx_http_ssl_init(ngx_conf_t *cf);
static ngx_conf_bitmask_t ngx_http_ssl_protocols[] = {
{ ngx_string("SSLv2"), NGX_SSL_SSLv2 },
{ ngx_string("SSLv3"), NGX_SSL_SSLv3 },
{ ngx_string("TLSv1"), NGX_SSL_TLSv1 },
{ ngx_string("TLSv1.1"), NGX_SSL_TLSv1_1 },
{ ngx_string("TLSv1.2"), NGX_SSL_TLSv1_2 },
{ ngx_string("TLSv1.3"), NGX_SSL_TLSv1_3 },
{ ngx_null_string, 0 }
};
static ngx_conf_enum_t ngx_http_ssl_verify[] = {
{ ngx_string("off"), 0 },
{ ngx_string("on"), 1 },
{ ngx_string("optional"), 2 },
{ ngx_string("optional_no_ca"), 3 },
{ ngx_null_string, 0 }
};
static ngx_command_t ngx_http_ssl_commands[] = {
{ ngx_string("ssl"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,
ngx_http_ssl_enable,
NGX_HTTP_SRV_CONF_OFFSET,
offsetof(ngx_http_ssl_srv_conf_t, enable),
NULL },
{ ngx_string("ssl_certificate"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
ngx_conf_set_str_array_slot,
NGX_HTTP_SRV_CONF_OFFSET,
offsetof(ngx_http_ssl_srv_conf_t, certificates),
NULL },
{ ngx_string("ssl_certificate_key"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
ngx_conf_set_str_array_slot,
NGX_HTTP_SRV_CONF_OFFSET,
offsetof(ngx_http_ssl_srv_conf_t, certificate_keys),
NULL },
{ ngx_string("ssl_password_file"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
ngx_http_ssl_password_file,
NGX_HTTP_SRV_CONF_OFFSET,
0,
NULL },
{ ngx_string("ssl_dhparam"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
ngx_conf_set_str_slot,
NGX_HTTP_SRV_CONF_OFFSET,
offsetof(ngx_http_ssl_srv_conf_t, dhparam),
NULL },
{ ngx_string("ssl_ecdh_curve"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
ngx_conf_set_str_slot,
NGX_HTTP_SRV_CONF_OFFSET,
offsetof(ngx_http_ssl_srv_conf_t, ecdh_curve),
NULL },
{ ngx_string("ssl_protocols"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_1MORE,
ngx_conf_set_bitmask_slot,
NGX_HTTP_SRV_CONF_OFFSET,
offsetof(ngx_http_ssl_srv_conf_t, protocols),
&ngx_http_ssl_protocols },
{ ngx_string("ssl_ciphers"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
ngx_conf_set_str_slot,
NGX_HTTP_SRV_CONF_OFFSET,
offsetof(ngx_http_ssl_srv_conf_t, ciphers),
NULL },
{ ngx_string("ssl_buffer_size"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
ngx_conf_set_size_slot,
NGX_HTTP_SRV_CONF_OFFSET,
offsetof(ngx_http_ssl_srv_conf_t, buffer_size),
NULL },
{ ngx_string("ssl_verify_client"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
ngx_conf_set_enum_slot,
NGX_HTTP_SRV_CONF_OFFSET,
offsetof(ngx_http_ssl_srv_conf_t, verify),
&ngx_http_ssl_verify },
{ ngx_string("ssl_verify_depth"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
ngx_conf_set_num_slot,
NGX_HTTP_SRV_CONF_OFFSET,
offsetof(ngx_http_ssl_srv_conf_t, verify_depth),
NULL },
{ ngx_string("ssl_client_certificate"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
ngx_conf_set_str_slot,
NGX_HTTP_SRV_CONF_OFFSET,
offsetof(ngx_http_ssl_srv_conf_t, client_certificate),
NULL },
{ ngx_string("ssl_trusted_certificate"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
ngx_conf_set_str_slot,
NGX_HTTP_SRV_CONF_OFFSET,
offsetof(ngx_http_ssl_srv_conf_t, trusted_certificate),
NULL },
{ ngx_string("ssl_prefer_server_ciphers"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_HTTP_SRV_CONF_OFFSET,
offsetof(ngx_http_ssl_srv_conf_t, prefer_server_ciphers),
NULL },
{ ngx_string("ssl_session_cache"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE12,
ngx_http_ssl_session_cache,
NGX_HTTP_SRV_CONF_OFFSET,
0,
NULL },
{ ngx_string("ssl_session_tickets"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_HTTP_SRV_CONF_OFFSET,
offsetof(ngx_http_ssl_srv_conf_t, session_tickets),
NULL },
{ ngx_string("ssl_session_ticket_key"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
ngx_conf_set_str_array_slot,
NGX_HTTP_SRV_CONF_OFFSET,
offsetof(ngx_http_ssl_srv_conf_t, session_ticket_keys),
NULL },
{ ngx_string("ssl_session_timeout"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
ngx_conf_set_sec_slot,
NGX_HTTP_SRV_CONF_OFFSET,
offsetof(ngx_http_ssl_srv_conf_t, session_timeout),
NULL },
{ ngx_string("ssl_crl"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
ngx_conf_set_str_slot,
NGX_HTTP_SRV_CONF_OFFSET,
offsetof(ngx_http_ssl_srv_conf_t, crl),
NULL },
{ ngx_string("ssl_stapling"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_HTTP_SRV_CONF_OFFSET,
offsetof(ngx_http_ssl_srv_conf_t, stapling),
NULL },
{ ngx_string("ssl_stapling_file"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
ngx_conf_set_str_slot,
NGX_HTTP_SRV_CONF_OFFSET,
offsetof(ngx_http_ssl_srv_conf_t, stapling_file),
NULL },
{ ngx_string("ssl_stapling_responder"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
ngx_conf_set_str_slot,
NGX_HTTP_SRV_CONF_OFFSET,
offsetof(ngx_http_ssl_srv_conf_t, stapling_responder),
NULL },
{ ngx_string("ssl_stapling_verify"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_HTTP_SRV_CONF_OFFSET,
offsetof(ngx_http_ssl_srv_conf_t, stapling_verify),
NULL },
ngx_null_command
};
static ngx_http_module_t ngx_http_ssl_module_ctx = {
ngx_http_ssl_add_variables, /* preconfiguration */
ngx_http_ssl_init, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
ngx_http_ssl_create_srv_conf, /* create server configuration */
ngx_http_ssl_merge_srv_conf, /* merge server configuration */
NULL, /* create location configuration */
NULL /* merge location configuration */
};
ngx_module_t ngx_http_ssl_module = {
NGX_MODULE_V1,
&ngx_http_ssl_module_ctx, /* module context */
ngx_http_ssl_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static ngx_http_variable_t ngx_http_ssl_vars[] = {
{ ngx_string("ssl_protocol"), NULL, ngx_http_ssl_static_variable,
(uintptr_t) ngx_ssl_get_protocol, NGX_HTTP_VAR_CHANGEABLE, 0 },
{ ngx_string("ssl_cipher"), NULL, ngx_http_ssl_static_variable,
(uintptr_t) ngx_ssl_get_cipher_name, NGX_HTTP_VAR_CHANGEABLE, 0 },
{ ngx_string("ssl_ciphers"), NULL, ngx_http_ssl_variable,
(uintptr_t) ngx_ssl_get_ciphers, NGX_HTTP_VAR_CHANGEABLE, 0 },
{ ngx_string("ssl_curves"), NULL, ngx_http_ssl_variable,
(uintptr_t) ngx_ssl_get_curves, NGX_HTTP_VAR_CHANGEABLE, 0 },
{ ngx_string("ssl_session_id"), NULL, ngx_http_ssl_variable,
(uintptr_t) ngx_ssl_get_session_id, NGX_HTTP_VAR_CHANGEABLE, 0 },
{ ngx_string("ssl_session_reused"), NULL, ngx_http_ssl_variable,
(uintptr_t) ngx_ssl_get_session_reused, NGX_HTTP_VAR_CHANGEABLE, 0 },
{ ngx_string("ssl_server_name"), NULL, ngx_http_ssl_variable,
(uintptr_t) ngx_ssl_get_server_name, NGX_HTTP_VAR_CHANGEABLE, 0 },
{ ngx_string("ssl_client_cert"), NULL, ngx_http_ssl_variable,
(uintptr_t) ngx_ssl_get_certificate, NGX_HTTP_VAR_CHANGEABLE, 0 },
{ ngx_string("ssl_client_raw_cert"), NULL, ngx_http_ssl_variable,
(uintptr_t) ngx_ssl_get_raw_certificate,
NGX_HTTP_VAR_CHANGEABLE, 0 },
{ ngx_string("ssl_client_escaped_cert"), NULL, ngx_http_ssl_variable,
(uintptr_t) ngx_ssl_get_escaped_certificate,
NGX_HTTP_VAR_CHANGEABLE, 0 },
{ ngx_string("ssl_client_s_dn"), NULL, ngx_http_ssl_variable,
(uintptr_t) ngx_ssl_get_subject_dn, NGX_HTTP_VAR_CHANGEABLE, 0 },
{ ngx_string("ssl_client_i_dn"), NULL, ngx_http_ssl_variable,
(uintptr_t) ngx_ssl_get_issuer_dn, NGX_HTTP_VAR_CHANGEABLE, 0 },
{ ngx_string("ssl_client_s_dn_legacy"), NULL, ngx_http_ssl_variable,
(uintptr_t) ngx_ssl_get_subject_dn_legacy, NGX_HTTP_VAR_CHANGEABLE, 0 },
{ ngx_string("ssl_client_i_dn_legacy"), NULL, ngx_http_ssl_variable,
(uintptr_t) ngx_ssl_get_issuer_dn_legacy, NGX_HTTP_VAR_CHANGEABLE, 0 },
{ ngx_string("ssl_client_serial"), NULL, ngx_http_ssl_variable,
(uintptr_t) ngx_ssl_get_serial_number, NGX_HTTP_VAR_CHANGEABLE, 0 },
{ ngx_string("ssl_client_fingerprint"), NULL, ngx_http_ssl_variable,
(uintptr_t) ngx_ssl_get_fingerprint, NGX_HTTP_VAR_CHANGEABLE, 0 },
{ ngx_string("ssl_client_verify"), NULL, ngx_http_ssl_variable,
(uintptr_t) ngx_ssl_get_client_verify, NGX_HTTP_VAR_CHANGEABLE, 0 },
{ ngx_string("ssl_client_v_start"), NULL, ngx_http_ssl_variable,
(uintptr_t) ngx_ssl_get_client_v_start, NGX_HTTP_VAR_CHANGEABLE, 0 },
{ ngx_string("ssl_client_v_end"), NULL, ngx_http_ssl_variable,
(uintptr_t) ngx_ssl_get_client_v_end, NGX_HTTP_VAR_CHANGEABLE, 0 },
{ ngx_string("ssl_client_v_remain"), NULL, ngx_http_ssl_variable,
(uintptr_t) ngx_ssl_get_client_v_remain, NGX_HTTP_VAR_CHANGEABLE, 0 },
ngx_http_null_variable
};
static ngx_str_t ngx_http_ssl_sess_id_ctx = ngx_string("HTTP");
#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
static int
ngx_http_ssl_alpn_select(ngx_ssl_conn_t *ssl_conn, const unsigned char **out,
unsigned char *outlen, const unsigned char *in, unsigned int inlen,
void *arg)
{
unsigned int srvlen;
unsigned char *srv;
#if (NGX_DEBUG)
unsigned int i;
#endif
#if (NGX_HTTP_V2)
ngx_http_connection_t *hc;
#endif
#if (NGX_HTTP_V2 || NGX_DEBUG)
ngx_connection_t *c;
c = ngx_ssl_get_connection(ssl_conn);
#endif
#if (NGX_DEBUG)
for (i = 0; i < inlen; i += in[i] + 1) {
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
"SSL ALPN supported by client: %*s",
(size_t) in[i], &in[i + 1]);
}
#endif
#if (NGX_HTTP_V2)
hc = c->data;
if (hc->addr_conf->http2) {
srv =
(unsigned char *) NGX_HTTP_V2_ALPN_ADVERTISE NGX_HTTP_NPN_ADVERTISE;
srvlen = sizeof(NGX_HTTP_V2_ALPN_ADVERTISE NGX_HTTP_NPN_ADVERTISE) - 1;
} else
#endif
{
srv = (unsigned char *) NGX_HTTP_NPN_ADVERTISE;
srvlen = sizeof(NGX_HTTP_NPN_ADVERTISE) - 1;
}
if (SSL_select_next_proto((unsigned char **) out, outlen, srv, srvlen,
in, inlen)
!= OPENSSL_NPN_NEGOTIATED)
{
return SSL_TLSEXT_ERR_NOACK;
}
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
"SSL ALPN selected: %*s", (size_t) *outlen, *out);
return SSL_TLSEXT_ERR_OK;
}
#endif
#ifdef TLSEXT_TYPE_next_proto_neg
static int
ngx_http_ssl_npn_advertised(ngx_ssl_conn_t *ssl_conn,
const unsigned char **out, unsigned int *outlen, void *arg)
{
#if (NGX_HTTP_V2 || NGX_DEBUG)
ngx_connection_t *c;
c = ngx_ssl_get_connection(ssl_conn);
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "SSL NPN advertised");
#endif
#if (NGX_HTTP_V2)
{
ngx_http_connection_t *hc;
hc = c->data;
if (hc->addr_conf->http2) {
*out =
(unsigned char *) NGX_HTTP_V2_NPN_ADVERTISE NGX_HTTP_NPN_ADVERTISE;
*outlen = sizeof(NGX_HTTP_V2_NPN_ADVERTISE NGX_HTTP_NPN_ADVERTISE) - 1;
return SSL_TLSEXT_ERR_OK;
}
}
#endif
*out = (unsigned char *) NGX_HTTP_NPN_ADVERTISE;
*outlen = sizeof(NGX_HTTP_NPN_ADVERTISE) - 1;
return SSL_TLSEXT_ERR_OK;
}
#endif
static ngx_int_t
ngx_http_ssl_static_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
ngx_ssl_variable_handler_pt handler = (ngx_ssl_variable_handler_pt) data;
size_t len;
ngx_str_t s;
if (r->connection->ssl) {
(void) handler(r->connection, NULL, &s);
v->data = s.data;
for (len = 0; v->data[len]; len++) { /* void */ }
v->len = len;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
return NGX_OK;
}
v->not_found = 1;
return NGX_OK;
}
static ngx_int_t
ngx_http_ssl_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
uintptr_t data)
{
ngx_ssl_variable_handler_pt handler = (ngx_ssl_variable_handler_pt) data;
ngx_str_t s;
if (r->connection->ssl) {
if (handler(r->connection, r->pool, &s) != NGX_OK) {
return NGX_ERROR;
}
v->len = s.len;
v->data = s.data;
if (v->len) {
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
return NGX_OK;
}
}
v->not_found = 1;
return NGX_OK;
}
static ngx_int_t
ngx_http_ssl_add_variables(ngx_conf_t *cf)
{
ngx_http_variable_t *var, *v;
for (v = ngx_http_ssl_vars; v->name.len; v++) {
var = ngx_http_add_variable(cf, &v->name, v->flags);
if (var == NULL) {
return NGX_ERROR;
}
var->get_handler = v->get_handler;
var->data = v->data;
}
return NGX_OK;
}
static void *
ngx_http_ssl_create_srv_conf(ngx_conf_t *cf)
{
ngx_http_ssl_srv_conf_t *sscf;
sscf = ngx_pcalloc(cf->pool, sizeof(ngx_http_ssl_srv_conf_t));
if (sscf == NULL) {
return NULL;
}
/*
* set by ngx_pcalloc():
*
* sscf->protocols = 0;
* sscf->dhparam = { 0, NULL };
* sscf->ecdh_curve = { 0, NULL };
* sscf->client_certificate = { 0, NULL };
* sscf->trusted_certificate = { 0, NULL };
* sscf->crl = { 0, NULL };
* sscf->ciphers = { 0, NULL };
* sscf->shm_zone = NULL;
* sscf->stapling_file = { 0, NULL };
* sscf->stapling_responder = { 0, NULL };
*/
sscf->enable = NGX_CONF_UNSET;
sscf->prefer_server_ciphers = NGX_CONF_UNSET;
sscf->buffer_size = NGX_CONF_UNSET_SIZE;
sscf->verify = NGX_CONF_UNSET_UINT;
sscf->verify_depth = NGX_CONF_UNSET_UINT;
sscf->certificates = NGX_CONF_UNSET_PTR;
sscf->certificate_keys = NGX_CONF_UNSET_PTR;
sscf->passwords = NGX_CONF_UNSET_PTR;
sscf->builtin_session_cache = NGX_CONF_UNSET;
sscf->session_timeout = NGX_CONF_UNSET;
sscf->session_tickets = NGX_CONF_UNSET;
sscf->session_ticket_keys = NGX_CONF_UNSET_PTR;
sscf->stapling = NGX_CONF_UNSET;
sscf->stapling_verify = NGX_CONF_UNSET;
return sscf;
}
static char *
ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
{
ngx_http_ssl_srv_conf_t *prev = parent;
ngx_http_ssl_srv_conf_t *conf = child;
ngx_pool_cleanup_t *cln;
if (conf->enable == NGX_CONF_UNSET) {
if (prev->enable == NGX_CONF_UNSET) {
conf->enable = 0;
} else {
conf->enable = prev->enable;
conf->file = prev->file;
conf->line = prev->line;
}
}
ngx_conf_merge_value(conf->session_timeout,
prev->session_timeout, 300);
ngx_conf_merge_value(conf->prefer_server_ciphers,
prev->prefer_server_ciphers, 0);
ngx_conf_merge_bitmask_value(conf->protocols, prev->protocols,
(NGX_CONF_BITMASK_SET|NGX_SSL_TLSv1
|NGX_SSL_TLSv1_1|NGX_SSL_TLSv1_2));
ngx_conf_merge_size_value(conf->buffer_size, prev->buffer_size,
NGX_SSL_BUFSIZE);
ngx_conf_merge_uint_value(conf->verify, prev->verify, 0);
ngx_conf_merge_uint_value(conf->verify_depth, prev->verify_depth, 1);
ngx_conf_merge_ptr_value(conf->certificates, prev->certificates, NULL);
ngx_conf_merge_ptr_value(conf->certificate_keys, prev->certificate_keys,
NULL);
ngx_conf_merge_ptr_value(conf->passwords, prev->passwords, NULL);
ngx_conf_merge_str_value(conf->dhparam, prev->dhparam, "");
ngx_conf_merge_str_value(conf->client_certificate, prev->client_certificate,
"");
ngx_conf_merge_str_value(conf->trusted_certificate,
prev->trusted_certificate, "");
ngx_conf_merge_str_value(conf->crl, prev->crl, "");
ngx_conf_merge_str_value(conf->ecdh_curve, prev->ecdh_curve,
NGX_DEFAULT_ECDH_CURVE);
ngx_conf_merge_str_value(conf->ciphers, prev->ciphers, NGX_DEFAULT_CIPHERS);
ngx_conf_merge_value(conf->stapling, prev->stapling, 0);
ngx_conf_merge_value(conf->stapling_verify, prev->stapling_verify, 0);
ngx_conf_merge_str_value(conf->stapling_file, prev->stapling_file, "");
ngx_conf_merge_str_value(conf->stapling_responder,
prev->stapling_responder, "");
conf->ssl.log = cf->log;
if (conf->enable) {
if (conf->certificates == NULL) {
ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
"no \"ssl_certificate\" is defined for "
"the \"ssl\" directive in %s:%ui",
conf->file, conf->line);
return NGX_CONF_ERROR;
}
if (conf->certificate_keys == NULL) {
ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
"no \"ssl_certificate_key\" is defined for "
"the \"ssl\" directive in %s:%ui",
conf->file, conf->line);
return NGX_CONF_ERROR;
}
if (conf->certificate_keys->nelts < conf->certificates->nelts) {
ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
"no \"ssl_certificate_key\" is defined "
"for certificate \"%V\" and "
"the \"ssl\" directive in %s:%ui",
((ngx_str_t *) conf->certificates->elts)
+ conf->certificates->nelts - 1,
conf->file, conf->line);
return NGX_CONF_ERROR;
}
} else {
if (conf->certificates == NULL) {
return NGX_CONF_OK;
}
if (conf->certificate_keys == NULL
|| conf->certificate_keys->nelts < conf->certificates->nelts)
{
ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
"no \"ssl_certificate_key\" is defined "
"for certificate \"%V\"",
((ngx_str_t *) conf->certificates->elts)
+ conf->certificates->nelts - 1);
return NGX_CONF_ERROR;
}
}
if (ngx_ssl_create(&conf->ssl, conf->protocols, conf) != NGX_OK) {
return NGX_CONF_ERROR;
}
#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
if (SSL_CTX_set_tlsext_servername_callback(conf->ssl.ctx,
ngx_http_ssl_servername)
== 0)
{
ngx_log_error(NGX_LOG_WARN, cf->log, 0,
"nginx was built with SNI support, however, now it is linked "
"dynamically to an OpenSSL library which has no tlsext support, "
"therefore SNI is not available");
}
#endif
#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
SSL_CTX_set_alpn_select_cb(conf->ssl.ctx, ngx_http_ssl_alpn_select, NULL);
#endif
#ifdef TLSEXT_TYPE_next_proto_neg
SSL_CTX_set_next_protos_advertised_cb(conf->ssl.ctx,
ngx_http_ssl_npn_advertised, NULL);
#endif
cln = ngx_pool_cleanup_add(cf->pool, 0);
if (cln == NULL) {
return NGX_CONF_ERROR;
}
cln->handler = ngx_ssl_cleanup_ctx;
cln->data = &conf->ssl;
if (ngx_ssl_certificates(cf, &conf->ssl, conf->certificates,
conf->certificate_keys, conf->passwords)
!= NGX_OK)
{
return NGX_CONF_ERROR;
}
if (ngx_ssl_ciphers(cf, &conf->ssl, &conf->ciphers,
conf->prefer_server_ciphers)
!= NGX_OK)
{
return NGX_CONF_ERROR;
}
conf->ssl.buffer_size = conf->buffer_size;
if (conf->verify) {
if (conf->client_certificate.len == 0 && conf->verify != 3) {
ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
"no ssl_client_certificate for ssl_client_verify");
return NGX_CONF_ERROR;
}
if (ngx_ssl_client_certificate(cf, &conf->ssl,
&conf->client_certificate,
conf->verify_depth)
!= NGX_OK)
{
return NGX_CONF_ERROR;
}
}
if (ngx_ssl_trusted_certificate(cf, &conf->ssl,
&conf->trusted_certificate,
conf->verify_depth)
!= NGX_OK)
{
return NGX_CONF_ERROR;
}
if (ngx_ssl_crl(cf, &conf->ssl, &conf->crl) != NGX_OK) {
return NGX_CONF_ERROR;
}
if (ngx_ssl_dhparam(cf, &conf->ssl, &conf->dhparam) != NGX_OK) {
return NGX_CONF_ERROR;
}
if (ngx_ssl_ecdh_curve(cf, &conf->ssl, &conf->ecdh_curve) != NGX_OK) {
return NGX_CONF_ERROR;
}
ngx_conf_merge_value(conf->builtin_session_cache,
prev->builtin_session_cache, NGX_SSL_NONE_SCACHE);
if (conf->shm_zone == NULL) {
conf->shm_zone = prev->shm_zone;
}
if (ngx_ssl_session_cache(&conf->ssl, &ngx_http_ssl_sess_id_ctx,
conf->builtin_session_cache,
conf->shm_zone, conf->session_timeout)
!= NGX_OK)
{
return NGX_CONF_ERROR;
}
ngx_conf_merge_value(conf->session_tickets, prev->session_tickets, 1);
#ifdef SSL_OP_NO_TICKET
if (!conf->session_tickets) {
SSL_CTX_set_options(conf->ssl.ctx, SSL_OP_NO_TICKET);
}
#endif
ngx_conf_merge_ptr_value(conf->session_ticket_keys,
prev->session_ticket_keys, NULL);
if (ngx_ssl_session_ticket_keys(cf, &conf->ssl, conf->session_ticket_keys)
!= NGX_OK)
{
return NGX_CONF_ERROR;
}
if (conf->stapling) {
if (ngx_ssl_stapling(cf, &conf->ssl, &conf->stapling_file,
&conf->stapling_responder, conf->stapling_verify)
!= NGX_OK)
{
return NGX_CONF_ERROR;
}
}
return NGX_CONF_OK;
}
static char *
ngx_http_ssl_enable(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_ssl_srv_conf_t *sscf = conf;
char *rv;
rv = ngx_conf_set_flag_slot(cf, cmd, conf);
if (rv != NGX_CONF_OK) {
return rv;
}
sscf->file = cf->conf_file->file.name.data;
sscf->line = cf->conf_file->line;
return NGX_CONF_OK;
}
static char *
ngx_http_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_ssl_srv_conf_t *sscf = conf;
ngx_str_t *value;
if (sscf->passwords != NGX_CONF_UNSET_PTR) {
return "is duplicate";
}
value = cf->args->elts;
sscf->passwords = ngx_ssl_read_password_file(cf, &value[1]);
if (sscf->passwords == NULL) {
return NGX_CONF_ERROR;
}
return NGX_CONF_OK;
}
static char *
ngx_http_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_ssl_srv_conf_t *sscf = conf;
size_t len;
ngx_str_t *value, name, size;
ngx_int_t n;
ngx_uint_t i, j;
value = cf->args->elts;
for (i = 1; i < cf->args->nelts; i++) {
if (ngx_strcmp(value[i].data, "off") == 0) {
sscf->builtin_session_cache = NGX_SSL_NO_SCACHE;
continue;
}
if (ngx_strcmp(value[i].data, "none") == 0) {
sscf->builtin_session_cache = NGX_SSL_NONE_SCACHE;
continue;
}
if (ngx_strcmp(value[i].data, "builtin") == 0) {
sscf->builtin_session_cache = NGX_SSL_DFLT_BUILTIN_SCACHE;
continue;
}
if (value[i].len > sizeof("builtin:") - 1
&& ngx_strncmp(value[i].data, "builtin:", sizeof("builtin:") - 1)
== 0)
{
n = ngx_atoi(value[i].data + sizeof("builtin:") - 1,
value[i].len - (sizeof("builtin:") - 1));
if (n == NGX_ERROR) {
goto invalid;
}
sscf->builtin_session_cache = n;
continue;
}
if (value[i].len > sizeof("shared:") - 1
&& ngx_strncmp(value[i].data, "shared:", sizeof("shared:") - 1)
== 0)
{
len = 0;
for (j = sizeof("shared:") - 1; j < value[i].len; j++) {
if (value[i].data[j] == ':') {
break;
}
len++;
}
if (len == 0) {
goto invalid;
}
name.len = len;
name.data = value[i].data + sizeof("shared:") - 1;
size.len = value[i].len - j - 1;
size.data = name.data + len + 1;
n = ngx_parse_size(&size);
if (n == NGX_ERROR) {
goto invalid;
}
if (n < (ngx_int_t) (8 * ngx_pagesize)) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"session cache \"%V\" is too small",
&value[i]);
return NGX_CONF_ERROR;
}
sscf->shm_zone = ngx_shared_memory_add(cf, &name, n,
&ngx_http_ssl_module);
if (sscf->shm_zone == NULL) {
return NGX_CONF_ERROR;
}
sscf->shm_zone->init = ngx_ssl_session_cache_init;
continue;
}
goto invalid;
}
if (sscf->shm_zone && sscf->builtin_session_cache == NGX_CONF_UNSET) {
sscf->builtin_session_cache = NGX_SSL_NO_BUILTIN_SCACHE;
}
return NGX_CONF_OK;
invalid:
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid session cache \"%V\"", &value[i]);
return NGX_CONF_ERROR;
}
static ngx_int_t
ngx_http_ssl_init(ngx_conf_t *cf)
{
ngx_uint_t s;
ngx_http_ssl_srv_conf_t *sscf;
ngx_http_core_loc_conf_t *clcf;
ngx_http_core_srv_conf_t **cscfp;
ngx_http_core_main_conf_t *cmcf;
cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
cscfp = cmcf->servers.elts;
for (s = 0; s < cmcf->servers.nelts; s++) {
sscf = cscfp[s]->ctx->srv_conf[ngx_http_ssl_module.ctx_index];
if (sscf->ssl.ctx == NULL || !sscf->stapling) {
continue;
}
clcf = cscfp[s]->ctx->loc_conf[ngx_http_core_module.ctx_index];
if (ngx_ssl_stapling_resolver(cf, &sscf->ssl, clcf->resolver,
clcf->resolver_timeout)
!= NGX_OK)
{
return NGX_ERROR;
}
}
return NGX_OK;
}
nginx-1.14.0/src/http/modules/ngx_http_slice_filter_module.c 000644 001751 001751 00000033101 13265410474 025420 0 ustar 00mdounin mdounin 000000 000000
/*
* Copyright (C) Roman Arutyunyan
* Copyright (C) Nginx, Inc.
*/
#include
#include
#include
typedef struct {
size_t size;
} ngx_http_slice_loc_conf_t;
typedef struct {
off_t start;
off_t end;
ngx_str_t range;
ngx_str_t etag;
unsigned last:1;
unsigned active:1;
ngx_http_request_t *sr;
} ngx_http_slice_ctx_t;
typedef struct {
off_t start;
off_t end;
off_t complete_length;
} ngx_http_slice_content_range_t;
static ngx_int_t ngx_http_slice_header_filter(ngx_http_request_t *r);
static ngx_int_t ngx_http_slice_body_filter(ngx_http_request_t *r,
ngx_chain_t *in);
static ngx_int_t ngx_http_slice_parse_content_range(ngx_http_request_t *r,
ngx_http_slice_content_range_t *cr);
static ngx_int_t ngx_http_slice_range_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static off_t ngx_http_slice_get_start(ngx_http_request_t *r);
static void *ngx_http_slice_create_loc_conf(ngx_conf_t *cf);
static char *ngx_http_slice_merge_loc_conf(ngx_conf_t *cf, void *parent,
void *child);
static ngx_int_t ngx_http_slice_add_variables(ngx_conf_t *cf);
static ngx_int_t ngx_http_slice_init(ngx_conf_t *cf);
static ngx_command_t ngx_http_slice_filter_commands[] = {
{ ngx_string("slice"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_size_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_slice_loc_conf_t, size),
NULL },
ngx_null_command
};
static ngx_http_module_t ngx_http_slice_filter_module_ctx = {
ngx_http_slice_add_variables, /* preconfiguration */
ngx_http_slice_init, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
ngx_http_slice_create_loc_conf, /* create location configuration */
ngx_http_slice_merge_loc_conf /* merge location configuration */
};
ngx_module_t ngx_http_slice_filter_module = {
NGX_MODULE_V1,
&ngx_http_slice_filter_module_ctx, /* module context */
ngx_http_slice_filter_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static ngx_str_t ngx_http_slice_range_name = ngx_string("slice_range");
static ngx_http_output_header_filter_pt ngx_http_next_header_filter;
static ngx_http_output_body_filter_pt ngx_http_next_body_filter;
static ngx_int_t
ngx_http_slice_header_filter(ngx_http_request_t *r)
{
off_t end;
ngx_int_t rc;
ngx_table_elt_t *h;
ngx_http_slice_ctx_t *ctx;
ngx_http_slice_loc_conf_t *slcf;
ngx_http_slice_content_range_t cr;
ctx = ngx_http_get_module_ctx(r, ngx_http_slice_filter_module);
if (ctx == NULL) {
return ngx_http_next_header_filter(r);
}
if (r->headers_out.status != NGX_HTTP_PARTIAL_CONTENT) {
if (r == r->main) {
ngx_http_set_ctx(r, NULL, ngx_http_slice_filter_module);
return ngx_http_next_header_filter(r);
}
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"unexpected status code %ui in slice response",
r->headers_out.status);
return NGX_ERROR;
}
h = r->headers_out.etag;
if (ctx->etag.len) {
if (h == NULL
|| h->value.len != ctx->etag.len
|| ngx_strncmp(h->value.data, ctx->etag.data, ctx->etag.len)
!= 0)
{
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"etag mismatch in slice response");
return NGX_ERROR;
}
}
if (h) {
ctx->etag = h->value;
}
if (ngx_http_slice_parse_content_range(r, &cr) != NGX_OK) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"invalid range in slice response");
return NGX_ERROR;
}
if (cr.complete_length == -1) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"no complete length in slice response");
return NGX_ERROR;
}
ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http slice response range: %O-%O/%O",
cr.start, cr.end, cr.complete_length);
slcf = ngx_http_get_module_loc_conf(r, ngx_http_slice_filter_module);
end = ngx_min(cr.start + (off_t) slcf->size, cr.complete_length);
if (cr.start != ctx->start || cr.end != end) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"unexpected range in slice response: %O-%O",
cr.start, cr.end);
return NGX_ERROR;
}
ctx->start = end;
ctx->active = 1;
r->headers_out.status = NGX_HTTP_OK;
r->headers_out.status_line.len = 0;
r->headers_out.content_length_n = cr.complete_length;
r->headers_out.content_offset = cr.start;
r->headers_out.content_range->hash = 0;
r->headers_out.content_range = NULL;
r->allow_ranges = 1;
r->subrequest_ranges = 1;
r->single_range = 1;
rc = ngx_http_next_header_filter(r);
if (r != r->main) {
return rc;
}
r->preserve_body = 1;
if (r->headers_out.status == NGX_HTTP_PARTIAL_CONTENT) {
if (ctx->start + (off_t) slcf->size <= r->headers_out.content_offset) {
ctx->start = slcf->size
* (r->headers_out.content_offset / slcf->size);
}
ctx->end = r->headers_out.content_offset
+ r->headers_out.content_length_n;
} else {
ctx->end = cr.complete_length;
}
return rc;
}
static ngx_int_t
ngx_http_slice_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
ngx_int_t rc;
ngx_chain_t *cl;
ngx_http_slice_ctx_t *ctx;
ngx_http_slice_loc_conf_t *slcf;
ctx = ngx_http_get_module_ctx(r, ngx_http_slice_filter_module);
if (ctx == NULL || r != r->main) {
return ngx_http_next_body_filter(r, in);
}
for (cl = in; cl; cl = cl->next) {
if (cl->buf->last_buf) {
cl->buf->last_buf = 0;
cl->buf->last_in_chain = 1;
cl->buf->sync = 1;
ctx->last = 1;
}
}
rc = ngx_http_next_body_filter(r, in);
if (rc == NGX_ERROR || !ctx->last) {
return rc;
}
if (ctx->sr && !ctx->sr->done) {
return rc;
}
if (!ctx->active) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"missing slice response");
return NGX_ERROR;
}
if (ctx->start >= ctx->end) {
ngx_http_set_ctx(r, NULL, ngx_http_slice_filter_module);
ngx_http_send_special(r, NGX_HTTP_LAST);
return rc;
}
if (r->buffered) {
return rc;
}
if (ngx_http_subrequest(r, &r->uri, &r->args, &ctx->sr, NULL,
NGX_HTTP_SUBREQUEST_CLONE)
!= NGX_OK)
{
return NGX_ERROR;
}
ngx_http_set_ctx(ctx->sr, ctx, ngx_http_slice_filter_module);
slcf = ngx_http_get_module_loc_conf(r, ngx_http_slice_filter_module);
ctx->range.len = ngx_sprintf(ctx->range.data, "bytes=%O-%O", ctx->start,
ctx->start + (off_t) slcf->size - 1)
- ctx->range.data;
ctx->active = 0;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http slice subrequest: \"%V\"", &ctx->range);
return rc;
}
static ngx_int_t
ngx_http_slice_parse_content_range(ngx_http_request_t *r,
ngx_http_slice_content_range_t *cr)
{
off_t start, end, complete_length, cutoff, cutlim;
u_char *p;
ngx_table_elt_t *h;
h = r->headers_out.content_range;
if (h == NULL
|| h->value.len < 7
|| ngx_strncmp(h->value.data, "bytes ", 6) != 0)
{
return NGX_ERROR;
}
p = h->value.data + 6;
cutoff = NGX_MAX_OFF_T_VALUE / 10;
cutlim = NGX_MAX_OFF_T_VALUE % 10;
start = 0;
end = 0;
complete_length = 0;
while (*p == ' ') { p++; }
if (*p < '0' || *p > '9') {
return NGX_ERROR;
}
while (*p >= '0' && *p <= '9') {
if (start >= cutoff && (start > cutoff || *p - '0' > cutlim)) {
return NGX_ERROR;
}
start = start * 10 + (*p++ - '0');
}
while (*p == ' ') { p++; }
if (*p++ != '-') {
return NGX_ERROR;
}
while (*p == ' ') { p++; }
if (*p < '0' || *p > '9') {
return NGX_ERROR;
}
while (*p >= '0' && *p <= '9') {
if (end >= cutoff && (end > cutoff || *p - '0' > cutlim)) {
return NGX_ERROR;
}
end = end * 10 + (*p++ - '0');
}
end++;
while (*p == ' ') { p++; }
if (*p++ != '/') {
return NGX_ERROR;
}
while (*p == ' ') { p++; }
if (*p != '*') {
if (*p < '0' || *p > '9') {
return NGX_ERROR;
}
while (*p >= '0' && *p <= '9') {
if (complete_length >= cutoff
&& (complete_length > cutoff || *p - '0' > cutlim))
{
return NGX_ERROR;
}
complete_length = complete_length * 10 + (*p++ - '0');
}
} else {
complete_length = -1;
p++;
}
while (*p == ' ') { p++; }
if (*p != '\0') {
return NGX_ERROR;
}
cr->start = start;
cr->end = end;
cr->complete_length = complete_length;
return NGX_OK;
}
static ngx_int_t
ngx_http_slice_range_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
u_char *p;
ngx_http_slice_ctx_t *ctx;
ngx_http_slice_loc_conf_t *slcf;
ctx = ngx_http_get_module_ctx(r, ngx_http_slice_filter_module);
if (ctx == NULL) {
if (r != r->main || r->headers_out.status) {
v->not_found = 1;
return NGX_OK;
}
slcf = ngx_http_get_module_loc_conf(r, ngx_http_slice_filter_module);
if (slcf->size == 0) {
v->not_found = 1;
return NGX_OK;
}
ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_slice_ctx_t));
if (ctx == NULL) {
return NGX_ERROR;
}
ngx_http_set_ctx(r, ctx, ngx_http_slice_filter_module);
p = ngx_pnalloc(r->pool, sizeof("bytes=-") - 1 + 2 * NGX_OFF_T_LEN);
if (p == NULL) {
return NGX_ERROR;
}
ctx->start = slcf->size * (ngx_http_slice_get_start(r) / slcf->size);
ctx->range.data = p;
ctx->range.len = ngx_sprintf(p, "bytes=%O-%O", ctx->start,
ctx->start + (off_t) slcf->size - 1)
- p;
}
v->data = ctx->range.data;
v->valid = 1;
v->not_found = 0;
v->no_cacheable = 1;
v->len = ctx->range.len;
return NGX_OK;
}
static off_t
ngx_http_slice_get_start(ngx_http_request_t *r)
{
off_t start, cutoff, cutlim;
u_char *p;
ngx_table_elt_t *h;
if (r->headers_in.if_range) {
return 0;
}
h = r->headers_in.range;
if (h == NULL
|| h->value.len < 7
|| ngx_strncasecmp(h->value.data, (u_char *) "bytes=", 6) != 0)
{
return 0;
}
p = h->value.data + 6;
if (ngx_strchr(p, ',')) {
return 0;
}
while (*p == ' ') { p++; }
if (*p == '-') {
return 0;
}
cutoff = NGX_MAX_OFF_T_VALUE / 10;
cutlim = NGX_MAX_OFF_T_VALUE % 10;
start = 0;
while (*p >= '0' && *p <= '9') {
if (start >= cutoff && (start > cutoff || *p - '0' > cutlim)) {
return 0;
}
start = start * 10 + (*p++ - '0');
}
return start;
}
static void *
ngx_http_slice_create_loc_conf(ngx_conf_t *cf)
{
ngx_http_slice_loc_conf_t *slcf;
slcf = ngx_palloc(cf->pool, sizeof(ngx_http_slice_loc_conf_t));
if (slcf == NULL) {
return NULL;
}
slcf->size = NGX_CONF_UNSET_SIZE;
return slcf;
}
static char *
ngx_http_slice_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
{
ngx_http_slice_loc_conf_t *prev = parent;
ngx_http_slice_loc_conf_t *conf = child;
ngx_conf_merge_size_value(conf->size, prev->size, 0);
return NGX_CONF_OK;
}
static ngx_int_t
ngx_http_slice_add_variables(ngx_conf_t *cf)
{
ngx_http_variable_t *var;
var = ngx_http_add_variable(cf, &ngx_http_slice_range_name, 0);
if (var == NULL) {
return NGX_ERROR;
}
var->get_handler = ngx_http_slice_range_variable;
return NGX_OK;
}
static ngx_int_t
ngx_http_slice_init(ngx_conf_t *cf)
{
ngx_http_next_header_filter = ngx_http_top_header_filter;
ngx_http_top_header_filter = ngx_http_slice_header_filter;
ngx_http_next_body_filter = ngx_http_top_body_filter;
ngx_http_top_body_filter = ngx_http_slice_body_filter;
return NGX_OK;
}
nginx-1.14.0/src/http/modules/ngx_http_split_clients_module.c 000644 001751 001751 00000014727 13265410474 025645 0 ustar 00mdounin mdounin 000000 000000
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#include
#include
#include
typedef struct {
uint32_t percent;
ngx_http_variable_value_t value;
} ngx_http_split_clients_part_t;
typedef struct {
ngx_http_complex_value_t value;
ngx_array_t parts;
} ngx_http_split_clients_ctx_t;
static char *ngx_conf_split_clients_block(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static char *ngx_http_split_clients(ngx_conf_t *cf, ngx_command_t *dummy,
void *conf);
static ngx_command_t ngx_http_split_clients_commands[] = {
{ ngx_string("split_clients"),
NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE2,
ngx_conf_split_clients_block,
NGX_HTTP_MAIN_CONF_OFFSET,
0,
NULL },
ngx_null_command
};
static ngx_http_module_t ngx_http_split_clients_module_ctx = {
NULL, /* preconfiguration */
NULL, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
NULL, /* create location configuration */
NULL /* merge location configuration */
};
ngx_module_t ngx_http_split_clients_module = {
NGX_MODULE_V1,
&ngx_http_split_clients_module_ctx, /* module context */
ngx_http_split_clients_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static ngx_int_t
ngx_http_split_clients_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
ngx_http_split_clients_ctx_t *ctx = (ngx_http_split_clients_ctx_t *) data;
uint32_t hash;
ngx_str_t val;
ngx_uint_t i;
ngx_http_split_clients_part_t *part;
*v = ngx_http_variable_null_value;
if (ngx_http_complex_value(r, &ctx->value, &val) != NGX_OK) {
return NGX_OK;
}
hash = ngx_murmur_hash2(val.data, val.len);
part = ctx->parts.elts;
for (i = 0; i < ctx->parts.nelts; i++) {
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http split: %uD %uD", hash, part[i].percent);
if (hash < part[i].percent || part[i].percent == 0) {
*v = part[i].value;
return NGX_OK;
}
}
return NGX_OK;
}
static char *
ngx_conf_split_clients_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
char *rv;
uint32_t sum, last;
ngx_str_t *value, name;
ngx_uint_t i;
ngx_conf_t save;
ngx_http_variable_t *var;
ngx_http_split_clients_ctx_t *ctx;
ngx_http_split_clients_part_t *part;
ngx_http_compile_complex_value_t ccv;
ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_split_clients_ctx_t));
if (ctx == NULL) {
return NGX_CONF_ERROR;
}
value = cf->args->elts;
ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
ccv.cf = cf;
ccv.value = &value[1];
ccv.complex_value = &ctx->value;
if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
return NGX_CONF_ERROR;
}
name = value[2];
if (name.data[0] != '$') {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid variable name \"%V\"", &name);
return NGX_CONF_ERROR;
}
name.len--;
name.data++;
var = ngx_http_add_variable(cf, &name, NGX_HTTP_VAR_CHANGEABLE);
if (var == NULL) {
return NGX_CONF_ERROR;
}
var->get_handler = ngx_http_split_clients_variable;
var->data = (uintptr_t) ctx;
if (ngx_array_init(&ctx->parts, cf->pool, 2,
sizeof(ngx_http_split_clients_part_t))
!= NGX_OK)
{
return NGX_CONF_ERROR;
}
save = *cf;
cf->ctx = ctx;
cf->handler = ngx_http_split_clients;
cf->handler_conf = conf;
rv = ngx_conf_parse(cf, NULL);
*cf = save;
if (rv != NGX_CONF_OK) {
return rv;
}
sum = 0;
last = 0;
part = ctx->parts.elts;
for (i = 0; i < ctx->parts.nelts; i++) {
sum = part[i].percent ? sum + part[i].percent : 10000;
if (sum > 10000) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"percent total is greater than 100%%");
return NGX_CONF_ERROR;
}
if (part[i].percent) {
last += part[i].percent * (uint64_t) 0xffffffff / 10000;
part[i].percent = last;
}
}
return rv;
}
static char *
ngx_http_split_clients(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
{
ngx_int_t n;
ngx_str_t *value;
ngx_http_split_clients_ctx_t *ctx;
ngx_http_split_clients_part_t *part;
ctx = cf->ctx;
value = cf->args->elts;
part = ngx_array_push(&ctx->parts);
if (part == NULL) {
return NGX_CONF_ERROR;
}
if (value[0].len == 1 && value[0].data[0] == '*') {
part->percent = 0;
} else {
if (value[0].len == 0 || value[0].data[value[0].len - 1] != '%') {
goto invalid;
}
n = ngx_atofp(value[0].data, value[0].len - 1, 2);
if (n == NGX_ERROR || n == 0) {
goto invalid;
}
part->percent = (uint32_t) n;
}
part->value.len = value[1].len;
part->value.valid = 1;
part->value.no_cacheable = 0;
part->value.not_found = 0;
part->value.data = value[1].data;
return NGX_CONF_OK;
invalid:
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid percent value \"%V\"", &value[0]);
return NGX_CONF_ERROR;
}
nginx-1.14.0/src/http/modules/ngx_http_ssi_filter_module.c 000644 001751 001751 00000233506 13265410474 025132 0 ustar 00mdounin mdounin 000000 000000
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#include
#include
#include
#define NGX_HTTP_SSI_ERROR 1
#define NGX_HTTP_SSI_DATE_LEN 2048
#define NGX_HTTP_SSI_ADD_PREFIX 1
#define NGX_HTTP_SSI_ADD_ZERO 2
typedef struct {
ngx_flag_t enable;
ngx_flag_t silent_errors;
ngx_flag_t ignore_recycled_buffers;
ngx_flag_t last_modified;
ngx_hash_t types;
size_t min_file_chunk;
size_t value_len;
ngx_array_t *types_keys;
} ngx_http_ssi_loc_conf_t;
typedef struct {
ngx_str_t name;
ngx_uint_t key;
ngx_str_t value;
} ngx_http_ssi_var_t;
typedef struct {
ngx_str_t name;
ngx_chain_t *bufs;
ngx_uint_t count;
} ngx_http_ssi_block_t;
typedef enum {
ssi_start_state = 0,
ssi_tag_state,
ssi_comment0_state,
ssi_comment1_state,
ssi_sharp_state,
ssi_precommand_state,
ssi_command_state,
ssi_preparam_state,
ssi_param_state,
ssi_preequal_state,
ssi_prevalue_state,
ssi_double_quoted_value_state,
ssi_quoted_value_state,
ssi_quoted_symbol_state,
ssi_postparam_state,
ssi_comment_end0_state,
ssi_comment_end1_state,
ssi_error_state,
ssi_error_end0_state,
ssi_error_end1_state
} ngx_http_ssi_state_e;
static ngx_int_t ngx_http_ssi_output(ngx_http_request_t *r,
ngx_http_ssi_ctx_t *ctx);
static void ngx_http_ssi_buffered(ngx_http_request_t *r,
ngx_http_ssi_ctx_t *ctx);
static ngx_int_t ngx_http_ssi_parse(ngx_http_request_t *r,
ngx_http_ssi_ctx_t *ctx);
static ngx_str_t *ngx_http_ssi_get_variable(ngx_http_request_t *r,
ngx_str_t *name, ngx_uint_t key);
static ngx_int_t ngx_http_ssi_evaluate_string(ngx_http_request_t *r,
ngx_http_ssi_ctx_t *ctx, ngx_str_t *text, ngx_uint_t flags);
static ngx_int_t ngx_http_ssi_regex_match(ngx_http_request_t *r,
ngx_str_t *pattern, ngx_str_t *str);
static ngx_int_t ngx_http_ssi_include(ngx_http_request_t *r,
ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);
static ngx_int_t ngx_http_ssi_stub_output(ngx_http_request_t *r, void *data,
ngx_int_t rc);
static ngx_int_t ngx_http_ssi_set_variable(ngx_http_request_t *r, void *data,
ngx_int_t rc);
static ngx_int_t ngx_http_ssi_echo(ngx_http_request_t *r,
ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);
static ngx_int_t ngx_http_ssi_config(ngx_http_request_t *r,
ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);
static ngx_int_t ngx_http_ssi_set(ngx_http_request_t *r,
ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);
static ngx_int_t ngx_http_ssi_if(ngx_http_request_t *r,
ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);
static ngx_int_t ngx_http_ssi_else(ngx_http_request_t *r,
ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);
static ngx_int_t ngx_http_ssi_endif(ngx_http_request_t *r,
ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);
static ngx_int_t ngx_http_ssi_block(ngx_http_request_t *r,
ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);
static ngx_int_t ngx_http_ssi_endblock(ngx_http_request_t *r,
ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);
static ngx_int_t ngx_http_ssi_date_gmt_local_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t gmt);
static ngx_int_t ngx_http_ssi_preconfiguration(ngx_conf_t *cf);
static void *ngx_http_ssi_create_main_conf(ngx_conf_t *cf);
static char *ngx_http_ssi_init_main_conf(ngx_conf_t *cf, void *conf);
static void *ngx_http_ssi_create_loc_conf(ngx_conf_t *cf);
static char *ngx_http_ssi_merge_loc_conf(ngx_conf_t *cf,
void *parent, void *child);
static ngx_int_t ngx_http_ssi_filter_init(ngx_conf_t *cf);
static ngx_command_t ngx_http_ssi_filter_commands[] = {
{ ngx_string("ssi"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_ssi_loc_conf_t, enable),
NULL },
{ ngx_string("ssi_silent_errors"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_ssi_loc_conf_t, silent_errors),
NULL },
{ ngx_string("ssi_ignore_recycled_buffers"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_ssi_loc_conf_t, ignore_recycled_buffers),
NULL },
{ ngx_string("ssi_min_file_chunk"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_size_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_ssi_loc_conf_t, min_file_chunk),
NULL },
{ ngx_string("ssi_value_length"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_size_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_ssi_loc_conf_t, value_len),
NULL },
{ ngx_string("ssi_types"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
ngx_http_types_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_ssi_loc_conf_t, types_keys),
&ngx_http_html_default_types[0] },
{ ngx_string("ssi_last_modified"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_ssi_loc_conf_t, last_modified),
NULL },
ngx_null_command
};
static ngx_http_module_t ngx_http_ssi_filter_module_ctx = {
ngx_http_ssi_preconfiguration, /* preconfiguration */
ngx_http_ssi_filter_init, /* postconfiguration */
ngx_http_ssi_create_main_conf, /* create main configuration */
ngx_http_ssi_init_main_conf, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
ngx_http_ssi_create_loc_conf, /* create location configuration */
ngx_http_ssi_merge_loc_conf /* merge location configuration */
};
ngx_module_t ngx_http_ssi_filter_module = {
NGX_MODULE_V1,
&ngx_http_ssi_filter_module_ctx, /* module context */
ngx_http_ssi_filter_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static ngx_http_output_header_filter_pt ngx_http_next_header_filter;
static ngx_http_output_body_filter_pt ngx_http_next_body_filter;
static u_char ngx_http_ssi_string[] = "