nginx-1.14.0/000755 001751 001751 00000000000 13265410502 014104 5ustar00mdouninmdounin000000 000000 nginx-1.14.0/auto/000755 001751 001751 00000000000 13265410475 015065 5ustar00mdouninmdounin000000 000000 nginx-1.14.0/conf/000755 001751 001751 00000000000 13265410474 015041 5ustar00mdouninmdounin000000 000000 nginx-1.14.0/contrib/000755 001751 001751 00000000000 13265410474 015554 5ustar00mdouninmdounin000000 000000 nginx-1.14.0/src/000755 001751 001751 00000000000 13265410475 014704 5ustar00mdouninmdounin000000 000000 nginx-1.14.0/configure000755 001751 001751 00000004706 13265410474 016032 0ustar00mdouninmdounin000000 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/LICENSE000644 001751 001751 00000002565 13265410474 015131 0ustar00mdouninmdounin000000 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/README000644 001751 001751 00000000061 13265410474 014771 0ustar00mdouninmdounin000000 000000 Documentation is available at http://nginx.org nginx-1.14.0/html/000755 001751 001751 00000000000 13265410475 015061 5ustar00mdouninmdounin000000 000000 nginx-1.14.0/man/000755 001751 001751 00000000000 13265410475 014670 5ustar00mdouninmdounin000000 000000 nginx-1.14.0/CHANGES.ru000644 001751 001751 00001526046 13265410500 015540 0ustar00mdouninmdounin000000 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.c000644 001751 001751 00000267374 13265410474 021454 0ustar00mdouninmdounin000000 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.h000644 001751 001751 00000050241 13265410474 021440 0ustar00mdouninmdounin000000 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.c000644 001751 001751 00000071452 13265410474 022457 0ustar00mdouninmdounin000000 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.c000644 001751 001751 00000123331 13265410474 021250 0ustar00mdouninmdounin000000 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.h000644 001751 001751 00000017101 13265410474 021252 0ustar00mdouninmdounin000000 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.c000644 001751 001751 00000200656 13265410474 021722 0ustar00mdouninmdounin000000 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.c000644 001751 001751 00000500360 13265410474 021605 0ustar00mdouninmdounin000000 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.h000644 001751 001751 00000035653 13265410474 021622 0ustar00mdouninmdounin000000 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 5ustar00mdouninmdounin000000 000000 nginx-1.14.0/src/http/ngx_http_upstream_round_robin.c000644 001751 001751 00000051671 13265410474 024213 0ustar00mdouninmdounin000000 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.h000644 001751 001751 00000011741 13265410474 024212 0ustar00mdouninmdounin000000 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.h000644 001751 001751 00000006164 13265410474 021725 0ustar00mdouninmdounin000000 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.c000644 001751 001751 00000021406 13265410474 024010 0ustar00mdouninmdounin000000 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.c000644 001751 001751 00000002247 13265410474 022141 0ustar00mdouninmdounin000000 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.c000644 001751 001751 00000401036 13265410474 020623 0ustar00mdouninmdounin000000 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.h000644 001751 001751 00000031717 13265410474 020635 0ustar00mdouninmdounin000000 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.c000644 001751 001751 00000157260 13265410474 023544 0ustar00mdouninmdounin000000 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.c000644 001751 001751 00000373443 13265410474 023150 0ustar00mdouninmdounin000000 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.c000644 001751 001751 00000031035 13265410474 023146 0ustar00mdouninmdounin000000 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.c000644 001751 001751 00000040106 13265410474 022165 0ustar00mdouninmdounin000000 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.h000644 001751 001751 00000002355 13265410474 022176 0ustar00mdouninmdounin000000 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.c000644 001751 001751 00000026661 13265410474 022001 0ustar00mdouninmdounin000000 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.c000644 001751 001751 00000015522 13265410474 026123 0ustar00mdouninmdounin000000 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.c000644 001751 001751 00000027132 13265410474 024224 0ustar00mdouninmdounin000000 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.c000644 001751 001751 00000120166 13265410474 025762 0ustar00mdouninmdounin000000 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.c000644 001751 001751 00000026572 13265410474 025074 0ustar00mdouninmdounin000000 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.c000644 001751 001751 00000026405 13265410474 025476 0ustar00mdouninmdounin000000 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.c000644 001751 001751 00000073765 13265410474 025000 0ustar00mdouninmdounin000000 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.c000644 001751 001751 00000046721 13265410474 024453 0ustar00mdouninmdounin000000 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.c000644 001751 001751 00000015500 13265410474 026764 0ustar00mdouninmdounin000000 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.c000644 001751 001751 00000021041 13265410474 025742 0ustar00mdouninmdounin000000 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.c000644 001751 001751 00000076722 13265410474 023546 0ustar00mdouninmdounin000000 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.c000644 001751 001751 00000014140 13265410474 025237 0ustar00mdouninmdounin000000 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.c000644 001751 001751 00000012640 13265410474 024744 0ustar00mdouninmdounin000000 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.c000644 001751 001751 00000325323 13265410474 024406 0ustar00mdouninmdounin000000 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.c000644 001751 001751 00000014737 13265410474 023561 0ustar00mdouninmdounin000000 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.c000644 001751 001751 00000125401 13265410474 023533 0ustar00mdouninmdounin000000 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.c000644 001751 001751 00000053543 13265410474 024073 0ustar00mdouninmdounin000000 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.c000644 001751 001751 00000402722 13265410474 023720 0ustar00mdouninmdounin000000 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.c000644 001751 001751 00000041216 13265410474 025643 0ustar00mdouninmdounin000000 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.c000644 001751 001751 00000077215 13265410474 025310 0ustar00mdouninmdounin000000 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.c000644 001751 001751 00000020424 13265410474 025300 0ustar00mdouninmdounin000000 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.c000644 001751 001751 00000052304 13265410474 025742 0ustar00mdouninmdounin000000 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.c000644 001751 001751 00000123111 13265410474 025404 0ustar00mdouninmdounin000000 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.c000644 001751 001751 00000035247 13265410474 024100 0ustar00mdouninmdounin000000 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.c000644 001751 001751 00000043116 13265410474 025116 0ustar00mdouninmdounin000000 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.c000644 001751 001751 00000060671 13265410474 024755 0ustar00mdouninmdounin000000 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.c000644 001751 001751 00000141155 13265410474 023546 0ustar00mdouninmdounin000000 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.c000644 001751 001751 00000035244 13265410474 023543 0ustar00mdouninmdounin000000 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.c000644 001751 001751 00000050267 13265410474 024676 0ustar00mdouninmdounin000000 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.c000644 001751 001751 00000015212 13265410474 024271 0ustar00mdouninmdounin000000 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.c000644 001751 001751 00000315101 13265410474 023457 0ustar00mdouninmdounin000000 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.c000644 001751 001751 00000021434 13265410474 025431 0ustar00mdouninmdounin000000 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.c000644 001751 001751 00000360413 13265410474 024146 0ustar00mdouninmdounin000000 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.c000644 001751 001751 00000016067 13265410474 026323 0ustar00mdouninmdounin000000 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.c000644 001751 001751 00000062606 13265410474 025431 0ustar00mdouninmdounin000000 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.c000644 001751 001751 00000035701 13265410474 024240 0ustar00mdouninmdounin000000 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.c000644 001751 001751 00000041751 13265410474 024420 0ustar00mdouninmdounin000000 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.c000644 001751 001751 00000067330 13265410474 024450 0ustar00mdouninmdounin000000 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.c000644 001751 001751 00000164542 13265410474 023717 0ustar00mdouninmdounin000000 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.c000644 001751 001751 00000021622 13265410474 025264 0ustar00mdouninmdounin000000 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.c000644 001751 001751 00000071630 13265410474 023566 0ustar00mdouninmdounin000000 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.c000644 001751 001751 00000033101 13265410474 025420 0ustar00mdouninmdounin000000 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.c000644 001751 001751 00000014727 13265410474 025645 0ustar00mdouninmdounin000000 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.c000644 001751 001751 00000233506 13265410474 025132 0ustar00mdouninmdounin000000 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[] = "