diff -Nru nginx-0.5.33/auto/cc/bcc nginx-0.8.53/auto/cc/bcc --- nginx-0.5.33/auto/cc/bcc 2006-10-19 10:19:19.000000000 +0000 +++ nginx-0.8.53/auto/cc/bcc 2009-04-20 06:08:47.000000000 +0000 @@ -24,7 +24,7 @@ # __stdcall #CPU_OPT="$CPU_OPT -ps" # __fastcall -CPU_OPT="$CPU_OPT -pr" +#CPU_OPT="$CPU_OPT -pr" CFLAGS="$CFLAGS $CPU_OPT" @@ -46,7 +46,7 @@ # Win32 GUI mode application -LINK="\$(CC) -laa" +#LINK="\$(CC) -laa" # the resource file diff -Nru nginx-0.5.33/auto/cc/conf nginx-0.8.53/auto/cc/conf --- nginx-0.5.33/auto/cc/conf 2006-11-27 11:07:09.000000000 +0000 +++ nginx-0.8.53/auto/cc/conf 2010-07-05 13:02:25.000000000 +0000 @@ -104,7 +104,7 @@ fi CFLAGS="$CFLAGS $NGX_CC_OPT" - +NGX_TEST_LD_OPT="$NGX_LD_OPT" if [ "$NGX_PLATFORM" != win32 ]; then @@ -125,16 +125,21 @@ fi fi - ngx_feature="gcc variadic macros" - ngx_feature_name="NGX_HAVE_GCC_VARIADIC_MACROS" + + ngx_feature="gcc builtin atomic operations" + ngx_feature_name=NGX_HAVE_GCC_ATOMIC ngx_feature_run=yes - ngx_feature_incs="#include -#define var(dummy, args...) sprintf(args)" + ngx_feature_incs= ngx_feature_path= ngx_feature_libs= - ngx_feature_test="char buf[30]; buf[0] = '0'; - var(0, buf, \"%d\", 1); - if (buf[0] != '1') return 1" + ngx_feature_test="long n = 0; + if (!__sync_bool_compare_and_swap(&n, 0, 1)) + return 1; + if (__sync_fetch_and_add(&n, 1) != 1) + return 1; + if (n != 2) + return 1; + __sync_synchronize();" . auto/feature @@ -155,6 +160,19 @@ fi + ngx_feature="gcc variadic macros" + ngx_feature_name="NGX_HAVE_GCC_VARIADIC_MACROS" + ngx_feature_run=yes + ngx_feature_incs="#include +#define var(dummy, args...) sprintf(args)" + ngx_feature_path= + ngx_feature_libs= + ngx_feature_test="char buf[30]; buf[0] = '0'; + var(0, buf, \"%d\", 1); + if (buf[0] != '1') return 1" + . auto/feature + + # ngx_feature="inline" # ngx_feature_name= # ngx_feature_run=no diff -Nru nginx-0.5.33/auto/cc/gcc nginx-0.8.53/auto/cc/gcc --- nginx-0.5.33/auto/cc/gcc 2007-01-11 16:20:18.000000000 +0000 +++ nginx-0.8.53/auto/cc/gcc 2009-12-21 17:51:30.000000000 +0000 @@ -51,8 +51,6 @@ #NGX_GCC_OPT="-Os" NGX_GCC_OPT="-O" -CFLAGS="$CFLAGS $NGX_GCC_OPT" - #CFLAGS="$CFLAGS -fomit-frame-pointer" case $CPU in @@ -90,7 +88,6 @@ # build 32-bit UltraSparc binary CPU_OPT="-m32" CORE_LINK="$CORE_LINK -m32" - CC_AUX_FLAGS="$CC_AUX_FLAGS -m32" NGX_CPU_CACHE_LINE=64 ;; @@ -98,7 +95,6 @@ # build 64-bit UltraSparc binary CPU_OPT="-m64" CORE_LINK="$CORE_LINK -m64" - CC_AUX_FLAGS="$CC_AUX_FLAGS -m64" NGX_CPU_CACHE_LINE=64 ;; @@ -108,12 +104,12 @@ CPU_OPT="$CPU_OPT -falign-functions=32 -falign-labels=32" CPU_OPT="$CPU_OPT -falign-loops=32 -falign-jumps=32" CORE_LINK="$CORE_LINK -m64" - CC_AUX_FLAGS="$CC_AUX_FLAGS -m64" NGX_CPU_CACHE_LINE=128 ;; esac +CC_AUX_FLAGS="$CC_AUX_FLAGS $CPU_OPT" case "$NGX_GCC_VER" in 2.7*) @@ -158,9 +154,10 @@ 3.* | 4.* ) # we have a lot of the unused function arguments CFLAGS="$CFLAGS -Wno-unused-parameter" - CFLAGS="$CFLAGS -Wno-unused-function" + CFLAGS="$CFLAGS -Wunused-function" CFLAGS="$CFLAGS -Wunused-variable" CFLAGS="$CFLAGS -Wunused-value" + # 4.2.1 shows the warning in wrong places #CFLAGS="$CFLAGS -Wunreachable-code" ;; diff -Nru nginx-0.5.33/auto/cc/msvc nginx-0.8.53/auto/cc/msvc --- nginx-0.5.33/auto/cc/msvc 2006-08-30 10:39:17.000000000 +0000 +++ nginx-0.8.53/auto/cc/msvc 2009-05-28 14:33:37.000000000 +0000 @@ -2,7 +2,9 @@ # Copyright (C) Igor Sysoev -# MSVC 6.0 SP2, MSVC Toolkit 2003 (7.1) +# MSVC 6.0 SP2 +# MSVC Toolkit 2003 (7.1) +# MSVC 2005 Express Edition SP1 (8.0) # optimizations @@ -49,12 +51,12 @@ ;; esac -# __cdecl, use with OpenSSL, md5 asm, and sha1 asm +# __cdecl, default, must be used with OpenSSL, md5 asm, and sha1 asm #CPU_OPT="$CPU_OPT -Gd" # __stdcall #CPU_OPT="$CPU_OPT -Gz" # __fastcall -CPU_OPT="$CPU_OPT -Gr" +#CPU_OPT="$CPU_OPT -Gr" CFLAGS="$CFLAGS $CPU_OPT" @@ -76,34 +78,41 @@ # the link flags CORE_LINK="$CORE_LINK -link -verbose:lib" -if [ $NGX_CC_NAME = msvc7 ]; then - # link with libcmt.lib, multithreaded - LIBC="-MT" -else - # link with msvcrt.dll - LIBC="-MD" -fi +# link with libcmt.lib, multithreaded +LIBC="-MT" +# link with msvcrt.dll +# however, MSVC Toolkit 2003 has no MSVCRT.LIB +#LIBC="-MD" CFLAGS="$CFLAGS $LIBC" -# Win32 GUI mode application CORE_LIBS="$CORE_LIBS kernel32.lib user32.lib" -CORE_LINK="$CORE_LINK -subsystem:windows -entry:mainCRTStartup" + +# Win32 GUI mode application +#CORE_LINK="$CORE_LINK -subsystem:windows -entry:mainCRTStartup" # debug -CFLAGS="$CFLAGS -Yd" -CORE_LINK="$CORE_LINK -debug -debugtype:coff" +# msvc8 under Wine issues +# Program database manager mismatch; please check your installation +if [ $NGX_CC_NAME != msvc8 ]; then + CFLAGS="$CFLAGS -Zi" + CORE_LINK="$CORE_LINK -debug" +fi -# precompiled headers -if [ $NGX_CC_NAME != msvc7 ]; then - CORE_DEPS="$CORE_DEPS $NGX_OBJS/ngx_config.pch" - NGX_PCH="$NGX_OBJS/ngx_config.pch" - NGX_BUILD_PCH="-Ycngx_config.h -Fp$NGX_OBJS/ngx_config.pch" - NGX_USE_PCH="-Yungx_config.h -Fp$NGX_OBJS/ngx_config.pch" +# MSVC 2005 supports C99 variadic macros +if [ $NGX_CC_NAME = msvc8 ]; then + have=NGX_HAVE_C99_VARIADIC_MACROS . auto/have fi +# precompiled headers +CORE_DEPS="$CORE_DEPS $NGX_OBJS/ngx_config.pch" +NGX_PCH="$NGX_OBJS/ngx_config.pch" +NGX_BUILD_PCH="-Ycngx_config.h -Fp$NGX_OBJS/ngx_config.pch" +NGX_USE_PCH="-Yungx_config.h -Fp$NGX_OBJS/ngx_config.pch" + + # the resource file NGX_RES="$NGX_OBJS/nginx.res" NGX_RCC="rc -fo$NGX_RES \$(CORE_INCS) $NGX_WIN32_RC" @@ -115,24 +124,13 @@ ngx_objext="obj" ngx_binext=".exe" -if [ "$BMAKE" = nmake ]; then - # MS nmake - - ngx_long_start='@<< - ' - ngx_long_end='<<' - ngx_long_regex_cont=' \ +ngx_long_start='@<< ' - ngx_long_cont=' +ngx_long_end='<<' +ngx_long_regex_cont=' \ + ' +ngx_long_cont=' ' - -else - # Borland make - - ngx_long_start='@&&| - ' - ngx_long_end='|' -fi # MSVC understand / in path #ngx_regex_dirsep='\\' diff -Nru nginx-0.5.33/auto/cc/name nginx-0.8.53/auto/cc/name --- nginx-0.5.33/auto/cc/name 2006-12-23 20:31:14.000000000 +0000 +++ nginx-0.8.53/auto/cc/name 2007-11-15 14:22:12.000000000 +0000 @@ -25,6 +25,13 @@ if [ "$CC" = cl ]; then if `$NGX_WINE $CC -v 2>&1 \ + | grep '^Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 14' \ + >/dev/null 2>&1`; then + + NGX_CC_NAME=msvc8 + echo " + using Microsoft Visual C++ 8 compiler" + + else if `$NGX_WINE $CC -v 2>&1 \ | grep '^Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 13' \ >/dev/null 2>&1`; then @@ -35,6 +42,7 @@ NGX_CC_NAME=msvc echo " + using Microsoft Visual C++ compiler" fi + fi else if [ "$CC" = wcl386 ]; then diff -Nru nginx-0.5.33/auto/cc/owc nginx-0.8.53/auto/cc/owc --- nginx-0.5.33/auto/cc/owc 2006-08-30 10:39:17.000000000 +0000 +++ nginx-0.8.53/auto/cc/owc 2009-05-12 13:29:00.000000000 +0000 @@ -71,7 +71,7 @@ # the link flags, built target is NT GUI mode application -CORE_LINK="$CORE_LINK -l=nt_win" +#CORE_LINK="$CORE_LINK -l=nt_win" # the resource file @@ -87,3 +87,17 @@ ngx_regex_dirsep='\\' ngx_dirsep="\\" + +ngx_long_start=' ' +ngx_long_end=' ' +ngx_long_regex_cont=' \&\ + ' +ngx_long_cont=' & + ' + +ngx_regex_cont=' \&\ + ' +ngx_cont=' & + ' +ngx_tab=' & + ' diff -Nru nginx-0.5.33/auto/cc/sunc nginx-0.8.53/auto/cc/sunc --- nginx-0.5.33/auto/cc/sunc 2007-08-11 10:13:37.000000000 +0000 +++ nginx-0.8.53/auto/cc/sunc 2009-05-08 09:41:43.000000000 +0000 @@ -45,21 +45,6 @@ case "$NGX_MACHINE" in i86pc) - ngx_feature="PAUSE hardware capability bug" - ngx_feature_name= - ngx_feature_run=bug - ngx_feature_incs= - ngx_feature_path= - ngx_feature_libs= - ngx_feature_test='__asm ("pause")' - - . auto/feature - - if [ $ngx_found = yes ]; then - # disable [ PAUSE ] hwcap for Sun Studio 11 - CORE_LINK="$CORE_LINK -Msrc/os/unix/ngx_sunpro_x86.map" - fi - NGX_AUX=" src/os/unix/ngx_sunpro_x86.il" ;; diff -Nru nginx-0.5.33/auto/feature nginx-0.8.53/auto/feature --- nginx-0.5.33/auto/feature 2006-12-23 20:31:14.000000000 +0000 +++ nginx-0.8.53/auto/feature 2009-11-27 22:02:04.000000000 +0000 @@ -19,7 +19,9 @@ fi if test -n "$ngx_feature_path"; then - ngx_feature_inc_path="-I $ngx_feature_path" + for ngx_temp in $ngx_feature_path; do + ngx_feature_inc_path="$ngx_feature_inc_path -I $ngx_temp" + done fi cat << END > $NGX_AUTOTEST.c @@ -37,7 +39,7 @@ ngx_test="$CC $CC_TEST_FLAGS $CC_AUX_FLAGS $ngx_feature_inc_path \ - -o $NGX_AUTOTEST $NGX_AUTOTEST.c $NGX_LD_OPT $ngx_feature_libs" + -o $NGX_AUTOTEST $NGX_AUTOTEST.c $NGX_TEST_LD_OPT $ngx_feature_libs" ngx_feature_inc_path= diff -Nru nginx-0.5.33/auto/headers nginx-0.8.53/auto/headers --- nginx-0.5.33/auto/headers 2006-08-30 10:39:17.000000000 +0000 +++ nginx-0.8.53/auto/headers 2009-03-30 12:33:33.000000000 +0000 @@ -2,7 +2,11 @@ # Copyright (C) Igor Sysoev -ngx_include="unistd.h"; . auto/include -ngx_include="inttypes.h"; . auto/include -ngx_include="limits.h"; . auto/include -ngx_include="sys/filio.h"; . auto/include +ngx_include="unistd.h"; . auto/include +ngx_include="inttypes.h"; . auto/include +ngx_include="limits.h"; . auto/include +ngx_include="sys/filio.h"; . auto/include +ngx_include="sys/param.h"; . auto/include +ngx_include="sys/mount.h"; . auto/include +ngx_include="sys/statvfs.h"; . auto/include +ngx_include="crypt.h"; . auto/include diff -Nru nginx-0.5.33/auto/include nginx-0.8.53/auto/include --- nginx-0.5.33/auto/include 2006-08-30 10:39:17.000000000 +0000 +++ nginx-0.8.53/auto/include 2009-03-30 12:33:33.000000000 +0000 @@ -16,6 +16,7 @@ cat << END > $NGX_AUTOTEST.c +$NGX_INCLUDE_SYS_PARAM_H #include <$ngx_include> int main() { diff -Nru nginx-0.5.33/auto/init nginx-0.8.53/auto/init --- nginx-0.5.33/auto/init 2007-09-22 19:06:46.000000000 +0000 +++ nginx-0.8.53/auto/init 2009-05-17 19:22:08.000000000 +0000 @@ -43,36 +43,8 @@ cat << END > Makefile -build: - \$(MAKE) -f $NGX_MAKEFILE - -install: - \$(MAKE) -f $NGX_MAKEFILE install +default: build clean: rm -rf Makefile $NGX_OBJS - -upgrade: - $NGX_SBIN_PATH -t - - kill -USR2 \`cat $NGX_PID_PATH\` - sleep 1 - test -f $NGX_PID_PATH.oldbin - - kill -QUIT \`cat $NGX_PID_PATH.oldbin\` - -upgrade1: - # upgrade 0.1.x to 0.2+ - - $NGX_SBIN_PATH -t - - cp $NGX_PID_PATH $NGX_PID_PATH.oldbin - - kill -USR2 \`cat $NGX_PID_PATH\` - sleep 1 - test -f $NGX_PID_PATH.oldbin - - cp $NGX_PID_PATH $NGX_PID_PATH.newbin - - kill -QUIT \`cat $NGX_PID_PATH.oldbin\` END diff -Nru nginx-0.5.33/auto/install nginx-0.8.53/auto/install --- nginx-0.5.33/auto/install 2006-08-30 10:39:17.000000000 +0000 +++ nginx-0.8.53/auto/install 2010-06-18 15:51:14.000000000 +0000 @@ -15,46 +15,147 @@ fi -cat << END >> $NGX_MAKEFILE +case ".$NGX_SBIN_PATH" in + ./*) + ;; + + .) + NGX_SBIN_PATH=$NGX_PREFIX/sbin/nginx + ;; + + *) + NGX_SBIN_PATH=$NGX_PREFIX/$NGX_SBIN_PATH + ;; +esac + + +case ".$NGX_CONF_PATH" in + ./*) + ;; + + *) + NGX_CONF_PATH=$NGX_PREFIX/$NGX_CONF_PATH + ;; +esac -install: $NGX_OBJS${ngx_dirsep}nginx${ngx_binext} \ - $NGX_INSTALL_PERL_MODULES - test -d '$NGX_PREFIX' || mkdir -p '$NGX_PREFIX' - test -d '`dirname "$NGX_SBIN_PATH"`' \ - || mkdir -p '`dirname "$NGX_SBIN_PATH"`' - test ! -f '$NGX_SBIN_PATH' || mv '$NGX_SBIN_PATH' '$NGX_SBIN_PATH.old' - cp $NGX_OBJS/nginx '$NGX_SBIN_PATH' +NGX_CONF_PREFIX=`dirname $NGX_CONF_PATH` - test -d '`dirname "$NGX_CONF_PATH"`' \ - || mkdir -p '`dirname "$NGX_CONF_PATH"`' - cp conf/koi-win '`dirname "$NGX_CONF_PATH"`' - cp conf/koi-utf '`dirname "$NGX_CONF_PATH"`' - cp conf/win-utf '`dirname "$NGX_CONF_PATH"`' +case ".$NGX_PID_PATH" in + ./*) + ;; - test -f '`dirname "$NGX_CONF_PATH"`/mime.types' || \ - cp conf/mime.types '`dirname "$NGX_CONF_PATH"`/mime.types' - cp conf/mime.types '`dirname "$NGX_CONF_PATH"`/mime.types.default' + *) + NGX_PID_PATH=$NGX_PREFIX/$NGX_PID_PATH + ;; +esac - test -f '$NGX_CONF_PATH' || cp conf/nginx.conf '$NGX_CONF_PATH' - cp conf/nginx.conf '`dirname "$NGX_CONF_PATH"`/nginx.conf.default' - test -d '`dirname "$NGX_PID_PATH"`' \ - || mkdir -p '`dirname "$NGX_PID_PATH"`' +case ".$NGX_ERROR_LOG_PATH" in + ./*) + ;; - test -d '`dirname "$NGX_HTTP_LOG_PATH"`' || \ - mkdir -p '`dirname "$NGX_HTTP_LOG_PATH"`' + *) + NGX_ERROR_LOG_PATH=$NGX_PREFIX/$NGX_ERROR_LOG_PATH + ;; +esac - test -d '$NGX_PREFIX/html' || cp -r html '$NGX_PREFIX' + +case ".$NGX_HTTP_LOG_PATH" in + ./*) + ;; + + *) + NGX_HTTP_LOG_PATH=$NGX_PREFIX/$NGX_HTTP_LOG_PATH + ;; +esac + + +cat << END >> $NGX_MAKEFILE + +install: $NGX_OBJS${ngx_dirsep}nginx${ngx_binext} \ + $NGX_INSTALL_PERL_MODULES + test -d '\$(DESTDIR)$NGX_PREFIX' || mkdir -p '\$(DESTDIR)$NGX_PREFIX' + + test -d '\$(DESTDIR)`dirname "$NGX_SBIN_PATH"`' \ + || mkdir -p '\$(DESTDIR)`dirname "$NGX_SBIN_PATH"`' + test ! -f '\$(DESTDIR)$NGX_SBIN_PATH' \ + || mv '\$(DESTDIR)$NGX_SBIN_PATH' \ + '\$(DESTDIR)$NGX_SBIN_PATH.old' + cp $NGX_OBJS/nginx '\$(DESTDIR)$NGX_SBIN_PATH' + + test -d '\$(DESTDIR)$NGX_CONF_PREFIX' \ + || mkdir -p '\$(DESTDIR)$NGX_CONF_PREFIX' + + cp conf/koi-win '\$(DESTDIR)$NGX_CONF_PREFIX' + cp conf/koi-utf '\$(DESTDIR)$NGX_CONF_PREFIX' + cp conf/win-utf '\$(DESTDIR)$NGX_CONF_PREFIX' + + test -f '\$(DESTDIR)$NGX_CONF_PREFIX/mime.types' \ + || cp conf/mime.types '\$(DESTDIR)$NGX_CONF_PREFIX' + cp conf/mime.types '\$(DESTDIR)$NGX_CONF_PREFIX/mime.types.default' + + test -f '\$(DESTDIR)$NGX_CONF_PREFIX/fastcgi_params' \ + || cp conf/fastcgi_params '\$(DESTDIR)$NGX_CONF_PREFIX' + cp conf/fastcgi_params \ + '\$(DESTDIR)$NGX_CONF_PREFIX/fastcgi_params.default' + + test -f '\$(DESTDIR)$NGX_CONF_PREFIX/fastcgi.conf' \ + || cp conf/fastcgi.conf '\$(DESTDIR)$NGX_CONF_PREFIX' + cp conf/fastcgi.conf '\$(DESTDIR)$NGX_CONF_PREFIX/fastcgi.conf.default' + + test -f '\$(DESTDIR)$NGX_CONF_PREFIX/uwsgi_params' \ + || cp conf/uwsgi_params '\$(DESTDIR)$NGX_CONF_PREFIX' + cp conf/uwsgi_params \ + '\$(DESTDIR)$NGX_CONF_PREFIX/uwsgi_params.default' + + test -f '\$(DESTDIR)$NGX_CONF_PREFIX/scgi_params' \ + || cp conf/scgi_params '\$(DESTDIR)$NGX_CONF_PREFIX' + cp conf/scgi_params \ + '\$(DESTDIR)$NGX_CONF_PREFIX/scgi_params.default' + + test -f '\$(DESTDIR)$NGX_CONF_PATH' \ + || cp conf/nginx.conf '\$(DESTDIR)$NGX_CONF_PATH' + cp conf/nginx.conf '\$(DESTDIR)$NGX_CONF_PREFIX/nginx.conf.default' + + test -d '\$(DESTDIR)`dirname "$NGX_PID_PATH"`' \ + || mkdir -p '\$(DESTDIR)`dirname "$NGX_PID_PATH"`' + + test -d '\$(DESTDIR)`dirname "$NGX_HTTP_LOG_PATH"`' || \ + mkdir -p '\$(DESTDIR)`dirname "$NGX_HTTP_LOG_PATH"`' + + test -d '\$(DESTDIR)$NGX_PREFIX/html' \ + || cp -r html '\$(DESTDIR)$NGX_PREFIX' END -if test -n "$NGX_ERROR_LOG_PATH"; then +if test -n "\$(DESTDIR)$NGX_ERROR_LOG_PATH"; then cat << END >> $NGX_MAKEFILE - test -d '`dirname "$NGX_ERROR_LOG_PATH"`' || \ - mkdir -p '`dirname "$NGX_ERROR_LOG_PATH"`' + test -d '\$(DESTDIR)`dirname "$NGX_ERROR_LOG_PATH"`' || \ + mkdir -p '\$(DESTDIR)`dirname "$NGX_ERROR_LOG_PATH"`' END fi + + +# create Makefile + +cat << END >> Makefile + +build: + \$(MAKE) -f $NGX_MAKEFILE + +install: + \$(MAKE) -f $NGX_MAKEFILE install + +upgrade: + $NGX_SBIN_PATH -t + + kill -USR2 \`cat $NGX_PID_PATH\` + sleep 1 + test -f $NGX_PID_PATH.oldbin + + kill -QUIT \`cat $NGX_PID_PATH.oldbin\` +END diff -Nru nginx-0.5.33/auto/lib/conf nginx-0.8.53/auto/lib/conf --- nginx-0.5.33/auto/lib/conf 2006-08-30 10:39:17.000000000 +0000 +++ nginx-0.8.53/auto/lib/conf 2009-11-25 17:55:25.000000000 +0000 @@ -4,18 +4,33 @@ if [ $USE_PCRE = YES -o $PCRE != NONE ]; then . auto/lib/pcre/conf + +else + if [ $USE_PCRE = DISABLED -a $HTTP_REWRITE = YES ]; then + +cat << END + +$0: error: the HTTP rewrite module requires the PCRE library. +You can either disable the module by using --without-http_rewrite_module +option or you have to enable the PCRE support. + +END + exit 1 + fi fi + if [ $USE_OPENSSL = YES ]; then . auto/lib/openssl/conf fi if [ $USE_MD5 = YES ]; then - if [ $OPENSSL != NONE -a $OPENSSL != NO ]; then + if [ $USE_OPENSSL = YES ]; then have=NGX_HAVE_OPENSSL_MD5_H . auto/have have=NGX_OPENSSL_MD5 . auto/have MD5=YES + MD5_LIB=OpenSSL else . auto/lib/md5/conf @@ -25,9 +40,10 @@ if [ $USE_SHA1 = YES ]; then - if [ $OPENSSL != NONE -a $OPENSSL != NO ]; then + if [ $USE_OPENSSL = YES ]; then have=NGX_HAVE_OPENSSL_SHA1_H . auto/have SHA1=YES + SHA1_LIB=OpenSSL else . auto/lib/sha1/conf @@ -39,6 +55,26 @@ . auto/lib/zlib/conf fi +if [ $USE_LIBXSLT = YES ]; then + . auto/lib/libxslt/conf +fi + +if [ $USE_LIBGD = YES ]; then + . auto/lib/libgd/conf +fi + if [ $USE_PERL = YES ]; then . auto/lib/perl/conf fi + +if [ $HTTP_GEOIP = YES ]; then + . auto/lib/geoip/conf +fi + +if [ $NGX_GOOGLE_PERFTOOLS = YES ]; then + . auto/lib/google-perftools/conf +fi + +if [ $NGX_LIBATOMIC != NO ]; then + . auto/lib/libatomic/conf +fi diff -Nru nginx-0.5.33/auto/lib/geoip/conf nginx-0.8.53/auto/lib/geoip/conf --- nginx-0.5.33/auto/lib/geoip/conf 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/auto/lib/geoip/conf 2009-07-20 07:10:43.000000000 +0000 @@ -0,0 +1,78 @@ + +# Copyright (C) Igor Sysoev + + + ngx_feature="GeoIP library" + ngx_feature_name= + ngx_feature_run=no + ngx_feature_incs= + ngx_feature_path= + ngx_feature_libs="-lGeoIP" + ngx_feature_test="GeoIP_open(NULL, 0)" + . auto/feature + + +if [ $ngx_found = no ]; then + + # FreeBSD port + + ngx_feature="GeoIP library in /usr/local/" + + if [ $NGX_RPATH = YES ]; then + ngx_feature_libs="-R/usr/local/lib -L/usr/local/lib -lGeoIP" + else + ngx_feature_libs="-L/usr/local/lib -lGeoIP" + fi + + . auto/feature +fi + + +if [ $ngx_found = no ]; then + + # NetBSD port + + ngx_feature="GeoIP library in /usr/pkg/" + ngx_feature_path="/usr/pkg/include/" + + if [ $NGX_RPATH = YES ]; then + ngx_feature_libs="-R/usr/pkg/lib -L/usr/pkg/lib -lGeoIP" + else + ngx_feature_libs="-L/usr/pkg/lib -lGeoIP" + fi + + . auto/feature +fi + + +if [ $ngx_found = no ]; then + + # MacPorts + + ngx_feature="GeoIP library in /opt/local/" + ngx_feature_path="/opt/local/include" + + if [ $NGX_RPATH = YES ]; then + ngx_feature_libs="-R/opt/local/lib -L/opt/local/lib -lGeoIP" + else + ngx_feature_libs="-L/opt/local/lib -lGeoIP" + fi + + . auto/feature +fi + + +if [ $ngx_found = yes ]; then + CORE_LIBS="$CORE_LIBS $ngx_feature_libs" + +else + +cat << END + +$0: error: the GeoIP module requires the GeoIP library. +You can either do not enable the module or install the library. + +END + + exit 1 +fi diff -Nru nginx-0.5.33/auto/lib/google-perftools/conf nginx-0.8.53/auto/lib/google-perftools/conf --- nginx-0.5.33/auto/lib/google-perftools/conf 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/auto/lib/google-perftools/conf 2009-05-26 14:28:49.000000000 +0000 @@ -0,0 +1,44 @@ + +# Copyright (C) Igor Sysoev + + + ngx_feature="Google perftools" + ngx_feature_name= + ngx_feature_run=no + ngx_feature_incs= + ngx_feature_path= + ngx_feature_libs="-lprofiler" + ngx_feature_test="ProfilerStop()" + . auto/feature + + +if [ $ngx_found = no ]; then + + # FreeBSD port + + ngx_feature="Google perftools in /usr/local/" + + if [ $NGX_RPATH = YES ]; then + ngx_feature_libs="-R/usr/local/lib -L/usr/local/lib -lprofiler" + else + ngx_feature_libs="-L/usr/local/lib -lprofiler" + fi + + . auto/feature +fi + + +if [ $ngx_found = yes ]; then + CORE_LIBS="$CORE_LIBS $ngx_feature_libs" + +else + +cat << END + +$0: error: the Google perftool module requires the Google perftools +library. You can either do not enable the module or install the library. + +END + + exit 1 +fi diff -Nru nginx-0.5.33/auto/lib/libatomic/conf nginx-0.8.53/auto/lib/libatomic/conf --- nginx-0.5.33/auto/lib/libatomic/conf 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/auto/lib/libatomic/conf 2009-12-07 15:32:38.000000000 +0000 @@ -0,0 +1,42 @@ + +# Copyright (C) Igor Sysoev + + +if [ $NGX_LIBATOMIC != YES ]; then + + have=NGX_HAVE_LIBATOMIC . auto/have + CORE_INCS="$CORE_INCS $NGX_LIBATOMIC/src" + LINK_DEPS="$LINK_DEPS $NGX_LIBATOMIC/src/libatomic_ops.a" + CORE_LIBS="$CORE_LIBS $NGX_LIBATOMIC/src/libatomic_ops.a" + +else + + ngx_feature="atomic_ops library" + ngx_feature_name=NGX_HAVE_LIBATOMIC + ngx_feature_run=yes + ngx_feature_incs="#define AO_REQUIRE_CAS + #include " + ngx_feature_path= + ngx_feature_libs="-latomic_ops" + ngx_feature_test="long n = 0; + if (!AO_compare_and_swap(&n, 0, 1)) + return 1; + if (AO_fetch_and_add(&n, 1) != 1) + return 1; + if (n != 2) + return 1; + AO_nop();" + . auto/feature + + if [ $ngx_found = yes ]; then + CORE_LIBS="$CORE_LIBS $ngx_feature_libs" + else + +cat << END + +$0: error: libatomic_ops library was not found. + +END + exit 1 + fi +fi diff -Nru nginx-0.5.33/auto/lib/libatomic/make nginx-0.8.53/auto/lib/libatomic/make --- nginx-0.5.33/auto/lib/libatomic/make 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/auto/lib/libatomic/make 2009-11-25 17:55:25.000000000 +0000 @@ -0,0 +1,13 @@ + +# Copyright (C) Igor Sysoev + + + cat << END >> $NGX_MAKEFILE + +$NGX_LIBATOMIC/src/libatomic_ops.a: $NGX_LIBATOMIC/Makefile + cd $NGX_LIBATOMIC && make + +$NGX_LIBATOMIC/Makefile: $NGX_MAKEFILE + cd $NGX_LIBATOMIC && ./configure + +END diff -Nru nginx-0.5.33/auto/lib/libgd/conf nginx-0.8.53/auto/lib/libgd/conf --- nginx-0.5.33/auto/lib/libgd/conf 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/auto/lib/libgd/conf 2009-05-04 15:57:12.000000000 +0000 @@ -0,0 +1,82 @@ + +# Copyright (C) Igor Sysoev + + + ngx_feature="GD library" + ngx_feature_name= + ngx_feature_run=no + ngx_feature_incs="#include " + ngx_feature_path= + ngx_feature_libs="-lgd" + ngx_feature_test="gdImagePtr img = gdImageCreateFromGifPtr(1, NULL);" + . auto/feature + + +if [ $ngx_found = no ]; then + + # FreeBSD port + + ngx_feature="GD library in /usr/local/" + ngx_feature_path="/usr/local/include" + + if [ $NGX_RPATH = YES ]; then + ngx_feature_libs="-R/usr/local/lib -L/usr/local/lib -lgd" + else + ngx_feature_libs="-L/usr/local/lib -lgd" + fi + + . auto/feature +fi + + +if [ $ngx_found = no ]; then + + # NetBSD port + + ngx_feature="GD library in /usr/pkg/" + ngx_feature_path="/usr/pkg/include/" + + if [ $NGX_RPATH = YES ]; then + ngx_feature_libs="-R/usr/pkg/lib -L/usr/pkg/lib -lgd" + else + ngx_feature_libs="-L/usr/pkg/lib -lgd" + fi + + . auto/feature +fi + + +if [ $ngx_found = no ]; then + + # MacPorts + + ngx_feature="GD library in /opt/local/" + ngx_feature_path="/opt/local/include" + + if [ $NGX_RPATH = YES ]; then + ngx_feature_libs="-R/opt/local/lib -L/opt/local/lib -lgd" + else + ngx_feature_libs="-L/opt/local/lib -lgd" + fi + + . auto/feature +fi + + +if [ $ngx_found = yes ]; then + + CORE_INCS="$CORE_INCS $ngx_feature_path" + CORE_LIBS="$CORE_LIBS $ngx_feature_libs" + +else + +cat << END + +$0: error: the HTTP image filter module requires the GD library. +You can either do not enable the module or install the libraries. + +END + + exit 1 + +fi diff -Nru nginx-0.5.33/auto/lib/libxslt/conf nginx-0.8.53/auto/lib/libxslt/conf --- nginx-0.5.33/auto/lib/libxslt/conf 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/auto/lib/libxslt/conf 2008-11-11 15:22:24.000000000 +0000 @@ -0,0 +1,155 @@ + +# Copyright (C) Igor Sysoev + + + ngx_feature="libxslt" + ngx_feature_name= + ngx_feature_run=no + ngx_feature_incs="#include + #include + #include + #include + #include + #include " + ngx_feature_path="/usr/include/libxml2" + ngx_feature_libs="-lxml2 -lxslt" + ngx_feature_test="xmlParserCtxtPtr ctxt = NULL; + xsltStylesheetPtr sheet = NULL; + xmlDocPtr doc; + doc = xmlParseChunk(ctxt, NULL, 0, 0); + xsltApplyStylesheet(sheet, doc, NULL);" + . auto/feature + + +if [ $ngx_found = no ]; then + + # FreeBSD port + + ngx_feature="libxslt in /usr/local/" + ngx_feature_path="/usr/local/include/libxml2 /usr/local/include" + + if [ $NGX_RPATH = YES ]; then + ngx_feature_libs="-R/usr/local/lib -L/usr/local/lib -lxml2 -lxslt" + else + ngx_feature_libs="-L/usr/local/lib -lxml2 -lxslt" + fi + + . auto/feature +fi + + +if [ $ngx_found = no ]; then + + # NetBSD port + + ngx_feature="libxslt in /usr/pkg/" + ngx_feature_path="/usr/pkg/include/libxml2 /usr/pkg/include" + + if [ $NGX_RPATH = YES ]; then + ngx_feature_libs="-R/usr/pkg/lib -L/usr/pkg/lib -lxml2 -lxslt" + else + ngx_feature_libs="-L/usr/pkg/lib -lxml2 -lxslt" + fi + + . auto/feature +fi + + +if [ $ngx_found = no ]; then + + # MacPorts + + ngx_feature="libxslt in /opt/local/" + ngx_feature_path="/opt/local/include/libxml2 /opt/local/include" + + if [ $NGX_RPATH = YES ]; then + ngx_feature_libs="-R/opt/local/lib -L/opt/local/lib -lxml2 -lxslt" + else + ngx_feature_libs="-L/opt/local/lib -lxml2 -lxslt" + fi + + . auto/feature +fi + + +if [ $ngx_found = yes ]; then + + CORE_INCS="$CORE_INCS $ngx_feature_path" + CORE_LIBS="$CORE_LIBS $ngx_feature_libs" + +else + +cat << END + +$0: error: the HTTP XSLT module requires the libxml2/libxslt +libraries. You can either do not enable the module or install the libraries. + +END + + exit 1 +fi + + + ngx_feature="libexslt" + ngx_feature_name=NGX_HAVE_EXSLT + ngx_feature_run=no + ngx_feature_incs="#include " + ngx_feature_path="/usr/include/libxml2" + ngx_feature_libs="-lexslt" + ngx_feature_test="exsltRegisterAll();" + . auto/feature + +if [ $ngx_found = no ]; then + + # FreeBSD port + + ngx_feature="libexslt in /usr/local/" + ngx_feature_path="/usr/local/include/libxml2 /usr/local/include" + + if [ $NGX_RPATH = YES ]; then + ngx_feature_libs="-R/usr/local/lib -L/usr/local/lib -lexslt" + else + ngx_feature_libs="-L/usr/local/lib -lexslt" + fi + + . auto/feature +fi + + +if [ $ngx_found = no ]; then + + # NetBSD port + + ngx_feature="libexslt in /usr/pkg/" + ngx_feature_path="/usr/pkg/include/libxml2 /usr/local/include" + + if [ $NGX_RPATH = YES ]; then + ngx_feature_libs="-R/usr/pkg/lib -L/usr/pkg/lib -lexslt" + else + ngx_feature_libs="-L/usr/pkg/lib -lexslt" + fi + + . auto/feature +fi + + +if [ $ngx_found = no ]; then + + # MacPorts + + ngx_feature="libexslt in /opt/local/" + ngx_feature_path="/opt/local/include/libxml2 /opt/local/include" + + if [ $NGX_RPATH = YES ]; then + ngx_feature_libs="-R/opt/local/lib -L/opt/local/lib -lexslt" + else + ngx_feature_libs="-L/opt/local/lib -lexslt" + fi + + . auto/feature +fi + + +if [ $ngx_found = yes ]; then + CORE_LIBS="$CORE_LIBS -lexslt" +fi diff -Nru nginx-0.5.33/auto/lib/make nginx-0.8.53/auto/lib/make --- nginx-0.5.33/auto/lib/make 2006-08-30 10:39:17.000000000 +0000 +++ nginx-0.8.53/auto/lib/make 2009-11-25 17:55:25.000000000 +0000 @@ -22,6 +22,10 @@ . auto/lib/zlib/make fi +if [ $NGX_LIBATOMIC != NO -a $NGX_LIBATOMIC != YES ]; then + . auto/lib/libatomic/make +fi + if [ $USE_PERL = YES ]; then . auto/lib/perl/make fi diff -Nru nginx-0.5.33/auto/lib/md5/conf nginx-0.8.53/auto/lib/md5/conf --- nginx-0.5.33/auto/lib/md5/conf 2006-11-27 11:07:09.000000000 +0000 +++ nginx-0.8.53/auto/lib/md5/conf 2010-07-08 15:57:36.000000000 +0000 @@ -4,7 +4,7 @@ if [ $MD5 != NONE ]; then - if grep MD5_Init $MD5/md5.h >/dev/null; then + if grep MD5_Init $MD5/md5.h 2>&1 >/dev/null; then # OpenSSL md5 OPENSSL_MD5=YES have=NGX_HAVE_OPENSSL_MD5 . auto/have @@ -45,29 +45,12 @@ else if [ "$NGX_PLATFORM" != win32 ]; then - MD5=NO - # Solaris 8/9 + MD5=NO - ngx_feature="rsaref md5 library" - ngx_feature_name= - ngx_feature_run=no - ngx_feature_incs="#include " - ngx_feature_path= - ngx_feature_libs="-lmd5" - ngx_feature_test="MD5_CTX md5; MD5Init(&md5)" - . auto/feature + # FreeBSD, Solaris 10 - if [ $ngx_found = yes ]; then - CORE_LIBS="$CORE_LIBS $ngx_feature_libs" - MD5=YES - MD5_LIB=md5 - ngx_found=no - - else - # FreeBSD - - ngx_feature="rsaref md library" + ngx_feature="system md library" ngx_feature_name= ngx_feature_run=no ngx_feature_incs="#include " @@ -75,38 +58,56 @@ ngx_feature_libs="-lmd" ngx_feature_test="MD5_CTX md5; MD5Init(&md5)" . auto/feature + + ngx_md5_lib="system md" + + if [ $ngx_found = no ]; then + + # Solaris 8/9 + + ngx_feature="system md5 library" + ngx_feature_libs="-lmd5" + . auto/feature + + ngx_md5_lib="system md5" fi + if [ $ngx_found = no ]; then - if [ $ngx_found = yes ]; then - CORE_LIBS="$CORE_LIBS $ngx_feature_libs" - MD5=YES - MD5_LIB=md - ngx_found=no + # OpenSSL crypto library - else - if [ $MD5 = NO ]; then + ngx_feature="OpenSSL md5 crypto library" + ngx_feature_name="NGX_OPENSSL_MD5" + ngx_feature_incs="#include " + ngx_feature_libs="-lcrypto" + ngx_feature_test="MD5_CTX md5; MD5_Init(&md5)" + . auto/feature - # OpenSSL crypto library + ngx_md5_lib="system crypto" - ngx_feature="OpenSSL md5 crypto library" - ngx_feature_name="NGX_OPENSSL_MD5" - ngx_feature_run=no - ngx_feature_incs="#include " - ngx_feature_path= - ngx_feature_libs="-lcrypto" - ngx_feature_test="MD5_CTX md5; MD5_Init(&md5)" - . auto/feature + if [ $ngx_found = yes ]; then + have=NGX_HAVE_OPENSSL_MD5_H . auto/have fi fi - if [ $ngx_found = yes ]; then - have=NGX_HAVE_OPENSSL_MD5_H . auto/have CORE_LIBS="$CORE_LIBS $ngx_feature_libs" MD5=YES - MD5_LIB=crypto + MD5_LIB=$ngx_md5_lib fi fi + if [ $MD5 != YES ]; then +cat << END + +$0: error: the HTTP cache module requires md5 functions +from OpenSSL library. You can either disable the module by using +--without-http-cache option, or install the OpenSSL library into the system, +or build the OpenSSL library statically from the source with nginx by using +--with-http_ssl_module --with-openssl= options. + +END + exit 1 + fi + fi diff -Nru nginx-0.5.33/auto/lib/md5/make nginx-0.8.53/auto/lib/md5/make --- nginx-0.5.33/auto/lib/md5/make 2006-08-30 10:39:17.000000000 +0000 +++ nginx-0.8.53/auto/lib/md5/make 2009-05-12 13:15:43.000000000 +0000 @@ -7,16 +7,19 @@ msvc*) ngx_makefile=makefile.msvc ngx_opt="CPU_OPT=\"$CPU_OPT\" LIBC=$LIBC MD5_ASM=$MD5_ASM" + ngx_md5="MD5=\"$MD5\"" ;; owc*) ngx_makefile=makefile.owc ngx_opt="CPU_OPT=\"$CPU_OPT\"" + ngx_md5=`echo MD5=\"$MD5\" | sed -e "s/\//$ngx_regex_dirsep/g"` ;; bcc) ngx_makefile=makefile.bcc ngx_opt="-DCPU_OPT=\"$CPU_OPT\" -DMD5_ASM=$MD5_ASM" + ngx_md5=`echo \-DMD5=\"$MD5\" | sed -e "s/\//$ngx_regex_dirsep/g"` ;; esac @@ -28,14 +31,10 @@ case "$NGX_PLATFORM" in win32) - cp auto/lib/md5/$ngx_makefile $MD5 - cat << END >> $NGX_MAKEFILE `echo "$MD5/md5.lib: $NGX_MAKEFILE" | sed -e "s/\//$ngx_regex_dirsep/g"` - cd `echo $MD5 | sed -e "s/\//$ngx_regex_dirsep/g"` - \$(MAKE) -f $ngx_makefile $ngx_opt - cd ..\\..\\.. + \$(MAKE) -f auto/lib/md5/$ngx_makefile $ngx_opt $ngx_md5 END diff -Nru nginx-0.5.33/auto/lib/md5/makefile.bcc nginx-0.8.53/auto/lib/md5/makefile.bcc --- nginx-0.5.33/auto/lib/md5/makefile.bcc 2006-08-30 10:39:17.000000000 +0000 +++ nginx-0.8.53/auto/lib/md5/makefile.bcc 2009-05-12 13:15:43.000000000 +0000 @@ -7,12 +7,14 @@ !if "$(MD5_ASM)" == "YES" md5.lib: + cd $(MD5) bcc32 -c $(CFLAGS) -DMD5_ASM md5_dgst.c tlib md5.lib +md5_dgst.obj +"asm\m-win32.obj" !else md5.lib: + cd $(MD5) bcc32 -c $(CFLAGS) md5_dgst.c tlib md5.lib +md5_dgst.obj diff -Nru nginx-0.5.33/auto/lib/md5/makefile.msvc nginx-0.8.53/auto/lib/md5/makefile.msvc --- nginx-0.5.33/auto/lib/md5/makefile.msvc 2006-08-30 10:39:17.000000000 +0000 +++ nginx-0.8.53/auto/lib/md5/makefile.msvc 2009-05-12 13:15:43.000000000 +0000 @@ -7,12 +7,14 @@ !IF "$(MD5_ASM)" == "YES" md5.lib: + cd $(MD5) cl -c $(CFLAGS) -D MD5_ASM md5_dgst.c link -lib -out:md5.lib md5_dgst.obj asm/m-win32.obj !ELSE md5.lib: + cd $(MD5) cl -c $(CFLAGS) md5_dgst.c link -lib -out:md5.lib md5_dgst.obj diff -Nru nginx-0.5.33/auto/lib/md5/makefile.owc nginx-0.8.53/auto/lib/md5/makefile.owc --- nginx-0.5.33/auto/lib/md5/makefile.owc 2006-08-30 10:39:17.000000000 +0000 +++ nginx-0.8.53/auto/lib/md5/makefile.owc 2009-05-12 13:15:43.000000000 +0000 @@ -5,5 +5,6 @@ CFLAGS = -zq -bt=nt -bm -ot -op -oi -oe -s $(CPU_OPT) md5.lib: + cd $(MD5) wcl386 -c $(CFLAGS) -dL_ENDIAN md5_dgst.c wlib -n md5.lib md5_dgst.obj diff -Nru nginx-0.5.33/auto/lib/openssl/conf nginx-0.8.53/auto/lib/openssl/conf --- nginx-0.5.33/auto/lib/openssl/conf 2006-11-27 11:07:09.000000000 +0000 +++ nginx-0.8.53/auto/lib/openssl/conf 2010-07-08 15:57:36.000000000 +0000 @@ -3,68 +3,71 @@ if [ $OPENSSL != NONE ]; then - CORE_INCS="$CORE_INCS $OPENSSL/include" case "$CC" in - *) + + cl | bcc32) have=NGX_OPENSSL . auto/have have=NGX_SSL . auto/have - LINK_DEPS="$LINK_DEPS $OPENSSL/libssl.a $OPENSSL/libcrypto.a" - CORE_LIBS="$CORE_LIBS $OPENSSL/libssl.a $OPENSSL/libcrypto.a" + + CFLAGS="$CFLAGS -DNO_SYS_TYPES_H" + + CORE_INCS="$CORE_INCS $OPENSSL/openssl/include" + CORE_DEPS="$CORE_DEPS $OPENSSL/openssl/include/openssl/ssl.h" + CORE_LIBS="$CORE_LIBS $OPENSSL/openssl/lib/ssleay32.lib" + CORE_LIBS="$CORE_LIBS $OPENSSL/openssl/lib/libeay32.lib" + + # libeay32.lib requires gdi32.lib + CORE_LIBS="$CORE_LIBS gdi32.lib" + # OpenSSL 1.0.0 requires crypt32.lib + CORE_LIBS="$CORE_LIBS crypt32.lib" ;; - esac - case "$NGX_SYSTEM" in - SunOS|Linux) - CORE_LIBS="$CORE_LIBS -ldl" + *) + have=NGX_OPENSSL . auto/have + have=NGX_SSL . auto/have + + CORE_INCS="$CORE_INCS $OPENSSL/.openssl/include" + CORE_DEPS="$CORE_DEPS $OPENSSL/.openssl/include/openssl/ssl.h" + CORE_LIBS="$CORE_LIBS $OPENSSL/.openssl/lib/libssl.a" + CORE_LIBS="$CORE_LIBS $OPENSSL/.openssl/lib/libcrypto.a" + CORE_LIBS="$CORE_LIBS $NGX_LIBDL" ;; esac - else - case "$NGX_PLATFORM" in + if [ "$NGX_PLATFORM" != win32 ]; then - win32) - have=NGX_OPENSSL . auto/have + OPENSSL=NO + + ngx_feature="OpenSSL library" + ngx_feature_name="NGX_OPENSSL" + ngx_feature_run=no + ngx_feature_incs="#include " + ngx_feature_path= + ngx_feature_libs="-lssl -lcrypto" + ngx_feature_test="SSL_library_init()" + . auto/feature + + if [ $ngx_found = yes ]; then have=NGX_SSL . auto/have + CORE_LIBS="$CORE_LIBS $ngx_feature_libs $NGX_LIBDL" OPENSSL=YES + fi + fi - CORE_INCS="$CORE_INCS c:/openssl/include" - CORE_LIBS="$CORE_LIBS c:/openssl/ssleay32.lib" - CORE_LIBS="$CORE_LIBS c:/openssl/libeay32.lib" + if [ $OPENSSL != YES ]; then - # libeay32.lib requires gdi32.lib - CORE_LIBS="$CORE_LIBS gdi32.lib" - # OpenSSL 0.8's libeay32.lib requires advapi32.lib - CORE_LIBS="$CORE_LIBS advapi32.lib" - ;; +cat << END - *) - OPENSSL=NO +$0: error: SSL modules require the OpenSSL library. +You can either do not enable the modules, or install the OpenSSL library +into the system, or build the OpenSSL library statically from the source +with nginx by using --with-openssl= option. - ngx_feature="OpenSSL library" - ngx_feature_name="NGX_OPENSSL" - ngx_feature_run=no - ngx_feature_incs="#include " - ngx_feature_path= - ngx_feature_libs="-lssl -lcrypto" - ngx_feature_test="SSL_library_init()" - . auto/feature - - if [ $ngx_found = yes ]; then - have=NGX_SSL . auto/have - CORE_LIBS="$CORE_LIBS $ngx_feature_libs" - OPENSSL=YES - - case "$NGX_SYSTEM" in - SunOS) - CORE_LIBS="$CORE_LIBS -ldl" - ;; - esac - fi - ;; - - esac +END + exit 1 + fi fi diff -Nru nginx-0.5.33/auto/lib/openssl/make nginx-0.8.53/auto/lib/openssl/make --- nginx-0.5.33/auto/lib/openssl/make 2007-01-10 15:21:44.000000000 +0000 +++ nginx-0.8.53/auto/lib/openssl/make 2010-04-02 14:19:45.000000000 +0000 @@ -2,26 +2,62 @@ # Copyright (C) Igor Sysoev -if test -n "$OPENSSL_OPT"; then - NGX_OPENSSL_CONFIG="./Configure \"$OPENSSL_OPT\"" -else - NGX_OPENSSL_CONFIG="./config" -fi - -case $USE_THREADS in - NO) NGX_OPENSSL_CONFIG="$NGX_OPENSSL_CONFIG no-threads" ;; - *) NGX_OPENSSL_CONFIG="$NGX_OPENSSL_CONFIG threads" ;; -esac +case "$CC" in + + cl) + + cat << END >> $NGX_MAKEFILE + +$OPENSSL/openssl/include/openssl/ssl.h: $NGX_MAKEFILE + \$(MAKE) -f auto/lib/openssl/makefile.msvc \ + OPENSSL="$OPENSSL" OPENSSL_OPT="$OPENSSL_OPT" + +END + + ;; + + bcc32) + + ngx_opt=`echo "-DOPENSSL=\"$OPENSSL\" -DOPENSSL_OPT=\"$OPENSSL_OPT\"" \ + | sed -e "s/\//$ngx_regex_dirsep/g"` + + cat << END >> $NGX_MAKEFILE + +`echo "$OPENSSL\\openssl\\lib\\libeay32.lib: \ + $OPENSSL\\openssl\\include\\openssl\\ssl.h" \ + | sed -e "s/\//$ngx_regex_dirsep/g"` + +`echo "$OPENSSL\\openssl\\lib\\ssleay32.lib: \ + $OPENSSL\\openssl\\include\\openssl\\ssl.h" \ + | sed -e "s/\//$ngx_regex_dirsep/g"` + +`echo "$OPENSSL\\openssl\\include\\openssl\\ssl.h: $NGX_MAKEFILE" \ + | sed -e "s/\//$ngx_regex_dirsep/g"` + \$(MAKE) -f auto/lib/openssl/makefile.bcc $ngx_opt + +END + + ;; -case "$NGX_PLATFORM" in *) + case $USE_THREADS in + NO) OPENSSL_OPT="$OPENSSL_OPT no-threads" ;; + *) OPENSSL_OPT="$OPENSSL_OPT threads" ;; + esac + + case $OPENSSL in + /*) ngx_prefix="$OPENSSL/.openssl" ;; + *) ngx_prefix="$PWD/$OPENSSL/.openssl" ;; + esac + cat << END >> $NGX_MAKEFILE -$OPENSSL/libssl.a: +$OPENSSL/.openssl/include/openssl/ssl.h: $NGX_MAKEFILE cd $OPENSSL \\ && \$(MAKE) clean \\ - && $NGX_OPENSSL_CONFIG no-shared \\ - && \$(MAKE) + && ./config --prefix=$ngx_prefix no-shared $OPENSSL_OPT \\ + && \$(MAKE) \\ + && \$(MAKE) install LIBDIR=lib END diff -Nru nginx-0.5.33/auto/lib/openssl/makefile.bcc nginx-0.8.53/auto/lib/openssl/makefile.bcc --- nginx-0.5.33/auto/lib/openssl/makefile.bcc 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/auto/lib/openssl/makefile.bcc 2010-04-01 15:18:29.000000000 +0000 @@ -0,0 +1,17 @@ + +# Copyright (C) Igor Sysoev + + +all: + cd $(OPENSSL) + + perl Configure BC-32 no-shared --prefix=openssl $(OPENSSL_OPT) + + ms\do_nasm + + $(MAKE) -f ms\bcb.mak + $(MAKE) -f ms\bcb.mak install + + # Borland's make does not expand "[ch]" in + # copy "inc32\openssl\*.[ch]" "openssl\include\openssl" + copy inc32\openssl\*.h openssl\include\openssl diff -Nru nginx-0.5.33/auto/lib/openssl/makefile.msvc nginx-0.8.53/auto/lib/openssl/makefile.msvc --- nginx-0.5.33/auto/lib/openssl/makefile.msvc 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/auto/lib/openssl/makefile.msvc 2010-04-01 15:18:29.000000000 +0000 @@ -0,0 +1,13 @@ + +# Copyright (C) Igor Sysoev + + +all: + cd $(OPENSSL) + + perl Configure VC-WIN32 no-shared --prefix=openssl $(OPENSSL_OPT) + + ms\do_ms + + $(MAKE) -f ms\nt.mak + $(MAKE) -f ms\nt.mak install diff -Nru nginx-0.5.33/auto/lib/pcre/conf nginx-0.8.53/auto/lib/pcre/conf --- nginx-0.5.33/auto/lib/pcre/conf 2007-10-29 14:32:18.000000000 +0000 +++ nginx-0.8.53/auto/lib/pcre/conf 2010-07-08 15:57:36.000000000 +0000 @@ -84,6 +84,7 @@ else if [ "$NGX_PLATFORM" != win32 ]; then + PCRE=NO ngx_feature="PCRE library" @@ -95,20 +96,11 @@ ngx_feature_test="pcre *re; re = pcre_compile(NULL, 0, NULL, 0, NULL)" . auto/feature - if [ $ngx_found = yes ]; then - CORE_DEPS="$CORE_DEPS $REGEX_DEPS" - CORE_SRCS="$CORE_SRCS $REGEX_SRCS" - CORE_LIBS="$CORE_LIBS $ngx_feature_libs" - PCRE=YES - ngx_found=no + if [ $ngx_found = no ]; then - else # FreeBSD port ngx_feature="PCRE library in /usr/local/" - ngx_feature_name="NGX_PCRE" - ngx_feature_run=no - ngx_feature_incs="#include " ngx_feature_path="/usr/local/include" if [ $NGX_RPATH = YES ]; then @@ -117,96 +109,50 @@ ngx_feature_libs="-L/usr/local/lib -lpcre" fi - ngx_feature_test="pcre *re; - re = pcre_compile(NULL, 0, NULL, 0, NULL)" . auto/feature fi - if [ $ngx_found = yes ]; then - CORE_DEPS="$CORE_DEPS $REGEX_DEPS" - CORE_SRCS="$CORE_SRCS $REGEX_SRCS" - CORE_INCS="$CORE_INCS $ngx_feature_path" - CORE_LIBS="$CORE_LIBS $ngx_feature_libs" - PCRE=YES - ngx_found=no + if [ $ngx_found = no ]; then - else - # Linux package + # RedHat RPM, Solaris package - if [ $PCRE = NO ]; then + ngx_feature="PCRE library in /usr/include/pcre/" + ngx_feature_path="/usr/include/pcre" + ngx_feature_libs="-lpcre" - ngx_feature="PCRE library in /usr/include/pcre/" - ngx_feature_name="NGX_PCRE" - ngx_feature_run=no - ngx_feature_incs="#include " - ngx_feature_path="/usr/include/pcre" - ngx_feature_libs="-lpcre" - ngx_feature_test="pcre *re; - re = pcre_compile(NULL, 0, NULL, 0, NULL)" - . auto/feature - fi + . auto/feature fi - if [ $ngx_found = yes ]; then - CORE_DEPS="$CORE_DEPS $REGEX_DEPS" - CORE_SRCS="$CORE_SRCS $REGEX_SRCS" - CORE_INCS="$CORE_INCS $ngx_feature_path" - CORE_LIBS="$CORE_LIBS $ngx_feature_libs" - PCRE=YES - ngx_found=no + if [ $ngx_found = no ]; then - else # NetBSD port - if [ $PCRE = NO ]; then + ngx_feature="PCRE library in /usr/pkg/" + ngx_feature_path="/usr/pkg/include" - ngx_feature="PCRE library in /usr/pkg/" - ngx_feature_name="NGX_PCRE" - ngx_feature_run=no - ngx_feature_incs="#include " - ngx_feature_path="/usr/pkg/include" - - if [ $NGX_RPATH = YES ]; then - ngx_feature_libs="-R/usr/pkg/lib -L/usr/pkg/lib -lpcre" - else - ngx_feature_libs="-L/usr/pkg/lib -lpcre" - fi - - ngx_feature_test="pcre *re; - re = pcre_compile(NULL, 0, NULL, 0, NULL)" - . auto/feature + if [ $NGX_RPATH = YES ]; then + ngx_feature_libs="-R/usr/pkg/lib -L/usr/pkg/lib -lpcre" + else + ngx_feature_libs="-L/usr/pkg/lib -lpcre" fi + + . auto/feature fi - if [ $ngx_found = yes ]; then - CORE_DEPS="$CORE_DEPS $REGEX_DEPS" - CORE_SRCS="$CORE_SRCS $REGEX_SRCS" - CORE_INCS="$CORE_INCS $ngx_feature_path" - CORE_LIBS="$CORE_LIBS $ngx_feature_libs" - PCRE=YES - ngx_found=no + if [ $ngx_found = no ]; then - else # MacPorts - if [ $PCRE = NO ]; then + ngx_feature="PCRE library in /opt/local/" + ngx_feature_path="/opt/local/include" - ngx_feature="PCRE library in /opt/local/" - ngx_feature_name="NGX_PCRE" - ngx_feature_run=no - ngx_feature_incs="#include " - ngx_feature_path="/opt/local/include" - - if [ $NGX_RPATH = YES ]; then - ngx_feature_libs="-R/opt/local/lib -L/opt/local/lib -lpcre" - else - ngx_feature_libs="-L/opt/local/lib -lpcre" - fi - - ngx_feature_test="pcre *re; - re = pcre_compile(NULL, 0, NULL, 0, NULL)" - . auto/feature + if [ $NGX_RPATH = YES ]; then + ngx_feature_libs="-R/opt/local/lib -L/opt/local/lib -lpcre" + else + ngx_feature_libs="-L/opt/local/lib -lpcre" fi + + . auto/feature fi if [ $ngx_found = yes ]; then @@ -215,8 +161,19 @@ CORE_INCS="$CORE_INCS $ngx_feature_path" CORE_LIBS="$CORE_LIBS $ngx_feature_libs" PCRE=YES - ngx_found=no fi + fi + + if [ $PCRE != YES ]; then +cat << END +$0: error: the HTTP rewrite module requires the PCRE library. +You can either disable the module by using --without-http_rewrite_module +option, or install the PCRE library into the system, or build the PCRE library +statically from the source with nginx by using --with-pcre= option. + +END + exit 1 fi + fi diff -Nru nginx-0.5.33/auto/lib/pcre/make nginx-0.8.53/auto/lib/pcre/make --- nginx-0.5.33/auto/lib/pcre/make 2007-04-18 11:09:38.000000000 +0000 +++ nginx-0.8.53/auto/lib/pcre/make 2009-05-13 19:48:21.000000000 +0000 @@ -7,16 +7,19 @@ msvc*) ngx_makefile=makefile.msvc ngx_opt="CPU_OPT=\"$CPU_OPT\" LIBC=$LIBC" + ngx_pcre="PCRE=\"$PCRE\"" ;; owc*) ngx_makefile=makefile.owc ngx_opt="CPU_OPT=\"$CPU_OPT\"" + ngx_pcre=`echo PCRE=\"$PCRE\" | sed -e "s/\//$ngx_regex_dirsep/g"` ;; bcc) ngx_makefile=makefile.bcc ngx_opt="-DCPU_OPT=\"$CPU_OPT\"" + ngx_pcre=`echo \-DPCRE=\"$PCRE\" | sed -e "s/\//$ngx_regex_dirsep/g"` ;; esac @@ -25,26 +28,14 @@ case "$NGX_PLATFORM" in win32) - cp auto/lib/pcre/patch.pcre.in $PCRE - cp auto/lib/pcre/patch.pcre.in.owc $PCRE - cp auto/lib/pcre/patch.config.in $PCRE - cp auto/lib/pcre/patch.pcre.c $PCRE - cp auto/lib/pcre/$ngx_makefile $PCRE - - ngx_pcre=`echo $PCRE | sed -e "s/\//$ngx_regex_dirsep/g"` cat << END >> $NGX_MAKEFILE -`echo "$PCRE/pcre.h: $NGX_MAKEFILE" | sed -e "s/\//$ngx_regex_dirsep/g"` - cd $ngx_pcre - \$(MAKE) -f $ngx_makefile pcre.h - cd ..\\..\\.. - +`echo "$PCRE/pcre.lib: $NGX_MAKEFILE" | sed -e "s/\//$ngx_regex_dirsep/g"` + \$(MAKE) -f auto/lib/pcre/$ngx_makefile $ngx_pcre $ngx_opt -`echo "$PCRE/pcre.lib: $PCRE/pcre.h" | sed -e "s/\//$ngx_regex_dirsep/g"` - cd $ngx_pcre - \$(MAKE) -f $ngx_makefile $ngx_opt - cd ..\\..\\.. +`echo "$PCRE/pcre.h:" | sed -e "s/\//$ngx_regex_dirsep/g"` + \$(MAKE) -f auto/lib/pcre/$ngx_makefile $ngx_pcre pcre.h END @@ -61,7 +52,6 @@ && CC="\$(CC)" CFLAGS="$PCRE_OPT" \\ ./configure --disable-shared - $PCRE/.libs/libpcre.a: $PCRE/Makefile cd $PCRE \\ && \$(MAKE) libpcre.la diff -Nru nginx-0.5.33/auto/lib/pcre/makefile.bcc nginx-0.8.53/auto/lib/pcre/makefile.bcc --- nginx-0.5.33/auto/lib/pcre/makefile.bcc 2006-08-30 10:39:17.000000000 +0000 +++ nginx-0.8.53/auto/lib/pcre/makefile.bcc 2009-05-13 19:48:21.000000000 +0000 @@ -3,19 +3,23 @@ CFLAGS = -q -O2 -tWM -w-8004 $(CPU_OPT) -PCREFLAGS = -DPCRE_STATIC -DPOSIX_MALLOC_THRESHOLD=10 +PCREFLAGS = -DHAVE_CONFIG_H -DPCRE_STATIC -DPOSIX_MALLOC_THRESHOLD=10 -pcre.lib: pcre.h - bcc32 -q -edftables dftables.c +pcre.lib: + cd $(PCRE) - dftables > chartables.c + bcc32 -c $(CFLAGS) -I. $(PCREFLAGS) pcre_*.c - bcc32 -c $(CFLAGS) $(PCREFLAGS) maketables.c get.c study.c pcre.c + > pcre.lst + for %n in (*.obj) do @echo +%n & >> pcre.lst + echo + >> pcre.lst - tlib pcre.lib +maketables.obj +get.obj +study.obj +pcre.obj + tlib pcre.lib @pcre.lst pcre.h: - patch -o pcre.h pcre.in patch.pcre.in - patch -o config.h config.in patch.config.in - patch -o pcre.c pcre.c patch.pcre.c + cd $(PCRE) + + copy /y pcre.h.generic pcre.h + copy /y config.h.generic config.h + copy /y pcre_chartables.c.dist pcre_chartables.c diff -Nru nginx-0.5.33/auto/lib/pcre/makefile.msvc nginx-0.8.53/auto/lib/pcre/makefile.msvc --- nginx-0.5.33/auto/lib/pcre/makefile.msvc 2006-08-30 10:39:17.000000000 +0000 +++ nginx-0.8.53/auto/lib/pcre/makefile.msvc 2009-05-13 19:48:21.000000000 +0000 @@ -3,21 +3,19 @@ CFLAGS = -O2 -Ob1 -Oi -Gs $(LIBC) $(CPU_OPT) -PCREFLAGS = -DPCRE_STATIC -DPOSIX_MALLOC_THRESHOLD=10 +PCREFLAGS = -DHAVE_CONFIG_H -DPCRE_STATIC -DPOSIX_MALLOC_THRESHOLD=10 -pcre.lib: pcre.h - cl -Fedftables dftables.c +pcre.lib: + cd $(PCRE) - dftables > chartables.c + cl -nologo -c $(CFLAGS) -I . $(PCREFLAGS) pcre_*.c - cl -nologo -c $(CFLAGS) $(PCREFLAGS) \ - maketables.c get.c study.c pcre.c - - link -lib -out:pcre.lib -verbose:lib \ - maketables.obj get.obj study.obj pcre.obj + link -lib -out:pcre.lib -verbose:lib pcre_*.obj pcre.h: - patch -o pcre.h pcre.in patch.pcre.in - patch -o config.h config.in patch.config.in - patch -o pcre.c pcre.c patch.pcre.c + cd $(PCRE) + + copy /y pcre.h.generic pcre.h + copy /y config.h.generic config.h + copy /y pcre_chartables.c.dist pcre_chartables.c diff -Nru nginx-0.5.33/auto/lib/pcre/makefile.owc nginx-0.8.53/auto/lib/pcre/makefile.owc --- nginx-0.5.33/auto/lib/pcre/makefile.owc 2006-08-30 10:39:17.000000000 +0000 +++ nginx-0.8.53/auto/lib/pcre/makefile.owc 2009-05-13 19:48:21.000000000 +0000 @@ -3,17 +3,21 @@ CFLAGS = -c -zq -bt=nt -ot -op -oi -oe -s -bm $(CPU_OPT) -PCREFLAGS = -DPCRE_STATIC -DPOSIX_MALLOC_THRESHOLD=10 +PCREFLAGS = -DHAVE_CONFIG_H -DPCRE_STATIC -DPOSIX_MALLOC_THRESHOLD=10 -pcre.lib: pcre.h - wcl386 -zq -bt=nt -l=nt -fe=dftables dftables.c - dftables > chartables.c +pcre.lib: + cd $(PCRE) - wcl386 $(CFLAGS) $(PCREFLAGS) maketables.c get.c study.c pcre.c - wlib -n pcre.lib maketables.obj get.obj study.obj pcre.obj + wcl386 $(CFLAGS) -i=. $(PCREFLAGS) pcre_*.c + dir /b *.obj > pcre.lst + + wlib -n pcre.lib @pcre.lst pcre.h: - patch -o pcre.h pcre.in patch.pcre.in.owc - patch -o config.h config.in patch.config.in + cd $(PCRE) + + copy /y pcre.h.generic pcre.h + copy /y config.h.generic config.h + copy /y pcre_chartables.c.dist pcre_chartables.c diff -Nru nginx-0.5.33/auto/lib/pcre/patch.config.in nginx-0.8.53/auto/lib/pcre/patch.config.in --- nginx-0.5.33/auto/lib/pcre/patch.config.in 2006-08-30 10:39:17.000000000 +0000 +++ nginx-0.8.53/auto/lib/pcre/patch.config.in 1970-01-01 00:00:00.000000000 +0000 @@ -1,11 +0,0 @@ ---- config.in Thu Aug 21 14:43:07 2003 -+++ config.in Sun Mar 7 02:37:24 2004 -@@ -28,7 +28,7 @@ - found. */ - - #define HAVE_STRERROR 0 --#define HAVE_MEMMOVE 0 -+#define HAVE_MEMMOVE 1 - - /* There are some non-Unix systems that don't even have bcopy(). If this macro - is false, an emulation is used. If HAVE_MEMMOVE is set to 1, the value of diff -Nru nginx-0.5.33/auto/lib/pcre/patch.pcre.c nginx-0.8.53/auto/lib/pcre/patch.pcre.c --- nginx-0.5.33/auto/lib/pcre/patch.pcre.c 2006-08-30 10:39:17.000000000 +0000 +++ nginx-0.8.53/auto/lib/pcre/patch.pcre.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ ---- pcre.c Thu Aug 21 14:43:07 2003 -+++ pcre.c Tue Mar 22 12:56:59 2005 -@@ -246,8 +246,8 @@ - extern "C" void (*pcre_free)(void *) = free; - extern "C" int (*pcre_callout)(pcre_callout_block *) = NULL; - #else --void *(*pcre_malloc)(size_t) = malloc; --void (*pcre_free)(void *) = free; -+void *(__cdecl *pcre_malloc)(size_t) = malloc; -+void (__cdecl *pcre_free)(void *) = free; - int (*pcre_callout)(pcre_callout_block *) = NULL; - #endif - #endif diff -Nru nginx-0.5.33/auto/lib/pcre/patch.pcre.in nginx-0.8.53/auto/lib/pcre/patch.pcre.in --- nginx-0.5.33/auto/lib/pcre/patch.pcre.in 2006-08-30 10:39:17.000000000 +0000 +++ nginx-0.8.53/auto/lib/pcre/patch.pcre.in 1970-01-01 00:00:00.000000000 +0000 @@ -1,26 +0,0 @@ ---- pcre.in Thu Aug 21 14:43:07 2003 -+++ pcre.h Tue Mar 22 12:56:59 2005 -@@ -10,9 +10,9 @@ - /* The file pcre.h is build by "configure". Do not edit it; instead - make changes to pcre.in. */ - --#define PCRE_MAJOR @PCRE_MAJOR@ --#define PCRE_MINOR @PCRE_MINOR@ --#define PCRE_DATE @PCRE_DATE@ -+#define PCRE_MAJOR 4 -+#define PCRE_MINOR 4 -+#define PCRE_DATE 21-August-2003 - - /* Win32 uses DLL by default */ - -@@ -143,8 +143,8 @@ - have to be different again. */ - - #ifndef VPCOMPAT --PCRE_DATA_SCOPE void *(*pcre_malloc)(size_t); --PCRE_DATA_SCOPE void (*pcre_free)(void *); -+PCRE_DATA_SCOPE void *(__cdecl *pcre_malloc)(size_t); -+PCRE_DATA_SCOPE void (__cdecl *pcre_free)(void *); - PCRE_DATA_SCOPE int (*pcre_callout)(pcre_callout_block *); - #else /* VPCOMPAT */ - extern void *pcre_malloc(size_t); diff -Nru nginx-0.5.33/auto/lib/pcre/patch.pcre.in.owc nginx-0.8.53/auto/lib/pcre/patch.pcre.in.owc --- nginx-0.5.33/auto/lib/pcre/patch.pcre.in.owc 2006-08-30 10:39:17.000000000 +0000 +++ nginx-0.8.53/auto/lib/pcre/patch.pcre.in.owc 1970-01-01 00:00:00.000000000 +0000 @@ -1,15 +0,0 @@ ---- pcre.in Thu Aug 21 14:43:07 2003 -+++ pcre.h Tue Mar 22 12:56:59 2005 -@@ -10,9 +10,9 @@ - /* The file pcre.h is build by "configure". Do not edit it; instead - make changes to pcre.in. */ - --#define PCRE_MAJOR @PCRE_MAJOR@ --#define PCRE_MINOR @PCRE_MINOR@ --#define PCRE_DATE @PCRE_DATE@ -+#define PCRE_MAJOR 4 -+#define PCRE_MINOR 4 -+#define PCRE_DATE 21-August-2003 - - /* Win32 uses DLL by default */ - diff -Nru nginx-0.5.33/auto/lib/sha1/conf nginx-0.8.53/auto/lib/sha1/conf --- nginx-0.5.33/auto/lib/sha1/conf 2006-11-27 11:07:09.000000000 +0000 +++ nginx-0.8.53/auto/lib/sha1/conf 2008-05-16 14:32:58.000000000 +0000 @@ -35,6 +35,7 @@ else if [ "$NGX_PLATFORM" != win32 ]; then + SHA1=NO # FreeBSD @@ -48,35 +49,28 @@ ngx_feature_test="SHA_CTX sha1; SHA1_Init(&sha1)" . auto/feature + ngx_sha1_lib="system md" - if [ $ngx_found = yes ]; then - CORE_LIBS="$CORE_LIBS $ngx_feature_libs" - SHA1=YES - SHA1_LIB=md - ngx_found=no + if [ $ngx_found = no ]; then + + # OpenSSL crypto library - else - if [ $SHA1 = NO ]; then + ngx_feature="OpenSSL sha1 crypto library" + ngx_feature_incs="#include " + ngx_feature_libs="-lcrypto" + . auto/feature - # OpenSSL crypto library + ngx_sha1_lib="system crypto" - ngx_feature="OpenSSL sha1 crypto library" - ngx_feature_name= - ngx_feature_run=no - ngx_feature_incs="#include " - ngx_feature_path= - ngx_feature_libs="-lcrypto" - ngx_feature_test="SHA_CTX sha1; SHA1_Init(&sha1)" - . auto/feature + if [ $ngx_found = yes ]; then + have=NGX_HAVE_OPENSSL_SHA1_H . auto/have fi fi - if [ $ngx_found = yes ]; then - have=NGX_HAVE_OPENSSL_SHA1_H . auto/have CORE_LIBS="$CORE_LIBS $ngx_feature_libs" SHA1=YES - SHA1_LIB=crypto + SHA1_LIB=$ngx_sha1_lib fi fi diff -Nru nginx-0.5.33/auto/lib/sha1/make nginx-0.8.53/auto/lib/sha1/make --- nginx-0.5.33/auto/lib/sha1/make 2006-08-30 10:39:17.000000000 +0000 +++ nginx-0.8.53/auto/lib/sha1/make 2009-05-12 13:15:43.000000000 +0000 @@ -7,16 +7,19 @@ msvc*) ngx_makefile=makefile.msvc ngx_opt="CPU_OPT=\"$CPU_OPT\" LIBC=$LIBC SHA1_ASM=$SHA1_ASM" + ngx_sha1="SHA1=\"$SHA1\"" ;; owc*) ngx_makefile=makefile.owc ngx_opt="CPU_OPT=\"$CPU_OPT\"" + ngx_sha1=`echo SHA1=\"$SHA1\" | sed -e "s/\//$ngx_regex_dirsep/g"` ;; bcc) ngx_makefile=makefile.bcc ngx_opt="-DCPU_OPT=\"$CPU_OPT\" -DSHA1_ASM=$SHA1_ASM" + ngx_sha1=`echo \-DSHA1=\"$SHA1\" | sed -e "s/\//$ngx_regex_dirsep/g"` ;; esac @@ -28,14 +31,10 @@ case "$NGX_PLATFORM" in win32) - cp auto/lib/sha1/$ngx_makefile $SHA1 - cat << END >> $NGX_MAKEFILE `echo "$SHA1/sha1.lib: $NGX_MAKEFILE" | sed -e "s/\//$ngx_regex_dirsep/g"` - cd `echo $SHA1 | sed -e "s/\//$ngx_regex_dirsep/g"` - \$(MAKE) -f $ngx_makefile $ngx_opt - cd ..\\..\\.. + \$(MAKE) -f auto/lib/sha1/$ngx_makefile $ngx_opt $ngx_sha1 END diff -Nru nginx-0.5.33/auto/lib/sha1/makefile.bcc nginx-0.8.53/auto/lib/sha1/makefile.bcc --- nginx-0.5.33/auto/lib/sha1/makefile.bcc 2006-08-30 10:39:17.000000000 +0000 +++ nginx-0.8.53/auto/lib/sha1/makefile.bcc 2009-05-12 13:15:43.000000000 +0000 @@ -7,12 +7,14 @@ !if "$(SHA1_ASM)" == "YES" sha1.lib: + cd $(SHA1) bcc32 -c $(CFLAGS) -DSHA1_ASM sha1dgst.c tlib sha1.lib +sha1dgst.obj +"asm\s-win32.obj" !else sha1.lib: + cd $(SHA1) bcc32 -c $(CFLAGS) sha1dgst.c tlib sha1.lib +sha1dgst.obj diff -Nru nginx-0.5.33/auto/lib/sha1/makefile.msvc nginx-0.8.53/auto/lib/sha1/makefile.msvc --- nginx-0.5.33/auto/lib/sha1/makefile.msvc 2006-08-30 10:39:17.000000000 +0000 +++ nginx-0.8.53/auto/lib/sha1/makefile.msvc 2009-05-12 13:15:43.000000000 +0000 @@ -7,12 +7,14 @@ !IF "$(SHA1_ASM)" == "YES" sha1.lib: + cd $(SHA1) cl -c $(CFLAGS) -D SHA1_ASM sha1dgst.c link -lib -out:sha1.lib sha1dgst.obj asm/s-win32.obj !ELSE sha1.lib: + cd $(SHA1) cl -c $(CFLAGS) sha1dgst.c link -lib -out:sha1.lib sha1dgst.obj diff -Nru nginx-0.5.33/auto/lib/sha1/makefile.owc nginx-0.8.53/auto/lib/sha1/makefile.owc --- nginx-0.5.33/auto/lib/sha1/makefile.owc 2006-08-30 10:39:17.000000000 +0000 +++ nginx-0.8.53/auto/lib/sha1/makefile.owc 2009-05-12 13:15:43.000000000 +0000 @@ -5,5 +5,6 @@ CFLAGS = -zq -bt=nt -bm -ot -op -oi -oe -s $(CPU_OPT) sha1.lib: + cd $(SHA1) wcl386 -c $(CFLAGS) -dL_ENDIAN sha1dgst.c wlib -n sha1.lib sha1dgst.obj diff -Nru nginx-0.5.33/auto/lib/zlib/conf nginx-0.8.53/auto/lib/zlib/conf --- nginx-0.5.33/auto/lib/zlib/conf 2006-11-27 11:07:09.000000000 +0000 +++ nginx-0.8.53/auto/lib/zlib/conf 2010-07-08 15:57:36.000000000 +0000 @@ -60,4 +60,16 @@ fi fi + if [ $ZLIB != YES ]; then +cat << END + +$0: error: the HTTP gzip module requires the zlib library. +You can either disable the module by using --without-http_gzip_module +option, or install the zlib library into the system, or build the zlib library +statically from the source with nginx by using --with-zlib= option. + +END + exit 1 + fi + fi diff -Nru nginx-0.5.33/auto/lib/zlib/make nginx-0.8.53/auto/lib/zlib/make --- nginx-0.5.33/auto/lib/zlib/make 2006-08-30 10:39:17.000000000 +0000 +++ nginx-0.8.53/auto/lib/zlib/make 2010-03-25 10:07:38.000000000 +0000 @@ -7,17 +7,20 @@ msvc*) ngx_makefile=makefile.msvc ngx_opt="CPU_OPT=\"$CPU_OPT\" LIBC=$LIBC" + ngx_zlib="ZLIB=\"$ZLIB\"" ;; owc*) ngx_makefile=makefile.owc ngx_opt="CPU_OPT=\"$CPU_OPT\"" + ngx_zlib=`echo ZLIB=\"$ZLIB\" | sed -e "s/\//$ngx_regex_dirsep/g"` ;; bcc) ngx_makefile=makefile.bcc ngx_opt="-DCPU_OPT=\"$CPU_OPT\"" + ngx_zlib=`echo \-DZLIB=\"$ZLIB\" | sed -e "s/\//$ngx_regex_dirsep/g"` ;; esac @@ -29,14 +32,10 @@ case "$NGX_PLATFORM" in win32) - cp auto/lib/zlib/$ngx_makefile $ZLIB - cat << END >> $NGX_MAKEFILE `echo "$ZLIB/zlib.lib: $NGX_MAKEFILE" | sed -e "s/\//$ngx_regex_dirsep/g"` - cd `echo $ZLIB | sed -e "s/\//$ngx_regex_dirsep/g"` - \$(MAKE) -f $ngx_makefile $ngx_opt - cd ..\\..\\.. + \$(MAKE) -f auto/lib/zlib/$ngx_makefile $ngx_opt $ngx_zlib END @@ -54,7 +53,7 @@ $ZLIB/libz.a: $NGX_MAKEFILE cd $ZLIB \\ - && \$(MAKE) clean \\ + && \$(MAKE) distclean \\ && cp contrib/asm586/match.S . \\ && CFLAGS="$ZLIB_OPT -DASMV" CC="\$(CC)" \\ ./configure \\ @@ -71,7 +70,7 @@ $ZLIB/libz.a: $NGX_MAKEFILE cd $ZLIB \\ - && \$(MAKE) clean \\ + && \$(MAKE) distclean \\ && cp contrib/asm686/match.S . \\ && CFLAGS="$ZLIB_OPT -DASMV" CC="\$(CC)" \\ ./configure \\ @@ -104,7 +103,7 @@ $ZLIB/libz.a: $NGX_MAKEFILE cd $ZLIB \\ - && \$(MAKE) clean \\ + && \$(MAKE) distclean \\ && CFLAGS="$ZLIB_OPT" CC="\$(CC)" \\ ./configure \\ && \$(MAKE) libz.a diff -Nru nginx-0.5.33/auto/lib/zlib/makefile.bcc nginx-0.8.53/auto/lib/zlib/makefile.bcc --- nginx-0.5.33/auto/lib/zlib/makefile.bcc 2006-08-30 10:39:17.000000000 +0000 +++ nginx-0.8.53/auto/lib/zlib/makefile.bcc 2009-05-12 13:15:43.000000000 +0000 @@ -5,6 +5,8 @@ CFLAGS = -q -O2 -tWM -w-8004 -w-8012 $(CPU_OPT) zlib.lib: + cd $(ZLIB) + bcc32 -c $(CFLAGS) adler32.c crc32.c deflate.c trees.c zutil.c \ compress.c diff -Nru nginx-0.5.33/auto/lib/zlib/makefile.msvc nginx-0.8.53/auto/lib/zlib/makefile.msvc --- nginx-0.5.33/auto/lib/zlib/makefile.msvc 2006-08-30 10:39:17.000000000 +0000 +++ nginx-0.8.53/auto/lib/zlib/makefile.msvc 2009-05-12 13:15:43.000000000 +0000 @@ -5,6 +5,8 @@ CFLAGS = -nologo -O2 -Ob1 -Oi -Gs $(LIBC) $(CPU_OPT) zlib.lib: + cd $(ZLIB) + cl -c $(CFLAGS) adler32.c crc32.c deflate.c trees.c zutil.c compress.c link -lib -out:zlib.lib adler32.obj crc32.obj deflate.obj \ diff -Nru nginx-0.5.33/auto/lib/zlib/makefile.owc nginx-0.8.53/auto/lib/zlib/makefile.owc --- nginx-0.5.33/auto/lib/zlib/makefile.owc 2006-08-30 10:39:17.000000000 +0000 +++ nginx-0.8.53/auto/lib/zlib/makefile.owc 2009-05-12 13:29:00.000000000 +0000 @@ -5,5 +5,9 @@ CFLAGS = -zq -bt=nt -ot -op -oi -oe -s -bm $(CPU_OPT) zlib.lib: - wcl386 -c $(CFLAGS) adler32.c crc32.c deflate.c trees.c zutil.c compress.c - wlib -n zlib.lib adler32.obj crc32.obj deflate.obj trees.obj zutil.obj compress.obj + cd $(ZLIB) + + wcl386 -c $(CFLAGS) adler32.c crc32.c deflate.c trees.c zutil.c & + compress.c + wlib -n zlib.lib adler32.obj crc32.obj deflate.obj trees.obj & + zutil.obj compress.obj diff -Nru nginx-0.5.33/auto/make nginx-0.8.53/auto/make --- nginx-0.5.33/auto/make 2007-03-19 13:36:56.000000000 +0000 +++ nginx-0.8.53/auto/make 2009-05-12 13:15:43.000000000 +0000 @@ -2,11 +2,14 @@ # Copyright (C) Igor Sysoev +echo "creating $NGX_MAKEFILE" + mkdir -p $NGX_OBJS/src/core $NGX_OBJS/src/event $NGX_OBJS/src/event/modules \ $NGX_OBJS/src/os/unix $NGX_OBJS/src/os/win32 \ $NGX_OBJS/src/http $NGX_OBJS/src/http/modules \ $NGX_OBJS/src/http/modules/perl \ - $NGX_OBJS/src/mail + $NGX_OBJS/src/mail \ + $NGX_OBJS/src/misc ngx_objs_dir=$NGX_OBJS$ngx_regex_dirsep @@ -22,22 +25,12 @@ END + if test -n "$NGX_PERL_CFLAGS"; then echo NGX_PERL_CFLAGS = $NGX_PERL_CFLAGS >> $NGX_MAKEFILE echo NGX_PM_CFLAGS = $NGX_PM_CFLAGS >> $NGX_MAKEFILE fi -if [ "$BMAKE" = wmake ]; then - echo MAKE = wmake >> $NGX_MAKEFILE - - ngx_regex_cont=' ' - ngx_long_regex_cont=' ' - ngx_cont=' ' - ngx_long_cont=' ' - ngx_tab=' ' - -fi - # ALL_INCS, required by the addons and by OpenWatcom C precompiled headers @@ -127,6 +120,9 @@ fi +ngx_all_srcs="$ngx_all_srcs $NGX_MISC_SRCS" + + if test -n "$NGX_ADDON_SRCS"; then cat << END >> $NGX_MAKEFILE @@ -309,6 +305,32 @@ fi +# the misc sources + +if test -n "$NGX_MISC_SRCS"; then + + ngx_cc="\$(CC) $ngx_compile_opt \$(CFLAGS) $ngx_use_pch \$(ALL_INCS)" + + for ngx_src in $NGX_MISC_SRCS + do + ngx_src=`echo $ngx_src | sed -e "s/\//$ngx_regex_dirsep/g"` + ngx_obj=`echo $ngx_src \ + | sed -e "s#^\(.*\.\)cpp\\$#$ngx_objs_dir\1$ngx_objext#g" \ + -e "s#^\(.*\.\)cc\\$#$ngx_objs_dir\1$ngx_objext#g" \ + -e "s#^\(.*\.\)c\\$#$ngx_objs_dir\1$ngx_objext#g" \ + -e "s#^\(.*\.\)S\\$#$ngx_objs_dir\1$ngx_objext#g"` + + cat << END >> $NGX_MAKEFILE + +$ngx_obj: \$(CORE_DEPS) $ngx_cont$ngx_src + $ngx_cc$ngx_tab$ngx_objout$ngx_obj$ngx_tab$ngx_src$NGX_AUX + +END + done + +fi + + # the addons sources if test -n "$NGX_ADDON_SRCS"; then diff -Nru nginx-0.5.33/auto/modules nginx-0.8.53/auto/modules --- nginx-0.5.33/auto/modules 2007-11-07 14:24:55.000000000 +0000 +++ nginx-0.8.53/auto/modules 2010-10-04 15:03:00.000000000 +0000 @@ -41,6 +41,7 @@ if [ $NGX_TEST_BUILD_EPOLL = YES ]; then have=NGX_HAVE_EPOLL . auto/have + have=NGX_HAVE_EVENTFD . auto/have have=NGX_TEST_BUILD_EPOLL . auto/have EVENT_MODULES="$EVENT_MODULES $EPOLL_MODULE" CORE_SRCS="$CORE_SRCS $EPOLL_SRCS" @@ -65,6 +66,13 @@ fi +if [ $HTTP_CACHE = YES ]; then + USE_MD5=YES + have=NGX_HTTP_CACHE . auto/have + HTTP_SRCS="$HTTP_SRCS $HTTP_FILE_CACHE_SRCS" +fi + + if [ $HTTP_SSI = YES ]; then HTTP_POSTPONE=YES fi @@ -76,8 +84,12 @@ # the module order is important +# ngx_http_static_module +# ngx_http_gzip_static_module +# ngx_http_dav_module # ngx_http_autoindex_module # ngx_http_index_module +# ngx_http_random_index_module # # ngx_http_access_module # ngx_http_realip_module @@ -90,8 +102,11 @@ # ngx_http_range_header_filter # ngx_http_gzip_filter # ngx_http_postpone_filter -# ngx_http_charset_filter # ngx_http_ssi_filter +# ngx_http_charset_filter +# ngx_http_xslt_filter +# ngx_http_image_filter +# ngx_http_sub_filter # ngx_http_addition_filter # ngx_http_userid_filter # ngx_http_headers_filter @@ -113,13 +128,7 @@ if [ $HTTP_POSTPONE = YES ]; then HTTP_FILTER_MODULES="$HTTP_FILTER_MODULES $HTTP_POSTPONE_FILTER_MODULE" - HTTP_SRCS="$HTTP_SRCS $HTPP_POSTPONE_FILTER_SRCS" -fi - -if [ $HTTP_CHARSET = YES ]; then - have=NGX_HTTP_CHARSET . auto/have - HTTP_FILTER_MODULES="$HTTP_FILTER_MODULES $HTTP_CHARSET_FILTER_MODULE" - HTTP_SRCS="$HTTP_SRCS $HTTP_CHARSET_SRCS" + HTTP_SRCS="$HTTP_SRCS $HTTP_POSTPONE_FILTER_SRCS" fi if [ $HTTP_SSI = YES ]; then @@ -129,6 +138,23 @@ HTTP_SRCS="$HTTP_SRCS $HTTP_SSI_SRCS" fi +if [ $HTTP_CHARSET = YES ]; then + HTTP_FILTER_MODULES="$HTTP_FILTER_MODULES $HTTP_CHARSET_FILTER_MODULE" + HTTP_SRCS="$HTTP_SRCS $HTTP_CHARSET_SRCS" +fi + +if [ $HTTP_XSLT = YES ]; then + USE_LIBXSLT=YES + HTTP_FILTER_MODULES="$HTTP_FILTER_MODULES $HTTP_XSLT_FILTER_MODULE" + HTTP_SRCS="$HTTP_SRCS $HTTP_XSLT_SRCS" +fi + +if [ $HTTP_IMAGE_FILTER = YES ]; then + USE_LIBGD=YES + HTTP_FILTER_MODULES="$HTTP_FILTER_MODULES $HTTP_IMAGE_FILTER_MODULE" + HTTP_SRCS="$HTTP_SRCS $HTTP_IMAGE_SRCS" +fi + if [ $HTTP_SUB = YES ]; then HTTP_FILTER_MODULES="$HTTP_FILTER_MODULES $HTTP_SUB_FILTER_MODULE" HTTP_SRCS="$HTTP_SRCS $HTTP_SUB_SRCS" @@ -146,6 +172,12 @@ HTTP_MODULES="$HTTP_MODULES $HTTP_STATIC_MODULE" +if [ $HTTP_GZIP_STATIC = YES ]; then + have=NGX_HTTP_GZIP . auto/have + HTTP_MODULES="$HTTP_MODULES $HTTP_GZIP_STATIC_MODULE" + HTTP_SRCS="$HTTP_SRCS $HTTP_GZIP_STATIC_SRCS" +fi + if [ $HTTP_DAV = YES ]; then have=NGX_HTTP_DAV . auto/have HTTP_MODULES="$HTTP_MODULES $HTTP_DAV_MODULE" @@ -153,15 +185,18 @@ fi if [ $HTTP_AUTOINDEX = YES ]; then - have=NGX_HTTP_AUTOINDEX . auto/have HTTP_MODULES="$HTTP_MODULES $HTTP_AUTOINDEX_MODULE" HTTP_SRCS="$HTTP_SRCS $HTTP_AUTOINDEX_SRCS" fi HTTP_MODULES="$HTTP_MODULES $HTTP_INDEX_MODULE" +if [ $HTTP_RANDOM_INDEX = YES ]; then + HTTP_MODULES="$HTTP_MODULES $HTTP_RANDOM_INDEX_MODULE" + HTTP_SRCS="$HTTP_SRCS $HTTP_RANDOM_INDEX_SRCS" +fi + if [ $HTTP_AUTH_BASIC = YES ]; then - have=NGX_HTTP_AUTH_BASIC . auto/have have=NGX_CRYPT . auto/have HTTP_MODULES="$HTTP_MODULES $HTTP_AUTH_BASIC_MODULE" HTTP_SRCS="$HTTP_SRCS $HTTP_AUTH_BASIC_SRCS" @@ -169,7 +204,6 @@ fi if [ $HTTP_ACCESS = YES ]; then - have=NGX_HTTP_ACCESS . auto/have HTTP_MODULES="$HTTP_MODULES $HTTP_ACCESS_MODULE" HTTP_SRCS="$HTTP_SRCS $HTTP_ACCESS_SRCS" fi @@ -179,6 +213,11 @@ HTTP_SRCS="$HTTP_SRCS $HTTP_LIMIT_ZONE_SRCS" fi +if [ $HTTP_LIMIT_REQ = YES ]; then + HTTP_MODULES="$HTTP_MODULES $HTTP_LIMIT_REQ_MODULE" + HTTP_SRCS="$HTTP_SRCS $HTTP_LIMIT_REQ_SRCS" +fi + if [ $HTTP_REALIP = YES ]; then have=NGX_HTTP_REALIP . auto/have HTTP_MODULES="$HTTP_MODULES $HTTP_REALIP_MODULE" @@ -186,7 +225,6 @@ fi if [ $HTTP_STATUS = YES ]; then - have=NGX_HTTP_STATUS . auto/have HTTP_MODULES="$HTTP_MODULES $HTTP_STATUS_MODULE" HTTP_SRCS="$HTTP_SRCS $HTTP_STATUS_SRCS" fi @@ -197,19 +235,27 @@ HTTP_SRCS="$HTTP_SRCS $HTTP_GEO_SRCS" fi +if [ $HTTP_GEOIP = YES ]; then + HTTP_MODULES="$HTTP_MODULES $HTTP_GEOIP_MODULE" + HTTP_SRCS="$HTTP_SRCS $HTTP_GEOIP_SRCS" +fi + if [ $HTTP_MAP = YES ]; then - have=NGX_HTTP_MAP . auto/have HTTP_MODULES="$HTTP_MODULES $HTTP_MAP_MODULE" HTTP_SRCS="$HTTP_SRCS $HTTP_MAP_SRCS" fi +if [ $HTTP_SPLIT_CLIENTS = YES ]; then + HTTP_MODULES="$HTTP_MODULES $HTTP_SPLIT_CLIENTS_MODULE" + HTTP_SRCS="$HTTP_SRCS $HTTP_SPLIT_CLIENTS_SRCS" +fi + if [ $HTTP_REFERER = YES ]; then HTTP_MODULES="$HTTP_MODULES $HTTP_REFERER_MODULE" HTTP_SRCS="$HTTP_SRCS $HTTP_REFERER_SRCS" fi if [ $HTTP_REWRITE = YES -a $USE_PCRE != DISABLED ]; then - have=NGX_HTTP_REWRITE . auto/have USE_PCRE=YES HTTP_MODULES="$HTTP_MODULES $HTTP_REWRITE_MODULE" HTTP_SRCS="$HTTP_SRCS $HTTP_REWRITE_SRCS" @@ -236,9 +282,18 @@ HTTP_SRCS="$HTTP_SRCS $HTTP_FASTCGI_SRCS" fi +if [ $HTTP_UWSGI = YES ]; then + HTTP_MODULES="$HTTP_MODULES $HTTP_UWSGI_MODULE" + HTTP_SRCS="$HTTP_SRCS $HTTP_UWSGI_SRCS" +fi + +if [ $HTTP_SCGI = YES ]; then + HTTP_MODULES="$HTTP_MODULES $HTTP_SCGI_MODULE" + HTTP_SRCS="$HTTP_SRCS $HTTP_SCGI_SRCS" +fi + if [ $HTTP_PERL = YES ]; then USE_PERL=YES - have=NGX_HTTP_PERL . auto/have HTTP_MODULES="$HTTP_MODULES $HTTP_PERL_MODULE" HTTP_INCS="$HTTP_INCS $HTTP_PERL_INCS" HTTP_DEPS="$HTTP_DEPS $HTTP_PERL_DEPS" @@ -260,6 +315,18 @@ HTTP_SRCS="$HTTP_SRCS $HTTP_BROWSER_SRCS" fi +if [ $HTTP_SECURE_LINK = YES ]; then + USE_MD5=YES + HTTP_MODULES="$HTTP_MODULES $HTTP_SECURE_LINK_MODULE" + HTTP_SRCS="$HTTP_SRCS $HTTP_SECURE_LINK_SRCS" +fi + +if [ $HTTP_DEGRADATION = YES ]; then + have=NGX_HTTP_DEGRADATION . auto/have + HTTP_MODULES="$HTTP_MODULES $HTTP_DEGRADATION_MODULE" + HTTP_SRCS="$HTTP_SRCS $HTTP_DEGRADATION_SRCS" +fi + if [ $HTTP_FLV = YES ]; then HTTP_MODULES="$HTTP_MODULES $HTTP_FLV_MODULE" HTTP_SRCS="$HTTP_SRCS $HTTP_FLV_SRCS" @@ -270,11 +337,6 @@ HTTP_SRCS="$HTTP_SRCS $HTTP_UPSTREAM_IP_HASH_SRCS" fi -# STUB -#USE_MD5=YES -#HTTP_SRCS="$HTTP_SRCS $HTPP_CACHE_SRCS" -#HTTP_SRCS="$HTTP_SRCS $HTPP_FILE_CACHE_SRCS" - if [ $HTTP_STUB_STATUS = YES ]; then have=NGX_STAT_STUB . auto/have HTTP_MODULES="$HTTP_MODULES ngx_http_stub_status_module" @@ -369,6 +431,17 @@ fi +if [ $NGX_GOOGLE_PERFTOOLS = YES ]; then + modules="$modules $NGX_GOOGLE_PERFTOOLS_MODULE" + NGX_MISC_SRCS="$NGX_MISC_SRCS $NGX_GOOGLE_PERFTOOLS_SRCS" +fi + + +if [ $NGX_CPP_TEST = YES ]; then + NGX_MISC_SRCS="$NGX_MISC_SRCS $NGX_CPP_TEST_SRCS" +fi + + cat << END > $NGX_MODULES_C #include diff -Nru nginx-0.5.33/auto/options nginx-0.8.53/auto/options --- nginx-0.5.33/auto/options 2007-11-07 14:24:55.000000000 +0000 +++ nginx-0.8.53/auto/options 2010-06-18 15:51:14.000000000 +0000 @@ -6,6 +6,7 @@ NGX_PREFIX= NGX_SBIN_PATH= +NGX_CONF_PREFIX= NGX_CONF_PATH= NGX_ERROR_LOG_PATH= NGX_PID_PATH= @@ -42,19 +43,27 @@ USE_THREADS=NO +NGX_FILE_AIO=NO +NGX_IPV6=NO + HTTP=YES NGX_HTTP_LOG_PATH= NGX_HTTP_CLIENT_TEMP_PATH= NGX_HTTP_PROXY_TEMP_PATH= NGX_HTTP_FASTCGI_TEMP_PATH= +NGX_HTTP_UWSGI_TEMP_PATH= +NGX_HTTP_SCGI_TEMP_PATH= +HTTP_CACHE=YES HTTP_CHARSET=YES HTTP_GZIP=YES HTTP_SSL=NO HTTP_SSI=YES HTTP_POSTPONE=NO HTTP_REALIP=NO +HTTP_XSLT=NO +HTTP_IMAGE_FILTER=NO HTTP_SUB=NO HTTP_ADDITION=NO HTTP_DAV=NO @@ -62,19 +71,28 @@ HTTP_AUTH_BASIC=YES HTTP_USERID=YES HTTP_AUTOINDEX=YES +HTTP_RANDOM_INDEX=NO HTTP_STATUS=NO HTTP_GEO=YES +HTTP_GEOIP=NO HTTP_MAP=YES +HTTP_SPLIT_CLIENTS=YES HTTP_REFERER=YES HTTP_REWRITE=YES HTTP_PROXY=YES HTTP_FASTCGI=YES +HTTP_UWSGI=YES +HTTP_SCGI=YES HTTP_PERL=NO HTTP_MEMCACHED=YES HTTP_LIMIT_ZONE=YES +HTTP_LIMIT_REQ=YES HTTP_EMPTY_GIF=YES HTTP_BROWSER=YES +HTTP_SECURE_LINK=NO +HTTP_DEGRADATION=NO HTTP_FLV=NO +HTTP_GZIP_STATIC=NO HTTP_UPSTREAM_IP_HASH=YES # STUB @@ -113,11 +131,22 @@ USE_PERL=NO NGX_PERL=perl +USE_LIBXSLT=NO +USE_LIBGD=NO + +NGX_GOOGLE_PERFTOOLS=NO +NGX_CPP_TEST=NO + +NGX_LIBATOMIC=NO + NGX_CPU_CACHE_LINE= +opt= for option do + opt="$opt `echo $option | sed -e \"s/\(--[^=]*=\)\(.* .*\)/\1'\2'/\"`" + case "$option" in -*=*) value=`echo "$option" | sed -e 's/[-_a-zA-Z0-9]*=//'` ;; *) value="" ;; @@ -126,6 +155,7 @@ case "$option" in --help) help=yes ;; + --prefix=) NGX_PREFIX="!" ;; --prefix=*) NGX_PREFIX="$value" ;; --sbin-path=*) NGX_SBIN_PATH="$value" ;; --conf-path=*) NGX_CONF_PATH="$value" ;; @@ -146,21 +176,35 @@ --without-poll_module) EVENT_POLL=NONE ;; --with-aio_module) EVENT_AIO=YES ;; - --with-threads=*) USE_THREADS="$value" ;; - --with-threads) USE_THREADS="pthreads" ;; + #--with-threads=*) USE_THREADS="$value" ;; + #--with-threads) USE_THREADS="pthreads" ;; + + --with-file-aio) NGX_FILE_AIO=YES ;; + --with-ipv6) NGX_IPV6=YES ;; --without-http) HTTP=NO ;; + --without-http-cache) HTTP_CACHE=NO ;; + --http-log-path=*) NGX_HTTP_LOG_PATH="$value" ;; --http-client-body-temp-path=*) NGX_HTTP_CLIENT_TEMP_PATH="$value" ;; --http-proxy-temp-path=*) NGX_HTTP_PROXY_TEMP_PATH="$value" ;; --http-fastcgi-temp-path=*) NGX_HTTP_FASTCGI_TEMP_PATH="$value" ;; + --http-uwsgi-temp-path=*) NGX_HTTP_UWSGI_TEMP_PATH="$value" ;; + --http-scgi-temp-path=*) NGX_HTTP_SCGI_TEMP_PATH="$value" ;; --with-http_ssl_module) HTTP_SSL=YES ;; --with-http_realip_module) HTTP_REALIP=YES ;; --with-http_addition_module) HTTP_ADDITION=YES ;; + --with-http_xslt_module) HTTP_XSLT=YES ;; + --with-http_image_filter_module) HTTP_IMAGE_FILTER=YES ;; + --with-http_geoip_module) HTTP_GEOIP=YES ;; --with-http_sub_module) HTTP_SUB=YES ;; --with-http_dav_module) HTTP_DAV=YES ;; --with-http_flv_module) HTTP_FLV=YES ;; + --with-http_gzip_static_module) HTTP_GZIP_STATIC=YES ;; + --with-http_random_index_module) HTTP_RANDOM_INDEX=YES ;; + --with-http_secure_link_module) HTTP_SECURE_LINK=YES ;; + --with-http_degradation_module) HTTP_DEGRADATION=YES ;; --without-http_charset_module) HTTP_CHARSET=NO ;; --without-http_gzip_module) HTTP_GZIP=NO ;; @@ -172,12 +216,16 @@ --without-http_status_module) HTTP_STATUS=NO ;; --without-http_geo_module) HTTP_GEO=NO ;; --without-http_map_module) HTTP_MAP=NO ;; + --without-http_split_clients_module) HTTP_SPLIT_CLIENTS=NO ;; --without-http_referer_module) HTTP_REFERER=NO ;; --without-http_rewrite_module) HTTP_REWRITE=NO ;; --without-http_proxy_module) HTTP_PROXY=NO ;; --without-http_fastcgi_module) HTTP_FASTCGI=NO ;; + --without-http_uwsgi_module) HTTP_UWSGI=NO ;; + --without-http_scgi_module) HTTP_SCGI=NO ;; --without-http_memcached_module) HTTP_MEMCACHED=NO ;; --without-http_limit_zone_module) HTTP_LIMIT_ZONE=NO ;; + --without-http_limit_req_module) HTTP_LIMIT_REQ=NO ;; --without-http_empty_gif_module) HTTP_EMPTY_GIF=NO ;; --without-http_browser_module) HTTP_BROWSER=NO ;; --without-http_upstream_ip_hash_module) HTTP_UPSTREAM_IP_HASH=NO ;; @@ -198,6 +246,9 @@ --without-mail_imap_module) MAIL_IMAP=NO ;; --without-mail_smtp_module) MAIL_SMTP=NO ;; + --with-google_perftools_module) NGX_GOOGLE_PERFTOOLS=YES ;; + --with-cpp_test_module) NGX_CPP_TEST=YES ;; + --add-module=*) NGX_ADDONS="$NGX_ADDONS $value" ;; --with-cc=*) CC="$value" ;; @@ -208,6 +259,7 @@ --with-debug) NGX_DEBUG=YES ;; --without-pcre) USE_PCRE=DISABLED ;; + --with-pcre) USE_PCRE=YES ;; --with-pcre=*) PCRE="$value" ;; --with-pcre-opt=*) PCRE_OPT="$value" ;; @@ -226,6 +278,9 @@ --with-zlib-opt=*) ZLIB_OPT="$value" ;; --with-zlib-asm=*) ZLIB_ASM="$value" ;; + --with-libatomic) NGX_LIBATOMIC=YES ;; + --with-libatomic=*) NGX_LIBATOMIC="$value" ;; + --test-build-devpoll) NGX_TEST_BUILD_DEVPOLL=YES ;; --test-build-eventport) NGX_TEST_BUILD_EVENTPORT=YES ;; --test-build-epoll) NGX_TEST_BUILD_EPOLL=YES ;; @@ -240,6 +295,9 @@ done +NGX_CONFIGURE="$opt" + + if [ $help = yes ]; then cat << END @@ -266,12 +324,22 @@ --with-poll_module enable poll module --without-poll_module disable poll module + --with-file-aio enable file aio support + --with-ipv6 enable ipv6 support + --with-http_ssl_module enable ngx_http_ssl_module --with-http_realip_module enable ngx_http_realip_module --with-http_addition_module enable ngx_http_addition_module + --with-http_xslt_module enable ngx_http_xslt_module + --with-http_image_filter_module enable ngx_http_image_filter_module + --with-http_geoip_module enable ngx_http_geoip_module --with-http_sub_module enable ngx_http_sub_module --with-http_dav_module enable ngx_http_dav_module --with-http_flv_module enable ngx_http_flv_module + --with-http_gzip_static_module enable ngx_http_gzip_static_module + --with-http_random_index_module enable ngx_http_random_index_module + --with-http_secure_link_module enable ngx_http_secure_link_module + --with-http_degradation_module enable ngx_http_degradation_module --with-http_stub_status_module enable ngx_http_stub_status_module --without-http_charset_module disable ngx_http_charset_module @@ -283,12 +351,16 @@ --without-http_autoindex_module disable ngx_http_autoindex_module --without-http_geo_module disable ngx_http_geo_module --without-http_map_module disable ngx_http_map_module + --without-http_split_clients_module disable ngx_http_split_clients_module --without-http_referer_module disable ngx_http_referer_module --without-http_rewrite_module disable ngx_http_rewrite_module --without-http_proxy_module disable ngx_http_proxy_module --without-http_fastcgi_module disable ngx_http_fastcgi_module + --without-http_uwsgi_module disable ngx_http_uwsgi_module + --without-http_scgi_module disable ngx_http_scgi_module --without-http_memcached_module disable ngx_http_memcached_module --without-http_limit_zone_module disable ngx_http_limit_zone_module + --without-http_limit_req_module disable ngx_http_limit_req_module --without-http_empty_gif_module disable ngx_http_empty_gif_module --without-http_browser_module disable ngx_http_browser_module --without-http_upstream_ip_hash_module @@ -304,8 +376,11 @@ --http-proxy-temp-path=PATH set path to the http proxy temporary files --http-fastcgi-temp-path=PATH set path to the http fastcgi temporary files + --http-uwsgi-temp-path=PATH set path to the http uwsgi temporary files + --http-scgi-temp-path=PATH set path to the http scgi temporary files --without-http disable HTTP server + --without-http-cache disable HTTP cache --with-mail enable POP3/IMAP4/SMTP proxy module --with-mail_ssl_module enable ngx_mail_ssl_module @@ -313,6 +388,9 @@ --without-mail_imap_module disable ngx_mail_imap_module --without-mail_smtp_module disable ngx_mail_smtp_module + --with-google_perftools_module enable ngx_google_perftools_module + --with-cpp_test_module enable ngx_cpp_test_module + --add-module=PATH enable an external module --with-cc=PATH set path to C compiler @@ -323,7 +401,8 @@ pentium, pentiumpro, pentium3, pentium4, athlon, opteron, sparc32, sparc64, ppc64 - --without-pcre disable PCRE libarary usage + --without-pcre disable PCRE library usage + --with-pcre force PCRE library usage --with-pcre=DIR set path to PCRE library sources --with-pcre-opt=OPTIONS set additional options for PCRE building @@ -341,6 +420,9 @@ for specified CPU, the valid values: pentium, pentiumpro + --with-libatomic force libatomic_ops library usage + --with-libatomic=DIR set path to libatomic_ops library sources + --with-openssl=DIR set path to OpenSSL library sources --with-openssl-opt=OPTIONS set additional options for OpenSSL building @@ -370,138 +452,23 @@ fi -NGX_PREFIX=${NGX_PREFIX:-/usr/local/nginx} - - -case ".$NGX_SBIN_PATH" in - ./*) - ;; - - .) - NGX_SBIN_PATH=$NGX_PREFIX/sbin/nginx - ;; - - *) - NGX_SBIN_PATH=$NGX_PREFIX/$NGX_SBIN_PATH - ;; -esac - - -case ".$NGX_CONF_PATH" in - ./*) - ;; - - .) - NGX_CONF_PATH=$NGX_PREFIX/conf/nginx.conf - ;; - - *) - NGX_CONF_PATH=$NGX_PREFIX/$NGX_CONF_PATH - ;; -esac - - -case ".$NGX_PID_PATH" in - ./*) - ;; - - .) - NGX_PID_PATH=$NGX_PREFIX/logs/nginx.pid - ;; - - *) - NGX_PID_PATH=$NGX_PREFIX/$NGX_PID_PATH - ;; -esac - - -case ".$NGX_LOCK_PATH" in - ./*) - ;; - - .) - NGX_LOCK_PATH=$NGX_PREFIX/logs/nginx.lock - ;; - - *) - NGX_LOCK_PATH=$NGX_PREFIX/$NGX_LOCK_PATH - ;; -esac - - -case ".$NGX_ERROR_LOG_PATH" in - ./*) - ;; - - .) - NGX_ERROR_LOG_PATH=$NGX_PREFIX/logs/error.log - ;; - - .stderr) - NGX_ERROR_LOG_PATH= - ;; - - *) - NGX_ERROR_LOG_PATH=$NGX_PREFIX/$NGX_ERROR_LOG_PATH - ;; -esac - - -case ".$NGX_HTTP_LOG_PATH" in - ./*) - ;; - - .) - NGX_HTTP_LOG_PATH=$NGX_PREFIX/logs/access.log - ;; - - *) - NGX_HTTP_LOG_PATH=$NGX_PREFIX/$NGX_HTTP_LOG_PATH - ;; -esac - - -case ".$NGX_HTTP_CLIENT_TEMP_PATH" in - ./*) - ;; - - .) - NGX_HTTP_CLIENT_TEMP_PATH=$NGX_PREFIX/client_body_temp - ;; - - *) - NGX_HTTP_CLIENT_TEMP_PATH=$NGX_PREFIX/$NGX_HTTP_CLIENT_TEMP_PATH - ;; -esac - - -case ".$NGX_HTTP_PROXY_TEMP_PATH" in - ./*) - ;; - - .) - NGX_HTTP_PROXY_TEMP_PATH=$NGX_PREFIX/proxy_temp - ;; - - *) - NGX_HTTP_PROXY_TEMP_PATH=$NGX_PREFIX/$NGX_HTTP_PROXY_TEMP_PATH - ;; -esac - - -case ".$NGX_HTTP_FASTCGI_TEMP_PATH" in - ./*) - ;; - - .) - NGX_HTTP_FASTCGI_TEMP_PATH=$NGX_PREFIX/fastcgi_temp - ;; - - *) - NGX_HTTP_FASTCGI_TEMP_PATH=$NGX_PREFIX/$NGX_HTTP_FASTCGI_TEMP_PATH - ;; -esac +NGX_CONF_PATH=${NGX_CONF_PATH:-conf/nginx.conf} +NGX_CONF_PREFIX=`dirname $NGX_CONF_PATH` +NGX_PID_PATH=${NGX_PID_PATH:-logs/nginx.pid} +NGX_LOCK_PATH=${NGX_LOCK_PATH:-logs/nginx.lock} + +if [ ".$NGX_ERROR_LOG_PATH" = ".stderr" ]; then + NGX_ERROR_LOG_PATH= +else + NGX_ERROR_LOG_PATH=${NGX_ERROR_LOG_PATH:-logs/error.log} +fi +NGX_HTTP_LOG_PATH=${NGX_HTTP_LOG_PATH:-logs/access.log} +NGX_HTTP_CLIENT_TEMP_PATH=${NGX_HTTP_CLIENT_TEMP_PATH:-client_body_temp} +NGX_HTTP_PROXY_TEMP_PATH=${NGX_HTTP_PROXY_TEMP_PATH:-proxy_temp} +NGX_HTTP_FASTCGI_TEMP_PATH=${NGX_HTTP_FASTCGI_TEMP_PATH:-fastcgi_temp} +NGX_HTTP_UWSGI_TEMP_PATH=${NGX_HTTP_UWSGI_TEMP_PATH:-uwsgi_temp} +NGX_HTTP_SCGI_TEMP_PATH=${NGX_HTTP_SCGI_TEMP_PATH:-scgi_temp} case ".$NGX_PERL_MODULES" in ./*) diff -Nru nginx-0.5.33/auto/os/conf nginx-0.8.53/auto/os/conf --- nginx-0.5.33/auto/os/conf 2006-12-22 12:54:08.000000000 +0000 +++ nginx-0.8.53/auto/os/conf 2009-05-10 19:49:14.000000000 +0000 @@ -18,6 +18,10 @@ . auto/os/solaris ;; + Darwin:*) + . auto/os/darwin + ;; + win32) . auto/os/win32 ;; @@ -36,24 +40,6 @@ ' ;; - Darwin:*) - have=NGX_DARWIN . auto/have_headers - have=NGX_HAVE_INHERITED_NONBLOCK . auto/have - CORE_INCS="$UNIX_INCS" - CORE_DEPS="$UNIX_DEPS $POSIX_DEPS" - CORE_SRCS="$UNIX_SRCS" - - ngx_feature="atomic(3)" - ngx_feature_name=NGX_DARWIN_ATOMIC - ngx_feature_run=no - ngx_feature_incs="#include " - ngx_feature_path= - ngx_feature_libs= - ngx_feature_test="int32_t lock, n; - n = OSAtomicCompareAndSwap32Barrier(0, 1, lock)" - . auto/feature - ;; - HP-UX:*) # HP/UX have=NGX_HPUX . auto/have_headers diff -Nru nginx-0.5.33/auto/os/darwin nginx-0.8.53/auto/os/darwin --- nginx-0.5.33/auto/os/darwin 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/auto/os/darwin 2008-07-30 12:18:07.000000000 +0000 @@ -0,0 +1,115 @@ + +# Copyright (C) Igor Sysoev + + +have=NGX_DARWIN . auto/have_headers + +CORE_INCS="$UNIX_INCS" +CORE_DEPS="$UNIX_DEPS $DARWIN_DEPS" +CORE_SRCS="$UNIX_SRCS $DARWIN_SRCS" + + + +ngx_spacer=' +' + +# kqueue + +echo " + kqueue found" +have=NGX_HAVE_KQUEUE . auto/have +have=NGX_HAVE_CLEAR_EVENT . auto/have +EVENT_MODULES="$EVENT_MODULES $KQUEUE_MODULE" +CORE_SRCS="$CORE_SRCS $KQUEUE_SRCS" +EVENT_FOUND=YES +NGX_KQUEUE_CHECKED=YES + +ngx_feature="kqueue's EVFILT_TIMER" +ngx_feature_name="NGX_HAVE_TIMER_EVENT" +ngx_feature_run=yes +ngx_feature_incs="#include + #include " +ngx_feature_path= +ngx_feature_libs= +ngx_feature_test="int kq; + struct kevent kev; + struct timespec ts; + + if ((kq = kqueue()) == -1) return 1; + + kev.ident = 0; + kev.filter = EVFILT_TIMER; + kev.flags = EV_ADD|EV_ENABLE; + kev.fflags = 0; + kev.data = 1000; + kev.udata = 0; + + ts.tv_sec = 0; + ts.tv_nsec = 0; + + if (kevent(kq, &kev, 1, &kev, 1, &ts) == -1) return 1; + + if (kev.flags & EV_ERROR) return 1;" + +. auto/feature + + +ngx_feature="Darwin 64-bit kqueue millisecond timeout bug" +ngx_feature_name=NGX_DARWIN_KEVENT_BUG +ngx_feature_run=bug +ngx_feature_incs="#include + #include " +ngx_feature_path= +ngx_feature_libs= +ngx_feature_test="int kq; + struct kevent kev; + struct timespec ts; + struct timeval tv, tv0; + + kq = kqueue(); + + ts.tv_sec = 0; + ts.tv_nsec = 999000000; + + gettimeofday(&tv, 0); + kevent(kq, NULL, 0, &kev, 1, &ts); + gettimeofday(&tv0, 0); + timersub(&tv0, &tv, &tv); + + if (tv.tv_sec * 1000000 + tv.tv_usec < 900000) return 1;" + +. auto/feature + + +# sendfile() + +CC_AUX_FLAGS="$CC_AUX_FLAGS" +ngx_feature="sendfile()" +ngx_feature_name="NGX_HAVE_SENDFILE" +ngx_feature_run=yes +ngx_feature_incs="#include + #include + #include + #include " +ngx_feature_path= +ngx_feature_libs= +ngx_feature_test="int s = 0, fd = 1; + off_t n; off_t off = 0; + n = sendfile(s, fd, off, &n, NULL, 0); + if (n == -1 && errno == ENOSYS) return 1" +. auto/feature + +if [ $ngx_found = yes ]; then + have=NGX_HAVE_SENDFILE . auto/have + CORE_SRCS="$CORE_SRCS $DARWIN_SENDFILE_SRCS" +fi + + +ngx_feature="atomic(3)" +ngx_feature_name=NGX_DARWIN_ATOMIC +ngx_feature_run=no +ngx_feature_incs="#include " +ngx_feature_path= +ngx_feature_libs= +ngx_feature_test="int32_t lock, n; + n = OSAtomicCompareAndSwap32Barrier(0, 1, lock)" +. auto/feature diff -Nru nginx-0.5.33/auto/os/features nginx-0.8.53/auto/os/features --- nginx-0.5.33/auto/os/features 2006-11-27 11:07:09.000000000 +0000 +++ nginx-0.8.53/auto/os/features 2011-02-26 14:04:40.000000000 +0000 @@ -98,7 +98,7 @@ ngx_feature_name="NGX_HAVE_TIMER_EVENT" ngx_feature_run=yes ngx_feature_incs="#include -#include " + #include " ngx_feature_path= ngx_feature_libs= ngx_feature_test="int kq; @@ -122,36 +122,6 @@ if (kev.flags & EV_ERROR) return 1;" . auto/feature - - - if [ "$NGX_SYSTEM" = "Darwin" ]; then - - ngx_feature="Darwin 64-bit kqueue millisecond timeout bug" - ngx_feature_name=NGX_DARWIN_KEVENT_BUG - ngx_feature_run=bug - ngx_feature_incs="#include -#include " - ngx_feature_path= - ngx_feature_libs= - ngx_feature_test="int kq; - struct kevent kev; - struct timespec ts; - struct timeval tv, tv0; - - kq = kqueue(); - - ts.tv_sec = 0; - ts.tv_nsec = 999000000; - - gettimeofday(&tv, 0); - kevent(kq, NULL, 0, &kev, 1, &ts); - gettimeofday(&tv0, 0); - timersub(&tv0, &tv, &tv); - - if (tv.tv_sec * 1000000 + tv.tv_usec < 900000) return 1;" - - . auto/feature - fi fi fi @@ -200,3 +170,163 @@ CRYPT_LIB="-lcrypt" fi fi + + +ngx_feature="F_READAHEAD" +ngx_feature_name="NGX_HAVE_F_READAHEAD" +ngx_feature_run=no +ngx_feature_incs="#include " +ngx_feature_path= +ngx_feature_libs= +ngx_feature_test="fcntl(0, F_READAHEAD, 1);" +. auto/feature + + +ngx_feature="posix_fadvise()" +ngx_feature_name="NGX_HAVE_POSIX_FADVISE" +ngx_feature_run=no +ngx_feature_incs="#include " +ngx_feature_path= +ngx_feature_libs= +ngx_feature_test="posix_fadvise(0, 0, 0, POSIX_FADV_SEQUENTIAL);" +. auto/feature + + +ngx_feature="O_DIRECT" +ngx_feature_name="NGX_HAVE_O_DIRECT" +ngx_feature_run=no +ngx_feature_incs="#include " +ngx_feature_path= +ngx_feature_libs= +ngx_feature_test="fcntl(0, F_SETFL, O_DIRECT);" +. auto/feature + + +if [ $ngx_found = yes -a "$NGX_SYSTEM" = "Linux" ]; then + have=NGX_HAVE_ALIGNED_DIRECTIO . auto/have +fi + +ngx_feature="F_NOCACHE" +ngx_feature_name="NGX_HAVE_F_NOCACHE" +ngx_feature_run=no +ngx_feature_incs="#include " +ngx_feature_path= +ngx_feature_libs= +ngx_feature_test="fcntl(0, F_NOCACHE, 1);" +. auto/feature + + +ngx_feature="directio()" +ngx_feature_name="NGX_HAVE_DIRECTIO" +ngx_feature_run=no +ngx_feature_incs="#include + #include " +ngx_feature_path= +ngx_feature_libs= +ngx_feature_test="directio(0, DIRECTIO_ON);" +. auto/feature + + +ngx_feature="statfs()" +ngx_feature_name="NGX_HAVE_STATFS" +ngx_feature_run=no +ngx_feature_incs="$NGX_INCLUDE_SYS_PARAM_H + $NGX_INCLUDE_SYS_MOUNT_H + $NGX_INCLUDE_SYS_VFS_H" +ngx_feature_path= +ngx_feature_libs= +ngx_feature_test="struct statfs fs; + statfs(NULL, &fs);" +. auto/feature + + +ngx_feature="statvfs()" +ngx_feature_name="NGX_HAVE_STATVFS" +ngx_feature_run=no +ngx_feature_incs="#include + #include " +ngx_feature_path= +ngx_feature_libs= +ngx_feature_test="struct statvfs fs; + statvfs(NULL, &fs);" +. auto/feature + + +ngx_feature="sched_yield()" +ngx_feature_name="NGX_HAVE_SCHED_YIELD" +ngx_feature_run=no +ngx_feature_incs="#include " +ngx_feature_path= +ngx_feature_libs= +ngx_feature_test="sched_yield()" +. auto/feature + + +if [ $ngx_found != yes ]; then + + ngx_feature="sched_yield() in librt" + ngx_feature_libs="-lrt" + . auto/feature + + if [ $ngx_found = yes ]; then + CORE_LIBS="$CORE_LIBS -lrt" + fi +fi + +ngx_feature="SO_SETFIB" +ngx_feature_name="NGX_HAVE_SETFIB" +ngx_feature_run=no +ngx_feature_incs="#include " +ngx_feature_path= +ngx_feature_libs= +ngx_feature_test="setsockopt(0, SOL_SOCKET, SO_SETFIB, NULL, 4)" +. auto/feature + + +if [ $NGX_FILE_AIO = YES ]; then + + ngx_feature="kqueue AIO support" + ngx_feature_name="NGX_HAVE_FILE_AIO" + ngx_feature_run=no + ngx_feature_incs="#include " + ngx_feature_path= + ngx_feature_libs= + ngx_feature_test="int n; struct aiocb iocb; + iocb.aio_sigevent.sigev_notify = SIGEV_KEVENT; + n = aio_read(&iocb)" + . auto/feature + + if [ $ngx_found = yes ]; then + CORE_SRCS="$CORE_SRCS $FILE_AIO_SRCS" + + elif [ $ngx_found = no ]; then + + ngx_feature="Linux AIO support" + ngx_feature_name="NGX_HAVE_FILE_AIO" + ngx_feature_run=no + ngx_feature_incs="#include + #include " + ngx_feature_path= + ngx_feature_libs= + ngx_feature_test="int n = SYS_eventfd; + struct iocb iocb; + iocb.aio_lio_opcode = IOCB_CMD_PREAD; + iocb.aio_flags = IOCB_FLAG_RESFD; + iocb.aio_resfd = -1;" + . auto/feature + + if [ $ngx_found = yes ]; then + have=NGX_HAVE_EVENTFD . auto/have + CORE_SRCS="$CORE_SRCS $LINUX_AIO_SRCS" + + else + cat << END + +$0: no supported file AIO was found +Currently file AIO is supported on FreeBSD 4.3+ and Linux 2.6.22+ only + +END + exit 1 + fi + fi +fi diff -Nru nginx-0.5.33/auto/os/freebsd nginx-0.8.53/auto/os/freebsd --- nginx-0.5.33/auto/os/freebsd 2006-09-06 19:07:31.000000000 +0000 +++ nginx-0.8.53/auto/os/freebsd 2009-08-30 09:52:39.000000000 +0000 @@ -43,6 +43,12 @@ CORE_SRCS="$CORE_SRCS $FREEBSD_SENDFILE_SRCS" fi +if [ $osreldate -gt 502103 ]; then + echo " + sendfile()'s SF_NODISKIO found" + + have=NGX_HAVE_AIO_SENDFILE . auto/have +fi + # kqueue diff -Nru nginx-0.5.33/auto/os/linux nginx-0.8.53/auto/os/linux --- nginx-0.5.33/auto/os/linux 2006-11-27 11:07:09.000000000 +0000 +++ nginx-0.8.53/auto/os/linux 2009-11-03 16:29:47.000000000 +0000 @@ -11,29 +11,36 @@ ngx_spacer=' ' -CC_AUX_FLAGS="$CC_AUX_FLAGS -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64" +cc_aux_flags="$CC_AUX_FLAGS" +CC_AUX_FLAGS="$cc_aux_flags -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64" # Linux kernel version -version=`grep "#define LINUX_VERSION_CODE" /usr/include/linux/version.h \ - | sed -e 's/^.* \(.*\)$/\1/'` +version=$((`uname -r \ + | sed 's/^\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\).*/\1*256*256+\2*256+\3/'`)) version=${version:-0} -# enable the rt signals on Linux 2.2.19 and onward +# enable the rt signals on Linux between 2.2.19 and 2.6.17 -if [ $version -ge 131609 -o $EVENT_RTSIG = YES ]; then +if [ \( $version -ge 131603 -a $version -lt 132626 \) -o $EVENT_RTSIG = YES ] +then echo " + rt signals found" have=NGX_HAVE_RTSIG . auto/have - have=NGX_HAVE_POLL . auto/have EVENT_MODULES="$EVENT_MODULES $RTSIG_MODULE" CORE_SRCS="$CORE_SRCS $RTSIG_SRCS" EVENT_FOUND=YES fi +# posix_fadvise64() had been implemented in 2.5.60 + +if [ $version -lt 132412 ]; then + have=NGX_HAVE_POSIX_FADVISE . auto/nohave +fi + # epoll, EPOLLET version ngx_feature="epoll" @@ -60,12 +67,12 @@ # sendfile() -CC_AUX_FLAGS="$CC_AUX_FLAGS -D_GNU_SOURCE" +CC_AUX_FLAGS="$cc_aux_flags -D_GNU_SOURCE" ngx_feature="sendfile()" ngx_feature_name="NGX_HAVE_SENDFILE" ngx_feature_run=yes ngx_feature_incs="#include -#include " + #include " ngx_feature_path= ngx_feature_libs= ngx_feature_test="int s = 0, fd = 1; @@ -81,12 +88,12 @@ # sendfile64() -CC_AUX_FLAGS="$CC_AUX_FLAGS -D_FILE_OFFSET_BITS=64" +CC_AUX_FLAGS="$cc_aux_flags -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64" ngx_feature="sendfile64()" ngx_feature_name="NGX_HAVE_SENDFILE64" ngx_feature_run=yes ngx_feature_incs="#include -#include " + #include " ngx_feature_path= ngx_feature_libs= ngx_feature_test="int s = 0, fd = 1; @@ -121,3 +128,22 @@ ngx_feature_test="long mask = 0; sched_setaffinity(0, 32, (cpu_set_t *) &mask)" . auto/feature + + +# crypt_r() + +ngx_feature="crypt_r()" +ngx_feature_name="NGX_HAVE_GNU_CRYPT_R" +ngx_feature_run=no +ngx_feature_incs="#include " +ngx_feature_path= +ngx_feature_libs=-lcrypt +ngx_feature_test="struct crypt_data cd; + crypt_r(NULL, NULL, &cd);" +. auto/feature + + +ngx_include="sys/vfs.h"; . auto/include + + +CC_AUX_FLAGS="$cc_aux_flags -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64" diff -Nru nginx-0.5.33/auto/os/solaris nginx-0.8.53/auto/os/solaris --- nginx-0.5.33/auto/os/solaris 2007-10-29 14:32:18.000000000 +0000 +++ nginx-0.8.53/auto/os/solaris 2009-05-08 09:36:16.000000000 +0000 @@ -7,14 +7,14 @@ CORE_INCS="$UNIX_INCS" CORE_DEPS="$UNIX_DEPS $SOLARIS_DEPS" CORE_SRCS="$UNIX_SRCS $SOLARIS_SRCS " -CORE_LIBS="$CORE_LIBS -lsocket -lnsl -lrt" +CORE_LIBS="$CORE_LIBS -lsocket -lnsl" NGX_RPATH=YES # Solaris's make does not support a blank line between target and rules ngx_spacer= -CC_AUX_FLAGS="$CC_AUX_FLAGS -D_FILE_OFFSET_BITS=64 -lsocket -lnsl -lrt" +CC_AUX_FLAGS="$CC_AUX_FLAGS -D_FILE_OFFSET_BITS=64 -lsocket -lnsl" if [ $ZLIB_ASM != NO ]; then diff -Nru nginx-0.5.33/auto/os/win32 nginx-0.8.53/auto/os/win32 --- nginx-0.5.33/auto/os/win32 2006-08-30 10:39:17.000000000 +0000 +++ nginx-0.8.53/auto/os/win32 2009-05-07 13:05:04.000000000 +0000 @@ -8,8 +8,9 @@ CORE_DEPS="$WIN32_DEPS" CORE_SRCS="$WIN32_SRCS $IOCP_SRCS" OS_CONFIG="$WIN32_CONFIG" -CORE_LIBS="$CORE_LIBS shell32.lib ws2_32.lib" +CORE_LIBS="$CORE_LIBS advapi32.lib ws2_32.lib" NGX_ICONS="$NGX_WIN32_ICONS" +SELECT_SRCS=$WIN32_SELECT_SRCS EVENT_MODULES="$EVENT_MODULES $IOCP_MODULE" EVENT_FOUND=YES @@ -19,5 +20,9 @@ EVENT_MODULES="$EVENT_MODULES $SELECT_MODULE" fi +if [ $NGX_IPV6 = YES ]; then + have=NGX_HAVE_INET6 . auto/have +fi + have=NGX_HAVE_AIO . auto/have have=NGX_HAVE_IOCP . auto/have diff -Nru nginx-0.5.33/auto/sources nginx-0.8.53/auto/sources --- nginx-0.5.33/auto/sources 2007-11-07 14:24:55.000000000 +0000 +++ nginx-0.8.53/auto/sources 2010-06-18 15:51:14.000000000 +0000 @@ -15,12 +15,15 @@ src/core/ngx_list.h \ src/core/ngx_hash.h \ src/core/ngx_buf.h \ + src/core/ngx_queue.h \ src/core/ngx_string.h \ src/core/ngx_parse.h \ src/core/ngx_inet.h \ src/core/ngx_file.h \ src/core/ngx_crc.h \ src/core/ngx_crc32.h \ + src/core/ngx_md5.h \ + src/core/ngx_sha1.h \ src/core/ngx_rbtree.h \ src/core/ngx_radix_tree.h \ src/core/ngx_slab.h \ @@ -29,7 +32,8 @@ src/core/ngx_connection.h \ src/core/ngx_cycle.h \ src/core/ngx_conf_file.h \ - src/core/ngx_garbage_collector.h" + src/core/ngx_resolver.h \ + src/core/ngx_open_file_cache.h" CORE_SRCS="src/core/nginx.c \ @@ -39,6 +43,7 @@ src/core/ngx_list.c \ src/core/ngx_hash.c \ src/core/ngx_buf.c \ + src/core/ngx_queue.c \ src/core/ngx_output_chain.c \ src/core/ngx_string.c \ src/core/ngx_parse.c \ @@ -55,7 +60,8 @@ src/core/ngx_spinlock.c \ src/core/ngx_cpuinfo.c \ src/core/ngx_conf_file.c \ - src/core/ngx_garbage_collector.c" + src/core/ngx_resolver.c \ + src/core/ngx_open_file_cache.c" REGEX_DEPS=src/core/ngx_regex.h @@ -89,6 +95,7 @@ SELECT_MODULE=ngx_select_module SELECT_SRCS=src/event/modules/ngx_select_module.c +WIN32_SELECT_SRCS=src/event/modules/ngx_win32_select_module.c POLL_MODULE=ngx_poll_module POLL_SRCS=src/event/modules/ngx_poll_module.c @@ -118,12 +125,13 @@ src/os/unix/ngx_aio_read_chain.c \ src/os/unix/ngx_aio_write_chain.c" +FILE_AIO_SRCS="src/os/unix/ngx_file_aio_read.c" +LINUX_AIO_SRCS="src/os/unix/ngx_linux_aio_read.c" UNIX_INCS="$CORE_INCS $EVENT_INCS src/os/unix" UNIX_DEPS="$CORE_DEPS $EVENT_DEPS \ src/os/unix/ngx_time.h \ - src/os/unix/ngx_types.h \ src/os/unix/ngx_errno.h \ src/os/unix/ngx_alloc.h \ src/os/unix/ngx_files.h \ @@ -157,6 +165,7 @@ src/os/unix/ngx_socket.c \ src/os/unix/ngx_recv.c \ src/os/unix/ngx_readv_chain.c \ + src/os/unix/ngx_udp_recv.c \ src/os/unix/ngx_send.c \ src/os/unix/ngx_writev_chain.c \ src/os/unix/ngx_channel.c \ @@ -170,7 +179,7 @@ POSIX_DEPS=src/os/unix/ngx_posix_config.h -FREEBSD_DEPS=src/os/unix/ngx_freebsd_config.h +FREEBSD_DEPS="src/os/unix/ngx_freebsd_config.h src/os/unix/ngx_freebsd.h" FREEBSD_SRCS=src/os/unix/ngx_freebsd_init.c FREEBSD_SENDFILE_SRCS=src/os/unix/ngx_freebsd_sendfile_chain.c FREEBSD_RFORK_DEPS="src/os/unix/ngx_freebsd_rfork_thread.h" @@ -179,22 +188,26 @@ PTHREAD_SRCS="src/os/unix/ngx_pthread_thread.c" -LINUX_DEPS=src/os/unix/ngx_linux_config.h +LINUX_DEPS="src/os/unix/ngx_linux_config.h src/os/unix/ngx_linux.h" LINUX_SRCS=src/os/unix/ngx_linux_init.c LINUX_SENDFILE_SRCS=src/os/unix/ngx_linux_sendfile_chain.c -SOLARIS_DEPS=src/os/unix/ngx_solaris_config.h +SOLARIS_DEPS="src/os/unix/ngx_solaris_config.h src/os/unix/ngx_solaris.h" SOLARIS_SRCS=src/os/unix/ngx_solaris_init.c SOLARIS_SENDFILEV_SRCS=src/os/unix/ngx_solaris_sendfilev_chain.c +DARWIN_DEPS="src/os/unix/ngx_darwin_config.h src/os/unix/ngx_darwin.h" +DARWIN_SRCS=src/os/unix/ngx_darwin_init.c +DARWIN_SENDFILE_SRCS=src/os/unix/ngx_darwin_sendfile_chain.c + + WIN32_INCS="$CORE_INCS $EVENT_INCS src/os/win32" WIN32_DEPS="$CORE_DEPS $EVENT_DEPS \ src/os/win32/ngx_win32_config.h \ src/os/win32/ngx_time.h \ - src/os/win32/ngx_types.h \ src/os/win32/ngx_errno.h \ src/os/win32/ngx_alloc.h \ src/os/win32/ngx_files.h \ @@ -205,8 +218,6 @@ src/os/win32/ngx_socket.h \ src/os/win32/ngx_os.h \ src/os/win32/ngx_user.h \ - src/os/win32/ngx_gui.h \ - src/os/win32/ngx_gui_resources.h \ src/os/win32/ngx_process_cycle.h" WIN32_CONFIG=src/os/win32/ngx_win32_config.h @@ -222,14 +233,16 @@ src/os/win32/ngx_socket.c \ src/os/win32/ngx_wsarecv.c \ src/os/win32/ngx_wsarecv_chain.c \ + src/os/win32/ngx_udp_wsarecv.c \ + src/os/win32/ngx_wsasend.c \ src/os/win32/ngx_wsasend_chain.c \ src/os/win32/ngx_win32_init.c \ src/os/win32/ngx_user.c \ - src/os/win32/ngx_gui.c \ + src/os/win32/ngx_event_log.c \ src/os/win32/ngx_process_cycle.c \ src/event/ngx_event_acceptex.c" -NGX_WIN32_ICONS="src/os/win32/nginx.ico src/os/win32/nginx_tray.ico" +NGX_WIN32_ICONS="src/os/win32/nginx.ico" NGX_WIN32_RC="src/os/win32/nginx.rc" @@ -241,8 +254,6 @@ ngx_http_log_module \ ngx_http_upstream_module" -HTTP_CACHE_MODULE=ngx_http_cache_module - HTTP_WRITE_FILTER_MODULE="ngx_http_write_filter_module" HTTP_HEADER_FILTER_MODULE="ngx_http_header_filter_module" @@ -298,10 +309,9 @@ # STUB HTTP_SRCS="$HTTP_SRCS src/http/ngx_http_busy_lock.c" -HTPP_POSTPONE_FILTER_SRCS=src/http/ngx_http_postpone_filter_module.c +HTTP_POSTPONE_FILTER_SRCS=src/http/ngx_http_postpone_filter_module.c -HTPP_CACHE_SRCS=src/http/ngx_http_cache.c -HTPP_FILE_CACHE_SRCS=src/http/ngx_http_file_cache.c +HTTP_FILE_CACHE_SRCS=src/http/ngx_http_file_cache.c HTTP_CHARSET_FILTER_MODULE=ngx_http_charset_filter_module @@ -317,6 +327,14 @@ HTTP_SSI_SRCS=src/http/modules/ngx_http_ssi_filter_module.c +HTTP_XSLT_FILTER_MODULE=ngx_http_xslt_filter_module +HTTP_XSLT_SRCS=src/http/modules/ngx_http_xslt_filter_module.c + + +HTTP_IMAGE_FILTER_MODULE=ngx_http_image_filter_module +HTTP_IMAGE_SRCS=src/http/modules/ngx_http_image_filter_module.c + + HTTP_SUB_FILTER_MODULE=ngx_http_sub_filter_module HTTP_SUB_SRCS=src/http/modules/ngx_http_sub_filter_module.c @@ -349,6 +367,10 @@ HTTP_AUTOINDEX_SRCS=src/http/modules/ngx_http_autoindex_module.c +HTTP_RANDOM_INDEX_MODULE=ngx_http_random_index_module +HTTP_RANDOM_INDEX_SRCS=src/http/modules/ngx_http_random_index_module.c + + HTTP_STATUS_MODULE=ngx_http_status_module HTTP_STATUS_SRCS=src/http/modules/ngx_http_status_module.c @@ -357,10 +379,18 @@ HTTP_GEO_SRCS=src/http/modules/ngx_http_geo_module.c +HTTP_GEOIP_MODULE=ngx_http_geoip_module +HTTP_GEOIP_SRCS=src/http/modules/ngx_http_geoip_module.c + + HTTP_MAP_MODULE=ngx_http_map_module HTTP_MAP_SRCS=src/http/modules/ngx_http_map_module.c +HTTP_SPLIT_CLIENTS_MODULE=ngx_http_split_clients_module +HTTP_SPLIT_CLIENTS_SRCS=src/http/modules/ngx_http_split_clients_module.c + + HTTP_REFERER_MODULE=ngx_http_referer_module HTTP_REFERER_SRCS=src/http/modules/ngx_http_referer_module.c @@ -382,6 +412,14 @@ HTTP_FASTCGI_SRCS=src/http/modules/ngx_http_fastcgi_module.c +HTTP_UWSGI_MODULE=ngx_http_uwsgi_module +HTTP_UWSGI_SRCS=src/http/modules/ngx_http_uwsgi_module.c + + +HTTP_SCGI_MODULE=ngx_http_scgi_module +HTTP_SCGI_SRCS=src/http/modules/ngx_http_scgi_module.c + + HTTP_PERL_MODULE=ngx_http_perl_module HTTP_PERL_INCS=src/http/modules/perl HTTP_PERL_DEPS=src/http/modules/perl/ngx_http_perl_module.h @@ -396,6 +434,10 @@ HTTP_LIMIT_ZONE_SRCS=src/http/modules/ngx_http_limit_zone_module.c +HTTP_LIMIT_REQ_MODULE=ngx_http_limit_req_module +HTTP_LIMIT_REQ_SRCS=src/http/modules/ngx_http_limit_req_module.c + + HTTP_EMPTY_GIF_MODULE=ngx_http_empty_gif_module HTTP_EMPTY_GIF_SRCS=src/http/modules/ngx_http_empty_gif_module.c @@ -404,10 +446,22 @@ HTTP_BROWSER_SRCS=src/http/modules/ngx_http_browser_module.c +HTTP_SECURE_LINK_MODULE=ngx_http_secure_link_module +HTTP_SECURE_LINK_SRCS=src/http/modules/ngx_http_secure_link_module.c + + +HTTP_DEGRADATION_MODULE=ngx_http_degradation_module +HTTP_DEGRADATION_SRCS=src/http/modules/ngx_http_degradation_module.c + + HTTP_FLV_MODULE=ngx_http_flv_module HTTP_FLV_SRCS=src/http/modules/ngx_http_flv_module.c +HTTP_GZIP_STATIC_MODULE=ngx_http_gzip_static_module +HTTP_GZIP_STATIC_SRCS=src/http/modules/ngx_http_gzip_static_module.c + + HTTP_UPSTREAM_IP_HASH_MODULE=ngx_http_upstream_ip_hash_module HTTP_UPSTREAM_IP_HASH_SRCS=src/http/modules/ngx_http_upstream_ip_hash_module.c @@ -447,3 +501,8 @@ MAIL_PROXY_MODULE="ngx_mail_proxy_module" MAIL_PROXY_SRCS="src/mail/ngx_mail_proxy_module.c" + +NGX_GOOGLE_PERFTOOLS_MODULE=ngx_google_perftools_module +NGX_GOOGLE_PERFTOOLS_SRCS=src/misc/ngx_google_perftools_module.c + +NGX_CPP_TEST_SRCS=src/misc/ngx_cpp_test_module.cpp diff -Nru nginx-0.5.33/auto/stubs nginx-0.8.53/auto/stubs --- nginx-0.5.33/auto/stubs 2006-08-30 10:39:17.000000000 +0000 +++ nginx-0.8.53/auto/stubs 2009-04-23 18:50:29.000000000 +0000 @@ -2,7 +2,6 @@ # Copyright (C) Igor Sysoev -have=NGX_USE_HTTP_FILE_CACHE_UNIQ . auto/have have=NGX_SUPPRESS_WARN . auto/have have=NGX_SMP . auto/have diff -Nru nginx-0.5.33/auto/summary nginx-0.8.53/auto/summary --- nginx-0.5.33/auto/summary 2006-08-30 10:39:17.000000000 +0000 +++ nginx-0.8.53/auto/summary 2010-07-08 15:57:36.000000000 +0000 @@ -21,15 +21,15 @@ echo "Configuration summary" -case $USE_THREADS in - rfork) echo " + using rfork()ed threads" ;; - pthreads) echo " + using libpthread threads library" ;; - libthr) echo " + using FreeBSD libthr threads library" ;; - libc_r) echo " + using FreeBSD libc_r threads library" ;; - linuxthreads) echo " + using FreeBSD LinuxThreads port library" ;; - NO) echo " + threads are not used" ;; - *) echo " + using lib$USE_THREADS threads library" ;; -esac +#case $USE_THREADS in +# rfork) echo " + using rfork()ed threads" ;; +# pthreads) echo " + using libpthread threads library" ;; +# libthr) echo " + using FreeBSD libthr threads library" ;; +# libc_r) echo " + using FreeBSD libc_r threads library" ;; +# linuxthreads) echo " + using FreeBSD LinuxThreads port library" ;; +# NO) echo " + threads are not used" ;; +# *) echo " + using lib$USE_THREADS threads library" ;; +#esac if [ $USE_PCRE = DISABLED ]; then echo " + PCRE library is disabled" @@ -38,7 +38,6 @@ case $PCRE in YES) echo " + using system PCRE library" ;; NONE) echo " + PCRE library is not used" ;; - NO) echo " + PCRE library is not found" ;; *) echo " + using PCRE library: $PCRE" ;; esac fi @@ -46,31 +45,17 @@ case $OPENSSL in YES) echo " + using system OpenSSL library" ;; NONE) echo " + OpenSSL library is not used" ;; - NO) echo " + OpenSSL library is not found" ;; *) echo " + using OpenSSL library: $OPENSSL" ;; esac case $MD5 in - YES) - case $OPENSSL in - NONE|NO) echo " + md5: using system $MD5_LIB library" ;; - *) echo " + md5: using OpenSSL library" ;; - esac - ;; - + YES) echo " + md5: using $MD5_LIB library" ;; NONE) echo " + md5 library is not used" ;; - NO) echo " + md5 library is not found" ;; *) echo " + using md5 library: $MD5" ;; esac case $SHA1 in - YES) - case $OPENSSL in - NONE|NO) echo " + sha1: using system $SHA1_LIB library" ;; - *) echo " + sha1: using OpenSSL library" ;; - esac - ;; - + YES) echo " + sha1: using $SHA1_LIB library" ;; NONE) echo " + sha1 library is not used" ;; NO) echo " + sha1 library is not found" ;; *) echo " + using sha1 library: $SHA1" ;; @@ -79,75 +64,22 @@ case $ZLIB in YES) echo " + using system zlib library" ;; NONE) echo " + zlib library is not used" ;; - NO) echo " + zlib library is not found" ;; *) echo " + using zlib library: $ZLIB" ;; esac -echo - - -if [ $HTTP_REWRITE = YES ]; then - if [ $USE_PCRE = DISABLED ]; then - -cat << END -$0: error: the HTTP rewrite module requires the PCRE library. -You can either disable the module by using --without-http_rewrite_module -option or you have to enable the PCRE support. - -END - exit 1 - fi - - if [ $PCRE = NONE -o $PCRE = NO ]; then - -cat << END -$0: error: the HTTP rewrite module requires the PCRE library. -You can either disable the module by using --without-http_rewrite_module -option, or install the PCRE library into the system, or build the PCRE library -statically from the source with nginx by using --with-pcre= option. - -END - - exit 1 - fi -fi - - -if [ $HTTP_GZIP = YES ]; then - if [ $ZLIB = NONE -o $ZLIB = NO ]; then - -cat << END -$0: error: the HTTP gzip module requires the zlib library. -You can either disable the module by using --without-http_gzip_module -option, or install the zlib library into the system, or build the zlib library -statically from the source with nginx by using --with-zlib= option. - -END - - exit 1 - fi -fi - - -if [ $HTTP_SSL = YES ]; then - if [ $OPENSSL = NONE -o $OPENSSL = NO ]; then - -cat << END -$0: error: the HTTP SSL module requires the OpenSSL library. -You can either do not enable the module, or install the OpenSSL library -into the system, or build the OpenSSL library statically from the source -with nginx by using --with-openssl= option. - -END +case $NGX_LIBATOMIC in + YES) echo " + using system libatomic_ops library" ;; + NO) ;; # not used + *) echo " + using libatomic_ops library: $NGX_LIBATOMIC" ;; +esac - exit 1 - fi -fi +echo cat << END nginx path prefix: "$NGX_PREFIX" nginx binary file: "$NGX_SBIN_PATH" + nginx configuration prefix: "$NGX_CONF_PREFIX" nginx configuration file: "$NGX_CONF_PATH" nginx pid file: "$NGX_PID_PATH" END @@ -161,7 +93,20 @@ cat << END nginx http access log file: "$NGX_HTTP_LOG_PATH" nginx http client request body temporary files: "$NGX_HTTP_CLIENT_TEMP_PATH" - nginx http proxy temporary files: "$NGX_HTTP_PROXY_TEMP_PATH" - nginx http fastcgi temporary files: "$NGX_HTTP_FASTCGI_TEMP_PATH" - END + +if [ $HTTP_PROXY = YES ]; then + echo " nginx http proxy temporary files: \"$NGX_HTTP_PROXY_TEMP_PATH\"" +fi + +if [ $HTTP_FASTCGI = YES ]; then + echo " nginx http fastcgi temporary files: \"$NGX_HTTP_FASTCGI_TEMP_PATH\"" +fi + +if [ $HTTP_UWSGI = YES ]; then + echo " nginx http uwsgi temporary files: \"$NGX_HTTP_UWSGI_TEMP_PATH\"" +fi + +if [ $HTTP_SCGI = YES ]; then + echo " nginx http scgi temporary files: \"$NGX_HTTP_SCGI_TEMP_PATH\"" +fi diff -Nru nginx-0.5.33/auto/unix nginx-0.8.53/auto/unix --- nginx-0.5.33/auto/unix 2006-11-27 11:07:09.000000000 +0000 +++ nginx-0.8.53/auto/unix 2010-03-12 11:15:26.000000000 +0000 @@ -64,6 +64,21 @@ # syscalls, libc calls and some features +if [ $NGX_IPV6 = YES ]; then + ngx_feature="AF_INET6" + ngx_feature_name="NGX_HAVE_INET6" + ngx_feature_run=no + ngx_feature_incs="#include + #include + #include " + ngx_feature_path= + ngx_feature_libs= + ngx_feature_test="struct sockaddr_in6 sin6; + sin6.sin6_family = AF_INET6;" + . auto/feature +fi + + ngx_feature="setproctitle()" ngx_feature_name="NGX_HAVE_SETPROCTITLE" ngx_feature_run=no @@ -118,6 +133,16 @@ . auto/feature +ngx_feature="sys_errlist[]" +ngx_feature_name="NGX_HAVE_SYS_ERRLIST" +ngx_feature_run=yes +ngx_feature_incs="#include " +ngx_feature_path= +ngx_feature_libs= +ngx_feature_test="int n = sys_nerr; const char *p = sys_errlist[1];" +. auto/feature + + ngx_feature="localtime_r()" ngx_feature_name="NGX_HAVE_LOCALTIME_R" ngx_feature_run=no @@ -148,16 +173,6 @@ . auto/feature -ngx_feature="sched_yield()" -ngx_feature_name="NGX_HAVE_SCHED_YIELD" -ngx_feature_run=no -ngx_feature_incs="#include " -ngx_feature_path= -ngx_feature_libs= -ngx_feature_test="sched_yield()" -. auto/feature - - ngx_feature="mmap(MAP_ANON|MAP_SHARED)" ngx_feature_name="NGX_HAVE_MAP_ANON" ngx_feature_run=yes @@ -175,8 +190,8 @@ ngx_feature_name="NGX_HAVE_MAP_DEVZERO" ngx_feature_run=yes ngx_feature_incs="#include -#include -#include " + #include + #include " ngx_feature_path= ngx_feature_libs= ngx_feature_test='void *p; int fd; @@ -190,7 +205,7 @@ ngx_feature_name="NGX_HAVE_SYSVSHM" ngx_feature_run=yes ngx_feature_incs="#include -#include " + #include " ngx_feature_path= ngx_feature_libs= ngx_feature_test="int id; @@ -214,7 +229,7 @@ ngx_feature_name="NGX_HAVE_FIONBIO" ngx_feature_run=no ngx_feature_incs="#include -$NGX_INCLUDE_SYS_FILIO_H" + $NGX_INCLUDE_SYS_FILIO_H" ngx_feature_path= ngx_feature_libs= ngx_feature_test="int i; i = FIONBIO" @@ -229,3 +244,23 @@ ngx_feature_libs= ngx_feature_test="struct tm tm; tm.tm_gmtoff = 0" . auto/feature + + +ngx_feature="struct dirent.d_namlen" +ngx_feature_name="NGX_HAVE_D_NAMLEN" +ngx_feature_run=no +ngx_feature_incs="#include " +ngx_feature_path= +ngx_feature_libs= +ngx_feature_test="struct dirent dir; dir.d_namlen = 0" +. auto/feature + + +ngx_feature="struct dirent.d_type" +ngx_feature_name="NGX_HAVE_D_TYPE" +ngx_feature_run=no +ngx_feature_incs="#include " +ngx_feature_path= +ngx_feature_libs= +ngx_feature_test="struct dirent dir; dir.d_type = DT_REG" +. auto/feature diff -Nru nginx-0.5.33/CHANGES nginx-0.8.53/CHANGES --- nginx-0.5.33/CHANGES 2007-11-07 14:32:57.000000000 +0000 +++ nginx-0.8.53/CHANGES 2010-10-18 12:03:33.000000000 +0000 @@ -1,71 +1,2284 @@ -Changes with nginx 0.5.33 07 Nov 2007 +Changes with nginx 0.8.53 18 Oct 2010 - *) Change: now by default the "echo" SSI command uses entity encoding. + *) Feature: now the "error_page" directive allows to change a status + code in a redirect. + + *) Feature: the "gzip_disable" directive supports special "degradation" + mask. + + *) Bugfix: a socket leak might occurred if file AIO was used. + Thanks to Maxim Dounin. + + *) Bugfix: if the first server had no "listen" directive and there was + no explicit default server, then a next server with a "listen" + directive became the default server; the bug had appeared in 0.8.21. + + +Changes with nginx 0.8.52 28 Sep 2010 + + *) Bugfix: nginx used SSL mode for a listen socket if any listen option + was set; the bug had appeared in 0.8.51. + + +Changes with nginx 0.8.51 27 Sep 2010 + + *) Change: the "secure_link_expires" directive has been canceled. + + *) Change: a logging level of resolver errors has been lowered from + "alert" to "error". + + *) Feature: now a listen socket "ssl" parameter may be set several + times. + + +Changes with nginx 0.8.50 02 Sep 2010 + + *) Feature: the "secure_link", "secure_link_md5", and + "secure_link_expires" directives of the ngx_http_secure_link_module. + + *) Feature: the -q switch. + Thanks to Gena Makhomed. + + *) Bugfix: worker processes may got caught in an endless loop during + reconfiguration, if a caching was used; the bug had appeared in + 0.8.48. + + *) Bugfix: in the "gzip_disable" directive. + Thanks to Derrick Petzold. + + *) Bugfix: nginx/Windows could not send stop, quit, reopen, and reload + signals to a process run in other session. + + +Changes with nginx 0.8.49 09 Aug 2010 + + *) Feature: the "image_filter_jpeg_quality" directive supports + variables. + + *) Bugfix: a segmentation fault might occur in a worker process, if the + $geoip_region_name variables was used; the bug had appeared in + 0.8.48. + + *) Bugfix: errors intercepted by error_page were cached only for next + request; the bug had appeared in 0.8.48. + + +Changes with nginx 0.8.48 03 Aug 2010 + + *) Change: now the "server_name" directive default value is an empty + name "". + Thanks to Gena Makhomed. + + *) Change: now the "server_name_in_redirect" directive default value is + "off". + + *) Feature: the $geoip_dma_code, $geoip_area_code, and + $geoip_region_name variables. + Thanks to Christine McGonagle. + + *) Bugfix: the "proxy_pass", "fastcgi_pass", "uwsgi_pass", and + "scgi_pass" directives were not inherited inside "limit_except" + blocks. + + *) Bugfix: the "proxy_cache_min_uses", "fastcgi_cache_min_uses" + "uwsgi_cache_min_uses", and "scgi_cache_min_uses" directives did not + work; the bug had appeared in 0.8.46. + + *) Bugfix: the "fastcgi_split_path_info" directive used incorrectly + captures, if only parts of an URI were captured. + Thanks to Yuriy Taraday and Frank Enderle. + + *) Bugfix: the "rewrite" directive did not escape a ";" character + during copying from URI to query string. + Thanks to Daisuke Murase. + + *) Bugfix: the ngx_http_image_filter_module closed a connection, if an + image was larger than "image_filter_buffer" size. + + +Changes with nginx 0.8.47 28 Jul 2010 + + *) Bugfix: $request_time variable had invalid values for subrequests. + + *) Bugfix: errors intercepted by error_page could not be cached. + + *) Bugfix: a cache manager process may got caught in an endless loop, + if max_size parameter was used; the bug had appeared in 0.8.46. + + +Changes with nginx 0.8.46 19 Jul 2010 + + *) Change: now the "proxy_no_cache", "fastcgi_no_cache", + "uwsgi_no_cache", and "scgi_no_cache" directives affect on a cached + response saving only. + + *) Feature: the "proxy_cache_bypass", "fastcgi_cache_bypass", + "uwsgi_cache_bypass", and "scgi_cache_bypass" directives. + + *) Bugfix: nginx did not free memory in cache keys zones if there was + an error during working with backend: the memory was freed only + after inactivity time or on memory low condition. + + +Changes with nginx 0.8.45 13 Jul 2010 + + *) Feature: ngx_http_xslt_filter improvements. + Thanks to Laurence Rowe. + + *) Bugfix: SSI response might be truncated after include with + wait="yes"; the bug had appeared in 0.7.25. + Thanks to Maxim Dounin. + + *) Bugfix: the "listen" directive did not support the "setfib=0" + parameter. + + +Changes with nginx 0.8.44 05 Jul 2010 + + *) Change: now nginx does not cache by default backend responses, if + they have a "Set-Cookie" header line. + + *) Feature: the "listen" directive supports the "setfib" parameter. + Thanks to Andrew Filonov. + + *) Bugfix: the "sub_filter" directive might change character case on + partial match. + + *) Bugfix: compatibility with HP/UX. + + *) Bugfix: compatibility with AIX xlC_r compiler. + + *) Bugfix: nginx treated large SSLv2 packets as plain requests. + Thanks to Miroslaw Jaworski. + + +Changes with nginx 0.8.43 30 Jun 2010 + + *) Feature: large geo ranges base loading speed-up. + + *) Bugfix: an error_page redirection to "location /zero {return 204;}" + without changing status code kept the error body; the bug had + appeared in 0.8.42. + + *) Bugfix: nginx might close IPv6 listen socket during + reconfiguration. + Thanks to Maxim Dounin. + + *) Bugfix: the $uid_set variable may be used at any request processing + stage. + + +Changes with nginx 0.8.42 21 Jun 2010 + + *) Change: now nginx tests locations given by regular expressions, if + request was matched exactly by a location given by a prefix string. + The previous behavior has been introduced in 0.7.1. + + *) Feature: the ngx_http_scgi_module. + Thanks to Manlio Perillo. + + *) Feature: a text answer may be added to a "return" directive. + + +Changes with nginx 0.8.41 15 Jun 2010 + + *) Security: nginx/Windows worker might be terminated abnormally if a + requested file name has invalid UTF-8 encoding. + + *) Change: now nginx allows to use spaces in a request line. + + *) Bugfix: the "proxy_redirect" directive changed incorrectly a backend + "Refresh" response header line. + Thanks to Andrey Andreew and Max Sogin. + + *) Bugfix: nginx did not support path without host name in + "Destination" request header line. + + +Changes with nginx 0.8.40 07 Jun 2010 + + *) Security: now nginx/Windows ignores default file stream name. + Thanks to Jose Antonio Vazquez Gonzalez. + + *) Feature: the ngx_http_uwsgi_module. + Thanks to Roberto De Ioris. + + *) Feature: a "fastcgi_param" directive with value starting with + "HTTP_" overrides a client request header line. + + *) Bugfix: the "If-Modified-Since", "If-Range", etc. client request + header lines were passed to FastCGI-server while caching. + + *) Bugfix: listen unix domain socket could not be changed during + reconfiguration. + Thanks to Maxim Dounin. + + +Changes with nginx 0.8.39 31 May 2010 + + *) Bugfix: an inherited "alias" directive worked incorrectly in + inclusive location. + + *) Bugfix: in "alias" with variables and "try_files" directives + combination. + + *) Bugfix: listen unix domain and IPv6 sockets did not inherit while + online upgrade. + Thanks to Maxim Dounin. + + +Changes with nginx 0.8.38 24 May 2010 + + *) Feature: the "proxy_no_cache" and "fastcgi_no_cache" directives. + + *) Feature: now the "rewrite" directive does a redirect automatically + if the $scheme variable is used. + Thanks to Piotr Sikora. + + *) Bugfix: now "limit_req" delay directive conforms to the described + algorithm. + Thanks to Maxim Dounin. + + *) Bugfix: the $uid_got variable might not be used in the SSI and perl + modules. + + +Changes with nginx 0.8.37 17 May 2010 + + *) Feature: the ngx_http_split_clients_module. + + *) Feature: the "map" directive supports keys more than 255 characters. + + *) Bugfix: nginx ignored the "private" and "no-store" values in the + "Cache-Control" backend response header line. + + *) Bugfix: a "stub" parameter of an "include" SSI directive was not + used, if empty response has 200 status code. + + *) Bugfix: if a proxied or FastCGI request was internally redirected to + another proxied or FastCGI location, then a segmentation fault might + occur in a worker process; the bug had appeared in 0.8.33. + Thanks to Yichun Zhang. + + *) Bugfix: IMAP connections may hang until they timed out while talking + to Zimbra server. + Thanks to Alan Batie. + + +Changes with nginx 0.8.36 22 Apr 2010 + + *) Bugfix: the ngx_http_dav_module handled incorrectly the DELETE, + COPY, and MOVE methods for symlinks. + + *) Bugfix: values of the $query_string, $arg_..., etc. variables cached + in main request were used by the SSI module in subrequests. + + *) Bugfix: a variable value was repeatedly encoded after each an "echo" + SSI-command output; the bug had appeared in 0.6.14. + + *) Bugfix: a worker process hung if a FIFO file was requested. + Thanks to Vicente Aguilar and Maxim Dounin. + + *) Bugfix: OpenSSL-1.0.0 compatibility on 64-bit Linux. + Thanks to Maxim Dounin. + + *) Bugfix: nginx could not be built --without-http-cache; the bug had + appeared in 0.8.35. + + +Changes with nginx 0.8.35 01 Apr 2010 + + *) Change: now the charset filter runs before the SSI filter. + + *) Feature: the "chunked_transfer_encoding" directive. + + *) Bugfix: an "&" character was not escaped when it was copied in + arguments part in a rewrite rule. + + *) Bugfix: nginx might be terminated abnormally while a signal + processing or if the directive "timer_resolution" was used on + platforms which do not support kqueue or eventport notification + methods. + Thanks to George Xie and Maxim Dounin. + + *) Bugfix: if temporary files and permanent storage area resided at + different file systems, then permanent file modification times were + incorrect. + Thanks to Maxim Dounin. + + *) Bugfix: ngx_http_memcached_module might issue the error message + "memcached sent invalid trailer". + Thanks to Maxim Dounin. + + *) Bugfix: nginx could not built zlib-1.2.4 library using the library + sources. + Thanks to Maxim Dounin. + + *) Bugfix: a segmentation fault occurred in a worker process, if there + was large stderr output before FastCGI response; the bug had + appeared in 0.8.34. + Thanks to Maxim Dounin. + + +Changes with nginx 0.8.34 03 Mar 2010 + + *) Bugfix: nginx did not support all ciphers and digests used in client + certificates. + Thanks to Innocenty Enikeew. + + *) Bugfix: nginx cached incorrectly FastCGI responses if there was + large stderr output before response. + + *) Bugfix: nginx did not support HTTPS referrers. + + *) Bugfix: nginx/Windows might not find file if path in configuration + was given in other character case; the bug had appeared in 0.8.33. + + *) Bugfix: the $date_local variable has an incorrect value, if the "%s" + format was used. + Thanks to Maxim Dounin. + + *) Bugfix: if ssl_session_cache was not set or was set to "none", then + during client certificate verify the error "session id context + uninitialized" might occur; the bug had appeared in 0.7.1. + + *) Bugfix: a geo range returned default value if the range included two + or more /16 networks and did not begin at /16 network boundary. + + *) Bugfix: a block used in a "stub" parameter of an "include" SSI + directive was output with "text/plain" MIME type. + + *) Bugfix: $r->sleep() did not work; the bug had appeared in 0.8.11. + + +Changes with nginx 0.8.33 01 Feb 2010 + + *) Security: now nginx/Windows ignores trailing spaces in URI. + Thanks to Dan Crowley, Core Security Technologies. + + *) Security: now nginx/Windows ignores short files names. + Thanks to Dan Crowley, Core Security Technologies. + + *) Change: now keepalive connections after POST requests are not + disabled for MSIE 7.0+. + Thanks to Adam Lounds. + + *) Workaround: now keepalive connections are disabled for Safari. + Thanks to Joshua Sierles. + + *) Bugfix: if a proxied or FastCGI request was internally redirected to + another proxied or FastCGI location, then $upstream_response_time + variable may have abnormally large value; the bug had appeared in + 0.8.7. + + *) Bugfix: a segmentation fault might occur in a worker process, while + discarding a request body; the bug had appeared in 0.8.11. + + +Changes with nginx 0.8.32 11 Jan 2010 + + *) Bugfix: UTF-8 encoding usage in the ngx_http_autoindex_module. + Thanks to Maxim Dounin. + + *) Bugfix: regular expression named captures worked for two names only. + Thanks to Maxim Dounin. + + *) Bugfix: now the "localhost" name is used in the "Host" request + header line, if an unix domain socket is defined in the "auth_http" + directive. + Thanks to Maxim Dounin. + + *) Bugfix: nginx did not support chunked transfer encoding for 201 + responses. + Thanks to Julian Reich. + + *) Bugfix: if the "expires modified" set date in the past, then a + negative number was set in the "Cache-Control" response header line. + Thanks to Alex Kapranoff. + + +Changes with nginx 0.8.31 23 Dec 2009 + + *) Feature: now the "error_page" directive may redirect the 301 and 302 + responses. + + *) Feature: the $geoip_city_continent_code, $geoip_latitude, and + $geoip_longitude variables. + Thanks to Arvind Sundararajan. + + *) Feature: now the ngx_http_image_filter_module deletes always EXIF + and other application specific data if the data consume more than 5% + of a JPEG file. + + *) Bugfix: nginx closed a connection if a cached response had an empty + body. + Thanks to Piotr Sikora. + + *) Bugfix: nginx might not be built by gcc 4.x if the -O2 or higher + optimization option was used. + Thanks to Maxim Dounin and Denis F. Latypoff. + + *) Bugfix: regular expressions in location were always tested in + case-sensitive mode; the bug had appeared in 0.8.25. + + *) Bugfix: nginx cached a 304 response if there was the "If-None-Match" + header line in a proxied request. + Thanks to Tim Dettrick and David Kostal. + + *) Bugfix: nginx/Windows tried to delete a temporary file twice if the + file should replace an already existent file. + + +Changes with nginx 0.8.30 15 Dec 2009 + + *) Change: now the default buffer size of the + "large_client_header_buffers" directive is 8K. + Thanks to Andrew Cholakian. + + *) Feature: the conf/fastcgi.conf for simple FastCGI configurations. + + *) Bugfix: nginx/Windows tried to rename a temporary file twice if the + file should replace an already existent file. + + *) Bugfix: of "double free or corruption" error issued if host could + not be resolved; the bug had appeared in 0.8.22. + Thanks to Konstantin Svist. + + *) Bugfix: in libatomic usage on some platforms. + Thanks to W-Mark Kubacki. + + +Changes with nginx 0.8.29 30 Nov 2009 + + *) Change: now the "009" status code is written to an access log for + proxied HTTP/0.9 responses. + + *) Feature: the "addition_types", "charset_types", "gzip_types", + "ssi_types", "sub_filter_types", and "xslt_types" directives support + an "*" parameter. + + *) Feature: GCC 4.1+ built-in atomic operations usage. + Thanks to W-Mark Kubacki. + + *) Feature: the --with-libatomic[=DIR] option in the configure. + Thanks to W-Mark Kubacki. + + *) Bugfix: listen unix domain socket had limited access rights. + + *) Bugfix: cached HTTP/0.9 responses were handled incorrectly. + + *) Bugfix: regular expression named captures given by "?P<...>" did not + work in a "server_name" directive. + Thanks to Maxim Dounin. + + +Changes with nginx 0.8.28 23 Nov 2009 + + *) Bugfix: nginx could not be built with the --without-pcre parameter; + the bug had appeared in 0.8.25. + + +Changes with nginx 0.8.27 17 Nov 2009 + + *) Bugfix: regular expressions did not work in nginx/Windows; the bug + had appeared in 0.8.25. + + +Changes with nginx 0.8.26 16 Nov 2009 + + *) Bugfix: in captures usage in "rewrite" directive; the bug had + appeared in 0.8.25. + + *) Bugfix: nginx could not be built without the --with-debug option; + the bug had appeared in 0.8.25. + + +Changes with nginx 0.8.25 16 Nov 2009 + + *) Change: now no message is written in an error log if a variable is + not found by $r->variable() method. + + *) Feature: the ngx_http_degradation_module. + + *) Feature: regular expression named captures. + + *) Feature: now URI part is not required a "proxy_pass" directive if + variables are used. + + *) Feature: now the "msie_padding" directive works for Chrome too. + + *) Bugfix: a segmentation fault occurred in a worker process on low + memory condition; the bug had appeared in 0.8.18. + + *) Bugfix: nginx sent gzipped responses to clients those do not support + gzip, if "gzip_static on" and "gzip_vary off"; the bug had appeared + in 0.8.16. + + +Changes with nginx 0.8.24 11 Nov 2009 + + *) Bugfix: nginx always added "Content-Encoding: gzip" response header + line in 304 responses sent by ngx_http_gzip_static_module. + + *) Bugfix: nginx could not be built without the --with-debug option; + the bug had appeared in 0.8.23. + + *) Bugfix: the "unix:" parameter of the "set_real_ip_from" directive + inherited incorrectly from previous level. + + *) Bugfix: in resolving empty name. + + +Changes with nginx 0.8.23 11 Nov 2009 + + *) Security: now SSL/TLS renegotiation is disabled. + Thanks to Maxim Dounin. + + *) Bugfix: listen unix domain socket did not inherit while online + upgrade. + + *) Bugfix: the "unix:" parameter of the "set_real_ip_from" directive + did not without yet another directive with any IP address. + + *) Bugfix: segmentation fault and infinite looping in resolver. + + *) Bugfix: in resolver. + Thanks to Artem Bokhan. + + +Changes with nginx 0.8.22 03 Nov 2009 + + *) Feature: the "proxy_bind", "fastcgi_bind", and "memcached_bind" + directives. + + *) Feature: the "access" and the "deny" directives support IPv6. + + *) Feature: the "set_real_ip_from" directive supports IPv6 addresses in + request headers. + + *) Feature: the "unix:" parameter of the "set_real_ip_from" directive. + + *) Bugfix: nginx did not delete unix domain socket after configuration + testing. + + *) Bugfix: nginx deleted unix domain socket while online upgrade. + + *) Bugfix: the "!-x" operator did not work. + Thanks to Maxim Dounin. + + *) Bugfix: a segmentation fault might occur in a worker process, if + limit_rate was used in HTTPS server. + Thanks to Maxim Dounin. + + *) Bugfix: a segmentation fault might occur in a worker process while + $limit_rate logging. + Thanks to Maxim Dounin. + + *) Bugfix: a segmentation fault might occur in a worker process, if + there was no "listen" directive in "server" block; the bug had + appeared in 0.8.21. + + +Changes with nginx 0.8.21 26 Oct 2009 + + *) Feature: now the "-V" switch shows TLS SNI support. + + *) Feature: the "listen" directive of the HTTP module supports unix + domain sockets. + Thanks to Hongli Lai. + + *) Feature: the "default_server" parameter of the "listen" directive. + + *) Feature: now a "default" parameter is not required to set listen + socket options. + + *) Bugfix: nginx did not support dates in 2038 year on 32-bit platforms; + + *) Bugfix: socket leak; the bug had appeared in 0.8.11. + + +Changes with nginx 0.8.20 14 Oct 2009 + + *) Change: now default SSL ciphers are "HIGH:!ADH:!MD5". + + *) Bugfix: the ngx_http_autoindex_module did not show the trailing + slash in links to a directory; the bug had appeared in 0.7.15. + + *) Bugfix: nginx did not close a log file set by the --error-log-path + configuration option; the bug had appeared in 0.7.53. + + *) Bugfix: nginx did not treat a comma as separator in the + "Cache-Control" backend response header line. + + *) Bugfix: nginx/Windows might not create temporary file, a cache file, + or "proxy/fastcgi_store"d file if a worker had no enough access + rights for top level directories. + + *) Bugfix: the "Set-Cookie" and "P3P" FastCGI response header lines + were not hidden while caching if no "fastcgi_hide_header" directives + were used with any parameters. + + *) Bugfix: nginx counted incorrectly disk cache size. + + +Changes with nginx 0.8.19 06 Oct 2009 + + *) Change: now SSLv2 protocol is disabled by default. + + *) Change: now default SSL ciphers are "ALL:!ADH:RC4+RSA:+HIGH:+MEDIUM". + + *) Bugfix: a "limit_req" directive did not work; the bug had appeared + in 0.8.18. + + +Changes with nginx 0.8.18 06 Oct 2009 + + *) Feature: the "read_ahead" directive. + + *) Feature: now several "perl_modules" directives may be used. + + *) Feature: the "limit_req_log_level" and "limit_conn_log_level" + directives. + + *) Bugfix: now "limit_req" directive conforms to the leaky bucket + algorithm. + Thanks to Maxim Dounin. + + *) Bugfix: nginx did not work on Linux/sparc. + Thanks to Marcus Ramberg. + + *) Bugfix: nginx sent '\0' in a "Location" response header line on + MKCOL request. + Thanks to Xie Zhenye. + + *) Bugfix: zero status code was logged instead of 499 status code; the + bug had appeared in 0.8.11. + + *) Bugfix: socket leak; the bug had appeared in 0.8.11. + + +Changes with nginx 0.8.17 28 Sep 2009 + + *) Security: now "/../" are disabled in "Destination" request header + line. + + *) Change: now $host variable value is always low case. + + *) Feature: the $ssl_session_id variable. + + *) Bugfix: socket leak; the bug had appeared in 0.8.11. + + +Changes with nginx 0.8.16 22 Sep 2009 + + *) Feature: the "image_filter_transparency" directive. + + *) Bugfix: "addition_types" directive was incorrectly named + "addtion_types". + + *) Bugfix: resolver cache poisoning. + Thanks to Matthew Dempsky. + + *) Bugfix: memory leak in resolver. + Thanks to Matthew Dempsky. + + *) Bugfix: invalid request line in $request variable was written in + access_log only if error_log was set to "info" or "debug" level. + + *) Bugfix: in PNG alpha-channel support in the + ngx_http_image_filter_module. + + *) Bugfix: nginx always added "Vary: Accept-Encoding" response header + line, if both "gzip_static" and "gzip_vary" were on. + + *) Bugfix: in UTF-8 encoding support by "try_files" directive in + nginx/Windows. + + *) Bugfix: in "post_action" directive usage; the bug had appeared in + 0.8.11. + Thanks to Igor Artemiev. + + +Changes with nginx 0.8.15 14 Sep 2009 + + *) Security: a segmentation fault might occur in worker process while + specially crafted request handling. + Thanks to Chris Ries. + + *) Bugfix: if names .domain.tld, .sub.domain.tld, and .domain-some.tld + were defined, then the name .sub.domain.tld was matched by + .domain.tld. + + *) Bugfix: in transparency support in the ngx_http_image_filter_module. + + *) Bugfix: in file AIO. + + *) Bugfix: in X-Accel-Redirect usage; the bug had appeared in 0.8.11. + + *) Bugfix: in embedded perl module; the bug had appeared in 0.8.11. + + +Changes with nginx 0.8.14 07 Sep 2009 + + *) Bugfix: an expired cached response might stick in the "UPDATING" + state. + + *) Bugfix: a segmentation fault might occur in worker process, if + error_log was set to info or debug level. + Thanks to Sergey Bochenkov. + + *) Bugfix: in embedded perl module; the bug had appeared in 0.8.11. + + *) Bugfix: an "error_page" directive did not redirect a 413 error; the + bug had appeared in 0.6.10. + + +Changes with nginx 0.8.13 31 Aug 2009 + + *) Bugfix: in the "aio sendfile" directive; the bug had appeared in + 0.8.12. + + *) Bugfix: nginx could not be built without the --with-file-aio option + on FreeBSD; the bug had appeared in 0.8.12. + + +Changes with nginx 0.8.12 31 Aug 2009 + + *) Feature: the "sendfile" parameter in the "aio" directive on FreeBSD. + + *) Bugfix: in try_files; the bug had appeared in 0.8.11. + + *) Bugfix: in memcached; the bug had appeared in 0.8.11. + + +Changes with nginx 0.8.11 28 Aug 2009 + + *) Change: now directive "gzip_disable msie6" does not disable gzipping + for MSIE 6.0 SV1. + + *) Feature: file AIO support on FreeBSD and Linux. + + *) Feature: the "directio_alignment" directive. + + +Changes with nginx 0.8.10 24 Aug 2009 + + *) Bugfix: memory leaks if GeoIP City database was used. + + *) Bugfix: in copying temporary files to permanent storage area; the + bug had appeared in 0.8.9. + + +Changes with nginx 0.8.9 17 Aug 2009 + + *) Feature: now the start cache loader runs in a separate process; this + should improve large caches handling. + + *) Feature: now temporary files and permanent storage area may reside + at different file systems. + + +Changes with nginx 0.8.8 10 Aug 2009 + + *) Bugfix: in handling FastCGI headers split in records. + + *) Bugfix: a segmentation fault occurred in worker process, if a + request was handled in two proxied or FastCGIed locations and a + caching was enabled in the first location; the bug had appeared in + 0.8.7. + + +Changes with nginx 0.8.7 27 Jul 2009 + + *) Change: minimum supported OpenSSL version is 0.9.7. + + *) Change: the "ask" parameter of the "ssl_verify_client" directive was + changed to the "optional" parameter and now it checks a client + certificate if it was offered. + Thanks to Brice Figureau. + + *) Feature: the $ssl_client_verify variable. + Thanks to Brice Figureau. + + *) Feature: the "ssl_crl" directive. + Thanks to Brice Figureau. + + *) Feature: the "proxy" parameter of the "geo" directive. + + *) Feature: the "image_filter" directive supports variables for setting + size. + + *) Bugfix: the $ssl_client_cert variable usage corrupted memory; the + bug had appeared in 0.7.7. + Thanks to Sergey Zhuravlev. + + *) Bugfix: "proxy_pass_header" and "fastcgi_pass_header" directives did + not pass to a client the "X-Accel-Redirect", "X-Accel-Limit-Rate", + "X-Accel-Buffering", and "X-Accel-Charset" lines from backend + response header. + Thanks to Maxim Dounin. + + *) Bugfix: in handling "Last-Modified" and "Accept-Ranges" backend + response header lines; the bug had appeared in 0.7.44. + Thanks to Maxim Dounin. + + *) Bugfix: the "[alert] zero size buf" error if subrequest returns an + empty response; the bug had appeared in 0.8.5. + + +Changes with nginx 0.8.6 20 Jul 2009 + + *) Feature: the ngx_http_geoip_module. + + *) Bugfix: XSLT filter may fail with message "not well formed XML + document" for valid XML document. + Thanks to Kuramoto Eiji. + + *) Bugfix: now in MacOSX, Cygwin, and nginx/Windows locations given by + a regular expression are always tested in case insensitive mode. + + *) Bugfix: now nginx/Windows ignores trailing dots in URI. + Thanks to Hugo Leisink. + + *) Bugfix: name of file specified in --conf-path was not honored during + installation; the bug had appeared in 0.6.6. + Thanks to Maxim Dounin. + + +Changes with nginx 0.8.5 13 Jul 2009 + + *) Bugfix: now nginx allows underscores in a request method. + + *) Bugfix: a 500 error code was returned for invalid login/password + while HTTP Basic authentication on Windows. + + *) Bugfix: ngx_http_perl_module responses did not work in subrequests. + + *) Bugfix: in ngx_http_limit_req_module. + Thanks to Maxim Dounin. + + +Changes with nginx 0.8.4 22 Jun 2009 + + *) Bugfix: nginx could not be built --without-http-cache; the bug had + appeared in 0.8.3. + + +Changes with nginx 0.8.3 19 Jun 2009 + + *) Feature: the $upstream_cache_status variable. + + *) Bugfix: nginx could not be built on MacOSX 10.6. + + *) Bugfix: nginx could not be built --without-http-cache; the bug had + appeared in 0.8.2. + + *) Bugfix: a segmentation fault occurred in worker process, if a + backend 401 error was intercepted and the backend did not set the + "WWW-Authenticate" response header line. + Thanks to Eugene Mychlo. + + +Changes with nginx 0.8.2 15 Jun 2009 + + *) Bugfix: in open_file_cache and proxy/fastcgi cache interaction on + start up. + + *) Bugfix: open_file_cache might cache open file descriptors too long; + the bug had appeared in 0.7.4. + + +Changes with nginx 0.8.1 08 Jun 2009 + + *) Feature: the "updating" parameter in "proxy_cache_use_stale" and + "fastcgi_cache_use_stale" directives. + + *) Bugfix: the "If-Modified-Since", "If-Range", etc. client request + header lines were passed to backend while caching if no + "proxy_set_header" directive was used with any parameters. + + *) Bugfix: the "Set-Cookie" and "P3P" response header lines were not + hidden while caching if no "proxy_hide_header/fastcgi_hide_header" + directives were used with any parameters. + + *) Bugfix: the ngx_http_image_filter_module did not support GIF87a + format. + Thanks to Denis Ilyinyh. + + *) Bugfix: nginx could not be built modules on Solaris 10 and early; + the bug had appeared in 0.7.56. + + +Changes with nginx 0.8.0 02 Jun 2009 + + *) Feature: the "keepalive_requests" directive. + + *) Feature: the "limit_rate_after" directive. + Thanks to Ivan Debnar. + + *) Bugfix: XLST filter did not work in subrequests. + + *) Bugfix: in relative paths handling in nginx/Windows. + + *) Bugfix: in proxy_store, fastcgi_store, proxy_cache, and + fastcgi_cache in nginx/Windows. + + *) Bugfix: in memory allocation error handling. + Thanks to Maxim Dounin and Kirill A. Korinskiy. + + +Changes with nginx 0.7.59 25 May 2009 + + *) Feature: the "proxy_cache_methods" and "fastcgi_cache_methods" + directives. + + *) Bugfix: socket leak; the bug had appeared in 0.7.25. + Thanks to Maxim Dounin. + + *) Bugfix: a segmentation fault occurred in worker process, + if a request had no body and the $request_body variable was used; + the bug had appeared in 0.7.58. + + *) Bugfix: the SSL modules might not built on Solaris and Linux; + the bug had appeared in 0.7.56. + + *) Bugfix: ngx_http_xslt_filter_module responses were not handled by + SSI, charset, and gzip filters. + + *) Bugfix: a "charset" directive did not set a charset to + ngx_http_gzip_static_module responses. + + +Changes with nginx 0.7.58 18 May 2009 + + *) Feature: a "listen" directive of the mail proxy module supports IPv6. + + *) Feature: the "image_filter_jpeg_quality" directive. + + *) Feature: the "client_body_in_single_buffer" directive. + + *) Feature: the $request_body variable. + + *) Bugfix: in ngx_http_autoindex_module in file name links having a ":" + symbol in the name. + + *) Bugfix: "make upgrade" procedure did not work; the bug had appeared + in 0.7.53. + Thanks to Denis F. Latypoff. + + +Changes with nginx 0.7.57 12 May 2009 + + *) Bugfix: a floating-point fault occurred in worker process, if the + ngx_http_image_filter_module errors were redirected to named + location; the bug had appeared in 0.7.56. + + +Changes with nginx 0.7.56 11 May 2009 + + *) Feature: nginx/Windows supports IPv6 in a "listen" directive of the + HTTP module. + + *) Bugfix: in ngx_http_image_filter_module. + + +Changes with nginx 0.7.55 06 May 2009 + + *) Bugfix: the http_XXX parameters in "proxy_cache_use_stale" and + "fastcgi_cache_use_stale" directives did not work. + + *) Bugfix: fastcgi cache did not cache header only responses. + + *) Bugfix: of "select() failed (9: Bad file descriptor)" error in + nginx/Unix and "select() failed (10038: ...)" error in nginx/Windows. + + *) Bugfix: a segmentation fault might occur in worker process, if an + "debug_connection" directive was used; the bug had appeared in + 0.7.54. + + *) Bugfix: fix ngx_http_image_filter_module building errors. + + *) Bugfix: the files bigger than 2G could not be transferred using + $r->sendfile. + Thanks to Maxim Dounin. + + +Changes with nginx 0.7.54 01 May 2009 + + *) Feature: the ngx_http_image_filter_module. + + *) Feature: the "proxy_ignore_headers" and "fastcgi_ignore_headers" + directives. + + *) Bugfix: a segmentation fault might occur in worker process, if an + "open_file_cache_errors off" directive was used; the bug had + appeared in 0.7.53. + + *) Bugfix: the "port_in_redirect off" directive did not work; the bug + had appeared in 0.7.39. + + *) Bugfix: improve handling of "select" method errors. + + *) Bugfix: of "select() failed (10022: ...)" error in nginx/Windows. + + *) Bugfix: in error text descriptions in nginx/Windows; the bug had + appeared in 0.7.53. + + +Changes with nginx 0.7.53 27 Apr 2009 + + *) Change: now a log set by --error-log-path is created from the very + start-up. + + *) Feature: now the start up errors and warnings are outputted to an + error_log and stderr. + + *) Feature: the empty --prefix= configure parameter forces nginx to use + a directory where it was run as prefix. + + *) Feature: the -p switch. + + *) Feature: the -s switch on Unix platforms. + + *) Feature: the -? and -h switches. + Thanks to Jerome Loyet. + + *) Feature: now switches may be set in condensed form. + + *) Bugfix: nginx/Windows did not work if configuration file was given + by the -c switch. + + *) Bugfix: temporary files might be not removed if the "proxy_store", + "fastcgi_store", "proxy_cache", or "fastcgi_cache" were used. + Thanks to Maxim Dounin. + + *) Bugfix: an incorrect value was passed to mail proxy authentication + server in "Auth-Method" header line; the bug had appeared + in 0.7.34. + Thanks to Simon Lecaille. + + *) Bugfix: system error text descriptions were not logged on Linux; + the bug had appeared in 0.7.45. + + *) Bugfix: the "fastcgi_cache_min_uses" directive did not work. + Thanks to Andrew Vorobyoff. + + +Changes with nginx 0.7.52 20 Apr 2009 + + *) Feature: the first native Windows binary release. + + *) Bugfix: in processing HEAD method while caching. + + *) Bugfix: in processing the "If-Modified-Since", "If-Range", etc. + client request header lines while caching. + + *) Bugfix: now the "Set-Cookie" and "P3P" header lines are hidden in + cacheable responses. + + *) Bugfix: if nginx was built with the ngx_http_perl_module and with a + perl which supports threads, then during a master process exit the + message "panic: MUTEX_LOCK" might be issued. + + *) Bugfix: nginx could not be built --without-http-cache; the bug had + appeared in 0.7.48. + + *) Bugfix: nginx could not be built on platforms different from i386, + amd64, sparc, and ppc; the bug had appeared in 0.7.42. + + +Changes with nginx 0.7.51 12 Apr 2009 + + *) Feature: the "try_files" directive supports a response code in the + fallback parameter. + + *) Feature: now any response code can be used in the "return" directive. + + *) Bugfix: the "error_page" directive made an external redirect without + query string; the bug had appeared in 0.7.44. + + *) Bugfix: if servers listened on several defined explicitly addresses, + then virtual servers might not work; the bug had appeared in 0.7.39. + + +Changes with nginx 0.7.50 06 Apr 2009 + + *) Bugfix: the $arg_... variables did not work; the bug had appeared in + 0.7.49. + + +Changes with nginx 0.7.49 06 Apr 2009 + + *) Bugfix: a segmentation fault might occur in worker process, if the + $arg_... variables were used; the bug had appeared in 0.7.48. + + +Changes with nginx 0.7.48 06 Apr 2009 + + *) Feature: the "proxy_cache_key" directive. + + *) Bugfix: now nginx takes into account the "X-Accel-Expires", + "Expires", and "Cache-Control" header lines in a backend response. + + *) Bugfix: now nginx caches responses for the GET requests only. + + *) Bugfix: the "fastcgi_cache_key" directive was not inherited. + + *) Bugfix: the $arg_... variables did not work with SSI subrequests. + Thanks to Maxim Dounin. + + *) Bugfix: nginx could not be built with uclibc library. + Thanks to Timothy Redaelli. + + *) Bugfix: nginx could not be built on OpenBSD; the bug had + appeared in 0.7.46. + + +Changes with nginx 0.7.47 01 Apr 2009 + + *) Bugfix: nginx could not be built on FreeBSD 6 and early versions; + the bug had appeared in 0.7.46. + + *) Bugfix: nginx could not be built on MacOSX; the bug had + appeared in 0.7.46. + + *) Bugfix: if the "max_size" parameter was set, then the cache manager + might purge a whole cache; the bug had appeared in 0.7.46. + + *) Change: a segmentation fault might occur in worker process, if the + "proxy_cache"/"fastcgi_cache" and the "proxy_cache_valid"/ + "fastcgi_cache_valid" were set on different levels; the bug had + appeared in 0.7.46. + + *) Bugfix: a segmentation fault might occur in worker process, if a + request was redirected to a proxied or FastCGI server via error_page + or try_files; the bug had appeared in 0.7.44. + + +Changes with nginx 0.7.46 30 Mar 2009 + + *) Bugfix: the previous release tarball was incorrect. + + +Changes with nginx 0.7.45 30 Mar 2009 + + *) Change: now the "proxy_cache" and the "proxy_cache_valid" directives + can be set on different levels. + + *) Change: the "clean_time" parameter of the "proxy_cache_path" + directive is canceled. + + *) Feature: the "max_size" parameter of the "proxy_cache_path" + directive. + + *) Feature: the ngx_http_fastcgi_module preliminary cache support. + + *) Feature: now on shared memory allocation errors directive and zone + names are logged. + + *) Bugfix: the directive "add_header last-modified ''" did not delete a + "Last-Modified" response header line; the bug had appeared in 0.7.44. + + *) Bugfix: a relative path in the "auth_basic_user_file" directive + given without variables did not work; the bug had appeared in + 0.7.44. + Thanks to Jerome Loyet. + + *) Bugfix: in an "alias" directive given using variables without + references to captures of regular expressions; the bug had appeared + in 0.7.42. + + +Changes with nginx 0.7.44 23 Mar 2009 + + *) Feature: the ngx_http_proxy_module preliminary cache support. + + *) Feature: the --with-pcre option in the configure. + + *) Feature: the "try_files" directive is now allowed on the server + block level. + + *) Bugfix: the "try_files" directive handled incorrectly a query string + in a fallback parameter. + + *) Bugfix: the "try_files" directive might test incorrectly directories. + + *) Bugfix: if there is the single server for given address:port pair, + then captures in regular expressions in a "server_name" directive + did not work. + + +Changes with nginx 0.7.43 18 Mar 2009 + + *) Bugfix: a request was handled incorrectly, if a "root" directive + used variables; the bug had appeared in 0.7.42. + + *) Bugfix: if a server listened on wildcard address, then the + $server_addr variable value was "0.0.0.0"; the bug had appeared in + 0.7.36. + + +Changes with nginx 0.7.42 16 Mar 2009 + + *) Change: now the "Invalid argument" error returned by + setsockopt(TCP_NODELAY) on Solaris, is ignored. + + *) Change: now if a file specified in a "auth_basic_user_file" + directive is absent, then the 403 error is returned instead of the + 500 one. + + *) Feature: the "auth_basic_user_file" directive supports variables. + Thanks to Kirill A. Korinskiy. + + *) Feature: the "listen" directive supports the "ipv6only" parameter. + Thanks to Zhang Hua. + + *) Bugfix: in an "alias" directive with references to captures of + regular expressions; the bug had appeared in 0.7.40. + + *) Bugfix: compatibility with Tru64 UNIX. + Thanks to Dustin Marquess. + + *) Bugfix: nginx could not be built without PCRE library; the bug had + appeared in 0.7.41. + + +Changes with nginx 0.7.41 11 Mar 2009 + + *) Bugfix: a segmentation fault might occur in worker process, if a + "server_name" or a "location" directives had captures in regular + expressions; the issue had appeared in 0.7.40. + Thanks to Vladimir Sopot. + + +Changes with nginx 0.7.40 09 Mar 2009 + + *) Feature: the "location" directive supports captures in regular + expressions. + + *) Feature: an "alias" directive with capture references may be used + inside a location given by a regular expression with captures. + + *) Feature: the "server_name" directive supports captures in regular + expressions. + + *) Workaround: the ngx_http_autoindex_module did not show the trailing + slash in directories on XFS filesystem; the issue had appeared in + 0.7.15. + Thanks to Dmitry Kuzmenko. + + +Changes with nginx 0.7.39 02 Mar 2009 + + *) Bugfix: large response with SSI might hang, if gzipping was enabled; + the bug had appeared in 0.7.28. + Thanks to Artem Bokhan. + + *) Bugfix: a segmentation fault might occur in worker process, if short + static variants are used in a "try_files" directive. + + +Changes with nginx 0.7.38 23 Feb 2009 + + *) Feature: authentication failures logging. + + *) Bugfix: name/password in auth_basic_user_file were ignored after odd + number of empty lines. + Thanks to Alexander Zagrebin. + + *) Bugfix: a segmentation fault occurred in a master process, if long + path was used in unix domain socket; the bug had appeared in 0.7.36. + + +Changes with nginx 0.7.37 21 Feb 2009 + + *) Bugfix: directives using upstreams did not work; the bug had + appeared in 0.7.36. + + +Changes with nginx 0.7.36 21 Feb 2009 + + *) Feature: a preliminary IPv6 support; the "listen" directive of the + HTTP module supports IPv6. + + *) Bugfix: the $ancient_browser variable did not work for browsers + preset by a "modern_browser" directives. + + +Changes with nginx 0.7.35 16 Feb 2009 + + *) Bugfix: a "ssl_engine" directive did not use a SSL-accelerator for + asymmetric ciphers. + Thanks to Marcin Gozdalik. + + *) Bugfix: a "try_files" directive set MIME type depending on an + original request extension. + + *) Bugfix: "*domain.tld" names were handled incorrectly in + "server_name", "valid_referers", and "map" directives, if + ".domain.tld" and ".subdomain.domain.tld" wildcards were used; + the bug had appeared in 0.7.9. + + +Changes with nginx 0.7.34 10 Feb 2009 + + *) Feature: the "off" parameter of the "if_modified_since" directive. + + *) Feature: now nginx sends an HELO/EHLO command after a XCLIENT + command. + Thanks to Maxim Dounin. + + *) Feature: Microsoft specific "AUTH LOGIN with User Name" mode support + in mail proxy server. + Thanks to Maxim Dounin. + + *) Bugfix: in a redirect rewrite directive original arguments were + concatenated with new arguments by a "?" rather than an "&"; + the bug had appeared in 0.1.18. + Thanks to Maxim Dounin. + + *) Bugfix: nginx could not be built on AIX. + + +Changes with nginx 0.7.33 02 Feb 2009 + + *) Bugfix: a double response might be returned if the epoll or rtsig + methods are used and a redirect was returned to a request with + body. + Thanks to Eden Li. + + *) Bugfix: the $sent_http_location variable was empty for some + redirects types. + + *) Bugfix: a segmentation fault might occur in worker process if + "resolver" directive was used in SMTP proxy. + + +Changes with nginx 0.7.32 26 Jan 2009 + + *) Feature: now a directory existence testing can be set explicitly in + the "try_files" directive. + + *) Bugfix: fastcgi_store stored files not always. + + *) Bugfix: in geo ranges. + + *) Bugfix: in shared memory allocations if nginx was built without + debugging. + Thanks to Andrey Kvasov. + + +Changes with nginx 0.7.31 19 Jan 2009 + + *) Change: now the "try_files" directive tests files only and ignores + directories. + + *) Feature: the "fastcgi_split_path_info" directive. + + *) Bugfixes in an "Expect" request header line support. + + *) Bugfixes in geo ranges. + + *) Bugfix: in a miss case ngx_http_memcached_module returned the "END" + line as response body instead of default 404 page body; the bug had + appeared in 0.7.18. + Thanks to Maxim Dounin. + + *) Bugfix: while SMTP proxying nginx issued message "250 2.0.0 OK" + instead of "235 2.0.0 OK"; the bug had appeared in 0.7.22. + Thanks to Maxim Dounin. + + +Changes with nginx 0.7.30 24 Dec 2008 + + *) Bugfix: a segmentation fault occurred in worker process, if + variables were used in the "fastcgi_pass" or "proxy_pass" directives + and host name must be resolved; the bug had appeared in 0.7.29. + + +Changes with nginx 0.7.29 24 Dec 2008 + + *) Bugfix: the "fastcgi_pass" and "proxy_pass" directives did not + support variables if unix domain sockets were used. + + *) Bugfixes in subrequest processing; the bugs had appeared in 0.7.25. + + *) Bugfix: a "100 Continue" response was issued for HTTP/1.0 + requests; + Thanks to Maxim Dounin. + + *) Bugfix: in memory allocation in the ngx_http_gzip_filter_module on + Cygwin. + + +Changes with nginx 0.7.28 22 Dec 2008 + + *) Change: in memory allocation in the ngx_http_gzip_filter_module. + + *) Change: the default "gzip_buffers" directive values have been + changed to 32 4k or 16 8k from 4 4k/8k. + + +Changes with nginx 0.7.27 15 Dec 2008 + + *) Feature: the "try_files" directive. + + *) Feature: variables support in the "fastcgi_pass" directive. + + *) Feature: now the $geo variable may get an address from a + variable. + Thanks to Andrei Nigmatulin. + + *) Feature: now a location's modifier may be used without space before + name. + + *) Feature: the $upstream_response_length variable. + + *) Bugfix: now a "add_header" directive does not add an empty value. + + *) Bugfix: if zero length static file was requested, then nginx just + closed connection; the bug had appeared in 0.7.25. + + *) Bugfix: a MOVE method could not move file in non-existent directory. + + *) Bugfix: a segmentation fault occurred in worker process, if no one + named location was defined in server, but some one was used in an + error_page directive. + Thanks to Sergey Bochenkov. + + +Changes with nginx 0.7.26 08 Dec 2008 + + *) Bugfix: in subrequest processing; the bug had appeared in 0.7.25. + + +Changes with nginx 0.7.25 08 Dec 2008 + + *) Change: in subrequest processing. + + *) Change: now POSTs without "Content-Length" header line are allowed. + + *) Bugfix: now the "limit_req" and "limit_conn" directives log a + prohibition reason. + + *) Bugfix: in the "delete" parameter of the "geo" directive. + + +Changes with nginx 0.7.24 01 Dec 2008 + + *) Feature: the "if_modified_since" directive. + + *) Bugfix: nginx did not process a FastCGI server response, if the + server send too many messages to stderr before response. + + *) Bugfix: the "$cookie_..." variables did not work in the SSI and the + perl module. + + +Changes with nginx 0.7.23 27 Nov 2008 + + *) Feature: the "delete" and "ranges" parameters in the "geo" directive. + + *) Feature: speeding up loading of geo base with large number of values. + + *) Feature: decrease of memory required for geo base load. + + +Changes with nginx 0.7.22 20 Nov 2008 + + *) Feature: the "none" parameter in the "smtp_auth" directive. + Thanks to Maxim Dounin. + + *) Feature: the "$cookie_..." variables. + + *) Bugfix: the "directio" directive did not work in XFS filesystem. + + *) Bugfix: the resolver did not understand big DNS responses. + Thanks to Zyb. + + +Changes with nginx 0.7.21 11 Nov 2008 + + *) Changes in the ngx_http_limit_req_module. + + *) Feature: the EXSLT support in the ngx_http_xslt_module. + Thanks to Denis F. Latypoff. + + *) Workaround: compatibility with glibc 2.3. + Thanks to Eric Benson and Maxim Dounin. + + *) Bugfix: nginx could not run on MacOSX 10.4 and earlier; the bug had + appeared in 0.7.6. + + +Changes with nginx 0.7.20 10 Nov 2008 + + *) Changes in the ngx_http_gzip_filter_module. + + *) Feature: the ngx_http_limit_req_module. + + *) Bugfix: worker processes might exit on a SIGBUS signal on sparc and + ppc platforms; the bug had appeared in 0.7.3. + Thanks to Maxim Dounin. + + *) Bugfix: the "proxy_pass http://host/some:uri" directives did not + work; the bug had appeared in 0.7.12. + + *) Bugfix: in HTTPS mode requests might fail with the "bad write retry" + error. + + *) Bugfix: the ngx_http_secure_link_module did not work inside + locations, whose names are less than 3 characters. + + *) Bugfix: $server_addr variable might have no value. + + +Changes with nginx 0.7.19 13 Oct 2008 + + *) Bugfix: version number update. + + +Changes with nginx 0.7.18 13 Oct 2008 + + *) Change: the "underscores_in_headers" directive; now nginx does not + allows underscores in a client request header line names. + + *) Feature: the ngx_http_secure_link_module. + + *) Feature: the "real_ip_header" directive supports any header. + + *) Feature: the "log_subrequest" directive. + + *) Feature: the $realpath_root variable. + + *) Feature: the "http_502" and "http_504" parameters of the + "proxy_next_upstream" directive. + + *) Bugfix: the "http_503" parameter of the "proxy_next_upstream" or + "fastcgi_next_upstream" directives did not work. + + *) Bugfix: nginx might send a "Transfer-Encoding: chunked" heaer line + for HEAD requests. + + *) Bugfix: now accept threshold depends on worker_connections. + + +Changes with nginx 0.7.17 15 Sep 2008 + + *) Feature: now the "directio" directive works on Linux. + + *) Feature: the $pid variable. + + *) Bugfix: the "directio" optimization that had appeared in 0.7.15 did + not work with open_file_cache. + + *) Bugfix: the "access_log" with variables did not work on Linux; the + bug had appeared in 0.7.7. + + *) Bugfix: the ngx_http_charset_module did not understand quoted + charset name received from backend. + + +Changes with nginx 0.7.16 08 Sep 2008 + + *) Bugfix: nginx could not be built on 64-bit platforms; the bug had + appeared in 0.7.15. + + +Changes with nginx 0.7.15 08 Sep 2008 + + *) Feature: the ngx_http_random_index_module. + + *) Feature: the "directio" directive has been optimized for file + requests starting from arbitrary position. + + *) Feature: the "directio" directive turns off sendfile if it is + necessary. + + *) Feature: now nginx allows underscores in a client request header + line names. + + +Changes with nginx 0.7.14 01 Sep 2008 + + *) Change: now the ssl_certificate and ssl_certificate_key directives + have not default values. + + *) Feature: the "listen" directive supports the "ssl" parameter. + + *) Feature: now nginx takes into account a time zone change while + reconfiguration on FreeBSD and Linux. + + *) Bugfix: the "listen" directive parameters such as "backlog", + "rcvbuf", etc. were not set, if a default server was not the first + one. + + *) Bugfix: if URI part captured by a "rewrite" directive was used as a + query string, then the query string was not escaped. + + *) Bugfix: configuration file validity test improvements. + + +Changes with nginx 0.7.13 26 Aug 2008 + + *) Bugfix: nginx could not be built on Linux and Solaris; the bug had + appeared in 0.7.12. + + +Changes with nginx 0.7.12 26 Aug 2008 + + *) Feature: the "server_name" directive supports empty name "". + + *) Feature: the "gzip_disable" directive supports special "msie6" mask. + + *) Bugfix: if the "max_fails=0" parameter was used in upstream with + several servers, then a worker process exited on a SIGFPE signal. + Thanks to Maxim Dounin. + + *) Bugfix: a request body was dropped while redirection via an + "error_page" directive. + + *) Bugfix: a full response was returned for request method HEAD while + redirection via an "error_page" directive. + + *) Bugfix: the $r->header_in() method did not return value of the + "Host", "User-Agent", and "Connection" request header lines; the bug + had appeared in 0.7.0. + + +Changes with nginx 0.7.11 18 Aug 2008 + + *) Change: now ngx_http_charset_module does not work by default with + text/css MIME type. + + *) Feature: now nginx returns the 405 status code for POST method + requesting a static file only if the file exists. + + *) Feature: the "proxy_ssl_session_reuse" directive. + + *) Bugfix: a "proxy_pass" directive without URI part might use original + request after the "X-Accel-Redirect" redirection was used; + + *) Bugfix: if a directory has search only rights and the first index + file was absent, then nginx returned the 500 status code. + + *) Bugfix: in inclusive locations; the bugs had appeared in 0.7.1. + + +Changes with nginx 0.7.10 13 Aug 2008 + + *) Bugfix: in the "addition_types", "charset_types", "gzip_types", + "ssi_types", "sub_filter_types", and "xslt_types" directives; the + bugs had appeared in 0.7.9. + + *) Bugfix: of recursive error_page for 500 status code. + + *) Bugfix: now the ngx_http_realip_module sets address not for whole + keepalive connection, but for each request passed via the connection. + + +Changes with nginx 0.7.9 12 Aug 2008 + + *) Change: now ngx_http_charset_module works by default with following + MIME types: text/html, text/css, text/xml, text/plain, + text/vnd.wap.wml, application/x-javascript, and application/rss+xml. + + *) Feature: the "charset_types" and "addition_types" directives. + + *) Feature: now the "gzip_types", "ssi_types", and "sub_filter_types" + directives use hash. + + *) Feature: the ngx_cpp_test_module. + + *) Feature: the "expires" directive supports daily time. + + *) Feature: the ngx_http_xslt_module improvements and bug fixing. + Thanks to Denis F. Latypoff and Maxim Dounin. + + *) Bugfix: the "log_not_found" directive did not work for index files + tests. + + *) Bugfix: HTTPS connections might hang, if kqueue, epoll, rtsig, or + eventport methods were used; the bug had appeared in 0.7.7. + + *) Bugfix: if the "server_name", "valid_referers", and "map" directives + used an "*.domain.tld" wildcard and exact name "domain.tld" was not + set, then the exact name was matched by the wildcard; the bug had + appeared in 0.3.18. + + +Changes with nginx 0.7.8 04 Aug 2008 + + *) Feature: the ngx_http_xslt_module. + + *) Feature: the "$arg_..." variables. + + *) Feature: Solaris directio support. + Thanks to Ivan Debnar. + + *) Bugfix: now if FastCGI server sends a "Location" header line without + status line, then nginx uses 302 status code. + Thanks to Maxim Dounin. + + +Changes with nginx 0.7.7 30 Jul 2008 + + *) Change: now the EAGAIN error returned by connect() is not considered + as temporary error. + + *) Change: now the $ssl_client_cert variable value is a certificate + with TAB character intended before each line except first one; an + unchanged certificate is available in the $ssl_client_raw_cert + variable. + + *) Feature: the "ask" parameter in the "ssl_verify_client" directive. + + *) Feature: byte-range processing improvements. + Thanks to Maxim Dounin. + + *) Feature: the "directio" directive. + Thanks to Jiang Hong. + + *) Feature: MacOSX 10.5 sendfile() support. + + *) Bugfix: now in MacOSX and Cygwin locations are tested in case + insensitive mode; however, the compare is provided by single-byte + locales only. + + *) Bugfix: mail proxy SSL connections hanged, if select, poll, or + /dev/poll methods were used. + + *) Bugfix: UTF-8 encoding usage in the ngx_http_autoindex_module. + + +Changes with nginx 0.7.6 07 Jul 2008 + + *) Bugfix: now if variables are used in the "access_log" directive a + request root existence is always tested. + + *) Bugfix: the ngx_http_flv_module did not support several values in a + query string. + + +Changes with nginx 0.7.5 01 Jul 2008 + + *) Bugfixes in variables support in the "access_log" directive; the + bugs had appeared in 0.7.4. + + *) Bugfix: nginx could not be built --without-http_gzip_module; the bug + had appeared in 0.7.3. + Thanks to Kirill A. Korinskiy. + + *) Bugfix: if sub_filter and SSI were used together, then responses + might were transferred incorrectly. + + +Changes with nginx 0.7.4 30 Jun 2008 + + *) Feature: variables support in the "access_log" directive. + + *) Feature: the "open_log_file_cache" directive. + + *) Feature: the -g switch. + + *) Feature: the "Expect" request header line support. + + *) Bugfix: large SSI inclusions might be truncated. + + +Changes with nginx 0.7.3 23 Jun 2008 + + *) Change: the "rss" extension MIME type has been changed to + "application/rss+xml". + + *) Change: now the "gzip_vary" directive turned on issues a + "Vary: Accept-Encoding" header line for uncompressed responses too. + + *) Feature: now the "rewrite" directive does a redirect automatically + if the "https://" protocol is used. + + *) Bugfix: the "proxy_pass" directive did not work with the HTTPS + protocol; the bug had appeared in 0.6.9. + + +Changes with nginx 0.7.2 16 Jun 2008 + + *) Feature: now nginx supports EDH key exchange ciphers. + + *) Feature: the "ssl_dhparam" directive. + + *) Feature: the $ssl_client_cert variable. + Thanks to Manlio Perillo. + + *) Bugfix: after changing URI via a "rewrite" directive nginx did not + search a new location; the bug had appeared in 0.7.1. + Thanks to Maxim Dounin. + + *) Bugfix: nginx could not be built without PCRE library; the bug had + appeared in 0.7.1. + + *) Bugfix: when a request to a directory was redirected with the slash + added, nginx dropped a query string from the original request. + + +Changes with nginx 0.7.1 26 May 2008 + + *) Change: now locations are searched in a tree. + + *) Change: the "optimize_server_names" directive was canceled due to + the "server_name_in_redirect" directive introduction. + + *) Change: some long deprecated directives are not supported anymore. + + *) Change: the "none" parameter in the "ssl_session_cache" directive; + now this is default parameter. + Thanks to Rob Mueller. + + *) Bugfix: worker processes might not catch reconfiguration and log + rotation signals. + + *) Bugfix: nginx could not be built on latest Fedora 9 Linux. + Thanks to Roxis. + + +Changes with nginx 0.7.0 19 May 2008 + + *) Change: now the 0x00-0x1F, '"' and '\' characters are escaped as + \xXX in an access_log. + Thanks to Maxim Dounin. + + *) Change: now nginx allows several "Host" request header line. + + *) Feature: the "modified" flag in the "expires" directive. + + *) Feature: the $uid_got and $uid_set variables may be used at any + request processing stage. + + *) Feature: the $hostname variable. + Thanks to Andrei Nigmatulin. + + *) Feature: DESTDIR support. + Thanks to Todd A. Fisher and Andras Voroskoi. + + *) Bugfix: a segmentation fault might occur in worker process on Linux, + if keepalive was enabled. + + +Changes with nginx 0.6.31 12 May 2008 + + *) Bugfix: nginx did not process FastCGI response if header was at the + end of FastCGI record; the bug had appeared in 0.6.2. + Thanks to Sergey Serov. + + *) Bugfix: a segmentation fault might occur in worker process if a file + was deleted and the "open_file_cache_errors" directive was off. + + +Changes with nginx 0.6.30 29 Apr 2008 + + *) Change: now if an "include" directive pattern does not match any + file, then nginx does not issue an error. + + *) Feature: now the time in directives may be specified without spaces, + for example, "1h50m". + + *) Bugfix: memory leaks if the "ssl_verify_client" directive was on. + Thanks to Chavelle Vincent. + + *) Bugfix: the "sub_filter" directive might set text to change into + output. + + *) Bugfix: the "error_page" directive did not take into account + arguments in redirected URI. + + *) Bugfix: now nginx always opens files in binary mode under Cygwin. + + *) Bugfix: nginx could not be built on OpenBSD; the bug had appeared in + 0.6.15. + + +Changes with nginx 0.6.29 18 Mar 2008 + + *) Feature: the ngx_google_perftools_module. + + *) Bugfix: the ngx_http_perl_module could not be built on 64-bit + platforms; the bug had appeared in 0.6.27. + + +Changes with nginx 0.6.28 13 Mar 2008 + + *) Bugfix: the rtsig method could not be built; the bug had appeared in + 0.6.27. + + +Changes with nginx 0.6.27 12 Mar 2008 + + *) Change: now by default the rtsig method is not built on + Linux 2.6.18+. + + *) Change: now a request method is not changed while redirection to a + named location via an "error_page" directive. + + *) Feature: the "resolver" and "resolver_timeout" directives in SMTP + proxy. + + *) Feature: the "post_action" directive supports named locations. + + *) Bugfix: a segmentation fault occurred in worker process, if a + request was redirected from proxy, FastCGI, or memcached location to + static named locations. + + *) Bugfix: browsers did not repeat SSL handshake if there is no valid + client certificate in first handshake. + Thanks to Alexander V. Inyukhin. + + *) Bugfix: if response code 495-497 was redirected via an "error_page" + directive without code change, then nginx tried to allocate too many + memory. + + *) Bugfix: memory leak in long-lived non buffered connections. + + *) Bugfix: memory leak in resolver. + + *) Bugfix: a segmentation fault occurred in worker process, if a + request was redirected from proxy, FastCGI, or memcached location to + static named locations. + + *) Bugfix: in the $proxy_host and $proxy_port variables caching. + Thanks to Sergey Bochenkov. + + *) Bugfix: a "proxy_pass" directive with variables used incorrectly the + same port as in another "proxy_pass" directive with the same host + name and without variables. + Thanks to Sergey Bochenkov. + + *) Bugfix: an alert "sendmsg() failed (9: Bad file descriptor)" on some + 64-bit platforms while reconfiguration. + + *) Bugfix: a segmentation fault occurred in worker process, if empty + stub block was used second time in SSI. + + *) Bugfix: in copying URI part contained escaped symbols into arguments. + + +Changes with nginx 0.6.26 11 Feb 2008 + + *) Bugfix: the "proxy_store" and "fastcgi_store" directives did not + check a response length. + + *) Bugfix: a segmentation fault occurred in worker process, if big + value was used in a "expires" directive. + Thanks to Joaquin Cuenca Abela. + + *) Bugfix: nginx incorrectly detected cache line size on Pentium 4. + Thanks to Gena Makhomed. + + *) Bugfix: in proxied or FastCGI subrequests a client original method + was used instead of the GET method. + + *) Bugfix: socket leak in HTTPS mode if deferred accept was used. + Thanks to Ben Maurer. + + *) Bugfix: nginx issued the bogus error message "SSL_shutdown() failed + (SSL: )"; the bug had appeared in 0.6.23. + + *) Bugfix: in HTTPS mode requests might fail with the "bad write retry" + error; the bug had appeared in 0.6.23. + + +Changes with nginx 0.6.25 08 Jan 2008 + + *) Change: now the "server_name_in_redirect" directive is used instead + of the "server_name" directive's special "*" parameter. + + *) Change: now wildcard and regex names can be used as main name in a + "server_name" directive. + + *) Change: the "satisfy_any" directive was replaced by the "satisfy" + directive. + + *) Workaround: old worker processes might hog CPU after reconfiguration + if they was run under Linux OpenVZ. + + *) Feature: the "min_delete_depth" directive. + + *) Bugfix: the COPY and MOVE methods did not work with single files. - *) Feature: the "encoding" parameter in the "echo" SSI command. + *) Bugfix: the ngx_http_gzip_static_module did not allow the + ngx_http_dav_module to work; the bug had appeared in 0.6.23. - *) Change: mail proxy was split on three modules: pop3, imap and smtp. + *) Bugfix: socket leak in HTTPS mode if deferred accept was used. + Thanks to Ben Maurer. - *) Feature: the --without-mail_pop3_module, --without-mail_imap_module, - and --without-mail_smtp_module configuration parameters. + *) Bugfix: nginx could not be built without PCRE library; the bug had + appeared in 0.6.23. - *) Feature: the "smtp_greeting_delay" and "smtp_client_buffer" - directives of the ngx_mail_smtp_module. - *) Feature: the "server_name" and "valid_referers" directives support - regular expressions. +Changes with nginx 0.6.24 27 Dec 2007 - *) Feature: the "server_name", "map", and "valid_referers" directives - support the "www.example.*" wildcards. + *) Bugfix: a segmentation fault might occur in worker process if HTTPS + was used; the bug had appeared in 0.6.23. - *) Bugfix: sub_filter did not work with empty substitution. - *) Bugfix: in sub_filter parsing. +Changes with nginx 0.6.23 27 Dec 2007 - *) Bugfix: a worker process may got caught in an endless loop, if the - memcached was used. + *) Change: the "off" parameter in the "ssl_session_cache" directive; + now this is default parameter. - *) Bugfix: nginx supported low case only "close" and "keep-alive" - values in the "Connection" request header line; bug appeared in - 0.5.32. + *) Change: the "open_file_cache_retest" directive was renamed to the + "open_file_cache_valid". - *) Bugfix: nginx could not start on Solaris if the shared PCRE library - located in non-standard place was used. + *) Feature: the "open_file_cache_min_uses" directive. + *) Feature: the ngx_http_gzip_static_module. -Changes with nginx 0.5.32 24 Sep 2007 + *) Feature: the "gzip_disable" directive. - *) Change: now nginx tries to set the "worker_priority", - "worker_rlimit_nofile", "worker_rlimit_core", and - "worker_rlimit_sigpending" without super-user privileges. + *) Feature: the "memcached_pass" directive may be used inside the "if" + block. - *) Change: now nginx escapes space and "%" in request to a mail proxy - authentication server. + *) Bugfix: a segmentation fault occurred in worker process, if the + "memcached_pass" and "if" directives were used in the same location. - *) Change: now nginx escapes "%" in $memcached_key variable. + *) Bugfix: if a "satisfy_any on" directive was used and not all access + and auth modules directives were set, then other given access and + auth directives were not tested; - *) Change: the special make target "upgrade1" was defined for online - upgrade of 0.1.x versions. + *) Bugfix: regex parameters in a "valid_referers" directive were not + inherited from previous level. - *) Feature: the "add_header Last-Modified ..." directive changes the + *) Bugfix: a "post_action" directive did run if a request was completed + with 499 status code. + + *) Bugfix: optimization of 16K buffer usage in a SSL connection. + Thanks to Ben Maurer. + + *) Bugfix: the STARTTLS in SMTP mode did not work. + Thanks to Oleg Motienko. + + *) Bugfix: in HTTPS mode requests might fail with the "bad write retry" + error; the bug had appeared in 0.5.13. + + +Changes with nginx 0.6.22 19 Dec 2007 + + *) Change: now all ngx_http_perl_module methods return values copied to + perl's allocated memory. + + *) Bugfix: if nginx was built with ngx_http_perl_module, the perl + before 5.8.6 was used, and perl supported threads, then during + reconfiguration the master process aborted; the bug had appeared in + 0.5.9. + Thanks to Boris Zhmurov. + + *) Bugfix: the ngx_http_perl_module methods may get invalid values of + the regex captures. + + *) Bugfix: a segmentation fault occurred in worker process, if the + $r->has_request_body() method was called for a request whose small + request body was already received. + + *) Bugfix: large_client_header_buffers did not freed before going to + keep-alive state. + Thanks to Olexander Shtepa. + + *) Bugfix: the last address was missed in the $upstream_addr variable; + the bug had appeared in 0.6.18. + + *) Bugfix: the "fastcgi_catch_stderr" directive did return error code; + now it returns 502 code, that can be rerouted to a next server using + the "fastcgi_next_upstream invalid_header" directive. + + *) Bugfix: a segmentation fault occurred in master process if the + "fastcgi_catch_stderr" directive was used; the bug had appeared in + 0.6.10. + Thanks to Manlio Perillo. + + +Changes with nginx 0.6.21 03 Dec 2007 + + *) Change: if variable values used in a "proxy_pass" directive contain + IP-addresses only, then a "resolver" directive is not mandatory. + + *) Bugfix: a segmentation fault might occur in worker process if a + "proxy_pass" directive with URI-part was used; the bug had appeared + in 0.6.19. + + *) Bugfix: if resolver was used on platform that does not support + kqueue, then nginx issued an alert "name is out of response". + Thanks to Andrei Nigmatulin. + + *) Bugfix: if the $server_protocol was used in FastCGI parameters and a + request line length was near to the "client_header_buffer_size" + directive value, then nginx issued an alert "fastcgi: the request + record is too big". + + *) Bugfix: if a plain text HTTP/0.9 version request was made to HTTPS + server, then nginx returned usual response. + + +Changes with nginx 0.6.20 28 Nov 2007 + + *) Bugfix: a segmentation fault might occur in worker process if a + "proxy_pass" directive with URI-part was used; the bug had appeared + in 0.6.19. + + +Changes with nginx 0.6.19 27 Nov 2007 + + *) Bugfix: the 0.6.18 version could not be built. + + +Changes with nginx 0.6.18 27 Nov 2007 + + *) Change: now the ngx_http_userid_module adds start time microseconds + to the cookie field contains a pid value. + + *) Change: now the full request line instead of URI only is written to + error_log. + + *) Feature: variables support in the "proxy_pass" directive. + + *) Feature: the "resolver" and "resolver_timeout" directives. + + *) Feature: now the directive "add_header last-modified ''" deletes a "Last-Modified" response header line. - *) Feature: the mail proxy supports AUTHENTICATE in IMAP mode. - Thanks to Maxim Dounin. + *) Bugfix: the "limit_rate" directive did not allow to use full + throughput, even if limit value was very high. - *) Feature: the mail proxy supports STARTTLS in SMTP mode. + +Changes with nginx 0.6.17 15 Nov 2007 + + *) Feature: the "If-Range" request header line support. + Thanks to Alexander V. Inyukhin. + + *) Bugfix: URL double escaping in a redirect of the "msie_refresh" + directive; the bug had appeared in 0.6.4. + + *) Bugfix: the "autoindex" directive did not work with the "alias /" + directive. + + *) Bugfix: a segmentation fault might occur in worker process if + subrequests were used. + + *) Bugfix: the big responses may be transferred truncated if SSL and + gzip were used. + + *) Bugfix: the $status variable was equal to 0 if a proxied server + returned response in HTTP/0.9 version. + + +Changes with nginx 0.6.16 29 Oct 2007 + + *) Change: now the uname(2) is used on Linux instead of procfs. + Thanks to Ilya Novikov. + + *) Bugfix: if the "?" character was in a "error_page" directive, then + it was escaped in a proxied request; the bug had appeared in 0.6.11. + + *) Bugfix: compatibility with mget. + + +Changes with nginx 0.6.15 22 Oct 2007 + + *) Feature: Cygwin compatibility. + Thanks to Vladimir Kutakov. + + *) Feature: the "merge_slashes" directive. + + *) Feature: the "gzip_vary" directive. + + *) Feature: the "server_tokens" directive. + + *) Bugfix: nginx did not unescape URI in the "include" SSI command. + + *) Bugfix: the segmentation fault was occurred on start or while + reconfiguration if variable was used in the "charset" or + "source_charset" directives. + + *) Bugfix: nginx returned the 400 response on requests like + "GET http://www.domain.com HTTP/1.0". + Thanks to James Oakley. + + *) Bugfix: if request with request body was redirected using the + "error_page" directive, then nginx tried to read the request body + again; the bug had appeared in 0.6.7. + + *) Bugfix: a segmentation fault occurred in worker process if no + server_name was explicitly defined for server processing request; + the bug had appeared in 0.6.7. + + +Changes with nginx 0.6.14 15 Oct 2007 + + *) Change: now by default the "echo" SSI command uses entity encoding. + + *) Feature: the "encoding" parameter in the "echo" SSI command. + + *) Feature: the "access_log" directive may be used inside the + "limit_except" block. + + *) Bugfix: if all upstream servers were failed, then all servers had + got weight the was equal one until servers became alive; the bug had + appeared in 0.6.6. + + *) Bugfix: a segmentation fault occurred in worker process if + $date_local and $date_gmt were used outside the + ngx_http_ssi_filter_module. + + *) Bugfix: a segmentation fault might occur in worker process if debug + log was enabled. + Thanks to Andrei Nigmatulin. + + *) Bugfix: ngx_http_memcached_module did not set + $upstream_response_time. Thanks to Maxim Dounin. + *) Bugfix: a worker process may got caught in an endless loop, if the + memcached was used. + + *) Bugfix: nginx supported low case only "close" and "keep-alive" + values in the "Connection" request header line; the bug had appeared + in 0.6.11. + + *) Bugfix: sub_filter did not work with empty substitution. + + *) Bugfix: in sub_filter parsing. + + +Changes with nginx 0.6.13 24 Sep 2007 + *) Bugfix: nginx did not close directory file on HEAD request if autoindex was used. Thanks to Arkadiusz Patyk. + +Changes with nginx 0.6.12 21 Sep 2007 + + *) Change: mail proxy was split on three modules: pop3, imap and smtp. + + *) Feature: the --without-mail_pop3_module, --without-mail_imap_module, + and --without-mail_smtp_module configuration parameters. + + *) Feature: the "smtp_greeting_delay" and "smtp_client_buffer" + directives of the ngx_mail_smtp_module. + + *) Bugfix: the trailing wildcards did not work; the bug had appeared in + 0.6.9. + + *) Bugfix: nginx could not start on Solaris if the shared PCRE library + located in non-standard place was used. + *) Bugfix: the "proxy_hide_header" and "fastcgi_hide_header" directives did not hide response header lines whose name was longer than 32 characters. Thanks to Manlio Perillo. + +Changes with nginx 0.6.11 11 Sep 2007 + *) Bugfix: active connection counter always increased if mail proxy was used. @@ -75,14 +2288,87 @@ *) Bugfix: nginx did not support several "Connection" request header lines. + *) Bugfix: if the "max_fails" was set for upstream server, then after + first failure server weight was always one; the bug had appeared in + 0.6.6. + + +Changes with nginx 0.6.10 03 Sep 2007 + + *) Feature: the "open_file_cache", "open_file_cache_retest", and + "open_file_cache_errors" directives. + + *) Bugfix: socket leak; the bug had appeared in 0.6.7. + *) Bugfix: a charset set by the "charset" directive was not appended to the "Content-Type" header set by $r->send_http_header(). *) Bugfix: a segmentation fault might occur in worker process if /dev/poll method was used. + +Changes with nginx 0.6.9 28 Aug 2007 + + *) Bugfix: a worker process may got caught in an endless loop, if the + HTTPS protocol was used; the bug had appeared in 0.6.7. + + *) Bugfix: if server listened on two addresses or ports and trailing + wildcard was used, then nginx did not run. + + *) Bugfix: the "ip_hash" directive might incorrectly mark servers as + down. + + *) Bugfix: nginx could not be built on amd64; the bug had appeared in + 0.6.8. + + +Changes with nginx 0.6.8 20 Aug 2007 + + *) Change: now nginx tries to set the "worker_priority", + "worker_rlimit_nofile", "worker_rlimit_core", and + "worker_rlimit_sigpending" without super-user privileges. + + *) Change: now nginx escapes space and "%" in request to a mail proxy + authentication server. + + *) Change: now nginx escapes "%" in $memcached_key variable. + + *) Bugfix: nginx used path relative to configuration prefix for + non-absolute configuration file path specified in the "-c" key; the + bug had appeared in 0.6.6. + *) Bugfix: nginx did not work on FreeBSD/sparc64. + +Changes with nginx 0.6.7 15 Aug 2007 + + *) Change: now the paths specified in the "include", + "auth_basic_user_file", "perl_modules", "ssl_certificate", + "ssl_certificate_key", and "ssl_client_certificate" directives are + relative to directory of nginx configuration file nginx.conf, but + not to nginx prefix directory. + + *) Change: the --sysconfdir=PATH option in configure was canceled. + + *) Change: the special make target "upgrade1" was defined for online + upgrade of 0.1.x versions. + + *) Feature: the "server_name" and "valid_referers" directives support + regular expressions. + + *) Feature: the "server" directive in the "upstream" context supports + the "backup" parameter. + + *) Feature: the ngx_http_perl_module supports the + $r->discard_request_body. + + *) Feature: the "add_header Last-Modified ..." directive changes the + "Last-Modified" response header line. + + *) Bugfix: if a response different than 200 was returned to a request + with body and connection went to the keep-alive state after the + request, then nginx returned 400 for the next request. + *) Bugfix: a segmentation fault occurred in worker process if invalid address was set in the "auth_http" directive. @@ -90,69 +2376,80 @@ platforms except FreeBSD. Thanks to Jiang Hong. + *) Bugfix: a worker process may got caught in an endless loop, if a + "server" inside "upstream" block was marked as "down"; the bug had + appeared in 0.6.6. + *) Bugfix: now Solaris sendfilev() is not used to transfer the client request body to FastCGI-server via the unix domain socket. - *) Bugfix: if the same host without specified port was used as backend - for HTTP and HTTPS, then nginx used only one port - 80 or 443. - - *) Bugfix: the "proxy_ignore_client_abort" and - "fastcgi_ignore_client_abort" directives did not work; bug appeared - in 0.5.13. +Changes with nginx 0.6.6 30 Jul 2007 -Changes with nginx 0.5.31 15 Aug 2007 + *) Feature: the --sysconfdir=PATH option in configure. *) Feature: named locations. - *) Feature: the "proxy_store" and "fastcgi_store" directives. - - *) Feature: the "proxy_store_access" and "fastcgi_store_access" - directives. - - -Changes with nginx 0.5.30 30 Jul 2007 - *) Feature: the $args variable can be set with the "set" directive. *) Feature: the $is_args variable. + *) Bugfix: fair big weight upstream balancer. + *) Bugfix: if a client has closed connection to mail proxy then nginx might not close connection to backend. - *) Bugfix: now nginx escapes space in $memcached_key variable. - - *) Bugfix: a segmentation fault might occur in worker process when the - HTTPS protocol was used in the "proxy_pass" directive. - - *) Bugfix: the perl $$ variable value in ngx_http_perl_module was equal - to the master process identification number. + *) Bugfix: if the same host without specified port was used as backend + for HTTP and HTTPS, then nginx used only one port - 80 or 443. *) Bugfix: fix building on Solaris/amd64 by Sun Studio 11 and early - versions; bug appeared in 0.5.29. + versions; the bug had appeared in 0.6.4. -Changes with nginx 0.5.29 23 Jul 2007 +Changes with nginx 0.6.5 23 Jul 2007 *) Feature: $nginx_version variable. Thanks to Nick S. Grechukh. - *) Bugfix: if the FastCGI header was split in records, then nginx - passed garbage in the header to a client. + *) Feature: the mail proxy supports AUTHENTICATE in IMAP mode. + Thanks to Maxim Dounin. + + *) Feature: the mail proxy supports STARTTLS in SMTP mode. + Thanks to Maxim Dounin. + + *) Bugfix: now nginx escapes space in $memcached_key variable. - *) Bugfix: Sun Studio compatibility on Solaris/amd64 and - Solaris/sparc64. - Thanks to Jiang Hong and Andrei Nigmatulin. + *) Bugfix: nginx was incorrectly built by Sun Studio on + Solaris/amd64. + Thanks to Jiang Hong. *) Bugfix: of minor potential bugs. Thanks to Coverity's Scan. -Changes with nginx 0.5.28 17 Jul 2007 +Changes with nginx 0.6.4 17 Jul 2007 *) Security: the "msie_refresh" directive allowed XSS. Thanks to Maxim Boguk. + *) Change: the "proxy_store" and "fastcgi_store" directives were + changed. + + *) Feature: the "proxy_store_access" and "fastcgi_store_access" + directives. + + *) Bugfix: nginx did not work on Solaris/sparc64 if it was built by Sun + Studio. + Thanks to Andrei Nigmatulin. + + *) Workaround: for Sun Studio 12. + Thanks to Jiang Hong. + + +Changes with nginx 0.6.3 12 Jul 2007 + + *) Feature: the "proxy_store" and "fastcgi_store" directives. + *) Bugfix: a segmentation fault might occur in worker process if the "auth_http_header" directive was used. Thanks to Maxim Dounin. @@ -160,11 +2457,26 @@ *) Bugfix: a segmentation fault occurred in worker process if the CRAM-MD5 authentication method was used, but it was not enabled. + *) Bugfix: a segmentation fault might occur in worker process when the + HTTPS protocol was used in the "proxy_pass" directive. + *) Bugfix: a segmentation fault might occur in worker process if the eventport method was used. + *) Bugfix: the "proxy_ignore_client_abort" and + "fastcgi_ignore_client_abort" directives did not work; the bug had + appeared in 0.5.13. + + +Changes with nginx 0.6.2 09 Jul 2007 + + *) Bugfix: if the FastCGI header was split in records, then nginx + passed garbage in the header to a client. + + +Changes with nginx 0.6.1 17 Jun 2007 -Changes with nginx 0.5.27 09 Jul 2007 + *) Bugfix: in SSI parsing. *) Bugfix: if remote SSI subrequest was used, then posterior local file subrequest might transferred to client in wrong order. @@ -172,16 +2484,21 @@ *) Bugfix: large SSI inclusions buffered in temporary files were truncated. + *) Bugfix: the perl $$ variable value in ngx_http_perl_module was equal + to the master process identification number. + -Changes with nginx 0.5.26 17 Jun 2007 +Changes with nginx 0.6.0 14 Jun 2007 - *) Bugfix: in SSI parsing. + *) Feature: the "server_name", "map", and "valid_referers" directives + support the "www.example.*" wildcards. Changes with nginx 0.5.25 11 Jun 2007 *) Bugfix: nginx could not be built with the - --without-http_rewrite_module parameter; bug appeared in 0.5.24. + --without-http_rewrite_module parameter; the bug had appeared in + 0.5.24. Changes with nginx 0.5.24 06 Jun 2007 @@ -190,7 +2507,7 @@ was made using HTTP/0.9. *) Bugfix: a part of response body might be passed uncompressed if gzip - was used; bug appeared in 0.5.23. + was used; the bug had appeared in 0.5.23. Changes with nginx 0.5.23 04 Jun 2007 @@ -213,8 +2530,8 @@ Changes with nginx 0.5.22 29 May 2007 - *) Bugfix: a big request body might not be passed to backend; bug - appeared in 0.5.21. + *) Bugfix: a big request body might not be passed to backend; the bug + had appeared in 0.5.21. Changes with nginx 0.5.21 28 May 2007 @@ -251,7 +2568,8 @@ Studio. Thanks to Andrei Nigmatulin. - *) Bugfix: the ngx_http_perl_module could not built by Solaris make. + *) Bugfix: the ngx_http_perl_module could not be built by Solaris + make. Thanks to Andrei Nigmatulin. @@ -286,11 +2604,12 @@ *) Bugfix: a segmentation fault occurred in master process after first reconfiguration and receiving any signal if nginx was built with - ngx_http_perl_module and perl did not support multiplicity; bug - appeared in 0.5.9. + ngx_http_perl_module and perl did not support multiplicity; the bug + had appeared in 0.5.9. *) Bugfix: if perl did not support multiplicity, then after - reconfiguration perl code did not work; bug appeared in 0.3.38. + reconfiguration perl code did not work; the bug had appeared in + 0.3.38. Changes with nginx 0.5.17 02 Apr 2007 @@ -317,14 +2636,14 @@ *) Bugfix: a segmentation fault might occur in worker process if a charset was set in the "Content-Type" header line and the line has - trailing ";"; bug appeared in 0.3.50. + trailing ";"; the bug had appeared in 0.3.50. *) Bugfix: the "[alert] zero size buf" error when FastCGI server was used and a request body written in a temporary file was multiple of 32K. *) Bugfix: nginx could not be built on Solaris without the --with-debug - option; bug appeared in 0.5.15. + option; the bug had appeared in 0.5.15. Changes with nginx 0.5.15 19 Mar 2007 @@ -380,17 +2699,17 @@ send timeout only. *) Bugfix: nginx could not be built on platforms different from i386, - amd64, sparc and ppc; bug appeared in 0.5.8. + amd64, sparc, and ppc; the bug had appeared in 0.5.8. Changes with nginx 0.5.12 12 Feb 2007 *) Bugfix: nginx could not be built on platforms different from i386, - amd64, sparc É ppc; bug appeared in 0.5.8. + amd64, sparc, and ppc; the bug had appeared in 0.5.8. *) Bugfix: a segmentation fault might occur in worker process if the - temporarily files were used while working with FastCGI server; bug - appeared in 0.5.8. + temporary files were used while working with FastCGI server; the bug + had appeared in 0.5.8. *) Bugfix: a segmentation fault might occur in worker process if the $fastcgi_script_name variable was logged. @@ -404,7 +2723,7 @@ Thanks to Chris McGrath. *) Bugfix: the response was incorrect if several ranges were requested; - bug appeared in 0.5.6. + the bug had appeared in 0.5.6. *) Bugfix: the "create_full_put_path" directive could not create the intermediate directories if no "dav_access" directive was set. @@ -420,10 +2739,10 @@ Changes with nginx 0.5.10 26 Jan 2007 *) Bugfix: while online executable file upgrade the new master process - did not inherit the listening sockets; bug appeared in 0.5.9. + did not inherit the listening sockets; the bug had appeared in 0.5.9. *) Bugfix: a segmentation fault might occur in worker process if nginx - was built with -O2 optimization; bug appeared in 0.5.1. + was built with -O2 optimization; the bug had appeared in 0.5.1. Changes with nginx 0.5.9 25 Jan 2007 @@ -459,7 +2778,7 @@ *) Bugfix: if the "proxy_buffering off" directive was used and a client connection was non-active, then the connection was closed after send - timeout; bug appeared in 0.4.7. + timeout; the bug had appeared in 0.4.7. *) Bugfix: if the "epoll" method was used and a client closed a connection prematurely, then nginx closed the connection after a @@ -540,7 +2859,7 @@ directive, then nginx might report about configuration error. *) Bugfix: a segmentation fault might occur if the $host variable was - used; bug appeared in 0.4.14. + used; the bug had appeared in 0.4.14. Changes with nginx 0.5.3 13 Dec 2006 @@ -557,8 +2876,8 @@ Changes with nginx 0.5.2 11 Dec 2006 *) Bugfix: if the "proxy_pass" directive used the name of the - "upstream" block, then nginx tried to resolve the name; bug appeared - in 0.5.1. + "upstream" block, then nginx tried to resolve the name; the bug had + appeared in 0.5.1. Changes with nginx 0.5.1 11 Dec 2006 @@ -566,19 +2885,20 @@ *) Bugfix: the "post_action" directive might not run after a unsuccessful completion of a request. - *) Workaround: for Eudora for Mac; bug appeared in 0.4.11. + *) Workaround: for Eudora for Mac; the bug had appeared in 0.4.11. Thanks to Bron Gondwana. *) Bugfix: if the "upstream" name was used in the "fastcgi_pass", then - the message "no port in upstream" was issued; bug appeared in 0.5.0. + the message "no port in upstream" was issued; the bug had appeared + in 0.5.0. *) Bugfix: if the "proxy_pass" and "fastcgi_pass" directives used the same servers but different ports, then these directives uses the - first described port; bug appeared in 0.5.0. + first described port; the bug had appeared in 0.5.0. *) Bugfix: if the "proxy_pass" and "fastcgi_pass" directives used the unix domain sockets, then these directives used first described - socket; bug appeared in 0.5.0. + socket; the bug had appeared in 0.5.0. *) Bugfix: ngx_http_auth_basic_module ignored the user if it was in the last line in the password file and there was no the carriage return, @@ -607,8 +2927,8 @@ *) Feature: the WAIT status in the "Auth-Status" header line of the IMAP/POP3 proxy authentication server response. - *) Bugfix: nginx could not be built on 64-bit platforms; bug appeared - in 0.4.14. + *) Bugfix: nginx could not be built on 64-bit platforms; the bug had + appeared in 0.4.14. Changes with nginx 0.4.14 27 Nov 2006 @@ -619,7 +2939,7 @@ Linux, and NetBSD. *) Bugfix: ngx_http_perl_module did not work with perl built with the - threads support; bug appeared in 0.3.38. + threads support; the bug had appeared in 0.3.38. *) Bugfix: ngx_http_perl_module did not work if perl was called recursively. @@ -654,7 +2974,7 @@ the deferred accept() were used. *) Bugfix: a charset could not be set for ngx_http_autoindex_module - responses; bug appeared in 0.3.50. + responses; the bug had appeared in 0.3.50. *) Bugfix: the "[alert] zero size buf" error when FastCGI server was used; @@ -662,8 +2982,8 @@ *) Bugfix: the --group= configuration parameter was ignored. Thanks to Thomas Moschny. - *) Bugfix: the 50th subrequest in SSI response did not work; bug - appeared in 0.3.50. + *) Bugfix: the 50th subrequest in SSI response did not work; the bug + had appeared in 0.3.50. Changes with nginx 0.4.12 31 Oct 2006 @@ -684,7 +3004,7 @@ method. *) Bugfix: if the APOP was enabled in the POP3 proxy, then the - USER/PASS commands might not work; bug appeared in 0.4.10. + USER/PASS commands might not work; the bug had appeared in 0.4.10. Changes with nginx 0.4.10 23 Oct 2006 @@ -699,10 +3019,10 @@ variable was used in the "map" directive. *) Bugfix: the ngx_http_flv_module did not support the byte ranges for - full responses; bug appeared in 0.4.7. + full responses; the bug had appeared in 0.4.7. - *) Bugfix: nginx could not be built on Debian amd64; bug appeared in - 0.4.9. + *) Bugfix: nginx could not be built on Debian amd64; the bug had + appeared in 0.4.9. Changes with nginx 0.4.9 13 Oct 2006 @@ -755,14 +3075,14 @@ $r->headers_out("Content-Length", ...) method. *) Bugfix: after redirecting error by an "error_page" directive any - ngx_http_rewrite_module directive returned this error code; bug - appeared in 0.4.4. + ngx_http_rewrite_module directive returned this error code; the bug + had appeared in 0.4.4. Changes with nginx 0.4.5 02 Oct 2006 - *) Bugfix: nginx could not be built on Linux and Solaris; bug appeared - in 0.4.4. + *) Bugfix: nginx could not be built on Linux and Solaris; the bug had + appeared in 0.4.4. Changes with nginx 0.4.4 02 Oct 2006 @@ -797,7 +3117,7 @@ error to the proxied server using a "proxy_pass" directive. *) Bugfix: a segmentation fault occurred if an unix domain socket was - used in a "proxy_pass" directive; bug appeared in 0.3.47. + used in a "proxy_pass" directive; the bug had appeared in 0.3.47. *) Bugfix: SSI did work with memcached and nonbuffered responses. @@ -806,8 +3126,8 @@ Changes with nginx 0.4.2 14 Sep 2006 - *) Bugfix: the O_NOATIME flag support on Linux was canceled; bug - appeared in 0.4.1. + *) Bugfix: the O_NOATIME flag support on Linux was canceled; the bug + had appeared in 0.4.1. Changes with nginx 0.4.1 14 Sep 2006 @@ -841,7 +3161,7 @@ *) Bugfix: a segmentation fault occurred if there was an "index" directive with variables and the first index name was without - variables; bug appeared in 0.1.29. + variables; the bug had appeared in 0.1.29. Changes with nginx 0.3.61 28 Aug 2006 @@ -859,7 +3179,7 @@ Changes with nginx 0.3.60 18 Aug 2006 *) Bugfix: a worker process may got caught in an endless loop while an - error redirection; bug appeared in 0.3.59. + error redirection; the bug had appeared in 0.3.59. Changes with nginx 0.3.59 16 Aug 2006 @@ -871,7 +3191,7 @@ *) Bugfix: the "error_page" directive did not changes the "Content-Type" header line after the "X-Accel-Redirect" was used; - bug appeared in 0.3.58. + the bug had appeared in 0.3.58. Changes with nginx 0.3.58 14 Aug 2006 @@ -931,7 +3251,7 @@ *) Bugfix: if the request contained "//" or "/./" and escaped symbols after them, then the proxied request was sent unescaped. - *) Bugfix: the $r->headers_in("Cookie") of the ngx_http_perl_module now + *) Bugfix: the $r->header_in("Cookie") of the ngx_http_perl_module now returns all "Cookie" header lines. *) Bugfix: a segmentation fault occurred if @@ -939,8 +3259,8 @@ upstream. *) Bugfix: on some condition while reconfiguration character codes - inside the "charset_map" may be treated invalid; bug appeared in - 0.3.50. + inside the "charset_map" may be treated invalid; the bug had + appeared in 0.3.50. Changes with nginx 0.3.54 11 Jul 2006 @@ -959,8 +3279,8 @@ *) Bugfix: the $upstream_response_time variable had the time of the first request to a backend only. - *) Bugfix: nginx could not be built on amd64 platform; bug appeared in - 0.3.53. + *) Bugfix: nginx could not be built on amd64 platform; the bug had + appeared in 0.3.53. Changes with nginx 0.3.53 07 Jul 2006 @@ -993,10 +3313,10 @@ return the 405 error. *) Bugfix: the worker process may got caught in an endless loop if the - limit rate was used; bug appeared in 0.3.37. + limit rate was used; the bug had appeared in 0.3.37. *) Bugfix: ngx_http_charset_module logged "unknown charset" alert, even - if the recoding was not needed; bug appeared in 0.3.50. + if the recoding was not needed; the bug had appeared in 0.3.50. *) Bugfix: if a code response of the PUT request was 409, then a temporary file was not removed. @@ -1005,7 +3325,7 @@ Changes with nginx 0.3.51 30 Jun 2006 *) Bugfix: the "<" symbols might disappeared some conditions in the - SSI; bug appeared in 0.3.50. + SSI; the bug had appeared in 0.3.50. Changes with nginx 0.3.50 28 Jun 2006 @@ -1050,10 +3370,11 @@ *) Bugfix: the internal redirect always transform client's HTTP method to GET, now the transformation is made for the "X-Accel-Redirect" - redirects only and if the method is not HEAD; bug appeared in 0.3.42. + redirects only and if the method is not HEAD; the bug had appeared + in 0.3.42. *) Bugfix: the ngx_http_perl_module could not be built, if the perl was - built with the threads support; bug appeared in 0.3.46. + built with the threads support; the bug had appeared in 0.3.46. Changes with nginx 0.3.47 23 May 2006 @@ -1090,7 +3411,8 @@ *) Change: the ° symbol codes were changed in koi-win conversion table. - *) Feature: the euro É N symbols were added to koi-win conversion table. + *) Feature: the euro and N symbols were added to koi-win conversion + table. *) Bugfix: if nginx distributed the requests among several backends and some backend failed, then requests intended for this backend was @@ -1167,9 +3489,9 @@ *) Bugfix: the active connection counter increased on the exceeding of the connection limit specified by the "worker_connections" - directive; bug appeared in 0.2.0. + directive; the bug had appeared in 0.2.0. - *) Bugfix: the limit rate might not work on some condition; bug + *) Bugfix: the limit rate might not work on some condition; the bug had appeared in 0.3.38. @@ -1243,7 +3565,7 @@ Changes with nginx 0.3.35 22 Mar 2006 *) Bugfix: the accept-filter and the TCP_DEFER_ACCEPT option were set - for first "listen" directive only; bug appeared in 0.3.31. + for first "listen" directive only; the bug had appeared in 0.3.31. *) Bugfix: in the "proxy_pass" directive without the URI part in a subrequest. @@ -1268,7 +3590,7 @@ Changes with nginx 0.3.32 11 Mar 2006 *) Bugfix: the debug logging on startup and reconfiguration time was - removed; bug appeared in 0.3.31. + removed; the bug had appeared in 0.3.31. Changes with nginx 0.3.31 10 Mar 2006 @@ -1286,11 +3608,12 @@ *) Bugfix: if there were several "listen" directives listening one various addresses inside one server, then server names like - "*.domain.tld" worked for first address only; bug appeared in 0.3.18. + "*.domain.tld" worked for first address only; the bug had appeared + in 0.3.18. *) Bugfix: if the HTTPS protocol was used in the "proxy_pass" directive - and the request body was in temporarily file then the request was - not transferred. + and the request body was in temporary file then the request was not + transferred. *) Bugfix: perl 5.8.8 compatibility. @@ -1304,7 +3627,7 @@ ngx_http_ssi_filter_module. *) Bugfix: nginx could not be built on i386 platform, if the PIC was - used; bug appeared in 0.3.27. + used; the bug had appeared in 0.3.27. Changes with nginx 0.3.29 20 Feb 2006 @@ -1380,8 +3703,8 @@ Changes with nginx 0.3.25 01 Feb 2006 *) Bugfix: the segmentation fault was occurred on start or while - reconfiguration if there was invalid configuration; bug appeared in - 0.3.24. + reconfiguration if there was invalid configuration; the bug had + appeared in 0.3.24. Changes with nginx 0.3.24 01 Feb 2006 @@ -1397,8 +3720,8 @@ location. *) Bugfix: on 64-bit platforms segmentation fault may occurred on start - if the many names were used in the "server_name" directives; bug - appeared in 0.3.18. + if the many names were used in the "server_name" directives; the bug + had appeared in 0.3.18. Changes with nginx 0.3.23 24 Jan 2006 @@ -1421,8 +3744,8 @@ canceled. *) Bugfix: segmentation fault was occurred if the "none" or "blocked" - values was specified in the "valid_referers" directive; bug appeared - in 0.3.18. + values was specified in the "valid_referers" directive; the bug had + appeared in 0.3.18. Changes with nginx 0.3.21 16 Jan 2006 @@ -1473,10 +3796,10 @@ ngx_http_map_module. *) Bugfix: segmentation fault was occurred if configuration file did - not exist; bug appeared in 0.3.12. + not exist; the bug had appeared in 0.3.12. *) Bugfix: on 64-bit platforms segmentation fault may occurred on - start; bug appeared in 0.3.16. + start; the bug had appeared in 0.3.16. Changes with nginx 0.3.17 18 Dec 2005 @@ -1487,8 +3810,8 @@ *) Feature: the "map" directive supports domain names in the ".domain.tld" form. - *) Bugfix: the timeouts were not used in SSL handshake; bug appeared in - 0.2.4. + *) Bugfix: the timeouts were not used in SSL handshake; the bug had + appeared in 0.2.4. *) Bugfix: in the HTTPS protocol in the "proxy_pass" directive. @@ -1514,11 +3837,11 @@ *) Bugfix: the "config timefmt" SSI command set incorrect time format. *) Bugfix: nginx did not close connection to IMAP/POP3 backend for the - SSL connections; bug appeared in 0.3.13. + SSL connections; the bug had appeared in 0.3.13. Thanks to Rob Mueller. - *) Bugfix: segmentation fault may occurred in at SSL shutdown; bug - appeared in 0.3.13. + *) Bugfix: segmentation fault may occurred in at SSL shutdown; the bug + had appeared in 0.3.13. Changes with nginx 0.3.15 07 Dec 2005 @@ -1534,8 +3857,8 @@ Changes with nginx 0.3.14 05 Dec 2005 - *) Bugfix: in the 304 response the body was transferred; bug appeared - in 0.3.13. + *) Bugfix: in the 304 response the body was transferred; the bug had + appeared in 0.3.13. Changes with nginx 0.3.13 05 Dec 2005 @@ -1551,7 +3874,7 @@ request body to FastCGI-server via the unix domain socket. *) Bugfix: the "auth_basic" directive did not disable the - authorization; bug appeared in 0.3.11. + authorization; the bug had appeared in 0.3.11. Changes with nginx 0.3.12 26 Nov 2005 @@ -1572,7 +3895,7 @@ *) Feature: the "proxy_buffering" directive. *) Bugfix: the changes in accept mutex handling when the "rtsig" method - was used; bug appeared in 0.3.0. + was used; the bug had appeared in 0.3.0. *) Bugfix: if the client sent the "Transfer-Encoding: chunked" header line, then nginx returns the 411 error. @@ -1583,7 +3906,7 @@ *) Bugfix: if the "combined" format was explicitly specified in the "access_log" directive, then the empty lines was written to the log; - bug appeared in 0.3.8. + the bug had appeared in 0.3.8. *) Bugfix: nginx did not run on the sparc platform under any OS except Solaris. @@ -1595,7 +3918,7 @@ Changes with nginx 0.3.11 15 Nov 2005 *) Bugfix: nginx did not pass the client request headers and body while - proxying; bug appeared in 0.3.10. + proxying; the bug had appeared in 0.3.10. Changes with nginx 0.3.10 15 Nov 2005 @@ -1634,7 +3957,7 @@ Changes with nginx 0.3.9 10 Nov 2005 *) Bugfix: nginx considered URI as unsafe if two any symbols was - between two slashes; bug appeared in 0.3.8. + between two slashes; the bug had appeared in 0.3.8. Changes with nginx 0.3.8 09 Nov 2005 @@ -1674,8 +3997,8 @@ *) Bugfix: if the request URI was changes by the "rewrite" directive and the request was proxied in location given by regular expression, - then the incorrect request was transferred to backend; bug appeared - in 0.2.6. + then the incorrect request was transferred to backend; the bug had + appeared in 0.2.6. *) Bugfix: the "expires" directive did not remove the previous "Expires" header. @@ -1696,7 +4019,7 @@ *) Feature: the "access_log" supports the "buffer=" parameter. *) Bugfix: nginx could not be built on platforms different from i386, - amd64, sparc É ppc; bug appeared in 0.3.2. + amd64, sparc, and ppc; the bug had appeared in 0.3.2. Changes with nginx 0.3.6 24 Oct 2005 @@ -1707,7 +4030,8 @@ *) Feature: the "log_format" supports the variables in the $name form. *) Bugfix: if at least in one server was no the "listen" directive, - then nginx did not listen on the 80 port; bug appeared in 0.3.3. + then nginx did not listen on the 80 port; the bug had appeared in + 0.3.3. *) Bugfix: if the URI part is omitted in "proxy_pass" directive, the the 80 port was always used. @@ -1716,10 +4040,10 @@ Changes with nginx 0.3.5 21 Oct 2005 *) Bugfix: the segmentation fault may occurred if the IMAP/POP3 login - was changed by authorization server; bug appeared in 0.2.2. + was changed by authorization server; the bug had appeared in 0.2.2. *) Bugfix: the accept mutex did not work and all connections were - handled by one process; bug appeared in 0.3.3. + handled by one process; the bug had appeared in 0.3.3. *) Bugfix: the timeout did not work if the "rtsig" method and the "timer_resolution" directive were used. @@ -1727,8 +4051,8 @@ Changes with nginx 0.3.4 19 Oct 2005 - *) Bugfix: nginx could not be built on Linux 2.4+ and MacOS X; bug - appeared in 0.3.3. + *) Bugfix: nginx could not be built on Linux 2.4+ and MacOS X; the bug + had appeared in 0.3.3. Changes with nginx 0.3.3 19 Oct 2005 @@ -1749,7 +4073,7 @@ the CLOSED state. *) Bugfix: the mime type may be incorrectly set to default value for - index file with variable in the name; bug appeared in 0.3.0. + index file with variable in the name; the bug had appeared in 0.3.0. *) Feature: the "timer_resolution" directive. @@ -1781,7 +4105,8 @@ Changes with nginx 0.3.1 10 Oct 2005 *) Bugfix: the segmentation fault occurred when the signal queue - overflowed if the "rtsig" method was used; bug appeared in 0.2.0. + overflowed if the "rtsig" method was used; the bug had appeared in + 0.2.0. *) Change: correct handling of the "\\", "\"", "\'", and "\$" pairs in SSI. @@ -1815,7 +4140,7 @@ *) Bugfix: if the "set" directive set the ngx_http_geo_module variable in some configuration part, the this variable was not available in other configuration parts and the "using uninitialized variable" - error was occurred; bug appeared in 0.2.2. + error was occurred; the bug had appeared in 0.2.2. Changes with nginx 0.2.5 04 Oct 2005 @@ -1837,17 +4162,17 @@ *) Feature: the ngx_http_ssi_module supports "$var=text", "$var!=text", "$var=/text/", and "$var!=/text/" expressions in the "if" command. - *) Bugfix: in proxying location without trailing slash; bug appeared in - 0.1.44. + *) Bugfix: in proxying location without trailing slash; the bug had + appeared in 0.1.44. *) Bugfix: the segmentation fault may occurred if the "rtsig" method - was used; bug appeared in 0.2.0. + was used; the bug had appeared in 0.2.0. Changes with nginx 0.2.3 30 Sep 2005 *) Bugfix: nginx could not be built without the --with-debug option; - bug appeared in 0.2.2. + the bug had appeared in 0.2.2. Changes with nginx 0.2.2 30 Sep 2005 @@ -1876,8 +4201,8 @@ Changes with nginx 0.2.1 23 Sep 2005 *) Bugfix: if all backend using in load-balancing failed after one - error, then nginx may got caught in an endless loop; bug appeared in - 0.2.0. + error, then nginx may got caught in an endless loop; the bug had + appeared in 0.2.0. Changes with nginx 0.2.0 23 Sep 2005 @@ -1955,7 +4280,7 @@ *) Bugfix: the segmentation fault occurred or the worker process may got caught in an endless loop if the proxied or FastCGI server sent the "Cache-Control" header line and the "expires" directive was - used; in the proxied mode the bug appeared in 0.1.29. + used; in the proxied mode the the bug had appeared in 0.1.29. Changes with nginx 0.1.42 23 Aug 2005 @@ -1965,7 +4290,7 @@ occurred in the ngx_http_proxy_module. *) Bugfix: the "limit_rate" directive did not work inside the "if" - block; bug appeared in 0.1.38. + block; the bug had appeared in 0.1.38. Changes with nginx 0.1.41 25 Jul 2005 @@ -1980,7 +4305,7 @@ information did not logged in the error log. *) Bugfix: the "Set-Cookie" header line was not transferred when the - "X-Accel-Redirect" was used; bug appeared in 0.1.39. + "X-Accel-Redirect" was used; the bug had appeared in 0.1.39. *) Bugfix: the "Content-Disposition" header line was not transferred when the "X-Accel-Redirect" was used. @@ -2002,8 +4327,8 @@ transferred while the 401 response code redirecting. *) Bugfix: the ngx_http_proxy_module and ngx_http_fastcgi_module may - close a connection before anything was transferred to a client; bug - appeared in 0.1.38. + close a connection before anything was transferred to a client; the + bug had appeared in 0.1.38. *) Workaround: the Linux glibc crypt_r() initialization bug. @@ -2012,17 +4337,17 @@ *) Bugfix: if the backend response had the "Location" header line and nginx should not rewrite this line, then the 500 code response body - was transferred; bug appeared in 0.1.29. + was transferred; the bug had appeared in 0.1.29. *) Bugfix: some directives of the ngx_http_proxy_module and ngx_http_fastcgi_module were not inherited from the server to the - location level; bug appeared in 0.1.29. + location level; the bug had appeared in 0.1.29. *) Bugfix: the ngx_http_ssl_module did not support the certificate chain. *) Bugfix: the ngx_http_autoindex_module did not show correctly the - long file names; bug appeared in 0.1.38. + long file names; the bug had appeared in 0.1.38. *) Bugfixes in IMAP/POP3 proxy in interaction with a backend at the login state. @@ -2050,8 +4375,8 @@ than one remote subrequest. *) Bugfix: nginx treated the backend response as invalid if the status - line in the header was transferred in two packets; bug appeared in - 0.1.29. + line in the header was transferred in two packets; the bug had + appeared in 0.1.29. *) Feature: the "ssi_types" directive. @@ -2105,7 +4430,7 @@ *) Feature: the "port_in_redirect" directive. *) Bugfix: the segmentation fault was occurred if the backend response - header was in several packets; bug appeared in 0.1.29. + header was in several packets; the bug had appeared in 0.1.29. *) Bugfix: if more than 10 servers were configured or some server did not use the "listen" directive, then the segmentation fault was @@ -2115,7 +4440,8 @@ bigger than the temporary file. *) Bugfix: nginx returned the 400 response on requests like - "GET http://www.domain.com/uri HTTP/1.0"; bug appeared in 0.1.28. + "GET http://www.domain.com/uri HTTP/1.0"; the bug had appeared in + 0.1.28. Changes with nginx 0.1.34 26 May 2005 @@ -2135,7 +4461,7 @@ Changes with nginx 0.1.33 23 May 2005 *) Bugfix: nginx could not be built with the --without-pcre parameter; - bug appeared in 0.1.29. + the bug had appeared in 0.1.29. *) Bugfix: 3, 4, 7, and 8 the "proxy_set_header" directives in one level cause the bus fault on start up. @@ -2149,7 +4475,7 @@ Changes with nginx 0.1.32 19 May 2005 *) Bugfix: the arguments were omitted in the redirects, issued by the - "rewrite" directive; bug appeared in 0.1.29. + "rewrite" directive; the bug had appeared in 0.1.29. *) Feature: the "if" directive supports the captures in regular expressions. @@ -2170,7 +4496,7 @@ *) Bugfix: errors while using SSI and gzipping. *) Bugfix: the redirect with the 301 code was transferred without - response body; bug appeared in 0.1.30. + response body; the bug had appeared in 0.1.30. Changes with nginx 0.1.30 14 May 2005 @@ -2182,7 +4508,8 @@ *) Bugfix: if the length of the response part received at once from proxied or FastCGI server was equal to 500, then nginx returns the - 500 response code; in proxy mode the bug appeared in 0.1.29 only. + 500 response code; in proxy mode the the bug had appeared in 0.1.29 + only. *) Bugfix: nginx did not consider the directives with 8 or 9 parameters as invalid. @@ -2261,7 +4588,7 @@ returned the 408 response. *) Bugfix: the segmentation fault was occurred if the backend sent an - invalid line in response header; bug appeared in 0.1.26. + invalid line in response header; the bug had appeared in 0.1.26. *) Bugfix: the segmentation fault may occurred in FastCGI fault tolerance configuration. @@ -2381,7 +4708,7 @@ server name of the "server_name" directive. *) Bugfix: nginx could not be built on platforms different from i386, - amd64, sparc É ppc; bug appeared in 0.1.22. + amd64, sparc, and ppc; the bug had appeared in 0.1.22. *) Bugfix: the ngx_http_autoindex_module now shows the information not about the symlink, but about file or directory it points to. @@ -2396,7 +4723,7 @@ connections statistics if the proxying or FastCGI server were used. *) Bugfix: the installation paths were incorrectly quoted on Linux and - Solaris; bug appeared in 0.1.21. + Solaris; the bug had appeared in 0.1.21. Changes with nginx 0.1.21 22 Feb 2005 @@ -2438,8 +4765,8 @@ *) Bugfix: the proxy_set_x_var and fastcgi_set_var directives were not inherited. - *) Bugfix: in the redirect rewrite directive the arguments were - concatenated with URI by the "&" rather than the "?". + *) Bugfix: in a redirect rewrite directive arguments were concatenated + with URI by an "&" rather than a "?". *) Bugfix: the lines without trailing ";" in the file being included by the ngx_http_geo_module were silently ignored. @@ -2481,7 +4808,8 @@ static page, then the segmentation fault occurred. *) Bugfix: if in a proxied "Location" header was a relative URL, then a - host name and a slash were added to them; bug appeared in 0.1.14. + host name and a slash were added to them; the bug had appeared in + 0.1.14. *) Bugfix: the system error message was not logged on Linux. @@ -2506,7 +4834,7 @@ *) Feature: the rewrite directive supports the arguments rewriting. *) Bugfix: the response code 400 was returned for the POST request with - the "Content-Length: 0" header; bug appeared in 0.1.14. + the "Content-Length: 0" header; the bug had appeared in 0.1.14. Changes with nginx 0.1.15 19 Jan 2005 @@ -2527,8 +4855,8 @@ to use the regular expressions in locations. *) Bugfix: the directive "proxy_preserve_host on" adds port 80 to the - "Host" headers, if upstream listen on port 80; bug appeared in - 0.1.14. + "Host" headers, if upstream listen on port 80; the bug had appeared + in 0.1.14. *) Bugfix: the same paths in autoconfiguration parameters --http-client-body-temp-path=PATH and --http-proxy-temp-path=PATH, @@ -2554,7 +4882,8 @@ fastcgi_max_temp_file_size, fastcgi_temp_file_write_size, fastcgi_next_upstream, and fastcgi_x_powered_by. - *) Bugfix: the "[alert] zero size buf" error; bug appeared in 0.1.3. + *) Bugfix: the "[alert] zero size buf" error; the bug had appeared in + 0.1.3. *) Change: the URI must be specified after the host name in the proxy_pass directive. @@ -2639,7 +4968,7 @@ *) Bugfix: if the request without arguments contains "//", "/./", "/../" or "%XX" then the lost character in the request line was - lost; bug appeared in 0.1.9. + lost; the bug had appeared in 0.1.9. *) Bugfix: the fix in 0.1.9 for the files bigger than 2G on Linux did not work. @@ -2657,7 +4986,8 @@ does not support sendfile64(). *) Bugfix: while the build configuration on Linux the - --with-poll_module parameter was required; bug appeared in 0.1.8. + --with-poll_module parameter was required; the bug had appeared in + 0.1.8. Changes with nginx 0.1.8 20 Nov 2004 @@ -2673,7 +5003,7 @@ Changes with nginx 0.1.7 12 Nov 2004 *) Bugfix: on FreeBSD the segmentation fault may occur if the size of - the transferred file was changed; bug appeared in 0.1.5. + the transferred file was changed; the bug had appeared in 0.1.5. Changes with nginx 0.1.6 11 Nov 2004 @@ -2732,13 +5062,13 @@ *) Bugfix: the portability improvements. *) Bugfix: if configuration file was set in command line, the - reconfiguration was impossible; bug appeared in 0.1.1. + reconfiguration was impossible; the bug had appeared in 0.1.1. *) Bugfix: proxy module may get caught in an endless loop when sendfile is not used. *) Bugfix: with sendfile the response was not recoded according to the - charset module directives; bug appeared in 0.1.1. + charset module directives; the bug had appeared in 0.1.1. *) Bugfix: very seldom bug in the kqueue processing. diff -Nru nginx-0.5.33/CHANGES.ru nginx-0.8.53/CHANGES.ru --- nginx-0.5.33/CHANGES.ru 2007-11-07 14:32:56.000000000 +0000 +++ nginx-0.8.53/CHANGES.ru 2010-10-18 12:03:31.000000000 +0000 @@ -1,75 +1,2339 @@ -éÚÍÅÎÅÎÉÑ × nginx 0.5.33 07.11.2007 +éÚÍÅÎÅÎÉÑ × 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. + óÐÁÓÉÂÏ íÁËÓÉÍÕ äÕÎÉÎÕ. + + *) éÓÐÒÁ×ÌÅÎÉÅ: ÐÒÉ ÐÒÏËÓÉÒÏ×ÁÎÉÉ SMPT 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. - *) éÚÍÅÎÅÎÉÅ: ÐÏÞÔÏ×ÙÊ ÐÒÏËÓÉ-ÓÅÒ×ÅÒ ÒÁÚÄẠ̊ΠÎÁ ÔÒÉ ÍÏÄÕÌÑ: 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. + *) äÏÂÁ×ÌÅÎÉÅ: ÄÉÒÅËÔÉ×Õ access_log ÍÏÖÎÏ ÉÓÐÏÌØÚÏ×ÁÔØ ×ÎÕÔÒÉ ÂÌÏËÁ + limit_except. - *) äÏÂÁ×ÌÅÎÉÅ: ÄÉÒÅËÔÉ×Ù server_name É valid_referers ÐÏÄÄÅÒÖÉ×ÁÀÔ - ÒÅÇÕÌÑÒÎÙÅ ×ÙÒÁÖÅÎÉÑ. + *) éÓÐÒÁ×ÌÅÎÉÅ: ÅÓÌÉ ×ÓÅ ÓÅÒ×ÅÒÁ ÁÐÓÔÒÉÍÁ ÏËÁÚÙ×ÁÌÉÓØ ÎÅÄÏÓÔÕÐÎÙÍÉ, ÔÏ + ÄÏ ×ÏÓÓÔÁÎÏ×ÌÅÎÉÑ ÒÁÂÏÔÏÓÐÏÓÏÂÎÏÓÔÉ Õ ×ÓÅÈ ÓÅÒ×ÅÒÏ× ×ÅÓ ÓÔÁÎÏ×ÉÌÓÑ + ÒÁ×ÎÙÍ ÏÄÎÏÍÕ; ÏÛÉÂËÁ ÐÏÑ×ÉÌÁÓØ × 0.6.6. - *) äÏÂÁ×ÌÅÎÉÅ: ÄÉÒÅËÔÉ×Ù "server_name", "map", and "valid_referers" - ÐÏÄÄÅÒÖÉ×ÁÀÔ ÍÁÓËÉ ×ÉÄÁ "www.example.*". + *) éÓÐÒÁ×ÌÅÎÉÅ: ÐÒÉ ÉÓÐÏÌØÚÏ×ÁÎÉÉ ÐÅÒÅÍÅÎÎÙÈ $date_local É $date_gmt + ×ÎÅ ÍÏÄÕÌÑ ngx_http_ssi_filter_module × ÒÁÂÏÞÅÍ ÐÒÏÃÅÓÓÅ ÐÒÏÉÓÈÏÄÉÌ + segmentation fault. - *) éÓÐÒÁ×ÌÅÎÉÅ: sub_filter ÎÅ ÒÁÂÏÔÁÌ Ó ÐÕÓÔÏÊ ÓÔÒÏËÏÊ ÚÁÍÅÎÙ. + *) éÓÐÒÁ×ÌÅÎÉÅ: ÐÒÉ ÉÓÐÏÌØÚÏ×ÁÎÉÉ ×ËÌÀÞ£ÎÎÏÍ ÏÔÌÁÄÏÞÎÏÍ ÌÏÇÅ × ÒÁÂÏÞÅÍ + ÐÒÏÃÅÓÓÅ ÍÏÇ ÐÒÏÉÚÏÊÔÉ segmentation fault. + óÐÁÓÉÂÏ áÎÄÒÅÀ îÉÇÍÁÔÕÌÉÎÕ. - *) éÓÐÒÁ×ÌÅÎÉÅ: × ÐÁÒÓÉÎÇÅ sub_filter. + *) éÓÐÒÁ×ÌÅÎÉÅ: ngx_http_memcached_module ÎÅ ÕÓÔÁÎÁ×ÌÉ×ÁÌ + $upstream_response_time. + óÐÁÓÉÂÏ íÁËÓÉÍÕ äÕÎÉÎÕ. *) éÓÐÒÁ×ÌÅÎÉÅ: ÒÁÂÏÞÉÊ ÐÒÏÃÅÓÓ ÍÏÇ ÚÁÃÉËÌÉÔØÓÑ ÐÒÉ ÉÓÐÏÌØÚÏ×ÁÎÉÉ memcached. *) éÓÐÒÁ×ÌÅÎÉÅ: nginx ÒÁÓÐÏÚÎÁ×ÁÌ ÐÁÒÁÍÅÔÒÙ "close" É "keep-alive" × ÓÔÒÏËÅ "Connection" × ÚÁÇÏÌÏ×ËÅ ÚÁÐÒÏÓÁ ÔÏÌØËÏ, ÅÓÌÉ ÏÎÉ ÂÙÌÉ × - ÎÉÖÎÅÍ ÒÅÇÉÓÔÒÅ; ÏÛÉÂËÁ ÐÏÑ×ÉÌÁÓØ × 0.5.32. + ÎÉÖÎÅÍ ÒÅÇÉÓÔÒÅ; ÏÛÉÂËÁ ÐÏÑ×ÉÌÁÓØ × 0.6.11. - *) éÓÐÒÁ×ÌÅÎÉÅ: ÐÒÉ ÉÓÐÏÌØÚÏ×ÁÎÉÉ ÒÁÚÄÅÌÑÅÍÏÊ ÂÉÂÌÉÏÔÅËÉ PCRE, - ÒÁÓÐÏÌÏÖÅÎÎÏÊ × ÎÅÓÔÁÎÄÁÒÔÎÏÍ ÍÅÓÔÅ, nginx ÎÅ ÚÁÐÕÓËÁÌÓÑ ÎÁ Solaris. + *) éÓÐÒÁ×ÌÅÎÉÅ: sub_filter ÎÅ ÒÁÂÏÔÁÌ Ó ÐÕÓÔÏÊ ÓÔÒÏËÏÊ ÚÁÍÅÎÙ. + *) éÓÐÒÁ×ÌÅÎÉÅ: × ÐÁÒÓÉÎÇÅ sub_filter. -éÚÍÅÎÅÎÉÑ × nginx 0.5.32 24.09.2007 - *) éÚÍÅÎÅÎÉÅ: ÔÅÐÅÒØ nginx ÐÙÔÁÅÔÓÑ ÕÓÔÁÎÏ×ÉÔØ ÄÉÒÅËÔÉ×Ù - worker_priority, worker_rlimit_nofile, worker_rlimit_core, - worker_rlimit_sigpending ÂÅÚ ÐÒÉ×ÉÌÅÇÉÊ root'Á. +éÚÍÅÎÅÎÉÑ × nginx 0.6.13 24.09.2007 - *) éÚÍÅÎÅÎÉÅ: ÔÅÐÅÒØ nginx ÜËÒÁÎÉÒÕÅÔ ÓÉÍ×ÏÌÙ ÐÒÏÂÅÌÁ É "%" ÐÒÉ - ÐÅÒÅÄÁÞÅ ÚÁÐÒÏÓÁ ÓÅÒ×ÅÒÕ ÁÕÔÅÎÔÉÆÉËÁÃÉÉ ÐÏÞÔÏ×ÏÇÏ ÐÒÏËÓÉ-ÓÅÒ×ÅÒÁ. + *) éÓÐÒÁ×ÌÅÎÉÅ: nginx ÎÅ ÚÁËÒÙ×ÁÌ ÆÁÊÌ ËÁÔÁÌÏÇÁ ÄÌÑ ÚÁÐÒÏÓÁ HEAD, ÅÓÌÉ + ÉÓÐÏÌØÚÏ×ÁÌÓÑ autoindex + óÐÁÓÉÂÏ Arkadiusz Patyk. - *) éÚÍÅÎÅÎÉÅ: ÔÅÐÅÒØ nginx ÜËÒÁÎÉÒÕÅÔ ÓÉÍ×ÏÌ "%" × ÐÅÒÅÍÅÎÎÏÊ - $memcached_key. - *) éÚÍÅÎÅÎÉÅ: ÄÌÑ ÏÂÎÏ×ÌÅÎÉÑ ÎÁ ÌÅÔÕ ×ÅÒÓÉÊ 0.1.x ÓÏÚÄÁÎ ÓÐÅÃÉÁÌØÎÙÊ - ÓÃÅÎÁÒÉÊ make upgrade1. +éÚÍÅÎÅÎÉÑ × nginx 0.6.12 21.09.2007 - *) äÏÂÁ×ÌÅÎÉÅ: ÄÉÒÅËÔÉ×Á "add_header Last-Modified ..." ÍÅÎÑÅÔ ÓÔÒÏËÕ - "Last-Modified" × ÚÁÇÏÌÏ×ËÅ ÏÔ×ÅÔÁ. + *) éÚÍÅÎÅÎÉÅ: ÐÏÞÔÏ×ÙÊ ÐÒÏËÓÉ-ÓÅÒ×ÅÒ ÒÁÚÄẠ̊ΠÎÁ ÔÒÉ ÍÏÄÕÌÑ: pop3, imap + É smtp. - *) äÏÂÁ×ÌÅÎÉÅ: ÐÏÞÔÏ×ÙÊ ÐÒÏËÓÉ-ÓÅÒ×ÅÒ ÐÏÄÄÅÒÖÉ×ÁÅÔ AUTHENTICATE × - ÒÅÖÉÍÅ IMAP. - óÐÁÓÉÂÏ íÁËÓÉÍÕ äÕÎÉÎÕ. + *) äÏÂÁ×ÌÅÎÉÅ: ÐÁÒÁÍÅÔÒÙ ËÏÎÆÉÇÕÒÁÃÉÉ --without-mail_pop3_module, + --without-mail_imap_module É --without-mail_smtp_module. - *) äÏÂÁ×ÌÅÎÉÅ: ÐÏÞÔÏ×ÙÊ ÐÒÏËÓÉ-ÓÅÒ×ÅÒ ÐÏÄÄÅÒÖÉ×ÁÅÔ STARTTLS × ÒÅÖÉÍÅ - SMTP. - óÐÁÓÉÂÏ íÁËÓÉÍÕ äÕÎÉÎÕ. + *) äÏÂÁ×ÌÅÎÉÅ: ÄÉÒÅËÔÉ×Ù smtp_greeting_delay É smtp_client_buffer + ÍÏÄÕÌÑ ngx_mail_smtp_module. - *) éÓÐÒÁ×ÌÅÎÉÅ: nginx ÎÅ ÚÁËÒÙ×ÁÌ ÆÁÊÌ ËÁÔÁÌÏÇÁ ÄÌÑ ÚÁÐÒÏÓÁ HEAD, ÅÓÌÉ - ÉÓÐÏÌØÚÏ×ÁÌÓÑ autoindex - óÐÁÓÉÂÏ Arkadiusz Patyk. + *) éÓÐÒÁ×ÌÅÎÉÅ: wildcard × ËÏÎÃÅ ÉÍÅÎÉ ÓÅÒ×ÅÒÁ ÎÅ ÒÁÂÏÔÁÌÉ; ÏÛÉÂËÁ + ÐÏÑ×ÉÌÁÓØ × 0.6.9. + + *) éÓÐÒÁ×ÌÅÎÉÅ: ÐÒÉ ÉÓÐÏÌØÚÏ×ÁÎÉÉ ÒÁÚÄÅÌÑÅÍÏÊ ÂÉÂÌÉÏÔÅËÉ PCRE, + ÒÁÓÐÏÌÏÖÅÎÎÏÊ × ÎÅÓÔÁÎÄÁÒÔÎÏÍ ÍÅÓÔÅ, nginx ÎÅ ÚÁÐÕÓËÁÌÓÑ ÎÁ Solaris. *) éÓÐÒÁ×ÌÅÎÉÅ: ÄÉÒÅËÔÉ×Ù proxy_hide_header É fastcgi_hide_header ÎÅ ÓËÒÙ×ÁÌÉ ÓÔÒÏËÉ ÚÁÇÏÌÏ×ËÁ ÏÔ×ÅÔÁ Ó ÉÍÅÎÅÍ ÂÏÌØÛÅ 32 ÓÉÍ×ÏÌÏ×. óÐÁÓÉÂÏ Manlio Perillo. + +éÚÍÅÎÅÎÉÑ × nginx 0.6.11 11.09.2007 + *) éÓÐÒÁ×ÌÅÎÉÅ: ÓÞ£ÔÞÉË ÁËÔÉ×ÎÙÈ ÓÏÅÄÉÎÅÎÉÊ ×ÓÅÇÄÁ ÒÏÓ ÐÒÉ ÉÓÐÏÌØÚÏ×ÁÎÉÉ ÐÏÞÔÏ×ÏÇÏ ÐÒÏËÓÉ-ÓÅÒ×ÅÒÁ. @@ -80,6 +2344,18 @@ *) éÓÐÒÁ×ÌÅÎÉÅ: 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. @@ -87,8 +2363,69 @@ *) éÓÐÒÁ×ÌÅÎÉÅ: ÐÒÉ ÉÓÐÏÌØÚÏ×ÁÎÉÉ ÍÅÔÏÄÁ /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. @@ -96,70 +2433,83 @@ listen backlog ÎÁ ×ÓÅÈ ÐÌÁÔÆÏÒÍÁÈ, ËÒÏÍÅ FreeBSD. óÐÁÓÉÂÏ Jiang Hong. + *) éÓÐÒÁ×ÌÅÎÉÅ: ÒÁÂÏÞÉÊ ÐÒÏÃÅÓÓ ÍÏÇ ÚÁÃÉËÌÉÔØÓÑ, ÅÓÌÉ server × ÂÌÏËÅ + upstream ÂÙÌ ÐÏÍÅÞÅÎ ËÁË down; ÏÛÉÂËÁ ÐÏÑ×ÉÌÁÓØ × 0.6.6. + *) éÓÐÒÁ×ÌÅÎÉÅ: sendfilev() × Solaris ÔÅÐÅÒØ ÎÅ ÉÓÐÏÌØÚÕÅÔÓÑ ÐÒÉ ÐÅÒÅÄÁÞÅ ÔÅÌÁ ÚÁÐÒÏÓÁ FastCGI-ÓÅÒ×ÅÒÕ ÞÅÒÅÚ unix domain ÓÏËÅÔ. - *) éÓÐÒÁ×ÌÅÎÉÅ: ÐÒÉ ÉÓÐÏÌØÚÏ×ÁÎÉÉ ÏÄÎÏÇÏ ÈÏÓÔÁ × ËÁÞÅÓÔ×Å ÂÜËÅÎÄÏ× ÄÌÑ - ÐÒÏÔÏËÏÌÏ× HTTP É HTTPS ÂÅÚ Ñ×ÎÏÇÏ ÕËÁÚÁÎÉÑ ÐÏÒÔÏ×, nginx - ÉÓÐÏÌØÚÏ×ÁÌ ÔÏÌØËÏ ÏÄÉÎ ÐÏÒÔ - 80 ÉÌÉ 443. - - *) éÓÐÒÁ×ÌÅÎÉÅ: ÄÉÒÅËÔÉ×Ù proxy_ignore_client_abort É - fastcgi_ignore_client_abort ÎÅ ÒÁÂÏÔÁÌÉ; ÏÛÉÂËÁ ÐÏÑ×ÉÌÁÓØ × 0.5.13. +éÚÍÅÎÅÎÉÑ × nginx 0.6.6 30.07.2007 -éÚÍÅÎÅÎÉÑ × nginx 0.5.31 15.08.2007 + *) äÏÂÁ×ÌÅÎÉÅ: ÐÁÒÁÍÅÔÒ --sysconfdir=PATH × configure. *) äÏÂÁ×ÌÅÎÉÅ: ÉÍÅÎÏ×ÁÎÎÙÅ location'Ù. - *) äÏÂÁ×ÌÅÎÉÅ: ÄÉÒÅËÔÉ×Ù proxy_store É fastcgi_store. - - *) äÏÂÁ×ÌÅÎÉÅ: ÄÉÒÅËÔÉ×Ù proxy_store_access É fastcgi_store_access. - - -éÚÍÅÎÅÎÉÑ × nginx 0.5.30 30.07.2007 - *) äÏÂÁ×ÌÅÎÉÅ: ÐÅÒÅÍÅÎÎÕÀ $args ÍÏÖÎÏ ÕÓÔÁÎÁ×ÌÉ×ÁÔØ Ó ÐÏÍÏÝØÀ set. *) äÏÂÁ×ÌÅÎÉÅ: ÐÅÒÅÍÅÎÎÁÑ $is_args. + *) éÓÐÒÁ×ÌÅÎÉÅ: ÒÁ×ÎÏÍÅÒÎÏÅ ÒÁÓÐÒÅÄÅÌÅÎÉÅ ÚÁÐÒÏÓÏ× Ë ÁÐÓÔÒÉÍÁÍ Ó + ÂÏÌØÛÉÍÉ ×ÅÓÁÍÉ. + *) éÓÐÒÁ×ÌÅÎÉÅ: ÅÓÌÉ ËÌÉÅÎÔ × ÐÏÞÔÏ×ÏÍ ÐÒÏËÓÉ-ÓÅÒ×ÅÒÅ ÚÁËÒÙ×ÁÌ ÓÏÅÄÉÎÅÎÉÅ, ÔÏ nginx ÍÏÇ ÎÅ ÚÁËÒÙ×ÁÔØ ÓÏÅÄÉÎÅÎÉÅ Ó ÂÜËÅÎÄÏÍ. - *) éÓÐÒÁ×ÌÅÎÉÅ: ÔÅÐÅÒØ nginx ÜËÒÁÎÉÒÕÅÔ ÐÒÏÂÅÌ × ÐÅÒÅÍÅÎÎÏÊ - $memcached_key. - - *) éÓÐÒÁ×ÌÅÎÉÅ: ÐÒÉ ÉÓÐÏÌØÚÏ×ÁÎÉÉ ÐÒÏÔÏËÏÌÁ HTTPS × ÄÉÒÅËÔÉ×Å - proxy_pass × ÒÁÂÏÞÅÍ ÐÒÏÃÅÓÓÅ ÍÏÇ ÐÒÏÉÚÏÊÔÉ segmentation fault. - - *) éÓÐÒÁ×ÌÅÎÉÅ: ÚÎÁÞÅÎÉÅ perl'Ï×ÏÊ ÐÅÒÅÍÅÎÎÏÊ $$ ÍÏÄÕÌÑ - ngx_http_perl_module ÂÙÌÏ ÒÁ×ÎÏ ÎÏÍÅÒÕ ÇÌÁ×ÎÏÇÏ ÐÒÏÃÅÓÓÁ. + *) éÓÐÒÁ×ÌÅÎÉÅ: ÐÒÉ ÉÓÐÏÌØÚÏ×ÁÎÉÉ ÏÄÎÏÇÏ ÈÏÓÔÁ × ËÁÞÅÓÔ×Å ÂÜËÅÎÄÏ× ÄÌÑ + ÐÒÏÔÏËÏÌÏ× HTTP É HTTPS ÂÅÚ Ñ×ÎÏÇÏ ÕËÁÚÁÎÉÑ ÐÏÒÔÏ×, nginx + ÉÓÐÏÌØÚÏ×ÁÌ ÔÏÌØËÏ ÏÄÉÎ ÐÏÒÔ - 80 ÉÌÉ 443. *) éÓÐÒÁ×ÌÅÎÉÅ: nginx ÎÅ ÓÏÂÉÒÁÌÓÑ ÎÁ Solaris/amd64 Sun Studio 11 É - ÂÏÌÅÅ ÒÁÎÎÉÍÉ ×ÅÒÓÉÑÍÉ; ÏÛÉÂËÁ ÐÏÑ×ÉÌÁÓØ × 0.5.29. + ÂÏÌÅÅ ÒÁÎÎÉÍÉ ×ÅÒÓÉÑÍÉ; ÏÛÉÂËÁ ÐÏÑ×ÉÌÁÓØ × 0.6.4. -éÚÍÅÎÅÎÉÑ × nginx 0.5.29 23.07.2007 +éÚÍÅÎÅÎÉÑ × nginx 0.6.5 23.07.2007 *) äÏÂÁ×ÌÅÎÉÅ: ÐÅÒÅÍÅÎÎÁÑ $nginx_version. óÐÁÓÉÂÏ îÉËÏÌÁÀ çÒÅÞÕÈÕ. - *) éÓÐÒÁ×ÌÅÎÉÅ: ÅÓÌÉ ÚÁÇÏÌÏ×ÏË ÏÔ×ÅÔÁ ÂÙÌ ÒÁÚÄÅÌ£Î × FastCGI-ÚÁÐÉÓÑÈ, - ÔÏ nginx ÐÅÒÅÄÁ×ÁÌ ËÌÉÅÎÔÕ ÍÕÓÏÒ × ÔÁËÉÈ ÚÁÇÏÌÏ×ËÁÈ. + *) äÏÂÁ×ÌÅÎÉÅ: ÐÏÞÔÏ×ÙÊ ÐÒÏËÓÉ-ÓÅÒ×ÅÒ ÐÏÄÄÅÒÖÉ×ÁÅÔ AUTHENTICATE × + ÒÅÖÉÍÅ IMAP. + óÐÁÓÉÂÏ íÁËÓÉÍÕ äÕÎÉÎÕ. - *) éÓÐÒÁ×ÌÅÎÉÅ: ÓÏ×ÍÅÓÔÉÍÏÓÔØ Ó Sun Studio ÎÁ Solaris/amd64 É - Solaris/sparc64. - óÐÁÓÉÂÏ Jiang Hong É áÎÄÒÅÀ îÉÇÍÁÔÕÌÉÎÕ. + *) äÏÂÁ×ÌÅÎÉÅ: ÐÏÞÔÏ×ÙÊ ÐÒÏËÓÉ-ÓÅÒ×ÅÒ ÐÏÄÄÅÒÖÉ×ÁÅÔ STARTTLS × ÒÅÖÉÍÅ + SMTP. + óÐÁÓÉÂÏ íÁËÓÉÍÕ äÕÎÉÎÕ. + + *) éÓÐÒÁ×ÌÅÎÉÅ: ÔÅÐÅÒØ nginx ÜËÒÁÎÉÒÕÅÔ ÐÒÏÂÅÌ × ÐÅÒÅÍÅÎÎÏÊ + $memcached_key. + + *) éÓÐÒÁ×ÌÅÎÉÅ: nginx ÎÅÐÒÁ×ÉÌØÎÏ ÓÏÂÉÒÁÌÓÑ Sun Studio ÎÁ + Solaris/amd64. + óÐÁÓÉÂÏ Jiang Hong. *) éÓÐÒÁ×ÌÅÎÉÅ: ÎÅÚÎÁÞÉÔÅÌØÎÙÈ ÐÏÔÅÎÃÉÁÌØÎÙÈ ÏÛÉÂÏË. óÐÁÓÉÂÏ Coverity's Scan. -éÚÍÅÎÅÎÉÑ × nginx 0.5.28 17.07.2007 +éÚÍÅÎÅÎÉÑ × 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. óÐÁÓÉÂÏ íÁËÓÉÍÕ äÕÎÉÎÕ. @@ -167,11 +2517,25 @@ *) éÓÐÒÁ×ÌÅÎÉÅ: ÅÓÌÉ ÉÓÐÏÌØÚÏ×ÁÌÓÑ ÍÅÔÏÄ ÁÕÔÅÎÔÉÆÉËÁÃÉÉ 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.5.27 09.07.2007 +éÚÍÅÎÅÎÉÑ × nginx 0.6.1 17.06.2007 + + *) éÓÐÒÁ×ÌÅÎÉÅ: × ÐÁÒÓÉÎÇÅ SSI. *) éÓÐÒÁ×ÌÅÎÉÅ: ÐÒÉ ÉÓÐÏÌØÚÏ×ÁÎÉÉ ÕÄÁÌ£ÎÎÏÇÏ ÐÏÄÚÁÐÒÏÓÁ × SSI ÐÏÓÌÅÄÕÀÝÉÊ ÐÏÄÚÁÐÒÏÓ ÌÏËÁÌØÎÏÇÏ ÆÁÊÌÁ ÍÏÇ ÏÔÄÁ×ÁÔØÓÑ ËÌÉÅÎÔÕ × @@ -180,10 +2544,14 @@ *) éÓÐÒÁ×ÌÅÎÉÅ: ÂÏÌØÛÉÅ ×ËÌÀÞÅÎÉÑ × SSI, ÓÏÈÒÁΣÎÎÙÅ ×Ï ×ÒÅÍÅÎÎÙÅ ÆÁÊÌÙ, ÐÅÒÅÄÁ×ÁÌÉÓØ ÎÅ ÐÏÌÎÏÓÔØÀ. + *) éÓÐÒÁ×ÌÅÎÉÅ: ÚÎÁÞÅÎÉÅ perl'Ï×ÏÊ ÐÅÒÅÍÅÎÎÏÊ $$ ÍÏÄÕÌÑ + ngx_http_perl_module ÂÙÌÏ ÒÁ×ÎÏ ÎÏÍÅÒÕ ÇÌÁ×ÎÏÇÏ ÐÒÏÃÅÓÓÁ. + -éÚÍÅÎÅÎÉÑ × nginx 0.5.26 17.06.2007 +éÚÍÅÎÅÎÉÑ × nginx 0.6.0 14.06.2007 - *) éÓÐÒÁ×ÌÅÎÉÅ: × ÐÁÒÓÉÎÇÅ SSI. + *) äÏÂÁ×ÌÅÎÉÅ: ÄÉÒÅËÔÉ×Ù "server_name", "map", and "valid_referers" + ÐÏÄÄÅÒÖÉ×ÁÀÔ ÍÁÓËÉ ×ÉÄÁ "www.example.*". éÚÍÅÎÅÎÉÑ × nginx 0.5.25 11.06.2007 @@ -955,7 +3323,7 @@ ÚÁËÏÄÉÒÏ×ÁÎÎÙÅ ÓÉÍ×ÏÌÙ × ×ÉÄÅ "%XX", ÔÏ ÐÒÏËÓÉÒÕÅÍÙÊ ÚÁÐÒÏÓ ÐÅÒÅÄÁ×ÁÌÓÑ ÎÅÚÁËÏÄÉÒÏ×ÁÎÎÙÍ. - *) éÓÐÒÁ×ÌÅÎÉÅ: ÍÅÔÏÄ $r->headers_in("Cookie") ÍÏÄÕÌÑ + *) éÓÐÒÁ×ÌÅÎÉÅ: ÍÅÔÏÄ $r->header_in("Cookie") ÍÏÄÕÌÑ ngx_http_perl_module ÔÅÐÅÒØ ×ÏÚ×ÒÁÝÁÅÔ ×ÓÅ ÓÔÒÏËÉ "Cookie" × ÚÁÇÏÌÏ×ËÅ ÚÁÐÒÏÓÁ. diff -Nru nginx-0.5.33/conf/fastcgi.conf nginx-0.8.53/conf/fastcgi.conf --- nginx-0.5.33/conf/fastcgi.conf 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/conf/fastcgi.conf 2009-12-15 13:54:41.000000000 +0000 @@ -0,0 +1,24 @@ + +fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; +fastcgi_param QUERY_STRING $query_string; +fastcgi_param REQUEST_METHOD $request_method; +fastcgi_param CONTENT_TYPE $content_type; +fastcgi_param CONTENT_LENGTH $content_length; + +fastcgi_param SCRIPT_NAME $fastcgi_script_name; +fastcgi_param REQUEST_URI $request_uri; +fastcgi_param DOCUMENT_URI $document_uri; +fastcgi_param DOCUMENT_ROOT $document_root; +fastcgi_param SERVER_PROTOCOL $server_protocol; + +fastcgi_param GATEWAY_INTERFACE CGI/1.1; +fastcgi_param SERVER_SOFTWARE nginx/$nginx_version; + +fastcgi_param REMOTE_ADDR $remote_addr; +fastcgi_param REMOTE_PORT $remote_port; +fastcgi_param SERVER_ADDR $server_addr; +fastcgi_param SERVER_PORT $server_port; +fastcgi_param SERVER_NAME $server_name; + +# PHP only, required if PHP was built with --enable-force-cgi-redirect +fastcgi_param REDIRECT_STATUS 200; diff -Nru nginx-0.5.33/conf/mime.types nginx-0.8.53/conf/mime.types --- nginx-0.5.33/conf/mime.types 2007-08-26 13:42:04.000000000 +0000 +++ nginx-0.8.53/conf/mime.types 2010-03-03 16:37:57.000000000 +0000 @@ -2,11 +2,12 @@ types { text/html html htm shtml; text/css css; - text/xml xml rss; + text/xml xml; image/gif gif; image/jpeg jpeg jpg; application/x-javascript js; application/atom+xml atom; + application/rss+xml rss; text/mathml mml; text/plain txt; @@ -32,6 +33,9 @@ application/vnd.ms-powerpoint ppt; application/vnd.wap.wmlc wmlc; application/vnd.wap.xhtml+xml xhtml; + application/vnd.google-earth.kml+xml kml; + application/vnd.google-earth.kmz kmz; + application/x-7z-compressed 7z; application/x-cocoa cco; application/x-java-archive-diff jardiff; application/x-java-jnlp-file jnlp; diff -Nru nginx-0.5.33/conf/nginx.conf nginx-0.8.53/conf/nginx.conf --- nginx-0.5.33/conf/nginx.conf 2007-01-18 07:08:18.000000000 +0000 +++ nginx-0.8.53/conf/nginx.conf 2009-04-06 13:43:46.000000000 +0000 @@ -15,11 +15,11 @@ http { - include conf/mime.types; + include mime.types; default_type application/octet-stream; - #log_format main '$remote_addr - $remote_user [$time_local] $request ' - # '"$status" $body_bytes_sent "$http_referer" ' + #log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + # '$status $body_bytes_sent "$http_referer" ' # '"$http_user_agent" "$http_x_forwarded_for"'; #access_log logs/access.log main; @@ -63,10 +63,11 @@ # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 # #location ~ \.php$ { + # root html; # fastcgi_pass 127.0.0.1:9000; # fastcgi_index index.php; # fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name; - # include conf/fastcgi_params; + # include fastcgi_params; #} # deny access to .htaccess files, if Apache's document root diff -Nru nginx-0.5.33/conf/scgi_params nginx-0.8.53/conf/scgi_params --- nginx-0.5.33/conf/scgi_params 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/conf/scgi_params 2010-06-18 15:51:14.000000000 +0000 @@ -0,0 +1,15 @@ + +scgi_param REQUEST_METHOD $request_method; +scgi_param REQUEST_URI $request_uri; +scgi_param QUERY_STRING $query_string; +scgi_param CONTENT_TYPE $content_type; + +scgi_param DOCUMENT_URI $document_uri; +scgi_param DOCUMENT_ROOT $document_root; +scgi_param SCGI 1; +scgi_param SERVER_PROTOCOL $server_protocol; + +scgi_param REMOTE_ADDR $remote_addr; +scgi_param REMOTE_PORT $remote_port; +scgi_param SERVER_PORT $server_port; +scgi_param SERVER_NAME $server_name; diff -Nru nginx-0.5.33/conf/uwsgi_params nginx-0.8.53/conf/uwsgi_params --- nginx-0.5.33/conf/uwsgi_params 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/conf/uwsgi_params 2010-06-01 15:55:04.000000000 +0000 @@ -0,0 +1,15 @@ + +uwsgi_param QUERY_STRING $query_string; +uwsgi_param REQUEST_METHOD $request_method; +uwsgi_param CONTENT_TYPE $content_type; +uwsgi_param CONTENT_LENGTH $content_length; + +uwsgi_param REQUEST_URI $request_uri; +uwsgi_param PATH_INFO $document_uri; +uwsgi_param DOCUMENT_ROOT $document_root; +uwsgi_param SERVER_PROTOCOL $server_protocol; + +uwsgi_param REMOTE_ADDR $remote_addr; +uwsgi_param REMOTE_PORT $remote_port; +uwsgi_param SERVER_PORT $server_port; +uwsgi_param SERVER_NAME $server_name; diff -Nru nginx-0.5.33/configure nginx-0.8.53/configure --- nginx-0.5.33/configure 2006-12-23 20:31:14.000000000 +0000 +++ nginx-0.8.53/configure 2010-06-18 15:51:14.000000000 +0000 @@ -3,8 +3,6 @@ # Copyright (C) Igor Sysoev -NGX_CONFIGURE=`echo $@ | sed 's/"/\\\\"/g'` - . auto/options . auto/init . auto/sources @@ -33,6 +31,12 @@ NGX_PLATFORM="$NGX_SYSTEM:$NGX_RELEASE:$NGX_MACHINE"; + case "$NGX_SYSTEM" in + MINGW32_*) + NGX_PLATFORM=win32 + ;; + esac + else echo "building for $NGX_PLATFORM" NGX_SYSTEM=$NGX_PLATFORM @@ -54,25 +58,30 @@ . auto/modules . auto/lib/conf -. auto/make -. auto/lib/make -. auto/install +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_PLATFORM" != win32 ]; then - . auto/unix +if [ ".$NGX_CONF_PREFIX" != "." ]; then + have=NGX_CONF_PREFIX value="\"$NGX_CONF_PREFIX/\"" . auto/define fi -# STUB -. auto/stubs - -have=NGX_PREFIX value="\"$NGX_PREFIX/\"" . auto/define 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 -if test -n "$NGX_ERROR_LOG_PATH"; then - have=NGX_ERROR_LOG_PATH value="\"$NGX_ERROR_LOG_PATH\"" . auto/define -fi +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\"" @@ -81,6 +90,21 @@ . 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 + +if [ "$NGX_PLATFORM" != win32 ]; then + . auto/unix +fi + +# STUB +. auto/stubs have=NGX_USER value="\"$NGX_USER\"" . auto/define have=NGX_GROUP value="\"$NGX_GROUP\"" . auto/define diff -Nru nginx-0.5.33/debian/changelog nginx-0.8.53/debian/changelog --- nginx-0.5.33/debian/changelog 2011-02-26 14:04:40.000000000 +0000 +++ nginx-0.8.53/debian/changelog 2011-02-26 14:04:40.000000000 +0000 @@ -1,3 +1,446 @@ +nginx (0.8.53-2~basak1) hardy; urgency=low + + * Backported to Hardy + + -- Robie Basak Sat, 26 Feb 2011 11:10:34 +0000 + +nginx (0.8.53-2) unstable; urgency=low + + [Kartik Mistry] + * debian/conf/mime.types: + + Added html5 codecs support (Closes: #605212) + * debian/conf/fastcgi_params: + + Fixed typo (Closes: #605582) + + [Michael Lustfield] + * debian/conf/nginx.conf: + + Reduced worker_connections to 768 (Closes: #605529) + * Add an nginx site management script to nginx-doc. (Closes: #593580) + + debian/help/examples/nginx_modsite: Added. + * debian/nginx.logrotate: + + Added prerotate chunk for awstats. (Closes: #590098) + * debian/copyright: + + Added Michael Lustfield + * Created nginx-doc: + + Added 'Package: nginx-doc' section to debian/control + + Moved debian/nginx.examples -> debian/nginx-doc.examples + + Moved debian/nginx.docs -> debian/nginx-doc.docs + + -- Kartik Mistry Mon, 06 Dec 2010 11:26:22 +0530 + +nginx (0.8.53-1) unstable; urgency=low + + [Kartik Mistry] + * debian/control: + + Added Michael Lustfield as co-maintainer + * nginx.conf: + + No need to use regex in gzip_disable for msie6, Thanks to António P. P. + Almeida (Closes: #592147) + * conf/sites-available/default: + + Fixed typo for "include fastcgi", Thanks to Mostafa Ghadamyari + (Closes: #593142, #593143) + * debian/patches/fix_reloading_ipv6.diff: + + Removed, merged upstream + * debian/init.d: + + Added fix to control nginx by user in a simple way by setting DAEMON + variable to an invalid name in /etc/default/nginx. Patch by Toni Mueller + (Closes: #594598) + * debian/NEWS.Debian: + + Updated news for 0.8.x as stable branch + + [Michael Lustfield] + * New upstream release (Closes: #602970) + + 0.8.x branch is declared stable by upstream now + * Add a UFW profile set: + + debian/nginx.ufw.profile: Added. + + debian/control: nginx: Suggests ufw. + + debian/dirs: Add 'etc/ufw/applications.d' + + debian/rules: Add install rule for the nginx UFW profile. + * Moved debian/dirs to debian/nginx.dirs + * Added types_hash_max_size to nginx.conf + * Install simple default index.html file (Closes: #581416) + + debian/dirs: Add 'usr/share/nginx/www'. + + debian/nginx.install: Add 'html/* usr/share/nginx/www'. + * debian/patches/nginx-echo.diff: + + Added Echo module + * Added files for nginx.docs + - /usr/share/doc/nginx/ + + debian/help/docs/fcgiwrap + + debian/help/docs/php + + debian/help/docs/support-irc + + debian/help/docs/upstream + * Added files for nginx.examples + - /usr/share/doc/nginx/examples/ + + debian/help/docs/drupal + + debian/help/docs/http + + debian/help/docs/mail + + debian/help/docs/mailman + + debian/help/docs/nginx.conf + + debian/help/docs/virtual_hosts + + debian/help/docs/wordpress + * debian/conf/: + + Removed excess spaces + + Added tabs where appropriate + + Added SCRIPT_FILENAME to fastcgi_params + + -- Kartik Mistry Sat, 27 Nov 2010 21:04:02 +0530 + +nginx (0.7.67-3) unstable; urgency=low + + * debian/rules: + + Readded the configure option --with-mail_ssl_module, Thanks to Roland + Rosenfeld (Closes: #590815) + * debian/control: + + Updated Standards-Version to 3.9.1 (no changes needed) + + -- Kartik Mistry Thu, 29 Jul 2010 21:41:52 +0530 + +nginx (0.7.67-2) unstable; urgency=low + + * debian/conf/sites-available/default: + + Removed reference to SSLv2 protocol (Closes: #589139) + * debian/control: + + Updated Standards-Version to 3.9.0 + * debian/copyright: + + Don't point to BSD license file, included exact upstream version of + license text + + Added missing copyright owner for contrib/ scripts + + debian/* license is same as upstream now as discussed with co-maintainers + + -- Kartik Mistry Mon, 19 Jul 2010 10:36:32 +0530 + +nginx (0.7.67-1) unstable; urgency=low + + * New upstream release + + -- Kartik Mistry Wed, 16 Jun 2010 01:26:51 +0530 + +nginx (0.7.65-7) unstable; urgency=low + + [Kartik Mistry] + * debian/rules: + + Enabled HTTPSubModule module in configure (Closes: #584828) + + Arranged configure options in better manner + + -- Kartik Mistry Mon, 07 Jun 2010 14:33:24 +0530 + +nginx (0.7.65-6) unstable; urgency=low + + [Kartik Mistry] + * debian/README.Debian: + + Fixed typo and somewhat better wordings + * debian/conf/mime.types: + + Added entry to support 7zip files (Closes: #580423) + * debian/init.d: + + Do not print config testing info until an error found, Thanks to Ubuntu + bug 568293 + * debian/copyright: + + Updated as per DEP-5 specification + + -- Kartik Mistry Sat, 22 May 2010 01:41:33 +0530 + +nginx (0.7.65-5) unstable; urgency=low + + [Kartik Mistry] + * debian/patches/fix_reloading_ipv6.diff: + + Added patch to fix reloading with IPv6 addresses, Thanks to + Matthias-Christian Ott for patch (Closes: #577456) + + -- Kartik Mistry Wed, 14 Apr 2010 11:36:48 +0530 + +nginx (0.7.65-4) unstable; urgency=low + + [Kartik Mistry] + * debian/conf/sites-available/default: + + Really listen for both IPv4 and IPv6 addresses. Thanks to Nikolaus + Schulz for notice (Closes: #574983) + * debian/control, debian/rules: + + Added GeoIP support, Thanks to Caetano Carezzato + (Closes: #575280) + * debian/conf/mime.types: + + Added svg entry to mime.types, Jeremy Lal + (Closes: #575155) + + -- Kartik Mistry Thu, 25 Mar 2010 00:21:50 +0530 + +nginx (0.7.65-3) unstable; urgency=medium + + [Kartik Mistry] + * Urgency set to medium due to Release Goal + * debian/conf/sites-available/default: + + Listen for both IPv4 and IPv6 addresses by default (Closes: #574983) + + -- Kartik Mistry Tue, 23 Mar 2010 10:30:18 +0530 + +nginx (0.7.65-2) unstable; urgency=low + + * debian/README.Debian: + + Added explanation about not installing files in /var/www/ + (Closes: #572513) + * debian/rules: + + Readded realip module support (Closes: #507419) + + -- Kartik Mistry Sat, 06 Mar 2010 13:14:48 +0530 + +nginx (0.7.65-1) unstable; urgency=low + + [Kartik Mistry] + * New upstream release + * debian/init.d: + + $local_fs $remote_fs $network $syslog is what we need in Required-Start + and Required-Stop (Closes: #568238) + * debian/copyright: + + Updated package copyright year + + Updated license text to make same as upstream license + + -- Kartik Mistry Fri, 05 Feb 2010 11:20:28 +0530 + +nginx (0.7.64-3) unstable; urgency=low + + [Kartik Mistry] + * debian/conf/sites-available/default: + + Added patch to fix default virtual host, Thanks to Thomas Venieris + (Closes: #564726) + * debian/init.d: + + Added dependency on $remote_fs in Required-Start and Required-Stop + (Closes: #566862) + * Converted package to use 3.0 (quilt) source format + * Updated to Standards-Version 3.8.4 + + -- Kartik Mistry Mon, 01 Feb 2010 23:44:21 +0530 + +nginx (0.7.64-2) unstable; urgency=low + + [Kartik Mistry] + * debian/rules: + + Used dh_prep instead of dh_clean -k + * debian/control: + + Added ${misc:Depends} in Depends + * Added patch for adding debug package, Thanks to Matthew Palmer + (Closes: #563339) + * debian/copyright: + + Added missing copyright owners for Debian package and fixed year and + licence doesn't point to versionless symlink + + [Fabio Tranchitella] + * debian/init.d: + + Added patch from Wouter de Bie to add $DAEMON_OPTS in + test_nginx_config() + + -- Kartik Mistry Sat, 09 Jan 2010 11:15:59 +0530 + +nginx (0.7.64-1) unstable; urgency=medium + + [Kartik Mistry] + * Urgency set to medium due to security issue + * New upstream release (Closes: #557602) + + fixes SSL renegotiation vuln CVE-2009-3555 (Closes: #557873) + * debian/nginx.1: + + Corrected homepage entry (Closes: #556617) + + Minor whitespace and empty line cleanups, added SEE ALSO section. Used + .TP instead of .br, Added missing options, and this should + (Closes: #556616) + * debian/copyright: + + Used © instead of deprecated (C) symbol + + Formatted some texts + * debian/control: + + Added myself as uploader + + Wrapped and rearranged Build-Depends for better readability + * debian/patches/dlopen.dpatch: + + Fixed patch name in comment + + Added missing DP comment from changelog + * debian/rules: + + Minor fixes related to formatting of file and whitespaces + * debian/watch: + + Removed comments out of it + + -- Kartik Mistry Fri, 27 Nov 2009 11:10:18 +0530 + +nginx (0.7.63-1) unstable; urgency=low + + * New upstream release. + + -- Fabio Tranchitella Sun, 08 Nov 2009 09:53:46 +0100 + +nginx (0.7.62-4) unstable; urgency=low + + * debian/conf/nginx.conf: commented out the mail proxy example. + (Closes: #551682) + * debian/init.d: do not redirect the output of nginx -t. + (Closes: #551683) + + -- Fabio Tranchitella Thu, 22 Oct 2009 19:50:05 +0200 + +nginx (0.7.62-3) unstable; urgency=low + + * debian/rules: fix the FTBFS on sparc. + + -- Fabio Tranchitella Fri, 16 Oct 2009 06:39:22 +0000 + +nginx (0.7.62-2) unstable; urgency=low + + * debian/rules: added --with-mail and --with-mail_ssl_module. + * debian/patches/nginx-upstream-fair.dpatch: added support for the + nginx-upstream-fair module. (Closes: #521447) + * debian/patches/dlopen.dpatch: applied patch to remove the unnecesssary link + to libdl. (Closes: #540599) + + -- Fabio Tranchitella Thu, 15 Oct 2009 18:32:00 +0000 + +nginx (0.7.62-1) unstable; urgency=low + + * New upstream release. + * debian/rules: added --with-ipv6. (Closes: #547249) + + -- Fabio Tranchitella Sun, 27 Sep 2009 22:25:16 +0200 + +nginx (0.7.61-2) unstable; urgency=low + + * debian/control: bumped Standards-Version to 3.8.3, no changes needed. + * debian/init.d: added status action. (Closes: #541228) + * debian/rules: + - added -with-http_gzip_static_module. (Closes: #541229) + - use the switch --with-cc-opt="-m32 -mcpu=ultrasparc" on sparc. + (Closes: #543571) + + -- Fabio Tranchitella Sat, 29 Aug 2009 17:03:44 +0200 + +nginx (0.7.61-1) unstable; urgency=low + + * New upstream version: + *) Bugfix: nginx could not be built --without-http-cache; the bug had + appeared in 0.7.60. + *) Bugfix: a segmentation fault occurred in worker process, if a + backend 401 error was intercepted and the backend did not set the + "WWW-Authenticate" response header line. + Thanks to Eugene Mychlo. + *) Feature: the "keepalive_requests" directive. + *) Bugfix: in open_file_cache and proxy/fastcgi cache interaction on + start up. + *) Bugfix: open_file_cache might cache open file descriptors too long. + *) Bugfix: XLST filter did not work in subrequests. + * Fix "gzip_disable in default conf for old IE", adding ignore rules for + certain MSIE versions in the default configuration (Closes: #540025) + * Fix "init.d: Config test before allowing a restart" adding a function + and calling it before reloading, restarting and starting (Closes: #539778) + * Drops path for pidof call in postinst script. + + -- Jose Parrella Wed, 05 Aug 2009 14:00:11 -0500 + +nginx (0.7.59-1) unstable; urgency=low + + * New upstream release, first in Debian for the 0.7 branch. Among other + issues, it also fixes the problem with wildcard dns names used with SSL. + (Closes: #515904) + * debian/watch: updated. + * debian/postinst: fixed a bashism. (Closes: #507913) + * debian/conf/nginx.conf: removed default_type. (Closes: #509390) + * debian/control: updated Standards-Version to 3.8.1, no changes needed. + * debian/NEWS.Debian: documented the issues with + server_names_hash_bucket_size. (Closes: #524785) + + -- Fabio Tranchitella Sun, 31 May 2009 18:38:56 +0200 + +nginx (0.7.14-1) experimental; urgency=low + + * New upstream release. + * Adding configtest to the init.d script (Closes: #496279) + * postinst now calls the builtin kill, so tweaks on the specific signal + calls were needed. + * Default HTML files are no longer installed. + * The default configuration file no longer requires a 50x file under + /var/www. + + -- Jose Parrella Tue, 02 Sep 2008 20:48:58 -0430 + +nginx (0.6.34-2) unstable; urgency=low + + * Added support for realip module. (Closes: #507419) + + -- Fabio Tranchitella Sat, 06 Dec 2008 10:34:01 +0100 + +nginx (0.6.34-1) unstable; urgency=low + + * New upstream release. + * debian/rules: removed the --with-debug configure switch. (Closes: #500891) + + -- Fabio Tranchitella Sun, 30 Nov 2008 11:37:13 +0100 + +nginx (0.6.32-3) unstable; urgency=low + + * debian/control: build again on all the architectures, but use the switch + --with-cc-opt="-m32 -mcpu=ultrasparc" on sparch. (Closes: #479185) + + -- Fabio Tranchitella Thu, 25 Sep 2008 17:14:27 +0200 + +nginx (0.6.32-2) unstable; urgency=low + + * debian/control: disabled support for sparc. (Closes: #479185) + + -- Fabio Tranchitella Sat, 20 Sep 2008 11:48:48 +0200 + +nginx (0.6.32-1) unstable; urgency=low + + * New upstream release. + + -- Fabio Tranchitella Fri, 18 Jul 2008 09:15:58 +0200 + +nginx (0.6.31-2) unstable; urgency=low + + * debian/control: removed httpd-cgi, nginx doesn't support executing + external programs. (Closes: #482332) + + -- Fabio Tranchitella Thu, 22 May 2008 08:18:52 +0200 + +nginx (0.6.31-1) unstable; urgency=low + + * New upstream release. + + -- Fabio Tranchitella Mon, 12 May 2008 22:34:55 +0200 + +nginx (0.6.30-2) unstable; urgency=low + + * Upload into unstable. + + -- Fabio Tranchitella Mon, 12 May 2008 14:24:53 +0200 + +nginx (0.6.30-1) experimental; urgency=low + + * New upstream release. + * Sync with the unstable packages. + * debian/conf/nginx.conf: add support for a conf.d directory. + (Closes: #476952) + + -- Fabio Tranchitella Fri, 02 May 2008 09:32:46 +0200 + +nginx (0.5.35-3) unstable; urgency=low + + * debian/conf/sites-available/default: adding support for /doc and /images + namespaces (Closes: #474519) + + -- Jose Parrella Mon, 07 Apr 2008 13:22:32 -0430 + +nginx (0.5.35-2) unstable; urgency=low + + * debian/init.d: do not break if start-stop-daemon exits with an error. + (Closes: #464453) + + -- Fabio Tranchitella Sun, 10 Feb 2008 17:57:57 +0100 + +nginx (0.5.35-1) unstable; urgency=low + + * New upstream release. + + -- Fabio Tranchitella Wed, 09 Jan 2008 07:58:56 +0100 + +nginx (0.5.34-1) unstable; urgency=low + + * New upstream release. + * debian/control: added Homepage, Vcs-Svn, Vcs-Browser fields; updated + Standard-Versions to 3.7.3. + + -- Fabio Tranchitella Mon, 24 Dec 2007 12:26:27 +0100 + nginx (0.5.33-1) unstable; urgency=low * New stable upstream release (Closes: #451173) diff -Nru nginx-0.5.33/debian/conf/fastcgi_params nginx-0.8.53/debian/conf/fastcgi_params --- nginx-0.5.33/debian/conf/fastcgi_params 2011-02-26 14:04:40.000000000 +0000 +++ nginx-0.8.53/debian/conf/fastcgi_params 2011-02-26 14:04:40.000000000 +0000 @@ -1,23 +1,23 @@ +fastcgi_param QUERY_STRING $query_string; +fastcgi_param REQUEST_METHOD $request_method; +fastcgi_param CONTENT_TYPE $content_type; +fastcgi_param CONTENT_LENGTH $content_length; -fastcgi_param QUERY_STRING $query_string; -fastcgi_param REQUEST_METHOD $request_method; -fastcgi_param CONTENT_TYPE $content_type; -fastcgi_param CONTENT_LENGTH $content_length; +fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; +fastcgi_param SCRIPT_NAME $fastcgi_script_name; +fastcgi_param REQUEST_URI $request_uri; +fastcgi_param DOCUMENT_URI $document_uri; +fastcgi_param DOCUMENT_ROOT $document_root; +fastcgi_param SERVER_PROTOCOL $server_protocol; -fastcgi_param SCRIPT_NAME $fastcgi_script_name; -fastcgi_param REQUEST_URI $request_uri; -fastcgi_param DOCUMENT_URI $document_uri; -fastcgi_param DOCUMENT_ROOT $document_root; -fastcgi_param SERVER_PROTOCOL $server_protocol; +fastcgi_param GATEWAY_INTERFACE CGI/1.1; +fastcgi_param SERVER_SOFTWARE nginx/$nginx_version; -fastcgi_param GATEWAY_INTERFACE CGI/1.1; -fastcgi_param SERVER_SOFTWARE nginx/$nginx_version; - -fastcgi_param REMOTE_ADDR $remote_addr; -fastcgi_param REMOTE_PORT $remote_port; -fastcgi_param SERVER_ADDR $server_addr; -fastcgi_param SERVER_PORT $server_port; -fastcgi_param SERVER_NAME $server_name; +fastcgi_param REMOTE_ADDR $remote_addr; +fastcgi_param REMOTE_PORT $remote_port; +fastcgi_param SERVER_ADDR $server_addr; +fastcgi_param SERVER_PORT $server_port; +fastcgi_param SERVER_NAME $server_name; # PHP only, required if PHP was built with --enable-force-cgi-redirect -fastcgi_param REDIRECT_STATUS 200; +fastcgi_param REDIRECT_STATUS 200; diff -Nru nginx-0.5.33/debian/conf/koi-utf nginx-0.8.53/debian/conf/koi-utf --- nginx-0.5.33/debian/conf/koi-utf 2011-02-26 14:04:40.000000000 +0000 +++ nginx-0.8.53/debian/conf/koi-utf 2011-02-26 14:04:40.000000000 +0000 @@ -1,109 +1,108 @@ - # This map is not a full koi8-r <> utf8 map: it does not contain -# box-drawing and some other characters. Besides this map contains +# box-drawing and some other characters. Besides this map contains # several koi8-u and Byelorussian letters which are not in koi8-r. # If you need a full and standard map, use contrib/unicode2nginx/koi-utf # map instead. -charset_map koi8-r utf-8 { +charset_map koi8-r utf-8 { - 80 E282AC ; # euro + 80 E282AC; # euro - 95 E280A2 ; # bullet + 95 E280A2; # bullet - 9A C2A0 ; #   + 9A C2A0; #   - 9E C2B7 ; # · - - A3 D191 ; # small yo - A4 D194 ; # small Ukrainian ye - - A6 D196 ; # small Ukrainian i - A7 D197 ; # small Ukrainian yi - - AD D291 ; # small Ukrainian soft g - AE D19E ; # small Byelorussian short u - - B0 C2B0 ; # ° - - B3 D081 ; # capital YO - B4 D084 ; # capital Ukrainian YE - - B6 D086 ; # capital Ukrainian I - B7 D087 ; # capital Ukrainian YI - - B9 E28496 ; # numero sign - - BD D290 ; # capital Ukrainian soft G - BE D18E ; # capital Byelorussian short U - - BF C2A9 ; # (C) - - C0 D18E ; # small yu - C1 D0B0 ; # small a - C2 D0B1 ; # small b - C3 D186 ; # small ts - C4 D0B4 ; # small d - C5 D0B5 ; # small ye - C6 D184 ; # small f - C7 D0B3 ; # small g - C8 D185 ; # small kh - C9 D0B8 ; # small i - CA D0B9 ; # small j - CB D0BA ; # small k - CC D0BB ; # small l - CD D0BC ; # small m - CE D0BD ; # small n - CF D0BE ; # small o - - D0 D0BF ; # small p - D1 D18F ; # small ya - D2 D180 ; # small r - D3 D181 ; # small s - D4 D182 ; # small t - D5 D183 ; # small u - D6 D0B6 ; # small zh - D7 D0B2 ; # small v - D8 D18C ; # small soft sign - D9 D18B ; # small y - DA D0B7 ; # small z - DB D188 ; # small sh - DC D18D ; # small e - DD D189 ; # small shch - DE D187 ; # small ch - DF D18A ; # small hard sign - - E0 D0AE ; # capital YU - E1 D090 ; # capital A - E2 D091 ; # capital B - E3 D0A6 ; # capital TS - E4 D094 ; # capital D - E5 D095 ; # capital YE - E6 D0A4 ; # capital F - E7 D093 ; # capital G - E8 D0A5 ; # capital KH - E9 D098 ; # capital I - EA D099 ; # capital J - EB D09A ; # capital K - EC D09B ; # capital L - ED D09C ; # capital M - EE D09D ; # capital N - EF D09E ; # capital O - - F0 D09F ; # capital P - F1 D0AF ; # capital YA - F2 D0A0 ; # capital R - F3 D0A1 ; # capital S - F4 D0A2 ; # capital T - F5 D0A3 ; # capital U - F6 D096 ; # capital ZH - F7 D092 ; # capital V - F8 D0AC ; # capital soft sign - F9 D0AB ; # capital Y - FA D097 ; # capital Z - FB D0A8 ; # capital SH - FC D0AD ; # capital E - FD D0A9 ; # capital SHCH - FE D0A7 ; # capital CH - FF D0AA ; # capital hard sign + 9E C2B7; # · + + A3 D191; # small yo + A4 D194; # small Ukrainian ye + + A6 D196; # small Ukrainian i + A7 D197; # small Ukrainian yi + + AD D291; # small Ukrainian soft g + AE D19E; # small Byelorussian short u + + B0 C2B0; # ° + + B3 D081; # capital YO + B4 D084; # capital Ukrainian YE + + B6 D086; # capital Ukrainian I + B7 D087; # capital Ukrainian YI + + B9 E28496; # numero sign + + BD D290; # capital Ukrainian soft G + BE D18E; # capital Byelorussian short U + + BF C2A9; # (C) + + C0 D18E; # small yu + C1 D0B0; # small a + C2 D0B1; # small b + C3 D186; # small ts + C4 D0B4; # small d + C5 D0B5; # small ye + C6 D184; # small f + C7 D0B3; # small g + C8 D185; # small kh + C9 D0B8; # small i + CA D0B9; # small j + CB D0BA; # small k + CC D0BB; # small l + CD D0BC; # small m + CE D0BD; # small n + CF D0BE; # small o + + D0 D0BF; # small p + D1 D18F; # small ya + D2 D180; # small r + D3 D181; # small s + D4 D182; # small t + D5 D183; # small u + D6 D0B6; # small zh + D7 D0B2; # small v + D8 D18C; # small soft sign + D9 D18B; # small y + DA D0B7; # small z + DB D188; # small sh + DC D18D; # small e + DD D189; # small shch + DE D187; # small ch + DF D18A; # small hard sign + + E0 D0AE; # capital YU + E1 D090; # capital A + E2 D091; # capital B + E3 D0A6; # capital TS + E4 D094; # capital D + E5 D095; # capital YE + E6 D0A4; # capital F + E7 D093; # capital G + E8 D0A5; # capital KH + E9 D098; # capital I + EA D099; # capital J + EB D09A; # capital K + EC D09B; # capital L + ED D09C; # capital M + EE D09D; # capital N + EF D09E; # capital O + + F0 D09F; # capital P + F1 D0AF; # capital YA + F2 D0A0; # capital R + F3 D0A1; # capital S + F4 D0A2; # capital T + F5 D0A3; # capital U + F6 D096; # capital ZH + F7 D092; # capital V + F8 D0AC; # capital soft sign + F9 D0AB; # capital Y + FA D097; # capital Z + FB D0A8; # capital SH + FC D0AD; # capital E + FD D0A9; # capital SHCH + FE D0A7; # capital CH + FF D0AA; # capital hard sign } diff -Nru nginx-0.5.33/debian/conf/koi-win nginx-0.8.53/debian/conf/koi-win --- nginx-0.5.33/debian/conf/koi-win 2011-02-26 14:04:40.000000000 +0000 +++ nginx-0.8.53/debian/conf/koi-win 2011-02-26 14:04:40.000000000 +0000 @@ -1,103 +1,102 @@ +charset_map koi8-r windows-1251 { -charset_map koi8-r windows-1251 { + 80 88; # euro - 80 88 ; # euro + 95 95; # bullet - 95 95 ; # bullet + 9A A0; #   - 9A A0 ; #   - - 9E B7 ; # · - - A3 B8 ; # small yo - A4 BA ; # small Ukrainian ye - - A6 B3 ; # small Ukrainian i - A7 BF ; # small Ukrainian yi - - AD B4 ; # small Ukrainian soft g - AE A2 ; # small Byelorussian short u - - B0 B0 ; # ° - - B3 A8 ; # capital YO - B4 AA ; # capital Ukrainian YE - - B6 B2 ; # capital Ukrainian I - B7 AF ; # capital Ukrainian YI - - B9 B9 ; # numero sign - - BD A5 ; # capital Ukrainian soft G - BE A1 ; # capital Byelorussian short U - - BF A9 ; # (C) - - C0 FE ; # small yu - C1 E0 ; # small a - C2 E1 ; # small b - C3 F6 ; # small ts - C4 E4 ; # small d - C5 E5 ; # small ye - C6 F4 ; # small f - C7 E3 ; # small g - C8 F5 ; # small kh - C9 E8 ; # small i - CA E9 ; # small j - CB EA ; # small k - CC EB ; # small l - CD EC ; # small m - CE ED ; # small n - CF EE ; # small o - - D0 EF ; # small p - D1 FF ; # small ya - D2 F0 ; # small r - D3 F1 ; # small s - D4 F2 ; # small t - D5 F3 ; # small u - D6 E6 ; # small zh - D7 E2 ; # small v - D8 FC ; # small soft sign - D9 FB ; # small y - DA E7 ; # small z - DB F8 ; # small sh - DC FD ; # small e - DD F9 ; # small shch - DE F7 ; # small ch - DF FA ; # small hard sign - - E0 DE ; # capital YU - E1 C0 ; # capital A - E2 C1 ; # capital B - E3 D6 ; # capital TS - E4 C4 ; # capital D - E5 C5 ; # capital YE - E6 D4 ; # capital F - E7 C3 ; # capital G - E8 D5 ; # capital KH - E9 C8 ; # capital I - EA C9 ; # capital J - EB CA ; # capital K - EC CB ; # capital L - ED CC ; # capital M - EE CD ; # capital N - EF CE ; # capital O - - F0 CF ; # capital P - F1 DF ; # capital YA - F2 D0 ; # capital R - F3 D1 ; # capital S - F4 D2 ; # capital T - F5 D3 ; # capital U - F6 C6 ; # capital ZH - F7 C2 ; # capital V - F8 DC ; # capital soft sign - F9 DB ; # capital Y - FA C7 ; # capital Z - FB D8 ; # capital SH - FC DD ; # capital E - FD D9 ; # capital SHCH - FE D7 ; # capital CH - FF DA ; # capital hard sign + 9E B7; # · + + A3 B8; # small yo + A4 BA; # small Ukrainian ye + + A6 B3; # small Ukrainian i + A7 BF; # small Ukrainian yi + + AD B4; # small Ukrainian soft g + AE A2; # small Byelorussian short u + + B0 B0; # ° + + B3 A8; # capital YO + B4 AA; # capital Ukrainian YE + + B6 B2; # capital Ukrainian I + B7 AF; # capital Ukrainian YI + + B9 B9; # numero sign + + BD A5; # capital Ukrainian soft G + BE A1; # capital Byelorussian short U + + BF A9; # (C) + + C0 FE; # small yu + C1 E0; # small a + C2 E1; # small b + C3 F6; # small ts + C4 E4; # small d + C5 E5; # small ye + C6 F4; # small f + C7 E3; # small g + C8 F5; # small kh + C9 E8; # small i + CA E9; # small j + CB EA; # small k + CC EB; # small l + CD EC; # small m + CE ED; # small n + CF EE; # small o + + D0 EF; # small p + D1 FF; # small ya + D2 F0; # small r + D3 F1; # small s + D4 F2; # small t + D5 F3; # small u + D6 E6; # small zh + D7 E2; # small v + D8 FC; # small soft sign + D9 FB; # small y + DA E7; # small z + DB F8; # small sh + DC FD; # small e + DD F9; # small shch + DE F7; # small ch + DF FA; # small hard sign + + E0 DE; # capital YU + E1 C0; # capital A + E2 C1; # capital B + E3 D6; # capital TS + E4 C4; # capital D + E5 C5; # capital YE + E6 D4; # capital F + E7 C3; # capital G + E8 D5; # capital KH + E9 C8; # capital I + EA C9; # capital J + EB CA; # capital K + EC CB; # capital L + ED CC; # capital M + EE CD; # capital N + EF CE; # capital O + + F0 CF; # capital P + F1 DF; # capital YA + F2 D0; # capital R + F3 D1; # capital S + F4 D2; # capital T + F5 D3; # capital U + F6 C6; # capital ZH + F7 C2; # capital V + F8 DC; # capital soft sign + F9 DB; # capital Y + FA C7; # capital Z + FB D8; # capital SH + FC DD; # capital E + FD D9; # capital SHCH + FE D7; # capital CH + FF DA; # capital hard sign } diff -Nru nginx-0.5.33/debian/conf/mime.types nginx-0.8.53/debian/conf/mime.types --- nginx-0.5.33/debian/conf/mime.types 2011-02-26 14:04:40.000000000 +0000 +++ nginx-0.8.53/debian/conf/mime.types 2011-02-26 14:04:40.000000000 +0000 @@ -1,69 +1,78 @@ - types { - text/html html htm shtml; - text/css css; - text/xml xml rss; - image/gif gif; - image/jpeg jpeg jpg; - application/x-javascript js; - application/atom+xml atom; - - text/mathml mml; - text/plain txt; - text/vnd.sun.j2me.app-descriptor jad; - text/vnd.wap.wml wml; - text/x-component htc; - - image/png png; - image/tiff tif tiff; - image/vnd.wap.wbmp wbmp; - image/x-icon ico; - image/x-jng jng; - image/x-ms-bmp bmp; - - application/java-archive jar war ear; - application/mac-binhex40 hqx; - application/msword doc; - application/pdf pdf; - application/postscript ps eps ai; - application/rtf rtf; - application/vnd.ms-excel xls; - application/vnd.ms-powerpoint ppt; - application/vnd.wap.wmlc wmlc; - application/vnd.wap.xhtml+xml xhtml; - application/x-cocoa cco; - application/x-java-archive-diff jardiff; - application/x-java-jnlp-file jnlp; - application/x-makeself run; - application/x-perl pl pm; - application/x-pilot prc pdb; - application/x-rar-compressed rar; - application/x-redhat-package-manager rpm; - application/x-sea sea; - application/x-shockwave-flash swf; - application/x-stuffit sit; - application/x-tcl tcl tk; - application/x-x509-ca-cert der pem crt; - application/x-xpinstall xpi; - application/zip zip; - - application/octet-stream bin exe dll; - application/octet-stream deb; - application/octet-stream dmg; - application/octet-stream eot; - application/octet-stream iso img; - application/octet-stream msi msp msm; - - audio/midi mid midi kar; - audio/mpeg mp3; - audio/x-realaudio ra; - - video/3gpp 3gpp 3gp; - video/mpeg mpeg mpg; - video/quicktime mov; - video/x-flv flv; - video/x-mng mng; - video/x-ms-asf asx asf; - video/x-ms-wmv wmv; - video/x-msvideo avi; + text/html html htm shtml; + text/css css; + text/xml xml rss; + image/gif gif; + image/jpeg jpeg jpg; + application/x-javascript js; + application/atom+xml atom; + + text/mathml mml; + text/plain txt; + text/vnd.sun.j2me.app-descriptor jad; + text/vnd.wap.wml wml; + text/x-component htc; + + image/png png; + image/tiff tif tiff; + image/vnd.wap.wbmp wbmp; + image/x-icon ico; + image/x-jng jng; + image/x-ms-bmp bmp; + image/svg+xml svg svgz; + + application/java-archive jar war ear; + application/mac-binhex40 hqx; + application/msword doc; + application/pdf pdf; + application/postscript ps eps ai; + application/rtf rtf; + application/vnd.ms-excel xls; + application/vnd.ms-powerpoint ppt; + application/vnd.wap.wmlc wmlc; + application/vnd.wap.xhtml+xml xhtml; + application/vnd.google-earth.kml+xml kml; + application/vnd.google-earth.kmz kmz; + application/x-7z-compressed 7z; + application/x-cocoa cco; + application/x-java-archive-diff jardiff; + application/x-java-jnlp-file jnlp; + application/x-makeself run; + application/x-perl pl pm; + application/x-pilot prc pdb; + application/x-rar-compressed rar; + application/x-redhat-package-manager rpm; + application/x-sea sea; + application/x-shockwave-flash swf; + application/x-stuffit sit; + application/x-tcl tcl tk; + application/x-x509-ca-cert der pem crt; + application/x-xpinstall xpi; + application/zip zip; + + application/octet-stream bin exe dll; + application/octet-stream deb; + application/octet-stream dmg; + application/octet-stream eot; + application/octet-stream iso img; + application/octet-stream msi msp msm; + application/ogg ogx; + + audio/midi mid midi kar; + audio/mpeg mpga mpega mp2 mp3 m4a; + audio/ogg oga ogg spx; + audio/x-realaudio ra; + audio/webm weba; + + video/3gpp 3gpp 3gp; + video/mp4 mp4; + video/mpeg mpeg mpg mpe; + video/ogg ogv; + video/quicktime mov; + video/webm webm; + video/x-flv flv; + video/x-mng mng; + video/x-ms-asf asx asf; + video/x-ms-wmv wmv; + video/x-msvideo avi; } diff -Nru nginx-0.5.33/debian/conf/nginx.conf nginx-0.8.53/debian/conf/nginx.conf --- nginx-0.5.33/debian/conf/nginx.conf 2011-02-26 14:04:40.000000000 +0000 +++ nginx-0.8.53/debian/conf/nginx.conf 2011-02-26 14:04:40.000000000 +0000 @@ -1,28 +1,78 @@ user www-data; -worker_processes 1; - -error_log /var/log/nginx/error.log; -pid /var/run/nginx.pid; +worker_processes 4; +pid /var/run/nginx.pid; events { - worker_connections 1024; + worker_connections 768; + # multi_accept on; } http { - include /etc/nginx/mime.types; - default_type application/octet-stream; - - access_log /var/log/nginx/access.log; - - sendfile on; - #tcp_nopush on; - #keepalive_timeout 0; - keepalive_timeout 65; - tcp_nodelay on; + ## + # Basic Settings + ## + + sendfile on; + tcp_nopush on; + tcp_nodelay on; + keepalive_timeout 65; + types_hash_max_size 2048; + # server_tokens off; + + # server_names_hash_bucket_size 64; + # server_name_in_redirect off; + + include /etc/nginx/mime.types; + default_type application/octet-stream; + + ## + # Logging Settings + ## + + access_log /var/log/nginx/access.log; + error_log /var/log/nginx/error.log; + + ## + # Gzip Settings + ## + + gzip on; + gzip_disable "msie6"; + + # gzip_vary on; + # gzip_proxied any; + # gzip_comp_level 6; + # gzip_buffers 16 8k; + # gzip_http_version 1.1; + # gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript; + + ## + # Virtual Host Configs + ## - gzip on; + include /etc/nginx/conf.d/*.conf; + include /etc/nginx/sites-enabled/*; +} - include /etc/nginx/sites-enabled/*; -} +#mail { +# # See sample authentication script at: +# # http://wiki.nginx.org/ImapAuthenticateWithApachePhpScript +# +# # auth_http localhost/auth.php; +# # pop3_capabilities "TOP" "USER"; +# # imap_capabilities "IMAP4rev1" "UIDPLUS"; +# +# server { +# listen localhost:110; +# protocol pop3; +# proxy on; +# } +# +# server { +# listen localhost:143; +# protocol imap; +# proxy on; +# } +#} diff -Nru nginx-0.5.33/debian/conf/scgi_params nginx-0.8.53/debian/conf/scgi_params --- nginx-0.5.33/debian/conf/scgi_params 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/debian/conf/scgi_params 2011-02-26 14:04:40.000000000 +0000 @@ -0,0 +1,14 @@ +scgi_param REQUEST_METHOD $request_method; +scgi_param REQUEST_URI $request_uri; +scgi_param QUERY_STRING $query_string; +scgi_param CONTENT_TYPE $content_type; + +scgi_param DOCUMENT_URI $document_uri; +scgi_param DOCUMENT_ROOT $document_root; +scgi_param SCGI 1; +scgi_param SERVER_PROTOCOL $server_protocol; + +scgi_param REMOTE_ADDR $remote_addr; +scgi_param REMOTE_PORT $remote_port; +scgi_param SERVER_PORT $server_port; +scgi_param SERVER_NAME $server_name; diff -Nru nginx-0.5.33/debian/conf/sites-available/default nginx-0.8.53/debian/conf/sites-available/default --- nginx-0.5.33/debian/conf/sites-available/default 2011-02-26 14:04:40.000000000 +0000 +++ nginx-0.8.53/debian/conf/sites-available/default 2011-02-26 14:04:40.000000000 +0000 @@ -2,48 +2,77 @@ # server { # ... # } -# statements for each of your virtual hosts +# statements for each of your virtual hosts to this file + +## +# You should look at the following URL's in order to grasp a solid understanding +# of Nginx configuration files in order to fully unleash the power of Nginx. +# http://wiki.nginx.org/Pitfalls +# http://wiki.nginx.org/Quickstart +# http://wiki.nginx.org/Configuration +# +# Generally, you will want to move this file somewhere, and start with a clean +# file but keep this around for reference. Or just disable in sites-enabled. +# +# Please see /usr/share/doc/nginx/examples/ for more detailed examples. +## server { - listen 80; - server_name localhost; + #listen 80; ## listen for ipv4; this line is default and implied + #listen [::]:80 default ipv6only=on; ## listen for ipv6 - access_log /var/log/nginx/localhost.access.log; + root /usr/share/nginx/www; + index index.html index.htm; + + # Make site accessible from http://localhost/ + server_name localhost; location / { - root /var/www/nginx-default; - index index.html index.htm; + # First attempt to serve request as file, then + # as directory, then fall back to index.html + try_files $uri $uri/ /index.html; + } + + location /doc { + root /usr/share; + autoindex on; + allow 127.0.0.1; + deny all; + } + + location /images { + root /usr/share; + autoindex off; } - #error_page 404 /404.html; + #error_page 404 /404.html; # redirect server error pages to the static page /50x.html # - error_page 500 502 503 504 /50x.html; - location = /50x.html { - root /var/www/nginx-default; - } + #error_page 500 502 503 504 /50x.html; + #location = /50x.html { + # root /usr/share/nginx/www; + #} # proxy the PHP scripts to Apache listening on 127.0.0.1:80 # #location ~ \.php$ { - #proxy_pass http://127.0.0.1; + # proxy_pass http://127.0.0.1; #} # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 # #location ~ \.php$ { - #fastcgi_pass 127.0.0.1:9000; - #fastcgi_index index.php; - #fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name; - #includefastcgi_params; + # fastcgi_pass 127.0.0.1:9000; + # fastcgi_index index.php; + # include fastcgi_params; #} # deny access to .htaccess files, if Apache's document root # concurs with nginx's one # #location ~ /\.ht { - #deny all; + # deny all; #} } @@ -51,35 +80,38 @@ # another virtual host using mix of IP-, name-, and port-based configuration # #server { -#listen 8000; -#listen somename:8080; -#server_name somename alias another.alias; - -#location / { -#root html; -#index index.html index.htm; -#} +# listen 8000; +# listen somename:8080; +# server_name somename alias another.alias; +# root html; +# index index.html index.htm; +# +# location / { +# try_files $uri $uri/ /index.html; +# } #} # HTTPS server # #server { -#listen 443; -#server_name localhost; - -#ssl on; -#ssl_certificate cert.pem; -#ssl_certificate_key cert.key; - -#ssl_session_timeout 5m; - -#ssl_protocols SSLv2 SSLv3 TLSv1; -#ssl_ciphers ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP; -#ssl_prefer_server_ciphers on; - -#location / { -#root html; -#index index.html index.htm; -#} +# listen 443; +# server_name localhost; +# +# root html; +# index index.html index.htm; +# +# ssl on; +# ssl_certificate cert.pem; +# ssl_certificate_key cert.key; +# +# ssl_session_timeout 5m; +# +# ssl_protocols SSLv3 TLSv1; +# ssl_ciphers ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv3:+EXP; +# ssl_prefer_server_ciphers on; +# +# location / { +# try_files $uri $uri/ /index.html; +# } #} diff -Nru nginx-0.5.33/debian/conf/uwsgi_params nginx-0.8.53/debian/conf/uwsgi_params --- nginx-0.5.33/debian/conf/uwsgi_params 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/debian/conf/uwsgi_params 2011-02-26 14:04:40.000000000 +0000 @@ -0,0 +1,14 @@ +uwsgi_param QUERY_STRING $query_string; +uwsgi_param REQUEST_METHOD $request_method; +uwsgi_param CONTENT_TYPE $content_type; +uwsgi_param CONTENT_LENGTH $content_length; + +uwsgi_param REQUEST_URI $request_uri; +uwsgi_param PATH_INFO $document_uri; +uwsgi_param DOCUMENT_ROOT $document_root; +uwsgi_param SERVER_PROTOCOL $server_protocol; + +uwsgi_param REMOTE_ADDR $remote_addr; +uwsgi_param REMOTE_PORT $remote_port; +uwsgi_param SERVER_PORT $server_port; +uwsgi_param SERVER_NAME $server_name; diff -Nru nginx-0.5.33/debian/conf/win-utf nginx-0.8.53/debian/conf/win-utf --- nginx-0.5.33/debian/conf/win-utf 2011-02-26 14:04:40.000000000 +0000 +++ nginx-0.8.53/debian/conf/win-utf 2011-02-26 14:04:40.000000000 +0000 @@ -1,126 +1,125 @@ - # This map is not a full windows-1251 <> utf8 map: it does not -# contain Serbian and Macedonian letters. If you need a full map, +# contain Serbian and Macedonian letters. If you need a full map, # use contrib/unicode2nginx/win-utf map instead. -charset_map windows-1251 utf-8 { +charset_map windows-1251 utf-8 { - 82 E2809A ; # single low-9 quotation mark + 82 E2809A; # single low-9 quotation mark - 84 E2809E ; # double low-9 quotation mark - 85 E280A6 ; # ellipsis - 86 E280A0 ; # dagger - 87 E280A1 ; # double dagger - 88 E282AC ; # euro - 89 E280B0 ; # per mille - - 91 E28098 ; # left single quotation mark - 92 E28099 ; # right single quotation mark - 93 E2809C ; # left double quotation mark - 94 E2809D ; # right double quotation mark - 95 E280A2 ; # bullet - 96 E28093 ; # en dash - 97 E28094 ; # em dash - - 99 E284A2 ; # trade mark sign - - A0 C2A0 ; #   - A1 D18E ; # capital Byelorussian short U - A2 D19E ; # small Byelorussian short u - - A4 C2A4 ; # currency sign - A5 D290 ; # capital Ukrainian soft G - A6 C2A6 ; # borken bar - A7 C2A7 ; # section sign - A8 D081 ; # capital YO - A9 C2A9 ; # (C) - AA D084 ; # capital Ukrainian YE - AB C2AB ; # left-pointing double angle quotation mark - AC C2AC ; # not sign - AD C2AD ; # soft hypen - AE C2AE ; # (R) - AF D087 ; # capital Ukrainian YI - - B0 C2B0 ; # ° - B1 C2B1 ; # plus-minus sign - B2 D086 ; # capital Ukrainian I - B3 D196 ; # small Ukrainian i - B4 D291 ; # small Ukrainian soft g - B5 C2B5 ; # micro sign - B6 C2B6 ; # pilcrow sign - B7 C2B7 ; # · - B8 D191 ; # small yo - B9 E28496 ; # numero sign - BA D194 ; # small Ukrainian ye - BB C2BB ; # right-pointing double angle quotation mark - - BF D197 ; # small Ukrainian yi - - C0 D090 ; # capital A - C1 D091 ; # capital B - C2 D092 ; # capital V - C3 D093 ; # capital G - C4 D094 ; # capital D - C5 D095 ; # capital YE - C6 D096 ; # capital ZH - C7 D097 ; # capital Z - C8 D098 ; # capital I - C9 D099 ; # capital J - CA D09A ; # capital K - CB D09B ; # capital L - CC D09C ; # capital M - CD D09D ; # capital N - CE D09E ; # capital O - CF D09F ; # capital P - - D0 D0A0 ; # capital R - D1 D0A1 ; # capital S - D2 D0A2 ; # capital T - D3 D0A3 ; # capital U - D4 D0A4 ; # capital F - D5 D0A5 ; # capital KH - D6 D0A6 ; # capital TS - D7 D0A7 ; # capital CH - D8 D0A8 ; # capital SH - D9 D0A9 ; # capital SHCH - DA D0AA ; # capital hard sign - DB D0AB ; # capital Y - DC D0AC ; # capital soft sign - DD D0AD ; # capital E - DE D0AE ; # capital YU - DF D0AF ; # capital YA - - E0 D0B0 ; # small a - E1 D0B1 ; # small b - E2 D0B2 ; # small v - E3 D0B3 ; # small g - E4 D0B4 ; # small d - E5 D0B5 ; # small ye - E6 D0B6 ; # small zh - E7 D0B7 ; # small z - E8 D0B8 ; # small i - E9 D0B9 ; # small j - EA D0BA ; # small k - EB D0BB ; # small l - EC D0BC ; # small m - ED D0BD ; # small n - EE D0BE ; # small o - EF D0BF ; # small p - - F0 D180 ; # small r - F1 D181 ; # small s - F2 D182 ; # small t - F3 D183 ; # small u - F4 D184 ; # small f - F5 D185 ; # small kh - F6 D186 ; # small ts - F7 D187 ; # small ch - F8 D188 ; # small sh - F9 D189 ; # small shch - FA D18A ; # small hard sign - FB D18B ; # small y - FC D18C ; # small soft sign - FD D18D ; # small e - FE D18E ; # small yu - FF D18F ; # small ya + 84 E2809E; # double low-9 quotation mark + 85 E280A6; # ellipsis + 86 E280A0; # dagger + 87 E280A1; # double dagger + 88 E282AC; # euro + 89 E280B0; # per mille + + 91 E28098; # left single quotation mark + 92 E28099; # right single quotation mark + 93 E2809C; # left double quotation mark + 94 E2809D; # right double quotation mark + 95 E280A2; # bullet + 96 E28093; # en dash + 97 E28094; # em dash + + 99 E284A2; # trade mark sign + + A0 C2A0; #   + A1 D18E; # capital Byelorussian short U + A2 D19E; # small Byelorussian short u + + A4 C2A4; # currency sign + A5 D290; # capital Ukrainian soft G + A6 C2A6; # borken bar + A7 C2A7; # section sign + A8 D081; # capital YO + A9 C2A9; # (C) + AA D084; # capital Ukrainian YE + AB C2AB; # left-pointing double angle quotation mark + AC C2AC; # not sign + AD C2AD; # soft hypen + AE C2AE; # (R) + AF D087; # capital Ukrainian YI + + B0 C2B0; # ° + B1 C2B1; # plus-minus sign + B2 D086; # capital Ukrainian I + B3 D196; # small Ukrainian i + B4 D291; # small Ukrainian soft g + B5 C2B5; # micro sign + B6 C2B6; # pilcrow sign + B7 C2B7; # · + B8 D191; # small yo + B9 E28496; # numero sign + BA D194; # small Ukrainian ye + BB C2BB; # right-pointing double angle quotation mark + + BF D197; # small Ukrainian yi + + C0 D090; # capital A + C1 D091; # capital B + C2 D092; # capital V + C3 D093; # capital G + C4 D094; # capital D + C5 D095; # capital YE + C6 D096; # capital ZH + C7 D097; # capital Z + C8 D098; # capital I + C9 D099; # capital J + CA D09A; # capital K + CB D09B; # capital L + CC D09C; # capital M + CD D09D; # capital N + CE D09E; # capital O + CF D09F; # capital P + + D0 D0A0; # capital R + D1 D0A1; # capital S + D2 D0A2; # capital T + D3 D0A3; # capital U + D4 D0A4; # capital F + D5 D0A5; # capital KH + D6 D0A6; # capital TS + D7 D0A7; # capital CH + D8 D0A8; # capital SH + D9 D0A9; # capital SHCH + DA D0AA; # capital hard sign + DB D0AB; # capital Y + DC D0AC; # capital soft sign + DD D0AD; # capital E + DE D0AE; # capital YU + DF D0AF; # capital YA + + E0 D0B0; # small a + E1 D0B1; # small b + E2 D0B2; # small v + E3 D0B3; # small g + E4 D0B4; # small d + E5 D0B5; # small ye + E6 D0B6; # small zh + E7 D0B7; # small z + E8 D0B8; # small i + E9 D0B9; # small j + EA D0BA; # small k + EB D0BB; # small l + EC D0BC; # small m + ED D0BD; # small n + EE D0BE; # small o + EF D0BF; # small p + + F0 D180; # small r + F1 D181; # small s + F2 D182; # small t + F3 D183; # small u + F4 D184; # small f + F5 D185; # small kh + F6 D186; # small ts + F7 D187; # small ch + F8 D188; # small sh + F9 D189; # small shch + FA D18A; # small hard sign + FB D18B; # small y + FC D18C; # small soft sign + FD D18D; # small e + FE D18E; # small yu + FF D18F; # small ya } diff -Nru nginx-0.5.33/debian/control nginx-0.8.53/debian/control --- nginx-0.5.33/debian/control 2011-02-26 14:04:40.000000000 +0000 +++ nginx-0.8.53/debian/control 2011-02-26 14:04:40.000000000 +0000 @@ -1,19 +1,58 @@ Source: nginx -Section: web +Section: httpd Priority: optional Maintainer: Jose Parrella -Uploaders: Fabio Tranchitella -Build-Depends: debhelper (>= 5), autotools-dev, libpcre3-dev, zlib1g-dev, libssl-dev -Standards-Version: 3.7.2 +Uploaders: Fabio Tranchitella , + Kartik Mistry , + Michael Lustfield +Build-Depends: debhelper (>= 6), + autotools-dev, + libgd2-noxpm-dev, + libgeoip-dev, + libssl-dev, + libpcre3-dev, + libxslt1-dev, + zlib1g-dev +Standards-Version: 3.9.1 +Homepage: http://nginx.net +Vcs-Svn: svn://svn.debian.org/svn/collab-maint/deb-maint/nginx/trunk +Vcs-Browser: http://svn.debian.org/wsvn/collab-maint/deb-maint/nginx/trunk Package: nginx Architecture: any -Depends: ${shlibs:Depends} -Provides: httpd, httpd-cgi -Description: small, but very powerful and efficient web server - Nginx (engine x) is a web server created by Igor Sysoev and kindly provided - to the open-source community. This server can be used as standalone HTTP - server and as a reverse proxy server before some Apache or another big - server to reduce load to backend servers by many concurrent HTTP-sessions. +Depends: ${misc:Depends}, ${shlibs:Depends}, lsb-base (>= 3.2-14) +Suggests: ufw, nginx-doc +Provides: httpd +Description: small, but very powerful and efficient web server and mail proxy + Nginx (engine x) is a web server created by Igor Sysoev and kindly provided to + the open-source community. This server can be used as standalone HTTP server + and as a reverse proxy server before some Apache or another big server to + reduce load to backend servers by many concurrent HTTP-sessions. . - Homepage: http://nginx.net/ + It can also act as a POP3/IMAP mail proxy with SSL and TLS SNI support. + +Package: nginx-dbg +Architecture: any +Section: debug +Priority: extra +Depends: ${misc:Depends}, nginx (= ${binary:Version}) +Description: Debugging symbols for nginx + Nginx (engine x) is a web server created by Igor Sysoev and kindly provided to + the open-source community. This server can be used as standalone HTTP server + and as a reverse proxy server before some Apache or another big server to + reduce load to backend servers by many concurrent HTTP-sessions. + . + This package provides debugging symbols for nginx, to assist in debugging + issues that you may find. It should not be required for normal operation. + +Package: nginx-doc +Architecture: any +Section: doc +Depends: ${misc:Depends} +Description: Documentation for nginx + Nginx (engine x) is a web server created by Igor Sysoev and kindly provided to + the open-source community. This server can be used as standalone HTTP server + and as a reverse proxy server before some Apache or another big server to + reduce load to backend servers by many concurrent HTTP-sessions. + . + This package provides extra documentation for Nginx. diff -Nru nginx-0.5.33/debian/copyright nginx-0.8.53/debian/copyright --- nginx-0.5.33/debian/copyright 2011-02-26 14:04:40.000000000 +0000 +++ nginx-0.8.53/debian/copyright 2011-02-26 14:04:40.000000000 +0000 @@ -1,30 +1,41 @@ -This package was debianized by Jose Parrella on -Tue, 5 Sep 2006 11:33:34 -0400. - -It was downloaded from http://nginx.net/ - +Format-Specification: http://svn.debian.org/wsvn/dep/web/deps/dep5.mdwn?op=file&rev=135 +Name: nginx +Maintainer: Jose Parrella +Source: http://nginx.net/ Upstream Author: Igor Sysoev -Copyright (C) 2002-2006 Igor Sysoev - -License: - Redistribution and use in source and binary forms, with or without - modification, are permitted under the terms of the BSD License. - - THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. - -On Debian systems, the complete text of the BSD License can be -found in `/usr/share/common-licenses/BSD'. - -The Debian packaging is (C) 2006, Jose Parrella and -is licensed under the GPL, see `/usr/share/common-licenses/GPL'. +Files: * +Copyright: © 2002-2010, Igor Sysoev +License: BSD + 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 REGENTS 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 REGENTS 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. + +Files: contrib/geo2nginx.pl +Copyright: © 2005, Andrei Nigmatulin + +Files: modules/nginx-upstream-fair/ngx_http_upstream_fair_module.c +Copyright: © 2007, Igor Sysoev, Grzegorz Nosek + +Files: debian/* +Copyright: © 2006-2010, Jose Parrella , + Fabio Tranchitella + © 2009-2010, Kartik Mistry + © 2010, Michael Lustfield +License: Same as upstream license. diff -Nru nginx-0.5.33/debian/dirs nginx-0.8.53/debian/dirs --- nginx-0.5.33/debian/dirs 2011-02-26 14:04:40.000000000 +0000 +++ nginx-0.8.53/debian/dirs 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -usr/sbin -etc/nginx -etc/nginx/sites-available -etc/nginx/sites-enabled -var/log/nginx -var/lib/nginx -var/www/nginx-default diff -Nru nginx-0.5.33/debian/docs nginx-0.8.53/debian/docs --- nginx-0.5.33/debian/docs 2011-02-26 14:04:40.000000000 +0000 +++ nginx-0.8.53/debian/docs 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -README diff -Nru nginx-0.5.33/debian/help/docs/fcgiwrap nginx-0.8.53/debian/help/docs/fcgiwrap --- nginx-0.5.33/debian/help/docs/fcgiwrap 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/debian/help/docs/fcgiwrap 2011-02-26 14:04:40.000000000 +0000 @@ -0,0 +1,15 @@ +## +# File: +# fcgiwrap +# Description: +# The fcgiwrap tool allows a user to quickly and painlessly setup a socket +# to handle CGI requests. This is useful for Python, Perl, etc. +## + +The easy way: +As of Ubuntu 10.10 (Maverick Meerkat) you can install it from repositories. +apt-get install fcgiwrap + +The less-easy way: +If your distribution does not provide it, the Nginx wiki describes the steps. +http://wiki.nginx.org/Fcgiwrap diff -Nru nginx-0.5.33/debian/help/docs/php nginx-0.8.53/debian/help/docs/php --- nginx-0.5.33/debian/help/docs/php 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/debian/help/docs/php 2011-02-26 14:04:40.000000000 +0000 @@ -0,0 +1,119 @@ +## +# File: +# php +# Description: +# This file is meant to help users get a basic understanding of a PHP stack +# with Nginx as the web server. +## + +# Note: This is not a configuration sample and comment lines will be userd differently. + + +== PHP Options == + +There are a number of options that can be used to provide PHP. The two most +common methods are php-cgi and php-fpm. The php-fpm option is relatively new +and is not yet a standard option. This package is stable however and is moving +toward being a standard option in distribution repositories. + +== PHP-FPM == + +The php-fpm option is considerably harder to debug. However, the hardest issues +to debug should be solved by including that fastcgi_params file provided by +this package. It should at a minimum remove all silent errors. + +# sudo apt-get install php5-fpm + +If you do not have php5-fpm available, you will want to add the repository for +the package. https://launchpad.net/~nginx/+archive/php5 + +In php5-fpm, you will want to edit the php pool. +Edit /etc/php5/fpm/pool.d/www.conf + +The listen directive is the most important piece in this file. It is suggested +to listen to a local unix socket. This listen directive will be used in your +nginx configuration. +Example: listen = /tmp/phpfpm.socket + +The rest of this file can be tweaked to your liking. + +== PHP-CGI == + +The simplest and easiest method to run PHP is to use php-cgi. It does not offer +the ability to monitor and restart processes that hang or die however. + +# sudo apt-get install php5-cgi + +To make php5-cgio work, you will need to create an init script. + +===== FILE: /etc/init.d/phpcgi ===== +#!/bin/bash +# +### BEGIN INIT INFO +# Provides: php-fcgi +# Required-Start: $local_fs $remote_fs $network $syslog +# Required-Stop: $local_fs $remote_fs $network $syslog +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: starts php-cgi processes +# Description: starts php-cgi using start-stop-daemon for each user +### END INIT INFO + +# Number of PHP processes to be able to handle connections. +CHILD=10 +# Maximum number of requests each child should handle before being regenerated +MAX_REQS=750 + +start() { + start-stop-daemon --quiet --start --background --chuid "www-data" \ + --exec /usr/bin/env \ + -- - USER="www-data" \ + PATH=/usr/bin PHP_FCGI_CHILDREN=$CHILD PHP_FCGI_MAX_REQUESTS=$MAX_REQS \ + php-cgi -b /tmp/phpcgi.socket & +} + +stop() { + killall -w php-cgi + rm /tmp/phpcgi.socket + sync + sleep 1 +} + +case "$1" in + start) start;; + stop) stop;; + restart) stop; start;; + *) echo "Usage: php-fastcgi {start|stop|restart} [user]"; exit 1;; +esac +===== END FILE ===== + +# Make file executable +chmod +x /etc/init.d/phpcgi + +# Add file to startup +update-rc.d phpcgi defaults + +== Using PHP in Nginx == + +In order to use the sockets you created (/tmp/phpfpm.socket or /tmp/phpcgi.socket) +you will need to add a php block to your Nignx configuration. + +# This block adds a little security. +# See /usr/share/doc/nginx/examples/drupal for context +location ~ \..*/.*\.php$ { + return 403; +} + +# This is basic PHP block that can be used to handle all PHP requests. +# See /usr/share/doc/nginx/examples/drupal for context +location ~ \.php$ { + fastcgi_split_path_info ^(.+\.php)(/.+)$; + include fastcgi_params; + # Intercepting errors will cause PHP errors to appear in Nginx logs + fastcgi_intercept_errors on; + fastcgi_pass unix:/tmp/phpcgi.socket; +} + +# The above example will use php5-cgi which is bound to /tmp/phpcgi.socket. +# If you choose to use php5-fpm the example above will bind to /tmp/phpcgi.socket +# instead and this should be used for fastcgi_pass instead. diff -Nru nginx-0.5.33/debian/help/docs/support-irc nginx-0.8.53/debian/help/docs/support-irc --- nginx-0.5.33/debian/help/docs/support-irc 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/debian/help/docs/support-irc 2011-02-26 14:04:40.000000000 +0000 @@ -0,0 +1,28 @@ +## +# File: +# support-irc +# Description: +# This file explains how to get support on IRC. +## + +Nginx has a moderately active IRC channel on Freenode. + +You can get there by: + * Client + Using an IRC Client and pointing it to: + #nginx on irc.freenode.net + * Browser + Opening a web browser and pointing it to: + http://webchat.freenode.net?channels=nginx + +Common sense is expected to be followed: + * Code of Conduct + http://www.ubuntu.com/community/conduct + * IRC Etiquette + http://www.ircbeginner.com/ircinfo/etiquette.html + * Patience + If somebody doesn't answer you right away, then wait. + Even active IRC users have to run off sometimes. + +If you need more help with IRC The Linux Documentation Project has more information: + http://tldp.org/HOWTO/IRC/beginners.html diff -Nru nginx-0.5.33/debian/help/docs/upstream nginx-0.8.53/debian/help/docs/upstream --- nginx-0.5.33/debian/help/docs/upstream 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/debian/help/docs/upstream 2011-02-26 14:04:40.000000000 +0000 @@ -0,0 +1,51 @@ +## +# File: +# upstream +# Description: +# This file describes how to use upstream blocks. +## + +An upstream block allows you to set a list of upstream locations for both +proxy_pass and fastcgi_pass directives. + +Examples of upstream blocks: + +# PHP listening on the same server +upstream php { + # ip_hash ensures the same backend is used for client reconnects. + ip_hash; + # This assumes we have multiple PHP listeners on different known ports. + server 127.0.0.1:9000; + server 127.0.0.1:9001; + server 127.0.0.1:9002; + server 127.0.0.1:9003; + # In addition to listening on ports, we can listen to unix sockets. + server unix:/tmp/php-cgi.socket; +} + +# Multiple backend Apache instances on separate servers +upstream apache { + # Adding a weight alters the chance the upstream server will be used. + server apache1.domain.com weight 5; + server apache2.domain.com; + server apache3.domain.com; + # Adding 'down' keeps the upstream from being used. Useful for downtime management. + server apache4.domain.com down; + server apache5.domain.com; +} + +Using an upstream location: + +# Passing PHP to upstream +location ~ \.php$ { + fastcgi_split_path_info ^(.+\.php)(/.+)$; + include fastcgi_params; + fastcgi_pass php; +} + +# Passing all requests for /cgi-bin/* to Apache upstreams. +location /cgi-bin { + proxy_pass apache; +} + +For more information see http://wiki.nginx.org/HttpUpstreamModule diff -Nru nginx-0.5.33/debian/help/examples/drupal nginx-0.8.53/debian/help/examples/drupal --- nginx-0.5.33/debian/help/examples/drupal 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/debian/help/examples/drupal 2011-02-26 14:04:40.000000000 +0000 @@ -0,0 +1,114 @@ +## +# File: +# drupal +# Description: +# This file is meant to offer a very detailed set of instructions and best +# practices for deploying a Drupal website with Nginx. This file should be +# almost drop-in if the user is able to understand the three lines that +# need to be changed. +## + +server { + + # This is the URI of your website. You can specify multiple sites to be + # served by the same Drupal installation. + server_name domain.com www.domain.com .example.net; + + # This is the root of the Drupal directory. + # Note that Drupal 6, Drupal 7, and Pressflow are interchangeable + root /var/www/drupal6; + + # In some cases a favicon does not exist but this is not something you + # normally need to worry about. It's also a microscopic image and will + # just clutter the logs. + location = /favicon.ico { + log_not_found off; + access_log off; + } + + # This is for the robots.txt file used by search engines. + location = /robots.txt { + # If you have one, you want to allow them access to it. + allow all; + # If you don't have one, you don't want to fill your logs with + # not found errors. + log_not_found off; + access_log off; + } + + # This matters if you use drush because drush copies backups of modules + # to this directory. In the event personal information wound up in the + # module, you want to know outside users can't access it. + location = /backup { + deny all; + } + + # Very rarely should these ever be accessed outside of your lan + # The above location for robots.txt is an exact match and will override + # this location block. + location ~* \.(txt|log)$ { + allow 192.168.0.0/16; + deny all; + } + + # This location block protects against a known attack. It happens if + # the attacker uploads a non-php file and attempts to run it as a + # php file on the server. + location ~ \..*/.*\.php$ { + return 403; + } + + # This is our primary location block. The try_files directive will + # attempt to serve the data in the order listed. First try the exact + # request (such as an image or text file). If it doesn't exist, see if + # the directory exists. If not, then we move to the rewrite which is + # used for the front-end controller pattern. + location / { + try_files $uri $uri/ @rewrite; + } + + # This will rewrite our request from domain.com/node/1/ to domain.com/index.php?q=node/1 + # This could be done in try_files without a rewrite however, the GlobalRedirect + # module enforces no slash (/) at the end of URL's. This rewrite removes that + # so no infinite redirect loop is reached. + location @rewrite { + rewrite ^/(.*)$ /index.php?q=$1; + } + + # If a PHP file is served, this block will handle the request. This block + # works on the assumption you are using php-cgi listening on /tmp/phpcgi.socket. + # Please see the php example (usr/share/doc/nginx/exmaples/php) for more + # information about setting up PHP. + # NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini + location ~ \.php$ { + fastcgi_split_path_info ^(.+\.php)(/.+)$; + include fastcgi_params; + # Intercepting errors will cause PHP errors to appear in Nginx logs + fastcgi_intercept_errors on; + fastcgi_pass unix:/tmp/phpcgi.socket; + } + + # The ImageCache module builds an image 'on the fly' which means that + # if it doesn't exist, it needs to be created. Nginx is king of static + # so there's no point in letting PHP decide if it needs to be servered + # from an existing file. + # If the image can't be served directly, it's assumed that it doesn't + # exist and is passed off to PHP via our previous rewrite to let PHP + # create and serve the image. + # Notice that try_files does not have $uri/ in it. This is because an + # image should never be a directory. So there's no point in wasting a + # stat to serve it that way. + location ~ ^/sites/.*/files/imagecache/ { + try_files $uri @rewrite; + } + + # As mentioned above, Nignx is king of static. If we're serving a static + # file that ends with one of the following extensions, it is best to set + # a very high expires time. This will generate fewer requests for the + # file. These requests will be logged if found, but not if they don't + # exist. + location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ { + expires max; + log_not_found off; + } +} diff -Nru nginx-0.5.33/debian/help/examples/http nginx-0.8.53/debian/help/examples/http --- nginx-0.5.33/debian/help/examples/http 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/debian/help/examples/http 2011-02-26 14:04:40.000000000 +0000 @@ -0,0 +1,59 @@ +## +# File: +# http +# Description: +# This file is meant to deliver a basic understanding of the http block. +## + +# All web configuration should be inside of the http block. +# Most settings here are pretty self explanatory. +# See http://wiki.nginx.org/HttpCoreModule for details. +http { + + ## + # Basic Settings + ## + + sendfile on; + tcp_nopush on; + tcp_nodelay on; + keepalive_timeout 65; + # server_tokens off; + + # server_names_hash_bucket_size 64; + # server_name_in_redirect off; + + include /etc/nginx/mime.types; + default_type application/octet-stream; + + ## + # Logging Settings + ## + + access_log /var/log/nginx/access.log; + error_log /var/log/nginx/error.log; + + ## + # Gzip Settings + ## + + gzip on; + gzip_disable "msie6"; + + # gzip_vary on; + # gzip_proxied any; + # gzip_comp_level 6; + # gzip_buffers 16 8k; + # gzip_http_version 1.1; + # gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript; + + ## + # Virtual Host Configs + # These lines will include: + # any files in /etc/nginx/sites-enabled/ + # and any in /etc/nginx/conf.d/ ending with .conf + ## + + include /etc/nginx/conf.d/*.conf; + include /etc/nginx/sites-enabled/*; +} diff -Nru nginx-0.5.33/debian/help/examples/mail nginx-0.8.53/debian/help/examples/mail --- nginx-0.5.33/debian/help/examples/mail 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/debian/help/examples/mail 2011-02-26 14:04:40.000000000 +0000 @@ -0,0 +1,30 @@ +## +# File: +# mail +# Description: +# Provides a basic example of a mail proxy. +## + +# All mail proxy configuration should be inside of the mail block. +# Most settings here are pretty self explanatory. +# See http://wiki.nginx.org/MailCoreModule +mail { + # See sample authentication script at: + # http://wiki.nginx.org/ImapAuthenticateWithApachePhpScript + + # auth_http localhost/auth.php; + # pop3_capabilities "TOP" "USER"; + # imap_capabilities "IMAP4rev1" "UIDPLUS"; + + server { + listen localhost:110; + protocol pop3; + proxy on; + } + + server { + listen localhost:143; + protocol imap; + proxy on; + } +} diff -Nru nginx-0.5.33/debian/help/examples/mailman nginx-0.8.53/debian/help/examples/mailman --- nginx-0.5.33/debian/help/examples/mailman 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/debian/help/examples/mailman 2011-02-26 14:04:40.000000000 +0000 @@ -0,0 +1,59 @@ +## +# File: +# mailman +# Description: +# This file explains how to install MailMan and offers a nearly drop-in +# model if MailMan was installed from Debian/Ubuntu repositories. +## + +server { + + # This is the URI of your website. You can specify multiple sites to be + # served by the same Drupal installation. + server_name lists.DOMAIN.TLD; + + # This is the default MailMan root directory. + root /usr/lib/cgi-bin; + + # If the request is exactly the server_name, then we need to redirect + # the browser to the listinfo page. Notice the = for an exact match. + location = / { + rewrite ^ /mailman/listinfo permanent; + } + + # Any requests need to be rewritten to /mailman/. + # This happens only if no other location block matches. + location / { + rewrite ^ /mailman$uri; + } + + # If /mailmain/ was part of the request, then we need to let python + # handle the request. + location /mailman/ { + fastcgi_split_path_info (^/mailman/[^/]*)(.*)$; + include fastcgi_params; + fastcgi_param GATEWAY_INTERFACE CGI/1.1; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + fastcgi_param PATH_INFO $fastcgi_path_info; + fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info; + # fcgiwrap was used to create this socket. + # See /usr/share/doc/nginx-doc/fcgiwrap + fastcgi_pass unix:/tmp/cgi.socket; + } + + # If a request was made for an image, we need to change the directory + # that is beeing looked at. Nothing else is needed because the images + # are static content. + location /images/mailman { + alias /var/lib/mailman/icons; + } + + # If a request for /pipermail was made, we are still only dealing with + # static content. The archives are at a different location and we need + # to point at it. Because these are public, there's no issue in letting + # them be browsed; autoindex is turned on to allow browsing. + location /pipermail { + alias /var/lib/mailman/archives/public; + autoindex on; + } +} diff -Nru nginx-0.5.33/debian/help/examples/nginx.conf nginx-0.8.53/debian/help/examples/nginx.conf --- nginx-0.5.33/debian/help/examples/nginx.conf 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/debian/help/examples/nginx.conf 2011-02-26 14:04:40.000000000 +0000 @@ -0,0 +1,34 @@ +## +# File: +# nginx.conf +# Description: +# Provides a very basic description of the use of nginx.conf. +## + +# For more options with more detailed descriptions: +# See http://wiki.nginx.org/CoreModule + +# Sets user/group of worker processes. If group is not specified, group is assumed +# to be the same as user. Syntax: user user [group] +user www-data; + +# nginx has the ability to use more than one worker process +worker_processes 4; + +# The pid-file. It can be used for the kill-command to send signals to nginx. +# Example: To reload the config: kill -HUP `cat /var/log/nginx.pid` +pid /var/run/nginx.pid; + +# See http://wiki.nginx.org/HttpEventsModule +events { + worker_connections 1024; + # multi_accept on; +} + +# See /usr/share/doc/nginx/examples/http +http { +} + +# See /usr/share/doc/nginx/examples/mail +mail { +} diff -Nru nginx-0.5.33/debian/help/examples/nginx_modsite nginx-0.8.53/debian/help/examples/nginx_modsite --- nginx-0.5.33/debian/help/examples/nginx_modsite 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/debian/help/examples/nginx_modsite 2011-02-26 14:04:40.000000000 +0000 @@ -0,0 +1,162 @@ +#!/bin/bash + +## +# File: +# nginx_modsite +# Description: +# Provides a basic script to automate enabling and disabling websites found +# in the default configuration directories: +# /etc/nginx/sites-available and /etc/nginx/sites-enabled +# For easy access to this script, copy it into the directory: +# /usr/local/sbin +# Run this script without any arguments or with -h or --help to see a basic +# help dialog displaying all options. +## + +# Copyright (C) 2010 Michael Lustfield + +# 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 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 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. + +## +# Default Settings +## + +NGINX_CONF_FILE="$(awk -F= -v RS=' ' '/conf-path/ {print $2}' <<< $(nginx -V 2>&1))" +NGINX_CONF_DIR="${NGINX_CONF_FILE%/*}" +NGINX_SITES_AVAILABLE="$NGINX_CONF_DIR/sites-available" +NGINX_SITES_ENABLED="$NGINX_CONF_DIR/sites-enabled" +SELECTED_SITE="$2" + +## +# Script Functions +## + +ngx_enable_site() { + [[ ! "$SELECTED_SITE" ]] && + ngx_select_site "not_enabled" + + [[ ! -e "$NGINX_SITES_AVAILABLE/$SELECTED_SITE" ]] && + ngx_error "Site does not appear to exist." + [[ -e "$NGINX_SITES_ENABLED/$SELECTED_SITE" ]] && + ngx_error "Site appears to already be enabled" + + ln -sf "$NGINX_SITES_AVAILABLE/$SELECTED_SITE" -T "$NGINX_SITES_ENABLED/$SELECTED_SITE" + ngx_reload +} + +ngx_disable_site() { + [[ ! "$SELECTED_SITE" ]] && + ngx_select_site "is_enabled" + + [[ ! -e "$NGINX_SITES_AVAILABLE/$SELECTED_SITE" ]] && + ngx_error "Site does not appear to be \'available\'. - Not Removing" + [[ ! -e "$NGINX_SITES_ENABLED/$SELECTED_SITE" ]] && + ngx_error "Site does not appear to be enabled." + + rm -f "$NGINX_SITES_ENABLED/$SELECTED_SITE" + ngx_reload +} + +ngx_list_site() { + echo "Available sites:" + ngx_sites "available" + echo "Enabled Sites" + ngx_sites "enabled" +} + +## +# Helper Functions +## + +ngx_select_site() { + sites_avail=($NGINX_SITES_AVAILABLE/*) + sa="${sites_avail[@]##*/}" + sites_en=($NGINX_SITES_ENABLED/*) + se="${sites_en[@]##*/}" + + case "$1" in + not_enabled) sites=$(comm -13 <(printf "%s\n" $se) <(printf "%s\n" $sa));; + is_enabled) sites=$(comm -12 <(printf "%s\n" $se) <(printf "%s\n" $sa));; + esac + + ngx_prompt "$sites" +} + +ngx_prompt() { + sites=($1) + i=0 + + echo "SELECT A WEBSITE:" + for site in ${sites[@]}; do + echo -e "$i:\t${sites[$i]}" + ((i++)) + done + + read -p "Enter number for website: " i + SELECTED_SITE="${sites[$i]}" +} + +ngx_sites() { + case "$1" in + available) dir="$NGINX_SITES_AVAILABLE";; + enabled) dir="$NGINX_SITES_ENABLED";; + esac + + for file in $dir/*; do + echo -e "\t${file#*$dir/}" + done +} + +ngx_reload() { + read -p "Would you like to reload the Nginx configuration now? (Y/n) " reload + [[ "$reload" != "n" && "$reload" != "N" ]] && invoke-rc.d nginx reload +} + +ngx_error() { + echo -e "${0##*/}: ERROR: $1" + [[ "$2" ]] && ngx_help + exit 1 +} + +ngx_help() { + echo "Usage: ${0##*/} [options]" + echo "Options:" + echo -e "\t<-e|--enable> \tEnable site" + echo -e "\t<-d|--disable> \tDisable site" + echo -e "\t<-l|--list>\t\tList sites" + echo -e "\t<-h|--help>\t\tDisplay help" + echo -e "\n\tIf is left out a selection of options will be presented." + echo -e "\tIt is assumed you are using the default sites-enabled and" + echo -e "\tsites-disabled located at $NGINX_CONF_DIR." +} + +## +# Core Piece +## + +case "$1" in + -e|--enable) ngx_enable_site;; + -d|--disable) ngx_disable_site;; + -l|--list) ngx_list_site;; + -h|--help) ngx_help;; + *) ngx_error "No Options Selected" 1; ngx_help;; +esac diff -Nru nginx-0.5.33/debian/help/examples/virtual_hosts nginx-0.8.53/debian/help/examples/virtual_hosts --- nginx-0.5.33/debian/help/examples/virtual_hosts 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/debian/help/examples/virtual_hosts 2011-02-26 14:04:40.000000000 +0000 @@ -0,0 +1,155 @@ +## +# File: +# virtual_hosts +# Description: +# This file is meant to deliver a basic understanding of server blocks. +## + +## +# You should look at the following URL's in order to grasp a solid understanding +# of Nginx configuration files in order to fully unleash the power of Nginx. +# http://wiki.nginx.org/Pitfalls +# http://wiki.nginx.org/Configuration +## + +## +# Every "virtual host" that you serve will need to be in its own server block. +# +# server { +# ... +# } +# +# After reading this file, you should understand the structure of server blocks +# and be able to understand how to modify them to your needs. +## + +server { + + # The listen directive is only needed if this server block: + # needs to listen for IPv6 + # needs to listen on another port + # If you need to listen for IPv6 then both of the following lines can + # be included. + # DO NOT listen for both SSL and non-SSL in the same server block. + #listen 80; ## listen for ipv4; this line is default and implied + #listen [::]:80 default ipv6only=on; ## listen for ipv6 + + # root specifies the document root for the requests + root /usr/share/nginx/www; + + # index specifies the list of files (in order) to be tried in the event + # no file is requested in the URI. + index index.html index.htm; + + # Make site accessible from http://localhost/ + server_name localhost; + + # Unless you run everything as a proxy, you will want to have a root + # location block. This example controls how files are requested. + location / { + # First attempt to serve request as file, then as directory, + # then fall back to index.html. /index.html would normally be + # front end controller pattern for handling "clean url's" in + # a CMS such as Drupal or Wordpress. + try_files $uri $uri/ /index.html; + } + + # This location block would server any requests for /doc as well + # as anything below it. + location /doc { + # root changes the root directory for these requests + root /usr/share; + # autoindex on allows these request to display directory listings + # if a directory was requested + autoindex on; + # We'll allow these requests for localhost + allow 127.0.0.1; + # Anyone outside is forbidden + deny all; + } + + # This location block would serve any requests for /images as well + # as anything below it. + location /images { + # This is the same as /doc except we don't allow indexes + root /usr/share; + autoindex off; + allow 127.0.0.1; + deny all; + } + + # This will serve the file 404.html in the event the request is not found. + error_page 404 /404.html; + + # redirect server error pages to the static page /50x.html + # This will hit if the server generates a 500, 502, 503, or 504 status code + error_page 500 502 503 504 /50x.html; + + # This location block isn't actually needed because our root for the + # server block is the same directory. However, this could be used to + # have a central directory for all error html files. + location = /50x.html { + root /usr/share/nginx/www; + } + + # proxy the PHP scripts to Apache listening on 127.0.0.1:80 + # If you're going to proxy to Apache, then just push the whole + # request to it. You'll generally have better performance with + # a dedicated php listener (fastcgi: php-cgi, php-fpm). + #location ~ \.php$ { + # proxy_pass http://127.0.0.1; + #} + + # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 + # This is used for passing to php-cgi and php-fpm. + # For more information see /usr/share/doc/nginx/examples/php + location ~ \.php$ { + # This is where the php socket is listening. + fastcgi_pass 127.0.0.1:9000; + fastcgi_index index.php; + include fastcgi_params; + } + + # deny access to .htaccess, .htpasswd, and .htgroup files + location ~ /\.ht { + deny all; + } +} + + +# another virtual host using mix of IP-, name-, and port-based configuration +server { + listen 8000; + listen somename:8080; + server_name somename alias another.alias; + root html; + index index.html index.htm; + + location / { + try_files $uri $uri/ /index.html; + } +} + + +# HTTPS server +server { + listen 443; + server_name localhost; + + root html; + index index.html index.htm; + + ssl on; + ssl_certificate cert.pem; + ssl_certificate_key cert.key; + + ssl_session_timeout 5m; + + ssl_protocols SSLv3 TLSv1; + ssl_ciphers ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv3:+EXP; + ssl_prefer_server_ciphers on; + + location / { + try_files $uri $uri/ /index.html; + } +} diff -Nru nginx-0.5.33/debian/help/examples/wordpress nginx-0.8.53/debian/help/examples/wordpress --- nginx-0.5.33/debian/help/examples/wordpress 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/debian/help/examples/wordpress 2011-02-26 14:04:40.000000000 +0000 @@ -0,0 +1,74 @@ +## +# File: +# wordpress +# Description: +# This file is meant to offer a basic guide to get a Wordpress site up and +# running on Nginx. This file should be almost drop-in if the user is able +# to understand the three lines that need to be changed. +## + +server { + + # This is the URI of your website. + server_name domain.com; + + # This is the root of the Wordpress directory. + root /var/www/wordpress; + + # In some cases a favicon does not exist but this is not something you + # normally need to worry about. It's also a microscopic image and will + # just clutter the logs. + location = /favicon.ico { + log_not_found off; + access_log off; + } + + # This is for the robots.txt file used by search engines. + location = /robots.txt { + # If you have one, you want to allow them access to it. + allow all; + # If you don't have one, you don't want to fill your logs with + # not found errors. + log_not_found off; + access_log off; + } + + # This location block protects against a known attack. It happens if + # the attacker uploads a non-php file and attempts to run it as a + # php file on the server. + location ~ \..*/.*\.php$ { + return 403; + } + + # This is our primary location block. The try_files directive will + # attempt to serve the data in the order listed. First try the exact + # request (such as an image or text file). If it doesn't exist, see if + # the directory exists. If not, then we move to the last options which + # passes the request to /index.php with the requested query. + location / { + try_files $uri $uri/ /index.php?q=$uri&$args; + } + + # If a PHP file is served, this block will handle the request. This block + # works on the assumption you are using php-cgi listening on /tmp/phpcgi.socket. + # Please see the php example (usr/share/doc/nginx/exmaples/php) for more + # information about setting up PHP. + # NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini + location ~ \.php$ { + fastcgi_split_path_info ^(.+\.php)(/.+)$; + include fastcgi_params; + # Intercepting errors will cause PHP errors to appear in Nginx logs + fastcgi_intercept_errors on; + fastcgi_pass unix:/tmp/phpcgi.socket; + } + + # As mentioned above, Nignx is king of static. If we're serving a static + # file that ends with one of the following extensions, it is best to set + # a very high expires time. This will generate fewer requests for the + # file. These requests will be logged if found, but not if they don't + # exist. + location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ { + expires max; + log_not_found off; + } +} diff -Nru nginx-0.5.33/debian/init.d nginx-0.8.53/debian/init.d --- nginx-0.5.33/debian/init.d 2011-02-26 14:04:40.000000000 +0000 +++ nginx-0.8.53/debian/init.d 2011-02-26 14:04:40.000000000 +0000 @@ -2,8 +2,8 @@ ### BEGIN INIT INFO # Provides: nginx -# Required-Start: $all -# Required-Stop: $all +# Required-Start: $local_fs $remote_fs $network $syslog +# Required-Stop: $local_fs $remote_fs $network $syslog # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: starts the nginx web server @@ -15,46 +15,72 @@ NAME=nginx DESC=nginx -test -x $DAEMON || exit 0 - # Include nginx defaults if available if [ -f /etc/default/nginx ] ; then . /etc/default/nginx fi +test -x $DAEMON || exit 0 + set -e +. /lib/lsb/init-functions + +test_nginx_config() { + if $DAEMON -t $DAEMON_OPTS >/dev/null 2>&1 + then + return 0 + else + $DAEMON -t $DAEMON_OPTS + return $? + fi +} + case "$1" in start) echo -n "Starting $DESC: " + test_nginx_config start-stop-daemon --start --quiet --pidfile /var/run/$NAME.pid \ - --exec $DAEMON -- $DAEMON_OPTS + --exec $DAEMON -- $DAEMON_OPTS || true echo "$NAME." ;; stop) echo -n "Stopping $DESC: " start-stop-daemon --stop --quiet --pidfile /var/run/$NAME.pid \ - --exec $DAEMON + --exec $DAEMON || true echo "$NAME." ;; restart|force-reload) echo -n "Restarting $DESC: " start-stop-daemon --stop --quiet --pidfile \ - /var/run/$NAME.pid --exec $DAEMON + /var/run/$NAME.pid --exec $DAEMON || true sleep 1 + test_nginx_config start-stop-daemon --start --quiet --pidfile \ - /var/run/$NAME.pid --exec $DAEMON -- $DAEMON_OPTS + /var/run/$NAME.pid --exec $DAEMON -- $DAEMON_OPTS || true echo "$NAME." ;; reload) - echo -n "Reloading $DESC configuration: " - start-stop-daemon --stop --signal HUP --quiet --pidfile /var/run/$NAME.pid \ - --exec $DAEMON - echo "$NAME." - ;; + echo -n "Reloading $DESC configuration: " + test_nginx_config + start-stop-daemon --stop --signal HUP --quiet --pidfile /var/run/$NAME.pid \ + --exec $DAEMON || true + echo "$NAME." + ;; + configtest) + echo -n "Testing $DESC configuration: " + if test_nginx_config + then + echo "$NAME." + else + exit $? + fi + ;; + status) + status_of_proc -p /var/run/$NAME.pid "$DAEMON" nginx && exit 0 || exit $? + ;; *) - N=/etc/init.d/$NAME - echo "Usage: $N {start|stop|restart|reload|force-reload}" >&2 + echo "Usage: $NAME {start|stop|restart|reload|force-reload|status|configtest}" >&2 exit 1 ;; esac diff -Nru nginx-0.5.33/debian/NEWS.Debian nginx-0.8.53/debian/NEWS.Debian --- nginx-0.5.33/debian/NEWS.Debian 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/debian/NEWS.Debian 2011-02-26 14:04:40.000000000 +0000 @@ -0,0 +1,50 @@ +nginx (0.8.53-1) unstable; urgency=low + + As stated by upstream, the 0.7.x branch is consedered legacy and 0.8.x will + be the new stable branch. For this reason, the nginx maintainers decided to + upload 0.8.53 to unstable. + + -- Kartik Mistry Fri, 26 Nov 2010 19:42:09 +0530 + +nginx (0.7.59-1) unstable; urgency=low + + As stated by upstream, the 0.6.x branch is consedered legacy and 0.7.x will + be the new stable branch. For this reason, the nginx maintainers decided to + upload 0.7.59 to unstable. + + Should you get the following error while starting nginx: + + could not build the server_names_hash, you should increase + server_names_hash_bucket_size: 32 + + Please add the following parameter to your nginx.conf: + + server_names_hash_bucket_size 100; + + Where 100 is the size of your server names hash bucket. For more information + about this option, please read the following resources: + + http://wiki.nginx.org/NginxHttpCoreModule#server_names_hash_bucket_size + http://thread.gmane.org/gmane.comp.web.nginx.english/820/focus=821 + http://thread.gmane.org/gmane.comp.web.nginx.english/985/focus=989 + + -- Fabio Tranchitella Sun, 31 May 2009 18:30:10 +0200 + +nginx (0.6.30-2) unstable; urgency=low + + As of May 4th., nginx 0.5.x branch is considered legacy and 0.6.x will + be the new stable branch. The announcement was made by Igor Sysoev when + releasing the last 0.5.x version, nginx 0.5.36. + + Debian, the universal operating system, has provided binary packages for + both 0.5 and 0.6 branches in unstable and experimental, and will now offer + only 0.6 packages in the unstable distribution, starting with the + 0.6.30-1 package. + + In the future, Debian will also provide experimental packages for the + next testing branch of nginx, at the moment upstream announces it. + + Should you have any problem with nginx in Debian, please file a bug in + the Debian Bug Tracking System. + + -- Fabio Tranchitella Mon, 12 May 2008 14:24:53 +0200 diff -Nru nginx-0.5.33/debian/nginx.1 nginx-0.8.53/debian/nginx.1 --- nginx-0.5.33/debian/nginx.1 2011-02-26 14:04:40.000000000 +0000 +++ nginx-0.8.53/debian/nginx.1 2011-02-26 14:04:40.000000000 +0000 @@ -1,47 +1,47 @@ .TH "nginx" "1" "" "" "" .SH "NAME" nginx \- small, but very powerful and efficient web server -.br - -.br .SH "SYNOPSIS" -\fBnginx\fR [options] -.br - -.br +\fBnginx\fR [\fIoptions\fR] <\fIconfiguration file\fR> .SH "DESCRIPTION" -\fBnginx\fR is a server that can be used as standalone HTTP server and as -a reverse proxy server before some Apache or another big server to reduce +.PP +\fBnginx\fR is a server that can be used as standalone HTTP server and as +a reverse proxy server before some Apache or another big server to reduce load to backend servers by many concurrent HTTP\-sessions. -.br - -.br .SH "OPTIONS" +.TP A summary of options is included below: -.br - -.br -\fB\-c\fR -Specifies a particular configuration file for nginx to load. -.br +.TP +\fB\-?\fR,\fB\-h\fR +Show this help. +.TP \fB\-v\fR -Show version of program. -.br +Show version and exit. +.TP +\fB\-V\fR +Show version and configure options then exit. +.TP +\fB\-s\fR \fIsignal\fR +Send signal to a master process: stop, quit, reopen, reload. +.TP +\fB\-p\fR \fIprefix\fR +Set prefix path. +.TP +\fB\-g\fR \fIdirectives\fR +Set global directives out of configuration file. +.TP +\fB\-c\fR <\fIconfiguration file\fR> +Specifies a particular configuration file for nginx to load. +.TP \fB\-t\fR -Tests nginx configuration -.br -.br - -.br -You can find further information about this webserver in http://nginx.net/docs_en.txt. -.br - -.br +Tests nginx configuration and exit. +.TP +.SH "SEE ALSO" +Website: +.TP .SH "AUTHORS" -\fBnginx\fR was written by Igor Sysoev. -.br - -.br -This manual page was written by Jose Parrella , -for the Debian project (but may be used by others). - +\fBnginx\fR was written by Igor Sysoev . +.TP +This manual page was written by Jose Parrella and +Kartik Mistry , for the Debian project (but may be used +by others). diff -Nru nginx-0.5.33/debian/nginx.dirs nginx-0.8.53/debian/nginx.dirs --- nginx-0.5.33/debian/nginx.dirs 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/debian/nginx.dirs 2011-02-26 14:04:40.000000000 +0000 @@ -0,0 +1,9 @@ +usr/sbin +etc/nginx +etc/nginx/sites-available +etc/nginx/sites-enabled +etc/nginx/conf.d +etc/ufw/applications.d +usr/share/nginx +var/log/nginx +var/lib/nginx diff -Nru nginx-0.5.33/debian/nginx-doc.docs nginx-0.8.53/debian/nginx-doc.docs --- nginx-0.5.33/debian/nginx-doc.docs 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/debian/nginx-doc.docs 2011-02-26 14:04:40.000000000 +0000 @@ -0,0 +1,3 @@ +README +debian/NEWS.Debian +debian/help/docs/* diff -Nru nginx-0.5.33/debian/nginx-doc.examples nginx-0.8.53/debian/nginx-doc.examples --- nginx-0.5.33/debian/nginx-doc.examples 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/debian/nginx-doc.examples 2011-02-26 14:04:40.000000000 +0000 @@ -0,0 +1 @@ +debian/help/examples/* diff -Nru nginx-0.5.33/debian/nginx.install nginx-0.8.53/debian/nginx.install --- nginx-0.5.33/debian/nginx.install 2011-02-26 14:04:40.000000000 +0000 +++ nginx-0.8.53/debian/nginx.install 2011-02-26 14:04:40.000000000 +0000 @@ -1,4 +1,5 @@ objs/nginx usr/sbin -html/* var/www/nginx-default debian/conf/* etc/nginx - +debian/NEWS.Debian usr/share/doc/nginx +debian/ufw.profile etc/ufw/applications.d/nginx +html/* usr/share/nginx/www/ diff -Nru nginx-0.5.33/debian/nginx.logrotate nginx-0.8.53/debian/nginx.logrotate --- nginx-0.5.33/debian/nginx.logrotate 2011-02-26 14:04:40.000000000 +0000 +++ nginx-0.8.53/debian/nginx.logrotate 2011-02-26 14:04:40.000000000 +0000 @@ -7,6 +7,11 @@ notifempty create 640 root adm sharedscripts + prerotate + if [ -d /etc/logrotate.d/httpd-prerotate ]; then \ + run-parts /etc/logrotate.d/httpd-prerotate; \ + fi; \ + endscript postrotate [ ! -f /var/run/nginx.pid ] || kill -USR1 `cat /var/run/nginx.pid` endscript diff -Nru nginx-0.5.33/debian/patches/dlopen.diff nginx-0.8.53/debian/patches/dlopen.diff --- nginx-0.5.33/debian/patches/dlopen.diff 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/debian/patches/dlopen.diff 2011-02-26 14:04:40.000000000 +0000 @@ -0,0 +1,37 @@ +Description: Patch to remove unnecessary link to libdl +Debian Bug: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=540599 +Author: Fabio Tranchitella + +Index: 0.8/auto/os/features +=================================================================== +--- 0.8.orig/auto/os/features 2010-10-31 07:32:20.408338001 +0000 ++++ 0.8/auto/os/features 2010-10-31 07:32:26.528338002 +0000 +@@ -252,28 +252,6 @@ + . auto/feature + + +-ngx_feature="dlopen()" +-ngx_feature_name= +-ngx_feature_run=no +-ngx_feature_incs="#include " +-ngx_feature_path= +-ngx_feature_libs= +-ngx_feature_test="dlopen(NULL, 0)" +-. auto/feature +- +- +-if [ $ngx_found != yes ]; then +- +- ngx_feature="dlopen() in libdl" +- ngx_feature_libs="-ldl" +- . auto/feature +- +- if [ $ngx_found = yes ]; then +- NGX_LIBDL="-ldl" +- fi +-fi +- +- + ngx_feature="sched_yield()" + ngx_feature_name="NGX_HAVE_SCHED_YIELD" + ngx_feature_run=no diff -Nru nginx-0.5.33/debian/patches/nginx-echo.diff nginx-0.8.53/debian/patches/nginx-echo.diff --- nginx-0.5.33/debian/patches/nginx-echo.diff 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/debian/patches/nginx-echo.diff 2011-02-26 14:04:40.000000000 +0000 @@ -0,0 +1,12298 @@ +Description: Patch for nginx echo module +Author: Michael Lustfield + +Index: 0.8/modules/nginx-echo/LICENSE +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ 0.8/modules/nginx-echo/LICENSE 2010-10-31 07:35:23.648338001 +0000 +@@ -0,0 +1,30 @@ ++Copyright (c) 2009, Taobao Inc., Alibaba Group ( http://www.taobao.com ). ++Copyright (c) 2009, agentzh . ++All rights reserved. ++ ++Redistribution and use in source and binary forms, with or without ++modification, are permitted provided that the following conditions ++are met: ++ ++ * Redistributions of source code must retain the above copyright ++ notice, this list of conditions and the following disclaimer. ++ ++ * 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. ++ ++ * Neither the name of the Taobao Inc. nor the names of its ++ contributors may be used to endorse or promote products derived from ++ this software without specific prior written permission. ++ ++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT ++HOLDER 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. +Index: 0.8/modules/nginx-echo/README +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ 0.8/modules/nginx-echo/README 2010-10-31 07:35:47.698338000 +0000 +@@ -0,0 +1,1740 @@ ++Name ++ ngx_echo - Brings "echo", "sleep", "time", "exec" and more shell-style ++ goodies to Nginx config file. ++ ++ *This module is not distributed with the Nginx source.* See the ++ installation instructions. ++ ++Version ++ This document describes echo-nginx-module v0.34 ++ () released ++ on September 14, 2010. ++ ++Synopsis ++ location /hello { ++ echo "hello, world!"; ++ } ++ ++ location /hello { ++ echo -n "hello, " ++ echo "world!"; ++ } ++ ++ location /timed_hello { ++ echo_reset_timer; ++ echo hello world; ++ echo "'hello world' takes about $echo_timer_elapsed sec."; ++ echo hiya igor; ++ echo "'hiya igor' takes about $echo_timer_elapsed sec."; ++ } ++ ++ location /echo_with_sleep { ++ echo hello; ++ echo_flush; # ensure the client can see previous output immediately ++ echo_sleep 2.5; # in sec ++ echo world; ++ } ++ ++ # in the following example, accessing /echo yields ++ # hello ++ # world ++ # blah ++ # hiya ++ # igor ++ location /echo { ++ echo_before_body hello; ++ echo_before_body world; ++ proxy_pass $scheme://127.0.0.1:$server_port$request_uri/more; ++ echo_after_body hiya; ++ echo_after_body igor; ++ } ++ location /echo/more { ++ echo blah; ++ } ++ ++ # the output of /main might be ++ # hello ++ # world ++ # took 0.000 sec for total. ++ # and the whole request would take about 2 sec to complete. ++ location /main { ++ echo_reset_timer; ++ ++ # subrequests in parallel ++ echo_location_async /sub1; ++ echo_location_async /sub2; ++ ++ echo "took $echo_timer_elapsed sec for total."; ++ } ++ location /sub1 { ++ echo_sleep 2; ++ echo hello; ++ } ++ location /sub2 { ++ echo_sleep 1; ++ echo world; ++ } ++ ++ # the output of /main might be ++ # hello ++ # world ++ # took 3.003 sec for total. ++ # and the whole request would take about 3 sec to complete. ++ location /main { ++ echo_reset_timer; ++ ++ # subrequests in series (chained by CPS) ++ echo_location /sub1; ++ echo_location /sub2; ++ ++ echo "took $echo_timer_elapsed sec for total."; ++ } ++ location /sub1 { ++ echo_sleep 2; ++ echo hello; ++ } ++ location /sub2 { ++ echo_sleep 1; ++ echo world; ++ } ++ ++ # Accessing /dup gives ++ # ------ END ------ ++ location /dup { ++ echo_duplicate 3 "--"; ++ echo_duplicate 1 " END "; ++ echo_duplicate 3 "--"; ++ echo; ++ } ++ ++ # /bighello will generate 1000,000,000 hello's. ++ location /bighello { ++ echo_duplicate 1000_000_000 'hello'; ++ } ++ ++ # echo back the client request ++ location /echoback { ++ echo_duplicate 1 $echo_client_request_headers; ++ echo "\r"; ++ ++ echo_read_request_body; ++ ++ echo_request_body; ++ } ++ ++ # GET /multi will yields ++ # querystring: foo=Foo ++ # method: POST ++ # body: hi ++ # content length: 2 ++ # /// ++ # querystring: bar=Bar ++ # method: PUT ++ # body: hello ++ # content length: 5 ++ # /// ++ location /multi { ++ echo_subrequest_async POST '/sub' -q 'foo=Foo' -b 'hi'; ++ echo_subrequest_async PUT '/sub' -q 'bar=Bar' -b 'hello'; ++ } ++ location /sub { ++ echo "querystring: $query_string"; ++ echo "method: $echo_request_method"; ++ echo "body: $echo_request_body"; ++ echo "content length: $http_content_length"; ++ echo '///'; ++ } ++ ++ # GET /merge?/foo.js&/bar/blah.js&/yui/baz.js will merge the .js resources together ++ location /merge { ++ default_type 'text/javascript'; ++ echo_foreach_split '&' $query_string; ++ echo "/* JS File $echo_it */"; ++ echo_location_async $echo_it; ++ echo; ++ echo_end; ++ } ++ ++ # accessing /if?val=abc yields the "hit" output ++ # while /if?val=bcd yields "miss": ++ location ^~ /if { ++ set $res miss; ++ if ($arg_val ~* '^a') { ++ set $res hit; ++ echo $res; ++ } ++ echo $res; ++ } ++ ++Description ++ This module wraps lots of Nginx internal APIs for streaming input and ++ output, parallel/sequential subrequests, timers and sleeping, as well as ++ various meta data accessing. ++ ++ Basically it provides various utilities that help testing and debugging ++ of other modules by trivially emulating different kinds of faked ++ subrequest locations. ++ ++ People will also find it useful in real-world applications that need to ++ ++ 1. serve static contents directly from memory (loading from the Nginx ++ config file). ++ ++ 2. wrap the upstream response with custom header and footer (kinda like ++ the addition module but with contents read directly from the config ++ file and Nginx variables). ++ ++ 3. merge contents of various "Nginx locations" (i.e., subrequests) ++ together in a single main request (using echo_location and its ++ friends). ++ ++ This is a special dual-role module that can *lazily* serve as a content ++ handler or register itself as an output filter only upon demand. By ++ default, this module does not do anything at all. ++ ++ Use of any of this module's directives (no matter content handler ++ directives or filter directives) will force the chunked encoding to be ++ used for the HTTP response due to the streaming nature of this module ++ (unless HTTP 1.0 is enforced by the client and the Content-Length header ++ will be set to the size of the first handler directive that generates ++ contents). ++ ++ Technially, this module has also demonstrated the following techniques ++ that might be helpful for module writers: ++ ++ 1. Issue parallel subreqeusts directly from content handler. ++ ++ 2. Issue chained subrequests directly from content handler, by passing ++ continuation along the subrequest chain. ++ ++ 3. Issue subrequests with all HTTP 1.1 methods and even an optional ++ faked HTTP request body. ++ ++ 4. Interact with the Nginx event model directly from content handler ++ using custom events and timers, and resume the content handler back ++ if necessary. ++ ++ 5. Dual-role module that can (lazily) serve as a content handler or an ++ output filter or both. ++ ++ 6. Nginx config file variable creation and interpolation. ++ ++ 7. Streaming output control using output_chain, flush and its friends. ++ ++ 8. Read client request body from the content handler, and returns back ++ (asynchronously) to the content handler after completion. ++ ++ 9. Use Perl-based declarative test suite to drive the development of ++ Nginx C modules. ++ ++Content Handler Directives ++ Use of the following directives register this module to the current ++ Nginx location as a content handler. If you want to use another module, ++ like the standard proxy module, as the content handler, use the filter ++ directives provided by this module. ++ ++ All the content handler directives can be mixed together in a single ++ Nginx location and they're supposed to run sequentially just as in the ++ Bash scripting language. ++ ++ Every content handler directive supports variable interpolation in its ++ arguments (if any). ++ ++ The MIME type set by the standard default_type directive is respected by ++ this module, as in: ++ ++ location /hello { ++ default_type text/plain; ++ echo hello; ++ } ++ ++ Then on the client side: ++ ++ $ curl -I 'http://localhost/echo' ++ HTTP/1.1 200 OK ++ Server: nginx/0.8.20 ++ Date: Sat, 17 Oct 2009 03:40:19 GMT ++ Content-Type: text/plain ++ Connection: keep-alive ++ ++ Since the v0.22 release, all of the directives are allowed in the ++ rewrite module's if directive block, for instance: ++ ++ location ^~ /if { ++ set $res miss; ++ if ($arg_val ~* '^a') { ++ set $res hit; ++ echo $res; ++ } ++ echo $res; ++ } ++ ++ echo ++ syntax: *echo [options] ...* ++ ++ default: *no* ++ ++ context: *location* ++ ++ Sends arguments joined by spaces, along with a trailing newline, out to ++ the client. ++ ++ Note that the data might be buffered by Nginx's underlying buffer. To ++ force the output data flushed immediately, use the echo_flush command ++ just after "echo", as in ++ ++ echo hello world; ++ echo_flush; ++ ++ When no argument is specified, *echo* emits the trailing newline alone, ++ just like the *echo* command in shell. ++ ++ Variables may appear in the arguments. An example is ++ ++ echo The current request uri is $request_uri; ++ ++ where $request_uri is a variable exposed by the [[NginxHttpCoreModule]]. ++ ++ This command can be used multiple times in a single location ++ configuration, as in ++ ++ location /echo { ++ echo hello; ++ echo world; ++ } ++ ++ The output on the client side looks like this ++ ++ $ curl 'http://localhost/echo' ++ hello ++ world ++ ++ Special characters like newlines ("\n") and tabs ("\t") can be escaped ++ using C-style escaping sequences. But a notable exception is the dollar ++ sign ("$"). As of Nginx 0.8.20, there's still no clean way to esacpe ++ this characters. (A work-around might be to use a $echo_dollor variable ++ that is always evaluated to the constant "$" character. This feature ++ will possibly be introduced in a future version of this module.) ++ ++ As of the echo v0.28 release, one can suppress the trailing newline ++ character in the output by using the "-n" option, as in ++ ++ location /echo { ++ echo -n "hello, "; ++ echo "world"; ++ } ++ ++ Accessing "/echo" gives ++ ++ $ curl 'http://localhost/echo' ++ hello, world ++ ++ Leading "-n" in variable values won't take effect and will be emitted ++ literally, as in ++ ++ location /echo { ++ set $opt -n; ++ echo $opt "hello,"; ++ echo "world"; ++ } ++ ++ This gives the following output ++ ++ $ curl 'http://localhost/echo' ++ -n hello, ++ world ++ ++ One can output leading "-n" literals and other options using the special ++ "--" option like this ++ ++ location /echo { ++ echo -- -n is an option; ++ } ++ ++ which yields ++ ++ $ curl 'http://localhost/echo' ++ -n is an option ++ ++ echo_duplicate ++ syntax: *echo_duplicate * ++ ++ default: *no* ++ ++ context: *location* ++ ++ Outputs duplication of a string indicated by the second argument, using ++ the times specified in the first argument. ++ ++ For instance, ++ ++ location /dup { ++ echo_duplicate 3 "abc"; ++ } ++ ++ will lead to an output of "abcabcabc". ++ ++ Underscores are allowed in the count number, just like in Perl. For ++ example, to emit 1000,000,000 instances of "hello, world": ++ ++ location /many_hellos { ++ echo_duplicate 1000_000_000 "hello, world"; ++ } ++ ++ The "count" argument could be zero, but not negative. The second ++ "string" argument could be an empty string ("") likewise. ++ ++ Unlike the echo directive, no trailing newline is appended to the ++ result. So it's possible to "abuse" this directive as a ++ no-trailing-newline version of echo by using "count" 1, as in ++ ++ location /echo_art { ++ echo_duplicate 2 '---'; ++ echo_duplicate 1 ' END '; # we don't want a trailing newline here ++ echo_duplicate 2 '---'; ++ echo; # we want a trailing newline here... ++ } ++ ++ You get ++ ++ ------ END ------ ++ ++ This directive was first introduced in version 0.11. ++ ++ echo_flush ++ syntax: *echo_flush* ++ ++ default: *no* ++ ++ context: *location* ++ ++ Forces the data potentially buffered by underlying Nginx output filters ++ to send immediately to the client side via socket. ++ ++ Note that techically the command just emits a ngx_buf_t object with ++ "flush" slot set to 1, so certain weird third-party output filter module ++ could still block it before it reaches Nginx's (last) write filter. ++ ++ This directive does not take any argument. ++ ++ Consider the following example: ++ ++ location /flush { ++ echo hello; ++ ++ echo_flush; ++ ++ echo_sleep 1; ++ echo world; ++ } ++ ++ Then on the client side, using curl to access "/flush", you'll see the ++ "hello" line immediately, but only after 1 second, the last "world" ++ line. Without calling "echo_flush" in the example above, you'll most ++ likely see no output until 1 second is elapsed due to the internal ++ buffering of Nginx. ++ ++ This directive will fail to flush the output buffer in case of ++ subrequests get involved. Consider the following example: ++ ++ location /main { ++ echo_location_async /sub; ++ echo hello; ++ echo_flush; ++ } ++ location /sub { ++ echo_sleep 1; ++ } ++ ++ Then the client won't see "hello" appear even if "echo_flush" has been ++ executed before the subrequest to "/sub" has actually started executing. ++ The outputs of "/main" that are sent *after* echo_location_async will be ++ postponed and buffered firmly. ++ ++ This does *not* apply to outputs sent before the subrequest initiated. ++ For a modified version of the example given above: ++ ++ location /main { ++ echo hello; ++ echo_flush; ++ echo_location_async /sub; ++ } ++ location /sub { ++ echo_sleep 1; ++ } ++ ++ The client will immediately see "hello" before "/sub" enters sleeping. ++ ++ See also echo, echo_sleep, and echo_location_async. ++ ++ echo_sleep ++ syntax: *echo_sleep * ++ ++ default: *no* ++ ++ context: *location* ++ ++ Sleeps for the time period specified by the argument, which is in ++ seconds. ++ ++ This operation is non-blocking on server side, so unlike the ++ echo_blocking_sleep directive, it won't block the whole Nginx worker ++ process. ++ ++ The period might takes three digits after the decimal point and must be ++ greater than 0.001. ++ ++ An example is ++ ++ location /echo_after_sleep { ++ echo_sleep 1.234; ++ echo resumed!; ++ } ++ ++ Behind the scene, it sets up a per-request "sleep" ngx_event_t object, ++ and adds a timer using that custom event to the Nginx event model and ++ just waits for a timeout on that event. Because the "sleep" event is ++ per-request, this directive can work in parallel subrequests. ++ ++ echo_blocking_sleep ++ syntax: *echo_blocking_sleep * ++ ++ default: *no* ++ ++ context: *location* ++ ++ This is a blocking version of the echo_sleep directive. ++ ++ See the documentation of echo_sleep for more detail. ++ ++ Behind the curtain, it calls the ngx_msleep macro provided by the Nginx ++ core which maps to usleep on POSIX-compliant systems. ++ ++ Note that this directive will block the current Nginx worker process ++ completely while being executed, so never use it in production ++ environment. ++ ++ echo_reset_timer ++ syntax: *echo_reset_timer* ++ ++ default: *no* ++ ++ context: *location* ++ ++ Reset the timer begin time to *now*, i.e., the time when this command is ++ executed during request. ++ ++ The timer begin time is default to the starting time of the current ++ request and can be overridden by this directive, potentially multiple ++ times in a single location. For example: ++ ++ location /timed_sleep { ++ echo_sleep 0.03; ++ echo "$echo_timer_elapsed sec elapsed."; ++ ++ echo_reset_timer; ++ ++ echo_sleep 0.02; ++ echo "$echo_timer_elapsed sec elapsed."; ++ } ++ ++ The output on the client side might be ++ ++ $ curl 'http://localhost/timed_sleep' ++ 0.032 sec elapsed. ++ 0.020 sec elapsed. ++ ++ The actual figures you get on your side may vary a bit due to your ++ system's current activities. ++ ++ Invocation of this directive will force the underlying Nginx timer to ++ get updated to the current system time (regardless the timer resolution ++ specified elsewhere in the config file). Furthermore, references of the ++ $echo_timer_elapsed variable will also trigger timer update forcibly. ++ ++ See also echo_sleep and $echo_timer_elapsed. ++ ++ echo_read_request_body ++ Explicitly reads request body so that the $request_body variable will ++ always have non-empty values (unless the body is so big that it has been ++ saved by Nginx to a local temporary file). ++ ++ Note that this might not be the original client request body because the ++ current request might be a subrequest with a "artificial" body specified ++ by its parent. ++ ++ This directive does not generate any output itself, just like ++ echo_sleep. ++ ++ Here's an example for echo'ing back the original HTTP client request ++ (both headers and body are included): ++ ++ location /echoback { ++ echo_duplicate 1 $echo_client_request_headers; ++ echo "\r"; ++ echo_read_request_body; ++ echo $request_body; ++ } ++ ++ The content of "/echoback" looks like this on my side (I was using ++ Perl's LWP utility to access this location on the server): ++ ++ $ (echo hello; echo world) | lwp-request -m POST 'http://localhost/echoback' ++ POST /echoback HTTP/1.1 ++ TE: deflate,gzip;q=0.3 ++ Connection: TE, close ++ Host: localhost ++ User-Agent: lwp-request/5.818 libwww-perl/5.820 ++ Content-Length: 12 ++ Content-Type: application/x-www-form-urlencoded ++ ++ hello ++ world ++ ++ Because "/echoback" is the main request, $request_body holds the ++ original client request body. ++ ++ Before Nginx 0.7.56, it makes no sense to use this directive because ++ $request_body was first introduced in Nginx 0.7.58. ++ ++ This directive itself was first introduced in the echo module's v0.14 ++ release. ++ ++ echo_location_async ++ syntax: *echo_location_async []* ++ ++ default: *no* ++ ++ context: *location* ++ ++ Issue GET subrequest to the location specified (first argument) with ++ optional url arguments specified in the second argument. ++ ++ As of Nginx 0.8.20, the "location" argument does *not* support named ++ location, due to a limitation in the "ngx_http_subrequest" function. The ++ same is true for its brother, the echo_location directive. ++ ++ A very simple example is ++ ++ location /main { ++ echo_location_async /sub; ++ echo world; ++ } ++ location /sub { ++ echo hello; ++ } ++ ++ Accessing "/main" gets ++ ++ hello ++ world ++ ++ Calling multiple locations in parallel is also possible: ++ ++ location /main { ++ echo_reset_timer; ++ echo_location_async /sub1; ++ echo_location_async /sub2; ++ echo "took $echo_timer_elapsed sec for total."; ++ } ++ location /sub1 { ++ echo_sleep 2; # sleeps 2 sec ++ echo hello; ++ } ++ location /sub2 { ++ echo_sleep 1; # sleeps 1 sec ++ echo world; ++ } ++ ++ Accessing "/main" yields ++ ++ $ time curl 'http://localhost/main' ++ hello ++ world ++ took 0.000 sec for total. ++ ++ real 0m2.006s ++ user 0m0.000s ++ sys 0m0.004s ++ ++ You can see that the main handler "/main" does *not* wait the ++ subrequests "/sub1" and "/sub2" to complete and quickly goes on, hence ++ the "0.000 sec" timing result. The whole request, however takes ++ approximately 2 sec in total to complete because "/sub1" and "/sub2" run ++ in parallel (or "concurrently" to be more accurate). ++ ++ If you use echo_blocking_sleep in the previous example instead, then ++ you'll get the same output, but with 3 sec total response time, because ++ "blocking sleep" blocks the whole Nginx worker process. ++ ++ Locations can also take an optional querystring argument, for instance ++ ++ location /main { ++ echo_location_async /sub 'foo=Foo&bar=Bar'; ++ } ++ location /sub { ++ echo $arg_foo $arg_bar; ++ } ++ ++ Accessing "/main" yields ++ ++ $ curl 'http://localhost/main' ++ Foo Bar ++ ++ Querystrings is *not* allowed to be concatenated onto the "location" ++ argument with "?" directly, for example, "/sub?foo=Foo&bar=Bar" is an ++ invalid location, and shouldn't be fed as the first argument to this ++ directive. ++ ++ Due to an unknown bug in Nginx (it still exists in Nginx 0.8.20), the ++ standard SSI module is required to ensure that the contents of the ++ subrequests issued by this directive are correctly merged into the ++ output chains of the main one. Fortunately, the SSI module is enabled by ++ default during Nginx's "configure" process. ++ ++ If calling this directive without SSI module enabled, you'll get ++ truncated response without contents of any subrequests and get an alert ++ message in your Nginx's "error.log", like this: ++ ++ [alert] 24212#0: *1 the http output chain is empty, client: 127.0.0.1, ... ++ ++ Technically speaking, this directive is an example that Nginx content ++ handler issues one or more subrequests directly. AFAIK, the fancyindex ++ module () also ++ does such kind of things ;) ++ ++ Nginx named locations like @foo is *not* supported here. ++ ++ This directive is logically equivalent to the GET version of ++ echo_subrequest_async. For example, ++ ++ echo_location_async /foo 'bar=Bar'; ++ ++ is logically equivalent to ++ ++ echo_subrequest_async GET /foo -q 'bar=Bar'; ++ ++ But calling this directive is slightly faster than calling ++ echo_subrequest_async using "GET" because we don't have to parse the ++ HTTP method names like "GET" and options like "-q". ++ ++ This directive is first introduced in version 0.09 of this module and ++ requires at least Nginx 0.7.46. ++ ++ echo_location ++ syntax: *echo_location []* ++ ++ default: *no* ++ ++ context: *location* ++ ++ Just like the echo_location_async directive, but "echo_location" issues ++ subrequests *in series* rather than in parallel. That is, the content ++ handler directives following this directive won't be executed until the ++ subrequest issued by this directive completes. ++ ++ The final response body is almost always equivalent to the case when ++ echo_location_async is used instead, only if timing variables is used in ++ the outputs. ++ ++ Consider the following example: ++ ++ location /main { ++ echo_reset_timer; ++ echo_location /sub1; ++ echo_location /sub2; ++ echo "took $echo_timer_elapsed sec for total."; ++ } ++ location /sub1 { ++ echo_sleep 2; ++ echo hello; ++ } ++ location /sub2 { ++ echo_sleep 1; ++ echo world; ++ } ++ ++ The location "/main" above will take for total 3 sec to complete ++ (compared to 2 sec if echo_location_async is used instead here). Here's ++ the result in action on my machine: ++ ++ $ curl 'http://localhost/main' ++ hello ++ world ++ took 3.003 sec for total. ++ ++ real 0m3.027s ++ user 0m0.020s ++ sys 0m0.004s ++ ++ This directive is logically equivalent to the GET version of ++ echo_subrequest. For example, ++ ++ echo_location /foo 'bar=Bar'; ++ ++ is logically equivalent to ++ ++ echo_subrequest GET /foo -q 'bar=Bar'; ++ ++ But calling this directive is slightly faster than calling ++ echo_subrequest using "GET" because we don't have to parse the HTTP ++ method names like "GET" and options like "-q". ++ ++ Behind the scene, it creates an "ngx_http_post_subrequest_t" object as a ++ *continuation* and passes it into the "ngx_http_subrequest" function ++ call. Nginx will later reopen this "continuation" in the subrequest's ++ "ngx_http_finalize_request" function call. We resumes the execution of ++ the parent-request's content handler and starts to run the next ++ directive (command) if any. ++ ++ Nginx named locations like @foo is *not* supported here. ++ ++ This directive was first introduced in the release v0.12. ++ ++ See also echo_location_async for more details about the meaning of the ++ arguments. ++ ++ echo_subrequest_async ++ syntax: *echo_subrequest_async [-q ] ++ [-b ]* ++ ++ default: *no* ++ ++ context: *location* ++ ++ Initiate an asynchronous subrequest using HTTP method, an optional url ++ arguments (or querystring), and an option request body. ++ ++ This directive is very much like a generalized version of the ++ echo_location_async directive. ++ ++ Here's a small example demonstrating its usage: ++ ++ location /multi { ++ echo_subrequest_async POST '/sub' -q 'foo=Foo' -b 'hi'; ++ echo_subrequest_async PUT '/sub' -q 'bar=Bar' -b 'hello'; ++ } ++ location /sub { ++ echo "querystring: $query_string"; ++ echo "method: $echo_request_method"; ++ echo "body: $echo_request_body"; ++ echo "content length: $http_content_length"; ++ echo '///'; ++ } ++ ++ Then on the client side: ++ ++ $ curl 'http://localhost/multi' ++ querystring: foo=Foo ++ method: POST ++ body: hi ++ content length: 2 ++ /// ++ querystring: bar=Bar ++ method: PUT ++ body: hello ++ content length: 5 ++ /// ++ ++ Here's more funny example using the standard proxy module to handle the ++ subrequest: ++ ++ location /main { ++ echo_subrequest_async POST /sub -b 'hello, world'; ++ } ++ location /sub { ++ proxy_pass $scheme://127.0.0.1:$server_port/proxied; ++ } ++ location /proxied { ++ echo "method: $echo_request_method."; ++ ++ # we need to read body explicitly here...or $echo_request_body ++ # will evaluate to empty ("") ++ echo_read_request_body; ++ ++ echo "body: $echo_request_body."; ++ } ++ ++ Then on the client side, we can see that ++ ++ $ curl 'http://localhost/main' ++ method: POST. ++ body: hello, world. ++ ++ Nginx named locations like @foo is *not* supported here. ++ ++ This directive was first introduced in the release v0.15. ++ ++ See also the echo_subrequest and echo_location_async directives. ++ ++ echo_subrequest ++ syntax: *echo_subrequest_async [-q ] ++ [-b ]* ++ ++ default: *no* ++ ++ context: *location* ++ ++ This is the synchronous version of the echo_subrequest_async directive. ++ And just like echo_location, it does not block the Nginx worker process ++ (while echo_blocking_sleep does), rather, it uses continuation to pass ++ control along the subrequest chain. ++ ++ See echo_subrequest_async for more details. ++ ++ Nginx named locations like @foo is *not* supported here. ++ ++ This directive was first introduced in the release v0.15. ++ ++ echo_foreach_split ++ syntax: *echo_foreach_split * ++ ++ default: *no* ++ ++ context: *location* ++ ++ Split the second argument "string" using the delimiter specified in the ++ first argument, and then iterate through the resulting items. For ++ instance: ++ ++ location /loop { ++ echo_foreach_split ',' $arg_list; ++ echo "item: $echo_it"; ++ echo_end; ++ } ++ ++ Accessing /main yields ++ ++ $ curl 'http://localhost/loop?list=cat,dog,mouse' ++ item: cat ++ item: dog ++ item: mouse ++ ++ As seen in the previous example, this directive should always be ++ accompanied by an echo_end directive. ++ ++ Parallel "echo_foreach_split" loops are allowed, but nested ones are ++ currently forbidden. ++ ++ The "delimiter" argument could contain *multiple* arbitrary characters, ++ like ++ ++ echo_foreach_split '-a-' 'cat-a-dog-a-mouse'; ++ echo $echo_it; ++ echo_end; ++ ++ Logically speaking, this looping structure is just the "foreach" loop ++ combined with a "split" function call in Perl (using the previous ++ example): ++ ++ foreach (split ',', $arg_list) { ++ print "item $_\n"; ++ } ++ ++ People will also find it useful in merging multiple ".js" or ".css" ++ resources into a whole. Here's an example: ++ ++ location /merge { ++ default_type 'text/javascript'; ++ ++ echo_foreach_split '&' $query_string; ++ echo "/* JS File $echo_it */"; ++ echo_location_async $echo_it; ++ echo; ++ echo_end; ++ } ++ ++ Then accessing /merge to merge the ".js" resources specified in the ++ query string: ++ ++ $ curl 'http://localhost/merge?/foo/bar.js&/yui/blah.js&/baz.js' ++ ++ One can also use third-party Nginx cache module to cache the merged ++ response generated by the "/merge" location in the previous example. ++ ++ This directive was first introduced in the release v0.17. ++ ++ echo_end ++ syntax: *echo_end* ++ ++ default: *no* ++ ++ context: *location* ++ ++ This directive is used to terminate the body of looping and conditional ++ control structures like echo_foreach_split. ++ ++ This directive was first introduced in the release v0.17. ++ ++ echo_request_body ++ syntax: *echo_request_body* ++ ++ default: *no* ++ ++ context: *location* ++ ++ Outputs the contents of the request body previous read. ++ ++ Behind the scene, it's implemented roughly like this: ++ ++ if (r->request_body && r->request_body->bufs) { ++ return ngx_http_output_filter(r, r->request_body->bufs); ++ } ++ ++ Unlike the $echo_request_body and $request_body variables, this ++ directive will show the whole request body even if some parts or all ++ parts of it are saved in temporary files on the disk. ++ ++ It is a "no-op" if no request body has been read yet. ++ ++ This directive was first introduced in the release v0.18. ++ ++ See also echo_read_request_body and the chunkin module. ++ ++ echo_exec ++ syntax: *echo_exec []* ++ ++ syntax: *echo_exec * ++ ++ default: *no* ++ ++ context: *location* ++ ++ Does an internal redirect to the location specified. An optional query ++ string can be specified for normal locations, as in ++ ++ location /foo { ++ echo_exec /bar weight=5; ++ } ++ location /bar { ++ echo $arg_weight; ++ } ++ ++ Or equivalently ++ ++ location /foo { ++ echo_exec /bar?weight=5; ++ } ++ location /bar { ++ echo $arg_weight; ++ } ++ ++ Named locations are also supported. Here's an example: ++ ++ location /foo { ++ echo_exec @bar; ++ } ++ location @bar { ++ # you'll get /foo rather than @bar ++ # due to a potential bug in nginx. ++ echo $echo_request_uri; ++ } ++ ++ But query string (if any) will always be ignored for named location ++ redirects due to a limitation in the "ngx_http_named_location" function. ++ ++ Never try to echo things before the "echo_exec" directive or you won't ++ see the proper response of the location you want to redirect to. Because ++ any echoing will cause the original location handler to send HTTP ++ headers before the redirection happens. ++ ++ Technically speaking, this directive exposes the Nginx internal API ++ functions "ngx_http_internal_redirect" and "ngx_http_named_location". ++ ++ This directive was first introduced in the v0.21 release. ++ ++Filter Directives ++ Use of the following directives trigger the filter registration of this ++ module. By default, no filter will be registered by this module. ++ ++ Every filter directive supports variable interpolation in its arguments ++ (if any). ++ ++ echo_before_body ++ syntax: *echo_before_body [options] [argument]...* ++ ++ default: *no* ++ ++ context: *location* ++ ++ It's the filter version of the echo directive, and prepends its output ++ to the beginning of the original outputs generated by the underlying ++ content handler. ++ ++ An example is ++ ++ location /echo { ++ echo_before_body hello; ++ proxy_pass $scheme://127.0.0.1:$server_port$request_uri/more; ++ } ++ location /echo/more { ++ echo world ++ } ++ ++ Accessing "/echo" from the client side yields ++ ++ hello ++ world ++ ++ In the previous sample, we borrow the standard proxy module to serve as ++ the underlying content handler that generates the "main contents". ++ ++ Multiple instances of this filter directive are also allowed, as in: ++ ++ location /echo { ++ echo_before_body hello; ++ echo_before_body world; ++ echo !; ++ } ++ ++ On the client side, the output is like ++ ++ $ curl 'http://localhost/echo' ++ hello ++ world ++ ! ++ ++ In this example, we also use the content handler directives provided by ++ this module as the underlying content handler. ++ ++ This directive also supports the "-n" and "--" options like the echo ++ directive. ++ ++ This directive can be mixed with its brother directive echo_after_body. ++ ++ echo_after_body ++ syntax: *echo_after_body [argument]...* ++ ++ default: *no* ++ ++ context: *location* ++ ++ WARNING this directive does not work for nginx >= 0.7.65. ++ ++ It's very much like the echo_before_body directive, but *appends* its ++ output to the end of the original outputs generated by the underlying ++ content handler. ++ ++ Here's a simple example: ++ ++ location /echo { ++ echo_after_body hello; ++ proxy_pass http://127.0.0.1:$server_port$request_uri/more; ++ } ++ location /echo/more { ++ echo world ++ } ++ ++ Accessing "/echo" from the client side yields ++ ++ world ++ hello ++ ++ Multiple instances are allowed, as in: ++ ++ location /echo { ++ echo_after_body hello; ++ echo_after_body world; ++ echo i; ++ echo say; ++ } ++ ++ The output on the client side while accessing the "/echo" location looks ++ like ++ ++ i ++ say ++ hello ++ world ++ ++ This directive also supports the "-n" and "--" options like the echo ++ directive. ++ ++ When this directive is used in a location accessed by a subrequest, it ++ replies on the "sync" flag set in a chain buffer to indicate the end of ++ the output for nginx >= 0.8.7. This is a hack because Nginx does not ++ provide a reliable way to determine the end of the output chain in a ++ subrequest's output filter. Use it in subrequests with care. ++ ++ This directive can be mixed with its brother directive echo_before_body. ++ ++Variables ++ $echo_it ++ This is a "topic variable" used by echo_foreach_split, just like the $_ ++ variable in Perl. ++ ++ $echo_timer_elapsed ++ This variable holds the seconds elapsed since the start of the current ++ request (might be a subrequest though) or the last invocation of the ++ echo_reset_timer command. ++ ++ The timing result takes three digits after the decimal point. ++ ++ References of this variable will force the underlying Nginx timer to ++ update to the current system time, regardless the timer resolution ++ settings elsewhere in the config file, just like the echo_reset_timer ++ directive. ++ ++ $echo_request_body ++ Evaluates to the current (sub)request's request body previously read if ++ no part of the body has been saved to a temporary file. To always show ++ the request body even if it's very large, use the echo_request_body ++ directive. ++ ++ $echo_request_method ++ Evaluates to the HTTP request method of the current request (it can be a ++ subrequest). ++ ++ Behind the scene, it just takes the string data stored in ++ "r->method_name". ++ ++ Compare it to the $echo_client_request_method variable. ++ ++ At least for Nginx 0.8.20 and older, the $request_method variable ++ provided by the http core module is actually doing what our ++ $echo_client_request_method is doing. ++ ++ This variable was first introduced in our v0.15 release. ++ ++ $echo_client_request_method ++ Always evaluates to the main request's HTTP method even if the current ++ request is a subrequest. ++ ++ Behind the scene, it just takes the string data stored in ++ "r->main->method_name". ++ ++ Compare it to the $echo_request_method variable. ++ ++ This variable was first introduced in our v0.15 release. ++ ++ $echo_client_request_headers ++ Evaluates to the original client request's headers. ++ ++ Just as the name suggests, it will always take the main request (or the ++ client request) even if it's currently executed in a subrequest. ++ ++ A simple example is below: ++ ++ location /echoback { ++ echo "headers are:" ++ echo $echo_client_request_headers; ++ } ++ ++ Accessing "/echoback" yields ++ ++ $ curl 'http://localhost/echoback' ++ headers are ++ GET /echoback HTTP/1.1 ++ User-Agent: curl/7.18.2 (i486-pc-linux-gnu) libcurl/7.18.2 OpenSSL/0.9.8g ++ Host: localhost:1984 ++ Accept: */* ++ ++ Behind the scene, it recovers "r->main->header_in" on the C level and ++ does not construct the headers itself by traversing parsed results in ++ the request object, and strips the last (trailing) CRLF. ++ ++ This variable was first introduced in version 0.15. ++ ++ $echo_cacheable_request_uri ++ Evaluates to the parsed form of the URI (usually led by "/") of the ++ current (sub-)request. Unlike the $echo_request_uri variable, it is ++ cacheable. ++ ++ See $echo_request_uri for more details. ++ ++ This variable was first introduced in version 0.17. ++ ++ $echo_request_uri ++ Evaluates to the parsed form of the URI (usually led by "/") of the ++ current (sub-)request. Unlike the $echo_cacheable_request_uri variable, ++ it is *not* cacheable. ++ ++ This is quite different from the $request_uri variable exported by the ++ [[NginxHttpCoreModule]], because $request_uri is the *unparsed* form of ++ the current request's URI. ++ ++ This variable was first introduced in version 0.17. ++ ++ $echo_incr ++ It is a counter that always generate the current counting number, ++ starting from 1. The counter is always associated with the main request ++ even if it is accessed within a subrequest. ++ ++ Consider the following example ++ ++ location /main { ++ echo "main pre: $echo_incr"; ++ echo_location_async /sub; ++ echo_location_async /sub; ++ echo "main post: $echo_incr"; ++ } ++ location /sub { ++ echo "sub: $echo_incr"; ++ } ++ ++ Accessing "/main" yields ++ ++ main pre: 1 ++ sub: 3 ++ sub: 4 ++ main post: 2 ++ ++ This directive was first introduced in the v0.18 release. ++ ++ $echo_response_status ++ Evaluates to the status code of the current (sub)request, null if not ++ any. ++ ++ Behind the scene, it's just the textual representation of ++ "r->headers_out->status". ++ ++ This directive was first introduced in the v0.23 release. ++ ++Installation ++ Grab the nginx source code from nginx.net (), for ++ example, the version 0.8.41 (see nginx compatibility), and then build ++ the source with this module: ++ ++ $ wget 'http://sysoev.ru/nginx/nginx-0.8.41.tar.gz' ++ $ tar -xzvf nginx-0.8.41.tar.gz ++ $ cd nginx-0.8.41/ ++ ++ # Here we assume you would install you nginx under /opt/nginx/. ++ $ ./configure --prefix=/opt/nginx \ ++ --add-module=/path/to/echo-nginx-module ++ ++ $ make -j2 ++ $ make install ++ ++ Download the latest version of the release tarball of this module from ++ echo-nginx-module file list ++ (). ++ ++Compatibility ++ The following versions of Nginx should work with this module: ++ ++ * 0.8.x (last tested version is 0.8.40) ++ ++ * 0.7.x >= 0.7.21 (last tested version is 0.7.66) ++ ++ In particular, ++ ++ * the directive echo_location_async and its brother ++ echo_subrequest_async do *not* work with 0.7.x < 0.7.46. ++ ++ * the echo_after_body directive does *not* work at all with nginx < ++ 0.8.7. ++ ++ * the echo_sleep directive cannot be used after echo_location or ++ echo_subrequest for nginx < 0.8.11. ++ ++ Earlier versions of Nginx like 0.6.x and 0.5.x will *not* work at all. ++ ++ If you find that any particular version of Nginx above 0.7.21 does not ++ work with this module, please consider reporting a bug. ++ ++Modules that use this module for testing ++ The following modules take advantage of this "echo" module in their test ++ suite: ++ ++ * The memc module that supports almost the whole memcached TCP ++ protocol. ++ ++ * The chunkin module that adds HTTP 1.1 chunked input support to ++ Nginx. ++ ++ * The headers_more module that allows you to add, set, and clear input ++ and output headers under the conditions that you specify. ++ ++ * The "echo" module itself. ++ ++ Please mail me other modules that use "echo" in any form and I'll add ++ them to the list above :) ++ ++Report Bugs ++ Although a lot of effort has been put into testing and code tuning, ++ there must be some serious bugs lurking somewhere in this module. So ++ whenever you are bitten by any quirks, please don't hesitate to ++ ++ 1. send a bug report or even patches to , ++ ++ 2. or create a ticket on the issue tracking interface ++ () provided by ++ GitHub. ++ ++Source Repository ++ Available on github at agentzh/echo-nginx-module ++ (). ++ ++ChangeLog ++ v0.34 ++ * we no longer use the problematic "ngx_strXcmp" macros in our source ++ because it may cause invalid reads and thus segmentation faults. ++ thanks Piotr Sikora. ++ ++ v0.33 ++ * fixed compatibility with nginx 0.7.66+ because the ngx_time_update ++ macro's parameter list has changed. Thanks Guang Feng (蔡镜明). ++ ++ v0.32 ++ * we should have used "ngx_calloc_buf" instead of "ngx_alloc_buf" for ++ the last chunk generated for echo_after_body. thanks valgrind's ++ memcheck tool. ++ ++ * we should initialize flags before feeding it into ++ "ngx_http_parse_unsafe_uri". thanks valgrind's memcheck tool. ++ ++ * fixed a minor issue in the echo_location/echo_subrequest ++ implementation, which used to have race conditions. ++ ++ v0.31 ++ * the echo wev handler should not proceed if it is still waiting for ++ some sequential subrequest or has just processed one to avoid ++ bouncing issues. ++ ++ * fixed a segfault for echo_exec for 0.7.x: we should check "r->done" ++ before proceeding. ++ ++ * no longer explicitly set "r->write_event_handler" to ++ "ngx_http_request_empty_handler" because it's totally wrong for the ++ state machine. ++ ++ * fixed the sequential subrequest model bugs: we should ensure the ++ "pr->write_event_handler" gets called immediately after the ++ "post_subrequest" callback when the subrequest finalizes. ++ ++ v0.30 ++ * fixed the echo_exec directive for nginx >= 0.8.11. we didn't get the ++ "r->main->count" right in the previous version. ++ ++ v0.29 ++ * refactored the core of this module. now the implementation of ++ echo_location, echo_subrequest, echo_sleep, and ++ echo_read_request_body finally fit well with the nginx event model ++ and Igor Sysoev's way of thinking. ++ ++ v0.28 ++ * added support for the "-n" and "--" options to the echo, ++ echo_before_body, and echo_after_body directives. ++ ++ v0.27 ++ * applied the patch from Sergey A. Osokin to work with nginx 0.8.35. ++ ++ v0.26 ++ * bug fix: we should bypass upstream filters in our echo filters. an ++ output filter should ever call "ngx_http_output_filter" nor ++ "ngx_http_send_special". ++ ++ v0.25 ++ * now we register a request cleanup handler to ensure our sleep ++ event's timer will always get properly deleted even if the request ++ is quit prematurely. this affects the echo_sleep directive. ++ ++ * use ngx_null_string whenever possible in the source. ++ ++ * sync'd the bundled test scaffold to Test::Nginx 0.07. ++ ++ v0.24 ++ * various source file name and coding style fixes. (the code now looks ++ more like Igor Sysoev's.) ++ ++ v0.23 ++ * now the subrequest can read the client request body directly (for ++ the main request) because we made subrequests inherit its parent's ++ "r->header_in" as well. This affects the echo_read_request_body ++ directive. ++ ++ * fixed echo_after_body in subrequests by using a hack (checking ++ "cl->buf->sync" for the last buf) for nginx 0.8.7+ only. ++ ++ * added new varaible $echo_response_status to help testing the status ++ code of a subrequest. (The memc module makes use of it.) ++ ++ * use the "ngx_calloc_buf" macro to allocate new bufs in the code ++ rather than explicit "ngx_pcalloc" calls for safety. ++ ++ v0.22 ++ * Now we allowed all the directives appear in the rewrite module's if ++ block. But so far I've only tested the echo directive. ++ ++ v0.21 ++ * Added a new directive named echo_exec which does internal redirect ++ to other (named) locations. ++ ++ v0.20 ++ * Fixed a bug in echo_sleep's "r->main->count" handling for nginx ++ 0.8.x. This bug will cause the server to hang when proxing a ++ location with echo_sleep. ++ ++ * Applied the "ngx_str3cmp", "ngx_str4cmp", and "ngx_str6cmp" ++ optimizing macros to the "parse_method_name" function, as suggested ++ by Marcus Clyne. ++ ++ * Added TODO items regarding $echo_random and "echo_repeat" suggested ++ by Marcus Clyne. ++ ++ v0.19 ++ * Fixed the CPS-style chained subrequest model for the echo_location ++ and echo_subrequest directives. they are now working perfectly and ++ will not hang the server with the recent nginx 0.8.21 ~ 0.8.27 ++ releases. To be specifically, the chained subrequest should call ++ "ngx_http_finalize_request" on its parent request if the content ++ handler of the parent request does not return "NGX_DONE". ++ ++ * Undeprecated the echo_location and echo_subrequest directives. ++ ++ v0.18 ++ * Fixed the "zero size buf in output" alerts in error.log. ++ ++ * Added the new directive echo_request_body. ++ ++ * Now we use the "ngx_http_parse_unsafe_uri" function to check the ++ locations to echo_location_async and its friends. Thanks Arvind ++ Jayaprakash for suggesting this fix. ++ ++ * Deprecated the echo_location and echo_subrequest directives. ++ ++ * For HTTP 1.0 clients, use the buf length of the first chain link as ++ the output header Content-Length. ++ ++ * Implemented new variable $echo_incr. ++ ++ v0.17 ++ * Added new directives echo_foreach_split and echo_end. Also ++ introduced a "topic variable" named $echo_it. ++ ++ * Added new variables $echo_request_uri and ++ $echo_cacheable_request_uri. ++ ++ v0.16 ++ * Now the subrequests issued by the echo_location_async and ++ echo_location directives no longer inherit cached variable values ++ from its parent request. (The underlying "ngx_http_subrequest" ++ function, however, does automatic cachable variable value ++ inheritance.) ++ ++ * Added an undocumented variable *echo_cached_request_uri* to help ++ testing of this module. ++ ++ v0.15 ++ * Added new directives echo_subrequest and echo_subrequest_async for ++ the full nginx subrequest API. ++ ++ * Removed the "echo_client_request_headers" directive, and provided ++ the $echo_client_request_headers variable instead. ++ ++ * Added new variables $echo_request_method and ++ $echo_client_request_method. ++ ++ v0.14 ++ * Added new directive echo_read_request_body to explicitly read client ++ request body so that the [[NginxHttpCoreModule#$request_body]] ++ variable will always have non-empty values. ++ ++ * Now we shuffer test cases automatically in .t files and fixed bugs ++ in the tests themselves which are hidden by config reload fallback ++ in failure. ++ ++ v0.13 ++ * Fixed the special cases when the outputs of a echo_duplicate ++ directive is empty. ++ ++ * Now we explicitly clear content length and accept ranges headers in ++ the content handler. ++ ++ v0.12 ++ * Implemented the echo_location directive, which can issue chained GET ++ subrequests in the Continuation Passing Style (CPS), rather than the ++ parallel subrequest issued by the echo_location_async directive. ++ ++ v0.11 ++ * Implemented the echo_duplicate directive to help generating large ++ chunk of data for testing. ++ ++ v0.10 ++ * Fixed compilation regression against Nginx 0.7.21. This bug appears ++ in version 0.09. ++ ++ * Refactored the codebase by splitting source into various small ++ files. ++ ++ v0.09 ++ * Reimplement the echo_sleep directive using per-request event and ++ timer; the old implementation uses the global connection's ++ read/write event to register timer, so it will break horribly when ++ multiple subrequests "sleep" at the same time. ++ ++ * Added the echo_location_async directive which can issue a GET ++ subrequest and insert its contents herein. ++ ++ v0.08 ++ * echo_sleep: now we delete our "write event timer" in the ++ "post_sleep" handle. ++ ++ * Added "doc/manpage.wiki" which tracks changes in the wiki page ++ (). ++ ++ * Added the "util/wiki2pod.pl" script to convert "doc/manpage.wiki" to ++ "README". ++ ++ * Disabled the "DDEBUG" macro in the C source by default. ++ ++Test Suite ++ This module comes with a Perl-driven test suite. The test cases ++ () are ++ declarative ++ ( ++ ) too. Thanks to the Test::Base ++ () module in the Perl world. ++ ++ To run it on your side: ++ ++ $ cd test ++ $ PATH=/path/to/your/nginx-with-echo-module:$PATH prove -r t ++ ++ You need to terminate any Nginx processes before running the test suite ++ if you have changed the Nginx server binary. ++ ++ At the moment, LWP::UserAgent ++ () is used by the test ++ scaffold ++ () for simplicity and it's rather weak in testing ++ *streaming* behavior of Nginx (I'm using "curl" to test these aspects ++ manually for now). I'm considering coding up my own Perl HTTP client ++ library based on IO::Select ++ () and IO::Socket ++ () (there might be already ++ one around?). ++ ++ Because a single nginx server (by default, "localhost:1984") is used ++ across all the test scripts (".t" files), it's meaningless to run the ++ test suite in parallel by specifying "-jN" when invoking the "prove" ++ utility. ++ ++ Some parts of the test suite requires standard modules proxy, rewrite ++ and SSI to be enabled as well when building Nginx. ++ ++TODO ++ * Fix the echo_after_body directive in subrequests. ++ ++ * Add directives *echo_read_client_request_body* and ++ *echo_request_headers*. ++ ++ * Add new directive *echo_log* to use Nginx's logging facility ++ directly from the config file and specific loglevel can be ++ specified, as in ++ ++ echo_log debug "I am being called."; ++ ++ * Add support for options "-h" and "-t" to echo_subrequest_async and ++ echo_subrequest. For example ++ ++ echo_subrequest POST /sub -q 'foo=Foo&bar=Bar' -b 'hello' -t 'text/plan' -h 'X-My-Header: blah blah' ++ ++ * Add options to control whether a subrequest should inherit cached ++ variables from its parent request (i.e. the current request that is ++ calling the subrequest in question). Currently none of the ++ subrequests issued by this module inherit the cached variables from ++ the parent request. ++ ++ * Add new variable *$echo_active_subrequests* to show "r->main->count ++ - 1". ++ ++ * Add the *echo_file* and *echo_cached_file* directives. ++ ++ * Add new varaible *$echo_request_headers* to accompany the existing ++ $echo_client_request_headers variable. ++ ++ * Add new directive *echo_foreach*, as in ++ ++ echo_foreach 'cat' 'dog' 'mouse'; ++ echo_location_async "/animals/$echo_it"; ++ echo_end; ++ ++ * Add new directive *echo_foreach_range*, as in ++ ++ echo_foreach_range '[1..100]' '[a-zA-z0-9]'; ++ echo_location_async "/item/$echo_it"; ++ echo_end; ++ ++ * Add new directive *echo_repeat*, as in ++ ++ echo_repeat 10 $i { ++ echo "Page $i"; ++ echo_location "/path/to/page/$i"; ++ } ++ ++ This is just another way of saying ++ ++ echo_foreach_range $i [1..10]; ++ echo "Page $i"; ++ echo_location "/path/to/page/$i"; ++ echo_end; ++ ++ Thanks Marcus Clyne for providing this idea. ++ ++ * Add new variable $echo_random which always returns a random ++ non-negative integer with the lower/upper limit specified by the new ++ directives "echo_random_min" and "echo_random_max". For example, ++ ++ echo_random_min 10 ++ echo_random_max 200 ++ echo "random number: $echo_random"; ++ ++ Thanks Marcus Clyne for providing this idea. ++ ++Getting involved ++ You'll be very welcomed to submit patches to the author or just ask for ++ a commit bit to the source repository on GitHub. ++ ++Author ++ agentzh (章亦春) ** ++ ++ This wiki page is also maintained by the author himself, and everybody ++ is encouraged to improve this page as well. ++ ++Copyright & License ++ Copyright (c) 2009, Taobao Inc., Alibaba Group ( http://www.taobao.com ++ ). ++ ++ Copyright (c) 2009, agentzh . ++ ++ This module is licensed under the terms of the BSD license. ++ ++ Redistribution and use in source and binary forms, with or without ++ modification, are permitted provided that the following conditions are ++ met: ++ ++ * Redistributions of source code must retain the above copyright ++ notice, this list of conditions and the following disclaimer. ++ ++ * 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. ++ ++ * Neither the name of the Taobao Inc. nor the names of its ++ contributors may be used to endorse or promote products derived from ++ this software without specific prior written permission. ++ ++ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT ++ HOLDER 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. ++ ++See Also ++ * The original blog post ++ () about this module's initial development. ++ ++ * The standard addition filter module. ++ ++ * The standard proxy module. ++ +Index: 0.8/modules/nginx-echo/config +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ 0.8/modules/nginx-echo/config 2010-10-31 07:35:23.648338001 +0000 +@@ -0,0 +1,5 @@ ++ngx_addon_name=ngx_http_echo_module ++HTTP_AUX_FILTER_MODULES="$HTTP_AUX_FILTER_MODULES ngx_http_echo_module" ++NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/src/ngx_http_echo_module.c $ngx_addon_dir/src/ngx_http_echo_util.c $ngx_addon_dir/src/ngx_http_echo_timer.c $ngx_addon_dir/src/ngx_http_echo_var.c $ngx_addon_dir/src/ngx_http_echo_handler.c $ngx_addon_dir/src/ngx_http_echo_filter.c $ngx_addon_dir/src/ngx_http_echo_sleep.c $ngx_addon_dir/src/ngx_http_echo_location.c $ngx_addon_dir/src/ngx_http_echo_echo.c $ngx_addon_dir/src/ngx_http_echo_request_info.c $ngx_addon_dir/src/ngx_http_echo_subrequest.c $ngx_addon_dir/src/ngx_http_echo_foreach.c" ++NGX_ADDON_DEPS="$NGX_ADDON_DEPS $ngx_addon_dir/src/ngx_http_echo_module.h $ngx_addon_dir/src/ddebug.h $ngx_addon_dir/src/ngx_http_echo_handler.h $ngx_addon_dir/src/ngx_http_echo_util.h $ngx_addon_dir/src/ngx_http_echo_sleep.h $ngx_addon_dir/src/ngx_http_echo_filter.h $ngx_addon_dir/src/ngx_http_echo_var.h $ngx_addon_dir/src/ngx_http_echo_location.h $ngx_addon_dir/src/ngx_http_echo_echo.h $ngx_addon_dir/src/ngx_http_echo_request_info.h $ngx_addon_dir/src/ngx_http_echo_subrequest.h $ngx_addon_dir/src/ngx_http_echo_foreach.h" ++ +Index: 0.8/modules/nginx-echo/doc/manpage.wiki +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ 0.8/modules/nginx-echo/doc/manpage.wiki 2010-10-31 07:35:47.708338000 +0000 +@@ -0,0 +1,1545 @@ ++= Name = ++ ++'''ngx_echo''' - Brings "echo", "sleep", "time", "exec" and more shell-style goodies to Nginx config file. ++ ++''This module is not distributed with the Nginx source.'' See [[#Installation|the installation instructions]]. ++ ++= Version = ++ ++This document describes echo-nginx-module [http://github.com/agentzh/echo-nginx-module/tarball/v0.34 v0.34] released on September 14, 2010. ++ ++= Synopsis = ++ ++ ++ location /hello { ++ echo "hello, world!"; ++ } ++ ++ ++ ++ location /hello { ++ echo -n "hello, " ++ echo "world!"; ++ } ++ ++ ++ ++ location /timed_hello { ++ echo_reset_timer; ++ echo hello world; ++ echo "'hello world' takes about $echo_timer_elapsed sec."; ++ echo hiya igor; ++ echo "'hiya igor' takes about $echo_timer_elapsed sec."; ++ } ++ ++ ++ ++ location /echo_with_sleep { ++ echo hello; ++ echo_flush; # ensure the client can see previous output immediately ++ echo_sleep 2.5; # in sec ++ echo world; ++ } ++ ++ ++ ++ # in the following example, accessing /echo yields ++ # hello ++ # world ++ # blah ++ # hiya ++ # igor ++ location /echo { ++ echo_before_body hello; ++ echo_before_body world; ++ proxy_pass $scheme://127.0.0.1:$server_port$request_uri/more; ++ echo_after_body hiya; ++ echo_after_body igor; ++ } ++ location /echo/more { ++ echo blah; ++ } ++ ++ ++ ++ # the output of /main might be ++ # hello ++ # world ++ # took 0.000 sec for total. ++ # and the whole request would take about 2 sec to complete. ++ location /main { ++ echo_reset_timer; ++ ++ # subrequests in parallel ++ echo_location_async /sub1; ++ echo_location_async /sub2; ++ ++ echo "took $echo_timer_elapsed sec for total."; ++ } ++ location /sub1 { ++ echo_sleep 2; ++ echo hello; ++ } ++ location /sub2 { ++ echo_sleep 1; ++ echo world; ++ } ++ ++ ++ ++ # the output of /main might be ++ # hello ++ # world ++ # took 3.003 sec for total. ++ # and the whole request would take about 3 sec to complete. ++ location /main { ++ echo_reset_timer; ++ ++ # subrequests in series (chained by CPS) ++ echo_location /sub1; ++ echo_location /sub2; ++ ++ echo "took $echo_timer_elapsed sec for total."; ++ } ++ location /sub1 { ++ echo_sleep 2; ++ echo hello; ++ } ++ location /sub2 { ++ echo_sleep 1; ++ echo world; ++ } ++ ++ ++ ++ # Accessing /dup gives ++ # ------ END ------ ++ location /dup { ++ echo_duplicate 3 "--"; ++ echo_duplicate 1 " END "; ++ echo_duplicate 3 "--"; ++ echo; ++ } ++ ++ ++ ++ # /bighello will generate 1000,000,000 hello's. ++ location /bighello { ++ echo_duplicate 1000_000_000 'hello'; ++ } ++ ++ ++ ++ # echo back the client request ++ location /echoback { ++ echo_duplicate 1 $echo_client_request_headers; ++ echo "\r"; ++ ++ echo_read_request_body; ++ ++ echo_request_body; ++ } ++ ++ ++ ++ # GET /multi will yields ++ # querystring: foo=Foo ++ # method: POST ++ # body: hi ++ # content length: 2 ++ # /// ++ # querystring: bar=Bar ++ # method: PUT ++ # body: hello ++ # content length: 5 ++ # /// ++ location /multi { ++ echo_subrequest_async POST '/sub' -q 'foo=Foo' -b 'hi'; ++ echo_subrequest_async PUT '/sub' -q 'bar=Bar' -b 'hello'; ++ } ++ location /sub { ++ echo "querystring: $query_string"; ++ echo "method: $echo_request_method"; ++ echo "body: $echo_request_body"; ++ echo "content length: $http_content_length"; ++ echo '///'; ++ } ++ ++ ++ ++ # GET /merge?/foo.js&/bar/blah.js&/yui/baz.js will merge the .js resources together ++ location /merge { ++ default_type 'text/javascript'; ++ echo_foreach_split '&' $query_string; ++ echo "/* JS File $echo_it */"; ++ echo_location_async $echo_it; ++ echo; ++ echo_end; ++ } ++ ++ ++ ++ # accessing /if?val=abc yields the "hit" output ++ # while /if?val=bcd yields "miss": ++ location ^~ /if { ++ set $res miss; ++ if ($arg_val ~* '^a') { ++ set $res hit; ++ echo $res; ++ } ++ echo $res; ++ } ++ ++ ++= Description = ++ ++This module wraps lots of Nginx internal APIs for streaming input and output, parallel/sequential subrequests, timers and sleeping, as well as various meta data accessing. ++ ++Basically it provides various utilities that help testing and debugging of other modules by trivially emulating different kinds of faked subrequest locations. ++ ++People will also find it useful in real-world applications that need to ++ ++# serve static contents directly from memory (loading from the Nginx config file). ++# wrap the upstream response with custom header and footer (kinda like the [[NginxHttpAdditionModule|addition module]] but with contents read directly from the config file and Nginx variables). ++# merge contents of various "Nginx locations" (i.e., subrequests) together in a single main request (using [[#echo_location|echo_location]] and its friends). ++ ++This is a special dual-role module that can ''lazily'' serve as a content handler or register itself as an output filter only upon demand. By default, this module does not do anything at all. ++ ++Use of any of this module's directives (no matter [[#Content Handler Directives|content handler directives]] or [[#Filter Directives|filter directives]]) will force the chunked encoding to be used for the HTTP response due to the streaming nature of this module (unless HTTP 1.0 is enforced by the client and the Content-Length header will be set to the size of the first handler directive that generates contents). ++ ++Technially, this module has also demonstrated the following techniques that might be helpful for module writers: ++ ++# Issue parallel subreqeusts directly from content handler. ++# Issue chained subrequests directly from content handler, by passing continuation along the subrequest chain. ++# Issue subrequests with all HTTP 1.1 methods and even an optional faked HTTP request body. ++# Interact with the Nginx event model directly from content handler using custom events and timers, and resume the content handler back if necessary. ++# Dual-role module that can (lazily) serve as a content handler or an output filter or both. ++# Nginx config file variable creation and interpolation. ++# Streaming output control using output_chain, flush and its friends. ++# Read client request body from the content handler, and returns back (asynchronously) to the content handler after completion. ++# Use Perl-based declarative [[#Test Suite|test suite]] to drive the development of Nginx C modules. ++ ++= Content Handler Directives = ++ ++Use of the following directives register this module to the current Nginx location as a content handler. If you want to use another module, like the [[NginxHttpProxyModule|standard proxy module]], as the content handler, use the [[#Filter Directives|filter directives]] provided by this module. ++ ++All the content handler directives can be mixed together in a single Nginx location and they're supposed to run sequentially just as in the Bash scripting language. ++ ++Every content handler directive supports variable interpolation in its arguments (if any). ++ ++The MIME type set by the [[NginxHttpCoreModule#default_type|standard default_type directive]] is respected by this module, as in: ++ ++ ++ location /hello { ++ default_type text/plain; ++ echo hello; ++ } ++ ++ ++Then on the client side: ++ ++ ++ $ curl -I 'http://localhost/echo' ++ HTTP/1.1 200 OK ++ Server: nginx/0.8.20 ++ Date: Sat, 17 Oct 2009 03:40:19 GMT ++ Content-Type: text/plain ++ Connection: keep-alive ++ ++ ++Since the [[#v0.22|v0.22]] release, all of the directives are allowed in the [[NginxHttpRewriteModule|rewrite module]]'s [[NginxHttpRewriteModule#if|if]] directive block, for instance: ++ ++ ++ location ^~ /if { ++ set $res miss; ++ if ($arg_val ~* '^a') { ++ set $res hit; ++ echo $res; ++ } ++ echo $res; ++ } ++ ++ ++== echo == ++'''syntax:''' ''echo [options] ...'' ++ ++'''default:''' ''no'' ++ ++'''context:''' ''location'' ++ ++Sends arguments joined by spaces, along with a trailing newline, out to the client. ++ ++Note that the data might be buffered by Nginx's underlying buffer. To force the output data flushed immediately, use the [[#echo_flush|echo_flush]] command just after echo, as in ++ ++ ++ echo hello world; ++ echo_flush; ++ ++ ++When no argument is specified, ''echo'' emits the trailing newline alone, just like the ''echo'' command in shell. ++ ++Variables may appear in the arguments. An example is ++ ++ ++ echo The current request uri is $request_uri; ++ ++ ++where [[NginxHttpCoreModule#$request_uri|$request_uri]] is a variable exposed by the [[NginxHttpCoreModule]]. ++ ++This command can be used multiple times in a single location configuration, as in ++ ++ ++ location /echo { ++ echo hello; ++ echo world; ++ } ++ ++ ++The output on the client side looks like this ++ ++ ++ $ curl 'http://localhost/echo' ++ hello ++ world ++ ++ ++Special characters like newlines (\n) and tabs (\t) can be escaped using C-style escaping sequences. But a notable exception is the dollar sign ($). As of Nginx 0.8.20, there's still no clean way to esacpe this characters. (A work-around might be to use a $echo_dollor variable that is always evaluated to the constant $ character. This feature will possibly be introduced in a future version of this module.) ++ ++As of the echo [[#v0.28|v0.28]] release, one can suppress the trailing newline character in the output by using the -n option, as in ++ ++ ++ location /echo { ++ echo -n "hello, "; ++ echo "world"; ++ } ++ ++ ++Accessing /echo gives ++ ++ ++ $ curl 'http://localhost/echo' ++ hello, world ++ ++ ++Leading -n in variable values won't take effect and will be emitted literally, as in ++ ++ ++ location /echo { ++ set $opt -n; ++ echo $opt "hello,"; ++ echo "world"; ++ } ++ ++ ++This gives the following output ++ ++ ++ $ curl 'http://localhost/echo' ++ -n hello, ++ world ++ ++ ++One can output leading -n literals and other options using the special -- option like this ++ ++ ++ location /echo { ++ echo -- -n is an option; ++ } ++ ++ ++which yields ++ ++ ++ $ curl 'http://localhost/echo' ++ -n is an option ++ ++ ++== echo_duplicate == ++'''syntax:''' ''echo_duplicate '' ++ ++'''default:''' ''no'' ++ ++'''context:''' ''location'' ++ ++Outputs duplication of a string indicated by the second argument, using the times specified in the first argument. ++ ++For instance, ++ ++ ++ location /dup { ++ echo_duplicate 3 "abc"; ++ } ++ ++ ++will lead to an output of "abcabcabc". ++ ++Underscores are allowed in the count number, just like in Perl. For example, to emit 1000,000,000 instances of "hello, world": ++ ++ ++ location /many_hellos { ++ echo_duplicate 1000_000_000 "hello, world"; ++ } ++ ++ ++The count argument could be zero, but not negative. The second string argument could be an empty string ("") likewise. ++ ++Unlike the [[#echo|echo]] directive, no trailing newline is appended to the result. So it's possible to "abuse" this directive as a no-trailing-newline version of [[#echo|echo]] by using "count" 1, as in ++ ++ ++ location /echo_art { ++ echo_duplicate 2 '---'; ++ echo_duplicate 1 ' END '; # we don't want a trailing newline here ++ echo_duplicate 2 '---'; ++ echo; # we want a trailing newline here... ++ } ++ ++ ++You get ++ ++ ------ END ------ ++ ++This directive was first introduced in [[#v0.11|version 0.11]]. ++ ++== echo_flush == ++'''syntax:''' ''echo_flush'' ++ ++'''default:''' ''no'' ++ ++'''context:''' ''location'' ++ ++Forces the data potentially buffered by underlying Nginx output filters to send immediately to the client side via socket. ++ ++Note that techically the command just emits a ngx_buf_t object with flush slot set to 1, so certain weird third-party output filter module could still block it before it reaches Nginx's (last) write filter. ++ ++This directive does not take any argument. ++ ++Consider the following example: ++ ++ ++ location /flush { ++ echo hello; ++ ++ echo_flush; ++ ++ echo_sleep 1; ++ echo world; ++ } ++ ++ ++Then on the client side, using curl to access /flush, you'll see the "hello" line immediately, but only after 1 second, the last "world" line. Without calling echo_flush in the example above, you'll most likely see no output until 1 second is elapsed due to the internal buffering of Nginx. ++ ++This directive will fail to flush the output buffer in case of subrequests get involved. Consider the following example: ++ ++ location /main { ++ echo_location_async /sub; ++ echo hello; ++ echo_flush; ++ } ++ location /sub { ++ echo_sleep 1; ++ } ++ ++Then the client won't see "hello" appear even if echo_flush has been executed before the subrequest to /sub has actually started executing. The outputs of /main that are sent ''after'' [[#echo_location_async|echo_location_async]] will be postponed and buffered firmly. ++ ++This does ''not'' apply to outputs sent before the subrequest initiated. For a modified version of the example given above: ++ ++ location /main { ++ echo hello; ++ echo_flush; ++ echo_location_async /sub; ++ } ++ location /sub { ++ echo_sleep 1; ++ } ++ ++The client will immediately see "hello" before /sub enters sleeping. ++ ++See also [[#echo|echo]], [[#echo_sleep|echo_sleep]], and [[#echo_location_async|echo_location_async]]. ++ ++== echo_sleep == ++'''syntax:''' ''echo_sleep '' ++ ++'''default:''' ''no'' ++ ++'''context:''' ''location'' ++ ++Sleeps for the time period specified by the argument, which is in seconds. ++ ++This operation is non-blocking on server side, so unlike the [[#echo_blocking_sleep|echo_blocking_sleep]] directive, it won't block the whole Nginx worker process. ++ ++The period might takes three digits after the decimal point and must be greater than 0.001. ++ ++An example is ++ ++ ++ location /echo_after_sleep { ++ echo_sleep 1.234; ++ echo resumed!; ++ } ++ ++ ++Behind the scene, it sets up a per-request "sleep" ngx_event_t object, and adds a timer using that custom event to the Nginx event model and just waits for a timeout on that event. Because the "sleep" event is per-request, this directive can work in parallel subrequests. ++ ++== echo_blocking_sleep == ++'''syntax:''' ''echo_blocking_sleep '' ++ ++'''default:''' ''no'' ++ ++'''context:''' ''location'' ++ ++This is a blocking version of the [[#echo_sleep|echo_sleep]] directive. ++ ++See the documentation of [[#echo_sleep|echo_sleep]] for more detail. ++ ++Behind the curtain, it calls the ngx_msleep macro provided by the Nginx core which maps to usleep on POSIX-compliant systems. ++ ++Note that this directive will block the current Nginx worker process completely while being executed, so never use it in production environment. ++ ++== echo_reset_timer == ++'''syntax:''' ''echo_reset_timer'' ++ ++'''default:''' ''no'' ++ ++'''context:''' ''location'' ++ ++Reset the timer begin time to ''now'', i.e., the time when this command is executed during request. ++ ++The timer begin time is default to the starting time of the current request and can be overridden by this directive, potentially multiple times in a single location. For example: ++ ++ ++ location /timed_sleep { ++ echo_sleep 0.03; ++ echo "$echo_timer_elapsed sec elapsed."; ++ ++ echo_reset_timer; ++ ++ echo_sleep 0.02; ++ echo "$echo_timer_elapsed sec elapsed."; ++ } ++ ++ ++The output on the client side might be ++ ++ ++ $ curl 'http://localhost/timed_sleep' ++ 0.032 sec elapsed. ++ 0.020 sec elapsed. ++ ++ ++The actual figures you get on your side may vary a bit due to your system's current activities. ++ ++Invocation of this directive will force the underlying Nginx timer to get updated to the current system time (regardless the timer resolution specified elsewhere in the config file). Furthermore, references of the [[#$echo_timer_elapsed|$echo_timer_elapsed]] variable will also trigger timer update forcibly. ++ ++See also [[#echo_sleep|echo_sleep]] and [[#$echo_timer_elapsed|$echo_timer_elapsed]]. ++ ++== echo_read_request_body == ++ ++Explicitly reads request body so that the [[NginxHttpCoreModule#$request_body|$request_body]] variable will always have non-empty values (unless the body is so big that it has been saved by Nginx to a local temporary file). ++ ++Note that this might not be the original client request body because the current request might be a subrequest with a "artificial" body specified by its parent. ++ ++This directive does not generate any output itself, just like [[#echo_sleep|echo_sleep]]. ++ ++Here's an example for echo'ing back the original HTTP client request (both headers and body are included): ++ ++ ++ location /echoback { ++ echo_duplicate 1 $echo_client_request_headers; ++ echo "\r"; ++ echo_read_request_body; ++ echo $request_body; ++ } ++ ++ ++The content of /echoback looks like this on my side (I was using Perl's LWP utility to access this location on the server): ++ ++ ++ $ (echo hello; echo world) | lwp-request -m POST 'http://localhost/echoback' ++ POST /echoback HTTP/1.1 ++ TE: deflate,gzip;q=0.3 ++ Connection: TE, close ++ Host: localhost ++ User-Agent: lwp-request/5.818 libwww-perl/5.820 ++ Content-Length: 12 ++ Content-Type: application/x-www-form-urlencoded ++ ++ hello ++ world ++ ++ ++Because /echoback is the main request, [[NginxHttpCoreModule#$request_body|$request_body]] holds the original client request body. ++ ++Before Nginx 0.7.56, it makes no sense to use this directive because [[NginxHttpCoreModule#$request_body|$request_body]] was first introduced in Nginx 0.7.58. ++ ++This directive itself was first introduced in the echo module's [[#v0.14|v0.14 release]]. ++ ++== echo_location_async == ++'''syntax:''' ''echo_location_async []'' ++ ++'''default:''' ''no'' ++ ++'''context:''' ''location'' ++ ++Issue GET subrequest to the location specified (first argument) with optional url arguments specified in the second argument. ++ ++As of Nginx 0.8.20, the location argument does ''not'' support named location, due to a limitation in the ngx_http_subrequest function. The same is true for its brother, the [[#echo_location|echo_location]] directive. ++ ++A very simple example is ++ ++ ++ location /main { ++ echo_location_async /sub; ++ echo world; ++ } ++ location /sub { ++ echo hello; ++ } ++ ++ ++Accessing /main gets ++ ++ hello ++ world ++ ++Calling multiple locations in parallel is also possible: ++ ++ ++ location /main { ++ echo_reset_timer; ++ echo_location_async /sub1; ++ echo_location_async /sub2; ++ echo "took $echo_timer_elapsed sec for total."; ++ } ++ location /sub1 { ++ echo_sleep 2; # sleeps 2 sec ++ echo hello; ++ } ++ location /sub2 { ++ echo_sleep 1; # sleeps 1 sec ++ echo world; ++ } ++ ++ ++Accessing /main yields ++ ++ ++ $ time curl 'http://localhost/main' ++ hello ++ world ++ took 0.000 sec for total. ++ ++ real 0m2.006s ++ user 0m0.000s ++ sys 0m0.004s ++ ++ ++You can see that the main handler /main does ''not'' wait the subrequests /sub1 and /sub2 to complete and quickly goes on, hence the "0.000 sec" timing result. The whole request, however takes approximately 2 sec in total to complete because /sub1 and /sub2 run in parallel (or "concurrently" to be more accurate). ++ ++If you use [[#echo_blocking_sleep|echo_blocking_sleep]] in the previous example instead, then you'll get the same output, but with 3 sec total response time, because "blocking sleep" blocks the whole Nginx worker process. ++ ++Locations can also take an optional querystring argument, for instance ++ ++ ++ location /main { ++ echo_location_async /sub 'foo=Foo&bar=Bar'; ++ } ++ location /sub { ++ echo $arg_foo $arg_bar; ++ } ++ ++ ++Accessing /main yields ++ ++ ++ $ curl 'http://localhost/main' ++ Foo Bar ++ ++ ++Querystrings is ''not'' allowed to be concatenated onto the location argument with "?" directly, for example, /sub?foo=Foo&bar=Bar is an invalid location, and shouldn't be fed as the first argument to this directive. ++ ++Due to an unknown bug in Nginx (it still exists in Nginx 0.8.20), the [[NginxHttpSsiModule|standard SSI module]] is required to ensure that the contents of the subrequests issued by this directive are correctly merged into the output chains of the main one. Fortunately, the SSI module is enabled by default during Nginx's configure process. ++ ++If calling this directive without SSI module enabled, you'll get truncated response without contents of any subrequests and get an alert message in your Nginx's error.log, like this: ++ ++ [alert] 24212#0: *1 the http output chain is empty, client: 127.0.0.1, ... ++ ++Technically speaking, this directive is an example that Nginx content handler issues one or more subrequests directly. AFAIK, the [https://connectical.com/projects/ngx-fancyindex/wiki fancyindex module] also does such kind of things ;) ++ ++Nginx named locations like @foo is ''not'' supported here. ++ ++This directive is logically equivalent to the GET version of [[#echo_subrequest_async|echo_subrequest_async]]. For example, ++ ++ echo_location_async /foo 'bar=Bar'; ++ ++is logically equivalent to ++ ++ echo_subrequest_async GET /foo -q 'bar=Bar'; ++ ++But calling this directive is slightly faster than calling [[#echo_subrequest_async|echo_subrequest_async]] using GET because we don't have to parse the HTTP method names like GET and options like -q. ++ ++This directive is first introduced in [[#v0.09|version 0.09]] of this module and requires at least Nginx 0.7.46. ++ ++== echo_location == ++'''syntax:''' ''echo_location []'' ++ ++'''default:''' ''no'' ++ ++'''context:''' ''location'' ++ ++Just like the [[#echo_location_async|echo_location_async]] directive, but echo_location issues subrequests ''in series'' rather than in parallel. That is, the content handler directives following this directive won't be executed until the subrequest issued by this directive completes. ++ ++The final response body is almost always equivalent to the case when [[#echo_location_async|echo_location_async]] is used instead, only if timing variables is used in the outputs. ++ ++Consider the following example: ++ ++ location /main { ++ echo_reset_timer; ++ echo_location /sub1; ++ echo_location /sub2; ++ echo "took $echo_timer_elapsed sec for total."; ++ } ++ location /sub1 { ++ echo_sleep 2; ++ echo hello; ++ } ++ location /sub2 { ++ echo_sleep 1; ++ echo world; ++ } ++ ++The location /main above will take for total 3 sec to complete (compared to 2 sec if [[#echo_location_async|echo_location_async]] is used instead here). Here's the result in action on my machine: ++ ++ $ curl 'http://localhost/main' ++ hello ++ world ++ took 3.003 sec for total. ++ ++ real 0m3.027s ++ user 0m0.020s ++ sys 0m0.004s ++ ++This directive is logically equivalent to the GET version of [[#echo_subrequest|echo_subrequest]]. For example, ++ ++ echo_location /foo 'bar=Bar'; ++ ++is logically equivalent to ++ ++ echo_subrequest GET /foo -q 'bar=Bar'; ++ ++But calling this directive is slightly faster than calling [[#echo_subrequest|echo_subrequest]] using GET because we don't have to parse the HTTP method names like GET and options like -q. ++ ++Behind the scene, it creates an ngx_http_post_subrequest_t object as a ''continuation'' and passes it into the ngx_http_subrequest function call. Nginx will later reopen this "continuation" in the subrequest's ngx_http_finalize_request function call. We resumes the execution of the parent-request's content handler and starts to run the next directive (command) if any. ++ ++Nginx named locations like @foo is ''not'' supported here. ++ ++This directive was first introduced in the [[#v0.12|release v0.12]]. ++ ++See also [[#echo_location_async|echo_location_async]] for more details about the meaning of the arguments. ++ ++== echo_subrequest_async == ++'''syntax:''' ''echo_subrequest_async [-q ] [-b ]'' ++ ++'''default:''' ''no'' ++ ++'''context:''' ''location'' ++ ++Initiate an asynchronous subrequest using HTTP method, an optional url arguments (or querystring), and an option request body. ++ ++This directive is very much like a generalized version of the [[#echo_location_async|echo_location_async]] directive. ++ ++Here's a small example demonstrating its usage: ++ ++ ++ location /multi { ++ echo_subrequest_async POST '/sub' -q 'foo=Foo' -b 'hi'; ++ echo_subrequest_async PUT '/sub' -q 'bar=Bar' -b 'hello'; ++ } ++ location /sub { ++ echo "querystring: $query_string"; ++ echo "method: $echo_request_method"; ++ echo "body: $echo_request_body"; ++ echo "content length: $http_content_length"; ++ echo '///'; ++ } ++ ++ ++Then on the client side: ++ ++ ++ $ curl 'http://localhost/multi' ++ querystring: foo=Foo ++ method: POST ++ body: hi ++ content length: 2 ++ /// ++ querystring: bar=Bar ++ method: PUT ++ body: hello ++ content length: 5 ++ /// ++ ++ ++Here's more funny example using the standard [[#NginxHttpProxyModule|proxy module]] to handle the subrequest: ++ ++ ++ location /main { ++ echo_subrequest_async POST /sub -b 'hello, world'; ++ } ++ location /sub { ++ proxy_pass $scheme://127.0.0.1:$server_port/proxied; ++ } ++ location /proxied { ++ echo "method: $echo_request_method."; ++ ++ # we need to read body explicitly here...or $echo_request_body ++ # will evaluate to empty ("") ++ echo_read_request_body; ++ ++ echo "body: $echo_request_body."; ++ } ++ ++ ++Then on the client side, we can see that ++ ++ ++ $ curl 'http://localhost/main' ++ method: POST. ++ body: hello, world. ++ ++ ++Nginx named locations like @foo is ''not'' supported here. ++ ++This directive was first introduced in the [[#v0.15|release v0.15]]. ++ ++See also the [[#echo_subrequest|echo_subrequest]] and [[#echo_location_async|echo_location_async]] directives. ++ ++== echo_subrequest == ++'''syntax:''' ''echo_subrequest_async [-q ] [-b ]'' ++ ++'''default:''' ''no'' ++ ++'''context:''' ''location'' ++ ++This is the synchronous version of the [[#echo_subrequest_async|echo_subrequest_async]] directive. And just like [[#echo_location|echo_location]], it does not block the Nginx worker process (while [[#echo_blocking_sleep|echo_blocking_sleep]] does), rather, it uses continuation to pass control along the subrequest chain. ++ ++See [[#echo_subrequest_async|echo_subrequest_async]] for more details. ++ ++Nginx named locations like @foo is ''not'' supported here. ++ ++This directive was first introduced in the [[#v0.15|release v0.15]]. ++ ++== echo_foreach_split == ++'''syntax:''' ''echo_foreach_split '' ++ ++'''default:''' ''no'' ++ ++'''context:''' ''location'' ++ ++Split the second argument string using the delimiter specified in the first argument, and then iterate through the resulting items. For instance: ++ ++ ++ location /loop { ++ echo_foreach_split ',' $arg_list; ++ echo "item: $echo_it"; ++ echo_end; ++ } ++ ++ ++Accessing /main yields ++ ++ ++ $ curl 'http://localhost/loop?list=cat,dog,mouse' ++ item: cat ++ item: dog ++ item: mouse ++ ++ ++As seen in the previous example, this directive should always be accompanied by an [[#echo_end|echo_end]] directive. ++ ++Parallel echo_foreach_split loops are allowed, but nested ones are currently forbidden. ++ ++The delimiter argument could contain ''multiple'' arbitrary characters, like ++ ++ ++ echo_foreach_split '-a-' 'cat-a-dog-a-mouse'; ++ echo $echo_it; ++ echo_end; ++ ++ ++Logically speaking, this looping structure is just the foreach loop combined with a split function call in Perl (using the previous example): ++ ++ ++ foreach (split ',', $arg_list) { ++ print "item $_\n"; ++ } ++ ++ ++People will also find it useful in merging multiple .js or .css resources into a whole. Here's an example: ++ ++ ++ location /merge { ++ default_type 'text/javascript'; ++ ++ echo_foreach_split '&' $query_string; ++ echo "/* JS File $echo_it */"; ++ echo_location_async $echo_it; ++ echo; ++ echo_end; ++ } ++ ++ ++Then accessing /merge to merge the .js resources specified in the query string: ++ ++ ++ $ curl 'http://localhost/merge?/foo/bar.js&/yui/blah.js&/baz.js' ++ ++ ++One can also use third-party Nginx cache module to cache the merged response generated by the /merge location in the previous example. ++ ++This directive was first introduced in the [[#v0.17|release v0.17]]. ++ ++== echo_end == ++'''syntax:''' ''echo_end'' ++ ++'''default:''' ''no'' ++ ++'''context:''' ''location'' ++ ++This directive is used to terminate the body of looping and conditional control structures like [[#echo_foreach_split|echo_foreach_split]]. ++ ++This directive was first introduced in the [[#v0.17|release v0.17]]. ++ ++== echo_request_body == ++'''syntax:''' ''echo_request_body'' ++ ++'''default:''' ''no'' ++ ++'''context:''' ''location'' ++ ++Outputs the contents of the request body previous read. ++ ++Behind the scene, it's implemented roughly like this: ++ ++ ++ if (r->request_body && r->request_body->bufs) { ++ return ngx_http_output_filter(r, r->request_body->bufs); ++ } ++ ++ ++Unlike the [[#$echo_request_body|$echo_request_body]] and $request_body variables, this directive will show the whole request body even if some parts or all parts of it are saved in temporary files on the disk. ++ ++It is a "no-op" if no request body has been read yet. ++ ++This directive was first introduced in the [[#v0.18|release v0.18]]. ++ ++See also [[#echo_read_request_body|echo_read_request_body]] and the [[NginxHttpChunkinModule|chunkin module]]. ++ ++== echo_exec == ++'''syntax:''' ''echo_exec []'' ++ ++'''syntax:''' ''echo_exec '' ++ ++'''default:''' ''no'' ++ ++'''context:''' ''location'' ++ ++Does an internal redirect to the location specified. An optional query string can be specified for normal locations, as in ++ ++ ++ location /foo { ++ echo_exec /bar weight=5; ++ } ++ location /bar { ++ echo $arg_weight; ++ } ++ ++ ++Or equivalently ++ ++ ++ location /foo { ++ echo_exec /bar?weight=5; ++ } ++ location /bar { ++ echo $arg_weight; ++ } ++ ++ ++Named locations are also supported. Here's an example: ++ ++ ++ location /foo { ++ echo_exec @bar; ++ } ++ location @bar { ++ # you'll get /foo rather than @bar ++ # due to a potential bug in nginx. ++ echo $echo_request_uri; ++ } ++ ++ ++But query string (if any) will always be ignored for named location redirects due to a limitation in the ngx_http_named_location function. ++ ++Never try to echo things before the echo_exec directive or you won't see the proper response of the location you want to redirect to. Because any echoing will cause the original location handler to send HTTP headers before the redirection happens. ++ ++Technically speaking, this directive exposes the Nginx internal API functions ngx_http_internal_redirect and ngx_http_named_location. ++ ++This directive was first introduced in the [[#v0.21|v0.21 release]]. ++ ++= Filter Directives = ++ ++Use of the following directives trigger the filter registration of this module. By default, no filter will be registered by this module. ++ ++Every filter directive supports variable interpolation in its arguments (if any). ++ ++== echo_before_body == ++'''syntax:''' ''echo_before_body [options] [argument]...'' ++ ++'''default:''' ''no'' ++ ++'''context:''' ''location'' ++ ++It's the filter version of the [[#echo|echo]] directive, and prepends its output to the beginning of the original outputs generated by the underlying content handler. ++ ++An example is ++ ++ ++ location /echo { ++ echo_before_body hello; ++ proxy_pass $scheme://127.0.0.1:$server_port$request_uri/more; ++ } ++ location /echo/more { ++ echo world ++ } ++ ++ ++Accessing /echo from the client side yields ++ ++ hello ++ world ++ ++In the previous sample, we borrow the [[NginxHttpProxyModule|standard proxy module]] to serve as the underlying content handler that generates the "main contents". ++ ++Multiple instances of this filter directive are also allowed, as in: ++ ++ ++ location /echo { ++ echo_before_body hello; ++ echo_before_body world; ++ echo !; ++ } ++ ++ ++On the client side, the output is like ++ ++ ++ $ curl 'http://localhost/echo' ++ hello ++ world ++ ! ++ ++ ++In this example, we also use the [[#Content Handler Directives|content handler directives]] provided by this module as the underlying content handler. ++ ++This directive also supports the -n and -- options like the [[#echo|echo]] directive. ++ ++This directive can be mixed with its brother directive [[#echo_after_body|echo_after_body]]. ++ ++== echo_after_body == ++'''syntax:''' ''echo_after_body [argument]...'' ++ ++'''default:''' ''no'' ++ ++'''context:''' ''location'' ++ ++'''WARNING''' this directive does not work for nginx >= 0.7.65. ++ ++It's very much like the [[#echo_before_body|echo_before_body]] directive, but ''appends'' its output to the end of the original outputs generated by the underlying content handler. ++ ++Here's a simple example: ++ ++ ++ location /echo { ++ echo_after_body hello; ++ proxy_pass http://127.0.0.1:$server_port$request_uri/more; ++ } ++ location /echo/more { ++ echo world ++ } ++ ++ ++Accessing /echo from the client side yields ++ ++ world ++ hello ++ ++Multiple instances are allowed, as in: ++ ++ ++ location /echo { ++ echo_after_body hello; ++ echo_after_body world; ++ echo i; ++ echo say; ++ } ++ ++ ++The output on the client side while accessing the /echo location looks like ++ ++ ++ i ++ say ++ hello ++ world ++ ++ ++This directive also supports the -n and -- options like the [[#echo|echo]] directive. ++ ++When this directive is used in a location accessed by a subrequest, it replies on the sync flag set in a chain buffer to indicate the end of the output for nginx >= 0.8.7. This is a hack because Nginx does not provide a reliable way to determine the end of the output chain in a subrequest's output filter. Use it in subrequests with care. ++ ++This directive can be mixed with its brother directive [[#echo_before_body|echo_before_body]]. ++ ++= Variables = ++ ++== $echo_it == ++ ++This is a "topic variable" used by [[#echo_foreach_split|echo_foreach_split]], just like the $_ variable in Perl. ++ ++== $echo_timer_elapsed == ++ ++This variable holds the seconds elapsed since the start of the current request (might be a subrequest though) or the last invocation of the [[#echo_reset_timer|echo_reset_timer]] command. ++ ++The timing result takes three digits after the decimal point. ++ ++References of this variable will force the underlying Nginx timer to update to the current system time, regardless the timer resolution settings elsewhere in the config file, just like the [[#echo_reset_timer|echo_reset_timer]] directive. ++ ++== $echo_request_body == ++ ++Evaluates to the current (sub)request's request body previously read if no part of the body has been saved to a temporary file. To always show the request body even if it's very large, use the [[#echo_request_body|echo_request_body]] directive. ++ ++== $echo_request_method == ++ ++Evaluates to the HTTP request method of the current request (it can be a subrequest). ++ ++Behind the scene, it just takes the string data stored in r->method_name. ++ ++Compare it to the [[#$echo_client_request_method|$echo_client_request_method]] variable. ++ ++At least for Nginx 0.8.20 and older, the [[NginxHttpCoreModule#$request_method|$request_method]] variable provided by the [[NginxHttpCoreModule|http core module]] is actually doing what our [[#$echo_client_request_method|$echo_client_request_method]] is doing. ++ ++This variable was first introduced in our [[#v0.15|v0.15 release]]. ++ ++== $echo_client_request_method == ++ ++Always evaluates to the main request's HTTP method even if the current request is a subrequest. ++ ++Behind the scene, it just takes the string data stored in r->main->method_name. ++ ++Compare it to the [[#$echo_request_method|$echo_request_method]] variable. ++ ++This variable was first introduced in our [[#v0.15|v0.15 release]]. ++ ++== $echo_client_request_headers == ++ ++Evaluates to the original client request's headers. ++ ++Just as the name suggests, it will always take the main request (or the client request) even if it's currently executed in a subrequest. ++ ++A simple example is below: ++ ++ ++ location /echoback { ++ echo "headers are:" ++ echo $echo_client_request_headers; ++ } ++ ++ ++Accessing /echoback yields ++ ++ ++ $ curl 'http://localhost/echoback' ++ headers are ++ GET /echoback HTTP/1.1 ++ User-Agent: curl/7.18.2 (i486-pc-linux-gnu) libcurl/7.18.2 OpenSSL/0.9.8g ++ Host: localhost:1984 ++ Accept: */* ++ ++ ++Behind the scene, it recovers r->main->header_in on the C level and does not construct the headers itself by traversing parsed results in the request object, and strips the last (trailing) CRLF. ++ ++This variable was first introduced in [[#v0.15|version 0.15]]. ++ ++== $echo_cacheable_request_uri == ++ ++Evaluates to the parsed form of the URI (usually led by /) of the current (sub-)request. Unlike the [[#$echo_request_uri|$echo_request_uri]] variable, it is cacheable. ++ ++See [[#$echo_request_uri|$echo_request_uri]] for more details. ++ ++This variable was first introduced in [[#v0.17|version 0.17]]. ++ ++== $echo_request_uri == ++ ++Evaluates to the parsed form of the URI (usually led by /) of the current (sub-)request. Unlike the [[#$echo_cacheable_request_uri|$echo_cacheable_request_uri]] variable, it is ''not'' cacheable. ++ ++This is quite different from the [[NginxHttpCoreModule#$request_uri|$request_uri]] variable exported by the [[NginxHttpCoreModule]], because $request_uri is the ''unparsed'' form of the current request's URI. ++ ++This variable was first introduced in [[#v0.17|version 0.17]]. ++ ++== $echo_incr == ++ ++It is a counter that always generate the current counting number, starting from 1. The counter is always associated with the main request even if it is accessed within a subrequest. ++ ++Consider the following example ++ ++ ++ location /main { ++ echo "main pre: $echo_incr"; ++ echo_location_async /sub; ++ echo_location_async /sub; ++ echo "main post: $echo_incr"; ++ } ++ location /sub { ++ echo "sub: $echo_incr"; ++ } ++ ++ ++Accessing /main yields ++ ++ main pre: 1 ++ sub: 3 ++ sub: 4 ++ main post: 2 ++ ++This directive was first introduced in the [[#v0.18|v0.18 release]]. ++ ++== $echo_response_status == ++ ++Evaluates to the status code of the current (sub)request, null if not any. ++ ++Behind the scene, it's just the textual representation of r->headers_out->status. ++ ++This directive was first introduced in the [[#v0.23|v0.23 release]]. ++ ++= Installation = ++ ++Grab the nginx source code from [http://nginx.net/ nginx.net], for example, ++the version 0.8.41 (see [[#Compatibility|nginx compatibility]]), and then build the source with this module: ++ ++ ++ $ wget 'http://sysoev.ru/nginx/nginx-0.8.41.tar.gz' ++ $ tar -xzvf nginx-0.8.41.tar.gz ++ $ cd nginx-0.8.41/ ++ ++ # Here we assume you would install you nginx under /opt/nginx/. ++ $ ./configure --prefix=/opt/nginx \ ++ --add-module=/path/to/echo-nginx-module ++ ++ $ make -j2 ++ $ make install ++ ++ ++Download the latest version of the release tarball of this module from [http://github.com/agentzh/echo-nginx-module/downloads echo-nginx-module file list]. ++ ++= Compatibility = ++ ++The following versions of Nginx should work with this module: ++ ++* '''0.8.x''' (last tested version is 0.8.40) ++* '''0.7.x >= 0.7.21''' (last tested version is 0.7.66) ++ ++In particular, ++ ++* the directive [[#echo_location_async|echo_location_async]] and its brother [[#echo_subrequest_async|echo_subrequest_async]] do ''not'' work with '''0.7.x < 0.7.46'''. ++* the [[#echo_after_body|echo_after_body]] directive does ''not'' work at all with nginx '''< 0.8.7'''. ++* the [[#echo_sleep|echo_sleep]] directive cannot be used after [[#echo_location|echo_location]] or [[#echo_subrequest|echo_subrequest]] for nginx '''< 0.8.11'''. ++ ++Earlier versions of Nginx like 0.6.x and 0.5.x will ''not'' work at all. ++ ++If you find that any particular version of Nginx above 0.7.21 does not work with this module, please consider [[#Report Bugs|reporting a bug]]. ++ ++= Modules that use this module for testing = ++ ++The following modules take advantage of this echo module in their test suite: ++ ++* The [[NginxHttpMemcModule|memc]] module that supports almost the whole memcached TCP protocol. ++* The [[NginxHttpChunkinModule|chunkin]] module that adds HTTP 1.1 chunked input support to Nginx. ++* The [[NginxHttpHeadersMoreModule|headers_more]] module that allows you to add, set, and clear input and output headers under the conditions that you specify. ++* The echo module itself. ++ ++Please mail me other modules that use echo in any form and I'll add them to the list above :) ++ ++= Report Bugs = ++ ++Although a lot of effort has been put into testing and code tuning, there must be some serious bugs lurking somewhere in this module. So whenever you are bitten by any quirks, please don't hesitate to ++ ++# send a bug report or even patches to , ++# or create a ticket on the [http://github.com/agentzh/echo-nginx-module/issues issue tracking interface] provided by GitHub. ++ ++= Source Repository = ++ ++Available on github at [http://github.com/agentzh/echo-nginx-module agentzh/echo-nginx-module]. ++ ++= ChangeLog = ++ ++== v0.34 == ++* we no longer use the problematic ngx_strXcmp macros in our source because it may cause invalid reads and thus segmentation faults. thanks Piotr Sikora. ++ ++== v0.33 == ++* fixed compatibility with nginx 0.7.66+ because the ngx_time_update macro's parameter list has changed. Thanks Guang Feng (蔡镜明). ++ ++== v0.32 == ++* we should have used ngx_calloc_buf instead of ngx_alloc_buf for the last chunk generated for [[#echo_after_body|echo_after_body]]. thanks valgrind's memcheck tool. ++* we should initialize flags before feeding it into ngx_http_parse_unsafe_uri. thanks valgrind's memcheck tool. ++* fixed a minor issue in the [[#echo_location|echo_location]]/[[#echo_subrequest|echo_subrequest]] implementation, which used to have race conditions. ++ ++== v0.31 == ++ ++* the echo wev handler should not proceed if it is still waiting for some sequential subrequest or has just processed one to avoid bouncing issues. ++* fixed a segfault for echo_exec for 0.7.x: we should check r->done before proceeding. ++* no longer explicitly set r->write_event_handler to ngx_http_request_empty_handler because it's totally wrong for the state machine. ++* fixed the sequential subrequest model bugs: we should ensure the pr->write_event_handler gets called immediately after the post_subrequest callback when the subrequest finalizes. ++ ++== v0.30 == ++ ++* fixed the [[#echo_exec|echo_exec]] directive for nginx >= 0.8.11. we didn't get the r->main->count right in the previous version. ++ ++== v0.29 == ++ ++* refactored the core of this module. now the implementation of [[#echo_location|echo_location]], [[#echo_subrequest|echo_subrequest]], [[#echo_sleep|echo_sleep]], and [[#echo_read_request_body|echo_read_request_body]] finally fit well with the nginx event model and Igor Sysoev's way of thinking. ++ ++== v0.28 == ++ ++* added support for the -n and -- options to the [[#echo|echo]], [[#echo_before_body|echo_before_body]], and [[#echo_after_body|echo_after_body]] directives. ++ ++== v0.27 == ++ ++* applied the patch from Sergey A. Osokin to work with nginx 0.8.35. ++ ++== v0.26 == ++ ++* bug fix: we should bypass upstream filters in our echo filters. an output filter should ever call ngx_http_output_filter nor ngx_http_send_special. ++ ++== v0.25 == ++ ++* now we register a request cleanup handler to ensure our sleep event's timer will always get properly deleted even if the request is quit prematurely. this affects the echo_sleep directive. ++* use ngx_null_string whenever possible in the source. ++* sync'd the bundled test scaffold to Test::Nginx 0.07. ++ ++== v0.24 == ++ ++* various source file name and coding style fixes. (the code now looks more like Igor Sysoev's.) ++ ++== v0.23 == ++ ++* now the subrequest can read the client request body directly (for the main request) because we made subrequests inherit its parent's r->header_in as well. This affects the [[#echo_read_request_body|echo_read_request_body]] directive. ++* fixed [[#echo_after_body|echo_after_body]] in subrequests by using a hack (checking cl->buf->sync for the last buf) for nginx 0.8.7+ only. ++* added new varaible [[#$echo_response_status|$echo_response_status]] to help testing the status code of a subrequest. (The [[NginxHttpMemcModule|memc]] module makes use of it.) ++* use the ngx_calloc_buf macro to allocate new bufs in the code rather than explicit ngx_pcalloc calls for safety. ++ ++== v0.22 == ++ ++* Now we allowed all the directives appear in the [[NginxHttpRewriteModule|rewrite module]]'s [[NginxHttpRewriteModule#if|if]] block. But so far I've only tested the [[#echo|echo]] directive. ++ ++== v0.21 == ++ ++* Added a new directive named [[#echo_exec|echo_exec]] which does internal redirect to other (named) locations. ++ ++== v0.20 == ++ ++* Fixed a bug in [[#echo_sleep|echo_sleep]]'s r->main->count handling for nginx 0.8.x. This bug will cause the server to hang when proxing a location with [[#echo_sleep|echo_sleep]]. ++* Applied the ngx_str3cmp, ngx_str4cmp, and ngx_str6cmp optimizing macros to the parse_method_name function, as suggested by Marcus Clyne. ++* Added [[#TODO|TODO items]] regarding $echo_random and echo_repeat suggested by Marcus Clyne. ++ ++== v0.19 == ++* Fixed the CPS-style chained subrequest model for the [[#echo_location|echo_location]] and [[#echo_subrequest|echo_subrequest]] directives. they are now working perfectly and will not hang the server with the recent nginx 0.8.21 ~ 0.8.27 releases. To be specifically, the chained subrequest should call ngx_http_finalize_request on its parent request if the content handler of the parent request does not return NGX_DONE. ++* Undeprecated the [[#echo_location|echo_location]] and [[#echo_subrequest|echo_subrequest]] directives. ++ ++== v0.18 == ++* Fixed the "zero size buf in output" alerts in error.log. ++* Added the new directive [[#echo_request_body|echo_request_body]]. ++* Now we use the ngx_http_parse_unsafe_uri function to check the locations to [[#echo_location_async|echo_location_async]] and its friends. Thanks Arvind Jayaprakash for suggesting this fix. ++* Deprecated the [[#echo_location|echo_location]] and [[#echo_subrequest|echo_subrequest]] directives. ++* For HTTP 1.0 clients, use the buf length of the first chain link as the output header Content-Length. ++* Implemented new variable [[#$echo_incr|$echo_incr]]. ++ ++== v0.17 == ++* Added new directives [[#echo_foreach_split|echo_foreach_split]] and [[#echo_end|echo_end]]. Also introduced a "topic variable" named [[#$echo_it|$echo_it]]. ++* Added new variables [[#$echo_request_uri|$echo_request_uri]] and [[#$echo_cacheable_request_uri|$echo_cacheable_request_uri]]. ++ ++== v0.16 == ++* Now the subrequests issued by the [[#echo_location|echo_location_async]] and [[#echo_location|echo_location]] directives no longer inherit cached variable values from its parent request. (The underlying ngx_http_subrequest function, however, does automatic cachable variable value inheritance.) ++* Added an undocumented variable ''echo_cached_request_uri'' to help testing of this module. ++ ++== v0.15 == ++ ++* Added new directives [[#echo_subrequest|echo_subrequest]] and [[#echo_subrequest_async|echo_subrequest_async]] for the full nginx subrequest API. ++* Removed the echo_client_request_headers directive, and provided the [[#$echo_client_request_headers|$echo_client_request_headers]] variable instead. ++* Added new variables [[#$echo_request_method|$echo_request_method]] and [[#$echo_client_request_method|$echo_client_request_method]]. ++ ++== v0.14 == ++ ++* Added new directive [[#echo_read_request_body|echo_read_request_body]] to explicitly read client request body so that the [[NginxHttpCoreModule#$request_body]] variable will always have non-empty values. ++* Now we shuffer test cases automatically in .t files and fixed bugs in the tests themselves which are hidden by config reload fallback in failure. ++ ++== v0.13 == ++ ++* Fixed the special cases when the outputs of a [[#echo_duplicate|echo_duplicate]] directive is empty. ++* Now we explicitly clear content length and accept ranges headers in the content handler. ++ ++== v0.12 == ++ ++* Implemented the [[#echo_location|echo_location]] directive, which can issue chained GET subrequests in the Continuation Passing Style (CPS), rather than the parallel subrequest issued by the [[#echo_location_async|echo_location_async]] directive. ++ ++== v0.11 == ++ ++* Implemented the [[#echo_duplicate|echo_duplicate]] directive to help generating large chunk of data for testing. ++ ++== v0.10 == ++ ++* Fixed compilation regression against Nginx 0.7.21. This bug appears in version 0.09. ++* Refactored the codebase by splitting source into various small files. ++ ++== v0.09 == ++ ++* Reimplement the [[#echo_sleep|echo_sleep]] directive using per-request event and timer; the old implementation uses the global connection's read/write event to register timer, so it will break horribly when multiple subrequests "sleep" at the same time. ++* Added the [[#echo_location_async|echo_location_async]] directive which can issue a GET subrequest and insert its contents herein. ++ ++== v0.08 == ++ ++* [[#echo_sleep|echo_sleep]]: now we delete our write event timer in the post_sleep handle. ++* Added doc/manpage.wiki which tracks changes in the [http://wiki.nginx.org/NginxHttpEchoModule wiki page]. ++* Added the util/wiki2pod.pl script to convert doc/manpage.wiki to README. ++* Disabled the DDEBUG macro in the C source by default. ++ ++= Test Suite = ++ ++This module comes with a Perl-driven test suite. The [http://github.com/agentzh/echo-nginx-module/tree/master/test/t/ test cases] are ++[http://github.com/agentzh/echo-nginx-module/blob/master/test/t/echo.t declarative] too. Thanks to the [http://search.cpan.org/perldoc?Test::Base Test::Base] module in the Perl world. ++ ++To run it on your side: ++ ++ ++ $ cd test ++ $ PATH=/path/to/your/nginx-with-echo-module:$PATH prove -r t ++ ++ ++You need to terminate any Nginx processes before running the test suite if you have changed the Nginx server binary. ++ ++At the moment, [http://search.cpan.org/perldoc?LWP::UserAgent LWP::UserAgent] is used by the [http://github.com/agentzh/echo-nginx-module/blob/master/test/lib/Test/Nginx/Echo.pm test scaffold] for simplicity and it's rather weak in testing ''streaming'' behavior of Nginx (I'm using "curl" to test these aspects manually for now). I'm considering coding up my own Perl HTTP client library based on [http://search.cpan.org/perldoc?IO::Select IO::Select] and [http://search.cpan.org/perldoc?IO::Socket IO::Socket] (there might be already one around?). ++ ++Because a single nginx server (by default, localhost:1984) is used across all the test scripts (.t files), it's meaningless to run the test suite in parallel by specifying -jN when invoking the prove utility. ++ ++Some parts of the test suite requires standard modules [[NginxHttpProxyModule|proxy]], [[NginxHttpRewriteModule|rewrite]] and [[NginxHttpSsiModule|SSI]] to be enabled as well when building Nginx. ++ ++= TODO = ++ ++* Fix the [[#echo_after_body|echo_after_body]] directive in subrequests. ++* Add directives ''echo_read_client_request_body'' and ''echo_request_headers''. ++* Add new directive ''echo_log'' to use Nginx's logging facility directly from the config file and specific loglevel can be specified, as in ++ ++ ++ echo_log debug "I am being called."; ++ ++ ++* Add support for options -h and -t to [[#echo_subrequest_async|echo_subrequest_async]] and [[#echo_subrequest|echo_subrequest]]. For example ++ ++ ++ echo_subrequest POST /sub -q 'foo=Foo&bar=Bar' -b 'hello' -t 'text/plan' -h 'X-My-Header: blah blah' ++ ++ ++* Add options to control whether a subrequest should inherit cached variables from its parent request (i.e. the current request that is calling the subrequest in question). Currently none of the subrequests issued by this module inherit the cached variables from the parent request. ++* Add new variable ''$echo_active_subrequests'' to show r->main->count - 1. ++* Add the ''echo_file'' and ''echo_cached_file'' directives. ++* Add new varaible ''$echo_request_headers'' to accompany the existing [[#$echo_client_request_headers|$echo_client_request_headers]] variable. ++* Add new directive ''echo_foreach'', as in ++ ++ ++ echo_foreach 'cat' 'dog' 'mouse'; ++ echo_location_async "/animals/$echo_it"; ++ echo_end; ++ ++ ++* Add new directive ''echo_foreach_range'', as in ++ ++ ++ echo_foreach_range '[1..100]' '[a-zA-z0-9]'; ++ echo_location_async "/item/$echo_it"; ++ echo_end; ++ ++ ++* Add new directive ''echo_repeat'', as in ++ ++ ++ echo_repeat 10 $i { ++ echo "Page $i"; ++ echo_location "/path/to/page/$i"; ++ } ++ ++ ++This is just another way of saying ++ ++ ++ echo_foreach_range $i [1..10]; ++ echo "Page $i"; ++ echo_location "/path/to/page/$i"; ++ echo_end; ++ ++ ++Thanks Marcus Clyne for providing this idea. ++ ++* Add new variable $echo_random which always returns a random non-negative integer with the lower/upper limit specified by the new directives echo_random_min and echo_random_max. For example, ++ ++ ++ echo_random_min 10 ++ echo_random_max 200 ++ echo "random number: $echo_random"; ++ ++ ++Thanks Marcus Clyne for providing this idea. ++ ++= Getting involved = ++ ++You'll be very welcomed to submit patches to the [[#Author|author]] or just ask for a commit bit to the [[#Source Repository|source repository]] on GitHub. ++ ++ ++ ++= Author = ++ ++agentzh (章亦春) '''' ++ ++This wiki page is also maintained by the author himself, and everybody is encouraged to improve this page as well. ++ ++= Copyright & License = ++ ++Copyright (c) 2009, Taobao Inc., Alibaba Group ( http://www.taobao.com ). ++ ++Copyright (c) 2009, agentzh . ++ ++This module is licensed under the terms of the BSD license. ++ ++Redistribution and use in source and binary forms, with or without ++modification, are permitted provided that the following conditions ++are met: ++ ++* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. ++* 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. ++* Neither the name of the Taobao Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. ++ ++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT ++HOLDER 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. ++ ++= See Also = ++ ++* The original [http://agentzh.spaces.live.com/blog/cns!FF3A735632E41548!478.entry blog post] about this module's initial development. ++* The standard [[NginxHttpAdditionModule|addition filter module]]. ++* The standard [[NginxHttpProxyModule|proxy module]]. ++ +Index: 0.8/modules/nginx-echo/src/ddebug.h +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ 0.8/modules/nginx-echo/src/ddebug.h 2010-10-31 07:35:23.648338001 +0000 +@@ -0,0 +1,109 @@ ++#ifndef DDEBUG_H ++#define DDEBUG_H ++ ++#include ++#include ++ ++#if defined(DDEBUG) && (DDEBUG) ++ ++# if (NGX_HAVE_VARIADIC_MACROS) ++ ++# define dd(...) fprintf(stderr, "echo *** %s: ", __func__); \ ++ fprintf(stderr, __VA_ARGS__); \ ++ fprintf(stderr, " at %s line %d.\n", __FILE__, __LINE__) ++ ++# else ++ ++#include ++#include ++ ++#include ++ ++static void dd(const char * fmt, ...) { ++} ++ ++# endif ++ ++# if DDEBUG > 1 ++ ++# define dd_enter() dd_enter_helper(r, __func__) ++ ++static void dd_enter_helper(ngx_http_request_t *r, const char *func) { ++ ngx_http_posted_request_t *pr; ++ ++ fprintf(stderr, ">enter %s %.*s %.*s?%.*s c:%d m:%p r:%p ar:%p pr:%p", ++ func, ++ (int) r->method_name.len, r->method_name.data, ++ (int) r->uri.len, r->uri.data, ++ (int) r->args.len, r->args.data, ++ 0/*(int) r->main->count*/, r->main, ++ r, r->connection->data, r->parent); ++ ++ if (r->posted_requests) { ++ fprintf(stderr, " posted:"); ++ ++ for (pr = r->posted_requests; pr; pr = pr->next) { ++ fprintf(stderr, "%p,", pr); ++ } ++ } ++ ++ fprintf(stderr, "\n"); ++} ++ ++# else ++ ++# define dd_enter() ++ ++# endif ++ ++#else ++ ++# if (NGX_HAVE_VARIADIC_MACROS) ++ ++# define dd(...) ++ ++# define dd_enter() ++ ++# else ++ ++#include ++ ++static void dd(const char * fmt, ...) { ++} ++ ++static void dd_enter() { ++} ++ ++# endif ++ ++#endif ++ ++#if defined(DDEBUG) && (DDEBUG) ++ ++#define dd_check_read_event_handler(r) \ ++ dd("r->read_event_handler = %s", \ ++ r->read_event_handler == ngx_http_block_reading ? \ ++ "ngx_http_block_reading" : \ ++ r->read_event_handler == ngx_http_test_reading ? \ ++ "ngx_http_test_reading" : \ ++ r->read_event_handler == ngx_http_request_empty_handler ? \ ++ "ngx_http_request_empty_handler" : "UNKNOWN") ++ ++#define dd_check_write_event_handler(r) \ ++ dd("r->write_event_handler = %s", \ ++ r->write_event_handler == ngx_http_handler ? \ ++ "ngx_http_handler" : \ ++ r->write_event_handler == ngx_http_core_run_phases ? \ ++ "ngx_http_core_run_phases" : \ ++ r->write_event_handler == ngx_http_request_empty_handler ? \ ++ "ngx_http_request_empty_handler" : "UNKNOWN") ++ ++#else ++ ++#define dd_check_read_event_handler(r) ++#define dd_check_write_event_handler(r) ++ ++#endif ++ ++#endif /* DDEBUG_H */ ++ +Index: 0.8/modules/nginx-echo/src/ngx_http_echo_echo.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ 0.8/modules/nginx-echo/src/ngx_http_echo_echo.c 2010-10-31 07:35:23.648338001 +0000 +@@ -0,0 +1,270 @@ ++#define DDEBUG 0 ++#include "ddebug.h" ++ ++#include "ngx_http_echo_echo.h" ++#include "ngx_http_echo_util.h" ++#include "ngx_http_echo_filter.h" ++ ++#include ++ ++static ngx_buf_t ngx_http_echo_space_buf; ++ ++static ngx_buf_t ngx_http_echo_newline_buf; ++ ++ngx_int_t ++ngx_http_echo_echo_init(ngx_conf_t *cf) ++{ ++ static u_char space_str[] = " "; ++ static u_char newline_str[] = "\n"; ++ ++ dd("global init..."); ++ ++ ngx_memzero(&ngx_http_echo_space_buf, sizeof(ngx_buf_t)); ++ ngx_http_echo_space_buf.memory = 1; ++ ngx_http_echo_space_buf.start = ++ ngx_http_echo_space_buf.pos = ++ space_str; ++ ngx_http_echo_space_buf.end = ++ ngx_http_echo_space_buf.last = ++ space_str + sizeof(space_str) - 1; ++ ++ ngx_memzero(&ngx_http_echo_newline_buf, sizeof(ngx_buf_t)); ++ ngx_http_echo_newline_buf.memory = 1; ++ ngx_http_echo_newline_buf.start = ++ ngx_http_echo_newline_buf.pos = ++ newline_str; ++ ngx_http_echo_newline_buf.end = ++ ngx_http_echo_newline_buf.last = ++ newline_str + sizeof(newline_str) - 1; ++ ++ return NGX_OK; ++} ++ ++ ++ngx_int_t ++ngx_http_echo_exec_echo(ngx_http_request_t *r, ++ ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args, ++ ngx_flag_t in_filter, ngx_array_t *opts) ++{ ++ ngx_uint_t i; ++ ++ ngx_buf_t *space_buf; ++ ngx_buf_t *newline_buf; ++ ngx_buf_t *buf; ++ ++ ngx_str_t *computed_arg; ++ ngx_str_t *computed_arg_elts; ++ ngx_str_t *opt; ++ ++ ngx_chain_t *cl = NULL; /* the head of the chain link */ ++ ngx_chain_t **ll = &cl; /* always point to the address of the last link */ ++ ++ ++ dd_enter(); ++ ++ if (computed_args == NULL) { ++ return NGX_HTTP_INTERNAL_SERVER_ERROR; ++ } ++ ++ computed_arg_elts = computed_args->elts; ++ for (i = 0; i < computed_args->nelts; i++) { ++ computed_arg = &computed_arg_elts[i]; ++ ++ if (computed_arg->len == 0) { ++ buf = NULL; ++ ++ } else { ++ buf = ngx_calloc_buf(r->pool); ++ if (buf == NULL) { ++ return NGX_HTTP_INTERNAL_SERVER_ERROR; ++ } ++ ++ buf->start = buf->pos = computed_arg->data; ++ buf->last = buf->end = computed_arg->data + ++ computed_arg->len; ++ ++ buf->memory = 1; ++ } ++ ++ if (cl == NULL) { ++ cl = ngx_alloc_chain_link(r->pool); ++ if (cl == NULL) { ++ return NGX_HTTP_INTERNAL_SERVER_ERROR; ++ } ++ cl->buf = buf; ++ cl->next = NULL; ++ ll = &cl->next; ++ } else { ++ /* append a space first */ ++ *ll = ngx_alloc_chain_link(r->pool); ++ ++ if (*ll == NULL) { ++ return NGX_HTTP_INTERNAL_SERVER_ERROR; ++ } ++ ++ space_buf = ngx_calloc_buf(r->pool); ++ ++ if (space_buf == NULL) { ++ return NGX_HTTP_INTERNAL_SERVER_ERROR; ++ } ++ ++ /* nginx clears buf flags at the end of each request handling, ++ * so we have to make a clone here. */ ++ *space_buf = ngx_http_echo_space_buf; ++ ++ (*ll)->buf = space_buf; ++ (*ll)->next = NULL; ++ ++ ll = &(*ll)->next; ++ ++ /* then append the buf only if it's non-empty */ ++ if (buf) { ++ *ll = ngx_alloc_chain_link(r->pool); ++ if (*ll == NULL) { ++ return NGX_HTTP_INTERNAL_SERVER_ERROR; ++ } ++ (*ll)->buf = buf; ++ (*ll)->next = NULL; ++ ++ ll = &(*ll)->next; ++ } ++ } ++ } /* end for */ ++ ++ if (opts && opts->nelts > 0) { ++ opt = opts->elts; ++ if (opt[0].len == 1 && opt[0].data[0] == 'n') { ++ goto done; ++ } ++ } ++ ++ /* append the newline character */ ++ ++ if (cl && cl->buf == NULL) { ++ cl = cl->next; ++ } ++ ++ newline_buf = ngx_calloc_buf(r->pool); ++ ++ if (newline_buf == NULL) { ++ return NGX_HTTP_INTERNAL_SERVER_ERROR; ++ } ++ ++ *newline_buf = ngx_http_echo_newline_buf; ++ ++ if (cl == NULL) { ++ cl = ngx_alloc_chain_link(r->pool); ++ ++ if (cl == NULL) { ++ return NGX_HTTP_INTERNAL_SERVER_ERROR; ++ } ++ ++ cl->buf = newline_buf; ++ cl->next = NULL; ++ /* ll = &cl->next; */ ++ ++ } else { ++ *ll = ngx_alloc_chain_link(r->pool); ++ ++ if (*ll == NULL) { ++ return NGX_HTTP_INTERNAL_SERVER_ERROR; ++ } ++ ++ (*ll)->buf = newline_buf; ++ (*ll)->next = NULL; ++ /* ll = &(*ll)->next; */ ++ } ++ ++done: ++ ++ if (cl == NULL || cl->buf == NULL) { ++ return NGX_OK; ++ } ++ ++ if (in_filter) { ++ return ngx_http_echo_next_body_filter(r, cl); ++ } ++ ++ return ngx_http_echo_send_chain_link(r, ctx, cl); ++} ++ ++ ++ngx_int_t ++ngx_http_echo_exec_echo_flush(ngx_http_request_t *r, ngx_http_echo_ctx_t *ctx) ++{ ++ return ngx_http_send_special(r, NGX_HTTP_FLUSH); ++} ++ ++ ++ngx_int_t ++ngx_http_echo_exec_echo_request_body(ngx_http_request_t *r, ngx_http_echo_ctx_t *ctx) ++{ ++ if (r->request_body && r->request_body->bufs) { ++ return ngx_http_echo_send_chain_link(r, ctx, r->request_body->bufs); ++ } ++ ++ return NGX_OK; ++} ++ ++ ++ngx_int_t ++ngx_http_echo_exec_echo_duplicate(ngx_http_request_t *r, ++ ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args) ++{ ++ ngx_str_t *computed_arg; ++ ngx_str_t *computed_arg_elts; ++ ssize_t i, count; ++ ngx_str_t *str; ++ u_char *p; ++ ngx_int_t rc; ++ ++ ngx_buf_t *buf; ++ ngx_chain_t *cl; ++ ++ ++ dd_enter(); ++ ++ computed_arg_elts = computed_args->elts; ++ ++ computed_arg = &computed_arg_elts[0]; ++ ++ count = ngx_http_echo_atosz(computed_arg->data, computed_arg->len); ++ ++ if (count == NGX_ERROR) { ++ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, ++ "invalid size specified: \"%V\"", computed_arg); ++ ++ return NGX_HTTP_INTERNAL_SERVER_ERROR; ++ } ++ ++ str = &computed_arg_elts[1]; ++ ++ if (count == 0 || str->len == 0) { ++ rc = ngx_http_echo_send_header_if_needed(r, ctx); ++ if (r->header_only || rc >= NGX_HTTP_SPECIAL_RESPONSE) { ++ return rc; ++ } ++ return NGX_OK; ++ } ++ ++ buf = ngx_create_temp_buf(r->pool, count * str->len); ++ if (buf == NULL) { ++ return NGX_HTTP_INTERNAL_SERVER_ERROR; ++ } ++ ++ p = buf->pos; ++ for (i = 0; i < count; i++) { ++ p = ngx_copy(p, str->data, str->len); ++ } ++ buf->last = p; ++ ++ cl = ngx_alloc_chain_link(r->pool); ++ if (cl == NULL) { ++ return NGX_HTTP_INTERNAL_SERVER_ERROR; ++ } ++ cl->next = NULL; ++ cl->buf = buf; ++ ++ return ngx_http_echo_send_chain_link(r, ctx, cl); ++} ++ +Index: 0.8/modules/nginx-echo/src/ngx_http_echo_echo.h +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ 0.8/modules/nginx-echo/src/ngx_http_echo_echo.h 2010-10-31 07:35:23.648338001 +0000 +@@ -0,0 +1,22 @@ ++#ifndef ECHO_ECHO_H ++#define ECHO_ECHO_H ++ ++#include "ngx_http_echo_module.h" ++ ++ngx_int_t ngx_http_echo_echo_init(ngx_conf_t *cf); ++ ++ngx_int_t ngx_http_echo_exec_echo(ngx_http_request_t *r, ++ ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args, ++ ngx_flag_t in_filter, ngx_array_t *opts); ++ ++ngx_int_t ngx_http_echo_exec_echo_request_body(ngx_http_request_t *r, ++ ngx_http_echo_ctx_t *ctx); ++ ++ngx_int_t ngx_http_echo_exec_echo_flush(ngx_http_request_t *r, ++ ngx_http_echo_ctx_t *ctx); ++ ++ngx_int_t ngx_http_echo_exec_echo_duplicate(ngx_http_request_t *r, ++ ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args); ++ ++#endif /* ECHO_ECHO_H */ ++ +Index: 0.8/modules/nginx-echo/src/ngx_http_echo_filter.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ 0.8/modules/nginx-echo/src/ngx_http_echo_filter.c 2010-10-31 07:35:23.648338001 +0000 +@@ -0,0 +1,240 @@ ++#define DDEBUG 0 ++ ++#include "ddebug.h" ++#include "ngx_http_echo_filter.h" ++#include "ngx_http_echo_util.h" ++#include "ngx_http_echo_echo.h" ++ ++#include ++ ++ngx_flag_t ngx_http_echo_filter_used = 0; ++ ++ngx_http_output_header_filter_pt ngx_http_echo_next_header_filter; ++ ++ngx_http_output_body_filter_pt ngx_http_echo_next_body_filter; ++ ++static ngx_int_t ngx_http_echo_header_filter(ngx_http_request_t *r); ++ ++static ngx_int_t ngx_http_echo_body_filter(ngx_http_request_t *r, ngx_chain_t *in); ++ ++/* filter handlers */ ++static ngx_int_t ngx_http_echo_exec_filter_cmds(ngx_http_request_t *r, ++ ngx_http_echo_ctx_t *ctx, ngx_array_t *cmds, ngx_uint_t *iterator); ++ ++ ++ngx_int_t ++ngx_http_echo_filter_init (ngx_conf_t *cf) ++{ ++ if (ngx_http_echo_filter_used) { ++ dd("top header filter: %ld", (unsigned long) ngx_http_top_header_filter); ++ ngx_http_echo_next_header_filter = ngx_http_top_header_filter; ++ ngx_http_top_header_filter = ngx_http_echo_header_filter; ++ ++ dd("top body filter: %ld", (unsigned long) ngx_http_top_body_filter); ++ ngx_http_echo_next_body_filter = ngx_http_top_body_filter; ++ ngx_http_top_body_filter = ngx_http_echo_body_filter; ++ } ++ ++ return NGX_OK; ++} ++ ++ ++static ngx_int_t ++ngx_http_echo_header_filter(ngx_http_request_t *r) ++{ ++ ngx_http_echo_loc_conf_t *conf; ++ ngx_http_echo_ctx_t *ctx; ++ ngx_int_t rc; ++ ++ dd("We're in the header filter..."); ++ ++ ctx = ngx_http_get_module_ctx(r, ngx_http_echo_module); ++ ++ /* XXX we should add option to insert contents for responses ++ * of non-200 status code here... */ ++ /* ++ if (r->headers_out.status != NGX_HTTP_OK) { ++ if (ctx != NULL) { ++ ctx->skip_filter = 1; ++ } ++ return ngx_http_echo_next_header_filter(r); ++ } ++ */ ++ ++ conf = ngx_http_get_module_loc_conf(r, ngx_http_echo_module); ++ if (conf->before_body_cmds == NULL && conf->after_body_cmds == NULL) { ++ if (ctx != NULL) { ++ ctx->skip_filter = 1; ++ } ++ return ngx_http_echo_next_header_filter(r); ++ } ++ ++ if (ctx == NULL) { ++ rc = ngx_http_echo_init_ctx(r, &ctx); ++ if (rc != NGX_OK) { ++ return NGX_ERROR; ++ } ++ ctx->headers_sent = 1; ++ ngx_http_set_ctx(r, ctx, ngx_http_echo_module); ++ } ++ ++ /* enable streaming here (use chunked encoding) */ ++ ngx_http_clear_content_length(r); ++ ngx_http_clear_accept_ranges(r); ++ ++ return ngx_http_echo_next_header_filter(r); ++} ++ ++ ++static ngx_int_t ++ngx_http_echo_body_filter(ngx_http_request_t *r, ngx_chain_t *in) ++{ ++ ngx_http_echo_ctx_t *ctx; ++ ngx_int_t rc; ++ ngx_http_echo_loc_conf_t *conf; ++ ngx_flag_t last; ++ ngx_chain_t *cl; ++ ngx_buf_t *buf; ++ ++ if (in == NULL || r->header_only) { ++ return ngx_http_echo_next_body_filter(r, in); ++ } ++ ++ ctx = ngx_http_get_module_ctx(r, ngx_http_echo_module); ++ ++ if (ctx == NULL || ctx->skip_filter) { ++ return ngx_http_echo_next_body_filter(r, in); ++ } ++ ++ conf = ngx_http_get_module_loc_conf(r, ngx_http_echo_module); ++ ++ if (!ctx->before_body_sent) { ++ ctx->before_body_sent = 1; ++ ++ if (conf->before_body_cmds != NULL) { ++ rc = ngx_http_echo_exec_filter_cmds(r, ctx, conf->before_body_cmds, ++ &ctx->next_before_body_cmd); ++ if (rc != NGX_OK) { ++ return NGX_ERROR; ++ } ++ } ++ } ++ ++ if (conf->after_body_cmds == NULL) { ++ ctx->skip_filter = 1; ++ return ngx_http_echo_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->sync = 1; ++ last = 1; ++ } else if (r != r->main && cl->buf->sync) { ++ dd("Found sync buf"); ++ last = 1; ++ } ++ } ++ ++ rc = ngx_http_echo_next_body_filter(r, in); ++ ++ if (rc == NGX_ERROR || !last) { ++ return rc; ++ } ++ ++ dd("exec filter cmds for after body cmds"); ++ rc = ngx_http_echo_exec_filter_cmds(r, ctx, conf->after_body_cmds, &ctx->next_after_body_cmd); ++ if (rc != NGX_OK) { ++ dd("FAILED: exec filter cmds for after body cmds"); ++ return NGX_ERROR; ++ } ++ ++ ctx->skip_filter = 1; ++ ++ dd("after body cmds executed...terminating..."); ++ ++ /* XXX we can NOT use ++ * ngx_http_send_special(r, NGX_HTTP_LAST) here ++ * because we should bypass the upstream filters. */ ++ if (r != r->main) { ++ return NGX_OK; ++ } ++ ++ buf = ngx_calloc_buf(r->pool); ++ buf->last_buf = 1; ++ ++ cl = ngx_alloc_chain_link(r->pool); ++ if (cl == NULL) { ++ return NGX_HTTP_INTERNAL_SERVER_ERROR; ++ } ++ ++ cl->next = NULL; ++ cl->buf = buf; ++ ++ return ngx_http_echo_next_body_filter(r, cl); ++} ++ ++ ++static ngx_int_t ++ngx_http_echo_exec_filter_cmds(ngx_http_request_t *r, ++ ngx_http_echo_ctx_t *ctx, ngx_array_t *cmds, ++ ngx_uint_t *iterator) ++{ ++ ngx_int_t rc; ++ ngx_array_t *computed_args = NULL; ++ ngx_http_echo_cmd_t *cmd; ++ ngx_http_echo_cmd_t *cmd_elts; ++ ngx_array_t *opts = NULL; ++ ++ for (cmd_elts = cmds->elts; *iterator < cmds->nelts; (*iterator)++) { ++ cmd = &cmd_elts[*iterator]; ++ ++ /* evaluate arguments for the current cmd (if any) */ ++ if (cmd->args) { ++ computed_args = ngx_array_create(r->pool, cmd->args->nelts, ++ sizeof(ngx_str_t)); ++ ++ if (computed_args == NULL) { ++ return NGX_HTTP_INTERNAL_SERVER_ERROR; ++ } ++ ++ opts = ngx_array_create(r->pool, 1, sizeof(ngx_str_t)); ++ ++ if (opts == NULL) { ++ return NGX_HTTP_INTERNAL_SERVER_ERROR; ++ } ++ ++ rc = ngx_http_echo_eval_cmd_args(r, cmd, computed_args, opts); ++ ++ if (rc != NGX_OK) { ++ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, ++ "Failed to evaluate arguments for " ++ "the directive."); ++ return rc; ++ } ++ } ++ ++ /* do command dispatch based on the opcode */ ++ switch (cmd->opcode) { ++ case echo_opcode_echo_before_body: ++ case echo_opcode_echo_after_body: ++ dd("exec echo_before_body or echo_after_body..."); ++ ++ rc = ngx_http_echo_exec_echo(r, ctx, computed_args, ++ 1 /* in filter */, opts); ++ ++ if (rc != NGX_OK) { ++ return rc; ++ } ++ ++ break; ++ default: ++ break; ++ } ++ } ++ ++ return NGX_OK; ++} ++ +Index: 0.8/modules/nginx-echo/src/ngx_http_echo_filter.h +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ 0.8/modules/nginx-echo/src/ngx_http_echo_filter.h 2010-10-31 07:35:23.648338001 +0000 +@@ -0,0 +1,16 @@ ++#ifndef ECHO_FILTER_H ++#define ECHO_FILTER_H ++ ++#include "ngx_http_echo_module.h" ++ ++extern ngx_flag_t ngx_http_echo_filter_used; ++ ++extern ngx_http_output_header_filter_pt ngx_http_echo_next_header_filter; ++ ++extern ngx_http_output_body_filter_pt ngx_http_echo_next_body_filter; ++ ++ ++ngx_int_t ngx_http_echo_filter_init (ngx_conf_t *cf); ++ ++#endif /* ECHO_FILTER_H */ ++ +Index: 0.8/modules/nginx-echo/src/ngx_http_echo_foreach.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ 0.8/modules/nginx-echo/src/ngx_http_echo_foreach.c 2010-10-31 07:35:23.648338001 +0000 +@@ -0,0 +1,174 @@ ++#define DDEBUG 0 ++#include "ddebug.h" ++ ++#include "ngx_http_echo_foreach.h" ++#include "ngx_http_echo_util.h" ++ ++#include ++ ++ngx_int_t ++ngx_http_echo_it_variable(ngx_http_request_t *r, ++ ngx_http_variable_value_t *v, uintptr_t data) ++{ ++ ngx_http_echo_ctx_t *ctx; ++ ngx_uint_t i; ++ ngx_array_t *choices; ++ ngx_str_t *choice_elts, *choice; ++ ++ ctx = ngx_http_get_module_ctx(r, ngx_http_echo_module); ++ ++ if (ctx->foreach != NULL) { ++ choices = ctx->foreach->choices; ++ i = ctx->foreach->next_choice; ++ if (i < choices->nelts) { ++ choice_elts = choices->elts; ++ choice = &choice_elts[i]; ++ ++ v->len = choice->len; ++ v->data = choice->data; ++ v->valid = 1; ++ v->no_cacheable = 1; ++ v->not_found = 0; ++ } ++ ++ return NGX_OK; ++ } ++ ++ v->not_found = 1; ++ ++ return NGX_OK; ++} ++ ++ ++ngx_int_t ++ngx_http_echo_exec_echo_foreach_split(ngx_http_request_t *r, ++ ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args) ++{ ++ ngx_http_echo_loc_conf_t *elcf; ++ ngx_str_t *delimiter, *compound; ++ u_char *pos, *last, *end; ++ ngx_str_t *choice; ++ ngx_str_t *computed_arg_elts; ++ ngx_array_t *cmds; ++ ngx_http_echo_cmd_t *cmd; ++ ngx_http_echo_cmd_t *cmd_elts; ++ ++ if (ctx->foreach != NULL) { ++ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, ++ "Nested echo_foreach not supported yet."); ++ return NGX_HTTP_INTERNAL_SERVER_ERROR; ++ } ++ ++ if (computed_args->nelts < 2) { ++ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, ++ "echo_foreach should take at least two arguments. " ++ "(if your delimiter starts with \"-\", preceding it with a " ++ "\"--\".)"); ++ ++ return NGX_HTTP_INTERNAL_SERVER_ERROR; ++ } ++ ++ computed_arg_elts = computed_args->elts; ++ ++ compound = &computed_arg_elts[1]; ++ ++ dd("HEY coumpound len: %u", compound->len); ++ ++ ctx->foreach = ngx_palloc(r->pool, sizeof(ngx_http_echo_foreach_ctx_t)); ++ ++ if (ctx->foreach == NULL) { ++ return NGX_ERROR; ++ } ++ ++ ctx->foreach->cmd_index = ctx->next_handler_cmd; ++ ++ ctx->foreach->next_choice = 0; ++ ++ ctx->foreach->choices = ngx_array_create(r->pool, 10, sizeof(ngx_str_t)); ++ if (ctx->foreach->choices == NULL) { ++ return NGX_ERROR; ++ } ++ ++ delimiter = &computed_arg_elts[0]; ++ ++ pos = compound->data; ++ end = compound->data + compound->len; ++ while ((last = ngx_http_echo_strlstrn(pos, end, delimiter->data, delimiter->len - 1)) ++ != NULL) { ++ dd("entered the loop"); ++ ++ if (last == pos) { ++ dd("!!! len == 0"); ++ pos = last + delimiter->len; ++ continue; ++ } ++ ++ choice = ngx_array_push(ctx->foreach->choices); ++ if (choice == NULL) { ++ return NGX_ERROR; ++ } ++ ++ choice->data = pos; ++ choice->len = last - pos; ++ pos = last + delimiter->len; ++ } ++ ++ if (pos < end) { ++ choice = ngx_array_push(ctx->foreach->choices); ++ if (choice == NULL) { ++ return NGX_ERROR; ++ } ++ ++ choice->data = pos; ++ choice->len = end - pos; ++ } ++ ++ if (ctx->foreach->choices->nelts == 0) { ++ /* skip the foreach body entirely */ ++ elcf = ngx_http_get_module_loc_conf(r, ngx_http_echo_module); ++ cmds = elcf->handler_cmds; ++ cmd_elts = cmds->elts; ++ for (; ctx->next_handler_cmd < cmds->nelts; ++ ctx->next_handler_cmd++) { ++ cmd = &cmd_elts[ctx->next_handler_cmd + 1]; ++ if (cmd->opcode == echo_opcode_echo_end) { ++ return NGX_OK; ++ } ++ } ++ ++ } ++ ++ return NGX_OK; ++} ++ ++ ++ngx_int_t ++ngx_http_echo_exec_echo_end(ngx_http_request_t *r, ++ ngx_http_echo_ctx_t *ctx) ++{ ++ if (ctx->foreach == NULL) { ++ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, ++ "Found a echo_end that has no corresponding echo_foreach " ++ "before it."); ++ return NGX_ERROR; ++ } ++ ++ ctx->foreach->next_choice++; ++ ++ if (ctx->foreach->next_choice >= ctx->foreach->choices->nelts) { ++ /* TODO We need to explicitly free the foreach ctx from ++ * the pool */ ++ ctx->foreach = NULL; ++ ++ return NGX_OK; ++ } ++ ++ dd("echo_end: ++ next_choice (total: %u): %u", ctx->foreach->choices->nelts, ctx->foreach->next_choice); ++ ++ /* the main handler dispatcher loop will increment ++ * ctx->next_handler_cmd for us anyway. */ ++ ctx->next_handler_cmd = ctx->foreach->cmd_index; ++ ++ return NGX_OK; ++} ++ +Index: 0.8/modules/nginx-echo/src/ngx_http_echo_foreach.h +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ 0.8/modules/nginx-echo/src/ngx_http_echo_foreach.h 2010-10-31 07:35:23.648338001 +0000 +@@ -0,0 +1,16 @@ ++#ifndef ECHO_FOREACH_H ++#define ECHO_FOREACH_H ++ ++#include "ngx_http_echo_module.h" ++ ++ngx_int_t ngx_http_echo_exec_echo_foreach_split(ngx_http_request_t *r, ++ ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args); ++ ++ngx_int_t ngx_http_echo_exec_echo_end(ngx_http_request_t *r, ++ ngx_http_echo_ctx_t *ctx); ++ ++ngx_int_t ngx_http_echo_it_variable(ngx_http_request_t *r, ++ ngx_http_variable_value_t *v, uintptr_t data); ++ ++#endif /* ECHO_FOREACH_H */ ++ +Index: 0.8/modules/nginx-echo/src/ngx_http_echo_handler.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ 0.8/modules/nginx-echo/src/ngx_http_echo_handler.c 2010-10-31 07:35:23.648338001 +0000 +@@ -0,0 +1,351 @@ ++#define DDEBUG 0 ++ ++#include "ddebug.h" ++ ++#include "ngx_http_echo_handler.h" ++#include "ngx_http_echo_echo.h" ++#include "ngx_http_echo_util.h" ++#include "ngx_http_echo_sleep.h" ++#include "ngx_http_echo_var.h" ++#include "ngx_http_echo_timer.h" ++#include "ngx_http_echo_location.h" ++#include "ngx_http_echo_subrequest.h" ++#include "ngx_http_echo_request_info.h" ++#include "ngx_http_echo_foreach.h" ++ ++#include ++#include ++ ++ngx_int_t ++ngx_http_echo_handler_init(ngx_conf_t *cf) ++{ ++ ngx_int_t rc; ++ ++ rc = ngx_http_echo_echo_init(cf); ++ if (rc != NGX_OK) { ++ return rc; ++ } ++ ++ return ngx_http_echo_add_variables(cf); ++} ++ ++ ++void ++ngx_http_echo_wev_handler(ngx_http_request_t *r) ++{ ++ ngx_int_t rc; ++ ngx_http_echo_ctx_t *ctx; ++ ++ dd_enter(); ++ ++ ctx = ngx_http_get_module_ctx(r, ngx_http_echo_module); ++ ++ if (ctx == NULL) { ++ ngx_http_finalize_request(r, NGX_ERROR); ++ return; ++ } ++ ++ if (ctx->waiting && ! ctx->done) { ++ if (r->main->posted_requests ++ && r->main->posted_requests->request != r) ++ { ++ dd("HOT SPIN"); ++ ++#if defined(nginx_version) && nginx_version >= 8012 ++ ngx_http_post_request(r, NULL); ++#else ++ ngx_http_post_request(r); ++#endif ++ ++ return; ++ } ++ } ++ ++ ctx->done = 0; ++ ++ ctx->next_handler_cmd++; ++ ++ rc = ngx_http_echo_run_cmds(r); ++ ++ dd("rc: %d", (int) rc); ++ ++ if (rc == NGX_DONE) { ++ return; ++ } ++ ++ if (rc == NGX_AGAIN) { ++ dd("mark busy %d", (int) ctx->next_handler_cmd); ++ ctx->waiting = 1; ++ ctx->done = 0; ++ ++ } else { ++ dd("mark ready %d", (int) ctx->next_handler_cmd); ++ ctx->waiting = 0; ++ ctx->done = 1; ++ ++ dd("finalizing with rc %d", (int) rc); ++ ++ dd("finalize request %.*s with %d", (int) r->uri.len, r->uri.data, (int) rc); ++ ++ ngx_http_finalize_request(r, rc); ++ } ++} ++ ++ ++ngx_int_t ++ngx_http_echo_handler(ngx_http_request_t *r) ++{ ++ ngx_int_t rc; ++ ngx_http_echo_ctx_t *ctx; ++ ++ rc = ngx_http_echo_run_cmds(r); ++ ++ if (rc == NGX_ERROR) { ++ return NGX_HTTP_INTERNAL_SERVER_ERROR; ++ } ++ ++ if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { ++ return rc; ++ } ++ ++ if (rc == NGX_DONE) { ++ return NGX_DONE; ++ } ++ ++ if (rc == NGX_AGAIN) { ++#if defined(nginx_version) && nginx_version >= 8011 ++ r->main->count++; ++#endif ++ ++ /* XXX we need this for 0.7.x and 0.8.x < 0.8.11 */ ++ dd("%d", r->connection->destroyed); ++ dd("%d", r->done); ++ ++ ctx = ngx_http_get_module_ctx(r, ngx_http_echo_module); ++ if (ctx) { ++ dd("mark busy %d", (int) ctx->next_handler_cmd); ++ ctx->waiting = 1; ++ ctx->done = 0; ++ } ++ ++ return NGX_DONE; ++ } ++ ++ return NGX_OK; ++} ++ ++ ++ngx_int_t ++ngx_http_echo_run_cmds(ngx_http_request_t *r) ++{ ++ ngx_http_echo_loc_conf_t *elcf; ++ ngx_http_echo_ctx_t *ctx; ++ ngx_int_t rc; ++ ngx_array_t *cmds; ++ ngx_array_t *computed_args = NULL; ++ ngx_http_echo_cmd_t *cmd; ++ ngx_http_echo_cmd_t *cmd_elts; ++ ngx_array_t *opts = NULL; ++ ++ ++ elcf = ngx_http_get_module_loc_conf(r, ngx_http_echo_module); ++ cmds = elcf->handler_cmds; ++ if (cmds == NULL) { ++ return NGX_DECLINED; ++ } ++ ++ ctx = ngx_http_get_module_ctx(r, ngx_http_echo_module); ++ if (ctx == NULL) { ++ rc = ngx_http_echo_init_ctx(r, &ctx); ++ if (rc != NGX_OK) { ++ return rc; ++ } ++ ++ ngx_http_set_ctx(r, ctx, ngx_http_echo_module); ++ } ++ ++ dd("exec handler: %.*s: %i", (int) r->uri.len, r->uri.data, ++ (int) ctx->next_handler_cmd); ++ ++ cmd_elts = cmds->elts; ++ ++ for (; ctx->next_handler_cmd < cmds->nelts; ctx->next_handler_cmd++) { ++ ++ cmd = &cmd_elts[ctx->next_handler_cmd]; ++ ++ /* evaluate arguments for the current cmd (if any) */ ++ if (cmd->args) { ++ computed_args = ngx_array_create(r->pool, cmd->args->nelts, ++ sizeof(ngx_str_t)); ++ ++ if (computed_args == NULL) { ++ return NGX_HTTP_INTERNAL_SERVER_ERROR; ++ } ++ ++ opts = ngx_array_create(r->pool, 1, sizeof(ngx_str_t)); ++ ++ if (opts == NULL) { ++ return NGX_HTTP_INTERNAL_SERVER_ERROR; ++ } ++ ++ rc = ngx_http_echo_eval_cmd_args(r, cmd, computed_args, opts); ++ if (rc != NGX_OK) { ++ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, ++ "Failed to evaluate arguments for " ++ "the directive."); ++ return rc; ++ } ++ } ++ ++ /* do command dispatch based on the opcode */ ++ switch (cmd->opcode) { ++ case echo_opcode_echo: ++ /* XXX moved the following code to a separate ++ * function */ ++ dd("found echo opcode"); ++ rc = ngx_http_echo_exec_echo(r, ctx, computed_args, ++ 0 /* in filter */, opts); ++ break; ++ ++ case echo_opcode_echo_request_body: ++ rc = ngx_http_echo_exec_echo_request_body(r, ctx); ++ break; ++ ++ case echo_opcode_echo_location_async: ++ dd("found opcode echo location async..."); ++ rc = ngx_http_echo_exec_echo_location_async(r, ctx, ++ computed_args); ++ break; ++ ++ case echo_opcode_echo_location: ++ return ngx_http_echo_exec_echo_location(r, ctx, computed_args); ++ break; ++ ++ case echo_opcode_echo_subrequest_async: ++ dd("found opcode echo subrequest async..."); ++ rc = ngx_http_echo_exec_echo_subrequest_async(r, ctx, ++ computed_args); ++ break; ++ ++ case echo_opcode_echo_subrequest: ++ return ngx_http_echo_exec_echo_subrequest(r, ctx, computed_args); ++ break; ++ ++ case echo_opcode_echo_sleep: ++ return ngx_http_echo_exec_echo_sleep(r, ctx, computed_args); ++ break; ++ ++ case echo_opcode_echo_flush: ++ rc = ngx_http_echo_exec_echo_flush(r, ctx); ++ break; ++ ++ case echo_opcode_echo_blocking_sleep: ++ rc = ngx_http_echo_exec_echo_blocking_sleep(r, ctx, ++ computed_args); ++ break; ++ ++ case echo_opcode_echo_reset_timer: ++ rc = ngx_http_echo_exec_echo_reset_timer(r, ctx); ++ break; ++ ++ case echo_opcode_echo_duplicate: ++ rc = ngx_http_echo_exec_echo_duplicate(r, ctx, computed_args); ++ break; ++ ++ case echo_opcode_echo_read_request_body: ++ ctx->wait_read_request_body = 0; ++ ++ rc = ngx_http_echo_exec_echo_read_request_body(r, ctx); ++ ++#if defined(nginx_version) && nginx_version >= 8011 ++ /* XXX read_client_request_body always increments the counter */ ++ r->main->count--; ++#endif ++ ++ dd("read request body: %d", (int) rc); ++ ++ if (rc == NGX_OK) { ++ continue; ++ } ++ ++ ctx->wait_read_request_body = 1; ++ ++ /* r->write_event_handler = ngx_http_request_empty_handler; */ ++ ++ return rc; ++ break; ++ ++ case echo_opcode_echo_foreach_split: ++ rc = ngx_http_echo_exec_echo_foreach_split(r, ctx, computed_args); ++ break; ++ ++ case echo_opcode_echo_end: ++ rc = ngx_http_echo_exec_echo_end(r, ctx); ++ break; ++ ++ case echo_opcode_echo_exec: ++ dd("echo_exec"); ++ return ngx_http_echo_exec_exec(r, ctx, computed_args); ++ break; ++ ++ default: ++ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, ++ "Unknown opcode: %d", cmd->opcode); ++ return NGX_HTTP_INTERNAL_SERVER_ERROR; ++ break; ++ } ++ ++ if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { ++ return rc; ++ } ++ } ++ ++ rc = ngx_http_echo_send_chain_link(r, ctx, NULL /* indicate LAST */); ++ ++ if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { ++ return rc; ++ } ++ ++ return NGX_OK; ++} ++ ++ ++ngx_int_t ++ngx_http_echo_post_subrequest(ngx_http_request_t *r, ++ void *data, ngx_int_t rc) ++{ ++ ngx_http_request_t *pr; ++ ngx_http_echo_ctx_t *pr_ctx; ++ ++ ++ dd_enter(); ++ ++ pr = r->parent; ++ ++ pr_ctx = ngx_http_get_module_ctx(pr, ngx_http_echo_module); ++ if (pr_ctx == NULL) { ++ return NGX_ERROR; ++ } ++ ++ dd("mark ready %d", (int) pr_ctx->next_handler_cmd); ++ ++ pr_ctx->waiting = 0; ++ pr_ctx->done = 1; ++ ++ pr->write_event_handler = ngx_http_echo_wev_handler; ++ ++ /* ensure that the parent request is (or will be) ++ * posted out the head of the r->posted_requests chain */ ++ ++ if (r->main->posted_requests ++ && r->main->posted_requests->request != pr) ++ { ++ rc = ngx_http_echo_post_request_at_head(pr, NULL); ++ if (rc != NGX_OK) { ++ return NGX_ERROR; ++ } ++ } ++ ++ return rc; ++} ++ +Index: 0.8/modules/nginx-echo/src/ngx_http_echo_handler.h +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ 0.8/modules/nginx-echo/src/ngx_http_echo_handler.h 2010-10-31 07:35:23.648338001 +0000 +@@ -0,0 +1,20 @@ ++#ifndef ECHO_HANDLER_H ++#define ECHO_HANDLER_H ++ ++#include "ngx_http_echo_module.h" ++ ++ ++void ngx_http_echo_wev_handler(ngx_http_request_t *r); ++ ++ngx_int_t ngx_http_echo_handler_init(ngx_conf_t *cf); ++ ++ngx_int_t ngx_http_echo_handler(ngx_http_request_t *r); ++ ++ngx_int_t ngx_http_echo_run_cmds(ngx_http_request_t *r); ++ ++ngx_int_t ngx_http_echo_post_subrequest(ngx_http_request_t *r, ++ void *data, ngx_int_t rc); ++ ++ ++#endif /* ECHO_HANDLER_H */ ++ +Index: 0.8/modules/nginx-echo/src/ngx_http_echo_location.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ 0.8/modules/nginx-echo/src/ngx_http_echo_location.c 2010-10-31 07:35:23.648338001 +0000 +@@ -0,0 +1,183 @@ ++#define DDEBUG 0 ++#include "ddebug.h" ++ ++#include "ngx_http_echo_util.h" ++#include "ngx_http_echo_location.h" ++#include "ngx_http_echo_handler.h" ++ ++#include ++ ++ ++static ngx_int_t ngx_http_echo_adjust_subrequest(ngx_http_request_t *sr); ++ ++ ++ngx_int_t ++ngx_http_echo_exec_echo_location_async(ngx_http_request_t *r, ++ ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args) ++{ ++ ngx_int_t rc; ++ ngx_http_request_t *sr; /* subrequest object */ ++ ngx_str_t *computed_arg_elts; ++ ngx_str_t location; ++ ngx_str_t *url_args; ++ ngx_str_t args; ++ ngx_uint_t flags = 0; ++ ++ ++ dd_enter(); ++ ++ computed_arg_elts = computed_args->elts; ++ ++ location = computed_arg_elts[0]; ++ ++ if (location.len == 0) { ++ return NGX_HTTP_INTERNAL_SERVER_ERROR; ++ } ++ ++ if (computed_args->nelts > 1) { ++ url_args = &computed_arg_elts[1]; ++ } else { ++ url_args = NULL; ++ } ++ ++ dd("location: %s", location.data); ++ dd("location args: %s", (char*) (url_args ? url_args->data : (u_char*)"NULL")); ++ ++ args.data = NULL; ++ args.len = 0; ++ ++ if (ngx_http_parse_unsafe_uri(r, &location, &args, &flags) != NGX_OK) { ++ ctx->headers_sent = 1; ++ return NGX_HTTP_INTERNAL_SERVER_ERROR; ++ } ++ ++ if (args.len > 0 && url_args == NULL) { ++ url_args = &args; ++ } ++ ++ rc = ngx_http_echo_send_header_if_needed(r, ctx); ++ if (r->header_only) { ++ return NGX_HTTP_INTERNAL_SERVER_ERROR; ++ } ++ ++ if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { ++ return rc; ++ } ++ ++ rc = ngx_http_subrequest(r, &location, url_args, &sr, NULL, 0); ++ ++ if (rc != NGX_OK) { ++ return NGX_ERROR; ++ } ++ ++ rc = ngx_http_echo_adjust_subrequest(sr); ++ if (rc != NGX_OK) { ++ return NGX_ERROR; ++ } ++ ++ return NGX_OK; ++} ++ ++ ++ngx_int_t ++ngx_http_echo_exec_echo_location(ngx_http_request_t *r, ++ ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args) ++{ ++ ngx_int_t rc; ++ ngx_http_request_t *sr; /* subrequest object */ ++ ngx_str_t *computed_arg_elts; ++ ngx_str_t location; ++ ngx_str_t *url_args; ++ ngx_http_post_subrequest_t *psr; ++ ngx_str_t args; ++ ngx_uint_t flags = 0; ++ ++ ++ dd_enter(); ++ ++ computed_arg_elts = computed_args->elts; ++ ++ location = computed_arg_elts[0]; ++ ++ if (location.len == 0) { ++ return NGX_HTTP_INTERNAL_SERVER_ERROR; ++ } ++ ++ if (computed_args->nelts > 1) { ++ url_args = &computed_arg_elts[1]; ++ } else { ++ url_args = NULL; ++ } ++ ++ args.data = NULL; ++ args.len = 0; ++ ++ if (ngx_http_parse_unsafe_uri(r, &location, &args, &flags) != NGX_OK) { ++ ctx->headers_sent = 1; ++ return NGX_HTTP_INTERNAL_SERVER_ERROR; ++ } ++ ++ if (args.len > 0 && url_args == NULL) { ++ url_args = &args; ++ } ++ ++ rc = ngx_http_echo_send_header_if_needed(r, ctx); ++ ++ if (r->header_only || rc >= NGX_HTTP_SPECIAL_RESPONSE) { ++ return rc; ++ } ++ ++ psr = ngx_palloc(r->pool, sizeof(ngx_http_post_subrequest_t)); ++ ++ if (psr == NULL) { ++ return NGX_HTTP_INTERNAL_SERVER_ERROR; ++ } ++ ++ psr->handler = ngx_http_echo_post_subrequest; ++ psr->data = ctx; ++ ++ rc = ngx_http_subrequest(r, &location, url_args, &sr, psr, 0); ++ ++ if (rc != NGX_OK) { ++ return NGX_ERROR; ++ } ++ ++ rc = ngx_http_echo_adjust_subrequest(sr); ++ ++ if (rc != NGX_OK) { ++ return NGX_ERROR; ++ } ++ ++ return NGX_AGAIN; ++} ++ ++ ++static ngx_int_t ++ngx_http_echo_adjust_subrequest(ngx_http_request_t *sr) ++{ ++ ngx_http_core_main_conf_t *cmcf; ++ ngx_http_request_t *r; ++ ++ ++ /* we do not inherit the parent request's variables */ ++ cmcf = ngx_http_get_module_main_conf(sr, ngx_http_core_module); ++ ++ r = sr->parent; ++ ++ sr->header_in = r->header_in; ++ ++ /* XXX work-around a bug in ngx_http_subrequest */ ++ if (r->headers_in.headers.last == &r->headers_in.headers.part) { ++ sr->headers_in.headers.last = &sr->headers_in.headers.part; ++ } ++ ++ sr->variables = ngx_pcalloc(sr->pool, cmcf->variables.nelts ++ * sizeof(ngx_http_variable_value_t)); ++ ++ if (sr->variables == NULL) { ++ return NGX_HTTP_INTERNAL_SERVER_ERROR; ++ } ++ ++ return NGX_OK; ++} ++ +Index: 0.8/modules/nginx-echo/src/ngx_http_echo_location.h +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ 0.8/modules/nginx-echo/src/ngx_http_echo_location.h 2010-10-31 07:35:23.648338001 +0000 +@@ -0,0 +1,13 @@ ++#ifndef ECHO_LOCATION_H ++#define ECHO_LOCATION_H ++ ++#include "ngx_http_echo_module.h" ++ ++ngx_int_t ngx_http_echo_exec_echo_location_async(ngx_http_request_t *r, ++ ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args); ++ ++ngx_int_t ngx_http_echo_exec_echo_location(ngx_http_request_t *r, ++ ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args); ++ ++#endif /* ECHO_LOCATION_H */ ++ +Index: 0.8/modules/nginx-echo/src/ngx_http_echo_module.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ 0.8/modules/nginx-echo/src/ngx_http_echo_module.c 2010-10-31 07:35:23.648338001 +0000 +@@ -0,0 +1,587 @@ ++#define DDEBUG 0 ++#include "ddebug.h" ++ ++#include "ngx_http_echo_handler.h" ++#include "ngx_http_echo_filter.h" ++#include "ngx_http_echo_request_info.h" ++ ++#include ++#include ++#include ++ ++/* config init handler */ ++static void * ngx_http_echo_create_conf(ngx_conf_t *cf); ++ ++/* config directive handlers */ ++static char * ngx_http_echo_echo(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); ++ ++static char * ngx_http_echo_echo_request_body(ngx_conf_t *cf, ++ ngx_command_t *cmd, void *conf); ++ ++static char * ngx_http_echo_echo_sleep(ngx_conf_t *cf, ngx_command_t *cmd, ++ void *conf); ++ ++static char * ngx_http_echo_echo_flush(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); ++ ++static char * ngx_http_echo_echo_blocking_sleep(ngx_conf_t *cf, ++ ngx_command_t *cmd, void *conf); ++ ++static char * ngx_http_echo_echo_reset_timer(ngx_conf_t *cf, ++ ngx_command_t *cmd, void *conf); ++ ++static char * ngx_http_echo_echo_before_body(ngx_conf_t *cf, ++ ngx_command_t *cmd, void *conf); ++ ++static char * ngx_http_echo_echo_after_body(ngx_conf_t *cf, ++ ngx_command_t *cmd, void *conf); ++ ++static char * ngx_http_echo_echo_location_async(ngx_conf_t *cf, ++ ngx_command_t *cmd, void *conf); ++ ++static char * ngx_http_echo_echo_location(ngx_conf_t *cf, ++ ngx_command_t *cmd, void *conf); ++ ++static char * ngx_http_echo_echo_subrequest_async(ngx_conf_t *cf, ++ ngx_command_t *cmd, void *conf); ++ ++static char * ngx_http_echo_echo_subrequest(ngx_conf_t *cf, ++ ngx_command_t *cmd, void *conf); ++ ++static char * ngx_http_echo_echo_duplicate(ngx_conf_t *cf, ++ ngx_command_t *cmd, void *conf); ++ ++static char * ngx_http_echo_echo_read_request_body(ngx_conf_t *cf, ++ ngx_command_t *cmd, void *conf); ++ ++static char * ngx_http_echo_echo_foreach_split(ngx_conf_t *cf, ++ ngx_command_t *cmd, void *conf); ++ ++static char * ngx_http_echo_echo_end(ngx_conf_t *cf, ++ ngx_command_t *cmd, void *conf); ++ ++static char * ngx_http_echo_echo_abort_parent(ngx_conf_t *cf, ngx_command_t *cmd, ++ void *conf); ++ ++static char * ngx_http_echo_echo_exec(ngx_conf_t *cf, ++ ngx_command_t *cmd, void *conf); ++ ++static char * ngx_http_echo_helper(ngx_http_echo_opcode_t opcode, ++ ngx_http_echo_cmd_category_t cat, ++ ngx_conf_t *cf, ngx_command_t *cmd, void* conf); ++ ++ ++static ngx_http_module_t ngx_http_echo_module_ctx = { ++ /* TODO we could add our own variables here... */ ++ ngx_http_echo_handler_init, /* preconfiguration */ ++ ngx_http_echo_filter_init, /* postconfiguration */ ++ ++ NULL, /* create main configuration */ ++ NULL, /* init main configuration */ ++ ++ NULL, /* create server configuration */ ++ NULL, /* merge server configuration */ ++ ++ ngx_http_echo_create_conf, /* create location configuration */ ++ NULL /* merge location configuration */ ++}; ++ ++ ++static ngx_command_t ngx_http_echo_commands[] = { ++ ++ { ngx_string("echo"), ++ NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_ANY, ++ ngx_http_echo_echo, ++ NGX_HTTP_LOC_CONF_OFFSET, ++ offsetof(ngx_http_echo_loc_conf_t, handler_cmds), ++ NULL }, ++ ++ { ngx_string("echo_request_body"), ++ NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_NOARGS, ++ ngx_http_echo_echo_request_body, ++ NGX_HTTP_LOC_CONF_OFFSET, ++ offsetof(ngx_http_echo_loc_conf_t, handler_cmds), ++ NULL }, ++ ++ { ngx_string("echo_sleep"), ++ NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1, ++ ngx_http_echo_echo_sleep, ++ NGX_HTTP_LOC_CONF_OFFSET, ++ offsetof(ngx_http_echo_loc_conf_t, handler_cmds), ++ NULL }, ++ ++ { ngx_string("echo_flush"), ++ NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_NOARGS, ++ ngx_http_echo_echo_flush, ++ NGX_HTTP_LOC_CONF_OFFSET, ++ offsetof(ngx_http_echo_loc_conf_t, handler_cmds), ++ NULL }, ++ ++ { ngx_string("echo_blocking_sleep"), ++ NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1, ++ ngx_http_echo_echo_blocking_sleep, ++ NGX_HTTP_LOC_CONF_OFFSET, ++ offsetof(ngx_http_echo_loc_conf_t, handler_cmds), ++ NULL }, ++ ++ { ngx_string("echo_reset_timer"), ++ NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_NOARGS, ++ ngx_http_echo_echo_reset_timer, ++ NGX_HTTP_LOC_CONF_OFFSET, ++ offsetof(ngx_http_echo_loc_conf_t, handler_cmds), ++ NULL }, ++ ++ { ngx_string("echo_before_body"), ++ NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_ANY, ++ ngx_http_echo_echo_before_body, ++ NGX_HTTP_LOC_CONF_OFFSET, ++ offsetof(ngx_http_echo_loc_conf_t, before_body_cmds), ++ NULL }, ++ ++ { ngx_string("echo_after_body"), ++ NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_ANY, ++ ngx_http_echo_echo_after_body, ++ NGX_HTTP_LOC_CONF_OFFSET, ++ offsetof(ngx_http_echo_loc_conf_t, after_body_cmds), ++ NULL }, ++ ++ { ngx_string("echo_location_async"), ++ NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE12, ++ ngx_http_echo_echo_location_async, ++ NGX_HTTP_LOC_CONF_OFFSET, ++ 0, ++ NULL }, ++ ++ { ngx_string("echo_location"), ++ NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE12, ++ ngx_http_echo_echo_location, ++ NGX_HTTP_LOC_CONF_OFFSET, ++ 0, ++ NULL }, ++ ++ { ngx_string("echo_subrequest_async"), ++ NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_2MORE, ++ ngx_http_echo_echo_subrequest_async, ++ NGX_HTTP_LOC_CONF_OFFSET, ++ 0, ++ NULL }, ++ ++ { ngx_string("echo_subrequest"), ++ NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_2MORE, ++ ngx_http_echo_echo_subrequest, ++ NGX_HTTP_LOC_CONF_OFFSET, ++ 0, ++ NULL }, ++ ++ { ngx_string("echo_duplicate"), ++ NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_2MORE, ++ ngx_http_echo_echo_duplicate, ++ NGX_HTTP_LOC_CONF_OFFSET, ++ 0, ++ NULL }, ++ ++ { ngx_string("echo_read_request_body"), ++ NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_NOARGS, ++ ngx_http_echo_echo_read_request_body, ++ NGX_HTTP_LOC_CONF_OFFSET, ++ offsetof(ngx_http_echo_loc_conf_t, handler_cmds), ++ NULL }, ++ ++ { ngx_string("echo_foreach_split"), ++ NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_2MORE, ++ ngx_http_echo_echo_foreach_split, ++ NGX_HTTP_LOC_CONF_OFFSET, ++ offsetof(ngx_http_echo_loc_conf_t, handler_cmds), ++ NULL }, ++ ++ { ngx_string("echo_end"), ++ NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_NOARGS, ++ ngx_http_echo_echo_end, ++ NGX_HTTP_LOC_CONF_OFFSET, ++ offsetof(ngx_http_echo_loc_conf_t, handler_cmds), ++ NULL }, ++ ++ { ngx_string("echo_abort_parent"), ++ NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_NOARGS, ++ ngx_http_echo_echo_abort_parent, ++ NGX_HTTP_LOC_CONF_OFFSET, ++ offsetof(ngx_http_echo_loc_conf_t, handler_cmds), ++ NULL }, ++ ++ { ngx_string("echo_exec"), ++ NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE12, ++ ngx_http_echo_echo_exec, ++ NGX_HTTP_LOC_CONF_OFFSET, ++ offsetof(ngx_http_echo_loc_conf_t, handler_cmds), ++ NULL }, ++ ++ ngx_null_command ++}; ++ ++ ++ngx_module_t ngx_http_echo_module = { ++ NGX_MODULE_V1, ++ &ngx_http_echo_module_ctx, /* module context */ ++ ngx_http_echo_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 void * ++ngx_http_echo_create_conf(ngx_conf_t *cf) ++{ ++ ngx_http_echo_loc_conf_t *conf; ++ conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_echo_loc_conf_t)); ++ if (conf == NULL) { ++ return NGX_CONF_ERROR; ++ } ++ ++ return conf; ++} ++ ++ ++static char * ++ngx_http_echo_helper(ngx_http_echo_opcode_t opcode, ++ ngx_http_echo_cmd_category_t cat, ++ ngx_conf_t *cf, ngx_command_t *cmd, void* conf) ++{ ++ ngx_http_core_loc_conf_t *clcf; ++ /* ngx_http_echo_loc_conf_t *ulcf = conf; */ ++ ngx_array_t **args_ptr; ++ ngx_http_script_compile_t sc; ++ ngx_str_t *raw_args; ++ ngx_http_echo_arg_template_t *arg; ++ ngx_array_t **cmds_ptr; ++ ngx_http_echo_cmd_t *echo_cmd; ++ ngx_uint_t i, n; ++ ++ /* cmds_ptr points to ngx_http_echo_loc_conf_t's ++ * handler_cmds, before_body_cmds, or after_body_cmds ++ * array, depending on the actual offset */ ++ cmds_ptr = (ngx_array_t**)(((u_char*)conf) + cmd->offset); ++ ++ if (*cmds_ptr == NULL) { ++ *cmds_ptr = ngx_array_create(cf->pool, 1, ++ sizeof(ngx_http_echo_cmd_t)); ++ ++ if (*cmds_ptr == NULL) { ++ return NGX_CONF_ERROR; ++ } ++ ++ if (cat == echo_handler_cmd) { ++ dd("registering the content handler"); ++ /* register the content handler */ ++ clcf = ngx_http_conf_get_module_loc_conf(cf, ++ ngx_http_core_module); ++ ++ dd("registering the content handler (2)"); ++ clcf->handler = ngx_http_echo_handler; ++ ++ } else { ++ dd("filter used = 1"); ++ ngx_http_echo_filter_used = 1; ++ } ++ } ++ ++ echo_cmd = ngx_array_push(*cmds_ptr); ++ ++ if (echo_cmd == NULL) { ++ return NGX_CONF_ERROR; ++ } ++ ++ echo_cmd->opcode = opcode; ++ ++ args_ptr = &echo_cmd->args; ++ *args_ptr = ngx_array_create(cf->pool, 1, ++ sizeof(ngx_http_echo_arg_template_t)); ++ ++ if (*args_ptr == NULL) { ++ return NGX_CONF_ERROR; ++ } ++ ++ raw_args = cf->args->elts; ++ ++ /* we skip the first arg and start from the second */ ++ ++ for (i = 1 ; i < cf->args->nelts; i++) { ++ arg = ngx_array_push(*args_ptr); ++ ++ if (arg == NULL) { ++ return NGX_CONF_ERROR; ++ } ++ ++ arg->raw_value = raw_args[i]; ++ ++ dd("found raw arg %s", raw_args[i].data); ++ ++ arg->lengths = NULL; ++ arg->values = NULL; ++ ++ n = ngx_http_script_variables_count(&arg->raw_value); ++ ++ if (n > 0) { ++ ngx_memzero(&sc, sizeof(ngx_http_script_compile_t)); ++ ++ sc.cf = cf; ++ sc.source = &arg->raw_value; ++ sc.lengths = &arg->lengths; ++ sc.values = &arg->values; ++ sc.variables = n; ++ sc.complete_lengths = 1; ++ sc.complete_values = 1; ++ ++ if (ngx_http_script_compile(&sc) != NGX_OK) { ++ return NGX_CONF_ERROR; ++ } ++ } ++ } /* end for */ ++ ++ return NGX_CONF_OK; ++} ++ ++ ++static char * ++ngx_http_echo_echo(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) ++{ ++ dd("in echo_echo..."); ++ return ngx_http_echo_helper(echo_opcode_echo, ++ echo_handler_cmd, ++ cf, cmd, conf); ++} ++ ++ ++static char * ++ngx_http_echo_echo_request_body(ngx_conf_t *cf, ++ ngx_command_t *cmd, void *conf) ++{ ++ dd("in echo_echo_request_body..."); ++ return ngx_http_echo_helper(echo_opcode_echo_request_body, ++ echo_handler_cmd, ++ cf, cmd, conf); ++} ++ ++ ++static char * ++ngx_http_echo_echo_sleep(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) ++{ ++ dd("in echo_sleep..."); ++ return ngx_http_echo_helper(echo_opcode_echo_sleep, ++ echo_handler_cmd, ++ cf, cmd, conf); ++} ++ ++ ++static char * ++ngx_http_echo_echo_flush(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) ++{ ++ dd("in echo_flush..."); ++ return ngx_http_echo_helper(echo_opcode_echo_flush, ++ echo_handler_cmd, ++ cf, cmd, conf); ++} ++ ++ ++static char * ++ngx_http_echo_echo_blocking_sleep(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) ++{ ++ dd("in echo_blocking_sleep..."); ++ return ngx_http_echo_helper(echo_opcode_echo_blocking_sleep, ++ echo_handler_cmd, ++ cf, cmd, conf); ++} ++ ++ ++static char * ++ngx_http_echo_echo_reset_timer(ngx_conf_t *cf, ++ ngx_command_t *cmd, void *conf) ++{ ++ return ngx_http_echo_helper(echo_opcode_echo_reset_timer, ++ echo_handler_cmd, ++ cf, cmd, conf); ++} ++ ++ ++static char * ++ngx_http_echo_echo_before_body(ngx_conf_t *cf, ++ ngx_command_t *cmd, void *conf) ++{ ++ dd("processing echo_before_body directive..."); ++ return ngx_http_echo_helper(echo_opcode_echo_before_body, ++ echo_filter_cmd, ++ cf, cmd, conf); ++} ++ ++ ++static char * ++ngx_http_echo_echo_after_body(ngx_conf_t *cf, ++ ngx_command_t *cmd, void *conf) ++{ ++ return ngx_http_echo_helper(echo_opcode_echo_after_body, ++ echo_filter_cmd, ++ cf, cmd, conf); ++} ++ ++ ++static char * ++ ngx_http_echo_echo_location_async(ngx_conf_t *cf, ngx_command_t *cmd, ++ void *conf) ++{ ++ ++#if ! defined(nginx_version) ++ ++ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, ++ "Directive echo_location_async does not work with nginx " ++ "versions ealier than 0.7.46."); ++ ++ return NGX_CONF_ERROR; ++ ++#else ++ ++ return ngx_http_echo_helper(echo_opcode_echo_location_async, ++ echo_handler_cmd, ++ cf, cmd, conf); ++ ++#endif ++ ++} ++ ++ ++static char * ++ ngx_http_echo_echo_location(ngx_conf_t *cf, ngx_command_t *cmd, ++ void *conf) ++{ ++ ++#if ! defined(nginx_version) ++ ++ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, ++ "Directive echo_location does not work with nginx " ++ "versions ealier than 0.7.46."); ++ ++ return NGX_CONF_ERROR; ++ ++#else ++ ++ return ngx_http_echo_helper(echo_opcode_echo_location, ++ echo_handler_cmd, ++ cf, cmd, conf); ++ ++#endif ++ ++} ++ ++ ++static char * ++ ngx_http_echo_echo_subrequest_async(ngx_conf_t *cf, ngx_command_t *cmd, ++ void *conf) ++{ ++ ++#if ! defined(nginx_version) ++ ++ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, ++ "Directive echo_subrequest_async does not work with nginx " ++ "versions ealier than 0.7.46."); ++ ++ return NGX_CONF_ERROR; ++ ++#else ++ ++ return ngx_http_echo_helper(echo_opcode_echo_subrequest_async, ++ echo_handler_cmd, ++ cf, cmd, conf); ++ ++#endif ++ ++} ++ ++ ++static char * ++ ngx_http_echo_echo_subrequest(ngx_conf_t *cf, ngx_command_t *cmd, ++ void *conf) ++{ ++ ++#if ! defined(nginx_version) ++ ++ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, ++ "Directive echo_subrequest does not work with nginx " ++ "versions ealier than 0.7.46."); ++ ++ return NGX_CONF_ERROR; ++ ++#else ++ ++ return ngx_http_echo_helper(echo_opcode_echo_subrequest, ++ echo_handler_cmd, ++ cf, cmd, conf); ++ ++#endif ++ ++} ++ ++ ++static char * ++ngx_http_echo_echo_duplicate(ngx_conf_t *cf, ++ ngx_command_t *cmd, void *conf) ++{ ++ return ngx_http_echo_helper(echo_opcode_echo_duplicate, ++ echo_handler_cmd, ++ cf, cmd, conf); ++} ++ ++ ++static char * ++ngx_http_echo_echo_read_request_body(ngx_conf_t *cf, ++ ngx_command_t *cmd, void *conf) ++{ ++ return ngx_http_echo_helper( ++ echo_opcode_echo_read_request_body, ++ echo_handler_cmd, ++ cf, cmd, conf); ++} ++ ++ ++static char * ++ngx_http_echo_echo_foreach_split(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) ++{ ++ return ngx_http_echo_helper( ++ echo_opcode_echo_foreach_split, ++ echo_handler_cmd, ++ cf, cmd, conf); ++} ++ ++ ++static char * ++ngx_http_echo_echo_end(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) ++{ ++ return ngx_http_echo_helper( ++ echo_opcode_echo_end, ++ echo_handler_cmd, ++ cf, cmd, conf); ++} ++ ++ ++static char * ++ngx_http_echo_echo_abort_parent(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) ++{ ++ return ngx_http_echo_helper( ++ echo_opcode_echo_abort_parent, ++ echo_handler_cmd, ++ cf, cmd, conf); ++} ++ ++ ++static char * ++ngx_http_echo_echo_exec(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) ++{ ++ return ngx_http_echo_helper( ++ echo_opcode_echo_exec, ++ echo_handler_cmd, ++ cf, cmd, conf); ++} ++ +Index: 0.8/modules/nginx-echo/src/ngx_http_echo_module.h +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ 0.8/modules/nginx-echo/src/ngx_http_echo_module.h 2010-10-31 07:35:23.648338001 +0000 +@@ -0,0 +1,118 @@ ++/* Copyright (C) by agentzh */ ++ ++#ifndef NGX_HTTP_ECHO_MODULE_H ++#define NGX_HTTP_ECHO_MODULE_H ++ ++#include ++#include ++#include ++ ++extern ngx_module_t ngx_http_echo_module; ++ ++/* config directive's opcode */ ++typedef enum { ++ echo_opcode_echo, ++ echo_opcode_echo_request_body, ++ echo_opcode_echo_sleep, ++ echo_opcode_echo_flush, ++ echo_opcode_echo_blocking_sleep, ++ echo_opcode_echo_reset_timer, ++ echo_opcode_echo_before_body, ++ echo_opcode_echo_after_body, ++ echo_opcode_echo_location_async, ++ echo_opcode_echo_location, ++ echo_opcode_echo_subrequest_async, ++ echo_opcode_echo_subrequest, ++ echo_opcode_echo_duplicate, ++ echo_opcode_echo_read_request_body, ++ echo_opcode_echo_foreach_split, ++ echo_opcode_echo_end, ++ echo_opcode_echo_abort_parent, ++ echo_opcode_echo_exec ++} ngx_http_echo_opcode_t; ++ ++/* all the various config directives (or commands) are ++ * divided into two categories: "handler commands", ++ * and "filter commands". For instance, the "echo" ++ * directive is a handler command while ++ * "echo_before_body" is a filter one. */ ++typedef enum { ++ echo_handler_cmd, ++ echo_filter_cmd ++ ++} ngx_http_echo_cmd_category_t; ++ ++/* compiled form of a config directive argument's value */ ++typedef struct { ++ /* holds the raw string of the argument value */ ++ ngx_str_t raw_value; ++ ++ /* fields "lengths" and "values" are set by ++ * the function ngx_http_script_compile, ++ * iff the argument value indeed contains ++ * nginx variables like "$foo" */ ++ ngx_array_t *lengths; ++ ngx_array_t *values; ++ ++} ngx_http_echo_arg_template_t; ++ ++/* represent a config directive (or command) like "echo". */ ++typedef struct { ++ ngx_http_echo_opcode_t opcode; ++ ++ /* each argument is of type echo_arg_template_t: */ ++ ngx_array_t *args; ++} ngx_http_echo_cmd_t; ++ ++/* location config struct */ ++typedef struct { ++ /* elements of the following arrays are of type ++ * ngx_http_echo_cmd_t */ ++ ngx_array_t *handler_cmds; ++ ngx_array_t *before_body_cmds; ++ ngx_array_t *after_body_cmds; ++ ++} ngx_http_echo_loc_conf_t; ++ ++typedef struct { ++ ngx_array_t *choices; /* items after splitting */ ++ ngx_uint_t next_choice; /* current item index */ ++ ngx_uint_t cmd_index; /* cmd index for the echo_foreach direcitve */ ++} ngx_http_echo_foreach_ctx_t; ++ ++/* context struct in the request handling cycle, holding ++ * the current states of the command evaluator */ ++typedef struct { ++ /* index of the next handler command in ++ * ngx_http_echo_loc_conf_t's "handler_cmds" array. */ ++ ngx_uint_t next_handler_cmd; ++ ++ /* index of the next before-body filter command in ++ * ngx_http_echo_loc_conf_t's "before_body_cmds" array. */ ++ ngx_uint_t next_before_body_cmd; ++ ++ /* index of the next after-body filter command in ++ * ngx_http_echo_loc_conf_t's "after_body_cmds" array. */ ++ ngx_uint_t next_after_body_cmd; ++ ++ ngx_http_echo_foreach_ctx_t *foreach; ++ ++ ngx_flag_t headers_sent; ++ ngx_flag_t before_body_sent; ++ ngx_flag_t skip_filter; ++ ++ ngx_time_t timer_begin; ++ ++ ngx_event_t sleep; ++ ++ ngx_uint_t counter; ++ ++ ngx_flag_t wait_read_request_body; ++ ++ ngx_flag_t waiting; ++ ngx_flag_t done; ++} ngx_http_echo_ctx_t; ++ ++ ++#endif /* NGX_HTTP_ECHO_MODULE_H */ ++ +Index: 0.8/modules/nginx-echo/src/ngx_http_echo_request_info.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ 0.8/modules/nginx-echo/src/ngx_http_echo_request_info.c 2010-10-31 07:35:23.648338001 +0000 +@@ -0,0 +1,269 @@ ++#define DDEBUG 0 ++#include "ddebug.h" ++ ++#include "ngx_http_echo_request_info.h" ++#include "ngx_http_echo_util.h" ++#include "ngx_http_echo_handler.h" ++ ++#include ++ ++ ++static void ngx_http_echo_post_read_request_body(ngx_http_request_t *r); ++ ++ngx_int_t ++ngx_http_echo_exec_echo_read_request_body( ++ ngx_http_request_t* r, ngx_http_echo_ctx_t *ctx) ++{ ++ return ngx_http_read_client_request_body(r, ngx_http_echo_post_read_request_body); ++} ++ ++ ++static void ++ngx_http_echo_post_read_request_body(ngx_http_request_t *r) ++{ ++ ngx_http_echo_ctx_t *ctx; ++ ++ ctx = ngx_http_get_module_ctx(r, ngx_http_echo_module); ++ ++ dd("wait read request body %d", (int) ctx->wait_read_request_body); ++ ++ if (ctx->wait_read_request_body) { ++ ctx->waiting = 0; ++ ctx->done = 1; ++ ++ r->write_event_handler = ngx_http_echo_wev_handler; ++ ++ ngx_http_echo_wev_handler(r); ++ } ++} ++ ++ ++/* this function's implementation is borrowed from nginx 0.8.20 ++ * and modified a bit to work with subrequests. ++ * Copyrighted (C) by Igor Sysoev */ ++ngx_int_t ++ngx_http_echo_request_method_variable(ngx_http_request_t *r, ++ ngx_http_variable_value_t *v, uintptr_t data) ++{ ++ if (r->method_name.data) { ++ v->len = r->method_name.len; ++ v->valid = 1; ++ v->no_cacheable = 0; ++ v->not_found = 0; ++ v->data = r->method_name.data; ++ } else { ++ v->not_found = 1; ++ } ++ ++ return NGX_OK; ++} ++ ++ ++/* this function's implementation is borrowed from nginx 0.8.20 ++ * and modified a bit to work with subrequests. ++ * Copyrighted (C) by Igor Sysoev */ ++ngx_int_t ++ngx_http_echo_client_request_method_variable(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; ++} ++ ++ ++/* this function's implementation is borrowed from nginx 0.8.20 ++ * and modified a bit to work with subrequests. ++ * Copyrighted (C) by Igor Sysoev */ ++ngx_int_t ++ngx_http_echo_request_body_variable(ngx_http_request_t *r, ++ ngx_http_variable_value_t *v, uintptr_t data) ++{ ++ u_char *p; ++ size_t len; ++ ngx_buf_t *buf, *next; ++ ngx_chain_t *cl; ++ ++#if 0 ++ ++ dd("rrr request_body null ? %d", r->request_body == NULL); ++ if (r->request_body) { ++ dd("rrr request_body bufs null ? %d", r->request_body->bufs == NULL); ++ dd("rrr request_body temp file ? %d", r->request_body->temp_file != NULL); ++ } ++ dd("rrr request_body content length ? %ld", (long) r->headers_in.content_length_n); ++ ++#endif ++ ++ 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; ++ } ++ ++ next = cl->next->buf; ++ len = (buf->last - buf->pos) + (next->last - next->pos); ++ ++ p = ngx_pnalloc(r->pool, len); ++ if (p == NULL) { ++ return NGX_ERROR; ++ } ++ ++ v->data = p; ++ ++ p = ngx_cpymem(p, buf->pos, buf->last - buf->pos); ++ ngx_memcpy(p, next->pos, next->last - next->pos); ++ ++ v->len = len; ++ v->valid = 1; ++ v->no_cacheable = 0; ++ v->not_found = 0; ++ ++ return NGX_OK; ++} ++ ++ ++ngx_int_t ++ngx_http_echo_client_request_headers_variable(ngx_http_request_t *r, ++ ngx_http_variable_value_t *v, uintptr_t data) ++{ ++ size_t size; ++ u_char *p, *last; ++ ngx_buf_t *header_in; ++ ngx_flag_t just_seen_crlf; ++ ++ if (r != r->main) { ++ header_in = r->main->header_in; ++ } else { ++ header_in = r->header_in; ++ } ++ ++ if (header_in == NULL) { ++ v->not_found = 1; ++ return NGX_OK; ++ } ++ ++ size = header_in->pos - header_in->start; ++ ++ v->data = ngx_palloc(r->pool, size); ++ last = ngx_cpymem(v->data, header_in->start, size); ++ ++ /* fix \0 introduced by the nginx header parser and ++ * locate the end of the header */ ++ just_seen_crlf = 0; ++ for (p = (u_char*)v->data; p != last; p++) { ++ if (*p == '\0') { ++ if (p + 1 != last && *(p + 1) == LF) { ++ just_seen_crlf = 1; ++ *p = CR; ++ } else { ++ *p = ':'; ++ just_seen_crlf = 0; ++ } ++ } else if (*p == CR) { ++ if (just_seen_crlf) { ++ *p = '\0'; ++ last = p; ++ break; ++ } ++ } else if (*p != LF) { ++ just_seen_crlf = 0; ++ } ++ } ++ ++ v->len = last - v->data; ++ v->valid = 1; ++ v->no_cacheable = 0; ++ v->not_found = 0; ++ ++ return NGX_OK; ++} ++ ++ ++ngx_int_t ++ngx_http_echo_cacheable_request_uri_variable(ngx_http_request_t *r, ++ ngx_http_variable_value_t *v, uintptr_t data) ++{ ++ if (r->uri.len) { ++ v->len = r->uri.len; ++ v->valid = 1; ++ v->no_cacheable = 0; ++ v->not_found = 0; ++ v->data = r->uri.data; ++ } else { ++ v->not_found = 1; ++ } ++ ++ return NGX_OK; ++} ++ ++ ++ngx_int_t ++ngx_http_echo_request_uri_variable(ngx_http_request_t *r, ++ ngx_http_variable_value_t *v, uintptr_t data) ++{ ++ if (r->uri.len) { ++ v->len = r->uri.len; ++ v->valid = 1; ++ v->no_cacheable = 1; ++ v->not_found = 0; ++ v->data = r->uri.data; ++ } else { ++ v->not_found = 1; ++ } ++ ++ return NGX_OK; ++} ++ ++ ++ngx_int_t ++ngx_http_echo_response_status_variable(ngx_http_request_t *r, ++ ngx_http_variable_value_t *v, uintptr_t data) ++{ ++ u_char *p; ++ ++ if (r->headers_out.status) { ++ dd("headers out status: %d", (int) r->headers_out.status); ++ ++ p = ngx_palloc(r->pool, NGX_INT_T_LEN); ++ if (p == NULL) { ++ return NGX_ERROR; ++ } ++ ++ v->len = ngx_sprintf(p, "%ui", r->headers_out.status) - p; ++ v->data = p; ++ ++ v->valid = 1; ++ v->no_cacheable = 1; ++ v->not_found = 0; ++ } else { ++ v->not_found = 1; ++ } ++ ++ return NGX_OK; ++} ++ +Index: 0.8/modules/nginx-echo/src/ngx_http_echo_request_info.h +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ 0.8/modules/nginx-echo/src/ngx_http_echo_request_info.h 2010-10-31 07:35:23.648338001 +0000 +@@ -0,0 +1,31 @@ ++#ifndef ECHO_REQUEST_INFO_H ++#define ECHO_REQUEST_INFO_H ++ ++#include "ngx_http_echo_module.h" ++ ++ngx_int_t ngx_http_echo_exec_echo_read_request_body( ++ ngx_http_request_t* r, ngx_http_echo_ctx_t *ctx); ++ ++ngx_int_t ngx_http_echo_request_method_variable(ngx_http_request_t *r, ++ ngx_http_variable_value_t *v, uintptr_t data); ++ ++ngx_int_t ngx_http_echo_client_request_method_variable(ngx_http_request_t *r, ++ ngx_http_variable_value_t *v, uintptr_t data); ++ ++ngx_int_t ngx_http_echo_request_body_variable(ngx_http_request_t *r, ++ ngx_http_variable_value_t *v, uintptr_t data); ++ ++ngx_int_t ngx_http_echo_client_request_headers_variable(ngx_http_request_t *r, ++ ngx_http_variable_value_t *v, uintptr_t data); ++ ++ngx_int_t ngx_http_echo_cacheable_request_uri_variable(ngx_http_request_t *r, ++ ngx_http_variable_value_t *v, uintptr_t data); ++ ++ngx_int_t ngx_http_echo_request_uri_variable(ngx_http_request_t *r, ++ ngx_http_variable_value_t *v, uintptr_t data); ++ ++ngx_int_t ngx_http_echo_response_status_variable(ngx_http_request_t *r, ++ ngx_http_variable_value_t *v, uintptr_t data); ++ ++#endif /* ECHO_REQUEST_INFO_H */ ++ +Index: 0.8/modules/nginx-echo/src/ngx_http_echo_sleep.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ 0.8/modules/nginx-echo/src/ngx_http_echo_sleep.c 2010-10-31 07:35:23.648338001 +0000 +@@ -0,0 +1,200 @@ ++/* Copyright (C) agentzh */ ++ ++#define DDEBUG 0 ++#include "ddebug.h" ++ ++#include "ngx_http_echo_sleep.h" ++#include "ngx_http_echo_handler.h" ++ ++#include ++#include ++ ++/* event handler for echo_sleep */ ++ ++static void ngx_http_echo_post_sleep(ngx_http_request_t *r); ++ ++static void ngx_http_echo_sleep_cleanup(void *data); ++ ++ ++ngx_int_t ++ngx_http_echo_exec_echo_sleep( ++ ngx_http_request_t *r, ngx_http_echo_ctx_t *ctx, ++ ngx_array_t *computed_args) ++{ ++ ngx_str_t *computed_arg; ++ ngx_str_t *computed_arg_elts; ++ float delay; /* in sec */ ++ ngx_http_cleanup_t *cln; ++ ++ computed_arg_elts = computed_args->elts; ++ computed_arg = &computed_arg_elts[0]; ++ ++ delay = atof( (char*) computed_arg->data ); ++ ++ if (delay < 0.001) { /* should be bigger than 1 msec */ ++ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, ++ "invalid sleep duration \"%V\"", &computed_arg_elts[0]); ++ ++ return NGX_HTTP_BAD_REQUEST; ++ } ++ ++ dd("DELAY = %.02lf sec", delay); ++ ++ ngx_add_timer(&ctx->sleep, (ngx_msec_t) (1000 * delay)); ++ ++ /* we don't check broken downstream connections ++ * ourselves so even if the client shuts down ++ * the connection prematurely, nginx will still ++ * go on waiting for our timers to get properly ++ * expired. However, we'd still register a ++ * cleanup handler for completeness. */ ++ ++ cln = ngx_http_cleanup_add(r, 0); ++ if (cln == NULL) { ++ return NGX_ERROR; ++ } ++ ++ cln->handler = ngx_http_echo_sleep_cleanup; ++ cln->data = r; ++ ++ return NGX_AGAIN; ++} ++ ++ ++static void ++ngx_http_echo_post_sleep(ngx_http_request_t *r) ++{ ++ ngx_http_echo_ctx_t *ctx; ++ /* ngx_int_t rc; */ ++ ++ dd("entered echo post sleep...(r->done: %d)", r->done); ++ ++ dd("sleep: before get module ctx"); ++ ++ ctx = ngx_http_get_module_ctx(r, ngx_http_echo_module); ++ ++ if (ctx == NULL) { ++ return; ++ } ++ ++ ctx->waiting = 0; ++ ctx->done = 1; ++ ++ dd("sleep: after get module ctx"); ++ ++ dd("timed out? %d", ctx->sleep.timedout); ++ dd("timer set? %d", ctx->sleep.timer_set); ++ ++ if ( ! ctx->sleep.timedout ) { ++ dd("HERE reached!"); ++ return; ++ } ++ ++ ctx->sleep.timedout = 0; ++ ++ if (ctx->sleep.timer_set) { ++ dd("deleting timer for echo_sleep"); ++ ++ ngx_del_timer(&ctx->sleep); ++ } ++ ++ /* r->write_event_handler = ngx_http_request_empty_handler; */ ++ ++ ngx_http_echo_wev_handler(r); ++} ++ ++ ++void ++ngx_http_echo_sleep_event_handler(ngx_event_t *ev) ++{ ++ ngx_connection_t *c; ++ ngx_http_request_t *r; ++ ngx_http_log_ctx_t *ctx; ++ ++ r = ev->data; ++ c = r->connection; ++ ++ if (c->destroyed) { ++ return; ++ } ++ ++ ctx = c->log->data; ++ ctx->current_request = r; ++ ++ /* XXX when r->done == 1 we should do cleaning immediately ++ * and delete our timer and then quit. */ ++ ++ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, ++ "echo sleep handler: \"%V?%V\"", &r->uri, &r->args); ++ ++ /* ++ if (r->done) { ++ return; ++ } ++ */ ++ ++ ngx_http_echo_post_sleep(r); ++ ++#if defined(nginx_version) ++ ++ dd("before run posted requests"); ++ ++ ngx_http_run_posted_requests(c); ++ ++ dd("after run posted requests"); ++ ++#endif ++ ++} ++ ++ ++ngx_int_t ++ngx_http_echo_exec_echo_blocking_sleep(ngx_http_request_t *r, ++ ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args) ++{ ++ ngx_str_t *computed_arg; ++ ngx_str_t *computed_arg_elts; ++ float delay; /* in sec */ ++ ++ computed_arg_elts = computed_args->elts; ++ computed_arg = &computed_arg_elts[0]; ++ ++ delay = atof( (char*) computed_arg->data ); ++ ++ if (delay < 0.001) { /* should be bigger than 1 msec */ ++ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, ++ "invalid sleep duration \"%V\"", &computed_arg_elts[0]); ++ return NGX_HTTP_BAD_REQUEST; ++ } ++ ++ dd("blocking DELAY = %.02lf sec", delay); ++ ++ ngx_msleep((ngx_msec_t) (1000 * delay)); ++ ++ return NGX_OK; ++} ++ ++ ++static void ++ngx_http_echo_sleep_cleanup(void *data) ++{ ++ ngx_http_request_t *r = data; ++ ngx_http_echo_ctx_t *ctx; ++ ++ dd("echo sleep cleanup"); ++ ++ ctx = ngx_http_get_module_ctx(r, ngx_http_echo_module); ++ if (ctx == NULL) { ++ return; ++ } ++ ++ if (ctx->sleep.timer_set) { ++ dd("cleanup: deleting timer for echo_sleep"); ++ ++ ngx_del_timer(&ctx->sleep); ++ return; ++ } ++ ++ dd("cleanup: timer not set"); ++} ++ +Index: 0.8/modules/nginx-echo/src/ngx_http_echo_sleep.h +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ 0.8/modules/nginx-echo/src/ngx_http_echo_sleep.h 2010-10-31 07:35:23.648338001 +0000 +@@ -0,0 +1,16 @@ ++#ifndef ECHO_SLEEP_H ++#define ECHO_SLEEP_H ++ ++#include "ngx_http_echo_module.h" ++ ++ngx_int_t ngx_http_echo_exec_echo_sleep( ++ ngx_http_request_t *r, ngx_http_echo_ctx_t *ctx, ++ ngx_array_t *computed_args); ++ ++ngx_int_t ngx_http_echo_exec_echo_blocking_sleep(ngx_http_request_t *r, ++ ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args); ++ ++void ngx_http_echo_sleep_event_handler(ngx_event_t *ev); ++ ++#endif /* ECHO_SLEEP_H */ ++ +Index: 0.8/modules/nginx-echo/src/ngx_http_echo_subrequest.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ 0.8/modules/nginx-echo/src/ngx_http_echo_subrequest.c 2010-10-31 07:35:23.648338001 +0000 +@@ -0,0 +1,606 @@ ++#define DDEBUG 0 ++#include "ddebug.h" ++ ++#include "ngx_http_echo_util.h" ++#include "ngx_http_echo_subrequest.h" ++#include "ngx_http_echo_handler.h" ++ ++#define ngx_http_echo_method_name(m) { sizeof(m) - 1, (u_char *) m " " } ++ ++ngx_str_t ngx_http_echo_content_length_header_key = ngx_string("Content-Length"); ++ ++ngx_str_t ngx_http_echo_get_method = ngx_http_echo_method_name("GET"); ++ngx_str_t ngx_http_echo_put_method = ngx_http_echo_method_name("PUT"); ++ngx_str_t ngx_http_echo_post_method = ngx_http_echo_method_name("POST"); ++ngx_str_t ngx_http_echo_head_method = ngx_http_echo_method_name("HEAD"); ++ngx_str_t ngx_http_echo_copy_method = ngx_http_echo_method_name("COPY"); ++ngx_str_t ngx_http_echo_move_method = ngx_http_echo_method_name("MOVE"); ++ngx_str_t ngx_http_echo_lock_method = ngx_http_echo_method_name("LOCK"); ++ngx_str_t ngx_http_echo_mkcol_method = ngx_http_echo_method_name("MKCOL"); ++ngx_str_t ngx_http_echo_trace_method = ngx_http_echo_method_name("TRACE"); ++ngx_str_t ngx_http_echo_delete_method = ngx_http_echo_method_name("DELETE"); ++ngx_str_t ngx_http_echo_unlock_method = ngx_http_echo_method_name("UNLOCK"); ++ngx_str_t ngx_http_echo_options_method = ngx_http_echo_method_name("OPTIONS"); ++ngx_str_t ngx_http_echo_propfind_method = ngx_http_echo_method_name("PROPFIND"); ++ngx_str_t ngx_http_echo_proppatch_method = ngx_http_echo_method_name("PROPPATCH"); ++ ++ ++typedef struct ngx_http_echo_subrequest_s { ++ ngx_uint_t method; ++ ngx_str_t *method_name; ++ ngx_str_t *location; ++ ngx_str_t *query_string; ++ ssize_t content_length_n; ++ ngx_http_request_body_t *request_body; ++} ngx_http_echo_subrequest_t; ++ ++ ++static ngx_int_t ngx_http_echo_parse_method_name(ngx_str_t **method_name_ptr); ++ ++static ngx_int_t ngx_http_echo_adjust_subrequest(ngx_http_request_t *sr, ++ ngx_http_echo_subrequest_t *parsed_sr); ++ ++static ngx_int_t ngx_http_echo_parse_subrequest_spec(ngx_http_request_t *r, ++ ngx_array_t *computed_args, ngx_http_echo_subrequest_t **parsed_sr_ptr); ++ ++ ++ngx_int_t ++ngx_http_echo_exec_echo_subrequest_async(ngx_http_request_t *r, ++ ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args) ++{ ++ ngx_int_t rc; ++ ngx_http_echo_subrequest_t *parsed_sr; ++ ngx_http_request_t *sr; /* subrequest object */ ++ ngx_str_t args; ++ ngx_uint_t flags = 0; ++ ++ ++ dd_enter(); ++ ++ rc = ngx_http_echo_parse_subrequest_spec(r, computed_args, &parsed_sr); ++ if (rc != NGX_OK) { ++ return rc; ++ } ++ ++ dd("location: %s", parsed_sr->location->data); ++ dd("location args: %s", (char*) (parsed_sr->query_string ? ++ parsed_sr->query_string->data : (u_char*)"NULL")); ++ ++ args.data = NULL; ++ args.len = 0; ++ ++ if (ngx_http_parse_unsafe_uri(r, parsed_sr->location, &args, &flags) ++ != NGX_OK) ++ { ++ ctx->headers_sent = 1; ++ return NGX_HTTP_INTERNAL_SERVER_ERROR; ++ } ++ ++ if (args.len > 0 && parsed_sr->query_string == NULL) { ++ parsed_sr->query_string = &args; ++ } ++ ++ rc = ngx_http_echo_send_header_if_needed(r, ctx); ++ if (r->header_only || rc >= NGX_HTTP_SPECIAL_RESPONSE) { ++ return rc; ++ } ++ ++ rc = ngx_http_subrequest(r, parsed_sr->location, parsed_sr->query_string, ++ &sr, NULL, 0); ++ ++ if (rc != NGX_OK) { ++ return NGX_ERROR; ++ } ++ ++ rc = ngx_http_echo_adjust_subrequest(sr, parsed_sr); ++ ++ if (rc != NGX_OK) { ++ return rc; ++ } ++ ++ return NGX_OK; ++} ++ ++ ++ngx_int_t ++ngx_http_echo_exec_echo_subrequest(ngx_http_request_t *r, ++ ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args) ++{ ++ ngx_int_t rc; ++ ngx_http_request_t *sr; /* subrequest object */ ++ ngx_http_post_subrequest_t *psr; ++ ngx_http_echo_subrequest_t *parsed_sr; ++ ngx_str_t args; ++ ngx_uint_t flags = 0; ++ ++ ++ dd_enter(); ++ ++ rc = ngx_http_echo_parse_subrequest_spec(r, computed_args, &parsed_sr); ++ if (rc != NGX_OK) { ++ return rc; ++ } ++ ++ args.data = NULL; ++ args.len = 0; ++ ++ if (ngx_http_parse_unsafe_uri(r, parsed_sr->location, &args, &flags) ++ != NGX_OK) { ++ ctx->headers_sent = 1; ++ return NGX_HTTP_INTERNAL_SERVER_ERROR; ++ } ++ ++ if (args.len > 0 && parsed_sr->query_string == NULL) { ++ parsed_sr->query_string = &args; ++ } ++ ++ rc = ngx_http_echo_send_header_if_needed(r, ctx); ++ ++ if (r->header_only || rc >= NGX_HTTP_SPECIAL_RESPONSE) { ++ return rc; ++ } ++ ++ psr = ngx_palloc(r->pool, sizeof(ngx_http_post_subrequest_t)); ++ ++ if (psr == NULL) { ++ return NGX_HTTP_INTERNAL_SERVER_ERROR; ++ } ++ ++ psr->handler = ngx_http_echo_post_subrequest; ++ psr->data = ctx; ++ ++ rc = ngx_http_subrequest(r, parsed_sr->location, parsed_sr->query_string, ++ &sr, psr, 0); ++ ++ if (rc != NGX_OK) { ++ return NGX_ERROR; ++ } ++ ++ rc = ngx_http_echo_adjust_subrequest(sr, parsed_sr); ++ ++ if (rc != NGX_OK) { ++ return NGX_ERROR; ++ } ++ ++ return NGX_AGAIN; ++} ++ ++ ++static ngx_int_t ++ngx_http_echo_parse_subrequest_spec(ngx_http_request_t *r, ++ ngx_array_t *computed_args, ngx_http_echo_subrequest_t **parsed_sr_ptr) ++{ ++ ngx_str_t *computed_arg_elts, *arg; ++ ngx_str_t **to_write = NULL; ++ ngx_str_t *method_name; ++ ngx_str_t *body_str = NULL; ++ ngx_uint_t i; ++ ngx_flag_t expecting_opt; ++ ngx_http_request_body_t *rb = NULL; ++ ngx_buf_t *b; ++ ngx_http_echo_subrequest_t *parsed_sr; ++ ++ *parsed_sr_ptr = ngx_pcalloc(r->pool, sizeof(ngx_http_echo_subrequest_t)); ++ if (*parsed_sr_ptr == NULL) { ++ return NGX_ERROR; ++ } ++ ++ parsed_sr = *parsed_sr_ptr; ++ ++ computed_arg_elts = computed_args->elts; ++ ++ method_name = &computed_arg_elts[0]; ++ ++ parsed_sr->location = &computed_arg_elts[1]; ++ ++ if (parsed_sr->location->len == 0) { ++ return NGX_HTTP_INTERNAL_SERVER_ERROR; ++ } ++ ++ expecting_opt = 1; ++ for (i = 2; i < computed_args->nelts; i++) { ++ arg = &computed_arg_elts[i]; ++ if (!expecting_opt) { ++ if (to_write == NULL) { ++ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, ++ "echo_subrequest_async: to_write should NOT be NULL"); ++ return NGX_HTTP_INTERNAL_SERVER_ERROR; ++ } ++ ++ *to_write = arg; ++ to_write = NULL; ++ ++ expecting_opt = 1; ++ ++ continue; ++ } ++ ++ if (arg->len == 2) { ++ if (ngx_strncmp("-q", arg->data, arg->len) == 0) { ++ to_write = &parsed_sr->query_string; ++ expecting_opt = 0; ++ continue; ++ } ++ ++ if (ngx_strncmp("-b", arg->data, arg->len) == 0) { ++ to_write = &body_str; ++ expecting_opt = 0; ++ continue; ++ } ++ } ++ ++ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, ++ "Unknown option for echo_subrequest_async: %V", arg); ++ ++ return NGX_HTTP_INTERNAL_SERVER_ERROR; ++ } ++ ++ if (body_str != NULL && body_str->len != 0) { ++ rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t)); ++ ++ if (rb == NULL) { ++ return NGX_HTTP_INTERNAL_SERVER_ERROR; ++ } ++ ++ parsed_sr->content_length_n = body_str->len; ++ ++ b = ngx_calloc_buf(r->pool); ++ if (b == NULL) { ++ return NGX_HTTP_INTERNAL_SERVER_ERROR; ++ } ++ ++ b->temporary = 1; ++ /* b->memory = 1; */ ++ b->start = b->pos = body_str->data; ++ b->end = b->last = body_str->data + body_str->len; ++ ++ rb->bufs = ngx_alloc_chain_link(r->pool); ++ if (rb->bufs == NULL) { ++ return NGX_HTTP_INTERNAL_SERVER_ERROR; ++ } ++ ++ rb->bufs->buf = b; ++ rb->bufs->next = NULL; ++ ++ rb->buf = b; ++ } ++ ++ parsed_sr->request_body = rb; ++ ++ parsed_sr->method = ngx_http_echo_parse_method_name(&method_name); ++ parsed_sr->method_name = method_name; ++ ++ return NGX_OK; ++} ++ ++ ++static ngx_int_t ++ngx_http_echo_adjust_subrequest(ngx_http_request_t *sr, ++ ngx_http_echo_subrequest_t *parsed_sr) ++{ ++ ngx_table_elt_t *h; ++ ngx_http_core_main_conf_t *cmcf; ++ ngx_http_request_t *r; ++ ++ sr->method = parsed_sr->method; ++ sr->method_name = *(parsed_sr->method_name); ++ ++ r = sr->parent; ++ ++ sr->header_in = r->header_in; ++ ++ /* XXX work-around a bug in ngx_http_subrequest */ ++ if (r->headers_in.headers.last == &r->headers_in.headers.part) { ++ sr->headers_in.headers.last = &sr->headers_in.headers.part; ++ } ++ ++ /* we do not inherit the parent request's variables */ ++ cmcf = ngx_http_get_module_main_conf(sr, ngx_http_core_module); ++ sr->variables = ngx_pcalloc(sr->pool, cmcf->variables.nelts ++ * sizeof(ngx_http_variable_value_t)); ++ ++ if (sr->variables == NULL) { ++ return NGX_HTTP_INTERNAL_SERVER_ERROR; ++ } ++ ++ if (parsed_sr->content_length_n > 0) { ++ sr->headers_in.content_length_n = parsed_sr->content_length_n; ++ sr->request_body = parsed_sr->request_body; ++ ++ sr->headers_in.content_length = ngx_pcalloc(sr->pool, ++ sizeof(ngx_table_elt_t)); ++ sr->headers_in.content_length->value.data = ++ ngx_palloc(sr->pool, NGX_OFF_T_LEN); ++ if (sr->headers_in.content_length->value.data == NULL) { ++ return NGX_HTTP_INTERNAL_SERVER_ERROR; ++ } ++ sr->headers_in.content_length->value.len = ngx_sprintf( ++ sr->headers_in.content_length->value.data, "%O", ++ sr->headers_in.content_length_n) - ++ sr->headers_in.content_length->value.data; ++ ++ if (ngx_list_init(&sr->headers_in.headers, sr->pool, 20, ++ sizeof(ngx_table_elt_t)) != NGX_OK) { ++ return NGX_HTTP_INTERNAL_SERVER_ERROR; ++ } ++ ++ h = ngx_list_push(&sr->headers_in.headers); ++ if (h == NULL) { ++ return NGX_HTTP_INTERNAL_SERVER_ERROR; ++ } ++ ++ h->hash = sr->header_hash; ++ ++ h->key = ngx_http_echo_content_length_header_key; ++ h->value = sr->headers_in.content_length->value; ++ ++ h->lowcase_key = ngx_pnalloc(sr->pool, h->key.len); ++ if (h->lowcase_key == NULL) { ++ return NGX_HTTP_INTERNAL_SERVER_ERROR; ++ } ++ ++ ngx_strlow(h->lowcase_key, h->key.data, h->key.len); ++ ++ dd("sr content length: %s", sr->headers_in.content_length->value.data); ++ } ++ ++ dd("subrequest body: %p", sr->request_body); ++ ++ return NGX_OK; ++} ++ ++ ++static ngx_int_t ++ngx_http_echo_parse_method_name(ngx_str_t **method_name_ptr) ++{ ++ const ngx_str_t* method_name = *method_name_ptr; ++ ++ switch (method_name->len) { ++ case 3: ++ if (ngx_http_echo_strcmp_const(method_name->data, "GET") == 0) { ++ *method_name_ptr = &ngx_http_echo_get_method; ++ return NGX_HTTP_GET; ++ break; ++ } ++ ++ if (ngx_http_echo_strcmp_const(method_name->data, "PUT") == 0) { ++ *method_name_ptr = &ngx_http_echo_put_method; ++ return NGX_HTTP_PUT; ++ break; ++ } ++ ++ return NGX_HTTP_UNKNOWN; ++ break; ++ ++ case 4: ++ if (ngx_http_echo_strcmp_const(method_name->data, "POST") == 0) { ++ *method_name_ptr = &ngx_http_echo_post_method; ++ return NGX_HTTP_POST; ++ break; ++ } ++ if (ngx_http_echo_strcmp_const(method_name->data, "HEAD") == 0) { ++ *method_name_ptr = &ngx_http_echo_head_method; ++ return NGX_HTTP_HEAD; ++ break; ++ } ++ if (ngx_http_echo_strcmp_const(method_name->data, "COPY") == 0) { ++ *method_name_ptr = &ngx_http_echo_copy_method; ++ return NGX_HTTP_COPY; ++ break; ++ } ++ if (ngx_http_echo_strcmp_const(method_name->data, "MOVE") == 0) { ++ *method_name_ptr = &ngx_http_echo_move_method; ++ return NGX_HTTP_MOVE; ++ break; ++ } ++ if (ngx_http_echo_strcmp_const(method_name->data, "LOCK") == 0) { ++ *method_name_ptr = &ngx_http_echo_lock_method; ++ return NGX_HTTP_LOCK; ++ break; ++ } ++ return NGX_HTTP_UNKNOWN; ++ break; ++ ++ case 5: ++ if (ngx_http_echo_strcmp_const(method_name->data, "MKCOL") == 0) { ++ *method_name_ptr = &ngx_http_echo_mkcol_method; ++ return NGX_HTTP_MKCOL; ++ break; ++ } ++ if (ngx_http_echo_strcmp_const(method_name->data, "TRACE") == 0) { ++ *method_name_ptr = &ngx_http_echo_trace_method; ++ return NGX_HTTP_TRACE; ++ break; ++ } ++ return NGX_HTTP_UNKNOWN; ++ break; ++ ++ case 6: ++ if (ngx_http_echo_strcmp_const(method_name->data, "DELETE") == 0) { ++ *method_name_ptr = &ngx_http_echo_delete_method; ++ return NGX_HTTP_DELETE; ++ break; ++ } ++ ++ if (ngx_http_echo_strcmp_const(method_name->data, "UNLOCK") == 0) { ++ *method_name_ptr = &ngx_http_echo_unlock_method; ++ return NGX_HTTP_UNLOCK; ++ break; ++ } ++ return NGX_HTTP_UNKNOWN; ++ break; ++ ++ case 7: ++ if (ngx_http_echo_strcmp_const(method_name->data, "OPTIONS") == 0) { ++ *method_name_ptr = &ngx_http_echo_options_method; ++ return NGX_HTTP_OPTIONS; ++ break; ++ } ++ return NGX_HTTP_UNKNOWN; ++ break; ++ ++ case 8: ++ if (ngx_http_echo_strcmp_const(method_name->data, "PROPFIND") == 0) { ++ *method_name_ptr = &ngx_http_echo_propfind_method; ++ return NGX_HTTP_PROPFIND; ++ break; ++ } ++ return NGX_HTTP_UNKNOWN; ++ break; ++ ++ case 9: ++ if (ngx_http_echo_strcmp_const(method_name->data, "PROPPATCH") == 0) { ++ *method_name_ptr = &ngx_http_echo_proppatch_method; ++ return NGX_HTTP_PROPPATCH; ++ break; ++ } ++ return NGX_HTTP_UNKNOWN; ++ break; ++ ++ default: ++ return NGX_HTTP_UNKNOWN; ++ break; ++ } ++ ++ return NGX_HTTP_UNKNOWN; ++} ++ ++ ++/* XXX extermely evil and not working yet */ ++ngx_int_t ++ngx_http_echo_exec_abort_parent(ngx_http_request_t *r, ++ ngx_http_echo_ctx_t *ctx) ++{ ++#if 0 ++ ngx_http_postponed_request_t *pr, *ppr; ++ ngx_http_request_t *saved_data = NULL; ++ ngx_chain_t *out = NULL; ++ /* ngx_int_t rc; */ ++ ++ dd("aborting parent..."); ++ ++ if (r == r->main || r->parent == NULL) { ++ return NGX_OK; ++ } ++ ++ if (r->parent->postponed) { ++ dd("Found parent->postponed..."); ++ ++ saved_data = r->connection->data; ++ ppr = NULL; ++ for (pr = r->parent->postponed; pr->next; pr = pr->next) { ++ if (pr->request == NULL) { ++ continue; ++ } ++ ++ if (pr->request == r) { ++ /* r->parent->postponed->next = pr; */ ++ dd("found the current subrequest"); ++ out = pr->out; ++ continue; ++ } ++ ++ /* r->connection->data = pr->request; */ ++ dd("finalizing the subrequest..."); ++ ngx_http_upstream_create(pr->request); ++ pr->request->upstream = NULL; ++ ++ if (ppr == NULL) { ++ r->parent->postponed = pr->next; ++ ppr = pr->next; ++ } else { ++ ppr->next = pr->next; ++ ppr = pr->next; ++ } ++ } ++ } ++ ++ r->parent->postponed->next = NULL; ++ ++ /* ++ r->connection->data = r->parent; ++ r->connection->buffered = 0; ++ ++ if (out != NULL) { ++ dd("trying to send more stuffs for the parent"); ++ ngx_http_output_filter(r->parent, out); ++ } ++ */ ++ ++ /* ngx_http_send_special(r->parent, NGX_HTTP_LAST); */ ++ ++ if (saved_data) { ++ r->connection->data = saved_data; ++ } ++ ++ dd("terminating the parent request"); ++ ++ return ngx_http_echo_send_chain_link(r, ctx, NULL /* indicate LAST */); ++ ++ /* ngx_http_upstream_create(r); */ ++ ++ /* ngx_http_finalize_request(r->parent, NGX_ERROR); */ ++#endif ++ ++ return NGX_OK; ++} ++ ++ ++ngx_int_t ++ngx_http_echo_exec_exec(ngx_http_request_t *r, ++ ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args) ++{ ++ ngx_str_t *uri; ++ ngx_str_t *user_args; ++ ngx_str_t args; ++ ngx_uint_t flags; ++ ngx_str_t *computed_arg; ++ ++ computed_arg = computed_args->elts; ++ ++ uri = &computed_arg[0]; ++ ++ if (uri->len == 0) { ++ return NGX_HTTP_BAD_REQUEST; ++ } ++ ++ if (computed_args->nelts > 1) { ++ user_args = &computed_arg[1]; ++ } else { ++ user_args = NULL; ++ } ++ ++ args.data = NULL; ++ args.len = 0; ++ ++ if (ngx_http_parse_unsafe_uri(r, uri, &args, &flags) ++ != NGX_OK) { ++ ctx->headers_sent = 1; ++ return NGX_HTTP_INTERNAL_SERVER_ERROR; ++ } ++ ++ if (args.len > 0 && user_args == NULL) { ++ user_args = &args; ++ } ++ ++ /* ++ rc = ngx_http_echo_send_header_if_needed(r, ctx); ++ if (r->header_only || rc >= NGX_HTTP_SPECIAL_RESPONSE) { ++ return rc; ++ } ++ */ ++ ++ if (uri->data[0] == '@') { ++ if (user_args && user_args->len > 0) { ++ ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, ++ "query strings %V ignored when exec'ing named location %V", ++ user_args, uri); ++ ++ } ++ ++ return ngx_http_named_location(r, uri); ++ } ++ ++ return ngx_http_internal_redirect(r, uri, user_args); ++} ++ +Index: 0.8/modules/nginx-echo/src/ngx_http_echo_subrequest.h +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ 0.8/modules/nginx-echo/src/ngx_http_echo_subrequest.h 2010-10-31 07:35:23.648338001 +0000 +@@ -0,0 +1,19 @@ ++#ifndef ECHO_SUBREQUEST_H ++#define ECHO_SUBREQUEST_H ++ ++#include "ngx_http_echo_module.h" ++ ++ngx_int_t ngx_http_echo_exec_echo_subrequest(ngx_http_request_t *r, ++ ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args); ++ ++ngx_int_t ngx_http_echo_exec_echo_subrequest_async(ngx_http_request_t *r, ++ ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args); ++ ++ngx_int_t ngx_http_echo_exec_abort_parent(ngx_http_request_t *r, ++ ngx_http_echo_ctx_t *ctx); ++ ++ngx_int_t ngx_http_echo_exec_exec(ngx_http_request_t *r, ++ ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args); ++ ++#endif /* ECHO_SUBREQUEST_H */ ++ +Index: 0.8/modules/nginx-echo/src/ngx_http_echo_timer.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ 0.8/modules/nginx-echo/src/ngx_http_echo_timer.c 2010-10-31 07:35:23.648338001 +0000 +@@ -0,0 +1,76 @@ ++#define DDEBUG 0 ++ ++#include "ddebug.h" ++ ++#include "ngx_http_echo_timer.h" ++ ++#include ++#include ++ ++ngx_int_t ++ngx_http_echo_timer_elapsed_variable(ngx_http_request_t *r, ++ ngx_http_variable_value_t *v, uintptr_t data) ++{ ++ ngx_http_echo_ctx_t *ctx; ++ ngx_msec_int_t ms; ++ u_char *p; ++ ngx_time_t *tp; ++ size_t size; ++ ++ ctx = ngx_http_get_module_ctx(r, ngx_http_echo_module); ++ if (ctx->timer_begin.sec == 0) { ++ ctx->timer_begin.sec = r->start_sec; ++ ctx->timer_begin.msec = (ngx_msec_t) r->start_msec; ++ } ++ ++ /* force the ngx timer to update */ ++ ++#if defined nginx_version && (nginx_version >= 8035 \ ++ || (nginx_version < 8000 && nginx_version >= 7066)) ++ ngx_time_update(); ++#else ++ ngx_time_update(0, 0); ++#endif ++ ++ tp = ngx_timeofday(); ++ ++ dd("old sec msec: %ld %d\n", ctx->timer_begin.sec, ctx->timer_begin.msec); ++ dd("new sec msec: %ld %d\n", tp->sec, tp->msec); ++ ++ ms = (ngx_msec_int_t) ++ ((tp->sec - ctx->timer_begin.sec) * 1000 + ++ (tp->msec - ctx->timer_begin.msec)); ++ ms = (ms >= 0) ? ms : 0; ++ ++ size = sizeof("-9223372036854775808.000") - 1; ++ p = ngx_palloc(r->pool, size); ++ v->len = ngx_snprintf(p, size, "%T.%03M", ++ ms / 1000, ms % 1000) - p; ++ v->data = p; ++ ++ v->valid = 1; ++ v->no_cacheable = 1; ++ v->not_found = 0; ++ return NGX_OK; ++} ++ ++ ++ngx_int_t ++ngx_http_echo_exec_echo_reset_timer(ngx_http_request_t *r, ++ ngx_http_echo_ctx_t *ctx) ++{ ++ dd("Exec timer..."); ++ ++ /* force the ngx timer to update */ ++ ++#if defined nginx_version && (nginx_version >= 8035 \ ++ || (nginx_version < 8000 && nginx_version >= 7066)) ++ ngx_time_update(); ++#else ++ ngx_time_update(0, 0); ++#endif ++ ++ ctx->timer_begin = *ngx_timeofday(); ++ return NGX_OK; ++} ++ +Index: 0.8/modules/nginx-echo/src/ngx_http_echo_timer.h +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ 0.8/modules/nginx-echo/src/ngx_http_echo_timer.h 2010-10-31 07:35:23.648338001 +0000 +@@ -0,0 +1,13 @@ ++#ifndef ECHO_TIMER_H ++#define ECHO_TIMER_H ++ ++#include "ngx_http_echo_module.h" ++ ++ngx_int_t ngx_http_echo_timer_elapsed_variable(ngx_http_request_t *r, ++ ngx_http_variable_value_t *v, uintptr_t data); ++ ++ngx_int_t ngx_http_echo_exec_echo_reset_timer(ngx_http_request_t *r, ++ ngx_http_echo_ctx_t *ctx); ++ ++#endif /* ECHO_TIMER_H */ ++ +Index: 0.8/modules/nginx-echo/src/ngx_http_echo_util.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ 0.8/modules/nginx-echo/src/ngx_http_echo_util.c 2010-10-31 07:35:23.648338001 +0000 +@@ -0,0 +1,248 @@ ++#define DDEBUG 0 ++#include "ddebug.h" ++ ++#include "ngx_http_echo_util.h" ++#include "ngx_http_echo_sleep.h" ++ ++ ++ngx_int_t ++ngx_http_echo_init_ctx(ngx_http_request_t *r, ngx_http_echo_ctx_t **ctx_ptr) ++{ ++ ngx_http_echo_ctx_t *ctx; ++ ++ *ctx_ptr = ngx_pcalloc(r->pool, sizeof(ngx_http_echo_ctx_t)); ++ if (*ctx_ptr == NULL) { ++ return NGX_HTTP_INTERNAL_SERVER_ERROR; ++ } ++ ++ ctx = *ctx_ptr; ++ ++ ctx->sleep.handler = ngx_http_echo_sleep_event_handler; ++ ctx->sleep.data = r; ++ ctx->sleep.log = r->connection->log; ++ ++ return NGX_OK; ++} ++ ++ngx_int_t ++ngx_http_echo_eval_cmd_args(ngx_http_request_t *r, ++ ngx_http_echo_cmd_t *cmd, ngx_array_t *computed_args, ++ ngx_array_t *opts) ++{ ++ ngx_uint_t i; ++ ngx_array_t *args = cmd->args; ++ ngx_str_t *arg, *raw, *opt; ++ ngx_http_echo_arg_template_t *value; ++ ngx_flag_t expecting_opts = 1; ++ ++ ++ value = args->elts; ++ ++ for (i = 0; i < args->nelts; i++) { ++ raw = &value[i].raw_value; ++ ++ if (value[i].lengths == NULL && raw->len > 0) { ++ if (expecting_opts) { ++ if (raw->len == 1 || raw->data[0] != '-') { ++ expecting_opts = 0; ++ ++ } else if (raw->data[1] == '-') { ++ expecting_opts = 0; ++ continue; ++ ++ } else { ++ opt = ngx_array_push(opts); ++ if (opt == NULL) { ++ return NGX_HTTP_INTERNAL_SERVER_ERROR; ++ } ++ ++ opt->len = raw->len - 1; ++ opt->data = raw->data + 1; ++ ++ continue; ++ } ++ } ++ } ++ ++ arg = ngx_array_push(computed_args); ++ if (arg == NULL) { ++ return NGX_HTTP_INTERNAL_SERVER_ERROR; ++ } ++ ++ if (value[i].lengths == NULL) { /* does not contain vars */ ++ dd("Using raw value \"%.*s\"", (int) raw->len, raw->data); ++ *arg = *raw; ++ ++ } else { ++ if (ngx_http_script_run(r, arg, value[i].lengths->elts, ++ 0, value[i].values->elts) == NULL) ++ { ++ return NGX_HTTP_INTERNAL_SERVER_ERROR; ++ } ++ } ++ } ++ ++ return NGX_OK; ++} ++ ++ ++ngx_int_t ++ngx_http_echo_send_chain_link(ngx_http_request_t* r, ++ ngx_http_echo_ctx_t *ctx, ngx_chain_t *cl) ++{ ++ ngx_int_t rc; ++ size_t size; ++ ngx_chain_t *p; ++ ++ rc = ngx_http_echo_send_header_if_needed(r, ctx); ++ ++ if (r->header_only || rc >= NGX_HTTP_SPECIAL_RESPONSE) { ++ return rc; ++ } ++ ++ if (r->http_version < NGX_HTTP_VERSION_11 && !ctx->headers_sent) { ++ ctx->headers_sent = 1; ++ ++ size = 0; ++ ++ for (p = cl; p; p = p->next) { ++ if (p->buf->memory) { ++ size += p->buf->last - p->buf->pos; ++ } ++ } ++ ++ r->headers_out.content_length_n = (off_t) size; ++ ++ if (r->headers_out.content_length) { ++ r->headers_out.content_length->hash = 0; ++ } ++ ++ r->headers_out.content_length = NULL; ++ ++ rc = ngx_http_send_header(r); ++ ++ if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { ++ return rc; ++ } ++ } ++ ++ if (cl == NULL) { ++ ++#if defined(nginx_version) && nginx_version <= 8004 ++ ++ /* earlier versions of nginx does not allow subrequests ++ to send last_buf themselves */ ++ if (r != r->main) { ++ return NGX_OK; ++ } ++ ++#endif ++ ++ rc = ngx_http_send_special(r, NGX_HTTP_LAST); ++ if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { ++ return rc; ++ } ++ ++ return NGX_OK; ++ } ++ ++ return ngx_http_output_filter(r, cl); ++} ++ ++ngx_int_t ++ngx_http_echo_send_header_if_needed(ngx_http_request_t* r, ++ ngx_http_echo_ctx_t *ctx) { ++ /* ngx_int_t rc; */ ++ ++ if ( ! ctx->headers_sent ) { ++ r->headers_out.status = NGX_HTTP_OK; ++ ++ if (ngx_http_set_content_type(r) != NGX_OK) { ++ return NGX_HTTP_INTERNAL_SERVER_ERROR; ++ } ++ ++ ngx_http_clear_content_length(r); ++ ngx_http_clear_accept_ranges(r); ++ ++ if (r->http_version >= NGX_HTTP_VERSION_11) { ++ ctx->headers_sent = 1; ++ return ngx_http_send_header(r); ++ } ++ } ++ ++ return NGX_OK; ++} ++ ++ssize_t ++ngx_http_echo_atosz(u_char *line, size_t n) { ++ ssize_t value; ++ ++ if (n == 0) { ++ return NGX_ERROR; ++ } ++ ++ for (value = 0; n--; line++) { ++ if (*line == '_') { /* we ignore undercores */ ++ continue; ++ } ++ ++ if (*line < '0' || *line > '9') { ++ return NGX_ERROR; ++ } ++ ++ value = value * 10 + (*line - '0'); ++ } ++ ++ if (value < 0) { ++ return NGX_ERROR; ++ } else { ++ return value; ++ } ++} ++ ++/* Modified from the ngx_strlcasestrn function in ngx_string.h ++ * Copyright (C) by Igor Sysoev */ ++u_char * ++ngx_http_echo_strlstrn(u_char *s1, u_char *last, u_char *s2, size_t n) ++{ ++ ngx_uint_t c1, c2; ++ ++ c2 = (ngx_uint_t) *s2++; ++ last -= n; ++ ++ do { ++ do { ++ if (s1 >= last) { ++ return NULL; ++ } ++ ++ c1 = (ngx_uint_t) *s1++; ++ ++ } while (c1 != c2); ++ ++ } while (ngx_strncmp(s1, s2, n) != 0); ++ ++ return --s1; ++} ++ ++ ++ngx_int_t ++ngx_http_echo_post_request_at_head(ngx_http_request_t *r, ++ ngx_http_posted_request_t *pr) ++{ ++ dd_enter(); ++ ++ 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 = r->main->posted_requests; ++ r->main->posted_requests = pr; ++ ++ return NGX_OK; ++} ++ +Index: 0.8/modules/nginx-echo/src/ngx_http_echo_util.h +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ 0.8/modules/nginx-echo/src/ngx_http_echo_util.h 2010-10-31 07:35:23.648338001 +0000 +@@ -0,0 +1,30 @@ ++#ifndef NGX_HTTP_ECHO_UTIL_H ++#define NGX_HTTP_ECHO_UTIL_H ++ ++#include "ngx_http_echo_module.h" ++ ++#define ngx_http_echo_strcmp_const(a, b) \ ++ ngx_strncmp(a, b, sizeof(b) - 1) ++ ++ngx_int_t ngx_http_echo_init_ctx(ngx_http_request_t *r, ngx_http_echo_ctx_t **ctx_ptr); ++ ++ngx_int_t ngx_http_echo_eval_cmd_args(ngx_http_request_t *r, ++ ngx_http_echo_cmd_t *cmd, ngx_array_t *computed_args, ++ ngx_array_t *opts); ++ ++ngx_int_t ngx_http_echo_send_header_if_needed(ngx_http_request_t* r, ++ ngx_http_echo_ctx_t *ctx); ++ ++ngx_int_t ngx_http_echo_send_chain_link(ngx_http_request_t* r, ++ ngx_http_echo_ctx_t *ctx, ngx_chain_t *cl); ++ ++ssize_t ngx_http_echo_atosz(u_char *line, size_t n); ++ ++u_char * ngx_http_echo_strlstrn(u_char *s1, u_char *last, u_char *s2, size_t n); ++ ++ngx_int_t ngx_http_echo_post_request_at_head(ngx_http_request_t *r, ++ ngx_http_posted_request_t *pr); ++ ++ ++#endif /* NGX_HTTP_ECHO_UTIL_H */ ++ +Index: 0.8/modules/nginx-echo/src/ngx_http_echo_var.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ 0.8/modules/nginx-echo/src/ngx_http_echo_var.c 2010-10-31 07:35:23.648338001 +0000 +@@ -0,0 +1,98 @@ ++#define DDEBUG 0 ++#include "ddebug.h" ++ ++#include "ngx_http_echo_var.h" ++#include "ngx_http_echo_timer.h" ++#include "ngx_http_echo_request_info.h" ++#include "ngx_http_echo_foreach.h" ++ ++static ngx_int_t ngx_http_echo_incr_variable(ngx_http_request_t *r, ++ ngx_http_variable_value_t *v, uintptr_t data); ++ ++static ngx_http_variable_t ngx_http_echo_variables[] = { ++ { ngx_string("echo_timer_elapsed"), NULL, ++ ngx_http_echo_timer_elapsed_variable, 0, ++ NGX_HTTP_VAR_NOCACHEABLE, 0 }, ++ ++ { ngx_string("echo_request_method"), NULL, ++ ngx_http_echo_request_method_variable, 0, ++ NGX_HTTP_VAR_NOCACHEABLE, 0 }, ++ ++ { ngx_string("echo_cacheable_request_uri"), NULL, ++ ngx_http_echo_cacheable_request_uri_variable, 0, ++ 0, 0 }, ++ ++ { ngx_string("echo_request_uri"), NULL, ++ ngx_http_echo_request_uri_variable, 0, ++ 0, 0 }, ++ ++ { ngx_string("echo_client_request_method"), NULL, ++ ngx_http_echo_client_request_method_variable, 0, ++ NGX_HTTP_VAR_NOCACHEABLE, 0 }, ++ ++ { ngx_string("echo_request_body"), NULL, ++ ngx_http_echo_request_body_variable, 0, ++ NGX_HTTP_VAR_NOCACHEABLE, 0 }, ++ ++ { ngx_string("echo_client_request_headers"), NULL, ++ ngx_http_echo_client_request_headers_variable, 0, ++ NGX_HTTP_VAR_NOCACHEABLE, 0 }, ++ ++ { ngx_string("echo_it"), NULL, ++ ngx_http_echo_it_variable, 0, ++ NGX_HTTP_VAR_NOCACHEABLE, 0 }, ++ ++ { ngx_string("echo_incr"), NULL, ++ ngx_http_echo_incr_variable, 0, ++ NGX_HTTP_VAR_NOCACHEABLE, 0 }, ++ ++ { ngx_string("echo_response_status"), NULL, ++ ngx_http_echo_response_status_variable, 0, ++ NGX_HTTP_VAR_NOCACHEABLE, 0 }, ++ ++ { ngx_null_string, NULL, NULL, 0, 0, 0 } ++}; ++ ++ngx_int_t ++ngx_http_echo_add_variables(ngx_conf_t *cf) { ++ ngx_http_variable_t *var, *v; ++ for (v = ngx_http_echo_variables; 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_echo_incr_variable(ngx_http_request_t *r, ++ ngx_http_variable_value_t *v, uintptr_t data) { ++ ngx_http_echo_ctx_t *ctx; ++ u_char *p; ++ ++ ctx = ngx_http_get_module_ctx(r->main, ngx_http_echo_module); ++ ++ if (ctx == NULL) { ++ return NGX_ERROR; ++ } ++ ++ ctx->counter++; ++ ++ p = ngx_palloc(r->pool, NGX_INT_T_LEN); ++ if (p == NULL) { ++ return NGX_ERROR; ++ } ++ ++ v->len = ngx_sprintf(p, "%ui", ctx->counter) - p; ++ v->data = p; ++ ++ v->valid = 1; ++ v->not_found = 0; ++ v->no_cacheable = 1; ++ ++ return NGX_OK; ++} ++ +Index: 0.8/modules/nginx-echo/src/ngx_http_echo_var.h +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ 0.8/modules/nginx-echo/src/ngx_http_echo_var.h 2010-10-31 07:35:23.648338001 +0000 +@@ -0,0 +1,9 @@ ++#ifndef ECHO_VAR_H ++#define ECHO_VAR_H ++ ++#include "ngx_http_echo_module.h" ++ ++ngx_int_t ngx_http_echo_add_variables(ngx_conf_t *cf); ++ ++#endif /* ECHO_VAR_H */ ++ +Index: 0.8/modules/nginx-echo/test/inc/Module/AutoInstall.pm +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ 0.8/modules/nginx-echo/test/inc/Module/AutoInstall.pm 2010-10-31 07:35:47.718338000 +0000 +@@ -0,0 +1,820 @@ ++#line 1 ++package Module::AutoInstall; ++ ++use strict; ++use Cwd (); ++use ExtUtils::MakeMaker (); ++ ++use vars qw{$VERSION}; ++BEGIN { ++ $VERSION = '1.03'; ++} ++ ++# special map on pre-defined feature sets ++my %FeatureMap = ( ++ '' => 'Core Features', # XXX: deprecated ++ '-core' => 'Core Features', ++); ++ ++# various lexical flags ++my ( @Missing, @Existing, %DisabledTests, $UnderCPAN, $HasCPANPLUS ); ++my ( ++ $Config, $CheckOnly, $SkipInstall, $AcceptDefault, $TestOnly, $AllDeps ++); ++my ( $PostambleActions, $PostambleUsed ); ++ ++# See if it's a testing or non-interactive session ++_accept_default( $ENV{AUTOMATED_TESTING} or ! -t STDIN ); ++_init(); ++ ++sub _accept_default { ++ $AcceptDefault = shift; ++} ++ ++sub missing_modules { ++ return @Missing; ++} ++ ++sub do_install { ++ __PACKAGE__->install( ++ [ ++ $Config ++ ? ( UNIVERSAL::isa( $Config, 'HASH' ) ? %{$Config} : @{$Config} ) ++ : () ++ ], ++ @Missing, ++ ); ++} ++ ++# initialize various flags, and/or perform install ++sub _init { ++ foreach my $arg ( ++ @ARGV, ++ split( ++ /[\s\t]+/, ++ $ENV{PERL_AUTOINSTALL} || $ENV{PERL_EXTUTILS_AUTOINSTALL} || '' ++ ) ++ ) ++ { ++ if ( $arg =~ /^--config=(.*)$/ ) { ++ $Config = [ split( ',', $1 ) ]; ++ } ++ elsif ( $arg =~ /^--installdeps=(.*)$/ ) { ++ __PACKAGE__->install( $Config, @Missing = split( /,/, $1 ) ); ++ exit 0; ++ } ++ elsif ( $arg =~ /^--default(?:deps)?$/ ) { ++ $AcceptDefault = 1; ++ } ++ elsif ( $arg =~ /^--check(?:deps)?$/ ) { ++ $CheckOnly = 1; ++ } ++ elsif ( $arg =~ /^--skip(?:deps)?$/ ) { ++ $SkipInstall = 1; ++ } ++ elsif ( $arg =~ /^--test(?:only)?$/ ) { ++ $TestOnly = 1; ++ } ++ elsif ( $arg =~ /^--all(?:deps)?$/ ) { ++ $AllDeps = 1; ++ } ++ } ++} ++ ++# overrides MakeMaker's prompt() to automatically accept the default choice ++sub _prompt { ++ goto &ExtUtils::MakeMaker::prompt unless $AcceptDefault; ++ ++ my ( $prompt, $default ) = @_; ++ my $y = ( $default =~ /^[Yy]/ ); ++ ++ print $prompt, ' [', ( $y ? 'Y' : 'y' ), '/', ( $y ? 'n' : 'N' ), '] '; ++ print "$default\n"; ++ return $default; ++} ++ ++# the workhorse ++sub import { ++ my $class = shift; ++ my @args = @_ or return; ++ my $core_all; ++ ++ print "*** $class version " . $class->VERSION . "\n"; ++ print "*** Checking for Perl dependencies...\n"; ++ ++ my $cwd = Cwd::cwd(); ++ ++ $Config = []; ++ ++ my $maxlen = length( ++ ( ++ sort { length($b) <=> length($a) } ++ grep { /^[^\-]/ } ++ map { ++ ref($_) ++ ? ( ( ref($_) eq 'HASH' ) ? keys(%$_) : @{$_} ) ++ : '' ++ } ++ map { +{@args}->{$_} } ++ grep { /^[^\-]/ or /^-core$/i } keys %{ +{@args} } ++ )[0] ++ ); ++ ++ # We want to know if we're under CPAN early to avoid prompting, but ++ # if we aren't going to try and install anything anyway then skip the ++ # check entirely since we don't want to have to load (and configure) ++ # an old CPAN just for a cosmetic message ++ ++ $UnderCPAN = _check_lock(1) unless $SkipInstall; ++ ++ while ( my ( $feature, $modules ) = splice( @args, 0, 2 ) ) { ++ my ( @required, @tests, @skiptests ); ++ my $default = 1; ++ my $conflict = 0; ++ ++ if ( $feature =~ m/^-(\w+)$/ ) { ++ my $option = lc($1); ++ ++ # check for a newer version of myself ++ _update_to( $modules, @_ ) and return if $option eq 'version'; ++ ++ # sets CPAN configuration options ++ $Config = $modules if $option eq 'config'; ++ ++ # promote every features to core status ++ $core_all = ( $modules =~ /^all$/i ) and next ++ if $option eq 'core'; ++ ++ next unless $option eq 'core'; ++ } ++ ++ print "[" . ( $FeatureMap{ lc($feature) } || $feature ) . "]\n"; ++ ++ $modules = [ %{$modules} ] if UNIVERSAL::isa( $modules, 'HASH' ); ++ ++ unshift @$modules, -default => &{ shift(@$modules) } ++ if ( ref( $modules->[0] ) eq 'CODE' ); # XXX: bugward combatability ++ ++ while ( my ( $mod, $arg ) = splice( @$modules, 0, 2 ) ) { ++ if ( $mod =~ m/^-(\w+)$/ ) { ++ my $option = lc($1); ++ ++ $default = $arg if ( $option eq 'default' ); ++ $conflict = $arg if ( $option eq 'conflict' ); ++ @tests = @{$arg} if ( $option eq 'tests' ); ++ @skiptests = @{$arg} if ( $option eq 'skiptests' ); ++ ++ next; ++ } ++ ++ printf( "- %-${maxlen}s ...", $mod ); ++ ++ if ( $arg and $arg =~ /^\D/ ) { ++ unshift @$modules, $arg; ++ $arg = 0; ++ } ++ ++ # XXX: check for conflicts and uninstalls(!) them. ++ my $cur = _load($mod); ++ if (_version_cmp ($cur, $arg) >= 0) ++ { ++ print "loaded. ($cur" . ( $arg ? " >= $arg" : '' ) . ")\n"; ++ push @Existing, $mod => $arg; ++ $DisabledTests{$_} = 1 for map { glob($_) } @skiptests; ++ } ++ else { ++ if (not defined $cur) # indeed missing ++ { ++ print "missing." . ( $arg ? " (would need $arg)" : '' ) . "\n"; ++ } ++ else ++ { ++ # no need to check $arg as _version_cmp ($cur, undef) would satisfy >= above ++ print "too old. ($cur < $arg)\n"; ++ } ++ ++ push @required, $mod => $arg; ++ } ++ } ++ ++ next unless @required; ++ ++ my $mandatory = ( $feature eq '-core' or $core_all ); ++ ++ if ( ++ !$SkipInstall ++ and ( ++ $CheckOnly ++ or ($mandatory and $UnderCPAN) ++ or $AllDeps ++ or _prompt( ++ qq{==> Auto-install the } ++ . ( @required / 2 ) ++ . ( $mandatory ? ' mandatory' : ' optional' ) ++ . qq{ module(s) from CPAN?}, ++ $default ? 'y' : 'n', ++ ) =~ /^[Yy]/ ++ ) ++ ) ++ { ++ push( @Missing, @required ); ++ $DisabledTests{$_} = 1 for map { glob($_) } @skiptests; ++ } ++ ++ elsif ( !$SkipInstall ++ and $default ++ and $mandatory ++ and ++ _prompt( qq{==> The module(s) are mandatory! Really skip?}, 'n', ) ++ =~ /^[Nn]/ ) ++ { ++ push( @Missing, @required ); ++ $DisabledTests{$_} = 1 for map { glob($_) } @skiptests; ++ } ++ ++ else { ++ $DisabledTests{$_} = 1 for map { glob($_) } @tests; ++ } ++ } ++ ++ if ( @Missing and not( $CheckOnly or $UnderCPAN ) ) { ++ require Config; ++ print ++"*** Dependencies will be installed the next time you type '$Config::Config{make}'.\n"; ++ ++ # make an educated guess of whether we'll need root permission. ++ print " (You may need to do that as the 'root' user.)\n" ++ if eval '$>'; ++ } ++ print "*** $class configuration finished.\n"; ++ ++ chdir $cwd; ++ ++ # import to main:: ++ no strict 'refs'; ++ *{'main::WriteMakefile'} = \&Write if caller(0) eq 'main'; ++ ++ return (@Existing, @Missing); ++} ++ ++sub _running_under { ++ my $thing = shift; ++ print <<"END_MESSAGE"; ++*** Since we're running under ${thing}, I'll just let it take care ++ of the dependency's installation later. ++END_MESSAGE ++ return 1; ++} ++ ++# Check to see if we are currently running under CPAN.pm and/or CPANPLUS; ++# if we are, then we simply let it taking care of our dependencies ++sub _check_lock { ++ return unless @Missing or @_; ++ ++ my $cpan_env = $ENV{PERL5_CPAN_IS_RUNNING}; ++ ++ if ($ENV{PERL5_CPANPLUS_IS_RUNNING}) { ++ return _running_under($cpan_env ? 'CPAN' : 'CPANPLUS'); ++ } ++ ++ require CPAN; ++ ++ if ($CPAN::VERSION > '1.89') { ++ if ($cpan_env) { ++ return _running_under('CPAN'); ++ } ++ return; # CPAN.pm new enough, don't need to check further ++ } ++ ++ # last ditch attempt, this -will- configure CPAN, very sorry ++ ++ _load_cpan(1); # force initialize even though it's already loaded ++ ++ # Find the CPAN lock-file ++ my $lock = MM->catfile( $CPAN::Config->{cpan_home}, ".lock" ); ++ return unless -f $lock; ++ ++ # Check the lock ++ local *LOCK; ++ return unless open(LOCK, $lock); ++ ++ if ( ++ ( $^O eq 'MSWin32' ? _under_cpan() : == getppid() ) ++ and ( $CPAN::Config->{prerequisites_policy} || '' ) ne 'ignore' ++ ) { ++ print <<'END_MESSAGE'; ++ ++*** Since we're running under CPAN, I'll just let it take care ++ of the dependency's installation later. ++END_MESSAGE ++ return 1; ++ } ++ ++ close LOCK; ++ return; ++} ++ ++sub install { ++ my $class = shift; ++ ++ my $i; # used below to strip leading '-' from config keys ++ my @config = ( map { s/^-// if ++$i; $_ } @{ +shift } ); ++ ++ my ( @modules, @installed ); ++ while ( my ( $pkg, $ver ) = splice( @_, 0, 2 ) ) { ++ ++ # grep out those already installed ++ if ( _version_cmp( _load($pkg), $ver ) >= 0 ) { ++ push @installed, $pkg; ++ } ++ else { ++ push @modules, $pkg, $ver; ++ } ++ } ++ ++ return @installed unless @modules; # nothing to do ++ return @installed if _check_lock(); # defer to the CPAN shell ++ ++ print "*** Installing dependencies...\n"; ++ ++ return unless _connected_to('cpan.org'); ++ ++ my %args = @config; ++ my %failed; ++ local *FAILED; ++ if ( $args{do_once} and open( FAILED, '.#autoinstall.failed' ) ) { ++ while () { chomp; $failed{$_}++ } ++ close FAILED; ++ ++ my @newmod; ++ while ( my ( $k, $v ) = splice( @modules, 0, 2 ) ) { ++ push @newmod, ( $k => $v ) unless $failed{$k}; ++ } ++ @modules = @newmod; ++ } ++ ++ if ( _has_cpanplus() and not $ENV{PERL_AUTOINSTALL_PREFER_CPAN} ) { ++ _install_cpanplus( \@modules, \@config ); ++ } else { ++ _install_cpan( \@modules, \@config ); ++ } ++ ++ print "*** $class installation finished.\n"; ++ ++ # see if we have successfully installed them ++ while ( my ( $pkg, $ver ) = splice( @modules, 0, 2 ) ) { ++ if ( _version_cmp( _load($pkg), $ver ) >= 0 ) { ++ push @installed, $pkg; ++ } ++ elsif ( $args{do_once} and open( FAILED, '>> .#autoinstall.failed' ) ) { ++ print FAILED "$pkg\n"; ++ } ++ } ++ ++ close FAILED if $args{do_once}; ++ ++ return @installed; ++} ++ ++sub _install_cpanplus { ++ my @modules = @{ +shift }; ++ my @config = _cpanplus_config( @{ +shift } ); ++ my $installed = 0; ++ ++ require CPANPLUS::Backend; ++ my $cp = CPANPLUS::Backend->new; ++ my $conf = $cp->configure_object; ++ ++ return unless $conf->can('conf') # 0.05x+ with "sudo" support ++ or _can_write($conf->_get_build('base')); # 0.04x ++ ++ # if we're root, set UNINST=1 to avoid trouble unless user asked for it. ++ my $makeflags = $conf->get_conf('makeflags') || ''; ++ if ( UNIVERSAL::isa( $makeflags, 'HASH' ) ) { ++ # 0.03+ uses a hashref here ++ $makeflags->{UNINST} = 1 unless exists $makeflags->{UNINST}; ++ ++ } else { ++ # 0.02 and below uses a scalar ++ $makeflags = join( ' ', split( ' ', $makeflags ), 'UNINST=1' ) ++ if ( $makeflags !~ /\bUNINST\b/ and eval qq{ $> eq '0' } ); ++ ++ } ++ $conf->set_conf( makeflags => $makeflags ); ++ $conf->set_conf( prereqs => 1 ); ++ ++ ++ ++ while ( my ( $key, $val ) = splice( @config, 0, 2 ) ) { ++ $conf->set_conf( $key, $val ); ++ } ++ ++ my $modtree = $cp->module_tree; ++ while ( my ( $pkg, $ver ) = splice( @modules, 0, 2 ) ) { ++ print "*** Installing $pkg...\n"; ++ ++ MY::preinstall( $pkg, $ver ) or next if defined &MY::preinstall; ++ ++ my $success; ++ my $obj = $modtree->{$pkg}; ++ ++ if ( $obj and _version_cmp( $obj->{version}, $ver ) >= 0 ) { ++ my $pathname = $pkg; ++ $pathname =~ s/::/\\W/; ++ ++ foreach my $inc ( grep { m/$pathname.pm/i } keys(%INC) ) { ++ delete $INC{$inc}; ++ } ++ ++ my $rv = $cp->install( modules => [ $obj->{module} ] ); ++ ++ if ( $rv and ( $rv->{ $obj->{module} } or $rv->{ok} ) ) { ++ print "*** $pkg successfully installed.\n"; ++ $success = 1; ++ } else { ++ print "*** $pkg installation cancelled.\n"; ++ $success = 0; ++ } ++ ++ $installed += $success; ++ } else { ++ print << "."; ++*** Could not find a version $ver or above for $pkg; skipping. ++. ++ } ++ ++ MY::postinstall( $pkg, $ver, $success ) if defined &MY::postinstall; ++ } ++ ++ return $installed; ++} ++ ++sub _cpanplus_config { ++ my @config = (); ++ while ( @_ ) { ++ my ($key, $value) = (shift(), shift()); ++ if ( $key eq 'prerequisites_policy' ) { ++ if ( $value eq 'follow' ) { ++ $value = CPANPLUS::Internals::Constants::PREREQ_INSTALL(); ++ } elsif ( $value eq 'ask' ) { ++ $value = CPANPLUS::Internals::Constants::PREREQ_ASK(); ++ } elsif ( $value eq 'ignore' ) { ++ $value = CPANPLUS::Internals::Constants::PREREQ_IGNORE(); ++ } else { ++ die "*** Cannot convert option $key = '$value' to CPANPLUS version.\n"; ++ } ++ } else { ++ die "*** Cannot convert option $key to CPANPLUS version.\n"; ++ } ++ } ++ return @config; ++} ++ ++sub _install_cpan { ++ my @modules = @{ +shift }; ++ my @config = @{ +shift }; ++ my $installed = 0; ++ my %args; ++ ++ _load_cpan(); ++ require Config; ++ ++ if (CPAN->VERSION < 1.80) { ++ # no "sudo" support, probe for writableness ++ return unless _can_write( MM->catfile( $CPAN::Config->{cpan_home}, 'sources' ) ) ++ and _can_write( $Config::Config{sitelib} ); ++ } ++ ++ # if we're root, set UNINST=1 to avoid trouble unless user asked for it. ++ my $makeflags = $CPAN::Config->{make_install_arg} || ''; ++ $CPAN::Config->{make_install_arg} = ++ join( ' ', split( ' ', $makeflags ), 'UNINST=1' ) ++ if ( $makeflags !~ /\bUNINST\b/ and eval qq{ $> eq '0' } ); ++ ++ # don't show start-up info ++ $CPAN::Config->{inhibit_startup_message} = 1; ++ ++ # set additional options ++ while ( my ( $opt, $arg ) = splice( @config, 0, 2 ) ) { ++ ( $args{$opt} = $arg, next ) ++ if $opt =~ /^force$/; # pseudo-option ++ $CPAN::Config->{$opt} = $arg; ++ } ++ ++ local $CPAN::Config->{prerequisites_policy} = 'follow'; ++ ++ while ( my ( $pkg, $ver ) = splice( @modules, 0, 2 ) ) { ++ MY::preinstall( $pkg, $ver ) or next if defined &MY::preinstall; ++ ++ print "*** Installing $pkg...\n"; ++ ++ my $obj = CPAN::Shell->expand( Module => $pkg ); ++ my $success = 0; ++ ++ if ( $obj and _version_cmp( $obj->cpan_version, $ver ) >= 0 ) { ++ my $pathname = $pkg; ++ $pathname =~ s/::/\\W/; ++ ++ foreach my $inc ( grep { m/$pathname.pm/i } keys(%INC) ) { ++ delete $INC{$inc}; ++ } ++ ++ my $rv = $args{force} ? CPAN::Shell->force( install => $pkg ) ++ : CPAN::Shell->install($pkg); ++ $rv ||= eval { ++ $CPAN::META->instance( 'CPAN::Distribution', $obj->cpan_file, ) ++ ->{install} ++ if $CPAN::META; ++ }; ++ ++ if ( $rv eq 'YES' ) { ++ print "*** $pkg successfully installed.\n"; ++ $success = 1; ++ } ++ else { ++ print "*** $pkg installation failed.\n"; ++ $success = 0; ++ } ++ ++ $installed += $success; ++ } ++ else { ++ print << "."; ++*** Could not find a version $ver or above for $pkg; skipping. ++. ++ } ++ ++ MY::postinstall( $pkg, $ver, $success ) if defined &MY::postinstall; ++ } ++ ++ return $installed; ++} ++ ++sub _has_cpanplus { ++ return ( ++ $HasCPANPLUS = ( ++ $INC{'CPANPLUS/Config.pm'} ++ or _load('CPANPLUS::Shell::Default') ++ ) ++ ); ++} ++ ++# make guesses on whether we're under the CPAN installation directory ++sub _under_cpan { ++ require Cwd; ++ require File::Spec; ++ ++ my $cwd = File::Spec->canonpath( Cwd::cwd() ); ++ my $cpan = File::Spec->canonpath( $CPAN::Config->{cpan_home} ); ++ ++ return ( index( $cwd, $cpan ) > -1 ); ++} ++ ++sub _update_to { ++ my $class = __PACKAGE__; ++ my $ver = shift; ++ ++ return ++ if _version_cmp( _load($class), $ver ) >= 0; # no need to upgrade ++ ++ if ( ++ _prompt( "==> A newer version of $class ($ver) is required. Install?", ++ 'y' ) =~ /^[Nn]/ ++ ) ++ { ++ die "*** Please install $class $ver manually.\n"; ++ } ++ ++ print << "."; ++*** Trying to fetch it from CPAN... ++. ++ ++ # install ourselves ++ _load($class) and return $class->import(@_) ++ if $class->install( [], $class, $ver ); ++ ++ print << '.'; exit 1; ++ ++*** Cannot bootstrap myself. :-( Installation terminated. ++. ++} ++ ++# check if we're connected to some host, using inet_aton ++sub _connected_to { ++ my $site = shift; ++ ++ return ( ++ ( _load('Socket') and Socket::inet_aton($site) ) or _prompt( ++ qq( ++*** Your host cannot resolve the domain name '$site', which ++ probably means the Internet connections are unavailable. ++==> Should we try to install the required module(s) anyway?), 'n' ++ ) =~ /^[Yy]/ ++ ); ++} ++ ++# check if a directory is writable; may create it on demand ++sub _can_write { ++ my $path = shift; ++ mkdir( $path, 0755 ) unless -e $path; ++ ++ return 1 if -w $path; ++ ++ print << "."; ++*** You are not allowed to write to the directory '$path'; ++ the installation may fail due to insufficient permissions. ++. ++ ++ if ( ++ eval '$>' and lc(`sudo -V`) =~ /version/ and _prompt( ++ qq( ++==> Should we try to re-execute the autoinstall process with 'sudo'?), ++ ((-t STDIN) ? 'y' : 'n') ++ ) =~ /^[Yy]/ ++ ) ++ { ++ ++ # try to bootstrap ourselves from sudo ++ print << "."; ++*** Trying to re-execute the autoinstall process with 'sudo'... ++. ++ my $missing = join( ',', @Missing ); ++ my $config = join( ',', ++ UNIVERSAL::isa( $Config, 'HASH' ) ? %{$Config} : @{$Config} ) ++ if $Config; ++ ++ return ++ unless system( 'sudo', $^X, $0, "--config=$config", ++ "--installdeps=$missing" ); ++ ++ print << "."; ++*** The 'sudo' command exited with error! Resuming... ++. ++ } ++ ++ return _prompt( ++ qq( ++==> Should we try to install the required module(s) anyway?), 'n' ++ ) =~ /^[Yy]/; ++} ++ ++# load a module and return the version it reports ++sub _load { ++ my $mod = pop; # class/instance doesn't matter ++ my $file = $mod; ++ ++ $file =~ s|::|/|g; ++ $file .= '.pm'; ++ ++ local $@; ++ return eval { require $file; $mod->VERSION } || ( $@ ? undef: 0 ); ++} ++ ++# Load CPAN.pm and it's configuration ++sub _load_cpan { ++ return if $CPAN::VERSION and $CPAN::Config and not @_; ++ require CPAN; ++ ++ # CPAN-1.82+ adds CPAN::Config::AUTOLOAD to redirect to ++ # CPAN::HandleConfig->load. CPAN reports that the redirection ++ # is deprecated in a warning printed at the user. ++ ++ # CPAN-1.81 expects CPAN::HandleConfig->load, does not have ++ # $CPAN::HandleConfig::VERSION but cannot handle ++ # CPAN::Config->load ++ ++ # Which "versions expect CPAN::Config->load? ++ ++ if ( $CPAN::HandleConfig::VERSION ++ || CPAN::HandleConfig->can('load') ++ ) { ++ # Newer versions of CPAN have a HandleConfig module ++ CPAN::HandleConfig->load; ++ } else { ++ # Older versions had the load method in Config directly ++ CPAN::Config->load; ++ } ++} ++ ++# compare two versions, either use Sort::Versions or plain comparison ++# return values same as <=> ++sub _version_cmp { ++ my ( $cur, $min ) = @_; ++ return -1 unless defined $cur; # if 0 keep comparing ++ return 1 unless $min; ++ ++ $cur =~ s/\s+$//; ++ ++ # check for version numbers that are not in decimal format ++ if ( ref($cur) or ref($min) or $cur =~ /v|\..*\./ or $min =~ /v|\..*\./ ) { ++ if ( ( $version::VERSION or defined( _load('version') )) and ++ version->can('new') ++ ) { ++ ++ # use version.pm if it is installed. ++ return version->new($cur) <=> version->new($min); ++ } ++ elsif ( $Sort::Versions::VERSION or defined( _load('Sort::Versions') ) ) ++ { ++ ++ # use Sort::Versions as the sorting algorithm for a.b.c versions ++ return Sort::Versions::versioncmp( $cur, $min ); ++ } ++ ++ warn "Cannot reliably compare non-decimal formatted versions.\n" ++ . "Please install version.pm or Sort::Versions.\n"; ++ } ++ ++ # plain comparison ++ local $^W = 0; # shuts off 'not numeric' bugs ++ return $cur <=> $min; ++} ++ ++# nothing; this usage is deprecated. ++sub main::PREREQ_PM { return {}; } ++ ++sub _make_args { ++ my %args = @_; ++ ++ $args{PREREQ_PM} = { %{ $args{PREREQ_PM} || {} }, @Existing, @Missing } ++ if $UnderCPAN or $TestOnly; ++ ++ if ( $args{EXE_FILES} and -e 'MANIFEST' ) { ++ require ExtUtils::Manifest; ++ my $manifest = ExtUtils::Manifest::maniread('MANIFEST'); ++ ++ $args{EXE_FILES} = ++ [ grep { exists $manifest->{$_} } @{ $args{EXE_FILES} } ]; ++ } ++ ++ $args{test}{TESTS} ||= 't/*.t'; ++ $args{test}{TESTS} = join( ' ', ++ grep { !exists( $DisabledTests{$_} ) } ++ map { glob($_) } split( /\s+/, $args{test}{TESTS} ) ); ++ ++ my $missing = join( ',', @Missing ); ++ my $config = ++ join( ',', UNIVERSAL::isa( $Config, 'HASH' ) ? %{$Config} : @{$Config} ) ++ if $Config; ++ ++ $PostambleActions = ( ++ ($missing and not $UnderCPAN) ++ ? "\$(PERL) $0 --config=$config --installdeps=$missing" ++ : "\$(NOECHO) \$(NOOP)" ++ ); ++ ++ return %args; ++} ++ ++# a wrapper to ExtUtils::MakeMaker::WriteMakefile ++sub Write { ++ require Carp; ++ Carp::croak "WriteMakefile: Need even number of args" if @_ % 2; ++ ++ if ($CheckOnly) { ++ print << "."; ++*** Makefile not written in check-only mode. ++. ++ return; ++ } ++ ++ my %args = _make_args(@_); ++ ++ no strict 'refs'; ++ ++ $PostambleUsed = 0; ++ local *MY::postamble = \&postamble unless defined &MY::postamble; ++ ExtUtils::MakeMaker::WriteMakefile(%args); ++ ++ print << "." unless $PostambleUsed; ++*** WARNING: Makefile written with customized MY::postamble() without ++ including contents from Module::AutoInstall::postamble() -- ++ auto installation features disabled. Please contact the author. ++. ++ ++ return 1; ++} ++ ++sub postamble { ++ $PostambleUsed = 1; ++ ++ return <<"END_MAKE"; ++ ++config :: installdeps ++\t\$(NOECHO) \$(NOOP) ++ ++checkdeps :: ++\t\$(PERL) $0 --checkdeps ++ ++installdeps :: ++\t$PostambleActions ++ ++END_MAKE ++ ++} ++ ++1; ++ ++__END__ ++ ++#line 1071 +Index: 0.8/modules/nginx-echo/test/inc/Module/Install.pm +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ 0.8/modules/nginx-echo/test/inc/Module/Install.pm 2010-10-31 07:35:23.648338001 +0000 +@@ -0,0 +1,466 @@ ++#line 1 ++package Module::Install; ++ ++# For any maintainers: ++# The load order for Module::Install is a bit magic. ++# It goes something like this... ++# ++# IF ( host has Module::Install installed, creating author mode ) { ++# 1. Makefile.PL calls "use inc::Module::Install" ++# 2. $INC{inc/Module/Install.pm} set to installed version of inc::Module::Install ++# 3. The installed version of inc::Module::Install loads ++# 4. inc::Module::Install calls "require Module::Install" ++# 5. The ./inc/ version of Module::Install loads ++# } ELSE { ++# 1. Makefile.PL calls "use inc::Module::Install" ++# 2. $INC{inc/Module/Install.pm} set to ./inc/ version of Module::Install ++# 3. The ./inc/ version of Module::Install loads ++# } ++ ++use 5.005; ++use strict 'vars'; ++use Cwd (); ++use File::Find (); ++use File::Path (); ++ ++use vars qw{$VERSION $MAIN}; ++BEGIN { ++ # All Module::Install core packages now require synchronised versions. ++ # This will be used to ensure we don't accidentally load old or ++ # different versions of modules. ++ # This is not enforced yet, but will be some time in the next few ++ # releases once we can make sure it won't clash with custom ++ # Module::Install extensions. ++ $VERSION = '0.99'; ++ ++ # Storage for the pseudo-singleton ++ $MAIN = undef; ++ ++ *inc::Module::Install::VERSION = *VERSION; ++ @inc::Module::Install::ISA = __PACKAGE__; ++ ++} ++ ++sub import { ++ my $class = shift; ++ my $self = $class->new(@_); ++ my $who = $self->_caller; ++ ++ #------------------------------------------------------------- ++ # all of the following checks should be included in import(), ++ # to allow "eval 'require Module::Install; 1' to test ++ # installation of Module::Install. (RT #51267) ++ #------------------------------------------------------------- ++ ++ # Whether or not inc::Module::Install is actually loaded, the ++ # $INC{inc/Module/Install.pm} is what will still get set as long as ++ # the caller loaded module this in the documented manner. ++ # If not set, the caller may NOT have loaded the bundled version, and thus ++ # they may not have a MI version that works with the Makefile.PL. This would ++ # result in false errors or unexpected behaviour. And we don't want that. ++ my $file = join( '/', 'inc', split /::/, __PACKAGE__ ) . '.pm'; ++ unless ( $INC{$file} ) { die <<"END_DIE" } ++ ++Please invoke ${\__PACKAGE__} with: ++ ++ use inc::${\__PACKAGE__}; ++ ++not: ++ ++ use ${\__PACKAGE__}; ++ ++END_DIE ++ ++ # This reportedly fixes a rare Win32 UTC file time issue, but ++ # as this is a non-cross-platform XS module not in the core, ++ # we shouldn't really depend on it. See RT #24194 for detail. ++ # (Also, this module only supports Perl 5.6 and above). ++ eval "use Win32::UTCFileTime" if $^O eq 'MSWin32' && $] >= 5.006; ++ ++ # If the script that is loading Module::Install is from the future, ++ # then make will detect this and cause it to re-run over and over ++ # again. This is bad. Rather than taking action to touch it (which ++ # is unreliable on some platforms and requires write permissions) ++ # for now we should catch this and refuse to run. ++ if ( -f $0 ) { ++ my $s = (stat($0))[9]; ++ ++ # If the modification time is only slightly in the future, ++ # sleep briefly to remove the problem. ++ my $a = $s - time; ++ if ( $a > 0 and $a < 5 ) { sleep 5 } ++ ++ # Too far in the future, throw an error. ++ my $t = time; ++ if ( $s > $t ) { die <<"END_DIE" } ++ ++Your installer $0 has a modification time in the future ($s > $t). ++ ++This is known to create infinite loops in make. ++ ++Please correct this, then run $0 again. ++ ++END_DIE ++ } ++ ++ ++ # Build.PL was formerly supported, but no longer is due to excessive ++ # difficulty in implementing every single feature twice. ++ if ( $0 =~ /Build.PL$/i ) { die <<"END_DIE" } ++ ++Module::Install no longer supports Build.PL. ++ ++It was impossible to maintain duel backends, and has been deprecated. ++ ++Please remove all Build.PL files and only use the Makefile.PL installer. ++ ++END_DIE ++ ++ #------------------------------------------------------------- ++ ++ # To save some more typing in Module::Install installers, every... ++ # use inc::Module::Install ++ # ...also acts as an implicit use strict. ++ $^H |= strict::bits(qw(refs subs vars)); ++ ++ #------------------------------------------------------------- ++ ++ unless ( -f $self->{file} ) { ++ foreach my $key (keys %INC) { ++ delete $INC{$key} if $key =~ /Module\/Install/; ++ } ++ ++ local $^W; ++ require "$self->{path}/$self->{dispatch}.pm"; ++ File::Path::mkpath("$self->{prefix}/$self->{author}"); ++ $self->{admin} = "$self->{name}::$self->{dispatch}"->new( _top => $self ); ++ $self->{admin}->init; ++ @_ = ($class, _self => $self); ++ goto &{"$self->{name}::import"}; ++ } ++ ++ local $^W; ++ *{"${who}::AUTOLOAD"} = $self->autoload; ++ $self->preload; ++ ++ # Unregister loader and worker packages so subdirs can use them again ++ delete $INC{'inc/Module/Install.pm'}; ++ delete $INC{'Module/Install.pm'}; ++ ++ # Save to the singleton ++ $MAIN = $self; ++ ++ return 1; ++} ++ ++sub autoload { ++ my $self = shift; ++ my $who = $self->_caller; ++ my $cwd = Cwd::cwd(); ++ my $sym = "${who}::AUTOLOAD"; ++ $sym->{$cwd} = sub { ++ my $pwd = Cwd::cwd(); ++ if ( my $code = $sym->{$pwd} ) { ++ # Delegate back to parent dirs ++ goto &$code unless $cwd eq $pwd; ++ } ++ unless ($$sym =~ s/([^:]+)$//) { ++ # XXX: it looks like we can't retrieve the missing function ++ # via $$sym (usually $main::AUTOLOAD) in this case. ++ # I'm still wondering if we should slurp Makefile.PL to ++ # get some context or not ... ++ my ($package, $file, $line) = caller; ++ die <<"EOT"; ++Unknown function is found at $file line $line. ++Execution of $file aborted due to runtime errors. ++ ++If you're a contributor to a project, you may need to install ++some Module::Install extensions from CPAN (or other repository). ++If you're a user of a module, please contact the author. ++EOT ++ } ++ my $method = $1; ++ if ( uc($method) eq $method ) { ++ # Do nothing ++ return; ++ } elsif ( $method =~ /^_/ and $self->can($method) ) { ++ # Dispatch to the root M:I class ++ return $self->$method(@_); ++ } ++ ++ # Dispatch to the appropriate plugin ++ unshift @_, ( $self, $1 ); ++ goto &{$self->can('call')}; ++ }; ++} ++ ++sub preload { ++ my $self = shift; ++ unless ( $self->{extensions} ) { ++ $self->load_extensions( ++ "$self->{prefix}/$self->{path}", $self ++ ); ++ } ++ ++ my @exts = @{$self->{extensions}}; ++ unless ( @exts ) { ++ @exts = $self->{admin}->load_all_extensions; ++ } ++ ++ my %seen; ++ foreach my $obj ( @exts ) { ++ while (my ($method, $glob) = each %{ref($obj) . '::'}) { ++ next unless $obj->can($method); ++ next if $method =~ /^_/; ++ next if $method eq uc($method); ++ $seen{$method}++; ++ } ++ } ++ ++ my $who = $self->_caller; ++ foreach my $name ( sort keys %seen ) { ++ local $^W; ++ *{"${who}::$name"} = sub { ++ ${"${who}::AUTOLOAD"} = "${who}::$name"; ++ goto &{"${who}::AUTOLOAD"}; ++ }; ++ } ++} ++ ++sub new { ++ my ($class, %args) = @_; ++ ++ delete $INC{'FindBin.pm'}; ++ require FindBin; ++ ++ # ignore the prefix on extension modules built from top level. ++ my $base_path = Cwd::abs_path($FindBin::Bin); ++ unless ( Cwd::abs_path(Cwd::cwd()) eq $base_path ) { ++ delete $args{prefix}; ++ } ++ return $args{_self} if $args{_self}; ++ ++ $args{dispatch} ||= 'Admin'; ++ $args{prefix} ||= 'inc'; ++ $args{author} ||= ($^O eq 'VMS' ? '_author' : '.author'); ++ $args{bundle} ||= 'inc/BUNDLES'; ++ $args{base} ||= $base_path; ++ $class =~ s/^\Q$args{prefix}\E:://; ++ $args{name} ||= $class; ++ $args{version} ||= $class->VERSION; ++ unless ( $args{path} ) { ++ $args{path} = $args{name}; ++ $args{path} =~ s!::!/!g; ++ } ++ $args{file} ||= "$args{base}/$args{prefix}/$args{path}.pm"; ++ $args{wrote} = 0; ++ ++ bless( \%args, $class ); ++} ++ ++sub call { ++ my ($self, $method) = @_; ++ my $obj = $self->load($method) or return; ++ splice(@_, 0, 2, $obj); ++ goto &{$obj->can($method)}; ++} ++ ++sub load { ++ my ($self, $method) = @_; ++ ++ $self->load_extensions( ++ "$self->{prefix}/$self->{path}", $self ++ ) unless $self->{extensions}; ++ ++ foreach my $obj (@{$self->{extensions}}) { ++ return $obj if $obj->can($method); ++ } ++ ++ my $admin = $self->{admin} or die <<"END_DIE"; ++The '$method' method does not exist in the '$self->{prefix}' path! ++Please remove the '$self->{prefix}' directory and run $0 again to load it. ++END_DIE ++ ++ my $obj = $admin->load($method, 1); ++ push @{$self->{extensions}}, $obj; ++ ++ $obj; ++} ++ ++sub load_extensions { ++ my ($self, $path, $top) = @_; ++ ++ my $should_reload = 0; ++ unless ( grep { ! ref $_ and lc $_ eq lc $self->{prefix} } @INC ) { ++ unshift @INC, $self->{prefix}; ++ $should_reload = 1; ++ } ++ ++ foreach my $rv ( $self->find_extensions($path) ) { ++ my ($file, $pkg) = @{$rv}; ++ next if $self->{pathnames}{$pkg}; ++ ++ local $@; ++ my $new = eval { local $^W; require $file; $pkg->can('new') }; ++ unless ( $new ) { ++ warn $@ if $@; ++ next; ++ } ++ $self->{pathnames}{$pkg} = ++ $should_reload ? delete $INC{$file} : $INC{$file}; ++ push @{$self->{extensions}}, &{$new}($pkg, _top => $top ); ++ } ++ ++ $self->{extensions} ||= []; ++} ++ ++sub find_extensions { ++ my ($self, $path) = @_; ++ ++ my @found; ++ File::Find::find( sub { ++ my $file = $File::Find::name; ++ return unless $file =~ m!^\Q$path\E/(.+)\.pm\Z!is; ++ my $subpath = $1; ++ return if lc($subpath) eq lc($self->{dispatch}); ++ ++ $file = "$self->{path}/$subpath.pm"; ++ my $pkg = "$self->{name}::$subpath"; ++ $pkg =~ s!/!::!g; ++ ++ # If we have a mixed-case package name, assume case has been preserved ++ # correctly. Otherwise, root through the file to locate the case-preserved ++ # version of the package name. ++ if ( $subpath eq lc($subpath) || $subpath eq uc($subpath) ) { ++ my $content = Module::Install::_read($subpath . '.pm'); ++ my $in_pod = 0; ++ foreach ( split //, $content ) { ++ $in_pod = 1 if /^=\w/; ++ $in_pod = 0 if /^=cut/; ++ next if ($in_pod || /^=cut/); # skip pod text ++ next if /^\s*#/; # and comments ++ if ( m/^\s*package\s+($pkg)\s*;/i ) { ++ $pkg = $1; ++ last; ++ } ++ } ++ } ++ ++ push @found, [ $file, $pkg ]; ++ }, $path ) if -d $path; ++ ++ @found; ++} ++ ++ ++ ++ ++ ++##################################################################### ++# Common Utility Functions ++ ++sub _caller { ++ my $depth = 0; ++ my $call = caller($depth); ++ while ( $call eq __PACKAGE__ ) { ++ $depth++; ++ $call = caller($depth); ++ } ++ return $call; ++} ++ ++# Done in evals to avoid confusing Perl::MinimumVersion ++eval( $] >= 5.006 ? <<'END_NEW' : <<'END_OLD' ); die $@ if $@; ++sub _read { ++ local *FH; ++ open( FH, '<', $_[0] ) or die "open($_[0]): $!"; ++ my $string = do { local $/; }; ++ close FH or die "close($_[0]): $!"; ++ return $string; ++} ++END_NEW ++sub _read { ++ local *FH; ++ open( FH, "< $_[0]" ) or die "open($_[0]): $!"; ++ my $string = do { local $/; }; ++ close FH or die "close($_[0]): $!"; ++ return $string; ++} ++END_OLD ++ ++sub _readperl { ++ my $string = Module::Install::_read($_[0]); ++ $string =~ s/(?:\015{1,2}\012|\015|\012)/\n/sg; ++ $string =~ s/(\n)\n*__(?:DATA|END)__\b.*\z/$1/s; ++ $string =~ s/\n\n=\w+.+?\n\n=cut\b.+?\n+/\n\n/sg; ++ return $string; ++} ++ ++sub _readpod { ++ my $string = Module::Install::_read($_[0]); ++ $string =~ s/(?:\015{1,2}\012|\015|\012)/\n/sg; ++ return $string if $_[0] =~ /\.pod\z/; ++ $string =~ s/(^|\n=cut\b.+?\n+)[^=\s].+?\n(\n=\w+|\z)/$1$2/sg; ++ $string =~ s/\n*=pod\b[^\n]*\n+/\n\n/sg; ++ $string =~ s/\n*=cut\b[^\n]*\n+/\n\n/sg; ++ $string =~ s/^\n+//s; ++ return $string; ++} ++ ++# Done in evals to avoid confusing Perl::MinimumVersion ++eval( $] >= 5.006 ? <<'END_NEW' : <<'END_OLD' ); die $@ if $@; ++sub _write { ++ local *FH; ++ open( FH, '>', $_[0] ) or die "open($_[0]): $!"; ++ foreach ( 1 .. $#_ ) { ++ print FH $_[$_] or die "print($_[0]): $!"; ++ } ++ close FH or die "close($_[0]): $!"; ++} ++END_NEW ++sub _write { ++ local *FH; ++ open( FH, "> $_[0]" ) or die "open($_[0]): $!"; ++ foreach ( 1 .. $#_ ) { ++ print FH $_[$_] or die "print($_[0]): $!"; ++ } ++ close FH or die "close($_[0]): $!"; ++} ++END_OLD ++ ++# _version is for processing module versions (eg, 1.03_05) not ++# Perl versions (eg, 5.8.1). ++sub _version ($) { ++ my $s = shift || 0; ++ my $d =()= $s =~ /(\.)/g; ++ if ( $d >= 2 ) { ++ # Normalise multipart versions ++ $s =~ s/(\.)(\d{1,3})/sprintf("$1%03d",$2)/eg; ++ } ++ $s =~ s/^(\d+)\.?//; ++ my $l = $1 || 0; ++ my @v = map { ++ $_ . '0' x (3 - length $_) ++ } $s =~ /(\d{1,3})\D?/g; ++ $l = $l . '.' . join '', @v if @v; ++ return $l + 0; ++} ++ ++sub _cmp ($$) { ++ _version($_[0]) <=> _version($_[1]); ++} ++ ++# Cloned from Params::Util::_CLASS ++sub _CLASS ($) { ++ ( ++ defined $_[0] ++ and ++ ! ref $_[0] ++ and ++ $_[0] =~ m/^[^\W\d]\w*(?:::\w+)*\z/s ++ ) ? $_[0] : undef; ++} ++ ++1; ++ ++# Copyright 2008 - 2010 Adam Kennedy. +Index: 0.8/modules/nginx-echo/test/inc/Spiffy.pm +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ 0.8/modules/nginx-echo/test/inc/Spiffy.pm 2010-10-31 07:35:47.698338000 +0000 +@@ -0,0 +1,539 @@ ++#line 1 ++package Spiffy; ++use strict; ++use 5.006001; ++use warnings; ++use Carp; ++require Exporter; ++our $VERSION = '0.30'; ++our @EXPORT = (); ++our @EXPORT_BASE = qw(field const stub super); ++our @EXPORT_OK = (@EXPORT_BASE, qw(id WWW XXX YYY ZZZ)); ++our %EXPORT_TAGS = (XXX => [qw(WWW XXX YYY ZZZ)]); ++ ++my $stack_frame = 0; ++my $dump = 'yaml'; ++my $bases_map = {}; ++ ++sub WWW; sub XXX; sub YYY; sub ZZZ; ++ ++# This line is here to convince "autouse" into believing we are autousable. ++sub can { ++ ($_[1] eq 'import' and caller()->isa('autouse')) ++ ? \&Exporter::import # pacify autouse's equality test ++ : $_[0]->SUPER::can($_[1]) # normal case ++} ++ ++# TODO ++# ++# Exported functions like field and super should be hidden so as not to ++# be confused with methods that can be inherited. ++# ++ ++sub new { ++ my $class = shift; ++ $class = ref($class) || $class; ++ my $self = bless {}, $class; ++ while (@_) { ++ my $method = shift; ++ $self->$method(shift); ++ } ++ return $self; ++} ++ ++my $filtered_files = {}; ++my $filter_dump = 0; ++my $filter_save = 0; ++our $filter_result = ''; ++sub import { ++ no strict 'refs'; ++ no warnings; ++ my $self_package = shift; ++ ++ # XXX Using parse_arguments here might cause confusion, because the ++ # subclass's boolean_arguments and paired_arguments can conflict, causing ++ # difficult debugging. Consider using something truly local. ++ my ($args, @export_list) = do { ++ local *boolean_arguments = sub { ++ qw( ++ -base -Base -mixin -selfless ++ -XXX -dumper -yaml ++ -filter_dump -filter_save ++ ) ++ }; ++ local *paired_arguments = sub { qw(-package) }; ++ $self_package->parse_arguments(@_); ++ }; ++ return spiffy_mixin_import(scalar(caller(0)), $self_package, @export_list) ++ if $args->{-mixin}; ++ ++ $filter_dump = 1 if $args->{-filter_dump}; ++ $filter_save = 1 if $args->{-filter_save}; ++ $dump = 'yaml' if $args->{-yaml}; ++ $dump = 'dumper' if $args->{-dumper}; ++ ++ local @EXPORT_BASE = @EXPORT_BASE; ++ ++ if ($args->{-XXX}) { ++ push @EXPORT_BASE, @{$EXPORT_TAGS{XXX}} ++ unless grep /^XXX$/, @EXPORT_BASE; ++ } ++ ++ spiffy_filter() ++ if ($args->{-selfless} or $args->{-Base}) and ++ not $filtered_files->{(caller($stack_frame))[1]}++; ++ ++ my $caller_package = $args->{-package} || caller($stack_frame); ++ push @{"$caller_package\::ISA"}, $self_package ++ if $args->{-Base} or $args->{-base}; ++ ++ for my $class (@{all_my_bases($self_package)}) { ++ next unless $class->isa('Spiffy'); ++ my @export = grep { ++ not defined &{"$caller_package\::$_"}; ++ } ( @{"$class\::EXPORT"}, ++ ($args->{-Base} or $args->{-base}) ++ ? @{"$class\::EXPORT_BASE"} : (), ++ ); ++ my @export_ok = grep { ++ not defined &{"$caller_package\::$_"}; ++ } @{"$class\::EXPORT_OK"}; ++ ++ # Avoid calling the expensive Exporter::export ++ # if there is nothing to do (optimization) ++ my %exportable = map { ($_, 1) } @export, @export_ok; ++ next unless keys %exportable; ++ ++ my @export_save = @{"$class\::EXPORT"}; ++ my @export_ok_save = @{"$class\::EXPORT_OK"}; ++ @{"$class\::EXPORT"} = @export; ++ @{"$class\::EXPORT_OK"} = @export_ok; ++ my @list = grep { ++ (my $v = $_) =~ s/^[\!\:]//; ++ $exportable{$v} or ${"$class\::EXPORT_TAGS"}{$v}; ++ } @export_list; ++ Exporter::export($class, $caller_package, @list); ++ @{"$class\::EXPORT"} = @export_save; ++ @{"$class\::EXPORT_OK"} = @export_ok_save; ++ } ++} ++ ++sub spiffy_filter { ++ require Filter::Util::Call; ++ my $done = 0; ++ Filter::Util::Call::filter_add( ++ sub { ++ return 0 if $done; ++ my ($data, $end) = ('', ''); ++ while (my $status = Filter::Util::Call::filter_read()) { ++ return $status if $status < 0; ++ if (/^__(?:END|DATA)__\r?$/) { ++ $end = $_; ++ last; ++ } ++ $data .= $_; ++ $_ = ''; ++ } ++ $_ = $data; ++ my @my_subs; ++ s[^(sub\s+\w+\s+\{)(.*\n)] ++ [${1}my \$self = shift;$2]gm; ++ s[^(sub\s+\w+)\s*\(\s*\)(\s+\{.*\n)] ++ [${1}${2}]gm; ++ s[^my\s+sub\s+(\w+)(\s+\{)(.*)((?s:.*?\n))\}\n] ++ [push @my_subs, $1; "\$$1 = sub$2my \$self = shift;$3$4\};\n"]gem; ++ my $preclare = ''; ++ if (@my_subs) { ++ $preclare = join ',', map "\$$_", @my_subs; ++ $preclare = "my($preclare);"; ++ } ++ $_ = "use strict;use warnings;$preclare${_};1;\n$end"; ++ if ($filter_dump) { print; exit } ++ if ($filter_save) { $filter_result = $_; $_ = $filter_result; } ++ $done = 1; ++ } ++ ); ++} ++ ++sub base { ++ push @_, -base; ++ goto &import; ++} ++ ++sub all_my_bases { ++ my $class = shift; ++ ++ return $bases_map->{$class} ++ if defined $bases_map->{$class}; ++ ++ my @bases = ($class); ++ no strict 'refs'; ++ for my $base_class (@{"${class}::ISA"}) { ++ push @bases, @{all_my_bases($base_class)}; ++ } ++ my $used = {}; ++ $bases_map->{$class} = [grep {not $used->{$_}++} @bases]; ++} ++ ++my %code = ( ++ sub_start => ++ "sub {\n", ++ set_default => ++ " \$_[0]->{%s} = %s\n unless exists \$_[0]->{%s};\n", ++ init => ++ " return \$_[0]->{%s} = do { my \$self = \$_[0]; %s }\n" . ++ " unless \$#_ > 0 or defined \$_[0]->{%s};\n", ++ weak_init => ++ " return do {\n" . ++ " \$_[0]->{%s} = do { my \$self = \$_[0]; %s };\n" . ++ " Scalar::Util::weaken(\$_[0]->{%s}) if ref \$_[0]->{%s};\n" . ++ " \$_[0]->{%s};\n" . ++ " } unless \$#_ > 0 or defined \$_[0]->{%s};\n", ++ return_if_get => ++ " return \$_[0]->{%s} unless \$#_ > 0;\n", ++ set => ++ " \$_[0]->{%s} = \$_[1];\n", ++ weaken => ++ " Scalar::Util::weaken(\$_[0]->{%s}) if ref \$_[0]->{%s};\n", ++ sub_end => ++ " return \$_[0]->{%s};\n}\n", ++); ++ ++sub field { ++ my $package = caller; ++ my ($args, @values) = do { ++ no warnings; ++ local *boolean_arguments = sub { (qw(-weak)) }; ++ local *paired_arguments = sub { (qw(-package -init)) }; ++ Spiffy->parse_arguments(@_); ++ }; ++ my ($field, $default) = @values; ++ $package = $args->{-package} if defined $args->{-package}; ++ die "Cannot have a default for a weakened field ($field)" ++ if defined $default && $args->{-weak}; ++ return if defined &{"${package}::$field"}; ++ require Scalar::Util if $args->{-weak}; ++ my $default_string = ++ ( ref($default) eq 'ARRAY' and not @$default ) ++ ? '[]' ++ : (ref($default) eq 'HASH' and not keys %$default ) ++ ? '{}' ++ : default_as_code($default); ++ ++ my $code = $code{sub_start}; ++ if ($args->{-init}) { ++ my $fragment = $args->{-weak} ? $code{weak_init} : $code{init}; ++ $code .= sprintf $fragment, $field, $args->{-init}, ($field) x 4; ++ } ++ $code .= sprintf $code{set_default}, $field, $default_string, $field ++ if defined $default; ++ $code .= sprintf $code{return_if_get}, $field; ++ $code .= sprintf $code{set}, $field; ++ $code .= sprintf $code{weaken}, $field, $field ++ if $args->{-weak}; ++ $code .= sprintf $code{sub_end}, $field; ++ ++ my $sub = eval $code; ++ die $@ if $@; ++ no strict 'refs'; ++ *{"${package}::$field"} = $sub; ++ return $code if defined wantarray; ++} ++ ++sub default_as_code { ++ require Data::Dumper; ++ local $Data::Dumper::Sortkeys = 1; ++ my $code = Data::Dumper::Dumper(shift); ++ $code =~ s/^\$VAR1 = //; ++ $code =~ s/;$//; ++ return $code; ++} ++ ++sub const { ++ my $package = caller; ++ my ($args, @values) = do { ++ no warnings; ++ local *paired_arguments = sub { (qw(-package)) }; ++ Spiffy->parse_arguments(@_); ++ }; ++ my ($field, $default) = @values; ++ $package = $args->{-package} if defined $args->{-package}; ++ no strict 'refs'; ++ return if defined &{"${package}::$field"}; ++ *{"${package}::$field"} = sub { $default } ++} ++ ++sub stub { ++ my $package = caller; ++ my ($args, @values) = do { ++ no warnings; ++ local *paired_arguments = sub { (qw(-package)) }; ++ Spiffy->parse_arguments(@_); ++ }; ++ my ($field, $default) = @values; ++ $package = $args->{-package} if defined $args->{-package}; ++ no strict 'refs'; ++ return if defined &{"${package}::$field"}; ++ *{"${package}::$field"} = ++ sub { ++ require Carp; ++ Carp::confess ++ "Method $field in package $package must be subclassed"; ++ } ++} ++ ++sub parse_arguments { ++ my $class = shift; ++ my ($args, @values) = ({}, ()); ++ my %booleans = map { ($_, 1) } $class->boolean_arguments; ++ my %pairs = map { ($_, 1) } $class->paired_arguments; ++ while (@_) { ++ my $elem = shift; ++ if (defined $elem and defined $booleans{$elem}) { ++ $args->{$elem} = (@_ and $_[0] =~ /^[01]$/) ++ ? shift ++ : 1; ++ } ++ elsif (defined $elem and defined $pairs{$elem} and @_) { ++ $args->{$elem} = shift; ++ } ++ else { ++ push @values, $elem; ++ } ++ } ++ return wantarray ? ($args, @values) : $args; ++} ++ ++sub boolean_arguments { () } ++sub paired_arguments { () } ++ ++# get a unique id for any node ++sub id { ++ if (not ref $_[0]) { ++ return 'undef' if not defined $_[0]; ++ \$_[0] =~ /\((\w+)\)$/o or die; ++ return "$1-S"; ++ } ++ require overload; ++ overload::StrVal($_[0]) =~ /\((\w+)\)$/o or die; ++ return $1; ++} ++ ++#=============================================================================== ++# It's super, man. ++#=============================================================================== ++package DB; ++{ ++ no warnings 'redefine'; ++ sub super_args { ++ my @dummy = caller(@_ ? $_[0] : 2); ++ return @DB::args; ++ } ++} ++ ++package Spiffy; ++sub super { ++ my $method; ++ my $frame = 1; ++ while ($method = (caller($frame++))[3]) { ++ $method =~ s/.*::// and last; ++ } ++ my @args = DB::super_args($frame); ++ @_ = @_ ? ($args[0], @_) : @args; ++ my $class = ref $_[0] ? ref $_[0] : $_[0]; ++ my $caller_class = caller; ++ my $seen = 0; ++ my @super_classes = reverse grep { ++ ($seen or $seen = ($_ eq $caller_class)) ? 0 : 1; ++ } reverse @{all_my_bases($class)}; ++ for my $super_class (@super_classes) { ++ no strict 'refs'; ++ next if $super_class eq $class; ++ if (defined &{"${super_class}::$method"}) { ++ ${"$super_class\::AUTOLOAD"} = ${"$class\::AUTOLOAD"} ++ if $method eq 'AUTOLOAD'; ++ return &{"${super_class}::$method"}; ++ } ++ } ++ return; ++} ++ ++#=============================================================================== ++# This code deserves a spanking, because it is being very naughty. ++# It is exchanging base.pm's import() for its own, so that people ++# can use base.pm with Spiffy modules, without being the wiser. ++#=============================================================================== ++my $real_base_import; ++my $real_mixin_import; ++ ++BEGIN { ++ require base unless defined $INC{'base.pm'}; ++ $INC{'mixin.pm'} ||= 'Spiffy/mixin.pm'; ++ $real_base_import = \&base::import; ++ $real_mixin_import = \&mixin::import; ++ no warnings; ++ *base::import = \&spiffy_base_import; ++ *mixin::import = \&spiffy_mixin_import; ++} ++ ++# my $i = 0; ++# while (my $caller = caller($i++)) { ++# next unless $caller eq 'base' or $caller eq 'mixin'; ++# croak <isa('Spiffy'); ++ } @base_classes; ++ my $inheritor = caller(0); ++ for my $base_class (@base_classes) { ++ next if $inheritor->isa($base_class); ++ croak "Can't mix Spiffy and non-Spiffy classes in 'use base'.\n", ++ "See the documentation of Spiffy.pm for details\n " ++ unless $base_class->isa('Spiffy'); ++ $stack_frame = 1; # tell import to use different caller ++ import($base_class, '-base'); ++ $stack_frame = 0; ++ } ++} ++ ++sub mixin { ++ my $self = shift; ++ my $target_class = ref($self); ++ spiffy_mixin_import($target_class, @_) ++} ++ ++sub spiffy_mixin_import { ++ my $target_class = shift; ++ $target_class = caller(0) ++ if $target_class eq 'mixin'; ++ my $mixin_class = shift ++ or die "Nothing to mixin"; ++ eval "require $mixin_class"; ++ my @roles = @_; ++ my $pseudo_class = join '-', $target_class, $mixin_class, @roles; ++ my %methods = spiffy_mixin_methods($mixin_class, @roles); ++ no strict 'refs'; ++ no warnings; ++ @{"$pseudo_class\::ISA"} = @{"$target_class\::ISA"}; ++ @{"$target_class\::ISA"} = ($pseudo_class); ++ for (keys %methods) { ++ *{"$pseudo_class\::$_"} = $methods{$_}; ++ } ++} ++ ++sub spiffy_mixin_methods { ++ my $mixin_class = shift; ++ no strict 'refs'; ++ my %methods = spiffy_all_methods($mixin_class); ++ map { ++ $methods{$_} ++ ? ($_, \ &{"$methods{$_}\::$_"}) ++ : ($_, \ &{"$mixin_class\::$_"}) ++ } @_ ++ ? (get_roles($mixin_class, @_)) ++ : (keys %methods); ++} ++ ++sub get_roles { ++ my $mixin_class = shift; ++ my @roles = @_; ++ while (grep /^!*:/, @roles) { ++ @roles = map { ++ s/!!//g; ++ /^!:(.*)/ ? do { ++ my $m = "_role_$1"; ++ map("!$_", $mixin_class->$m); ++ } : ++ /^:(.*)/ ? do { ++ my $m = "_role_$1"; ++ ($mixin_class->$m); ++ } : ++ ($_) ++ } @roles; ++ } ++ if (@roles and $roles[0] =~ /^!/) { ++ my %methods = spiffy_all_methods($mixin_class); ++ unshift @roles, keys(%methods); ++ } ++ my %roles; ++ for (@roles) { ++ s/!!//g; ++ delete $roles{$1}, next ++ if /^!(.*)/; ++ $roles{$_} = 1; ++ } ++ keys %roles; ++} ++ ++sub spiffy_all_methods { ++ no strict 'refs'; ++ my $class = shift; ++ return if $class eq 'Spiffy'; ++ my %methods = map { ++ ($_, $class) ++ } grep { ++ defined &{"$class\::$_"} and not /^_/ ++ } keys %{"$class\::"}; ++ my %super_methods; ++ %super_methods = spiffy_all_methods(${"$class\::ISA"}[0]) ++ if @{"$class\::ISA"}; ++ %{{%super_methods, %methods}}; ++} ++ ++ ++# END of naughty code. ++#=============================================================================== ++# Debugging support ++#=============================================================================== ++sub spiffy_dump { ++ no warnings; ++ if ($dump eq 'dumper') { ++ require Data::Dumper; ++ $Data::Dumper::Sortkeys = 1; ++ $Data::Dumper::Indent = 1; ++ return Data::Dumper::Dumper(@_); ++ } ++ require YAML; ++ $YAML::UseVersion = 0; ++ return YAML::Dump(@_) . "...\n"; ++} ++ ++sub at_line_number { ++ my ($file_path, $line_number) = (caller(1))[1,2]; ++ " at $file_path line $line_number\n"; ++} ++ ++sub WWW { ++ warn spiffy_dump(@_) . at_line_number; ++ return wantarray ? @_ : $_[0]; ++} ++ ++sub XXX { ++ die spiffy_dump(@_) . at_line_number; ++} ++ ++sub YYY { ++ print spiffy_dump(@_) . at_line_number; ++ return wantarray ? @_ : $_[0]; ++} ++ ++sub ZZZ { ++ require Carp; ++ Carp::confess spiffy_dump(@_); ++} ++ ++1; ++ ++__END__ ++ ++#line 1066 +Index: 0.8/modules/nginx-echo/test/inc/Test/Base.pm +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ 0.8/modules/nginx-echo/test/inc/Test/Base.pm 2010-10-31 07:35:47.678338000 +0000 +@@ -0,0 +1,684 @@ ++#line 1 ++# TODO: ++# ++package Test::Base; ++use 5.006001; ++use Spiffy 0.30 -Base; ++use Spiffy ':XXX'; ++our $VERSION = '0.59'; ++ ++my @test_more_exports; ++BEGIN { ++ @test_more_exports = qw( ++ ok isnt like unlike is_deeply cmp_ok ++ skip todo_skip pass fail ++ eq_array eq_hash eq_set ++ plan can_ok isa_ok diag ++ use_ok ++ $TODO ++ ); ++} ++ ++use Test::More import => \@test_more_exports; ++use Carp; ++ ++our @EXPORT = (@test_more_exports, qw( ++ is no_diff ++ ++ blocks next_block first_block ++ delimiters spec_file spec_string ++ filters filters_delay filter_arguments ++ run run_compare run_is run_is_deeply run_like run_unlike ++ skip_all_unless_require is_deep run_is_deep ++ WWW XXX YYY ZZZ ++ tie_output no_diag_on_only ++ ++ find_my_self default_object ++ ++ croak carp cluck confess ++)); ++ ++field '_spec_file'; ++field '_spec_string'; ++field _filters => [qw(norm trim)]; ++field _filters_map => {}; ++field spec => ++ -init => '$self->_spec_init'; ++field block_list => ++ -init => '$self->_block_list_init'; ++field _next_list => []; ++field block_delim => ++ -init => '$self->block_delim_default'; ++field data_delim => ++ -init => '$self->data_delim_default'; ++field _filters_delay => 0; ++field _no_diag_on_only => 0; ++ ++field block_delim_default => '==='; ++field data_delim_default => '---'; ++ ++my $default_class; ++my $default_object; ++my $reserved_section_names = {}; ++ ++sub default_object { ++ $default_object ||= $default_class->new; ++ return $default_object; ++} ++ ++my $import_called = 0; ++sub import() { ++ $import_called = 1; ++ my $class = (grep /^-base$/i, @_) ++ ? scalar(caller) ++ : $_[0]; ++ if (not defined $default_class) { ++ $default_class = $class; ++ } ++# else { ++# croak "Can't use $class after using $default_class" ++# unless $default_class->isa($class); ++# } ++ ++ unless (grep /^-base$/i, @_) { ++ my @args; ++ for (my $ii = 1; $ii <= $#_; ++$ii) { ++ if ($_[$ii] eq '-package') { ++ ++$ii; ++ } else { ++ push @args, $_[$ii]; ++ } ++ } ++ Test::More->import(import => \@test_more_exports, @args) ++ if @args; ++ } ++ ++ _strict_warnings(); ++ goto &Spiffy::import; ++} ++ ++# Wrap Test::Builder::plan ++my $plan_code = \&Test::Builder::plan; ++my $Have_Plan = 0; ++{ ++ no warnings 'redefine'; ++ *Test::Builder::plan = sub { ++ $Have_Plan = 1; ++ goto &$plan_code; ++ }; ++} ++ ++my $DIED = 0; ++$SIG{__DIE__} = sub { $DIED = 1; die @_ }; ++ ++sub block_class { $self->find_class('Block') } ++sub filter_class { $self->find_class('Filter') } ++ ++sub find_class { ++ my $suffix = shift; ++ my $class = ref($self) . "::$suffix"; ++ return $class if $class->can('new'); ++ $class = __PACKAGE__ . "::$suffix"; ++ return $class if $class->can('new'); ++ eval "require $class"; ++ return $class if $class->can('new'); ++ die "Can't find a class for $suffix"; ++} ++ ++sub check_late { ++ if ($self->{block_list}) { ++ my $caller = (caller(1))[3]; ++ $caller =~ s/.*:://; ++ croak "Too late to call $caller()" ++ } ++} ++ ++sub find_my_self() { ++ my $self = ref($_[0]) eq $default_class ++ ? splice(@_, 0, 1) ++ : default_object(); ++ return $self, @_; ++} ++ ++sub blocks() { ++ (my ($self), @_) = find_my_self(@_); ++ ++ croak "Invalid arguments passed to 'blocks'" ++ if @_ > 1; ++ croak sprintf("'%s' is invalid argument to blocks()", shift(@_)) ++ if @_ && $_[0] !~ /^[a-zA-Z]\w*$/; ++ ++ my $blocks = $self->block_list; ++ ++ my $section_name = shift || ''; ++ my @blocks = $section_name ++ ? (grep { exists $_->{$section_name} } @$blocks) ++ : (@$blocks); ++ ++ return scalar(@blocks) unless wantarray; ++ ++ return (@blocks) if $self->_filters_delay; ++ ++ for my $block (@blocks) { ++ $block->run_filters ++ unless $block->is_filtered; ++ } ++ ++ return (@blocks); ++} ++ ++sub next_block() { ++ (my ($self), @_) = find_my_self(@_); ++ my $list = $self->_next_list; ++ if (@$list == 0) { ++ $list = [@{$self->block_list}, undef]; ++ $self->_next_list($list); ++ } ++ my $block = shift @$list; ++ if (defined $block and not $block->is_filtered) { ++ $block->run_filters; ++ } ++ return $block; ++} ++ ++sub first_block() { ++ (my ($self), @_) = find_my_self(@_); ++ $self->_next_list([]); ++ $self->next_block; ++} ++ ++sub filters_delay() { ++ (my ($self), @_) = find_my_self(@_); ++ $self->_filters_delay(defined $_[0] ? shift : 1); ++} ++ ++sub no_diag_on_only() { ++ (my ($self), @_) = find_my_self(@_); ++ $self->_no_diag_on_only(defined $_[0] ? shift : 1); ++} ++ ++sub delimiters() { ++ (my ($self), @_) = find_my_self(@_); ++ $self->check_late; ++ my ($block_delimiter, $data_delimiter) = @_; ++ $block_delimiter ||= $self->block_delim_default; ++ $data_delimiter ||= $self->data_delim_default; ++ $self->block_delim($block_delimiter); ++ $self->data_delim($data_delimiter); ++ return $self; ++} ++ ++sub spec_file() { ++ (my ($self), @_) = find_my_self(@_); ++ $self->check_late; ++ $self->_spec_file(shift); ++ return $self; ++} ++ ++sub spec_string() { ++ (my ($self), @_) = find_my_self(@_); ++ $self->check_late; ++ $self->_spec_string(shift); ++ return $self; ++} ++ ++sub filters() { ++ (my ($self), @_) = find_my_self(@_); ++ if (ref($_[0]) eq 'HASH') { ++ $self->_filters_map(shift); ++ } ++ else { ++ my $filters = $self->_filters; ++ push @$filters, @_; ++ } ++ return $self; ++} ++ ++sub filter_arguments() { ++ $Test::Base::Filter::arguments; ++} ++ ++sub have_text_diff { ++ eval { require Text::Diff; 1 } && ++ $Text::Diff::VERSION >= 0.35 && ++ $Algorithm::Diff::VERSION >= 1.15; ++} ++ ++sub is($$;$) { ++ (my ($self), @_) = find_my_self(@_); ++ my ($actual, $expected, $name) = @_; ++ local $Test::Builder::Level = $Test::Builder::Level + 1; ++ if ($ENV{TEST_SHOW_NO_DIFFS} or ++ not defined $actual or ++ not defined $expected or ++ $actual eq $expected or ++ not($self->have_text_diff) or ++ $expected !~ /\n./s ++ ) { ++ Test::More::is($actual, $expected, $name); ++ } ++ else { ++ $name = '' unless defined $name; ++ ok $actual eq $expected, ++ $name . "\n" . Text::Diff::diff(\$expected, \$actual); ++ } ++} ++ ++sub run(&;$) { ++ (my ($self), @_) = find_my_self(@_); ++ my $callback = shift; ++ for my $block (@{$self->block_list}) { ++ $block->run_filters unless $block->is_filtered; ++ &{$callback}($block); ++ } ++} ++ ++my $name_error = "Can't determine section names"; ++sub _section_names { ++ return @_ if @_ == 2; ++ my $block = $self->first_block ++ or croak $name_error; ++ my @names = grep { ++ $_ !~ /^(ONLY|LAST|SKIP)$/; ++ } @{$block->{_section_order}[0] || []}; ++ croak "$name_error. Need two sections in first block" ++ unless @names == 2; ++ return @names; ++} ++ ++sub _assert_plan { ++ plan('no_plan') unless $Have_Plan; ++} ++ ++sub END { ++ run_compare() unless $Have_Plan or $DIED or not $import_called; ++} ++ ++sub run_compare() { ++ (my ($self), @_) = find_my_self(@_); ++ $self->_assert_plan; ++ my ($x, $y) = $self->_section_names(@_); ++ local $Test::Builder::Level = $Test::Builder::Level + 1; ++ for my $block (@{$self->block_list}) { ++ next unless exists($block->{$x}) and exists($block->{$y}); ++ $block->run_filters unless $block->is_filtered; ++ if (ref $block->$x) { ++ is_deeply($block->$x, $block->$y, ++ $block->name ? $block->name : ()); ++ } ++ elsif (ref $block->$y eq 'Regexp') { ++ my $regexp = ref $y ? $y : $block->$y; ++ like($block->$x, $regexp, $block->name ? $block->name : ()); ++ } ++ else { ++ is($block->$x, $block->$y, $block->name ? $block->name : ()); ++ } ++ } ++} ++ ++sub run_is() { ++ (my ($self), @_) = find_my_self(@_); ++ $self->_assert_plan; ++ my ($x, $y) = $self->_section_names(@_); ++ local $Test::Builder::Level = $Test::Builder::Level + 1; ++ for my $block (@{$self->block_list}) { ++ next unless exists($block->{$x}) and exists($block->{$y}); ++ $block->run_filters unless $block->is_filtered; ++ is($block->$x, $block->$y, ++ $block->name ? $block->name : () ++ ); ++ } ++} ++ ++sub run_is_deeply() { ++ (my ($self), @_) = find_my_self(@_); ++ $self->_assert_plan; ++ my ($x, $y) = $self->_section_names(@_); ++ for my $block (@{$self->block_list}) { ++ next unless exists($block->{$x}) and exists($block->{$y}); ++ $block->run_filters unless $block->is_filtered; ++ is_deeply($block->$x, $block->$y, ++ $block->name ? $block->name : () ++ ); ++ } ++} ++ ++sub run_like() { ++ (my ($self), @_) = find_my_self(@_); ++ $self->_assert_plan; ++ my ($x, $y) = $self->_section_names(@_); ++ for my $block (@{$self->block_list}) { ++ next unless exists($block->{$x}) and defined($y); ++ $block->run_filters unless $block->is_filtered; ++ my $regexp = ref $y ? $y : $block->$y; ++ like($block->$x, $regexp, ++ $block->name ? $block->name : () ++ ); ++ } ++} ++ ++sub run_unlike() { ++ (my ($self), @_) = find_my_self(@_); ++ $self->_assert_plan; ++ my ($x, $y) = $self->_section_names(@_); ++ for my $block (@{$self->block_list}) { ++ next unless exists($block->{$x}) and defined($y); ++ $block->run_filters unless $block->is_filtered; ++ my $regexp = ref $y ? $y : $block->$y; ++ unlike($block->$x, $regexp, ++ $block->name ? $block->name : () ++ ); ++ } ++} ++ ++sub skip_all_unless_require() { ++ (my ($self), @_) = find_my_self(@_); ++ my $module = shift; ++ eval "require $module; 1" ++ or Test::More::plan( ++ skip_all => "$module failed to load" ++ ); ++} ++ ++sub is_deep() { ++ (my ($self), @_) = find_my_self(@_); ++ require Test::Deep; ++ Test::Deep::cmp_deeply(@_); ++} ++ ++sub run_is_deep() { ++ (my ($self), @_) = find_my_self(@_); ++ $self->_assert_plan; ++ my ($x, $y) = $self->_section_names(@_); ++ for my $block (@{$self->block_list}) { ++ next unless exists($block->{$x}) and exists($block->{$y}); ++ $block->run_filters unless $block->is_filtered; ++ is_deep($block->$x, $block->$y, ++ $block->name ? $block->name : () ++ ); ++ } ++} ++ ++sub _pre_eval { ++ my $spec = shift; ++ return $spec unless $spec =~ ++ s/\A\s*<<<(.*?)>>>\s*$//sm; ++ my $eval_code = $1; ++ eval "package main; $eval_code"; ++ croak $@ if $@; ++ return $spec; ++} ++ ++sub _block_list_init { ++ my $spec = $self->spec; ++ $spec = $self->_pre_eval($spec); ++ my $cd = $self->block_delim; ++ my @hunks = ($spec =~ /^(\Q${cd}\E.*?(?=^\Q${cd}\E|\z))/msg); ++ my $blocks = $self->_choose_blocks(@hunks); ++ $self->block_list($blocks); # Need to set early for possible filter use ++ my $seq = 1; ++ for my $block (@$blocks) { ++ $block->blocks_object($self); ++ $block->seq_num($seq++); ++ } ++ return $blocks; ++} ++ ++sub _choose_blocks { ++ my $blocks = []; ++ for my $hunk (@_) { ++ my $block = $self->_make_block($hunk); ++ if (exists $block->{ONLY}) { ++ diag "I found ONLY: maybe you're debugging?" ++ unless $self->_no_diag_on_only; ++ return [$block]; ++ } ++ next if exists $block->{SKIP}; ++ push @$blocks, $block; ++ if (exists $block->{LAST}) { ++ return $blocks; ++ } ++ } ++ return $blocks; ++} ++ ++sub _check_reserved { ++ my $id = shift; ++ croak "'$id' is a reserved name. Use something else.\n" ++ if $reserved_section_names->{$id} or ++ $id =~ /^_/; ++} ++ ++sub _make_block { ++ my $hunk = shift; ++ my $cd = $self->block_delim; ++ my $dd = $self->data_delim; ++ my $block = $self->block_class->new; ++ $hunk =~ s/\A\Q${cd}\E[ \t]*(.*)\s+// or die; ++ my $name = $1; ++ my @parts = split /^\Q${dd}\E +\(?(\w+)\)? *(.*)?\n/m, $hunk; ++ my $description = shift @parts; ++ $description ||= ''; ++ unless ($description =~ /\S/) { ++ $description = $name; ++ } ++ $description =~ s/\s*\z//; ++ $block->set_value(description => $description); ++ ++ my $section_map = {}; ++ my $section_order = []; ++ while (@parts) { ++ my ($type, $filters, $value) = splice(@parts, 0, 3); ++ $self->_check_reserved($type); ++ $value = '' unless defined $value; ++ $filters = '' unless defined $filters; ++ if ($filters =~ /:(\s|\z)/) { ++ croak "Extra lines not allowed in '$type' section" ++ if $value =~ /\S/; ++ ($filters, $value) = split /\s*:(?:\s+|\z)/, $filters, 2; ++ $value = '' unless defined $value; ++ $value =~ s/^\s*(.*?)\s*$/$1/; ++ } ++ $section_map->{$type} = { ++ filters => $filters, ++ }; ++ push @$section_order, $type; ++ $block->set_value($type, $value); ++ } ++ $block->set_value(name => $name); ++ $block->set_value(_section_map => $section_map); ++ $block->set_value(_section_order => $section_order); ++ return $block; ++} ++ ++sub _spec_init { ++ return $self->_spec_string ++ if $self->_spec_string; ++ local $/; ++ my $spec; ++ if (my $spec_file = $self->_spec_file) { ++ open FILE, $spec_file or die $!; ++ $spec = ; ++ close FILE; ++ } ++ else { ++ $spec = do { ++ package main; ++ no warnings 'once'; ++ ; ++ }; ++ } ++ return $spec; ++} ++ ++sub _strict_warnings() { ++ require Filter::Util::Call; ++ my $done = 0; ++ Filter::Util::Call::filter_add( ++ sub { ++ return 0 if $done; ++ my ($data, $end) = ('', ''); ++ while (my $status = Filter::Util::Call::filter_read()) { ++ return $status if $status < 0; ++ if (/^__(?:END|DATA)__\r?$/) { ++ $end = $_; ++ last; ++ } ++ $data .= $_; ++ $_ = ''; ++ } ++ $_ = "use strict;use warnings;$data$end"; ++ $done = 1; ++ } ++ ); ++} ++ ++sub tie_output() { ++ my $handle = shift; ++ die "No buffer to tie" unless @_; ++ tie $handle, 'Test::Base::Handle', $_[0]; ++} ++ ++sub no_diff { ++ $ENV{TEST_SHOW_NO_DIFFS} = 1; ++} ++ ++package Test::Base::Handle; ++ ++sub TIEHANDLE() { ++ my $class = shift; ++ bless \ $_[0], $class; ++} ++ ++sub PRINT { ++ $$self .= $_ for @_; ++} ++ ++#=============================================================================== ++# Test::Base::Block ++# ++# This is the default class for accessing a Test::Base block object. ++#=============================================================================== ++package Test::Base::Block; ++our @ISA = qw(Spiffy); ++ ++our @EXPORT = qw(block_accessor); ++ ++sub AUTOLOAD { ++ return; ++} ++ ++sub block_accessor() { ++ my $accessor = shift; ++ no strict 'refs'; ++ return if defined &$accessor; ++ *$accessor = sub { ++ my $self = shift; ++ if (@_) { ++ Carp::croak "Not allowed to set values for '$accessor'"; ++ } ++ my @list = @{$self->{$accessor} || []}; ++ return wantarray ++ ? (@list) ++ : $list[0]; ++ }; ++} ++ ++block_accessor 'name'; ++block_accessor 'description'; ++Spiffy::field 'seq_num'; ++Spiffy::field 'is_filtered'; ++Spiffy::field 'blocks_object'; ++Spiffy::field 'original_values' => {}; ++ ++sub set_value { ++ no strict 'refs'; ++ my $accessor = shift; ++ block_accessor $accessor ++ unless defined &$accessor; ++ $self->{$accessor} = [@_]; ++} ++ ++sub run_filters { ++ my $map = $self->_section_map; ++ my $order = $self->_section_order; ++ Carp::croak "Attempt to filter a block twice" ++ if $self->is_filtered; ++ for my $type (@$order) { ++ my $filters = $map->{$type}{filters}; ++ my @value = $self->$type; ++ $self->original_values->{$type} = $value[0]; ++ for my $filter ($self->_get_filters($type, $filters)) { ++ $Test::Base::Filter::arguments = ++ $filter =~ s/=(.*)$// ? $1 : undef; ++ my $function = "main::$filter"; ++ no strict 'refs'; ++ if (defined &$function) { ++ local $_ = ++ (@value == 1 and not defined($value[0])) ? undef : ++ join '', @value; ++ my $old = $_; ++ @value = &$function(@value); ++ if (not(@value) or ++ @value == 1 and defined($value[0]) and $value[0] =~ /\A(\d+|)\z/ ++ ) { ++ if ($value[0] && $_ eq $old) { ++ Test::Base::diag("Filters returning numbers are supposed to do munging \$_: your filter '$function' apparently doesn't."); ++ } ++ @value = ($_); ++ } ++ } ++ else { ++ my $filter_object = $self->blocks_object->filter_class->new; ++ die "Can't find a function or method for '$filter' filter\n" ++ unless $filter_object->can($filter); ++ $filter_object->current_block($self); ++ @value = $filter_object->$filter(@value); ++ } ++ # Set the value after each filter since other filters may be ++ # introspecting. ++ $self->set_value($type, @value); ++ } ++ } ++ $self->is_filtered(1); ++} ++ ++sub _get_filters { ++ my $type = shift; ++ my $string = shift || ''; ++ $string =~ s/\s*(.*?)\s*/$1/; ++ my @filters = (); ++ my $map_filters = $self->blocks_object->_filters_map->{$type} || []; ++ $map_filters = [ $map_filters ] unless ref $map_filters; ++ my @append = (); ++ for ( ++ @{$self->blocks_object->_filters}, ++ @$map_filters, ++ split(/\s+/, $string), ++ ) { ++ my $filter = $_; ++ last unless length $filter; ++ if ($filter =~ s/^-//) { ++ @filters = grep { $_ ne $filter } @filters; ++ } ++ elsif ($filter =~ s/^\+//) { ++ push @append, $filter; ++ } ++ else { ++ push @filters, $filter; ++ } ++ } ++ return @filters, @append; ++} ++ ++{ ++ %$reserved_section_names = map { ++ ($_, 1); ++ } keys(%Test::Base::Block::), qw( new DESTROY ); ++} ++ ++__DATA__ ++ ++=encoding utf8 ++ ++#line 1376 +Index: 0.8/modules/nginx-echo/test/inc/Test/Builder.pm +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ 0.8/modules/nginx-echo/test/inc/Test/Builder.pm 2010-10-31 07:35:23.648338001 +0000 +@@ -0,0 +1,1413 @@ ++#line 1 ++package Test::Builder; ++ ++use 5.006; ++use strict; ++use warnings; ++ ++our $VERSION = '0.92'; ++$VERSION = eval $VERSION; ## no critic (BuiltinFunctions::ProhibitStringyEval) ++ ++BEGIN { ++ if( $] < 5.008 ) { ++ require Test::Builder::IO::Scalar; ++ } ++} ++ ++ ++# Make Test::Builder thread-safe for ithreads. ++BEGIN { ++ use Config; ++ # Load threads::shared when threads are turned on. ++ # 5.8.0's threads are so busted we no longer support them. ++ if( $] >= 5.008001 && $Config{useithreads} && $INC{'threads.pm'} ) { ++ require threads::shared; ++ ++ # Hack around YET ANOTHER threads::shared bug. It would ++ # occassionally forget the contents of the variable when sharing it. ++ # So we first copy the data, then share, then put our copy back. ++ *share = sub (\[$@%]) { ++ my $type = ref $_[0]; ++ my $data; ++ ++ if( $type eq 'HASH' ) { ++ %$data = %{ $_[0] }; ++ } ++ elsif( $type eq 'ARRAY' ) { ++ @$data = @{ $_[0] }; ++ } ++ elsif( $type eq 'SCALAR' ) { ++ $$data = ${ $_[0] }; ++ } ++ else { ++ die( "Unknown type: " . $type ); ++ } ++ ++ $_[0] = &threads::shared::share( $_[0] ); ++ ++ if( $type eq 'HASH' ) { ++ %{ $_[0] } = %$data; ++ } ++ elsif( $type eq 'ARRAY' ) { ++ @{ $_[0] } = @$data; ++ } ++ elsif( $type eq 'SCALAR' ) { ++ ${ $_[0] } = $$data; ++ } ++ else { ++ die( "Unknown type: " . $type ); ++ } ++ ++ return $_[0]; ++ }; ++ } ++ # 5.8.0's threads::shared is busted when threads are off ++ # and earlier Perls just don't have that module at all. ++ else { ++ *share = sub { return $_[0] }; ++ *lock = sub { 0 }; ++ } ++} ++ ++#line 117 ++ ++my $Test = Test::Builder->new; ++ ++sub new { ++ my($class) = shift; ++ $Test ||= $class->create; ++ return $Test; ++} ++ ++#line 139 ++ ++sub create { ++ my $class = shift; ++ ++ my $self = bless {}, $class; ++ $self->reset; ++ ++ return $self; ++} ++ ++#line 158 ++ ++our $Level; ++ ++sub reset { ## no critic (Subroutines::ProhibitBuiltinHomonyms) ++ my($self) = @_; ++ ++ # We leave this a global because it has to be localized and localizing ++ # hash keys is just asking for pain. Also, it was documented. ++ $Level = 1; ++ ++ $self->{Have_Plan} = 0; ++ $self->{No_Plan} = 0; ++ $self->{Have_Output_Plan} = 0; ++ ++ $self->{Original_Pid} = $$; ++ ++ share( $self->{Curr_Test} ); ++ $self->{Curr_Test} = 0; ++ $self->{Test_Results} = &share( [] ); ++ ++ $self->{Exported_To} = undef; ++ $self->{Expected_Tests} = 0; ++ ++ $self->{Skip_All} = 0; ++ ++ $self->{Use_Nums} = 1; ++ ++ $self->{No_Header} = 0; ++ $self->{No_Ending} = 0; ++ ++ $self->{Todo} = undef; ++ $self->{Todo_Stack} = []; ++ $self->{Start_Todo} = 0; ++ $self->{Opened_Testhandles} = 0; ++ ++ $self->_dup_stdhandles; ++ ++ return; ++} ++ ++#line 219 ++ ++my %plan_cmds = ( ++ no_plan => \&no_plan, ++ skip_all => \&skip_all, ++ tests => \&_plan_tests, ++); ++ ++sub plan { ++ my( $self, $cmd, $arg ) = @_; ++ ++ return unless $cmd; ++ ++ local $Level = $Level + 1; ++ ++ $self->croak("You tried to plan twice") if $self->{Have_Plan}; ++ ++ if( my $method = $plan_cmds{$cmd} ) { ++ local $Level = $Level + 1; ++ $self->$method($arg); ++ } ++ else { ++ my @args = grep { defined } ( $cmd, $arg ); ++ $self->croak("plan() doesn't understand @args"); ++ } ++ ++ return 1; ++} ++ ++ ++sub _plan_tests { ++ my($self, $arg) = @_; ++ ++ if($arg) { ++ local $Level = $Level + 1; ++ return $self->expected_tests($arg); ++ } ++ elsif( !defined $arg ) { ++ $self->croak("Got an undefined number of tests"); ++ } ++ else { ++ $self->croak("You said to run 0 tests"); ++ } ++ ++ return; ++} ++ ++ ++#line 275 ++ ++sub expected_tests { ++ my $self = shift; ++ my($max) = @_; ++ ++ if(@_) { ++ $self->croak("Number of tests must be a positive integer. You gave it '$max'") ++ unless $max =~ /^\+?\d+$/; ++ ++ $self->{Expected_Tests} = $max; ++ $self->{Have_Plan} = 1; ++ ++ $self->_output_plan($max) unless $self->no_header; ++ } ++ return $self->{Expected_Tests}; ++} ++ ++#line 299 ++ ++sub no_plan { ++ my($self, $arg) = @_; ++ ++ $self->carp("no_plan takes no arguments") if $arg; ++ ++ $self->{No_Plan} = 1; ++ $self->{Have_Plan} = 1; ++ ++ return 1; ++} ++ ++ ++#line 333 ++ ++sub _output_plan { ++ my($self, $max, $directive, $reason) = @_; ++ ++ $self->carp("The plan was already output") if $self->{Have_Output_Plan}; ++ ++ my $plan = "1..$max"; ++ $plan .= " # $directive" if defined $directive; ++ $plan .= " $reason" if defined $reason; ++ ++ $self->_print("$plan\n"); ++ ++ $self->{Have_Output_Plan} = 1; ++ ++ return; ++} ++ ++#line 384 ++ ++sub done_testing { ++ my($self, $num_tests) = @_; ++ ++ # If done_testing() specified the number of tests, shut off no_plan. ++ if( defined $num_tests ) { ++ $self->{No_Plan} = 0; ++ } ++ else { ++ $num_tests = $self->current_test; ++ } ++ ++ if( $self->{Done_Testing} ) { ++ my($file, $line) = @{$self->{Done_Testing}}[1,2]; ++ $self->ok(0, "done_testing() was already called at $file line $line"); ++ return; ++ } ++ ++ $self->{Done_Testing} = [caller]; ++ ++ if( $self->expected_tests && $num_tests != $self->expected_tests ) { ++ $self->ok(0, "planned to run @{[ $self->expected_tests ]} ". ++ "but done_testing() expects $num_tests"); ++ } ++ else { ++ $self->{Expected_Tests} = $num_tests; ++ } ++ ++ $self->_output_plan($num_tests) unless $self->{Have_Output_Plan}; ++ ++ $self->{Have_Plan} = 1; ++ ++ return 1; ++} ++ ++ ++#line 429 ++ ++sub has_plan { ++ my $self = shift; ++ ++ return( $self->{Expected_Tests} ) if $self->{Expected_Tests}; ++ return('no_plan') if $self->{No_Plan}; ++ return(undef); ++} ++ ++#line 446 ++ ++sub skip_all { ++ my( $self, $reason ) = @_; ++ ++ $self->{Skip_All} = 1; ++ ++ $self->_output_plan(0, "SKIP", $reason) unless $self->no_header; ++ exit(0); ++} ++ ++#line 468 ++ ++sub exported_to { ++ my( $self, $pack ) = @_; ++ ++ if( defined $pack ) { ++ $self->{Exported_To} = $pack; ++ } ++ return $self->{Exported_To}; ++} ++ ++#line 498 ++ ++sub ok { ++ my( $self, $test, $name ) = @_; ++ ++ # $test might contain an object which we don't want to accidentally ++ # store, so we turn it into a boolean. ++ $test = $test ? 1 : 0; ++ ++ lock $self->{Curr_Test}; ++ $self->{Curr_Test}++; ++ ++ # In case $name is a string overloaded object, force it to stringify. ++ $self->_unoverload_str( \$name ); ++ ++ $self->diag(<<"ERR") if defined $name and $name =~ /^[\d\s]+$/; ++ You named your test '$name'. You shouldn't use numbers for your test names. ++ Very confusing. ++ERR ++ ++ # Capture the value of $TODO for the rest of this ok() call ++ # so it can more easily be found by other routines. ++ my $todo = $self->todo(); ++ my $in_todo = $self->in_todo; ++ local $self->{Todo} = $todo if $in_todo; ++ ++ $self->_unoverload_str( \$todo ); ++ ++ my $out; ++ my $result = &share( {} ); ++ ++ unless($test) { ++ $out .= "not "; ++ @$result{ 'ok', 'actual_ok' } = ( ( $self->in_todo ? 1 : 0 ), 0 ); ++ } ++ else { ++ @$result{ 'ok', 'actual_ok' } = ( 1, $test ); ++ } ++ ++ $out .= "ok"; ++ $out .= " $self->{Curr_Test}" if $self->use_numbers; ++ ++ if( defined $name ) { ++ $name =~ s|#|\\#|g; # # in a name can confuse Test::Harness. ++ $out .= " - $name"; ++ $result->{name} = $name; ++ } ++ else { ++ $result->{name} = ''; ++ } ++ ++ if( $self->in_todo ) { ++ $out .= " # TODO $todo"; ++ $result->{reason} = $todo; ++ $result->{type} = 'todo'; ++ } ++ else { ++ $result->{reason} = ''; ++ $result->{type} = ''; ++ } ++ ++ $self->{Test_Results}[ $self->{Curr_Test} - 1 ] = $result; ++ $out .= "\n"; ++ ++ $self->_print($out); ++ ++ unless($test) { ++ my $msg = $self->in_todo ? "Failed (TODO)" : "Failed"; ++ $self->_print_to_fh( $self->_diag_fh, "\n" ) if $ENV{HARNESS_ACTIVE}; ++ ++ my( undef, $file, $line ) = $self->caller; ++ if( defined $name ) { ++ $self->diag(qq[ $msg test '$name'\n]); ++ $self->diag(qq[ at $file line $line.\n]); ++ } ++ else { ++ $self->diag(qq[ $msg test at $file line $line.\n]); ++ } ++ } ++ ++ return $test ? 1 : 0; ++} ++ ++sub _unoverload { ++ my $self = shift; ++ my $type = shift; ++ ++ $self->_try(sub { require overload; }, die_on_fail => 1); ++ ++ foreach my $thing (@_) { ++ if( $self->_is_object($$thing) ) { ++ if( my $string_meth = overload::Method( $$thing, $type ) ) { ++ $$thing = $$thing->$string_meth(); ++ } ++ } ++ } ++ ++ return; ++} ++ ++sub _is_object { ++ my( $self, $thing ) = @_; ++ ++ return $self->_try( sub { ref $thing && $thing->isa('UNIVERSAL') } ) ? 1 : 0; ++} ++ ++sub _unoverload_str { ++ my $self = shift; ++ ++ return $self->_unoverload( q[""], @_ ); ++} ++ ++sub _unoverload_num { ++ my $self = shift; ++ ++ $self->_unoverload( '0+', @_ ); ++ ++ for my $val (@_) { ++ next unless $self->_is_dualvar($$val); ++ $$val = $$val + 0; ++ } ++ ++ return; ++} ++ ++# This is a hack to detect a dualvar such as $! ++sub _is_dualvar { ++ my( $self, $val ) = @_; ++ ++ # Objects are not dualvars. ++ return 0 if ref $val; ++ ++ no warnings 'numeric'; ++ my $numval = $val + 0; ++ return $numval != 0 and $numval ne $val ? 1 : 0; ++} ++ ++#line 649 ++ ++sub is_eq { ++ my( $self, $got, $expect, $name ) = @_; ++ local $Level = $Level + 1; ++ ++ $self->_unoverload_str( \$got, \$expect ); ++ ++ if( !defined $got || !defined $expect ) { ++ # undef only matches undef and nothing else ++ my $test = !defined $got && !defined $expect; ++ ++ $self->ok( $test, $name ); ++ $self->_is_diag( $got, 'eq', $expect ) unless $test; ++ return $test; ++ } ++ ++ return $self->cmp_ok( $got, 'eq', $expect, $name ); ++} ++ ++sub is_num { ++ my( $self, $got, $expect, $name ) = @_; ++ local $Level = $Level + 1; ++ ++ $self->_unoverload_num( \$got, \$expect ); ++ ++ if( !defined $got || !defined $expect ) { ++ # undef only matches undef and nothing else ++ my $test = !defined $got && !defined $expect; ++ ++ $self->ok( $test, $name ); ++ $self->_is_diag( $got, '==', $expect ) unless $test; ++ return $test; ++ } ++ ++ return $self->cmp_ok( $got, '==', $expect, $name ); ++} ++ ++sub _diag_fmt { ++ my( $self, $type, $val ) = @_; ++ ++ if( defined $$val ) { ++ if( $type eq 'eq' or $type eq 'ne' ) { ++ # quote and force string context ++ $$val = "'$$val'"; ++ } ++ else { ++ # force numeric context ++ $self->_unoverload_num($val); ++ } ++ } ++ else { ++ $$val = 'undef'; ++ } ++ ++ return; ++} ++ ++sub _is_diag { ++ my( $self, $got, $type, $expect ) = @_; ++ ++ $self->_diag_fmt( $type, $_ ) for \$got, \$expect; ++ ++ local $Level = $Level + 1; ++ return $self->diag(<<"DIAGNOSTIC"); ++ got: $got ++ expected: $expect ++DIAGNOSTIC ++ ++} ++ ++sub _isnt_diag { ++ my( $self, $got, $type ) = @_; ++ ++ $self->_diag_fmt( $type, \$got ); ++ ++ local $Level = $Level + 1; ++ return $self->diag(<<"DIAGNOSTIC"); ++ got: $got ++ expected: anything else ++DIAGNOSTIC ++} ++ ++#line 746 ++ ++sub isnt_eq { ++ my( $self, $got, $dont_expect, $name ) = @_; ++ local $Level = $Level + 1; ++ ++ if( !defined $got || !defined $dont_expect ) { ++ # undef only matches undef and nothing else ++ my $test = defined $got || defined $dont_expect; ++ ++ $self->ok( $test, $name ); ++ $self->_isnt_diag( $got, 'ne' ) unless $test; ++ return $test; ++ } ++ ++ return $self->cmp_ok( $got, 'ne', $dont_expect, $name ); ++} ++ ++sub isnt_num { ++ my( $self, $got, $dont_expect, $name ) = @_; ++ local $Level = $Level + 1; ++ ++ if( !defined $got || !defined $dont_expect ) { ++ # undef only matches undef and nothing else ++ my $test = defined $got || defined $dont_expect; ++ ++ $self->ok( $test, $name ); ++ $self->_isnt_diag( $got, '!=' ) unless $test; ++ return $test; ++ } ++ ++ return $self->cmp_ok( $got, '!=', $dont_expect, $name ); ++} ++ ++#line 797 ++ ++sub like { ++ my( $self, $this, $regex, $name ) = @_; ++ ++ local $Level = $Level + 1; ++ return $self->_regex_ok( $this, $regex, '=~', $name ); ++} ++ ++sub unlike { ++ my( $self, $this, $regex, $name ) = @_; ++ ++ local $Level = $Level + 1; ++ return $self->_regex_ok( $this, $regex, '!~', $name ); ++} ++ ++#line 821 ++ ++my %numeric_cmps = map { ( $_, 1 ) } ( "<", "<=", ">", ">=", "==", "!=", "<=>" ); ++ ++sub cmp_ok { ++ my( $self, $got, $type, $expect, $name ) = @_; ++ ++ my $test; ++ my $error; ++ { ++ ## no critic (BuiltinFunctions::ProhibitStringyEval) ++ ++ local( $@, $!, $SIG{__DIE__} ); # isolate eval ++ ++ my($pack, $file, $line) = $self->caller(); ++ ++ $test = eval qq[ ++#line 1 "cmp_ok [from $file line $line]" ++\$got $type \$expect; ++]; ++ $error = $@; ++ } ++ local $Level = $Level + 1; ++ my $ok = $self->ok( $test, $name ); ++ ++ # Treat overloaded objects as numbers if we're asked to do a ++ # numeric comparison. ++ my $unoverload ++ = $numeric_cmps{$type} ++ ? '_unoverload_num' ++ : '_unoverload_str'; ++ ++ $self->diag(<<"END") if $error; ++An error occurred while using $type: ++------------------------------------ ++$error ++------------------------------------ ++END ++ ++ unless($ok) { ++ $self->$unoverload( \$got, \$expect ); ++ ++ if( $type =~ /^(eq|==)$/ ) { ++ $self->_is_diag( $got, $type, $expect ); ++ } ++ elsif( $type =~ /^(ne|!=)$/ ) { ++ $self->_isnt_diag( $got, $type ); ++ } ++ else { ++ $self->_cmp_diag( $got, $type, $expect ); ++ } ++ } ++ return $ok; ++} ++ ++sub _cmp_diag { ++ my( $self, $got, $type, $expect ) = @_; ++ ++ $got = defined $got ? "'$got'" : 'undef'; ++ $expect = defined $expect ? "'$expect'" : 'undef'; ++ ++ local $Level = $Level + 1; ++ return $self->diag(<<"DIAGNOSTIC"); ++ $got ++ $type ++ $expect ++DIAGNOSTIC ++} ++ ++sub _caller_context { ++ my $self = shift; ++ ++ my( $pack, $file, $line ) = $self->caller(1); ++ ++ my $code = ''; ++ $code .= "#line $line $file\n" if defined $file and defined $line; ++ ++ return $code; ++} ++ ++#line 920 ++ ++sub BAIL_OUT { ++ my( $self, $reason ) = @_; ++ ++ $self->{Bailed_Out} = 1; ++ $self->_print("Bail out! $reason"); ++ exit 255; ++} ++ ++#line 933 ++ ++*BAILOUT = \&BAIL_OUT; ++ ++#line 944 ++ ++sub skip { ++ my( $self, $why ) = @_; ++ $why ||= ''; ++ $self->_unoverload_str( \$why ); ++ ++ lock( $self->{Curr_Test} ); ++ $self->{Curr_Test}++; ++ ++ $self->{Test_Results}[ $self->{Curr_Test} - 1 ] = &share( ++ { ++ 'ok' => 1, ++ actual_ok => 1, ++ name => '', ++ type => 'skip', ++ reason => $why, ++ } ++ ); ++ ++ my $out = "ok"; ++ $out .= " $self->{Curr_Test}" if $self->use_numbers; ++ $out .= " # skip"; ++ $out .= " $why" if length $why; ++ $out .= "\n"; ++ ++ $self->_print($out); ++ ++ return 1; ++} ++ ++#line 985 ++ ++sub todo_skip { ++ my( $self, $why ) = @_; ++ $why ||= ''; ++ ++ lock( $self->{Curr_Test} ); ++ $self->{Curr_Test}++; ++ ++ $self->{Test_Results}[ $self->{Curr_Test} - 1 ] = &share( ++ { ++ 'ok' => 1, ++ actual_ok => 0, ++ name => '', ++ type => 'todo_skip', ++ reason => $why, ++ } ++ ); ++ ++ my $out = "not ok"; ++ $out .= " $self->{Curr_Test}" if $self->use_numbers; ++ $out .= " # TODO & SKIP $why\n"; ++ ++ $self->_print($out); ++ ++ return 1; ++} ++ ++#line 1062 ++ ++sub maybe_regex { ++ my( $self, $regex ) = @_; ++ my $usable_regex = undef; ++ ++ return $usable_regex unless defined $regex; ++ ++ my( $re, $opts ); ++ ++ # Check for qr/foo/ ++ if( _is_qr($regex) ) { ++ $usable_regex = $regex; ++ } ++ # Check for '/foo/' or 'm,foo,' ++ elsif(( $re, $opts ) = $regex =~ m{^ /(.*)/ (\w*) $ }sx or ++ ( undef, $re, $opts ) = $regex =~ m,^ m([^\w\s]) (.+) \1 (\w*) $,sx ++ ) ++ { ++ $usable_regex = length $opts ? "(?$opts)$re" : $re; ++ } ++ ++ return $usable_regex; ++} ++ ++sub _is_qr { ++ my $regex = shift; ++ ++ # is_regexp() checks for regexes in a robust manner, say if they're ++ # blessed. ++ return re::is_regexp($regex) if defined &re::is_regexp; ++ return ref $regex eq 'Regexp'; ++} ++ ++sub _regex_ok { ++ my( $self, $this, $regex, $cmp, $name ) = @_; ++ ++ my $ok = 0; ++ my $usable_regex = $self->maybe_regex($regex); ++ unless( defined $usable_regex ) { ++ local $Level = $Level + 1; ++ $ok = $self->ok( 0, $name ); ++ $self->diag(" '$regex' doesn't look much like a regex to me."); ++ return $ok; ++ } ++ ++ { ++ ## no critic (BuiltinFunctions::ProhibitStringyEval) ++ ++ my $test; ++ my $code = $self->_caller_context; ++ ++ local( $@, $!, $SIG{__DIE__} ); # isolate eval ++ ++ # Yes, it has to look like this or 5.4.5 won't see the #line ++ # directive. ++ # Don't ask me, man, I just work here. ++ $test = eval " ++$code" . q{$test = $this =~ /$usable_regex/ ? 1 : 0}; ++ ++ $test = !$test if $cmp eq '!~'; ++ ++ local $Level = $Level + 1; ++ $ok = $self->ok( $test, $name ); ++ } ++ ++ unless($ok) { ++ $this = defined $this ? "'$this'" : 'undef'; ++ my $match = $cmp eq '=~' ? "doesn't match" : "matches"; ++ ++ local $Level = $Level + 1; ++ $self->diag( sprintf <<'DIAGNOSTIC', $this, $match, $regex ); ++ %s ++ %13s '%s' ++DIAGNOSTIC ++ ++ } ++ ++ return $ok; ++} ++ ++# I'm not ready to publish this. It doesn't deal with array return ++# values from the code or context. ++ ++#line 1162 ++ ++sub _try { ++ my( $self, $code, %opts ) = @_; ++ ++ my $error; ++ my $return; ++ { ++ local $!; # eval can mess up $! ++ local $@; # don't set $@ in the test ++ local $SIG{__DIE__}; # don't trip an outside DIE handler. ++ $return = eval { $code->() }; ++ $error = $@; ++ } ++ ++ die $error if $error and $opts{die_on_fail}; ++ ++ return wantarray ? ( $return, $error ) : $return; ++} ++ ++#line 1191 ++ ++sub is_fh { ++ my $self = shift; ++ my $maybe_fh = shift; ++ return 0 unless defined $maybe_fh; ++ ++ return 1 if ref $maybe_fh eq 'GLOB'; # its a glob ref ++ return 1 if ref \$maybe_fh eq 'GLOB'; # its a glob ++ ++ return eval { $maybe_fh->isa("IO::Handle") } || ++ # 5.5.4's tied() and can() doesn't like getting undef ++ eval { ( tied($maybe_fh) || '' )->can('TIEHANDLE') }; ++} ++ ++#line 1235 ++ ++sub level { ++ my( $self, $level ) = @_; ++ ++ if( defined $level ) { ++ $Level = $level; ++ } ++ return $Level; ++} ++ ++#line 1267 ++ ++sub use_numbers { ++ my( $self, $use_nums ) = @_; ++ ++ if( defined $use_nums ) { ++ $self->{Use_Nums} = $use_nums; ++ } ++ return $self->{Use_Nums}; ++} ++ ++#line 1300 ++ ++foreach my $attribute (qw(No_Header No_Ending No_Diag)) { ++ my $method = lc $attribute; ++ ++ my $code = sub { ++ my( $self, $no ) = @_; ++ ++ if( defined $no ) { ++ $self->{$attribute} = $no; ++ } ++ return $self->{$attribute}; ++ }; ++ ++ no strict 'refs'; ## no critic ++ *{ __PACKAGE__ . '::' . $method } = $code; ++} ++ ++#line 1353 ++ ++sub diag { ++ my $self = shift; ++ ++ $self->_print_comment( $self->_diag_fh, @_ ); ++} ++ ++#line 1368 ++ ++sub note { ++ my $self = shift; ++ ++ $self->_print_comment( $self->output, @_ ); ++} ++ ++sub _diag_fh { ++ my $self = shift; ++ ++ local $Level = $Level + 1; ++ return $self->in_todo ? $self->todo_output : $self->failure_output; ++} ++ ++sub _print_comment { ++ my( $self, $fh, @msgs ) = @_; ++ ++ return if $self->no_diag; ++ return unless @msgs; ++ ++ # Prevent printing headers when compiling (i.e. -c) ++ return if $^C; ++ ++ # Smash args together like print does. ++ # Convert undef to 'undef' so its readable. ++ my $msg = join '', map { defined($_) ? $_ : 'undef' } @msgs; ++ ++ # Escape the beginning, _print will take care of the rest. ++ $msg =~ s/^/# /; ++ ++ local $Level = $Level + 1; ++ $self->_print_to_fh( $fh, $msg ); ++ ++ return 0; ++} ++ ++#line 1418 ++ ++sub explain { ++ my $self = shift; ++ ++ return map { ++ ref $_ ++ ? do { ++ $self->_try(sub { require Data::Dumper }, die_on_fail => 1); ++ ++ my $dumper = Data::Dumper->new( [$_] ); ++ $dumper->Indent(1)->Terse(1); ++ $dumper->Sortkeys(1) if $dumper->can("Sortkeys"); ++ $dumper->Dump; ++ } ++ : $_ ++ } @_; ++} ++ ++#line 1447 ++ ++sub _print { ++ my $self = shift; ++ return $self->_print_to_fh( $self->output, @_ ); ++} ++ ++sub _print_to_fh { ++ my( $self, $fh, @msgs ) = @_; ++ ++ # Prevent printing headers when only compiling. Mostly for when ++ # tests are deparsed with B::Deparse ++ return if $^C; ++ ++ my $msg = join '', @msgs; ++ ++ local( $\, $", $, ) = ( undef, ' ', '' ); ++ ++ # Escape each line after the first with a # so we don't ++ # confuse Test::Harness. ++ $msg =~ s{\n(?!\z)}{\n# }sg; ++ ++ # Stick a newline on the end if it needs it. ++ $msg .= "\n" unless $msg =~ /\n\z/; ++ ++ return print $fh $msg; ++} ++ ++#line 1506 ++ ++sub output { ++ my( $self, $fh ) = @_; ++ ++ if( defined $fh ) { ++ $self->{Out_FH} = $self->_new_fh($fh); ++ } ++ return $self->{Out_FH}; ++} ++ ++sub failure_output { ++ my( $self, $fh ) = @_; ++ ++ if( defined $fh ) { ++ $self->{Fail_FH} = $self->_new_fh($fh); ++ } ++ return $self->{Fail_FH}; ++} ++ ++sub todo_output { ++ my( $self, $fh ) = @_; ++ ++ if( defined $fh ) { ++ $self->{Todo_FH} = $self->_new_fh($fh); ++ } ++ return $self->{Todo_FH}; ++} ++ ++sub _new_fh { ++ my $self = shift; ++ my($file_or_fh) = shift; ++ ++ my $fh; ++ if( $self->is_fh($file_or_fh) ) { ++ $fh = $file_or_fh; ++ } ++ elsif( ref $file_or_fh eq 'SCALAR' ) { ++ # Scalar refs as filehandles was added in 5.8. ++ if( $] >= 5.008 ) { ++ open $fh, ">>", $file_or_fh ++ or $self->croak("Can't open scalar ref $file_or_fh: $!"); ++ } ++ # Emulate scalar ref filehandles with a tie. ++ else { ++ $fh = Test::Builder::IO::Scalar->new($file_or_fh) ++ or $self->croak("Can't tie scalar ref $file_or_fh"); ++ } ++ } ++ else { ++ open $fh, ">", $file_or_fh ++ or $self->croak("Can't open test output log $file_or_fh: $!"); ++ _autoflush($fh); ++ } ++ ++ return $fh; ++} ++ ++sub _autoflush { ++ my($fh) = shift; ++ my $old_fh = select $fh; ++ $| = 1; ++ select $old_fh; ++ ++ return; ++} ++ ++my( $Testout, $Testerr ); ++ ++sub _dup_stdhandles { ++ my $self = shift; ++ ++ $self->_open_testhandles; ++ ++ # Set everything to unbuffered else plain prints to STDOUT will ++ # come out in the wrong order from our own prints. ++ _autoflush($Testout); ++ _autoflush( \*STDOUT ); ++ _autoflush($Testerr); ++ _autoflush( \*STDERR ); ++ ++ $self->reset_outputs; ++ ++ return; ++} ++ ++sub _open_testhandles { ++ my $self = shift; ++ ++ return if $self->{Opened_Testhandles}; ++ ++ # We dup STDOUT and STDERR so people can change them in their ++ # test suites while still getting normal test output. ++ open( $Testout, ">&STDOUT" ) or die "Can't dup STDOUT: $!"; ++ open( $Testerr, ">&STDERR" ) or die "Can't dup STDERR: $!"; ++ ++ # $self->_copy_io_layers( \*STDOUT, $Testout ); ++ # $self->_copy_io_layers( \*STDERR, $Testerr ); ++ ++ $self->{Opened_Testhandles} = 1; ++ ++ return; ++} ++ ++sub _copy_io_layers { ++ my( $self, $src, $dst ) = @_; ++ ++ $self->_try( ++ sub { ++ require PerlIO; ++ my @src_layers = PerlIO::get_layers($src); ++ ++ binmode $dst, join " ", map ":$_", @src_layers if @src_layers; ++ } ++ ); ++ ++ return; ++} ++ ++#line 1631 ++ ++sub reset_outputs { ++ my $self = shift; ++ ++ $self->output ($Testout); ++ $self->failure_output($Testerr); ++ $self->todo_output ($Testout); ++ ++ return; ++} ++ ++#line 1657 ++ ++sub _message_at_caller { ++ my $self = shift; ++ ++ local $Level = $Level + 1; ++ my( $pack, $file, $line ) = $self->caller; ++ return join( "", @_ ) . " at $file line $line.\n"; ++} ++ ++sub carp { ++ my $self = shift; ++ return warn $self->_message_at_caller(@_); ++} ++ ++sub croak { ++ my $self = shift; ++ return die $self->_message_at_caller(@_); ++} ++ ++ ++#line 1697 ++ ++sub current_test { ++ my( $self, $num ) = @_; ++ ++ lock( $self->{Curr_Test} ); ++ if( defined $num ) { ++ $self->{Curr_Test} = $num; ++ ++ # If the test counter is being pushed forward fill in the details. ++ my $test_results = $self->{Test_Results}; ++ if( $num > @$test_results ) { ++ my $start = @$test_results ? @$test_results : 0; ++ for( $start .. $num - 1 ) { ++ $test_results->[$_] = &share( ++ { ++ 'ok' => 1, ++ actual_ok => undef, ++ reason => 'incrementing test number', ++ type => 'unknown', ++ name => undef ++ } ++ ); ++ } ++ } ++ # If backward, wipe history. Its their funeral. ++ elsif( $num < @$test_results ) { ++ $#{$test_results} = $num - 1; ++ } ++ } ++ return $self->{Curr_Test}; ++} ++ ++#line 1739 ++ ++sub summary { ++ my($self) = shift; ++ ++ return map { $_->{'ok'} } @{ $self->{Test_Results} }; ++} ++ ++#line 1794 ++ ++sub details { ++ my $self = shift; ++ return @{ $self->{Test_Results} }; ++} ++ ++#line 1823 ++ ++sub todo { ++ my( $self, $pack ) = @_; ++ ++ return $self->{Todo} if defined $self->{Todo}; ++ ++ local $Level = $Level + 1; ++ my $todo = $self->find_TODO($pack); ++ return $todo if defined $todo; ++ ++ return ''; ++} ++ ++#line 1845 ++ ++sub find_TODO { ++ my( $self, $pack ) = @_; ++ ++ $pack = $pack || $self->caller(1) || $self->exported_to; ++ return unless $pack; ++ ++ no strict 'refs'; ## no critic ++ return ${ $pack . '::TODO' }; ++} ++ ++#line 1863 ++ ++sub in_todo { ++ my $self = shift; ++ ++ local $Level = $Level + 1; ++ return( defined $self->{Todo} || $self->find_TODO ) ? 1 : 0; ++} ++ ++#line 1913 ++ ++sub todo_start { ++ my $self = shift; ++ my $message = @_ ? shift : ''; ++ ++ $self->{Start_Todo}++; ++ if( $self->in_todo ) { ++ push @{ $self->{Todo_Stack} } => $self->todo; ++ } ++ $self->{Todo} = $message; ++ ++ return; ++} ++ ++#line 1935 ++ ++sub todo_end { ++ my $self = shift; ++ ++ if( !$self->{Start_Todo} ) { ++ $self->croak('todo_end() called without todo_start()'); ++ } ++ ++ $self->{Start_Todo}--; ++ ++ if( $self->{Start_Todo} && @{ $self->{Todo_Stack} } ) { ++ $self->{Todo} = pop @{ $self->{Todo_Stack} }; ++ } ++ else { ++ delete $self->{Todo}; ++ } ++ ++ return; ++} ++ ++#line 1968 ++ ++sub caller { ## no critic (Subroutines::ProhibitBuiltinHomonyms) ++ my( $self, $height ) = @_; ++ $height ||= 0; ++ ++ my $level = $self->level + $height + 1; ++ my @caller; ++ do { ++ @caller = CORE::caller( $level ); ++ $level--; ++ } until @caller; ++ return wantarray ? @caller : $caller[0]; ++} ++ ++#line 1985 ++ ++#line 1999 ++ ++#'# ++sub _sanity_check { ++ my $self = shift; ++ ++ $self->_whoa( $self->{Curr_Test} < 0, 'Says here you ran a negative number of tests!' ); ++ $self->_whoa( $self->{Curr_Test} != @{ $self->{Test_Results} }, ++ 'Somehow you got a different number of results than tests ran!' ); ++ ++ return; ++} ++ ++#line 2020 ++ ++sub _whoa { ++ my( $self, $check, $desc ) = @_; ++ if($check) { ++ local $Level = $Level + 1; ++ $self->croak(<<"WHOA"); ++WHOA! $desc ++This should never happen! Please contact the author immediately! ++WHOA ++ } ++ ++ return; ++} ++ ++#line 2044 ++ ++sub _my_exit { ++ $? = $_[0]; ## no critic (Variables::RequireLocalizedPunctuationVars) ++ ++ return 1; ++} ++ ++#line 2056 ++ ++sub _ending { ++ my $self = shift; ++ ++ my $real_exit_code = $?; ++ ++ # Don't bother with an ending if this is a forked copy. Only the parent ++ # should do the ending. ++ if( $self->{Original_Pid} != $$ ) { ++ return; ++ } ++ ++ # Ran tests but never declared a plan or hit done_testing ++ if( !$self->{Have_Plan} and $self->{Curr_Test} ) { ++ $self->diag("Tests were run but no plan was declared and done_testing() was not seen."); ++ } ++ ++ # Exit if plan() was never called. This is so "require Test::Simple" ++ # doesn't puke. ++ if( !$self->{Have_Plan} ) { ++ return; ++ } ++ ++ # Don't do an ending if we bailed out. ++ if( $self->{Bailed_Out} ) { ++ return; ++ } ++ ++ # Figure out if we passed or failed and print helpful messages. ++ my $test_results = $self->{Test_Results}; ++ if(@$test_results) { ++ # The plan? We have no plan. ++ if( $self->{No_Plan} ) { ++ $self->_output_plan($self->{Curr_Test}) unless $self->no_header; ++ $self->{Expected_Tests} = $self->{Curr_Test}; ++ } ++ ++ # Auto-extended arrays and elements which aren't explicitly ++ # filled in with a shared reference will puke under 5.8.0 ++ # ithreads. So we have to fill them in by hand. :( ++ my $empty_result = &share( {} ); ++ for my $idx ( 0 .. $self->{Expected_Tests} - 1 ) { ++ $test_results->[$idx] = $empty_result ++ unless defined $test_results->[$idx]; ++ } ++ ++ my $num_failed = grep !$_->{'ok'}, @{$test_results}[ 0 .. $self->{Curr_Test} - 1 ]; ++ ++ my $num_extra = $self->{Curr_Test} - $self->{Expected_Tests}; ++ ++ if( $num_extra != 0 ) { ++ my $s = $self->{Expected_Tests} == 1 ? '' : 's'; ++ $self->diag(<<"FAIL"); ++Looks like you planned $self->{Expected_Tests} test$s but ran $self->{Curr_Test}. ++FAIL ++ } ++ ++ if($num_failed) { ++ my $num_tests = $self->{Curr_Test}; ++ my $s = $num_failed == 1 ? '' : 's'; ++ ++ my $qualifier = $num_extra == 0 ? '' : ' run'; ++ ++ $self->diag(<<"FAIL"); ++Looks like you failed $num_failed test$s of $num_tests$qualifier. ++FAIL ++ } ++ ++ if($real_exit_code) { ++ $self->diag(<<"FAIL"); ++Looks like your test exited with $real_exit_code just after $self->{Curr_Test}. ++FAIL ++ ++ _my_exit($real_exit_code) && return; ++ } ++ ++ my $exit_code; ++ if($num_failed) { ++ $exit_code = $num_failed <= 254 ? $num_failed : 254; ++ } ++ elsif( $num_extra != 0 ) { ++ $exit_code = 255; ++ } ++ else { ++ $exit_code = 0; ++ } ++ ++ _my_exit($exit_code) && return; ++ } ++ elsif( $self->{Skip_All} ) { ++ _my_exit(0) && return; ++ } ++ elsif($real_exit_code) { ++ $self->diag(<<"FAIL"); ++Looks like your test exited with $real_exit_code before it could output anything. ++FAIL ++ _my_exit($real_exit_code) && return; ++ } ++ else { ++ $self->diag("No tests run!\n"); ++ _my_exit(255) && return; ++ } ++ ++ $self->_whoa( 1, "We fell off the end of _ending()" ); ++} ++ ++END { ++ $Test->_ending if defined $Test and !$Test->no_ending; ++} ++ ++#line 2236 ++ ++1; ++ +Index: 0.8/modules/nginx-echo/test/inc/Test/Builder/Module.pm +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ 0.8/modules/nginx-echo/test/inc/Test/Builder/Module.pm 2010-10-31 07:35:23.648338001 +0000 +@@ -0,0 +1,81 @@ ++#line 1 ++package Test::Builder::Module; ++ ++use strict; ++ ++use Test::Builder; ++ ++require Exporter; ++our @ISA = qw(Exporter); ++ ++our $VERSION = '0.92'; ++$VERSION = eval $VERSION; ## no critic (BuiltinFunctions::ProhibitStringyEval) ++ ++# 5.004's Exporter doesn't have export_to_level. ++my $_export_to_level = sub { ++ my $pkg = shift; ++ my $level = shift; ++ (undef) = shift; # redundant arg ++ my $callpkg = caller($level); ++ $pkg->export( $callpkg, @_ ); ++}; ++ ++#line 82 ++ ++sub import { ++ my($class) = shift; ++ ++ # Don't run all this when loading ourself. ++ return 1 if $class eq 'Test::Builder::Module'; ++ ++ my $test = $class->builder; ++ ++ my $caller = caller; ++ ++ $test->exported_to($caller); ++ ++ $class->import_extra( \@_ ); ++ my(@imports) = $class->_strip_imports( \@_ ); ++ ++ $test->plan(@_); ++ ++ $class->$_export_to_level( 1, $class, @imports ); ++} ++ ++sub _strip_imports { ++ my $class = shift; ++ my $list = shift; ++ ++ my @imports = (); ++ my @other = (); ++ my $idx = 0; ++ while( $idx <= $#{$list} ) { ++ my $item = $list->[$idx]; ++ ++ if( defined $item and $item eq 'import' ) { ++ push @imports, @{ $list->[ $idx + 1 ] }; ++ $idx++; ++ } ++ else { ++ push @other, $item; ++ } ++ ++ $idx++; ++ } ++ ++ @$list = @other; ++ ++ return @imports; ++} ++ ++#line 145 ++ ++sub import_extra { } ++ ++#line 175 ++ ++sub builder { ++ return Test::Builder->new; ++} ++ ++1; +Index: 0.8/modules/nginx-echo/test/inc/Test/More.pm +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ 0.8/modules/nginx-echo/test/inc/Test/More.pm 2010-10-31 07:35:47.678338000 +0000 +@@ -0,0 +1,735 @@ ++#line 1 ++package Test::More; ++ ++use 5.006; ++use strict; ++use warnings; ++ ++#---- perlcritic exemptions. ----# ++ ++# We use a lot of subroutine prototypes ++## no critic (Subroutines::ProhibitSubroutinePrototypes) ++ ++# Can't use Carp because it might cause use_ok() to accidentally succeed ++# even though the module being used forgot to use Carp. Yes, this ++# actually happened. ++sub _carp { ++ my( $file, $line ) = ( caller(1) )[ 1, 2 ]; ++ return warn @_, " at $file line $line\n"; ++} ++ ++our $VERSION = '0.92'; ++$VERSION = eval $VERSION; ## no critic (BuiltinFunctions::ProhibitStringyEval) ++ ++use Test::Builder::Module; ++our @ISA = qw(Test::Builder::Module); ++our @EXPORT = qw(ok use_ok require_ok ++ is isnt like unlike is_deeply ++ cmp_ok ++ skip todo todo_skip ++ pass fail ++ eq_array eq_hash eq_set ++ $TODO ++ plan ++ done_testing ++ can_ok isa_ok new_ok ++ diag note explain ++ BAIL_OUT ++); ++ ++#line 163 ++ ++sub plan { ++ my $tb = Test::More->builder; ++ ++ return $tb->plan(@_); ++} ++ ++# This implements "use Test::More 'no_diag'" but the behavior is ++# deprecated. ++sub import_extra { ++ my $class = shift; ++ my $list = shift; ++ ++ my @other = (); ++ my $idx = 0; ++ while( $idx <= $#{$list} ) { ++ my $item = $list->[$idx]; ++ ++ if( defined $item and $item eq 'no_diag' ) { ++ $class->builder->no_diag(1); ++ } ++ else { ++ push @other, $item; ++ } ++ ++ $idx++; ++ } ++ ++ @$list = @other; ++ ++ return; ++} ++ ++#line 216 ++ ++sub done_testing { ++ my $tb = Test::More->builder; ++ $tb->done_testing(@_); ++} ++ ++#line 289 ++ ++sub ok ($;$) { ++ my( $test, $name ) = @_; ++ my $tb = Test::More->builder; ++ ++ return $tb->ok( $test, $name ); ++} ++ ++#line 367 ++ ++sub is ($$;$) { ++ my $tb = Test::More->builder; ++ ++ return $tb->is_eq(@_); ++} ++ ++sub isnt ($$;$) { ++ my $tb = Test::More->builder; ++ ++ return $tb->isnt_eq(@_); ++} ++ ++*isn't = \&isnt; ++ ++#line 411 ++ ++sub like ($$;$) { ++ my $tb = Test::More->builder; ++ ++ return $tb->like(@_); ++} ++ ++#line 426 ++ ++sub unlike ($$;$) { ++ my $tb = Test::More->builder; ++ ++ return $tb->unlike(@_); ++} ++ ++#line 471 ++ ++sub cmp_ok($$$;$) { ++ my $tb = Test::More->builder; ++ ++ return $tb->cmp_ok(@_); ++} ++ ++#line 506 ++ ++sub can_ok ($@) { ++ my( $proto, @methods ) = @_; ++ my $class = ref $proto || $proto; ++ my $tb = Test::More->builder; ++ ++ unless($class) { ++ my $ok = $tb->ok( 0, "->can(...)" ); ++ $tb->diag(' can_ok() called with empty class or reference'); ++ return $ok; ++ } ++ ++ unless(@methods) { ++ my $ok = $tb->ok( 0, "$class->can(...)" ); ++ $tb->diag(' can_ok() called with no methods'); ++ return $ok; ++ } ++ ++ my @nok = (); ++ foreach my $method (@methods) { ++ $tb->_try( sub { $proto->can($method) } ) or push @nok, $method; ++ } ++ ++ my $name = (@methods == 1) ? "$class->can('$methods[0]')" : ++ "$class->can(...)" ; ++ ++ my $ok = $tb->ok( !@nok, $name ); ++ ++ $tb->diag( map " $class->can('$_') failed\n", @nok ); ++ ++ return $ok; ++} ++ ++#line 572 ++ ++sub isa_ok ($$;$) { ++ my( $object, $class, $obj_name ) = @_; ++ my $tb = Test::More->builder; ++ ++ my $diag; ++ ++ if( !defined $object ) { ++ $obj_name = 'The thing' unless defined $obj_name; ++ $diag = "$obj_name isn't defined"; ++ } ++ else { ++ my $whatami = ref $object ? 'object' : 'class'; ++ # We can't use UNIVERSAL::isa because we want to honor isa() overrides ++ my( $rslt, $error ) = $tb->_try( sub { $object->isa($class) } ); ++ if($error) { ++ if( $error =~ /^Can't call method "isa" on unblessed reference/ ) { ++ # Its an unblessed reference ++ $obj_name = 'The reference' unless defined $obj_name; ++ if( !UNIVERSAL::isa( $object, $class ) ) { ++ my $ref = ref $object; ++ $diag = "$obj_name isn't a '$class' it's a '$ref'"; ++ } ++ } ++ elsif( $error =~ /Can't call method "isa" without a package/ ) { ++ # It's something that can't even be a class ++ $diag = "$obj_name isn't a class or reference"; ++ } ++ else { ++ die <isa on your $whatami and got some weird error. ++Here's the error. ++$error ++WHOA ++ } ++ } ++ else { ++ $obj_name = "The $whatami" unless defined $obj_name; ++ if( !$rslt ) { ++ my $ref = ref $object; ++ $diag = "$obj_name isn't a '$class' it's a '$ref'"; ++ } ++ } ++ } ++ ++ my $name = "$obj_name isa $class"; ++ my $ok; ++ if($diag) { ++ $ok = $tb->ok( 0, $name ); ++ $tb->diag(" $diag\n"); ++ } ++ else { ++ $ok = $tb->ok( 1, $name ); ++ } ++ ++ return $ok; ++} ++ ++#line 650 ++ ++sub new_ok { ++ my $tb = Test::More->builder; ++ $tb->croak("new_ok() must be given at least a class") unless @_; ++ ++ my( $class, $args, $object_name ) = @_; ++ ++ $args ||= []; ++ $object_name = "The object" unless defined $object_name; ++ ++ my $obj; ++ my( $success, $error ) = $tb->_try( sub { $obj = $class->new(@$args); 1 } ); ++ if($success) { ++ local $Test::Builder::Level = $Test::Builder::Level + 1; ++ isa_ok $obj, $class, $object_name; ++ } ++ else { ++ $tb->ok( 0, "new() died" ); ++ $tb->diag(" Error was: $error"); ++ } ++ ++ return $obj; ++} ++ ++#line 690 ++ ++sub pass (;$) { ++ my $tb = Test::More->builder; ++ ++ return $tb->ok( 1, @_ ); ++} ++ ++sub fail (;$) { ++ my $tb = Test::More->builder; ++ ++ return $tb->ok( 0, @_ ); ++} ++ ++#line 753 ++ ++sub use_ok ($;@) { ++ my( $module, @imports ) = @_; ++ @imports = () unless @imports; ++ my $tb = Test::More->builder; ++ ++ my( $pack, $filename, $line ) = caller; ++ ++ my $code; ++ if( @imports == 1 and $imports[0] =~ /^\d+(?:\.\d+)?$/ ) { ++ # probably a version check. Perl needs to see the bare number ++ # for it to work with non-Exporter based modules. ++ $code = <ok( $eval_result, "use $module;" ); ++ ++ unless($ok) { ++ chomp $eval_error; ++ $@ =~ s{^BEGIN failed--compilation aborted at .*$} ++ {BEGIN failed--compilation aborted at $filename line $line.}m; ++ $tb->diag(<builder; ++ ++ my $pack = caller; ++ ++ # Try to deterine if we've been given a module name or file. ++ # Module names must be barewords, files not. ++ $module = qq['$module'] unless _is_module_name($module); ++ ++ my $code = <ok( $eval_result, "require $module;" ); ++ ++ unless($ok) { ++ chomp $eval_error; ++ $tb->diag(<builder; ++ ++ unless( @_ == 2 or @_ == 3 ) { ++ my $msg = <<'WARNING'; ++is_deeply() takes two or three args, you gave %d. ++This usually means you passed an array or hash instead ++of a reference to it ++WARNING ++ chop $msg; # clip off newline so carp() will put in line/file ++ ++ _carp sprintf $msg, scalar @_; ++ ++ return $tb->ok(0); ++ } ++ ++ my( $got, $expected, $name ) = @_; ++ ++ $tb->_unoverload_str( \$expected, \$got ); ++ ++ my $ok; ++ if( !ref $got and !ref $expected ) { # neither is a reference ++ $ok = $tb->is_eq( $got, $expected, $name ); ++ } ++ elsif( !ref $got xor !ref $expected ) { # one's a reference, one isn't ++ $ok = $tb->ok( 0, $name ); ++ $tb->diag( _format_stack({ vals => [ $got, $expected ] }) ); ++ } ++ else { # both references ++ local @Data_Stack = (); ++ if( _deep_check( $got, $expected ) ) { ++ $ok = $tb->ok( 1, $name ); ++ } ++ else { ++ $ok = $tb->ok( 0, $name ); ++ $tb->diag( _format_stack(@Data_Stack) ); ++ } ++ } ++ ++ return $ok; ++} ++ ++sub _format_stack { ++ my(@Stack) = @_; ++ ++ my $var = '$FOO'; ++ my $did_arrow = 0; ++ foreach my $entry (@Stack) { ++ my $type = $entry->{type} || ''; ++ my $idx = $entry->{'idx'}; ++ if( $type eq 'HASH' ) { ++ $var .= "->" unless $did_arrow++; ++ $var .= "{$idx}"; ++ } ++ elsif( $type eq 'ARRAY' ) { ++ $var .= "->" unless $did_arrow++; ++ $var .= "[$idx]"; ++ } ++ elsif( $type eq 'REF' ) { ++ $var = "\${$var}"; ++ } ++ } ++ ++ my @vals = @{ $Stack[-1]{vals} }[ 0, 1 ]; ++ my @vars = (); ++ ( $vars[0] = $var ) =~ s/\$FOO/ \$got/; ++ ( $vars[1] = $var ) =~ s/\$FOO/\$expected/; ++ ++ my $out = "Structures begin differing at:\n"; ++ foreach my $idx ( 0 .. $#vals ) { ++ my $val = $vals[$idx]; ++ $vals[$idx] ++ = !defined $val ? 'undef' ++ : _dne($val) ? "Does not exist" ++ : ref $val ? "$val" ++ : "'$val'"; ++ } ++ ++ $out .= "$vars[0] = $vals[0]\n"; ++ $out .= "$vars[1] = $vals[1]\n"; ++ ++ $out =~ s/^/ /msg; ++ return $out; ++} ++ ++sub _type { ++ my $thing = shift; ++ ++ return '' if !ref $thing; ++ ++ for my $type (qw(ARRAY HASH REF SCALAR GLOB CODE Regexp)) { ++ return $type if UNIVERSAL::isa( $thing, $type ); ++ } ++ ++ return ''; ++} ++ ++#line 1059 ++ ++sub diag { ++ return Test::More->builder->diag(@_); ++} ++ ++sub note { ++ return Test::More->builder->note(@_); ++} ++ ++#line 1085 ++ ++sub explain { ++ return Test::More->builder->explain(@_); ++} ++ ++#line 1151 ++ ++## no critic (Subroutines::RequireFinalReturn) ++sub skip { ++ my( $why, $how_many ) = @_; ++ my $tb = Test::More->builder; ++ ++ unless( defined $how_many ) { ++ # $how_many can only be avoided when no_plan is in use. ++ _carp "skip() needs to know \$how_many tests are in the block" ++ unless $tb->has_plan eq 'no_plan'; ++ $how_many = 1; ++ } ++ ++ if( defined $how_many and $how_many =~ /\D/ ) { ++ _carp ++ "skip() was passed a non-numeric number of tests. Did you get the arguments backwards?"; ++ $how_many = 1; ++ } ++ ++ for( 1 .. $how_many ) { ++ $tb->skip($why); ++ } ++ ++ no warnings 'exiting'; ++ last SKIP; ++} ++ ++#line 1238 ++ ++sub todo_skip { ++ my( $why, $how_many ) = @_; ++ my $tb = Test::More->builder; ++ ++ unless( defined $how_many ) { ++ # $how_many can only be avoided when no_plan is in use. ++ _carp "todo_skip() needs to know \$how_many tests are in the block" ++ unless $tb->has_plan eq 'no_plan'; ++ $how_many = 1; ++ } ++ ++ for( 1 .. $how_many ) { ++ $tb->todo_skip($why); ++ } ++ ++ no warnings 'exiting'; ++ last TODO; ++} ++ ++#line 1293 ++ ++sub BAIL_OUT { ++ my $reason = shift; ++ my $tb = Test::More->builder; ++ ++ $tb->BAIL_OUT($reason); ++} ++ ++#line 1332 ++ ++#'# ++sub eq_array { ++ local @Data_Stack = (); ++ _deep_check(@_); ++} ++ ++sub _eq_array { ++ my( $a1, $a2 ) = @_; ++ ++ if( grep _type($_) ne 'ARRAY', $a1, $a2 ) { ++ warn "eq_array passed a non-array ref"; ++ return 0; ++ } ++ ++ return 1 if $a1 eq $a2; ++ ++ my $ok = 1; ++ my $max = $#$a1 > $#$a2 ? $#$a1 : $#$a2; ++ for( 0 .. $max ) { ++ my $e1 = $_ > $#$a1 ? $DNE : $a1->[$_]; ++ my $e2 = $_ > $#$a2 ? $DNE : $a2->[$_]; ++ ++ push @Data_Stack, { type => 'ARRAY', idx => $_, vals => [ $e1, $e2 ] }; ++ $ok = _deep_check( $e1, $e2 ); ++ pop @Data_Stack if $ok; ++ ++ last unless $ok; ++ } ++ ++ return $ok; ++} ++ ++sub _deep_check { ++ my( $e1, $e2 ) = @_; ++ my $tb = Test::More->builder; ++ ++ my $ok = 0; ++ ++ # Effectively turn %Refs_Seen into a stack. This avoids picking up ++ # the same referenced used twice (such as [\$a, \$a]) to be considered ++ # circular. ++ local %Refs_Seen = %Refs_Seen; ++ ++ { ++ # Quiet uninitialized value warnings when comparing undefs. ++ no warnings 'uninitialized'; ++ ++ $tb->_unoverload_str( \$e1, \$e2 ); ++ ++ # Either they're both references or both not. ++ my $same_ref = !( !ref $e1 xor !ref $e2 ); ++ my $not_ref = ( !ref $e1 and !ref $e2 ); ++ ++ if( defined $e1 xor defined $e2 ) { ++ $ok = 0; ++ } ++ elsif( !defined $e1 and !defined $e2 ) { ++ # Shortcut if they're both defined. ++ $ok = 1; ++ } ++ elsif( _dne($e1) xor _dne($e2) ) { ++ $ok = 0; ++ } ++ elsif( $same_ref and( $e1 eq $e2 ) ) { ++ $ok = 1; ++ } ++ elsif($not_ref) { ++ push @Data_Stack, { type => '', vals => [ $e1, $e2 ] }; ++ $ok = 0; ++ } ++ else { ++ if( $Refs_Seen{$e1} ) { ++ return $Refs_Seen{$e1} eq $e2; ++ } ++ else { ++ $Refs_Seen{$e1} = "$e2"; ++ } ++ ++ my $type = _type($e1); ++ $type = 'DIFFERENT' unless _type($e2) eq $type; ++ ++ if( $type eq 'DIFFERENT' ) { ++ push @Data_Stack, { type => $type, vals => [ $e1, $e2 ] }; ++ $ok = 0; ++ } ++ elsif( $type eq 'ARRAY' ) { ++ $ok = _eq_array( $e1, $e2 ); ++ } ++ elsif( $type eq 'HASH' ) { ++ $ok = _eq_hash( $e1, $e2 ); ++ } ++ elsif( $type eq 'REF' ) { ++ push @Data_Stack, { type => $type, vals => [ $e1, $e2 ] }; ++ $ok = _deep_check( $$e1, $$e2 ); ++ pop @Data_Stack if $ok; ++ } ++ elsif( $type eq 'SCALAR' ) { ++ push @Data_Stack, { type => 'REF', vals => [ $e1, $e2 ] }; ++ $ok = _deep_check( $$e1, $$e2 ); ++ pop @Data_Stack if $ok; ++ } ++ elsif($type) { ++ push @Data_Stack, { type => $type, vals => [ $e1, $e2 ] }; ++ $ok = 0; ++ } ++ else { ++ _whoa( 1, "No type in _deep_check" ); ++ } ++ } ++ } ++ ++ return $ok; ++} ++ ++sub _whoa { ++ my( $check, $desc ) = @_; ++ if($check) { ++ die <<"WHOA"; ++WHOA! $desc ++This should never happen! Please contact the author immediately! ++WHOA ++ } ++} ++ ++#line 1465 ++ ++sub eq_hash { ++ local @Data_Stack = (); ++ return _deep_check(@_); ++} ++ ++sub _eq_hash { ++ my( $a1, $a2 ) = @_; ++ ++ if( grep _type($_) ne 'HASH', $a1, $a2 ) { ++ warn "eq_hash passed a non-hash ref"; ++ return 0; ++ } ++ ++ return 1 if $a1 eq $a2; ++ ++ my $ok = 1; ++ my $bigger = keys %$a1 > keys %$a2 ? $a1 : $a2; ++ foreach my $k ( keys %$bigger ) { ++ my $e1 = exists $a1->{$k} ? $a1->{$k} : $DNE; ++ my $e2 = exists $a2->{$k} ? $a2->{$k} : $DNE; ++ ++ push @Data_Stack, { type => 'HASH', idx => $k, vals => [ $e1, $e2 ] }; ++ $ok = _deep_check( $e1, $e2 ); ++ pop @Data_Stack if $ok; ++ ++ last unless $ok; ++ } ++ ++ return $ok; ++} ++ ++#line 1522 ++ ++sub eq_set { ++ my( $a1, $a2 ) = @_; ++ return 0 unless @$a1 == @$a2; ++ ++ no warnings 'uninitialized'; ++ ++ # It really doesn't matter how we sort them, as long as both arrays are ++ # sorted with the same algorithm. ++ # ++ # Ensure that references are not accidentally treated the same as a ++ # string containing the reference. ++ # ++ # Have to inline the sort routine due to a threading/sort bug. ++ # See [rt.cpan.org 6782] ++ # ++ # I don't know how references would be sorted so we just don't sort ++ # them. This means eq_set doesn't really work with refs. ++ return eq_array( ++ [ grep( ref, @$a1 ), sort( grep( !ref, @$a1 ) ) ], ++ [ grep( ref, @$a2 ), sort( grep( !ref, @$a2 ) ) ], ++ ); ++} ++ ++#line 1735 ++ ++1; +Index: 0.8/modules/nginx-echo/test/valgrind.suppress +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ 0.8/modules/nginx-echo/test/valgrind.suppress 2010-10-31 07:35:23.648338001 +0000 +@@ -0,0 +1,88 @@ ++{ ++ init_request_leak ++ Memcheck:Leak ++ fun:memalign ++ fun:posix_memalign ++ fun:ngx_memalign ++ fun:ngx_palloc_block ++ fun:ngx_palloc ++ fun:ngx_pcalloc ++ fun:ngx_create_temp_buf ++ fun:ngx_http_init_request ++ fun:ngx_epoll_process_events ++ fun:ngx_process_events_and_timers ++ fun:ngx_single_process_cycle ++ fun:main ++} ++{ ++ nginx-core-process-init ++ Memcheck:Leak ++ fun:malloc ++ fun:ngx_alloc ++ fun:ngx_event_process_init ++ fun:ngx_single_process_cycle ++ fun:main ++} ++{ ++ nginx-core-crc32-init ++ Memcheck:Leak ++ fun:malloc ++ fun:ngx_alloc ++ fun:ngx_crc32_table_init ++ fun:main ++} ++{ ++ palloc_large_for_init_request ++ Memcheck:Leak ++ fun:malloc ++ fun:ngx_alloc ++ fun:ngx_palloc_large ++ fun:ngx_palloc ++ fun:ngx_pcalloc ++ fun:ngx_http_init_request ++ fun:ngx_epoll_process_events ++ fun:ngx_process_events_and_timers ++ fun:ngx_single_process_cycle ++ fun:main ++} ++{ ++ palloc_large_for_create_temp_buf ++ Memcheck:Leak ++ fun:malloc ++ fun:ngx_alloc ++ fun:ngx_palloc_large ++ fun:ngx_palloc ++ fun:ngx_create_temp_buf ++ fun:ngx_http_init_request ++ fun:ngx_epoll_process_events ++ fun:ngx_process_events_and_timers ++ fun:ngx_single_process_cycle ++ fun:main ++} ++{ ++ accept_create_pool ++ Memcheck:Leak ++ fun:memalign ++ fun:posix_memalign ++ fun:ngx_memalign ++ fun:ngx_create_pool ++ fun:ngx_event_accept ++ fun:ngx_epoll_process_events ++ fun:ngx_process_events_and_timers ++ fun:ngx_single_process_cycle ++ fun:main ++} ++{ ++ create_pool_for_init_req ++ Memcheck:Leak ++ fun:memalign ++ fun:posix_memalign ++ fun:ngx_memalign ++ fun:ngx_create_pool ++ fun:ngx_http_init_request ++ fun:ngx_epoll_process_events ++ fun:ngx_process_events_and_timers ++ fun:ngx_single_process_cycle ++ fun:main ++} ++ +Index: 0.8/modules/nginx-echo/util/build.sh +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ 0.8/modules/nginx-echo/util/build.sh 2010-10-31 07:35:23.648338001 +0000 +@@ -0,0 +1,66 @@ ++#!/bin/bash ++ ++# this file is mostly meant to be used by the author himself. ++ ++root=`pwd` ++cd ~/work ++version=$1 ++opts=$2 ++home=~ ++ ++if [ ! -s "nginx-$version.tar.gz" ]; then ++ wget "http://sysoev.ru/nginx/nginx-$version.tar.gz" -O nginx-$version.tar.gz || exit 1 ++ tar -xzvf nginx-$version.tar.gz || exit 1 ++ if [ "$version" = "0.8.41" ]; then ++ cp $root/../no-pool-nginx/nginx-$version-no_pool.patch ./ ++ patch -p0 < nginx-$version-no_pool.patch || exit 1 ++ fi ++fi ++ ++#tar -xzvf nginx-$version.tar.gz || exit 1 ++#cp $root/../no-pool-nginx/nginx-0.8.41-no_pool.patch ./ ++#patch -p0 < nginx-0.8.41-no_pool.patch || exit 1 ++ ++cd nginx-$version/ ++if [[ "$BUILD_CLEAN" -eq 1 || ! -f Makefile \ ++ || "$root/config" -nt Makefile ++ || "$root/util/build.sh" -nt Makefile ]]; then ++ ./configure --prefix=/opt/nginx \ ++ --with-cc-opt="-DDEBUG_MALLOC" \ ++ --with-http_stub_status_module \ ++ --without-mail_pop3_module \ ++ --without-mail_imap_module \ ++ --without-mail_smtp_module \ ++ --without-http_upstream_ip_hash_module \ ++ --without-http_empty_gif_module \ ++ --without-http_memcached_module \ ++ --without-http_referer_module \ ++ --without-http_autoindex_module \ ++ --without-http_auth_basic_module \ ++ --without-http_userid_module \ ++ --with-http_addition_module \ ++ --add-module=$root/../ndk-nginx-module \ ++ --add-module=$root/../set-misc-nginx-module \ ++ --add-module=$root/../eval-nginx-module \ ++ --add-module=$root/../xss-nginx-module \ ++ --add-module=$root/../rds-json-nginx-module \ ++ --add-module=$root/../headers-more-nginx-module \ ++ --add-module=$root $opts \ ++ --with-debug ++ #--add-module=$root/../lz-session-nginx-module \ ++ #--add-module=$home/work/ndk \ ++ #--add-module=$home/work/ndk/examples/http/set_var \ ++ #--add-module=$root/../eval-nginx-module \ ++ #--add-module=/home/agentz/work/nginx_eval_module-1.0.1 \ ++ #--without-http_ssi_module # we cannot disable ssi because echo_location_async depends on it (i dunno why?!) ++ ++fi ++if [ -f /opt/nginx/sbin/nginx ]; then ++ rm -f /opt/nginx/sbin/nginx ++fi ++if [ -f /opt/nginx/logs/nginx.pid ]; then ++ kill `cat /opt/nginx/logs/nginx.pid` ++fi ++make -j3 ++make install ++ +Index: 0.8/modules/nginx-echo/util/releng +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ 0.8/modules/nginx-echo/util/releng 2010-10-31 07:35:23.648338001 +0000 +@@ -0,0 +1,7 @@ ++#!/bin/bash ++ ++./update-readme ++ack '(?<=\#define)\s*DDEBUG\s*[12]' src ++echo ======================================= ++ack '(?<=This document describes echo-nginx-module v)\d+\.\d+' README ++ +Index: 0.8/modules/nginx-echo/util/update-readme.sh +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ 0.8/modules/nginx-echo/util/update-readme.sh 2010-10-31 07:35:23.648338001 +0000 +@@ -0,0 +1,6 @@ ++#!/bin/bash ++ ++perl util/wiki2pod.pl doc/manpage.wiki > /tmp/a.pod \ ++ && pod2text /tmp/a.pod > README \ ++ && perl -i -pe 's{(https?://.*?)>}{$1 >}g' README ++ +Index: 0.8/modules/nginx-echo/util/wiki2pod.pl +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ 0.8/modules/nginx-echo/util/wiki2pod.pl 2010-10-31 07:35:23.648338001 +0000 +@@ -0,0 +1,131 @@ ++#!/usr/bin/env perl ++ ++use strict; ++use warnings; ++use bytes; ++ ++my @nl_counts; ++my $last_nl_count_level; ++ ++my @bl_counts; ++my $last_bl_count_level; ++ ++sub fmt_pos ($) { ++ (my $s = $_[0]) =~ s{\#(.*)}{/"$1"}; ++ $s; ++} ++ ++sub fmt_mark ($$) { ++ my ($tag, $s) = @_; ++ my $max_level = 0; ++ while ($s =~ /([<>])\1*/g) { ++ my $level = length $&; ++ if ($level > $max_level) { ++ $max_level = $level; ++ } ++ } ++ ++ my $times = $max_level + 1; ++ if ($times > 1) { ++ $s = " $s "; ++ } ++ return $tag . ('<' x $times) . $s . ('>' x $times); ++} ++ ++print "=encoding utf-8\n\n"; ++ ++while (<>) { ++ if ($. == 1) { ++ # strip the leading U+FEFF byte in MS-DOS text files ++ my $first = ord(substr($_, 0, 1)); ++ #printf STDERR "0x%x", $first; ++ #my $second = ord(substr($_, 2, 1)); ++ #printf STDERR "0x%x", $second; ++ if ($first == 0xEF) { ++ substr($_, 0, 1, ''); ++ #warn "Hit!"; ++ } ++ } ++ s{\[(http[^ \]]+) ([^\]]*)\]}{$2 (L<$1>)}gi; ++ s{ \[\[ ( [^\]\|]+ ) \| ([^\]]*) \]\] }{"L<$2|" . fmt_pos($1) . ">"}gixe; ++ s{(.*?)}{fmt_mark('C', $1)}gie; ++ s{'''(.*?)'''}{fmt_mark('B', $1)}ge; ++ s{''(.*?)''}{fmt_mark('I', $1)}ge; ++ if (s{^\s*<[^>]+>\s*$}{}) { ++ next; ++ } ++ ++ if (/^\s*$/) { ++ print "\n"; ++ next; ++ } ++ ++=begin cmt ++ ++ if ($. == 1) { ++ warn $_; ++ for my $i (0..length($_) - 1) { ++ my $chr = substr($_, $i, 1); ++ warn "chr ord($i): ".ord($chr)." \"$chr\"\n"; ++ } ++ } ++ ++=end cmt ++=cut ++ ++ if (/(=+) (.*) \1$/) { ++ #warn "HERE! $_" if $. == 1; ++ my ($level, $title) = (length $1, $2); ++ collapse_lists(); ++ ++ print "\n=head$level $title\n\n"; ++ } elsif (/^(\#+) (.*)/) { ++ my ($level, $txt) = (length($1) - 1, $2); ++ if (defined $last_nl_count_level && $level != $last_nl_count_level) { ++ print "\n=back\n\n"; ++ } ++ $last_nl_count_level = $level; ++ $nl_counts[$level] ||= 0; ++ if ($nl_counts[$level] == 0) { ++ print "\n=over\n\n"; ++ } ++ $nl_counts[$level]++; ++ print "\n=item $nl_counts[$level].\n\n"; ++ print "$txt\n"; ++ } elsif (/^(\*+) (.*)/) { ++ my ($level, $txt) = (length($1) - 1, $2); ++ if (defined $last_bl_count_level && $level != $last_bl_count_level) { ++ print "\n=back\n\n"; ++ } ++ $last_bl_count_level = $level; ++ $bl_counts[$level] ||= 0; ++ if ($bl_counts[$level] == 0) { ++ print "\n=over\n\n"; ++ } ++ $bl_counts[$level]++; ++ print "\n=item *\n\n"; ++ print "$txt\n"; ++ } else { ++ collapse_lists(); ++ print; ++ } ++} ++ ++collapse_lists(); ++ ++sub collapse_lists { ++ while (defined $last_nl_count_level && $last_nl_count_level >= 0) { ++ print "\n=back\n\n"; ++ $last_nl_count_level--; ++ } ++ undef $last_nl_count_level; ++ undef @nl_counts; ++ ++ while (defined $last_bl_count_level && $last_bl_count_level >= 0) { ++ print "\n=back\n\n"; ++ $last_bl_count_level--; ++ } ++ undef $last_bl_count_level; ++ undef @bl_counts; ++} ++ diff -Nru nginx-0.5.33/debian/patches/nginx-upstream-fair.diff nginx-0.8.53/debian/patches/nginx-upstream-fair.diff --- nginx-0.5.33/debian/patches/nginx-upstream-fair.diff 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/debian/patches/nginx-upstream-fair.diff 2011-02-26 14:04:40.000000000 +0000 @@ -0,0 +1,1368 @@ +Description: Patch for nginx upstream fair module +Author: Fabio Tranchitella + +diff -urN nginx/modules/nginx-upstream-fair/config nginx.debian/modules/nginx-upstream-fair/config +--- nginx/modules/nginx-upstream-fair/config 1970-01-01 00:00:00.000000000 +0000 ++++ nginx.debian/modules/nginx-upstream-fair/config 2009-10-15 18:10:40.000000000 +0000 +@@ -0,0 +1,3 @@ ++ngx_addon_name=ngx_http_upstream_fair_module ++HTTP_MODULES="$HTTP_MODULES ngx_http_upstream_fair_module" ++NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_upstream_fair_module.c" +diff -urN nginx/modules/nginx-upstream-fair/ngx_http_upstream_fair_module.c nginx.debian/modules/nginx-upstream-fair/ngx_http_upstream_fair_module.c +--- nginx/modules/nginx-upstream-fair/ngx_http_upstream_fair_module.c 1970-01-01 00:00:00.000000000 +0000 ++++ nginx.debian/modules/nginx-upstream-fair/ngx_http_upstream_fair_module.c 2009-10-15 18:10:54.000000000 +0000 +@@ -0,0 +1,1354 @@ ++/* ++ * Copyright (C) 2007 Grzegorz Nosek ++ * Work sponsored by Ezra Zygmuntowicz & EngineYard.com ++ * ++ * Based on nginx source (C) Igor Sysoev ++ */ ++ ++#include ++#include ++#include ++ ++typedef struct { ++ ngx_uint_t nreq; ++ ngx_uint_t total_req; ++ ngx_uint_t last_req_id; ++ ngx_uint_t fails; ++ ngx_uint_t current_weight; ++} ngx_http_upstream_fair_shared_t; ++ ++typedef struct ngx_http_upstream_fair_peers_s ngx_http_upstream_fair_peers_t; ++ ++typedef struct { ++ ngx_rbtree_node_t node; ++ ngx_uint_t generation; ++ uintptr_t peers; /* forms a unique cookie together with generation */ ++ ngx_uint_t total_nreq; ++ ngx_uint_t total_requests; ++ ngx_atomic_t lock; ++ ngx_http_upstream_fair_shared_t stats[1]; ++} ngx_http_upstream_fair_shm_block_t; ++ ++/* ngx_spinlock is defined without a matching unlock primitive */ ++#define ngx_spinlock_unlock(lock) (void) ngx_atomic_cmp_set(lock, ngx_pid, 0) ++ ++typedef struct { ++ ngx_http_upstream_fair_shared_t *shared; ++ struct sockaddr *sockaddr; ++ socklen_t socklen; ++ ngx_str_t name; ++ ++ ngx_uint_t weight; ++ ngx_uint_t max_fails; ++ time_t fail_timeout; ++ ++ time_t accessed; ++ ngx_uint_t down:1; ++ ++#if (NGX_HTTP_SSL) ++ ngx_ssl_session_t *ssl_session; /* local to a process */ ++#endif ++ ++} ngx_http_upstream_fair_peer_t; ++ ++#define NGX_HTTP_UPSTREAM_FAIR_NO_RR (1<<26) ++#define NGX_HTTP_UPSTREAM_FAIR_WEIGHT_MODE_IDLE (1<<27) ++#define NGX_HTTP_UPSTREAM_FAIR_WEIGHT_MODE_PEAK (1<<28) ++#define NGX_HTTP_UPSTREAM_FAIR_WEIGHT_MODE_MASK ((1<<27) | (1<<28)) ++ ++enum { WM_DEFAULT = 0, WM_IDLE, WM_PEAK }; ++ ++struct ngx_http_upstream_fair_peers_s { ++ ngx_http_upstream_fair_shm_block_t *shared; ++ ngx_uint_t current; ++ ngx_uint_t size_err:1; ++ ngx_uint_t no_rr:1; ++ ngx_uint_t weight_mode:2; ++ ngx_uint_t number; ++ ngx_str_t *name; ++ ngx_http_upstream_fair_peers_t *next; /* for backup peers support, not really used yet */ ++ ngx_http_upstream_fair_peer_t peer[1]; ++}; ++ ++ ++#define NGX_PEER_INVALID (~0UL) ++ ++typedef struct { ++ ngx_http_upstream_fair_peers_t *peers; ++ ngx_uint_t current; ++ uintptr_t *tried; ++ uintptr_t *done; ++ uintptr_t data; ++ uintptr_t data2; ++} ngx_http_upstream_fair_peer_data_t; ++ ++ ++static ngx_int_t ngx_http_upstream_init_fair(ngx_conf_t *cf, ++ ngx_http_upstream_srv_conf_t *us); ++static ngx_int_t ngx_http_upstream_get_fair_peer(ngx_peer_connection_t *pc, ++ void *data); ++static void ngx_http_upstream_free_fair_peer(ngx_peer_connection_t *pc, ++ void *data, ngx_uint_t state); ++static ngx_int_t ngx_http_upstream_init_fair_peer(ngx_http_request_t *r, ++ ngx_http_upstream_srv_conf_t *us); ++static char *ngx_http_upstream_fair(ngx_conf_t *cf, ngx_command_t *cmd, ++ void *conf); ++static char *ngx_http_upstream_fair_set_shm_size(ngx_conf_t *cf, ++ ngx_command_t *cmd, void *conf); ++static ngx_int_t ngx_http_upstream_fair_init_module(ngx_cycle_t *cycle); ++ ++#if (NGX_HTTP_EXTENDED_STATUS) ++static ngx_chain_t *ngx_http_upstream_fair_report_status(ngx_http_request_t *r, ++ ngx_int_t *length); ++#endif ++ ++#if (NGX_HTTP_SSL) ++static ngx_int_t ngx_http_upstream_fair_set_session(ngx_peer_connection_t *pc, ++ void *data); ++static void ngx_http_upstream_fair_save_session(ngx_peer_connection_t *pc, ++ void *data); ++#endif ++ ++static ngx_command_t ngx_http_upstream_fair_commands[] = { ++ ++ { ngx_string("fair"), ++ NGX_HTTP_UPS_CONF|NGX_CONF_ANY, ++ ngx_http_upstream_fair, ++ 0, ++ 0, ++ NULL }, ++ ++ { ngx_string("upstream_fair_shm_size"), ++ NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1, ++ ngx_http_upstream_fair_set_shm_size, ++ 0, ++ 0, ++ NULL }, ++ ++ ngx_null_command ++}; ++ ++ ++static ngx_http_module_t ngx_http_upstream_fair_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 */ ++ ++#if (NGX_HTTP_EXTENDED_STATUS) ++ ngx_http_upstream_fair_report_status, ++#endif ++}; ++ ++ ++ngx_module_t ngx_http_upstream_fair_module = { ++ NGX_MODULE_V1, ++ &ngx_http_upstream_fair_module_ctx, /* module context */ ++ ngx_http_upstream_fair_commands, /* module directives */ ++ NGX_HTTP_MODULE, /* module type */ ++ NULL, /* init master */ ++ ngx_http_upstream_fair_init_module, /* init module */ ++ NULL, /* init process */ ++ NULL, /* init thread */ ++ NULL, /* exit thread */ ++ NULL, /* exit process */ ++ NULL, /* exit master */ ++ NGX_MODULE_V1_PADDING ++}; ++ ++ ++static ngx_uint_t ngx_http_upstream_fair_shm_size; ++static ngx_shm_zone_t * ngx_http_upstream_fair_shm_zone; ++static ngx_rbtree_t * ngx_http_upstream_fair_rbtree; ++static ngx_uint_t ngx_http_upstream_fair_generation; ++ ++static int ++ngx_http_upstream_fair_compare_rbtree_node(const ngx_rbtree_node_t *v_left, ++ const ngx_rbtree_node_t *v_right) ++{ ++ ngx_http_upstream_fair_shm_block_t *left, *right; ++ ++ left = (ngx_http_upstream_fair_shm_block_t *) v_left; ++ right = (ngx_http_upstream_fair_shm_block_t *) v_right; ++ ++ if (left->generation < right->generation) { ++ return -1; ++ } else if (left->generation > right->generation) { ++ return 1; ++ } else { /* left->generation == right->generation */ ++ if (left->peers < right->peers) { ++ return -1; ++ } else if (left->peers > right->peers) { ++ return 1; ++ } else { ++ return 0; ++ } ++ } ++} ++ ++/* ++ * generic functions start here ++ */ ++static void ++ngx_rbtree_generic_insert(ngx_rbtree_node_t *temp, ++ ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel, ++ int (*compare)(const ngx_rbtree_node_t *left, const ngx_rbtree_node_t *right)) ++{ ++ for ( ;; ) { ++ if (node->key < temp->key) { ++ ++ if (temp->left == sentinel) { ++ temp->left = node; ++ break; ++ } ++ ++ temp = temp->left; ++ ++ } else if (node->key > temp->key) { ++ ++ if (temp->right == sentinel) { ++ temp->right = node; ++ break; ++ } ++ ++ temp = temp->right; ++ ++ } else { /* node->key == temp->key */ ++ if (compare(node, temp) < 0) { ++ ++ if (temp->left == sentinel) { ++ temp->left = node; ++ break; ++ } ++ ++ temp = temp->left; ++ ++ } else { ++ ++ if (temp->right == sentinel) { ++ temp->right = node; ++ break; ++ } ++ ++ temp = temp->right; ++ } ++ } ++ } ++ ++ node->parent = temp; ++ node->left = sentinel; ++ node->right = sentinel; ++ ngx_rbt_red(node); ++} ++ ++#define NGX_BITVECTOR_ELT_SIZE (sizeof(uintptr_t) * 8) ++ ++static uintptr_t * ++ngx_bitvector_alloc(ngx_pool_t *pool, ngx_uint_t size, uintptr_t *small) ++{ ++ ngx_uint_t nelts = (size + NGX_BITVECTOR_ELT_SIZE - 1) / NGX_BITVECTOR_ELT_SIZE; ++ ++ if (small && nelts == 1) { ++ *small = 0; ++ return small; ++ } ++ ++ return ngx_pcalloc(pool, nelts * NGX_BITVECTOR_ELT_SIZE); ++} ++ ++static ngx_int_t ++ngx_bitvector_test(uintptr_t *bv, ngx_uint_t bit) ++{ ++ ngx_uint_t n, m; ++ ++ n = bit / NGX_BITVECTOR_ELT_SIZE; ++ m = 1 << (bit % NGX_BITVECTOR_ELT_SIZE); ++ ++ return bv[n] & m; ++} ++ ++static void ++ngx_bitvector_set(uintptr_t *bv, ngx_uint_t bit) ++{ ++ ngx_uint_t n, m; ++ ++ n = bit / NGX_BITVECTOR_ELT_SIZE; ++ m = 1 << (bit % NGX_BITVECTOR_ELT_SIZE); ++ ++ bv[n] |= m; ++} ++ ++/* ++ * generic functions end here ++ */ ++ ++static ngx_int_t ++ngx_http_upstream_fair_init_module(ngx_cycle_t *cycle) ++{ ++ ngx_http_upstream_fair_generation++; ++ return NGX_OK; ++} ++ ++static void ++ngx_http_upstream_fair_rbtree_insert(ngx_rbtree_node_t *temp, ++ ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel) { ++ ++ ngx_rbtree_generic_insert(temp, node, sentinel, ++ ngx_http_upstream_fair_compare_rbtree_node); ++} ++ ++ ++static ngx_int_t ++ngx_http_upstream_fair_init_shm_zone(ngx_shm_zone_t *shm_zone, void *data) ++{ ++ ngx_slab_pool_t *shpool; ++ ngx_rbtree_t *tree; ++ ngx_rbtree_node_t *sentinel; ++ ++ if (data) { ++ shm_zone->data = data; ++ return NGX_OK; ++ } ++ ++ shpool = (ngx_slab_pool_t *) shm_zone->shm.addr; ++ tree = ngx_slab_alloc(shpool, sizeof *tree); ++ if (tree == NULL) { ++ return NGX_ERROR; ++ } ++ ++ sentinel = ngx_slab_alloc(shpool, sizeof *sentinel); ++ if (sentinel == NULL) { ++ return NGX_ERROR; ++ } ++ ++ ngx_rbtree_sentinel_init(sentinel); ++ tree->root = sentinel; ++ tree->sentinel = sentinel; ++ tree->insert = ngx_http_upstream_fair_rbtree_insert; ++ shm_zone->data = tree; ++ ngx_http_upstream_fair_rbtree = tree; ++ ++ return NGX_OK; ++} ++ ++ ++static char * ++ngx_http_upstream_fair_set_shm_size(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) ++{ ++ ssize_t new_shm_size; ++ ngx_str_t *value; ++ ++ value = cf->args->elts; ++ ++ new_shm_size = ngx_parse_size(&value[1]); ++ if (new_shm_size == NGX_ERROR) { ++ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "Invalid memory area size `%V'", &value[1]); ++ return NGX_CONF_ERROR; ++ } ++ ++ new_shm_size = ngx_align(new_shm_size, ngx_pagesize); ++ ++ if (new_shm_size < 8 * (ssize_t) ngx_pagesize) { ++ ngx_conf_log_error(NGX_LOG_WARN, cf, 0, "The upstream_fair_shm_size value must be at least %udKiB", (8 * ngx_pagesize) >> 10); ++ new_shm_size = 8 * ngx_pagesize; ++ } ++ ++ if (ngx_http_upstream_fair_shm_size && ++ ngx_http_upstream_fair_shm_size != (ngx_uint_t) new_shm_size) { ++ ngx_conf_log_error(NGX_LOG_WARN, cf, 0, "Cannot change memory area size without restart, ignoring change"); ++ } else { ++ ngx_http_upstream_fair_shm_size = new_shm_size; ++ } ++ ngx_conf_log_error(NGX_LOG_DEBUG, cf, 0, "Using %udKiB of shared memory for upstream_fair", new_shm_size >> 10); ++ ++ return NGX_CONF_OK; ++} ++ ++ ++static char * ++ngx_http_upstream_fair(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) ++{ ++ ngx_http_upstream_srv_conf_t *uscf; ++ ngx_uint_t i; ++ ngx_uint_t extra_peer_flags = 0; ++ ++ for (i = 1; i < cf->args->nelts; i++) { ++ ngx_str_t *value = cf->args->elts; ++ if (ngx_strcmp(value[i].data, "no_rr") == 0) { ++ extra_peer_flags |= NGX_HTTP_UPSTREAM_FAIR_NO_RR; ++ } else if (ngx_strcmp(value[i].data, "weight_mode=peak") == 0) { ++ if (extra_peer_flags & NGX_HTTP_UPSTREAM_FAIR_WEIGHT_MODE_MASK) { ++ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "weight_mode= options are mutually exclusive"); ++ return NGX_CONF_ERROR; ++ } ++ extra_peer_flags |= NGX_HTTP_UPSTREAM_FAIR_WEIGHT_MODE_PEAK; ++ } else if (ngx_strcmp(value[i].data, "weight_mode=idle") == 0) { ++ if (extra_peer_flags & NGX_HTTP_UPSTREAM_FAIR_WEIGHT_MODE_MASK) { ++ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "weight_mode= options are mutually exclusive"); ++ return NGX_CONF_ERROR; ++ } ++ extra_peer_flags |= NGX_HTTP_UPSTREAM_FAIR_WEIGHT_MODE_IDLE; ++ } else { ++ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "Invalid `fair' parameter `%V'", &value[i]); ++ return NGX_CONF_ERROR; ++ } ++ } ++ ++ uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module); ++ ++ uscf->peer.init_upstream = ngx_http_upstream_init_fair; ++ ++ uscf->flags = NGX_HTTP_UPSTREAM_CREATE ++ |NGX_HTTP_UPSTREAM_WEIGHT ++ |NGX_HTTP_UPSTREAM_MAX_FAILS ++ |NGX_HTTP_UPSTREAM_FAIL_TIMEOUT ++ |NGX_HTTP_UPSTREAM_DOWN ++ |extra_peer_flags; ++ ++ return NGX_CONF_OK; ++} ++ ++ ++static ngx_int_t ++ngx_http_upstream_cmp_servers(const void *one, const void *two) ++{ ++ const ngx_http_upstream_fair_peer_t *first, *second; ++ ++ first = one; ++ second = two; ++ ++ return (first->weight < second->weight); ++} ++ ++ ++/* TODO: Actually support backup servers */ ++static ngx_int_t ++ngx_http_upstream_init_fair_rr(ngx_conf_t *cf, ngx_http_upstream_srv_conf_t *us) ++{ ++ ngx_url_t u; ++ ngx_uint_t i, j, n; ++ ngx_http_upstream_server_t *server; ++ ngx_http_upstream_fair_peers_t *peers, *backup; ++ ++ if (us->servers) { ++ server = us->servers->elts; ++ ++ n = 0; ++ ++ for (i = 0; i < us->servers->nelts; i++) { ++ if (server[i].backup) { ++ continue; ++ } ++ ++ n += server[i].naddrs; ++ } ++ ++ peers = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_fair_peers_t) ++ + sizeof(ngx_http_upstream_fair_peer_t) * (n - 1)); ++ if (peers == NULL) { ++ return NGX_ERROR; ++ } ++ ++ peers->number = n; ++ peers->name = &us->host; ++ ++ n = 0; ++ ++ for (i = 0; i < us->servers->nelts; i++) { ++ for (j = 0; j < server[i].naddrs; j++) { ++ if (server[i].backup) { ++ continue; ++ } ++ ++ peers->peer[n].sockaddr = server[i].addrs[j].sockaddr; ++ peers->peer[n].socklen = server[i].addrs[j].socklen; ++ peers->peer[n].name = server[i].addrs[j].name; ++ peers->peer[n].max_fails = server[i].max_fails; ++ peers->peer[n].fail_timeout = server[i].fail_timeout; ++ peers->peer[n].down = server[i].down; ++ peers->peer[n].weight = server[i].down ? 0 : server[i].weight; ++ n++; ++ } ++ } ++ ++ us->peer.data = peers; ++ ++ ngx_sort(&peers->peer[0], (size_t) n, ++ sizeof(ngx_http_upstream_fair_peer_t), ++ ngx_http_upstream_cmp_servers); ++ ++ /* backup servers */ ++ ++ n = 0; ++ ++ for (i = 0; i < us->servers->nelts; i++) { ++ if (!server[i].backup) { ++ continue; ++ } ++ ++ n += server[i].naddrs; ++ } ++ ++ if (n == 0) { ++ return NGX_OK; ++ } ++ ++ backup = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_fair_peers_t) ++ + sizeof(ngx_http_upstream_fair_peer_t) * (n - 1)); ++ if (backup == NULL) { ++ return NGX_ERROR; ++ } ++ ++ backup->number = n; ++ backup->name = &us->host; ++ ++ n = 0; ++ ++ for (i = 0; i < us->servers->nelts; i++) { ++ for (j = 0; j < server[i].naddrs; j++) { ++ if (!server[i].backup) { ++ continue; ++ } ++ ++ backup->peer[n].sockaddr = server[i].addrs[j].sockaddr; ++ backup->peer[n].socklen = server[i].addrs[j].socklen; ++ backup->peer[n].name = server[i].addrs[j].name; ++ backup->peer[n].weight = server[i].weight; ++ backup->peer[n].max_fails = server[i].max_fails; ++ backup->peer[n].fail_timeout = server[i].fail_timeout; ++ backup->peer[n].down = server[i].down; ++ n++; ++ } ++ } ++ ++ peers->next = backup; ++ ++ ngx_sort(&backup->peer[0], (size_t) n, ++ sizeof(ngx_http_upstream_fair_peer_t), ++ ngx_http_upstream_cmp_servers); ++ ++ return NGX_OK; ++ } ++ ++ ++ /* an upstream implicitly defined by proxy_pass, etc. */ ++ ++ if (us->port == 0 && us->default_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 = (in_port_t) (us->port ? us->port : us->default_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_fair_peers_t) ++ + sizeof(ngx_http_upstream_fair_peer_t) * (n - 1)); ++ if (peers == NULL) { ++ return NGX_ERROR; ++ } ++ ++ peers->number = n; ++ peers->name = &us->host; ++ ++ for (i = 0; i < u.naddrs; i++) { ++ peers->peer[i].sockaddr = u.addrs[i].sockaddr; ++ peers->peer[i].socklen = u.addrs[i].socklen; ++ peers->peer[i].name = u.addrs[i].name; ++ peers->peer[i].weight = 1; ++ peers->peer[i].max_fails = 1; ++ peers->peer[i].fail_timeout = 10; ++ } ++ ++ us->peer.data = peers; ++ ++ /* implicitly defined upstream has no backup servers */ ++ ++ return NGX_OK; ++} ++ ++static ngx_int_t ++ngx_http_upstream_init_fair(ngx_conf_t *cf, ngx_http_upstream_srv_conf_t *us) ++{ ++ ngx_http_upstream_fair_peers_t *peers; ++ ngx_uint_t n; ++ ngx_str_t *shm_name; ++ ++ /* do the dirty work using rr module */ ++ if (ngx_http_upstream_init_fair_rr(cf, us) != NGX_OK) { ++ return NGX_ERROR; ++ } ++ ++ /* setup our wrapper around rr */ ++ peers = ngx_palloc(cf->pool, sizeof *peers); ++ if (peers == NULL) { ++ return NGX_ERROR; ++ } ++ peers = us->peer.data; ++ n = peers->number; ++ ++ shm_name = ngx_palloc(cf->pool, sizeof *shm_name); ++ shm_name->len = sizeof("upstream_fair"); ++ shm_name->data = (unsigned char *) "upstream_fair"; ++ ++ if (ngx_http_upstream_fair_shm_size == 0) { ++ ngx_http_upstream_fair_shm_size = 8 * ngx_pagesize; ++ } ++ ++ ngx_http_upstream_fair_shm_zone = ngx_shared_memory_add( ++ cf, shm_name, ngx_http_upstream_fair_shm_size, &ngx_http_upstream_fair_module); ++ if (ngx_http_upstream_fair_shm_zone == NULL) { ++ return NGX_ERROR; ++ } ++ ngx_http_upstream_fair_shm_zone->init = ngx_http_upstream_fair_init_shm_zone; ++ ++ peers->shared = NULL; ++ peers->current = n - 1; ++ if (us->flags & NGX_HTTP_UPSTREAM_FAIR_NO_RR) { ++ peers->no_rr = 1; ++ } ++ if (us->flags & NGX_HTTP_UPSTREAM_FAIR_WEIGHT_MODE_IDLE) { ++ peers->weight_mode = WM_IDLE; ++ } else if (us->flags & NGX_HTTP_UPSTREAM_FAIR_WEIGHT_MODE_PEAK) { ++ peers->weight_mode = WM_PEAK; ++ } ++ peers->size_err = 0; ++ ++ us->peer.init = ngx_http_upstream_init_fair_peer; ++ ++ return NGX_OK; ++} ++ ++ ++static void ++ngx_http_upstream_fair_update_nreq(ngx_http_upstream_fair_peer_data_t *fp, int delta, ngx_log_t *log) ++{ ++ ngx_uint_t nreq; ++ ngx_uint_t total_nreq; ++ ++ nreq = (fp->peers->peer[fp->current].shared->nreq += delta); ++ total_nreq = (fp->peers->shared->total_nreq += delta); ++ ++ ngx_log_debug6(NGX_LOG_DEBUG_HTTP, log, 0, ++ "[upstream_fair] nreq for peer %ui @ %p/%p now %d, total %d, delta %d", ++ fp->current, fp->peers, fp->peers->peer[fp->current].shared, nreq, ++ total_nreq, delta); ++} ++ ++/* ++ * SCHED_COUNTER_BITS is the portion of an ngx_uint_t which represents ++ * the req_delta part (number of requests serviced on _other_ ++ * backends). The rest (top bits) represents the number of currently ++ * processed requests. ++ * ++ * The value is not too critical because overflow is handled via ++ * saturation. With the default value of 20, scheduling is exact for ++ * fewer than 4k concurrent requests per backend (on 32-bit ++ * architectures) and fewer than 1M concurrent requests to all backends ++ * together. Beyond these limits, the algorithm essentially falls back ++ * to pure weighted round-robin. ++ * ++ * A higher score means less suitable. ++ * ++ * The `delta' parameter is bit-negated so that high values yield low ++ * scores and get chosen more often. ++ */ ++ ++#define SCHED_COUNTER_BITS 20 ++#define SCHED_NREQ_MAX ((~0UL) >> SCHED_COUNTER_BITS) ++#define SCHED_COUNTER_MAX ((1 << SCHED_COUNTER_BITS) - 1) ++#define SCHED_SCORE(nreq,delta) (((nreq) << SCHED_COUNTER_BITS) | (~(delta))) ++#define ngx_upstream_fair_min(a,b) (((a) < (b)) ? (a) : (b)) ++ ++static ngx_uint_t ++ngx_http_upstream_fair_sched_score(ngx_peer_connection_t *pc, ++ ngx_http_upstream_fair_peer_data_t *fp, ++ ngx_uint_t n) ++{ ++ ngx_http_upstream_fair_peer_t *peer = &fp->peers->peer[n]; ++ ngx_http_upstream_fair_shared_t *fs = peer->shared; ++ ngx_uint_t req_delta = fp->peers->shared->total_requests - fs->last_req_id; ++ ++ /* sanity check */ ++ if ((ngx_int_t)fs->nreq < 0) { ++ ngx_log_error(NGX_LOG_WARN, pc->log, 0, "[upstream_fair] upstream %ui has negative nreq (%i)", n, fs->nreq); ++ return SCHED_SCORE(0, req_delta); ++ } ++ ++ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, pc->log, 0, "[upstream_fair] peer %ui: nreq = %i, req_delta = %ui", n, fs->nreq, req_delta); ++ ++ return SCHED_SCORE( ++ ngx_upstream_fair_min(fs->nreq, SCHED_NREQ_MAX), ++ ngx_upstream_fair_min(req_delta, SCHED_COUNTER_MAX)); ++} ++ ++/* ++ * the core of load balancing logic ++ */ ++ ++static ngx_int_t ++ngx_http_upstream_fair_try_peer(ngx_peer_connection_t *pc, ++ ngx_http_upstream_fair_peer_data_t *fp, ++ ngx_uint_t peer_id) ++{ ++ ngx_http_upstream_fair_peer_t *peer; ++ ++ if (ngx_bitvector_test(fp->tried, peer_id)) ++ return NGX_BUSY; ++ ++ peer = &fp->peers->peer[peer_id]; ++ ++ if (!peer->down) { ++ if (peer->max_fails == 0 || peer->shared->fails < peer->max_fails) { ++ return NGX_OK; ++ } ++ ++ if (ngx_time() - peer->accessed > peer->fail_timeout) { ++ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, pc->log, 0, "[upstream_fair] resetting fail count for peer %d, time delta %d > %d", ++ peer_id, ngx_time() - peer->accessed, peer->fail_timeout); ++ peer->shared->fails = 0; ++ return NGX_OK; ++ } ++ } ++ ++ return NGX_BUSY; ++} ++ ++static ngx_uint_t ++ngx_http_upstream_choose_fair_peer_idle(ngx_peer_connection_t *pc, ++ ngx_http_upstream_fair_peer_data_t *fp) ++{ ++ ngx_uint_t i, n; ++ ngx_uint_t npeers = fp->peers->number; ++ ngx_uint_t weight_mode = fp->peers->weight_mode; ++ ngx_uint_t best_idx = NGX_PEER_INVALID; ++ ngx_uint_t best_nreq = ~0U; ++ ++ for (i = 0, n = fp->current; i < npeers; i++, n = (n + 1) % npeers) { ++ ngx_uint_t nreq = fp->peers->peer[n].shared->nreq; ++ ngx_uint_t weight = fp->peers->peer[n].weight; ++ ++ if (fp->peers->peer[n].shared->fails > 0) ++ continue; ++ ++ if (nreq >= weight || (nreq > 0 && weight_mode != WM_IDLE)) { ++ continue; ++ } ++ ++ if (ngx_http_upstream_fair_try_peer(pc, fp, n) != NGX_OK) { ++ continue; ++ } ++ ++ /* not in WM_IDLE+no_rr mode: the first completely idle backend gets chosen */ ++ if (weight_mode != WM_IDLE || !fp->peers->no_rr) { ++ best_idx = n; ++ break; ++ } ++ ++ /* in WM_IDLE+no_rr mode we actually prefer slightly loaded backends ++ * to totally idle ones, under the assumption that they're spawned ++ * on demand and can handle up to 'weight' concurrent requests ++ */ ++ if (best_idx == NGX_PEER_INVALID || nreq) { ++ if (best_nreq <= nreq) { ++ continue; ++ } ++ best_idx = n; ++ best_nreq = nreq; ++ } ++ } ++ ++ return best_idx; ++} ++ ++static ngx_int_t ++ngx_http_upstream_choose_fair_peer_busy(ngx_peer_connection_t *pc, ++ ngx_http_upstream_fair_peer_data_t *fp) ++{ ++ ngx_uint_t i, n; ++ ngx_uint_t npeers = fp->peers->number; ++ ngx_uint_t weight_mode = fp->peers->weight_mode; ++ ngx_uint_t best_idx = NGX_PEER_INVALID; ++ ngx_uint_t sched_score; ++ ngx_uint_t best_sched_score = ~0UL; ++ ++ /* ++ * calculate sched scores for all the peers, choosing the lowest one ++ */ ++ for (i = 0, n = fp->current; i < npeers; i++, n = (n + 1) % npeers) { ++ ngx_http_upstream_fair_peer_t *peer; ++ ngx_uint_t nreq; ++ ngx_uint_t weight; ++ ++ peer = &fp->peers->peer[n]; ++ nreq = fp->peers->peer[n].shared->nreq; ++ ++ if (weight_mode == WM_PEAK && nreq >= peer->weight) { ++ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, pc->log, 0, "[upstream_fair] backend %d has nreq %ui >= weight %ui in WM_PEAK mode", n, nreq, peer->weight); ++ continue; ++ } ++ ++ if (ngx_http_upstream_fair_try_peer(pc, fp, n) != NGX_OK) { ++ if (!pc->tries) { ++ ngx_log_debug(NGX_LOG_DEBUG_HTTP, pc->log, 0, "[upstream_fair] all backends exhausted"); ++ return NGX_PEER_INVALID; ++ } ++ ++ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0, "[upstream_fair] backend %d already tried", n); ++ continue; ++ } ++ ++ sched_score = ngx_http_upstream_fair_sched_score(pc, fp, n); ++ ++ if (weight_mode == WM_DEFAULT) { ++ /* ++ * take peer weight into account ++ */ ++ weight = peer->shared->current_weight; ++ if (peer->max_fails) { ++ ngx_uint_t mf = peer->max_fails; ++ weight = peer->shared->current_weight * (mf - peer->shared->fails) / mf; ++ } ++ if (weight > 0) { ++ sched_score /= weight; ++ } ++ ngx_log_debug8(NGX_LOG_DEBUG_HTTP, pc->log, 0, "[upstream_fair] bss = %ui, ss = %ui (n = %d, w = %d/%d, f = %d/%d, weight = %d)", ++ best_sched_score, sched_score, n, peer->shared->current_weight, peer->weight, peer->shared->fails, peer->max_fails, weight); ++ } ++ ++ if (sched_score <= best_sched_score) { ++ best_idx = n; ++ best_sched_score = sched_score; ++ } ++ } ++ ++ return best_idx; ++} ++ ++static ngx_int_t ++ngx_http_upstream_choose_fair_peer(ngx_peer_connection_t *pc, ++ ngx_http_upstream_fair_peer_data_t *fp, ngx_uint_t *peer_id) ++{ ++ ngx_uint_t npeers; ++ ngx_uint_t best_idx = NGX_PEER_INVALID; ++ ngx_uint_t weight_mode; ++ ++ npeers = fp->peers->number; ++ weight_mode = fp->peers->weight_mode; ++ ++ /* just a single backend */ ++ if (npeers == 1) { ++ *peer_id = 0; ++ return NGX_OK; ++ } ++ ++ /* any idle backends? */ ++ best_idx = ngx_http_upstream_choose_fair_peer_idle(pc, fp); ++ if (best_idx != NGX_PEER_INVALID) { ++ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0, "[upstream_fair] peer %i is idle", best_idx); ++ goto chosen; ++ } ++ ++ /* no idle backends, choose the least loaded one */ ++ best_idx = ngx_http_upstream_choose_fair_peer_busy(pc, fp); ++ if (best_idx != NGX_PEER_INVALID) { ++ goto chosen; ++ } ++ ++ return NGX_BUSY; ++ ++chosen: ++ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0, "[upstream_fair] chose peer %i", best_idx); ++ *peer_id = best_idx; ++ ngx_bitvector_set(fp->tried, best_idx); ++ ++ if (weight_mode == WM_DEFAULT) { ++ ngx_http_upstream_fair_peer_t *peer = &fp->peers->peer[best_idx]; ++ ++ if (peer->shared->current_weight-- == 0) { ++ peer->shared->current_weight = peer->weight; ++ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0, "[upstream_fair] peer %d expired weight, reset to %d", best_idx, peer->weight); ++ } ++ } ++ return NGX_OK; ++} ++ ++ngx_int_t ++ngx_http_upstream_get_fair_peer(ngx_peer_connection_t *pc, void *data) ++{ ++ ngx_int_t ret; ++ ngx_uint_t peer_id, i; ++ ngx_http_upstream_fair_peer_data_t *fp = data; ++ ngx_http_upstream_fair_peer_t *peer; ++ ngx_atomic_t *lock; ++ ++ peer_id = fp->current; ++ fp->current = (fp->current + 1) % fp->peers->number; ++ ++ lock = &fp->peers->shared->lock; ++ ngx_spinlock(lock, ngx_pid, 1024); ++ ret = ngx_http_upstream_choose_fair_peer(pc, fp, &peer_id); ++ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, pc->log, 0, "[upstream_fair] fp->current = %d, peer_id = %d, ret = %d", ++ fp->current, peer_id, ret); ++ ++ if (pc) ++ pc->tries--; ++ ++ if (ret == NGX_BUSY) { ++ for (i = 0; i < fp->peers->number; i++) { ++ fp->peers->peer[i].shared->fails = 0; ++ } ++ ++ pc->name = fp->peers->name; ++ fp->current = NGX_PEER_INVALID; ++ ngx_spinlock_unlock(lock); ++ return NGX_BUSY; ++ } ++ ++ /* assert(ret == NGX_OK); */ ++ peer = &fp->peers->peer[peer_id]; ++ fp->current = peer_id; ++ if (!fp->peers->no_rr) { ++ fp->peers->current = peer_id; ++ } ++ pc->sockaddr = peer->sockaddr; ++ pc->socklen = peer->socklen; ++ pc->name = &peer->name; ++ ++ peer->shared->last_req_id = fp->peers->shared->total_requests; ++ ngx_http_upstream_fair_update_nreq(fp, 1, pc->log); ++ peer->shared->total_req++; ++ ngx_spinlock_unlock(lock); ++ return ret; ++} ++ ++ ++void ++ngx_http_upstream_free_fair_peer(ngx_peer_connection_t *pc, void *data, ++ ngx_uint_t state) ++{ ++ ngx_http_upstream_fair_peer_data_t *fp = data; ++ ngx_http_upstream_fair_peer_t *peer; ++ ngx_atomic_t *lock; ++ ++ ngx_log_debug4(NGX_LOG_DEBUG_HTTP, pc->log, 0, "[upstream_fair] fp->current = %d, state = %ui, pc->tries = %d, pc->data = %p", ++ fp->current, state, pc->tries, pc->data); ++ ++ if (fp->current == NGX_PEER_INVALID) { ++ return; ++ } ++ ++ lock = &fp->peers->shared->lock; ++ ngx_spinlock(lock, ngx_pid, 1024); ++ if (!ngx_bitvector_test(fp->done, fp->current)) { ++ ngx_bitvector_set(fp->done, fp->current); ++ ngx_http_upstream_fair_update_nreq(fp, -1, pc->log); ++ } ++ ++ if (fp->peers->number == 1) { ++ pc->tries = 0; ++ } ++ ++ if (state & NGX_PEER_FAILED) { ++ peer = &fp->peers->peer[fp->current]; ++ ++ peer->shared->fails++; ++ peer->accessed = ngx_time(); ++ } ++ ngx_spinlock_unlock(lock); ++} ++ ++/* ++ * walk through the rbtree, removing old entries and looking for ++ * a matching one -- compared by (cycle, peers) pair ++ * ++ * no attempt at optimisation is made, for two reasons: ++ * - the tree will be quite small, anyway ++ * - being called once per worker startup per upstream block, ++ * this code isn't really the hot path ++ */ ++static ngx_http_upstream_fair_shm_block_t * ++ngx_http_upstream_fair_walk_shm( ++ ngx_slab_pool_t *shpool, ++ ngx_rbtree_node_t *node, ++ ngx_rbtree_node_t *sentinel, ++ ngx_http_upstream_fair_peers_t *peers) ++{ ++ ngx_http_upstream_fair_shm_block_t *uf_node; ++ ngx_http_upstream_fair_shm_block_t *found_node = NULL; ++ ngx_http_upstream_fair_shm_block_t *tmp_node; ++ ++ if (node == sentinel) { ++ return NULL; ++ } ++ ++ /* visit left node */ ++ if (node->left != sentinel) { ++ tmp_node = ngx_http_upstream_fair_walk_shm(shpool, node->left, ++ sentinel, peers); ++ if (tmp_node) { ++ found_node = tmp_node; ++ } ++ } ++ ++ /* visit right node */ ++ if (node->right != sentinel) { ++ tmp_node = ngx_http_upstream_fair_walk_shm(shpool, node->right, ++ sentinel, peers); ++ if (tmp_node) { ++ found_node = tmp_node; ++ } ++ } ++ ++ /* visit current node */ ++ uf_node = (ngx_http_upstream_fair_shm_block_t *) node; ++ if (uf_node->generation != ngx_http_upstream_fair_generation) { ++ ngx_spinlock(&uf_node->lock, ngx_pid, 1024); ++ if (uf_node->total_nreq == 0) { ++ /* don't bother unlocking */ ++ ngx_rbtree_delete(ngx_http_upstream_fair_rbtree, node); ++ ngx_slab_free_locked(shpool, node); ++ } ++ ngx_spinlock_unlock(&uf_node->lock); ++ } else if (uf_node->peers == (uintptr_t) peers) { ++ found_node = uf_node; ++ } ++ ++ return found_node; ++} ++ ++static ngx_int_t ++ngx_http_upstream_fair_shm_alloc(ngx_http_upstream_fair_peers_t *usfp, ngx_log_t *log) ++{ ++ ngx_slab_pool_t *shpool; ++ ngx_uint_t i; ++ ++ if (usfp->shared) { ++ return NGX_OK; ++ } ++ ++ shpool = (ngx_slab_pool_t *)ngx_http_upstream_fair_shm_zone->shm.addr; ++ ++ ngx_shmtx_lock(&shpool->mutex); ++ ++ usfp->shared = ngx_http_upstream_fair_walk_shm(shpool, ++ ngx_http_upstream_fair_rbtree->root, ++ ngx_http_upstream_fair_rbtree->sentinel, ++ usfp); ++ ++ if (usfp->shared) { ++ ngx_shmtx_unlock(&shpool->mutex); ++ return NGX_OK; ++ } ++ ++ usfp->shared = ngx_slab_alloc_locked(shpool, ++ sizeof(ngx_http_upstream_fair_shm_block_t) + ++ (usfp->number - 1) * sizeof(ngx_http_upstream_fair_shared_t)); ++ ++ if (!usfp->shared) { ++ ngx_shmtx_unlock(&shpool->mutex); ++ if (!usfp->size_err) { ++ ngx_log_error(NGX_LOG_EMERG, log, 0, ++ "upstream_fair_shm_size too small (current value is %udKiB)", ++ ngx_http_upstream_fair_shm_size >> 10); ++ usfp->size_err = 1; ++ } ++ return NGX_ERROR; ++ } ++ ++ usfp->shared->node.key = ngx_crc32_short((u_char *) &ngx_cycle, sizeof ngx_cycle) ^ ++ ngx_crc32_short((u_char *) &usfp, sizeof(usfp)); ++ ++ usfp->shared->generation = ngx_http_upstream_fair_generation; ++ usfp->shared->peers = (uintptr_t) usfp; ++ usfp->shared->total_nreq = 0; ++ usfp->shared->total_requests = 0; ++ ++ for (i = 0; i < usfp->number; i++) { ++ usfp->shared->stats[i].nreq = 0; ++ usfp->shared->stats[i].last_req_id = 0; ++ usfp->shared->stats[i].total_req = 0; ++ } ++ ++ ngx_rbtree_insert(ngx_http_upstream_fair_rbtree, &usfp->shared->node); ++ ++ ngx_shmtx_unlock(&shpool->mutex); ++ return NGX_OK; ++} ++ ++ngx_int_t ++ngx_http_upstream_init_fair_peer(ngx_http_request_t *r, ++ ngx_http_upstream_srv_conf_t *us) ++{ ++ ngx_http_upstream_fair_peer_data_t *fp; ++ ngx_http_upstream_fair_peers_t *usfp; ++ ngx_uint_t n; ++ ++ fp = r->upstream->peer.data; ++ ++ if (fp == NULL) { ++ fp = ngx_palloc(r->pool, sizeof(ngx_http_upstream_fair_peer_data_t)); ++ if (fp == NULL) { ++ return NGX_ERROR; ++ } ++ ++ r->upstream->peer.data = fp; ++ } ++ ++ usfp = us->peer.data; ++ ++ fp->tried = ngx_bitvector_alloc(r->pool, usfp->number, &fp->data); ++ fp->done = ngx_bitvector_alloc(r->pool, usfp->number, &fp->data2); ++ ++ if (fp->tried == NULL || fp->done == NULL) { ++ return NGX_ERROR; ++ } ++ ++ /* set up shared memory area */ ++ ngx_http_upstream_fair_shm_alloc(usfp, r->connection->log); ++ ++ fp->current = usfp->current; ++ fp->peers = usfp; ++ usfp->shared->total_requests++; ++ ++ for (n = 0; n < usfp->number; n++) { ++ usfp->peer[n].shared = &usfp->shared->stats[n]; ++ } ++ ++ r->upstream->peer.get = ngx_http_upstream_get_fair_peer; ++ r->upstream->peer.free = ngx_http_upstream_free_fair_peer; ++ r->upstream->peer.tries = usfp->number; ++#if (NGX_HTTP_SSL) ++ r->upstream->peer.set_session = ++ ngx_http_upstream_fair_set_session; ++ r->upstream->peer.save_session = ++ ngx_http_upstream_fair_save_session; ++#endif ++ ++ return NGX_OK; ++} ++ ++#if (NGX_HTTP_SSL) ++static ngx_int_t ++ngx_http_upstream_fair_set_session(ngx_peer_connection_t *pc, void *data) ++{ ++ ngx_http_upstream_fair_peer_data_t *fp = data; ++ ++ ngx_int_t rc; ++ ngx_ssl_session_t *ssl_session; ++ ngx_http_upstream_fair_peer_t *peer; ++ ++ if (fp->current == NGX_PEER_INVALID) ++ return NGX_OK; ++ ++ peer = &fp->peers->peer[fp->current]; ++ ++ /* TODO: threads only mutex */ ++ /* ngx_lock_mutex(fp->peers->mutex); */ ++ ++ ssl_session = peer->ssl_session; ++ ++ rc = ngx_ssl_set_session(pc->connection, ssl_session); ++ ++ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0, ++ "set session: %p:%d", ++ ssl_session, ssl_session ? ssl_session->references : 0); ++ ++ /* ngx_unlock_mutex(fp->peers->mutex); */ ++ ++ return rc; ++} ++ ++static void ++ngx_http_upstream_fair_save_session(ngx_peer_connection_t *pc, void *data) ++{ ++ ngx_http_upstream_fair_peer_data_t *fp = data; ++ ++ ngx_ssl_session_t *old_ssl_session, *ssl_session; ++ ngx_http_upstream_fair_peer_t *peer; ++ ++ if (fp->current == NGX_PEER_INVALID) ++ return; ++ ++ ssl_session = ngx_ssl_get_session(pc->connection); ++ ++ if (ssl_session == NULL) { ++ return; ++ } ++ ++ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0, ++ "save session: %p:%d", ssl_session, ssl_session->references); ++ ++ peer = &fp->peers->peer[fp->current]; ++ ++ /* TODO: threads only mutex */ ++ /* ngx_lock_mutex(fp->peers->mutex); */ ++ ++ old_ssl_session = peer->ssl_session; ++ peer->ssl_session = ssl_session; ++ ++ /* ngx_unlock_mutex(fp->peers->mutex); */ ++ ++ if (old_ssl_session) { ++ ++ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0, ++ "old session: %p:%d", ++ old_ssl_session, old_ssl_session->references); ++ ++ /* TODO: may block */ ++ ++ ngx_ssl_free_session(old_ssl_session); ++ } ++} ++ ++#endif ++ ++#if (NGX_HTTP_EXTENDED_STATUS) ++static void ++ngx_http_upstream_fair_walk_status(ngx_pool_t *pool, ngx_chain_t *cl, ngx_int_t *length, ++ ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel) ++{ ++ ngx_http_upstream_fair_shm_block_t *s_node = (ngx_http_upstream_fair_shm_block_t *) node; ++ ngx_http_upstream_fair_peers_t *peers; ++ ngx_chain_t *new_cl; ++ ngx_buf_t *b; ++ ngx_uint_t size, i; ++ ++ if (node == sentinel) { ++ return; ++ } ++ ++ if (node->left != sentinel) { ++ ngx_http_upstream_fair_walk_status(pool, cl, length, node->left, sentinel); ++ } ++ ++ if (s_node->generation != ngx_http_upstream_fair_generation) { ++ size = 100; ++ peers = NULL; ++ } else { ++ /* this is rather ugly (casting an uintptr_t back into a pointer ++ * but as long as the generation is still the same (verified above), ++ * it should be still safe ++ */ ++ peers = (ngx_http_upstream_fair_peers_t *) s_node->peers; ++ if (!peers->shared) { ++ goto next; ++ } ++ ++ size = 200 + peers->number * 120; /* LOTS of slack */ ++ } ++ ++ b = ngx_create_temp_buf(pool, size); ++ if (!b) { ++ goto next; ++ } ++ ++ new_cl = ngx_alloc_chain_link(pool); ++ if (!new_cl) { ++ goto next; ++ } ++ ++ new_cl->buf = b; ++ new_cl->next = NULL; ++ ++ while (cl->next) { ++ cl = cl->next; ++ } ++ cl->next = new_cl; ++ ++ if (peers) { ++ b->last = ngx_sprintf(b->last, "upstream %V (%p): current peer %d/%d, total requests: %ui\n", peers->name, (void*) node, peers->current, peers->number, s_node->total_requests); ++ for (i = 0; i < peers->number; i++) { ++ ngx_http_upstream_fair_peer_t *peer = &peers->peer[i]; ++ ngx_http_upstream_fair_shared_t *sh = peer->shared; ++ b->last = ngx_sprintf(b->last, " peer %d: %V weight: %d/%d, fails: %d/%d, acc: %d, down: %d, nreq: %d, total_req: %ui, last_req: %ui\n", ++ i, &peer->name, sh->current_weight, peer->weight, sh->fails, peer->max_fails, peer->accessed, peer->down, ++ sh->nreq, sh->total_req, sh->last_req_id); ++ } ++ } else { ++ b->last = ngx_sprintf(b->last, "upstream %p: gen %ui != %ui, total_nreq = %ui", (void*) node, s_node->generation, ngx_http_upstream_fair_generation, s_node->total_nreq); ++ } ++ b->last = ngx_sprintf(b->last, "\n"); ++ b->last_buf = 1; ++ ++ *length += b->last - b->pos; ++ ++ if (cl->buf) { ++ cl->buf->last_buf = 0; ++ } ++ ++ cl = cl->next; ++next: ++ ++ if (node->right != sentinel) { ++ ngx_http_upstream_fair_walk_status(pool, cl, length, node->right, sentinel); ++ } ++} ++ ++static ngx_chain_t* ++ngx_http_upstream_fair_report_status(ngx_http_request_t *r, ngx_int_t *length) ++{ ++ ngx_buf_t *b; ++ ngx_chain_t *cl; ++ ngx_slab_pool_t *shpool; ++ ++ b = ngx_create_temp_buf(r->pool, sizeof("\nupstream_fair status report:\n")); ++ if (!b) { ++ return NULL; ++ } ++ ++ cl = ngx_alloc_chain_link(r->pool); ++ if (!cl) { ++ return NULL; ++ } ++ cl->next = NULL; ++ cl->buf = b; ++ ++ b->last = ngx_cpymem(b->last, "\nupstream_fair status report:\n", ++ sizeof("\nupstream_fair status report:\n") - 1); ++ ++ *length = b->last - b->pos; ++ ++ shpool = (ngx_slab_pool_t *)ngx_http_upstream_fair_shm_zone->shm.addr; ++ ++ ngx_shmtx_lock(&shpool->mutex); ++ ++ ngx_http_upstream_fair_walk_status(r->pool, cl, ++ length, ++ ngx_http_upstream_fair_rbtree->root, ++ ngx_http_upstream_fair_rbtree->sentinel); ++ ++ ngx_shmtx_unlock(&shpool->mutex); ++ ++ if (!cl->next || !cl->next->buf) { ++ /* no upstream_fair status to report */ ++ return NULL; ++ } ++ ++ return cl; ++} ++#endif ++ ++/* vim: set et ts=4 sw=4: */ diff -Nru nginx-0.5.33/debian/patches/series nginx-0.8.53/debian/patches/series --- nginx-0.5.33/debian/patches/series 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/debian/patches/series 2011-02-26 14:04:40.000000000 +0000 @@ -0,0 +1,3 @@ +nginx-upstream-fair.diff +dlopen.diff +nginx-echo.diff diff -Nru nginx-0.5.33/debian/postinst nginx-0.8.53/debian/postinst --- nginx-0.5.33/debian/postinst 2011-02-26 14:04:40.000000000 +0000 +++ nginx-0.8.53/debian/postinst 2011-02-26 14:04:40.000000000 +0000 @@ -3,54 +3,47 @@ set -e case "$1" in - - configure) - - if [ -z $2 ] && [ ! -e /etc/nginx/sites-enabled/default ] ; - then - ln -s /etc/nginx/sites-available/default /etc/nginx/sites-enabled/default - fi - + configure) + if [ -z $2 ] && [ ! -e /etc/nginx/sites-enabled/default ] ; + then + ln -s /etc/nginx/sites-available/default /etc/nginx/sites-enabled/default + fi ;; - - abort-upgrade|abort-remove|abort-deconfigure) - + abort-upgrade|abort-remove|abort-deconfigure) ;; - - *) - - echo "postinst called with unknown argument \`$1'" >&2 - exit 1 - + *) + echo "postinst called with unknown argument \`$1'" >&2 + exit 1 ;; - esac -if [ -x "/etc/init.d/nginx" ] +if [ -x /etc/init.d/nginx ] then - if [ -f /var/run/nginx.pid ] && /bin/pidof /usr/sbin/nginx >/dev/null + if [ -f /var/run/nginx.pid ] && pidof /usr/sbin/nginx >/dev/null + then + echo "Trying a soft restart" + NGX_PID=`cat /var/run/nginx.pid` + if kill -s USR2 $NGX_PID 2>/dev/null + then + echo "PID IS RIGHT" + while [ ! -f /var/run/nginx.pid.oldbin ] + do + echo "WAITING" + cnt=`expr $cnt + 1` + if [ $cnt -gt 10 ] then - echo "Trying a soft restart" - if /bin/kill -USR2 `cat /var/run/nginx.pid` 2>/dev/null - then - echo "PID IS RIGHT" - while [ ! -f /var/run/nginx.pid.oldbin ] - do - echo "WAITING" - cnt=`expr $cnt + 1` - if [ $cnt -gt 10 ] - then - echo "Nginx 'soft' update failed, doing restart"; - /bin/kill -9 `cat /var/run/nginx.pid` - invoke-rc.d nginx start - exit 0 - fi - sleep 1 - done - echo "QUIT" - /bin/kill -QUIT `cat /var/run/nginx.pid.oldbin` - fi - fi + echo "Nginx 'soft' update failed, doing restart" + kill -s KILL $NGX_PID + invoke-rc.d nginx start + exit 0 + fi + sleep 1 + done + echo "QUIT" + NGX_OLD_PID=`cat /var/run/nginx.pid.oldbin` + kill -s QUIT $NGX_OLD_PID + fi + fi fi #DEBHELPER# diff -Nru nginx-0.5.33/debian/README.Debian nginx-0.8.53/debian/README.Debian --- nginx-0.5.33/debian/README.Debian 2011-02-26 14:04:40.000000000 +0000 +++ nginx-0.8.53/debian/README.Debian 2011-02-26 14:04:40.000000000 +0000 @@ -1,12 +1,9 @@ -Please note that Debian is currently based on the 0.5 branch of the -nginx webserver, since 0.6 isn't regarded as stable now. However, please -note that it should be easy to build a Debian package out of the 0.6 -branch just by copying the debian/ subdirectory from the source package. -Well, it should. +README for Debian +----------------- -The package didn't handle paths correctly (still), so the configure line -has grown up a bit. I've also tweaked a bit the main configuration file. -If you would like to supply patches and write or translate documentation -from russian, it will be greatly appreciated. + Files under /var/www/ are not supported as per Debian Policy. + Please see: http://lintian.debian.org/tags/dir-or-file-in-var-www.html and, + http://www.pathname.com/fhs/pub/fhs-2.3.html#THEVARHIERARCHY for more + details and explanations. -Jose Parrella -- bureado@debian.org.ve + -- Kartik Mistry Fri, 05 Mar 2010 13:31:15 +0530 diff -Nru nginx-0.5.33/debian/rules nginx-0.8.53/debian/rules --- nginx-0.5.33/debian/rules 2011-02-26 14:04:40.000000000 +0000 +++ nginx-0.8.53/debian/rules 2011-02-26 14:04:40.000000000 +0000 @@ -2,27 +2,61 @@ CFLAGS = -Wall -g +DEB_BUILD_ARCH ?=$(shell dpkg-architecture -qDEB_BUILD_ARCH) +ifneq (,$(findstring sparc,$(DEB_BUILD_ARCH))) + CONFIGURE_OPTS = --with-cc-opt="-m32 -mcpu=ultrasparc" +endif + ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) CFLAGS += -O0 else CFLAGS += -O2 endif -config.status: +config.sub: dh_testdir - ifneq "$(wildcard /usr/share/misc/config.sub)" "" cp -f /usr/share/misc/config.sub config.sub endif + +config.guess: + dh_testdir ifneq "$(wildcard /usr/share/misc/config.guess)" "" cp -f /usr/share/misc/config.guess config.guess endif - ./configure --conf-path=/etc/nginx/nginx.conf \ - --error-log-path=/var/log/nginx/error.log --pid-path=/var/run/nginx.pid \ - --lock-path=/var/lock/nginx.lock --http-log-path=/var/log/nginx/access.log \ - --http-client-body-temp-path=/var/lib/nginx/body --http-proxy-temp-path=/var/lib/nginx/proxy \ - --http-fastcgi-temp-path=/var/lib/nginx/fastcgi --with-debug --with-http_stub_status_module \ - --with-http_flv_module --with-http_ssl_module --with-http_dav_module + +config.status: config.sub config.guess + ./configure \ + --conf-path=/etc/nginx/nginx.conf \ + --error-log-path=/var/log/nginx/error.log \ + --http-client-body-temp-path=/var/lib/nginx/body \ + --http-fastcgi-temp-path=/var/lib/nginx/fastcgi \ + --http-log-path=/var/log/nginx/access.log \ + --http-proxy-temp-path=/var/lib/nginx/proxy \ + --http-scgi-temp-path=/var/lib/nginx/scgi \ + --http-uwsgi-temp-path=/var/lib/nginx/uwsgi \ + --lock-path=/var/lock/nginx.lock \ + --pid-path=/var/run/nginx.pid \ + --with-debug \ + --with-http_dav_module \ + --with-http_flv_module \ + --with-http_geoip_module \ + --with-http_gzip_static_module \ + --with-http_image_filter_module \ + --with-http_realip_module \ + --with-http_stub_status_module \ + --with-http_ssl_module \ + --with-http_sub_module \ + --with-http_xslt_module \ + --with-ipv6 \ + --with-sha1=/usr/include/openssl \ + --with-md5=/usr/include/openssl \ + --with-mail \ + --with-mail_ssl_module \ + --add-module=$(CURDIR)/modules/nginx-upstream-fair \ + --add-module=$(CURDIR)/modules/nginx-echo \ + $(CONFIGURE_OPTS) >$@ + touch $@ build: config.status $(MAKE) build @@ -34,21 +68,15 @@ clean: dh_testdir dh_testroot - rm -f build-stamp + rm -f config.sub config.guess + rm -f build-stamp [ ! -f Makefile ] || $(MAKE) clean - -ifneq "$(wildcard /usr/share/misc/config.sub)" "" - rm -f config.sub -endif -ifneq "$(wildcard /usr/share/misc/config.guess)" "" - rm -f config.guess -endif - dh_clean + dh_clean install: dh_testdir dh_testroot - dh_clean -k + dh_clean -k dh_installdirs dh_install @@ -57,13 +85,14 @@ binary-arch: install dh_testdir dh_testroot + dh_installman debian/nginx.1 dh_installchangelogs CHANGES dh_installdocs + dh_installexamples dh_installinit -r --no-start - dh_installman debian/nginx.1 dh_installlogrotate dh_link - dh_strip + dh_strip --dbg-package=nginx-dbg dh_compress dh_fixperms dh_installdeb @@ -74,4 +103,4 @@ binary: binary-indep binary-arch -.PHONY: build clean binary-indep binary-arch binary install +.PHONY: build clean binary-indep binary-arch binary install diff -Nru nginx-0.5.33/debian/source/format nginx-0.8.53/debian/source/format --- nginx-0.5.33/debian/source/format 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/debian/source/format 2011-02-26 14:04:40.000000000 +0000 @@ -0,0 +1 @@ +1.0 diff -Nru nginx-0.5.33/debian/ufw.profile nginx-0.8.53/debian/ufw.profile --- nginx-0.5.33/debian/ufw.profile 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/debian/ufw.profile 2011-02-26 14:04:40.000000000 +0000 @@ -0,0 +1,14 @@ +[Nginx HTTP] +title=Web Server (Nginx, HTTP) +description=Small, but very powerful and efficient web server +ports=80/tcp + +[Nginx HTTPS] +title=Web Server (Nginx, HTTPS) +description=Small, but very powerful and efficient web server +ports=443/tcp + +[Nginx Full] +title=Web Server (Nginx, HTTP + HTTPS) +description=Small, but very powerful and efficient web server +ports=80,443/tcp diff -Nru nginx-0.5.33/debian/watch nginx-0.8.53/debian/watch --- nginx-0.5.33/debian/watch 2011-02-26 14:04:40.000000000 +0000 +++ nginx-0.8.53/debian/watch 2011-02-26 14:04:40.000000000 +0000 @@ -1,11 +1,2 @@ -# Example watch control file for uscan -# Rename this file to "watch" and then you can run the "uscan" command -# to check for upstream updates and more. -# See uscan(1) for format - -# Compulsory line, this is a version 3 file version=3 - -# Uncomment to examine a Webpage -# -http://sysoev.ru/nginx/ nginx-(.*)\.tar\.gz +http://sysoev.ru/nginx/ nginx-([\.0-9]+)\.tar\.gz diff -Nru nginx-0.5.33/LICENSE nginx-0.8.53/LICENSE --- nginx-0.5.33/LICENSE 2007-01-01 18:19:37.000000000 +0000 +++ nginx-0.8.53/LICENSE 2010-01-01 14:53:56.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2007 Igor Sysoev + * Copyright (C) 2002-2010 Igor Sysoev * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -21,5 +21,4 @@ * 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. - * */ diff -Nru nginx-0.5.33/modules/nginx-echo/config nginx-0.8.53/modules/nginx-echo/config --- nginx-0.5.33/modules/nginx-echo/config 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/modules/nginx-echo/config 2011-02-26 14:04:40.000000000 +0000 @@ -0,0 +1,5 @@ +ngx_addon_name=ngx_http_echo_module +HTTP_AUX_FILTER_MODULES="$HTTP_AUX_FILTER_MODULES ngx_http_echo_module" +NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/src/ngx_http_echo_module.c $ngx_addon_dir/src/ngx_http_echo_util.c $ngx_addon_dir/src/ngx_http_echo_timer.c $ngx_addon_dir/src/ngx_http_echo_var.c $ngx_addon_dir/src/ngx_http_echo_handler.c $ngx_addon_dir/src/ngx_http_echo_filter.c $ngx_addon_dir/src/ngx_http_echo_sleep.c $ngx_addon_dir/src/ngx_http_echo_location.c $ngx_addon_dir/src/ngx_http_echo_echo.c $ngx_addon_dir/src/ngx_http_echo_request_info.c $ngx_addon_dir/src/ngx_http_echo_subrequest.c $ngx_addon_dir/src/ngx_http_echo_foreach.c" +NGX_ADDON_DEPS="$NGX_ADDON_DEPS $ngx_addon_dir/src/ngx_http_echo_module.h $ngx_addon_dir/src/ddebug.h $ngx_addon_dir/src/ngx_http_echo_handler.h $ngx_addon_dir/src/ngx_http_echo_util.h $ngx_addon_dir/src/ngx_http_echo_sleep.h $ngx_addon_dir/src/ngx_http_echo_filter.h $ngx_addon_dir/src/ngx_http_echo_var.h $ngx_addon_dir/src/ngx_http_echo_location.h $ngx_addon_dir/src/ngx_http_echo_echo.h $ngx_addon_dir/src/ngx_http_echo_request_info.h $ngx_addon_dir/src/ngx_http_echo_subrequest.h $ngx_addon_dir/src/ngx_http_echo_foreach.h" + diff -Nru nginx-0.5.33/modules/nginx-echo/doc/manpage.wiki nginx-0.8.53/modules/nginx-echo/doc/manpage.wiki --- nginx-0.5.33/modules/nginx-echo/doc/manpage.wiki 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/modules/nginx-echo/doc/manpage.wiki 2011-02-26 14:04:40.000000000 +0000 @@ -0,0 +1,1545 @@ += Name = + +'''ngx_echo''' - Brings "echo", "sleep", "time", "exec" and more shell-style goodies to Nginx config file. + +''This module is not distributed with the Nginx source.'' See [[#Installation|the installation instructions]]. + += Version = + +This document describes echo-nginx-module [http://github.com/agentzh/echo-nginx-module/tarball/v0.34 v0.34] released on September 14, 2010. + += Synopsis = + + + location /hello { + echo "hello, world!"; + } + + + + location /hello { + echo -n "hello, " + echo "world!"; + } + + + + location /timed_hello { + echo_reset_timer; + echo hello world; + echo "'hello world' takes about $echo_timer_elapsed sec."; + echo hiya igor; + echo "'hiya igor' takes about $echo_timer_elapsed sec."; + } + + + + location /echo_with_sleep { + echo hello; + echo_flush; # ensure the client can see previous output immediately + echo_sleep 2.5; # in sec + echo world; + } + + + + # in the following example, accessing /echo yields + # hello + # world + # blah + # hiya + # igor + location /echo { + echo_before_body hello; + echo_before_body world; + proxy_pass $scheme://127.0.0.1:$server_port$request_uri/more; + echo_after_body hiya; + echo_after_body igor; + } + location /echo/more { + echo blah; + } + + + + # the output of /main might be + # hello + # world + # took 0.000 sec for total. + # and the whole request would take about 2 sec to complete. + location /main { + echo_reset_timer; + + # subrequests in parallel + echo_location_async /sub1; + echo_location_async /sub2; + + echo "took $echo_timer_elapsed sec for total."; + } + location /sub1 { + echo_sleep 2; + echo hello; + } + location /sub2 { + echo_sleep 1; + echo world; + } + + + + # the output of /main might be + # hello + # world + # took 3.003 sec for total. + # and the whole request would take about 3 sec to complete. + location /main { + echo_reset_timer; + + # subrequests in series (chained by CPS) + echo_location /sub1; + echo_location /sub2; + + echo "took $echo_timer_elapsed sec for total."; + } + location /sub1 { + echo_sleep 2; + echo hello; + } + location /sub2 { + echo_sleep 1; + echo world; + } + + + + # Accessing /dup gives + # ------ END ------ + location /dup { + echo_duplicate 3 "--"; + echo_duplicate 1 " END "; + echo_duplicate 3 "--"; + echo; + } + + + + # /bighello will generate 1000,000,000 hello's. + location /bighello { + echo_duplicate 1000_000_000 'hello'; + } + + + + # echo back the client request + location /echoback { + echo_duplicate 1 $echo_client_request_headers; + echo "\r"; + + echo_read_request_body; + + echo_request_body; + } + + + + # GET /multi will yields + # querystring: foo=Foo + # method: POST + # body: hi + # content length: 2 + # /// + # querystring: bar=Bar + # method: PUT + # body: hello + # content length: 5 + # /// + location /multi { + echo_subrequest_async POST '/sub' -q 'foo=Foo' -b 'hi'; + echo_subrequest_async PUT '/sub' -q 'bar=Bar' -b 'hello'; + } + location /sub { + echo "querystring: $query_string"; + echo "method: $echo_request_method"; + echo "body: $echo_request_body"; + echo "content length: $http_content_length"; + echo '///'; + } + + + + # GET /merge?/foo.js&/bar/blah.js&/yui/baz.js will merge the .js resources together + location /merge { + default_type 'text/javascript'; + echo_foreach_split '&' $query_string; + echo "/* JS File $echo_it */"; + echo_location_async $echo_it; + echo; + echo_end; + } + + + + # accessing /if?val=abc yields the "hit" output + # while /if?val=bcd yields "miss": + location ^~ /if { + set $res miss; + if ($arg_val ~* '^a') { + set $res hit; + echo $res; + } + echo $res; + } + + += Description = + +This module wraps lots of Nginx internal APIs for streaming input and output, parallel/sequential subrequests, timers and sleeping, as well as various meta data accessing. + +Basically it provides various utilities that help testing and debugging of other modules by trivially emulating different kinds of faked subrequest locations. + +People will also find it useful in real-world applications that need to + +# serve static contents directly from memory (loading from the Nginx config file). +# wrap the upstream response with custom header and footer (kinda like the [[NginxHttpAdditionModule|addition module]] but with contents read directly from the config file and Nginx variables). +# merge contents of various "Nginx locations" (i.e., subrequests) together in a single main request (using [[#echo_location|echo_location]] and its friends). + +This is a special dual-role module that can ''lazily'' serve as a content handler or register itself as an output filter only upon demand. By default, this module does not do anything at all. + +Use of any of this module's directives (no matter [[#Content Handler Directives|content handler directives]] or [[#Filter Directives|filter directives]]) will force the chunked encoding to be used for the HTTP response due to the streaming nature of this module (unless HTTP 1.0 is enforced by the client and the Content-Length header will be set to the size of the first handler directive that generates contents). + +Technially, this module has also demonstrated the following techniques that might be helpful for module writers: + +# Issue parallel subreqeusts directly from content handler. +# Issue chained subrequests directly from content handler, by passing continuation along the subrequest chain. +# Issue subrequests with all HTTP 1.1 methods and even an optional faked HTTP request body. +# Interact with the Nginx event model directly from content handler using custom events and timers, and resume the content handler back if necessary. +# Dual-role module that can (lazily) serve as a content handler or an output filter or both. +# Nginx config file variable creation and interpolation. +# Streaming output control using output_chain, flush and its friends. +# Read client request body from the content handler, and returns back (asynchronously) to the content handler after completion. +# Use Perl-based declarative [[#Test Suite|test suite]] to drive the development of Nginx C modules. + += Content Handler Directives = + +Use of the following directives register this module to the current Nginx location as a content handler. If you want to use another module, like the [[NginxHttpProxyModule|standard proxy module]], as the content handler, use the [[#Filter Directives|filter directives]] provided by this module. + +All the content handler directives can be mixed together in a single Nginx location and they're supposed to run sequentially just as in the Bash scripting language. + +Every content handler directive supports variable interpolation in its arguments (if any). + +The MIME type set by the [[NginxHttpCoreModule#default_type|standard default_type directive]] is respected by this module, as in: + + + location /hello { + default_type text/plain; + echo hello; + } + + +Then on the client side: + + + $ curl -I 'http://localhost/echo' + HTTP/1.1 200 OK + Server: nginx/0.8.20 + Date: Sat, 17 Oct 2009 03:40:19 GMT + Content-Type: text/plain + Connection: keep-alive + + +Since the [[#v0.22|v0.22]] release, all of the directives are allowed in the [[NginxHttpRewriteModule|rewrite module]]'s [[NginxHttpRewriteModule#if|if]] directive block, for instance: + + + location ^~ /if { + set $res miss; + if ($arg_val ~* '^a') { + set $res hit; + echo $res; + } + echo $res; + } + + +== echo == +'''syntax:''' ''echo [options] ...'' + +'''default:''' ''no'' + +'''context:''' ''location'' + +Sends arguments joined by spaces, along with a trailing newline, out to the client. + +Note that the data might be buffered by Nginx's underlying buffer. To force the output data flushed immediately, use the [[#echo_flush|echo_flush]] command just after echo, as in + + + echo hello world; + echo_flush; + + +When no argument is specified, ''echo'' emits the trailing newline alone, just like the ''echo'' command in shell. + +Variables may appear in the arguments. An example is + + + echo The current request uri is $request_uri; + + +where [[NginxHttpCoreModule#$request_uri|$request_uri]] is a variable exposed by the [[NginxHttpCoreModule]]. + +This command can be used multiple times in a single location configuration, as in + + + location /echo { + echo hello; + echo world; + } + + +The output on the client side looks like this + + + $ curl 'http://localhost/echo' + hello + world + + +Special characters like newlines (\n) and tabs (\t) can be escaped using C-style escaping sequences. But a notable exception is the dollar sign ($). As of Nginx 0.8.20, there's still no clean way to esacpe this characters. (A work-around might be to use a $echo_dollor variable that is always evaluated to the constant $ character. This feature will possibly be introduced in a future version of this module.) + +As of the echo [[#v0.28|v0.28]] release, one can suppress the trailing newline character in the output by using the -n option, as in + + + location /echo { + echo -n "hello, "; + echo "world"; + } + + +Accessing /echo gives + + + $ curl 'http://localhost/echo' + hello, world + + +Leading -n in variable values won't take effect and will be emitted literally, as in + + + location /echo { + set $opt -n; + echo $opt "hello,"; + echo "world"; + } + + +This gives the following output + + + $ curl 'http://localhost/echo' + -n hello, + world + + +One can output leading -n literals and other options using the special -- option like this + + + location /echo { + echo -- -n is an option; + } + + +which yields + + + $ curl 'http://localhost/echo' + -n is an option + + +== echo_duplicate == +'''syntax:''' ''echo_duplicate '' + +'''default:''' ''no'' + +'''context:''' ''location'' + +Outputs duplication of a string indicated by the second argument, using the times specified in the first argument. + +For instance, + + + location /dup { + echo_duplicate 3 "abc"; + } + + +will lead to an output of "abcabcabc". + +Underscores are allowed in the count number, just like in Perl. For example, to emit 1000,000,000 instances of "hello, world": + + + location /many_hellos { + echo_duplicate 1000_000_000 "hello, world"; + } + + +The count argument could be zero, but not negative. The second string argument could be an empty string ("") likewise. + +Unlike the [[#echo|echo]] directive, no trailing newline is appended to the result. So it's possible to "abuse" this directive as a no-trailing-newline version of [[#echo|echo]] by using "count" 1, as in + + + location /echo_art { + echo_duplicate 2 '---'; + echo_duplicate 1 ' END '; # we don't want a trailing newline here + echo_duplicate 2 '---'; + echo; # we want a trailing newline here... + } + + +You get + + ------ END ------ + +This directive was first introduced in [[#v0.11|version 0.11]]. + +== echo_flush == +'''syntax:''' ''echo_flush'' + +'''default:''' ''no'' + +'''context:''' ''location'' + +Forces the data potentially buffered by underlying Nginx output filters to send immediately to the client side via socket. + +Note that techically the command just emits a ngx_buf_t object with flush slot set to 1, so certain weird third-party output filter module could still block it before it reaches Nginx's (last) write filter. + +This directive does not take any argument. + +Consider the following example: + + + location /flush { + echo hello; + + echo_flush; + + echo_sleep 1; + echo world; + } + + +Then on the client side, using curl to access /flush, you'll see the "hello" line immediately, but only after 1 second, the last "world" line. Without calling echo_flush in the example above, you'll most likely see no output until 1 second is elapsed due to the internal buffering of Nginx. + +This directive will fail to flush the output buffer in case of subrequests get involved. Consider the following example: + + location /main { + echo_location_async /sub; + echo hello; + echo_flush; + } + location /sub { + echo_sleep 1; + } + +Then the client won't see "hello" appear even if echo_flush has been executed before the subrequest to /sub has actually started executing. The outputs of /main that are sent ''after'' [[#echo_location_async|echo_location_async]] will be postponed and buffered firmly. + +This does ''not'' apply to outputs sent before the subrequest initiated. For a modified version of the example given above: + + location /main { + echo hello; + echo_flush; + echo_location_async /sub; + } + location /sub { + echo_sleep 1; + } + +The client will immediately see "hello" before /sub enters sleeping. + +See also [[#echo|echo]], [[#echo_sleep|echo_sleep]], and [[#echo_location_async|echo_location_async]]. + +== echo_sleep == +'''syntax:''' ''echo_sleep '' + +'''default:''' ''no'' + +'''context:''' ''location'' + +Sleeps for the time period specified by the argument, which is in seconds. + +This operation is non-blocking on server side, so unlike the [[#echo_blocking_sleep|echo_blocking_sleep]] directive, it won't block the whole Nginx worker process. + +The period might takes three digits after the decimal point and must be greater than 0.001. + +An example is + + + location /echo_after_sleep { + echo_sleep 1.234; + echo resumed!; + } + + +Behind the scene, it sets up a per-request "sleep" ngx_event_t object, and adds a timer using that custom event to the Nginx event model and just waits for a timeout on that event. Because the "sleep" event is per-request, this directive can work in parallel subrequests. + +== echo_blocking_sleep == +'''syntax:''' ''echo_blocking_sleep '' + +'''default:''' ''no'' + +'''context:''' ''location'' + +This is a blocking version of the [[#echo_sleep|echo_sleep]] directive. + +See the documentation of [[#echo_sleep|echo_sleep]] for more detail. + +Behind the curtain, it calls the ngx_msleep macro provided by the Nginx core which maps to usleep on POSIX-compliant systems. + +Note that this directive will block the current Nginx worker process completely while being executed, so never use it in production environment. + +== echo_reset_timer == +'''syntax:''' ''echo_reset_timer'' + +'''default:''' ''no'' + +'''context:''' ''location'' + +Reset the timer begin time to ''now'', i.e., the time when this command is executed during request. + +The timer begin time is default to the starting time of the current request and can be overridden by this directive, potentially multiple times in a single location. For example: + + + location /timed_sleep { + echo_sleep 0.03; + echo "$echo_timer_elapsed sec elapsed."; + + echo_reset_timer; + + echo_sleep 0.02; + echo "$echo_timer_elapsed sec elapsed."; + } + + +The output on the client side might be + + + $ curl 'http://localhost/timed_sleep' + 0.032 sec elapsed. + 0.020 sec elapsed. + + +The actual figures you get on your side may vary a bit due to your system's current activities. + +Invocation of this directive will force the underlying Nginx timer to get updated to the current system time (regardless the timer resolution specified elsewhere in the config file). Furthermore, references of the [[#$echo_timer_elapsed|$echo_timer_elapsed]] variable will also trigger timer update forcibly. + +See also [[#echo_sleep|echo_sleep]] and [[#$echo_timer_elapsed|$echo_timer_elapsed]]. + +== echo_read_request_body == + +Explicitly reads request body so that the [[NginxHttpCoreModule#$request_body|$request_body]] variable will always have non-empty values (unless the body is so big that it has been saved by Nginx to a local temporary file). + +Note that this might not be the original client request body because the current request might be a subrequest with a "artificial" body specified by its parent. + +This directive does not generate any output itself, just like [[#echo_sleep|echo_sleep]]. + +Here's an example for echo'ing back the original HTTP client request (both headers and body are included): + + + location /echoback { + echo_duplicate 1 $echo_client_request_headers; + echo "\r"; + echo_read_request_body; + echo $request_body; + } + + +The content of /echoback looks like this on my side (I was using Perl's LWP utility to access this location on the server): + + + $ (echo hello; echo world) | lwp-request -m POST 'http://localhost/echoback' + POST /echoback HTTP/1.1 + TE: deflate,gzip;q=0.3 + Connection: TE, close + Host: localhost + User-Agent: lwp-request/5.818 libwww-perl/5.820 + Content-Length: 12 + Content-Type: application/x-www-form-urlencoded + + hello + world + + +Because /echoback is the main request, [[NginxHttpCoreModule#$request_body|$request_body]] holds the original client request body. + +Before Nginx 0.7.56, it makes no sense to use this directive because [[NginxHttpCoreModule#$request_body|$request_body]] was first introduced in Nginx 0.7.58. + +This directive itself was first introduced in the echo module's [[#v0.14|v0.14 release]]. + +== echo_location_async == +'''syntax:''' ''echo_location_async []'' + +'''default:''' ''no'' + +'''context:''' ''location'' + +Issue GET subrequest to the location specified (first argument) with optional url arguments specified in the second argument. + +As of Nginx 0.8.20, the location argument does ''not'' support named location, due to a limitation in the ngx_http_subrequest function. The same is true for its brother, the [[#echo_location|echo_location]] directive. + +A very simple example is + + + location /main { + echo_location_async /sub; + echo world; + } + location /sub { + echo hello; + } + + +Accessing /main gets + + hello + world + +Calling multiple locations in parallel is also possible: + + + location /main { + echo_reset_timer; + echo_location_async /sub1; + echo_location_async /sub2; + echo "took $echo_timer_elapsed sec for total."; + } + location /sub1 { + echo_sleep 2; # sleeps 2 sec + echo hello; + } + location /sub2 { + echo_sleep 1; # sleeps 1 sec + echo world; + } + + +Accessing /main yields + + + $ time curl 'http://localhost/main' + hello + world + took 0.000 sec for total. + + real 0m2.006s + user 0m0.000s + sys 0m0.004s + + +You can see that the main handler /main does ''not'' wait the subrequests /sub1 and /sub2 to complete and quickly goes on, hence the "0.000 sec" timing result. The whole request, however takes approximately 2 sec in total to complete because /sub1 and /sub2 run in parallel (or "concurrently" to be more accurate). + +If you use [[#echo_blocking_sleep|echo_blocking_sleep]] in the previous example instead, then you'll get the same output, but with 3 sec total response time, because "blocking sleep" blocks the whole Nginx worker process. + +Locations can also take an optional querystring argument, for instance + + + location /main { + echo_location_async /sub 'foo=Foo&bar=Bar'; + } + location /sub { + echo $arg_foo $arg_bar; + } + + +Accessing /main yields + + + $ curl 'http://localhost/main' + Foo Bar + + +Querystrings is ''not'' allowed to be concatenated onto the location argument with "?" directly, for example, /sub?foo=Foo&bar=Bar is an invalid location, and shouldn't be fed as the first argument to this directive. + +Due to an unknown bug in Nginx (it still exists in Nginx 0.8.20), the [[NginxHttpSsiModule|standard SSI module]] is required to ensure that the contents of the subrequests issued by this directive are correctly merged into the output chains of the main one. Fortunately, the SSI module is enabled by default during Nginx's configure process. + +If calling this directive without SSI module enabled, you'll get truncated response without contents of any subrequests and get an alert message in your Nginx's error.log, like this: + + [alert] 24212#0: *1 the http output chain is empty, client: 127.0.0.1, ... + +Technically speaking, this directive is an example that Nginx content handler issues one or more subrequests directly. AFAIK, the [https://connectical.com/projects/ngx-fancyindex/wiki fancyindex module] also does such kind of things ;) + +Nginx named locations like @foo is ''not'' supported here. + +This directive is logically equivalent to the GET version of [[#echo_subrequest_async|echo_subrequest_async]]. For example, + + echo_location_async /foo 'bar=Bar'; + +is logically equivalent to + + echo_subrequest_async GET /foo -q 'bar=Bar'; + +But calling this directive is slightly faster than calling [[#echo_subrequest_async|echo_subrequest_async]] using GET because we don't have to parse the HTTP method names like GET and options like -q. + +This directive is first introduced in [[#v0.09|version 0.09]] of this module and requires at least Nginx 0.7.46. + +== echo_location == +'''syntax:''' ''echo_location []'' + +'''default:''' ''no'' + +'''context:''' ''location'' + +Just like the [[#echo_location_async|echo_location_async]] directive, but echo_location issues subrequests ''in series'' rather than in parallel. That is, the content handler directives following this directive won't be executed until the subrequest issued by this directive completes. + +The final response body is almost always equivalent to the case when [[#echo_location_async|echo_location_async]] is used instead, only if timing variables is used in the outputs. + +Consider the following example: + + location /main { + echo_reset_timer; + echo_location /sub1; + echo_location /sub2; + echo "took $echo_timer_elapsed sec for total."; + } + location /sub1 { + echo_sleep 2; + echo hello; + } + location /sub2 { + echo_sleep 1; + echo world; + } + +The location /main above will take for total 3 sec to complete (compared to 2 sec if [[#echo_location_async|echo_location_async]] is used instead here). Here's the result in action on my machine: + + $ curl 'http://localhost/main' + hello + world + took 3.003 sec for total. + + real 0m3.027s + user 0m0.020s + sys 0m0.004s + +This directive is logically equivalent to the GET version of [[#echo_subrequest|echo_subrequest]]. For example, + + echo_location /foo 'bar=Bar'; + +is logically equivalent to + + echo_subrequest GET /foo -q 'bar=Bar'; + +But calling this directive is slightly faster than calling [[#echo_subrequest|echo_subrequest]] using GET because we don't have to parse the HTTP method names like GET and options like -q. + +Behind the scene, it creates an ngx_http_post_subrequest_t object as a ''continuation'' and passes it into the ngx_http_subrequest function call. Nginx will later reopen this "continuation" in the subrequest's ngx_http_finalize_request function call. We resumes the execution of the parent-request's content handler and starts to run the next directive (command) if any. + +Nginx named locations like @foo is ''not'' supported here. + +This directive was first introduced in the [[#v0.12|release v0.12]]. + +See also [[#echo_location_async|echo_location_async]] for more details about the meaning of the arguments. + +== echo_subrequest_async == +'''syntax:''' ''echo_subrequest_async [-q ] [-b ]'' + +'''default:''' ''no'' + +'''context:''' ''location'' + +Initiate an asynchronous subrequest using HTTP method, an optional url arguments (or querystring), and an option request body. + +This directive is very much like a generalized version of the [[#echo_location_async|echo_location_async]] directive. + +Here's a small example demonstrating its usage: + + + location /multi { + echo_subrequest_async POST '/sub' -q 'foo=Foo' -b 'hi'; + echo_subrequest_async PUT '/sub' -q 'bar=Bar' -b 'hello'; + } + location /sub { + echo "querystring: $query_string"; + echo "method: $echo_request_method"; + echo "body: $echo_request_body"; + echo "content length: $http_content_length"; + echo '///'; + } + + +Then on the client side: + + + $ curl 'http://localhost/multi' + querystring: foo=Foo + method: POST + body: hi + content length: 2 + /// + querystring: bar=Bar + method: PUT + body: hello + content length: 5 + /// + + +Here's more funny example using the standard [[#NginxHttpProxyModule|proxy module]] to handle the subrequest: + + + location /main { + echo_subrequest_async POST /sub -b 'hello, world'; + } + location /sub { + proxy_pass $scheme://127.0.0.1:$server_port/proxied; + } + location /proxied { + echo "method: $echo_request_method."; + + # we need to read body explicitly here...or $echo_request_body + # will evaluate to empty ("") + echo_read_request_body; + + echo "body: $echo_request_body."; + } + + +Then on the client side, we can see that + + + $ curl 'http://localhost/main' + method: POST. + body: hello, world. + + +Nginx named locations like @foo is ''not'' supported here. + +This directive was first introduced in the [[#v0.15|release v0.15]]. + +See also the [[#echo_subrequest|echo_subrequest]] and [[#echo_location_async|echo_location_async]] directives. + +== echo_subrequest == +'''syntax:''' ''echo_subrequest_async [-q ] [-b ]'' + +'''default:''' ''no'' + +'''context:''' ''location'' + +This is the synchronous version of the [[#echo_subrequest_async|echo_subrequest_async]] directive. And just like [[#echo_location|echo_location]], it does not block the Nginx worker process (while [[#echo_blocking_sleep|echo_blocking_sleep]] does), rather, it uses continuation to pass control along the subrequest chain. + +See [[#echo_subrequest_async|echo_subrequest_async]] for more details. + +Nginx named locations like @foo is ''not'' supported here. + +This directive was first introduced in the [[#v0.15|release v0.15]]. + +== echo_foreach_split == +'''syntax:''' ''echo_foreach_split '' + +'''default:''' ''no'' + +'''context:''' ''location'' + +Split the second argument string using the delimiter specified in the first argument, and then iterate through the resulting items. For instance: + + + location /loop { + echo_foreach_split ',' $arg_list; + echo "item: $echo_it"; + echo_end; + } + + +Accessing /main yields + + + $ curl 'http://localhost/loop?list=cat,dog,mouse' + item: cat + item: dog + item: mouse + + +As seen in the previous example, this directive should always be accompanied by an [[#echo_end|echo_end]] directive. + +Parallel echo_foreach_split loops are allowed, but nested ones are currently forbidden. + +The delimiter argument could contain ''multiple'' arbitrary characters, like + + + echo_foreach_split '-a-' 'cat-a-dog-a-mouse'; + echo $echo_it; + echo_end; + + +Logically speaking, this looping structure is just the foreach loop combined with a split function call in Perl (using the previous example): + + + foreach (split ',', $arg_list) { + print "item $_\n"; + } + + +People will also find it useful in merging multiple .js or .css resources into a whole. Here's an example: + + + location /merge { + default_type 'text/javascript'; + + echo_foreach_split '&' $query_string; + echo "/* JS File $echo_it */"; + echo_location_async $echo_it; + echo; + echo_end; + } + + +Then accessing /merge to merge the .js resources specified in the query string: + + + $ curl 'http://localhost/merge?/foo/bar.js&/yui/blah.js&/baz.js' + + +One can also use third-party Nginx cache module to cache the merged response generated by the /merge location in the previous example. + +This directive was first introduced in the [[#v0.17|release v0.17]]. + +== echo_end == +'''syntax:''' ''echo_end'' + +'''default:''' ''no'' + +'''context:''' ''location'' + +This directive is used to terminate the body of looping and conditional control structures like [[#echo_foreach_split|echo_foreach_split]]. + +This directive was first introduced in the [[#v0.17|release v0.17]]. + +== echo_request_body == +'''syntax:''' ''echo_request_body'' + +'''default:''' ''no'' + +'''context:''' ''location'' + +Outputs the contents of the request body previous read. + +Behind the scene, it's implemented roughly like this: + + + if (r->request_body && r->request_body->bufs) { + return ngx_http_output_filter(r, r->request_body->bufs); + } + + +Unlike the [[#$echo_request_body|$echo_request_body]] and $request_body variables, this directive will show the whole request body even if some parts or all parts of it are saved in temporary files on the disk. + +It is a "no-op" if no request body has been read yet. + +This directive was first introduced in the [[#v0.18|release v0.18]]. + +See also [[#echo_read_request_body|echo_read_request_body]] and the [[NginxHttpChunkinModule|chunkin module]]. + +== echo_exec == +'''syntax:''' ''echo_exec []'' + +'''syntax:''' ''echo_exec '' + +'''default:''' ''no'' + +'''context:''' ''location'' + +Does an internal redirect to the location specified. An optional query string can be specified for normal locations, as in + + + location /foo { + echo_exec /bar weight=5; + } + location /bar { + echo $arg_weight; + } + + +Or equivalently + + + location /foo { + echo_exec /bar?weight=5; + } + location /bar { + echo $arg_weight; + } + + +Named locations are also supported. Here's an example: + + + location /foo { + echo_exec @bar; + } + location @bar { + # you'll get /foo rather than @bar + # due to a potential bug in nginx. + echo $echo_request_uri; + } + + +But query string (if any) will always be ignored for named location redirects due to a limitation in the ngx_http_named_location function. + +Never try to echo things before the echo_exec directive or you won't see the proper response of the location you want to redirect to. Because any echoing will cause the original location handler to send HTTP headers before the redirection happens. + +Technically speaking, this directive exposes the Nginx internal API functions ngx_http_internal_redirect and ngx_http_named_location. + +This directive was first introduced in the [[#v0.21|v0.21 release]]. + += Filter Directives = + +Use of the following directives trigger the filter registration of this module. By default, no filter will be registered by this module. + +Every filter directive supports variable interpolation in its arguments (if any). + +== echo_before_body == +'''syntax:''' ''echo_before_body [options] [argument]...'' + +'''default:''' ''no'' + +'''context:''' ''location'' + +It's the filter version of the [[#echo|echo]] directive, and prepends its output to the beginning of the original outputs generated by the underlying content handler. + +An example is + + + location /echo { + echo_before_body hello; + proxy_pass $scheme://127.0.0.1:$server_port$request_uri/more; + } + location /echo/more { + echo world + } + + +Accessing /echo from the client side yields + + hello + world + +In the previous sample, we borrow the [[NginxHttpProxyModule|standard proxy module]] to serve as the underlying content handler that generates the "main contents". + +Multiple instances of this filter directive are also allowed, as in: + + + location /echo { + echo_before_body hello; + echo_before_body world; + echo !; + } + + +On the client side, the output is like + + + $ curl 'http://localhost/echo' + hello + world + ! + + +In this example, we also use the [[#Content Handler Directives|content handler directives]] provided by this module as the underlying content handler. + +This directive also supports the -n and -- options like the [[#echo|echo]] directive. + +This directive can be mixed with its brother directive [[#echo_after_body|echo_after_body]]. + +== echo_after_body == +'''syntax:''' ''echo_after_body [argument]...'' + +'''default:''' ''no'' + +'''context:''' ''location'' + +'''WARNING''' this directive does not work for nginx >= 0.7.65. + +It's very much like the [[#echo_before_body|echo_before_body]] directive, but ''appends'' its output to the end of the original outputs generated by the underlying content handler. + +Here's a simple example: + + + location /echo { + echo_after_body hello; + proxy_pass http://127.0.0.1:$server_port$request_uri/more; + } + location /echo/more { + echo world + } + + +Accessing /echo from the client side yields + + world + hello + +Multiple instances are allowed, as in: + + + location /echo { + echo_after_body hello; + echo_after_body world; + echo i; + echo say; + } + + +The output on the client side while accessing the /echo location looks like + + + i + say + hello + world + + +This directive also supports the -n and -- options like the [[#echo|echo]] directive. + +When this directive is used in a location accessed by a subrequest, it replies on the sync flag set in a chain buffer to indicate the end of the output for nginx >= 0.8.7. This is a hack because Nginx does not provide a reliable way to determine the end of the output chain in a subrequest's output filter. Use it in subrequests with care. + +This directive can be mixed with its brother directive [[#echo_before_body|echo_before_body]]. + += Variables = + +== $echo_it == + +This is a "topic variable" used by [[#echo_foreach_split|echo_foreach_split]], just like the $_ variable in Perl. + +== $echo_timer_elapsed == + +This variable holds the seconds elapsed since the start of the current request (might be a subrequest though) or the last invocation of the [[#echo_reset_timer|echo_reset_timer]] command. + +The timing result takes three digits after the decimal point. + +References of this variable will force the underlying Nginx timer to update to the current system time, regardless the timer resolution settings elsewhere in the config file, just like the [[#echo_reset_timer|echo_reset_timer]] directive. + +== $echo_request_body == + +Evaluates to the current (sub)request's request body previously read if no part of the body has been saved to a temporary file. To always show the request body even if it's very large, use the [[#echo_request_body|echo_request_body]] directive. + +== $echo_request_method == + +Evaluates to the HTTP request method of the current request (it can be a subrequest). + +Behind the scene, it just takes the string data stored in r->method_name. + +Compare it to the [[#$echo_client_request_method|$echo_client_request_method]] variable. + +At least for Nginx 0.8.20 and older, the [[NginxHttpCoreModule#$request_method|$request_method]] variable provided by the [[NginxHttpCoreModule|http core module]] is actually doing what our [[#$echo_client_request_method|$echo_client_request_method]] is doing. + +This variable was first introduced in our [[#v0.15|v0.15 release]]. + +== $echo_client_request_method == + +Always evaluates to the main request's HTTP method even if the current request is a subrequest. + +Behind the scene, it just takes the string data stored in r->main->method_name. + +Compare it to the [[#$echo_request_method|$echo_request_method]] variable. + +This variable was first introduced in our [[#v0.15|v0.15 release]]. + +== $echo_client_request_headers == + +Evaluates to the original client request's headers. + +Just as the name suggests, it will always take the main request (or the client request) even if it's currently executed in a subrequest. + +A simple example is below: + + + location /echoback { + echo "headers are:" + echo $echo_client_request_headers; + } + + +Accessing /echoback yields + + + $ curl 'http://localhost/echoback' + headers are + GET /echoback HTTP/1.1 + User-Agent: curl/7.18.2 (i486-pc-linux-gnu) libcurl/7.18.2 OpenSSL/0.9.8g + Host: localhost:1984 + Accept: */* + + +Behind the scene, it recovers r->main->header_in on the C level and does not construct the headers itself by traversing parsed results in the request object, and strips the last (trailing) CRLF. + +This variable was first introduced in [[#v0.15|version 0.15]]. + +== $echo_cacheable_request_uri == + +Evaluates to the parsed form of the URI (usually led by /) of the current (sub-)request. Unlike the [[#$echo_request_uri|$echo_request_uri]] variable, it is cacheable. + +See [[#$echo_request_uri|$echo_request_uri]] for more details. + +This variable was first introduced in [[#v0.17|version 0.17]]. + +== $echo_request_uri == + +Evaluates to the parsed form of the URI (usually led by /) of the current (sub-)request. Unlike the [[#$echo_cacheable_request_uri|$echo_cacheable_request_uri]] variable, it is ''not'' cacheable. + +This is quite different from the [[NginxHttpCoreModule#$request_uri|$request_uri]] variable exported by the [[NginxHttpCoreModule]], because $request_uri is the ''unparsed'' form of the current request's URI. + +This variable was first introduced in [[#v0.17|version 0.17]]. + +== $echo_incr == + +It is a counter that always generate the current counting number, starting from 1. The counter is always associated with the main request even if it is accessed within a subrequest. + +Consider the following example + + + location /main { + echo "main pre: $echo_incr"; + echo_location_async /sub; + echo_location_async /sub; + echo "main post: $echo_incr"; + } + location /sub { + echo "sub: $echo_incr"; + } + + +Accessing /main yields + + main pre: 1 + sub: 3 + sub: 4 + main post: 2 + +This directive was first introduced in the [[#v0.18|v0.18 release]]. + +== $echo_response_status == + +Evaluates to the status code of the current (sub)request, null if not any. + +Behind the scene, it's just the textual representation of r->headers_out->status. + +This directive was first introduced in the [[#v0.23|v0.23 release]]. + += Installation = + +Grab the nginx source code from [http://nginx.net/ nginx.net], for example, +the version 0.8.41 (see [[#Compatibility|nginx compatibility]]), and then build the source with this module: + + + $ wget 'http://sysoev.ru/nginx/nginx-0.8.41.tar.gz' + $ tar -xzvf nginx-0.8.41.tar.gz + $ cd nginx-0.8.41/ + + # Here we assume you would install you nginx under /opt/nginx/. + $ ./configure --prefix=/opt/nginx \ + --add-module=/path/to/echo-nginx-module + + $ make -j2 + $ make install + + +Download the latest version of the release tarball of this module from [http://github.com/agentzh/echo-nginx-module/downloads echo-nginx-module file list]. + += Compatibility = + +The following versions of Nginx should work with this module: + +* '''0.8.x''' (last tested version is 0.8.40) +* '''0.7.x >= 0.7.21''' (last tested version is 0.7.66) + +In particular, + +* the directive [[#echo_location_async|echo_location_async]] and its brother [[#echo_subrequest_async|echo_subrequest_async]] do ''not'' work with '''0.7.x < 0.7.46'''. +* the [[#echo_after_body|echo_after_body]] directive does ''not'' work at all with nginx '''< 0.8.7'''. +* the [[#echo_sleep|echo_sleep]] directive cannot be used after [[#echo_location|echo_location]] or [[#echo_subrequest|echo_subrequest]] for nginx '''< 0.8.11'''. + +Earlier versions of Nginx like 0.6.x and 0.5.x will ''not'' work at all. + +If you find that any particular version of Nginx above 0.7.21 does not work with this module, please consider [[#Report Bugs|reporting a bug]]. + += Modules that use this module for testing = + +The following modules take advantage of this echo module in their test suite: + +* The [[NginxHttpMemcModule|memc]] module that supports almost the whole memcached TCP protocol. +* The [[NginxHttpChunkinModule|chunkin]] module that adds HTTP 1.1 chunked input support to Nginx. +* The [[NginxHttpHeadersMoreModule|headers_more]] module that allows you to add, set, and clear input and output headers under the conditions that you specify. +* The echo module itself. + +Please mail me other modules that use echo in any form and I'll add them to the list above :) + += Report Bugs = + +Although a lot of effort has been put into testing and code tuning, there must be some serious bugs lurking somewhere in this module. So whenever you are bitten by any quirks, please don't hesitate to + +# send a bug report or even patches to , +# or create a ticket on the [http://github.com/agentzh/echo-nginx-module/issues issue tracking interface] provided by GitHub. + += Source Repository = + +Available on github at [http://github.com/agentzh/echo-nginx-module agentzh/echo-nginx-module]. + += ChangeLog = + +== v0.34 == +* we no longer use the problematic ngx_strXcmp macros in our source because it may cause invalid reads and thus segmentation faults. thanks Piotr Sikora. + +== v0.33 == +* fixed compatibility with nginx 0.7.66+ because the ngx_time_update macro's parameter list has changed. Thanks Guang Feng (蔡镜明). + +== v0.32 == +* we should have used ngx_calloc_buf instead of ngx_alloc_buf for the last chunk generated for [[#echo_after_body|echo_after_body]]. thanks valgrind's memcheck tool. +* we should initialize flags before feeding it into ngx_http_parse_unsafe_uri. thanks valgrind's memcheck tool. +* fixed a minor issue in the [[#echo_location|echo_location]]/[[#echo_subrequest|echo_subrequest]] implementation, which used to have race conditions. + +== v0.31 == + +* the echo wev handler should not proceed if it is still waiting for some sequential subrequest or has just processed one to avoid bouncing issues. +* fixed a segfault for echo_exec for 0.7.x: we should check r->done before proceeding. +* no longer explicitly set r->write_event_handler to ngx_http_request_empty_handler because it's totally wrong for the state machine. +* fixed the sequential subrequest model bugs: we should ensure the pr->write_event_handler gets called immediately after the post_subrequest callback when the subrequest finalizes. + +== v0.30 == + +* fixed the [[#echo_exec|echo_exec]] directive for nginx >= 0.8.11. we didn't get the r->main->count right in the previous version. + +== v0.29 == + +* refactored the core of this module. now the implementation of [[#echo_location|echo_location]], [[#echo_subrequest|echo_subrequest]], [[#echo_sleep|echo_sleep]], and [[#echo_read_request_body|echo_read_request_body]] finally fit well with the nginx event model and Igor Sysoev's way of thinking. + +== v0.28 == + +* added support for the -n and -- options to the [[#echo|echo]], [[#echo_before_body|echo_before_body]], and [[#echo_after_body|echo_after_body]] directives. + +== v0.27 == + +* applied the patch from Sergey A. Osokin to work with nginx 0.8.35. + +== v0.26 == + +* bug fix: we should bypass upstream filters in our echo filters. an output filter should ever call ngx_http_output_filter nor ngx_http_send_special. + +== v0.25 == + +* now we register a request cleanup handler to ensure our sleep event's timer will always get properly deleted even if the request is quit prematurely. this affects the echo_sleep directive. +* use ngx_null_string whenever possible in the source. +* sync'd the bundled test scaffold to Test::Nginx 0.07. + +== v0.24 == + +* various source file name and coding style fixes. (the code now looks more like Igor Sysoev's.) + +== v0.23 == + +* now the subrequest can read the client request body directly (for the main request) because we made subrequests inherit its parent's r->header_in as well. This affects the [[#echo_read_request_body|echo_read_request_body]] directive. +* fixed [[#echo_after_body|echo_after_body]] in subrequests by using a hack (checking cl->buf->sync for the last buf) for nginx 0.8.7+ only. +* added new varaible [[#$echo_response_status|$echo_response_status]] to help testing the status code of a subrequest. (The [[NginxHttpMemcModule|memc]] module makes use of it.) +* use the ngx_calloc_buf macro to allocate new bufs in the code rather than explicit ngx_pcalloc calls for safety. + +== v0.22 == + +* Now we allowed all the directives appear in the [[NginxHttpRewriteModule|rewrite module]]'s [[NginxHttpRewriteModule#if|if]] block. But so far I've only tested the [[#echo|echo]] directive. + +== v0.21 == + +* Added a new directive named [[#echo_exec|echo_exec]] which does internal redirect to other (named) locations. + +== v0.20 == + +* Fixed a bug in [[#echo_sleep|echo_sleep]]'s r->main->count handling for nginx 0.8.x. This bug will cause the server to hang when proxing a location with [[#echo_sleep|echo_sleep]]. +* Applied the ngx_str3cmp, ngx_str4cmp, and ngx_str6cmp optimizing macros to the parse_method_name function, as suggested by Marcus Clyne. +* Added [[#TODO|TODO items]] regarding $echo_random and echo_repeat suggested by Marcus Clyne. + +== v0.19 == +* Fixed the CPS-style chained subrequest model for the [[#echo_location|echo_location]] and [[#echo_subrequest|echo_subrequest]] directives. they are now working perfectly and will not hang the server with the recent nginx 0.8.21 ~ 0.8.27 releases. To be specifically, the chained subrequest should call ngx_http_finalize_request on its parent request if the content handler of the parent request does not return NGX_DONE. +* Undeprecated the [[#echo_location|echo_location]] and [[#echo_subrequest|echo_subrequest]] directives. + +== v0.18 == +* Fixed the "zero size buf in output" alerts in error.log. +* Added the new directive [[#echo_request_body|echo_request_body]]. +* Now we use the ngx_http_parse_unsafe_uri function to check the locations to [[#echo_location_async|echo_location_async]] and its friends. Thanks Arvind Jayaprakash for suggesting this fix. +* Deprecated the [[#echo_location|echo_location]] and [[#echo_subrequest|echo_subrequest]] directives. +* For HTTP 1.0 clients, use the buf length of the first chain link as the output header Content-Length. +* Implemented new variable [[#$echo_incr|$echo_incr]]. + +== v0.17 == +* Added new directives [[#echo_foreach_split|echo_foreach_split]] and [[#echo_end|echo_end]]. Also introduced a "topic variable" named [[#$echo_it|$echo_it]]. +* Added new variables [[#$echo_request_uri|$echo_request_uri]] and [[#$echo_cacheable_request_uri|$echo_cacheable_request_uri]]. + +== v0.16 == +* Now the subrequests issued by the [[#echo_location|echo_location_async]] and [[#echo_location|echo_location]] directives no longer inherit cached variable values from its parent request. (The underlying ngx_http_subrequest function, however, does automatic cachable variable value inheritance.) +* Added an undocumented variable ''echo_cached_request_uri'' to help testing of this module. + +== v0.15 == + +* Added new directives [[#echo_subrequest|echo_subrequest]] and [[#echo_subrequest_async|echo_subrequest_async]] for the full nginx subrequest API. +* Removed the echo_client_request_headers directive, and provided the [[#$echo_client_request_headers|$echo_client_request_headers]] variable instead. +* Added new variables [[#$echo_request_method|$echo_request_method]] and [[#$echo_client_request_method|$echo_client_request_method]]. + +== v0.14 == + +* Added new directive [[#echo_read_request_body|echo_read_request_body]] to explicitly read client request body so that the [[NginxHttpCoreModule#$request_body]] variable will always have non-empty values. +* Now we shuffer test cases automatically in .t files and fixed bugs in the tests themselves which are hidden by config reload fallback in failure. + +== v0.13 == + +* Fixed the special cases when the outputs of a [[#echo_duplicate|echo_duplicate]] directive is empty. +* Now we explicitly clear content length and accept ranges headers in the content handler. + +== v0.12 == + +* Implemented the [[#echo_location|echo_location]] directive, which can issue chained GET subrequests in the Continuation Passing Style (CPS), rather than the parallel subrequest issued by the [[#echo_location_async|echo_location_async]] directive. + +== v0.11 == + +* Implemented the [[#echo_duplicate|echo_duplicate]] directive to help generating large chunk of data for testing. + +== v0.10 == + +* Fixed compilation regression against Nginx 0.7.21. This bug appears in version 0.09. +* Refactored the codebase by splitting source into various small files. + +== v0.09 == + +* Reimplement the [[#echo_sleep|echo_sleep]] directive using per-request event and timer; the old implementation uses the global connection's read/write event to register timer, so it will break horribly when multiple subrequests "sleep" at the same time. +* Added the [[#echo_location_async|echo_location_async]] directive which can issue a GET subrequest and insert its contents herein. + +== v0.08 == + +* [[#echo_sleep|echo_sleep]]: now we delete our write event timer in the post_sleep handle. +* Added doc/manpage.wiki which tracks changes in the [http://wiki.nginx.org/NginxHttpEchoModule wiki page]. +* Added the util/wiki2pod.pl script to convert doc/manpage.wiki to README. +* Disabled the DDEBUG macro in the C source by default. + += Test Suite = + +This module comes with a Perl-driven test suite. The [http://github.com/agentzh/echo-nginx-module/tree/master/test/t/ test cases] are +[http://github.com/agentzh/echo-nginx-module/blob/master/test/t/echo.t declarative] too. Thanks to the [http://search.cpan.org/perldoc?Test::Base Test::Base] module in the Perl world. + +To run it on your side: + + + $ cd test + $ PATH=/path/to/your/nginx-with-echo-module:$PATH prove -r t + + +You need to terminate any Nginx processes before running the test suite if you have changed the Nginx server binary. + +At the moment, [http://search.cpan.org/perldoc?LWP::UserAgent LWP::UserAgent] is used by the [http://github.com/agentzh/echo-nginx-module/blob/master/test/lib/Test/Nginx/Echo.pm test scaffold] for simplicity and it's rather weak in testing ''streaming'' behavior of Nginx (I'm using "curl" to test these aspects manually for now). I'm considering coding up my own Perl HTTP client library based on [http://search.cpan.org/perldoc?IO::Select IO::Select] and [http://search.cpan.org/perldoc?IO::Socket IO::Socket] (there might be already one around?). + +Because a single nginx server (by default, localhost:1984) is used across all the test scripts (.t files), it's meaningless to run the test suite in parallel by specifying -jN when invoking the prove utility. + +Some parts of the test suite requires standard modules [[NginxHttpProxyModule|proxy]], [[NginxHttpRewriteModule|rewrite]] and [[NginxHttpSsiModule|SSI]] to be enabled as well when building Nginx. + += TODO = + +* Fix the [[#echo_after_body|echo_after_body]] directive in subrequests. +* Add directives ''echo_read_client_request_body'' and ''echo_request_headers''. +* Add new directive ''echo_log'' to use Nginx's logging facility directly from the config file and specific loglevel can be specified, as in + + + echo_log debug "I am being called."; + + +* Add support for options -h and -t to [[#echo_subrequest_async|echo_subrequest_async]] and [[#echo_subrequest|echo_subrequest]]. For example + + + echo_subrequest POST /sub -q 'foo=Foo&bar=Bar' -b 'hello' -t 'text/plan' -h 'X-My-Header: blah blah' + + +* Add options to control whether a subrequest should inherit cached variables from its parent request (i.e. the current request that is calling the subrequest in question). Currently none of the subrequests issued by this module inherit the cached variables from the parent request. +* Add new variable ''$echo_active_subrequests'' to show r->main->count - 1. +* Add the ''echo_file'' and ''echo_cached_file'' directives. +* Add new varaible ''$echo_request_headers'' to accompany the existing [[#$echo_client_request_headers|$echo_client_request_headers]] variable. +* Add new directive ''echo_foreach'', as in + + + echo_foreach 'cat' 'dog' 'mouse'; + echo_location_async "/animals/$echo_it"; + echo_end; + + +* Add new directive ''echo_foreach_range'', as in + + + echo_foreach_range '[1..100]' '[a-zA-z0-9]'; + echo_location_async "/item/$echo_it"; + echo_end; + + +* Add new directive ''echo_repeat'', as in + + + echo_repeat 10 $i { + echo "Page $i"; + echo_location "/path/to/page/$i"; + } + + +This is just another way of saying + + + echo_foreach_range $i [1..10]; + echo "Page $i"; + echo_location "/path/to/page/$i"; + echo_end; + + +Thanks Marcus Clyne for providing this idea. + +* Add new variable $echo_random which always returns a random non-negative integer with the lower/upper limit specified by the new directives echo_random_min and echo_random_max. For example, + + + echo_random_min 10 + echo_random_max 200 + echo "random number: $echo_random"; + + +Thanks Marcus Clyne for providing this idea. + += Getting involved = + +You'll be very welcomed to submit patches to the [[#Author|author]] or just ask for a commit bit to the [[#Source Repository|source repository]] on GitHub. + + + += Author = + +agentzh (章亦春) '''' + +This wiki page is also maintained by the author himself, and everybody is encouraged to improve this page as well. + += Copyright & License = + +Copyright (c) 2009, Taobao Inc., Alibaba Group ( http://www.taobao.com ). + +Copyright (c) 2009, agentzh . + +This module is licensed under the terms of the BSD license. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +* 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. +* Neither the name of the Taobao Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT +HOLDER 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. + += See Also = + +* The original [http://agentzh.spaces.live.com/blog/cns!FF3A735632E41548!478.entry blog post] about this module's initial development. +* The standard [[NginxHttpAdditionModule|addition filter module]]. +* The standard [[NginxHttpProxyModule|proxy module]]. + diff -Nru nginx-0.5.33/modules/nginx-echo/LICENSE nginx-0.8.53/modules/nginx-echo/LICENSE --- nginx-0.5.33/modules/nginx-echo/LICENSE 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/modules/nginx-echo/LICENSE 2011-02-26 14:04:40.000000000 +0000 @@ -0,0 +1,30 @@ +Copyright (c) 2009, Taobao Inc., Alibaba Group ( http://www.taobao.com ). +Copyright (c) 2009, agentzh . +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * 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. + + * Neither the name of the Taobao Inc. nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT +HOLDER 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. diff -Nru nginx-0.5.33/modules/nginx-echo/README nginx-0.8.53/modules/nginx-echo/README --- nginx-0.5.33/modules/nginx-echo/README 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/modules/nginx-echo/README 2011-02-26 14:04:40.000000000 +0000 @@ -0,0 +1,1740 @@ +Name + ngx_echo - Brings "echo", "sleep", "time", "exec" and more shell-style + goodies to Nginx config file. + + *This module is not distributed with the Nginx source.* See the + installation instructions. + +Version + This document describes echo-nginx-module v0.34 + () released + on September 14, 2010. + +Synopsis + location /hello { + echo "hello, world!"; + } + + location /hello { + echo -n "hello, " + echo "world!"; + } + + location /timed_hello { + echo_reset_timer; + echo hello world; + echo "'hello world' takes about $echo_timer_elapsed sec."; + echo hiya igor; + echo "'hiya igor' takes about $echo_timer_elapsed sec."; + } + + location /echo_with_sleep { + echo hello; + echo_flush; # ensure the client can see previous output immediately + echo_sleep 2.5; # in sec + echo world; + } + + # in the following example, accessing /echo yields + # hello + # world + # blah + # hiya + # igor + location /echo { + echo_before_body hello; + echo_before_body world; + proxy_pass $scheme://127.0.0.1:$server_port$request_uri/more; + echo_after_body hiya; + echo_after_body igor; + } + location /echo/more { + echo blah; + } + + # the output of /main might be + # hello + # world + # took 0.000 sec for total. + # and the whole request would take about 2 sec to complete. + location /main { + echo_reset_timer; + + # subrequests in parallel + echo_location_async /sub1; + echo_location_async /sub2; + + echo "took $echo_timer_elapsed sec for total."; + } + location /sub1 { + echo_sleep 2; + echo hello; + } + location /sub2 { + echo_sleep 1; + echo world; + } + + # the output of /main might be + # hello + # world + # took 3.003 sec for total. + # and the whole request would take about 3 sec to complete. + location /main { + echo_reset_timer; + + # subrequests in series (chained by CPS) + echo_location /sub1; + echo_location /sub2; + + echo "took $echo_timer_elapsed sec for total."; + } + location /sub1 { + echo_sleep 2; + echo hello; + } + location /sub2 { + echo_sleep 1; + echo world; + } + + # Accessing /dup gives + # ------ END ------ + location /dup { + echo_duplicate 3 "--"; + echo_duplicate 1 " END "; + echo_duplicate 3 "--"; + echo; + } + + # /bighello will generate 1000,000,000 hello's. + location /bighello { + echo_duplicate 1000_000_000 'hello'; + } + + # echo back the client request + location /echoback { + echo_duplicate 1 $echo_client_request_headers; + echo "\r"; + + echo_read_request_body; + + echo_request_body; + } + + # GET /multi will yields + # querystring: foo=Foo + # method: POST + # body: hi + # content length: 2 + # /// + # querystring: bar=Bar + # method: PUT + # body: hello + # content length: 5 + # /// + location /multi { + echo_subrequest_async POST '/sub' -q 'foo=Foo' -b 'hi'; + echo_subrequest_async PUT '/sub' -q 'bar=Bar' -b 'hello'; + } + location /sub { + echo "querystring: $query_string"; + echo "method: $echo_request_method"; + echo "body: $echo_request_body"; + echo "content length: $http_content_length"; + echo '///'; + } + + # GET /merge?/foo.js&/bar/blah.js&/yui/baz.js will merge the .js resources together + location /merge { + default_type 'text/javascript'; + echo_foreach_split '&' $query_string; + echo "/* JS File $echo_it */"; + echo_location_async $echo_it; + echo; + echo_end; + } + + # accessing /if?val=abc yields the "hit" output + # while /if?val=bcd yields "miss": + location ^~ /if { + set $res miss; + if ($arg_val ~* '^a') { + set $res hit; + echo $res; + } + echo $res; + } + +Description + This module wraps lots of Nginx internal APIs for streaming input and + output, parallel/sequential subrequests, timers and sleeping, as well as + various meta data accessing. + + Basically it provides various utilities that help testing and debugging + of other modules by trivially emulating different kinds of faked + subrequest locations. + + People will also find it useful in real-world applications that need to + + 1. serve static contents directly from memory (loading from the Nginx + config file). + + 2. wrap the upstream response with custom header and footer (kinda like + the addition module but with contents read directly from the config + file and Nginx variables). + + 3. merge contents of various "Nginx locations" (i.e., subrequests) + together in a single main request (using echo_location and its + friends). + + This is a special dual-role module that can *lazily* serve as a content + handler or register itself as an output filter only upon demand. By + default, this module does not do anything at all. + + Use of any of this module's directives (no matter content handler + directives or filter directives) will force the chunked encoding to be + used for the HTTP response due to the streaming nature of this module + (unless HTTP 1.0 is enforced by the client and the Content-Length header + will be set to the size of the first handler directive that generates + contents). + + Technially, this module has also demonstrated the following techniques + that might be helpful for module writers: + + 1. Issue parallel subreqeusts directly from content handler. + + 2. Issue chained subrequests directly from content handler, by passing + continuation along the subrequest chain. + + 3. Issue subrequests with all HTTP 1.1 methods and even an optional + faked HTTP request body. + + 4. Interact with the Nginx event model directly from content handler + using custom events and timers, and resume the content handler back + if necessary. + + 5. Dual-role module that can (lazily) serve as a content handler or an + output filter or both. + + 6. Nginx config file variable creation and interpolation. + + 7. Streaming output control using output_chain, flush and its friends. + + 8. Read client request body from the content handler, and returns back + (asynchronously) to the content handler after completion. + + 9. Use Perl-based declarative test suite to drive the development of + Nginx C modules. + +Content Handler Directives + Use of the following directives register this module to the current + Nginx location as a content handler. If you want to use another module, + like the standard proxy module, as the content handler, use the filter + directives provided by this module. + + All the content handler directives can be mixed together in a single + Nginx location and they're supposed to run sequentially just as in the + Bash scripting language. + + Every content handler directive supports variable interpolation in its + arguments (if any). + + The MIME type set by the standard default_type directive is respected by + this module, as in: + + location /hello { + default_type text/plain; + echo hello; + } + + Then on the client side: + + $ curl -I 'http://localhost/echo' + HTTP/1.1 200 OK + Server: nginx/0.8.20 + Date: Sat, 17 Oct 2009 03:40:19 GMT + Content-Type: text/plain + Connection: keep-alive + + Since the v0.22 release, all of the directives are allowed in the + rewrite module's if directive block, for instance: + + location ^~ /if { + set $res miss; + if ($arg_val ~* '^a') { + set $res hit; + echo $res; + } + echo $res; + } + + echo + syntax: *echo [options] ...* + + default: *no* + + context: *location* + + Sends arguments joined by spaces, along with a trailing newline, out to + the client. + + Note that the data might be buffered by Nginx's underlying buffer. To + force the output data flushed immediately, use the echo_flush command + just after "echo", as in + + echo hello world; + echo_flush; + + When no argument is specified, *echo* emits the trailing newline alone, + just like the *echo* command in shell. + + Variables may appear in the arguments. An example is + + echo The current request uri is $request_uri; + + where $request_uri is a variable exposed by the [[NginxHttpCoreModule]]. + + This command can be used multiple times in a single location + configuration, as in + + location /echo { + echo hello; + echo world; + } + + The output on the client side looks like this + + $ curl 'http://localhost/echo' + hello + world + + Special characters like newlines ("\n") and tabs ("\t") can be escaped + using C-style escaping sequences. But a notable exception is the dollar + sign ("$"). As of Nginx 0.8.20, there's still no clean way to esacpe + this characters. (A work-around might be to use a $echo_dollor variable + that is always evaluated to the constant "$" character. This feature + will possibly be introduced in a future version of this module.) + + As of the echo v0.28 release, one can suppress the trailing newline + character in the output by using the "-n" option, as in + + location /echo { + echo -n "hello, "; + echo "world"; + } + + Accessing "/echo" gives + + $ curl 'http://localhost/echo' + hello, world + + Leading "-n" in variable values won't take effect and will be emitted + literally, as in + + location /echo { + set $opt -n; + echo $opt "hello,"; + echo "world"; + } + + This gives the following output + + $ curl 'http://localhost/echo' + -n hello, + world + + One can output leading "-n" literals and other options using the special + "--" option like this + + location /echo { + echo -- -n is an option; + } + + which yields + + $ curl 'http://localhost/echo' + -n is an option + + echo_duplicate + syntax: *echo_duplicate * + + default: *no* + + context: *location* + + Outputs duplication of a string indicated by the second argument, using + the times specified in the first argument. + + For instance, + + location /dup { + echo_duplicate 3 "abc"; + } + + will lead to an output of "abcabcabc". + + Underscores are allowed in the count number, just like in Perl. For + example, to emit 1000,000,000 instances of "hello, world": + + location /many_hellos { + echo_duplicate 1000_000_000 "hello, world"; + } + + The "count" argument could be zero, but not negative. The second + "string" argument could be an empty string ("") likewise. + + Unlike the echo directive, no trailing newline is appended to the + result. So it's possible to "abuse" this directive as a + no-trailing-newline version of echo by using "count" 1, as in + + location /echo_art { + echo_duplicate 2 '---'; + echo_duplicate 1 ' END '; # we don't want a trailing newline here + echo_duplicate 2 '---'; + echo; # we want a trailing newline here... + } + + You get + + ------ END ------ + + This directive was first introduced in version 0.11. + + echo_flush + syntax: *echo_flush* + + default: *no* + + context: *location* + + Forces the data potentially buffered by underlying Nginx output filters + to send immediately to the client side via socket. + + Note that techically the command just emits a ngx_buf_t object with + "flush" slot set to 1, so certain weird third-party output filter module + could still block it before it reaches Nginx's (last) write filter. + + This directive does not take any argument. + + Consider the following example: + + location /flush { + echo hello; + + echo_flush; + + echo_sleep 1; + echo world; + } + + Then on the client side, using curl to access "/flush", you'll see the + "hello" line immediately, but only after 1 second, the last "world" + line. Without calling "echo_flush" in the example above, you'll most + likely see no output until 1 second is elapsed due to the internal + buffering of Nginx. + + This directive will fail to flush the output buffer in case of + subrequests get involved. Consider the following example: + + location /main { + echo_location_async /sub; + echo hello; + echo_flush; + } + location /sub { + echo_sleep 1; + } + + Then the client won't see "hello" appear even if "echo_flush" has been + executed before the subrequest to "/sub" has actually started executing. + The outputs of "/main" that are sent *after* echo_location_async will be + postponed and buffered firmly. + + This does *not* apply to outputs sent before the subrequest initiated. + For a modified version of the example given above: + + location /main { + echo hello; + echo_flush; + echo_location_async /sub; + } + location /sub { + echo_sleep 1; + } + + The client will immediately see "hello" before "/sub" enters sleeping. + + See also echo, echo_sleep, and echo_location_async. + + echo_sleep + syntax: *echo_sleep * + + default: *no* + + context: *location* + + Sleeps for the time period specified by the argument, which is in + seconds. + + This operation is non-blocking on server side, so unlike the + echo_blocking_sleep directive, it won't block the whole Nginx worker + process. + + The period might takes three digits after the decimal point and must be + greater than 0.001. + + An example is + + location /echo_after_sleep { + echo_sleep 1.234; + echo resumed!; + } + + Behind the scene, it sets up a per-request "sleep" ngx_event_t object, + and adds a timer using that custom event to the Nginx event model and + just waits for a timeout on that event. Because the "sleep" event is + per-request, this directive can work in parallel subrequests. + + echo_blocking_sleep + syntax: *echo_blocking_sleep * + + default: *no* + + context: *location* + + This is a blocking version of the echo_sleep directive. + + See the documentation of echo_sleep for more detail. + + Behind the curtain, it calls the ngx_msleep macro provided by the Nginx + core which maps to usleep on POSIX-compliant systems. + + Note that this directive will block the current Nginx worker process + completely while being executed, so never use it in production + environment. + + echo_reset_timer + syntax: *echo_reset_timer* + + default: *no* + + context: *location* + + Reset the timer begin time to *now*, i.e., the time when this command is + executed during request. + + The timer begin time is default to the starting time of the current + request and can be overridden by this directive, potentially multiple + times in a single location. For example: + + location /timed_sleep { + echo_sleep 0.03; + echo "$echo_timer_elapsed sec elapsed."; + + echo_reset_timer; + + echo_sleep 0.02; + echo "$echo_timer_elapsed sec elapsed."; + } + + The output on the client side might be + + $ curl 'http://localhost/timed_sleep' + 0.032 sec elapsed. + 0.020 sec elapsed. + + The actual figures you get on your side may vary a bit due to your + system's current activities. + + Invocation of this directive will force the underlying Nginx timer to + get updated to the current system time (regardless the timer resolution + specified elsewhere in the config file). Furthermore, references of the + $echo_timer_elapsed variable will also trigger timer update forcibly. + + See also echo_sleep and $echo_timer_elapsed. + + echo_read_request_body + Explicitly reads request body so that the $request_body variable will + always have non-empty values (unless the body is so big that it has been + saved by Nginx to a local temporary file). + + Note that this might not be the original client request body because the + current request might be a subrequest with a "artificial" body specified + by its parent. + + This directive does not generate any output itself, just like + echo_sleep. + + Here's an example for echo'ing back the original HTTP client request + (both headers and body are included): + + location /echoback { + echo_duplicate 1 $echo_client_request_headers; + echo "\r"; + echo_read_request_body; + echo $request_body; + } + + The content of "/echoback" looks like this on my side (I was using + Perl's LWP utility to access this location on the server): + + $ (echo hello; echo world) | lwp-request -m POST 'http://localhost/echoback' + POST /echoback HTTP/1.1 + TE: deflate,gzip;q=0.3 + Connection: TE, close + Host: localhost + User-Agent: lwp-request/5.818 libwww-perl/5.820 + Content-Length: 12 + Content-Type: application/x-www-form-urlencoded + + hello + world + + Because "/echoback" is the main request, $request_body holds the + original client request body. + + Before Nginx 0.7.56, it makes no sense to use this directive because + $request_body was first introduced in Nginx 0.7.58. + + This directive itself was first introduced in the echo module's v0.14 + release. + + echo_location_async + syntax: *echo_location_async []* + + default: *no* + + context: *location* + + Issue GET subrequest to the location specified (first argument) with + optional url arguments specified in the second argument. + + As of Nginx 0.8.20, the "location" argument does *not* support named + location, due to a limitation in the "ngx_http_subrequest" function. The + same is true for its brother, the echo_location directive. + + A very simple example is + + location /main { + echo_location_async /sub; + echo world; + } + location /sub { + echo hello; + } + + Accessing "/main" gets + + hello + world + + Calling multiple locations in parallel is also possible: + + location /main { + echo_reset_timer; + echo_location_async /sub1; + echo_location_async /sub2; + echo "took $echo_timer_elapsed sec for total."; + } + location /sub1 { + echo_sleep 2; # sleeps 2 sec + echo hello; + } + location /sub2 { + echo_sleep 1; # sleeps 1 sec + echo world; + } + + Accessing "/main" yields + + $ time curl 'http://localhost/main' + hello + world + took 0.000 sec for total. + + real 0m2.006s + user 0m0.000s + sys 0m0.004s + + You can see that the main handler "/main" does *not* wait the + subrequests "/sub1" and "/sub2" to complete and quickly goes on, hence + the "0.000 sec" timing result. The whole request, however takes + approximately 2 sec in total to complete because "/sub1" and "/sub2" run + in parallel (or "concurrently" to be more accurate). + + If you use echo_blocking_sleep in the previous example instead, then + you'll get the same output, but with 3 sec total response time, because + "blocking sleep" blocks the whole Nginx worker process. + + Locations can also take an optional querystring argument, for instance + + location /main { + echo_location_async /sub 'foo=Foo&bar=Bar'; + } + location /sub { + echo $arg_foo $arg_bar; + } + + Accessing "/main" yields + + $ curl 'http://localhost/main' + Foo Bar + + Querystrings is *not* allowed to be concatenated onto the "location" + argument with "?" directly, for example, "/sub?foo=Foo&bar=Bar" is an + invalid location, and shouldn't be fed as the first argument to this + directive. + + Due to an unknown bug in Nginx (it still exists in Nginx 0.8.20), the + standard SSI module is required to ensure that the contents of the + subrequests issued by this directive are correctly merged into the + output chains of the main one. Fortunately, the SSI module is enabled by + default during Nginx's "configure" process. + + If calling this directive without SSI module enabled, you'll get + truncated response without contents of any subrequests and get an alert + message in your Nginx's "error.log", like this: + + [alert] 24212#0: *1 the http output chain is empty, client: 127.0.0.1, ... + + Technically speaking, this directive is an example that Nginx content + handler issues one or more subrequests directly. AFAIK, the fancyindex + module () also + does such kind of things ;) + + Nginx named locations like @foo is *not* supported here. + + This directive is logically equivalent to the GET version of + echo_subrequest_async. For example, + + echo_location_async /foo 'bar=Bar'; + + is logically equivalent to + + echo_subrequest_async GET /foo -q 'bar=Bar'; + + But calling this directive is slightly faster than calling + echo_subrequest_async using "GET" because we don't have to parse the + HTTP method names like "GET" and options like "-q". + + This directive is first introduced in version 0.09 of this module and + requires at least Nginx 0.7.46. + + echo_location + syntax: *echo_location []* + + default: *no* + + context: *location* + + Just like the echo_location_async directive, but "echo_location" issues + subrequests *in series* rather than in parallel. That is, the content + handler directives following this directive won't be executed until the + subrequest issued by this directive completes. + + The final response body is almost always equivalent to the case when + echo_location_async is used instead, only if timing variables is used in + the outputs. + + Consider the following example: + + location /main { + echo_reset_timer; + echo_location /sub1; + echo_location /sub2; + echo "took $echo_timer_elapsed sec for total."; + } + location /sub1 { + echo_sleep 2; + echo hello; + } + location /sub2 { + echo_sleep 1; + echo world; + } + + The location "/main" above will take for total 3 sec to complete + (compared to 2 sec if echo_location_async is used instead here). Here's + the result in action on my machine: + + $ curl 'http://localhost/main' + hello + world + took 3.003 sec for total. + + real 0m3.027s + user 0m0.020s + sys 0m0.004s + + This directive is logically equivalent to the GET version of + echo_subrequest. For example, + + echo_location /foo 'bar=Bar'; + + is logically equivalent to + + echo_subrequest GET /foo -q 'bar=Bar'; + + But calling this directive is slightly faster than calling + echo_subrequest using "GET" because we don't have to parse the HTTP + method names like "GET" and options like "-q". + + Behind the scene, it creates an "ngx_http_post_subrequest_t" object as a + *continuation* and passes it into the "ngx_http_subrequest" function + call. Nginx will later reopen this "continuation" in the subrequest's + "ngx_http_finalize_request" function call. We resumes the execution of + the parent-request's content handler and starts to run the next + directive (command) if any. + + Nginx named locations like @foo is *not* supported here. + + This directive was first introduced in the release v0.12. + + See also echo_location_async for more details about the meaning of the + arguments. + + echo_subrequest_async + syntax: *echo_subrequest_async [-q ] + [-b ]* + + default: *no* + + context: *location* + + Initiate an asynchronous subrequest using HTTP method, an optional url + arguments (or querystring), and an option request body. + + This directive is very much like a generalized version of the + echo_location_async directive. + + Here's a small example demonstrating its usage: + + location /multi { + echo_subrequest_async POST '/sub' -q 'foo=Foo' -b 'hi'; + echo_subrequest_async PUT '/sub' -q 'bar=Bar' -b 'hello'; + } + location /sub { + echo "querystring: $query_string"; + echo "method: $echo_request_method"; + echo "body: $echo_request_body"; + echo "content length: $http_content_length"; + echo '///'; + } + + Then on the client side: + + $ curl 'http://localhost/multi' + querystring: foo=Foo + method: POST + body: hi + content length: 2 + /// + querystring: bar=Bar + method: PUT + body: hello + content length: 5 + /// + + Here's more funny example using the standard proxy module to handle the + subrequest: + + location /main { + echo_subrequest_async POST /sub -b 'hello, world'; + } + location /sub { + proxy_pass $scheme://127.0.0.1:$server_port/proxied; + } + location /proxied { + echo "method: $echo_request_method."; + + # we need to read body explicitly here...or $echo_request_body + # will evaluate to empty ("") + echo_read_request_body; + + echo "body: $echo_request_body."; + } + + Then on the client side, we can see that + + $ curl 'http://localhost/main' + method: POST. + body: hello, world. + + Nginx named locations like @foo is *not* supported here. + + This directive was first introduced in the release v0.15. + + See also the echo_subrequest and echo_location_async directives. + + echo_subrequest + syntax: *echo_subrequest_async [-q ] + [-b ]* + + default: *no* + + context: *location* + + This is the synchronous version of the echo_subrequest_async directive. + And just like echo_location, it does not block the Nginx worker process + (while echo_blocking_sleep does), rather, it uses continuation to pass + control along the subrequest chain. + + See echo_subrequest_async for more details. + + Nginx named locations like @foo is *not* supported here. + + This directive was first introduced in the release v0.15. + + echo_foreach_split + syntax: *echo_foreach_split * + + default: *no* + + context: *location* + + Split the second argument "string" using the delimiter specified in the + first argument, and then iterate through the resulting items. For + instance: + + location /loop { + echo_foreach_split ',' $arg_list; + echo "item: $echo_it"; + echo_end; + } + + Accessing /main yields + + $ curl 'http://localhost/loop?list=cat,dog,mouse' + item: cat + item: dog + item: mouse + + As seen in the previous example, this directive should always be + accompanied by an echo_end directive. + + Parallel "echo_foreach_split" loops are allowed, but nested ones are + currently forbidden. + + The "delimiter" argument could contain *multiple* arbitrary characters, + like + + echo_foreach_split '-a-' 'cat-a-dog-a-mouse'; + echo $echo_it; + echo_end; + + Logically speaking, this looping structure is just the "foreach" loop + combined with a "split" function call in Perl (using the previous + example): + + foreach (split ',', $arg_list) { + print "item $_\n"; + } + + People will also find it useful in merging multiple ".js" or ".css" + resources into a whole. Here's an example: + + location /merge { + default_type 'text/javascript'; + + echo_foreach_split '&' $query_string; + echo "/* JS File $echo_it */"; + echo_location_async $echo_it; + echo; + echo_end; + } + + Then accessing /merge to merge the ".js" resources specified in the + query string: + + $ curl 'http://localhost/merge?/foo/bar.js&/yui/blah.js&/baz.js' + + One can also use third-party Nginx cache module to cache the merged + response generated by the "/merge" location in the previous example. + + This directive was first introduced in the release v0.17. + + echo_end + syntax: *echo_end* + + default: *no* + + context: *location* + + This directive is used to terminate the body of looping and conditional + control structures like echo_foreach_split. + + This directive was first introduced in the release v0.17. + + echo_request_body + syntax: *echo_request_body* + + default: *no* + + context: *location* + + Outputs the contents of the request body previous read. + + Behind the scene, it's implemented roughly like this: + + if (r->request_body && r->request_body->bufs) { + return ngx_http_output_filter(r, r->request_body->bufs); + } + + Unlike the $echo_request_body and $request_body variables, this + directive will show the whole request body even if some parts or all + parts of it are saved in temporary files on the disk. + + It is a "no-op" if no request body has been read yet. + + This directive was first introduced in the release v0.18. + + See also echo_read_request_body and the chunkin module. + + echo_exec + syntax: *echo_exec []* + + syntax: *echo_exec * + + default: *no* + + context: *location* + + Does an internal redirect to the location specified. An optional query + string can be specified for normal locations, as in + + location /foo { + echo_exec /bar weight=5; + } + location /bar { + echo $arg_weight; + } + + Or equivalently + + location /foo { + echo_exec /bar?weight=5; + } + location /bar { + echo $arg_weight; + } + + Named locations are also supported. Here's an example: + + location /foo { + echo_exec @bar; + } + location @bar { + # you'll get /foo rather than @bar + # due to a potential bug in nginx. + echo $echo_request_uri; + } + + But query string (if any) will always be ignored for named location + redirects due to a limitation in the "ngx_http_named_location" function. + + Never try to echo things before the "echo_exec" directive or you won't + see the proper response of the location you want to redirect to. Because + any echoing will cause the original location handler to send HTTP + headers before the redirection happens. + + Technically speaking, this directive exposes the Nginx internal API + functions "ngx_http_internal_redirect" and "ngx_http_named_location". + + This directive was first introduced in the v0.21 release. + +Filter Directives + Use of the following directives trigger the filter registration of this + module. By default, no filter will be registered by this module. + + Every filter directive supports variable interpolation in its arguments + (if any). + + echo_before_body + syntax: *echo_before_body [options] [argument]...* + + default: *no* + + context: *location* + + It's the filter version of the echo directive, and prepends its output + to the beginning of the original outputs generated by the underlying + content handler. + + An example is + + location /echo { + echo_before_body hello; + proxy_pass $scheme://127.0.0.1:$server_port$request_uri/more; + } + location /echo/more { + echo world + } + + Accessing "/echo" from the client side yields + + hello + world + + In the previous sample, we borrow the standard proxy module to serve as + the underlying content handler that generates the "main contents". + + Multiple instances of this filter directive are also allowed, as in: + + location /echo { + echo_before_body hello; + echo_before_body world; + echo !; + } + + On the client side, the output is like + + $ curl 'http://localhost/echo' + hello + world + ! + + In this example, we also use the content handler directives provided by + this module as the underlying content handler. + + This directive also supports the "-n" and "--" options like the echo + directive. + + This directive can be mixed with its brother directive echo_after_body. + + echo_after_body + syntax: *echo_after_body [argument]...* + + default: *no* + + context: *location* + + WARNING this directive does not work for nginx >= 0.7.65. + + It's very much like the echo_before_body directive, but *appends* its + output to the end of the original outputs generated by the underlying + content handler. + + Here's a simple example: + + location /echo { + echo_after_body hello; + proxy_pass http://127.0.0.1:$server_port$request_uri/more; + } + location /echo/more { + echo world + } + + Accessing "/echo" from the client side yields + + world + hello + + Multiple instances are allowed, as in: + + location /echo { + echo_after_body hello; + echo_after_body world; + echo i; + echo say; + } + + The output on the client side while accessing the "/echo" location looks + like + + i + say + hello + world + + This directive also supports the "-n" and "--" options like the echo + directive. + + When this directive is used in a location accessed by a subrequest, it + replies on the "sync" flag set in a chain buffer to indicate the end of + the output for nginx >= 0.8.7. This is a hack because Nginx does not + provide a reliable way to determine the end of the output chain in a + subrequest's output filter. Use it in subrequests with care. + + This directive can be mixed with its brother directive echo_before_body. + +Variables + $echo_it + This is a "topic variable" used by echo_foreach_split, just like the $_ + variable in Perl. + + $echo_timer_elapsed + This variable holds the seconds elapsed since the start of the current + request (might be a subrequest though) or the last invocation of the + echo_reset_timer command. + + The timing result takes three digits after the decimal point. + + References of this variable will force the underlying Nginx timer to + update to the current system time, regardless the timer resolution + settings elsewhere in the config file, just like the echo_reset_timer + directive. + + $echo_request_body + Evaluates to the current (sub)request's request body previously read if + no part of the body has been saved to a temporary file. To always show + the request body even if it's very large, use the echo_request_body + directive. + + $echo_request_method + Evaluates to the HTTP request method of the current request (it can be a + subrequest). + + Behind the scene, it just takes the string data stored in + "r->method_name". + + Compare it to the $echo_client_request_method variable. + + At least for Nginx 0.8.20 and older, the $request_method variable + provided by the http core module is actually doing what our + $echo_client_request_method is doing. + + This variable was first introduced in our v0.15 release. + + $echo_client_request_method + Always evaluates to the main request's HTTP method even if the current + request is a subrequest. + + Behind the scene, it just takes the string data stored in + "r->main->method_name". + + Compare it to the $echo_request_method variable. + + This variable was first introduced in our v0.15 release. + + $echo_client_request_headers + Evaluates to the original client request's headers. + + Just as the name suggests, it will always take the main request (or the + client request) even if it's currently executed in a subrequest. + + A simple example is below: + + location /echoback { + echo "headers are:" + echo $echo_client_request_headers; + } + + Accessing "/echoback" yields + + $ curl 'http://localhost/echoback' + headers are + GET /echoback HTTP/1.1 + User-Agent: curl/7.18.2 (i486-pc-linux-gnu) libcurl/7.18.2 OpenSSL/0.9.8g + Host: localhost:1984 + Accept: */* + + Behind the scene, it recovers "r->main->header_in" on the C level and + does not construct the headers itself by traversing parsed results in + the request object, and strips the last (trailing) CRLF. + + This variable was first introduced in version 0.15. + + $echo_cacheable_request_uri + Evaluates to the parsed form of the URI (usually led by "/") of the + current (sub-)request. Unlike the $echo_request_uri variable, it is + cacheable. + + See $echo_request_uri for more details. + + This variable was first introduced in version 0.17. + + $echo_request_uri + Evaluates to the parsed form of the URI (usually led by "/") of the + current (sub-)request. Unlike the $echo_cacheable_request_uri variable, + it is *not* cacheable. + + This is quite different from the $request_uri variable exported by the + [[NginxHttpCoreModule]], because $request_uri is the *unparsed* form of + the current request's URI. + + This variable was first introduced in version 0.17. + + $echo_incr + It is a counter that always generate the current counting number, + starting from 1. The counter is always associated with the main request + even if it is accessed within a subrequest. + + Consider the following example + + location /main { + echo "main pre: $echo_incr"; + echo_location_async /sub; + echo_location_async /sub; + echo "main post: $echo_incr"; + } + location /sub { + echo "sub: $echo_incr"; + } + + Accessing "/main" yields + + main pre: 1 + sub: 3 + sub: 4 + main post: 2 + + This directive was first introduced in the v0.18 release. + + $echo_response_status + Evaluates to the status code of the current (sub)request, null if not + any. + + Behind the scene, it's just the textual representation of + "r->headers_out->status". + + This directive was first introduced in the v0.23 release. + +Installation + Grab the nginx source code from nginx.net (), for + example, the version 0.8.41 (see nginx compatibility), and then build + the source with this module: + + $ wget 'http://sysoev.ru/nginx/nginx-0.8.41.tar.gz' + $ tar -xzvf nginx-0.8.41.tar.gz + $ cd nginx-0.8.41/ + + # Here we assume you would install you nginx under /opt/nginx/. + $ ./configure --prefix=/opt/nginx \ + --add-module=/path/to/echo-nginx-module + + $ make -j2 + $ make install + + Download the latest version of the release tarball of this module from + echo-nginx-module file list + (). + +Compatibility + The following versions of Nginx should work with this module: + + * 0.8.x (last tested version is 0.8.40) + + * 0.7.x >= 0.7.21 (last tested version is 0.7.66) + + In particular, + + * the directive echo_location_async and its brother + echo_subrequest_async do *not* work with 0.7.x < 0.7.46. + + * the echo_after_body directive does *not* work at all with nginx < + 0.8.7. + + * the echo_sleep directive cannot be used after echo_location or + echo_subrequest for nginx < 0.8.11. + + Earlier versions of Nginx like 0.6.x and 0.5.x will *not* work at all. + + If you find that any particular version of Nginx above 0.7.21 does not + work with this module, please consider reporting a bug. + +Modules that use this module for testing + The following modules take advantage of this "echo" module in their test + suite: + + * The memc module that supports almost the whole memcached TCP + protocol. + + * The chunkin module that adds HTTP 1.1 chunked input support to + Nginx. + + * The headers_more module that allows you to add, set, and clear input + and output headers under the conditions that you specify. + + * The "echo" module itself. + + Please mail me other modules that use "echo" in any form and I'll add + them to the list above :) + +Report Bugs + Although a lot of effort has been put into testing and code tuning, + there must be some serious bugs lurking somewhere in this module. So + whenever you are bitten by any quirks, please don't hesitate to + + 1. send a bug report or even patches to , + + 2. or create a ticket on the issue tracking interface + () provided by + GitHub. + +Source Repository + Available on github at agentzh/echo-nginx-module + (). + +ChangeLog + v0.34 + * we no longer use the problematic "ngx_strXcmp" macros in our source + because it may cause invalid reads and thus segmentation faults. + thanks Piotr Sikora. + + v0.33 + * fixed compatibility with nginx 0.7.66+ because the ngx_time_update + macro's parameter list has changed. Thanks Guang Feng (蔡镜明). + + v0.32 + * we should have used "ngx_calloc_buf" instead of "ngx_alloc_buf" for + the last chunk generated for echo_after_body. thanks valgrind's + memcheck tool. + + * we should initialize flags before feeding it into + "ngx_http_parse_unsafe_uri". thanks valgrind's memcheck tool. + + * fixed a minor issue in the echo_location/echo_subrequest + implementation, which used to have race conditions. + + v0.31 + * the echo wev handler should not proceed if it is still waiting for + some sequential subrequest or has just processed one to avoid + bouncing issues. + + * fixed a segfault for echo_exec for 0.7.x: we should check "r->done" + before proceeding. + + * no longer explicitly set "r->write_event_handler" to + "ngx_http_request_empty_handler" because it's totally wrong for the + state machine. + + * fixed the sequential subrequest model bugs: we should ensure the + "pr->write_event_handler" gets called immediately after the + "post_subrequest" callback when the subrequest finalizes. + + v0.30 + * fixed the echo_exec directive for nginx >= 0.8.11. we didn't get the + "r->main->count" right in the previous version. + + v0.29 + * refactored the core of this module. now the implementation of + echo_location, echo_subrequest, echo_sleep, and + echo_read_request_body finally fit well with the nginx event model + and Igor Sysoev's way of thinking. + + v0.28 + * added support for the "-n" and "--" options to the echo, + echo_before_body, and echo_after_body directives. + + v0.27 + * applied the patch from Sergey A. Osokin to work with nginx 0.8.35. + + v0.26 + * bug fix: we should bypass upstream filters in our echo filters. an + output filter should ever call "ngx_http_output_filter" nor + "ngx_http_send_special". + + v0.25 + * now we register a request cleanup handler to ensure our sleep + event's timer will always get properly deleted even if the request + is quit prematurely. this affects the echo_sleep directive. + + * use ngx_null_string whenever possible in the source. + + * sync'd the bundled test scaffold to Test::Nginx 0.07. + + v0.24 + * various source file name and coding style fixes. (the code now looks + more like Igor Sysoev's.) + + v0.23 + * now the subrequest can read the client request body directly (for + the main request) because we made subrequests inherit its parent's + "r->header_in" as well. This affects the echo_read_request_body + directive. + + * fixed echo_after_body in subrequests by using a hack (checking + "cl->buf->sync" for the last buf) for nginx 0.8.7+ only. + + * added new varaible $echo_response_status to help testing the status + code of a subrequest. (The memc module makes use of it.) + + * use the "ngx_calloc_buf" macro to allocate new bufs in the code + rather than explicit "ngx_pcalloc" calls for safety. + + v0.22 + * Now we allowed all the directives appear in the rewrite module's if + block. But so far I've only tested the echo directive. + + v0.21 + * Added a new directive named echo_exec which does internal redirect + to other (named) locations. + + v0.20 + * Fixed a bug in echo_sleep's "r->main->count" handling for nginx + 0.8.x. This bug will cause the server to hang when proxing a + location with echo_sleep. + + * Applied the "ngx_str3cmp", "ngx_str4cmp", and "ngx_str6cmp" + optimizing macros to the "parse_method_name" function, as suggested + by Marcus Clyne. + + * Added TODO items regarding $echo_random and "echo_repeat" suggested + by Marcus Clyne. + + v0.19 + * Fixed the CPS-style chained subrequest model for the echo_location + and echo_subrequest directives. they are now working perfectly and + will not hang the server with the recent nginx 0.8.21 ~ 0.8.27 + releases. To be specifically, the chained subrequest should call + "ngx_http_finalize_request" on its parent request if the content + handler of the parent request does not return "NGX_DONE". + + * Undeprecated the echo_location and echo_subrequest directives. + + v0.18 + * Fixed the "zero size buf in output" alerts in error.log. + + * Added the new directive echo_request_body. + + * Now we use the "ngx_http_parse_unsafe_uri" function to check the + locations to echo_location_async and its friends. Thanks Arvind + Jayaprakash for suggesting this fix. + + * Deprecated the echo_location and echo_subrequest directives. + + * For HTTP 1.0 clients, use the buf length of the first chain link as + the output header Content-Length. + + * Implemented new variable $echo_incr. + + v0.17 + * Added new directives echo_foreach_split and echo_end. Also + introduced a "topic variable" named $echo_it. + + * Added new variables $echo_request_uri and + $echo_cacheable_request_uri. + + v0.16 + * Now the subrequests issued by the echo_location_async and + echo_location directives no longer inherit cached variable values + from its parent request. (The underlying "ngx_http_subrequest" + function, however, does automatic cachable variable value + inheritance.) + + * Added an undocumented variable *echo_cached_request_uri* to help + testing of this module. + + v0.15 + * Added new directives echo_subrequest and echo_subrequest_async for + the full nginx subrequest API. + + * Removed the "echo_client_request_headers" directive, and provided + the $echo_client_request_headers variable instead. + + * Added new variables $echo_request_method and + $echo_client_request_method. + + v0.14 + * Added new directive echo_read_request_body to explicitly read client + request body so that the [[NginxHttpCoreModule#$request_body]] + variable will always have non-empty values. + + * Now we shuffer test cases automatically in .t files and fixed bugs + in the tests themselves which are hidden by config reload fallback + in failure. + + v0.13 + * Fixed the special cases when the outputs of a echo_duplicate + directive is empty. + + * Now we explicitly clear content length and accept ranges headers in + the content handler. + + v0.12 + * Implemented the echo_location directive, which can issue chained GET + subrequests in the Continuation Passing Style (CPS), rather than the + parallel subrequest issued by the echo_location_async directive. + + v0.11 + * Implemented the echo_duplicate directive to help generating large + chunk of data for testing. + + v0.10 + * Fixed compilation regression against Nginx 0.7.21. This bug appears + in version 0.09. + + * Refactored the codebase by splitting source into various small + files. + + v0.09 + * Reimplement the echo_sleep directive using per-request event and + timer; the old implementation uses the global connection's + read/write event to register timer, so it will break horribly when + multiple subrequests "sleep" at the same time. + + * Added the echo_location_async directive which can issue a GET + subrequest and insert its contents herein. + + v0.08 + * echo_sleep: now we delete our "write event timer" in the + "post_sleep" handle. + + * Added "doc/manpage.wiki" which tracks changes in the wiki page + (). + + * Added the "util/wiki2pod.pl" script to convert "doc/manpage.wiki" to + "README". + + * Disabled the "DDEBUG" macro in the C source by default. + +Test Suite + This module comes with a Perl-driven test suite. The test cases + () are + declarative + ( + ) too. Thanks to the Test::Base + () module in the Perl world. + + To run it on your side: + + $ cd test + $ PATH=/path/to/your/nginx-with-echo-module:$PATH prove -r t + + You need to terminate any Nginx processes before running the test suite + if you have changed the Nginx server binary. + + At the moment, LWP::UserAgent + () is used by the test + scaffold + () for simplicity and it's rather weak in testing + *streaming* behavior of Nginx (I'm using "curl" to test these aspects + manually for now). I'm considering coding up my own Perl HTTP client + library based on IO::Select + () and IO::Socket + () (there might be already + one around?). + + Because a single nginx server (by default, "localhost:1984") is used + across all the test scripts (".t" files), it's meaningless to run the + test suite in parallel by specifying "-jN" when invoking the "prove" + utility. + + Some parts of the test suite requires standard modules proxy, rewrite + and SSI to be enabled as well when building Nginx. + +TODO + * Fix the echo_after_body directive in subrequests. + + * Add directives *echo_read_client_request_body* and + *echo_request_headers*. + + * Add new directive *echo_log* to use Nginx's logging facility + directly from the config file and specific loglevel can be + specified, as in + + echo_log debug "I am being called."; + + * Add support for options "-h" and "-t" to echo_subrequest_async and + echo_subrequest. For example + + echo_subrequest POST /sub -q 'foo=Foo&bar=Bar' -b 'hello' -t 'text/plan' -h 'X-My-Header: blah blah' + + * Add options to control whether a subrequest should inherit cached + variables from its parent request (i.e. the current request that is + calling the subrequest in question). Currently none of the + subrequests issued by this module inherit the cached variables from + the parent request. + + * Add new variable *$echo_active_subrequests* to show "r->main->count + - 1". + + * Add the *echo_file* and *echo_cached_file* directives. + + * Add new varaible *$echo_request_headers* to accompany the existing + $echo_client_request_headers variable. + + * Add new directive *echo_foreach*, as in + + echo_foreach 'cat' 'dog' 'mouse'; + echo_location_async "/animals/$echo_it"; + echo_end; + + * Add new directive *echo_foreach_range*, as in + + echo_foreach_range '[1..100]' '[a-zA-z0-9]'; + echo_location_async "/item/$echo_it"; + echo_end; + + * Add new directive *echo_repeat*, as in + + echo_repeat 10 $i { + echo "Page $i"; + echo_location "/path/to/page/$i"; + } + + This is just another way of saying + + echo_foreach_range $i [1..10]; + echo "Page $i"; + echo_location "/path/to/page/$i"; + echo_end; + + Thanks Marcus Clyne for providing this idea. + + * Add new variable $echo_random which always returns a random + non-negative integer with the lower/upper limit specified by the new + directives "echo_random_min" and "echo_random_max". For example, + + echo_random_min 10 + echo_random_max 200 + echo "random number: $echo_random"; + + Thanks Marcus Clyne for providing this idea. + +Getting involved + You'll be very welcomed to submit patches to the author or just ask for + a commit bit to the source repository on GitHub. + +Author + agentzh (章亦春) ** + + This wiki page is also maintained by the author himself, and everybody + is encouraged to improve this page as well. + +Copyright & License + Copyright (c) 2009, Taobao Inc., Alibaba Group ( http://www.taobao.com + ). + + Copyright (c) 2009, agentzh . + + This module is licensed under the terms of the BSD license. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * 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. + + * Neither the name of the Taobao Inc. nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT + HOLDER 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. + +See Also + * The original blog post + () about this module's initial development. + + * The standard addition filter module. + + * The standard proxy module. + diff -Nru nginx-0.5.33/modules/nginx-echo/src/ddebug.h nginx-0.8.53/modules/nginx-echo/src/ddebug.h --- nginx-0.5.33/modules/nginx-echo/src/ddebug.h 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/modules/nginx-echo/src/ddebug.h 2011-02-26 14:04:40.000000000 +0000 @@ -0,0 +1,109 @@ +#ifndef DDEBUG_H +#define DDEBUG_H + +#include +#include + +#if defined(DDEBUG) && (DDEBUG) + +# if (NGX_HAVE_VARIADIC_MACROS) + +# define dd(...) fprintf(stderr, "echo *** %s: ", __func__); \ + fprintf(stderr, __VA_ARGS__); \ + fprintf(stderr, " at %s line %d.\n", __FILE__, __LINE__) + +# else + +#include +#include + +#include + +static void dd(const char * fmt, ...) { +} + +# endif + +# if DDEBUG > 1 + +# define dd_enter() dd_enter_helper(r, __func__) + +static void dd_enter_helper(ngx_http_request_t *r, const char *func) { + ngx_http_posted_request_t *pr; + + fprintf(stderr, ">enter %s %.*s %.*s?%.*s c:%d m:%p r:%p ar:%p pr:%p", + func, + (int) r->method_name.len, r->method_name.data, + (int) r->uri.len, r->uri.data, + (int) r->args.len, r->args.data, + 0/*(int) r->main->count*/, r->main, + r, r->connection->data, r->parent); + + if (r->posted_requests) { + fprintf(stderr, " posted:"); + + for (pr = r->posted_requests; pr; pr = pr->next) { + fprintf(stderr, "%p,", pr); + } + } + + fprintf(stderr, "\n"); +} + +# else + +# define dd_enter() + +# endif + +#else + +# if (NGX_HAVE_VARIADIC_MACROS) + +# define dd(...) + +# define dd_enter() + +# else + +#include + +static void dd(const char * fmt, ...) { +} + +static void dd_enter() { +} + +# endif + +#endif + +#if defined(DDEBUG) && (DDEBUG) + +#define dd_check_read_event_handler(r) \ + dd("r->read_event_handler = %s", \ + r->read_event_handler == ngx_http_block_reading ? \ + "ngx_http_block_reading" : \ + r->read_event_handler == ngx_http_test_reading ? \ + "ngx_http_test_reading" : \ + r->read_event_handler == ngx_http_request_empty_handler ? \ + "ngx_http_request_empty_handler" : "UNKNOWN") + +#define dd_check_write_event_handler(r) \ + dd("r->write_event_handler = %s", \ + r->write_event_handler == ngx_http_handler ? \ + "ngx_http_handler" : \ + r->write_event_handler == ngx_http_core_run_phases ? \ + "ngx_http_core_run_phases" : \ + r->write_event_handler == ngx_http_request_empty_handler ? \ + "ngx_http_request_empty_handler" : "UNKNOWN") + +#else + +#define dd_check_read_event_handler(r) +#define dd_check_write_event_handler(r) + +#endif + +#endif /* DDEBUG_H */ + diff -Nru nginx-0.5.33/modules/nginx-echo/src/ngx_http_echo_echo.c nginx-0.8.53/modules/nginx-echo/src/ngx_http_echo_echo.c --- nginx-0.5.33/modules/nginx-echo/src/ngx_http_echo_echo.c 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/modules/nginx-echo/src/ngx_http_echo_echo.c 2011-02-26 14:04:40.000000000 +0000 @@ -0,0 +1,270 @@ +#define DDEBUG 0 +#include "ddebug.h" + +#include "ngx_http_echo_echo.h" +#include "ngx_http_echo_util.h" +#include "ngx_http_echo_filter.h" + +#include + +static ngx_buf_t ngx_http_echo_space_buf; + +static ngx_buf_t ngx_http_echo_newline_buf; + +ngx_int_t +ngx_http_echo_echo_init(ngx_conf_t *cf) +{ + static u_char space_str[] = " "; + static u_char newline_str[] = "\n"; + + dd("global init..."); + + ngx_memzero(&ngx_http_echo_space_buf, sizeof(ngx_buf_t)); + ngx_http_echo_space_buf.memory = 1; + ngx_http_echo_space_buf.start = + ngx_http_echo_space_buf.pos = + space_str; + ngx_http_echo_space_buf.end = + ngx_http_echo_space_buf.last = + space_str + sizeof(space_str) - 1; + + ngx_memzero(&ngx_http_echo_newline_buf, sizeof(ngx_buf_t)); + ngx_http_echo_newline_buf.memory = 1; + ngx_http_echo_newline_buf.start = + ngx_http_echo_newline_buf.pos = + newline_str; + ngx_http_echo_newline_buf.end = + ngx_http_echo_newline_buf.last = + newline_str + sizeof(newline_str) - 1; + + return NGX_OK; +} + + +ngx_int_t +ngx_http_echo_exec_echo(ngx_http_request_t *r, + ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args, + ngx_flag_t in_filter, ngx_array_t *opts) +{ + ngx_uint_t i; + + ngx_buf_t *space_buf; + ngx_buf_t *newline_buf; + ngx_buf_t *buf; + + ngx_str_t *computed_arg; + ngx_str_t *computed_arg_elts; + ngx_str_t *opt; + + ngx_chain_t *cl = NULL; /* the head of the chain link */ + ngx_chain_t **ll = &cl; /* always point to the address of the last link */ + + + dd_enter(); + + if (computed_args == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + computed_arg_elts = computed_args->elts; + for (i = 0; i < computed_args->nelts; i++) { + computed_arg = &computed_arg_elts[i]; + + if (computed_arg->len == 0) { + buf = NULL; + + } else { + buf = ngx_calloc_buf(r->pool); + if (buf == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + buf->start = buf->pos = computed_arg->data; + buf->last = buf->end = computed_arg->data + + computed_arg->len; + + buf->memory = 1; + } + + if (cl == NULL) { + cl = ngx_alloc_chain_link(r->pool); + if (cl == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + cl->buf = buf; + cl->next = NULL; + ll = &cl->next; + } else { + /* append a space first */ + *ll = ngx_alloc_chain_link(r->pool); + + if (*ll == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + space_buf = ngx_calloc_buf(r->pool); + + if (space_buf == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + /* nginx clears buf flags at the end of each request handling, + * so we have to make a clone here. */ + *space_buf = ngx_http_echo_space_buf; + + (*ll)->buf = space_buf; + (*ll)->next = NULL; + + ll = &(*ll)->next; + + /* then append the buf only if it's non-empty */ + if (buf) { + *ll = ngx_alloc_chain_link(r->pool); + if (*ll == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + (*ll)->buf = buf; + (*ll)->next = NULL; + + ll = &(*ll)->next; + } + } + } /* end for */ + + if (opts && opts->nelts > 0) { + opt = opts->elts; + if (opt[0].len == 1 && opt[0].data[0] == 'n') { + goto done; + } + } + + /* append the newline character */ + + if (cl && cl->buf == NULL) { + cl = cl->next; + } + + newline_buf = ngx_calloc_buf(r->pool); + + if (newline_buf == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + *newline_buf = ngx_http_echo_newline_buf; + + if (cl == NULL) { + cl = ngx_alloc_chain_link(r->pool); + + if (cl == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + cl->buf = newline_buf; + cl->next = NULL; + /* ll = &cl->next; */ + + } else { + *ll = ngx_alloc_chain_link(r->pool); + + if (*ll == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + (*ll)->buf = newline_buf; + (*ll)->next = NULL; + /* ll = &(*ll)->next; */ + } + +done: + + if (cl == NULL || cl->buf == NULL) { + return NGX_OK; + } + + if (in_filter) { + return ngx_http_echo_next_body_filter(r, cl); + } + + return ngx_http_echo_send_chain_link(r, ctx, cl); +} + + +ngx_int_t +ngx_http_echo_exec_echo_flush(ngx_http_request_t *r, ngx_http_echo_ctx_t *ctx) +{ + return ngx_http_send_special(r, NGX_HTTP_FLUSH); +} + + +ngx_int_t +ngx_http_echo_exec_echo_request_body(ngx_http_request_t *r, ngx_http_echo_ctx_t *ctx) +{ + if (r->request_body && r->request_body->bufs) { + return ngx_http_echo_send_chain_link(r, ctx, r->request_body->bufs); + } + + return NGX_OK; +} + + +ngx_int_t +ngx_http_echo_exec_echo_duplicate(ngx_http_request_t *r, + ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args) +{ + ngx_str_t *computed_arg; + ngx_str_t *computed_arg_elts; + ssize_t i, count; + ngx_str_t *str; + u_char *p; + ngx_int_t rc; + + ngx_buf_t *buf; + ngx_chain_t *cl; + + + dd_enter(); + + computed_arg_elts = computed_args->elts; + + computed_arg = &computed_arg_elts[0]; + + count = ngx_http_echo_atosz(computed_arg->data, computed_arg->len); + + if (count == NGX_ERROR) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "invalid size specified: \"%V\"", computed_arg); + + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + str = &computed_arg_elts[1]; + + if (count == 0 || str->len == 0) { + rc = ngx_http_echo_send_header_if_needed(r, ctx); + if (r->header_only || rc >= NGX_HTTP_SPECIAL_RESPONSE) { + return rc; + } + return NGX_OK; + } + + buf = ngx_create_temp_buf(r->pool, count * str->len); + if (buf == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + p = buf->pos; + for (i = 0; i < count; i++) { + p = ngx_copy(p, str->data, str->len); + } + buf->last = p; + + cl = ngx_alloc_chain_link(r->pool); + if (cl == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + cl->next = NULL; + cl->buf = buf; + + return ngx_http_echo_send_chain_link(r, ctx, cl); +} + diff -Nru nginx-0.5.33/modules/nginx-echo/src/ngx_http_echo_echo.h nginx-0.8.53/modules/nginx-echo/src/ngx_http_echo_echo.h --- nginx-0.5.33/modules/nginx-echo/src/ngx_http_echo_echo.h 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/modules/nginx-echo/src/ngx_http_echo_echo.h 2011-02-26 14:04:40.000000000 +0000 @@ -0,0 +1,22 @@ +#ifndef ECHO_ECHO_H +#define ECHO_ECHO_H + +#include "ngx_http_echo_module.h" + +ngx_int_t ngx_http_echo_echo_init(ngx_conf_t *cf); + +ngx_int_t ngx_http_echo_exec_echo(ngx_http_request_t *r, + ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args, + ngx_flag_t in_filter, ngx_array_t *opts); + +ngx_int_t ngx_http_echo_exec_echo_request_body(ngx_http_request_t *r, + ngx_http_echo_ctx_t *ctx); + +ngx_int_t ngx_http_echo_exec_echo_flush(ngx_http_request_t *r, + ngx_http_echo_ctx_t *ctx); + +ngx_int_t ngx_http_echo_exec_echo_duplicate(ngx_http_request_t *r, + ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args); + +#endif /* ECHO_ECHO_H */ + diff -Nru nginx-0.5.33/modules/nginx-echo/src/ngx_http_echo_filter.c nginx-0.8.53/modules/nginx-echo/src/ngx_http_echo_filter.c --- nginx-0.5.33/modules/nginx-echo/src/ngx_http_echo_filter.c 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/modules/nginx-echo/src/ngx_http_echo_filter.c 2011-02-26 14:04:40.000000000 +0000 @@ -0,0 +1,240 @@ +#define DDEBUG 0 + +#include "ddebug.h" +#include "ngx_http_echo_filter.h" +#include "ngx_http_echo_util.h" +#include "ngx_http_echo_echo.h" + +#include + +ngx_flag_t ngx_http_echo_filter_used = 0; + +ngx_http_output_header_filter_pt ngx_http_echo_next_header_filter; + +ngx_http_output_body_filter_pt ngx_http_echo_next_body_filter; + +static ngx_int_t ngx_http_echo_header_filter(ngx_http_request_t *r); + +static ngx_int_t ngx_http_echo_body_filter(ngx_http_request_t *r, ngx_chain_t *in); + +/* filter handlers */ +static ngx_int_t ngx_http_echo_exec_filter_cmds(ngx_http_request_t *r, + ngx_http_echo_ctx_t *ctx, ngx_array_t *cmds, ngx_uint_t *iterator); + + +ngx_int_t +ngx_http_echo_filter_init (ngx_conf_t *cf) +{ + if (ngx_http_echo_filter_used) { + dd("top header filter: %ld", (unsigned long) ngx_http_top_header_filter); + ngx_http_echo_next_header_filter = ngx_http_top_header_filter; + ngx_http_top_header_filter = ngx_http_echo_header_filter; + + dd("top body filter: %ld", (unsigned long) ngx_http_top_body_filter); + ngx_http_echo_next_body_filter = ngx_http_top_body_filter; + ngx_http_top_body_filter = ngx_http_echo_body_filter; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_echo_header_filter(ngx_http_request_t *r) +{ + ngx_http_echo_loc_conf_t *conf; + ngx_http_echo_ctx_t *ctx; + ngx_int_t rc; + + dd("We're in the header filter..."); + + ctx = ngx_http_get_module_ctx(r, ngx_http_echo_module); + + /* XXX we should add option to insert contents for responses + * of non-200 status code here... */ + /* + if (r->headers_out.status != NGX_HTTP_OK) { + if (ctx != NULL) { + ctx->skip_filter = 1; + } + return ngx_http_echo_next_header_filter(r); + } + */ + + conf = ngx_http_get_module_loc_conf(r, ngx_http_echo_module); + if (conf->before_body_cmds == NULL && conf->after_body_cmds == NULL) { + if (ctx != NULL) { + ctx->skip_filter = 1; + } + return ngx_http_echo_next_header_filter(r); + } + + if (ctx == NULL) { + rc = ngx_http_echo_init_ctx(r, &ctx); + if (rc != NGX_OK) { + return NGX_ERROR; + } + ctx->headers_sent = 1; + ngx_http_set_ctx(r, ctx, ngx_http_echo_module); + } + + /* enable streaming here (use chunked encoding) */ + ngx_http_clear_content_length(r); + ngx_http_clear_accept_ranges(r); + + return ngx_http_echo_next_header_filter(r); +} + + +static ngx_int_t +ngx_http_echo_body_filter(ngx_http_request_t *r, ngx_chain_t *in) +{ + ngx_http_echo_ctx_t *ctx; + ngx_int_t rc; + ngx_http_echo_loc_conf_t *conf; + ngx_flag_t last; + ngx_chain_t *cl; + ngx_buf_t *buf; + + if (in == NULL || r->header_only) { + return ngx_http_echo_next_body_filter(r, in); + } + + ctx = ngx_http_get_module_ctx(r, ngx_http_echo_module); + + if (ctx == NULL || ctx->skip_filter) { + return ngx_http_echo_next_body_filter(r, in); + } + + conf = ngx_http_get_module_loc_conf(r, ngx_http_echo_module); + + if (!ctx->before_body_sent) { + ctx->before_body_sent = 1; + + if (conf->before_body_cmds != NULL) { + rc = ngx_http_echo_exec_filter_cmds(r, ctx, conf->before_body_cmds, + &ctx->next_before_body_cmd); + if (rc != NGX_OK) { + return NGX_ERROR; + } + } + } + + if (conf->after_body_cmds == NULL) { + ctx->skip_filter = 1; + return ngx_http_echo_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->sync = 1; + last = 1; + } else if (r != r->main && cl->buf->sync) { + dd("Found sync buf"); + last = 1; + } + } + + rc = ngx_http_echo_next_body_filter(r, in); + + if (rc == NGX_ERROR || !last) { + return rc; + } + + dd("exec filter cmds for after body cmds"); + rc = ngx_http_echo_exec_filter_cmds(r, ctx, conf->after_body_cmds, &ctx->next_after_body_cmd); + if (rc != NGX_OK) { + dd("FAILED: exec filter cmds for after body cmds"); + return NGX_ERROR; + } + + ctx->skip_filter = 1; + + dd("after body cmds executed...terminating..."); + + /* XXX we can NOT use + * ngx_http_send_special(r, NGX_HTTP_LAST) here + * because we should bypass the upstream filters. */ + if (r != r->main) { + return NGX_OK; + } + + buf = ngx_calloc_buf(r->pool); + buf->last_buf = 1; + + cl = ngx_alloc_chain_link(r->pool); + if (cl == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + cl->next = NULL; + cl->buf = buf; + + return ngx_http_echo_next_body_filter(r, cl); +} + + +static ngx_int_t +ngx_http_echo_exec_filter_cmds(ngx_http_request_t *r, + ngx_http_echo_ctx_t *ctx, ngx_array_t *cmds, + ngx_uint_t *iterator) +{ + ngx_int_t rc; + ngx_array_t *computed_args = NULL; + ngx_http_echo_cmd_t *cmd; + ngx_http_echo_cmd_t *cmd_elts; + ngx_array_t *opts = NULL; + + for (cmd_elts = cmds->elts; *iterator < cmds->nelts; (*iterator)++) { + cmd = &cmd_elts[*iterator]; + + /* evaluate arguments for the current cmd (if any) */ + if (cmd->args) { + computed_args = ngx_array_create(r->pool, cmd->args->nelts, + sizeof(ngx_str_t)); + + if (computed_args == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + opts = ngx_array_create(r->pool, 1, sizeof(ngx_str_t)); + + if (opts == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + rc = ngx_http_echo_eval_cmd_args(r, cmd, computed_args, opts); + + if (rc != NGX_OK) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "Failed to evaluate arguments for " + "the directive."); + return rc; + } + } + + /* do command dispatch based on the opcode */ + switch (cmd->opcode) { + case echo_opcode_echo_before_body: + case echo_opcode_echo_after_body: + dd("exec echo_before_body or echo_after_body..."); + + rc = ngx_http_echo_exec_echo(r, ctx, computed_args, + 1 /* in filter */, opts); + + if (rc != NGX_OK) { + return rc; + } + + break; + default: + break; + } + } + + return NGX_OK; +} + diff -Nru nginx-0.5.33/modules/nginx-echo/src/ngx_http_echo_filter.h nginx-0.8.53/modules/nginx-echo/src/ngx_http_echo_filter.h --- nginx-0.5.33/modules/nginx-echo/src/ngx_http_echo_filter.h 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/modules/nginx-echo/src/ngx_http_echo_filter.h 2011-02-26 14:04:40.000000000 +0000 @@ -0,0 +1,16 @@ +#ifndef ECHO_FILTER_H +#define ECHO_FILTER_H + +#include "ngx_http_echo_module.h" + +extern ngx_flag_t ngx_http_echo_filter_used; + +extern ngx_http_output_header_filter_pt ngx_http_echo_next_header_filter; + +extern ngx_http_output_body_filter_pt ngx_http_echo_next_body_filter; + + +ngx_int_t ngx_http_echo_filter_init (ngx_conf_t *cf); + +#endif /* ECHO_FILTER_H */ + diff -Nru nginx-0.5.33/modules/nginx-echo/src/ngx_http_echo_foreach.c nginx-0.8.53/modules/nginx-echo/src/ngx_http_echo_foreach.c --- nginx-0.5.33/modules/nginx-echo/src/ngx_http_echo_foreach.c 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/modules/nginx-echo/src/ngx_http_echo_foreach.c 2011-02-26 14:04:40.000000000 +0000 @@ -0,0 +1,174 @@ +#define DDEBUG 0 +#include "ddebug.h" + +#include "ngx_http_echo_foreach.h" +#include "ngx_http_echo_util.h" + +#include + +ngx_int_t +ngx_http_echo_it_variable(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + ngx_http_echo_ctx_t *ctx; + ngx_uint_t i; + ngx_array_t *choices; + ngx_str_t *choice_elts, *choice; + + ctx = ngx_http_get_module_ctx(r, ngx_http_echo_module); + + if (ctx->foreach != NULL) { + choices = ctx->foreach->choices; + i = ctx->foreach->next_choice; + if (i < choices->nelts) { + choice_elts = choices->elts; + choice = &choice_elts[i]; + + v->len = choice->len; + v->data = choice->data; + v->valid = 1; + v->no_cacheable = 1; + v->not_found = 0; + } + + return NGX_OK; + } + + v->not_found = 1; + + return NGX_OK; +} + + +ngx_int_t +ngx_http_echo_exec_echo_foreach_split(ngx_http_request_t *r, + ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args) +{ + ngx_http_echo_loc_conf_t *elcf; + ngx_str_t *delimiter, *compound; + u_char *pos, *last, *end; + ngx_str_t *choice; + ngx_str_t *computed_arg_elts; + ngx_array_t *cmds; + ngx_http_echo_cmd_t *cmd; + ngx_http_echo_cmd_t *cmd_elts; + + if (ctx->foreach != NULL) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "Nested echo_foreach not supported yet."); + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + if (computed_args->nelts < 2) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "echo_foreach should take at least two arguments. " + "(if your delimiter starts with \"-\", preceding it with a " + "\"--\".)"); + + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + computed_arg_elts = computed_args->elts; + + compound = &computed_arg_elts[1]; + + dd("HEY coumpound len: %u", compound->len); + + ctx->foreach = ngx_palloc(r->pool, sizeof(ngx_http_echo_foreach_ctx_t)); + + if (ctx->foreach == NULL) { + return NGX_ERROR; + } + + ctx->foreach->cmd_index = ctx->next_handler_cmd; + + ctx->foreach->next_choice = 0; + + ctx->foreach->choices = ngx_array_create(r->pool, 10, sizeof(ngx_str_t)); + if (ctx->foreach->choices == NULL) { + return NGX_ERROR; + } + + delimiter = &computed_arg_elts[0]; + + pos = compound->data; + end = compound->data + compound->len; + while ((last = ngx_http_echo_strlstrn(pos, end, delimiter->data, delimiter->len - 1)) + != NULL) { + dd("entered the loop"); + + if (last == pos) { + dd("!!! len == 0"); + pos = last + delimiter->len; + continue; + } + + choice = ngx_array_push(ctx->foreach->choices); + if (choice == NULL) { + return NGX_ERROR; + } + + choice->data = pos; + choice->len = last - pos; + pos = last + delimiter->len; + } + + if (pos < end) { + choice = ngx_array_push(ctx->foreach->choices); + if (choice == NULL) { + return NGX_ERROR; + } + + choice->data = pos; + choice->len = end - pos; + } + + if (ctx->foreach->choices->nelts == 0) { + /* skip the foreach body entirely */ + elcf = ngx_http_get_module_loc_conf(r, ngx_http_echo_module); + cmds = elcf->handler_cmds; + cmd_elts = cmds->elts; + for (; ctx->next_handler_cmd < cmds->nelts; + ctx->next_handler_cmd++) { + cmd = &cmd_elts[ctx->next_handler_cmd + 1]; + if (cmd->opcode == echo_opcode_echo_end) { + return NGX_OK; + } + } + + } + + return NGX_OK; +} + + +ngx_int_t +ngx_http_echo_exec_echo_end(ngx_http_request_t *r, + ngx_http_echo_ctx_t *ctx) +{ + if (ctx->foreach == NULL) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "Found a echo_end that has no corresponding echo_foreach " + "before it."); + return NGX_ERROR; + } + + ctx->foreach->next_choice++; + + if (ctx->foreach->next_choice >= ctx->foreach->choices->nelts) { + /* TODO We need to explicitly free the foreach ctx from + * the pool */ + ctx->foreach = NULL; + + return NGX_OK; + } + + dd("echo_end: ++ next_choice (total: %u): %u", ctx->foreach->choices->nelts, ctx->foreach->next_choice); + + /* the main handler dispatcher loop will increment + * ctx->next_handler_cmd for us anyway. */ + ctx->next_handler_cmd = ctx->foreach->cmd_index; + + return NGX_OK; +} + diff -Nru nginx-0.5.33/modules/nginx-echo/src/ngx_http_echo_foreach.h nginx-0.8.53/modules/nginx-echo/src/ngx_http_echo_foreach.h --- nginx-0.5.33/modules/nginx-echo/src/ngx_http_echo_foreach.h 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/modules/nginx-echo/src/ngx_http_echo_foreach.h 2011-02-26 14:04:40.000000000 +0000 @@ -0,0 +1,16 @@ +#ifndef ECHO_FOREACH_H +#define ECHO_FOREACH_H + +#include "ngx_http_echo_module.h" + +ngx_int_t ngx_http_echo_exec_echo_foreach_split(ngx_http_request_t *r, + ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args); + +ngx_int_t ngx_http_echo_exec_echo_end(ngx_http_request_t *r, + ngx_http_echo_ctx_t *ctx); + +ngx_int_t ngx_http_echo_it_variable(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); + +#endif /* ECHO_FOREACH_H */ + diff -Nru nginx-0.5.33/modules/nginx-echo/src/ngx_http_echo_handler.c nginx-0.8.53/modules/nginx-echo/src/ngx_http_echo_handler.c --- nginx-0.5.33/modules/nginx-echo/src/ngx_http_echo_handler.c 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/modules/nginx-echo/src/ngx_http_echo_handler.c 2011-02-26 14:04:40.000000000 +0000 @@ -0,0 +1,351 @@ +#define DDEBUG 0 + +#include "ddebug.h" + +#include "ngx_http_echo_handler.h" +#include "ngx_http_echo_echo.h" +#include "ngx_http_echo_util.h" +#include "ngx_http_echo_sleep.h" +#include "ngx_http_echo_var.h" +#include "ngx_http_echo_timer.h" +#include "ngx_http_echo_location.h" +#include "ngx_http_echo_subrequest.h" +#include "ngx_http_echo_request_info.h" +#include "ngx_http_echo_foreach.h" + +#include +#include + +ngx_int_t +ngx_http_echo_handler_init(ngx_conf_t *cf) +{ + ngx_int_t rc; + + rc = ngx_http_echo_echo_init(cf); + if (rc != NGX_OK) { + return rc; + } + + return ngx_http_echo_add_variables(cf); +} + + +void +ngx_http_echo_wev_handler(ngx_http_request_t *r) +{ + ngx_int_t rc; + ngx_http_echo_ctx_t *ctx; + + dd_enter(); + + ctx = ngx_http_get_module_ctx(r, ngx_http_echo_module); + + if (ctx == NULL) { + ngx_http_finalize_request(r, NGX_ERROR); + return; + } + + if (ctx->waiting && ! ctx->done) { + if (r->main->posted_requests + && r->main->posted_requests->request != r) + { + dd("HOT SPIN"); + +#if defined(nginx_version) && nginx_version >= 8012 + ngx_http_post_request(r, NULL); +#else + ngx_http_post_request(r); +#endif + + return; + } + } + + ctx->done = 0; + + ctx->next_handler_cmd++; + + rc = ngx_http_echo_run_cmds(r); + + dd("rc: %d", (int) rc); + + if (rc == NGX_DONE) { + return; + } + + if (rc == NGX_AGAIN) { + dd("mark busy %d", (int) ctx->next_handler_cmd); + ctx->waiting = 1; + ctx->done = 0; + + } else { + dd("mark ready %d", (int) ctx->next_handler_cmd); + ctx->waiting = 0; + ctx->done = 1; + + dd("finalizing with rc %d", (int) rc); + + dd("finalize request %.*s with %d", (int) r->uri.len, r->uri.data, (int) rc); + + ngx_http_finalize_request(r, rc); + } +} + + +ngx_int_t +ngx_http_echo_handler(ngx_http_request_t *r) +{ + ngx_int_t rc; + ngx_http_echo_ctx_t *ctx; + + rc = ngx_http_echo_run_cmds(r); + + if (rc == NGX_ERROR) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { + return rc; + } + + if (rc == NGX_DONE) { + return NGX_DONE; + } + + if (rc == NGX_AGAIN) { +#if defined(nginx_version) && nginx_version >= 8011 + r->main->count++; +#endif + + /* XXX we need this for 0.7.x and 0.8.x < 0.8.11 */ + dd("%d", r->connection->destroyed); + dd("%d", r->done); + + ctx = ngx_http_get_module_ctx(r, ngx_http_echo_module); + if (ctx) { + dd("mark busy %d", (int) ctx->next_handler_cmd); + ctx->waiting = 1; + ctx->done = 0; + } + + return NGX_DONE; + } + + return NGX_OK; +} + + +ngx_int_t +ngx_http_echo_run_cmds(ngx_http_request_t *r) +{ + ngx_http_echo_loc_conf_t *elcf; + ngx_http_echo_ctx_t *ctx; + ngx_int_t rc; + ngx_array_t *cmds; + ngx_array_t *computed_args = NULL; + ngx_http_echo_cmd_t *cmd; + ngx_http_echo_cmd_t *cmd_elts; + ngx_array_t *opts = NULL; + + + elcf = ngx_http_get_module_loc_conf(r, ngx_http_echo_module); + cmds = elcf->handler_cmds; + if (cmds == NULL) { + return NGX_DECLINED; + } + + ctx = ngx_http_get_module_ctx(r, ngx_http_echo_module); + if (ctx == NULL) { + rc = ngx_http_echo_init_ctx(r, &ctx); + if (rc != NGX_OK) { + return rc; + } + + ngx_http_set_ctx(r, ctx, ngx_http_echo_module); + } + + dd("exec handler: %.*s: %i", (int) r->uri.len, r->uri.data, + (int) ctx->next_handler_cmd); + + cmd_elts = cmds->elts; + + for (; ctx->next_handler_cmd < cmds->nelts; ctx->next_handler_cmd++) { + + cmd = &cmd_elts[ctx->next_handler_cmd]; + + /* evaluate arguments for the current cmd (if any) */ + if (cmd->args) { + computed_args = ngx_array_create(r->pool, cmd->args->nelts, + sizeof(ngx_str_t)); + + if (computed_args == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + opts = ngx_array_create(r->pool, 1, sizeof(ngx_str_t)); + + if (opts == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + rc = ngx_http_echo_eval_cmd_args(r, cmd, computed_args, opts); + if (rc != NGX_OK) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "Failed to evaluate arguments for " + "the directive."); + return rc; + } + } + + /* do command dispatch based on the opcode */ + switch (cmd->opcode) { + case echo_opcode_echo: + /* XXX moved the following code to a separate + * function */ + dd("found echo opcode"); + rc = ngx_http_echo_exec_echo(r, ctx, computed_args, + 0 /* in filter */, opts); + break; + + case echo_opcode_echo_request_body: + rc = ngx_http_echo_exec_echo_request_body(r, ctx); + break; + + case echo_opcode_echo_location_async: + dd("found opcode echo location async..."); + rc = ngx_http_echo_exec_echo_location_async(r, ctx, + computed_args); + break; + + case echo_opcode_echo_location: + return ngx_http_echo_exec_echo_location(r, ctx, computed_args); + break; + + case echo_opcode_echo_subrequest_async: + dd("found opcode echo subrequest async..."); + rc = ngx_http_echo_exec_echo_subrequest_async(r, ctx, + computed_args); + break; + + case echo_opcode_echo_subrequest: + return ngx_http_echo_exec_echo_subrequest(r, ctx, computed_args); + break; + + case echo_opcode_echo_sleep: + return ngx_http_echo_exec_echo_sleep(r, ctx, computed_args); + break; + + case echo_opcode_echo_flush: + rc = ngx_http_echo_exec_echo_flush(r, ctx); + break; + + case echo_opcode_echo_blocking_sleep: + rc = ngx_http_echo_exec_echo_blocking_sleep(r, ctx, + computed_args); + break; + + case echo_opcode_echo_reset_timer: + rc = ngx_http_echo_exec_echo_reset_timer(r, ctx); + break; + + case echo_opcode_echo_duplicate: + rc = ngx_http_echo_exec_echo_duplicate(r, ctx, computed_args); + break; + + case echo_opcode_echo_read_request_body: + ctx->wait_read_request_body = 0; + + rc = ngx_http_echo_exec_echo_read_request_body(r, ctx); + +#if defined(nginx_version) && nginx_version >= 8011 + /* XXX read_client_request_body always increments the counter */ + r->main->count--; +#endif + + dd("read request body: %d", (int) rc); + + if (rc == NGX_OK) { + continue; + } + + ctx->wait_read_request_body = 1; + + /* r->write_event_handler = ngx_http_request_empty_handler; */ + + return rc; + break; + + case echo_opcode_echo_foreach_split: + rc = ngx_http_echo_exec_echo_foreach_split(r, ctx, computed_args); + break; + + case echo_opcode_echo_end: + rc = ngx_http_echo_exec_echo_end(r, ctx); + break; + + case echo_opcode_echo_exec: + dd("echo_exec"); + return ngx_http_echo_exec_exec(r, ctx, computed_args); + break; + + default: + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "Unknown opcode: %d", cmd->opcode); + return NGX_HTTP_INTERNAL_SERVER_ERROR; + break; + } + + if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { + return rc; + } + } + + rc = ngx_http_echo_send_chain_link(r, ctx, NULL /* indicate LAST */); + + if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { + return rc; + } + + return NGX_OK; +} + + +ngx_int_t +ngx_http_echo_post_subrequest(ngx_http_request_t *r, + void *data, ngx_int_t rc) +{ + ngx_http_request_t *pr; + ngx_http_echo_ctx_t *pr_ctx; + + + dd_enter(); + + pr = r->parent; + + pr_ctx = ngx_http_get_module_ctx(pr, ngx_http_echo_module); + if (pr_ctx == NULL) { + return NGX_ERROR; + } + + dd("mark ready %d", (int) pr_ctx->next_handler_cmd); + + pr_ctx->waiting = 0; + pr_ctx->done = 1; + + pr->write_event_handler = ngx_http_echo_wev_handler; + + /* ensure that the parent request is (or will be) + * posted out the head of the r->posted_requests chain */ + + if (r->main->posted_requests + && r->main->posted_requests->request != pr) + { + rc = ngx_http_echo_post_request_at_head(pr, NULL); + if (rc != NGX_OK) { + return NGX_ERROR; + } + } + + return rc; +} + diff -Nru nginx-0.5.33/modules/nginx-echo/src/ngx_http_echo_handler.h nginx-0.8.53/modules/nginx-echo/src/ngx_http_echo_handler.h --- nginx-0.5.33/modules/nginx-echo/src/ngx_http_echo_handler.h 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/modules/nginx-echo/src/ngx_http_echo_handler.h 2011-02-26 14:04:40.000000000 +0000 @@ -0,0 +1,20 @@ +#ifndef ECHO_HANDLER_H +#define ECHO_HANDLER_H + +#include "ngx_http_echo_module.h" + + +void ngx_http_echo_wev_handler(ngx_http_request_t *r); + +ngx_int_t ngx_http_echo_handler_init(ngx_conf_t *cf); + +ngx_int_t ngx_http_echo_handler(ngx_http_request_t *r); + +ngx_int_t ngx_http_echo_run_cmds(ngx_http_request_t *r); + +ngx_int_t ngx_http_echo_post_subrequest(ngx_http_request_t *r, + void *data, ngx_int_t rc); + + +#endif /* ECHO_HANDLER_H */ + diff -Nru nginx-0.5.33/modules/nginx-echo/src/ngx_http_echo_location.c nginx-0.8.53/modules/nginx-echo/src/ngx_http_echo_location.c --- nginx-0.5.33/modules/nginx-echo/src/ngx_http_echo_location.c 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/modules/nginx-echo/src/ngx_http_echo_location.c 2011-02-26 14:04:40.000000000 +0000 @@ -0,0 +1,183 @@ +#define DDEBUG 0 +#include "ddebug.h" + +#include "ngx_http_echo_util.h" +#include "ngx_http_echo_location.h" +#include "ngx_http_echo_handler.h" + +#include + + +static ngx_int_t ngx_http_echo_adjust_subrequest(ngx_http_request_t *sr); + + +ngx_int_t +ngx_http_echo_exec_echo_location_async(ngx_http_request_t *r, + ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args) +{ + ngx_int_t rc; + ngx_http_request_t *sr; /* subrequest object */ + ngx_str_t *computed_arg_elts; + ngx_str_t location; + ngx_str_t *url_args; + ngx_str_t args; + ngx_uint_t flags = 0; + + + dd_enter(); + + computed_arg_elts = computed_args->elts; + + location = computed_arg_elts[0]; + + if (location.len == 0) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + if (computed_args->nelts > 1) { + url_args = &computed_arg_elts[1]; + } else { + url_args = NULL; + } + + dd("location: %s", location.data); + dd("location args: %s", (char*) (url_args ? url_args->data : (u_char*)"NULL")); + + args.data = NULL; + args.len = 0; + + if (ngx_http_parse_unsafe_uri(r, &location, &args, &flags) != NGX_OK) { + ctx->headers_sent = 1; + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + if (args.len > 0 && url_args == NULL) { + url_args = &args; + } + + rc = ngx_http_echo_send_header_if_needed(r, ctx); + if (r->header_only) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { + return rc; + } + + rc = ngx_http_subrequest(r, &location, url_args, &sr, NULL, 0); + + if (rc != NGX_OK) { + return NGX_ERROR; + } + + rc = ngx_http_echo_adjust_subrequest(sr); + if (rc != NGX_OK) { + return NGX_ERROR; + } + + return NGX_OK; +} + + +ngx_int_t +ngx_http_echo_exec_echo_location(ngx_http_request_t *r, + ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args) +{ + ngx_int_t rc; + ngx_http_request_t *sr; /* subrequest object */ + ngx_str_t *computed_arg_elts; + ngx_str_t location; + ngx_str_t *url_args; + ngx_http_post_subrequest_t *psr; + ngx_str_t args; + ngx_uint_t flags = 0; + + + dd_enter(); + + computed_arg_elts = computed_args->elts; + + location = computed_arg_elts[0]; + + if (location.len == 0) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + if (computed_args->nelts > 1) { + url_args = &computed_arg_elts[1]; + } else { + url_args = NULL; + } + + args.data = NULL; + args.len = 0; + + if (ngx_http_parse_unsafe_uri(r, &location, &args, &flags) != NGX_OK) { + ctx->headers_sent = 1; + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + if (args.len > 0 && url_args == NULL) { + url_args = &args; + } + + rc = ngx_http_echo_send_header_if_needed(r, ctx); + + if (r->header_only || rc >= NGX_HTTP_SPECIAL_RESPONSE) { + return rc; + } + + psr = ngx_palloc(r->pool, sizeof(ngx_http_post_subrequest_t)); + + if (psr == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + psr->handler = ngx_http_echo_post_subrequest; + psr->data = ctx; + + rc = ngx_http_subrequest(r, &location, url_args, &sr, psr, 0); + + if (rc != NGX_OK) { + return NGX_ERROR; + } + + rc = ngx_http_echo_adjust_subrequest(sr); + + if (rc != NGX_OK) { + return NGX_ERROR; + } + + return NGX_AGAIN; +} + + +static ngx_int_t +ngx_http_echo_adjust_subrequest(ngx_http_request_t *sr) +{ + ngx_http_core_main_conf_t *cmcf; + ngx_http_request_t *r; + + + /* we do not inherit the parent request's variables */ + cmcf = ngx_http_get_module_main_conf(sr, ngx_http_core_module); + + r = sr->parent; + + sr->header_in = r->header_in; + + /* XXX work-around a bug in ngx_http_subrequest */ + if (r->headers_in.headers.last == &r->headers_in.headers.part) { + sr->headers_in.headers.last = &sr->headers_in.headers.part; + } + + sr->variables = ngx_pcalloc(sr->pool, cmcf->variables.nelts + * sizeof(ngx_http_variable_value_t)); + + if (sr->variables == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + return NGX_OK; +} + diff -Nru nginx-0.5.33/modules/nginx-echo/src/ngx_http_echo_location.h nginx-0.8.53/modules/nginx-echo/src/ngx_http_echo_location.h --- nginx-0.5.33/modules/nginx-echo/src/ngx_http_echo_location.h 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/modules/nginx-echo/src/ngx_http_echo_location.h 2011-02-26 14:04:40.000000000 +0000 @@ -0,0 +1,13 @@ +#ifndef ECHO_LOCATION_H +#define ECHO_LOCATION_H + +#include "ngx_http_echo_module.h" + +ngx_int_t ngx_http_echo_exec_echo_location_async(ngx_http_request_t *r, + ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args); + +ngx_int_t ngx_http_echo_exec_echo_location(ngx_http_request_t *r, + ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args); + +#endif /* ECHO_LOCATION_H */ + diff -Nru nginx-0.5.33/modules/nginx-echo/src/ngx_http_echo_module.c nginx-0.8.53/modules/nginx-echo/src/ngx_http_echo_module.c --- nginx-0.5.33/modules/nginx-echo/src/ngx_http_echo_module.c 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/modules/nginx-echo/src/ngx_http_echo_module.c 2011-02-26 14:04:40.000000000 +0000 @@ -0,0 +1,587 @@ +#define DDEBUG 0 +#include "ddebug.h" + +#include "ngx_http_echo_handler.h" +#include "ngx_http_echo_filter.h" +#include "ngx_http_echo_request_info.h" + +#include +#include +#include + +/* config init handler */ +static void * ngx_http_echo_create_conf(ngx_conf_t *cf); + +/* config directive handlers */ +static char * ngx_http_echo_echo(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); + +static char * ngx_http_echo_echo_request_body(ngx_conf_t *cf, + ngx_command_t *cmd, void *conf); + +static char * ngx_http_echo_echo_sleep(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); + +static char * ngx_http_echo_echo_flush(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); + +static char * ngx_http_echo_echo_blocking_sleep(ngx_conf_t *cf, + ngx_command_t *cmd, void *conf); + +static char * ngx_http_echo_echo_reset_timer(ngx_conf_t *cf, + ngx_command_t *cmd, void *conf); + +static char * ngx_http_echo_echo_before_body(ngx_conf_t *cf, + ngx_command_t *cmd, void *conf); + +static char * ngx_http_echo_echo_after_body(ngx_conf_t *cf, + ngx_command_t *cmd, void *conf); + +static char * ngx_http_echo_echo_location_async(ngx_conf_t *cf, + ngx_command_t *cmd, void *conf); + +static char * ngx_http_echo_echo_location(ngx_conf_t *cf, + ngx_command_t *cmd, void *conf); + +static char * ngx_http_echo_echo_subrequest_async(ngx_conf_t *cf, + ngx_command_t *cmd, void *conf); + +static char * ngx_http_echo_echo_subrequest(ngx_conf_t *cf, + ngx_command_t *cmd, void *conf); + +static char * ngx_http_echo_echo_duplicate(ngx_conf_t *cf, + ngx_command_t *cmd, void *conf); + +static char * ngx_http_echo_echo_read_request_body(ngx_conf_t *cf, + ngx_command_t *cmd, void *conf); + +static char * ngx_http_echo_echo_foreach_split(ngx_conf_t *cf, + ngx_command_t *cmd, void *conf); + +static char * ngx_http_echo_echo_end(ngx_conf_t *cf, + ngx_command_t *cmd, void *conf); + +static char * ngx_http_echo_echo_abort_parent(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); + +static char * ngx_http_echo_echo_exec(ngx_conf_t *cf, + ngx_command_t *cmd, void *conf); + +static char * ngx_http_echo_helper(ngx_http_echo_opcode_t opcode, + ngx_http_echo_cmd_category_t cat, + ngx_conf_t *cf, ngx_command_t *cmd, void* conf); + + +static ngx_http_module_t ngx_http_echo_module_ctx = { + /* TODO we could add our own variables here... */ + ngx_http_echo_handler_init, /* preconfiguration */ + ngx_http_echo_filter_init, /* postconfiguration */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + NULL, /* create server configuration */ + NULL, /* merge server configuration */ + + ngx_http_echo_create_conf, /* create location configuration */ + NULL /* merge location configuration */ +}; + + +static ngx_command_t ngx_http_echo_commands[] = { + + { ngx_string("echo"), + NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_ANY, + ngx_http_echo_echo, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_echo_loc_conf_t, handler_cmds), + NULL }, + + { ngx_string("echo_request_body"), + NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_NOARGS, + ngx_http_echo_echo_request_body, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_echo_loc_conf_t, handler_cmds), + NULL }, + + { ngx_string("echo_sleep"), + NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1, + ngx_http_echo_echo_sleep, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_echo_loc_conf_t, handler_cmds), + NULL }, + + { ngx_string("echo_flush"), + NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_NOARGS, + ngx_http_echo_echo_flush, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_echo_loc_conf_t, handler_cmds), + NULL }, + + { ngx_string("echo_blocking_sleep"), + NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1, + ngx_http_echo_echo_blocking_sleep, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_echo_loc_conf_t, handler_cmds), + NULL }, + + { ngx_string("echo_reset_timer"), + NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_NOARGS, + ngx_http_echo_echo_reset_timer, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_echo_loc_conf_t, handler_cmds), + NULL }, + + { ngx_string("echo_before_body"), + NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_ANY, + ngx_http_echo_echo_before_body, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_echo_loc_conf_t, before_body_cmds), + NULL }, + + { ngx_string("echo_after_body"), + NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_ANY, + ngx_http_echo_echo_after_body, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_echo_loc_conf_t, after_body_cmds), + NULL }, + + { ngx_string("echo_location_async"), + NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE12, + ngx_http_echo_echo_location_async, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("echo_location"), + NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE12, + ngx_http_echo_echo_location, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("echo_subrequest_async"), + NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_2MORE, + ngx_http_echo_echo_subrequest_async, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("echo_subrequest"), + NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_2MORE, + ngx_http_echo_echo_subrequest, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("echo_duplicate"), + NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_2MORE, + ngx_http_echo_echo_duplicate, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("echo_read_request_body"), + NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_NOARGS, + ngx_http_echo_echo_read_request_body, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_echo_loc_conf_t, handler_cmds), + NULL }, + + { ngx_string("echo_foreach_split"), + NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_2MORE, + ngx_http_echo_echo_foreach_split, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_echo_loc_conf_t, handler_cmds), + NULL }, + + { ngx_string("echo_end"), + NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_NOARGS, + ngx_http_echo_echo_end, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_echo_loc_conf_t, handler_cmds), + NULL }, + + { ngx_string("echo_abort_parent"), + NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_NOARGS, + ngx_http_echo_echo_abort_parent, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_echo_loc_conf_t, handler_cmds), + NULL }, + + { ngx_string("echo_exec"), + NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE12, + ngx_http_echo_echo_exec, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_echo_loc_conf_t, handler_cmds), + NULL }, + + ngx_null_command +}; + + +ngx_module_t ngx_http_echo_module = { + NGX_MODULE_V1, + &ngx_http_echo_module_ctx, /* module context */ + ngx_http_echo_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 void * +ngx_http_echo_create_conf(ngx_conf_t *cf) +{ + ngx_http_echo_loc_conf_t *conf; + conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_echo_loc_conf_t)); + if (conf == NULL) { + return NGX_CONF_ERROR; + } + + return conf; +} + + +static char * +ngx_http_echo_helper(ngx_http_echo_opcode_t opcode, + ngx_http_echo_cmd_category_t cat, + ngx_conf_t *cf, ngx_command_t *cmd, void* conf) +{ + ngx_http_core_loc_conf_t *clcf; + /* ngx_http_echo_loc_conf_t *ulcf = conf; */ + ngx_array_t **args_ptr; + ngx_http_script_compile_t sc; + ngx_str_t *raw_args; + ngx_http_echo_arg_template_t *arg; + ngx_array_t **cmds_ptr; + ngx_http_echo_cmd_t *echo_cmd; + ngx_uint_t i, n; + + /* cmds_ptr points to ngx_http_echo_loc_conf_t's + * handler_cmds, before_body_cmds, or after_body_cmds + * array, depending on the actual offset */ + cmds_ptr = (ngx_array_t**)(((u_char*)conf) + cmd->offset); + + if (*cmds_ptr == NULL) { + *cmds_ptr = ngx_array_create(cf->pool, 1, + sizeof(ngx_http_echo_cmd_t)); + + if (*cmds_ptr == NULL) { + return NGX_CONF_ERROR; + } + + if (cat == echo_handler_cmd) { + dd("registering the content handler"); + /* register the content handler */ + clcf = ngx_http_conf_get_module_loc_conf(cf, + ngx_http_core_module); + + dd("registering the content handler (2)"); + clcf->handler = ngx_http_echo_handler; + + } else { + dd("filter used = 1"); + ngx_http_echo_filter_used = 1; + } + } + + echo_cmd = ngx_array_push(*cmds_ptr); + + if (echo_cmd == NULL) { + return NGX_CONF_ERROR; + } + + echo_cmd->opcode = opcode; + + args_ptr = &echo_cmd->args; + *args_ptr = ngx_array_create(cf->pool, 1, + sizeof(ngx_http_echo_arg_template_t)); + + if (*args_ptr == NULL) { + return NGX_CONF_ERROR; + } + + raw_args = cf->args->elts; + + /* we skip the first arg and start from the second */ + + for (i = 1 ; i < cf->args->nelts; i++) { + arg = ngx_array_push(*args_ptr); + + if (arg == NULL) { + return NGX_CONF_ERROR; + } + + arg->raw_value = raw_args[i]; + + dd("found raw arg %s", raw_args[i].data); + + arg->lengths = NULL; + arg->values = NULL; + + n = ngx_http_script_variables_count(&arg->raw_value); + + if (n > 0) { + ngx_memzero(&sc, sizeof(ngx_http_script_compile_t)); + + sc.cf = cf; + sc.source = &arg->raw_value; + sc.lengths = &arg->lengths; + sc.values = &arg->values; + sc.variables = n; + sc.complete_lengths = 1; + sc.complete_values = 1; + + if (ngx_http_script_compile(&sc) != NGX_OK) { + return NGX_CONF_ERROR; + } + } + } /* end for */ + + return NGX_CONF_OK; +} + + +static char * +ngx_http_echo_echo(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + dd("in echo_echo..."); + return ngx_http_echo_helper(echo_opcode_echo, + echo_handler_cmd, + cf, cmd, conf); +} + + +static char * +ngx_http_echo_echo_request_body(ngx_conf_t *cf, + ngx_command_t *cmd, void *conf) +{ + dd("in echo_echo_request_body..."); + return ngx_http_echo_helper(echo_opcode_echo_request_body, + echo_handler_cmd, + cf, cmd, conf); +} + + +static char * +ngx_http_echo_echo_sleep(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + dd("in echo_sleep..."); + return ngx_http_echo_helper(echo_opcode_echo_sleep, + echo_handler_cmd, + cf, cmd, conf); +} + + +static char * +ngx_http_echo_echo_flush(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + dd("in echo_flush..."); + return ngx_http_echo_helper(echo_opcode_echo_flush, + echo_handler_cmd, + cf, cmd, conf); +} + + +static char * +ngx_http_echo_echo_blocking_sleep(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + dd("in echo_blocking_sleep..."); + return ngx_http_echo_helper(echo_opcode_echo_blocking_sleep, + echo_handler_cmd, + cf, cmd, conf); +} + + +static char * +ngx_http_echo_echo_reset_timer(ngx_conf_t *cf, + ngx_command_t *cmd, void *conf) +{ + return ngx_http_echo_helper(echo_opcode_echo_reset_timer, + echo_handler_cmd, + cf, cmd, conf); +} + + +static char * +ngx_http_echo_echo_before_body(ngx_conf_t *cf, + ngx_command_t *cmd, void *conf) +{ + dd("processing echo_before_body directive..."); + return ngx_http_echo_helper(echo_opcode_echo_before_body, + echo_filter_cmd, + cf, cmd, conf); +} + + +static char * +ngx_http_echo_echo_after_body(ngx_conf_t *cf, + ngx_command_t *cmd, void *conf) +{ + return ngx_http_echo_helper(echo_opcode_echo_after_body, + echo_filter_cmd, + cf, cmd, conf); +} + + +static char * + ngx_http_echo_echo_location_async(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf) +{ + +#if ! defined(nginx_version) + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "Directive echo_location_async does not work with nginx " + "versions ealier than 0.7.46."); + + return NGX_CONF_ERROR; + +#else + + return ngx_http_echo_helper(echo_opcode_echo_location_async, + echo_handler_cmd, + cf, cmd, conf); + +#endif + +} + + +static char * + ngx_http_echo_echo_location(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf) +{ + +#if ! defined(nginx_version) + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "Directive echo_location does not work with nginx " + "versions ealier than 0.7.46."); + + return NGX_CONF_ERROR; + +#else + + return ngx_http_echo_helper(echo_opcode_echo_location, + echo_handler_cmd, + cf, cmd, conf); + +#endif + +} + + +static char * + ngx_http_echo_echo_subrequest_async(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf) +{ + +#if ! defined(nginx_version) + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "Directive echo_subrequest_async does not work with nginx " + "versions ealier than 0.7.46."); + + return NGX_CONF_ERROR; + +#else + + return ngx_http_echo_helper(echo_opcode_echo_subrequest_async, + echo_handler_cmd, + cf, cmd, conf); + +#endif + +} + + +static char * + ngx_http_echo_echo_subrequest(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf) +{ + +#if ! defined(nginx_version) + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "Directive echo_subrequest does not work with nginx " + "versions ealier than 0.7.46."); + + return NGX_CONF_ERROR; + +#else + + return ngx_http_echo_helper(echo_opcode_echo_subrequest, + echo_handler_cmd, + cf, cmd, conf); + +#endif + +} + + +static char * +ngx_http_echo_echo_duplicate(ngx_conf_t *cf, + ngx_command_t *cmd, void *conf) +{ + return ngx_http_echo_helper(echo_opcode_echo_duplicate, + echo_handler_cmd, + cf, cmd, conf); +} + + +static char * +ngx_http_echo_echo_read_request_body(ngx_conf_t *cf, + ngx_command_t *cmd, void *conf) +{ + return ngx_http_echo_helper( + echo_opcode_echo_read_request_body, + echo_handler_cmd, + cf, cmd, conf); +} + + +static char * +ngx_http_echo_echo_foreach_split(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + return ngx_http_echo_helper( + echo_opcode_echo_foreach_split, + echo_handler_cmd, + cf, cmd, conf); +} + + +static char * +ngx_http_echo_echo_end(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + return ngx_http_echo_helper( + echo_opcode_echo_end, + echo_handler_cmd, + cf, cmd, conf); +} + + +static char * +ngx_http_echo_echo_abort_parent(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + return ngx_http_echo_helper( + echo_opcode_echo_abort_parent, + echo_handler_cmd, + cf, cmd, conf); +} + + +static char * +ngx_http_echo_echo_exec(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + return ngx_http_echo_helper( + echo_opcode_echo_exec, + echo_handler_cmd, + cf, cmd, conf); +} + diff -Nru nginx-0.5.33/modules/nginx-echo/src/ngx_http_echo_module.h nginx-0.8.53/modules/nginx-echo/src/ngx_http_echo_module.h --- nginx-0.5.33/modules/nginx-echo/src/ngx_http_echo_module.h 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/modules/nginx-echo/src/ngx_http_echo_module.h 2011-02-26 14:04:40.000000000 +0000 @@ -0,0 +1,118 @@ +/* Copyright (C) by agentzh */ + +#ifndef NGX_HTTP_ECHO_MODULE_H +#define NGX_HTTP_ECHO_MODULE_H + +#include +#include +#include + +extern ngx_module_t ngx_http_echo_module; + +/* config directive's opcode */ +typedef enum { + echo_opcode_echo, + echo_opcode_echo_request_body, + echo_opcode_echo_sleep, + echo_opcode_echo_flush, + echo_opcode_echo_blocking_sleep, + echo_opcode_echo_reset_timer, + echo_opcode_echo_before_body, + echo_opcode_echo_after_body, + echo_opcode_echo_location_async, + echo_opcode_echo_location, + echo_opcode_echo_subrequest_async, + echo_opcode_echo_subrequest, + echo_opcode_echo_duplicate, + echo_opcode_echo_read_request_body, + echo_opcode_echo_foreach_split, + echo_opcode_echo_end, + echo_opcode_echo_abort_parent, + echo_opcode_echo_exec +} ngx_http_echo_opcode_t; + +/* all the various config directives (or commands) are + * divided into two categories: "handler commands", + * and "filter commands". For instance, the "echo" + * directive is a handler command while + * "echo_before_body" is a filter one. */ +typedef enum { + echo_handler_cmd, + echo_filter_cmd + +} ngx_http_echo_cmd_category_t; + +/* compiled form of a config directive argument's value */ +typedef struct { + /* holds the raw string of the argument value */ + ngx_str_t raw_value; + + /* fields "lengths" and "values" are set by + * the function ngx_http_script_compile, + * iff the argument value indeed contains + * nginx variables like "$foo" */ + ngx_array_t *lengths; + ngx_array_t *values; + +} ngx_http_echo_arg_template_t; + +/* represent a config directive (or command) like "echo". */ +typedef struct { + ngx_http_echo_opcode_t opcode; + + /* each argument is of type echo_arg_template_t: */ + ngx_array_t *args; +} ngx_http_echo_cmd_t; + +/* location config struct */ +typedef struct { + /* elements of the following arrays are of type + * ngx_http_echo_cmd_t */ + ngx_array_t *handler_cmds; + ngx_array_t *before_body_cmds; + ngx_array_t *after_body_cmds; + +} ngx_http_echo_loc_conf_t; + +typedef struct { + ngx_array_t *choices; /* items after splitting */ + ngx_uint_t next_choice; /* current item index */ + ngx_uint_t cmd_index; /* cmd index for the echo_foreach direcitve */ +} ngx_http_echo_foreach_ctx_t; + +/* context struct in the request handling cycle, holding + * the current states of the command evaluator */ +typedef struct { + /* index of the next handler command in + * ngx_http_echo_loc_conf_t's "handler_cmds" array. */ + ngx_uint_t next_handler_cmd; + + /* index of the next before-body filter command in + * ngx_http_echo_loc_conf_t's "before_body_cmds" array. */ + ngx_uint_t next_before_body_cmd; + + /* index of the next after-body filter command in + * ngx_http_echo_loc_conf_t's "after_body_cmds" array. */ + ngx_uint_t next_after_body_cmd; + + ngx_http_echo_foreach_ctx_t *foreach; + + ngx_flag_t headers_sent; + ngx_flag_t before_body_sent; + ngx_flag_t skip_filter; + + ngx_time_t timer_begin; + + ngx_event_t sleep; + + ngx_uint_t counter; + + ngx_flag_t wait_read_request_body; + + ngx_flag_t waiting; + ngx_flag_t done; +} ngx_http_echo_ctx_t; + + +#endif /* NGX_HTTP_ECHO_MODULE_H */ + diff -Nru nginx-0.5.33/modules/nginx-echo/src/ngx_http_echo_request_info.c nginx-0.8.53/modules/nginx-echo/src/ngx_http_echo_request_info.c --- nginx-0.5.33/modules/nginx-echo/src/ngx_http_echo_request_info.c 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/modules/nginx-echo/src/ngx_http_echo_request_info.c 2011-02-26 14:04:40.000000000 +0000 @@ -0,0 +1,269 @@ +#define DDEBUG 0 +#include "ddebug.h" + +#include "ngx_http_echo_request_info.h" +#include "ngx_http_echo_util.h" +#include "ngx_http_echo_handler.h" + +#include + + +static void ngx_http_echo_post_read_request_body(ngx_http_request_t *r); + +ngx_int_t +ngx_http_echo_exec_echo_read_request_body( + ngx_http_request_t* r, ngx_http_echo_ctx_t *ctx) +{ + return ngx_http_read_client_request_body(r, ngx_http_echo_post_read_request_body); +} + + +static void +ngx_http_echo_post_read_request_body(ngx_http_request_t *r) +{ + ngx_http_echo_ctx_t *ctx; + + ctx = ngx_http_get_module_ctx(r, ngx_http_echo_module); + + dd("wait read request body %d", (int) ctx->wait_read_request_body); + + if (ctx->wait_read_request_body) { + ctx->waiting = 0; + ctx->done = 1; + + r->write_event_handler = ngx_http_echo_wev_handler; + + ngx_http_echo_wev_handler(r); + } +} + + +/* this function's implementation is borrowed from nginx 0.8.20 + * and modified a bit to work with subrequests. + * Copyrighted (C) by Igor Sysoev */ +ngx_int_t +ngx_http_echo_request_method_variable(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + if (r->method_name.data) { + v->len = r->method_name.len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = r->method_name.data; + } else { + v->not_found = 1; + } + + return NGX_OK; +} + + +/* this function's implementation is borrowed from nginx 0.8.20 + * and modified a bit to work with subrequests. + * Copyrighted (C) by Igor Sysoev */ +ngx_int_t +ngx_http_echo_client_request_method_variable(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; +} + + +/* this function's implementation is borrowed from nginx 0.8.20 + * and modified a bit to work with subrequests. + * Copyrighted (C) by Igor Sysoev */ +ngx_int_t +ngx_http_echo_request_body_variable(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + u_char *p; + size_t len; + ngx_buf_t *buf, *next; + ngx_chain_t *cl; + +#if 0 + + dd("rrr request_body null ? %d", r->request_body == NULL); + if (r->request_body) { + dd("rrr request_body bufs null ? %d", r->request_body->bufs == NULL); + dd("rrr request_body temp file ? %d", r->request_body->temp_file != NULL); + } + dd("rrr request_body content length ? %ld", (long) r->headers_in.content_length_n); + +#endif + + 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; + } + + next = cl->next->buf; + len = (buf->last - buf->pos) + (next->last - next->pos); + + p = ngx_pnalloc(r->pool, len); + if (p == NULL) { + return NGX_ERROR; + } + + v->data = p; + + p = ngx_cpymem(p, buf->pos, buf->last - buf->pos); + ngx_memcpy(p, next->pos, next->last - next->pos); + + v->len = len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + + return NGX_OK; +} + + +ngx_int_t +ngx_http_echo_client_request_headers_variable(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + size_t size; + u_char *p, *last; + ngx_buf_t *header_in; + ngx_flag_t just_seen_crlf; + + if (r != r->main) { + header_in = r->main->header_in; + } else { + header_in = r->header_in; + } + + if (header_in == NULL) { + v->not_found = 1; + return NGX_OK; + } + + size = header_in->pos - header_in->start; + + v->data = ngx_palloc(r->pool, size); + last = ngx_cpymem(v->data, header_in->start, size); + + /* fix \0 introduced by the nginx header parser and + * locate the end of the header */ + just_seen_crlf = 0; + for (p = (u_char*)v->data; p != last; p++) { + if (*p == '\0') { + if (p + 1 != last && *(p + 1) == LF) { + just_seen_crlf = 1; + *p = CR; + } else { + *p = ':'; + just_seen_crlf = 0; + } + } else if (*p == CR) { + if (just_seen_crlf) { + *p = '\0'; + last = p; + break; + } + } else if (*p != LF) { + just_seen_crlf = 0; + } + } + + v->len = last - v->data; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + + return NGX_OK; +} + + +ngx_int_t +ngx_http_echo_cacheable_request_uri_variable(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + if (r->uri.len) { + v->len = r->uri.len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = r->uri.data; + } else { + v->not_found = 1; + } + + return NGX_OK; +} + + +ngx_int_t +ngx_http_echo_request_uri_variable(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + if (r->uri.len) { + v->len = r->uri.len; + v->valid = 1; + v->no_cacheable = 1; + v->not_found = 0; + v->data = r->uri.data; + } else { + v->not_found = 1; + } + + return NGX_OK; +} + + +ngx_int_t +ngx_http_echo_response_status_variable(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + u_char *p; + + if (r->headers_out.status) { + dd("headers out status: %d", (int) r->headers_out.status); + + p = ngx_palloc(r->pool, NGX_INT_T_LEN); + if (p == NULL) { + return NGX_ERROR; + } + + v->len = ngx_sprintf(p, "%ui", r->headers_out.status) - p; + v->data = p; + + v->valid = 1; + v->no_cacheable = 1; + v->not_found = 0; + } else { + v->not_found = 1; + } + + return NGX_OK; +} + diff -Nru nginx-0.5.33/modules/nginx-echo/src/ngx_http_echo_request_info.h nginx-0.8.53/modules/nginx-echo/src/ngx_http_echo_request_info.h --- nginx-0.5.33/modules/nginx-echo/src/ngx_http_echo_request_info.h 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/modules/nginx-echo/src/ngx_http_echo_request_info.h 2011-02-26 14:04:40.000000000 +0000 @@ -0,0 +1,31 @@ +#ifndef ECHO_REQUEST_INFO_H +#define ECHO_REQUEST_INFO_H + +#include "ngx_http_echo_module.h" + +ngx_int_t ngx_http_echo_exec_echo_read_request_body( + ngx_http_request_t* r, ngx_http_echo_ctx_t *ctx); + +ngx_int_t ngx_http_echo_request_method_variable(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); + +ngx_int_t ngx_http_echo_client_request_method_variable(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); + +ngx_int_t ngx_http_echo_request_body_variable(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); + +ngx_int_t ngx_http_echo_client_request_headers_variable(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); + +ngx_int_t ngx_http_echo_cacheable_request_uri_variable(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); + +ngx_int_t ngx_http_echo_request_uri_variable(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); + +ngx_int_t ngx_http_echo_response_status_variable(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); + +#endif /* ECHO_REQUEST_INFO_H */ + diff -Nru nginx-0.5.33/modules/nginx-echo/src/ngx_http_echo_sleep.c nginx-0.8.53/modules/nginx-echo/src/ngx_http_echo_sleep.c --- nginx-0.5.33/modules/nginx-echo/src/ngx_http_echo_sleep.c 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/modules/nginx-echo/src/ngx_http_echo_sleep.c 2011-02-26 14:04:40.000000000 +0000 @@ -0,0 +1,200 @@ +/* Copyright (C) agentzh */ + +#define DDEBUG 0 +#include "ddebug.h" + +#include "ngx_http_echo_sleep.h" +#include "ngx_http_echo_handler.h" + +#include +#include + +/* event handler for echo_sleep */ + +static void ngx_http_echo_post_sleep(ngx_http_request_t *r); + +static void ngx_http_echo_sleep_cleanup(void *data); + + +ngx_int_t +ngx_http_echo_exec_echo_sleep( + ngx_http_request_t *r, ngx_http_echo_ctx_t *ctx, + ngx_array_t *computed_args) +{ + ngx_str_t *computed_arg; + ngx_str_t *computed_arg_elts; + float delay; /* in sec */ + ngx_http_cleanup_t *cln; + + computed_arg_elts = computed_args->elts; + computed_arg = &computed_arg_elts[0]; + + delay = atof( (char*) computed_arg->data ); + + if (delay < 0.001) { /* should be bigger than 1 msec */ + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "invalid sleep duration \"%V\"", &computed_arg_elts[0]); + + return NGX_HTTP_BAD_REQUEST; + } + + dd("DELAY = %.02lf sec", delay); + + ngx_add_timer(&ctx->sleep, (ngx_msec_t) (1000 * delay)); + + /* we don't check broken downstream connections + * ourselves so even if the client shuts down + * the connection prematurely, nginx will still + * go on waiting for our timers to get properly + * expired. However, we'd still register a + * cleanup handler for completeness. */ + + cln = ngx_http_cleanup_add(r, 0); + if (cln == NULL) { + return NGX_ERROR; + } + + cln->handler = ngx_http_echo_sleep_cleanup; + cln->data = r; + + return NGX_AGAIN; +} + + +static void +ngx_http_echo_post_sleep(ngx_http_request_t *r) +{ + ngx_http_echo_ctx_t *ctx; + /* ngx_int_t rc; */ + + dd("entered echo post sleep...(r->done: %d)", r->done); + + dd("sleep: before get module ctx"); + + ctx = ngx_http_get_module_ctx(r, ngx_http_echo_module); + + if (ctx == NULL) { + return; + } + + ctx->waiting = 0; + ctx->done = 1; + + dd("sleep: after get module ctx"); + + dd("timed out? %d", ctx->sleep.timedout); + dd("timer set? %d", ctx->sleep.timer_set); + + if ( ! ctx->sleep.timedout ) { + dd("HERE reached!"); + return; + } + + ctx->sleep.timedout = 0; + + if (ctx->sleep.timer_set) { + dd("deleting timer for echo_sleep"); + + ngx_del_timer(&ctx->sleep); + } + + /* r->write_event_handler = ngx_http_request_empty_handler; */ + + ngx_http_echo_wev_handler(r); +} + + +void +ngx_http_echo_sleep_event_handler(ngx_event_t *ev) +{ + ngx_connection_t *c; + ngx_http_request_t *r; + ngx_http_log_ctx_t *ctx; + + r = ev->data; + c = r->connection; + + if (c->destroyed) { + return; + } + + ctx = c->log->data; + ctx->current_request = r; + + /* XXX when r->done == 1 we should do cleaning immediately + * and delete our timer and then quit. */ + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "echo sleep handler: \"%V?%V\"", &r->uri, &r->args); + + /* + if (r->done) { + return; + } + */ + + ngx_http_echo_post_sleep(r); + +#if defined(nginx_version) + + dd("before run posted requests"); + + ngx_http_run_posted_requests(c); + + dd("after run posted requests"); + +#endif + +} + + +ngx_int_t +ngx_http_echo_exec_echo_blocking_sleep(ngx_http_request_t *r, + ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args) +{ + ngx_str_t *computed_arg; + ngx_str_t *computed_arg_elts; + float delay; /* in sec */ + + computed_arg_elts = computed_args->elts; + computed_arg = &computed_arg_elts[0]; + + delay = atof( (char*) computed_arg->data ); + + if (delay < 0.001) { /* should be bigger than 1 msec */ + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "invalid sleep duration \"%V\"", &computed_arg_elts[0]); + return NGX_HTTP_BAD_REQUEST; + } + + dd("blocking DELAY = %.02lf sec", delay); + + ngx_msleep((ngx_msec_t) (1000 * delay)); + + return NGX_OK; +} + + +static void +ngx_http_echo_sleep_cleanup(void *data) +{ + ngx_http_request_t *r = data; + ngx_http_echo_ctx_t *ctx; + + dd("echo sleep cleanup"); + + ctx = ngx_http_get_module_ctx(r, ngx_http_echo_module); + if (ctx == NULL) { + return; + } + + if (ctx->sleep.timer_set) { + dd("cleanup: deleting timer for echo_sleep"); + + ngx_del_timer(&ctx->sleep); + return; + } + + dd("cleanup: timer not set"); +} + diff -Nru nginx-0.5.33/modules/nginx-echo/src/ngx_http_echo_sleep.h nginx-0.8.53/modules/nginx-echo/src/ngx_http_echo_sleep.h --- nginx-0.5.33/modules/nginx-echo/src/ngx_http_echo_sleep.h 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/modules/nginx-echo/src/ngx_http_echo_sleep.h 2011-02-26 14:04:40.000000000 +0000 @@ -0,0 +1,16 @@ +#ifndef ECHO_SLEEP_H +#define ECHO_SLEEP_H + +#include "ngx_http_echo_module.h" + +ngx_int_t ngx_http_echo_exec_echo_sleep( + ngx_http_request_t *r, ngx_http_echo_ctx_t *ctx, + ngx_array_t *computed_args); + +ngx_int_t ngx_http_echo_exec_echo_blocking_sleep(ngx_http_request_t *r, + ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args); + +void ngx_http_echo_sleep_event_handler(ngx_event_t *ev); + +#endif /* ECHO_SLEEP_H */ + diff -Nru nginx-0.5.33/modules/nginx-echo/src/ngx_http_echo_subrequest.c nginx-0.8.53/modules/nginx-echo/src/ngx_http_echo_subrequest.c --- nginx-0.5.33/modules/nginx-echo/src/ngx_http_echo_subrequest.c 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/modules/nginx-echo/src/ngx_http_echo_subrequest.c 2011-02-26 14:04:40.000000000 +0000 @@ -0,0 +1,606 @@ +#define DDEBUG 0 +#include "ddebug.h" + +#include "ngx_http_echo_util.h" +#include "ngx_http_echo_subrequest.h" +#include "ngx_http_echo_handler.h" + +#define ngx_http_echo_method_name(m) { sizeof(m) - 1, (u_char *) m " " } + +ngx_str_t ngx_http_echo_content_length_header_key = ngx_string("Content-Length"); + +ngx_str_t ngx_http_echo_get_method = ngx_http_echo_method_name("GET"); +ngx_str_t ngx_http_echo_put_method = ngx_http_echo_method_name("PUT"); +ngx_str_t ngx_http_echo_post_method = ngx_http_echo_method_name("POST"); +ngx_str_t ngx_http_echo_head_method = ngx_http_echo_method_name("HEAD"); +ngx_str_t ngx_http_echo_copy_method = ngx_http_echo_method_name("COPY"); +ngx_str_t ngx_http_echo_move_method = ngx_http_echo_method_name("MOVE"); +ngx_str_t ngx_http_echo_lock_method = ngx_http_echo_method_name("LOCK"); +ngx_str_t ngx_http_echo_mkcol_method = ngx_http_echo_method_name("MKCOL"); +ngx_str_t ngx_http_echo_trace_method = ngx_http_echo_method_name("TRACE"); +ngx_str_t ngx_http_echo_delete_method = ngx_http_echo_method_name("DELETE"); +ngx_str_t ngx_http_echo_unlock_method = ngx_http_echo_method_name("UNLOCK"); +ngx_str_t ngx_http_echo_options_method = ngx_http_echo_method_name("OPTIONS"); +ngx_str_t ngx_http_echo_propfind_method = ngx_http_echo_method_name("PROPFIND"); +ngx_str_t ngx_http_echo_proppatch_method = ngx_http_echo_method_name("PROPPATCH"); + + +typedef struct ngx_http_echo_subrequest_s { + ngx_uint_t method; + ngx_str_t *method_name; + ngx_str_t *location; + ngx_str_t *query_string; + ssize_t content_length_n; + ngx_http_request_body_t *request_body; +} ngx_http_echo_subrequest_t; + + +static ngx_int_t ngx_http_echo_parse_method_name(ngx_str_t **method_name_ptr); + +static ngx_int_t ngx_http_echo_adjust_subrequest(ngx_http_request_t *sr, + ngx_http_echo_subrequest_t *parsed_sr); + +static ngx_int_t ngx_http_echo_parse_subrequest_spec(ngx_http_request_t *r, + ngx_array_t *computed_args, ngx_http_echo_subrequest_t **parsed_sr_ptr); + + +ngx_int_t +ngx_http_echo_exec_echo_subrequest_async(ngx_http_request_t *r, + ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args) +{ + ngx_int_t rc; + ngx_http_echo_subrequest_t *parsed_sr; + ngx_http_request_t *sr; /* subrequest object */ + ngx_str_t args; + ngx_uint_t flags = 0; + + + dd_enter(); + + rc = ngx_http_echo_parse_subrequest_spec(r, computed_args, &parsed_sr); + if (rc != NGX_OK) { + return rc; + } + + dd("location: %s", parsed_sr->location->data); + dd("location args: %s", (char*) (parsed_sr->query_string ? + parsed_sr->query_string->data : (u_char*)"NULL")); + + args.data = NULL; + args.len = 0; + + if (ngx_http_parse_unsafe_uri(r, parsed_sr->location, &args, &flags) + != NGX_OK) + { + ctx->headers_sent = 1; + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + if (args.len > 0 && parsed_sr->query_string == NULL) { + parsed_sr->query_string = &args; + } + + rc = ngx_http_echo_send_header_if_needed(r, ctx); + if (r->header_only || rc >= NGX_HTTP_SPECIAL_RESPONSE) { + return rc; + } + + rc = ngx_http_subrequest(r, parsed_sr->location, parsed_sr->query_string, + &sr, NULL, 0); + + if (rc != NGX_OK) { + return NGX_ERROR; + } + + rc = ngx_http_echo_adjust_subrequest(sr, parsed_sr); + + if (rc != NGX_OK) { + return rc; + } + + return NGX_OK; +} + + +ngx_int_t +ngx_http_echo_exec_echo_subrequest(ngx_http_request_t *r, + ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args) +{ + ngx_int_t rc; + ngx_http_request_t *sr; /* subrequest object */ + ngx_http_post_subrequest_t *psr; + ngx_http_echo_subrequest_t *parsed_sr; + ngx_str_t args; + ngx_uint_t flags = 0; + + + dd_enter(); + + rc = ngx_http_echo_parse_subrequest_spec(r, computed_args, &parsed_sr); + if (rc != NGX_OK) { + return rc; + } + + args.data = NULL; + args.len = 0; + + if (ngx_http_parse_unsafe_uri(r, parsed_sr->location, &args, &flags) + != NGX_OK) { + ctx->headers_sent = 1; + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + if (args.len > 0 && parsed_sr->query_string == NULL) { + parsed_sr->query_string = &args; + } + + rc = ngx_http_echo_send_header_if_needed(r, ctx); + + if (r->header_only || rc >= NGX_HTTP_SPECIAL_RESPONSE) { + return rc; + } + + psr = ngx_palloc(r->pool, sizeof(ngx_http_post_subrequest_t)); + + if (psr == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + psr->handler = ngx_http_echo_post_subrequest; + psr->data = ctx; + + rc = ngx_http_subrequest(r, parsed_sr->location, parsed_sr->query_string, + &sr, psr, 0); + + if (rc != NGX_OK) { + return NGX_ERROR; + } + + rc = ngx_http_echo_adjust_subrequest(sr, parsed_sr); + + if (rc != NGX_OK) { + return NGX_ERROR; + } + + return NGX_AGAIN; +} + + +static ngx_int_t +ngx_http_echo_parse_subrequest_spec(ngx_http_request_t *r, + ngx_array_t *computed_args, ngx_http_echo_subrequest_t **parsed_sr_ptr) +{ + ngx_str_t *computed_arg_elts, *arg; + ngx_str_t **to_write = NULL; + ngx_str_t *method_name; + ngx_str_t *body_str = NULL; + ngx_uint_t i; + ngx_flag_t expecting_opt; + ngx_http_request_body_t *rb = NULL; + ngx_buf_t *b; + ngx_http_echo_subrequest_t *parsed_sr; + + *parsed_sr_ptr = ngx_pcalloc(r->pool, sizeof(ngx_http_echo_subrequest_t)); + if (*parsed_sr_ptr == NULL) { + return NGX_ERROR; + } + + parsed_sr = *parsed_sr_ptr; + + computed_arg_elts = computed_args->elts; + + method_name = &computed_arg_elts[0]; + + parsed_sr->location = &computed_arg_elts[1]; + + if (parsed_sr->location->len == 0) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + expecting_opt = 1; + for (i = 2; i < computed_args->nelts; i++) { + arg = &computed_arg_elts[i]; + if (!expecting_opt) { + if (to_write == NULL) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "echo_subrequest_async: to_write should NOT be NULL"); + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + *to_write = arg; + to_write = NULL; + + expecting_opt = 1; + + continue; + } + + if (arg->len == 2) { + if (ngx_strncmp("-q", arg->data, arg->len) == 0) { + to_write = &parsed_sr->query_string; + expecting_opt = 0; + continue; + } + + if (ngx_strncmp("-b", arg->data, arg->len) == 0) { + to_write = &body_str; + expecting_opt = 0; + continue; + } + } + + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "Unknown option for echo_subrequest_async: %V", arg); + + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + if (body_str != NULL && body_str->len != 0) { + rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t)); + + if (rb == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + parsed_sr->content_length_n = body_str->len; + + b = ngx_calloc_buf(r->pool); + if (b == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + b->temporary = 1; + /* b->memory = 1; */ + b->start = b->pos = body_str->data; + b->end = b->last = body_str->data + body_str->len; + + rb->bufs = ngx_alloc_chain_link(r->pool); + if (rb->bufs == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + rb->bufs->buf = b; + rb->bufs->next = NULL; + + rb->buf = b; + } + + parsed_sr->request_body = rb; + + parsed_sr->method = ngx_http_echo_parse_method_name(&method_name); + parsed_sr->method_name = method_name; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_echo_adjust_subrequest(ngx_http_request_t *sr, + ngx_http_echo_subrequest_t *parsed_sr) +{ + ngx_table_elt_t *h; + ngx_http_core_main_conf_t *cmcf; + ngx_http_request_t *r; + + sr->method = parsed_sr->method; + sr->method_name = *(parsed_sr->method_name); + + r = sr->parent; + + sr->header_in = r->header_in; + + /* XXX work-around a bug in ngx_http_subrequest */ + if (r->headers_in.headers.last == &r->headers_in.headers.part) { + sr->headers_in.headers.last = &sr->headers_in.headers.part; + } + + /* we do not inherit the parent request's variables */ + cmcf = ngx_http_get_module_main_conf(sr, ngx_http_core_module); + sr->variables = ngx_pcalloc(sr->pool, cmcf->variables.nelts + * sizeof(ngx_http_variable_value_t)); + + if (sr->variables == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + if (parsed_sr->content_length_n > 0) { + sr->headers_in.content_length_n = parsed_sr->content_length_n; + sr->request_body = parsed_sr->request_body; + + sr->headers_in.content_length = ngx_pcalloc(sr->pool, + sizeof(ngx_table_elt_t)); + sr->headers_in.content_length->value.data = + ngx_palloc(sr->pool, NGX_OFF_T_LEN); + if (sr->headers_in.content_length->value.data == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + sr->headers_in.content_length->value.len = ngx_sprintf( + sr->headers_in.content_length->value.data, "%O", + sr->headers_in.content_length_n) - + sr->headers_in.content_length->value.data; + + if (ngx_list_init(&sr->headers_in.headers, sr->pool, 20, + sizeof(ngx_table_elt_t)) != NGX_OK) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + h = ngx_list_push(&sr->headers_in.headers); + if (h == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + h->hash = sr->header_hash; + + h->key = ngx_http_echo_content_length_header_key; + h->value = sr->headers_in.content_length->value; + + h->lowcase_key = ngx_pnalloc(sr->pool, h->key.len); + if (h->lowcase_key == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + ngx_strlow(h->lowcase_key, h->key.data, h->key.len); + + dd("sr content length: %s", sr->headers_in.content_length->value.data); + } + + dd("subrequest body: %p", sr->request_body); + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_echo_parse_method_name(ngx_str_t **method_name_ptr) +{ + const ngx_str_t* method_name = *method_name_ptr; + + switch (method_name->len) { + case 3: + if (ngx_http_echo_strcmp_const(method_name->data, "GET") == 0) { + *method_name_ptr = &ngx_http_echo_get_method; + return NGX_HTTP_GET; + break; + } + + if (ngx_http_echo_strcmp_const(method_name->data, "PUT") == 0) { + *method_name_ptr = &ngx_http_echo_put_method; + return NGX_HTTP_PUT; + break; + } + + return NGX_HTTP_UNKNOWN; + break; + + case 4: + if (ngx_http_echo_strcmp_const(method_name->data, "POST") == 0) { + *method_name_ptr = &ngx_http_echo_post_method; + return NGX_HTTP_POST; + break; + } + if (ngx_http_echo_strcmp_const(method_name->data, "HEAD") == 0) { + *method_name_ptr = &ngx_http_echo_head_method; + return NGX_HTTP_HEAD; + break; + } + if (ngx_http_echo_strcmp_const(method_name->data, "COPY") == 0) { + *method_name_ptr = &ngx_http_echo_copy_method; + return NGX_HTTP_COPY; + break; + } + if (ngx_http_echo_strcmp_const(method_name->data, "MOVE") == 0) { + *method_name_ptr = &ngx_http_echo_move_method; + return NGX_HTTP_MOVE; + break; + } + if (ngx_http_echo_strcmp_const(method_name->data, "LOCK") == 0) { + *method_name_ptr = &ngx_http_echo_lock_method; + return NGX_HTTP_LOCK; + break; + } + return NGX_HTTP_UNKNOWN; + break; + + case 5: + if (ngx_http_echo_strcmp_const(method_name->data, "MKCOL") == 0) { + *method_name_ptr = &ngx_http_echo_mkcol_method; + return NGX_HTTP_MKCOL; + break; + } + if (ngx_http_echo_strcmp_const(method_name->data, "TRACE") == 0) { + *method_name_ptr = &ngx_http_echo_trace_method; + return NGX_HTTP_TRACE; + break; + } + return NGX_HTTP_UNKNOWN; + break; + + case 6: + if (ngx_http_echo_strcmp_const(method_name->data, "DELETE") == 0) { + *method_name_ptr = &ngx_http_echo_delete_method; + return NGX_HTTP_DELETE; + break; + } + + if (ngx_http_echo_strcmp_const(method_name->data, "UNLOCK") == 0) { + *method_name_ptr = &ngx_http_echo_unlock_method; + return NGX_HTTP_UNLOCK; + break; + } + return NGX_HTTP_UNKNOWN; + break; + + case 7: + if (ngx_http_echo_strcmp_const(method_name->data, "OPTIONS") == 0) { + *method_name_ptr = &ngx_http_echo_options_method; + return NGX_HTTP_OPTIONS; + break; + } + return NGX_HTTP_UNKNOWN; + break; + + case 8: + if (ngx_http_echo_strcmp_const(method_name->data, "PROPFIND") == 0) { + *method_name_ptr = &ngx_http_echo_propfind_method; + return NGX_HTTP_PROPFIND; + break; + } + return NGX_HTTP_UNKNOWN; + break; + + case 9: + if (ngx_http_echo_strcmp_const(method_name->data, "PROPPATCH") == 0) { + *method_name_ptr = &ngx_http_echo_proppatch_method; + return NGX_HTTP_PROPPATCH; + break; + } + return NGX_HTTP_UNKNOWN; + break; + + default: + return NGX_HTTP_UNKNOWN; + break; + } + + return NGX_HTTP_UNKNOWN; +} + + +/* XXX extermely evil and not working yet */ +ngx_int_t +ngx_http_echo_exec_abort_parent(ngx_http_request_t *r, + ngx_http_echo_ctx_t *ctx) +{ +#if 0 + ngx_http_postponed_request_t *pr, *ppr; + ngx_http_request_t *saved_data = NULL; + ngx_chain_t *out = NULL; + /* ngx_int_t rc; */ + + dd("aborting parent..."); + + if (r == r->main || r->parent == NULL) { + return NGX_OK; + } + + if (r->parent->postponed) { + dd("Found parent->postponed..."); + + saved_data = r->connection->data; + ppr = NULL; + for (pr = r->parent->postponed; pr->next; pr = pr->next) { + if (pr->request == NULL) { + continue; + } + + if (pr->request == r) { + /* r->parent->postponed->next = pr; */ + dd("found the current subrequest"); + out = pr->out; + continue; + } + + /* r->connection->data = pr->request; */ + dd("finalizing the subrequest..."); + ngx_http_upstream_create(pr->request); + pr->request->upstream = NULL; + + if (ppr == NULL) { + r->parent->postponed = pr->next; + ppr = pr->next; + } else { + ppr->next = pr->next; + ppr = pr->next; + } + } + } + + r->parent->postponed->next = NULL; + + /* + r->connection->data = r->parent; + r->connection->buffered = 0; + + if (out != NULL) { + dd("trying to send more stuffs for the parent"); + ngx_http_output_filter(r->parent, out); + } + */ + + /* ngx_http_send_special(r->parent, NGX_HTTP_LAST); */ + + if (saved_data) { + r->connection->data = saved_data; + } + + dd("terminating the parent request"); + + return ngx_http_echo_send_chain_link(r, ctx, NULL /* indicate LAST */); + + /* ngx_http_upstream_create(r); */ + + /* ngx_http_finalize_request(r->parent, NGX_ERROR); */ +#endif + + return NGX_OK; +} + + +ngx_int_t +ngx_http_echo_exec_exec(ngx_http_request_t *r, + ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args) +{ + ngx_str_t *uri; + ngx_str_t *user_args; + ngx_str_t args; + ngx_uint_t flags; + ngx_str_t *computed_arg; + + computed_arg = computed_args->elts; + + uri = &computed_arg[0]; + + if (uri->len == 0) { + return NGX_HTTP_BAD_REQUEST; + } + + if (computed_args->nelts > 1) { + user_args = &computed_arg[1]; + } else { + user_args = NULL; + } + + args.data = NULL; + args.len = 0; + + if (ngx_http_parse_unsafe_uri(r, uri, &args, &flags) + != NGX_OK) { + ctx->headers_sent = 1; + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + if (args.len > 0 && user_args == NULL) { + user_args = &args; + } + + /* + rc = ngx_http_echo_send_header_if_needed(r, ctx); + if (r->header_only || rc >= NGX_HTTP_SPECIAL_RESPONSE) { + return rc; + } + */ + + if (uri->data[0] == '@') { + if (user_args && user_args->len > 0) { + ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, + "query strings %V ignored when exec'ing named location %V", + user_args, uri); + + } + + return ngx_http_named_location(r, uri); + } + + return ngx_http_internal_redirect(r, uri, user_args); +} + diff -Nru nginx-0.5.33/modules/nginx-echo/src/ngx_http_echo_subrequest.h nginx-0.8.53/modules/nginx-echo/src/ngx_http_echo_subrequest.h --- nginx-0.5.33/modules/nginx-echo/src/ngx_http_echo_subrequest.h 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/modules/nginx-echo/src/ngx_http_echo_subrequest.h 2011-02-26 14:04:40.000000000 +0000 @@ -0,0 +1,19 @@ +#ifndef ECHO_SUBREQUEST_H +#define ECHO_SUBREQUEST_H + +#include "ngx_http_echo_module.h" + +ngx_int_t ngx_http_echo_exec_echo_subrequest(ngx_http_request_t *r, + ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args); + +ngx_int_t ngx_http_echo_exec_echo_subrequest_async(ngx_http_request_t *r, + ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args); + +ngx_int_t ngx_http_echo_exec_abort_parent(ngx_http_request_t *r, + ngx_http_echo_ctx_t *ctx); + +ngx_int_t ngx_http_echo_exec_exec(ngx_http_request_t *r, + ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args); + +#endif /* ECHO_SUBREQUEST_H */ + diff -Nru nginx-0.5.33/modules/nginx-echo/src/ngx_http_echo_timer.c nginx-0.8.53/modules/nginx-echo/src/ngx_http_echo_timer.c --- nginx-0.5.33/modules/nginx-echo/src/ngx_http_echo_timer.c 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/modules/nginx-echo/src/ngx_http_echo_timer.c 2011-02-26 14:04:40.000000000 +0000 @@ -0,0 +1,76 @@ +#define DDEBUG 0 + +#include "ddebug.h" + +#include "ngx_http_echo_timer.h" + +#include +#include + +ngx_int_t +ngx_http_echo_timer_elapsed_variable(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + ngx_http_echo_ctx_t *ctx; + ngx_msec_int_t ms; + u_char *p; + ngx_time_t *tp; + size_t size; + + ctx = ngx_http_get_module_ctx(r, ngx_http_echo_module); + if (ctx->timer_begin.sec == 0) { + ctx->timer_begin.sec = r->start_sec; + ctx->timer_begin.msec = (ngx_msec_t) r->start_msec; + } + + /* force the ngx timer to update */ + +#if defined nginx_version && (nginx_version >= 8035 \ + || (nginx_version < 8000 && nginx_version >= 7066)) + ngx_time_update(); +#else + ngx_time_update(0, 0); +#endif + + tp = ngx_timeofday(); + + dd("old sec msec: %ld %d\n", ctx->timer_begin.sec, ctx->timer_begin.msec); + dd("new sec msec: %ld %d\n", tp->sec, tp->msec); + + ms = (ngx_msec_int_t) + ((tp->sec - ctx->timer_begin.sec) * 1000 + + (tp->msec - ctx->timer_begin.msec)); + ms = (ms >= 0) ? ms : 0; + + size = sizeof("-9223372036854775808.000") - 1; + p = ngx_palloc(r->pool, size); + v->len = ngx_snprintf(p, size, "%T.%03M", + ms / 1000, ms % 1000) - p; + v->data = p; + + v->valid = 1; + v->no_cacheable = 1; + v->not_found = 0; + return NGX_OK; +} + + +ngx_int_t +ngx_http_echo_exec_echo_reset_timer(ngx_http_request_t *r, + ngx_http_echo_ctx_t *ctx) +{ + dd("Exec timer..."); + + /* force the ngx timer to update */ + +#if defined nginx_version && (nginx_version >= 8035 \ + || (nginx_version < 8000 && nginx_version >= 7066)) + ngx_time_update(); +#else + ngx_time_update(0, 0); +#endif + + ctx->timer_begin = *ngx_timeofday(); + return NGX_OK; +} + diff -Nru nginx-0.5.33/modules/nginx-echo/src/ngx_http_echo_timer.h nginx-0.8.53/modules/nginx-echo/src/ngx_http_echo_timer.h --- nginx-0.5.33/modules/nginx-echo/src/ngx_http_echo_timer.h 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/modules/nginx-echo/src/ngx_http_echo_timer.h 2011-02-26 14:04:40.000000000 +0000 @@ -0,0 +1,13 @@ +#ifndef ECHO_TIMER_H +#define ECHO_TIMER_H + +#include "ngx_http_echo_module.h" + +ngx_int_t ngx_http_echo_timer_elapsed_variable(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); + +ngx_int_t ngx_http_echo_exec_echo_reset_timer(ngx_http_request_t *r, + ngx_http_echo_ctx_t *ctx); + +#endif /* ECHO_TIMER_H */ + diff -Nru nginx-0.5.33/modules/nginx-echo/src/ngx_http_echo_util.c nginx-0.8.53/modules/nginx-echo/src/ngx_http_echo_util.c --- nginx-0.5.33/modules/nginx-echo/src/ngx_http_echo_util.c 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/modules/nginx-echo/src/ngx_http_echo_util.c 2011-02-26 14:04:40.000000000 +0000 @@ -0,0 +1,248 @@ +#define DDEBUG 0 +#include "ddebug.h" + +#include "ngx_http_echo_util.h" +#include "ngx_http_echo_sleep.h" + + +ngx_int_t +ngx_http_echo_init_ctx(ngx_http_request_t *r, ngx_http_echo_ctx_t **ctx_ptr) +{ + ngx_http_echo_ctx_t *ctx; + + *ctx_ptr = ngx_pcalloc(r->pool, sizeof(ngx_http_echo_ctx_t)); + if (*ctx_ptr == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + ctx = *ctx_ptr; + + ctx->sleep.handler = ngx_http_echo_sleep_event_handler; + ctx->sleep.data = r; + ctx->sleep.log = r->connection->log; + + return NGX_OK; +} + +ngx_int_t +ngx_http_echo_eval_cmd_args(ngx_http_request_t *r, + ngx_http_echo_cmd_t *cmd, ngx_array_t *computed_args, + ngx_array_t *opts) +{ + ngx_uint_t i; + ngx_array_t *args = cmd->args; + ngx_str_t *arg, *raw, *opt; + ngx_http_echo_arg_template_t *value; + ngx_flag_t expecting_opts = 1; + + + value = args->elts; + + for (i = 0; i < args->nelts; i++) { + raw = &value[i].raw_value; + + if (value[i].lengths == NULL && raw->len > 0) { + if (expecting_opts) { + if (raw->len == 1 || raw->data[0] != '-') { + expecting_opts = 0; + + } else if (raw->data[1] == '-') { + expecting_opts = 0; + continue; + + } else { + opt = ngx_array_push(opts); + if (opt == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + opt->len = raw->len - 1; + opt->data = raw->data + 1; + + continue; + } + } + } + + arg = ngx_array_push(computed_args); + if (arg == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + if (value[i].lengths == NULL) { /* does not contain vars */ + dd("Using raw value \"%.*s\"", (int) raw->len, raw->data); + *arg = *raw; + + } else { + if (ngx_http_script_run(r, arg, value[i].lengths->elts, + 0, value[i].values->elts) == NULL) + { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + } + } + + return NGX_OK; +} + + +ngx_int_t +ngx_http_echo_send_chain_link(ngx_http_request_t* r, + ngx_http_echo_ctx_t *ctx, ngx_chain_t *cl) +{ + ngx_int_t rc; + size_t size; + ngx_chain_t *p; + + rc = ngx_http_echo_send_header_if_needed(r, ctx); + + if (r->header_only || rc >= NGX_HTTP_SPECIAL_RESPONSE) { + return rc; + } + + if (r->http_version < NGX_HTTP_VERSION_11 && !ctx->headers_sent) { + ctx->headers_sent = 1; + + size = 0; + + for (p = cl; p; p = p->next) { + if (p->buf->memory) { + size += p->buf->last - p->buf->pos; + } + } + + r->headers_out.content_length_n = (off_t) size; + + if (r->headers_out.content_length) { + r->headers_out.content_length->hash = 0; + } + + r->headers_out.content_length = NULL; + + rc = ngx_http_send_header(r); + + if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { + return rc; + } + } + + if (cl == NULL) { + +#if defined(nginx_version) && nginx_version <= 8004 + + /* earlier versions of nginx does not allow subrequests + to send last_buf themselves */ + if (r != r->main) { + return NGX_OK; + } + +#endif + + rc = ngx_http_send_special(r, NGX_HTTP_LAST); + if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { + return rc; + } + + return NGX_OK; + } + + return ngx_http_output_filter(r, cl); +} + +ngx_int_t +ngx_http_echo_send_header_if_needed(ngx_http_request_t* r, + ngx_http_echo_ctx_t *ctx) { + /* ngx_int_t rc; */ + + if ( ! ctx->headers_sent ) { + r->headers_out.status = NGX_HTTP_OK; + + if (ngx_http_set_content_type(r) != NGX_OK) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + ngx_http_clear_content_length(r); + ngx_http_clear_accept_ranges(r); + + if (r->http_version >= NGX_HTTP_VERSION_11) { + ctx->headers_sent = 1; + return ngx_http_send_header(r); + } + } + + return NGX_OK; +} + +ssize_t +ngx_http_echo_atosz(u_char *line, size_t n) { + ssize_t value; + + if (n == 0) { + return NGX_ERROR; + } + + for (value = 0; n--; line++) { + if (*line == '_') { /* we ignore undercores */ + continue; + } + + if (*line < '0' || *line > '9') { + return NGX_ERROR; + } + + value = value * 10 + (*line - '0'); + } + + if (value < 0) { + return NGX_ERROR; + } else { + return value; + } +} + +/* Modified from the ngx_strlcasestrn function in ngx_string.h + * Copyright (C) by Igor Sysoev */ +u_char * +ngx_http_echo_strlstrn(u_char *s1, u_char *last, u_char *s2, size_t n) +{ + ngx_uint_t c1, c2; + + c2 = (ngx_uint_t) *s2++; + last -= n; + + do { + do { + if (s1 >= last) { + return NULL; + } + + c1 = (ngx_uint_t) *s1++; + + } while (c1 != c2); + + } while (ngx_strncmp(s1, s2, n) != 0); + + return --s1; +} + + +ngx_int_t +ngx_http_echo_post_request_at_head(ngx_http_request_t *r, + ngx_http_posted_request_t *pr) +{ + dd_enter(); + + 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 = r->main->posted_requests; + r->main->posted_requests = pr; + + return NGX_OK; +} + diff -Nru nginx-0.5.33/modules/nginx-echo/src/ngx_http_echo_util.h nginx-0.8.53/modules/nginx-echo/src/ngx_http_echo_util.h --- nginx-0.5.33/modules/nginx-echo/src/ngx_http_echo_util.h 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/modules/nginx-echo/src/ngx_http_echo_util.h 2011-02-26 14:04:40.000000000 +0000 @@ -0,0 +1,30 @@ +#ifndef NGX_HTTP_ECHO_UTIL_H +#define NGX_HTTP_ECHO_UTIL_H + +#include "ngx_http_echo_module.h" + +#define ngx_http_echo_strcmp_const(a, b) \ + ngx_strncmp(a, b, sizeof(b) - 1) + +ngx_int_t ngx_http_echo_init_ctx(ngx_http_request_t *r, ngx_http_echo_ctx_t **ctx_ptr); + +ngx_int_t ngx_http_echo_eval_cmd_args(ngx_http_request_t *r, + ngx_http_echo_cmd_t *cmd, ngx_array_t *computed_args, + ngx_array_t *opts); + +ngx_int_t ngx_http_echo_send_header_if_needed(ngx_http_request_t* r, + ngx_http_echo_ctx_t *ctx); + +ngx_int_t ngx_http_echo_send_chain_link(ngx_http_request_t* r, + ngx_http_echo_ctx_t *ctx, ngx_chain_t *cl); + +ssize_t ngx_http_echo_atosz(u_char *line, size_t n); + +u_char * ngx_http_echo_strlstrn(u_char *s1, u_char *last, u_char *s2, size_t n); + +ngx_int_t ngx_http_echo_post_request_at_head(ngx_http_request_t *r, + ngx_http_posted_request_t *pr); + + +#endif /* NGX_HTTP_ECHO_UTIL_H */ + diff -Nru nginx-0.5.33/modules/nginx-echo/src/ngx_http_echo_var.c nginx-0.8.53/modules/nginx-echo/src/ngx_http_echo_var.c --- nginx-0.5.33/modules/nginx-echo/src/ngx_http_echo_var.c 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/modules/nginx-echo/src/ngx_http_echo_var.c 2011-02-26 14:04:40.000000000 +0000 @@ -0,0 +1,98 @@ +#define DDEBUG 0 +#include "ddebug.h" + +#include "ngx_http_echo_var.h" +#include "ngx_http_echo_timer.h" +#include "ngx_http_echo_request_info.h" +#include "ngx_http_echo_foreach.h" + +static ngx_int_t ngx_http_echo_incr_variable(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); + +static ngx_http_variable_t ngx_http_echo_variables[] = { + { ngx_string("echo_timer_elapsed"), NULL, + ngx_http_echo_timer_elapsed_variable, 0, + NGX_HTTP_VAR_NOCACHEABLE, 0 }, + + { ngx_string("echo_request_method"), NULL, + ngx_http_echo_request_method_variable, 0, + NGX_HTTP_VAR_NOCACHEABLE, 0 }, + + { ngx_string("echo_cacheable_request_uri"), NULL, + ngx_http_echo_cacheable_request_uri_variable, 0, + 0, 0 }, + + { ngx_string("echo_request_uri"), NULL, + ngx_http_echo_request_uri_variable, 0, + 0, 0 }, + + { ngx_string("echo_client_request_method"), NULL, + ngx_http_echo_client_request_method_variable, 0, + NGX_HTTP_VAR_NOCACHEABLE, 0 }, + + { ngx_string("echo_request_body"), NULL, + ngx_http_echo_request_body_variable, 0, + NGX_HTTP_VAR_NOCACHEABLE, 0 }, + + { ngx_string("echo_client_request_headers"), NULL, + ngx_http_echo_client_request_headers_variable, 0, + NGX_HTTP_VAR_NOCACHEABLE, 0 }, + + { ngx_string("echo_it"), NULL, + ngx_http_echo_it_variable, 0, + NGX_HTTP_VAR_NOCACHEABLE, 0 }, + + { ngx_string("echo_incr"), NULL, + ngx_http_echo_incr_variable, 0, + NGX_HTTP_VAR_NOCACHEABLE, 0 }, + + { ngx_string("echo_response_status"), NULL, + ngx_http_echo_response_status_variable, 0, + NGX_HTTP_VAR_NOCACHEABLE, 0 }, + + { ngx_null_string, NULL, NULL, 0, 0, 0 } +}; + +ngx_int_t +ngx_http_echo_add_variables(ngx_conf_t *cf) { + ngx_http_variable_t *var, *v; + for (v = ngx_http_echo_variables; 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_echo_incr_variable(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) { + ngx_http_echo_ctx_t *ctx; + u_char *p; + + ctx = ngx_http_get_module_ctx(r->main, ngx_http_echo_module); + + if (ctx == NULL) { + return NGX_ERROR; + } + + ctx->counter++; + + p = ngx_palloc(r->pool, NGX_INT_T_LEN); + if (p == NULL) { + return NGX_ERROR; + } + + v->len = ngx_sprintf(p, "%ui", ctx->counter) - p; + v->data = p; + + v->valid = 1; + v->not_found = 0; + v->no_cacheable = 1; + + return NGX_OK; +} + diff -Nru nginx-0.5.33/modules/nginx-echo/src/ngx_http_echo_var.h nginx-0.8.53/modules/nginx-echo/src/ngx_http_echo_var.h --- nginx-0.5.33/modules/nginx-echo/src/ngx_http_echo_var.h 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/modules/nginx-echo/src/ngx_http_echo_var.h 2011-02-26 14:04:40.000000000 +0000 @@ -0,0 +1,9 @@ +#ifndef ECHO_VAR_H +#define ECHO_VAR_H + +#include "ngx_http_echo_module.h" + +ngx_int_t ngx_http_echo_add_variables(ngx_conf_t *cf); + +#endif /* ECHO_VAR_H */ + diff -Nru nginx-0.5.33/modules/nginx-echo/test/inc/Module/AutoInstall.pm nginx-0.8.53/modules/nginx-echo/test/inc/Module/AutoInstall.pm --- nginx-0.5.33/modules/nginx-echo/test/inc/Module/AutoInstall.pm 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/modules/nginx-echo/test/inc/Module/AutoInstall.pm 2011-02-26 14:04:40.000000000 +0000 @@ -0,0 +1,820 @@ +#line 1 +package Module::AutoInstall; + +use strict; +use Cwd (); +use ExtUtils::MakeMaker (); + +use vars qw{$VERSION}; +BEGIN { + $VERSION = '1.03'; +} + +# special map on pre-defined feature sets +my %FeatureMap = ( + '' => 'Core Features', # XXX: deprecated + '-core' => 'Core Features', +); + +# various lexical flags +my ( @Missing, @Existing, %DisabledTests, $UnderCPAN, $HasCPANPLUS ); +my ( + $Config, $CheckOnly, $SkipInstall, $AcceptDefault, $TestOnly, $AllDeps +); +my ( $PostambleActions, $PostambleUsed ); + +# See if it's a testing or non-interactive session +_accept_default( $ENV{AUTOMATED_TESTING} or ! -t STDIN ); +_init(); + +sub _accept_default { + $AcceptDefault = shift; +} + +sub missing_modules { + return @Missing; +} + +sub do_install { + __PACKAGE__->install( + [ + $Config + ? ( UNIVERSAL::isa( $Config, 'HASH' ) ? %{$Config} : @{$Config} ) + : () + ], + @Missing, + ); +} + +# initialize various flags, and/or perform install +sub _init { + foreach my $arg ( + @ARGV, + split( + /[\s\t]+/, + $ENV{PERL_AUTOINSTALL} || $ENV{PERL_EXTUTILS_AUTOINSTALL} || '' + ) + ) + { + if ( $arg =~ /^--config=(.*)$/ ) { + $Config = [ split( ',', $1 ) ]; + } + elsif ( $arg =~ /^--installdeps=(.*)$/ ) { + __PACKAGE__->install( $Config, @Missing = split( /,/, $1 ) ); + exit 0; + } + elsif ( $arg =~ /^--default(?:deps)?$/ ) { + $AcceptDefault = 1; + } + elsif ( $arg =~ /^--check(?:deps)?$/ ) { + $CheckOnly = 1; + } + elsif ( $arg =~ /^--skip(?:deps)?$/ ) { + $SkipInstall = 1; + } + elsif ( $arg =~ /^--test(?:only)?$/ ) { + $TestOnly = 1; + } + elsif ( $arg =~ /^--all(?:deps)?$/ ) { + $AllDeps = 1; + } + } +} + +# overrides MakeMaker's prompt() to automatically accept the default choice +sub _prompt { + goto &ExtUtils::MakeMaker::prompt unless $AcceptDefault; + + my ( $prompt, $default ) = @_; + my $y = ( $default =~ /^[Yy]/ ); + + print $prompt, ' [', ( $y ? 'Y' : 'y' ), '/', ( $y ? 'n' : 'N' ), '] '; + print "$default\n"; + return $default; +} + +# the workhorse +sub import { + my $class = shift; + my @args = @_ or return; + my $core_all; + + print "*** $class version " . $class->VERSION . "\n"; + print "*** Checking for Perl dependencies...\n"; + + my $cwd = Cwd::cwd(); + + $Config = []; + + my $maxlen = length( + ( + sort { length($b) <=> length($a) } + grep { /^[^\-]/ } + map { + ref($_) + ? ( ( ref($_) eq 'HASH' ) ? keys(%$_) : @{$_} ) + : '' + } + map { +{@args}->{$_} } + grep { /^[^\-]/ or /^-core$/i } keys %{ +{@args} } + )[0] + ); + + # We want to know if we're under CPAN early to avoid prompting, but + # if we aren't going to try and install anything anyway then skip the + # check entirely since we don't want to have to load (and configure) + # an old CPAN just for a cosmetic message + + $UnderCPAN = _check_lock(1) unless $SkipInstall; + + while ( my ( $feature, $modules ) = splice( @args, 0, 2 ) ) { + my ( @required, @tests, @skiptests ); + my $default = 1; + my $conflict = 0; + + if ( $feature =~ m/^-(\w+)$/ ) { + my $option = lc($1); + + # check for a newer version of myself + _update_to( $modules, @_ ) and return if $option eq 'version'; + + # sets CPAN configuration options + $Config = $modules if $option eq 'config'; + + # promote every features to core status + $core_all = ( $modules =~ /^all$/i ) and next + if $option eq 'core'; + + next unless $option eq 'core'; + } + + print "[" . ( $FeatureMap{ lc($feature) } || $feature ) . "]\n"; + + $modules = [ %{$modules} ] if UNIVERSAL::isa( $modules, 'HASH' ); + + unshift @$modules, -default => &{ shift(@$modules) } + if ( ref( $modules->[0] ) eq 'CODE' ); # XXX: bugward combatability + + while ( my ( $mod, $arg ) = splice( @$modules, 0, 2 ) ) { + if ( $mod =~ m/^-(\w+)$/ ) { + my $option = lc($1); + + $default = $arg if ( $option eq 'default' ); + $conflict = $arg if ( $option eq 'conflict' ); + @tests = @{$arg} if ( $option eq 'tests' ); + @skiptests = @{$arg} if ( $option eq 'skiptests' ); + + next; + } + + printf( "- %-${maxlen}s ...", $mod ); + + if ( $arg and $arg =~ /^\D/ ) { + unshift @$modules, $arg; + $arg = 0; + } + + # XXX: check for conflicts and uninstalls(!) them. + my $cur = _load($mod); + if (_version_cmp ($cur, $arg) >= 0) + { + print "loaded. ($cur" . ( $arg ? " >= $arg" : '' ) . ")\n"; + push @Existing, $mod => $arg; + $DisabledTests{$_} = 1 for map { glob($_) } @skiptests; + } + else { + if (not defined $cur) # indeed missing + { + print "missing." . ( $arg ? " (would need $arg)" : '' ) . "\n"; + } + else + { + # no need to check $arg as _version_cmp ($cur, undef) would satisfy >= above + print "too old. ($cur < $arg)\n"; + } + + push @required, $mod => $arg; + } + } + + next unless @required; + + my $mandatory = ( $feature eq '-core' or $core_all ); + + if ( + !$SkipInstall + and ( + $CheckOnly + or ($mandatory and $UnderCPAN) + or $AllDeps + or _prompt( + qq{==> Auto-install the } + . ( @required / 2 ) + . ( $mandatory ? ' mandatory' : ' optional' ) + . qq{ module(s) from CPAN?}, + $default ? 'y' : 'n', + ) =~ /^[Yy]/ + ) + ) + { + push( @Missing, @required ); + $DisabledTests{$_} = 1 for map { glob($_) } @skiptests; + } + + elsif ( !$SkipInstall + and $default + and $mandatory + and + _prompt( qq{==> The module(s) are mandatory! Really skip?}, 'n', ) + =~ /^[Nn]/ ) + { + push( @Missing, @required ); + $DisabledTests{$_} = 1 for map { glob($_) } @skiptests; + } + + else { + $DisabledTests{$_} = 1 for map { glob($_) } @tests; + } + } + + if ( @Missing and not( $CheckOnly or $UnderCPAN ) ) { + require Config; + print +"*** Dependencies will be installed the next time you type '$Config::Config{make}'.\n"; + + # make an educated guess of whether we'll need root permission. + print " (You may need to do that as the 'root' user.)\n" + if eval '$>'; + } + print "*** $class configuration finished.\n"; + + chdir $cwd; + + # import to main:: + no strict 'refs'; + *{'main::WriteMakefile'} = \&Write if caller(0) eq 'main'; + + return (@Existing, @Missing); +} + +sub _running_under { + my $thing = shift; + print <<"END_MESSAGE"; +*** Since we're running under ${thing}, I'll just let it take care + of the dependency's installation later. +END_MESSAGE + return 1; +} + +# Check to see if we are currently running under CPAN.pm and/or CPANPLUS; +# if we are, then we simply let it taking care of our dependencies +sub _check_lock { + return unless @Missing or @_; + + my $cpan_env = $ENV{PERL5_CPAN_IS_RUNNING}; + + if ($ENV{PERL5_CPANPLUS_IS_RUNNING}) { + return _running_under($cpan_env ? 'CPAN' : 'CPANPLUS'); + } + + require CPAN; + + if ($CPAN::VERSION > '1.89') { + if ($cpan_env) { + return _running_under('CPAN'); + } + return; # CPAN.pm new enough, don't need to check further + } + + # last ditch attempt, this -will- configure CPAN, very sorry + + _load_cpan(1); # force initialize even though it's already loaded + + # Find the CPAN lock-file + my $lock = MM->catfile( $CPAN::Config->{cpan_home}, ".lock" ); + return unless -f $lock; + + # Check the lock + local *LOCK; + return unless open(LOCK, $lock); + + if ( + ( $^O eq 'MSWin32' ? _under_cpan() : == getppid() ) + and ( $CPAN::Config->{prerequisites_policy} || '' ) ne 'ignore' + ) { + print <<'END_MESSAGE'; + +*** Since we're running under CPAN, I'll just let it take care + of the dependency's installation later. +END_MESSAGE + return 1; + } + + close LOCK; + return; +} + +sub install { + my $class = shift; + + my $i; # used below to strip leading '-' from config keys + my @config = ( map { s/^-// if ++$i; $_ } @{ +shift } ); + + my ( @modules, @installed ); + while ( my ( $pkg, $ver ) = splice( @_, 0, 2 ) ) { + + # grep out those already installed + if ( _version_cmp( _load($pkg), $ver ) >= 0 ) { + push @installed, $pkg; + } + else { + push @modules, $pkg, $ver; + } + } + + return @installed unless @modules; # nothing to do + return @installed if _check_lock(); # defer to the CPAN shell + + print "*** Installing dependencies...\n"; + + return unless _connected_to('cpan.org'); + + my %args = @config; + my %failed; + local *FAILED; + if ( $args{do_once} and open( FAILED, '.#autoinstall.failed' ) ) { + while () { chomp; $failed{$_}++ } + close FAILED; + + my @newmod; + while ( my ( $k, $v ) = splice( @modules, 0, 2 ) ) { + push @newmod, ( $k => $v ) unless $failed{$k}; + } + @modules = @newmod; + } + + if ( _has_cpanplus() and not $ENV{PERL_AUTOINSTALL_PREFER_CPAN} ) { + _install_cpanplus( \@modules, \@config ); + } else { + _install_cpan( \@modules, \@config ); + } + + print "*** $class installation finished.\n"; + + # see if we have successfully installed them + while ( my ( $pkg, $ver ) = splice( @modules, 0, 2 ) ) { + if ( _version_cmp( _load($pkg), $ver ) >= 0 ) { + push @installed, $pkg; + } + elsif ( $args{do_once} and open( FAILED, '>> .#autoinstall.failed' ) ) { + print FAILED "$pkg\n"; + } + } + + close FAILED if $args{do_once}; + + return @installed; +} + +sub _install_cpanplus { + my @modules = @{ +shift }; + my @config = _cpanplus_config( @{ +shift } ); + my $installed = 0; + + require CPANPLUS::Backend; + my $cp = CPANPLUS::Backend->new; + my $conf = $cp->configure_object; + + return unless $conf->can('conf') # 0.05x+ with "sudo" support + or _can_write($conf->_get_build('base')); # 0.04x + + # if we're root, set UNINST=1 to avoid trouble unless user asked for it. + my $makeflags = $conf->get_conf('makeflags') || ''; + if ( UNIVERSAL::isa( $makeflags, 'HASH' ) ) { + # 0.03+ uses a hashref here + $makeflags->{UNINST} = 1 unless exists $makeflags->{UNINST}; + + } else { + # 0.02 and below uses a scalar + $makeflags = join( ' ', split( ' ', $makeflags ), 'UNINST=1' ) + if ( $makeflags !~ /\bUNINST\b/ and eval qq{ $> eq '0' } ); + + } + $conf->set_conf( makeflags => $makeflags ); + $conf->set_conf( prereqs => 1 ); + + + + while ( my ( $key, $val ) = splice( @config, 0, 2 ) ) { + $conf->set_conf( $key, $val ); + } + + my $modtree = $cp->module_tree; + while ( my ( $pkg, $ver ) = splice( @modules, 0, 2 ) ) { + print "*** Installing $pkg...\n"; + + MY::preinstall( $pkg, $ver ) or next if defined &MY::preinstall; + + my $success; + my $obj = $modtree->{$pkg}; + + if ( $obj and _version_cmp( $obj->{version}, $ver ) >= 0 ) { + my $pathname = $pkg; + $pathname =~ s/::/\\W/; + + foreach my $inc ( grep { m/$pathname.pm/i } keys(%INC) ) { + delete $INC{$inc}; + } + + my $rv = $cp->install( modules => [ $obj->{module} ] ); + + if ( $rv and ( $rv->{ $obj->{module} } or $rv->{ok} ) ) { + print "*** $pkg successfully installed.\n"; + $success = 1; + } else { + print "*** $pkg installation cancelled.\n"; + $success = 0; + } + + $installed += $success; + } else { + print << "."; +*** Could not find a version $ver or above for $pkg; skipping. +. + } + + MY::postinstall( $pkg, $ver, $success ) if defined &MY::postinstall; + } + + return $installed; +} + +sub _cpanplus_config { + my @config = (); + while ( @_ ) { + my ($key, $value) = (shift(), shift()); + if ( $key eq 'prerequisites_policy' ) { + if ( $value eq 'follow' ) { + $value = CPANPLUS::Internals::Constants::PREREQ_INSTALL(); + } elsif ( $value eq 'ask' ) { + $value = CPANPLUS::Internals::Constants::PREREQ_ASK(); + } elsif ( $value eq 'ignore' ) { + $value = CPANPLUS::Internals::Constants::PREREQ_IGNORE(); + } else { + die "*** Cannot convert option $key = '$value' to CPANPLUS version.\n"; + } + } else { + die "*** Cannot convert option $key to CPANPLUS version.\n"; + } + } + return @config; +} + +sub _install_cpan { + my @modules = @{ +shift }; + my @config = @{ +shift }; + my $installed = 0; + my %args; + + _load_cpan(); + require Config; + + if (CPAN->VERSION < 1.80) { + # no "sudo" support, probe for writableness + return unless _can_write( MM->catfile( $CPAN::Config->{cpan_home}, 'sources' ) ) + and _can_write( $Config::Config{sitelib} ); + } + + # if we're root, set UNINST=1 to avoid trouble unless user asked for it. + my $makeflags = $CPAN::Config->{make_install_arg} || ''; + $CPAN::Config->{make_install_arg} = + join( ' ', split( ' ', $makeflags ), 'UNINST=1' ) + if ( $makeflags !~ /\bUNINST\b/ and eval qq{ $> eq '0' } ); + + # don't show start-up info + $CPAN::Config->{inhibit_startup_message} = 1; + + # set additional options + while ( my ( $opt, $arg ) = splice( @config, 0, 2 ) ) { + ( $args{$opt} = $arg, next ) + if $opt =~ /^force$/; # pseudo-option + $CPAN::Config->{$opt} = $arg; + } + + local $CPAN::Config->{prerequisites_policy} = 'follow'; + + while ( my ( $pkg, $ver ) = splice( @modules, 0, 2 ) ) { + MY::preinstall( $pkg, $ver ) or next if defined &MY::preinstall; + + print "*** Installing $pkg...\n"; + + my $obj = CPAN::Shell->expand( Module => $pkg ); + my $success = 0; + + if ( $obj and _version_cmp( $obj->cpan_version, $ver ) >= 0 ) { + my $pathname = $pkg; + $pathname =~ s/::/\\W/; + + foreach my $inc ( grep { m/$pathname.pm/i } keys(%INC) ) { + delete $INC{$inc}; + } + + my $rv = $args{force} ? CPAN::Shell->force( install => $pkg ) + : CPAN::Shell->install($pkg); + $rv ||= eval { + $CPAN::META->instance( 'CPAN::Distribution', $obj->cpan_file, ) + ->{install} + if $CPAN::META; + }; + + if ( $rv eq 'YES' ) { + print "*** $pkg successfully installed.\n"; + $success = 1; + } + else { + print "*** $pkg installation failed.\n"; + $success = 0; + } + + $installed += $success; + } + else { + print << "."; +*** Could not find a version $ver or above for $pkg; skipping. +. + } + + MY::postinstall( $pkg, $ver, $success ) if defined &MY::postinstall; + } + + return $installed; +} + +sub _has_cpanplus { + return ( + $HasCPANPLUS = ( + $INC{'CPANPLUS/Config.pm'} + or _load('CPANPLUS::Shell::Default') + ) + ); +} + +# make guesses on whether we're under the CPAN installation directory +sub _under_cpan { + require Cwd; + require File::Spec; + + my $cwd = File::Spec->canonpath( Cwd::cwd() ); + my $cpan = File::Spec->canonpath( $CPAN::Config->{cpan_home} ); + + return ( index( $cwd, $cpan ) > -1 ); +} + +sub _update_to { + my $class = __PACKAGE__; + my $ver = shift; + + return + if _version_cmp( _load($class), $ver ) >= 0; # no need to upgrade + + if ( + _prompt( "==> A newer version of $class ($ver) is required. Install?", + 'y' ) =~ /^[Nn]/ + ) + { + die "*** Please install $class $ver manually.\n"; + } + + print << "."; +*** Trying to fetch it from CPAN... +. + + # install ourselves + _load($class) and return $class->import(@_) + if $class->install( [], $class, $ver ); + + print << '.'; exit 1; + +*** Cannot bootstrap myself. :-( Installation terminated. +. +} + +# check if we're connected to some host, using inet_aton +sub _connected_to { + my $site = shift; + + return ( + ( _load('Socket') and Socket::inet_aton($site) ) or _prompt( + qq( +*** Your host cannot resolve the domain name '$site', which + probably means the Internet connections are unavailable. +==> Should we try to install the required module(s) anyway?), 'n' + ) =~ /^[Yy]/ + ); +} + +# check if a directory is writable; may create it on demand +sub _can_write { + my $path = shift; + mkdir( $path, 0755 ) unless -e $path; + + return 1 if -w $path; + + print << "."; +*** You are not allowed to write to the directory '$path'; + the installation may fail due to insufficient permissions. +. + + if ( + eval '$>' and lc(`sudo -V`) =~ /version/ and _prompt( + qq( +==> Should we try to re-execute the autoinstall process with 'sudo'?), + ((-t STDIN) ? 'y' : 'n') + ) =~ /^[Yy]/ + ) + { + + # try to bootstrap ourselves from sudo + print << "."; +*** Trying to re-execute the autoinstall process with 'sudo'... +. + my $missing = join( ',', @Missing ); + my $config = join( ',', + UNIVERSAL::isa( $Config, 'HASH' ) ? %{$Config} : @{$Config} ) + if $Config; + + return + unless system( 'sudo', $^X, $0, "--config=$config", + "--installdeps=$missing" ); + + print << "."; +*** The 'sudo' command exited with error! Resuming... +. + } + + return _prompt( + qq( +==> Should we try to install the required module(s) anyway?), 'n' + ) =~ /^[Yy]/; +} + +# load a module and return the version it reports +sub _load { + my $mod = pop; # class/instance doesn't matter + my $file = $mod; + + $file =~ s|::|/|g; + $file .= '.pm'; + + local $@; + return eval { require $file; $mod->VERSION } || ( $@ ? undef: 0 ); +} + +# Load CPAN.pm and it's configuration +sub _load_cpan { + return if $CPAN::VERSION and $CPAN::Config and not @_; + require CPAN; + + # CPAN-1.82+ adds CPAN::Config::AUTOLOAD to redirect to + # CPAN::HandleConfig->load. CPAN reports that the redirection + # is deprecated in a warning printed at the user. + + # CPAN-1.81 expects CPAN::HandleConfig->load, does not have + # $CPAN::HandleConfig::VERSION but cannot handle + # CPAN::Config->load + + # Which "versions expect CPAN::Config->load? + + if ( $CPAN::HandleConfig::VERSION + || CPAN::HandleConfig->can('load') + ) { + # Newer versions of CPAN have a HandleConfig module + CPAN::HandleConfig->load; + } else { + # Older versions had the load method in Config directly + CPAN::Config->load; + } +} + +# compare two versions, either use Sort::Versions or plain comparison +# return values same as <=> +sub _version_cmp { + my ( $cur, $min ) = @_; + return -1 unless defined $cur; # if 0 keep comparing + return 1 unless $min; + + $cur =~ s/\s+$//; + + # check for version numbers that are not in decimal format + if ( ref($cur) or ref($min) or $cur =~ /v|\..*\./ or $min =~ /v|\..*\./ ) { + if ( ( $version::VERSION or defined( _load('version') )) and + version->can('new') + ) { + + # use version.pm if it is installed. + return version->new($cur) <=> version->new($min); + } + elsif ( $Sort::Versions::VERSION or defined( _load('Sort::Versions') ) ) + { + + # use Sort::Versions as the sorting algorithm for a.b.c versions + return Sort::Versions::versioncmp( $cur, $min ); + } + + warn "Cannot reliably compare non-decimal formatted versions.\n" + . "Please install version.pm or Sort::Versions.\n"; + } + + # plain comparison + local $^W = 0; # shuts off 'not numeric' bugs + return $cur <=> $min; +} + +# nothing; this usage is deprecated. +sub main::PREREQ_PM { return {}; } + +sub _make_args { + my %args = @_; + + $args{PREREQ_PM} = { %{ $args{PREREQ_PM} || {} }, @Existing, @Missing } + if $UnderCPAN or $TestOnly; + + if ( $args{EXE_FILES} and -e 'MANIFEST' ) { + require ExtUtils::Manifest; + my $manifest = ExtUtils::Manifest::maniread('MANIFEST'); + + $args{EXE_FILES} = + [ grep { exists $manifest->{$_} } @{ $args{EXE_FILES} } ]; + } + + $args{test}{TESTS} ||= 't/*.t'; + $args{test}{TESTS} = join( ' ', + grep { !exists( $DisabledTests{$_} ) } + map { glob($_) } split( /\s+/, $args{test}{TESTS} ) ); + + my $missing = join( ',', @Missing ); + my $config = + join( ',', UNIVERSAL::isa( $Config, 'HASH' ) ? %{$Config} : @{$Config} ) + if $Config; + + $PostambleActions = ( + ($missing and not $UnderCPAN) + ? "\$(PERL) $0 --config=$config --installdeps=$missing" + : "\$(NOECHO) \$(NOOP)" + ); + + return %args; +} + +# a wrapper to ExtUtils::MakeMaker::WriteMakefile +sub Write { + require Carp; + Carp::croak "WriteMakefile: Need even number of args" if @_ % 2; + + if ($CheckOnly) { + print << "."; +*** Makefile not written in check-only mode. +. + return; + } + + my %args = _make_args(@_); + + no strict 'refs'; + + $PostambleUsed = 0; + local *MY::postamble = \&postamble unless defined &MY::postamble; + ExtUtils::MakeMaker::WriteMakefile(%args); + + print << "." unless $PostambleUsed; +*** WARNING: Makefile written with customized MY::postamble() without + including contents from Module::AutoInstall::postamble() -- + auto installation features disabled. Please contact the author. +. + + return 1; +} + +sub postamble { + $PostambleUsed = 1; + + return <<"END_MAKE"; + +config :: installdeps +\t\$(NOECHO) \$(NOOP) + +checkdeps :: +\t\$(PERL) $0 --checkdeps + +installdeps :: +\t$PostambleActions + +END_MAKE + +} + +1; + +__END__ + +#line 1071 diff -Nru nginx-0.5.33/modules/nginx-echo/test/inc/Module/Install.pm nginx-0.8.53/modules/nginx-echo/test/inc/Module/Install.pm --- nginx-0.5.33/modules/nginx-echo/test/inc/Module/Install.pm 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/modules/nginx-echo/test/inc/Module/Install.pm 2011-02-26 14:04:40.000000000 +0000 @@ -0,0 +1,466 @@ +#line 1 +package Module::Install; + +# For any maintainers: +# The load order for Module::Install is a bit magic. +# It goes something like this... +# +# IF ( host has Module::Install installed, creating author mode ) { +# 1. Makefile.PL calls "use inc::Module::Install" +# 2. $INC{inc/Module/Install.pm} set to installed version of inc::Module::Install +# 3. The installed version of inc::Module::Install loads +# 4. inc::Module::Install calls "require Module::Install" +# 5. The ./inc/ version of Module::Install loads +# } ELSE { +# 1. Makefile.PL calls "use inc::Module::Install" +# 2. $INC{inc/Module/Install.pm} set to ./inc/ version of Module::Install +# 3. The ./inc/ version of Module::Install loads +# } + +use 5.005; +use strict 'vars'; +use Cwd (); +use File::Find (); +use File::Path (); + +use vars qw{$VERSION $MAIN}; +BEGIN { + # All Module::Install core packages now require synchronised versions. + # This will be used to ensure we don't accidentally load old or + # different versions of modules. + # This is not enforced yet, but will be some time in the next few + # releases once we can make sure it won't clash with custom + # Module::Install extensions. + $VERSION = '0.99'; + + # Storage for the pseudo-singleton + $MAIN = undef; + + *inc::Module::Install::VERSION = *VERSION; + @inc::Module::Install::ISA = __PACKAGE__; + +} + +sub import { + my $class = shift; + my $self = $class->new(@_); + my $who = $self->_caller; + + #------------------------------------------------------------- + # all of the following checks should be included in import(), + # to allow "eval 'require Module::Install; 1' to test + # installation of Module::Install. (RT #51267) + #------------------------------------------------------------- + + # Whether or not inc::Module::Install is actually loaded, the + # $INC{inc/Module/Install.pm} is what will still get set as long as + # the caller loaded module this in the documented manner. + # If not set, the caller may NOT have loaded the bundled version, and thus + # they may not have a MI version that works with the Makefile.PL. This would + # result in false errors or unexpected behaviour. And we don't want that. + my $file = join( '/', 'inc', split /::/, __PACKAGE__ ) . '.pm'; + unless ( $INC{$file} ) { die <<"END_DIE" } + +Please invoke ${\__PACKAGE__} with: + + use inc::${\__PACKAGE__}; + +not: + + use ${\__PACKAGE__}; + +END_DIE + + # This reportedly fixes a rare Win32 UTC file time issue, but + # as this is a non-cross-platform XS module not in the core, + # we shouldn't really depend on it. See RT #24194 for detail. + # (Also, this module only supports Perl 5.6 and above). + eval "use Win32::UTCFileTime" if $^O eq 'MSWin32' && $] >= 5.006; + + # If the script that is loading Module::Install is from the future, + # then make will detect this and cause it to re-run over and over + # again. This is bad. Rather than taking action to touch it (which + # is unreliable on some platforms and requires write permissions) + # for now we should catch this and refuse to run. + if ( -f $0 ) { + my $s = (stat($0))[9]; + + # If the modification time is only slightly in the future, + # sleep briefly to remove the problem. + my $a = $s - time; + if ( $a > 0 and $a < 5 ) { sleep 5 } + + # Too far in the future, throw an error. + my $t = time; + if ( $s > $t ) { die <<"END_DIE" } + +Your installer $0 has a modification time in the future ($s > $t). + +This is known to create infinite loops in make. + +Please correct this, then run $0 again. + +END_DIE + } + + + # Build.PL was formerly supported, but no longer is due to excessive + # difficulty in implementing every single feature twice. + if ( $0 =~ /Build.PL$/i ) { die <<"END_DIE" } + +Module::Install no longer supports Build.PL. + +It was impossible to maintain duel backends, and has been deprecated. + +Please remove all Build.PL files and only use the Makefile.PL installer. + +END_DIE + + #------------------------------------------------------------- + + # To save some more typing in Module::Install installers, every... + # use inc::Module::Install + # ...also acts as an implicit use strict. + $^H |= strict::bits(qw(refs subs vars)); + + #------------------------------------------------------------- + + unless ( -f $self->{file} ) { + foreach my $key (keys %INC) { + delete $INC{$key} if $key =~ /Module\/Install/; + } + + local $^W; + require "$self->{path}/$self->{dispatch}.pm"; + File::Path::mkpath("$self->{prefix}/$self->{author}"); + $self->{admin} = "$self->{name}::$self->{dispatch}"->new( _top => $self ); + $self->{admin}->init; + @_ = ($class, _self => $self); + goto &{"$self->{name}::import"}; + } + + local $^W; + *{"${who}::AUTOLOAD"} = $self->autoload; + $self->preload; + + # Unregister loader and worker packages so subdirs can use them again + delete $INC{'inc/Module/Install.pm'}; + delete $INC{'Module/Install.pm'}; + + # Save to the singleton + $MAIN = $self; + + return 1; +} + +sub autoload { + my $self = shift; + my $who = $self->_caller; + my $cwd = Cwd::cwd(); + my $sym = "${who}::AUTOLOAD"; + $sym->{$cwd} = sub { + my $pwd = Cwd::cwd(); + if ( my $code = $sym->{$pwd} ) { + # Delegate back to parent dirs + goto &$code unless $cwd eq $pwd; + } + unless ($$sym =~ s/([^:]+)$//) { + # XXX: it looks like we can't retrieve the missing function + # via $$sym (usually $main::AUTOLOAD) in this case. + # I'm still wondering if we should slurp Makefile.PL to + # get some context or not ... + my ($package, $file, $line) = caller; + die <<"EOT"; +Unknown function is found at $file line $line. +Execution of $file aborted due to runtime errors. + +If you're a contributor to a project, you may need to install +some Module::Install extensions from CPAN (or other repository). +If you're a user of a module, please contact the author. +EOT + } + my $method = $1; + if ( uc($method) eq $method ) { + # Do nothing + return; + } elsif ( $method =~ /^_/ and $self->can($method) ) { + # Dispatch to the root M:I class + return $self->$method(@_); + } + + # Dispatch to the appropriate plugin + unshift @_, ( $self, $1 ); + goto &{$self->can('call')}; + }; +} + +sub preload { + my $self = shift; + unless ( $self->{extensions} ) { + $self->load_extensions( + "$self->{prefix}/$self->{path}", $self + ); + } + + my @exts = @{$self->{extensions}}; + unless ( @exts ) { + @exts = $self->{admin}->load_all_extensions; + } + + my %seen; + foreach my $obj ( @exts ) { + while (my ($method, $glob) = each %{ref($obj) . '::'}) { + next unless $obj->can($method); + next if $method =~ /^_/; + next if $method eq uc($method); + $seen{$method}++; + } + } + + my $who = $self->_caller; + foreach my $name ( sort keys %seen ) { + local $^W; + *{"${who}::$name"} = sub { + ${"${who}::AUTOLOAD"} = "${who}::$name"; + goto &{"${who}::AUTOLOAD"}; + }; + } +} + +sub new { + my ($class, %args) = @_; + + delete $INC{'FindBin.pm'}; + require FindBin; + + # ignore the prefix on extension modules built from top level. + my $base_path = Cwd::abs_path($FindBin::Bin); + unless ( Cwd::abs_path(Cwd::cwd()) eq $base_path ) { + delete $args{prefix}; + } + return $args{_self} if $args{_self}; + + $args{dispatch} ||= 'Admin'; + $args{prefix} ||= 'inc'; + $args{author} ||= ($^O eq 'VMS' ? '_author' : '.author'); + $args{bundle} ||= 'inc/BUNDLES'; + $args{base} ||= $base_path; + $class =~ s/^\Q$args{prefix}\E:://; + $args{name} ||= $class; + $args{version} ||= $class->VERSION; + unless ( $args{path} ) { + $args{path} = $args{name}; + $args{path} =~ s!::!/!g; + } + $args{file} ||= "$args{base}/$args{prefix}/$args{path}.pm"; + $args{wrote} = 0; + + bless( \%args, $class ); +} + +sub call { + my ($self, $method) = @_; + my $obj = $self->load($method) or return; + splice(@_, 0, 2, $obj); + goto &{$obj->can($method)}; +} + +sub load { + my ($self, $method) = @_; + + $self->load_extensions( + "$self->{prefix}/$self->{path}", $self + ) unless $self->{extensions}; + + foreach my $obj (@{$self->{extensions}}) { + return $obj if $obj->can($method); + } + + my $admin = $self->{admin} or die <<"END_DIE"; +The '$method' method does not exist in the '$self->{prefix}' path! +Please remove the '$self->{prefix}' directory and run $0 again to load it. +END_DIE + + my $obj = $admin->load($method, 1); + push @{$self->{extensions}}, $obj; + + $obj; +} + +sub load_extensions { + my ($self, $path, $top) = @_; + + my $should_reload = 0; + unless ( grep { ! ref $_ and lc $_ eq lc $self->{prefix} } @INC ) { + unshift @INC, $self->{prefix}; + $should_reload = 1; + } + + foreach my $rv ( $self->find_extensions($path) ) { + my ($file, $pkg) = @{$rv}; + next if $self->{pathnames}{$pkg}; + + local $@; + my $new = eval { local $^W; require $file; $pkg->can('new') }; + unless ( $new ) { + warn $@ if $@; + next; + } + $self->{pathnames}{$pkg} = + $should_reload ? delete $INC{$file} : $INC{$file}; + push @{$self->{extensions}}, &{$new}($pkg, _top => $top ); + } + + $self->{extensions} ||= []; +} + +sub find_extensions { + my ($self, $path) = @_; + + my @found; + File::Find::find( sub { + my $file = $File::Find::name; + return unless $file =~ m!^\Q$path\E/(.+)\.pm\Z!is; + my $subpath = $1; + return if lc($subpath) eq lc($self->{dispatch}); + + $file = "$self->{path}/$subpath.pm"; + my $pkg = "$self->{name}::$subpath"; + $pkg =~ s!/!::!g; + + # If we have a mixed-case package name, assume case has been preserved + # correctly. Otherwise, root through the file to locate the case-preserved + # version of the package name. + if ( $subpath eq lc($subpath) || $subpath eq uc($subpath) ) { + my $content = Module::Install::_read($subpath . '.pm'); + my $in_pod = 0; + foreach ( split //, $content ) { + $in_pod = 1 if /^=\w/; + $in_pod = 0 if /^=cut/; + next if ($in_pod || /^=cut/); # skip pod text + next if /^\s*#/; # and comments + if ( m/^\s*package\s+($pkg)\s*;/i ) { + $pkg = $1; + last; + } + } + } + + push @found, [ $file, $pkg ]; + }, $path ) if -d $path; + + @found; +} + + + + + +##################################################################### +# Common Utility Functions + +sub _caller { + my $depth = 0; + my $call = caller($depth); + while ( $call eq __PACKAGE__ ) { + $depth++; + $call = caller($depth); + } + return $call; +} + +# Done in evals to avoid confusing Perl::MinimumVersion +eval( $] >= 5.006 ? <<'END_NEW' : <<'END_OLD' ); die $@ if $@; +sub _read { + local *FH; + open( FH, '<', $_[0] ) or die "open($_[0]): $!"; + my $string = do { local $/; }; + close FH or die "close($_[0]): $!"; + return $string; +} +END_NEW +sub _read { + local *FH; + open( FH, "< $_[0]" ) or die "open($_[0]): $!"; + my $string = do { local $/; }; + close FH or die "close($_[0]): $!"; + return $string; +} +END_OLD + +sub _readperl { + my $string = Module::Install::_read($_[0]); + $string =~ s/(?:\015{1,2}\012|\015|\012)/\n/sg; + $string =~ s/(\n)\n*__(?:DATA|END)__\b.*\z/$1/s; + $string =~ s/\n\n=\w+.+?\n\n=cut\b.+?\n+/\n\n/sg; + return $string; +} + +sub _readpod { + my $string = Module::Install::_read($_[0]); + $string =~ s/(?:\015{1,2}\012|\015|\012)/\n/sg; + return $string if $_[0] =~ /\.pod\z/; + $string =~ s/(^|\n=cut\b.+?\n+)[^=\s].+?\n(\n=\w+|\z)/$1$2/sg; + $string =~ s/\n*=pod\b[^\n]*\n+/\n\n/sg; + $string =~ s/\n*=cut\b[^\n]*\n+/\n\n/sg; + $string =~ s/^\n+//s; + return $string; +} + +# Done in evals to avoid confusing Perl::MinimumVersion +eval( $] >= 5.006 ? <<'END_NEW' : <<'END_OLD' ); die $@ if $@; +sub _write { + local *FH; + open( FH, '>', $_[0] ) or die "open($_[0]): $!"; + foreach ( 1 .. $#_ ) { + print FH $_[$_] or die "print($_[0]): $!"; + } + close FH or die "close($_[0]): $!"; +} +END_NEW +sub _write { + local *FH; + open( FH, "> $_[0]" ) or die "open($_[0]): $!"; + foreach ( 1 .. $#_ ) { + print FH $_[$_] or die "print($_[0]): $!"; + } + close FH or die "close($_[0]): $!"; +} +END_OLD + +# _version is for processing module versions (eg, 1.03_05) not +# Perl versions (eg, 5.8.1). +sub _version ($) { + my $s = shift || 0; + my $d =()= $s =~ /(\.)/g; + if ( $d >= 2 ) { + # Normalise multipart versions + $s =~ s/(\.)(\d{1,3})/sprintf("$1%03d",$2)/eg; + } + $s =~ s/^(\d+)\.?//; + my $l = $1 || 0; + my @v = map { + $_ . '0' x (3 - length $_) + } $s =~ /(\d{1,3})\D?/g; + $l = $l . '.' . join '', @v if @v; + return $l + 0; +} + +sub _cmp ($$) { + _version($_[0]) <=> _version($_[1]); +} + +# Cloned from Params::Util::_CLASS +sub _CLASS ($) { + ( + defined $_[0] + and + ! ref $_[0] + and + $_[0] =~ m/^[^\W\d]\w*(?:::\w+)*\z/s + ) ? $_[0] : undef; +} + +1; + +# Copyright 2008 - 2010 Adam Kennedy. diff -Nru nginx-0.5.33/modules/nginx-echo/test/inc/Spiffy.pm nginx-0.8.53/modules/nginx-echo/test/inc/Spiffy.pm --- nginx-0.5.33/modules/nginx-echo/test/inc/Spiffy.pm 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/modules/nginx-echo/test/inc/Spiffy.pm 2011-02-26 14:04:40.000000000 +0000 @@ -0,0 +1,539 @@ +#line 1 +package Spiffy; +use strict; +use 5.006001; +use warnings; +use Carp; +require Exporter; +our $VERSION = '0.30'; +our @EXPORT = (); +our @EXPORT_BASE = qw(field const stub super); +our @EXPORT_OK = (@EXPORT_BASE, qw(id WWW XXX YYY ZZZ)); +our %EXPORT_TAGS = (XXX => [qw(WWW XXX YYY ZZZ)]); + +my $stack_frame = 0; +my $dump = 'yaml'; +my $bases_map = {}; + +sub WWW; sub XXX; sub YYY; sub ZZZ; + +# This line is here to convince "autouse" into believing we are autousable. +sub can { + ($_[1] eq 'import' and caller()->isa('autouse')) + ? \&Exporter::import # pacify autouse's equality test + : $_[0]->SUPER::can($_[1]) # normal case +} + +# TODO +# +# Exported functions like field and super should be hidden so as not to +# be confused with methods that can be inherited. +# + +sub new { + my $class = shift; + $class = ref($class) || $class; + my $self = bless {}, $class; + while (@_) { + my $method = shift; + $self->$method(shift); + } + return $self; +} + +my $filtered_files = {}; +my $filter_dump = 0; +my $filter_save = 0; +our $filter_result = ''; +sub import { + no strict 'refs'; + no warnings; + my $self_package = shift; + + # XXX Using parse_arguments here might cause confusion, because the + # subclass's boolean_arguments and paired_arguments can conflict, causing + # difficult debugging. Consider using something truly local. + my ($args, @export_list) = do { + local *boolean_arguments = sub { + qw( + -base -Base -mixin -selfless + -XXX -dumper -yaml + -filter_dump -filter_save + ) + }; + local *paired_arguments = sub { qw(-package) }; + $self_package->parse_arguments(@_); + }; + return spiffy_mixin_import(scalar(caller(0)), $self_package, @export_list) + if $args->{-mixin}; + + $filter_dump = 1 if $args->{-filter_dump}; + $filter_save = 1 if $args->{-filter_save}; + $dump = 'yaml' if $args->{-yaml}; + $dump = 'dumper' if $args->{-dumper}; + + local @EXPORT_BASE = @EXPORT_BASE; + + if ($args->{-XXX}) { + push @EXPORT_BASE, @{$EXPORT_TAGS{XXX}} + unless grep /^XXX$/, @EXPORT_BASE; + } + + spiffy_filter() + if ($args->{-selfless} or $args->{-Base}) and + not $filtered_files->{(caller($stack_frame))[1]}++; + + my $caller_package = $args->{-package} || caller($stack_frame); + push @{"$caller_package\::ISA"}, $self_package + if $args->{-Base} or $args->{-base}; + + for my $class (@{all_my_bases($self_package)}) { + next unless $class->isa('Spiffy'); + my @export = grep { + not defined &{"$caller_package\::$_"}; + } ( @{"$class\::EXPORT"}, + ($args->{-Base} or $args->{-base}) + ? @{"$class\::EXPORT_BASE"} : (), + ); + my @export_ok = grep { + not defined &{"$caller_package\::$_"}; + } @{"$class\::EXPORT_OK"}; + + # Avoid calling the expensive Exporter::export + # if there is nothing to do (optimization) + my %exportable = map { ($_, 1) } @export, @export_ok; + next unless keys %exportable; + + my @export_save = @{"$class\::EXPORT"}; + my @export_ok_save = @{"$class\::EXPORT_OK"}; + @{"$class\::EXPORT"} = @export; + @{"$class\::EXPORT_OK"} = @export_ok; + my @list = grep { + (my $v = $_) =~ s/^[\!\:]//; + $exportable{$v} or ${"$class\::EXPORT_TAGS"}{$v}; + } @export_list; + Exporter::export($class, $caller_package, @list); + @{"$class\::EXPORT"} = @export_save; + @{"$class\::EXPORT_OK"} = @export_ok_save; + } +} + +sub spiffy_filter { + require Filter::Util::Call; + my $done = 0; + Filter::Util::Call::filter_add( + sub { + return 0 if $done; + my ($data, $end) = ('', ''); + while (my $status = Filter::Util::Call::filter_read()) { + return $status if $status < 0; + if (/^__(?:END|DATA)__\r?$/) { + $end = $_; + last; + } + $data .= $_; + $_ = ''; + } + $_ = $data; + my @my_subs; + s[^(sub\s+\w+\s+\{)(.*\n)] + [${1}my \$self = shift;$2]gm; + s[^(sub\s+\w+)\s*\(\s*\)(\s+\{.*\n)] + [${1}${2}]gm; + s[^my\s+sub\s+(\w+)(\s+\{)(.*)((?s:.*?\n))\}\n] + [push @my_subs, $1; "\$$1 = sub$2my \$self = shift;$3$4\};\n"]gem; + my $preclare = ''; + if (@my_subs) { + $preclare = join ',', map "\$$_", @my_subs; + $preclare = "my($preclare);"; + } + $_ = "use strict;use warnings;$preclare${_};1;\n$end"; + if ($filter_dump) { print; exit } + if ($filter_save) { $filter_result = $_; $_ = $filter_result; } + $done = 1; + } + ); +} + +sub base { + push @_, -base; + goto &import; +} + +sub all_my_bases { + my $class = shift; + + return $bases_map->{$class} + if defined $bases_map->{$class}; + + my @bases = ($class); + no strict 'refs'; + for my $base_class (@{"${class}::ISA"}) { + push @bases, @{all_my_bases($base_class)}; + } + my $used = {}; + $bases_map->{$class} = [grep {not $used->{$_}++} @bases]; +} + +my %code = ( + sub_start => + "sub {\n", + set_default => + " \$_[0]->{%s} = %s\n unless exists \$_[0]->{%s};\n", + init => + " return \$_[0]->{%s} = do { my \$self = \$_[0]; %s }\n" . + " unless \$#_ > 0 or defined \$_[0]->{%s};\n", + weak_init => + " return do {\n" . + " \$_[0]->{%s} = do { my \$self = \$_[0]; %s };\n" . + " Scalar::Util::weaken(\$_[0]->{%s}) if ref \$_[0]->{%s};\n" . + " \$_[0]->{%s};\n" . + " } unless \$#_ > 0 or defined \$_[0]->{%s};\n", + return_if_get => + " return \$_[0]->{%s} unless \$#_ > 0;\n", + set => + " \$_[0]->{%s} = \$_[1];\n", + weaken => + " Scalar::Util::weaken(\$_[0]->{%s}) if ref \$_[0]->{%s};\n", + sub_end => + " return \$_[0]->{%s};\n}\n", +); + +sub field { + my $package = caller; + my ($args, @values) = do { + no warnings; + local *boolean_arguments = sub { (qw(-weak)) }; + local *paired_arguments = sub { (qw(-package -init)) }; + Spiffy->parse_arguments(@_); + }; + my ($field, $default) = @values; + $package = $args->{-package} if defined $args->{-package}; + die "Cannot have a default for a weakened field ($field)" + if defined $default && $args->{-weak}; + return if defined &{"${package}::$field"}; + require Scalar::Util if $args->{-weak}; + my $default_string = + ( ref($default) eq 'ARRAY' and not @$default ) + ? '[]' + : (ref($default) eq 'HASH' and not keys %$default ) + ? '{}' + : default_as_code($default); + + my $code = $code{sub_start}; + if ($args->{-init}) { + my $fragment = $args->{-weak} ? $code{weak_init} : $code{init}; + $code .= sprintf $fragment, $field, $args->{-init}, ($field) x 4; + } + $code .= sprintf $code{set_default}, $field, $default_string, $field + if defined $default; + $code .= sprintf $code{return_if_get}, $field; + $code .= sprintf $code{set}, $field; + $code .= sprintf $code{weaken}, $field, $field + if $args->{-weak}; + $code .= sprintf $code{sub_end}, $field; + + my $sub = eval $code; + die $@ if $@; + no strict 'refs'; + *{"${package}::$field"} = $sub; + return $code if defined wantarray; +} + +sub default_as_code { + require Data::Dumper; + local $Data::Dumper::Sortkeys = 1; + my $code = Data::Dumper::Dumper(shift); + $code =~ s/^\$VAR1 = //; + $code =~ s/;$//; + return $code; +} + +sub const { + my $package = caller; + my ($args, @values) = do { + no warnings; + local *paired_arguments = sub { (qw(-package)) }; + Spiffy->parse_arguments(@_); + }; + my ($field, $default) = @values; + $package = $args->{-package} if defined $args->{-package}; + no strict 'refs'; + return if defined &{"${package}::$field"}; + *{"${package}::$field"} = sub { $default } +} + +sub stub { + my $package = caller; + my ($args, @values) = do { + no warnings; + local *paired_arguments = sub { (qw(-package)) }; + Spiffy->parse_arguments(@_); + }; + my ($field, $default) = @values; + $package = $args->{-package} if defined $args->{-package}; + no strict 'refs'; + return if defined &{"${package}::$field"}; + *{"${package}::$field"} = + sub { + require Carp; + Carp::confess + "Method $field in package $package must be subclassed"; + } +} + +sub parse_arguments { + my $class = shift; + my ($args, @values) = ({}, ()); + my %booleans = map { ($_, 1) } $class->boolean_arguments; + my %pairs = map { ($_, 1) } $class->paired_arguments; + while (@_) { + my $elem = shift; + if (defined $elem and defined $booleans{$elem}) { + $args->{$elem} = (@_ and $_[0] =~ /^[01]$/) + ? shift + : 1; + } + elsif (defined $elem and defined $pairs{$elem} and @_) { + $args->{$elem} = shift; + } + else { + push @values, $elem; + } + } + return wantarray ? ($args, @values) : $args; +} + +sub boolean_arguments { () } +sub paired_arguments { () } + +# get a unique id for any node +sub id { + if (not ref $_[0]) { + return 'undef' if not defined $_[0]; + \$_[0] =~ /\((\w+)\)$/o or die; + return "$1-S"; + } + require overload; + overload::StrVal($_[0]) =~ /\((\w+)\)$/o or die; + return $1; +} + +#=============================================================================== +# It's super, man. +#=============================================================================== +package DB; +{ + no warnings 'redefine'; + sub super_args { + my @dummy = caller(@_ ? $_[0] : 2); + return @DB::args; + } +} + +package Spiffy; +sub super { + my $method; + my $frame = 1; + while ($method = (caller($frame++))[3]) { + $method =~ s/.*::// and last; + } + my @args = DB::super_args($frame); + @_ = @_ ? ($args[0], @_) : @args; + my $class = ref $_[0] ? ref $_[0] : $_[0]; + my $caller_class = caller; + my $seen = 0; + my @super_classes = reverse grep { + ($seen or $seen = ($_ eq $caller_class)) ? 0 : 1; + } reverse @{all_my_bases($class)}; + for my $super_class (@super_classes) { + no strict 'refs'; + next if $super_class eq $class; + if (defined &{"${super_class}::$method"}) { + ${"$super_class\::AUTOLOAD"} = ${"$class\::AUTOLOAD"} + if $method eq 'AUTOLOAD'; + return &{"${super_class}::$method"}; + } + } + return; +} + +#=============================================================================== +# This code deserves a spanking, because it is being very naughty. +# It is exchanging base.pm's import() for its own, so that people +# can use base.pm with Spiffy modules, without being the wiser. +#=============================================================================== +my $real_base_import; +my $real_mixin_import; + +BEGIN { + require base unless defined $INC{'base.pm'}; + $INC{'mixin.pm'} ||= 'Spiffy/mixin.pm'; + $real_base_import = \&base::import; + $real_mixin_import = \&mixin::import; + no warnings; + *base::import = \&spiffy_base_import; + *mixin::import = \&spiffy_mixin_import; +} + +# my $i = 0; +# while (my $caller = caller($i++)) { +# next unless $caller eq 'base' or $caller eq 'mixin'; +# croak <isa('Spiffy'); + } @base_classes; + my $inheritor = caller(0); + for my $base_class (@base_classes) { + next if $inheritor->isa($base_class); + croak "Can't mix Spiffy and non-Spiffy classes in 'use base'.\n", + "See the documentation of Spiffy.pm for details\n " + unless $base_class->isa('Spiffy'); + $stack_frame = 1; # tell import to use different caller + import($base_class, '-base'); + $stack_frame = 0; + } +} + +sub mixin { + my $self = shift; + my $target_class = ref($self); + spiffy_mixin_import($target_class, @_) +} + +sub spiffy_mixin_import { + my $target_class = shift; + $target_class = caller(0) + if $target_class eq 'mixin'; + my $mixin_class = shift + or die "Nothing to mixin"; + eval "require $mixin_class"; + my @roles = @_; + my $pseudo_class = join '-', $target_class, $mixin_class, @roles; + my %methods = spiffy_mixin_methods($mixin_class, @roles); + no strict 'refs'; + no warnings; + @{"$pseudo_class\::ISA"} = @{"$target_class\::ISA"}; + @{"$target_class\::ISA"} = ($pseudo_class); + for (keys %methods) { + *{"$pseudo_class\::$_"} = $methods{$_}; + } +} + +sub spiffy_mixin_methods { + my $mixin_class = shift; + no strict 'refs'; + my %methods = spiffy_all_methods($mixin_class); + map { + $methods{$_} + ? ($_, \ &{"$methods{$_}\::$_"}) + : ($_, \ &{"$mixin_class\::$_"}) + } @_ + ? (get_roles($mixin_class, @_)) + : (keys %methods); +} + +sub get_roles { + my $mixin_class = shift; + my @roles = @_; + while (grep /^!*:/, @roles) { + @roles = map { + s/!!//g; + /^!:(.*)/ ? do { + my $m = "_role_$1"; + map("!$_", $mixin_class->$m); + } : + /^:(.*)/ ? do { + my $m = "_role_$1"; + ($mixin_class->$m); + } : + ($_) + } @roles; + } + if (@roles and $roles[0] =~ /^!/) { + my %methods = spiffy_all_methods($mixin_class); + unshift @roles, keys(%methods); + } + my %roles; + for (@roles) { + s/!!//g; + delete $roles{$1}, next + if /^!(.*)/; + $roles{$_} = 1; + } + keys %roles; +} + +sub spiffy_all_methods { + no strict 'refs'; + my $class = shift; + return if $class eq 'Spiffy'; + my %methods = map { + ($_, $class) + } grep { + defined &{"$class\::$_"} and not /^_/ + } keys %{"$class\::"}; + my %super_methods; + %super_methods = spiffy_all_methods(${"$class\::ISA"}[0]) + if @{"$class\::ISA"}; + %{{%super_methods, %methods}}; +} + + +# END of naughty code. +#=============================================================================== +# Debugging support +#=============================================================================== +sub spiffy_dump { + no warnings; + if ($dump eq 'dumper') { + require Data::Dumper; + $Data::Dumper::Sortkeys = 1; + $Data::Dumper::Indent = 1; + return Data::Dumper::Dumper(@_); + } + require YAML; + $YAML::UseVersion = 0; + return YAML::Dump(@_) . "...\n"; +} + +sub at_line_number { + my ($file_path, $line_number) = (caller(1))[1,2]; + " at $file_path line $line_number\n"; +} + +sub WWW { + warn spiffy_dump(@_) . at_line_number; + return wantarray ? @_ : $_[0]; +} + +sub XXX { + die spiffy_dump(@_) . at_line_number; +} + +sub YYY { + print spiffy_dump(@_) . at_line_number; + return wantarray ? @_ : $_[0]; +} + +sub ZZZ { + require Carp; + Carp::confess spiffy_dump(@_); +} + +1; + +__END__ + +#line 1066 diff -Nru nginx-0.5.33/modules/nginx-echo/test/inc/Test/Base.pm nginx-0.8.53/modules/nginx-echo/test/inc/Test/Base.pm --- nginx-0.5.33/modules/nginx-echo/test/inc/Test/Base.pm 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/modules/nginx-echo/test/inc/Test/Base.pm 2011-02-26 14:04:40.000000000 +0000 @@ -0,0 +1,684 @@ +#line 1 +# TODO: +# +package Test::Base; +use 5.006001; +use Spiffy 0.30 -Base; +use Spiffy ':XXX'; +our $VERSION = '0.59'; + +my @test_more_exports; +BEGIN { + @test_more_exports = qw( + ok isnt like unlike is_deeply cmp_ok + skip todo_skip pass fail + eq_array eq_hash eq_set + plan can_ok isa_ok diag + use_ok + $TODO + ); +} + +use Test::More import => \@test_more_exports; +use Carp; + +our @EXPORT = (@test_more_exports, qw( + is no_diff + + blocks next_block first_block + delimiters spec_file spec_string + filters filters_delay filter_arguments + run run_compare run_is run_is_deeply run_like run_unlike + skip_all_unless_require is_deep run_is_deep + WWW XXX YYY ZZZ + tie_output no_diag_on_only + + find_my_self default_object + + croak carp cluck confess +)); + +field '_spec_file'; +field '_spec_string'; +field _filters => [qw(norm trim)]; +field _filters_map => {}; +field spec => + -init => '$self->_spec_init'; +field block_list => + -init => '$self->_block_list_init'; +field _next_list => []; +field block_delim => + -init => '$self->block_delim_default'; +field data_delim => + -init => '$self->data_delim_default'; +field _filters_delay => 0; +field _no_diag_on_only => 0; + +field block_delim_default => '==='; +field data_delim_default => '---'; + +my $default_class; +my $default_object; +my $reserved_section_names = {}; + +sub default_object { + $default_object ||= $default_class->new; + return $default_object; +} + +my $import_called = 0; +sub import() { + $import_called = 1; + my $class = (grep /^-base$/i, @_) + ? scalar(caller) + : $_[0]; + if (not defined $default_class) { + $default_class = $class; + } +# else { +# croak "Can't use $class after using $default_class" +# unless $default_class->isa($class); +# } + + unless (grep /^-base$/i, @_) { + my @args; + for (my $ii = 1; $ii <= $#_; ++$ii) { + if ($_[$ii] eq '-package') { + ++$ii; + } else { + push @args, $_[$ii]; + } + } + Test::More->import(import => \@test_more_exports, @args) + if @args; + } + + _strict_warnings(); + goto &Spiffy::import; +} + +# Wrap Test::Builder::plan +my $plan_code = \&Test::Builder::plan; +my $Have_Plan = 0; +{ + no warnings 'redefine'; + *Test::Builder::plan = sub { + $Have_Plan = 1; + goto &$plan_code; + }; +} + +my $DIED = 0; +$SIG{__DIE__} = sub { $DIED = 1; die @_ }; + +sub block_class { $self->find_class('Block') } +sub filter_class { $self->find_class('Filter') } + +sub find_class { + my $suffix = shift; + my $class = ref($self) . "::$suffix"; + return $class if $class->can('new'); + $class = __PACKAGE__ . "::$suffix"; + return $class if $class->can('new'); + eval "require $class"; + return $class if $class->can('new'); + die "Can't find a class for $suffix"; +} + +sub check_late { + if ($self->{block_list}) { + my $caller = (caller(1))[3]; + $caller =~ s/.*:://; + croak "Too late to call $caller()" + } +} + +sub find_my_self() { + my $self = ref($_[0]) eq $default_class + ? splice(@_, 0, 1) + : default_object(); + return $self, @_; +} + +sub blocks() { + (my ($self), @_) = find_my_self(@_); + + croak "Invalid arguments passed to 'blocks'" + if @_ > 1; + croak sprintf("'%s' is invalid argument to blocks()", shift(@_)) + if @_ && $_[0] !~ /^[a-zA-Z]\w*$/; + + my $blocks = $self->block_list; + + my $section_name = shift || ''; + my @blocks = $section_name + ? (grep { exists $_->{$section_name} } @$blocks) + : (@$blocks); + + return scalar(@blocks) unless wantarray; + + return (@blocks) if $self->_filters_delay; + + for my $block (@blocks) { + $block->run_filters + unless $block->is_filtered; + } + + return (@blocks); +} + +sub next_block() { + (my ($self), @_) = find_my_self(@_); + my $list = $self->_next_list; + if (@$list == 0) { + $list = [@{$self->block_list}, undef]; + $self->_next_list($list); + } + my $block = shift @$list; + if (defined $block and not $block->is_filtered) { + $block->run_filters; + } + return $block; +} + +sub first_block() { + (my ($self), @_) = find_my_self(@_); + $self->_next_list([]); + $self->next_block; +} + +sub filters_delay() { + (my ($self), @_) = find_my_self(@_); + $self->_filters_delay(defined $_[0] ? shift : 1); +} + +sub no_diag_on_only() { + (my ($self), @_) = find_my_self(@_); + $self->_no_diag_on_only(defined $_[0] ? shift : 1); +} + +sub delimiters() { + (my ($self), @_) = find_my_self(@_); + $self->check_late; + my ($block_delimiter, $data_delimiter) = @_; + $block_delimiter ||= $self->block_delim_default; + $data_delimiter ||= $self->data_delim_default; + $self->block_delim($block_delimiter); + $self->data_delim($data_delimiter); + return $self; +} + +sub spec_file() { + (my ($self), @_) = find_my_self(@_); + $self->check_late; + $self->_spec_file(shift); + return $self; +} + +sub spec_string() { + (my ($self), @_) = find_my_self(@_); + $self->check_late; + $self->_spec_string(shift); + return $self; +} + +sub filters() { + (my ($self), @_) = find_my_self(@_); + if (ref($_[0]) eq 'HASH') { + $self->_filters_map(shift); + } + else { + my $filters = $self->_filters; + push @$filters, @_; + } + return $self; +} + +sub filter_arguments() { + $Test::Base::Filter::arguments; +} + +sub have_text_diff { + eval { require Text::Diff; 1 } && + $Text::Diff::VERSION >= 0.35 && + $Algorithm::Diff::VERSION >= 1.15; +} + +sub is($$;$) { + (my ($self), @_) = find_my_self(@_); + my ($actual, $expected, $name) = @_; + local $Test::Builder::Level = $Test::Builder::Level + 1; + if ($ENV{TEST_SHOW_NO_DIFFS} or + not defined $actual or + not defined $expected or + $actual eq $expected or + not($self->have_text_diff) or + $expected !~ /\n./s + ) { + Test::More::is($actual, $expected, $name); + } + else { + $name = '' unless defined $name; + ok $actual eq $expected, + $name . "\n" . Text::Diff::diff(\$expected, \$actual); + } +} + +sub run(&;$) { + (my ($self), @_) = find_my_self(@_); + my $callback = shift; + for my $block (@{$self->block_list}) { + $block->run_filters unless $block->is_filtered; + &{$callback}($block); + } +} + +my $name_error = "Can't determine section names"; +sub _section_names { + return @_ if @_ == 2; + my $block = $self->first_block + or croak $name_error; + my @names = grep { + $_ !~ /^(ONLY|LAST|SKIP)$/; + } @{$block->{_section_order}[0] || []}; + croak "$name_error. Need two sections in first block" + unless @names == 2; + return @names; +} + +sub _assert_plan { + plan('no_plan') unless $Have_Plan; +} + +sub END { + run_compare() unless $Have_Plan or $DIED or not $import_called; +} + +sub run_compare() { + (my ($self), @_) = find_my_self(@_); + $self->_assert_plan; + my ($x, $y) = $self->_section_names(@_); + local $Test::Builder::Level = $Test::Builder::Level + 1; + for my $block (@{$self->block_list}) { + next unless exists($block->{$x}) and exists($block->{$y}); + $block->run_filters unless $block->is_filtered; + if (ref $block->$x) { + is_deeply($block->$x, $block->$y, + $block->name ? $block->name : ()); + } + elsif (ref $block->$y eq 'Regexp') { + my $regexp = ref $y ? $y : $block->$y; + like($block->$x, $regexp, $block->name ? $block->name : ()); + } + else { + is($block->$x, $block->$y, $block->name ? $block->name : ()); + } + } +} + +sub run_is() { + (my ($self), @_) = find_my_self(@_); + $self->_assert_plan; + my ($x, $y) = $self->_section_names(@_); + local $Test::Builder::Level = $Test::Builder::Level + 1; + for my $block (@{$self->block_list}) { + next unless exists($block->{$x}) and exists($block->{$y}); + $block->run_filters unless $block->is_filtered; + is($block->$x, $block->$y, + $block->name ? $block->name : () + ); + } +} + +sub run_is_deeply() { + (my ($self), @_) = find_my_self(@_); + $self->_assert_plan; + my ($x, $y) = $self->_section_names(@_); + for my $block (@{$self->block_list}) { + next unless exists($block->{$x}) and exists($block->{$y}); + $block->run_filters unless $block->is_filtered; + is_deeply($block->$x, $block->$y, + $block->name ? $block->name : () + ); + } +} + +sub run_like() { + (my ($self), @_) = find_my_self(@_); + $self->_assert_plan; + my ($x, $y) = $self->_section_names(@_); + for my $block (@{$self->block_list}) { + next unless exists($block->{$x}) and defined($y); + $block->run_filters unless $block->is_filtered; + my $regexp = ref $y ? $y : $block->$y; + like($block->$x, $regexp, + $block->name ? $block->name : () + ); + } +} + +sub run_unlike() { + (my ($self), @_) = find_my_self(@_); + $self->_assert_plan; + my ($x, $y) = $self->_section_names(@_); + for my $block (@{$self->block_list}) { + next unless exists($block->{$x}) and defined($y); + $block->run_filters unless $block->is_filtered; + my $regexp = ref $y ? $y : $block->$y; + unlike($block->$x, $regexp, + $block->name ? $block->name : () + ); + } +} + +sub skip_all_unless_require() { + (my ($self), @_) = find_my_self(@_); + my $module = shift; + eval "require $module; 1" + or Test::More::plan( + skip_all => "$module failed to load" + ); +} + +sub is_deep() { + (my ($self), @_) = find_my_self(@_); + require Test::Deep; + Test::Deep::cmp_deeply(@_); +} + +sub run_is_deep() { + (my ($self), @_) = find_my_self(@_); + $self->_assert_plan; + my ($x, $y) = $self->_section_names(@_); + for my $block (@{$self->block_list}) { + next unless exists($block->{$x}) and exists($block->{$y}); + $block->run_filters unless $block->is_filtered; + is_deep($block->$x, $block->$y, + $block->name ? $block->name : () + ); + } +} + +sub _pre_eval { + my $spec = shift; + return $spec unless $spec =~ + s/\A\s*<<<(.*?)>>>\s*$//sm; + my $eval_code = $1; + eval "package main; $eval_code"; + croak $@ if $@; + return $spec; +} + +sub _block_list_init { + my $spec = $self->spec; + $spec = $self->_pre_eval($spec); + my $cd = $self->block_delim; + my @hunks = ($spec =~ /^(\Q${cd}\E.*?(?=^\Q${cd}\E|\z))/msg); + my $blocks = $self->_choose_blocks(@hunks); + $self->block_list($blocks); # Need to set early for possible filter use + my $seq = 1; + for my $block (@$blocks) { + $block->blocks_object($self); + $block->seq_num($seq++); + } + return $blocks; +} + +sub _choose_blocks { + my $blocks = []; + for my $hunk (@_) { + my $block = $self->_make_block($hunk); + if (exists $block->{ONLY}) { + diag "I found ONLY: maybe you're debugging?" + unless $self->_no_diag_on_only; + return [$block]; + } + next if exists $block->{SKIP}; + push @$blocks, $block; + if (exists $block->{LAST}) { + return $blocks; + } + } + return $blocks; +} + +sub _check_reserved { + my $id = shift; + croak "'$id' is a reserved name. Use something else.\n" + if $reserved_section_names->{$id} or + $id =~ /^_/; +} + +sub _make_block { + my $hunk = shift; + my $cd = $self->block_delim; + my $dd = $self->data_delim; + my $block = $self->block_class->new; + $hunk =~ s/\A\Q${cd}\E[ \t]*(.*)\s+// or die; + my $name = $1; + my @parts = split /^\Q${dd}\E +\(?(\w+)\)? *(.*)?\n/m, $hunk; + my $description = shift @parts; + $description ||= ''; + unless ($description =~ /\S/) { + $description = $name; + } + $description =~ s/\s*\z//; + $block->set_value(description => $description); + + my $section_map = {}; + my $section_order = []; + while (@parts) { + my ($type, $filters, $value) = splice(@parts, 0, 3); + $self->_check_reserved($type); + $value = '' unless defined $value; + $filters = '' unless defined $filters; + if ($filters =~ /:(\s|\z)/) { + croak "Extra lines not allowed in '$type' section" + if $value =~ /\S/; + ($filters, $value) = split /\s*:(?:\s+|\z)/, $filters, 2; + $value = '' unless defined $value; + $value =~ s/^\s*(.*?)\s*$/$1/; + } + $section_map->{$type} = { + filters => $filters, + }; + push @$section_order, $type; + $block->set_value($type, $value); + } + $block->set_value(name => $name); + $block->set_value(_section_map => $section_map); + $block->set_value(_section_order => $section_order); + return $block; +} + +sub _spec_init { + return $self->_spec_string + if $self->_spec_string; + local $/; + my $spec; + if (my $spec_file = $self->_spec_file) { + open FILE, $spec_file or die $!; + $spec = ; + close FILE; + } + else { + $spec = do { + package main; + no warnings 'once'; + ; + }; + } + return $spec; +} + +sub _strict_warnings() { + require Filter::Util::Call; + my $done = 0; + Filter::Util::Call::filter_add( + sub { + return 0 if $done; + my ($data, $end) = ('', ''); + while (my $status = Filter::Util::Call::filter_read()) { + return $status if $status < 0; + if (/^__(?:END|DATA)__\r?$/) { + $end = $_; + last; + } + $data .= $_; + $_ = ''; + } + $_ = "use strict;use warnings;$data$end"; + $done = 1; + } + ); +} + +sub tie_output() { + my $handle = shift; + die "No buffer to tie" unless @_; + tie $handle, 'Test::Base::Handle', $_[0]; +} + +sub no_diff { + $ENV{TEST_SHOW_NO_DIFFS} = 1; +} + +package Test::Base::Handle; + +sub TIEHANDLE() { + my $class = shift; + bless \ $_[0], $class; +} + +sub PRINT { + $$self .= $_ for @_; +} + +#=============================================================================== +# Test::Base::Block +# +# This is the default class for accessing a Test::Base block object. +#=============================================================================== +package Test::Base::Block; +our @ISA = qw(Spiffy); + +our @EXPORT = qw(block_accessor); + +sub AUTOLOAD { + return; +} + +sub block_accessor() { + my $accessor = shift; + no strict 'refs'; + return if defined &$accessor; + *$accessor = sub { + my $self = shift; + if (@_) { + Carp::croak "Not allowed to set values for '$accessor'"; + } + my @list = @{$self->{$accessor} || []}; + return wantarray + ? (@list) + : $list[0]; + }; +} + +block_accessor 'name'; +block_accessor 'description'; +Spiffy::field 'seq_num'; +Spiffy::field 'is_filtered'; +Spiffy::field 'blocks_object'; +Spiffy::field 'original_values' => {}; + +sub set_value { + no strict 'refs'; + my $accessor = shift; + block_accessor $accessor + unless defined &$accessor; + $self->{$accessor} = [@_]; +} + +sub run_filters { + my $map = $self->_section_map; + my $order = $self->_section_order; + Carp::croak "Attempt to filter a block twice" + if $self->is_filtered; + for my $type (@$order) { + my $filters = $map->{$type}{filters}; + my @value = $self->$type; + $self->original_values->{$type} = $value[0]; + for my $filter ($self->_get_filters($type, $filters)) { + $Test::Base::Filter::arguments = + $filter =~ s/=(.*)$// ? $1 : undef; + my $function = "main::$filter"; + no strict 'refs'; + if (defined &$function) { + local $_ = + (@value == 1 and not defined($value[0])) ? undef : + join '', @value; + my $old = $_; + @value = &$function(@value); + if (not(@value) or + @value == 1 and defined($value[0]) and $value[0] =~ /\A(\d+|)\z/ + ) { + if ($value[0] && $_ eq $old) { + Test::Base::diag("Filters returning numbers are supposed to do munging \$_: your filter '$function' apparently doesn't."); + } + @value = ($_); + } + } + else { + my $filter_object = $self->blocks_object->filter_class->new; + die "Can't find a function or method for '$filter' filter\n" + unless $filter_object->can($filter); + $filter_object->current_block($self); + @value = $filter_object->$filter(@value); + } + # Set the value after each filter since other filters may be + # introspecting. + $self->set_value($type, @value); + } + } + $self->is_filtered(1); +} + +sub _get_filters { + my $type = shift; + my $string = shift || ''; + $string =~ s/\s*(.*?)\s*/$1/; + my @filters = (); + my $map_filters = $self->blocks_object->_filters_map->{$type} || []; + $map_filters = [ $map_filters ] unless ref $map_filters; + my @append = (); + for ( + @{$self->blocks_object->_filters}, + @$map_filters, + split(/\s+/, $string), + ) { + my $filter = $_; + last unless length $filter; + if ($filter =~ s/^-//) { + @filters = grep { $_ ne $filter } @filters; + } + elsif ($filter =~ s/^\+//) { + push @append, $filter; + } + else { + push @filters, $filter; + } + } + return @filters, @append; +} + +{ + %$reserved_section_names = map { + ($_, 1); + } keys(%Test::Base::Block::), qw( new DESTROY ); +} + +__DATA__ + +=encoding utf8 + +#line 1376 diff -Nru nginx-0.5.33/modules/nginx-echo/test/inc/Test/Builder/Module.pm nginx-0.8.53/modules/nginx-echo/test/inc/Test/Builder/Module.pm --- nginx-0.5.33/modules/nginx-echo/test/inc/Test/Builder/Module.pm 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/modules/nginx-echo/test/inc/Test/Builder/Module.pm 2011-02-26 14:04:40.000000000 +0000 @@ -0,0 +1,81 @@ +#line 1 +package Test::Builder::Module; + +use strict; + +use Test::Builder; + +require Exporter; +our @ISA = qw(Exporter); + +our $VERSION = '0.92'; +$VERSION = eval $VERSION; ## no critic (BuiltinFunctions::ProhibitStringyEval) + +# 5.004's Exporter doesn't have export_to_level. +my $_export_to_level = sub { + my $pkg = shift; + my $level = shift; + (undef) = shift; # redundant arg + my $callpkg = caller($level); + $pkg->export( $callpkg, @_ ); +}; + +#line 82 + +sub import { + my($class) = shift; + + # Don't run all this when loading ourself. + return 1 if $class eq 'Test::Builder::Module'; + + my $test = $class->builder; + + my $caller = caller; + + $test->exported_to($caller); + + $class->import_extra( \@_ ); + my(@imports) = $class->_strip_imports( \@_ ); + + $test->plan(@_); + + $class->$_export_to_level( 1, $class, @imports ); +} + +sub _strip_imports { + my $class = shift; + my $list = shift; + + my @imports = (); + my @other = (); + my $idx = 0; + while( $idx <= $#{$list} ) { + my $item = $list->[$idx]; + + if( defined $item and $item eq 'import' ) { + push @imports, @{ $list->[ $idx + 1 ] }; + $idx++; + } + else { + push @other, $item; + } + + $idx++; + } + + @$list = @other; + + return @imports; +} + +#line 145 + +sub import_extra { } + +#line 175 + +sub builder { + return Test::Builder->new; +} + +1; diff -Nru nginx-0.5.33/modules/nginx-echo/test/inc/Test/Builder.pm nginx-0.8.53/modules/nginx-echo/test/inc/Test/Builder.pm --- nginx-0.5.33/modules/nginx-echo/test/inc/Test/Builder.pm 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/modules/nginx-echo/test/inc/Test/Builder.pm 2011-02-26 14:04:40.000000000 +0000 @@ -0,0 +1,1413 @@ +#line 1 +package Test::Builder; + +use 5.006; +use strict; +use warnings; + +our $VERSION = '0.92'; +$VERSION = eval $VERSION; ## no critic (BuiltinFunctions::ProhibitStringyEval) + +BEGIN { + if( $] < 5.008 ) { + require Test::Builder::IO::Scalar; + } +} + + +# Make Test::Builder thread-safe for ithreads. +BEGIN { + use Config; + # Load threads::shared when threads are turned on. + # 5.8.0's threads are so busted we no longer support them. + if( $] >= 5.008001 && $Config{useithreads} && $INC{'threads.pm'} ) { + require threads::shared; + + # Hack around YET ANOTHER threads::shared bug. It would + # occassionally forget the contents of the variable when sharing it. + # So we first copy the data, then share, then put our copy back. + *share = sub (\[$@%]) { + my $type = ref $_[0]; + my $data; + + if( $type eq 'HASH' ) { + %$data = %{ $_[0] }; + } + elsif( $type eq 'ARRAY' ) { + @$data = @{ $_[0] }; + } + elsif( $type eq 'SCALAR' ) { + $$data = ${ $_[0] }; + } + else { + die( "Unknown type: " . $type ); + } + + $_[0] = &threads::shared::share( $_[0] ); + + if( $type eq 'HASH' ) { + %{ $_[0] } = %$data; + } + elsif( $type eq 'ARRAY' ) { + @{ $_[0] } = @$data; + } + elsif( $type eq 'SCALAR' ) { + ${ $_[0] } = $$data; + } + else { + die( "Unknown type: " . $type ); + } + + return $_[0]; + }; + } + # 5.8.0's threads::shared is busted when threads are off + # and earlier Perls just don't have that module at all. + else { + *share = sub { return $_[0] }; + *lock = sub { 0 }; + } +} + +#line 117 + +my $Test = Test::Builder->new; + +sub new { + my($class) = shift; + $Test ||= $class->create; + return $Test; +} + +#line 139 + +sub create { + my $class = shift; + + my $self = bless {}, $class; + $self->reset; + + return $self; +} + +#line 158 + +our $Level; + +sub reset { ## no critic (Subroutines::ProhibitBuiltinHomonyms) + my($self) = @_; + + # We leave this a global because it has to be localized and localizing + # hash keys is just asking for pain. Also, it was documented. + $Level = 1; + + $self->{Have_Plan} = 0; + $self->{No_Plan} = 0; + $self->{Have_Output_Plan} = 0; + + $self->{Original_Pid} = $$; + + share( $self->{Curr_Test} ); + $self->{Curr_Test} = 0; + $self->{Test_Results} = &share( [] ); + + $self->{Exported_To} = undef; + $self->{Expected_Tests} = 0; + + $self->{Skip_All} = 0; + + $self->{Use_Nums} = 1; + + $self->{No_Header} = 0; + $self->{No_Ending} = 0; + + $self->{Todo} = undef; + $self->{Todo_Stack} = []; + $self->{Start_Todo} = 0; + $self->{Opened_Testhandles} = 0; + + $self->_dup_stdhandles; + + return; +} + +#line 219 + +my %plan_cmds = ( + no_plan => \&no_plan, + skip_all => \&skip_all, + tests => \&_plan_tests, +); + +sub plan { + my( $self, $cmd, $arg ) = @_; + + return unless $cmd; + + local $Level = $Level + 1; + + $self->croak("You tried to plan twice") if $self->{Have_Plan}; + + if( my $method = $plan_cmds{$cmd} ) { + local $Level = $Level + 1; + $self->$method($arg); + } + else { + my @args = grep { defined } ( $cmd, $arg ); + $self->croak("plan() doesn't understand @args"); + } + + return 1; +} + + +sub _plan_tests { + my($self, $arg) = @_; + + if($arg) { + local $Level = $Level + 1; + return $self->expected_tests($arg); + } + elsif( !defined $arg ) { + $self->croak("Got an undefined number of tests"); + } + else { + $self->croak("You said to run 0 tests"); + } + + return; +} + + +#line 275 + +sub expected_tests { + my $self = shift; + my($max) = @_; + + if(@_) { + $self->croak("Number of tests must be a positive integer. You gave it '$max'") + unless $max =~ /^\+?\d+$/; + + $self->{Expected_Tests} = $max; + $self->{Have_Plan} = 1; + + $self->_output_plan($max) unless $self->no_header; + } + return $self->{Expected_Tests}; +} + +#line 299 + +sub no_plan { + my($self, $arg) = @_; + + $self->carp("no_plan takes no arguments") if $arg; + + $self->{No_Plan} = 1; + $self->{Have_Plan} = 1; + + return 1; +} + + +#line 333 + +sub _output_plan { + my($self, $max, $directive, $reason) = @_; + + $self->carp("The plan was already output") if $self->{Have_Output_Plan}; + + my $plan = "1..$max"; + $plan .= " # $directive" if defined $directive; + $plan .= " $reason" if defined $reason; + + $self->_print("$plan\n"); + + $self->{Have_Output_Plan} = 1; + + return; +} + +#line 384 + +sub done_testing { + my($self, $num_tests) = @_; + + # If done_testing() specified the number of tests, shut off no_plan. + if( defined $num_tests ) { + $self->{No_Plan} = 0; + } + else { + $num_tests = $self->current_test; + } + + if( $self->{Done_Testing} ) { + my($file, $line) = @{$self->{Done_Testing}}[1,2]; + $self->ok(0, "done_testing() was already called at $file line $line"); + return; + } + + $self->{Done_Testing} = [caller]; + + if( $self->expected_tests && $num_tests != $self->expected_tests ) { + $self->ok(0, "planned to run @{[ $self->expected_tests ]} ". + "but done_testing() expects $num_tests"); + } + else { + $self->{Expected_Tests} = $num_tests; + } + + $self->_output_plan($num_tests) unless $self->{Have_Output_Plan}; + + $self->{Have_Plan} = 1; + + return 1; +} + + +#line 429 + +sub has_plan { + my $self = shift; + + return( $self->{Expected_Tests} ) if $self->{Expected_Tests}; + return('no_plan') if $self->{No_Plan}; + return(undef); +} + +#line 446 + +sub skip_all { + my( $self, $reason ) = @_; + + $self->{Skip_All} = 1; + + $self->_output_plan(0, "SKIP", $reason) unless $self->no_header; + exit(0); +} + +#line 468 + +sub exported_to { + my( $self, $pack ) = @_; + + if( defined $pack ) { + $self->{Exported_To} = $pack; + } + return $self->{Exported_To}; +} + +#line 498 + +sub ok { + my( $self, $test, $name ) = @_; + + # $test might contain an object which we don't want to accidentally + # store, so we turn it into a boolean. + $test = $test ? 1 : 0; + + lock $self->{Curr_Test}; + $self->{Curr_Test}++; + + # In case $name is a string overloaded object, force it to stringify. + $self->_unoverload_str( \$name ); + + $self->diag(<<"ERR") if defined $name and $name =~ /^[\d\s]+$/; + You named your test '$name'. You shouldn't use numbers for your test names. + Very confusing. +ERR + + # Capture the value of $TODO for the rest of this ok() call + # so it can more easily be found by other routines. + my $todo = $self->todo(); + my $in_todo = $self->in_todo; + local $self->{Todo} = $todo if $in_todo; + + $self->_unoverload_str( \$todo ); + + my $out; + my $result = &share( {} ); + + unless($test) { + $out .= "not "; + @$result{ 'ok', 'actual_ok' } = ( ( $self->in_todo ? 1 : 0 ), 0 ); + } + else { + @$result{ 'ok', 'actual_ok' } = ( 1, $test ); + } + + $out .= "ok"; + $out .= " $self->{Curr_Test}" if $self->use_numbers; + + if( defined $name ) { + $name =~ s|#|\\#|g; # # in a name can confuse Test::Harness. + $out .= " - $name"; + $result->{name} = $name; + } + else { + $result->{name} = ''; + } + + if( $self->in_todo ) { + $out .= " # TODO $todo"; + $result->{reason} = $todo; + $result->{type} = 'todo'; + } + else { + $result->{reason} = ''; + $result->{type} = ''; + } + + $self->{Test_Results}[ $self->{Curr_Test} - 1 ] = $result; + $out .= "\n"; + + $self->_print($out); + + unless($test) { + my $msg = $self->in_todo ? "Failed (TODO)" : "Failed"; + $self->_print_to_fh( $self->_diag_fh, "\n" ) if $ENV{HARNESS_ACTIVE}; + + my( undef, $file, $line ) = $self->caller; + if( defined $name ) { + $self->diag(qq[ $msg test '$name'\n]); + $self->diag(qq[ at $file line $line.\n]); + } + else { + $self->diag(qq[ $msg test at $file line $line.\n]); + } + } + + return $test ? 1 : 0; +} + +sub _unoverload { + my $self = shift; + my $type = shift; + + $self->_try(sub { require overload; }, die_on_fail => 1); + + foreach my $thing (@_) { + if( $self->_is_object($$thing) ) { + if( my $string_meth = overload::Method( $$thing, $type ) ) { + $$thing = $$thing->$string_meth(); + } + } + } + + return; +} + +sub _is_object { + my( $self, $thing ) = @_; + + return $self->_try( sub { ref $thing && $thing->isa('UNIVERSAL') } ) ? 1 : 0; +} + +sub _unoverload_str { + my $self = shift; + + return $self->_unoverload( q[""], @_ ); +} + +sub _unoverload_num { + my $self = shift; + + $self->_unoverload( '0+', @_ ); + + for my $val (@_) { + next unless $self->_is_dualvar($$val); + $$val = $$val + 0; + } + + return; +} + +# This is a hack to detect a dualvar such as $! +sub _is_dualvar { + my( $self, $val ) = @_; + + # Objects are not dualvars. + return 0 if ref $val; + + no warnings 'numeric'; + my $numval = $val + 0; + return $numval != 0 and $numval ne $val ? 1 : 0; +} + +#line 649 + +sub is_eq { + my( $self, $got, $expect, $name ) = @_; + local $Level = $Level + 1; + + $self->_unoverload_str( \$got, \$expect ); + + if( !defined $got || !defined $expect ) { + # undef only matches undef and nothing else + my $test = !defined $got && !defined $expect; + + $self->ok( $test, $name ); + $self->_is_diag( $got, 'eq', $expect ) unless $test; + return $test; + } + + return $self->cmp_ok( $got, 'eq', $expect, $name ); +} + +sub is_num { + my( $self, $got, $expect, $name ) = @_; + local $Level = $Level + 1; + + $self->_unoverload_num( \$got, \$expect ); + + if( !defined $got || !defined $expect ) { + # undef only matches undef and nothing else + my $test = !defined $got && !defined $expect; + + $self->ok( $test, $name ); + $self->_is_diag( $got, '==', $expect ) unless $test; + return $test; + } + + return $self->cmp_ok( $got, '==', $expect, $name ); +} + +sub _diag_fmt { + my( $self, $type, $val ) = @_; + + if( defined $$val ) { + if( $type eq 'eq' or $type eq 'ne' ) { + # quote and force string context + $$val = "'$$val'"; + } + else { + # force numeric context + $self->_unoverload_num($val); + } + } + else { + $$val = 'undef'; + } + + return; +} + +sub _is_diag { + my( $self, $got, $type, $expect ) = @_; + + $self->_diag_fmt( $type, $_ ) for \$got, \$expect; + + local $Level = $Level + 1; + return $self->diag(<<"DIAGNOSTIC"); + got: $got + expected: $expect +DIAGNOSTIC + +} + +sub _isnt_diag { + my( $self, $got, $type ) = @_; + + $self->_diag_fmt( $type, \$got ); + + local $Level = $Level + 1; + return $self->diag(<<"DIAGNOSTIC"); + got: $got + expected: anything else +DIAGNOSTIC +} + +#line 746 + +sub isnt_eq { + my( $self, $got, $dont_expect, $name ) = @_; + local $Level = $Level + 1; + + if( !defined $got || !defined $dont_expect ) { + # undef only matches undef and nothing else + my $test = defined $got || defined $dont_expect; + + $self->ok( $test, $name ); + $self->_isnt_diag( $got, 'ne' ) unless $test; + return $test; + } + + return $self->cmp_ok( $got, 'ne', $dont_expect, $name ); +} + +sub isnt_num { + my( $self, $got, $dont_expect, $name ) = @_; + local $Level = $Level + 1; + + if( !defined $got || !defined $dont_expect ) { + # undef only matches undef and nothing else + my $test = defined $got || defined $dont_expect; + + $self->ok( $test, $name ); + $self->_isnt_diag( $got, '!=' ) unless $test; + return $test; + } + + return $self->cmp_ok( $got, '!=', $dont_expect, $name ); +} + +#line 797 + +sub like { + my( $self, $this, $regex, $name ) = @_; + + local $Level = $Level + 1; + return $self->_regex_ok( $this, $regex, '=~', $name ); +} + +sub unlike { + my( $self, $this, $regex, $name ) = @_; + + local $Level = $Level + 1; + return $self->_regex_ok( $this, $regex, '!~', $name ); +} + +#line 821 + +my %numeric_cmps = map { ( $_, 1 ) } ( "<", "<=", ">", ">=", "==", "!=", "<=>" ); + +sub cmp_ok { + my( $self, $got, $type, $expect, $name ) = @_; + + my $test; + my $error; + { + ## no critic (BuiltinFunctions::ProhibitStringyEval) + + local( $@, $!, $SIG{__DIE__} ); # isolate eval + + my($pack, $file, $line) = $self->caller(); + + $test = eval qq[ +#line 1 "cmp_ok [from $file line $line]" +\$got $type \$expect; +]; + $error = $@; + } + local $Level = $Level + 1; + my $ok = $self->ok( $test, $name ); + + # Treat overloaded objects as numbers if we're asked to do a + # numeric comparison. + my $unoverload + = $numeric_cmps{$type} + ? '_unoverload_num' + : '_unoverload_str'; + + $self->diag(<<"END") if $error; +An error occurred while using $type: +------------------------------------ +$error +------------------------------------ +END + + unless($ok) { + $self->$unoverload( \$got, \$expect ); + + if( $type =~ /^(eq|==)$/ ) { + $self->_is_diag( $got, $type, $expect ); + } + elsif( $type =~ /^(ne|!=)$/ ) { + $self->_isnt_diag( $got, $type ); + } + else { + $self->_cmp_diag( $got, $type, $expect ); + } + } + return $ok; +} + +sub _cmp_diag { + my( $self, $got, $type, $expect ) = @_; + + $got = defined $got ? "'$got'" : 'undef'; + $expect = defined $expect ? "'$expect'" : 'undef'; + + local $Level = $Level + 1; + return $self->diag(<<"DIAGNOSTIC"); + $got + $type + $expect +DIAGNOSTIC +} + +sub _caller_context { + my $self = shift; + + my( $pack, $file, $line ) = $self->caller(1); + + my $code = ''; + $code .= "#line $line $file\n" if defined $file and defined $line; + + return $code; +} + +#line 920 + +sub BAIL_OUT { + my( $self, $reason ) = @_; + + $self->{Bailed_Out} = 1; + $self->_print("Bail out! $reason"); + exit 255; +} + +#line 933 + +*BAILOUT = \&BAIL_OUT; + +#line 944 + +sub skip { + my( $self, $why ) = @_; + $why ||= ''; + $self->_unoverload_str( \$why ); + + lock( $self->{Curr_Test} ); + $self->{Curr_Test}++; + + $self->{Test_Results}[ $self->{Curr_Test} - 1 ] = &share( + { + 'ok' => 1, + actual_ok => 1, + name => '', + type => 'skip', + reason => $why, + } + ); + + my $out = "ok"; + $out .= " $self->{Curr_Test}" if $self->use_numbers; + $out .= " # skip"; + $out .= " $why" if length $why; + $out .= "\n"; + + $self->_print($out); + + return 1; +} + +#line 985 + +sub todo_skip { + my( $self, $why ) = @_; + $why ||= ''; + + lock( $self->{Curr_Test} ); + $self->{Curr_Test}++; + + $self->{Test_Results}[ $self->{Curr_Test} - 1 ] = &share( + { + 'ok' => 1, + actual_ok => 0, + name => '', + type => 'todo_skip', + reason => $why, + } + ); + + my $out = "not ok"; + $out .= " $self->{Curr_Test}" if $self->use_numbers; + $out .= " # TODO & SKIP $why\n"; + + $self->_print($out); + + return 1; +} + +#line 1062 + +sub maybe_regex { + my( $self, $regex ) = @_; + my $usable_regex = undef; + + return $usable_regex unless defined $regex; + + my( $re, $opts ); + + # Check for qr/foo/ + if( _is_qr($regex) ) { + $usable_regex = $regex; + } + # Check for '/foo/' or 'm,foo,' + elsif(( $re, $opts ) = $regex =~ m{^ /(.*)/ (\w*) $ }sx or + ( undef, $re, $opts ) = $regex =~ m,^ m([^\w\s]) (.+) \1 (\w*) $,sx + ) + { + $usable_regex = length $opts ? "(?$opts)$re" : $re; + } + + return $usable_regex; +} + +sub _is_qr { + my $regex = shift; + + # is_regexp() checks for regexes in a robust manner, say if they're + # blessed. + return re::is_regexp($regex) if defined &re::is_regexp; + return ref $regex eq 'Regexp'; +} + +sub _regex_ok { + my( $self, $this, $regex, $cmp, $name ) = @_; + + my $ok = 0; + my $usable_regex = $self->maybe_regex($regex); + unless( defined $usable_regex ) { + local $Level = $Level + 1; + $ok = $self->ok( 0, $name ); + $self->diag(" '$regex' doesn't look much like a regex to me."); + return $ok; + } + + { + ## no critic (BuiltinFunctions::ProhibitStringyEval) + + my $test; + my $code = $self->_caller_context; + + local( $@, $!, $SIG{__DIE__} ); # isolate eval + + # Yes, it has to look like this or 5.4.5 won't see the #line + # directive. + # Don't ask me, man, I just work here. + $test = eval " +$code" . q{$test = $this =~ /$usable_regex/ ? 1 : 0}; + + $test = !$test if $cmp eq '!~'; + + local $Level = $Level + 1; + $ok = $self->ok( $test, $name ); + } + + unless($ok) { + $this = defined $this ? "'$this'" : 'undef'; + my $match = $cmp eq '=~' ? "doesn't match" : "matches"; + + local $Level = $Level + 1; + $self->diag( sprintf <<'DIAGNOSTIC', $this, $match, $regex ); + %s + %13s '%s' +DIAGNOSTIC + + } + + return $ok; +} + +# I'm not ready to publish this. It doesn't deal with array return +# values from the code or context. + +#line 1162 + +sub _try { + my( $self, $code, %opts ) = @_; + + my $error; + my $return; + { + local $!; # eval can mess up $! + local $@; # don't set $@ in the test + local $SIG{__DIE__}; # don't trip an outside DIE handler. + $return = eval { $code->() }; + $error = $@; + } + + die $error if $error and $opts{die_on_fail}; + + return wantarray ? ( $return, $error ) : $return; +} + +#line 1191 + +sub is_fh { + my $self = shift; + my $maybe_fh = shift; + return 0 unless defined $maybe_fh; + + return 1 if ref $maybe_fh eq 'GLOB'; # its a glob ref + return 1 if ref \$maybe_fh eq 'GLOB'; # its a glob + + return eval { $maybe_fh->isa("IO::Handle") } || + # 5.5.4's tied() and can() doesn't like getting undef + eval { ( tied($maybe_fh) || '' )->can('TIEHANDLE') }; +} + +#line 1235 + +sub level { + my( $self, $level ) = @_; + + if( defined $level ) { + $Level = $level; + } + return $Level; +} + +#line 1267 + +sub use_numbers { + my( $self, $use_nums ) = @_; + + if( defined $use_nums ) { + $self->{Use_Nums} = $use_nums; + } + return $self->{Use_Nums}; +} + +#line 1300 + +foreach my $attribute (qw(No_Header No_Ending No_Diag)) { + my $method = lc $attribute; + + my $code = sub { + my( $self, $no ) = @_; + + if( defined $no ) { + $self->{$attribute} = $no; + } + return $self->{$attribute}; + }; + + no strict 'refs'; ## no critic + *{ __PACKAGE__ . '::' . $method } = $code; +} + +#line 1353 + +sub diag { + my $self = shift; + + $self->_print_comment( $self->_diag_fh, @_ ); +} + +#line 1368 + +sub note { + my $self = shift; + + $self->_print_comment( $self->output, @_ ); +} + +sub _diag_fh { + my $self = shift; + + local $Level = $Level + 1; + return $self->in_todo ? $self->todo_output : $self->failure_output; +} + +sub _print_comment { + my( $self, $fh, @msgs ) = @_; + + return if $self->no_diag; + return unless @msgs; + + # Prevent printing headers when compiling (i.e. -c) + return if $^C; + + # Smash args together like print does. + # Convert undef to 'undef' so its readable. + my $msg = join '', map { defined($_) ? $_ : 'undef' } @msgs; + + # Escape the beginning, _print will take care of the rest. + $msg =~ s/^/# /; + + local $Level = $Level + 1; + $self->_print_to_fh( $fh, $msg ); + + return 0; +} + +#line 1418 + +sub explain { + my $self = shift; + + return map { + ref $_ + ? do { + $self->_try(sub { require Data::Dumper }, die_on_fail => 1); + + my $dumper = Data::Dumper->new( [$_] ); + $dumper->Indent(1)->Terse(1); + $dumper->Sortkeys(1) if $dumper->can("Sortkeys"); + $dumper->Dump; + } + : $_ + } @_; +} + +#line 1447 + +sub _print { + my $self = shift; + return $self->_print_to_fh( $self->output, @_ ); +} + +sub _print_to_fh { + my( $self, $fh, @msgs ) = @_; + + # Prevent printing headers when only compiling. Mostly for when + # tests are deparsed with B::Deparse + return if $^C; + + my $msg = join '', @msgs; + + local( $\, $", $, ) = ( undef, ' ', '' ); + + # Escape each line after the first with a # so we don't + # confuse Test::Harness. + $msg =~ s{\n(?!\z)}{\n# }sg; + + # Stick a newline on the end if it needs it. + $msg .= "\n" unless $msg =~ /\n\z/; + + return print $fh $msg; +} + +#line 1506 + +sub output { + my( $self, $fh ) = @_; + + if( defined $fh ) { + $self->{Out_FH} = $self->_new_fh($fh); + } + return $self->{Out_FH}; +} + +sub failure_output { + my( $self, $fh ) = @_; + + if( defined $fh ) { + $self->{Fail_FH} = $self->_new_fh($fh); + } + return $self->{Fail_FH}; +} + +sub todo_output { + my( $self, $fh ) = @_; + + if( defined $fh ) { + $self->{Todo_FH} = $self->_new_fh($fh); + } + return $self->{Todo_FH}; +} + +sub _new_fh { + my $self = shift; + my($file_or_fh) = shift; + + my $fh; + if( $self->is_fh($file_or_fh) ) { + $fh = $file_or_fh; + } + elsif( ref $file_or_fh eq 'SCALAR' ) { + # Scalar refs as filehandles was added in 5.8. + if( $] >= 5.008 ) { + open $fh, ">>", $file_or_fh + or $self->croak("Can't open scalar ref $file_or_fh: $!"); + } + # Emulate scalar ref filehandles with a tie. + else { + $fh = Test::Builder::IO::Scalar->new($file_or_fh) + or $self->croak("Can't tie scalar ref $file_or_fh"); + } + } + else { + open $fh, ">", $file_or_fh + or $self->croak("Can't open test output log $file_or_fh: $!"); + _autoflush($fh); + } + + return $fh; +} + +sub _autoflush { + my($fh) = shift; + my $old_fh = select $fh; + $| = 1; + select $old_fh; + + return; +} + +my( $Testout, $Testerr ); + +sub _dup_stdhandles { + my $self = shift; + + $self->_open_testhandles; + + # Set everything to unbuffered else plain prints to STDOUT will + # come out in the wrong order from our own prints. + _autoflush($Testout); + _autoflush( \*STDOUT ); + _autoflush($Testerr); + _autoflush( \*STDERR ); + + $self->reset_outputs; + + return; +} + +sub _open_testhandles { + my $self = shift; + + return if $self->{Opened_Testhandles}; + + # We dup STDOUT and STDERR so people can change them in their + # test suites while still getting normal test output. + open( $Testout, ">&STDOUT" ) or die "Can't dup STDOUT: $!"; + open( $Testerr, ">&STDERR" ) or die "Can't dup STDERR: $!"; + + # $self->_copy_io_layers( \*STDOUT, $Testout ); + # $self->_copy_io_layers( \*STDERR, $Testerr ); + + $self->{Opened_Testhandles} = 1; + + return; +} + +sub _copy_io_layers { + my( $self, $src, $dst ) = @_; + + $self->_try( + sub { + require PerlIO; + my @src_layers = PerlIO::get_layers($src); + + binmode $dst, join " ", map ":$_", @src_layers if @src_layers; + } + ); + + return; +} + +#line 1631 + +sub reset_outputs { + my $self = shift; + + $self->output ($Testout); + $self->failure_output($Testerr); + $self->todo_output ($Testout); + + return; +} + +#line 1657 + +sub _message_at_caller { + my $self = shift; + + local $Level = $Level + 1; + my( $pack, $file, $line ) = $self->caller; + return join( "", @_ ) . " at $file line $line.\n"; +} + +sub carp { + my $self = shift; + return warn $self->_message_at_caller(@_); +} + +sub croak { + my $self = shift; + return die $self->_message_at_caller(@_); +} + + +#line 1697 + +sub current_test { + my( $self, $num ) = @_; + + lock( $self->{Curr_Test} ); + if( defined $num ) { + $self->{Curr_Test} = $num; + + # If the test counter is being pushed forward fill in the details. + my $test_results = $self->{Test_Results}; + if( $num > @$test_results ) { + my $start = @$test_results ? @$test_results : 0; + for( $start .. $num - 1 ) { + $test_results->[$_] = &share( + { + 'ok' => 1, + actual_ok => undef, + reason => 'incrementing test number', + type => 'unknown', + name => undef + } + ); + } + } + # If backward, wipe history. Its their funeral. + elsif( $num < @$test_results ) { + $#{$test_results} = $num - 1; + } + } + return $self->{Curr_Test}; +} + +#line 1739 + +sub summary { + my($self) = shift; + + return map { $_->{'ok'} } @{ $self->{Test_Results} }; +} + +#line 1794 + +sub details { + my $self = shift; + return @{ $self->{Test_Results} }; +} + +#line 1823 + +sub todo { + my( $self, $pack ) = @_; + + return $self->{Todo} if defined $self->{Todo}; + + local $Level = $Level + 1; + my $todo = $self->find_TODO($pack); + return $todo if defined $todo; + + return ''; +} + +#line 1845 + +sub find_TODO { + my( $self, $pack ) = @_; + + $pack = $pack || $self->caller(1) || $self->exported_to; + return unless $pack; + + no strict 'refs'; ## no critic + return ${ $pack . '::TODO' }; +} + +#line 1863 + +sub in_todo { + my $self = shift; + + local $Level = $Level + 1; + return( defined $self->{Todo} || $self->find_TODO ) ? 1 : 0; +} + +#line 1913 + +sub todo_start { + my $self = shift; + my $message = @_ ? shift : ''; + + $self->{Start_Todo}++; + if( $self->in_todo ) { + push @{ $self->{Todo_Stack} } => $self->todo; + } + $self->{Todo} = $message; + + return; +} + +#line 1935 + +sub todo_end { + my $self = shift; + + if( !$self->{Start_Todo} ) { + $self->croak('todo_end() called without todo_start()'); + } + + $self->{Start_Todo}--; + + if( $self->{Start_Todo} && @{ $self->{Todo_Stack} } ) { + $self->{Todo} = pop @{ $self->{Todo_Stack} }; + } + else { + delete $self->{Todo}; + } + + return; +} + +#line 1968 + +sub caller { ## no critic (Subroutines::ProhibitBuiltinHomonyms) + my( $self, $height ) = @_; + $height ||= 0; + + my $level = $self->level + $height + 1; + my @caller; + do { + @caller = CORE::caller( $level ); + $level--; + } until @caller; + return wantarray ? @caller : $caller[0]; +} + +#line 1985 + +#line 1999 + +#'# +sub _sanity_check { + my $self = shift; + + $self->_whoa( $self->{Curr_Test} < 0, 'Says here you ran a negative number of tests!' ); + $self->_whoa( $self->{Curr_Test} != @{ $self->{Test_Results} }, + 'Somehow you got a different number of results than tests ran!' ); + + return; +} + +#line 2020 + +sub _whoa { + my( $self, $check, $desc ) = @_; + if($check) { + local $Level = $Level + 1; + $self->croak(<<"WHOA"); +WHOA! $desc +This should never happen! Please contact the author immediately! +WHOA + } + + return; +} + +#line 2044 + +sub _my_exit { + $? = $_[0]; ## no critic (Variables::RequireLocalizedPunctuationVars) + + return 1; +} + +#line 2056 + +sub _ending { + my $self = shift; + + my $real_exit_code = $?; + + # Don't bother with an ending if this is a forked copy. Only the parent + # should do the ending. + if( $self->{Original_Pid} != $$ ) { + return; + } + + # Ran tests but never declared a plan or hit done_testing + if( !$self->{Have_Plan} and $self->{Curr_Test} ) { + $self->diag("Tests were run but no plan was declared and done_testing() was not seen."); + } + + # Exit if plan() was never called. This is so "require Test::Simple" + # doesn't puke. + if( !$self->{Have_Plan} ) { + return; + } + + # Don't do an ending if we bailed out. + if( $self->{Bailed_Out} ) { + return; + } + + # Figure out if we passed or failed and print helpful messages. + my $test_results = $self->{Test_Results}; + if(@$test_results) { + # The plan? We have no plan. + if( $self->{No_Plan} ) { + $self->_output_plan($self->{Curr_Test}) unless $self->no_header; + $self->{Expected_Tests} = $self->{Curr_Test}; + } + + # Auto-extended arrays and elements which aren't explicitly + # filled in with a shared reference will puke under 5.8.0 + # ithreads. So we have to fill them in by hand. :( + my $empty_result = &share( {} ); + for my $idx ( 0 .. $self->{Expected_Tests} - 1 ) { + $test_results->[$idx] = $empty_result + unless defined $test_results->[$idx]; + } + + my $num_failed = grep !$_->{'ok'}, @{$test_results}[ 0 .. $self->{Curr_Test} - 1 ]; + + my $num_extra = $self->{Curr_Test} - $self->{Expected_Tests}; + + if( $num_extra != 0 ) { + my $s = $self->{Expected_Tests} == 1 ? '' : 's'; + $self->diag(<<"FAIL"); +Looks like you planned $self->{Expected_Tests} test$s but ran $self->{Curr_Test}. +FAIL + } + + if($num_failed) { + my $num_tests = $self->{Curr_Test}; + my $s = $num_failed == 1 ? '' : 's'; + + my $qualifier = $num_extra == 0 ? '' : ' run'; + + $self->diag(<<"FAIL"); +Looks like you failed $num_failed test$s of $num_tests$qualifier. +FAIL + } + + if($real_exit_code) { + $self->diag(<<"FAIL"); +Looks like your test exited with $real_exit_code just after $self->{Curr_Test}. +FAIL + + _my_exit($real_exit_code) && return; + } + + my $exit_code; + if($num_failed) { + $exit_code = $num_failed <= 254 ? $num_failed : 254; + } + elsif( $num_extra != 0 ) { + $exit_code = 255; + } + else { + $exit_code = 0; + } + + _my_exit($exit_code) && return; + } + elsif( $self->{Skip_All} ) { + _my_exit(0) && return; + } + elsif($real_exit_code) { + $self->diag(<<"FAIL"); +Looks like your test exited with $real_exit_code before it could output anything. +FAIL + _my_exit($real_exit_code) && return; + } + else { + $self->diag("No tests run!\n"); + _my_exit(255) && return; + } + + $self->_whoa( 1, "We fell off the end of _ending()" ); +} + +END { + $Test->_ending if defined $Test and !$Test->no_ending; +} + +#line 2236 + +1; + diff -Nru nginx-0.5.33/modules/nginx-echo/test/inc/Test/More.pm nginx-0.8.53/modules/nginx-echo/test/inc/Test/More.pm --- nginx-0.5.33/modules/nginx-echo/test/inc/Test/More.pm 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/modules/nginx-echo/test/inc/Test/More.pm 2011-02-26 14:04:40.000000000 +0000 @@ -0,0 +1,735 @@ +#line 1 +package Test::More; + +use 5.006; +use strict; +use warnings; + +#---- perlcritic exemptions. ----# + +# We use a lot of subroutine prototypes +## no critic (Subroutines::ProhibitSubroutinePrototypes) + +# Can't use Carp because it might cause use_ok() to accidentally succeed +# even though the module being used forgot to use Carp. Yes, this +# actually happened. +sub _carp { + my( $file, $line ) = ( caller(1) )[ 1, 2 ]; + return warn @_, " at $file line $line\n"; +} + +our $VERSION = '0.92'; +$VERSION = eval $VERSION; ## no critic (BuiltinFunctions::ProhibitStringyEval) + +use Test::Builder::Module; +our @ISA = qw(Test::Builder::Module); +our @EXPORT = qw(ok use_ok require_ok + is isnt like unlike is_deeply + cmp_ok + skip todo todo_skip + pass fail + eq_array eq_hash eq_set + $TODO + plan + done_testing + can_ok isa_ok new_ok + diag note explain + BAIL_OUT +); + +#line 163 + +sub plan { + my $tb = Test::More->builder; + + return $tb->plan(@_); +} + +# This implements "use Test::More 'no_diag'" but the behavior is +# deprecated. +sub import_extra { + my $class = shift; + my $list = shift; + + my @other = (); + my $idx = 0; + while( $idx <= $#{$list} ) { + my $item = $list->[$idx]; + + if( defined $item and $item eq 'no_diag' ) { + $class->builder->no_diag(1); + } + else { + push @other, $item; + } + + $idx++; + } + + @$list = @other; + + return; +} + +#line 216 + +sub done_testing { + my $tb = Test::More->builder; + $tb->done_testing(@_); +} + +#line 289 + +sub ok ($;$) { + my( $test, $name ) = @_; + my $tb = Test::More->builder; + + return $tb->ok( $test, $name ); +} + +#line 367 + +sub is ($$;$) { + my $tb = Test::More->builder; + + return $tb->is_eq(@_); +} + +sub isnt ($$;$) { + my $tb = Test::More->builder; + + return $tb->isnt_eq(@_); +} + +*isn't = \&isnt; + +#line 411 + +sub like ($$;$) { + my $tb = Test::More->builder; + + return $tb->like(@_); +} + +#line 426 + +sub unlike ($$;$) { + my $tb = Test::More->builder; + + return $tb->unlike(@_); +} + +#line 471 + +sub cmp_ok($$$;$) { + my $tb = Test::More->builder; + + return $tb->cmp_ok(@_); +} + +#line 506 + +sub can_ok ($@) { + my( $proto, @methods ) = @_; + my $class = ref $proto || $proto; + my $tb = Test::More->builder; + + unless($class) { + my $ok = $tb->ok( 0, "->can(...)" ); + $tb->diag(' can_ok() called with empty class or reference'); + return $ok; + } + + unless(@methods) { + my $ok = $tb->ok( 0, "$class->can(...)" ); + $tb->diag(' can_ok() called with no methods'); + return $ok; + } + + my @nok = (); + foreach my $method (@methods) { + $tb->_try( sub { $proto->can($method) } ) or push @nok, $method; + } + + my $name = (@methods == 1) ? "$class->can('$methods[0]')" : + "$class->can(...)" ; + + my $ok = $tb->ok( !@nok, $name ); + + $tb->diag( map " $class->can('$_') failed\n", @nok ); + + return $ok; +} + +#line 572 + +sub isa_ok ($$;$) { + my( $object, $class, $obj_name ) = @_; + my $tb = Test::More->builder; + + my $diag; + + if( !defined $object ) { + $obj_name = 'The thing' unless defined $obj_name; + $diag = "$obj_name isn't defined"; + } + else { + my $whatami = ref $object ? 'object' : 'class'; + # We can't use UNIVERSAL::isa because we want to honor isa() overrides + my( $rslt, $error ) = $tb->_try( sub { $object->isa($class) } ); + if($error) { + if( $error =~ /^Can't call method "isa" on unblessed reference/ ) { + # Its an unblessed reference + $obj_name = 'The reference' unless defined $obj_name; + if( !UNIVERSAL::isa( $object, $class ) ) { + my $ref = ref $object; + $diag = "$obj_name isn't a '$class' it's a '$ref'"; + } + } + elsif( $error =~ /Can't call method "isa" without a package/ ) { + # It's something that can't even be a class + $diag = "$obj_name isn't a class or reference"; + } + else { + die <isa on your $whatami and got some weird error. +Here's the error. +$error +WHOA + } + } + else { + $obj_name = "The $whatami" unless defined $obj_name; + if( !$rslt ) { + my $ref = ref $object; + $diag = "$obj_name isn't a '$class' it's a '$ref'"; + } + } + } + + my $name = "$obj_name isa $class"; + my $ok; + if($diag) { + $ok = $tb->ok( 0, $name ); + $tb->diag(" $diag\n"); + } + else { + $ok = $tb->ok( 1, $name ); + } + + return $ok; +} + +#line 650 + +sub new_ok { + my $tb = Test::More->builder; + $tb->croak("new_ok() must be given at least a class") unless @_; + + my( $class, $args, $object_name ) = @_; + + $args ||= []; + $object_name = "The object" unless defined $object_name; + + my $obj; + my( $success, $error ) = $tb->_try( sub { $obj = $class->new(@$args); 1 } ); + if($success) { + local $Test::Builder::Level = $Test::Builder::Level + 1; + isa_ok $obj, $class, $object_name; + } + else { + $tb->ok( 0, "new() died" ); + $tb->diag(" Error was: $error"); + } + + return $obj; +} + +#line 690 + +sub pass (;$) { + my $tb = Test::More->builder; + + return $tb->ok( 1, @_ ); +} + +sub fail (;$) { + my $tb = Test::More->builder; + + return $tb->ok( 0, @_ ); +} + +#line 753 + +sub use_ok ($;@) { + my( $module, @imports ) = @_; + @imports = () unless @imports; + my $tb = Test::More->builder; + + my( $pack, $filename, $line ) = caller; + + my $code; + if( @imports == 1 and $imports[0] =~ /^\d+(?:\.\d+)?$/ ) { + # probably a version check. Perl needs to see the bare number + # for it to work with non-Exporter based modules. + $code = <ok( $eval_result, "use $module;" ); + + unless($ok) { + chomp $eval_error; + $@ =~ s{^BEGIN failed--compilation aborted at .*$} + {BEGIN failed--compilation aborted at $filename line $line.}m; + $tb->diag(<builder; + + my $pack = caller; + + # Try to deterine if we've been given a module name or file. + # Module names must be barewords, files not. + $module = qq['$module'] unless _is_module_name($module); + + my $code = <ok( $eval_result, "require $module;" ); + + unless($ok) { + chomp $eval_error; + $tb->diag(<builder; + + unless( @_ == 2 or @_ == 3 ) { + my $msg = <<'WARNING'; +is_deeply() takes two or three args, you gave %d. +This usually means you passed an array or hash instead +of a reference to it +WARNING + chop $msg; # clip off newline so carp() will put in line/file + + _carp sprintf $msg, scalar @_; + + return $tb->ok(0); + } + + my( $got, $expected, $name ) = @_; + + $tb->_unoverload_str( \$expected, \$got ); + + my $ok; + if( !ref $got and !ref $expected ) { # neither is a reference + $ok = $tb->is_eq( $got, $expected, $name ); + } + elsif( !ref $got xor !ref $expected ) { # one's a reference, one isn't + $ok = $tb->ok( 0, $name ); + $tb->diag( _format_stack({ vals => [ $got, $expected ] }) ); + } + else { # both references + local @Data_Stack = (); + if( _deep_check( $got, $expected ) ) { + $ok = $tb->ok( 1, $name ); + } + else { + $ok = $tb->ok( 0, $name ); + $tb->diag( _format_stack(@Data_Stack) ); + } + } + + return $ok; +} + +sub _format_stack { + my(@Stack) = @_; + + my $var = '$FOO'; + my $did_arrow = 0; + foreach my $entry (@Stack) { + my $type = $entry->{type} || ''; + my $idx = $entry->{'idx'}; + if( $type eq 'HASH' ) { + $var .= "->" unless $did_arrow++; + $var .= "{$idx}"; + } + elsif( $type eq 'ARRAY' ) { + $var .= "->" unless $did_arrow++; + $var .= "[$idx]"; + } + elsif( $type eq 'REF' ) { + $var = "\${$var}"; + } + } + + my @vals = @{ $Stack[-1]{vals} }[ 0, 1 ]; + my @vars = (); + ( $vars[0] = $var ) =~ s/\$FOO/ \$got/; + ( $vars[1] = $var ) =~ s/\$FOO/\$expected/; + + my $out = "Structures begin differing at:\n"; + foreach my $idx ( 0 .. $#vals ) { + my $val = $vals[$idx]; + $vals[$idx] + = !defined $val ? 'undef' + : _dne($val) ? "Does not exist" + : ref $val ? "$val" + : "'$val'"; + } + + $out .= "$vars[0] = $vals[0]\n"; + $out .= "$vars[1] = $vals[1]\n"; + + $out =~ s/^/ /msg; + return $out; +} + +sub _type { + my $thing = shift; + + return '' if !ref $thing; + + for my $type (qw(ARRAY HASH REF SCALAR GLOB CODE Regexp)) { + return $type if UNIVERSAL::isa( $thing, $type ); + } + + return ''; +} + +#line 1059 + +sub diag { + return Test::More->builder->diag(@_); +} + +sub note { + return Test::More->builder->note(@_); +} + +#line 1085 + +sub explain { + return Test::More->builder->explain(@_); +} + +#line 1151 + +## no critic (Subroutines::RequireFinalReturn) +sub skip { + my( $why, $how_many ) = @_; + my $tb = Test::More->builder; + + unless( defined $how_many ) { + # $how_many can only be avoided when no_plan is in use. + _carp "skip() needs to know \$how_many tests are in the block" + unless $tb->has_plan eq 'no_plan'; + $how_many = 1; + } + + if( defined $how_many and $how_many =~ /\D/ ) { + _carp + "skip() was passed a non-numeric number of tests. Did you get the arguments backwards?"; + $how_many = 1; + } + + for( 1 .. $how_many ) { + $tb->skip($why); + } + + no warnings 'exiting'; + last SKIP; +} + +#line 1238 + +sub todo_skip { + my( $why, $how_many ) = @_; + my $tb = Test::More->builder; + + unless( defined $how_many ) { + # $how_many can only be avoided when no_plan is in use. + _carp "todo_skip() needs to know \$how_many tests are in the block" + unless $tb->has_plan eq 'no_plan'; + $how_many = 1; + } + + for( 1 .. $how_many ) { + $tb->todo_skip($why); + } + + no warnings 'exiting'; + last TODO; +} + +#line 1293 + +sub BAIL_OUT { + my $reason = shift; + my $tb = Test::More->builder; + + $tb->BAIL_OUT($reason); +} + +#line 1332 + +#'# +sub eq_array { + local @Data_Stack = (); + _deep_check(@_); +} + +sub _eq_array { + my( $a1, $a2 ) = @_; + + if( grep _type($_) ne 'ARRAY', $a1, $a2 ) { + warn "eq_array passed a non-array ref"; + return 0; + } + + return 1 if $a1 eq $a2; + + my $ok = 1; + my $max = $#$a1 > $#$a2 ? $#$a1 : $#$a2; + for( 0 .. $max ) { + my $e1 = $_ > $#$a1 ? $DNE : $a1->[$_]; + my $e2 = $_ > $#$a2 ? $DNE : $a2->[$_]; + + push @Data_Stack, { type => 'ARRAY', idx => $_, vals => [ $e1, $e2 ] }; + $ok = _deep_check( $e1, $e2 ); + pop @Data_Stack if $ok; + + last unless $ok; + } + + return $ok; +} + +sub _deep_check { + my( $e1, $e2 ) = @_; + my $tb = Test::More->builder; + + my $ok = 0; + + # Effectively turn %Refs_Seen into a stack. This avoids picking up + # the same referenced used twice (such as [\$a, \$a]) to be considered + # circular. + local %Refs_Seen = %Refs_Seen; + + { + # Quiet uninitialized value warnings when comparing undefs. + no warnings 'uninitialized'; + + $tb->_unoverload_str( \$e1, \$e2 ); + + # Either they're both references or both not. + my $same_ref = !( !ref $e1 xor !ref $e2 ); + my $not_ref = ( !ref $e1 and !ref $e2 ); + + if( defined $e1 xor defined $e2 ) { + $ok = 0; + } + elsif( !defined $e1 and !defined $e2 ) { + # Shortcut if they're both defined. + $ok = 1; + } + elsif( _dne($e1) xor _dne($e2) ) { + $ok = 0; + } + elsif( $same_ref and( $e1 eq $e2 ) ) { + $ok = 1; + } + elsif($not_ref) { + push @Data_Stack, { type => '', vals => [ $e1, $e2 ] }; + $ok = 0; + } + else { + if( $Refs_Seen{$e1} ) { + return $Refs_Seen{$e1} eq $e2; + } + else { + $Refs_Seen{$e1} = "$e2"; + } + + my $type = _type($e1); + $type = 'DIFFERENT' unless _type($e2) eq $type; + + if( $type eq 'DIFFERENT' ) { + push @Data_Stack, { type => $type, vals => [ $e1, $e2 ] }; + $ok = 0; + } + elsif( $type eq 'ARRAY' ) { + $ok = _eq_array( $e1, $e2 ); + } + elsif( $type eq 'HASH' ) { + $ok = _eq_hash( $e1, $e2 ); + } + elsif( $type eq 'REF' ) { + push @Data_Stack, { type => $type, vals => [ $e1, $e2 ] }; + $ok = _deep_check( $$e1, $$e2 ); + pop @Data_Stack if $ok; + } + elsif( $type eq 'SCALAR' ) { + push @Data_Stack, { type => 'REF', vals => [ $e1, $e2 ] }; + $ok = _deep_check( $$e1, $$e2 ); + pop @Data_Stack if $ok; + } + elsif($type) { + push @Data_Stack, { type => $type, vals => [ $e1, $e2 ] }; + $ok = 0; + } + else { + _whoa( 1, "No type in _deep_check" ); + } + } + } + + return $ok; +} + +sub _whoa { + my( $check, $desc ) = @_; + if($check) { + die <<"WHOA"; +WHOA! $desc +This should never happen! Please contact the author immediately! +WHOA + } +} + +#line 1465 + +sub eq_hash { + local @Data_Stack = (); + return _deep_check(@_); +} + +sub _eq_hash { + my( $a1, $a2 ) = @_; + + if( grep _type($_) ne 'HASH', $a1, $a2 ) { + warn "eq_hash passed a non-hash ref"; + return 0; + } + + return 1 if $a1 eq $a2; + + my $ok = 1; + my $bigger = keys %$a1 > keys %$a2 ? $a1 : $a2; + foreach my $k ( keys %$bigger ) { + my $e1 = exists $a1->{$k} ? $a1->{$k} : $DNE; + my $e2 = exists $a2->{$k} ? $a2->{$k} : $DNE; + + push @Data_Stack, { type => 'HASH', idx => $k, vals => [ $e1, $e2 ] }; + $ok = _deep_check( $e1, $e2 ); + pop @Data_Stack if $ok; + + last unless $ok; + } + + return $ok; +} + +#line 1522 + +sub eq_set { + my( $a1, $a2 ) = @_; + return 0 unless @$a1 == @$a2; + + no warnings 'uninitialized'; + + # It really doesn't matter how we sort them, as long as both arrays are + # sorted with the same algorithm. + # + # Ensure that references are not accidentally treated the same as a + # string containing the reference. + # + # Have to inline the sort routine due to a threading/sort bug. + # See [rt.cpan.org 6782] + # + # I don't know how references would be sorted so we just don't sort + # them. This means eq_set doesn't really work with refs. + return eq_array( + [ grep( ref, @$a1 ), sort( grep( !ref, @$a1 ) ) ], + [ grep( ref, @$a2 ), sort( grep( !ref, @$a2 ) ) ], + ); +} + +#line 1735 + +1; diff -Nru nginx-0.5.33/modules/nginx-echo/test/valgrind.suppress nginx-0.8.53/modules/nginx-echo/test/valgrind.suppress --- nginx-0.5.33/modules/nginx-echo/test/valgrind.suppress 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/modules/nginx-echo/test/valgrind.suppress 2011-02-26 14:04:40.000000000 +0000 @@ -0,0 +1,88 @@ +{ + init_request_leak + Memcheck:Leak + fun:memalign + fun:posix_memalign + fun:ngx_memalign + fun:ngx_palloc_block + fun:ngx_palloc + fun:ngx_pcalloc + fun:ngx_create_temp_buf + fun:ngx_http_init_request + fun:ngx_epoll_process_events + fun:ngx_process_events_and_timers + fun:ngx_single_process_cycle + fun:main +} +{ + nginx-core-process-init + Memcheck:Leak + fun:malloc + fun:ngx_alloc + fun:ngx_event_process_init + fun:ngx_single_process_cycle + fun:main +} +{ + nginx-core-crc32-init + Memcheck:Leak + fun:malloc + fun:ngx_alloc + fun:ngx_crc32_table_init + fun:main +} +{ + palloc_large_for_init_request + Memcheck:Leak + fun:malloc + fun:ngx_alloc + fun:ngx_palloc_large + fun:ngx_palloc + fun:ngx_pcalloc + fun:ngx_http_init_request + fun:ngx_epoll_process_events + fun:ngx_process_events_and_timers + fun:ngx_single_process_cycle + fun:main +} +{ + palloc_large_for_create_temp_buf + Memcheck:Leak + fun:malloc + fun:ngx_alloc + fun:ngx_palloc_large + fun:ngx_palloc + fun:ngx_create_temp_buf + fun:ngx_http_init_request + fun:ngx_epoll_process_events + fun:ngx_process_events_and_timers + fun:ngx_single_process_cycle + fun:main +} +{ + accept_create_pool + Memcheck:Leak + fun:memalign + fun:posix_memalign + fun:ngx_memalign + fun:ngx_create_pool + fun:ngx_event_accept + fun:ngx_epoll_process_events + fun:ngx_process_events_and_timers + fun:ngx_single_process_cycle + fun:main +} +{ + create_pool_for_init_req + Memcheck:Leak + fun:memalign + fun:posix_memalign + fun:ngx_memalign + fun:ngx_create_pool + fun:ngx_http_init_request + fun:ngx_epoll_process_events + fun:ngx_process_events_and_timers + fun:ngx_single_process_cycle + fun:main +} + diff -Nru nginx-0.5.33/modules/nginx-echo/util/build.sh nginx-0.8.53/modules/nginx-echo/util/build.sh --- nginx-0.5.33/modules/nginx-echo/util/build.sh 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/modules/nginx-echo/util/build.sh 2011-02-26 14:04:40.000000000 +0000 @@ -0,0 +1,66 @@ +#!/bin/bash + +# this file is mostly meant to be used by the author himself. + +root=`pwd` +cd ~/work +version=$1 +opts=$2 +home=~ + +if [ ! -s "nginx-$version.tar.gz" ]; then + wget "http://sysoev.ru/nginx/nginx-$version.tar.gz" -O nginx-$version.tar.gz || exit 1 + tar -xzvf nginx-$version.tar.gz || exit 1 + if [ "$version" = "0.8.41" ]; then + cp $root/../no-pool-nginx/nginx-$version-no_pool.patch ./ + patch -p0 < nginx-$version-no_pool.patch || exit 1 + fi +fi + +#tar -xzvf nginx-$version.tar.gz || exit 1 +#cp $root/../no-pool-nginx/nginx-0.8.41-no_pool.patch ./ +#patch -p0 < nginx-0.8.41-no_pool.patch || exit 1 + +cd nginx-$version/ +if [[ "$BUILD_CLEAN" -eq 1 || ! -f Makefile \ + || "$root/config" -nt Makefile + || "$root/util/build.sh" -nt Makefile ]]; then + ./configure --prefix=/opt/nginx \ + --with-cc-opt="-DDEBUG_MALLOC" \ + --with-http_stub_status_module \ + --without-mail_pop3_module \ + --without-mail_imap_module \ + --without-mail_smtp_module \ + --without-http_upstream_ip_hash_module \ + --without-http_empty_gif_module \ + --without-http_memcached_module \ + --without-http_referer_module \ + --without-http_autoindex_module \ + --without-http_auth_basic_module \ + --without-http_userid_module \ + --with-http_addition_module \ + --add-module=$root/../ndk-nginx-module \ + --add-module=$root/../set-misc-nginx-module \ + --add-module=$root/../eval-nginx-module \ + --add-module=$root/../xss-nginx-module \ + --add-module=$root/../rds-json-nginx-module \ + --add-module=$root/../headers-more-nginx-module \ + --add-module=$root $opts \ + --with-debug + #--add-module=$root/../lz-session-nginx-module \ + #--add-module=$home/work/ndk \ + #--add-module=$home/work/ndk/examples/http/set_var \ + #--add-module=$root/../eval-nginx-module \ + #--add-module=/home/agentz/work/nginx_eval_module-1.0.1 \ + #--without-http_ssi_module # we cannot disable ssi because echo_location_async depends on it (i dunno why?!) + +fi +if [ -f /opt/nginx/sbin/nginx ]; then + rm -f /opt/nginx/sbin/nginx +fi +if [ -f /opt/nginx/logs/nginx.pid ]; then + kill `cat /opt/nginx/logs/nginx.pid` +fi +make -j3 +make install + diff -Nru nginx-0.5.33/modules/nginx-echo/util/releng nginx-0.8.53/modules/nginx-echo/util/releng --- nginx-0.5.33/modules/nginx-echo/util/releng 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/modules/nginx-echo/util/releng 2011-02-26 14:04:40.000000000 +0000 @@ -0,0 +1,7 @@ +#!/bin/bash + +./update-readme +ack '(?<=\#define)\s*DDEBUG\s*[12]' src +echo ======================================= +ack '(?<=This document describes echo-nginx-module v)\d+\.\d+' README + diff -Nru nginx-0.5.33/modules/nginx-echo/util/update-readme.sh nginx-0.8.53/modules/nginx-echo/util/update-readme.sh --- nginx-0.5.33/modules/nginx-echo/util/update-readme.sh 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/modules/nginx-echo/util/update-readme.sh 2011-02-26 14:04:40.000000000 +0000 @@ -0,0 +1,6 @@ +#!/bin/bash + +perl util/wiki2pod.pl doc/manpage.wiki > /tmp/a.pod \ + && pod2text /tmp/a.pod > README \ + && perl -i -pe 's{(https?://.*?)>}{$1 >}g' README + diff -Nru nginx-0.5.33/modules/nginx-echo/util/wiki2pod.pl nginx-0.8.53/modules/nginx-echo/util/wiki2pod.pl --- nginx-0.5.33/modules/nginx-echo/util/wiki2pod.pl 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/modules/nginx-echo/util/wiki2pod.pl 2011-02-26 14:04:40.000000000 +0000 @@ -0,0 +1,131 @@ +#!/usr/bin/env perl + +use strict; +use warnings; +use bytes; + +my @nl_counts; +my $last_nl_count_level; + +my @bl_counts; +my $last_bl_count_level; + +sub fmt_pos ($) { + (my $s = $_[0]) =~ s{\#(.*)}{/"$1"}; + $s; +} + +sub fmt_mark ($$) { + my ($tag, $s) = @_; + my $max_level = 0; + while ($s =~ /([<>])\1*/g) { + my $level = length $&; + if ($level > $max_level) { + $max_level = $level; + } + } + + my $times = $max_level + 1; + if ($times > 1) { + $s = " $s "; + } + return $tag . ('<' x $times) . $s . ('>' x $times); +} + +print "=encoding utf-8\n\n"; + +while (<>) { + if ($. == 1) { + # strip the leading U+FEFF byte in MS-DOS text files + my $first = ord(substr($_, 0, 1)); + #printf STDERR "0x%x", $first; + #my $second = ord(substr($_, 2, 1)); + #printf STDERR "0x%x", $second; + if ($first == 0xEF) { + substr($_, 0, 1, ''); + #warn "Hit!"; + } + } + s{\[(http[^ \]]+) ([^\]]*)\]}{$2 (L<$1>)}gi; + s{ \[\[ ( [^\]\|]+ ) \| ([^\]]*) \]\] }{"L<$2|" . fmt_pos($1) . ">"}gixe; + s{(.*?)}{fmt_mark('C', $1)}gie; + s{'''(.*?)'''}{fmt_mark('B', $1)}ge; + s{''(.*?)''}{fmt_mark('I', $1)}ge; + if (s{^\s*<[^>]+>\s*$}{}) { + next; + } + + if (/^\s*$/) { + print "\n"; + next; + } + +=begin cmt + + if ($. == 1) { + warn $_; + for my $i (0..length($_) - 1) { + my $chr = substr($_, $i, 1); + warn "chr ord($i): ".ord($chr)." \"$chr\"\n"; + } + } + +=end cmt +=cut + + if (/(=+) (.*) \1$/) { + #warn "HERE! $_" if $. == 1; + my ($level, $title) = (length $1, $2); + collapse_lists(); + + print "\n=head$level $title\n\n"; + } elsif (/^(\#+) (.*)/) { + my ($level, $txt) = (length($1) - 1, $2); + if (defined $last_nl_count_level && $level != $last_nl_count_level) { + print "\n=back\n\n"; + } + $last_nl_count_level = $level; + $nl_counts[$level] ||= 0; + if ($nl_counts[$level] == 0) { + print "\n=over\n\n"; + } + $nl_counts[$level]++; + print "\n=item $nl_counts[$level].\n\n"; + print "$txt\n"; + } elsif (/^(\*+) (.*)/) { + my ($level, $txt) = (length($1) - 1, $2); + if (defined $last_bl_count_level && $level != $last_bl_count_level) { + print "\n=back\n\n"; + } + $last_bl_count_level = $level; + $bl_counts[$level] ||= 0; + if ($bl_counts[$level] == 0) { + print "\n=over\n\n"; + } + $bl_counts[$level]++; + print "\n=item *\n\n"; + print "$txt\n"; + } else { + collapse_lists(); + print; + } +} + +collapse_lists(); + +sub collapse_lists { + while (defined $last_nl_count_level && $last_nl_count_level >= 0) { + print "\n=back\n\n"; + $last_nl_count_level--; + } + undef $last_nl_count_level; + undef @nl_counts; + + while (defined $last_bl_count_level && $last_bl_count_level >= 0) { + print "\n=back\n\n"; + $last_bl_count_level--; + } + undef $last_bl_count_level; + undef @bl_counts; +} + diff -Nru nginx-0.5.33/modules/nginx-upstream-fair/config nginx-0.8.53/modules/nginx-upstream-fair/config --- nginx-0.5.33/modules/nginx-upstream-fair/config 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/modules/nginx-upstream-fair/config 2011-02-26 14:04:40.000000000 +0000 @@ -0,0 +1,3 @@ +ngx_addon_name=ngx_http_upstream_fair_module +HTTP_MODULES="$HTTP_MODULES ngx_http_upstream_fair_module" +NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_upstream_fair_module.c" diff -Nru nginx-0.5.33/modules/nginx-upstream-fair/ngx_http_upstream_fair_module.c nginx-0.8.53/modules/nginx-upstream-fair/ngx_http_upstream_fair_module.c --- nginx-0.5.33/modules/nginx-upstream-fair/ngx_http_upstream_fair_module.c 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/modules/nginx-upstream-fair/ngx_http_upstream_fair_module.c 2011-02-26 14:04:40.000000000 +0000 @@ -0,0 +1,1354 @@ +/* + * Copyright (C) 2007 Grzegorz Nosek + * Work sponsored by Ezra Zygmuntowicz & EngineYard.com + * + * Based on nginx source (C) Igor Sysoev + */ + +#include +#include +#include + +typedef struct { + ngx_uint_t nreq; + ngx_uint_t total_req; + ngx_uint_t last_req_id; + ngx_uint_t fails; + ngx_uint_t current_weight; +} ngx_http_upstream_fair_shared_t; + +typedef struct ngx_http_upstream_fair_peers_s ngx_http_upstream_fair_peers_t; + +typedef struct { + ngx_rbtree_node_t node; + ngx_uint_t generation; + uintptr_t peers; /* forms a unique cookie together with generation */ + ngx_uint_t total_nreq; + ngx_uint_t total_requests; + ngx_atomic_t lock; + ngx_http_upstream_fair_shared_t stats[1]; +} ngx_http_upstream_fair_shm_block_t; + +/* ngx_spinlock is defined without a matching unlock primitive */ +#define ngx_spinlock_unlock(lock) (void) ngx_atomic_cmp_set(lock, ngx_pid, 0) + +typedef struct { + ngx_http_upstream_fair_shared_t *shared; + struct sockaddr *sockaddr; + socklen_t socklen; + ngx_str_t name; + + ngx_uint_t weight; + ngx_uint_t max_fails; + time_t fail_timeout; + + time_t accessed; + ngx_uint_t down:1; + +#if (NGX_HTTP_SSL) + ngx_ssl_session_t *ssl_session; /* local to a process */ +#endif + +} ngx_http_upstream_fair_peer_t; + +#define NGX_HTTP_UPSTREAM_FAIR_NO_RR (1<<26) +#define NGX_HTTP_UPSTREAM_FAIR_WEIGHT_MODE_IDLE (1<<27) +#define NGX_HTTP_UPSTREAM_FAIR_WEIGHT_MODE_PEAK (1<<28) +#define NGX_HTTP_UPSTREAM_FAIR_WEIGHT_MODE_MASK ((1<<27) | (1<<28)) + +enum { WM_DEFAULT = 0, WM_IDLE, WM_PEAK }; + +struct ngx_http_upstream_fair_peers_s { + ngx_http_upstream_fair_shm_block_t *shared; + ngx_uint_t current; + ngx_uint_t size_err:1; + ngx_uint_t no_rr:1; + ngx_uint_t weight_mode:2; + ngx_uint_t number; + ngx_str_t *name; + ngx_http_upstream_fair_peers_t *next; /* for backup peers support, not really used yet */ + ngx_http_upstream_fair_peer_t peer[1]; +}; + + +#define NGX_PEER_INVALID (~0UL) + +typedef struct { + ngx_http_upstream_fair_peers_t *peers; + ngx_uint_t current; + uintptr_t *tried; + uintptr_t *done; + uintptr_t data; + uintptr_t data2; +} ngx_http_upstream_fair_peer_data_t; + + +static ngx_int_t ngx_http_upstream_init_fair(ngx_conf_t *cf, + ngx_http_upstream_srv_conf_t *us); +static ngx_int_t ngx_http_upstream_get_fair_peer(ngx_peer_connection_t *pc, + void *data); +static void ngx_http_upstream_free_fair_peer(ngx_peer_connection_t *pc, + void *data, ngx_uint_t state); +static ngx_int_t ngx_http_upstream_init_fair_peer(ngx_http_request_t *r, + ngx_http_upstream_srv_conf_t *us); +static char *ngx_http_upstream_fair(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static char *ngx_http_upstream_fair_set_shm_size(ngx_conf_t *cf, + ngx_command_t *cmd, void *conf); +static ngx_int_t ngx_http_upstream_fair_init_module(ngx_cycle_t *cycle); + +#if (NGX_HTTP_EXTENDED_STATUS) +static ngx_chain_t *ngx_http_upstream_fair_report_status(ngx_http_request_t *r, + ngx_int_t *length); +#endif + +#if (NGX_HTTP_SSL) +static ngx_int_t ngx_http_upstream_fair_set_session(ngx_peer_connection_t *pc, + void *data); +static void ngx_http_upstream_fair_save_session(ngx_peer_connection_t *pc, + void *data); +#endif + +static ngx_command_t ngx_http_upstream_fair_commands[] = { + + { ngx_string("fair"), + NGX_HTTP_UPS_CONF|NGX_CONF_ANY, + ngx_http_upstream_fair, + 0, + 0, + NULL }, + + { ngx_string("upstream_fair_shm_size"), + NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1, + ngx_http_upstream_fair_set_shm_size, + 0, + 0, + NULL }, + + ngx_null_command +}; + + +static ngx_http_module_t ngx_http_upstream_fair_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 */ + +#if (NGX_HTTP_EXTENDED_STATUS) + ngx_http_upstream_fair_report_status, +#endif +}; + + +ngx_module_t ngx_http_upstream_fair_module = { + NGX_MODULE_V1, + &ngx_http_upstream_fair_module_ctx, /* module context */ + ngx_http_upstream_fair_commands, /* module directives */ + NGX_HTTP_MODULE, /* module type */ + NULL, /* init master */ + ngx_http_upstream_fair_init_module, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; + + +static ngx_uint_t ngx_http_upstream_fair_shm_size; +static ngx_shm_zone_t * ngx_http_upstream_fair_shm_zone; +static ngx_rbtree_t * ngx_http_upstream_fair_rbtree; +static ngx_uint_t ngx_http_upstream_fair_generation; + +static int +ngx_http_upstream_fair_compare_rbtree_node(const ngx_rbtree_node_t *v_left, + const ngx_rbtree_node_t *v_right) +{ + ngx_http_upstream_fair_shm_block_t *left, *right; + + left = (ngx_http_upstream_fair_shm_block_t *) v_left; + right = (ngx_http_upstream_fair_shm_block_t *) v_right; + + if (left->generation < right->generation) { + return -1; + } else if (left->generation > right->generation) { + return 1; + } else { /* left->generation == right->generation */ + if (left->peers < right->peers) { + return -1; + } else if (left->peers > right->peers) { + return 1; + } else { + return 0; + } + } +} + +/* + * generic functions start here + */ +static void +ngx_rbtree_generic_insert(ngx_rbtree_node_t *temp, + ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel, + int (*compare)(const ngx_rbtree_node_t *left, const ngx_rbtree_node_t *right)) +{ + for ( ;; ) { + if (node->key < temp->key) { + + if (temp->left == sentinel) { + temp->left = node; + break; + } + + temp = temp->left; + + } else if (node->key > temp->key) { + + if (temp->right == sentinel) { + temp->right = node; + break; + } + + temp = temp->right; + + } else { /* node->key == temp->key */ + if (compare(node, temp) < 0) { + + if (temp->left == sentinel) { + temp->left = node; + break; + } + + temp = temp->left; + + } else { + + if (temp->right == sentinel) { + temp->right = node; + break; + } + + temp = temp->right; + } + } + } + + node->parent = temp; + node->left = sentinel; + node->right = sentinel; + ngx_rbt_red(node); +} + +#define NGX_BITVECTOR_ELT_SIZE (sizeof(uintptr_t) * 8) + +static uintptr_t * +ngx_bitvector_alloc(ngx_pool_t *pool, ngx_uint_t size, uintptr_t *small) +{ + ngx_uint_t nelts = (size + NGX_BITVECTOR_ELT_SIZE - 1) / NGX_BITVECTOR_ELT_SIZE; + + if (small && nelts == 1) { + *small = 0; + return small; + } + + return ngx_pcalloc(pool, nelts * NGX_BITVECTOR_ELT_SIZE); +} + +static ngx_int_t +ngx_bitvector_test(uintptr_t *bv, ngx_uint_t bit) +{ + ngx_uint_t n, m; + + n = bit / NGX_BITVECTOR_ELT_SIZE; + m = 1 << (bit % NGX_BITVECTOR_ELT_SIZE); + + return bv[n] & m; +} + +static void +ngx_bitvector_set(uintptr_t *bv, ngx_uint_t bit) +{ + ngx_uint_t n, m; + + n = bit / NGX_BITVECTOR_ELT_SIZE; + m = 1 << (bit % NGX_BITVECTOR_ELT_SIZE); + + bv[n] |= m; +} + +/* + * generic functions end here + */ + +static ngx_int_t +ngx_http_upstream_fair_init_module(ngx_cycle_t *cycle) +{ + ngx_http_upstream_fair_generation++; + return NGX_OK; +} + +static void +ngx_http_upstream_fair_rbtree_insert(ngx_rbtree_node_t *temp, + ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel) { + + ngx_rbtree_generic_insert(temp, node, sentinel, + ngx_http_upstream_fair_compare_rbtree_node); +} + + +static ngx_int_t +ngx_http_upstream_fair_init_shm_zone(ngx_shm_zone_t *shm_zone, void *data) +{ + ngx_slab_pool_t *shpool; + ngx_rbtree_t *tree; + ngx_rbtree_node_t *sentinel; + + if (data) { + shm_zone->data = data; + return NGX_OK; + } + + shpool = (ngx_slab_pool_t *) shm_zone->shm.addr; + tree = ngx_slab_alloc(shpool, sizeof *tree); + if (tree == NULL) { + return NGX_ERROR; + } + + sentinel = ngx_slab_alloc(shpool, sizeof *sentinel); + if (sentinel == NULL) { + return NGX_ERROR; + } + + ngx_rbtree_sentinel_init(sentinel); + tree->root = sentinel; + tree->sentinel = sentinel; + tree->insert = ngx_http_upstream_fair_rbtree_insert; + shm_zone->data = tree; + ngx_http_upstream_fair_rbtree = tree; + + return NGX_OK; +} + + +static char * +ngx_http_upstream_fair_set_shm_size(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ssize_t new_shm_size; + ngx_str_t *value; + + value = cf->args->elts; + + new_shm_size = ngx_parse_size(&value[1]); + if (new_shm_size == NGX_ERROR) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "Invalid memory area size `%V'", &value[1]); + return NGX_CONF_ERROR; + } + + new_shm_size = ngx_align(new_shm_size, ngx_pagesize); + + if (new_shm_size < 8 * (ssize_t) ngx_pagesize) { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, "The upstream_fair_shm_size value must be at least %udKiB", (8 * ngx_pagesize) >> 10); + new_shm_size = 8 * ngx_pagesize; + } + + if (ngx_http_upstream_fair_shm_size && + ngx_http_upstream_fair_shm_size != (ngx_uint_t) new_shm_size) { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, "Cannot change memory area size without restart, ignoring change"); + } else { + ngx_http_upstream_fair_shm_size = new_shm_size; + } + ngx_conf_log_error(NGX_LOG_DEBUG, cf, 0, "Using %udKiB of shared memory for upstream_fair", new_shm_size >> 10); + + return NGX_CONF_OK; +} + + +static char * +ngx_http_upstream_fair(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_http_upstream_srv_conf_t *uscf; + ngx_uint_t i; + ngx_uint_t extra_peer_flags = 0; + + for (i = 1; i < cf->args->nelts; i++) { + ngx_str_t *value = cf->args->elts; + if (ngx_strcmp(value[i].data, "no_rr") == 0) { + extra_peer_flags |= NGX_HTTP_UPSTREAM_FAIR_NO_RR; + } else if (ngx_strcmp(value[i].data, "weight_mode=peak") == 0) { + if (extra_peer_flags & NGX_HTTP_UPSTREAM_FAIR_WEIGHT_MODE_MASK) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "weight_mode= options are mutually exclusive"); + return NGX_CONF_ERROR; + } + extra_peer_flags |= NGX_HTTP_UPSTREAM_FAIR_WEIGHT_MODE_PEAK; + } else if (ngx_strcmp(value[i].data, "weight_mode=idle") == 0) { + if (extra_peer_flags & NGX_HTTP_UPSTREAM_FAIR_WEIGHT_MODE_MASK) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "weight_mode= options are mutually exclusive"); + return NGX_CONF_ERROR; + } + extra_peer_flags |= NGX_HTTP_UPSTREAM_FAIR_WEIGHT_MODE_IDLE; + } else { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "Invalid `fair' parameter `%V'", &value[i]); + return NGX_CONF_ERROR; + } + } + + uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module); + + uscf->peer.init_upstream = ngx_http_upstream_init_fair; + + uscf->flags = NGX_HTTP_UPSTREAM_CREATE + |NGX_HTTP_UPSTREAM_WEIGHT + |NGX_HTTP_UPSTREAM_MAX_FAILS + |NGX_HTTP_UPSTREAM_FAIL_TIMEOUT + |NGX_HTTP_UPSTREAM_DOWN + |extra_peer_flags; + + return NGX_CONF_OK; +} + + +static ngx_int_t +ngx_http_upstream_cmp_servers(const void *one, const void *two) +{ + const ngx_http_upstream_fair_peer_t *first, *second; + + first = one; + second = two; + + return (first->weight < second->weight); +} + + +/* TODO: Actually support backup servers */ +static ngx_int_t +ngx_http_upstream_init_fair_rr(ngx_conf_t *cf, ngx_http_upstream_srv_conf_t *us) +{ + ngx_url_t u; + ngx_uint_t i, j, n; + ngx_http_upstream_server_t *server; + ngx_http_upstream_fair_peers_t *peers, *backup; + + if (us->servers) { + server = us->servers->elts; + + n = 0; + + for (i = 0; i < us->servers->nelts; i++) { + if (server[i].backup) { + continue; + } + + n += server[i].naddrs; + } + + peers = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_fair_peers_t) + + sizeof(ngx_http_upstream_fair_peer_t) * (n - 1)); + if (peers == NULL) { + return NGX_ERROR; + } + + peers->number = n; + peers->name = &us->host; + + n = 0; + + for (i = 0; i < us->servers->nelts; i++) { + for (j = 0; j < server[i].naddrs; j++) { + if (server[i].backup) { + continue; + } + + peers->peer[n].sockaddr = server[i].addrs[j].sockaddr; + peers->peer[n].socklen = server[i].addrs[j].socklen; + peers->peer[n].name = server[i].addrs[j].name; + peers->peer[n].max_fails = server[i].max_fails; + peers->peer[n].fail_timeout = server[i].fail_timeout; + peers->peer[n].down = server[i].down; + peers->peer[n].weight = server[i].down ? 0 : server[i].weight; + n++; + } + } + + us->peer.data = peers; + + ngx_sort(&peers->peer[0], (size_t) n, + sizeof(ngx_http_upstream_fair_peer_t), + ngx_http_upstream_cmp_servers); + + /* backup servers */ + + n = 0; + + for (i = 0; i < us->servers->nelts; i++) { + if (!server[i].backup) { + continue; + } + + n += server[i].naddrs; + } + + if (n == 0) { + return NGX_OK; + } + + backup = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_fair_peers_t) + + sizeof(ngx_http_upstream_fair_peer_t) * (n - 1)); + if (backup == NULL) { + return NGX_ERROR; + } + + backup->number = n; + backup->name = &us->host; + + n = 0; + + for (i = 0; i < us->servers->nelts; i++) { + for (j = 0; j < server[i].naddrs; j++) { + if (!server[i].backup) { + continue; + } + + backup->peer[n].sockaddr = server[i].addrs[j].sockaddr; + backup->peer[n].socklen = server[i].addrs[j].socklen; + backup->peer[n].name = server[i].addrs[j].name; + backup->peer[n].weight = server[i].weight; + backup->peer[n].max_fails = server[i].max_fails; + backup->peer[n].fail_timeout = server[i].fail_timeout; + backup->peer[n].down = server[i].down; + n++; + } + } + + peers->next = backup; + + ngx_sort(&backup->peer[0], (size_t) n, + sizeof(ngx_http_upstream_fair_peer_t), + ngx_http_upstream_cmp_servers); + + return NGX_OK; + } + + + /* an upstream implicitly defined by proxy_pass, etc. */ + + if (us->port == 0 && us->default_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 = (in_port_t) (us->port ? us->port : us->default_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_fair_peers_t) + + sizeof(ngx_http_upstream_fair_peer_t) * (n - 1)); + if (peers == NULL) { + return NGX_ERROR; + } + + peers->number = n; + peers->name = &us->host; + + for (i = 0; i < u.naddrs; i++) { + peers->peer[i].sockaddr = u.addrs[i].sockaddr; + peers->peer[i].socklen = u.addrs[i].socklen; + peers->peer[i].name = u.addrs[i].name; + peers->peer[i].weight = 1; + peers->peer[i].max_fails = 1; + peers->peer[i].fail_timeout = 10; + } + + us->peer.data = peers; + + /* implicitly defined upstream has no backup servers */ + + return NGX_OK; +} + +static ngx_int_t +ngx_http_upstream_init_fair(ngx_conf_t *cf, ngx_http_upstream_srv_conf_t *us) +{ + ngx_http_upstream_fair_peers_t *peers; + ngx_uint_t n; + ngx_str_t *shm_name; + + /* do the dirty work using rr module */ + if (ngx_http_upstream_init_fair_rr(cf, us) != NGX_OK) { + return NGX_ERROR; + } + + /* setup our wrapper around rr */ + peers = ngx_palloc(cf->pool, sizeof *peers); + if (peers == NULL) { + return NGX_ERROR; + } + peers = us->peer.data; + n = peers->number; + + shm_name = ngx_palloc(cf->pool, sizeof *shm_name); + shm_name->len = sizeof("upstream_fair"); + shm_name->data = (unsigned char *) "upstream_fair"; + + if (ngx_http_upstream_fair_shm_size == 0) { + ngx_http_upstream_fair_shm_size = 8 * ngx_pagesize; + } + + ngx_http_upstream_fair_shm_zone = ngx_shared_memory_add( + cf, shm_name, ngx_http_upstream_fair_shm_size, &ngx_http_upstream_fair_module); + if (ngx_http_upstream_fair_shm_zone == NULL) { + return NGX_ERROR; + } + ngx_http_upstream_fair_shm_zone->init = ngx_http_upstream_fair_init_shm_zone; + + peers->shared = NULL; + peers->current = n - 1; + if (us->flags & NGX_HTTP_UPSTREAM_FAIR_NO_RR) { + peers->no_rr = 1; + } + if (us->flags & NGX_HTTP_UPSTREAM_FAIR_WEIGHT_MODE_IDLE) { + peers->weight_mode = WM_IDLE; + } else if (us->flags & NGX_HTTP_UPSTREAM_FAIR_WEIGHT_MODE_PEAK) { + peers->weight_mode = WM_PEAK; + } + peers->size_err = 0; + + us->peer.init = ngx_http_upstream_init_fair_peer; + + return NGX_OK; +} + + +static void +ngx_http_upstream_fair_update_nreq(ngx_http_upstream_fair_peer_data_t *fp, int delta, ngx_log_t *log) +{ + ngx_uint_t nreq; + ngx_uint_t total_nreq; + + nreq = (fp->peers->peer[fp->current].shared->nreq += delta); + total_nreq = (fp->peers->shared->total_nreq += delta); + + ngx_log_debug6(NGX_LOG_DEBUG_HTTP, log, 0, + "[upstream_fair] nreq for peer %ui @ %p/%p now %d, total %d, delta %d", + fp->current, fp->peers, fp->peers->peer[fp->current].shared, nreq, + total_nreq, delta); +} + +/* + * SCHED_COUNTER_BITS is the portion of an ngx_uint_t which represents + * the req_delta part (number of requests serviced on _other_ + * backends). The rest (top bits) represents the number of currently + * processed requests. + * + * The value is not too critical because overflow is handled via + * saturation. With the default value of 20, scheduling is exact for + * fewer than 4k concurrent requests per backend (on 32-bit + * architectures) and fewer than 1M concurrent requests to all backends + * together. Beyond these limits, the algorithm essentially falls back + * to pure weighted round-robin. + * + * A higher score means less suitable. + * + * The `delta' parameter is bit-negated so that high values yield low + * scores and get chosen more often. + */ + +#define SCHED_COUNTER_BITS 20 +#define SCHED_NREQ_MAX ((~0UL) >> SCHED_COUNTER_BITS) +#define SCHED_COUNTER_MAX ((1 << SCHED_COUNTER_BITS) - 1) +#define SCHED_SCORE(nreq,delta) (((nreq) << SCHED_COUNTER_BITS) | (~(delta))) +#define ngx_upstream_fair_min(a,b) (((a) < (b)) ? (a) : (b)) + +static ngx_uint_t +ngx_http_upstream_fair_sched_score(ngx_peer_connection_t *pc, + ngx_http_upstream_fair_peer_data_t *fp, + ngx_uint_t n) +{ + ngx_http_upstream_fair_peer_t *peer = &fp->peers->peer[n]; + ngx_http_upstream_fair_shared_t *fs = peer->shared; + ngx_uint_t req_delta = fp->peers->shared->total_requests - fs->last_req_id; + + /* sanity check */ + if ((ngx_int_t)fs->nreq < 0) { + ngx_log_error(NGX_LOG_WARN, pc->log, 0, "[upstream_fair] upstream %ui has negative nreq (%i)", n, fs->nreq); + return SCHED_SCORE(0, req_delta); + } + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, pc->log, 0, "[upstream_fair] peer %ui: nreq = %i, req_delta = %ui", n, fs->nreq, req_delta); + + return SCHED_SCORE( + ngx_upstream_fair_min(fs->nreq, SCHED_NREQ_MAX), + ngx_upstream_fair_min(req_delta, SCHED_COUNTER_MAX)); +} + +/* + * the core of load balancing logic + */ + +static ngx_int_t +ngx_http_upstream_fair_try_peer(ngx_peer_connection_t *pc, + ngx_http_upstream_fair_peer_data_t *fp, + ngx_uint_t peer_id) +{ + ngx_http_upstream_fair_peer_t *peer; + + if (ngx_bitvector_test(fp->tried, peer_id)) + return NGX_BUSY; + + peer = &fp->peers->peer[peer_id]; + + if (!peer->down) { + if (peer->max_fails == 0 || peer->shared->fails < peer->max_fails) { + return NGX_OK; + } + + if (ngx_time() - peer->accessed > peer->fail_timeout) { + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, pc->log, 0, "[upstream_fair] resetting fail count for peer %d, time delta %d > %d", + peer_id, ngx_time() - peer->accessed, peer->fail_timeout); + peer->shared->fails = 0; + return NGX_OK; + } + } + + return NGX_BUSY; +} + +static ngx_uint_t +ngx_http_upstream_choose_fair_peer_idle(ngx_peer_connection_t *pc, + ngx_http_upstream_fair_peer_data_t *fp) +{ + ngx_uint_t i, n; + ngx_uint_t npeers = fp->peers->number; + ngx_uint_t weight_mode = fp->peers->weight_mode; + ngx_uint_t best_idx = NGX_PEER_INVALID; + ngx_uint_t best_nreq = ~0U; + + for (i = 0, n = fp->current; i < npeers; i++, n = (n + 1) % npeers) { + ngx_uint_t nreq = fp->peers->peer[n].shared->nreq; + ngx_uint_t weight = fp->peers->peer[n].weight; + + if (fp->peers->peer[n].shared->fails > 0) + continue; + + if (nreq >= weight || (nreq > 0 && weight_mode != WM_IDLE)) { + continue; + } + + if (ngx_http_upstream_fair_try_peer(pc, fp, n) != NGX_OK) { + continue; + } + + /* not in WM_IDLE+no_rr mode: the first completely idle backend gets chosen */ + if (weight_mode != WM_IDLE || !fp->peers->no_rr) { + best_idx = n; + break; + } + + /* in WM_IDLE+no_rr mode we actually prefer slightly loaded backends + * to totally idle ones, under the assumption that they're spawned + * on demand and can handle up to 'weight' concurrent requests + */ + if (best_idx == NGX_PEER_INVALID || nreq) { + if (best_nreq <= nreq) { + continue; + } + best_idx = n; + best_nreq = nreq; + } + } + + return best_idx; +} + +static ngx_int_t +ngx_http_upstream_choose_fair_peer_busy(ngx_peer_connection_t *pc, + ngx_http_upstream_fair_peer_data_t *fp) +{ + ngx_uint_t i, n; + ngx_uint_t npeers = fp->peers->number; + ngx_uint_t weight_mode = fp->peers->weight_mode; + ngx_uint_t best_idx = NGX_PEER_INVALID; + ngx_uint_t sched_score; + ngx_uint_t best_sched_score = ~0UL; + + /* + * calculate sched scores for all the peers, choosing the lowest one + */ + for (i = 0, n = fp->current; i < npeers; i++, n = (n + 1) % npeers) { + ngx_http_upstream_fair_peer_t *peer; + ngx_uint_t nreq; + ngx_uint_t weight; + + peer = &fp->peers->peer[n]; + nreq = fp->peers->peer[n].shared->nreq; + + if (weight_mode == WM_PEAK && nreq >= peer->weight) { + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, pc->log, 0, "[upstream_fair] backend %d has nreq %ui >= weight %ui in WM_PEAK mode", n, nreq, peer->weight); + continue; + } + + if (ngx_http_upstream_fair_try_peer(pc, fp, n) != NGX_OK) { + if (!pc->tries) { + ngx_log_debug(NGX_LOG_DEBUG_HTTP, pc->log, 0, "[upstream_fair] all backends exhausted"); + return NGX_PEER_INVALID; + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0, "[upstream_fair] backend %d already tried", n); + continue; + } + + sched_score = ngx_http_upstream_fair_sched_score(pc, fp, n); + + if (weight_mode == WM_DEFAULT) { + /* + * take peer weight into account + */ + weight = peer->shared->current_weight; + if (peer->max_fails) { + ngx_uint_t mf = peer->max_fails; + weight = peer->shared->current_weight * (mf - peer->shared->fails) / mf; + } + if (weight > 0) { + sched_score /= weight; + } + ngx_log_debug8(NGX_LOG_DEBUG_HTTP, pc->log, 0, "[upstream_fair] bss = %ui, ss = %ui (n = %d, w = %d/%d, f = %d/%d, weight = %d)", + best_sched_score, sched_score, n, peer->shared->current_weight, peer->weight, peer->shared->fails, peer->max_fails, weight); + } + + if (sched_score <= best_sched_score) { + best_idx = n; + best_sched_score = sched_score; + } + } + + return best_idx; +} + +static ngx_int_t +ngx_http_upstream_choose_fair_peer(ngx_peer_connection_t *pc, + ngx_http_upstream_fair_peer_data_t *fp, ngx_uint_t *peer_id) +{ + ngx_uint_t npeers; + ngx_uint_t best_idx = NGX_PEER_INVALID; + ngx_uint_t weight_mode; + + npeers = fp->peers->number; + weight_mode = fp->peers->weight_mode; + + /* just a single backend */ + if (npeers == 1) { + *peer_id = 0; + return NGX_OK; + } + + /* any idle backends? */ + best_idx = ngx_http_upstream_choose_fair_peer_idle(pc, fp); + if (best_idx != NGX_PEER_INVALID) { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0, "[upstream_fair] peer %i is idle", best_idx); + goto chosen; + } + + /* no idle backends, choose the least loaded one */ + best_idx = ngx_http_upstream_choose_fair_peer_busy(pc, fp); + if (best_idx != NGX_PEER_INVALID) { + goto chosen; + } + + return NGX_BUSY; + +chosen: + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0, "[upstream_fair] chose peer %i", best_idx); + *peer_id = best_idx; + ngx_bitvector_set(fp->tried, best_idx); + + if (weight_mode == WM_DEFAULT) { + ngx_http_upstream_fair_peer_t *peer = &fp->peers->peer[best_idx]; + + if (peer->shared->current_weight-- == 0) { + peer->shared->current_weight = peer->weight; + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0, "[upstream_fair] peer %d expired weight, reset to %d", best_idx, peer->weight); + } + } + return NGX_OK; +} + +ngx_int_t +ngx_http_upstream_get_fair_peer(ngx_peer_connection_t *pc, void *data) +{ + ngx_int_t ret; + ngx_uint_t peer_id, i; + ngx_http_upstream_fair_peer_data_t *fp = data; + ngx_http_upstream_fair_peer_t *peer; + ngx_atomic_t *lock; + + peer_id = fp->current; + fp->current = (fp->current + 1) % fp->peers->number; + + lock = &fp->peers->shared->lock; + ngx_spinlock(lock, ngx_pid, 1024); + ret = ngx_http_upstream_choose_fair_peer(pc, fp, &peer_id); + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, pc->log, 0, "[upstream_fair] fp->current = %d, peer_id = %d, ret = %d", + fp->current, peer_id, ret); + + if (pc) + pc->tries--; + + if (ret == NGX_BUSY) { + for (i = 0; i < fp->peers->number; i++) { + fp->peers->peer[i].shared->fails = 0; + } + + pc->name = fp->peers->name; + fp->current = NGX_PEER_INVALID; + ngx_spinlock_unlock(lock); + return NGX_BUSY; + } + + /* assert(ret == NGX_OK); */ + peer = &fp->peers->peer[peer_id]; + fp->current = peer_id; + if (!fp->peers->no_rr) { + fp->peers->current = peer_id; + } + pc->sockaddr = peer->sockaddr; + pc->socklen = peer->socklen; + pc->name = &peer->name; + + peer->shared->last_req_id = fp->peers->shared->total_requests; + ngx_http_upstream_fair_update_nreq(fp, 1, pc->log); + peer->shared->total_req++; + ngx_spinlock_unlock(lock); + return ret; +} + + +void +ngx_http_upstream_free_fair_peer(ngx_peer_connection_t *pc, void *data, + ngx_uint_t state) +{ + ngx_http_upstream_fair_peer_data_t *fp = data; + ngx_http_upstream_fair_peer_t *peer; + ngx_atomic_t *lock; + + ngx_log_debug4(NGX_LOG_DEBUG_HTTP, pc->log, 0, "[upstream_fair] fp->current = %d, state = %ui, pc->tries = %d, pc->data = %p", + fp->current, state, pc->tries, pc->data); + + if (fp->current == NGX_PEER_INVALID) { + return; + } + + lock = &fp->peers->shared->lock; + ngx_spinlock(lock, ngx_pid, 1024); + if (!ngx_bitvector_test(fp->done, fp->current)) { + ngx_bitvector_set(fp->done, fp->current); + ngx_http_upstream_fair_update_nreq(fp, -1, pc->log); + } + + if (fp->peers->number == 1) { + pc->tries = 0; + } + + if (state & NGX_PEER_FAILED) { + peer = &fp->peers->peer[fp->current]; + + peer->shared->fails++; + peer->accessed = ngx_time(); + } + ngx_spinlock_unlock(lock); +} + +/* + * walk through the rbtree, removing old entries and looking for + * a matching one -- compared by (cycle, peers) pair + * + * no attempt at optimisation is made, for two reasons: + * - the tree will be quite small, anyway + * - being called once per worker startup per upstream block, + * this code isn't really the hot path + */ +static ngx_http_upstream_fair_shm_block_t * +ngx_http_upstream_fair_walk_shm( + ngx_slab_pool_t *shpool, + ngx_rbtree_node_t *node, + ngx_rbtree_node_t *sentinel, + ngx_http_upstream_fair_peers_t *peers) +{ + ngx_http_upstream_fair_shm_block_t *uf_node; + ngx_http_upstream_fair_shm_block_t *found_node = NULL; + ngx_http_upstream_fair_shm_block_t *tmp_node; + + if (node == sentinel) { + return NULL; + } + + /* visit left node */ + if (node->left != sentinel) { + tmp_node = ngx_http_upstream_fair_walk_shm(shpool, node->left, + sentinel, peers); + if (tmp_node) { + found_node = tmp_node; + } + } + + /* visit right node */ + if (node->right != sentinel) { + tmp_node = ngx_http_upstream_fair_walk_shm(shpool, node->right, + sentinel, peers); + if (tmp_node) { + found_node = tmp_node; + } + } + + /* visit current node */ + uf_node = (ngx_http_upstream_fair_shm_block_t *) node; + if (uf_node->generation != ngx_http_upstream_fair_generation) { + ngx_spinlock(&uf_node->lock, ngx_pid, 1024); + if (uf_node->total_nreq == 0) { + /* don't bother unlocking */ + ngx_rbtree_delete(ngx_http_upstream_fair_rbtree, node); + ngx_slab_free_locked(shpool, node); + } + ngx_spinlock_unlock(&uf_node->lock); + } else if (uf_node->peers == (uintptr_t) peers) { + found_node = uf_node; + } + + return found_node; +} + +static ngx_int_t +ngx_http_upstream_fair_shm_alloc(ngx_http_upstream_fair_peers_t *usfp, ngx_log_t *log) +{ + ngx_slab_pool_t *shpool; + ngx_uint_t i; + + if (usfp->shared) { + return NGX_OK; + } + + shpool = (ngx_slab_pool_t *)ngx_http_upstream_fair_shm_zone->shm.addr; + + ngx_shmtx_lock(&shpool->mutex); + + usfp->shared = ngx_http_upstream_fair_walk_shm(shpool, + ngx_http_upstream_fair_rbtree->root, + ngx_http_upstream_fair_rbtree->sentinel, + usfp); + + if (usfp->shared) { + ngx_shmtx_unlock(&shpool->mutex); + return NGX_OK; + } + + usfp->shared = ngx_slab_alloc_locked(shpool, + sizeof(ngx_http_upstream_fair_shm_block_t) + + (usfp->number - 1) * sizeof(ngx_http_upstream_fair_shared_t)); + + if (!usfp->shared) { + ngx_shmtx_unlock(&shpool->mutex); + if (!usfp->size_err) { + ngx_log_error(NGX_LOG_EMERG, log, 0, + "upstream_fair_shm_size too small (current value is %udKiB)", + ngx_http_upstream_fair_shm_size >> 10); + usfp->size_err = 1; + } + return NGX_ERROR; + } + + usfp->shared->node.key = ngx_crc32_short((u_char *) &ngx_cycle, sizeof ngx_cycle) ^ + ngx_crc32_short((u_char *) &usfp, sizeof(usfp)); + + usfp->shared->generation = ngx_http_upstream_fair_generation; + usfp->shared->peers = (uintptr_t) usfp; + usfp->shared->total_nreq = 0; + usfp->shared->total_requests = 0; + + for (i = 0; i < usfp->number; i++) { + usfp->shared->stats[i].nreq = 0; + usfp->shared->stats[i].last_req_id = 0; + usfp->shared->stats[i].total_req = 0; + } + + ngx_rbtree_insert(ngx_http_upstream_fair_rbtree, &usfp->shared->node); + + ngx_shmtx_unlock(&shpool->mutex); + return NGX_OK; +} + +ngx_int_t +ngx_http_upstream_init_fair_peer(ngx_http_request_t *r, + ngx_http_upstream_srv_conf_t *us) +{ + ngx_http_upstream_fair_peer_data_t *fp; + ngx_http_upstream_fair_peers_t *usfp; + ngx_uint_t n; + + fp = r->upstream->peer.data; + + if (fp == NULL) { + fp = ngx_palloc(r->pool, sizeof(ngx_http_upstream_fair_peer_data_t)); + if (fp == NULL) { + return NGX_ERROR; + } + + r->upstream->peer.data = fp; + } + + usfp = us->peer.data; + + fp->tried = ngx_bitvector_alloc(r->pool, usfp->number, &fp->data); + fp->done = ngx_bitvector_alloc(r->pool, usfp->number, &fp->data2); + + if (fp->tried == NULL || fp->done == NULL) { + return NGX_ERROR; + } + + /* set up shared memory area */ + ngx_http_upstream_fair_shm_alloc(usfp, r->connection->log); + + fp->current = usfp->current; + fp->peers = usfp; + usfp->shared->total_requests++; + + for (n = 0; n < usfp->number; n++) { + usfp->peer[n].shared = &usfp->shared->stats[n]; + } + + r->upstream->peer.get = ngx_http_upstream_get_fair_peer; + r->upstream->peer.free = ngx_http_upstream_free_fair_peer; + r->upstream->peer.tries = usfp->number; +#if (NGX_HTTP_SSL) + r->upstream->peer.set_session = + ngx_http_upstream_fair_set_session; + r->upstream->peer.save_session = + ngx_http_upstream_fair_save_session; +#endif + + return NGX_OK; +} + +#if (NGX_HTTP_SSL) +static ngx_int_t +ngx_http_upstream_fair_set_session(ngx_peer_connection_t *pc, void *data) +{ + ngx_http_upstream_fair_peer_data_t *fp = data; + + ngx_int_t rc; + ngx_ssl_session_t *ssl_session; + ngx_http_upstream_fair_peer_t *peer; + + if (fp->current == NGX_PEER_INVALID) + return NGX_OK; + + peer = &fp->peers->peer[fp->current]; + + /* TODO: threads only mutex */ + /* ngx_lock_mutex(fp->peers->mutex); */ + + ssl_session = peer->ssl_session; + + rc = ngx_ssl_set_session(pc->connection, ssl_session); + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0, + "set session: %p:%d", + ssl_session, ssl_session ? ssl_session->references : 0); + + /* ngx_unlock_mutex(fp->peers->mutex); */ + + return rc; +} + +static void +ngx_http_upstream_fair_save_session(ngx_peer_connection_t *pc, void *data) +{ + ngx_http_upstream_fair_peer_data_t *fp = data; + + ngx_ssl_session_t *old_ssl_session, *ssl_session; + ngx_http_upstream_fair_peer_t *peer; + + if (fp->current == NGX_PEER_INVALID) + return; + + ssl_session = ngx_ssl_get_session(pc->connection); + + if (ssl_session == NULL) { + return; + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0, + "save session: %p:%d", ssl_session, ssl_session->references); + + peer = &fp->peers->peer[fp->current]; + + /* TODO: threads only mutex */ + /* ngx_lock_mutex(fp->peers->mutex); */ + + old_ssl_session = peer->ssl_session; + peer->ssl_session = ssl_session; + + /* ngx_unlock_mutex(fp->peers->mutex); */ + + if (old_ssl_session) { + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0, + "old session: %p:%d", + old_ssl_session, old_ssl_session->references); + + /* TODO: may block */ + + ngx_ssl_free_session(old_ssl_session); + } +} + +#endif + +#if (NGX_HTTP_EXTENDED_STATUS) +static void +ngx_http_upstream_fair_walk_status(ngx_pool_t *pool, ngx_chain_t *cl, ngx_int_t *length, + ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel) +{ + ngx_http_upstream_fair_shm_block_t *s_node = (ngx_http_upstream_fair_shm_block_t *) node; + ngx_http_upstream_fair_peers_t *peers; + ngx_chain_t *new_cl; + ngx_buf_t *b; + ngx_uint_t size, i; + + if (node == sentinel) { + return; + } + + if (node->left != sentinel) { + ngx_http_upstream_fair_walk_status(pool, cl, length, node->left, sentinel); + } + + if (s_node->generation != ngx_http_upstream_fair_generation) { + size = 100; + peers = NULL; + } else { + /* this is rather ugly (casting an uintptr_t back into a pointer + * but as long as the generation is still the same (verified above), + * it should be still safe + */ + peers = (ngx_http_upstream_fair_peers_t *) s_node->peers; + if (!peers->shared) { + goto next; + } + + size = 200 + peers->number * 120; /* LOTS of slack */ + } + + b = ngx_create_temp_buf(pool, size); + if (!b) { + goto next; + } + + new_cl = ngx_alloc_chain_link(pool); + if (!new_cl) { + goto next; + } + + new_cl->buf = b; + new_cl->next = NULL; + + while (cl->next) { + cl = cl->next; + } + cl->next = new_cl; + + if (peers) { + b->last = ngx_sprintf(b->last, "upstream %V (%p): current peer %d/%d, total requests: %ui\n", peers->name, (void*) node, peers->current, peers->number, s_node->total_requests); + for (i = 0; i < peers->number; i++) { + ngx_http_upstream_fair_peer_t *peer = &peers->peer[i]; + ngx_http_upstream_fair_shared_t *sh = peer->shared; + b->last = ngx_sprintf(b->last, " peer %d: %V weight: %d/%d, fails: %d/%d, acc: %d, down: %d, nreq: %d, total_req: %ui, last_req: %ui\n", + i, &peer->name, sh->current_weight, peer->weight, sh->fails, peer->max_fails, peer->accessed, peer->down, + sh->nreq, sh->total_req, sh->last_req_id); + } + } else { + b->last = ngx_sprintf(b->last, "upstream %p: gen %ui != %ui, total_nreq = %ui", (void*) node, s_node->generation, ngx_http_upstream_fair_generation, s_node->total_nreq); + } + b->last = ngx_sprintf(b->last, "\n"); + b->last_buf = 1; + + *length += b->last - b->pos; + + if (cl->buf) { + cl->buf->last_buf = 0; + } + + cl = cl->next; +next: + + if (node->right != sentinel) { + ngx_http_upstream_fair_walk_status(pool, cl, length, node->right, sentinel); + } +} + +static ngx_chain_t* +ngx_http_upstream_fair_report_status(ngx_http_request_t *r, ngx_int_t *length) +{ + ngx_buf_t *b; + ngx_chain_t *cl; + ngx_slab_pool_t *shpool; + + b = ngx_create_temp_buf(r->pool, sizeof("\nupstream_fair status report:\n")); + if (!b) { + return NULL; + } + + cl = ngx_alloc_chain_link(r->pool); + if (!cl) { + return NULL; + } + cl->next = NULL; + cl->buf = b; + + b->last = ngx_cpymem(b->last, "\nupstream_fair status report:\n", + sizeof("\nupstream_fair status report:\n") - 1); + + *length = b->last - b->pos; + + shpool = (ngx_slab_pool_t *)ngx_http_upstream_fair_shm_zone->shm.addr; + + ngx_shmtx_lock(&shpool->mutex); + + ngx_http_upstream_fair_walk_status(r->pool, cl, + length, + ngx_http_upstream_fair_rbtree->root, + ngx_http_upstream_fair_rbtree->sentinel); + + ngx_shmtx_unlock(&shpool->mutex); + + if (!cl->next || !cl->next->buf) { + /* no upstream_fair status to report */ + return NULL; + } + + return cl; +} +#endif + +/* vim: set et ts=4 sw=4: */ diff -Nru nginx-0.5.33/.pc/applied-patches nginx-0.8.53/.pc/applied-patches --- nginx-0.5.33/.pc/applied-patches 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/.pc/applied-patches 2011-02-26 14:04:40.000000000 +0000 @@ -0,0 +1,3 @@ +nginx-upstream-fair.diff +dlopen.diff +fix_reloading_ipv6.diff diff -Nru nginx-0.5.33/.pc/dlopen.diff/auto/os/features nginx-0.8.53/.pc/dlopen.diff/auto/os/features --- nginx-0.5.33/.pc/dlopen.diff/auto/os/features 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/.pc/dlopen.diff/auto/os/features 2011-02-26 14:04:40.000000000 +0000 @@ -0,0 +1,276 @@ + +# Copyright (C) Igor Sysoev + + +NGX_USER=${NGX_USER:-nobody} + +if [ -z "$NGX_GROUP" ]; then + if [ $NGX_USER = nobody ]; then + if grep nobody /etc/group 2>&1 >/dev/null; then + echo "checking for nobody group ... found" + NGX_GROUP=nobody + else + echo "checking for nobody group ... not found" + + if grep nogroup /etc/group 2>&1 >/dev/null; then + echo "checking for nogroup group ... found" + NGX_GROUP=nogroup + else + echo "checking for nogroup group ... not found" + NGX_GROUP=nobody + fi + fi + else + NGX_GROUP=$NGX_USER + fi +fi + + +ngx_feature="poll()" +ngx_feature_name= +ngx_feature_run=no +ngx_feature_incs="#include " +ngx_feature_path= +ngx_feature_libs= +ngx_feature_test="int n, dp; struct pollfd pl; + dp = 0; + pl.fd = 0; + pl.events = 0; + pl.revents = 0; + n = poll(&pl, 1, 0)" +. auto/feature + +if [ $ngx_found = no ]; then + EVENT_POLL=NONE +fi + + +ngx_feature="/dev/poll" +ngx_feature_name="NGX_HAVE_DEVPOLL" +ngx_feature_run=no +ngx_feature_incs="#include " +ngx_feature_path= +ngx_feature_libs= +ngx_feature_test="int n, dp; struct dvpoll dvp; + dp = 0; + dvp.dp_fds = NULL; + dvp.dp_nfds = 0; + dvp.dp_timeout = 0; + n = ioctl(dp, DP_POLL, &dvp)" +. auto/feature + +if [ $ngx_found = yes ]; then + CORE_SRCS="$CORE_SRCS $DEVPOLL_SRCS" + EVENT_MODULES="$EVENT_MODULES $DEVPOLL_MODULE" + EVENT_FOUND=YES +fi + + +if test -z "$NGX_KQUEUE_CHECKED"; then + ngx_feature="kqueue" + ngx_feature_name="NGX_HAVE_KQUEUE" + ngx_feature_run=no + ngx_feature_incs="#include " + ngx_feature_path= + ngx_feature_libs= + ngx_feature_test="int kq; kq = kqueue()" + . auto/feature + + if [ $ngx_found = yes ]; then + + have=NGX_HAVE_CLEAR_EVENT . auto/have + EVENT_MODULES="$EVENT_MODULES $KQUEUE_MODULE" + CORE_SRCS="$CORE_SRCS $KQUEUE_SRCS" + EVENT_FOUND=YES + + ngx_feature="kqueue's NOTE_LOWAT" + ngx_feature_name="NGX_HAVE_LOWAT_EVENT" + ngx_feature_run=no + ngx_feature_incs="#include " + ngx_feature_path= + ngx_feature_libs= + ngx_feature_test="struct kevent kev; + kev.fflags = NOTE_LOWAT;" + . auto/feature + + + ngx_feature="kqueue's EVFILT_TIMER" + ngx_feature_name="NGX_HAVE_TIMER_EVENT" + ngx_feature_run=yes + ngx_feature_incs="#include + #include " + ngx_feature_path= + ngx_feature_libs= + ngx_feature_test="int kq; + struct kevent kev; + struct timespec ts; + + if ((kq = kqueue()) == -1) return 1; + + kev.ident = 0; + kev.filter = EVFILT_TIMER; + kev.flags = EV_ADD|EV_ENABLE; + kev.fflags = 0; + kev.data = 1000; + kev.udata = 0; + + ts.tv_sec = 0; + ts.tv_nsec = 0; + + if (kevent(kq, &kev, 1, &kev, 1, &ts) == -1) return 1; + + if (kev.flags & EV_ERROR) return 1;" + + . auto/feature + fi +fi + + +if [ "$NGX_SYSTEM" = "NetBSD" ]; then + + # NetBSD 2.0 incompatibly defines kevent.udata as "intptr_t" + + cat << END >> $NGX_AUTO_CONFIG_H + +#define NGX_KQUEUE_UDATA_T + +END + +else + cat << END >> $NGX_AUTO_CONFIG_H + +#define NGX_KQUEUE_UDATA_T (void *) + +END + +fi + + +ngx_feature="crypt()" +ngx_feature_name= +ngx_feature_run=no +ngx_feature_incs= +ngx_feature_path= +ngx_feature_libs= +ngx_feature_test="crypt(\"test\", \"salt\");" +. auto/feature + + +if [ $ngx_found = no ]; then + + ngx_feature="crypt() in libcrypt" + ngx_feature_name= + ngx_feature_run=no + ngx_feature_incs= + ngx_feature_path= + ngx_feature_libs=-lcrypt + . auto/feature + + if [ $ngx_found = yes ]; then + CRYPT_LIB="-lcrypt" + fi +fi + + +ngx_feature="O_DIRECT" +ngx_feature_name="NGX_HAVE_O_DIRECT" +ngx_feature_run=no +ngx_feature_incs="#include " +ngx_feature_path= +ngx_feature_libs= +ngx_feature_test="fcntl(0, F_SETFL, O_DIRECT);" +. auto/feature + + +if [ $ngx_found = yes -a "$NGX_SYSTEM" = "Linux" ]; then + have=NGX_HAVE_ALIGNED_DIRECTIO . auto/have +fi + +ngx_feature="F_NOCACHE" +ngx_feature_name="NGX_HAVE_F_NOCACHE" +ngx_feature_run=no +ngx_feature_incs="#include " +ngx_feature_path= +ngx_feature_libs= +ngx_feature_test="fcntl(0, F_NOCACHE, 1);" +. auto/feature + + +ngx_feature="directio()" +ngx_feature_name="NGX_HAVE_DIRECTIO" +ngx_feature_run=no +ngx_feature_incs="#include + #include " +ngx_feature_path= +ngx_feature_libs= +ngx_feature_test="directio(0, DIRECTIO_ON);" +. auto/feature + + +ngx_feature="statfs()" +ngx_feature_name="NGX_HAVE_STATFS" +ngx_feature_run=no +ngx_feature_incs="$NGX_INCLUDE_SYS_PARAM_H + $NGX_INCLUDE_SYS_MOUNT_H + $NGX_INCLUDE_SYS_VFS_H" +ngx_feature_path= +ngx_feature_libs= +ngx_feature_test="struct statfs fs; + statfs(NULL, &fs);" +. auto/feature + + +ngx_feature="statvfs()" +ngx_feature_name="NGX_HAVE_STATVFS" +ngx_feature_run=no +ngx_feature_incs="#include + #include " +ngx_feature_path= +ngx_feature_libs= +ngx_feature_test="struct statvfs fs; + statvfs(NULL, &fs);" +. auto/feature + + +ngx_feature="dlopen()" +ngx_feature_name= +ngx_feature_run=no +ngx_feature_incs="#include " +ngx_feature_path= +ngx_feature_libs= +ngx_feature_test="dlopen(NULL, 0)" +. auto/feature + + +if [ $ngx_found != yes ]; then + + ngx_feature="dlopen() in libdl" + ngx_feature_libs="-ldl" + . auto/feature + + if [ $ngx_found = yes ]; then + NGX_LIBDL="-ldl" + fi +fi + + +ngx_feature="sched_yield()" +ngx_feature_name="NGX_HAVE_SCHED_YIELD" +ngx_feature_run=no +ngx_feature_incs="#include " +ngx_feature_path= +ngx_feature_libs= +ngx_feature_test="sched_yield()" +. auto/feature + + +if [ $ngx_found != yes ]; then + + ngx_feature="sched_yield() in librt" + ngx_feature_libs="-lrt" + . auto/feature + + if [ $ngx_found = yes ]; then + CORE_LIBS="$CORE_LIBS -lrt" + fi +fi diff -Nru nginx-0.5.33/.pc/fix_reloading_ipv6.diff/src/core/ngx_cycle.c nginx-0.8.53/.pc/fix_reloading_ipv6.diff/src/core/ngx_cycle.c --- nginx-0.5.33/.pc/fix_reloading_ipv6.diff/src/core/ngx_cycle.c 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/.pc/fix_reloading_ipv6.diff/src/core/ngx_cycle.c 2011-02-26 14:04:40.000000000 +0000 @@ -0,0 +1,1343 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include + + +static void ngx_destroy_cycle_pools(ngx_conf_t *conf); +static ngx_int_t ngx_cmp_sockaddr(struct sockaddr *sa1, struct sockaddr *sa2); +static ngx_int_t ngx_init_zone_pool(ngx_cycle_t *cycle, + ngx_shm_zone_t *shm_zone); +static ngx_int_t ngx_test_lockfile(u_char *file, ngx_log_t *log); +static void ngx_clean_old_cycles(ngx_event_t *ev); + + +volatile ngx_cycle_t *ngx_cycle; +ngx_array_t ngx_old_cycles; + +static ngx_pool_t *ngx_temp_pool; +static ngx_event_t ngx_cleaner_event; + +ngx_uint_t ngx_test_config; + +#if (NGX_THREADS) +ngx_tls_key_t ngx_core_tls_key; +#endif + + +/* STUB NAME */ +static ngx_connection_t dumb; +/* STUB */ + +static ngx_str_t error_log = ngx_string(NGX_ERROR_LOG_PATH); + + +ngx_cycle_t * +ngx_init_cycle(ngx_cycle_t *old_cycle) +{ + void *rv; + char **senv, **env; + ngx_uint_t i, n; + ngx_log_t *log; + ngx_time_t *tp; + ngx_conf_t conf; + ngx_pool_t *pool; + ngx_cycle_t *cycle, **old; + ngx_shm_zone_t *shm_zone, *oshm_zone; + ngx_list_part_t *part, *opart; + ngx_open_file_t *file; + ngx_listening_t *ls, *nls; + ngx_core_conf_t *ccf, *old_ccf; + ngx_core_module_t *module; + char hostname[NGX_MAXHOSTNAMELEN]; + + ngx_timezone_update(); + + /* force localtime update with a new timezone */ + + tp = ngx_timeofday(); + tp->sec = 0; + + ngx_time_update(); + + + log = old_cycle->log; + + pool = ngx_create_pool(NGX_CYCLE_POOL_SIZE, log); + if (pool == NULL) { + return NULL; + } + pool->log = log; + + cycle = ngx_pcalloc(pool, sizeof(ngx_cycle_t)); + if (cycle == NULL) { + ngx_destroy_pool(pool); + return NULL; + } + + cycle->pool = pool; + cycle->log = log; + cycle->new_log.log_level = NGX_LOG_ERR; + cycle->old_cycle = old_cycle; + + cycle->conf_prefix.len = old_cycle->conf_prefix.len; + cycle->conf_prefix.data = ngx_pstrdup(pool, &old_cycle->conf_prefix); + if (cycle->conf_prefix.data == NULL) { + ngx_destroy_pool(pool); + return NULL; + } + + cycle->prefix.len = old_cycle->prefix.len; + cycle->prefix.data = ngx_pstrdup(pool, &old_cycle->prefix); + if (cycle->prefix.data == NULL) { + ngx_destroy_pool(pool); + return NULL; + } + + cycle->conf_file.len = old_cycle->conf_file.len; + cycle->conf_file.data = ngx_pnalloc(pool, old_cycle->conf_file.len + 1); + if (cycle->conf_file.data == NULL) { + ngx_destroy_pool(pool); + return NULL; + } + ngx_cpystrn(cycle->conf_file.data, old_cycle->conf_file.data, + old_cycle->conf_file.len + 1); + + cycle->conf_param.len = old_cycle->conf_param.len; + cycle->conf_param.data = ngx_pstrdup(pool, &old_cycle->conf_param); + if (cycle->conf_param.data == NULL) { + ngx_destroy_pool(pool); + return NULL; + } + + + n = old_cycle->pathes.nelts ? old_cycle->pathes.nelts : 10; + + cycle->pathes.elts = ngx_pcalloc(pool, n * sizeof(ngx_path_t *)); + if (cycle->pathes.elts == NULL) { + ngx_destroy_pool(pool); + return NULL; + } + + cycle->pathes.nelts = 0; + cycle->pathes.size = sizeof(ngx_path_t *); + cycle->pathes.nalloc = n; + cycle->pathes.pool = pool; + + + if (old_cycle->open_files.part.nelts) { + n = old_cycle->open_files.part.nelts; + for (part = old_cycle->open_files.part.next; part; part = part->next) { + n += part->nelts; + } + + } else { + n = 20; + } + + if (ngx_list_init(&cycle->open_files, pool, n, sizeof(ngx_open_file_t)) + != NGX_OK) + { + ngx_destroy_pool(pool); + return NULL; + } + + + if (old_cycle->shared_memory.part.nelts) { + n = old_cycle->shared_memory.part.nelts; + for (part = old_cycle->shared_memory.part.next; part; part = part->next) + { + n += part->nelts; + } + + } else { + n = 1; + } + + if (ngx_list_init(&cycle->shared_memory, pool, n, sizeof(ngx_shm_zone_t)) + != NGX_OK) + { + ngx_destroy_pool(pool); + return NULL; + } + + n = old_cycle->listening.nelts ? old_cycle->listening.nelts : 10; + + cycle->listening.elts = ngx_pcalloc(pool, n * sizeof(ngx_listening_t)); + if (cycle->listening.elts == NULL) { + ngx_destroy_pool(pool); + return NULL; + } + + cycle->listening.nelts = 0; + cycle->listening.size = sizeof(ngx_listening_t); + cycle->listening.nalloc = n; + cycle->listening.pool = pool; + + + cycle->conf_ctx = ngx_pcalloc(pool, ngx_max_module * sizeof(void *)); + if (cycle->conf_ctx == NULL) { + ngx_destroy_pool(pool); + return NULL; + } + + + if (gethostname(hostname, NGX_MAXHOSTNAMELEN) == -1) { + ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "gethostname() failed"); + ngx_destroy_pool(pool); + return NULL; + } + + /* on Linux gethostname() silently truncates name that does not fit */ + + hostname[NGX_MAXHOSTNAMELEN - 1] = '\0'; + cycle->hostname.len = ngx_strlen(hostname); + + cycle->hostname.data = ngx_pnalloc(pool, cycle->hostname.len); + if (cycle->hostname.data == NULL) { + ngx_destroy_pool(pool); + return NULL; + } + + ngx_memcpy(cycle->hostname.data, hostname, cycle->hostname.len); + + + for (i = 0; ngx_modules[i]; i++) { + if (ngx_modules[i]->type != NGX_CORE_MODULE) { + continue; + } + + module = ngx_modules[i]->ctx; + + if (module->create_conf) { + rv = module->create_conf(cycle); + if (rv == NULL) { + ngx_destroy_pool(pool); + return NULL; + } + cycle->conf_ctx[ngx_modules[i]->index] = rv; + } + } + + + senv = environ; + + + ngx_memzero(&conf, sizeof(ngx_conf_t)); + /* STUB: init array ? */ + conf.args = ngx_array_create(pool, 10, sizeof(ngx_str_t)); + if (conf.args == NULL) { + ngx_destroy_pool(pool); + return NULL; + } + + conf.temp_pool = ngx_create_pool(NGX_CYCLE_POOL_SIZE, log); + if (conf.temp_pool == NULL) { + ngx_destroy_pool(pool); + return NULL; + } + + + conf.ctx = cycle->conf_ctx; + conf.cycle = cycle; + conf.pool = pool; + conf.log = log; + conf.module_type = NGX_CORE_MODULE; + conf.cmd_type = NGX_MAIN_CONF; + +#if 0 + log->log_level = NGX_LOG_DEBUG_ALL; +#endif + + if (ngx_conf_param(&conf) != NGX_CONF_OK) { + environ = senv; + ngx_destroy_cycle_pools(&conf); + return NULL; + } + + if (ngx_conf_parse(&conf, &cycle->conf_file) != NGX_CONF_OK) { + environ = senv; + ngx_destroy_cycle_pools(&conf); + return NULL; + } + + if (ngx_test_config) { + ngx_log_stderr(0, "the configuration file %s syntax is ok", + cycle->conf_file.data); + } + + for (i = 0; ngx_modules[i]; i++) { + if (ngx_modules[i]->type != NGX_CORE_MODULE) { + continue; + } + + module = ngx_modules[i]->ctx; + + if (module->init_conf) { + if (module->init_conf(cycle, cycle->conf_ctx[ngx_modules[i]->index]) + == NGX_CONF_ERROR) + { + environ = senv; + ngx_destroy_cycle_pools(&conf); + return NULL; + } + } + } + + if (ngx_process == NGX_PROCESS_SIGNALLER) { + return cycle; + } + + ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module); + + if (ngx_test_config) { + + if (ngx_create_pidfile(&ccf->pid, log) != NGX_OK) { + goto failed; + } + + } else if (!ngx_is_init_cycle(old_cycle)) { + + /* + * we do not create the pid file in the first ngx_init_cycle() call + * because we need to write the demonized process pid + */ + + old_ccf = (ngx_core_conf_t *) ngx_get_conf(old_cycle->conf_ctx, + ngx_core_module); + if (ccf->pid.len != old_ccf->pid.len + || ngx_strcmp(ccf->pid.data, old_ccf->pid.data) != 0) + { + /* new pid file name */ + + if (ngx_create_pidfile(&ccf->pid, log) != NGX_OK) { + goto failed; + } + + ngx_delete_pidfile(old_cycle); + } + } + + + if (ngx_test_lockfile(cycle->lock_file.data, log) != NGX_OK) { + goto failed; + } + + + if (ngx_create_pathes(cycle, ccf->user) != NGX_OK) { + goto failed; + } + + + if (cycle->new_log.file == NULL) { + cycle->new_log.file = ngx_conf_open_file(cycle, &error_log); + if (cycle->new_log.file == NULL) { + goto failed; + } + } + + /* open the new files */ + + part = &cycle->open_files.part; + file = part->elts; + + for (i = 0; /* void */ ; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + part = part->next; + file = part->elts; + i = 0; + } + + if (file[i].name.len == 0) { + continue; + } + + file[i].fd = ngx_open_file(file[i].name.data, + NGX_FILE_APPEND, + NGX_FILE_CREATE_OR_OPEN, + NGX_FILE_DEFAULT_ACCESS); + + ngx_log_debug3(NGX_LOG_DEBUG_CORE, log, 0, + "log: %p %d \"%s\"", + &file[i], file[i].fd, file[i].name.data); + + if (file[i].fd == NGX_INVALID_FILE) { + ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, + ngx_open_file_n " \"%s\" failed", + file[i].name.data); + goto failed; + } + +#if !(NGX_WIN32) + if (fcntl(file[i].fd, F_SETFD, FD_CLOEXEC) == -1) { + ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, + "fcntl(FD_CLOEXEC) \"%s\" failed", + file[i].name.data); + goto failed; + } +#endif + } + + cycle->log = &cycle->new_log; + pool->log = &cycle->new_log; + + + /* create shared memory */ + + part = &cycle->shared_memory.part; + shm_zone = part->elts; + + for (i = 0; /* void */ ; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + part = part->next; + shm_zone = part->elts; + i = 0; + } + + if (shm_zone[i].shm.size == 0) { + ngx_log_error(NGX_LOG_EMERG, log, 0, + "zero size shared memory zone \"%V\"", + &shm_zone[i].shm.name); + goto failed; + } + + if (shm_zone[i].init == NULL) { + /* unused shared zone */ + continue; + } + + shm_zone[i].shm.log = cycle->log; + + opart = &old_cycle->shared_memory.part; + oshm_zone = opart->elts; + + for (n = 0; /* void */ ; n++) { + + if (n >= opart->nelts) { + if (opart->next == NULL) { + break; + } + opart = opart->next; + oshm_zone = opart->elts; + n = 0; + } + + if (shm_zone[i].shm.name.len != oshm_zone[n].shm.name.len) { + continue; + } + + if (ngx_strncmp(shm_zone[i].shm.name.data, + oshm_zone[n].shm.name.data, + shm_zone[i].shm.name.len) + != 0) + { + continue; + } + + if (shm_zone[i].shm.size == oshm_zone[n].shm.size) { + shm_zone[i].shm.addr = oshm_zone[n].shm.addr; + + if (shm_zone[i].init(&shm_zone[i], oshm_zone[n].data) + != NGX_OK) + { + goto failed; + } + + goto shm_zone_found; + } + + ngx_shm_free(&oshm_zone[n].shm); + + break; + } + + if (ngx_shm_alloc(&shm_zone[i].shm) != NGX_OK) { + goto failed; + } + + if (ngx_init_zone_pool(cycle, &shm_zone[i]) != NGX_OK) { + goto failed; + } + + if (shm_zone[i].init(&shm_zone[i], NULL) != NGX_OK) { + goto failed; + } + + shm_zone_found: + + continue; + } + + + /* handle the listening sockets */ + + if (old_cycle->listening.nelts) { + ls = old_cycle->listening.elts; + for (i = 0; i < old_cycle->listening.nelts; i++) { + ls[i].remain = 0; + } + + nls = cycle->listening.elts; + for (n = 0; n < cycle->listening.nelts; n++) { + + for (i = 0; i < old_cycle->listening.nelts; i++) { + if (ls[i].ignore) { + continue; + } + + if (ngx_cmp_sockaddr(nls[n].sockaddr, ls[i].sockaddr) == NGX_OK) + { + nls[n].fd = ls[i].fd; + nls[n].previous = &ls[i]; + ls[i].remain = 1; + + if (ls[n].backlog != nls[i].backlog) { + nls[n].listen = 1; + } + +#if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER) + + /* + * FreeBSD, except the most recent versions, + * could not remove accept filter + */ + nls[n].deferred_accept = ls[i].deferred_accept; + + if (ls[i].accept_filter && nls[n].accept_filter) { + if (ngx_strcmp(ls[i].accept_filter, + nls[n].accept_filter) + != 0) + { + nls[n].delete_deferred = 1; + nls[n].add_deferred = 1; + } + + } else if (ls[i].accept_filter) { + nls[n].delete_deferred = 1; + + } else if (nls[n].accept_filter) { + nls[n].add_deferred = 1; + } +#endif + +#if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT) + + if (ls[n].deferred_accept && !nls[n].deferred_accept) { + nls[n].delete_deferred = 1; + + } else if (ls[i].deferred_accept != nls[n].deferred_accept) + { + nls[n].add_deferred = 1; + } +#endif + break; + } + } + + if (nls[n].fd == -1) { + nls[n].open = 1; + } + } + + } else { + ls = cycle->listening.elts; + for (i = 0; i < cycle->listening.nelts; i++) { + ls[i].open = 1; +#if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER) + if (ls[i].accept_filter) { + ls[i].add_deferred = 1; + } +#endif +#if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT) + if (ls[i].deferred_accept) { + ls[i].add_deferred = 1; + } +#endif + } + } + + if (ngx_open_listening_sockets(cycle) != NGX_OK) { + goto failed; + } + + if (!ngx_test_config) { + ngx_configure_listening_sockets(cycle); + } + + + /* commit the new cycle configuration */ + + if (!ngx_use_stderr && cycle->log->file->fd != ngx_stderr) { + + if (ngx_set_stderr(cycle->log->file->fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + ngx_set_stderr_n " failed"); + } + } + + pool->log = cycle->log; + + for (i = 0; ngx_modules[i]; i++) { + if (ngx_modules[i]->init_module) { + if (ngx_modules[i]->init_module(cycle) != NGX_OK) { + /* fatal */ + exit(1); + } + } + } + + + /* close and delete stuff that lefts from an old cycle */ + + /* free the unnecessary shared memory */ + + opart = &old_cycle->shared_memory.part; + oshm_zone = opart->elts; + + for (i = 0; /* void */ ; i++) { + + if (i >= opart->nelts) { + if (opart->next == NULL) { + goto old_shm_zone_done; + } + opart = opart->next; + oshm_zone = opart->elts; + i = 0; + } + + part = &cycle->shared_memory.part; + shm_zone = part->elts; + + for (n = 0; /* void */ ; n++) { + + if (n >= part->nelts) { + if (part->next == NULL) { + break; + } + part = part->next; + shm_zone = part->elts; + n = 0; + } + + if (oshm_zone[i].shm.name.len == shm_zone[n].shm.name.len + && ngx_strncmp(oshm_zone[i].shm.name.data, + shm_zone[n].shm.name.data, + oshm_zone[i].shm.name.len) + == 0) + { + goto live_shm_zone; + } + } + + ngx_shm_free(&oshm_zone[i].shm); + + live_shm_zone: + + continue; + } + +old_shm_zone_done: + + + /* close the unnecessary listening sockets */ + + ls = old_cycle->listening.elts; + for (i = 0; i < old_cycle->listening.nelts; i++) { + + if (ls[i].remain || ls[i].fd == -1) { + continue; + } + + if (ngx_close_socket(ls[i].fd) == -1) { + ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno, + ngx_close_socket_n " listening socket on %V failed", + &ls[i].addr_text); + } + } + + + /* close the unnecessary open files */ + + part = &old_cycle->open_files.part; + file = part->elts; + + for (i = 0; /* void */ ; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + part = part->next; + file = part->elts; + i = 0; + } + + if (file[i].fd == NGX_INVALID_FILE || file[i].fd == ngx_stderr) { + continue; + } + + if (ngx_close_file(file[i].fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, + ngx_close_file_n " \"%s\" failed", + file[i].name.data); + } + } + + ngx_destroy_pool(conf.temp_pool); + + if (ngx_process == NGX_PROCESS_MASTER || ngx_is_init_cycle(old_cycle)) { + + /* + * perl_destruct() frees environ, if it is not the same as it was at + * perl_construct() time, therefore we save the previous cycle + * environment before ngx_conf_parse() where it will be changed. + */ + + env = environ; + environ = senv; + + ngx_destroy_pool(old_cycle->pool); + cycle->old_cycle = NULL; + + environ = env; + + return cycle; + } + + + if (ngx_temp_pool == NULL) { + ngx_temp_pool = ngx_create_pool(128, cycle->log); + if (ngx_temp_pool == NULL) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, + "can not create ngx_temp_pool"); + exit(1); + } + + n = 10; + ngx_old_cycles.elts = ngx_pcalloc(ngx_temp_pool, + n * sizeof(ngx_cycle_t *)); + if (ngx_old_cycles.elts == NULL) { + exit(1); + } + ngx_old_cycles.nelts = 0; + ngx_old_cycles.size = sizeof(ngx_cycle_t *); + ngx_old_cycles.nalloc = n; + ngx_old_cycles.pool = ngx_temp_pool; + + ngx_cleaner_event.handler = ngx_clean_old_cycles; + ngx_cleaner_event.log = cycle->log; + ngx_cleaner_event.data = &dumb; + dumb.fd = (ngx_socket_t) -1; + } + + ngx_temp_pool->log = cycle->log; + + old = ngx_array_push(&ngx_old_cycles); + if (old == NULL) { + exit(1); + } + *old = old_cycle; + + if (!ngx_cleaner_event.timer_set) { + ngx_add_timer(&ngx_cleaner_event, 30000); + ngx_cleaner_event.timer_set = 1; + } + + return cycle; + + +failed: + + if (!ngx_is_init_cycle(old_cycle)) { + old_ccf = (ngx_core_conf_t *) ngx_get_conf(old_cycle->conf_ctx, + ngx_core_module); + if (old_ccf->environment) { + environ = old_ccf->environment; + } + } + + /* rollback the new cycle configuration */ + + part = &cycle->open_files.part; + file = part->elts; + + for (i = 0; /* void */ ; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + part = part->next; + file = part->elts; + i = 0; + } + + if (file[i].fd == NGX_INVALID_FILE || file[i].fd == ngx_stderr) { + continue; + } + + if (ngx_close_file(file[i].fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, + ngx_close_file_n " \"%s\" failed", + file[i].name.data); + } + } + + if (ngx_test_config) { + ngx_destroy_cycle_pools(&conf); + return NULL; + } + + ls = cycle->listening.elts; + for (i = 0; i < cycle->listening.nelts; i++) { + if (ls[i].fd == -1 || !ls[i].open) { + continue; + } + + if (ngx_close_socket(ls[i].fd) == -1) { + ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno, + ngx_close_socket_n " %V failed", + &ls[i].addr_text); + } + } + + ngx_destroy_cycle_pools(&conf); + + return NULL; +} + + +static void +ngx_destroy_cycle_pools(ngx_conf_t *conf) +{ + ngx_destroy_pool(conf->temp_pool); + ngx_destroy_pool(conf->pool); +} + + +static ngx_int_t +ngx_cmp_sockaddr(struct sockaddr *sa1, struct sockaddr *sa2) +{ + struct sockaddr_in *sin1, *sin2; +#if (NGX_HAVE_INET6) + struct sockaddr_in6 *sin61, *sin62; +#endif + + if (sa1->sa_family != sa2->sa_family) { + return NGX_DECLINED; + } + + switch (sa1->sa_family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + sin61 = (struct sockaddr_in6 *) sa1; + sin62 = (struct sockaddr_in6 *) sa2; + + if (sin61->sin6_port != sin61->sin6_port) { + return NGX_DECLINED; + } + + if (ngx_memcmp(&sin61->sin6_addr, &sin62->sin6_addr, 16) != 0) { + return NGX_DECLINED; + } + + break; +#endif + + default: /* AF_INET */ + + sin1 = (struct sockaddr_in *) sa1; + sin2 = (struct sockaddr_in *) sa2; + + if (sin1->sin_port != sin2->sin_port) { + return NGX_DECLINED; + } + + if (sin1->sin_addr.s_addr != sin2->sin_addr.s_addr) { + return NGX_DECLINED; + } + + break; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_init_zone_pool(ngx_cycle_t *cycle, ngx_shm_zone_t *zn) +{ + u_char *file; + ngx_slab_pool_t *sp; + + sp = (ngx_slab_pool_t *) zn->shm.addr; + + if (zn->shm.exists) { + + if (sp == sp->addr) { + return NGX_OK; + } + + ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, + "shared zone \"%V\" has no equal addresses: %p vs %p", + &zn->shm.name, sp->addr, sp); + return NGX_ERROR; + } + + sp->end = zn->shm.addr + zn->shm.size; + sp->min_shift = 3; + sp->addr = zn->shm.addr; + +#if (NGX_HAVE_ATOMIC_OPS) + + file = NULL; + +#else + + file = ngx_pnalloc(cycle->pool, cycle->lock_file.len + zn->shm.name.len); + if (file == NULL) { + return NGX_ERROR; + } + + (void) ngx_sprintf(file, "%V%V%Z", &cycle->lock_file, &zn->shm.name); + +#endif + + if (ngx_shmtx_create(&sp->mutex, (void *) &sp->lock, file) != NGX_OK) { + return NGX_ERROR; + } + + ngx_slab_init(sp); + + return NGX_OK; +} + + +ngx_int_t +ngx_create_pidfile(ngx_str_t *name, ngx_log_t *log) +{ + size_t len; + ngx_uint_t create; + ngx_file_t file; + u_char pid[NGX_INT64_LEN + 2]; + + if (ngx_process > NGX_PROCESS_MASTER) { + return NGX_OK; + } + + ngx_memzero(&file, sizeof(ngx_file_t)); + + file.name = *name; + file.log = log; + + create = ngx_test_config ? NGX_FILE_CREATE_OR_OPEN : NGX_FILE_TRUNCATE; + + file.fd = ngx_open_file(file.name.data, NGX_FILE_RDWR, + create, NGX_FILE_DEFAULT_ACCESS); + + if (file.fd == NGX_INVALID_FILE) { + ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, + ngx_open_file_n " \"%s\" failed", file.name.data); + return NGX_ERROR; + } + + if (!ngx_test_config) { + len = ngx_snprintf(pid, NGX_INT64_LEN + 2, "%P%N", ngx_pid) - pid; + + if (ngx_write_file(&file, pid, len, 0) == NGX_ERROR) { + return NGX_ERROR; + } + } + + if (ngx_close_file(file.fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + ngx_close_file_n " \"%s\" failed", file.name.data); + } + + return NGX_OK; +} + + +void +ngx_delete_pidfile(ngx_cycle_t *cycle) +{ + u_char *name; + ngx_core_conf_t *ccf; + + ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module); + + name = ngx_new_binary ? ccf->oldpid.data : ccf->pid.data; + + if (ngx_delete_file(name) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + ngx_delete_file_n " \"%s\" failed", name); + } +} + + +ngx_int_t +ngx_signal_process(ngx_cycle_t *cycle, char *sig) +{ + ssize_t n; + ngx_int_t pid; + ngx_file_t file; + ngx_core_conf_t *ccf; + u_char buf[NGX_INT64_LEN + 2]; + + ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "signal process started"); + + ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module); + + file.name = ccf->pid; + file.log = cycle->log; + + file.fd = ngx_open_file(file.name.data, NGX_FILE_RDONLY, + NGX_FILE_OPEN, NGX_FILE_DEFAULT_ACCESS); + + if (file.fd == NGX_INVALID_FILE) { + ngx_log_error(NGX_LOG_ERR, cycle->log, ngx_errno, + ngx_open_file_n " \"%s\" failed", file.name.data); + return 1; + } + + n = ngx_read_file(&file, buf, NGX_INT64_LEN + 2, 0); + + if (ngx_close_file(file.fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + ngx_close_file_n " \"%s\" failed", file.name.data); + } + + if (n == NGX_ERROR) { + return 1; + } + + while (n-- && (buf[n] == CR || buf[n] == LF)) { /* void */ } + + pid = ngx_atoi(buf, ++n); + + if (pid == NGX_ERROR) { + ngx_log_error(NGX_LOG_ERR, cycle->log, 0, + "invalid PID number \"%*s\" in \"%s\"", + n, buf, file.name.data); + return 1; + } + + return ngx_os_signal_process(cycle, sig, pid); + +} + + +static ngx_int_t +ngx_test_lockfile(u_char *file, ngx_log_t *log) +{ +#if !(NGX_HAVE_ATOMIC_OPS) + ngx_fd_t fd; + + fd = ngx_open_file(file, NGX_FILE_RDWR, NGX_FILE_CREATE_OR_OPEN, + NGX_FILE_DEFAULT_ACCESS); + + if (fd == NGX_INVALID_FILE) { + ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, + ngx_open_file_n " \"%s\" failed", file); + return NGX_ERROR; + } + + if (ngx_close_file(fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + ngx_close_file_n " \"%s\" failed", file); + } + + if (ngx_delete_file(file) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + ngx_delete_file_n " \"%s\" failed", file); + } + +#endif + + return NGX_OK; +} + + +void +ngx_reopen_files(ngx_cycle_t *cycle, ngx_uid_t user) +{ + ssize_t n, len; + ngx_fd_t fd; + ngx_uint_t i; + ngx_list_part_t *part; + ngx_open_file_t *file; + + part = &cycle->open_files.part; + file = part->elts; + + for (i = 0; /* void */ ; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + part = part->next; + file = part->elts; + i = 0; + } + + if (file[i].name.len == 0) { + continue; + } + + len = file[i].pos - file[i].buffer; + + if (file[i].buffer && len != 0) { + + n = ngx_write_fd(file[i].fd, file[i].buffer, len); + + if (n == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + ngx_write_fd_n " to \"%s\" failed", + file[i].name.data); + + } else if (n != len) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, + ngx_write_fd_n " to \"%s\" was incomplete: %z of %uz", + file[i].name.data, n, len); + } + + file[i].pos = file[i].buffer; + } + + fd = ngx_open_file(file[i].name.data, NGX_FILE_APPEND, + NGX_FILE_CREATE_OR_OPEN, NGX_FILE_DEFAULT_ACCESS); + + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, cycle->log, 0, + "reopen file \"%s\", old:%d new:%d", + file[i].name.data, file[i].fd, fd); + + if (fd == NGX_INVALID_FILE) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, + ngx_open_file_n " \"%s\" failed", file[i].name.data); + continue; + } + +#if !(NGX_WIN32) + if (user != (ngx_uid_t) NGX_CONF_UNSET_UINT) { + ngx_file_info_t fi; + + if (ngx_file_info((const char *) file[i].name.data, &fi) + == NGX_FILE_ERROR) + { + ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, + ngx_file_info_n " \"%s\" failed", + file[i].name.data); + + if (ngx_close_file(fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, + ngx_close_file_n " \"%s\" failed", + file[i].name.data); + } + } + + if (fi.st_uid != user) { + if (chown((const char *) file[i].name.data, user, -1) == -1) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, + "chown(\"%s\", %d) failed", + file[i].name.data, user); + + if (ngx_close_file(fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, + ngx_close_file_n " \"%s\" failed", + file[i].name.data); + } + } + } + + if ((fi.st_mode & (S_IRUSR|S_IWUSR)) != (S_IRUSR|S_IWUSR)) { + + fi.st_mode |= (S_IRUSR|S_IWUSR); + + if (chmod((const char *) file[i].name.data, fi.st_mode) == -1) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, + "chmod() \"%s\" failed", file[i].name.data); + + if (ngx_close_file(fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, + ngx_close_file_n " \"%s\" failed", + file[i].name.data); + } + } + } + } + + if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, + "fcntl(FD_CLOEXEC) \"%s\" failed", + file[i].name.data); + + if (ngx_close_file(fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, + ngx_close_file_n " \"%s\" failed", + file[i].name.data); + } + + continue; + } +#endif + + if (ngx_close_file(file[i].fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, + ngx_close_file_n " \"%s\" failed", + file[i].name.data); + } + + file[i].fd = fd; + } + +#if !(NGX_WIN32) + + if (cycle->log->file->fd != STDERR_FILENO) { + if (dup2(cycle->log->file->fd, STDERR_FILENO) == -1) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, + "dup2(STDERR) failed"); + } + } + +#endif +} + + +ngx_shm_zone_t * +ngx_shared_memory_add(ngx_conf_t *cf, ngx_str_t *name, size_t size, void *tag) +{ + ngx_uint_t i; + ngx_shm_zone_t *shm_zone; + ngx_list_part_t *part; + + part = &cf->cycle->shared_memory.part; + shm_zone = part->elts; + + for (i = 0; /* void */ ; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + part = part->next; + shm_zone = part->elts; + i = 0; + } + + if (name->len != shm_zone[i].shm.name.len) { + continue; + } + + if (ngx_strncmp(name->data, shm_zone[i].shm.name.data, name->len) + != 0) + { + continue; + } + + if (size && size != shm_zone[i].shm.size) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "the size %uz of shared memory zone \"%V\" " + "conflicts with already declared size %uz", + size, &shm_zone[i].shm.name, shm_zone[i].shm.size); + return NULL; + } + + if (tag != shm_zone[i].tag) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "the shared memory zone \"%V\" is " + "already declared for a different use", + &shm_zone[i].shm.name); + return NULL; + } + + return &shm_zone[i]; + } + + shm_zone = ngx_list_push(&cf->cycle->shared_memory); + + if (shm_zone == NULL) { + return NULL; + } + + shm_zone->data = NULL; + shm_zone->shm.log = cf->cycle->log; + shm_zone->shm.size = size; + shm_zone->shm.name = *name; + shm_zone->shm.exists = 0; + shm_zone->init = NULL; + shm_zone->tag = tag; + + return shm_zone; +} + + +static void +ngx_clean_old_cycles(ngx_event_t *ev) +{ + ngx_uint_t i, n, found, live; + ngx_log_t *log; + ngx_cycle_t **cycle; + + log = ngx_cycle->log; + ngx_temp_pool->log = log; + + ngx_log_debug0(NGX_LOG_DEBUG_CORE, log, 0, "clean old cycles"); + + live = 0; + + cycle = ngx_old_cycles.elts; + for (i = 0; i < ngx_old_cycles.nelts; i++) { + + if (cycle[i] == NULL) { + continue; + } + + found = 0; + + for (n = 0; n < cycle[i]->connection_n; n++) { + if (cycle[i]->connections[n].fd != (ngx_socket_t) -1) { + found = 1; + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, log, 0, "live fd:%d", n); + + break; + } + } + + if (found) { + live = 1; + continue; + } + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, log, 0, "clean old cycle: %d", i); + + ngx_destroy_pool(cycle[i]->pool); + cycle[i] = NULL; + } + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, log, 0, "old cycles status: %d", live); + + if (live) { + ngx_add_timer(ev, 30000); + + } else { + ngx_destroy_pool(ngx_temp_pool); + ngx_temp_pool = NULL; + ngx_old_cycles.nelts = 0; + } +} diff -Nru nginx-0.5.33/.pc/.quilt_patches nginx-0.8.53/.pc/.quilt_patches --- nginx-0.5.33/.pc/.quilt_patches 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/.pc/.quilt_patches 2011-02-26 14:04:40.000000000 +0000 @@ -0,0 +1 @@ +debian/patches diff -Nru nginx-0.5.33/.pc/.quilt_series nginx-0.8.53/.pc/.quilt_series --- nginx-0.5.33/.pc/.quilt_series 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/.pc/.quilt_series 2011-02-26 14:04:40.000000000 +0000 @@ -0,0 +1 @@ +series diff -Nru nginx-0.5.33/.pc/.version nginx-0.8.53/.pc/.version --- nginx-0.5.33/.pc/.version 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/.pc/.version 2011-02-26 14:04:40.000000000 +0000 @@ -0,0 +1 @@ +2 diff -Nru nginx-0.5.33/src/core/nginx.c nginx-0.8.53/src/core/nginx.c --- nginx-0.5.33/src/core/nginx.c 2007-07-22 08:40:39.000000000 +0000 +++ nginx-0.8.53/src/core/nginx.c 2010-09-15 15:24:21.000000000 +0000 @@ -6,12 +6,12 @@ #include #include -#include #include static ngx_int_t ngx_add_inherited_sockets(ngx_cycle_t *cycle); -static ngx_int_t ngx_getopt(ngx_cycle_t *cycle, int argc, char *const *argv); +static ngx_int_t ngx_get_options(int argc, char *const *argv); +static ngx_int_t ngx_process_options(ngx_cycle_t *cycle); static ngx_int_t ngx_save_argv(ngx_cycle_t *cycle, int argc, char *const *argv); static void *ngx_core_module_create_conf(ngx_cycle_t *cycle); static char *ngx_core_module_init_conf(ngx_cycle_t *cycle, void *conf); @@ -110,7 +110,7 @@ { ngx_string("worker_rlimit_core"), NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1, - ngx_conf_set_size_slot, + ngx_conf_set_off_slot, 0, offsetof(ngx_core_conf_t, rlimit_core), NULL }, @@ -181,10 +181,16 @@ }; -ngx_uint_t ngx_max_module; +ngx_uint_t ngx_max_module; + +static ngx_uint_t ngx_show_help; +static ngx_uint_t ngx_show_version; +static ngx_uint_t ngx_show_configure; +static u_char *ngx_prefix; +static u_char *ngx_conf_file; +static u_char *ngx_conf_params; +static char *ngx_signal; -static ngx_uint_t ngx_show_version; -static ngx_uint_t ngx_show_configure; static char **ngx_os_environ; @@ -197,6 +203,59 @@ ngx_cycle_t *cycle, init_cycle; ngx_core_conf_t *ccf; + if (ngx_get_options(argc, argv) != NGX_OK) { + return 1; + } + + if (ngx_show_version) { + ngx_log_stderr(0, "nginx version: " NGINX_VER); + + if (ngx_show_help) { + ngx_log_stderr(0, + "Usage: nginx [-?hvVtq] [-s signal] [-c filename] " + "[-p prefix] [-g directives]" CRLF CRLF + "Options:" CRLF + " -?,-h : this help" CRLF + " -v : show version and exit" CRLF + " -V : show version and configure options then exit" + CRLF + " -t : test configuration and exit" CRLF + " -q : suppress non-error messages " + "during configuration testing" CRLF + " -s signal : send signal to a master process: " + "stop, quit, reopen, reload" CRLF +#ifdef NGX_PREFIX + " -p prefix : set prefix path (default: " + NGX_PREFIX ")" CRLF +#else + " -p prefix : set prefix path (default: NONE)" CRLF +#endif + " -c filename : set configuration file (default: " + NGX_CONF_PATH ")" CRLF + " -g directives : set global directives out of configuration " + "file" CRLF + ); + } + + if (ngx_show_configure) { +#ifdef NGX_COMPILER + ngx_log_stderr(0, "built by " NGX_COMPILER); +#endif +#if (NGX_SSL) +#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME + ngx_log_stderr(0, "TLS SNI support enabled"); +#else + ngx_log_stderr(0, "TLS SNI support disabled"); +#endif +#endif + ngx_log_stderr(0, "configure arguments:" NGX_CONFIGURE); + } + + if (!ngx_test_config) { + return 0; + } + } + #if (NGX_FREEBSD) ngx_debug_init(); #endif @@ -211,7 +270,7 @@ ngx_pid = ngx_getpid(); - log = ngx_log_init(); + log = ngx_log_init(ngx_prefix); if (log == NULL) { return 1; } @@ -221,7 +280,10 @@ ngx_ssl_init(log); #endif - /* init_cycle->log is required for signal handlers and ngx_getopt() */ + /* + * init_cycle->log is required for signal handlers and + * ngx_process_options() + */ ngx_memzero(&init_cycle, sizeof(ngx_cycle_t)); init_cycle.log = log; @@ -236,46 +298,19 @@ return 1; } - if (ngx_getopt(&init_cycle, argc, ngx_argv) != NGX_OK) { + if (ngx_process_options(&init_cycle) != NGX_OK) { return 1; } - if (ngx_show_version) { - ngx_write_fd(ngx_stderr_fileno, "nginx version: " NGINX_VER CRLF, - sizeof("nginx version: " NGINX_VER CRLF) - 1); - - if (ngx_show_configure) { -#ifdef NGX_COMPILER - ngx_write_fd(ngx_stderr_fileno, "built by " NGX_COMPILER CRLF, - sizeof("built by " NGX_COMPILER CRLF) - 1); -#endif - -#ifndef __WATCOMC__ - - /* OpenWatcomC could not build the long NGX_CONFIGURE string */ - - ngx_write_fd(ngx_stderr_fileno, - "configure arguments: " NGX_CONFIGURE CRLF, - sizeof("configure arguments :" NGX_CONFIGURE CRLF) - 1); -#endif - } - - if (!ngx_test_config) { - return 0; - } - } - - if (ngx_test_config) { - log->log_level = NGX_LOG_INFO; - } - if (ngx_os_init(log) != NGX_OK) { return 1; } - /* ngx_crc32_init() requires ngx_cacheline_size set in ngx_os_init() */ + /* + * ngx_crc32_table_init() requires ngx_cacheline_size set in ngx_os_init() + */ - if (ngx_crc32_init() != NGX_OK) { + if (ngx_crc32_table_init() != NGX_OK) { return 1; } @@ -291,45 +326,37 @@ cycle = ngx_init_cycle(&init_cycle); if (cycle == NULL) { if (ngx_test_config) { - ngx_log_error(NGX_LOG_EMERG, log, 0, - "the configuration file %s test failed", - init_cycle.conf_file.data); + ngx_log_stderr(0, "configuration file %s test failed", + init_cycle.conf_file.data); } return 1; } if (ngx_test_config) { - ngx_log_error(NGX_LOG_INFO, log, 0, - "the configuration file %s was tested successfully", - cycle->conf_file.data); + if (!ngx_quiet_mode) { + ngx_log_stderr(0, "configuration file %s test is successful", + cycle->conf_file.data); + } + return 0; } + if (ngx_signal) { + return ngx_signal_process(cycle, ngx_signal); + } + ngx_os_status(cycle->log); ngx_cycle = cycle; ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module); - ngx_process = ccf->master ? NGX_PROCESS_MASTER : NGX_PROCESS_SINGLE; - -#if (NGX_WIN32) - -#if 0 - - TODO: - - if (ccf->run_as_service) { - if (ngx_service(cycle->log) != NGX_OK) { - return 1; - } - - return 0; + if (ccf->master && ngx_process == NGX_PROCESS_SINGLE) { + ngx_process = NGX_PROCESS_MASTER; } -#endif -#else +#if !(NGX_WIN32) if (ngx_init_signals(cycle->log) != NGX_OK) { return 1; @@ -343,17 +370,35 @@ ngx_daemonized = 1; } +#endif + if (ngx_create_pidfile(&ccf->pid, cycle->log) != NGX_OK) { return 1; } -#endif + if (cycle->log->file->fd != ngx_stderr) { - if (ngx_process == NGX_PROCESS_MASTER) { - ngx_master_process_cycle(cycle); + if (ngx_set_stderr(cycle->log->file->fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, + ngx_set_stderr_n " failed"); + return 1; + } + } - } else { + if (log->file->fd != ngx_stderr) { + if (ngx_close_file(log->file->fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + ngx_close_file_n " built-in log failed"); + } + } + + ngx_use_stderr = 0; + + if (ngx_process == NGX_PROCESS_SINGLE) { ngx_single_process_cycle(cycle); + + } else { + ngx_master_process_cycle(cycle); } return 0; @@ -378,7 +423,7 @@ if (ngx_array_init(&cycle->listening, cycle->pool, 10, sizeof(ngx_listening_t)) - == NGX_ERROR) + != NGX_OK) { return NGX_ERROR; } @@ -523,6 +568,8 @@ ngx_core_conf_t *ccf; ngx_listening_t *ls; + ngx_memzero(&ctx, sizeof(ngx_exec_ctx_t)); + ctx.path = argv[0]; ctx.name = "new binary process"; ctx.argv = argv; @@ -606,58 +653,122 @@ static ngx_int_t -ngx_getopt(ngx_cycle_t *cycle, int argc, char *const *argv) +ngx_get_options(int argc, char *const *argv) { - ngx_int_t i; + u_char *p; + ngx_int_t i; for (i = 1; i < argc; i++) { - if (argv[i][0] != '-') { - ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, - "invalid option: \"%s\"", argv[i]); + + p = (u_char *) argv[i]; + + if (*p++ != '-') { + ngx_log_stderr(0, "invalid option: \"%s\"", argv[i]); return NGX_ERROR; } - switch (argv[i][1]) { + while (*p) { - case 'v': - ngx_show_version = 1; - break; + switch (*p++) { - case 'V': - ngx_show_version = 1; - ngx_show_configure = 1; - break; + case '?': + case 'h': + ngx_show_version = 1; + ngx_show_help = 1; + break; - case 't': - ngx_test_config = 1; - break; + case 'v': + ngx_show_version = 1; + break; - case 'c': - if (argv[i + 1] == NULL) { - ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, - "the option: \"%s\" requires file name", - argv[i]); + case 'V': + ngx_show_version = 1; + ngx_show_configure = 1; + break; + + case 't': + ngx_test_config = 1; + break; + + case 'q': + ngx_quiet_mode = 1; + break; + + case 'p': + if (*p) { + ngx_prefix = p; + goto next; + } + + if (argv[++i]) { + ngx_prefix = (u_char *) argv[i]; + goto next; + } + + ngx_log_stderr(0, "option \"-p\" requires directory name"); return NGX_ERROR; - } - cycle->conf_file.data = (u_char *) argv[++i]; - cycle->conf_file.len = ngx_strlen(cycle->conf_file.data); - break; + case 'c': + if (*p) { + ngx_conf_file = p; + goto next; + } + + if (argv[++i]) { + ngx_conf_file = (u_char *) argv[i]; + goto next; + } - default: - ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, - "invalid option: \"%s\"", argv[i]); - return NGX_ERROR; + ngx_log_stderr(0, "option \"-c\" requires file name"); + return NGX_ERROR; + + case 'g': + if (*p) { + ngx_conf_params = p; + goto next; + } + + if (argv[++i]) { + ngx_conf_params = (u_char *) argv[i]; + goto next; + } + + ngx_log_stderr(0, "option \"-g\" requires parameter"); + return NGX_ERROR; + + case 's': + if (*p) { + ngx_signal = (char *) p; + + } else if (argv[++i]) { + ngx_signal = argv[i]; + + } else { + ngx_log_stderr(0, "option \"-s\" requires parameter"); + return NGX_ERROR; + } + + if (ngx_strcmp(ngx_signal, "stop") == 0 + || ngx_strcmp(ngx_signal, "quit") == 0 + || ngx_strcmp(ngx_signal, "reopen") == 0 + || ngx_strcmp(ngx_signal, "reload") == 0) + { + ngx_process = NGX_PROCESS_SIGNALLER; + goto next; + } + + ngx_log_stderr(0, "invalid option: \"-s %s\"", ngx_signal); + return NGX_ERROR; + + default: + ngx_log_stderr(0, "invalid option: \"%c\"", *(p - 1)); + return NGX_ERROR; + } } - } - if (cycle->conf_file.data == NULL) { - cycle->conf_file.len = sizeof(NGX_CONF_PATH) - 1; - cycle->conf_file.data = (u_char *) NGX_CONF_PATH; - } + next: - if (ngx_conf_full_name(cycle, &cycle->conf_file) == NGX_ERROR) { - return NGX_ERROR; + continue; } return NGX_OK; @@ -706,6 +817,102 @@ } +static ngx_int_t +ngx_process_options(ngx_cycle_t *cycle) +{ + u_char *p; + size_t len; + + if (ngx_prefix) { + len = ngx_strlen(ngx_prefix); + p = ngx_prefix; + + if (!ngx_path_separator(*p)) { + p = ngx_pnalloc(cycle->pool, len + 1); + if (p == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(p, ngx_prefix, len); + p[len++] = '/'; + } + + cycle->conf_prefix.len = len; + cycle->conf_prefix.data = p; + cycle->prefix.len = len; + cycle->prefix.data = p; + + } else { + +#ifndef NGX_PREFIX + + p = ngx_pnalloc(cycle->pool, NGX_MAX_PATH); + if (p == NULL) { + return NGX_ERROR; + } + + if (ngx_getcwd(p, NGX_MAX_PATH) == 0) { + ngx_log_stderr(ngx_errno, "[emerg]: " ngx_getcwd_n " failed"); + return NGX_ERROR; + } + + len = ngx_strlen(p); + + p[len++] = '/'; + + cycle->conf_prefix.len = len; + cycle->conf_prefix.data = p; + cycle->prefix.len = len; + cycle->prefix.data = p; + +#else + +#ifdef NGX_CONF_PREFIX + ngx_str_set(&cycle->conf_prefix, NGX_CONF_PREFIX); +#else + ngx_str_set(&cycle->conf_prefix, NGX_PREFIX); +#endif + ngx_str_set(&cycle->prefix, NGX_PREFIX); + +#endif + } + + if (ngx_conf_file) { + cycle->conf_file.len = ngx_strlen(ngx_conf_file); + cycle->conf_file.data = ngx_conf_file; + + } else { + ngx_str_set(&cycle->conf_file, NGX_CONF_PATH); + } + + if (ngx_conf_full_name(cycle, &cycle->conf_file, 0) != NGX_OK) { + return NGX_ERROR; + } + + for (p = cycle->conf_file.data + cycle->conf_file.len - 1; + p > cycle->conf_file.data; + p--) + { + if (ngx_path_separator(*p)) { + cycle->conf_prefix.len = p - ngx_cycle->conf_file.data + 1; + cycle->conf_prefix.data = ngx_cycle->conf_file.data; + break; + } + } + + if (ngx_conf_params) { + cycle->conf_param.len = ngx_strlen(ngx_conf_params); + cycle->conf_param.data = ngx_conf_params; + } + + if (ngx_test_config) { + cycle->log->log_level = NGX_LOG_INFO; + } + + return NGX_OK; +} + + static void * ngx_core_module_create_conf(ngx_cycle_t *cycle) { @@ -717,7 +924,7 @@ } /* - * set by pcalloc() + * set by ngx_pcalloc() * * ccf->pid = NULL; * ccf->oldpid = NULL; @@ -734,7 +941,7 @@ ccf->debug_points = NGX_CONF_UNSET; ccf->rlimit_nofile = NGX_CONF_UNSET; - ccf->rlimit_core = NGX_CONF_UNSET_SIZE; + ccf->rlimit_core = NGX_CONF_UNSET; ccf->rlimit_sigpending = NGX_CONF_UNSET; ccf->user = (ngx_uid_t) NGX_CONF_UNSET_UINT; @@ -760,12 +967,6 @@ { ngx_core_conf_t *ccf = conf; -#if !(NGX_WIN32) - ngx_str_t lock_file; - struct group *grp; - struct passwd *pwd; -#endif - ngx_conf_init_value(ccf->daemon, 1); ngx_conf_init_value(ccf->master, 1); ngx_conf_init_msec_value(ccf->timer_resolution, 0); @@ -795,9 +996,31 @@ #endif + + if (ccf->pid.len == 0) { + ngx_str_set(&ccf->pid, NGX_PID_PATH); + } + + if (ngx_conf_full_name(cycle, &ccf->pid, 0) != NGX_OK) { + return NGX_CONF_ERROR; + } + + ccf->oldpid.len = ccf->pid.len + sizeof(NGX_OLDPID_EXT); + + ccf->oldpid.data = ngx_pnalloc(cycle->pool, ccf->oldpid.len); + if (ccf->oldpid.data == NULL) { + return NGX_CONF_ERROR; + } + + ngx_memcpy(ngx_cpymem(ccf->oldpid.data, ccf->pid.data, ccf->pid.len), + NGX_OLDPID_EXT, sizeof(NGX_OLDPID_EXT)); + + #if !(NGX_WIN32) if (ccf->user == (uid_t) NGX_CONF_UNSET_UINT && geteuid() == 0) { + struct group *grp; + struct passwd *pwd; ngx_set_errno(0); pwd = getpwnam(NGX_USER); @@ -821,35 +1044,18 @@ ccf->group = grp->gr_gid; } - if (ccf->pid.len == 0) { - ccf->pid.len = sizeof(NGX_PID_PATH) - 1; - ccf->pid.data = (u_char *) NGX_PID_PATH; - } - - if (ngx_conf_full_name(cycle, &ccf->pid) == NGX_ERROR) { - return NGX_CONF_ERROR; - } - - ccf->oldpid.len = ccf->pid.len + sizeof(NGX_OLDPID_EXT); - - ccf->oldpid.data = ngx_palloc(cycle->pool, ccf->oldpid.len); - if (ccf->oldpid.data == NULL) { - return NGX_CONF_ERROR; - } - - ngx_memcpy(ngx_cpymem(ccf->oldpid.data, ccf->pid.data, ccf->pid.len), - NGX_OLDPID_EXT, sizeof(NGX_OLDPID_EXT)); - if (ccf->lock_file.len == 0) { - ccf->lock_file.len = sizeof(NGX_LOCK_PATH) - 1; - ccf->lock_file.data = (u_char *) NGX_LOCK_PATH; + ngx_str_set(&ccf->lock_file, NGX_LOCK_PATH); } - if (ngx_conf_full_name(cycle, &ccf->lock_file) == NGX_ERROR) { + if (ngx_conf_full_name(cycle, &ccf->lock_file, 0) != NGX_OK) { return NGX_CONF_ERROR; } + { + ngx_str_t lock_file; + lock_file = cycle->old_cycle->lock_file; if (lock_file.len) { @@ -873,7 +1079,7 @@ } else { cycle->lock_file.len = ccf->lock_file.len + 1; - cycle->lock_file.data = ngx_palloc(cycle->pool, + cycle->lock_file.data = ngx_pnalloc(cycle->pool, ccf->lock_file.len + sizeof(".accept")); if (cycle->lock_file.data == NULL) { return NGX_CONF_ERROR; @@ -883,6 +1089,7 @@ ccf->lock_file.len), ".accept", sizeof(".accept")); } + } #endif @@ -1080,7 +1287,7 @@ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid character \"%c\" in \"worker_cpu_affinity\"", ch); - return NGX_CONF_ERROR ; + return NGX_CONF_ERROR; } } diff -Nru nginx-0.5.33/src/core/nginx.h nginx-0.8.53/src/core/nginx.h --- nginx-0.5.33/src/core/nginx.h 2007-09-24 04:19:28.000000000 +0000 +++ nginx-0.8.53/src/core/nginx.h 2010-10-04 13:50:09.000000000 +0000 @@ -8,7 +8,8 @@ #define _NGINX_H_INCLUDED_ -#define NGINX_VERSION "0.5.33" +#define nginx_version 8053 +#define NGINX_VERSION "0.8.53" #define NGINX_VER "nginx/" NGINX_VERSION #define NGINX_VAR "NGINX" diff -Nru nginx-0.5.33/src/core/ngx_array.c nginx-0.8.53/src/core/ngx_array.c --- nginx-0.5.33/src/core/ngx_array.c 2006-08-30 10:39:17.000000000 +0000 +++ nginx-0.8.53/src/core/ngx_array.c 2008-06-17 15:00:30.000000000 +0000 @@ -39,12 +39,12 @@ p = a->pool; - if ((u_char *) a->elts + a->size * a->nalloc == p->last) { - p->last -= a->size * a->nalloc; + if ((u_char *) a->elts + a->size * a->nalloc == p->d.last) { + p->d.last -= a->size * a->nalloc; } - if ((u_char *) a + sizeof(ngx_array_t) == p->last) { - p->last = (u_char *) a; + if ((u_char *) a + sizeof(ngx_array_t) == p->d.last) { + p->d.last = (u_char *) a; } } @@ -64,14 +64,15 @@ p = a->pool; - if ((u_char *) a->elts + size == p->last && p->last + a->size <= p->end) + if ((u_char *) a->elts + size == p->d.last + && p->d.last + a->size <= p->d.end) { /* * the array allocation is the last in the pool * and there is space for new allocation */ - p->last += a->size; + p->d.last += a->size; a->nalloc++; } else { @@ -111,15 +112,15 @@ p = a->pool; - if ((u_char *) a->elts + a->size * a->nalloc == p->last - && p->last + size <= p->end) + if ((u_char *) a->elts + a->size * a->nalloc == p->d.last + && p->d.last + size <= p->d.end) { /* * the array allocation is the last in the pool * and there is space for new allocation */ - p->last += size; + p->d.last += size; a->nalloc += n; } else { diff -Nru nginx-0.5.33/src/core/ngx_buf.c nginx-0.8.53/src/core/ngx_buf.c --- nginx-0.5.33/src/core/ngx_buf.c 2006-08-30 10:39:17.000000000 +0000 +++ nginx-0.8.53/src/core/ngx_buf.c 2008-12-17 20:47:18.000000000 +0000 @@ -201,12 +201,6 @@ break; } -#if (NGX_HAVE_WRITE_ZEROCOPY) - if ((*busy)->buf->zerocopy_busy) { - break; - } -#endif - if ((*busy)->buf->tag != tag) { *busy = (*busy)->next; continue; diff -Nru nginx-0.5.33/src/core/ngx_buf.h nginx-0.8.53/src/core/ngx_buf.h --- nginx-0.5.33/src/core/ngx_buf.h 2006-08-30 10:39:17.000000000 +0000 +++ nginx-0.8.53/src/core/ngx_buf.h 2009-09-13 06:28:17.000000000 +0000 @@ -51,8 +51,6 @@ unsigned last_shadow:1; unsigned temp_file:1; - unsigned zerocopy_busy:1; - /* STUB */ int num; }; @@ -69,17 +67,35 @@ } ngx_bufs_t; +typedef struct ngx_output_chain_ctx_s ngx_output_chain_ctx_t; + typedef ngx_int_t (*ngx_output_chain_filter_pt)(void *ctx, ngx_chain_t *in); -typedef struct { +#if (NGX_HAVE_FILE_AIO) +typedef void (*ngx_output_chain_aio_pt)(ngx_output_chain_ctx_t *ctx, + ngx_file_t *file); +#endif + +struct ngx_output_chain_ctx_s { ngx_buf_t *buf; ngx_chain_t *in; ngx_chain_t *free; ngx_chain_t *busy; - unsigned sendfile; - unsigned need_in_memory; - unsigned need_in_temp; + unsigned sendfile:1; + unsigned directio:1; +#if (NGX_HAVE_ALIGNED_DIRECTIO) + unsigned unaligned:1; +#endif + unsigned need_in_memory:1; + unsigned need_in_temp:1; +#if (NGX_HAVE_FILE_AIO) + unsigned aio:1; + + ngx_output_chain_aio_pt aio_handler; +#endif + + off_t alignment; ngx_pool_t *pool; ngx_int_t allocated; @@ -88,7 +104,7 @@ ngx_output_chain_filter_pt output_filter; void *filter_ctx; -} ngx_output_chain_ctx_t; +}; typedef struct { diff -Nru nginx-0.5.33/src/core/ngx_conf_file.c nginx-0.8.53/src/core/ngx_conf_file.c --- nginx-0.5.33/src/core/ngx_conf_file.c 2007-09-01 09:53:10.000000000 +0000 +++ nginx-0.8.53/src/core/ngx_conf_file.c 2010-06-23 16:34:54.000000000 +0000 @@ -7,10 +7,12 @@ #include #include +#define NGX_CONF_BUFFER 4096 static ngx_int_t ngx_conf_handler(ngx_conf_t *cf, ngx_int_t last); static ngx_int_t ngx_conf_read_token(ngx_conf_t *cf); static char *ngx_conf_include(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static ngx_int_t ngx_conf_test_full_name(ngx_str_t *name); static void ngx_conf_flush_files(ngx_cycle_t *cycle); @@ -43,9 +45,9 @@ }; -/* The ten fixed arguments */ +/* The eight fixed arguments */ -static int argument_number[] = { +static ngx_uint_t argument_number[] = { NGX_CONF_NOARGS, NGX_CONF_TAKE1, NGX_CONF_TAKE2, @@ -58,14 +60,57 @@ char * +ngx_conf_param(ngx_conf_t *cf) +{ + char *rv; + ngx_str_t *param; + ngx_buf_t b; + ngx_conf_file_t conf_file; + + param = &cf->cycle->conf_param; + + if (param->len == 0) { + return NGX_CONF_OK; + } + + ngx_memzero(&conf_file, sizeof(ngx_conf_file_t)); + + ngx_memzero(&b, sizeof(ngx_buf_t)); + + b.start = param->data; + b.pos = param->data; + b.last = param->data + param->len; + b.end = b.last; + b.temporary = 1; + + conf_file.file.fd = NGX_INVALID_FILE; + conf_file.file.name.data = NULL; + conf_file.line = 0; + + cf->conf_file = &conf_file; + cf->conf_file->buffer = &b; + + rv = ngx_conf_parse(cf, NULL); + + cf->conf_file = NULL; + + return rv; +} + + +char * ngx_conf_parse(ngx_conf_t *cf, ngx_str_t *filename) { char *rv; ngx_fd_t fd; ngx_int_t rc; - ngx_buf_t *b; - ngx_uint_t block; - ngx_conf_file_t *prev; + ngx_buf_t buf; + ngx_conf_file_t *prev, conf_file; + enum { + parse_file = 0, + parse_block, + parse_param + } type; #if (NGX_SUPPRESS_WARN) fd = NGX_INVALID_FILE; @@ -86,44 +131,40 @@ prev = cf->conf_file; - cf->conf_file = ngx_palloc(cf->pool, sizeof(ngx_conf_file_t)); - if (cf->conf_file == NULL) { - return NGX_CONF_ERROR; - } + cf->conf_file = &conf_file; if (ngx_fd_info(fd, &cf->conf_file->file.info) == -1) { ngx_log_error(NGX_LOG_EMERG, cf->log, ngx_errno, ngx_fd_info_n " \"%s\" failed", filename->data); } - b = ngx_calloc_buf(cf->pool); - if (b == NULL) { - return NGX_CONF_ERROR; - } - - cf->conf_file->buffer = b; + cf->conf_file->buffer = &buf; - b->start = ngx_alloc(ngx_pagesize, cf->log); - if (b->start == NULL) { - return NGX_CONF_ERROR; + buf.start = ngx_alloc(NGX_CONF_BUFFER, cf->log); + if (buf.start == NULL) { + goto failed; } - b->pos = b->start; - b->last = b->start; - b->end = b->last + ngx_pagesize; - b->temporary = 1; + buf.pos = buf.start; + buf.last = buf.start; + buf.end = buf.last + NGX_CONF_BUFFER; + buf.temporary = 1; cf->conf_file->file.fd = fd; cf->conf_file->file.name.len = filename->len; cf->conf_file->file.name.data = filename->data; cf->conf_file->file.offset = 0; - cf->conf_file->file.log = cf->log;; + cf->conf_file->file.log = cf->log; cf->conf_file->line = 1; - block = 0; + type = parse_file; + + } else if (cf->conf_file->file.fd != NGX_INVALID_FILE) { + + type = parse_block; } else { - block = 1; + type = parse_param; } @@ -145,24 +186,38 @@ } if (rc == NGX_CONF_BLOCK_DONE) { - if (!block) { + + if (type != parse_block) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "unexpected \"}\""); goto failed; } - block = 0; + goto done; } - if (rc == NGX_CONF_FILE_DONE && block) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "unexpected end of file, expecting \"}\""); - goto failed; - } + if (rc == NGX_CONF_FILE_DONE) { + + if (type == parse_block) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "unexpected end of file, expecting \"}\""); + goto failed; + } - if (rc != NGX_OK && rc != NGX_CONF_BLOCK_START) { goto done; } + if (rc == NGX_CONF_BLOCK_START) { + + if (type == parse_param) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "block directives are not supported " + "in -g option"); + goto failed; + } + } + + /* rc == NGX_OK || rc == NGX_CONF_BLOCK_START */ + if (cf->handler) { /* @@ -199,16 +254,18 @@ done: if (filename) { - ngx_free(cf->conf_file->buffer->start); - - cf->conf_file = prev; + if (cf->conf_file->buffer->start) { + ngx_free(cf->conf_file->buffer->start); + } if (ngx_close_file(fd) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno, ngx_close_file_n " %s failed", - cf->conf_file->file.name.data); + filename->data); return NGX_CONF_ERROR; } + + cf->conf_file = prev; } if (rc == NGX_ERROR) { @@ -377,10 +434,11 @@ ngx_conf_read_token(ngx_conf_t *cf) { u_char *start, ch, *src, *dst; - int len; - int found, need_space, last_space, sharp_comment, variable; - int quoted, s_quoted, d_quoted; - ssize_t n; + off_t file_size; + size_t len; + ssize_t n, size; + ngx_uint_t found, need_space, last_space, sharp_comment, variable; + ngx_uint_t quoted, s_quoted, d_quoted, start_line; ngx_str_t *word; ngx_buf_t *b; @@ -389,19 +447,32 @@ last_space = 1; sharp_comment = 0; variable = 0; - quoted = s_quoted = d_quoted = 0; + quoted = 0; + s_quoted = 0; + d_quoted = 0; cf->args->nelts = 0; b = cf->conf_file->buffer; start = b->pos; + start_line = cf->conf_file->line; + + file_size = ngx_file_size(&cf->conf_file->file.info); for ( ;; ) { if (b->pos >= b->last) { - if (cf->conf_file->file.offset - >= ngx_file_size(&cf->conf_file->file.info)) - { + + if (cf->conf_file->file.offset >= file_size) { + if (cf->args->nelts > 0) { + + if (cf->conf_file->file.fd == NGX_INVALID_FILE) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "unexpected end of parameter, " + "expecting \";\""); + return NGX_ERROR; + } + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "unexpected end of file, " "expecting \";\" or \"}\""); @@ -411,22 +482,58 @@ return NGX_CONF_FILE_DONE; } - if (b->pos - start) { - ngx_memcpy(b->start, start, b->pos - start); + len = b->pos - start; + + if (len == NGX_CONF_BUFFER) { + cf->conf_file->line = start_line; + + if (d_quoted) { + ch = '"'; + + } else if (s_quoted) { + ch = '\''; + + } else { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "too long parameter \"%*s...\" started", + 10, start); + return NGX_ERROR; + } + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "too long parameter, probably " + "missing terminating \"%c\" character", ch); + return NGX_ERROR; } - n = ngx_read_file(&cf->conf_file->file, - b->start + (b->pos - start), - b->end - (b->start + (b->pos - start)), + if (len) { + ngx_memcpy(b->start, start, len); + } + + size = (ssize_t) (file_size - cf->conf_file->file.offset); + + if (size > b->end - (b->start + len)) { + size = b->end - (b->start + len); + } + + n = ngx_read_file(&cf->conf_file->file, b->start + len, size, cf->conf_file->file.offset); if (n == NGX_ERROR) { return NGX_ERROR; } - b->pos = b->start + (b->pos - start); - start = b->start; + if (n != size) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + ngx_read_file_n " returned " + "only %z bytes instead of %z", + n, size); + return NGX_ERROR; + } + + b->pos = b->start + len; b->last = b->pos + n; + start = b->start; } ch = *b->pos++; @@ -480,6 +587,7 @@ } start = b->pos - 1; + start_line = cf->conf_file->line; switch (ch) { @@ -563,7 +671,8 @@ } } else if (ch == ' ' || ch == '\t' || ch == CR || ch == LF - || ch == ';' || ch == '{') { + || ch == ';' || ch == '{') + { last_space = 1; found = 1; } @@ -574,7 +683,7 @@ return NGX_ERROR; } - word->data = ngx_palloc(cf->pool, b->pos - start + 1); + word->data = ngx_pnalloc(cf->pool, b->pos - start + 1); if (word->data == NULL) { return NGX_ERROR; } @@ -633,7 +742,7 @@ { char *rv; ngx_int_t n; - ngx_str_t *value, file; + ngx_str_t *value, file, name; ngx_glob_t gl; value = cf->args->elts; @@ -641,14 +750,22 @@ ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data); - if (ngx_conf_full_name(cf->cycle, &file) == NGX_ERROR) { + if (ngx_conf_full_name(cf->cycle, &file, 1) != NGX_OK) { return NGX_CONF_ERROR; } + if (strpbrk((char *) file.data, "*?[") == NULL) { + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data); + + return ngx_conf_parse(cf, &file); + } + ngx_memzero(&gl, sizeof(ngx_glob_t)); gl.pattern = file.data; gl.log = cf->log; + gl.test = 1; if (ngx_open_glob(&gl) != NGX_OK) { ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno, @@ -659,12 +776,15 @@ rv = NGX_CONF_OK; for ( ;; ) { - n = ngx_read_glob(&gl, &file); + n = ngx_read_glob(&gl, &name); if (n != NGX_OK) { break; } + file.len = name.len++; + file.data = ngx_pstrdup(cf->pool, &name); + ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data); rv = ngx_conf_parse(cf, &file); @@ -681,40 +801,97 @@ ngx_int_t -ngx_conf_full_name(ngx_cycle_t *cycle, ngx_str_t *name) +ngx_conf_full_name(ngx_cycle_t *cycle, ngx_str_t *name, ngx_uint_t conf_prefix) { - u_char *p; - ngx_str_t old; + size_t len; + u_char *p, *n, *prefix; + ngx_int_t rc; - if (name->data[0] == '/') { - return NGX_OK; + rc = ngx_conf_test_full_name(name); + + if (rc == NGX_OK) { + return rc; + } + + if (conf_prefix) { + len = cycle->conf_prefix.len; + prefix = cycle->conf_prefix.data; + + } else { + len = cycle->prefix.len; + prefix = cycle->prefix.data; } #if (NGX_WIN32) - if (name->len > 2 - && name->data[1] == ':' - && ((name->data[0] >= 'a' && name->data[0] <= 'z') - || (name->data[0] >= 'A' && name->data[0] <= 'Z'))) - { - return NGX_OK; + if (rc == 2) { + len = rc; } #endif - old = *name; + n = ngx_pnalloc(cycle->pool, len + name->len + 1); + if (n == NULL) { + return NGX_ERROR; + } + + p = ngx_cpymem(n, prefix, len); + ngx_cpystrn(p, name->data, name->len + 1); + + name->len += len; + name->data = n; + + return NGX_OK; +} + - name->len = cycle->root.len + old.len; +static ngx_int_t +ngx_conf_test_full_name(ngx_str_t *name) +{ +#if (NGX_WIN32) + u_char c0, c1; - name->data = ngx_palloc(cycle->pool, name->len + 1); - if (name->data == NULL) { - return NGX_ERROR; + c0 = name->data[0]; + + if (name->len < 2) { + if (c0 == '/') { + return 2; + } + + return NGX_DECLINED; } - p = ngx_cpymem(name->data, cycle->root.data, cycle->root.len), - ngx_cpystrn(p, old.data, old.len + 1); + c1 = name->data[1]; - return NGX_OK; + if (c1 == ':') { + c0 |= 0x20; + + if ((c0 >= 'a' && c0 <= 'z')) { + return NGX_OK; + } + + return NGX_DECLINED; + } + + if (c1 == '/') { + return NGX_OK; + } + + if (c0 == '/') { + return 2; + } + + return NGX_DECLINED; + +#else + + if (name->data[0] == '/') { + return NGX_OK; + } + + return NGX_DECLINED; + +#endif } @@ -727,14 +904,13 @@ ngx_open_file_t *file; #if (NGX_SUPPRESS_WARN) - full.len = 0; - full.data = NULL; + ngx_str_null(&full); #endif - if (name) { + if (name->len) { full = *name; - if (ngx_conf_full_name(cycle, &full) == NGX_ERROR) { + if (ngx_conf_full_name(cycle, &full, 0) != NGX_OK) { return NULL; } @@ -767,14 +943,13 @@ return NULL; } - if (name) { + if (name->len) { file->fd = NGX_INVALID_FILE; file->name = full; } else { - file->fd = ngx_stderr_fileno; - file->name.len = 0; - file->name.data = NULL; + file->fd = ngx_stderr; + file->name = *name; } file->buffer = NULL; @@ -786,6 +961,7 @@ static void ngx_conf_flush_files(ngx_cycle_t *cycle) { + ssize_t n, len; ngx_uint_t i; ngx_list_part_t *part; ngx_open_file_t *file; @@ -806,44 +982,59 @@ i = 0; } - if (file[i].buffer == NULL || file[i].pos - file[i].buffer == 0) { + len = file[i].pos - file[i].buffer; + + if (file[i].buffer == NULL || len == 0) { continue; } - ngx_write_fd(file[i].fd, file[i].buffer, file[i].pos - file[i].buffer); + n = ngx_write_fd(file[i].fd, file[i].buffer, len); + + if (n == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + ngx_write_fd_n " to \"%s\" failed", + file[i].name.data); + + } else if (n != len) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, + ngx_write_fd_n " to \"%s\" was incomplete: %z of %uz", + file[i].name.data, n, len); + } } } void ngx_cdecl ngx_conf_log_error(ngx_uint_t level, ngx_conf_t *cf, ngx_err_t err, - char *fmt, ...) + const char *fmt, ...) { - u_char errstr[NGX_MAX_CONF_ERRSTR], *buf, *last; + u_char errstr[NGX_MAX_CONF_ERRSTR], *p, *last; va_list args; last = errstr + NGX_MAX_CONF_ERRSTR; va_start(args, fmt); - buf = ngx_vsnprintf(errstr, last - errstr, fmt, args); + p = ngx_vslprintf(errstr, last, fmt, args); va_end(args); - *buf = '\0'; - if (err) { - buf = ngx_snprintf(buf, last - buf - 1, " (%d: ", err); - buf = ngx_strerror_r(err, buf, last - buf - 1); - *buf++ = ')'; - *buf = '\0'; + p = ngx_log_errno(p, last, err); } if (cf->conf_file == NULL) { - ngx_log_error(level, cf->log, 0, "%s", errstr); + ngx_log_error(level, cf->log, 0, "%*s", p - errstr, errstr); return; } - ngx_log_error(level, cf->log, 0, "%s in %s:%ui", - errstr, cf->conf_file->file.name.data, cf->conf_file->line); + if (cf->conf_file->file.fd == NGX_INVALID_FILE) { + ngx_log_error(level, cf->log, 0, "%*s in command line", + p - errstr, errstr); + return; + } + + ngx_log_error(level, cf->log, 0, "%*s in %s:%ui", + p - errstr, errstr, + cf->conf_file->file.name.data, cf->conf_file->line); } @@ -925,7 +1116,7 @@ a = (ngx_array_t **) (p + cmd->offset); - if (*a == NULL) { + if (*a == NGX_CONF_UNSET_PTR) { *a = ngx_array_create(cf->pool, 4, sizeof(ngx_str_t)); if (*a == NULL) { return NGX_CONF_ERROR; diff -Nru nginx-0.5.33/src/core/ngx_conf_file.h nginx-0.8.53/src/core/ngx_conf_file.h --- nginx-0.5.33/src/core/ngx_conf_file.h 2007-09-23 19:15:42.000000000 +0000 +++ nginx-0.8.53/src/core/ngx_conf_file.h 2010-02-12 09:45:05.000000000 +0000 @@ -71,7 +71,7 @@ #define NGX_CONF_MODULE 0x464E4F43 /* "CONF" */ -#define NGX_MAX_CONF_ERRSTR 256 +#define NGX_MAX_CONF_ERRSTR 1024 struct ngx_command_s { @@ -314,16 +314,15 @@ } -#define addressof(addr) ((int) &addr) - - +char *ngx_conf_param(ngx_conf_t *cf); char *ngx_conf_parse(ngx_conf_t *cf, ngx_str_t *filename); -ngx_int_t ngx_conf_full_name(ngx_cycle_t *cycle, ngx_str_t *name); +ngx_int_t ngx_conf_full_name(ngx_cycle_t *cycle, ngx_str_t *name, + ngx_uint_t conf_prefix); ngx_open_file_t *ngx_conf_open_file(ngx_cycle_t *cycle, ngx_str_t *name); void ngx_cdecl ngx_conf_log_error(ngx_uint_t level, ngx_conf_t *cf, - ngx_err_t err, char *fmt, ...); + ngx_err_t err, const char *fmt, ...); char *ngx_conf_set_flag_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); diff -Nru nginx-0.5.33/src/core/ngx_config.h nginx-0.8.53/src/core/ngx_config.h --- nginx-0.5.33/src/core/ngx_config.h 2007-11-07 13:54:40.000000000 +0000 +++ nginx-0.8.53/src/core/ngx_config.h 2008-09-19 12:47:13.000000000 +0000 @@ -29,6 +29,10 @@ #include +#elif (NGX_DARWIN) +#include + + #elif (NGX_WIN32) #include @@ -106,21 +110,16 @@ #define ngx_inline inline #endif -#define NGX_ACCEPT_THRESHOLD 100 - #ifndef INADDR_NONE /* Solaris */ #define INADDR_NONE ((unsigned int) -1) #endif -#ifndef INET_ADDRSTRLEN /* Win32 */ -#define INET_ADDRSTRLEN 16 +#ifdef MAXHOSTNAMELEN +#define NGX_MAXHOSTNAMELEN MAXHOSTNAMELEN +#else +#define NGX_MAXHOSTNAMELEN 256 #endif -#define NGX_MAXHOSTNAMELEN 64 -/* -#define NGX_MAXHOSTNAMELEN MAXHOSTNAMELEN -*/ - #if ((__GNU__ == 2) && (__GNUC_MINOR__ < 8)) #define NGX_MAX_UINT32_VALUE (uint32_t) 0xffffffffLL diff -Nru nginx-0.5.33/src/core/ngx_connection.c nginx-0.8.53/src/core/ngx_connection.c --- nginx-0.5.33/src/core/ngx_connection.c 2007-09-23 18:59:07.000000000 +0000 +++ nginx-0.8.53/src/core/ngx_connection.c 2010-07-05 13:49:16.000000000 +0000 @@ -13,11 +13,12 @@ ngx_listening_t * -ngx_listening_inet_stream_socket(ngx_conf_t *cf, in_addr_t addr, in_port_t port) +ngx_create_listening(ngx_conf_t *cf, void *sockaddr, socklen_t socklen) { - size_t len; - ngx_listening_t *ls; - struct sockaddr_in *sin; + size_t len; + ngx_listening_t *ls; + struct sockaddr *sa; + u_char text[NGX_SOCKADDR_STRLEN]; ls = ngx_array_push(&cf->cycle->listening); if (ls == NULL) { @@ -26,34 +27,56 @@ ngx_memzero(ls, sizeof(ngx_listening_t)); - sin = ngx_pcalloc(cf->pool, sizeof(struct sockaddr_in)); - if (sin == NULL) { + sa = ngx_palloc(cf->pool, socklen); + if (sa == NULL) { return NULL; } - sin->sin_family = AF_INET; - sin->sin_addr.s_addr = addr; - sin->sin_port = htons(port); + ngx_memcpy(sa, sockaddr, socklen); + + ls->sockaddr = sa; + ls->socklen = socklen; + len = ngx_sock_ntop(sa, text, NGX_SOCKADDR_STRLEN, 1); + ls->addr_text.len = len; + + switch (ls->sockaddr->sa_family) { +#if (NGX_HAVE_INET6) + case AF_INET6: + ls->addr_text_max_len = NGX_INET6_ADDRSTRLEN; + break; +#endif +#if (NGX_HAVE_UNIX_DOMAIN) + case AF_UNIX: + ls->addr_text_max_len = NGX_UNIX_ADDRSTRLEN; + len++; + break; +#endif + case AF_INET: + ls->addr_text_max_len = NGX_INET_ADDRSTRLEN; + break; + default: + ls->addr_text_max_len = NGX_SOCKADDR_STRLEN; + break; + } - ls->addr_text.data = ngx_palloc(cf->pool, - INET_ADDRSTRLEN - 1 + sizeof(":65535") - 1); + ls->addr_text.data = ngx_pnalloc(cf->pool, len); if (ls->addr_text.data == NULL) { return NULL; } - len = ngx_inet_ntop(AF_INET, &addr, ls->addr_text.data, INET_ADDRSTRLEN); - - ls->addr_text.len = ngx_sprintf(ls->addr_text.data + len, ":%d", port) - - ls->addr_text.data; + ngx_memcpy(ls->addr_text.data, text, len); ls->fd = (ngx_socket_t) -1; - ls->family = AF_INET; ls->type = SOCK_STREAM; - ls->sockaddr = (struct sockaddr *) sin; - ls->socklen = sizeof(struct sockaddr_in); - ls->addr = offsetof(struct sockaddr_in, sin_addr); - ls->addr_text_max_len = INET_ADDRSTRLEN; + + ls->backlog = NGX_LISTEN_BACKLOG; + ls->rcvbuf = -1; + ls->sndbuf = -1; + +#if (NGX_HAVE_SETFIB) + ls->setfib = -1; +#endif return ls; } @@ -65,7 +88,6 @@ size_t len; ngx_uint_t i; ngx_listening_t *ls; - struct sockaddr_in *sin; socklen_t olen; #if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER) ngx_err_t err; @@ -78,14 +100,12 @@ ls = cycle->listening.elts; for (i = 0; i < cycle->listening.nelts; i++) { - /* AF_INET only */ - - ls[i].sockaddr = ngx_palloc(cycle->pool, sizeof(struct sockaddr_in)); + ls[i].sockaddr = ngx_palloc(cycle->pool, NGX_SOCKADDRLEN); if (ls[i].sockaddr == NULL) { return NGX_ERROR; } - ls[i].socklen = sizeof(struct sockaddr_in); + ls[i].socklen = NGX_SOCKADDRLEN; if (getsockname(ls[i].fd, ls[i].sockaddr, &ls[i].socklen) == -1) { ngx_log_error(NGX_LOG_CRIT, cycle->log, ngx_socket_errno, "getsockname() of the inherited " @@ -94,34 +114,46 @@ continue; } - sin = (struct sockaddr_in *) ls[i].sockaddr; + switch (ls[i].sockaddr->sa_family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + ls[i].addr_text_max_len = NGX_INET6_ADDRSTRLEN; + len = NGX_INET6_ADDRSTRLEN + sizeof(":65535") - 1; + break; +#endif + +#if (NGX_HAVE_UNIX_DOMAIN) + case AF_UNIX: + ls[i].addr_text_max_len = NGX_UNIX_ADDRSTRLEN; + len = NGX_UNIX_ADDRSTRLEN; + break; +#endif + + case AF_INET: + ls[i].addr_text_max_len = NGX_INET_ADDRSTRLEN; + len = NGX_INET_ADDRSTRLEN + sizeof(":65535") - 1; + break; - if (sin->sin_family != AF_INET) { + default: ngx_log_error(NGX_LOG_CRIT, cycle->log, ngx_socket_errno, "the inherited socket #%d has " - "unsupported family", ls[i].fd); + "an unsupported protocol family", ls[i].fd); ls[i].ignore = 1; continue; } - ls[i].addr_text_max_len = INET_ADDRSTRLEN; - - ls[i].addr_text.data = ngx_palloc(cycle->pool, INET_ADDRSTRLEN - 1 - + sizeof(":65535") - 1); + ls[i].addr_text.data = ngx_pnalloc(cycle->pool, len); if (ls[i].addr_text.data == NULL) { return NGX_ERROR; } - ls[i].family = sin->sin_family; - len = ngx_sock_ntop(ls[i].family, ls[i].sockaddr, - ls[i].addr_text.data, INET_ADDRSTRLEN); + len = ngx_sock_ntop(ls[i].sockaddr, ls[i].addr_text.data, len, 1); if (len == 0) { return NGX_ERROR; } - ls[i].addr_text.len = ngx_sprintf(ls[i].addr_text.data + len, ":%d", - ntohs(sin->sin_port)) - - ls[i].addr_text.data; + ls[i].addr_text.len = len; ls[i].backlog = NGX_LISTEN_BACKLOG; @@ -151,6 +183,25 @@ ls[i].sndbuf = -1; } +#if 0 + /* SO_SETFIB is currently a set only option */ + +#if (NGX_HAVE_SETFIB) + + if (getsockopt(ls[i].setfib, SOL_SOCKET, SO_SETFIB, + (void *) &ls[i].setfib, &olen) + == -1) + { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno, + "getsockopt(SO_SETFIB) %V failed, ignored", + &ls[i].addr_text); + + ls[i].setfib = -1; + } + +#endif +#endif + #if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER) ngx_memzero(&af, sizeof(struct accept_filter_arg)); @@ -229,7 +280,7 @@ /* TODO: configurable try number */ - for (tries = 5 ; tries; tries--) { + for (tries = 5; tries; tries--) { failed = 0; /* for each listening socket */ @@ -254,7 +305,7 @@ continue; } - s = ngx_socket(ls[i].family, ls[i].type, 0); + s = ngx_socket(ls[i].sockaddr->sa_family, ls[i].type, 0); if (s == -1) { ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno, @@ -279,6 +330,23 @@ return NGX_ERROR; } +#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY) + + if (ls[i].sockaddr->sa_family == AF_INET6 && ls[i].ipv6only) { + int ipv6only; + + ipv6only = (ls[i].ipv6only == 1); + + if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, + (const void *) &ipv6only, sizeof(int)) + == -1) + { + ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno, + "setsockopt(IPV6_V6ONLY) %V failed, ignored", + &ls[i].addr_text); + } + } +#endif /* TODO: close on exit */ if (!(ngx_event_flags & NGX_USE_AIO_EVENT)) { @@ -297,7 +365,7 @@ } } - ngx_log_debug2(NGX_LOG_DEBUG_CORE, cycle->log, 0, + ngx_log_debug2(NGX_LOG_DEBUG_CORE, log, 0, "bind() %V #%d ", &ls[i].addr_text, s); if (bind(s, ls[i].sockaddr, ls[i].socklen) == -1) { @@ -325,6 +393,29 @@ continue; } +#if (NGX_HAVE_UNIX_DOMAIN) + + if (ls[i].sockaddr->sa_family == AF_UNIX) { + mode_t mode; + u_char *name; + + name = ls[i].addr_text.data + sizeof("unix:") - 1; + mode = (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH); + + if (chmod((char *) name, mode) == -1) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, + "chmod() \"%s\" failed", name); + } + + if (ngx_test_config) { + if (ngx_delete_file(name) == -1) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, + ngx_delete_file_n " %s failed", name); + } + } + } +#endif + if (listen(s, ls[i].backlog) == -1) { ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno, "listen() to %V, backlog %d failed", @@ -366,7 +457,7 @@ void -ngx_configure_listening_socket(ngx_cycle_t *cycle) +ngx_configure_listening_sockets(ngx_cycle_t *cycle) { ngx_uint_t i; ngx_listening_t *ls; @@ -381,6 +472,8 @@ ls = cycle->listening.elts; for (i = 0; i < cycle->listening.nelts; i++) { + ls[i].log = *ls[i].logp; + if (ls[i].rcvbuf != -1) { if (setsockopt(ls[i].fd, SOL_SOCKET, SO_RCVBUF, (const void *) &ls[i].rcvbuf, sizeof(int)) @@ -403,6 +496,19 @@ } } +#if (NGX_HAVE_SETFIB) + if (ls[i].setfib != -1) { + if (setsockopt(ls[i].fd, SOL_SOCKET, SO_SETFIB, + (const void *) &ls[i].setfib, sizeof(int)) + == -1) + { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno, + "setsockopt(SO_SETFIB, %d) %V failed, ignored", + ls[i].setfib, &ls[i].addr_text); + } + } +#endif + #if 0 if (1) { int tcp_nodelay = 1; @@ -537,20 +643,30 @@ c = ls[i].connection; - if (ngx_event_flags & NGX_USE_RTSIG_EVENT) { + if (c) { if (c->read->active) { - ngx_del_conn(c, NGX_CLOSE_EVENT); - } + if (ngx_event_flags & NGX_USE_RTSIG_EVENT) { + ngx_del_conn(c, NGX_CLOSE_EVENT); - } else { - if (c->read->active) { - ngx_del_event(c->read, NGX_READ_EVENT, NGX_CLOSE_EVENT); + } else if (ngx_event_flags & NGX_USE_EPOLL_EVENT) { + + /* + * it seems that Linux-2.6.x OpenVZ sends events + * for closed shared listening sockets unless + * the events was explicity deleted + */ + + ngx_del_event(c->read, NGX_READ_EVENT, 0); + + } else { + ngx_del_event(c->read, NGX_READ_EVENT, NGX_CLOSE_EVENT); + } } - } - ngx_free_connection(c); + ngx_free_connection(c); - c->fd = (ngx_socket_t) -1; + c->fd = (ngx_socket_t) -1; + } ngx_log_debug2(NGX_LOG_DEBUG_CORE, cycle->log, 0, "close listening %V #%d ", &ls[i].addr_text, ls[i].fd); @@ -559,6 +675,24 @@ ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_socket_errno, ngx_close_socket_n " %V failed", &ls[i].addr_text); } + +#if (NGX_HAVE_UNIX_DOMAIN) + + if (ls[i].sockaddr->sa_family == AF_UNIX + && ngx_process <= NGX_PROCESS_MASTER + && ngx_new_binary == 0) + { + u_char *name = ls[i].addr_text.data + sizeof("unix:") - 1; + + if (ngx_delete_file(name) == -1) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_socket_errno, + ngx_delete_file_n " %s failed", name); + } + } + +#endif + + ls[i].fd = (ngx_socket_t) -1; } } @@ -586,7 +720,7 @@ if (c == NULL) { ngx_log_error(NGX_LOG_ALERT, log, 0, - "%ui worker_connections is not enough", + "%ui worker_connections are not enough", ngx_cycle->connection_n); /* ngx_mutex_unlock */ @@ -653,6 +787,8 @@ void ngx_close_connection(ngx_connection_t *c) { + ngx_err_t err; + ngx_uint_t log_error, level; ngx_socket_t fd; if (c->fd == -1) { @@ -725,6 +861,8 @@ #endif + log_error = c->log_error; + ngx_free_connection(c); fd = c->fd; @@ -732,52 +870,146 @@ if (ngx_close_socket(fd) == -1) { + err = ngx_socket_errno; + + if (err == NGX_ECONNRESET || err == NGX_ENOTCONN) { + + switch (log_error) { + + case NGX_ERROR_INFO: + level = NGX_LOG_INFO; + break; + + case NGX_ERROR_ERR: + level = NGX_LOG_ERR; + break; + + default: + level = NGX_LOG_CRIT; + } + + } else { + level = NGX_LOG_CRIT; + } + /* we use ngx_cycle->log because c->log was in c->pool */ - ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_socket_errno, + ngx_log_error(level, ngx_cycle->log, err, ngx_close_socket_n " %d failed", fd); } } ngx_int_t +ngx_connection_local_sockaddr(ngx_connection_t *c, ngx_str_t *s, + ngx_uint_t port) +{ + socklen_t len; + ngx_uint_t addr; + u_char sa[NGX_SOCKADDRLEN]; + struct sockaddr_in *sin; +#if (NGX_HAVE_INET6) + ngx_uint_t i; + struct sockaddr_in6 *sin6; +#endif + + switch (c->local_sockaddr->sa_family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + sin6 = (struct sockaddr_in6 *) c->local_sockaddr; + + for (addr = 0, i = 0; addr == 0 && i < 16; i++) { + addr |= sin6->sin6_addr.s6_addr[i]; + } + + break; +#endif + + default: /* AF_INET */ + sin = (struct sockaddr_in *) c->local_sockaddr; + addr = sin->sin_addr.s_addr; + break; + } + + if (addr == 0) { + + len = NGX_SOCKADDRLEN; + + if (getsockname(c->fd, (struct sockaddr *) &sa, &len) == -1) { + ngx_connection_error(c, ngx_socket_errno, "getsockname() failed"); + return NGX_ERROR; + } + + c->local_sockaddr = ngx_palloc(c->pool, len); + if (c->local_sockaddr == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(c->local_sockaddr, &sa, len); + } + + if (s == NULL) { + return NGX_OK; + } + + s->len = ngx_sock_ntop(c->local_sockaddr, s->data, s->len, port); + + return NGX_OK; +} + + +ngx_int_t ngx_connection_error(ngx_connection_t *c, ngx_err_t err, char *text) { ngx_uint_t level; - if (err == NGX_ECONNRESET - && c->log_error == NGX_ERROR_IGNORE_ECONNRESET) + /* Winsock may return NGX_ECONNABORTED instead of NGX_ECONNRESET */ + + if ((err == NGX_ECONNRESET +#if (NGX_WIN32) + || err == NGX_ECONNABORTED +#endif + ) && c->log_error == NGX_ERROR_IGNORE_ECONNRESET) { return 0; } +#if (NGX_SOLARIS) + if (err == NGX_EINVAL && c->log_error == NGX_ERROR_IGNORE_EINVAL) { + return 0; + } +#endif + if (err == 0 || err == NGX_ECONNRESET -#if !(NGX_WIN32) +#if (NGX_WIN32) + || err == NGX_ECONNABORTED +#else || err == NGX_EPIPE #endif || err == NGX_ENOTCONN || err == NGX_ETIMEDOUT || err == NGX_ECONNREFUSED + || err == NGX_ENETDOWN + || err == NGX_ENETUNREACH + || err == NGX_EHOSTDOWN || err == NGX_EHOSTUNREACH) { switch (c->log_error) { + case NGX_ERROR_IGNORE_EINVAL: case NGX_ERROR_IGNORE_ECONNRESET: case NGX_ERROR_INFO: level = NGX_LOG_INFO; break; - case NGX_ERROR_ERR: - level = NGX_LOG_ERR; - break; - default: - level = NGX_LOG_CRIT; + level = NGX_LOG_ERR; } } else { - level = NGX_LOG_CRIT; + level = NGX_LOG_ALERT; } ngx_log_error(level, c->log, err, text); diff -Nru nginx-0.5.33/src/core/ngx_connection.h nginx-0.8.53/src/core/ngx_connection.h --- nginx-0.5.33/src/core/ngx_connection.h 2007-03-19 13:20:15.000000000 +0000 +++ nginx-0.8.53/src/core/ngx_connection.h 2010-07-05 13:49:16.000000000 +0000 @@ -19,11 +19,9 @@ struct sockaddr *sockaddr; socklen_t socklen; /* size of sockaddr */ - size_t addr; /* offset to address in sockaddr */ size_t addr_text_max_len; ngx_str_t addr_text; - int family; int type; int backlog; @@ -36,6 +34,7 @@ void *servers; /* array of ngx_http_in_addr_t, for example */ ngx_log_t log; + ngx_log_t *logp; size_t pool_size; /* should be here because of the AcceptEx() preread */ @@ -58,6 +57,10 @@ unsigned shared:1; /* shared between threads or processes */ unsigned addr_ntop:1; +#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY) + unsigned ipv6only:2; +#endif + #if (NGX_HAVE_DEFERRED_ACCEPT) unsigned deferred_accept:1; unsigned delete_deferred:1; @@ -66,15 +69,19 @@ char *accept_filter; #endif #endif +#if (NGX_HAVE_SETFIB) + int setfib; +#endif }; typedef enum { - NGX_ERROR_CRIT = 0, + NGX_ERROR_ALERT = 0, NGX_ERROR_ERR, NGX_ERROR_INFO, - NGX_ERROR_IGNORE_ECONNRESET + NGX_ERROR_IGNORE_ECONNRESET, + NGX_ERROR_IGNORE_EINVAL } ngx_connection_log_error_e; @@ -124,18 +131,17 @@ ngx_ssl_connection_t *ssl; #endif -#if (NGX_HAVE_IOCP) struct sockaddr *local_sockaddr; - socklen_t local_socklen; -#endif ngx_buf_t *buffer; ngx_atomic_uint_t number; + ngx_uint_t requests; + unsigned buffered:8; - unsigned log_error:2; /* ngx_connection_log_error_e */ + unsigned log_error:3; /* ngx_connection_log_error_e */ unsigned single_connection:1; unsigned unexpected_eof:1; @@ -155,24 +161,26 @@ unsigned accept_context_updated:1; #endif +#if (NGX_HAVE_AIO_SENDFILE) + unsigned aio_sendfile:1; + ngx_buf_t *busy_sendfile; +#endif + #if (NGX_THREADS) ngx_atomic_t lock; #endif }; -#ifndef ngx_ssl_set_nosendshut -#define ngx_ssl_set_nosendshut(ssl) -#endif - - -ngx_listening_t *ngx_listening_inet_stream_socket(ngx_conf_t *cf, - in_addr_t addr, in_port_t port); +ngx_listening_t *ngx_create_listening(ngx_conf_t *cf, void *sockaddr, + socklen_t socklen); ngx_int_t ngx_set_inherited_sockets(ngx_cycle_t *cycle); ngx_int_t ngx_open_listening_sockets(ngx_cycle_t *cycle); -void ngx_configure_listening_socket(ngx_cycle_t *cycle); +void ngx_configure_listening_sockets(ngx_cycle_t *cycle); void ngx_close_listening_sockets(ngx_cycle_t *cycle); void ngx_close_connection(ngx_connection_t *c); +ngx_int_t ngx_connection_local_sockaddr(ngx_connection_t *c, ngx_str_t *s, + ngx_uint_t port); ngx_int_t ngx_connection_error(ngx_connection_t *c, ngx_err_t err, char *text); ngx_connection_t *ngx_get_connection(ngx_socket_t s, ngx_log_t *log); diff -Nru nginx-0.5.33/src/core/ngx_core.h nginx-0.8.53/src/core/ngx_core.h --- nginx-0.5.33/src/core/ngx_core.h 2007-01-03 15:25:40.000000000 +0000 +++ nginx-0.8.53/src/core/ngx_core.h 2009-11-16 12:48:41.000000000 +0000 @@ -19,6 +19,7 @@ typedef struct ngx_command_s ngx_command_t; typedef struct ngx_file_s ngx_file_t; typedef struct ngx_event_s ngx_event_t; +typedef struct ngx_event_aio_s ngx_event_aio_t; typedef struct ngx_connection_s ngx_connection_t; typedef void (*ngx_event_handler_pt)(ngx_event_t *ev); @@ -40,21 +41,21 @@ #include #include #include -#include +#include +#include #include #include #include -#include #include #include #include #include #include +#include #include #include #include #include -#include #include #include #if (NGX_PCRE) @@ -71,6 +72,8 @@ #endif #include #include +#include +#include #include #include @@ -80,7 +83,9 @@ #define CRLF "\x0d\x0a" -#define ngx_abs(value) (((value) >= 0) ? (value) : - (value)) +#define ngx_abs(value) (((value) >= 0) ? (value) : - (value)) +#define ngx_max(val1, val2) ((val1 < val2) ? (val2) : (val1)) +#define ngx_min(val1, val2) ((val1 > val2) ? (val2) : (val1)) void ngx_cpuinfo(void); diff -Nru nginx-0.5.33/src/core/ngx_cpuinfo.c nginx-0.8.53/src/core/ngx_cpuinfo.c --- nginx-0.5.33/src/core/ngx_cpuinfo.c 2006-08-30 10:39:17.000000000 +0000 +++ nginx-0.8.53/src/core/ngx_cpuinfo.c 2009-03-28 12:43:41.000000000 +0000 @@ -72,7 +72,7 @@ ngx_cpuinfo(void) { u_char *vendor; - uint32_t vbuf[5], cpu[4]; + uint32_t vbuf[5], cpu[4], model; vbuf[0] = 0; vbuf[1] = 0; @@ -92,13 +92,24 @@ if (ngx_strcmp(vendor, "GenuineIntel") == 0) { - switch (cpu[0] & 0xf00) { + switch ((cpu[0] & 0xf00) >> 8) { /* Pentium */ case 5: + ngx_cacheline_size = 32; + break; + /* Pentium Pro, II, III */ case 6: ngx_cacheline_size = 32; + + model = ((cpu[0] & 0xf0000) >> 8) | (cpu[0] & 0xf0); + + if (model >= 0xd0) { + /* Intel Core, Core 2, Atom */ + ngx_cacheline_size = 64; + } + break; /* diff -Nru nginx-0.5.33/src/core/ngx_crc32.c nginx-0.8.53/src/core/ngx_crc32.c --- nginx-0.5.33/src/core/ngx_crc32.c 2007-01-12 18:05:41.000000000 +0000 +++ nginx-0.8.53/src/core/ngx_crc32.c 2007-12-07 20:19:41.000000000 +0000 @@ -102,7 +102,7 @@ ngx_int_t -ngx_crc32_init(void) +ngx_crc32_table_init(void) { void *p; diff -Nru nginx-0.5.33/src/core/ngx_crc32.h nginx-0.8.53/src/core/ngx_crc32.h --- nginx-0.5.33/src/core/ngx_crc32.h 2006-12-15 21:31:03.000000000 +0000 +++ nginx-0.8.53/src/core/ngx_crc32.h 2007-12-08 22:12:37.000000000 +0000 @@ -49,7 +49,30 @@ } -ngx_int_t ngx_crc32_init(void); +#define ngx_crc32_init(crc) \ + crc = 0xffffffff + + +static ngx_inline void +ngx_crc32_update(uint32_t *crc, u_char *p, size_t len) +{ + uint32_t c; + + c = *crc; + + while (len--) { + c = ngx_crc32_table256[(c ^ *p++) & 0xff] ^ (c >> 8); + } + + *crc = c; +} + + +#define ngx_crc32_final(crc) \ + crc ^= 0xffffffff + + +ngx_int_t ngx_crc32_table_init(void); #endif /* _NGX_CRC32_H_INCLUDED_ */ diff -Nru nginx-0.5.33/src/core/ngx_cycle.c nginx-0.8.53/src/core/ngx_cycle.c --- nginx-0.5.33/src/core/ngx_cycle.c 2007-04-18 11:28:11.000000000 +0000 +++ nginx-0.8.53/src/core/ngx_cycle.c 2010-09-02 13:43:02.000000000 +0000 @@ -9,9 +9,11 @@ #include -static ngx_int_t ngx_test_lockfile(u_char *file, ngx_log_t *log); static void ngx_destroy_cycle_pools(ngx_conf_t *conf); static ngx_int_t ngx_cmp_sockaddr(struct sockaddr *sa1, struct sockaddr *sa2); +static ngx_int_t ngx_init_zone_pool(ngx_cycle_t *cycle, + ngx_shm_zone_t *shm_zone); +static ngx_int_t ngx_test_lockfile(u_char *file, ngx_log_t *log); static void ngx_clean_old_cycles(ngx_event_t *ev); @@ -22,6 +24,7 @@ static ngx_event_t ngx_cleaner_event; ngx_uint_t ngx_test_config; +ngx_uint_t ngx_quiet_mode; #if (NGX_THREADS) ngx_tls_key_t ngx_core_tls_key; @@ -32,30 +35,37 @@ static ngx_connection_t dumb; /* STUB */ -#ifdef NGX_ERROR_LOG_PATH static ngx_str_t error_log = ngx_string(NGX_ERROR_LOG_PATH); -#else -static ngx_str_t error_log = ngx_null_string; -#endif ngx_cycle_t * ngx_init_cycle(ngx_cycle_t *old_cycle) { - void *rv; - u_char *lock_file; - ngx_uint_t i, n; - ngx_log_t *log; - ngx_conf_t conf; - ngx_pool_t *pool; - ngx_cycle_t *cycle, **old; - ngx_shm_zone_t *shm_zone, *oshm_zone; - ngx_slab_pool_t *shpool; - ngx_list_part_t *part, *opart; - ngx_open_file_t *file; - ngx_listening_t *ls, *nls; - ngx_core_conf_t *ccf, *old_ccf; - ngx_core_module_t *module; + void *rv; + char **senv, **env; + ngx_uint_t i, n; + ngx_log_t *log; + ngx_time_t *tp; + ngx_conf_t conf; + ngx_pool_t *pool; + ngx_cycle_t *cycle, **old; + ngx_shm_zone_t *shm_zone, *oshm_zone; + ngx_list_part_t *part, *opart; + ngx_open_file_t *file; + ngx_listening_t *ls, *nls; + ngx_core_conf_t *ccf, *old_ccf; + ngx_core_module_t *module; + char hostname[NGX_MAXHOSTNAMELEN]; + + ngx_timezone_update(); + + /* force localtime update with a new timezone */ + + tp = ngx_timeofday(); + tp->sec = 0; + + ngx_time_update(); + log = old_cycle->log; @@ -73,13 +83,25 @@ cycle->pool = pool; cycle->log = log; + cycle->new_log.log_level = NGX_LOG_ERR; cycle->old_cycle = old_cycle; - cycle->root.len = sizeof(NGX_PREFIX) - 1; - cycle->root.data = (u_char *) NGX_PREFIX; + cycle->conf_prefix.len = old_cycle->conf_prefix.len; + cycle->conf_prefix.data = ngx_pstrdup(pool, &old_cycle->conf_prefix); + if (cycle->conf_prefix.data == NULL) { + ngx_destroy_pool(pool); + return NULL; + } + + cycle->prefix.len = old_cycle->prefix.len; + cycle->prefix.data = ngx_pstrdup(pool, &old_cycle->prefix); + if (cycle->prefix.data == NULL) { + ngx_destroy_pool(pool); + return NULL; + } cycle->conf_file.len = old_cycle->conf_file.len; - cycle->conf_file.data = ngx_palloc(pool, old_cycle->conf_file.len + 1); + cycle->conf_file.data = ngx_pnalloc(pool, old_cycle->conf_file.len + 1); if (cycle->conf_file.data == NULL) { ngx_destroy_pool(pool); return NULL; @@ -87,6 +109,13 @@ ngx_cpystrn(cycle->conf_file.data, old_cycle->conf_file.data, old_cycle->conf_file.len + 1); + cycle->conf_param.len = old_cycle->conf_param.len; + cycle->conf_param.data = ngx_pstrdup(pool, &old_cycle->conf_param); + if (cycle->conf_param.data == NULL) { + ngx_destroy_pool(pool); + return NULL; + } + n = old_cycle->pathes.nelts ? old_cycle->pathes.nelts : 10; @@ -113,7 +142,7 @@ } if (ngx_list_init(&cycle->open_files, pool, n, sizeof(ngx_open_file_t)) - == NGX_ERROR) + != NGX_OK) { ngx_destroy_pool(pool); return NULL; @@ -132,22 +161,12 @@ } if (ngx_list_init(&cycle->shared_memory, pool, n, sizeof(ngx_shm_zone_t)) - == NGX_ERROR) + != NGX_OK) { ngx_destroy_pool(pool); return NULL; } - - cycle->new_log = ngx_log_create_errlog(cycle, NULL); - if (cycle->new_log == NULL) { - ngx_destroy_pool(pool); - return NULL; - } - - cycle->new_log->file->name = error_log; - - n = old_cycle->listening.nelts ? old_cycle->listening.nelts : 10; cycle->listening.elts = ngx_pcalloc(pool, n * sizeof(ngx_listening_t)); @@ -169,6 +188,26 @@ } + if (gethostname(hostname, NGX_MAXHOSTNAMELEN) == -1) { + ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "gethostname() failed"); + ngx_destroy_pool(pool); + return NULL; + } + + /* on Linux gethostname() silently truncates name that does not fit */ + + hostname[NGX_MAXHOSTNAMELEN - 1] = '\0'; + cycle->hostname.len = ngx_strlen(hostname); + + cycle->hostname.data = ngx_pnalloc(pool, cycle->hostname.len); + if (cycle->hostname.data == NULL) { + ngx_destroy_pool(pool); + return NULL; + } + + ngx_strlow(cycle->hostname.data, (u_char *) hostname, cycle->hostname.len); + + for (i = 0; ngx_modules[i]; i++) { if (ngx_modules[i]->type != NGX_CORE_MODULE) { continue; @@ -178,7 +217,7 @@ if (module->create_conf) { rv = module->create_conf(cycle); - if (rv == NGX_CONF_ERROR) { + if (rv == NULL) { ngx_destroy_pool(pool); return NULL; } @@ -187,6 +226,9 @@ } + senv = environ; + + ngx_memzero(&conf, sizeof(ngx_conf_t)); /* STUB: init array ? */ conf.args = ngx_array_create(pool, 10, sizeof(ngx_str_t)); @@ -213,17 +255,22 @@ log->log_level = NGX_LOG_DEBUG_ALL; #endif - if (ngx_conf_parse(&conf, &cycle->conf_file) != NGX_CONF_OK) { + if (ngx_conf_param(&conf) != NGX_CONF_OK) { + environ = senv; ngx_destroy_cycle_pools(&conf); return NULL; } - if (ngx_test_config) { - ngx_log_error(NGX_LOG_INFO, log, 0, - "the configuration file %s syntax is ok", - cycle->conf_file.data); + if (ngx_conf_parse(&conf, &cycle->conf_file) != NGX_CONF_OK) { + environ = senv; + ngx_destroy_cycle_pools(&conf); + return NULL; } + if (ngx_test_config && !ngx_quiet_mode) { + ngx_log_stderr(0, "the configuration file %s syntax is ok", + cycle->conf_file.data); + } for (i = 0; ngx_modules[i]; i++) { if (ngx_modules[i]->type != NGX_CORE_MODULE) { @@ -236,17 +283,19 @@ if (module->init_conf(cycle, cycle->conf_ctx[ngx_modules[i]->index]) == NGX_CONF_ERROR) { + environ = senv; ngx_destroy_cycle_pools(&conf); return NULL; } } } + if (ngx_process == NGX_PROCESS_SIGNALLER) { + return cycle; + } ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module); -#if !(NGX_WIN32) - if (ngx_test_config) { if (ngx_create_pidfile(&ccf->pid, log) != NGX_OK) { @@ -275,8 +324,6 @@ } } -#endif - if (ngx_test_lockfile(cycle->lock_file.data, log) != NGX_OK) { goto failed; @@ -288,6 +335,13 @@ } + if (cycle->new_log.file == NULL) { + cycle->new_log.file = ngx_conf_open_file(cycle, &error_log); + if (cycle->new_log.file == NULL) { + goto failed; + } + } + /* open the new files */ part = &cycle->open_files.part; @@ -304,12 +358,13 @@ i = 0; } - if (file[i].name.data == NULL) { + if (file[i].name.len == 0) { continue; } - file[i].fd = ngx_open_file(file[i].name.data, NGX_FILE_RDWR, - NGX_FILE_CREATE_OR_OPEN|NGX_FILE_APPEND, + file[i].fd = ngx_open_file(file[i].name.data, + NGX_FILE_APPEND, + NGX_FILE_CREATE_OR_OPEN, NGX_FILE_DEFAULT_ACCESS); ngx_log_debug3(NGX_LOG_DEBUG_CORE, log, 0, @@ -323,14 +378,7 @@ goto failed; } -#if (NGX_WIN32) - if (ngx_file_append_mode(file[i].fd) != NGX_OK) { - ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, - ngx_file_append_mode_n " \"%s\" failed", - file[i].name.data); - goto failed; - } -#else +#if !(NGX_WIN32) if (fcntl(file[i].fd, F_SETFD, FD_CLOEXEC) == -1) { ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "fcntl(FD_CLOEXEC) \"%s\" failed", @@ -340,12 +388,8 @@ #endif } - cycle->log = cycle->new_log; - pool->log = cycle->new_log; - - if (cycle->log->log_level == 0) { - cycle->log->log_level = NGX_LOG_ERR; - } + cycle->log = &cycle->new_log; + pool->log = &cycle->new_log; /* create shared memory */ @@ -367,7 +411,7 @@ if (shm_zone[i].shm.size == 0) { ngx_log_error(NGX_LOG_EMERG, log, 0, "zero size shared memory zone \"%V\"", - &shm_zone[i].name); + &shm_zone[i].shm.name); goto failed; } @@ -392,12 +436,13 @@ n = 0; } - if (shm_zone[i].name.len != oshm_zone[n].name.len) { + if (shm_zone[i].shm.name.len != oshm_zone[n].shm.name.len) { continue; } - if (ngx_strncmp(shm_zone[i].name.data, oshm_zone[n].name.data, - shm_zone[i].name.len) + if (ngx_strncmp(shm_zone[i].shm.name.data, + oshm_zone[n].shm.name.data, + shm_zone[i].shm.name.len) != 0) { continue; @@ -424,38 +469,10 @@ goto failed; } - shpool = (ngx_slab_pool_t *) shm_zone[i].shm.addr; - - shpool->end = shm_zone[i].shm.addr + shm_zone[i].shm.size; - shpool->min_shift = 3; - -#if (NGX_HAVE_ATOMIC_OPS) - - lock_file = NULL; - -#else - - lock_file = ngx_palloc(cycle->pool, - cycle->lock_file.len + shm_zone[i].name.len); - - if (lock_file == NULL) { + if (ngx_init_zone_pool(cycle, &shm_zone[i]) != NGX_OK) { goto failed; } - (void) ngx_cpystrn(ngx_cpymem(lock_file, cycle->lock_file.data, - cycle->lock_file.len), - shm_zone[i].name.data, shm_zone[i].name.len + 1); - -#endif - - if (ngx_shmtx_create(&shpool->mutex, (void *) &shpool->lock, lock_file) - != NGX_OK) - { - goto failed; - } - - ngx_slab_init(shpool); - if (shm_zone[i].init(&shm_zone[i], NULL) != NGX_OK) { goto failed; } @@ -558,31 +575,20 @@ } if (!ngx_test_config) { - ngx_configure_listening_socket(cycle); + ngx_configure_listening_sockets(cycle); } /* commit the new cycle configuration */ -#if !(NGX_WIN32) - - if (!ngx_test_config && cycle->log->file->fd != STDERR_FILENO) { - - ngx_log_debug3(NGX_LOG_DEBUG_CORE, log, 0, - "dup2: %p %d \"%s\"", - cycle->log->file, - cycle->log->file->fd, cycle->log->file->name.data); + if (!ngx_use_stderr && cycle->log->file->fd != ngx_stderr) { - if (dup2(cycle->log->file->fd, STDERR_FILENO) == -1) { - ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, - "dup2(STDERR) failed"); - /* fatal */ - exit(1); + if (ngx_set_stderr(cycle->log->file->fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + ngx_set_stderr_n " failed"); } } -#endif - pool->log = cycle->log; for (i = 0; ngx_modules[i]; i++) { @@ -627,10 +633,10 @@ n = 0; } - if (oshm_zone[i].name.len == shm_zone[n].name.len - && ngx_strncmp(oshm_zone[i].name.data, - shm_zone[n].name.data, - oshm_zone[i].name.len) + if (oshm_zone[i].shm.name.len == shm_zone[n].shm.name.len + && ngx_strncmp(oshm_zone[i].shm.name.data, + shm_zone[n].shm.name.data, + oshm_zone[i].shm.name.len) == 0) { goto live_shm_zone; @@ -651,7 +657,8 @@ ls = old_cycle->listening.elts; for (i = 0; i < old_cycle->listening.nelts; i++) { - if (ls[i].remain) { + + if (ls[i].remain || ls[i].fd == -1) { continue; } @@ -660,6 +667,24 @@ ngx_close_socket_n " listening socket on %V failed", &ls[i].addr_text); } + +#if (NGX_HAVE_UNIX_DOMAIN) + + if (ls[i].sockaddr->sa_family == AF_UNIX) { + u_char *name; + + name = ls[i].addr_text.data + sizeof("unix:") - 1; + + ngx_log_error(NGX_LOG_WARN, cycle->log, 0, + "deleting socket %s", name); + + if (ngx_delete_file(name) == -1) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_socket_errno, + ngx_delete_file_n " %s failed", name); + } + } + +#endif } @@ -679,7 +704,7 @@ i = 0; } - if (file[i].fd == NGX_INVALID_FILE || file[i].fd == ngx_stderr_fileno) { + if (file[i].fd == NGX_INVALID_FILE || file[i].fd == ngx_stderr) { continue; } @@ -694,9 +719,20 @@ if (ngx_process == NGX_PROCESS_MASTER || ngx_is_init_cycle(old_cycle)) { + /* + * perl_destruct() frees environ, if it is not the same as it was at + * perl_construct() time, therefore we save the previous cycle + * environment before ngx_conf_parse() where it will be changed. + */ + + env = environ; + environ = senv; + ngx_destroy_pool(old_cycle->pool); cycle->old_cycle = NULL; + environ = env; + return cycle; } @@ -768,7 +804,7 @@ i = 0; } - if (file[i].fd == NGX_INVALID_FILE || file[i].fd == ngx_stderr_fileno) { + if (file[i].fd == NGX_INVALID_FILE || file[i].fd == ngx_stderr) { continue; } @@ -814,49 +850,141 @@ static ngx_int_t ngx_cmp_sockaddr(struct sockaddr *sa1, struct sockaddr *sa2) { - struct sockaddr_in *sin1, *sin2; - - /* AF_INET only */ + struct sockaddr_in *sin1, *sin2; +#if (NGX_HAVE_INET6) + struct sockaddr_in6 *sin61, *sin62; +#endif +#if (NGX_HAVE_UNIX_DOMAIN) + struct sockaddr_un *saun1, *saun2; +#endif - if (sa1->sa_family != AF_INET || sa2->sa_family != AF_INET) { + if (sa1->sa_family != sa2->sa_family) { return NGX_DECLINED; } - sin1 = (struct sockaddr_in *) sa1; - sin2 = (struct sockaddr_in *) sa2; + switch (sa1->sa_family) { - if (sin1->sin_addr.s_addr != sin2->sin_addr.s_addr) { - return NGX_DECLINED; - } +#if (NGX_HAVE_INET6) + case AF_INET6: + sin61 = (struct sockaddr_in6 *) sa1; + sin62 = (struct sockaddr_in6 *) sa2; - if (sin1->sin_port != sin2->sin_port) { - return NGX_DECLINED; + if (sin61->sin6_port != sin62->sin6_port) { + return NGX_DECLINED; + } + + if (ngx_memcmp(&sin61->sin6_addr, &sin62->sin6_addr, 16) != 0) { + return NGX_DECLINED; + } + + break; +#endif + +#if (NGX_HAVE_UNIX_DOMAIN) + case AF_UNIX: + saun1 = (struct sockaddr_un *) sa1; + saun2 = (struct sockaddr_un *) sa2; + + if (ngx_memcmp(&saun1->sun_path, &saun2->sun_path, + sizeof(saun1->sun_path)) + != 0) + { + return NGX_DECLINED; + } + + break; +#endif + + default: /* AF_INET */ + + sin1 = (struct sockaddr_in *) sa1; + sin2 = (struct sockaddr_in *) sa2; + + if (sin1->sin_port != sin2->sin_port) { + return NGX_DECLINED; + } + + if (sin1->sin_addr.s_addr != sin2->sin_addr.s_addr) { + return NGX_DECLINED; + } + + break; } return NGX_OK; } -#if !(NGX_WIN32) +static ngx_int_t +ngx_init_zone_pool(ngx_cycle_t *cycle, ngx_shm_zone_t *zn) +{ + u_char *file; + ngx_slab_pool_t *sp; + + sp = (ngx_slab_pool_t *) zn->shm.addr; + + if (zn->shm.exists) { + + if (sp == sp->addr) { + return NGX_OK; + } + + ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, + "shared zone \"%V\" has no equal addresses: %p vs %p", + &zn->shm.name, sp->addr, sp); + return NGX_ERROR; + } + + sp->end = zn->shm.addr + zn->shm.size; + sp->min_shift = 3; + sp->addr = zn->shm.addr; + +#if (NGX_HAVE_ATOMIC_OPS) + + file = NULL; + +#else + + file = ngx_pnalloc(cycle->pool, cycle->lock_file.len + zn->shm.name.len); + if (file == NULL) { + return NGX_ERROR; + } + + (void) ngx_sprintf(file, "%V%V%Z", &cycle->lock_file, &zn->shm.name); + +#endif + + if (ngx_shmtx_create(&sp->mutex, (void *) &sp->lock, file) != NGX_OK) { + return NGX_ERROR; + } + + ngx_slab_init(sp); + + return NGX_OK; +} + ngx_int_t ngx_create_pidfile(ngx_str_t *name, ngx_log_t *log) { - size_t len; - ngx_uint_t trunc; - ngx_file_t file; - u_char pid[NGX_INT64_LEN + 2]; + size_t len; + ngx_uint_t create; + ngx_file_t file; + u_char pid[NGX_INT64_LEN + 2]; + + if (ngx_process > NGX_PROCESS_MASTER) { + return NGX_OK; + } ngx_memzero(&file, sizeof(ngx_file_t)); file.name = *name; file.log = log; - trunc = ngx_test_config ? 0 : NGX_FILE_TRUNCATE; + create = ngx_test_config ? NGX_FILE_CREATE_OR_OPEN : NGX_FILE_TRUNCATE; file.fd = ngx_open_file(file.name.data, NGX_FILE_RDWR, - NGX_FILE_CREATE_OR_OPEN|trunc, - NGX_FILE_DEFAULT_ACCESS); + create, NGX_FILE_DEFAULT_ACCESS); if (file.fd == NGX_INVALID_FILE) { ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, @@ -897,7 +1025,57 @@ } } -#endif + +ngx_int_t +ngx_signal_process(ngx_cycle_t *cycle, char *sig) +{ + ssize_t n; + ngx_int_t pid; + ngx_file_t file; + ngx_core_conf_t *ccf; + u_char buf[NGX_INT64_LEN + 2]; + + ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "signal process started"); + + ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module); + + file.name = ccf->pid; + file.log = cycle->log; + + file.fd = ngx_open_file(file.name.data, NGX_FILE_RDONLY, + NGX_FILE_OPEN, NGX_FILE_DEFAULT_ACCESS); + + if (file.fd == NGX_INVALID_FILE) { + ngx_log_error(NGX_LOG_ERR, cycle->log, ngx_errno, + ngx_open_file_n " \"%s\" failed", file.name.data); + return 1; + } + + n = ngx_read_file(&file, buf, NGX_INT64_LEN + 2, 0); + + if (ngx_close_file(file.fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + ngx_close_file_n " \"%s\" failed", file.name.data); + } + + if (n == NGX_ERROR) { + return 1; + } + + while (n-- && (buf[n] == CR || buf[n] == LF)) { /* void */ } + + pid = ngx_atoi(buf, ++n); + + if (pid == NGX_ERROR) { + ngx_log_error(NGX_LOG_ERR, cycle->log, 0, + "invalid PID number \"%*s\" in \"%s\"", + n, buf, file.name.data); + return 1; + } + + return ngx_os_signal_process(cycle, sig, pid); + +} static ngx_int_t @@ -934,13 +1112,11 @@ void ngx_reopen_files(ngx_cycle_t *cycle, ngx_uid_t user) { + ssize_t n, len; ngx_fd_t fd; ngx_uint_t i; ngx_list_part_t *part; ngx_open_file_t *file; -#if !(NGX_WIN32) - ngx_file_info_t fi; -#endif part = &cycle->open_files.part; file = part->elts; @@ -956,19 +1132,32 @@ i = 0; } - if (file[i].name.data == NULL) { + if (file[i].name.len == 0) { continue; } - if (file[i].buffer && file[i].pos - file[i].buffer != 0) { - ngx_write_fd(file[i].fd, file[i].buffer, - file[i].pos - file[i].buffer); + len = file[i].pos - file[i].buffer; + + if (file[i].buffer && len != 0) { + + n = ngx_write_fd(file[i].fd, file[i].buffer, len); + + if (n == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + ngx_write_fd_n " to \"%s\" failed", + file[i].name.data); + + } else if (n != len) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, + ngx_write_fd_n " to \"%s\" was incomplete: %z of %uz", + file[i].name.data, n, len); + } + file[i].pos = file[i].buffer; } - fd = ngx_open_file(file[i].name.data, NGX_FILE_RDWR, - NGX_FILE_CREATE_OR_OPEN|NGX_FILE_APPEND, - NGX_FILE_DEFAULT_ACCESS); + fd = ngx_open_file(file[i].name.data, NGX_FILE_APPEND, + NGX_FILE_CREATE_OR_OPEN, NGX_FILE_DEFAULT_ACCESS); ngx_log_debug3(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "reopen file \"%s\", old:%d new:%d", @@ -980,24 +1169,13 @@ continue; } -#if (NGX_WIN32) - if (ngx_file_append_mode(fd) == NGX_ERROR) { - ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, - ngx_file_append_mode_n " \"%s\" failed", - file[i].name.data); - - if (ngx_close_file(fd) == NGX_FILE_ERROR) { - ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, - ngx_close_file_n " \"%s\" failed", - file[i].name.data); - } - - continue; - } -#else +#if !(NGX_WIN32) if (user != (ngx_uid_t) NGX_CONF_UNSET_UINT) { + ngx_file_info_t fi; - if (ngx_file_info((const char *) file[i].name.data, &fi) == -1) { + if (ngx_file_info((const char *) file[i].name.data, &fi) + == NGX_FILE_ERROR) + { ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, ngx_file_info_n " \"%s\" failed", file[i].name.data); @@ -1098,27 +1276,29 @@ i = 0; } - if (name->len != shm_zone[i].name.len) { + if (name->len != shm_zone[i].shm.name.len) { continue; } - if (ngx_strncmp(name->data, shm_zone[i].name.data, name->len) != 0) { + if (ngx_strncmp(name->data, shm_zone[i].shm.name.data, name->len) + != 0) + { continue; } if (size && size != shm_zone[i].shm.size) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "the size %uz of shared memory zone \"%V\" " - "conflicts with already declared size %uz", - size, &shm_zone[i].name, shm_zone[i].shm.size); + "the size %uz of shared memory zone \"%V\" " + "conflicts with already declared size %uz", + size, &shm_zone[i].shm.name, shm_zone[i].shm.size); return NULL; } if (tag != shm_zone[i].tag) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "the shared memory zone \"%V\" is " - "already declared for a different use", - &shm_zone[i].name); + "the shared memory zone \"%V\" is " + "already declared for a different use", + &shm_zone[i].shm.name); return NULL; } @@ -1134,8 +1314,9 @@ shm_zone->data = NULL; shm_zone->shm.log = cf->cycle->log; shm_zone->shm.size = size; + shm_zone->shm.name = *name; + shm_zone->shm.exists = 0; shm_zone->init = NULL; - shm_zone->name = *name; shm_zone->tag = tag; return shm_zone; diff -Nru nginx-0.5.33/src/core/ngx_cycle.h nginx-0.8.53/src/core/ngx_cycle.h --- nginx-0.5.33/src/core/ngx_cycle.h 2007-01-20 19:26:48.000000000 +0000 +++ nginx-0.8.53/src/core/ngx_cycle.h 2010-09-15 15:24:21.000000000 +0000 @@ -29,7 +29,6 @@ void *data; ngx_shm_t shm; ngx_shm_zone_init_pt init; - ngx_str_t name; void *tag; }; @@ -39,7 +38,7 @@ ngx_pool_t *pool; ngx_log_t *log; - ngx_log_t *new_log; + ngx_log_t new_log; ngx_connection_t **files; ngx_connection_t *free_connections; @@ -60,8 +59,11 @@ ngx_cycle_t *old_cycle; ngx_str_t conf_file; - ngx_str_t root; + ngx_str_t conf_param; + ngx_str_t conf_prefix; + ngx_str_t prefix; ngx_str_t lock_file; + ngx_str_t hostname; }; @@ -76,7 +78,7 @@ ngx_int_t rlimit_nofile; ngx_int_t rlimit_sigpending; - size_t rlimit_core; + off_t rlimit_core; int priority; @@ -115,6 +117,7 @@ ngx_cycle_t *ngx_init_cycle(ngx_cycle_t *old_cycle); ngx_int_t ngx_create_pidfile(ngx_str_t *name, ngx_log_t *log); void ngx_delete_pidfile(ngx_cycle_t *cycle); +ngx_int_t ngx_signal_process(ngx_cycle_t *cycle, char *sig); void ngx_reopen_files(ngx_cycle_t *cycle, ngx_uid_t user); char **ngx_set_environment(ngx_cycle_t *cycle, ngx_uint_t *last); ngx_pid_t ngx_exec_new_binary(ngx_cycle_t *cycle, char *const *argv); @@ -127,6 +130,7 @@ extern ngx_array_t ngx_old_cycles; extern ngx_module_t ngx_core_module; extern ngx_uint_t ngx_test_config; +extern ngx_uint_t ngx_quiet_mode; #if (NGX_THREADS) extern ngx_tls_key_t ngx_core_tls_key; #endif diff -Nru nginx-0.5.33/src/core/ngx_file.c nginx-0.8.53/src/core/ngx_file.c --- nginx-0.5.33/src/core/ngx_file.c 2007-08-11 10:11:33.000000000 +0000 +++ nginx-0.8.53/src/core/ngx_file.c 2010-05-14 09:56:37.000000000 +0000 @@ -8,8 +8,9 @@ #include -static ngx_atomic_uint_t ngx_temp_number; -static ngx_atomic_uint_t ngx_random_number; +static ngx_atomic_t temp_number = 0; +ngx_atomic_t *ngx_temp_number = &temp_number; +ngx_atomic_int_t ngx_random_number = 123456; ssize_t @@ -46,7 +47,7 @@ file->name.len = path->name.len + 1 + path->len + 10; - file->name.data = ngx_palloc(pool, file->name.len + 1); + file->name.data = ngx_pnalloc(pool, file->name.len + 1); if (file->name.data == NULL) { return NGX_ERROR; } @@ -61,16 +62,19 @@ n = (uint32_t) ngx_next_temp_number(0); + cln = ngx_pool_cleanup_add(pool, sizeof(ngx_pool_cleanup_file_t)); + if (cln == NULL) { + return NGX_ERROR; + } + for ( ;; ) { (void) ngx_sprintf(file->name.data + path->name.len + 1 + path->len, "%010uD%Z", n); - ngx_create_hashed_filename(file, path); + ngx_create_hashed_filename(path, file->name.data, file->name.len); - cln = ngx_pool_cleanup_add(pool, sizeof(ngx_pool_cleanup_file_t)); - if (cln == NULL) { - return NGX_ERROR; - } + ngx_log_debug1(NGX_LOG_DEBUG_CORE, file->log, 0, + "hashed path: %s", file->name.data); file->fd = ngx_open_tempfile(file->name.data, persistent, access); @@ -96,13 +100,7 @@ continue; } - if ((path->level[0] == 0) - || (err != NGX_ENOENT -#if (NGX_WIN32) - && err != NGX_ENOTDIR -#endif - )) - { + if ((path->level[0] == 0) || (err != NGX_ENOPATH)) { ngx_log_error(NGX_LOG_CRIT, file->log, err, ngx_open_tempfile_n " \"%s\" failed", file->name.data); @@ -117,31 +115,27 @@ void -ngx_create_hashed_filename(ngx_file_t *file, ngx_path_t *path) +ngx_create_hashed_filename(ngx_path_t *path, u_char *file, size_t len) { - size_t name, pos, level; - ngx_uint_t i; + size_t i, level; + ngx_uint_t n; - name = file->name.len; - pos = path->name.len + 1; + i = path->name.len + 1; - file->name.data[path->name.len + path->len] = '/'; + file[path->name.len + path->len] = '/'; - for (i = 0; i < 3; i++) { - level = path->level[i]; + for (n = 0; n < 3; n++) { + level = path->level[n]; if (level == 0) { break; } - name -= level; - file->name.data[pos - 1] = '/'; - ngx_memcpy(&file->name.data[pos], &file->name.data[name], level); - pos += level + 1; + len -= level; + file[i - 1] = '/'; + ngx_memcpy(&file[i], &file[len], level); + i += level + 1; } - - ngx_log_debug1(NGX_LOG_DEBUG_CORE, file->log, 0, - "hashed path: %s", file->name.data); } @@ -189,7 +183,15 @@ u_char *p, ch; ngx_err_t err; - for (p = dir + 1; *p; p++) { + err = 0; + +#if (NGX_WIN32) + p = dir + 3; +#else + p = dir + 1; +#endif + + for ( /* void */ ; *p; p++) { ch = *p; if (ch != '/') { @@ -200,7 +202,14 @@ if (ngx_create_dir(dir, access) == NGX_FILE_ERROR) { err = ngx_errno; - if (err != NGX_EEXIST) { + + switch (err) { + case NGX_EEXIST: + err = 0; + case NGX_EACCES: + break; + + default: return err; } } @@ -208,26 +217,20 @@ *p = '/'; } - return 0; -} - - -void -ngx_init_temp_number(void) -{ - ngx_temp_number = 0; - ngx_random_number = 123456; + return err; } ngx_atomic_uint_t ngx_next_temp_number(ngx_uint_t collision) { - if (collision) { - ngx_temp_number += ngx_random_number; - } + ngx_atomic_uint_t n, add; + + add = collision ? ngx_random_number : 1; + + n = ngx_atomic_fetch_add(ngx_temp_number, add); - return ngx_temp_number++; + return n + add; } @@ -260,12 +263,13 @@ path->name.len--; } - if (ngx_conf_full_name(cf->cycle, &path->name) == NGX_ERROR) { + if (ngx_conf_full_name(cf->cycle, &path->name, 0) != NGX_OK) { return NULL; } path->len = 0; - path->cleaner = (ngx_gc_handler_pt) cmd->post; + path->manager = NULL; + path->loader = NULL; path->conf_file = cf->conf_file->file.name.data; path->line = cf->conf_file->line; @@ -294,6 +298,50 @@ char * +ngx_conf_merge_path_value(ngx_conf_t *cf, ngx_path_t **path, ngx_path_t *prev, + ngx_path_init_t *init) +{ + if (*path) { + return NGX_CONF_OK; + } + + if (prev) { + *path = prev; + return NGX_CONF_OK; + } + + *path = ngx_palloc(cf->pool, sizeof(ngx_path_t)); + if (*path == NULL) { + return NGX_CONF_ERROR; + } + + (*path)->name = init->name; + + if (ngx_conf_full_name(cf->cycle, &(*path)->name, 0) != NGX_OK) { + return NGX_CONF_ERROR; + } + + (*path)->level[0] = init->level[0]; + (*path)->level[1] = init->level[1]; + (*path)->level[2] = init->level[2]; + + (*path)->len = init->level[0] + (init->level[0] ? 1 : 0) + + init->level[1] + (init->level[1] ? 1 : 0) + + init->level[2] + (init->level[2] ? 1 : 0); + + (*path)->manager = NULL; + (*path)->loader = NULL; + (*path)->conf_file = NULL; + + if (ngx_add_path(cf, path) != NGX_OK) { + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; +} + + +char * ngx_conf_set_access_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { char *confp = conf; @@ -425,9 +473,6 @@ ngx_err_t err; ngx_uint_t i; ngx_path_t **path; -#if !(NGX_WIN32) - ngx_file_info_t fi; -#endif path = cycle->pathes.elts; for (i = 0; i < cycle->pathes.nelts; i++) { @@ -447,8 +492,12 @@ } #if !(NGX_WIN32) + { + ngx_file_info_t fi; - if (ngx_file_info((const char *) path[i]->name.data, &fi) == -1) { + if (ngx_file_info((const char *) path[i]->name.data, &fi) + == NGX_FILE_ERROR) + { ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, ngx_file_info_n " \"%s\" failed", path[i]->name.data); return NGX_ERROR; @@ -474,7 +523,7 @@ return NGX_ERROR; } } - + } #endif } @@ -483,6 +532,289 @@ ngx_int_t +ngx_ext_rename_file(ngx_str_t *src, ngx_str_t *to, ngx_ext_rename_file_t *ext) +{ + u_char *name; + ngx_err_t err; + ngx_copy_file_t cf; + +#if !(NGX_WIN32) + + if (ext->access) { + if (ngx_change_file_access(src->data, ext->access) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_CRIT, ext->log, ngx_errno, + ngx_change_file_access_n " \"%s\" failed", src->data); + err = 0; + goto failed; + } + } + +#endif + + if (ext->time != -1) { + if (ngx_set_file_time(src->data, ext->fd, ext->time) != NGX_OK) { + ngx_log_error(NGX_LOG_CRIT, ext->log, ngx_errno, + ngx_set_file_time_n " \"%s\" failed", src->data); + err = 0; + goto failed; + } + } + + if (ngx_rename_file(src->data, to->data) != NGX_FILE_ERROR) { + return NGX_OK; + } + + err = ngx_errno; + + if (err == NGX_ENOPATH) { + + if (!ext->create_path) { + goto failed; + } + + err = ngx_create_full_path(to->data, ngx_dir_access(ext->path_access)); + + if (err) { + ngx_log_error(NGX_LOG_CRIT, ext->log, err, + ngx_create_dir_n " \"%s\" failed", to->data); + err = 0; + goto failed; + } + + if (ngx_rename_file(src->data, to->data) != NGX_FILE_ERROR) { + return NGX_OK; + } + + err = ngx_errno; + } + +#if (NGX_WIN32) + + if (err == NGX_EEXIST) { + err = ngx_win32_rename_file(src, to, ext->log); + + if (err == 0) { + return NGX_OK; + } + } + +#endif + + if (err == NGX_EXDEV) { + + cf.size = -1; + cf.buf_size = 0; + cf.access = ext->access; + cf.time = ext->time; + cf.log = ext->log; + + name = ngx_alloc(to->len + 1 + 10 + 1, ext->log); + if (name == NULL) { + return NGX_ERROR; + } + + (void) ngx_sprintf(name, "%*s.%010uD%Z", to->len, to->data, + (uint32_t) ngx_next_temp_number(0)); + + if (ngx_copy_file(src->data, name, &cf) == NGX_OK) { + + if (ngx_rename_file(name, to->data) != NGX_FILE_ERROR) { + ngx_free(name); + + if (ngx_delete_file(src->data) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_CRIT, ext->log, ngx_errno, + ngx_delete_file_n " \"%s\" failed", + src->data); + return NGX_ERROR; + } + + return NGX_OK; + } + + ngx_log_error(NGX_LOG_CRIT, ext->log, ngx_errno, + ngx_rename_file_n " \"%s\" to \"%s\" failed", + name, to->data); + + if (ngx_delete_file(name) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_CRIT, ext->log, ngx_errno, + ngx_delete_file_n " \"%s\" failed", name); + + } + } + + ngx_free(name); + + err = 0; + } + +failed: + + if (ext->delete_file) { + if (ngx_delete_file(src->data) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_CRIT, ext->log, ngx_errno, + ngx_delete_file_n " \"%s\" failed", src->data); + } + } + + if (err) { + ngx_log_error(NGX_LOG_CRIT, ext->log, err, + ngx_rename_file_n " \"%s\" to \"%s\" failed", + src->data, to->data); + } + + return NGX_ERROR; +} + + +ngx_int_t +ngx_copy_file(u_char *from, u_char *to, ngx_copy_file_t *cf) +{ + char *buf; + off_t size; + size_t len; + ssize_t n; + ngx_fd_t fd, nfd; + ngx_int_t rc; + ngx_file_info_t fi; + + rc = NGX_ERROR; + buf = NULL; + nfd = NGX_INVALID_FILE; + + fd = ngx_open_file(from, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0); + + if (fd == NGX_INVALID_FILE) { + ngx_log_error(NGX_LOG_CRIT, cf->log, ngx_errno, + ngx_open_file_n " \"%s\" failed", from); + goto failed; + } + + if (cf->size != -1) { + size = cf->size; + + } else { + if (ngx_fd_info(fd, &fi) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno, + ngx_fd_info_n " \"%s\" failed", from); + + goto failed; + } + + size = ngx_file_size(&fi); + } + + len = cf->buf_size ? cf->buf_size : 65536; + + if ((off_t) len > size) { + len = (size_t) size; + } + + buf = ngx_alloc(len, cf->log); + if (buf == NULL) { + goto failed; + } + + nfd = ngx_open_file(to, NGX_FILE_WRONLY, NGX_FILE_CREATE_OR_OPEN, + cf->access); + + if (nfd == NGX_INVALID_FILE) { + ngx_log_error(NGX_LOG_CRIT, cf->log, ngx_errno, + ngx_open_file_n " \"%s\" failed", to); + goto failed; + } + + while (size > 0) { + + if ((off_t) len > size) { + len = (size_t) size; + } + + n = ngx_read_fd(fd, buf, len); + + if (n == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno, + ngx_read_fd_n " \"%s\" failed", from); + goto failed; + } + + if ((size_t) n != len) { + ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno, + ngx_read_fd_n " has read only %z of %uz from %s", + n, size, from); + goto failed; + } + + n = ngx_write_fd(nfd, buf, len); + + if (n == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno, + ngx_write_fd_n " \"%s\" failed", to); + goto failed; + } + + if ((size_t) n != len) { + ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno, + ngx_write_fd_n " has written only %z of %uz to %s", + n, size, to); + goto failed; + } + + size -= n; + } + + if (cf->time != -1) { + if (ngx_set_file_time(to, nfd, cf->time) != NGX_OK) { + ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno, + ngx_set_file_time_n " \"%s\" failed", to); + goto failed; + } + } + + rc = NGX_OK; + +failed: + + if (nfd != NGX_INVALID_FILE) { + if (ngx_close_file(nfd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno, + ngx_close_file_n " \"%s\" failed", to); + } + } + + if (fd != NGX_INVALID_FILE) { + if (ngx_close_file(fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno, + ngx_close_file_n " \"%s\" failed", from); + } + } + + if (buf) { + ngx_free(buf); + } + + return rc; +} + + +/* + * ctx->init_handler() - see ctx->alloc + * ctx->file_handler() - file handler + * ctx->pre_tree_handler() - handler is called before entering directory + * ctx->post_tree_handler() - handler is called after leaving directory + * ctx->spec_handler() - special (socket, FIFO, etc.) file handler + * + * ctx->data - some data structure, it may be the same on all levels, or + * reallocated if ctx->alloc is nonzero + * + * ctx->alloc - a size of data structure that is allocated at every level + * and is initilialized by ctx->init_handler() + * + * ctx->log - a log + * + * on fatal (memory) error handler must return NGX_ABORT to stop walking tree + */ + +ngx_int_t ngx_walk_tree(ngx_tree_ctx_t *ctx, ngx_str_t *tree) { void *data, *prev; @@ -493,8 +825,7 @@ ngx_str_t file, buf; ngx_dir_t dir; - buf.len = 0; - buf.data = NULL; + ngx_str_null(&buf); ngx_log_debug1(NGX_LOG_DEBUG_CORE, ctx->log, 0, "walk tree \"%V\"", tree); diff -Nru nginx-0.5.33/src/core/ngx_file.h nginx-0.8.53/src/core/ngx_file.h --- nginx-0.5.33/src/core/ngx_file.h 2007-08-11 10:11:33.000000000 +0000 +++ nginx-0.8.53/src/core/ngx_file.h 2009-08-28 08:12:35.000000000 +0000 @@ -11,52 +11,92 @@ #include #include -typedef struct ngx_path_s ngx_path_t; - -#include - struct ngx_file_s { - ngx_fd_t fd; - ngx_str_t name; - ngx_file_info_t info; + ngx_fd_t fd; + ngx_str_t name; + ngx_file_info_t info; - off_t offset; - off_t sys_offset; + off_t offset; + off_t sys_offset; - ngx_log_t *log; + ngx_log_t *log; - ngx_uint_t valid_info; /* unsigned valid_info:1; */ +#if (NGX_HAVE_FILE_AIO) + ngx_event_aio_t *aio; +#endif + + unsigned valid_info:1; + unsigned directio:1; }; + #define NGX_MAX_PATH_LEVEL 3 -struct ngx_path_s { - ngx_str_t name; - size_t len; - size_t level[3]; - ngx_gc_handler_pt cleaner; - u_char *conf_file; - ngx_uint_t line; -}; +typedef time_t (*ngx_path_manager_pt) (void *data); +typedef void (*ngx_path_loader_pt) (void *data); typedef struct { - ngx_file_t file; - off_t offset; - ngx_path_t *path; - ngx_pool_t *pool; - char *warn; - - ngx_uint_t access; - - unsigned log_level:8; - unsigned persistent:1; - unsigned clean:1; + ngx_str_t name; + size_t len; + size_t level[3]; + + ngx_path_manager_pt manager; + ngx_path_loader_pt loader; + void *data; + + u_char *conf_file; + ngx_uint_t line; +} ngx_path_t; + + +typedef struct { + ngx_str_t name; + size_t level[3]; +} ngx_path_init_t; + + +typedef struct { + ngx_file_t file; + off_t offset; + ngx_path_t *path; + ngx_pool_t *pool; + char *warn; + + ngx_uint_t access; + + unsigned log_level:8; + unsigned persistent:1; + unsigned clean:1; } ngx_temp_file_t; +typedef struct { + ngx_uint_t access; + ngx_uint_t path_access; + time_t time; + ngx_fd_t fd; + + unsigned create_path:1; + unsigned delete_file:1; + + ngx_log_t *log; +} ngx_ext_rename_file_t; + + +typedef struct { + off_t size; + size_t buf_size; + + ngx_uint_t access; + time_t time; + + ngx_log_t *log; +} ngx_copy_file_t; + + typedef struct ngx_tree_ctx_s ngx_tree_ctx_t; typedef ngx_int_t (*ngx_tree_init_handler_pt) (void *ctx, void *prev); @@ -84,51 +124,26 @@ ngx_int_t ngx_create_temp_file(ngx_file_t *file, ngx_path_t *path, ngx_pool_t *pool, ngx_uint_t persistent, ngx_uint_t clean, ngx_uint_t access); -void ngx_create_hashed_filename(ngx_file_t *file, ngx_path_t *path); +void ngx_create_hashed_filename(ngx_path_t *path, u_char *file, size_t len); ngx_int_t ngx_create_path(ngx_file_t *file, ngx_path_t *path); ngx_err_t ngx_create_full_path(u_char *dir, ngx_uint_t access); ngx_int_t ngx_add_path(ngx_conf_t *cf, ngx_path_t **slot); ngx_int_t ngx_create_pathes(ngx_cycle_t *cycle, ngx_uid_t user); +ngx_int_t ngx_ext_rename_file(ngx_str_t *src, ngx_str_t *to, + ngx_ext_rename_file_t *ext); +ngx_int_t ngx_copy_file(u_char *from, u_char *to, ngx_copy_file_t *cf); ngx_int_t ngx_walk_tree(ngx_tree_ctx_t *ctx, ngx_str_t *tree); -void ngx_init_temp_number(void); ngx_atomic_uint_t ngx_next_temp_number(ngx_uint_t collision); char *ngx_conf_set_path_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +char *ngx_conf_merge_path_value(ngx_conf_t *cf, ngx_path_t **path, + ngx_path_t *prev, ngx_path_init_t *init); char *ngx_conf_set_access_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); -#define ngx_conf_merge_path_value(curr, prev, path, l1, l2, l3, clean, cf) \ - if (curr == NULL) { \ - if (prev == NULL) { \ - curr = ngx_palloc(cf->pool, sizeof(ngx_path_t)); \ - if (curr == NULL) { \ - return NGX_CONF_ERROR; \ - } \ - \ - curr->name.len = sizeof(path) - 1; \ - curr->name.data = (u_char *) path; \ - \ - if (ngx_conf_full_name(cf->cycle, &curr->name) == NGX_ERROR) { \ - return NGX_CONF_ERROR; \ - } \ - \ - curr->level[0] = l1; \ - curr->level[1] = l2; \ - curr->level[2] = l3; \ - curr->len = l1 + l2 + l3 + (l1 ? 1:0) + (l2 ? 1:0) + (l3 ? 1:0); \ - curr->cleaner = clean; \ - curr->conf_file = NULL; \ - \ - if (ngx_add_path(cf, &curr) == NGX_ERROR) { \ - return NGX_CONF_ERROR; \ - } \ - \ - } else { \ - curr = prev; \ - } \ - } - +extern ngx_atomic_t *ngx_temp_number; +extern ngx_atomic_int_t ngx_random_number; #endif /* _NGX_FILE_H_INCLUDED_ */ diff -Nru nginx-0.5.33/src/core/ngx_garbage_collector.c nginx-0.8.53/src/core/ngx_garbage_collector.c --- nginx-0.5.33/src/core/ngx_garbage_collector.c 2006-08-30 10:39:17.000000000 +0000 +++ nginx-0.8.53/src/core/ngx_garbage_collector.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,217 +0,0 @@ - -/* - * Copyright (C) Igor Sysoev - */ - - -#include -#include - - - -ngx_int_t ngx_collect_garbage(ngx_gc_t *ctx, ngx_str_t *dname, ngx_int_t level) -{ - int rc; - u_char *last; - size_t len; - ngx_err_t err; - ngx_str_t fname, buf; - ngx_dir_t dir; - - buf.len = 0; -#if (NGX_SUPPRESS_WARN) - buf.data = NULL; - fname.data = NULL; -#endif - - ngx_log_debug2(NGX_LOG_DEBUG_CORE, ctx->log, 0, - "gc dir \"%s\":%d", dname->data, dname->len); - - if (ngx_open_dir(dname, &dir) == NGX_ERROR) { - ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno, - ngx_open_dir_n " \"%s\" failed", dname->data); - return NGX_ERROR; - } - - 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, ctx->log, err, - ngx_read_dir_n " \"%s\" failed", dname->data); - rc = NGX_ERROR; - - } else { - rc = NGX_OK; - } - - break; - } - - len = ngx_de_namelen(&dir); - - ngx_log_debug2(NGX_LOG_DEBUG_CORE, ctx->log, 0, - "gc name \"%s\":%d", ngx_de_name(&dir), len); - - if (len == 1 && ngx_de_name(&dir)[0] == '.') { - continue; - } - - if (len == 2 - && ngx_de_name(&dir)[0] == '.' - && ngx_de_name(&dir)[1] == '.') - { - continue; - } - - fname.len = dname->len + 1+ len; - - if (fname.len + NGX_DIR_MASK_LEN > buf.len) { - - if (buf.len) { - ngx_free(buf.data); - } - - buf.len = dname->len + 1 + len + NGX_DIR_MASK_LEN; - - buf.data = ngx_alloc(buf.len + 1, ctx->log); - if (buf.data == NULL) { - return NGX_ABORT; - } - } - - last = ngx_cpymem(buf.data, dname->data, dname->len); - *last++ = '/'; - ngx_memcpy(last, ngx_de_name(&dir), len + 1); - fname.data = buf.data; - - ngx_log_debug1(NGX_LOG_DEBUG_CORE, ctx->log, 0, - "gc path: \"%s\"", fname.data); - - if (!dir.valid_info) { - if (ngx_de_info(fname.data, &dir) == NGX_FILE_ERROR) { - ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno, - ngx_de_info_n " \"%s\" failed", fname.data); - continue; - } - } - - if (ngx_de_is_dir(&dir)) { - - ngx_log_debug1(NGX_LOG_DEBUG_CORE, ctx->log, 0, - "gc enter dir \"%s\"", fname.data); - - if (level == -1 - /* there can not be directory on the last level */ - || level == NGX_MAX_PATH_LEVEL - /* an directory from the old path hierarchy */ - || len != ctx->path->level[level]) - { - if (ngx_collect_garbage(ctx, &fname, -1) == NGX_ABORT) { - return NGX_ABORT; - } - - fname.data[fname.len] = '\0'; - - ngx_log_error(NGX_LOG_NOTICE, ctx->log, 0, - "delete old hierachy directory \"%s\"", - fname.data); - - if (ngx_delete_dir(fname.data) == NGX_FILE_ERROR) { - ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno, - ngx_delete_dir_n " \"%s\" failed", - fname.data); - } else { - ctx->deleted++; - ctx->freed += ngx_de_size(&dir); - } - - continue; - } - - if (ngx_collect_garbage(ctx, &fname, level + 1) == NGX_ABORT) { - return NGX_ABORT; - } - - } else if (ngx_de_is_file(&dir)) { - - ngx_log_debug1(NGX_LOG_DEBUG_CORE, ctx->log, 0, - "gc file \"%s\"", fname.data); - - if (level == -1 - || (level < NGX_MAX_PATH_LEVEL && ctx->path->level[level] != 0)) - { - if (ngx_delete_file(fname.data) == NGX_FILE_ERROR) { - ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno, - ngx_delete_file_n " \"%s\" failed", - fname.data); - } else { - ctx->deleted++; - ctx->freed += ngx_de_size(&dir); - } - - continue; - } - - if (ctx->handler(ctx, &fname, &dir) == NGX_ABORT) { - return NGX_ABORT; - } - - } else { - ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno, - "the file \"%s\" has unknown type, deleting", - fname.data); - - if (ngx_delete_file(fname.data) == NGX_FILE_ERROR) { - ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno, - ngx_delete_file_n " \"%s\" failed", fname.data); - } else { - ctx->deleted++; - ctx->freed += ngx_de_size(&dir); - } - } - } - - if (buf.len) { - ngx_free(buf.data); - } - - if (ngx_close_dir(&dir) == NGX_ERROR) { - ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno, - ngx_close_dir_n " \"%s\" failed", fname.data); - } - - return rc; -} - - -ngx_int_t ngx_garbage_collector_temp_handler(ngx_gc_t *ctx, ngx_str_t *name, - ngx_dir_t *dir) -{ - /* - * We use mtime only and do not use atime because: - * on NTFS access time has a resolution of 1 hour, - * on NT FAT access time has a resolution of 1 day, - * Unices have the mount option "noatime". - */ - - if (ngx_time() - ngx_de_mtime(dir) < 3600) { - return NGX_OK; - } - - ngx_log_error(NGX_LOG_NOTICE, ctx->log, 0, - "delete the stale temporary file \"%s\"", name->data); - - if (ngx_delete_file(name->data) == NGX_FILE_ERROR) { - ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno, - ngx_delete_file_n " \"%s\" failed", name->data); - return NGX_ERROR; - } - - ctx->deleted++; - ctx->freed += ngx_de_size(dir); - - return NGX_OK; -} diff -Nru nginx-0.5.33/src/core/ngx_garbage_collector.h nginx-0.8.53/src/core/ngx_garbage_collector.h --- nginx-0.5.33/src/core/ngx_garbage_collector.h 2006-08-30 10:39:17.000000000 +0000 +++ nginx-0.8.53/src/core/ngx_garbage_collector.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,31 +0,0 @@ - -/* - * Copyright (C) Igor Sysoev - */ - - -#ifndef _NGX_GARBAGE_COLLECTOR_H_INCLUDED_ -#define _NGX_GARBAGE_COLLECTOR_H_INCLUDED_ - - -typedef struct ngx_gc_s ngx_gc_t; - -typedef ngx_int_t (*ngx_gc_handler_pt) (ngx_gc_t *ctx, ngx_str_t *name, - ngx_dir_t *dir); - - -struct ngx_gc_s { - ngx_path_t *path; - u_int deleted; - off_t freed; - ngx_gc_handler_pt handler; - ngx_log_t *log; -}; - - -ngx_int_t ngx_collect_garbage(ngx_gc_t *ctx, ngx_str_t *dname, ngx_int_t level); -ngx_int_t ngx_garbage_collector_temp_handler(ngx_gc_t *ctx, ngx_str_t *name, - ngx_dir_t *dir); - - -#endif /* _NGX_GARBAGE_COLLECTOR_H_INCLUDED_ */ diff -Nru nginx-0.5.33/src/core/ngx_hash.c nginx-0.8.53/src/core/ngx_hash.c --- nginx-0.5.33/src/core/ngx_hash.c 2007-11-07 13:39:53.000000000 +0000 +++ nginx-0.8.53/src/core/ngx_hash.c 2010-05-13 12:52:45.000000000 +0000 @@ -15,11 +15,7 @@ ngx_hash_elt_t *elt; #if 0 - ngx_str_t line; - - line.len = len; - line.data = name; - ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "hf:\"%V\"", &line); + ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "hf:\"%*s\"", len, name); #endif elt = hash->buckets[key % hash->size]; @@ -59,11 +55,7 @@ ngx_uint_t i, n, key; #if 0 - ngx_str_t line; - - line.len = len; - line.data = name; - ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "wch:\"%V\"", &line); + ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "wch:\"%*s\"", len, name); #endif n = len; @@ -88,30 +80,40 @@ value = ngx_hash_find(&hwc->hash, key, &name[n], len - n); +#if 0 + ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "value:\"%p\"", value); +#endif + if (value) { /* * the 2 low bits of value have the special meaning: - * 00 - value is data pointer, - * 01 - value is pointer to wildcard hash allowing - * "*.example.com" only, + * 00 - value is data pointer for both "example.com" + * and "*.example.com"; + * 01 - value is data pointer for "*.example.com" only; + * 10 - value is pointer to wildcard hash allowing + * both "example.com" and "*.example.com"; * 11 - value is pointer to wildcard hash allowing - * both "example.com" and "*.example.com". + * "*.example.com" only. */ - if ((uintptr_t) value & 1) { - - hwc = (ngx_hash_wildcard_t *) ((uintptr_t) value & (uintptr_t) ~3); + if ((uintptr_t) value & 2) { if (n == 0) { - if ((uintptr_t) value & 2) { - return hwc->value; - } else { + /* "example.com" */ + + if ((uintptr_t) value & 1) { return NULL; } + + hwc = (ngx_hash_wildcard_t *) + ((uintptr_t) value & (uintptr_t) ~3); + return hwc->value; } + hwc = (ngx_hash_wildcard_t *) ((uintptr_t) value & (uintptr_t) ~3); + value = ngx_hash_find_wc_head(hwc, name, n - 1); if (value) { @@ -121,6 +123,18 @@ return hwc->value; } + if ((uintptr_t) value & 1) { + + if (n == 0) { + + /* "example.com" */ + + return NULL; + } + + return (void *) ((uintptr_t) value & (uintptr_t) ~3); + } + return value; } @@ -135,11 +149,7 @@ ngx_uint_t i, key; #if 0 - ngx_str_t line; - - line.len = len; - line.data = name; - ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "wct:\"%V\"", &line); + ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "wct:\"%*s\"", len, name); #endif key = 0; @@ -162,15 +172,19 @@ value = ngx_hash_find(&hwc->hash, key, name, i); +#if 0 + ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "value:\"%p\"", value); +#endif + if (value) { /* * the 2 low bits of value have the special meaning: - * 00 - value is data pointer, - * 01 - value is pointer to wildcard hash allowing "example.*". + * 00 - value is data pointer; + * 11 - value is pointer to wildcard hash allowing "example.*". */ - if ((uintptr_t) value & 1) { + if ((uintptr_t) value & 2) { i++; @@ -206,6 +220,10 @@ } } + if (len == 0) { + return NULL; + } + if (hash->wc_head && hash->wc_head->hash.buckets) { value = ngx_hash_find_wc_head(hash->wc_head, name, len); @@ -227,7 +245,7 @@ #define NGX_HASH_ELT_SIZE(name) \ - (sizeof(void *) + ngx_align((name)->key.len + 1, sizeof(void *))) + (sizeof(void *) + ngx_align((name)->key.len + 2, sizeof(void *))) ngx_int_t ngx_hash_init(ngx_hash_init_t *hinit, ngx_hash_key_t *names, ngx_uint_t nelts) @@ -239,14 +257,6 @@ ngx_hash_elt_t *elt, **buckets; for (n = 0; n < nelts; n++) { - if (names[n].key.len >= 255) { - ngx_log_error(NGX_LOG_EMERG, hinit->pool->log, 0, - "the \"%V\" value to hash is to long: %uz bytes, " - "the maximum length can be 255 bytes only", - &names[n].key, names[n].key.len); - return NGX_ERROR; - } - if (hinit->bucket_size < NGX_HASH_ELT_SIZE(&names[n]) + sizeof(void *)) { ngx_log_error(NGX_LOG_EMERG, hinit->pool->log, 0, @@ -388,11 +398,9 @@ elt = (ngx_hash_elt_t *) ((u_char *) buckets[key] + test[key]); elt->value = names[n].value; - elt->len = (u_char) names[n].key.len; + elt->len = (u_short) names[n].key.len; - for (i = 0; i < names[n].key.len; i++) { - elt->name[i] = ngx_tolower(names[n].key.data[i]); - } + ngx_strlow(elt->name, names[n].key.data, names[n].key.len); test[key] = (u_short) (test[key] + NGX_HASH_ELT_SIZE(&names[n])); } @@ -518,7 +526,7 @@ next_name->key.len = names[n].key.len - len; next_name->key.data = names[n].key.data + len; - next_name->key_hash= 0; + next_name->key_hash = 0; next_name->value = names[n].value; #if 0 @@ -546,7 +554,7 @@ next_name->key.len = names[i].key.len - dot_len; next_name->key.data = names[i].key.data + dot_len; - next_name->key_hash= 0; + next_name->key_hash = 0; next_name->value = names[i].value; #if 0 @@ -571,13 +579,12 @@ if (names[n].key.len == len) { wdc->value = names[n].value; -#if 0 - ngx_log_error(NGX_LOG_ALERT, hinit->pool->log, 0, - "wdc: \"%V\"", wdc->value); -#endif } - name->value = (void *) ((uintptr_t) wdc | (dot ? 1 : 3)); + name->value = (void *) ((uintptr_t) wdc | (dot ? 3 : 2)); + + } else if (dot) { + name->value = (void *) ((uintptr_t) name->value | 1); } } @@ -622,6 +629,24 @@ } +ngx_uint_t +ngx_hash_strlow(u_char *dst, u_char *src, size_t n) +{ + ngx_uint_t key; + + key = 0; + + while (n--) { + *dst = ngx_tolower(*src); + key = ngx_hash(key, *dst); + dst++; + src++; + } + + return key; +} + + ngx_int_t ngx_hash_keys_array_init(ngx_hash_keys_arrays_t *ha, ngx_uint_t type) { @@ -796,12 +821,7 @@ /* wildcard hash */ - k = 0; - - for (i = skip; i < last; i++) { - key->data[i] = ngx_tolower(key->data[i]); - k = ngx_hash(k, key->data[i]); - } + k = ngx_hash_strlow(&key->data[skip], &key->data[skip], last - skip); k %= ha->hsize; @@ -839,7 +859,7 @@ } name->len = last - 1; - name->data = ngx_palloc(ha->temp_pool, name->len); + name->data = ngx_pnalloc(ha->temp_pool, name->len); if (name->data == NULL) { return NGX_ERROR; } @@ -855,7 +875,7 @@ * and ".example.com" to "com.example\0" */ - p = ngx_palloc(ha->temp_pool, last); + p = ngx_pnalloc(ha->temp_pool, last); if (p == NULL) { return NGX_ERROR; } @@ -891,7 +911,7 @@ last++; - p = ngx_palloc(ha->temp_pool, last); + p = ngx_pnalloc(ha->temp_pool, last); if (p == NULL) { return NGX_ERROR; } @@ -944,7 +964,7 @@ } name->len = last - skip; - name->data = ngx_palloc(ha->temp_pool, name->len); + name->data = ngx_pnalloc(ha->temp_pool, name->len); if (name->data == NULL) { return NGX_ERROR; } diff -Nru nginx-0.5.33/src/core/ngx_hash.h nginx-0.8.53/src/core/ngx_hash.h --- nginx-0.5.33/src/core/ngx_hash.h 2007-11-07 13:39:53.000000000 +0000 +++ nginx-0.8.53/src/core/ngx_hash.h 2010-05-13 12:52:45.000000000 +0000 @@ -14,7 +14,7 @@ typedef struct { void *value; - u_char len; + u_short len; u_char name[1]; } ngx_hash_elt_t; @@ -110,6 +110,8 @@ #define ngx_hash(key, c) ((ngx_uint_t) key * 31 + c) ngx_uint_t ngx_hash_key(u_char *data, size_t len); ngx_uint_t ngx_hash_key_lc(u_char *data, size_t len); +ngx_uint_t ngx_hash_strlow(u_char *dst, u_char *src, size_t n); + ngx_int_t ngx_hash_keys_array_init(ngx_hash_keys_arrays_t *ha, ngx_uint_t type); ngx_int_t ngx_hash_add_key(ngx_hash_keys_arrays_t *ha, ngx_str_t *key, diff -Nru nginx-0.5.33/src/core/ngx_inet.c nginx-0.8.53/src/core/ngx_inet.c --- nginx-0.5.33/src/core/ngx_inet.c 2007-09-22 19:02:39.000000000 +0000 +++ nginx-0.8.53/src/core/ngx_inet.c 2009-12-07 15:13:46.000000000 +0000 @@ -8,483 +8,920 @@ #include -/* - * ngx_sock_ntop() and ngx_inet_ntop() may be implemented as - * "ngx_sprintf(text, "%ud.%ud.%ud.%ud", p[0], p[1], p[2], p[3])", however, - * they had been implemented long before the ngx_sprintf() had appeared - * and they are faster by 1.5-2.5 times, so it is worth to keep them. - * - * By the way, the implementation using ngx_sprintf() is faster by 2.5-3 times - * than using FreeBSD libc's snprintf(). - */ +static ngx_int_t ngx_parse_unix_domain_url(ngx_pool_t *pool, ngx_url_t *u); +static ngx_int_t ngx_parse_inet_url(ngx_pool_t *pool, ngx_url_t *u); +static ngx_int_t ngx_parse_inet6_url(ngx_pool_t *pool, ngx_url_t *u); -static ngx_inline size_t -ngx_sprint_uchar(u_char *text, u_char c, size_t len) +in_addr_t +ngx_inet_addr(u_char *text, size_t len) { - size_t n; - ngx_uint_t c1, c2; + u_char *p, c; + in_addr_t addr; + ngx_uint_t octet, n; + addr = 0; + octet = 0; n = 0; - if (len == n) { - return n; - } - - c1 = c / 100; + for (p = text; p < text + len; p++) { - if (c1) { - *text++ = (u_char) (c1 + '0'); - n++; + c = *p; - if (len == n) { - return n; + if (c >= '0' && c <= '9') { + octet = octet * 10 + (c - '0'); + continue; } - } - - c2 = (c % 100) / 10; - - if (c1 || c2) { - *text++ = (u_char) (c2 + '0'); - n++; - if (len == n) { - return n; + if (c == '.' && octet < 256) { + addr = (addr << 8) + octet; + octet = 0; + n++; + continue; } + + return INADDR_NONE; } - c2 = c % 10; + if (n != 3) { + return INADDR_NONE; + } - *text++ = (u_char) (c2 + '0'); - n++; + if (octet < 256) { + addr = (addr << 8) + octet; + return htonl(addr); + } - return n; + return INADDR_NONE; } -/* AF_INET only */ +#if (NGX_HAVE_INET6) -size_t -ngx_sock_ntop(int family, struct sockaddr *sa, u_char *text, size_t len) +ngx_int_t +ngx_inet6_addr(u_char *p, size_t len, u_char *addr) { - u_char *p; - size_t n; - ngx_uint_t i; - struct sockaddr_in *sin; + u_char c, *zero, *digit, *s, *d; + size_t len4; + ngx_uint_t n, nibbles, word; if (len == 0) { - return 0; + return NGX_ERROR; } - if (family != AF_INET) { - return 0; + zero = NULL; + digit = NULL; + len4 = 0; + nibbles = 0; + word = 0; + n = 8; + + if (p[0] == ':') { + p++; + len--; + } + + for (/* void */; len; len--) { + c = *p++; + + if (c == ':') { + if (nibbles) { + digit = p; + len4 = len; + *addr++ = (u_char) (word >> 8); + *addr++ = (u_char) (word & 0xff); + + if (--n) { + nibbles = 0; + word = 0; + continue; + } + + } else { + if (zero == NULL) { + digit = p; + len4 = len; + zero = addr; + continue; + } + } + + return NGX_ERROR; + } + + if (c == '.' && nibbles) { + if (n < 2) { + return NGX_ERROR; + } + + word = ngx_inet_addr(digit, len4 - 1); + if (word == INADDR_NONE) { + return NGX_ERROR; + } + + word = ntohl(word); + *addr++ = (u_char) ((word >> 24) & 0xff); + *addr++ = (u_char) ((word >> 16) & 0xff); + n--; + break; + } + + if (++nibbles > 4) { + return NGX_ERROR; + } + + if (c >= '0' && c <= '9') { + word = word * 16 + (c - '0'); + continue; + } + + c |= 0x20; + + if (c >= 'a' && c <= 'f') { + word = word * 16 + (c - 'a') + 10; + continue; + } + + return NGX_ERROR; + } + + if (nibbles == 0 && zero == NULL) { + return NGX_ERROR; } - sin = (struct sockaddr_in *) sa; - p = (u_char *) &sin->sin_addr; + *addr++ = (u_char) (word >> 8); + *addr++ = (u_char) (word & 0xff); + + if (--n) { + if (zero) { + n *= 2; + s = addr - 1; + d = s + n; + while (s >= zero) { + *d-- = *s--; + } + ngx_memzero(zero, n); + return NGX_OK; + } - if (len > INET_ADDRSTRLEN) { - len = INET_ADDRSTRLEN; + } else { + if (zero == NULL) { + return NGX_OK; + } } - n = ngx_sprint_uchar(text, p[0], len); + return NGX_ERROR; +} + +#endif + - i = 1; +size_t +ngx_sock_ntop(struct sockaddr *sa, u_char *text, size_t len, ngx_uint_t port) +{ + u_char *p; + struct sockaddr_in *sin; +#if (NGX_HAVE_INET6) + size_t n; + struct sockaddr_in6 *sin6; +#endif +#if (NGX_HAVE_UNIX_DOMAIN) + struct sockaddr_un *saun; +#endif + + switch (sa->sa_family) { - do { - if (len == n) { - text[n - 1] = '\0'; - return n; + case AF_INET: + + sin = (struct sockaddr_in *) sa; + p = (u_char *) &sin->sin_addr; + + if (port) { + p = ngx_snprintf(text, len, "%ud.%ud.%ud.%ud:%d", + p[0], p[1], p[2], p[3], ntohs(sin->sin_port)); + } else { + p = ngx_snprintf(text, len, "%ud.%ud.%ud.%ud", + p[0], p[1], p[2], p[3]); } - text[n++] = '.'; + return (p - text); + +#if (NGX_HAVE_INET6) + + case AF_INET6: - if (len == n) { - text[n - 1] = '\0'; - return n; + sin6 = (struct sockaddr_in6 *) sa; + + n = 0; + + if (port) { + text[n++] = '['; } - n += ngx_sprint_uchar(&text[n], p[i++], len - n); + n = ngx_inet6_ntop(sin6->sin6_addr.s6_addr, &text[n], len); - } while (i < 4); + if (port) { + n = ngx_sprintf(&text[1 + n], "]:%d", + ntohs(sin6->sin6_port)) - text; + } - if (len == n) { - text[n] = '\0'; return n; - } +#endif - text[n] = '\0'; +#if (NGX_HAVE_UNIX_DOMAIN) + + case AF_UNIX: + saun = (struct sockaddr_un *) sa; + + /* we do not include trailing zero in address length */ + + return ngx_snprintf(text, len, "unix:%s%Z", saun->sun_path) - text - 1; + +#endif - return n; + default: + return 0; + } } + size_t ngx_inet_ntop(int family, void *addr, u_char *text, size_t len) { - u_char *p; - size_t n; - ngx_uint_t i; + u_char *p; - if (len == 0) { + switch (family) { + + case AF_INET: + + p = addr; + + return ngx_snprintf(text, len, "%ud.%ud.%ud.%ud", + p[0], p[1], p[2], p[3]) + - text; + +#if (NGX_HAVE_INET6) + + case AF_INET6: + return ngx_inet6_ntop(addr, text, len); + +#endif + + default: return 0; } +} - if (family != AF_INET) { + +#if (NGX_HAVE_INET6) + +size_t +ngx_inet6_ntop(u_char *p, u_char *text, size_t len) +{ + u_char *dst; + size_t max, n; + ngx_uint_t i, zero, last; + + if (len < NGX_INET6_ADDRSTRLEN) { return 0; } - p = (u_char *) addr; + zero = (ngx_uint_t) -1; + last = (ngx_uint_t) -1; + max = 1; + n = 0; - if (len > INET_ADDRSTRLEN) { - len = INET_ADDRSTRLEN; - } + for (i = 0; i < 16; i += 2) { + + if (p[i] || p[i + 1]) { - n = ngx_sprint_uchar(text, p[0], len); + if (max < n) { + zero = last; + max = n; + } - i = 1; + n = 0; + continue; + } - do { - if (len == n) { - text[n - 1] = '\0'; - return n; + if (n++ == 0) { + last = i; } + } + + if (max < n) { + zero = last; + max = n; + } + + dst = text; + n = 16; - text[n++] = '.'; + if (zero == 0) { - if (len == n) { - text[n - 1] = '\0'; - return n; + if ((max == 5 && p[10] == 0xff && p[11] == 0xff) + || (max == 6) + || (max == 7 && p[14] != 0 && p[15] != 1)) + { + n = 12; } - n += ngx_sprint_uchar(&text[n], p[i++], len - n); + *dst++ = ':'; + } - } while (i < 4); + for (i = 0; i < n; i += 2) { - if (len == n) { - text[n] = '\0'; - return n; + if (i == zero) { + *dst++ = ':'; + i += (max - 1) * 2; + continue; + } + + dst = ngx_sprintf(dst, "%uxi", p[i] * 256 + p[i + 1]); + + if (i < 14) { + *dst++ = ':'; + } } - text[n] = '\0'; + if (n == 12) { + dst = ngx_sprintf(dst, "%ud.%ud.%ud.%ud", p[12], p[13], p[14], p[15]); + } - return n; + return dst - text; } +#endif -/* AF_INET only */ ngx_int_t -ngx_ptocidr(ngx_str_t *text, void *cidr) +ngx_ptocidr(ngx_str_t *text, ngx_cidr_t *cidr) { - ngx_int_t m; - ngx_uint_t i; - ngx_inet_cidr_t *in_cidr; + u_char *addr, *mask, *last; + size_t len; + ngx_int_t shift; +#if (NGX_HAVE_INET6) + ngx_int_t rc; + ngx_uint_t s, i; +#endif - in_cidr = cidr; + addr = text->data; + last = addr + text->len; - for (i = 0; i < text->len; i++) { - if (text->data[i] == '/') { - break; + mask = ngx_strlchr(addr, last, '/'); + len = (mask ? mask : last) - addr; + + cidr->u.in.addr = ngx_inet_addr(addr, len); + + if (cidr->u.in.addr != INADDR_NONE) { + cidr->family = AF_INET; + + if (mask == NULL) { + cidr->u.in.mask = 0xffffffff; + return NGX_OK; } - } - if (i == text->len) { +#if (NGX_HAVE_INET6) + } else if (ngx_inet6_addr(addr, len, cidr->u.in6.addr.s6_addr) == NGX_OK) { + cidr->family = AF_INET6; + + if (mask == NULL) { + ngx_memset(cidr->u.in6.mask.s6_addr, 0xff, 16); + return NGX_OK; + } + +#endif + } else { return NGX_ERROR; } - text->data[i] = '\0'; - in_cidr->addr = inet_addr((char *) text->data); - text->data[i] = '/'; - if (in_cidr->addr == INADDR_NONE) { + mask++; + + shift = ngx_atoi(mask, last - mask); + if (shift == NGX_ERROR) { return NGX_ERROR; } - m = ngx_atoi(&text->data[i + 1], text->len - (i + 1)); - if (m == NGX_ERROR) { + switch (cidr->family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + addr = cidr->u.in6.addr.s6_addr; + mask = cidr->u.in6.mask.s6_addr; + rc = NGX_OK; + + for (i = 0; i < 16; i++) { + + s = (shift > 8) ? 8 : shift; + shift -= s; + + mask[i] = (u_char) (0 - (1 << (8 - s))); + + if (addr[i] != (addr[i] & mask[i])) { + rc = NGX_DONE; + addr[i] &= mask[i]; + } + } + + return rc; +#endif + + default: /* AF_INET */ + + if (shift) { + cidr->u.in.mask = htonl((ngx_uint_t) (0 - (1 << (32 - shift)))); + + } else { + /* x86 compilers use a shl instruction that shifts by modulo 32 */ + cidr->u.in.mask = 0; + } + + if (cidr->u.in.addr == (cidr->u.in.addr & cidr->u.in.mask)) { + return NGX_OK; + } + + cidr->u.in.addr &= cidr->u.in.mask; + + return NGX_DONE; + } +} + + +ngx_int_t +ngx_parse_addr(ngx_pool_t *pool, ngx_addr_t *addr, u_char *text, size_t len) +{ + in_addr_t inaddr; + ngx_uint_t family; + struct sockaddr_in *sin; +#if (NGX_HAVE_INET6) + struct in6_addr inaddr6; + struct sockaddr_in6 *sin6; + + /* + * prevent MSVC8 waring: + * potentially uninitialized local variable 'inaddr6' used + */ + ngx_memzero(inaddr6.s6_addr, sizeof(struct in6_addr)); +#endif + + inaddr = ngx_inet_addr(text, len); + + if (inaddr != INADDR_NONE) { + family = AF_INET; + len = sizeof(struct sockaddr_in); + +#if (NGX_HAVE_INET6) + } else if (ngx_inet6_addr(text, len, inaddr6.s6_addr) == NGX_OK) { + family = AF_INET6; + len = sizeof(struct sockaddr_in6); + +#endif + } else { + return NGX_DECLINED; + } + + addr->sockaddr = ngx_pcalloc(pool, len); + if (addr->sockaddr == NULL) { return NGX_ERROR; } - if (m == 0) { + addr->sockaddr->sa_family = (u_char) family; + addr->socklen = len; - /* the x86 compilers use the shl instruction that shifts by modulo 32 */ + switch (family) { - in_cidr->mask = 0; - return NGX_OK; +#if (NGX_HAVE_INET6) + case AF_INET6: + sin6 = (struct sockaddr_in6 *) addr->sockaddr; + ngx_memcpy(sin6->sin6_addr.s6_addr, inaddr6.s6_addr, 16); + break; +#endif + + default: /* AF_INET */ + sin = (struct sockaddr_in *) addr->sockaddr; + sin->sin_addr.s_addr = inaddr; + break; } - in_cidr->mask = htonl((ngx_uint_t) (0 - (1 << (32 - m)))); + return NGX_OK; +} - if (in_cidr->addr == (in_cidr->addr & in_cidr->mask)) { - return NGX_OK; + +ngx_int_t +ngx_parse_url(ngx_pool_t *pool, ngx_url_t *u) +{ + u_char *p; + + p = u->url.data; + + if (ngx_strncasecmp(p, (u_char *) "unix:", 5) == 0) { + return ngx_parse_unix_domain_url(pool, u); + } + + if ((p[0] == ':' || p[0] == '/') && !u->listen) { + u->err = "invalid host"; + return NGX_ERROR; } - in_cidr->addr &= in_cidr->mask; + if (p[0] == '[') { + return ngx_parse_inet6_url(pool, u); + } - return NGX_DONE; + return ngx_parse_inet_url(pool, u); } -ngx_int_t -ngx_parse_url(ngx_conf_t *cf, ngx_url_t *u) +static ngx_int_t +ngx_parse_unix_domain_url(ngx_pool_t *pool, ngx_url_t *u) { - u_char *p, *host, *port_start; - size_t len, port_len; - ngx_int_t port; - ngx_uint_t i; - struct hostent *h; #if (NGX_HAVE_UNIX_DOMAIN) + u_char *path, *uri, *last; + size_t len; struct sockaddr_un *saun; -#endif len = u->url.len; - p = u->url.data; + path = u->url.data; - if (ngx_strncasecmp(p, (u_char *) "unix:", 5) == 0) { + path += 5; + len -= 5; -#if (NGX_HAVE_UNIX_DOMAIN) + if (u->uri_part) { + + last = path + len; + uri = ngx_strlchr(path, last, ':'); - p += 5; - len -= 5; + if (uri) { + len = uri - path; + uri++; + u->uri.len = last - uri; + u->uri.data = uri; + } + } - u->uri.len = len; - u->uri.data = p; + if (len == 0) { + u->err = "no path in the unix domain socket"; + return NGX_ERROR; + } - if (u->uri_part) { - for (i = 0; i < len; i++) { + u->host.len = len++; + u->host.data = path; - if (p[i] == ':') { - len = i; + if (len > sizeof(saun->sun_path)) { + u->err = "too long path in the unix domain socket"; + return NGX_ERROR; + } - u->uri.len -= len + 1; - u->uri.data += len + 1; + u->socklen = sizeof(struct sockaddr_un); + saun = (struct sockaddr_un *) &u->sockaddr; + saun->sun_family = AF_UNIX; + (void) ngx_cpystrn((u_char *) saun->sun_path, path, len); - break; - } - } + u->addrs = ngx_pcalloc(pool, sizeof(ngx_addr_t)); + if (u->addrs == NULL) { + return NGX_ERROR; + } + + saun = ngx_pcalloc(pool, sizeof(struct sockaddr_un)); + if (saun == NULL) { + return NGX_ERROR; + } + + u->family = AF_UNIX; + u->naddrs = 1; + + saun->sun_family = AF_UNIX; + (void) ngx_cpystrn((u_char *) saun->sun_path, path, len); + + u->addrs[0].sockaddr = (struct sockaddr *) saun; + u->addrs[0].socklen = sizeof(struct sockaddr_un); + u->addrs[0].name.len = len + 4; + u->addrs[0].name.data = u->url.data; + + return NGX_OK; + +#else + + u->err = "the unix domain sockets are not supported on this platform"; + + return NGX_ERROR; + +#endif +} + + +static ngx_int_t +ngx_parse_inet_url(ngx_pool_t *pool, ngx_url_t *u) +{ + u_char *p, *host, *port, *last, *uri, *args; + size_t len; + ngx_int_t n; + struct hostent *h; + struct sockaddr_in *sin; + + u->socklen = sizeof(struct sockaddr_in); + sin = (struct sockaddr_in *) &u->sockaddr; + sin->sin_family = AF_INET; + + u->family = AF_INET; + + host = u->url.data; + + last = host + u->url.len; + + port = ngx_strlchr(host, last, ':'); + + uri = ngx_strlchr(host, last, '/'); + + args = ngx_strlchr(host, last, '?'); + + if (args) { + if (uri == NULL) { + uri = args; + + } else if (args < uri) { + uri = args; } + } - if (len == 0) { - u->err = "no path in the unix domain socket"; + if (uri) { + if (u->listen || !u->uri_part) { + u->err = "invalid host"; return NGX_ERROR; } - if (len + 1 > sizeof(saun->sun_path)) { - u->err = "too long path in the unix domain socket"; - return NGX_ERROR; + u->uri.len = last - uri; + u->uri.data = uri; + + last = uri; + + if (uri < port) { + port = NULL; } + } - u->addrs = ngx_pcalloc(cf->pool, sizeof(ngx_peer_addr_t)); - if (u->addrs == NULL) { + if (port) { + port++; + + len = last - port; + + if (len == 0) { + u->err = "invalid port"; return NGX_ERROR; } - saun = ngx_pcalloc(cf->pool, sizeof(struct sockaddr_un)); - if (saun == NULL) { + n = ngx_atoi(port, len); + + if (n < 1 || n > 65536) { + u->err = "invalid port"; return NGX_ERROR; } - u->naddrs = 1; + u->port = (in_port_t) n; + sin->sin_port = htons((in_port_t) n); - saun->sun_family = AF_UNIX; - (void) ngx_cpystrn((u_char *) saun->sun_path, p, len + 1); + u->port_text.len = len; + u->port_text.data = port; - u->addrs[0].sockaddr = (struct sockaddr *) saun; - u->addrs[0].socklen = sizeof(struct sockaddr_un); - u->addrs[0].name.len = len + 5; - u->addrs[0].name.data = u->url.data; + last = port - 1; - u->host.len = len; - u->host.data = p; + } else { + if (uri == NULL) { - u->unix_socket = 1; + if (u->listen) { - return NGX_OK; + /* test value as port only */ -#else - u->err = "the unix domain sockets are not supported on this platform"; + n = ngx_atoi(host, last - host); - return NGX_ERROR; + if (n != NGX_ERROR) { -#endif + if (n < 1 || n > 65536) { + u->err = "invalid port"; + return NGX_ERROR; + } + + u->port = (in_port_t) n; + sin->sin_port = htons((in_port_t) n); + + u->port_text.len = last - host; + u->port_text.data = host; + + u->wildcard = 1; + + return NGX_OK; + } + } + } + + u->no_port = 1; } - if ((p[0] == ':' || p[0] == '/') && !u->listen) { - u->err = "invalid host"; + len = last - host; + + if (len == 0) { + u->err = "no host"; return NGX_ERROR; } - u->host.data = p; + if (len == 1 && *host == '*') { + len = 0; + } - port_start = NULL; - port_len = 0; + u->host.len = len; + u->host.data = host; - for (i = 0; i < len; i++) { + if (u->no_resolve) { + return NGX_OK; + } - if (p[i] == ':') { - port_start = &p[i + 1]; - u->host.len = i; + if (len) { + sin->sin_addr.s_addr = ngx_inet_addr(host, len); - if (!u->uri_part) { - port_len = len - (i + 1); - break; + if (sin->sin_addr.s_addr == INADDR_NONE) { + p = ngx_alloc(++len, pool->log); + if (p == NULL) { + return NGX_ERROR; } - } - if (p[i] == '/') { - u->uri.len = len - i; - u->uri.data = &p[i]; + (void) ngx_cpystrn(p, host, len); - if (u->host.len == 0) { - u->host.len = i; - } + h = gethostbyname((const char *) p); - if (port_start == NULL) { - u->no_port = 1; - goto no_port; - } - - port_len = &p[i] - port_start; + ngx_free(p); - if (port_len == 0) { - u->err = "invalid port"; + if (h == NULL || h->h_addr_list[0] == NULL) { + u->err = "host not found"; return NGX_ERROR; } - break; + sin->sin_addr.s_addr = *(in_addr_t *) (h->h_addr_list[0]); + } + + if (sin->sin_addr.s_addr == INADDR_ANY) { + u->wildcard = 1; } + + } else { + sin->sin_addr.s_addr = INADDR_ANY; + u->wildcard = 1; } - if (port_start) { + if (u->no_port) { + u->port = u->default_port; + sin->sin_port = htons(u->default_port); + } - if (port_len == 0) { - port_len = &p[i] - port_start; + if (u->listen) { + return NGX_OK; + } - if (port_len == 0) { - u->err = "invalid port"; - return NGX_ERROR; - } - } + if (ngx_inet_resolve_host(pool, u) != NGX_OK) { + return NGX_ERROR; + } - port = ngx_atoi(port_start, port_len); + return NGX_OK; +} - if (port == NGX_ERROR || port < 1 || port > 65536) { - u->err = "invalid port"; - return NGX_ERROR; - } - } else { - port = ngx_atoi(p, len); +static ngx_int_t +ngx_parse_inet6_url(ngx_pool_t *pool, ngx_url_t *u) +{ +#if (NGX_HAVE_INET6) + u_char *p, *host, *port, *last, *uri; + size_t len; + ngx_int_t n; + struct sockaddr_in6 *sin6; - if (port == NGX_ERROR) { - u->host.len = len; - u->no_port = 1; + u->socklen = sizeof(struct sockaddr_in6); + sin6 = (struct sockaddr_in6 *) &u->sockaddr; + sin6->sin6_family = AF_INET6; - goto no_port; - } + host = u->url.data + 1; - u->wildcard = 1; + last = u->url.data + u->url.len; + + p = ngx_strlchr(host, last, ']'); + + if (p == NULL) { + u->err = "invalid host"; + return NGX_ERROR; } - u->port = (in_port_t) port; + if (last - p) { -no_port: + port = p + 1; - if (u->listen) { + uri = ngx_strlchr(port, last, '/'); - if (u->port == 0) { - if (u->default_port == 0) { - u->err = "no port"; + if (uri) { + if (u->listen || !u->uri_part) { + u->err = "invalid host"; return NGX_ERROR; } - u->port = u->default_port; - } - - if (u->host.len == 1 && u->host.data[0] == '*') { - u->host.len = 0; + u->uri.len = last - uri; + u->uri.data = uri; } - /* AF_INET only */ - - if (u->host.len) { + if (*port == ':') { + port++; - host = ngx_palloc(cf->temp_pool, u->host.len + 1); - if (host == NULL) { - return NGX_ERROR; - } + len = last - port; - (void) ngx_cpystrn(host, u->host.data, u->host.len + 1); + if (len == 0) { + u->err = "invalid port"; + return NGX_ERROR; + } - u->addr.in_addr = inet_addr((const char *) host); + n = ngx_atoi(port, len); - if (u->addr.in_addr == INADDR_NONE) { - h = gethostbyname((const char *) host); + if (n < 1 || n > 65536) { + u->err = "invalid port"; + return NGX_ERROR; + } - if (h == NULL || h->h_addr_list[0] == NULL) { - u->err = "host not found"; - return NGX_ERROR; - } + u->port = (in_port_t) n; + sin6->sin6_port = htons((in_port_t) n); - u->addr.in_addr = *(in_addr_t *) (h->h_addr_list[0]); - } + u->port_text.len = len; + u->port_text.data = port; } else { - u->addr.in_addr = INADDR_ANY; + u->no_port = 1; } - - return NGX_OK; } - if (u->host.len == 0) { + len = p - host; + + if (len == 0) { u->err = "no host"; return NGX_ERROR; } + u->host.len = len; + u->host.data = host; + + if (ngx_inet6_addr(host, len, sin6->sin6_addr.s6_addr) != NGX_OK) { + u->err = "invalid IPv6 address"; + return NGX_ERROR; + } + + if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { + u->wildcard = 1; + } + + u->family = AF_INET6; + if (u->no_resolve) { return NGX_OK; } if (u->no_port) { u->port = u->default_port; + sin6->sin6_port = htons(u->default_port); } - if (u->port == 0) { - u->err = "no port"; - return NGX_ERROR; - } + return NGX_OK; - if (ngx_inet_resolve_host(cf, u) != NGX_OK) { - return NGX_ERROR; - } +#else - return NGX_OK; + u->err = "the INET6 sockets are not supported on this platform"; + + return NGX_ERROR; + +#endif } ngx_int_t -ngx_inet_resolve_host(ngx_conf_t *cf, ngx_url_t *u) +ngx_inet_resolve_host(ngx_pool_t *pool, ngx_url_t *u) { u_char *p, *host; size_t len; + in_port_t port; in_addr_t in_addr; ngx_uint_t i; struct hostent *h; struct sockaddr_in *sin; - host = ngx_palloc(cf->temp_pool, u->host.len + 1); - if (host == NULL) { - return NGX_ERROR; - } - - (void) ngx_cpystrn(host, u->host.data, u->host.len + 1); - /* AF_INET only */ - in_addr = inet_addr((char *) host); + port = htons(u->port); + + in_addr = ngx_inet_addr(u->host.data, u->host.len); if (in_addr == INADDR_NONE) { + host = ngx_alloc(u->host.len + 1, pool->log); + if (host == NULL) { + return NGX_ERROR; + } + + (void) ngx_cpystrn(host, u->host.data, u->host.len + 1); + h = gethostbyname((char *) host); + ngx_free(host); + if (h == NULL || h->h_addr_list[0] == NULL) { u->err = "host not found"; return NGX_ERROR; @@ -499,7 +936,7 @@ /* MP: ngx_shared_palloc() */ - u->addrs = ngx_pcalloc(cf->pool, i * sizeof(ngx_peer_addr_t)); + u->addrs = ngx_pcalloc(pool, i * sizeof(ngx_addr_t)); if (u->addrs == NULL) { return NGX_ERROR; } @@ -508,28 +945,28 @@ for (i = 0; h->h_addr_list[i] != NULL; i++) { - sin = ngx_pcalloc(cf->pool, sizeof(struct sockaddr_in)); + sin = ngx_pcalloc(pool, sizeof(struct sockaddr_in)); if (sin == NULL) { return NGX_ERROR; } sin->sin_family = AF_INET; - sin->sin_port = htons(u->port); + sin->sin_port = port; sin->sin_addr.s_addr = *(in_addr_t *) (h->h_addr_list[i]); u->addrs[i].sockaddr = (struct sockaddr *) sin; u->addrs[i].socklen = sizeof(struct sockaddr_in); - len = INET_ADDRSTRLEN - 1 + 1 + sizeof(":65536") - 1; + len = NGX_INET_ADDRSTRLEN + sizeof(":65535") - 1; - p = ngx_palloc(cf->pool, len); + p = ngx_pnalloc(pool, len); if (p == NULL) { return NGX_ERROR; } - len = ngx_sock_ntop(AF_INET, (struct sockaddr *) sin, p, len); + len = ngx_sock_ntop((struct sockaddr *) sin, p, len, 1); - u->addrs[i].name.len = ngx_sprintf(&p[len], ":%d", u->port) - p; + u->addrs[i].name.len = len; u->addrs[i].name.data = p; } @@ -537,12 +974,12 @@ /* MP: ngx_shared_palloc() */ - u->addrs = ngx_pcalloc(cf->pool, sizeof(ngx_peer_addr_t)); + u->addrs = ngx_pcalloc(pool, sizeof(ngx_addr_t)); if (u->addrs == NULL) { return NGX_ERROR; } - sin = ngx_pcalloc(cf->pool, sizeof(struct sockaddr_in)); + sin = ngx_pcalloc(pool, sizeof(struct sockaddr_in)); if (sin == NULL) { return NGX_ERROR; } @@ -550,18 +987,19 @@ u->naddrs = 1; sin->sin_family = AF_INET; - sin->sin_port = htons(u->port); + sin->sin_port = port; sin->sin_addr.s_addr = in_addr; u->addrs[0].sockaddr = (struct sockaddr *) sin; u->addrs[0].socklen = sizeof(struct sockaddr_in); - p = ngx_palloc(cf->pool, u->host.len + sizeof(":65536") - 1); + p = ngx_pnalloc(pool, u->host.len + sizeof(":65535") - 1); if (p == NULL) { return NGX_ERROR; } - u->addrs[0].name.len = ngx_sprintf(p, "%V:%d", &u->host, u->port) - p; + u->addrs[0].name.len = ngx_sprintf(p, "%V:%d", + &u->host, ntohs(port)) - p; u->addrs[0].name.data = p; } diff -Nru nginx-0.5.33/src/core/ngx_inet.h nginx-0.8.53/src/core/ngx_inet.h --- nginx-0.5.33/src/core/ngx_inet.h 2006-12-12 16:46:16.000000000 +0000 +++ nginx-0.8.53/src/core/ngx_inet.h 2009-11-03 12:44:55.000000000 +0000 @@ -12,58 +12,108 @@ #include -typedef struct { - in_addr_t addr; - in_addr_t mask; -} ngx_inet_cidr_t; +/* + * TODO: autoconfigure NGX_SOCKADDRLEN and NGX_SOCKADDR_STRLEN as + * sizeof(struct sockaddr_storage) + * sizeof(struct sockaddr_un) + * sizeof(struct sockaddr_in6) + * sizeof(struct sockaddr_in) + */ + +#define NGX_INET_ADDRSTRLEN (sizeof("255.255.255.255") - 1) +#define NGX_INET6_ADDRSTRLEN \ + (sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255") - 1) +#define NGX_UNIX_ADDRSTRLEN \ + (sizeof(struct sockaddr_un) - offsetof(struct sockaddr_un, sun_path)) + +#if (NGX_HAVE_UNIX_DOMAIN) +#define NGX_SOCKADDR_STRLEN (sizeof("unix:") - 1 + NGX_UNIX_ADDRSTRLEN) +#else +#define NGX_SOCKADDR_STRLEN (NGX_INET6_ADDRSTRLEN + sizeof(":65535") - 1) +#endif + +#if (NGX_HAVE_UNIX_DOMAIN) +#define NGX_SOCKADDRLEN sizeof(struct sockaddr_un) +#else +#define NGX_SOCKADDRLEN 512 +#endif + +typedef struct { + in_addr_t addr; + in_addr_t mask; +} ngx_in_cidr_t; -typedef union { - in_addr_t in_addr; -} ngx_url_addr_t; +#if (NGX_HAVE_INET6) typedef struct { - struct sockaddr *sockaddr; - socklen_t socklen; - ngx_str_t name; -} ngx_peer_addr_t; + struct in6_addr addr; + struct in6_addr mask; +} ngx_in6_cidr_t; + +#endif typedef struct { - ngx_int_t type; + ngx_uint_t family; + union { + ngx_in_cidr_t in; +#if (NGX_HAVE_INET6) + ngx_in6_cidr_t in6; +#endif + } u; +} ngx_cidr_t; - ngx_str_t url; - ngx_str_t host; - ngx_str_t uri; - in_port_t port; - in_port_t default_port; +typedef struct { + struct sockaddr *sockaddr; + socklen_t socklen; + ngx_str_t name; +} ngx_addr_t; - unsigned listen:1; - unsigned uri_part:1; - unsigned no_resolve:1; - unsigned one_addr:1; - unsigned wildcard:1; - unsigned no_port:1; - unsigned unix_socket:1; +typedef struct { + ngx_str_t url; + ngx_str_t host; + ngx_str_t port_text; + ngx_str_t uri; + + in_port_t port; + in_port_t default_port; + int family; + + unsigned listen:1; + unsigned uri_part:1; + unsigned no_resolve:1; + unsigned one_addr:1; + + unsigned no_port:1; + unsigned wildcard:1; - ngx_url_addr_t addr; + socklen_t socklen; + u_char sockaddr[NGX_SOCKADDRLEN]; - ngx_peer_addr_t *addrs; - ngx_uint_t naddrs; + ngx_addr_t *addrs; + ngx_uint_t naddrs; - char *err; + char *err; } ngx_url_t; -size_t ngx_sock_ntop(int family, struct sockaddr *sa, u_char *text, size_t len); +in_addr_t ngx_inet_addr(u_char *text, size_t len); +#if (NGX_HAVE_INET6) +ngx_int_t ngx_inet6_addr(u_char *p, size_t len, u_char *addr); +size_t ngx_inet6_ntop(u_char *p, u_char *text, size_t len); +#endif +size_t ngx_sock_ntop(struct sockaddr *sa, u_char *text, size_t len, + ngx_uint_t port); size_t ngx_inet_ntop(int family, void *addr, u_char *text, size_t len); -ngx_int_t ngx_ptocidr(ngx_str_t *text, void *cidr); -ngx_int_t ngx_parse_url(ngx_conf_t *cf, ngx_url_t *u); -ngx_int_t ngx_inet_resolve_host(ngx_conf_t *cf, ngx_url_t *u); - +ngx_int_t ngx_ptocidr(ngx_str_t *text, ngx_cidr_t *cidr); +ngx_int_t ngx_parse_addr(ngx_pool_t *pool, ngx_addr_t *addr, u_char *text, + size_t len); +ngx_int_t ngx_parse_url(ngx_pool_t *pool, ngx_url_t *u); +ngx_int_t ngx_inet_resolve_host(ngx_pool_t *pool, ngx_url_t *u); #endif /* _NGX_INET_H_INCLUDED_ */ diff -Nru nginx-0.5.33/src/core/ngx_log.c nginx-0.8.53/src/core/ngx_log.c --- nginx-0.5.33/src/core/ngx_log.c 2007-03-19 13:36:56.000000000 +0000 +++ nginx-0.8.53/src/core/ngx_log.c 2010-05-14 09:56:37.000000000 +0000 @@ -8,14 +8,14 @@ #include -static char *ngx_set_error_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static char *ngx_error_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static ngx_command_t ngx_errlog_commands[] = { {ngx_string("error_log"), NGX_MAIN_CONF|NGX_CONF_1MORE, - ngx_set_error_log, + ngx_error_log, 0, 0, NULL}, @@ -48,12 +48,20 @@ static ngx_log_t ngx_log; -static ngx_open_file_t ngx_stderr; +static ngx_open_file_t ngx_log_file; +ngx_uint_t ngx_use_stderr = 1; -static const char *err_levels[] = { - "stderr", "emerg", "alert", "crit", "error", - "warn", "notice", "info", "debug" +static ngx_str_t err_levels[] = { + ngx_null_string, + ngx_string("emerg"), + ngx_string("alert"), + ngx_string("crit"), + ngx_string("error"), + ngx_string("warn"), + ngx_string("notice"), + ngx_string("info"), + ngx_string("debug") }; static const char *debug_levels[] = { @@ -79,7 +87,8 @@ #if (NGX_HAVE_VARIADIC_MACROS) va_list args; #endif - u_char errstr[NGX_MAX_ERROR_STR], *p, *last; + u_char *p, *last, *msg; + u_char errstr[NGX_MAX_ERROR_STR]; if (log->file->fd == NGX_INVALID_FILE) { return; @@ -92,60 +101,32 @@ p = errstr + ngx_cached_err_log_time.len; - p = ngx_snprintf(p, last - p, " [%s] ", err_levels[level]); + p = ngx_slprintf(p, last, " [%V] ", &err_levels[level]); /* pid#tid */ - p = ngx_snprintf(p, last - p, "%P#" NGX_TID_T_FMT ": ", + p = ngx_slprintf(p, last, "%P#" NGX_TID_T_FMT ": ", ngx_log_pid, ngx_log_tid); if (log->connection) { - p = ngx_snprintf(p, last - p, "*%uA ", log->connection); + p = ngx_slprintf(p, last, "*%uA ", log->connection); } + msg = p; + #if (NGX_HAVE_VARIADIC_MACROS) va_start(args, fmt); - p = ngx_vsnprintf(p, last - p, fmt, args); + p = ngx_vslprintf(p, last, fmt, args); va_end(args); #else - p = ngx_vsnprintf(p, last - p, fmt, args); + p = ngx_vslprintf(p, last, fmt, args); #endif if (err) { - - if (p > last - 50) { - - /* leave a space for an error code */ - - p = last - 50; - *p++ = '.'; - *p++ = '.'; - *p++ = '.'; - } - -#if (NGX_WIN32) - - if ((unsigned) err >= 0x80000000) { - p = ngx_snprintf(p, last - p, " (%Xd: ", err); - - } else { - p = ngx_snprintf(p, last - p, " (%d: ", err); - } - -#else - - p = ngx_snprintf(p, last - p, " (%d: ", err); - -#endif - - p = ngx_strerror_r(err, p, last - p); - - if (p < last) { - *p++ = ')'; - } + p = ngx_log_errno(p, last, err); } if (level != NGX_LOG_DEBUG && log->handler) { @@ -158,7 +139,20 @@ ngx_linefeed(p); - ngx_write_fd(log->file->fd, errstr, p - errstr); + (void) ngx_write_fd(log->file->fd, errstr, p - errstr); + + if (!ngx_use_stderr + || level > NGX_LOG_WARN + || log->file->fd == ngx_stderr) + { + return; + } + + msg -= (err_levels[level].len + 4); + + (void) ngx_sprintf(msg, "[%V]: ", &err_levels[level]); + + (void) ngx_write_console(ngx_stderr, msg, p - msg); } @@ -191,64 +185,169 @@ #endif -void -ngx_log_abort(ngx_err_t err, const char *text) +void ngx_cdecl +ngx_log_abort(ngx_err_t err, const char *fmt, ...) { - ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, err, text); + u_char *p; + va_list args; + u_char errstr[NGX_MAX_CONF_ERRSTR]; + + va_start(args, fmt); + p = ngx_vsnprintf(errstr, sizeof(errstr) - 1, fmt, args); + va_end(args); + + ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, err, + "%*s", p - errstr, errstr); +} + + +void ngx_cdecl +ngx_log_stderr(ngx_err_t err, const char *fmt, ...) +{ + u_char *p, *last; + va_list args; + u_char errstr[NGX_MAX_ERROR_STR]; + + last = errstr + NGX_MAX_ERROR_STR; + + va_start(args, fmt); + p = ngx_vslprintf(errstr, last, fmt, args); + va_end(args); + + if (err) { + p = ngx_log_errno(p, last, err); + } + + if (p > last - NGX_LINEFEED_SIZE) { + p = last - NGX_LINEFEED_SIZE; + } + + ngx_linefeed(p); + + (void) ngx_write_console(ngx_stderr, errstr, p - errstr); +} + + +u_char * +ngx_log_errno(u_char *buf, u_char *last, ngx_err_t err) +{ + if (buf > last - 50) { + + /* leave a space for an error code */ + + buf = last - 50; + *buf++ = '.'; + *buf++ = '.'; + *buf++ = '.'; + } + +#if (NGX_WIN32) + buf = ngx_slprintf(buf, last, ((unsigned) err < 0x80000000) + ? " (%d: " : " (%Xd: ", err); +#else + buf = ngx_slprintf(buf, last, " (%d: ", err); +#endif + + buf = ngx_strerror_r(err, buf, last - buf); + + if (buf < last) { + *buf++ = ')'; + } + + return buf; } ngx_log_t * -ngx_log_init(void) +ngx_log_init(u_char *prefix) { - ngx_log.file = &ngx_stderr; + u_char *p, *name; + size_t nlen, plen; + + ngx_log.file = &ngx_log_file; ngx_log.log_level = NGX_LOG_NOTICE; -#if (NGX_WIN32) + name = (u_char *) NGX_ERROR_LOG_PATH; - ngx_stderr_fileno = GetStdHandle(STD_ERROR_HANDLE); + /* + * we use ngx_strlen() here since BCC warns about + * condition is always false and unreachable code + */ - ngx_stderr.fd = ngx_open_file(NGX_ERROR_LOG_PATH, NGX_FILE_RDWR, - NGX_FILE_CREATE_OR_OPEN|NGX_FILE_APPEND, 0); + nlen = ngx_strlen(name); - if (ngx_stderr.fd == NGX_INVALID_FILE) { - ngx_message_box("nginx", MB_OK, ngx_errno, - "Could not open error log file: " - ngx_open_file_n " \"" NGX_ERROR_LOG_PATH "\" failed"); - return NULL; + if (nlen == 0) { + ngx_log_file.fd = ngx_stderr; + return &ngx_log; } - if (ngx_file_append_mode(ngx_stderr.fd) == NGX_ERROR) { - ngx_message_box("nginx", MB_OK, ngx_errno, - "Could not open error log file: " - ngx_file_append_mode_n " \"" NGX_ERROR_LOG_PATH - "\" failed"); - return NULL; - } + p = NULL; +#if (NGX_WIN32) + if (name[1] != ':') { #else + if (name[0] != '/') { +#endif + + if (prefix) { + plen = ngx_strlen(prefix); + + } else { +#ifdef NGX_PREFIX + prefix = (u_char *) NGX_PREFIX; + plen = ngx_strlen(prefix); +#else + plen = 0; +#endif + } - ngx_stderr.fd = STDERR_FILENO; + if (plen) { + name = malloc(plen + nlen + 2); + if (name == NULL) { + return NULL; + } + + p = ngx_cpymem(name, prefix, plen); + + if (!ngx_path_separator(*(p - 1))) { + *p++ = '/'; + } + ngx_cpystrn(p, (u_char *) NGX_ERROR_LOG_PATH, nlen + 1); + + p = name; + } + } + + ngx_log_file.fd = ngx_open_file(name, NGX_FILE_APPEND, + NGX_FILE_CREATE_OR_OPEN, + NGX_FILE_DEFAULT_ACCESS); + + if (ngx_log_file.fd == NGX_INVALID_FILE) { + ngx_log_stderr(ngx_errno, + "[alert]: could not open error log file: " + ngx_open_file_n " \"%s\" failed", name); +#if (NGX_WIN32) + ngx_event_log(ngx_errno, + "could not open error log file: " + ngx_open_file_n " \"%s\" failed", name); #endif + ngx_log_file.fd = ngx_stderr; + } + + if (p) { + ngx_free(p); + } + return &ngx_log; } ngx_log_t * -ngx_log_create_errlog(ngx_cycle_t *cycle, ngx_array_t *args) +ngx_log_create(ngx_cycle_t *cycle, ngx_str_t *name) { ngx_log_t *log; - ngx_str_t *value, *name; - - if (args) { - value = args->elts; - name = &value[1]; - - } else { - name = NULL; - } log = ngx_pcalloc(cycle->pool, sizeof(ngx_log_t)); if (log == NULL) { @@ -265,7 +364,7 @@ char * -ngx_set_error_log_levels(ngx_conf_t *cf, ngx_log_t *log) +ngx_log_set_levels(ngx_conf_t *cf, ngx_log_t *log) { ngx_uint_t i, n, d; ngx_str_t *value; @@ -275,12 +374,12 @@ for (i = 2; i < cf->args->nelts; i++) { for (n = 1; n <= NGX_LOG_DEBUG; n++) { - if (ngx_strcmp(value[i].data, err_levels[n]) == 0) { + if (ngx_strcmp(value[i].data, err_levels[n].data) == 0) { if (log->log_level != 0) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "duplicate log level \"%s\"", - value[i].data); + "duplicate log level \"%V\"", + &value[i]); return NGX_CONF_ERROR; } @@ -293,8 +392,8 @@ if (ngx_strcmp(value[i].data, debug_levels[n++]) == 0) { if (log->log_level & ~NGX_LOG_DEBUG_ALL) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "invalid log level \"%s\"", - value[i].data); + "invalid log level \"%V\"", + &value[i]); return NGX_CONF_ERROR; } @@ -305,7 +404,7 @@ if (log->log_level == 0) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "invalid log level \"%s\"", value[i].data); + "invalid log level \"%V\"", &value[i]); return NGX_CONF_ERROR; } } @@ -319,26 +418,34 @@ static char * -ngx_set_error_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +ngx_error_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { - ngx_str_t *value; + ngx_str_t *value, name; + + if (cf->cycle->new_log.file) { + return "is duplicate"; + } value = cf->args->elts; - if (value[1].len == 6 && ngx_strcmp(value[1].data, "stderr") == 0) { - cf->cycle->new_log->file->fd = ngx_stderr.fd; - cf->cycle->new_log->file->name.len = 0; - cf->cycle->new_log->file->name.data = NULL; + if (ngx_strcmp(value[1].data, "stderr") == 0) { + ngx_str_null(&name); } else { - cf->cycle->new_log->file->name = value[1]; + name = value[1]; + } - if (ngx_conf_full_name(cf->cycle, &cf->cycle->new_log->file->name) - == NGX_ERROR) - { - return NGX_CONF_ERROR; - } + cf->cycle->new_log.file = ngx_conf_open_file(cf->cycle, &name); + if (cf->cycle->new_log.file == NULL) { + return NULL; } - return ngx_set_error_log_levels(cf, cf->cycle->new_log); + if (cf->args->nelts == 2) { + cf->cycle->new_log.log_level = NGX_LOG_ERR; + return NGX_CONF_OK; + } + + cf->cycle->new_log.log_level = 0; + + return ngx_log_set_levels(cf, &cf->cycle->new_log); } diff -Nru nginx-0.5.33/src/core/ngx_log.h nginx-0.8.53/src/core/ngx_log.h --- nginx-0.5.33/src/core/ngx_log.h 2007-03-19 13:36:56.000000000 +0000 +++ nginx-0.8.53/src/core/ngx_log.h 2010-07-05 13:02:25.000000000 +0000 @@ -68,35 +68,35 @@ /*********************************/ -#if (NGX_HAVE_GCC_VARIADIC_MACROS) +#if (NGX_HAVE_C99_VARIADIC_MACROS) #define NGX_HAVE_VARIADIC_MACROS 1 -#define ngx_log_error(level, log, args...) \ - if ((log)->log_level >= level) ngx_log_error_core(level, log, args) +#define ngx_log_error(level, log, ...) \ + if ((log)->log_level >= level) ngx_log_error_core(level, log, __VA_ARGS__) void ngx_log_error_core(ngx_uint_t level, ngx_log_t *log, ngx_err_t err, const char *fmt, ...); -#define ngx_log_debug(level, log, args...) \ +#define ngx_log_debug(level, log, ...) \ if ((log)->log_level & level) \ - ngx_log_error_core(NGX_LOG_DEBUG, log, args) + ngx_log_error_core(NGX_LOG_DEBUG, log, __VA_ARGS__) /*********************************/ -#elif (NGX_HAVE_C99_VARIADIC_MACROS) +#elif (NGX_HAVE_GCC_VARIADIC_MACROS) #define NGX_HAVE_VARIADIC_MACROS 1 -#define ngx_log_error(level, log, ...) \ - if ((log)->log_level >= level) ngx_log_error_core(level, log, __VA_ARGS__) +#define ngx_log_error(level, log, args...) \ + if ((log)->log_level >= level) ngx_log_error_core(level, log, args) void ngx_log_error_core(ngx_uint_t level, ngx_log_t *log, ngx_err_t err, const char *fmt, ...); -#define ngx_log_debug(level, log, ...) \ +#define ngx_log_debug(level, log, args...) \ if ((log)->log_level & level) \ - ngx_log_error_core(NGX_LOG_DEBUG, log, __VA_ARGS__) + ngx_log_error_core(NGX_LOG_DEBUG, log, args) /*********************************/ @@ -195,16 +195,16 @@ /*********************************/ -#define ngx_log_alloc_log(pool, log) ngx_palloc(pool, log, sizeof(ngx_log_t)) -#define ngx_log_copy_log(new, old) ngx_memcpy(new, old, sizeof(ngx_log_t)) - -ngx_log_t *ngx_log_init(void); -ngx_log_t *ngx_log_create_errlog(ngx_cycle_t *cycle, ngx_array_t *args); -char *ngx_set_error_log_levels(ngx_conf_t *cf, ngx_log_t *log); -void ngx_log_abort(ngx_err_t err, const char *text); +ngx_log_t *ngx_log_init(u_char *prefix); +ngx_log_t *ngx_log_create(ngx_cycle_t *cycle, ngx_str_t *name); +char *ngx_log_set_levels(ngx_conf_t *cf, ngx_log_t *log); +void ngx_cdecl ngx_log_abort(ngx_err_t err, const char *fmt, ...); +void ngx_cdecl ngx_log_stderr(ngx_err_t err, const char *fmt, ...); +u_char *ngx_log_errno(u_char *buf, u_char *last, ngx_err_t err); extern ngx_module_t ngx_errlog_module; +extern ngx_uint_t ngx_use_stderr; #endif /* _NGX_LOG_H_INCLUDED_ */ diff -Nru nginx-0.5.33/src/core/ngx_md5.h nginx-0.8.53/src/core/ngx_md5.h --- nginx-0.5.33/src/core/ngx_md5.h 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/src/core/ngx_md5.h 2007-10-22 15:22:08.000000000 +0000 @@ -0,0 +1,40 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#ifndef _NGX_MD5_H_INCLUDED_ +#define _NGX_MD5_H_INCLUDED_ + + +#include +#include + + +#if (NGX_HAVE_OPENSSL_MD5_H) +#include +#else +#include +#endif + + +typedef MD5_CTX ngx_md5_t; + + +#if (NGX_OPENSSL_MD5) + +#define ngx_md5_init MD5_Init +#define ngx_md5_update MD5_Update +#define ngx_md5_final MD5_Final + +#else + +#define ngx_md5_init MD5Init +#define ngx_md5_update MD5Update +#define ngx_md5_final MD5Final + +#endif + + +#endif /* _NGX_MD5_H_INCLUDED_ */ diff -Nru nginx-0.5.33/src/core/ngx_open_file_cache.c nginx-0.8.53/src/core/ngx_open_file_cache.c --- nginx-0.5.33/src/core/ngx_open_file_cache.c 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/src/core/ngx_open_file_cache.c 2010-04-21 15:59:36.000000000 +0000 @@ -0,0 +1,886 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include + + +/* + * open file cache caches + * open file handles with stat() info; + * directories stat() info; + * files and directories errors: not found, access denied, etc. + */ + + +#define NGX_MIN_READ_AHEAD (128 * 1024) + + +static void ngx_open_file_cache_cleanup(void *data); +static ngx_int_t ngx_open_and_stat_file(u_char *name, ngx_open_file_info_t *of, + ngx_log_t *log); +static void ngx_open_file_add_event(ngx_open_file_cache_t *cache, + ngx_cached_open_file_t *file, ngx_open_file_info_t *of, ngx_log_t *log); +static void ngx_open_file_cleanup(void *data); +static void ngx_close_cached_file(ngx_open_file_cache_t *cache, + ngx_cached_open_file_t *file, ngx_uint_t min_uses, ngx_log_t *log); +static void ngx_open_file_del_event(ngx_cached_open_file_t *file); +static void ngx_expire_old_cached_files(ngx_open_file_cache_t *cache, + ngx_uint_t n, ngx_log_t *log); +static void ngx_open_file_cache_rbtree_insert_value(ngx_rbtree_node_t *temp, + ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel); +static ngx_cached_open_file_t * + ngx_open_file_lookup(ngx_open_file_cache_t *cache, ngx_str_t *name, + uint32_t hash); +static void ngx_open_file_cache_remove(ngx_event_t *ev); + + +ngx_open_file_cache_t * +ngx_open_file_cache_init(ngx_pool_t *pool, ngx_uint_t max, time_t inactive) +{ + ngx_pool_cleanup_t *cln; + ngx_open_file_cache_t *cache; + + cache = ngx_palloc(pool, sizeof(ngx_open_file_cache_t)); + if (cache == NULL) { + return NULL; + } + + ngx_rbtree_init(&cache->rbtree, &cache->sentinel, + ngx_open_file_cache_rbtree_insert_value); + + ngx_queue_init(&cache->expire_queue); + + cache->current = 0; + cache->max = max; + cache->inactive = inactive; + + cln = ngx_pool_cleanup_add(pool, 0); + if (cln == NULL) { + return NULL; + } + + cln->handler = ngx_open_file_cache_cleanup; + cln->data = cache; + + return cache; +} + + +static void +ngx_open_file_cache_cleanup(void *data) +{ + ngx_open_file_cache_t *cache = data; + + ngx_queue_t *q; + ngx_cached_open_file_t *file; + + ngx_log_debug0(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0, + "open file cache cleanup"); + + for ( ;; ) { + + if (ngx_queue_empty(&cache->expire_queue)) { + break; + } + + q = ngx_queue_last(&cache->expire_queue); + + file = ngx_queue_data(q, ngx_cached_open_file_t, queue); + + ngx_queue_remove(q); + + ngx_rbtree_delete(&cache->rbtree, &file->node); + + cache->current--; + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0, + "delete cached open file: %s", file->name); + + if (!file->err && !file->is_dir) { + file->close = 1; + file->count = 0; + ngx_close_cached_file(cache, file, 0, ngx_cycle->log); + + } else { + ngx_free(file->name); + ngx_free(file); + } + } + + if (cache->current) { + ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, + "%d items still leave in open file cache", + cache->current); + } + + if (cache->rbtree.root != cache->rbtree.sentinel) { + ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, + "rbtree still is not empty in open file cache"); + + } +} + + +ngx_int_t +ngx_open_cached_file(ngx_open_file_cache_t *cache, ngx_str_t *name, + ngx_open_file_info_t *of, ngx_pool_t *pool) +{ + time_t now; + uint32_t hash; + ngx_int_t rc; + ngx_file_info_t fi; + ngx_pool_cleanup_t *cln; + ngx_cached_open_file_t *file; + ngx_pool_cleanup_file_t *clnf; + ngx_open_file_cache_cleanup_t *ofcln; + + of->fd = NGX_INVALID_FILE; + of->err = 0; + + if (cache == NULL) { + + if (of->test_only) { + + if (ngx_file_info(name->data, &fi) == NGX_FILE_ERROR) { + of->err = ngx_errno; + of->failed = ngx_file_info_n; + return NGX_ERROR; + } + + of->uniq = ngx_file_uniq(&fi); + of->mtime = ngx_file_mtime(&fi); + of->size = ngx_file_size(&fi); + of->is_dir = ngx_is_dir(&fi); + of->is_file = ngx_is_file(&fi); + of->is_link = ngx_is_link(&fi); + of->is_exec = ngx_is_exec(&fi); + + return NGX_OK; + } + + cln = ngx_pool_cleanup_add(pool, sizeof(ngx_pool_cleanup_file_t)); + if (cln == NULL) { + return NGX_ERROR; + } + + rc = ngx_open_and_stat_file(name->data, of, pool->log); + + if (rc == NGX_OK && !of->is_dir) { + cln->handler = ngx_pool_cleanup_file; + clnf = cln->data; + + clnf->fd = of->fd; + clnf->name = name->data; + clnf->log = pool->log; + } + + return rc; + } + + cln = ngx_pool_cleanup_add(pool, sizeof(ngx_open_file_cache_cleanup_t)); + if (cln == NULL) { + return NGX_ERROR; + } + + now = ngx_time(); + + hash = ngx_crc32_long(name->data, name->len); + + file = ngx_open_file_lookup(cache, name, hash); + + if (file) { + + file->uses++; + + ngx_queue_remove(&file->queue); + + if (file->fd == NGX_INVALID_FILE && file->err == 0 && !file->is_dir) { + + /* file was not used often enough to keep open */ + + rc = ngx_open_and_stat_file(name->data, of, pool->log); + + if (rc != NGX_OK && (of->err == 0 || !of->errors)) { + goto failed; + } + + goto add_event; + } + + if (file->use_event + || (file->event == NULL + && (of->uniq == 0 || of->uniq == file->uniq) + && now - file->created < of->valid)) + { + if (file->err == 0) { + + of->fd = file->fd; + of->uniq = file->uniq; + of->mtime = file->mtime; + of->size = file->size; + + of->is_dir = file->is_dir; + of->is_file = file->is_file; + of->is_link = file->is_link; + of->is_exec = file->is_exec; + of->is_directio = file->is_directio; + + if (!file->is_dir) { + file->count++; + ngx_open_file_add_event(cache, file, of, pool->log); + } + + } else { + of->err = file->err; + of->failed = ngx_open_file_n; + } + + goto found; + } + + ngx_log_debug4(NGX_LOG_DEBUG_CORE, pool->log, 0, + "retest open file: %s, fd:%d, c:%d, e:%d", + file->name, file->fd, file->count, file->err); + + if (file->is_dir) { + + /* + * chances that directory became file are very small + * so test_dir flag allows to use a single syscall + * in ngx_file_info() instead of three syscalls + */ + + of->test_dir = 1; + } + + of->fd = file->fd; + of->uniq = file->uniq; + + rc = ngx_open_and_stat_file(name->data, of, pool->log); + + if (rc != NGX_OK && (of->err == 0 || !of->errors)) { + goto failed; + } + + if (of->is_dir) { + + if (file->is_dir || file->err) { + goto update; + } + + /* file became directory */ + + } else if (of->err == 0) { /* file */ + + if (file->is_dir || file->err) { + goto add_event; + } + + if (of->uniq == file->uniq) { + + file->count++; + + if (file->event) { + file->use_event = 1; + } + + goto renew; + } + + /* file was changed */ + + } else { /* error to cache */ + + if (file->err || file->is_dir) { + goto update; + } + + /* file was removed, etc. */ + } + + if (file->count == 0) { + + ngx_open_file_del_event(file); + + if (ngx_close_file(file->fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, pool->log, ngx_errno, + ngx_close_file_n " \"%s\" failed", + name->data); + } + + goto add_event; + } + + ngx_rbtree_delete(&cache->rbtree, &file->node); + + cache->current--; + + file->close = 1; + + goto create; + } + + /* not found */ + + rc = ngx_open_and_stat_file(name->data, of, pool->log); + + if (rc != NGX_OK && (of->err == 0 || !of->errors)) { + goto failed; + } + +create: + + if (cache->current >= cache->max) { + ngx_expire_old_cached_files(cache, 0, pool->log); + } + + file = ngx_alloc(sizeof(ngx_cached_open_file_t), pool->log); + + if (file == NULL) { + goto failed; + } + + file->name = ngx_alloc(name->len + 1, pool->log); + + if (file->name == NULL) { + ngx_free(file); + file = NULL; + goto failed; + } + + ngx_cpystrn(file->name, name->data, name->len + 1); + + file->node.key = hash; + + ngx_rbtree_insert(&cache->rbtree, &file->node); + + cache->current++; + + file->uses = 1; + file->count = 0; + file->use_event = 0; + file->event = NULL; + +add_event: + + ngx_open_file_add_event(cache, file, of, pool->log); + +update: + + file->fd = of->fd; + file->err = of->err; + + if (of->err == 0) { + file->uniq = of->uniq; + file->mtime = of->mtime; + file->size = of->size; + + file->close = 0; + + file->is_dir = of->is_dir; + file->is_file = of->is_file; + file->is_link = of->is_link; + file->is_exec = of->is_exec; + file->is_directio = of->is_directio; + + if (!of->is_dir) { + file->count++; + } + } + +renew: + + file->created = now; + +found: + + file->accessed = now; + + ngx_queue_insert_head(&cache->expire_queue, &file->queue); + + ngx_log_debug5(NGX_LOG_DEBUG_CORE, pool->log, 0, + "cached open file: %s, fd:%d, c:%d, e:%d, u:%d", + file->name, file->fd, file->count, file->err, file->uses); + + if (of->err == 0) { + + if (!of->is_dir) { + cln->handler = ngx_open_file_cleanup; + ofcln = cln->data; + + ofcln->cache = cache; + ofcln->file = file; + ofcln->min_uses = of->min_uses; + ofcln->log = pool->log; + } + + return NGX_OK; + } + + return NGX_ERROR; + +failed: + + if (file) { + ngx_rbtree_delete(&cache->rbtree, &file->node); + + cache->current--; + + if (file->count == 0) { + + if (file->fd != NGX_INVALID_FILE) { + if (ngx_close_file(file->fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, pool->log, ngx_errno, + ngx_close_file_n " \"%s\" failed", + file->name); + } + } + + ngx_free(file->name); + ngx_free(file); + + } else { + file->close = 1; + } + } + + if (of->fd != NGX_INVALID_FILE) { + if (ngx_close_file(of->fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, pool->log, ngx_errno, + ngx_close_file_n " \"%s\" failed", name->data); + } + } + + return NGX_ERROR; +} + + +static ngx_int_t +ngx_open_and_stat_file(u_char *name, ngx_open_file_info_t *of, ngx_log_t *log) +{ + ngx_fd_t fd; + ngx_file_info_t fi; + + if (of->fd != NGX_INVALID_FILE) { + + if (ngx_file_info(name, &fi) == NGX_FILE_ERROR) { + of->failed = ngx_file_info_n; + goto failed; + } + + if (of->uniq == ngx_file_uniq(&fi)) { + goto done; + } + + } else if (of->test_dir) { + + if (ngx_file_info(name, &fi) == NGX_FILE_ERROR) { + of->failed = ngx_file_info_n; + goto failed; + } + + if (ngx_is_dir(&fi)) { + goto done; + } + } + + if (!of->log) { + + /* + * Use non-blocking open() not to hang on FIFO files, etc. + * This flag has no effect on a regular files. + */ + + fd = ngx_open_file(name, NGX_FILE_RDONLY|NGX_FILE_NONBLOCK, + NGX_FILE_OPEN, 0); + + } else { + fd = ngx_open_file(name, NGX_FILE_APPEND, NGX_FILE_CREATE_OR_OPEN, + NGX_FILE_DEFAULT_ACCESS); + } + + if (fd == NGX_INVALID_FILE) { + of->failed = ngx_open_file_n; + goto failed; + } + + if (ngx_fd_info(fd, &fi) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_CRIT, log, ngx_errno, + ngx_fd_info_n " \"%s\" failed", name); + + if (ngx_close_file(fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + ngx_close_file_n " \"%s\" failed", name); + } + + of->fd = NGX_INVALID_FILE; + + return NGX_ERROR; + } + + if (ngx_is_dir(&fi)) { + if (ngx_close_file(fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + ngx_close_file_n " \"%s\" failed", name); + } + + of->fd = NGX_INVALID_FILE; + + } else { + of->fd = fd; + + if (of->read_ahead && ngx_file_size(&fi) > NGX_MIN_READ_AHEAD) { + if (ngx_read_ahead(fd, of->read_ahead) == NGX_ERROR) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + ngx_read_ahead_n " \"%s\" failed", name); + } + } + + if (of->directio <= ngx_file_size(&fi)) { + if (ngx_directio_on(fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + ngx_directio_on_n " \"%s\" failed", name); + + } else { + of->is_directio = 1; + } + } + } + +done: + + of->uniq = ngx_file_uniq(&fi); + of->mtime = ngx_file_mtime(&fi); + of->size = ngx_file_size(&fi); + of->is_dir = ngx_is_dir(&fi); + of->is_file = ngx_is_file(&fi); + of->is_link = ngx_is_link(&fi); + of->is_exec = ngx_is_exec(&fi); + + return NGX_OK; + +failed: + + of->fd = NGX_INVALID_FILE; + of->err = ngx_errno; + + return NGX_ERROR; +} + + +/* + * we ignore any possible event setting error and + * fallback to usual periodic file retests + */ + +static void +ngx_open_file_add_event(ngx_open_file_cache_t *cache, + ngx_cached_open_file_t *file, ngx_open_file_info_t *of, ngx_log_t *log) +{ + ngx_open_file_cache_event_t *fev; + + if (!(ngx_event_flags & NGX_USE_VNODE_EVENT) + || !of->events + || file->event + || of->fd == NGX_INVALID_FILE + || file->uses < of->min_uses) + { + return; + } + + file->use_event = 0; + + file->event = ngx_calloc(sizeof(ngx_event_t), log); + if (file->event== NULL) { + return; + } + + fev = ngx_alloc(sizeof(ngx_open_file_cache_event_t), log); + if (fev == NULL) { + ngx_free(file->event); + file->event = NULL; + return; + } + + fev->fd = of->fd; + fev->file = file; + fev->cache = cache; + + file->event->handler = ngx_open_file_cache_remove; + file->event->data = fev; + + /* + * although vnode event may be called while ngx_cycle->poll + * destruction, however, cleanup procedures are run before any + * memory freeing and events will be canceled. + */ + + file->event->log = ngx_cycle->log; + + if (ngx_add_event(file->event, NGX_VNODE_EVENT, NGX_ONESHOT_EVENT) + != NGX_OK) + { + ngx_free(file->event->data); + ngx_free(file->event); + file->event = NULL; + return; + } + + /* + * we do not set file->use_event here because there may be a race + * condition: a file may be deleted between opening the file and + * adding event, so we rely upon event notification only after + * one file revalidation on next file access + */ + + return; +} + + +static void +ngx_open_file_cleanup(void *data) +{ + ngx_open_file_cache_cleanup_t *c = data; + + c->file->count--; + + ngx_close_cached_file(c->cache, c->file, c->min_uses, c->log); + + /* drop one or two expired open files */ + ngx_expire_old_cached_files(c->cache, 1, c->log); +} + + +static void +ngx_close_cached_file(ngx_open_file_cache_t *cache, + ngx_cached_open_file_t *file, ngx_uint_t min_uses, ngx_log_t *log) +{ + ngx_log_debug5(NGX_LOG_DEBUG_CORE, log, 0, + "close cached open file: %s, fd:%d, c:%d, u:%d, %d", + file->name, file->fd, file->count, file->uses, file->close); + + if (!file->close) { + + file->accessed = ngx_time(); + + ngx_queue_remove(&file->queue); + + ngx_queue_insert_head(&cache->expire_queue, &file->queue); + + if (file->uses >= min_uses || file->count) { + return; + } + } + + ngx_open_file_del_event(file); + + if (file->count) { + return; + } + + if (file->fd != NGX_INVALID_FILE) { + + if (ngx_close_file(file->fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + ngx_close_file_n " \"%s\" failed", file->name); + } + + file->fd = NGX_INVALID_FILE; + } + + if (!file->close) { + return; + } + + ngx_free(file->name); + ngx_free(file); +} + + +static void +ngx_open_file_del_event(ngx_cached_open_file_t *file) +{ + if (file->event == NULL) { + return; + } + + (void) ngx_del_event(file->event, NGX_VNODE_EVENT, + file->count ? NGX_FLUSH_EVENT : NGX_CLOSE_EVENT); + + ngx_free(file->event->data); + ngx_free(file->event); + file->event = NULL; + file->use_event = 0; +} + + +static void +ngx_expire_old_cached_files(ngx_open_file_cache_t *cache, ngx_uint_t n, + ngx_log_t *log) +{ + time_t now; + ngx_queue_t *q; + ngx_cached_open_file_t *file; + + now = ngx_time(); + + /* + * n == 1 deletes one or two inactive files + * n == 0 deletes least recently used file by force + * and one or two inactive files + */ + + while (n < 3) { + + if (ngx_queue_empty(&cache->expire_queue)) { + return; + } + + q = ngx_queue_last(&cache->expire_queue); + + file = ngx_queue_data(q, ngx_cached_open_file_t, queue); + + if (n++ != 0 && now - file->accessed <= cache->inactive) { + return; + } + + ngx_queue_remove(q); + + ngx_rbtree_delete(&cache->rbtree, &file->node); + + cache->current--; + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, log, 0, + "expire cached open file: %s", file->name); + + if (!file->err && !file->is_dir) { + file->close = 1; + ngx_close_cached_file(cache, file, 0, log); + + } else { + ngx_free(file->name); + ngx_free(file); + } + } +} + + +static void +ngx_open_file_cache_rbtree_insert_value(ngx_rbtree_node_t *temp, + ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel) +{ + ngx_rbtree_node_t **p; + ngx_cached_open_file_t *file, *file_temp; + + for ( ;; ) { + + if (node->key < temp->key) { + + p = &temp->left; + + } else if (node->key > temp->key) { + + p = &temp->right; + + } else { /* node->key == temp->key */ + + file = (ngx_cached_open_file_t *) node; + file_temp = (ngx_cached_open_file_t *) temp; + + p = (ngx_strcmp(file->name, file_temp->name) < 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_cached_open_file_t * +ngx_open_file_lookup(ngx_open_file_cache_t *cache, ngx_str_t *name, + uint32_t hash) +{ + ngx_int_t rc; + ngx_rbtree_node_t *node, *sentinel; + ngx_cached_open_file_t *file; + + node = cache->rbtree.root; + sentinel = cache->rbtree.sentinel; + + while (node != sentinel) { + + if (hash < node->key) { + node = node->left; + continue; + } + + if (hash > node->key) { + node = node->right; + continue; + } + + /* hash == node->key */ + + do { + file = (ngx_cached_open_file_t *) node; + + rc = ngx_strcmp(name->data, file->name); + + if (rc == 0) { + return file; + } + + node = (rc < 0) ? node->left : node->right; + + } while (node != sentinel && hash == node->key); + + break; + } + + return NULL; +} + + +static void +ngx_open_file_cache_remove(ngx_event_t *ev) +{ + ngx_cached_open_file_t *file; + ngx_open_file_cache_event_t *fev; + + fev = ev->data; + file = fev->file; + + ngx_queue_remove(&file->queue); + + ngx_rbtree_delete(&fev->cache->rbtree, &file->node); + + fev->cache->current--; + + /* NGX_ONESHOT_EVENT was already deleted */ + file->event = NULL; + file->use_event = 0; + + file->close = 1; + + ngx_close_cached_file(fev->cache, file, 0, ev->log); + + /* free memory only when fev->cache and fev->file are already not needed */ + + ngx_free(ev->data); + ngx_free(ev); +} diff -Nru nginx-0.5.33/src/core/ngx_open_file_cache.h nginx-0.8.53/src/core/ngx_open_file_cache.h --- nginx-0.5.33/src/core/ngx_open_file_cache.h 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/src/core/ngx_open_file_cache.h 2009-09-30 13:21:52.000000000 +0000 @@ -0,0 +1,117 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include + + +#ifndef _NGX_OPEN_FILE_CACHE_H_INCLUDED_ +#define _NGX_OPEN_FILE_CACHE_H_INCLUDED_ + + +#define NGX_OPEN_FILE_DIRECTIO_OFF NGX_MAX_OFF_T_VALUE + + +typedef struct { + ngx_fd_t fd; + ngx_file_uniq_t uniq; + time_t mtime; + off_t size; + off_t directio; + size_t read_ahead; + + ngx_err_t err; + char *failed; + + time_t valid; + + ngx_uint_t min_uses; + + unsigned test_dir:1; + unsigned test_only:1; + unsigned log:1; + unsigned errors:1; + unsigned events:1; + + unsigned is_dir:1; + unsigned is_file:1; + unsigned is_link:1; + unsigned is_exec:1; + unsigned is_directio:1; +} ngx_open_file_info_t; + + +typedef struct ngx_cached_open_file_s ngx_cached_open_file_t; + +struct ngx_cached_open_file_s { + ngx_rbtree_node_t node; + ngx_queue_t queue; + + u_char *name; + time_t created; + time_t accessed; + + ngx_fd_t fd; + ngx_file_uniq_t uniq; + time_t mtime; + off_t size; + ngx_err_t err; + + uint32_t uses; + + unsigned count:24; + unsigned close:1; + unsigned use_event:1; + + unsigned is_dir:1; + unsigned is_file:1; + unsigned is_link:1; + unsigned is_exec:1; + unsigned is_directio:1; + + ngx_event_t *event; +}; + + +typedef struct { + ngx_rbtree_t rbtree; + ngx_rbtree_node_t sentinel; + ngx_queue_t expire_queue; + + ngx_uint_t current; + ngx_uint_t max; + time_t inactive; +} ngx_open_file_cache_t; + + +typedef struct { + ngx_open_file_cache_t *cache; + ngx_cached_open_file_t *file; + ngx_uint_t min_uses; + ngx_log_t *log; +} ngx_open_file_cache_cleanup_t; + + +typedef struct { + + /* ngx_connection_t stub to allow use c->fd as event ident */ + void *data; + ngx_event_t *read; + ngx_event_t *write; + ngx_fd_t fd; + + ngx_cached_open_file_t *file; + ngx_open_file_cache_t *cache; +} ngx_open_file_cache_event_t; + + +ngx_open_file_cache_t *ngx_open_file_cache_init(ngx_pool_t *pool, + ngx_uint_t max, time_t inactive); +ngx_int_t ngx_open_cached_file(ngx_open_file_cache_t *cache, ngx_str_t *name, + ngx_open_file_info_t *of, ngx_pool_t *pool); + + +#endif /* _NGX_OPEN_FILE_CACHE_H_INCLUDED_ */ diff -Nru nginx-0.5.33/src/core/ngx_output_chain.c nginx-0.8.53/src/core/ngx_output_chain.c --- nginx-0.5.33/src/core/ngx_output_chain.c 2007-06-06 05:56:51.000000000 +0000 +++ nginx-0.8.53/src/core/ngx_output_chain.c 2010-10-12 12:06:52.000000000 +0000 @@ -13,25 +13,35 @@ #define NGX_SENDFILE_LIMIT 4096 #endif +/* + * When DIRECTIO is enabled FreeBSD, Solaris, and MacOSX read directly + * to an application memory from a device if parameters are aligned + * to device sector boundary (512 bytes). They fallback to usual read + * operation if the parameters are not aligned. + * Linux allows DIRECTIO only if the parameters are aligned to a filesystem + * sector boundary, otherwise it returns EINVAL. The sector size is + * usually 512 bytes, however, on XFS it may be 4096 bytes. + */ #define NGX_NONE 1 static ngx_inline ngx_int_t - ngx_output_chain_need_to_copy(ngx_output_chain_ctx_t *ctx, ngx_buf_t *buf); + ngx_output_chain_as_is(ngx_output_chain_ctx_t *ctx, ngx_buf_t *buf); static ngx_int_t ngx_output_chain_add_copy(ngx_pool_t *pool, ngx_chain_t **chain, ngx_chain_t *in); -static ngx_int_t ngx_output_chain_copy_buf(ngx_buf_t *dst, ngx_buf_t *src, - ngx_uint_t sendfile); +static ngx_int_t ngx_output_chain_align_file_buf(ngx_output_chain_ctx_t *ctx, + off_t bsize); +static ngx_int_t ngx_output_chain_get_buf(ngx_output_chain_ctx_t *ctx, + off_t bsize); +static ngx_int_t ngx_output_chain_copy_buf(ngx_output_chain_ctx_t *ctx); ngx_int_t ngx_output_chain(ngx_output_chain_ctx_t *ctx, ngx_chain_t *in) { off_t bsize; - size_t size; ngx_int_t rc, last; - ngx_uint_t recycled; ngx_chain_t *cl, *out, **last_out; if (ctx->in == NULL && ctx->busy == NULL) { @@ -50,7 +60,7 @@ #if (NGX_SENDFILE_LIMIT) && !(in->buf->in_file && in->buf->file_last > NGX_SENDFILE_LIMIT) #endif - && !ngx_output_chain_need_to_copy(ctx, in->buf)) + && ngx_output_chain_as_is(ctx, in->buf)) { return ctx->output_filter(ctx->filter_ctx, in); } @@ -70,11 +80,17 @@ for ( ;; ) { +#if (NGX_HAVE_FILE_AIO) + if (ctx->aio) { + return NGX_AGAIN; + } +#endif + while (ctx->in) { /* * cycle while there are the ctx->in bufs - * or there are the free output bufs to copy in + * and there are the free output bufs to copy in */ bsize = ngx_buf_size(ctx->in->buf); @@ -101,7 +117,7 @@ continue; } - if (!ngx_output_chain_need_to_copy(ctx, ctx->in->buf)) { + if (ngx_output_chain_as_is(ctx, ctx->in->buf)) { /* move the chain link to the output chain */ @@ -117,63 +133,35 @@ if (ctx->buf == NULL) { - /* get the free buf */ + rc = ngx_output_chain_align_file_buf(ctx, bsize); - if (ctx->free) { - cl = ctx->free; - ctx->buf = cl->buf; - ctx->free = cl->next; - ngx_free_chain(ctx->pool, cl); + if (rc == NGX_ERROR) { + return NGX_ERROR; + } - } else if (out || ctx->allocated == ctx->bufs.num) { + if (rc != NGX_OK) { - break; + if (ctx->free) { - } else { + /* get the free buf */ - size = ctx->bufs.size; - recycled = 1; + cl = ctx->free; + ctx->buf = cl->buf; + ctx->free = cl->next; - if (ctx->in->buf->last_in_chain) { + ngx_free_chain(ctx->pool, cl); - if (bsize < (off_t) ctx->bufs.size) { + } else if (out || ctx->allocated == ctx->bufs.num) { - /* - * allocate small temp buf for the small last buf - * or its small last part - */ - - size = (size_t) bsize; - recycled = 0; - - } else if (ctx->bufs.num == 1 - && (bsize < (off_t) (ctx->bufs.size - + (ctx->bufs.size >> 2)))) - { - /* - * allocate a temp buf that equals - * to the last buf if the last buf size is lesser - * than 1.25 of bufs.size and a temp buf is single - */ - - size = (size_t) bsize; - recycled = 0; - } - } + break; - ctx->buf = ngx_create_temp_buf(ctx->pool, size); - if (ctx->buf == NULL) { + } else if (ngx_output_chain_get_buf(ctx, bsize) != NGX_OK) { return NGX_ERROR; } - - ctx->buf->tag = ctx->tag; - ctx->buf->recycled = recycled; - ctx->allocated++; } } - rc = ngx_output_chain_copy_buf(ctx->buf, ctx->in->buf, - ctx->sendfile); + rc = ngx_output_chain_copy_buf(ctx); if (rc == NGX_ERROR) { return rc; @@ -227,11 +215,15 @@ static ngx_inline ngx_int_t -ngx_output_chain_need_to_copy(ngx_output_chain_ctx_t *ctx, ngx_buf_t *buf) +ngx_output_chain_as_is(ngx_output_chain_ctx_t *ctx, ngx_buf_t *buf) { ngx_uint_t sendfile; if (ngx_buf_special(buf)) { + return 1; + } + + if (buf->in_file && buf->file->directio) { return 0; } @@ -248,21 +240,21 @@ if (!sendfile) { if (!ngx_buf_in_memory(buf)) { - return 1; + return 0; } buf->in_file = 0; } if (ctx->need_in_memory && !ngx_buf_in_memory(buf)) { - return 1; + return 0; } if (ctx->need_in_temp && (buf->memory || buf->mmap)) { - return 1; + return 0; } - return 0; + return 1; } @@ -296,6 +288,8 @@ && buf->file_pos < NGX_SENDFILE_LIMIT && buf->file_last > NGX_SENDFILE_LIMIT) { + /* split a file buf on two bufs by the sendfile limit */ + b = ngx_calloc_buf(pool); if (b == NULL) { return NGX_ERROR; @@ -324,28 +318,157 @@ #endif + cl->next = NULL; *ll = cl; ll = &cl->next; } - *ll = NULL; + return NGX_OK; +} + + +static ngx_int_t +ngx_output_chain_align_file_buf(ngx_output_chain_ctx_t *ctx, off_t bsize) +{ + size_t size; + ngx_buf_t *in; + + in = ctx->in->buf; + + if (in->file == NULL || !in->file->directio) { + return NGX_DECLINED; + } + + ctx->directio = 1; + + size = (size_t) (in->file_pos - (in->file_pos & ~(ctx->alignment - 1))); + + if (size == 0) { + + if (bsize >= (off_t) ctx->bufs.size) { + return NGX_DECLINED; + } + + size = (size_t) bsize; + + } else { + size = (size_t) ctx->alignment - size; + + if ((off_t) size > bsize) { + size = (size_t) bsize; + } + } + + ctx->buf = ngx_create_temp_buf(ctx->pool, size); + if (ctx->buf == NULL) { + return NGX_ERROR; + } + + /* + * we do not set ctx->buf->tag, because we do not want + * to reuse the buf via ctx->free list + */ + +#if (NGX_HAVE_ALIGNED_DIRECTIO) + ctx->unaligned = 1; +#endif return NGX_OK; } static ngx_int_t -ngx_output_chain_copy_buf(ngx_buf_t *dst, ngx_buf_t *src, ngx_uint_t sendfile) +ngx_output_chain_get_buf(ngx_output_chain_ctx_t *ctx, off_t bsize) { - off_t size; - ssize_t n; + size_t size; + ngx_buf_t *b, *in; + ngx_uint_t recycled; - size = ngx_buf_size(src); + in = ctx->in->buf; + size = ctx->bufs.size; + recycled = 1; + + if (in->last_in_chain) { - if (size > dst->end - dst->pos) { - size = dst->end - dst->pos; + if (bsize < (off_t) size) { + + /* + * allocate a small temp buf for a small last buf + * or its small last part + */ + + size = (size_t) bsize; + recycled = 0; + + } else if (!ctx->directio + && ctx->bufs.num == 1 + && (bsize < (off_t) (size + size / 4))) + { + /* + * allocate a temp buf that equals to a last buf, + * if there is no directio, the last buf size is lesser + * than 1.25 of bufs.size and the temp buf is single + */ + + size = (size_t) bsize; + recycled = 0; + } } + b = ngx_calloc_buf(ctx->pool); + if (b == NULL) { + return NGX_ERROR; + } + + if (ctx->directio) { + + /* + * allocate block aligned to a disk sector size to enable + * userland buffer direct usage conjunctly with directio + */ + + b->start = ngx_pmemalign(ctx->pool, size, (size_t) ctx->alignment); + if (b->start == NULL) { + return NGX_ERROR; + } + + } else { + b->start = ngx_palloc(ctx->pool, size); + if (b->start == NULL) { + return NGX_ERROR; + } + } + + b->pos = b->start; + b->last = b->start; + b->end = b->last + size; + b->temporary = 1; + b->tag = ctx->tag; + b->recycled = recycled; + + ctx->buf = b; + ctx->allocated++; + + return NGX_OK; +} + + +static ngx_int_t +ngx_output_chain_copy_buf(ngx_output_chain_ctx_t *ctx) +{ + off_t size; + ssize_t n; + ngx_buf_t *src, *dst; + ngx_uint_t sendfile; + + src = ctx->in->buf; + dst = ctx->buf; + + size = ngx_buf_size(src); + size = ngx_min(size, dst->end - dst->pos); + + sendfile = ctx->sendfile & !ctx->directio; + #if (NGX_SENDFILE_LIMIT) if (src->in_file && src->file_pos >= NGX_SENDFILE_LIMIT) { @@ -380,28 +503,72 @@ if (src->pos == src->last) { dst->flush = src->flush; dst->last_buf = src->last_buf; + dst->last_in_chain = src->last_in_chain; } } else { + +#if (NGX_HAVE_ALIGNED_DIRECTIO) + + if (ctx->unaligned) { + if (ngx_directio_off(src->file->fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, ctx->pool->log, ngx_errno, + ngx_directio_off_n " \"%s\" failed", + src->file->name.data); + } + } + +#endif + +#if (NGX_HAVE_FILE_AIO) + + if (ctx->aio_handler) { + n = ngx_file_aio_read(src->file, dst->pos, (size_t) size, + src->file_pos, ctx->pool); + if (n == NGX_AGAIN) { + ctx->aio_handler(ctx, src->file); + return NGX_AGAIN; + } + + } else { + n = ngx_read_file(src->file, dst->pos, (size_t) size, + src->file_pos); + } +#else + n = ngx_read_file(src->file, dst->pos, (size_t) size, src->file_pos); - if (n == NGX_ERROR) { - return (ngx_int_t) n; +#endif + +#if (NGX_HAVE_ALIGNED_DIRECTIO) + + if (ctx->unaligned) { + ngx_err_t err; + + err = ngx_errno; + + if (ngx_directio_on(src->file->fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, ctx->pool->log, ngx_errno, + ngx_directio_on_n " \"%s\" failed", + src->file->name.data); + } + + ngx_set_errno(err); + + ctx->unaligned = 0; } -#if (NGX_FILE_AIO_READ) - if (n == NGX_AGAIN) { +#endif + + if (n == NGX_ERROR) { return (ngx_int_t) n; } -#endif if (n != size) { - ngx_log_error(NGX_LOG_ALERT, src->file->log, 0, - ngx_read_file_n " reads only %z of %O from file", - n, size); - if (n == 0) { - return NGX_ERROR; - } + ngx_log_error(NGX_LOG_ALERT, ctx->pool->log, 0, + ngx_read_file_n " read only %z of %O from \"%s\"", + n, size, src->file->name.data); + return NGX_ERROR; } dst->last += n; @@ -421,6 +588,7 @@ if (src->file_pos == src->file_last) { dst->flush = src->flush; dst->last_buf = src->last_buf; + dst->last_in_chain = src->last_in_chain; } } @@ -433,8 +601,11 @@ { ngx_chain_writer_ctx_t *ctx = data; - off_t size; - ngx_chain_t *cl; + off_t size; + ngx_chain_t *cl; + ngx_connection_t *c; + + c = ctx->connection; for (size = 0; in; in = in->next) { @@ -446,7 +617,7 @@ size += ngx_buf_size(in->buf); - ngx_log_debug2(NGX_LOG_DEBUG_CORE, ctx->connection->log, 0, + ngx_log_debug2(NGX_LOG_DEBUG_CORE, c->log, 0, "chain writer buf fl:%d s:%uO", in->buf->flush, ngx_buf_size(in->buf)); @@ -461,7 +632,7 @@ ctx->last = &cl->next; } - ngx_log_debug1(NGX_LOG_DEBUG_CORE, ctx->connection->log, 0, + ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0, "chain writer in: %p", ctx->out); for (cl = ctx->out; cl; cl = cl->next) { @@ -476,14 +647,13 @@ size += ngx_buf_size(cl->buf); } - if (size == 0 && !ctx->connection->buffered) { + if (size == 0 && !c->buffered) { return NGX_OK; } - ctx->out = ctx->connection->send_chain(ctx->connection, ctx->out, - ctx->limit); + ctx->out = c->send_chain(c, ctx->out, ctx->limit); - ngx_log_debug1(NGX_LOG_DEBUG_CORE, ctx->connection->log, 0, + ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0, "chain writer out: %p", ctx->out); if (ctx->out == NGX_CHAIN_ERROR) { @@ -493,7 +663,7 @@ if (ctx->out == NULL) { ctx->last = &ctx->out; - if (!ctx->connection->buffered) { + if (!c->buffered) { return NGX_OK; } } diff -Nru nginx-0.5.33/src/core/ngx_palloc.c nginx-0.8.53/src/core/ngx_palloc.c --- nginx-0.5.33/src/core/ngx_palloc.c 2007-07-22 08:40:39.000000000 +0000 +++ nginx-0.8.53/src/core/ngx_palloc.c 2009-12-17 12:25:46.000000000 +0000 @@ -8,21 +8,30 @@ #include +static void *ngx_palloc_block(ngx_pool_t *pool, size_t size); +static void *ngx_palloc_large(ngx_pool_t *pool, size_t size); + + ngx_pool_t * ngx_create_pool(size_t size, ngx_log_t *log) { ngx_pool_t *p; - p = ngx_alloc(size, log); + p = ngx_memalign(NGX_POOL_ALIGNMENT, size, log); if (p == NULL) { return NULL; } - p->last = (u_char *) p + sizeof(ngx_pool_t); - p->end = (u_char *) p + size; + p->d.last = (u_char *) p + sizeof(ngx_pool_t); + p->d.end = (u_char *) p + size; + p->d.next = NULL; + p->d.failed = 0; + + size = size - sizeof(ngx_pool_t); + p->max = (size < NGX_MAX_ALLOC_FROM_POOL) ? size : NGX_MAX_ALLOC_FROM_POOL; + p->current = p; p->chain = NULL; - p->next = NULL; p->large = NULL; p->cleanup = NULL; p->log = log; @@ -40,6 +49,8 @@ for (c = pool->cleanup; c; c = c->next) { if (c->handler) { + ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, 0, + "run cleanup: %p", c); c->handler(c->data); } } @@ -60,9 +71,9 @@ * so we can not use this log while the free()ing the pool */ - for (p = pool, n = pool->next; /* void */; p = n, n = n->next) { + for (p = pool, n = pool->d.next; /* void */; p = n, n = n->d.next) { ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, pool->log, 0, - "free: %p, unused: %uz", p, p->end - p->last); + "free: %p, unused: %uz", p, p->d.end - p->d.last); if (n == NULL) { break; @@ -71,7 +82,7 @@ #endif - for (p = pool, n = pool->next; /* void */; p = n, n = n->next) { + for (p = pool, n = pool->d.next; /* void */; p = n, n = n->d.next) { ngx_free(p); if (n == NULL) { @@ -81,85 +92,175 @@ } +void +ngx_reset_pool(ngx_pool_t *pool) +{ + ngx_pool_t *p; + ngx_pool_large_t *l; + + for (l = pool->large; l; l = l->next) { + if (l->alloc) { + ngx_free(l->alloc); + } + } + + pool->large = NULL; + + for (p = pool; p; p = p->d.next) { + p->d.last = (u_char *) p + sizeof(ngx_pool_t); + } +} + + void * ngx_palloc(ngx_pool_t *pool, size_t size) { - u_char *m; - ngx_pool_t *p, *n, *current; - ngx_pool_large_t *large; + u_char *m; + ngx_pool_t *p; + + if (size <= pool->max) { - if (size <= (size_t) NGX_MAX_ALLOC_FROM_POOL - && size <= (size_t) (pool->end - (u_char *) pool) - - (size_t) ngx_align_ptr(sizeof(ngx_pool_t), NGX_ALIGNMENT)) - { p = pool->current; - current = p; - for ( ;; ) { + do { + m = ngx_align_ptr(p->d.last, NGX_ALIGNMENT); -#if (NGX_HAVE_NONALIGNED) + if ((size_t) (p->d.end - m) >= size) { + p->d.last = m + size; - /* - * allow non-aligned memory blocks for small allocations (1, 2, - * or 3 bytes) and for odd length strings (struct's have aligned - * size) - */ + return m; + } - if (size < sizeof(int) || (size & 1)) { - m = p->last; + p = p->d.next; - } else -#endif + } while (p); - { - m = ngx_align_ptr(p->last, NGX_ALIGNMENT); - } + return ngx_palloc_block(pool, size); + } + + return ngx_palloc_large(pool, size); +} + + +void * +ngx_pnalloc(ngx_pool_t *pool, size_t size) +{ + u_char *m; + ngx_pool_t *p; + + if (size <= pool->max) { + + p = pool->current; - if ((size_t) (p->end - m) >= size) { - p->last = m + size; + do { + m = p->d.last; + + if ((size_t) (p->d.end - m) >= size) { + p->d.last = m + size; return m; } - if ((size_t) (p->end - m) < NGX_ALIGNMENT) { - current = p->next; - } + p = p->d.next; - if (p->next == NULL) { - break; - } + } while (p); - p = p->next; - pool->current = current; - } + return ngx_palloc_block(pool, size); + } - /* allocate a new pool block */ + return ngx_palloc_large(pool, size); +} - n = ngx_create_pool((size_t) (p->end - (u_char *) p), p->log); - if (n == NULL) { - return NULL; - } - pool->current = current ? current : n; +static void * +ngx_palloc_block(ngx_pool_t *pool, size_t size) +{ + u_char *m; + size_t psize; + ngx_pool_t *p, *new, *current; - p->next = n; - m = ngx_align_ptr(n->last, NGX_ALIGNMENT); - n->last = m + size; + psize = (size_t) (pool->d.end - (u_char *) pool); - return m; + m = ngx_memalign(NGX_POOL_ALIGNMENT, psize, pool->log); + if (m == NULL) { + return NULL; } -#if 0 - p = ngx_memalign(ngx_pagesize, size, pool->log); + new = (ngx_pool_t *) m; + + new->d.end = m + psize; + new->d.next = NULL; + new->d.failed = 0; + + m += sizeof(ngx_pool_data_t); + m = ngx_align_ptr(m, NGX_ALIGNMENT); + new->d.last = m + size; + + current = pool->current; + + for (p = current; p->d.next; p = p->d.next) { + if (p->d.failed++ > 4) { + current = p->d.next; + } + } + + p->d.next = new; + + pool->current = current ? current : new; + + return m; +} + + +static void * +ngx_palloc_large(ngx_pool_t *pool, size_t size) +{ + void *p; + ngx_uint_t n; + ngx_pool_large_t *large; + + p = ngx_alloc(size, pool->log); if (p == NULL) { return NULL; } -#else - p = ngx_alloc(size, pool->log); + + n = 0; + + for (large = pool->large; large; large = large->next) { + if (large->alloc == NULL) { + large->alloc = p; + return p; + } + + if (n++ > 3) { + break; + } + } + + large = ngx_palloc(pool, sizeof(ngx_pool_large_t)); + if (large == NULL) { + ngx_free(p); + return NULL; + } + + large->alloc = p; + large->next = pool->large; + pool->large = large; + + return p; +} + + +void * +ngx_pmemalign(ngx_pool_t *pool, size_t size, size_t alignment) +{ + void *p; + ngx_pool_large_t *large; + + p = ngx_memalign(alignment, size, pool->log); if (p == NULL) { return NULL; } -#endif large = ngx_palloc(pool, sizeof(ngx_pool_large_t)); if (large == NULL) { @@ -241,12 +342,33 @@ void +ngx_pool_run_cleanup_file(ngx_pool_t *p, ngx_fd_t fd) +{ + ngx_pool_cleanup_t *c; + ngx_pool_cleanup_file_t *cf; + + for (c = p->cleanup; c; c = c->next) { + if (c->handler == ngx_pool_cleanup_file) { + + cf = c->data; + + if (cf->fd == fd) { + c->handler(cf); + c->handler = NULL; + return; + } + } + } +} + + +void ngx_pool_cleanup_file(void *data) { ngx_pool_cleanup_file_t *c = data; - ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, c->log, 0, "run cleanup: %p, fd:%d", - c, c->fd); + ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, c->log, 0, "file cleanup: fd:%d", + c->fd); if (ngx_close_file(c->fd) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno, @@ -262,8 +384,8 @@ ngx_err_t err; - ngx_log_debug3(NGX_LOG_DEBUG_ALLOC, c->log, 0, "run cleanup: %p, fd:%d %s", - c, c->fd, c->name); + ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, c->log, 0, "file cleanup: fd:%d %s", + c->fd, c->name); if (ngx_delete_file(c->name) == NGX_FILE_ERROR) { err = ngx_errno; diff -Nru nginx-0.5.33/src/core/ngx_palloc.h nginx-0.8.53/src/core/ngx_palloc.h --- nginx-0.5.33/src/core/ngx_palloc.h 2007-05-07 09:20:42.000000000 +0000 +++ nginx-0.8.53/src/core/ngx_palloc.h 2009-12-17 12:25:46.000000000 +0000 @@ -14,14 +14,16 @@ /* * NGX_MAX_ALLOC_FROM_POOL should be (ngx_pagesize - 1), i.e. 4095 on x86. - * On FreeBSD 5.x it allows to use the zero copy sending. * On Windows NT it decreases a number of locked pages in a kernel. */ #define NGX_MAX_ALLOC_FROM_POOL (ngx_pagesize - 1) #define NGX_DEFAULT_POOL_SIZE (16 * 1024) + +#define NGX_POOL_ALIGNMENT 16 #define NGX_MIN_POOL_SIZE \ - (sizeof(ngx_pool_t) + 2 * sizeof(ngx_pool_large_t)) + ngx_align((sizeof(ngx_pool_t) + 2 * sizeof(ngx_pool_large_t)), \ + NGX_POOL_ALIGNMENT) typedef void (*ngx_pool_cleanup_pt)(void *data); @@ -43,12 +45,19 @@ }; -struct ngx_pool_s { +typedef struct { u_char *last; u_char *end; + ngx_pool_t *next; + ngx_uint_t failed; +} ngx_pool_data_t; + + +struct ngx_pool_s { + ngx_pool_data_t d; + size_t max; ngx_pool_t *current; ngx_chain_t *chain; - ngx_pool_t *next; ngx_pool_large_t *large; ngx_pool_cleanup_t *cleanup; ngx_log_t *log; @@ -67,13 +76,17 @@ ngx_pool_t *ngx_create_pool(size_t size, ngx_log_t *log); void ngx_destroy_pool(ngx_pool_t *pool); +void ngx_reset_pool(ngx_pool_t *pool); void *ngx_palloc(ngx_pool_t *pool, size_t size); +void *ngx_pnalloc(ngx_pool_t *pool, size_t size); void *ngx_pcalloc(ngx_pool_t *pool, size_t size); +void *ngx_pmemalign(ngx_pool_t *pool, size_t size, size_t alignment); ngx_int_t ngx_pfree(ngx_pool_t *pool, void *p); ngx_pool_cleanup_t *ngx_pool_cleanup_add(ngx_pool_t *p, size_t size); +void ngx_pool_run_cleanup_file(ngx_pool_t *p, ngx_fd_t fd); void ngx_pool_cleanup_file(void *data); void ngx_pool_delete_file(void *data); diff -Nru nginx-0.5.33/src/core/ngx_parse.c nginx-0.8.53/src/core/ngx_parse.c --- nginx-0.5.33/src/core/ngx_parse.c 2006-08-30 10:39:17.000000000 +0000 +++ nginx-0.8.53/src/core/ngx_parse.c 2008-04-17 14:23:20.000000000 +0000 @@ -11,15 +11,15 @@ ssize_t ngx_parse_size(ngx_str_t *line) { - u_char last; + u_char unit; size_t len; ssize_t size; ngx_int_t scale; len = line->len; - last = line->data[len - 1]; + unit = line->data[len - 1]; - switch (last) { + switch (unit) { case 'K': case 'k': len--; @@ -50,15 +50,15 @@ off_t ngx_parse_offset(ngx_str_t *line) { - u_char last; + u_char unit; off_t offset; size_t len; ngx_int_t scale; len = line->len; - last = line->data[len - 1]; + unit = line->data[len - 1]; - switch (last) { + switch (unit) { case 'K': case 'k': len--; @@ -93,12 +93,11 @@ ngx_int_t -ngx_parse_time(ngx_str_t *line, ngx_int_t sec) +ngx_parse_time(ngx_str_t *line, ngx_uint_t sec) { - size_t len; - u_char *start, last; + u_char *p, *last; ngx_int_t value, total, scale; - ngx_uint_t max, i; + ngx_uint_t max, valid; enum { st_start = 0, st_year, @@ -112,39 +111,30 @@ st_last } step; - - start = line->data; - len = 0; + valid = 0; + value = 0; total = 0; step = sec ? st_start : st_month; + scale = sec ? 1 : 1000; - for (i = 0; /* void */ ; i++) { - - if (i < line->len) { - if (line->data[i] != ' ') { - len++; - continue; - } + p = line->data; + last = p + line->len; - if (line->data[i] == ' ' && len == 0) { - start = &line->data[i + 1]; - continue; - } - } + while (p < last) { - if (len == 0) { - break; + if (*p >= '0' && *p <= '9') { + value = value * 10 + (*p++ - '0'); + valid = 1; + continue; } - last = line->data[i - 1]; + switch (*p++) { - switch (last) { case 'y': if (step > st_start) { return NGX_ERROR; } step = st_year; - len--; max = 68; scale = 60 * 60 * 24 * 365; break; @@ -154,7 +144,6 @@ return NGX_ERROR; } step = st_month; - len--; max = 828; scale = 60 * 60 * 24 * 30; break; @@ -164,7 +153,6 @@ return NGX_ERROR; } step = st_week; - len--; max = 3550; scale = 60 * 60 * 24 * 7; break; @@ -174,7 +162,6 @@ return NGX_ERROR; } step = st_day; - len--; max = 24855; scale = 60 * 60 * 24; break; @@ -184,52 +171,49 @@ return NGX_ERROR; } step = st_hour; - len--; max = 596523; scale = 60 * 60; break; case 'm': - if (step > st_hour) { - return NGX_ERROR; - } - step = st_min; - len--; - max = 35791394; - scale = 60; - break; - - case 's': - len--; - - if (line->data[i - 2] == 'm') { + if (*p == 's') { if (sec || step > st_sec) { return NGX_ERROR; } + p++; step = st_msec; - len--; max = 2147483647; scale = 1; break; } - if (step > st_min) { + if (step > st_hour) { return NGX_ERROR; } + step = st_min; + max = 35791394; + scale = 60; + break; + case 's': + if (step > st_min) { + return NGX_ERROR; + } step = st_sec; max = 2147483647; scale = 1; break; - default: + case ' ': + if (step > st_min) { + return NGX_ERROR; + } step = st_last; max = 2147483647; scale = 1; - } + break; - value = ngx_atoi(start, len); - if (value == NGX_ERROR) { + default: return NGX_ERROR; } @@ -238,23 +222,27 @@ max /= 1000; } - if ((u_int) value > max) { - return NGX_PARSE_LARGE_TIME; + if ((ngx_uint_t) value > max) { + return NGX_ERROR; } total += value * scale; - if ((u_int) total > 2147483647) { - return NGX_PARSE_LARGE_TIME; + if ((ngx_uint_t) total > 2147483647) { + return NGX_ERROR; } - if (i >= line->len) { - break; + value = 0; + scale = sec ? 1 : 1000; + + while (p < last && *p == ' ') { + p++; } + } - len = 0; - start = &line->data[i + 1]; + if (valid) { + return total + value * scale; } - return total; + return NGX_ERROR; } diff -Nru nginx-0.5.33/src/core/ngx_parse.h nginx-0.8.53/src/core/ngx_parse.h --- nginx-0.5.33/src/core/ngx_parse.h 2006-08-30 10:39:17.000000000 +0000 +++ nginx-0.8.53/src/core/ngx_parse.h 2008-04-17 14:23:20.000000000 +0000 @@ -17,7 +17,7 @@ ssize_t ngx_parse_size(ngx_str_t *line); off_t ngx_parse_offset(ngx_str_t *line); -ngx_int_t ngx_parse_time(ngx_str_t *line, ngx_int_t sec); +ngx_int_t ngx_parse_time(ngx_str_t *line, ngx_uint_t sec); #endif /* _NGX_PARSE_H_INCLUDED_ */ diff -Nru nginx-0.5.33/src/core/ngx_queue.c nginx-0.8.53/src/core/ngx_queue.c --- nginx-0.5.33/src/core/ngx_queue.c 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/src/core/ngx_queue.c 2008-05-24 14:10:01.000000000 +0000 @@ -0,0 +1,79 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include + + +/* + * find the middle queue element if the queue has odd number of elements + * or the first element of the queue's second part otherwise + */ + +ngx_queue_t * +ngx_queue_middle(ngx_queue_t *queue) +{ + ngx_queue_t *middle, *next; + + middle = ngx_queue_head(queue); + + if (middle == ngx_queue_last(queue)) { + return middle; + } + + next = ngx_queue_head(queue); + + for ( ;; ) { + middle = ngx_queue_next(middle); + + next = ngx_queue_next(next); + + if (next == ngx_queue_last(queue)) { + return middle; + } + + next = ngx_queue_next(next); + + if (next == ngx_queue_last(queue)) { + return middle; + } + } +} + + +/* the stable insertion sort */ + +void +ngx_queue_sort(ngx_queue_t *queue, + ngx_int_t (*cmp)(const ngx_queue_t *, const ngx_queue_t *)) +{ + ngx_queue_t *q, *prev, *next; + + q = ngx_queue_head(queue); + + if (q == ngx_queue_last(queue)) { + return; + } + + for (q = ngx_queue_next(q); q != ngx_queue_sentinel(queue); q = next) { + + prev = ngx_queue_prev(q); + next = ngx_queue_next(q); + + ngx_queue_remove(q); + + do { + if (cmp(prev, q) <= 0) { + break; + } + + prev = ngx_queue_prev(prev); + + } while (prev != ngx_queue_sentinel(queue)); + + ngx_queue_insert_after(prev, q); + } +} diff -Nru nginx-0.5.33/src/core/ngx_queue.h nginx-0.8.53/src/core/ngx_queue.h --- nginx-0.5.33/src/core/ngx_queue.h 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/src/core/ngx_queue.h 2008-05-24 14:10:01.000000000 +0000 @@ -0,0 +1,111 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include + + +#ifndef _NGX_QUEUE_H_INCLUDED_ +#define _NGX_QUEUE_H_INCLUDED_ + + +typedef struct ngx_queue_s ngx_queue_t; + +struct ngx_queue_s { + ngx_queue_t *prev; + ngx_queue_t *next; +}; + + +#define ngx_queue_init(q) \ + (q)->prev = q; \ + (q)->next = q + + +#define ngx_queue_empty(h) \ + (h == (h)->prev) + + +#define ngx_queue_insert_head(h, x) \ + (x)->next = (h)->next; \ + (x)->next->prev = x; \ + (x)->prev = h; \ + (h)->next = x + + +#define ngx_queue_insert_after ngx_queue_insert_head + + +#define ngx_queue_insert_tail(h, x) \ + (x)->prev = (h)->prev; \ + (x)->prev->next = x; \ + (x)->next = h; \ + (h)->prev = x + + +#define ngx_queue_head(h) \ + (h)->next + + +#define ngx_queue_last(h) \ + (h)->prev + + +#define ngx_queue_sentinel(h) \ + (h) + + +#define ngx_queue_next(q) \ + (q)->next + + +#define ngx_queue_prev(q) \ + (q)->prev + + +#if (NGX_DEBUG) + +#define ngx_queue_remove(x) \ + (x)->next->prev = (x)->prev; \ + (x)->prev->next = (x)->next; \ + (x)->prev = NULL; \ + (x)->next = NULL + +#else + +#define ngx_queue_remove(x) \ + (x)->next->prev = (x)->prev; \ + (x)->prev->next = (x)->next + +#endif + + +#define ngx_queue_split(h, q, n) \ + (n)->prev = (h)->prev; \ + (n)->prev->next = n; \ + (n)->next = q; \ + (h)->prev = (q)->prev; \ + (h)->prev->next = h; \ + (q)->prev = n; + + +#define ngx_queue_add(h, n) \ + (h)->prev->next = (n)->next; \ + (n)->next->prev = (h)->prev; \ + (h)->prev = (n)->prev; \ + (h)->prev->next = h; + + +#define ngx_queue_data(q, type, link) \ + (type *) ((u_char *) q - offsetof(type, link)) + + +ngx_queue_t *ngx_queue_middle(ngx_queue_t *queue); +void ngx_queue_sort(ngx_queue_t *queue, + ngx_int_t (*cmp)(const ngx_queue_t *, const ngx_queue_t *)); + + +#endif /* _NGX_QUEUE_H_INCLUDED_ */ diff -Nru nginx-0.5.33/src/core/ngx_radix_tree.c nginx-0.8.53/src/core/ngx_radix_tree.c --- nginx-0.5.33/src/core/ngx_radix_tree.c 2007-03-06 12:18:45.000000000 +0000 +++ nginx-0.8.53/src/core/ngx_radix_tree.c 2008-12-02 16:50:56.000000000 +0000 @@ -42,13 +42,13 @@ } /* - * The preallocation the first nodes: 0, 1, 00, 01, 10, 11, 000, 001, etc. - * increases the TLB hits even if for the first lookup iterations. - * On the 32-bit platforms the 7 preallocated bits takes continuous 4K, - * 8 - 8K, 9 - 16K, etc. On the 64-bit platforms the 6 preallocated bits + * Preallocation of first nodes : 0, 1, 00, 01, 10, 11, 000, 001, etc. + * increases TLB hits even if for first lookup iterations. + * On 32-bit platforms the 7 preallocated bits takes continuous 4K, + * 8 - 8K, 9 - 16K, etc. On 64-bit platforms the 6 preallocated bits * takes continuous 4K, 7 - 8K, 8 - 16K, etc. There is no sense to * to preallocate more than one page, because further preallocation - * distributes the only bit per page. Instead, the random insertion + * distributes the only bit per page. Instead, a random insertion * may distribute several bits per page. * * Thus, by default we preallocate maximum @@ -274,7 +274,7 @@ } if (tree->size < sizeof(ngx_radix_node_t)) { - tree->start = ngx_palloc(tree->pool, ngx_pagesize); + tree->start = ngx_pmemalign(tree->pool, ngx_pagesize, ngx_pagesize); if (tree->start == NULL) { return NULL; } diff -Nru nginx-0.5.33/src/core/ngx_rbtree.c nginx-0.8.53/src/core/ngx_rbtree.c --- nginx-0.5.33/src/core/ngx_rbtree.c 2007-01-12 19:48:30.000000000 +0000 +++ nginx-0.8.53/src/core/ngx_rbtree.c 2007-12-21 15:32:51.000000000 +0000 @@ -97,28 +97,20 @@ ngx_rbtree_insert_value(ngx_rbtree_node_t *temp, ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel) { - for ( ;; ) { - - if (node->key < temp->key) { - - if (temp->left == sentinel) { - temp->left = node; - break; - } - - temp = temp->left; + ngx_rbtree_node_t **p; - } else { + for ( ;; ) { - if (temp->right == sentinel) { - temp->right = node; - break; - } + p = (node->key < temp->key) ? &temp->left : &temp->right; - temp = temp->right; + if (*p == sentinel) { + break; } + + temp = *p; } + *p = node; node->parent = temp; node->left = sentinel; node->right = sentinel; @@ -130,6 +122,8 @@ ngx_rbtree_insert_timer_value(ngx_rbtree_node_t *temp, ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel) { + ngx_rbtree_node_t **p; + for ( ;; ) { /* @@ -139,29 +133,20 @@ * The comparison takes into account that overflow. */ - if ((ngx_rbtree_key_int_t) node->key - (ngx_rbtree_key_int_t) temp->key - < 0) - { - /* node->key < temp->key */ - - if (temp->left == sentinel) { - temp->left = node; - break; - } + /* node->key < temp->key */ - temp = temp->left; + p = ((ngx_rbtree_key_int_t) node->key - (ngx_rbtree_key_int_t) temp->key + < 0) + ? &temp->left : &temp->right; - } else { - - if (temp->right == sentinel) { - temp->right = node; - break; - } - - temp = temp->right; + if (*p == sentinel) { + break; } + + temp = *p; } + *p = node; node->parent = temp; node->left = sentinel; node->right = sentinel; @@ -257,14 +242,14 @@ if (subst->right != sentinel) { subst->right->parent = subst; } - - /* DEBUG stuff */ - node->left = NULL; - node->right = NULL; - node->parent = NULL; - node->key = 0; } + /* DEBUG stuff */ + node->left = NULL; + node->right = NULL; + node->parent = NULL; + node->key = 0; + if (red) { return; } diff -Nru nginx-0.5.33/src/core/ngx_rbtree.h nginx-0.8.53/src/core/ngx_rbtree.h --- nginx-0.5.33/src/core/ngx_rbtree.h 2007-01-12 19:48:30.000000000 +0000 +++ nginx-0.8.53/src/core/ngx_rbtree.h 2007-12-03 12:17:15.000000000 +0000 @@ -40,6 +40,13 @@ }; +#define ngx_rbtree_init(tree, s, i) \ + ngx_rbtree_sentinel_init(s); \ + (tree)->root = s; \ + (tree)->sentinel = s; \ + (tree)->insert = i + + void ngx_rbtree_insert(ngx_thread_volatile ngx_rbtree_t *tree, ngx_rbtree_node_t *node); void ngx_rbtree_delete(ngx_thread_volatile ngx_rbtree_t *tree, diff -Nru nginx-0.5.33/src/core/ngx_regex.c nginx-0.8.53/src/core/ngx_regex.c --- nginx-0.5.33/src/core/ngx_regex.c 2006-08-30 10:39:17.000000000 +0000 +++ nginx-0.8.53/src/core/ngx_regex.c 2009-11-17 10:24:45.000000000 +0000 @@ -23,61 +23,116 @@ } -ngx_regex_t * -ngx_regex_compile(ngx_str_t *pattern, ngx_int_t options, ngx_pool_t *pool, - ngx_str_t *err) -{ - int erroff; - const char *errstr; - ngx_regex_t *re; +static ngx_inline void +ngx_regex_malloc_init(ngx_pool_t *pool) +{ #if (NGX_THREADS) ngx_core_tls_t *tls; -#if (NGX_SUPPRESS_WARN) - tls = NULL; -#endif - if (ngx_threaded) { tls = ngx_thread_get_tls(ngx_core_tls_key); tls->pool = pool; - } else { - ngx_pcre_pool = pool; + return; } -#else +#endif ngx_pcre_pool = pool; +} + + +static ngx_inline void +ngx_regex_malloc_done(void) +{ +#if (NGX_THREADS) + ngx_core_tls_t *tls; + + if (ngx_threaded) { + tls = ngx_thread_get_tls(ngx_core_tls_key); + tls->pool = NULL; + return; + } #endif - re = pcre_compile((const char *) pattern->data, (int) options, + ngx_pcre_pool = NULL; +} + + +ngx_int_t +ngx_regex_compile(ngx_regex_compile_t *rc) +{ + int n, erroff; + char *p; + const char *errstr; + ngx_regex_t *re; + + ngx_regex_malloc_init(rc->pool); + + re = pcre_compile((const char *) rc->pattern.data, (int) rc->options, &errstr, &erroff, NULL); + /* ensure that there is no current pool */ + ngx_regex_malloc_done(); + if (re == NULL) { - if ((size_t) erroff == pattern->len) { - ngx_snprintf(err->data, err->len - 1, - "pcre_compile() failed: %s in \"%s\"%Z", - errstr, pattern->data); + if ((size_t) erroff == rc->pattern.len) { + rc->err.len = ngx_snprintf(rc->err.data, rc->err.len, + "pcre_compile() failed: %s in \"%V\"", + errstr, &rc->pattern) + - rc->err.data; + } else { - ngx_snprintf(err->data, err->len - 1, - "pcre_compile() failed: %s in \"%s\" at \"%s\"%Z", - errstr, pattern->data, pattern->data + erroff); + rc->err.len = ngx_snprintf(rc->err.data, rc->err.len, + "pcre_compile() failed: %s in \"%V\" at \"%s\"", + errstr, &rc->pattern, rc->pattern.data + erroff) + - rc->err.data; } + + return NGX_ERROR; } - /* ensure that there is no current pool */ + rc->regex = re; -#if (NGX_THREADS) - if (ngx_threaded) { - tls->pool = NULL; - } else { - ngx_pcre_pool = NULL; + n = pcre_fullinfo(re, NULL, PCRE_INFO_CAPTURECOUNT, &rc->captures); + if (n < 0) { + p = "pcre_fullinfo(\"%V\", PCRE_INFO_CAPTURECOUNT) failed: %d"; + goto failed; + } + + if (rc->captures == 0) { + return NGX_OK; + } + + n = pcre_fullinfo(re, NULL, PCRE_INFO_NAMECOUNT, &rc->named_captures); + if (n < 0) { + p = "pcre_fullinfo(\"%V\", PCRE_INFO_NAMECOUNT) failed: %d"; + goto failed; + } + + if (rc->named_captures == 0) { + return NGX_OK; + } + + n = pcre_fullinfo(re, NULL, PCRE_INFO_NAMEENTRYSIZE, &rc->name_size); + if (n < 0) { + p = "pcre_fullinfo(\"%V\", PCRE_INFO_NAMEENTRYSIZE) failed: %d"; + goto failed; } -#else - ngx_pcre_pool = NULL; -#endif - return re; + n = pcre_fullinfo(re, NULL, PCRE_INFO_NAMETABLE, &rc->names); + if (n < 0) { + p = "pcre_fullinfo(\"%V\", PCRE_INFO_NAMETABLE) failed: %d"; + goto failed; + } + + return NGX_OK; + +failed: + + rc->err.len = ngx_snprintf(rc->err.data, rc->err.len, p, &rc->pattern, n) + - rc->err.data; + return NGX_OK; } @@ -99,18 +154,35 @@ ngx_int_t -ngx_regex_exec(ngx_regex_t *re, ngx_str_t *s, int *captures, ngx_int_t size) +ngx_regex_exec_array(ngx_array_t *a, ngx_str_t *s, ngx_log_t *log) { - int rc; + ngx_int_t n; + ngx_uint_t i; + ngx_regex_elt_t *re; - rc = pcre_exec(re, NULL, (const char *) s->data, s->len, 0, 0, - captures, size); + re = a->elts; + + for (i = 0; i < a->nelts; i++) { + + n = ngx_regex_exec(re[i].regex, s, NULL, 0); + + if (n == NGX_REGEX_NO_MATCHED) { + continue; + } - if (rc == -1) { - return NGX_REGEX_NO_MATCHED; + if (n < 0) { + ngx_log_error(NGX_LOG_ALERT, log, 0, + ngx_regex_exec_n " failed: %i on \"%V\" using \"%s\"", + n, s, re[i].name); + return NGX_ERROR; + } + + /* match */ + + return NGX_OK; } - return rc; + return NGX_DECLINED; } @@ -124,11 +196,15 @@ if (ngx_threaded) { tls = ngx_thread_get_tls(ngx_core_tls_key); pool = tls->pool; + } else { pool = ngx_pcre_pool; } + #else + pool = ngx_pcre_pool; + #endif if (pool) { diff -Nru nginx-0.5.33/src/core/ngx_regex.h nginx-0.8.53/src/core/ngx_regex.h --- nginx-0.5.33/src/core/ngx_regex.h 2007-04-18 19:43:22.000000000 +0000 +++ nginx-0.8.53/src/core/ngx_regex.h 2009-11-16 12:19:02.000000000 +0000 @@ -14,21 +14,42 @@ #include -#define NGX_REGEX_NO_MATCHED -1000 +#define NGX_REGEX_NO_MATCHED PCRE_ERROR_NOMATCH /* -1 */ #define NGX_REGEX_CASELESS PCRE_CASELESS typedef pcre ngx_regex_t; + +typedef struct { + ngx_str_t pattern; + ngx_pool_t *pool; + ngx_int_t options; + + ngx_regex_t *regex; + int captures; + int named_captures; + int name_size; + u_char *names; + ngx_str_t err; +} ngx_regex_compile_t; + + +typedef struct { + ngx_regex_t *regex; + u_char *name; +} ngx_regex_elt_t; + + void ngx_regex_init(void); -ngx_regex_t *ngx_regex_compile(ngx_str_t *pattern, ngx_int_t options, - ngx_pool_t *pool, ngx_str_t *err); -ngx_int_t ngx_regex_capture_count(ngx_regex_t *re); -ngx_int_t ngx_regex_exec(ngx_regex_t *re, ngx_str_t *s, int *captures, - ngx_int_t size); +ngx_int_t ngx_regex_compile(ngx_regex_compile_t *rc); + +#define ngx_regex_exec(re, s, captures, size) \ + pcre_exec(re, NULL, (const char *) (s)->data, (s)->len, 0, 0, \ + captures, size) +#define ngx_regex_exec_n "pcre_exec()" -#define ngx_regex_exec_n "pcre_exec()" -#define ngx_regex_capture_count_n "pcre_fullinfo()" +ngx_int_t ngx_regex_exec_array(ngx_array_t *a, ngx_str_t *s, ngx_log_t *log); #endif /* _NGX_REGEX_H_INCLUDED_ */ diff -Nru nginx-0.5.33/src/core/ngx_resolver.c nginx-0.8.53/src/core/ngx_resolver.c --- nginx-0.5.33/src/core/ngx_resolver.c 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/src/core/ngx_resolver.c 2010-09-27 11:23:45.000000000 +0000 @@ -0,0 +1,2204 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include + + +#define NGX_RESOLVER_UDP_SIZE 4096 + + +typedef struct { + u_char ident_hi; + u_char ident_lo; + u_char flags_hi; + u_char flags_lo; + u_char nqs_hi; + u_char nqs_lo; + u_char nan_hi; + u_char nan_lo; + u_char nns_hi; + u_char nns_lo; + u_char nar_hi; + u_char nar_lo; +} ngx_resolver_query_t; + + +typedef struct { + u_char type_hi; + u_char type_lo; + u_char class_hi; + u_char class_lo; +} ngx_resolver_qs_t; + + +typedef struct { + u_char type_hi; + u_char type_lo; + u_char class_hi; + u_char class_lo; + u_char ttl[4]; + u_char len_hi; + u_char len_lo; +} ngx_resolver_an_t; + + +ngx_int_t ngx_udp_connect(ngx_udp_connection_t *uc); + + +static void ngx_resolver_cleanup(void *data); +static void ngx_resolver_cleanup_tree(ngx_resolver_t *r, ngx_rbtree_t *tree); +static ngx_int_t ngx_resolve_name_locked(ngx_resolver_t *r, + ngx_resolver_ctx_t *ctx); +static void ngx_resolver_expire(ngx_resolver_t *r, ngx_rbtree_t *tree, + ngx_queue_t *queue); +static ngx_int_t ngx_resolver_send_query(ngx_resolver_t *r, + ngx_resolver_node_t *rn); +static ngx_int_t ngx_resolver_create_name_query(ngx_resolver_node_t *rn, + ngx_resolver_ctx_t *ctx); +static ngx_int_t ngx_resolver_create_addr_query(ngx_resolver_node_t *rn, + ngx_resolver_ctx_t *ctx); +static void ngx_resolver_resend_handler(ngx_event_t *ev); +static time_t ngx_resolver_resend(ngx_resolver_t *r, ngx_rbtree_t *tree, + ngx_queue_t *queue); +static void ngx_resolver_read_response(ngx_event_t *rev); +static void ngx_resolver_process_response(ngx_resolver_t *r, u_char *buf, + size_t n); +static void ngx_resolver_process_a(ngx_resolver_t *r, u_char *buf, size_t n, + ngx_uint_t ident, ngx_uint_t code, ngx_uint_t nan, ngx_uint_t ans); +static void ngx_resolver_process_ptr(ngx_resolver_t *r, u_char *buf, size_t n, + ngx_uint_t ident, ngx_uint_t code, ngx_uint_t nan); +static ngx_resolver_node_t *ngx_resolver_lookup_name(ngx_resolver_t *r, + ngx_str_t *name, uint32_t hash); +static ngx_resolver_node_t *ngx_resolver_lookup_addr(ngx_resolver_t *r, + in_addr_t addr); +static void ngx_resolver_rbtree_insert_value(ngx_rbtree_node_t *temp, + ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel); +static ngx_int_t ngx_resolver_copy(ngx_resolver_t *r, ngx_str_t *name, + u_char *buf, u_char *src, u_char *last); +static void ngx_resolver_timeout_handler(ngx_event_t *ev); +static void ngx_resolver_free_node(ngx_resolver_t *r, ngx_resolver_node_t *rn); +static void *ngx_resolver_alloc(ngx_resolver_t *r, size_t size); +static void *ngx_resolver_calloc(ngx_resolver_t *r, size_t size); +static void ngx_resolver_free(ngx_resolver_t *r, void *p); +static void ngx_resolver_free_locked(ngx_resolver_t *r, void *p); +static void *ngx_resolver_dup(ngx_resolver_t *r, void *src, size_t size); +static u_char *ngx_resolver_log_error(ngx_log_t *log, u_char *buf, size_t len); + + +ngx_resolver_t * +ngx_resolver_create(ngx_conf_t *cf, ngx_addr_t *addr) +{ + ngx_resolver_t *r; + ngx_pool_cleanup_t *cln; + ngx_udp_connection_t *uc; + + cln = ngx_pool_cleanup_add(cf->pool, 0); + if (cln == NULL) { + return NULL; + } + + cln->handler = ngx_resolver_cleanup; + + r = ngx_calloc(sizeof(ngx_resolver_t), cf->log); + if (r == NULL) { + return NULL; + } + + cln->data = r; + + r->event = ngx_calloc(sizeof(ngx_event_t), cf->log); + if (r->event == NULL) { + return NULL; + } + + ngx_rbtree_init(&r->name_rbtree, &r->name_sentinel, + ngx_resolver_rbtree_insert_value); + + ngx_rbtree_init(&r->addr_rbtree, &r->addr_sentinel, + ngx_rbtree_insert_value); + + ngx_queue_init(&r->name_resend_queue); + ngx_queue_init(&r->addr_resend_queue); + + ngx_queue_init(&r->name_expire_queue); + ngx_queue_init(&r->addr_expire_queue); + + r->event->handler = ngx_resolver_resend_handler; + r->event->data = r; + r->event->log = &cf->cycle->new_log; + r->ident = -1; + + r->resend_timeout = 5; + r->expire = 30; + r->valid = 300; + + r->log = &cf->cycle->new_log; + r->log_level = NGX_LOG_ERR; + + if (addr) { + uc = ngx_calloc(sizeof(ngx_udp_connection_t), cf->log); + if (uc == NULL) { + return NULL; + } + + r->udp_connection = uc; + + uc->sockaddr = addr->sockaddr; + uc->socklen = addr->socklen; + uc->server = addr->name; + + uc->log = cf->cycle->new_log; + uc->log.handler = ngx_resolver_log_error; + uc->log.data = uc; + uc->log.action = "resolving"; + } + + return r; +} + + +static void +ngx_resolver_cleanup(void *data) +{ + ngx_resolver_t *r = data; + + if (r) { + ngx_log_debug0(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0, + "cleanup resolver"); + + ngx_resolver_cleanup_tree(r, &r->name_rbtree); + + ngx_resolver_cleanup_tree(r, &r->addr_rbtree); + + if (r->event) { + ngx_free(r->event); + } + + if (r->udp_connection) { + if (r->udp_connection->connection) { + ngx_close_connection(r->udp_connection->connection); + } + + ngx_free(r->udp_connection); + } + + ngx_free(r); + } +} + + +static void +ngx_resolver_cleanup_tree(ngx_resolver_t *r, ngx_rbtree_t *tree) +{ + ngx_resolver_ctx_t *ctx, *next; + ngx_resolver_node_t *rn; + + while (tree->root != tree->sentinel) { + + rn = (ngx_resolver_node_t *) ngx_rbtree_min(tree->root, tree->sentinel); + + ngx_queue_remove(&rn->queue); + + for (ctx = rn->waiting; ctx; ctx = next) { + next = ctx->next; + + if (ctx->event) { + ngx_resolver_free(r, ctx->event); + } + + ngx_resolver_free(r, ctx); + } + + ngx_rbtree_delete(tree, &rn->node); + + ngx_resolver_free_node(r, rn); + } +} + + +ngx_resolver_ctx_t * +ngx_resolve_start(ngx_resolver_t *r, ngx_resolver_ctx_t *temp) +{ + in_addr_t addr; + ngx_resolver_ctx_t *ctx; + + if (temp) { + addr = ngx_inet_addr(temp->name.data, temp->name.len); + + if (addr != INADDR_NONE) { + temp->resolver = r; + temp->state = NGX_OK; + temp->naddrs = 1; + temp->addrs = &temp->addr; + temp->addr = addr; + temp->quick = 1; + + return temp; + } + } + + if (r->udp_connection == NULL) { + return NGX_NO_RESOLVER; + } + + ctx = ngx_resolver_calloc(r, sizeof(ngx_resolver_ctx_t)); + + if (ctx) { + ctx->resolver = r; + } + + return ctx; +} + + +ngx_int_t +ngx_resolve_name(ngx_resolver_ctx_t *ctx) +{ + ngx_int_t rc; + ngx_resolver_t *r; + + r = ctx->resolver; + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, r->log, 0, + "resolve: \"%V\"", &ctx->name); + + if (ctx->quick) { + ctx->handler(ctx); + return NGX_OK; + } + + /* lock name mutex */ + + rc = ngx_resolve_name_locked(r, ctx); + + if (rc == NGX_OK) { + return NGX_OK; + } + + /* unlock name mutex */ + + if (rc == NGX_AGAIN) { + return NGX_OK; + } + + /* NGX_ERROR */ + + if (ctx->event) { + ngx_resolver_free(r, ctx->event); + } + + ngx_resolver_free(r, ctx); + + return NGX_ERROR; +} + + +void +ngx_resolve_name_done(ngx_resolver_ctx_t *ctx) +{ + uint32_t hash; + ngx_resolver_t *r; + ngx_resolver_ctx_t *w, **p; + ngx_resolver_node_t *rn; + + r = ctx->resolver; + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, r->log, 0, + "resolve name done: %i", ctx->state); + + if (ctx->quick) { + return; + } + + if (ctx->event && ctx->event->timer_set) { + ngx_del_timer(ctx->event); + } + + /* lock name mutex */ + + if (ctx->state == NGX_AGAIN || ctx->state == NGX_RESOLVE_TIMEDOUT) { + + hash = ngx_crc32_short(ctx->name.data, ctx->name.len); + + rn = ngx_resolver_lookup_name(r, &ctx->name, hash); + + if (rn) { + p = &rn->waiting; + w = rn->waiting; + + while (w) { + if (w == ctx) { + *p = w->next; + + goto done; + } + + p = &w->next; + w = w->next; + } + } + + ngx_log_error(NGX_LOG_ALERT, r->log, 0, + "could not cancel %V resolving", &ctx->name); + } + +done: + + ngx_resolver_expire(r, &r->name_rbtree, &r->name_expire_queue); + + /* unlock name mutex */ + + /* lock alloc mutex */ + + if (ctx->event) { + ngx_resolver_free_locked(r, ctx->event); + } + + ngx_resolver_free_locked(r, ctx); + + /* unlock alloc mutex */ +} + + +/* NGX_RESOLVE_A only */ + +static ngx_int_t +ngx_resolve_name_locked(ngx_resolver_t *r, ngx_resolver_ctx_t *ctx) +{ + uint32_t hash; + in_addr_t addr, *addrs; + ngx_int_t rc; + ngx_uint_t naddrs; + ngx_resolver_ctx_t *next; + ngx_resolver_node_t *rn; + + hash = ngx_crc32_short(ctx->name.data, ctx->name.len); + + rn = ngx_resolver_lookup_name(r, &ctx->name, hash); + + if (rn) { + + if (rn->valid >= ngx_time()) { + + ngx_log_debug0(NGX_LOG_DEBUG_CORE, r->log, 0, "resolve cached"); + + ngx_queue_remove(&rn->queue); + + rn->expire = ngx_time() + r->expire; + + ngx_queue_insert_head(&r->name_expire_queue, &rn->queue); + + naddrs = rn->naddrs; + + if (naddrs) { + + /* NGX_RESOLVE_A answer */ + + if (naddrs != 1) { + addr = 0; + addrs = ngx_resolver_dup(r, rn->u.addrs, + naddrs * sizeof(in_addr_t)); + if (addrs == NULL) { + return NGX_ERROR; + } + + } else { + addr = rn->u.addr; + addrs = NULL; + } + + ctx->next = rn->waiting; + rn->waiting = NULL; + + /* unlock name mutex */ + + do { + ctx->state = NGX_OK; + ctx->naddrs = naddrs; + ctx->addrs = (naddrs == 1) ? &ctx->addr : addrs; + ctx->addr = addr; + next = ctx->next; + + ctx->handler(ctx); + + ctx = next; + } while (ctx); + + if (addrs) { + ngx_resolver_free(r, addrs); + } + + return NGX_OK; + } + + /* NGX_RESOLVE_CNAME */ + + if (ctx->recursion++ < NGX_RESOLVER_MAX_RECURSION) { + + ctx->name.len = rn->cnlen; + ctx->name.data = rn->u.cname; + + return ngx_resolve_name_locked(r, ctx); + } + + ctx->next = rn->waiting; + rn->waiting = NULL; + + /* unlock name mutex */ + + do { + ctx->state = NGX_RESOLVE_NXDOMAIN; + next = ctx->next; + + ctx->handler(ctx); + + ctx = next; + } while (ctx); + + return NGX_OK; + } + + if (rn->waiting) { + + ctx->next = rn->waiting; + rn->waiting = ctx; + ctx->state = NGX_AGAIN; + + return NGX_AGAIN; + } + + ngx_queue_remove(&rn->queue); + + /* lock alloc mutex */ + + ngx_resolver_free_locked(r, rn->query); + rn->query = NULL; + + if (rn->cnlen) { + ngx_resolver_free_locked(r, rn->u.cname); + } + + if (rn->naddrs > 1) { + ngx_resolver_free_locked(r, rn->u.addrs); + } + + /* unlock alloc mutex */ + + } else { + + rn = ngx_resolver_alloc(r, sizeof(ngx_resolver_node_t)); + if (rn == NULL) { + return NGX_ERROR; + } + + rn->name = ngx_resolver_dup(r, ctx->name.data, ctx->name.len); + if (rn->name == NULL) { + ngx_resolver_free(r, rn); + return NGX_ERROR; + } + + rn->node.key = hash; + rn->nlen = (u_short) ctx->name.len; + rn->query = NULL; + + ngx_rbtree_insert(&r->name_rbtree, &rn->node); + } + + rc = ngx_resolver_create_name_query(rn, ctx); + + if (rc == NGX_ERROR) { + goto failed; + } + + if (rc == NGX_DECLINED) { + ngx_rbtree_delete(&r->name_rbtree, &rn->node); + + ngx_resolver_free(r, rn->query); + ngx_resolver_free(r, rn->name); + ngx_resolver_free(r, rn); + + ctx->state = NGX_RESOLVE_NXDOMAIN; + ctx->handler(ctx); + + return NGX_OK; + } + + if (ngx_resolver_send_query(r, rn) != NGX_OK) { + goto failed; + } + + if (ctx->event == NULL) { + ctx->event = ngx_resolver_calloc(r, sizeof(ngx_event_t)); + if (ctx->event == NULL) { + goto failed; + } + + ctx->event->handler = ngx_resolver_timeout_handler; + ctx->event->data = ctx; + ctx->event->log = r->log; + ctx->ident = -1; + + ngx_add_timer(ctx->event, ctx->timeout); + } + + if (ngx_queue_empty(&r->name_resend_queue)) { + ngx_add_timer(r->event, (ngx_msec_t) (r->resend_timeout * 1000)); + } + + rn->expire = ngx_time() + r->resend_timeout; + + ngx_queue_insert_head(&r->name_resend_queue, &rn->queue); + + rn->cnlen = 0; + rn->naddrs = 0; + rn->valid = 0; + rn->waiting = ctx; + + ctx->state = NGX_AGAIN; + + return NGX_AGAIN; + +failed: + + ngx_rbtree_delete(&r->name_rbtree, &rn->node); + + if (rn->query) { + ngx_resolver_free(r, rn->query); + } + + ngx_resolver_free(r, rn->name); + + ngx_resolver_free(r, rn); + + return NGX_ERROR; +} + + +ngx_int_t +ngx_resolve_addr(ngx_resolver_ctx_t *ctx) +{ + u_char *name; + ngx_resolver_t *r; + ngx_resolver_node_t *rn; + + r = ctx->resolver; + + ctx->addr = ntohl(ctx->addr); + + /* lock addr mutex */ + + rn = ngx_resolver_lookup_addr(r, ctx->addr); + + if (rn) { + + if (rn->valid >= ngx_time()) { + + ngx_log_debug0(NGX_LOG_DEBUG_CORE, r->log, 0, "resolve cached"); + + ngx_queue_remove(&rn->queue); + + rn->expire = ngx_time() + r->expire; + + ngx_queue_insert_head(&r->addr_expire_queue, &rn->queue); + + name = ngx_resolver_dup(r, rn->name, rn->nlen); + if (name == NULL) { + goto failed; + } + + ctx->name.len = rn->nlen; + ctx->name.data = name; + + /* unlock addr mutex */ + + ctx->state = NGX_OK; + + ctx->handler(ctx); + + ngx_resolver_free(r, name); + + return NGX_OK; + } + + if (rn->waiting) { + + ctx->next = rn->waiting; + rn->waiting = ctx; + ctx->state = NGX_AGAIN; + + /* unlock addr mutex */ + + return NGX_OK; + } + + ngx_queue_remove(&rn->queue); + + ngx_resolver_free(r, rn->query); + rn->query = NULL; + + } else { + rn = ngx_resolver_alloc(r, sizeof(ngx_resolver_node_t)); + if (rn == NULL) { + goto failed; + } + + rn->node.key = ctx->addr; + rn->query = NULL; + + ngx_rbtree_insert(&r->addr_rbtree, &rn->node); + } + + if (ngx_resolver_create_addr_query(rn, ctx) != NGX_OK) { + goto failed; + } + + if (ngx_resolver_send_query(r, rn) != NGX_OK) { + goto failed; + } + + ctx->event = ngx_resolver_calloc(r, sizeof(ngx_event_t)); + if (ctx->event == NULL) { + goto failed; + } + + ctx->event->handler = ngx_resolver_timeout_handler; + ctx->event->data = ctx; + ctx->event->log = r->log; + ctx->ident = -1; + + ngx_add_timer(ctx->event, ctx->timeout); + + if (ngx_queue_empty(&r->addr_resend_queue)) { + ngx_add_timer(r->event, (ngx_msec_t) (r->resend_timeout * 1000)); + } + + rn->expire = ngx_time() + r->resend_timeout; + + ngx_queue_insert_head(&r->addr_resend_queue, &rn->queue); + + rn->cnlen = 0; + rn->naddrs = 0; + rn->name = NULL; + rn->nlen = 0; + rn->valid = 0; + rn->waiting = ctx; + + /* unlock addr mutex */ + + ctx->state = NGX_AGAIN; + + return NGX_OK; + +failed: + + if (rn) { + ngx_rbtree_delete(&r->addr_rbtree, &rn->node); + + if (rn->query) { + ngx_resolver_free(r, rn->query); + } + + ngx_resolver_free(r, rn); + } + + /* unlock addr mutex */ + + if (ctx->event) { + ngx_resolver_free(r, ctx->event); + } + + ngx_resolver_free(r, ctx); + + return NGX_ERROR; +} + + +void +ngx_resolve_addr_done(ngx_resolver_ctx_t *ctx) +{ + in_addr_t addr; + ngx_resolver_t *r; + ngx_resolver_ctx_t *w, **p; + ngx_resolver_node_t *rn; + + r = ctx->resolver; + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, r->log, 0, + "resolve addr done: %i", ctx->state); + + if (ctx->event && ctx->event->timer_set) { + ngx_del_timer(ctx->event); + } + + /* lock addr mutex */ + + if (ctx->state == NGX_AGAIN || ctx->state == NGX_RESOLVE_TIMEDOUT) { + + rn = ngx_resolver_lookup_addr(r, ctx->addr); + + if (rn) { + p = &rn->waiting; + w = rn->waiting; + + while (w) { + if (w == ctx) { + *p = w->next; + + goto done; + } + + p = &w->next; + w = w->next; + } + } + + addr = ntohl(ctx->addr); + + ngx_log_error(NGX_LOG_ALERT, r->log, 0, + "could not cancel %ud.%ud.%ud.%ud resolving", + (addr >> 24) & 0xff, (addr >> 16) & 0xff, + (addr >> 8) & 0xff, addr & 0xff); + } + +done: + + ngx_resolver_expire(r, &r->addr_rbtree, &r->addr_expire_queue); + + /* unlock addr mutex */ + + /* lock alloc mutex */ + + if (ctx->event) { + ngx_resolver_free_locked(r, ctx->event); + } + + ngx_resolver_free_locked(r, ctx); + + /* unlock alloc mutex */ +} + + +static void +ngx_resolver_expire(ngx_resolver_t *r, ngx_rbtree_t *tree, ngx_queue_t *queue) +{ + time_t now; + ngx_uint_t i; + ngx_queue_t *q; + ngx_resolver_node_t *rn; + + ngx_log_debug0(NGX_LOG_DEBUG_CORE, r->log, 0, "resolver expire"); + + now = ngx_time(); + + for (i = 0; i < 2; i++) { + if (ngx_queue_empty(queue)) { + return; + } + + q = ngx_queue_last(queue); + + rn = ngx_queue_data(q, ngx_resolver_node_t, queue); + + if (now <= rn->expire) { + return; + } + + ngx_log_debug2(NGX_LOG_DEBUG_CORE, r->log, 0, + "resolver expire \"%*s\"", (size_t) rn->nlen, rn->name); + + ngx_queue_remove(q); + + ngx_rbtree_delete(tree, &rn->node); + + ngx_resolver_free_node(r, rn); + } +} + + +static ngx_int_t +ngx_resolver_send_query(ngx_resolver_t *r, ngx_resolver_node_t *rn) +{ + ssize_t n; + ngx_udp_connection_t *uc; + + uc = r->udp_connection; + + if (uc->connection == NULL) { + if (ngx_udp_connect(uc) != NGX_OK) { + return NGX_ERROR; + } + + uc->connection->data = r; + uc->connection->read->handler = ngx_resolver_read_response; + uc->connection->read->resolver = 1; + } + + n = ngx_send(uc->connection, rn->query, rn->qlen); + + if (n == -1) { + return NGX_ERROR; + } + + if ((size_t) n != (size_t) rn->qlen) { + ngx_log_error(NGX_LOG_CRIT, &uc->log, 0, "send() incomplete"); + return NGX_ERROR; + } + + return NGX_OK; +} + + +static void +ngx_resolver_resend_handler(ngx_event_t *ev) +{ + time_t timer, atimer, ntimer; + ngx_resolver_t *r; + + r = ev->data; + + ngx_log_debug0(NGX_LOG_DEBUG_CORE, r->log, 0, + "resolver resend handler"); + + /* lock name mutex */ + + ntimer = ngx_resolver_resend(r, &r->name_rbtree, &r->name_resend_queue); + + /* unlock name mutex */ + + /* lock addr mutex */ + + atimer = ngx_resolver_resend(r, &r->addr_rbtree, &r->addr_resend_queue); + + /* unlock addr mutex */ + + if (ntimer == 0) { + timer = atimer; + + } else if (atimer == 0) { + timer = ntimer; + + } else { + timer = (atimer < ntimer) ? atimer : ntimer; + } + + if (timer) { + ngx_add_timer(r->event, (ngx_msec_t) (timer * 1000)); + } +} + + +static time_t +ngx_resolver_resend(ngx_resolver_t *r, ngx_rbtree_t *tree, ngx_queue_t *queue) +{ + time_t now; + ngx_queue_t *q; + ngx_resolver_node_t *rn; + + now = ngx_time(); + + for ( ;; ) { + if (ngx_queue_empty(queue)) { + return 0; + } + + q = ngx_queue_last(queue); + + rn = ngx_queue_data(q, ngx_resolver_node_t, queue); + + if (now < rn->expire) { + return rn->expire - now; + } + + ngx_log_debug3(NGX_LOG_DEBUG_CORE, r->log, 0, + "resolver resend \"%*s\" %p", + (size_t) rn->nlen, rn->name, rn->waiting); + + ngx_queue_remove(q); + + if (rn->waiting) { + + if (ngx_resolver_send_query(r, rn) == NGX_OK) { + + rn->expire = now + r->resend_timeout; + + ngx_queue_insert_head(queue, &rn->queue); + } + + continue; + } + + ngx_rbtree_delete(tree, &rn->node); + + ngx_resolver_free_node(r, rn); + } +} + + +static void +ngx_resolver_read_response(ngx_event_t *rev) +{ + ssize_t n; + ngx_connection_t *c; + u_char buf[NGX_RESOLVER_UDP_SIZE]; + + c = rev->data; + + do { + n = ngx_udp_recv(c, buf, NGX_RESOLVER_UDP_SIZE); + + if (n < 0) { + return; + } + + ngx_resolver_process_response(c->data, buf, n); + + } while (rev->ready); +} + + +static void +ngx_resolver_process_response(ngx_resolver_t *r, u_char *buf, size_t n) +{ + char *err; + size_t len; + ngx_uint_t i, times, ident, qident, flags, code, nqs, nan, + qtype, qclass; + ngx_queue_t *q; + ngx_resolver_qs_t *qs; + ngx_resolver_node_t *rn; + ngx_resolver_query_t *query; + + if ((size_t) n < sizeof(ngx_resolver_query_t)) { + goto short_response; + } + + query = (ngx_resolver_query_t *) buf; + + ident = (query->ident_hi << 8) + query->ident_lo; + flags = (query->flags_hi << 8) + query->flags_lo; + nqs = (query->nqs_hi << 8) + query->nqs_lo; + nan = (query->nan_hi << 8) + query->nan_lo; + + ngx_log_debug6(NGX_LOG_DEBUG_CORE, r->log, 0, + "resolver DNS response %ui fl:%04Xui %ui/%ui/%ui/%ui", + ident, flags, nqs, nan, + (query->nns_hi << 8) + query->nns_lo, + (query->nar_hi << 8) + query->nar_lo); + + if (!(flags & 0x8000)) { + ngx_log_error(r->log_level, r->log, 0, + "invalid DNS response %ui fl:%04Xui", ident, flags); + return; + } + + code = flags & 0x7f; + + if (code == NGX_RESOLVE_FORMERR) { + + times = 0; + + for (q = ngx_queue_head(&r->name_resend_queue); + q != ngx_queue_sentinel(&r->name_resend_queue) || times++ < 100; + q = ngx_queue_next(q)) + { + rn = ngx_queue_data(q, ngx_resolver_node_t, queue); + qident = (rn->query[0] << 8) + rn->query[1]; + + if (qident == ident) { + ngx_log_error(r->log_level, r->log, 0, + "DNS error (%ui: %s), query id:%ui, name:\"%*s\"", + code, ngx_resolver_strerror(code), ident, + rn->nlen, rn->name); + return; + } + } + + goto dns_error; + } + + if (code > NGX_RESOLVE_REFUSED) { + goto dns_error; + } + + if (nqs != 1) { + err = "invalid number of questions in DNS response"; + goto done; + } + + i = sizeof(ngx_resolver_query_t); + + while (i < (ngx_uint_t) n) { + if (buf[i] == '\0') { + goto found; + } + + len = buf[i]; + i += 1 + len; + } + + goto short_response; + +found: + + if (i++ == 0) { + err = "zero-length domain name in DNS response"; + goto done; + } + + if (i + sizeof(ngx_resolver_qs_t) + nan * (2 + sizeof(ngx_resolver_an_t)) + > (ngx_uint_t) n) + { + goto short_response; + } + + qs = (ngx_resolver_qs_t *) &buf[i]; + + qtype = (qs->type_hi << 8) + qs->type_lo; + qclass = (qs->class_hi << 8) + qs->class_lo; + + ngx_log_debug2(NGX_LOG_DEBUG_CORE, r->log, 0, + "resolver DNS response qt:%ui cl:%ui", qtype, qclass); + + if (qclass != 1) { + ngx_log_error(r->log_level, r->log, 0, + "unknown query class %ui in DNS response", qclass); + return; + } + + switch (qtype) { + + case NGX_RESOLVE_A: + + ngx_resolver_process_a(r, buf, n, ident, code, nan, + i + sizeof(ngx_resolver_qs_t)); + + break; + + case NGX_RESOLVE_PTR: + + ngx_resolver_process_ptr(r, buf, n, ident, code, nan); + + break; + + default: + ngx_log_error(r->log_level, r->log, 0, + "unknown query type %ui in DNS response", qtype); + return; + } + + return; + +short_response: + + err = "short dns response"; + +done: + + ngx_log_error(r->log_level, r->log, 0, err); + + return; + +dns_error: + + ngx_log_error(r->log_level, r->log, 0, + "DNS error (%ui: %s), query id:%ui", + code, ngx_resolver_strerror(code), ident); + return; +} + + +static void +ngx_resolver_process_a(ngx_resolver_t *r, u_char *buf, size_t last, + ngx_uint_t ident, ngx_uint_t code, ngx_uint_t nan, ngx_uint_t ans) +{ + char *err; + u_char *cname; + size_t len; + uint32_t hash; + in_addr_t addr, *addrs; + ngx_str_t name; + ngx_uint_t qtype, qident, naddrs, a, i, n, start; + ngx_resolver_an_t *an; + ngx_resolver_ctx_t *ctx, *next; + ngx_resolver_node_t *rn; + + if (ngx_resolver_copy(r, &name, buf, &buf[12], &buf[last]) != NGX_OK) { + return; + } + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, r->log, 0, "resolver qs:%V", &name); + + hash = ngx_crc32_short(name.data, name.len); + + /* lock name mutex */ + + rn = ngx_resolver_lookup_name(r, &name, hash); + + if (rn == NULL || rn->query == NULL) { + ngx_log_error(r->log_level, r->log, 0, + "unexpected response for %V", &name); + goto failed; + } + + qident = (rn->query[0] << 8) + rn->query[1]; + + if (ident != qident) { + ngx_log_error(r->log_level, r->log, 0, + "wrong ident %ui response for %V, expect %ui", + ident, &name, qident); + goto failed; + } + + ngx_resolver_free(r, name.data); + + if (code == 0 && nan == 0) { + code = 3; /* NXDOMAIN */ + } + + if (code) { + next = rn->waiting; + rn->waiting = NULL; + + ngx_queue_remove(&rn->queue); + + ngx_rbtree_delete(&r->name_rbtree, &rn->node); + + ngx_resolver_free_node(r, rn); + + /* unlock name mutex */ + + while (next) { + ctx = next; + ctx->state = code; + next = ctx->next; + + ctx->handler(ctx); + } + + return; + } + + i = ans; + naddrs = 0; + addr = 0; + addrs = NULL; + cname = NULL; + qtype = 0; + + for (a = 0; a < nan; a++) { + + start = i; + + while (i < last) { + + if (buf[i] & 0xc0) { + i += 2; + goto found; + } + + if (buf[i] == 0) { + i++; + goto test_length; + } + + i += 1 + buf[i]; + } + + goto short_response; + + test_length: + + if (i - start < 2) { + err = "invalid name in dns response"; + goto invalid; + } + + found: + + if (i + sizeof(ngx_resolver_an_t) >= last) { + goto short_response; + } + + an = (ngx_resolver_an_t *) &buf[i]; + + qtype = (an->type_hi << 8) + an->type_lo; + len = (an->len_hi << 8) + an->len_lo; + + if (qtype == NGX_RESOLVE_A) { + + i += sizeof(ngx_resolver_an_t); + + if (i + len > last) { + goto short_response; + } + + addr = htonl((buf[i] << 24) + (buf[i + 1] << 16) + + (buf[i + 2] << 8) + (buf[i + 3])); + + naddrs++; + + i += len; + + } else if (qtype == NGX_RESOLVE_CNAME) { + cname = &buf[i] + sizeof(ngx_resolver_an_t); + i += sizeof(ngx_resolver_an_t) + len; + + } else if (qtype == NGX_RESOLVE_DNAME) { + i += sizeof(ngx_resolver_an_t) + len; + + } else { + ngx_log_error(r->log_level, r->log, 0, + "unexpected qtype %ui", qtype); + } + } + + ngx_log_debug2(NGX_LOG_DEBUG_CORE, r->log, 0, + "resolver naddrs:%ui cname:%p", naddrs, cname); + + if (naddrs) { + + if (naddrs == 1) { + rn->u.addr = addr; + + } else { + + addrs = ngx_resolver_alloc(r, naddrs * sizeof(in_addr_t)); + if (addrs == NULL) { + return; + } + + n = 0; + i = ans; + + for (a = 0; a < nan; a++) { + + for ( ;; ) { + + if (buf[i] & 0xc0) { + i += 2; + goto ok; + } + + if (buf[i] == 0) { + i++; + goto ok; + } + + i += 1 + buf[i]; + } + + ok: + + an = (ngx_resolver_an_t *) &buf[i]; + + qtype = (an->type_hi << 8) + an->type_lo; + len = (an->len_hi << 8) + an->len_lo; + + i += sizeof(ngx_resolver_an_t); + + if (qtype == NGX_RESOLVE_A) { + + addrs[n++] = htonl((buf[i] << 24) + (buf[i + 1] << 16) + + (buf[i + 2] << 8) + (buf[i + 3])); + + if (n == naddrs) { + break; + } + } + + i += len; + } + + rn->u.addrs = addrs; + + addrs = ngx_resolver_dup(r, rn->u.addrs, + naddrs * sizeof(in_addr_t)); + if (addrs == NULL) { + return; + } + } + + rn->naddrs = (u_short) naddrs; + + ngx_queue_remove(&rn->queue); + + rn->valid = ngx_time() + r->valid; + rn->expire = ngx_time() + r->expire; + + ngx_queue_insert_head(&r->name_expire_queue, &rn->queue); + + next = rn->waiting; + rn->waiting = NULL; + + /* unlock name mutex */ + + while (next) { + ctx = next; + ctx->state = NGX_OK; + ctx->naddrs = naddrs; + ctx->addrs = (naddrs == 1) ? &ctx->addr : addrs; + ctx->addr = addr; + next = ctx->next; + + ctx->handler(ctx); + } + + if (naddrs > 1) { + ngx_resolver_free(r, addrs); + } + + return; + + } else if (cname) { + + /* CNAME only */ + + if (ngx_resolver_copy(r, &name, buf, cname, &buf[last]) != NGX_OK) { + return; + } + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, r->log, 0, + "resolver cname:\"%V\"", &name); + + ngx_queue_remove(&rn->queue); + + rn->cnlen = (u_short) name.len; + rn->u.cname = name.data; + rn->valid = ngx_time() + r->valid; + rn->expire = ngx_time() + r->expire; + + ngx_queue_insert_head(&r->name_expire_queue, &rn->queue); + + ctx = rn->waiting; + rn->waiting = NULL; + + if (ctx) { + ctx->name = name; + + (void) ngx_resolve_name_locked(r, ctx); + } + + return; + } + + ngx_log_error(r->log_level, r->log, 0, + "no A or CNAME types in DNS responses, unknown query type: %ui", + qtype); + return; + +short_response: + + err = "short dns response"; + +invalid: + + /* unlock name mutex */ + + ngx_log_error(r->log_level, r->log, 0, err); + + return; + +failed: + + /* unlock name mutex */ + + ngx_resolver_free(r, name.data); + + return; +} + + +static void +ngx_resolver_process_ptr(ngx_resolver_t *r, u_char *buf, size_t n, + ngx_uint_t ident, ngx_uint_t code, ngx_uint_t nan) +{ + char *err; + size_t len; + in_addr_t addr; + ngx_int_t digit; + ngx_str_t name; + ngx_uint_t i, mask, qtype, qclass, qident; + ngx_resolver_an_t *an; + ngx_resolver_ctx_t *ctx, *next; + ngx_resolver_node_t *rn; + + if (ngx_resolver_copy(r, NULL, buf, &buf[12], &buf[n]) != NGX_OK) { + goto invalid_in_addr_arpa; + } + + addr = 0; + i = 12; + + for (mask = 0; mask < 32; mask += 8) { + len = buf[i++]; + + digit = ngx_atoi(&buf[i], len); + if (digit == NGX_ERROR || digit > 255) { + goto invalid_in_addr_arpa; + } + + addr += digit << mask; + i += len; + } + + if (ngx_strcmp(&buf[i], "\7in-addr\4arpa") != 0) { + goto invalid_in_addr_arpa; + } + + /* lock addr mutex */ + + rn = ngx_resolver_lookup_addr(r, addr); + + if (rn == NULL || rn->query == NULL) { + ngx_log_error(r->log_level, r->log, 0, + "unexpected response for %ud.%ud.%ud.%ud", + (addr >> 24) & 0xff, (addr >> 16) & 0xff, + (addr >> 8) & 0xff, addr & 0xff); + goto failed; + } + + qident = (rn->query[0] << 8) + rn->query[1]; + + if (ident != qident) { + ngx_log_error(r->log_level, r->log, 0, + "wrong ident %ui response for %ud.%ud.%ud.%ud, expect %ui", + ident, (addr >> 24) & 0xff, (addr >> 16) & 0xff, + (addr >> 8) & 0xff, addr & 0xff, qident); + goto failed; + } + + if (code == 0 && nan == 0) { + code = 3; /* NXDOMAIN */ + } + + if (code) { + next = rn->waiting; + rn->waiting = NULL; + + ngx_queue_remove(&rn->queue); + + ngx_rbtree_delete(&r->addr_rbtree, &rn->node); + + ngx_resolver_free_node(r, rn); + + /* unlock addr mutex */ + + while (next) { + ctx = next; + ctx->state = code; + next = ctx->next; + + ctx->handler(ctx); + } + + return; + } + + i += sizeof("\7in-addr\4arpa") + sizeof(ngx_resolver_qs_t); + + if (i + 2 + sizeof(ngx_resolver_an_t) > (ngx_uint_t) n) { + goto short_response; + } + + /* compression pointer to "XX.XX.XX.XX.in-addr.arpa */ + + if (buf[i] != 0xc0 || buf[i + 1] != 0x0c) { + err = "invalid in-addr.arpa name in DNS response"; + goto invalid; + } + + an = (ngx_resolver_an_t *) &buf[i + 2]; + + qtype = (an->type_hi << 8) + an->type_lo; + qclass = (an->class_hi << 8) + an->class_lo; + len = (an->len_hi << 8) + an->len_lo; + + ngx_log_debug3(NGX_LOG_DEBUG_CORE, r->log, 0, + "resolver qt:%ui cl:%ui len:%uz", qtype, qclass, len); + + i += 2 + sizeof(ngx_resolver_an_t); + + if (i + len > (ngx_uint_t) n) { + goto short_response; + } + + if (ngx_resolver_copy(r, &name, buf, &buf[i], &buf[n]) != NGX_OK) { + return; + } + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, r->log, 0, "resolver an:%V", &name); + + if (name.len != (size_t) rn->nlen + || ngx_strncmp(name.data, rn->name, name.len) != 0) + { + if (rn->nlen) { + ngx_resolver_free(r, rn->name); + } + + rn->nlen = (u_short) name.len; + rn->name = name.data; + + name.data = ngx_resolver_dup(r, rn->name, name.len); + if (name.data == NULL) { + goto failed; + } + } + + ngx_queue_remove(&rn->queue); + + rn->valid = ngx_time() + r->valid; + rn->expire = ngx_time() + r->expire; + + ngx_queue_insert_head(&r->addr_expire_queue, &rn->queue); + + next = rn->waiting; + rn->waiting = NULL; + + /* unlock addr mutex */ + + while (next) { + ctx = next; + ctx->state = NGX_OK; + ctx->name = name; + next = ctx->next; + + ctx->handler(ctx); + } + + ngx_resolver_free(r, name.data); + + return; + +invalid_in_addr_arpa: + + ngx_log_error(r->log_level, r->log, 0, + "invalid in-addr.arpa name in DNS response"); + return; + +short_response: + + err = "short DNS response"; + +invalid: + + /* unlock addr mutex */ + + ngx_log_error(r->log_level, r->log, 0, err); + + return; + +failed: + + /* unlock addr mutex */ + + return; +} + + +static ngx_resolver_node_t * +ngx_resolver_lookup_name(ngx_resolver_t *r, ngx_str_t *name, uint32_t hash) +{ + ngx_int_t rc; + ngx_rbtree_node_t *node, *sentinel; + ngx_resolver_node_t *rn; + + node = r->name_rbtree.root; + sentinel = r->name_rbtree.sentinel; + + while (node != sentinel) { + + if (hash < node->key) { + node = node->left; + continue; + } + + if (hash > node->key) { + node = node->right; + continue; + } + + /* hash == node->key */ + + do { + rn = (ngx_resolver_node_t *) node; + + rc = ngx_memn2cmp(name->data, rn->name, name->len, rn->nlen); + + if (rc == 0) { + return rn; + } + + node = (rc < 0) ? node->left : node->right; + + } while (node != sentinel && hash == node->key); + + break; + } + + /* not found */ + + return NULL; +} + + +static ngx_resolver_node_t * +ngx_resolver_lookup_addr(ngx_resolver_t *r, in_addr_t addr) +{ + ngx_rbtree_node_t *node, *sentinel; + + node = r->addr_rbtree.root; + sentinel = r->addr_rbtree.sentinel; + + while (node != sentinel) { + + if (addr < node->key) { + node = node->left; + continue; + } + + if (addr > node->key) { + node = node->right; + continue; + } + + /* addr == node->key */ + + return (ngx_resolver_node_t *) node; + } + + /* not found */ + + return NULL; +} + + +static void +ngx_resolver_rbtree_insert_value(ngx_rbtree_node_t *temp, + ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel) +{ + ngx_rbtree_node_t **p; + ngx_resolver_node_t *rn, *rn_temp; + + for ( ;; ) { + + if (node->key < temp->key) { + + p = &temp->left; + + } else if (node->key > temp->key) { + + p = &temp->right; + + } else { /* node->key == temp->key */ + + rn = (ngx_resolver_node_t *) node; + rn_temp = (ngx_resolver_node_t *) temp; + + p = (ngx_memn2cmp(rn->name, rn_temp->name, rn->nlen, rn_temp->nlen) + < 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_resolver_create_name_query(ngx_resolver_node_t *rn, ngx_resolver_ctx_t *ctx) +{ + u_char *p, *s; + size_t len, nlen; + ngx_uint_t ident; + ngx_resolver_qs_t *qs; + ngx_resolver_query_t *query; + + nlen = ctx->name.len ? (1 + ctx->name.len + 1) : 1; + + len = sizeof(ngx_resolver_query_t) + nlen + sizeof(ngx_resolver_qs_t); + + p = ngx_resolver_alloc(ctx->resolver, len); + if (p == NULL) { + return NGX_ERROR; + } + + rn->qlen = (u_short) len; + rn->query = p; + + query = (ngx_resolver_query_t *) p; + + ident = ngx_random(); + + ngx_log_debug2(NGX_LOG_DEBUG_CORE, ctx->resolver->log, 0, + "resolve: \"%V\" %i", &ctx->name, ident & 0xffff); + + query->ident_hi = (u_char) ((ident >> 8) & 0xff); + query->ident_lo = (u_char) (ident & 0xff); + + /* recursion query */ + query->flags_hi = 1; query->flags_lo = 0; + + /* one question */ + query->nqs_hi = 0; query->nqs_lo = 1; + query->nan_hi = 0; query->nan_lo = 0; + query->nns_hi = 0; query->nns_lo = 0; + query->nar_hi = 0; query->nar_lo = 0; + + p += sizeof(ngx_resolver_query_t) + nlen; + + qs = (ngx_resolver_qs_t *) p; + + /* query type */ + qs->type_hi = 0; qs->type_lo = (u_char) ctx->type; + + /* IP query class */ + qs->class_hi = 0; qs->class_lo = 1; + + /* convert "www.example.com" to "\3www\7example\3com\0" */ + + len = 0; + p--; + *p-- = '\0'; + + for (s = ctx->name.data + ctx->name.len - 1; s >= ctx->name.data; s--) { + if (*s != '.') { + *p = *s; + len++; + + } else { + if (len == 0) { + return NGX_DECLINED; + } + + *p = (u_char) len; + len = 0; + } + + p--; + } + + *p = (u_char) len; + + return NGX_OK; +} + + +/* AF_INET only */ + +static ngx_int_t +ngx_resolver_create_addr_query(ngx_resolver_node_t *rn, ngx_resolver_ctx_t *ctx) +{ + u_char *p, *d; + size_t len; + ngx_int_t n; + ngx_uint_t ident; + ngx_resolver_query_t *query; + + len = sizeof(ngx_resolver_query_t) + + sizeof(".255.255.255.255.in-addr.arpa.") - 1 + + sizeof(ngx_resolver_qs_t); + + p = ngx_resolver_alloc(ctx->resolver, len); + if (p == NULL) { + return NGX_ERROR; + } + + rn->query = p; + query = (ngx_resolver_query_t *) p; + + ident = ngx_random(); + + query->ident_hi = (u_char) ((ident >> 8) & 0xff); + query->ident_lo = (u_char) (ident & 0xff); + + /* recursion query */ + query->flags_hi = 1; query->flags_lo = 0; + + /* one question */ + query->nqs_hi = 0; query->nqs_lo = 1; + query->nan_hi = 0; query->nan_lo = 0; + query->nns_hi = 0; query->nns_lo = 0; + query->nar_hi = 0; query->nar_lo = 0; + + p += sizeof(ngx_resolver_query_t); + + for (n = 0; n < 32; n += 8) { + d = ngx_sprintf(&p[1], "%ud", (ctx->addr >> n) & 0xff); + *p = (u_char) (d - &p[1]); + p = d; + } + + /* query type "PTR", IP query class */ + ngx_memcpy(p, "\7in-addr\4arpa\0\0\14\0\1", 18); + + rn->qlen = (u_short) + (p + sizeof("\7in-addr\4arpa") + sizeof(ngx_resolver_qs_t) + - rn->query); + + return NGX_OK; +} + + +static ngx_int_t +ngx_resolver_copy(ngx_resolver_t *r, ngx_str_t *name, u_char *buf, u_char *src, + u_char *last) +{ + char *err; + u_char *p, *dst; + ssize_t len; + ngx_uint_t i, n; + + p = src; + len = -1; + + /* + * compression pointers allow to create endless loop, so we set limit; + * 128 pointers should be enough to store 255-byte name + */ + + for (i = 0; i < 128; i++) { + n = *p++; + + if (n == 0) { + goto done; + } + + if (n & 0xc0) { + n = ((n & 0x3f) << 8) + *p; + p = &buf[n]; + + } else { + len += 1 + n; + p = &p[n]; + } + + if (p >= last) { + err = "name is out of response"; + goto invalid; + } + } + + err = "compression pointers loop"; + +invalid: + + ngx_log_error(r->log_level, r->log, 0, err); + + return NGX_ERROR; + +done: + + if (name == NULL) { + return NGX_OK; + } + + if (len == -1) { + name->len = 0; + name->data = NULL; + return NGX_OK; + } + + dst = ngx_resolver_alloc(r, len); + if (dst == NULL) { + return NGX_ERROR; + } + + name->data = dst; + + n = *src++; + + for ( ;; ) { + if (n != 0xc0) { + ngx_memcpy(dst, src, n); + dst += n; + src += n; + + n = *src++; + + if (n != 0) { + *dst++ = '.'; + } + + } else { + n = ((n & 0x3f) << 8) + *src; + src = &buf[n]; + + n = *src++; + } + + if (n == 0) { + name->len = dst - name->data; + return NGX_OK; + } + } +} + + +static void +ngx_resolver_timeout_handler(ngx_event_t *ev) +{ + ngx_resolver_ctx_t *ctx; + + ctx = ev->data; + + ctx->state = NGX_RESOLVE_TIMEDOUT; + + ctx->handler(ctx); +} + + +static void +ngx_resolver_free_node(ngx_resolver_t *r, ngx_resolver_node_t *rn) +{ + /* lock alloc mutex */ + + if (rn->query) { + ngx_resolver_free_locked(r, rn->query); + } + + if (rn->name) { + ngx_resolver_free_locked(r, rn->name); + } + + if (rn->cnlen) { + ngx_resolver_free_locked(r, rn->u.cname); + } + + if (rn->naddrs > 1) { + ngx_resolver_free_locked(r, rn->u.addrs); + } + + ngx_resolver_free_locked(r, rn); + + /* unlock alloc mutex */ +} + + +static void * +ngx_resolver_alloc(ngx_resolver_t *r, size_t size) +{ + u_char *p; + + /* lock alloc mutex */ + + p = ngx_alloc(size, r->log); + + /* unlock alloc mutex */ + + return p; +} + + +static void * +ngx_resolver_calloc(ngx_resolver_t *r, size_t size) +{ + u_char *p; + + p = ngx_resolver_alloc(r, size); + + if (p) { + ngx_memzero(p, size); + } + + return p; +} + + +static void +ngx_resolver_free(ngx_resolver_t *r, void *p) +{ + /* lock alloc mutex */ + + ngx_free(p); + + /* unlock alloc mutex */ +} + + +static void +ngx_resolver_free_locked(ngx_resolver_t *r, void *p) +{ + ngx_free(p); +} + + +static void * +ngx_resolver_dup(ngx_resolver_t *r, void *src, size_t size) +{ + void *dst; + + dst = ngx_resolver_alloc(r, size); + + if (dst == NULL) { + return dst; + } + + ngx_memcpy(dst, src, size); + + return dst; +} + + +char * +ngx_resolver_strerror(ngx_int_t err) +{ + static char *errors[] = { + "Format error", /* FORMERR */ + "Server failure", /* SERVFAIL */ + "Host not found", /* NXDOMAIN */ + "Unimplemented", /* NOTIMP */ + "Operation refused" /* REFUSED */ + }; + + if (err > 0 && err < 6) { + return errors[err - 1]; + } + + if (err == NGX_RESOLVE_TIMEDOUT) { + return "Operation timed out"; + } + + return "Unknown error"; +} + + +static u_char * +ngx_resolver_log_error(ngx_log_t *log, u_char *buf, size_t len) +{ + u_char *p; + ngx_udp_connection_t *uc; + + p = buf; + + if (log->action) { + p = ngx_snprintf(buf, len, " while %s", log->action); + len -= p - buf; + } + + uc = log->data; + + if (uc) { + p = ngx_snprintf(p, len, ", resolver: %V", &uc->server); + } + + return p; +} + + +ngx_int_t +ngx_udp_connect(ngx_udp_connection_t *uc) +{ + int rc; + ngx_int_t event; + ngx_event_t *rev, *wev; + ngx_socket_t s; + ngx_connection_t *c; + + s = ngx_socket(AF_INET, SOCK_DGRAM, 0); + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, &uc->log, 0, "UDP socket %d", s); + + if (s == -1) { + ngx_log_error(NGX_LOG_ALERT, &uc->log, ngx_socket_errno, + ngx_socket_n " failed"); + return NGX_ERROR; + } + + c = ngx_get_connection(s, &uc->log); + + if (c == NULL) { + if (ngx_close_socket(s) == -1) { + ngx_log_error(NGX_LOG_ALERT, &uc->log, ngx_socket_errno, + ngx_close_socket_n "failed"); + } + + return NGX_ERROR; + } + + if (ngx_nonblocking(s) == -1) { + ngx_log_error(NGX_LOG_ALERT, &uc->log, ngx_socket_errno, + ngx_nonblocking_n " failed"); + + ngx_free_connection(c); + + if (ngx_close_socket(s) == -1) { + ngx_log_error(NGX_LOG_ALERT, &uc->log, ngx_socket_errno, + ngx_close_socket_n " failed"); + } + + return NGX_ERROR; + } + + rev = c->read; + wev = c->write; + + rev->log = &uc->log; + wev->log = &uc->log; + + uc->connection = c; + + c->number = ngx_atomic_fetch_add(ngx_connection_counter, 1); + +#if (NGX_THREADS) + + /* TODO: lock event when call completion handler */ + + rev->lock = &c->lock; + wev->lock = &c->lock; + rev->own_lock = &c->lock; + wev->own_lock = &c->lock; + +#endif + + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, &uc->log, 0, + "connect to %V, fd:%d #%d", &uc->server, s, c->number); + + rc = connect(s, uc->sockaddr, uc->socklen); + + /* TODO: aio, iocp */ + + if (rc == -1) { + ngx_log_error(NGX_LOG_CRIT, &uc->log, ngx_socket_errno, + "connect() failed"); + + return NGX_ERROR; + } + + /* UDP sockets are always ready to write */ + wev->ready = 1; + + if (ngx_add_event) { + + event = (ngx_event_flags & NGX_USE_CLEAR_EVENT) ? + /* kqueue, epoll */ NGX_CLEAR_EVENT: + /* select, poll, /dev/poll */ NGX_LEVEL_EVENT; + /* eventport event type has no meaning: oneshot only */ + + if (ngx_add_event(rev, NGX_READ_EVENT, event) != NGX_OK) { + return NGX_ERROR; + } + + } else { + /* rtsig */ + + if (ngx_add_conn(c) == NGX_ERROR) { + return NGX_ERROR; + } + } + + return NGX_OK; +} diff -Nru nginx-0.5.33/src/core/ngx_resolver.h nginx-0.8.53/src/core/ngx_resolver.h --- nginx-0.5.33/src/core/ngx_resolver.h 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/src/core/ngx_resolver.h 2010-01-11 11:01:02.000000000 +0000 @@ -0,0 +1,148 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include + + +#ifndef _NGX_RESOLVER_H_INCLUDED_ +#define _NGX_RESOLVER_H_INCLUDED_ + + +#define NGX_RESOLVE_A 1 +#define NGX_RESOLVE_CNAME 5 +#define NGX_RESOLVE_PTR 12 +#define NGX_RESOLVE_MX 15 +#define NGX_RESOLVE_TXT 16 +#define NGX_RESOLVE_DNAME 39 + +#define NGX_RESOLVE_FORMERR 1 +#define NGX_RESOLVE_SERVFAIL 2 +#define NGX_RESOLVE_NXDOMAIN 3 +#define NGX_RESOLVE_NOTIMP 4 +#define NGX_RESOLVE_REFUSED 5 +#define NGX_RESOLVE_TIMEDOUT NGX_ETIMEDOUT + + +#define NGX_NO_RESOLVER (void *) -1 + +#define NGX_RESOLVER_MAX_RECURSION 50 + + +typedef struct { + ngx_connection_t *connection; + struct sockaddr *sockaddr; + socklen_t socklen; + ngx_str_t server; + ngx_log_t log; +} ngx_udp_connection_t; + + +typedef struct ngx_resolver_ctx_s ngx_resolver_ctx_t; + +typedef void (*ngx_resolver_handler_pt)(ngx_resolver_ctx_t *ctx); + + +typedef struct { + ngx_rbtree_node_t node; + ngx_queue_t queue; + + /* PTR: resolved name, A: name to resolve */ + u_char *name; + + u_short nlen; + u_short qlen; + + u_char *query; + + union { + in_addr_t addr; + in_addr_t *addrs; + u_char *cname; + } u; + + u_short naddrs; + u_short cnlen; + + time_t expire; + time_t valid; + + ngx_resolver_ctx_t *waiting; +} ngx_resolver_node_t; + + +typedef struct { + /* has to be pointer because of "incomplete type" */ + ngx_event_t *event; + + /* TODO: DNS peers balancer */ + /* STUB */ + ngx_udp_connection_t *udp_connection; + + ngx_log_t *log; + + /* ident must be after 3 pointers */ + ngx_int_t ident; + + ngx_rbtree_t name_rbtree; + ngx_rbtree_node_t name_sentinel; + + ngx_rbtree_t addr_rbtree; + ngx_rbtree_node_t addr_sentinel; + + ngx_queue_t name_resend_queue; + ngx_queue_t addr_resend_queue; + + ngx_queue_t name_expire_queue; + ngx_queue_t addr_expire_queue; + + time_t resend_timeout; + time_t expire; + time_t valid; + + ngx_uint_t log_level; +} ngx_resolver_t; + + +struct ngx_resolver_ctx_s { + ngx_resolver_ctx_t *next; + ngx_resolver_t *resolver; + ngx_udp_connection_t *udp_connection; + + /* ident must be after 3 pointers */ + ngx_int_t ident; + + ngx_int_t state; + ngx_int_t type; + ngx_str_t name; + + ngx_uint_t naddrs; + in_addr_t *addrs; + in_addr_t addr; + + /* TODO: DNS peers balancer ctx */ + + ngx_resolver_handler_pt handler; + void *data; + ngx_msec_t timeout; + + ngx_uint_t quick; /* unsigned quick:1; */ + ngx_uint_t recursion; + ngx_event_t *event; +}; + + +ngx_resolver_t *ngx_resolver_create(ngx_conf_t *cf, ngx_addr_t *addr); +ngx_resolver_ctx_t *ngx_resolve_start(ngx_resolver_t *r, + ngx_resolver_ctx_t *temp); +ngx_int_t ngx_resolve_name(ngx_resolver_ctx_t *ctx); +void ngx_resolve_name_done(ngx_resolver_ctx_t *ctx); +ngx_int_t ngx_resolve_addr(ngx_resolver_ctx_t *ctx); +void ngx_resolve_addr_done(ngx_resolver_ctx_t *ctx); +char *ngx_resolver_strerror(ngx_int_t err); + + +#endif /* _NGX_RESOLVER_H_INCLUDED_ */ diff -Nru nginx-0.5.33/src/core/ngx_sha1.h nginx-0.8.53/src/core/ngx_sha1.h --- nginx-0.5.33/src/core/ngx_sha1.h 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/src/core/ngx_sha1.h 2007-10-22 15:22:08.000000000 +0000 @@ -0,0 +1,30 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#ifndef _NGX_SHA1_H_INCLUDED_ +#define _NGX_SHA1_H_INCLUDED_ + + +#include +#include + + +#if (NGX_HAVE_OPENSSL_SHA1_H) +#include +#else +#include +#endif + + +typedef SHA_CTX ngx_sha1_t; + + +#define ngx_sha1_init SHA1_Init +#define ngx_sha1_update SHA1_Update +#define ngx_sha1_final SHA1_Final + + +#endif /* _NGX_SHA1_H_INCLUDED_ */ diff -Nru nginx-0.5.33/src/core/ngx_shmtx.h nginx-0.8.53/src/core/ngx_shmtx.h --- nginx-0.5.33/src/core/ngx_shmtx.h 2007-02-14 13:52:47.000000000 +0000 +++ nginx-0.8.53/src/core/ngx_shmtx.h 2009-03-13 14:53:30.000000000 +0000 @@ -57,7 +57,15 @@ return 0; } - ngx_log_abort(err, ngx_trylock_fd_n " failed"); +#if __osf__ /* Tru64 UNIX */ + + if (err == NGX_EACCESS) { + return 0; + } + +#endif + + ngx_log_abort(err, ngx_trylock_fd_n " %s failed", mtx->name); return 0; } @@ -74,7 +82,7 @@ return; } - ngx_log_abort(err, ngx_lock_fd_n " failed"); + ngx_log_abort(err, ngx_lock_fd_n " %s failed", mtx->name); } @@ -89,7 +97,7 @@ return; } - ngx_log_abort(err, ngx_unlock_fd_n " failed"); + ngx_log_abort(err, ngx_unlock_fd_n " %s failed", mtx->name); } diff -Nru nginx-0.5.33/src/core/ngx_slab.c nginx-0.8.53/src/core/ngx_slab.c --- nginx-0.5.33/src/core/ngx_slab.c 2007-01-11 19:13:46.000000000 +0000 +++ nginx-0.8.53/src/core/ngx_slab.c 2009-04-15 19:44:41.000000000 +0000 @@ -6,23 +6,6 @@ #include #include -/* - - 12 - 2048 2 11 - 1024 4 10 - 512 8 9 - 256 16 8 - - 128 32 4 32 7 - - 64 64 8 63 6 1 - 32 128 16 127 5 1 - 16 256 32 254 4 2 - 8 512 64 504 3 8 - - */ - #define NGX_SLAB_PAGE_MASK 3 #define NGX_SLAB_PAGE 0 @@ -58,15 +41,30 @@ #if (NGX_DEBUG_MALLOC) -#define ngx_slab_junk(p, size) ngx_memset(p, 0xD0, size) + +#define ngx_slab_junk(p, size) ngx_memset(p, 0xD0, size) + +#else + +#if (NGX_FREEBSD) + +#define ngx_slab_junk(p, size) \ + if (ngx_freebsd_debug_malloc) ngx_memset(p, 0xD0, size) + #else + #define ngx_slab_junk(p, size) + +#endif + #endif static ngx_slab_page_t *ngx_slab_alloc_pages(ngx_slab_pool_t *pool, ngx_uint_t pages); static void ngx_slab_free_pages(ngx_slab_pool_t *pool, ngx_slab_page_t *page, ngx_uint_t pages); +static void ngx_slab_error(ngx_slab_pool_t *pool, ngx_uint_t level, + char *text); static ngx_uint_t ngx_slab_max_size; @@ -111,10 +109,7 @@ p += n * sizeof(ngx_slab_page_t); - /* STUB: possible overflow on 64-bit platform */ - pages = (ngx_uint_t) ((uint64_t) size * ngx_pagesize - / (ngx_pagesize + sizeof(ngx_slab_page_t)) - / ngx_pagesize); + pages = (ngx_uint_t) (size / (ngx_pagesize + sizeof(ngx_slab_page_t))); ngx_memzero(p, pages * sizeof(ngx_slab_page_t)); @@ -137,11 +132,8 @@ pool->pages->slab = pages; } -#if 0 - ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "slab: %p, %p, %ui, %d", - pool, pool->start, pages, - (pool->end - pool->start) / ngx_pagesize - pages); -#endif + pool->log_ctx = &pool->zero; + pool->zero = '\0'; } @@ -428,8 +420,7 @@ ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, ngx_cycle->log, 0, "slab free: %p", p); if ((u_char *) p < pool->start || (u_char *) p > pool->end) { - ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, - "ngx_slab_free(): outside of pool"); + ngx_slab_error(pool, NGX_LOG_ALERT, "ngx_slab_free(): outside of pool"); goto fail; } @@ -577,14 +568,14 @@ } if (slab == NGX_SLAB_PAGE_FREE) { - ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, - "ngx_slab_free(): page is already free"); + ngx_slab_error(pool, NGX_LOG_ALERT, + "ngx_slab_free(): page is already free"); goto fail; } if (slab == NGX_SLAB_PAGE_BUSY) { - ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, - "ngx_slab_free(): pointer to wrong page"); + ngx_slab_error(pool, NGX_LOG_ALERT, + "ngx_slab_free(): pointer to wrong page"); goto fail; } @@ -593,9 +584,9 @@ ngx_slab_free_pages(pool, &pool->pages[n], size); - size <<= ngx_pagesize_shift; + ngx_slab_junk(p, size << ngx_pagesize_shift); - goto done; + return; } /* not reached */ @@ -610,15 +601,15 @@ wrong_chunk: - ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, - "ngx_slab_free(): pointer to wrong chunk"); + ngx_slab_error(pool, NGX_LOG_ALERT, + "ngx_slab_free(): pointer to wrong chunk"); goto fail; chunk_already_free: - ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, - "ngx_slab_free(): chunk is already free"); + ngx_slab_error(pool, NGX_LOG_ALERT, + "ngx_slab_free(): chunk is already free"); fail: @@ -651,11 +642,8 @@ } page->slab = pages | NGX_SLAB_PAGE_START; - -#if (NGX_DEBUG) page->next = NULL; page->prev = NGX_SLAB_PAGE; -#endif if (--pages == 0) { return page; @@ -663,10 +651,8 @@ for (p = page + 1; pages; pages--) { p->slab = NGX_SLAB_PAGE_BUSY; -#if (NGX_DEBUG) p->next = NULL; p->prev = NGX_SLAB_PAGE; -#endif p++; } @@ -674,8 +660,7 @@ } } - ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, NGX_ENOMEM, - "ngx_slab_alloc(): failed"); + ngx_slab_error(pool, NGX_LOG_CRIT, "ngx_slab_alloc() failed: no memory"); return NULL; } @@ -706,3 +691,10 @@ pool->free.next = page; } + + +static void +ngx_slab_error(ngx_slab_pool_t *pool, ngx_uint_t level, char *text) +{ + ngx_log_error(level, ngx_cycle->log, 0, "%s%s", text, pool->log_ctx); +} diff -Nru nginx-0.5.33/src/core/ngx_slab.h nginx-0.8.53/src/core/ngx_slab.h --- nginx-0.5.33/src/core/ngx_slab.h 2007-01-02 23:10:42.000000000 +0000 +++ nginx-0.8.53/src/core/ngx_slab.h 2009-06-02 13:57:59.000000000 +0000 @@ -34,6 +34,12 @@ u_char *end; ngx_shmtx_t mutex; + + u_char *log_ctx; + u_char zero; + + void *data; + void *addr; } ngx_slab_pool_t; diff -Nru nginx-0.5.33/src/core/ngx_string.c nginx-0.8.53/src/core/ngx_string.c --- nginx-0.5.33/src/core/ngx_string.c 2007-10-29 15:10:31.000000000 +0000 +++ nginx-0.8.53/src/core/ngx_string.c 2010-09-02 14:37:16.000000000 +0000 @@ -8,6 +8,24 @@ #include +static u_char *ngx_sprintf_num(u_char *buf, u_char *last, uint64_t ui64, + u_char zero, ngx_uint_t hexadecimal, ngx_uint_t width); +static ngx_int_t ngx_decode_base64_internal(ngx_str_t *dst, ngx_str_t *src, + const u_char *basis); + + +void +ngx_strlow(u_char *dst, u_char *src, size_t n) +{ + while (n) { + *dst = ngx_tolower(*src); + dst++; + src++; + n--; + } +} + + u_char * ngx_cpystrn(u_char *dst, u_char *src, size_t n) { @@ -15,12 +33,15 @@ return dst; } - for ( /* void */ ; --n; dst++, src++) { + while (--n) { *dst = *src; if (*dst == '\0') { return dst; } + + dst++; + src++; } *dst = '\0'; @@ -34,7 +55,7 @@ { u_char *dst; - dst = ngx_palloc(pool, src->len); + dst = ngx_pnalloc(pool, src->len); if (dst == NULL) { return NULL; } @@ -56,6 +77,7 @@ * %[0][width][u][x|X]D int32_t/uint32_t * %[0][width][u][x|X]L int64_t/uint64_t * %[0][width|m][u][x|X]A ngx_atomic_int_t/ngx_atomic_uint_t + * %[0][width][.width]f double, max valid number fits to %18.15f * %P ngx_pid_t * %M ngx_msec_t * %r rlim_t @@ -63,6 +85,7 @@ * %V ngx_str_t * * %v ngx_variable_value_t * * %s null-terminated string + * %*s length and string * %Z '\0' * %N '\n' * %c char @@ -70,7 +93,7 @@ * * reserved: * %t ptrdiff_t - * %S null-teminated wchar string + * %S null-terminated wchar string * %C wchar */ @@ -82,7 +105,7 @@ va_list args; va_start(args, fmt); - p = ngx_vsnprintf(buf, /* STUB */ 65536, fmt, args); + p = ngx_vslprintf(buf, (void *) -1, fmt, args); va_end(args); return p; @@ -96,7 +119,21 @@ va_list args; va_start(args, fmt); - p = ngx_vsnprintf(buf, max, fmt, args); + p = ngx_vslprintf(buf, buf + max, fmt, args); + va_end(args); + + return p; +} + + +u_char * ngx_cdecl +ngx_slprintf(u_char *buf, u_char *last, const char *fmt, ...) +{ + u_char *p; + va_list args; + + va_start(args, fmt); + p = ngx_vslprintf(buf, last, fmt, args); va_end(args); return p; @@ -104,30 +141,18 @@ u_char * -ngx_vsnprintf(u_char *buf, size_t max, const char *fmt, va_list args) +ngx_vslprintf(u_char *buf, u_char *last, const char *fmt, va_list args) { - u_char *p, zero, *last, temp[NGX_INT64_LEN + 1]; - /* - * really we need temp[NGX_INT64_LEN] only, - * but icc issues the warning - */ + u_char *p, zero; int d; - size_t len; - uint32_t ui32; + double f, scale; + size_t len, slen; int64_t i64; uint64_t ui64; ngx_msec_t ms; - ngx_uint_t width, sign, hexadecimal, max_width; + ngx_uint_t width, sign, hex, max_width, frac_width, n; ngx_str_t *v; ngx_variable_value_t *vv; - static u_char hex[] = "0123456789abcdef"; - static u_char HEX[] = "0123456789ABCDEF"; - - if (max == 0) { - return buf; - } - - last = buf + max; while (*fmt && buf < last) { @@ -144,10 +169,10 @@ zero = (u_char) ((*++fmt == '0') ? '0' : ' '); width = 0; sign = 1; - hexadecimal = 0; + hex = 0; max_width = 0; - - p = temp + NGX_INT64_LEN; + frac_width = 0; + slen = (size_t) -1; while (*fmt >= '0' && *fmt <= '9') { width = width * 10 + *fmt++ - '0'; @@ -168,17 +193,31 @@ continue; case 'X': - hexadecimal = 2; + hex = 2; sign = 0; fmt++; continue; case 'x': - hexadecimal = 1; + hex = 1; sign = 0; fmt++; continue; + case '.': + fmt++; + + while (*fmt >= '0' && *fmt <= '9') { + frac_width = frac_width * 10 + *fmt++ - '0'; + } + + break; + + case '*': + slen = va_arg(args, size_t); + fmt++; + continue; + default: break; } @@ -192,9 +231,7 @@ case 'V': v = va_arg(args, ngx_str_t *); - len = v->len; - len = (buf + len < last) ? len : (size_t) (last - buf); - + len = ngx_min(((size_t) (last - buf)), v->len); buf = ngx_cpymem(buf, v->data, len); fmt++; @@ -203,9 +240,7 @@ case 'v': vv = va_arg(args, ngx_variable_value_t *); - len = vv->len; - len = (buf + len < last) ? len : (size_t) (last - buf); - + len = ngx_min(((size_t) (last - buf)), vv->len); buf = ngx_cpymem(buf, vv->data, len); fmt++; @@ -214,9 +249,16 @@ case 's': p = va_arg(args, u_char *); - while (*p && buf < last) { - *buf++ = *p++; + if (slen == (size_t) -1) { + while (*p && buf < last) { + *buf++ = *p++; + } + + } else { + len = ngx_min(((size_t) (last - buf)), slen); + buf = ngx_cpymem(buf, p, len); } + fmt++; continue; @@ -313,6 +355,43 @@ break; + case 'f': + f = va_arg(args, double); + + if (f < 0) { + *buf++ = '-'; + f = -f; + } + + ui64 = (int64_t) f; + + buf = ngx_sprintf_num(buf, last, ui64, zero, 0, width); + + if (frac_width) { + + if (buf < last) { + *buf++ = '.'; + } + + scale = 1.0; + + for (n = frac_width; n; n--) { + scale *= 10.0; + } + + /* + * (int64_t) cast is required for msvc6: + * it can not convert uint64_t to double + */ + ui64 = (uint64_t) ((f - (int64_t) ui64) * scale + 0.5); + + buf = ngx_sprintf_num(buf, last, ui64, '0', 0, frac_width); + } + + fmt++; + + continue; + #if !(NGX_WIN32) case 'r': i64 = (int64_t) va_arg(args, rlim_t); @@ -322,7 +401,7 @@ case 'p': ui64 = (uintptr_t) va_arg(args, void *); - hexadecimal = 2; + hex = 2; sign = 0; zero = '0'; width = NGX_PTR_SIZE * 2; @@ -372,72 +451,102 @@ } } - if (hexadecimal == 1) { - do { + buf = ngx_sprintf_num(buf, last, ui64, zero, hex, width); - /* the "(uint32_t)" cast disables the BCC's warning */ - *--p = hex[(uint32_t) (ui64 & 0xf)]; + fmt++; - } while (ui64 >>= 4); + } else { + *buf++ = *fmt++; + } + } - } else if (hexadecimal == 2) { - do { + return buf; +} - /* the "(uint32_t)" cast disables the BCC's warning */ - *--p = HEX[(uint32_t) (ui64 & 0xf)]; - } while (ui64 >>= 4); +static u_char * +ngx_sprintf_num(u_char *buf, u_char *last, uint64_t ui64, u_char zero, + ngx_uint_t hexadecimal, ngx_uint_t width) +{ + u_char *p, temp[NGX_INT64_LEN + 1]; + /* + * we need temp[NGX_INT64_LEN] only, + * but icc issues the warning + */ + size_t len; + uint32_t ui32; + static u_char hex[] = "0123456789abcdef"; + static u_char HEX[] = "0123456789ABCDEF"; - } else if (ui64 <= NGX_MAX_UINT32_VALUE) { + p = temp + NGX_INT64_LEN; - /* - * To divide 64-bit number and to find the remainder - * on the x86 platform gcc and icc call the libc functions - * [u]divdi3() and [u]moddi3(), they call another function - * in its turn. On FreeBSD it is the qdivrem() function, - * its source code is about 170 lines of the code. - * The glibc counterpart is about 150 lines of the code. - * - * For 32-bit numbers and some divisors gcc and icc use - * the inlined multiplication and shifts. For example, - * unsigned "i32 / 10" is compiled to - * - * (i32 * 0xCCCCCCCD) >> 35 - */ + if (hexadecimal == 0) { - ui32 = (uint32_t) ui64; + if (ui64 <= NGX_MAX_UINT32_VALUE) { - do { - *--p = (u_char) (ui32 % 10 + '0'); - } while (ui32 /= 10); + /* + * To divide 64-bit numbers and to find remainders + * on the x86 platform gcc and icc call the libc functions + * [u]divdi3() and [u]moddi3(), they call another function + * in its turn. On FreeBSD it is the qdivrem() function, + * its source code is about 170 lines of the code. + * The glibc counterpart is about 150 lines of the code. + * + * For 32-bit numbers and some divisors gcc and icc use + * a inlined multiplication and shifts. For example, + * unsigned "i32 / 10" is compiled to + * + * (i32 * 0xCCCCCCCD) >> 35 + */ + + ui32 = (uint32_t) ui64; + + do { + *--p = (u_char) (ui32 % 10 + '0'); + } while (ui32 /= 10); - } else { - do { - *--p = (u_char) (ui64 % 10 + '0'); - } while (ui64 /= 10); - } + } else { + do { + *--p = (u_char) (ui64 % 10 + '0'); + } while (ui64 /= 10); + } - len = (temp + NGX_INT64_LEN) - p; + } else if (hexadecimal == 1) { - while (len++ < width && buf < last) { - *buf++ = zero; - } + do { - len = (temp + NGX_INT64_LEN) - p; - if (buf + len > last) { - len = last - buf; - } + /* the "(uint32_t)" cast disables the BCC's warning */ + *--p = hex[(uint32_t) (ui64 & 0xf)]; - buf = ngx_cpymem(buf, p, len); + } while (ui64 >>= 4); - fmt++; + } else { /* hexadecimal == 2 */ - } else { - *buf++ = *fmt++; - } + do { + + /* the "(uint32_t)" cast disables the BCC's warning */ + *--p = HEX[(uint32_t) (ui64 & 0xf)]; + + } while (ui64 >>= 4); } - return buf; + /* zero or space padding */ + + len = (temp + NGX_INT64_LEN) - p; + + while (len++ < width && buf < last) { + *buf++ = zero; + } + + /* number safe copy */ + + len = (temp + NGX_INT64_LEN) - p; + + if (buf + len > last) { + len = last - buf; + } + + return ngx_cpymem(buf, p, len); } @@ -457,8 +566,8 @@ c1 = (ngx_uint_t) *s1++; c2 = (ngx_uint_t) *s2++; - c1 = (c1 >= 'A' && c1 <= 'Z') ? (c1 | 0x20) : c1; - c2 = (c2 >= 'A' && c2 <= 'Z') ? (c2 | 0x20) : c2; + c1 = (c1 >= 'A' && c1 <= 'Z') ? (c1 | 0x20) : c1; + c2 = (c2 >= 'A' && c2 <= 'Z') ? (c2 | 0x20) : c2; if (c1 == c2) { @@ -483,8 +592,8 @@ c1 = (ngx_uint_t) *s1++; c2 = (ngx_uint_t) *s2++; - c1 = (c1 >= 'A' && c1 <= 'Z') ? (c1 | 0x20) : c1; - c2 = (c2 >= 'A' && c2 <= 'Z') ? (c2 | 0x20) : c2; + c1 = (c1 >= 'A' && c1 <= 'Z') ? (c1 | 0x20) : c1; + c2 = (c2 >= 'A' && c2 <= 'Z') ? (c2 | 0x20) : c2; if (c1 == c2) { @@ -572,7 +681,7 @@ ngx_uint_t c1, c2; c2 = (ngx_uint_t) *s2++; - c2 = (c2 >= 'A' && c2 <= 'Z') ? (c2 | 0x20) : c2; + c2 = (c2 >= 'A' && c2 <= 'Z') ? (c2 | 0x20) : c2; do { do { @@ -582,7 +691,7 @@ return NULL; } - c1 = (c1 >= 'A' && c1 <= 'Z') ? (c1 | 0x20) : c1; + c1 = (c1 >= 'A' && c1 <= 'Z') ? (c1 | 0x20) : c1; } while (c1 != c2); @@ -592,6 +701,39 @@ } +/* + * ngx_strlcasestrn() is intended to search for static substring + * with known length in string until the argument last. The argument n + * must be length of the second substring - 1. + */ + +u_char * +ngx_strlcasestrn(u_char *s1, u_char *last, u_char *s2, size_t n) +{ + ngx_uint_t c1, c2; + + c2 = (ngx_uint_t) *s2++; + c2 = (c2 >= 'A' && c2 <= 'Z') ? (c2 | 0x20) : c2; + last -= n; + + do { + do { + if (s1 >= last) { + return NULL; + } + + c1 = (ngx_uint_t) *s1++; + + c1 = (c1 >= 'A' && c1 <= 'Z') ? (c1 | 0x20) : c1; + + } while (c1 != c2); + + } while (ngx_strncasecmp(s1, s2, n) != 0); + + return --s1; +} + + ngx_int_t ngx_rstrncmp(u_char *s1, u_char *s2, size_t n) { @@ -676,6 +818,37 @@ ngx_int_t +ngx_dns_strcmp(u_char *s1, u_char *s2) +{ + ngx_uint_t c1, c2; + + for ( ;; ) { + c1 = (ngx_uint_t) *s1++; + c2 = (ngx_uint_t) *s2++; + + c1 = (c1 >= 'A' && c1 <= 'Z') ? (c1 | 0x20) : c1; + c2 = (c2 >= 'A' && c2 <= 'Z') ? (c2 | 0x20) : c2; + + if (c1 == c2) { + + if (c1) { + continue; + } + + return 0; + } + + /* in ASCII '.' > '-', but we need '.' to be the lowest character */ + + c1 = (c1 == '.') ? ' ' : c1; + c2 = (c2 == '.') ? ' ' : c2; + + return c1 - c2; + } +} + + +ngx_int_t ngx_atoi(u_char *line, size_t n) { ngx_int_t value; @@ -701,6 +874,56 @@ } +/* parse a fixed point number, e.g., ngx_atofp("10.5", 4, 2) returns 1050 */ + +ngx_int_t +ngx_atofp(u_char *line, size_t n, size_t point) +{ + ngx_int_t value; + ngx_uint_t dot; + + if (n == 0) { + return NGX_ERROR; + } + + dot = 0; + + for (value = 0; n--; line++) { + + if (point == 0) { + return NGX_ERROR; + } + + if (*line == '.') { + if (dot) { + return NGX_ERROR; + } + + dot = 1; + continue; + } + + if (*line < '0' || *line > '9') { + return NGX_ERROR; + } + + value = value * 10 + (*line - '0'); + point -= dot; + } + + while (point--) { + value = value * 10; + } + + if (value < 0) { + return NGX_ERROR; + + } else { + return value; + } +} + + ssize_t ngx_atosz(u_char *line, size_t n) { @@ -816,18 +1039,17 @@ } -void -ngx_md5_text(u_char *text, u_char *md5) +u_char * +ngx_hex_dump(u_char *dst, u_char *src, size_t len) { - int i; static u_char hex[] = "0123456789abcdef"; - for (i = 0; i < 16; i++) { - *text++ = hex[md5[i] >> 4]; - *text++ = hex[md5[i] & 0xf]; + while (len--) { + *dst++ = hex[*src >> 4]; + *dst++ = hex[*src++ & 0xf]; } - *text = '\0'; + return dst; } @@ -875,8 +1097,6 @@ ngx_int_t ngx_decode_base64(ngx_str_t *dst, ngx_str_t *src) { - size_t len; - u_char *d, *s; static u_char basis64[] = { 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, @@ -897,12 +1117,49 @@ 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77 }; + return ngx_decode_base64_internal(dst, src, basis64); +} + + +ngx_int_t +ngx_decode_base64url(ngx_str_t *dst, ngx_str_t *src) +{ + static u_char basis64[] = { + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 62, 77, 77, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 77, 77, 77, 77, 77, 77, + 77, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 77, 77, 77, 77, 63, + 77, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 77, 77, 77, 77, 77, + + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77 + }; + + return ngx_decode_base64_internal(dst, src, basis64); +} + + +static ngx_int_t +ngx_decode_base64_internal(ngx_str_t *dst, ngx_str_t *src, const u_char *basis) +{ + size_t len; + u_char *d, *s; + for (len = 0; len < src->len; len++) { if (src->data[len] == '=') { break; } - if (basis64[src->data[len]] == 77) { + if (basis[src->data[len]] == 77) { return NGX_ERROR; } } @@ -915,20 +1172,20 @@ d = dst->data; while (len > 3) { - *d++ = (u_char) (basis64[s[0]] << 2 | basis64[s[1]] >> 4); - *d++ = (u_char) (basis64[s[1]] << 4 | basis64[s[2]] >> 2); - *d++ = (u_char) (basis64[s[2]] << 6 | basis64[s[3]]); + *d++ = (u_char) (basis[s[0]] << 2 | basis[s[1]] >> 4); + *d++ = (u_char) (basis[s[1]] << 4 | basis[s[2]] >> 2); + *d++ = (u_char) (basis[s[2]] << 6 | basis[s[3]]); s += 4; len -= 4; } if (len > 1) { - *d++ = (u_char) (basis64[s[0]] << 2 | basis64[s[1]] >> 4); + *d++ = (u_char) (basis[s[0]] << 2 | basis[s[1]] >> 4); } if (len > 2) { - *d++ = (u_char) (basis64[s[1]] << 4 | basis64[s[2]] >> 2); + *d++ = (u_char) (basis[s[1]] << 4 | basis[s[2]] >> 2); } dst->len = d - dst->data; @@ -938,16 +1195,16 @@ /* - * ngx_utf_decode() decodes two and more bytes UTF sequences only + * ngx_utf8_decode() decodes two and more bytes UTF sequences only * the return values: * 0x80 - 0x10ffff valid character - * 0x10ffff - 0xfffffffd invalid sequence + * 0x110000 - 0xfffffffd invalid sequence * 0xfffffffe incomplete sequence * 0xffffffff error */ uint32_t -ngx_utf_decode(u_char **p, size_t n) +ngx_utf8_decode(u_char **p, size_t n) { size_t len; uint32_t u, i, valid; @@ -1004,31 +1261,26 @@ size_t -ngx_utf_length(u_char *p, size_t n) +ngx_utf8_length(u_char *p, size_t n) { - u_char c; - size_t len; - ngx_uint_t i; + u_char c, *last; + size_t len; - for (len = 0, i = 0; i < n; len++, i++) { + last = p + n; - c = p[i]; + for (len = 0; p < last; len++) { + + c = *p; if (c < 0x80) { + p++; continue; } - if (c >= 0xc0) { - for (c <<= 1; c & 0x80; c <<= 1) { - i++; - } - - continue; + if (ngx_utf8_decode(&p, n) > 0x10ffff) { + /* invalid UTF-8 */ + return n; } - - /* invalid utf */ - - return n; } return len; @@ -1036,36 +1288,43 @@ u_char * -ngx_utf_cpystrn(u_char *dst, u_char *src, size_t n) +ngx_utf8_cpystrn(u_char *dst, u_char *src, size_t n, size_t len) { - u_char c; + u_char c, *next; if (n == 0) { return dst; } - for ( /* void */ ; --n; dst++, src++) { + while (--n) { c = *src; *dst = c; if (c < 0x80) { - if (*dst != '\0') { + + if (c != '\0') { + dst++; + src++; + len--; + continue; } return dst; } - if (c >= 0xc0) { - for (c <<= 1; c & 0x80; c <<= 1) { - *++dst = *++src; - } + next = src; - continue; + if (ngx_utf8_decode(&next, len) > 0x10ffff) { + /* invalid UTF-8 */ + break; } - /* invalid utf */ + while (src < next) { + *dst++ = *src++; + len--; + } } *dst = '\0'; @@ -1077,7 +1336,7 @@ uintptr_t ngx_escape_uri(u_char *dst, u_char *src, size_t size, ngx_uint_t type) { - ngx_uint_t i, n; + ngx_uint_t n; uint32_t *escape; static u_char hex[] = "0123456789abcdef"; @@ -1101,13 +1360,13 @@ 0xffffffff /* 1111 1111 1111 1111 1111 1111 1111 1111 */ }; - /* " ", "#", "%", "+", "?", %00-%1F, %7F-%FF */ + /* " ", "#", "%", "&", "+", "?", %00-%1F, %7F-%FF */ static uint32_t args[] = { 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */ - 0x80000829, /* 1000 0000 0000 0000 0000 1000 0010 1001 */ + 0x88000869, /* 1000 1000 0000 0000 0000 1000 0110 1001 */ /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */ 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ @@ -1147,7 +1406,7 @@ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */ - 0x000000a5, /* 0000 0000 0000 0000 0000 0000 1010 0101 */ + 0x00000085, /* 0000 0000 0000 0000 0000 0000 1000 0101 */ /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */ 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ @@ -1193,19 +1452,20 @@ /* find the number of the characters to be escaped */ - n = 0; + n = 0; - for (i = 0; i < size; i++) { + while (size) { if (escape[*src >> 5] & (1 << (*src & 0x1f))) { n++; } src++; + size--; } return (uintptr_t) n; } - for (i = 0; i < size; i++) { + while (size) { if (escape[*src >> 5] & (1 << (*src & 0x1f))) { *dst++ = '%'; *dst++ = hex[*src >> 4]; @@ -1215,6 +1475,7 @@ } else { *dst++ = *src++; } + size--; } return (uintptr_t) dst; @@ -1243,7 +1504,9 @@ switch (state) { case sw_usual: - if (ch == '?' && type == NGX_UNESCAPE_URI) { + if (ch == '?' + && (type & (NGX_UNESCAPE_URI|NGX_UNESCAPE_REDIRECT))) + { *d++ = ch; goto done; } @@ -1286,7 +1549,7 @@ if (ch >= '0' && ch <= '9') { ch = (u_char) ((decoded << 4) + ch - '0'); - if (type == NGX_UNESCAPE_URI) { + if (type & NGX_UNESCAPE_REDIRECT) { if (ch > '%' && ch < 0x7f) { *d++ = ch; break; @@ -1306,7 +1569,17 @@ if (c >= 'a' && c <= 'f') { ch = (u_char) ((decoded << 4) + c - 'a' + 10); - if (type == NGX_UNESCAPE_URI) { + if (type & NGX_UNESCAPE_URI) { + if (ch == '?') { + *d++ = ch; + goto done; + } + + *d++ = ch; + break; + } + + if (type & NGX_UNESCAPE_REDIRECT) { if (ch == '?') { *d++ = ch; goto done; @@ -1343,36 +1616,37 @@ ngx_escape_html(u_char *dst, u_char *src, size_t size) { u_char ch; - ngx_uint_t i, len; + ngx_uint_t len; if (dst == NULL) { len = 0; - for (i = 0; i < size; i++) { + while (size) { switch (*src++) { case '<': len += sizeof("<") - 2; - break; + break; case '>': len += sizeof(">") - 2; - break; + break; case '&': len += sizeof("&") - 2; - break; + break; default: break; } + size--; } return (uintptr_t) len; } - for (i = 0; i < size; i++) { + while (size) { ch = *src++; switch (ch) { @@ -1394,36 +1668,126 @@ *dst++ = ch; break; } + size--; } return (uintptr_t) dst; } +void +ngx_str_rbtree_insert_value(ngx_rbtree_node_t *temp, + ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel) +{ + ngx_str_node_t *n, *t; + ngx_rbtree_node_t **p; + + for ( ;; ) { + + n = (ngx_str_node_t *) node; + t = (ngx_str_node_t *) temp; + + if (node->key != temp->key) { + + p = (node->key < temp->key) ? &temp->left : &temp->right; + + } else if (n->str.len != t->str.len) { + + p = (n->str.len < t->str.len) ? &temp->left : &temp->right; + + } else { + p = (ngx_memcmp(n->str.data, t->str.data, n->str.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); +} + + +ngx_str_node_t * +ngx_str_rbtree_lookup(ngx_rbtree_t *rbtree, ngx_str_t *val, uint32_t hash) +{ + ngx_int_t rc; + ngx_str_node_t *n; + ngx_rbtree_node_t *node, *sentinel; + + node = rbtree->root; + sentinel = rbtree->sentinel; + + while (node != sentinel) { + + n = (ngx_str_node_t *) node; + + if (hash != node->key) { + node = (hash < node->key) ? node->left : node->right; + continue; + } + + if (val->len != n->str.len) { + node = (val->len < n->str.len) ? node->left : node->right; + continue; + } + + rc = ngx_memcmp(val->data, n->str.data, val->len); + + if (rc < 0) { + node = node->left; + continue; + } + + if (rc > 0) { + node = node->right; + continue; + } + + return n; + } + + return NULL; +} + + /* ngx_sort() is implemented as insertion sort because we need stable sort */ void ngx_sort(void *base, size_t n, size_t size, - int (*cmp)(const void *, const void *)) + ngx_int_t (*cmp)(const void *, const void *)) { - u_char *p1, *p2; - u_char buf[256]; + u_char *p1, *p2, *p; + + p = ngx_alloc(size, ngx_cycle->log); + if (p == NULL) { + return; + } for (p1 = (u_char *) base + size; p1 < (u_char *) base + n * size; p1 += size) { - ngx_memcpy(buf, p1, size); + ngx_memcpy(p, p1, size); for (p2 = p1; - p2 > (u_char *) base && cmp(p2 - size, buf) > 0; + p2 > (u_char *) base && cmp(p2 - size, p) > 0; p2 -= size) { ngx_memcpy(p2, p2 - size, size); } - ngx_memcpy(p2, buf, size); + ngx_memcpy(p2, p, size); } + + ngx_free(p); } diff -Nru nginx-0.5.33/src/core/ngx_string.h nginx-0.8.53/src/core/ngx_string.h --- nginx-0.5.33/src/core/ngx_string.h 2007-10-29 15:10:31.000000000 +0000 +++ nginx-0.8.53/src/core/ngx_string.h 2010-09-02 14:37:16.000000000 +0000 @@ -25,11 +25,12 @@ typedef struct { - unsigned len:29; + unsigned len:28; unsigned valid:1; - unsigned no_cachable:1; + unsigned no_cacheable:1; unsigned not_found:1; + unsigned escape:1; u_char *data; } ngx_variable_value_t; @@ -37,11 +38,16 @@ #define ngx_string(str) { sizeof(str) - 1, (u_char *) str } #define ngx_null_string { 0, NULL } +#define ngx_str_set(str, text) \ + (str)->len = sizeof(text) - 1; (str)->data = (u_char *) text +#define ngx_str_null(str) (str)->len = 0; (str)->data = NULL #define ngx_tolower(c) (u_char) ((c >= 'A' && c <= 'Z') ? (c | 0x20) : c) #define ngx_toupper(c) (u_char) ((c >= 'a' && c <= 'z') ? (c & ~0x20) : c) +void ngx_strlow(u_char *dst, u_char *src, size_t n); + #define ngx_strncmp(s1, s2, n) strncmp((const char *) s1, (const char *) s2, n) @@ -51,9 +57,25 @@ #define ngx_strstr(s1, s2) strstr((const char *) s1, (const char *) s2) -#define ngx_strchr(s1, c) strchr((const char *) s1, (int) c) #define ngx_strlen(s) strlen((const char *) s) +#define ngx_strchr(s1, c) strchr((const char *) s1, (int) c) + +static ngx_inline u_char * +ngx_strlchr(u_char *p, u_char *last, u_char c) +{ + while (p < last) { + + if (*p == c) { + return p; + } + + p++; + } + + return NULL; +} + /* * msvc and icc7 compile memset() to the inline "rep stos" @@ -67,7 +89,7 @@ #if (NGX_MEMCPY_LIMIT) void *ngx_memcpy(void *dst, void *src, size_t n); -#define ngx_cpymem(dst, src, n) ((u_char *) ngx_memcpy(dst, src, n)) + (n) +#define ngx_cpymem(dst, src, n) (((u_char *) ngx_memcpy(dst, src, n)) + (n)) #else @@ -77,7 +99,7 @@ * icc8 compile memcpy(d, s, 4) to the inline "mov"es or XMM moves. */ #define ngx_memcpy(dst, src, n) (void) memcpy(dst, src, n) -#define ngx_cpymem(dst, src, n) ((u_char *) memcpy(dst, src, n)) + (n) +#define ngx_cpymem(dst, src, n) (((u_char *) memcpy(dst, src, n)) + (n)) #endif @@ -114,14 +136,18 @@ /* msvc and icc7 compile memcmp() to the inline loop */ -#define ngx_memcmp memcmp +#define ngx_memcmp(s1, s2, n) memcmp((const char *) s1, (const char *) s2, n) u_char *ngx_cpystrn(u_char *dst, u_char *src, size_t n); u_char *ngx_pstrdup(ngx_pool_t *pool, ngx_str_t *src); u_char * ngx_cdecl ngx_sprintf(u_char *buf, const char *fmt, ...); u_char * ngx_cdecl ngx_snprintf(u_char *buf, size_t max, const char *fmt, ...); -u_char *ngx_vsnprintf(u_char *buf, size_t max, const char *fmt, va_list args); +u_char * ngx_cdecl ngx_slprintf(u_char *buf, u_char *last, const char *fmt, + ...); +u_char *ngx_vslprintf(u_char *buf, u_char *last, const char *fmt, va_list args); +#define ngx_vsnprintf(buf, max, fmt, args) \ + ngx_vslprintf(buf, buf + (max), fmt, args) ngx_int_t ngx_strcasecmp(u_char *s1, u_char *s2); ngx_int_t ngx_strncasecmp(u_char *s1, u_char *s2, size_t n); @@ -130,18 +156,21 @@ u_char *ngx_strstrn(u_char *s1, char *s2, size_t n); u_char *ngx_strcasestrn(u_char *s1, char *s2, size_t n); +u_char *ngx_strlcasestrn(u_char *s1, u_char *last, u_char *s2, size_t n); ngx_int_t ngx_rstrncmp(u_char *s1, u_char *s2, size_t n); ngx_int_t ngx_rstrncasecmp(u_char *s1, u_char *s2, size_t n); ngx_int_t ngx_memn2cmp(u_char *s1, u_char *s2, size_t n1, size_t n2); +ngx_int_t ngx_dns_strcmp(u_char *s1, u_char *s2); ngx_int_t ngx_atoi(u_char *line, size_t n); +ngx_int_t ngx_atofp(u_char *line, size_t n, size_t point); ssize_t ngx_atosz(u_char *line, size_t n); off_t ngx_atoof(u_char *line, size_t n); time_t ngx_atotm(u_char *line, size_t n); ngx_int_t ngx_hextoi(u_char *line, size_t n); -void ngx_md5_text(u_char *text, u_char *md5); +u_char *ngx_hex_dump(u_char *dst, u_char *src, size_t len); #define ngx_base64_encoded_length(len) (((len + 2) / 3) * 4) @@ -149,20 +178,22 @@ void ngx_encode_base64(ngx_str_t *dst, ngx_str_t *src); ngx_int_t ngx_decode_base64(ngx_str_t *dst, ngx_str_t *src); +ngx_int_t ngx_decode_base64url(ngx_str_t *dst, ngx_str_t *src); -uint32_t ngx_utf_decode(u_char **p, size_t n); -size_t ngx_utf_length(u_char *p, size_t n); -u_char *ngx_utf_cpystrn(u_char *dst, u_char *src, size_t n); - - -#define NGX_ESCAPE_URI 0 -#define NGX_ESCAPE_ARGS 1 -#define NGX_ESCAPE_HTML 2 -#define NGX_ESCAPE_REFRESH 3 -#define NGX_ESCAPE_MEMCACHED 4 -#define NGX_ESCAPE_MAIL_AUTH 5 +uint32_t ngx_utf8_decode(u_char **p, size_t n); +size_t ngx_utf8_length(u_char *p, size_t n); +u_char *ngx_utf8_cpystrn(u_char *dst, u_char *src, size_t n, size_t len); + + +#define NGX_ESCAPE_URI 0 +#define NGX_ESCAPE_ARGS 1 +#define NGX_ESCAPE_HTML 2 +#define NGX_ESCAPE_REFRESH 3 +#define NGX_ESCAPE_MEMCACHED 4 +#define NGX_ESCAPE_MAIL_AUTH 5 -#define NGX_UNESCAPE_URI 1 +#define NGX_UNESCAPE_URI 1 +#define NGX_UNESCAPE_REDIRECT 2 uintptr_t ngx_escape_uri(u_char *dst, u_char *src, size_t size, ngx_uint_t type); @@ -170,9 +201,20 @@ uintptr_t ngx_escape_html(u_char *dst, u_char *src, size_t size); +typedef struct { + ngx_rbtree_node_t node; + ngx_str_t str; +} ngx_str_node_t; + + +void ngx_str_rbtree_insert_value(ngx_rbtree_node_t *temp, + ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel); +ngx_str_node_t *ngx_str_rbtree_lookup(ngx_rbtree_t *rbtree, ngx_str_t *name, + uint32_t hash); + void ngx_sort(void *base, size_t n, size_t size, - int (*cmp)(const void *, const void *)); + ngx_int_t (*cmp)(const void *, const void *)); #define ngx_qsort qsort diff -Nru nginx-0.5.33/src/core/ngx_times.c nginx-0.8.53/src/core/ngx_times.c --- nginx-0.5.33/src/core/ngx_times.c 2007-01-29 11:52:25.000000000 +0000 +++ nginx-0.8.53/src/core/ngx_times.c 2010-05-31 14:44:17.000000000 +0000 @@ -28,6 +28,17 @@ volatile ngx_str_t ngx_cached_http_time; volatile ngx_str_t ngx_cached_http_log_time; +#if !(NGX_WIN32) + +/* + * locatime() and localtime_r() are not Async-Signal-Safe functions, therefore, + * they must not be called by a signal handler, so we use the cached + * GMT offset value. Fortunately the value is changed only two times a year. + */ + +static ngx_int_t cached_gmtoff; +#endif + static ngx_time_t cached_time[NGX_TIME_SLOTS]; static u_char cached_err_log_time[NGX_TIME_SLOTS] [sizeof("1970/09/28 12:00:00")]; @@ -50,19 +61,17 @@ ngx_cached_time = &cached_time[0]; -#if !(NGX_WIN32) - tzset(); -#endif - - ngx_time_update(0, 0); + ngx_time_update(); } void -ngx_time_update(time_t sec, ngx_uint_t msec) +ngx_time_update(void) { u_char *p0, *p1, *p2; ngx_tm_t tm, gmt; + time_t sec; + ngx_uint_t msec; ngx_time_t *tp; struct timeval tv; @@ -70,12 +79,10 @@ return; } - if (sec == 0) { - ngx_gettimeofday(&tv); + ngx_gettimeofday(&tv); - sec = tv.tv_sec; - msec = tv.tv_usec / 1000; - } + sec = tv.tv_sec; + msec = tv.tv_usec / 1000; ngx_current_msec = (ngx_msec_t) sec * 1000 + msec; @@ -116,12 +123,14 @@ #elif (NGX_HAVE_GMTOFF) ngx_localtime(sec, &tm); - tp->gmtoff = (ngx_int_t) (tm.ngx_tm_gmtoff / 60); + cached_gmtoff = (ngx_int_t) (tm.ngx_tm_gmtoff / 60); + tp->gmtoff = cached_gmtoff; #else ngx_localtime(sec, &tm); - tp->gmtoff = ngx_timezone(tm.ngx_tm_isdst); + cached_gmtoff = ngx_timezone(tm.ngx_tm_isdst); + tp->gmtoff = cached_gmtoff; #endif @@ -155,6 +164,57 @@ } +#if !(NGX_WIN32) + +void +ngx_time_sigsafe_update(void) +{ + u_char *p; + ngx_tm_t tm; + time_t sec; + ngx_time_t *tp; + struct timeval tv; + + if (!ngx_trylock(&ngx_time_lock)) { + return; + } + + ngx_gettimeofday(&tv); + + sec = tv.tv_sec; + + tp = &cached_time[slot]; + + if (tp->sec == sec) { + ngx_unlock(&ngx_time_lock); + return; + } + + if (slot == NGX_TIME_SLOTS - 1) { + slot = 0; + } else { + slot++; + } + + ngx_gmtime(sec + cached_gmtoff * 60, &tm); + + p = &cached_err_log_time[slot][0]; + + (void) ngx_sprintf(p, "%4d/%02d/%02d %02d:%02d:%02d", + tm.ngx_tm_year, tm.ngx_tm_mon, + tm.ngx_tm_mday, tm.ngx_tm_hour, + tm.ngx_tm_min, tm.ngx_tm_sec); + + ngx_memory_barrier(); + + ngx_cached_err_log_time.data = p; + + ngx_unlock(&ngx_time_lock); +} + +#endif + + u_char * ngx_http_time(u_char *buf, time_t t) { @@ -203,70 +263,84 @@ void ngx_gmtime(time_t t, ngx_tm_t *tp) { - ngx_int_t sec, min, hour, mday, mon, year, wday, yday, days; + ngx_int_t yday; + ngx_uint_t n, sec, min, hour, mday, mon, year, wday, days, leap; + + /* the calculation is valid for positive time_t only */ + + n = (ngx_uint_t) t; - days = t / 86400; + days = n / 86400; /* Jaunary 1, 1970 was Thursday */ + wday = (4 + days) % 7; - t %= 86400; - hour = t / 3600; - t %= 3600; - min = t / 60; - sec = t % 60; + n %= 86400; + hour = n / 3600; + n %= 3600; + min = n / 60; + sec = n % 60; - /* the algorithm based on Gauss's formula */ + /* + * the algorithm based on Gauss' formula, + * see src/http/ngx_http_parse_time.c + */ + /* days since March 1, 1 BC */ days = days - (31 + 28) + 719527; - year = days * 400 / (365 * 400 + 100 - 4 + 1); + /* + * The "days" should be adjusted to 1 only, however, some March 1st's go + * to previous year, so we adjust them to 2. This causes also shift of the + * last Feburary days to next year, but we catch the case when "yday" + * becomes negative. + */ + + year = (days + 2) * 400 / (365 * 400 + 100 - 4 + 1); + yday = days - (365 * year + year / 4 - year / 100 + year / 400); - mon = (yday + 31) * 12 / 367; - mday = yday - (mon * 367 / 12 - 31); + if (yday < 0) { + leap = (year % 4 == 0) && (year % 100 || (year % 400 == 0)); + yday = 365 + leap + yday; + year--; + } + + /* + * The empirical formula that maps "yday" to month. + * There are at least 10 variants, some of them are: + * mon = (yday + 31) * 15 / 459 + * mon = (yday + 31) * 17 / 520 + * mon = (yday + 31) * 20 / 612 + */ + + mon = (yday + 31) * 10 / 306; - mon += 2; + /* the Gauss' formula that evaluates days before the month */ + + mday = yday - (367 * mon / 12 - 30) + 1; if (yday >= 306) { + year++; + mon -= 10; + /* * there is no "yday" in Win32 SYSTEMTIME * * yday -= 306; */ - year++; - mon -= 12; + } else { - if (mday == 0) { - /* Jaunary 31 */ - mon = 1; - mday = 31; - - } else if (mon == 2) { - - if ((year % 4 == 0) && (year % 100 || (year % 400 == 0))) { - if (mday > 29) { - mon = 3; - mday -= 29; - } - - } else if (mday > 28) { - mon = 3; - mday -= 28; - } - } -/* - * there is no "yday" in Win32 SYSTEMTIME - * - * } else { - * yday += 31 + 28; - * - * if ((year % 4 == 0) && (year % 100 || (year % 400 == 0))) { - * yday++; - * } - */ + mon += 2; + + /* + * there is no "yday" in Win32 SYSTEMTIME + * + * yday += 31 + 28 + leap; + */ } tp->ngx_tm_sec = (ngx_tm_sec_t) sec; @@ -277,3 +351,42 @@ tp->ngx_tm_year = (ngx_tm_year_t) year; tp->ngx_tm_wday = (ngx_tm_wday_t) wday; } + + +time_t +ngx_next_time(time_t when) +{ + time_t now, next; + struct tm tm; + + now = ngx_time(); + + ngx_libc_localtime(now, &tm); + + tm.tm_hour = (int) (when / 3600); + when %= 3600; + tm.tm_min = (int) (when / 60); + tm.tm_sec = (int) (when % 60); + + next = mktime(&tm); + + if (next == -1) { + return -1; + } + + if (next - now > 0) { + return next; + } + + tm.tm_mday++; + + /* mktime() should normalize a date (Jan 32, etc) */ + + next = mktime(&tm); + + if (next != -1) { + return next; + } + + return -1; +} diff -Nru nginx-0.5.33/src/core/ngx_times.h nginx-0.8.53/src/core/ngx_times.h --- nginx-0.5.33/src/core/ngx_times.h 2006-08-30 10:39:17.000000000 +0000 +++ nginx-0.8.53/src/core/ngx_times.h 2010-03-25 09:10:10.000000000 +0000 @@ -20,11 +20,15 @@ void ngx_time_init(void); -void ngx_time_update(time_t sec, ngx_uint_t msec); +void ngx_time_update(void); +void ngx_time_sigsafe_update(void); u_char *ngx_http_time(u_char *buf, time_t t); u_char *ngx_http_cookie_time(u_char *buf, time_t t); void ngx_gmtime(time_t t, ngx_tm_t *tp); +time_t ngx_next_time(time_t when); +#define ngx_next_time_n "mktime()" + extern volatile ngx_time_t *ngx_cached_time; diff -Nru nginx-0.5.33/src/event/modules/ngx_aio_module.c nginx-0.8.53/src/event/modules/ngx_aio_module.c --- nginx-0.5.33/src/event/modules/ngx_aio_module.c 2007-11-07 13:54:40.000000000 +0000 +++ nginx-0.8.53/src/event/modules/ngx_aio_module.c 2009-08-25 09:09:13.000000000 +0000 @@ -7,11 +7,9 @@ #include #include #include -#include -#if (NGX_HAVE_KQUEUE) -#include -#endif + +extern ngx_event_module_t ngx_kqueue_module_ctx; static ngx_int_t ngx_aio_init(ngx_cycle_t *cycle, ngx_msec_t timer); @@ -28,6 +26,7 @@ ngx_os_io_t ngx_os_aio = { ngx_aio_read, ngx_aio_read_chain, + NULL, ngx_aio_write, ngx_aio_write_chain, 0 @@ -72,7 +71,6 @@ }; - #if (NGX_HAVE_KQUEUE) static ngx_int_t @@ -133,12 +131,14 @@ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "aio_cancel: %d", rc); if (rc == AIO_CANCELED) { - c->read->active = c->write->active = 0; + c->read->active = 0; + c->write->active = 0; return NGX_OK; } if (rc == AIO_ALLDONE) { - c->read->active = c->write->active = 0; + c->read->active = 0; + c->write->active = 0; ngx_log_error(NGX_LOG_ALERT, c->log, 0, "aio_cancel() returned AIO_ALLDONE"); return NGX_OK; diff -Nru nginx-0.5.33/src/event/modules/ngx_devpoll_module.c nginx-0.8.53/src/event/modules/ngx_devpoll_module.c --- nginx-0.5.33/src/event/modules/ngx_devpoll_module.c 2007-11-07 13:54:40.000000000 +0000 +++ nginx-0.8.53/src/event/modules/ngx_devpoll_module.c 2010-03-25 09:10:10.000000000 +0000 @@ -369,14 +369,10 @@ dvp.dp_timeout = timer; events = ioctl(dp, DP_POLL, &dvp); - if (events == -1) { - err = ngx_errno; - } else { - err = 0; - } + err = (events == -1) ? ngx_errno : 0; - if (flags & NGX_UPDATE_TIME) { - ngx_time_update(0, 0); + if (flags & NGX_UPDATE_TIME || ngx_event_timer_alarm) { + ngx_time_update(); } if (err) { @@ -550,7 +546,7 @@ dpcf = ngx_palloc(cycle->pool, sizeof(ngx_devpoll_conf_t)); if (dpcf == NULL) { - return NGX_CONF_ERROR; + return NULL; } dpcf->changes = NGX_CONF_UNSET; diff -Nru nginx-0.5.33/src/event/modules/ngx_epoll_module.c nginx-0.8.53/src/event/modules/ngx_epoll_module.c --- nginx-0.5.33/src/event/modules/ngx_epoll_module.c 2007-11-07 13:54:40.000000000 +0000 +++ nginx-0.8.53/src/event/modules/ngx_epoll_module.c 2010-03-25 09:10:10.000000000 +0000 @@ -43,10 +43,6 @@ epoll_data_t data; }; -int epoll_create(int size); -int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); -int epoll_wait(int epfd, struct epoll_event *events, int nevents, int timeout); - int epoll_create(int size) { return -1; @@ -62,6 +58,29 @@ return -1; } +#if (NGX_HAVE_FILE_AIO) + +#define SYS_io_setup 245 +#define SYS_io_destroy 246 +#define SYS_io_getevents 247 +#define SYS_eventfd 323 + +typedef u_int aio_context_t; + +struct io_event { + uint64_t data; /* the data field from the iocb */ + uint64_t obj; /* what iocb this event came from */ + int64_t res; /* result code for this event */ + int64_t res2; /* secondary result */ +}; + + +int eventfd(u_int initval) +{ + return -1; +} + +#endif #endif @@ -82,6 +101,10 @@ static ngx_int_t ngx_epoll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags); +#if (NGX_HAVE_FILE_AIO) +static void ngx_epoll_eventfd_handler(ngx_event_t *ev); +#endif + static void *ngx_epoll_create_conf(ngx_cycle_t *cycle); static char *ngx_epoll_init_conf(ngx_cycle_t *cycle, void *conf); @@ -89,6 +112,15 @@ static struct epoll_event *event_list; static ngx_uint_t nevents; +#if (NGX_HAVE_FILE_AIO) + +int ngx_eventfd = -1; +aio_context_t ngx_aio_ctx = 0; + +static ngx_event_t ngx_eventfd_event; +static ngx_connection_t ngx_eventfd_conn; + +#endif static ngx_str_t epoll_name = ngx_string("epoll"); @@ -140,24 +172,106 @@ }; +#if (NGX_HAVE_FILE_AIO) + +/* + * We call io_setup(), io_destroy() io_submit(), and io_getevents() directly + * as syscalls instead of libaio usage, because the library header file + * supports eventfd() since 0.3.107 version only. + * + * Also we do not use eventfd() in glibc, because glibc supports it + * since 2.8 version and glibc maps two syscalls eventfd() and eventfd2() + * into single eventfd() function with different number of parameters. + */ + +static long +io_setup(u_int nr_reqs, aio_context_t *ctx) +{ + return syscall(SYS_io_setup, nr_reqs, ctx); +} + + +static int +io_destroy(aio_context_t ctx) +{ + return syscall(SYS_io_destroy, ctx); +} + + +static long +io_getevents(aio_context_t ctx, long min_nr, long nr, struct io_event *events, + struct timespec *tmo) +{ + return syscall(SYS_io_getevents, ctx, min_nr, nr, events, tmo); +} + +#endif + + static ngx_int_t ngx_epoll_init(ngx_cycle_t *cycle, ngx_msec_t timer) { - ngx_event_conf_t *ecf; ngx_epoll_conf_t *epcf; - ecf = ngx_event_get_conf(cycle->conf_ctx, ngx_event_core_module); - epcf = ngx_event_get_conf(cycle->conf_ctx, ngx_epoll_module); if (ep == -1) { - ep = epoll_create(ecf->connections / 2); + ep = epoll_create(cycle->connection_n / 2); if (ep == -1) { ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, "epoll_create() failed"); return NGX_ERROR; } + +#if (NGX_HAVE_FILE_AIO) + { + int n; + struct epoll_event ee; + + ngx_eventfd = syscall(SYS_eventfd, 0); + + if (ngx_eventfd == -1) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, + "eventfd() failed"); + return NGX_ERROR; + } + + n = 1; + + if (ioctl(ngx_eventfd, FIONBIO, &n) == -1) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, + "ioctl(eventfd, FIONBIO) failed"); + } + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, + "eventfd: %d", ngx_eventfd); + + n = io_setup(1024, &ngx_aio_ctx); + + if (n != 0) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, -n, "io_setup() failed"); + return NGX_ERROR; + } + + ngx_eventfd_event.data = &ngx_eventfd_conn; + ngx_eventfd_event.handler = ngx_epoll_eventfd_handler; + ngx_eventfd_event.log = cycle->log; + ngx_eventfd_event.active = 1; + ngx_eventfd_conn.fd = ngx_eventfd; + ngx_eventfd_conn.read = &ngx_eventfd_event; + ngx_eventfd_conn.log = cycle->log; + + ee.events = EPOLLIN|EPOLLET; + ee.data.ptr = &ngx_eventfd_conn; + + if (epoll_ctl(ep, EPOLL_CTL_ADD, ngx_eventfd, &ee) == -1) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, + "epoll_ctl(EPOLL_CTL_ADD, eventfd) failed"); + return NGX_ERROR; + } + } +#endif } if (nevents < epcf->events) { @@ -200,6 +314,17 @@ ep = -1; +#if (NGX_HAVE_FILE_AIO) + + if (io_destroy(ngx_aio_ctx) != 0) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "io_destroy() failed"); + } + + ngx_aio_ctx = 0; + +#endif + ngx_free(event_list); event_list = NULL; @@ -404,14 +529,10 @@ events = epoll_wait(ep, event_list, (int) nevents, timer); - if (events == -1) { - err = ngx_errno; - } else { - err = 0; - } + err = (events == -1) ? ngx_errno : 0; - if (flags & NGX_UPDATE_TIME) { - ngx_time_update(0, 0); + if (flags & NGX_UPDATE_TIME || ngx_event_timer_alarm) { + ngx_time_update(); } if (err) { @@ -548,6 +669,91 @@ } +#if (NGX_HAVE_FILE_AIO) + +static void +ngx_epoll_eventfd_handler(ngx_event_t *ev) +{ + int n; + long i, events; + uint64_t ready; + ngx_err_t err; + ngx_event_t *e; + ngx_event_aio_t *aio; + struct io_event event[64]; + struct timespec ts; + + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0, "eventfd handler"); + + n = read(ngx_eventfd, &ready, 8); + + err = ngx_errno; + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ev->log, 0, "eventfd: %d", n); + + if (n != 8) { + if (n == -1) { + if (err == NGX_EAGAIN) { + return; + } + + ngx_log_error(NGX_LOG_ALERT, ev->log, err, "read(eventfd) failed"); + return; + } + + ngx_log_error(NGX_LOG_ALERT, ev->log, 0, + "read(eventfd) returned only %d bytes", n); + return; + } + + ts.tv_sec = 0; + ts.tv_nsec = 0; + + while (ready) { + + events = io_getevents(ngx_aio_ctx, 1, 64, event, &ts); + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ev->log, 0, + "io_getevents: %l", events); + + if (events > 0) { + ready -= events; + + for (i = 0; i < events; i++) { + + ngx_log_debug4(NGX_LOG_DEBUG_EVENT, ev->log, 0, + "io_event: %uXL %uXL %L %L", + event[i].data, event[i].obj, + event[i].res, event[i].res2); + + e = (ngx_event_t *) (uintptr_t) event[i].data; + + e->complete = 1; + e->active = 0; + e->ready = 1; + + aio = e->data; + aio->res = event[i].res; + + ngx_post_event(e, &ngx_posted_events); + } + + continue; + } + + if (events == 0) { + return; + } + + /* events < 0 */ + ngx_log_error(NGX_LOG_ALERT, ev->log, -events, "io_getevents() failed"); + return; + } +} + +#endif + + static void * ngx_epoll_create_conf(ngx_cycle_t *cycle) { @@ -555,7 +761,7 @@ epcf = ngx_palloc(cycle->pool, sizeof(ngx_epoll_conf_t)); if (epcf == NULL) { - return NGX_CONF_ERROR; + return NULL; } epcf->events = NGX_CONF_UNSET; diff -Nru nginx-0.5.33/src/event/modules/ngx_eventport_module.c nginx-0.8.53/src/event/modules/ngx_eventport_module.c --- nginx-0.5.33/src/event/modules/ngx_eventport_module.c 2007-11-07 13:54:40.000000000 +0000 +++ nginx-0.8.53/src/event/modules/ngx_eventport_module.c 2010-03-25 09:10:10.000000000 +0000 @@ -40,11 +40,15 @@ void *portnfy_user; /* user defined */ } port_notify_t; +#if (__FreeBSD_version < 700005) + typedef struct itimerspec { /* definition per POSIX.4 */ struct timespec it_interval;/* timer period */ struct timespec it_value; /* timer expiration */ } itimerspec_t; +#endif + int port_create(void) { return -1; @@ -106,7 +110,7 @@ static int ep = -1; static port_event_t *event_list; static ngx_uint_t nevents; -static timer_t event_timer = -1; +static timer_t event_timer = (timer_t) -1; static ngx_str_t eventport_name = ngx_string("eventport"); @@ -237,13 +241,13 @@ static void ngx_eventport_done(ngx_cycle_t *cycle) { - if (event_timer != -1) { + if (event_timer != (timer_t) -1) { if (timer_delete(event_timer) == -1) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "timer_delete() failed"); } - event_timer = -1; + event_timer = (timer_t) -1; } if (close(ep) == -1) { @@ -401,7 +405,7 @@ err = ngx_errno; if (flags & NGX_UPDATE_TIME) { - ngx_time_update(0, 0); + ngx_time_update(); } if (n == -1) { @@ -435,7 +439,7 @@ for (i = 0; i < events; i++) { if (event_list[i].portev_source == PORT_SOURCE_TIMER) { - ngx_time_update(0, 0); + ngx_time_update(); continue; } @@ -577,7 +581,7 @@ epcf = ngx_palloc(cycle->pool, sizeof(ngx_eventport_conf_t)); if (epcf == NULL) { - return NGX_CONF_ERROR; + return NULL; } epcf->events = NGX_CONF_UNSET; diff -Nru nginx-0.5.33/src/event/modules/ngx_kqueue_module.c nginx-0.8.53/src/event/modules/ngx_kqueue_module.c --- nginx-0.5.33/src/event/modules/ngx_kqueue_module.c 2007-11-07 13:54:40.000000000 +0000 +++ nginx-0.8.53/src/event/modules/ngx_kqueue_module.c 2010-03-25 09:10:10.000000000 +0000 @@ -7,7 +7,6 @@ #include #include #include -#include typedef struct { @@ -113,7 +112,6 @@ }; - static ngx_int_t ngx_kqueue_init(ngx_cycle_t *cycle, ngx_msec_t timer) { @@ -200,7 +198,9 @@ } } - ngx_event_flags = 0; + ngx_event_flags = NGX_USE_ONESHOT_EVENT + |NGX_USE_KQUEUE_EVENT + |NGX_USE_VNODE_EVENT; #if (NGX_HAVE_TIMER_EVENT) @@ -226,8 +226,6 @@ #endif - ngx_event_flags |= NGX_USE_ONESHOT_EVENT|NGX_USE_KQUEUE_EVENT; - #if (NGX_HAVE_CLEAR_EVENT) ngx_event_flags |= NGX_USE_CLEAR_EVENT; #else @@ -389,10 +387,12 @@ if (flags & NGX_DISABLE_EVENT) { ev->disabled = 1; + + } else { + flags |= EV_DELETE; } - rc = ngx_kqueue_set_event(ev, event, - flags & NGX_DISABLE_EVENT ? EV_DISABLE : EV_DELETE); + rc = ngx_kqueue_set_event(ev, event, flags); ngx_mutex_unlock(list_mutex); @@ -444,7 +444,7 @@ || __FreeBSD_version >= 500018 |NOTE_REVOKE #endif - ; + ; kev->data = 0; } else { @@ -466,6 +466,22 @@ ev->index = nchanges; nchanges++; + if (flags & NGX_FLUSH_EVENT) { + ts.tv_sec = 0; + ts.tv_nsec = 0; + + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0, "kevent flush"); + + if (kevent(ngx_kqueue, change_list, (int) nchanges, NULL, 0, &ts) + == -1) + { + ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno, "kevent() failed"); + return NGX_ERROR; + } + + nchanges = 0; + } + return NGX_OK; } @@ -519,14 +535,10 @@ events = kevent(ngx_kqueue, change_list, n, event_list, (int) nevents, tp); - if (events == -1) { - err = ngx_errno; - } else { - err = 0; - } + err = (events == -1) ? ngx_errno : 0; - if (flags & NGX_UPDATE_TIME) { - ngx_time_update(0, 0); + if (flags & NGX_UPDATE_TIME || ngx_event_timer_alarm) { + ngx_time_update(); } ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, @@ -577,7 +589,7 @@ #if (NGX_HAVE_TIMER_EVENT) if (event_list[i].filter == EVFILT_TIMER) { - ngx_time_update(0, 0); + ngx_time_update(); continue; } @@ -750,7 +762,7 @@ kcf = ngx_palloc(cycle->pool, sizeof(ngx_kqueue_conf_t)); if (kcf == NULL) { - return NGX_CONF_ERROR; + return NULL; } kcf->changes = NGX_CONF_UNSET; diff -Nru nginx-0.5.33/src/event/modules/ngx_kqueue_module.h nginx-0.8.53/src/event/modules/ngx_kqueue_module.h --- nginx-0.5.33/src/event/modules/ngx_kqueue_module.h 2007-11-07 13:54:40.000000000 +0000 +++ nginx-0.8.53/src/event/modules/ngx_kqueue_module.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,16 +0,0 @@ - -/* - * Copyright (C) Igor Sysoev - */ - - -#ifndef _NGX_KQUEUE_MODULE_H_INCLUDED_ -#define _NGX_KQUEUE_MODULE_H_INCLUDED_ - - -extern int ngx_kqueue; -extern ngx_module_t ngx_kqueue_module; -extern ngx_event_module_t ngx_kqueue_module_ctx; - - -#endif /* _NGX_KQUEUE_MODULE_H_INCLUDED_ */ diff -Nru nginx-0.5.33/src/event/modules/ngx_poll_module.c nginx-0.8.53/src/event/modules/ngx_poll_module.c --- nginx-0.5.33/src/event/modules/ngx_poll_module.c 2007-11-07 13:54:40.000000000 +0000 +++ nginx-0.8.53/src/event/modules/ngx_poll_module.c 2010-03-25 09:10:10.000000000 +0000 @@ -72,7 +72,7 @@ nevents = 0; } - if (ngx_process == NGX_PROCESS_WORKER + if (ngx_process >= NGX_PROCESS_WORKER || cycle->old_cycle == NULL || cycle->old_cycle->connection_n < cycle->connection_n) { @@ -260,14 +260,10 @@ ready = poll(event_list, (u_int) nevents, (int) timer); - if (ready == -1) { - err = ngx_errno; - } else { - err = 0; - } + err = (ready == -1) ? ngx_errno : 0; - if (flags & NGX_UPDATE_TIME) { - ngx_time_update(0, 0); + if (flags & NGX_UPDATE_TIME || ngx_event_timer_alarm) { + ngx_time_update(); } ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0, diff -Nru nginx-0.5.33/src/event/modules/ngx_rtsig_module.c nginx-0.8.53/src/event/modules/ngx_rtsig_module.c --- nginx-0.5.33/src/event/modules/ngx_rtsig_module.c 2007-11-07 13:54:40.000000000 +0000 +++ nginx-0.8.53/src/event/modules/ngx_rtsig_module.c 2010-03-25 09:10:10.000000000 +0000 @@ -11,9 +11,14 @@ #if (NGX_TEST_BUILD_RTSIG) -#define F_SETSIG 10 +#ifdef SIGRTMIN +#define si_fd _reason.__spare__.__spare2__[0] +#else #define SIGRTMIN 33 #define si_fd __spare__[0] +#endif + +#define F_SETSIG 10 #define KERN_RTSIGNR 30 #define KERN_RTSIGMAX 31 @@ -318,7 +323,7 @@ "rtsig signo:%d", signo); if (flags & NGX_UPDATE_TIME) { - ngx_time_update(0, 0); + ngx_time_update(); } if (err == NGX_EAGAIN) { @@ -344,7 +349,7 @@ signo, si.si_fd, si.si_band); if (flags & NGX_UPDATE_TIME) { - ngx_time_update(0, 0); + ngx_time_update(); } rtscf = ngx_event_get_conf(ngx_cycle->conf_ctx, ngx_rtsig_module); @@ -414,7 +419,7 @@ } else if (signo == SIGALRM) { - ngx_time_update(0, 0); + ngx_time_update(); return NGX_OK; @@ -666,7 +671,7 @@ } if (flags & NGX_UPDATE_TIME) { - ngx_time_update(0, 0); + ngx_time_update(); } ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, @@ -686,7 +691,7 @@ rtscf = ngx_palloc(cycle->pool, sizeof(ngx_rtsig_conf_t)); if (rtscf == NULL) { - return NGX_CONF_ERROR; + return NULL; } rtscf->signo = NGX_CONF_UNSET; diff -Nru nginx-0.5.33/src/event/modules/ngx_select_module.c nginx-0.8.53/src/event/modules/ngx_select_module.c --- nginx-0.5.33/src/event/modules/ngx_select_module.c 2007-11-07 13:54:40.000000000 +0000 +++ nginx-0.8.53/src/event/modules/ngx_select_module.c 2010-03-25 09:10:10.000000000 +0000 @@ -9,7 +9,6 @@ #include - static ngx_int_t ngx_select_init(ngx_cycle_t *cycle, ngx_msec_t timer); static void ngx_select_done(ngx_cycle_t *cycle); static ngx_int_t ngx_select_add_event(ngx_event_t *ev, ngx_int_t event, @@ -18,6 +17,7 @@ ngx_uint_t flags); static ngx_int_t ngx_select_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags); +static void ngx_select_repair_fd_sets(ngx_cycle_t *cycle); static char *ngx_select_init_conf(ngx_cycle_t *cycle, void *conf); @@ -26,13 +26,7 @@ static fd_set work_read_fd_set; static fd_set work_write_fd_set; -#if (NGX_WIN32) -static ngx_uint_t max_read; -static ngx_uint_t max_write; -#else static ngx_int_t max_fd; -#endif - static ngx_uint_t nevents; static ngx_event_t **event_index; @@ -87,7 +81,7 @@ nevents = 0; } - if (ngx_process == NGX_PROCESS_WORKER + if (ngx_process >= NGX_PROCESS_WORKER || cycle->old_cycle == NULL || cycle->old_cycle->connection_n < cycle->connection_n) { @@ -111,11 +105,7 @@ ngx_event_flags = NGX_USE_LEVEL_EVENT; -#if (NGX_WIN32) - max_read = max_write = 0; -#else max_fd = -1; -#endif return NGX_OK; } @@ -146,30 +136,17 @@ return NGX_OK; } -#if (NGX_WIN32) - - if ((event == NGX_READ_EVENT) && (max_read >= FD_SETSIZE) - || (event == NGX_WRITE_EVENT) && (max_write >= FD_SETSIZE)) + if ((event == NGX_READ_EVENT && ev->write) + || (event == NGX_WRITE_EVENT && !ev->write)) { - ngx_log_error(NGX_LOG_ERR, ev->log, 0, - "maximum number of descriptors " - "supported by select() is %d", FD_SETSIZE); + ngx_log_error(NGX_LOG_ALERT, ev->log, 0, + "invalid select %s event fd:%d ev:%i", + ev->write ? "write" : "read", c->fd, event); return NGX_ERROR; } if (event == NGX_READ_EVENT) { FD_SET(c->fd, &master_read_fd_set); - max_read++; - - } else if (event == NGX_WRITE_EVENT) { - FD_SET(c->fd, &master_write_fd_set); - max_write++; - } - -#else - - if (event == NGX_READ_EVENT) { - FD_SET(c->fd, &master_read_fd_set); } else if (event == NGX_WRITE_EVENT) { FD_SET(c->fd, &master_write_fd_set); @@ -179,8 +156,6 @@ max_fd = c->fd; } -#endif - ev->active = 1; event_index[nevents] = ev; @@ -194,6 +169,7 @@ static ngx_int_t ngx_select_del_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags) { + ngx_event_t *e; ngx_connection_t *c; c = ev->data; @@ -207,19 +183,6 @@ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0, "select del event fd:%d ev:%i", c->fd, event); -#if (NGX_WIN32) - - if (event == NGX_READ_EVENT) { - FD_CLR(c->fd, &master_read_fd_set); - max_read--; - - } else if (event == NGX_WRITE_EVENT) { - FD_CLR(c->fd, &master_write_fd_set); - max_write--; - } - -#else - if (event == NGX_READ_EVENT) { FD_CLR(c->fd, &master_read_fd_set); @@ -231,11 +194,10 @@ max_fd = -1; } -#endif - if (ev->index < --nevents) { - event_index[ev->index] = event_index[nevents]; - event_index[ev->index]->index = ev->index; + e = event_index[nevents]; + event_index[ev->index] = e; + e->index = ev->index; } ev->index = NGX_INVALID_INDEX; @@ -248,17 +210,12 @@ ngx_select_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags) { - int ready, nready; - ngx_uint_t i, found; - ngx_err_t err; - ngx_event_t *ev, **queue; - ngx_connection_t *c; - struct timeval tv, *tp; -#if !(NGX_WIN32) - ngx_uint_t level; -#endif - -#if !(NGX_WIN32) + int ready, nready; + ngx_err_t err; + ngx_uint_t i, found; + ngx_event_t *ev, **queue; + struct timeval tv, *tp; + ngx_connection_t *c; if (max_fd == -1) { for (i = 0; i < nevents; i++) { @@ -272,8 +229,6 @@ "change max_fd: %d", max_fd); } -#endif - #if (NGX_DEBUG) if (cycle->log->log_level & NGX_LOG_DEBUG_ALL) { for (i = 0; i < nevents; i++) { @@ -283,10 +238,8 @@ "select event: fd:%d wr:%d", c->fd, ev->write); } -#if !(NGX_WIN32) ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "max_fd: %d", max_fd); -#endif } #endif @@ -305,49 +258,20 @@ work_read_fd_set = master_read_fd_set; work_write_fd_set = master_write_fd_set; -#if 1 - ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, - /* - * (void *) disables "dereferencing type-punned - * pointer will break strict-aliasing rules - */ - "select read fd_set: %08Xd", - *(int *) (void *) &work_read_fd_set); -#endif - -#if (NGX_WIN32) - - ready = select(0, &work_read_fd_set, &work_write_fd_set, NULL, tp); - -#else - ready = select(max_fd + 1, &work_read_fd_set, &work_write_fd_set, NULL, tp); -#endif + err = (ready == -1) ? ngx_errno : 0; - if (ready == -1) { - err = ngx_socket_errno; - } else { - err = 0; - } - - if (flags & NGX_UPDATE_TIME) { - ngx_time_update(0, 0); + if (flags & NGX_UPDATE_TIME || ngx_event_timer_alarm) { + ngx_time_update(); } ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "select ready %d", ready); -#if (NGX_WIN32) - if (err) { - ngx_log_error(NGX_LOG_ALERT, cycle->log, err, "select() failed"); - return NGX_ERROR; - } + ngx_uint_t level; -#else - - if (err) { if (err == NGX_EINTR) { if (ngx_event_timer_alarm) { @@ -362,11 +286,14 @@ } ngx_log_error(level, cycle->log, err, "select() failed"); + + if (err == EBADF) { + ngx_select_repair_fd_sets(cycle); + } + return NGX_ERROR; } -#endif - if (ready == 0) { if (timer != NGX_TIMER_INFINITE) { return NGX_OK; @@ -415,13 +342,64 @@ ngx_mutex_unlock(ngx_posted_events_mutex); if (ready != nready) { - ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, "select ready != events"); + ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, + "select ready != events: %d:%d", ready, nready); + + ngx_select_repair_fd_sets(cycle); } return NGX_OK; } +static void +ngx_select_repair_fd_sets(ngx_cycle_t *cycle) +{ + int n; + socklen_t len; + ngx_err_t err; + ngx_socket_t s; + + for (s = 0; s <= max_fd; s++) { + + if (FD_ISSET(s, &master_read_fd_set) == 0) { + continue; + } + + len = sizeof(int); + + if (getsockopt(s, SOL_SOCKET, SO_TYPE, &n, &len) == -1) { + err = ngx_socket_errno; + + ngx_log_error(NGX_LOG_ALERT, cycle->log, err, + "invalid descriptor #%d in read fd_set", s); + + FD_CLR(s, &master_read_fd_set); + } + } + + for (s = 0; s <= max_fd; s++) { + + if (FD_ISSET(s, &master_write_fd_set) == 0) { + continue; + } + + len = sizeof(int); + + if (getsockopt(s, SOL_SOCKET, SO_TYPE, &n, &len) == -1) { + err = ngx_socket_errno; + + ngx_log_error(NGX_LOG_ALERT, cycle->log, err, + "invalid descriptor #%d in write fd_set", s); + + FD_CLR(s, &master_write_fd_set); + } + } + + max_fd = -1; +} + + static char * ngx_select_init_conf(ngx_cycle_t *cycle, void *conf) { @@ -435,18 +413,14 @@ /* disable warning: the default FD_SETSIZE is 1024U in FreeBSD 5.x */ -#if !(NGX_WIN32) - - if ((unsigned) ecf->connections > FD_SETSIZE) { + if (cycle->connection_n > FD_SETSIZE) { ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, "the maximum number of files " - "supported by select() is " ngx_value(FD_SETSIZE)); + "supported by select() is %ud", FD_SETSIZE); return NGX_CONF_ERROR; } -#endif - -#if (NGX_THREADS) && !(NGX_WIN32) +#if (NGX_THREADS) ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, "select() is not supported in the threaded mode"); diff -Nru nginx-0.5.33/src/event/modules/ngx_win32_select_module.c nginx-0.8.53/src/event/modules/ngx_win32_select_module.c --- nginx-0.5.33/src/event/modules/ngx_win32_select_module.c 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/src/event/modules/ngx_win32_select_module.c 2010-03-25 09:10:10.000000000 +0000 @@ -0,0 +1,399 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include + + +static ngx_int_t ngx_select_init(ngx_cycle_t *cycle, ngx_msec_t timer); +static void ngx_select_done(ngx_cycle_t *cycle); +static ngx_int_t ngx_select_add_event(ngx_event_t *ev, ngx_int_t event, + ngx_uint_t flags); +static ngx_int_t ngx_select_del_event(ngx_event_t *ev, ngx_int_t event, + ngx_uint_t flags); +static ngx_int_t ngx_select_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, + ngx_uint_t flags); +static void ngx_select_repair_fd_sets(ngx_cycle_t *cycle); +static char *ngx_select_init_conf(ngx_cycle_t *cycle, void *conf); + + +static fd_set master_read_fd_set; +static fd_set master_write_fd_set; +static fd_set work_read_fd_set; +static fd_set work_write_fd_set; + +static ngx_uint_t max_read; +static ngx_uint_t max_write; +static ngx_uint_t nevents; + +static ngx_event_t **event_index; + + +static ngx_str_t select_name = ngx_string("select"); + +ngx_event_module_t ngx_select_module_ctx = { + &select_name, + NULL, /* create configuration */ + ngx_select_init_conf, /* init configuration */ + + { + ngx_select_add_event, /* add an event */ + ngx_select_del_event, /* delete an event */ + ngx_select_add_event, /* enable an event */ + ngx_select_del_event, /* disable an event */ + NULL, /* add an connection */ + NULL, /* delete an connection */ + NULL, /* process the changes */ + ngx_select_process_events, /* process the events */ + ngx_select_init, /* init the events */ + ngx_select_done /* done the events */ + } + +}; + +ngx_module_t ngx_select_module = { + NGX_MODULE_V1, + &ngx_select_module_ctx, /* module context */ + NULL, /* module directives */ + NGX_EVENT_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_select_init(ngx_cycle_t *cycle, ngx_msec_t timer) +{ + ngx_event_t **index; + + if (event_index == NULL) { + FD_ZERO(&master_read_fd_set); + FD_ZERO(&master_write_fd_set); + nevents = 0; + } + + if (ngx_process >= NGX_PROCESS_WORKER + || cycle->old_cycle == NULL + || cycle->old_cycle->connection_n < cycle->connection_n) + { + index = ngx_alloc(sizeof(ngx_event_t *) * 2 * cycle->connection_n, + cycle->log); + if (index == NULL) { + return NGX_ERROR; + } + + if (event_index) { + ngx_memcpy(index, event_index, sizeof(ngx_event_t *) * nevents); + ngx_free(event_index); + } + + event_index = index; + } + + ngx_io = ngx_os_io; + + ngx_event_actions = ngx_select_module_ctx.actions; + + ngx_event_flags = NGX_USE_LEVEL_EVENT; + + max_read = 0; + max_write = 0; + + return NGX_OK; +} + + +static void +ngx_select_done(ngx_cycle_t *cycle) +{ + ngx_free(event_index); + + event_index = NULL; +} + + +static ngx_int_t +ngx_select_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags) +{ + ngx_connection_t *c; + + c = ev->data; + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0, + "select add event fd:%d ev:%i", c->fd, event); + + if (ev->index != NGX_INVALID_INDEX) { + ngx_log_error(NGX_LOG_ALERT, ev->log, 0, + "select event fd:%d ev:%i is already set", c->fd, event); + return NGX_OK; + } + + if ((event == NGX_READ_EVENT && ev->write) + || (event == NGX_WRITE_EVENT && !ev->write)) + { + ngx_log_error(NGX_LOG_ALERT, ev->log, 0, + "invalid select %s event fd:%d ev:%i", + ev->write ? "write" : "read", c->fd, event); + return NGX_ERROR; + } + + if ((event == NGX_READ_EVENT) && (max_read >= FD_SETSIZE) + || (event == NGX_WRITE_EVENT) && (max_write >= FD_SETSIZE)) + { + ngx_log_error(NGX_LOG_ERR, ev->log, 0, + "maximum number of descriptors " + "supported by select() is %d", FD_SETSIZE); + return NGX_ERROR; + } + + if (event == NGX_READ_EVENT) { + FD_SET(c->fd, &master_read_fd_set); + max_read++; + + } else if (event == NGX_WRITE_EVENT) { + FD_SET(c->fd, &master_write_fd_set); + max_write++; + } + + ev->active = 1; + + event_index[nevents] = ev; + ev->index = nevents; + nevents++; + + return NGX_OK; +} + + +static ngx_int_t +ngx_select_del_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags) +{ + ngx_event_t *e; + ngx_connection_t *c; + + c = ev->data; + + ev->active = 0; + + if (ev->index == NGX_INVALID_INDEX) { + return NGX_OK; + } + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0, + "select del event fd:%d ev:%i", c->fd, event); + + if (event == NGX_READ_EVENT) { + FD_CLR(c->fd, &master_read_fd_set); + max_read--; + + } else if (event == NGX_WRITE_EVENT) { + FD_CLR(c->fd, &master_write_fd_set); + max_write--; + } + + if (ev->index < --nevents) { + e = event_index[nevents]; + event_index[ev->index] = e; + e->index = ev->index; + } + + ev->index = NGX_INVALID_INDEX; + + return NGX_OK; +} + + +static ngx_int_t +ngx_select_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, + ngx_uint_t flags) +{ + int ready, nready; + ngx_err_t err; + ngx_uint_t i, found; + ngx_event_t *ev, **queue; + struct timeval tv, *tp; + ngx_connection_t *c; + +#if (NGX_DEBUG) + if (cycle->log->log_level & NGX_LOG_DEBUG_ALL) { + for (i = 0; i < nevents; i++) { + ev = event_index[i]; + c = ev->data; + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0, + "select event: fd:%d wr:%d", c->fd, ev->write); + } + } +#endif + + if (timer == NGX_TIMER_INFINITE) { + tp = NULL; + + } else { + tv.tv_sec = (long) (timer / 1000); + tv.tv_usec = (long) ((timer % 1000) * 1000); + tp = &tv; + } + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, + "select timer: %M", timer); + + work_read_fd_set = master_read_fd_set; + work_write_fd_set = master_write_fd_set; + + if (max_read || max_write) { + ready = select(0, &work_read_fd_set, &work_write_fd_set, NULL, tp); + + } else { + + /* + * Winsock select() requires that at least one descriptor set must be + * be non-null, and any non-null descriptor set must contain at least + * one handle to a socket. Otherwise select() returns WSAEINVAL. + */ + + ngx_msleep(timer); + + ready = 0; + } + + err = (ready == -1) ? ngx_socket_errno : 0; + + if (flags & NGX_UPDATE_TIME) { + ngx_time_update(); + } + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, + "select ready %d", ready); + + if (err) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, err, "select() failed"); + + if (err == WSAENOTSOCK) { + ngx_select_repair_fd_sets(cycle); + } + + return NGX_ERROR; + } + + if (ready == 0) { + if (timer != NGX_TIMER_INFINITE) { + return NGX_OK; + } + + ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, + "select() returned no events without timeout"); + return NGX_ERROR; + } + + ngx_mutex_lock(ngx_posted_events_mutex); + + nready = 0; + + for (i = 0; i < nevents; i++) { + ev = event_index[i]; + c = ev->data; + found = 0; + + if (ev->write) { + if (FD_ISSET(c->fd, &work_write_fd_set)) { + found = 1; + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, + "select write %d", c->fd); + } + + } else { + if (FD_ISSET(c->fd, &work_read_fd_set)) { + found = 1; + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, + "select read %d", c->fd); + } + } + + if (found) { + ev->ready = 1; + + queue = (ngx_event_t **) (ev->accept ? &ngx_posted_accept_events: + &ngx_posted_events); + ngx_locked_post_event(ev, queue); + + nready++; + } + } + + ngx_mutex_unlock(ngx_posted_events_mutex); + + if (ready != nready) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, + "select ready != events: %d:%d", ready, nready); + + ngx_select_repair_fd_sets(cycle); + } + + return NGX_OK; +} + + +static void +ngx_select_repair_fd_sets(ngx_cycle_t *cycle) +{ + int n; + u_int i; + socklen_t len; + ngx_err_t err; + ngx_socket_t s; + + for (i = 0; i < master_read_fd_set.fd_count; i++) { + + s = master_read_fd_set.fd_array[i]; + len = sizeof(int); + + if (getsockopt(s, SOL_SOCKET, SO_TYPE, (char *) &n, &len) == -1) { + err = ngx_socket_errno; + + ngx_log_error(NGX_LOG_ALERT, cycle->log, err, + "invalid descriptor #%d in read fd_set", s); + + FD_CLR(s, &master_read_fd_set); + } + } + + for (i = 0; i < master_write_fd_set.fd_count; i++) { + + s = master_write_fd_set.fd_array[i]; + len = sizeof(int); + + if (getsockopt(s, SOL_SOCKET, SO_TYPE, (char *) &n, &len) == -1) { + err = ngx_socket_errno; + + ngx_log_error(NGX_LOG_ALERT, cycle->log, err, + "invalid descriptor #%d in write fd_set", s); + + FD_CLR(s, &master_write_fd_set); + } + } +} + + +static char * +ngx_select_init_conf(ngx_cycle_t *cycle, void *conf) +{ + ngx_event_conf_t *ecf; + + ecf = ngx_event_get_conf(cycle->conf_ctx, ngx_event_core_module); + + if (ecf->use != ngx_select_module.ctx_index) { + return NGX_CONF_OK; + } + + return NGX_CONF_OK; +} diff -Nru nginx-0.5.33/src/event/ngx_event_accept.c nginx-0.8.53/src/event/ngx_event_accept.c --- nginx-0.5.33/src/event/ngx_event_accept.c 2006-09-26 12:20:12.000000000 +0000 +++ nginx-0.8.53/src/event/ngx_event_accept.c 2009-11-01 19:29:49.000000000 +0000 @@ -9,10 +9,6 @@ #include -/* the buffer size is enough to hold "struct sockaddr_un" */ -#define NGX_SOCKLEN 512 - - static ngx_int_t ngx_enable_accept_events(ngx_cycle_t *cycle); static ngx_int_t ngx_disable_accept_events(ngx_cycle_t *cycle); static void ngx_close_accepted_connection(ngx_connection_t *c); @@ -29,7 +25,7 @@ ngx_listening_t *ls; ngx_connection_t *c, *lc; ngx_event_conf_t *ecf; - char sa[NGX_SOCKLEN]; + u_char sa[NGX_SOCKADDRLEN]; ecf = ngx_event_get_conf(ngx_cycle->conf_ctx, ngx_event_core_module); @@ -48,7 +44,7 @@ "accept on %V, ready: %d", &ls->addr_text, ev->available); do { - socklen = NGX_SOCKLEN; + socklen = NGX_SOCKADDRLEN; s = accept(lc->fd, (struct sockaddr *) sa, &socklen); @@ -61,8 +57,8 @@ return; } - ngx_log_error((err == NGX_ECONNABORTED) ? NGX_LOG_ERR: - NGX_LOG_ALERT, + ngx_log_error((ngx_uint_t) ((err == NGX_ECONNABORTED) ? + NGX_LOG_ERR : NGX_LOG_ALERT), ev->log, err, "accept() failed"); if (err == NGX_ECONNABORTED) { @@ -79,10 +75,10 @@ } #if (NGX_STAT_STUB) - ngx_atomic_fetch_add(ngx_stat_accepted, 1); + (void) ngx_atomic_fetch_add(ngx_stat_accepted, 1); #endif - ngx_accept_disabled = NGX_ACCEPT_THRESHOLD + ngx_accept_disabled = ngx_cycle->connection_n / 8 - ngx_cycle->free_connection_n; c = ngx_get_connection(s, ev->log); @@ -97,7 +93,7 @@ } #if (NGX_STAT_STUB) - ngx_atomic_fetch_add(ngx_stat_active, 1); + (void) ngx_atomic_fetch_add(ngx_stat_active, 1); #endif c->pool = ngx_create_pool(ls->pool_size, ev->log); @@ -153,11 +149,23 @@ c->log = log; c->pool->log = log; - c->listening = ls; c->socklen = socklen; + c->listening = ls; + c->local_sockaddr = ls->sockaddr; c->unexpected_eof = 1; +#if (NGX_HAVE_UNIX_DOMAIN) + if (c->sockaddr->sa_family == AF_UNIX) { + c->tcp_nopush = NGX_TCP_NOPUSH_DISABLED; + c->tcp_nodelay = NGX_TCP_NODELAY_DISABLED; +#if (NGX_SOLARIS) + /* Solaris's sendfilev() supports AF_NCA, AF_INET, and AF_INET6 */ + c->sendfile = 0; +#endif + } +#endif + rev = c->read; wev = c->write; @@ -190,7 +198,7 @@ c->number = ngx_atomic_fetch_add(ngx_connection_counter, 1); #if (NGX_STAT_STUB) - ngx_atomic_fetch_add(ngx_stat_handled, 1); + (void) ngx_atomic_fetch_add(ngx_stat_handled, 1); #endif #if (NGX_THREADS) @@ -201,15 +209,14 @@ #endif if (ls->addr_ntop) { - c->addr_text.data = ngx_palloc(c->pool, ls->addr_text_max_len); + c->addr_text.data = ngx_pnalloc(c->pool, ls->addr_text_max_len); if (c->addr_text.data == NULL) { ngx_close_accepted_connection(c); return; } - c->addr_text.len = ngx_sock_ntop(ls->family, c->sockaddr, - c->addr_text.data, - ls->addr_text_max_len); + c->addr_text.len = ngx_sock_ntop(c->sockaddr, c->addr_text.data, + ls->addr_text_max_len, 0); if (c->addr_text.len == 0) { ngx_close_accepted_connection(c); return; @@ -382,7 +389,7 @@ } #if (NGX_STAT_STUB) - ngx_atomic_fetch_add(ngx_stat_active, -1); + (void) ngx_atomic_fetch_add(ngx_stat_active, -1); #endif } diff -Nru nginx-0.5.33/src/event/ngx_event_busy_lock.c nginx-0.8.53/src/event/ngx_event_busy_lock.c --- nginx-0.5.33/src/event/ngx_event_busy_lock.c 2007-11-07 13:54:40.000000000 +0000 +++ nginx-0.8.53/src/event/ngx_event_busy_lock.c 2007-10-14 18:56:15.000000000 +0000 @@ -9,7 +9,7 @@ #include -static ngx_int_t ngx_event_busy_lock_look_cachable(ngx_event_busy_lock_t *bl, +static ngx_int_t ngx_event_busy_lock_look_cacheable(ngx_event_busy_lock_t *bl, ngx_event_busy_lock_ctx_t *ctx); static void ngx_event_busy_lock_handler(ngx_event_t *ev); static void ngx_event_busy_lock_posted_handler(ngx_event_t *ev); @@ -65,14 +65,14 @@ ngx_int_t -ngx_event_busy_lock_cachable(ngx_event_busy_lock_t *bl, +ngx_event_busy_lock_cacheable(ngx_event_busy_lock_t *bl, ngx_event_busy_lock_ctx_t *ctx) { ngx_int_t rc; ngx_mutex_lock(bl->mutex); - rc = ngx_event_busy_lock_look_cachable(bl, ctx); + rc = ngx_event_busy_lock_look_cacheable(bl, ctx); ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ctx->event->log, 0, "event busy lock: %d w:%d mw:%d", @@ -201,14 +201,14 @@ static ngx_int_t -ngx_event_busy_lock_look_cachable(ngx_event_busy_lock_t *bl, +ngx_event_busy_lock_look_cacheable(ngx_event_busy_lock_t *bl, ngx_event_busy_lock_ctx_t *ctx) { ngx_int_t free; - ngx_uint_t i, bit, cachable, mask; + ngx_uint_t i, bit, cacheable, mask; bit = 0; - cachable = 0; + cacheable = 0; free = -1; #if (NGX_SUPPRESS_WARN) @@ -227,14 +227,14 @@ ctx->slot = i; return NGX_AGAIN; } - cachable++; + cacheable++; } else if (free == -1) { free = i; } - if (cachable == bl->cachable) { - if (free == -1 && cachable < bl->max_busy) { + if (cacheable == bl->cacheable) { + if (free == -1 && cacheable < bl->max_busy) { free = i + 1; } @@ -259,7 +259,7 @@ bl->md5_mask[free / 8] |= 1 << (free & 7); ctx->slot = free; - bl->cachable++; + bl->cacheable++; bl->busy++; return NGX_OK; diff -Nru nginx-0.5.33/src/event/ngx_event_busy_lock.h nginx-0.8.53/src/event/ngx_event_busy_lock.h --- nginx-0.5.33/src/event/ngx_event_busy_lock.h 2006-08-30 10:39:17.000000000 +0000 +++ nginx-0.8.53/src/event/ngx_event_busy_lock.h 2007-10-14 18:56:15.000000000 +0000 @@ -34,7 +34,7 @@ typedef struct { u_char *md5_mask; char *md5; - ngx_uint_t cachable; + ngx_uint_t cacheable; ngx_uint_t busy; ngx_uint_t max_busy; @@ -53,7 +53,7 @@ ngx_int_t ngx_event_busy_lock(ngx_event_busy_lock_t *bl, ngx_event_busy_lock_ctx_t *ctx); -ngx_int_t ngx_event_busy_lock_cachable(ngx_event_busy_lock_t *bl, +ngx_int_t ngx_event_busy_lock_cacheable(ngx_event_busy_lock_t *bl, ngx_event_busy_lock_ctx_t *ctx); void ngx_event_busy_unlock(ngx_event_busy_lock_t *bl, ngx_event_busy_lock_ctx_t *ctx); diff -Nru nginx-0.5.33/src/event/ngx_event.c nginx-0.8.53/src/event/ngx_event.c --- nginx-0.5.33/src/event/ngx_event.c 2007-11-07 13:54:40.000000000 +0000 +++ nginx-0.8.53/src/event/ngx_event.c 2010-03-12 14:31:47.000000000 +0000 @@ -43,7 +43,7 @@ ngx_event_actions_t ngx_event_actions; -ngx_atomic_t connection_counter = 1; +static ngx_atomic_t connection_counter = 1; ngx_atomic_t *ngx_connection_counter = &connection_counter; @@ -428,13 +428,10 @@ void ***cf; u_char *shared; size_t size, cl; - ngx_event_conf_t *ecf; - ngx_core_conf_t *ccf; ngx_shm_t shm; -#if !(NGX_WIN32) - ngx_int_t limit; - struct rlimit rlmt; -#endif + ngx_time_t *tp; + ngx_core_conf_t *ccf; + ngx_event_conf_t *ecf; cf = ngx_get_conf(cycle->conf_ctx, ngx_events_module); @@ -446,7 +443,7 @@ ecf = (*cf)[ngx_event_core_module.ctx_index]; - if (!ngx_test_config) { + if (!ngx_test_config && ngx_process <= NGX_PROCESS_MASTER) { ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "using the \"%s\" event method", ecf->name); } @@ -456,6 +453,9 @@ ngx_timer_resolution = ccf->timer_resolution; #if !(NGX_WIN32) + { + ngx_int_t limit; + struct rlimit rlmt; if (getrlimit(RLIMIT_NOFILE, &rlmt) == -1) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, @@ -475,7 +475,7 @@ ecf->connections, limit); } } - + } #endif /* !(NGX_WIN32) */ @@ -493,7 +493,8 @@ cl = 128; size = cl /* ngx_accept_mutex */ - + cl; /* ngx_connection_counter */ + + cl /* ngx_connection_counter */ + + cl; /* ngx_temp_number */ #if (NGX_STAT_STUB) @@ -507,6 +508,8 @@ #endif shm.size = size; + shm.name.len = sizeof("nginx_shared_zone"); + shm.name.data = (u_char *) "nginx_shared_zone"; shm.log = cycle->log; if (ngx_shm_alloc(&shm) != NGX_OK) { @@ -525,23 +528,29 @@ ngx_connection_counter = (ngx_atomic_t *) (shared + 1 * cl); -#if (NGX_STAT_STUB) - - ngx_stat_accepted = (ngx_atomic_t *) (shared + 2 * cl); - ngx_stat_handled = (ngx_atomic_t *) (shared + 3 * cl); - ngx_stat_requests = (ngx_atomic_t *) (shared + 4 * cl); - ngx_stat_active = (ngx_atomic_t *) (shared + 5 * cl); - ngx_stat_reading = (ngx_atomic_t *) (shared + 6 * cl); - ngx_stat_writing = (ngx_atomic_t *) (shared + 7 * cl); - -#endif - - *ngx_connection_counter = 1; + (void) ngx_atomic_cmp_set(ngx_connection_counter, 0, 1); ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "counter: %p, %d", ngx_connection_counter, *ngx_connection_counter); + ngx_temp_number = (ngx_atomic_t *) (shared + 2 * cl); + + tp = ngx_timeofday(); + + ngx_random_number = (tp->msec << 16) + ngx_pid; + +#if (NGX_STAT_STUB) + + ngx_stat_accepted = (ngx_atomic_t *) (shared + 3 * cl); + ngx_stat_handled = (ngx_atomic_t *) (shared + 4 * cl); + ngx_stat_requests = (ngx_atomic_t *) (shared + 5 * cl); + ngx_stat_active = (ngx_atomic_t *) (shared + 6 * cl); + ngx_stat_reading = (ngx_atomic_t *) (shared + 7 * cl); + ngx_stat_writing = (ngx_atomic_t *) (shared + 8 * cl); + +#endif + return NGX_OK; } @@ -553,8 +562,6 @@ { ngx_event_timer_alarm = 1; - ngx_time_update(0, 0); - #if 1 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ngx_cycle->log, 0, "timer signal"); #endif @@ -573,13 +580,6 @@ ngx_core_conf_t *ccf; ngx_event_conf_t *ecf; ngx_event_module_t *module; -#if (NGX_WIN32) - ngx_iocp_conf_t *iocpcf; -#else - struct rlimit rlmt; - struct sigaction sa; - struct itimerval itv; -#endif ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module); ecf = ngx_event_get_conf(cycle->conf_ctx, ngx_event_core_module); @@ -604,27 +604,30 @@ return NGX_ERROR; } - cycle->connection_n = ecf->connections; - for (m = 0; ngx_modules[m]; m++) { if (ngx_modules[m]->type != NGX_EVENT_MODULE) { continue; } - if (ngx_modules[m]->ctx_index == ecf->use) { - module = ngx_modules[m]->ctx; - if (module->actions.init(cycle, ngx_timer_resolution) == NGX_ERROR) - { - /* fatal */ - exit(2); - } - break; + if (ngx_modules[m]->ctx_index != ecf->use) { + continue; } + + module = ngx_modules[m]->ctx; + + if (module->actions.init(cycle, ngx_timer_resolution) != NGX_OK) { + /* fatal */ + exit(2); + } + + break; } #if !(NGX_WIN32) if (ngx_timer_resolution && !(ngx_event_flags & NGX_USE_TIMER_EVENT)) { + struct sigaction sa; + struct itimerval itv; ngx_memzero(&sa, sizeof(struct sigaction)); sa.sa_handler = ngx_timer_signal_handler; @@ -648,6 +651,7 @@ } if (ngx_event_flags & NGX_USE_FD_EVENT) { + struct rlimit rlmt; if (getrlimit(RLIMIT_NOFILE, &rlmt) == -1) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, @@ -666,15 +670,15 @@ #endif - cycle->connections = ngx_alloc(sizeof(ngx_connection_t) * ecf->connections, - cycle->log); + cycle->connections = + ngx_alloc(sizeof(ngx_connection_t) * cycle->connection_n, cycle->log); if (cycle->connections == NULL) { return NGX_ERROR; } c = cycle->connections; - cycle->read_events = ngx_alloc(sizeof(ngx_event_t) * ecf->connections, + cycle->read_events = ngx_alloc(sizeof(ngx_event_t) * cycle->connection_n, cycle->log); if (cycle->read_events == NULL) { return NGX_ERROR; @@ -690,7 +694,7 @@ #endif } - cycle->write_events = ngx_alloc(sizeof(ngx_event_t) * ecf->connections, + cycle->write_events = ngx_alloc(sizeof(ngx_event_t) * cycle->connection_n, cycle->log); if (cycle->write_events == NULL) { return NGX_ERROR; @@ -724,7 +728,7 @@ } while (i); cycle->free_connections = next; - cycle->free_connection_n = ecf->connections; + cycle->free_connection_n = cycle->connection_n; /* for each listening socket */ @@ -774,8 +778,14 @@ #if (NGX_WIN32) if (ngx_event_flags & NGX_USE_IOCP_EVENT) { + ngx_iocp_conf_t *iocpcf; + rev->handler = ngx_event_acceptex; + if (ngx_use_accept_mutex) { + continue; + } + if (ngx_add_event(rev, 0, NGX_IOCP_ACCEPT) == NGX_ERROR) { return NGX_ERROR; } @@ -792,6 +802,10 @@ } else { rev->handler = ngx_event_accept; + if (ngx_use_accept_mutex) { + continue; + } + if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) { return NGX_ERROR; } @@ -945,7 +959,7 @@ ngx_str_t *value; if (ecf->connections != NGX_CONF_UNSET_UINT) { - return "is duplicate" ; + return "is duplicate"; } if (ngx_strcmp(cmd->name.data, "connections") == 0) { @@ -980,7 +994,7 @@ ngx_event_module_t *module; if (ecf->use != NGX_CONF_UNSET_UINT) { - return "is duplicate" ; + return "is duplicate"; } value = cf->args->elts; @@ -1042,25 +1056,16 @@ ngx_str_t *value; ngx_event_debug_t *dc; struct hostent *h; - ngx_inet_cidr_t in_cidr; + ngx_cidr_t cidr; value = cf->args->elts; - /* AF_INET only */ - dc = ngx_array_push(&ecf->debug_connection); if (dc == NULL) { return NGX_CONF_ERROR; } - dc->addr = inet_addr((char *) value[1].data); - - if (dc->addr != INADDR_NONE) { - dc->mask = 0xffffffff; - return NGX_CONF_OK; - } - - rc = ngx_ptocidr(&value[1], &in_cidr); + rc = ngx_ptocidr(&value[1], &cidr); if (rc == NGX_DONE) { ngx_conf_log_error(NGX_LOG_WARN, cf, 0, @@ -1069,8 +1074,18 @@ } if (rc == NGX_OK) { - dc->mask = in_cidr.mask; - dc->addr = in_cidr.addr; + + /* AF_INET only */ + + if (cidr.family != AF_INET) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "\"debug_connection\" supports IPv4 only"); + return NGX_CONF_ERROR; + } + + dc->mask = cidr.u.in.mask; + dc->addr = cidr.u.in.addr; + return NGX_CONF_OK; } @@ -1104,7 +1119,7 @@ ecf = ngx_palloc(cycle->pool, sizeof(ngx_event_conf_t)); if (ecf == NULL) { - return NGX_CONF_ERROR; + return NULL; } ecf->connections = NGX_CONF_UNSET_UINT; @@ -1119,7 +1134,7 @@ if (ngx_array_init(&ecf->debug_connection, cycle->pool, 4, sizeof(ngx_event_debug_t)) == NGX_ERROR) { - return NGX_CONF_ERROR; + return NULL; } #endif @@ -1140,11 +1155,10 @@ ngx_uint_t rtsig; ngx_core_conf_t *ccf; #endif - ngx_int_t i, connections; + ngx_int_t i; ngx_module_t *module; ngx_event_module_t *event_module; - connections = NGX_CONF_UNSET_UINT; module = NULL; #if (NGX_HAVE_EPOLL) && !(NGX_TEST_BUILD_EPOLL) @@ -1153,11 +1167,9 @@ if (fd != -1) { close(fd); - connections = DEFAULT_CONNECTIONS; module = &ngx_epoll_module; } else if (ngx_errno != NGX_ENOSYS) { - connections = DEFAULT_CONNECTIONS; module = &ngx_epoll_module; } @@ -1166,7 +1178,6 @@ #if (NGX_HAVE_RTSIG) if (module == NULL) { - connections = DEFAULT_CONNECTIONS; module = &ngx_rtsig_module; rtsig = 1; @@ -1178,14 +1189,12 @@ #if (NGX_HAVE_DEVPOLL) - connections = DEFAULT_CONNECTIONS; module = &ngx_devpoll_module; #endif #if (NGX_HAVE_KQUEUE) - connections = DEFAULT_CONNECTIONS; module = &ngx_kqueue_module; #endif @@ -1193,12 +1202,6 @@ #if (NGX_HAVE_SELECT) if (module == NULL) { - -#if (NGX_WIN32 || FD_SETSIZE >= DEFAULT_CONNECTIONS) - connections = DEFAULT_CONNECTIONS; -#else - connections = FD_SETSIZE; -#endif module = &ngx_select_module; } @@ -1206,18 +1209,20 @@ if (module == NULL) { for (i = 0; ngx_modules[i]; i++) { - if (ngx_modules[i]->type == NGX_EVENT_MODULE) { - event_module = ngx_modules[i]->ctx; - if (ngx_strcmp(event_module->name->data, event_core_name.data) - == 0) - { - continue; - } + if (ngx_modules[i]->type != NGX_EVENT_MODULE) { + continue; + } - module = ngx_modules[i]; - break; + event_module = ngx_modules[i]->ctx; + + if (ngx_strcmp(event_module->name->data, event_core_name.data) == 0) + { + continue; } + + module = ngx_modules[i]; + break; } } @@ -1226,7 +1231,7 @@ return NGX_CONF_ERROR; } - ngx_conf_init_uint_value(ecf->connections, connections); + ngx_conf_init_uint_value(ecf->connections, DEFAULT_CONNECTIONS); cycle->connection_n = ecf->connections; ngx_conf_init_uint_value(ecf->use, module->ctx_index); diff -Nru nginx-0.5.33/src/event/ngx_event_connect.c nginx-0.8.53/src/event/ngx_event_connect.c --- nginx-0.5.33/src/event/ngx_event_connect.c 2007-11-07 13:54:40.000000000 +0000 +++ nginx-0.8.53/src/event/ngx_event_connect.c 2009-11-25 18:03:59.000000000 +0000 @@ -54,15 +54,7 @@ { ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno, "setsockopt(SO_RCVBUF) failed"); - - ngx_free_connection(c); - - if (ngx_close_socket(s) == -1) { - ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno, - ngx_close_socket_n " failed"); - } - - return NGX_ERROR; + goto failed; } } @@ -70,14 +62,16 @@ ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno, ngx_nonblocking_n " failed"); - ngx_free_connection(c); + goto failed; + } - if (ngx_close_socket(s) == -1) { - ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno, - ngx_close_socket_n " failed"); - } + if (pc->local) { + if (bind(s, pc->local->sockaddr, pc->local->socklen) == -1) { + ngx_log_error(NGX_LOG_CRIT, pc->log, ngx_socket_errno, + "bind(%V) failed", &pc->local->name); - return NGX_ERROR; + goto failed; + } } c->recv = ngx_recv; @@ -107,27 +101,22 @@ pc->connection = c; - /* - * TODO: MT: - ngx_atomic_fetch_add() - * or protection by critical section or mutex - * - * TODO: MP: - allocated in a shared memory - * - ngx_atomic_fetch_add() - * or protection by critical section or mutex - */ - c->number = ngx_atomic_fetch_add(ngx_connection_counter, 1); #if (NGX_THREADS) + + /* TODO: lock event when call completion handler */ + rev->lock = pc->lock; wev->lock = pc->lock; rev->own_lock = &c->lock; wev->own_lock = &c->lock; + #endif if (ngx_add_conn) { if (ngx_add_conn(c) == NGX_ERROR) { - return NGX_ERROR; + goto failed; } } @@ -139,12 +128,30 @@ if (rc == -1) { err = ngx_socket_errno; - /* Winsock returns WSAEWOULDBLOCK (NGX_EAGAIN) */ - if (err != NGX_EINPROGRESS && err != NGX_EAGAIN) { - - if (err == NGX_ECONNREFUSED || err == NGX_EHOSTUNREACH) { + if (err != NGX_EINPROGRESS +#if (NGX_WIN32) + /* Winsock returns WSAEWOULDBLOCK (NGX_EAGAIN) */ + && err != NGX_EAGAIN +#endif + ) + { + if (err == NGX_ECONNREFUSED +#if (NGX_LINUX) + /* + * Linux returns EAGAIN instead of ECONNREFUSED + * for unix sockets if listen queue is full + */ + || err == NGX_EAGAIN +#endif + || err == NGX_ECONNRESET + || err == NGX_ENETDOWN + || err == NGX_ENETUNREACH + || err == NGX_EHOSTDOWN + || err == NGX_EHOSTUNREACH) + { level = NGX_LOG_ERR; + } else { level = NGX_LOG_CRIT; } @@ -181,7 +188,7 @@ if (ngx_blocking(s) == -1) { ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno, ngx_blocking_n " failed"); - return NGX_ERROR; + goto failed; } /* @@ -211,7 +218,7 @@ } if (ngx_add_event(rev, NGX_READ_EVENT, event) != NGX_OK) { - return NGX_ERROR; + goto failed; } if (rc == -1) { @@ -219,7 +226,7 @@ /* NGX_EINPROGRESS */ if (ngx_add_event(wev, NGX_WRITE_EVENT, event) != NGX_OK) { - return NGX_ERROR; + goto failed; } return NGX_AGAIN; @@ -230,6 +237,17 @@ wev->ready = 1; return NGX_OK; + +failed: + + ngx_free_connection(c); + + if (ngx_close_socket(s) == -1) { + ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno, + ngx_close_socket_n " failed"); + } + + return NGX_ERROR; } diff -Nru nginx-0.5.33/src/event/ngx_event_connect.h nginx-0.8.53/src/event/ngx_event_connect.h --- nginx-0.5.33/src/event/ngx_event_connect.h 2007-07-29 17:25:06.000000000 +0000 +++ nginx-0.8.53/src/event/ngx_event_connect.h 2009-11-02 15:24:02.000000000 +0000 @@ -55,6 +55,8 @@ ngx_atomic_t *lock; #endif + ngx_addr_t *local; + int rcvbuf; ngx_log_t *log; @@ -70,5 +72,4 @@ ngx_int_t ngx_event_get_peer(ngx_peer_connection_t *pc, void *data); - #endif /* _NGX_EVENT_CONNECT_H_INCLUDED_ */ diff -Nru nginx-0.5.33/src/event/ngx_event.h nginx-0.8.53/src/event/ngx_event.h --- nginx-0.5.33/src/event/ngx_event.h 2007-11-07 13:54:40.000000000 +0000 +++ nginx-0.8.53/src/event/ngx_event.h 2009-08-30 09:52:39.000000000 +0000 @@ -136,6 +136,7 @@ /* to test on worker exit */ unsigned channel:1; + unsigned resolver:1; #if (NGX_THREADS) @@ -188,6 +189,37 @@ }; +#if (NGX_HAVE_FILE_AIO) + +struct ngx_event_aio_s { + void *data; + ngx_event_handler_pt handler; + ngx_file_t *file; + + ngx_fd_t fd; + +#if (NGX_HAVE_EVENTFD) + int64_t res; +#if (NGX_TEST_BUILD_EPOLL) + ngx_err_t err; + size_t nbytes; +#endif +#else + ngx_err_t err; + size_t nbytes; +#endif + +#if (NGX_HAVE_AIO_SENDFILE) + off_t last_offset; +#endif + + ngx_aiocb_t aiocb; + ngx_event_t event; +}; + +#endif + + typedef struct { in_addr_t mask; in_addr_t addr; @@ -291,6 +323,10 @@ */ #define NGX_USE_EVENTPORT_EVENT 0x00001000 +/* + * The event filter support vnode notifications: kqueue. + */ +#define NGX_USE_VNODE_EVENT 0x00002000 /* @@ -311,6 +347,11 @@ */ #define NGX_DISABLE_EVENT 2 +/* + * event must be passed to kernel right now, do not wait until batch processing. + */ +#define NGX_FLUSH_EVENT 4 + /* these flags have a meaning only for kqueue */ #define NGX_LOWAT_EVENT 0 @@ -326,11 +367,11 @@ #define NGX_VNODE_EVENT EVFILT_VNODE /* - * NGX_CLOSE_EVENT and NGX_LOWAT_EVENT are the module flags and they would - * not go into a kernel so we need to choose the value that would not interfere - * with any existent and future kqueue flags. kqueue has such values - - * EV_FLAG1, EV_EOF and EV_ERROR. They are reserved and cleared on a kernel - * entrance. + * NGX_CLOSE_EVENT, NGX_LOWAT_EVENT, and NGX_FLUSH_EVENT are the module flags + * and they must not go into a kernel so we need to choose the value + * that must not interfere with any existent and future kqueue flags. + * kqueue has such values - EV_FLAG1, EV_EOF, and EV_ERROR: + * they are reserved and cleared on a kernel entrance. */ #undef NGX_CLOSE_EVENT #define NGX_CLOSE_EVENT EV_EOF @@ -338,6 +379,9 @@ #undef NGX_LOWAT_EVENT #define NGX_LOWAT_EVENT EV_FLAG1 +#undef NGX_FLUSH_EVENT +#define NGX_FLUSH_EVENT EV_ERROR + #define NGX_LEVEL_EVENT 0 #define NGX_ONESHOT_EVENT EV_ONESHOT #define NGX_CLEAR_EVENT EV_CLEAR @@ -417,6 +461,7 @@ #define ngx_recv ngx_io.recv #define ngx_recv_chain ngx_io.recv_chain +#define ngx_udp_recv ngx_io.udp_recv #define ngx_send ngx_io.send #define ngx_send_chain ngx_io.send_chain diff -Nru nginx-0.5.33/src/event/ngx_event_openssl.c nginx-0.8.53/src/event/ngx_event_openssl.c --- nginx-0.5.33/src/event/ngx_event_openssl.c 2007-11-07 13:54:40.000000000 +0000 +++ nginx-0.8.53/src/event/ngx_event_openssl.c 2010-07-29 09:30:15.000000000 +0000 @@ -10,11 +10,13 @@ typedef struct { - ngx_str_t engine; + ngx_uint_t engine; /* unsigned engine:1; */ } ngx_openssl_conf_t; static int ngx_http_ssl_verify_callback(int ok, X509_STORE_CTX *x509_store); +static void ngx_ssl_info_callback(const ngx_ssl_conn_t *ssl_conn, int where, + int ret); static void ngx_ssl_handshake_handler(ngx_event_t *ev); static ngx_int_t ngx_ssl_handle_recv(ngx_connection_t *c, int n); static void ngx_ssl_write_handler(ngx_event_t *wev); @@ -22,6 +24,7 @@ static void ngx_ssl_shutdown_handler(ngx_event_t *ev); static void ngx_ssl_connection_error(ngx_connection_t *c, int sslerr, ngx_err_t err, char *text); +static void ngx_ssl_clear_error(ngx_log_t *log); static ngx_int_t ngx_ssl_session_cache_init(ngx_shm_zone_t *shm_zone, void *data); @@ -36,26 +39,17 @@ ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel); static void *ngx_openssl_create_conf(ngx_cycle_t *cycle); -static char *ngx_openssl_init_conf(ngx_cycle_t *cycle, void *conf); +static char *ngx_openssl_engine(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static void ngx_openssl_exit(ngx_cycle_t *cycle); -#if !(NGX_SSL_ENGINE) -static char *ngx_openssl_noengine(ngx_conf_t *cf, ngx_command_t *cmd, - void *conf); -#endif - static ngx_command_t ngx_openssl_commands[] = { { ngx_string("ssl_engine"), NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1, -#if (NGX_SSL_ENGINE) - ngx_conf_set_str_slot, -#else - ngx_openssl_noengine, -#endif + ngx_openssl_engine, + 0, 0, - offsetof(ngx_openssl_conf_t, engine), NULL }, ngx_null_command @@ -65,7 +59,7 @@ static ngx_core_module_t ngx_openssl_module_ctx = { ngx_string("openssl"), ngx_openssl_create_conf, - ngx_openssl_init_conf + NULL }; @@ -105,16 +99,14 @@ ngx_int_t ngx_ssl_init(ngx_log_t *log) { -#if OPENSSL_VERSION_NUMBER >= 0x00907000 OPENSSL_config(NULL); -#endif SSL_library_init(); SSL_load_error_strings(); -#if (NGX_SSL_ENGINE) ENGINE_load_builtin_engines(); -#endif + + OpenSSL_add_all_algorithms(); ngx_ssl_connection_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL); @@ -177,19 +169,18 @@ SSL_CTX_set_options(ssl->ctx, SSL_OP_TLS_D5_BUG); SSL_CTX_set_options(ssl->ctx, SSL_OP_TLS_BLOCK_PADDING_BUG); -#ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS SSL_CTX_set_options(ssl->ctx, SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS); -#endif + SSL_CTX_set_options(ssl->ctx, SSL_OP_SINGLE_DH_USE); if (ngx_ssl_protocols[protocols >> 1] != 0) { SSL_CTX_set_options(ssl->ctx, ngx_ssl_protocols[protocols >> 1]); } - SSL_CTX_set_mode(ssl->ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); - SSL_CTX_set_read_ahead(ssl->ctx, 1); + SSL_CTX_set_info_callback(ssl->ctx, ngx_ssl_info_callback); + return NGX_OK; } @@ -198,7 +189,7 @@ ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert, ngx_str_t *key) { - if (ngx_conf_full_name(cf->cycle, cert) == NGX_ERROR) { + if (ngx_conf_full_name(cf->cycle, cert, 1) != NGX_OK) { return NGX_ERROR; } @@ -211,7 +202,7 @@ return NGX_ERROR; } - if (ngx_conf_full_name(cf->cycle, key) == NGX_ERROR) { + if (ngx_conf_full_name(cf->cycle, key, 1) != NGX_OK) { return NGX_ERROR; } @@ -242,7 +233,7 @@ return NGX_OK; } - if (ngx_conf_full_name(cf->cycle, cert) == NGX_ERROR) { + if (ngx_conf_full_name(cf->cycle, cert, 1) != NGX_OK) { return NGX_ERROR; } @@ -276,13 +267,59 @@ } +ngx_int_t +ngx_ssl_crl(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *crl) +{ + X509_STORE *store; + X509_LOOKUP *lookup; + + if (crl->len == 0) { + return NGX_OK; + } + + if (ngx_conf_full_name(cf->cycle, crl, 1) != NGX_OK) { + return NGX_ERROR; + } + + store = SSL_CTX_get_cert_store(ssl->ctx); + + if (store == NULL) { + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, + "SSL_CTX_get_cert_store() failed"); + return NGX_ERROR; + } + + lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file()); + + if (lookup == NULL) { + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, + "X509_STORE_add_lookup() failed"); + return NGX_ERROR; + } + + if (X509_LOOKUP_load_file(lookup, (char *) crl->data, X509_FILETYPE_PEM) + == 0) + { + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, + "X509_LOOKUP_load_file(\"%s\") failed", crl->data); + return NGX_ERROR; + } + + X509_STORE_set_flags(store, + X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL); + + return NGX_OK; +} + + static int ngx_http_ssl_verify_callback(int ok, X509_STORE_CTX *x509_store) { +#if (NGX_DEBUG) char *subject, *issuer; int err, depth; X509 *cert; - X509_NAME *name; + X509_NAME *sname, *iname; ngx_connection_t *c; ngx_ssl_conn_t *ssl_conn; @@ -295,21 +332,46 @@ err = X509_STORE_CTX_get_error(x509_store); depth = X509_STORE_CTX_get_error_depth(x509_store); - name = X509_get_subject_name(cert); - subject = name ? X509_NAME_oneline(name, NULL, 0) : "(none)"; + sname = X509_get_subject_name(cert); + subject = sname ? X509_NAME_oneline(sname, NULL, 0) : "(none)"; - name = X509_get_issuer_name(cert); - issuer = name ? X509_NAME_oneline(name, NULL, 0) : "(none)"; + iname = X509_get_issuer_name(cert); + issuer = iname ? X509_NAME_oneline(iname, NULL, 0) : "(none)"; ngx_log_debug5(NGX_LOG_DEBUG_EVENT, c->log, 0, "verify:%d, error:%d, depth:%d, " "subject:\"%s\",issuer: \"%s\"", ok, err, depth, subject, issuer); + if (sname) { + OPENSSL_free(subject); + } + + if (iname) { + OPENSSL_free(issuer); + } +#endif + return 1; } +static void +ngx_ssl_info_callback(const ngx_ssl_conn_t *ssl_conn, int where, int ret) +{ + ngx_connection_t *c; + + if (where & SSL_CB_HANDSHAKE_START) { + c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn); + + if (c->ssl->handshaked) { + c->ssl->renegotiation = 1; + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL renegotiation"); + } + } +} + + ngx_int_t ngx_ssl_generate_rsa512_key(ngx_ssl_t *ssl) { @@ -336,6 +398,89 @@ ngx_int_t +ngx_ssl_dhparam(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file) +{ + DH *dh; + BIO *bio; + + /* + * -----BEGIN DH PARAMETERS----- + * MIGHAoGBALu8LcrYRnSQfEP89YDpz9vZWKP1aLQtSwju1OsPs1BMbAMCducQgAxc + * y7qokiYUxb7spWWl/fHSh6K8BJvmd4Bg6RqSp1fjBI9osHb302zI8pul34HcLKcl + * 7OZicMyaUDXYzs7vnqAnSmOrHlj6/UmI0PZdFGdX2gcd8EXP4WubAgEC + * -----END DH PARAMETERS----- + */ + + static unsigned char dh1024_p[] = { + 0xBB, 0xBC, 0x2D, 0xCA, 0xD8, 0x46, 0x74, 0x90, 0x7C, 0x43, 0xFC, 0xF5, + 0x80, 0xE9, 0xCF, 0xDB, 0xD9, 0x58, 0xA3, 0xF5, 0x68, 0xB4, 0x2D, 0x4B, + 0x08, 0xEE, 0xD4, 0xEB, 0x0F, 0xB3, 0x50, 0x4C, 0x6C, 0x03, 0x02, 0x76, + 0xE7, 0x10, 0x80, 0x0C, 0x5C, 0xCB, 0xBA, 0xA8, 0x92, 0x26, 0x14, 0xC5, + 0xBE, 0xEC, 0xA5, 0x65, 0xA5, 0xFD, 0xF1, 0xD2, 0x87, 0xA2, 0xBC, 0x04, + 0x9B, 0xE6, 0x77, 0x80, 0x60, 0xE9, 0x1A, 0x92, 0xA7, 0x57, 0xE3, 0x04, + 0x8F, 0x68, 0xB0, 0x76, 0xF7, 0xD3, 0x6C, 0xC8, 0xF2, 0x9B, 0xA5, 0xDF, + 0x81, 0xDC, 0x2C, 0xA7, 0x25, 0xEC, 0xE6, 0x62, 0x70, 0xCC, 0x9A, 0x50, + 0x35, 0xD8, 0xCE, 0xCE, 0xEF, 0x9E, 0xA0, 0x27, 0x4A, 0x63, 0xAB, 0x1E, + 0x58, 0xFA, 0xFD, 0x49, 0x88, 0xD0, 0xF6, 0x5D, 0x14, 0x67, 0x57, 0xDA, + 0x07, 0x1D, 0xF0, 0x45, 0xCF, 0xE1, 0x6B, 0x9B + }; + + static unsigned char dh1024_g[] = { 0x02 }; + + + if (file->len == 0) { + + dh = DH_new(); + if (dh == NULL) { + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, "DH_new() failed"); + return NGX_ERROR; + } + + dh->p = BN_bin2bn(dh1024_p, sizeof(dh1024_p), NULL); + dh->g = BN_bin2bn(dh1024_g, sizeof(dh1024_g), NULL); + + if (dh->p == NULL || dh->g == NULL) { + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, "BN_bin2bn() failed"); + DH_free(dh); + return NGX_ERROR; + } + + SSL_CTX_set_tmp_dh(ssl->ctx, dh); + + DH_free(dh); + + return NGX_OK; + } + + if (ngx_conf_full_name(cf->cycle, file, 1) != NGX_OK) { + return NGX_ERROR; + } + + bio = BIO_new_file((char *) file->data, "r"); + if (bio == NULL) { + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, + "BIO_new_file(\"%s\") failed", file->data); + return NGX_ERROR; + } + + dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL); + if (dh == NULL) { + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, + "PEM_read_bio_DHparams(\"%s\") failed", file->data); + BIO_free(bio); + return NGX_ERROR; + } + + SSL_CTX_set_tmp_dh(ssl->ctx, dh); + + DH_free(dh); + BIO_free(bio); + + return NGX_OK; +} + + +ngx_int_t ngx_ssl_create_connection(ngx_ssl_t *ssl, ngx_connection_t *c, ngx_uint_t flags) { ngx_ssl_connection_t *sc; @@ -345,14 +490,7 @@ return NGX_ERROR; } - if (flags & NGX_SSL_BUFFER) { - sc->buffer = 1; - - sc->buf = ngx_create_temp_buf(c->pool, NGX_SSL_BUFSIZE); - if (sc->buf == NULL) { - return NGX_ERROR; - } - } + sc->buffer = ((flags & NGX_SSL_BUFFER) != 0); sc->connection = SSL_new(ssl->ctx); @@ -404,23 +542,28 @@ int n, sslerr; ngx_err_t err; + ngx_ssl_clear_error(c->log); + n = SSL_do_handshake(c->ssl->connection); ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_do_handshake: %d", n); if (n == 1) { - if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) { + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { return NGX_ERROR; } - if (ngx_handle_write_event(c->write, 0) == NGX_ERROR) { + if (ngx_handle_write_event(c->write, 0) != NGX_OK) { return NGX_ERROR; } #if (NGX_DEBUG) { char buf[129], *s, *d; +#if OPENSSL_VERSION_NUMBER >= 0x1000000fL + const +#endif SSL_CIPHER *cipher; cipher = SSL_get_current_cipher(c->ssl->connection); @@ -469,6 +612,11 @@ c->recv_chain = ngx_ssl_recv_chain; c->send_chain = ngx_ssl_send_chain; + /* initial handshake done, disable renegotiation (CVE-2009-3555) */ + if (c->ssl->connection->s3) { + c->ssl->connection->s3->flags |= SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS; + } + return NGX_OK; } @@ -481,7 +629,7 @@ c->read->handler = ngx_ssl_handshake_handler; c->write->handler = ngx_ssl_handshake_handler; - if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) { + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { return NGX_ERROR; } @@ -493,7 +641,7 @@ c->read->handler = ngx_ssl_handshake_handler; c->write->handler = ngx_ssl_handshake_handler; - if (ngx_handle_write_event(c->write, 0) == NGX_ERROR) { + if (ngx_handle_write_event(c->write, 0) != NGX_OK) { return NGX_ERROR; } @@ -579,6 +727,11 @@ } if (bytes) { + + if (n == 0 || n == NGX_ERROR) { + c->read->ready = 1; + } + return bytes; } @@ -593,15 +746,20 @@ int n, bytes; if (c->ssl->last == NGX_ERROR) { + c->read->error = 1; return NGX_ERROR; } if (c->ssl->last == NGX_DONE) { + c->read->ready = 0; + c->read->eof = 1; return 0; } bytes = 0; + ngx_ssl_clear_error(c->log); + /* * SSL_read() may return data in parts, so try to read * until SSL_read() would return no data @@ -619,26 +777,38 @@ c->ssl->last = ngx_ssl_handle_recv(c, n); - if (c->ssl->last != NGX_OK) { + if (c->ssl->last == NGX_OK) { + + size -= n; - if (bytes) { + if (size == 0) { return bytes; } - if (c->ssl->last == NGX_DONE) { - return 0; - } + buf += n; - return c->ssl->last; + continue; } - size -= n; - - if (size == 0) { + if (bytes) { return bytes; } - buf += n; + switch (c->ssl->last) { + + case NGX_DONE: + c->read->ready = 0; + c->read->eof = 1; + return 0; + + case NGX_ERROR: + c->read->error = 1; + + /* fall thruogh */ + + case NGX_AGAIN: + return c->ssl->last; + } } } @@ -649,6 +819,21 @@ int sslerr; ngx_err_t err; + if (c->ssl->renegotiation) { + /* + * disable renegotiation (CVE-2009-3555): + * OpenSSL (at least up to 0.9.8l) does not handle disabled + * renegotiation gracefully, so drop connection here + */ + + ngx_log_error(NGX_LOG_NOTICE, c->log, 0, "SSL renegotiation disabled"); + + c->ssl->no_wait_shutdown = 1; + c->ssl->no_send_shutdown = 1; + + return NGX_ERROR; + } + if (n > 0) { if (c->ssl->saved_write_handler) { @@ -657,7 +842,7 @@ c->ssl->saved_write_handler = NULL; c->write->ready = 1; - if (ngx_handle_write_event(c->write, 0) == NGX_ERROR) { + if (ngx_handle_write_event(c->write, 0) != NGX_OK) { return NGX_ERROR; } @@ -685,7 +870,7 @@ c->write->ready = 0; - if (ngx_handle_write_event(c->write, 0) == NGX_ERROR) { + if (ngx_handle_write_event(c->write, 0) != NGX_OK) { return NGX_ERROR; } @@ -703,7 +888,6 @@ c->ssl->no_wait_shutdown = 1; c->ssl->no_send_shutdown = 1; - c->read->eof = 1; if (sslerr == SSL_ERROR_ZERO_RETURN || ERR_peek_error() == 0) { ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, @@ -711,7 +895,6 @@ return NGX_DONE; } - c->read->error = 1; ngx_ssl_connection_error(c, sslerr, err, "SSL_read() failed"); return NGX_ERROR; @@ -745,14 +928,7 @@ ssize_t send, size; ngx_buf_t *buf; - if (!c->ssl->buffer - || (in && in->next == NULL && !(c->buffered & NGX_SSL_BUFFERED))) - { - /* - * we avoid a buffer copy if - * we do not need to buffer the output - * or the incoming buf is a single and our buffer is empty - */ + if (!c->ssl->buffer) { while (in) { if (ngx_buf_special(in->buf)) { @@ -788,14 +964,34 @@ limit = NGX_MAX_UINT32_VALUE - ngx_pagesize; } - buf = c->ssl->buf; + + if (buf == NULL) { + buf = ngx_create_temp_buf(c->pool, NGX_SSL_BUFSIZE); + if (buf == NULL) { + return NGX_CHAIN_ERROR; + } + + c->ssl->buf = buf; + } + + if (buf->start == NULL) { + buf->start = ngx_palloc(c->pool, NGX_SSL_BUFSIZE); + if (buf->start == NULL) { + return NGX_CHAIN_ERROR; + } + + buf->pos = buf->start; + buf->last = buf->start; + buf->end = buf->start + NGX_SSL_BUFSIZE; + } + send = 0; flush = (in == NULL) ? 1 : 0; for ( ;; ) { - while (in && buf->last < buf->end) { + while (in && buf->last < buf->end && send < limit) { if (in->buf->last_buf || in->buf->flush) { flush = 1; } @@ -822,8 +1018,8 @@ ngx_memcpy(buf->last, in->buf->pos, size); buf->last += size; - in->buf->pos += size; + send += size; if (in->buf->pos == in->buf->last) { in = in->next; @@ -848,7 +1044,6 @@ } buf->pos += n; - send += n; c->sent += n; if (n < size) { @@ -882,6 +1077,8 @@ int n, sslerr; ngx_err_t err; + ngx_ssl_clear_error(c->log); + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL to write: %d", size); n = SSL_write(c->ssl->connection, data, size); @@ -896,7 +1093,7 @@ c->ssl->saved_read_handler = NULL; c->read->ready = 1; - if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) { + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { return NGX_ERROR; } @@ -924,7 +1121,7 @@ c->read->ready = 0; - if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) { + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { return NGX_ERROR; } @@ -962,12 +1159,22 @@ } +void +ngx_ssl_free_buffer(ngx_connection_t *c) +{ + if (c->ssl->buf && c->ssl->buf->start) { + if (ngx_pfree(c->pool, c->ssl->buf->start) == NGX_OK) { + c->ssl->buf->start = NULL; + } + } +} + + ngx_int_t ngx_ssl_shutdown(ngx_connection_t *c) { - int n, sslerr, mode; - ngx_err_t err; - ngx_uint_t again; + int n, sslerr, mode; + ngx_err_t err; if (c->timedout) { mode = SSL_RECEIVED_SHUTDOWN|SSL_SENT_SHUTDOWN; @@ -986,52 +1193,43 @@ SSL_set_shutdown(c->ssl->connection, mode); - again = 0; - sslerr = 0; - - for ( ;; ) { - n = SSL_shutdown(c->ssl->connection); + ngx_ssl_clear_error(c->log); - ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_shutdown: %d", n); + n = SSL_shutdown(c->ssl->connection); - if (n == 1 || (n == 0 && c->timedout)) { - SSL_free(c->ssl->connection); - c->ssl = NULL; + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_shutdown: %d", n); - return NGX_OK; - } - - if (n == 0) { - again = 1; - break; - } + sslerr = 0; - break; - } + /* SSL_shutdown() never returns -1, on error it returns 0 */ - if (!again) { + if (n != 1 && ERR_peek_error()) { sslerr = SSL_get_error(c->ssl->connection, n); ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_get_error: %d", sslerr); } - if (again - || sslerr == SSL_ERROR_WANT_READ - || sslerr == SSL_ERROR_WANT_WRITE) - { + if (n == 1 || sslerr == 0 || sslerr == SSL_ERROR_ZERO_RETURN) { + SSL_free(c->ssl->connection); + c->ssl = NULL; + + return NGX_OK; + } + + if (sslerr == SSL_ERROR_WANT_READ || sslerr == SSL_ERROR_WANT_WRITE) { c->read->handler = ngx_ssl_shutdown_handler; c->write->handler = ngx_ssl_shutdown_handler; - if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) { + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { return NGX_ERROR; } - if (ngx_handle_write_event(c->write, 0) == NGX_ERROR) { + if (ngx_handle_write_event(c->write, 0) != NGX_OK) { return NGX_ERROR; } - if (again || sslerr == SSL_ERROR_WANT_READ) { + if (sslerr == SSL_ERROR_WANT_READ) { ngx_add_timer(c->read, 30000); } @@ -1076,6 +1274,7 @@ ngx_ssl_connection_error(ngx_connection_t *c, int sslerr, ngx_err_t err, char *text) { + int n; ngx_uint_t level; level = NGX_LOG_CRIT; @@ -1085,10 +1284,11 @@ if (err == NGX_ECONNRESET || err == NGX_EPIPE || err == NGX_ENOTCONN -#if !(NGX_CRIT_ETIMEDOUT) || err == NGX_ETIMEDOUT -#endif || err == NGX_ECONNREFUSED + || err == NGX_ENETDOWN + || err == NGX_ENETUNREACH + || err == NGX_EHOSTDOWN || err == NGX_EHOSTUNREACH) { switch (c->log_error) { @@ -1106,28 +1306,99 @@ break; } } + + } else if (sslerr == SSL_ERROR_SSL) { + + n = ERR_GET_REASON(ERR_peek_error()); + + /* handshake failures */ + if (n == SSL_R_BLOCK_CIPHER_PAD_IS_WRONG /* 129 */ + || n == SSL_R_DIGEST_CHECK_FAILED /* 149 */ + || n == SSL_R_LENGTH_MISMATCH /* 159 */ + || n == SSL_R_NO_CIPHERS_PASSED /* 182 */ + || n == SSL_R_NO_CIPHERS_SPECIFIED /* 183 */ + || n == SSL_R_NO_SHARED_CIPHER /* 193 */ + || n == SSL_R_RECORD_LENGTH_MISMATCH /* 213 */ + || n == SSL_R_UNEXPECTED_MESSAGE /* 244 */ + || n == SSL_R_UNEXPECTED_RECORD /* 245 */ + || n == SSL_R_UNKNOWN_ALERT_TYPE /* 246 */ + || n == SSL_R_UNKNOWN_PROTOCOL /* 252 */ + || n == SSL_R_WRONG_VERSION_NUMBER /* 267 */ + || n == SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC /* 281 */ + || n == 1000 /* SSL_R_SSLV3_ALERT_CLOSE_NOTIFY */ + || n == SSL_R_SSLV3_ALERT_UNEXPECTED_MESSAGE /* 1010 */ + || n == SSL_R_SSLV3_ALERT_BAD_RECORD_MAC /* 1020 */ + || n == SSL_R_TLSV1_ALERT_DECRYPTION_FAILED /* 1021 */ + || n == SSL_R_TLSV1_ALERT_RECORD_OVERFLOW /* 1022 */ + || n == SSL_R_SSLV3_ALERT_DECOMPRESSION_FAILURE /* 1030 */ + || n == SSL_R_SSLV3_ALERT_HANDSHAKE_FAILURE /* 1040 */ + || n == SSL_R_SSLV3_ALERT_NO_CERTIFICATE /* 1041 */ + || n == SSL_R_SSLV3_ALERT_BAD_CERTIFICATE /* 1042 */ + || n == SSL_R_SSLV3_ALERT_UNSUPPORTED_CERTIFICATE /* 1043 */ + || n == SSL_R_SSLV3_ALERT_CERTIFICATE_REVOKED /* 1044 */ + || n == SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED /* 1045 */ + || n == SSL_R_SSLV3_ALERT_CERTIFICATE_UNKNOWN /* 1046 */ + || n == SSL_R_SSLV3_ALERT_ILLEGAL_PARAMETER /* 1047 */ + || n == SSL_R_TLSV1_ALERT_UNKNOWN_CA /* 1048 */ + || n == SSL_R_TLSV1_ALERT_ACCESS_DENIED /* 1049 */ + || n == SSL_R_TLSV1_ALERT_DECODE_ERROR /* 1050 */ + || n == SSL_R_TLSV1_ALERT_DECRYPT_ERROR /* 1051 */ + || n == SSL_R_TLSV1_ALERT_EXPORT_RESTRICTION /* 1060 */ + || n == SSL_R_TLSV1_ALERT_PROTOCOL_VERSION /* 1070 */ + || n == SSL_R_TLSV1_ALERT_INSUFFICIENT_SECURITY /* 1071 */ + || n == SSL_R_TLSV1_ALERT_INTERNAL_ERROR /* 1080 */ + || n == SSL_R_TLSV1_ALERT_USER_CANCELLED /* 1090 */ + || n == SSL_R_TLSV1_ALERT_NO_RENEGOTIATION) /* 1100 */ + { + switch (c->log_error) { + + case NGX_ERROR_IGNORE_ECONNRESET: + case NGX_ERROR_INFO: + level = NGX_LOG_INFO; + break; + + case NGX_ERROR_ERR: + level = NGX_LOG_ERR; + break; + + default: + break; + } + } } ngx_ssl_error(level, c->log, err, text); } +static void +ngx_ssl_clear_error(ngx_log_t *log) +{ + while (ERR_peek_error()) { + ngx_ssl_error(NGX_LOG_ALERT, log, 0, "ignoring stale global SSL error"); + } + + ERR_clear_error(); +} + + void ngx_cdecl ngx_ssl_error(ngx_uint_t level, ngx_log_t *log, ngx_err_t err, char *fmt, ...) { - u_long n; - va_list args; - u_char errstr[NGX_MAX_CONF_ERRSTR], *p, *last; + u_long n; + va_list args; + u_char *p, *last; + u_char errstr[NGX_MAX_CONF_ERRSTR]; last = errstr + NGX_MAX_CONF_ERRSTR; va_start(args, fmt); - p = ngx_vsnprintf(errstr, sizeof(errstr) - 1, fmt, args); + p = ngx_vslprintf(errstr, last - 1, fmt, args); va_end(args); p = ngx_cpystrn(p, (u_char *) " (SSL:", last - p); - while (p < last) { + for ( ;; ) { n = ERR_get_error(); @@ -1135,6 +1406,10 @@ break; } + if (p >= last) { + continue; + } + *p++ = ' '; ERR_error_string_n(n, (char *) p, last - p); @@ -1154,6 +1429,36 @@ { long cache_mode; + if (builtin_session_cache == NGX_SSL_NO_SCACHE) { + SSL_CTX_set_session_cache_mode(ssl->ctx, SSL_SESS_CACHE_OFF); + return NGX_OK; + } + + SSL_CTX_set_session_id_context(ssl->ctx, sess_ctx->data, sess_ctx->len); + + if (builtin_session_cache == NGX_SSL_NONE_SCACHE) { + + /* + * If the server explicitly says that it does not support + * session reuse (see SSL_SESS_CACHE_OFF above), then + * Outlook Express fails to upload a sent email to + * the Sent Items folder on the IMAP server via a separate IMAP + * connection in the background. Therefore we have a special + * mode (SSL_SESS_CACHE_SERVER|SSL_SESS_CACHE_NO_INTERNAL_STORE) + * where the server pretends that it supports session reuse, + * but it does not actually store any session. + */ + + SSL_CTX_set_session_cache_mode(ssl->ctx, + SSL_SESS_CACHE_SERVER + |SSL_SESS_CACHE_NO_AUTO_CLEAR + |SSL_SESS_CACHE_NO_INTERNAL_STORE); + + SSL_CTX_sess_set_cache_size(ssl->ctx, 1); + + return NGX_OK; + } + cache_mode = SSL_SESS_CACHE_SERVER; if (shm_zone && builtin_session_cache == NGX_SSL_NO_BUILTIN_SCACHE) { @@ -1162,8 +1467,6 @@ SSL_CTX_set_session_cache_mode(ssl->ctx, cache_mode); - SSL_CTX_set_session_id_context(ssl->ctx, sess_ctx->data, sess_ctx->len); - if (builtin_session_cache != NGX_SSL_NO_BUILTIN_SCACHE) { if (builtin_session_cache != NGX_SSL_DFLT_BUILTIN_SCACHE) { @@ -1171,7 +1474,7 @@ } } - SSL_CTX_set_timeout(ssl->ctx, timeout); + SSL_CTX_set_timeout(ssl->ctx, (long) timeout); if (shm_zone) { shm_zone->init = ngx_ssl_session_cache_init; @@ -1196,8 +1499,8 @@ static ngx_int_t ngx_ssl_session_cache_init(ngx_shm_zone_t *shm_zone, void *data) { + size_t len; ngx_slab_pool_t *shpool; - ngx_rbtree_node_t *sentinel; ngx_ssl_session_cache_t *cache; if (data) { @@ -1205,6 +1508,11 @@ return NGX_OK; } + if (shm_zone->shm.exists) { + shm_zone->data = data; + return NGX_OK; + } + shpool = (ngx_slab_pool_t *) shm_zone->shm.addr; cache = ngx_slab_alloc(shpool, sizeof(ngx_ssl_session_cache_t)); @@ -1212,29 +1520,23 @@ return NGX_ERROR; } - cache->session_cache_head.prev = NULL; - cache->session_cache_head.next = &cache->session_cache_tail; + shpool->data = cache; + shm_zone->data = cache; - cache->session_cache_tail.prev = &cache->session_cache_head; - cache->session_cache_tail.next = NULL; + ngx_rbtree_init(&cache->session_rbtree, &cache->sentinel, + ngx_ssl_session_rbtree_insert_value); - cache->session_rbtree = ngx_slab_alloc(shpool, sizeof(ngx_rbtree_t)); - if (cache->session_rbtree == NULL) { - return NGX_ERROR; - } + ngx_queue_init(&cache->expire_queue); + + len = sizeof(" in SSL session shared cache \"\"") + shm_zone->shm.name.len; - sentinel = ngx_slab_alloc(shpool, sizeof(ngx_rbtree_node_t)); - if (sentinel == NULL) { + shpool->log_ctx = ngx_slab_alloc(shpool, len); + if (shpool->log_ctx == NULL) { return NGX_ERROR; } - ngx_rbtree_sentinel_init(sentinel); - - cache->session_rbtree->root = sentinel; - cache->session_rbtree->sentinel = sentinel; - cache->session_rbtree->insert = ngx_ssl_session_rbtree_insert_value; - - shm_zone->data = cache; + ngx_sprintf(shpool->log_ctx, " in SSL session shared cache \"%V\"%Z", + &shm_zone->shm.name); return NGX_OK; } @@ -1264,7 +1566,6 @@ u_char *p, *id, *cached_sess; uint32_t hash; SSL_CTX *ssl_ctx; - ngx_time_t *tp; ngx_shm_zone_t *shm_zone; ngx_connection_t *c; ngx_slab_pool_t *shpool; @@ -1337,25 +1638,20 @@ hash = ngx_crc32_short(sess->session_id, sess->session_id_length); ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, - "http ssl new session: %08XD:%d:%d", + "ssl new session: %08XD:%d:%d", hash, sess->session_id_length, len); - tp = ngx_timeofday(); - sess_id->node.key = hash; sess_id->node.data = (u_char) sess->session_id_length; sess_id->id = id; sess_id->len = len; sess_id->session = cached_sess; - sess_id->expire = tp->sec + SSL_CTX_get_timeout(ssl_ctx); + sess_id->expire = ngx_time() + SSL_CTX_get_timeout(ssl_ctx); - sess_id->next = cache->session_cache_head.next; - sess_id->next->prev = sess_id; - sess_id->prev = &cache->session_cache_head; - cache->session_cache_head.next = sess_id; + ngx_queue_insert_head(&cache->expire_queue, &sess_id->queue); - ngx_rbtree_insert(cache->session_rbtree, &sess_id->node); + ngx_rbtree_insert(&cache->session_rbtree, &sess_id->node); ngx_shmtx_unlock(&shpool->mutex); @@ -1390,7 +1686,6 @@ u_char *p; uint32_t hash; ngx_int_t rc; - ngx_time_t *tp; ngx_shm_zone_t *shm_zone; ngx_slab_pool_t *shpool; ngx_connection_t *c; @@ -1406,25 +1701,21 @@ *copy = 0; ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, - "http ssl get session: %08XD:%d", hash, len); + "ssl get session: %08XD:%d", hash, len); shm_zone = SSL_CTX_get_ex_data(SSL_get_SSL_CTX(ssl_conn), ngx_ssl_session_cache_index); cache = shm_zone->data; - if (cache->session_rbtree == NULL) { - return NULL; - } - sess = NULL; shpool = (ngx_slab_pool_t *) shm_zone->shm.addr; ngx_shmtx_lock(&shpool->mutex); - node = cache->session_rbtree->root; - sentinel = cache->session_rbtree->sentinel; + node = cache->session_rbtree.root; + sentinel = cache->session_rbtree.sentinel; while (node != sentinel) { @@ -1447,9 +1738,7 @@ (size_t) len, (size_t) node->data); if (rc == 0) { - tp = ngx_timeofday(); - - if (sess_id->expire > tp->sec) { + if (sess_id->expire > ngx_time()) { ngx_memcpy(buf, sess_id->session, sess_id->len); ngx_shmtx_unlock(&shpool->mutex); @@ -1460,10 +1749,9 @@ return sess; } - sess_id->next->prev = sess_id->prev; - sess_id->prev->next = sess_id->next; + ngx_queue_remove(&sess_id->queue); - ngx_rbtree_delete(cache->session_rbtree, node); + ngx_rbtree_delete(&cache->session_rbtree, node); ngx_slab_free_locked(shpool, sess_id->session); #if (NGX_PTR_SIZE == 4) @@ -1491,6 +1779,15 @@ } +void +ngx_ssl_remove_cached_session(SSL_CTX *ssl, ngx_ssl_session_t *sess) +{ + SSL_CTX_remove_session(ssl, sess); + + ngx_ssl_remove_session(ssl, sess); +} + + static void ngx_ssl_remove_session(SSL_CTX *ssl, ngx_ssl_session_t *sess) { @@ -1506,6 +1803,10 @@ shm_zone = SSL_CTX_get_ex_data(ssl, ngx_ssl_session_cache_index); + if (shm_zone == NULL) { + return; + } + cache = shm_zone->data; id = sess->session_id; @@ -1514,14 +1815,14 @@ hash = ngx_crc32_short(id, len); ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ngx_cycle->log, 0, - "http ssl remove session: %08XD:%uz", hash, len); + "ssl remove session: %08XD:%uz", hash, len); shpool = (ngx_slab_pool_t *) shm_zone->shm.addr; ngx_shmtx_lock(&shpool->mutex); - node = cache->session_rbtree->root; - sentinel = cache->session_rbtree->sentinel; + node = cache->session_rbtree.root; + sentinel = cache->session_rbtree.sentinel; while (node != sentinel) { @@ -1543,10 +1844,10 @@ rc = ngx_memn2cmp(id, sess_id->id, len, (size_t) node->data); if (rc == 0) { - sess_id->next->prev = sess_id->prev; - sess_id->prev->next = sess_id->next; - ngx_rbtree_delete(cache->session_rbtree, node); + ngx_queue_remove(&sess_id->queue); + + ngx_rbtree_delete(&cache->session_rbtree, node); ngx_slab_free_locked(shpool, sess_id->session); #if (NGX_PTR_SIZE == 4) @@ -1574,31 +1875,33 @@ ngx_ssl_expire_sessions(ngx_ssl_session_cache_t *cache, ngx_slab_pool_t *shpool, ngx_uint_t n) { - ngx_time_t *tp; + time_t now; + ngx_queue_t *q; ngx_ssl_sess_id_t *sess_id; - tp = ngx_timeofday(); + now = ngx_time(); while (n < 3) { - sess_id = cache->session_cache_tail.prev; - - if (sess_id == &cache->session_cache_head) { + if (ngx_queue_empty(&cache->expire_queue)) { return; } - if (n++ != 0 && sess_id->expire > tp->sec) { + q = ngx_queue_last(&cache->expire_queue); + + sess_id = ngx_queue_data(q, ngx_ssl_sess_id_t, queue); + + if (n++ != 0 && sess_id->expire > now) { return; } - sess_id->next->prev = sess_id->prev; - sess_id->prev->next = sess_id->next; - - ngx_rbtree_delete(cache->session_rbtree, &sess_id->node); + ngx_queue_remove(q); ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ngx_cycle->log, 0, "expire session: %08Xi", sess_id->node.key); + ngx_rbtree_delete(&cache->session_rbtree, &sess_id->node); + ngx_slab_free_locked(shpool, sess_id->session); #if (NGX_PTR_SIZE == 4) ngx_slab_free_locked(shpool, sess_id->id); @@ -1612,56 +1915,37 @@ ngx_ssl_session_rbtree_insert_value(ngx_rbtree_node_t *temp, ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel) { - ngx_ssl_sess_id_t *sess_id, *sess_id_temp; + ngx_rbtree_node_t **p; + ngx_ssl_sess_id_t *sess_id, *sess_id_temp; for ( ;; ) { if (node->key < temp->key) { - if (temp->left == sentinel) { - temp->left = node; - break; - } - - temp = temp->left; + p = &temp->left; } else if (node->key > temp->key) { - if (temp->right == sentinel) { - temp->right = node; - break; - } - - temp = temp->right; + p = &temp->right; } else { /* node->key == temp->key */ sess_id = (ngx_ssl_sess_id_t *) node; sess_id_temp = (ngx_ssl_sess_id_t *) temp; - if (ngx_memn2cmp(sess_id->id, sess_id_temp->id, - (size_t) node->data, (size_t) temp->data) - < 0) - { - if (temp->left == sentinel) { - temp->left = node; - break; - } - - temp = temp->left; - - } else { - - if (temp->right == sentinel) { - temp->right = node; - break; - } + p = (ngx_memn2cmp(sess_id->id, sess_id_temp->id, + (size_t) node->data, (size_t) temp->data) + < 0) ? &temp->left : &temp->right; + } - temp = temp->right; - } + if (*p == sentinel) { + break; } + + temp = *p; } + *p = node; node->parent = temp; node->left = sentinel; node->right = sentinel; @@ -1695,6 +1979,134 @@ ngx_int_t +ngx_ssl_get_session_id(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s) +{ + int len; + u_char *p, *buf; + SSL_SESSION *sess; + + sess = SSL_get0_session(c->ssl->connection); + + len = i2d_SSL_SESSION(sess, NULL); + + buf = ngx_alloc(len, c->log); + if (buf == NULL) { + return NGX_ERROR; + } + + s->len = 2 * len; + s->data = ngx_pnalloc(pool, 2 * len); + if (s->data == NULL) { + ngx_free(buf); + return NGX_ERROR; + } + + p = buf; + i2d_SSL_SESSION(sess, &p); + + ngx_hex_dump(s->data, buf, len); + + ngx_free(buf); + + return NGX_OK; +} + + +ngx_int_t +ngx_ssl_get_raw_certificate(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s) +{ + size_t len; + BIO *bio; + X509 *cert; + + s->len = 0; + + cert = SSL_get_peer_certificate(c->ssl->connection); + if (cert == NULL) { + return NGX_OK; + } + + bio = BIO_new(BIO_s_mem()); + if (bio == NULL) { + ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "BIO_new() failed"); + X509_free(cert); + return NGX_ERROR; + } + + if (PEM_write_bio_X509(bio, cert) == 0) { + ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "PEM_write_bio_X509() failed"); + goto failed; + } + + len = BIO_pending(bio); + s->len = len; + + s->data = ngx_pnalloc(pool, len); + if (s->data == NULL) { + goto failed; + } + + BIO_read(bio, s->data, len); + + BIO_free(bio); + X509_free(cert); + + return NGX_OK; + +failed: + + BIO_free(bio); + X509_free(cert); + + return NGX_ERROR; +} + + +ngx_int_t +ngx_ssl_get_certificate(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s) +{ + u_char *p; + size_t len; + ngx_uint_t i; + ngx_str_t cert; + + if (ngx_ssl_get_raw_certificate(c, pool, &cert) != NGX_OK) { + return NGX_ERROR; + } + + if (cert.len == 0) { + s->len = 0; + return NGX_OK; + } + + len = cert.len - 1; + + for (i = 0; i < cert.len - 1; i++) { + if (cert.data[i] == LF) { + len++; + } + } + + s->len = len; + s->data = ngx_pnalloc(pool, len); + if (s->data == NULL) { + return NGX_ERROR; + } + + p = s->data; + + for (i = 0; i < cert.len - 1; i++) { + *p++ = cert.data[i]; + if (cert.data[i] == LF) { + *p++ = '\t'; + } + } + + return NGX_OK; +} + + +ngx_int_t ngx_ssl_get_subject_dn(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s) { char *p; @@ -1711,6 +2123,7 @@ name = X509_get_subject_name(cert); if (name == NULL) { + X509_free(cert); return NGX_ERROR; } @@ -1719,15 +2132,17 @@ for (len = 0; p[len]; len++) { /* void */ } s->len = len; - s->data = ngx_palloc(pool, len); + s->data = ngx_pnalloc(pool, len); if (s->data == NULL) { OPENSSL_free(p); + X509_free(cert); return NGX_ERROR; } ngx_memcpy(s->data, p, len); OPENSSL_free(p); + X509_free(cert); return NGX_OK; } @@ -1750,6 +2165,7 @@ name = X509_get_issuer_name(cert); if (name == NULL) { + X509_free(cert); return NGX_ERROR; } @@ -1758,15 +2174,17 @@ for (len = 0; p[len]; len++) { /* void */ } s->len = len; - s->data = ngx_palloc(pool, len); + s->data = ngx_pnalloc(pool, len); if (s->data == NULL) { OPENSSL_free(p); + X509_free(cert); return NGX_ERROR; } ngx_memcpy(s->data, p, len); OPENSSL_free(p); + X509_free(cert); return NGX_OK; } @@ -1788,6 +2206,7 @@ bio = BIO_new(BIO_s_mem()); if (bio == NULL) { + X509_free(cert); return NGX_ERROR; } @@ -1795,14 +2214,41 @@ len = BIO_pending(bio); s->len = len; - s->data = ngx_palloc(pool, len); + s->data = ngx_pnalloc(pool, len); if (s->data == NULL) { BIO_free(bio); + X509_free(cert); return NGX_ERROR; } BIO_read(bio, s->data, len); BIO_free(bio); + X509_free(cert); + + return NGX_OK; +} + + +ngx_int_t +ngx_ssl_get_client_verify(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s) +{ + X509 *cert; + + if (SSL_get_verify_result(c->ssl->connection) != X509_V_OK) { + ngx_str_set(s, "FAILED"); + return NGX_OK; + } + + cert = SSL_get_peer_certificate(c->ssl->connection); + + if (cert) { + ngx_str_set(s, "SUCCESS"); + + } else { + ngx_str_set(s, "NONE"); + } + + X509_free(cert); return NGX_OK; } @@ -1815,14 +2261,13 @@ oscf = ngx_pcalloc(cycle->pool, sizeof(ngx_openssl_conf_t)); if (oscf == NULL) { - return NGX_CONF_ERROR; + return NULL; } /* * set by ngx_pcalloc(): * - * oscf->engine.len = 0; - * oscf->engine.data = NULL; + * oscf->engine = 0; */ return oscf; @@ -1830,59 +2275,48 @@ static char * -ngx_openssl_init_conf(ngx_cycle_t *cycle, void *conf) +ngx_openssl_engine(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { -#if (NGX_SSL_ENGINE) ngx_openssl_conf_t *oscf = conf; - ENGINE *engine; + ENGINE *engine; + ngx_str_t *value; - if (oscf->engine.len == 0) { - return NGX_CONF_OK; + if (oscf->engine) { + return "is duplicate"; } - engine = ENGINE_by_id((const char *) oscf->engine.data); + oscf->engine = 1; + + value = cf->args->elts; + + engine = ENGINE_by_id((const char *) value[1].data); if (engine == NULL) { - ngx_ssl_error(NGX_LOG_WARN, cycle->log, 0, - "ENGINE_by_id(\"%V\") failed", &oscf->engine); + ngx_ssl_error(NGX_LOG_WARN, cf->log, 0, + "ENGINE_by_id(\"%V\") failed", &value[1]); return NGX_CONF_ERROR; } if (ENGINE_set_default(engine, ENGINE_METHOD_ALL) == 0) { - ngx_ssl_error(NGX_LOG_WARN, cycle->log, 0, + ngx_ssl_error(NGX_LOG_WARN, cf->log, 0, "ENGINE_set_default(\"%V\", ENGINE_METHOD_ALL) failed", - &oscf->engine); + &value[1]); + + ENGINE_free(engine); + return NGX_CONF_ERROR; } ENGINE_free(engine); -#endif - return NGX_CONF_OK; } -#if !(NGX_SSL_ENGINE) - -static char * -ngx_openssl_noengine(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) -{ - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "\"ssl_engine\" directive is available only in " - "OpenSSL 0.9.7 and higher,"); - - return NGX_CONF_ERROR; -} - -#endif - - static void ngx_openssl_exit(ngx_cycle_t *cycle) { -#if (NGX_SSL_ENGINE) + EVP_cleanup(); ENGINE_cleanup(); -#endif } diff -Nru nginx-0.5.33/src/event/ngx_event_openssl.h nginx-0.8.53/src/event/ngx_event_openssl.h --- nginx-0.5.33/src/event/ngx_event_openssl.h 2007-01-11 18:59:17.000000000 +0000 +++ nginx-0.8.53/src/event/ngx_event_openssl.h 2010-03-03 16:23:14.000000000 +0000 @@ -13,12 +13,9 @@ #include #include - -#if OPENSSL_VERSION_NUMBER >= 0x00907000 #include #include -#define NGX_SSL_ENGINE 1 -#endif +#include #define NGX_SSL_NAME "OpenSSL" @@ -45,17 +42,20 @@ ngx_event_handler_pt saved_write_handler; unsigned handshaked:1; + unsigned renegotiation:1; unsigned buffer:1; unsigned no_wait_shutdown:1; unsigned no_send_shutdown:1; } ngx_ssl_connection_t; -#define NGX_SSL_DFLT_BUILTIN_SCACHE -2 -#define NGX_SSL_NO_BUILTIN_SCACHE -3 +#define NGX_SSL_NO_SCACHE -2 +#define NGX_SSL_NONE_SCACHE -3 +#define NGX_SSL_NO_BUILTIN_SCACHE -4 +#define NGX_SSL_DFLT_BUILTIN_SCACHE -5 -#define NGX_SSL_MAX_SESSION_SIZE (4096) +#define NGX_SSL_MAX_SESSION_SIZE 4096 typedef struct ngx_ssl_sess_id_s ngx_ssl_sess_id_t; @@ -64,8 +64,7 @@ u_char *id; size_t len; u_char *session; - ngx_ssl_sess_id_t *prev; - ngx_ssl_sess_id_t *next; + ngx_queue_t queue; time_t expire; #if (NGX_PTR_SIZE == 8) void *stub; @@ -75,9 +74,9 @@ typedef struct { - ngx_rbtree_t *session_rbtree; - ngx_ssl_sess_id_t session_cache_head; - ngx_ssl_sess_id_t session_cache_tail; + ngx_rbtree_t session_rbtree; + ngx_rbtree_node_t sentinel; + ngx_queue_t expire_queue; } ngx_ssl_session_cache_t; @@ -99,12 +98,15 @@ ngx_str_t *cert, ngx_str_t *key); ngx_int_t ngx_ssl_client_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert, ngx_int_t depth); +ngx_int_t ngx_ssl_crl(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *crl); ngx_int_t ngx_ssl_generate_rsa512_key(ngx_ssl_t *ssl); +ngx_int_t ngx_ssl_dhparam(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file); ngx_int_t ngx_ssl_session_cache(ngx_ssl_t *ssl, ngx_str_t *sess_ctx, ssize_t builtin_session_cache, ngx_shm_zone_t *shm_zone, time_t timeout); ngx_int_t ngx_ssl_create_connection(ngx_ssl_t *ssl, ngx_connection_t *c, ngx_uint_t flags); +void ngx_ssl_remove_cached_session(SSL_CTX *ssl, ngx_ssl_session_t *sess); ngx_int_t ngx_ssl_set_session(ngx_connection_t *c, ngx_ssl_session_t *session); #define ngx_ssl_get_session(c) SSL_get1_session(c->ssl->connection) #define ngx_ssl_free_session SSL_SESSION_free @@ -118,12 +120,20 @@ ngx_str_t *s); ngx_int_t ngx_ssl_get_cipher_name(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s); +ngx_int_t ngx_ssl_get_session_id(ngx_connection_t *c, ngx_pool_t *pool, + ngx_str_t *s); +ngx_int_t ngx_ssl_get_raw_certificate(ngx_connection_t *c, ngx_pool_t *pool, + ngx_str_t *s); +ngx_int_t ngx_ssl_get_certificate(ngx_connection_t *c, ngx_pool_t *pool, + ngx_str_t *s); ngx_int_t ngx_ssl_get_subject_dn(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s); ngx_int_t ngx_ssl_get_issuer_dn(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s); ngx_int_t ngx_ssl_get_serial_number(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s); +ngx_int_t ngx_ssl_get_client_verify(ngx_connection_t *c, ngx_pool_t *pool, + ngx_str_t *s); ngx_int_t ngx_ssl_handshake(ngx_connection_t *c); @@ -132,6 +142,7 @@ ssize_t ngx_ssl_recv_chain(ngx_connection_t *c, ngx_chain_t *cl); ngx_chain_t *ngx_ssl_send_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit); +void ngx_ssl_free_buffer(ngx_connection_t *c); ngx_int_t ngx_ssl_shutdown(ngx_connection_t *c); void ngx_cdecl ngx_ssl_error(ngx_uint_t level, ngx_log_t *log, ngx_err_t err, char *fmt, ...); diff -Nru nginx-0.5.33/src/event/ngx_event_pipe.c nginx-0.8.53/src/event/ngx_event_pipe.c --- nginx-0.5.33/src/event/ngx_event_pipe.c 2007-11-07 13:54:40.000000000 +0000 +++ nginx-0.8.53/src/event/ngx_event_pipe.c 2009-08-28 08:12:35.000000000 +0000 @@ -24,15 +24,22 @@ ngx_event_pipe(ngx_event_pipe_t *p, ngx_int_t do_write) { u_int flags; + ngx_int_t rc; ngx_event_t *rev, *wev; for ( ;; ) { if (do_write) { p->log->action = "sending to client"; - if (ngx_event_pipe_write_to_downstream(p) == NGX_ABORT) { + rc = ngx_event_pipe_write_to_downstream(p); + + if (rc == NGX_ABORT) { return NGX_ABORT; } + + if (rc == NGX_BUSY) { + return NGX_OK; + } } p->read = 0; @@ -56,7 +63,7 @@ flags = (rev->eof || rev->error) ? NGX_CLOSE_EVENT : 0; - if (ngx_handle_read_event(rev, flags) == NGX_ERROR) { + if (ngx_handle_read_event(rev, flags) != NGX_OK) { return NGX_ABORT; } @@ -70,7 +77,7 @@ if (p->downstream->fd != -1 && p->downstream->data == p->output_ctx) { wev = p->downstream->write; - if (ngx_handle_write_event(wev, p->send_lowat) == NGX_ERROR) { + if (ngx_handle_write_event(wev, p->send_lowat) != NGX_OK) { return NGX_ABORT; } @@ -192,7 +199,7 @@ chain->buf = b; chain->next = NULL; - } else if (!p->cachable + } else if (!p->cacheable && p->downstream->data == p->output_ctx && p->downstream->write->ready && !p->downstream->write->delayed) @@ -209,7 +216,7 @@ break; - } else if (p->cachable + } else if (p->cacheable || p->temp_file->offset < p->max_temp_file_size) { @@ -397,7 +404,7 @@ p->free_raw_bufs = p->free_raw_bufs->next; - if (p->free_bufs) { + if (p->free_bufs && p->buf_to_file == NULL) { for (cl = p->free_raw_bufs; cl; cl = cl->next) { if (cl->buf->shadow == NULL) { ngx_pfree(p->pool, cl->buf->start); @@ -406,7 +413,7 @@ } } - if (p->cachable && p->in) { + if (p->cacheable && p->in) { if (ngx_event_pipe_write_chain_to_temp_file(p) == NGX_ABORT) { return NGX_ABORT; } @@ -421,8 +428,9 @@ { u_char *prev; size_t bsize; - ngx_uint_t flush, prev_last_shadow; - ngx_chain_t *out, **ll, *cl; + ngx_int_t rc; + ngx_uint_t flush, flushed, prev_last_shadow; + ngx_chain_t *out, **ll, *cl, file; ngx_connection_t *downstream; downstream = p->downstream; @@ -430,6 +438,8 @@ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0, "pipe write downstream: %d", downstream->write->ready); + flushed = 0; + for ( ;; ) { if (p->downstream_error) { return ngx_event_pipe_drain_chains(p); @@ -451,7 +461,9 @@ cl->buf->recycled = 0; } - if (p->output_filter(p->output_ctx, p->out) == NGX_ERROR) { + rc = p->output_filter(p->output_ctx, p->out); + + if (rc == NGX_ERROR) { p->downstream_error = 1; return ngx_event_pipe_drain_chains(p); } @@ -467,12 +479,9 @@ cl->buf->recycled = 0; } - if (p->output_filter(p->output_ctx, p->in) == NGX_ERROR) { - - if (downstream->destroyed) { - return NGX_ABORT; - } + rc = p->output_filter(p->output_ctx, p->in); + if (rc == NGX_ERROR) { p->downstream_error = 1; return ngx_event_pipe_drain_chains(p); } @@ -480,6 +489,18 @@ p->in = NULL; } + if (p->cacheable && p->buf_to_file) { + + file.buf = p->buf_to_file; + file.next = NULL; + + if (ngx_write_chain_to_temp_file(p->temp_file, &file) + == NGX_ERROR) + { + return NGX_ABORT; + } + } + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, p->log, 0, "pipe write downstream done"); @@ -542,7 +563,7 @@ ngx_event_pipe_free_shadow_raw_buf(&p->free_raw_bufs, cl->buf); - } else if (!p->cachable && p->in) { + } else if (!p->cacheable && p->in) { cl = p->in; ngx_log_debug3(NGX_LOG_DEBUG_EVENT, p->log, 0, @@ -598,11 +619,21 @@ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, p->log, 0, "pipe write: out:%p, f:%d", out, flush); - if (out == NULL && !flush) { - break; + if (out == NULL) { + + if (!flush) { + break; + } + + /* a workaround for AIO */ + if (flushed++ > 10) { + return NGX_BUSY; + } } - if (p->output_filter(p->output_ctx, out) == NGX_ERROR) { + rc = p->output_filter(p->output_ctx, out); + + if (rc == NGX_ERROR) { p->downstream_error = 1; return ngx_event_pipe_drain_chains(p); } @@ -612,7 +643,7 @@ for (cl = p->free; cl; cl = cl->next) { if (cl->buf->temp_file) { - if (p->cachable || !p->cyclic_temp_file) { + if (p->cacheable || !p->cyclic_temp_file) { continue; } @@ -659,7 +690,7 @@ out = p->in; } - if (!p->cachable) { + if (!p->cacheable) { size = 0; cl = out; @@ -866,7 +897,7 @@ ll = free; - for (cl = *free ; cl; cl = cl->next) { + for (cl = *free; cl; cl = cl->next) { if (cl->buf == s) { *ll = cl->next; break; diff -Nru nginx-0.5.33/src/event/ngx_event_pipe.h nginx-0.8.53/src/event/ngx_event_pipe.h --- nginx-0.5.33/src/event/ngx_event_pipe.h 2007-11-07 13:54:40.000000000 +0000 +++ nginx-0.8.53/src/event/ngx_event_pipe.h 2007-10-23 14:09:12.000000000 +0000 @@ -47,7 +47,7 @@ void *output_ctx; unsigned read:1; - unsigned cachable:1; + unsigned cacheable:1; unsigned single_buf:1; unsigned free_bufs:1; unsigned upstream_done:1; diff -Nru nginx-0.5.33/src/event/ngx_event_timer.c nginx-0.8.53/src/event/ngx_event_timer.c --- nginx-0.5.33/src/event/ngx_event_timer.c 2007-01-12 19:26:38.000000000 +0000 +++ nginx-0.8.53/src/event/ngx_event_timer.c 2007-12-17 08:52:00.000000000 +0000 @@ -26,9 +26,8 @@ ngx_int_t ngx_event_timer_init(ngx_log_t *log) { - ngx_event_timer_rbtree.root = &ngx_event_timer_sentinel; - ngx_event_timer_rbtree.sentinel = &ngx_event_timer_sentinel; - ngx_event_timer_rbtree.insert = ngx_rbtree_insert_timer_value; + ngx_rbtree_init(&ngx_event_timer_rbtree, &ngx_event_timer_sentinel, + ngx_rbtree_insert_timer_value); #if (NGX_THREADS) diff -Nru nginx-0.5.33/src/event/ngx_event_timer.h nginx-0.8.53/src/event/ngx_event_timer.h --- nginx-0.5.33/src/event/ngx_event_timer.h 2006-08-30 10:39:17.000000000 +0000 +++ nginx-0.8.53/src/event/ngx_event_timer.h 2007-12-29 16:55:31.000000000 +0000 @@ -65,9 +65,9 @@ if (ev->timer_set) { /* - * Use the previous timer value if a difference between them is less - * then NGX_TIMER_LAZY_DELAY milliseconds. It allows to minimize - * the rbtree operations for the fast connections. + * Use a previous timer value if difference between it and a new + * value is less than NGX_TIMER_LAZY_DELAY milliseconds: this allows + * to minimize the rbtree operations for fast connections. */ diff = (ngx_msec_int_t) (key - ev->timer.key); diff -Nru nginx-0.5.33/src/http/modules/ngx_http_access_module.c nginx-0.8.53/src/http/modules/ngx_http_access_module.c --- nginx-0.5.33/src/http/modules/ngx_http_access_module.c 2007-09-22 19:02:39.000000000 +0000 +++ nginx-0.8.53/src/http/modules/ngx_http_access_module.c 2010-07-08 16:17:11.000000000 +0000 @@ -9,21 +9,38 @@ #include -/* AF_INET only */ - typedef struct { - in_addr_t mask; - in_addr_t addr; - ngx_uint_t deny; /* unsigned deny:1; */ + 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 { - ngx_array_t *rules; /* array of ngx_http_access_rule_t */ + struct in6_addr addr; + struct in6_addr mask; + ngx_uint_t deny; /* unsigned deny:1; */ +} ngx_http_access_rule6_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 } 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 +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); @@ -89,43 +106,127 @@ static ngx_int_t ngx_http_access_handler(ngx_http_request_t *r) { - ngx_uint_t i; struct sockaddr_in *sin; - ngx_http_access_rule_t *rule; - ngx_http_core_loc_conf_t *clcf; ngx_http_access_loc_conf_t *alcf; alcf = ngx_http_get_module_loc_conf(r, ngx_http_access_module); - if (alcf->rules == NULL) { - return NGX_OK; +#if (NGX_HAVE_INET6) + + if (alcf->rules6 && r->connection->sockaddr->sa_family == AF_INET6) { + u_char *p; + in_addr_t addr; + struct sockaddr_in6 *sin6; + + sin6 = (struct sockaddr_in6 *) r->connection->sockaddr; + p = sin6->sin6_addr.s6_addr; + + if (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)); + } + + return ngx_http_access_inet6(r, alcf, p); } - /* AF_INET only */ +#endif - sin = (struct sockaddr_in *) r->connection->sockaddr; + if (alcf->rules && r->connection->sockaddr->sa_family == AF_INET) { + sin = (struct sockaddr_in *) r->connection->sockaddr; + return ngx_http_access_inet(r, alcf, sin->sin_addr.s_addr); + } + + 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", - sin->sin_addr.s_addr, rule[i].mask, rule[i].addr); + addr, rule[i].mask, rule[i].addr); - if ((sin->sin_addr.s_addr & rule[i].mask) == rule[i].addr) { - if (rule[i].deny) { - clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); - - if (!clcf->satisfy_any) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "access forbidden by rule"); - } + if ((addr & rule[i].mask) == rule[i].addr) { + return ngx_http_access_found(r, rule[i].deny); + } + } - return NGX_HTTP_FORBIDDEN; + 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 - return NGX_OK; + +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; @@ -137,59 +238,87 @@ { ngx_http_access_loc_conf_t *alcf = conf; - ngx_int_t rc; - ngx_str_t *value; - ngx_inet_cidr_t in_cidr; - ngx_http_access_rule_t *rule; + 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 (alcf->rules == NULL) { - alcf->rules = ngx_array_create(cf->pool, 4, - sizeof(ngx_http_access_rule_t)); - if (alcf->rules == NULL) { + ngx_memzero(&cidr, sizeof(ngx_cidr_t)); + + value = cf->args->elts; + + all = (value[1].len == 3 && ngx_strcmp(value[1].data, "all") == 0); + + if (!all) { + + 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; } - } - rule = ngx_array_push(alcf->rules); - if (rule == NULL) { - 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]); + } } - value = cf->args->elts; + switch (cidr.family) { - rule->deny = (value[0].data[0] == 'd') ? 1 : 0; +#if (NGX_HAVE_INET6) + case AF_INET6: + case 0: /* 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; + } + } - if (value[1].len == 3 && ngx_strcmp(value[1].data, "all") == 0) { - rule->mask = 0; - rule->addr = 0; + rule6 = ngx_array_push(alcf->rules6); + if (rule6 == NULL) { + return NGX_CONF_ERROR; + } - return NGX_CONF_OK; - } + rule6->mask = cidr.u.in6.mask; + rule6->addr = cidr.u.in6.addr; + rule6->deny = (value[0].data[0] == 'd') ? 1 : 0; - rule->addr = inet_addr((char *) value[1].data); + if (!all) { + break; + } - if (rule->addr != INADDR_NONE) { - rule->mask = 0xffffffff; + /* "all" passes through */ +#endif - return NGX_CONF_OK; - } + default: /* AF_INET */ - rc = ngx_ptocidr(&value[1], &in_cidr); + 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; + } + } - if (rc == NGX_ERROR) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameter \"%V\"", - &value[1]); - return NGX_CONF_ERROR; - } + rule = ngx_array_push(alcf->rules); + if (rule == NULL) { + 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]); + rule->mask = cidr.u.in.mask; + rule->addr = cidr.u.in.addr; + rule->deny = (value[0].data[0] == 'd') ? 1 : 0; } - rule->mask = in_cidr.mask; - rule->addr = in_cidr.addr; - return NGX_CONF_OK; } @@ -201,7 +330,7 @@ conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_access_loc_conf_t)); if (conf == NULL) { - return NGX_CONF_ERROR; + return NULL; } return conf; @@ -218,6 +347,12 @@ conf->rules = prev->rules; } +#if (NGX_HAVE_INET6) + if (conf->rules6 == NULL) { + conf->rules6 = prev->rules6; + } +#endif + return NGX_CONF_OK; } diff -Nru nginx-0.5.33/src/http/modules/ngx_http_addition_filter_module.c nginx-0.8.53/src/http/modules/ngx_http_addition_filter_module.c --- nginx-0.5.33/src/http/modules/ngx_http_addition_filter_module.c 2007-02-14 18:51:19.000000000 +0000 +++ nginx-0.8.53/src/http/modules/ngx_http_addition_filter_module.c 2009-11-30 13:15:10.000000000 +0000 @@ -10,13 +10,16 @@ typedef struct { - ngx_str_t before_body; - ngx_str_t after_body; + 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_uint_t before_body_sent; } ngx_http_addition_ctx_t; @@ -42,6 +45,13 @@ 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 }; @@ -87,10 +97,7 @@ ngx_http_addition_ctx_t *ctx; ngx_http_addition_conf_t *conf; - if (r->headers_out.status != NGX_HTTP_OK - || r != r->main - || r->headers_out.content_type.data == NULL) - { + if (r->headers_out.status != NGX_HTTP_OK || r != r->main) { return ngx_http_next_header_filter(r); } @@ -100,10 +107,7 @@ return ngx_http_next_header_filter(r); } - if (ngx_strncasecmp(r->headers_out.content_type.data, - (u_char *) "text/html", sizeof("text/html") - 1) - != 0) - { + if (ngx_http_test_content_type(r, &conf->types) == NULL) { return ngx_http_next_header_filter(r); } @@ -147,10 +151,10 @@ ctx->before_body_sent = 1; if (conf->before_body.len) { - rc = ngx_http_subrequest(r, &conf->before_body, NULL, &sr, NULL, 0); - - if (rc == NGX_ERROR || rc == NGX_DONE) { - return rc; + if (ngx_http_subrequest(r, &conf->before_body, NULL, &sr, NULL, 0) + != NGX_OK) + { + return NGX_ERROR; } } } @@ -176,10 +180,10 @@ return rc; } - rc = ngx_http_subrequest(r, &conf->after_body, NULL, &sr, NULL, 0); - - if (rc == NGX_ERROR || rc == NGX_DONE) { - 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); @@ -208,16 +212,16 @@ conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_addition_conf_t)); if (conf == NULL) { - return NGX_CONF_ERROR; + return NULL; } /* * set by ngx_pcalloc(): * - * conf->before_body.len = 0; - * conf->before_body.date = NULL; - * conf->after_body.len = 0; - * conf->after_body.date = NULL; + * conf->before_body = { 0, NULL }; + * conf->after_body = { 0, NULL }; + * conf->types = { NULL }; + * conf->types_keys = NULL; */ return conf; @@ -233,5 +237,13 @@ 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; } diff -Nru nginx-0.5.33/src/http/modules/ngx_http_auth_basic_module.c nginx-0.8.53/src/http/modules/ngx_http_auth_basic_module.c --- nginx-0.5.33/src/http/modules/ngx_http_auth_basic_module.c 2007-01-18 20:15:09.000000000 +0000 +++ nginx-0.8.53/src/http/modules/ngx_http_auth_basic_module.c 2010-05-14 09:56:37.000000000 +0000 @@ -13,13 +13,13 @@ typedef struct { - ngx_str_t passwd; + ngx_str_t passwd; } ngx_http_auth_basic_ctx_t; typedef struct { - ngx_str_t realm; - ngx_str_t user_file; + ngx_str_t realm; + ngx_http_complex_value_t user_file; } ngx_http_auth_basic_loc_conf_t; @@ -34,6 +34,8 @@ void *parent, void *child); static ngx_int_t ngx_http_auth_basic_init(ngx_conf_t *cf); static char *ngx_http_auth_basic(ngx_conf_t *cf, void *post, void *data); +static char *ngx_http_auth_basic_user_file(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); static ngx_conf_post_handler_pt ngx_http_auth_basic_p = ngx_http_auth_basic; @@ -51,7 +53,7 @@ { 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_conf_set_str_slot, + ngx_http_auth_basic_user_file, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_auth_basic_loc_conf_t, user_file), NULL }, @@ -98,8 +100,9 @@ ssize_t n; ngx_fd_t fd; ngx_int_t rc; - ngx_str_t pwd; - ngx_uint_t i, login, left, passwd; + ngx_err_t err; + ngx_str_t pwd, user_file; + ngx_uint_t i, level, login, left, passwd; ngx_file_t file; ngx_http_auth_basic_ctx_t *ctx; ngx_http_auth_basic_loc_conf_t *alcf; @@ -112,8 +115,8 @@ alcf = ngx_http_get_module_loc_conf(r, ngx_http_auth_basic_module); - if (alcf->realm.len == 0 || alcf->user_file.len == 0) { - return NGX_OK; + if (alcf->realm.len == 0 || alcf->user_file.value.len == 0) { + return NGX_DECLINED; } ctx = ngx_http_get_module_ctx(r, ngx_http_auth_basic_module); @@ -126,6 +129,10 @@ rc = ngx_http_auth_basic_user(r); if (rc == NGX_DECLINED) { + + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "no user/password was provided for basic authentication"); + return ngx_http_auth_basic_set_realm(r, &alcf->realm); } @@ -133,18 +140,34 @@ return NGX_HTTP_INTERNAL_SERVER_ERROR; } - fd = ngx_open_file(alcf->user_file.data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0); + 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) { - ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno, - ngx_open_file_n " \"%s\" failed", alcf->user_file.data); - return NGX_HTTP_INTERNAL_SERVER_ERROR; + 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 = alcf->user_file; + file.name = user_file; file.log = r->connection->log; state = sw_login; @@ -172,9 +195,16 @@ switch (state) { case sw_login: - if (login == 0 && buf[i] == '#') { - state = sw_skip; - break; + 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]) { @@ -232,7 +262,7 @@ if (state == sw_passwd) { pwd.len = i - passwd; - pwd.data = ngx_palloc(r->pool, pwd.len + 1); + pwd.data = ngx_pnalloc(r->pool, pwd.len + 1); if (pwd.data == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } @@ -242,6 +272,10 @@ return ngx_http_auth_basic_crypt_handler(r, NULL, &pwd, &alcf->realm); } + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "user \"%V\" was not found in \"%V\"", + &r->headers_in.user, &user_file); + return ngx_http_auth_basic_set_realm(r, &alcf->realm); } @@ -257,8 +291,8 @@ &encrypted); ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "rc: %d user: \"%V\" salt: \"%s\"", - rc, &r->headers_in.user, passwd->data); + "rc: %d user: \"%V\" salt: \"%s\"", + rc, &r->headers_in.user, passwd->data); if (rc == NGX_OK) { if (ngx_strcmp(encrypted, passwd->data) == 0) { @@ -268,6 +302,10 @@ 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); } @@ -310,8 +348,7 @@ } r->headers_out.www_authenticate->hash = 1; - r->headers_out.www_authenticate->key.len = sizeof("WWW-Authenticate") - 1; - r->headers_out.www_authenticate->key.data = (u_char *) "WWW-Authenticate"; + ngx_str_set(&r->headers_out.www_authenticate->key, "WWW-Authenticate"); r->headers_out.www_authenticate->value = *realm; return NGX_HTTP_UNAUTHORIZED; @@ -334,7 +371,7 @@ conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_auth_basic_loc_conf_t)); if (conf == NULL) { - return NGX_CONF_ERROR; + return NULL; } return conf; @@ -351,12 +388,7 @@ conf->realm = prev->realm; } - if (conf->user_file.data) { - if (ngx_conf_full_name(cf->cycle, &conf->user_file) != NGX_OK) { - return NGX_CONF_ERROR; - } - - } else { + if (conf->user_file.value.len == 0) { conf->user_file = prev->user_file; } @@ -392,15 +424,13 @@ u_char *basic, *p; if (ngx_strcmp(realm->data, "off") == 0) { - realm->len = 0; - realm->data = (u_char *) ""; - + ngx_str_set(realm, ""); return NGX_CONF_OK; } len = sizeof("Basic realm=\"") - 1 + realm->len + 1; - basic = ngx_palloc(cf->pool, len); + basic = ngx_pnalloc(cf->pool, len); if (basic == NULL) { return NGX_CONF_ERROR; } @@ -414,3 +444,33 @@ return NGX_CONF_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.len) { + 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; +} diff -Nru nginx-0.5.33/src/http/modules/ngx_http_autoindex_module.c nginx-0.8.53/src/http/modules/ngx_http_autoindex_module.c --- nginx-0.5.33/src/http/modules/ngx_http_autoindex_module.c 2007-09-23 19:30:51.000000000 +0000 +++ nginx-0.8.53/src/http/modules/ngx_http_autoindex_module.c 2010-05-24 12:35:10.000000000 +0000 @@ -25,8 +25,11 @@ typedef struct { ngx_str_t name; size_t utf_len; - ngx_uint_t escape; - ngx_uint_t dir; + size_t escape; + + unsigned dir:1; + unsigned colon:1; + time_t mtime; off_t size; } ngx_http_autoindex_entry_t; @@ -135,14 +138,14 @@ { u_char *last, *filename, scale; off_t length; - size_t len, copy, allocated, root; + size_t len, utf_len, allocated, root; ngx_tm_t tm; ngx_err_t err; ngx_buf_t *b; ngx_int_t rc, size; ngx_str_t path; ngx_dir_t dir; - ngx_uint_t i, level; + ngx_uint_t i, level, utf8; ngx_pool_t *pool; ngx_time_t *tp; ngx_chain_t out; @@ -157,11 +160,6 @@ return NGX_DECLINED; } - /* TODO: Win32 */ - if (r->zero_in_uri) { - return NGX_DECLINED; - } - if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) { return NGX_DECLINED; } @@ -181,7 +179,10 @@ } allocated = path.len; - path.len = last - path.data - 1; + 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, @@ -230,8 +231,7 @@ r->headers_out.status = NGX_HTTP_OK; r->headers_out.content_type_len = sizeof("text/html") - 1; - r->headers_out.content_type.len = sizeof("text/html") - 1; - r->headers_out.content_type.data = (u_char *) "text/html"; + ngx_str_set(&r->headers_out.content_type, "text/html"); rc = ngx_http_send_header(r); @@ -247,6 +247,16 @@ filename = path.data; filename[path.len] = '/'; + 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; + } + for ( ;; ) { ngx_set_errno(0); @@ -279,7 +289,7 @@ allocated = path.len + 1 + len + 1 + NGX_HTTP_AUTOINDEX_PREALLOCATE; - filename = ngx_palloc(pool, allocated); + filename = ngx_pnalloc(pool, allocated); if (filename == NULL) { return ngx_http_autoindex_error(r, &dir, &path); } @@ -296,6 +306,11 @@ if (err != NGX_ENOENT) { 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); } @@ -315,7 +330,7 @@ entry->name.len = len; - entry->name.data = ngx_palloc(pool, len + 1); + entry->name.data = ngx_pnalloc(pool, len + 1); if (entry->name.data == NULL) { return ngx_http_autoindex_error(r, &dir, &path); } @@ -325,12 +340,14 @@ entry->escape = 2 * ngx_escape_uri(NULL, ngx_de_name(&dir), len, NGX_ESCAPE_HTML); - if (r->utf8) { - entry->utf_len = ngx_utf_length(entry->name.data, entry->name.len); + if (utf8) { + entry->utf_len = ngx_utf8_length(entry->name.data, entry->name.len); } else { entry->utf_len = len; } + entry->colon = (ngx_strchr(entry->name.data, ':') != NULL); + entry->dir = ngx_de_is_dir(&dir); entry->mtime = ngx_de_mtime(&dir); entry->size = ngx_de_size(&dir); @@ -356,7 +373,7 @@ + entry[i].name.len + entry[i].escape + 1 /* 1 is for "/" */ + sizeof("\">") - 1 - + entry[i].name.len - entry[i].utf_len + + entry[i].name.len - entry[i].utf_len + entry[i].colon * 2 + NGX_HTTP_AUTOINDEX_NAME_LEN + sizeof(">") - 2 + sizeof("") - 1 + sizeof(" 28-Sep-1970 12:00 ") - 1 @@ -389,6 +406,11 @@ for (i = 0; i < entries.nelts; i++) { b->last = ngx_cpymem(b->last, "last++ = '.'; + *b->last++ = '/'; + } + if (entry[i].escape) { ngx_escape_uri(b->last, entry[i].name.data, entry[i].name.len, NGX_ESCAPE_HTML); @@ -409,15 +431,16 @@ len = entry[i].utf_len; - if (entry[i].name.len - len) { + if (entry[i].name.len != len) { if (len > NGX_HTTP_AUTOINDEX_NAME_LEN) { - copy = NGX_HTTP_AUTOINDEX_NAME_LEN - 3 + 1; + utf_len = NGX_HTTP_AUTOINDEX_NAME_LEN - 3 + 1; } else { - copy = NGX_HTTP_AUTOINDEX_NAME_LEN + 1; + utf_len = NGX_HTTP_AUTOINDEX_NAME_LEN + 1; } - b->last = ngx_utf_cpystrn(b->last, entry[i].name.data, copy); + b->last = ngx_utf8_cpystrn(b->last, entry[i].name.data, + utf_len, entry[i].name.len + 1); last = b->last; } else { @@ -604,7 +627,7 @@ conf = ngx_palloc(cf->pool, sizeof(ngx_http_autoindex_loc_conf_t)); if (conf == NULL) { - return NGX_CONF_ERROR; + return NULL; } conf->enable = NGX_CONF_UNSET; diff -Nru nginx-0.5.33/src/http/modules/ngx_http_browser_module.c nginx-0.8.53/src/http/modules/ngx_http_browser_module.c --- nginx-0.5.33/src/http/modules/ngx_http_browser_module.c 2006-10-09 15:38:59.000000000 +0000 +++ nginx-0.8.53/src/http/modules/ngx_http_browser_module.c 2009-06-02 16:09:44.000000000 +0000 @@ -318,6 +318,10 @@ 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; } @@ -339,6 +343,8 @@ if (version >= modern[i].version) { return NGX_HTTP_MODERN_BROWSER; } + + return NGX_HTTP_ANCIENT_BROWSER; } if (!cf->modern_unlisted_browsers) { @@ -397,7 +403,7 @@ for (var = ngx_http_browsers; var->name.len; var++) { - v = ngx_http_add_variable(cf, &var->name, NGX_HTTP_VAR_CHANGABLE); + v = ngx_http_add_variable(cf, &var->name, NGX_HTTP_VAR_CHANGEABLE); if (v == NULL) { return NGX_ERROR; } @@ -417,7 +423,7 @@ conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_browser_conf_t)); if (conf == NULL) { - return NGX_CONF_ERROR; + return NULL; } /* @@ -673,7 +679,7 @@ bcf->modern_browser_value->len = value[1].len; bcf->modern_browser_value->valid = 1; - bcf->modern_browser_value->no_cachable = 0; + bcf->modern_browser_value->no_cacheable = 0; bcf->modern_browser_value->not_found = 0; bcf->modern_browser_value->data = value[1].data; @@ -698,7 +704,7 @@ bcf->ancient_browser_value->len = value[1].len; bcf->ancient_browser_value->valid = 1; - bcf->ancient_browser_value->no_cachable = 0; + bcf->ancient_browser_value->no_cacheable = 0; bcf->ancient_browser_value->not_found = 0; bcf->ancient_browser_value->data = value[1].data; diff -Nru nginx-0.5.33/src/http/modules/ngx_http_charset_filter_module.c nginx-0.8.53/src/http/modules/ngx_http_charset_filter_module.c --- nginx-0.5.33/src/http/modules/ngx_http_charset_filter_module.c 2007-07-22 08:47:45.000000000 +0000 +++ nginx-0.8.53/src/http/modules/ngx_http_charset_filter_module.c 2009-11-30 13:15:10.000000000 +0000 @@ -9,8 +9,9 @@ #include -#define NGX_HTTP_NO_CHARSET -2 -#define NGX_HTTP_CHARSET_VAR 0x10000 +#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 @@ -52,12 +53,16 @@ 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; @@ -79,9 +84,16 @@ } ngx_http_charset_conf_ctx_t; -static ngx_int_t ngx_http_charset_get_charset(ngx_http_charset_t *charsets, - ngx_uint_t n, ngx_str_t *charset); -static ngx_int_t ngx_http_charset_set_charset(ngx_http_request_t *r, +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, @@ -110,6 +122,17 @@ static ngx_int_t ngx_http_charset_postconfiguration(ngx_conf_t *cf); +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/x-javascript"), + ngx_string("application/rss+xml"), + ngx_null_string +}; + + static ngx_command_t ngx_http_charset_filter_commands[] = { { ngx_string("charset"), @@ -136,6 +159,13 @@ 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, @@ -185,208 +215,265 @@ static ngx_int_t ngx_http_charset_header_filter(ngx_http_request_t *r) { - u_char *ct; ngx_int_t charset, source_charset; - ngx_str_t *mc, *from, *to, s; - ngx_uint_t n; + ngx_str_t dst, src; ngx_http_charset_t *charsets; - ngx_http_charset_ctx_t *ctx; - ngx_http_variable_value_t *vv; - ngx_http_charset_loc_conf_t *lcf, *mlcf; ngx_http_charset_main_conf_t *mcf; - mcf = ngx_http_get_module_main_conf(r, ngx_http_charset_filter_module); + if (r == r->main) { + charset = ngx_http_destination_charset(r, &dst); - charsets = mcf->charsets.elts; - n = mcf->charsets.nelts; + } else { + charset = ngx_http_main_request_charset(r, &dst); + } - /* destination charset */ + if (charset == NGX_ERROR) { + return NGX_ERROR; + } - if (r == r->main) { + if (charset == NGX_DECLINED) { + return ngx_http_next_header_filter(r); + } - if (r->headers_out.content_type.len == 0) { - return ngx_http_next_header_filter(r); - } + /* charset: charset index or NGX_HTTP_NO_CHARSET */ - if (r->headers_out.override_charset - && r->headers_out.override_charset->len) - { - charset = ngx_http_charset_get_charset(charsets, n, - r->headers_out.override_charset); + source_charset = ngx_http_source_charset(r, &src); - if (charset == NGX_HTTP_NO_CHARSET) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "unknown charset \"%V\" to override", - r->headers_out.override_charset); + if (source_charset == NGX_ERROR) { + return NGX_ERROR; + } - return ngx_http_next_header_filter(r); - } + /* + * source_charset: charset index, NGX_HTTP_NO_CHARSET, + * or NGX_HTTP_CHARSET_OFF + */ - } else { - mlcf = ngx_http_get_module_loc_conf(r, - ngx_http_charset_filter_module); - charset = mlcf->charset; + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "charset: \"%V\" > \"%V\"", &src, &dst); - if (charset == NGX_HTTP_NO_CHARSET) { - return ngx_http_next_header_filter(r); - } + if (source_charset == NGX_HTTP_CHARSET_OFF) { + ngx_http_set_charset(r, &dst); - if (r->headers_out.charset.len) { - if (mlcf->override_charset == 0) { - return ngx_http_next_header_filter(r); - } + return ngx_http_next_header_filter(r); + } - } else { - ct = r->headers_out.content_type.data; + 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; + } - if (ngx_strncasecmp(ct, (u_char *) "text/", 5) != 0 - && ngx_strncasecmp(ct, - (u_char *) "application/x-javascript", 24) - != 0) - { - return ngx_http_next_header_filter(r); - } - } + ngx_http_set_charset(r, &dst); - if (charset >= NGX_HTTP_CHARSET_VAR) { - vv = ngx_http_get_indexed_variable(r, - charset - NGX_HTTP_CHARSET_VAR); + return ngx_http_next_header_filter(r); + } - if (vv == NULL || vv->not_found) { - return NGX_ERROR; - } + mcf = ngx_http_get_module_main_conf(r, ngx_http_charset_filter_module); + charsets = mcf->charsets.elts; + + if (source_charset != charset + && (charsets[source_charset].tables == NULL + || charsets[source_charset].tables[charset] == NULL)) + { + goto no_charset_map; + } - s.len = vv->len; - s.data = vv->data; + r->headers_out.content_type.len = r->headers_out.content_type_len; - charset = ngx_http_charset_get_charset(charsets, n, &s); - } - } + ngx_http_set_charset(r, &dst); - } else { - ctx = ngx_http_get_module_ctx(r->main, ngx_http_charset_filter_module); + if (source_charset != charset) { + return ngx_http_charset_ctx(r, charsets, charset, source_charset); + } - if (ctx == NULL) { + return ngx_http_next_header_filter(r); - mc = &r->main->headers_out.charset; +no_charset_map: - if (mc->len == 0) { - return ngx_http_next_header_filter(r); - } + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "no \"charset_map\" between the charsets \"%V\" and \"%V\"", + &src, &dst); - ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_charset_ctx_t)); - if (ctx == NULL) { - return NGX_ERROR; - } + return ngx_http_next_header_filter(r); +} - ngx_http_set_ctx(r->main, ctx, ngx_http_charset_filter_module); - charset = ngx_http_charset_get_charset(charsets, n, mc); +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; - ctx->charset = charset; + if (!r->ignore_content_encoding + && r->headers_out.content_encoding + && r->headers_out.content_encoding->value.len) + { + return NGX_DECLINED; + } - } else { - charset = ctx->charset; - } + if (r->headers_out.content_type.len == 0) { + return NGX_DECLINED; } - /* source charset */ + if (r->headers_out.override_charset + && r->headers_out.override_charset->len) + { + *name = *r->headers_out.override_charset; - if (r->headers_out.charset.len == 0) { - lcf = ngx_http_get_module_loc_conf(r, ngx_http_charset_filter_module); + charset = ngx_http_get_charset(r, name); - source_charset = lcf->source_charset; + if (charset != NGX_HTTP_NO_CHARSET) { + return charset; + } - if (source_charset >= NGX_HTTP_CHARSET_VAR) { - vv = ngx_http_get_indexed_variable(r, - source_charset - NGX_HTTP_CHARSET_VAR); + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "unknown charset \"%V\" to override", name); - if (vv == NULL || vv->not_found) { - return NGX_ERROR; - } + return NGX_DECLINED; + } - s.len = vv->len; - s.data = vv->data; + mlcf = ngx_http_get_module_loc_conf(r, ngx_http_charset_filter_module); + charset = mlcf->charset; - source_charset = ngx_http_charset_get_charset(charsets, n, &s); - } + if (charset == NGX_HTTP_CHARSET_OFF) { + return NGX_DECLINED; + } - if (charset != NGX_HTTP_NO_CHARSET) { - return ngx_http_charset_set_charset(r, mcf->charsets.elts, charset, - source_charset); + if (r->headers_out.charset.len) { + if (mlcf->override_charset == 0) { + return NGX_DECLINED; } - if (source_charset == NGX_CONF_UNSET) { - return ngx_http_next_header_filter(r); + } 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; + } - from = &charsets[source_charset].name; - to = &r->main->headers_out.charset; + vv = ngx_http_get_indexed_variable(r, charset - NGX_HTTP_CHARSET_VAR); - goto no_charset_map; + if (vv == NULL || vv->not_found) { + return NGX_ERROR; } - source_charset = ngx_http_charset_get_charset(charsets, n, - &r->headers_out.charset); + name->len = vv->len; + name->data = vv->data; - if (charset == NGX_HTTP_NO_CHARSET - || source_charset == NGX_HTTP_NO_CHARSET) - { - if (charset != source_charset - || ngx_strcasecmp(r->main->headers_out.charset.data, - r->headers_out.charset.data) - != 0) - { - from = &r->headers_out.charset; - to = (charset == NGX_HTTP_NO_CHARSET) ? - &r->main->headers_out.charset: - &charsets[charset].name; + return ngx_http_get_charset(r, name); +} - goto no_charset_map; - } - return ngx_http_next_header_filter(r); +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; } - if (source_charset != charset - && (charsets[source_charset].tables == NULL - || charsets[source_charset].tables[charset] == NULL)) - { - from = &charsets[source_charset].name; - to = &charsets[charset].name; + main_charset = &r->main->headers_out.charset; - goto no_charset_map; + if (main_charset->len == 0) { + return NGX_DECLINED; } - r->headers_out.content_type.len = r->headers_out.content_type_len; + ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_charset_ctx_t)); + if (ctx == NULL) { + return NGX_ERROR; + } - return ngx_http_charset_set_charset(r, mcf->charsets.elts, charset, - source_charset); + ngx_http_set_ctx(r->main, ctx, ngx_http_charset_filter_module); -no_charset_map: + charset = ngx_http_get_charset(r, main_charset); - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "no \"charset_map\" between the charsets " - "\"%V\" and \"%V\"", from, to); + ctx->charset = charset; + ctx->charset_name = *main_charset; + *src = *main_charset; - return ngx_http_next_header_filter(r); + 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_charset_get_charset(ngx_http_charset_t *charsets, ngx_uint_t n, - ngx_str_t *charset) +ngx_http_get_charset(ngx_http_request_t *r, ngx_str_t *name) { - ngx_uint_t i; + 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 (charsets[i].name.len != charset->len) { + if (charset[i].name.len != name->len) { continue; } - if (ngx_strncasecmp(charsets[i].name.data, charset->data, charset->len) - == 0) - { + if (ngx_strncasecmp(charset[i].name.data, name->data, name->len) == 0) { return i; } } @@ -395,11 +482,12 @@ } -static ngx_int_t -ngx_http_charset_set_charset(ngx_http_request_t *r, - ngx_http_charset_t *charsets, ngx_int_t charset, ngx_int_t source_charset) +static ngx_inline void +ngx_http_set_charset(ngx_http_request_t *r, ngx_str_t *charset) { - ngx_http_charset_ctx_t *ctx; + if (r != r->main) { + return; + } if (r->headers_out.status == NGX_HTTP_MOVED_PERMANENTLY || r->headers_out.status == NGX_HTTP_MOVED_TEMPORARILY) @@ -410,16 +498,18 @@ */ r->headers_out.charset.len = 0; - - return ngx_http_next_header_filter(r); + return; } - r->headers_out.charset = charsets[charset].name; - r->utf8 = charsets[charset].utf8; + r->headers_out.charset = *charset; +} + - if (source_charset == NGX_CONF_UNSET || source_charset == charset) { - return ngx_http_next_header_filter(r); - } +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) { @@ -430,6 +520,7 @@ 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; @@ -521,12 +612,6 @@ break; } -#if (NGX_HAVE_WRITE_ZEROCOPY) - if (b->zerocopy_busy) { - break; - } -#endif - ctx->busy = cl->next; if (b->tag != (ngx_buf_tag_t) &ngx_http_charset_filter_module) { @@ -561,25 +646,33 @@ static ngx_uint_t ngx_http_charset_recode(ngx_buf_t *b, u_char *table) { - u_char *p; + u_char *p, *last; - for (p = b->pos; p < b->last; p++) { + last = b->last; - if (*p == table[*p]) { - continue; + for (p = b->pos; p < last; p++) { + + if (*p != table[*p]) { + goto recode; } + } + + return 0; - while (p < b->last) { +recode: + + do { + if (*p != table[*p]) { *p = table[*p]; - p++; } - b->in_file = 0; + p++; - return 1; - } + } while (p < last); - return 0; + b->in_file = 0; + + return 1; } @@ -628,7 +721,7 @@ size = buf->last - src; saved = src; - n = ngx_utf_decode(&saved, size); + n = ngx_utf8_decode(&saved, size); if (n == 0xfffffffe) { /* incomplete UTF-8 symbol */ @@ -696,7 +789,7 @@ } saved = ctx->saved; - n = ngx_utf_decode(&saved, i); + n = ngx_utf8_decode(&saved, i); c = '\0'; @@ -804,7 +897,7 @@ len = buf->last - src; - n = ngx_utf_decode(&src, len); + n = ngx_utf8_decode(&src, len); if (n < 0x10000) { @@ -1256,7 +1349,7 @@ p = &table->src2dst[src * NGX_UTF_LEN] + 1; - n = ngx_utf_decode(&p, i); + n = ngx_utf8_decode(&p, i); if (n > 0xffff) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, @@ -1315,7 +1408,7 @@ if (cmd->offset == offsetof(ngx_http_charset_loc_conf_t, charset) && ngx_strcmp(value[1].data, "off") == 0) { - *cp = NGX_HTTP_NO_CHARSET; + *cp = NGX_HTTP_CHARSET_OFF; return NGX_CONF_OK; } @@ -1395,25 +1488,27 @@ mcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_charset_main_conf_t)); if (mcf == NULL) { - return NGX_CONF_ERROR; + return NULL; } if (ngx_array_init(&mcf->charsets, cf->pool, 2, sizeof(ngx_http_charset_t)) - == NGX_ERROR) + != NGX_OK) { - return NGX_CONF_ERROR; + return NULL; } if (ngx_array_init(&mcf->tables, cf->pool, 1, - sizeof(ngx_http_charset_tables_t)) == NGX_ERROR) + sizeof(ngx_http_charset_tables_t)) + != NGX_OK) { - return NGX_CONF_ERROR; + return NULL; } if (ngx_array_init(&mcf->recodes, cf->pool, 2, - sizeof(ngx_http_charset_recode_t)) == NGX_ERROR) + sizeof(ngx_http_charset_recode_t)) + != NGX_OK) { - return NGX_CONF_ERROR; + return NULL; } return mcf; @@ -1427,9 +1522,16 @@ lcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_charset_loc_conf_t)); if (lcf == NULL) { - return NGX_CONF_ERROR; + 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; @@ -1448,16 +1550,28 @@ 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_NO_CHARSET); + 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->source_charset == NGX_CONF_UNSET) { - conf->source_charset = prev->source_charset; + 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->charset == NGX_HTTP_NO_CHARSET - || conf->source_charset == NGX_CONF_UNSET - || conf->charset == conf->source_charset) + if (conf->source_charset >= NGX_HTTP_CHARSET_VAR + || conf->charset >= NGX_HTTP_CHARSET_VAR) { return NGX_CONF_OK; } @@ -1519,9 +1633,8 @@ } 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); + "no \"charset_map\" between the charsets \"%V\" and \"%V\"", + &charset[c].name, &charset[recode[i].dst].name); return NGX_ERROR; next: diff -Nru nginx-0.5.33/src/http/modules/ngx_http_chunked_filter_module.c nginx-0.8.53/src/http/modules/ngx_http_chunked_filter_module.c --- nginx-0.5.33/src/http/modules/ngx_http_chunked_filter_module.c 2006-08-30 10:39:17.000000000 +0000 +++ nginx-0.8.53/src/http/modules/ngx_http_chunked_filter_module.c 2010-04-01 10:18:00.000000000 +0000 @@ -50,10 +50,12 @@ static ngx_int_t ngx_http_chunked_header_filter(ngx_http_request_t *r) { + ngx_http_core_loc_conf_t *clcf; + if (r->headers_out.status == NGX_HTTP_NOT_MODIFIED || r->headers_out.status == NGX_HTTP_NO_CONTENT - || r->headers_out.status == NGX_HTTP_CREATED - || r != r->main) + || r != r->main + || (r->method & NGX_HTTP_HEAD)) { return ngx_http_next_header_filter(r); } @@ -63,7 +65,14 @@ r->keepalive = 0; } else { - r->chunked = 1; + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + if (clcf->chunked_transfer_encoding) { + r->chunked = 1; + + } else { + r->keepalive = 0; + } } } diff -Nru nginx-0.5.33/src/http/modules/ngx_http_dav_module.c nginx-0.8.53/src/http/modules/ngx_http_dav_module.c --- nginx-0.5.33/src/http/modules/ngx_http_dav_module.c 2007-08-11 10:12:22.000000000 +0000 +++ nginx-0.8.53/src/http/modules/ngx_http_dav_module.c 2010-06-10 08:17:16.000000000 +0000 @@ -21,8 +21,9 @@ typedef struct { ngx_uint_t methods; - ngx_flag_t create_full_put_path; ngx_uint_t access; + ngx_uint_t min_delete_depth; + ngx_flag_t create_full_put_path; } ngx_http_dav_loc_conf_t; @@ -37,10 +38,11 @@ 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_no_init(void *ctx, void *prev); -static ngx_int_t ngx_http_dav_noop(ngx_tree_ctx_t *ctx, ngx_str_t *path); +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); @@ -49,10 +51,9 @@ 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_file(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_delete_path(ngx_http_request_t *r, - ngx_str_t *path, ngx_uint_t dir); 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); @@ -90,6 +91,13 @@ 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, @@ -138,11 +146,6 @@ ngx_int_t rc; ngx_http_dav_loc_conf_t *dlcf; - /* TODO: Win32 */ - if (r->zero_in_uri) { - return NGX_DECLINED; - } - dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_module); if (!(r->method & dlcf->methods)) { @@ -154,7 +157,9 @@ case NGX_HTTP_PUT: if (r->uri.data[r->uri.len - 1] == '/') { - return NGX_HTTP_BAD_REQUEST; + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "can not PUT to a collection"); + return NGX_HTTP_CONFLICT; } r->request_body_in_file_only = 1; @@ -195,24 +200,24 @@ static void ngx_http_dav_put_handler(ngx_http_request_t *r) { - char *failed; - u_char *name; size_t root; time_t date; - ngx_err_t err; ngx_str_t *temp, path; - ngx_uint_t status, not_found; + ngx_uint_t status; ngx_file_info_t fi; + ngx_ext_rename_file_t ext; ngx_http_dav_loc_conf_t *dlcf; ngx_http_map_uri_to_path(r, &path, &root, 0); + 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) == -1) { + if (ngx_file_info(path.data, &fi) == NGX_FILE_ERROR) { status = NGX_HTTP_CREATED; } else { @@ -235,94 +240,28 @@ dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_module); -#if !(NGX_WIN32) - - if (ngx_change_file_access(temp->data, dlcf->access) == NGX_FILE_ERROR) { - err = ngx_errno; - not_found = NGX_HTTP_INTERNAL_SERVER_ERROR; - failed = ngx_change_file_access_n; - name = temp->data; - - goto failed; - } - -#endif + 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_http_parse_time(r->headers_in.date->value.data, r->headers_in.date->value.len); if (date != NGX_ERROR) { - if (ngx_set_file_time(temp->data, - r->request_body->temp_file->file.fd, date) - != NGX_OK) - { - err = ngx_errno; - not_found = NGX_HTTP_INTERNAL_SERVER_ERROR; - failed = ngx_set_file_time_n; - name = temp->data; - - goto failed; - } + ext.time = date; + ext.fd = r->request_body->temp_file->file.fd; } } - not_found = NGX_HTTP_CONFLICT; - failed = ngx_rename_file_n; - name = path.data; - - if (ngx_rename_file(temp->data, path.data) != NGX_FILE_ERROR) { - goto ok; + if (ngx_ext_rename_file(temp, &path, &ext) != NGX_OK) { + ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return; } - err = ngx_errno; - - if (err == NGX_ENOENT) { - - if (dlcf->create_full_put_path) { - err = ngx_create_full_path(path.data, ngx_dir_access(dlcf->access)); - - if (err == 0) { - if (ngx_rename_file(temp->data, path.data) != NGX_FILE_ERROR) { - goto ok; - } - - err = ngx_errno; - } - } - } - -#if (NGX_WIN32) - - if (err == NGX_EEXIST) { - if (ngx_win32_rename_file(temp, &path, r->pool) != NGX_ERROR) { - - if (ngx_rename_file(temp->data, path.data) != NGX_FILE_ERROR) { - goto ok; - } - } - - err = ngx_errno; - } - -#endif - -failed: - - 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_dav_error(r->connection->log, err, - not_found, failed, name)); - - return; - -ok: - 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); @@ -343,43 +282,67 @@ static ngx_int_t ngx_http_dav_delete_handler(ngx_http_request_t *r) { - size_t root; - ngx_int_t rc, depth; - ngx_uint_t dir; - ngx_str_t path; - ngx_file_info_t fi; + 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; } - rc = ngx_http_discard_body(r); + dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_module); + + if (dlcf->min_delete_depth) { + d = 0; - if (rc != NGX_OK && rc != NGX_AGAIN) { - return rc; + 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: + ngx_http_map_uri_to_path(r, &path, &root, 0); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http delete filename: \"%s\"", path.data); - if (ngx_file_info(path.data, &fi) == -1) { - return ngx_http_dav_error(r->connection->log, ngx_errno, - NGX_HTTP_NOT_FOUND, ngx_file_info_n, - 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] != '/') { - /* TODO: 301 */ - return NGX_HTTP_BAD_REQUEST; + 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; } @@ -389,9 +352,16 @@ } 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; } @@ -409,16 +379,45 @@ static ngx_int_t -ngx_http_dav_no_init(void *ctx, void *prev) +ngx_http_dav_delete_path(ngx_http_request_t *r, ngx_str_t *path, ngx_uint_t dir) { - return NGX_OK; -} + char *failed; + ngx_tree_ctx_t tree; + if (dir) { -static ngx_int_t -ngx_http_dav_noop(ngx_tree_ctx_t *ctx, ngx_str_t *path) -{ - return NGX_OK; + 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); } @@ -459,23 +458,35 @@ 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_int_t rc; 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; } - rc = ngx_http_discard_body(r); - - if (rc != NGX_OK && rc != NGX_AGAIN) { - return rc; + 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; } - ngx_http_map_uri_to_path(r, &path, &root, 0); + p = ngx_http_map_uri_to_path(r, &path, &root, 0); + + *(p - 1) = '\0'; + r->uri.len--; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http mkcol path: \"%s\"", path.data); @@ -491,8 +502,7 @@ } return ngx_http_dav_error(r->connection->log, ngx_errno, - NGX_HTTP_BAD_REQUEST, ngx_create_dir_n, - path.data); + NGX_HTTP_CONFLICT, ngx_create_dir_n, path.data); } @@ -500,15 +510,18 @@ ngx_http_dav_copy_move_handler(ngx_http_request_t *r) { u_char *p, *host, *last, ch; - size_t root; + size_t len, root; ngx_err_t err; ngx_int_t rc, depth; - ngx_uint_t overwrite, slash; - ngx_str_t path, uri; + 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; @@ -522,8 +535,19 @@ return NGX_HTTP_BAD_REQUEST; } - if (dest->value.len < sizeof("http://") - 1 + r->server_name.len + 1) { - goto invalid_destination; + 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) @@ -549,18 +573,17 @@ host = dest->value.data + sizeof("http://") - 1; } - if (ngx_strncmp(host, r->server_name.data, r->server_name.len) != 0) { + 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 " + "\"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 + r->server_name.len; p < last; p++) { + for (p = host + len; p < last; p++) { if (*p == '/') { goto destination_done; } @@ -571,15 +594,44 @@ 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: - depth = ngx_http_dav_depth(r, 0); + duri.len = last - p; + duri.data = p; + flags = 0; - if (depth != 0 && depth != NGX_HTTP_DAV_INFINITY_DEPTH) { - return NGX_HTTP_BAD_REQUEST; + 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; @@ -603,7 +655,6 @@ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "client sent invalid \"Overwrite\" header: \"%V\"", &over->value); - return NGX_HTTP_BAD_REQUEST; } @@ -611,21 +662,13 @@ overwrite_done: - rc = ngx_http_discard_body(r); - - if (rc != NGX_OK && rc != NGX_AGAIN) { - return rc; - } - ngx_http_map_uri_to_path(r, &path, &root, 0); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http copy from: \"%s\"", path.data); uri = r->uri; - - r->uri.len = last - p; - r->uri.data = p; + r->uri = duri; ngx_http_map_uri_to_path(r, ©.path, &root, 0); @@ -645,53 +688,68 @@ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http copy to: \"%s\"", copy.path.data); - if (ngx_file_info(copy.path.data, &fi) == -1) { + 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_file_info_n, + 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; } - 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, ngx_is_dir(&fi)); - - if (rc != NGX_OK) { - return rc; - } + dir = ngx_is_dir(&fi); } - if (ngx_file_info(path.data, &fi) == -1) { + 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_file_info_n, + NGX_HTTP_NOT_FOUND, ngx_link_info_n, path.data); } - if (ngx_is_dir(&fi)) { if (r->uri.data[r->uri.len - 1] != '/') { - /* TODO: 301 */ + 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) { @@ -710,8 +768,8 @@ copy.len = path.len; - tree.init_handler = ngx_http_dav_no_init; - tree.file_handler = ngx_http_dav_copy_file; + 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; @@ -734,29 +792,33 @@ } else { - if (dest->value.data[dest->value.len - 1] == '/') { - return NGX_HTTP_BAD_REQUEST; - } - if (r->method == NGX_HTTP_MOVE) { - if (ngx_rename_file(path.data, copy.path.data) != NGX_FILE_ERROR) { + + 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; } - } - tree.data = © - tree.log = r->connection->log; - - if (ngx_http_dav_copy_file(&tree, &path) != NGX_FILE_ERROR) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } - if (r->method == NGX_HTTP_MOVE) { - rc = ngx_http_dav_delete_path(r, &path, 0); + dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_module); - if (rc != NGX_OK) { - return rc; - } - } + 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; } } @@ -807,9 +869,6 @@ u_char *p, *dir; size_t len; ngx_http_dav_copy_ctx_t *copy; -#if (WIN32) - ngx_fd_t fd; -#endif ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0, "http copy dir time: \"%s\"", path->data); @@ -829,7 +888,9 @@ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0, "http copy dir time to: \"%s\"", dir); -#if (WIN32) +#if (NGX_WIN32) + { + ngx_fd_t fd; fd = ngx_open_file(dir, NGX_FILE_RDWR, NGX_FILE_OPEN, 0); @@ -847,6 +908,7 @@ ngx_log_error(NGX_LOG_ALERT, ctx->log, ngx_errno, ngx_close_file_n " \"%s\" failed", dir); } + } failed: @@ -866,15 +928,12 @@ static ngx_int_t -ngx_http_dav_copy_file(ngx_tree_ctx_t *ctx, ngx_str_t *path) +ngx_http_dav_copy_tree_file(ngx_tree_ctx_t *ctx, ngx_str_t *path) { u_char *p, *file; size_t len; - off_t size; - ssize_t n; - ngx_fd_t fd, copy_fd; + ngx_copy_file_t cf; ngx_http_dav_copy_ctx_t *copy; - u_char buf[NGX_HTTP_DAV_COPY_BLOCK]; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0, "http copy file: \"%s\"", path->data); @@ -894,57 +953,13 @@ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0, "http copy file to: \"%s\"", file); - fd = ngx_open_file(path->data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0); - - if (fd == NGX_INVALID_FILE) { - (void) ngx_http_dav_error(ctx->log, ngx_errno, 0, ngx_open_file_n, - path->data); - goto failed; - } - - copy_fd = ngx_open_file(file, NGX_FILE_WRONLY, NGX_FILE_CREATE_OR_OPEN, - ctx->access); - - if (copy_fd == NGX_INVALID_FILE) { - (void) ngx_http_dav_error(ctx->log, ngx_errno, 0, ngx_open_file_n, - file); - goto copy_failed; - } - - for (size = ctx->size; size > 0; size -= n) { - - n = ngx_read_fd(fd, buf, NGX_HTTP_DAV_COPY_BLOCK); - - if (n == NGX_FILE_ERROR) { - ngx_log_error(NGX_LOG_ALERT, ctx->log, ngx_errno, - ngx_read_fd_n " \"%s\" failed", path->data); - break; - } - - if (ngx_write_fd(copy_fd, buf, n) == NGX_FILE_ERROR) { - ngx_log_error(NGX_LOG_ALERT, ctx->log, ngx_errno, - ngx_write_fd_n " \"%s\" failed", file); - } - } - - if (ngx_set_file_time(file, copy_fd, ctx->mtime) != NGX_OK) { - ngx_log_error(NGX_LOG_ALERT, ctx->log, ngx_errno, - ngx_set_file_time_n " \"%s\" failed", file); - } - - if (ngx_close_file(copy_fd) == NGX_FILE_ERROR) { - ngx_log_error(NGX_LOG_ALERT, ctx->log, ngx_errno, - ngx_close_file_n " \"%s\" failed", file); - } - -copy_failed: + cf.size = ctx->size; + cf.buf_size = 0; + cf.access = ctx->access; + cf.time = ctx->mtime; + cf.log = ctx->log; - if (ngx_close_file(fd) == NGX_FILE_ERROR) { - ngx_log_error(NGX_LOG_ALERT, ctx->log, ngx_errno, - ngx_close_file_n " \"%s\" failed", path->data); - } - -failed: + (void) ngx_copy_file(path->data, file, &cf); ngx_free(file); @@ -953,49 +968,6 @@ 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 = ngx_http_dav_no_init; - 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_depth(ngx_http_request_t *r, ngx_int_t dflt) { ngx_table_elt_t *depth; @@ -1084,7 +1056,7 @@ location = path + clcf->root.len; } else { - location = ngx_palloc(r->pool, r->uri.len); + location = ngx_pnalloc(r->pool, r->uri.len); if (location == NULL) { return NGX_ERROR; } @@ -1111,7 +1083,7 @@ conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_dav_loc_conf_t)); if (conf == NULL) { - return NGX_CONF_ERROR; + return NULL; } /* @@ -1120,8 +1092,9 @@ * conf->methods = 0; */ - conf->create_full_put_path = NGX_CONF_UNSET; + conf->min_delete_depth = NGX_CONF_UNSET_UINT; conf->access = NGX_CONF_UNSET_UINT; + conf->create_full_put_path = NGX_CONF_UNSET; return conf; } @@ -1136,11 +1109,14 @@ ngx_conf_merge_bitmask_value(conf->methods, prev->methods, (NGX_CONF_BITMASK_SET|NGX_HTTP_DAV_OFF)); - ngx_conf_merge_value(conf->create_full_put_path, prev->create_full_put_path, - 0); + 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; } diff -Nru nginx-0.5.33/src/http/modules/ngx_http_degradation_module.c nginx-0.8.53/src/http/modules/ngx_http_degradation_module.c --- nginx-0.5.33/src/http/modules/ngx_http_degradation_module.c 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/src/http/modules/ngx_http_degradation_module.c 2010-10-04 14:59:41.000000000 +0000 @@ -0,0 +1,249 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#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_uint_t ngx_degraded; + + +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 substract 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) { + ngx_degraded = 1; + + if (log) { + ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0, + "degradation sbrk:%uzM", + sbrk_size / (1024 * 1024)); + } + + return 1; + } + } + + ngx_degraded = 0; + + 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; +} diff -Nru nginx-0.5.33/src/http/modules/ngx_http_empty_gif_module.c nginx-0.8.53/src/http/modules/ngx_http_empty_gif_module.c --- nginx-0.5.33/src/http/modules/ngx_http_empty_gif_module.c 2007-10-29 15:08:43.000000000 +0000 +++ nginx-0.8.53/src/http/modules/ngx_http_empty_gif_module.c 2010-06-18 15:17:07.000000000 +0000 @@ -105,58 +105,32 @@ }; +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_int_t rc; - ngx_buf_t *b; - ngx_chain_t out; + ngx_int_t rc; + ngx_http_complex_value_t cv; if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) { return NGX_HTTP_NOT_ALLOWED; } - rc = ngx_http_discard_body(r); + rc = ngx_http_discard_request_body(r); - if (rc != NGX_OK && rc != NGX_AGAIN) { + if (rc != NGX_OK) { return rc; } - r->headers_out.content_type.len = sizeof("image/gif") - 1; - r->headers_out.content_type.data = (u_char *) "image/gif"; - - if (r->method == NGX_HTTP_HEAD) { - r->headers_out.status = NGX_HTTP_OK; - r->headers_out.content_length_n = sizeof(ngx_empty_gif); - r->headers_out.last_modified_time = 23349600; - - return ngx_http_send_header(r); - } - - b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t)); - if (b == NULL) { - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } + ngx_memzero(&cv, sizeof(ngx_http_complex_value_t)); - out.buf = b; - out.next = NULL; - - b->pos = ngx_empty_gif; - b->last = ngx_empty_gif + sizeof(ngx_empty_gif); - b->memory = 1; - b->last_buf = 1; - - r->headers_out.status = NGX_HTTP_OK; - r->headers_out.content_length_n = sizeof(ngx_empty_gif); + cv.value.len = sizeof(ngx_empty_gif); + cv.value.data = ngx_empty_gif; r->headers_out.last_modified_time = 23349600; - rc = ngx_http_send_header(r); - - if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) { - return rc; - } - - return ngx_http_output_filter(r, &out); + return ngx_http_send_response(r, NGX_HTTP_OK, &ngx_http_gif_type, &cv); } diff -Nru nginx-0.5.33/src/http/modules/ngx_http_fastcgi_module.c nginx-0.8.53/src/http/modules/ngx_http_fastcgi_module.c --- nginx-0.5.33/src/http/modules/ngx_http_fastcgi_module.c 2007-09-23 19:28:29.000000000 +0000 +++ nginx-0.8.53/src/http/modules/ngx_http_fastcgi_module.c 2010-08-03 13:35:48.000000000 +0000 @@ -7,7 +7,6 @@ #include #include #include -#include typedef struct { @@ -20,6 +19,21 @@ ngx_array_t *params; ngx_array_t *params_source; ngx_array_t *catch_stderr; + + ngx_array_t *fastcgi_lengths; + ngx_array_t *fastcgi_values; + + ngx_hash_t headers_hash; + ngx_uint_t header_params; + +#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; @@ -51,9 +65,13 @@ size_t length; size_t padding; - ngx_uint_t fastcgi_stdout; /* unsigned :1 */ + unsigned fastcgi_stdout:1; + unsigned large_stderr:1; ngx_array_t *split_parts; + + ngx_str_t script_name; + ngx_str_t path_info; } ngx_http_fastcgi_ctx_t; @@ -104,6 +122,11 @@ } 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_process_header(ngx_http_request_t *r); @@ -121,58 +144,31 @@ void *parent, void *child); 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 char *ngx_http_fastcgi_upstream_max_fails_unsupported(ngx_conf_t *cf, - ngx_command_t *cmd, void *conf); -static char *ngx_http_fastcgi_upstream_fail_timeout_unsupported(ngx_conf_t *cf, - ngx_command_t *cmd, void *conf); - - -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_str_t ngx_http_fastcgi_script_name = - ngx_string("fastcgi_script_name"); - static ngx_conf_post_t ngx_http_fastcgi_lowat_post = { ngx_http_fastcgi_lowat_check }; -static ngx_conf_deprecated_t ngx_conf_deprecated_fastcgi_header_buffer_size = { - ngx_conf_deprecated, "fastcgi_header_buffer_size", "fastcgi_buffer_size" -}; - -static ngx_conf_deprecated_t ngx_conf_deprecated_fastcgi_redirect_errors = { - ngx_conf_deprecated, "fastcgi_redirect_errors", "fastcgi_intercept_errors" -}; - static ngx_conf_bitmask_t ngx_http_fastcgi_next_upstream_masks[] = { { ngx_string("error"), NGX_HTTP_UPSTREAM_FT_ERROR }, @@ -181,11 +177,15 @@ { ngx_string("http_500"), NGX_HTTP_UPSTREAM_FT_HTTP_500 }, { ngx_string("http_503"), NGX_HTTP_UPSTREAM_FT_HTTP_503 }, { ngx_string("http_404"), NGX_HTTP_UPSTREAM_FT_HTTP_404 }, + { 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"), @@ -202,6 +202,13 @@ 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, @@ -223,6 +230,13 @@ 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_TAKE1, + 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, @@ -251,13 +265,6 @@ offsetof(ngx_http_fastcgi_loc_conf_t, upstream.buffer_size), NULL }, - { ngx_string("fastcgi_header_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), - &ngx_conf_deprecated_fastcgi_header_buffer_size }, - { 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, @@ -279,13 +286,6 @@ offsetof(ngx_http_fastcgi_loc_conf_t, upstream.intercept_errors), NULL }, - { ngx_string("fastcgi_redirect_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), - &ngx_conf_deprecated_fastcgi_redirect_errors }, - { 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, @@ -307,12 +307,79 @@ offsetof(ngx_http_fastcgi_loc_conf_t, upstream.busy_buffers_size_conf), 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, + 0, + 0, + &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_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 }, + +#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), - (void *) ngx_garbage_collector_temp_handler }, + NULL }, { ngx_string("fastcgi_max_temp_file_size"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, @@ -335,20 +402,6 @@ offsetof(ngx_http_fastcgi_loc_conf_t, upstream.next_upstream), &ngx_http_fastcgi_next_upstream_masks }, - { ngx_string("fastcgi_upstream_max_fails"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, - ngx_http_fastcgi_upstream_max_fails_unsupported, - 0, - 0, - NULL }, - - { ngx_string("fastcgi_upstream_fail_timeout"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, - ngx_http_fastcgi_upstream_fail_timeout_unsupported, - 0, - 0, - NULL }, - { ngx_string("fastcgi_param"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2, ngx_conf_set_keyval_slot, @@ -357,19 +410,26 @@ NULL }, { ngx_string("fastcgi_pass_header"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, + 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_FLAG, + 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_FLAG, ngx_conf_set_str_array_slot, @@ -412,47 +472,118 @@ }; +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_null_string, NULL, NULL, 0, 0, 0 } +}; + + 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-Buffer"), + 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("") }, + { ngx_string("HTTP_IF_UNMODIFIED_SINCE"), ngx_string("") }, + { ngx_string("HTTP_IF_NONE_MATCH"), ngx_string("") }, + { 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 (r->subrequest_in_memory) { ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, "ngx_http_fastcgi_module does not support " - "subrequest in memeory"); + "subrequest in memory"); return NGX_HTTP_INTERNAL_SERVER_ERROR; } - flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module); + if (ngx_http_upstream_create(r) != NGX_OK) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } - u = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_t)); - if (u == NULL) { + f = ngx_pcalloc(r->pool, sizeof(ngx_http_fastcgi_ctx_t)); + if (f == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } - u->peer.log = r->connection->log; - u->peer.log_error = NGX_ERROR_ERR; -#if (NGX_THREADS) - u->peer.lock = &r->connection->lock; -#endif + 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) + 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; @@ -469,8 +600,6 @@ u->pipe->input_filter = ngx_http_fastcgi_input_filter; u->pipe->input_ctx = r; - r->upstream = u; - rc = ngx_http_read_client_request_body(r, ngx_http_upstream_init); if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { @@ -482,16 +611,96 @@ 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; + } + + if (url.no_port) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "no port in upstream \"%V\"", &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 && url.addrs[0].sockaddr) { + u->resolved->sockaddr = url.addrs[0].sockaddr; + u->resolved->socklen = url.addrs[0].socklen; + u->resolved->naddrs = 1; + u->resolved->host = url.addrs[0].name; + + } else { + u->resolved->host = url.host; + u->resolved->port = url.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; - size_t size, len, key_len, val_len, padding; - ngx_uint_t i, n, next; + u_char ch, *pos, *lowcase_key; + size_t size, len, key_len, val_len, padding, + allocated; + ngx_uint_t i, n, next, hash, header_params; ngx_buf_t *b; ngx_chain_t *cl, *body; ngx_list_part_t *part; - ngx_table_elt_t *header; + ngx_table_elt_t *header, **ignored; ngx_http_script_code_pt code; ngx_http_script_engine_t e, le; ngx_http_fastcgi_header_t *h; @@ -499,13 +708,15 @@ ngx_http_script_len_code_pt lcode; len = 0; + header_params = 0; + ignored = NULL; flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module); if (flcf->params_len) { ngx_memzero(&le, sizeof(ngx_http_script_engine_t)); - ngx_http_script_flush_no_cachable_variables(r, flcf->flushes); + ngx_http_script_flush_no_cacheable_variables(r, flcf->flushes); le.flushed = 1; le.ip = flcf->params_len->elts; @@ -527,6 +738,16 @@ if (flcf->upstream.pass_request_headers) { + allocated = 0; + lowcase_key = NULL; + + if (flcf->header_params) { + ignored = ngx_palloc(r->pool, flcf->header_params * sizeof(void *)); + if (ignored == NULL) { + return NGX_ERROR; + } + } + part = &r->headers_in.headers.part; header = part->elts; @@ -542,16 +763,51 @@ i = 0; } - len += ((sizeof("HTTP_") - 1 + header[i].key.len > 127) ? 4 : 1) - + ((header[i].value.len > 127) ? 4 : 1) - + sizeof("HTTP_") - 1 + header[i].key.len + header[i].value.len; + if (flcf->header_params) { + 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(&flcf->headers_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: the request record is too big"); + "fastcgi request record is too big: %uz", len); return NGX_ERROR; } @@ -636,6 +892,11 @@ 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; @@ -659,26 +920,32 @@ i = 0; } - len = sizeof("HTTP_") - 1 + header[i].key.len; - if (len > 127) { - *b->last++ = (u_char) (((len >> 24) & 0x7f) | 0x80); - *b->last++ = (u_char) ((len >> 16) & 0xff); - *b->last++ = (u_char) ((len >> 8) & 0xff); - *b->last++ = (u_char) (len & 0xff); - - } else { - *b->last++ = (u_char) len; + for (n = 0; n < header_params; n++) { + if (&header[i] == ignored[n]) { + goto next; + } } - len = header[i].value.len; - if (len > 127) { - *b->last++ = (u_char) (((len >> 24) & 0x7f) | 0x80); - *b->last++ = (u_char) ((len >> 16) & 0xff); - *b->last++ = (u_char) ((len >> 8) & 0xff); - *b->last++ = (u_char) (len & 0xff); + 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) len; + *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); @@ -696,8 +963,15 @@ *b->last++ = ch; } - b->last = ngx_copy(b->last, header[i].value.data, - header[i].value.len); + 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; } } @@ -856,6 +1130,7 @@ f->state = ngx_http_fastcgi_st_version; f->fastcgi_stdout = 0; + f->large_stderr = 0; return NGX_OK; } @@ -864,9 +1139,10 @@ static ngx_int_t ngx_http_fastcgi_process_header(ngx_http_request_t *r) { - u_char *p, *start, *last, *part_start; + u_char *p, *msg, *start, *last, + *part_start, *part_end; size_t size; - ngx_str_t *status_line, line, *pattern; + ngx_str_t *status_line, *pattern; ngx_int_t rc, status; ngx_buf_t buf; ngx_uint_t i; @@ -882,15 +1158,6 @@ umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module); - if (f == NULL) { - 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); - } - u = r->upstream; for ( ;; ) { @@ -959,41 +1226,40 @@ if (f->type == NGX_HTTP_FASTCGI_STDERR) { if (f->length) { - line.data = u->buffer.pos; + msg = u->buffer.pos; if (u->buffer.pos + f->length <= u->buffer.last) { - line.len = f->length; u->buffer.pos += f->length; f->length = 0; f->state = ngx_http_fastcgi_st_padding; } else { - line.len = u->buffer.last - u->buffer.pos; f->length -= u->buffer.last - u->buffer.pos; u->buffer.pos = u->buffer.last; } - while (line.data[line.len - 1] == LF - || line.data[line.len - 1] == CR - || line.data[line.len - 1] == '.' - || line.data[line.len - 1] == ' ') - { - line.len--; + 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: \"%V\"", &line); + "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; - line.data[line.len - 1] = '\0'; - for (i = 0; i < flcf->catch_stderr->nelts; i++) { - if (ngx_strstr(line.data, pattern[i].data)) { - return NGX_HTTP_BAD_GATEWAY; + if (ngx_strnstr(msg, (char *) pattern[i].data, + p - msg) + != NULL) + { + return NGX_HTTP_UPSTREAM_INVALID_HEADER; } } } @@ -1007,8 +1273,17 @@ * of the PHP warnings to not allocate memory */ - u->buffer.pos = u->buffer.start; - u->buffer.last = u->buffer.start; +#if (NGX_HTTP_CACHE) + if (r->cache) { + u->buffer.pos = u->buffer.start + + r->cache->header_start; + } else { + u->buffer.pos = u->buffer.start; + } +#endif + + u->buffer.last = u->buffer.pos; + f->large_stderr = 1; } return NGX_AGAIN; @@ -1024,6 +1299,46 @@ /* f->type == NGX_HTTP_FASTCGI_STDOUT */ +#if (NGX_HTTP_CACHE) + + if (f->large_stderr && r->cache) { + u_char *start; + 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; @@ -1045,8 +1360,9 @@ for ( ;; ) { part_start = u->buffer.pos; + part_end = u->buffer.last; - rc = ngx_http_parse_header_line(r, &u->buffer); + rc = ngx_http_parse_header_line(r, &u->buffer, 1); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http fastcgi parser: %d", rc); @@ -1061,7 +1377,7 @@ h = ngx_list_push(&u->headers_in.headers); if (h == NULL) { - return NGX_HTTP_INTERNAL_SERVER_ERROR; + return NGX_ERROR; } if (f->split_parts && f->split_parts->nelts) { @@ -1073,9 +1389,9 @@ size += part[i].end - part[i].start; } - p = ngx_palloc(r->pool, size); + p = ngx_pnalloc(r->pool, size); if (p == NULL) { - return NGX_HTTP_INTERNAL_SERVER_ERROR; + return NGX_ERROR; } buf.pos = p; @@ -1091,7 +1407,7 @@ f->split_parts->nelts = 0; - rc = ngx_http_parse_header_line(r, &buf); + rc = ngx_http_parse_header_line(r, &buf, 1); h->key.len = r->header_name_end - r->header_name_start; h->key.data = r->header_name_start; @@ -1101,9 +1417,9 @@ h->value.data = r->header_start; h->value.data[h->value.len] = '\0'; - h->lowcase_key = ngx_palloc(r->pool, h->key.len); + h->lowcase_key = ngx_pnalloc(r->pool, h->key.len); if (h->lowcase_key == NULL) { - return NGX_HTTP_INTERNAL_SERVER_ERROR; + return NGX_ERROR; } } else { @@ -1111,11 +1427,11 @@ h->key.len = r->header_name_end - r->header_name_start; h->value.len = r->header_end - r->header_start; - h->key.data = ngx_palloc(r->pool, - h->key.len + 1 + h->value.len + 1 - + h->key.len); + h->key.data = ngx_pnalloc(r->pool, + h->key.len + 1 + h->value.len + 1 + + h->key.len); if (h->key.data == NULL) { - return NGX_HTTP_INTERNAL_SERVER_ERROR; + return NGX_ERROR; } h->value.data = h->key.data + h->key.len + 1; @@ -1134,16 +1450,14 @@ ngx_memcpy(h->lowcase_key, r->lowcase_header, h->key.len); } else { - for (i = 0; i < h->key.len; i++) { - h->lowcase_key[i] = ngx_tolower(h->key.data[i]); - } + 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_HTTP_INTERNAL_SERVER_ERROR; + return NGX_ERROR; } ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, @@ -1172,24 +1486,28 @@ status = ngx_atoi(status_line->data, 3); if (status == NGX_ERROR) { - return NGX_HTTP_INTERNAL_SERVER_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; - u->headers_in.status_line.len = sizeof("200 OK") - 1; - u->headers_in.status_line.data = (u_char *) "200 OK"; + ngx_str_set(&u->headers_in.status_line, "200 OK"); } - u->state->status = u->headers_in.status_n; -#if 0 - if (u->cachable) { - u->cachable = ngx_http_upstream_is_cachable(r); + if (u->state) { + u->state->status = u->headers_in.status_n; } -#endif break; } @@ -1221,7 +1539,7 @@ } if (rc == NGX_OK) { - return NGX_AGAIN; + continue; } /* rc == NGX_AGAIN */ @@ -1233,14 +1551,18 @@ f->split_parts = ngx_array_create(r->pool, 1, sizeof(ngx_http_fastcgi_split_part_t)); if (f->split_parts == NULL) { - return NGX_HTTP_INTERNAL_SERVER_ERROR; + return NGX_ERROR; } } part = ngx_array_push(f->split_parts); part->start = part_start; - part->end = u->buffer.last; + part->end = part_end; + + if (u->buffer.pos < u->buffer.last) { + continue; + } return NGX_AGAIN; } @@ -1250,9 +1572,9 @@ 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_str_t line; ngx_chain_t *cl; ngx_http_request_t *r; ngx_http_fastcgi_ctx_t *f; @@ -1336,30 +1658,27 @@ break; } - line.data = f->pos; + msg = f->pos; if (f->pos + f->length <= f->last) { - line.len = f->length; f->pos += f->length; f->length = 0; f->state = ngx_http_fastcgi_st_padding; } else { - line.len = f->last - f->pos; f->length -= f->last - f->pos; f->pos = f->last; } - while (line.data[line.len - 1] == LF - || line.data[line.len - 1] == CR - || line.data[line.len - 1] == '.' - || line.data[line.len - 1] == ' ') - { - line.len--; + 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: \"%V\"", &line); + "FastCGI sent in stderr: \"%*s\"", + m + 1 - msg, msg); if (f->pos == f->last) { break; @@ -1607,15 +1926,17 @@ static ngx_int_t ngx_http_fastcgi_add_variables(ngx_conf_t *cf) { - ngx_http_variable_t *var; + ngx_http_variable_t *var, *v; - var = ngx_http_add_variable(cf, &ngx_http_fastcgi_script_name, - NGX_HTTP_VAR_NOHASH|NGX_HTTP_VAR_NOCACHABLE); - if (var == NULL) { - return NGX_ERROR; - } + 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 = ngx_http_fastcgi_script_name_variable; + var->get_handler = v->get_handler; + var->data = v->data; + } return NGX_OK; } @@ -1628,26 +1949,25 @@ conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_fastcgi_loc_conf_t)); if (conf == NULL) { - return NGX_CONF_ERROR; + return NULL; } /* * set by ngx_pcalloc(): * * conf->upstream.bufs.num = 0; + * conf->upstream.ignore_headers = 0; * conf->upstream.next_upstream = 0; + * 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.hide_headers = NULL; - * conf->upstream.pass_headers = NULL; - * conf->upstream.schema = { 0, NULL }; * conf->upstream.uri = { 0, NULL }; * conf->upstream.location = NULL; * conf->upstream.store_lengths = NULL; * conf->upstream.store_values = NULL; * - * conf->index.len = 0; - * conf->index.data = NULL; + * conf->index.len = { 0, NULL }; */ conf->upstream.store = NGX_CONF_UNSET; @@ -1669,6 +1989,17 @@ 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_PTR; + conf->upstream.cache_min_uses = NGX_CONF_UNSET_UINT; + conf->upstream.cache_bypass = NGX_CONF_UNSET_PTR; + conf->upstream.no_cache = NGX_CONF_UNSET_PTR; + conf->upstream.cache_valid = NGX_CONF_UNSET_PTR; +#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 */ @@ -1689,18 +2020,18 @@ u_char *p; size_t size; uintptr_t *code; - ngx_str_t *header; - ngx_uint_t i, j; - ngx_array_t hide_headers; + ngx_uint_t i; + ngx_array_t headers_names; ngx_keyval_t *src; ngx_hash_key_t *hk; ngx_hash_init_t hash; + ngx_http_core_loc_conf_t *clcf; ngx_http_script_compile_t sc; ngx_http_script_copy_code_t *copy; if (conf->upstream.store != 0) { ngx_conf_merge_value(conf->upstream.store, - prev->upstream.store, 0); + prev->upstream.store, 0); if (conf->upstream.store_lengths == NULL) { conf->upstream.store_lengths = prev->upstream.store_lengths; @@ -1826,6 +2157,11 @@ } + 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 @@ -1837,140 +2173,149 @@ |NGX_HTTP_UPSTREAM_FT_OFF; } - ngx_conf_merge_path_value(conf->upstream.temp_path, + if (ngx_conf_merge_path_value(cf, &conf->upstream.temp_path, prev->upstream.temp_path, - NGX_HTTP_FASTCGI_TEMP_PATH, 1, 2, 0, - ngx_garbage_collector_temp_handler, cf); - - 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_http_fastcgi_temp_path) + != NGX_OK) + { + return NGX_CONF_ERROR; + } - ngx_conf_merge_str_value(conf->index, prev->index, ""); +#if (NGX_HTTP_CACHE) - if (conf->upstream.hide_headers == NULL - && conf->upstream.pass_headers == NULL) - { - conf->upstream.hide_headers = prev->upstream.hide_headers; - conf->upstream.pass_headers = prev->upstream.pass_headers; - conf->upstream.hide_headers_hash = prev->upstream.hide_headers_hash; + ngx_conf_merge_ptr_value(conf->upstream.cache, + prev->upstream.cache, NULL); - if (conf->upstream.hide_headers_hash.buckets) { - goto peers; - } + if (conf->upstream.cache && conf->upstream.cache->data == NULL) { + ngx_shm_zone_t *shm_zone; - } else { - if (conf->upstream.hide_headers == NULL) { - conf->upstream.hide_headers = prev->upstream.hide_headers; - } + shm_zone = conf->upstream.cache; - if (conf->upstream.pass_headers == NULL) { - conf->upstream.pass_headers = prev->upstream.pass_headers; - } - } + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "\"fastcgi_cache\" zone \"%V\" is unknown", + &shm_zone->shm.name); - if (ngx_array_init(&hide_headers, cf->temp_pool, 4, sizeof(ngx_hash_key_t)) - != NGX_OK) - { return NGX_CONF_ERROR; } - for (header = ngx_http_fastcgi_hide_headers; header->len; header++) { - hk = ngx_array_push(&hide_headers); - if (hk == NULL) { - return NGX_CONF_ERROR; - } - - hk->key = *header; - hk->key_hash = ngx_hash_key_lc(header->data, header->len); - hk->value = (void *) 1; - } + ngx_conf_merge_uint_value(conf->upstream.cache_min_uses, + prev->upstream.cache_min_uses, 1); - if (conf->upstream.hide_headers) { + ngx_conf_merge_bitmask_value(conf->upstream.cache_use_stale, + prev->upstream.cache_use_stale, + (NGX_CONF_BITMASK_SET + |NGX_HTTP_UPSTREAM_FT_OFF)); - header = conf->upstream.hide_headers->elts; + 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; + } - for (i = 0; i < conf->upstream.hide_headers->nelts; i++) { + if (conf->upstream.cache_methods == 0) { + conf->upstream.cache_methods = prev->upstream.cache_methods; + } - hk = hide_headers.elts; + conf->upstream.cache_methods |= NGX_HTTP_GET|NGX_HTTP_HEAD; - for (j = 0; j < hide_headers.nelts; j++) { - if (ngx_strcasecmp(header[i].data, hk[j].key.data) == 0) { - goto exist; - } - } + ngx_conf_merge_ptr_value(conf->upstream.cache_bypass, + prev->upstream.cache_bypass, NULL); - hk = ngx_array_push(&hide_headers); - if (hk == NULL) { - return NGX_CONF_ERROR; - } + ngx_conf_merge_ptr_value(conf->upstream.no_cache, + prev->upstream.no_cache, NULL); - hk->key = header[i]; - hk->key_hash = ngx_hash_key_lc(header[i].data, header[i].len); - hk->value = (void *) 1; + if (conf->upstream.no_cache && conf->upstream.cache_bypass == NULL) { + ngx_log_error(NGX_LOG_WARN, cf->log, 0, + "\"fastcgi_no_cache\" functionality has been changed in 0.8.46, " + "now it should be used together with \"fastcgi_cache_bypass\""); + } - exist: + ngx_conf_merge_ptr_value(conf->upstream.cache_valid, + prev->upstream.cache_valid, NULL); - continue; - } + if (conf->cache_key.value.data == NULL) { + conf->cache_key = prev->cache_key; } - if (conf->upstream.pass_headers) { +#endif - hk = hide_headers.elts; - header = conf->upstream.pass_headers->elts; + 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); - for (i = 0; i < conf->upstream.pass_headers->nelts; i++) { + ngx_conf_merge_value(conf->upstream.intercept_errors, + prev->upstream.intercept_errors, 0); - for (j = 0; j < hide_headers.nelts; j++) { + ngx_conf_merge_ptr_value(conf->catch_stderr, prev->catch_stderr, NULL); - if (hk[j].key.data == NULL) { - continue; - } - if (ngx_strcasecmp(header[i].data, hk[j].key.data) == 0) { - hk[j].key.data = NULL; - break; - } - } - } - } + ngx_conf_merge_str_value(conf->index, prev->index, ""); - hash.hash = &conf->upstream.hide_headers_hash; - hash.key = ngx_hash_key_lc; hash.max_size = 512; hash.bucket_size = ngx_align(64, ngx_cacheline_size); hash.name = "fastcgi_hide_headers_hash"; - hash.pool = cf->pool; - hash.temp_pool = NULL; - if (ngx_hash_init(&hash, hide_headers.elts, hide_headers.nelts) != NGX_OK) { + if (ngx_http_upstream_hide_headers_hash(cf, &conf->upstream, + &prev->upstream, ngx_http_fastcgi_hide_headers, &hash) + != NGX_OK) + { return NGX_CONF_ERROR; } -peers: - if (conf->upstream.upstream == NULL) { conf->upstream.upstream = prev->upstream.upstream; - conf->upstream.schema = prev->upstream.schema; } + if (conf->fastcgi_lengths == NULL) { + conf->fastcgi_lengths = prev->fastcgi_lengths; + conf->fastcgi_values = prev->fastcgi_values; + } + + if (conf->upstream.upstream || conf->fastcgi_lengths) { + clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); + if (clcf->handler == NULL && clcf->lmt_excpt) { + 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->flushes = prev->flushes; conf->params_len = prev->params_len; conf->params = prev->params; conf->params_source = prev->params_source; + conf->headers_hash = prev->headers_hash; + +#if (NGX_HTTP_CACHE) + + if (conf->params_source == NULL) { + + if ((conf->upstream.cache == NULL) + == (prev->upstream.cache == NULL)) + { + return NGX_CONF_OK; + } + + /* 6 is a number of ngx_http_fastcgi_cache_headers entries */ + conf->params_source = ngx_array_create(cf->pool, 6, + sizeof(ngx_keyval_t)); + if (conf->params_source == NULL) { + return NGX_CONF_ERROR; + } + } +#else if (conf->params_source == NULL) { return NGX_CONF_OK; } + +#endif } conf->params_len = ngx_array_create(cf->pool, 64, 1); @@ -1983,89 +2328,100 @@ return NGX_CONF_ERROR; } + if (ngx_array_init(&headers_names, cf->temp_pool, 4, sizeof(ngx_hash_key_t)) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + src = conf->params_source->elts; - for (i = 0; i < conf->params_source->nelts; i++) { - if (ngx_http_script_variables_count(&src[i].value) == 0) { - copy = ngx_array_push_n(conf->params_len, - sizeof(ngx_http_script_copy_code_t)); - if (copy == NULL) { - return NGX_CONF_ERROR; - } +#if (NGX_HTTP_CACHE) - copy->code = (ngx_http_script_code_pt) - ngx_http_script_copy_len_code; - copy->len = src[i].key.len; + if (conf->upstream.cache) { + ngx_keyval_t *h, *s; + for (h = ngx_http_fastcgi_cache_headers; h->key.len; h++) { - copy = ngx_array_push_n(conf->params_len, - sizeof(ngx_http_script_copy_code_t)); - if (copy == NULL) { - return NGX_CONF_ERROR; + for (i = 0; i < conf->params_source->nelts; i++) { + if (ngx_strcasecmp(h->key.data, src[i].key.data) == 0) { + goto next; + } } - copy->code = (ngx_http_script_code_pt) - ngx_http_script_copy_len_code; - copy->len = src[i].value.len; + s = ngx_array_push(conf->params_source); + if (s == NULL) { + return NGX_CONF_ERROR; + } + *s = *h; - size = (sizeof(ngx_http_script_copy_code_t) - + src[i].key.len + src[i].value.len - + sizeof(uintptr_t) - 1) - & ~(sizeof(uintptr_t) - 1); + src = conf->params_source->elts; - copy = ngx_array_push_n(conf->params, size); - if (copy == NULL) { - return NGX_CONF_ERROR; - } + next: - copy->code = ngx_http_script_copy_code; - copy->len = src[i].key.len + src[i].value.len; + h++; + } + } - p = (u_char *) copy + sizeof(ngx_http_script_copy_code_t); +#endif - p = ngx_cpymem(p, src[i].key.data, src[i].key.len); - ngx_memcpy(p, src[i].value.data, src[i].value.len); + for (i = 0; i < conf->params_source->nelts; i++) { - } else { - copy = ngx_array_push_n(conf->params_len, - sizeof(ngx_http_script_copy_code_t)); - if (copy == NULL) { + 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_CONF_ERROR; } - copy->code = (ngx_http_script_code_pt) - ngx_http_script_copy_len_code; - copy->len = src[i].key.len; + 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(conf->params_len, + sizeof(ngx_http_script_copy_code_t)); + if (copy == NULL) { + return NGX_CONF_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(conf->params, size); - if (copy == NULL) { - return NGX_CONF_ERROR; - } + 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(conf->params, size); + if (copy == NULL) { + return NGX_CONF_ERROR; + } - copy->code = ngx_http_script_copy_code; - copy->len = src[i].key.len; + 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); + 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)); + ngx_memzero(&sc, sizeof(ngx_http_script_compile_t)); - sc.cf = cf; - sc.source = &src[i].value; - sc.flushes = &conf->flushes; - sc.lengths = &conf->params_len; - sc.values = &conf->params; + sc.cf = cf; + sc.source = &src[i].value; + sc.flushes = &conf->flushes; + sc.lengths = &conf->params_len; + sc.values = &conf->params; - if (ngx_http_script_compile(&sc) != NGX_OK) { - return NGX_CONF_ERROR; - } + if (ngx_http_script_compile(&sc) != NGX_OK) { + return NGX_CONF_ERROR; } code = ngx_array_push_n(conf->params_len, sizeof(uintptr_t)); @@ -2091,6 +2447,22 @@ *code = (uintptr_t) NULL; + + conf->header_params = headers_names.nelts; + + hash.hash = &conf->headers_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; + + if (ngx_hash_init(&hash, headers_names.elts, headers_names.nelts) != NGX_OK) + { + return NGX_CONF_ERROR; + } + return NGX_CONF_OK; } @@ -2100,84 +2472,244 @@ 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; - if (r->uri.len) { + 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_cachable = 0; + v->no_cacheable = 0; v->not_found = 0; + v->data = f->script_name.data; - flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module); + return NGX_OK; + } - if (r->uri.data[r->uri.len - 1] != '/') { - v->len = r->uri.len; - v->data = r->uri.data; - return NGX_OK; - } + v->len = f->script_name.len + flcf->index.len; - v->len = r->uri.len + flcf->index.len; + v->data = ngx_pnalloc(r->pool, v->len); + if (v->data == NULL) { + return NGX_ERROR; + } - v->data = ngx_palloc(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); - p = ngx_copy(v->data, r->uri.data, r->uri.len); - ngx_memcpy(p, flcf->index.data, flcf->index.len); + return NGX_OK; +} - } else { - v->len = 0; - v->valid = 1; - v->no_cachable = 0; - v->not_found = 0; - v->data = NULL; - 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 *lcf = conf; + ngx_http_fastcgi_loc_conf_t *flcf = conf; - ngx_url_t u; - ngx_str_t *value; - ngx_http_core_loc_conf_t *clcf; + 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 (lcf->upstream.schema.len) { + 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; - lcf->upstream.upstream = ngx_http_upstream_add(cf, &u, 0); - if (lcf->upstream.upstream == NULL) { + flcf->upstream.upstream = ngx_http_upstream_add(cf, &u, 0); + if (flcf->upstream.upstream == NULL) { return NGX_CONF_ERROR; } - lcf->upstream.schema.len = sizeof("fastcgi://") - 1; - lcf->upstream.schema.data = (u_char *) "fastcgi://"; + return NGX_CONF_OK; +} - clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); - clcf->handler = ngx_http_fastcgi_handler; +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; - lcf->upstream.location = clcf->name; + ngx_str_t *value; + ngx_regex_compile_t rc; + u_char errstr[NGX_MAX_CONF_ERRSTR]; - if (clcf->name.data[clcf->name.len - 1] == '/') { - clcf->auto_redirect = 1; + 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 } @@ -2189,20 +2721,31 @@ ngx_str_t *value; ngx_http_script_compile_t sc; - if (flcf->upstream.store != NGX_CONF_UNSET || flcf->upstream.store_lengths) + if (flcf->upstream.store != NGX_CONF_UNSET + || flcf->upstream.store_lengths) { return "is duplicate"; } value = cf->args->elts; - if (ngx_strcmp(value[1].data, "on") == 0) { - flcf->upstream.store = 1; + if (ngx_strcmp(value[1].data, "off") == 0) { + flcf->upstream.store = 0; return NGX_CONF_OK; } - if (ngx_strcmp(value[1].data, "off") == 0) { - flcf->upstream.store = 0; +#if (NGX_HTTP_CACHE) + + if (flcf->upstream.cache != NGX_CONF_UNSET_PTR + && flcf->upstream.cache != NULL) + { + return "is incompatible with \"fastcgi_cache\""; + } + +#endif + + if (ngx_strcmp(value[1].data, "on") == 0) { + flcf->upstream.store = 1; return NGX_CONF_OK; } @@ -2215,7 +2758,7 @@ 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.variables = ngx_http_script_variables_count(&value[1]); sc.complete_lengths = 1; sc.complete_values = 1; @@ -2227,6 +2770,70 @@ } +#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; + + value = cf->args->elts; + + if (flcf->upstream.cache != NGX_CONF_UNSET_PTR) { + return "is duplicate"; + } + + if (ngx_strcmp(value[1].data, "off") == 0) { + flcf->upstream.cache = NULL; + return NGX_CONF_OK; + } + + if (flcf->upstream.store > 0 || flcf->upstream.store_lengths) { + return "is incompatible with \"fastcgi_store\""; + } + + flcf->upstream.cache = ngx_shared_memory_add(cf, &value[1], 0, + &ngx_http_fastcgi_module); + if (flcf->upstream.cache == 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.len) { + 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) { @@ -2254,29 +2861,3 @@ return NGX_CONF_OK; } - - -static char * -ngx_http_fastcgi_upstream_max_fails_unsupported(ngx_conf_t *cf, - ngx_command_t *cmd, void *conf) -{ - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "\"fastcgi_upstream_max_fails\" is not supported, " - "use the \"max_fails\" parameter of the \"server\" directive ", - "inside the \"upstream\" block"); - - return NGX_CONF_ERROR; -} - - -static char * -ngx_http_fastcgi_upstream_fail_timeout_unsupported(ngx_conf_t *cf, - ngx_command_t *cmd, void *conf) -{ - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "\"fastcgi_upstream_fail_timeout\" is not supported, " - "use the \"fail_timeout\" parameter of the \"server\" directive ", - "inside the \"upstream\" block"); - - return NGX_CONF_ERROR; -} diff -Nru nginx-0.5.33/src/http/modules/ngx_http_flv_module.c nginx-0.8.53/src/http/modules/ngx_http_flv_module.c --- nginx-0.5.33/src/http/modules/ngx_http_flv_module.c 2007-10-29 15:10:31.000000000 +0000 +++ nginx-0.8.53/src/http/modules/ngx_http_flv_module.c 2010-05-24 12:35:10.000000000 +0000 @@ -60,20 +60,16 @@ static ngx_int_t ngx_http_flv_handler(ngx_http_request_t *r) { - u_char *p; + u_char *last; off_t start, len; size_t root; - ngx_fd_t fd; ngx_int_t rc; ngx_uint_t level, i; - ngx_str_t path; - ngx_err_t err; + ngx_str_t path, value; ngx_log_t *log; ngx_buf_t *b; ngx_chain_t out[2]; - ngx_file_info_t fi; - ngx_pool_cleanup_t *cln; - ngx_pool_cleanup_file_t *clnf; + ngx_open_file_info_t of; ngx_http_core_loc_conf_t *clcf; if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) { @@ -84,77 +80,75 @@ return NGX_DECLINED; } - /* TODO: Win32 */ - if (r->zero_in_uri) { - return NGX_DECLINED; - } + rc = ngx_http_discard_request_body(r); - rc = ngx_http_discard_body(r); - - if (rc != NGX_OK && rc != NGX_AGAIN) { + if (rc != NGX_OK) { return rc; } - if (ngx_http_map_uri_to_path(r, &path, &root, 0) == NULL) { + 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: \"%s\"", path.data); + "http flv filename: \"%V\"", &path); - cln = ngx_pool_cleanup_add(r->pool, sizeof(ngx_pool_cleanup_file_t)); - if (cln == NULL) { - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + ngx_memzero(&of, sizeof(ngx_open_file_info_t)); - fd = ngx_open_file(path.data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0); + 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 (fd == NGX_INVALID_FILE) { - err = ngx_errno; + 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: - if (err == NGX_ENOENT - || err == NGX_ENOTDIR - || err == NGX_ENAMETOOLONG) - { level = NGX_LOG_ERR; rc = NGX_HTTP_NOT_FOUND; + break; + + case NGX_EACCES: - } else if (err == NGX_EACCES) { level = NGX_LOG_ERR; rc = NGX_HTTP_FORBIDDEN; + break; + + default: - } else { level = NGX_LOG_CRIT; rc = NGX_HTTP_INTERNAL_SERVER_ERROR; + break; } - clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); - if (rc != NGX_HTTP_NOT_FOUND || clcf->log_not_found) { - ngx_log_error(level, log, err, - ngx_open_file_n " \"%s\" failed", path.data); + ngx_log_error(level, log, of.err, + "%s \"%s\" failed", of.failed, path.data); } return rc; } - if (ngx_fd_info(fd, &fi) == NGX_FILE_ERROR) { - ngx_log_error(NGX_LOG_CRIT, log, ngx_errno, - ngx_fd_info_n " \"%s\" failed", path.data); + if (!of.is_file) { - if (ngx_close_file(fd) == NGX_FILE_ERROR) { - ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, - ngx_close_file_n " \"%s\" failed", path.data); - } - - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } - - if (!ngx_is_file(&fi)) { - - if (ngx_close_file(fd) == NGX_FILE_ERROR) { + 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); } @@ -162,17 +156,17 @@ return NGX_DECLINED; } + r->root_tested = !r->error_page; + start = 0; - len = ngx_file_size(&fi); + len = of.size; i = 1; if (r->args.len) { - p = (u_char *) ngx_strnstr(r->args.data, "start=", r->args.len); - if (p) { - p += 6; + if (ngx_http_arg(r, (u_char *) "start", 5, &value) == NGX_OK) { - start = ngx_atoof(p, r->args.len - (p - r->args.data)); + start = ngx_atoof(value.data, value.len); if (start == NGX_ERROR || start >= len) { start = 0; @@ -187,16 +181,9 @@ log->action = "sending flv to client"; - cln->handler = ngx_pool_cleanup_file; - clnf = cln->data; - - clnf->fd = fd; - clnf->name = path.data; - clnf->log = r->pool->log; - r->headers_out.status = NGX_HTTP_OK; r->headers_out.content_length_n = len; - r->headers_out.last_modified_time = ngx_file_mtime(&fi); + r->headers_out.last_modified_time = of.mtime; if (ngx_http_set_content_type(r) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; @@ -214,9 +201,6 @@ out[0].buf = b; out[0].next = &out[1]; - - } else { - r->allow_ranges = 1; } @@ -230,6 +214,8 @@ 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) { @@ -237,15 +223,16 @@ } b->file_pos = start; - b->file_last = ngx_file_size(&fi); + b->file_last = of.size; b->in_file = b->file_last ? 1: 0; b->last_buf = 1; b->last_in_chain = 1; - b->file->fd = fd; + 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; diff -Nru nginx-0.5.33/src/http/modules/ngx_http_geoip_module.c nginx-0.8.53/src/http/modules/ngx_http_geoip_module.c --- nginx-0.5.33/src/http/modules/ngx_http_geoip_module.c 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/src/http/modules/ngx_http_geoip_module.c 2010-08-03 18:38:08.000000000 +0000 @@ -0,0 +1,516 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include + +#include +#include + + +typedef struct { + GeoIP *country; + GeoIP *city; +} 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); + +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_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_country(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 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_TAKE1, + ngx_http_geoip_country, + NGX_HTTP_MAIN_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("geoip_city"), + NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1, + ngx_http_geoip_city, + NGX_HTTP_MAIN_CONF_OFFSET, + 0, + 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 */ + 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_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, + (uintptr_t) GeoIP_country_code_by_ipnum, 0, 0 }, + + { ngx_string("geoip_country_code3"), NULL, + ngx_http_geoip_country_variable, + (uintptr_t) GeoIP_country_code3_by_ipnum, 0, 0 }, + + { ngx_string("geoip_country_name"), NULL, + ngx_http_geoip_country_variable, + (uintptr_t) GeoIP_country_name_by_ipnum, 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_null_string, NULL, NULL, 0, 0, 0 } +}; + + +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_variable_handler_pt) data; + + u_long addr; + const char *val; + struct sockaddr_in *sin; + 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 (r->connection->sockaddr->sa_family != AF_INET) { + goto not_found; + } + + sin = (struct sockaddr_in *) r->connection->sockaddr; + addr = ntohl(sin->sin_addr.s_addr); + + val = handler(gcf->country, addr); + + 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_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; + + 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; + + GeoIPRecord_delete(gr); + + return NGX_OK; +} + + +static GeoIPRecord * +ngx_http_geoip_get_city_record(ngx_http_request_t *r) +{ + u_long addr; + struct sockaddr_in *sin; + ngx_http_geoip_conf_t *gcf; + + gcf = ngx_http_get_module_main_conf(r, ngx_http_geoip_module); + + if (gcf->city && r->connection->sockaddr->sa_family == AF_INET) { + + sin = (struct sockaddr_in *) r->connection->sockaddr; + addr = ntohl(sin->sin_addr.s_addr); + + return GeoIP_record_by_ipnum(gcf->city, addr); + } + + 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; + } + + 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_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, + "GeoIO_open(\"%V\") failed", &value[1]); + + return NGX_CONF_ERROR; + } + + switch (gcf->country->databaseType) { + + case GEOIP_COUNTRY_EDITION: + case GEOIP_PROXY_EDITION: + case GEOIP_NETSPEED_EDITION: + + return NGX_CONF_OK; + + 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_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, + "GeoIO_open(\"%V\") failed", &value[1]); + + return NGX_CONF_ERROR; + } + + switch (gcf->city->databaseType) { + + case GEOIP_CITY_EDITION_REV0: + case GEOIP_CITY_EDITION_REV1: + + return NGX_CONF_OK; + + 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 void +ngx_http_geoip_cleanup(void *data) +{ + ngx_http_geoip_conf_t *gcf = data; + + if (gcf->country) { + GeoIP_delete(gcf->country); + } + + if (gcf->city) { + GeoIP_delete(gcf->city); + } +} diff -Nru nginx-0.5.33/src/http/modules/ngx_http_geo_module.c nginx-0.8.53/src/http/modules/ngx_http_geo_module.c --- nginx-0.5.33/src/http/modules/ngx_http_geo_module.c 2007-09-22 19:18:36.000000000 +0000 +++ nginx-0.8.53/src/http/modules/ngx_http_geo_module.c 2010-06-30 14:28:54.000000000 +0000 @@ -10,20 +10,94 @@ typedef struct { - ngx_radix_tree_t *tree; - ngx_pool_t *pool; - ngx_array_t values; + ngx_http_variable_value_t *value; + u_short start; + u_short end; +} ngx_http_geo_range_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; + 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; } ngx_http_geo_conf_ctx_t; +typedef struct { + union { + ngx_radix_tree_t *tree; + ngx_http_geo_high_ranges_t high; + } u; + + ngx_array_t *proxies; + + ngx_int_t index; +} ngx_http_geo_ctx_t; + + +static in_addr_t ngx_http_geo_addr(ngx_http_request_t *r, + ngx_http_geo_ctx_t *ctx); +static in_addr_t ngx_http_geo_real_addr(ngx_http_request_t *r, + ngx_http_geo_ctx_t *ctx); 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 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_TAKE1, + NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE12, ngx_http_geo_block, NGX_HTTP_MAIN_CONF_OFFSET, 0, @@ -64,87 +138,219 @@ }; +typedef struct { + u_char GEORNG[6]; + u_char version; + u_char ptr_size; + uint32_t endianess; + 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 +}; + + /* AF_INET only */ static ngx_int_t -ngx_http_geo_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, +ngx_http_geo_cidr_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) { - ngx_radix_tree_t *tree = (ngx_radix_tree_t *) data; + ngx_http_geo_ctx_t *ctx = (ngx_http_geo_ctx_t *) data; - struct sockaddr_in *sin; ngx_http_variable_value_t *vv; - sin = (struct sockaddr_in *) r->connection->sockaddr; - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http geo started"); - vv = (ngx_http_variable_value_t *) - ngx_radix32tree_find(tree, ntohl(sin->sin_addr.s_addr)); + ngx_radix32tree_find(ctx->u.tree, ngx_http_geo_addr(r, ctx)); *v = *vv; - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http geo: %V %v", &r->connection->addr_text, v); + 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 addr; + ngx_uint_t n; + ngx_http_geo_range_t *range; + + *v = *ctx->u.high.default_value; + + addr = ngx_http_geo_addr(r, ctx); + + range = ctx->u.high.low[addr >> 16]; + + if (range) { + n = addr & 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 in_addr_t +ngx_http_geo_addr(ngx_http_request_t *r, ngx_http_geo_ctx_t *ctx) +{ + u_char *p, *ip; + size_t len; + in_addr_t addr; + ngx_uint_t i, n; + ngx_in_cidr_t *proxies; + ngx_table_elt_t *xfwd; + + addr = ngx_http_geo_real_addr(r, ctx); + + xfwd = r->headers_in.x_forwarded_for; + + if (xfwd == NULL || ctx->proxies == NULL) { + return addr; + } + + proxies = ctx->proxies->elts; + n = ctx->proxies->nelts; + + for (i = 0; i < n; i++) { + if ((addr & proxies[i].mask) == proxies[i].addr) { + + len = xfwd->value.len; + ip = xfwd->value.data; + + for (p = ip + len - 1; p > ip; p--) { + if (*p == ' ' || *p == ',') { + p++; + len -= p - ip; + ip = p; + break; + } + } + + return ntohl(ngx_inet_addr(ip, len)); + } + } + + return addr; +} + + +static in_addr_t +ngx_http_geo_real_addr(ngx_http_request_t *r, ngx_http_geo_ctx_t *ctx) +{ + struct sockaddr_in *sin; + 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); + + if (r->connection->sockaddr->sa_family != AF_INET) { + return 0; + } + + sin = (struct sockaddr_in *) r->connection->sockaddr; + return ntohl(sin->sin_addr.s_addr); + } + + 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 0; + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http geo started: %v", v); + + return ntohl(ngx_inet_addr(v->data, v->len)); +} + + static char * ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { char *rv; + void **p; + size_t len; ngx_str_t *value, name; + ngx_uint_t i; ngx_conf_t save; ngx_pool_t *pool; - ngx_radix_tree_t *tree; + ngx_array_t *a; ngx_http_variable_t *var; + ngx_http_geo_ctx_t *geo; ngx_http_geo_conf_ctx_t ctx; 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]; + name.len--; + name.data++; - if (name.data[0] != '$') { - ngx_conf_log_error(NGX_LOG_WARN, cf, 0, - "\"%V\" variable name should start with '$'", - &value[1]); - } else { + 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]; name.len--; name.data++; - } - var = ngx_http_add_variable(cf, &name, NGX_HTTP_VAR_CHANGABLE); - if (var == NULL) { - return NGX_CONF_ERROR; + } else { + geo->index = -1; } - tree = ngx_radix_tree_create(cf->pool, -1); - - if (tree == NULL) { + var = ngx_http_add_variable(cf, &name, NGX_HTTP_VAR_CHANGEABLE); + if (var == NULL) { return NGX_CONF_ERROR; } - var->get_handler = ngx_http_geo_variable; - var->data = (uintptr_t) tree; - pool = ngx_create_pool(16384, cf->log); if (pool == NULL) { return NGX_CONF_ERROR; } - if (ngx_array_init(&ctx.values, pool, 512, - sizeof(ngx_http_variable_value_t *)) - != NGX_OK) - { - ngx_destroy_pool(pool); + ngx_memzero(&ctx, sizeof(ngx_http_geo_conf_ctx_t)); + + ctx.temp_pool = ngx_create_pool(16384, cf->log); + if (ctx.temp_pool == NULL) { return NGX_CONF_ERROR; } - ctx.tree = tree; + 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; @@ -156,121 +362,585 @@ *cf = save; - ngx_destroy_pool(pool); + geo->proxies = ctx.proxies; - if (ngx_radix32tree_find(tree, 0) != NGX_RADIX_NO_VALUE) { - return rv; - } + if (ctx.high.low) { - if (ngx_radix32tree_insert(tree, 0, 0, - (uintptr_t) &ngx_http_variable_null_value) - == NGX_ERROR) - { - return NGX_CONF_ERROR; + if (!ctx.binary_include) { + for (i = 0; i < 0x10000; i++) { + a = (ngx_array_t *) ctx.high.low[i]; + + if (a == NULL || a->nelts == 0) { + 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) { + return NGX_CONF_ERROR; + } + + p = (void **) ngx_cpymem(ctx.high.low[i], a->elts, len); + *p = 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); + } + } + + geo->u.high = ctx.high; + + var->get_handler = ngx_http_geo_range_variable; + var->data = (uintptr_t) geo; + + if (ctx.high.default_value == NULL) { + ctx.high.default_value = &ngx_http_variable_null_value; + } + + ngx_destroy_pool(ctx.temp_pool); + ngx_destroy_pool(pool); + + } else { + if (ctx.tree == NULL) { + ctx.tree = ngx_radix_tree_create(cf->pool, -1); + if (ctx.tree == NULL) { + return NGX_CONF_ERROR; + } + } + + geo->u.tree = ctx.tree; + + var->get_handler = ngx_http_geo_cidr_variable; + var->data = (uintptr_t) geo; + + ngx_destroy_pool(ctx.temp_pool); + ngx_destroy_pool(pool); + + if (ngx_radix32tree_find(ctx.tree, 0) != NGX_RADIX_NO_VALUE) { + return rv; + } + + if (ngx_radix32tree_insert(ctx.tree, 0, 0, + (uintptr_t) &ngx_http_variable_null_value) + == NGX_ERROR) + { + return NGX_CONF_ERROR; + } } return rv; } -/* AF_INET only */ - static char * ngx_http_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf) { - ngx_int_t rc; - ngx_str_t *value, file; - ngx_uint_t i; - ngx_inet_cidr_t cidrin; - ngx_http_geo_conf_ctx_t *ctx; - ngx_http_variable_value_t *var, *old, **v; + 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) { + 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; + } + } + if (cf->args->nelts != 2) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid number of the geo parameters"); - return NGX_CONF_ERROR; + goto failed; } - value = cf->args->elts; - if (ngx_strcmp(value[0].data, "include") == 0) { - file = value[1]; - if (ngx_conf_full_name(cf->cycle, &file) == NGX_ERROR){ - return NGX_CONF_ERROR; + 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; } - ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data); + rv = ngx_http_geo_add_proxy(cf, ctx, &cidr); + + goto done; + } + + if (ctx->ranges) { + rv = ngx_http_geo_range(cf, ctx, value); - return ngx_conf_parse(cf, &file); + } 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) { - cidrin.addr = 0; - cidrin.mask = 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\" may not 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 { - rc = ngx_ptocidr(&value[0], &cidrin); + 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_memcpy(&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_memcpy(&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_memcpy(&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_memcpy(&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; - if (rc == NGX_ERROR) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "invalid parameter \"%V\"", &value[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; } - if (rc == NGX_DONE) { - ngx_conf_log_error(NGX_LOG_WARN, cf, 0, - "low address bits of %V are meaningless", - &value[0]); + /* add the first range */ + + range = ngx_array_push(a); + if (range == NULL) { + return NGX_CONF_ERROR; } - cidrin.addr = ntohl(cidrin.addr); - cidrin.mask = ntohl(cidrin.mask); + range->start = (u_short) s; + range->end = (u_short) e; + range->value = ctx->value; + + next: + + continue; } - var = NULL; - v = ctx->values.elts; + 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 += 0x10000) { + + h = n >> 16; + + if (n == start) { + s = n & 0xffff; + } else { + s = 0; + } + + if ((n | 0xffff) > end) { + e = end & 0xffff; - for (i = 0; i < ctx->values.nelts; i++) { - if ((size_t) v[i]->len != value[1].len) { + } else { + e = 0xffff; + } + + a = (ngx_array_t *) ctx->high.low[h]; + + if (a == NULL) { + warn = 1; continue; } - if (ngx_strncmp(value[1].data, v[i]->data, value[1].len) == 0) { - var = v[i]; - break; + 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_memcpy(&range[i], &range[i + 1], + (a->nelts - 1 - i) * sizeof(ngx_http_geo_range_t)); + + a->nelts--; + + break; + } + + if (s != (ngx_uint_t) range[i].start + && e != (ngx_uint_t) range[i].end) + { + continue; + } + + warn = 1; } } - if (var == NULL) { - var = ngx_palloc(ctx->pool, sizeof(ngx_http_variable_value_t)); - if (var == NULL) { + return warn; +} + + +static char * +ngx_http_geo_cidr(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx, + ngx_str_t *value) +{ + ngx_int_t rc, del; + ngx_str_t *net; + ngx_uint_t i; + ngx_cidr_t cidr; + ngx_http_variable_value_t *val, *old; + + if (ctx->tree == NULL) { + ctx->tree = ngx_radix_tree_create(ctx->pool, -1); + if (ctx->tree == NULL) { return NGX_CONF_ERROR; } + } + + if (ngx_strcmp(value[0].data, "default") == 0) { + cidr.u.in.addr = 0; + cidr.u.in.mask = 0; + net = &value[0]; - var->len = value[1].len; - var->data = ngx_pstrdup(ctx->pool, &value[1]); - if (var->data == NULL) { + } else { + 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; } - var->valid = 1; - var->no_cachable = 0; - var->not_found = 0; + if (del) { + if (ngx_radix32tree_delete(ctx->tree, cidr.u.in.addr, + cidr.u.in.mask) + != NGX_OK) + { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "no network \"%V\" to delete", net); + } - v = ngx_array_push(&ctx->values); - if (v == NULL) { - return NGX_CONF_ERROR; + return NGX_CONF_OK; } + } + + val = ngx_http_geo_value(cf, ctx, &value[1]); - *v = var; + if (val == NULL) { + return NGX_CONF_ERROR; } for (i = 2; i; i--) { - rc = ngx_radix32tree_insert(ctx->tree, cidrin.addr, cidrin.mask, - (uintptr_t) var); + 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; } @@ -281,19 +951,466 @@ /* rc == NGX_BUSY */ - old = (ngx_http_variable_value_t *) - ngx_radix32tree_find(ctx->tree, cidrin.addr & cidrin.mask); + old = (ngx_http_variable_value_t *) + ngx_radix32tree_find(ctx->tree, cidr.u.in.addr & cidr.u.in.mask); ngx_conf_log_error(NGX_LOG_WARN, cf, 0, - "duplicate parameter \"%V\", value: \"%v\", old value: \"%v\"", - &value[0], var, old); + "duplicate network \"%V\", value: \"%v\", old value: \"%v\"", + net, val, old); - rc = ngx_radix32tree_delete(ctx->tree, cidrin.addr, cidrin.mask); + 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; } } 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_in_cidr_t *c; + + if (ctx->proxies == NULL) { + ctx->proxies = ngx_array_create(ctx->pool, 4, sizeof(ngx_in_cidr_t)); + if (ctx->proxies == NULL) { + return NGX_CONF_ERROR; + } + } + + c = ngx_array_push(ctx->proxies); + if (c == NULL) { + return NGX_CONF_ERROR; + } + + c->addr = cidr->u.in.addr; + c->mask = cidr->u.in.mask; + + 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->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 (cidr->family != AF_INET) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "\"geo\" supports IPv4 only"); + return NGX_ERROR; + } + + if (rc == NGX_DONE) { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "low address bits of %V are meaningless", net); + } + + cidr->u.in.addr = ntohl(cidr->u.in.addr); + cidr->u.in.mask = ntohl(cidr->u.in.mask); + + 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, 0, 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\" may not 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\" may not 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); +} diff -Nru nginx-0.5.33/src/http/modules/ngx_http_gzip_filter_module.c nginx-0.8.53/src/http/modules/ngx_http_gzip_filter_module.c --- nginx-0.5.33/src/http/modules/ngx_http_gzip_filter_module.c 2007-10-29 14:52:51.000000000 +0000 +++ nginx-0.8.53/src/http/modules/ngx_http_gzip_filter_module.c 2010-10-04 15:03:00.000000000 +0000 @@ -15,29 +15,18 @@ ngx_flag_t enable; ngx_flag_t no_buffer; - ngx_array_t *types; /* array of ngx_str_t */ + ngx_hash_t types; ngx_bufs_t bufs; - ngx_uint_t http_version; - ngx_uint_t proxied; - + size_t postpone_gzipping; ngx_int_t level; size_t wbits; size_t memlevel; ssize_t min_length; -} ngx_http_gzip_conf_t; - -#define NGX_HTTP_GZIP_PROXIED_OFF 0x0002 -#define NGX_HTTP_GZIP_PROXIED_EXPIRED 0x0004 -#define NGX_HTTP_GZIP_PROXIED_NO_CACHE 0x0008 -#define NGX_HTTP_GZIP_PROXIED_NO_STORE 0x0010 -#define NGX_HTTP_GZIP_PROXIED_PRIVATE 0x0020 -#define NGX_HTTP_GZIP_PROXIED_NO_LM 0x0040 -#define NGX_HTTP_GZIP_PROXIED_NO_ETAG 0x0080 -#define NGX_HTTP_GZIP_PROXIED_AUTH 0x0100 -#define NGX_HTTP_GZIP_PROXIED_ANY 0x0200 + ngx_array_t *types_keys; +} ngx_http_gzip_conf_t; typedef struct { @@ -46,19 +35,27 @@ 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; - off_t length; - 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; size_t zin; size_t zout; @@ -69,12 +66,45 @@ } ngx_http_gzip_ctx_t; -static ngx_int_t ngx_http_gzip_proxied(ngx_http_request_t *r, - ngx_http_gzip_conf_t *conf); +#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_error(ngx_http_gzip_ctx_t *ctx); +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, @@ -84,8 +114,6 @@ 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_types(ngx_conf_t *cf, ngx_command_t *cmd, - void *conf); 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); @@ -98,27 +126,6 @@ static ngx_conf_post_handler_pt ngx_http_gzip_hash_p = ngx_http_gzip_hash; -static ngx_conf_enum_t ngx_http_gzip_http_version[] = { - { ngx_string("1.0"), NGX_HTTP_VERSION_10 }, - { ngx_string("1.1"), NGX_HTTP_VERSION_11 }, - { ngx_null_string, 0 } -}; - - -static ngx_conf_bitmask_t ngx_http_gzip_proxied_mask[] = { - { ngx_string("off"), NGX_HTTP_GZIP_PROXIED_OFF }, - { ngx_string("expired"), NGX_HTTP_GZIP_PROXIED_EXPIRED }, - { ngx_string("no-cache"), NGX_HTTP_GZIP_PROXIED_NO_CACHE }, - { ngx_string("no-store"), NGX_HTTP_GZIP_PROXIED_NO_STORE }, - { ngx_string("private"), NGX_HTTP_GZIP_PROXIED_PRIVATE }, - { ngx_string("no_last_modified"), NGX_HTTP_GZIP_PROXIED_NO_LM }, - { ngx_string("no_etag"), NGX_HTTP_GZIP_PROXIED_NO_ETAG }, - { ngx_string("auth"), NGX_HTTP_GZIP_PROXIED_AUTH }, - { ngx_string("any"), NGX_HTTP_GZIP_PROXIED_ANY }, - { ngx_null_string, 0 } -}; - - static ngx_command_t ngx_http_gzip_filter_commands[] = { { ngx_string("gzip"), @@ -138,10 +145,10 @@ { ngx_string("gzip_types"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE, - ngx_http_gzip_types, + ngx_http_types_slot, NGX_HTTP_LOC_CONF_OFFSET, - 0, - NULL }, + 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, @@ -164,6 +171,13 @@ 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, @@ -171,20 +185,6 @@ offsetof(ngx_http_gzip_conf_t, no_buffer), NULL }, - { ngx_string("gzip_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_gzip_conf_t, http_version), - &ngx_http_gzip_http_version }, - - { ngx_string("gzip_proxied"), - 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_gzip_conf_t, proxied), - &ngx_http_gzip_proxied_mask }, - { 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, @@ -227,30 +227,7 @@ }; -static u_char gzheader[10] = { 0x1f, 0x8b, Z_DEFLATED, 0, 0, 0, 0, 0, 0, 3 }; - -#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 ngx_str_t ngx_http_gzip_ratio = ngx_string("gzip_ratio"); -static ngx_str_t ngx_http_gzip_no_cache = ngx_string("no-cache"); -static ngx_str_t ngx_http_gzip_no_store = ngx_string("no-store"); -static ngx_str_t ngx_http_gzip_private = ngx_string("private"); - static ngx_http_output_header_filter_pt ngx_http_next_header_filter; static ngx_http_output_body_filter_pt ngx_http_next_body_filter; @@ -259,8 +236,7 @@ static ngx_int_t ngx_http_gzip_header_filter(ngx_http_request_t *r) { - ngx_str_t *type; - ngx_uint_t i; + ngx_table_elt_t *h; ngx_http_gzip_ctx_t *ctx; ngx_http_gzip_conf_t *conf; @@ -270,59 +246,36 @@ || (r->headers_out.status != NGX_HTTP_OK && r->headers_out.status != NGX_HTTP_FORBIDDEN && r->headers_out.status != NGX_HTTP_NOT_FOUND) - || r->header_only - || r != r->main - || r->http_version < conf->http_version - || r->headers_out.content_type.len == 0 || (r->headers_out.content_encoding && r->headers_out.content_encoding->value.len) - || r->headers_in.accept_encoding == NULL || (r->headers_out.content_length_n != -1 && r->headers_out.content_length_n < conf->min_length) - || ngx_strcasestrn(r->headers_in.accept_encoding->value.data, - "gzip", 4 - 1) - == NULL - ) + || ngx_http_test_content_type(r, &conf->types) == NULL + || r->header_only) { return ngx_http_next_header_filter(r); } + r->gzip_vary = 1; - type = conf->types->elts; - for (i = 0; i < conf->types->nelts; i++) { - if (r->headers_out.content_type.len >= type[i].len - && ngx_strncasecmp(r->headers_out.content_type.data, - type[i].data, type[i].len) == 0) - { - goto found; - } - } - - return ngx_http_next_header_filter(r); - +#if (NGX_HTTP_DEGRADATION) + { + ngx_http_core_loc_conf_t *clcf; -found: + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); - if (r->headers_in.via) { - if (conf->proxied & NGX_HTTP_GZIP_PROXIED_OFF) { - return ngx_http_next_header_filter(r); - } + if (clcf->gzip_disable_degradation && ngx_http_degraded(r)) { + return ngx_http_next_header_filter(r); + } + } +#endif - if (!(conf->proxied & NGX_HTTP_GZIP_PROXIED_ANY) - && ngx_http_gzip_proxied(r, conf) == NGX_DECLINED) - { + if (!r->gzip_tested) { + if (ngx_http_gzip_ok(r) != NGX_OK) { return ngx_http_next_header_filter(r); } - } - - - /* - * if the URL (without the "http://" prefix) is longer than 253 bytes - * then MSIE 4.x can not handle the compressed stream - it waits too long, - * hangs up or crashes - */ - if (r->headers_in.msie4 && r->unparsed_uri.len > 200) { + } else if (!r->gzip_ok) { return ngx_http_next_header_filter(r); } @@ -333,21 +286,20 @@ 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); - r->headers_out.content_encoding = ngx_list_push(&r->headers_out.headers); - if (r->headers_out.content_encoding == NULL) { + h = ngx_list_push(&r->headers_out.headers); + if (h == NULL) { return NGX_ERROR; } - r->headers_out.content_encoding->hash = 1; - r->headers_out.content_encoding->key.len = sizeof("Content-Encoding") - 1; - r->headers_out.content_encoding->key.data = (u_char *) "Content-Encoding"; - r->headers_out.content_encoding->value.len = sizeof("gzip") - 1; - r->headers_out.content_encoding->value.data = (u_char *) "gzip"; - - ctx->length = r->headers_out.content_length_n; + 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; @@ -359,488 +311,645 @@ static ngx_int_t -ngx_http_gzip_proxied(ngx_http_request_t *r, ngx_http_gzip_conf_t *conf) +ngx_http_gzip_body_filter(ngx_http_request_t *r, ngx_chain_t *in) { - time_t date, expires; + int rc; + ngx_chain_t *cl; + ngx_http_gzip_ctx_t *ctx; - if (r->headers_in.authorization - && (conf->proxied & NGX_HTTP_GZIP_PROXIED_AUTH)) - { - return NGX_OK; + ctx = ngx_http_get_module_ctx(r, ngx_http_gzip_filter_module); + + if (ctx == NULL || ctx->done) { + return ngx_http_next_body_filter(r, in); } - if (r->headers_out.expires) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http gzip filter"); - if (!(conf->proxied & NGX_HTTP_GZIP_PROXIED_EXPIRED)) { - return NGX_DECLINED; - } + if (ctx->buffering) { - expires = ngx_http_parse_time(r->headers_out.expires->value.data, - r->headers_out.expires->value.len); - if (expires == NGX_ERROR) { - return NGX_DECLINED; - } + /* + * 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 (r->headers_out.date) { - date = ngx_http_parse_time(r->headers_out.date->value.data, - r->headers_out.date->value.len); - if (date == NGX_ERROR) { - return NGX_DECLINED; + 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 { - date = ngx_time(); + ctx->buffering = 0; } + } - if (expires < date) { - return NGX_OK; + if (ctx->preallocated == NULL) { + if (ngx_http_gzip_filter_deflate_start(r, ctx) != NGX_OK) { + goto failed; } + } - return NGX_DECLINED; + if (in) { + if (ngx_chain_add_copy(r->pool, &ctx->in, in) != NGX_OK) { + goto failed; + } } - if (r->headers_out.cache_control.elts) { + if (ctx->nomem) { + + /* flush busy buffers */ - if ((conf->proxied & NGX_HTTP_GZIP_PROXIED_NO_CACHE) - && ngx_http_parse_multi_header_lines(&r->headers_out.cache_control, - &ngx_http_gzip_no_cache, NULL) >= 0) - { - return NGX_OK; - } - - if ((conf->proxied & NGX_HTTP_GZIP_PROXIED_NO_STORE) - && ngx_http_parse_multi_header_lines(&r->headers_out.cache_control, - &ngx_http_gzip_no_store, NULL) >= 0) - { - return NGX_OK; - } - - if ((conf->proxied & NGX_HTTP_GZIP_PROXIED_PRIVATE) - && ngx_http_parse_multi_header_lines(&r->headers_out.cache_control, - &ngx_http_gzip_private, NULL) >= 0) - { - return NGX_OK; + if (ngx_http_next_body_filter(r, NULL) == NGX_ERROR) { + goto failed; } - return NGX_DECLINED; + cl = NULL; + + ngx_chain_update_chains(&ctx->free, &ctx->busy, &cl, + (ngx_buf_tag_t) &ngx_http_gzip_filter_module); + ctx->nomem = 0; } - if ((conf->proxied & NGX_HTTP_GZIP_PROXIED_NO_LM) - && r->headers_out.last_modified) - { - return NGX_DECLINED; + 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) { + 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(&ctx->free, &ctx->busy, &ctx->out, + (ngx_buf_tag_t) &ngx_http_gzip_filter_module); + ctx->last_out = &ctx->out; + + ctx->nomem = 0; + + if (ctx->done) { + return rc; + } } - if ((conf->proxied & NGX_HTTP_GZIP_PROXIED_NO_ETAG) - && r->headers_out.etag) - { - return NGX_DECLINED; + /* unreachable */ + +failed: + + ctx->done = 1; + + if (ctx->preallocated) { + deflateEnd(&ctx->zstream); + + ngx_pfree(r->pool, ctx->preallocated); } - return NGX_OK; + ngx_http_gzip_filter_free_copy_buf(r, ctx); + + return NGX_ERROR; } -static ngx_int_t -ngx_http_gzip_body_filter(ngx_http_request_t *r, ngx_chain_t *in) +static void +ngx_http_gzip_filter_memory(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx) { - int rc, wbits, memlevel; - ngx_int_t last; - struct gztrailer *trailer; - ngx_buf_t *b; - ngx_chain_t *cl, out; - ngx_http_gzip_ctx_t *ctx; + int wbits, memlevel; ngx_http_gzip_conf_t *conf; - ctx = ngx_http_get_module_ctx(r, ngx_http_gzip_filter_module); + conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module); - if (ctx == NULL || ctx->done) { - return ngx_http_next_body_filter(r, in); + 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--; + } } - conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module); + ctx->wbits = wbits; + ctx->memlevel = memlevel; - if (ctx->preallocated == NULL) { - wbits = conf->wbits; - memlevel = conf->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 (ctx->length > 0) { + ctx->allocated = 8192 + (1 << (wbits + 2)) + (1 << (memlevel + 9)); +} - /* the actual zlib window size is smaller by 262 bytes */ - while (ctx->length < ((1 << (wbits - 1)) - 262)) { - wbits--; - memlevel--; - } - } +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; - /* - * 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() and so on). - * Besides we free this memory as soon as the 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 - */ + r = ctx->request; - ctx->allocated = 8192 + (1 << (wbits + 2)) + (1 << (memlevel + 9)); + r->connection->buffered |= NGX_HTTP_GZIP_BUFFERED; - ctx->preallocated = ngx_palloc(r->pool, ctx->allocated); - if (ctx->preallocated == NULL) { - return NGX_ERROR; - } + buffered = 0; + ll = &ctx->in; - ctx->free_mem = ctx->preallocated; + 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); - 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, - -wbits, memlevel, Z_DEFAULT_STRATEGY); - - if (rc != Z_OK) { - ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, - "deflateInit2() failed: %d", rc); - ngx_http_gzip_error(ctx); + while (in) { + cl = ngx_alloc_chain_link(r->pool); + if (cl == NULL) { return NGX_ERROR; } - b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t)); - if (b == NULL) { - ngx_http_gzip_error(ctx); - 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; } - b->memory = 1; - b->pos = gzheader; - b->last = b->pos + 10; + if (ctx->buffering && size) { - out.buf = b; - out.next = NULL; + buf = ngx_create_temp_buf(r->pool, size); + if (buf == NULL) { + return NGX_ERROR; + } - /* - * We pass the gzheader to the next filter now to avoid its linking - * to the ctx->busy chain. zlib does not usually output the compressed - * data in the initial iterations, so the gzheader that was linked - * to the ctx->busy chain would be flushed by ngx_http_write_filter(). - */ + buf->last = ngx_cpymem(buf->pos, b->pos, size); + b->pos = b->last; - if (ngx_http_next_body_filter(r, &out) == NGX_ERROR) { - ngx_http_gzip_error(ctx); - return NGX_ERROR; + buf->last_buf = b->last_buf; + buf->tag = (ngx_buf_tag_t) &ngx_http_gzip_filter_module; + + cl->buf = buf; + + } else { + cl->buf = b; } - r->connection->buffered |= NGX_HTTP_GZIP_BUFFERED; + *ll = cl; + ll = &cl->next; + in = in->next; + } - ctx->last_out = &ctx->out; + *ll = NULL; - ctx->crc32 = crc32(0L, Z_NULL, 0); - ctx->flush = Z_NO_FLUSH; + 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; } - if (in) { - if (ngx_chain_add_copy(r->pool, &ctx->in, in) == NGX_ERROR) { - ngx_http_gzip_error(ctx); - 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; } - last = NGX_NONE; + ctx->last_out = &ctx->out; + ctx->crc32 = crc32(0L, Z_NULL, 0); + ctx->flush = Z_NO_FLUSH; - for ( ;; ) { + return NGX_OK; +} - for ( ;; ) { - /* does zlib need a new data ? */ +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 }; - if (ctx->zstream.avail_in == 0 - && ctx->flush == Z_NO_FLUSH - && !ctx->redo) - { - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "gzip in: %p", ctx->in); - - if (ctx->in == NULL) { - break; - } - - 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, - "gzip in_buf:%p ni:%p ai:%ud", - ctx->in_buf, - ctx->zstream.next_in, ctx->zstream.avail_in); - - /* STUB */ - if (ctx->in_buf->last < ctx->in_buf->pos) { - ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, - "zstream.avail_in is huge"); - ctx->done = 1; - return NGX_ERROR; - } - /**/ - - 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 == 0) { - if (ctx->flush == Z_NO_FLUSH) { - continue; - } - - } else { - ctx->crc32 = crc32(ctx->crc32, ctx->zstream.next_in, - ctx->zstream.avail_in); - } - } + b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t)); + if (b == NULL) { + return NGX_ERROR; + } + b->memory = 1; + b->pos = gzheader; + b->last = b->pos + 10; - /* is there a space for the gzipped data ? */ + cl = ngx_alloc_chain_link(r->pool); + if (cl == NULL) { + return NGX_ERROR; + } - if (ctx->zstream.avail_out == 0) { + cl->buf = b; + cl->next = ctx->out; + ctx->out = cl; - if (ctx->free) { - ctx->out_buf = ctx->free->buf; - ctx->free = ctx->free->next; - - } else if (ctx->bufs < conf->bufs.num) { - ctx->out_buf = ngx_create_temp_buf(r->pool, - conf->bufs.size); - if (ctx->out_buf == NULL) { - ngx_http_gzip_error(ctx); - return NGX_ERROR; - } - - ctx->out_buf->tag = (ngx_buf_tag_t) - &ngx_http_gzip_filter_module; - ctx->out_buf->recycled = 1; - ctx->bufs++; - - } else { - break; - } + ctx->gzheader = 1; - ctx->zstream.next_out = ctx->out_buf->pos; - ctx->zstream.avail_out = conf->bufs.size; - } + return NGX_OK; +} - 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) { - ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, - "deflate() failed: %d, %d", ctx->flush, rc); - ngx_http_gzip_error(ctx); - 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; - } - } +static ngx_int_t +ngx_http_gzip_filter_add_data(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx) +{ + if (ctx->zstream.avail_in || ctx->flush != Z_NO_FLUSH || ctx->redo) { + return NGX_OK; + } - ctx->out_buf->last = ctx->zstream.next_out; + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "gzip in: %p", ctx->in); - if (ctx->zstream.avail_out == 0) { + if (ctx->in == NULL) { + return NGX_DECLINED; + } - /* zlib wants to output some more gzipped data */ + if (ctx->copy_buf) { - cl = ngx_alloc_chain_link(r->pool); - if (cl == NULL) { - ngx_http_gzip_error(ctx); - return NGX_ERROR; - } + /* + * to avoid CPU cache trashing we do not free() just quit buf, + * but postpone free()ing after zlib compressing and data output + */ - cl->buf = ctx->out_buf; - cl->next = NULL; - *ctx->last_out = cl; - ctx->last_out = &cl->next; + ctx->copy_buf->next = ctx->copied; + ctx->copied = ctx->copy_buf; + ctx->copy_buf = NULL; + } - ctx->redo = 1; + ctx->in_buf = ctx->in->buf; - continue; - } + if (ctx->in_buf->tag == (ngx_buf_tag_t) &ngx_http_gzip_filter_module) { + ctx->copy_buf = ctx->in; + } - ctx->redo = 0; + ctx->in = ctx->in->next; - if (ctx->flush == Z_SYNC_FLUSH) { + ctx->zstream.next_in = ctx->in_buf->pos; + ctx->zstream.avail_in = ctx->in_buf->last - ctx->in_buf->pos; - ctx->zstream.avail_out = 0; - ctx->out_buf->flush = 1; - ctx->flush = Z_NO_FLUSH; - - cl = ngx_alloc_chain_link(r->pool); - if (cl == NULL) { - ngx_http_gzip_error(ctx); - return NGX_ERROR; - } - - cl->buf = ctx->out_buf; - cl->next = NULL; - *ctx->last_out = cl; - ctx->last_out = &cl->next; + 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); - break; - } + if (ctx->in_buf->last_buf) { + ctx->flush = Z_FINISH; - if (rc == Z_STREAM_END) { + } else if (ctx->in_buf->flush) { + ctx->flush = Z_SYNC_FLUSH; + } - ctx->zin = ctx->zstream.total_in; - ctx->zout = 10 + ctx->zstream.total_out + 8; + if (ctx->zstream.avail_in) { - rc = deflateEnd(&ctx->zstream); + ctx->crc32 = crc32(ctx->crc32, ctx->zstream.next_in, + ctx->zstream.avail_in); - if (rc != Z_OK) { - ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, - "deflateEnd() failed: %d", rc); - ngx_http_gzip_error(ctx); - return NGX_ERROR; - } - - ngx_pfree(r->pool, ctx->preallocated); - - cl = ngx_alloc_chain_link(r->pool); - if (cl == NULL) { - ngx_http_gzip_error(ctx); - 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) { - ngx_http_gzip_error(ctx); - return NGX_ERROR; - } - - b->last_buf = 1; - - cl = ngx_alloc_chain_link(r->pool); - if (cl == NULL) { - ngx_http_gzip_error(ctx); - 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; - } + } else if (ctx->flush == Z_NO_FLUSH) { + return NGX_AGAIN; + } -#if (NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED) + return NGX_OK; +} - 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 +static ngx_int_t +ngx_http_gzip_filter_get_buf(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx) +{ + ngx_http_gzip_conf_t *conf; + + if (ctx->zstream.avail_out) { + return NGX_OK; + } - ctx->zstream.avail_in = 0; - ctx->zstream.avail_out = 0; + conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module); - ctx->done = 1; + if (ctx->free) { + ctx->out_buf = ctx->free->buf; + ctx->free = ctx->free->next; - r->connection->buffered &= ~NGX_HTTP_GZIP_BUFFERED; + } else if (ctx->bufs < conf->bufs.num) { - break; - } + ctx->out_buf = ngx_create_temp_buf(r->pool, conf->bufs.size); + if (ctx->out_buf == NULL) { + return NGX_ERROR; + } - if (conf->no_buffer && ctx->in == NULL) { + ctx->out_buf->tag = (ngx_buf_tag_t) &ngx_http_gzip_filter_module; + ctx->out_buf->recycled = 1; + ctx->bufs++; - cl = ngx_alloc_chain_link(r->pool); - if (cl == NULL) { - ngx_http_gzip_error(ctx); - return NGX_ERROR; - } - - cl->buf = ctx->out_buf; - cl->next = NULL; - *ctx->last_out = cl; - ctx->last_out = &cl->next; + } else { + ctx->nomem = 1; + return NGX_DECLINED; + } - break; - } + 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_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) { + 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; } + } - if (last == NGX_AGAIN && !ctx->done) { - return NGX_AGAIN; + 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; } - if (ctx->out == NULL && ctx->busy == NULL) { - return NGX_OK; + 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->zstream.avail_out = 0; + ctx->out_buf->flush = 1; + ctx->flush = Z_NO_FLUSH; + + cl = ngx_alloc_chain_link(r->pool); + if (cl == NULL) { + return NGX_ERROR; } - last = ngx_http_next_body_filter(r, ctx->out); + cl->buf = ctx->out_buf; + cl->next = NULL; + *ctx->last_out = cl; + ctx->last_out = &cl->next; - /* - * we do not check NGX_AGAIN here because the downstream filters - * may free some buffers and zlib may compress some data into them - */ + return NGX_OK; + } - if (last == NGX_ERROR) { - ngx_http_gzip_error(ctx); + if (rc == Z_STREAM_END) { + + if (ngx_http_gzip_filter_deflate_end(r, ctx) != NGX_OK) { return NGX_ERROR; } - ngx_chain_update_chains(&ctx->free, &ctx->busy, &ctx->out, - (ngx_buf_tag_t) &ngx_http_gzip_filter_module); - ctx->last_out = &ctx->out; + return NGX_OK; + } - if (ctx->done) { - return last; + 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; } @@ -854,14 +963,14 @@ alloc = items * size; - if (alloc % 512 != 0) { + if (alloc % 512 != 0 && alloc < 8192) { /* * The zlib deflate_state allocation, it takes about 6K, * we allocate 8K. Other allocations are divisible by 512. */ - alloc = (alloc + ngx_pagesize - 1) & ~(ngx_pagesize - 1); + alloc = 8192; } if (alloc <= ctx->allocated) { @@ -899,20 +1008,16 @@ static void -ngx_http_gzip_error(ngx_http_gzip_ctx_t *ctx) +ngx_http_gzip_filter_free_copy_buf(ngx_http_request_t *r, + ngx_http_gzip_ctx_t *ctx) { - deflateEnd(&ctx->zstream); + ngx_chain_t *cl; - if (ctx->preallocated) { - ngx_pfree(ctx->request->pool, ctx->preallocated); + for (cl = ctx->copied; cl; cl = cl->next) { + ngx_pfree(r->pool, cl->buf->start); } - ctx->zstream.avail_in = 0; - ctx->zstream.avail_out = 0; - - ctx->done = 1; - - return; + ctx->copied = NULL; } @@ -940,7 +1045,7 @@ ngx_http_gzip_ctx_t *ctx; v->valid = 1; - v->no_cachable = 0; + v->no_cacheable = 0; v->not_found = 0; ctx = ngx_http_get_module_ctx(r, ngx_http_gzip_filter_module); @@ -950,7 +1055,7 @@ return NGX_OK; } - v->data = ngx_palloc(r->pool, NGX_INT32_LEN + 3); + v->data = ngx_pnalloc(r->pool, NGX_INT32_LEN + 3); if (v->data == NULL) { return NGX_ERROR; } @@ -983,25 +1088,24 @@ conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_gzip_conf_t)); if (conf == NULL) { - return NGX_CONF_ERROR; + return NULL; } /* * set by ngx_pcalloc(): * * conf->bufs.num = 0; - * conf->proxied = 0; - * conf->types = NULL; + * conf->types = { NULL }; + * conf->types_keys = NULL; */ conf->enable = NGX_CONF_UNSET; conf->no_buffer = NGX_CONF_UNSET; - conf->http_version = NGX_CONF_UNSET_UINT; - + conf->postpone_gzipping = NGX_CONF_UNSET_SIZE; conf->level = NGX_CONF_UNSET; - conf->wbits = (size_t) NGX_CONF_UNSET; - conf->memlevel = (size_t) NGX_CONF_UNSET; + conf->wbits = NGX_CONF_UNSET_SIZE; + conf->memlevel = NGX_CONF_UNSET_SIZE; conf->min_length = NGX_CONF_UNSET; return conf; @@ -1014,42 +1118,26 @@ ngx_http_gzip_conf_t *prev = parent; ngx_http_gzip_conf_t *conf = child; - ngx_str_t *type; - 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, 4, ngx_pagesize); - - ngx_conf_merge_uint_value(conf->http_version, prev->http_version, - NGX_HTTP_VERSION_11); - ngx_conf_merge_bitmask_value(conf->proxied, prev->proxied, - (NGX_CONF_BITMASK_SET|NGX_HTTP_GZIP_PROXIED_OFF)); + 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); - ngx_conf_merge_value(conf->no_buffer, prev->no_buffer, 0); - if (conf->types == NULL) { - if (prev->types == NULL) { - conf->types = ngx_array_create(cf->pool, 1, sizeof(ngx_str_t)); - if (conf->types == NULL) { - return NGX_CONF_ERROR; - } - - type = ngx_array_push(conf->types); - if (type == NULL) { - return NGX_CONF_ERROR; - } - - type->len = sizeof("text/html") - 1; - type->data = (u_char *) "text/html"; - - } else { - conf->types = prev->types; - } + 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; @@ -1070,61 +1158,11 @@ static char * -ngx_http_gzip_types(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) -{ - ngx_http_gzip_conf_t *gcf = conf; - - ngx_str_t *value, *type; - ngx_uint_t i; - - if (gcf->types == NULL) { - gcf->types = ngx_array_create(cf->pool, 4, sizeof(ngx_str_t)); - if (gcf->types == NULL) { - return NGX_CONF_ERROR; - } - - type = ngx_array_push(gcf->types); - if (type == NULL) { - return NGX_CONF_ERROR; - } - - type->len = sizeof("text/html") - 1; - type->data = (u_char *) "text/html"; - } - - value = cf->args->elts; - - for (i = 1; i < cf->args->nelts; i++) { - - if (ngx_strcmp(value[i].data, "text/html") == 0) { - continue; - } - - type = ngx_array_push(gcf->types); - if (type == NULL) { - return NGX_CONF_ERROR; - } - - type->len = value[i].len; - - type->data = ngx_palloc(cf->pool, type->len + 1); - if (type->data == NULL) { - return NGX_CONF_ERROR; - } - - ngx_cpystrn(type->data, value[i].data, type->len + 1); - } - - return NGX_CONF_OK; -} - - -static char * ngx_http_gzip_window(ngx_conf_t *cf, void *post, void *data) { - int *np = data; + size_t *np = data; - int wbits, wsize; + size_t wbits, wsize; wbits = 15; @@ -1146,9 +1184,9 @@ static char * ngx_http_gzip_hash(ngx_conf_t *cf, void *post, void *data) { - int *np = data; + size_t *np = data; - int memlevel, hsize; + size_t memlevel, hsize; memlevel = 9; diff -Nru nginx-0.5.33/src/http/modules/ngx_http_gzip_static_module.c nginx-0.8.53/src/http/modules/ngx_http_gzip_static_module.c --- nginx-0.5.33/src/http/modules/ngx_http_gzip_static_module.c 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/src/http/modules/ngx_http_gzip_static_module.c 2010-05-24 12:35:10.000000000 +0000 @@ -0,0 +1,298 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include + + +typedef struct { + ngx_flag_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_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_FLAG, + ngx_conf_set_flag_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_gzip_static_conf_t, enable), + NULL }, + + ngx_null_command +}; + + +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) { + return NGX_DECLINED; + } + + rc = ngx_http_gzip_ok(r); + + 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_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: + + 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; + } + + 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_content_type(r) != NGX_OK) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + 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->ignore_content_encoding = 1; + + /* we need to allocate all before the header would be sent */ + + b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t)); + 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 = 1; + 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; + + 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_value(conf->enable, prev->enable, 0); + + 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; +} diff -Nru nginx-0.5.33/src/http/modules/ngx_http_headers_filter_module.c nginx-0.8.53/src/http/modules/ngx_http_headers_filter_module.c --- nginx-0.5.33/src/http/modules/ngx_http_headers_filter_module.c 2007-09-22 18:54:28.000000000 +0000 +++ nginx-0.8.53/src/http/modules/ngx_http_headers_filter_module.c 2010-05-14 09:56:37.000000000 +0000 @@ -16,33 +16,36 @@ typedef struct { - ngx_str_t name; - ngx_uint_t offset; - ngx_http_set_header_pt handler; + 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_table_elt_t value; - ngx_uint_t offset; - ngx_http_set_header_pt handler; - ngx_array_t *lengths; - ngx_array_t *values; + ngx_http_complex_value_t value; + ngx_uint_t hash; + ngx_str_t key; + ngx_http_set_header_pt handler; + ngx_uint_t offset; }; +#define NGX_HTTP_EXPIRES_OFF 0 +#define NGX_HTTP_EXPIRES_EPOCH 1 +#define NGX_HTTP_EXPIRES_MAX 2 +#define NGX_HTTP_EXPIRES_ACCESS 3 +#define NGX_HTTP_EXPIRES_MODIFIED 4 +#define NGX_HTTP_EXPIRES_DAILY 5 + + typedef struct { - time_t expires; + ngx_uint_t expires; + time_t expires_time; ngx_array_t *headers; } ngx_http_headers_conf_t; -#define NGX_HTTP_EXPIRES_UNSET -2147483647 -#define NGX_HTTP_EXPIRES_OFF -2147483646 -#define NGX_HTTP_EXPIRES_EPOCH -2147483645 -#define NGX_HTTP_EXPIRES_MAX -2147483644 - - 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_add_cache_control(ngx_http_request_t *r, @@ -76,7 +79,7 @@ { ngx_string("expires"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF - |NGX_CONF_TAKE1, + |NGX_CONF_TAKE12, ngx_http_headers_expires, NGX_HTTP_LOC_CONF_OFFSET, 0, @@ -159,16 +162,8 @@ h = conf->headers->elts; for (i = 0; i < conf->headers->nelts; i++) { - if (h[i].lengths == NULL) { - value = h[i].value.value; - - } else { - if (ngx_http_script_run(r, &value, h[i].lengths->elts, 0, - h[i].values->elts) - == NULL) - { - return NGX_ERROR; - } + 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) { @@ -185,6 +180,7 @@ ngx_http_set_expires(ngx_http_request_t *r, ngx_http_headers_conf_t *conf) { size_t len; + time_t now, expires_time, max_age; ngx_uint_t i; ngx_table_elt_t *expires, *cc, **ccp; @@ -200,8 +196,7 @@ r->headers_out.expires = expires; expires->hash = 1; - expires->key.len = sizeof("Expires") - 1; - expires->key.data = (u_char *) "Expires"; + ngx_str_set(&expires->key, "Expires"); } len = sizeof("Mon, 28 Sep 1970 06:00:00 GMT"); @@ -229,9 +224,7 @@ } cc->hash = 1; - cc->key.len = sizeof("Cache-Control") - 1; - cc->key.data = (u_char *) "Cache-Control"; - + ngx_str_set(&cc->key, "Cache-Control"); *ccp = cc; } else { @@ -244,54 +237,60 @@ if (conf->expires == NGX_HTTP_EXPIRES_EPOCH) { expires->value.data = (u_char *) "Thu, 01 Jan 1970 00:00:01 GMT"; - - cc->value.len = sizeof("no-cache") - 1; - cc->value.data = (u_char *) "no-cache"; - + ngx_str_set(&cc->value, "no-cache"); return NGX_OK; } if (conf->expires == NGX_HTTP_EXPIRES_MAX) { expires->value.data = (u_char *) "Thu, 31 Dec 2037 23:55:55 GMT"; - /* 10 years */ - cc->value.len = sizeof("max-age=315360000") - 1; - cc->value.data = (u_char *) "max-age=315360000"; - + ngx_str_set(&cc->value, "max-age=315360000"); return NGX_OK; } - expires->value.data = ngx_palloc(r->pool, len); + expires->value.data = ngx_pnalloc(r->pool, len); if (expires->value.data == NULL) { return NGX_ERROR; } - if (conf->expires == 0) { + if (conf->expires_time == 0) { ngx_memcpy(expires->value.data, ngx_cached_http_time.data, ngx_cached_http_time.len + 1); - - cc->value.len = sizeof("max-age=0") - 1; - cc->value.data = (u_char *) "max-age=0"; - + ngx_str_set(&cc->value, "max-age=0"); return NGX_OK; } - ngx_http_time(expires->value.data, ngx_time() + conf->expires); + now = ngx_time(); - if (conf->expires < 0) { - cc->value.len = sizeof("no-cache") - 1; - cc->value.data = (u_char *) "no-cache"; + if (conf->expires == NGX_HTTP_EXPIRES_ACCESS + || r->headers_out.last_modified_time == -1) + { + expires_time = now + conf->expires_time; + max_age = conf->expires_time; + + } else if (conf->expires == NGX_HTTP_EXPIRES_DAILY) { + expires_time = ngx_next_time(conf->expires_time); + max_age = expires_time - now; + } else { + expires_time = r->headers_out.last_modified_time + conf->expires_time; + max_age = expires_time - now; + } + + ngx_http_time(expires->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_palloc(r->pool, - sizeof("max-age=") + NGX_TIME_T_LEN + 1); + 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", conf->expires) + cc->value.len = ngx_sprintf(cc->value.data, "max-age=%T", max_age) - cc->value.data; return NGX_OK; @@ -304,14 +303,16 @@ { ngx_table_elt_t *h; - h = ngx_list_push(&r->headers_out.headers); - if (h == NULL) { - return NGX_ERROR; - } + if (value->len) { + h = ngx_list_push(&r->headers_out.headers); + if (h == NULL) { + return NGX_ERROR; + } - h->hash = hv->value.hash; - h->key = hv->value.key; - h->value = *value; + h->hash = hv->hash; + h->key = hv->key; + h->value = *value; + } return NGX_OK; } @@ -346,8 +347,7 @@ } cc->hash = 1; - cc->key.len = sizeof("Cache-Control") - 1; - cc->key.data = (u_char *) "Cache-Control"; + ngx_str_set(&cc->key, "Cache-Control"); cc->value = *value; *ccp = cc; @@ -369,7 +369,14 @@ old = NULL; } + r->headers_out.last_modified_time = -1; + if (old == NULL || *old == NULL) { + + if (value->len == 0) { + return NGX_OK; + } + h = ngx_list_push(&r->headers_out.headers); if (h == NULL) { return NGX_ERROR; @@ -377,14 +384,17 @@ } else { h = *old; + + if (value->len == 0) { + h->hash = 0; + return NGX_OK; + } } - h->hash = hv->value.hash; - h->key = hv->value.key; + h->hash = hv->hash; + h->key = hv->key; h->value = *value; - r->headers_out.last_modified_time = -1; - return NGX_OK; } @@ -396,16 +406,17 @@ conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_headers_conf_t)); if (conf == NULL) { - return NGX_CONF_ERROR; + return NULL; } /* * set by ngx_pcalloc(): * * conf->headers = NULL; + * conf->expires_time = 0; */ - conf->expires = NGX_HTTP_EXPIRES_UNSET; + conf->expires = NGX_CONF_UNSET_UINT; return conf; } @@ -417,9 +428,13 @@ 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 == NGX_HTTP_EXPIRES_UNSET) ? - NGX_HTTP_EXPIRES_OFF : prev->expires; + if (conf->expires == NGX_CONF_UNSET_UINT) { + conf->expires = prev->expires; + conf->expires_time = prev->expires_time; + + if (conf->expires == NGX_CONF_UNSET_UINT) { + conf->expires = NGX_HTTP_EXPIRES_OFF; + } } if (conf->headers == NULL) { @@ -445,56 +460,90 @@ { ngx_http_headers_conf_t *hcf = conf; - ngx_uint_t minus; + ngx_uint_t minus, n; ngx_str_t *value; - if (hcf->expires != NGX_HTTP_EXPIRES_UNSET) { + if (hcf->expires != NGX_CONF_UNSET_UINT) { return "is duplicate"; } value = cf->args->elts; - if (ngx_strcmp(value[1].data, "epoch") == 0) { - hcf->expires = NGX_HTTP_EXPIRES_EPOCH; - return NGX_CONF_OK; - } + if (cf->args->nelts == 2) { - if (ngx_strcmp(value[1].data, "max") == 0) { - hcf->expires = NGX_HTTP_EXPIRES_MAX; - return NGX_CONF_OK; - } + if (ngx_strcmp(value[1].data, "epoch") == 0) { + hcf->expires = NGX_HTTP_EXPIRES_EPOCH; + return NGX_CONF_OK; + } - if (ngx_strcmp(value[1].data, "off") == 0) { - hcf->expires = NGX_HTTP_EXPIRES_OFF; - return NGX_CONF_OK; + if (ngx_strcmp(value[1].data, "max") == 0) { + hcf->expires = NGX_HTTP_EXPIRES_MAX; + return NGX_CONF_OK; + } + + if (ngx_strcmp(value[1].data, "off") == 0) { + hcf->expires = NGX_HTTP_EXPIRES_OFF; + return NGX_CONF_OK; + } + + 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; } - if (value[1].data[0] == '+') { - value[1].data++; - value[1].len--; + if (value[n].data[0] == '@') { + value[n].data++; + value[n].len--; + minus = 0; + + if (hcf->expires == NGX_HTTP_EXPIRES_MODIFIED) { + return "daily time can not be used with \"modified\" parameter"; + } + + hcf->expires = NGX_HTTP_EXPIRES_DAILY; + + } else if (value[n].data[0] == '+') { + value[n].data++; + value[n].len--; minus = 0; - } else if (value[1].data[0] == '-') { - value[1].data++; - value[1].len--; + } else if (value[n].data[0] == '-') { + value[n].data++; + value[n].len--; minus = 1; } else { minus = 0; } - hcf->expires = ngx_parse_time(&value[1], 1); + hcf->expires_time = ngx_parse_time(&value[n], 1); - if (hcf->expires == NGX_ERROR) { + if (hcf->expires_time == NGX_ERROR) { return "invalid value"; } - if (hcf->expires == NGX_PARSE_LARGE_TIME) { + if (hcf->expires == NGX_HTTP_EXPIRES_DAILY + && hcf->expires_time > 24 * 60 * 60) + { + return "daily time value must be less than 24 hours"; + } + + if (hcf->expires_time == NGX_PARSE_LARGE_TIME) { return "value must be less than 68 years"; } if (minus) { - hcf->expires = - hcf->expires; + hcf->expires_time = - hcf->expires_time; } return NGX_CONF_OK; @@ -506,12 +555,11 @@ { ngx_http_headers_conf_t *hcf = conf; - ngx_int_t n; - ngx_str_t *value; - ngx_uint_t i; - ngx_http_header_val_t *h; - ngx_http_set_header_t *sh; - ngx_http_script_compile_t sc; + ngx_str_t *value; + ngx_uint_t i; + ngx_http_header_val_t *hv; + ngx_http_set_header_t *set; + ngx_http_compile_complex_value_t ccv; value = cf->args->elts; @@ -523,47 +571,40 @@ } } - h = ngx_array_push(hcf->headers); - if (h == NULL) { + hv = ngx_array_push(hcf->headers); + if (hv == NULL) { return NGX_CONF_ERROR; } - h->value.hash = 1; - h->value.key = value[1]; - h->value.value = value[2]; - h->offset = 0; - h->handler = ngx_http_add_header; - h->lengths = NULL; - h->values = NULL; - - sh = ngx_http_set_headers; - for (i = 0; sh[i].name.len; i++) { - if (ngx_strcasecmp(value[1].data, sh[i].name.data) != 0) { + hv->hash = 1; + hv->key = value[1]; + hv->handler = ngx_http_add_header; + hv->offset = 0; + + 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; } - h->offset = sh[i].offset; - h->handler = sh[i].handler; + hv->offset = set[i].offset; + hv->handler = set[i].handler; + break; } - n = ngx_http_script_variables_count(&value[2]); - - if (n == 0) { + if (value[2].len == 0) { + ngx_memzero(&hv->value, sizeof(ngx_http_complex_value_t)); return NGX_CONF_OK; } - ngx_memzero(&sc, sizeof(ngx_http_script_compile_t)); + ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); - sc.cf = cf; - sc.source = &value[2]; - sc.lengths = &h->lengths; - sc.values = &h->values; - sc.variables = n; - sc.complete_lengths = 1; - sc.complete_values = 1; + ccv.cf = cf; + ccv.value = &value[2]; + ccv.complex_value = &hv->value; - if (ngx_http_script_compile(&sc) != NGX_OK) { + if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { return NGX_CONF_ERROR; } diff -Nru nginx-0.5.33/src/http/modules/ngx_http_image_filter_module.c nginx-0.8.53/src/http/modules/ngx_http_image_filter_module.c --- nginx-0.5.33/src/http/modules/ngx_http_image_filter_module.c 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/src/http/modules/ngx_http_image_filter_module.c 2010-08-06 15:55:05.000000000 +0000 @@ -0,0 +1,1302 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#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_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_BUFFERED 0x08 + + +typedef struct { + ngx_uint_t filter; + ngx_uint_t width; + ngx_uint_t height; + ngx_uint_t jpeg_quality; + + ngx_flag_t transparency; + + ngx_http_complex_value_t *wcv; + ngx_http_complex_value_t *hcv; + ngx_http_complex_value_t *jqcv; + + 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 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 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_TAKE13, + 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_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_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") +}; + + +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; + } + + 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; + size = (rest < size) ? rest : size; + + 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->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->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_pcalloc(r->pool, sizeof(ngx_buf_t)); + if (b == NULL) { + return NULL; + } + + b->memory = 1; + b->last_buf = 1; + + ngx_http_clean_header(r); + + r->headers_out.status = NGX_HTTP_OK; + ngx_str_set(&r->headers_out.content_type, "text/plain"); + 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_pcalloc(r->pool, sizeof(ngx_buf_t)); + 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; + + default: + + return NGX_DECLINED; + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "image size: %d x %d", width, 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, size, + colors, palette, transparent, + red, green, blue; + 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 + && (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 { /* NGX_HTTP_IMAGE_CROP */ + + resize = 0; + + if ((ngx_uint_t) (dx * 100 / dy) + < ctx->max_width * 100 / 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 (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)); + } + + 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_pcalloc(r->pool, sizeof(ngx_buf_t)); + 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); + + 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; + + 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 jq; + 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); + + jq = ngx_http_image_filter_get_value(r, conf->jqcv, conf->jpeg_quality); + if (jq <= 0) { + return NULL; + } + + out = gdImageJpegPtr(img, size, jq); + 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; + + 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; + } + + conf->filter = NGX_CONF_UNSET_UINT; + conf->jpeg_quality = NGX_CONF_UNSET_UINT; + conf->transparency = 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->wcv = prev->wcv; + conf->hcv = prev->hcv; + } + } + + /* 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; + } + + ngx_conf_merge_value(conf->transparency, prev->transparency, 1); + + 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; + } + + 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 parameter \"%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 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; +} diff -Nru nginx-0.5.33/src/http/modules/ngx_http_index_module.c nginx-0.8.53/src/http/modules/ngx_http_index_module.c --- nginx-0.5.33/src/http/modules/ngx_http_index_module.c 2007-01-18 20:15:09.000000000 +0000 +++ nginx-0.8.53/src/http/modules/ngx_http_index_module.c 2010-05-24 12:35:10.000000000 +0000 @@ -22,25 +22,13 @@ } ngx_http_index_loc_conf_t; -typedef struct { - ngx_uint_t current; - - ngx_str_t path; - ngx_str_t index; - - size_t root; - - ngx_uint_t tested; /* unsigned tested:1 */ -} ngx_http_index_ctx_t; - - #define NGX_HTTP_DEFAULT_INDEX "index.html" static ngx_int_t ngx_http_index_test_dir(ngx_http_request_t *r, - ngx_http_index_ctx_t *ctx); + 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_index_ctx_t *ctx, ngx_err_t err); + 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); @@ -59,17 +47,6 @@ 0, NULL }, -#if (NGX_HTTP_CACHE) - - { ngx_string("index_cache"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE3, - ngx_http_set_cache_slot, - NGX_HTTP_LOC_CONF_OFFSET, - offsetof(ngx_http_index_loc_conf_t, index_cache), - NULL }, - -#endif - ngx_null_command }; @@ -106,30 +83,25 @@ /* - * Try to open the first index file before the test of the directory existence - * because the valid requests should be many more than invalid ones. - * If open() would fail, then stat() should be more quickly because some data - * is already cached in the kernel. - * Besides, Win32 has ERROR_PATH_NOT_FOUND (NGX_ENOTDIR). - * Unix has ENOTDIR error, although it less helpfull - it points only - * that path contains the usual file in place of the directory. + * Try to open/test the first index file before the test of directory + * existence because valid requests should be much more than invalid ones. + * If the file open()/stat() would fail, then the directory stat() should + * be more quickly because some data is already cached in the kernel. + * 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 contains an usual file in place of directory. */ static ngx_int_t ngx_http_index_handler(ngx_http_request_t *r) { - u_char *last; - size_t len; - ngx_fd_t fd; + u_char *p, *name; + size_t len, root, reserve, allocated; ngx_int_t rc; - ngx_err_t err; - ngx_str_t uri; - ngx_log_t *log; - ngx_uint_t i; + ngx_str_t path, uri; + ngx_uint_t i, dir_tested; ngx_http_index_t *index; - ngx_http_index_ctx_t *ctx; - ngx_pool_cleanup_t *cln; - ngx_pool_cleanup_file_t *clnf; + 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; @@ -144,33 +116,18 @@ return NGX_DECLINED; } - /* TODO: Win32 */ - if (r->zero_in_uri) { - return NGX_DECLINED; - } - - log = r->connection->log; - - /* - * we use context because the handler supports an async file opening, - * and may be called several times - */ - ilcf = ngx_http_get_module_loc_conf(r, ngx_http_index_module); + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); - ctx = ngx_http_get_module_ctx(r, ngx_http_index_module); - if (ctx == NULL) { - - ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_index_ctx_t)); - if (ctx == NULL) { - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } - - ngx_http_set_ctx(r, ctx, ngx_http_index_module); - } + allocated = 0; + root = 0; + dir_tested = 0; + name = NULL; + /* suppress MSVC warning */ + path.data = NULL; index = ilcf->indices->elts; - for (i = ctx->current; i < ilcf->indices->nelts; i++) { + for (i = 0; i < ilcf->indices->nelts; i++) { if (index[i].lengths == NULL) { @@ -178,8 +135,8 @@ return ngx_http_internal_redirect(r, &index[i].name, &r->args); } - len = ilcf->max_index_len; - ctx->index.len = index[i].name.len; + reserve = ilcf->max_index_len; + len = index[i].name.len; } else { ngx_memzero(&e, sizeof(ngx_http_script_engine_t)); @@ -188,8 +145,7 @@ e.request = r; e.flushed = 1; - /* 1 byte for terminating '\0' */ - + /* 1 is for terminating '\0' as in static names */ len = 1; while (*(uintptr_t *) e.ip) { @@ -197,116 +153,112 @@ len += lcode(&e); } - ctx->index.len = len; - /* 16 bytes are preallocation */ - len += 16; + reserve = len + 16; } - if (len > (size_t) (ctx->path.data + ctx->path.len - ctx->index.data)) { + if (reserve > allocated) { - last = ngx_http_map_uri_to_path(r, &ctx->path, &ctx->root, len); - if (last == NULL) { + name = ngx_http_map_uri_to_path(r, &path, &root, reserve); + if (name == NULL) { return NGX_ERROR; } - ctx->index.data = last; + allocated = path.data + path.len - name; } if (index[i].values == NULL) { /* index[i].name.len includes the terminating '\0' */ - ngx_memcpy(ctx->index.data, index[i].name.data, index[i].name.len); + 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 = ctx->index.data; + e.pos = name; while (*(uintptr_t *) e.ip) { code = *(ngx_http_script_code_pt *) e.ip; code((ngx_http_script_engine_t *) &e); } - if (*ctx->index.data == '/') { - ctx->index.len--; - return ngx_http_internal_redirect(r, &ctx->index, &r->args); + if (*name == '/') { + uri.len = len - 1; + uri.data = name; + return ngx_http_internal_redirect(r, &uri, &r->args); } - *e.pos++ = '\0'; - } - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, - "open index \"%s\"", ctx->path.data); + path.len = e.pos - path.data; - cln = ngx_pool_cleanup_add(r->pool, sizeof(ngx_pool_cleanup_file_t)); - if (cln == NULL) { - return NGX_HTTP_INTERNAL_SERVER_ERROR; + *e.pos = '\0'; } - fd = ngx_open_file(ctx->path.data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "open index \"%V\"", &path); - if (fd == (ngx_fd_t) NGX_AGAIN) { - ctx->current = i; - return NGX_AGAIN; - } + ngx_memzero(&of, sizeof(ngx_open_file_info_t)); - if (fd == NGX_INVALID_FILE) { - err = ngx_errno; + 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; - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, err, - ngx_open_file_n " \"%s\" failed", ctx->path.data); + if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool) + != NGX_OK) + { + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, of.err, + "%s \"%s\" failed", of.failed, path.data); - if (err == NGX_ENOTDIR) { - return ngx_http_index_error(r, ctx, err); + if (of.err == 0) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } - } else if (err == NGX_EACCES) { - return ngx_http_index_error(r, ctx, err); + 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 (!ctx->tested) { - rc = ngx_http_index_test_dir(r, ctx); + if (!dir_tested) { + rc = ngx_http_index_test_dir(r, clcf, path.data, name - 1); if (rc != NGX_OK) { return rc; } - ctx->tested = 1; + dir_tested = 1; } - if (err == NGX_ENOENT) { + if (of.err == NGX_ENOENT) { continue; } - ngx_log_error(NGX_LOG_ERR, log, err, - ngx_open_file_n " \"%s\" failed", ctx->path.data); + ngx_log_error(NGX_LOG_CRIT, r->connection->log, of.err, + "%s \"%s\" failed", of.failed, path.data); return NGX_HTTP_INTERNAL_SERVER_ERROR; } - cln->handler = ngx_pool_cleanup_file; - clnf = cln->data; - - clnf->fd = fd; - clnf->name = ctx->path.data; - clnf->log = r->pool->log; - - clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); - - uri.len = r->uri.len + ctx->index.len - 1; + uri.len = r->uri.len + len - 1; if (!clcf->alias) { - uri.data = ctx->path.data + ctx->root; + uri.data = path.data + root; } else { - uri.data = ngx_palloc(r->pool, uri.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, ctx->index.data, ctx->index.len - 1); + 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); @@ -317,61 +269,91 @@ static ngx_int_t -ngx_http_index_test_dir(ngx_http_request_t *r, ngx_http_index_ctx_t *ctx) +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_uint_t i; - ngx_err_t err; - ngx_file_info_t fi; - - c = *(ctx->index.data - 1); - i = (c == '/') ? 1 : 0; - *(ctx->index.data - i) = '\0'; + 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: \"%s\"", ctx->path.data); + "http index check dir: \"%V\"", &dir); - if (ngx_file_info(ctx->path.data, &fi) == -1) { + ngx_memzero(&of, sizeof(ngx_open_file_info_t)); - err = ngx_errno; + of.test_dir = 1; + of.test_only = 1; + of.valid = clcf->open_file_cache_valid; + of.errors = clcf->open_file_cache_errors; + + if (ngx_open_cached_file(clcf->open_file_cache, &dir, &of, r->pool) + != NGX_OK) + { + if (of.err) { + + if (of.err == NGX_ENOENT) { + *last = c; + return ngx_http_index_error(r, clcf, dir.data, NGX_ENOENT); + } - if (err == NGX_ENOENT) { - *(ctx->index.data - i) = c; - return ngx_http_index_error(r, ctx, err); - } + if (of.err == NGX_EACCES) { + + *last = c; - ngx_log_error(NGX_LOG_CRIT, r->connection->log, err, - ngx_file_info_n " \"%s\" failed", ctx->path.data); + /* + * 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; } - *(ctx->index.data - i) = c; + *last = c; - if (ngx_is_dir(&fi)) { + if (of.is_dir) { return NGX_OK; } ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, - "\"%s\" is not a directory", ctx->path.data); + "\"%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_index_ctx_t *ctx, - ngx_err_t err) +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", ctx->path.data); + "\"%s\" is forbidden", file); return NGX_HTTP_FORBIDDEN; } - ngx_log_error(NGX_LOG_ERR, r->connection->log, err, - "\"%s\" is not found", ctx->path.data); + 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; } @@ -384,7 +366,7 @@ conf = ngx_palloc(cf->pool, sizeof(ngx_http_index_loc_conf_t)); if (conf == NULL) { - return NGX_CONF_ERROR; + return NULL; } conf->indices = NULL; @@ -473,11 +455,11 @@ 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_EMERG, cf, 0, + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, "only the last index in \"index\" directive " - "may be absolute"); - return NGX_CONF_ERROR; + "should be absolute"); } if (value[i].len == 0) { @@ -504,7 +486,11 @@ ilcf->max_index_len = index->name.len; } - /* include the terminating '\0' to the length to use ngx_copy() */ + if (index->name.data[0] == '/') { + continue; + } + + /* include the terminating '\0' to the length to use ngx_memcpy() */ index->name.len++; continue; diff -Nru nginx-0.5.33/src/http/modules/ngx_http_limit_req_module.c nginx-0.8.53/src/http/modules/ngx_http_limit_req_module.c --- nginx-0.5.33/src/http/modules/ngx_http_limit_req_module.c 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/src/http/modules/ngx_http_limit_req_module.c 2010-10-14 09:20:01.000000000 +0000 @@ -0,0 +1,811 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#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; + 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_int_t index; + ngx_str_t var; +} 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 limit_log_level; + ngx_uint_t delay_log_level; + + ngx_uint_t nodelay; /* unsigned nodelay:1 */ +} 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_conf_t *lrcf, + ngx_uint_t hash, u_char *data, size_t len, ngx_uint_t *ep); +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_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_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 configration */ + ngx_http_limit_req_merge_conf /* merge location configration */ +}; + + +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) +{ + size_t len, n; + uint32_t hash; + ngx_int_t rc; + ngx_uint_t excess; + ngx_time_t *tp; + ngx_rbtree_node_t *node; + ngx_http_variable_value_t *vv; + ngx_http_limit_req_ctx_t *ctx; + ngx_http_limit_req_node_t *lr; + ngx_http_limit_req_conf_t *lrcf; + + if (r->main->limit_req_set) { + return NGX_DECLINED; + } + + lrcf = ngx_http_get_module_loc_conf(r, ngx_http_limit_req_module); + + if (lrcf->shm_zone == NULL) { + return NGX_DECLINED; + } + + ctx = lrcf->shm_zone->data; + + vv = ngx_http_get_indexed_variable(r, ctx->index); + + if (vv == NULL || vv->not_found) { + return NGX_DECLINED; + } + + len = vv->len; + + if (len == 0) { + return NGX_DECLINED; + } + + if (len > 65535) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "the value of the \"%V\" variable " + "is more than 65535 bytes: \"%v\"", + &ctx->var, vv); + return NGX_DECLINED; + } + + r->main->limit_req_set = 1; + + hash = ngx_crc32_short(vv->data, len); + + ngx_shmtx_lock(&ctx->shpool->mutex); + + ngx_http_limit_req_expire(ctx, 1); + + rc = ngx_http_limit_req_lookup(lrcf, hash, vv->data, len, &excess); + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "limit_req: %i %ui.%03ui", rc, excess / 1000, excess % 1000); + + if (rc == NGX_DECLINED) { + + n = offsetof(ngx_rbtree_node_t, color) + + offsetof(ngx_http_limit_req_node_t, data) + + len; + + node = ngx_slab_alloc_locked(ctx->shpool, n); + if (node == NULL) { + + ngx_http_limit_req_expire(ctx, 0); + + node = ngx_slab_alloc_locked(ctx->shpool, n); + if (node == NULL) { + ngx_shmtx_unlock(&ctx->shpool->mutex); + return NGX_HTTP_SERVICE_UNAVAILABLE; + } + } + + lr = (ngx_http_limit_req_node_t *) &node->color; + + node->key = hash; + lr->len = (u_char) len; + + tp = ngx_timeofday(); + lr->last = (ngx_msec_t) (tp->sec * 1000 + tp->msec); + + lr->excess = 0; + ngx_memcpy(lr->data, vv->data, len); + + ngx_rbtree_insert(&ctx->sh->rbtree, node); + + ngx_queue_insert_head(&ctx->sh->queue, &lr->queue); + + ngx_shmtx_unlock(&ctx->shpool->mutex); + + return NGX_DECLINED; + } + + ngx_shmtx_unlock(&ctx->shpool->mutex); + + if (rc == NGX_OK) { + return NGX_DECLINED; + } + + 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, &lrcf->shm_zone->shm.name); + + return NGX_HTTP_SERVICE_UNAVAILABLE; + } + + /* rc == NGX_AGAIN */ + + if (lrcf->nodelay) { + 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, &lrcf->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; + ngx_add_timer(r->connection->write, + (ngx_msec_t) excess * 1000 / ctx->rate); + + 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->timedout) { + + if (ngx_handle_write_event(wev, 0) != NGX_OK) { + ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + } + + return; + } + + wev->timedout = 0; + + 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_conf_t *lrcf, ngx_uint_t hash, + u_char *data, size_t len, ngx_uint_t *ep) +{ + ngx_int_t rc, excess; + ngx_time_t *tp; + 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; + + ctx = lrcf->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 */ + + do { + lr = (ngx_http_limit_req_node_t *) &node->color; + + rc = ngx_memn2cmp(data, lr->data, len, (size_t) lr->len); + + if (rc == 0) { + ngx_queue_remove(&lr->queue); + ngx_queue_insert_head(&ctx->sh->queue, &lr->queue); + + tp = ngx_timeofday(); + + now = (ngx_msec_t) (tp->sec * 1000 + tp->msec); + 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 > lrcf->burst) { + return NGX_BUSY; + } + + lr->excess = excess; + lr->last = now; + + if (excess) { + return NGX_AGAIN; + } + + return NGX_OK; + } + + node = (rc < 0) ? node->left : node->right; + + } while (node != sentinel && hash == node->key); + + break; + } + + *ep = 0; + + return NGX_DECLINED; +} + + +static void +ngx_http_limit_req_expire(ngx_http_limit_req_ctx_t *ctx, ngx_uint_t n) +{ + ngx_int_t excess; + ngx_time_t *tp; + 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; + + tp = ngx_timeofday(); + + now = (ngx_msec_t) (tp->sec * 1000 + tp->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 (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 (ngx_strcmp(ctx->var.data, octx->var.data) != 0) { + ngx_log_error(NGX_LOG_EMERG, shm_zone->shm.log, 0, + "limit_req \"%V\" uses the \"%V\" variable " + "while previously it used the \"%V\" variable", + &shm_zone->shm.name, &ctx->var, &octx->var); + 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); + + 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->shm_zone = NULL; + * conf->burst = 0; + * conf->nodelay = 0; + */ + + conf->limit_log_level = 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->shm_zone == NULL) { + *conf = *prev; + } + + 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; + + 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 size, len; + 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; + + value = cf->args->elts; + + ctx = NULL; + size = 0; + rate = 1; + scale = 1; + name.len = 0; + + for (i = 1; 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) { + *p = '\0'; + + name.len = p - name.data; + + p++; + + s.len = value[i].data + value[i].len - p; + s.data = p; + + size = ngx_parse_size(&s); + if (size > 8191) { + continue; + } + } + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid zone size \"%V\"", &value[i]); + return NGX_CONF_ERROR; + } + + 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 <= NGX_ERROR) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid rate \"%V\"", &value[i]); + return NGX_CONF_ERROR; + } + + continue; + } + + if (value[i].data[0] == '$') { + + value[i].len--; + value[i].data++; + + ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_limit_req_ctx_t)); + if (ctx == NULL) { + return NGX_CONF_ERROR; + } + + ctx->index = ngx_http_get_variable_index(cf, &value[i]); + if (ctx->index == NGX_ERROR) { + return NGX_CONF_ERROR; + } + + ctx->var = value[i]; + + continue; + } + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid parameter \"%V\"", &value[i]); + return NGX_CONF_ERROR; + } + + if (name.len == 0 || size == 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "\"%V\" must have \"zone\" parameter", + &cmd->name); + return NGX_CONF_ERROR; + } + + if (ctx == NULL) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "no variable is defined for limit_req_zone \"%V\"", + &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, + "limit_req_zone \"%V\" is already bound to variable \"%V\"", + &value[1], &ctx->var); + 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; + + if (lrcf->shm_zone) { + return "is duplicate"; + } + + value = cf->args->elts; + + burst = 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; + + lrcf->shm_zone = ngx_shared_memory_add(cf, &s, 0, + &ngx_http_limit_req_module); + if (lrcf->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_strncmp(value[i].data, "nodelay", 7) == 0) { + lrcf->nodelay = 1; + continue; + } + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid parameter \"%V\"", &value[i]); + return NGX_CONF_ERROR; + } + + if (lrcf->shm_zone == NULL) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "\"%V\" must have \"zone\" parameter", + &cmd->name); + return NGX_CONF_ERROR; + } + + if (lrcf->shm_zone->data == NULL) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "unknown limit_req_zone \"%V\"", + &lrcf->shm_zone->shm.name); + return NGX_CONF_ERROR; + } + + lrcf->burst = burst * 1000; + + 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; +} diff -Nru nginx-0.5.33/src/http/modules/ngx_http_limit_zone_module.c nginx-0.8.53/src/http/modules/ngx_http_limit_zone_module.c --- nginx-0.5.33/src/http/modules/ngx_http_limit_zone_module.c 2007-09-22 19:18:36.000000000 +0000 +++ nginx-0.8.53/src/http/modules/ngx_http_limit_zone_module.c 2009-10-06 10:14:29.000000000 +0000 @@ -33,6 +33,7 @@ typedef struct { ngx_shm_zone_t *shm_zone; ngx_uint_t conn; + ngx_uint_t log_level; } ngx_http_limit_zone_conf_t; @@ -48,6 +49,15 @@ static ngx_int_t ngx_http_limit_zone_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_command_t ngx_http_limit_zone_commands[] = { { ngx_string("limit_zone"), @@ -64,6 +74,13 @@ 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_zone_conf_t, log_level), + &ngx_http_limit_conn_log_levels }, + ngx_null_command }; @@ -189,6 +206,10 @@ ngx_shmtx_unlock(&shpool->mutex); + ngx_log_error(lzcf->log_level, r->connection->log, 0, + "limiting connections by zone \"%V\"", + &lzcf->shm_zone->shm.name); + return NGX_HTTP_SERVICE_UNAVAILABLE; } @@ -239,54 +260,36 @@ ngx_http_limit_zone_rbtree_insert_value(ngx_rbtree_node_t *temp, ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel) { - ngx_http_limit_zone_node_t *lzn, *lznt; + ngx_rbtree_node_t **p; + ngx_http_limit_zone_node_t *lzn, *lznt; for ( ;; ) { if (node->key < temp->key) { - if (temp->left == sentinel) { - temp->left = node; - break; - } - - temp = temp->left; + p = &temp->left; } else if (node->key > temp->key) { - if (temp->right == sentinel) { - temp->right = node; - break; - } - - temp = temp->right; + p = &temp->right; } else { /* node->key == temp->key */ lzn = (ngx_http_limit_zone_node_t *) &node->color; lznt = (ngx_http_limit_zone_node_t *) &temp->color; - if (ngx_memn2cmp(lzn->data, lznt->data, lzn->len, lznt->len) < 0) { - - if (temp->left == sentinel) { - temp->left = node; - break; - } - - temp = temp->left; - - } else { - - if (temp->right == sentinel) { - temp->right = node; - break; - } + p = (ngx_memn2cmp(lzn->data, lznt->data, lzn->len, lznt->len) < 0) + ? &temp->left : &temp->right; + } - temp = temp->right; - } + if (*p == sentinel) { + break; } + + temp = *p; } + *p = node; node->parent = temp; node->left = sentinel; node->right = sentinel; @@ -330,6 +333,7 @@ { ngx_http_limit_zone_ctx_t *octx = data; + size_t len; ngx_slab_pool_t *shpool; ngx_rbtree_node_t *sentinel; ngx_http_limit_zone_ctx_t *ctx; @@ -341,7 +345,7 @@ ngx_log_error(NGX_LOG_EMERG, shm_zone->shm.log, 0, "limit_zone \"%V\" uses the \"%V\" variable " "while previously it used the \"%V\" variable", - &shm_zone->name, &ctx->var, &octx->var); + &shm_zone->shm.name, &ctx->var, &octx->var); return NGX_ERROR; } @@ -352,21 +356,36 @@ 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_sentinel_init(sentinel); + ngx_rbtree_init(ctx->rbtree, sentinel, + ngx_http_limit_zone_rbtree_insert_value); + + len = sizeof(" in limit_zone \"\"") + shm_zone->shm.name.len; - ctx->rbtree->root = sentinel; - ctx->rbtree->sentinel = sentinel; - ctx->rbtree->insert = ngx_http_limit_zone_rbtree_insert_value; + shpool->log_ctx = ngx_slab_alloc(shpool, len); + if (shpool->log_ctx == NULL) { + return NGX_ERROR; + } + + ngx_sprintf(shpool->log_ctx, " in limit_zone \"%V\"%Z", + &shm_zone->shm.name); return NGX_OK; } @@ -379,7 +398,7 @@ conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_limit_zone_conf_t)); if (conf == NULL) { - return NGX_CONF_ERROR; + return NULL; } /* @@ -389,6 +408,8 @@ * conf->conn = 0; */ + conf->log_level = NGX_CONF_UNSET_UINT; + return conf; } @@ -403,6 +424,8 @@ *conf = *prev; } + ngx_conf_merge_uint_value(conf->log_level, prev->log_level, NGX_LOG_ERR); + return NGX_CONF_OK; } @@ -483,6 +506,10 @@ ngx_int_t n; ngx_str_t *value; + if (lzcf->shm_zone) { + return "is duplicate"; + } + value = cf->args->elts; lzcf->shm_zone = ngx_shared_memory_add(cf, &value[1], 0, diff -Nru nginx-0.5.33/src/http/modules/ngx_http_log_module.c nginx-0.8.53/src/http/modules/ngx_http_log_module.c --- nginx-0.5.33/src/http/modules/ngx_http_log_module.c 2007-09-01 09:51:57.000000000 +0000 +++ nginx-0.8.53/src/http/modules/ngx_http_log_module.c 2010-05-14 09:56:37.000000000 +0000 @@ -7,7 +7,6 @@ #include #include #include -#include typedef struct ngx_http_log_op_s ngx_http_log_op_t; @@ -29,6 +28,7 @@ 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; @@ -40,15 +40,27 @@ 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_array_t *ops; /* array of ngx_http_log_op_t */ + ngx_http_log_fmt_t *format; } 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; @@ -62,6 +74,8 @@ 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); static u_char *ngx_http_log_connection(ngx_http_request_t *r, u_char *buf, ngx_http_log_op_t *op); @@ -88,6 +102,7 @@ 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 void *ngx_http_log_create_main_conf(ngx_conf_t *cf); @@ -99,7 +114,9 @@ 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 *ops, ngx_array_t *args, ngx_uint_t s); + 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); @@ -114,12 +131,19 @@ { ngx_string("access_log"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF - |NGX_CONF_TAKE123, + |NGX_HTTP_LMT_CONF|NGX_CONF_TAKE123, 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 }; @@ -219,9 +243,11 @@ continue; } + ngx_http_script_flush_no_cacheable_variables(r, log[l].format->flushes); + len = 0; - op = log[l].ops->elts; - for (i = 0; i < log[l].ops->nelts; i++) { + 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); @@ -234,7 +260,7 @@ file = log[l].file; - if (file->buffer) { + if (file && file->buffer) { if (len > (size_t) (file->last - file->pos)) { @@ -248,7 +274,7 @@ p = file->pos; - for (i = 0; i < log[l].ops->nelts; i++) { + for (i = 0; i < log[l].format->ops->nelts; i++) { p = op[i].run(r, p, &op[i]); } @@ -260,14 +286,14 @@ } } - line = ngx_palloc(r->pool, len); + line = ngx_pnalloc(r->pool, len); if (line == NULL) { return NGX_ERROR; } p = line; - for (i = 0; i < log[l].ops->nelts; i++) { + for (i = 0; i < log[l].format->ops->nelts; i++) { p = op[i].run(r, p, &op[i]); } @@ -284,11 +310,19 @@ ngx_http_log_write(ngx_http_request_t *r, ngx_http_log_t *log, u_char *buf, size_t len) { - time_t now; - ssize_t n; - ngx_err_t err; + u_char *name; + time_t now; + ssize_t n; + ngx_err_t err; + + if (log->script == NULL) { + name = log->file->name.data; + n = ngx_write_fd(log->file->fd, buf, len); - n = ngx_write_fd(log->file->fd, buf, len); + } else { + name = NULL; + n = ngx_http_log_script_write(r, log->script, &name, buf, len); + } if (n == (ssize_t) len) { return; @@ -305,8 +339,7 @@ if (now - log->error_log_time > 59) { ngx_log_error(NGX_LOG_ALERT, r->connection->log, err, - ngx_write_fd_n " to \"%V\" failed", - &log->file->name); + ngx_write_fd_n " to \"%s\" failed", name); log->error_log_time = now; } @@ -316,14 +349,112 @@ if (now - log->error_log_time > 59) { ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, - ngx_write_fd_n " to \"%V\" was incomplete: %z of %uz", - &log->file->name, n, len); + 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; + + if (!r->root_tested) { + + /* test root directory existance */ + + if (ngx_http_map_uri_to_path(r, &path, &root, 0) == NULL) { + /* simulate successfull logging */ + return len; + } + + path.data[root] = '\0'; + + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + 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_open_cached_file(clcf->open_file_cache, &path, &of, r->pool) + != NGX_OK) + { + if (of.err == 0) { + /* simulate successfull logging */ + return len; + } + + ngx_log_error(NGX_LOG_ERR, r->connection->log, of.err, + "testing \"%s\" existence failed", path.data); + + /* simulate successfull 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 successfull logging */ + return len; + } + } + + if (ngx_http_script_run(r, &log, script->lengths->elts, 1, + script->values->elts) + == NULL) + { + /* simulate successfull 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_open_cached_file(llcf->open_file_cache, &log, &of, r->pool) + != NGX_OK) + { + ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno, + "%s \"%s\" failed", of.failed, log.data); + /* simulate successfull 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; +} + + static u_char * ngx_http_log_copy_short(ngx_http_request_t *r, u_char *buf, ngx_http_log_op_t *op) @@ -400,8 +531,9 @@ tp = ngx_timeofday(); - ms = (tp->sec - r->start_sec) * 1000 + (tp->msec - r->start_msec); - ms = (ms >= 0) ? ms : 0; + 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", ms / 1000, ms % 1000); } @@ -410,8 +542,25 @@ static u_char * ngx_http_log_status(ngx_http_request_t *r, u_char *buf, ngx_http_log_op_t *op) { - return ngx_sprintf(buf, "%ui", - r->err_status ? r->err_status : r->headers_out.status); + 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) { + *buf++ = '0'; + *buf++ = '0'; + *buf++ = '9'; + return buf; + + } else { + status = 0; + } + + return ngx_sprintf(buf, "%ui", status); } @@ -477,6 +626,7 @@ 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); @@ -485,7 +635,11 @@ return 1; } - return value->len; + len = ngx_http_log_escape(NULL, value->data, value->len); + + value->escape = len ? 1 : 0; + + return value->len + len * 3; } @@ -501,7 +655,72 @@ return buf + 1; } - return ngx_cpymem(buf, value->data, value->len); + 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` */ + 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ + + 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ + 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ + 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ + 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ + }; + + + if (dst == NULL) { + + /* find the number of the characters to be escaped */ + + n = 0; + + while (size) { + if (escape[*src >> 5] & (1 << (*src & 0x1f))) { + n++; + } + src++; + size--; + } + + return (uintptr_t) n; + } + + while (size) { + if (escape[*src >> 5] & (1 << (*src & 0x1f))) { + *dst++ = '\\'; + *dst++ = 'x'; + *dst++ = hex[*src >> 4]; + *dst++ = hex[*src & 0xf]; + src++; + + } else { + *dst++ = *src++; + } + size--; + } + + return (uintptr_t) dst; } @@ -514,26 +733,27 @@ conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_log_main_conf_t)); if (conf == NULL) { - return NGX_CONF_ERROR; + return NULL; } if (ngx_array_init(&conf->formats, cf->pool, 4, sizeof(ngx_http_log_fmt_t)) != NGX_OK) { - return NGX_CONF_ERROR; + return NULL; } fmt = ngx_array_push(&conf->formats); if (fmt == NULL) { - return NGX_CONF_ERROR; + return NULL; } - fmt->name.len = sizeof("combined") - 1; - fmt->name.data = (u_char *) "combined"; + 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 NGX_CONF_ERROR; + return NULL; } return conf; @@ -547,9 +767,11 @@ conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_log_loc_conf_t)); if (conf == NULL) { - return NGX_CONF_ERROR; + return NULL; } + conf->open_file_cache = NGX_CONF_UNSET_PTR; + return conf; } @@ -564,11 +786,23 @@ 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 = *prev; + conf->logs = prev->logs; + conf->off = prev->off; if (conf->logs || conf->off) { return NGX_CONF_OK; @@ -589,6 +823,7 @@ return NGX_CONF_ERROR; } + log->script = NULL; log->disk_full_time = 0; log->error_log_time = 0; @@ -596,7 +831,7 @@ fmt = lmcf->formats.elts; /* the default "combined" format */ - log->ops = fmt[0].ops; + log->format = &fmt[0]; lmcf->combined_used = 1; return NGX_CONF_OK; @@ -608,18 +843,25 @@ { ngx_http_log_loc_conf_t *llcf = conf; - ssize_t buf; - ngx_uint_t i; - ngx_str_t *value, name; - ngx_http_log_t *log; - ngx_http_log_fmt_t *fmt; - ngx_http_log_main_conf_t *lmcf; + ssize_t buf; + ngx_uint_t i, n; + ngx_str_t *value, name; + ngx_http_log_t *log; + ngx_http_log_fmt_t *fmt; + ngx_http_log_main_conf_t *lmcf; + ngx_http_script_compile_t sc; value = cf->args->elts; if (ngx_strcmp(value[1].data, "off") == 0) { llcf->off = 1; - return NGX_CONF_OK; + 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) { @@ -636,13 +878,40 @@ return NGX_CONF_ERROR; } - log->file = ngx_conf_open_file(cf->cycle, &value[1]); - if (log->file == NULL) { - return NGX_CONF_ERROR; - } + ngx_memzero(log, sizeof(ngx_http_log_t)); - log->disk_full_time = 0; - log->error_log_time = 0; + 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; + } + } if (cf->args->nelts >= 3) { name = value[2]; @@ -652,8 +921,7 @@ } } else { - name.len = sizeof("combined") - 1; - name.data = (u_char *) "combined"; + ngx_str_set(&name, "combined"); lmcf->combined_used = 1; } @@ -662,7 +930,7 @@ if (fmt[i].name.len == name.len && ngx_strcasecmp(fmt[i].name.data, name.data) == 0) { - log->ops = fmt[i].ops; + log->format = &fmt[i]; goto buffer; } } @@ -680,6 +948,12 @@ return NGX_CONF_ERROR; } + if (log->script) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "buffered logs can not have variables in name"); + return NGX_CONF_ERROR; + } + name.len = value[3].len - 7; name.data = value[3].data + 7; @@ -727,7 +1001,10 @@ if (fmt[i].name.len == value[1].len && ngx_strcmp(fmt[i].name.data, value[1].data) == 0) { - return "duplicate \"log_format\" name"; + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "duplicate \"log_format\" name \"%V\"", + &value[1]); + return NGX_CONF_ERROR; } } @@ -738,22 +1015,28 @@ 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->ops, cf->args, 2); + 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 *ops, - ngx_array_t *args, ngx_uint_t s) +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; ngx_http_log_op_t *op; ngx_http_log_var_t *v; @@ -867,6 +1150,16 @@ 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; @@ -897,7 +1190,7 @@ } else { op->run = ngx_http_log_copy_long; - p = ngx_palloc(cf->pool, len); + p = ngx_pnalloc(cf->pool, len); if (p == NULL) { return NGX_CONF_ERROR; } @@ -919,6 +1212,114 @@ } +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 < 0) { + 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 < 0) { + 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) { @@ -944,7 +1345,7 @@ *value = ngx_http_combined_fmt; fmt = lmcf->formats.elts; - if (ngx_http_log_compile_format(cf, fmt->ops, &a, 0) + if (ngx_http_log_compile_format(cf, NULL, fmt->ops, &a, 0) != NGX_CONF_OK) { return NGX_ERROR; diff -Nru nginx-0.5.33/src/http/modules/ngx_http_map_module.c nginx-0.8.53/src/http/modules/ngx_http_map_module.c --- nginx-0.5.33/src/http/modules/ngx_http_map_module.c 2007-11-07 13:39:53.000000000 +0000 +++ nginx-0.8.53/src/http/modules/ngx_http_map_module.c 2010-06-23 16:34:54.000000000 +0000 @@ -106,7 +106,7 @@ size_t len; u_char *name; - ngx_uint_t key, i; + ngx_uint_t key; ngx_http_variable_value_t *vv, *value; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, @@ -130,16 +130,12 @@ return NGX_OK; } - name = ngx_palloc(r->pool, len); + name = ngx_pnalloc(r->pool, len); if (name == NULL) { return NGX_ERROR; } - key = 0; - for (i = 0; i < len; i++) { - name[i] = ngx_tolower(vv->data[i]); - key = ngx_hash(key, name[i]); - } + key = ngx_hash_strlow(name, vv->data, len); value = ngx_hash_find_combined(&map->hash, key, name, len); @@ -164,7 +160,7 @@ mcf = ngx_palloc(cf->pool, sizeof(ngx_http_map_conf_t)); if (mcf == NULL) { - return NGX_CONF_ERROR; + return NULL; } mcf->hash_max_size = NGX_CONF_UNSET_UINT; @@ -221,7 +217,7 @@ name.len--; name.data++; - var = ngx_http_add_variable(cf, &name, NGX_HTTP_VAR_CHANGABLE); + var = ngx_http_add_variable(cf, &name, NGX_HTTP_VAR_CHANGEABLE); if (var == NULL) { return NGX_CONF_ERROR; } @@ -341,7 +337,7 @@ first = (ngx_hash_key_t *) one; second = (ngx_hash_key_t *) two; - return ngx_strcmp(first->key.data, second->key.data); + return ngx_dns_strcmp(first->key.data, second->key.data); } @@ -378,7 +374,7 @@ if (ngx_strcmp(value[0].data, "include") == 0) { file = value[1]; - if (ngx_conf_full_name(cf->cycle, &file) == NGX_ERROR){ + if (ngx_conf_full_name(cf->cycle, &file, 1) != NGX_OK) { return NGX_CONF_ERROR; } @@ -430,7 +426,7 @@ } var->valid = 1; - var->no_cachable = 0; + var->no_cacheable = 0; var->not_found = 0; vp = ngx_array_push(&ctx->values_hash[key]); diff -Nru nginx-0.5.33/src/http/modules/ngx_http_memcached_module.c nginx-0.8.53/src/http/modules/ngx_http_memcached_module.c --- nginx-0.5.33/src/http/modules/ngx_http_memcached_module.c 2007-10-29 14:52:51.000000000 +0000 +++ nginx-0.8.53/src/http/modules/ngx_http_memcached_module.c 2010-05-20 11:46:01.000000000 +0000 @@ -6,7 +6,6 @@ #include #include -#include #include @@ -39,11 +38,6 @@ static char *ngx_http_memcached_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); -static char *ngx_http_memcached_upstream_max_fails_unsupported(ngx_conf_t *cf, - ngx_command_t *cmd, void *conf); -static char *ngx_http_memcached_upstream_fail_timeout_unsupported(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 }, @@ -58,12 +52,19 @@ static ngx_command_t ngx_http_memcached_commands[] = { { ngx_string("memcached_pass"), - NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + 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_TAKE1, + 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, @@ -99,20 +100,6 @@ offsetof(ngx_http_memcached_loc_conf_t, upstream.next_upstream), &ngx_http_memcached_next_upstream_masks }, - { ngx_string("memcached_upstream_max_fails"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, - ngx_http_memcached_upstream_max_fails_unsupported, - 0, - 0, - NULL }, - - { ngx_string("memcached_upstream_fail_timeout"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, - ngx_http_memcached_upstream_fail_timeout_unsupported, - 0, - 0, - NULL }, - ngx_null_command }; @@ -167,9 +154,9 @@ return NGX_HTTP_NOT_ALLOWED; } - rc = ngx_http_discard_body(r); + rc = ngx_http_discard_request_body(r); - if (rc != NGX_OK && rc != NGX_AGAIN) { + if (rc != NGX_OK) { return rc; } @@ -177,21 +164,17 @@ return NGX_HTTP_INTERNAL_SERVER_ERROR; } - mlcf = ngx_http_get_module_loc_conf(r, ngx_http_memcached_module); - - u = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_t)); - if (u == NULL) { + if (ngx_http_upstream_create(r) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } - u->peer.log = r->connection->log; - u->peer.log_error = NGX_ERROR_ERR; -#if (NGX_THREADS) - u->peer.lock = &r->connection->lock; -#endif + 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; @@ -200,8 +183,6 @@ u->abort_request = ngx_http_memcached_abort_request; u->finalize_request = ngx_http_memcached_finalize_request; - r->upstream = u; - ctx = ngx_palloc(r->pool, sizeof(ngx_http_memcached_ctx_t)); if (ctx == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; @@ -216,6 +197,8 @@ u->input_filter = ngx_http_memcached_filter; u->input_filter_ctx = ctx; + r->main->count++; + ngx_http_upstream_init(r); return NGX_DONE; @@ -371,6 +354,7 @@ } u->headers_in.status_n = 200; + u->state->status = 200; u->buffer.pos = p + 1; return NGX_OK; @@ -381,6 +365,7 @@ "key: \"%V\" was not found by memcached", &ctx->key); u->headers_in.status_n = 404; + u->state->status = 404; return NGX_OK; } @@ -426,15 +411,20 @@ if (ngx_strncmp(b->last, ngx_http_memcached_end + NGX_HTTP_MEMCACHED_END - ctx->rest, - 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 = 0; - ctx->rest = 0; + u->length -= bytes; + ctx->rest -= bytes; return NGX_OK; } @@ -457,6 +447,7 @@ 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:%z rest:%z", @@ -508,7 +499,7 @@ conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_memcached_loc_conf_t)); if (conf == NULL) { - return NGX_CONF_ERROR; + return NULL; } /* @@ -517,11 +508,8 @@ * conf->upstream.bufs.num = 0; * conf->upstream.next_upstream = 0; * conf->upstream.temp_path = NULL; - * conf->upstream.schema = { 0, NULL }; * conf->upstream.uri = { 0, NULL }; * conf->upstream.location = NULL; - * - * conf->index = 0; */ conf->upstream.connect_timeout = NGX_CONF_UNSET_MSEC; @@ -544,6 +532,8 @@ conf->upstream.pass_request_headers = 0; conf->upstream.pass_request_body = 0; + conf->index = NGX_CONF_UNSET; + return conf; } @@ -578,6 +568,14 @@ |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; + } + return NGX_CONF_OK; } @@ -585,13 +583,13 @@ static char * ngx_http_memcached_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { - ngx_http_memcached_loc_conf_t *lcf = 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 (lcf->upstream.schema.len) { + if (mlcf->upstream.upstream) { return "is duplicate"; } @@ -602,55 +600,24 @@ u.url = value[1]; u.no_resolve = 1; - lcf->upstream.upstream = ngx_http_upstream_add(cf, &u, 0); - if (lcf->upstream.upstream == NULL) { + mlcf->upstream.upstream = ngx_http_upstream_add(cf, &u, 0); + if (mlcf->upstream.upstream == NULL) { return NGX_CONF_ERROR; } - lcf->upstream.schema.len = sizeof("memcached://") - 1; - lcf->upstream.schema.data = (u_char *) "memcached://"; - clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); clcf->handler = ngx_http_memcached_handler; - lcf->upstream.location = clcf->name; - if (clcf->name.data[clcf->name.len - 1] == '/') { clcf->auto_redirect = 1; } - lcf->index = ngx_http_get_variable_index(cf, &ngx_http_memcached_key); + mlcf->index = ngx_http_get_variable_index(cf, &ngx_http_memcached_key); - if (lcf->index == NGX_ERROR) { + if (mlcf->index == NGX_ERROR) { return NGX_CONF_ERROR; } return NGX_CONF_OK; } - - -static char * -ngx_http_memcached_upstream_max_fails_unsupported(ngx_conf_t *cf, - ngx_command_t *cmd, void *conf) -{ - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "\"memcached_upstream_max_fails\" is not supported, " - "use the \"max_fails\" parameter of the \"server\" directive ", - "inside the \"upstream\" block"); - - return NGX_CONF_ERROR; -} - - -static char * -ngx_http_memcached_upstream_fail_timeout_unsupported(ngx_conf_t *cf, - ngx_command_t *cmd, void *conf) -{ - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "\"memcached_upstream_fail_timeout\" is not supported, " - "use the \"fail_timeout\" parameter of the \"server\" directive ", - "inside the \"upstream\" block"); - - return NGX_CONF_ERROR; -} diff -Nru nginx-0.5.33/src/http/modules/ngx_http_not_modified_filter_module.c nginx-0.8.53/src/http/modules/ngx_http_not_modified_filter_module.c --- nginx-0.5.33/src/http/modules/ngx_http_not_modified_filter_module.c 2006-08-30 10:39:17.000000000 +0000 +++ nginx-0.8.53/src/http/modules/ngx_http_not_modified_filter_module.c 2009-11-11 14:32:49.000000000 +0000 @@ -47,10 +47,11 @@ 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) +static ngx_int_t +ngx_http_not_modified_header_filter(ngx_http_request_t *r) { - time_t ims; + time_t ims; + ngx_http_core_loc_conf_t *clcf; if (r->headers_out.status != NGX_HTTP_OK || r != r->main @@ -60,29 +61,44 @@ return ngx_http_next_header_filter(r); } + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + if (clcf->if_modified_since == NGX_HTTP_IMS_OFF) { + return ngx_http_next_header_filter(r); + } + ims = ngx_http_parse_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:%d lm:%d", ims, r->headers_out.last_modified_time); - /* - * I think that the equality of the dates is correcter - */ - - if (ims != NGX_ERROR && ims == r->headers_out.last_modified_time) { - r->headers_out.status = NGX_HTTP_NOT_MODIFIED; - r->headers_out.content_type.len = 0; - ngx_http_clear_content_length(r); - ngx_http_clear_accept_ranges(r); + if (ims != r->headers_out.last_modified_time) { + + if (clcf->if_modified_since == NGX_HTTP_IMS_EXACT + || ims < r->headers_out.last_modified_time) + { + return ngx_http_next_header_filter(r); + } + } + + 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); } -static -ngx_int_t ngx_http_not_modified_filter_init(ngx_conf_t *cf) +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; diff -Nru nginx-0.5.33/src/http/modules/ngx_http_proxy_module.c nginx-0.8.53/src/http/modules/ngx_http_proxy_module.c --- nginx-0.5.33/src/http/modules/ngx_http_proxy_module.c 2007-09-23 19:28:29.000000000 +0000 +++ nginx-0.8.53/src/http/modules/ngx_http_proxy_module.c 2010-08-03 12:59:14.000000000 +0000 @@ -6,7 +6,6 @@ #include #include -#include #include @@ -33,6 +32,15 @@ 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_http_upstream_conf_t upstream; ngx_array_t *flushes; @@ -43,15 +51,23 @@ ngx_hash_t headers_set_hash; ngx_array_t *headers_source; - ngx_array_t *headers_names; + + ngx_array_t *proxy_lengths; + ngx_array_t *proxy_values; ngx_array_t *redirects; ngx_str_t body_source; ngx_str_t method; - ngx_str_t host_header; - ngx_str_t port; + 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; @@ -61,23 +77,20 @@ typedef struct { - ngx_uint_t status; - ngx_uint_t status_count; - u_char *status_start; - u_char *status_end; - + ngx_http_status_t status; + ngx_http_proxy_vars_t vars; size_t internal_body_length; } ngx_http_proxy_ctx_t; -#define NGX_HTTP_PROXY_PARSE_NO_HEADER 20 - - +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_process_status_line(ngx_http_request_t *r); -static ngx_int_t ngx_http_proxy_parse_status_line(ngx_http_request_t *r, - ngx_http_proxy_ctx_t *p); static ngx_int_t ngx_http_proxy_process_header(ngx_http_request_t *r); static void ngx_http_proxy_abort_request(ngx_http_request_t *r); static void ngx_http_proxy_finalize_request(ngx_http_request_t *r, @@ -100,6 +113,8 @@ 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_merge_headers(ngx_conf_t *cf, + ngx_http_proxy_loc_conf_t *conf, ngx_http_proxy_loc_conf_t *prev); static char *ngx_http_proxy_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); @@ -107,39 +122,44 @@ 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 static char *ngx_http_proxy_lowat_check(ngx_conf_t *cf, void *post, void *data); -static char *ngx_http_proxy_upstream_max_fails_unsupported(ngx_conf_t *cf, - ngx_command_t *cmd, void *conf); -static char *ngx_http_proxy_upstream_fail_timeout_unsupported(ngx_conf_t *cf, - ngx_command_t *cmd, void *conf); +#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_deprecated_t ngx_conf_deprecated_proxy_header_buffer_size = { - ngx_conf_deprecated, "proxy_header_buffer_size", "proxy_buffer_size" -}; - -static ngx_conf_deprecated_t ngx_conf_deprecated_proxy_redirect_errors = { - ngx_conf_deprecated, "proxy_redirect_errors", "proxy_intercept_errors" -}; - 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("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_404"), NGX_HTTP_UPSTREAM_FT_HTTP_404 }, + { 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_proxy_module; + + static ngx_command_t ngx_http_proxy_commands[] = { { ngx_string("proxy_pass"), @@ -184,6 +204,13 @@ 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_TAKE1, + 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, @@ -212,13 +239,6 @@ offsetof(ngx_http_proxy_loc_conf_t, upstream.intercept_errors), NULL }, - { ngx_string("proxy_redirect_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), - &ngx_conf_deprecated_proxy_redirect_errors }, - { 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, @@ -275,13 +295,6 @@ offsetof(ngx_http_proxy_loc_conf_t, upstream.buffer_size), NULL }, - { ngx_string("proxy_header_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), - &ngx_conf_deprecated_proxy_header_buffer_size }, - { 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, @@ -303,12 +316,79 @@ offsetof(ngx_http_proxy_loc_conf_t, upstream.busy_buffers_size_conf), 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, + 0, + 0, + &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_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 }, + +#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), - (void *) ngx_garbage_collector_temp_handler }, + NULL }, { ngx_string("proxy_max_temp_file_size"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, @@ -331,34 +411,38 @@ offsetof(ngx_http_proxy_loc_conf_t, upstream.next_upstream), &ngx_http_proxy_next_upstream_masks }, - { ngx_string("proxy_upstream_max_fails"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, - ngx_http_proxy_upstream_max_fails_unsupported, - 0, - 0, - NULL }, - - { ngx_string("proxy_upstream_fail_timeout"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, - ngx_http_proxy_upstream_fail_timeout_unsupported, - 0, - 0, - NULL }, - { ngx_string("proxy_pass_header"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, + 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_FLAG, + 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 }, + +#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 }, + +#endif + ngx_null_command }; @@ -401,6 +485,7 @@ { ngx_string("Host"), ngx_string("$proxy_host") }, { ngx_string("Connection"), ngx_string("close") }, { ngx_string("Keep-Alive"), ngx_string("") }, + { ngx_string("Expect"), ngx_string("") }, { ngx_null_string, ngx_null_string } }; @@ -412,18 +497,38 @@ ngx_string("X-Accel-Expires"), ngx_string("X-Accel-Redirect"), ngx_string("X-Accel-Limit-Rate"), - ngx_string("X-Accel-Buffer"), + 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("Keep-Alive"), ngx_string("") }, + { ngx_string("Expect"), ngx_string("") }, + { ngx_string("If-Modified-Since"), ngx_string("") }, + { ngx_string("If-Unmodified-Since"), ngx_string("") }, + { ngx_string("If-None-Match"), ngx_string("") }, + { 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_CHANGABLE|NGX_HTTP_VAR_NOHASH, 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_CHANGABLE|NGX_HTTP_VAR_NOHASH, 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 }, @@ -439,35 +544,60 @@ }; +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; - plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module); - - u = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_t)); - if (u == NULL) { + if (ngx_http_upstream_create(r) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } - u->peer.log = r->connection->log; - u->peer.log_error = NGX_ERROR_ERR; -#if (NGX_THREADS) - u->peer.lock = &r->connection->lock; + ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_proxy_ctx_t)); + if (ctx == NULL) { + return NGX_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) + 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; @@ -484,8 +614,6 @@ u->accel = 1; - r->upstream = u; - rc = ngx_http_read_client_request_body(r, ngx_http_upstream_init); if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { @@ -497,9 +625,214 @@ 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 (ngx_strncasecmp(proxy.data, (u_char *) "http://", 7) == 0) { + + add = 7; + port = 80; + +#if (NGX_HTTP_SSL) + + } else if (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; + } + + } else { + url.uri = r->unparsed_uri; + } + + 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 && url.addrs[0].sockaddr) { + u->resolved->sockaddr = url.addrs[0].sockaddr; + u->resolved->socklen = url.addrs[0].socklen; + u->resolved->naddrs = 1; + u->resolved->host = url.addrs[0].name; + + } else { + 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.len) { + + 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) { + + *key = ctx->vars.uri; + u->uri = ctx->vars.uri; + + return NGX_OK; + + } else if (ctx->vars.uri.len == 0 && r->valid_unparsed_uri && r == r->main) + { + *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->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, loc_len, body_len; + size_t len, uri_len, loc_len, body_len; uintptr_t escape; ngx_buf_t *b; ngx_str_t method; @@ -508,7 +841,7 @@ ngx_list_part_t *part; ngx_table_elt_t *header; ngx_http_upstream_t *u; - ngx_http_proxy_ctx_t *p; + ngx_http_proxy_ctx_t *ctx; ngx_http_script_code_pt code; ngx_http_script_engine_t e, le; ngx_http_proxy_loc_conf_t *plcf; @@ -518,15 +851,6 @@ plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module); - p = ngx_pcalloc(r->pool, sizeof(ngx_http_proxy_ctx_t)); - if (p == NULL) { - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } - - ngx_http_set_ctx(r, p, ngx_http_proxy_module); - - len = sizeof(ngx_http_proxy_version) - 1 + sizeof(CRLF) - 1; - if (u->method.len) { /* HEAD was changed to GET to cache response */ method = u->method; @@ -540,28 +864,44 @@ method.len++; } - len += method.len + u->conf->uri.len; + len = method.len + sizeof(ngx_http_proxy_version) - 1 + sizeof(CRLF) - 1; escape = 0; + loc_len = 0; + unparsed_uri = 0; - loc_len = (r->valid_location && u->conf->uri.len) ? u->conf->location.len: - 0; + ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module); - if (u->conf->uri.len == 0 && r->valid_unparsed_uri && r == r->main) { + if (plcf->proxy_lengths) { + uri_len = ctx->vars.uri.len; + + } else if (ctx->vars.uri.len == 0 && r->valid_unparsed_uri && r == r->main) + { unparsed_uri = 1; - len += r->unparsed_uri.len; + uri_len = r->unparsed_uri.len; } else { - unparsed_uri = 0; - if (r->quoted_uri || r->internal) { + 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); } - len += r->uri.len - loc_len + escape + sizeof("?") - 1 + r->args.len; + 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; } - ngx_http_script_flush_no_cachable_variables(r, plcf->flushes); + len += uri_len; + + ngx_http_script_flush_no_cacheable_variables(r, plcf->flushes); if (plcf->body_set_len) { le.ip = plcf->body_set_len->elts; @@ -574,7 +914,7 @@ body_len += lcode(&le); } - p->internal_body_length = body_len; + ctx->internal_body_length = body_len; len += body_len; } @@ -638,12 +978,15 @@ u->uri.data = b->last; - if (unparsed_uri) { + if (plcf->proxy_lengths) { + 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, u->conf->uri.data, u->conf->uri.len); + b->last = ngx_copy(b->last, ctx->vars.uri.data, ctx->vars.uri.len); } if (escape) { @@ -759,16 +1102,9 @@ b->last = e.pos; } -#if (NGX_DEBUG) - { - ngx_str_t s; - - s.len = b->last - b->pos; - s.data = b->pos; - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http proxy header:\n\"%V\"", &s); - } -#endif + 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 (plcf->body_set == NULL && plcf->upstream.pass_request_body) { @@ -809,20 +1145,21 @@ static ngx_int_t ngx_http_proxy_reinit_request(ngx_http_request_t *r) { - ngx_http_proxy_ctx_t *p; + ngx_http_proxy_ctx_t *ctx; - p = ngx_http_get_module_ctx(r, ngx_http_proxy_module); + ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module); - if (p == NULL) { + if (ctx == NULL) { return NGX_OK; } - p->status = 0; - p->status_count = 0; - p->status_start = NULL; - p->status_end = NULL; + ctx->status.code = 0; + ctx->status.count = 0; + ctx->status.start = NULL; + ctx->status.end = NULL; r->upstream->process_header = ngx_http_proxy_process_status_line; + r->state = 0; return NGX_OK; } @@ -831,25 +1168,36 @@ 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 *p; + ngx_http_proxy_ctx_t *ctx; - p = ngx_http_get_module_ctx(r, ngx_http_proxy_module); + ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module); - if (p == NULL) { - return NGX_HTTP_INTERNAL_SERVER_ERROR; + if (ctx == NULL) { + return NGX_ERROR; } - rc = ngx_http_proxy_parse_status_line(r, p); + u = r->upstream; + + rc = ngx_http_parse_status_line(r, &u->buffer, &ctx->status); if (rc == NGX_AGAIN) { return rc; } - u = r->upstream; + if (rc == NGX_ERROR) { + +#if (NGX_HTTP_CACHE) + + if (r->cache) { + r->http_version = NGX_HTTP_VERSION_9; + return NGX_OK; + } + +#endif - if (rc == NGX_HTTP_PROXY_PARSE_NO_HEADER) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "upstream sent no valid HTTP/1.0 header"); @@ -860,23 +1208,26 @@ #endif r->http_version = NGX_HTTP_VERSION_9; - p->status = NGX_HTTP_OK; + u->state->status = NGX_HTTP_OK; return NGX_OK; } - u->headers_in.status_n = p->status; - u->state->status = p->status; + if (u->state) { + 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.len = p->status_end - p->status_start; - u->headers_in.status_line.data = ngx_palloc(r->pool, - u->headers_in.status_line.len); + u->headers_in.status_line.data = ngx_pnalloc(r->pool, len); if (u->headers_in.status_line.data == NULL) { - return NGX_HTTP_INTERNAL_SERVER_ERROR; + return NGX_ERROR; } - ngx_memcpy(u->headers_in.status_line.data, p->status_start, - u->headers_in.status_line.len); + 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\"", @@ -889,226 +1240,18 @@ static ngx_int_t -ngx_http_proxy_parse_status_line(ngx_http_request_t *r, ngx_http_proxy_ctx_t *p) -{ - u_char ch; - u_char *pos; - ngx_http_upstream_t *u; - enum { - sw_start = 0, - sw_H, - sw_HT, - sw_HTT, - sw_HTTP, - sw_first_major_digit, - sw_major_digit, - sw_first_minor_digit, - sw_minor_digit, - sw_status, - sw_space_after_status, - sw_status_text, - sw_almost_done - } state; - - u = r->upstream; - - state = r->state; - - for (pos = u->buffer.pos; pos < u->buffer.last; pos++) { - ch = *pos; - - switch (state) { - - /* "HTTP/" */ - case sw_start: - switch (ch) { - case 'H': - state = sw_H; - break; - default: - return NGX_HTTP_PROXY_PARSE_NO_HEADER; - } - break; - - case sw_H: - switch (ch) { - case 'T': - state = sw_HT; - break; - default: - return NGX_HTTP_PROXY_PARSE_NO_HEADER; - } - break; - - case sw_HT: - switch (ch) { - case 'T': - state = sw_HTT; - break; - default: - return NGX_HTTP_PROXY_PARSE_NO_HEADER; - } - break; - - case sw_HTT: - switch (ch) { - case 'P': - state = sw_HTTP; - break; - default: - return NGX_HTTP_PROXY_PARSE_NO_HEADER; - } - break; - - case sw_HTTP: - switch (ch) { - case '/': - state = sw_first_major_digit; - break; - default: - return NGX_HTTP_PROXY_PARSE_NO_HEADER; - } - break; - - /* the first digit of major HTTP version */ - case sw_first_major_digit: - if (ch < '1' || ch > '9') { - return NGX_HTTP_PROXY_PARSE_NO_HEADER; - } - - state = sw_major_digit; - break; - - /* the major HTTP version or dot */ - case sw_major_digit: - if (ch == '.') { - state = sw_first_minor_digit; - break; - } - - if (ch < '0' || ch > '9') { - return NGX_HTTP_PROXY_PARSE_NO_HEADER; - } - - break; - - /* the first digit of minor HTTP version */ - case sw_first_minor_digit: - if (ch < '0' || ch > '9') { - return NGX_HTTP_PROXY_PARSE_NO_HEADER; - } - - state = sw_minor_digit; - break; - - /* the minor HTTP version or the end of the request line */ - case sw_minor_digit: - if (ch == ' ') { - state = sw_status; - break; - } - - if (ch < '0' || ch > '9') { - return NGX_HTTP_PROXY_PARSE_NO_HEADER; - } - - break; - - /* HTTP status code */ - case sw_status: - if (ch == ' ') { - break; - } - - if (ch < '0' || ch > '9') { - return NGX_HTTP_PROXY_PARSE_NO_HEADER; - } - - p->status = p->status * 10 + ch - '0'; - - if (++p->status_count == 3) { - state = sw_space_after_status; - p->status_start = pos - 2; - } - - break; - - /* space or end of line */ - case sw_space_after_status: - switch (ch) { - case ' ': - state = sw_status_text; - break; - case '.': /* IIS may send 403.1, 403.2, etc */ - state = sw_status_text; - break; - case CR: - state = sw_almost_done; - break; - case LF: - goto done; - default: - return NGX_HTTP_PROXY_PARSE_NO_HEADER; - } - break; - - /* any text until end of line */ - case sw_status_text: - switch (ch) { - case CR: - state = sw_almost_done; - - break; - case LF: - goto done; - } - break; - - /* end of status line */ - case sw_almost_done: - p->status_end = pos - 1; - switch (ch) { - case LF: - goto done; - default: - return NGX_HTTP_PROXY_PARSE_NO_HEADER; - } - } - } - - u->buffer.pos = pos; - r->state = state; - - return NGX_AGAIN; - -done: - - u->buffer.pos = pos + 1; - - if (p->status_end == NULL) { - p->status_end = pos; - } - - r->state = sw_start; - - return NGX_OK; -} - - -static ngx_int_t ngx_http_proxy_process_header(ngx_http_request_t *r) { ngx_int_t rc; - ngx_uint_t i; 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); - for ( ;; ) { + for ( ;; ) { - rc = ngx_http_parse_header_line(r, &r->upstream->buffer); + rc = ngx_http_parse_header_line(r, &r->upstream->buffer, 1); if (rc == NGX_OK) { @@ -1116,7 +1259,7 @@ h = ngx_list_push(&r->upstream->headers_in.headers); if (h == NULL) { - return NGX_HTTP_INTERNAL_SERVER_ERROR; + return NGX_ERROR; } h->hash = r->header_hash; @@ -1124,10 +1267,10 @@ h->key.len = r->header_name_end - r->header_name_start; h->value.len = r->header_end - r->header_start; - h->key.data = ngx_palloc(r->pool, + h->key.data = ngx_pnalloc(r->pool, h->key.len + 1 + h->value.len + 1 + h->key.len); if (h->key.data == NULL) { - return NGX_HTTP_INTERNAL_SERVER_ERROR; + return NGX_ERROR; } h->value.data = h->key.data + h->key.len + 1; @@ -1140,16 +1283,14 @@ ngx_memcpy(h->lowcase_key, r->lowcase_header, h->key.len); } else { - for (i = 0; i < h->key.len; i++) { - h->lowcase_key[i] = ngx_tolower(h->key.data[i]); - } + 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_HTTP_INTERNAL_SERVER_ERROR; + return NGX_ERROR; } ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, @@ -1174,31 +1315,27 @@ if (r->upstream->headers_in.server == NULL) { h = ngx_list_push(&r->upstream->headers_in.headers); if (h == NULL) { - return NGX_HTTP_INTERNAL_SERVER_ERROR; + return NGX_ERROR; } h->hash = ngx_hash(ngx_hash(ngx_hash(ngx_hash( ngx_hash('s', 'e'), 'r'), 'v'), 'e'), 'r'); - h->key.len = sizeof("Server") - 1; - h->key.data = (u_char *) "Server"; - h->value.len = 0; - h->value.data = NULL; + 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_HTTP_INTERNAL_SERVER_ERROR; + return NGX_ERROR; } h->hash = ngx_hash(ngx_hash(ngx_hash('d', 'a'), 't'), 'e'); - h->key.len = sizeof("Date") - 1; - h->key.data = (u_char *) "Date"; - h->value.len = 0; - h->value.data = NULL; + ngx_str_set(&h->key, "Date"); + ngx_str_null(&h->value); h->lowcase_key = (u_char *) "date"; } @@ -1243,15 +1380,20 @@ ngx_http_proxy_host_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) { - ngx_http_proxy_loc_conf_t *plcf; + ngx_http_proxy_ctx_t *ctx; - plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module); + ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module); - v->len = plcf->host_header.len; + if (ctx == NULL) { + v->not_found = 1; + return NGX_OK; + } + + v->len = ctx->vars.host_header.len; v->valid = 1; - v->no_cachable = 0; + v->no_cacheable = 0; v->not_found = 0; - v->data = plcf->host_header.data; + v->data = ctx->vars.host_header.data; return NGX_OK; } @@ -1261,15 +1403,20 @@ ngx_http_proxy_port_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) { - ngx_http_proxy_loc_conf_t *plcf; + ngx_http_proxy_ctx_t *ctx; - plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module); + ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module); + + if (ctx == NULL) { + v->not_found = 1; + return NGX_OK; + } - v->len = plcf->port.len; + v->len = ctx->vars.port.len; v->valid = 1; - v->no_cachable = 0; + v->no_cacheable = 0; v->not_found = 0; - v->data = plcf->port.data; + v->data = ctx->vars.port.data; return NGX_OK; } @@ -1282,7 +1429,7 @@ u_char *p; v->valid = 1; - v->no_cachable = 0; + v->no_cacheable = 0; v->not_found = 0; if (r->headers_in.x_forwarded_for == NULL) { @@ -1294,7 +1441,7 @@ v->len = r->headers_in.x_forwarded_for->value.len + sizeof(", ") - 1 + r->connection->addr_text.len; - p = ngx_palloc(r->pool, v->len); + p = ngx_pnalloc(r->pool, v->len); if (p == NULL) { return NGX_ERROR; } @@ -1316,26 +1463,26 @@ 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 *p; + ngx_http_proxy_ctx_t *ctx; - p = ngx_http_get_module_ctx(r, ngx_http_proxy_module); + ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module); - if (p == NULL) { + if (ctx == NULL) { v->not_found = 1; return NGX_OK; } v->valid = 1; - v->no_cachable = 0; + v->no_cacheable = 0; v->not_found = 0; - v->data = ngx_palloc(r->connection->pool, NGX_SIZE_T_LEN); + v->data = ngx_pnalloc(r->connection->pool, NGX_SIZE_T_LEN); if (v->data == NULL) { return NGX_ERROR; } - v->len = ngx_sprintf(v->data, "%uz", p->internal_body_length) - v->data; + v->len = ngx_sprintf(v->data, "%uz", ctx->internal_body_length) - v->data; return NGX_OK; } @@ -1384,16 +1531,14 @@ return NGX_DECLINED; } - len = prefix + pr->replacement.text.len + h->value.len - pr->redirect.len; + len = pr->replacement.text.len + h->value.len - pr->redirect.len; - data = ngx_palloc(r->pool, len); + data = ngx_pnalloc(r->pool, len); if (data == NULL) { return NGX_ERROR; } - p = data; - - p = ngx_copy(p, h->value.data, prefix); + p = ngx_copy(data, h->value.data, prefix); if (pr->replacement.text.len) { p = ngx_copy(p, pr->replacement.text.data, pr->replacement.text.len); @@ -1431,21 +1576,19 @@ e.ip = pr->replacement.vars.lengths; e.request = r; - len = prefix + h->value.len - pr->redirect.len; + len = h->value.len - pr->redirect.len; while (*(uintptr_t *) e.ip) { lcode = *(ngx_http_script_len_code_pt *) e.ip; len += lcode(&e); } - data = ngx_palloc(r->pool, len); + data = ngx_pnalloc(r->pool, len); if (data == NULL) { return NGX_ERROR; } - p = data; - - p = ngx_copy(p, h->value.data, prefix); + p = ngx_copy(data, h->value.data, prefix); e.ip = pr->replacement.vars.values; e.pos = p; @@ -1491,19 +1634,19 @@ conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_proxy_loc_conf_t)); if (conf == NULL) { - return NGX_CONF_ERROR; + return NULL; } /* * set by ngx_pcalloc(): * * conf->upstream.bufs.num = 0; + * conf->upstream.ignore_headers = 0; * conf->upstream.next_upstream = 0; + * 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.hide_headers = NULL; - * conf->upstream.pass_headers = NULL; - * conf->upstream.schema = { 0, NULL }; * conf->upstream.uri = { 0, NULL }; * conf->upstream.location = NULL; * conf->upstream.store_lengths = NULL; @@ -1517,7 +1660,7 @@ * conf->body_set_len = NULL; * conf->body_set = NULL; * conf->body_source = { 0, NULL }; - * conf->rewrite_locations = NULL; + * conf->redirects = NULL; */ conf->upstream.store = NGX_CONF_UNSET; @@ -1539,7 +1682,21 @@ 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_PTR; + conf->upstream.cache_min_uses = NGX_CONF_UNSET_UINT; + conf->upstream.cache_bypass = NGX_CONF_UNSET_PTR; + conf->upstream.no_cache = NGX_CONF_UNSET_PTR; + conf->upstream.cache_valid = NGX_CONF_UNSET_PTR; +#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; +#endif /* "proxy_cyclic_temp_file" is disabled */ conf->upstream.cyclic_temp_file = 0; @@ -1560,22 +1717,16 @@ ngx_http_proxy_loc_conf_t *prev = parent; ngx_http_proxy_loc_conf_t *conf = child; - u_char *p; - size_t size; - uintptr_t *code; - ngx_str_t *header; - ngx_uint_t i, j; - ngx_array_t hide_headers; - ngx_keyval_t *src, *s, *h; - ngx_hash_key_t *hk; - ngx_hash_init_t hash; - ngx_http_proxy_redirect_t *pr; - ngx_http_script_compile_t sc; - ngx_http_script_copy_code_t *copy; + size_t size; + ngx_keyval_t *s; + ngx_hash_init_t hash; + ngx_http_core_loc_conf_t *clcf; + ngx_http_proxy_redirect_t *pr; + ngx_http_script_compile_t sc; if (conf->upstream.store != 0) { ngx_conf_merge_value(conf->upstream.store, - prev->upstream.store, 0); + prev->upstream.store, 0); if (conf->upstream.store_lengths == NULL) { conf->upstream.store_lengths = prev->upstream.store_lengths; @@ -1699,6 +1850,11 @@ } + 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 @@ -1710,10 +1866,71 @@ |NGX_HTTP_UPSTREAM_FT_OFF; } - ngx_conf_merge_path_value(conf->upstream.temp_path, + if (ngx_conf_merge_path_value(cf, &conf->upstream.temp_path, prev->upstream.temp_path, - NGX_HTTP_PROXY_TEMP_PATH, 1, 2, 0, - ngx_garbage_collector_temp_handler, cf); + &ngx_http_proxy_temp_path) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + + +#if (NGX_HTTP_CACHE) + + ngx_conf_merge_ptr_value(conf->upstream.cache, + prev->upstream.cache, NULL); + + if (conf->upstream.cache && conf->upstream.cache->data == NULL) { + ngx_shm_zone_t *shm_zone; + + shm_zone = conf->upstream.cache; + + 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_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_methods == 0) { + conf->upstream.cache_methods = prev->upstream.cache_methods; + } + + conf->upstream.cache_methods |= NGX_HTTP_GET|NGX_HTTP_HEAD; + + 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; + } + + 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); + + if (conf->upstream.no_cache && conf->upstream.cache_bypass == NULL) { + ngx_log_error(NGX_LOG_WARN, cf->log, 0, + "\"proxy_no_cache\" functionality has been changed in 0.8.46, " + "now it should be used together with \"proxy_cache_bypass\""); + } + + 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; + } + +#endif if (conf->method.len == 0) { conf->method = prev->method; @@ -1731,6 +1948,11 @@ 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); +#endif + ngx_conf_merge_value(conf->redirect, prev->redirect, 1); if (conf->redirect) { @@ -1739,7 +1961,7 @@ conf->redirects = prev->redirects; } - if (conf->redirects == NULL && conf->upstream.url.data) { + if (conf->redirects == NULL && conf->url.data) { conf->redirects = ngx_array_create(cf->pool, 1, sizeof(ngx_http_proxy_redirect_t)); @@ -1753,136 +1975,60 @@ } pr->handler = ngx_http_proxy_rewrite_redirect_text; - pr->redirect = conf->upstream.url; + pr->redirect = conf->url; - if (conf->upstream.uri.len) { - pr->replacement.text = conf->upstream.location; + if (conf->vars.uri.len) { + pr->replacement.text = conf->location; } else { - pr->replacement.text.len = 0; - pr->replacement.text.data = NULL; + ngx_str_null(&pr->replacement.text); } } } - 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); - - if (conf->upstream.hide_headers == NULL - && conf->upstream.pass_headers == NULL) - { - conf->upstream.hide_headers = prev->upstream.hide_headers; - conf->upstream.pass_headers = prev->upstream.pass_headers; - conf->upstream.hide_headers_hash = prev->upstream.hide_headers_hash; - - if (conf->upstream.hide_headers_hash.buckets) { - goto peers; - } - - } else { - if (conf->upstream.hide_headers == NULL) { - conf->upstream.hide_headers = prev->upstream.hide_headers; - } - - if (conf->upstream.pass_headers == NULL) { - conf->upstream.pass_headers = prev->upstream.pass_headers; - } - } - - if (ngx_array_init(&hide_headers, cf->temp_pool, 4, sizeof(ngx_hash_key_t)) - != NGX_OK) - { - return NGX_CONF_ERROR; - } - - for (header = ngx_http_proxy_hide_headers; header->len; header++) { - hk = ngx_array_push(&hide_headers); - if (hk == NULL) { - return NGX_CONF_ERROR; - } - - hk->key = *header; - hk->key_hash = ngx_hash_key_lc(header->data, header->len); - hk->value = (void *) 1; - } - - if (conf->upstream.hide_headers) { - - header = conf->upstream.hide_headers->elts; - - for (i = 0; i < conf->upstream.hide_headers->nelts; i++) { - - hk = hide_headers.elts; - - for (j = 0; j < hide_headers.nelts; j++) { - if (ngx_strcasecmp(header[i].data, hk[j].key.data) == 0) { - goto exist; - } - } - - hk = ngx_array_push(&hide_headers); - if (hk == NULL) { - return NGX_CONF_ERROR; - } - - hk->key = header[i]; - hk->key_hash = ngx_hash_key_lc(header[i].data, header[i].len); - hk->value = (void *) 1; - - exist: - - continue; - } +#if (NGX_HTTP_SSL) + if (conf->upstream.ssl == NULL) { + conf->upstream.ssl = prev->upstream.ssl; } +#endif - if (conf->upstream.pass_headers) { - - hk = hide_headers.elts; - header = conf->upstream.pass_headers->elts; - - for (i = 0; i < conf->upstream.pass_headers->nelts; i++) { - for (j = 0; j < hide_headers.nelts; j++) { + ngx_conf_merge_uint_value(conf->headers_hash_max_size, + prev->headers_hash_max_size, 512); - if (hk[j].key.data == NULL) { - continue; - } + ngx_conf_merge_uint_value(conf->headers_hash_bucket_size, + prev->headers_hash_bucket_size, 64); - if (ngx_strcasecmp(header[i].data, hk[j].key.data) == 0) { - hk[j].key.data = NULL; - break; - } - } - } - } + conf->headers_hash_bucket_size = ngx_align(conf->headers_hash_bucket_size, + ngx_cacheline_size); - hash.hash = &conf->upstream.hide_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; - if (ngx_hash_init(&hash, hide_headers.elts, hide_headers.nelts) != NGX_OK) { + if (ngx_http_upstream_hide_headers_hash(cf, &conf->upstream, + &prev->upstream, ngx_http_proxy_hide_headers, &hash) + != NGX_OK) + { return NGX_CONF_ERROR; } -peers: - if (conf->upstream.upstream == NULL) { conf->upstream.upstream = prev->upstream.upstream; + conf->vars = prev->vars; + } - conf->host_header = prev->host_header; - conf->port = prev->port; - conf->upstream.schema = prev->upstream.schema; + if (conf->proxy_lengths == NULL) { + conf->proxy_lengths = prev->proxy_lengths; + conf->proxy_values = prev->proxy_values; } + if (conf->upstream.upstream || conf->proxy_lengths) { + clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); + if (clcf->handler == NULL && clcf->lmt_excpt) { + clcf->handler = ngx_http_proxy_handler; + conf->location = prev->location; + } + } if (conf->body_source.data == NULL) { conf->body_source = prev->body_source; @@ -1919,12 +2065,32 @@ return NGX_CONF_ERROR; } - s->key.len = sizeof("Content-Length") - 1; - s->key.data = (u_char *) "Content-Length"; - s->value.len = sizeof("$proxy_internal_body_length") - 1; - s->value.data = (u_char *) "$proxy_internal_body_length"; + ngx_str_set(&s->key, "Content-Length"); + ngx_str_set(&s->value, "$proxy_internal_body_length"); + } + + if (ngx_http_proxy_merge_headers(cf, conf, prev) != NGX_OK) { + return NGX_CONF_ERROR; } + return NGX_CONF_OK; +} + + +static ngx_int_t +ngx_http_proxy_merge_headers(ngx_conf_t *cf, ngx_http_proxy_loc_conf_t *conf, + ngx_http_proxy_loc_conf_t *prev) +{ + u_char *p; + size_t size; + uintptr_t *code; + ngx_uint_t i; + ngx_array_t headers_names; + 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 (conf->headers_source == NULL) { conf->flushes = prev->flushes; @@ -1934,38 +2100,54 @@ conf->headers_source = prev->headers_source; } - if (conf->headers_set_hash.buckets) { - return NGX_CONF_OK; + if (conf->headers_set_hash.buckets +#if (NGX_HTTP_CACHE) + && ((conf->upstream.cache == NULL) == (prev->upstream.cache == NULL)) +#endif + ) + { + return NGX_OK; } - conf->headers_names = ngx_array_create(cf->pool, 4, sizeof(ngx_hash_key_t)); - if (conf->headers_names == NULL) { - return NGX_CONF_ERROR; + if (ngx_array_init(&headers_names, cf->temp_pool, 4, sizeof(ngx_hash_key_t)) + != NGX_OK) + { + return NGX_ERROR; } if (conf->headers_source == NULL) { conf->headers_source = ngx_array_create(cf->pool, 4, sizeof(ngx_keyval_t)); if (conf->headers_source == NULL) { - return NGX_CONF_ERROR; + return NGX_ERROR; } } conf->headers_set_len = ngx_array_create(cf->pool, 64, 1); if (conf->headers_set_len == NULL) { - return NGX_CONF_ERROR; + return NGX_ERROR; } conf->headers_set = ngx_array_create(cf->pool, 512, 1); if (conf->headers_set == NULL) { - return NGX_CONF_ERROR; + return NGX_ERROR; } src = conf->headers_source->elts; - for (h = ngx_http_proxy_headers; h->key.len; h++) { +#if (NGX_HTTP_CACHE) + + h = conf->upstream.cache ? ngx_http_proxy_cache_headers: + ngx_http_proxy_headers; +#else + + h = ngx_http_proxy_headers; + +#endif + + while (h->key.len) { for (i = 0; i < conf->headers_source->nelts; i++) { if (ngx_strcasecmp(h->key.data, src[i].key.data) == 0) { @@ -1975,7 +2157,7 @@ s = ngx_array_push(conf->headers_source); if (s == NULL) { - return NGX_CONF_ERROR; + return NGX_ERROR; } *s = *h; @@ -1984,16 +2166,16 @@ next: - continue; + h++; } src = conf->headers_source->elts; for (i = 0; i < conf->headers_source->nelts; i++) { - hk = ngx_array_push(conf->headers_names); + hk = ngx_array_push(&headers_names); if (hk == NULL) { - return NGX_CONF_ERROR; + return NGX_ERROR; } hk->key = src[i].key; @@ -2008,7 +2190,7 @@ copy = ngx_array_push_n(conf->headers_set_len, sizeof(ngx_http_script_copy_code_t)); if (copy == NULL) { - return NGX_CONF_ERROR; + return NGX_ERROR; } copy->code = (ngx_http_script_code_pt) @@ -2025,7 +2207,7 @@ copy = ngx_array_push_n(conf->headers_set, size); if (copy == NULL) { - return NGX_CONF_ERROR; + return NGX_ERROR; } copy->code = ngx_http_script_copy_code; @@ -2043,7 +2225,7 @@ copy = ngx_array_push_n(conf->headers_set_len, sizeof(ngx_http_script_copy_code_t)); if (copy == NULL) { - return NGX_CONF_ERROR; + return NGX_ERROR; } copy->code = (ngx_http_script_code_pt) @@ -2057,7 +2239,7 @@ copy = ngx_array_push_n(conf->headers_set, size); if (copy == NULL) { - return NGX_CONF_ERROR; + return NGX_ERROR; } copy->code = ngx_http_script_copy_code; @@ -2077,14 +2259,14 @@ sc.values = &conf->headers_set; if (ngx_http_script_compile(&sc) != NGX_OK) { - return NGX_CONF_ERROR; + return NGX_ERROR; } copy = ngx_array_push_n(conf->headers_set_len, sizeof(ngx_http_script_copy_code_t)); if (copy == NULL) { - return NGX_CONF_ERROR; + return NGX_ERROR; } copy->code = (ngx_http_script_code_pt) @@ -2098,7 +2280,7 @@ copy = ngx_array_push_n(conf->headers_set, size); if (copy == NULL) { - return NGX_CONF_ERROR; + return NGX_ERROR; } copy->code = ngx_http_script_copy_code; @@ -2110,14 +2292,14 @@ code = ngx_array_push_n(conf->headers_set_len, sizeof(uintptr_t)); if (code == NULL) { - return NGX_CONF_ERROR; + return NGX_ERROR; } *code = (uintptr_t) NULL; code = ngx_array_push_n(conf->headers_set, sizeof(uintptr_t)); if (code == NULL) { - return NGX_CONF_ERROR; + return NGX_ERROR; } *code = (uintptr_t) NULL; @@ -2125,7 +2307,7 @@ code = ngx_array_push_n(conf->headers_set_len, sizeof(uintptr_t)); if (code == NULL) { - return NGX_CONF_ERROR; + return NGX_ERROR; } *code = (uintptr_t) NULL; @@ -2139,14 +2321,7 @@ hash.pool = cf->pool; hash.temp_pool = NULL; - if (ngx_hash_init(&hash, conf->headers_names->elts, - conf->headers_names->nelts) - != NGX_OK) - { - return NGX_CONF_ERROR; - } - - return NGX_CONF_OK; + return ngx_hash_init(&hash, headers_names.elts, headers_names.nelts); } @@ -2155,57 +2330,70 @@ { ngx_http_proxy_loc_conf_t *plcf = conf; - u_char *p; - size_t add; - u_short port; - ngx_str_t *value, *url; - ngx_url_t u; - ngx_http_core_loc_conf_t *clcf; -#if (NGX_HTTP_SSL) - ngx_pool_cleanup_t *cln; -#endif + 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.schema.len) { + 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]; - if (ngx_strncasecmp(url->data, (u_char *) "http://", 7) == 0) { - add = 7; - port = 80; + n = ngx_http_script_variables_count(url); - } else if (ngx_strncasecmp(url->data, (u_char *) "https://", 8) == 0) { + if (n) { -#if (NGX_HTTP_SSL) + ngx_memzero(&sc, sizeof(ngx_http_script_compile_t)); - add = 8; - port = 443; + 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; - plcf->upstream.ssl = ngx_pcalloc(cf->pool, sizeof(ngx_ssl_t)); - if (plcf->upstream.ssl == NULL) { + if (ngx_http_script_compile(&sc) != NGX_OK) { return NGX_CONF_ERROR; } - plcf->upstream.ssl->log = cf->log; - - if (ngx_ssl_create(plcf->upstream.ssl, - NGX_SSL_SSLv2|NGX_SSL_SSLv3|NGX_SSL_TLSv1, NULL) - != NGX_OK) - { +#if (NGX_HTTP_SSL) + if (ngx_http_proxy_set_ssl(cf, plcf) != NGX_OK) { return NGX_CONF_ERROR; } +#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) { - cln = ngx_pool_cleanup_add(cf->pool, 0); - if (cln == NULL) { +#if (NGX_HTTP_SSL) + if (ngx_http_proxy_set_ssl(cf, plcf) != NGX_OK) { return NGX_CONF_ERROR; } - cln->handler = ngx_ssl_cleanup_ctx; - cln->data = plcf->upstream.ssl; - + add = 8; + port = 443; #else ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "https protocol requires SSL support"); @@ -2230,51 +2418,13 @@ return NGX_CONF_ERROR; } - if (!u.unix_socket) { - if (u.no_port || u.port == port) { - plcf->host_header = u.host; - - if (port == 80) { - plcf->port.len = sizeof("80") - 1; - plcf->port.data = (u_char *) "80"; - - } else { - plcf->port.len = sizeof("443") - 1; - plcf->port.data = (u_char *) "443"; - } - - } else { - p = ngx_palloc(cf->pool, u.host.len + sizeof(":65536") - 1); - if (p == NULL) { - return NGX_CONF_ERROR; - } - - plcf->host_header.len = ngx_sprintf(p, "%V:%d", &u.host, u.port) - - p; - plcf->host_header.data = p; - - plcf->port.len = plcf->host_header.len - u.host.len - 1; - plcf->port.data = p + u.host.len + 1; - } - - - } else { - plcf->host_header.len = sizeof("localhost") - 1; - plcf->host_header.data = (u_char *) "localhost"; - plcf->port.len = 0; - plcf->port.data = (u_char *) ""; - } - - plcf->upstream.uri = u.uri; - - plcf->upstream.schema.len = add; - plcf->upstream.schema.data = url->data; + plcf->vars.schema.len = add; + plcf->vars.schema.data = url->data; + plcf->vars.key_start = plcf->vars.schema; - clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); - - clcf->handler = ngx_http_proxy_handler; + ngx_http_proxy_set_vars(&u, &plcf->vars); - plcf->upstream.location = clcf->name; + plcf->location = clcf->name; if (clcf->named #if (NGX_PCRE) @@ -2282,7 +2432,7 @@ #endif || clcf->noname) { - if (plcf->upstream.uri.len) { + if (plcf->vars.uri.len) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "\"proxy_pass\" may not have URI part in " "location given by regular expression, " @@ -2292,14 +2442,10 @@ return NGX_CONF_ERROR; } - plcf->upstream.location.len = 0; + plcf->location.len = 0; } - plcf->upstream.url = *url; - - if (clcf->name.data[clcf->name.len - 1] == '/') { - clcf->auto_redirect = 1; - } + plcf->url = *url; return NGX_CONF_OK; } @@ -2321,10 +2467,26 @@ value = cf->args->elts; - if (ngx_strcmp(value[1].data, "off") == 0) { - plcf->redirect = 0; - plcf->redirects = NULL; - return NGX_CONF_OK; + 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) { @@ -2340,23 +2502,29 @@ return NGX_CONF_ERROR; } - if (cf->args->nelts == 2 && ngx_strcmp(value[1].data, "default") == 0) { - if (plcf->upstream.url.data == NULL) { + if (ngx_strcmp(value[1].data, "default") == 0) { + if (plcf->proxy_lengths) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "\"proxy_redirect default\" may not 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_rewrite_location default\" must go " + "\"proxy_redirect default\" must go " "after the \"proxy_pass\" directive"); return NGX_CONF_ERROR; } pr->handler = ngx_http_proxy_rewrite_redirect_text; - pr->redirect = plcf->upstream.url; + pr->redirect = plcf->url; - if (plcf->upstream.uri.len) { - pr->replacement.text = plcf->upstream.location; + if (plcf->vars.uri.len) { + pr->replacement.text = plcf->location; } else { - pr->replacement.text.len = 0; - pr->replacement.text.data = NULL; + ngx_str_null(&pr->replacement.text); } return NGX_CONF_OK; @@ -2403,20 +2571,31 @@ ngx_str_t *value; ngx_http_script_compile_t sc; - if (plcf->upstream.store != NGX_CONF_UNSET || plcf->upstream.store_lengths) + if (plcf->upstream.store != NGX_CONF_UNSET + || plcf->upstream.store_lengths) { return "is duplicate"; } value = cf->args->elts; - if (ngx_strcmp(value[1].data, "on") == 0) { - plcf->upstream.store = 1; + if (ngx_strcmp(value[1].data, "off") == 0) { + plcf->upstream.store = 0; return NGX_CONF_OK; } - if (ngx_strcmp(value[1].data, "off") == 0) { - plcf->upstream.store = 0; +#if (NGX_HTTP_CACHE) + + if (plcf->upstream.cache != NGX_CONF_UNSET_PTR + && plcf->upstream.cache != NULL) + { + return "is incompatible with \"proxy_cache\""; + } + +#endif + + if (ngx_strcmp(value[1].data, "on") == 0) { + plcf->upstream.store = 1; return NGX_CONF_OK; } @@ -2429,7 +2608,7 @@ 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.variables = ngx_http_script_variables_count(&value[1]); sc.complete_lengths = 1; sc.complete_values = 1; @@ -2441,6 +2620,70 @@ } +#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; + + value = cf->args->elts; + + if (plcf->upstream.cache != NGX_CONF_UNSET_PTR) { + return "is duplicate"; + } + + if (ngx_strcmp(value[1].data, "off") == 0) { + plcf->upstream.cache = NULL; + return NGX_CONF_OK; + } + + if (plcf->upstream.store > 0 || plcf->upstream.store_lengths) { + return "is incompatible with \"proxy_store\""; + } + + plcf->upstream.cache = ngx_shared_memory_add(cf, &value[1], 0, + &ngx_http_proxy_module); + if (plcf->upstream.cache == 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.len) { + 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 + + static char * ngx_http_proxy_lowat_check(ngx_conf_t *cf, void *post, void *data) { @@ -2470,27 +2713,70 @@ } -static char * -ngx_http_proxy_upstream_max_fails_unsupported(ngx_conf_t *cf, - ngx_command_t *cmd, void *conf) +#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_conf_log_error(NGX_LOG_EMERG, cf, 0, - "\"proxy_upstream_max_fails\" is not supported, " - "use the \"max_fails\" parameter of the \"server\" directive ", - "inside the \"upstream\" block"); + 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, + NGX_SSL_SSLv2|NGX_SSL_SSLv3|NGX_SSL_TLSv1, 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; - return NGX_CONF_ERROR; + return NGX_OK; } +#endif + -static char * -ngx_http_proxy_upstream_fail_timeout_unsupported(ngx_conf_t *cf, - ngx_command_t *cmd, void *conf) +static void +ngx_http_proxy_set_vars(ngx_url_t *u, ngx_http_proxy_vars_t *v) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "\"proxy_upstream_fail_timeout\" is not supported, " - "use the \"fail_timeout\" parameter of the \"server\" directive ", - "inside the \"upstream\" block"); + 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; + } - return NGX_CONF_ERROR; + v->uri = u->uri; } diff -Nru nginx-0.5.33/src/http/modules/ngx_http_random_index_module.c nginx-0.8.53/src/http/modules/ngx_http_random_index_module.c --- nginx-0.5.33/src/http/modules/ngx_http_random_index_module.c 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/src/http/modules/ngx_http_random_index_module.c 2010-05-24 12:35:10.000000000 +0000 @@ -0,0 +1,316 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#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 configration */ + ngx_http_random_index_merge_loc_conf /* merge location configration */ +}; + + +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 " \"%s\" 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; +} diff -Nru nginx-0.5.33/src/http/modules/ngx_http_range_filter_module.c nginx-0.8.53/src/http/modules/ngx_http_range_filter_module.c --- nginx-0.5.33/src/http/modules/ngx_http_range_filter_module.c 2007-05-06 18:15:32.000000000 +0000 +++ nginx-0.8.53/src/http/modules/ngx_http_range_filter_module.c 2010-05-14 09:56:37.000000000 +0000 @@ -58,6 +58,20 @@ } ngx_http_range_filter_ctx_t; +ngx_int_t ngx_http_range_parse(ngx_http_request_t *r, + ngx_http_range_filter_ctx_t *ctx); +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); @@ -131,14 +145,8 @@ static ngx_int_t ngx_http_range_header_filter(ngx_http_request_t *r) { - u_char *p; - size_t len; - off_t start, end; + time_t if_range; ngx_int_t rc; - ngx_uint_t suffix, i; - ngx_atomic_uint_t boundary; - ngx_table_elt_t *content_range; - ngx_http_range_t *range; ngx_http_range_filter_ctx_t *ctx; if (r->http_version < NGX_HTTP_VERSION_10 @@ -156,18 +164,21 @@ (u_char *) "bytes=", 6) != 0) { - r->headers_out.accept_ranges = ngx_list_push(&r->headers_out.headers); - if (r->headers_out.accept_ranges == NULL) { - return NGX_ERROR; - } + goto next_filter; + } - r->headers_out.accept_ranges->hash = 1; - r->headers_out.accept_ranges->key.len = sizeof("Accept-Ranges") - 1; - r->headers_out.accept_ranges->key.data = (u_char *) "Accept-Ranges"; - r->headers_out.accept_ranges->value.len = sizeof("bytes") - 1; - r->headers_out.accept_ranges->value.data = (u_char *) "bytes"; + if (r->headers_in.if_range && r->headers_out.last_modified_time != -1) { - return ngx_http_next_header_filter(r); + if_range = ngx_http_parse_time(r->headers_in.if_range->value.data, + r->headers_in.if_range->value.len); + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http ir:%d lm:%d", + if_range, r->headers_out.last_modified_time); + + if (if_range != r->headers_out.last_modified_time) { + goto next_filter; + } } ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_range_filter_ctx_t)); @@ -176,13 +187,58 @@ } if (ngx_array_init(&ctx->ranges, r->pool, 1, sizeof(ngx_http_range_t)) - == NGX_ERROR) + != NGX_OK) { return NGX_ERROR; } - rc = 0; - range = NULL; + rc = ngx_http_range_parse(r, ctx); + + if (rc == 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); + } + + if (rc == NGX_HTTP_RANGE_NOT_SATISFIABLE) { + return ngx_http_range_not_satisfiable(r); + } + + /* rc == NGX_ERROR */ + + return rc; + +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); +} + + +ngx_int_t +ngx_http_range_parse(ngx_http_request_t *r, ngx_http_range_filter_ctx_t *ctx) +{ + u_char *p; + off_t start, end; + ngx_uint_t suffix; + ngx_http_range_t *range; + p = r->headers_in.range->value.data + 6; for ( ;; ) { @@ -194,8 +250,7 @@ if (*p != '-') { if (*p < '0' || *p > '9') { - rc = NGX_HTTP_RANGE_NOT_SATISFIABLE; - break; + return NGX_HTTP_RANGE_NOT_SATISFIABLE; } while (*p >= '0' && *p <= '9') { @@ -205,13 +260,11 @@ while (*p == ' ') { p++; } if (*p++ != '-') { - rc = NGX_HTTP_RANGE_NOT_SATISFIABLE; - break; + return NGX_HTTP_RANGE_NOT_SATISFIABLE; } if (start >= r->headers_out.content_length_n) { - rc = NGX_HTTP_RANGE_NOT_SATISFIABLE; - break; + return NGX_HTTP_RANGE_NOT_SATISFIABLE; } while (*p == ' ') { p++; } @@ -226,7 +279,7 @@ range->end = r->headers_out.content_length_n; if (*p++ != ',') { - break; + return NGX_OK; } continue; @@ -238,8 +291,7 @@ } if (*p < '0' || *p > '9') { - rc = NGX_HTTP_RANGE_NOT_SATISFIABLE; - break; + return NGX_HTTP_RANGE_NOT_SATISFIABLE; } while (*p >= '0' && *p <= '9') { @@ -249,8 +301,7 @@ while (*p == ' ') { p++; } if (*p != ',' && *p != '\0') { - rc = NGX_HTTP_RANGE_NOT_SATISFIABLE; - break; + return NGX_HTTP_RANGE_NOT_SATISFIABLE; } if (suffix) { @@ -259,8 +310,7 @@ } if (start > end) { - rc = NGX_HTTP_RANGE_NOT_SATISFIABLE; - break; + return NGX_HTTP_RANGE_NOT_SATISFIABLE; } range = ngx_array_push(&ctx->ranges); @@ -282,86 +332,64 @@ } if (*p++ != ',') { - break; + return NGX_OK; } } +} - if (rc) { - - /* rc == NGX_HTTP_RANGE_NOT_SATISFIABLE */ - - r->headers_out.status = rc; - - 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; - content_range->key.len = sizeof("Content-Range") - 1; - content_range->key.data = (u_char *) "Content-Range"; - - content_range->value.data = ngx_palloc(r->pool, - sizeof("bytes */") - 1 + NGX_OFF_T_LEN); - if (content_range->value.data == 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); +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; - return rc; + content_range = ngx_list_push(&r->headers_out.headers); + if (content_range == NULL) { + return NGX_ERROR; } - ngx_http_set_ctx(r, ctx, ngx_http_range_body_filter_module); - - r->headers_out.status = NGX_HTTP_PARTIAL_CONTENT; - - if (ctx->ranges.nelts == 1) { - - content_range = ngx_list_push(&r->headers_out.headers); - if (content_range == NULL) { - return NGX_ERROR; - } + r->headers_out.content_range = content_range; - r->headers_out.content_range = content_range; + content_range->hash = 1; + ngx_str_set(&content_range->key, "Content-Range"); - content_range->hash = 1; - content_range->key.len = sizeof("Content-Range") - 1; - content_range->key.data = (u_char *) "Content-Range"; - - content_range->value.data = - ngx_palloc(r->pool, sizeof("bytes -/") - 1 + 3 * NGX_OFF_T_LEN); - if (content_range->value.data == NULL) { - return NGX_ERROR; - } + content_range->value.data = ngx_pnalloc(r->pool, + sizeof("bytes -/") - 1 + 3 * NGX_OFF_T_LEN); + if (content_range->value.data == NULL) { + return NGX_ERROR; + } - /* "Content-Range: bytes SSSS-EEEE/TTTT" header */ + /* "Content-Range: bytes SSSS-EEEE/TTTT" header */ - 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; + range = ctx->ranges.elts; - r->headers_out.content_length_n = range->end - range->start; + 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; - if (r->headers_out.content_length) { - r->headers_out.content_length->hash = 0; - r->headers_out.content_length = NULL; - } + r->headers_out.content_length_n = range->end - range->start; - return ngx_http_next_header_filter(r); + 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); +} + - /* TODO: what if no content_type ?? */ +static ngx_int_t +ngx_http_range_multipart_header(ngx_http_request_t *r, + ngx_http_range_filter_ctx_t *ctx) +{ + size_t len; + ngx_uint_t i; + ngx_http_range_t *range; + ngx_atomic_uint_t boundary; len = sizeof(CRLF "--") - 1 + NGX_ATOMIC_T_LEN + sizeof(CRLF "Content-Type: ") - 1 @@ -372,7 +400,7 @@ len += sizeof("; charset=") - 1 + r->headers_out.charset.len; } - ctx->boundary_header.data = ngx_palloc(r->pool, len); + ctx->boundary_header.data = ngx_pnalloc(r->pool, len); if (ctx->boundary_header.data == NULL) { return NGX_ERROR; } @@ -399,7 +427,7 @@ r->headers_out.charset.len = 0; - } else { + } 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 @@ -407,17 +435,26 @@ 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_palloc(r->pool, - sizeof("Content-Type: multipart/byteranges; boundary=") - 1 - + NGX_ATOMIC_T_LEN); + 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 = @@ -426,6 +463,7 @@ boundary) - r->headers_out.content_type.data; + r->headers_out.content_type_len = r->headers_out.content_type.len; /* the size of the last boundary CRLF "--0123456789--" CRLF */ @@ -437,7 +475,7 @@ /* the size of the range: "SSSS-EEEE/TTTT" CRLF CRLF */ range[i].content_range.data = - ngx_palloc(r->pool, 3 * NGX_OFF_T_LEN + 2 + 4); + ngx_pnalloc(r->pool, 3 * NGX_OFF_T_LEN + 2 + 4); if (range[i].content_range.data == NULL) { return NGX_ERROR; @@ -465,13 +503,42 @@ 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) { + 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) { - off_t start, last; - ngx_buf_t *b, *buf; - ngx_uint_t i; - ngx_chain_t *out, *hcl, *rcl, *dcl, **ll; - ngx_http_range_t *range; ngx_http_range_filter_ctx_t *ctx; if (in == NULL) { @@ -484,17 +551,40 @@ return ngx_http_next_body_filter(r, in); } - buf = in->buf; + 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; } - range = ctx->ranges.elts; + buf = in->buf; if (!buf->last_buf) { @@ -507,6 +597,7 @@ last = buf->last - buf->start + ctx->offset; } + range = ctx->ranges.elts; for (i = 0; i < ctx->ranges.nelts; i++) { if (start > range[i].start || last < range[i].end) { goto overlapped; @@ -514,29 +605,124 @@ } } - /* - * the optimized version for the responses - * that are passed in the single buffer - */ - ctx->offset = ngx_buf_size(buf); - if (ctx->ranges.nelts == 1) { + return NGX_OK; - if (buf->in_file) { - buf->file_pos = range->start; - buf->file_last = range->end; +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 (ngx_buf_in_memory(buf)) { - buf->pos = buf->start + (size_t) range->start; - buf->last = buf->start + (size_t) range->end; + 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; } - return ngx_http_next_body_filter(r, in); + 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 = 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) +{ + off_t body_start; + 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; + +#if (NGX_HTTP_CACHE) + body_start = r->cached ? r->cache->body_start : 0; +#else + body_start = 0; +#endif for (i = 0; i < ctx->ranges.nelts; i++) { @@ -598,8 +784,8 @@ b->file = buf->file; if (buf->in_file) { - b->file_pos = range[i].start; - b->file_last = range[i].end; + b->file_pos = body_start + range[i].start; + b->file_last = body_start + range[i].end; } if (ngx_buf_in_memory(buf)) { @@ -630,8 +816,8 @@ b->temporary = 1; b->last_buf = 1; - b->pos = ngx_palloc(r->pool, sizeof(CRLF "--") - 1 + NGX_ATOMIC_T_LEN - + sizeof("--" CRLF) - 1); + b->pos = ngx_pnalloc(r->pool, sizeof(CRLF "--") - 1 + NGX_ATOMIC_T_LEN + + sizeof("--" CRLF) - 1); if (b->pos == NULL) { return NGX_ERROR; } @@ -652,13 +838,6 @@ *ll = hcl; return ngx_http_next_body_filter(r, out); - -overlapped: - - ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, - "range in overlapped buffers"); - - return NGX_ERROR; } diff -Nru nginx-0.5.33/src/http/modules/ngx_http_realip_module.c nginx-0.8.53/src/http/modules/ngx_http_realip_module.c --- nginx-0.5.33/src/http/modules/ngx_http_realip_module.c 2007-09-22 19:02:39.000000000 +0000 +++ nginx-0.8.53/src/http/modules/ngx_http_realip_module.c 2009-11-11 13:41:16.000000000 +0000 @@ -9,37 +9,49 @@ #include -/* AF_INET only */ +#define NGX_HTTP_REALIP_XREALIP 0 +#define NGX_HTTP_REALIP_XFWD 1 +#define NGX_HTTP_REALIP_HEADER 2 + typedef struct { - in_addr_t mask; - in_addr_t addr; + in_addr_t mask; + in_addr_t addr; } ngx_http_realip_from_t; typedef struct { - ngx_array_t *from; /* array of ngx_http_realip_from_t */ - - ngx_uint_t xfwd; + ngx_array_t *from; /* array of ngx_http_realip_from_t */ + ngx_uint_t type; + ngx_uint_t hash; + ngx_str_t header; +#if (NGX_HAVE_UNIX_DOMAIN) + ngx_uint_t unixsock; /* unsigned unixsock:2; */ +#endif } 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, u_char *ip, + size_t len); +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_init(ngx_conf_t *cf); -static ngx_conf_enum_t ngx_http_realip_header[] = { - { ngx_string("X-Forwarded-For"), 1 }, - { ngx_string("X-Real-IP"), 0 }, - { ngx_null_string, 0 } -}; - - static ngx_command_t ngx_http_realip_commands[] = { { ngx_string("set_real_ip_from"), @@ -51,10 +63,10 @@ { ngx_string("real_ip_header"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, - ngx_conf_set_enum_slot, + ngx_http_realip, NGX_HTTP_LOC_CONF_OFFSET, - offsetof(ngx_http_realip_loc_conf_t, xfwd), - &ngx_http_realip_header }, + 0, + NULL }, ngx_null_command }; @@ -97,23 +109,36 @@ { u_char *ip, *p; size_t len; - in_addr_t addr; - ngx_uint_t i; + ngx_uint_t i, hash; + ngx_list_part_t *part; + ngx_table_elt_t *header; struct sockaddr_in *sin; + ngx_connection_t *c; + ngx_http_realip_ctx_t *ctx; ngx_http_realip_from_t *from; ngx_http_realip_loc_conf_t *rlcf; - if (r->realip_set) { + ctx = ngx_http_get_module_ctx(r, ngx_http_realip_module); + + if (ctx) { return NGX_DECLINED; } rlcf = ngx_http_get_module_loc_conf(r, ngx_http_realip_module); - if (rlcf->from == NULL) { + if (rlcf->from == NULL +#if (NGX_HAVE_UNIX_DOMAIN) + && !rlcf->unixsock +#endif + ) + { return NGX_DECLINED; } - if (rlcf->xfwd == 0) { + switch (rlcf->type) { + + case NGX_HTTP_REALIP_XREALIP: + if (r->headers_in.x_real_ip == NULL) { return NGX_DECLINED; } @@ -121,7 +146,10 @@ len = r->headers_in.x_real_ip->value.len; ip = r->headers_in.x_real_ip->value.data; - } else { + break; + + case NGX_HTTP_REALIP_XFWD: + if (r->headers_in.x_forwarded_for == NULL) { return NGX_DECLINED; } @@ -137,61 +165,169 @@ break; } } - } - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "realip: \"%s\"", ip); + break; - /* AF_INET only */ + default: /* NGX_HTTP_REALIP_HEADER */ - sin = (struct sockaddr_in *) r->connection->sockaddr; + part = &r->headers_in.headers.part; + header = part->elts; - from = rlcf->from->elts; - for (i = 0; i < rlcf->from->nelts; i++) { + hash = rlcf->hash; + len = rlcf->header.len; + p = rlcf->header.data; - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "realip: %08XD %08XD %08XD", - sin->sin_addr.s_addr, from[i].mask, from[i].addr); + for (i = 0; /* void */ ; i++) { - if ((sin->sin_addr.s_addr & from[i].mask) == from[i].addr) { + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } - r->realip_set = 1; + part = part->next; + header = part->elts; + i = 0; + } - addr = inet_addr((char *) ip); + if (hash == header[i].hash + && len == header[i].key.len + && ngx_strncmp(p, header[i].lowcase_key, len) == 0) + { + len = header[i].value.len; + ip = header[i].value.data; - if (addr == INADDR_NONE) { - return NGX_DECLINED; + goto found; } + } - p = ngx_palloc(r->connection->pool, len); - if (p == NULL) { - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } + return NGX_DECLINED; + } - ngx_memcpy(p, ip, len); +found: - sin->sin_addr.s_addr = addr; + c = r->connection; - r->connection->addr_text.len = len; - r->connection->addr_text.data = p; + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "realip: \"%s\"", ip); - return NGX_DECLINED; + /* AF_INET only */ + + if (c->sockaddr->sa_family == AF_INET) { + sin = (struct sockaddr_in *) c->sockaddr; + + from = rlcf->from->elts; + for (i = 0; i < rlcf->from->nelts; i++) { + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, + "realip: %08XD %08XD %08XD", + sin->sin_addr.s_addr, from[i].mask, from[i].addr); + + if ((sin->sin_addr.s_addr & from[i].mask) == from[i].addr) { + return ngx_http_realip_set_addr(r, ip, len); + } } } +#if (NGX_HAVE_UNIX_DOMAIN) + + if (c->sockaddr->sa_family == AF_UNIX && rlcf->unixsock) { + return ngx_http_realip_set_addr(r, ip, len); + } + +#endif + return NGX_DECLINED; } +static ngx_int_t +ngx_http_realip_set_addr(ngx_http_request_t *r, u_char *ip, size_t len) +{ + u_char *p; + ngx_int_t rc; + ngx_addr_t addr; + 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; + ngx_http_set_ctx(r, ctx, ngx_http_realip_module); + + c = r->connection; + + rc = ngx_parse_addr(c->pool, &addr, ip, len); + + switch (rc) { + case NGX_DECLINED: + return NGX_DECLINED; + case NGX_ERROR: + return NGX_HTTP_INTERNAL_SERVER_ERROR; + default: /* NGX_OK */ + break; + } + + p = ngx_pnalloc(c->pool, len); + if (p == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + ngx_memcpy(p, ip, len); + + cln->handler = ngx_http_realip_cleanup; + + 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_inet_cidr_t in_cidr; - ngx_http_realip_from_t *from; + ngx_int_t rc; + ngx_str_t *value; + ngx_cidr_t cidr; + ngx_http_realip_from_t *from; + + value = cf->args->elts; + +#if (NGX_HAVE_UNIX_DOMAIN) + + if (ngx_strcmp(value[1].data, "unix:") == 0) { + rlcf->unixsock = 1; + return NGX_CONF_OK; + } + +#endif if (rlcf->from == NULL) { rlcf->from = ngx_array_create(cf->pool, 2, @@ -206,17 +342,7 @@ return NGX_CONF_ERROR; } - value = cf->args->elts; - - from->addr = inet_addr((char *) value[1].data); - - if (from->addr != INADDR_NONE) { - from->mask = 0xffffffff; - - return NGX_CONF_OK; - } - - rc = ngx_ptocidr(&value[1], &in_cidr); + rc = ngx_ptocidr(&value[1], &cidr); if (rc == NGX_ERROR) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameter \"%V\"", @@ -224,13 +350,46 @@ return NGX_CONF_ERROR; } + if (cidr.family != AF_INET) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "\"set_real_ip_from\" supports IPv4 only"); + 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]); } - from->mask = in_cidr.mask; - from->addr = in_cidr.addr; + from->mask = cidr.u.in.mask; + from->addr = cidr.u.in.addr; + + 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; + + 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; + } + + 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; } @@ -243,16 +402,21 @@ conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_realip_loc_conf_t)); if (conf == NULL) { - return NGX_CONF_ERROR; + return NULL; } /* * set by ngx_pcalloc(): * * conf->from = NULL; + * conf->hash = 0; + * conf->header = { 0, NULL }; */ - conf->xfwd = NGX_CONF_UNSET_UINT; + conf->type = NGX_CONF_UNSET_UINT; +#if (NGX_HAVE_UNIX_DOMAIN) + conf->unixsock = 2; +#endif return conf; } @@ -268,7 +432,18 @@ conf->from = prev->from; } - ngx_conf_merge_uint_value(conf->xfwd, prev->xfwd, 0); +#if (NGX_HAVE_UNIX_DOMAIN) + if (conf->unixsock == 2) { + conf->unixsock = (prev->unixsock == 2) ? 0 : prev->unixsock; + } +#endif + + ngx_conf_merge_uint_value(conf->type, prev->type, NGX_HTTP_REALIP_XREALIP); + + if (conf->header.len == 0) { + conf->hash = prev->hash; + conf->header = prev->header; + } return NGX_CONF_OK; } diff -Nru nginx-0.5.33/src/http/modules/ngx_http_referer_module.c nginx-0.8.53/src/http/modules/ngx_http_referer_module.c --- nginx-0.5.33/src/http/modules/ngx_http_referer_module.c 2007-11-07 13:46:29.000000000 +0000 +++ nginx-0.8.53/src/http/modules/ngx_http_referer_module.c 2010-05-14 09:56:37.000000000 +0000 @@ -11,14 +11,7 @@ #define NGX_HTTP_REFERER_NO_URI_PART ((void *) 4) -#if (NGX_PCRE) - -typedef struct { - ngx_regex_t *regex; - ngx_str_t name; -} ngx_http_referer_regex_t; - -#else +#if !(NGX_PCRE) #define ngx_regex_t void @@ -106,11 +99,6 @@ ngx_uint_t i, key; ngx_http_referer_conf_t *rlcf; u_char buf[256]; -#if (NGX_PCRE) - ngx_int_t n; - ngx_str_t referer; - ngx_http_referer_regex_t *regex; -#endif rlcf = ngx_http_get_module_loc_conf(r, ngx_http_referer_module); @@ -136,18 +124,27 @@ len = r->headers_in.referer->value.len; ref = r->headers_in.referer->value.data; - if (len < sizeof("http://i.ru") - 1 - || (ngx_strncasecmp(ref, (u_char *) "http://", 7) != 0)) - { - if (rlcf->blocked_referer) { - goto valid; + if (len >= sizeof("http://i.ru") - 1) { + last = ref + len; + + if (ngx_strncasecmp(ref, (u_char *) "http://", 7) == 0) { + ref += 7; + goto valid_scheme; + + } else if (ngx_strncasecmp(ref, (u_char *) "https://", 8) == 0) { + ref += 8; + goto valid_scheme; } + } - goto invalid; + if (rlcf->blocked_referer) { + goto valid; } - last = ref + len; - ref += 7; + goto invalid; + +valid_scheme: + i = 0; key = 0; @@ -173,31 +170,23 @@ #if (NGX_PCRE) if (rlcf->regex) { + ngx_int_t rc; + ngx_str_t referer; referer.len = len - 7; referer.data = ref; - regex = rlcf->regex->elts; - - for (i = 0; i < rlcf->regex->nelts; i++) { - n = ngx_regex_exec(regex[i].regex, &referer, NULL, 0); - - if (n == NGX_REGEX_NO_MATCHED) { - continue; - } - - if (n < 0) { - ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, - ngx_regex_exec_n - " failed: %d on \"%V\" using \"%V\"", - n, &referer, ®ex[i].name); - return NGX_ERROR; - } - - /* match */ + 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 @@ -241,9 +230,13 @@ conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_referer_conf_t)); if (conf == NULL) { - return NGX_CONF_ERROR; + return NULL; } +#if (NGX_PCRE) + conf->regex = NGX_CONF_UNSET_PTR; +#endif + conf->no_referer = NGX_CONF_UNSET; conf->blocked_referer = NGX_CONF_UNSET; @@ -262,6 +255,9 @@ if (conf->keys == NULL) { conf->hash = prev->hash; +#if (NGX_PCRE) + ngx_conf_merge_ptr_value(conf->regex, prev->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); @@ -337,6 +333,10 @@ conf->hash.wc_tail = (ngx_hash_wildcard_t *) hash.hash; } +#if (NGX_PCRE) + ngx_conf_merge_ptr_value(conf->regex, prev->regex, NULL); +#endif + if (conf->no_referer == NGX_CONF_UNSET) { conf->no_referer = 0; } @@ -363,11 +363,10 @@ ngx_http_server_name_t *sn; ngx_http_core_srv_conf_t *cscf; - name.len = sizeof("invalid_referer") - 1; - name.data = (u_char *) "invalid_referer"; + ngx_str_set(&name, "invalid_referer"); var = ngx_http_add_variable(cf, &name, - NGX_HTTP_VAR_CHANGABLE|NGX_HTTP_VAR_NOHASH); + NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOHASH); if (var == NULL) { return NGX_CONF_ERROR; } @@ -407,8 +406,7 @@ continue; } - uri.len = 0; - uri.data = NULL; + ngx_str_null(&uri); if (ngx_strcmp(value[i].data, "server_names") == 0) { @@ -421,7 +419,7 @@ if (sn[n].regex) { if (ngx_http_add_regex_referer(cf, rlcf, &sn[n].name, - sn[n].regex) + sn[n].regex->regex) != NGX_OK) { return NGX_CONF_ERROR; @@ -511,44 +509,52 @@ ngx_str_t *name, ngx_regex_t *regex) { #if (NGX_PCRE) - ngx_str_t err; - ngx_http_referer_regex_t *rr; - u_char errstr[NGX_MAX_CONF_ERRSTR]; - - if (rlcf->regex == NULL) { - rlcf->regex = ngx_array_create(cf->pool, 2, - sizeof(ngx_http_referer_regex_t)); + 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_CONF_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_CONF_ERROR; } } - rr = ngx_array_push(rlcf->regex); - if (rr == NULL) { + re = ngx_array_push(rlcf->regex); + if (re == NULL) { return NGX_CONF_ERROR; } if (regex) { - rr->regex = regex; - rr->name = *name; + re->regex = regex; + re->name = name->data; return NGX_CONF_OK; } - err.len = NGX_MAX_CONF_ERRSTR; - err.data = errstr; - name->len--; name->data++; - rr->regex = ngx_regex_compile(name, NGX_REGEX_CASELESS, cf->pool, &err); + 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 (rr->regex == NULL) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%s", err.data); + if (ngx_regex_compile(&rc) != NGX_OK) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%V", &rc.err); return NGX_CONF_ERROR; } - rr->name = *name; + re->regex = rc.regex; + re->name = name->data; return NGX_CONF_OK; @@ -572,5 +578,5 @@ first = (ngx_hash_key_t *) one; second = (ngx_hash_key_t *) two; - return ngx_strcmp(first->key.data, second->key.data); + return ngx_dns_strcmp(first->key.data, second->key.data); } diff -Nru nginx-0.5.33/src/http/modules/ngx_http_rewrite_module.c nginx-0.8.53/src/http/modules/ngx_http_rewrite_module.c --- nginx-0.5.33/src/http/modules/ngx_http_rewrite_module.c 2007-06-05 11:42:59.000000000 +0000 +++ nginx-0.8.53/src/http/modules/ngx_http_rewrite_module.c 2010-06-18 15:15:20.000000000 +0000 @@ -12,7 +12,6 @@ typedef struct { ngx_array_t *codes; /* uintptr_t */ - ngx_uint_t captures; ngx_uint_t stack_size; ngx_flag_t log; @@ -53,7 +52,7 @@ { ngx_string("return"), NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF - |NGX_CONF_TAKE1, + |NGX_CONF_TAKE12, ngx_http_rewrite_return, NGX_HTTP_LOC_CONF_OFFSET, 0, @@ -157,16 +156,6 @@ return NGX_HTTP_INTERNAL_SERVER_ERROR; } - if (rlcf->captures) { - e->captures = ngx_palloc(r->pool, rlcf->captures * sizeof(int)); - if (e->captures == NULL) { - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } - - } else { - e->captures = NULL; - } - e->ip = rlcf->codes->elts; e->request = r; e->quote = 1; @@ -231,7 +220,7 @@ conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_rewrite_loc_conf_t)); if (conf == NULL) { - return NGX_CONF_ERROR; + return NULL; } conf->stack_size = NGX_CONF_UNSET_UINT; @@ -305,9 +294,9 @@ { ngx_http_rewrite_loc_conf_t *lcf = conf; - ngx_str_t *value, err; - ngx_int_t n; + 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; @@ -324,15 +313,16 @@ value = cf->args->elts; - err.len = NGX_MAX_CONF_ERRSTR; - err.data = errstr; + ngx_memzero(&rc, sizeof(ngx_regex_compile_t)); - /* TODO: NGX_REGEX_CASELESS */ + rc.pattern = value[1]; + rc.err.len = NGX_MAX_CONF_ERRSTR; + rc.err.data = errstr; - regex->regex = ngx_regex_compile(&value[1], 0, cf->pool, &err); + /* TODO: NGX_REGEX_CASELESS */ + regex->regex = ngx_http_regex_compile(cf, &rc); if (regex->regex == NULL) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%s", err.data); return NGX_CONF_ERROR; } @@ -351,7 +341,10 @@ last = 0; - if (ngx_strncmp(value[2].data, "http://", sizeof("http://") - 1) == 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; @@ -399,7 +392,6 @@ regex = sc.main; - regex->ncaptures = sc.ncaptures; regex->size = sc.size; regex->args = sc.args; @@ -407,35 +399,6 @@ regex->lengths = NULL; } - n = ngx_regex_capture_count(regex->regex); - - if (n < 0) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - ngx_regex_capture_count_n " failed for " - "pattern \"%V\"", &value[1]); - return NGX_CONF_ERROR; - } - - if (regex->ncaptures > (ngx_uint_t) n) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "pattern \"%V\" has less captures " - "than referrenced in substitution \"%V\"", - &value[1], &value[2]); - return NGX_CONF_ERROR; - } - - if (regex->ncaptures < (ngx_uint_t) n) { - regex->ncaptures = (ngx_uint_t) n; - } - - if (regex->ncaptures) { - regex->ncaptures = (regex->ncaptures + 1) * 3; - - if (lcf->captures < regex->ncaptures) { - lcf->captures = regex->ncaptures; - } - } - regex_end = ngx_http_script_add_code(lcf->codes, sizeof(ngx_http_script_regex_end_code_t), ®ex); @@ -455,7 +418,7 @@ return NGX_CONF_ERROR; } - *code = (uintptr_t) NULL; + *code = NULL; } regex->next = (u_char *) lcf->codes->elts + lcf->codes->nelts @@ -470,8 +433,10 @@ { ngx_http_rewrite_loc_conf_t *lcf = conf; - ngx_str_t *value; - ngx_http_script_return_code_t *ret; + 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)); @@ -481,12 +446,46 @@ value = cf->args->elts; + ngx_memzero(ret, sizeof(ngx_http_script_return_code_t)); + ret->code = ngx_http_script_return_code; - ret->null = (uintptr_t) NULL; - ret->status = ngx_atoi(value[1].data, value[1].len); + 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 (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; } @@ -524,7 +523,7 @@ ngx_conf_t save; ngx_http_module_t *module; ngx_http_conf_ctx_t *ctx, *pctx; - ngx_http_core_loc_conf_t *clcf, *pclcf, **clcfp; + ngx_http_core_loc_conf_t *clcf, *pclcf; ngx_http_script_if_code_t *if_code; ngx_http_rewrite_loc_conf_t *nlcf; @@ -567,28 +566,17 @@ clcf->name = pclcf->name; clcf->noname = 1; - if (pclcf->locations == NULL) { - pclcf->locations = ngx_array_create(cf->pool, 2, sizeof(void *)); - if (pclcf->locations == NULL) { - return NGX_CONF_ERROR; - } - } - - clcfp = ngx_array_push(pclcf->locations); - if (clcfp == NULL) { + if (ngx_http_add_location(cf, &pclcf->locations, clcf) != NGX_OK) { return NGX_CONF_ERROR; } - *clcfp = clcf; - - 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 NULL; + return NGX_CONF_ERROR; } if_code->code = ngx_http_script_if_code; @@ -623,11 +611,6 @@ } - if (lcf->captures < nlcf->captures) { - lcf->captures = nlcf->captures; - } - - if (elts != lcf->codes->elts) { if_code = (ngx_http_script_if_code_t *) ((u_char *) if_code + ((u_char *) lcf->codes->elts - elts)); @@ -649,8 +632,9 @@ { u_char *p; size_t len; - ngx_str_t *value, err; - ngx_uint_t cur, last, n; + 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; @@ -758,15 +742,15 @@ ngx_memzero(regex, sizeof(ngx_http_script_regex_code_t)); - err.len = NGX_MAX_CONF_ERRSTR; - err.data = errstr; + ngx_memzero(&rc, sizeof(ngx_regex_compile_t)); - regex->regex = ngx_regex_compile(&value[last], - (p[len - 1] == '*') ? NGX_REGEX_CASELESS : 0, - cf->pool, &err); + 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) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%s", err.data); return NGX_CONF_ERROR; } @@ -778,16 +762,6 @@ } regex->name = value[last]; - n = ngx_regex_capture_count(regex->regex); - - if (n) { - regex->ncaptures = (n + 1) * 3; - - if (lcf->captures < regex->ncaptures) { - lcf->captures = regex->ncaptures; - } - } - return NGX_CONF_OK; } @@ -924,7 +898,7 @@ value[1].len--; value[1].data++; - v = ngx_http_add_variable(cf, &value[1], NGX_HTTP_VAR_CHANGABLE); + v = ngx_http_add_variable(cf, &value[1], NGX_HTTP_VAR_CHANGEABLE); if (v == NULL) { return NGX_CONF_ERROR; } diff -Nru nginx-0.5.33/src/http/modules/ngx_http_scgi_module.c nginx-0.8.53/src/http/modules/ngx_http_scgi_module.c --- nginx-0.5.33/src/http/modules/ngx_http_scgi_module.c 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/src/http/modules/ngx_http_scgi_module.c 2010-08-03 09:24:25.000000000 +0000 @@ -0,0 +1,1661 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Manlio Perillo (manlio.perillo@gmail.com) + */ + + +#include +#include +#include + + +typedef struct { + ngx_http_upstream_conf_t upstream; + + ngx_array_t *flushes; + ngx_array_t *params_len; + ngx_array_t *params; + ngx_array_t *params_source; + + ngx_hash_t headers_hash; + ngx_uint_t header_params; + + 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 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_loc_conf(ngx_conf_t *cf); +static char *ngx_http_scgi_merge_loc_conf(ngx_conf_t *cf, void *parent, + void *child); + +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("http_500"), NGX_HTTP_UPSTREAM_FT_HTTP_500 }, + { ngx_string("http_503"), NGX_HTTP_UPSTREAM_FT_HTTP_503 }, + { ngx_string("http_404"), NGX_HTTP_UPSTREAM_FT_HTTP_404 }, + { 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_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_TAKE1, + 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 }, + +#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, + 0, + 0, + &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_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 }, + +#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_param"), + 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_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 */ + + NULL, /* 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("") }, + { ngx_string("HTTP_IF_UNMODIFIED_SINCE"), ngx_string("") }, + { ngx_string("HTTP_IF_NONE_MATCH"), ngx_string("") }, + { 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 (r->subrequest_in_memory) { + ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, + "ngx_http_scgi_module does not support " + "subrequests in memory"); + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + 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) + 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; + + u->buffering = 1; + + 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; + + 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; + } + + if (url.no_port) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "no port in upstream \"%V\"", &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 && url.addrs[0].sockaddr) { + u->resolved->sockaddr = url.addrs[0].sockaddr; + u->resolved->socklen = url.addrs[0].socklen; + u->resolved->naddrs = 1; + u->resolved->host = url.addrs[0].name; + + } else { + u->resolved->host = url.host; + u->resolved->port = url.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) +{ + u_char ch, *key, *val, *lowcase_key; + size_t len, allocated; + ngx_buf_t *b; + ngx_str_t *content_length; + ngx_uint_t i, n, hash, header_params; + ngx_chain_t *cl, *body; + ngx_list_part_t *part; + ngx_table_elt_t *header, **ignored; + 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; + static ngx_str_t zero = ngx_string("0"); + + content_length = r->headers_in.content_length ? + &r->headers_in.content_length->value : &zero; + + 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 (scf->params_len) { + ngx_memzero(&le, sizeof(ngx_http_script_engine_t)); + + ngx_http_script_flush_no_cacheable_variables(r, scf->flushes); + le.flushed = 1; + + le.ip = scf->params_len->elts; + le.request = r; + + while (*(uintptr_t *) le.ip) { + + lcode = *(ngx_http_script_len_code_pt *) le.ip; + len += lcode(&le); + + while (*(uintptr_t *) le.ip) { + lcode = *(ngx_http_script_len_code_pt *) le.ip; + len += lcode(&le) + 1; + } + le.ip += sizeof(uintptr_t); + } + } + + if (scf->upstream.pass_request_headers) { + + allocated = 0; + lowcase_key = NULL; + + if (scf->header_params) { + ignored = ngx_palloc(r->pool, scf->header_params * 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 (scf->header_params) { + 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(&scf->headers_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_snprintf(b->last, + NGX_SIZE_T_LEN + 1 + sizeof("CONTENT_LENGTH") + + NGX_OFF_T_LEN + 1, + "%ui:CONTENT_LENGTH%Z%V%Z", + len, content_length); + + if (scf->params_len) { + ngx_memzero(&e, sizeof(ngx_http_script_engine_t)); + + e.ip = scf->params->elts; + e.pos = b->last; + e.request = r; + e.flushed = 1; + + while (*(uintptr_t *) e.ip) { + +#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 (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; + + 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) { + + r->http_version = NGX_HTTP_VERSION_9; + + u->process_header = ngx_http_scgi_process_header; + + return ngx_http_scgi_process_header(r); + } + + if (u->state) { + 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) { + 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_cpystrn(h->key.data, r->header_name_start, h->key.len + 1); + ngx_cpystrn(h->value.data, r->header_start, h->value.len + 1); + + 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"); + + if (r->http_version > NGX_HTTP_VERSION_9) { + return NGX_OK; + } + + u = r->upstream; + + 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; + } + + r->http_version = NGX_HTTP_VERSION_10; + u->headers_in.status_n = status; + u->headers_in.status_line = *status_line; + + } else if (u->headers_in.location) { + r->http_version = NGX_HTTP_VERSION_10; + u->headers_in.status_n = 302; + ngx_str_set(&u->headers_in.status_line, + "302 Moved Temporarily"); + + } else { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "upstream sent neither valid HTTP/1.0 header " + "nor \"Status\" header line"); + u->headers_in.status_n = 200; + ngx_str_set(&u->headers_in.status_line, "200 OK"); + } + + if (u->state) { + u->state->status = u->headers_in.status_n; + } + + 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_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.buffering = NGX_CONF_UNSET; + conf->upstream.ignore_client_abort = NGX_CONF_UNSET; + + 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.send_lowat = NGX_CONF_UNSET_SIZE; + conf->upstream.buffer_size = 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_PTR; + conf->upstream.cache_min_uses = NGX_CONF_UNSET_UINT; + conf->upstream.cache_bypass = NGX_CONF_UNSET_PTR; + conf->upstream.no_cache = NGX_CONF_UNSET_PTR; + conf->upstream.cache_valid = NGX_CONF_UNSET_PTR; +#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; + + 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; + + u_char *p; + size_t size; + uintptr_t *code; + ngx_uint_t i; + ngx_array_t headers_names; + ngx_keyval_t *src; + ngx_hash_key_t *hk; + ngx_hash_init_t hash; + ngx_http_core_loc_conf_t *clcf; + ngx_http_script_compile_t sc; + ngx_http_script_copy_code_t *copy; + + if (conf->upstream.store != 0) { + ngx_conf_merge_value(conf->upstream.store, prev->upstream.store, 0); + + if (conf->upstream.store_lengths == NULL) { + 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_value(conf->upstream.buffering, + prev->upstream.buffering, 1); + + ngx_conf_merge_value(conf->upstream.ignore_client_abort, + prev->upstream.ignore_client_abort, 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_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_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 or bigger " + "than 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 or bigger than " + "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 " + "the temporary files usage or must be equal or bigger than " + "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) + + ngx_conf_merge_ptr_value(conf->upstream.cache, + prev->upstream.cache, NULL); + + if (conf->upstream.cache && conf->upstream.cache->data == NULL) { + ngx_shm_zone_t *shm_zone; + + shm_zone = conf->upstream.cache; + + 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_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_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; + } + +#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; + } + + if (conf->upstream.upstream == NULL) { + conf->upstream.upstream = prev->upstream.upstream; + } + + if (conf->scgi_lengths == NULL) { + conf->scgi_lengths = prev->scgi_lengths; + conf->scgi_values = prev->scgi_values; + } + + if (conf->upstream.upstream || conf->scgi_lengths) { + clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); + if (clcf->handler == NULL && clcf->lmt_excpt) { + clcf->handler = ngx_http_scgi_handler; + } + } + + if (conf->params_source == NULL) { + conf->flushes = prev->flushes; + conf->params_len = prev->params_len; + conf->params = prev->params; + conf->params_source = prev->params_source; + conf->headers_hash = prev->headers_hash; + +#if (NGX_HTTP_CACHE) + + if (conf->params_source == NULL) { + + if ((conf->upstream.cache == NULL) + == (prev->upstream.cache == NULL)) + { + return NGX_CONF_OK; + } + + /* 6 is a number of ngx_http_scgi_cache_headers entries */ + conf->params_source = ngx_array_create(cf->pool, 6, + sizeof(ngx_keyval_t)); + if (conf->params_source == NULL) { + return NGX_CONF_ERROR; + } + } +#else + + if (conf->params_source == NULL) { + return NGX_CONF_OK; + } + +#endif + } + + conf->params_len = ngx_array_create(cf->pool, 64, 1); + if (conf->params_len == NULL) { + return NGX_CONF_ERROR; + } + + conf->params = ngx_array_create(cf->pool, 512, 1); + if (conf->params == NULL) { + return NGX_CONF_ERROR; + } + + if (ngx_array_init(&headers_names, cf->temp_pool, 4, sizeof(ngx_hash_key_t)) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + + src = conf->params_source->elts; + +#if (NGX_HTTP_CACHE) + + if (conf->upstream.cache) { + ngx_keyval_t *h, *s; + + for (h = ngx_http_scgi_cache_headers; h->key.len; h++) { + + for (i = 0; i < conf->params_source->nelts; i++) { + if (ngx_strcasecmp(h->key.data, src[i].key.data) == 0) { + goto next; + } + } + + s = ngx_array_push(conf->params_source); + if (s == NULL) { + return NGX_CONF_ERROR; + } + + *s = *h; + + src = conf->params_source->elts; + + next: + + h++; + } + } + +#endif + + for (i = 0; i < conf->params_source->nelts; 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_CONF_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(conf->params_len, + sizeof(ngx_http_script_copy_code_t)); + if (copy == NULL) { + return NGX_CONF_ERROR; + } + + copy->code = (ngx_http_script_code_pt) ngx_http_script_copy_len_code; + copy->len = src[i].key.len + 1; + + + 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(conf->params, size); + if (copy == NULL) { + return NGX_CONF_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 = &conf->flushes; + sc.lengths = &conf->params_len; + sc.values = &conf->params; + + if (ngx_http_script_compile(&sc) != NGX_OK) { + return NGX_CONF_ERROR; + } + + code = ngx_array_push_n(conf->params_len, sizeof(uintptr_t)); + if (code == NULL) { + return NGX_CONF_ERROR; + } + + *code = (uintptr_t) NULL; + + + code = ngx_array_push_n(conf->params, sizeof(uintptr_t)); + if (code == NULL) { + return NGX_CONF_ERROR; + } + + *code = (uintptr_t) NULL; + } + + code = ngx_array_push_n(conf->params_len, sizeof(uintptr_t)); + if (code == NULL) { + return NGX_CONF_ERROR; + } + + *code = (uintptr_t) NULL; + + code = ngx_array_push_n(conf->params, sizeof(uintptr_t)); + if (code == NULL) { + return NGX_CONF_ERROR; + } + + *code = (uintptr_t) NULL; + + conf->header_params = headers_names.nelts; + + hash.hash = &conf->headers_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; + + if (ngx_hash_init(&hash, headers_names.elts, headers_names.nelts) != NGX_OK) + { + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; +} + + +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 || scf->upstream.store_lengths) { + 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 != NGX_CONF_UNSET_PTR + && scf->upstream.cache != NULL) + { + return "is incompatible with \"scgi_cache\""; + } + +#endif + + if (ngx_strcmp(value[1].data, "on") == 0) { + scf->upstream.store = 1; + 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; + + value = cf->args->elts; + + if (scf->upstream.cache != NGX_CONF_UNSET_PTR) { + return "is duplicate"; + } + + if (ngx_strcmp(value[1].data, "off") == 0) { + scf->upstream.cache = NULL; + return NGX_CONF_OK; + } + + if (scf->upstream.store > 0 || scf->upstream.store_lengths) { + return "is incompatible with \"scgi_store\""; + } + + scf->upstream.cache = ngx_shared_memory_add(cf, &value[1], 0, + &ngx_http_scgi_module); + if (scf->upstream.cache == 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.len) { + 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 diff -Nru nginx-0.5.33/src/http/modules/ngx_http_secure_link_module.c nginx-0.8.53/src/http/modules/ngx_http_secure_link_module.c --- nginx-0.5.33/src/http/modules/ngx_http_secure_link_module.c 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/src/http/modules/ngx_http_secure_link_module.c 2010-09-13 12:44:43.000000000 +0000 @@ -0,0 +1,354 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#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[16], md5_buf[16]; + + conf = ngx_http_get_module_loc_conf(r, ngx_http_secure_link_module); + + if (conf->secret.len) { + 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.len = 16; + 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; + + ngx_conf_merge_str_value(conf->secret, prev->secret, ""); + + if (conf->variable == NULL) { + conf->variable = prev->variable; + } + + if (conf->md5 == NULL) { + conf->md5 = prev->md5; + } + + 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; +} diff -Nru nginx-0.5.33/src/http/modules/ngx_http_split_clients_module.c nginx-0.8.53/src/http/modules/ngx_http_split_clients_module.c --- nginx-0.5.33/src/http/modules/ngx_http_split_clients_module.c 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/src/http/modules/ngx_http_split_clients_module.c 2010-06-08 09:06:57.000000000 +0000 @@ -0,0 +1,241 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#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_crc32_short(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, + "%D %D", hash, part[i].percent); + + if (hash < part[i].percent) { + *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; + ngx_str_t *value, name; + ngx_uint_t i, sum, last; + 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]; + 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 sum is more than 100%%"); + return NGX_CONF_ERROR; + } + + if (part[i].percent) { + part[i].percent = (uint32_t) + (last + 0xffffffff / 10000 * part[i].percent); + } else { + part[i].percent = 0xffffffff; + } + + last = part[i].percent; + } + + 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].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; +} diff -Nru nginx-0.5.33/src/http/modules/ngx_http_ssi_filter_module.c nginx-0.8.53/src/http/modules/ngx_http_ssi_filter_module.c --- nginx-0.5.33/src/http/modules/ngx_http_ssi_filter_module.c 2007-10-29 15:03:04.000000000 +0000 +++ nginx-0.8.53/src/http/modules/ngx_http_ssi_filter_module.c 2010-07-07 10:17:19.000000000 +0000 @@ -14,7 +14,6 @@ #define NGX_HTTP_SSI_ADD_PREFIX 1 #define NGX_HTTP_SSI_ADD_ZERO 2 -#define NGX_HTTP_SSI_EXPR_TEST 4 typedef struct { @@ -22,10 +21,12 @@ ngx_flag_t silent_errors; ngx_flag_t ignore_recycled_buffers; - ngx_array_t *types; /* array of ngx_str_t */ + ngx_hash_t types; size_t min_file_chunk; size_t value_len; + + ngx_array_t *types_keys; } ngx_http_ssi_loc_conf_t; @@ -69,6 +70,8 @@ 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, @@ -100,9 +103,7 @@ 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 char *ngx_http_ssi_types(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); + 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); @@ -153,10 +154,10 @@ { ngx_string("ssi_types"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE, - ngx_http_ssi_types, + ngx_http_types_slot, NGX_HTTP_LOC_CONF_OFFSET, - 0, - NULL }, + offsetof(ngx_http_ssi_loc_conf_t, types_keys), + &ngx_http_html_default_types[0] }, ngx_null_command }; @@ -303,10 +304,10 @@ static ngx_http_variable_t ngx_http_ssi_vars[] = { { ngx_string("date_local"), NULL, ngx_http_ssi_date_gmt_local_variable, 0, - NGX_HTTP_VAR_NOCACHABLE, 0 }, + NGX_HTTP_VAR_NOCACHEABLE, 0 }, { ngx_string("date_gmt"), NULL, ngx_http_ssi_date_gmt_local_variable, 1, - NGX_HTTP_VAR_NOCACHABLE, 0 }, + NGX_HTTP_VAR_NOCACHEABLE, 0 }, { ngx_null_string, NULL, NULL, 0, 0, 0 } }; @@ -316,36 +317,18 @@ static ngx_int_t ngx_http_ssi_header_filter(ngx_http_request_t *r) { - ngx_uint_t i; - ngx_str_t *type; ngx_http_ssi_ctx_t *ctx; ngx_http_ssi_loc_conf_t *slcf; slcf = ngx_http_get_module_loc_conf(r, ngx_http_ssi_filter_module); if (!slcf->enable - || r->headers_out.content_type.len == 0 - || r->headers_out.content_length_n == 0) + || r->headers_out.content_length_n == 0 + || ngx_http_test_content_type(r, &slcf->types) == NULL) { return ngx_http_next_header_filter(r); } - - type = slcf->types->elts; - for (i = 0; i < slcf->types->nelts; i++) { - if (r->headers_out.content_type.len >= type[i].len - && ngx_strncasecmp(r->headers_out.content_type.data, - type[i].data, type[i].len) == 0) - { - goto found; - } - } - - return ngx_http_next_header_filter(r); - - -found: - ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_ssi_ctx_t)); if (ctx == NULL) { return NGX_ERROR; @@ -365,19 +348,16 @@ ctx->params.nalloc = NGX_HTTP_SSI_PARAMS_N; ctx->params.pool = r->pool; - ctx->timefmt.len = sizeof("%A, %d-%b-%Y %H:%M:%S %Z") - 1; - ctx->timefmt.data = (u_char *) "%A, %d-%b-%Y %H:%M:%S %Z"; - - ctx->errmsg.len = - sizeof("[an error occurred while processing the directive]") - 1; - ctx->errmsg.data = (u_char *) - "[an error occurred while processing the directive]"; + ngx_str_set(&ctx->timefmt, "%A, %d-%b-%Y %H:%M:%S %Z"); + ngx_str_set(&ctx->errmsg, + "[an error occurred while processing the directive]"); r->filter_need_in_memory = 1; if (r == r->main) { ngx_http_clear_content_length(r); ngx_http_clear_last_modified(r); + ngx_http_clear_accept_ranges(r); } return ngx_http_next_header_filter(r); @@ -393,7 +373,6 @@ ngx_uint_t i, index; ngx_chain_t *cl, **ll; ngx_table_elt_t *param; - ngx_http_request_t *pr; ngx_http_ssi_ctx_t *ctx, *mctx; ngx_http_ssi_block_t *bl; ngx_http_ssi_param_t *prm; @@ -416,52 +395,45 @@ /* add the incoming chain to the chain ctx->in */ if (in) { - if (ngx_chain_add_copy(r->pool, &ctx->in, in) == NGX_ERROR) { + if (ngx_chain_add_copy(r->pool, &ctx->in, in) != NGX_OK) { return NGX_ERROR; } } + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http ssi filter \"%V?%V\"", &r->uri, &r->args); + if (ctx->wait) { - if (r->connection->data != r) { - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http ssi filter \"%V\" wait", &r->uri); - return NGX_AGAIN; - } - for (pr = ctx->wait->parent; pr; pr = pr->parent) { - if (pr == r) { - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http ssi filter \"%V\" flush", &r->uri); + if (r != r->connection->data) { + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http ssi filter wait \"%V?%V\" non-active", + &ctx->wait->uri, &ctx->wait->args); - rc = ngx_http_next_body_filter(r, NULL); + return NGX_AGAIN; + } - if (ctx->wait->done) { - ctx->wait = NULL; - } + if (ctx->wait->done) { + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http ssi filter wait \"%V?%V\" done", + &ctx->wait->uri, &ctx->wait->args); - if (rc == NGX_ERROR || rc == NGX_AGAIN) { - return rc; - } + ctx->wait = NULL; - break; - } - } + } else { + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http ssi filter wait \"%V?%V\"", + &ctx->wait->uri, &ctx->wait->args); - if (ctx->wait == r) { - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http ssi filter \"%V\" continue", &r->uri); - ctx->wait = NULL; + return ngx_http_next_body_filter(r, NULL); } } slcf = ngx_http_get_module_loc_conf(r, ngx_http_ssi_filter_module); - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http ssi filter \"%V\"", &r->uri); - while (ctx->in || ctx->buf) { - if (ctx->buf == NULL ){ + if (ctx->buf == NULL) { ctx->buf = ctx->in->buf; ctx->in = ctx->in->next; ctx->pos = ctx->buf->pos; @@ -558,8 +530,9 @@ if (b->in_file) { if (slcf->min_file_chunk < (size_t) (b->last - b->pos)) { - b->file_last = b->file_pos + (b->last - b->start); - b->file_pos += b->pos - b->start; + b->file_last = b->file_pos + + (b->last - ctx->buf->pos); + b->file_pos += b->pos - ctx->buf->pos; } else { b->in_file = 0; @@ -805,16 +778,12 @@ } } - if (cmd->flush) { + if (cmd->flush && ctx->out) { - if (ctx->out) { - rc = ngx_http_ssi_output(r, ctx); + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "ssi flush"); - } else { - rc = ngx_http_next_body_filter(r, NULL); - } - - if (rc == NGX_ERROR) { + if (ngx_http_ssi_output(r, ctx) == NGX_ERROR) { return NGX_ERROR; } } @@ -826,6 +795,7 @@ } if (rc == NGX_DONE || rc == NGX_AGAIN || rc == NGX_ERROR) { + ngx_http_ssi_buffered(r, ctx); return rc; } } @@ -964,12 +934,6 @@ break; } -#if (NGX_HAVE_WRITE_ZEROCOPY) - if (b->zerocopy_busy) { - break; - } -#endif - if (b->shadow) { b->shadow->pos = b->shadow->last; } @@ -984,14 +948,21 @@ } } + ngx_http_ssi_buffered(r, ctx); + + return rc; +} + + +static void +ngx_http_ssi_buffered(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx) +{ if (ctx->in || ctx->buf) { r->buffered |= NGX_HTTP_SSI_BUFFERED; } else { r->buffered &= ~NGX_HTTP_SSI_BUFFERED; } - - return rc; } @@ -1151,8 +1122,8 @@ default: ctx->command.len = 1; - ctx->command.data = ngx_palloc(r->pool, - NGX_HTTP_SSI_COMMAND_LEN); + ctx->command.data = ngx_pnalloc(r->pool, + NGX_HTTP_SSI_COMMAND_LEN); if (ctx->command.data == NULL) { return NGX_ERROR; } @@ -1218,8 +1189,8 @@ } ctx->param->key.len = 1; - ctx->param->key.data = ngx_palloc(r->pool, - NGX_HTTP_SSI_PARAM_LEN); + ctx->param->key.data = ngx_pnalloc(r->pool, + NGX_HTTP_SSI_PARAM_LEN); if (ctx->param->key.data == NULL) { return NGX_ERROR; } @@ -1229,8 +1200,8 @@ ctx->param->value.len = 0; if (ctx->value_buf == NULL) { - ctx->param->value.data = ngx_palloc(r->pool, - ctx->value_len); + ctx->param->value.data = ngx_pnalloc(r->pool, + ctx->value_len); if (ctx->param->value.data == NULL) { return NGX_ERROR; } @@ -1408,7 +1379,7 @@ case ssi_postparam_state: if (ctx->param->value.len + 1 < ctx->value_len / 2) { - value = ngx_palloc(r->pool, ctx->param->value.len + 1); + value = ngx_pnalloc(r->pool, ctx->param->value.len + 1); if (value == NULL) { return NGX_ERROR; } @@ -1604,7 +1575,7 @@ size_t *size, len, prefix, part_len; ngx_str_t var, *val; ngx_int_t key; - ngx_uint_t i, j, n, bracket, quoted; + ngx_uint_t i, n, bracket, quoted; ngx_array_t lengths, values; ngx_http_variable_value_t *vv; @@ -1626,7 +1597,7 @@ if (prefix) { len = prefix + text->len; - data = ngx_palloc(r->pool, len); + data = ngx_pnalloc(r->pool, len); if (data == NULL) { return NGX_ERROR; } @@ -1637,7 +1608,7 @@ quoted = 0; - for (i = 0 ; i < text->len; i++) { + for (i = 0; i < text->len; i++) { ch = text->data[i]; if (!quoted) { @@ -1730,18 +1701,12 @@ goto invalid_variable; } - key = 0; - - for (j = 0; j < var.len; j++) { - var.data[j] = ngx_tolower(var.data[j]); - key = ngx_hash(key, var.data[j]); - } + key = ngx_hash_strlow(var.data, var.data, var.len); val = ngx_http_ssi_get_variable(r, &var, key); if (val == NULL) { - vv = ngx_http_get_variable(r, &var, key, - flags & NGX_HTTP_SSI_EXPR_TEST); + vv = ngx_http_get_variable(r, &var, key); if (vv == NULL) { return NGX_ERROR; } @@ -1829,7 +1794,7 @@ } } - p = ngx_palloc(r->pool, len + ((flags & NGX_HTTP_SSI_ADD_ZERO) ? 1 : 0)); + p = ngx_pnalloc(r->pool, len + ((flags & NGX_HTTP_SSI_ADD_ZERO) ? 1 : 0)); if (p == NULL) { return NGX_ERROR; } @@ -1858,6 +1823,8 @@ ngx_http_ssi_include(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx, ngx_str_t **params) { + u_char *dst, *src; + size_t len; ngx_int_t rc, key; ngx_str_t *uri, *file, *wait, *set, *stub, args; ngx_buf_t *b; @@ -1919,6 +1886,7 @@ if (uri == NULL) { uri = file; + wait = (ngx_str_t *) -1; } rc = ngx_http_ssi_evaluate_string(r, ctx, uri, NGX_HTTP_SSI_ADD_PREFIX); @@ -1927,13 +1895,24 @@ return rc; } - args.len = 0; - args.data = NULL; - flags = 0; + dst = uri->data; + src = uri->data; + + ngx_unescape_uri(&dst, &src, uri->len, NGX_UNESCAPE_URI); + + len = (uri->data + uri->len) - src; + if (len) { + dst = ngx_copy(dst, src, len); + } + + uri->len = dst - uri->data; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ssi include: \"%V\"", uri); + ngx_str_null(&args); + flags = NGX_HTTP_LOG_UNSAFE; + if (ngx_http_parse_unsafe_uri(r, uri, &args, &flags) != NGX_OK) { return NGX_HTTP_SSI_ERROR; } @@ -1969,6 +1948,7 @@ if (bl[i].count++) { + out = NULL; ll = &out; for (tl = bl[i].bufs; tl; tl = tl->next) { @@ -2008,13 +1988,12 @@ } } - if (set) { - key = 0; + if (wait) { + flags |= NGX_HTTP_SUBREQUEST_WAITED; + } - for (i = 0; i < set->len; i++) { - set->data[i] = ngx_tolower(set->data[i]); - key = ngx_hash(key, set->data[i]); - } + if (set) { + key = ngx_hash_strlow(set->data, set->data, set->len); psr = ngx_palloc(r->pool, sizeof(ngx_http_post_subrequest_t)); if (psr == NULL) { @@ -2045,16 +2024,10 @@ psr->data = &var->value; } - flags |= NGX_HTTP_SUBREQUEST_IN_MEMORY; - } - - rc = ngx_http_subrequest(r, uri, &args, &sr, psr, flags); - - if (rc == NGX_DONE) { - return NGX_DONE; + flags |= NGX_HTTP_SUBREQUEST_IN_MEMORY|NGX_HTTP_SUBREQUEST_WAITED; } - if (rc == NGX_ERROR) { + if (ngx_http_subrequest(r, uri, &args, &sr, psr, flags) != NGX_OK) { return NGX_HTTP_SSI_ERROR; } @@ -2062,17 +2035,17 @@ return NGX_OK; } - if (rc == NGX_AGAIN) { - if (ctx->wait == NULL) { - ctx->wait = sr; + if (ctx->wait == NULL) { + ctx->wait = sr; - } else { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "only one subrequest may be waited at the same time"); - } + return NGX_AGAIN; + + } else { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "only one subrequest may be waited at the same time"); } - return rc; + return NGX_OK; } @@ -2091,9 +2064,9 @@ out = data; if (!r->header_sent) { - if (ngx_http_set_content_type(r) == NGX_ERROR) { - return NGX_ERROR; - } + r->headers_out.content_type_len = + r->parent->headers_out.content_type_len; + r->headers_out.content_type = r->parent->headers_out.content_type; if (ngx_http_send_header(r) == NGX_ERROR) { return NGX_ERROR; @@ -2125,7 +2098,6 @@ u_char *p; uintptr_t len; ngx_int_t key; - ngx_uint_t i; ngx_buf_t *b; ngx_str_t *var, *value, *enc, text; ngx_chain_t *cl; @@ -2136,17 +2108,12 @@ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ssi echo \"%V\"", var); - key = 0; - - for (i = 0; i < var->len; i++) { - var->data[i] = ngx_tolower(var->data[i]); - key = ngx_hash(key, var->data[i]); - } + key = ngx_hash_strlow(var->data, var->data, var->len); value = ngx_http_ssi_get_variable(r, var, key); if (value == NULL) { - vv = ngx_http_get_variable(r, var, key, 1); + vv = ngx_http_get_variable(r, var, key); if (vv == NULL) { return NGX_HTTP_SSI_ERROR; @@ -2197,44 +2164,43 @@ } } - switch (ctx->encoding) { + p = value->data; - case NGX_HTTP_SSI_NO_ENCODING: - break; + switch (ctx->encoding) { case NGX_HTTP_SSI_URL_ENCODING: len = 2 * ngx_escape_uri(NULL, value->data, value->len, NGX_ESCAPE_HTML); if (len) { - p = ngx_palloc(r->pool, value->len + len); + p = ngx_pnalloc(r->pool, value->len + len); if (p == NULL) { return NGX_HTTP_SSI_ERROR; } (void) ngx_escape_uri(p, value->data, value->len, NGX_ESCAPE_HTML); - - value->len += len; - value->data = p; } + len += value->len; break; case NGX_HTTP_SSI_ENTITY_ENCODING: len = ngx_escape_html(NULL, value->data, value->len); if (len) { - p = ngx_palloc(r->pool, value->len + len); + p = ngx_pnalloc(r->pool, value->len + len); if (p == NULL) { return NGX_HTTP_SSI_ERROR; } (void) ngx_escape_html(p, value->data, value->len); - - value->len += len; - value->data = p; } + len += value->len; + break; + + default: /* NGX_HTTP_SSI_NO_ENCODING */ + len = value->len; break; } @@ -2249,8 +2215,8 @@ } b->memory = 1; - b->pos = value->data; - b->last = value->data + value->len; + b->pos = p; + b->last = p + len; cl->buf = b; cl->next = NULL; @@ -2271,7 +2237,7 @@ if (value) { ctx->timefmt.len = value->len; - ctx->timefmt.data = ngx_palloc(r->pool, value->len + 1); + ctx->timefmt.data = ngx_pnalloc(r->pool, value->len + 1); if (ctx->timefmt.data == NULL) { return NGX_HTTP_SSI_ERROR; } @@ -2294,7 +2260,6 @@ ngx_str_t **params) { ngx_int_t key, rc; - ngx_uint_t i; ngx_str_t *name, *value, *vv; ngx_http_ssi_var_t *var; ngx_http_ssi_ctx_t *mctx; @@ -2321,12 +2286,7 @@ return rc; } - key = 0; - - for (i = 0; i < name->len; i++) { - name->data[i] = ngx_tolower(name->data[i]); - key = ngx_hash(key, name->data[i]); - } + key = ngx_hash_strlow(name->data, name->data, name->len); vv = ngx_http_ssi_get_variable(r, name, key); @@ -2359,11 +2319,6 @@ ngx_str_t *expr, left, right; ngx_int_t rc; ngx_uint_t negative, noregex, flags; -#if (NGX_PCRE) - ngx_str_t err; - ngx_regex_t *regex; - u_char errstr[NGX_MAX_CONF_ERRSTR]; -#endif if (ctx->command.len == 2) { if (ctx->conditional) { @@ -2409,7 +2364,7 @@ p++; } - flags = (p == last) ? NGX_HTTP_SSI_EXPR_TEST : 0; + flags = 0; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "left: \"%V\"", &left); @@ -2497,23 +2452,28 @@ } else { #if (NGX_PCRE) - err.len = NGX_MAX_CONF_ERRSTR; - err.data = errstr; + ngx_regex_compile_t rgc; + u_char errstr[NGX_MAX_CONF_ERRSTR]; right.data[right.len] = '\0'; - regex = ngx_regex_compile(&right, 0, r->pool, &err); + ngx_memzero(&rgc, sizeof(ngx_regex_compile_t)); - if (regex == NULL) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "%s", err.data); + rgc.pattern = right; + rgc.pool = r->pool; + rgc.err.len = NGX_MAX_CONF_ERRSTR; + rgc.err.data = errstr; + + if (ngx_regex_compile(&rgc) != NGX_OK) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "%V", &rgc.err); return NGX_HTTP_SSI_ERROR; } - rc = ngx_regex_exec(regex, &left, NULL, 0); + rc = ngx_regex_exec(rgc.regex, &left, NULL, 0); - if (rc != NGX_REGEX_NO_MATCHED && rc < 0) { + if (rc < NGX_REGEX_NO_MATCHED) { ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, - ngx_regex_exec_n " failed: %d on \"%V\" using \"%V\"", + ngx_regex_exec_n " failed: %i on \"%V\" using \"%V\"", rc, &left, &right); return NGX_HTTP_SSI_ERROR; } @@ -2633,7 +2593,7 @@ 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) + ngx_http_variable_value_t *v, uintptr_t gmt) { ngx_http_ssi_ctx_t *ctx; ngx_time_t *tp; @@ -2641,23 +2601,23 @@ char buf[NGX_HTTP_SSI_DATE_LEN]; v->valid = 1; - v->no_cachable = 0; + v->no_cacheable = 0; v->not_found = 0; tp = ngx_timeofday(); ctx = ngx_http_get_module_ctx(r, ngx_http_ssi_filter_module); - if (ctx->timefmt.len == sizeof("%s") - 1 - && ctx->timefmt.data[0] == '%' && ctx->timefmt.data[1] == 's') + if (ctx == NULL + || (ctx->timefmt.len == sizeof("%s") - 1 + && ctx->timefmt.data[0] == '%' && ctx->timefmt.data[1] == 's')) { - v->data = ngx_palloc(r->pool, NGX_TIME_T_LEN); + v->data = ngx_pnalloc(r->pool, NGX_TIME_T_LEN); if (v->data == NULL) { return NGX_ERROR; } - v->len = ngx_sprintf(v->data, "%T", tp->sec + (gmt ? 0 : tp->gmtoff)) - - v->data; + v->len = ngx_sprintf(v->data, "%T", tp->sec) - v->data; return NGX_OK; } @@ -2674,7 +2634,7 @@ return NGX_ERROR; } - v->data = ngx_palloc(r->pool, v->len); + v->data = ngx_pnalloc(r->pool, v->len); if (v->data == NULL) { return NGX_ERROR; } @@ -2685,56 +2645,6 @@ } -static char * -ngx_http_ssi_types(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) -{ - ngx_http_ssi_loc_conf_t *slcf = conf; - - ngx_str_t *value, *type; - ngx_uint_t i; - - if (slcf->types == NULL) { - slcf->types = ngx_array_create(cf->pool, 4, sizeof(ngx_str_t)); - if (slcf->types == NULL) { - return NGX_CONF_ERROR; - } - - type = ngx_array_push(slcf->types); - if (type == NULL) { - return NGX_CONF_ERROR; - } - - type->len = sizeof("text/html") - 1; - type->data = (u_char *) "text/html"; - } - - value = cf->args->elts; - - for (i = 1; i < cf->args->nelts; i++) { - - if (ngx_strcmp(value[i].data, "text/html") == 0) { - continue; - } - - type = ngx_array_push(slcf->types); - if (type == NULL) { - return NGX_CONF_ERROR; - } - - type->len = value[i].len; - - type->data = ngx_palloc(cf->pool, type->len + 1); - if (type->data == NULL) { - return NGX_CONF_ERROR; - } - - ngx_cpystrn(type->data, value[i].data, type->len + 1); - } - - return NGX_CONF_OK; -} - - static ngx_int_t ngx_http_ssi_preconfiguration(ngx_conf_t *cf) { @@ -2782,14 +2692,14 @@ smcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_ssi_main_conf_t)); if (smcf == NULL) { - return NGX_CONF_ERROR; + return NULL; } smcf->commands.pool = cf->pool; smcf->commands.temp_pool = cf->temp_pool; if (ngx_hash_keys_array_init(&smcf->commands, NGX_HASH_SMALL) != NGX_OK) { - return NGX_CONF_ERROR; + return NULL; } return smcf; @@ -2829,13 +2739,14 @@ slcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_ssi_loc_conf_t)); if (slcf == NULL) { - return NGX_CONF_ERROR; + return NULL; } /* * set by ngx_pcalloc(): * - * conf->types = NULL; + * conf->types = { NULL }; + * conf->types_keys = NULL; */ slcf->enable = NGX_CONF_UNSET; @@ -2855,8 +2766,6 @@ ngx_http_ssi_loc_conf_t *prev = parent; ngx_http_ssi_loc_conf_t *conf = child; - ngx_str_t *type; - ngx_conf_merge_value(conf->enable, prev->enable, 0); ngx_conf_merge_value(conf->silent_errors, prev->silent_errors, 0); ngx_conf_merge_value(conf->ignore_recycled_buffers, @@ -2865,24 +2774,12 @@ ngx_conf_merge_size_value(conf->min_file_chunk, prev->min_file_chunk, 1024); ngx_conf_merge_size_value(conf->value_len, prev->value_len, 256); - if (conf->types == NULL) { - if (prev->types == NULL) { - conf->types = ngx_array_create(cf->pool, 1, sizeof(ngx_str_t)); - if (conf->types == NULL) { - return NGX_CONF_ERROR; - } - - type = ngx_array_push(conf->types); - if (type == NULL) { - return NGX_CONF_ERROR; - } - - type->len = sizeof("text/html") - 1; - type->data = (u_char *) "text/html"; - - } else { - conf->types = prev->types; - } + 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; diff -Nru nginx-0.5.33/src/http/modules/ngx_http_ssl_module.c nginx-0.8.53/src/http/modules/ngx_http_ssl_module.c --- nginx-0.5.33/src/http/modules/ngx_http_ssl_module.c 2007-07-22 08:47:45.000000000 +0000 +++ nginx-0.8.53/src/http/modules/ngx_http_ssl_module.c 2010-05-14 09:56:37.000000000 +0000 @@ -13,9 +13,7 @@ ngx_pool_t *pool, ngx_str_t *s); -#define NGX_DEFLAUT_CERTIFICATE "cert.pem" -#define NGX_DEFLAUT_CERTIFICATE_KEY "cert.pem" -#define NGX_DEFLAUT_CIPHERS "ALL:!ADH:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP" +#define NGX_DEFAULT_CIPHERS "HIGH:!ADH:!MD5" static ngx_int_t ngx_http_ssl_static_variable(ngx_http_request_t *r, @@ -28,18 +26,11 @@ static char *ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child); -static char *ngx_http_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd, +static char *ngx_http_ssl_enable(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); - -#if !defined (SSL_OP_CIPHER_SERVER_PREFERENCE) - -static char *ngx_http_ssl_nosupported(ngx_conf_t *cf, ngx_command_t *cmd, +static char *ngx_http_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); -static char ngx_http_ssl_openssl097[] = "OpenSSL 0.9.7 and higher"; - -#endif - static ngx_conf_bitmask_t ngx_http_ssl_protocols[] = { { ngx_string("SSLv2"), NGX_SSL_SSLv2 }, @@ -49,11 +40,19 @@ }; +static ngx_conf_enum_t ngx_http_ssl_verify[] = { + { ngx_string("off"), 0 }, + { ngx_string("on"), 1 }, + { ngx_string("optional"), 2 }, + { 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_conf_set_flag_slot, + ngx_http_ssl_enable, NGX_HTTP_SRV_CONF_OFFSET, offsetof(ngx_http_ssl_srv_conf_t, enable), NULL }, @@ -72,6 +71,13 @@ offsetof(ngx_http_ssl_srv_conf_t, certificate_key), 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_protocols"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_1MORE, ngx_conf_set_bitmask_slot, @@ -88,10 +94,10 @@ { ngx_string("ssl_verify_client"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG, - ngx_conf_set_flag_slot, + ngx_conf_set_enum_slot, NGX_HTTP_SRV_CONF_OFFSET, offsetof(ngx_http_ssl_srv_conf_t, verify), - NULL }, + &ngx_http_ssl_verify }, { ngx_string("ssl_verify_depth"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_1MORE, @@ -109,14 +115,10 @@ { ngx_string("ssl_prefer_server_ciphers"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG, -#ifdef SSL_OP_CIPHER_SERVER_PREFERENCE ngx_conf_set_flag_slot, NGX_HTTP_SRV_CONF_OFFSET, offsetof(ngx_http_ssl_srv_conf_t, prefer_server_ciphers), NULL }, -#else - ngx_http_ssl_nosupported, 0, 0, ngx_http_ssl_openssl097 }, -#endif { ngx_string("ssl_session_cache"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE12, @@ -132,6 +134,13 @@ 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_null_command }; @@ -170,19 +179,32 @@ 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_CHANGABLE, 0 }, + (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_CHANGABLE, 0 }, + (uintptr_t) ngx_ssl_get_cipher_name, 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_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_s_dn"), NULL, ngx_http_ssl_variable, - (uintptr_t) ngx_ssl_get_subject_dn, NGX_HTTP_VAR_CHANGABLE, 0 }, + (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_CHANGABLE, 0 }, + (uintptr_t) ngx_ssl_get_issuer_dn, 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_CHANGABLE, 0 }, + (uintptr_t) ngx_ssl_get_serial_number, 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_null_string, NULL, NULL, 0, 0, 0 } }; @@ -210,7 +232,7 @@ v->len = len; v->valid = 1; - v->no_cachable = 0; + v->no_cacheable = 0; v->not_found = 0; return NGX_OK; @@ -241,7 +263,7 @@ if (v->len) { v->valid = 1; - v->no_cachable = 0; + v->no_cacheable = 0; v->not_found = 0; return NGX_OK; @@ -280,28 +302,26 @@ sscf = ngx_pcalloc(cf->pool, sizeof(ngx_http_ssl_srv_conf_t)); if (sscf == NULL) { - return NGX_CONF_ERROR; + return NULL; } /* * set by ngx_pcalloc(): * * sscf->protocols = 0; - * sscf->certificate.len = 0; - * sscf->certificate.data = NULL; - * sscf->certificate_key.len = 0; - * sscf->certificate_key.data = NULL; - * sscf->client_certificate.len = 0; - * sscf->client_certificate.data = NULL; - * sscf->ciphers.len = 0; - * sscf->ciphers.data = NULL; + * sscf->certificate = { 0, NULL }; + * sscf->certificate_key = { 0, NULL }; + * sscf->dhparam = { 0, NULL }; + * sscf->client_certificate = { 0, NULL }; + * sscf->crl = { 0, NULL }; + * sscf->ciphers = { 0, NULL }; * sscf->shm_zone = NULL; */ sscf->enable = NGX_CONF_UNSET; - sscf->verify = NGX_CONF_UNSET; - sscf->verify_depth = NGX_CONF_UNSET; sscf->prefer_server_ciphers = NGX_CONF_UNSET; + sscf->verify = NGX_CONF_UNSET_UINT; + sscf->verify_depth = NGX_CONF_UNSET_UINT; sscf->builtin_session_cache = NGX_CONF_UNSET; sscf->session_timeout = NGX_CONF_UNSET; @@ -319,10 +339,6 @@ ngx_conf_merge_value(conf->enable, prev->enable, 0); - if (conf->enable == 0) { - return NGX_CONF_OK; - } - ngx_conf_merge_value(conf->session_timeout, prev->session_timeout, 300); @@ -330,26 +346,57 @@ prev->prefer_server_ciphers, 0); ngx_conf_merge_bitmask_value(conf->protocols, prev->protocols, - (NGX_CONF_BITMASK_SET - |NGX_SSL_SSLv2|NGX_SSL_SSLv3|NGX_SSL_TLSv1)); + (NGX_CONF_BITMASK_SET|NGX_SSL_SSLv3|NGX_SSL_TLSv1)); - ngx_conf_merge_value(conf->verify, prev->verify, 0); - ngx_conf_merge_value(conf->verify_depth, prev->verify_depth, 1); + 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_str_value(conf->certificate, prev->certificate, - NGX_DEFLAUT_CERTIFICATE); + ngx_conf_merge_str_value(conf->certificate, prev->certificate, ""); + ngx_conf_merge_str_value(conf->certificate_key, prev->certificate_key, ""); - ngx_conf_merge_str_value(conf->certificate_key, prev->certificate_key, - NGX_DEFLAUT_CERTIFICATE_KEY); + 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->crl, prev->crl, ""); - ngx_conf_merge_str_value(conf->ciphers, prev->ciphers, NGX_DEFLAUT_CIPHERS); + ngx_conf_merge_str_value(conf->ciphers, prev->ciphers, NGX_DEFAULT_CIPHERS); conf->ssl.log = cf->log; + if (conf->enable) { + + if (conf->certificate.len == 0) { + 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_key.len == 0) { + 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; + } + + } else { + + if (conf->certificate.len == 0) { + return NGX_CONF_OK; + } + + if (conf->certificate_key.len == 0) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "no \"ssl_certificate_key\" is defined " + "for certificate \"%V\"", &conf->certificate); + return NGX_CONF_ERROR; + } + } + if (ngx_ssl_create(&conf->ssl, conf->protocols, conf) != NGX_OK) { return NGX_CONF_ERROR; } @@ -360,9 +407,10 @@ ngx_http_ssl_servername) == 0) { - ngx_ssl_error(NGX_LOG_EMERG, cf->log, 0, - "SSL_CTX_set_tlsext_servername_callback() failed"); - return NGX_CONF_ERROR; + 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 @@ -392,6 +440,13 @@ } if (conf->verify) { + + if (conf->client_certificate.len == 0) { + 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) @@ -399,24 +454,27 @@ { return NGX_CONF_ERROR; } - } -#ifdef SSL_OP_CIPHER_SERVER_PREFERENCE + if (ngx_ssl_crl(cf, &conf->ssl, &conf->crl) != NGX_OK) { + return NGX_CONF_ERROR; + } + } if (conf->prefer_server_ciphers) { SSL_CTX_set_options(conf->ssl.ctx, SSL_OP_CIPHER_SERVER_PREFERENCE); } -#endif - /* a temporary 512-bit RSA key is required for export versions of MSIE */ if (ngx_ssl_generate_rsa512_key(&conf->ssl) != NGX_OK) { return NGX_CONF_ERROR; } + if (ngx_ssl_dhparam(cf, &conf->ssl, &conf->dhparam) != NGX_OK) { + return NGX_CONF_ERROR; + } + ngx_conf_merge_value(conf->builtin_session_cache, - prev->builtin_session_cache, - NGX_SSL_DFLT_BUILTIN_SCACHE); + prev->builtin_session_cache, NGX_SSL_NONE_SCACHE); if (conf->shm_zone == NULL) { conf->shm_zone = prev->shm_zone; @@ -435,6 +493,26 @@ 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_session_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_http_ssl_srv_conf_t *sscf = conf; @@ -448,6 +526,16 @@ 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; @@ -477,6 +565,7 @@ for (j = sizeof("shared:") - 1; j < value[i].len; j++) { if (value[i].data[j] == ':') { + value[i].data[j] = '\0'; break; } @@ -532,18 +621,3 @@ return NGX_CONF_ERROR; } - - -#if !defined (SSL_OP_CIPHER_SERVER_PREFERENCE) - -static char * -ngx_http_ssl_nosupported(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) -{ - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "\"%V\" directive is available only in %s,", - &cmd->name, cmd->post); - - return NGX_CONF_ERROR; -} - -#endif diff -Nru nginx-0.5.33/src/http/modules/ngx_http_ssl_module.h nginx-0.8.53/src/http/modules/ngx_http_ssl_module.h --- nginx-0.5.33/src/http/modules/ngx_http_ssl_module.h 2007-01-03 15:25:40.000000000 +0000 +++ nginx-0.8.53/src/http/modules/ngx_http_ssl_module.h 2009-07-23 12:21:26.000000000 +0000 @@ -22,8 +22,8 @@ ngx_uint_t protocols; - ngx_int_t verify; - ngx_int_t verify_depth; + ngx_uint_t verify; + ngx_uint_t verify_depth; ssize_t builtin_session_cache; @@ -31,11 +31,16 @@ ngx_str_t certificate; ngx_str_t certificate_key; + ngx_str_t dhparam; ngx_str_t client_certificate; + ngx_str_t crl; ngx_str_t ciphers; ngx_shm_zone_t *shm_zone; + + u_char *file; + ngx_uint_t line; } ngx_http_ssl_srv_conf_t; diff -Nru nginx-0.5.33/src/http/modules/ngx_http_static_module.c nginx-0.8.53/src/http/modules/ngx_http_static_module.c --- nginx-0.5.33/src/http/modules/ngx_http_static_module.c 2007-01-18 20:15:09.000000000 +0000 +++ nginx-0.8.53/src/http/modules/ngx_http_static_module.c 2010-05-24 12:35:10.000000000 +0000 @@ -9,35 +9,10 @@ #include -typedef struct { - ngx_http_cache_hash_t *redirect_cache; -} ngx_http_static_loc_conf_t; - - static ngx_int_t ngx_http_static_handler(ngx_http_request_t *r); -static void *ngx_http_static_create_loc_conf(ngx_conf_t *cf); -static char *ngx_http_static_merge_loc_conf(ngx_conf_t *cf, - void *parent, void *child); static ngx_int_t ngx_http_static_init(ngx_conf_t *cf); -static ngx_command_t ngx_http_static_commands[] = { - -#if (NGX_HTTP_CACHE) - - { ngx_string("redirect_cache"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE3, - ngx_http_set_cache_slot, - NGX_HTTP_LOC_CONF_OFFSET, - offsetof(ngx_http_static_loc_conf_t, redirect_cache), - NULL }, - -#endif - - ngx_null_command -}; - - ngx_http_module_t ngx_http_static_module_ctx = { NULL, /* preconfiguration */ ngx_http_static_init, /* postconfiguration */ @@ -48,15 +23,15 @@ NULL, /* create server configuration */ NULL, /* merge server configuration */ - ngx_http_static_create_loc_conf, /* create location configuration */ - ngx_http_static_merge_loc_conf /* merge location configuration */ + NULL, /* create location configuration */ + NULL /* merge location configuration */ }; ngx_module_t ngx_http_static_module = { NGX_MODULE_V1, &ngx_http_static_module_ctx, /* module context */ - ngx_http_static_commands, /* module directives */ + NULL, /* module directives */ NGX_HTTP_MODULE, /* module type */ NULL, /* init master */ NULL, /* init module */ @@ -73,21 +48,17 @@ ngx_http_static_handler(ngx_http_request_t *r) { u_char *last, *location; - size_t root; - ngx_fd_t fd; + size_t root, len; + ngx_str_t path; ngx_int_t rc; ngx_uint_t level; - ngx_str_t path; - ngx_err_t err; ngx_log_t *log; ngx_buf_t *b; ngx_chain_t out; - ngx_file_info_t fi; - ngx_pool_cleanup_t *cln; - ngx_pool_cleanup_file_t *clnf; + ngx_open_file_info_t of; ngx_http_core_loc_conf_t *clcf; - if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) { + if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD|NGX_HTTP_POST))) { return NGX_HTTP_NOT_ALLOWED; } @@ -95,17 +66,6 @@ return NGX_DECLINED; } - /* TODO: Win32 */ - if (r->zero_in_uri) { - return NGX_DECLINED; - } - - rc = ngx_http_discard_body(r); - - if (rc != NGX_OK && rc != NGX_AGAIN) { - return rc; - } - log = r->connection->log; /* @@ -118,95 +78,105 @@ return NGX_HTTP_INTERNAL_SERVER_ERROR; } + path.len = last - path.data; + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, "http filename: \"%s\"", path.data); - cln = ngx_pool_cleanup_add(r->pool, sizeof(ngx_pool_cleanup_file_t)); - if (cln == NULL) { - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + ngx_memzero(&of, sizeof(ngx_open_file_info_t)); - fd = ngx_open_file(path.data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0); + 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_open_cached_file(clcf->open_file_cache, &path, &of, r->pool) + != NGX_OK) + { + switch (of.err) { + + case 0: + return NGX_HTTP_INTERNAL_SERVER_ERROR; - if (fd == NGX_INVALID_FILE) { - err = ngx_errno; + case NGX_ENOENT: + case NGX_ENOTDIR: + case NGX_ENAMETOOLONG: - if (err == NGX_ENOENT - || err == NGX_ENOTDIR - || err == NGX_ENAMETOOLONG) - { level = NGX_LOG_ERR; rc = NGX_HTTP_NOT_FOUND; + break; + + case NGX_EACCES: - } else if (err == NGX_EACCES) { level = NGX_LOG_ERR; rc = NGX_HTTP_FORBIDDEN; + break; + + default: - } else { level = NGX_LOG_CRIT; rc = NGX_HTTP_INTERNAL_SERVER_ERROR; + break; } - clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); - if (rc != NGX_HTTP_NOT_FOUND || clcf->log_not_found) { - ngx_log_error(level, log, err, - ngx_open_file_n " \"%s\" failed", path.data); + ngx_log_error(level, log, of.err, + "%s \"%s\" failed", of.failed, path.data); } return rc; } - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, "http static fd: %d", fd); + r->root_tested = !r->error_page; - if (ngx_fd_info(fd, &fi) == NGX_FILE_ERROR) { - ngx_log_error(NGX_LOG_CRIT, log, ngx_errno, - ngx_fd_info_n " \"%s\" failed", path.data); - - if (ngx_close_file(fd) == NGX_FILE_ERROR) { - ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, - ngx_close_file_n " \"%s\" failed", path.data); - } - - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, "http static fd: %d", of.fd); - if (ngx_is_dir(&fi)) { + if (of.is_dir) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, log, 0, "http dir"); - if (ngx_close_file(fd) == NGX_FILE_ERROR) { - ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, - ngx_close_file_n " \"%s\" failed", path.data); - } - r->headers_out.location = ngx_palloc(r->pool, sizeof(ngx_table_elt_t)); if (r->headers_out.location == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } - clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + len = r->uri.len + 1; - if (!clcf->alias && clcf->root_lengths == NULL) { + if (!clcf->alias && clcf->root_lengths == NULL && r->args.len == 0) { location = path.data + clcf->root.len; + *last = '/'; + } else { - location = ngx_palloc(r->pool, r->uri.len + 1); + if (r->args.len) { + len += r->args.len + 1; + } + + location = ngx_pnalloc(r->pool, len); if (location == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } last = ngx_copy(location, r->uri.data, r->uri.len); - } - *last = '/'; + *last = '/'; + + if (r->args.len) { + *++last = '?'; + ngx_memcpy(++last, r->args.data, r->args.len); + } + } /* * we do not need to set the r->headers_out.location->hash and * r->headers_out.location->key fields */ - r->headers_out.location->value.len = r->uri.len + 1; + r->headers_out.location->value.len = len; r->headers_out.location->value.data = location; return NGX_HTTP_MOVED_PERMANENTLY; @@ -214,38 +184,36 @@ #if !(NGX_WIN32) /* the not regular files are probably Unix specific */ - if (!ngx_is_file(&fi)) { - ngx_log_error(NGX_LOG_CRIT, log, ngx_errno, + if (!of.is_file) { + ngx_log_error(NGX_LOG_CRIT, log, 0, "\"%s\" is not a regular file", path.data); - if (ngx_close_file(fd) == NGX_FILE_ERROR) { - ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, - ngx_close_file_n " \"%s\" failed", path.data); - } - return NGX_HTTP_NOT_FOUND; } #endif - log->action = "sending response to client"; + if (r->method & NGX_HTTP_POST) { + return NGX_HTTP_NOT_ALLOWED; + } - cln->handler = ngx_pool_cleanup_file; - clnf = cln->data; + rc = ngx_http_discard_request_body(r); - clnf->fd = fd; - clnf->name = path.data; - clnf->log = r->pool->log; + 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 = ngx_file_size(&fi); - r->headers_out.last_modified_time = ngx_file_mtime(&fi); + r->headers_out.content_length_n = of.size; + r->headers_out.last_modified_time = of.mtime; if (ngx_http_set_content_type(r) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } - if (r != r->main && ngx_file_size(&fi) == 0) { + if (r != r->main && of.size == 0) { return ngx_http_send_header(r); } @@ -270,15 +238,16 @@ } b->file_pos = 0; - b->file_last = ngx_file_size(&fi); + 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 = fd; + 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; @@ -287,36 +256,6 @@ } -static void * -ngx_http_static_create_loc_conf(ngx_conf_t *cf) -{ - ngx_http_static_loc_conf_t *conf; - - conf = ngx_palloc(cf->pool, sizeof(ngx_http_static_loc_conf_t)); - if (conf == NULL) { - return NGX_CONF_ERROR; - } - - conf->redirect_cache = NULL; - - return conf; -} - - -static char * -ngx_http_static_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) -{ - ngx_http_static_loc_conf_t *prev = parent; - ngx_http_static_loc_conf_t *conf = child; - - if (conf->redirect_cache == NULL) { - conf->redirect_cache = prev->redirect_cache; - } - - return NGX_CONF_OK; -} - - static ngx_int_t ngx_http_static_init(ngx_conf_t *cf) { diff -Nru nginx-0.5.33/src/http/modules/ngx_http_stub_status_module.c nginx-0.8.53/src/http/modules/ngx_http_stub_status_module.c --- nginx-0.5.33/src/http/modules/ngx_http_stub_status_module.c 2006-08-30 10:39:17.000000000 +0000 +++ nginx-0.8.53/src/http/modules/ngx_http_stub_status_module.c 2010-05-14 09:56:37.000000000 +0000 @@ -69,14 +69,13 @@ return NGX_HTTP_NOT_ALLOWED; } - rc = ngx_http_discard_body(r); + rc = ngx_http_discard_request_body(r); - if (rc != NGX_OK && rc != NGX_AGAIN) { + if (rc != NGX_OK) { return rc; } - r->headers_out.content_type.len = sizeof("text/plain") - 1; - r->headers_out.content_type.data = (u_char *) "text/plain"; + ngx_str_set(&r->headers_out.content_type, "text/plain"); if (r->method == NGX_HTTP_HEAD) { r->headers_out.status = NGX_HTTP_OK; @@ -129,7 +128,7 @@ return rc; } - return ngx_http_output_filter(r, &out);; + return ngx_http_output_filter(r, &out); } diff -Nru nginx-0.5.33/src/http/modules/ngx_http_sub_filter_module.c nginx-0.8.53/src/http/modules/ngx_http_sub_filter_module.c --- nginx-0.5.33/src/http/modules/ngx_http_sub_filter_module.c 2007-11-07 13:59:32.000000000 +0000 +++ nginx-0.8.53/src/http/modules/ngx_http_sub_filter_module.c 2010-07-05 13:45:10.000000000 +0000 @@ -10,15 +10,14 @@ typedef struct { - ngx_str_t match; - ngx_str_t sub; + ngx_str_t match; + ngx_http_complex_value_t value; - ngx_array_t *types; /* array of ngx_str_t */ + ngx_hash_t types; - ngx_array_t *sub_lengths; - ngx_array_t *sub_values; + ngx_flag_t once; - ngx_flag_t once; + ngx_array_t *types_keys; } ngx_http_sub_loc_conf_t; @@ -29,27 +28,27 @@ typedef struct { - ngx_str_t match; + ngx_str_t match; + ngx_str_t saved; + ngx_str_t looked; - ngx_uint_t once; /* unsigned once:1 */ + ngx_uint_t once; /* unsigned once:1 */ - ngx_buf_t *buf; + ngx_buf_t *buf; - u_char *pos; - u_char *copy_start; - u_char *copy_end; + u_char *pos; + u_char *copy_start; + u_char *copy_end; - ngx_chain_t *in; - ngx_chain_t *out; - ngx_chain_t **last_out; - ngx_chain_t *busy; - ngx_chain_t *free; + ngx_chain_t *in; + ngx_chain_t *out; + ngx_chain_t **last_out; + ngx_chain_t *busy; + ngx_chain_t *free; - ngx_str_t sub; + ngx_str_t sub; - ngx_uint_t state; - size_t saved; - size_t looked; + ngx_uint_t state; } ngx_http_sub_ctx_t; @@ -60,7 +59,6 @@ static char * ngx_http_sub_filter(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); -static char *ngx_http_sub_types(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static void *ngx_http_sub_create_conf(ngx_conf_t *cf); static char *ngx_http_sub_merge_conf(ngx_conf_t *cf, void *parent, void *child); @@ -78,10 +76,10 @@ { ngx_string("sub_filter_types"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE, - ngx_http_sub_types, + ngx_http_types_slot, NGX_HTTP_LOC_CONF_OFFSET, - 0, - NULL }, + offsetof(ngx_http_sub_loc_conf_t, types_keys), + &ngx_http_html_default_types[0] }, { ngx_string("sub_filter_once"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, @@ -132,36 +130,30 @@ static ngx_int_t ngx_http_sub_header_filter(ngx_http_request_t *r) { - ngx_str_t *type; - ngx_uint_t i; ngx_http_sub_ctx_t *ctx; ngx_http_sub_loc_conf_t *slcf; slcf = ngx_http_get_module_loc_conf(r, ngx_http_sub_filter_module); if (slcf->match.len == 0 - || r->headers_out.content_type.len == 0 - || r->headers_out.content_length_n == 0) + || r->headers_out.content_length_n == 0 + || ngx_http_test_content_type(r, &slcf->types) == NULL) { return ngx_http_next_header_filter(r); } - type = slcf->types->elts; - for (i = 0; i < slcf->types->nelts; i++) { - if (r->headers_out.content_type.len >= type[i].len - && ngx_strncasecmp(r->headers_out.content_type.data, - type[i].data, type[i].len) == 0) - { - goto found; - } + ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_sub_ctx_t)); + if (ctx == NULL) { + return NGX_ERROR; } - return ngx_http_next_header_filter(r); - -found: + ctx->saved.data = ngx_pnalloc(r->pool, slcf->match.len); + if (ctx->saved.data == NULL) { + return NGX_ERROR; + } - ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_sub_ctx_t)); - if (ctx == NULL) { + ctx->looked.data = ngx_pnalloc(r->pool, slcf->match.len); + if (ctx->looked.data == NULL) { return NGX_ERROR; } @@ -169,7 +161,6 @@ ctx->match = slcf->match; ctx->last_out = &ctx->out; - ctx->sub = slcf->sub; r->filter_need_in_memory = 1; @@ -219,7 +210,7 @@ /* add the incoming chain to the chain ctx->in */ if (in) { - if (ngx_chain_add_copy(r->pool, &ctx->in, in) == NGX_ERROR) { + if (ngx_chain_add_copy(r->pool, &ctx->in, in) != NGX_OK) { return NGX_ERROR; } } @@ -229,7 +220,7 @@ while (ctx->in || ctx->buf) { - if (ctx->buf == NULL ){ + if (ctx->buf == NULL) { ctx->buf = ctx->in->buf; ctx->in = ctx->in->next; ctx->pos = ctx->buf->pos; @@ -245,13 +236,13 @@ while (ctx->pos < ctx->buf->last) { ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "saved: %d state: %d", ctx->saved, ctx->state); + "saved: \"%V\" state: %d", &ctx->saved, ctx->state); rc = ngx_http_sub_parse(r, ctx); ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "parse: %d, looked: %d %p-%p", - rc, ctx->looked, ctx->copy_start, ctx->copy_end); + "parse: %d, looked: \"%V\" %p-%p", + rc, &ctx->looked, ctx->copy_start, ctx->copy_end); if (rc == NGX_ERROR) { return rc; @@ -260,9 +251,9 @@ if (ctx->copy_start != ctx->copy_end) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "saved: %d", ctx->saved); + "saved: \"%V\"", &ctx->saved); - if (ctx->saved) { + if (ctx->saved.len) { if (ctx->free) { cl = ctx->free; @@ -284,14 +275,19 @@ cl->buf = b; } + b->pos = ngx_pnalloc(r->pool, ctx->saved.len); + if (b->pos == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(b->pos, ctx->saved.data, ctx->saved.len); + b->last = b->pos + ctx->saved.len; b->memory = 1; - b->pos = ctx->match.data; - b->last = ctx->match.data + ctx->saved; *ctx->last_out = cl; ctx->last_out = &cl->next; - ctx->saved = 0; + ctx->saved.len = 0; } if (ctx->free) { @@ -322,8 +318,8 @@ b->recycled = 0; if (b->in_file) { - b->file_last = b->file_pos + (b->last - b->start); - b->file_pos += b->pos - b->start; + b->file_last = b->file_pos + (b->last - ctx->buf->pos); + b->file_pos += b->pos - ctx->buf->pos; } cl->next = NULL; @@ -361,9 +357,8 @@ if (ctx->sub.data == NULL) { - if (ngx_http_script_run(r, &ctx->sub, slcf->sub_lengths->elts, - 0, slcf->sub_values->elts) - == NULL) + if (ngx_http_complex_value(r, &slcf->value, &ctx->sub) + != NGX_OK) { return NGX_ERROR; } @@ -425,7 +420,8 @@ ctx->buf = NULL; - ctx->saved = ctx->looked; + ctx->saved.len = ctx->looked.len; + ngx_memcpy(ctx->saved.data, ctx->looked.data, ctx->looked.len); } if (ctx->out == NULL && ctx->busy == NULL) { @@ -480,12 +476,6 @@ break; } -#if (NGX_HAVE_WRITE_ZEROCOPY) - if (b->zerocopy_busy) { - break; - } -#endif - if (b->shadow) { b->shadow->pos = b->shadow->last; } @@ -522,7 +512,7 @@ ctx->copy_start = ctx->pos; ctx->copy_end = ctx->buf->last; ctx->pos = ctx->buf->last; - ctx->looked = 0; + ctx->looked.len = 0; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "once"); @@ -530,7 +520,7 @@ } state = ctx->state; - looked = ctx->looked; + looked = ctx->looked.len; last = ctx->buf->last; copy_end = ctx->copy_end; @@ -548,6 +538,7 @@ for ( ;; ) { if (ch == match) { copy_end = p; + ctx->looked.data[0] = *p; looked = 1; state = sub_match_state; @@ -564,7 +555,7 @@ ctx->state = state; ctx->pos = p; - ctx->looked = looked; + ctx->looked.len = looked; ctx->copy_end = p; if (ctx->copy_start == NULL) { @@ -581,16 +572,17 @@ /* state == sub_match_state */ if (ch == ctx->match.data[looked]) { + ctx->looked.data[looked] = *p; looked++; if (looked == ctx->match.len) { if ((size_t) (p - ctx->pos) < looked) { - ctx->saved = 0; + ctx->saved.len = 0; } ctx->state = sub_start_state; ctx->pos = p + 1; - ctx->looked = looked; + ctx->looked.len = 0; ctx->copy_end = copy_end; if (ctx->copy_start == NULL && copy_end) { @@ -602,6 +594,7 @@ } else if (ch == ctx->match.data[0]) { copy_end = p; + ctx->looked.data[0] = *p; looked = 1; } else { @@ -613,7 +606,7 @@ ctx->state = state; ctx->pos = p; - ctx->looked = looked; + ctx->looked.len = looked; ctx->copy_end = (state == sub_start_state) ? p : copy_end; @@ -630,10 +623,8 @@ { ngx_http_sub_loc_conf_t *slcf = conf; - ngx_str_t *value; - ngx_int_t n; - ngx_uint_t i; - ngx_http_script_compile_t sc; + ngx_str_t *value; + ngx_http_compile_complex_value_t ccv; if (slcf->match.len) { return "is duplicate"; @@ -641,30 +632,17 @@ value = cf->args->elts; - slcf->match = value[1]; - - for (i = 0; i < value[1].len; i++) { - value[1].data[i] = ngx_tolower(value[1].data[i]); - } - - n = ngx_http_script_variables_count(&value[2]); + ngx_strlow(value[1].data, value[1].data, value[1].len); - if (n == 0) { - slcf->sub = value[2]; - return NGX_CONF_OK; - } + slcf->match = value[1]; - ngx_memzero(&sc, sizeof(ngx_http_script_compile_t)); + ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); - sc.cf = cf; - sc.source = &value[2]; - sc.lengths = &slcf->sub_lengths; - sc.values = &slcf->sub_values; - sc.variables = n; - sc.complete_lengths = 1; - sc.complete_values = 1; + ccv.cf = cf; + ccv.value = &value[2]; + ccv.complex_value = &slcf->value; - if (ngx_http_script_compile(&sc) != NGX_OK) { + if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { return NGX_CONF_ERROR; } @@ -672,56 +650,6 @@ } -static char * -ngx_http_sub_types(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) -{ - ngx_http_sub_loc_conf_t *slcf = conf; - - ngx_str_t *value, *type; - ngx_uint_t i; - - if (slcf->types == NULL) { - slcf->types = ngx_array_create(cf->pool, 4, sizeof(ngx_str_t)); - if (slcf->types == NULL) { - return NGX_CONF_ERROR; - } - - type = ngx_array_push(slcf->types); - if (type == NULL) { - return NGX_CONF_ERROR; - } - - type->len = sizeof("text/html") - 1; - type->data = (u_char *) "text/html"; - } - - value = cf->args->elts; - - for (i = 1; i < cf->args->nelts; i++) { - - if (ngx_strcmp(value[i].data, "text/html") == 0) { - continue; - } - - type = ngx_array_push(slcf->types); - if (type == NULL) { - return NGX_CONF_ERROR; - } - - type->len = value[i].len; - - type->data = ngx_palloc(cf->pool, type->len + 1); - if (type->data == NULL) { - return NGX_CONF_ERROR; - } - - ngx_cpystrn(type->data, value[i].data, type->len + 1); - } - - return NGX_CONF_OK; -} - - static void * ngx_http_sub_create_conf(ngx_conf_t *cf) { @@ -729,19 +657,18 @@ slcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_sub_loc_conf_t)); if (slcf == NULL) { - return NGX_CONF_ERROR; + return NULL; } /* * set by ngx_pcalloc(): * - * conf->match.len = 0; - * conf->match.data = NULL; - * conf->sub.len = 0; - * conf->sub.data = NULL; + * conf->match = { 0, NULL }; + * conf->sub = { 0, NULL }; * conf->sub_lengths = NULL; * conf->sub_values = NULL; - * conf->types = NULL; + * conf->types = { NULL }; + * conf->types_keys = NULL; */ slcf->once = NGX_CONF_UNSET; @@ -756,35 +683,19 @@ ngx_http_sub_loc_conf_t *prev = parent; ngx_http_sub_loc_conf_t *conf = child; - ngx_str_t *type; - ngx_conf_merge_value(conf->once, prev->once, 1); ngx_conf_merge_str_value(conf->match, prev->match, ""); - if (conf->sub.data == NULL && conf->sub_lengths == NULL) { - conf->sub = prev->sub; - conf->sub_lengths = prev->sub_lengths; - conf->sub_values = prev->sub_values; + if (conf->value.value.len == 0) { + conf->value = prev->value; } - if (conf->types == NULL) { - if (prev->types == NULL) { - conf->types = ngx_array_create(cf->pool, 1, sizeof(ngx_str_t)); - if (conf->types == NULL) { - return NGX_CONF_ERROR; - } - - type = ngx_array_push(conf->types); - if (type == NULL) { - return NGX_CONF_ERROR; - } - - type->len = sizeof("text/html") - 1; - type->data = (u_char *) "text/html"; - - } else { - conf->types = prev->types; - } + 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; diff -Nru nginx-0.5.33/src/http/modules/ngx_http_upstream_ip_hash_module.c nginx-0.8.53/src/http/modules/ngx_http_upstream_ip_hash_module.c --- nginx-0.5.33/src/http/modules/ngx_http_upstream_ip_hash_module.c 2007-07-29 17:25:06.000000000 +0000 +++ nginx-0.8.53/src/http/modules/ngx_http_upstream_ip_hash_module.c 2009-02-21 07:02:02.000000000 +0000 @@ -15,7 +15,6 @@ ngx_uint_t hash; - /* AF_INET only */ u_char addr[3]; u_char tries; @@ -111,11 +110,20 @@ r->upstream->peer.get = ngx_http_upstream_get_ip_hash_peer; /* AF_INET only */ - sin = (struct sockaddr_in *) r->connection->sockaddr; - p = (u_char *) &sin->sin_addr.s_addr; - iphp->addr[0] = p[0]; - iphp->addr[1] = p[1]; - iphp->addr[2] = p[2]; + + if (r->connection->sockaddr->sa_family == AF_INET) { + + sin = (struct sockaddr_in *) r->connection->sockaddr; + p = (u_char *) &sin->sin_addr.s_addr; + iphp->addr[0] = p[0]; + iphp->addr[1] = p[1]; + iphp->addr[2] = p[2]; + + } else { + iphp->addr[0] = 0; + iphp->addr[1] = 0; + iphp->addr[2] = 0; + } iphp->hash = 89; iphp->tries = 0; @@ -140,7 +148,7 @@ /* TODO: cached */ - if (iphp->tries > 20 || iphp->rrp.peers->number == 1) { + if (iphp->tries > 20 || iphp->rrp.peers->single) { return iphp->get_rr_peer(pc, &iphp->rrp); } @@ -160,7 +168,7 @@ p = hash % iphp->rrp.peers->number; n = p / (8 * sizeof(uintptr_t)); - m = 1 << p % (8 * sizeof(uintptr_t)); + m = (uintptr_t) 1 << p % (8 * sizeof(uintptr_t)); if (!(iphp->rrp.tried[n] & m)) { @@ -195,6 +203,8 @@ } } + iphp->rrp.current = p; + pc->sockaddr = peer->sockaddr; pc->socklen = peer->socklen; pc->name = &peer->name; diff -Nru nginx-0.5.33/src/http/modules/ngx_http_userid_filter_module.c nginx-0.8.53/src/http/modules/ngx_http_userid_filter_module.c --- nginx-0.5.33/src/http/modules/ngx_http_userid_filter_module.c 2006-11-27 14:46:15.000000000 +0000 +++ nginx-0.8.53/src/http/modules/ngx_http_userid_filter_module.c 2010-06-30 14:30:55.000000000 +0000 @@ -41,15 +41,16 @@ } ngx_http_userid_ctx_t; -static void ngx_http_userid_get_uid(ngx_http_request_t *r, - ngx_http_userid_ctx_t *ctx, ngx_http_userid_conf_t *conf); +static ngx_http_userid_ctx_t *ngx_http_userid_get_uid(ngx_http_request_t *r, + ngx_http_userid_conf_t *conf); +static ngx_int_t ngx_http_userid_variable(ngx_http_request_t *r, + ngx_http_variable_value_t *v, ngx_str_t *name, uint32_t *uid); static ngx_int_t ngx_http_userid_set_uid(ngx_http_request_t *r, ngx_http_userid_ctx_t *ctx, ngx_http_userid_conf_t *conf); +static ngx_int_t ngx_http_userid_create_uid(ngx_http_request_t *r, + ngx_http_userid_ctx_t *ctx, ngx_http_userid_conf_t *conf); static ngx_int_t ngx_http_userid_add_variables(ngx_conf_t *cf); -static ngx_int_t ngx_http_userid_variable(ngx_http_request_t *r, - ngx_http_variable_value_t *v, uintptr_t data); - static ngx_int_t ngx_http_userid_init(ngx_conf_t *cf); static void *ngx_http_userid_create_conf(ngx_conf_t *cf); static char *ngx_http_userid_merge_conf(ngx_conf_t *cf, void *parent, @@ -61,9 +62,11 @@ static char *ngx_http_userid_p3p(ngx_conf_t *cf, void *post, void *data); static char *ngx_http_userid_mark(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static ngx_int_t ngx_http_userid_init_worker(ngx_cycle_t *cycle); +static uint32_t start_value; static uint32_t sequencer_v1 = 1; static uint32_t sequencer_v2 = 0x03030302; @@ -173,7 +176,7 @@ NGX_HTTP_MODULE, /* module type */ NULL, /* init master */ NULL, /* init module */ - NULL, /* init process */ + ngx_http_userid_init_worker, /* init process */ NULL, /* init thread */ NULL, /* exit thread */ NULL, /* exit process */ @@ -189,7 +192,6 @@ static ngx_int_t ngx_http_userid_filter(ngx_http_request_t *r) { - ngx_int_t rc; ngx_http_userid_ctx_t *ctx; ngx_http_userid_conf_t *conf; @@ -199,60 +201,114 @@ conf = ngx_http_get_module_loc_conf(r, ngx_http_userid_filter_module); - if (conf->enable == NGX_HTTP_USERID_OFF) { + if (conf->enable < NGX_HTTP_USERID_V1) { return ngx_http_next_header_filter(r); } + ctx = ngx_http_userid_get_uid(r, conf); - ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_userid_ctx_t)); if (ctx == NULL) { return NGX_ERROR; } - ngx_http_set_ctx(r, ctx, ngx_http_userid_filter_module); + if (ngx_http_userid_set_uid(r, ctx, conf) == NGX_OK) { + return ngx_http_next_header_filter(r); + } - ngx_http_userid_get_uid(r, ctx, conf); + return NGX_ERROR; +} - if (conf->enable == NGX_HTTP_USERID_LOG) { - return ngx_http_next_header_filter(r); + +static ngx_int_t +ngx_http_userid_got_variable(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + ngx_http_userid_ctx_t *ctx; + ngx_http_userid_conf_t *conf; + + conf = ngx_http_get_module_loc_conf(r->main, ngx_http_userid_filter_module); + + if (conf->enable == NGX_HTTP_USERID_OFF) { + v->not_found = 1; + return NGX_OK; + } + + ctx = ngx_http_userid_get_uid(r->main, conf); + + if (ctx == NULL) { + return NGX_ERROR; } if (ctx->uid_got[3] != 0) { - if (conf->mark == '\0') { - return ngx_http_next_header_filter(r); + return ngx_http_userid_variable(r->main, v, &conf->name, ctx->uid_got); + } - } else { - if (ctx->cookie.len > 23 - && ctx->cookie.data[22] == conf->mark - && ctx->cookie.data[23] == '=') - { - return ngx_http_next_header_filter(r); - } - } + v->not_found = 1; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_userid_set_variable(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + ngx_http_userid_ctx_t *ctx; + ngx_http_userid_conf_t *conf; + + conf = ngx_http_get_module_loc_conf(r->main, ngx_http_userid_filter_module); + + if (conf->enable < NGX_HTTP_USERID_V1) { + v->not_found = 1; + return NGX_OK; } - rc = ngx_http_userid_set_uid(r, ctx, conf); + ctx = ngx_http_userid_get_uid(r->main, conf); + + if (ctx == NULL) { + return NGX_ERROR; + } - if (rc != NGX_OK) { - return rc; + if (ngx_http_userid_create_uid(r->main, ctx, conf) != NGX_OK) { + return NGX_ERROR; } - return ngx_http_next_header_filter(r); + if (ctx->uid_set[3] == 0) { + v->not_found = 1; + return NGX_OK; + } + + return ngx_http_userid_variable(r->main, v, &conf->name, ctx->uid_set); } -static void -ngx_http_userid_get_uid(ngx_http_request_t *r, ngx_http_userid_ctx_t *ctx, - ngx_http_userid_conf_t *conf) +static ngx_http_userid_ctx_t * +ngx_http_userid_get_uid(ngx_http_request_t *r, ngx_http_userid_conf_t *conf) { - ngx_int_t n; - ngx_str_t src, dst; - ngx_table_elt_t **cookies; + ngx_int_t n; + ngx_str_t src, dst; + ngx_table_elt_t **cookies; + ngx_http_userid_ctx_t *ctx; + + ctx = ngx_http_get_module_ctx(r, ngx_http_userid_filter_module); + + if (ctx) { + return ctx; + } + + if (ctx == NULL) { + ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_userid_ctx_t)); + if (ctx == NULL) { + return NULL; + } + + ngx_http_set_ctx(r, ctx, ngx_http_userid_filter_module); + } n = ngx_http_parse_multi_header_lines(&r->headers_in.cookies, &conf->name, &ctx->cookie); if (n == NGX_DECLINED) { - return; + return ctx; } ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, @@ -263,7 +319,7 @@ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "client sent too short userid cookie \"%V\"", &cookies[n]->value); - return; + return ctx; } src = ctx->cookie; @@ -284,13 +340,15 @@ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "client sent invalid userid cookie \"%V\"", &cookies[n]->value); - return; + return ctx; } ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "uid: %08XD%08XD%08XD%08XD", ctx->uid_got[0], ctx->uid_got[1], ctx->uid_got[2], ctx->uid_got[3]); + + return ctx; } @@ -298,67 +356,17 @@ ngx_http_userid_set_uid(ngx_http_request_t *r, ngx_http_userid_ctx_t *ctx, ngx_http_userid_conf_t *conf) { - u_char *cookie, *p; - size_t len; - socklen_t slen; - struct sockaddr_in sin; - ngx_str_t src, dst; - ngx_table_elt_t *set_cookie, *p3p; - - /* - * TODO: in the threaded mode the sequencers should be in TLS and their - * ranges should be divided between threads - */ + u_char *cookie, *p; + size_t len; + ngx_str_t src, dst; + ngx_table_elt_t *set_cookie, *p3p; - if (ctx->uid_got[3] == 0) { - - if (conf->enable == NGX_HTTP_USERID_V1) { - if (conf->service == NGX_CONF_UNSET) { - ctx->uid_set[0] = 0; - } else { - ctx->uid_set[0] = conf->service; - } - ctx->uid_set[1] = ngx_time(); - ctx->uid_set[2] = ngx_pid; - ctx->uid_set[3] = sequencer_v1; - sequencer_v1 += 0x100; - - } else { - if (conf->service == NGX_CONF_UNSET) { - if (r->in_addr == 0) { - slen = sizeof(struct sockaddr_in); - if (getsockname(r->connection->fd, - (struct sockaddr *) &sin, &slen) - == -1) - { - ngx_connection_error(r->connection, ngx_socket_errno, - "getsockname() failed"); - return NGX_ERROR; - } - - r->in_addr = sin.sin_addr.s_addr; - } - - ctx->uid_set[0] = htonl(r->in_addr); - - } else { - ctx->uid_set[0] = htonl(conf->service); - } - - ctx->uid_set[1] = htonl(ngx_time()); - ctx->uid_set[2] = htonl(ngx_pid); - ctx->uid_set[3] = htonl(sequencer_v2); - sequencer_v2 += 0x100; - if (sequencer_v2 < 0x03030302) { - sequencer_v2 = 0x03030302; - } - } + if (ngx_http_userid_create_uid(r, ctx, conf) != NGX_OK) { + return NGX_ERROR; + } - } else { - ctx->uid_set[0] = ctx->uid_got[0]; - ctx->uid_set[1] = ctx->uid_got[1]; - ctx->uid_set[2] = ctx->uid_got[2]; - ctx->uid_set[3] = ctx->uid_got[3]; + if (ctx->uid_set[3] == 0) { + return NGX_OK; } len = conf->name.len + 1 + ngx_base64_encoded_length(16) + conf->path.len; @@ -371,7 +379,7 @@ len += conf->domain.len; } - cookie = ngx_palloc(r->pool, len); + cookie = ngx_pnalloc(r->pool, len); if (cookie == NULL) { return NGX_ERROR; } @@ -416,8 +424,7 @@ } set_cookie->hash = 1; - set_cookie->key.len = sizeof("Set-Cookie") - 1; - set_cookie->key.data = (u_char *) "Set-Cookie"; + ngx_str_set(&set_cookie->key, "Set-Cookie"); set_cookie->value.len = p - cookie; set_cookie->value.data = cookie; @@ -434,8 +441,7 @@ } p3p->hash = 1; - p3p->key.len = sizeof("P3P") - 1; - p3p->key.data = (u_char *) "P3P"; + ngx_str_set(&p3p->key, "P3P"); p3p->value = conf->p3p; return NGX_OK; @@ -443,61 +449,140 @@ static ngx_int_t -ngx_http_userid_add_variables(ngx_conf_t *cf) +ngx_http_userid_create_uid(ngx_http_request_t *r, ngx_http_userid_ctx_t *ctx, + ngx_http_userid_conf_t *conf) { - ngx_http_variable_t *var; + ngx_connection_t *c; + struct sockaddr_in *sin; +#if (NGX_HAVE_INET6) + u_char *p; + struct sockaddr_in6 *sin6; +#endif - var = ngx_http_add_variable(cf, &ngx_http_userid_got, NGX_HTTP_VAR_NOHASH); - if (var == NULL) { - return NGX_ERROR; + if (ctx->uid_set[3] != 0) { + return NGX_OK; } - var->get_handler = ngx_http_userid_variable; - var->data = offsetof(ngx_http_userid_ctx_t, uid_got); + if (ctx->uid_got[3] != 0) { - var = ngx_http_add_variable(cf, &ngx_http_userid_set, NGX_HTTP_VAR_NOHASH); - if (var == NULL) { - return NGX_ERROR; + if (conf->mark == '\0' + || (ctx->cookie.len > 23 + && ctx->cookie.data[22] == conf->mark + && ctx->cookie.data[23] == '=')) + { + return NGX_OK; + } + + ctx->uid_set[0] = ctx->uid_got[0]; + ctx->uid_set[1] = ctx->uid_got[1]; + ctx->uid_set[2] = ctx->uid_got[2]; + ctx->uid_set[3] = ctx->uid_got[3]; + + return NGX_OK; } - var->get_handler = ngx_http_userid_variable; - var->data = offsetof(ngx_http_userid_ctx_t, uid_set); + /* + * TODO: in the threaded mode the sequencers should be in TLS and their + * ranges should be divided between threads + */ - return NGX_OK; -} + if (conf->enable == NGX_HTTP_USERID_V1) { + if (conf->service == NGX_CONF_UNSET) { + ctx->uid_set[0] = 0; + } else { + ctx->uid_set[0] = conf->service; + } + ctx->uid_set[1] = (uint32_t) ngx_time(); + ctx->uid_set[2] = start_value; + ctx->uid_set[3] = sequencer_v1; + sequencer_v1 += 0x100; + } else { + if (conf->service == NGX_CONF_UNSET) { -static ngx_int_t -ngx_http_userid_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, - uintptr_t data) -{ - uint32_t *uid; - ngx_http_userid_ctx_t *ctx; - ngx_http_userid_conf_t *conf; + c = r->connection; - ctx = ngx_http_get_module_ctx(r, ngx_http_userid_filter_module); + if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) { + return NGX_ERROR; + } - uid = (uint32_t *) ((char *) ctx + data); + switch (c->local_sockaddr->sa_family) { - if (ctx == NULL || uid[3] == 0) { - v->not_found = 1; - return NGX_OK; +#if (NGX_HAVE_INET6) + case AF_INET6: + sin6 = (struct sockaddr_in6 *) c->local_sockaddr; + + p = (u_char *) &ctx->uid_set[0]; + + *p++ = sin6->sin6_addr.s6_addr[12]; + *p++ = sin6->sin6_addr.s6_addr[13]; + *p++ = sin6->sin6_addr.s6_addr[14]; + *p = sin6->sin6_addr.s6_addr[15]; + + break; +#endif + default: /* AF_INET */ + sin = (struct sockaddr_in *) c->local_sockaddr; + ctx->uid_set[0] = sin->sin_addr.s_addr; + break; + } + + } else { + ctx->uid_set[0] = htonl(conf->service); + } + + ctx->uid_set[1] = htonl((uint32_t) ngx_time()); + ctx->uid_set[2] = htonl(start_value); + ctx->uid_set[3] = htonl(sequencer_v2); + sequencer_v2 += 0x100; + if (sequencer_v2 < 0x03030302) { + sequencer_v2 = 0x03030302; + } } - conf = ngx_http_get_module_loc_conf(r, ngx_http_userid_filter_module); + return NGX_OK; +} + - v->len = conf->name.len + sizeof("=00001111222233334444555566667777") - 1; - v->data = ngx_palloc(r->pool, v->len); +static ngx_int_t +ngx_http_userid_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, + ngx_str_t *name, uint32_t *uid) +{ + v->len = name->len + sizeof("=00001111222233334444555566667777") - 1; + v->data = ngx_pnalloc(r->pool, v->len); if (v->data == NULL) { return NGX_ERROR; } v->valid = 1; - v->no_cachable = 0; + v->no_cacheable = 0; v->not_found = 0; ngx_sprintf(v->data, "%V=%08XD%08XD%08XD%08XD", - &conf->name, uid[0], uid[1], uid[2], uid[3]); + name, uid[0], uid[1], uid[2], uid[3]); + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_userid_add_variables(ngx_conf_t *cf) +{ + ngx_http_variable_t *var; + + var = ngx_http_add_variable(cf, &ngx_http_userid_got, 0); + if (var == NULL) { + return NGX_ERROR; + } + + var->get_handler = ngx_http_userid_got_variable; + + var = ngx_http_add_variable(cf, &ngx_http_userid_set, 0); + if (var == NULL) { + return NGX_ERROR; + } + + var->get_handler = ngx_http_userid_set_variable; return NGX_OK; } @@ -510,20 +595,16 @@ conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_userid_conf_t)); if (conf == NULL) { - return NGX_CONF_ERROR; + return NULL; } /* * set by ngx_pcalloc(): * - * conf->name.len = 0; - * conf->name.date = NULL; - * conf->domain.len = 0; - * conf->domain.date = NULL; - * conf->path.len = 0; - * conf->path.date = NULL; - * conf->p3p.len = 0; - * conf->p3p.date = NULL; + * conf->name = { 0, NULL }; + * conf->domain = { 0, NULL }; + * conf->path = { 0, NULL }; + * conf->p3p = { 0, NULL }; */ conf->enable = NGX_CONF_UNSET_UINT; @@ -582,13 +663,11 @@ u_char *p, *new; if (ngx_strcmp(domain->data, "none") == 0) { - domain->len = 0; - domain->data = (u_char *) ""; - + ngx_str_set(domain, ""); return NGX_CONF_OK; } - new = ngx_palloc(cf->pool, sizeof("; domain=") - 1 + domain->len); + new = ngx_pnalloc(cf->pool, sizeof("; domain=") - 1 + domain->len); if (new == NULL) { return NGX_CONF_ERROR; } @@ -610,7 +689,7 @@ u_char *p, *new; - new = ngx_palloc(cf->pool, sizeof("; path=") - 1 + path->len); + new = ngx_pnalloc(cf->pool, sizeof("; path=") - 1 + path->len); if (new == NULL) { return NGX_CONF_ERROR; } @@ -667,8 +746,7 @@ ngx_str_t *p3p = data; if (ngx_strcmp(p3p->data, "none") == 0) { - p3p->len = 0; - p3p->data = (u_char *) ""; + ngx_str_set(p3p, ""); } return NGX_CONF_OK; @@ -706,3 +784,17 @@ return NGX_CONF_OK; } + + +static ngx_int_t +ngx_http_userid_init_worker(ngx_cycle_t *cycle) +{ + struct timeval tp; + + ngx_gettimeofday(&tp); + + /* use the most significant usec part that fits to 16 bits */ + start_value = ((tp.tv_usec / 20) << 16) | ngx_pid; + + return NGX_OK; +} diff -Nru nginx-0.5.33/src/http/modules/ngx_http_uwsgi_module.c nginx-0.8.53/src/http/modules/ngx_http_uwsgi_module.c --- nginx-0.5.33/src/http/modules/ngx_http_uwsgi_module.c 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/src/http/modules/ngx_http_uwsgi_module.c 2010-08-03 09:24:25.000000000 +0000 @@ -0,0 +1,1713 @@ + +/* + * Copyright (C) Unbit S.a.s. 2009-2010 + * Copyright (C) 2008 Manlio Perillo (manlio.perillo@gmail.com) + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include + + +typedef struct { + ngx_http_upstream_conf_t upstream; + + ngx_array_t *flushes; + ngx_array_t *params_len; + ngx_array_t *params; + ngx_array_t *params_source; + + ngx_hash_t headers_hash; + ngx_uint_t header_params; + + ngx_array_t *uwsgi_lengths; + ngx_array_t *uwsgi_values; + +#if (NGX_HTTP_CACHE) + ngx_http_complex_value_t cache_key; +#endif + + ngx_str_t uwsgi_string; + + ngx_uint_t modifier1; + ngx_uint_t modifier2; +} ngx_http_uwsgi_loc_conf_t; + + +static ngx_int_t ngx_http_uwsgi_eval(ngx_http_request_t *r, + ngx_http_uwsgi_loc_conf_t *uwcf); +static ngx_int_t ngx_http_uwsgi_create_request(ngx_http_request_t *r); +static ngx_int_t ngx_http_uwsgi_reinit_request(ngx_http_request_t *r); +static ngx_int_t ngx_http_uwsgi_process_status_line(ngx_http_request_t *r); +static ngx_int_t ngx_http_uwsgi_process_header(ngx_http_request_t *r); +static ngx_int_t ngx_http_uwsgi_process_header(ngx_http_request_t *r); +static void ngx_http_uwsgi_abort_request(ngx_http_request_t *r); +static void ngx_http_uwsgi_finalize_request(ngx_http_request_t *r, + ngx_int_t rc); + +static void *ngx_http_uwsgi_create_loc_conf(ngx_conf_t *cf); +static char *ngx_http_uwsgi_merge_loc_conf(ngx_conf_t *cf, void *parent, + void *child); + +static char *ngx_http_uwsgi_pass(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static char *ngx_http_uwsgi_store(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); + +#if (NGX_HTTP_CACHE) +static ngx_int_t ngx_http_uwsgi_create_key(ngx_http_request_t *r); +static char *ngx_http_uwsgi_cache(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static char *ngx_http_uwsgi_cache_key(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +#endif + + +static ngx_conf_num_bounds_t ngx_http_uwsgi_modifier_bounds = { + ngx_conf_check_num_bounds, 0, 255 +}; + + +static ngx_conf_bitmask_t ngx_http_uwsgi_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("http_500"), NGX_HTTP_UPSTREAM_FT_HTTP_500 }, + { ngx_string("http_503"), NGX_HTTP_UPSTREAM_FT_HTTP_503 }, + { ngx_string("http_404"), NGX_HTTP_UPSTREAM_FT_HTTP_404 }, + { 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_uwsgi_module; + + +static ngx_command_t ngx_http_uwsgi_commands[] = { + + { ngx_string("uwsgi_pass"), + NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1, + ngx_http_uwsgi_pass, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("uwsgi_modifier1"), + 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_uwsgi_loc_conf_t, modifier1), + &ngx_http_uwsgi_modifier_bounds }, + + { ngx_string("uwsgi_modifier2"), + 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_uwsgi_loc_conf_t, modifier2), + &ngx_http_uwsgi_modifier_bounds }, + + { ngx_string("uwsgi_store"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_http_uwsgi_store, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("uwsgi_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_uwsgi_loc_conf_t, upstream.store_access), + NULL }, + + { ngx_string("uwsgi_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_uwsgi_loc_conf_t, upstream.ignore_client_abort), + NULL }, + + { ngx_string("uwsgi_bind"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_http_upstream_bind_set_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_uwsgi_loc_conf_t, upstream.local), + NULL }, + + { ngx_string("uwsgi_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_uwsgi_loc_conf_t, upstream.connect_timeout), + NULL }, + + { ngx_string("uwsgi_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_uwsgi_loc_conf_t, upstream.send_timeout), + NULL }, + + { ngx_string("uwsgi_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_uwsgi_loc_conf_t, upstream.buffer_size), + NULL }, + + { ngx_string("uwsgi_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_uwsgi_loc_conf_t, upstream.pass_request_headers), + NULL }, + + { ngx_string("uwsgi_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_uwsgi_loc_conf_t, upstream.pass_request_body), + NULL }, + + { ngx_string("uwsgi_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_uwsgi_loc_conf_t, upstream.intercept_errors), + NULL }, + + { ngx_string("uwsgi_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_uwsgi_loc_conf_t, upstream.read_timeout), + NULL }, + + { ngx_string("uwsgi_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_uwsgi_loc_conf_t, upstream.bufs), + NULL }, + + { ngx_string("uwsgi_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_uwsgi_loc_conf_t, upstream.busy_buffers_size_conf), + NULL }, + +#if (NGX_HTTP_CACHE) + + { ngx_string("uwsgi_cache"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_http_uwsgi_cache, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("uwsgi_cache_key"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_http_uwsgi_cache_key, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("uwsgi_cache_path"), + NGX_HTTP_MAIN_CONF|NGX_CONF_2MORE, + ngx_http_file_cache_set_slot, + 0, + 0, + &ngx_http_uwsgi_module }, + + { ngx_string("uwsgi_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_uwsgi_loc_conf_t, upstream.cache_bypass), + NULL }, + + { ngx_string("uwsgi_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_uwsgi_loc_conf_t, upstream.no_cache), + NULL }, + + { ngx_string("uwsgi_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_uwsgi_loc_conf_t, upstream.cache_valid), + NULL }, + + { ngx_string("uwsgi_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_uwsgi_loc_conf_t, upstream.cache_min_uses), + NULL }, + + { ngx_string("uwsgi_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_uwsgi_loc_conf_t, upstream.cache_use_stale), + &ngx_http_uwsgi_next_upstream_masks }, + + { ngx_string("uwsgi_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_uwsgi_loc_conf_t, upstream.cache_methods), + &ngx_http_upstream_cache_method_mask }, + +#endif + + { ngx_string("uwsgi_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_uwsgi_loc_conf_t, upstream.temp_path), + NULL }, + + { ngx_string("uwsgi_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_uwsgi_loc_conf_t, upstream.max_temp_file_size_conf), + NULL }, + + { ngx_string("uwsgi_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_uwsgi_loc_conf_t, upstream.temp_file_write_size_conf), + NULL }, + + { ngx_string("uwsgi_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_uwsgi_loc_conf_t, upstream.next_upstream), + &ngx_http_uwsgi_next_upstream_masks }, + + { ngx_string("uwsgi_param"), + 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_uwsgi_loc_conf_t, params_source), + NULL }, + + { ngx_string("uwsgi_string"), + 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_uwsgi_loc_conf_t, uwsgi_string), + NULL }, + + { ngx_string("uwsgi_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_uwsgi_loc_conf_t, upstream.pass_headers), + NULL }, + + { ngx_string("uwsgi_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_uwsgi_loc_conf_t, upstream.hide_headers), + NULL }, + + { ngx_string("uwsgi_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_uwsgi_loc_conf_t, upstream.ignore_headers), + &ngx_http_upstream_ignore_headers_masks }, + + ngx_null_command +}; + + +static ngx_http_module_t ngx_http_uwsgi_module_ctx = { + NULL, /* preconfiguration */ + NULL, /* postconfiguration */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + NULL, /* create server configuration */ + NULL, /* merge server configuration */ + + ngx_http_uwsgi_create_loc_conf, /* create location configuration */ + ngx_http_uwsgi_merge_loc_conf /* merge location configuration */ +}; + + +ngx_module_t ngx_http_uwsgi_module = { + NGX_MODULE_V1, + &ngx_http_uwsgi_module_ctx, /* module context */ + ngx_http_uwsgi_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_uwsgi_hide_headers[] = { + 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_uwsgi_cache_headers[] = { + { ngx_string("HTTP_IF_MODIFIED_SINCE"), ngx_string("") }, + { ngx_string("HTTP_IF_UNMODIFIED_SINCE"), ngx_string("") }, + { ngx_string("HTTP_IF_NONE_MATCH"), ngx_string("") }, + { 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_uwsgi_temp_path = { + ngx_string(NGX_HTTP_UWSGI_TEMP_PATH), { 1, 2, 0 } +}; + + +static ngx_int_t +ngx_http_uwsgi_handler(ngx_http_request_t *r) +{ + ngx_int_t rc; + ngx_http_status_t *status; + ngx_http_upstream_t *u; + ngx_http_uwsgi_loc_conf_t *uwcf; + + if (r->subrequest_in_memory) { + ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, + "ngx_http_uwsgi_module does not support " + "subrequests in memory"); + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + 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_uwsgi_module); + + uwcf = ngx_http_get_module_loc_conf(r, ngx_http_uwsgi_module); + + if (uwcf->uwsgi_lengths) { + if (ngx_http_uwsgi_eval(r, uwcf) != NGX_OK) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + } + + u = r->upstream; + + ngx_str_set(&u->schema, "uwsgi://"); + u->output.tag = (ngx_buf_tag_t) &ngx_http_uwsgi_module; + + u->conf = &uwcf->upstream; + +#if (NGX_HTTP_CACHE) + u->create_key = ngx_http_uwsgi_create_key; +#endif + u->create_request = ngx_http_uwsgi_create_request; + u->reinit_request = ngx_http_uwsgi_reinit_request; + u->process_header = ngx_http_uwsgi_process_status_line; + u->abort_request = ngx_http_uwsgi_abort_request; + u->finalize_request = ngx_http_uwsgi_finalize_request; + + u->buffering = 1; + + 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; + + 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_uwsgi_eval(ngx_http_request_t *r, ngx_http_uwsgi_loc_conf_t * uwcf) +{ + ngx_url_t url; + ngx_http_upstream_t *u; + + ngx_memzero(&url, sizeof(ngx_url_t)); + + if (ngx_http_script_run(r, &url.url, uwcf->uwsgi_lengths->elts, 0, + uwcf->uwsgi_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; + } + + if (url.no_port) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "no port in upstream \"%V\"", &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 && url.addrs[0].sockaddr) { + u->resolved->sockaddr = url.addrs[0].sockaddr; + u->resolved->socklen = url.addrs[0].socklen; + u->resolved->naddrs = 1; + u->resolved->host = url.addrs[0].name; + + } else { + u->resolved->host = url.host; + u->resolved->port = url.port; + } + + return NGX_OK; +} + + +#if (NGX_HTTP_CACHE) + +static ngx_int_t +ngx_http_uwsgi_create_key(ngx_http_request_t *r) +{ + ngx_str_t *key; + ngx_http_uwsgi_loc_conf_t *uwcf; + + key = ngx_array_push(&r->cache->keys); + if (key == NULL) { + return NGX_ERROR; + } + + uwcf = ngx_http_get_module_loc_conf(r, ngx_http_uwsgi_module); + + if (ngx_http_complex_value(r, &uwcf->cache_key, key) != NGX_OK) { + return NGX_ERROR; + } + + return NGX_OK; +} + +#endif + + +static ngx_int_t +ngx_http_uwsgi_create_request(ngx_http_request_t *r) +{ + u_char ch, *lowcase_key; + size_t key_len, val_len, len, allocated; + ngx_uint_t i, n, hash, header_params; + ngx_buf_t *b; + ngx_chain_t *cl, *body; + ngx_list_part_t *part; + ngx_table_elt_t *header, **ignored; + ngx_http_script_code_pt code; + ngx_http_script_engine_t e, le; + ngx_http_uwsgi_loc_conf_t *uwcf; + ngx_http_script_len_code_pt lcode; + + len = 0; + header_params = 0; + ignored = NULL; + + uwcf = ngx_http_get_module_loc_conf(r, ngx_http_uwsgi_module); + + if (uwcf->params_len) { + ngx_memzero(&le, sizeof(ngx_http_script_engine_t)); + + ngx_http_script_flush_no_cacheable_variables(r, uwcf->flushes); + le.flushed = 1; + + le.ip = uwcf->params_len->elts; + le.request = r; + + 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); + + len += 2 + key_len + 2 + val_len; + } + } + + if (uwcf->upstream.pass_request_headers) { + + allocated = 0; + lowcase_key = NULL; + + if (uwcf->header_params) { + ignored = ngx_palloc(r->pool, uwcf->header_params * 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 (uwcf->header_params) { + 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(&uwcf->headers_hash, hash, lowcase_key, n)) { + ignored[header_params++] = &header[i]; + continue; + } + } + + len += 2 + sizeof("HTTP_") - 1 + header[i].key.len + + 2 + header[i].value.len; + } + } + + len += uwcf->uwsgi_string.len; + +#if 0 + /* allow custom uwsgi packet */ + if (len > 0 && len < 2) { + ngx_log_error (NGX_LOG_ALERT, r->connection->log, 0, + "uwsgi request is too little: %uz", len); + return NGX_ERROR; + } +#endif + + b = ngx_create_temp_buf(r->pool, len + 4); + if (b == NULL) { + return NGX_ERROR; + } + + cl = ngx_alloc_chain_link(r->pool); + if (cl == NULL) { + return NGX_ERROR; + } + + cl->buf = b; + + *b->last++ = (u_char) uwcf->modifier1; + *b->last++ = (u_char) (len & 0xff); + *b->last++ = (u_char) ((len >> 8) & 0xff); + *b->last++ = (u_char) uwcf->modifier2; + + if (uwcf->params_len) { + ngx_memzero(&e, sizeof(ngx_http_script_engine_t)); + + e.ip = uwcf->params->elts; + e.pos = b->last; + e.request = r; + e.flushed = 1; + + le.ip = uwcf->params_len->elts; + + while (*(uintptr_t *) le.ip) { + + lcode = *(ngx_http_script_len_code_pt *) le.ip; + key_len = (u_char) 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); + + *e.pos++ = (u_char) (key_len & 0xff); + *e.pos++ = (u_char) ((key_len >> 8) & 0xff); + + code = *(ngx_http_script_code_pt *) e.ip; + code((ngx_http_script_engine_t *) & e); + + *e.pos++ = (u_char) (val_len & 0xff); + *e.pos++ = (u_char) ((val_len >> 8) & 0xff); + + 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, + "uwsgi param: \"%*s: %*s\"", + key_len, e.pos - (key_len + 2 + val_len), + val_len, e.pos - val_len); + } + + b->last = e.pos; + } + + if (uwcf->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; + *b->last++ = (u_char) (key_len & 0xff); + *b->last++ = (u_char) ((key_len >> 8) & 0xff); + + 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; + } + + val_len = header[i].value.len; + *b->last++ = (u_char) (val_len & 0xff); + *b->last++ = (u_char) ((val_len >> 8) & 0xff); + b->last = ngx_copy(b->last, header[i].value.data, val_len); + + ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "uwsgi param: \"%*s: %*s\"", + key_len, b->last - (key_len + 2 + val_len), + val_len, b->last - val_len); + next: + + continue; + } + } + + b->last = ngx_copy(b->last, uwcf->uwsgi_string.data, + uwcf->uwsgi_string.len); + + if (uwcf->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_uwsgi_reinit_request(ngx_http_request_t *r) +{ + ngx_http_status_t *status; + + status = ngx_http_get_module_ctx(r, ngx_http_uwsgi_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_uwsgi_process_status_line; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_uwsgi_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_uwsgi_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) { + r->http_version = NGX_HTTP_VERSION_9; + + u->process_header = ngx_http_uwsgi_process_header; + + return ngx_http_uwsgi_process_header(r); + } + + if (u->state) { + 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 uwsgi status %ui \"%V\"", + u->headers_in.status_n, &u->headers_in.status_line); + + u->process_header = ngx_http_uwsgi_process_header; + + return ngx_http_uwsgi_process_header(r); +} + + +static ngx_int_t +ngx_http_uwsgi_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) { + 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_cpystrn(h->key.data, r->header_name_start, h->key.len + 1); + ngx_cpystrn(h->value.data, r->header_start, h->value.len + 1); + + 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 uwsgi 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 uwsgi header done"); + + if (r->http_version > NGX_HTTP_VERSION_9) { + return NGX_OK; + } + + u = r->upstream; + + 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; + } + + r->http_version = NGX_HTTP_VERSION_10; + u->headers_in.status_n = status; + u->headers_in.status_line = *status_line; + + } else if (u->headers_in.location) { + r->http_version = NGX_HTTP_VERSION_10; + u->headers_in.status_n = 302; + ngx_str_set(&u->headers_in.status_line, + "302 Moved Temporarily"); + + } else { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "upstream sent neither valid HTTP/1.0 header " + "nor \"Status\" header line"); + u->headers_in.status_n = 200; + ngx_str_set(&u->headers_in.status_line, "200 OK"); + } + + if (u->state) { + u->state->status = u->headers_in.status_n; + } + + 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_uwsgi_abort_request(ngx_http_request_t *r) +{ + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "abort http uwsgi request"); + + return; +} + + +static void +ngx_http_uwsgi_finalize_request(ngx_http_request_t *r, ngx_int_t rc) +{ + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "finalize http uwsgi request"); + + return; +} + + +static void * +ngx_http_uwsgi_create_loc_conf(ngx_conf_t *cf) +{ + ngx_http_uwsgi_loc_conf_t *conf; + + conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_uwsgi_loc_conf_t)); + if (conf == NULL) { + return NULL; + } + + conf->modifier1 = NGX_CONF_UNSET_UINT; + conf->modifier2 = NGX_CONF_UNSET_UINT; + + conf->upstream.store = NGX_CONF_UNSET; + conf->upstream.store_access = NGX_CONF_UNSET_UINT; + conf->upstream.buffering = NGX_CONF_UNSET; + conf->upstream.ignore_client_abort = NGX_CONF_UNSET; + + 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.send_lowat = NGX_CONF_UNSET_SIZE; + conf->upstream.buffer_size = 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_PTR; + conf->upstream.cache_min_uses = NGX_CONF_UNSET_UINT; + conf->upstream.cache_bypass = NGX_CONF_UNSET_PTR; + conf->upstream.no_cache = NGX_CONF_UNSET_PTR; + conf->upstream.cache_valid = NGX_CONF_UNSET_PTR; +#endif + + conf->upstream.hide_headers = NGX_CONF_UNSET_PTR; + conf->upstream.pass_headers = NGX_CONF_UNSET_PTR; + + conf->upstream.intercept_errors = NGX_CONF_UNSET; + + /* "uwsgi_cyclic_temp_file" is disabled */ + conf->upstream.cyclic_temp_file = 0; + + return conf; +} + + +static char * +ngx_http_uwsgi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) +{ + ngx_http_uwsgi_loc_conf_t *prev = parent; + ngx_http_uwsgi_loc_conf_t *conf = child; + + u_char *p; + size_t size; + uintptr_t *code; + ngx_uint_t i; + ngx_array_t headers_names; + ngx_keyval_t *src; + ngx_hash_key_t *hk; + ngx_hash_init_t hash; + ngx_http_core_loc_conf_t *clcf; + ngx_http_script_compile_t sc; + ngx_http_script_copy_code_t *copy; + + if (conf->upstream.store != 0) { + ngx_conf_merge_value(conf->upstream.store, prev->upstream.store, 0); + + if (conf->upstream.store_lengths == NULL) { + 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_value(conf->upstream.buffering, + prev->upstream.buffering, 1); + + ngx_conf_merge_value(conf->upstream.ignore_client_abort, + prev->upstream.ignore_client_abort, 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_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_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 \"uwsgi_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, + "\"uwsgi_busy_buffers_size\" must be equal or bigger " + "than maximum of the value of \"uwsgi_buffer_size\" and " + "one of the \"uwsgi_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, + "\"uwsgi_busy_buffers_size\" must be less than " + "the size of all \"uwsgi_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, + "\"uwsgi_temp_file_write_size\" must be equal or bigger than " + "maximum of the value of \"uwsgi_buffer_size\" and " + "one of the \"uwsgi_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, + "\"uwsgi_max_temp_file_size\" must be equal to zero to disable " + "the temporary files usage or must be equal or bigger than " + "maximum of the value of \"uwsgi_buffer_size\" and " + "one of the \"uwsgi_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_uwsgi_temp_path) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + +#if (NGX_HTTP_CACHE) + + ngx_conf_merge_ptr_value(conf->upstream.cache, + prev->upstream.cache, NULL); + + if (conf->upstream.cache && conf->upstream.cache->data == NULL) { + ngx_shm_zone_t *shm_zone; + + shm_zone = conf->upstream.cache; + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "\"uwsgi_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_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_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; + } + +#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_str_value(conf->uwsgi_string, prev->uwsgi_string, ""); + + hash.max_size = 512; + hash.bucket_size = ngx_align(64, ngx_cacheline_size); + hash.name = "uwsgi_hide_headers_hash"; + + if (ngx_http_upstream_hide_headers_hash(cf, &conf->upstream, + &prev->upstream, ngx_http_uwsgi_hide_headers, &hash) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + + if (conf->upstream.upstream == NULL) { + conf->upstream.upstream = prev->upstream.upstream; + } + + if (conf->uwsgi_lengths == NULL) { + conf->uwsgi_lengths = prev->uwsgi_lengths; + conf->uwsgi_values = prev->uwsgi_values; + } + + if (conf->upstream.upstream || conf->uwsgi_lengths) { + clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); + if (clcf->handler == NULL && clcf->lmt_excpt) { + clcf->handler = ngx_http_uwsgi_handler; + } + } + + ngx_conf_merge_uint_value(conf->modifier1, prev->modifier1, 0); + ngx_conf_merge_uint_value(conf->modifier2, prev->modifier2, 0); + + if (conf->params_source == NULL) { + conf->flushes = prev->flushes; + conf->params_len = prev->params_len; + conf->params = prev->params; + conf->params_source = prev->params_source; + conf->headers_hash = prev->headers_hash; + +#if (NGX_HTTP_CACHE) + + if (conf->params_source == NULL) { + + if ((conf->upstream.cache == NULL) + == (prev->upstream.cache == NULL)) + { + return NGX_CONF_OK; + } + + /* 6 is a number of ngx_http_uwsgi_cache_headers entries */ + conf->params_source = ngx_array_create(cf->pool, 6, + sizeof(ngx_keyval_t)); + if (conf->params_source == NULL) { + return NGX_CONF_ERROR; + } + } +#else + + if (conf->params_source == NULL) { + return NGX_CONF_OK; + } + +#endif + } + + conf->params_len = ngx_array_create(cf->pool, 64, 1); + if (conf->params_len == NULL) { + return NGX_CONF_ERROR; + } + + conf->params = ngx_array_create(cf->pool, 512, 1); + if (conf->params == NULL) { + return NGX_CONF_ERROR; + } + + if (ngx_array_init(&headers_names, cf->temp_pool, 4, sizeof(ngx_hash_key_t)) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + + src = conf->params_source->elts; + +#if (NGX_HTTP_CACHE) + + if (conf->upstream.cache) { + ngx_keyval_t *h, *s; + + for (h = ngx_http_uwsgi_cache_headers; h->key.len; h++) { + + for (i = 0; i < conf->params_source->nelts; i++) { + if (ngx_strcasecmp(h->key.data, src[i].key.data) == 0) { + goto next; + } + } + + s = ngx_array_push(conf->params_source); + if (s == NULL) { + return NGX_CONF_ERROR; + } + + *s = *h; + + src = conf->params_source->elts; + + next: + + h++; + } + } + +#endif + + for (i = 0; i < conf->params_source->nelts; 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_CONF_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(conf->params_len, + sizeof(ngx_http_script_copy_code_t)); + if (copy == NULL) { + return NGX_CONF_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(conf->params, size); + if (copy == NULL) { + return NGX_CONF_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 = &conf->flushes; + sc.lengths = &conf->params_len; + sc.values = &conf->params; + + if (ngx_http_script_compile(&sc) != NGX_OK) { + return NGX_CONF_ERROR; + } + + code = ngx_array_push_n(conf->params_len, sizeof(uintptr_t)); + if (code == NULL) { + return NGX_CONF_ERROR; + } + + *code = (uintptr_t) NULL; + + + code = ngx_array_push_n(conf->params, sizeof(uintptr_t)); + if (code == NULL) { + return NGX_CONF_ERROR; + } + + *code = (uintptr_t) NULL; + } + + code = ngx_array_push_n(conf->params_len, sizeof(uintptr_t)); + if (code == NULL) { + return NGX_CONF_ERROR; + } + + *code = (uintptr_t) NULL; + + conf->header_params = headers_names.nelts; + + hash.hash = &conf->headers_hash; + hash.key = ngx_hash_key_lc; + hash.max_size = 512; + hash.bucket_size = 64; + hash.name = "uwsgi_params_hash"; + hash.pool = cf->pool; + hash.temp_pool = NULL; + + if (ngx_hash_init(&hash, headers_names.elts, headers_names.nelts) != NGX_OK) + { + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; +} + + +static char * +ngx_http_uwsgi_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_http_uwsgi_loc_conf_t *uwcf = 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 (uwcf->upstream.upstream || uwcf->uwsgi_lengths) { + return "is duplicate"; + } + + clcf = ngx_http_conf_get_module_loc_conf (cf, ngx_http_core_module); + clcf->handler = ngx_http_uwsgi_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 = &uwcf->uwsgi_lengths; + sc.values = &uwcf->uwsgi_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; + + uwcf->upstream.upstream = ngx_http_upstream_add(cf, &u, 0); + if (uwcf->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_uwsgi_store(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_http_uwsgi_loc_conf_t *uwcf = conf; + + ngx_str_t *value; + ngx_http_script_compile_t sc; + + if (uwcf->upstream.store != NGX_CONF_UNSET || uwcf->upstream.store_lengths) + { + return "is duplicate"; + } + + value = cf->args->elts; + + if (ngx_strcmp(value[1].data, "off") == 0) { + uwcf->upstream.store = 0; + return NGX_CONF_OK; + } + +#if (NGX_HTTP_CACHE) + + if (uwcf->upstream.cache != NGX_CONF_UNSET_PTR + && uwcf->upstream.cache != NULL) + { + return "is incompatible with \"uwsgi_cache\""; + } + +#endif + + if (ngx_strcmp(value[1].data, "on") == 0) { + uwcf->upstream.store = 1; + 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 = &uwcf->upstream.store_lengths; + sc.values = &uwcf->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_uwsgi_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_http_uwsgi_loc_conf_t *uwcf = conf; + + ngx_str_t *value; + + value = cf->args->elts; + + if (uwcf->upstream.cache != NGX_CONF_UNSET_PTR) { + return "is duplicate"; + } + + if (ngx_strcmp(value[1].data, "off") == 0) { + uwcf->upstream.cache = NULL; + return NGX_CONF_OK; + } + + if (uwcf->upstream.store > 0 || uwcf->upstream.store_lengths) { + return "is incompatible with \"uwsgi_store\""; + } + + uwcf->upstream.cache = ngx_shared_memory_add(cf, &value[1], 0, + &ngx_http_uwsgi_module); + if (uwcf->upstream.cache == NULL) { + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; +} + + +static char * +ngx_http_uwsgi_cache_key(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_http_uwsgi_loc_conf_t *uwcf = conf; + + ngx_str_t *value; + ngx_http_compile_complex_value_t ccv; + + value = cf->args->elts; + + if (uwcf->cache_key.value.len) { + return "is duplicate"; + } + + ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); + + ccv.cf = cf; + ccv.value = &value[1]; + ccv.complex_value = &uwcf->cache_key; + + if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; +} + +#endif diff -Nru nginx-0.5.33/src/http/modules/ngx_http_xslt_filter_module.c nginx-0.8.53/src/http/modules/ngx_http_xslt_filter_module.c --- nginx-0.5.33/src/http/modules/ngx_http_xslt_filter_module.c 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/src/http/modules/ngx_http_xslt_filter_module.c 2010-07-12 12:52:01.000000000 +0000 @@ -0,0 +1,983 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#if (NGX_HAVE_EXSLT) +#include +#endif + + +#ifndef NGX_HTTP_XSLT_REUSE_DTD +#define NGX_HTTP_XSLT_REUSE_DTD 1 +#endif + + +typedef struct { + u_char *name; + void *data; +} ngx_http_xslt_file_t; + + +typedef struct { + ngx_array_t dtd_files; /* ngx_http_xslt_file_t */ + ngx_array_t sheet_files; /* ngx_http_xslt_file_t */ +} ngx_http_xslt_filter_main_conf_t; + + +typedef struct { + xsltStylesheetPtr stylesheet; + ngx_array_t params; /* ngx_http_complex_value_t */ +} ngx_http_xslt_sheet_t; + + +typedef struct { + xmlDtdPtr dtd; + ngx_array_t sheets; /* ngx_http_xslt_sheet_t */ + ngx_hash_t types; + ngx_array_t *types_keys; +} ngx_http_xslt_filter_loc_conf_t; + + +typedef struct { + xmlDocPtr doc; + xmlParserCtxtPtr ctxt; + ngx_http_request_t *request; + ngx_array_t params; + + ngx_uint_t done; /* unsigned done:1; */ +} ngx_http_xslt_filter_ctx_t; + + +static ngx_int_t ngx_http_xslt_send(ngx_http_request_t *r, + ngx_http_xslt_filter_ctx_t *ctx, ngx_buf_t *b); +static ngx_int_t ngx_http_xslt_add_chunk(ngx_http_request_t *r, + ngx_http_xslt_filter_ctx_t *ctx, ngx_buf_t *b); + + +static void ngx_http_xslt_sax_external_subset(void *data, const xmlChar *name, + const xmlChar *externalId, const xmlChar *systemId); +static void ngx_cdecl ngx_http_xslt_sax_error(void *data, const char *msg, ...); + + +static ngx_buf_t *ngx_http_xslt_apply_stylesheet(ngx_http_request_t *r, + ngx_http_xslt_filter_ctx_t *ctx); +static ngx_int_t ngx_http_xslt_params(ngx_http_request_t *r, + ngx_http_xslt_filter_ctx_t *ctx, ngx_array_t *params); +static u_char *ngx_http_xslt_content_type(xsltStylesheetPtr s); +static u_char *ngx_http_xslt_encoding(xsltStylesheetPtr s); +static void ngx_http_xslt_cleanup(void *data); + +static char *ngx_http_xslt_entities(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static char *ngx_http_xslt_stylesheet(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static void ngx_http_xslt_cleanup_dtd(void *data); +static void ngx_http_xslt_cleanup_stylesheet(void *data); +static void *ngx_http_xslt_filter_create_main_conf(ngx_conf_t *cf); +static void *ngx_http_xslt_filter_create_conf(ngx_conf_t *cf); +static char *ngx_http_xslt_filter_merge_conf(ngx_conf_t *cf, void *parent, + void *child); +static ngx_int_t ngx_http_xslt_filter_init(ngx_conf_t *cf); +static void ngx_http_xslt_filter_exit(ngx_cycle_t *cycle); + + +ngx_str_t ngx_http_xslt_default_types[] = { + ngx_string("text/xml"), + ngx_null_string +}; + + +static ngx_command_t ngx_http_xslt_filter_commands[] = { + + { ngx_string("xml_entities"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_http_xslt_entities, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("xslt_stylesheet"), + NGX_HTTP_LOC_CONF|NGX_CONF_1MORE, + ngx_http_xslt_stylesheet, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("xslt_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_xslt_filter_loc_conf_t, types_keys), + &ngx_http_xslt_default_types[0] }, + + ngx_null_command +}; + + +static ngx_http_module_t ngx_http_xslt_filter_module_ctx = { + NULL, /* preconfiguration */ + ngx_http_xslt_filter_init, /* postconfiguration */ + + ngx_http_xslt_filter_create_main_conf, /* create main configuration */ + NULL, /* init main configuration */ + + NULL, /* create server configuration */ + NULL, /* merge server configuration */ + + ngx_http_xslt_filter_create_conf, /* create location configuration */ + ngx_http_xslt_filter_merge_conf /* merge location configuration */ +}; + + +ngx_module_t ngx_http_xslt_filter_module = { + NGX_MODULE_V1, + &ngx_http_xslt_filter_module_ctx, /* module context */ + ngx_http_xslt_filter_commands, /* module directives */ + NGX_HTTP_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + ngx_http_xslt_filter_exit, /* exit process */ + ngx_http_xslt_filter_exit, /* 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_xslt_header_filter(ngx_http_request_t *r) +{ + ngx_http_xslt_filter_ctx_t *ctx; + ngx_http_xslt_filter_loc_conf_t *conf; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "xslt filter header"); + + if (r->headers_out.status == NGX_HTTP_NOT_MODIFIED) { + return ngx_http_next_header_filter(r); + } + + conf = ngx_http_get_module_loc_conf(r, ngx_http_xslt_filter_module); + + if (conf->sheets.nelts == 0 + || ngx_http_test_content_type(r, &conf->types) == NULL) + { + return ngx_http_next_header_filter(r); + } + + ctx = ngx_http_get_module_ctx(r, ngx_http_xslt_filter_module); + + if (ctx) { + return ngx_http_next_header_filter(r); + } + + ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_xslt_filter_ctx_t)); + if (ctx == NULL) { + return NGX_ERROR; + } + + ngx_http_set_ctx(r, ctx, ngx_http_xslt_filter_module); + + r->main_filter_need_in_memory = 1; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_xslt_body_filter(ngx_http_request_t *r, ngx_chain_t *in) +{ + int wellFormed; + ngx_chain_t *cl; + ngx_http_xslt_filter_ctx_t *ctx; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "xslt filter body"); + + if (in == NULL) { + return ngx_http_next_body_filter(r, in); + } + + ctx = ngx_http_get_module_ctx(r, ngx_http_xslt_filter_module); + + if (ctx == NULL || ctx->done) { + return ngx_http_next_body_filter(r, in); + } + + for (cl = in; cl; cl = cl->next) { + + if (ngx_http_xslt_add_chunk(r, ctx, cl->buf) != NGX_OK) { + + if (ctx->ctxt->myDoc) { + +#if (NGX_HTTP_XSLT_REUSE_DTD) + ctx->ctxt->myDoc->extSubset = NULL; +#endif + xmlFreeDoc(ctx->ctxt->myDoc); + } + + xmlFreeParserCtxt(ctx->ctxt); + + return ngx_http_xslt_send(r, ctx, NULL); + } + + if (cl->buf->last_buf || cl->buf->last_in_chain) { + + ctx->doc = ctx->ctxt->myDoc; + +#if (NGX_HTTP_XSLT_REUSE_DTD) + ctx->doc->extSubset = NULL; +#endif + + wellFormed = ctx->ctxt->wellFormed; + + xmlFreeParserCtxt(ctx->ctxt); + + if (wellFormed) { + return ngx_http_xslt_send(r, ctx, + ngx_http_xslt_apply_stylesheet(r, ctx)); + } + + xmlFreeDoc(ctx->doc); + + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "not well formed XML document"); + + return ngx_http_xslt_send(r, ctx, NULL); + } + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_xslt_send(ngx_http_request_t *r, ngx_http_xslt_filter_ctx_t *ctx, + ngx_buf_t *b) +{ + ngx_int_t rc; + ngx_chain_t out; + ngx_pool_cleanup_t *cln; + + ctx->done = 1; + + if (b == NULL) { + return ngx_http_filter_finalize_request(r, NULL, + NGX_HTTP_INTERNAL_SERVER_ERROR); + } + + cln = ngx_pool_cleanup_add(r->pool, 0); + + if (cln == NULL) { + ngx_free(b->pos); + return ngx_http_filter_finalize_request(r, NULL, + NGX_HTTP_INTERNAL_SERVER_ERROR); + } + + if (r == r->main) { + 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; + } + + ngx_http_clear_last_modified(r); + } + + rc = ngx_http_next_header_filter(r); + + if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) { + ngx_free(b->pos); + return rc; + } + + cln->handler = ngx_http_xslt_cleanup; + cln->data = b->pos; + + out.buf = b; + out.next = NULL; + + return ngx_http_next_body_filter(r, &out); +} + + +static ngx_int_t +ngx_http_xslt_add_chunk(ngx_http_request_t *r, ngx_http_xslt_filter_ctx_t *ctx, + ngx_buf_t *b) +{ + int err; + xmlParserCtxtPtr ctxt; + + if (ctx->ctxt == NULL) { + + ctxt = xmlCreatePushParserCtxt(NULL, NULL, NULL, 0, NULL); + if (ctxt == NULL) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "xmlCreatePushParserCtxt() failed"); + return NGX_ERROR; + } + + ctxt->sax->externalSubset = ngx_http_xslt_sax_external_subset; + ctxt->sax->setDocumentLocator = NULL; + ctxt->sax->warning = NULL; + ctxt->sax->error = ngx_http_xslt_sax_error; + ctxt->sax->fatalError = ngx_http_xslt_sax_error; + ctxt->sax->_private = ctx; + ctxt->replaceEntities = 1; + ctxt->loadsubset = 1; + + ctx->ctxt = ctxt; + ctx->request = r; + } + + err = xmlParseChunk(ctx->ctxt, (char *) b->pos, (int) (b->last - b->pos), + (b->last_buf) || (b->last_in_chain)); + + if (err == 0) { + b->pos = b->last; + return NGX_OK; + } + + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "xmlParseChunk() failed, error:%d", err); + + return NGX_ERROR; +} + + +static void +ngx_http_xslt_sax_external_subset(void *data, const xmlChar *name, + const xmlChar *externalId, const xmlChar *systemId) +{ + xmlParserCtxtPtr ctxt = data; + + xmlDocPtr doc; + xmlDtdPtr dtd; + ngx_http_request_t *r; + ngx_http_xslt_filter_ctx_t *ctx; + ngx_http_xslt_filter_loc_conf_t *conf; + + ctx = ctxt->sax->_private; + r = ctx->request; + + conf = ngx_http_get_module_loc_conf(r, ngx_http_xslt_filter_module); + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "xslt filter extSubset: \"%s\" \"%s\" \"%s\"", + name ? name : (xmlChar *) "", + externalId ? externalId : (xmlChar *) "", + systemId ? systemId : (xmlChar *) ""); + + doc = ctxt->myDoc; + +#if (NGX_HTTP_XSLT_REUSE_DTD) + + dtd = conf->dtd; + +#else + + dtd = xmlCopyDtd(conf->dtd); + if (dtd == NULL) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "xmlCopyDtd() failed"); + return; + } + + if (doc->children == NULL) { + xmlAddChild((xmlNodePtr) doc, (xmlNodePtr) dtd); + + } else { + xmlAddPrevSibling(doc->children, (xmlNodePtr) dtd); + } + +#endif + + doc->extSubset = dtd; +} + + +static void ngx_cdecl +ngx_http_xslt_sax_error(void *data, const char *msg, ...) +{ + xmlParserCtxtPtr ctxt = data; + + size_t n; + va_list args; + ngx_http_xslt_filter_ctx_t *ctx; + u_char buf[NGX_MAX_ERROR_STR]; + + ctx = ctxt->sax->_private; + + buf[0] = '\0'; + + va_start(args, msg); + n = (size_t) vsnprintf((char *) buf, NGX_MAX_ERROR_STR, msg, args); + va_end(args); + + while (--n && (buf[n] == CR || buf[n] == LF)) { /* void */ } + + ngx_log_error(NGX_LOG_ERR, ctx->request->connection->log, 0, + "libxml2 error: \"%*s\"", n + 1, buf); +} + + +static ngx_buf_t * +ngx_http_xslt_apply_stylesheet(ngx_http_request_t *r, + ngx_http_xslt_filter_ctx_t *ctx) +{ + int len, rc, doc_type; + u_char *type, *encoding; + ngx_buf_t *b; + ngx_uint_t i; + xmlChar *buf; + xmlDocPtr doc, res; + ngx_http_xslt_sheet_t *sheet; + ngx_http_xslt_filter_loc_conf_t *conf; + + conf = ngx_http_get_module_loc_conf(r, ngx_http_xslt_filter_module); + sheet = conf->sheets.elts; + doc = ctx->doc; + + /* preallocate array for 4 params */ + + if (ngx_array_init(&ctx->params, r->pool, 4 * 2 + 1, sizeof(char *)) + != NGX_OK) + { + xmlFreeDoc(doc); + return NULL; + } + + for (i = 0; i < conf->sheets.nelts; i++) { + + if (ngx_http_xslt_params(r, ctx, &sheet[i].params) != NGX_OK) { + xmlFreeDoc(doc); + return NULL; + } + + res = xsltApplyStylesheet(sheet[i].stylesheet, doc, ctx->params.elts); + + xmlFreeDoc(doc); + + if (res == NULL) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "xsltApplyStylesheet() failed"); + return NULL; + } + + doc = res; + + /* reset array elements */ + ctx->params.nelts = 0; + } + + /* there must be at least one stylesheet */ + + if (r == r->main) { + type = ngx_http_xslt_content_type(sheet[i - 1].stylesheet); + + } else { + type = NULL; + } + + encoding = ngx_http_xslt_encoding(sheet[i - 1].stylesheet); + doc_type = doc->type; + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "xslt filter type: %d t:%s e:%s", + doc_type, type ? type : (u_char *) "(null)", + encoding ? encoding : (u_char *) "(null)"); + + rc = xsltSaveResultToString(&buf, &len, doc, sheet[i - 1].stylesheet); + + xmlFreeDoc(doc); + + if (rc != 0) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "xsltSaveResultToString() failed"); + return NULL; + } + + if (len == 0) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "xsltSaveResultToString() returned zero-length result"); + return NULL; + } + + b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t)); + if (b == NULL) { + ngx_free(buf); + return NULL; + } + + b->pos = buf; + b->last = buf + len; + b->memory = 1; + + if (encoding) { + r->headers_out.charset.len = ngx_strlen(encoding); + r->headers_out.charset.data = encoding; + } + + if (r != r->main) { + return b; + } + + b->last_buf = 1; + + if (type) { + len = ngx_strlen(type); + + r->headers_out.content_type_len = len; + r->headers_out.content_type.len = len; + r->headers_out.content_type.data = type; + + } else if (doc_type == XML_HTML_DOCUMENT_NODE) { + + 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; + + return b; +} + + +static ngx_int_t +ngx_http_xslt_params(ngx_http_request_t *r, ngx_http_xslt_filter_ctx_t *ctx, + ngx_array_t *params) +{ + u_char *p, *last, *value, *dst, *src, **s; + size_t len; + ngx_uint_t i; + ngx_str_t string; + ngx_http_complex_value_t *param; + + param = params->elts; + + for (i = 0; i < params->nelts; i++) { + + if (ngx_http_complex_value(r, ¶m[i], &string) != NGX_OK) { + return NGX_ERROR; + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "xslt filter param: \"%s\"", string.data); + + p = string.data; + last = string.data + string.len - 1; + + while (p && *p) { + + value = p; + p = (u_char *) ngx_strchr(p, '='); + if (p == NULL) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "invalid libxslt parameter \"%s\"", value); + return NGX_ERROR; + } + *p++ = '\0'; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "xslt filter param name: \"%s\"", value); + + s = ngx_array_push(&ctx->params); + if (s == NULL) { + return NGX_ERROR; + } + + *s = value; + + value = p; + p = (u_char *) ngx_strchr(p, ':'); + + if (p) { + len = p - value; + *p++ = '\0'; + + } else { + len = last - value; + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "xslt filter param value: \"%s\"", value); + + dst = value; + src = value; + + ngx_unescape_uri(&dst, &src, len, 0); + + *dst = '\0'; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "xslt filter param unescaped: \"%s\"", value); + + s = ngx_array_push(&ctx->params); + if (s == NULL) { + return NGX_ERROR; + } + + *s = value; + } + } + + s = ngx_array_push(&ctx->params); + if (s == NULL) { + return NGX_ERROR; + } + + *s = NULL; + + return NGX_OK; +} + + +static u_char * +ngx_http_xslt_content_type(xsltStylesheetPtr s) +{ + u_char *type; + + if (s->mediaType) { + return s->mediaType; + } + + for (s = s->imports; s; s = s->next) { + + type = ngx_http_xslt_content_type(s); + + if (type) { + return type; + } + } + + return NULL; +} + + +static u_char * +ngx_http_xslt_encoding(xsltStylesheetPtr s) +{ + u_char *encoding; + + if (s->encoding) { + return s->encoding; + } + + for (s = s->imports; s; s = s->next) { + + encoding = ngx_http_xslt_encoding(s); + + if (encoding) { + return encoding; + } + } + + return NULL; +} + + +static void +ngx_http_xslt_cleanup(void *data) +{ + ngx_free(data); +} + + +static char * +ngx_http_xslt_entities(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_http_xslt_filter_loc_conf_t *xlcf = conf; + + ngx_str_t *value; + ngx_uint_t i; + ngx_pool_cleanup_t *cln; + ngx_http_xslt_file_t *file; + ngx_http_xslt_filter_main_conf_t *xmcf; + + if (xlcf->dtd) { + return "is duplicate"; + } + + value = cf->args->elts; + + xmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_xslt_filter_module); + + file = xmcf->dtd_files.elts; + for (i = 0; i < xmcf->dtd_files.nelts; i++) { + if (ngx_strcmp(file[i].name, &value[1].data) == 0) { + xlcf->dtd = file[i].data; + return NGX_CONF_OK; + } + } + + cln = ngx_pool_cleanup_add(cf->pool, 0); + if (cln == NULL) { + return NGX_CONF_ERROR; + } + + xlcf->dtd = xmlParseDTD(NULL, (xmlChar *) value[1].data); + + if (xlcf->dtd == NULL) { + ngx_conf_log_error(NGX_LOG_ERR, cf, 0, "xmlParseDTD() failed"); + return NGX_CONF_ERROR; + } + + cln->handler = ngx_http_xslt_cleanup_dtd; + cln->data = xlcf->dtd; + + file = ngx_array_push(&xmcf->dtd_files); + if (file == NULL) { + return NGX_CONF_ERROR; + } + + file->name = value[1].data; + file->data = xlcf->dtd; + + return NGX_CONF_OK; +} + + + +static char * +ngx_http_xslt_stylesheet(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_http_xslt_filter_loc_conf_t *xlcf = conf; + + ngx_str_t *value; + ngx_uint_t i, n; + ngx_pool_cleanup_t *cln; + ngx_http_xslt_file_t *file; + ngx_http_xslt_sheet_t *sheet; + ngx_http_complex_value_t *param; + ngx_http_compile_complex_value_t ccv; + ngx_http_xslt_filter_main_conf_t *xmcf; + + value = cf->args->elts; + + if (xlcf->sheets.elts == NULL) { + if (ngx_array_init(&xlcf->sheets, cf->pool, 1, + sizeof(ngx_http_xslt_sheet_t)) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + } + + sheet = ngx_array_push(&xlcf->sheets); + if (sheet == NULL) { + return NGX_CONF_ERROR; + } + + ngx_memzero(sheet, sizeof(ngx_http_xslt_sheet_t)); + + if (ngx_conf_full_name(cf->cycle, &value[1], 0) != NGX_OK) { + return NGX_CONF_ERROR; + } + + xmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_xslt_filter_module); + + file = xmcf->sheet_files.elts; + for (i = 0; i < xmcf->sheet_files.nelts; i++) { + if (ngx_strcmp(file[i].name, &value[1].data) == 0) { + sheet->stylesheet = file[i].data; + goto found; + } + } + + cln = ngx_pool_cleanup_add(cf->pool, 0); + if (cln == NULL) { + return NGX_CONF_ERROR; + } + + sheet->stylesheet = xsltParseStylesheetFile(value[1].data); + if (sheet->stylesheet == NULL) { + ngx_conf_log_error(NGX_LOG_ERR, cf, 0, + "xsltParseStylesheetFile(\"%s\") failed", + value[1].data); + return NGX_CONF_ERROR; + } + + cln->handler = ngx_http_xslt_cleanup_stylesheet; + cln->data = sheet->stylesheet; + + file = ngx_array_push(&xmcf->sheet_files); + if (file == NULL) { + return NGX_CONF_ERROR; + } + + file->name = value[1].data; + file->data = sheet->stylesheet; + +found: + + n = cf->args->nelts; + + if (n == 2) { + return NGX_CONF_OK; + } + + if (ngx_array_init(&sheet->params, cf->pool, n - 2, + sizeof(ngx_http_complex_value_t)) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + + for (i = 2; i < n; i++) { + + param = ngx_array_push(&sheet->params); + if (param == 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 = param; + ccv.zero = 1; + + if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { + return NGX_CONF_ERROR; + } + } + + return NGX_CONF_OK; +} + + +static void +ngx_http_xslt_cleanup_dtd(void *data) +{ + xmlFreeDtd(data); +} + + +static void +ngx_http_xslt_cleanup_stylesheet(void *data) +{ + xsltFreeStylesheet(data); +} + + +static void * +ngx_http_xslt_filter_create_main_conf(ngx_conf_t *cf) +{ + ngx_http_xslt_filter_main_conf_t *conf; + + conf = ngx_palloc(cf->pool, sizeof(ngx_http_xslt_filter_main_conf_t)); + if (conf == NULL) { + return NULL; + } + + if (ngx_array_init(&conf->dtd_files, cf->pool, 1, + sizeof(ngx_http_xslt_file_t)) + != NGX_OK) + { + return NULL; + } + + if (ngx_array_init(&conf->sheet_files, cf->pool, 1, + sizeof(ngx_http_xslt_file_t)) + != NGX_OK) + { + return NULL; + } + + return conf; +} + + +static void * +ngx_http_xslt_filter_create_conf(ngx_conf_t *cf) +{ + ngx_http_xslt_filter_loc_conf_t *conf; + + conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_xslt_filter_loc_conf_t)); + if (conf == NULL) { + return NULL; + } + + /* + * set by ngx_pcalloc(): + * + * conf->dtd = NULL; + * conf->sheets = { NULL }; + * conf->types = { NULL }; + * conf->types_keys = NULL; + */ + + return conf; +} + + +static char * +ngx_http_xslt_filter_merge_conf(ngx_conf_t *cf, void *parent, void *child) +{ + ngx_http_xslt_filter_loc_conf_t *prev = parent; + ngx_http_xslt_filter_loc_conf_t *conf = child; + + if (conf->dtd == NULL) { + conf->dtd = prev->dtd; + } + + if (conf->sheets.nelts == 0) { + conf->sheets = prev->sheets; + } + + if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types, + &prev->types_keys, &prev->types, + ngx_http_xslt_default_types) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; +} + + +static ngx_int_t +ngx_http_xslt_filter_init(ngx_conf_t *cf) +{ + xmlInitParser(); + +#if (NGX_HAVE_EXSLT) + exsltRegisterAll(); +#endif + + ngx_http_next_header_filter = ngx_http_top_header_filter; + ngx_http_top_header_filter = ngx_http_xslt_header_filter; + + ngx_http_next_body_filter = ngx_http_top_body_filter; + ngx_http_top_body_filter = ngx_http_xslt_body_filter; + + return NGX_OK; +} + + +static void +ngx_http_xslt_filter_exit(ngx_cycle_t *cycle) +{ + xsltCleanupGlobals(); + xmlCleanupParser(); +} diff -Nru nginx-0.5.33/src/http/modules/perl/nginx.pm nginx-0.8.53/src/http/modules/perl/nginx.pm --- nginx-0.5.33/src/http/modules/perl/nginx.pm 2007-09-24 04:19:28.000000000 +0000 +++ nginx-0.8.53/src/http/modules/perl/nginx.pm 2010-10-04 13:50:09.000000000 +0000 @@ -14,6 +14,7 @@ HTTP_OK HTTP_CREATED + HTTP_ACCEPTED HTTP_NO_CONTENT HTTP_PARTIAL_CONTENT @@ -47,7 +48,7 @@ HTTP_INSUFFICIENT_STORAGE ); -our $VERSION = '0.5.33'; +our $VERSION = '0.8.53'; require XSLoader; XSLoader::load('nginx', $VERSION); @@ -59,6 +60,7 @@ use constant HTTP_OK => 200; use constant HTTP_CREATED => 201; +use constant HTTP_ACCEPTED => 202; use constant HTTP_NO_CONTENT => 204; use constant HTTP_PARTIAL_CONTENT => 206; diff -Nru nginx-0.5.33/src/http/modules/perl/nginx.xs nginx-0.8.53/src/http/modules/perl/nginx.xs --- nginx-0.5.33/src/http/modules/perl/nginx.xs 2007-09-23 19:26:53.000000000 +0000 +++ nginx-0.8.53/src/http/modules/perl/nginx.xs 2010-04-22 14:02:45.000000000 +0000 @@ -13,19 +13,16 @@ #include "XSUB.h" + #define ngx_http_perl_set_request(r) \ r = INT2PTR(ngx_http_request_t *, SvIV((SV *) SvRV(ST(0)))) -#define ngx_http_perl_set_targ(p, len, z) \ +#define ngx_http_perl_set_targ(p, len) \ \ - sv_upgrade(TARG, SVt_PV); \ + SvUPGRADE(TARG, SVt_PV); \ SvPOK_on(TARG); \ - SvPV_set(TARG, (char *) p); \ - SvLEN_set(TARG, len + z); \ - SvCUR_set(TARG, len); \ - SvFAKE_on(TARG); \ - SvREADONLY_on(TARG); \ + sv_setpvn(TARG, (char *) p, len) static ngx_int_t @@ -42,18 +39,25 @@ s->len = len; - if (SvREADONLY(sv)) { + if (SvREADONLY(sv) && SvPOK(sv)) { s->data = p; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "perl sv2str: %08XD \"%V\"", sv->sv_flags, s); + return NGX_OK; } - s->data = ngx_palloc(r->pool, len); + s->data = ngx_pnalloc(r->pool, len); if (s->data == NULL) { return NGX_ERROR; } ngx_memcpy(s->data, p, len); + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "perl sv2str: %08XD \"%V\"", sv->sv_flags, s); + return NGX_OK; } @@ -165,7 +169,7 @@ ngx_http_request_t *r; ngx_http_perl_set_request(r); - ngx_http_perl_set_targ(r->uri.data, r->uri.len, 0); + ngx_http_perl_set_targ(r->uri.data, r->uri.len); ST(0) = TARG; @@ -178,7 +182,7 @@ ngx_http_request_t *r; ngx_http_perl_set_request(r); - ngx_http_perl_set_targ(r->args.data, r->args.len, 0); + ngx_http_perl_set_targ(r->args.data, r->args.len); ST(0) = TARG; @@ -191,7 +195,7 @@ ngx_http_request_t *r; ngx_http_perl_set_request(r); - ngx_http_perl_set_targ(r->method_name.data, r->method_name.len, 0); + ngx_http_perl_set_targ(r->method_name.data, r->method_name.len); ST(0) = TARG; @@ -205,7 +209,7 @@ ngx_http_perl_set_request(r); ngx_http_perl_set_targ(r->connection->addr_text.data, - r->connection->addr_text.len, 1); + r->connection->addr_text.len); ST(0) = TARG; @@ -238,16 +242,12 @@ /* look up hashed headers */ - lowcase_key = ngx_palloc(r->pool, len); + lowcase_key = ngx_pnalloc(r->pool, len); if (lowcase_key == NULL) { XSRETURN_UNDEF; } - hash = 0; - for (i = 0; i < len; i++) { - lowcase_key[i] = ngx_tolower(p[i]); - hash = ngx_hash(hash, lowcase_key[i]); - } + hash = ngx_hash_strlow(lowcase_key, p, len); cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); @@ -259,7 +259,7 @@ ph = (ngx_table_elt_t **) ((char *) &r->headers_in + hh->offset); if (*ph) { - ngx_http_perl_set_targ((*ph)->value.data, (*ph)->value.len, 0); + ngx_http_perl_set_targ((*ph)->value.data, (*ph)->value.len); goto done; } @@ -278,7 +278,7 @@ ph = r->headers_in.cookies.elts; if (n == 1) { - ngx_http_perl_set_targ((*ph)->value.data, (*ph)->value.len, 0); + ngx_http_perl_set_targ((*ph)->value.data, (*ph)->value.len); goto done; } @@ -289,7 +289,7 @@ size += ph[i]->value.len + sizeof("; ") - 1; } - cookie = ngx_palloc(r->pool, size); + cookie = ngx_pnalloc(r->pool, size); if (cookie == NULL) { XSRETURN_UNDEF; } @@ -306,7 +306,7 @@ *p++ = ';'; *p++ = ' '; } - ngx_http_perl_set_targ(cookie, size, 0); + ngx_http_perl_set_targ(cookie, size); goto done; } @@ -334,7 +334,7 @@ continue; } - ngx_http_perl_set_targ(h[i].value.data, h[i].value.len, 0); + ngx_http_perl_set_targ(h[i].value.data, h[i].value.len); goto done; } @@ -402,7 +402,7 @@ XSRETURN_UNDEF; } - ngx_http_perl_set_targ(r->request_body->bufs->buf->pos, len, 0); + ngx_http_perl_set_targ(r->request_body->bufs->buf->pos, len); ST(0) = TARG; @@ -421,12 +421,23 @@ } ngx_http_perl_set_targ(r->request_body->temp_file->file.name.data, - r->request_body->temp_file->file.name.len, 1); + r->request_body->temp_file->file.name.len); ST(0) = TARG; void +discard_request_body(r) + CODE: + + ngx_http_request_t *r; + + ngx_http_perl_set_request(r); + + ngx_http_discard_request_body(r); + + +void header_out(r, key, value) CODE: @@ -463,8 +474,6 @@ r->headers_out.content_length = header; } - XSRETURN_EMPTY; - void filename(r) @@ -491,7 +500,7 @@ done: - ngx_http_perl_set_targ(ctx->filename.data, ctx->filename.len, 1); + ngx_http_perl_set_targ(ctx->filename.data, ctx->filename.len); ST(0) = TARG; @@ -523,7 +532,7 @@ sv = SvRV(sv); } - if (SvREADONLY(sv)) { + if (SvREADONLY(sv) && SvPOK(sv)) { p = (u_char *) SvPV(sv, len); @@ -591,22 +600,19 @@ (void) ngx_http_perl_output(r, b); - XSRETURN_EMPTY; - void sendfile(r, filename, offset = -1, bytes = 0) CODE: - ngx_http_request_t *r; - char *filename; - int offset; - size_t bytes; - ngx_fd_t fd; - ngx_buf_t *b; - ngx_file_info_t fi; - ngx_pool_cleanup_t *cln; - ngx_pool_cleanup_file_t *clnf; + ngx_http_request_t *r; + char *filename; + off_t offset; + size_t bytes; + ngx_str_t path; + ngx_buf_t *b; + ngx_open_file_info_t of; + ngx_http_core_loc_conf_t *clcf; ngx_http_perl_set_request(r); @@ -629,16 +635,35 @@ XSRETURN_EMPTY; } - cln = ngx_pool_cleanup_add(r->pool, sizeof(ngx_pool_cleanup_file_t)); - if (cln == NULL) { + path.len = ngx_strlen(filename); + + path.data = ngx_pnalloc(r->pool, path.len + 1); + if (path.data == NULL) { XSRETURN_EMPTY; } - fd = ngx_open_file((u_char *) filename, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0); + (void) ngx_cpystrn(path.data, filename, path.len + 1); + + 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_open_cached_file(clcf->open_file_cache, &path, &of, r->pool) + != NGX_OK) + { + if (of.err == 0) { + XSRETURN_EMPTY; + } - if (fd == NGX_INVALID_FILE) { ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno, - ngx_open_file_n " \"%s\" failed", filename); + "%s \"%s\" failed", of.failed, filename); XSRETURN_EMPTY; } @@ -647,40 +672,20 @@ } if (bytes == 0) { - if (ngx_fd_info(fd, &fi) == NGX_FILE_ERROR) { - ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno, - ngx_fd_info_n " \"%s\" failed", filename); - - if (ngx_close_file(fd) == NGX_FILE_ERROR) { - ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno, - ngx_close_file_n " \"%s\" failed", filename); - } - - XSRETURN_EMPTY; - } - - bytes = ngx_file_size(&fi) - offset; + bytes = of.size - offset; } - cln->handler = ngx_pool_cleanup_file; - clnf = cln->data; - - clnf->fd = fd; - clnf->name = (u_char *) ""; - clnf->log = r->pool->log; - b->in_file = 1; b->file_pos = offset; b->file_last = offset + bytes; - b->file->fd = fd; + b->file->fd = of.fd; b->file->log = r->connection->log; + b->file->directio = of.is_directio; (void) ngx_http_perl_output(r, b); - XSRETURN_EMPTY; - void flush(r) @@ -746,8 +751,6 @@ r->allow_ranges = 1; - XSRETURN_EMPTY; - void unescape(r, text, type = 0) @@ -766,7 +769,7 @@ src = (u_char *) SvPV(text, len); - p = ngx_palloc(r->pool, len + 1); + p = ngx_pnalloc(r->pool, len + 1); if (p == NULL) { XSRETURN_UNDEF; } @@ -778,7 +781,7 @@ ngx_unescape_uri(&dst, &src, len, (ngx_uint_t) type); *dst = '\0'; - ngx_http_perl_set_targ(p, dst - p, 1); + ngx_http_perl_set_targ(p, dst - p); ST(0) = TARG; @@ -823,16 +826,12 @@ p = (u_char *) SvPV(name, len); - lowcase = ngx_palloc(r->pool, len); + lowcase = ngx_pnalloc(r->pool, len); if (lowcase == NULL) { XSRETURN_UNDEF; } - hash = 0; - for (i = 0; i < len; i++) { - lowcase[i] = ngx_tolower(p[i]); - hash = ngx_hash(hash, lowcase[i]); - } + hash = ngx_hash_strlow(lowcase, p, len); var.len = len; var.data = lowcase; @@ -849,7 +848,7 @@ #endif - vv = ngx_http_get_variable(r, &var, hash, 1); + vv = ngx_http_get_variable(r, &var, hash); if (vv == NULL) { XSRETURN_UNDEF; } @@ -875,7 +874,7 @@ XSRETURN_UNDEF; } - ngx_http_perl_set_targ(v[i].value.data, v[i].value.len, 0); + ngx_http_perl_set_targ(v[i].value.data, v[i].value.len); goto done; } @@ -903,23 +902,20 @@ XSRETURN_UNDEF; } - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "variable \"%V\" not found", &var); - XSRETURN_UNDEF; } if (value) { vv->len = val.len; vv->valid = 1; - vv->no_cachable = 0; + vv->no_cacheable = 0; vv->not_found = 0; vv->data = val.data; XSRETURN_UNDEF; } - ngx_http_perl_set_targ(vv->data, vv->len, 0); + ngx_http_perl_set_targ(vv->data, vv->len); done: @@ -930,21 +926,25 @@ sleep(r, sleep, next) CODE: - dXSTARG; ngx_http_request_t *r; + ngx_msec_t sleep; ngx_http_perl_ctx_t *ctx; ngx_http_perl_set_request(r); + sleep = (ngx_msec_t) SvIV(ST(1)); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "perl sleep: %M", sleep); + ctx = ngx_http_get_module_ctx(r, ngx_http_perl_module); - ctx->sleep = SvIV(ST(1)); ctx->next = SvRV(ST(2)); - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "perl sleep: %d", ctx->sleep); + ngx_add_timer(r->connection->write, sleep); - XSRETURN_EMPTY; + r->write_event_handler = ngx_http_perl_sleep_handler; + r->main->count++; void @@ -976,5 +976,3 @@ p = (u_char *) SvPV(msg, len); ngx_log_error(NGX_LOG_ERR, r->connection->log, e, "perl: %s", p); - - XSRETURN_EMPTY; diff -Nru nginx-0.5.33/src/http/modules/perl/ngx_http_perl_module.c nginx-0.8.53/src/http/modules/perl/ngx_http_perl_module.c --- nginx-0.5.33/src/http/modules/perl/ngx_http_perl_module.c 2007-07-29 17:31:00.000000000 +0000 +++ nginx-0.8.53/src/http/modules/perl/ngx_http_perl_module.c 2010-05-24 12:35:10.000000000 +0000 @@ -13,8 +13,8 @@ typedef struct { PerlInterpreter *perl; HV *nginx; - ngx_str_t modules; - ngx_array_t requires; + ngx_array_t *modules; + ngx_array_t *requires; } ngx_http_perl_main_conf_t; @@ -30,18 +30,11 @@ } ngx_http_perl_variable_t; -typedef struct { - SV *sv; - PerlInterpreter *perl; -} ngx_http_perl_cleanup_t; - - #if (NGX_HTTP_SSI) static ngx_int_t ngx_http_perl_ssi(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ssi_ctx, ngx_str_t **params); #endif -static void ngx_http_perl_sleep_handler(ngx_http_request_t *r); static char *ngx_http_perl_init_interpreter(ngx_conf_t *cf, ngx_http_perl_main_conf_t *pmcf); static PerlInterpreter *ngx_http_perl_create_interpreter(ngx_conf_t *cf, @@ -49,7 +42,7 @@ static ngx_int_t ngx_http_perl_run_requires(pTHX_ ngx_array_t *requires, ngx_log_t *log); static ngx_int_t ngx_http_perl_call_handler(pTHX_ ngx_http_request_t *r, - HV *nginx, SV *sub, ngx_str_t **args, ngx_str_t *handler, ngx_str_t *rv); + HV *nginx, SV *sub, SV **args, ngx_str_t *handler, ngx_str_t *rv); static void ngx_http_perl_eval_anon_sub(pTHX_ ngx_str_t *handler, SV **sv); static ngx_int_t ngx_http_perl_preconfiguration(ngx_conf_t *cf); @@ -58,8 +51,6 @@ static void *ngx_http_perl_create_loc_conf(ngx_conf_t *cf); static char *ngx_http_perl_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child); -static char *ngx_http_perl_require(ngx_conf_t *cf, ngx_command_t *cmd, - void *conf); static char *ngx_http_perl(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_http_perl_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); @@ -75,16 +66,16 @@ { ngx_string("perl_modules"), NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1, - ngx_conf_set_str_slot, + ngx_conf_set_str_array_slot, NGX_HTTP_MAIN_CONF_OFFSET, offsetof(ngx_http_perl_main_conf_t, modules), NULL }, { ngx_string("perl_require"), NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1, - ngx_http_perl_require, + ngx_conf_set_str_array_slot, NGX_HTTP_MAIN_CONF_OFFSET, - 0, + offsetof(ngx_http_perl_main_conf_t, requires), NULL }, { ngx_string("perl"), @@ -155,10 +146,15 @@ #endif -static ngx_str_t ngx_null_name = ngx_null_string; +static ngx_str_t ngx_null_name = ngx_null_string; +static HV *nginx_stash; +#if (NGX_HAVE_PERL_MULTIPLICITY) +static ngx_uint_t ngx_perl_term; +#else +static PerlInterpreter *perl; +#endif -static HV *nginx_stash; static void ngx_http_perl_xs_init(pTHX) @@ -172,10 +168,7 @@ static ngx_int_t ngx_http_perl_handler(ngx_http_request_t *r) { - /* TODO: Win32 */ - if (r->zero_in_uri) { - return NGX_HTTP_NOT_FOUND; - } + r->main->count++; ngx_http_perl_handle_request(r); @@ -230,13 +223,18 @@ } + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "perl handler done: %i", rc); + + if (rc == NGX_DONE) { + ngx_http_finalize_request(r, rc); + return; + } + if (rc > 600) { rc = NGX_OK; } - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "perl handler done: %i", rc); - if (ctx->redirect_uri.len) { uri = ctx->redirect_uri; args = ctx->redirect_args; @@ -248,18 +246,14 @@ ctx->filename.data = NULL; ctx->redirect_uri.len = 0; - if (ctx->sleep) { - ngx_add_timer(r->connection->write, (ngx_msec_t) ctx->sleep); - r->write_event_handler = ngx_http_perl_sleep_handler; - ctx->sleep = 0; - } - if (ctx->done || ctx->next) { + ngx_http_finalize_request(r, NGX_DONE); return; } if (uri.len) { ngx_http_internal_redirect(r, &uri, &args); + ngx_http_finalize_request(r, NGX_DONE); return; } @@ -272,7 +266,7 @@ } -static void +void ngx_http_perl_sleep_handler(ngx_http_request_t *r) { ngx_event_t *wev; @@ -288,7 +282,7 @@ return; } - if (ngx_handle_write_event(wev, 0) == NGX_ERROR) { + if (ngx_handle_write_event(wev, 0) != NGX_OK) { ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); } } @@ -336,7 +330,7 @@ if (value.data) { v->len = value.len; v->valid = 1; - v->no_cachable = 0; + v->no_cacheable = 0; v->not_found = 0; v->data = value.data; @@ -360,9 +354,10 @@ ngx_http_perl_ssi(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ssi_ctx, ngx_str_t **params) { - SV *sv; + SV *sv, **asv; ngx_int_t rc; - ngx_str_t *handler; + ngx_str_t *handler, **args; + ngx_uint_t i; ngx_http_perl_ctx_t *ctx; ngx_http_perl_main_conf_t *pmcf; @@ -412,9 +407,31 @@ sv = newSVpvn((char *) handler->data, handler->len); - rc = ngx_http_perl_call_handler(aTHX_ r, pmcf->nginx, sv, - ¶ms[NGX_HTTP_PERL_SSI_ARG], - handler, NULL); + args = ¶ms[NGX_HTTP_PERL_SSI_ARG]; + + if (args) { + + for (i = 0; args[i]; i++) { /* void */ } + + asv = ngx_pcalloc(r->pool, (i + 1) * sizeof(SV *)); + + if (asv == NULL) { + SvREFCNT_dec(sv); + return NGX_ERROR; + } + + asv[0] = (SV *) i; + + for (i = 0; args[i]; i++) { + asv[i + 1] = newSVpvn((char *) args[i]->data, args[i]->len); + } + + } else { + asv = NULL; + } + + rc = ngx_http_perl_call_handler(aTHX_ r, pmcf->nginx, sv, asv, handler, + NULL); SvREFCNT_dec(sv); @@ -435,27 +452,41 @@ static char * ngx_http_perl_init_interpreter(ngx_conf_t *cf, ngx_http_perl_main_conf_t *pmcf) { + ngx_str_t *m; + ngx_uint_t i; #if (NGX_HAVE_PERL_MULTIPLICITY) - ngx_pool_cleanup_t *cln; + ngx_pool_cleanup_t *cln; cln = ngx_pool_cleanup_add(cf->pool, 0); if (cln == NULL) { return NGX_CONF_ERROR; } -#else - static PerlInterpreter *perl; #endif #ifdef NGX_PERL_MODULES - if (pmcf->modules.data == NULL) { - pmcf->modules.data = NGX_PERL_MODULES; + if (pmcf->modules == NGX_CONF_UNSET_PTR) { + + pmcf->modules = ngx_array_create(cf->pool, 1, sizeof(ngx_str_t)); + if (pmcf->modules == NULL) { + return NGX_CONF_ERROR; + } + + m = ngx_array_push(pmcf->modules); + if (m == NULL) { + return NGX_CONF_ERROR; + } + + ngx_str_set(m, NGX_PERL_MODULES); } #endif - if (pmcf->modules.data) { - if (ngx_conf_full_name(cf->cycle, &pmcf->modules) != NGX_OK) { - return NGX_CONF_ERROR; + if (pmcf->modules != NGX_CONF_UNSET_PTR) { + m = pmcf->modules->elts; + for (i = 0; i < pmcf->modules->nelts; i++) { + if (ngx_conf_full_name(cf->cycle, &m[i], 0) != NGX_OK) { + return NGX_CONF_ERROR; + } } } @@ -467,7 +498,7 @@ return NGX_CONF_ERROR; } - if (ngx_http_perl_run_requires(aTHX_ &pmcf->requires, cf->log) + if (ngx_http_perl_run_requires(aTHX_ pmcf->requires, cf->log) != NGX_OK) { return NGX_CONF_ERROR; @@ -515,7 +546,9 @@ int n; STRLEN len; SV *sv; - char *ver, *embedding[6]; + char *ver, **embedding; + ngx_str_t *m; + ngx_uint_t i; PerlInterpreter *perl; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, cf->log, 0, "create perl interpreter"); @@ -541,15 +574,21 @@ PL_exit_flags |= PERL_EXIT_DESTRUCT_END; #endif - embedding[0] = ""; + n = (pmcf->modules != NGX_CONF_UNSET_PTR) ? pmcf->modules->nelts * 2 : 0; - if (pmcf->modules.data) { - embedding[1] = "-I"; - embedding[2] = (char *) pmcf->modules.data; - n = 3; + embedding = ngx_palloc(cf->pool, (4 + n) * sizeof(char *)); + if (embedding == NULL) { + goto fail; + } - } else { - n = 1; + embedding[0] = ""; + + if (n++) { + m = pmcf->modules->elts; + for (i = 0; i < pmcf->modules->nelts; i++) { + embedding[2 * i + 1] = "-I"; + embedding[2 * i + 2] = (char *) m[i].data; + } } embedding[n++] = "-Mnginx"; @@ -573,7 +612,7 @@ goto fail; } - if (ngx_http_perl_run_requires(aTHX_ &pmcf->requires, cf->log) != NGX_OK) { + if (ngx_http_perl_run_requires(aTHX_ pmcf->requires, cf->log) != NGX_OK) { goto fail; } @@ -594,26 +633,28 @@ static ngx_int_t ngx_http_perl_run_requires(pTHX_ ngx_array_t *requires, ngx_log_t *log) { - char **script; + u_char *err; STRLEN len; - ngx_str_t err; + ngx_str_t *script; ngx_uint_t i; + if (requires == NGX_CONF_UNSET_PTR) { + return NGX_OK; + } + script = requires->elts; for (i = 0; i < requires->nelts; i++) { - require_pv(script[i]); + require_pv((char *) script[i].data); if (SvTRUE(ERRSV)) { - err.data = (u_char *) SvPV(ERRSV, len); - for (len--; err.data[len] == LF || err.data[len] == CR; len--) { - /* void */ - } - err.len = len + 1; + err = (u_char *) SvPV(ERRSV, len); + while (--len && (err[len] == CR || err[len] == LF)) { /* void */ } ngx_log_error(NGX_LOG_EMERG, log, 0, - "require_pv(\"%s\") failed: \"%V\"", script[i], &err); + "require_pv(\"%s\") failed: \"%*s\"", + script[i].data, len + 1, err); return NGX_ERROR; } @@ -625,14 +666,15 @@ static ngx_int_t ngx_http_perl_call_handler(pTHX_ ngx_http_request_t *r, HV *nginx, SV *sub, - ngx_str_t **args, ngx_str_t *handler, ngx_str_t *rv) + SV **args, ngx_str_t *handler, ngx_str_t *rv) { - SV *sv; - int n, status; - char *line; - STRLEN len, n_a; - ngx_str_t err; - ngx_uint_t i; + SV *sv; + int n, status; + char *line; + u_char *err; + STRLEN len, n_a; + ngx_uint_t i; + ngx_connection_t *c; dSP; @@ -647,17 +689,17 @@ XPUSHs(sv); if (args) { - for (i = 0; args[i]; i++) { /* void */ } + EXTEND(sp, (intptr_t) args[0]); - EXTEND(sp, (int) i); - - for (i = 0; args[i]; i++) { - PUSHs(sv_2mortal(newSVpvn((char *) args[i]->data, args[i]->len))); + for (i = 1; i <= (ngx_uint_t) args[0]; i++) { + PUSHs(sv_2mortal(args[i])); } } PUTBACK; + c = r->connection; + n = call_sv(sub, G_EVAL); SPAGAIN; @@ -666,14 +708,14 @@ if (rv == NULL) { status = POPi; - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "call_sv: %d", status); } else { line = SvPVx(POPs, n_a); rv->len = n_a; - rv->data = ngx_palloc(r->pool, n_a); + rv->data = ngx_pnalloc(r->pool, n_a); if (rv->data == NULL) { return NGX_ERROR; } @@ -691,15 +733,11 @@ if (SvTRUE(ERRSV)) { - err.data = (u_char *) SvPV(ERRSV, len); - for (len--; err.data[len] == LF || err.data[len] == CR; len--) { - /* void */ - } - err.len = len + 1; + err = (u_char *) SvPV(ERRSV, len); + while (--len && (err[len] == CR || err[len] == LF)) { /* void */ } - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "call_sv(\"%V\") failed: \"%V\"", - handler, &err); + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "call_sv(\"%V\") failed: \"%*s\"", handler, len + 1, err); if (rv) { return NGX_ERROR; @@ -709,7 +747,7 @@ } if (n != 1) { - ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, + ngx_log_error(NGX_LOG_ALERT, c->log, 0, "call_sv(\"%V\") returned %d results", handler, n); status = NGX_OK; } @@ -733,7 +771,10 @@ } } - if (ngx_strncmp(p, "sub ", 4) == 0 || ngx_strncmp(p, "use ", 4) == 0) { + if (ngx_strncmp(p, "sub ", 4) == 0 + || ngx_strncmp(p, "sub{", 4) == 0 + || ngx_strncmp(p, "use ", 4) == 0) + { *sv = eval_pv((char *) p, FALSE); /* eval_pv() does not set ERRSV on failure */ @@ -752,15 +793,12 @@ pmcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_perl_main_conf_t)); if (pmcf == NULL) { - return NGX_CONF_ERROR; - } - - if (ngx_array_init(&pmcf->requires, cf->pool, 1, sizeof(u_char *)) - != NGX_OK) - { return NULL; } + pmcf->modules = NGX_CONF_UNSET_PTR; + pmcf->requires = NGX_CONF_UNSET_PTR; + return pmcf; } @@ -792,6 +830,12 @@ (void) perl_destruct(perl); perl_free(perl); + + if (ngx_perl_term) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, "perl term"); + + PERL_SYS_TERM(); + } } #endif @@ -831,7 +875,7 @@ plcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_perl_loc_conf_t)); if (plcf == NULL) { - return NGX_CONF_ERROR; + return NULL; } /* @@ -860,28 +904,6 @@ static char * -ngx_http_perl_require(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) -{ - ngx_http_perl_main_conf_t *pmcf = conf; - - u_char **p; - ngx_str_t *value; - - value = cf->args->elts; - - p = ngx_array_push(&pmcf->requires); - - if (p == NULL) { - return NGX_CONF_ERROR; - } - - *p = value[1].data; - - return NGX_CONF_OK; -} - - -static char * ngx_http_perl(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_http_perl_loc_conf_t *plcf = conf; @@ -954,7 +976,7 @@ value[1].len--; value[1].data++; - v = ngx_http_add_variable(cf, &value[1], NGX_HTTP_VAR_CHANGABLE); + v = ngx_http_add_variable(cf, &value[1], NGX_HTTP_VAR_CHANGEABLE); if (v == NULL) { return NGX_CONF_ERROR; } @@ -1012,22 +1034,42 @@ pmcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_perl_module); - { + if (pmcf) { + dTHXa(pmcf->perl); + PERL_SET_CONTEXT(pmcf->perl); - dTHXa(pmcf->perl); - PERL_SET_CONTEXT(pmcf->perl); - - /* set worker's $$ */ - - sv_setiv(GvSV(gv_fetchpv("$", TRUE, SVt_PV)), (I32) ngx_pid); + /* set worker's $$ */ + sv_setiv(GvSV(gv_fetchpv("$", TRUE, SVt_PV)), (I32) ngx_pid); } return NGX_OK; } + static void ngx_http_perl_exit(ngx_cycle_t *cycle) { - PERL_SYS_TERM(); +#if (NGX_HAVE_PERL_MULTIPLICITY) + + /* + * the master exit hook is run before global pool cleanup, + * therefore just set flag here + */ + + ngx_perl_term = 1; + +#else + + if (nginx_stash) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, cycle->log, 0, "perl term"); + + (void) perl_destruct(perl); + + perl_free(perl); + + PERL_SYS_TERM(); + } + +#endif } diff -Nru nginx-0.5.33/src/http/modules/perl/ngx_http_perl_module.h nginx-0.8.53/src/http/modules/perl/ngx_http_perl_module.h --- nginx-0.5.33/src/http/modules/perl/ngx_http_perl_module.h 2006-12-12 22:06:03.000000000 +0000 +++ nginx-0.8.53/src/http/modules/perl/ngx_http_perl_module.h 2008-02-16 14:23:14.000000000 +0000 @@ -25,9 +25,8 @@ ngx_str_t redirect_args; SV *next; - int sleep; - ngx_uint_t done; /* unsigned done:1; */ + ngx_uint_t done; /* unsigned done:1; */ ngx_array_t *variables; /* array of ngx_http_perl_var_t */ @@ -61,6 +60,7 @@ void ngx_http_perl_handle_request(ngx_http_request_t *r); +void ngx_http_perl_sleep_handler(ngx_http_request_t *r); #endif /* _NGX_HTTP_PERL_MODULE_H_INCLUDED_ */ diff -Nru nginx-0.5.33/src/http/ngx_http_busy_lock.c nginx-0.8.53/src/http/ngx_http_busy_lock.c --- nginx-0.5.33/src/http/ngx_http_busy_lock.c 2006-08-30 10:39:17.000000000 +0000 +++ nginx-0.8.53/src/http/ngx_http_busy_lock.c 2007-10-14 18:56:15.000000000 +0000 @@ -10,9 +10,9 @@ -static int ngx_http_busy_lock_look_cachable(ngx_http_busy_lock_t *bl, - ngx_http_busy_lock_ctx_t *bc, - int lock); +static int ngx_http_busy_lock_look_cacheable(ngx_http_busy_lock_t *bl, + ngx_http_busy_lock_ctx_t *bc, + int lock); int ngx_http_busy_lock(ngx_http_busy_lock_t *bl, ngx_http_busy_lock_ctx_t *bc) @@ -60,12 +60,12 @@ } -int ngx_http_busy_lock_cachable(ngx_http_busy_lock_t *bl, - ngx_http_busy_lock_ctx_t *bc, int lock) +int ngx_http_busy_lock_cacheable(ngx_http_busy_lock_t *bl, + ngx_http_busy_lock_ctx_t *bc, int lock) { int rc; - rc = ngx_http_busy_lock_look_cachable(bl, bc, lock); + rc = ngx_http_busy_lock_look_cacheable(bl, bc, lock); ngx_log_debug3(NGX_LOG_DEBUG_HTTP, bc->event->log, 0, "http busylock: %d w:%d mw::%d", @@ -121,22 +121,22 @@ if (bl->md5) { bl->md5_mask[bc->slot / 8] &= ~(1 << (bc->slot & 7)); - bl->cachable--; + bl->cacheable--; } bl->busy--; } -static int ngx_http_busy_lock_look_cachable(ngx_http_busy_lock_t *bl, - ngx_http_busy_lock_ctx_t *bc, - int lock) +static int ngx_http_busy_lock_look_cacheable(ngx_http_busy_lock_t *bl, + ngx_http_busy_lock_ctx_t *bc, + int lock) { - int i, b, cachable, free; + int i, b, cacheable, free; u_int mask; b = 0; - cachable = 0; + cacheable = 0; free = -1; #if (NGX_SUPPRESS_WARN) @@ -153,15 +153,15 @@ if (ngx_memcmp(&bl->md5[i * 16], bc->md5, 16) == 0) { return NGX_AGAIN; } - cachable++; + cacheable++; } else if (free == -1) { free = i; } #if 1 - if (cachable == bl->cachable) { - if (free == -1 && cachable < bl->max_busy) { + if (cacheable == bl->cacheable) { + if (free == -1 && cacheable < bl->max_busy) { free = i + 1; } @@ -186,7 +186,7 @@ bl->md5_mask[free / 8] |= 1 << (free & 7); bc->slot = free; - bl->cachable++; + bl->cacheable++; bl->busy++; } diff -Nru nginx-0.5.33/src/http/ngx_http_busy_lock.h nginx-0.8.53/src/http/ngx_http_busy_lock.h --- nginx-0.5.33/src/http/ngx_http_busy_lock.h 2006-08-30 10:39:17.000000000 +0000 +++ nginx-0.8.53/src/http/ngx_http_busy_lock.h 2007-10-14 18:56:15.000000000 +0000 @@ -17,7 +17,7 @@ typedef struct { u_char *md5_mask; char *md5; - int cachable; + int cacheable; int busy; int max_busy; @@ -41,8 +41,8 @@ int ngx_http_busy_lock(ngx_http_busy_lock_t *bl, ngx_http_busy_lock_ctx_t *bc); -int ngx_http_busy_lock_cachable(ngx_http_busy_lock_t *bl, - ngx_http_busy_lock_ctx_t *bc, int lock); +int ngx_http_busy_lock_cacheable(ngx_http_busy_lock_t *bl, + ngx_http_busy_lock_ctx_t *bc, int lock); void ngx_http_busy_unlock(ngx_http_busy_lock_t *bl, ngx_http_busy_lock_ctx_t *bc); diff -Nru nginx-0.5.33/src/http/ngx_http.c nginx-0.8.53/src/http/ngx_http.c --- nginx-0.5.33/src/http/ngx_http.c 2007-11-07 13:46:29.000000000 +0000 +++ nginx-0.8.53/src/http/ngx_http.c 2010-09-28 06:50:52.000000000 +0000 @@ -6,33 +6,78 @@ #include #include -#include #include static char *ngx_http_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static ngx_int_t ngx_http_init_phases(ngx_conf_t *cf, + ngx_http_core_main_conf_t *cmcf); +static ngx_int_t ngx_http_init_headers_in_hash(ngx_conf_t *cf, + ngx_http_core_main_conf_t *cmcf); +static ngx_int_t ngx_http_init_phase_handlers(ngx_conf_t *cf, + ngx_http_core_main_conf_t *cmcf); + +static ngx_int_t ngx_http_add_addresses(ngx_conf_t *cf, + ngx_http_core_srv_conf_t *cscf, ngx_http_conf_port_t *port, + ngx_http_listen_opt_t *lsopt); static ngx_int_t ngx_http_add_address(ngx_conf_t *cf, - ngx_http_conf_in_port_t *in_port, ngx_http_listen_t *lscf, - ngx_http_core_srv_conf_t *cscf); -static ngx_int_t ngx_http_add_names(ngx_conf_t *cf, - ngx_http_conf_in_addr_t *in_addr, ngx_http_core_srv_conf_t *cscf); + ngx_http_core_srv_conf_t *cscf, ngx_http_conf_port_t *port, + ngx_http_listen_opt_t *lsopt); +static ngx_int_t ngx_http_add_server(ngx_conf_t *cf, + ngx_http_core_srv_conf_t *cscf, ngx_http_conf_addr_t *addr); + +static char *ngx_http_merge_servers(ngx_conf_t *cf, + ngx_http_core_main_conf_t *cmcf, ngx_http_module_t *module, + ngx_uint_t ctx_index); static char *ngx_http_merge_locations(ngx_conf_t *cf, - ngx_array_t *locations, void **loc_conf, ngx_http_module_t *module, + ngx_queue_t *locations, void **loc_conf, ngx_http_module_t *module, ngx_uint_t ctx_index); -static int ngx_http_cmp_conf_in_addrs(const void *one, const void *two); +static ngx_int_t ngx_http_init_locations(ngx_conf_t *cf, + ngx_http_core_srv_conf_t *cscf, ngx_http_core_loc_conf_t *pclcf); +static ngx_int_t ngx_http_init_static_location_trees(ngx_conf_t *cf, + ngx_http_core_loc_conf_t *pclcf); +static ngx_int_t ngx_http_cmp_locations(const ngx_queue_t *one, + const ngx_queue_t *two); +static ngx_int_t ngx_http_join_exact_locations(ngx_conf_t *cf, + ngx_queue_t *locations); +static void ngx_http_create_locations_list(ngx_queue_t *locations, + ngx_queue_t *q); +static ngx_http_location_tree_node_t * + ngx_http_create_locations_tree(ngx_conf_t *cf, ngx_queue_t *locations, + size_t prefix); + +static ngx_int_t ngx_http_optimize_servers(ngx_conf_t *cf, + ngx_http_core_main_conf_t *cmcf, ngx_array_t *ports); +static ngx_int_t ngx_http_server_names(ngx_conf_t *cf, + ngx_http_core_main_conf_t *cmcf, ngx_http_conf_addr_t *addr); +static ngx_int_t ngx_http_cmp_conf_addrs(const void *one, const void *two); static int ngx_libc_cdecl ngx_http_cmp_dns_wildcards(const void *one, const void *two); -ngx_uint_t ngx_http_max_module; +static ngx_int_t ngx_http_init_listening(ngx_conf_t *cf, + ngx_http_conf_port_t *port); +static ngx_listening_t *ngx_http_add_listening(ngx_conf_t *cf, + ngx_http_conf_addr_t *addr); +static ngx_int_t ngx_http_add_addrs(ngx_conf_t *cf, ngx_http_port_t *hport, + ngx_http_conf_addr_t *addr); +#if (NGX_HAVE_INET6) +static ngx_int_t ngx_http_add_addrs6(ngx_conf_t *cf, ngx_http_port_t *hport, + ngx_http_conf_addr_t *addr); +#endif -ngx_uint_t ngx_http_total_requests; -uint64_t ngx_http_total_sent; +ngx_uint_t ngx_http_max_module; ngx_int_t (*ngx_http_top_header_filter) (ngx_http_request_t *r); ngx_int_t (*ngx_http_top_body_filter) (ngx_http_request_t *r, ngx_chain_t *ch); +ngx_str_t ngx_http_html_default_types[] = { + ngx_string("text/html"), + ngx_null_string +}; + + static ngx_command_t ngx_http_commands[] = { { ngx_string("http"), @@ -73,37 +118,13 @@ ngx_http_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { char *rv; - ngx_int_t rc, j; - ngx_uint_t mi, m, s, l, p, a, i, n; - ngx_uint_t find_config_index, use_rewrite, use_access; - ngx_uint_t last, bind_all, done; + ngx_uint_t mi, m, s; ngx_conf_t pcf; - ngx_array_t headers_in, in_ports; - ngx_hash_key_t *hk; - ngx_hash_init_t hash; - ngx_listening_t *ls; - ngx_http_listen_t *lscf; ngx_http_module_t *module; - ngx_http_header_t *header; - ngx_http_in_port_t *hip; - ngx_http_handler_pt *h; ngx_http_conf_ctx_t *ctx; - ngx_http_conf_in_port_t *in_port; - ngx_http_conf_in_addr_t *in_addr; - ngx_hash_keys_arrays_t ha; - ngx_http_server_name_t *name; - ngx_http_phase_handler_t *ph; - ngx_http_virtual_names_t *vn; - ngx_http_core_srv_conf_t **cscfp, *cscf; ngx_http_core_loc_conf_t *clcf; - ngx_http_phase_handler_pt checker; + ngx_http_core_srv_conf_t **cscfp; ngx_http_core_main_conf_t *cmcf; -#if (NGX_PCRE) - ngx_uint_t regex; -#endif -#if (NGX_WIN32) - ngx_iocp_conf_t *iocpcf; -#endif /* the main http context */ @@ -202,7 +223,6 @@ } module = ngx_modules[m]->ctx; - mi = ngx_modules[m]->ctx_index; if (module->preconfiguration) { if (module->preconfiguration(cf) != NGX_OK) { @@ -218,8 +238,7 @@ rv = ngx_conf_parse(cf, NULL); if (rv != NGX_CONF_OK) { - *cf = pcf; - return rv; + goto failed; } /* @@ -243,119 +262,163 @@ if (module->init_main_conf) { rv = module->init_main_conf(cf, ctx->main_conf[mi]); if (rv != NGX_CONF_OK) { - *cf = pcf; - return rv; + goto failed; } } - for (s = 0; s < cmcf->servers.nelts; s++) { + rv = ngx_http_merge_servers(cf, cmcf, module, mi); + if (rv != NGX_CONF_OK) { + goto failed; + } + } + - /* merge the server{}s' srv_conf's */ + /* create location trees */ - if (module->merge_srv_conf) { - rv = module->merge_srv_conf(cf, - ctx->srv_conf[mi], - cscfp[s]->ctx->srv_conf[mi]); - if (rv != NGX_CONF_OK) { - *cf = pcf; - return rv; - } - } + for (s = 0; s < cmcf->servers.nelts; s++) { - if (module->merge_loc_conf) { + clcf = cscfp[s]->ctx->loc_conf[ngx_http_core_module.ctx_index]; - /* merge the server{}'s loc_conf */ + if (ngx_http_init_locations(cf, cscfp[s], clcf) != NGX_OK) { + return NGX_CONF_ERROR; + } - rv = module->merge_loc_conf(cf, - ctx->loc_conf[mi], - cscfp[s]->ctx->loc_conf[mi]); - if (rv != NGX_CONF_OK) { - *cf = pcf; - return rv; - } + if (ngx_http_init_static_location_trees(cf, clcf) != NGX_OK) { + return NGX_CONF_ERROR; + } + } - /* merge the locations{}' loc_conf's */ - rv = ngx_http_merge_locations(cf, &cscfp[s]->locations, - cscfp[s]->ctx->loc_conf, - module, mi); - if (rv != NGX_CONF_OK) { - *cf = pcf; - return rv; - } + if (ngx_http_init_phases(cf, cmcf) != NGX_OK) { + return NGX_CONF_ERROR; + } + + if (ngx_http_init_headers_in_hash(cf, cmcf) != NGX_OK) { + return NGX_CONF_ERROR; + } + + + for (m = 0; ngx_modules[m]; m++) { + if (ngx_modules[m]->type != NGX_HTTP_MODULE) { + continue; + } + + module = ngx_modules[m]->ctx; + + if (module->postconfiguration) { + if (module->postconfiguration(cf) != NGX_OK) { + return NGX_CONF_ERROR; } } } + if (ngx_http_variables_init_vars(cf) != NGX_OK) { + return NGX_CONF_ERROR; + } + + /* + * http{}'s cf->ctx was needed while the configuration merging + * and in postconfiguration process + */ + + *cf = pcf; + + + if (ngx_http_init_phase_handlers(cf, cmcf) != NGX_OK) { + return NGX_CONF_ERROR; + } + + + /* optimize the lists of ports, addresses and server names */ + + if (ngx_http_optimize_servers(cf, cmcf, cmcf->ports) != NGX_OK) { + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; + +failed: + + *cf = pcf; + + return rv; +} - /* init lists of the handlers */ +static ngx_int_t +ngx_http_init_phases(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf) +{ if (ngx_array_init(&cmcf->phases[NGX_HTTP_POST_READ_PHASE].handlers, cf->pool, 1, sizeof(ngx_http_handler_pt)) != NGX_OK) { - return NGX_CONF_ERROR; + return NGX_ERROR; } - if (ngx_array_init(&cmcf->phases[NGX_HTTP_SERVER_REWRITE_PHASE].handlers, cf->pool, 1, sizeof(ngx_http_handler_pt)) != NGX_OK) { - return NGX_CONF_ERROR; + return NGX_ERROR; } - if (ngx_array_init(&cmcf->phases[NGX_HTTP_REWRITE_PHASE].handlers, cf->pool, 1, sizeof(ngx_http_handler_pt)) != NGX_OK) { - return NGX_CONF_ERROR; + return NGX_ERROR; } - if (ngx_array_init(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers, cf->pool, 1, sizeof(ngx_http_handler_pt)) != NGX_OK) { - return NGX_CONF_ERROR; + return NGX_ERROR; } - if (ngx_array_init(&cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers, cf->pool, 2, sizeof(ngx_http_handler_pt)) != NGX_OK) { - return NGX_CONF_ERROR; + return NGX_ERROR; } - if (ngx_array_init(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers, cf->pool, 4, sizeof(ngx_http_handler_pt)) != NGX_OK) { - return NGX_CONF_ERROR; + return NGX_ERROR; } - if (ngx_array_init(&cmcf->phases[NGX_HTTP_LOG_PHASE].handlers, cf->pool, 1, sizeof(ngx_http_handler_pt)) != NGX_OK) { - return NGX_CONF_ERROR; + return NGX_ERROR; } + return NGX_OK; +} + + +static ngx_int_t +ngx_http_init_headers_in_hash(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf) +{ + ngx_array_t headers_in; + ngx_hash_key_t *hk; + ngx_hash_init_t hash; + ngx_http_header_t *header; if (ngx_array_init(&headers_in, cf->temp_pool, 32, sizeof(ngx_hash_key_t)) != NGX_OK) { - return NGX_CONF_ERROR; + return NGX_ERROR; } for (header = ngx_http_headers_in; header->name.len; header++) { hk = ngx_array_push(&headers_in); if (hk == NULL) { - return NGX_CONF_ERROR; + return NGX_ERROR; } hk->key = header->name; @@ -372,36 +435,22 @@ hash.temp_pool = NULL; if (ngx_hash_init(&hash, headers_in.elts, headers_in.nelts) != NGX_OK) { - return NGX_CONF_ERROR; - } - - - for (m = 0; ngx_modules[m]; m++) { - if (ngx_modules[m]->type != NGX_HTTP_MODULE) { - continue; - } - - module = ngx_modules[m]->ctx; - mi = ngx_modules[m]->ctx_index; - - if (module->postconfiguration) { - if (module->postconfiguration(cf) != NGX_OK) { - return NGX_CONF_ERROR; - } - } - } - - if (ngx_http_variables_init_vars(cf) != NGX_OK) { - return NGX_CONF_ERROR; + return NGX_ERROR; } - /* - * http{}'s cf->ctx was needed while the configuration merging - * and in postconfiguration process - */ + return NGX_OK; +} - *cf = pcf; +static ngx_int_t +ngx_http_init_phase_handlers(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf) +{ + ngx_int_t j; + ngx_uint_t i, n; + ngx_uint_t find_config_index, use_rewrite, use_access; + ngx_http_handler_pt *h; + ngx_http_phase_handler_t *ph; + ngx_http_phase_handler_pt checker; cmcf->phase_engine.server_rewrite_index = (ngx_uint_t) -1; cmcf->phase_engine.location_rewrite_index = (ngx_uint_t) -1; @@ -409,7 +458,7 @@ use_rewrite = cmcf->phases[NGX_HTTP_REWRITE_PHASE].handlers.nelts ? 1 : 0; use_access = cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers.nelts ? 1 : 0; - n = use_rewrite + use_access + 1; /* find config phase */ + n = use_rewrite + use_access + cmcf->try_files + 1 /* find config phase */; for (i = 0; i < NGX_HTTP_LOG_PHASE; i++) { n += cmcf->phases[i].handlers.nelts; @@ -418,7 +467,7 @@ ph = ngx_pcalloc(cf->pool, n * sizeof(ngx_http_phase_handler_t) + sizeof(void *)); if (ph == NULL) { - return NGX_CONF_ERROR; + return NGX_ERROR; } cmcf->phase_engine.handlers = ph; @@ -433,7 +482,7 @@ if (cmcf->phase_engine.server_rewrite_index == (ngx_uint_t) -1) { cmcf->phase_engine.server_rewrite_index = n; } - checker = ngx_http_core_generic_phase; + checker = ngx_http_core_rewrite_phase; break; @@ -450,7 +499,7 @@ if (cmcf->phase_engine.location_rewrite_index == (ngx_uint_t) -1) { cmcf->phase_engine.location_rewrite_index = n; } - checker = ngx_http_core_generic_phase; + checker = ngx_http_core_rewrite_phase; break; @@ -478,6 +527,15 @@ continue; + case NGX_HTTP_TRY_FILES_PHASE: + if (cmcf->try_files) { + ph->checker = ngx_http_core_try_files_phase; + n++; + ph++; + } + + continue; + case NGX_HTTP_CONTENT_PHASE: checker = ngx_http_core_content_phase; break; @@ -496,634 +554,1513 @@ } } + return NGX_OK; +} - /* - * create the lists of ports, addresses and server names - * to quickly find the server core module configuration at run-time - */ - - if (ngx_array_init(&in_ports, cf->temp_pool, 2, - sizeof(ngx_http_conf_in_port_t)) - != NGX_OK) - { - return NGX_CONF_ERROR; - } - /* "server" directives */ +static char * +ngx_http_merge_servers(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf, + ngx_http_module_t *module, ngx_uint_t ctx_index) +{ + char *rv; + ngx_uint_t s; + ngx_http_conf_ctx_t *ctx, saved; + ngx_http_core_loc_conf_t *clcf; + ngx_http_core_srv_conf_t **cscfp; cscfp = cmcf->servers.elts; + ctx = (ngx_http_conf_ctx_t *) cf->ctx; + saved = *ctx; + rv = NGX_CONF_OK; + for (s = 0; s < cmcf->servers.nelts; s++) { - /* "listen" directives */ + /* merge the server{}s' srv_conf's */ - lscf = cscfp[s]->listen.elts; - for (l = 0; l < cscfp[s]->listen.nelts; l++) { + ctx->srv_conf = cscfp[s]->ctx->srv_conf; - /* AF_INET only */ + if (module->merge_srv_conf) { + rv = module->merge_srv_conf(cf, saved.srv_conf[ctx_index], + cscfp[s]->ctx->srv_conf[ctx_index]); + if (rv != NGX_CONF_OK) { + goto failed; + } + } - in_port = in_ports.elts; - for (p = 0; p < in_ports.nelts; p++) { + if (module->merge_loc_conf) { - if (lscf[l].port != in_port[p].port) { - continue; - } + /* merge the server{}'s loc_conf */ - /* the port is already in the port list */ + ctx->loc_conf = cscfp[s]->ctx->loc_conf; - in_addr = in_port[p].addrs.elts; - for (a = 0; a < in_port[p].addrs.nelts; a++) { + rv = module->merge_loc_conf(cf, saved.loc_conf[ctx_index], + cscfp[s]->ctx->loc_conf[ctx_index]); + if (rv != NGX_CONF_OK) { + goto failed; + } - if (lscf[l].addr != in_addr[a].addr) { - continue; - } + /* merge the locations{}' loc_conf's */ - /* the address is already in the address list */ + clcf = cscfp[s]->ctx->loc_conf[ngx_http_core_module.ctx_index]; - if (ngx_http_add_names(cf, &in_addr[a], cscfp[s]) != NGX_OK) - { - return NGX_CONF_ERROR; - } + rv = ngx_http_merge_locations(cf, clcf->locations, + cscfp[s]->ctx->loc_conf, + module, ctx_index); + if (rv != NGX_CONF_OK) { + goto failed; + } + } + } - /* - * check the duplicate "default" server - * for this address:port - */ +failed: - if (lscf[l].conf.default_server) { + *ctx = saved; - if (in_addr[a].default_server) { - ngx_log_error(NGX_LOG_ERR, cf->log, 0, - "the duplicate default server in %s:%ui", - &lscf[l].file_name, lscf[l].line); + return rv; +} - return NGX_CONF_ERROR; - } - in_addr[a].core_srv_conf = cscfp[s]; - in_addr[a].default_server = 1; - } +static char * +ngx_http_merge_locations(ngx_conf_t *cf, ngx_queue_t *locations, + void **loc_conf, ngx_http_module_t *module, ngx_uint_t ctx_index) +{ + char *rv; + ngx_queue_t *q; + ngx_http_conf_ctx_t *ctx, saved; + ngx_http_core_loc_conf_t *clcf; + ngx_http_location_queue_t *lq; - goto found; - } + if (locations == NULL) { + return NGX_CONF_OK; + } - /* - * add the address to the addresses list that - * bound to this port - */ - - if (ngx_http_add_address(cf, &in_port[p], &lscf[l], cscfp[s]) - != NGX_OK) - { - return NGX_CONF_ERROR; - } + ctx = (ngx_http_conf_ctx_t *) cf->ctx; + saved = *ctx; - goto found; - } + for (q = ngx_queue_head(locations); + q != ngx_queue_sentinel(locations); + q = ngx_queue_next(q)) + { + lq = (ngx_http_location_queue_t *) q; - /* add the port to the in_port list */ + clcf = lq->exact ? lq->exact : lq->inclusive; + ctx->loc_conf = clcf->loc_conf; - in_port = ngx_array_push(&in_ports); - if (in_port == NULL) { - return NGX_CONF_ERROR; - } + rv = module->merge_loc_conf(cf, loc_conf[ctx_index], + clcf->loc_conf[ctx_index]); + if (rv != NGX_CONF_OK) { + return rv; + } - in_port->port = lscf[l].port; - in_port->addrs.elts = NULL; + rv = ngx_http_merge_locations(cf, clcf->locations, clcf->loc_conf, + module, ctx_index); + if (rv != NGX_CONF_OK) { + return rv; + } + } - if (ngx_http_add_address(cf, in_port, &lscf[l], cscfp[s]) != NGX_OK) - { - return NGX_CONF_ERROR; - } + *ctx = saved; - found: + return NGX_CONF_OK; +} - continue; - } - } +static ngx_int_t +ngx_http_init_locations(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf, + ngx_http_core_loc_conf_t *pclcf) +{ + ngx_uint_t n; + ngx_queue_t *q, *locations, *named, tail; + ngx_http_core_loc_conf_t *clcf; + ngx_http_location_queue_t *lq; + ngx_http_core_loc_conf_t **clcfp; +#if (NGX_PCRE) + ngx_uint_t r; + ngx_queue_t *regex; +#endif - /* optimize the lists of ports, addresses and server names */ + locations = pclcf->locations; - /* AF_INET only */ + if (locations == NULL) { + return NGX_OK; + } - in_port = in_ports.elts; - for (p = 0; p < in_ports.nelts; p++) { + ngx_queue_sort(locations, ngx_http_cmp_locations); - ngx_sort(in_port[p].addrs.elts, (size_t) in_port[p].addrs.nelts, - sizeof(ngx_http_conf_in_addr_t), ngx_http_cmp_conf_in_addrs); + named = NULL; + n = 0; +#if (NGX_PCRE) + regex = NULL; + r = 0; +#endif - /* - * check whether all name-based servers have the same configuraiton - * as the default server, - * or some servers disable optimizing the server names - */ + for (q = ngx_queue_head(locations); + q != ngx_queue_sentinel(locations); + q = ngx_queue_next(q)) + { + lq = (ngx_http_location_queue_t *) q; - in_addr = in_port[p].addrs.elts; - for (a = 0; a < in_port[p].addrs.nelts; a++) { + clcf = lq->exact ? lq->exact : lq->inclusive; - name = in_addr[a].names.elts; - for (s = 0; s < in_addr[a].names.nelts; s++) { + if (ngx_http_init_locations(cf, NULL, clcf) != NGX_OK) { + return NGX_ERROR; + } - if (in_addr[a].core_srv_conf != name[s].core_srv_conf - || name[s].core_srv_conf->optimize_server_names == 0) - { - goto virtual_names; - } - } +#if (NGX_PCRE) - /* - * if all name-based servers have the same configuration - * as the default server, - * and no servers disable optimizing the server names - * then we do not need to check them at run-time at all - */ + if (clcf->regex) { + r++; - in_addr[a].names.nelts = 0; + if (regex == NULL) { + regex = q; + } continue; + } - virtual_names: +#endif - ngx_memzero(&ha, sizeof(ngx_hash_keys_arrays_t)); + if (clcf->named) { + n++; - ha.temp_pool = ngx_create_pool(16384, cf->log); - if (ha.temp_pool == NULL) { - return NGX_CONF_ERROR; + if (named == NULL) { + named = q; } - ha.pool = cf->pool; + continue; + } - if (ngx_hash_keys_array_init(&ha, NGX_HASH_LARGE) != NGX_OK) { - ngx_destroy_pool(ha.temp_pool); - return NGX_CONF_ERROR; - } + if (clcf->noname) { + break; + } + } -#if (NGX_PCRE) - regex = 0; -#endif + if (q != ngx_queue_sentinel(locations)) { + ngx_queue_split(locations, q, &tail); + } - name = in_addr[a].names.elts; + if (named) { + clcfp = ngx_palloc(cf->pool, + (n + 1) * sizeof(ngx_http_core_loc_conf_t **)); + if (clcfp == NULL) { + return NGX_ERROR; + } - for (s = 0; s < in_addr[a].names.nelts; s++) { + cscf->named_locations = clcfp; -#if (NGX_PCRE) - if (name[s].regex) { - regex++; - continue; - } -#endif + for (q = named; + q != ngx_queue_sentinel(locations); + q = ngx_queue_next(q)) + { + lq = (ngx_http_location_queue_t *) q; - rc = ngx_hash_add_key(&ha, &name[s].name, name[s].core_srv_conf, - NGX_HASH_WILDCARD_KEY); + *(clcfp++) = lq->exact; + } - if (rc == NGX_ERROR) { - return NGX_CONF_ERROR; - } + *clcfp = NULL; - if (rc == NGX_DECLINED) { - ngx_log_error(NGX_LOG_EMERG, cf->log, 0, - "invalid server name or wildcard \"%V\" on %s", - &name[s].name, in_addr[a].listen_conf->addr); - return NGX_CONF_ERROR; - } + ngx_queue_split(locations, named, &tail); + } - if (rc == NGX_BUSY) { - ngx_log_error(NGX_LOG_WARN, cf->log, 0, - "conflicting server name \"%V\" on %s, ignored", - &name[s].name, in_addr[a].listen_conf->addr); - } - } +#if (NGX_PCRE) - hash.key = ngx_hash_key_lc; - hash.max_size = cmcf->server_names_hash_max_size; - hash.bucket_size = cmcf->server_names_hash_bucket_size; - hash.name = "server_names_hash"; - hash.pool = cf->pool; - - if (ha.keys.nelts) { - hash.hash = &in_addr[a].hash; - hash.temp_pool = NULL; - - if (ngx_hash_init(&hash, ha.keys.elts, ha.keys.nelts) != NGX_OK) - { - ngx_destroy_pool(ha.temp_pool); - return NGX_CONF_ERROR; - } - } + if (regex) { - if (ha.dns_wc_head.nelts) { + clcfp = ngx_palloc(cf->pool, + (r + 1) * sizeof(ngx_http_core_loc_conf_t **)); + if (clcfp == NULL) { + return NGX_ERROR; + } - ngx_qsort(ha.dns_wc_head.elts, - (size_t) ha.dns_wc_head.nelts, - sizeof(ngx_hash_key_t), - ngx_http_cmp_dns_wildcards); - - hash.hash = NULL; - hash.temp_pool = ha.temp_pool; - - if (ngx_hash_wildcard_init(&hash, ha.dns_wc_head.elts, - ha.dns_wc_head.nelts) - != NGX_OK) - { - ngx_destroy_pool(ha.temp_pool); - return NGX_CONF_ERROR; - } + pclcf->regex_locations = clcfp; - in_addr[a].wc_head = (ngx_hash_wildcard_t *) hash.hash; - } + for (q = regex; + q != ngx_queue_sentinel(locations); + q = ngx_queue_next(q)) + { + lq = (ngx_http_location_queue_t *) q; - if (ha.dns_wc_tail.nelts) { + *(clcfp++) = lq->exact; + } - ngx_qsort(ha.dns_wc_tail.elts, - (size_t) ha.dns_wc_tail.nelts, - sizeof(ngx_hash_key_t), - ngx_http_cmp_dns_wildcards); + *clcfp = NULL; - hash.hash = NULL; - hash.temp_pool = ha.temp_pool; + ngx_queue_split(locations, regex, &tail); + } - if (ngx_hash_wildcard_init(&hash, ha.dns_wc_tail.elts, - ha.dns_wc_tail.nelts) - != NGX_OK) - { - ngx_destroy_pool(ha.temp_pool); - return NGX_CONF_ERROR; - } +#endif - in_addr[a].wc_tail = (ngx_hash_wildcard_t *) hash.hash; - } + return NGX_OK; +} - ngx_destroy_pool(ha.temp_pool); + +static ngx_int_t +ngx_http_init_static_location_trees(ngx_conf_t *cf, + ngx_http_core_loc_conf_t *pclcf) +{ + ngx_queue_t *q, *locations; + ngx_http_core_loc_conf_t *clcf; + ngx_http_location_queue_t *lq; + + locations = pclcf->locations; + + if (locations == NULL) { + return NGX_OK; + } + + if (ngx_queue_empty(locations)) { + return NGX_OK; + } + + for (q = ngx_queue_head(locations); + q != ngx_queue_sentinel(locations); + q = ngx_queue_next(q)) + { + lq = (ngx_http_location_queue_t *) q; + + clcf = lq->exact ? lq->exact : lq->inclusive; + + if (ngx_http_init_static_location_trees(cf, clcf) != NGX_OK) { + return NGX_ERROR; + } + } + + if (ngx_http_join_exact_locations(cf, locations) != NGX_OK) { + return NGX_ERROR; + } + + ngx_http_create_locations_list(locations, ngx_queue_head(locations)); + + pclcf->static_locations = ngx_http_create_locations_tree(cf, locations, 0); + if (pclcf->static_locations == NULL) { + return NGX_ERROR; + } + + return NGX_OK; +} + + +ngx_int_t +ngx_http_add_location(ngx_conf_t *cf, ngx_queue_t **locations, + ngx_http_core_loc_conf_t *clcf) +{ + ngx_http_location_queue_t *lq; + + if (*locations == NULL) { + *locations = ngx_palloc(cf->temp_pool, + sizeof(ngx_http_location_queue_t)); + if (*locations == NULL) { + return NGX_ERROR; + } + + ngx_queue_init(*locations); + } + + lq = ngx_palloc(cf->temp_pool, sizeof(ngx_http_location_queue_t)); + if (lq == NULL) { + return NGX_ERROR; + } + + if (clcf->exact_match +#if (NGX_PCRE) + || clcf->regex +#endif + || clcf->named || clcf->noname) + { + lq->exact = clcf; + lq->inclusive = NULL; + + } else { + lq->exact = NULL; + lq->inclusive = clcf; + } + + lq->name = &clcf->name; + lq->file_name = cf->conf_file->file.name.data; + lq->line = cf->conf_file->line; + + ngx_queue_init(&lq->list); + + ngx_queue_insert_tail(*locations, &lq->queue); + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_cmp_locations(const ngx_queue_t *one, const ngx_queue_t *two) +{ + ngx_int_t rc; + ngx_http_core_loc_conf_t *first, *second; + ngx_http_location_queue_t *lq1, *lq2; + + lq1 = (ngx_http_location_queue_t *) one; + lq2 = (ngx_http_location_queue_t *) two; + + first = lq1->exact ? lq1->exact : lq1->inclusive; + second = lq2->exact ? lq2->exact : lq2->inclusive; + + if (first->noname && !second->noname) { + /* shift no named locations to the end */ + return 1; + } + + if (!first->noname && second->noname) { + /* shift no named locations to the end */ + return -1; + } + + if (first->noname || second->noname) { + /* do not sort no named locations */ + return 0; + } + + if (first->named && !second->named) { + /* shift named locations to the end */ + return 1; + } + + if (!first->named && second->named) { + /* shift named locations to the end */ + return -1; + } + + if (first->named && second->named) { + return ngx_strcmp(first->name.data, second->name.data); + } #if (NGX_PCRE) - if (regex == 0) { + if (first->regex && !second->regex) { + /* shift the regex matches to the end */ + return 1; + } + + if (!first->regex && second->regex) { + /* shift the regex matches to the end */ + return -1; + } + + if (first->regex || second->regex) { + /* do not sort the regex matches */ + return 0; + } + +#endif + + rc = ngx_strcmp(first->name.data, second->name.data); + + if (rc == 0 && !first->exact_match && second->exact_match) { + /* an exact match must be before the same inclusive one */ + return 1; + } + + return rc; +} + + +static ngx_int_t +ngx_http_join_exact_locations(ngx_conf_t *cf, ngx_queue_t *locations) +{ + ngx_queue_t *q, *x; + ngx_http_location_queue_t *lq, *lx; + + q = ngx_queue_head(locations); + + while (q != ngx_queue_last(locations)) { + + x = ngx_queue_next(q); + + lq = (ngx_http_location_queue_t *) q; + lx = (ngx_http_location_queue_t *) x; + + if (ngx_strcmp(lq->name->data, lx->name->data) == 0) { + + if ((lq->exact && lx->exact) || (lq->inclusive && lx->inclusive)) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "duplicate location \"%V\" in %s:%ui", + lx->name, lx->file_name, lx->line); + + return NGX_ERROR; + } + + lq->inclusive = lx->inclusive; + + ngx_queue_remove(x); + + continue; + } + + q = ngx_queue_next(q); + } + + return NGX_OK; +} + + +static void +ngx_http_create_locations_list(ngx_queue_t *locations, ngx_queue_t *q) +{ + u_char *name; + size_t len; + ngx_queue_t *x, tail; + ngx_http_location_queue_t *lq, *lx; + + if (q == ngx_queue_last(locations)) { + return; + } + + lq = (ngx_http_location_queue_t *) q; + + if (lq->inclusive == NULL) { + ngx_http_create_locations_list(locations, ngx_queue_next(q)); + return; + } + + len = lq->name->len; + name = lq->name->data; + + for (x = ngx_queue_next(q); + x != ngx_queue_sentinel(locations); + x = ngx_queue_next(x)) + { + lx = (ngx_http_location_queue_t *) x; + + if (len > lx->name->len + || (ngx_strncmp(name, lx->name->data, len) != 0)) + { + break; + } + } + + q = ngx_queue_next(q); + + if (q == x) { + ngx_http_create_locations_list(locations, x); + return; + } + + ngx_queue_split(locations, q, &tail); + ngx_queue_add(&lq->list, &tail); + + if (x == ngx_queue_sentinel(locations)) { + ngx_http_create_locations_list(&lq->list, ngx_queue_head(&lq->list)); + return; + } + + ngx_queue_split(&lq->list, x, &tail); + ngx_queue_add(locations, &tail); + + ngx_http_create_locations_list(&lq->list, ngx_queue_head(&lq->list)); + + ngx_http_create_locations_list(locations, x); +} + + +/* + * to keep cache locality for left leaf nodes, allocate nodes in following + * order: node, left subtree, right subtree, inclusive subtree + */ + +static ngx_http_location_tree_node_t * +ngx_http_create_locations_tree(ngx_conf_t *cf, ngx_queue_t *locations, + size_t prefix) +{ + size_t len; + ngx_queue_t *q, tail; + ngx_http_location_queue_t *lq; + ngx_http_location_tree_node_t *node; + + q = ngx_queue_middle(locations); + + lq = (ngx_http_location_queue_t *) q; + len = lq->name->len - prefix; + + node = ngx_palloc(cf->pool, + offsetof(ngx_http_location_tree_node_t, name) + len); + if (node == NULL) { + return NULL; + } + + node->left = NULL; + node->right = NULL; + node->tree = NULL; + node->exact = lq->exact; + node->inclusive = lq->inclusive; + + node->auto_redirect = (u_char) ((lq->exact && lq->exact->auto_redirect) + || (lq->inclusive && lq->inclusive->auto_redirect)); + + node->len = (u_char) len; + ngx_memcpy(node->name, &lq->name->data[prefix], len); + + ngx_queue_split(locations, q, &tail); + + if (ngx_queue_empty(locations)) { + /* + * ngx_queue_split() insures that if left part is empty, + * then right one is empty too + */ + goto inclusive; + } + + node->left = ngx_http_create_locations_tree(cf, locations, prefix); + if (node->left == NULL) { + return NULL; + } + + ngx_queue_remove(q); + + if (ngx_queue_empty(&tail)) { + goto inclusive; + } + + node->right = ngx_http_create_locations_tree(cf, &tail, prefix); + if (node->right == NULL) { + return NULL; + } + +inclusive: + + if (ngx_queue_empty(&lq->list)) { + return node; + } + + node->tree = ngx_http_create_locations_tree(cf, &lq->list, prefix + len); + if (node->tree == NULL) { + return NULL; + } + + return node; +} + + +ngx_int_t +ngx_http_add_listen(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf, + ngx_http_listen_opt_t *lsopt) +{ + in_port_t p; + ngx_uint_t i; + struct sockaddr *sa; + struct sockaddr_in *sin; + ngx_http_conf_port_t *port; + ngx_http_core_main_conf_t *cmcf; +#if (NGX_HAVE_INET6) + struct sockaddr_in6 *sin6; +#endif + + cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); + + if (cmcf->ports == NULL) { + cmcf->ports = ngx_array_create(cf->temp_pool, 2, + sizeof(ngx_http_conf_port_t)); + if (cmcf->ports == NULL) { + return NGX_ERROR; + } + } + + sa = &lsopt->u.sockaddr; + + switch (sa->sa_family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + sin6 = &lsopt->u.sockaddr_in6; + p = sin6->sin6_port; + break; +#endif + +#if (NGX_HAVE_UNIX_DOMAIN) + case AF_UNIX: + p = 0; + break; +#endif + + default: /* AF_INET */ + sin = &lsopt->u.sockaddr_in; + p = sin->sin_port; + break; + } + + port = cmcf->ports->elts; + for (i = 0; i < cmcf->ports->nelts; i++) { + + if (p != port[i].port || sa->sa_family != port[i].family) { + continue; + } + + /* a port is already in the port list */ + + return ngx_http_add_addresses(cf, cscf, &port[i], lsopt); + } + + /* add a port to the port list */ + + port = ngx_array_push(cmcf->ports); + if (port == NULL) { + return NGX_ERROR; + } + + port->family = sa->sa_family; + port->port = p; + port->addrs.elts = NULL; + + return ngx_http_add_address(cf, cscf, port, lsopt); +} + + +static ngx_int_t +ngx_http_add_addresses(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf, + ngx_http_conf_port_t *port, ngx_http_listen_opt_t *lsopt) +{ + u_char *p; + size_t len, off; + ngx_uint_t i, default_server; + struct sockaddr *sa; + ngx_http_conf_addr_t *addr; +#if (NGX_HAVE_UNIX_DOMAIN) + struct sockaddr_un *saun; +#endif +#if (NGX_HTTP_SSL) + ngx_uint_t ssl; +#endif + + /* + * we can not compare whole sockaddr struct's as kernel + * may fill some fields in inherited sockaddr struct's + */ + + sa = &lsopt->u.sockaddr; + + switch (sa->sa_family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + off = offsetof(struct sockaddr_in6, sin6_addr); + len = 16; + break; +#endif + +#if (NGX_HAVE_UNIX_DOMAIN) + case AF_UNIX: + off = offsetof(struct sockaddr_un, sun_path); + len = sizeof(saun->sun_path); + break; +#endif + + default: /* AF_INET */ + off = offsetof(struct sockaddr_in, sin_addr); + len = 4; + break; + } + + p = lsopt->u.sockaddr_data + off; + + addr = port->addrs.elts; + + for (i = 0; i < port->addrs.nelts; i++) { + + if (ngx_memcmp(p, addr[i].opt.u.sockaddr_data + off, len) != 0) { + continue; + } + + /* the address is already in the address list */ + + if (ngx_http_add_server(cf, cscf, &addr[i]) != NGX_OK) { + return NGX_ERROR; + } + + /* preserve default_server bit during listen options overwriting */ + default_server = addr[i].opt.default_server; + +#if (NGX_HTTP_SSL) + ssl = lsopt->ssl || addr[i].opt.ssl; +#endif + + if (lsopt->set) { + + if (addr[i].opt.set) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "a duplicate listen options for %s", addr[i].opt.addr); + return NGX_ERROR; + } + + addr[i].opt = *lsopt; + } + + /* check the duplicate "default" server for this address:port */ + + if (lsopt->default_server) { + + if (default_server) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "a duplicate default server for %s", addr[i].opt.addr); + return NGX_ERROR; + } + + default_server = 1; + addr[i].default_server = cscf; + } + + addr[i].opt.default_server = default_server; +#if (NGX_HTTP_SSL) + addr[i].opt.ssl = ssl; +#endif + + return NGX_OK; + } + + /* add the address to the addresses list that bound to this port */ + + return ngx_http_add_address(cf, cscf, port, lsopt); +} + + +/* + * add the server address, the server names and the server core module + * configurations to the port list + */ + +static ngx_int_t +ngx_http_add_address(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf, + ngx_http_conf_port_t *port, ngx_http_listen_opt_t *lsopt) +{ + ngx_http_conf_addr_t *addr; + + if (port->addrs.elts == NULL) { + if (ngx_array_init(&port->addrs, cf->temp_pool, 4, + sizeof(ngx_http_conf_addr_t)) + != NGX_OK) + { + return NGX_ERROR; + } + } + + addr = ngx_array_push(&port->addrs); + if (addr == NULL) { + return NGX_ERROR; + } + + addr->opt = *lsopt; + addr->hash.buckets = NULL; + addr->hash.size = 0; + addr->wc_head = NULL; + addr->wc_tail = NULL; +#if (NGX_PCRE) + addr->nregex = 0; + addr->regex = NULL; +#endif + addr->default_server = cscf; + addr->servers.elts = NULL; + + return ngx_http_add_server(cf, cscf, addr); +} + + +/* add the server core module configuration to the address:port */ + +static ngx_int_t +ngx_http_add_server(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf, + ngx_http_conf_addr_t *addr) +{ + ngx_uint_t i; + ngx_http_core_srv_conf_t **server; + + if (addr->servers.elts == NULL) { + if (ngx_array_init(&addr->servers, cf->temp_pool, 4, + sizeof(ngx_http_core_srv_conf_t *)) + != NGX_OK) + { + return NGX_ERROR; + } + + } else { + server = addr->servers.elts; + for (i = 0; i < addr->servers.nelts; i++) { + if (server[i] == cscf) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "a duplicate listen %s", addr->opt.addr); + return NGX_ERROR; + } + } + } + + server = ngx_array_push(&addr->servers); + if (server == NULL) { + return NGX_ERROR; + } + + *server = cscf; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_optimize_servers(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf, + ngx_array_t *ports) +{ + ngx_uint_t p, a; + ngx_http_conf_port_t *port; + ngx_http_conf_addr_t *addr; + + if (ports == NULL) { + return NGX_OK; + } + + port = ports->elts; + for (p = 0; p < ports->nelts; p++) { + + ngx_sort(port[p].addrs.elts, (size_t) port[p].addrs.nelts, + sizeof(ngx_http_conf_addr_t), ngx_http_cmp_conf_addrs); + + /* + * check whether all name-based servers have the same + * configuraiton as a default server for given address:port + */ + + addr = port[p].addrs.elts; + for (a = 0; a < port[p].addrs.nelts; a++) { + + if (addr[a].servers.nelts > 1 +#if (NGX_PCRE) + || addr[a].default_server->captures +#endif + ) + { + if (ngx_http_server_names(cf, cmcf, &addr[a]) != NGX_OK) { + return NGX_ERROR; + } + } + } + + if (ngx_http_init_listening(cf, &port[p]) != NGX_OK) { + return NGX_ERROR; + } + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_server_names(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf, + ngx_http_conf_addr_t *addr) +{ + ngx_int_t rc; + ngx_uint_t n, s; + ngx_hash_init_t hash; + ngx_hash_keys_arrays_t ha; + ngx_http_server_name_t *name; + ngx_http_core_srv_conf_t **cscfp; +#if (NGX_PCRE) + ngx_uint_t regex, i; + + regex = 0; +#endif + + ngx_memzero(&ha, sizeof(ngx_hash_keys_arrays_t)); + + ha.temp_pool = ngx_create_pool(16384, cf->log); + if (ha.temp_pool == NULL) { + return NGX_ERROR; + } + + ha.pool = cf->pool; + + if (ngx_hash_keys_array_init(&ha, NGX_HASH_LARGE) != NGX_OK) { + goto failed; + } + + cscfp = addr->servers.elts; + + for (s = 0; s < addr->servers.nelts; s++) { + + name = cscfp[s]->server_names.elts; + + for (n = 0; n < cscfp[s]->server_names.nelts; n++) { + +#if (NGX_PCRE) + if (name[n].regex) { + regex++; continue; } +#endif - in_addr[a].nregex = regex; - in_addr[a].regex = ngx_palloc(cf->pool, - regex * sizeof(ngx_http_server_name_t)); + rc = ngx_hash_add_key(&ha, &name[n].name, name[n].server, + NGX_HASH_WILDCARD_KEY); - if (in_addr[a].regex == NULL) { - return NGX_CONF_ERROR; + if (rc == NGX_ERROR) { + return NGX_ERROR; } - for (i = 0, s = 0; s < in_addr[a].names.nelts; s++) { - if (name[s].regex) { - in_addr[a].regex[i++] = name[s]; - } + if (rc == NGX_DECLINED) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "invalid server name or wildcard \"%V\" on %s", + &name[n].name, addr->opt.addr); + return NGX_ERROR; + } + + if (rc == NGX_BUSY) { + ngx_log_error(NGX_LOG_WARN, cf->log, 0, + "conflicting server name \"%V\" on %s, ignored", + &name[n].name, addr->opt.addr); } -#endif } + } - in_addr = in_port[p].addrs.elts; - last = in_port[p].addrs.nelts; + hash.key = ngx_hash_key_lc; + hash.max_size = cmcf->server_names_hash_max_size; + hash.bucket_size = cmcf->server_names_hash_bucket_size; + hash.name = "server_names_hash"; + hash.pool = cf->pool; - /* - * if there is the binding to the "*:port" then we need to bind() - * to the "*:port" only and ignore the other bindings - */ + if (ha.keys.nelts) { + hash.hash = &addr->hash; + hash.temp_pool = NULL; - if (in_addr[last - 1].addr == INADDR_ANY) { - in_addr[last - 1].bind = 1; - bind_all = 0; + if (ngx_hash_init(&hash, ha.keys.elts, ha.keys.nelts) != NGX_OK) { + goto failed; + } + } - } else { - bind_all = 1; + if (ha.dns_wc_head.nelts) { + + ngx_qsort(ha.dns_wc_head.elts, (size_t) ha.dns_wc_head.nelts, + sizeof(ngx_hash_key_t), ngx_http_cmp_dns_wildcards); + + hash.hash = NULL; + hash.temp_pool = ha.temp_pool; + + if (ngx_hash_wildcard_init(&hash, ha.dns_wc_head.elts, + ha.dns_wc_head.nelts) + != NGX_OK) + { + goto failed; } - for (a = 0; a < last; /* void */ ) { + addr->wc_head = (ngx_hash_wildcard_t *) hash.hash; + } - if (!bind_all && !in_addr[a].bind) { - a++; - continue; - } + if (ha.dns_wc_tail.nelts) { - ls = ngx_listening_inet_stream_socket(cf, in_addr[a].addr, - in_port[p].port); - if (ls == NULL) { - return NGX_CONF_ERROR; - } + ngx_qsort(ha.dns_wc_tail.elts, (size_t) ha.dns_wc_tail.nelts, + sizeof(ngx_hash_key_t), ngx_http_cmp_dns_wildcards); - ls->addr_ntop = 1; + hash.hash = NULL; + hash.temp_pool = ha.temp_pool; - ls->handler = ngx_http_init_connection; + if (ngx_hash_wildcard_init(&hash, ha.dns_wc_tail.elts, + ha.dns_wc_tail.nelts) + != NGX_OK) + { + goto failed; + } - cscf = in_addr[a].core_srv_conf; - ls->pool_size = cscf->connection_pool_size; - ls->post_accept_timeout = cscf->client_header_timeout; + addr->wc_tail = (ngx_hash_wildcard_t *) hash.hash; + } - clcf = cscf->ctx->loc_conf[ngx_http_core_module.ctx_index]; + ngx_destroy_pool(ha.temp_pool); - ls->log = *clcf->err_log; - ls->log.data = &ls->addr_text; - ls->log.handler = ngx_accept_log_error; +#if (NGX_PCRE) -#if (NGX_WIN32) - iocpcf = ngx_event_get_conf(cf->cycle->conf_ctx, ngx_iocp_module); - if (iocpcf->acceptex_read) { - ls->post_accept_buffer_size = cscf->client_header_buffer_size; + if (regex == 0) { + return NGX_OK; + } + + addr->nregex = regex; + addr->regex = ngx_palloc(cf->pool, regex * sizeof(ngx_http_server_name_t)); + if (addr->regex == NULL) { + return NGX_ERROR; + } + + i = 0; + + for (s = 0; s < addr->servers.nelts; s++) { + + name = cscfp[s]->server_names.elts; + + for (n = 0; n < cscfp[s]->server_names.nelts; n++) { + if (name[n].regex) { + addr->regex[i++] = name[n]; } + } + } + #endif - ls->backlog = in_addr[a].listen_conf->backlog; - ls->rcvbuf = in_addr[a].listen_conf->rcvbuf; - ls->sndbuf = in_addr[a].listen_conf->sndbuf; + return NGX_OK; -#if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER) - ls->accept_filter = in_addr[a].listen_conf->accept_filter; -#endif +failed: -#if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT) - ls->deferred_accept = in_addr[a].listen_conf->deferred_accept; -#endif + ngx_destroy_pool(ha.temp_pool); - hip = ngx_palloc(cf->pool, sizeof(ngx_http_in_port_t)); - if (hip == NULL) { - return NGX_CONF_ERROR; - } + return NGX_ERROR; +} - hip->port = in_port[p].port; - hip->port_text.data = ngx_palloc(cf->pool, 7); - if (hip->port_text.data == NULL) { - return NGX_CONF_ERROR; - } +static ngx_int_t +ngx_http_cmp_conf_addrs(const void *one, const void *two) +{ + ngx_http_conf_addr_t *first, *second; - ls->servers = hip; + first = (ngx_http_conf_addr_t *) one; + second = (ngx_http_conf_addr_t *) two; - hip->port_text.len = ngx_sprintf(hip->port_text.data, ":%d", - hip->port) - - hip->port_text.data; + if (first->opt.wildcard) { + /* a wildcard address must be the last resort, shift it to the end */ + return 1; + } - in_addr = in_port[p].addrs.elts; + if (first->opt.bind && !second->opt.bind) { + /* shift explicit bind()ed addresses to the start */ + return -1; + } - if (in_addr[a].bind && in_addr[a].addr != INADDR_ANY) { - hip->naddrs = 1; - done = 0; + if (!first->opt.bind && second->opt.bind) { + /* shift explicit bind()ed addresses to the start */ + return 1; + } - } else if (in_port[p].addrs.nelts > 1 - && in_addr[last - 1].addr == INADDR_ANY) - { - hip->naddrs = last; - done = 1; + /* do not sort by default */ - } else { - hip->naddrs = 1; - done = 0; - } + return 0; +} -#if 0 - ngx_log_error(NGX_LOG_ALERT, cf->log, 0, - "%ui: %V %d %ui %ui", - a, &ls->addr_text, in_addr[a].bind, - hip->naddrs, last); -#endif - hip->addrs = ngx_pcalloc(cf->pool, - hip->naddrs * sizeof(ngx_http_in_addr_t)); - if (hip->addrs == NULL) { - return NGX_CONF_ERROR; - } +static int ngx_libc_cdecl +ngx_http_cmp_dns_wildcards(const void *one, const void *two) +{ + ngx_hash_key_t *first, *second; - for (i = 0; i < hip->naddrs; i++) { - hip->addrs[i].addr = in_addr[i].addr; - hip->addrs[i].core_srv_conf = in_addr[i].core_srv_conf; - - if (in_addr[i].hash.buckets == NULL - && (in_addr[i].wc_head == NULL - || in_addr[i].wc_head->hash.buckets == NULL) - && (in_addr[i].wc_head == NULL - || in_addr[i].wc_head->hash.buckets == NULL)) - { - continue; - } + first = (ngx_hash_key_t *) one; + second = (ngx_hash_key_t *) two; - vn = ngx_palloc(cf->pool, sizeof(ngx_http_virtual_names_t)); - if (vn == NULL) { - return NGX_CONF_ERROR; - } - hip->addrs[i].virtual_names = vn; + return ngx_dns_strcmp(first->key.data, second->key.data); +} - vn->names.hash = in_addr[i].hash; - vn->names.wc_head = in_addr[i].wc_head; - vn->names.wc_tail = in_addr[i].wc_tail; -#if (NGX_PCRE) - vn->nregex = in_addr[i].nregex; - vn->regex = in_addr[i].regex; + +static ngx_int_t +ngx_http_init_listening(ngx_conf_t *cf, ngx_http_conf_port_t *port) +{ + ngx_uint_t i, last, bind_wildcard; + ngx_listening_t *ls; + ngx_http_port_t *hport; + ngx_http_conf_addr_t *addr; + + addr = port->addrs.elts; + last = port->addrs.nelts; + + /* + * If there is a binding to an "*:port" then we need to bind() to + * the "*:port" only and ignore other implicit bindings. The bindings + * have been already sorted: explicit bindings are on the start, then + * implicit bindings go, and wildcard binding is in the end. + */ + + if (addr[last - 1].opt.wildcard) { + addr[last - 1].opt.bind = 1; + bind_wildcard = 1; + + } else { + bind_wildcard = 0; + } + + i = 0; + + while (i < last) { + + if (bind_wildcard && !addr[i].opt.bind) { + i++; + continue; + } + + ls = ngx_http_add_listening(cf, &addr[i]); + if (ls == NULL) { + return NGX_ERROR; + } + + hport = ngx_pcalloc(cf->pool, sizeof(ngx_http_port_t)); + if (hport == NULL) { + return NGX_ERROR; + } + + ls->servers = hport; + + if (i == last - 1) { + hport->naddrs = last; + + } else { + hport->naddrs = 1; + i = 0; + } + + switch (ls->sockaddr->sa_family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + if (ngx_http_add_addrs6(cf, hport, addr) != NGX_OK) { + return NGX_ERROR; + } + break; #endif + default: /* AF_INET */ + if (ngx_http_add_addrs(cf, hport, addr) != NGX_OK) { + return NGX_ERROR; } + break; + } - if (done) { - break; - } + addr++; + last--; + } + + return NGX_OK; +} - in_addr++; - in_port[p].addrs.elts = in_addr; - last--; - a = 0; - } +static ngx_listening_t * +ngx_http_add_listening(ngx_conf_t *cf, ngx_http_conf_addr_t *addr) +{ + ngx_listening_t *ls; + ngx_http_core_loc_conf_t *clcf; + ngx_http_core_srv_conf_t *cscf; + + ls = ngx_create_listening(cf, &addr->opt.u.sockaddr, addr->opt.socklen); + if (ls == NULL) { + return NULL; } -#if 0 + ls->addr_ntop = 1; + + ls->handler = ngx_http_init_connection; + + cscf = addr->default_server; + ls->pool_size = cscf->connection_pool_size; + ls->post_accept_timeout = cscf->client_header_timeout; + + clcf = cscf->ctx->loc_conf[ngx_http_core_module.ctx_index]; + + ls->logp = clcf->error_log; + ls->log.data = &ls->addr_text; + ls->log.handler = ngx_accept_log_error; + +#if (NGX_WIN32) { - u_char address[20]; - ngx_uint_t p, a; + ngx_iocp_conf_t *iocpcf; - in_port = in_ports.elts; - for (p = 0; p < in_ports.nelts; p++) { - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, cf->log, 0, - "port: %d %p", in_port[p].port, &in_port[p]); - in_addr = in_port[p].addrs.elts; - for (a = 0; a < in_port[p].addrs.nelts; a++) { - ngx_inet_ntop(AF_INET, &in_addr[a].addr, address, 20); - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, cf->log, 0, - "%s:%d %p", - address, in_port[p].port, in_addr[a].core_srv_conf); - name = in_addr[a].names.elts; - for (n = 0; n < in_addr[a].names.nelts; n++) { - ngx_log_debug4(NGX_LOG_DEBUG_HTTP, cf->log, 0, - "%s:%d %V %p", - address, in_port[p].port, &name[n].name, - name[n].core_srv_conf); - } - } + iocpcf = ngx_event_get_conf(cf->cycle->conf_ctx, ngx_iocp_module); + if (iocpcf->acceptex_read) { + ls->post_accept_buffer_size = cscf->client_header_buffer_size; } } #endif - return NGX_CONF_OK; -} + ls->backlog = addr->opt.backlog; + ls->rcvbuf = addr->opt.rcvbuf; + ls->sndbuf = addr->opt.sndbuf; +#if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER) + ls->accept_filter = addr->opt.accept_filter; +#endif + +#if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT) + ls->deferred_accept = addr->opt.deferred_accept; +#endif + +#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY) + ls->ipv6only = addr->opt.ipv6only; +#endif + +#if (NGX_HAVE_SETFIB) + ls->setfib = addr->opt.setfib; +#endif + + return ls; +} -/* - * add the server address, the server names and the server core module - * configurations to the port (in_port) - */ static ngx_int_t -ngx_http_add_address(ngx_conf_t *cf, ngx_http_conf_in_port_t *in_port, - ngx_http_listen_t *lscf, ngx_http_core_srv_conf_t *cscf) +ngx_http_add_addrs(ngx_conf_t *cf, ngx_http_port_t *hport, + ngx_http_conf_addr_t *addr) { - ngx_http_conf_in_addr_t *in_addr; + ngx_uint_t i; + ngx_http_in_addr_t *addrs; + struct sockaddr_in *sin; + ngx_http_virtual_names_t *vn; + + hport->addrs = ngx_pcalloc(cf->pool, + hport->naddrs * sizeof(ngx_http_in_addr_t)); + if (hport->addrs == NULL) { + return NGX_ERROR; + } - if (in_port->addrs.elts == NULL) { - if (ngx_array_init(&in_port->addrs, cf->temp_pool, 4, - sizeof(ngx_http_conf_in_addr_t)) - != NGX_OK) + addrs = hport->addrs; + + for (i = 0; i < hport->naddrs; i++) { + + sin = &addr[i].opt.u.sockaddr_in; + addrs[i].addr = sin->sin_addr.s_addr; + addrs[i].conf.default_server = addr[i].default_server; +#if (NGX_HTTP_SSL) + addrs[i].conf.ssl = addr[i].opt.ssl; +#endif + + if (addr[i].hash.buckets == NULL + && (addr[i].wc_head == NULL + || addr[i].wc_head->hash.buckets == NULL) + && (addr[i].wc_tail == NULL + || addr[i].wc_tail->hash.buckets == NULL) +#if (NGX_PCRE) + && addr[i].nregex == 0 +#endif + ) { + continue; + } + + vn = ngx_palloc(cf->pool, sizeof(ngx_http_virtual_names_t)); + if (vn == NULL) { return NGX_ERROR; } - } - in_addr = ngx_array_push(&in_port->addrs); - if (in_addr == NULL) { - return NGX_ERROR; - } + addrs[i].conf.virtual_names = vn; - in_addr->addr = lscf->addr; - in_addr->hash.buckets = NULL; - in_addr->hash.size = 0; - in_addr->wc_head = NULL; - in_addr->wc_tail = NULL; - in_addr->names.elts = NULL; + vn->names.hash = addr[i].hash; + vn->names.wc_head = addr[i].wc_head; + vn->names.wc_tail = addr[i].wc_tail; #if (NGX_PCRE) - in_addr->nregex = 0; - in_addr->regex = NULL; + vn->nregex = addr[i].nregex; + vn->regex = addr[i].regex; #endif - in_addr->core_srv_conf = cscf; - in_addr->default_server = lscf->conf.default_server; - in_addr->bind = lscf->conf.bind; - in_addr->listen_conf = &lscf->conf; - -#if (NGX_DEBUG) - { - u_char text[20]; - ngx_inet_ntop(AF_INET, &in_addr->addr, text, 20); - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, cf->log, 0, "address: %s:%d", - text, in_port->port); } -#endif - return ngx_http_add_names(cf, in_addr, cscf); + return NGX_OK; } -/* - * add the server names and the server core module - * configurations to the address:port (in_addr) - */ +#if (NGX_HAVE_INET6) static ngx_int_t -ngx_http_add_names(ngx_conf_t *cf, ngx_http_conf_in_addr_t *in_addr, - ngx_http_core_srv_conf_t *cscf) +ngx_http_add_addrs6(ngx_conf_t *cf, ngx_http_port_t *hport, + ngx_http_conf_addr_t *addr) { - ngx_uint_t i, n; - ngx_http_server_name_t *server_names, *name; - - if (in_addr->names.elts == NULL) { - if (ngx_array_init(&in_addr->names, cf->temp_pool, 4, - sizeof(ngx_http_server_name_t)) - != NGX_OK) - { - return NGX_ERROR; - } + ngx_uint_t i; + ngx_http_in6_addr_t *addrs6; + struct sockaddr_in6 *sin6; + ngx_http_virtual_names_t *vn; + + hport->addrs = ngx_pcalloc(cf->pool, + hport->naddrs * sizeof(ngx_http_in6_addr_t)); + if (hport->addrs == NULL) { + return NGX_ERROR; } - server_names = cscf->server_names.elts; + addrs6 = hport->addrs; - for (i = 0; i < cscf->server_names.nelts; i++) { + for (i = 0; i < hport->naddrs; i++) { - for (n = 0; n < server_names[i].name.len; n++) { - server_names[i].name.data[n] = - ngx_tolower(server_names[i].name.data[n]); - } + sin6 = &addr[i].opt.u.sockaddr_in6; + addrs6[i].addr6 = sin6->sin6_addr; + addrs6[i].conf.default_server = addr[i].default_server; +#if (NGX_HTTP_SSL) + addrs6[i].conf.ssl = addr[i].opt.ssl; +#endif - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, cf->log, 0, - "name: %V", &server_names[i].name); + if (addr[i].hash.buckets == NULL + && (addr[i].wc_head == NULL + || addr[i].wc_head->hash.buckets == NULL) + && (addr[i].wc_head == NULL + || addr[i].wc_head->hash.buckets == NULL)) + { + continue; + } - name = ngx_array_push(&in_addr->names); - if (name == NULL) { + vn = ngx_palloc(cf->pool, sizeof(ngx_http_virtual_names_t)); + if (vn == NULL) { return NGX_ERROR; } - *name = server_names[i]; + addrs6[i].conf.virtual_names = vn; + + vn->names.hash = addr[i].hash; + vn->names.wc_head = addr[i].wc_head; + vn->names.wc_tail = addr[i].wc_tail; +#if (NGX_PCRE) + vn->nregex = addr[i].nregex; + vn->regex = addr[i].regex; +#endif } return NGX_OK; } +#endif -static char * -ngx_http_merge_locations(ngx_conf_t *cf, ngx_array_t *locations, - void **loc_conf, ngx_http_module_t *module, ngx_uint_t ctx_index) + +char * +ngx_http_types_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { - char *rv; - ngx_uint_t i; - ngx_http_core_loc_conf_t **clcfp; + char *p = conf; - clcfp = locations->elts; + ngx_array_t **types; + ngx_str_t *value, *default_type; + ngx_uint_t i, n, hash; + ngx_hash_key_t *type; - for (i = 0; i < locations->nelts; i++) { - rv = module->merge_loc_conf(cf, loc_conf[ctx_index], - clcfp[i]->loc_conf[ctx_index]); - if (rv != NGX_CONF_OK) { - return rv; + types = (ngx_array_t **) (p + cmd->offset); + + if (*types == (void *) -1) { + return NGX_CONF_OK; + } + + default_type = cmd->post; + + if (*types == NULL) { + *types = ngx_array_create(cf->temp_pool, 1, sizeof(ngx_hash_key_t)); + if (*types == NULL) { + return NGX_CONF_ERROR; } - if (clcfp[i]->locations == NULL) { - continue; + if (default_type) { + type = ngx_array_push(*types); + if (type == NULL) { + return NGX_CONF_ERROR; + } + + type->key = *default_type; + type->key_hash = ngx_hash_key(default_type->data, + default_type->len); + type->value = (void *) 4; } + } - rv = ngx_http_merge_locations(cf, clcfp[i]->locations, - clcfp[i]->loc_conf, module, ctx_index); - if (rv != NGX_CONF_OK) { - return rv; + value = cf->args->elts; + + for (i = 1; i < cf->args->nelts; i++) { + + if (value[i].len == 1 && value[i].data[0] == '*') { + *types = (void *) -1; + return NGX_CONF_OK; + } + + hash = ngx_hash_strlow(value[i].data, value[i].data, value[i].len); + value[i].data[value[i].len] = '\0'; + + type = (*types)->elts; + for (n = 0; n < (*types)->nelts; n++) { + + if (ngx_strcmp(value[i].data, type[n].key.data) == 0) { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "duplicate MIME type \"%V\"", &value[i]); + continue; + } + } + + type = ngx_array_push(*types); + if (type == NULL) { + return NGX_CONF_ERROR; } + + type->key = value[i]; + type->key_hash = hash; + type->value = (void *) 4; } return NGX_CONF_OK; } -static int -ngx_http_cmp_conf_in_addrs(const void *one, const void *two) +char * +ngx_http_merge_types(ngx_conf_t *cf, ngx_array_t **keys, ngx_hash_t *types_hash, + ngx_array_t **prev_keys, ngx_hash_t *prev_types_hash, + ngx_str_t *default_types) { - ngx_http_conf_in_addr_t *first, *second; + ngx_hash_init_t hash; - first = (ngx_http_conf_in_addr_t *) one; - second = (ngx_http_conf_in_addr_t *) two; + if (*keys) { - if (first->addr == INADDR_ANY) { - /* the INADDR_ANY must be the last resort, shift it to the end */ - return 1; - } + if (*keys == (void *) -1) { + return NGX_CONF_OK; + } - if (first->bind && !second->bind) { - /* shift explicit bind()ed addresses to the start */ - return -1; + hash.hash = types_hash; + hash.key = NULL; + hash.max_size = 2048; + hash.bucket_size = 64; + hash.name = "test_types_hash"; + hash.pool = cf->pool; + hash.temp_pool = NULL; + + if (ngx_hash_init(&hash, (*keys)->elts, (*keys)->nelts) != NGX_OK) { + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; } - if (!first->bind && second->bind) { - /* shift explicit bind()ed addresses to the start */ - return 1; + if (prev_types_hash->buckets == NULL) { + + if (*prev_keys == NULL) { + + if (ngx_http_set_default_types(cf, prev_keys, default_types) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + + } else if (*prev_keys == (void *) -1) { + *keys = *prev_keys; + return NGX_CONF_OK; + } + + hash.hash = prev_types_hash; + hash.key = NULL; + hash.max_size = 2048; + hash.bucket_size = 64; + hash.name = "test_types_hash"; + hash.pool = cf->pool; + hash.temp_pool = NULL; + + if (ngx_hash_init(&hash, (*prev_keys)->elts, (*prev_keys)->nelts) + != NGX_OK) + { + return NGX_CONF_ERROR; + } } - /* do not sort by default */ + *types_hash = *prev_types_hash; + + return NGX_CONF_OK; - return 0; } -static int ngx_libc_cdecl -ngx_http_cmp_dns_wildcards(const void *one, const void *two) +ngx_int_t +ngx_http_set_default_types(ngx_conf_t *cf, ngx_array_t **types, + ngx_str_t *default_type) { - ngx_hash_key_t *first, *second; + ngx_hash_key_t *type; - first = (ngx_hash_key_t *) one; - second = (ngx_hash_key_t *) two; + *types = ngx_array_create(cf->temp_pool, 1, sizeof(ngx_hash_key_t)); + if (*types == NULL) { + return NGX_ERROR; + } - return ngx_strcmp(first->key.data, second->key.data); + while (default_type->len) { + + type = ngx_array_push(*types); + if (type == NULL) { + return NGX_ERROR; + } + + type->key = *default_type; + type->key_hash = ngx_hash_key(default_type->data, + default_type->len); + type->value = (void *) 4; + + default_type++; + } + + return NGX_OK; } diff -Nru nginx-0.5.33/src/http/ngx_http_cache.c nginx-0.8.53/src/http/ngx_http_cache.c --- nginx-0.5.33/src/http/ngx_http_cache.c 2006-08-30 10:39:17.000000000 +0000 +++ nginx-0.8.53/src/http/ngx_http_cache.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,576 +0,0 @@ - -/* - * Copyright (C) Igor Sysoev - */ - - -#include -#include -#include - - -#if 0 - -static ngx_http_module_t ngx_http_cache_module_ctx = { - NULL, /* pre conf */ - - 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_cache_module = { - NGX_MODULE, - &ngx_http_cache_module_ctx, /* module context */ - NULL, /* module directives */ - NGX_HTTP_MODULE, /* module type */ - NULL, /* init module */ - NULL /* init process */ -}; - -#endif - - -static ngx_int_t ngx_http_cache_create(ngx_http_request_t *r) -{ - ngx_str_t *key; - - if (!(r->cache = ngx_pcalloc(r->pool, sizeof(ngx_http_cache_t)))) { - return NGX_ERROR; - } - - if (ngx_array_init(&r->cache->key, r->pool, 5, sizeof(ngx_str_t)) - == NGX_ERROR) - { - return NGX_ERROR; - } - - /* preallocate the primary key */ - - if (!(key = ngx_array_push(&r->cache->key))) { - return NGX_ERROR; - } - - key->len = 0; - key->data = NULL; - - /* - * we use offsetof() because sizeof() pads the struct size to the int size - */ - - r->cache->header_size = offsetof(ngx_http_cache_header_t, key); - - r->cache->log = r->connection->log; - r->cache->file.log = r->connection->log; - - return NGX_OK; -} - - -ngx_int_t ngx_http_cache_get(ngx_http_request_t *r, ngx_http_cache_ctx_t *ctx) -{ - ngx_str_t *key; - ngx_http_cache_t *c; - - if (r->cache == NULL) { - if (ngx_http_cache_create(r) == NGX_ERROR) { - return NGX_ABORT; - } - } - - c = r->cache; - key = c->key.elts; - - if (ctx->primary) { - key[0] = ctx->key; - c->header_size += ctx->key.len; - c->key_len += ctx->key.len; - c->buf = ctx->buf; - - } else { - if (key[0].len == 0) { - key[0] = r->uri; - c->header_size += r->uri.len; - c->key_len += ctx->key.len; - } - - if (!(key = ngx_array_push(&r->cache->key))) { - return NGX_ABORT; - } - - c->header_size += ctx->key.len; - c->key_len += ctx->key.len; - } - -#if 0 - - if (ctx->memory) { - ngx_http_memory_cache_get(r, ctx); - } - -#endif - - if (ctx->file) { - return ngx_http_file_cache_get(r, ctx); - } - - return NGX_DECLINED; -} - - -#if 0 - - -ngx_http_cache_t *ngx_http_cache_get(ngx_http_cache_hash_t *hash, - ngx_http_cleanup_t *cleanup, - ngx_str_t *key, uint32_t *crc) -{ - ngx_uint_t i; - ngx_http_cache_t *c; - - *crc = ngx_crc(key->data, key->len); - - c = hash->elts + *crc % hash->hash * hash->nelts; - - if (ngx_mutex_lock(&hash->mutex) == NGX_ERROR) { - return (void *) NGX_ERROR; - } - - for (i = 0; i < hash->nelts; i++) { - if (c[i].crc == *crc - && c[i].key.len == key->len - && ngx_rstrncmp(c[i].key.data, key->data, key->len) == 0) - { -#if 0 - if (c[i].expired) { - ngx_mutex_unlock(&hash->mutex); - return (void *) NGX_AGAIN; - } -#endif - - c[i].refs++; - - if ((!(c[i].notify && (ngx_event_flags & NGX_USE_KQUEUE_EVENT))) - && (ngx_cached_time - c[i].updated >= hash->update)) - { - c[i].expired = 1; - } - - ngx_mutex_unlock(&hash->mutex); - - if (cleanup) { - cleanup->data.cache.hash = hash; - cleanup->data.cache.cache = &c[i]; - cleanup->valid = 1; - cleanup->cache = 1; - } - - return &c[i]; - } - } - - ngx_mutex_unlock(&hash->mutex); - - return NULL; -} - - -ngx_http_cache_t *ngx_http_cache_alloc(ngx_http_cache_hash_t *hash, - ngx_http_cache_t *cache, - ngx_http_cleanup_t *cleanup, - ngx_str_t *key, uint32_t crc, - ngx_str_t *value, ngx_log_t *log) -{ - time_t old; - ngx_uint_t i; - ngx_http_cache_t *c; - - old = ngx_cached_time + 1; - - c = hash->elts + crc % hash->hash * hash->nelts; - - if (ngx_mutex_lock(&hash->mutex) == NGX_ERROR) { - return (void *) NGX_ERROR; - } - - if (cache == NULL) { - - /* allocate a new entry */ - - for (i = 0; i < hash->nelts; i++) { - if (c[i].refs > 0) { - /* a busy entry */ - continue; - } - - if (c[i].key.len == 0) { - /* a free entry is found */ - cache = &c[i]; - break; - } - - /* looking for the oldest cache entry */ - - if (old > c[i].accessed) { - - old = c[i].accessed; - cache = &c[i]; - } - } - - if (cache == NULL) { - ngx_mutex_unlock(&hash->mutex); - return NULL; - } - - ngx_http_cache_free(cache, key, value, log); - - if (cache->key.data == NULL) { - cache->key.data = ngx_alloc(key->len, log); - if (cache->key.data == NULL) { - ngx_http_cache_free(cache, NULL, NULL, log); - ngx_mutex_unlock(&hash->mutex); - return NULL; - } - } - - cache->key.len = key->len; - ngx_memcpy(cache->key.data, key->data, key->len); - - } else if (value) { - ngx_http_cache_free(cache, key, value, log); - } - - if (value) { - if (cache->data.value.data == NULL) { - cache->data.value.data = ngx_alloc(value->len, log); - if (cache->data.value.data == NULL) { - ngx_http_cache_free(cache, NULL, NULL, log); - ngx_mutex_unlock(&hash->mutex); - return NULL; - } - } - - cache->data.value.len = value->len; - ngx_memcpy(cache->data.value.data, value->data, value->len); - } - - cache->crc = crc; - cache->key.len = key->len; - - cache->refs = 1; - cache->count = 0; - - cache->deleted = 0; - cache->expired = 0; - cache->memory = 0; - cache->mmap = 0; - cache->notify = 0; - - if (cleanup) { - cleanup->data.cache.hash = hash; - cleanup->data.cache.cache = cache; - cleanup->valid = 1; - cleanup->cache = 1; - } - - ngx_mutex_unlock(&hash->mutex); - - return cache; -} - - -void ngx_http_cache_free(ngx_http_cache_t *cache, - ngx_str_t *key, ngx_str_t *value, ngx_log_t *log) -{ - if (cache->memory) { - if (cache->data.value.data - && (value == NULL || value->len > cache->data.value.len)) - { - ngx_free(cache->data.value.data); - cache->data.value.data = NULL; - } - } - - /* TODO: mmap */ - - cache->data.value.len = 0; - - if (cache->fd != NGX_INVALID_FILE) { - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, - "http cache close fd: %d", cache->fd); - - if (ngx_close_file(cache->fd) == NGX_FILE_ERROR) { - ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, - ngx_close_file_n " \"%s\" failed", - cache->key.data); - } - - cache->fd = NGX_INVALID_FILE; - } - - if (cache->key.data && (key == NULL || key->len > cache->key.len)) { - ngx_free(cache->key.data); - cache->key.data = NULL; - } - - cache->key.len = 0; - - cache->refs = 0; -} - - -void ngx_http_cache_lock(ngx_http_cache_hash_t *hash, ngx_http_cache_t *cache) -{ - if (ngx_mutex_lock(&hash->mutex) == NGX_ERROR) { - return; - } -} - - -void ngx_http_cache_unlock(ngx_http_cache_hash_t *hash, - ngx_http_cache_t *cache, ngx_log_t *log) -{ - if (ngx_mutex_lock(&hash->mutex) == NGX_ERROR) { - return; - } - - cache->refs--; - - if (cache->refs == 0 && cache->deleted) { - ngx_http_cache_free(cache, NULL, NULL, log); - } - - ngx_mutex_unlock(&hash->mutex); -} - - -#if 0 - -ngx_http_cache_add_file_event(ngx_http_cache_hash_t *hash, - ngx_http_cache_t *cache) -{ - ngx_event_t *ev; - ngx_http_cache_event_ctx_t *ctx; - - ev = &ngx_cycle->read_events[fd]; - ngx_memzero(ev, sizeof(ngx_event_t); - - ev->data = data; - ev->event_handler = ngx_http_cache_invalidate; - - return ngx_add_event(ev, NGX_VNODE_EVENT, 0); -} - - -void ngx_http_cache_invalidate(ngx_event_t *ev) -{ - ngx_http_cache_event_ctx_t *ctx; - - ctx = ev->data; - - ngx_http_cache_lock(&ctx->hash->mutex); - - if (ctx->cache->refs == 0) - ngx_http_cache_free(ctx->cache, NULL, NULL, ctx->log); - - } else { - ctx->cache->deleted = 1; - } - - ngx_http_cache_unlock(&ctx->hash->mutex); -} - -#endif - - -/* TODO: currently fd only */ - -ngx_int_t ngx_http_send_cached(ngx_http_request_t *r) -{ - ngx_int_t rc; - ngx_hunk_t *h; - ngx_chain_t out; - ngx_http_log_ctx_t *ctx; - - ctx = r->connection->log->data; - ctx->action = "sending response to client"; - - r->headers_out.status = NGX_HTTP_OK; - r->headers_out.content_length_n = r->cache->data.size; - r->headers_out.last_modified_time = r->cache->last_modified; - - if (ngx_http_set_content_type(r) != NGX_OK) { - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } - - /* we need to allocate all before the header would be sent */ - - if (!(h = ngx_pcalloc(r->pool, sizeof(ngx_hunk_t)))) { - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } - - if (!(h->file = ngx_pcalloc(r->pool, sizeof(ngx_file_t)))) { - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } - - rc = ngx_http_send_header(r); - - if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) { - return rc; - } - - h->type = r->main ? NGX_HUNK_FILE : NGX_HUNK_FILE|NGX_HUNK_LAST; - - h->file_pos = 0; - h->file_last = r->cache->data.size; - - h->file->fd = r->cache->fd; - h->file->log = r->connection->log; - - out.hunk = h; - out.next = NULL; - - return ngx_http_output_filter(r, &out); -} - - -char *ngx_http_set_cache_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) -{ - char *p = conf; - - ngx_int_t i, j, dup, invalid; - ngx_str_t *value, line; - ngx_http_cache_t *c; - ngx_http_cache_hash_t *ch, **chp; - - chp = (ngx_http_cache_hash_t **) (p + cmd->offset); - if (*chp) { - return "is duplicate"; - } - - if (!(ch = ngx_pcalloc(cf->pool, sizeof(ngx_http_cache_hash_t)))) { - return NGX_CONF_ERROR; - } - *chp = ch; - - dup = 0; - invalid = 0; - - value = cf->args->elts; - - for (i = 1; i < cf->args->nelts; i++) { - - if (value[i].data[1] != '=') { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "invalid value \"%s\"", value[i].data); - return NGX_CONF_ERROR; - } - - switch (value[i].data[0]) { - - case 'h': - if (ch->hash) { - dup = 1; - break; - } - - ch->hash = ngx_atoi(value[i].data + 2, value[i].len - 2); - if (ch->hash == (size_t) NGX_ERROR || ch->hash == 0) { - invalid = 1; - break; - } - - continue; - - case 'n': - if (ch->nelts) { - dup = 1; - break; - } - - ch->nelts = ngx_atoi(value[i].data + 2, value[i].len - 2); - if (ch->nelts == (size_t) NGX_ERROR || ch->nelts == 0) { - invalid = 1; - break; - } - - continue; - - case 'l': - if (ch->life) { - dup = 1; - break; - } - - line.len = value[i].len - 2; - line.data = value[i].data + 2; - - ch->life = ngx_parse_time(&line, 1); - if (ch->life == NGX_ERROR || ch->life == 0) { - invalid = 1; - break; - } - - continue; - - case 'u': - if (ch->update) { - dup = 1; - break; - } - - line.len = value[i].len - 2; - line.data = value[i].data + 2; - - ch->update = ngx_parse_time(&line, 1); - if (ch->update == NGX_ERROR || ch->update == 0) { - invalid = 1; - break; - } - - continue; - - default: - invalid = 1; - } - - if (dup) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "duplicate value \"%s\"", value[i].data); - return NGX_CONF_ERROR; - } - - if (invalid) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "invalid value \"%s\"", value[i].data); - return NGX_CONF_ERROR; - } - } - - ch->elts = ngx_pcalloc(cf->pool, - ch->hash * ch->nelts * sizeof(ngx_http_cache_t)); - if (ch->elts == NULL) { - return NGX_CONF_ERROR; - } - - for (i = 0; i < (ngx_int_t) ch->hash; i++) { - c = ch->elts + i * ch->nelts; - - for (j = 0; j < (ngx_int_t) ch->nelts; j++) { - c[j].fd = NGX_INVALID_FILE; - } - } - - return NGX_CONF_OK; -} - - -#endif diff -Nru nginx-0.5.33/src/http/ngx_http_cache.h nginx-0.8.53/src/http/ngx_http_cache.h --- nginx-0.5.33/src/http/ngx_http_cache.h 2006-08-30 10:39:17.000000000 +0000 +++ nginx-0.8.53/src/http/ngx_http_cache.h 2010-09-02 14:31:47.000000000 +0000 @@ -13,145 +13,134 @@ #include -/* - * The 3 bits allows the 7 uses before the cache entry allocation. - * We can use maximum 7 bits, i.e up to the 127 uses. - */ -#define NGX_HTTP_CACHE_LAZY_ALLOCATION_BITS 3 +#define NGX_HTTP_CACHE_MISS 1 +#define NGX_HTTP_CACHE_BYPASS 2 +#define NGX_HTTP_CACHE_EXPIRED 3 +#define NGX_HTTP_CACHE_STALE 4 +#define NGX_HTTP_CACHE_UPDATING 5 +#define NGX_HTTP_CACHE_HIT 6 +#define NGX_HTTP_CACHE_SCARCE 7 - -typedef struct { - uint32_t crc; - ngx_str_t key; - time_t accessed; - - unsigned refs:20; /* 1048576 references */ - - unsigned count:NGX_HTTP_CACHE_LAZY_ALLOCATION_BITS; - - unsigned deleted:1; - unsigned expired:1; - unsigned memory:1; - unsigned mmap:1; - unsigned notify:1; - - ngx_fd_t fd; -#if (NGX_USE_HTTP_FILE_CACHE_UNIQ) - ngx_file_uniq_t uniq; /* no needed with kqueue */ -#endif - time_t last_modified; - time_t updated; - - union { - off_t size; - ngx_str_t value; - } data; -} ngx_http_cache_entry_t; +#define NGX_HTTP_CACHE_KEY_LEN 16 typedef struct { - time_t expires; - time_t last_modified; - time_t date; - off_t length; - size_t key_len; - char key[1]; -} ngx_http_cache_header_t; + ngx_uint_t status; + time_t valid; +} ngx_http_cache_valid_t; -#define NGX_HTTP_CACHE_HASH 7 -#define NGX_HTTP_CACHE_NELTS 4 - typedef struct { - ngx_http_cache_entry_t *elts; - size_t hash; - size_t nelts; - time_t life; - time_t update; -#if (NGX_THREADS) - ngx_mutex_t mutex; -#endif - ngx_pool_t *pool; -} ngx_http_cache_hash_t; + ngx_rbtree_node_t node; + ngx_queue_t queue; + u_char key[NGX_HTTP_CACHE_KEY_LEN + - sizeof(ngx_rbtree_key_t)]; -typedef struct { - ngx_http_cache_hash_t *hash; - ngx_http_cache_entry_t *cache; - ngx_file_t file; - ngx_array_t key; - uint32_t crc; - u_char md5[16]; - ngx_path_t *path; - ngx_buf_t *buf; - time_t expires; - time_t last_modified; - time_t date; - off_t length; - size_t key_len; - size_t file_start; - ngx_file_uniq_t uniq; - ngx_log_t *log; - - /* STUB */ - ssize_t header_size; - ngx_str_t key0; -} ngx_http_cache_t; + unsigned count:20; + unsigned uses:10; + unsigned valid_msec:10; + unsigned error:10; + unsigned exists:1; + unsigned updating:1; + unsigned deleting:1; + /* 11 unused bits */ + + ngx_file_uniq_t uniq; + time_t expire; + time_t valid_sec; + size_t body_start; + off_t length; +} ngx_http_file_cache_node_t; + + +struct ngx_http_cache_s { + ngx_file_t file; + ngx_array_t keys; + uint32_t crc32; + u_char key[NGX_HTTP_CACHE_KEY_LEN]; + + ngx_file_uniq_t uniq; + time_t valid_sec; + time_t last_modified; + time_t date; + + size_t header_start; + size_t body_start; + off_t length; + + ngx_uint_t min_uses; + ngx_uint_t error; + ngx_uint_t valid_msec; + + ngx_buf_t *buf; + + ngx_http_file_cache_t *file_cache; + ngx_http_file_cache_node_t *node; + + unsigned updated:1; + unsigned updating:1; + unsigned exists:1; + unsigned temp_file:1; +}; typedef struct { - ngx_path_t *path; - ngx_str_t key; - ngx_buf_t *buf; + time_t valid_sec; + time_t last_modified; + time_t date; + uint32_t crc32; + u_short valid_msec; + u_short header_start; + u_short body_start; +} ngx_http_file_cache_header_t; - unsigned file:1; - unsigned memory:1; - unsigned primary:1; -} ngx_http_cache_ctx_t; +typedef struct { + ngx_rbtree_t rbtree; + ngx_rbtree_node_t sentinel; + ngx_queue_t queue; + ngx_atomic_t cold; + ngx_atomic_t loading; + off_t size; +} ngx_http_file_cache_sh_t; -#define NGX_HTTP_CACHE_STALE 1 -#define NGX_HTTP_CACHE_AGED 2 -#define NGX_HTTP_CACHE_THE_SAME 3 - - -ngx_int_t ngx_http_cache_get(ngx_http_request_t *r, ngx_http_cache_ctx_t *ctx); - -ngx_int_t ngx_http_file_cache_get(ngx_http_request_t *r, - ngx_http_cache_ctx_t *ctx); -ngx_int_t ngx_http_file_cache_open(ngx_http_cache_t *c); +struct ngx_http_file_cache_s { + ngx_http_file_cache_sh_t *sh; + ngx_slab_pool_t *shpool; -ngx_int_t ngx_http_cache_cleaner_handler(ngx_gc_t *gc, ngx_str_t *name, - ngx_dir_t *dir); + ngx_path_t *path; + off_t max_size; + size_t bsize; -#if 0 + time_t inactive; -ngx_http_cache_t *ngx_http_cache_get(ngx_http_cache_hash_t *cache, - ngx_http_cleanup_t *cleanup, - ngx_str_t *key, uint32_t *crc); + ngx_msec_t last; + ngx_uint_t files; -ngx_http_cache_t *ngx_http_cache_alloc(ngx_http_cache_hash_t *hash, - ngx_http_cache_t *cache, - ngx_http_cleanup_t *cleanup, - ngx_str_t *key, uint32_t crc, - ngx_str_t *value, ngx_log_t *log); -void ngx_http_cache_free(ngx_http_cache_t *cache, - ngx_str_t *key, ngx_str_t *value, ngx_log_t *log); -void ngx_http_cache_lock(ngx_http_cache_hash_t *hash, ngx_http_cache_t *cache); -void ngx_http_cache_unlock(ngx_http_cache_hash_t *hash, - ngx_http_cache_t *cache, ngx_log_t *log); + ngx_shm_zone_t *shm_zone; +}; -int ngx_http_cache_update_file(ngx_http_request_t *r,ngx_http_cache_ctx_t *ctx, - ngx_str_t *temp_file); -int ngx_http_send_cached(ngx_http_request_t *r); +ngx_int_t ngx_http_file_cache_new(ngx_http_request_t *r); +ngx_int_t ngx_http_file_cache_create(ngx_http_request_t *r); +void ngx_http_file_cache_create_key(ngx_http_request_t *r); +ngx_int_t ngx_http_file_cache_open(ngx_http_request_t *r); +void ngx_http_file_cache_set_header(ngx_http_request_t *r, u_char *buf); +void ngx_http_file_cache_update(ngx_http_request_t *r, ngx_temp_file_t *tf); +ngx_int_t ngx_http_cache_send(ngx_http_request_t *); +void ngx_http_file_cache_free(ngx_http_cache_t *c, ngx_temp_file_t *tf); +time_t ngx_http_file_cache_valid(ngx_array_t *cache_valid, ngx_uint_t status); +char *ngx_http_file_cache_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +char *ngx_http_file_cache_valid_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); -char *ngx_http_set_cache_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); -#endif +extern ngx_str_t ngx_http_cache_status[]; #endif /* _NGX_HTTP_CACHE_H_INCLUDED_ */ diff -Nru nginx-0.5.33/src/http/ngx_http_config.h nginx-0.8.53/src/http/ngx_http_config.h --- nginx-0.5.33/src/http/ngx_http_config.h 2006-08-30 10:39:17.000000000 +0000 +++ nginx-0.8.53/src/http/ngx_http_config.h 2010-08-02 15:28:04.000000000 +0000 @@ -56,10 +56,6 @@ #define ngx_http_get_module_srv_conf(r, module) (r)->srv_conf[module.ctx_index] #define ngx_http_get_module_loc_conf(r, module) (r)->loc_conf[module.ctx_index] -/* - * ngx_http_conf_get_module_srv_conf() and ngx_http_conf_get_module_loc_conf() - * must not be used at the merge phase because cf->ctx points to http{}'s ctx - */ #define ngx_http_conf_get_module_main_conf(cf, module) \ ((ngx_http_conf_ctx_t *) cf->ctx)->main_conf[module.ctx_index] @@ -69,8 +65,10 @@ ((ngx_http_conf_ctx_t *) cf->ctx)->loc_conf[module.ctx_index] #define ngx_http_cycle_get_module_main_conf(cycle, module) \ - ((ngx_http_conf_ctx_t *) \ - cycle->conf_ctx[ngx_http_module.index])->main_conf[module.ctx_index] + (cycle->conf_ctx[ngx_http_module.index] ? \ + ((ngx_http_conf_ctx_t *) cycle->conf_ctx[ngx_http_module.index]) \ + ->main_conf[module.ctx_index]: \ + NULL) #endif /* _NGX_HTTP_CONFIG_H_INCLUDED_ */ diff -Nru nginx-0.5.33/src/http/ngx_http_copy_filter_module.c nginx-0.8.53/src/http/ngx_http_copy_filter_module.c --- nginx-0.5.33/src/http/ngx_http_copy_filter_module.c 2007-07-05 11:42:11.000000000 +0000 +++ nginx-0.8.53/src/http/ngx_http_copy_filter_module.c 2010-10-12 12:18:39.000000000 +0000 @@ -14,6 +14,15 @@ } ngx_http_copy_filter_conf_t; +#if (NGX_HAVE_FILE_AIO) +static void ngx_http_copy_aio_handler(ngx_output_chain_ctx_t *ctx, + ngx_file_t *file); +static void ngx_http_copy_aio_event_handler(ngx_event_t *ev); +#if (NGX_HAVE_AIO_SENDFILE) +static void ngx_http_copy_aio_sendfile_event_handler(ngx_event_t *ev); +#endif +#endif + static void *ngx_http_copy_filter_create_conf(ngx_conf_t *cf); static char *ngx_http_copy_filter_merge_conf(ngx_conf_t *cf, void *parent, void *child); @@ -73,18 +82,17 @@ ngx_int_t rc; ngx_connection_t *c; ngx_output_chain_ctx_t *ctx; + ngx_http_core_loc_conf_t *clcf; ngx_http_copy_filter_conf_t *conf; c = r->connection; ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, - "copy filter: \"%V?%V\"", &r->uri, &r->args); + "http copy filter: \"%V?%V\"", &r->uri, &r->args); ctx = ngx_http_get_module_ctx(r, ngx_http_copy_filter_module); if (ctx == NULL) { - conf = ngx_http_get_module_loc_conf(r, ngx_http_copy_filter_module); - ctx = ngx_pcalloc(r->pool, sizeof(ngx_output_chain_ctx_t)); if (ctx == NULL) { return NGX_ERROR; @@ -92,11 +100,16 @@ ngx_http_set_ctx(r, ctx, ngx_http_copy_filter_module); + conf = ngx_http_get_module_loc_conf(r, ngx_http_copy_filter_module); + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + ctx->sendfile = c->sendfile; ctx->need_in_memory = r->main_filter_need_in_memory || r->filter_need_in_memory; ctx->need_in_temp = r->filter_need_temporary; + ctx->alignment = clcf->directio_alignment; + ctx->pool = r->pool; ctx->bufs = conf->bufs; ctx->tag = (ngx_buf_tag_t) &ngx_http_copy_filter_module; @@ -104,34 +117,144 @@ ctx->output_filter = (ngx_output_chain_filter_pt) ngx_http_next_filter; ctx->filter_ctx = r; - r->request_output = 1; +#if (NGX_HAVE_FILE_AIO) + if (ngx_file_aio) { + if (clcf->aio) { + ctx->aio_handler = ngx_http_copy_aio_handler; + } +#if (NGX_HAVE_AIO_SENDFILE) + c->aio_sendfile = (clcf->aio == NGX_HTTP_AIO_SENDFILE); +#endif + } +#endif + + if (in && in->buf && ngx_buf_size(in->buf)) { + r->request_output = 1; + } } - rc = ngx_output_chain(ctx, in); +#if (NGX_HAVE_FILE_AIO) + ctx->aio = r->aio; +#endif - if (!c->destroyed) { + for ( ;; ) { + rc = ngx_output_chain(ctx, in); if (ctx->in == NULL) { r->buffered &= ~NGX_HTTP_COPY_BUFFERED; + } else { r->buffered |= NGX_HTTP_COPY_BUFFERED; } - if (r != r->main) { - r->out = ctx->in; - } + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http copy filter: %i \"%V?%V\"", rc, &r->uri, &r->args); + +#if (NGX_HAVE_FILE_AIO && NGX_HAVE_AIO_SENDFILE) -#if (NGX_DEBUG) - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "copy filter: %i \"%V?%V\"", rc, &r->uri, &r->args); + if (c->busy_sendfile) { + ssize_t n; + off_t offset; + ngx_file_t *file; + ngx_http_ephemeral_t *e; + + file = c->busy_sendfile->file; + offset = c->busy_sendfile->file_pos; + + if (file->aio) { + c->aio_sendfile = (offset != file->aio->last_offset); + file->aio->last_offset = offset; + + if (c->aio_sendfile == 0) { + ngx_log_error(NGX_LOG_ALERT, c->log, 0, + "sendfile(%V) returned busy again", + &file->name); + } + } + + c->busy_sendfile = NULL; + e = (ngx_http_ephemeral_t *) &r->uri_start; + + n = ngx_file_aio_read(file, &e->aio_preload, 1, offset, r->pool); + + if (n > 0) { + in = NULL; + continue; + } + + rc = n; + + if (file->aio) { + file->aio->data = r; + file->aio->handler = ngx_http_copy_aio_sendfile_event_handler; + + r->main->blocked++; + r->aio = 1; + } + } #endif + return rc; } +} + - return rc; +#if (NGX_HAVE_FILE_AIO) + +static void +ngx_http_copy_aio_handler(ngx_output_chain_ctx_t *ctx, ngx_file_t *file) +{ + ngx_http_request_t *r; + + r = ctx->filter_ctx; + + file->aio->data = r; + file->aio->handler = ngx_http_copy_aio_event_handler; + + r->main->blocked++; + r->aio = 1; + ctx->aio = 1; } +static void +ngx_http_copy_aio_event_handler(ngx_event_t *ev) +{ + ngx_event_aio_t *aio; + ngx_http_request_t *r; + + aio = ev->data; + r = aio->data; + + r->main->blocked--; + r->aio = 0; + + r->connection->write->handler(r->connection->write); +} + + +#if (NGX_HAVE_AIO_SENDFILE) + +static void +ngx_http_copy_aio_sendfile_event_handler(ngx_event_t *ev) +{ + ngx_event_aio_t *aio; + ngx_http_request_t *r; + + aio = ev->data; + r = aio->data; + + r->main->blocked--; + r->aio = 0; + ev->complete = 0; + + r->connection->write->handler(r->connection->write); +} + +#endif +#endif + + static void * ngx_http_copy_filter_create_conf(ngx_conf_t *cf) { diff -Nru nginx-0.5.33/src/http/ngx_http_core_module.c nginx-0.8.53/src/http/ngx_http_core_module.c --- nginx-0.5.33/src/http/ngx_http_core_module.c 2007-11-07 13:46:29.000000000 +0000 +++ nginx-0.8.53/src/http/ngx_http_core_module.c 2010-10-18 10:14:00.000000000 +0000 @@ -6,9 +6,7 @@ #include #include -#include #include -#include typedef struct { @@ -17,19 +15,14 @@ } ngx_http_method_name_t; -#define NGX_HTTP_LOCATION_EXACT 1 -#define NGX_HTTP_LOCATION_AUTO_REDIRECT 2 -#define NGX_HTTP_LOCATION_NOREGEX 3 -#define NGX_HTTP_LOCATION_REGEX 4 - - #define NGX_HTTP_REQUEST_BODY_FILE_OFF 0 #define NGX_HTTP_REQUEST_BODY_FILE_ON 1 #define NGX_HTTP_REQUEST_BODY_FILE_CLEAN 2 -static ngx_int_t ngx_http_core_find_location(ngx_http_request_t *r, - ngx_array_t *locations, ngx_uint_t regex_start, size_t len); +static ngx_int_t ngx_http_core_find_location(ngx_http_request_t *r); +static ngx_int_t ngx_http_core_find_static_location(ngx_http_request_t *r, + ngx_http_location_tree_node_t *node); static ngx_int_t ngx_http_core_preconfiguration(ngx_conf_t *cf); static void *ngx_http_core_create_main_conf(ngx_conf_t *cf); @@ -45,7 +38,8 @@ void *dummy); static char *ngx_http_core_location(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy); -static int ngx_http_core_cmp_locations(const void *first, const void *second); +static ngx_int_t ngx_http_core_regex_location(ngx_conf_t *cf, + ngx_http_core_loc_conf_t *clcf, ngx_str_t *regex, ngx_uint_t caseless); static char *ngx_http_core_types(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); @@ -59,14 +53,26 @@ static char *ngx_http_core_root(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_http_core_limit_except(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static char *ngx_http_core_directio(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); static char *ngx_http_core_error_page(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static char *ngx_http_core_try_files(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static char *ngx_http_core_open_file_cache(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); static char *ngx_http_core_error_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_http_core_keepalive(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_http_core_internal(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static char *ngx_http_core_resolver(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +#if (NGX_HTTP_GZIP) +static char *ngx_http_gzip_disable(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +#endif static char *ngx_http_core_lowat_check(ngx_conf_t *cf, void *post, void *data); static char *ngx_http_core_pool_size(ngx_conf_t *cf, void *post, void *data); @@ -77,8 +83,16 @@ static ngx_conf_post_handler_pt ngx_http_core_pool_size_p = ngx_http_core_pool_size; -static ngx_conf_deprecated_t ngx_conf_deprecated_optimize_host_names = { - ngx_conf_deprecated, "optimize_host_names", "optimize_server_names" +static ngx_conf_deprecated_t ngx_conf_deprecated_optimize_server_names = { + ngx_conf_deprecated, "optimize_server_names", "server_name_in_redirect" +}; + +static ngx_conf_deprecated_t ngx_conf_deprecated_open_file_cache_retest = { + ngx_conf_deprecated, "open_file_cache_retest", "open_file_cache_valid" +}; + +static ngx_conf_deprecated_t ngx_conf_deprecated_satisfy_any = { + ngx_conf_deprecated, "satisfy_any", "satisfy" }; @@ -90,6 +104,70 @@ }; +#if (NGX_HAVE_FILE_AIO) + +static ngx_conf_enum_t ngx_http_core_aio[] = { + { ngx_string("off"), NGX_HTTP_AIO_OFF }, + { ngx_string("on"), NGX_HTTP_AIO_ON }, +#if (NGX_HAVE_AIO_SENDFILE) + { ngx_string("sendfile"), NGX_HTTP_AIO_SENDFILE }, +#endif + { ngx_null_string, 0 } +}; + +#endif + + +static ngx_conf_enum_t ngx_http_core_satisfy[] = { + { ngx_string("all"), NGX_HTTP_SATISFY_ALL }, + { ngx_string("any"), NGX_HTTP_SATISFY_ANY }, + { ngx_null_string, 0 } +}; + + +static ngx_conf_enum_t ngx_http_core_if_modified_since[] = { + { ngx_string("off"), NGX_HTTP_IMS_OFF }, + { ngx_string("exact"), NGX_HTTP_IMS_EXACT }, + { ngx_string("before"), NGX_HTTP_IMS_BEFORE }, + { ngx_null_string, 0 } +}; + + +static ngx_path_init_t ngx_http_client_temp_path = { + ngx_string(NGX_HTTP_CLIENT_TEMP_PATH), { 0, 0, 0 } +}; + + +#if (NGX_HTTP_GZIP) + +static ngx_conf_enum_t ngx_http_gzip_http_version[] = { + { ngx_string("1.0"), NGX_HTTP_VERSION_10 }, + { ngx_string("1.1"), NGX_HTTP_VERSION_11 }, + { ngx_null_string, 0 } +}; + + +static ngx_conf_bitmask_t ngx_http_gzip_proxied_mask[] = { + { ngx_string("off"), NGX_HTTP_GZIP_PROXIED_OFF }, + { ngx_string("expired"), NGX_HTTP_GZIP_PROXIED_EXPIRED }, + { ngx_string("no-cache"), NGX_HTTP_GZIP_PROXIED_NO_CACHE }, + { ngx_string("no-store"), NGX_HTTP_GZIP_PROXIED_NO_STORE }, + { ngx_string("private"), NGX_HTTP_GZIP_PROXIED_PRIVATE }, + { ngx_string("no_last_modified"), NGX_HTTP_GZIP_PROXIED_NO_LM }, + { ngx_string("no_etag"), NGX_HTTP_GZIP_PROXIED_NO_ETAG }, + { ngx_string("auth"), NGX_HTTP_GZIP_PROXIED_AUTH }, + { ngx_string("any"), NGX_HTTP_GZIP_PROXIED_ANY }, + { ngx_null_string, 0 } +}; + + +static ngx_str_t ngx_http_gzip_no_cache = ngx_string("no-cache"); +static ngx_str_t ngx_http_gzip_no_store = ngx_string("no-store"); +static ngx_str_t ngx_http_gzip_private = ngx_string("private"); + +#endif + + static ngx_command_t ngx_http_core_commands[] = { { ngx_string("variables_hash_max_size"), @@ -165,22 +243,29 @@ { ngx_string("optimize_server_names"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG, ngx_conf_set_flag_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_core_loc_conf_t, server_name_in_redirect), + &ngx_conf_deprecated_optimize_server_names }, + + { ngx_string("ignore_invalid_headers"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, NGX_HTTP_SRV_CONF_OFFSET, - offsetof(ngx_http_core_srv_conf_t, optimize_server_names), + offsetof(ngx_http_core_srv_conf_t, ignore_invalid_headers), NULL }, - { ngx_string("optimize_host_names"), + { ngx_string("merge_slashes"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG, ngx_conf_set_flag_slot, NGX_HTTP_SRV_CONF_OFFSET, - offsetof(ngx_http_core_srv_conf_t, optimize_server_names), - &ngx_conf_deprecated_optimize_host_names }, + offsetof(ngx_http_core_srv_conf_t, merge_slashes), + NULL }, - { ngx_string("ignore_invalid_headers"), + { ngx_string("underscores_in_headers"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG, ngx_conf_set_flag_slot, NGX_HTTP_SRV_CONF_OFFSET, - offsetof(ngx_http_core_srv_conf_t, ignore_invalid_headers), + offsetof(ngx_http_core_srv_conf_t, underscores_in_headers), NULL }, { ngx_string("location"), @@ -281,15 +366,22 @@ ngx_conf_set_path_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_core_loc_conf_t, client_body_temp_path), - (void *) ngx_garbage_collector_temp_handler }, + NULL }, { ngx_string("client_body_in_file_only"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, + 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_core_loc_conf_t, client_body_in_file_only), &ngx_http_core_request_body_in_file }, + { ngx_string("client_body_in_single_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_core_loc_conf_t, client_body_in_single_buffer), + NULL }, + { ngx_string("sendfile"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF |NGX_CONF_TAKE1, @@ -305,6 +397,38 @@ offsetof(ngx_http_core_loc_conf_t, sendfile_max_chunk), NULL }, +#if (NGX_HAVE_FILE_AIO) + + { ngx_string("aio"), + 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_core_loc_conf_t, aio), + &ngx_http_core_aio }, + +#endif + + { ngx_string("read_ahead"), + 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_core_loc_conf_t, read_ahead), + NULL }, + + { ngx_string("directio"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_http_core_directio, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("directio_alignment"), + 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_core_loc_conf_t, directio_alignment), + NULL }, + { ngx_string("tcp_nopush"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, ngx_conf_set_flag_slot, @@ -348,6 +472,14 @@ offsetof(ngx_http_core_loc_conf_t, limit_rate), NULL }, + { ngx_string("limit_rate_after"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF + |NGX_CONF_TAKE1, + ngx_conf_set_size_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_core_loc_conf_t, limit_rate_after), + NULL }, + { ngx_string("keepalive_timeout"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12, ngx_http_core_keepalive, @@ -355,12 +487,26 @@ 0, NULL }, + { ngx_string("keepalive_requests"), + 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_core_loc_conf_t, keepalive_requests), + NULL }, + + { ngx_string("satisfy"), + 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_core_loc_conf_t, satisfy), + &ngx_http_core_satisfy }, + { ngx_string("satisfy_any"), 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_core_loc_conf_t, satisfy_any), - NULL }, + offsetof(ngx_http_core_loc_conf_t, satisfy), + &ngx_conf_deprecated_satisfy_any }, { ngx_string("internal"), NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS, @@ -390,6 +536,13 @@ offsetof(ngx_http_core_loc_conf_t, reset_timedout_connection), NULL }, + { ngx_string("server_name_in_redirect"), + 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_core_loc_conf_t, server_name_in_redirect), + NULL }, + { ngx_string("port_in_redirect"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, ngx_conf_set_flag_slot, @@ -418,6 +571,13 @@ offsetof(ngx_http_core_loc_conf_t, log_not_found), NULL }, + { ngx_string("log_subrequest"), + 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_core_loc_conf_t, log_subrequest), + NULL }, + { ngx_string("recursive_error_pages"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, ngx_conf_set_flag_slot, @@ -425,6 +585,27 @@ offsetof(ngx_http_core_loc_conf_t, recursive_error_pages), NULL }, + { ngx_string("server_tokens"), + 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_core_loc_conf_t, server_tokens), + NULL }, + + { ngx_string("if_modified_since"), + 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_core_loc_conf_t, if_modified_since), + &ngx_http_core_if_modified_since }, + + { ngx_string("chunked_transfer_encoding"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_conf_set_flag_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_core_loc_conf_t, chunked_transfer_encoding), + NULL }, + { ngx_string("error_page"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF |NGX_CONF_2MORE, @@ -433,6 +614,13 @@ 0, NULL }, + { ngx_string("try_files"), + NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_2MORE, + ngx_http_core_try_files, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + NULL }, + { ngx_string("post_action"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF |NGX_CONF_TAKE1, @@ -448,13 +636,90 @@ 0, NULL }, -#if (NGX_HTTP_CACHE) - { ngx_string("open_file_cache"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE4, - ngx_http_set_cache_slot, + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12, + ngx_http_core_open_file_cache, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_core_loc_conf_t, open_file_cache), + NULL }, + + { ngx_string("open_file_cache_valid"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_conf_set_sec_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_core_loc_conf_t, open_file_cache_valid), + NULL }, + + { ngx_string("open_file_cache_retest"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_conf_set_sec_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_core_loc_conf_t, open_file_cache_valid), + &ngx_conf_deprecated_open_file_cache_retest }, + + { ngx_string("open_file_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_core_loc_conf_t, open_file_cache_min_uses), + NULL }, + + { ngx_string("open_file_cache_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_core_loc_conf_t, open_file_cache_errors), + NULL }, + + { ngx_string("open_file_cache_events"), + 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_core_loc_conf_t, open_file_cache_events), + NULL }, + + { ngx_string("resolver"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_http_core_resolver, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("resolver_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_core_loc_conf_t, resolver_timeout), + NULL }, + +#if (NGX_HTTP_GZIP) + + { ngx_string("gzip_vary"), + 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_core_loc_conf_t, gzip_vary), + NULL }, + + { ngx_string("gzip_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_core_loc_conf_t, gzip_http_version), + &ngx_http_gzip_http_version }, + + { ngx_string("gzip_proxied"), + 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_core_loc_conf_t, open_files), + offsetof(ngx_http_core_loc_conf_t, gzip_proxied), + &ngx_http_gzip_proxied_mask }, + + { ngx_string("gzip_disable"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE, + ngx_http_gzip_disable, + NGX_HTTP_LOC_CONF_OFFSET, + 0, NULL }, #endif @@ -494,6 +759,9 @@ }; +ngx_str_t ngx_http_core_get_method = { 3, (u_char *) "GET " }; + + void ngx_http_handler(ngx_http_request_t *r) { @@ -522,14 +790,24 @@ break; } - if (r->keepalive && r->headers_in.msie && r->method == NGX_HTTP_POST) { + if (r->keepalive) { - /* - * MSIE may wait for some time if an response for - * a POST request was sent over a keepalive connection - */ + if (r->headers_in.msie6) { + if (r->method == NGX_HTTP_POST) { + /* + * MSIE may wait for some time if an response for + * a POST request was sent over a keepalive connection + */ + r->keepalive = 0; + } - r->keepalive = 0; + } else if (r->headers_in.safari) { + /* + * Safari may send a POST request to a closed keepalive + * connection and stalls for some time + */ + r->keepalive = 0; + } } if (r->headers_in.content_length_n > 0) { @@ -546,11 +824,12 @@ r->phase_handler = cmcf->phase_engine.server_rewrite_index; } - if (r->unparsed_uri.len) { - r->valid_unparsed_uri = 1; - } - r->valid_location = 1; +#if (NGX_HTTP_GZIP) + r->gzip_tested = 0; + r->gzip_ok = 0; + r->gzip_vary = 0; +#endif r->write_event_handler = ngx_http_core_run_phases; ngx_http_core_run_phases(r); @@ -586,7 +865,7 @@ /* * generic phase checker, - * used by the post read, server rewrite, rewrite, and pre-access phases + * used by the post read and pre-access phases */ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, @@ -617,6 +896,29 @@ ngx_int_t +ngx_http_core_rewrite_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph) +{ + ngx_int_t rc; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "rewrite phase: %ui", r->phase_handler); + + rc = ph->handler(r); + + if (rc == NGX_DECLINED) { + r->phase_handler++; + return NGX_AGAIN; + } + + /* rc == NGX_OK || rc == NGX_ERROR || rc == NGX_HTTP_... */ + + ngx_http_finalize_request(r, rc); + + return NGX_OK; +} + + +ngx_int_t ngx_http_core_find_config_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph) { @@ -624,16 +926,13 @@ size_t len; ngx_int_t rc; ngx_http_core_loc_conf_t *clcf; - ngx_http_core_srv_conf_t *cscf; r->content_handler = NULL; r->uri_changed = 0; - cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); - - rc = ngx_http_core_find_location(r, &cscf->locations, cscf->regex_start, 0); + rc = ngx_http_core_find_location(r); - if (rc == NGX_HTTP_INTERNAL_SERVER_ERROR) { + if (rc == NGX_ERROR) { ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); return NGX_OK; } @@ -662,15 +961,15 @@ && clcf->client_max_body_size < r->headers_in.content_length_n) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "client intented to send too large body: %O bytes", + "client intended to send too large body: %O bytes", r->headers_in.content_length_n); + (void) ngx_http_discard_request_body(r); ngx_http_finalize_request(r, NGX_HTTP_REQUEST_ENTITY_TOO_LARGE); return NGX_OK; } - - if (rc == NGX_HTTP_LOCATION_AUTO_REDIRECT) { + if (rc == NGX_DONE) { r->headers_out.location = ngx_list_push(&r->headers_out.headers); if (r->headers_out.location == NULL) { ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); @@ -687,7 +986,7 @@ } else { len = clcf->name.len + 1 + r->args.len; - p = ngx_palloc(r->pool, len); + p = ngx_pnalloc(r->pool, len); if (p == NULL) { ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); @@ -715,6 +1014,8 @@ ngx_http_core_post_rewrite_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph) { + ngx_http_core_srv_conf_t *cscf; + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "post rewrite phase: %ui", r->phase_handler); @@ -746,6 +1047,9 @@ r->phase_handler = ph->next; + cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); + r->loc_conf = cscf->ctx->loc_conf; + return NGX_AGAIN; } @@ -777,7 +1081,7 @@ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); - if (clcf->satisfy_any == 0) { + if (clcf->satisfy == NGX_HTTP_SATISFY_ALL) { if (rc == NGX_OK) { r->phase_handler++; @@ -835,53 +1139,244 @@ ngx_int_t -ngx_http_core_content_phase(ngx_http_request_t *r, +ngx_http_core_try_files_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph) { - size_t root; - ngx_int_t rc; - ngx_str_t path; - - if (r->content_handler) { - r->write_event_handler = ngx_http_request_empty_handler; - ngx_http_finalize_request(r, r->content_handler(r)); - return NGX_OK; - } + size_t len, root, alias, reserve, allocated; + u_char *p, *name; + ngx_str_t path, args; + ngx_uint_t test_dir; + ngx_http_try_file_t *tf; + 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_script_len_code_pt lcode; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "content phase: %ui", r->phase_handler); + "try files phase: %ui", r->phase_handler); - rc = ph->handler(r); + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); - if (rc == NGX_DONE) { - return NGX_OK; + if (clcf->try_files == NULL) { + r->phase_handler++; + return NGX_AGAIN; } - if (rc != NGX_DECLINED) { - ngx_http_finalize_request(r, rc); - return NGX_OK; - } + allocated = 0; + root = 0; + name = NULL; + /* suppress MSVC warning */ + path.data = NULL; - /* rc == NGX_DECLINED */ + tf = clcf->try_files; - ph++; + alias = clcf->alias; - if (ph->checker) { - r->phase_handler++; - return NGX_AGAIN; - } + for ( ;; ) { - /* no content handler was found */ + if (tf->lengths) { + ngx_memzero(&e, sizeof(ngx_http_script_engine_t)); - if (r->uri.data[r->uri.len - 1] == '/' && !r->zero_in_uri) { + e.ip = tf->lengths->elts; + e.request = r; - if (ngx_http_map_uri_to_path(r, &path, &root, 0) != NULL) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "directory index of \"%s\" is forbidden", path.data); - } + /* 1 is for terminating '\0' as in static names */ + len = 1; - ngx_http_finalize_request(r, NGX_HTTP_FORBIDDEN); - return NGX_OK; + while (*(uintptr_t *) e.ip) { + lcode = *(ngx_http_script_len_code_pt *) e.ip; + len += lcode(&e); + } + + } else { + len = tf->name.len; + } + + /* 16 bytes are preallocation */ + reserve = ngx_abs((ssize_t) (len - r->uri.len)) + alias + 16; + + if (reserve > allocated) { + + /* we just need to allocate path and to copy a root */ + + if (ngx_http_map_uri_to_path(r, &path, &root, reserve) == NULL) { + ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return NGX_OK; + } + + name = path.data + root; + allocated = path.len - root - (r->uri.len - alias); + } + + if (tf->values == NULL) { + + /* tf->name.len includes the terminating '\0' */ + + ngx_memcpy(name, tf->name.data, tf->name.len); + + path.len = (name + tf->name.len - 1) - path.data; + + } else { + e.ip = tf->values->elts; + e.pos = name; + e.flushed = 1; + + while (*(uintptr_t *) e.ip) { + code = *(ngx_http_script_code_pt *) e.ip; + code((ngx_http_script_engine_t *) &e); + } + + path.len = e.pos - path.data; + + *e.pos = '\0'; + + if (alias && ngx_strncmp(name, clcf->name.data, alias) == 0) { + ngx_memcpy(name, name + alias, len - alias); + path.len -= alias; + } + } + + test_dir = tf->test_dir; + + tf++; + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "try to use %s: \"%s\" \"%s\"", + test_dir ? "dir" : "file", name, path.data); + + if (tf->lengths == NULL && tf->name.len == 0) { + + if (tf->code) { + ngx_http_finalize_request(r, tf->code); + return NGX_OK; + } + + path.len -= root; + path.data += root; + + if (path.data[0] == '@') { + (void) ngx_http_named_location(r, &path); + + } else { + ngx_http_split_args(r, &path, &args); + + (void) ngx_http_internal_redirect(r, &path, &args); + } + + ngx_http_finalize_request(r, NGX_DONE); + return NGX_OK; + } + + ngx_memzero(&of, sizeof(ngx_open_file_info_t)); + + 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_open_cached_file(clcf->open_file_cache, &path, &of, r->pool) + != NGX_OK) + { + 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, path.data); + } + + continue; + } + + if (of.is_dir && !test_dir) { + continue; + } + + path.len -= root; + path.data += root; + + if (!alias) { + r->uri = path; + +#if (NGX_PCRE) + } else if (clcf->regex) { + if (!test_dir) { + r->uri = path; + r->add_uri_to_alias = 1; + } +#endif + } else { + r->uri.len = alias + path.len; + r->uri.data = ngx_pnalloc(r->pool, r->uri.len); + if (r->uri.data == NULL) { + ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return NGX_OK; + } + + p = ngx_copy(r->uri.data, clcf->name.data, alias); + ngx_memcpy(p, name, path.len); + } + + ngx_http_set_exten(r); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "try file uri: \"%V\"", &r->uri); + + r->phase_handler++; + return NGX_AGAIN; + } + + /* not reached */ +} + + +ngx_int_t +ngx_http_core_content_phase(ngx_http_request_t *r, + ngx_http_phase_handler_t *ph) +{ + size_t root; + ngx_int_t rc; + ngx_str_t path; + + if (r->content_handler) { + r->write_event_handler = ngx_http_request_empty_handler; + ngx_http_finalize_request(r, r->content_handler(r)); + return NGX_OK; + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "content phase: %ui", r->phase_handler); + + rc = ph->handler(r); + + if (rc != NGX_DECLINED) { + ngx_http_finalize_request(r, rc); + return NGX_OK; + } + + /* rc == NGX_DECLINED */ + + ph++; + + if (ph->checker) { + r->phase_handler++; + return NGX_AGAIN; + } + + /* no content handler was found */ + + if (r->uri.data[r->uri.len - 1] == '/') { + + if (ngx_http_map_uri_to_path(r, &path, &root, 0) != NULL) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "directory index of \"%s\" is forbidden", path.data); + } + + ngx_http_finalize_request(r, NGX_HTTP_FORBIDDEN); + return NGX_OK; } ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "no handler found"); @@ -904,10 +1399,10 @@ } if (r == r->main) { - r->connection->log->file = clcf->err_log->file; + r->connection->log->file = clcf->error_log->file; if (!(r->connection->log->log_level & NGX_LOG_DEBUG_CONNECTION)) { - r->connection->log->log_level = clcf->err_log->log_level; + r->connection->log->log_level = clcf->error_log->log_level; } } @@ -929,8 +1424,15 @@ r->request_body_file_log_level = NGX_LOG_WARN; } - if (r->keepalive && clcf->keepalive_timeout == 0) { - r->keepalive = 0; + r->request_body_in_single_buf = clcf->client_body_in_single_buffer; + + if (r->keepalive) { + if (clcf->keepalive_timeout == 0) { + r->keepalive = 0; + + } else if (r->connection->requests >= clcf->keepalive_requests) { + r->keepalive = 0; + } } if (!clcf->tcp_nopush) { @@ -948,151 +1450,216 @@ } +/* + * NGX_OK - exact or regex match + * NGX_DONE - auto redirect + * NGX_AGAIN - inclusive match + * NGX_ERROR - regex error + * NGX_DECLINED - no match + */ + static ngx_int_t -ngx_http_core_find_location(ngx_http_request_t *r, - ngx_array_t *locations, ngx_uint_t regex_start, size_t len) +ngx_http_core_find_location(ngx_http_request_t *r) { - ngx_int_t n, rc; - ngx_uint_t i, found; - ngx_http_core_loc_conf_t *clcf, **clcfp; + ngx_int_t rc; + ngx_http_core_loc_conf_t *pclcf; #if (NGX_PCRE) + ngx_int_t n; ngx_uint_t noregex; + ngx_http_core_loc_conf_t *clcf, **clcfp; + + noregex = 0; #endif - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "find location for \"%V\"", &r->uri); + pclcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + rc = ngx_http_core_find_static_location(r, pclcf->static_locations); + + if (rc == NGX_AGAIN) { - found = 0; #if (NGX_PCRE) - noregex = 0; + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + noregex = clcf->noregex; #endif - clcfp = locations->elts; - for (i = 0; i < locations->nelts; i++) { + /* look up nested locations */ + + rc = ngx_http_core_find_location(r); + } + + if (rc == NGX_OK || rc == NGX_DONE) { + return rc; + } + + /* rc == NGX_DECLINED or rc == NGX_AGAIN in nested location */ - if (clcfp[i]->noname #if (NGX_PCRE) - || clcfp[i]->regex + + if (noregex == 0 && pclcf->regex_locations) { + + for (clcfp = pclcf->regex_locations; *clcfp; clcfp++) { + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "test location: ~ \"%V\"", &(*clcfp)->name); + + n = ngx_http_regex_exec(r, (*clcfp)->regex, &r->uri); + + if (n == NGX_OK) { + r->loc_conf = (*clcfp)->loc_conf; + + /* look up nested locations */ + + rc = ngx_http_core_find_location(r); + + return (rc == NGX_ERROR) ? rc : NGX_OK; + } + + if (n == NGX_DECLINED) { + continue; + } + + return NGX_ERROR; + } + } #endif - || clcfp[i]->named) - { - break; + + return rc; +} + + +/* + * NGX_OK - exact match + * NGX_DONE - auto redirect + * NGX_AGAIN - inclusive match + * NGX_DECLINED - no match + */ + +static ngx_int_t +ngx_http_core_find_static_location(ngx_http_request_t *r, + ngx_http_location_tree_node_t *node) +{ + u_char *uri; + size_t len, n; + ngx_int_t rc, rv; + + len = r->uri.len; + uri = r->uri.data; + + rv = NGX_DECLINED; + + for ( ;; ) { + + if (node == NULL) { + return rv; } ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "find location: %s\"%V\"", - clcfp[i]->exact_match ? "= " : "", &clcfp[i]->name); + "test location: \"%*s\"", node->len, node->name); - if (clcfp[i]->auto_redirect - && r->uri.len == clcfp[i]->name.len - 1 - && ngx_strncmp(r->uri.data, clcfp[i]->name.data, - clcfp[i]->name.len - 1) - == 0) - { - /* the locations are lexicographically sorted */ + n = (len <= (size_t) node->len) ? len : node->len; - r->loc_conf = clcfp[i]->loc_conf; + rc = ngx_filename_cmp(uri, node->name, n); - return NGX_HTTP_LOCATION_AUTO_REDIRECT; - } + if (rc != 0) { + node = (rc < 0) ? node->left : node->right; - if (r->uri.len < clcfp[i]->name.len) { continue; } - n = ngx_strncmp(r->uri.data, clcfp[i]->name.data, clcfp[i]->name.len); + if (len > (size_t) node->len) { - if (n < 0) { - /* the locations are lexicographically sorted */ - break; - } + if (node->inclusive) { - if (n == 0) { - if (clcfp[i]->exact_match) { + r->loc_conf = node->inclusive->loc_conf; + rv = NGX_AGAIN; - if (r->uri.len == clcfp[i]->name.len) { - r->loc_conf = clcfp[i]->loc_conf; - return NGX_HTTP_LOCATION_EXACT; - } + node = node->tree; + uri += n; + len -= n; continue; } - if (len > clcfp[i]->name.len) { - /* the previous match is longer */ - break; - } + /* exact only */ - found = 1; + node = node->right; - r->loc_conf = clcfp[i]->loc_conf; -#if (NGX_PCRE) - noregex = clcfp[i]->noregex; -#endif + continue; } - } - if (found) { - clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + if (len == (size_t) node->len) { - if (clcf->locations) { - rc = ngx_http_core_find_location(r, clcf->locations, - clcf->regex_start, len); + if (node->exact) { + r->loc_conf = node->exact->loc_conf; + return NGX_OK; - if (rc != NGX_OK) { - return rc; + } else { + r->loc_conf = node->inclusive->loc_conf; + return NGX_AGAIN; } } - } -#if (NGX_PCRE) + /* len < node->len */ + + if (len + 1 == (size_t) node->len && node->auto_redirect) { + + r->loc_conf = (node->exact) ? node->exact->loc_conf: + node->inclusive->loc_conf; + rv = NGX_DONE; + } - if (noregex) { - return NGX_HTTP_LOCATION_NOREGEX; + node = node->left; } +} - /* regex matches */ - for (i = regex_start; i < locations->nelts; i++) { +void * +ngx_http_test_content_type(ngx_http_request_t *r, ngx_hash_t *types_hash) +{ + u_char c, *lowcase; + size_t len; + ngx_uint_t i, hash; - if (!clcfp[i]->regex) { - break; - } + if (types_hash->size == 0) { + return (void *) 4; + } - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "find location: ~ \"%V\"", &clcfp[i]->name); + if (r->headers_out.content_type.len == 0) { + return NULL; + } - n = ngx_regex_exec(clcfp[i]->regex, &r->uri, NULL, 0); + len = r->headers_out.content_type_len; - if (n == NGX_REGEX_NO_MATCHED) { - continue; - } + if (r->headers_out.content_type_lowcase == NULL) { - if (n < 0) { - ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, - ngx_regex_exec_n - " failed: %d on \"%V\" using \"%V\"", - n, &r->uri, &clcfp[i]->name); - return NGX_HTTP_INTERNAL_SERVER_ERROR; + lowcase = ngx_pnalloc(r->pool, len); + if (lowcase == NULL) { + return NULL; } - /* match */ + r->headers_out.content_type_lowcase = lowcase; - r->loc_conf = clcfp[i]->loc_conf; + hash = 0; - return NGX_HTTP_LOCATION_REGEX; - } + for (i = 0; i < len; i++) { + c = ngx_tolower(r->headers_out.content_type.data[i]); + hash = ngx_hash(hash, c); + lowcase[i] = c; + } -#endif /* NGX_PCRE */ + r->headers_out.content_type_hash = hash; + } - return NGX_OK; + return ngx_hash_find(types_hash, r->headers_out.content_type_hash, + r->headers_out.content_type_lowcase, len); } ngx_int_t ngx_http_set_content_type(ngx_http_request_t *r) { - u_char c, *p, *exten; + u_char c, *exten; ngx_str_t *type; ngx_uint_t i, hash; ngx_http_core_loc_conf_t *clcf; @@ -1112,19 +1679,12 @@ if (c >= 'A' && c <= 'Z') { - p = ngx_palloc(r->pool, r->exten.len); - if (p == NULL) { - return NGX_HTTP_INTERNAL_SERVER_ERROR; + exten = ngx_pnalloc(r->pool, r->exten.len); + if (exten == NULL) { + return NGX_ERROR; } - hash = 0; - exten = p; - - for (i = 0; i < r->exten.len; i++) { - c = ngx_tolower(r->exten.data[i]); - hash = ngx_hash(hash, c); - *p++ = c; - } + hash = ngx_hash_strlow(exten, r->exten.data, r->exten.len); r->exten.data = exten; @@ -1152,13 +1712,12 @@ } -ngx_int_t +void ngx_http_set_exten(ngx_http_request_t *r) { ngx_int_t i; - r->exten.len = 0; - r->exten.data = NULL; + ngx_str_null(&r->exten); for (i = r->uri.len - 1; i > 1; i--) { if (r->uri.data[i] == '.' && r->uri.data[i - 1] != '/') { @@ -1166,14 +1725,88 @@ r->exten.len = r->uri.len - i - 1; r->exten.data = &r->uri.data[i + 1]; - break; + return; } else if (r->uri.data[i] == '/') { - break; + return; } } - return NGX_OK; + return; +} + + +ngx_int_t +ngx_http_send_response(ngx_http_request_t *r, ngx_uint_t status, + ngx_str_t *ct, ngx_http_complex_value_t *cv) +{ + ngx_int_t rc; + ngx_str_t val; + ngx_buf_t *b; + ngx_chain_t out; + + r->headers_out.status = status; + + if (status == NGX_HTTP_NO_CONTENT) { + r->header_only = 1; + return ngx_http_send_header(r); + } + + if (ngx_http_complex_value(r, cv, &val) != NGX_OK) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + if (status >= NGX_HTTP_MOVED_PERMANENTLY && status <= NGX_HTTP_SEE_OTHER) { + + r->headers_out.location = ngx_list_push(&r->headers_out.headers); + if (r->headers_out.location == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + r->headers_out.location->hash = 1; + ngx_str_set(&r->headers_out.location->key, "Location"); + r->headers_out.location->value = val; + + return status; + } + + r->headers_out.content_length_n = val.len; + + if (ct) { + r->headers_out.content_type_len = ct->len; + r->headers_out.content_type = *ct; + + } else { + if (ngx_http_set_content_type(r) != NGX_OK) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + } + + if (r->method == NGX_HTTP_HEAD || (r != r->main && val.len == 0)) { + return ngx_http_send_header(r); + } + + b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t)); + if (b == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + b->pos = val.data; + b->last = val.data + val.len; + b->memory = val.len ? 1 : 0; + b->last_buf = (r == r->main) ? 1 : 0; + b->last_in_chain = 1; + + out.buf = b; + out.next = NULL; + + rc = ngx_http_send_header(r); + + if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) { + return rc; + } + + return ngx_http_output_filter(r, &out); } @@ -1192,16 +1825,19 @@ ngx_int_t ngx_http_output_filter(ngx_http_request_t *r, ngx_chain_t *in) { - ngx_int_t rc; + ngx_int_t rc; + ngx_connection_t *c; - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + c = r->connection; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "http output filter \"%V?%V\"", &r->uri, &r->args); rc = ngx_http_top_body_filter(r, in); if (rc == NGX_ERROR) { /* NGX_ERROR may be returned by any filter */ - r->connection->error = 1; + c->error = 1; } return rc; @@ -1218,7 +1854,7 @@ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); - alias = clcf->alias ? clcf->name.len : 0; + alias = clcf->alias; if (alias && !r->valid_location) { ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, @@ -1227,15 +1863,13 @@ return NULL; } - reserved += r->uri.len - alias + 1; - if (clcf->root_lengths == NULL) { *root_length = clcf->root.len; - path->len = clcf->root.len + reserved; + path->len = clcf->root.len + reserved + r->uri.len - alias + 1; - path->data = ngx_palloc(r->pool, path->len); + path->data = ngx_pnalloc(r->pool, path->len); if (path->data == NULL) { return NULL; } @@ -1243,6 +1877,18 @@ last = ngx_copy(path->data, clcf->root.data, clcf->root.len); } else { + +#if (NGX_PCRE) + ngx_uint_t captures; + + captures = alias && clcf->regex; + + reserved += captures ? r->add_uri_to_alias ? r->uri.len + 1 : 1 + : r->uri.len - alias + 1; +#else + reserved += r->uri.len - alias + 1; +#endif + if (ngx_http_script_run(r, path, clcf->root_lengths->elts, reserved, clcf->root_values->elts) == NULL) @@ -1250,12 +1896,23 @@ return NULL; } - if (ngx_conf_full_name((ngx_cycle_t *) ngx_cycle, path) == NGX_ERROR) { + if (ngx_conf_full_name((ngx_cycle_t *) ngx_cycle, path, 0) != NGX_OK) { return NULL; } *root_length = path->len - reserved; last = path->data + *root_length; + +#if (NGX_PCRE) + if (captures) { + if (!r->add_uri_to_alias) { + *last = '\0'; + return last; + } + + alias = 0; + } +#endif } last = ngx_cpystrn(last, r->uri.data + alias, r->uri.len - alias + 1); @@ -1304,7 +1961,7 @@ } auth.len = ngx_base64_decoded_length(encoded.len); - auth.data = ngx_palloc(r->pool, auth.len + 1); + auth.data = ngx_pnalloc(r->pool, auth.len + 1); if (auth.data == NULL) { return NGX_ERROR; } @@ -1336,14 +1993,168 @@ } +#if (NGX_HTTP_GZIP) + +ngx_int_t +ngx_http_gzip_ok(ngx_http_request_t *r) +{ + time_t date, expires; + ngx_uint_t p; + ngx_array_t *cc; + ngx_table_elt_t *e, *d; + ngx_http_core_loc_conf_t *clcf; + + r->gzip_tested = 1; + + if (r != r->main + || r->headers_in.accept_encoding == NULL + || ngx_strcasestrn(r->headers_in.accept_encoding->value.data, + "gzip", 4 - 1) + == NULL + + /* + * if the URL (without the "http://" prefix) is longer than 253 bytes, + * then MSIE 4.x can not handle the compressed stream - it waits + * too long, hangs up or crashes + */ + + || (r->headers_in.msie4 && r->unparsed_uri.len > 200)) + { + return NGX_DECLINED; + } + + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + if (r->headers_in.msie6 && clcf->gzip_disable_msie6) { + return NGX_DECLINED; + } + + if (r->http_version < clcf->gzip_http_version) { + return NGX_DECLINED; + } + + if (r->headers_in.via == NULL) { + goto ok; + } + + p = clcf->gzip_proxied; + + if (p & NGX_HTTP_GZIP_PROXIED_OFF) { + return NGX_DECLINED; + } + + if (p & NGX_HTTP_GZIP_PROXIED_ANY) { + goto ok; + } + + if (r->headers_in.authorization && (p & NGX_HTTP_GZIP_PROXIED_AUTH)) { + goto ok; + } + + e = r->headers_out.expires; + + if (e) { + + if (!(p & NGX_HTTP_GZIP_PROXIED_EXPIRED)) { + return NGX_DECLINED; + } + + expires = ngx_http_parse_time(e->value.data, e->value.len); + if (expires == NGX_ERROR) { + return NGX_DECLINED; + } + + d = r->headers_out.date; + + if (d) { + date = ngx_http_parse_time(d->value.data, d->value.len); + if (date == NGX_ERROR) { + return NGX_DECLINED; + } + + } else { + date = ngx_time(); + } + + if (expires < date) { + goto ok; + } + + return NGX_DECLINED; + } + + cc = &r->headers_out.cache_control; + + if (cc->elts) { + + if ((p & NGX_HTTP_GZIP_PROXIED_NO_CACHE) + && ngx_http_parse_multi_header_lines(cc, &ngx_http_gzip_no_cache, + NULL) + >= 0) + { + goto ok; + } + + if ((p & NGX_HTTP_GZIP_PROXIED_NO_STORE) + && ngx_http_parse_multi_header_lines(cc, &ngx_http_gzip_no_store, + NULL) + >= 0) + { + goto ok; + } + + if ((p & NGX_HTTP_GZIP_PROXIED_PRIVATE) + && ngx_http_parse_multi_header_lines(cc, &ngx_http_gzip_private, + NULL) + >= 0) + { + goto ok; + } + + return NGX_DECLINED; + } + + if ((p & NGX_HTTP_GZIP_PROXIED_NO_LM) && r->headers_out.last_modified) { + return NGX_DECLINED; + } + + if ((p & NGX_HTTP_GZIP_PROXIED_NO_ETAG) && r->headers_out.etag) { + return NGX_DECLINED; + } + +ok: + +#if (NGX_PCRE) + + if (clcf->gzip_disable && r->headers_in.user_agent) { + + if (ngx_regex_exec_array(clcf->gzip_disable, + &r->headers_in.user_agent->value, + r->connection->log) + != NGX_DECLINED) + { + return NGX_DECLINED; + } + } + +#endif + + r->gzip_ok = 1; + + return NGX_OK; +} + +#endif + + ngx_int_t ngx_http_subrequest(ngx_http_request_t *r, ngx_str_t *uri, ngx_str_t *args, ngx_http_request_t **psr, ngx_http_post_subrequest_t *ps, ngx_uint_t flags) { + ngx_time_t *tp; ngx_connection_t *c; ngx_http_request_t *sr; - ngx_http_log_ctx_t *ctx; ngx_http_core_srv_conf_t *cscf; ngx_http_postponed_request_t *pr, *p; @@ -1373,7 +2184,7 @@ if (ngx_list_init(&sr->headers_out.headers, r->pool, 20, sizeof(ngx_table_elt_t)) - == NGX_ERROR) + != NGX_OK) { return NGX_ERROR; } @@ -1406,32 +2217,25 @@ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "http subrequest \"%V?%V\"", uri, &sr->args); - sr->zero_in_uri = (flags & NGX_HTTP_ZERO_IN_URI) != 0; sr->subrequest_in_memory = (flags & NGX_HTTP_SUBREQUEST_IN_MEMORY) != 0; + sr->waited = (flags & NGX_HTTP_SUBREQUEST_WAITED) != 0; sr->unparsed_uri = r->unparsed_uri; - sr->method_name = r->method_name; + sr->method_name = ngx_http_core_get_method; sr->http_protocol = r->http_protocol; - if (ngx_http_set_exten(sr) != NGX_OK) { - return NGX_ERROR; - } + ngx_http_set_exten(sr); sr->main = r->main; sr->parent = r; sr->post_subrequest = ps; sr->read_event_handler = ngx_http_request_empty_handler; - sr->write_event_handler = ngx_http_request_empty_handler; + sr->write_event_handler = ngx_http_handler; - if (c->data == r) { + if (c->data == r && r->postponed == NULL) { c->data = sr; } - sr->in_addr = r->in_addr; - sr->port = r->port; - sr->port_text = r->port_text; - sr->server_name = r->server_name; - sr->variables = r->variables; sr->log_handler = r->log_handler; @@ -1453,39 +2257,24 @@ r->postponed = pr; } - ctx = c->log->data; - ctx->current_request = sr; - sr->internal = 1; - sr->fast_subrequest = 1; sr->discard_body = r->discard_body; + sr->expect_tested = 1; sr->main_filter_need_in_memory = r->main_filter_need_in_memory; sr->uri_changes = NGX_HTTP_MAX_URI_CHANGES + 1; - ngx_http_handler(sr); + tp = ngx_timeofday(); + r->start_sec = tp->sec; + r->start_msec = tp->msec; - if (!c->destroyed) { - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http subrequest done \"%V?%V\"", uri, &sr->args); + r->main->subrequests++; + r->main->count++; - r->main->subrequests++; + *psr = sr; - *psr = sr; - - if (sr->fast_subrequest) { - sr->fast_subrequest = 0; - - if (sr->done) { - return NGX_OK; - } - } - - return NGX_AGAIN; - } - - return NGX_DONE; + return ngx_http_post_request(sr, NULL); } @@ -1502,6 +2291,7 @@ "rewrite or internal redirection cycle " "while internal redirect to \"%V\"", uri); + r->main->count++; ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); return NGX_DONE; } @@ -1512,17 +2302,13 @@ r->args = *args; } else { - r->args.len = 0; - r->args.data = NULL; + ngx_str_null(&r->args); } ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "internal redirect: \"%V?%V\"", uri, &r->args); - if (ngx_http_set_exten(r) != NGX_OK) { - ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); - return NGX_DONE; - } + ngx_http_set_exten(r); /* clear the modules contexts */ ngx_memzero(r->ctx, sizeof(void *) * ngx_http_max_module); @@ -1532,7 +2318,13 @@ ngx_http_update_location_config(r); +#if (NGX_HTTP_CACHE) + r->cache = NULL; +#endif + r->internal = 1; + r->add_uri_to_alias = 0; + r->main->count++; ngx_http_handler(r); @@ -1543,44 +2335,52 @@ ngx_int_t ngx_http_named_location(ngx_http_request_t *r, ngx_str_t *name) { - ngx_uint_t i; ngx_http_core_srv_conf_t *cscf; ngx_http_core_loc_conf_t **clcfp; ngx_http_core_main_conf_t *cmcf; + r->main->count++; + cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); - clcfp = cscf->locations.elts; + if (cscf->named_locations) { - for (i = cscf->named_start; i < cscf->locations.nelts; i++) { + for (clcfp = cscf->named_locations; *clcfp; clcfp++) { - if (name->len != clcfp[i]->name.len - || ngx_strncmp(name->data, clcfp[i]->name.data, name->len) != 0) - { - continue; - } + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "test location: \"%V\"", &(*clcfp)->name); - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "named location: %V \"%V?%V\"", name, &r->uri, &r->args); + if (name->len != (*clcfp)->name.len + || ngx_strncmp(name->data, (*clcfp)->name.data, name->len) != 0) + { + continue; + } - r->internal = 1; + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "using location: %V \"%V?%V\"", + name, &r->uri, &r->args); - r->loc_conf = clcfp[i]->loc_conf; + r->internal = 1; + r->content_handler = NULL; + r->loc_conf = (*clcfp)->loc_conf; - ngx_http_update_location_config(r); + ngx_http_update_location_config(r); - cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); + cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); - r->phase_handler = cmcf->phase_engine.location_rewrite_index; - ngx_http_core_run_phases(r); + r->phase_handler = cmcf->phase_engine.location_rewrite_index; - return NGX_DONE; + ngx_http_core_run_phases(r); + + return NGX_DONE; + } } ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "could not find name location \"%V\"", name); + "could not find named location \"%V\"", name); ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return NGX_DONE; } @@ -1627,9 +2427,10 @@ ngx_uint_t i; ngx_conf_t pcf; ngx_http_module_t *module; + struct sockaddr_in *sin; ngx_http_conf_ctx_t *ctx, *http_ctx; + ngx_http_listen_opt_t lsopt; ngx_http_core_srv_conf_t *cscf, **cscfp; - ngx_http_core_loc_conf_t **clcfp; ngx_http_core_main_conf_t *cmcf; ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t)); @@ -1707,34 +2508,34 @@ *cf = pcf; - if (rv != NGX_CONF_OK) { - return rv; - } - - ngx_sort(cscf->locations.elts, (size_t) cscf->locations.nelts, - sizeof(ngx_http_core_loc_conf_t *), ngx_http_core_cmp_locations); + if (rv == NGX_CONF_OK && !cscf->listen) { + ngx_memzero(&lsopt, sizeof(ngx_http_listen_opt_t)); - clcfp = cscf->locations.elts; + sin = &lsopt.u.sockaddr_in; -#if (NGX_PCRE) - - cscf->regex_start = cscf->locations.nelts; + sin->sin_family = AF_INET; +#if (NGX_WIN32) + sin->sin_port = htons(80); +#else + sin->sin_port = htons((getuid() == 0) ? 80 : 8000); +#endif + sin->sin_addr.s_addr = INADDR_ANY; - for (i = 0; i < cscf->locations.nelts; i++) { - if (clcfp[i]->regex) { - cscf->regex_start = i; - break; - } - } + lsopt.socklen = sizeof(struct sockaddr_in); + lsopt.backlog = NGX_LISTEN_BACKLOG; + lsopt.rcvbuf = -1; + lsopt.sndbuf = -1; +#if (NGX_HAVE_SETFIB) + lsopt.setfib = -1; #endif + lsopt.wildcard = 1; - cscf->named_start = cscf->locations.nelts; + (void) ngx_sock_ntop(&lsopt.u.sockaddr, lsopt.addr, + NGX_SOCKADDR_STRLEN, 1); - for (i = 0; i < cscf->locations.nelts; i++) { - if (clcfp[i]->named) { - cscf->named_start = i; - break; + if (ngx_http_add_listen(cf, cscf, &lsopt) != NGX_OK) { + return NGX_CONF_ERROR; } } @@ -1746,17 +2547,14 @@ ngx_http_core_location(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy) { char *rv; + u_char *mod; + size_t len; + ngx_str_t *value, *name; ngx_uint_t i; - ngx_str_t *value; ngx_conf_t save; ngx_http_module_t *module; ngx_http_conf_ctx_t *ctx, *pctx; - ngx_http_core_srv_conf_t *cscf; - ngx_http_core_loc_conf_t *clcf, *pclcf, **clcfp; -#if (NGX_PCRE) - ngx_str_t err; - u_char errstr[NGX_MAX_CONF_ERRSTR]; -#endif + ngx_http_core_loc_conf_t *clcf, *pclcf; ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t)); if (ctx == NULL) { @@ -1794,42 +2592,32 @@ value = cf->args->elts; if (cf->args->nelts == 3) { - if (value[1].len == 1 && value[1].data[0] == '=') { - clcf->name = value[2]; + + len = value[1].len; + mod = value[1].data; + name = &value[2]; + + if (len == 1 && mod[0] == '=') { + + clcf->name = *name; clcf->exact_match = 1; - } else if (value[1].len == 2 - && value[1].data[0] == '^' - && value[1].data[1] == '~') - { - clcf->name = value[2]; - clcf->noregex = 1; + } else if (len == 2 && mod[0] == '^' && mod[1] == '~') { - } else if ((value[1].len == 1 && value[1].data[0] == '~') - || (value[1].len == 2 - && value[1].data[0] == '~' - && value[1].data[1] == '*')) - { -#if (NGX_PCRE) - err.len = NGX_MAX_CONF_ERRSTR; - err.data = errstr; + clcf->name = *name; + clcf->noregex = 1; - clcf->regex = ngx_regex_compile(&value[2], - value[1].len == 2 ? NGX_REGEX_CASELESS: 0, - cf->pool, &err); + } else if (len == 1 && mod[0] == '~') { - if (clcf->regex == NULL) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%s", err.data); + if (ngx_http_core_regex_location(cf, clcf, name, 0) != NGX_OK) { return NGX_CONF_ERROR; } - clcf->name = value[2]; -#else - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "the using of the regex \"%V\" " - "requires PCRE library", &value[2]); - return NGX_CONF_ERROR; -#endif + } else if (len == 2 && mod[0] == '~' && mod[1] == '*') { + + if (ngx_http_core_regex_location(cf, clcf, name, 1) != NGX_OK) { + return NGX_CONF_ERROR; + } } else { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, @@ -1839,24 +2627,56 @@ } else { - clcf->name = value[1]; + name = &value[1]; + + if (name->data[0] == '=') { + + clcf->name.len = name->len - 1; + clcf->name.data = name->data + 1; + clcf->exact_match = 1; + + } else if (name->data[0] == '^' && name->data[1] == '~') { + + clcf->name.len = name->len - 2; + clcf->name.data = name->data + 2; + clcf->noregex = 1; + + } else if (name->data[0] == '~') { + + name->len--; + name->data++; + + if (name->data[0] == '*') { + + name->len--; + name->data++; - if (value[1].data[0] == '@') { - clcf->named = 1; + if (ngx_http_core_regex_location(cf, clcf, name, 1) != NGX_OK) { + return NGX_CONF_ERROR; + } + + } else { + if (ngx_http_core_regex_location(cf, clcf, name, 0) != NGX_OK) { + return NGX_CONF_ERROR; + } + } + + } else { + + clcf->name = *name; + + if (name->data[0] == '@') { + clcf->named = 1; + } } } pclcf = pctx->loc_conf[ngx_http_core_module.ctx_index]; - if (pclcf->name.len == 0) { - cscf = ctx->srv_conf[ngx_http_core_module.ctx_index]; + if (pclcf->name.len) { - clcfp = ngx_array_push(&cscf->locations); - if (clcfp == NULL) { - return NGX_CONF_ERROR; - } + /* nested location */ - } else { #if 0 clcf->prev_location = pclcf; #endif @@ -1877,36 +2697,33 @@ return NGX_CONF_ERROR; } -#if (NGX_PCRE) - if (clcf->regex == NULL - && ngx_strncmp(clcf->name.data, pclcf->name.data, pclcf->name.len) - != 0) -#else - if (ngx_strncmp(clcf->name.data, pclcf->name.data, pclcf->name.len) - != 0) -#endif - { + if (clcf->named) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "location \"%V\" is outside location \"%V\"", - &clcf->name, &pclcf->name); + "named location \"%V\" must be " + "on server level only", + &clcf->name); return NGX_CONF_ERROR; } - if (pclcf->locations == NULL) { - pclcf->locations = ngx_array_create(cf->pool, 2, sizeof(void *)); - - if (pclcf->locations == NULL) { - return NGX_CONF_ERROR; - } - } + len = pclcf->name.len; - clcfp = ngx_array_push(pclcf->locations); - if (clcfp == NULL) { +#if (NGX_PCRE) + if (clcf->regex == NULL + && ngx_strncmp(clcf->name.data, pclcf->name.data, len) != 0) +#else + if (ngx_strncmp(clcf->name.data, pclcf->name.data, len) != 0) +#endif + { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "location \"%V\" is outside location \"%V\"", + &clcf->name, &pclcf->name); return NGX_CONF_ERROR; } } - *clcfp = clcf; + if (ngx_http_add_location(cf, &pclcf->locations, clcf) != NGX_OK) { + return NGX_CONF_ERROR; + } save = *cf; cf->ctx = ctx; @@ -1916,114 +2733,61 @@ *cf = save; - if (rv != NGX_CONF_OK) { - return rv; - } - - if (clcf->locations == NULL) { - return rv; - } - - ngx_sort(clcf->locations->elts, (size_t) clcf->locations->nelts, - sizeof(ngx_http_core_loc_conf_t *), ngx_http_core_cmp_locations); - -#if (NGX_PCRE) - - clcf->regex_start = clcf->locations->nelts; - clcfp = clcf->locations->elts; - - for (i = 0; i < clcf->locations->nelts; i++) { - if (clcfp[i]->regex) { - clcf->regex_start = i; - break; - } - } - -#endif - return rv; } -static int -ngx_http_core_cmp_locations(const void *one, const void *two) +static ngx_int_t +ngx_http_core_regex_location(ngx_conf_t *cf, ngx_http_core_loc_conf_t *clcf, + ngx_str_t *regex, ngx_uint_t caseless) { - ngx_int_t rc; - ngx_http_core_loc_conf_t *first, *second; - - first = *(ngx_http_core_loc_conf_t **) one; - second = *(ngx_http_core_loc_conf_t **) two; - - if (first->named && !second->named) { - /* shift named locations to the end */ - return 1; - } - - if (!first->named && second->named) { - /* shift named locations to the end */ - return -1; - } +#if (NGX_PCRE) + ngx_regex_compile_t rc; + u_char errstr[NGX_MAX_CONF_ERRSTR]; - if (first->named && second->named) { - return ngx_strcmp(first->name.data, second->name.data); - } + ngx_memzero(&rc, sizeof(ngx_regex_compile_t)); - if (first->noname && !second->noname) { - /* shift no named locations to the end */ - return 1; - } + rc.pattern = *regex; + rc.err.len = NGX_MAX_CONF_ERRSTR; + rc.err.data = errstr; - if (!first->noname && second->noname) { - /* shift no named locations to the end */ - return -1; - } +#if (NGX_HAVE_CASELESS_FILESYSTEM) + rc.options = NGX_REGEX_CASELESS; +#else + rc.options = caseless; +#endif - if (first->noname || second->noname) { - /* do not sort no named locations */ - return 0; + clcf->regex = ngx_http_regex_compile(cf, &rc); + if (clcf->regex == NULL) { + return NGX_ERROR; } -#if (NGX_PCRE) + clcf->name = *regex; - if (first->regex && !second->regex) { - /* shift the regex matches to the end */ - return 1; - } + return NGX_OK; - if (!first->regex && second->regex) { - /* shift the regex matches to the end */ - return -1; - } +#else - if (first->regex || second->regex) { - /* do not sort the regex matches */ - return 0; - } + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "the using of the regex \"%V\" requires PCRE library", + regex); + return NGX_ERROR; #endif - - rc = ngx_strcmp(first->name.data, second->name.data); - - if (rc == 0 && second->exact_match) { - /* an exact match must be before the same inclusive one */ - return 1; - } - - return (int) rc; } static char * ngx_http_core_types(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { - ngx_http_core_loc_conf_t *lcf = conf; + ngx_http_core_loc_conf_t *clcf = conf; char *rv; ngx_conf_t save; - if (lcf->types == NULL) { - lcf->types = ngx_array_create(cf->pool, 64, sizeof(ngx_hash_key_t)); - if (lcf->types == NULL) { + if (clcf->types == NULL) { + clcf->types = ngx_array_create(cf->pool, 64, sizeof(ngx_hash_key_t)); + if (clcf->types == NULL) { return NGX_CONF_ERROR; } } @@ -2043,10 +2807,10 @@ static char * ngx_http_core_type(ngx_conf_t *cf, ngx_command_t *dummy, void *conf) { - ngx_http_core_loc_conf_t *lcf = conf; + ngx_http_core_loc_conf_t *clcf = conf; ngx_str_t *value, *content_type, *old, file; - ngx_uint_t i, n; + ngx_uint_t i, n, hash; ngx_hash_key_t *type; value = cf->args->elts; @@ -2054,7 +2818,7 @@ if (ngx_strcmp(value[0].data, "include") == 0) { file = value[1]; - if (ngx_conf_full_name(cf->cycle, &file) == NGX_ERROR){ + if (ngx_conf_full_name(cf->cycle, &file, 1) != NGX_OK) { return NGX_CONF_ERROR; } @@ -2072,12 +2836,10 @@ for (i = 1; i < cf->args->nelts; i++) { - for (n = 0; n < value[i].len; n++) { - value[i].data[n] = ngx_tolower(value[i].data[n]); - } + hash = ngx_hash_strlow(value[i].data, value[i].data, value[i].len); - type = lcf->types->elts; - for (n = 0; n < lcf->types->nelts; n++) { + type = clcf->types->elts; + for (n = 0; n < clcf->types->nelts; n++) { if (ngx_strcmp(value[i].data, type[n].key.data) == 0) { old = type[n].value; type[n].value = content_type; @@ -2092,13 +2854,13 @@ } - type = ngx_array_push(lcf->types); + type = ngx_array_push(clcf->types); if (type == NULL) { return NGX_CONF_ERROR; } type->key = value[i]; - type->key_hash = ngx_hash_key(value[i].data, value[i].len); + type->key_hash = hash; type->value = content_type; } @@ -2120,14 +2882,14 @@ cmcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_core_main_conf_t)); if (cmcf == NULL) { - return NGX_CONF_ERROR; + return NULL; } if (ngx_array_init(&cmcf->servers, cf->pool, 4, sizeof(ngx_http_core_srv_conf_t *)) != NGX_OK) { - return NGX_CONF_ERROR; + return NULL; } cmcf->server_names_hash_max_size = NGX_CONF_UNSET_UINT; @@ -2168,6 +2930,10 @@ cmcf->variables_hash_bucket_size = ngx_align(cmcf->variables_hash_bucket_size, ngx_cacheline_size); + if (cmcf->ncaptures) { + cmcf->ncaptures = (cmcf->ncaptures + 1) * 3; + } + return NGX_CONF_OK; } @@ -2179,7 +2945,7 @@ cscf = ngx_pcalloc(cf->pool, sizeof(ngx_http_core_srv_conf_t)); if (cscf == NULL) { - return NGX_CONF_ERROR; + return NULL; } /* @@ -2188,31 +2954,20 @@ * conf->client_large_buffers.num = 0; */ - if (ngx_array_init(&cscf->locations, cf->pool, 4, sizeof(void *)) - == NGX_ERROR) - { - return NGX_CONF_ERROR; - } - - if (ngx_array_init(&cscf->listen, cf->pool, 4, sizeof(ngx_http_listen_t)) - == NGX_ERROR) - { - return NGX_CONF_ERROR; - } - if (ngx_array_init(&cscf->server_names, cf->temp_pool, 4, sizeof(ngx_http_server_name_t)) - == NGX_ERROR) + != NGX_OK) { - return NGX_CONF_ERROR; + return NULL; } cscf->connection_pool_size = NGX_CONF_UNSET_SIZE; cscf->request_pool_size = NGX_CONF_UNSET_SIZE; cscf->client_header_timeout = NGX_CONF_UNSET_MSEC; cscf->client_header_buffer_size = NGX_CONF_UNSET_SIZE; - cscf->optimize_server_names = NGX_CONF_UNSET; cscf->ignore_invalid_headers = NGX_CONF_UNSET; + cscf->merge_slashes = NGX_CONF_UNSET; + cscf->underscores_in_headers = NGX_CONF_UNSET; return cscf; } @@ -2224,62 +2979,10 @@ ngx_http_core_srv_conf_t *prev = parent; ngx_http_core_srv_conf_t *conf = child; - ngx_http_listen_t *ls; ngx_http_server_name_t *sn; /* TODO: it does not merge, it inits only */ - if (conf->listen.nelts == 0) { - ls = ngx_array_push(&conf->listen); - if (ls == NULL) { - return NGX_CONF_ERROR; - } - - ngx_memzero(ls, sizeof(ngx_http_listen_t)); - - ls->addr = INADDR_ANY; -#if (NGX_WIN32) - ls->port = 80; -#else - /* STUB: getuid() should be cached */ - ls->port = (getuid() == 0) ? 80 : 8000; -#endif - ls->family = AF_INET; - - ls->conf.backlog = NGX_LISTEN_BACKLOG; - ls->conf.rcvbuf = -1; - ls->conf.sndbuf = -1; - } - - if (conf->server_name.data == NULL) { - conf->server_name.data = ngx_palloc(cf->pool, NGX_MAXHOSTNAMELEN); - if (conf->server_name.data == NULL) { - return NGX_CONF_ERROR; - } - - if (gethostname((char *) conf->server_name.data, NGX_MAXHOSTNAMELEN) - == -1) - { - ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno, - "gethostname() failed"); - return NGX_CONF_ERROR; - } - - conf->server_name.len = ngx_strlen(conf->server_name.data); - - sn = ngx_array_push(&conf->server_names); - if (sn == NULL) { - return NGX_CONF_ERROR; - } - -#if (NGX_PCRE) - sn->regex = NULL; -#endif - sn->core_srv_conf = conf; - sn->name.len = conf->server_name.len; - sn->name.data = conf->server_name.data; - } - ngx_conf_merge_size_value(conf->connection_pool_size, prev->connection_pool_size, 256); ngx_conf_merge_size_value(conf->request_pool_size, @@ -2290,7 +2993,7 @@ prev->client_header_buffer_size, 1024); ngx_conf_merge_bufs_value(conf->large_client_header_buffers, prev->large_client_header_buffers, - 4, ngx_pagesize); + 4, 8192); if (conf->large_client_header_buffers.size < conf->connection_pool_size) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, @@ -2299,12 +3002,29 @@ return NGX_CONF_ERROR; } - ngx_conf_merge_value(conf->optimize_server_names, - prev->optimize_server_names, 1); - ngx_conf_merge_value(conf->ignore_invalid_headers, prev->ignore_invalid_headers, 1); + ngx_conf_merge_value(conf->merge_slashes, prev->merge_slashes, 1); + + ngx_conf_merge_value(conf->underscores_in_headers, + prev->underscores_in_headers, 0); + + if (conf->server_name.data == NULL) { + ngx_str_set(&conf->server_name, ""); + + sn = ngx_array_push(&conf->server_names); + if (sn == NULL) { + return NGX_CONF_ERROR; + } + +#if (NGX_PCRE) + sn->regex = NULL; +#endif + sn->server = conf; + ngx_str_set(&sn->name, ""); + } + return NGX_CONF_OK; } @@ -2312,58 +3032,93 @@ static void * ngx_http_core_create_loc_conf(ngx_conf_t *cf) { - ngx_http_core_loc_conf_t *lcf; + ngx_http_core_loc_conf_t *clcf; - lcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_core_loc_conf_t)); - if (lcf == NULL) { - return NGX_CONF_ERROR; + clcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_core_loc_conf_t)); + if (clcf == NULL) { + return NULL; } /* * set by ngx_pcalloc(): * - * lcf->root = { 0, NULL }; - * lcf->limit_except = 0; - * lcf->post_action = { 0, NULL }; - * lcf->types = NULL; - * lcf->default_type = { 0, NULL }; - * lcf->err_log = NULL; - * lcf->error_pages = NULL; - * lcf->client_body_path = NULL; - * lcf->regex = NULL; - * lcf->exact_match = 0; - * lcf->auto_redirect = 0; - * lcf->alias = 0; + * clcf->root = { 0, NULL }; + * clcf->limit_except = 0; + * clcf->post_action = { 0, NULL }; + * clcf->types = NULL; + * clcf->default_type = { 0, NULL }; + * clcf->error_log = NULL; + * clcf->error_pages = NULL; + * clcf->try_files = NULL; + * clcf->client_body_path = NULL; + * clcf->regex = NULL; + * clcf->exact_match = 0; + * clcf->auto_redirect = 0; + * clcf->alias = 0; + * clcf->gzip_proxied = 0; */ - lcf->client_max_body_size = NGX_CONF_UNSET; - lcf->client_body_buffer_size = NGX_CONF_UNSET_SIZE; - lcf->client_body_timeout = NGX_CONF_UNSET_MSEC; - lcf->satisfy_any = NGX_CONF_UNSET; - lcf->internal = NGX_CONF_UNSET; - lcf->client_body_in_file_only = NGX_CONF_UNSET; - lcf->sendfile = NGX_CONF_UNSET; - lcf->sendfile_max_chunk = NGX_CONF_UNSET_SIZE; - lcf->tcp_nopush = NGX_CONF_UNSET; - lcf->tcp_nodelay = NGX_CONF_UNSET; - lcf->send_timeout = NGX_CONF_UNSET_MSEC; - lcf->send_lowat = NGX_CONF_UNSET_SIZE; - lcf->postpone_output = NGX_CONF_UNSET_SIZE; - lcf->limit_rate = NGX_CONF_UNSET_SIZE; - lcf->keepalive_timeout = NGX_CONF_UNSET_MSEC; - lcf->keepalive_header = NGX_CONF_UNSET; - lcf->lingering_time = NGX_CONF_UNSET_MSEC; - lcf->lingering_timeout = NGX_CONF_UNSET_MSEC; - lcf->reset_timedout_connection = NGX_CONF_UNSET; - lcf->port_in_redirect = NGX_CONF_UNSET; - lcf->msie_padding = NGX_CONF_UNSET; - lcf->msie_refresh = NGX_CONF_UNSET; - lcf->log_not_found = NGX_CONF_UNSET; - lcf->recursive_error_pages = NGX_CONF_UNSET; - lcf->types_hash_max_size = NGX_CONF_UNSET_UINT; - lcf->types_hash_bucket_size = NGX_CONF_UNSET_UINT; + clcf->client_max_body_size = NGX_CONF_UNSET; + clcf->client_body_buffer_size = NGX_CONF_UNSET_SIZE; + clcf->client_body_timeout = NGX_CONF_UNSET_MSEC; + clcf->satisfy = NGX_CONF_UNSET_UINT; + clcf->if_modified_since = NGX_CONF_UNSET_UINT; + clcf->client_body_in_file_only = NGX_CONF_UNSET_UINT; + clcf->client_body_in_single_buffer = NGX_CONF_UNSET; + clcf->internal = NGX_CONF_UNSET; + clcf->sendfile = NGX_CONF_UNSET; + clcf->sendfile_max_chunk = NGX_CONF_UNSET_SIZE; +#if (NGX_HAVE_FILE_AIO) + clcf->aio = NGX_CONF_UNSET; +#endif + clcf->read_ahead = NGX_CONF_UNSET_SIZE; + clcf->directio = NGX_CONF_UNSET; + clcf->directio_alignment = NGX_CONF_UNSET; + clcf->tcp_nopush = NGX_CONF_UNSET; + clcf->tcp_nodelay = NGX_CONF_UNSET; + clcf->send_timeout = NGX_CONF_UNSET_MSEC; + clcf->send_lowat = NGX_CONF_UNSET_SIZE; + clcf->postpone_output = NGX_CONF_UNSET_SIZE; + clcf->limit_rate = NGX_CONF_UNSET_SIZE; + clcf->limit_rate_after = NGX_CONF_UNSET_SIZE; + clcf->keepalive_timeout = NGX_CONF_UNSET_MSEC; + clcf->keepalive_header = NGX_CONF_UNSET; + clcf->keepalive_requests = NGX_CONF_UNSET_UINT; + clcf->lingering_time = NGX_CONF_UNSET_MSEC; + clcf->lingering_timeout = NGX_CONF_UNSET_MSEC; + clcf->resolver_timeout = NGX_CONF_UNSET_MSEC; + clcf->reset_timedout_connection = NGX_CONF_UNSET; + clcf->server_name_in_redirect = NGX_CONF_UNSET; + clcf->port_in_redirect = NGX_CONF_UNSET; + clcf->msie_padding = NGX_CONF_UNSET; + clcf->msie_refresh = NGX_CONF_UNSET; + clcf->log_not_found = NGX_CONF_UNSET; + clcf->log_subrequest = NGX_CONF_UNSET; + clcf->recursive_error_pages = NGX_CONF_UNSET; + clcf->server_tokens = NGX_CONF_UNSET; + clcf->chunked_transfer_encoding = NGX_CONF_UNSET; + clcf->types_hash_max_size = NGX_CONF_UNSET_UINT; + clcf->types_hash_bucket_size = NGX_CONF_UNSET_UINT; + + clcf->open_file_cache = NGX_CONF_UNSET_PTR; + clcf->open_file_cache_valid = NGX_CONF_UNSET; + clcf->open_file_cache_min_uses = NGX_CONF_UNSET_UINT; + clcf->open_file_cache_errors = NGX_CONF_UNSET; + clcf->open_file_cache_events = NGX_CONF_UNSET; + +#if (NGX_HTTP_GZIP) + clcf->gzip_vary = NGX_CONF_UNSET; + clcf->gzip_http_version = NGX_CONF_UNSET_UINT; +#if (NGX_PCRE) + clcf->gzip_disable = NGX_CONF_UNSET_PTR; +#endif + clcf->gzip_disable_msie6 = 3; +#if (NGX_HTTP_DEGRADATION) + clcf->gzip_disable_degradation = 3; +#endif +#endif - return lcf; + return clcf; } @@ -2397,10 +3152,9 @@ conf->root_values = prev->root_values; if (prev->root.data == NULL) { - conf->root.len = sizeof("html") - 1; - conf->root.data = (u_char *) "html"; + ngx_str_set(&conf->root, "html"); - if (ngx_conf_full_name(cf->cycle, &conf->root) == NGX_ERROR) { + if (ngx_conf_full_name(cf->cycle, &conf->root, 0) != NGX_OK) { return NGX_CONF_ERROR; } } @@ -2484,11 +3238,11 @@ } } - if (conf->err_log == NULL) { - if (prev->err_log) { - conf->err_log = prev->err_log; + if (conf->error_log == NULL) { + if (prev->error_log) { + conf->error_log = prev->error_log; } else { - conf->err_log = cf->cycle->new_log; + conf->error_log = &cf->cycle->new_log; } } @@ -2507,13 +3261,26 @@ ngx_conf_merge_msec_value(conf->client_body_timeout, prev->client_body_timeout, 60000); - ngx_conf_merge_value(conf->satisfy_any, prev->satisfy_any, 0); - ngx_conf_merge_value(conf->internal, prev->internal, 0); - ngx_conf_merge_value(conf->client_body_in_file_only, + ngx_conf_merge_uint_value(conf->satisfy, prev->satisfy, + NGX_HTTP_SATISFY_ALL); + ngx_conf_merge_uint_value(conf->if_modified_since, prev->if_modified_since, + NGX_HTTP_IMS_EXACT); + ngx_conf_merge_uint_value(conf->client_body_in_file_only, prev->client_body_in_file_only, 0); + ngx_conf_merge_value(conf->client_body_in_single_buffer, + prev->client_body_in_single_buffer, 0); + ngx_conf_merge_value(conf->internal, prev->internal, 0); ngx_conf_merge_value(conf->sendfile, prev->sendfile, 0); ngx_conf_merge_size_value(conf->sendfile_max_chunk, prev->sendfile_max_chunk, 0); +#if (NGX_HAVE_FILE_AIO) + ngx_conf_merge_value(conf->aio, prev->aio, 0); +#endif + ngx_conf_merge_size_value(conf->read_ahead, prev->read_ahead, 0); + ngx_conf_merge_off_value(conf->directio, prev->directio, + NGX_MAX_OFF_T_VALUE); + ngx_conf_merge_off_value(conf->directio_alignment, prev->directio_alignment, + 512); ngx_conf_merge_value(conf->tcp_nopush, prev->tcp_nopush, 0); ngx_conf_merge_value(conf->tcp_nodelay, prev->tcp_nodelay, 1); @@ -2522,53 +3289,119 @@ ngx_conf_merge_size_value(conf->postpone_output, prev->postpone_output, 1460); ngx_conf_merge_size_value(conf->limit_rate, prev->limit_rate, 0); + ngx_conf_merge_size_value(conf->limit_rate_after, prev->limit_rate_after, + 0); ngx_conf_merge_msec_value(conf->keepalive_timeout, prev->keepalive_timeout, 75000); ngx_conf_merge_sec_value(conf->keepalive_header, prev->keepalive_header, 0); + ngx_conf_merge_uint_value(conf->keepalive_requests, + prev->keepalive_requests, 100); ngx_conf_merge_msec_value(conf->lingering_time, prev->lingering_time, 30000); ngx_conf_merge_msec_value(conf->lingering_timeout, prev->lingering_timeout, 5000); + ngx_conf_merge_msec_value(conf->resolver_timeout, + prev->resolver_timeout, 30000); + + if (conf->resolver == NULL) { + + if (prev->resolver == NULL) { + + /* + * create dummy resolver in http {} context + * to inherit it in all servers + */ + + prev->resolver = ngx_resolver_create(cf, NULL); + if (prev->resolver == NULL) { + return NGX_CONF_ERROR; + } + } + + conf->resolver = prev->resolver; + } - ngx_conf_merge_path_value(conf->client_body_temp_path, + if (ngx_conf_merge_path_value(cf, &conf->client_body_temp_path, prev->client_body_temp_path, - NGX_HTTP_CLIENT_TEMP_PATH, 0, 0, 0, - ngx_garbage_collector_temp_handler, cf); + &ngx_http_client_temp_path) + != NGX_OK) + { + return NGX_CONF_ERROR; + } ngx_conf_merge_value(conf->reset_timedout_connection, prev->reset_timedout_connection, 0); + ngx_conf_merge_value(conf->server_name_in_redirect, + prev->server_name_in_redirect, 0); ngx_conf_merge_value(conf->port_in_redirect, prev->port_in_redirect, 1); ngx_conf_merge_value(conf->msie_padding, prev->msie_padding, 1); ngx_conf_merge_value(conf->msie_refresh, prev->msie_refresh, 0); ngx_conf_merge_value(conf->log_not_found, prev->log_not_found, 1); + ngx_conf_merge_value(conf->log_subrequest, prev->log_subrequest, 0); ngx_conf_merge_value(conf->recursive_error_pages, prev->recursive_error_pages, 0); + ngx_conf_merge_value(conf->server_tokens, prev->server_tokens, 1); + ngx_conf_merge_value(conf->chunked_transfer_encoding, + prev->chunked_transfer_encoding, 1); + + ngx_conf_merge_ptr_value(conf->open_file_cache, + prev->open_file_cache, NULL); + + ngx_conf_merge_sec_value(conf->open_file_cache_valid, + prev->open_file_cache_valid, 60); + + ngx_conf_merge_uint_value(conf->open_file_cache_min_uses, + prev->open_file_cache_min_uses, 1); + + ngx_conf_merge_sec_value(conf->open_file_cache_errors, + prev->open_file_cache_errors, 0); + + ngx_conf_merge_sec_value(conf->open_file_cache_events, + prev->open_file_cache_events, 0); +#if (NGX_HTTP_GZIP) + + ngx_conf_merge_value(conf->gzip_vary, prev->gzip_vary, 0); + ngx_conf_merge_uint_value(conf->gzip_http_version, prev->gzip_http_version, + NGX_HTTP_VERSION_11); + ngx_conf_merge_bitmask_value(conf->gzip_proxied, prev->gzip_proxied, + (NGX_CONF_BITMASK_SET|NGX_HTTP_GZIP_PROXIED_OFF)); - if (conf->open_files == NULL) { - conf->open_files = prev->open_files; +#if (NGX_PCRE) + ngx_conf_merge_ptr_value(conf->gzip_disable, prev->gzip_disable, NULL); +#endif + + if (conf->gzip_disable_msie6 == 3) { + conf->gzip_disable_msie6 = + (prev->gzip_disable_msie6 == 3) ? 0 : prev->gzip_disable_msie6; + } + +#if (NGX_HTTP_DEGRADATION) + + if (conf->gzip_disable_degradation == 3) { + conf->gzip_disable_degradation = + (prev->gzip_disable_degradation == 3) ? + 0 : prev->gzip_disable_degradation; } +#endif +#endif + return NGX_CONF_OK; } -/* AF_INET only */ - static char * ngx_http_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { - ngx_http_core_srv_conf_t *scf = conf; + ngx_http_core_srv_conf_t *cscf = conf; - ngx_str_t *value, size; - ngx_url_t u; - ngx_uint_t n; - ngx_http_listen_t *ls; + ngx_str_t *value, size; + ngx_url_t u; + ngx_uint_t n; + ngx_http_listen_opt_t lsopt; - /* - * TODO: check duplicate 'listen' directives, - * add resolved name to server names ??? - */ + cscf->listen = 1; value = cf->args->elts; @@ -2578,7 +3411,7 @@ u.listen = 1; u.default_port = 80; - if (ngx_parse_url(cf, &u) != NGX_OK) { + if (ngx_parse_url(cf->pool, &u) != NGX_OK) { if (u.err) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%s in \"%V\" of the \"listen\" directive", @@ -2588,57 +3421,56 @@ return NGX_CONF_ERROR; } - ls = ngx_array_push(&scf->listen); - if (ls == NULL) { - return NGX_CONF_ERROR; - } - - ngx_memzero(ls, sizeof(ngx_http_listen_t)); + ngx_memzero(&lsopt, sizeof(ngx_http_listen_opt_t)); - ls->family = AF_INET; - ls->addr = u.addr.in_addr; - ls->port = u.port; - ls->file_name = cf->conf_file->file.name.data; - ls->line = cf->conf_file->line; - ls->conf.backlog = NGX_LISTEN_BACKLOG; - ls->conf.rcvbuf = -1; - ls->conf.sndbuf = -1; + ngx_memcpy(&lsopt.u.sockaddr, u.sockaddr, u.socklen); - n = ngx_inet_ntop(AF_INET, &ls->addr, ls->conf.addr, INET_ADDRSTRLEN + 6); - ngx_sprintf(&ls->conf.addr[n], ":%ui", ls->port); - - if (cf->args->nelts == 2) { - return NGX_CONF_OK; - } - - if (ngx_strcmp(value[2].data, "default") == 0) { - ls->conf.default_server = 1; - n = 3; + lsopt.socklen = u.socklen; + lsopt.backlog = NGX_LISTEN_BACKLOG; + lsopt.rcvbuf = -1; + lsopt.sndbuf = -1; +#if (NGX_HAVE_SETFIB) + lsopt.setfib = -1; +#endif + lsopt.wildcard = u.wildcard; - } else { - n = 2; - } + (void) ngx_sock_ntop(&lsopt.u.sockaddr, lsopt.addr, + NGX_SOCKADDR_STRLEN, 1); - for ( /* void */ ; n < cf->args->nelts; n++) { + for (n = 2; n < cf->args->nelts; n++) { - if (ls->conf.default_server == 0) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "\"%V\" parameter can be specified for " - "the default \"listen\" directive only", - &value[n]); - return NGX_CONF_ERROR; + if (ngx_strcmp(value[n].data, "default_server") == 0 + || ngx_strcmp(value[n].data, "default") == 0) + { + lsopt.default_server = 1; + continue; } if (ngx_strcmp(value[n].data, "bind") == 0) { - ls->conf.bind = 1; + lsopt.set = 1; + lsopt.bind = 1; continue; } +#if (NGX_HAVE_SETFIB) + if (ngx_strncmp(value[n].data, "setfib=", 7) == 0) { + lsopt.setfib = ngx_atoi(value[n].data + 7, value[n].len - 7); + + if (lsopt.setfib == NGX_ERROR) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid setfib \"%V\"", &value[n]); + return NGX_CONF_ERROR; + } + + continue; + } +#endif if (ngx_strncmp(value[n].data, "backlog=", 8) == 0) { - ls->conf.backlog = ngx_atoi(value[n].data + 8, value[n].len - 8); - ls->conf.bind = 1; + lsopt.backlog = ngx_atoi(value[n].data + 8, value[n].len - 8); + lsopt.set = 1; + lsopt.bind = 1; - if (ls->conf.backlog == NGX_ERROR || ls->conf.backlog == 0) { + if (lsopt.backlog == NGX_ERROR || lsopt.backlog == 0) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid backlog \"%V\"", &value[n]); return NGX_CONF_ERROR; @@ -2651,10 +3483,11 @@ size.len = value[n].len - 7; size.data = value[n].data + 7; - ls->conf.rcvbuf = ngx_parse_size(&size); - ls->conf.bind = 1; + lsopt.rcvbuf = ngx_parse_size(&size); + lsopt.set = 1; + lsopt.bind = 1; - if (ls->conf.rcvbuf == NGX_ERROR) { + if (lsopt.rcvbuf == NGX_ERROR) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid rcvbuf \"%V\"", &value[n]); return NGX_CONF_ERROR; @@ -2667,10 +3500,11 @@ size.len = value[n].len - 7; size.data = value[n].data + 7; - ls->conf.sndbuf = ngx_parse_size(&size); - ls->conf.bind = 1; + lsopt.sndbuf = ngx_parse_size(&size); + lsopt.set = 1; + lsopt.bind = 1; - if (ls->conf.sndbuf == NGX_ERROR) { + if (lsopt.sndbuf == NGX_ERROR) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid sndbuf \"%V\"", &value[n]); return NGX_CONF_ERROR; @@ -2681,8 +3515,9 @@ if (ngx_strncmp(value[n].data, "accept_filter=", 14) == 0) { #if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER) - ls->conf.accept_filter = (char *) &value[n].data[14]; - ls->conf.bind = 1; + lsopt.accept_filter = (char *) &value[n].data[14]; + lsopt.set = 1; + lsopt.bind = 1; #else ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "accept filters \"%V\" are not supported " @@ -2694,8 +3529,9 @@ if (ngx_strcmp(value[n].data, "deferred") == 0) { #if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT) - ls->conf.deferred_accept = 1; - ls->conf.bind = 1; + lsopt.deferred_accept = 1; + lsopt.set = 1; + lsopt.bind = 1; #else ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "the deferred accept is not supported " @@ -2704,12 +3540,67 @@ continue; } + if (ngx_strncmp(value[n].data, "ipv6only=o", 10) == 0) { +#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY) + struct sockaddr *sa; + + sa = &lsopt.u.sockaddr; + + if (sa->sa_family == AF_INET6) { + + if (ngx_strcmp(&value[n].data[10], "n") == 0) { + lsopt.ipv6only = 1; + + } else if (ngx_strcmp(&value[n].data[10], "ff") == 0) { + lsopt.ipv6only = 2; + + } else { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid ipv6only flags \"%s\"", + &value[n].data[9]); + return NGX_CONF_ERROR; + } + + lsopt.set = 1; + lsopt.bind = 1; + + } else { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "ipv6only is not supported " + "on addr \"%s\", ignored", lsopt.addr); + } + + continue; +#else + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "bind ipv6only is not supported " + "on this platform"); + return NGX_CONF_ERROR; +#endif + } + + if (ngx_strcmp(value[n].data, "ssl") == 0) { +#if (NGX_HTTP_SSL) + lsopt.ssl = 1; + continue; +#else + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "the \"ssl\" parameter requires " + "ngx_http_ssl_module"); + return NGX_CONF_ERROR; +#endif + } + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "the invalid \"%V\" parameter", &value[n]); return NGX_CONF_ERROR; } - return NGX_CONF_OK; + if (ngx_http_add_listen(cf, cscf, &lsopt) == NGX_OK) { + return NGX_CONF_OK; + } + + return NGX_CONF_ERROR; } @@ -2722,30 +3613,12 @@ ngx_str_t *value, name; ngx_uint_t i; ngx_http_server_name_t *sn; -#if (NGX_PCRE) - ngx_str_t err; - u_char errstr[NGX_MAX_CONF_ERRSTR]; -#endif value = cf->args->elts; ch = value[1].data[0]; - if (cscf->server_name.data == NULL && value[1].len) { - if (ngx_strchr(value[1].data, '*')) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "first server name \"%V\" must not be wildcard", - &value[1]); - return NGX_CONF_ERROR; - } - - if (value[1].data[0] == '~') { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "first server name \"%V\" " - "must not be regular expression", &value[1]); - return NGX_CONF_ERROR; - } - + if (cscf->server_name.data == NULL) { name = value[1]; if (ch == '.') { @@ -2764,13 +3637,7 @@ ch = value[i].data[0]; - if (value[i].len == 1 && ch == '*') { - cscf->wildcard = 1; - continue; - } - - if (value[i].len == 0 - || (ch == '*' && (value[i].len < 3 || value[i].data[1] != '.')) + if ((ch == '*' && (value[i].len < 3 || value[i].data[1] != '.')) || (ch == '.' && value[i].len < 2)) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, @@ -2784,6 +3651,13 @@ &value[i]); } + if (value[i].len == 1 && ch == '*') { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "\"server_name *\" is unsupported, use " + "\"server_name_in_redirect off\" instead"); + return NGX_CONF_ERROR; + } + sn = ngx_array_push(&cscf->server_names); if (sn == NULL) { return NGX_CONF_ERROR; @@ -2792,32 +3666,50 @@ #if (NGX_PCRE) sn->regex = NULL; #endif - sn->core_srv_conf = cscf; - sn->name.len = value[i].len; - sn->name.data = value[i].data; + sn->server = cscf; + sn->name = value[i]; if (value[i].data[0] != '~') { + ngx_strlow(sn->name.data, sn->name.data, sn->name.len); continue; } #if (NGX_PCRE) - err.len = NGX_MAX_CONF_ERRSTR; - err.data = errstr; + { + u_char *p; + ngx_regex_compile_t rc; + u_char errstr[NGX_MAX_CONF_ERRSTR]; + + if (value[i].len == 1) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "empty regex in server name \"%V\"", &value[i]); + return NGX_CONF_ERROR; + } value[i].len--; value[i].data++; - sn->regex = ngx_regex_compile(&value[i], NGX_REGEX_CASELESS, cf->pool, - &err); + ngx_memzero(&rc, sizeof(ngx_regex_compile_t)); + + rc.pattern = value[i]; + rc.err.len = NGX_MAX_CONF_ERRSTR; + rc.err.data = errstr; + + for (p = value[i].data; p < value[i].data + value[i].len; p++) { + if (*p >= 'A' && *p <= 'Z') { + rc.options = NGX_REGEX_CASELESS; + break; + } + } + sn->regex = ngx_http_regex_compile(cf, &rc); if (sn->regex == NULL) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%s", err.data); return NGX_CONF_ERROR; } - sn->name.len = value[i].len; - sn->name.data = value[i].data; - + sn->name = value[i]; + cscf->captures = (rc.captures > 0); + } #else ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "the using of the regex \"%V\" " @@ -2834,19 +3726,18 @@ static char * ngx_http_core_root(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { - ngx_http_core_loc_conf_t *lcf = conf; + ngx_http_core_loc_conf_t *clcf = conf; ngx_str_t *value; - ngx_uint_t alias, n; + ngx_int_t alias; + ngx_uint_t n; ngx_http_script_compile_t sc; alias = (cmd->name.len == sizeof("alias") - 1) ? 1 : 0; - if (lcf->root.data) { - - /* the (ngx_uint_t) cast is required by gcc 2.7.2.3 */ + if (clcf->root.data) { - if ((ngx_uint_t) lcf->alias == alias) { + if ((clcf->alias != 0) == alias) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "\"%V\" directive is duplicate", &cmd->name); @@ -2854,13 +3745,13 @@ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "\"%V\" directive is duplicate, " "\"%s\" directive is specified before", - &cmd->name, lcf->alias ? "alias" : "root"); + &cmd->name, clcf->alias ? "alias" : "root"); } return NGX_CONF_ERROR; } - if (lcf->named && alias) { + if (clcf->named && alias) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "the \"alias\" directive may not be used " "inside named location"); @@ -2868,62 +3759,65 @@ return NGX_CONF_ERROR; } -#if (NGX_PCRE) + value = cf->args->elts; - if (lcf->regex && alias) { + if (ngx_strstr(value[1].data, "$document_root") + || ngx_strstr(value[1].data, "${document_root}")) + { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "the \"alias\" directive may not be used " - "inside location given by regular expression"); + "the $document_root variable may not be used " + "in the \"%V\" directive", + &cmd->name); return NGX_CONF_ERROR; } -#endif - - value = cf->args->elts; - - if (ngx_strstr(value[1].data, "$document_root") - || ngx_strstr(value[1].data, "${document_root}")) + if (ngx_strstr(value[1].data, "$realpath_root") + || ngx_strstr(value[1].data, "${realpath_root}")) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "the $document_root variable may not be used " + "the $realpath_root variable may not be used " "in the \"%V\" directive", &cmd->name); return NGX_CONF_ERROR; } - lcf->alias = alias; - lcf->root = value[1]; + clcf->alias = alias ? clcf->name.len : 0; + clcf->root = value[1]; - if (!alias && lcf->root.data[lcf->root.len - 1] == '/') { - lcf->root.len--; + if (!alias && clcf->root.data[clcf->root.len - 1] == '/') { + clcf->root.len--; } - if (lcf->root.data[0] != '$') { - if (ngx_conf_full_name(cf->cycle, &lcf->root) == NGX_ERROR) { + if (clcf->root.data[0] != '$') { + if (ngx_conf_full_name(cf->cycle, &clcf->root, 0) != NGX_OK) { return NGX_CONF_ERROR; } } - n = ngx_http_script_variables_count(&lcf->root); + n = ngx_http_script_variables_count(&clcf->root); - if (n == 0) { - return NGX_CONF_OK; - } + ngx_memzero(&sc, sizeof(ngx_http_script_compile_t)); + sc.variables = n; - ngx_memzero(&sc, sizeof(ngx_http_script_compile_t)); +#if (NGX_PCRE) + if (alias && clcf->regex) { + n = 1; + } +#endif - sc.cf = cf; - sc.source = &lcf->root; - sc.lengths = &lcf->root_lengths; - sc.values = &lcf->root_values; - sc.variables = n; - sc.complete_lengths = 1; - sc.complete_values = 1; + if (n) { + sc.cf = cf; + sc.source = &clcf->root; + sc.lengths = &clcf->root_lengths; + sc.values = &clcf->root_values; + sc.complete_lengths = 1; + sc.complete_values = 1; - if (ngx_http_script_compile(&sc) != NGX_OK) { - return NGX_CONF_ERROR; + if (ngx_http_script_compile(&sc) != NGX_OK) { + return NGX_CONF_ERROR; + } } return NGX_CONF_OK; @@ -2944,6 +3838,7 @@ { (u_char *) "PROPPATCH", (uint32_t) ~NGX_HTTP_PROPPATCH }, { (u_char *) "LOCK", (uint32_t) ~NGX_HTTP_LOCK }, { (u_char *) "UNLOCK", (uint32_t) ~NGX_HTTP_UNLOCK }, + { (u_char *) "PATCH", (uint32_t) ~NGX_HTTP_PATCH }, { NULL, 0 } }; @@ -2951,7 +3846,7 @@ static char * ngx_http_core_limit_except(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { - ngx_http_core_loc_conf_t *clcf = conf; + ngx_http_core_loc_conf_t *pclcf = conf; char *rv; void *mconf; @@ -2961,13 +3856,13 @@ ngx_http_module_t *module; ngx_http_conf_ctx_t *ctx, *pctx; ngx_http_method_name_t *name; - ngx_http_core_loc_conf_t *lcf, **clcfp; + ngx_http_core_loc_conf_t *clcf; - if (clcf->limit_except) { + if (pclcf->limit_except) { return "duplicate"; } - clcf->limit_except = 0xffffffff; + pclcf->limit_except = 0xffffffff; value = cf->args->elts; @@ -2975,7 +3870,7 @@ for (name = ngx_methods_names; name->name; name++) { if (ngx_strcasecmp(value[i].data, name->name) == 0) { - clcf->limit_except &= name->method; + pclcf->limit_except &= name->method; goto next; } } @@ -2988,8 +3883,8 @@ continue; } - if (!(clcf->limit_except & NGX_HTTP_GET)) { - clcf->limit_except &= (uint32_t) ~NGX_HTTP_HEAD; + if (!(pclcf->limit_except & NGX_HTTP_GET)) { + pclcf->limit_except &= (uint32_t) ~NGX_HTTP_HEAD; } ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t)); @@ -3025,27 +3920,17 @@ } - lcf = ctx->loc_conf[ngx_http_core_module.ctx_index]; - clcf->limit_except_loc_conf = ctx->loc_conf; - lcf->loc_conf = ctx->loc_conf; - lcf->name = clcf->name; - lcf->noname = 1; - - if (clcf->locations == NULL) { - clcf->locations = ngx_array_create(cf->pool, 2, sizeof(void *)); - if (clcf->locations == NULL) { - return NGX_CONF_ERROR; - } - } + clcf = ctx->loc_conf[ngx_http_core_module.ctx_index]; + pclcf->limit_except_loc_conf = ctx->loc_conf; + clcf->loc_conf = ctx->loc_conf; + clcf->name = pclcf->name; + clcf->noname = 1; + clcf->lmt_excpt = 1; - clcfp = ngx_array_push(clcf->locations); - if (clcfp == NULL) { + if (ngx_http_add_location(cf, &pclcf->locations, clcf) != NGX_OK) { return NGX_CONF_ERROR; } - *clcfp = lcf; - - save = *cf; cf->ctx = ctx; cf->cmd_type = NGX_HTTP_LMT_CONF; @@ -3059,21 +3944,49 @@ static char * -ngx_http_core_error_page(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +ngx_http_core_directio(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { - ngx_http_core_loc_conf_t *lcf = conf; + ngx_http_core_loc_conf_t *clcf = conf; - ngx_int_t overwrite; - ngx_str_t *value, uri; - ngx_uint_t i, n, nvar; - ngx_array_t *uri_lengths, *uri_values; - ngx_http_err_page_t *err; - ngx_http_script_compile_t sc; + ngx_str_t *value; + + if (clcf->directio != NGX_CONF_UNSET) { + return "is duplicate"; + } + + value = cf->args->elts; - if (lcf->error_pages == NULL) { - lcf->error_pages = ngx_array_create(cf->pool, 4, - sizeof(ngx_http_err_page_t)); - if (lcf->error_pages == NULL) { + if (ngx_strcmp(value[1].data, "off") == 0) { + clcf->directio = NGX_OPEN_FILE_DIRECTIO_OFF; + return NGX_CONF_OK; + } + + clcf->directio = ngx_parse_offset(&value[1]); + if (clcf->directio == (off_t) NGX_ERROR) { + return "invalid value"; + } + + return NGX_CONF_OK; +} + + +static char * +ngx_http_core_error_page(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_http_core_loc_conf_t *clcf = conf; + + u_char *p; + ngx_int_t overwrite; + ngx_str_t *value, uri, args; + ngx_uint_t i, n; + ngx_http_err_page_t *err; + ngx_http_complex_value_t cv; + ngx_http_compile_complex_value_t ccv; + + if (clcf->error_pages == NULL) { + clcf->error_pages = ngx_array_create(cf->pool, 4, + sizeof(ngx_http_err_page_t)); + if (clcf->error_pages == NULL) { return NGX_CONF_ERROR; } } @@ -3110,29 +4023,33 @@ } uri = value[cf->args->nelts - 1]; - uri_lengths = NULL; - uri_values = NULL; - nvar = ngx_http_script_variables_count(&uri); + ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); - if (nvar) { - ngx_memzero(&sc, sizeof(ngx_http_script_compile_t)); + ccv.cf = cf; + ccv.value = &uri; + ccv.complex_value = &cv; - sc.cf = cf; - sc.source = &uri; - sc.lengths = &uri_lengths; - sc.values = &uri_values; - sc.variables = nvar; - sc.complete_lengths = 1; - sc.complete_values = 1; + if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { + return NGX_CONF_ERROR; + } - if (ngx_http_script_compile(&sc) != NGX_OK) { - return NGX_CONF_ERROR; + ngx_str_null(&args); + + if (cv.lengths == NULL && uri.data[0] == '/') { + p = (u_char *) ngx_strchr(uri.data, '?'); + + if (p) { + cv.value.len = p - uri.data; + cv.value.data = uri.data; + p++; + args.len = (uri.data + uri.len) - p; + args.data = p; } } for (i = 1; i < cf->args->nelts - n; i++) { - err = ngx_array_push(lcf->error_pages); + err = ngx_array_push(clcf->error_pages); if (err == NULL) { return NGX_CONF_ERROR; } @@ -3145,18 +4062,108 @@ return NGX_CONF_ERROR; } - if (err->status < 400 || err->status > 599) { + if (err->status < 300 || err->status > 599) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "value \"%V\" must be between 400 and 599", + "value \"%V\" must be between 300 and 599", &value[i]); return NGX_CONF_ERROR; } - err->overwrite = (overwrite >= 0) ? overwrite : err->status; + err->overwrite = overwrite; + + if (overwrite == -1) { + switch (err->status) { + case NGX_HTTP_TO_HTTPS: + case NGX_HTTPS_CERT_ERROR: + case NGX_HTTPS_NO_CERT: + err->overwrite = NGX_HTTP_BAD_REQUEST; + default: + break; + } + } + + err->value = cv; + err->args = args; + } + + return NGX_CONF_OK; +} + + +static char * +ngx_http_core_try_files(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_http_core_loc_conf_t *clcf = conf; + + ngx_str_t *value; + ngx_int_t code; + ngx_uint_t i, n; + ngx_http_try_file_t *tf; + ngx_http_script_compile_t sc; + ngx_http_core_main_conf_t *cmcf; + + if (clcf->try_files) { + return "is duplicate"; + } + + cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); + + cmcf->try_files = 1; + + tf = ngx_pcalloc(cf->pool, cf->args->nelts * sizeof(ngx_http_try_file_t)); + if (tf == NULL) { + return NGX_CONF_ERROR; + } + + clcf->try_files = tf; + + value = cf->args->elts; + + for (i = 0; i < cf->args->nelts - 1; i++) { + + tf[i].name = value[i + 1]; + + if (tf[i].name.data[tf[i].name.len - 1] == '/') { + tf[i].test_dir = 1; + tf[i].name.len--; + tf[i].name.data[tf[i].name.len] = '\0'; + } + + n = ngx_http_script_variables_count(&tf[i].name); + + if (n) { + ngx_memzero(&sc, sizeof(ngx_http_script_compile_t)); + + sc.cf = cf; + sc.source = &tf[i].name; + sc.lengths = &tf[i].lengths; + sc.values = &tf[i].values; + sc.variables = n; + sc.complete_lengths = 1; + sc.complete_values = 1; + + if (ngx_http_script_compile(&sc) != NGX_OK) { + return NGX_CONF_ERROR; + } + + } else { + /* add trailing '\0' to length */ + tf[i].name.len++; + } + } + + if (tf[i - 1].name.data[0] == '=') { + + code = ngx_atoi(tf[i - 1].name.data + 1, tf[i - 1].name.len - 2); + + if (code == NGX_ERROR) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid code \"%*s\"", + tf[i - 1].name.len - 1, tf[i - 1].name.data); + return NGX_CONF_ERROR; + } - err->uri = uri; - err->uri_lengths = uri_lengths; - err->uri_values = uri_values; + tf[i].code = code; } return NGX_CONF_OK; @@ -3164,39 +4171,130 @@ static char * +ngx_http_core_open_file_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_http_core_loc_conf_t *clcf = conf; + + time_t inactive; + ngx_str_t *value, s; + ngx_int_t max; + ngx_uint_t i; + + if (clcf->open_file_cache != NGX_CONF_UNSET_PTR) { + return "is duplicate"; + } + + value = cf->args->elts; + + max = 0; + inactive = 60; + + 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 < 0) { + goto failed; + } + + continue; + } + + if (ngx_strcmp(value[i].data, "off") == 0) { + + clcf->open_file_cache = NULL; + + continue; + } + + failed: + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid \"open_file_cache\" parameter \"%V\"", + &value[i]); + return NGX_CONF_ERROR; + } + + if (clcf->open_file_cache == NULL) { + return NGX_CONF_OK; + } + + if (max == 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "\"open_file_cache\" must have \"max\" parameter"); + return NGX_CONF_ERROR; + } + + clcf->open_file_cache = ngx_open_file_cache_init(cf->pool, max, inactive); + if (clcf->open_file_cache) { + return NGX_CONF_OK; + } + + return NGX_CONF_ERROR; +} + + +static char * ngx_http_core_error_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { - ngx_http_core_loc_conf_t *lcf = conf; + ngx_http_core_loc_conf_t *clcf = conf; + + ngx_str_t *value; + + if (clcf->error_log) { + return "is duplicate"; + } + + value = cf->args->elts; - lcf->err_log = ngx_log_create_errlog(cf->cycle, cf->args); - if (lcf->err_log == NULL) { + clcf->error_log = ngx_log_create(cf->cycle, &value[1]); + if (clcf->error_log == NULL) { return NGX_CONF_ERROR; } - return ngx_set_error_log_levels(cf, lcf->err_log); + if (cf->args->nelts == 2) { + clcf->error_log->log_level = NGX_LOG_ERR; + return NGX_CONF_OK; + } + + return ngx_log_set_levels(cf, clcf->error_log); } static char * ngx_http_core_keepalive(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { - ngx_http_core_loc_conf_t *lcf = conf; + ngx_http_core_loc_conf_t *clcf = conf; ngx_str_t *value; - if (lcf->keepalive_timeout != NGX_CONF_UNSET_MSEC) { + if (clcf->keepalive_timeout != NGX_CONF_UNSET_MSEC) { return "is duplicate"; } value = cf->args->elts; - lcf->keepalive_timeout = ngx_parse_time(&value[1], 0); + clcf->keepalive_timeout = ngx_parse_time(&value[1], 0); - if (lcf->keepalive_timeout == (ngx_msec_t) NGX_ERROR) { + if (clcf->keepalive_timeout == (ngx_msec_t) NGX_ERROR) { return "invalid value"; } - if (lcf->keepalive_timeout == (ngx_msec_t) NGX_PARSE_LARGE_TIME) { + if (clcf->keepalive_timeout == (ngx_msec_t) NGX_PARSE_LARGE_TIME) { return "value must be less than 597 hours"; } @@ -3204,13 +4302,13 @@ return NGX_CONF_OK; } - lcf->keepalive_header = ngx_parse_time(&value[2], 1); + clcf->keepalive_header = ngx_parse_time(&value[2], 1); - if (lcf->keepalive_header == NGX_ERROR) { + if (clcf->keepalive_header == NGX_ERROR) { return "invalid value"; } - if (lcf->keepalive_header == NGX_PARSE_LARGE_TIME) { + if (clcf->keepalive_header == NGX_PARSE_LARGE_TIME) { return "value must be less than 68 years"; } @@ -3221,17 +4319,152 @@ static char * ngx_http_core_internal(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { - ngx_http_core_loc_conf_t *lcf = conf; + ngx_http_core_loc_conf_t *clcf = conf; + + if (clcf->internal != NGX_CONF_UNSET) { + return "is duplicate"; + } + + clcf->internal = 1; + + return NGX_CONF_OK; +} + + +static char * +ngx_http_core_resolver(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_http_core_loc_conf_t *clcf = conf; + + ngx_url_t u; + ngx_str_t *value; - if (lcf->internal != NGX_CONF_UNSET) { + if (clcf->resolver) { return "is duplicate"; } - lcf->internal = 1; + value = cf->args->elts; + + ngx_memzero(&u, sizeof(ngx_url_t)); + + u.host = value[1]; + u.port = 53; + + if (ngx_inet_resolve_host(cf->pool, &u) != NGX_OK) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%V: %s", &u.host, u.err); + return NGX_CONF_ERROR; + } + + clcf->resolver = ngx_resolver_create(cf, &u.addrs[0]); + if (clcf->resolver == NULL) { + return NGX_OK; + } + + return NGX_CONF_OK; +} + + +#if (NGX_HTTP_GZIP) + +static char * +ngx_http_gzip_disable(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_http_core_loc_conf_t *clcf = conf; + +#if (NGX_PCRE) + + ngx_str_t *value; + ngx_uint_t i; + ngx_regex_elt_t *re; + ngx_regex_compile_t rc; + u_char errstr[NGX_MAX_CONF_ERRSTR]; + + if (clcf->gzip_disable == NGX_CONF_UNSET_PTR) { + clcf->gzip_disable = ngx_array_create(cf->pool, 2, + sizeof(ngx_regex_elt_t)); + if (clcf->gzip_disable == NULL) { + return NGX_CONF_ERROR; + } + } + + value = cf->args->elts; + + ngx_memzero(&rc, sizeof(ngx_regex_compile_t)); + + rc.pool = cf->pool; + rc.err.len = NGX_MAX_CONF_ERRSTR; + rc.err.data = errstr; + + for (i = 1; i < cf->args->nelts; i++) { + + if (ngx_strcmp(value[i].data, "msie6") == 0) { + clcf->gzip_disable_msie6 = 1; + continue; + } + +#if (NGX_HTTP_DEGRADATION) + + if (ngx_strcmp(value[i].data, "degradation") == 0) { + clcf->gzip_disable_degradation = 1; + continue; + } + +#endif + + re = ngx_array_push(clcf->gzip_disable); + if (re == NULL) { + return NGX_CONF_ERROR; + } + + rc.pattern = value[i]; + rc.options = NGX_REGEX_CASELESS; + + if (ngx_regex_compile(&rc) != NGX_OK) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%V", &rc.err); + return NGX_CONF_ERROR; + } + + re->regex = rc.regex; + re->name = value[i].data; + } + + return NGX_CONF_OK; + +#else + ngx_str_t *value; + ngx_uint_t i; + + value = cf->args->elts; + + for (i = 1; i < cf->args->nelts; i++) { + if (ngx_strcmp(value[i].data, "msie6") == 0) { + clcf->gzip_disable_msie6 = 1; + continue; + } + +#if (NGX_HTTP_DEGRADATION) + + if (ngx_strcmp(value[i].data, "degradation") == 0) { + clcf->gzip_disable_degradation = 1; + continue; + } + +#endif + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "without PCRE library \"gzip_disable\" supports " + "builtin \"msie6\" and \"degradation\" mask only"); + + return NGX_CONF_ERROR; + } return NGX_CONF_OK; + +#endif } +#endif + static char * ngx_http_core_lowat_check(ngx_conf_t *cf, void *post, void *data) @@ -3269,8 +4502,15 @@ if (*sp < NGX_MIN_POOL_SIZE) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "pool must be no less than %uz", NGX_MIN_POOL_SIZE); + "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; } diff -Nru nginx-0.5.33/src/http/ngx_http_core_module.h nginx-0.8.53/src/http/ngx_http_core_module.h --- nginx-0.5.33/src/http/ngx_http_core_module.h 2007-11-07 13:46:29.000000000 +0000 +++ nginx-0.8.53/src/http/ngx_http_core_module.h 2010-10-04 15:03:00.000000000 +0000 @@ -8,18 +8,72 @@ #define _NGX_HTTP_CORE_H_INCLUDED_ -#include -#include +#include +#include #include +#define NGX_HTTP_GZIP_PROXIED_OFF 0x0002 +#define NGX_HTTP_GZIP_PROXIED_EXPIRED 0x0004 +#define NGX_HTTP_GZIP_PROXIED_NO_CACHE 0x0008 +#define NGX_HTTP_GZIP_PROXIED_NO_STORE 0x0010 +#define NGX_HTTP_GZIP_PROXIED_PRIVATE 0x0020 +#define NGX_HTTP_GZIP_PROXIED_NO_LM 0x0040 +#define NGX_HTTP_GZIP_PROXIED_NO_ETAG 0x0080 +#define NGX_HTTP_GZIP_PROXIED_AUTH 0x0100 +#define NGX_HTTP_GZIP_PROXIED_ANY 0x0200 + + +#define NGX_HTTP_AIO_OFF 0 +#define NGX_HTTP_AIO_ON 1 +#define NGX_HTTP_AIO_SENDFILE 2 + + +#define NGX_HTTP_SATISFY_ALL 0 +#define NGX_HTTP_SATISFY_ANY 1 + + +#define NGX_HTTP_IMS_OFF 0 +#define NGX_HTTP_IMS_EXACT 1 +#define NGX_HTTP_IMS_BEFORE 2 + + +typedef struct ngx_http_location_tree_node_s ngx_http_location_tree_node_t; +typedef struct ngx_http_core_loc_conf_s ngx_http_core_loc_conf_t; + + typedef struct { + union { + struct sockaddr sockaddr; + struct sockaddr_in sockaddr_in; +#if (NGX_HAVE_INET6) + struct sockaddr_in6 sockaddr_in6; +#endif +#if (NGX_HAVE_UNIX_DOMAIN) + struct sockaddr_un sockaddr_un; +#endif + u_char sockaddr_data[NGX_SOCKADDRLEN]; + } u; + + socklen_t socklen; + + unsigned set:1; unsigned default_server:1; unsigned bind:1; + unsigned wildcard:1; +#if (NGX_HTTP_SSL) + unsigned ssl:1; +#endif +#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY) + unsigned ipv6only:2; +#endif int backlog; int rcvbuf; int sndbuf; +#if (NGX_HAVE_SETFIB) + int setfib; +#endif #if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER) char *accept_filter; @@ -28,21 +82,8 @@ ngx_uint_t deferred_accept; #endif - u_char addr[INET_ADDRSTRLEN + 6]; - -} ngx_http_listen_conf_t; - - -typedef struct { - in_addr_t addr; - in_port_t port; - int family; - - u_char *file_name; - ngx_uint_t line; - - ngx_http_listen_conf_t conf; -} ngx_http_listen_t; + u_char addr[NGX_SOCKADDR_STRLEN + 1]; +} ngx_http_listen_opt_t; typedef enum { @@ -59,6 +100,7 @@ NGX_HTTP_ACCESS_PHASE, NGX_HTTP_POST_ACCESS_PHASE, + NGX_HTTP_TRY_FILES_PHASE, NGX_HTTP_CONTENT_PHASE, NGX_HTTP_LOG_PHASE @@ -98,6 +140,7 @@ ngx_hash_t variables_hash; ngx_array_t variables; /* ngx_http_variable_t */ + ngx_uint_t ncaptures; ngx_uint_t server_names_hash_max_size; ngx_uint_t server_names_hash_bucket_size; @@ -107,42 +150,41 @@ ngx_hash_keys_arrays_t *variables_keys; + ngx_array_t *ports; + + ngx_uint_t try_files; /* unsigned try_files:1 */ + ngx_http_phase_t phases[NGX_HTTP_LOG_PHASE + 1]; } ngx_http_core_main_conf_t; typedef struct { - /* - * array of the ngx_http_core_loc_conf_t *, - * used in the ngx_http_core_find_location() and in the merge phase - */ - ngx_array_t locations; - - unsigned regex_start:15; - unsigned named_start:15; - unsigned wildcard:1; - - /* array of the ngx_http_listen_t, "listen" directive */ - ngx_array_t listen; - /* array of the ngx_http_server_name_t, "server_name" directive */ - ngx_array_t server_names; + ngx_array_t server_names; /* server ctx */ - ngx_http_conf_ctx_t *ctx; + ngx_http_conf_ctx_t *ctx; - ngx_str_t server_name; + ngx_str_t server_name; - size_t connection_pool_size; - size_t request_pool_size; - size_t client_header_buffer_size; + size_t connection_pool_size; + size_t request_pool_size; + size_t client_header_buffer_size; - ngx_bufs_t large_client_header_buffers; + ngx_bufs_t large_client_header_buffers; - ngx_msec_t client_header_timeout; + ngx_msec_t client_header_timeout; - ngx_flag_t optimize_server_names; - ngx_flag_t ignore_invalid_headers; + ngx_flag_t ignore_invalid_headers; + ngx_flag_t merge_slashes; + ngx_flag_t underscores_in_headers; + + unsigned listen:1; +#if (NGX_PCRE) + unsigned captures:1; +#endif + + ngx_http_core_loc_conf_t **named_locations; } ngx_http_core_srv_conf_t; @@ -150,58 +192,70 @@ typedef struct { - in_addr_t addr; - /* the default server configuration for this address:port */ - ngx_http_core_srv_conf_t *core_srv_conf; + ngx_http_core_srv_conf_t *default_server; ngx_http_virtual_names_t *virtual_names; + +#if (NGX_HTTP_SSL) + ngx_uint_t ssl; /* unsigned ssl:1; */ +#endif +} ngx_http_addr_conf_t; + + +typedef struct { + in_addr_t addr; + ngx_http_addr_conf_t conf; } ngx_http_in_addr_t; +#if (NGX_HAVE_INET6) + typedef struct { - in_port_t port; - ngx_str_t port_text; - ngx_http_in_addr_t *addrs; + struct in6_addr addr6; + ngx_http_addr_conf_t conf; +} ngx_http_in6_addr_t; + +#endif + + +typedef struct { + /* ngx_http_in_addr_t or ngx_http_in6_addr_t */ + void *addrs; ngx_uint_t naddrs; -} ngx_http_in_port_t; +} ngx_http_port_t; typedef struct { + ngx_int_t family; in_port_t port; - ngx_array_t addrs; /* array of ngx_http_conf_in_addr_t */ -} ngx_http_conf_in_port_t; + ngx_array_t addrs; /* array of ngx_http_conf_addr_t */ +} ngx_http_conf_port_t; typedef struct { - in_addr_t addr; + ngx_http_listen_opt_t opt; ngx_hash_t hash; ngx_hash_wildcard_t *wc_head; ngx_hash_wildcard_t *wc_tail; - ngx_array_t names; /* array of ngx_http_server_name_t */ - #if (NGX_PCRE) ngx_uint_t nregex; ngx_http_server_name_t *regex; #endif /* the default server configuration for this address:port */ - ngx_http_core_srv_conf_t *core_srv_conf; - - unsigned default_server:1; - unsigned bind:1; - - ngx_http_listen_conf_t *listen_conf; -} ngx_http_conf_in_addr_t; + ngx_http_core_srv_conf_t *default_server; + ngx_array_t servers; /* array of ngx_http_core_srv_conf_t */ +} ngx_http_conf_addr_t; struct ngx_http_server_name_s { #if (NGX_PCRE) - ngx_regex_t *regex; + ngx_http_regex_t *regex; #endif - ngx_http_core_srv_conf_t *core_srv_conf; /* virtual name server conf */ + ngx_http_core_srv_conf_t *server; /* virtual name server conf */ ngx_str_t name; }; @@ -209,43 +263,58 @@ typedef struct { ngx_int_t status; ngx_int_t overwrite; - ngx_str_t uri; - ngx_array_t *uri_lengths; - ngx_array_t *uri_values; + ngx_http_complex_value_t value; + ngx_str_t args; } ngx_http_err_page_t; -typedef struct ngx_http_core_loc_conf_s ngx_http_core_loc_conf_t; +typedef struct { + ngx_array_t *lengths; + ngx_array_t *values; + ngx_str_t name; + + unsigned code:10; + unsigned test_dir:1; +} ngx_http_try_file_t; + struct ngx_http_core_loc_conf_s { ngx_str_t name; /* location name */ #if (NGX_PCRE) - ngx_regex_t *regex; + ngx_http_regex_t *regex; #endif - unsigned regex_start:15; - unsigned noname:1; /* "if () {}" block or limit_except */ + unsigned lmt_excpt:1; unsigned named:1; unsigned exact_match:1; unsigned noregex:1; unsigned auto_redirect:1; - unsigned alias:1; +#if (NGX_HTTP_GZIP) + unsigned gzip_disable_msie6:2; +#if (NGX_HTTP_DEGRADATION) + unsigned gzip_disable_degradation:2; +#endif +#endif - /* array of inclusive ngx_http_core_loc_conf_t */ - ngx_array_t *locations; + ngx_http_location_tree_node_t *static_locations; +#if (NGX_PCRE) + ngx_http_core_loc_conf_t **regex_locations; +#endif /* pointer to the modules' loc_conf */ - void **loc_conf ; + void **loc_conf; uint32_t limit_except; - void **limit_except_loc_conf ; + void **limit_except_loc_conf; ngx_http_handler_pt handler; + /* location name length for inclusive location with inherited alias */ + size_t alias; ngx_str_t root; /* root, alias */ ngx_str_t post_action; @@ -257,54 +326,118 @@ ngx_str_t default_type; off_t client_max_body_size; /* client_max_body_size */ + off_t directio; /* directio */ + off_t directio_alignment; /* directio_alignment */ size_t client_body_buffer_size; /* client_body_buffer_size */ size_t send_lowat; /* send_lowat */ size_t postpone_output; /* postpone_output */ size_t limit_rate; /* limit_rate */ + size_t limit_rate_after; /* limit_rate_after */ size_t sendfile_max_chunk; /* sendfile_max_chunk */ + size_t read_ahead; /* read_ahead */ ngx_msec_t client_body_timeout; /* client_body_timeout */ ngx_msec_t send_timeout; /* send_timeout */ ngx_msec_t keepalive_timeout; /* keepalive_timeout */ ngx_msec_t lingering_time; /* lingering_time */ ngx_msec_t lingering_timeout; /* lingering_timeout */ + ngx_msec_t resolver_timeout; /* resolver_timeout */ + + ngx_resolver_t *resolver; /* resolver */ time_t keepalive_header; /* keepalive_timeout */ - ngx_flag_t satisfy_any; /* satisfy_any */ + ngx_uint_t keepalive_requests; /* keepalive_requests */ + ngx_uint_t satisfy; /* satisfy */ + ngx_uint_t if_modified_since; /* if_modified_since */ + ngx_uint_t client_body_in_file_only; /* client_body_in_file_only */ + + ngx_flag_t client_body_in_single_buffer; + /* client_body_in_singe_buffer */ ngx_flag_t internal; /* internal */ - ngx_flag_t client_body_in_file_only; /* client_body_in_file_only */ ngx_flag_t sendfile; /* sendfile */ +#if (NGX_HAVE_FILE_AIO) + ngx_flag_t aio; /* aio */ +#endif ngx_flag_t tcp_nopush; /* tcp_nopush */ ngx_flag_t tcp_nodelay; /* tcp_nodelay */ ngx_flag_t reset_timedout_connection; /* reset_timedout_connection */ + ngx_flag_t server_name_in_redirect; /* server_name_in_redirect */ ngx_flag_t port_in_redirect; /* port_in_redirect */ ngx_flag_t msie_padding; /* msie_padding */ ngx_flag_t msie_refresh; /* msie_refresh */ ngx_flag_t log_not_found; /* log_not_found */ + ngx_flag_t log_subrequest; /* log_subrequest */ ngx_flag_t recursive_error_pages; /* recursive_error_pages */ + ngx_flag_t server_tokens; /* server_tokens */ + ngx_flag_t chunked_transfer_encoding; /* chunked_transfer_encoding */ + +#if (NGX_HTTP_GZIP) + ngx_flag_t gzip_vary; /* gzip_vary */ + + ngx_uint_t gzip_http_version; /* gzip_http_version */ + ngx_uint_t gzip_proxied; /* gzip_proxied */ + +#if (NGX_PCRE) + ngx_array_t *gzip_disable; /* gzip_disable */ +#endif +#endif ngx_array_t *error_pages; /* error_page */ + ngx_http_try_file_t *try_files; /* try_files */ ngx_path_t *client_body_temp_path; /* client_body_temp_path */ - ngx_http_cache_hash_t *open_files; + ngx_open_file_cache_t *open_file_cache; + time_t open_file_cache_valid; + ngx_uint_t open_file_cache_min_uses; + ngx_flag_t open_file_cache_errors; + ngx_flag_t open_file_cache_events; - ngx_log_t *err_log; + ngx_log_t *error_log; ngx_uint_t types_hash_max_size; ngx_uint_t types_hash_bucket_size; + ngx_queue_t *locations; + #if 0 ngx_http_core_loc_conf_t *prev_location; #endif }; +typedef struct { + ngx_queue_t queue; + ngx_http_core_loc_conf_t *exact; + ngx_http_core_loc_conf_t *inclusive; + ngx_str_t *name; + u_char *file_name; + ngx_uint_t line; + ngx_queue_t list; +} ngx_http_location_queue_t; + + +struct ngx_http_location_tree_node_s { + ngx_http_location_tree_node_t *left; + ngx_http_location_tree_node_t *right; + ngx_http_location_tree_node_t *tree; + + ngx_http_core_loc_conf_t *exact; + ngx_http_core_loc_conf_t *inclusive; + + u_char auto_redirect; + u_char len; + u_char name[1]; +}; + + void ngx_http_core_run_phases(ngx_http_request_t *r); ngx_int_t ngx_http_core_generic_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph); +ngx_int_t ngx_http_core_rewrite_phase(ngx_http_request_t *r, + ngx_http_phase_handler_t *ph); ngx_int_t ngx_http_core_find_config_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph); ngx_int_t ngx_http_core_post_rewrite_phase(ngx_http_request_t *r, @@ -313,14 +446,24 @@ ngx_http_phase_handler_t *ph); ngx_int_t ngx_http_core_post_access_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph); +ngx_int_t ngx_http_core_try_files_phase(ngx_http_request_t *r, + ngx_http_phase_handler_t *ph); ngx_int_t ngx_http_core_content_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph); + +void *ngx_http_test_content_type(ngx_http_request_t *r, ngx_hash_t *types_hash); ngx_int_t ngx_http_set_content_type(ngx_http_request_t *r); -ngx_int_t ngx_http_set_exten(ngx_http_request_t *r); +void ngx_http_set_exten(ngx_http_request_t *r); +ngx_int_t ngx_http_send_response(ngx_http_request_t *r, ngx_uint_t status, + ngx_str_t *ct, ngx_http_complex_value_t *cv); u_char *ngx_http_map_uri_to_path(ngx_http_request_t *r, ngx_str_t *name, size_t *root_length, size_t reserved); ngx_int_t ngx_http_auth_basic_user(ngx_http_request_t *r); +#if (NGX_HTTP_GZIP) +ngx_int_t ngx_http_gzip_ok(ngx_http_request_t *r); +#endif + ngx_int_t ngx_http_subrequest(ngx_http_request_t *r, ngx_str_t *uri, ngx_str_t *args, ngx_http_request_t **sr, @@ -346,6 +489,8 @@ extern ngx_uint_t ngx_http_max_module; +extern ngx_str_t ngx_http_core_get_method; + #define ngx_http_clear_content_length(r) \ \ @@ -359,7 +504,7 @@ \ r->allow_ranges = 0; \ if (r->headers_out.accept_ranges) { \ - r->headers_out.accept_ranges->hash = 0 ; \ + r->headers_out.accept_ranges->hash = 0; \ r->headers_out.accept_ranges = NULL; \ } diff -Nru nginx-0.5.33/src/http/ngx_http_file_cache.c nginx-0.8.53/src/http/ngx_http_file_cache.c --- nginx-0.5.33/src/http/ngx_http_file_cache.c 2006-08-30 10:39:17.000000000 +0000 +++ nginx-0.8.53/src/http/ngx_http_file_cache.c 2010-09-02 14:31:47.000000000 +0000 @@ -7,253 +7,1814 @@ #include #include #include +#include -#if (NGX_HAVE_OPENSSL_MD5_H) -#include -#else -#include +static ngx_int_t ngx_http_file_cache_read(ngx_http_request_t *r, + ngx_http_cache_t *c); +static ssize_t ngx_http_file_cache_aio_read(ngx_http_request_t *r, + ngx_http_cache_t *c); +#if (NGX_HAVE_FILE_AIO) +static void ngx_http_cache_aio_event_handler(ngx_event_t *ev); #endif +static ngx_int_t ngx_http_file_cache_exists(ngx_http_file_cache_t *cache, + ngx_http_cache_t *c); +static ngx_int_t ngx_http_file_cache_name(ngx_http_request_t *r, + ngx_path_t *path); +static ngx_http_file_cache_node_t * + ngx_http_file_cache_lookup(ngx_http_file_cache_t *cache, u_char *key); +static void ngx_http_file_cache_rbtree_insert_value(ngx_rbtree_node_t *temp, + ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel); +static void ngx_http_file_cache_cleanup(void *data); +static time_t ngx_http_file_cache_forced_expire(ngx_http_file_cache_t *cache); +static time_t ngx_http_file_cache_expire(ngx_http_file_cache_t *cache); +static void ngx_http_file_cache_delete(ngx_http_file_cache_t *cache, + ngx_queue_t *q, u_char *name); +static ngx_int_t + ngx_http_file_cache_manager_sleep(ngx_http_file_cache_t *cache); +static ngx_int_t ngx_http_file_cache_noop(ngx_tree_ctx_t *ctx, + ngx_str_t *path); +static ngx_int_t ngx_http_file_cache_manage_file(ngx_tree_ctx_t *ctx, + ngx_str_t *path); +static ngx_int_t ngx_http_file_cache_add_file(ngx_tree_ctx_t *ctx, + ngx_str_t *path); +static ngx_int_t ngx_http_file_cache_add(ngx_http_file_cache_t *cache, + ngx_http_cache_t *c); +static ngx_int_t ngx_http_file_cache_delete_file(ngx_tree_ctx_t *ctx, + ngx_str_t *path); + + +ngx_str_t ngx_http_cache_status[] = { + ngx_string("MISS"), + ngx_string("BYPASS"), + ngx_string("EXPIRED"), + ngx_string("STALE"), + ngx_string("UPDATING"), + ngx_string("HIT") +}; -#if (NGX_OPENSSL_MD5) -#define MD5Init MD5_Init -#define MD5Update MD5_Update -#define MD5Final MD5_Final -#endif + +static u_char ngx_http_file_cache_key[] = { LF, 'K', 'E', 'Y', ':', ' ' }; -ngx_int_t ngx_http_file_cache_get(ngx_http_request_t *r, - ngx_http_cache_ctx_t *ctx) +static ngx_int_t +ngx_http_file_cache_init(ngx_shm_zone_t *shm_zone, void *data) +{ + ngx_http_file_cache_t *ocache = data; + + size_t len; + ngx_uint_t n; + ngx_http_file_cache_t *cache; + + cache = shm_zone->data; + + if (ocache) { + if (ngx_strcmp(cache->path->name.data, ocache->path->name.data) != 0) { + ngx_log_error(NGX_LOG_EMERG, shm_zone->shm.log, 0, + "cache \"%V\" uses the \"%V\" cache path " + "while previously it used the \"%V\" cache path", + &shm_zone->shm.name, &cache->path->name, + &ocache->path->name); + + return NGX_ERROR; + } + + for (n = 0; n < 3; n++) { + if (cache->path->level[n] != ocache->path->level[n]) { + ngx_log_error(NGX_LOG_EMERG, shm_zone->shm.log, 0, + "cache \"%V\" had previously different levels", + &shm_zone->shm.name); + return NGX_ERROR; + } + } + + cache->sh = ocache->sh; + + cache->shpool = ocache->shpool; + cache->bsize = ocache->bsize; + + cache->max_size /= cache->bsize; + + if (!cache->sh->cold || cache->sh->loading) { + cache->path->loader = NULL; + } + + return NGX_OK; + } + + cache->shpool = (ngx_slab_pool_t *) shm_zone->shm.addr; + + if (shm_zone->shm.exists) { + cache->sh = cache->shpool->data; + cache->bsize = ngx_fs_bsize(cache->path->name.data); + + return NGX_OK; + } + + cache->sh = ngx_slab_alloc(cache->shpool, sizeof(ngx_http_file_cache_sh_t)); + if (cache->sh == NULL) { + return NGX_ERROR; + } + + cache->shpool->data = cache->sh; + + ngx_rbtree_init(&cache->sh->rbtree, &cache->sh->sentinel, + ngx_http_file_cache_rbtree_insert_value); + + ngx_queue_init(&cache->sh->queue); + + cache->sh->cold = 1; + cache->sh->loading = 0; + cache->sh->size = 0; + + cache->bsize = ngx_fs_bsize(cache->path->name.data); + + cache->max_size /= cache->bsize; + + len = sizeof(" in cache keys zone \"\"") + shm_zone->shm.name.len; + + cache->shpool->log_ctx = ngx_slab_alloc(cache->shpool, len); + if (cache->shpool->log_ctx == NULL) { + return NGX_ERROR; + } + + ngx_sprintf(cache->shpool->log_ctx, " in cache keys zone \"%V\"%Z", + &shm_zone->shm.name); + + return NGX_OK; +} + + +ngx_int_t +ngx_http_file_cache_new(ngx_http_request_t *r) { - ngx_uint_t i; - ngx_str_t *key; ngx_http_cache_t *c; - MD5_CTX md5; + + c = ngx_pcalloc(r->pool, sizeof(ngx_http_cache_t)); + if (c == NULL) { + return NGX_ERROR; + } + + if (ngx_array_init(&c->keys, r->pool, 4, sizeof(ngx_str_t)) != NGX_OK) { + return NGX_ERROR; + } + + r->cache = c; + c->file.log = r->connection->log; + c->file.fd = NGX_INVALID_FILE; + + return NGX_OK; +} + + +ngx_int_t +ngx_http_file_cache_create(ngx_http_request_t *r) +{ + ngx_http_cache_t *c; + ngx_pool_cleanup_t *cln; + ngx_http_file_cache_t *cache; + + ngx_http_file_cache_create_key(r); c = r->cache; + cache = c->file_cache; + + cln = ngx_pool_cleanup_add(r->pool, 0); + if (cln == NULL) { + return NGX_ERROR; + } - c->file.name.len = ctx->path->name.len + 1 + ctx->path->len + 32; - if (!(c->file.name.data = ngx_palloc(r->pool, c->file.name.len + 1))) { - return NGX_ABORT; + if (ngx_http_file_cache_exists(cache, c) == NGX_ERROR) { + return NGX_ERROR; } - MD5Init(&md5); + cln->handler = ngx_http_file_cache_cleanup; + cln->data = c; - key = c->key.elts; - for (i = 0; i < c->key.nelts; i++) { - MD5Update(&md5, key[i].data, key[i].len); + if (ngx_http_file_cache_name(r, cache->path) != NGX_OK) { + return NGX_ERROR; } - MD5Update(&md5, ctx->key.data, ctx->key.len); + return NGX_OK; +} - MD5Final(c->md5, &md5); - ngx_memcpy(c->file.name.data, ctx->path->name.data, ctx->path->name.len); +void +ngx_http_file_cache_create_key(ngx_http_request_t *r) +{ + size_t len; + ngx_str_t *key; + ngx_uint_t i; + ngx_md5_t md5; + ngx_http_cache_t *c; - ngx_md5_text(c->file.name.data + ctx->path->name.len + 1 + ctx->path->len, - c->md5); + c = r->cache; - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "file cache key: %V, md5: %s", &ctx->key, - c->file.name.data + ctx->path->name.len + 1 + ctx->path->len); + len = 0; - ngx_create_hashed_filename(&c->file, ctx->path); + ngx_crc32_init(c->crc32); + ngx_md5_init(&md5); - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "file cache name: %s", c->file.name.data); + key = c->keys.elts; + for (i = 0; i < c->keys.nelts; i++) { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http cache key: \"%V\"", &key[i]); + + len += key[i].len; + + ngx_crc32_update(&c->crc32, key[i].data, key[i].len); + ngx_md5_update(&md5, key[i].data, key[i].len); + } - return ngx_http_file_cache_open(r->cache); + c->header_start = sizeof(ngx_http_file_cache_header_t) + + sizeof(ngx_http_file_cache_key) + len + 1; + + ngx_crc32_final(c->crc32); + ngx_md5_final(c->key, &md5); } -ngx_int_t ngx_http_file_cache_open(ngx_http_cache_t *c) +ngx_int_t +ngx_http_file_cache_open(ngx_http_request_t *r) { - ssize_t n; - ngx_err_t err; - ngx_http_cache_header_t *h; + ngx_int_t rc, rv; + ngx_uint_t cold, test; + ngx_http_cache_t *c; + ngx_pool_cleanup_t *cln; + ngx_open_file_info_t of; + ngx_http_file_cache_t *cache; + ngx_http_core_loc_conf_t *clcf; - c->file.fd = ngx_open_file(c->file.name.data, - NGX_FILE_RDONLY, NGX_FILE_OPEN); + c = r->cache; - if (c->file.fd == NGX_INVALID_FILE) { - err = ngx_errno; + if (c->buf) { + return ngx_http_file_cache_read(r, c); + } - if (err == NGX_ENOENT || err == NGX_ENOTDIR) { - return NGX_DECLINED; - } + cache = c->file_cache; - ngx_log_error(NGX_LOG_CRIT, c->log, ngx_errno, - ngx_open_file_n " \"%s\" failed", c->file.name.data); + cln = ngx_pool_cleanup_add(r->pool, 0); + if (cln == NULL) { return NGX_ERROR; } - if (c->uniq) { - if (ngx_fd_info(c->file.fd, &c->file.info) == NGX_FILE_ERROR) { - ngx_log_error(NGX_LOG_CRIT, c->log, ngx_errno, - ngx_fd_info_n " \"%s\" failed", c->file.name.data); + rc = ngx_http_file_cache_exists(cache, c); - return NGX_ERROR; + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http file cache exists: %i e:%d", rc, c->exists); + + if (rc == NGX_ERROR) { + return rc; + } + + cln->handler = ngx_http_file_cache_cleanup; + cln->data = c; + + if (rc == NGX_AGAIN) { + return NGX_HTTP_CACHE_SCARCE; + } + + cold = cache->sh->cold; + + if (rc == NGX_OK) { + + if (c->error) { + return c->error; } - if (ngx_file_uniq(&c->file.info) == c->uniq) { - if (ngx_close_file(c->file.fd) == NGX_FILE_ERROR) { - ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno, - ngx_close_file_n " \"%s\" failed", - c->file.name.data); + c->temp_file = 1; + test = c->exists ? 1 : 0; + rv = NGX_DECLINED; + + } else { /* rc == NGX_DECLINED */ + + if (c->min_uses > 1) { + + if (!cold) { + return NGX_HTTP_CACHE_SCARCE; } - return NGX_HTTP_CACHE_THE_SAME; + test = 1; + rv = NGX_HTTP_CACHE_SCARCE; + + } else { + c->temp_file = 1; + test = cold ? 1 : 0; + rv = NGX_DECLINED; + } + } + + if (ngx_http_file_cache_name(r, cache->path) != NGX_OK) { + return NGX_ERROR; + } + + if (!test) { + return NGX_DECLINED; + } + + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + ngx_memzero(&of, sizeof(ngx_open_file_info_t)); + + of.uniq = c->uniq; + of.valid = clcf->open_file_cache_valid; + of.min_uses = clcf->open_file_cache_min_uses; + of.events = clcf->open_file_cache_events; + of.directio = NGX_OPEN_FILE_DIRECTIO_OFF; + of.read_ahead = clcf->read_ahead; + + if (ngx_open_cached_file(clcf->open_file_cache, &c->file.name, &of, r->pool) + != NGX_OK) + { + switch (of.err) { + + case 0: + return NGX_ERROR; + + case NGX_ENOENT: + case NGX_ENOTDIR: + return rv; + + default: + ngx_log_error(NGX_LOG_CRIT, r->connection->log, of.err, + ngx_open_file_n " \"%s\" failed", c->file.name.data); + return NGX_ERROR; } } - n = ngx_read_file(&c->file, c->buf->pos, c->buf->end - c->buf->last, 0); + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http file cache fd: %d", of.fd); + + c->file.fd = of.fd; + c->file.log = r->connection->log; + c->uniq = of.uniq; + c->length = of.size; + + c->buf = ngx_create_temp_buf(r->pool, c->body_start); + if (c->buf == NULL) { + return NGX_ERROR; + } + + return ngx_http_file_cache_read(r, c); +} + + +static ngx_int_t +ngx_http_file_cache_read(ngx_http_request_t *r, ngx_http_cache_t *c) +{ + time_t now; + ssize_t n; + ngx_int_t rc; + ngx_http_file_cache_t *cache; + ngx_http_file_cache_header_t *h; + + n = ngx_http_file_cache_aio_read(r, c); - if (n == NGX_ERROR || n == NGX_AGAIN) { + if (n < 0) { return n; } - if (n <= c->header_size) { - ngx_log_error(NGX_LOG_CRIT, c->log, 0, + if ((size_t) n < c->header_start) { + ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0, "cache file \"%s\" is too small", c->file.name.data); return NGX_ERROR; } - h = (ngx_http_cache_header_t *) c->buf->pos; - c->expires = h->expires; - c->last_modified= h->last_modified; - c->date = h->date; - c->length = h->length; + h = (ngx_http_file_cache_header_t *) c->buf->pos; - if (h->key_len > (size_t) (c->buf->end - c->buf->pos)) { - ngx_log_error(NGX_LOG_ALERT, c->log, 0, - "cache file \"%s\" is probably invalid", - c->file.name.data); + if (h->crc32 != c->crc32) { + ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0, + "cache file \"%s\" has md5 collision", c->file.name.data); return NGX_DECLINED; } -#if 0 + c->buf->last += n; - /* TODO */ + c->valid_sec = h->valid_sec; + c->last_modified = h->last_modified; + c->date = h->date; + c->valid_msec = h->valid_msec; + c->header_start = h->header_start; + c->body_start = h->body_start; - if (c->key_len && h->key_len != c->key_len) { + r->cached = 1; - ngx_strncmp(h->key, c->key_data, h->key_len) != 0)) + cache = c->file_cache; - h->key[h->key_len] = '\0'; - ngx_log_error(NGX_LOG_ALERT, c->log, 0, - "md5 collision: \"%s\" and \"%s\"", - h->key, c->key.data); - return NGX_DECLINED; - } + if (cache->sh->cold) { -#endif + ngx_shmtx_lock(&cache->shpool->mutex); - c->buf->last += n; + if (!c->node->exists) { + c->node->uses = 1; + c->node->body_start = c->body_start; + c->node->exists = 1; + c->node->uniq = c->uniq; - if (c->expires < ngx_time()) { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http file cache expired"); - return NGX_HTTP_CACHE_STALE; + cache->sh->size += (c->length + cache->bsize - 1) / cache->bsize; + } + + ngx_shmtx_unlock(&cache->shpool->mutex); } - /* TODO: NGX_HTTP_CACHE_AGED */ + now = ngx_time(); + + if (c->valid_sec < now) { + + ngx_shmtx_lock(&cache->shpool->mutex); + + if (c->node->updating) { + rc = NGX_HTTP_CACHE_UPDATING; + + } else { + c->node->updating = 1; + c->updating = 1; + rc = NGX_HTTP_CACHE_STALE; + } + + ngx_shmtx_unlock(&cache->shpool->mutex); - /* STUB */ return NGX_DECLINED; + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http file cache expired: %i %T %T", + rc, c->valid_sec, now); + + return rc; + } return NGX_OK; } -#if 0 +static ssize_t +ngx_http_file_cache_aio_read(ngx_http_request_t *r, ngx_http_cache_t *c) +{ +#if (NGX_HAVE_FILE_AIO) + ssize_t n; + ngx_http_core_loc_conf_t *clcf; + + if (!ngx_file_aio) { + goto noaio; + } + + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + if (!clcf->aio) { + goto noaio; + } + + n = ngx_file_aio_read(&c->file, c->buf->pos, c->body_start, 0, r->pool); + + if (n != NGX_AGAIN) { + return n; + } + + c->file.aio->data = r; + c->file.aio->handler = ngx_http_cache_aio_event_handler; + + r->main->blocked++; + r->aio = 1; + + return NGX_AGAIN; + +noaio: + +#endif + + return ngx_read_file(&c->file, c->buf->pos, c->body_start, 0); +} -int ngx_http_cache_update_file(ngx_http_request_t *r, ngx_http_cache_ctx_t *ctx, - ngx_str_t *temp_file) +#if (NGX_HAVE_FILE_AIO) + +static void +ngx_http_cache_aio_event_handler(ngx_event_t *ev) { - int retry; - ngx_err_t err; + ngx_event_aio_t *aio; + ngx_http_request_t *r; - retry = 0; + aio = ev->data; + r = aio->data; - for ( ;; ) { - if (ngx_rename_file(temp_file->data, ctx->file.name.data) == NGX_OK) { - return NGX_OK; - } + r->main->blocked--; + r->aio = 0; + + r->connection->write->handler(r->connection->write); +} + +#endif - err = ngx_errno; -#if (NGX_WIN32) - if (err == NGX_EEXIST) { - if (ngx_win32_rename_file(temp_file, &ctx->file.name, r->pool) - == NGX_ERROR) - { - return NGX_ERROR; +static ngx_int_t +ngx_http_file_cache_exists(ngx_http_file_cache_t *cache, ngx_http_cache_t *c) +{ + ngx_int_t rc; + ngx_http_file_cache_node_t *fcn; + + ngx_shmtx_lock(&cache->shpool->mutex); + + fcn = ngx_http_file_cache_lookup(cache, c->key); + + if (fcn) { + ngx_queue_remove(&fcn->queue); + + fcn->uses++; + fcn->count++; + + if (fcn->error) { + + if (fcn->valid_sec < ngx_time()) { + goto renew; } + + rc = NGX_OK; + + goto done; } -#endif - if (retry || (err != NGX_ENOENT && err != NGX_ENOTDIR)) { - ngx_log_error(NGX_LOG_CRIT, r->connection->log, err, - ngx_rename_file_n "(\"%s\", \"%s\") failed", - temp_file->data, ctx->file.name.data); + if (fcn->exists) { - return NGX_ERROR; + c->exists = fcn->exists; + c->body_start = fcn->body_start; + + rc = NGX_OK; + + goto done; } - if (ngx_create_path(&ctx->file, ctx->path) == NGX_ERROR) { - return NGX_ERROR; + if (fcn->uses >= c->min_uses) { + + c->exists = fcn->exists; + c->body_start = fcn->body_start; + + rc = NGX_OK; + + } else { + rc = NGX_AGAIN; } - retry = 1; + goto done; + } + + fcn = ngx_slab_alloc_locked(cache->shpool, + sizeof(ngx_http_file_cache_node_t)); + if (fcn == NULL) { + ngx_shmtx_unlock(&cache->shpool->mutex); + + (void) ngx_http_file_cache_forced_expire(cache); + + ngx_shmtx_lock(&cache->shpool->mutex); + + fcn = ngx_slab_alloc_locked(cache->shpool, + sizeof(ngx_http_file_cache_node_t)); + if (fcn == NULL) { + rc = NGX_ERROR; + goto failed; + } } + + ngx_memcpy((u_char *) &fcn->node.key, c->key, sizeof(ngx_rbtree_key_t)); + + ngx_memcpy(fcn->key, &c->key[sizeof(ngx_rbtree_key_t)], + NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t)); + + ngx_rbtree_insert(&cache->sh->rbtree, &fcn->node); + + fcn->uses = 1; + fcn->count = 1; + fcn->updating = 0; + fcn->deleting = 0; + +renew: + + rc = NGX_DECLINED; + + fcn->valid_msec = 0; + fcn->error = 0; + fcn->exists = 0; + fcn->valid_sec = 0; + fcn->uniq = 0; + fcn->body_start = 0; + fcn->length = 0; + +done: + + fcn->expire = ngx_time() + cache->inactive; + + ngx_queue_insert_head(&cache->sh->queue, &fcn->queue); + + c->uniq = fcn->uniq; + c->error = fcn->error; + c->node = fcn; + +failed: + + ngx_shmtx_unlock(&cache->shpool->mutex); + + return rc; } -#endif +static ngx_int_t +ngx_http_file_cache_name(ngx_http_request_t *r, ngx_path_t *path) +{ + u_char *p; + ngx_http_cache_t *c; + + c = r->cache; + + c->file.name.len = path->name.len + 1 + path->len + + 2 * NGX_HTTP_CACHE_KEY_LEN; + + c->file.name.data = ngx_pnalloc(r->pool, c->file.name.len + 1); + if (c->file.name.data == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(c->file.name.data, path->name.data, path->name.len); + p = c->file.name.data + path->name.len + 1 + path->len; + p = ngx_hex_dump(p, c->key, NGX_HTTP_CACHE_KEY_LEN); + *p = '\0'; -ngx_int_t ngx_http_cache_cleaner_handler(ngx_gc_t *gc, ngx_str_t *name, - ngx_dir_t *dir) + ngx_create_hashed_filename(path, c->file.name.data, c->file.name.len); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "cache file: \"%s\"", c->file.name.data); + + return NGX_OK; +} + + +static ngx_http_file_cache_node_t * +ngx_http_file_cache_lookup(ngx_http_file_cache_t *cache, u_char *key) { - int rc; - ngx_buf_t buf; - ngx_http_cache_t c; - u_char data[sizeof(ngx_http_cache_header_t)]; + ngx_int_t rc; + ngx_rbtree_key_t node_key; + ngx_rbtree_node_t *node, *sentinel; + ngx_http_file_cache_node_t *fcn; - ngx_memzero(&c, sizeof(ngx_http_cache_t)); + ngx_memcpy((u_char *) &node_key, key, sizeof(ngx_rbtree_key_t)); - c.file.fd = NGX_INVALID_FILE; - c.file.name = *name; - c.file.log = gc->log; + node = cache->sh->rbtree.root; + sentinel = cache->sh->rbtree.sentinel; - c.header_size = sizeof(ngx_http_cache_header_t); - c.buf = &buf; - c.log = gc->log; - c.key_len = 0; + while (node != sentinel) { - buf.memory = 1; - buf.temporary = 1; - buf.pos = data; - buf.last = data; - buf.start = data; - buf.end = data + sizeof(ngx_http_cache_header_t); + if (node_key < node->key) { + node = node->left; + continue; + } - rc = ngx_http_file_cache_open(&c); + if (node_key > node->key) { + node = node->right; + continue; + } - /* TODO: NGX_AGAIN */ + /* node_key == node->key */ - if (rc != NGX_ERROR&& rc != NGX_DECLINED && rc != NGX_HTTP_CACHE_STALE) { - return NGX_OK; + do { + fcn = (ngx_http_file_cache_node_t *) node; + + rc = ngx_memcmp(&key[sizeof(ngx_rbtree_key_t)], fcn->key, + NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t)); + + if (rc == 0) { + return fcn; + } + + node = (rc < 0) ? node->left : node->right; + + } while (node != sentinel && node_key == node->key); + + break; } - if (ngx_delete_file(name->data) == NGX_FILE_ERROR) { - ngx_log_error(NGX_LOG_CRIT, c.log, ngx_errno, - ngx_delete_file_n " \"%s\" failed", name->data); - return NGX_ERROR; + /* not found */ + + return NULL; +} + + +static void +ngx_http_file_cache_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_file_cache_node_t *cn, *cnt; + + for ( ;; ) { + + if (node->key < temp->key) { + + p = &temp->left; + + } else if (node->key > temp->key) { + + p = &temp->right; + + } else { /* node->key == temp->key */ + + cn = (ngx_http_file_cache_node_t *) node; + cnt = (ngx_http_file_cache_node_t *) temp; + + p = (ngx_memcmp(cn->key, cnt->key, + NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t)) + < 0) + ? &temp->left : &temp->right; + } + + if (*p == sentinel) { + break; + } + + temp = *p; } - gc->deleted++; - gc->freed += ngx_de_size(dir); + *p = node; + node->parent = temp; + node->left = sentinel; + node->right = sentinel; + ngx_rbt_red(node); +} + - return NGX_OK; +void +ngx_http_file_cache_set_header(ngx_http_request_t *r, u_char *buf) +{ + ngx_http_file_cache_header_t *h = (ngx_http_file_cache_header_t *) buf; + + u_char *p; + ngx_str_t *key; + ngx_uint_t i; + ngx_http_cache_t *c; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http file cache set header"); + + c = r->cache; + + h->valid_sec = c->valid_sec; + h->last_modified = c->last_modified; + h->date = c->date; + h->crc32 = c->crc32; + h->valid_msec = (u_short) c->valid_msec; + h->header_start = (u_short) c->header_start; + h->body_start = (u_short) c->body_start; + + p = buf + sizeof(ngx_http_file_cache_header_t); + + p = ngx_cpymem(p, ngx_http_file_cache_key, sizeof(ngx_http_file_cache_key)); + + key = c->keys.elts; + for (i = 0; i < c->keys.nelts; i++) { + p = ngx_copy(p, key[i].data, key[i].len); + } + + *p = LF; +} + + +void +ngx_http_file_cache_update(ngx_http_request_t *r, ngx_temp_file_t *tf) +{ + off_t size, length; + ngx_int_t rc; + ngx_file_uniq_t uniq; + ngx_file_info_t fi; + ngx_http_cache_t *c; + ngx_ext_rename_file_t ext; + ngx_http_file_cache_t *cache; + + c = r->cache; + + if (c->updated) { + return; + } + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http file cache update"); + + c->updated = 1; + c->updating = 0; + + cache = c->file_cache; + + uniq = 0; + length = 0; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http file cache rename: \"%s\" to \"%s\"", + tf->file.name.data, c->file.name.data); + + ext.access = NGX_FILE_OWNER_ACCESS; + ext.path_access = NGX_FILE_OWNER_ACCESS; + ext.time = -1; + ext.create_path = 1; + ext.delete_file = 1; + ext.log = r->connection->log; + + rc = ngx_ext_rename_file(&tf->file.name, &c->file.name, &ext); + + if (rc == NGX_OK) { + + if (ngx_fd_info(tf->file.fd, &fi) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno, + ngx_fd_info_n " \"%s\" failed", tf->file.name.data); + + rc = NGX_ERROR; + + } else { + uniq = ngx_file_uniq(&fi); + length = ngx_file_size(&fi); + } + } + + size = (length + cache->bsize - 1) / cache->bsize; + + ngx_shmtx_lock(&cache->shpool->mutex); + + c->node->count--; + c->node->uniq = uniq; + c->node->body_start = c->body_start; + + size = size - (c->node->length + cache->bsize - 1) / cache->bsize; + + c->node->length = length; + + cache->sh->size += size; + + if (rc == NGX_OK) { + c->node->exists = 1; + } + + c->node->updating = 0; + + ngx_shmtx_unlock(&cache->shpool->mutex); +} + + +ngx_int_t +ngx_http_cache_send(ngx_http_request_t *r) +{ + ngx_int_t rc; + ngx_buf_t *b; + ngx_chain_t out; + ngx_http_cache_t *c; + + c = r->cache; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http file cache send: %s", c->file.name.data); + + /* we need to allocate all before the header would be sent */ + + b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t)); + 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->header_only = (c->length - c->body_start) == 0; + + rc = ngx_http_send_header(r); + + if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) { + return rc; + } + + b->file_pos = c->body_start; + b->file_last = c->length; + + b->in_file = 1; + b->last_buf = (r == r->main) ? 1: 0; + b->last_in_chain = 1; + + b->file->fd = c->file.fd; + b->file->name = c->file.name; + b->file->log = r->connection->log; + + out.buf = b; + out.next = NULL; + + return ngx_http_output_filter(r, &out); +} + + +void +ngx_http_file_cache_free(ngx_http_cache_t *c, ngx_temp_file_t *tf) +{ + ngx_http_file_cache_t *cache; + ngx_http_file_cache_node_t *fcn; + + if (c->updated) { + return; + } + + cache = c->file_cache; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->file.log, 0, + "http file cache free, fd: %d", c->file.fd); + + ngx_shmtx_lock(&cache->shpool->mutex); + + fcn = c->node; + fcn->count--; + + if (c->updating) { + fcn->updating = 0; + } + + if (c->error) { + fcn->error = c->error; + + if (c->valid_sec) { + fcn->valid_sec = c->valid_sec; + fcn->valid_msec = c->valid_msec; + } + + } else if (!fcn->exists && fcn->count == 0 && c->min_uses == 1) { + ngx_queue_remove(&fcn->queue); + ngx_rbtree_delete(&cache->sh->rbtree, &fcn->node); + ngx_slab_free_locked(cache->shpool, fcn); + c->node = NULL; + } + + ngx_shmtx_unlock(&cache->shpool->mutex); + + c->updated = 1; + c->updating = 0; + + if (c->temp_file) { + if (tf && tf->file.fd != NGX_INVALID_FILE) { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->file.log, 0, + "http file cache incomplete: \"%s\"", + tf->file.name.data); + + if (ngx_delete_file(tf->file.name.data) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_CRIT, c->file.log, ngx_errno, + ngx_delete_file_n " \"%s\" failed", + tf->file.name.data); + } + } + } +} + + +static void +ngx_http_file_cache_cleanup(void *data) +{ + ngx_http_cache_t *c = data; + + if (c->updated) { + return; + } + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->file.log, 0, + "http file cache cleanup"); + + if (c->updating) { + ngx_log_error(NGX_LOG_ALERT, c->file.log, 0, + "stalled cache updating, error:%ui", c->error); + } + + ngx_http_file_cache_free(c, NULL); +} + + +static time_t +ngx_http_file_cache_forced_expire(ngx_http_file_cache_t *cache) +{ + u_char *name; + size_t len; + time_t wait; + ngx_uint_t tries; + ngx_path_t *path; + ngx_queue_t *q; + ngx_http_file_cache_node_t *fcn; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "http file cache forced expire"); + + path = cache->path; + len = path->name.len + 1 + path->len + 2 * NGX_HTTP_CACHE_KEY_LEN; + + name = ngx_alloc(len + 1, ngx_cycle->log); + if (name == NULL) { + return 10; + } + + ngx_memcpy(name, path->name.data, path->name.len); + + wait = 10; + tries = 20; + + ngx_shmtx_lock(&cache->shpool->mutex); + + for (q = ngx_queue_last(&cache->sh->queue); + q != ngx_queue_sentinel(&cache->sh->queue); + q = ngx_queue_prev(q)) + { + fcn = ngx_queue_data(q, ngx_http_file_cache_node_t, queue); + + ngx_log_debug6(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "http file cache forced expire: #%d %d %02xd%02xd%02xd%02xd", + fcn->count, fcn->exists, + fcn->key[0], fcn->key[1], fcn->key[2], fcn->key[3]); + + if (fcn->count == 0) { + ngx_http_file_cache_delete(cache, q, name); + + } else { + if (--tries) { + continue; + } + + wait = 1; + } + + break; + } + + ngx_shmtx_unlock(&cache->shpool->mutex); + + ngx_free(name); + + return wait; +} + + +static time_t +ngx_http_file_cache_expire(ngx_http_file_cache_t *cache) +{ + u_char *name, *p; + size_t len; + time_t now, wait; + ngx_path_t *path; + ngx_queue_t *q; + ngx_http_file_cache_node_t *fcn; + u_char key[2 * NGX_HTTP_CACHE_KEY_LEN]; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "http file cache expire"); + + path = cache->path; + len = path->name.len + 1 + path->len + 2 * NGX_HTTP_CACHE_KEY_LEN; + + name = ngx_alloc(len + 1, ngx_cycle->log); + if (name == NULL) { + return 10; + } + + ngx_memcpy(name, path->name.data, path->name.len); + + now = ngx_time(); + + ngx_shmtx_lock(&cache->shpool->mutex); + + for ( ;; ) { + + if (ngx_queue_empty(&cache->sh->queue)) { + wait = 10; + break; + } + + q = ngx_queue_last(&cache->sh->queue); + + fcn = ngx_queue_data(q, ngx_http_file_cache_node_t, queue); + + wait = fcn->expire - now; + + if (wait > 0) { + wait = wait > 10 ? 10 : wait; + break; + } + + ngx_log_debug6(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "http file cache expire: #%d %d %02xd%02xd%02xd%02xd", + fcn->count, fcn->exists, + fcn->key[0], fcn->key[1], fcn->key[2], fcn->key[3]); + + if (fcn->count == 0) { + ngx_http_file_cache_delete(cache, q, name); + continue; + } + + if (fcn->deleting) { + continue; + } + + p = ngx_hex_dump(key, (u_char *) &fcn->node.key, + sizeof(ngx_rbtree_key_t)); + len = NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t); + (void) ngx_hex_dump(p, fcn->key, len); + + /* + * abnormally exited workers may leave locked cache entries, + * and although it may be safe to remove them completely, + * we prefer to remove them from inactive queue and rbtree + * only, and to allow other leaks + */ + + ngx_queue_remove(q); + ngx_rbtree_delete(&cache->sh->rbtree, &fcn->node); + + ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, + "ignore long locked inactive cache entry %*s, count:%d", + 2 * NGX_HTTP_CACHE_KEY_LEN, key, fcn->count); + } + + ngx_shmtx_unlock(&cache->shpool->mutex); + + ngx_free(name); + + return wait; +} + + +static void +ngx_http_file_cache_delete(ngx_http_file_cache_t *cache, ngx_queue_t *q, + u_char *name) +{ + u_char *p; + size_t len; + ngx_path_t *path; + ngx_http_file_cache_node_t *fcn; + + fcn = ngx_queue_data(q, ngx_http_file_cache_node_t, queue); + + if (fcn->exists) { + cache->sh->size -= (fcn->length + cache->bsize - 1) / cache->bsize; + + path = cache->path; + p = name + path->name.len + 1 + path->len; + p = ngx_hex_dump(p, (u_char *) &fcn->node.key, + sizeof(ngx_rbtree_key_t)); + len = NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t); + p = ngx_hex_dump(p, fcn->key, len); + *p = '\0'; + + fcn->count++; + fcn->deleting = 1; + ngx_shmtx_unlock(&cache->shpool->mutex); + + len = path->name.len + 1 + path->len + 2 * NGX_HTTP_CACHE_KEY_LEN; + ngx_create_hashed_filename(path, name, len); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "http file cache expire: \"%s\"", name); + + if (ngx_delete_file(name) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_CRIT, ngx_cycle->log, ngx_errno, + ngx_delete_file_n " \"%s\" failed", name); + } + + ngx_shmtx_lock(&cache->shpool->mutex); + fcn->count--; + fcn->deleting = 0; + } + + if (fcn->count == 0) { + ngx_queue_remove(q); + ngx_rbtree_delete(&cache->sh->rbtree, &fcn->node); + ngx_slab_free_locked(cache->shpool, fcn); + } +} + + +static time_t +ngx_http_file_cache_manager(void *data) +{ + ngx_http_file_cache_t *cache = data; + + off_t size; + time_t next; + + next = ngx_http_file_cache_expire(cache); + + cache->last = ngx_current_msec; + cache->files = 0; + + for ( ;; ) { + ngx_shmtx_lock(&cache->shpool->mutex); + + size = cache->sh->size; + + ngx_shmtx_unlock(&cache->shpool->mutex); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "http file cache size: %O", size); + + if (size < cache->max_size) { + return next; + } + + next = ngx_http_file_cache_forced_expire(cache); + + if (ngx_http_file_cache_manager_sleep(cache) != NGX_OK) { + return next; + } + } +} + + +static void +ngx_http_file_cache_loader(void *data) +{ + ngx_http_file_cache_t *cache = data; + + ngx_tree_ctx_t tree; + + if (!cache->sh->cold || cache->sh->loading) { + return; + } + + if (!ngx_atomic_cmp_set(&cache->sh->loading, 0, ngx_pid)) { + return; + } + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "http file cache loader"); + + tree.init_handler = NULL; + tree.file_handler = ngx_http_file_cache_manage_file; + tree.pre_tree_handler = ngx_http_file_cache_noop; + tree.post_tree_handler = ngx_http_file_cache_noop; + tree.spec_handler = ngx_http_file_cache_delete_file; + tree.data = cache; + tree.alloc = 0; + tree.log = ngx_cycle->log; + + cache->last = ngx_current_msec; + cache->files = 0; + + if (ngx_walk_tree(&tree, &cache->path->name) == NGX_ABORT) { + cache->sh->loading = 0; + return; + } + + cache->sh->cold = 0; + cache->sh->loading = 0; + + ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0, + "http file cache: %V %.3fM, bsize: %uz", + &cache->path->name, + ((double) cache->sh->size * cache->bsize) / (1024 * 1024), + cache->bsize); +} + + +static ngx_int_t +ngx_http_file_cache_manager_sleep(ngx_http_file_cache_t *cache) +{ + ngx_msec_t elapsed; + + if (cache->files++ > 100) { + + ngx_time_update(); + + elapsed = ngx_abs((ngx_msec_int_t) (ngx_current_msec - cache->last)); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, + "http file cache manager time: %M", elapsed); + + if (elapsed > 200) { + + /* + * if processing 100 files takes more than 200ms, + * it seems that many operations require disk i/o, + * therefore sleep 200ms + */ + + ngx_msleep(200); + + ngx_time_update(); + } + + cache->last = ngx_current_msec; + cache->files = 0; + } + + return (ngx_quit || ngx_terminate) ? NGX_ABORT : NGX_OK; +} + + +static ngx_int_t +ngx_http_file_cache_noop(ngx_tree_ctx_t *ctx, ngx_str_t *path) +{ + return NGX_OK; +} + + +static ngx_int_t +ngx_http_file_cache_manage_file(ngx_tree_ctx_t *ctx, ngx_str_t *path) +{ + ngx_http_file_cache_t *cache; + + cache = ctx->data; + + if (ngx_http_file_cache_add_file(ctx, path) != NGX_OK) { + (void) ngx_http_file_cache_delete_file(ctx, path); + } + + return ngx_http_file_cache_manager_sleep(cache); +} + + +static ngx_int_t +ngx_http_file_cache_add_file(ngx_tree_ctx_t *ctx, ngx_str_t *name) +{ + u_char *p; + ngx_fd_t fd; + ngx_int_t n; + ngx_uint_t i; + ngx_file_info_t fi; + ngx_http_cache_t c; + ngx_http_file_cache_t *cache; + ngx_http_file_cache_header_t h; + + if (name->len < 2 * NGX_HTTP_CACHE_KEY_LEN) { + return NGX_ERROR; + } + + ngx_memzero(&c, sizeof(ngx_http_cache_t)); + + fd = ngx_open_file(name->data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0); + + if (fd == NGX_INVALID_FILE) { + ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno, + ngx_open_file_n " \"%s\" failed", name->data); + return NGX_ERROR; + } + + c.file.fd = fd; + c.file.name = *name; + c.file.log = ctx->log; + + n = ngx_read_file(&c.file, (u_char *) &h, + sizeof(ngx_http_file_cache_header_t), 0); + if (n == NGX_ERROR) { + return NGX_ERROR; + } + + if ((size_t) n < sizeof(ngx_http_file_cache_header_t)) { + ngx_log_error(NGX_LOG_CRIT, ctx->log, 0, + "cache file \"%s\" is too small", name->data); + return NGX_ERROR; + } + + if (ngx_fd_info(fd, &fi) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno, + ngx_fd_info_n " \"%s\" failed", name->data); + + } else { + c.uniq = ngx_file_uniq(&fi); + c.valid_sec = h.valid_sec; + c.valid_msec = h.valid_msec; + c.body_start = h.body_start; + c.length = ngx_file_size(&fi); + } + + if (ngx_close_file(fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, ctx->log, ngx_errno, + ngx_close_file_n " \"%s\" failed", name->data); + } + + if (c.body_start == 0) { + return NGX_ERROR; + } + + p = &name->data[name->len - 2 * NGX_HTTP_CACHE_KEY_LEN]; + + for (i = 0; i < NGX_HTTP_CACHE_KEY_LEN; i++) { + n = ngx_hextoi(p, 2); + + if (n == NGX_ERROR) { + return NGX_ERROR; + } + + p += 2; + + c.key[i] = (u_char) n; + } + + cache = ctx->data; + + return ngx_http_file_cache_add(cache, &c); +} + + +static ngx_int_t +ngx_http_file_cache_add(ngx_http_file_cache_t *cache, ngx_http_cache_t *c) +{ + ngx_http_file_cache_node_t *fcn; + + ngx_shmtx_lock(&cache->shpool->mutex); + + fcn = ngx_http_file_cache_lookup(cache, c->key); + + if (fcn == NULL) { + + fcn = ngx_slab_alloc_locked(cache->shpool, + sizeof(ngx_http_file_cache_node_t)); + if (fcn == NULL) { + ngx_shmtx_unlock(&cache->shpool->mutex); + return NGX_ERROR; + } + + ngx_memcpy((u_char *) &fcn->node.key, c->key, sizeof(ngx_rbtree_key_t)); + + ngx_memcpy(fcn->key, &c->key[sizeof(ngx_rbtree_key_t)], + NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t)); + + ngx_rbtree_insert(&cache->sh->rbtree, &fcn->node); + + fcn->uses = 1; + fcn->count = 0; + fcn->valid_msec = c->valid_msec; + fcn->error = 0; + fcn->exists = 1; + fcn->updating = 0; + fcn->deleting = 0; + fcn->uniq = c->uniq; + fcn->valid_sec = c->valid_sec; + fcn->body_start = c->body_start; + fcn->length = c->length; + + cache->sh->size += (c->length + cache->bsize - 1) / cache->bsize; + + } else { + ngx_queue_remove(&fcn->queue); + } + + fcn->expire = ngx_time() + cache->inactive; + + ngx_queue_insert_head(&cache->sh->queue, &fcn->queue); + + ngx_shmtx_unlock(&cache->shpool->mutex); + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_file_cache_delete_file(ngx_tree_ctx_t *ctx, ngx_str_t *path) +{ + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0, + "http file cache delete: \"%s\"", path->data); + + if (ngx_delete_file(path->data) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno, + ngx_delete_file_n " \"%s\" failed", path->data); + } + + return NGX_OK; +} + + +time_t +ngx_http_file_cache_valid(ngx_array_t *cache_valid, ngx_uint_t status) +{ + ngx_uint_t i; + ngx_http_cache_valid_t *valid; + + if (cache_valid == NULL) { + return 0; + } + + valid = cache_valid->elts; + for (i = 0; i < cache_valid->nelts; i++) { + + if (valid[i].status == 0) { + return valid[i].valid; + } + + if (valid[i].status == status) { + return valid[i].valid; + } + } + + return 0; +} + + +char * +ngx_http_file_cache_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + off_t max_size; + u_char *last, *p; + time_t inactive; + ssize_t size; + ngx_str_t s, name, *value; + ngx_uint_t i, n; + ngx_http_file_cache_t *cache; + + cache = ngx_pcalloc(cf->pool, sizeof(ngx_http_file_cache_t)); + if (cache == NULL) { + return NGX_CONF_ERROR; + } + + cache->path = ngx_pcalloc(cf->pool, sizeof(ngx_path_t)); + if (cache->path == NULL) { + return NGX_CONF_ERROR; + } + + inactive = 600; + + name.len = 0; + size = 0; + max_size = NGX_MAX_OFF_T_VALUE; + + value = cf->args->elts; + + cache->path->name = value[1]; + + if (cache->path->name.data[cache->path->name.len - 1] == '/') { + cache->path->name.len--; + } + + if (ngx_conf_full_name(cf->cycle, &cache->path->name, 0) != NGX_OK) { + return NGX_CONF_ERROR; + } + + for (i = 2; i < cf->args->nelts; i++) { + + if (ngx_strncmp(value[i].data, "levels=", 7) == 0) { + + p = value[i].data + 7; + last = value[i].data + value[i].len; + + for (n = 0; n < 3 && p < last; n++) { + + if (*p > '0' && *p < '3') { + + cache->path->level[n] = *p++ - '0'; + cache->path->len += cache->path->level[n] + 1; + + if (p == last) { + break; + } + + if (*p++ == ':' && n < 2 && p != last) { + continue; + } + + goto invalid_levels; + } + + goto invalid_levels; + } + + if (cache->path->len < 10 + 3) { + continue; + } + + invalid_levels: + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid \"levels\" \"%V\"", &value[i]); + return NGX_CONF_ERROR; + } + + if (ngx_strncmp(value[i].data, "keys_zone=", 10) == 0) { + + name.data = value[i].data + 10; + + p = (u_char *) ngx_strchr(name.data, ':'); + + if (p) { + *p = '\0'; + + name.len = p - name.data; + + p++; + + s.len = value[i].data + value[i].len - p; + s.data = p; + + size = ngx_parse_size(&s); + if (size > 8191) { + continue; + } + } + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid keys zone size \"%V\"", &value[i]); + return NGX_CONF_ERROR; + } + + 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 < 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid inactive value \"%V\"", &value[i]); + return NGX_CONF_ERROR; + } + + continue; + } + + if (ngx_strncmp(value[i].data, "max_size=", 9) == 0) { + + s.len = value[i].len - 9; + s.data = value[i].data + 9; + + max_size = ngx_parse_offset(&s); + if (max_size < 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid max_size value \"%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 || size == 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "\"%V\" must have \"keys_zone\" parameter", + &cmd->name); + return NGX_CONF_ERROR; + } + + cache->path->manager = ngx_http_file_cache_manager; + cache->path->loader = ngx_http_file_cache_loader; + cache->path->data = cache; + + if (ngx_add_path(cf, &cache->path) != NGX_OK) { + return NGX_CONF_ERROR; + } + + cache->shm_zone = ngx_shared_memory_add(cf, &name, size, cmd->post); + if (cache->shm_zone == NULL) { + return NGX_CONF_ERROR; + } + + if (cache->shm_zone->data) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "duplicate zone \"%V\"", &name); + return NGX_CONF_ERROR; + } + + + cache->shm_zone->init = ngx_http_file_cache_init; + cache->shm_zone->data = cache; + + cache->inactive = inactive; + cache->max_size = max_size; + + return NGX_CONF_OK; +} + + +char * +ngx_http_file_cache_valid_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf) +{ + char *p = conf; + + time_t valid; + ngx_str_t *value; + ngx_uint_t i, n, status; + ngx_array_t **a; + ngx_http_cache_valid_t *v; + static ngx_uint_t statuses[] = { 200, 301, 302 }; + + a = (ngx_array_t **) (p + cmd->offset); + + if (*a == NGX_CONF_UNSET_PTR) { + *a = ngx_array_create(cf->pool, 1, sizeof(ngx_http_cache_valid_t)); + if (*a == NULL) { + return NGX_CONF_ERROR; + } + } + + value = cf->args->elts; + n = cf->args->nelts - 1; + + valid = ngx_parse_time(&value[n], 1); + if (valid < 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid time value \"%V\"", &value[n]); + return NGX_CONF_ERROR; + } + + if (n == 1) { + + for (i = 0; i < 3; i++) { + v = ngx_array_push(*a); + if (v == NULL) { + return NGX_CONF_ERROR; + } + + v->status = statuses[i]; + v->valid = valid; + } + + return NGX_CONF_OK; + } + + for (i = 1; i < n; i++) { + + if (ngx_strcmp(value[i].data, "any") == 0) { + + status = 0; + + } else { + + status = ngx_atoi(value[i].data, value[i].len); + if (status < 100) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid status \"%V\"", &value[i]); + return NGX_CONF_ERROR; + } + } + + v = ngx_array_push(*a); + if (v == NULL) { + return NGX_CONF_ERROR; + } + + v->status = status; + v->valid = valid; + } + + return NGX_CONF_OK; +} + + +ngx_int_t +ngx_http_cache(ngx_http_request_t *r, ngx_array_t *no_cache) +{ + ngx_str_t val; + ngx_uint_t i; + ngx_http_complex_value_t *cv; + + cv = no_cache->elts; + + for (i = 0; i < no_cache->nelts; i++) { + if (ngx_http_complex_value(r, &cv[i], &val) != NGX_OK) { + return NGX_ERROR; + } + + if (val.len && val.data[0] != '0') { + return NGX_DECLINED; + } + } + + return NGX_OK; +} + + +char * +ngx_http_no_cache_set_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; } diff -Nru nginx-0.5.33/src/http/ngx_http.h nginx-0.8.53/src/http/ngx_http.h --- nginx-0.5.33/src/http/ngx_http.h 2007-05-29 15:21:09.000000000 +0000 +++ nginx-0.8.53/src/http/ngx_http.h 2010-10-04 14:59:41.000000000 +0000 @@ -10,12 +10,13 @@ #include #include -#include -typedef struct ngx_http_request_s ngx_http_request_t; -typedef struct ngx_http_upstream_s ngx_http_upstream_t; -typedef struct ngx_http_log_ctx_s ngx_http_log_ctx_t; +typedef struct ngx_http_request_s ngx_http_request_t; +typedef struct ngx_http_upstream_s ngx_http_upstream_t; +typedef struct ngx_http_cache_s ngx_http_cache_t; +typedef struct ngx_http_file_cache_s ngx_http_file_cache_t; +typedef struct ngx_http_log_ctx_s ngx_http_log_ctx_t; typedef ngx_int_t (*ngx_http_header_handler_pt)(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset); @@ -23,21 +24,18 @@ ngx_http_request_t *sr, u_char *buf, size_t len); -#if (NGX_HTTP_CACHE) -#include -#endif -/* STUB */ -#include - #include #include #include #include #include #include -#include #include +#include +#if (NGX_HTTP_CACHE) +#include +#endif #if (NGX_HTTP_SSI) #include #endif @@ -47,16 +45,30 @@ struct ngx_http_log_ctx_s { - ngx_str_t *client; + ngx_connection_t *connection; ngx_http_request_t *request; ngx_http_request_t *current_request; }; +typedef struct { + ngx_uint_t code; + ngx_uint_t count; + u_char *start; + u_char *end; +} ngx_http_status_t; + + #define ngx_http_get_module_ctx(r, module) (r)->ctx[module.ctx_index] #define ngx_http_set_ctx(r, c, module) r->ctx[module.ctx_index] = c; +ngx_int_t ngx_http_add_location(ngx_conf_t *cf, ngx_queue_t **locations, + ngx_http_core_loc_conf_t *clcf); +ngx_int_t ngx_http_add_listen(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf, + ngx_http_listen_opt_t *lsopt); + + void ngx_http_init_connection(ngx_connection_t *c); #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME @@ -64,21 +76,37 @@ #endif ngx_int_t ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b); -ngx_int_t ngx_http_parse_complex_uri(ngx_http_request_t *r); +ngx_int_t ngx_http_parse_complex_uri(ngx_http_request_t *r, + ngx_uint_t merge_slashes); +ngx_int_t ngx_http_parse_status_line(ngx_http_request_t *r, ngx_buf_t *b, + ngx_http_status_t *status); ngx_int_t ngx_http_parse_unsafe_uri(ngx_http_request_t *r, ngx_str_t *uri, ngx_str_t *args, ngx_uint_t *flags); -ngx_int_t ngx_http_parse_header_line(ngx_http_request_t *r, ngx_buf_t *b); +ngx_int_t ngx_http_parse_header_line(ngx_http_request_t *r, ngx_buf_t *b, + ngx_uint_t allow_underscores); ngx_int_t ngx_http_parse_multi_header_lines(ngx_array_t *headers, ngx_str_t *name, ngx_str_t *value); +ngx_int_t ngx_http_arg(ngx_http_request_t *r, u_char *name, size_t len, + ngx_str_t *value); +void ngx_http_split_args(ngx_http_request_t *r, ngx_str_t *uri, + ngx_str_t *args); + ngx_int_t ngx_http_find_server_conf(ngx_http_request_t *r); void ngx_http_update_location_config(ngx_http_request_t *r); void ngx_http_handler(ngx_http_request_t *r); +void ngx_http_run_posted_requests(ngx_connection_t *c); +ngx_int_t ngx_http_post_request(ngx_http_request_t *r, + ngx_http_posted_request_t *pr); void ngx_http_finalize_request(ngx_http_request_t *r, ngx_int_t rc); void ngx_http_empty_handler(ngx_event_t *wev); void ngx_http_request_empty_handler(ngx_http_request_t *r); + +#define ngx_http_ephemeral(r) (void *) (&r->uri_start) + + #define NGX_HTTP_LAST 1 #define NGX_HTTP_FLUSH 2 @@ -91,6 +119,9 @@ ngx_int_t ngx_http_send_header(ngx_http_request_t *r); ngx_int_t ngx_http_special_response_handler(ngx_http_request_t *r, ngx_int_t error); +ngx_int_t ngx_http_filter_finalize_request(ngx_http_request_t *r, + ngx_module_t *m, ngx_int_t error); +void ngx_http_clean_header(ngx_http_request_t *r); time_t ngx_http_parse_time(u_char *value, size_t len); @@ -98,23 +129,31 @@ -ngx_int_t ngx_http_discard_body(ngx_http_request_t *r); +ngx_int_t ngx_http_discard_request_body(ngx_http_request_t *r); +void ngx_http_discarded_request_body_handler(ngx_http_request_t *r); +void ngx_http_block_reading(ngx_http_request_t *r); +void ngx_http_test_reading(ngx_http_request_t *r); -extern ngx_module_t ngx_http_module; +char *ngx_http_types_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +char *ngx_http_merge_types(ngx_conf_t *cf, ngx_array_t **keys, + ngx_hash_t *types_hash, ngx_array_t **prev_keys, + ngx_hash_t *prev_types_hash, ngx_str_t *default_types); +ngx_int_t ngx_http_set_default_types(ngx_conf_t *cf, ngx_array_t **types, + ngx_str_t *default_type); +#if (NGX_HTTP_DEGRADATION) +ngx_uint_t ngx_http_degraded(ngx_http_request_t *); +#endif -extern ngx_uint_t ngx_http_total_requests; -extern uint64_t ngx_http_total_sent; +extern ngx_module_t ngx_http_module; -extern ngx_http_output_header_filter_pt ngx_http_top_header_filter; -extern ngx_http_output_body_filter_pt ngx_http_top_body_filter; +extern ngx_str_t ngx_http_html_default_types[]; -/* STUB */ -ngx_int_t ngx_http_log_handler(ngx_http_request_t *r); -/**/ +extern ngx_http_output_header_filter_pt ngx_http_top_header_filter; +extern ngx_http_output_body_filter_pt ngx_http_top_body_filter; #endif /* _NGX_HTTP_H_INCLUDED_ */ diff -Nru nginx-0.5.33/src/http/ngx_http_header_filter_module.c nginx-0.8.53/src/http/ngx_http_header_filter_module.c --- nginx-0.5.33/src/http/ngx_http_header_filter_module.c 2007-01-18 20:51:51.000000000 +0000 +++ nginx-0.8.53/src/http/ngx_http_header_filter_module.c 2010-06-30 14:39:28.000000000 +0000 @@ -45,14 +45,15 @@ }; -static char ngx_http_server_string[] = "Server: " NGINX_VER CRLF; +static char ngx_http_server_string[] = "Server: nginx" CRLF; +static char ngx_http_server_full_string[] = "Server: " NGINX_VER CRLF; static ngx_str_t ngx_http_status_lines[] = { ngx_string("200 OK"), ngx_string("201 Created"), - ngx_null_string, /* "202 Accepted" */ + ngx_string("202 Accepted"), ngx_null_string, /* "203 Non-Authoritative Information" */ ngx_string("204 No Content"), ngx_null_string, /* "205 Reset Content" */ @@ -60,20 +61,22 @@ /* ngx_null_string, */ /* "207 Multi-Status" */ -#define NGX_HTTP_LEVEL_200 7 +#define NGX_HTTP_LAST_LEVEL_200 207 +#define NGX_HTTP_LEVEL_200 (NGX_HTTP_LAST_LEVEL_200 - 200) /* ngx_null_string, */ /* "300 Multiple Choices" */ ngx_string("301 Moved Permanently"), ngx_string("302 Moved Temporarily"), - ngx_null_string, /* "303 See Other" */ + ngx_string("303 See Other"), ngx_string("304 Not Modified"), /* ngx_null_string, */ /* "305 Use Proxy" */ /* ngx_null_string, */ /* "306 unused" */ /* ngx_null_string, */ /* "307 Temporary Redirect" */ -#define NGX_HTTP_LEVEL_300 4 +#define NGX_HTTP_LAST_LEVEL_300 305 +#define NGX_HTTP_LEVEL_300 (NGX_HTTP_LAST_LEVEL_300 - 301) ngx_string("400 Bad Request"), ngx_string("401 Unauthorized"), @@ -105,7 +108,8 @@ /* ngx_null_string, */ /* "423 Locked" */ /* ngx_null_string, */ /* "424 Failed Dependency" */ -#define NGX_HTTP_LEVEL_400 17 +#define NGX_HTTP_LAST_LEVEL_400 417 +#define NGX_HTTP_LEVEL_400 (NGX_HTTP_LAST_LEVEL_400 - 400) ngx_string("500 Internal Server Error"), ngx_string("501 Method Not Implemented"), @@ -119,16 +123,15 @@ /* ngx_null_string, */ /* "508 unused" */ /* ngx_null_string, */ /* "509 unused" */ /* ngx_null_string, */ /* "510 Not Extended" */ + +#define NGX_HTTP_LAST_LEVEL_500 508 + }; ngx_http_header_out_t ngx_http_headers_out[] = { { ngx_string("Server"), offsetof(ngx_http_headers_out_t, server) }, { ngx_string("Date"), offsetof(ngx_http_headers_out_t, date) }, -#if 0 - { ngx_string("Content-Type"), - offsetof(ngx_http_headers_out_t, content_type) }, -#endif { ngx_string("Content-Length"), offsetof(ngx_http_headers_out_t, content_length) }, { ngx_string("Content-Encoding"), @@ -152,12 +155,24 @@ { u_char *p; size_t len; + ngx_str_t host, *status_line; ngx_buf_t *b; - ngx_uint_t status, i; + ngx_uint_t status, i, port; ngx_chain_t out; ngx_list_part_t *part; ngx_table_elt_t *header; + ngx_connection_t *c; ngx_http_core_loc_conf_t *clcf; + ngx_http_core_srv_conf_t *cscf; + struct sockaddr_in *sin; +#if (NGX_HAVE_INET6) + struct sockaddr_in6 *sin6; +#endif + u_char addr[NGX_SOCKADDR_STRLEN]; + + if (r->header_sent) { + return NGX_OK; + } r->header_sent = 1; @@ -191,54 +206,80 @@ if (r->headers_out.status_line.len) { len += r->headers_out.status_line.len; + status_line = &r->headers_out.status_line; #if (NGX_SUPPRESS_WARN) - status = NGX_INVALID_ARRAY_INDEX; + status = 0; #endif } else { - if (r->headers_out.status < NGX_HTTP_MOVED_PERMANENTLY) { + status = r->headers_out.status; + + if (status >= NGX_HTTP_OK + && status < NGX_HTTP_LAST_LEVEL_200) + { /* 2XX */ - status = r->headers_out.status - NGX_HTTP_OK; - if (r->headers_out.status == NGX_HTTP_NO_CONTENT) { + if (status == NGX_HTTP_NO_CONTENT) { r->header_only = 1; - r->headers_out.content_type.len = 0; - r->headers_out.content_type.data = NULL; + ngx_str_null(&r->headers_out.content_type); r->headers_out.last_modified_time = -1; r->headers_out.last_modified = NULL; r->headers_out.content_length = NULL; r->headers_out.content_length_n = -1; } - } else if (r->headers_out.status < NGX_HTTP_BAD_REQUEST) { + status -= NGX_HTTP_OK; + status_line = &ngx_http_status_lines[status]; + len += ngx_http_status_lines[status].len; + + } else if (status >= NGX_HTTP_MOVED_PERMANENTLY + && status < NGX_HTTP_LAST_LEVEL_300) + { /* 3XX */ - status = r->headers_out.status - NGX_HTTP_MOVED_PERMANENTLY - + NGX_HTTP_LEVEL_200; - if (r->headers_out.status == NGX_HTTP_NOT_MODIFIED) { + if (status == NGX_HTTP_NOT_MODIFIED) { r->header_only = 1; } - } else if (r->headers_out.status < NGX_HTTP_INTERNAL_SERVER_ERROR) { + status = status - NGX_HTTP_MOVED_PERMANENTLY + NGX_HTTP_LEVEL_200; + status_line = &ngx_http_status_lines[status]; + len += ngx_http_status_lines[status].len; + + } else if (status >= NGX_HTTP_BAD_REQUEST + && status < NGX_HTTP_LAST_LEVEL_400) + { /* 4XX */ - status = r->headers_out.status - NGX_HTTP_BAD_REQUEST - + NGX_HTTP_LEVEL_200 - + NGX_HTTP_LEVEL_300; + status = status - NGX_HTTP_BAD_REQUEST + + NGX_HTTP_LEVEL_200 + + NGX_HTTP_LEVEL_300; - } else { + status_line = &ngx_http_status_lines[status]; + len += ngx_http_status_lines[status].len; + + } else if (status >= NGX_HTTP_INTERNAL_SERVER_ERROR + && status < NGX_HTTP_LAST_LEVEL_500) + { /* 5XX */ - status = r->headers_out.status - NGX_HTTP_INTERNAL_SERVER_ERROR - + NGX_HTTP_LEVEL_200 - + NGX_HTTP_LEVEL_300 - + NGX_HTTP_LEVEL_400; - } + status = status - NGX_HTTP_INTERNAL_SERVER_ERROR + + NGX_HTTP_LEVEL_200 + + NGX_HTTP_LEVEL_300 + + NGX_HTTP_LEVEL_400; - len += ngx_http_status_lines[status].len; + status_line = &ngx_http_status_lines[status]; + len += ngx_http_status_lines[status].len; + + } else { + len += NGX_INT_T_LEN; + status_line = NULL; + } } + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + if (r->headers_out.server == NULL) { - len += sizeof(ngx_http_server_string) - 1; + len += clcf->server_tokens ? sizeof(ngx_http_server_full_string) - 1: + sizeof(ngx_http_server_string) - 1; } if (r->headers_out.date == NULL) { @@ -268,7 +309,7 @@ len += sizeof("Last-Modified: Mon, 28 Sep 1970 06:00:00 GMT" CRLF) - 1; } - clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + c = r->connection; if (r->headers_out.location && r->headers_out.location->value.len @@ -276,27 +317,65 @@ { r->headers_out.location->hash = 0; -#if (NGX_HTTP_SSL) - if (r->connection->ssl) { - len += sizeof("Location: https://") - 1 - + r->server_name.len - + r->headers_out.location->value.len + 2; + if (clcf->server_name_in_redirect) { + cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); + host = cscf->server_name; - if (clcf->port_in_redirect && r->port != 443) { - len += r->port_text->len; + } 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(c, &host, 0) != NGX_OK) { + return NGX_ERROR; } + } - } else + switch (c->local_sockaddr->sa_family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + sin6 = (struct sockaddr_in6 *) c->local_sockaddr; + port = ntohs(sin6->sin6_port); + break; #endif - { - len += sizeof("Location: http://") - 1 - + r->server_name.len - + r->headers_out.location->value.len + 2; +#if (NGX_HAVE_UNIX_DOMAIN) + case AF_UNIX: + port = 0; + break; +#endif + default: /* AF_INET */ + sin = (struct sockaddr_in *) c->local_sockaddr; + port = ntohs(sin->sin_port); + break; + } - if (clcf->port_in_redirect && r->port != 80) { - len += r->port_text->len; - } + len += sizeof("Location: https://") - 1 + + host.len + + r->headers_out.location->value.len + 2; + + if (clcf->port_in_redirect) { + +#if (NGX_HTTP_SSL) + if (c->ssl) + port = (port == 443) ? 0 : port; + else +#endif + port = (port == 80) ? 0 : port; + + } else { + port = 0; + } + + if (port) { + len += sizeof(":65535") - 1; } + + } else { + ngx_str_null(&host); + port = 0; } if (r->chunked) { @@ -322,6 +401,17 @@ len += sizeof("Connection: closed" CRLF) - 1; } +#if (NGX_HTTP_GZIP) + if (r->gzip_vary) { + if (clcf->gzip_vary) { + len += sizeof("Vary: Accept-Encoding" CRLF) - 1; + + } else { + r->gzip_vary = 0; + } + } +#endif + part = &r->headers_out.headers.part; header = part->elts; @@ -354,19 +444,25 @@ b->last = ngx_cpymem(b->last, "HTTP/1.1 ", sizeof("HTTP/1.x ") - 1); /* status line */ - if (r->headers_out.status_line.len) { - b->last = ngx_copy(b->last, r->headers_out.status_line.data, - r->headers_out.status_line.len); + if (status_line) { + b->last = ngx_copy(b->last, status_line->data, status_line->len); } else { - b->last = ngx_copy(b->last, ngx_http_status_lines[status].data, - ngx_http_status_lines[status].len); + b->last = ngx_sprintf(b->last, "%ui", status); } *b->last++ = CR; *b->last++ = LF; if (r->headers_out.server == NULL) { - b->last = ngx_cpymem(b->last, ngx_http_server_string, - sizeof(ngx_http_server_string) - 1); + if (clcf->server_tokens) { + p = (u_char *) ngx_http_server_full_string; + len = sizeof(ngx_http_server_full_string) - 1; + + } else { + p = (u_char *) ngx_http_server_string; + len = sizeof(ngx_http_server_string) - 1; + } + + b->last = ngx_cpymem(b->last, p, len); } if (r->headers_out.date == NULL) { @@ -418,39 +514,24 @@ *b->last++ = CR; *b->last++ = LF; } - if (r->headers_out.location - && r->headers_out.location->value.len - && r->headers_out.location->value.data[0] == '/') - { + if (host.data) { + p = b->last + sizeof("Location: ") - 1; b->last = ngx_cpymem(b->last, "Location: http", sizeof("Location: http") - 1); #if (NGX_HTTP_SSL) - if (r->connection->ssl) { + if (c->ssl) { *b->last++ ='s'; } #endif *b->last++ = ':'; *b->last++ = '/'; *b->last++ = '/'; - b->last = ngx_copy(b->last, r->server_name.data, r->server_name.len); + b->last = ngx_copy(b->last, host.data, host.len); - if (clcf->port_in_redirect) { -#if (NGX_HTTP_SSL) - if (r->connection->ssl) { - if (r->port != 443) { - b->last = ngx_copy(b->last, r->port_text->data, - r->port_text->len); - } - } else -#endif - { - if (r->port != 80) { - b->last = ngx_copy(b->last, r->port_text->data, - r->port_text->len); - } - } + if (port) { + b->last = ngx_sprintf(b->last, ":%ui", port); } b->last = ngx_copy(b->last, r->headers_out.location->value.data, @@ -460,8 +541,7 @@ r->headers_out.location->value.len = b->last - p; r->headers_out.location->value.data = p; - r->headers_out.location->key.len = sizeof("Location: ") - 1; - r->headers_out.location->key.data = (u_char *) "Location: "; + ngx_str_set(&r->headers_out.location->key, "Location"); *b->last++ = CR; *b->last++ = LF; } @@ -485,6 +565,13 @@ sizeof("Connection: close" CRLF) - 1); } +#if (NGX_HTTP_GZIP) + if (r->gzip_vary) { + b->last = ngx_cpymem(b->last, "Vary: Accept-Encoding" CRLF, + sizeof("Vary: Accept-Encoding" CRLF) - 1); + } +#endif + part = &r->headers_out.headers.part; header = part->elts; @@ -505,16 +592,14 @@ } b->last = ngx_copy(b->last, header[i].key.data, header[i].key.len); - *b->last++ = ':' ; *b->last++ = ' ' ; + *b->last++ = ':'; *b->last++ = ' '; b->last = ngx_copy(b->last, header[i].value.data, header[i].value.len); *b->last++ = CR; *b->last++ = LF; } -#if (NGX_DEBUG) - *b->last = '\0'; - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "%s\n", b->pos); -#endif + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "%*s", (size_t) (b->last - b->pos), b->pos); /* the end of HTTP header */ *b->last++ = CR; *b->last++ = LF; diff -Nru nginx-0.5.33/src/http/ngx_http_parse.c nginx-0.8.53/src/http/ngx_http_parse.c --- nginx-0.5.33/src/http/ngx_http_parse.c 2007-05-07 06:27:14.000000000 +0000 +++ nginx-0.8.53/src/http/ngx_http_parse.c 2010-06-23 16:34:54.000000000 +0000 @@ -112,8 +112,10 @@ sw_schema_slash_slash, sw_host, sw_port, + sw_host_http_09, sw_after_slash_in_uri, sw_check_uri, + sw_check_uri_http_09, sw_uri, sw_http_09, sw_http_H, @@ -124,6 +126,7 @@ sw_major_digit, sw_first_minor_digit, sw_minor_digit, + sw_spaces_after_digit, sw_almost_done } state; @@ -142,7 +145,7 @@ break; } - if (ch < 'A' || ch > 'Z') { + if ((ch < 'A' || ch > 'Z') && ch != '_') { return NGX_HTTP_PARSE_INVALID_METHOD; } @@ -207,6 +210,10 @@ r->method = NGX_HTTP_MKCOL; } + if (ngx_str5cmp(m, 'P', 'A', 'T', 'C', 'H')) { + r->method = NGX_HTTP_PATCH; + } + if (ngx_str5cmp(m, 'T', 'R', 'A', 'C', 'E')) { r->method = NGX_HTTP_TRACE; } @@ -256,7 +263,7 @@ break; } - if (ch < 'A' || ch > 'Z') { + if ((ch < 'A' || ch > 'Z') && ch != '_') { return NGX_HTTP_PARSE_INVALID_METHOD; } @@ -265,7 +272,7 @@ /* space* before URI */ case sw_spaces_before_uri: - if (ch == '/' ){ + if (ch == '/') { r->uri_start = p; state = sw_after_slash_in_uri; break; @@ -335,18 +342,26 @@ break; } + r->host_end = p; + switch (ch) { case ':': - r->host_end = p; state = sw_port; break; case '/': - r->host_end = p; r->uri_start = p; state = sw_after_slash_in_uri; break; + case ' ': + /* + * use single "/" from request line to preserve pointers, + * if request line will be copied to large client buffer + */ + r->uri_start = r->schema_end + 1; + r->uri_end = r->schema_end + 2; + state = sw_host_http_09; + break; default: - r->host_end = p; return NGX_HTTP_PARSE_INVALID_REQUEST; } break; @@ -362,11 +377,43 @@ r->uri_start = p; state = sw_after_slash_in_uri; break; + case ' ': + r->port_end = p; + /* + * use single "/" from request line to preserve pointers, + * if request line will be copied to large client buffer + */ + r->uri_start = r->schema_end + 1; + r->uri_end = r->schema_end + 2; + state = sw_host_http_09; + break; default: return NGX_HTTP_PARSE_INVALID_REQUEST; } break; + /* space+ after "http://host[:port] " */ + case sw_host_http_09: + switch (ch) { + case ' ': + break; + case CR: + r->http_minor = 9; + state = sw_almost_done; + break; + case LF: + r->http_minor = 9; + goto done; + case 'H': + r->http_protocol.data = p; + state = sw_http_H; + break; + default: + return NGX_HTTP_PARSE_INVALID_REQUEST; + } + break; + + /* check "/.", "//", "%", and "\" (Win32) in URI */ case sw_after_slash_in_uri: @@ -378,7 +425,7 @@ switch (ch) { case ' ': r->uri_end = p; - state = sw_http_09; + state = sw_check_uri_http_09; break; case CR: r->uri_end = p; @@ -419,8 +466,7 @@ r->plus_in_uri = 1; break; case '\0': - r->zero_in_uri = 1; - break; + return NGX_HTTP_PARSE_INVALID_REQUEST; default: state = sw_check_uri; break; @@ -444,7 +490,7 @@ break; case ' ': r->uri_end = p; - state = sw_http_09; + state = sw_check_uri_http_09; break; case CR: r->uri_end = p; @@ -477,11 +523,34 @@ r->plus_in_uri = 1; break; case '\0': - r->zero_in_uri = 1; + return NGX_HTTP_PARSE_INVALID_REQUEST; + } + break; + + /* space+ after URI */ + case sw_check_uri_http_09: + switch (ch) { + case ' ': + break; + case CR: + r->http_minor = 9; + state = sw_almost_done; + break; + case LF: + r->http_minor = 9; + goto done; + case 'H': + r->http_protocol.data = p; + state = sw_http_H; + break; + default: + r->space_in_uri = 1; + state = sw_check_uri; break; } break; + /* URI */ case sw_uri: @@ -507,8 +576,7 @@ r->complex_uri = 1; break; case '\0': - r->zero_in_uri = 1; - break; + return NGX_HTTP_PARSE_INVALID_REQUEST; } break; @@ -529,7 +597,9 @@ state = sw_http_H; break; default: - return NGX_HTTP_PARSE_INVALID_REQUEST; + r->space_in_uri = 1; + state = sw_uri; + break; } break; @@ -618,6 +688,11 @@ goto done; } + if (ch == ' ') { + state = sw_spaces_after_digit; + break; + } + if (ch < '0' || ch > '9') { return NGX_HTTP_PARSE_INVALID_REQUEST; } @@ -625,6 +700,20 @@ r->http_minor = r->http_minor * 10 + ch - '0'; break; + case sw_spaces_after_digit: + switch (ch) { + case ' ': + break; + case CR: + state = sw_almost_done; + break; + case LF: + goto done; + default: + return NGX_HTTP_PARSE_INVALID_REQUEST; + } + break; + /* end of request line */ case sw_almost_done: r->request_end = p - 1; @@ -662,7 +751,8 @@ ngx_int_t -ngx_http_parse_header_line(ngx_http_request_t *r, ngx_buf_t *b) +ngx_http_parse_header_line(ngx_http_request_t *r, ngx_buf_t *b, + ngx_uint_t allow_underscores) { u_char c, ch, *p; ngx_uint_t hash, i; @@ -700,6 +790,7 @@ /* first char */ case sw_start: + r->header_name_start = p; r->invalid_header = 0; switch (ch) { @@ -712,7 +803,6 @@ goto header_done; default: state = sw_name; - r->header_name_start = p; c = lowcase[ch]; @@ -737,7 +827,20 @@ if (c) { hash = ngx_hash(hash, c); r->lowcase_header[i++] = c; - i &= ~NGX_HTTP_LC_HEADER_LEN; + i &= (NGX_HTTP_LC_HEADER_LEN - 1); + break; + } + + if (ch == '_') { + if (allow_underscores) { + hash = ngx_hash(hash, ch); + r->lowcase_header[i++] = ch; + i &= (NGX_HTTP_LC_HEADER_LEN - 1); + + } else { + r->invalid_header = 1; + } + break; } @@ -844,10 +947,10 @@ /* end of header line */ case sw_almost_done: switch (ch) { - case CR: - break; case LF: goto done; + case CR: + break; default: return NGX_HTTP_PARSE_INVALID_HEADER; } @@ -890,7 +993,7 @@ ngx_int_t -ngx_http_parse_complex_uri(ngx_http_request_t *r) +ngx_http_parse_complex_uri(ngx_http_request_t *r, ngx_uint_t merge_slashes) { u_char c, ch, decoded, *p, *u; enum { @@ -898,9 +1001,6 @@ sw_slash, sw_dot, sw_dot_dot, -#if (NGX_WIN32) - sw_dot_dot_dot, -#endif sw_quoted, sw_quoted_second } state, quoted_state; @@ -998,8 +1098,12 @@ switch(ch) { #if (NGX_WIN32) case '\\': + break; #endif case '/': + if (!merge_slashes) { + *u++ = ch; + } break; case '.': state = sw_dot; @@ -1081,65 +1185,15 @@ #endif case '/': state = sw_slash; - u -= 4; - if (u < r->uri.data) { - return NGX_HTTP_PARSE_INVALID_REQUEST; - } - while (*(u - 1) != '/') { - u--; - } - break; - case '%': - quoted_state = state; - state = sw_quoted; - break; - case '?': - r->args_start = p; - goto args; - case '#': - goto done; -#if (NGX_WIN32) - case '.': - state = sw_dot_dot_dot; - *u++ = ch; - break; -#endif - case '+': - r->plus_in_uri = 1; - default: - state = sw_usual; - *u++ = ch; - break; - } - - ch = *p++; - break; - -#if (NGX_WIN32) - case sw_dot_dot_dot: - - if (usual[ch >> 5] & (1 << (ch & 0x1f))) { - state = sw_usual; - *u++ = ch; - ch = *p++; - break; - } - - switch(ch) { - case '\\': - case '/': - state = sw_slash; u -= 5; - if (u < r->uri.data) { - return NGX_HTTP_PARSE_INVALID_REQUEST; - } - while (*u != '/') { - u--; - } - if (u < r->uri.data) { - return NGX_HTTP_PARSE_INVALID_REQUEST; - } - while (*(u - 1) != '/') { + for ( ;; ) { + if (u < r->uri.data) { + return NGX_HTTP_PARSE_INVALID_REQUEST; + } + if (*u == '/') { + u++; + break; + } u--; } break; @@ -1162,7 +1216,6 @@ ch = *p++; break; -#endif case sw_quoted: r->quoted_uri = 1; @@ -1188,19 +1241,14 @@ if (ch >= '0' && ch <= '9') { ch = (u_char) ((decoded << 4) + ch - '0'); - if (ch == '%') { + if (ch == '%' || ch == '#') { state = sw_usual; *u++ = ch; ch = *p++; break; - } - - if (ch == '#') { - *u++ = ch; - ch = *p++; } else if (ch == '\0') { - r->zero_in_uri = 1; + return NGX_HTTP_PARSE_INVALID_REQUEST; } state = quoted_state; @@ -1212,8 +1260,10 @@ ch = (u_char) ((decoded << 4) + c - 'a' + 10); if (ch == '?') { + state = sw_usual; *u++ = ch; ch = *p++; + break; } else if (ch == '+') { r->plus_in_uri = 1; @@ -1268,6 +1318,211 @@ ngx_int_t +ngx_http_parse_status_line(ngx_http_request_t *r, ngx_buf_t *b, + ngx_http_status_t *status) +{ + u_char ch; + u_char *p; + enum { + sw_start = 0, + sw_H, + sw_HT, + sw_HTT, + sw_HTTP, + sw_first_major_digit, + sw_major_digit, + sw_first_minor_digit, + sw_minor_digit, + sw_status, + sw_space_after_status, + sw_status_text, + sw_almost_done + } state; + + state = r->state; + + for (p = b->pos; p < b->last; p++) { + ch = *p; + + switch (state) { + + /* "HTTP/" */ + case sw_start: + switch (ch) { + case 'H': + state = sw_H; + break; + default: + return NGX_ERROR; + } + break; + + case sw_H: + switch (ch) { + case 'T': + state = sw_HT; + break; + default: + return NGX_ERROR; + } + break; + + case sw_HT: + switch (ch) { + case 'T': + state = sw_HTT; + break; + default: + return NGX_ERROR; + } + break; + + case sw_HTT: + switch (ch) { + case 'P': + state = sw_HTTP; + break; + default: + return NGX_ERROR; + } + break; + + case sw_HTTP: + switch (ch) { + case '/': + state = sw_first_major_digit; + break; + default: + return NGX_ERROR; + } + break; + + /* the first digit of major HTTP version */ + case sw_first_major_digit: + if (ch < '1' || ch > '9') { + return NGX_ERROR; + } + + state = sw_major_digit; + break; + + /* the major HTTP version or dot */ + case sw_major_digit: + if (ch == '.') { + state = sw_first_minor_digit; + break; + } + + if (ch < '0' || ch > '9') { + return NGX_ERROR; + } + + break; + + /* the first digit of minor HTTP version */ + case sw_first_minor_digit: + if (ch < '0' || ch > '9') { + return NGX_ERROR; + } + + state = sw_minor_digit; + break; + + /* the minor HTTP version or the end of the request line */ + case sw_minor_digit: + if (ch == ' ') { + state = sw_status; + break; + } + + if (ch < '0' || ch > '9') { + return NGX_ERROR; + } + + break; + + /* HTTP status code */ + case sw_status: + if (ch == ' ') { + break; + } + + if (ch < '0' || ch > '9') { + return NGX_ERROR; + } + + status->code = status->code * 10 + ch - '0'; + + if (++status->count == 3) { + state = sw_space_after_status; + status->start = p - 2; + } + + break; + + /* space or end of line */ + case sw_space_after_status: + switch (ch) { + case ' ': + state = sw_status_text; + break; + case '.': /* IIS may send 403.1, 403.2, etc */ + state = sw_status_text; + break; + case CR: + state = sw_almost_done; + break; + case LF: + goto done; + default: + return NGX_ERROR; + } + break; + + /* any text until end of line */ + case sw_status_text: + switch (ch) { + case CR: + state = sw_almost_done; + + break; + case LF: + goto done; + } + break; + + /* end of status line */ + case sw_almost_done: + status->end = p - 1; + switch (ch) { + case LF: + goto done; + default: + return NGX_ERROR; + } + } + } + + b->pos = p; + r->state = state; + + return NGX_AGAIN; + +done: + + b->pos = p + 1; + + if (status->end == NULL) { + status->end = p; + } + + r->state = sw_start; + + return NGX_OK; +} + + +ngx_int_t ngx_http_parse_unsafe_uri(ngx_http_request_t *r, ngx_str_t *uri, ngx_str_t *args, ngx_uint_t *flags) { @@ -1281,12 +1536,7 @@ goto unsafe; } - if (p[0] == '.' && len == 3 && p[1] == '.' && (p[2] == '/' -#if (NGX_WIN32) - || p[2] == '\\' -#endif - )) - { + if (p[0] == '.' && len == 3 && p[1] == '.' && (ngx_path_separator(p[2]))) { goto unsafe; } @@ -1307,39 +1557,16 @@ } if (ch == '\0') { - *flags |= NGX_HTTP_ZERO_IN_URI; - continue; + goto unsafe; } - if ((ch == '/' -#if (NGX_WIN32) - || ch == '\\' -#endif - ) && len > 2) - { - /* detect "/../" */ - - if (p[0] == '.' && p[1] == '.' && p[2] == '/') { - goto unsafe; - } + if (ngx_path_separator(ch) && len > 2) { -#if (NGX_WIN32) + /* detect "/../" */ - if (p[2] == '\\') { + if (p[0] == '.' && p[1] == '.' && ngx_path_separator(p[2])) { goto unsafe; } - - if (len > 3) { - - /* detect "/.../" */ - - if (p[0] == '.' && p[1] == '.' && p[2] == '.' - && (p[3] == '/' || p[3] == '\\')) - { - goto unsafe; - } - } -#endif } } @@ -1347,8 +1574,10 @@ unsafe: - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "unsafe URI \"%V\" was detected", uri); + if (*flags & NGX_HTTP_LOG_UNSAFE) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "unsafe URI \"%V\" was detected", uri); + } return NGX_ERROR; } @@ -1425,3 +1654,66 @@ return NGX_DECLINED; } + + +ngx_int_t +ngx_http_arg(ngx_http_request_t *r, u_char *name, size_t len, ngx_str_t *value) +{ + u_char *p, *last; + + if (r->args.len == 0) { + return NGX_DECLINED; + } + + p = r->args.data; + last = p + r->args.len; + + for ( /* void */ ; p < last; p++) { + + /* we need '=' after name, so drop one char from last */ + + p = ngx_strlcasestrn(p, last - 1, name, len - 1); + + if (p == NULL) { + return NGX_DECLINED; + } + + if ((p == r->args.data || *(p - 1) == '&') && *(p + len) == '=') { + + value->data = p + len + 1; + + p = ngx_strlchr(p, last, '&'); + + if (p == NULL) { + p = r->args.data + r->args.len; + } + + value->len = p - value->data; + + return NGX_OK; + } + } + + return NGX_DECLINED; +} + + +void +ngx_http_split_args(ngx_http_request_t *r, ngx_str_t *uri, ngx_str_t *args) +{ + u_char *p, *last; + + last = uri->data + uri->len; + + p = ngx_strlchr(uri->data, last, '?'); + + if (p) { + uri->len = p - uri->data; + p++; + args->len = last - p; + args->data = p; + + } else { + args->len = 0; + } +} diff -Nru nginx-0.5.33/src/http/ngx_http_parse_time.c nginx-0.8.53/src/http/ngx_http_parse_time.c --- nginx-0.5.33/src/http/ngx_http_parse_time.c 2007-01-19 11:35:26.000000000 +0000 +++ nginx-0.8.53/src/http/ngx_http_parse_time.c 2009-10-15 13:19:34.000000000 +0000 @@ -6,15 +6,17 @@ #include #include -#include -static int mday[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; +static ngx_uint_t mday[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; -time_t ngx_http_parse_time(u_char *value, size_t len) +time_t +ngx_http_parse_time(u_char *value, size_t len) { - u_char *p, *end; - int day, month, year, hour, min, sec; + u_char *p, *end; + ngx_int_t month; + ngx_uint_t day, year, hour, min, sec; + uint64_t time; enum { no = 0, rfc822, /* Tue, 10 Nov 2002 23:50:13 */ @@ -229,17 +231,9 @@ return NGX_ERROR; } -#if (NGX_TIME_T_SIZE <= 4) - - if (year >= 2038) { - return NGX_ERROR; - } - -#endif - /* * shift new year to March 1 and start months from 1 (not 0), - * it is needed for Gauss's formula + * it is needed for Gauss' formula */ if (--month <= 0) { @@ -247,11 +241,20 @@ year -= 1; } - /* Gauss's formula for Grigorian days from 1 March 1 BC */ + /* Gauss' formula for Grigorian days since March 1, 1 BC */ + + time = (uint64_t) ( + /* days in years including leap years since March 1, 1 BC */ + + 365 * year + year / 4 - year / 100 + year / 400 + + /* days before the month */ - return (365 * year + year / 4 - year / 100 + year / 400 - + 367 * month / 12 - 31 - + day + + 367 * month / 12 - 30 + + /* days before the day */ + + + day - 1 /* * 719527 days were between March 1, 1 BC and March 1, 1970, @@ -259,4 +262,14 @@ */ - 719527 + 31 + 28) * 86400 + hour * 3600 + min * 60 + sec; + +#if (NGX_TIME_T_SIZE <= 4) + + if (time > 0x7fffffff) { + return NGX_ERROR; + } + +#endif + + return (time_t) time; } diff -Nru nginx-0.5.33/src/http/ngx_http_postpone_filter_module.c nginx-0.8.53/src/http/ngx_http_postpone_filter_module.c --- nginx-0.5.33/src/http/ngx_http_postpone_filter_module.c 2006-08-30 10:39:17.000000000 +0000 +++ nginx-0.8.53/src/http/ngx_http_postpone_filter_module.c 2009-08-30 09:47:11.000000000 +0000 @@ -9,8 +9,8 @@ #include -static ngx_int_t - ngx_http_postpone_filter_output_postponed_request(ngx_http_request_t *r); +static ngx_int_t ngx_http_postpone_filter_add(ngx_http_request_t *r, + ngx_chain_t *in); static ngx_int_t ngx_http_postpone_filter_init(ngx_conf_t *cf); @@ -51,170 +51,119 @@ static ngx_int_t ngx_http_postpone_filter(ngx_http_request_t *r, ngx_chain_t *in) { - ngx_int_t rc; - ngx_chain_t *out; - ngx_http_postponed_request_t *pr, **ppr; - - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http postpone filter \"%V?%V\" %p", &r->uri, &r->args, in); + ngx_connection_t *c; + ngx_http_postponed_request_t *pr; - if (r != r->connection->data || (r->postponed && in)) { + c = r->connection; - if (r->postponed) { - for (pr = r->postponed; pr->next; pr = pr->next) { /* void */ } + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http postpone filter \"%V?%V\" %p", &r->uri, &r->args, in); - ppr = pr->request ? &pr->next : NULL; + if (r != c->data) { - } else { - ppr = &r->postponed; -#if (NGX_SUPPRESS_WARN) - pr = NULL; -#endif + if (in) { + ngx_http_postpone_filter_add(r, in); + return NGX_OK; } - if (ppr) { - pr = ngx_palloc(r->pool, sizeof(ngx_http_postponed_request_t)); - if (pr == NULL) { - return NGX_ERROR; - } +#if 0 + /* TODO: SSI may pass NULL */ + ngx_log_error(NGX_LOG_ALERT, c->log, 0, + "http postpone filter NULL inactive request", + &r->uri, &r->args); +#endif - *ppr = pr; + return NGX_OK; + } - pr->request = NULL; - pr->out = NULL; - pr->next = NULL; - } + if (r->postponed == NULL) { - if (ngx_chain_add_copy(r->pool, &pr->out, in) == NGX_ERROR) { - return NGX_ERROR; + if (in || c->buffered) { + return ngx_http_next_filter(r->main, in); } -#if 1 - { - ngx_chain_t *cl; - ngx_buf_t *b = NULL; - for (cl = pr->out; cl; cl = cl->next) { - if (cl->buf == b) { - ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, - "the same buf was used in postponed %p %p", - b, b->pos); - ngx_debug_point(); - return NGX_ERROR; - } - b = cl->buf; - } - } -#endif + return NGX_OK; + } - if (r != r->connection->data || r->postponed->request) { - return NGX_AGAIN; - } + if (in) { + ngx_http_postpone_filter_add(r, in); } - if (r->postponed) { - out = r->postponed->out; - if (out) { - r->postponed = r->postponed->next; - } + do { + pr = r->postponed; - } else { - out = in; - } + if (pr->request) { + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http postpone filter wake \"%V?%V\"", + &pr->request->uri, &pr->request->args); - rc = NGX_OK; + r->postponed = pr->next; - if (out - || (r->connection->buffered - & (NGX_HTTP_LOWLEVEL_BUFFERED|NGX_LOWLEVEL_BUFFERED))) - { - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http postpone filter out \"%V?%V\"", &r->uri, &r->args); + c->data = pr->request; - if (!(out && out->next == NULL && ngx_buf_sync_only(out->buf))) { + return ngx_http_post_request(pr->request, NULL); + } - rc = ngx_http_next_filter(r->main, out); + if (pr->out == NULL) { + ngx_log_error(NGX_LOG_ALERT, c->log, 0, + "http postpone filter NULL output", + &r->uri, &r->args); - if (rc == NGX_ERROR) { - /* NGX_ERROR may be returned by any filter */ - r->connection->error = 1; + } else { + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http postpone filter output \"%V?%V\"", + &r->uri, &r->args); + + if (ngx_http_next_filter(r->main, pr->out) == NGX_ERROR) { + return NGX_ERROR; } } - } - - if (r->postponed == NULL) { - return rc; - } - rc = ngx_http_postpone_filter_output_postponed_request(r); + r->postponed = pr->next; - if (rc == NGX_ERROR) { - /* NGX_ERROR may be returned by any filter */ - r->connection->error = 1; - } + } while (r->postponed); - return rc; + return NGX_OK; } static ngx_int_t -ngx_http_postpone_filter_output_postponed_request(ngx_http_request_t *r) +ngx_http_postpone_filter_add(ngx_http_request_t *r, ngx_chain_t *in) { - ngx_int_t rc; - ngx_chain_t *out; - ngx_http_log_ctx_t *ctx; - ngx_http_postponed_request_t *pr; + ngx_http_postponed_request_t *pr, **ppr; - for ( ;; ) { - pr = r->postponed; + if (r->postponed) { + for (pr = r->postponed; pr->next; pr = pr->next) { /* void */ } - if (pr == NULL) { - return NGX_OK; + if (pr->request == NULL) { + goto found; } - if (pr->request) { - - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http postpone filter handle \"%V?%V\"", - &pr->request->uri, &pr->request->args); - - ctx = r->connection->log->data; - ctx->current_request = pr->request; - - if (!pr->request->done) { - r->connection->data = pr->request; - return NGX_AGAIN; - } - - rc = ngx_http_postpone_filter_output_postponed_request(pr->request); + ppr = &pr->next; - if (rc == NGX_AGAIN || rc == NGX_ERROR) { - return rc; - } - - r->postponed = r->postponed->next; - pr = r->postponed; - } + } else { + ppr = &r->postponed; + } - if (pr == NULL) { - return NGX_OK; - } + pr = ngx_palloc(r->pool, sizeof(ngx_http_postponed_request_t)); + if (pr == NULL) { + return NGX_ERROR; + } - out = pr->out; + *ppr = pr; - if (out) { - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http postpone filter out postponed \"%V?%V\"", - &r->uri, &r->args); + pr->request = NULL; + pr->out = NULL; + pr->next = NULL; - if (!(out && out->next == NULL && ngx_buf_sync_only(out->buf))) { - if (ngx_http_next_filter(r->main, out) == NGX_ERROR) { - return NGX_ERROR; - } - } - } +found: - r->postponed = r->postponed->next; + if (ngx_chain_add_copy(r->pool, &pr->out, in) == NGX_OK) { + return NGX_OK; } + + return NGX_ERROR; } diff -Nru nginx-0.5.33/src/http/ngx_http_request_body.c nginx-0.8.53/src/http/ngx_http_request_body.c --- nginx-0.5.33/src/http/ngx_http_request_body.c 2007-07-13 10:47:26.000000000 +0000 +++ nginx-0.8.53/src/http/ngx_http_request_body.c 2009-10-19 16:12:13.000000000 +0000 @@ -6,7 +6,6 @@ #include #include -#include #include @@ -14,8 +13,8 @@ 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, ngx_chain_t *body); -static void ngx_http_read_discarded_body_handler(ngx_http_request_t *r); -static ngx_int_t ngx_http_read_discarded_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_test_expect(ngx_http_request_t *r); /* @@ -37,11 +36,17 @@ ngx_http_request_body_t *rb; ngx_http_core_loc_conf_t *clcf; + r->main->count++; + if (r->request_body || r->discard_body) { post_handler(r); return NGX_OK; } + if (ngx_http_test_expect(r) != NGX_OK) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t)); if (rb == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; @@ -328,7 +333,7 @@ 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_ERROR) { + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } @@ -425,7 +430,7 @@ ngx_int_t -ngx_http_discard_body(ngx_http_request_t *r) +ngx_http_discard_request_body(ngx_http_request_t *r) { ssize_t size; ngx_event_t *rev; @@ -434,6 +439,10 @@ return NGX_OK; } + 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"); @@ -442,9 +451,7 @@ ngx_del_timer(rev); } - r->discard_body = 1; - - if (r->headers_in.content_length_n <= 0) { + if (r->headers_in.content_length_n <= 0 || r->request_body) { return NGX_OK; } @@ -452,6 +459,7 @@ if (size) { if (r->headers_in.content_length_n > size) { + r->header_in->pos += size; r->headers_in.content_length_n -= size; } else { @@ -461,38 +469,91 @@ } } - r->read_event_handler = ngx_http_read_discarded_body_handler; + r->read_event_handler = ngx_http_discarded_request_body_handler; - if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { + if (ngx_handle_read_event(rev, 0) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } - return ngx_http_read_discarded_body(r); + if (ngx_http_read_discarded_request_body(r) == NGX_OK) { + r->lingering_close = 0; + + } else { + r->count++; + r->discard_body = 1; + } + + return NGX_OK; } -static void -ngx_http_read_discarded_body_handler(ngx_http_request_t *r) +void +ngx_http_discarded_request_body_handler(ngx_http_request_t *r) { - ngx_int_t rc; + 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; - rc = ngx_http_read_discarded_body(r); + if (rev->timedout) { + c->timedout = 1; + c->error = 1; + ngx_http_finalize_request(r, NGX_ERROR); + return; + } - if (rc == NGX_AGAIN) { - if (ngx_handle_read_event(r->connection->read, 0) == NGX_ERROR) { - ngx_http_finalize_request(r, rc); + if (r->lingering_time) { + timer = (ngx_msec_t) (r->lingering_time - ngx_time()); + + if (timer <= 0) { + r->discard_body = 0; + r->lingering_close = 0; + ngx_http_finalize_request(r, NGX_ERROR); return; } + + } else { + timer = 0; } - if (rc != NGX_OK) { - ngx_http_finalize_request(r, rc); + 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; + } + + /* 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_body(ngx_http_request_t *r) +ngx_http_read_discarded_request_body(ngx_http_request_t *r) { size_t size; ssize_t n; @@ -501,33 +562,77 @@ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http read discarded body"); - if (r->headers_in.content_length_n == 0) { - return NGX_OK; + 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 = (r->headers_in.content_length_n > NGX_HTTP_DISCARD_BUFFER_SIZE) ? + NGX_HTTP_DISCARD_BUFFER_SIZE: + (size_t) r->headers_in.content_length_n; + + 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; + } + + r->headers_in.content_length_n -= n; } +} - size = (r->headers_in.content_length_n > NGX_HTTP_DISCARD_BUFFER_SIZE) ? - NGX_HTTP_DISCARD_BUFFER_SIZE: - (size_t) r->headers_in.content_length_n; - n = r->connection->recv(r->connection, buffer, size); +static ngx_int_t +ngx_http_test_expect(ngx_http_request_t *r) +{ + ngx_int_t n; + ngx_str_t *expect; - if (n == NGX_ERROR) { + if (r->expect_tested + || r->headers_in.expect == NULL + || r->http_version < NGX_HTTP_VERSION_11) + { + return NGX_OK; + } - r->connection->error = 1; + r->expect_tested = 1; - /* - * if a client request body is discarded then we already set - * some HTTP response code for client and we can ignore the error - */ + 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; } - if (n == NGX_AGAIN) { - return NGX_AGAIN; + 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; } - r->headers_in.content_length_n -= n; + /* we assume that such small packet should be send successfully */ - return NGX_OK; + return NGX_ERROR; } diff -Nru nginx-0.5.33/src/http/ngx_http_request.c nginx-0.8.53/src/http/ngx_http_request.c --- nginx-0.5.33/src/http/ngx_http_request.c 2007-11-07 13:46:29.000000000 +0000 +++ nginx-0.8.53/src/http/ngx_http_request.c 2010-07-05 13:35:20.000000000 +0000 @@ -6,7 +6,6 @@ #include #include -#include #include @@ -21,29 +20,38 @@ 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_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_process_cookie(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset); static ngx_int_t ngx_http_process_request_header(ngx_http_request_t *r); static void ngx_http_process_request(ngx_http_request_t *r); -static void ngx_http_find_virtual_server(ngx_http_request_t *r, u_char *host, - size_t len, ngx_uint_t hash); +static ssize_t ngx_http_validate_host(ngx_http_request_t *r, u_char **host, + size_t len, ngx_uint_t alloc); +static ngx_int_t ngx_http_find_virtual_server(ngx_http_request_t *r, + u_char *host, size_t len); 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_block_read(ngx_http_request_t *r); -static void ngx_http_test_read(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_request_done(ngx_http_request_t *r, ngx_int_t error); +static void ngx_http_free_request(ngx_http_request_t *r, ngx_int_t error); +static void ngx_http_log_request(ngx_http_request_t *r); static void ngx_http_close_connection(ngx_connection_t *c); static u_char *ngx_http_log_error(ngx_log_t *log, u_char *buf, size_t len); @@ -71,7 +79,7 @@ ngx_http_header_t ngx_http_headers_in[] = { { ngx_string("Host"), offsetof(ngx_http_headers_in_t, host), - ngx_http_process_unique_header_line }, + ngx_http_process_host }, { ngx_string("Connection"), offsetof(ngx_http_headers_in_t, connection), ngx_http_process_connection }, @@ -81,7 +89,7 @@ ngx_http_process_unique_header_line }, { ngx_string("User-Agent"), offsetof(ngx_http_headers_in_t, user_agent), - ngx_http_process_header_line }, + ngx_http_process_user_agent }, { ngx_string("Referer"), offsetof(ngx_http_headers_in_t, referer), ngx_http_process_header_line }, @@ -97,10 +105,18 @@ { 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("Expect"), + offsetof(ngx_http_headers_in_t, expect), + ngx_http_process_unique_header_line }, + #if (NGX_HTTP_GZIP) { ngx_string("Accept-Encoding"), offsetof(ngx_http_headers_in_t, accept_encoding), @@ -117,7 +133,7 @@ { ngx_string("Keep-Alive"), offsetof(ngx_http_headers_in_t, keep_alive), ngx_http_process_header_line }, -#if (NGX_HTTP_PROXY || NGX_HTTP_REALIP) +#if (NGX_HTTP_PROXY || NGX_HTTP_REALIP || NGX_HTTP_GEO) { ngx_string("X-Forwarded-For"), offsetof(ngx_http_headers_in_t, x_forwarded_for), ngx_http_process_header_line }, @@ -170,7 +186,7 @@ return; } - ctx->client = &c->addr_text; + ctx->connection = c; ctx->request = NULL; ctx->current_request = NULL; @@ -186,7 +202,7 @@ c->write->handler = ngx_http_empty_handler; #if (NGX_STAT_STUB) - ngx_atomic_fetch_add(ngx_stat_reading, 1); + (void) ngx_atomic_fetch_add(ngx_stat_reading, 1); #endif if (rev->ready) { @@ -203,9 +219,9 @@ ngx_add_timer(rev, c->listening->post_accept_timeout); - if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { + if (ngx_handle_read_event(rev, 0) != NGX_OK) { #if (NGX_STAT_STUB) - ngx_atomic_fetch_add(ngx_stat_reading, -1); + (void) ngx_atomic_fetch_add(ngx_stat_reading, -1); #endif ngx_http_close_connection(c); return; @@ -217,24 +233,25 @@ ngx_http_init_request(ngx_event_t *rev) { ngx_time_t *tp; - socklen_t len; ngx_uint_t i; - struct sockaddr_in sin; ngx_connection_t *c; ngx_http_request_t *r; - ngx_http_in_port_t *hip; - ngx_http_in_addr_t *hia; + struct sockaddr_in *sin; + ngx_http_port_t *port; + ngx_http_in_addr_t *addr; ngx_http_log_ctx_t *ctx; + ngx_http_addr_conf_t *addr_conf; 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; -#if (NGX_HTTP_SSL) - ngx_http_ssl_srv_conf_t *sscf; +#if (NGX_HAVE_INET6) + struct sockaddr_in6 *sin6; + ngx_http_in6_addr_t *addr6; #endif #if (NGX_STAT_STUB) - ngx_atomic_fetch_add(ngx_stat_reading, -1); + (void) ngx_atomic_fetch_add(ngx_stat_reading, -1); #endif c = rev->data; @@ -246,6 +263,8 @@ return; } + c->requests++; + hc = c->data; if (hc == NULL) { @@ -285,78 +304,114 @@ /* find the server configuration for the address:port */ - /* AF_INET only */ - - hip = c->listening->servers; - hia = hip->addrs; - - r->port = hip->port; - r->port_text = &hip->port_text; + port = c->listening->servers; - i = 0; + r->connection = c; - if (hip->naddrs > 1) { + if (port->naddrs > 1) { /* - * There are several addresses on this port and one of them - * is the "*:port" wildcard so getsockname() is needed to determine - * the server address. - * - * AcceptEx() already gave this address. + * 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_WIN32) - if (c->local_sockaddr) { - r->in_addr = - ((struct sockaddr_in *) c->local_sockaddr)->sin_addr.s_addr; + if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) { + ngx_http_close_connection(c); + return; + } - } else -#endif - { - len = sizeof(struct sockaddr_in); - if (getsockname(c->fd, (struct sockaddr *) &sin, &len) == -1) { - ngx_connection_error(c, ngx_socket_errno, - "getsockname() failed"); - 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; + } } - r->in_addr = sin.sin_addr.s_addr; - } + addr_conf = &addr6[i].conf; + + break; +#endif - /* the last address is "*" */ + default: /* AF_INET */ + sin = (struct sockaddr_in *) c->local_sockaddr; - for ( /* void */ ; i < hip->naddrs - 1; i++) { - if (hia[i].addr == r->in_addr) { - break; + 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; + } } + + addr_conf = &addr[i].conf; + + break; } } else { - r->in_addr = hia[0].addr; + + switch (c->local_sockaddr->sa_family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + addr6 = port->addrs; + addr_conf = &addr6[0].conf; + break; +#endif + + default: /* AF_INET */ + addr = port->addrs; + addr_conf = &addr[0].conf; + break; + } } - r->virtual_names = hia[i].virtual_names; + r->virtual_names = addr_conf->virtual_names; /* the default server configuration for the address:port */ - cscf = hia[i].core_srv_conf; + cscf = addr_conf->default_server; r->main_conf = cscf->ctx->main_conf; r->srv_conf = cscf->ctx->srv_conf; r->loc_conf = cscf->ctx->loc_conf; - r->server_name = cscf->server_name; - rev->handler = ngx_http_process_request_line; + r->read_event_handler = ngx_http_block_reading; #if (NGX_HTTP_SSL) + { + ngx_http_ssl_srv_conf_t *sscf; + sscf = ngx_http_get_module_srv_conf(r, ngx_http_ssl_module); - if (sscf->enable) { + if (sscf->enable || addr_conf->ssl) { if (c->ssl == NULL) { + + c->log->action = "SSL handshaking"; + + if (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; + } + if (ngx_ssl_create_connection(&sscf->ssl, c, NGX_SSL_BUFFER) - == NGX_ERROR) + != NGX_OK) { ngx_http_close_connection(c); return; @@ -367,13 +422,14 @@ r->main_filter_need_in_memory = 1; } + } #endif clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); - c->log->file = clcf->err_log->file; + c->log->file = clcf->error_log->file; if (!(c->log->log_level & NGX_LOG_DEBUG_CONNECTION)) { - c->log->log_level = clcf->err_log->log_level; + c->log->log_level = clcf->error_log->log_level; } if (c->buffer == NULL) { @@ -398,15 +454,17 @@ if (ngx_list_init(&r->headers_out.headers, r->pool, 20, sizeof(ngx_table_elt_t)) - == NGX_ERROR) + != NGX_OK) { - ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + ngx_destroy_pool(r->pool); + ngx_http_close_connection(c); return; } r->ctx = ngx_pcalloc(r->pool, sizeof(void *) * ngx_http_max_module); if (r->ctx == NULL) { - ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + ngx_destroy_pool(r->pool); + ngx_http_close_connection(c); return; } @@ -415,16 +473,16 @@ r->variables = ngx_pcalloc(r->pool, cmcf->variables.nelts * sizeof(ngx_http_variable_value_t)); if (r->variables == NULL) { - ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + ngx_destroy_pool(r->pool); + ngx_http_close_connection(c); return; } c->single_connection = 1; c->destroyed = 0; - r->connection = c; - r->main = r; + r->count = 1; tp = ngx_timeofday(); r->start_sec = tp->sec; @@ -448,9 +506,9 @@ r->log_handler = ngx_http_log_error_handler; #if (NGX_STAT_STUB) - ngx_atomic_fetch_add(ngx_stat_reading, 1); + (void) ngx_atomic_fetch_add(ngx_stat_reading, 1); r->stat_reading = 1; - ngx_atomic_fetch_add(ngx_stat_requests, 1); + (void) ngx_atomic_fetch_add(ngx_stat_requests, 1); #endif rev->handler(rev); @@ -484,17 +542,31 @@ n = recv(c->fd, (char *) buf, 1, MSG_PEEK); if (n == -1 && ngx_socket_errno == NGX_EAGAIN) { + + if (!rev->timer_set) { + ngx_add_timer(rev, c->listening->post_accept_timeout); + } + + if (ngx_handle_read_event(rev, 0) != NGX_OK) { + ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + } + return; } if (n == 1) { - if (buf[0] == 0x80 /* SSLv2 */ || buf[0] == 0x16 /* SSLv3/TLSv1 */) { + 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]); rc = ngx_ssl_handshake(c); if (rc == NGX_AGAIN) { + + if (!rev->timer_set) { + ngx_add_timer(rev, c->listening->post_accept_timeout); + } + c->ssl->handler = ngx_http_ssl_handshake_handler; return; } @@ -511,6 +583,8 @@ } } + c->log->action = "reading client request line"; + rev->handler = ngx_http_process_request_line; ngx_http_process_request_line(rev); } @@ -553,8 +627,8 @@ int ngx_http_ssl_servername(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg) { - u_char *p; - ngx_uint_t hash; + size_t len; + u_char *host; const char *servername; ngx_connection_t *c; ngx_http_request_t *r; @@ -571,22 +645,25 @@ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "SSL server name: \"%s\"", servername); - r = c->data; + len = ngx_strlen(servername); - if (r->virtual_names == NULL) { + if (len == 0) { return SSL_TLSEXT_ERR_NOACK; } - /* it seems browsers send low case server name */ + r = c->data; + + host = (u_char *) servername; - hash = 0; + len = ngx_http_validate_host(r, &host, len, 1); - for (p = (u_char *) servername; *p; p++) { - hash = ngx_hash(hash, *p); + if (len <= 0) { + return SSL_TLSEXT_ERR_NOACK; } - ngx_http_find_virtual_server(r, (u_char *) servername, - p - (u_char *) servername, hash); + if (ngx_http_find_virtual_server(r, host, len) != NGX_OK) { + return SSL_TLSEXT_ERR_NOACK; + } sscf = ngx_http_get_module_srv_conf(r, ngx_http_ssl_module); @@ -603,10 +680,12 @@ static void ngx_http_process_request_line(ngx_event_t *rev) { - ssize_t n; - ngx_int_t rc, rv; - ngx_connection_t *c; - ngx_http_request_t *r; + u_char *host; + ssize_t n; + ngx_int_t rc, rv; + ngx_connection_t *c; + ngx_http_request_t *r; + ngx_http_core_srv_conf_t *cscf; c = rev->data; r = c->data; @@ -652,13 +731,15 @@ if (r->complex_uri || r->quoted_uri) { - r->uri.data = ngx_palloc(r->pool, r->uri.len + 1); + 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; } - rc = ngx_http_parse_complex_uri(r); + cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); + + rc = ngx_http_parse_complex_uri(r, cscf->merge_slashes); if (rc == NGX_HTTP_PARSE_INVALID_REQUEST) { ngx_log_error(NGX_LOG_INFO, c->log, 0, @@ -675,6 +756,7 @@ 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; r->method_name.len = r->method_end - r->request_start + 1; r->method_name.data = r->request_line.data; @@ -701,6 +783,39 @@ r->args.data = r->args_start; } +#if (NGX_WIN32) + { + u_char *p; + + p = r->uri.data + r->uri.len - 1; + + while (p > r->uri.data) { + + if (*p == ' ') { + p--; + continue; + } + + if (*p == '.') { + p--; + continue; + } + + if (ngx_strncasecmp(p - 6, (u_char *) "::$data", 7) == 0) { + p -= 7; + 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, c->log, 0, "http request line: \"%V\"", &r->request_line); @@ -714,7 +829,38 @@ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http exten: \"%V\"", &r->exten); + if (r->host_start && r->host_end) { + + host = r->host_start; + n = ngx_http_validate_host(r, &host, + r->host_end - r->host_start, 0); + + if (n == 0) { + 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 (n < 0) { + ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + r->headers_in.server.len = n; + r->headers_in.server.data = host; + } + if (r->http_version < NGX_HTTP_VERSION_10) { + + if (ngx_http_find_virtual_server(r, r->headers_in.server.data, + r->headers_in.server.len) + == NGX_ERROR) + { + ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + ngx_http_process_request(r); return; } @@ -722,7 +868,7 @@ if (ngx_list_init(&r->headers_in.headers, r->pool, 20, sizeof(ngx_table_elt_t)) - == NGX_ERROR) + != NGX_OK) { ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); return; @@ -731,7 +877,7 @@ if (ngx_array_init(&r->headers_in.cookies, r->pool, 2, sizeof(ngx_table_elt_t *)) - == NGX_ERROR) + != NGX_OK) { ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); return; @@ -783,10 +929,10 @@ 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_str_t header; - ngx_uint_t i; ngx_table_elt_t *h; ngx_connection_t *c; ngx_http_header_t *hh; @@ -826,19 +972,25 @@ } if (rv == NGX_DECLINED) { - header.len = r->header_in->end - r->header_name_start; - header.data = r->header_name_start; + p = r->header_name_start; + + if (p == NULL) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "client sent too large request"); + ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); + return; + } - if (header.len > NGX_MAX_ERROR_STR - 300) { - header.len = NGX_MAX_ERROR_STR - 300; - header.data[header.len++] = '.'; - header.data[header.len++] = '.'; - header.data[header.len++] = '.'; + len = r->header_in->end - p; + + if (len > NGX_MAX_ERROR_STR - 300) { + len = NGX_MAX_ERROR_STR - 300; + p[len++] = '.'; p[len++] = '.'; p[len++] = '.'; } ngx_log_error(NGX_LOG_INFO, c->log, 0, - "client sent too long header line: \"%V\"", - &header); + "client sent too long header line: \"%*s\"", + len, r->header_name_start); ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); return; } @@ -851,7 +1003,8 @@ } } - rc = ngx_http_parse_header_line(r, r->header_in); + rc = ngx_http_parse_header_line(r, r->header_in, + cscf->underscores_in_headers); if (rc == NGX_OK) { @@ -859,12 +1012,10 @@ /* there was error while a header line parsing */ - header.len = r->header_end - r->header_name_start; - header.data = r->header_name_start; - ngx_log_error(NGX_LOG_INFO, c->log, 0, - "client sent invalid header line: \"%V\"", - &header); + "client sent invalid header line: \"%*s\"", + r->header_end - r->header_name_start, + r->header_name_start); continue; } @@ -886,7 +1037,7 @@ h->value.data = r->header_start; h->value.data[h->value.len] = '\0'; - h->lowcase_key = ngx_palloc(r->pool, h->key.len); + 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; @@ -896,9 +1047,7 @@ ngx_memcpy(h->lowcase_key, r->lowcase_header, h->key.len); } else { - for (i = 0; i < h->key.len; i++) { - h->lowcase_key[i] = ngx_tolower(h->key.data[i]); - } + ngx_strlow(h->lowcase_key, h->key.data, h->key.len); } hh = ngx_hash_find(&cmcf->headers_in_hash, h->hash, @@ -946,11 +1095,10 @@ /* rc == NGX_HTTP_PARSE_INVALID_HEADER: "\r" is not followed by "\n" */ - header.len = r->header_end - r->header_name_start; - header.data = r->header_name_start; ngx_log_error(NGX_LOG_INFO, c->log, 0, - "client sent invalid header line: \"%V\\r...\"", - &header); + "client sent invalid header line: \"%*s\\r...\"", + r->header_end - r->header_name_start, + r->header_name_start); ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); return; } @@ -982,13 +1130,12 @@ } if (n == NGX_AGAIN) { - if (!r->header_timeout_set) { + if (!rev->timer_set) { cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); ngx_add_timer(rev, cscf->client_header_timeout); - r->header_timeout_set = 1; } - if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { + if (ngx_handle_read_event(rev, 0) != NGX_OK) { ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); return NGX_ERROR; } @@ -1148,6 +1295,10 @@ 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); @@ -1202,6 +1353,43 @@ static ngx_int_t +ngx_http_process_host(ngx_http_request_t *r, ngx_table_elt_t *h, + ngx_uint_t offset) +{ + u_char *host; + ssize_t len; + + if (r->headers_in.host == NULL) { + r->headers_in.host = h; + } + + host = h->value.data; + len = ngx_http_validate_host(r, &host, h->value.len, 0); + + if (len == 0) { + 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 (len < 0) { + ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return NGX_ERROR; + } + + if (r->headers_in.server.len) { + return NGX_OK; + } + + r->headers_in.server.len = len; + r->headers_in.server.data = 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) { @@ -1217,77 +1405,113 @@ static ngx_int_t -ngx_http_process_cookie(ngx_http_request_t *r, ngx_table_elt_t *h, +ngx_http_process_user_agent(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset) { - ngx_table_elt_t **cookie; + u_char *user_agent, *msie; - cookie = ngx_array_push(&r->headers_in.cookies); - if (cookie) { - *cookie = h; + if (r->headers_in.user_agent) { return NGX_OK; } - ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + r->headers_in.user_agent = h; - return NGX_ERROR; -} + /* check some widespread browsers while the header is in CPU cache */ + user_agent = h->value.data; -static ngx_int_t -ngx_http_process_request_header(ngx_http_request_t *r) -{ - size_t len; - u_char *host, *ua, *user_agent, ch; - ngx_uint_t hash; + msie = ngx_strstrn(user_agent, "MSIE ", 5 - 1); - if (r->headers_in.host) { + if (msie && msie + 7 < user_agent + h->value.len) { - hash = 0; + r->headers_in.msie = 1; - for (len = 0; len < r->headers_in.host->value.len; len++) { - ch = r->headers_in.host->value.data[len]; + if (msie[6] == '.') { - if (ch == ':') { + switch (msie[5]) { + case '4': + r->headers_in.msie4 = 1; + /* fall through */ + 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; } - - ch = ngx_tolower(ch); - r->headers_in.host->value.data[len] = ch; - hash = ngx_hash(hash, ch); } - if (len && r->headers_in.host->value.data[len - 1] == '.') { - len--; - hash = ngx_hash_key(r->headers_in.host->value.data, len); +#if 0 + /* MSIE ignores the SSL "close notify" alert */ + if (c->ssl) { + c->ssl->no_send_shutdown = 1; } +#endif + } - r->headers_in.host_name_len = len; + if (ngx_strstrn(user_agent, "Opera", 5 - 1)) { + r->headers_in.opera = 1; + r->headers_in.msie = 0; + r->headers_in.msie4 = 0; + r->headers_in.msie6 = 0; + } - if (r->virtual_names) { + if (!r->headers_in.msie && !r->headers_in.opera) { - host = r->host_start; + if (ngx_strstrn(user_agent, "Gecko/", 6 - 1)) { + r->headers_in.gecko = 1; - if (host == NULL) { - host = r->headers_in.host->value.data; - len = r->headers_in.host_name_len; + } else if (ngx_strstrn(user_agent, "Chrome/", 7 - 1)) { + r->headers_in.chrome = 1; - } else { - len = r->host_end - host; - } + } else if (ngx_strstrn(user_agent, "Safari/", 7 - 1)) { + r->headers_in.safari = 1; - ngx_http_find_virtual_server(r, host, len, hash); + } else if (ngx_strstrn(user_agent, "Konqueror", 9 - 1)) { + r->headers_in.konqueror = 1; } + } - } else { - if (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; - } + return NGX_OK; +} + + +static ngx_int_t +ngx_http_process_cookie(ngx_http_request_t *r, ngx_table_elt_t *h, + ngx_uint_t offset) +{ + ngx_table_elt_t **cookie; + + cookie = ngx_array_push(&r->headers_in.cookies); + if (cookie) { + *cookie = h; + return NGX_OK; + } + + ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + + return NGX_ERROR; +} + + +static ngx_int_t +ngx_http_process_request_header(ngx_http_request_t *r) +{ + if (ngx_http_find_virtual_server(r, r->headers_in.server.data, + r->headers_in.server.len) + == NGX_ERROR) + { + ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return NGX_ERROR; + } - r->headers_in.host_name_len = 0; + 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) { @@ -1303,11 +1527,10 @@ } } - if (r->method & (NGX_HTTP_POST|NGX_HTTP_PUT) - && r->headers_in.content_length_n == -1) - { + if (r->method & NGX_HTTP_PUT && r->headers_in.content_length_n == -1) { ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, - "client sent POST method without \"Content-Length\" header"); + "client sent %V method without \"Content-Length\" header", + &r->method_name); ngx_http_finalize_request(r, NGX_HTTP_LENGTH_REQUIRED); return NGX_ERROR; } @@ -1329,13 +1552,6 @@ return NGX_ERROR; } - if (r->plain_http) { - ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, - "client sent plain HTTP request to HTTPS port"); - ngx_http_finalize_request(r, NGX_HTTP_TO_HTTPS); - 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 = @@ -1344,88 +1560,63 @@ } } - if (r->headers_in.user_agent) { + return NGX_OK; +} - /* - * check some widespread browsers while the headers are still - * in CPU cache - */ - user_agent = r->headers_in.user_agent->value.data; +static void +ngx_http_process_request(ngx_http_request_t *r) +{ + ngx_connection_t *c; - ua = ngx_strstrn(user_agent, "MSIE", 4 - 1); + c = r->connection; - if (ua && ua + 8 < user_agent + r->headers_in.user_agent->value.len) { + if (r->plain_http) { + 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; + } - r->headers_in.msie = 1; +#if (NGX_HTTP_SSL) - if (ua[4] == ' ' && ua[5] == '4' && ua[6] == '.') { - r->headers_in.msie4 = 1; - } + if (c->ssl) { + long rc; + X509 *cert; + ngx_http_ssl_srv_conf_t *sscf; -#if 0 - /* MSIE ignores the SSL "close notify" alert */ - if (c->ssl) { - c->ssl->no_send_shutdown = 1; - } -#endif - } + sscf = ngx_http_get_module_srv_conf(r, ngx_http_ssl_module); - if (ngx_strstrn(user_agent, "Opera", 5 - 1)) { - r->headers_in.opera = 1; - r->headers_in.msie = 0; - r->headers_in.msie4 = 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, "Konqueror", 9 - 1)) { - r->headers_in.konqueror = 1; - } - } - } - - return NGX_OK; -} - - -static void -ngx_http_process_request(ngx_http_request_t *r) -{ - ngx_connection_t *c; -#if (NGX_HTTP_SSL) - long rc; - ngx_http_ssl_srv_conf_t *sscf; -#endif - - c = r->connection; - -#if (NGX_HTTP_SSL) - - if (c->ssl) { - sscf = ngx_http_get_module_srv_conf(r, ngx_http_ssl_module); - - if (sscf->verify) { - rc = SSL_get_verify_result(c->ssl->connection); + if (sscf->verify) { + rc = SSL_get_verify_result(c->ssl->connection); if (rc != X509_V_OK) { 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(sscf->ssl.ctx, + (SSL_get0_session(c->ssl->connection))); + ngx_http_finalize_request(r, NGX_HTTPS_CERT_ERROR); return; } - if (SSL_get_peer_certificate(c->ssl->connection) - == NULL) - { - ngx_log_error(NGX_LOG_INFO, c->log, 0, - "client sent no required SSL certificate"); - ngx_http_finalize_request(r, NGX_HTTPS_NO_CERT); - 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(sscf->ssl.ctx, + (SSL_get0_session(c->ssl->connection))); + + ngx_http_finalize_request(r, NGX_HTTPS_NO_CERT); + return; + } + + X509_free(cert); } } } @@ -1437,36 +1628,91 @@ } #if (NGX_STAT_STUB) - ngx_atomic_fetch_add(ngx_stat_reading, -1); + (void) ngx_atomic_fetch_add(ngx_stat_reading, -1); r->stat_reading = 0; - ngx_atomic_fetch_add(ngx_stat_writing, 1); + (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_read; + r->read_event_handler = ngx_http_block_reading; ngx_http_handler(r); - return; + ngx_http_run_posted_requests(c); } -static void -ngx_http_find_virtual_server(ngx_http_request_t *r, u_char *host, size_t len, - ngx_uint_t hash) +static ssize_t +ngx_http_validate_host(ngx_http_request_t *r, u_char **host, size_t len, + ngx_uint_t alloc) +{ + u_char *h, ch; + size_t i, last; + ngx_uint_t dot; + + last = len; + h = *host; + dot = 0; + + for (i = 0; i < len; i++) { + ch = h[i]; + + if (ch == '.') { + if (dot) { + return 0; + } + + dot = 1; + continue; + } + + dot = 0; + + if (ch == ':') { + last = i; + continue; + } + + if (ngx_path_separator(ch) || ch == '\0') { + return 0; + } + + if (ch >= 'A' || ch < 'Z') { + alloc = 1; + } + } + + if (dot) { + last--; + } + + if (alloc) { + *host = ngx_pnalloc(r->pool, last) ; + if (*host == NULL) { + return -1; + } + + ngx_strlow(*host, h, last); + } + + return last; +} + + +static ngx_int_t +ngx_http_find_virtual_server(ngx_http_request_t *r, u_char *host, size_t len) { ngx_http_core_loc_conf_t *clcf; ngx_http_core_srv_conf_t *cscf; -#if (NGX_PCRE) - ngx_int_t n; - ngx_uint_t i; - ngx_str_t name; - ngx_http_server_name_t *sn; -#endif - cscf = ngx_hash_find_combined(&r->virtual_names->names, hash, host, len); + if (r->virtual_names == NULL) { + return NGX_DECLINED; + } + + cscf = ngx_hash_find_combined(&r->virtual_names->names, + ngx_hash_key(host, len), host, len); if (cscf) { goto found; @@ -1474,7 +1720,11 @@ #if (NGX_PCRE) - if (r->virtual_names->nregex) { + if (len && r->virtual_names->nregex) { + ngx_int_t n; + ngx_uint_t i; + ngx_str_t name; + ngx_http_server_name_t *sn; name.len = len; name.data = host; @@ -1483,55 +1733,38 @@ for (i = 0; i < r->virtual_names->nregex; i++) { - n = ngx_regex_exec(sn[i].regex, &name, NULL, 0); + n = ngx_http_regex_exec(r, sn[i].regex, &name); - if (n == NGX_REGEX_NO_MATCHED) { - continue; + if (n == NGX_OK) { + cscf = sn[i].server; + goto found; } - if (n < 0) { - ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, - ngx_regex_exec_n - " failed: %d on \"%V\" using \"%V\"", - n, &name, &sn[i].name); - return; + if (n == NGX_DECLINED) { + continue; } - /* match */ - - cscf = sn[i].core_srv_conf; - - goto found; + return NGX_ERROR; } } #endif - cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); - - if (cscf->wildcard) { - r->server_name.len = len; - r->server_name.data = host; - } - - return; + return NGX_OK; found: - r->server_name.len = len; - r->server_name.data = host; - 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); - r->connection->log->file = clcf->err_log->file; + r->connection->log->file = clcf->error_log->file; if (!(r->connection->log->log_level & NGX_LOG_DEBUG_CONNECTION)) { - r->connection->log->log_level = clcf->err_log->log_level; + r->connection->log->log_level = clcf->error_log->log_level; } - return; + return NGX_OK; } @@ -1548,30 +1781,100 @@ ctx = c->log->data; ctx->current_request = r; + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http run request: \"%V?%V\"", &r->uri, &r->args); + 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_log_ctx_t *ctx; + 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; + + ctx = c->log->data; + ctx->current_request = 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_log_ctx_t *ctx; ngx_http_core_loc_conf_t *clcf; + c = r->connection; + + ngx_log_debug5(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http finalize request: %d, \"%V?%V\" a:%d, c:%d", + rc, &r->uri, &r->args, r == c->data, r->main->count); + if (rc == NGX_DONE) { - /* the request pool may be already destroyed */ + ngx_http_finalize_connection(r); return; } - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http finalize request: %d, \"%V?%V\"", - rc, &r->uri, &r->args); + if (rc == NGX_OK && r->filter_finalize) { + c->error = 1; + return; + } if (rc == NGX_DECLINED) { r->content_handler = NULL; @@ -1586,17 +1889,18 @@ if (rc == NGX_ERROR || rc == NGX_HTTP_REQUEST_TIME_OUT - || r->connection->error) + || rc == NGX_HTTP_CLIENT_CLOSED_REQUEST + || c->error) { - if (rc > 0 && r->headers_out.status == 0) { - r->headers_out.status = rc; - } - if (ngx_http_post_action(r) == NGX_OK) { return; } - ngx_http_close_request(r, 0); + if (r->main->blocked) { + r->write_event_handler = ngx_http_request_finalizer; + } + + ngx_http_terminate_request(r, rc); return; } @@ -1605,96 +1909,117 @@ || rc == NGX_HTTP_NO_CONTENT) { if (rc == NGX_HTTP_CLOSE) { - ngx_http_close_request(r, rc); + ngx_http_terminate_request(r, rc); return; } if (r == r->main) { - if (r->connection->read->timer_set) { - ngx_del_timer(r->connection->read); + if (c->read->timer_set) { + ngx_del_timer(c->read); } - if (r->connection->write->timer_set) { - ngx_del_timer(r->connection->write); + 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 || rc == NGX_AGAIN) { - if (ngx_http_set_write_handler(r) != NGX_OK) { + if (r != r->main) { + + if (r->buffered || r->postponed) { + + if (ngx_http_set_write_handler(r) != NGX_OK) { + ngx_http_terminate_request(r, 0); + } + return; } - } - r->done = 1; +#if (NGX_DEBUG) + if (r != c->data) { + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http finalize non-active request: \"%V?%V\"", + &r->uri, &r->args); + } +#endif - if (r != r->connection->data) { - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http finalize non-active request: \"%V?%V\"", - &r->uri, &r->args); - return; - } + pr = r->parent; - if (r != r->main) { + if (r == c->data) { - pr = r->parent; + r->main->count--; - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http parent request: \"%V?%V\"", &pr->uri, &pr->args); + if (!r->logged) { - if (rc != NGX_AGAIN) { - r->connection->data = pr; - } + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); - ctx = r->connection->log->data; - ctx->current_request = pr; + if (clcf->log_subrequest) { + ngx_http_log_request(r); + } - if (pr->postponed) { + r->logged = 1; - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http request: \"%V?%V\" has postponed", - &pr->uri, &pr->args); + } else { + ngx_log_error(NGX_LOG_ALERT, c->log, 0, + "subrequest: \"%V?%V\" logged again", + &r->uri, &r->args); + } + + r->done = 1; - if (rc != NGX_AGAIN && pr->postponed->request == r) { + if (pr->postponed && pr->postponed->request == r) { pr->postponed = pr->postponed->next; } - if (r->fast_subrequest) { + c->data = pr; - if (rc == NGX_AGAIN) { - r->fast_subrequest = 0; - } - - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http fast subrequest: \"%V?%V\" done", - &r->uri, &r->args); - return; - } + } else { - if (rc != NGX_AGAIN) { - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http wake parent request: \"%V?%V\"", - &pr->uri, &pr->args); + r->write_event_handler = ngx_http_request_finalizer; - pr->write_event_handler(pr); + 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 (rc == NGX_AGAIN) { + if (r->buffered || c->buffered || r->postponed || r->blocked) { + + if (ngx_http_set_write_handler(r) != NGX_OK) { + ngx_http_terminate_request(r, 0); + } + return; } - if (r->connection->buffered) { - (void) ngx_http_set_write_handler(r); + 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->write_event_handler = ngx_http_request_empty_handler; + if (!r->post_action) { r->request_complete = 1; } @@ -1703,24 +2028,110 @@ return; } - if (r->connection->read->timer_set) { - ngx_del_timer(r->connection->read); + 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; } - if (r->connection->write->timer_set) { - r->connection->write->delayed = 0; - ngx_del_timer(r->connection->write); + cln = mr->cleanup; + mr->cleanup = NULL; + + while (cln) { + if (cln->handler) { + cln->handler(cln->data); + } + + cln = cln->next; } - if (r->connection->destroyed) { + 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) { + 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; + 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; + + if (r->lingering_time == 0) { + r->lingering_time = ngx_time() + + (time_t) (clcf->lingering_time / 1000); + ngx_add_timer(r->connection->read, clcf->lingering_timeout); + } + } + + ngx_http_close_request(r, 0); + return; + } + if (!ngx_terminate && !ngx_exiting - && r->keepalive != 0 + && r->keepalive && clcf->keepalive_timeout > 0) { ngx_http_set_keepalive(r); @@ -1743,7 +2154,9 @@ r->http_state = NGX_HTTP_WRITING_REQUEST_STATE; - r->read_event_handler = ngx_http_test_read; + 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; @@ -1757,7 +2170,7 @@ ngx_add_timer(wev, clcf->send_timeout); } - if (ngx_handle_write_event(wev, clcf->send_lowat) == NGX_ERROR) { + if (ngx_handle_write_event(wev, clcf->send_lowat) != NGX_OK) { ngx_http_close_request(r, 0); return NGX_ERROR; } @@ -1780,6 +2193,8 @@ 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) { if (!wev->delayed) { ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, @@ -1794,10 +2209,9 @@ wev->delayed = 0; if (!wev->ready) { - clcf = ngx_http_get_module_loc_conf(r->main, ngx_http_core_module); ngx_add_timer(wev, clcf->send_timeout); - if (ngx_handle_write_event(wev, clcf->send_lowat) == NGX_ERROR) { + if (ngx_handle_write_event(wev, clcf->send_lowat) != NGX_OK) { ngx_http_close_request(r, 0); } @@ -1805,13 +2219,11 @@ } } else { - if (wev->delayed) { + if (wev->delayed || r->aio) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, wev->log, 0, "http writer delayed"); - clcf = ngx_http_get_module_loc_conf(r->main, ngx_http_core_module); - - if (ngx_handle_write_event(wev, clcf->send_lowat) == NGX_ERROR) { + if (ngx_handle_write_event(wev, clcf->send_lowat) != NGX_OK) { ngx_http_close_request(r, 0); } @@ -1821,60 +2233,67 @@ rc = ngx_http_output_filter(r, NULL); - if (c->destroyed) { - return; - } - ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, "http writer output filter: %d, \"%V?%V\"", rc, &r->uri, &r->args); - if (rc == NGX_AGAIN) { - clcf = ngx_http_get_module_loc_conf(r->main, ngx_http_core_module); + if (rc == NGX_ERROR) { + ngx_http_finalize_request(r, rc); + return; + } + + if (r->buffered || r->postponed || (r == r->main && c->buffered)) { + if (!wev->ready && !wev->delayed) { ngx_add_timer(wev, clcf->send_timeout); } - if (ngx_handle_write_event(wev, clcf->send_lowat) == NGX_ERROR) { + if (ngx_handle_write_event(wev, clcf->send_lowat) != NGX_OK) { ngx_http_close_request(r, 0); } - if (r == r->main || r->buffered) { - return; - } - - rc = NGX_OK; + 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_block_read(ngx_http_request_t *r) +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 read blocked"); + "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_ERROR) - { + if (ngx_del_event(r->connection->read, NGX_READ_EVENT, 0) != NGX_OK) { ngx_http_close_request(r, 0); } } } -static void -ngx_http_test_read(ngx_http_request_t *r) +void +ngx_http_test_reading(ngx_http_request_t *r) { int n; char buf[1]; @@ -1885,7 +2304,7 @@ c = r->connection; rev = c->read; - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http test read"); + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http test reading"); #if (NGX_HAVE_KQUEUE) @@ -1928,7 +2347,7 @@ if ((ngx_event_flags & NGX_USE_LEVEL_EVENT) && rev->active) { - if (ngx_del_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) { + if (ngx_del_event(rev, NGX_READ_EVENT, 0) != NGX_OK) { ngx_http_close_request(r, 0); } } @@ -1963,8 +2382,17 @@ 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; @@ -2008,15 +2436,15 @@ } } - clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + r->keepalive = 0; - ngx_http_request_done(r, 0); + ngx_http_free_request(r, 0); c->data = hc; ngx_add_timer(rev, clcf->keepalive_timeout); - if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { + if (ngx_handle_read_event(rev, 0) != NGX_OK) { ngx_http_close_connection(c); return; } @@ -2029,13 +2457,14 @@ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "pipelined request"); #if (NGX_STAT_STUB) - ngx_atomic_fetch_add(ngx_stat_reading, 1); + (void) ngx_atomic_fetch_add(ngx_stat_reading, 1); #endif hc->pipeline = 1; c->log->action = "reading client pipelined request line"; - ngx_http_init_request(rev); + rev->handler = ngx_http_init_request; + ngx_post_event(rev, &ngx_posted_events); return; } @@ -2074,7 +2503,7 @@ if (hc->free) { for (i = 0; i < hc->nfree; i++) { - ngx_pfree(c->pool, hc->free[i]); + ngx_pfree(c->pool, hc->free[i]->start); hc->free[i] = NULL; } @@ -2086,17 +2515,23 @@ if (hc->busy) { for (i = 0; i < hc->nbusy; i++) { - ngx_pfree(c->pool, hc->busy[i]); + ngx_pfree(c->pool, hc->busy[i]->start); hc->busy[i] = 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_ERROR) { + if (ngx_del_event(wev, NGX_WRITE_EVENT, 0) != NGX_OK) { ngx_http_close_connection(c); return; } @@ -2128,8 +2563,15 @@ (const void *) &tcp_nodelay, sizeof(int)) == -1) { +#if (NGX_SOLARIS) + /* Solaris returns EINVAL if a socket has been shut down */ + c->log_error = NGX_ERROR_IGNORE_EINVAL; +#endif + ngx_connection_error(c, ngx_socket_errno, "setsockopt(TCP_NODELAY) failed"); + + c->log_error = NGX_ERROR_INFO; ngx_http_close_connection(c); return; } @@ -2145,7 +2587,7 @@ c->idle = 1; if (rev->ready) { - ngx_http_keepalive_handler(rev); + ngx_post_event(rev, &ngx_posted_events); } } @@ -2221,7 +2663,7 @@ c->log_error = NGX_ERROR_INFO; if (n == NGX_AGAIN) { - if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { + if (ngx_handle_read_event(rev, 0) != NGX_OK) { ngx_http_close_connection(c); } @@ -2245,7 +2687,7 @@ b->last += n; #if (NGX_STAT_STUB) - ngx_atomic_fetch_add(ngx_stat_reading, 1); + (void) ngx_atomic_fetch_add(ngx_stat_reading, 1); #endif c->log->handler = ngx_http_log_error; @@ -2274,7 +2716,7 @@ 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_ERROR) { + if (ngx_handle_read_event(rev, 0) != NGX_OK) { ngx_http_close_request(r, 0); return; } @@ -2283,7 +2725,7 @@ 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_ERROR) { + if (ngx_del_event(wev, NGX_WRITE_EVENT, 0) != NGX_OK) { ngx_http_close_request(r, 0); return; } @@ -2324,7 +2766,7 @@ return; } - timer = r->lingering_time - ngx_time(); + timer = (ngx_msec_t) (r->lingering_time - ngx_time()); if (timer <= 0) { ngx_http_close_request(r, 0); return; @@ -2342,7 +2784,7 @@ } while (rev->ready); - if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { + if (ngx_handle_read_event(rev, 0) != NGX_OK) { ngx_http_close_request(r, 0); return; } @@ -2390,7 +2832,14 @@ } if (flags & NGX_HTTP_LAST) { - b->last_buf = 1; + + 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) { @@ -2418,41 +2867,59 @@ 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_read; + r->read_event_handler = ngx_http_block_reading; + + if (clcf->post_action.data[0] == '/') { + ngx_http_internal_redirect(r, &clcf->post_action, NULL); - 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 error) +ngx_http_close_request(ngx_http_request_t *r, ngx_int_t rc) { ngx_connection_t *c; + r = r->main; c = r->connection; - ngx_http_request_done(r->main, error); + 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; + } + + ngx_http_free_request(r, rc); ngx_http_close_connection(c); } static void -ngx_http_request_done(ngx_http_request_t *r, ngx_int_t error) +ngx_http_free_request(ngx_http_request_t *r, ngx_int_t rc) { - ngx_log_t *log; - ngx_uint_t i, n; - struct linger linger; - ngx_http_cleanup_t *cln; - ngx_http_log_ctx_t *ctx; - ngx_http_handler_pt *log_handler; - ngx_http_core_loc_conf_t *clcf; - ngx_http_core_main_conf_t *cmcf; + ngx_log_t *log; + 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; @@ -2472,26 +2939,24 @@ #if (NGX_STAT_STUB) if (r->stat_reading) { - ngx_atomic_fetch_add(ngx_stat_reading, -1); + (void) ngx_atomic_fetch_add(ngx_stat_reading, -1); } if (r->stat_writing) { - ngx_atomic_fetch_add(ngx_stat_writing, -1); + (void) ngx_atomic_fetch_add(ngx_stat_writing, -1); } #endif - if (error && r->headers_out.status == 0) { - r->headers_out.status = error; + if (rc > 0 && (r->headers_out.status == 0 || r->connection->sent == 0)) { + r->headers_out.status = rc; } - cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); + log->action = "logging request"; - 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); - } + 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); @@ -2522,6 +2987,24 @@ 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); + } +} + + +static void ngx_http_close_connection(ngx_connection_t *c) { ngx_pool_t *pool; @@ -2541,7 +3024,7 @@ #endif #if (NGX_STAT_STUB) - ngx_atomic_fetch_add(ngx_stat_active, -1); + (void) ngx_atomic_fetch_add(ngx_stat_active, -1); #endif c->destroyed = 1; @@ -2569,13 +3052,17 @@ ctx = log->data; - p = ngx_snprintf(buf, len, ", client: %V", ctx->client); + 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; @@ -2586,38 +3073,32 @@ 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; + char *uri_separator; + u_char *p; + ngx_http_upstream_t *u; + ngx_http_core_srv_conf_t *cscf; - if (r->server_name.data) { - p = ngx_snprintf(buf, len, ", server: %V", &r->server_name); - len -= p - buf; - buf = p; - } + cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); - if (r->unparsed_uri.data) { - p = ngx_snprintf(buf, len, ", URL: \"%V\"", &r->unparsed_uri); - len -= p - buf; - buf = p; + p = ngx_snprintf(buf, len, ", server: %V", &cscf->server_name); + len -= p - buf; + buf = p; - } else { - 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; - } + 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; - } + 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) { @@ -2639,7 +3120,7 @@ #endif p = ngx_snprintf(buf, len, ", upstream: \"%V%V%s%V\"", - &u->conf->schema, u->peer.name, + &u->schema, u->peer.name, uri_separator, &u->uri); len -= p - buf; buf = p; diff -Nru nginx-0.5.33/src/http/ngx_http_request.h nginx-0.8.53/src/http/ngx_http_request.h --- nginx-0.5.33/src/http/ngx_http_request.h 2007-11-07 13:46:29.000000000 +0000 +++ nginx-0.8.53/src/http/ngx_http_request.h 2010-06-30 14:42:15.000000000 +0000 @@ -37,7 +37,8 @@ #define NGX_HTTP_PROPPATCH 0x0800 #define NGX_HTTP_LOCK 0x1000 #define NGX_HTTP_UNLOCK 0x2000 -#define NGX_HTTP_TRACE 0x4000 +#define NGX_HTTP_PATCH 0x4000 +#define NGX_HTTP_TRACE 0x8000 #define NGX_HTTP_CONNECTION_CLOSE 1 #define NGX_HTTP_CONNECTION_KEEP_ALIVE 2 @@ -56,18 +57,22 @@ #define NGX_HTTP_PARSE_INVALID_HEADER 13 -#define NGX_HTTP_ZERO_IN_URI 1 +/* unused 1 */ #define NGX_HTTP_SUBREQUEST_IN_MEMORY 2 +#define NGX_HTTP_SUBREQUEST_WAITED 4 +#define NGX_HTTP_LOG_UNSAFE 8 #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_BAD_REQUEST 400 @@ -168,8 +173,10 @@ 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 *expect; #if (NGX_HTTP_GZIP) ngx_table_elt_t *accept_encoding; @@ -180,7 +187,7 @@ ngx_table_elt_t *keep_alive; -#if (NGX_HTTP_PROXY || NGX_HTTP_REALIP) +#if (NGX_HTTP_PROXY || NGX_HTTP_REALIP || NGX_HTTP_GEO) ngx_table_elt_t *x_forwarded_for; #endif @@ -205,15 +212,18 @@ ngx_array_t cookies; - size_t host_name_len; + ngx_str_t server; off_t content_length_n; time_t keep_alive_n; unsigned connection_type:2; unsigned msie:1; unsigned msie4:1; + unsigned msie6:1; unsigned opera:1; unsigned gecko:1; + unsigned chrome:1; + unsigned safari:1; unsigned konqueror:1; } ngx_http_headers_in_t; @@ -229,6 +239,7 @@ 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; @@ -241,6 +252,8 @@ 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; @@ -315,6 +328,14 @@ }; +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); @@ -332,7 +353,9 @@ 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; @@ -367,11 +390,8 @@ 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; - uint32_t in_addr; - ngx_uint_t port; - ngx_str_t *port_text; /* ":80" */ - ngx_str_t server_name; ngx_http_virtual_names_t *virtual_names; ngx_int_t phase_handler; @@ -380,6 +400,12 @@ ngx_http_variable_value_t *variables; +#if (NGX_PCRE) + ngx_uint_t ncaptures; + int *captures; + u_char *captures_data; +#endif + size_t limit_rate; /* used to learn the Apache compatible response length without a header */ @@ -395,6 +421,12 @@ ngx_http_cleanup_t *cleanup; + unsigned subrequests:8; + unsigned count:8; + unsigned blocked:8; + + unsigned aio:1; + unsigned http_state:4; /* URI with "/." and on Win32 with "//" */ @@ -406,11 +438,12 @@ /* URI with "+" */ unsigned plus_in_uri:1; - /* URI with "\0" or "%00" */ - unsigned zero_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; @@ -423,51 +456,54 @@ unsigned request_body_file_group_access:1; unsigned request_body_file_log_level:3; - unsigned fast_subrequest:1; unsigned subrequest_in_memory:1; + unsigned waited:1; - unsigned header_timeout_set: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; -#if (NGX_HTTP_REALIP) - - /* - * instead of using the request context data in ngx_http_realip_module - * we use the single bit in the request structure - */ - unsigned realip_set:1; - -#endif - /* - * instead of using the request context data in ngx_http_limit_zone_module - * we use the single bit in the request structure + * instead of using the request context data in + * ngx_http_limit_zone_module and ngx_http_limit_req_module + * we use the single bits in the request structure */ unsigned limit_zone_set:1; + unsigned limit_req_set:1; #if 0 - unsigned cachable:1; + unsigned cacheable:1; #endif unsigned pipeline:1; unsigned plain_http:1; unsigned chunked:1; unsigned header_only:1; - unsigned zero_body:1; unsigned keepalive:1; unsigned lingering_close:1; unsigned discard_body:1; unsigned internal:1; unsigned error_page:1; + unsigned ignore_content_encoding: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 utf8:1; + unsigned logged:1; unsigned buffered:4; @@ -481,11 +517,24 @@ unsigned stat_writing:1; #endif - unsigned subrequests:8; - /* 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; @@ -499,20 +548,20 @@ u_char *host_end; u_char *port_start; u_char *port_end; - u_char *header_name_start; - u_char *header_name_end; - u_char *header_start; - u_char *header_end; unsigned http_minor:16; unsigned http_major:16; - - ngx_uint_t header_hash; - ngx_uint_t lowcase_index; - u_char lowcase_header[NGX_HTTP_LC_HEADER_LEN]; }; +typedef struct { + ngx_http_posted_request_t terminal_posted_request; +#if (NGX_HAVE_AIO_SENDFILE) + u_char aio_preload; +#endif +} ngx_http_ephemeral_t; + + extern ngx_http_header_t ngx_http_headers_in[]; extern ngx_http_header_out_t ngx_http_headers_out[]; diff -Nru nginx-0.5.33/src/http/ngx_http_script.c nginx-0.8.53/src/http/ngx_http_script.c --- nginx-0.5.33/src/http/ngx_http_script.c 2007-09-22 19:18:36.000000000 +0000 +++ nginx-0.8.53/src/http/ngx_http_script.c 2010-09-13 12:44:43.000000000 +0000 @@ -9,75 +9,339 @@ #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; -ngx_uint_t -ngx_http_script_variables_count(ngx_str_t *value) +void +ngx_http_script_flush_complex_value(ngx_http_request_t *r, + ngx_http_complex_value_t *val) { - ngx_uint_t i, n; + ngx_uint_t *index; - for (n = 0, i = 0; i < value->len; i++) { - if (value->data[i] == '$') { - n++; + 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++; } } +} - return n; + +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_script_compile(ngx_http_script_compile_t *sc) +ngx_http_compile_complex_value(ngx_http_compile_complex_value_t *ccv) { - u_char ch; - size_t size; - ngx_int_t index, *p; - ngx_str_t name; - uintptr_t *code; - ngx_uint_t i, n, bracket; - ngx_http_script_var_code_t *var_code; - ngx_http_script_copy_code_t *copy; - ngx_http_script_copy_capture_code_t *copy_capture; + 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; - 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; - } + v = ccv->value; + + if (v->len == 0) { + ngx_conf_log_error(NGX_LOG_EMERG, ccv->cf, 0, "empty parameter"); + return NGX_ERROR; } + nv = 0; + nc = 0; - 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); + for (i = 0; i < v->len; i++) { + if (v->data[i] == '$') { + if (v->data[i + 1] >= '1' && v->data[i + 1] <= '9') { + nc++; - *sc->lengths = ngx_array_create(sc->cf->pool, n, 1); - if (*sc->lengths == NULL) { + } else { + nv++; + } + } + } + + if (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 (*sc->values == NULL) { - n = (sc->variables * (2 * sizeof(ngx_http_script_copy_code_t) - + sizeof(ngx_http_script_var_code_t)) + 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) - + sc->source->len + + v->len + sizeof(uintptr_t) - 1) & ~(sizeof(uintptr_t) - 1); - *sc->values = ngx_array_create(sc->cf->pool, n, 1); - if (*sc->values == NULL) { + 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 "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.data[0] != '0') { + return NGX_DECLINED; + } } - sc->variables = 0; + 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 */ ) { @@ -89,6 +353,10 @@ goto invalid_variable; } +#if (NGX_PCRE) + { + ngx_uint_t n; + if (sc->source->data[i] >= '1' && sc->source->data[i] <= '9') { n = sc->source->data[i] - '0'; @@ -99,36 +367,16 @@ sc->captures_mask |= 1 << n; - copy_capture = ngx_http_script_add_code(*sc->lengths, - sizeof(ngx_http_script_copy_capture_code_t), - NULL); - if (copy_capture == NULL) { + if (ngx_http_script_add_capture_code(sc, n) != NGX_OK) { return NGX_ERROR; } - copy_capture->code = (ngx_http_script_code_pt) - ngx_http_script_copy_capture_len_code; - copy_capture->n = 2 * n; - - - copy_capture = ngx_http_script_add_code(*sc->values, - sizeof(ngx_http_script_copy_capture_code_t), - &sc->main); - if (copy_capture == NULL) { - return NGX_ERROR; - } - - copy_capture->code = ngx_http_script_copy_capture_code; - copy_capture->n = 2 * n; - - if (sc->ncaptures < n) { - sc->ncaptures = n; - } - i++; continue; } + } +#endif if (sc->source->data[i] == '{') { bracket = 1; @@ -177,43 +425,10 @@ sc->variables++; - 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; - } - - var_code = ngx_http_script_add_code(*sc->lengths, - sizeof(ngx_http_script_var_code_t), - NULL); - if (var_code == NULL) { + if (ngx_http_script_add_var_code(sc, &name) != NGX_OK) { return NGX_ERROR; } - var_code->code = (ngx_http_script_code_pt) - ngx_http_script_copy_var_len_code; - var_code->index = (uintptr_t) index; - - - var_code = ngx_http_script_add_code(*sc->values, - sizeof(ngx_http_script_var_code_t), - &sc->main); - if (var_code == NULL) { - return NGX_ERROR; - } - - var_code->code = ngx_http_script_copy_var_code; - var_code->index = (uintptr_t) index; - continue; } @@ -221,14 +436,10 @@ sc->args = 1; sc->compile_args = 0; - code = ngx_http_script_add_code(*sc->values, sizeof(uintptr_t), - &sc->main); - if (code == NULL) { + if (ngx_http_script_add_args_code(sc) != NGX_OK) { return NGX_ERROR; } - *code = (uintptr_t) ngx_http_script_start_args_code; - i++; continue; @@ -236,62 +447,35 @@ name.data = &sc->source->data[i]; - while (i < sc->source->len - && sc->source->data[i] != '$' - && !(sc->source->data[i] == '?' && sc->compile_args)) - { - i++; - name.len++; - } - - sc->size += name.len; - - copy = ngx_http_script_add_code(*sc->lengths, - sizeof(ngx_http_script_copy_code_t), - NULL); - if (copy == NULL) { - return NGX_ERROR; - } + while (i < sc->source->len) { - copy->code = (ngx_http_script_code_pt) ngx_http_script_copy_len_code; - copy->len = name.len; - - size = (sizeof(ngx_http_script_copy_code_t) + name.len - + sizeof(uintptr_t) - 1) - & ~(sizeof(uintptr_t) - 1); + if (sc->source->data[i] == '$') { + break; + } - copy = ngx_http_script_add_code(*sc->values, size, &sc->main); - if (copy == NULL) { - return NGX_ERROR; - } + if (sc->source->data[i] == '?') { - copy->code = ngx_http_script_copy_code; - copy->len = name.len; + sc->args = 1; - ngx_memcpy((u_char *) copy + sizeof(ngx_http_script_copy_code_t), - name.data, name.len); - } + if (sc->compile_args) { + break; + } + } - if (sc->complete_lengths) { - code = ngx_http_script_add_code(*sc->lengths, sizeof(uintptr_t), NULL); - if (code == NULL) { - return NGX_ERROR; + i++; + name.len++; } - *code = (uintptr_t) NULL; - } + sc->size += name.len; - if (sc->complete_values) { - code = ngx_http_script_add_code(*sc->values, sizeof(uintptr_t), - &sc->main); - if (code == NULL) { + if (ngx_http_script_add_copy_code(sc, &name, (i == sc->source->len)) + != NGX_OK) + { return NGX_ERROR; } - - *code = (uintptr_t) NULL; } - return NGX_OK; + return ngx_http_script_done(sc); invalid_variable: @@ -314,7 +498,7 @@ 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_cachable) { + if (r->variables[i].no_cacheable) { r->variables[i].valid = 0; r->variables[i].not_found = 0; } @@ -333,7 +517,7 @@ value->len = len; - value->data = ngx_palloc(r->pool, len); + value->data = ngx_pnalloc(r->pool, len); if (value->data == NULL) { return NULL; } @@ -346,25 +530,114 @@ code((ngx_http_script_engine_t *) &e); } - return e.pos; + 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; } -void -ngx_http_script_flush_no_cachable_variables(ngx_http_request_t *r, - ngx_array_t *indices) +static ngx_int_t +ngx_http_script_done(ngx_http_script_compile_t *sc) { - ngx_uint_t n, *index; + ngx_str_t zero; + uintptr_t *code; - if (indices) { - index = indices->elts; - for (n = 0; n < indices->nelts; n++) { - if (r->variables[index[n]].no_cachable) { - r->variables[index[n]].valid = 0; - r->variables[index[n]].not_found = 0; - } + 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; } @@ -392,7 +665,7 @@ new = ngx_array_push_n(codes, size); if (new == NULL) { - return NGX_CONF_ERROR; + return NULL; } if (code) { @@ -406,6 +679,49 @@ } +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) { @@ -422,20 +738,67 @@ 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(e->pos, e->ip + sizeof(ngx_http_script_copy_code_t), + 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_debug1(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0, - "http script copy: \"%V\"", &e->buf); + 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; } @@ -467,6 +830,7 @@ 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; @@ -484,69 +848,47 @@ } if (value && !value->not_found) { - e->pos = ngx_copy(e->pos, value->data, value->len); + p = e->pos; + e->pos = ngx_copy(p, value->data, value->len); - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0, - "http script var: \"%V\"", &e->buf); + "http script var: \"%*s\"", e->pos - p, p); } } } -size_t -ngx_http_script_copy_capture_len_code(ngx_http_script_engine_t *e) +static ngx_int_t +ngx_http_script_add_args_code(ngx_http_script_compile_t *sc) { - ngx_http_script_copy_capture_code_t *code; + uintptr_t *code; - code = (ngx_http_script_copy_capture_code_t *) e->ip; + code = ngx_http_script_add_code(*sc->lengths, sizeof(uintptr_t), NULL); + if (code == NULL) { + return NGX_ERROR; + } - e->ip += sizeof(ngx_http_script_copy_capture_code_t); + *code = (uintptr_t) ngx_http_script_mark_args_code; - if (code->n < e->ncaptures) { - if ((e->args || e->quote) - && (e->request->quoted_uri || e->request->plus_in_uri)) - { - return e->captures[code->n + 1] - e->captures[code->n] - + 2 * ngx_escape_uri(NULL, - &e->line.data[e->captures[code->n]], - e->captures[code->n + 1] - e->captures[code->n], - NGX_ESCAPE_ARGS); - } else { - return e->captures[code->n + 1] - e->captures[code->n]; - } + code = ngx_http_script_add_code(*sc->values, sizeof(uintptr_t), &sc->main); + if (code == NULL) { + return NGX_ERROR; } - return 0; + *code = (uintptr_t) ngx_http_script_start_args_code; + + return NGX_OK; } -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) { - ngx_http_script_copy_capture_code_t *code; - - code = (ngx_http_script_copy_capture_code_t *) e->ip; - - e->ip += sizeof(ngx_http_script_copy_capture_code_t); - - if (code->n < e->ncaptures) { - if ((e->args || e->quote) - && (e->request->quoted_uri || e->request->plus_in_uri)) - { - e->pos = (u_char *) ngx_escape_uri(e->pos, - &e->line.data[e->captures[code->n]], - e->captures[code->n + 1] - e->captures[code->n], - NGX_ESCAPE_ARGS); - } else { - e->pos = ngx_copy(e->pos, - &e->line.data[e->captures[code->n]], - e->captures[code->n + 1] - e->captures[code->n]); - } - } + e->is_args = 1; + e->ip += sizeof(uintptr_t); - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0, - "http script capture: \"%V\"", &e->buf); + return 1; } @@ -556,12 +898,12 @@ 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 @@ -590,16 +932,16 @@ e->line.data = e->sp->data; } - rc = ngx_regex_exec(code->regex, &e->line, e->captures, code->ncaptures); + rc = ngx_http_regex_exec(r, code->regex, &e->line); - if (rc == NGX_REGEX_NO_MATCHED) { - if (e->log) { + 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); } - e->ncaptures = 0; + r->ncaptures = 0; if (code->test) { if (code->negative_test) { @@ -621,23 +963,17 @@ return; } - if (rc < 0) { - ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, - ngx_regex_exec_n " failed: %d on \"%V\" using \"%V\"", - rc, &e->line, &code->name); - + if (rc == NGX_ERROR) { e->ip = ngx_http_script_exit; e->status = NGX_HTTP_INTERNAL_SERVER_ERROR; return; } - if (e->log) { + 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); } - e->ncaptures = code->ncaptures; - if (code->test) { if (code->negative_test) { e->sp->len = 0; @@ -680,14 +1016,14 @@ e->buf.len = code->size; if (code->uri) { - if (rc && (r->quoted_uri || r->plus_in_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 = 1; n < (ngx_uint_t) rc; n++) { - e->buf.len += e->captures[2 * n + 1] - e->captures[2 * n]; + for (n = 2; n < r->ncaptures; n += 2) { + e->buf.len += r->captures[n + 1] - r->captures[n]; } } else { @@ -696,11 +1032,9 @@ le.ip = code->lengths->elts; le.line = e->line; le.request = r; - le.captures = e->captures; - le.ncaptures = e->ncaptures; le.quote = code->redirect; - len = 1; /* reserve 1 byte for possible "?" */ + len = 0; while (*(uintptr_t *) le.ip) { lcode = *(ngx_http_script_len_code_pt *) le.ip; @@ -708,13 +1042,14 @@ } e->buf.len = len; + e->is_args = le.is_args; } if (code->add_args && r->args.len) { e->buf.len += r->args.len + 1; } - e->buf.data = ngx_palloc(r->pool, e->buf.len); + 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; @@ -750,7 +1085,8 @@ dst = e->buf.data; src = e->buf.data; - ngx_unescape_uri(&dst, &src, e->pos - e->buf.data, NGX_UNESCAPE_URI); + ngx_unescape_uri(&dst, &src, e->pos - e->buf.data, + NGX_UNESCAPE_REDIRECT); if (src < e->pos) { dst = ngx_copy(dst, src, e->pos - src); @@ -765,7 +1101,7 @@ e->buf.len = e->pos - e->buf.data; - if (e->log) { + 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); } @@ -778,8 +1114,7 @@ } r->headers_out.location->hash = 1; - r->headers_out.location->key.len = sizeof("Location") - 1; - r->headers_out.location->key.data = (u_char *) "Location"; + 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); @@ -807,7 +1142,7 @@ } } - if (e->log) { + 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); @@ -824,19 +1159,200 @@ return; } - if (ngx_http_set_exten(r) != NGX_OK) { - 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; + + code = (ngx_http_script_full_name_code_t *) e->ip; + + value.data = e->buf.data; + value.len = e->pos - e->buf.data; + + if (ngx_conf_full_name((ngx_cycle_t *) ngx_cycle, &value, code->conf_prefix) + != 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) { @@ -844,14 +1360,17 @@ code = (ngx_http_script_return_code_t *) e->ip; - e->status = code->status; - - if (code->status == NGX_HTTP_NO_CONTENT) { - e->request->header_only = 1; - e->request->zero_body = 1; + 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 += sizeof(ngx_http_script_return_code_t) - sizeof(uintptr_t); + e->ip = ngx_http_script_exit; } @@ -907,8 +1426,8 @@ e->ip += sizeof(uintptr_t); - if (val->len == res->len && ngx_strncmp(val->data, res->data, res->len) - == 0) + if (val->len == res->len + && ngx_strncmp(val->data, res->data, res->len) == 0) { *res = ngx_http_variable_true_value; return; @@ -935,8 +1454,8 @@ e->ip += sizeof(uintptr_t); - if (val->len == res->len && ngx_strncmp(val->data, res->data, res->len) - == 0) + 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"); @@ -952,8 +1471,10 @@ void ngx_http_script_file_code(ngx_http_script_engine_t *e) { - ngx_err_t err; - ngx_file_info_t fi; + 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; @@ -962,15 +1483,35 @@ code = (ngx_http_script_file_code_t *) e->ip; e->ip += sizeof(ngx_http_script_file_code_t); - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0, - "http script file op %p", code->op); + 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\"", code->op, &path); + + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); - if (ngx_file_info(value->data, &fi) == -1) { - err = ngx_errno; + ngx_memzero(&of, sizeof(ngx_open_file_info_t)); - if (err != NGX_ENOENT && err != NGX_ENOTDIR) { - ngx_log_error(NGX_LOG_CRIT, e->request->connection->log, err, - ngx_file_info_n " \"%s\" failed", value->data); + 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_open_cached_file(clcf->open_file_cache, &path, &of, r->pool) + != NGX_OK) + { + 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) { @@ -979,89 +1520,77 @@ case ngx_http_script_file_dir: case ngx_http_script_file_exists: case ngx_http_script_file_exec: - goto false; + 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; + goto true_value; } - goto false; + goto false_value; } switch (code->op) { case ngx_http_script_file_plain: - if (ngx_is_file(&fi)) { - goto true; + if (of.is_file) { + goto true_value; } - goto false; + goto false_value; case ngx_http_script_file_not_plain: - if (ngx_is_file(&fi)) { - goto false; + if (of.is_file) { + goto false_value; } - goto true; + goto true_value; case ngx_http_script_file_dir: - if (ngx_is_dir(&fi)) { - goto true; + if (of.is_dir) { + goto true_value; } - goto false; + goto false_value; case ngx_http_script_file_not_dir: - if (ngx_is_dir(&fi)) { - goto false; + if (of.is_dir) { + goto false_value; } - goto true; + goto true_value; case ngx_http_script_file_exists: - if (ngx_is_file(&fi) || ngx_is_dir(&fi) || ngx_is_link(&fi)) { - goto true; + if (of.is_file || of.is_dir || of.is_link) { + goto true_value; } - goto false; + goto false_value; case ngx_http_script_file_not_exists: - if (ngx_is_file(&fi) || ngx_is_dir(&fi) || ngx_is_link(&fi)) { - goto false; + if (of.is_file || of.is_dir || of.is_link) { + goto false_value; } - goto true; - -#if (NGX_WIN32) - - case ngx_http_script_file_exec: - goto false; - - case ngx_http_script_file_not_exec: - goto true; - -#else + goto true_value; case ngx_http_script_file_exec: - if (ngx_is_exec(&fi)) { - goto true; + if (of.is_exec) { + goto true_value; } - goto false; + goto false_value; case ngx_http_script_file_not_exec: - if (ngx_is_exec(&fi)) { - goto false; + if (of.is_exec) { + goto false_value; } - goto true; - -#endif + goto true_value; } -false: +false_value: - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0, + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http script file op false"); *value = ngx_http_variable_null_value; return; -true: +true_value: *value = ngx_http_variable_true_value; return; @@ -1088,8 +1617,6 @@ le.ip = code->lengths->elts; le.line = e->line; le.request = e->request; - le.captures = e->captures; - le.ncaptures = e->ncaptures; le.quote = e->quote; for (len = 0; *(uintptr_t *) le.ip; len += lcode(&le)) { @@ -1097,7 +1624,7 @@ } e->buf.len = len; - e->buf.data = ngx_palloc(e->request->pool, 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; @@ -1125,7 +1652,7 @@ 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); + "http script value: \"%v\"", e->sp); e->sp++; } @@ -1137,9 +1664,6 @@ ngx_http_request_t *r; ngx_http_script_var_code_t *code; - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0, - "http script set var"); - code = (ngx_http_script_var_code_t *) e->ip; e->ip += sizeof(ngx_http_script_var_code_t); @@ -1150,9 +1674,23 @@ r->variables[code->index].len = e->sp->len; r->variables[code->index].valid = 1; - r->variables[code->index].no_cachable = 0; + 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 } diff -Nru nginx-0.5.33/src/http/ngx_http_script.h nginx-0.8.53/src/http/ngx_http_script.h --- nginx-0.5.33/src/http/ngx_http_script.h 2006-08-30 10:39:17.000000000 +0000 +++ nginx-0.8.53/src/http/ngx_http_script.h 2010-09-13 12:44:43.000000000 +0000 @@ -27,11 +27,9 @@ unsigned flushed:1; unsigned skip:1; unsigned quote:1; + unsigned is_args:1; unsigned log:1; - int *captures; - ngx_uint_t ncaptures; - ngx_int_t status; ngx_http_request_t *request; } ngx_http_script_engine_t; @@ -55,86 +53,113 @@ unsigned compile_args:1; unsigned complete_lengths:1; unsigned complete_values:1; - unsigned dup_capture: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_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_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_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_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_regex_t *regex; - ngx_array_t *lengths; - uintptr_t size; - uintptr_t ncaptures; - uintptr_t status; - uintptr_t next; - - uintptr_t test:1; - uintptr_t negative_test:1; - uintptr_t uri:1; - uintptr_t args:1; + ngx_http_script_code_pt code; + ngx_http_regex_t *regex; + ngx_array_t *lengths; + uintptr_t size; + uintptr_t status; + uintptr_t next; + + uintptr_t test:1; + uintptr_t negative_test:1; + uintptr_t uri:1; + uintptr_t args:1; /* add the r->args to the new arguments */ - uintptr_t add_args:1; + uintptr_t add_args:1; - uintptr_t redirect:1; - uintptr_t break_cycle:1; + uintptr_t redirect:1; + uintptr_t break_cycle:1; - ngx_str_t name; + ngx_str_t name; } ngx_http_script_regex_code_t; typedef struct { - ngx_http_script_code_pt code; + ngx_http_script_code_pt code; - uintptr_t uri:1; - uintptr_t args:1; + uintptr_t uri:1; + uintptr_t args:1; /* add the r->args to the new arguments */ - uintptr_t add_args:1; + uintptr_t add_args:1; - uintptr_t redirect:1; + uintptr_t redirect:1; } ngx_http_script_regex_end_code_t; #endif typedef struct { - ngx_http_script_code_pt code; - uintptr_t status; - uintptr_t null; + 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; @@ -151,37 +176,51 @@ typedef struct { - ngx_http_script_code_pt code; - uintptr_t op; + 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_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_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_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_cachable_variables(ngx_http_request_t *r, +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, @@ -194,6 +233,7 @@ 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); diff -Nru nginx-0.5.33/src/http/ngx_http_special_response.c nginx-0.8.53/src/http/ngx_http_special_response.c --- nginx-0.5.33/src/http/ngx_http_special_response.c 2007-08-14 20:02:09.000000000 +0000 +++ nginx-0.8.53/src/http/ngx_http_special_response.c 2010-10-18 10:14:00.000000000 +0000 @@ -10,20 +10,34 @@ #include -static u_char error_tail[] = +static ngx_int_t ngx_http_send_error_page(ngx_http_request_t *r, + ngx_http_err_page_t *err_page); +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); +static ngx_int_t ngx_http_send_refresh(ngx_http_request_t *r); + + +static u_char ngx_http_error_full_tail[] = "
" NGINX_VER "
" CRLF "" CRLF "" CRLF ; -static u_char ngx_http_msie_stub[] = -"" CRLF -"" CRLF -"" CRLF -"" CRLF -"" CRLF -"" CRLF +static u_char ngx_http_error_tail[] = +"
nginx
" CRLF +"" CRLF +"" CRLF +; + + +static u_char ngx_http_msie_padding[] = +"" CRLF +"" CRLF +"" CRLF +"" CRLF +"" CRLF +"" CRLF ; @@ -35,7 +49,7 @@ "\">" CRLF; -static char error_301_page[] = +static char ngx_http_error_301_page[] = "" CRLF "301 Moved Permanently" CRLF "" CRLF @@ -43,7 +57,7 @@ ; -static char error_302_page[] = +static char ngx_http_error_302_page[] = "" CRLF "302 Found" CRLF "" CRLF @@ -51,7 +65,15 @@ ; -static char error_400_page[] = +static char ngx_http_error_303_page[] = +"" CRLF +"303 See Other" CRLF +"" CRLF +"

303 See Other

" CRLF +; + + +static char ngx_http_error_400_page[] = "" CRLF "400 Bad Request" CRLF "" CRLF @@ -59,7 +81,7 @@ ; -static char error_401_page[] = +static char ngx_http_error_401_page[] = "" CRLF "401 Authorization Required" CRLF "" CRLF @@ -67,7 +89,7 @@ ; -static char error_402_page[] = +static char ngx_http_error_402_page[] = "" CRLF "402 Payment Required" CRLF "" CRLF @@ -75,7 +97,7 @@ ; -static char error_403_page[] = +static char ngx_http_error_403_page[] = "" CRLF "403 Forbidden" CRLF "" CRLF @@ -83,7 +105,7 @@ ; -static char error_404_page[] = +static char ngx_http_error_404_page[] = "" CRLF "404 Not Found" CRLF "" CRLF @@ -91,7 +113,7 @@ ; -static char error_405_page[] = +static char ngx_http_error_405_page[] = "" CRLF "405 Not Allowed" CRLF "" CRLF @@ -99,7 +121,7 @@ ; -static char error_406_page[] = +static char ngx_http_error_406_page[] = "" CRLF "406 Not Acceptable" CRLF "" CRLF @@ -107,7 +129,7 @@ ; -static char error_408_page[] = +static char ngx_http_error_408_page[] = "" CRLF "408 Request Time-out" CRLF "" CRLF @@ -115,7 +137,7 @@ ; -static char error_409_page[] = +static char ngx_http_error_409_page[] = "" CRLF "409 Conflict" CRLF "" CRLF @@ -123,7 +145,7 @@ ; -static char error_410_page[] = +static char ngx_http_error_410_page[] = "" CRLF "410 Gone" CRLF "" CRLF @@ -131,7 +153,7 @@ ; -static char error_411_page[] = +static char ngx_http_error_411_page[] = "" CRLF "411 Length Required" CRLF "" CRLF @@ -139,7 +161,7 @@ ; -static char error_412_page[] = +static char ngx_http_error_412_page[] = "" CRLF "412 Precondition Failed" CRLF "" CRLF @@ -147,7 +169,7 @@ ; -static char error_413_page[] = +static char ngx_http_error_413_page[] = "" CRLF "413 Request Entity Too Large" CRLF "" CRLF @@ -155,7 +177,7 @@ ; -static char error_414_page[] = +static char ngx_http_error_414_page[] = "" CRLF "414 Request-URI Too Large" CRLF "" CRLF @@ -163,7 +185,7 @@ ; -static char error_415_page[] = +static char ngx_http_error_415_page[] = "" CRLF "415 Unsupported Media Type" CRLF "" CRLF @@ -171,7 +193,7 @@ ; -static char error_416_page[] = +static char ngx_http_error_416_page[] = "" CRLF "416 Requested Range Not Satisfiable" CRLF "" CRLF @@ -179,7 +201,7 @@ ; -static char error_495_page[] = +static char ngx_http_error_495_page[] = "" CRLF "400 The SSL certificate error" CRLF @@ -189,7 +211,7 @@ ; -static char error_496_page[] = +static char ngx_http_error_496_page[] = "" CRLF "400 No required SSL certificate was sent" CRLF @@ -199,7 +221,7 @@ ; -static char error_497_page[] = +static char ngx_http_error_497_page[] = "" CRLF "400 The plain HTTP request was sent to HTTPS port" CRLF @@ -209,7 +231,7 @@ ; -static char error_500_page[] = +static char ngx_http_error_500_page[] = "" CRLF "500 Internal Server Error" CRLF "" CRLF @@ -217,7 +239,7 @@ ; -static char error_501_page[] = +static char ngx_http_error_501_page[] = "" CRLF "501 Method Not Implemented" CRLF "" CRLF @@ -225,7 +247,7 @@ ; -static char error_502_page[] = +static char ngx_http_error_502_page[] = "" CRLF "502 Bad Gateway" CRLF "" CRLF @@ -233,7 +255,7 @@ ; -static char error_503_page[] = +static char ngx_http_error_503_page[] = "" CRLF "503 Service Temporarily Unavailable" CRLF "" CRLF @@ -241,7 +263,7 @@ ; -static char error_504_page[] = +static char ngx_http_error_504_page[] = "" CRLF "504 Gateway Time-out" CRLF "" CRLF @@ -249,7 +271,7 @@ ; -static char error_507_page[] = +static char ngx_http_error_507_page[] = "" CRLF "507 Insufficient Storage" CRLF "" CRLF @@ -257,53 +279,59 @@ ; -static ngx_str_t error_pages[] = { +static ngx_str_t ngx_http_error_pages[] = { + + ngx_null_string, /* 201, 204 */ + +#define NGX_HTTP_LAST_LEVEL_200 202 +#define NGX_HTTP_LEVEL_200 (NGX_HTTP_LAST_LEVEL_200 - 201) - ngx_null_string, /* 201, 204 */ + /* 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), + +#define NGX_HTTP_LAST_LEVEL_300 304 +#define NGX_HTTP_LEVEL_300 (NGX_HTTP_LAST_LEVEL_300 - 301) + + 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), + +#define NGX_HTTP_LAST_LEVEL_400 417 +#define NGX_HTTP_LEVEL_400 (NGX_HTTP_LAST_LEVEL_400 - 400) + + 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_null_string, /* 505 */ + ngx_null_string, /* 506 */ + ngx_string(ngx_http_error_507_page) -#define NGX_HTTP_LEVEL_200 1 +#define NGX_HTTP_LAST_LEVEL_500 508 - /* ngx_null_string, */ /* 300 */ - ngx_string(error_301_page), - ngx_string(error_302_page), - ngx_null_string, /* 303 */ - -#define NGX_HTTP_LEVEL_300 3 - - ngx_string(error_400_page), - ngx_string(error_401_page), - ngx_string(error_402_page), - ngx_string(error_403_page), - ngx_string(error_404_page), - ngx_string(error_405_page), - ngx_string(error_406_page), - ngx_null_string, /* 407 */ - ngx_string(error_408_page), - ngx_string(error_409_page), - ngx_string(error_410_page), - ngx_string(error_411_page), - ngx_string(error_412_page), - ngx_string(error_413_page), - ngx_string(error_414_page), - ngx_string(error_415_page), - ngx_string(error_416_page), - -#define NGX_HTTP_LEVEL_400 17 - - ngx_string(error_495_page), /* 495, https certificate error */ - ngx_string(error_496_page), /* 496, https no certificate */ - ngx_string(error_497_page), /* 497, http to https */ - ngx_string(error_404_page), /* 498, invalid host name */ - ngx_null_string, /* 499, client had closed connection */ - - ngx_string(error_500_page), - ngx_string(error_501_page), - ngx_string(error_502_page), - ngx_string(error_503_page), - ngx_string(error_504_page), - ngx_null_string, /* 505 */ - ngx_null_string, /* 506 */ - ngx_string(error_507_page) }; @@ -313,29 +341,17 @@ ngx_int_t ngx_http_special_response_handler(ngx_http_request_t *r, ngx_int_t error) { - u_char *p; - size_t msie_refresh; - uintptr_t escape; - ngx_int_t rc; - ngx_buf_t *b; - ngx_str_t *uri, *location; - ngx_uint_t i, n, err, msie_padding; - ngx_chain_t *out, *cl; + ngx_uint_t i, err; ngx_http_err_page_t *err_page; ngx_http_core_loc_conf_t *clcf; - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http special response: %d, \"%V\"", error, &r->uri); - - rc = ngx_http_discard_body(r); - - if (rc == NGX_HTTP_INTERNAL_SERVER_ERROR) { - error = NGX_HTTP_INTERNAL_SERVER_ERROR; - } + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http special response: %d, \"%V?%V\"", + error, &r->uri, &r->args); r->err_status = error; - if (r->keepalive != 0) { + if (r->keepalive) { switch (error) { case NGX_HTTP_BAD_REQUEST: case NGX_HTTP_REQUEST_ENTITY_TOO_LARGE: @@ -362,7 +378,7 @@ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); - if (!r->error_page && clcf->error_pages) { + if (!r->error_page && clcf->error_pages && r->uri_changes != 0) { if (clcf->recursive_error_pages == 0) { r->error_page = 1; @@ -371,66 +387,24 @@ err_page = clcf->error_pages->elts; for (i = 0; i < clcf->error_pages->nelts; i++) { - if (err_page[i].status == error) { - r->err_status = err_page[i].overwrite; + return ngx_http_send_error_page(r, &err_page[i]); + } + } + } - r->method = NGX_HTTP_GET; - r->method_name = ngx_http_get_name; + r->expect_tested = 1; - uri = &err_page[i].uri; + if (ngx_http_discard_request_body(r) != NGX_OK) { + error = NGX_HTTP_INTERNAL_SERVER_ERROR; + } - if (err_page[i].uri_lengths) { - if (ngx_http_script_run(r, uri, - err_page[i].uri_lengths->elts, 0, - err_page[i].uri_values->elts) - == NULL) - { - return NGX_ERROR; - } - - if (r->zero_in_uri) { - for (n = 0; n < uri->len; n++) { - if (uri->data[n] == '\0') { - goto zero; - } - } - - r->zero_in_uri = 0; - } - - } else { - r->zero_in_uri = 0; - } - - zero: - - if (uri->data[0] == '/') { - return ngx_http_internal_redirect(r, uri, NULL); - } - - if (uri->data[0] == '@') { - return ngx_http_named_location(r, uri); - } - - r->headers_out.location = - ngx_list_push(&r->headers_out.headers); - - if (r->headers_out.location) { - error = NGX_HTTP_MOVED_TEMPORARILY; - - r->err_status = NGX_HTTP_MOVED_TEMPORARILY; - - r->headers_out.location->hash = 1; - r->headers_out.location->key.len = sizeof("Location") - 1; - r->headers_out.location->key.data = (u_char *) "Location"; - r->headers_out.location->value = *uri; - - } else { - return NGX_ERROR; - } - } - } + 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) { @@ -442,16 +416,22 @@ /* 204 */ err = 0; - } else if (error < NGX_HTTP_BAD_REQUEST) { + } else if (error >= NGX_HTTP_MOVED_PERMANENTLY + && error < NGX_HTTP_LAST_LEVEL_300) + { /* 3XX */ err = error - NGX_HTTP_MOVED_PERMANENTLY + NGX_HTTP_LEVEL_200; - } else if (error < NGX_HTTP_OWN_CODES) { + } else if (error >= NGX_HTTP_BAD_REQUEST + && error < NGX_HTTP_LAST_LEVEL_400) + { /* 4XX */ err = error - NGX_HTTP_BAD_REQUEST + NGX_HTTP_LEVEL_200 + NGX_HTTP_LEVEL_300; - } else { + } else if (error >= NGX_HTTP_OWN_CODES + && error < NGX_HTTP_LAST_LEVEL_500) + { /* 49X, 5XX */ err = error - NGX_HTTP_OWN_CODES + NGX_HTTP_LEVEL_200 + NGX_HTTP_LEVEL_300 @@ -461,71 +441,188 @@ case NGX_HTTPS_CERT_ERROR: case NGX_HTTPS_NO_CERT: r->err_status = NGX_HTTP_BAD_REQUEST; - error = NGX_HTTP_BAD_REQUEST; break; } + + } else { + /* unknown code, zero body */ + err = 0; } - msie_padding = 0; + return ngx_http_send_special_response(r, clcf, err); +} - if (!r->zero_body) { - if (error_pages[err].len) { - r->headers_out.content_length_n = error_pages[err].len - + sizeof(error_tail) - 1; - - if (clcf->msie_padding - && r->headers_in.msie - && r->http_version >= NGX_HTTP_VERSION_10 - && error >= NGX_HTTP_BAD_REQUEST - && error != NGX_HTTP_REQUEST_URI_TOO_LARGE) - { - r->headers_out.content_length_n += - sizeof(ngx_http_msie_stub) - 1; - msie_padding = 1; - } - r->headers_out.content_type.len = sizeof("text/html") - 1; - r->headers_out.content_type.data = (u_char *) "text/html"; +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.data[0] == '/') { + + if (err_page->value.lengths) { + ngx_http_split_args(r, &uri, &args); } else { - r->headers_out.content_length_n = -1; + args = err_page->args; } - } else { - r->headers_out.content_length_n = 0; - err = 0; + if (r->method != NGX_HTTP_HEAD) { + r->method = NGX_HTTP_GET; + r->method_name = ngx_http_get_name; + } + + return ngx_http_internal_redirect(r, &uri, &args); } - if (r->headers_out.content_length) { - r->headers_out.content_length->hash = 0; - r->headers_out.content_length = NULL; + if (uri.data[0] == '@') { + return ngx_http_named_location(r, &uri); } - if (clcf->msie_refresh - && r->headers_in.msie - && (error == NGX_HTTP_MOVED_PERMANENTLY - || error == NGX_HTTP_MOVED_TEMPORARILY)) - { + location = ngx_list_push(&r->headers_out.headers); - location = &r->headers_out.location->value; + if (location == NULL) { + return NGX_ERROR; + } + + r->err_status = overwrite > 0 ? overwrite : NGX_HTTP_MOVED_TEMPORARILY; - escape = 2 * ngx_escape_uri(NULL, location->data, location->len, - NGX_ESCAPE_REFRESH); + location->hash = 1; + ngx_str_set(&location->key, "Location"); + location->value = uri; - msie_refresh = sizeof(ngx_http_msie_refresh_head) - 1 - + escape + location->len - + sizeof(ngx_http_msie_refresh_tail) - 1; + 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_LEVEL_200); +} + + +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) { + len = sizeof(ngx_http_error_full_tail) - 1; + tail = ngx_http_error_full_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_LEVEL_300) + { + r->headers_out.content_length_n += + sizeof(ngx_http_msie_padding) - 1; + msie_padding = 1; + } - r->err_status = NGX_HTTP_OK; r->headers_out.content_type_len = sizeof("text/html") - 1; - r->headers_out.content_length_n = msie_refresh; - r->headers_out.location->hash = 0; - r->headers_out.location = NULL; + ngx_str_set(&r->headers_out.content_type, "text/html"); + r->headers_out.content_type_lowcase = NULL; } else { - location = NULL; - escape = 0; - msie_refresh = 0; + r->headers_out.content_length_n = -1; + } + + 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); @@ -537,103 +634,127 @@ return rc; } + if (ngx_http_error_pages[err].len == 0) { + return NGX_OK; + } - if (msie_refresh == 0) { + b = ngx_calloc_buf(r->pool); + if (b == NULL) { + return NGX_ERROR; + } - if (error_pages[err].len == 0) { - return NGX_OK; - } + 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; - b = ngx_calloc_buf(r->pool); - if (b == NULL) { - return NGX_ERROR; - } + out[0].buf = b; + out[0].next = &out[1]; - b->memory = 1; - b->pos = error_pages[err].data; - b->last = error_pages[err].data + error_pages[err].len; + b = ngx_calloc_buf(r->pool); + if (b == NULL) { + return NGX_ERROR; + } - cl = ngx_alloc_chain_link(r->pool); - if (cl == NULL) { - return NGX_ERROR; - } + b->memory = 1; - cl->buf = b; - out = cl; + 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 = error_tail; - b->last = error_tail + sizeof(error_tail) - 1; + b->pos = ngx_http_msie_padding; + b->last = ngx_http_msie_padding + sizeof(ngx_http_msie_padding) - 1; - cl->next = ngx_alloc_chain_link(r->pool); - if (cl->next == NULL) { - return NGX_ERROR; - } + out[1].next = &out[2]; + out[2].buf = b; + out[2].next = NULL; + } - cl = cl->next; - cl->buf = b; + if (r == r->main) { + b->last_buf = 1; + } - if (msie_padding) { - b = ngx_calloc_buf(r->pool); - if (b == NULL) { - return NGX_ERROR; - } + b->last_in_chain = 1; - b->memory = 1; - b->pos = ngx_http_msie_stub; - b->last = ngx_http_msie_stub + sizeof(ngx_http_msie_stub) - 1; - - cl->next = ngx_alloc_chain_link(r->pool); - if (cl->next == NULL) { - return NGX_ERROR; - } + return ngx_http_output_filter(r, &out[0]); +} - cl = cl->next; - cl->buf = b; - } - } else { - b = ngx_create_temp_buf(r->pool, msie_refresh); - if (b == NULL) { - return NGX_ERROR; - } +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; - p = ngx_cpymem(b->pos, ngx_http_msie_refresh_head, - sizeof(ngx_http_msie_refresh_head) - 1); + len = r->headers_out.location->value.len; + location = r->headers_out.location->value.data; - if (escape == 0) { - p = ngx_cpymem(p, location->data, location->len); + escape = 2 * ngx_escape_uri(NULL, location, len, NGX_ESCAPE_REFRESH); - } else { - p = (u_char *) ngx_escape_uri(p, location->data, location->len, - NGX_ESCAPE_REFRESH); - } + size = sizeof(ngx_http_msie_refresh_head) - 1 + + escape + len + + sizeof(ngx_http_msie_refresh_tail) - 1; - b->last = ngx_cpymem(p, ngx_http_msie_refresh_tail, - sizeof(ngx_http_msie_refresh_tail) - 1); + r->err_status = NGX_HTTP_OK; - cl = ngx_alloc_chain_link(r->pool); - if (cl == NULL) { - return NGX_ERROR; - } + 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; - cl->buf = b; - out = cl; + if (r->headers_out.content_length) { + r->headers_out.content_length->hash = 0; + r->headers_out.content_length = NULL; } - if (r == r->main) { - b->last_buf = 1; + ngx_http_clear_accept_ranges(r); + ngx_http_clear_last_modified(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 = 1; b->last_in_chain = 1; - cl->next = NULL; + out.buf = b; + out.next = NULL; - return ngx_http_output_filter(r, out); + return ngx_http_output_filter(r, &out); } diff -Nru nginx-0.5.33/src/http/ngx_http_upstream.c nginx-0.8.53/src/http/ngx_http_upstream.c --- nginx-0.5.33/src/http/ngx_http_upstream.c 2007-10-29 14:52:51.000000000 +0000 +++ nginx-0.8.53/src/http/ngx_http_upstream.c 2010-07-28 15:56:56.000000000 +0000 @@ -9,6 +9,17 @@ #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_send(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); +#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, @@ -19,23 +30,40 @@ ngx_http_upstream_t *u); static void ngx_http_upstream_send_request(ngx_http_request_t *r, ngx_http_upstream_t *u); -static void ngx_http_upstream_send_request_handler(ngx_event_t *wev); -static void ngx_http_upstream_process_header(ngx_event_t *rev); +static void ngx_http_upstream_send_request_handler(ngx_http_request_t *r, + ngx_http_upstream_t *u); +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 void ngx_http_upstream_process_body_in_memory(ngx_event_t *rev); +static ngx_int_t ngx_http_upstream_process_headers(ngx_http_request_t *r, + ngx_http_upstream_t *u); +static void ngx_http_upstream_process_body_in_memory(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_process_non_buffered_downstream(ngx_http_request_t *r); -static void ngx_http_upstream_process_non_buffered_body(ngx_event_t *ev); +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); static void ngx_http_upstream_process_downstream(ngx_http_request_t *r); -static void ngx_http_upstream_process_body(ngx_event_t *ev); +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); static void ngx_http_upstream_store(ngx_http_request_t *r, ngx_http_upstream_t *u); -static void ngx_http_upstream_dummy_handler(ngx_event_t *wev); +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); @@ -44,11 +72,17 @@ 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_set_cookie(ngx_http_request_t *r, + ngx_table_elt_t *h, ngx_uint_t offset); static ngx_int_t - ngx_http_upstream_process_multi_header_lines(ngx_http_request_t *r, + 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, @@ -64,10 +98,15 @@ ngx_table_elt_t *h, ngx_uint_t offset); static ngx_int_t ngx_http_upstream_copy_content_length(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_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); @@ -80,6 +119,8 @@ 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 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, @@ -121,8 +162,13 @@ { ngx_string("Last-Modified"), ngx_http_upstream_process_header_line, offsetof(ngx_http_upstream_headers_in_t, last_modified), + 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, last_modified), 0 }, + offsetof(ngx_http_headers_out_t, etag), 0 }, { ngx_string("Server"), ngx_http_upstream_process_header_line, @@ -136,7 +182,8 @@ ngx_http_upstream_copy_header_line, 0, 0 }, { ngx_string("Location"), - ngx_http_upstream_ignore_header_line, 0, + ngx_http_upstream_process_header_line, + offsetof(ngx_http_upstream_headers_in_t, location), ngx_http_upstream_rewrite_location, 0, 0 }, { ngx_string("Refresh"), @@ -144,7 +191,7 @@ ngx_http_upstream_rewrite_refresh, 0, 0 }, { ngx_string("Set-Cookie"), - ngx_http_upstream_ignore_header_line, 0, + ngx_http_upstream_process_set_cookie, 0, ngx_http_upstream_copy_header_line, 0, 1 }, { ngx_string("Content-Disposition"), @@ -152,21 +199,19 @@ ngx_http_upstream_copy_header_line, 0, 1 }, { ngx_string("Cache-Control"), - ngx_http_upstream_process_multi_header_lines, - offsetof(ngx_http_upstream_headers_in_t, 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_header_line, - offsetof(ngx_http_upstream_headers_in_t, 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_header_line, + ngx_http_upstream_copy_allow_ranges, offsetof(ngx_http_headers_out_t, accept_ranges), 1 }, { ngx_string("Connection"), @@ -182,26 +227,25 @@ ngx_http_upstream_copy_header_line, 0, 0 }, { ngx_string("X-Accel-Expires"), - ngx_http_upstream_process_header_line, - offsetof(ngx_http_upstream_headers_in_t, 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_ignore_header_line, 0, 0 }, + ngx_http_upstream_copy_header_line, 0, 0 }, { ngx_string("X-Accel-Limit-Rate"), ngx_http_upstream_process_limit_rate, 0, - ngx_http_upstream_ignore_header_line, 0, 0 }, + ngx_http_upstream_copy_header_line, 0, 0 }, { ngx_string("X-Accel-Buffering"), ngx_http_upstream_process_buffering, 0, - ngx_http_upstream_ignore_header_line, 0, 0 }, + ngx_http_upstream_copy_header_line, 0, 0 }, { ngx_string("X-Accel-Charset"), ngx_http_upstream_process_charset, 0, - ngx_http_upstream_ignore_header_line, 0, 0 }, + ngx_http_upstream_copy_header_line, 0, 0 }, #if (NGX_HTTP_GZIP) { ngx_string("Content-Encoding"), @@ -268,25 +312,98 @@ static ngx_http_variable_t ngx_http_upstream_vars[] = { { ngx_string("upstream_addr"), NULL, - ngx_http_upstream_addr_variable, 0, NGX_HTTP_VAR_NOHASH, 0 }, + ngx_http_upstream_addr_variable, 0, + NGX_HTTP_VAR_NOHASH|NGX_HTTP_VAR_NOCACHEABLE, 0 }, { ngx_string("upstream_status"), NULL, - ngx_http_upstream_status_variable, 0, NGX_HTTP_VAR_NOHASH, 0 }, + ngx_http_upstream_status_variable, 0, + NGX_HTTP_VAR_NOHASH|NGX_HTTP_VAR_NOCACHEABLE, 0 }, { ngx_string("upstream_response_time"), NULL, - ngx_http_upstream_response_time_variable, 0, NGX_HTTP_VAR_NOHASH, 0 }, + ngx_http_upstream_response_time_variable, 0, + NGX_HTTP_VAR_NOHASH|NGX_HTTP_VAR_NOCACHEABLE, 0 }, + + { ngx_string("upstream_response_length"), NULL, + ngx_http_upstream_response_length_variable, 0, + NGX_HTTP_VAR_NOHASH|NGX_HTTP_VAR_NOCACHEABLE, 0 }, + +#if (NGX_HTTP_CACHE) + + { ngx_string("upstream_cache_status"), NULL, + ngx_http_upstream_cache_status, 0, + NGX_HTTP_VAR_NOHASH|NGX_HTTP_VAR_NOCACHEABLE, 0 }, + +#endif { ngx_null_string, NULL, NULL, 0, 0, 0 } }; +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 }, + { 404, NGX_HTTP_UPSTREAM_FT_HTTP_404 }, + { 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("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_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_THREADS) + u->peer.lock = &r->connection->lock; +#endif + +#if (NGX_HTTP_CACHE) + r->cache = NULL; +#endif + + return NGX_OK; +} + + void ngx_http_upstream_init(ngx_http_request_t *r) { - ngx_connection_t *c; - ngx_http_cleanup_t *cln; - ngx_http_upstream_t *u; - ngx_http_core_loc_conf_t *clcf; + ngx_connection_t *c; c = r->connection; @@ -297,13 +414,6 @@ ngx_del_timer(c->read); } - u = r->upstream; - - if (!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 (ngx_event_flags & NGX_USE_CLEAR_EVENT) { if (!c->write->active) { @@ -316,23 +426,75 @@ } } - if (r->request_body) { - u->request_bufs = r->request_body->bufs; - } + ngx_http_upstream_init_request(r); +} - if (u->conf->upstream->peer.init(r, u->conf->upstream) != NGX_OK) { - ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + +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_DONE) { + return; + } + + if (rc != NGX_DECLINED) { + ngx_http_finalize_request(r, rc); + return; + } + } + +#endif + + u->store = (u->conf->store || u->conf->store_lengths); + + 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; } + u->peer.local = u->conf->local; + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); - u->output.sendfile = r->connection->sendfile; + 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; @@ -372,167 +534,533 @@ cln->data = r; u->cleanup = &cln->handler; - u->store = (u->conf->store || u->conf->store_lengths); - - ngx_http_upstream_connect(r, u); -} - + if (u->resolved == NULL) { -static void -ngx_http_upstream_rd_check_broken_connection(ngx_http_request_t *r) -{ - ngx_http_upstream_check_broken_connection(r, r->connection->read); -} + uscf = u->conf->upstream; + } else { -static void -ngx_http_upstream_wr_check_broken_connection(ngx_http_request_t *r) -{ - ngx_http_upstream_check_broken_connection(r, r->connection->write); -} + if (u->resolved->sockaddr) { + 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; + } -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_connection_t *c; - ngx_http_upstream_t *u; + ngx_http_upstream_connect(r, u); - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ev->log, 0, - "http upstream check client, write event:%d, \"%V\"", - ev->write, &r->uri); + return; + } - c = r->connection; - u = r->upstream; + host = &u->resolved->host; - if (c->error) { - ngx_http_upstream_finalize_request(r, u, - NGX_HTTP_CLIENT_CLOSED_REQUEST); - return; - } + umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module); - if (u->peer.connection == NULL) { - return; - } + uscfp = umcf->upstreams.elts; -#if (NGX_HAVE_KQUEUE) + for (i = 0; i < umcf->upstreams.nelts; i++) { - if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) { + uscf = uscfp[i]; - if (!ev->pending_eof) { - return; + if (uscf->host.len == host->len + && ((uscf->port == 0 && u->resolved->no_port) + || uscf->port == u->resolved->port) + && ngx_memcmp(uscf->host.data, host->data, host->len) == 0) + { + goto found; + } } - ev->eof = 1; - c->error = 1; - - if (ev->kq_errno) { - ev->error = 1; - } + temp.name = *host; - if (!u->cachable && !u->store && u->peer.connection) { - ngx_log_error(NGX_LOG_INFO, ev->log, ev->kq_errno, - "kevent() reported that client closed prematurely " - "connection, so upstream connection is closed too"); + ctx = ngx_resolve_start(clcf->resolver, &temp); + if (ctx == NULL) { ngx_http_upstream_finalize_request(r, u, - NGX_HTTP_CLIENT_CLOSED_REQUEST); + NGX_HTTP_INTERNAL_SERVER_ERROR); return; } - ngx_log_error(NGX_LOG_INFO, ev->log, ev->kq_errno, - "kevent() reported that client closed " - "prematurely connection"); + if (ctx == NGX_NO_RESOLVER) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "no resolver defined to resolve %V", host); - if (u->peer.connection == NULL) { - ngx_http_upstream_finalize_request(r, u, - NGX_HTTP_CLIENT_CLOSED_REQUEST); + ngx_http_upstream_finalize_request(r, u, NGX_HTTP_BAD_GATEWAY); return; } - 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); - - /* - * we do not need to disable the write event because - * that event has NGX_USE_CLEAR_EVENT type - */ + ctx->name = *host; + ctx->type = NGX_RESOLVE_A; + ctx->handler = ngx_http_upstream_resolve_handler; + ctx->data = r; + ctx->timeout = clcf->resolver_timeout; - if (ev->write && (n >= 0 || err == NGX_EAGAIN)) { - return; - } + u->resolved->ctx = ctx; - if ((ngx_event_flags & NGX_USE_LEVEL_EVENT) && ev->active) { - if (ngx_del_event(ev, NGX_READ_EVENT, 0) == NGX_ERROR) { + if (ngx_resolve_name(ctx) != NGX_OK) { + u->resolved->ctx = NULL; 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; +found: - if (!u->cachable && !u->store && u->peer.connection) { - ngx_log_error(NGX_LOG_INFO, ev->log, err, - "client closed prematurely connection, " - "so upstream connection is closed too"); + if (uscf->peer.init(r, uscf) != NGX_OK) { ngx_http_upstream_finalize_request(r, u, - NGX_HTTP_CLIENT_CLOSED_REQUEST); + NGX_HTTP_INTERNAL_SERVER_ERROR); return; } - ngx_log_error(NGX_LOG_INFO, ev->log, err, - "client closed prematurely connection"); - - if (u->peer.connection == NULL) { - ngx_http_upstream_finalize_request(r, u, - NGX_HTTP_CLIENT_CLOSED_REQUEST); - return; - } + ngx_http_upstream_connect(r, u); } -static void -ngx_http_upstream_connect(ngx_http_request_t *r, ngx_http_upstream_t *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_time_t *tp; - ngx_connection_t *c; + ngx_http_cache_t *c; - r->connection->log->action = "connecting to upstream"; + c = r->cache; - r->connection->single_connection = 0; + if (c == NULL) { - if (u->state && u->state->response_sec) { - tp = ngx_timeofday(); + 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; + } + + if (!(r->method & u->conf->cache_methods)) { + return NGX_DECLINED; + } + + if (r->method & NGX_HTTP_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); + + u->cacheable = 1; + + c = r->cache; + + c->min_uses = u->conf->cache_min_uses; + c->body_start = u->conf->buffer_size; + c->file_cache = u->conf->cache->data; + + 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_UPDATING: + + if (u->conf->cache_use_stale & NGX_HTTP_UPSTREAM_FT_UPDATING) { + 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: + + rc = ngx_http_upstream_cache_send(r, u); + + if (rc != NGX_HTTP_UPSTREAM_INVALID_HEADER) { + return rc; + } + + break; + + case NGX_HTTP_CACHE_STALE: + + c->valid_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; + } + + r->cached = 0; + + return NGX_DECLINED; +} + + +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)); + + if (ngx_list_init(&u->headers_in.headers, r->pool, 8, + 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; + } + + /* rc == NGX_HTTP_UPSTREAM_INVALID_HEADER */ + + /* TODO: delete file */ + + return rc; +} + +#endif + + +static void +ngx_http_upstream_resolve_handler(ngx_resolver_ctx_t *ctx) +{ + ngx_http_request_t *r; + ngx_http_upstream_t *u; + ngx_http_upstream_resolved_t *ur; + + r = ctx->data; + + u = r->upstream; + ur = u->resolved; + + 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); + return; + } + + ur->naddrs = ctx->naddrs; + ur->addrs = ctx->addrs; + +#if (NGX_DEBUG) + { + in_addr_t addr; + ngx_uint_t i; + + for (i = 0; i < ctx->naddrs; i++) { + addr = ntohl(ur->addrs[i]); + + ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "name was resolved to %ud.%ud.%ud.%ud", + (addr >> 24) & 0xff, (addr >> 16) & 0xff, + (addr >> 8) & 0xff, addr & 0xff); + } + } +#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); + return; + } + + ngx_resolve_name_done(ctx); + ur->ctx = NULL; + + ngx_http_upstream_connect(r, u); +} + + +static void +ngx_http_upstream_handler(ngx_event_t *ev) +{ + ngx_connection_t *c; + ngx_http_request_t *r; + ngx_http_log_ctx_t *ctx; + ngx_http_upstream_t *u; + + c = ev->data; + r = c->data; + + u = r->upstream; + c = r->connection; + + ctx = c->log->data; + ctx->current_request = r; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http upstream request: \"%V?%V\"", &r->uri, &r->args); + + 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_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 closed prematurely " + "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 closed " + "prematurely 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 closed prematurely 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 closed prematurely 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_time_t *tp; + ngx_connection_t *c; + + r->connection->log->action = "connecting to upstream"; + + r->connection->single_connection = 0; + + if (u->state && u->state->response_sec) { + tp = ngx_timeofday(); u->state->response_sec = tp->sec - u->state->response_sec; u->state->response_msec = tp->msec - u->state->response_msec; } @@ -565,9 +1093,11 @@ 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_BUSY || rc == NGX_DECLINED) { + if (rc == NGX_DECLINED) { ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR); return; } @@ -578,13 +1108,19 @@ c->data = r; - c->write->handler = ngx_http_upstream_send_request_handler; - c->read->handler = ngx_http_upstream_process_header; + 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; c->pool = r->pool; - c->read->log = c->write->log = c->log = r->connection->log; + c->log = r->connection->log; + c->read->log = c->log; + c->write->log = c->log; /* init or reinit the ngx_output_chain() and ngx_chain_writer() contexts */ @@ -636,7 +1172,7 @@ #if (NGX_HTTP_SSL) - if (u->conf->ssl && c->ssl == NULL) { + if (u->ssl && c->ssl == NULL) { ngx_http_upstream_ssl_init_connection(r, u, c); return; } @@ -657,7 +1193,7 @@ if (ngx_ssl_create_connection(u->conf->ssl, c, NGX_SSL_BUFFER|NGX_SSL_CLIENT) - == NGX_ERROR) + != NGX_OK) { ngx_http_upstream_finalize_request(r, u, NGX_HTTP_INTERNAL_SERVER_ERROR); @@ -667,10 +1203,12 @@ c->sendfile = 0; u->output.sendfile = 0; - 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; + 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; + } } r->connection->log->action = "SSL handshaking to upstream"; @@ -697,10 +1235,12 @@ if (c->ssl->handshaked) { - u->peer.save_session(&u->peer, u->peer.data); + if (u->conf->ssl_session_reuse) { + u->peer.save_session(&u->peer, u->peer.data); + } - c->write->handler = ngx_http_upstream_send_request_handler; - c->read->handler = ngx_http_upstream_process_header; + c->write->handler = ngx_http_upstream_handler; + c->read->handler = ngx_http_upstream_handler; ngx_http_upstream_send_request(r, u); @@ -723,10 +1263,9 @@ return NGX_ERROR; } - ngx_memzero(&r->upstream->headers_in, - sizeof(ngx_http_upstream_headers_in_t)); + ngx_memzero(&u->headers_in, sizeof(ngx_http_upstream_headers_in_t)); - if (ngx_list_init(&r->upstream->headers_in.headers, r->pool, 8, + if (ngx_list_init(&u->headers_in.headers, r->pool, 8, sizeof(ngx_table_elt_t)) != NGX_OK) { @@ -763,22 +1302,18 @@ /* reinit u->buffer */ -#if 0 - if (u->cache) { - u->buffer.pos = u->buffer.start + u->cache->ctx.header_size; - u->buffer.last = u->buffer.pos; + u->buffer.pos = u->buffer.start; - } else { - u->buffer.pos = u->buffer.start; - u->buffer.last = u->buffer.start; - } -#else +#if (NGX_HTTP_CACHE) - u->buffer.pos = u->buffer.start; - u->buffer.last = u->buffer.start; + if (r->cache) { + u->buffer.pos += r->cache->header_start; + } #endif + u->buffer.last = u->buffer.pos; + return NGX_OK; } @@ -817,8 +1352,7 @@ if (rc == NGX_AGAIN) { ngx_add_timer(c->write, u->conf->send_timeout); - if (ngx_handle_write_event(c->write, u->conf->send_lowat) == NGX_ERROR) - { + 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; @@ -855,14 +1389,14 @@ * it's better to do here because we postpone header buffer allocation */ - ngx_http_upstream_process_header(c->read); + ngx_http_upstream_process_header(r, u); return; } #endif - c->write->handler = ngx_http_upstream_dummy_handler; + u->write_event_handler = ngx_http_upstream_dummy_handler; - if (ngx_handle_write_event(c->write, 0) == NGX_ERROR) { + if (ngx_handle_write_event(c->write, 0) != NGX_OK) { ngx_http_upstream_finalize_request(r, u, NGX_HTTP_INTERNAL_SERVER_ERROR); return; @@ -871,27 +1405,24 @@ static void -ngx_http_upstream_send_request_handler(ngx_event_t *wev) +ngx_http_upstream_send_request_handler(ngx_http_request_t *r, + ngx_http_upstream_t *u) { - ngx_connection_t *c; - ngx_http_request_t *r; - ngx_http_upstream_t *u; + ngx_connection_t *c; - c = wev->data; - r = c->data; - u = r->upstream; + c = u->peer.connection; - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, wev->log, 0, + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http upstream send request handler"); - if (wev->timedout) { + if (c->write->timedout) { ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_TIMEOUT); return; } #if (NGX_HTTP_SSL) - if (u->conf->ssl && c->ssl == NULL) { + if (u->ssl && c->ssl == NULL) { ngx_http_upstream_ssl_init_connection(r, u, c); return; } @@ -899,9 +1430,9 @@ #endif if (u->header_sent) { - wev->handler = ngx_http_upstream_dummy_handler; + u->write_event_handler = ngx_http_upstream_dummy_handler; - (void) ngx_handle_write_event(wev, 0); + (void) ngx_handle_write_event(c->write, 0); return; } @@ -911,32 +1442,20 @@ static void -ngx_http_upstream_process_header(ngx_event_t *rev) +ngx_http_upstream_process_header(ngx_http_request_t *r, ngx_http_upstream_t *u) { - ssize_t n; - ngx_int_t rc; - ngx_str_t *uri, args; - ngx_uint_t i, flags; - ngx_list_part_t *part; - ngx_table_elt_t *h; - ngx_connection_t *c; - ngx_http_request_t *r; - ngx_http_upstream_t *u; - ngx_http_err_page_t *err_page; - ngx_http_core_loc_conf_t *clcf; - ngx_http_upstream_header_t *hh; - ngx_http_upstream_main_conf_t *umcf; + ssize_t n; + ngx_int_t rc; + ngx_connection_t *c; - c = rev->data; - r = c->data; - u = r->upstream; + c = u->peer.connection; - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http upstream process header"); c->log->action = "reading response header from upstream"; - if (rev->timedout) { + if (c->read->timedout) { ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_TIMEOUT); return; } @@ -961,7 +1480,7 @@ u->buffer.tag = u->output.tag; - if (ngx_list_init(&r->upstream->headers_in.headers, r->pool, 8, + if (ngx_list_init(&u->headers_in.headers, r->pool, 8, sizeof(ngx_table_elt_t)) != NGX_OK) { @@ -970,71 +1489,68 @@ return; } -#if 0 - if (u->cache) { - u->buffer.pos += u->cache->ctx.header_size; +#if (NGX_HTTP_CACHE) + + if (r->cache) { + u->buffer.pos += r->cache->header_start; u->buffer.last = u->buffer.pos; } #endif } - n = u->peer.connection->recv(u->peer.connection, u->buffer.last, - u->buffer.end - u->buffer.last); + for ( ;; ) { + + n = c->recv(c, u->buffer.last, u->buffer.end - u->buffer.last); - if (n == NGX_AGAIN) { + if (n == NGX_AGAIN) { #if 0 - ngx_add_timer(rev, u->read_timeout); + ngx_add_timer(rev, u->read_timeout); #endif - if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { - ngx_http_upstream_finalize_request(r, u, + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { + ngx_http_upstream_finalize_request(r, u, NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + return; } - return; - } - - if (n == 0) { - ngx_log_error(NGX_LOG_ERR, rev->log, 0, - "upstream prematurely closed connection"); - } + 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; - } + if (n == NGX_ERROR || n == 0) { + ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR); + return; + } - u->buffer.last += n; + u->buffer.last += n; #if 0 - u->valid_header_in = 0; + u->valid_header_in = 0; - u->peer.cached = 0; + u->peer.cached = 0; #endif - rc = u->process_header(r); + rc = u->process_header(r); - if (rc == NGX_AGAIN) { -#if 0 - ngx_add_timer(rev, u->read_timeout); -#endif + if (rc == NGX_AGAIN) { - if (u->buffer.pos == u->buffer.end) { - ngx_log_error(NGX_LOG_ERR, rev->log, 0, - "upstream sent too big header"); + if (u->buffer.pos == 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; - } + ngx_http_upstream_next(r, u, + NGX_HTTP_UPSTREAM_FT_INVALID_HEADER); + return; + } - if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { - ngx_http_upstream_finalize_request(r, u, - NGX_HTTP_INTERNAL_SERVER_ERROR); - return; + continue; } - return; + break; } if (rc == NGX_HTTP_UPSTREAM_INVALID_HEADER) { @@ -1042,7 +1558,7 @@ return; } - if (rc == NGX_ERROR || rc == NGX_HTTP_INTERNAL_SERVER_ERROR) { + if (rc == NGX_ERROR) { ngx_http_upstream_finalize_request(r, u, NGX_HTTP_INTERNAL_SERVER_ERROR); return; @@ -1050,92 +1566,247 @@ /* rc == NGX_OK */ - if (u->headers_in.status_n >= NGX_HTTP_BAD_REQUEST - && r->subrequest_in_memory) - { - u->buffer.last = u->buffer.pos; - } + if (u->headers_in.status_n > NGX_HTTP_SPECIAL_RESPONSE) { - if (u->headers_in.status_n == NGX_HTTP_INTERNAL_SERVER_ERROR) { + if (r->subrequest_in_memory) { + u->buffer.last = u->buffer.pos; + } - if (u->peer.tries > 1 - && (u->conf->next_upstream & NGX_HTTP_UPSTREAM_FT_HTTP_500)) - { - ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_HTTP_500); + if (ngx_http_upstream_test_next(r, u) == NGX_OK) { return; } -#if (NGX_HTTP_CACHE) - - if (u->peer.tries == 0 - && u->stale - && (u->conf->use_stale & NGX_HTTP_UPSTREAM_FT_HTTP_500)) - { - ngx_http_upstream_finalize_request(r, u, - ngx_http_send_cached_response(r)); + if (ngx_http_upstream_intercept_errors(r, u) == NGX_OK) { return; } + } -#endif + if (ngx_http_upstream_process_headers(r, u) != NGX_OK) { + return; + } + + if (!r->subrequest_in_memory) { + ngx_http_upstream_send_response(r, u); + return; } - if (u->headers_in.status_n == NGX_HTTP_NOT_FOUND) { + /* subrequest content in memory */ - if (u->peer.tries > 1 - && u->conf->next_upstream & NGX_HTTP_UPSTREAM_FT_HTTP_404) - { - ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_HTTP_404); + 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; + } + + if (u->input_filter_init(u->input_filter_ctx) == NGX_ERROR) { + ngx_http_upstream_finalize_request(r, u, + NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + n = u->buffer.last - u->buffer.pos; + + if (n) { + u->buffer.last -= 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; } - if (u->conf->intercept_404) { - ngx_http_upstream_finalize_request(r, u, NGX_HTTP_NOT_FOUND); + if (u->length == 0) { + ngx_http_upstream_finalize_request(r, u, 0); return; } } + u->read_event_handler = ngx_http_upstream_process_body_in_memory; - if (u->headers_in.status_n >= NGX_HTTP_BAD_REQUEST - && u->conf->intercept_errors) - { - clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + ngx_http_upstream_process_body_in_memory(r, u); +} - if (clcf->error_pages) { - err_page = clcf->error_pages->elts; - for (i = 0; i < clcf->error_pages->nelts; i++) { - if (err_page[i].status == (ngx_int_t) u->headers_in.status_n) { +static ngx_int_t +ngx_http_upstream_test_next(ngx_http_request_t *r, ngx_http_upstream_t *u) +{ + ngx_uint_t status; + ngx_http_upstream_next_t *un; - if (u->headers_in.status_n == NGX_HTTP_UNAUTHORIZED) { + status = u->headers_in.status_n; - r->headers_out.www_authenticate = - ngx_list_push(&r->headers_out.headers); + for (un = ngx_http_upstream_next_errors; un->status; un++) { - if (r->headers_out.www_authenticate == NULL) { - ngx_http_upstream_finalize_request(r, u, - NGX_HTTP_INTERNAL_SERVER_ERROR); - return; - } + if (status != un->status) { + continue; + } + + if (u->peer.tries > 1 && (u->conf->next_upstream & un->mask)) { + 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)) + { + ngx_int_t rc; + + rc = u->reinit_request(r); + + if (rc == NGX_OK) { + u->cache_status = NGX_HTTP_CACHE_STALE; + rc = ngx_http_upstream_cache_send(r, u); + } + + 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); - *r->headers_out.www_authenticate = - *u->headers_in.www_authenticate; - } + 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, - u->headers_in.status_n); - return; + 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) { + time_t valid; + + valid = ngx_http_file_cache_valid(u->conf->cache_valid, status); + + if (valid) { + r->cache->valid_sec = ngx_time() + 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; } } - umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module); + 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->log->action = "connecting to upstream"; + (void) ngx_connection_error(c, c->write->kq_errno, + "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_errno; + } + + if (err) { + c->log->action = "connecting to upstream"; + (void) ngx_connection_error(c, err, "connect() failed"); + return NGX_ERROR; + } + } - if (r->upstream->headers_in.x_accel_redirect) { + 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 = &r->upstream->headers_in.headers.part; + part = &u->headers_in.headers.part; h = part->elts; for (i = 0; /* void */; i++) { @@ -1157,34 +1828,32 @@ if (hh->copy_handler(r, &h[i], hh->conf) != NGX_OK) { ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); - return; + return NGX_DONE; } } } - uri = &r->upstream->headers_in.x_accel_redirect->value; - args.len = 0; - args.data = NULL; - flags = 0; + uri = &u->headers_in.x_accel_redirect->value; + 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; - } - - if (flags & NGX_HTTP_ZERO_IN_URI) { - r->zero_in_uri = 1; + return NGX_DONE; } if (r->method != NGX_HTTP_HEAD) { r->method = NGX_HTTP_GET; } + r->valid_unparsed_uri = 0; + ngx_http_internal_redirect(r, uri, &args); - return; + ngx_http_finalize_request(r, NGX_DONE); + return NGX_DONE; } - part = &r->upstream->headers_in.headers.part; + part = &u->headers_in.headers.part; h = part->elts; for (i = 0; /* void */; i++) { @@ -1212,7 +1881,7 @@ if (hh->copy_handler(r, &h[i], hh->conf) != NGX_OK) { ngx_http_upstream_finalize_request(r, u, NGX_HTTP_INTERNAL_SERVER_ERROR); - return; + return NGX_DONE; } continue; @@ -1221,7 +1890,7 @@ 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; + return NGX_DONE; } } @@ -1236,6 +1905,8 @@ r->headers_out.status = u->headers_in.status_n; r->headers_out.status_line = u->headers_in.status_line; + u->headers_in.content_length_n = r->headers_out.content_length_n; + if (r->headers_out.content_length_n != -1) { u->length = (size_t) r->headers_out.content_length_n; @@ -1243,98 +1914,22 @@ u->length = NGX_MAX_SIZE_T_VALUE; } - if (!r->subrequest_in_memory) { - ngx_http_upstream_send_response(r, u); - return; - } - - /* subrequest content in memory */ - - 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; - } - - if (u->input_filter_init(u->input_filter_ctx) == NGX_ERROR) { - ngx_http_upstream_finalize_request(r, u, - NGX_HTTP_INTERNAL_SERVER_ERROR); - return; - } - - if (u->buffer.last - u->buffer.pos >= (ssize_t) u->length) { - if (u->input_filter(u->input_filter_ctx, 0) == NGX_ERROR) { - ngx_http_upstream_finalize_request(r, u, NGX_ERROR); - return; - } - - ngx_http_upstream_finalize_request(r, u, 0); - return; - } - - rev->handler = ngx_http_upstream_process_body_in_memory; - - ngx_http_upstream_process_body_in_memory(rev); -} - - -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->log->action = "connecting to upstream"; - (void) ngx_connection_error(c, c->write->kq_errno, - "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_errno; - } - - if (err) { - c->log->action = "connecting to upstream"; - (void) ngx_connection_error(c, err, "connect() failed"); - return NGX_ERROR; - } - } - return NGX_OK; } static void -ngx_http_upstream_process_body_in_memory(ngx_event_t *rev) +ngx_http_upstream_process_body_in_memory(ngx_http_request_t *r, + ngx_http_upstream_t *u) { - size_t size; - ssize_t n; - ngx_buf_t *b; - ngx_connection_t *c; - ngx_http_request_t *r; - ngx_http_upstream_t *u; + size_t size; + ssize_t n; + ngx_buf_t *b; + ngx_event_t *rev; + ngx_connection_t *c; - c = rev->data; - r = c->data; - u = r->upstream; + c = u->peer.connection; + rev = c->read; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http upstream process body on memory"); @@ -1353,7 +1948,7 @@ if (size == 0) { ngx_log_error(NGX_LOG_ALERT, c->log, 0, - "upstream buffer is too small to read repsonse"); + "upstream buffer is too small to read response"); ngx_http_upstream_finalize_request(r, u, NGX_ERROR); return; } @@ -1369,6 +1964,8 @@ return; } + 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; @@ -1379,7 +1976,7 @@ } } - if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { + if (ngx_handle_read_event(rev, 0) != NGX_OK) { ngx_http_upstream_finalize_request(r, u, NGX_ERROR); return; } @@ -1397,39 +1994,46 @@ ngx_http_upstream_send_response(ngx_http_request_t *r, ngx_http_upstream_t *u) { int tcp_nodelay; - ssize_t size; + ssize_t n; ngx_int_t rc; ngx_event_pipe_t *p; ngx_connection_t *c; - ngx_pool_cleanup_t *cl; - ngx_pool_cleanup_file_t *clf; ngx_http_core_loc_conf_t *clcf; rc = ngx_http_send_header(r); - if (rc == NGX_ERROR || rc > NGX_OK || r->post_action || r->header_only) { + if (rc == NGX_ERROR || rc > NGX_OK || r->post_action) { ngx_http_upstream_finalize_request(r, u, rc); return; } - u->header_sent = 1; + c = r->connection; - if (r->request_body && r->request_body->temp_file) { - for (cl = r->pool->cleanup; cl; cl = cl->next) { - if (cl->handler == ngx_pool_cleanup_file) { - clf = cl->data; - - if (clf->fd == r->request_body->temp_file->file.fd) { - cl->handler(clf); - cl->handler = NULL; - r->request_body->temp_file->file.fd = NGX_INVALID_FILE; - break; - } + if (r->header_only) { + + if (u->cacheable || u->store) { + + if (ngx_shutdown_socket(c->fd, NGX_WRITE_SHUTDOWN) == -1) { + ngx_connection_error(c, ngx_socket_errno, + ngx_shutdown_socket_n " failed"); } + + r->read_event_handler = ngx_http_request_empty_handler; + r->write_event_handler = ngx_http_request_empty_handler; + c->error = 1; + + } else { + ngx_http_upstream_finalize_request(r, u, rc); + return; } } - c = r->connection; + u->header_sent = 1; + + if (r->request_body && r->request_body->temp_file) { + 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); @@ -1441,8 +2045,7 @@ u->input_filter_ctx = r; } - u->peer.connection->read->handler = - ngx_http_upstream_process_non_buffered_body; + u->read_event_handler = ngx_http_upstream_process_non_buffered_upstream; r->write_event_handler = ngx_http_upstream_process_non_buffered_downstream; @@ -1470,17 +2073,19 @@ c->tcp_nodelay = NGX_TCP_NODELAY_SET; } - size = u->buffer.last - u->buffer.pos; + n = u->buffer.last - u->buffer.pos; - if (size) { + if (n) { u->buffer.last = u->buffer.pos; - if (u->input_filter(u->input_filter_ctx, size) == NGX_ERROR) { + u->state->response_length += n; + + if (u->input_filter(u->input_filter_ctx, n) == NGX_ERROR) { ngx_http_upstream_finalize_request(r, u, 0); return; } - ngx_http_upstream_process_non_buffered_body(c->write); + ngx_http_upstream_process_non_buffered_downstream(r); } else { u->buffer.pos = u->buffer.start; @@ -1492,8 +2097,7 @@ } if (u->peer.connection->read->ready) { - ngx_http_upstream_process_non_buffered_body( - u->peer.connection->read); + ngx_http_upstream_process_non_buffered_upstream(r, u); } } @@ -1502,28 +2106,87 @@ /* TODO: preallocate event_pipe bufs, look "Content-Length" */ -#if 0 +#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, 0); + return; + + case NGX_DECLINED: + u->cacheable = 0; + break; + + default: /* NGX_OK */ + + if (u->cache_status == NGX_HTTP_CACHE_BYPASS) { + + if (ngx_http_file_cache_new(r) != NGX_OK) { + ngx_http_upstream_finalize_request(r, u, 0); + return; + } + + if (u->create_key(r) != NGX_OK) { + ngx_http_upstream_finalize_request(r, u, 0); + return; + } + + /* TODO: add keys */ - if (u->cache && u->cache->ctx.file.fd != NGX_INVALID_FILE) { - if (ngx_close_file(u->cache->ctx.file.fd) == NGX_FILE_ERROR) { - ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno, - ngx_close_file_n " \"%s\" failed", - u->cache->ctx.file.name.data); + r->cache->min_uses = u->conf->cache_min_uses; + r->cache->body_start = u->conf->buffer_size; + r->cache->file_cache = u->conf->cache->data; + + if (ngx_http_file_cache_create(r) != NGX_OK) { + ngx_http_upstream_finalize_request(r, u, 0); + return; + } + + u->cacheable = 1; + } + + 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->last_modified = r->headers_out.last_modified_time; + r->cache->date = now; + r->cache->body_start = (u_short) (u->buffer.pos - u->buffer.start); + + ngx_http_file_cache_set_header(r, u->buffer.start); + + } else { + u->cacheable = 0; + r->headers_out.last_modified_time = -1; } } - if (u->cachable) { - header = (ngx_http_cache_header_t *) u->buffer->start; - - header->expires = u->cache->ctx.expires; - header->last_modified = u->cache->ctx.last_modified; - header->date = u->cache->ctx.date; - header->length = r->headers_out.content_length_n; - u->cache->ctx.length = r->headers_out.content_length_n; - - header->key_len = u->cache->ctx.key0.len; - ngx_memcpy(&header->key, u->cache->ctx.key0.data, header->key_len); - header->key[header->key_len] = LF; + 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); } #endif @@ -1540,7 +2203,7 @@ p->pool = r->pool; p->log = c->log; - p->cachable = u->cachable || u->store; + p->cacheable = u->cacheable || u->store; p->temp_file = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t)); if (p->temp_file == NULL) { @@ -1553,7 +2216,7 @@ p->temp_file->path = u->conf->temp_path; p->temp_file->pool = r->pool; - if (u->cachable || u->store) { + if (p->cacheable) { p->temp_file->persistent = 1; } else { @@ -1577,7 +2240,7 @@ p->preread_size = u->buffer.last - u->buffer.pos; - if (u->cachable) { + if (u->cacheable) { p->buf_to_file = ngx_calloc_buf(r->pool); if (p->buf_to_file == NULL) { @@ -1623,69 +2286,82 @@ p->send_timeout = clcf->send_timeout; p->send_lowat = clcf->send_lowat; - u->peer.connection->read->handler = ngx_http_upstream_process_body; + u->read_event_handler = ngx_http_upstream_process_upstream; r->write_event_handler = ngx_http_upstream_process_downstream; - ngx_http_upstream_process_body(u->peer.connection->read); + ngx_http_upstream_process_upstream(r, u); } static void ngx_http_upstream_process_non_buffered_downstream(ngx_http_request_t *r) { - ngx_http_upstream_process_non_buffered_body(r->connection->write); + 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, 0); + return; + } + + ngx_http_upstream_process_non_buffered_request(r, 1); } static void -ngx_http_upstream_process_non_buffered_body(ngx_event_t *ev) +ngx_http_upstream_process_non_buffered_upstream(ngx_http_request_t *r, + ngx_http_upstream_t *u) { - size_t size; - ssize_t n; - ngx_buf_t *b; - ngx_int_t rc; - ngx_uint_t do_write; - ngx_connection_t *c, *downstream, *upstream; - ngx_http_request_t *r; - ngx_http_upstream_t *u; - ngx_http_core_loc_conf_t *clcf; - - c = ev->data; - r = c->data; - u = r->upstream; - - if (ev->write) { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http upstream process non buffered downstream"); - c->log->action = "sending to client"; + ngx_connection_t *c; - } else { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http upstream process non buffered upstream"); - c->log->action = "reading upstream"; - } + c = u->peer.connection; - if (ev->timedout) { - if (ev->write) { - c->timedout = 1; - ngx_connection_error(c, NGX_ETIMEDOUT, "client timed out"); + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http upstream process non buffered upstream"); - } else { - ngx_connection_error(c, NGX_ETIMEDOUT, "upstream timed out"); - } + 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, 0); 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; - clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); - - do_write = ev->write || u->length == 0; + do_write = do_write || u->length == 0; for ( ;; ) { @@ -1694,10 +2370,6 @@ if (u->out_bufs || u->busy_bufs) { rc = ngx_http_output_filter(r, u->out_bufs); - if (downstream->destroyed) { - return; - } - if (rc == NGX_ERROR) { ngx_http_upstream_finalize_request(r, u, 0); return; @@ -1737,6 +2409,8 @@ } if (n > 0) { + u->state->response_length += n; + if (u->input_filter(u->input_filter_ctx, n) == NGX_ERROR) { ngx_http_upstream_finalize_request(r, u, 0); return; @@ -1751,9 +2425,11 @@ 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_ERROR) + != NGX_OK) { ngx_http_upstream_finalize_request(r, u, 0); return; @@ -1767,7 +2443,7 @@ ngx_del_timer(downstream->write); } - if (ngx_handle_read_event(upstream->read, 0) == NGX_ERROR) { + if (ngx_handle_read_event(upstream->read, 0) != NGX_OK) { ngx_http_upstream_finalize_request(r, u, 0); return; } @@ -1818,6 +2494,7 @@ cl->buf->pos = b->last; b->last += bytes; cl->buf->last = b->last; + cl->buf->tag = u->output.tag; if (u->length == NGX_MAX_SIZE_T_VALUE) { return NGX_OK; @@ -1832,119 +2509,137 @@ static void ngx_http_upstream_process_downstream(ngx_http_request_t *r) { - ngx_http_upstream_process_body(r->connection->write); -} - - -static void -ngx_http_upstream_process_body(ngx_event_t *ev) -{ + ngx_event_t *wev; + ngx_connection_t *c; ngx_event_pipe_t *p; - ngx_connection_t *c, *downstream; - ngx_http_log_ctx_t *ctx; - ngx_http_request_t *r; ngx_http_upstream_t *u; - c = ev->data; - r = c->data; + c = r->connection; u = r->upstream; - downstream = r->connection; - - if (ev->write) { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http upstream process downstream"); - c->log->action = "sending to client"; - - } else { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http upstream process upstream"); - c->log->action = "reading upstream"; + p = u->pipe; + wev = c->write; - ctx = c->log->data; - ctx->current_request = r; - } + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http upstream process downstream"); - p = u->pipe; + c->log->action = "sending to client"; - if (ev->timedout) { - if (ev->write) { - if (ev->delayed) { - - ev->timedout = 0; - ev->delayed = 0; - - if (!ev->ready) { - ngx_add_timer(ev, p->send_timeout); - - if (ngx_handle_write_event(ev, p->send_lowat) == NGX_ERROR) - { - ngx_http_upstream_finalize_request(r, u, 0); - return; - } + if (wev->timedout) { - return; - } + if (wev->delayed) { - if (ngx_event_pipe(p, ev->write) == NGX_ABORT) { + wev->timedout = 0; + wev->delayed = 0; - if (downstream->destroyed) { - return; - } + if (!wev->ready) { + ngx_add_timer(wev, p->send_timeout); + if (ngx_handle_write_event(wev, p->send_lowat) != NGX_OK) { ngx_http_upstream_finalize_request(r, u, 0); - return; } - } else { - p->downstream_error = 1; - c->timedout = 1; - ngx_connection_error(c, NGX_ETIMEDOUT, "client timed out"); + return; + } + + if (ngx_event_pipe(p, wev->write) == NGX_ABORT) { + ngx_http_upstream_finalize_request(r, u, 0); + return; } } else { - p->upstream_error = 1; - ngx_connection_error(c, NGX_ETIMEDOUT, "upstream timed out"); + p->downstream_error = 1; + c->timedout = 1; + ngx_connection_error(c, NGX_ETIMEDOUT, "client timed out"); } } else { - if (ev->write && ev->delayed) { + + if (wev->delayed) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http downstream delayed"); - if (ngx_handle_write_event(ev, p->send_lowat) == NGX_ERROR) { - return; - } + if (ngx_handle_write_event(wev, p->send_lowat) != NGX_OK) { + ngx_http_upstream_finalize_request(r, u, 0); + } + + return; + } + + if (ngx_event_pipe(p, 1) == NGX_ABORT) { + ngx_http_upstream_finalize_request(r, u, 0); + return; + } + } + + ngx_http_upstream_process_request(r); +} + + +static void +ngx_http_upstream_process_upstream(ngx_http_request_t *r, + ngx_http_upstream_t *u) +{ + ngx_connection_t *c; + + c = u->peer.connection; - return; - } + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http upstream process upstream"); - if (ngx_event_pipe(p, ev->write) == NGX_ABORT) { + c->log->action = "reading upstream"; - if (downstream->destroyed) { - return; - } + if (c->read->timedout) { + u->pipe->upstream_error = 1; + ngx_connection_error(c, NGX_ETIMEDOUT, "upstream timed out"); + } else { + if (ngx_event_pipe(u->pipe, 0) == NGX_ABORT) { ngx_http_upstream_finalize_request(r, u, 0); return; } } + ngx_http_upstream_process_request(r); +} + + +static void +ngx_http_upstream_process_request(ngx_http_request_t *r) +{ + ngx_uint_t del; + ngx_temp_file_t *tf; + ngx_event_pipe_t *p; + ngx_http_upstream_t *u; + + u = r->upstream; + p = u->pipe; + if (u->peer.connection) { if (u->store) { - if (p->upstream_eof && u->headers_in.status_n == NGX_HTTP_OK) { + del = p->upstream_error; - ngx_http_upstream_store(r, u); + tf = u->pipe->temp_file; - } else if ((p->upstream_error - || (p->upstream_eof - && u->headers_in.status_n != NGX_HTTP_OK)) - && u->pipe->temp_file->file.fd != NGX_INVALID_FILE) - { - if (ngx_delete_file(u->pipe->temp_file->file.name.data) - == NGX_FILE_ERROR) + if (p->upstream_eof || p->upstream_done) { + + if (u->headers_in.status_n == NGX_HTTP_OK + && (u->headers_in.content_length_n == -1 + || (u->headers_in.content_length_n == tf->offset))) { + ngx_http_upstream_store(r, u); + + } else { + del = 1; + } + } + + if (del && tf->file.fd != NGX_INVALID_FILE) { + + if (ngx_delete_file(tf->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); @@ -1952,30 +2647,28 @@ } } -#if (NGX_HTTP_FILE_CACHE) +#if (NGX_HTTP_CACHE) + + if (u->cacheable) { - if (p->upstream_done && u->cachable) { - if (ngx_http_cache_update(r) == NGX_ERROR) { - ngx_http_busy_unlock(u->conf->busy_lock, &u->busy_lock); - ngx_http_upstream_finalize_request(r, u, 0); - return; - } + if (p->upstream_done) { + ngx_http_file_cache_update(r, u->pipe->temp_file); - } else if (p->upstream_eof && u->cachable) { + } else if (p->upstream_eof) { - /* TODO: check length & update cache */ + /* TODO: check length & update cache */ - if (ngx_http_cache_update(r) == NGX_ERROR) { - ngx_http_busy_unlock(u->conf->busy_lock, &u->busy_lock); - ngx_http_upstream_finalize_request(r, u, 0); - return; + ngx_http_file_cache_update(r, u->pipe->temp_file); + + } else if (p->upstream_error) { + ngx_http_file_cache_free(r->cache, u->pipe->temp_file); } } #endif if (p->upstream_done || p->upstream_eof || p->upstream_error) { - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http upstream exit: %p", p->out); #if 0 ngx_http_busy_unlock(u->conf->busy_lock, &u->busy_lock); @@ -1986,10 +2679,10 @@ } if (p->downstream_error) { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http upstream downstream error"); - if (!u->cachable && u->peer.connection) { + if (!u->cacheable && !u->store && u->peer.connection) { ngx_http_upstream_finalize_request(r, u, 0); } } @@ -1999,15 +2692,15 @@ static void ngx_http_upstream_store(ngx_http_request_t *r, ngx_http_upstream_t *u) { - char *failed; - u_char *name; - size_t root; - time_t lm; - ngx_err_t err; - ngx_str_t *temp, path, *last_modified; - ngx_temp_file_t *tf; + size_t root; + time_t lm; + ngx_str_t path; + ngx_temp_file_t *tf; + ngx_ext_rename_file_t ext; - if (u->pipe->temp_file->file.fd == NGX_INVALID_FILE) { + tf = u->pipe->temp_file; + + if (tf->file.fd == NGX_INVALID_FILE) { /* create file for empty 200 response */ @@ -2032,38 +2725,21 @@ u->pipe->temp_file = tf; } - temp = &u->pipe->temp_file->file.name; - -#if !(NGX_WIN32) - - if (ngx_change_file_access(temp->data, u->conf->store_access) - == NGX_FILE_ERROR) - { - err = ngx_errno; - failed = ngx_change_file_access_n; - name = temp->data; - - goto failed; - } - -#endif - - if (r->upstream->headers_in.last_modified) { + 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; - last_modified = &r->upstream->headers_in.last_modified->value; + if (u->headers_in.last_modified) { - lm = ngx_http_parse_time(last_modified->data, last_modified->len); + lm = ngx_http_parse_time(u->headers_in.last_modified->value.data, + u->headers_in.last_modified->value.len); if (lm != NGX_ERROR) { - if (ngx_set_file_time(temp->data, u->pipe->temp_file->file.fd, lm) - != NGX_OK) - { - err = ngx_errno; - failed = ngx_set_file_time_n; - name = temp->data; - - goto failed; - } + ext.time = lm; + ext.fd = tf->file.fd; } } @@ -2080,63 +2756,20 @@ } } - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "upstream stores \"%s\" to \"%s\"", temp->data, path.data); - - failed = ngx_rename_file_n; - name = path.data; - - if (ngx_rename_file(temp->data, path.data) != NGX_FILE_ERROR) { - return; - } - - err = ngx_errno; - - if (err == NGX_ENOENT) { - - err = ngx_create_full_path(path.data, - ngx_dir_access(u->conf->store_access)); - if (err == 0) { - if (ngx_rename_file(temp->data, path.data) != NGX_FILE_ERROR) { - return; - } - - err = ngx_errno; - } - } - -#if (NGX_WIN32) - - if (err == NGX_EEXIST) { - if (ngx_win32_rename_file(temp, &path, r->pool) != NGX_ERROR) { - - if (ngx_rename_file(temp->data, path.data) != NGX_FILE_ERROR) { - return; - } - } - - err = ngx_errno; - } - -#endif - -failed: + path.len--; - 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_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "upstream stores \"%s\" to \"%s\"", + tf->file.name.data, path.data); - ngx_log_error(NGX_LOG_CRIT, r->connection->log, err, - "%s \"%s\" failed", failed, name); + (void) ngx_ext_rename_file(&tf->file.name, &path, &ext); } static void -ngx_http_upstream_dummy_handler(ngx_event_t *wev) +ngx_http_upstream_dummy_handler(ngx_http_request_t *r, ngx_http_upstream_t *u) { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, wev->log, 0, + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http upstream dummy handler"); } @@ -2160,7 +2793,9 @@ state = NGX_PEER_FAILED; } - u->peer.free(&u->peer, u->peer.data, state); + if (ft_type != NGX_HTTP_UPSTREAM_FT_NOLIVE) { + u->peer.free(&u->peer, u->peer.data, state); + } if (ft_type == NGX_HTTP_UPSTREAM_FT_TIMEOUT) { ngx_log_error(NGX_LOG_ERR, r->connection->log, NGX_ETIMEDOUT, @@ -2208,12 +2843,21 @@ #if (NGX_HTTP_CACHE) - if (u->stale && (u->conf->use_stale & ft_type)) { - ngx_http_upstream_finalize_request(r, u, - ngx_http_send_cached_response(r)); + if (u->cache_status == NGX_HTTP_CACHE_EXPIRED + && (u->conf->cache_use_stale & ft_type)) + { + ngx_int_t rc; + + rc = u->reinit_request(r); + + if (rc == NGX_OK) { + u->cache_status = NGX_HTTP_CACHE_STALE; + rc = ngx_http_upstream_cache_send(r, u); + } + + ngx_http_upstream_finalize_request(r, u, rc); return; } - #endif ngx_http_upstream_finalize_request(r, u, status); @@ -2254,10 +2898,19 @@ { ngx_http_request_t *r = data; + ngx_http_upstream_t *u; + 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); + u = r->upstream; + + if (u->resolved && u->resolved->ctx) { + ngx_resolve_name_done(u->resolved->ctx); + u->resolved->ctx = NULL; + } + + ngx_http_upstream_finalize_request(r, u, NGX_DONE); } @@ -2270,17 +2923,31 @@ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "finalize http upstream request: %i", rc); - *u->cleanup = NULL; + if (u->cleanup) { + *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->response_sec) { + if (u->state && u->state->response_sec) { tp = ngx_timeofday(); u->state->response_sec = tp->sec - u->state->response_sec; u->state->response_msec = tp->msec - u->state->response_msec; + + if (u->pipe) { + u->state->response_length = u->pipe->read_length; + } } u->finalize_request(r, rc); - u->peer.free(&u->peer, u->peer.data, 0); + if (u->peer.free) { + u->peer.free(&u->peer, u->peer.data, 0); + } if (u->peer.connection) { @@ -2311,25 +2978,38 @@ u->peer.connection = NULL; - if (u->header_sent && (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE)) - { - rc = 0; - } - 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 0 - if (u->cache) { - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http upstream cache fd: %d", - u->cache->ctx.file.fd); +#if (NGX_HTTP_CACHE) + + if (u->cacheable && r->cache) { + time_t valid; + + if (rc == NGX_HTTP_BAD_GATEWAY || rc == NGX_HTTP_GATEWAY_TIME_OUT) { + + 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 + if (u->header_sent + && (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE)) + { + rc = 0; + } + if (rc == NGX_DECLINED) { return; } @@ -2337,16 +3017,7 @@ r->connection->log->action = "sending to client"; if (rc == 0) { - if (r == r->main) { - if (!r->post_action) { - rc = ngx_http_send_special(r, NGX_HTTP_LAST); - } - - } else { - if (r->out) { - rc = NGX_AGAIN; - } - } + rc = ngx_http_send_special(r, NGX_HTTP_LAST); } ngx_http_finalize_request(r, rc); @@ -2365,41 +3036,216 @@ *ph = h; } - return NGX_OK; -} + 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_set_cookie(ngx_http_request_t *r, ngx_table_elt_t *h, + ngx_uint_t offset) +{ +#if (NGX_HTTP_CACHE) + ngx_http_upstream_t *u; + + u = r->upstream; + + 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, *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) { + return NGX_OK; + } + + p = h->value.data; + last = p + h->value.len; + + if (ngx_strlcasestrn(p, last, (u_char *) "no-cache", 8 - 1) != NULL + || ngx_strlcasestrn(p, last, (u_char *) "no-store", 8 - 1) != NULL + || ngx_strlcasestrn(p, last, (u_char *) "private", 7 - 1) != NULL) + { + u->cacheable = 0; + return NGX_OK; + } + + p = ngx_strlcasestrn(p, last, (u_char *) "max-age=", 8 - 1); + + if (p == NULL) { + return NGX_OK; + } + + n = 0; + + for (p += 8; 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; + } +#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_http_parse_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; + } -static ngx_int_t -ngx_http_upstream_process_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 **ph; + len = h->value.len; + p = h->value.data; - pa = (ngx_array_t *) ((char *) &r->upstream->headers_in + offset); + if (p[0] != '@') { + n = ngx_atoi(p, len); - if (pa->elts == NULL) { - if (ngx_array_init(pa, r->pool, 2, sizeof(ngx_table_elt_t *)) != NGX_OK) - { - return NGX_ERROR; - } - } + switch (n) { + case 0: + u->cacheable = 0; + case NGX_ERROR: + return NGX_OK; - ph = ngx_array_push(pa); - if (ph == NULL) { - return NGX_ERROR; + default: + r->cache->valid_sec = ngx_time() + n; + return NGX_OK; + } } - *ph = h; + p++; + len--; - return NGX_OK; -} + n = ngx_atoi(p, len); + if (n != NGX_ERROR) { + r->cache->valid_sec = n; + } + } +#endif -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; } @@ -2526,6 +3372,7 @@ 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++) { @@ -2537,6 +3384,10 @@ while (*++p == ' ') { /* void */ } + if (*p == '\0') { + return NGX_OK; + } + if (ngx_strncasecmp(p, (u_char *) "charset=", 8) != 0) { continue; } @@ -2545,7 +3396,17 @@ r->headers_out.content_type_len = last - h->value.data; - r->headers_out.charset.len = h->value.data + h->value.len - p; + 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; @@ -2576,6 +3437,34 @@ 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; + +#if (NGX_HTTP_CACHE) + + if (r->upstream->cacheable) { + r->headers_out.last_modified_time = ngx_http_parse_time(h->value.data, + h->value.len); + } + +#endif + + 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) { @@ -2606,6 +3495,10 @@ return rc; } + if (ho->value.data[0] != '/') { + r->headers_out.location = ho; + } + /* * we do not set r->headers_out.location here to avoid the handling * the local redirects without a host name by ngx_http_header_filter() @@ -2645,16 +3538,47 @@ return NGX_OK; } -#if (NGX_DEBUG) 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); } -#endif return rc; } + r->headers_out.refresh = ho; + + 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 (NGX_HTTP_CACHE) + + if (r->cached) { + r->allow_ranges = 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; } @@ -2711,7 +3635,7 @@ ngx_http_upstream_state_t *state; v->valid = 1; - v->no_cachable = 0; + v->no_cacheable = 0; v->not_found = 0; if (r->upstream_states == NULL || r->upstream_states->nelts == 0) { @@ -2731,7 +3655,7 @@ } } - p = ngx_palloc(r->pool, len); + p = ngx_pnalloc(r->pool, len); if (p == NULL) { return NGX_ERROR; } @@ -2782,7 +3706,7 @@ ngx_http_upstream_state_t *state; v->valid = 1; - v->no_cachable = 0; + v->no_cacheable = 0; v->not_found = 0; if (r->upstream_states == NULL || r->upstream_states->nelts == 0) { @@ -2792,7 +3716,7 @@ len = r->upstream_states->nelts * (3 + 2); - p = ngx_palloc(r->pool, len); + p = ngx_pnalloc(r->pool, len); if (p == NULL) { return NGX_ERROR; } @@ -2848,7 +3772,7 @@ ngx_http_upstream_state_t *state; v->valid = 1; - v->no_cachable = 0; + v->no_cacheable = 0; v->not_found = 0; if (r->upstream_states == NULL || r->upstream_states->nelts == 0) { @@ -2858,7 +3782,7 @@ len = r->upstream_states->nelts * (NGX_TIME_T_LEN + 4 + 2); - p = ngx_palloc(r->pool, len); + p = ngx_pnalloc(r->pool, len); if (p == NULL) { return NGX_ERROR; } @@ -2870,8 +3794,9 @@ for ( ;; ) { if (state[i].status) { - ms = state[i].response_sec * 1000 + state[i].response_msec; - ms = (ms >= 0) ? ms : 0; + ms = (ngx_msec_int_t) + (state[i].response_sec * 1000 + state[i].response_msec); + ms = ngx_max(ms, 0); p = ngx_sprintf(p, "%d.%03d", ms / 1000, ms % 1000); } else { @@ -2905,6 +3830,66 @@ } +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 ( ;; ) { + 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; +} + + ngx_int_t ngx_http_upstream_header_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) @@ -2920,6 +3905,33 @@ } +#if (NGX_HTTP_CACHE) + +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; +} + +#endif + + static char * ngx_http_upstream(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy) { @@ -3062,7 +4074,7 @@ u.url = value[1]; u.default_port = 80; - if (ngx_parse_url(cf, &u) != NGX_OK) { + 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); @@ -3118,10 +4130,21 @@ fail_timeout = ngx_parse_time(&s, 1); - if (fail_timeout < 0) { + if (fail_timeout == NGX_ERROR) { + goto invalid; + } + + continue; + } + + if (ngx_strncmp(value[i].data, "backup", 6) == 0) { + + if (!(uscf->flags & NGX_HTTP_UPSTREAM_BACKUP)) { goto invalid; } + us->backup = 1; + continue; } @@ -3166,7 +4189,7 @@ if (!(flags & NGX_HTTP_UPSTREAM_CREATE)) { - if (ngx_parse_url(cf, u) != NGX_OK) { + 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); @@ -3266,6 +4289,153 @@ } +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_addr_t **paddr; + + paddr = (ngx_addr_t **) (p + cmd->offset); + + *paddr = ngx_palloc(cf->pool, sizeof(ngx_addr_t)); + if (*paddr == NULL) { + return NGX_CONF_ERROR; + } + + value = cf->args->elts; + + rc = ngx_parse_addr(cf->pool, *paddr, value[1].data, value[1].len); + + switch (rc) { + case NGX_OK: + (*paddr)->name = value[1]; + return NGX_CONF_OK; + + case NGX_DECLINED: + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid address \"%V\"", &value[1]); + default: + return NGX_CONF_ERROR; + } +} + + +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_hash = prev->hide_headers_hash; + + if (conf->hide_headers_hash.buckets +#if (NGX_HTTP_CACHE) + && ((conf->cache == NULL) == (prev->cache == NULL)) +#endif + ) + { + return NGX_OK; + } + + conf->hide_headers = prev->hide_headers; + conf->pass_headers = prev->pass_headers; + + } 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; + + return ngx_hash_init(hash, hide_headers.elts, hide_headers.nelts); +} + + static void * ngx_http_upstream_create_main_conf(ngx_conf_t *cf) { @@ -3280,7 +4450,7 @@ sizeof(ngx_http_upstream_srv_conf_t *)) != NGX_OK) { - return NGX_CONF_ERROR; + return NULL; } return umcf; diff -Nru nginx-0.5.33/src/http/ngx_http_upstream.h nginx-0.8.53/src/http/ngx_http_upstream.h --- nginx-0.5.33/src/http/ngx_http_upstream.h 2007-09-23 19:29:22.000000000 +0000 +++ nginx-0.8.53/src/http/ngx_http_upstream.h 2010-07-19 09:36:04.000000000 +0000 @@ -20,31 +20,48 @@ #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_503 0x00000020 -#define NGX_HTTP_UPSTREAM_FT_HTTP_404 0x00000040 -#define NGX_HTTP_UPSTREAM_FT_BUSY_LOCK 0x00000080 -#define NGX_HTTP_UPSTREAM_FT_MAX_WAITING 0x00000100 +#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_404 0x00000100 +#define NGX_HTTP_UPSTREAM_FT_UPDATING 0x00000200 +#define NGX_HTTP_UPSTREAM_FT_BUSY_LOCK 0x00000400 +#define NGX_HTTP_UPSTREAM_FT_MAX_WAITING 0x00000800 +#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_404) #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 + + typedef struct { - ngx_msec_t bl_time; - ngx_uint_t bl_state; + ngx_msec_t bl_time; + ngx_uint_t bl_state; - ngx_uint_t status; - time_t response_sec; - ngx_uint_t response_msec; + ngx_uint_t status; + time_t response_sec; + ngx_uint_t response_msec; + off_t response_length; - ngx_str_t *peer; + ngx_str_t *peer; } ngx_http_upstream_state_t; typedef struct { - ngx_hash_t headers_in_hash; - ngx_array_t upstreams; + ngx_hash_t headers_in_hash; + ngx_array_t upstreams; /* ngx_http_upstream_srv_conf_t */ } ngx_http_upstream_main_conf_t; @@ -57,21 +74,21 @@ typedef struct { - ngx_http_upstream_init_pt init_upstream; - ngx_http_upstream_init_peer_pt init; - void *data; + ngx_http_upstream_init_pt init_upstream; + ngx_http_upstream_init_peer_pt init; + void *data; } ngx_http_upstream_peer_t; typedef struct { - ngx_peer_addr_t *addrs; - ngx_uint_t naddrs; - ngx_uint_t weight; - ngx_uint_t max_fails; - time_t fail_timeout; + ngx_addr_t *addrs; + ngx_uint_t naddrs; + ngx_uint_t weight; + ngx_uint_t max_fails; + time_t fail_timeout; - unsigned down:1; - unsigned backup:1; + unsigned down:1; + unsigned backup:1; } ngx_http_upstream_server_t; @@ -84,188 +101,245 @@ struct ngx_http_upstream_srv_conf_s { - ngx_http_upstream_peer_t peer; - void **srv_conf; + ngx_http_upstream_peer_t peer; + void **srv_conf; - ngx_array_t *servers; /* ngx_http_upstream_server_t */ + 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; - in_port_t default_port; + ngx_uint_t flags; + ngx_str_t host; + u_char *file_name; + ngx_uint_t line; + in_port_t port; + in_port_t default_port; }; typedef struct { - ngx_http_upstream_srv_conf_t *upstream; + 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 timeout; + + size_t send_lowat; + size_t buffer_size; - ngx_msec_t connect_timeout; - ngx_msec_t send_timeout; - ngx_msec_t read_timeout; - ngx_msec_t timeout; + size_t busy_buffers_size; + size_t max_temp_file_size; + size_t temp_file_write_size; - size_t send_lowat; - size_t buffer_size; + size_t busy_buffers_size_conf; + size_t max_temp_file_size_conf; + size_t temp_file_write_size_conf; - size_t busy_buffers_size; - size_t max_temp_file_size; - size_t temp_file_write_size; + ngx_bufs_t bufs; - size_t busy_buffers_size_conf; - size_t max_temp_file_size_conf; - size_t temp_file_write_size_conf; + ngx_uint_t ignore_headers; + ngx_uint_t next_upstream; + ngx_uint_t store_access; + ngx_flag_t buffering; + ngx_flag_t pass_request_headers; + ngx_flag_t pass_request_body; - ngx_uint_t next_upstream; - ngx_uint_t store_access; + ngx_flag_t ignore_client_abort; + ngx_flag_t intercept_errors; + ngx_flag_t cyclic_temp_file; - ngx_bufs_t bufs; + ngx_path_t *temp_path; - ngx_flag_t buffering; - ngx_flag_t pass_request_headers; - ngx_flag_t pass_request_body; + ngx_hash_t hide_headers_hash; + ngx_array_t *hide_headers; + ngx_array_t *pass_headers; - ngx_flag_t ignore_client_abort; - ngx_flag_t intercept_errors; - ngx_flag_t cyclic_temp_file; + ngx_addr_t *local; - ngx_path_t *temp_path; +#if (NGX_HTTP_CACHE) + ngx_shm_zone_t *cache; - ngx_hash_t hide_headers_hash; - ngx_array_t *hide_headers; - ngx_array_t *pass_headers; + ngx_uint_t cache_min_uses; + ngx_uint_t cache_use_stale; + ngx_uint_t cache_methods; - ngx_str_t schema; - ngx_str_t uri; - ngx_str_t location; - ngx_str_t url; /* used in proxy_rewrite_location */ + ngx_array_t *cache_valid; + ngx_array_t *cache_bypass; + ngx_array_t *no_cache; +#endif - ngx_array_t *store_lengths; - ngx_array_t *store_values; + ngx_array_t *store_lengths; + ngx_array_t *store_values; - signed store:2; - unsigned intercept_404:1; - unsigned change_buffering:1; + signed store:2; + unsigned intercept_404:1; + unsigned change_buffering:1; #if (NGX_HTTP_SSL) - ngx_ssl_t *ssl; + ngx_ssl_t *ssl; + ngx_flag_t ssl_session_reuse; #endif } 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_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 headers; - ngx_uint_t status_n; - ngx_str_t status_line; + 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 *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; #if (NGX_HTTP_GZIP) - ngx_table_elt_t *content_encoding; + ngx_table_elt_t *content_encoding; #endif - ngx_array_t cache_control; + off_t content_length_n; + + ngx_array_t cache_control; } ngx_http_upstream_headers_in_t; -struct ngx_http_upstream_s { - ngx_peer_connection_t peer; +typedef struct { + ngx_str_t host; + in_port_t port; + ngx_uint_t no_port; /* unsigned no_port:1 */ - ngx_event_pipe_t *pipe; + ngx_uint_t naddrs; + in_addr_t *addrs; - ngx_chain_t *request_bufs; + struct sockaddr *sockaddr; + socklen_t socklen; - ngx_output_chain_ctx_t output; - ngx_chain_writer_ctx_t writer; + ngx_resolver_ctx_t *ctx; +} ngx_http_upstream_resolved_t; - ngx_http_upstream_conf_t *conf; - ngx_http_upstream_headers_in_t headers_in; +typedef void (*ngx_http_upstream_handler_pt)(ngx_http_request_t *r, + ngx_http_upstream_t *u); - ngx_buf_t buffer; - size_t length; - ngx_chain_t *out_bufs; - ngx_chain_t *busy_bufs; - ngx_chain_t *free_bufs; +struct ngx_http_upstream_s { + ngx_http_upstream_handler_pt read_event_handler; + ngx_http_upstream_handler_pt write_event_handler; - ngx_int_t (*input_filter_init)(void *data); - ngx_int_t (*input_filter)(void *data, ssize_t bytes); - void *input_filter_ctx; + ngx_peer_connection_t peer; - 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_event_pipe_t *pipe; - ngx_msec_t timeout; + ngx_chain_t *request_bufs; - ngx_str_t method; + ngx_output_chain_ctx_t output; + ngx_chain_writer_ctx_t writer; - ngx_http_upstream_state_t *state; + ngx_http_upstream_conf_t *conf; - ngx_str_t uri; + ngx_http_upstream_headers_in_t headers_in; - ngx_http_cleanup_pt *cleanup; + ngx_http_upstream_resolved_t *resolved; - unsigned store:1; - unsigned cachable:1; - unsigned accel:1; + ngx_buf_t buffer; + size_t length; - unsigned buffering:1; + ngx_chain_t *out_bufs; + ngx_chain_t *busy_bufs; + ngx_chain_t *free_bufs; - unsigned request_sent:1; - unsigned header_sent:1; + 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_msec_t timeout; + + ngx_http_upstream_state_t *state; + + ngx_str_t method; + ngx_str_t schema; + ngx_str_t uri; + + 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 request_sent:1; + unsigned header_sent:1; }; +typedef struct { + ngx_uint_t status; + ngx_uint_t mask; +} ngx_http_upstream_next_t; + + ngx_int_t ngx_http_upstream_header_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); +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); +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_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_ */ diff -Nru nginx-0.5.33/src/http/ngx_http_upstream_round_robin.c nginx-0.8.53/src/http/ngx_http_upstream_round_robin.c --- nginx-0.5.33/src/http/ngx_http_upstream_round_robin.c 2007-09-23 19:29:22.000000000 +0000 +++ nginx-0.8.53/src/http/ngx_http_upstream_round_robin.c 2008-12-23 19:35:12.000000000 +0000 @@ -9,6 +9,12 @@ #include +static ngx_int_t ngx_http_upstream_cmp_servers(const void *one, + const void *two); +static ngx_uint_t +ngx_http_upstream_get_peer(ngx_http_upstream_rr_peers_t *peers); + + ngx_int_t ngx_http_upstream_init_round_robin(ngx_conf_t *cf, ngx_http_upstream_srv_conf_t *us) @@ -16,15 +22,20 @@ ngx_url_t u; ngx_uint_t i, j, n; ngx_http_upstream_server_t *server; - ngx_http_upstream_rr_peers_t *peers; + ngx_http_upstream_rr_peers_t *peers, *backup; us->peer.init = ngx_http_upstream_init_round_robin_peer; if (us->servers) { - n = 0; server = us->servers->elts; + n = 0; + for (i = 0; i < us->servers->nelts; i++) { + if (server[i].backup) { + continue; + } + n += server[i].naddrs; } @@ -34,6 +45,7 @@ return NGX_ERROR; } + peers->single = (n == 1); peers->number = n; peers->name = &us->host; @@ -41,20 +53,81 @@ for (i = 0; i < us->servers->nelts; i++) { for (j = 0; j < server[i].naddrs; j++) { + if (server[i].backup) { + continue; + } + peers->peer[n].sockaddr = server[i].addrs[j].sockaddr; peers->peer[n].socklen = server[i].addrs[j].socklen; peers->peer[n].name = server[i].addrs[j].name; - peers->peer[n].weight = server[i].weight; - peers->peer[n].current_weight = server[i].weight; peers->peer[n].max_fails = server[i].max_fails; peers->peer[n].fail_timeout = server[i].fail_timeout; peers->peer[n].down = server[i].down; + peers->peer[n].weight = server[i].down ? 0 : server[i].weight; + peers->peer[n].current_weight = peers->peer[n].weight; n++; } } us->peer.data = peers; + ngx_sort(&peers->peer[0], (size_t) n, + sizeof(ngx_http_upstream_rr_peer_t), + ngx_http_upstream_cmp_servers); + + /* backup servers */ + + n = 0; + + for (i = 0; i < us->servers->nelts; i++) { + if (!server[i].backup) { + continue; + } + + n += server[i].naddrs; + } + + if (n == 0) { + return NGX_OK; + } + + backup = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_rr_peers_t) + + sizeof(ngx_http_upstream_rr_peer_t) * (n - 1)); + if (backup == NULL) { + return NGX_ERROR; + } + + peers->single = 0; + backup->single = 0; + backup->number = n; + backup->name = &us->host; + + n = 0; + + for (i = 0; i < us->servers->nelts; i++) { + for (j = 0; j < server[i].naddrs; j++) { + if (!server[i].backup) { + continue; + } + + backup->peer[n].sockaddr = server[i].addrs[j].sockaddr; + backup->peer[n].socklen = server[i].addrs[j].socklen; + backup->peer[n].name = server[i].addrs[j].name; + backup->peer[n].weight = server[i].weight; + backup->peer[n].current_weight = server[i].weight; + backup->peer[n].max_fails = server[i].max_fails; + backup->peer[n].fail_timeout = server[i].fail_timeout; + backup->peer[n].down = server[i].down; + n++; + } + } + + peers->next = backup; + + ngx_sort(&backup->peer[0], (size_t) n, + sizeof(ngx_http_upstream_rr_peer_t), + ngx_http_upstream_cmp_servers); + return NGX_OK; } @@ -73,7 +146,7 @@ u.host = us->host; u.port = (in_port_t) (us->port ? us->port : us->default_port); - if (ngx_inet_resolve_host(cf, &u) != NGX_OK) { + 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", @@ -91,28 +164,40 @@ return NGX_ERROR; } + peers->single = (n == 1); peers->number = n; peers->name = &us->host; - n = 0; - for (i = 0; i < u.naddrs; i++) { - peers->peer[n].sockaddr = u.addrs[i].sockaddr; - peers->peer[n].socklen = u.addrs[i].socklen; - peers->peer[n].name = u.addrs[i].name; - peers->peer[n].weight = 1; - peers->peer[n].current_weight = 1; - peers->peer[n].max_fails = 1; - peers->peer[n].fail_timeout = 10; - n++; + peers->peer[i].sockaddr = u.addrs[i].sockaddr; + peers->peer[i].socklen = u.addrs[i].socklen; + peers->peer[i].name = u.addrs[i].name; + peers->peer[i].weight = 1; + peers->peer[i].current_weight = 1; + peers->peer[i].max_fails = 1; + peers->peer[i].fail_timeout = 10; } us->peer.data = peers; + /* implicitly defined upstream has no backup servers */ + return NGX_OK; } +static ngx_int_t +ngx_http_upstream_cmp_servers(const void *one, const void *two) +{ + ngx_http_upstream_rr_peer_t *first, *second; + + first = (ngx_http_upstream_rr_peer_t *) one; + second = (ngx_http_upstream_rr_peer_t *) two; + + return (first->weight < second->weight); +} + + ngx_int_t ngx_http_upstream_init_round_robin_peer(ngx_http_request_t *r, ngx_http_upstream_srv_conf_t *us) @@ -163,15 +248,123 @@ 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; + ngx_uint_t i, n; + struct sockaddr_in *sin; + 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) + + sizeof(ngx_http_upstream_rr_peer_t) * (ur->naddrs - 1)); + if (peers == NULL) { + return NGX_ERROR; + } + + peers->single = (ur->naddrs == 1); + peers->number = ur->naddrs; + peers->name = &ur->host; + + if (ur->sockaddr) { + peers->peer[0].sockaddr = ur->sockaddr; + peers->peer[0].socklen = ur->socklen; + peers->peer[0].name = ur->host; + peers->peer[0].weight = 1; + peers->peer[0].current_weight = 1; + peers->peer[0].max_fails = 1; + peers->peer[0].fail_timeout = 10; + + } else { + + for (i = 0; i < ur->naddrs; i++) { + + len = NGX_INET_ADDRSTRLEN + sizeof(":65536") - 1; + + p = ngx_pnalloc(r->pool, len); + if (p == NULL) { + return NGX_ERROR; + } + + len = ngx_inet_ntop(AF_INET, &ur->addrs[i], p, NGX_INET_ADDRSTRLEN); + len = ngx_sprintf(&p[len], ":%d", ur->port) - p; + + sin = ngx_pcalloc(r->pool, sizeof(struct sockaddr_in)); + if (sin == NULL) { + return NGX_ERROR; + } + + sin->sin_family = AF_INET; + sin->sin_port = htons(ur->port); + sin->sin_addr.s_addr = ur->addrs[i]; + + peers->peer[i].sockaddr = (struct sockaddr *) sin; + peers->peer[i].socklen = sizeof(struct sockaddr_in); + peers->peer[i].name.len = len; + peers->peer[i].name.data = p; + peers->peer[i].weight = 1; + peers->peer[i].current_weight = 1; + peers->peer[i].max_fails = 1; + peers->peer[i].fail_timeout = 10; + } + } + + rrp->peers = peers; + rrp->current = 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 = rrp->peers->number; +#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_get_round_robin_peer(ngx_peer_connection_t *pc, void *data) { ngx_http_upstream_rr_peer_data_t *rrp = data; - time_t now; - uintptr_t m; - ngx_uint_t i, n; - ngx_connection_t *c; - ngx_http_upstream_rr_peer_t *peer; + time_t now; + uintptr_t m; + ngx_int_t rc; + ngx_uint_t i, n; + ngx_connection_t *c; + 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); @@ -203,7 +396,7 @@ pc->cached = 0; pc->connection = NULL; - if (rrp->peers->number == 1) { + if (rrp->peers->single) { peer = &rrp->peers->peer[0]; } else { @@ -214,8 +407,15 @@ /* it's a first try - get a current peer */ + i = pc->tries; + for ( ;; ) { - rrp->current = rrp->peers->current; + rrp->current = ngx_http_upstream_get_peer(rrp->peers); + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0, + "get rr peer, current: %ui %i", + rrp->current, + rrp->peers->peer[rrp->current].current_weight); n = rrp->current / (8 * sizeof(uintptr_t)); m = (uintptr_t) 1 << rrp->current % (8 * sizeof(uintptr_t)); @@ -236,6 +436,8 @@ break; } + peer->current_weight = 0; + } else { rrp->tried[n] |= m; } @@ -243,32 +445,24 @@ pc->tries--; } - rrp->peers->current++; - - if (rrp->peers->current >= rrp->peers->number) { - rrp->peers->current = 0; + if (pc->tries == 0) { + goto failed; } - if (pc->tries) { - continue; + if (--i == 0) { + ngx_log_error(NGX_LOG_ALERT, pc->log, 0, + "round robin upstream stuck on %ui tries", + pc->tries); + goto failed; } - - goto failed; } peer->current_weight--; - if (peer->current_weight == 0) { - peer->current_weight = peer->weight; - - rrp->peers->current++; + } else { - if (rrp->peers->current >= rrp->peers->number) { - rrp->peers->current = 0; - } - } + i = pc->tries; - } else { for ( ;; ) { n = rrp->current / (8 * sizeof(uintptr_t)); m = (uintptr_t) 1 << rrp->current % (8 * sizeof(uintptr_t)); @@ -290,6 +484,8 @@ break; } + peer->current_weight = 0; + } else { rrp->tried[n] |= m; } @@ -303,26 +499,19 @@ rrp->current = 0; } - if (pc->tries) { - continue; + if (pc->tries == 0) { + goto failed; } - goto failed; + if (--i == 0) { + ngx_log_error(NGX_LOG_ALERT, pc->log, 0, + "round robin upstream stuck on %ui tries", + pc->tries); + goto failed; + } } peer->current_weight--; - - if (peer->current_weight == 0) { - peer->current_weight = peer->weight; - - if (rrp->current == rrp->peers->current) { - rrp->peers->current++; - - if (rrp->peers->current >= rrp->peers->number) { - rrp->peers->current = 0; - } - } - } } rrp->tried[n] |= m; @@ -334,24 +523,107 @@ /* ngx_unlock_mutex(rrp->peers->mutex); */ + if (pc->tries == 1 && rrp->peers->next) { + pc->tries += rrp->peers->next->number; + + n = rrp->peers->next->number / (8 * sizeof(uintptr_t)) + 1; + for (i = 0; i < n; i++) { + rrp->tried[i] = 0; + } + } + return NGX_OK; failed: + peers = rrp->peers; + + if (peers->next) { + + /* ngx_unlock_mutex(peers->mutex); */ + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0, "backup servers"); + + rrp->peers = peers->next; + pc->tries = rrp->peers->number; + + n = rrp->peers->number / (8 * sizeof(uintptr_t)) + 1; + for (i = 0; i < n; i++) { + rrp->tried[i] = 0; + } + + rc = ngx_http_upstream_get_round_robin_peer(pc, rrp); + + if (rc != NGX_BUSY) { + return rc; + } + + /* ngx_lock_mutex(peers->mutex); */ + } + /* all peers failed, mark them as live for quick recovery */ - for (i = 0; i < rrp->peers->number; i++) { - rrp->peers->peer[i].fails = 0; + for (i = 0; i < peers->number; i++) { + peers->peer[i].fails = 0; } - /* ngx_unlock_mutex(rrp->peers->mutex); */ + /* ngx_unlock_mutex(peers->mutex); */ - pc->name = rrp->peers->name; + pc->name = peers->name; return NGX_BUSY; } +static ngx_uint_t +ngx_http_upstream_get_peer(ngx_http_upstream_rr_peers_t *peers) +{ + ngx_uint_t i, n; + ngx_http_upstream_rr_peer_t *peer; + + peer = &peers->peer[0]; + + for ( ;; ) { + + for (i = 0; i < peers->number; i++) { + + if (peer[i].current_weight <= 0) { + continue; + } + + n = i; + + while (i < peers->number - 1) { + + i++; + + if (peer[i].current_weight <= 0) { + continue; + } + + if (peer[n].current_weight * 1000 / peer[i].current_weight + > peer[n].weight * 1000 / peer[i].weight) + { + return n; + } + + n = i; + } + + if (peer[i].current_weight > 0) { + n = i; + } + + return n; + } + + for (i = 0; i < peers->number; i++) { + peer[i].current_weight = peer[i].weight; + } + } +} + + void ngx_http_upstream_free_round_robin_peer(ngx_peer_connection_t *pc, void *data, ngx_uint_t state) @@ -370,7 +642,7 @@ /* TODO: NGX_PEER_KEEPALIVE */ - if (rrp->peers->number == 1) { + if (rrp->peers->single) { pc->tries = 0; return; } @@ -385,8 +657,16 @@ peer->fails++; peer->accessed = now; - if (peer->current_weight > 1) { - peer->current_weight /= 2; + if (peer->max_fails) { + peer->current_weight -= peer->weight / peer->max_fails; + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0, + "free rr peer failed: %ui %i", + rrp->current, peer->current_weight); + + if (peer->current_weight < 0) { + peer->current_weight = 0; } /* ngx_unlock_mutex(rrp->peers->mutex); */ diff -Nru nginx-0.5.33/src/http/ngx_http_upstream_round_robin.h nginx-0.8.53/src/http/ngx_http_upstream_round_robin.h --- nginx-0.5.33/src/http/ngx_http_upstream_round_robin.h 2007-07-29 17:25:06.000000000 +0000 +++ nginx-0.8.53/src/http/ngx_http_upstream_round_robin.h 2009-11-02 12:41:56.000000000 +0000 @@ -18,8 +18,8 @@ socklen_t socklen; ngx_str_t name; - ngx_uint_t current_weight; - ngx_uint_t weight; + ngx_int_t current_weight; + ngx_int_t weight; ngx_uint_t fails; time_t accessed; @@ -29,15 +29,16 @@ ngx_uint_t down; /* unsigned down:1; */ -#if (NGX_SSL) +#if (NGX_HTTP_SSL) ngx_ssl_session_t *ssl_session; /* local to a process */ #endif } ngx_http_upstream_rr_peer_t; -typedef struct { - ngx_uint_t current; +typedef struct ngx_http_upstream_rr_peers_s ngx_http_upstream_rr_peers_t; +struct ngx_http_upstream_rr_peers_s { + ngx_uint_t single; /* unsigned single:1; */ ngx_uint_t number; ngx_uint_t last_cached; @@ -46,8 +47,10 @@ ngx_str_t *name; + ngx_http_upstream_rr_peers_t *next; + ngx_http_upstream_rr_peer_t peer[1]; -} ngx_http_upstream_rr_peers_t; +}; typedef struct { @@ -62,6 +65,8 @@ 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, @@ -76,5 +81,4 @@ #endif - #endif /* _NGX_HTTP_UPSTREAM_ROUND_ROBIN_H_INCLUDED_ */ diff -Nru nginx-0.5.33/src/http/ngx_http_variables.c nginx-0.8.53/src/http/ngx_http_variables.c --- nginx-0.5.33/src/http/ngx_http_variables.c 2007-07-29 18:55:59.000000000 +0000 +++ nginx-0.8.53/src/http/ngx_http_variables.c 2010-06-23 15:31:33.000000000 +0000 @@ -6,7 +6,6 @@ #include #include -#include #include #include @@ -15,6 +14,8 @@ ngx_http_variable_value_t *v, uintptr_t data); static void ngx_http_variable_request_set(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); +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, @@ -26,6 +27,12 @@ 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_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); static ngx_int_t ngx_http_variable_host(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); @@ -45,8 +52,12 @@ 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, @@ -55,6 +66,8 @@ 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); @@ -62,6 +75,8 @@ 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, @@ -73,6 +88,10 @@ 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); /* * TODO: @@ -143,40 +162,42 @@ { ngx_string("uri"), NULL, ngx_http_variable_request, offsetof(ngx_http_request_t, uri), - NGX_HTTP_VAR_NOCACHABLE, 0 }, + NGX_HTTP_VAR_NOCACHEABLE, 0 }, { ngx_string("document_uri"), NULL, ngx_http_variable_request, offsetof(ngx_http_request_t, uri), - NGX_HTTP_VAR_NOCACHABLE, 0 }, + NGX_HTTP_VAR_NOCACHEABLE, 0 }, - { ngx_string("request"), NULL, ngx_http_variable_request, - offsetof(ngx_http_request_t, request_line), 0, 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_NOCACHABLE, 0 }, + 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_NOCACHABLE, 0 }, + NGX_HTTP_VAR_NOCACHEABLE, 0 }, { ngx_string("args"), ngx_http_variable_request_set, ngx_http_variable_request, offsetof(ngx_http_request_t, args), - NGX_HTTP_VAR_CHANGABLE|NGX_HTTP_VAR_NOCACHABLE, 0 }, + NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE, 0 }, { ngx_string("is_args"), NULL, ngx_http_variable_is_args, - 0, NGX_HTTP_VAR_NOCACHABLE, 0 }, + 0, NGX_HTTP_VAR_NOCACHEABLE, 0 }, { ngx_string("request_filename"), NULL, ngx_http_variable_request_filename, 0, - NGX_HTTP_VAR_NOCACHABLE, 0 }, + NGX_HTTP_VAR_NOCACHEABLE, 0 }, - { ngx_string("server_name"), NULL, ngx_http_variable_request, - offsetof(ngx_http_request_t, server_name), 0, 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, 0, 0 }, + ngx_http_variable_request_method, 0, + NGX_HTTP_VAR_NOCACHEABLE, 0 }, { ngx_string("remote_user"), NULL, ngx_http_variable_remote_user, 0, 0, 0 }, @@ -187,6 +208,10 @@ 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 }, @@ -197,6 +222,9 @@ { 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 }, @@ -213,13 +241,19 @@ offsetof(ngx_http_request_t, headers_out.cache_control), 0, 0 }, { ngx_string("limit_rate"), ngx_http_variable_request_set_size, - ngx_http_variable_request, + ngx_http_variable_request_get_size, offsetof(ngx_http_request_t, limit_rate), - NGX_HTTP_VAR_CHANGABLE|NGX_HTTP_VAR_NOCACHABLE, 0 }, + NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE, 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_null_string, NULL, NULL, 0, 0, 0 } }; @@ -251,7 +285,7 @@ v = key[i].value; - if (!(v->flags & NGX_HTTP_VAR_CHANGABLE)) { + if (!(v->flags & NGX_HTTP_VAR_CHANGEABLE)) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "the duplicate \"%V\" variable", name); return NULL; @@ -266,14 +300,12 @@ } v->name.len = name->len; - v->name.data = ngx_palloc(cf->pool, name->len); + v->name.data = ngx_pnalloc(cf->pool, name->len); if (v->name.data == NULL) { return NULL; } - for (i = 0; i < name->len; i++) { - v->name.data[i] = ngx_tolower(name->data[i]); - } + ngx_strlow(v->name.data, name->data, name->len); v->set_handler = NULL; v->get_handler = NULL; @@ -310,7 +342,8 @@ if (v == NULL) { if (ngx_array_init(&cmcf->variables, cf->pool, 4, - sizeof(ngx_http_variable_t)) == NGX_ERROR) + sizeof(ngx_http_variable_t)) + != NGX_OK) { return NGX_ERROR; } @@ -333,14 +366,12 @@ } v->name.len = name->len; - v->name.data = ngx_palloc(cf->pool, name->len); + v->name.data = ngx_pnalloc(cf->pool, name->len); if (v->name.data == NULL) { return NGX_ERROR; } - for (i = 0; i < name->len; i++) { - v->name.data[i] = ngx_tolower(name->data[i]); - } + ngx_strlow(v->name.data, name->data, name->len); v->set_handler = NULL; v->get_handler = NULL; @@ -375,8 +406,8 @@ if (v[index].get_handler(r, &r->variables[index], v[index].data) == NGX_OK) { - if (v[index].flags & NGX_HTTP_VAR_NOCACHABLE) { - r->variables[index].no_cachable = 1; + if (v[index].flags & NGX_HTTP_VAR_NOCACHEABLE) { + r->variables[index].no_cacheable = 1; } return &r->variables[index]; @@ -397,7 +428,7 @@ v = &r->variables[index]; if (v->valid) { - if (!v->no_cachable) { + if (!v->no_cacheable) { return v; } @@ -410,8 +441,7 @@ ngx_http_variable_value_t * -ngx_http_get_variable(ngx_http_request_t *r, ngx_str_t *name, ngx_uint_t key, - ngx_uint_t nowarn) +ngx_http_get_variable(ngx_http_request_t *r, ngx_str_t *name, ngx_uint_t key) { ngx_http_variable_t *v; ngx_http_variable_value_t *vv; @@ -423,7 +453,7 @@ if (v) { if (v->flags & NGX_HTTP_VAR_INDEXED) { - return ngx_http_get_indexed_variable(r, v->index); + return ngx_http_get_flushed_variable(r, v->index); } else { @@ -464,7 +494,7 @@ return NULL; } - if (ngx_strncmp(name->data, "upstream_http_", 10) == 0) { + if (ngx_strncmp(name->data, "upstream_http_", 14) == 0) { if (ngx_http_upstream_header_variable(r, vv, (uintptr_t) name) == NGX_OK) @@ -475,13 +505,26 @@ return NULL; } - vv->not_found = 1; + if (ngx_strncmp(name->data, "cookie_", 7) == 0) { - if (nowarn == 0) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, - "unknown \"%V\" variable", name); + if (ngx_http_variable_cookie(r, vv, (uintptr_t) name) == NGX_OK) { + return vv; + } + + return NULL; + } + + if (ngx_strncmp(name->data, "arg_", 4) == 0) { + + if (ngx_http_variable_argument(r, vv, (uintptr_t) name) == NGX_OK) { + return vv; + } + + return NULL; } + vv->not_found = 1; + return vv; } @@ -497,7 +540,7 @@ if (s->data) { v->len = s->len; v->valid = 1; - v->no_cachable = 0; + v->no_cacheable = 0; v->not_found = 0; v->data = s->data; @@ -522,6 +565,28 @@ } +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) @@ -559,7 +624,7 @@ if (h) { v->len = h->value.len; v->valid = 1; - v->no_cachable = 0; + v->no_cacheable = 0; v->not_found = 0; v->data = h->value.data; @@ -591,7 +656,7 @@ } v->valid = 1; - v->no_cachable = 0; + v->no_cacheable = 0; v->not_found = 0; h = a->elts; @@ -609,7 +674,7 @@ len += h[i]->value.len + sizeof("; ") - 1; } - p = ngx_palloc(r->pool, len); + p = ngx_pnalloc(r->pool, len); if (p == NULL) { return NGX_ERROR; } @@ -691,7 +756,7 @@ if (n + prefix == var->len && n == header[i].key.len) { v->len = header[i].value.len; v->valid = 1; - v->no_cachable = 0; + v->no_cacheable = 0; v->not_found = 0; v->data = header[i].value.data; @@ -706,52 +771,157 @@ static ngx_int_t -ngx_http_variable_host(ngx_http_request_t *r, ngx_http_variable_value_t *v, - uintptr_t data) +ngx_http_variable_request_line(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) { - if (r->host_start == NULL) { + u_char *p, *s; - if (r->headers_in.host) { - v->len = r->headers_in.host_name_len; - v->data = r->headers_in.host->value.data; + s = r->request_line.data; - } else { - v->len = r->server_name.len; - v->data = r->server_name.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; + } } - } else if (r->host_end) { - v->len = r->host_end - r->host_start; - v->data = r->host_start; + r->request_line.len = p - s; + r->request_line.data = s; + } - } else { + 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_cachable = 0; + v->no_cacheable = 0; v->not_found = 0; + v->data = cookie.data; 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) +ngx_http_variable_argument(ngx_http_request_t *r, ngx_http_variable_value_t *v, + uintptr_t data) { - struct sockaddr_in *sin; + ngx_str_t *name = (ngx_str_t *) data; - /* AF_INET only */ + u_char *arg; + size_t len; + ngx_str_t value; - sin = (struct sockaddr_in *) r->connection->sockaddr; + 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->len = sizeof(in_addr_t); + v->data = value.data; + v->len = value.len; v->valid = 1; - v->no_cachable = 0; + v->no_cacheable = 0; v->not_found = 0; - v->data = (u_char *) &sin->sin_addr.s_addr; + + 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 + + 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; } @@ -763,7 +933,7 @@ { v->len = r->connection->addr_text.len; v->valid = 1; - v->no_cachable = 0; + v->no_cacheable = 0; v->not_found = 0; v->data = r->connection->addr_text.data; @@ -775,29 +945,39 @@ ngx_http_variable_remote_port(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) { - ngx_uint_t port; - struct sockaddr_in *sin; + ngx_uint_t port; + struct sockaddr_in *sin; +#if (NGX_HAVE_INET6) + struct sockaddr_in6 *sin6; +#endif v->len = 0; v->valid = 1; - v->no_cachable = 0; + v->no_cacheable = 0; v->not_found = 0; - v->data = ngx_palloc(r->pool, sizeof("65535") - 1); + v->data = ngx_pnalloc(r->pool, sizeof("65535") - 1); if (v->data == NULL) { return NGX_ERROR; } - /* AF_INET only */ + switch (r->connection->sockaddr->sa_family) { - if (r->connection->sockaddr->sa_family == AF_INET) { - sin = (struct sockaddr_in *) r->connection->sockaddr; +#if (NGX_HAVE_INET6) + case AF_INET6: + sin6 = (struct sockaddr_in6 *) r->connection->sockaddr; + port = ntohs(sin6->sin6_port); + break; +#endif + default: /* AF_INET */ + sin = (struct sockaddr_in *) r->connection->sockaddr; port = ntohs(sin->sin_port); + break; + } - if (port > 0 && port < 65536) { - v->len = ngx_sprintf(v->data, "%ui", port) - v->data; - } + if (port > 0 && port < 65536) { + v->len = ngx_sprintf(v->data, "%ui", port) - v->data; } return NGX_OK; @@ -808,32 +988,28 @@ ngx_http_variable_server_addr(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) { - socklen_t len; - ngx_connection_t *c; - struct sockaddr_in sin; + ngx_str_t s; + u_char addr[NGX_SOCKADDR_STRLEN]; - v->data = ngx_palloc(r->pool, INET_ADDRSTRLEN); - if (v->data == NULL) { + s.len = NGX_SOCKADDR_STRLEN; + s.data = addr; + + if (ngx_connection_local_sockaddr(r->connection, &s, 0) != NGX_OK) { return NGX_ERROR; } - c = r->connection; - - if (r->in_addr == 0) { - len = sizeof(struct sockaddr_in); - if (getsockname(c->fd, (struct sockaddr *) &sin, &len) == -1) { - ngx_connection_error(c, ngx_socket_errno, "getsockname() failed"); - return NGX_ERROR; - } - - r->in_addr = sin.sin_addr.s_addr; + s.data = ngx_pnalloc(r->pool, s.len); + if (s.data == NULL) { + return NGX_ERROR; } - v->len = ngx_inet_ntop(c->listening->family, &r->in_addr, - v->data, INET_ADDRSTRLEN); + ngx_memcpy(s.data, addr, s.len); + + v->len = s.len; v->valid = 1; - v->no_cachable = 0; + v->no_cacheable = 0; v->not_found = 0; + v->data = s.data; return NGX_OK; } @@ -843,11 +1019,44 @@ ngx_http_variable_server_port(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) { - v->len = r->port_text->len - 1; + ngx_uint_t port; + struct sockaddr_in *sin; +#if (NGX_HAVE_INET6) + struct sockaddr_in6 *sin6; +#endif + + v->len = 0; v->valid = 1; - v->no_cachable = 0; + v->no_cacheable = 0; v->not_found = 0; - v->data = r->port_text->data + 1; + + 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; + } + + switch (r->connection->local_sockaddr->sa_family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + sin6 = (struct sockaddr_in6 *) r->connection->local_sockaddr; + port = ntohs(sin6->sin6_port); + break; +#endif + + default: /* AF_INET */ + sin = (struct sockaddr_in *) r->connection->local_sockaddr; + port = ntohs(sin->sin_port); + break; + } + + if (port > 0 && port < 65536) { + v->len = ngx_sprintf(v->data, "%ui", port) - v->data; + } return NGX_OK; } @@ -862,7 +1071,7 @@ if (r->connection->ssl) { v->len = sizeof("https") - 1; v->valid = 1; - v->no_cachable = 0; + v->no_cacheable = 0; v->not_found = 0; v->data = (u_char *) "https"; @@ -873,7 +1082,7 @@ v->len = sizeof("http") - 1; v->valid = 1; - v->no_cachable = 0; + v->no_cacheable = 0; v->not_found = 0; v->data = (u_char *) "http"; @@ -886,7 +1095,7 @@ ngx_http_variable_value_t *v, uintptr_t data) { v->valid = 1; - v->no_cachable = 0; + v->no_cacheable = 0; v->not_found = 0; if (r->args.len == 0) { @@ -914,7 +1123,7 @@ if (clcf->root_lengths == NULL) { v->len = clcf->root.len; v->valid = 1; - v->no_cachable = 0; + v->no_cacheable = 0; v->not_found = 0; v->data = clcf->root.data; @@ -926,13 +1135,13 @@ return NGX_ERROR; } - if (ngx_conf_full_name((ngx_cycle_t *) ngx_cycle, &path) == NGX_ERROR) { + if (ngx_conf_full_name((ngx_cycle_t *) ngx_cycle, &path, 0) != NGX_OK) { return NGX_ERROR; } v->len = path.len; v->valid = 1; - v->no_cachable = 0; + v->no_cacheable = 0; v->not_found = 0; v->data = path.data; } @@ -942,6 +1151,59 @@ static ngx_int_t +ngx_http_variable_realpath_root(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + size_t len; + ngx_str_t path; + ngx_http_core_loc_conf_t *clcf; + u_char real[NGX_MAX_PATH]; + + 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_conf_full_name((ngx_cycle_t *) ngx_cycle, &path, 0) != NGX_OK) { + return NGX_ERROR; + } + } + + if (ngx_realpath(path.data, 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) { + return NGX_ERROR; + } + + v->len = len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + + ngx_memcpy(v->data, real, len); + + 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) { @@ -956,7 +1218,7 @@ v->len = path.len - 1; v->valid = 1; - v->no_cachable = 0; + v->no_cacheable = 0; v->not_found = 0; v->data = path.data; @@ -965,13 +1227,31 @@ 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_cachable = 0; + v->no_cacheable = 0; v->not_found = 0; v->data = r->main->method_name.data; @@ -1002,7 +1282,7 @@ v->len = r->headers_in.user.len; v->valid = 1; - v->no_cachable = 0; + v->no_cacheable = 0; v->not_found = 0; v->data = r->headers_in.user.data; @@ -1023,14 +1303,14 @@ sent = 0; } - p = ngx_palloc(r->pool, NGX_OFF_T_LEN); + 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_cachable = 0; + v->no_cacheable = 0; v->not_found = 0; v->data = p; @@ -1045,7 +1325,7 @@ if (r->headers_out.content_type.len) { v->len = r->headers_out.content_type.len; v->valid = 1; - v->no_cachable = 0; + v->no_cacheable = 0; v->not_found = 0; v->data = r->headers_out.content_type.data; @@ -1066,7 +1346,7 @@ if (r->headers_out.content_length) { v->len = r->headers_out.content_length->value.len; v->valid = 1; - v->no_cachable = 0; + v->no_cacheable = 0; v->not_found = 0; v->data = r->headers_out.content_length->value.data; @@ -1074,14 +1354,14 @@ } if (r->headers_out.content_length_n >= 0) { - p = ngx_palloc(r->pool, NGX_OFF_T_LEN); + 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_cachable = 0; + v->no_cacheable = 0; v->not_found = 0; v->data = p; @@ -1095,6 +1375,30 @@ 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) { @@ -1103,7 +1407,7 @@ if (r->headers_out.last_modified) { v->len = r->headers_out.last_modified->value.len; v->valid = 1; - v->no_cachable = 0; + v->no_cacheable = 0; v->not_found = 0; v->data = r->headers_out.last_modified->value.data; @@ -1111,7 +1415,7 @@ } if (r->headers_out.last_modified_time >= 0) { - p = ngx_palloc(r->pool, + p = ngx_pnalloc(r->pool, sizeof("Last-Modified: Mon, 28 Sep 1970 06:00:00 GMT") - 1); if (p == NULL) { return NGX_ERROR; @@ -1119,7 +1423,7 @@ v->len = ngx_http_time(p, r->headers_out.last_modified_time) - p; v->valid = 1; - v->no_cachable = 0; + v->no_cacheable = 0; v->not_found = 0; v->data = p; @@ -1150,7 +1454,7 @@ v->len = len; v->valid = 1; - v->no_cachable = 0; + v->no_cacheable = 0; v->not_found = 0; v->data = (u_char *) p; @@ -1170,14 +1474,14 @@ if (clcf->keepalive_header) { - p = ngx_palloc(r->pool, sizeof("timeout=") - 1 + NGX_TIME_T_LEN); + 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_cachable = 0; + v->no_cacheable = 0; v->not_found = 0; v->data = p; @@ -1198,7 +1502,7 @@ if (r->chunked) { v->len = sizeof("chunked") - 1; v->valid = 1; - v->no_cachable = 0; + v->no_cacheable = 0; v->not_found = 0; v->data = (u_char *) "chunked"; @@ -1217,7 +1521,7 @@ if (r->request_complete) { v->len = 2; v->valid = 1; - v->no_cachable = 0; + v->no_cacheable = 0; v->not_found = 0; v->data = (u_char *) "OK"; @@ -1226,7 +1530,7 @@ v->len = 0; v->valid = 1; - v->no_cachable = 0; + v->no_cacheable = 0; v->not_found = 0; v->data = (u_char *) ""; @@ -1235,6 +1539,59 @@ 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, *next; + 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; + } + + next = cl->next->buf; + len = (buf->last - buf->pos) + (next->last - next->pos); + + p = ngx_pnalloc(r->pool, len); + if (p == NULL) { + return NGX_ERROR; + } + + v->data = p; + + p = ngx_cpymem(p, buf->pos, buf->last - buf->pos); + ngx_memcpy(p, next->pos, next->last - next->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) { @@ -1246,7 +1603,7 @@ v->len = r->request_body->temp_file->file.name.len; v->valid = 1; - v->no_cachable = 0; + v->no_cacheable = 0; v->not_found = 0; v->data = r->request_body->temp_file->file.name.data; @@ -1260,7 +1617,7 @@ { v->len = sizeof(NGINX_VERSION) - 1; v->valid = 1; - v->no_cachable = 0; + v->no_cacheable = 0; v->not_found = 0; v->data = (u_char *) NGINX_VERSION; @@ -1268,6 +1625,196 @@ } +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; +} + + +#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; + + 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; + re->name = rc->pattern; + + 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_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http regex set $%V to \"%*s\"", + &v[index].name, vv->len, vv->data); + } +#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) { @@ -1368,7 +1915,22 @@ if (ngx_strncmp(v[i].name.data, "upstream_http_", 14) == 0) { v[i].get_handler = ngx_http_upstream_header_variable; v[i].data = (uintptr_t) &v[i].name; - v[i].flags = NGX_HTTP_VAR_NOCACHABLE; + v[i].flags = NGX_HTTP_VAR_NOCACHEABLE; + + continue; + } + + if (ngx_strncmp(v[i].name.data, "cookie_", 7) == 0) { + v[i].get_handler = ngx_http_variable_cookie; + v[i].data = (uintptr_t) &v[i].name; + + continue; + } + + if (ngx_strncmp(v[i].name.data, "arg_", 4) == 0) { + v[i].get_handler = ngx_http_variable_argument; + v[i].data = (uintptr_t) &v[i].name; + v[i].flags = NGX_HTTP_VAR_NOCACHEABLE; continue; } diff -Nru nginx-0.5.33/src/http/ngx_http_variables.h nginx-0.8.53/src/http/ngx_http_variables.h --- nginx-0.5.33/src/http/ngx_http_variables.h 2007-07-22 08:47:45.000000000 +0000 +++ nginx-0.8.53/src/http/ngx_http_variables.h 2010-06-23 15:31:33.000000000 +0000 @@ -10,13 +10,12 @@ #include #include -#include #include typedef ngx_variable_value_t ngx_http_variable_value_t; -#define ngx_http_variable(v) { sizeof(v) - 1, 1, 0, 0, (u_char *) v } +#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; @@ -26,10 +25,10 @@ ngx_http_variable_value_t *v, uintptr_t data); -#define NGX_HTTP_VAR_CHANGABLE 1 -#define NGX_HTTP_VAR_NOCACHABLE 2 -#define NGX_HTTP_VAR_INDEXED 4 -#define NGX_HTTP_VAR_NOHASH 8 +#define NGX_HTTP_VAR_CHANGEABLE 1 +#define NGX_HTTP_VAR_NOCACHEABLE 2 +#define NGX_HTTP_VAR_INDEXED 4 +#define NGX_HTTP_VAR_NOHASH 8 struct ngx_http_variable_s { @@ -51,7 +50,7 @@ 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_uint_t nowarn); + 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); @@ -60,6 +59,31 @@ #define ngx_http_clear_variable(r, index) r->variables0[index].text.data = NULL; +#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; + + +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 + + 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); diff -Nru nginx-0.5.33/src/http/ngx_http_write_filter_module.c nginx-0.8.53/src/http/ngx_http_write_filter_module.c --- nginx-0.5.33/src/http/ngx_http_write_filter_module.c 2007-09-22 19:23:34.000000000 +0000 +++ nginx-0.8.53/src/http/ngx_http_write_filter_module.c 2009-06-02 14:01:50.000000000 +0000 @@ -6,7 +6,6 @@ #include #include -#include #include @@ -47,8 +46,9 @@ ngx_int_t ngx_http_write_filter(ngx_http_request_t *r, ngx_chain_t *in) { - off_t size, sent, limit; + off_t size, sent, nsent, limit; ngx_uint_t last, flush; + ngx_msec_t delay; ngx_chain_t *cl, *ln, **ll, *chain; ngx_connection_t *c; ngx_http_core_loc_conf_t *clcf; @@ -210,7 +210,8 @@ } if (r->limit_rate) { - limit = r->limit_rate * (ngx_time() - r->start_sec + 1) - c->sent; + limit = r->limit_rate * (ngx_time() - r->start_sec + 1) + - (c->sent - clcf->limit_rate_after); if (limit <= 0) { c->write->delayed = 1; @@ -245,14 +246,33 @@ } if (r->limit_rate) { - sent = c->sent - sent; - c->write->delayed = 1; - ngx_add_timer(c->write, (ngx_msec_t) (sent * 1000 / r->limit_rate + 1)); + + nsent = c->sent; + + if (clcf->limit_rate_after) { + + sent -= clcf->limit_rate_after; + if (sent < 0) { + sent = 0; + } + + nsent -= clcf->limit_rate_after; + if (nsent < 0) { + nsent = 0; + } + } + + delay = (ngx_msec_t) ((nsent - sent) * 1000 / r->limit_rate + 1); + + if (delay > 0) { + c->write->delayed = 1; + ngx_add_timer(c->write, delay); + } } else if (c->write->ready && clcf->sendfile_max_chunk && (size_t) (c->sent - sent) - >= clcf->sendfile_max_chunk - 2 * ngx_pagesize) + >= clcf->sendfile_max_chunk - 2 * ngx_pagesize) { c->write->delayed = 1; ngx_add_timer(c->write, 1); diff -Nru nginx-0.5.33/src/mail/ngx_mail_auth_http_module.c nginx-0.8.53/src/mail/ngx_mail_auth_http_module.c --- nginx-0.5.33/src/mail/ngx_mail_auth_http_module.c 2007-11-07 14:24:55.000000000 +0000 +++ nginx-0.8.53/src/mail/ngx_mail_auth_http_module.c 2010-05-14 09:56:37.000000000 +0000 @@ -12,7 +12,7 @@ typedef struct { - ngx_peer_addr_t *peer; + ngx_addr_t *peer; ngx_msec_t timeout; @@ -40,7 +40,6 @@ ngx_mail_auth_http_handler_pt handler; ngx_uint_t state; - ngx_uint_t hash; /* no needed ? */ u_char *header_name_start; u_char *header_name_end; @@ -140,8 +139,10 @@ static ngx_str_t ngx_mail_auth_http_method[] = { ngx_string("plain"), ngx_string("plain"), + ngx_string("plain"), ngx_string("apop"), - ngx_string("cram-md5") + ngx_string("cram-md5"), + ngx_string("none") }; static ngx_str_t ngx_mail_smtp_errcode = ngx_string("535 5.7.0"); @@ -268,7 +269,7 @@ ngx_del_timer(wev); } - if (ngx_handle_write_event(wev, 0) == NGX_ERROR) { + if (ngx_handle_write_event(wev, 0) != NGX_OK) { ngx_close_connection(c); ngx_destroy_pool(ctx->pool); ngx_mail_session_internal_server_error(s); @@ -456,7 +457,7 @@ time_t timer; size_t len, size; ngx_int_t rc, port, n; - ngx_peer_addr_t *peer; + ngx_addr_t *peer; struct sockaddr_in *sin; ngx_log_debug0(NGX_LOG_DEBUG_MAIL, s->connection->log, 0, @@ -528,7 +529,7 @@ continue; } - p = ngx_pcalloc(s->connection->pool, size); + p = ngx_pnalloc(s->connection->pool, size); if (p == NULL) { ngx_close_connection(ctx->peer.connection); ngx_destroy_pool(ctx->pool); @@ -593,7 +594,7 @@ { s->login.len = ctx->header_end - ctx->header_start; - s->login.data = ngx_palloc(s->connection->pool, s->login.len); + s->login.data = ngx_pnalloc(s->connection->pool, s->login.len); if (s->login.data == NULL) { ngx_close_connection(ctx->peer.connection); ngx_destroy_pool(ctx->pool); @@ -614,7 +615,8 @@ { s->passwd.len = ctx->header_end - ctx->header_start; - s->passwd.data = ngx_palloc(s->connection->pool, s->passwd.len); + s->passwd.data = ngx_pnalloc(s->connection->pool, + s->passwd.len); if (s->passwd.data == NULL) { ngx_close_connection(ctx->peer.connection); ngx_destroy_pool(ctx->pool); @@ -651,8 +653,8 @@ { ctx->errcode.len = ctx->header_end - ctx->header_start; - ctx->errcode.data = ngx_palloc(s->connection->pool, - ctx->errcode.len); + ctx->errcode.data = ngx_pnalloc(s->connection->pool, + ctx->errcode.len); if (ctx->errcode.data == NULL) { ngx_close_connection(ctx->peer.connection); ngx_destroy_pool(ctx->pool); @@ -691,7 +693,7 @@ ctx->err.len = ctx->errcode.len + ctx->errmsg.len + sizeof(" " CRLF) - 1; - p = ngx_palloc(s->connection->pool, ctx->err.len); + p = ngx_pnalloc(s->connection->pool, ctx->err.len); if (p == NULL) { ngx_close_connection(ctx->peer.connection); ngx_destroy_pool(ctx->pool); @@ -718,7 +720,7 @@ return; } - ngx_add_timer(s->connection->read, timer * 1000); + ngx_add_timer(s->connection->read, (ngx_msec_t) (timer * 1000)); s->connection->read->handler = ngx_mail_auth_sleep_handler; @@ -735,7 +737,7 @@ return; } - ngx_add_timer(s->connection->read, timer * 1000); + ngx_add_timer(s->connection->read, (ngx_msec_t) (timer * 1000)); s->connection->read->handler = ngx_mail_auth_sleep_handler; @@ -762,13 +764,15 @@ return; } - peer = ngx_pcalloc(s->connection->pool, sizeof(ngx_peer_addr_t)); + peer = ngx_pcalloc(s->connection->pool, sizeof(ngx_addr_t)); if (peer == NULL) { ngx_destroy_pool(ctx->pool); ngx_mail_session_internal_server_error(s); return; } + /* AF_INET only */ + sin = ngx_pcalloc(s->connection->pool, sizeof(struct sockaddr_in)); if (sin == NULL) { ngx_destroy_pool(ctx->pool); @@ -791,8 +795,7 @@ sin->sin_port = htons((in_port_t) port); - ctx->addr.data[ctx->addr.len] = '\0'; - sin->sin_addr.s_addr = inet_addr((char *) ctx->addr.data); + sin->sin_addr.s_addr = ngx_inet_addr(ctx->addr.data, ctx->addr.len); if (sin->sin_addr.s_addr == INADDR_NONE) { ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, "auth http server %V sent invalid server " @@ -810,7 +813,7 @@ peer->name.len = len; - peer->name.data = ngx_palloc(s->connection->pool, len); + peer->name.data = ngx_pnalloc(s->connection->pool, len); if (peer->name.data == NULL) { ngx_destroy_pool(ctx->pool); ngx_mail_session_internal_server_error(s); @@ -893,7 +896,7 @@ return; } - if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { + if (ngx_handle_read_event(rev, 0) != NGX_OK) { ngx_mail_close_connection(c); } @@ -901,7 +904,7 @@ } if (rev->active) { - if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { + if (ngx_handle_read_event(rev, 0) != NGX_OK) { ngx_mail_close_connection(c); } } @@ -913,7 +916,6 @@ ngx_mail_auth_http_ctx_t *ctx) { u_char c, ch, *p; - ngx_uint_t hash; enum { sw_start = 0, sw_name, @@ -925,7 +927,6 @@ } state; state = ctx->state; - hash = ctx->hash; for (p = ctx->response->pos; p < ctx->response->last; p++) { ch = *p; @@ -949,12 +950,10 @@ c = (u_char) (ch | 0x20); if (c >= 'a' && c <= 'z') { - hash = c; break; } if (ch >= '0' && ch <= '9') { - hash = ch; break; } @@ -966,7 +965,6 @@ case sw_name: c = (u_char) (ch | 0x20); if (c >= 'a' && c <= 'z') { - hash += c; break; } @@ -977,12 +975,10 @@ } if (ch == '-') { - hash += ch; break; } if (ch >= '0' && ch <= '9') { - hash += ch; break; } @@ -1079,7 +1075,6 @@ ctx->response->pos = p; ctx->state = state; - ctx->hash = hash; return NGX_AGAIN; @@ -1087,7 +1082,6 @@ ctx->response->pos = p + 1; ctx->state = sw_start; - ctx->hash = hash; return NGX_OK; @@ -1110,7 +1104,7 @@ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, "mail auth http block read"); - if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { + if (ngx_handle_read_event(rev, 0) != NGX_OK) { c = rev->data; s = c->data; @@ -1164,6 +1158,10 @@ + sizeof(CRLF) - 1 + sizeof("Client-IP: ") - 1 + s->connection->addr_text.len + sizeof(CRLF) - 1 + + sizeof("Client-Host: ") - 1 + s->host.len + sizeof(CRLF) - 1 + + sizeof("Auth-SMTP-Helo: ") - 1 + s->smtp_helo.len + + sizeof("Auth-SMTP-From: ") - 1 + s->smtp_from.len + + sizeof("Auth-SMTP-To: ") - 1 + s->smtp_to.len + ahcf->header.len + sizeof(CRLF) - 1; @@ -1215,9 +1213,37 @@ b->last = ngx_cpymem(b->last, "Client-IP: ", sizeof("Client-IP: ") - 1); b->last = ngx_copy(b->last, s->connection->addr_text.data, - s->connection->addr_text.len); + s->connection->addr_text.len); *b->last++ = CR; *b->last++ = LF; + if (s->host.len) { + b->last = ngx_cpymem(b->last, "Client-Host: ", + sizeof("Client-Host: ") - 1); + b->last = ngx_copy(b->last, s->host.data, s->host.len); + *b->last++ = CR; *b->last++ = LF; + } + + if (s->auth_method == NGX_MAIL_AUTH_NONE) { + + /* HELO, MAIL FROM, and RCPT TO can't contain CRLF, no need to escape */ + + b->last = ngx_cpymem(b->last, "Auth-SMTP-Helo: ", + sizeof("Auth-SMTP-Helo: ") - 1); + b->last = ngx_copy(b->last, s->smtp_helo.data, s->smtp_helo.len); + *b->last++ = CR; *b->last++ = LF; + + b->last = ngx_cpymem(b->last, "Auth-SMTP-From: ", + sizeof("Auth-SMTP-From: ") - 1); + b->last = ngx_copy(b->last, s->smtp_from.data, s->smtp_from.len); + *b->last++ = CR; *b->last++ = LF; + + b->last = ngx_cpymem(b->last, "Auth-SMTP-To: ", + sizeof("Auth-SMTP-To: ") - 1); + b->last = ngx_copy(b->last, s->smtp_to.data, s->smtp_to.len); + *b->last++ = CR; *b->last++ = LF; + + } + if (ahcf->header.len) { b->last = ngx_copy(b->last, ahcf->header.data, ahcf->header.len); } @@ -1255,7 +1281,7 @@ escaped->len = text->len + n * 2; - p = ngx_palloc(pool, escaped->len); + p = ngx_pnalloc(pool, escaped->len); if (p == NULL) { return NGX_ERROR; } @@ -1275,7 +1301,7 @@ ahcf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_auth_http_conf_t)); if (ahcf == NULL) { - return NGX_CONF_ERROR; + return NULL; } ahcf->timeout = NGX_CONF_UNSET_MSEC; @@ -1326,7 +1352,7 @@ len += header[i].key.len + 2 + header[i].value.len + 2; } - p = ngx_palloc(cf->pool, len); + p = ngx_pnalloc(cf->pool, len); if (p == NULL) { return NGX_CONF_ERROR; } @@ -1368,7 +1394,7 @@ u.url.data += 7; } - if (ngx_parse_url(cf, &u) != NGX_OK) { + if (ngx_parse_url(cf->pool, &u) != NGX_OK) { if (u.err) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%s in auth_http \"%V\"", u.err, &u.url); @@ -1379,12 +1405,17 @@ ahcf->peer = u.addrs; - ahcf->host_header = u.host; + if (u.family != AF_UNIX) { + ahcf->host_header = u.host; + + } else { + ngx_str_set(&ahcf->host_header, "localhost"); + } + ahcf->uri = u.uri; if (ahcf->uri.len == 0) { - ahcf->uri.len = sizeof("/") - 1; - ahcf->uri.data = (u_char *) "/"; + ngx_str_set(&ahcf->uri, "/"); } return NGX_CONF_OK; diff -Nru nginx-0.5.33/src/mail/ngx_mail.c nginx-0.8.53/src/mail/ngx_mail.c --- nginx-0.5.33/src/mail/ngx_mail.c 2007-11-07 14:24:55.000000000 +0000 +++ nginx-0.8.53/src/mail/ngx_mail.c 2009-05-18 12:20:22.000000000 +0000 @@ -11,7 +11,16 @@ static char *ngx_mail_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); -static int ngx_mail_cmp_conf_in_addrs(const void *one, const void *two); +static ngx_int_t ngx_mail_add_ports(ngx_conf_t *cf, ngx_array_t *ports, + ngx_mail_listen_t *listen); +static char *ngx_mail_optimize_servers(ngx_conf_t *cf, ngx_array_t *ports); +static ngx_int_t ngx_mail_add_addrs(ngx_conf_t *cf, ngx_mail_port_t *mport, + ngx_mail_conf_addr_t *addr); +#if (NGX_HAVE_INET6) +static ngx_int_t ngx_mail_add_addrs6(ngx_conf_t *cf, ngx_mail_port_t *mport, + ngx_mail_conf_addr_t *addr); +#endif +static ngx_int_t ngx_mail_cmp_conf_addrs(const void *one, const void *two); ngx_uint_t ngx_mail_max_module; @@ -64,18 +73,12 @@ ngx_mail_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { char *rv; - u_char *text; - size_t len; - ngx_uint_t i, a, l, m, mi, s, p, last, bind_all, done; + ngx_uint_t i, m, mi, s; ngx_conf_t pcf; - ngx_array_t in_ports; - ngx_listening_t *ls; - ngx_mail_listen_t *imls; + ngx_array_t ports; + ngx_mail_listen_t *listen; ngx_mail_module_t *module; - ngx_mail_in_port_t *imip; ngx_mail_conf_ctx_t *ctx; - ngx_mail_conf_in_port_t *in_port; - ngx_mail_conf_in_addr_t *in_addr; ngx_mail_core_srv_conf_t **cscfp; ngx_mail_core_main_conf_t *cmcf; @@ -216,188 +219,309 @@ *cf = pcf; - if (ngx_array_init(&in_ports, cf->temp_pool, 4, - sizeof(ngx_mail_conf_in_port_t)) + if (ngx_array_init(&ports, cf->temp_pool, 4, sizeof(ngx_mail_conf_port_t)) != NGX_OK) { return NGX_CONF_ERROR; } - imls = cmcf->listen.elts; + listen = cmcf->listen.elts; - for (l = 0; l < cmcf->listen.nelts; l++) { + for (i = 0; i < cmcf->listen.nelts; i++) { + if (ngx_mail_add_ports(cf, &ports, &listen[i]) != NGX_OK) { + return NGX_CONF_ERROR; + } + } - /* AF_INET only */ + return ngx_mail_optimize_servers(cf, &ports); +} - in_port = in_ports.elts; - for (p = 0; p < in_ports.nelts; p++) { - if (in_port[p].port == imls[l].port) { - in_port = &in_port[p]; - goto found; - } - } - in_port = ngx_array_push(&in_ports); - if (in_port == NULL) { - return NGX_CONF_ERROR; - } +static ngx_int_t +ngx_mail_add_ports(ngx_conf_t *cf, ngx_array_t *ports, + ngx_mail_listen_t *listen) +{ + in_port_t p; + ngx_uint_t i; + struct sockaddr *sa; + struct sockaddr_in *sin; + ngx_mail_conf_port_t *port; + ngx_mail_conf_addr_t *addr; +#if (NGX_HAVE_INET6) + struct sockaddr_in6 *sin6; +#endif - in_port->port = imls[l].port; + sa = (struct sockaddr *) &listen->sockaddr; - if (ngx_array_init(&in_port->addrs, cf->temp_pool, 2, - sizeof(ngx_mail_conf_in_addr_t)) - != NGX_OK) - { - return NGX_CONF_ERROR; - } + switch (sa->sa_family) { - found: +#if (NGX_HAVE_INET6) + case AF_INET6: + sin6 = (struct sockaddr_in6 *) sa; + p = sin6->sin6_port; + break; +#endif - in_addr = ngx_array_push(&in_port->addrs); - if (in_addr == NULL) { - return NGX_CONF_ERROR; + default: /* AF_INET */ + sin = (struct sockaddr_in *) sa; + p = sin->sin_port; + break; + } + + port = ports->elts; + for (i = 0; i < ports->nelts; i++) { + if (p == port[i].port && sa->sa_family == port[i].family) { + + /* a port is already in the port list */ + + port = &port[i]; + goto found; } + } + + /* add a port to the port list */ + + port = ngx_array_push(ports); + if (port == NULL) { + return NGX_ERROR; + } - in_addr->addr = imls[l].addr; - in_addr->ctx = imls[l].ctx; - in_addr->bind = imls[l].bind; + port->family = sa->sa_family; + port->port = p; + + if (ngx_array_init(&port->addrs, cf->temp_pool, 2, + sizeof(ngx_mail_conf_addr_t)) + != NGX_OK) + { + return NGX_ERROR; + } + +found: + + addr = ngx_array_push(&port->addrs); + if (addr == NULL) { + return NGX_ERROR; } - /* optimize the lists of ports and addresses */ + addr->sockaddr = (struct sockaddr *) &listen->sockaddr; + addr->socklen = listen->socklen; + addr->ctx = listen->ctx; + addr->bind = listen->bind; + addr->wildcard = listen->wildcard; +#if (NGX_MAIL_SSL) + addr->ssl = listen->ssl; +#endif +#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY) + addr->ipv6only = listen->ipv6only; +#endif + + return NGX_OK; +} - /* AF_INET only */ - in_port = in_ports.elts; - for (p = 0; p < in_ports.nelts; p++) { +static char * +ngx_mail_optimize_servers(ngx_conf_t *cf, ngx_array_t *ports) +{ + ngx_uint_t i, p, last, bind_wildcard; + ngx_listening_t *ls; + ngx_mail_port_t *mport; + ngx_mail_conf_port_t *port; + ngx_mail_conf_addr_t *addr; - ngx_sort(in_port[p].addrs.elts, (size_t) in_port[p].addrs.nelts, - sizeof(ngx_mail_conf_in_addr_t), ngx_mail_cmp_conf_in_addrs); + port = ports->elts; + for (p = 0; p < ports->nelts; p++) { - in_addr = in_port[p].addrs.elts; - last = in_port[p].addrs.nelts; + ngx_sort(port[p].addrs.elts, (size_t) port[p].addrs.nelts, + sizeof(ngx_mail_conf_addr_t), ngx_mail_cmp_conf_addrs); + + addr = port[p].addrs.elts; + last = port[p].addrs.nelts; /* * if there is the binding to the "*:port" then we need to bind() * to the "*:port" only and ignore the other bindings */ - if (in_addr[last - 1].addr == INADDR_ANY) { - in_addr[last - 1].bind = 1; - bind_all = 0; + if (addr[last - 1].wildcard) { + addr[last - 1].bind = 1; + bind_wildcard = 1; } else { - bind_all = 1; + bind_wildcard = 0; } - for (a = 0; a < last; /* void */ ) { + i = 0; + + while (i < last) { - if (!bind_all && !in_addr[a].bind) { - a++; + if (bind_wildcard && !addr[i].bind) { + i++; continue; } - ls = ngx_listening_inet_stream_socket(cf, in_addr[a].addr, - in_port[p].port); + ls = ngx_create_listening(cf, addr[i].sockaddr, addr[i].socklen); if (ls == NULL) { return NGX_CONF_ERROR; } - ls->backlog = NGX_LISTEN_BACKLOG; - ls->rcvbuf = -1; - ls->sndbuf = -1; - ls->addr_ntop = 1; ls->handler = ngx_mail_init_connection; ls->pool_size = 256; - /* STUB */ - ls->log = *cf->cycle->new_log; + /* TODO: error_log directive */ + ls->logp = &cf->cycle->new_log; ls->log.data = &ls->addr_text; ls->log.handler = ngx_accept_log_error; - /**/ - imip = ngx_palloc(cf->pool, sizeof(ngx_mail_in_port_t)); - if (imip == NULL) { +#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY) + ls->ipv6only = addr[i].ipv6only; +#endif + + mport = ngx_palloc(cf->pool, sizeof(ngx_mail_port_t)); + if (mport == NULL) { return NGX_CONF_ERROR; } - ls->servers = imip; - - in_addr = in_port[p].addrs.elts; + ls->servers = mport; - if (in_addr[a].bind && in_addr[a].addr != INADDR_ANY) { - imip->naddrs = 1; - done = 0; - - } else if (in_port[p].addrs.nelts > 1 - && in_addr[last - 1].addr == INADDR_ANY) - { - imip->naddrs = last; - done = 1; + if (i == last - 1) { + mport->naddrs = last; } else { - imip->naddrs = 1; - done = 0; + mport->naddrs = 1; + i = 0; } -#if 0 - ngx_log_error(NGX_LOG_ALERT, cf->log, 0, - "%ui: %V %d %ui %ui", - a, &ls->addr_text, in_addr[a].bind, - imip->naddrs, last); + switch (ls->sockaddr->sa_family) { +#if (NGX_HAVE_INET6) + case AF_INET6: + if (ngx_mail_add_addrs6(cf, mport, addr) != NGX_OK) { + return NGX_CONF_ERROR; + } + break; #endif - - imip->addrs = ngx_pcalloc(cf->pool, - imip->naddrs * sizeof(ngx_mail_in_addr_t)); - if (imip->addrs == NULL) { - return NGX_CONF_ERROR; - } - - for (i = 0; i < imip->naddrs; i++) { - imip->addrs[i].addr = in_addr[i].addr; - imip->addrs[i].ctx = in_addr[i].ctx; - - text = ngx_palloc(cf->pool, - INET_ADDRSTRLEN - 1 + sizeof(":65535") - 1); - if (text == NULL) { + default: /* AF_INET */ + if (ngx_mail_add_addrs(cf, mport, addr) != NGX_OK) { return NGX_CONF_ERROR; } + break; + } - len = ngx_inet_ntop(AF_INET, &in_addr[i].addr, text, - INET_ADDRSTRLEN); + addr++; + last--; + } + } - len = ngx_sprintf(text + len, ":%d", in_port[p].port) - text; + return NGX_CONF_OK; +} - imip->addrs[i].addr_text.len = len; - imip->addrs[i].addr_text.data = text; - } - if (done) { - break; - } +static ngx_int_t +ngx_mail_add_addrs(ngx_conf_t *cf, ngx_mail_port_t *mport, + ngx_mail_conf_addr_t *addr) +{ + u_char *p; + size_t len; + ngx_uint_t i; + ngx_mail_in_addr_t *addrs; + struct sockaddr_in *sin; + u_char buf[NGX_SOCKADDR_STRLEN]; - in_addr++; - in_port[p].addrs.elts = in_addr; - last--; + mport->addrs = ngx_pcalloc(cf->pool, + mport->naddrs * sizeof(ngx_mail_in_addr_t)); + if (mport->addrs == NULL) { + return NGX_ERROR; + } + + addrs = mport->addrs; + + for (i = 0; i < mport->naddrs; i++) { + + sin = (struct sockaddr_in *) addr[i].sockaddr; + addrs[i].addr = sin->sin_addr.s_addr; - a = 0; + addrs[i].conf.ctx = addr[i].ctx; +#if (NGX_MAIL_SSL) + addrs[i].conf.ssl = addr[i].ssl; +#endif + + len = ngx_sock_ntop(addr[i].sockaddr, buf, NGX_SOCKADDR_STRLEN, 1); + + p = ngx_pnalloc(cf->pool, len); + if (p == NULL) { + return NGX_ERROR; } + + ngx_memcpy(p, buf, len); + + addrs[i].conf.addr_text.len = len; + addrs[i].conf.addr_text.data = p; } - return NGX_CONF_OK; + return NGX_OK; +} + + +#if (NGX_HAVE_INET6) + +static ngx_int_t +ngx_mail_add_addrs6(ngx_conf_t *cf, ngx_mail_port_t *mport, + ngx_mail_conf_addr_t *addr) +{ + u_char *p; + size_t len; + ngx_uint_t i; + ngx_mail_in6_addr_t *addrs6; + struct sockaddr_in6 *sin6; + u_char buf[NGX_SOCKADDR_STRLEN]; + + mport->addrs = ngx_pcalloc(cf->pool, + mport->naddrs * sizeof(ngx_mail_in6_addr_t)); + if (mport->addrs == NULL) { + return NGX_ERROR; + } + + addrs6 = mport->addrs; + + for (i = 0; i < mport->naddrs; i++) { + + sin6 = (struct sockaddr_in6 *) addr[i].sockaddr; + addrs6[i].addr6 = sin6->sin6_addr; + + addrs6[i].conf.ctx = addr[i].ctx; +#if (NGX_MAIL_SSL) + addrs6[i].conf.ssl = addr[i].ssl; +#endif + + len = ngx_sock_ntop(addr[i].sockaddr, buf, NGX_SOCKADDR_STRLEN, 1); + + p = ngx_pnalloc(cf->pool, len); + if (p == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(p, buf, len); + + addrs6[i].conf.addr_text.len = len; + addrs6[i].conf.addr_text.data = p; + } + + return NGX_OK; } +#endif + -static int -ngx_mail_cmp_conf_in_addrs(const void *one, const void *two) +static ngx_int_t +ngx_mail_cmp_conf_addrs(const void *one, const void *two) { - ngx_mail_conf_in_addr_t *first, *second; + ngx_mail_conf_addr_t *first, *second; - first = (ngx_mail_conf_in_addr_t *) one; - second = (ngx_mail_conf_in_addr_t *) two; + first = (ngx_mail_conf_addr_t *) one; + second = (ngx_mail_conf_addr_t *) two; - if (first->addr == INADDR_ANY) { - /* the INADDR_ANY must be the last resort, shift it to the end */ + if (first->wildcard) { + /* a wildcard must be the last resort, shift it to the end */ return 1; } diff -Nru nginx-0.5.33/src/mail/ngx_mail_core_module.c nginx-0.8.53/src/mail/ngx_mail_core_module.c --- nginx-0.5.33/src/mail/ngx_mail_core_module.c 2007-11-07 14:24:55.000000000 +0000 +++ nginx-0.8.53/src/mail/ngx_mail_core_module.c 2009-06-02 16:09:44.000000000 +0000 @@ -20,6 +20,8 @@ void *conf); static char *ngx_mail_core_protocol(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static char *ngx_mail_core_resolver(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); static ngx_command_t ngx_mail_core_commands[] = { @@ -66,6 +68,20 @@ offsetof(ngx_mail_core_srv_conf_t, server_name), NULL }, + { ngx_string("resolver"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1, + ngx_mail_core_resolver, + NGX_MAIL_SRV_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("resolver_timeout"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_msec_slot, + NGX_MAIL_SRV_CONF_OFFSET, + offsetof(ngx_mail_core_srv_conf_t, resolver_timeout), + NULL }, + ngx_null_command }; @@ -104,20 +120,20 @@ cmcf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_core_main_conf_t)); if (cmcf == NULL) { - return NGX_CONF_ERROR; + return NULL; } if (ngx_array_init(&cmcf->servers, cf->pool, 4, sizeof(ngx_mail_core_srv_conf_t *)) != NGX_OK) { - return NGX_CONF_ERROR; + return NULL; } if (ngx_array_init(&cmcf->listen, cf->pool, 4, sizeof(ngx_mail_listen_t)) != NGX_OK) { - return NGX_CONF_ERROR; + return NULL; } return cmcf; @@ -141,8 +157,14 @@ */ cscf->timeout = NGX_CONF_UNSET_MSEC; + cscf->resolver_timeout = NGX_CONF_UNSET_MSEC; cscf->so_keepalive = NGX_CONF_UNSET; + cscf->resolver = NGX_CONF_UNSET_PTR; + + cscf->file_name = cf->conf_file->file.name.data; + cscf->line = cf->conf_file->line; + return cscf; } @@ -154,6 +176,8 @@ ngx_mail_core_srv_conf_t *conf = child; ngx_conf_merge_msec_value(conf->timeout, prev->timeout, 60000); + ngx_conf_merge_msec_value(conf->resolver_timeout, prev->resolver_timeout, + 30000); ngx_conf_merge_value(conf->so_keepalive, prev->so_keepalive, 0); @@ -161,20 +185,7 @@ ngx_conf_merge_str_value(conf->server_name, prev->server_name, ""); if (conf->server_name.len == 0) { - conf->server_name.data = ngx_palloc(cf->pool, NGX_MAXHOSTNAMELEN); - if (conf->server_name.data == NULL) { - return NGX_CONF_ERROR; - } - - if (gethostname((char *) conf->server_name.data, NGX_MAXHOSTNAMELEN) - == -1) - { - ngx_log_error(NGX_LOG_EMERG, cf->log, ngx_errno, - "gethostname() failed"); - return NGX_CONF_ERROR; - } - - conf->server_name.len = ngx_strlen(conf->server_name.data); + conf->server_name = cf->cycle->hostname; } if (conf->protocol == NULL) { @@ -184,6 +195,8 @@ return NGX_CONF_ERROR; } + ngx_conf_merge_ptr_value(conf->resolver, prev->resolver, NULL); + return NGX_CONF_OK; } @@ -237,9 +250,6 @@ cscf = ctx->srv_conf[ngx_mail_core_module.ctx_index]; cscf->ctx = ctx; - cscf->file_name = cf->conf_file->file.name.data; - cscf->line = cf->conf_file->line; - cmcf = ctx->main_conf[ngx_mail_core_module.ctx_index]; cscfp = ngx_array_push(&cmcf->servers); @@ -264,19 +274,24 @@ } -/* AF_INET only */ - static char * ngx_mail_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_mail_core_srv_conf_t *cscf = conf; + size_t len, off; + in_port_t port; ngx_str_t *value; ngx_url_t u; ngx_uint_t i, m; - ngx_mail_listen_t *imls; + struct sockaddr *sa; + ngx_mail_listen_t *ls; ngx_mail_module_t *module; + struct sockaddr_in *sin; ngx_mail_core_main_conf_t *cmcf; +#if (NGX_HAVE_INET6) + struct sockaddr_in6 *sin6; +#endif value = cf->args->elts; @@ -285,7 +300,7 @@ u.url = value[1]; u.listen = 1; - if (ngx_parse_url(cf, &u) != NGX_OK) { + if (ngx_parse_url(cf->pool, &u) != NGX_OK) { if (u.err) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%s in \"%V\" of the \"listen\" directive", @@ -297,11 +312,40 @@ cmcf = ngx_mail_conf_get_module_main_conf(cf, ngx_mail_core_module); - imls = cmcf->listen.elts; + ls = cmcf->listen.elts; for (i = 0; i < cmcf->listen.nelts; i++) { - if (imls[i].addr != u.addr.in_addr || imls[i].port != u.port) { + sa = (struct sockaddr *) ls[i].sockaddr; + + if (sa->sa_family != u.family) { + continue; + } + + switch (sa->sa_family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + off = offsetof(struct sockaddr_in6, sin6_addr); + len = 16; + sin6 = (struct sockaddr_in6 *) sa; + port = sin6->sin6_port; + break; +#endif + + default: /* AF_INET */ + off = offsetof(struct sockaddr_in, sin_addr); + len = 4; + sin = (struct sockaddr_in *) sa; + port = sin->sin_port; + break; + } + + if (ngx_memcmp(ls[i].sockaddr + off, u.sockaddr + off, len) != 0) { + continue; + } + + if (port != u.port) { continue; } @@ -310,17 +354,18 @@ return NGX_CONF_ERROR; } - imls = ngx_array_push(&cmcf->listen); - if (imls == NULL) { + ls = ngx_array_push(&cmcf->listen); + if (ls == NULL) { return NGX_CONF_ERROR; } - ngx_memzero(imls, sizeof(ngx_mail_listen_t)); + ngx_memzero(ls, sizeof(ngx_mail_listen_t)); + + ngx_memcpy(ls->sockaddr, u.sockaddr, u.socklen); - imls->addr = u.addr.in_addr; - imls->port = u.port; - imls->family = AF_INET; - imls->ctx = cf->ctx; + ls->socklen = u.socklen; + ls->wildcard = u.wildcard; + ls->ctx = cf->ctx; for (m = 0; ngx_modules[m]; m++) { if (ngx_modules[m]->type != NGX_MAIL_MODULE) { @@ -341,18 +386,72 @@ } } - if (cf->args->nelts == 2) { - return NGX_CONF_OK; - } + for (i = 2; i < cf->args->nelts; i++) { - if (ngx_strcmp(value[2].data, "bind") == 0) { - imls->bind = 1; - return NGX_CONF_OK; + if (ngx_strcmp(value[i].data, "bind") == 0) { + ls->bind = 1; + continue; + } + + if (ngx_strncmp(value[i].data, "ipv6only=o", 10) == 0) { +#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY) + struct sockaddr *sa; + u_char buf[NGX_SOCKADDR_STRLEN]; + + sa = (struct sockaddr *) ls->sockaddr; + + if (sa->sa_family == AF_INET6) { + + if (ngx_strcmp(&value[i].data[10], "n") == 0) { + ls->ipv6only = 1; + + } else if (ngx_strcmp(&value[i].data[10], "ff") == 0) { + ls->ipv6only = 2; + + } else { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid ipv6only flags \"%s\"", + &value[i].data[9]); + return NGX_CONF_ERROR; + } + + ls->bind = 1; + + } else { + len = ngx_sock_ntop(sa, buf, NGX_SOCKADDR_STRLEN, 1); + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "ipv6only is not supported " + "on addr \"%*s\", ignored", len, buf); + } + + continue; +#else + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "bind ipv6only is not supported " + "on this platform"); + return NGX_CONF_ERROR; +#endif + } + + if (ngx_strcmp(value[i].data, "ssl") == 0) { +#if (NGX_MAIL_SSL) + ls->ssl = 1; + continue; +#else + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "the \"ssl\" parameter requires " + "ngx_mail_ssl_module"); + return NGX_CONF_ERROR; +#endif + } + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "the invalid \"%V\" parameter", &value[i]); + return NGX_CONF_ERROR; } - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "the invalid \"%V\" parameter", &value[2]); - return NGX_CONF_ERROR; + return NGX_CONF_OK; } @@ -389,6 +488,44 @@ } +static char * +ngx_mail_core_resolver(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_mail_core_srv_conf_t *cscf = conf; + + ngx_url_t u; + ngx_str_t *value; + + value = cf->args->elts; + + if (cscf->resolver != NGX_CONF_UNSET_PTR) { + return "is duplicate"; + } + + if (ngx_strcmp(value[1].data, "off") == 0) { + cscf->resolver = NULL; + return NGX_CONF_OK; + } + + ngx_memzero(&u, sizeof(ngx_url_t)); + + u.host = value[1]; + u.port = 53; + + if (ngx_inet_resolve_host(cf->pool, &u) != NGX_OK) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%V: %s", &u.host, u.err); + return NGX_CONF_ERROR; + } + + cscf->resolver = ngx_resolver_create(cf, &u.addrs[0]); + if (cscf->resolver == NULL) { + return NGX_CONF_OK; + } + + return NGX_CONF_OK; +} + + char * ngx_mail_capabilities(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { diff -Nru nginx-0.5.33/src/mail/ngx_mail.h nginx-0.8.53/src/mail/ngx_mail.h --- nginx-0.5.33/src/mail/ngx_mail.h 2007-11-07 14:24:55.000000000 +0000 +++ nginx-0.8.53/src/mail/ngx_mail.h 2009-11-02 15:14:17.000000000 +0000 @@ -26,41 +26,76 @@ typedef struct { - in_addr_t addr; - in_port_t port; - int family; + u_char sockaddr[NGX_SOCKADDRLEN]; + socklen_t socklen; /* server ctx */ ngx_mail_conf_ctx_t *ctx; unsigned bind:1; + unsigned wildcard:1; +#if (NGX_MAIL_SSL) + unsigned ssl:1; +#endif +#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY) + unsigned ipv6only:2; +#endif } ngx_mail_listen_t; typedef struct { - in_addr_t addr; ngx_mail_conf_ctx_t *ctx; ngx_str_t addr_text; +#if (NGX_MAIL_SSL) + ngx_uint_t ssl; /* unsigned ssl:1; */ +#endif +} ngx_mail_addr_conf_t; + +typedef struct { + in_addr_t addr; + ngx_mail_addr_conf_t conf; } ngx_mail_in_addr_t; +#if (NGX_HAVE_INET6) + +typedef struct { + struct in6_addr addr6; + ngx_mail_addr_conf_t conf; +} ngx_mail_in6_addr_t; + +#endif + + typedef struct { - ngx_mail_in_addr_t *addrs; /* array of ngx_mail_in_addr_t */ + /* ngx_mail_in_addr_t or ngx_mail_in6_addr_t */ + void *addrs; ngx_uint_t naddrs; -} ngx_mail_in_port_t; +} ngx_mail_port_t; typedef struct { + int family; in_port_t port; - ngx_array_t addrs; /* array of ngx_mail_conf_in_addr_t */ -} ngx_mail_conf_in_port_t; + ngx_array_t addrs; /* array of ngx_mail_conf_addr_t */ +} ngx_mail_conf_port_t; typedef struct { - in_addr_t addr; + struct sockaddr *sockaddr; + socklen_t socklen; + ngx_mail_conf_ctx_t *ctx; + unsigned bind:1; -} ngx_mail_conf_in_addr_t; + unsigned wildcard:1; +#if (NGX_MAIL_SSL) + unsigned ssl:1; +#endif +#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY) + unsigned ipv6only:2; +#endif +} ngx_mail_conf_addr_t; typedef struct { @@ -81,6 +116,7 @@ ngx_mail_protocol_t *protocol; ngx_msec_t timeout; + ngx_msec_t resolver_timeout; ngx_flag_t so_keepalive; @@ -89,6 +125,8 @@ u_char *file_name; ngx_int_t line; + ngx_resolver_t *resolver; + /* server ctx */ ngx_mail_conf_ctx_t *ctx; } ngx_mail_core_srv_conf_t; @@ -102,7 +140,7 @@ ngx_pop3_auth_login_password, ngx_pop3_auth_plain, ngx_pop3_auth_cram_md5 -} ngx_po3_state_e; +} ngx_pop3_state_e; typedef enum { @@ -124,8 +162,13 @@ ngx_smtp_auth_plain, ngx_smtp_auth_cram_md5, ngx_smtp_helo, - ngx_smtp_noxclient, - ngx_smtp_xclient + ngx_smtp_helo_xclient, + ngx_smtp_helo_from, + ngx_smtp_xclient, + ngx_smtp_xclient_from, + ngx_smtp_xclient_helo, + ngx_smtp_from, + ngx_smtp_to } ngx_smtp_state_e; @@ -147,6 +190,8 @@ void **main_conf; void **srv_conf; + ngx_resolver_ctx_t *resolver_ctx; + ngx_mail_proxy_ctx_t *proxy; ngx_uint_t mail_state; @@ -159,7 +204,7 @@ unsigned no_sync_literal:1; unsigned starttls:1; unsigned esmtp:1; - unsigned auth_method:2; + unsigned auth_method:3; unsigned auth_wait:1; ngx_str_t login; @@ -171,7 +216,10 @@ ngx_str_t text; ngx_str_t *addr_text; + ngx_str_t host; ngx_str_t smtp_helo; + ngx_str_t smtp_from; + ngx_str_t smtp_to; ngx_uint_t command; ngx_array_t args; @@ -237,16 +285,19 @@ #define NGX_SMTP_STARTTLS 13 -#define NGX_MAIL_AUTH_PLAIN 0 -#define NGX_MAIL_AUTH_LOGIN 1 -#define NGX_MAIL_AUTH_APOP 2 -#define NGX_MAIL_AUTH_CRAM_MD5 3 +#define NGX_MAIL_AUTH_PLAIN 0 +#define NGX_MAIL_AUTH_LOGIN 1 +#define NGX_MAIL_AUTH_LOGIN_USERNAME 2 +#define NGX_MAIL_AUTH_APOP 3 +#define NGX_MAIL_AUTH_CRAM_MD5 4 +#define NGX_MAIL_AUTH_NONE 5 #define NGX_MAIL_AUTH_PLAIN_ENABLED 0x0002 #define NGX_MAIL_AUTH_LOGIN_ENABLED 0x0004 #define NGX_MAIL_AUTH_APOP_ENABLED 0x0008 #define NGX_MAIL_AUTH_CRAM_MD5_ENABLED 0x0010 +#define NGX_MAIL_AUTH_NONE_ENABLED 0x0020 #define NGX_MAIL_PARSE_INVALID_COMMAND 20 @@ -323,7 +374,7 @@ ngx_int_t ngx_mail_auth_plain(ngx_mail_session_t *s, ngx_connection_t *c, ngx_uint_t n); ngx_int_t ngx_mail_auth_login_username(ngx_mail_session_t *s, - ngx_connection_t *c); + ngx_connection_t *c, ngx_uint_t n); ngx_int_t ngx_mail_auth_login_password(ngx_mail_session_t *s, ngx_connection_t *c); ngx_int_t ngx_mail_auth_cram_md5_salt(ngx_mail_session_t *s, @@ -343,7 +394,7 @@ /* STUB */ -void ngx_mail_proxy_init(ngx_mail_session_t *s, ngx_peer_addr_t *peer); +void ngx_mail_proxy_init(ngx_mail_session_t *s, ngx_addr_t *peer); void ngx_mail_auth_http_init(ngx_mail_session_t *s); /**/ diff -Nru nginx-0.5.33/src/mail/ngx_mail_handler.c nginx-0.8.53/src/mail/ngx_mail_handler.c --- nginx-0.5.33/src/mail/ngx_mail_handler.c 2007-11-07 14:24:55.000000000 +0000 +++ nginx-0.8.53/src/mail/ngx_mail_handler.c 2010-06-23 16:34:54.000000000 +0000 @@ -21,16 +21,17 @@ void ngx_mail_init_connection(ngx_connection_t *c) { - in_addr_t in_addr; - socklen_t len; - ngx_uint_t i; - struct sockaddr_in sin; - ngx_mail_log_ctx_t *ctx; - ngx_mail_in_port_t *imip; - ngx_mail_in_addr_t *imia; - ngx_mail_session_t *s; -#if (NGX_MAIL_SSL) - ngx_mail_ssl_conf_t *sslcf; + ngx_uint_t i; + ngx_mail_port_t *port; + struct sockaddr *sa; + struct sockaddr_in *sin; + ngx_mail_log_ctx_t *ctx; + ngx_mail_in_addr_t *addr; + ngx_mail_session_t *s; + ngx_mail_addr_conf_t *addr_conf; +#if (NGX_HAVE_INET6) + struct sockaddr_in6 *sin6; + ngx_mail_in6_addr_t *addr6; #endif @@ -38,12 +39,9 @@ /* AF_INET only */ - imip = c->listening->servers; - imia = imip->addrs; + port = c->listening->servers; - i = 0; - - if (imip->naddrs > 1) { + if (port->naddrs > 1) { /* * There are several addresses on this port and one of them @@ -53,34 +51,68 @@ * AcceptEx() already gave this address. */ -#if (NGX_WIN32) - if (c->local_sockaddr) { - in_addr = - ((struct sockaddr_in *) c->local_sockaddr)->sin_addr.s_addr; - - } else -#endif - { - len = sizeof(struct sockaddr_in); - if (getsockname(c->fd, (struct sockaddr *) &sin, &len) == -1) { - ngx_connection_error(c, ngx_socket_errno, - "getsockname() failed"); - ngx_mail_close_connection(c); - return; + if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) { + ngx_mail_close_connection(c); + return; + } + + sa = c->local_sockaddr; + + switch (sa->sa_family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + sin6 = (struct sockaddr_in6 *) sa; + + 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; + } } - in_addr = sin.sin_addr.s_addr; - } + addr_conf = &addr6[i].conf; + + break; +#endif + + default: /* AF_INET */ + sin = (struct sockaddr_in *) sa; - /* the last address is "*" */ + addr = port->addrs; - for ( /* void */ ; i < imip->naddrs - 1; i++) { - if (in_addr == imia[i].addr) { - break; + /* the last address is "*" */ + + for (i = 0; i < port->naddrs - 1; i++) { + if (addr[i].addr == sin->sin_addr.s_addr) { + break; + } } + + addr_conf = &addr[i].conf; + + break; } - } + } else { + switch (c->local_sockaddr->sa_family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + addr6 = port->addrs; + addr_conf = &addr6[0].conf; + break; +#endif + + default: /* AF_INET */ + addr = port->addrs; + addr_conf = &addr[0].conf; + break; + } + } s = ngx_pcalloc(c->pool, sizeof(ngx_mail_session_t)); if (s == NULL) { @@ -88,10 +120,10 @@ return; } - s->main_conf = imia[i].ctx->main_conf; - s->srv_conf = imia[i].ctx->srv_conf; + s->main_conf = addr_conf->ctx->main_conf; + s->srv_conf = addr_conf->ctx->srv_conf; - s->addr_text = &imia[i].addr_text; + s->addr_text = &addr_conf->addr_text; c->data = s; s->connection = c; @@ -116,14 +148,35 @@ c->log_error = NGX_ERROR_INFO; #if (NGX_MAIL_SSL) + { + ngx_mail_ssl_conf_t *sslcf; sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module); if (sslcf->enable) { + c->log->action = "SSL handshaking"; + + ngx_mail_ssl_init_connection(&sslcf->ssl, c); + return; + } + + if (addr_conf->ssl) { + + c->log->action = "SSL handshaking"; + + if (sslcf->ssl.ctx == NULL) { + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "no \"ssl_certificate\" is defined " + "in server listening on SSL port"); + ngx_mail_close_connection(c); + return; + } + ngx_mail_ssl_init_connection(&sslcf->ssl, c); return; } + } #endif ngx_mail_init_session(c); @@ -200,6 +253,8 @@ return; } + c->read->ready = 0; + ngx_mail_init_session(c); return; } @@ -238,10 +293,10 @@ ngx_mail_salt(ngx_mail_session_t *s, ngx_connection_t *c, ngx_mail_core_srv_conf_t *cscf) { - s->salt.data = ngx_palloc(c->pool, - sizeof(" <18446744073709551616.@>" CRLF) - 1 - + NGX_TIME_T_LEN - + cscf->server_name.len); + s->salt.data = ngx_pnalloc(c->pool, + sizeof(" <18446744073709551616.@>" CRLF) - 1 + + NGX_TIME_T_LEN + + cscf->server_name.len); if (s->salt.data == NULL) { return NGX_ERROR; } @@ -290,8 +345,8 @@ "mail auth plain: \"%V\"", &arg[n]); #endif - plain.data = ngx_palloc(c->pool, ngx_base64_decoded_length(arg[n].len)); - if (plain.data == NULL){ + plain.data = ngx_pnalloc(c->pool, ngx_base64_decoded_length(arg[n].len)); + if (plain.data == NULL) { return NGX_ERROR; } @@ -337,21 +392,22 @@ ngx_int_t -ngx_mail_auth_login_username(ngx_mail_session_t *s, ngx_connection_t *c) +ngx_mail_auth_login_username(ngx_mail_session_t *s, ngx_connection_t *c, + ngx_uint_t n) { ngx_str_t *arg; arg = s->args.elts; ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, - "mail auth login username: \"%V\"", &arg[0]); + "mail auth login username: \"%V\"", &arg[n]); - s->login.data = ngx_palloc(c->pool, ngx_base64_decoded_length(arg[0].len)); - if (s->login.data == NULL){ + s->login.data = ngx_pnalloc(c->pool, ngx_base64_decoded_length(arg[n].len)); + if (s->login.data == NULL) { return NGX_ERROR; } - if (ngx_decode_base64(&s->login, &arg[0]) != NGX_OK) { + if (ngx_decode_base64(&s->login, &arg[n]) != NGX_OK) { ngx_log_error(NGX_LOG_INFO, c->log, 0, "client sent invalid base64 encoding in AUTH LOGIN command"); return NGX_MAIL_PARSE_INVALID_COMMAND; @@ -376,8 +432,9 @@ "mail auth login password: \"%V\"", &arg[0]); #endif - s->passwd.data = ngx_palloc(c->pool, ngx_base64_decoded_length(arg[0].len)); - if (s->passwd.data == NULL){ + s->passwd.data = ngx_pnalloc(c->pool, + ngx_base64_decoded_length(arg[0].len)); + if (s->passwd.data == NULL) { return NGX_ERROR; } @@ -404,7 +461,7 @@ ngx_str_t salt; ngx_uint_t n; - p = ngx_palloc(c->pool, len + ngx_base64_encoded_length(s->salt.len) + 2); + p = ngx_pnalloc(c->pool, len + ngx_base64_encoded_length(s->salt.len) + 2); if (p == NULL) { return NGX_ERROR; } @@ -436,8 +493,8 @@ ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, "mail auth cram-md5: \"%V\"", &arg[0]); - s->login.data = ngx_palloc(c->pool, ngx_base64_decoded_length(arg[0].len)); - if (s->login.data == NULL){ + s->login.data = ngx_pnalloc(c->pool, ngx_base64_decoded_length(arg[0].len)); + if (s->login.data == NULL) { return NGX_ERROR; } @@ -493,7 +550,7 @@ } if (s->out.len == 0) { - if (ngx_handle_write_event(c->write, 0) == NGX_ERROR) { + if (ngx_handle_write_event(c->write, 0) != NGX_OK) { ngx_mail_close_connection(c); } @@ -532,7 +589,7 @@ ngx_add_timer(c->write, cscf->timeout); - if (ngx_handle_write_event(c->write, 0) == NGX_ERROR) { + if (ngx_handle_write_event(c->write, 0) != NGX_OK) { ngx_mail_close_connection(c); return; } @@ -559,7 +616,7 @@ } if (n == NGX_AGAIN) { - if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) { + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { ngx_mail_session_internal_server_error(s); return NGX_ERROR; } @@ -653,7 +710,7 @@ #endif #if (NGX_STAT_STUB) - ngx_atomic_fetch_add(ngx_stat_active, -1); + (void) ngx_atomic_fetch_add(ngx_stat_active, -1); #endif c->destroyed = 1; diff -Nru nginx-0.5.33/src/mail/ngx_mail_imap_handler.c nginx-0.8.53/src/mail/ngx_mail_imap_handler.c --- nginx-0.5.33/src/mail/ngx_mail_imap_handler.c 2007-11-07 14:24:55.000000000 +0000 +++ nginx-0.8.53/src/mail/ngx_mail_imap_handler.c 2010-05-14 09:56:37.000000000 +0000 @@ -39,14 +39,13 @@ cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); - s->out.len = sizeof(imap_greeting) - 1; - s->out.data = imap_greeting; + ngx_str_set(&s->out, imap_greeting); c->read->handler = ngx_mail_imap_init_protocol; ngx_add_timer(c->read, cscf->timeout); - if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) { + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { ngx_mail_close_connection(c); } @@ -136,8 +135,7 @@ tag = 1; s->text.len = 0; - s->out.len = sizeof(imap_ok) - 1; - s->out.data = imap_ok; + ngx_str_set(&s->out, imap_ok); if (rc == NGX_OK) { @@ -186,8 +184,7 @@ case NGX_IMAP_LOGOUT: s->quit = 1; - s->text.len = sizeof(imap_bye) - 1; - s->text.data = imap_bye; + ngx_str_set(&s->text, imap_bye); break; case NGX_IMAP_NOOP: @@ -205,11 +202,10 @@ break; case ngx_imap_auth_login_username: - rc = ngx_mail_auth_login_username(s, c); + rc = ngx_mail_auth_login_username(s, c, 0); tag = 0; - s->out.len = sizeof(imap_password) - 1; - s->out.data = imap_password; + ngx_str_set(&s->out, imap_password); s->mail_state = ngx_imap_auth_login_password; break; @@ -229,8 +225,7 @@ } else if (rc == NGX_IMAP_NEXT) { tag = 0; - s->out.len = sizeof(imap_next) - 1; - s->out.data = imap_next; + ngx_str_set(&s->out, imap_next); } switch (rc) { @@ -245,21 +240,19 @@ case NGX_MAIL_PARSE_INVALID_COMMAND: s->state = 0; - s->out.len = sizeof(imap_invalid_command) - 1; - s->out.data = imap_invalid_command; + ngx_str_set(&s->out, imap_invalid_command); s->mail_state = ngx_imap_start; break; } if (tag) { if (s->tag.len == 0) { - s->tag.len = sizeof(imap_star) - 1; - s->tag.data = (u_char *) imap_star; + ngx_str_set(&s->tag, imap_star); } if (s->tagged_line.len < s->tag.len + s->text.len + s->out.len) { s->tagged_line.len = s->tag.len + s->text.len + s->out.len; - s->tagged_line.data = ngx_palloc(c->pool, s->tagged_line.len); + s->tagged_line.data = ngx_pnalloc(c->pool, s->tagged_line.len); if (s->tagged_line.data == NULL) { ngx_mail_close_connection(c); return; @@ -302,7 +295,7 @@ static ngx_int_t ngx_mail_imap_login(ngx_mail_session_t *s, ngx_connection_t *c) { - ngx_str_t *arg; + ngx_str_t *arg; #if (NGX_MAIL_SSL) if (ngx_mail_starttls_only(s, c)) { @@ -317,7 +310,7 @@ } s->login.len = arg[0].len; - s->login.data = ngx_palloc(c->pool, s->login.len); + s->login.data = ngx_pnalloc(c->pool, s->login.len); if (s->login.data == NULL) { return NGX_ERROR; } @@ -325,7 +318,7 @@ ngx_memcpy(s->login.data, arg[0].data, s->login.len); s->passwd.len = arg[1].len; - s->passwd.data = ngx_palloc(c->pool, s->passwd.len); + s->passwd.data = ngx_pnalloc(c->pool, s->passwd.len); if (s->passwd.data == NULL) { return NGX_ERROR; } @@ -364,16 +357,21 @@ case NGX_MAIL_AUTH_LOGIN: - s->out.len = sizeof(imap_username) - 1; - s->out.data = imap_username; + ngx_str_set(&s->out, imap_username); s->mail_state = ngx_imap_auth_login_username; return NGX_OK; + case NGX_MAIL_AUTH_LOGIN_USERNAME: + + ngx_str_set(&s->out, imap_password); + s->mail_state = ngx_imap_auth_login_password; + + return ngx_mail_auth_login_username(s, c, 1); + case NGX_MAIL_AUTH_PLAIN: - s->out.len = sizeof(imap_plain_next) - 1; - s->out.data = imap_plain_next; + ngx_str_set(&s->out, imap_plain_next); s->mail_state = ngx_imap_auth_plain; return NGX_OK; @@ -410,15 +408,14 @@ ngx_mail_imap_capability(ngx_mail_session_t *s, ngx_connection_t *c) { ngx_mail_imap_srv_conf_t *iscf; -#if (NGX_MAIL_SSL) - ngx_mail_ssl_conf_t *sslcf; -#endif iscf = ngx_mail_get_module_srv_conf(s, ngx_mail_imap_module); #if (NGX_MAIL_SSL) if (c->ssl == NULL) { + ngx_mail_ssl_conf_t *sslcf; + sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module); if (sslcf->starttls == NGX_MAIL_STARTTLS_ON) { diff -Nru nginx-0.5.33/src/mail/ngx_mail_imap_module.c nginx-0.8.53/src/mail/ngx_mail_imap_module.c --- nginx-0.5.33/src/mail/ngx_mail_imap_module.c 2007-11-07 14:24:55.000000000 +0000 +++ nginx-0.8.53/src/mail/ngx_mail_imap_module.c 2008-11-13 13:25:34.000000000 +0000 @@ -36,7 +36,8 @@ ngx_string("AUTH=PLAIN"), ngx_string("AUTH=LOGIN"), ngx_null_string, /* APOP */ - ngx_string("AUTH=CRAM-MD5") + ngx_string("AUTH=CRAM-MD5"), + ngx_null_string /* NONE */ }; @@ -183,7 +184,7 @@ } } - p = ngx_palloc(cf->pool, size); + p = ngx_pnalloc(cf->pool, size); if (p == NULL) { return NGX_CONF_ERROR; } @@ -216,7 +217,7 @@ size += sizeof(" STARTTLS") - 1; - p = ngx_palloc(cf->pool, size); + p = ngx_pnalloc(cf->pool, size); if (p == NULL) { return NGX_CONF_ERROR; } @@ -233,7 +234,7 @@ size = (auth - conf->capability.data) + sizeof(CRLF) - 1 + sizeof(" STARTTLS LOGINDISABLED") - 1; - p = ngx_palloc(cf->pool, size); + p = ngx_pnalloc(cf->pool, size); if (p == NULL) { return NGX_CONF_ERROR; } diff -Nru nginx-0.5.33/src/mail/ngx_mail_parse.c nginx-0.8.53/src/mail/ngx_mail_parse.c --- nginx-0.5.33/src/mail/ngx_mail_parse.c 2007-11-07 14:24:55.000000000 +0000 +++ nginx-0.8.53/src/mail/ngx_mail_parse.c 2009-02-09 12:03:55.000000000 +0000 @@ -746,7 +746,7 @@ s->arg_end = p; goto done; default: - if (s->args.nelts <= 2) { + if (s->args.nelts <= 10) { state = sw_argument; s->arg_start = p; break; @@ -848,6 +848,10 @@ return NGX_MAIL_AUTH_LOGIN; } + if (s->args.nelts == 2) { + return NGX_MAIL_AUTH_LOGIN_USERNAME; + } + return NGX_MAIL_PARSE_INVALID_COMMAND; } diff -Nru nginx-0.5.33/src/mail/ngx_mail_pop3_handler.c nginx-0.8.53/src/mail/ngx_mail_pop3_handler.c --- nginx-0.5.33/src/mail/ngx_mail_pop3_handler.c 2007-11-07 14:24:55.000000000 +0000 +++ nginx-0.8.53/src/mail/ngx_mail_pop3_handler.c 2010-05-14 09:56:37.000000000 +0000 @@ -46,7 +46,7 @@ return; } - s->out.data = ngx_palloc(c->pool, sizeof(pop3_greeting) + s->salt.len); + s->out.data = ngx_pnalloc(c->pool, sizeof(pop3_greeting) + s->salt.len); if (s->out.data == NULL) { ngx_mail_session_internal_server_error(s); return; @@ -59,15 +59,14 @@ s->out.len = p - s->out.data; } else { - s->out.len = sizeof(pop3_greeting) - 1; - s->out.data = pop3_greeting; + ngx_str_set(&s->out, pop3_greeting); } c->read->handler = ngx_mail_pop3_init_protocol; ngx_add_timer(c->read, cscf->timeout); - if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) { + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { ngx_mail_close_connection(c); } @@ -149,8 +148,7 @@ return; } - s->out.len = sizeof(pop3_ok) - 1; - s->out.data = pop3_ok; + ngx_str_set(&s->out, pop3_ok); if (rc == NGX_OK) { switch (s->mail_state) { @@ -188,7 +186,6 @@ default: rc = NGX_MAIL_PARSE_INVALID_COMMAND; - s->mail_state = ngx_pop3_start; break; } @@ -215,7 +212,6 @@ default: rc = NGX_MAIL_PARSE_INVALID_COMMAND; - s->mail_state = ngx_pop3_start; break; } @@ -226,10 +222,9 @@ break; case ngx_pop3_auth_login_username: - rc = ngx_mail_auth_login_username(s, c); + rc = ngx_mail_auth_login_username(s, c, 0); - s->out.len = sizeof(pop3_password) - 1; - s->out.data = pop3_password; + ngx_str_set(&s->out, pop3_password); s->mail_state = ngx_pop3_auth_login_password; break; @@ -261,8 +256,7 @@ s->mail_state = ngx_pop3_start; s->state = 0; - s->out.len = sizeof(pop3_invalid_command) - 1; - s->out.data = pop3_invalid_command; + ngx_str_set(&s->out, pop3_invalid_command); /* fall through */ @@ -283,7 +277,7 @@ static ngx_int_t ngx_mail_pop3_user(ngx_mail_session_t *s, ngx_connection_t *c) { - ngx_str_t *arg; + ngx_str_t *arg; #if (NGX_MAIL_SSL) if (ngx_mail_starttls_only(s, c)) { @@ -297,7 +291,7 @@ arg = s->args.elts; s->login.len = arg[0].len; - s->login.data = ngx_palloc(c->pool, s->login.len); + s->login.data = ngx_pnalloc(c->pool, s->login.len); if (s->login.data == NULL) { return NGX_ERROR; } @@ -324,7 +318,7 @@ arg = s->args.elts; s->passwd.len = arg[0].len; - s->passwd.data = ngx_palloc(c->pool, s->passwd.len); + s->passwd.data = ngx_pnalloc(c->pool, s->passwd.len); if (s->passwd.data == NULL) { return NGX_ERROR; } @@ -344,15 +338,14 @@ ngx_mail_pop3_capa(ngx_mail_session_t *s, ngx_connection_t *c, ngx_int_t stls) { ngx_mail_pop3_srv_conf_t *pscf; -#if (NGX_MAIL_SSL) - ngx_mail_ssl_conf_t *sslcf; -#endif pscf = ngx_mail_get_module_srv_conf(s, ngx_mail_pop3_module); #if (NGX_MAIL_SSL) if (stls && c->ssl == NULL) { + ngx_mail_ssl_conf_t *sslcf; + sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module); if (sslcf->starttls == NGX_MAIL_STARTTLS_ON) { @@ -418,7 +411,7 @@ arg = s->args.elts; s->login.len = arg[0].len; - s->login.data = ngx_palloc(c->pool, s->login.len); + s->login.data = ngx_pnalloc(c->pool, s->login.len); if (s->login.data == NULL) { return NGX_ERROR; } @@ -426,7 +419,7 @@ ngx_memcpy(s->login.data, arg[0].data, s->login.len); s->passwd.len = arg[1].len; - s->passwd.data = ngx_palloc(c->pool, s->passwd.len); + s->passwd.data = ngx_pnalloc(c->pool, s->passwd.len); if (s->passwd.data == NULL) { return NGX_ERROR; } @@ -469,16 +462,21 @@ case NGX_MAIL_AUTH_LOGIN: - s->out.len = sizeof(pop3_username) - 1; - s->out.data = pop3_username; + ngx_str_set(&s->out, pop3_username); s->mail_state = ngx_pop3_auth_login_username; return NGX_OK; + case NGX_MAIL_AUTH_LOGIN_USERNAME: + + ngx_str_set(&s->out, pop3_password); + s->mail_state = ngx_pop3_auth_login_password; + + return ngx_mail_auth_login_username(s, c, 1); + case NGX_MAIL_AUTH_PLAIN: - s->out.len = sizeof(pop3_next) - 1; - s->out.data = pop3_next; + ngx_str_set(&s->out, pop3_next); s->mail_state = ngx_pop3_auth_plain; return NGX_OK; diff -Nru nginx-0.5.33/src/mail/ngx_mail_pop3_module.c nginx-0.8.53/src/mail/ngx_mail_pop3_module.c --- nginx-0.5.33/src/mail/ngx_mail_pop3_module.c 2007-11-07 14:24:55.000000000 +0000 +++ nginx-0.8.53/src/mail/ngx_mail_pop3_module.c 2008-06-17 15:00:30.000000000 +0000 @@ -183,7 +183,7 @@ size += sizeof("SASL LOGIN PLAIN" CRLF) - 1; } - p = ngx_palloc(cf->pool, size); + p = ngx_pnalloc(cf->pool, size); if (p == NULL) { return NGX_CONF_ERROR; } @@ -213,7 +213,7 @@ size += sizeof("STLS" CRLF) - 1; - p = ngx_palloc(cf->pool, size); + p = ngx_pnalloc(cf->pool, size); if (p == NULL) { return NGX_CONF_ERROR; } @@ -236,7 +236,7 @@ } - p = ngx_palloc(cf->pool, stls_only_size); + p = ngx_pnalloc(cf->pool, stls_only_size); if (p == NULL) { return NGX_CONF_ERROR; } diff -Nru nginx-0.5.33/src/mail/ngx_mail_proxy_module.c nginx-0.8.53/src/mail/ngx_mail_proxy_module.c --- nginx-0.5.33/src/mail/ngx_mail_proxy_module.c 2007-11-07 14:24:55.000000000 +0000 +++ nginx-0.8.53/src/mail/ngx_mail_proxy_module.c 2010-05-14 09:56:37.000000000 +0000 @@ -104,11 +104,11 @@ }; -static u_char smtp_ok[] = "235 2.0.0 OK" CRLF; +static u_char smtp_auth_ok[] = "235 2.0.0 OK" CRLF; void -ngx_mail_proxy_init(ngx_mail_session_t *s, ngx_peer_addr_t *peer) +ngx_mail_proxy_init(ngx_mail_session_t *s, ngx_addr_t *peer) { int keepalive; ngx_int_t rc; @@ -171,6 +171,8 @@ return; } + s->out.len = 0; + switch (s->protocol) { case NGX_MAIL_POP3_PROTOCOL: @@ -199,7 +201,7 @@ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, "mail proxy block read"); - if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { + if (ngx_handle_read_event(rev, 0) != NGX_OK) { c = rev->data; s = c->data; @@ -251,7 +253,7 @@ s->connection->log->action = "sending user name to upstream"; line.len = sizeof("USER ") - 1 + s->login.len + 2; - line.data = ngx_palloc(c->pool, line.len); + line.data = ngx_pnalloc(c->pool, line.len); if (line.data == NULL) { ngx_mail_proxy_internal_server_error(s); return; @@ -270,7 +272,7 @@ s->connection->log->action = "sending password to upstream"; line.len = sizeof("PASS ") - 1 + s->passwd.len + 2; - line.data = ngx_palloc(c->pool, line.len); + line.data = ngx_pnalloc(c->pool, line.len); if (line.data == NULL) { ngx_mail_proxy_internal_server_error(s); return; @@ -302,8 +304,7 @@ default: #if (NGX_SUPPRESS_WARN) - line.len = 0; - line.data = NULL; + ngx_str_null(&line); #endif break; } @@ -367,7 +368,7 @@ line.len = s->tag.len + sizeof("LOGIN ") - 1 + 1 + NGX_SIZE_T_LEN + 1 + 2; - line.data = ngx_palloc(c->pool, line.len); + line.data = ngx_pnalloc(c->pool, line.len); if (line.data == NULL) { ngx_mail_proxy_internal_server_error(s); return; @@ -386,7 +387,7 @@ s->connection->log->action = "sending user name to upstream"; line.len = s->login.len + 1 + 1 + NGX_SIZE_T_LEN + 1 + 2; - line.data = ngx_palloc(c->pool, line.len); + line.data = ngx_pnalloc(c->pool, line.len); if (line.data == NULL) { ngx_mail_proxy_internal_server_error(s); return; @@ -406,7 +407,7 @@ s->connection->log->action = "sending password to upstream"; line.len = s->passwd.len + 2; - line.data = ngx_palloc(c->pool, line.len); + line.data = ngx_pnalloc(c->pool, line.len); if (line.data == NULL) { ngx_mail_proxy_internal_server_error(s); return; @@ -437,8 +438,7 @@ default: #if (NGX_SUPPRESS_WARN) - line.len = 0; - line.data = NULL; + ngx_str_null(&line); #endif break; } @@ -463,6 +463,7 @@ u_char *p; ngx_int_t rc; ngx_str_t line; + ngx_buf_t *b; ngx_connection_t *c; ngx_mail_session_t *s; ngx_mail_proxy_conf_t *pcf; @@ -503,7 +504,7 @@ cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); line.len = sizeof("HELO ") - 1 + cscf->server_name.len + 2; - line.data = ngx_palloc(c->pool, line.len); + line.data = ngx_pnalloc(c->pool, line.len); if (line.data == NULL) { ngx_mail_proxy_internal_server_error(s); return; @@ -518,52 +519,130 @@ p = ngx_cpymem(p, cscf->server_name.data, cscf->server_name.len); *p++ = CR; *p = LF; - s->mail_state = pcf->xclient ? ngx_smtp_helo: ngx_smtp_noxclient; + if (pcf->xclient) { + s->mail_state = ngx_smtp_helo_xclient; + + } else if (s->auth_method == NGX_MAIL_AUTH_NONE) { + s->mail_state = ngx_smtp_helo_from; + + } else { + s->mail_state = ngx_smtp_helo; + } break; - case ngx_smtp_helo: + case ngx_smtp_helo_xclient: ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, "mail proxy send xclient"); s->connection->log->action = "sending XCLIENT to upstream"; - line.len = sizeof("XCLIENT PROTO=SMTP HELO= ADDR= LOGIN= " - "NAME=[UNAVAILABLE]" CRLF) - 1 - + s->esmtp + s->smtp_helo.len - + s->connection->addr_text.len + s->login.len; + line.len = sizeof("XCLIENT ADDR= LOGIN= NAME=" + CRLF) - 1 + + s->connection->addr_text.len + s->login.len + s->host.len; - line.data = ngx_palloc(c->pool, line.len); + line.data = ngx_pnalloc(c->pool, line.len); if (line.data == NULL) { ngx_mail_proxy_internal_server_error(s); return; } + line.len = ngx_sprintf(line.data, + "XCLIENT ADDR=%V%s%V NAME=%V" CRLF, + &s->connection->addr_text, + (s->login.len ? " LOGIN=" : ""), &s->login, &s->host) + - line.data; + if (s->smtp_helo.len) { - line.len = ngx_sprintf(line.data, - "XCLIENT PROTO=%sSMTP HELO=%V ADDR=%V LOGIN=%V " - "NAME=[UNAVAILABLE]" CRLF, - (s->esmtp ? "E" : ""), &s->smtp_helo, - &s->connection->addr_text, &s->login) - - line.data; + s->mail_state = ngx_smtp_xclient_helo; + + } else if (s->auth_method == NGX_MAIL_AUTH_NONE) { + s->mail_state = ngx_smtp_xclient_from; + } else { - line.len = ngx_sprintf(line.data, - "XCLIENT PROTO=SMTP ADDR=%V LOGIN=%V " - "NAME=[UNAVAILABLE]" CRLF, - &s->connection->addr_text, &s->login) - - line.data; + s->mail_state = ngx_smtp_xclient; + } + + break; + + case ngx_smtp_xclient_helo: + ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, + "mail proxy send client ehlo"); + + s->connection->log->action = "sending client HELO/EHLO to upstream"; + + line.len = sizeof("HELO " CRLF) - 1 + s->smtp_helo.len; + + line.data = ngx_pnalloc(c->pool, line.len); + if (line.data == NULL) { + ngx_mail_proxy_internal_server_error(s); + return; + } + + line.len = ngx_sprintf(line.data, + ((s->esmtp) ? "EHLO %V" CRLF : "HELO %V" CRLF), + &s->smtp_helo) + - line.data; + + s->mail_state = (s->auth_method == NGX_MAIL_AUTH_NONE) ? + ngx_smtp_helo_from : ngx_smtp_helo; + + break; + + case ngx_smtp_helo_from: + case ngx_smtp_xclient_from: + ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, + "mail proxy send mail from"); + + s->connection->log->action = "sending MAIL FROM to upstream"; + + line.len = s->smtp_from.len + sizeof(CRLF) - 1; + line.data = ngx_pnalloc(c->pool, line.len); + if (line.data == NULL) { + ngx_mail_proxy_internal_server_error(s); + return; + } + + p = ngx_cpymem(line.data, s->smtp_from.data, s->smtp_from.len); + *p++ = CR; *p = LF; + + s->mail_state = ngx_smtp_from; + + break; + + case ngx_smtp_from: + ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, + "mail proxy send rcpt to"); + + s->connection->log->action = "sending RCPT TO to upstream"; + + line.len = s->smtp_to.len + sizeof(CRLF) - 1; + line.data = ngx_pnalloc(c->pool, line.len); + if (line.data == NULL) { + ngx_mail_proxy_internal_server_error(s); + return; } - s->mail_state = ngx_smtp_xclient; + p = ngx_cpymem(line.data, s->smtp_to.data, s->smtp_to.len); + *p++ = CR; *p = LF; + + s->mail_state = ngx_smtp_to; + break; - case ngx_smtp_noxclient: + case ngx_smtp_helo: case ngx_smtp_xclient: + case ngx_smtp_to: + + b = s->proxy->buffer; - ngx_memcpy(s->proxy->buffer->start, smtp_ok, sizeof(smtp_ok) - 1); + if (s->auth_method == NGX_MAIL_AUTH_NONE) { + b->pos = b->start; - s->proxy->buffer->pos = s->proxy->buffer->start; - s->proxy->buffer->last = s->proxy->buffer->start + sizeof(smtp_ok) - 1; + } else { + ngx_memcpy(b->start, smtp_auth_ok, sizeof(smtp_auth_ok) - 1); + b->last = b->start + sizeof(smtp_auth_ok) - 1; + } s->connection->read->handler = ngx_mail_proxy_handler; s->connection->write->handler = ngx_mail_proxy_handler; @@ -583,8 +662,7 @@ default: #if (NGX_SUPPRESS_WARN) - line.len = 0; - line.data = NULL; + ngx_str_null(&line); #endif break; } @@ -611,7 +689,7 @@ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, wev->log, 0, "mail proxy dummy handler"); - if (ngx_handle_write_event(wev, 0) == NGX_ERROR) { + if (ngx_handle_write_event(wev, 0) != NGX_OK) { c = wev->data; s = c->data; @@ -645,7 +723,7 @@ b->last += n; - if (b->last - b->pos < 5) { + if (b->last - b->pos < 4) { return NGX_AGAIN; } @@ -702,19 +780,31 @@ default: /* NGX_MAIL_SMTP_PROTOCOL */ switch (state) { + case ngx_smtp_start: + if (p[0] == '2' && p[1] == '2' && p[2] == '0') { + return NGX_OK; + } + break; + case ngx_smtp_helo: - case ngx_smtp_noxclient: + case ngx_smtp_helo_xclient: + case ngx_smtp_helo_from: + case ngx_smtp_from: if (p[0] == '2' && p[1] == '5' && p[2] == '0') { return NGX_OK; } break; - case ngx_smtp_start: case ngx_smtp_xclient: - if (p[0] == '2' && p[1] == '2' && p[2] == '0') { + case ngx_smtp_xclient_from: + case ngx_smtp_xclient_helo: + if (p[0] == '2' && (p[1] == '2' || p[1] == '5') && p[2] == '0') { return NGX_OK; } break; + + case ngx_smtp_to: + return NGX_OK; } break; @@ -883,22 +973,22 @@ return; } - if (ngx_handle_write_event(dst->write, 0) == NGX_ERROR) { + if (ngx_handle_write_event(dst->write, 0) != NGX_OK) { ngx_mail_proxy_close_session(s); return; } - if (ngx_handle_read_event(dst->read, 0) == NGX_ERROR) { + if (ngx_handle_read_event(dst->read, 0) != NGX_OK) { ngx_mail_proxy_close_session(s); return; } - if (ngx_handle_write_event(src->write, 0) == NGX_ERROR) { + if (ngx_handle_write_event(src->write, 0) != NGX_OK) { ngx_mail_proxy_close_session(s); return; } - if (ngx_handle_read_event(src->read, 0) == NGX_ERROR) { + if (ngx_handle_read_event(src->read, 0) != NGX_OK) { ngx_mail_proxy_close_session(s); return; } @@ -968,7 +1058,7 @@ pcf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_proxy_conf_t)); if (pcf == NULL) { - return NGX_CONF_ERROR; + return NULL; } pcf->enable = NGX_CONF_UNSET; diff -Nru nginx-0.5.33/src/mail/ngx_mail_smtp_handler.c nginx-0.8.53/src/mail/ngx_mail_smtp_handler.c --- nginx-0.5.33/src/mail/ngx_mail_smtp_handler.c 2007-11-07 14:24:55.000000000 +0000 +++ nginx-0.8.53/src/mail/ngx_mail_smtp_handler.c 2010-05-14 09:56:37.000000000 +0000 @@ -11,6 +11,10 @@ #include +static void ngx_mail_smtp_resolve_addr_handler(ngx_resolver_ctx_t *ctx); +static void ngx_mail_smtp_resolve_name(ngx_event_t *rev); +static void ngx_mail_smtp_resolve_name_handler(ngx_resolver_ctx_t *ctx); +static void ngx_mail_smtp_greeting(ngx_mail_session_t *s, ngx_connection_t *c); static void ngx_mail_smtp_invalid_pipelining(ngx_event_t *rev); static ngx_int_t ngx_mail_smtp_create_buffer(ngx_mail_session_t *s, ngx_connection_t *c); @@ -20,6 +24,8 @@ static ngx_int_t ngx_mail_smtp_mail(ngx_mail_session_t *s, ngx_connection_t *c); static ngx_int_t ngx_mail_smtp_starttls(ngx_mail_session_t *s, ngx_connection_t *c); +static ngx_int_t ngx_mail_smtp_rset(ngx_mail_session_t *s, ngx_connection_t *c); +static ngx_int_t ngx_mail_smtp_rcpt(ngx_mail_session_t *s, ngx_connection_t *c); static ngx_int_t ngx_mail_smtp_discard_command(ngx_mail_session_t *s, ngx_connection_t *c, char *err); @@ -29,6 +35,7 @@ static u_char smtp_ok[] = "250 2.0.0 OK" CRLF; static u_char smtp_bye[] = "221 2.0.0 Bye" CRLF; +static u_char smtp_starttls[] = "220 2.0.0 Start TLS" CRLF; static u_char smtp_next[] = "334 " CRLF; static u_char smtp_username[] = "334 VXNlcm5hbWU6" CRLF; static u_char smtp_password[] = "334 UGFzc3dvcmQ6" CRLF; @@ -37,22 +44,214 @@ "503 5.5.0 Improper use of SMTP command pipelining" CRLF; static u_char smtp_invalid_argument[] = "501 5.5.4 Invalid argument" CRLF; static u_char smtp_auth_required[] = "530 5.7.1 Authentication required" CRLF; +static u_char smtp_bad_sequence[] = "503 5.5.1 Bad sequence of commands" CRLF; + + +static ngx_str_t smtp_unavailable = ngx_string("[UNAVAILABLE]"); +static ngx_str_t smtp_tempunavail = ngx_string("[TEMPUNAVAIL]"); void ngx_mail_smtp_init_session(ngx_mail_session_t *s, ngx_connection_t *c) { + struct sockaddr_in *sin; + ngx_resolver_ctx_t *ctx; + ngx_mail_core_srv_conf_t *cscf; + + cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); + + if (cscf->resolver == NULL) { + s->host = smtp_unavailable; + ngx_mail_smtp_greeting(s, c); + return; + } + + if (c->sockaddr->sa_family != AF_INET) { + s->host = smtp_tempunavail; + ngx_mail_smtp_greeting(s, c); + return; + } + + c->log->action = "in resolving client address"; + + ctx = ngx_resolve_start(cscf->resolver, NULL); + if (ctx == NULL) { + ngx_mail_close_connection(c); + return; + } + + /* AF_INET only */ + + sin = (struct sockaddr_in *) c->sockaddr; + + ctx->addr = sin->sin_addr.s_addr; + ctx->handler = ngx_mail_smtp_resolve_addr_handler; + ctx->data = s; + ctx->timeout = cscf->resolver_timeout; + + if (ngx_resolve_addr(ctx) != NGX_OK) { + ngx_mail_close_connection(c); + } +} + + +static void +ngx_mail_smtp_resolve_addr_handler(ngx_resolver_ctx_t *ctx) +{ + ngx_connection_t *c; + ngx_mail_session_t *s; + + s = ctx->data; + c = s->connection; + + if (ctx->state) { + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "%V could not be resolved (%i: %s)", + &c->addr_text, ctx->state, + ngx_resolver_strerror(ctx->state)); + + if (ctx->state == NGX_RESOLVE_NXDOMAIN) { + s->host = smtp_unavailable; + + } else { + s->host = smtp_tempunavail; + } + + ngx_resolve_addr_done(ctx); + + ngx_mail_smtp_greeting(s, s->connection); + + return; + } + + c->log->action = "in resolving client hostname"; + + s->host.data = ngx_pstrdup(c->pool, &ctx->name); + if (s->host.data == NULL) { + ngx_resolve_addr_done(ctx); + ngx_mail_close_connection(c); + return; + } + + s->host.len = ctx->name.len; + + ngx_resolve_addr_done(ctx); + + ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, + "address resolved: %V", &s->host); + + c->read->handler = ngx_mail_smtp_resolve_name; + + ngx_post_event(c->read, &ngx_posted_events); +} + + +static void +ngx_mail_smtp_resolve_name(ngx_event_t *rev) +{ + ngx_connection_t *c; + ngx_mail_session_t *s; + ngx_resolver_ctx_t *ctx; + ngx_mail_core_srv_conf_t *cscf; + + c = rev->data; + s = c->data; + + cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); + + ctx = ngx_resolve_start(cscf->resolver, NULL); + if (ctx == NULL) { + ngx_mail_close_connection(c); + return; + } + + ctx->name = s->host; + ctx->type = NGX_RESOLVE_A; + ctx->handler = ngx_mail_smtp_resolve_name_handler; + ctx->data = s; + ctx->timeout = cscf->resolver_timeout; + + if (ngx_resolve_name(ctx) != NGX_OK) { + ngx_mail_close_connection(c); + } +} + + +static void +ngx_mail_smtp_resolve_name_handler(ngx_resolver_ctx_t *ctx) +{ + in_addr_t addr; + ngx_uint_t i; + ngx_connection_t *c; + struct sockaddr_in *sin; + ngx_mail_session_t *s; + + s = ctx->data; + c = s->connection; + + if (ctx->state) { + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "\"%V\" could not be resolved (%i: %s)", + &ctx->name, ctx->state, + ngx_resolver_strerror(ctx->state)); + + if (ctx->state == NGX_RESOLVE_NXDOMAIN) { + s->host = smtp_unavailable; + + } else { + s->host = smtp_tempunavail; + } + + } else { + + /* AF_INET only */ + + sin = (struct sockaddr_in *) c->sockaddr; + + for (i = 0; i < ctx->naddrs; i++) { + + addr = ctx->addrs[i]; + + ngx_log_debug4(NGX_LOG_DEBUG_MAIL, c->log, 0, + "name was resolved to %ud.%ud.%ud.%ud", + (ntohl(addr) >> 24) & 0xff, + (ntohl(addr) >> 16) & 0xff, + (ntohl(addr) >> 8) & 0xff, + ntohl(addr) & 0xff); + + if (addr == sin->sin_addr.s_addr) { + goto found; + } + } + + s->host = smtp_unavailable; + } + +found: + + ngx_resolve_name_done(ctx); + + ngx_mail_smtp_greeting(s, c); +} + + +static void +ngx_mail_smtp_greeting(ngx_mail_session_t *s, ngx_connection_t *c) +{ ngx_msec_t timeout; ngx_mail_core_srv_conf_t *cscf; ngx_mail_smtp_srv_conf_t *sscf; + ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, + "smtp greeting for \"%V\"", &s->host); + cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module); timeout = sscf->greeting_delay ? sscf->greeting_delay : cscf->timeout; ngx_add_timer(c->read, timeout); - if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) { + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { ngx_mail_close_connection(c); } @@ -94,7 +293,7 @@ ngx_add_timer(c->read, cscf->timeout); - if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) { + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { ngx_mail_close_connection(c); return; } @@ -120,8 +319,7 @@ return; } - s->out.len = sizeof(smtp_invalid_pipelining) - 1; - s->out.data = smtp_invalid_pipelining; + ngx_str_set(&s->out, smtp_invalid_pipelining); } ngx_mail_send(c->write); @@ -215,8 +413,7 @@ return; } - s->out.len = sizeof(smtp_ok) - 1; - s->out.data = smtp_ok; + ngx_str_set(&s->out, smtp_ok); if (rc == NGX_OK) { switch (s->mail_state) { @@ -236,20 +433,27 @@ case NGX_SMTP_QUIT: s->quit = 1; - s->out.len = sizeof(smtp_bye) - 1; - s->out.data = smtp_bye; + ngx_str_set(&s->out, smtp_bye); break; case NGX_SMTP_MAIL: rc = ngx_mail_smtp_mail(s, c); break; - case NGX_SMTP_NOOP: + case NGX_SMTP_RCPT: + rc = ngx_mail_smtp_rcpt(s, c); + break; + case NGX_SMTP_RSET: + rc = ngx_mail_smtp_rset(s, c); + break; + + case NGX_SMTP_NOOP: break; case NGX_SMTP_STARTTLS: rc = ngx_mail_smtp_starttls(s, c); + ngx_str_set(&s->out, smtp_starttls); break; default: @@ -260,10 +464,9 @@ break; case ngx_smtp_auth_login_username: - rc = ngx_mail_auth_login_username(s, c); + rc = ngx_mail_auth_login_username(s, c, 0); - s->out.len = sizeof(smtp_password) - 1; - s->out.data = smtp_password; + ngx_str_set(&s->out, smtp_password); s->mail_state = ngx_smtp_auth_login_password; break; @@ -294,9 +497,7 @@ case NGX_MAIL_PARSE_INVALID_COMMAND: s->mail_state = ngx_smtp_start; s->state = 0; - - s->out.len = sizeof(smtp_invalid_command) - 1; - s->out.data = smtp_invalid_command; + ngx_str_set(&s->out, smtp_invalid_command); /* fall through */ @@ -319,13 +520,9 @@ { ngx_str_t *arg; ngx_mail_smtp_srv_conf_t *sscf; -#if (NGX_MAIL_SSL) - ngx_mail_ssl_conf_t *sslcf; -#endif if (s->args.nelts != 1) { - s->out.len = sizeof(smtp_invalid_argument) - 1; - s->out.data = smtp_invalid_argument; + ngx_str_set(&s->out, smtp_invalid_argument); s->state = 0; return NGX_OK; } @@ -334,13 +531,16 @@ s->smtp_helo.len = arg[0].len; - s->smtp_helo.data = ngx_palloc(c->pool, arg[0].len); + s->smtp_helo.data = ngx_pnalloc(c->pool, arg[0].len); if (s->smtp_helo.data == NULL) { return NGX_ERROR; } ngx_memcpy(s->smtp_helo.data, arg[0].data, arg[0].len); + ngx_str_null(&s->smtp_from); + ngx_str_null(&s->smtp_to); + sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module); if (s->command == NGX_SMTP_HELO) { @@ -352,6 +552,8 @@ #if (NGX_MAIL_SSL) if (c->ssl == NULL) { + ngx_mail_ssl_conf_t *sslcf; + sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module); if (sslcf->starttls == NGX_MAIL_STARTTLS_ON) { @@ -387,8 +589,7 @@ #endif if (s->args.nelts == 0) { - s->out.len = sizeof(smtp_invalid_argument) - 1; - s->out.data = smtp_invalid_argument; + ngx_str_set(&s->out, smtp_invalid_argument); s->state = 0; return NGX_OK; } @@ -399,16 +600,21 @@ case NGX_MAIL_AUTH_LOGIN: - s->out.len = sizeof(smtp_username) - 1; - s->out.data = smtp_username; + ngx_str_set(&s->out, smtp_username); s->mail_state = ngx_smtp_auth_login_username; return NGX_OK; + case NGX_MAIL_AUTH_LOGIN_USERNAME: + + ngx_str_set(&s->out, smtp_password); + s->mail_state = ngx_smtp_auth_login_password; + + return ngx_mail_auth_login_username(s, c, 1); + case NGX_MAIL_AUTH_PLAIN: - s->out.len = sizeof(smtp_next) - 1; - s->out.data = smtp_next; + ngx_str_set(&s->out, smtp_next); s->mail_state = ngx_smtp_auth_plain; return NGX_OK; @@ -444,10 +650,126 @@ static ngx_int_t ngx_mail_smtp_mail(ngx_mail_session_t *s, ngx_connection_t *c) { - ngx_mail_smtp_log_rejected_command(s, c, "client was rejected: \"%V\""); + u_char ch; + ngx_str_t l; + ngx_uint_t i; + ngx_mail_smtp_srv_conf_t *sscf; + + sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module); + + if (!(sscf->auth_methods & NGX_MAIL_AUTH_NONE_ENABLED)) { + ngx_mail_smtp_log_rejected_command(s, c, "client was rejected: \"%V\""); + ngx_str_set(&s->out, smtp_auth_required); + return NGX_OK; + } + + /* auth none */ + + if (s->smtp_from.len) { + ngx_str_set(&s->out, smtp_bad_sequence); + return NGX_OK; + } + + l.len = s->buffer->last - s->buffer->start; + l.data = s->buffer->start; + + for (i = 0; i < l.len; i++) { + ch = l.data[i]; + + if (ch != CR && ch != LF) { + continue; + } + + l.data[i] = ' '; + } + + while (i) { + if (l.data[i - 1] != ' ') { + break; + } + + i--; + } - s->out.len = sizeof(smtp_auth_required) - 1; - s->out.data = smtp_auth_required; + l.len = i; + + s->smtp_from.len = l.len; + + s->smtp_from.data = ngx_pnalloc(c->pool, l.len); + if (s->smtp_from.data == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(s->smtp_from.data, l.data, l.len); + + ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, + "smtp mail from:\"%V\"", &s->smtp_from); + + ngx_str_set(&s->out, smtp_ok); + + return NGX_OK; +} + + +static ngx_int_t +ngx_mail_smtp_rcpt(ngx_mail_session_t *s, ngx_connection_t *c) +{ + u_char ch; + ngx_str_t l; + ngx_uint_t i; + + if (s->smtp_from.len == 0) { + ngx_str_set(&s->out, smtp_bad_sequence); + return NGX_OK; + } + + l.len = s->buffer->last - s->buffer->start; + l.data = s->buffer->start; + + for (i = 0; i < l.len; i++) { + ch = l.data[i]; + + if (ch != CR && ch != LF) { + continue; + } + + l.data[i] = ' '; + } + + while (i) { + if (l.data[i - 1] != ' ') { + break; + } + + i--; + } + + l.len = i; + + s->smtp_to.len = l.len; + + s->smtp_to.data = ngx_pnalloc(c->pool, l.len); + if (s->smtp_to.data == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(s->smtp_to.data, l.data, l.len); + + ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, + "smtp rcpt to:\"%V\"", &s->smtp_to); + + s->auth_method = NGX_MAIL_AUTH_NONE; + + return NGX_DONE; +} + + +static ngx_int_t +ngx_mail_smtp_rset(ngx_mail_session_t *s, ngx_connection_t *c) +{ + ngx_str_null(&s->smtp_from); + ngx_str_null(&s->smtp_to); + ngx_str_set(&s->out, smtp_ok); return NGX_OK; } @@ -468,8 +790,9 @@ * obtained from client before STARTTLS. */ - s->smtp_helo.len = 0; - s->smtp_helo.data = NULL; + ngx_str_null(&s->smtp_helo); + ngx_str_null(&s->smtp_from); + ngx_str_null(&s->smtp_to); c->read->handler = ngx_mail_starttls_handler; return NGX_OK; @@ -500,7 +823,7 @@ } if (n == NGX_AGAIN) { - if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) { + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { ngx_mail_session_internal_server_error(s); return NGX_ERROR; } diff -Nru nginx-0.5.33/src/mail/ngx_mail_smtp_module.c nginx-0.8.53/src/mail/ngx_mail_smtp_module.c --- nginx-0.5.33/src/mail/ngx_mail_smtp_module.c 2007-11-07 14:24:55.000000000 +0000 +++ nginx-0.8.53/src/mail/ngx_mail_smtp_module.c 2008-11-13 13:25:34.000000000 +0000 @@ -20,6 +20,7 @@ { ngx_string("plain"), NGX_MAIL_AUTH_PLAIN_ENABLED }, { ngx_string("login"), NGX_MAIL_AUTH_LOGIN_ENABLED }, { ngx_string("cram-md5"), NGX_MAIL_AUTH_CRAM_MD5_ENABLED }, + { ngx_string("none"), NGX_MAIL_AUTH_NONE_ENABLED }, { ngx_null_string, 0 } }; @@ -28,7 +29,8 @@ ngx_string("PLAIN"), ngx_string("LOGIN"), ngx_null_string, /* APOP */ - ngx_string("CRAM-MD5") + ngx_string("CRAM-MD5"), + ngx_null_string /* NONE */ }; @@ -136,10 +138,10 @@ ngx_mail_smtp_srv_conf_t *prev = parent; ngx_mail_smtp_srv_conf_t *conf = child; - u_char *p, *auth; + u_char *p, *auth, *last; size_t size; ngx_str_t *c; - ngx_uint_t i, m; + ngx_uint_t i, m, auth_enabled; ngx_mail_core_srv_conf_t *cscf; ngx_conf_merge_size_value(conf->client_buffer_size, @@ -160,7 +162,7 @@ size = sizeof("220 ESMTP ready" CRLF) - 1 + cscf->server_name.len; - p = ngx_palloc(cf->pool, size); + p = ngx_pnalloc(cf->pool, size); if (p == NULL) { return NGX_CONF_ERROR; } @@ -175,7 +177,7 @@ size = sizeof("250 " CRLF) - 1 + cscf->server_name.len; - p = ngx_palloc(cf->pool, size); + p = ngx_pnalloc(cf->pool, size); if (p == NULL) { return NGX_CONF_ERROR; } @@ -192,24 +194,30 @@ conf->capabilities = prev->capabilities; } - size = sizeof("250-") - 1 + cscf->server_name.len + sizeof(CRLF) - 1 - + sizeof("250 AUTH") - 1 + sizeof(CRLF) - 1; + size = sizeof("250-") - 1 + cscf->server_name.len + sizeof(CRLF) - 1; c = conf->capabilities.elts; for (i = 0; i < conf->capabilities.nelts; i++) { size += sizeof("250 ") - 1 + c[i].len + sizeof(CRLF) - 1; } + auth_enabled = 0; + for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0; m <= NGX_MAIL_AUTH_CRAM_MD5_ENABLED; m <<= 1, i++) { if (m & conf->auth_methods) { size += 1 + ngx_mail_smtp_auth_methods_names[i].len; + auth_enabled = 1; } } - p = ngx_palloc(cf->pool, size); + if (auth_enabled) { + size += sizeof("250 AUTH") - 1 + sizeof(CRLF) - 1; + } + + p = ngx_pnalloc(cf->pool, size); if (p == NULL) { return NGX_CONF_ERROR; } @@ -217,11 +225,14 @@ conf->capability.len = size; conf->capability.data = p; + last = p; + *p++ = '2'; *p++ = '5'; *p++ = '0'; *p++ = '-'; p = ngx_cpymem(p, cscf->server_name.data, cscf->server_name.len); *p++ = CR; *p++ = LF; for (i = 0; i < conf->capabilities.nelts; i++) { + last = p; *p++ = '2'; *p++ = '5'; *p++ = '0'; *p++ = '-'; p = ngx_cpymem(p, c[i].data, c[i].len); *p++ = CR; *p++ = LF; @@ -229,25 +240,32 @@ auth = p; - *p++ = '2'; *p++ = '5'; *p++ = '0'; *p++ = ' '; - *p++ = 'A'; *p++ = 'U'; *p++ = 'T'; *p++ = 'H'; + if (auth_enabled) { + last = p; - for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0; - m <= NGX_MAIL_AUTH_CRAM_MD5_ENABLED; - m <<= 1, i++) - { - if (m & conf->auth_methods) { - *p++ = ' '; - p = ngx_cpymem(p, ngx_mail_smtp_auth_methods_names[i].data, - ngx_mail_smtp_auth_methods_names[i].len); + *p++ = '2'; *p++ = '5'; *p++ = '0'; *p++ = ' '; + *p++ = 'A'; *p++ = 'U'; *p++ = 'T'; *p++ = 'H'; + + for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0; + m <= NGX_MAIL_AUTH_CRAM_MD5_ENABLED; + m <<= 1, i++) + { + if (m & conf->auth_methods) { + *p++ = ' '; + p = ngx_cpymem(p, ngx_mail_smtp_auth_methods_names[i].data, + ngx_mail_smtp_auth_methods_names[i].len); + } } - } - *p++ = CR; *p = LF; + *p++ = CR; *p = LF; + + } else { + last[3] = ' '; + } size += sizeof("250 STARTTLS" CRLF) - 1; - p = ngx_palloc(cf->pool, size); + p = ngx_pnalloc(cf->pool, size); if (p == NULL) { return NGX_CONF_ERROR; } @@ -255,20 +273,19 @@ conf->starttls_capability.len = size; conf->starttls_capability.data = p; - p = ngx_cpymem(p, conf->capability.data, - conf->capability.len); + p = ngx_cpymem(p, conf->capability.data, conf->capability.len); p = ngx_cpymem(p, "250 STARTTLS" CRLF, sizeof("250 STARTTLS" CRLF) - 1); *p++ = CR; *p = LF; p = conf->starttls_capability.data - + (auth - conf->capability.data) + 3; + + (last - conf->capability.data) + 3; *p = '-'; size = (auth - conf->capability.data) + sizeof("250 STARTTLS" CRLF) - 1; - p = ngx_palloc(cf->pool, size); + p = ngx_pnalloc(cf->pool, size); if (p == NULL) { return NGX_CONF_ERROR; } @@ -276,10 +293,15 @@ conf->starttls_only_capability.len = size; conf->starttls_only_capability.data = p; - p = ngx_cpymem(p, conf->capability.data, - auth - conf->capability.data); + p = ngx_cpymem(p, conf->capability.data, auth - conf->capability.data); ngx_memcpy(p, "250 STARTTLS" CRLF, sizeof("250 STARTTLS" CRLF) - 1); + if (last < auth) { + p = conf->starttls_only_capability.data + + (last - conf->capability.data) + 3; + *p = '-'; + } + return NGX_CONF_OK; } diff -Nru nginx-0.5.33/src/mail/ngx_mail_ssl_module.c nginx-0.8.53/src/mail/ngx_mail_ssl_module.c --- nginx-0.5.33/src/mail/ngx_mail_ssl_module.c 2007-11-07 14:24:55.000000000 +0000 +++ nginx-0.8.53/src/mail/ngx_mail_ssl_module.c 2010-05-14 09:56:37.000000000 +0000 @@ -9,24 +9,18 @@ #include -#define NGX_DEFLAUT_CERTIFICATE "cert.pem" -#define NGX_DEFLAUT_CERTIFICATE_KEY "cert.pem" -#define NGX_DEFLAUT_CIPHERS "ALL:!ADH:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP" +#define NGX_DEFAULT_CIPHERS "HIGH:!ADH:!MD5" static void *ngx_mail_ssl_create_conf(ngx_conf_t *cf); static char *ngx_mail_ssl_merge_conf(ngx_conf_t *cf, void *parent, void *child); -static char *ngx_mail_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd, - void *conf); - -#if !defined (SSL_OP_CIPHER_SERVER_PREFERENCE) -static char *ngx_mail_ssl_nosupported(ngx_conf_t *cf, ngx_command_t *cmd, +static char *ngx_mail_ssl_enable(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static char *ngx_mail_ssl_starttls(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static char *ngx_mail_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); - -static char ngx_mail_ssl_openssl097[] = "OpenSSL 0.9.7 and higher"; - -#endif static ngx_conf_enum_t ngx_http_starttls_state[] = { @@ -50,14 +44,14 @@ { ngx_string("ssl"), NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG, - ngx_conf_set_flag_slot, + ngx_mail_ssl_enable, NGX_MAIL_SRV_CONF_OFFSET, offsetof(ngx_mail_ssl_conf_t, enable), NULL }, { ngx_string("starttls"), NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1, - ngx_conf_set_enum_slot, + ngx_mail_ssl_starttls, NGX_MAIL_SRV_CONF_OFFSET, offsetof(ngx_mail_ssl_conf_t, starttls), ngx_http_starttls_state }, @@ -76,6 +70,13 @@ offsetof(ngx_mail_ssl_conf_t, certificate_key), NULL }, + { ngx_string("ssl_dhparam"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_str_slot, + NGX_MAIL_SRV_CONF_OFFSET, + offsetof(ngx_mail_ssl_conf_t, dhparam), + NULL }, + { ngx_string("ssl_protocols"), NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE, ngx_conf_set_bitmask_slot, @@ -92,14 +93,10 @@ { ngx_string("ssl_prefer_server_ciphers"), NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG, -#ifdef SSL_OP_CIPHER_SERVER_PREFERENCE ngx_conf_set_flag_slot, NGX_MAIL_SRV_CONF_OFFSET, offsetof(ngx_mail_ssl_conf_t, prefer_server_ciphers), NULL }, -#else - ngx_mail_ssl_nosupported, 0, 0, ngx_mail_ssl_openssl097 }, -#endif { ngx_string("ssl_session_cache"), NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE12, @@ -156,24 +153,22 @@ scf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_ssl_conf_t)); if (scf == NULL) { - return NGX_CONF_ERROR; + return NULL; } /* * set by ngx_pcalloc(): * * scf->protocols = 0; - * scf->certificate.len = 0; - * scf->certificate.data = NULL; - * scf->certificate_key.len = 0; - * scf->certificate_key.data = NULL; - * scf->ciphers.len = 0; - * scf->ciphers.data = NULL; + * scf->certificate = { 0, NULL }; + * scf->certificate_key = { 0, NULL }; + * scf->dhparam = { 0, NULL }; + * scf->ciphers = { 0, NULL }; * scf->shm_zone = NULL; */ scf->enable = NGX_CONF_UNSET; - scf->starttls = NGX_CONF_UNSET; + scf->starttls = NGX_CONF_UNSET_UINT; scf->prefer_server_ciphers = NGX_CONF_UNSET; scf->builtin_session_cache = NGX_CONF_UNSET; scf->session_timeout = NGX_CONF_UNSET; @@ -188,14 +183,12 @@ ngx_mail_ssl_conf_t *prev = parent; ngx_mail_ssl_conf_t *conf = child; + char *mode; ngx_pool_cleanup_t *cln; ngx_conf_merge_value(conf->enable, prev->enable, 0); - ngx_conf_merge_value(conf->starttls, prev->starttls, NGX_MAIL_STARTTLS_OFF); - - if (conf->enable == 0 && conf->starttls == NGX_MAIL_STARTTLS_OFF) { - return NGX_CONF_OK; - } + ngx_conf_merge_uint_value(conf->starttls, prev->starttls, + NGX_MAIL_STARTTLS_OFF); ngx_conf_merge_value(conf->session_timeout, prev->session_timeout, 300); @@ -204,20 +197,61 @@ prev->prefer_server_ciphers, 0); ngx_conf_merge_bitmask_value(conf->protocols, prev->protocols, - (NGX_CONF_BITMASK_SET - |NGX_SSL_SSLv2|NGX_SSL_SSLv3|NGX_SSL_TLSv1)); + (NGX_CONF_BITMASK_SET|NGX_SSL_SSLv3|NGX_SSL_TLSv1)); - ngx_conf_merge_str_value(conf->certificate, prev->certificate, - NGX_DEFLAUT_CERTIFICATE); + ngx_conf_merge_str_value(conf->certificate, prev->certificate, ""); + ngx_conf_merge_str_value(conf->certificate_key, prev->certificate_key, ""); - ngx_conf_merge_str_value(conf->certificate_key, prev->certificate_key, - NGX_DEFLAUT_CERTIFICATE_KEY); + ngx_conf_merge_str_value(conf->dhparam, prev->dhparam, ""); - ngx_conf_merge_str_value(conf->ciphers, prev->ciphers, NGX_DEFLAUT_CIPHERS); + ngx_conf_merge_str_value(conf->ciphers, prev->ciphers, NGX_DEFAULT_CIPHERS); conf->ssl.log = cf->log; + if (conf->enable) { + mode = "ssl"; + + } else if (conf->starttls != NGX_MAIL_STARTTLS_OFF) { + mode = "starttls"; + + } else { + mode = ""; + } + + if (*mode) { + + if (conf->certificate.len == 0) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "no \"ssl_certificate\" is defined for " + "the \"%s\" directive in %s:%ui", + mode, conf->file, conf->line); + return NGX_CONF_ERROR; + } + + if (conf->certificate_key.len == 0) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "no \"ssl_certificate_key\" is defined for " + "the \"%s\" directive in %s:%ui", + mode, conf->file, conf->line); + return NGX_CONF_ERROR; + } + + } else { + + if (conf->certificate.len == 0) { + return NGX_CONF_OK; + } + + if (conf->certificate_key.len == 0) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "no \"ssl_certificate_key\" is defined " + "for certificate \"%V\"", + &conf->certificate); + return NGX_CONF_ERROR; + } + } + if (ngx_ssl_create(&conf->ssl, conf->protocols, NULL) != NGX_OK) { return NGX_CONF_ERROR; } @@ -248,21 +282,20 @@ } } -#ifdef SSL_OP_CIPHER_SERVER_PREFERENCE - if (conf->prefer_server_ciphers) { SSL_CTX_set_options(conf->ssl.ctx, SSL_OP_CIPHER_SERVER_PREFERENCE); } -#endif - if (ngx_ssl_generate_rsa512_key(&conf->ssl) != NGX_OK) { return NGX_CONF_ERROR; } + if (ngx_ssl_dhparam(cf, &conf->ssl, &conf->dhparam) != NGX_OK) { + return NGX_CONF_ERROR; + } + ngx_conf_merge_value(conf->builtin_session_cache, - prev->builtin_session_cache, - NGX_SSL_DFLT_BUILTIN_SCACHE); + prev->builtin_session_cache, NGX_SSL_NONE_SCACHE); if (conf->shm_zone == NULL) { conf->shm_zone = prev->shm_zone; @@ -281,6 +314,58 @@ static char * +ngx_mail_ssl_enable(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_mail_ssl_conf_t *scf = conf; + + char *rv; + + rv = ngx_conf_set_flag_slot(cf, cmd, conf); + + if (rv != NGX_CONF_OK) { + return rv; + } + + if (scf->enable && (ngx_int_t) scf->starttls > NGX_MAIL_STARTTLS_OFF) { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "\"starttls\" directive conflicts with \"ssl on\""); + return NGX_CONF_ERROR; + } + + scf->file = cf->conf_file->file.name.data; + scf->line = cf->conf_file->line; + + return NGX_CONF_OK; +} + + +static char * +ngx_mail_ssl_starttls(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_mail_ssl_conf_t *scf = conf; + + char *rv; + + rv = ngx_conf_set_enum_slot(cf, cmd, conf); + + if (rv != NGX_CONF_OK) { + return rv; + } + + if (scf->enable == 1 && (ngx_int_t) scf->starttls > NGX_MAIL_STARTTLS_OFF) { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "\"ssl\" directive conflicts with \"starttls\""); + return NGX_CONF_ERROR; + } + + scf->file = cf->conf_file->file.name.data; + scf->line = cf->conf_file->line; + + return NGX_CONF_OK; +} + + +static char * ngx_mail_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_mail_ssl_conf_t *scf = conf; @@ -294,6 +379,16 @@ for (i = 1; i < cf->args->nelts; i++) { + if (ngx_strcmp(value[i].data, "off") == 0) { + scf->builtin_session_cache = NGX_SSL_NO_SCACHE; + continue; + } + + if (ngx_strcmp(value[i].data, "none") == 0) { + scf->builtin_session_cache = NGX_SSL_NONE_SCACHE; + continue; + } + if (ngx_strcmp(value[i].data, "builtin") == 0) { scf->builtin_session_cache = NGX_SSL_DFLT_BUILTIN_SCACHE; continue; @@ -378,18 +473,3 @@ return NGX_CONF_ERROR; } - - -#if !defined (SSL_OP_CIPHER_SERVER_PREFERENCE) - -static char * -ngx_mail_ssl_nosupported(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) -{ - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "\"%V\" directive is available only in %s,", - &cmd->name, cmd->post); - - return NGX_CONF_ERROR; -} - -#endif diff -Nru nginx-0.5.33/src/mail/ngx_mail_ssl_module.h nginx-0.8.53/src/mail/ngx_mail_ssl_module.h --- nginx-0.5.33/src/mail/ngx_mail_ssl_module.h 2007-03-19 13:36:56.000000000 +0000 +++ nginx-0.8.53/src/mail/ngx_mail_ssl_module.h 2008-09-01 14:19:01.000000000 +0000 @@ -20,12 +20,11 @@ typedef struct { ngx_flag_t enable; + ngx_flag_t prefer_server_ciphers; ngx_ssl_t ssl; - ngx_flag_t prefer_server_ciphers; - ngx_flag_t starttls; - + ngx_uint_t starttls; ngx_uint_t protocols; ssize_t builtin_session_cache; @@ -34,10 +33,14 @@ ngx_str_t certificate; ngx_str_t certificate_key; + ngx_str_t dhparam; ngx_str_t ciphers; ngx_shm_zone_t *shm_zone; + + u_char *file; + ngx_uint_t line; } ngx_mail_ssl_conf_t; diff -Nru nginx-0.5.33/src/misc/ngx_cpp_test_module.cpp nginx-0.8.53/src/misc/ngx_cpp_test_module.cpp --- nginx-0.5.33/src/misc/ngx_cpp_test_module.cpp 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/src/misc/ngx_cpp_test_module.cpp 2008-08-05 19:32:50.000000000 +0000 @@ -0,0 +1,27 @@ + +// stub module to test header files' C++ compatibilty + +extern "C" { + #include + #include + #include + #include + #include + + #include + + #include + #include + #include + #include +} + +// nginx header files should go before other, because they define 64-bit off_t +// #include + + +void +ngx_cpp_test_handler(void *data) +{ + return; +} diff -Nru nginx-0.5.33/src/misc/ngx_google_perftools_module.c nginx-0.8.53/src/misc/ngx_google_perftools_module.c --- nginx-0.5.33/src/misc/ngx_google_perftools_module.c 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/src/misc/ngx_google_perftools_module.c 2010-03-26 21:17:26.000000000 +0000 @@ -0,0 +1,125 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include + +/* + * declare Profiler interface here because + * is C++ header file + */ + +int ProfilerStart(u_char* fname); +void ProfilerStop(void); +void ProfilerRegisterThread(void); + + +static void *ngx_google_perftools_create_conf(ngx_cycle_t *cycle); +static ngx_int_t ngx_google_perftools_worker(ngx_cycle_t *cycle); + + +typedef struct { + ngx_str_t profiles; +} ngx_google_perftools_conf_t; + + +static ngx_command_t ngx_google_perftools_commands[] = { + + { ngx_string("google_perftools_profiles"), + NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1, + ngx_conf_set_str_slot, + 0, + offsetof(ngx_google_perftools_conf_t, profiles), + NULL }, + + ngx_null_command +}; + + +static ngx_core_module_t ngx_google_perftools_module_ctx = { + ngx_string("google_perftools"), + ngx_google_perftools_create_conf, + NULL +}; + + +ngx_module_t ngx_google_perftools_module = { + NGX_MODULE_V1, + &ngx_google_perftools_module_ctx, /* module context */ + ngx_google_perftools_commands, /* module directives */ + NGX_CORE_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + ngx_google_perftools_worker, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; + + +static void * +ngx_google_perftools_create_conf(ngx_cycle_t *cycle) +{ + ngx_google_perftools_conf_t *gptcf; + + gptcf = ngx_pcalloc(cycle->pool, sizeof(ngx_google_perftools_conf_t)); + if (gptcf == NULL) { + return NULL; + } + + /* + * set by ngx_pcalloc() + * + * gptcf->profiles = { 0, NULL }; + */ + + return gptcf; +} + + +static ngx_int_t +ngx_google_perftools_worker(ngx_cycle_t *cycle) +{ + u_char *profile; + ngx_google_perftools_conf_t *gptcf; + + gptcf = (ngx_google_perftools_conf_t *) + ngx_get_conf(cycle->conf_ctx, ngx_google_perftools_module); + + if (gptcf->profiles.len == 0) { + return NGX_OK; + } + + profile = ngx_alloc(gptcf->profiles.len + NGX_INT_T_LEN + 2, cycle->log); + if (profile == NULL) { + return NGX_OK; + } + + if (getenv("CPUPROFILE")) { + /* disable inherited Profiler enabled in master process */ + ProfilerStop(); + } + + ngx_sprintf(profile, "%V.%d%Z", &gptcf->profiles, ngx_pid); + + if (ProfilerStart(profile)) { + /* start ITIMER_PROF timer */ + ProfilerRegisterThread(); + + } else { + ngx_log_error(NGX_LOG_CRIT, cycle->log, ngx_errno, + "ProfilerStart(%s) failed", profile); + } + + ngx_free(profile); + + return NGX_OK; +} + + +/* ProfilerStop() is called on Profiler destruction */ diff -Nru nginx-0.5.33/src/os/unix/ngx_aio.h nginx-0.8.53/src/os/unix/ngx_aio.h --- nginx-0.5.33/src/os/unix/ngx_aio.h 2006-08-30 10:39:17.000000000 +0000 +++ nginx-0.8.53/src/os/unix/ngx_aio.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,21 +0,0 @@ - -/* - * Copyright (C) Igor Sysoev - */ - - -#ifndef _NGX_AIO_H_INCLUDED_ -#define _NGX_AIO_H_INCLUDED_ - - -#include - - -ssize_t ngx_aio_read(ngx_connection_t *c, u_char *buf, size_t size); -ssize_t ngx_aio_read_chain(ngx_connection_t *c, ngx_chain_t *cl); -ssize_t ngx_aio_write(ngx_connection_t *c, u_char *buf, size_t size); -ngx_chain_t *ngx_aio_write_chain(ngx_connection_t *c, ngx_chain_t *in, - off_t limit); - - -#endif /* _NGX_AIO_H_INCLUDED_ */ diff -Nru nginx-0.5.33/src/os/unix/ngx_aio_read.c nginx-0.8.53/src/os/unix/ngx_aio_read.c --- nginx-0.5.33/src/os/unix/ngx_aio_read.c 2006-08-30 10:39:17.000000000 +0000 +++ nginx-0.8.53/src/os/unix/ngx_aio_read.c 2009-08-25 09:09:13.000000000 +0000 @@ -7,20 +7,10 @@ #include #include #include -#include -#if (NGX_HAVE_KQUEUE) -#include -#endif +extern int ngx_kqueue; -/* - * the ready data requires 3 syscalls: - * aio_write(), aio_error(), aio_return() - * the non-ready data requires 4 (kqueue) or 5 syscalls: - * aio_write(), aio_error(), notifiction, aio_error(), aio_return() - * timeout, aio_cancel(), aio_error() - */ ssize_t ngx_aio_read(ngx_connection_t *c, u_char *buf, size_t size) diff -Nru nginx-0.5.33/src/os/unix/ngx_aio_read_chain.c nginx-0.8.53/src/os/unix/ngx_aio_read_chain.c --- nginx-0.5.33/src/os/unix/ngx_aio_read_chain.c 2006-08-30 10:39:17.000000000 +0000 +++ nginx-0.8.53/src/os/unix/ngx_aio_read_chain.c 2009-08-25 09:09:13.000000000 +0000 @@ -7,7 +7,6 @@ #include #include #include -#include ssize_t diff -Nru nginx-0.5.33/src/os/unix/ngx_aio_write.c nginx-0.8.53/src/os/unix/ngx_aio_write.c --- nginx-0.5.33/src/os/unix/ngx_aio_write.c 2006-08-30 10:39:17.000000000 +0000 +++ nginx-0.8.53/src/os/unix/ngx_aio_write.c 2009-08-25 09:09:13.000000000 +0000 @@ -7,20 +7,10 @@ #include #include #include -#include -#if (NGX_HAVE_KQUEUE) -#include -#endif +extern int ngx_kqueue; -/* - * the ready data requires 3 syscalls: - * aio_write(), aio_error(), aio_return() - * the non-ready data requires 4 (kqueue) or 5 syscalls: - * aio_write(), aio_error(), notifiction, aio_error(), aio_return() - * timeout, aio_cancel(), aio_error() - */ ssize_t ngx_aio_write(ngx_connection_t *c, u_char *buf, size_t size) diff -Nru nginx-0.5.33/src/os/unix/ngx_aio_write_chain.c nginx-0.8.53/src/os/unix/ngx_aio_write_chain.c --- nginx-0.5.33/src/os/unix/ngx_aio_write_chain.c 2007-11-07 13:54:40.000000000 +0000 +++ nginx-0.8.53/src/os/unix/ngx_aio_write_chain.c 2009-08-25 09:09:13.000000000 +0000 @@ -7,7 +7,6 @@ #include #include #include -#include ngx_chain_t * diff -Nru nginx-0.5.33/src/os/unix/ngx_alloc.c nginx-0.8.53/src/os/unix/ngx_alloc.c --- nginx-0.5.33/src/os/unix/ngx_alloc.c 2006-11-20 08:51:45.000000000 +0000 +++ nginx-0.8.53/src/os/unix/ngx_alloc.c 2009-11-16 16:00:52.000000000 +0000 @@ -21,7 +21,7 @@ p = malloc(size); if (p == NULL) { ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, - "malloc() %uz bytes failed", size); + "malloc(%uz) failed", size); } ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, log, 0, "malloc: %p:%uz", p, size); @@ -51,15 +51,18 @@ ngx_memalign(size_t alignment, size_t size, ngx_log_t *log) { void *p; + int err; - if (posix_memalign(&p, alignment, size) == -1) { - ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, - "posix_memalign() %uz bytes aligned to %uz failed", - size, alignment); + err = posix_memalign(&p, alignment, size); + + if (err) { + ngx_log_error(NGX_LOG_EMERG, log, err, + "posix_memalign(%uz, %uz) failed", alignment, size); + p = NULL; } - ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, log, 0, - "posix_memalign: %p:%uz", p, size); + ngx_log_debug3(NGX_LOG_DEBUG_ALLOC, log, 0, + "posix_memalign: %p:%uz @%uz", p, size, alignment); return p; } @@ -74,12 +77,11 @@ p = memalign(alignment, size); if (p == NULL) { ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, - "memalign() %uz bytes aligned to %uz failed", - size, alignment); + "memalign(%uz, %uz) failed", alignment, size); } - ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, log, 0, - "memalign: %p:%uz", p, size); + ngx_log_debug3(NGX_LOG_DEBUG_ALLOC, log, 0, + "memalign: %p:%uz @%uz", p, size, alignment); return p; } diff -Nru nginx-0.5.33/src/os/unix/ngx_alloc.h nginx-0.8.53/src/os/unix/ngx_alloc.h --- nginx-0.5.33/src/os/unix/ngx_alloc.h 2006-11-20 08:51:45.000000000 +0000 +++ nginx-0.8.53/src/os/unix/ngx_alloc.h 2008-07-30 12:34:04.000000000 +0000 @@ -21,8 +21,8 @@ /* * Linux has memalign() or posix_memalign() * Solaris has memalign() - * FreeBSD has not memalign() or posix_memalign() but its malloc() alignes - * allocations bigger than page size at the page boundary. + * FreeBSD 7.0 has posix_memalign(), besides, early version's malloc() + * aligns allocations bigger than page size at the page boundary */ #if (NGX_HAVE_POSIX_MEMALIGN || NGX_HAVE_MEMALIGN) diff -Nru nginx-0.5.33/src/os/unix/ngx_atomic.h nginx-0.8.53/src/os/unix/ngx_atomic.h --- nginx-0.5.33/src/os/unix/ngx_atomic.h 2007-07-22 08:47:45.000000000 +0000 +++ nginx-0.8.53/src/os/unix/ngx_atomic.h 2009-12-07 15:32:38.000000000 +0000 @@ -12,7 +12,32 @@ #include -#if (NGX_DARWIN_ATOMIC) +#if (NGX_HAVE_LIBATOMIC) + +#define AO_REQUIRE_CAS +#include + +#define NGX_HAVE_ATOMIC_OPS 1 + +typedef long ngx_atomic_int_t; +typedef AO_t ngx_atomic_uint_t; +typedef volatile ngx_atomic_uint_t ngx_atomic_t; + +#if (NGX_PTR_SIZE == 8) +#define NGX_ATOMIC_T_LEN (sizeof("-9223372036854775808") - 1) +#else +#define NGX_ATOMIC_T_LEN (sizeof("-2147483648") - 1) +#endif + +#define ngx_atomic_cmp_set(lock, old, new) \ + AO_compare_and_swap(lock, old, new) +#define ngx_atomic_fetch_add(value, add) \ + AO_fetch_and_add(value, add) +#define ngx_memory_barrier() AO_nop() +#define ngx_cpu_pause() + + +#elif (NGX_DARWIN_ATOMIC) /* * use Darwin 8 atomic(3) and barrier(3) operations @@ -21,13 +46,8 @@ #include -/* "bool" conflicts with perl's CORE/handy.h - * "true" and "false" conflict with nginx, and of course we can rename them, - * but we need to undef "bool" anyway - */ +/* "bool" conflicts with perl's CORE/handy.h */ #undef bool -#undef true -#undef false #define NGX_HAVE_ATOMIC_OPS 1 @@ -36,7 +56,7 @@ typedef int64_t ngx_atomic_int_t; typedef uint64_t ngx_atomic_uint_t; -#define NGX_ATOMIC_T_LEN sizeof("-9223372036854775808") - 1 +#define NGX_ATOMIC_T_LEN (sizeof("-9223372036854775808") - 1) #define ngx_atomic_cmp_set(lock, old, new) \ OSAtomicCompareAndSwap64Barrier(old, new, (int64_t *) lock) @@ -48,7 +68,7 @@ typedef int32_t ngx_atomic_int_t; typedef uint32_t ngx_atomic_uint_t; -#define NGX_ATOMIC_T_LEN sizeof("-2147483648") - 1 +#define NGX_ATOMIC_T_LEN (sizeof("-2147483648") - 1) #define ngx_atomic_cmp_set(lock, old, new) \ OSAtomicCompareAndSwap32Barrier(old, new, (int32_t *) lock) @@ -65,15 +85,45 @@ typedef volatile ngx_atomic_uint_t ngx_atomic_t; -#else /* !(NGX_DARWIN) */ +#elif (NGX_HAVE_GCC_ATOMIC) + +/* GCC 4.1 builtin atomic operations */ + +#define NGX_HAVE_ATOMIC_OPS 1 + +typedef long ngx_atomic_int_t; +typedef unsigned long ngx_atomic_uint_t; + +#if (NGX_PTR_SIZE == 8) +#define NGX_ATOMIC_T_LEN (sizeof("-9223372036854775808") - 1) +#else +#define NGX_ATOMIC_T_LEN (sizeof("-2147483648") - 1) +#endif + +typedef volatile ngx_atomic_uint_t ngx_atomic_t; + + +#define ngx_atomic_cmp_set(lock, old, set) \ + __sync_bool_compare_and_swap(lock, old, set) +#define ngx_atomic_fetch_add(value, add) \ + __sync_fetch_and_add(value, add) -#if ( __i386__ || __i386 ) +#define ngx_memory_barrier() __sync_synchronize() + +#if ( __i386__ || __i386 || __amd64__ || __amd64 ) +#define ngx_cpu_pause() __asm__ ("pause") +#else +#define ngx_cpu_pause() +#endif + + +#elif ( __i386__ || __i386 ) typedef int32_t ngx_atomic_int_t; typedef uint32_t ngx_atomic_uint_t; typedef volatile ngx_atomic_uint_t ngx_atomic_t; -#define NGX_ATOMIC_T_LEN sizeof("-2147483648") - 1 +#define NGX_ATOMIC_T_LEN (sizeof("-2147483648") - 1) #if ( __SUNPRO_C ) @@ -114,7 +164,7 @@ typedef int64_t ngx_atomic_int_t; typedef uint64_t ngx_atomic_uint_t; typedef volatile ngx_atomic_uint_t ngx_atomic_t; -#define NGX_ATOMIC_T_LEN sizeof("-9223372036854775808") - 1 +#define NGX_ATOMIC_T_LEN (sizeof("-9223372036854775808") - 1) #if ( __SUNPRO_C ) @@ -156,13 +206,13 @@ typedef int64_t ngx_atomic_int_t; typedef uint64_t ngx_atomic_uint_t; -#define NGX_ATOMIC_T_LEN sizeof("-9223372036854775808") - 1 +#define NGX_ATOMIC_T_LEN (sizeof("-9223372036854775808") - 1) #else typedef int32_t ngx_atomic_int_t; typedef uint32_t ngx_atomic_uint_t; -#define NGX_ATOMIC_T_LEN sizeof("-2147483648") - 1 +#define NGX_ATOMIC_T_LEN (sizeof("-2147483648") - 1) #endif @@ -193,13 +243,13 @@ typedef int64_t ngx_atomic_int_t; typedef uint64_t ngx_atomic_uint_t; -#define NGX_ATOMIC_T_LEN sizeof("-9223372036854775808") - 1 +#define NGX_ATOMIC_T_LEN (sizeof("-9223372036854775808") - 1) #else typedef int32_t ngx_atomic_int_t; typedef uint32_t ngx_atomic_uint_t; -#define NGX_ATOMIC_T_LEN sizeof("-2147483648") - 1 +#define NGX_ATOMIC_T_LEN (sizeof("-2147483648") - 1) #endif @@ -208,9 +258,6 @@ #include "ngx_gcc_atomic_ppc.h" - -#endif - #endif @@ -221,7 +268,7 @@ typedef int32_t ngx_atomic_int_t; typedef uint32_t ngx_atomic_uint_t; typedef volatile ngx_atomic_uint_t ngx_atomic_t; -#define NGX_ATOMIC_T_LEN sizeof("-2147483648") - 1 +#define NGX_ATOMIC_T_LEN (sizeof("-2147483648") - 1) static ngx_inline ngx_atomic_uint_t diff -Nru nginx-0.5.33/src/os/unix/ngx_channel.c nginx-0.8.53/src/os/unix/ngx_channel.c --- nginx-0.5.33/src/os/unix/ngx_channel.c 2006-08-30 10:39:17.000000000 +0000 +++ nginx-0.8.53/src/os/unix/ngx_channel.c 2010-07-08 14:02:09.000000000 +0000 @@ -33,10 +33,21 @@ msg.msg_control = (caddr_t) &cmsg; msg.msg_controllen = sizeof(cmsg); - cmsg.cm.cmsg_len = sizeof(cmsg); + cmsg.cm.cmsg_len = CMSG_LEN(sizeof(int)); cmsg.cm.cmsg_level = SOL_SOCKET; cmsg.cm.cmsg_type = SCM_RIGHTS; - *(int *) CMSG_DATA(&cmsg.cm) = ch->fd; + + /* + * We have to use ngx_memcpy() instead of simple + * *(int *) CMSG_DATA(&cmsg.cm) = ch->fd; + * because some gcc 4.4 with -O2/3/s optimization issues the warning: + * dereferencing type-punned pointer will break strict-aliasing rules + * + * Fortunately, gcc with -O1 compiles this ngx_memcpy() + * in the same simple assignment as in the code above + */ + + ngx_memcpy(CMSG_DATA(&cmsg.cm), &ch->fd, sizeof(int)); } msg.msg_flags = 0; @@ -138,7 +149,7 @@ if (ch->command == NGX_CMD_OPEN_CHANNEL) { - if (cmsg.cm.cmsg_len < (socklen_t) sizeof(cmsg)) { + if (cmsg.cm.cmsg_len < (socklen_t) CMSG_LEN(sizeof(int))) { ngx_log_error(NGX_LOG_ALERT, log, 0, "recvmsg() returned too small ancillary data"); return NGX_ERROR; @@ -153,7 +164,9 @@ return NGX_ERROR; } - ch->fd = *(int *) CMSG_DATA(&cmsg.cm); + /* ch->fd = *(int *) CMSG_DATA(&cmsg.cm); */ + + ngx_memcpy(&ch->fd, CMSG_DATA(&cmsg.cm), sizeof(int)); } if (msg.msg_flags & (MSG_TRUNC|MSG_CTRUNC)) { diff -Nru nginx-0.5.33/src/os/unix/ngx_darwin_config.h nginx-0.8.53/src/os/unix/ngx_darwin_config.h --- nginx-0.5.33/src/os/unix/ngx_darwin_config.h 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/src/os/unix/ngx_darwin_config.h 2009-04-01 13:09:36.000000000 +0000 @@ -0,0 +1,88 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#ifndef _NGX_DARWIN_CONFIG_H_INCLUDED_ +#define _NGX_DARWIN_CONFIG_H_INCLUDED_ + + + +#include +#include +#include +#include +#include +#include /* offsetof() */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* statfs() */ + +#include /* FIONBIO */ +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include /* TCP_NODELAY */ +#include +#include +#include + +#include +#include + + +#ifndef IOV_MAX +#define IOV_MAX 64 +#endif + + +#include + + +#if (NGX_HAVE_POLL) +#include +#endif + + +#if (NGX_HAVE_KQUEUE) +#include +#endif + + +#define NGX_LISTEN_BACKLOG -1 + + +#ifndef NGX_HAVE_INHERITED_NONBLOCK +#define NGX_HAVE_INHERITED_NONBLOCK 1 +#endif + + +#ifndef NGX_HAVE_CASELESS_FILESYSTEM +#define NGX_HAVE_CASELESS_FILESYSTEM 1 +#endif + + +#define NGX_HAVE_OS_SPECIFIC_INIT 1 + + +extern char **environ; + + +#endif /* _NGX_DARWIN_CONFIG_H_INCLUDED_ */ diff -Nru nginx-0.5.33/src/os/unix/ngx_darwin.h nginx-0.8.53/src/os/unix/ngx_darwin.h --- nginx-0.5.33/src/os/unix/ngx_darwin.h 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/src/os/unix/ngx_darwin.h 2008-07-30 12:18:07.000000000 +0000 @@ -0,0 +1,19 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#ifndef _NGX_DARWIN_H_INCLUDED_ +#define _NGX_DARWIN_H_INCLUDED_ + + +ngx_chain_t *ngx_darwin_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, + off_t limit); + +extern int ngx_darwin_kern_osreldate; +extern int ngx_darwin_hw_ncpu; +extern u_long ngx_darwin_net_inet_tcp_sendspace; + + +#endif /* _NGX_DARWIN_H_INCLUDED_ */ diff -Nru nginx-0.5.33/src/os/unix/ngx_darwin_init.c nginx-0.8.53/src/os/unix/ngx_darwin_init.c --- nginx-0.5.33/src/os/unix/ngx_darwin_init.c 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/src/os/unix/ngx_darwin_init.c 2008-11-11 19:44:30.000000000 +0000 @@ -0,0 +1,169 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include + + +char ngx_darwin_kern_ostype[16]; +char ngx_darwin_kern_osrelease[128]; +int ngx_darwin_hw_ncpu; +int ngx_darwin_kern_ipc_somaxconn; +u_long ngx_darwin_net_inet_tcp_sendspace; + + +static ngx_os_io_t ngx_darwin_io = { + ngx_unix_recv, + ngx_readv_chain, + ngx_udp_unix_recv, + ngx_unix_send, +#if (NGX_HAVE_SENDFILE) + ngx_darwin_sendfile_chain, + NGX_IO_SENDFILE +#else + ngx_writev_chain, + 0 +#endif +}; + + +typedef struct { + char *name; + void *value; + size_t size; + ngx_uint_t exists; +} sysctl_t; + + +sysctl_t sysctls[] = { + { "hw.ncpu", + &ngx_darwin_hw_ncpu, + sizeof(ngx_darwin_hw_ncpu), 0 }, + + { "net.inet.tcp.sendspace", + &ngx_darwin_net_inet_tcp_sendspace, + sizeof(ngx_darwin_net_inet_tcp_sendspace), 0 }, + + { "kern.ipc.somaxconn", + &ngx_darwin_kern_ipc_somaxconn, + sizeof(ngx_darwin_kern_ipc_somaxconn), 0 }, + + { NULL, NULL, 0, 0 } +}; + + +ngx_int_t +ngx_os_specific_init(ngx_log_t *log) +{ + int somaxconn; + size_t size; + ngx_err_t err; + ngx_uint_t i; + + size = sizeof(ngx_darwin_kern_ostype); + if (sysctlbyname("kern.ostype", ngx_darwin_kern_ostype, &size, NULL, 0) + == -1) + { + err = ngx_errno; + + if (err != NGX_ENOENT) { + + ngx_log_error(NGX_LOG_ALERT, log, err, + "sysctlbyname(kern.ostype) failed"); + + if (err != NGX_ENOMEM) { + return NGX_ERROR; + } + + ngx_darwin_kern_ostype[size - 1] = '\0'; + } + } + + size = sizeof(ngx_darwin_kern_osrelease); + if (sysctlbyname("kern.osrelease", ngx_darwin_kern_osrelease, &size, + NULL, 0) + == -1) + { + err = ngx_errno; + + if (err != NGX_ENOENT) { + + ngx_log_error(NGX_LOG_ALERT, log, err, + "sysctlbyname(kern.osrelease) failed"); + + if (err != NGX_ENOMEM) { + return NGX_ERROR; + } + + ngx_darwin_kern_osrelease[size - 1] = '\0'; + } + } + + for (i = 0; sysctls[i].name; i++) { + size = sysctls[i].size; + + if (sysctlbyname(sysctls[i].name, sysctls[i].value, &size, NULL, 0) + == 0) + { + sysctls[i].exists = 1; + continue; + } + + err = ngx_errno; + + if (err == NGX_ENOENT) { + continue; + } + + ngx_log_error(NGX_LOG_ALERT, log, err, + "sysctlbyname(%s) failed", sysctls[i].name); + return NGX_ERROR; + } + + ngx_ncpu = ngx_darwin_hw_ncpu; + + somaxconn = 32676; + + if (ngx_darwin_kern_ipc_somaxconn > somaxconn) { + ngx_log_error(NGX_LOG_ALERT, log, 0, + "sysctl kern.ipc.somaxconn must be no more than %d", + somaxconn); + return NGX_ERROR; + } + + ngx_tcp_nodelay_and_tcp_nopush = 1; + + ngx_os_io = ngx_darwin_io; + + return NGX_OK; +} + + +void +ngx_os_specific_status(ngx_log_t *log) +{ + u_long value; + ngx_uint_t i; + + if (ngx_darwin_kern_ostype[0]) { + ngx_log_error(NGX_LOG_NOTICE, log, 0, "OS: %s %s", + ngx_darwin_kern_ostype, ngx_darwin_kern_osrelease); + } + + for (i = 0; sysctls[i].name; i++) { + if (sysctls[i].exists) { + if (sysctls[i].size == sizeof(long)) { + value = *(long *) sysctls[i].value; + + } else { + value = *(int *) sysctls[i].value; + } + + ngx_log_error(NGX_LOG_NOTICE, log, 0, "%s: %l", + sysctls[i].name, value); + } + } +} diff -Nru nginx-0.5.33/src/os/unix/ngx_darwin_sendfile_chain.c nginx-0.8.53/src/os/unix/ngx_darwin_sendfile_chain.c --- nginx-0.5.33/src/os/unix/ngx_darwin_sendfile_chain.c 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/src/os/unix/ngx_darwin_sendfile_chain.c 2009-08-30 09:42:29.000000000 +0000 @@ -0,0 +1,365 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include + + +/* + * It seems that Darwin 9.4 (Mac OS X 1.5) sendfile() has the same + * old bug as early FreeBSD sendfile() syscall: + * http://www.freebsd.org/cgi/query-pr.cgi?pr=33771 + * + * Besides sendfile() has another bug: if one calls sendfile() + * with both a header and a trailer, then sendfile() ignores a file part + * at all and sends only the header and the trailer together. + * For this reason we send a trailer only if there is no a header. + * + * Although sendfile() allows to pass a header or a trailer, + * it may send the header or the trailer and a part of the file + * in different packets. And FreeBSD workaround (TCP_NOPUSH option) + * does not help. + */ + + +#if (IOV_MAX > 64) +#define NGX_HEADERS 64 +#define NGX_TRAILERS 64 +#else +#define NGX_HEADERS IOV_MAX +#define NGX_TRAILERS IOV_MAX +#endif + + +ngx_chain_t * +ngx_darwin_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit) +{ + int rc; + u_char *prev; + off_t size, send, prev_send, aligned, sent, fprev; + off_t header_size, file_size; + ngx_uint_t eintr, complete; + ngx_err_t err; + ngx_buf_t *file; + ngx_array_t header, trailer; + ngx_event_t *wev; + ngx_chain_t *cl; + struct sf_hdtr hdtr; + struct iovec *iov, headers[NGX_HEADERS], trailers[NGX_TRAILERS]; + + wev = c->write; + + if (!wev->ready) { + return in; + } + +#if (NGX_HAVE_KQUEUE) + + if ((ngx_event_flags & NGX_USE_KQUEUE_EVENT) && wev->pending_eof) { + (void) ngx_connection_error(c, wev->kq_errno, + "kevent() reported about an closed connection"); + wev->error = 1; + return NGX_CHAIN_ERROR; + } + +#endif + + /* the maximum limit size is the maximum size_t value - the page size */ + + if (limit == 0 || limit > (off_t) (NGX_MAX_SIZE_T_VALUE - ngx_pagesize)) { + limit = NGX_MAX_SIZE_T_VALUE - ngx_pagesize; + } + + send = 0; + + header.elts = headers; + header.size = sizeof(struct iovec); + header.nalloc = NGX_HEADERS; + header.pool = c->pool; + + trailer.elts = trailers; + trailer.size = sizeof(struct iovec); + trailer.nalloc = NGX_TRAILERS; + trailer.pool = c->pool; + + for ( ;; ) { + file = NULL; + file_size = 0; + header_size = 0; + eintr = 0; + complete = 0; + prev_send = send; + + header.nelts = 0; + trailer.nelts = 0; + + /* create the header iovec and coalesce the neighbouring bufs */ + + prev = NULL; + iov = NULL; + + for (cl = in; + cl && header.nelts < IOV_MAX && send < limit; + cl = cl->next) + { + if (ngx_buf_special(cl->buf)) { + continue; + } + + if (!ngx_buf_in_memory_only(cl->buf)) { + break; + } + + size = cl->buf->last - cl->buf->pos; + + if (send + size > limit) { + size = limit - send; + } + + if (prev == cl->buf->pos) { + iov->iov_len += (size_t) size; + + } else { + iov = ngx_array_push(&header); + if (iov == NULL) { + return NGX_CHAIN_ERROR; + } + + iov->iov_base = (void *) cl->buf->pos; + iov->iov_len = (size_t) size; + } + + prev = cl->buf->pos + (size_t) size; + header_size += size; + send += size; + } + + + if (cl && cl->buf->in_file && send < limit) { + file = cl->buf; + + /* coalesce the neighbouring file bufs */ + + do { + size = cl->buf->file_last - cl->buf->file_pos; + + if (send + size > limit) { + size = limit - send; + + aligned = (cl->buf->file_pos + size + ngx_pagesize - 1) + & ~((off_t) ngx_pagesize - 1); + + if (aligned <= cl->buf->file_last) { + size = aligned - cl->buf->file_pos; + } + } + + file_size += size; + send += size; + fprev = cl->buf->file_pos + size; + cl = cl->next; + + } while (cl + && cl->buf->in_file + && send < limit + && file->file->fd == cl->buf->file->fd + && fprev == cl->buf->file_pos); + } + + if (file && header.nelts == 0) { + + /* create the tailer iovec and coalesce the neighbouring bufs */ + + prev = NULL; + iov = NULL; + + while (cl && header.nelts < IOV_MAX && send < limit) { + + if (ngx_buf_special(cl->buf)) { + cl = cl->next; + continue; + } + + if (!ngx_buf_in_memory_only(cl->buf)) { + break; + } + + size = cl->buf->last - cl->buf->pos; + + if (send + size > limit) { + size = limit - send; + } + + if (prev == cl->buf->pos) { + iov->iov_len += (size_t) size; + + } else { + iov = ngx_array_push(&trailer); + if (iov == NULL) { + return NGX_CHAIN_ERROR; + } + + iov->iov_base = (void *) cl->buf->pos; + iov->iov_len = (size_t) size; + } + + prev = cl->buf->pos + (size_t) size; + send += size; + cl = cl->next; + } + } + + if (file) { + + /* + * sendfile() returns EINVAL if sf_hdtr's count is 0, + * but corresponding pointer is not NULL + */ + + hdtr.headers = header.nelts ? (struct iovec *) header.elts: NULL; + hdtr.hdr_cnt = header.nelts; + hdtr.trailers = trailer.nelts ? (struct iovec *) trailer.elts: NULL; + hdtr.trl_cnt = trailer.nelts; + + sent = header_size + file_size; + + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, + "sendfile: @%O %O h:%O", + file->file_pos, sent, header_size); + + rc = sendfile(file->file->fd, c->fd, file->file_pos, + &sent, &hdtr, 0); + + if (rc == -1) { + err = ngx_errno; + + switch (err) { + case NGX_EAGAIN: + break; + + case NGX_EINTR: + eintr = 1; + break; + + default: + wev->error = 1; + (void) ngx_connection_error(c, err, "sendfile() failed"); + return NGX_CHAIN_ERROR; + } + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, err, + "sendfile() sent only %O bytes", sent); + } + + if (rc == 0 && sent == 0) { + + /* + * if rc and sent equal to zero, then someone + * has truncated the file, so the offset became beyond + * the end of the file + */ + + ngx_log_error(NGX_LOG_ALERT, c->log, 0, + "sendfile() reported that \"%s\" was truncated", + file->file->name.data); + + return NGX_CHAIN_ERROR; + } + + ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0, + "sendfile: %d, @%O %O:%O", + rc, file->file_pos, sent, file_size + header_size); + + } else { + rc = writev(c->fd, header.elts, header.nelts); + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, + "writev: %d of %uz", rc, send); + + if (rc == -1) { + err = ngx_errno; + + switch (err) { + case NGX_EAGAIN: + break; + + case NGX_EINTR: + eintr = 1; + break; + + default: + wev->error = 1; + ngx_connection_error(c, err, "writev() failed"); + return NGX_CHAIN_ERROR; + } + + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err, + "writev() not ready"); + } + + sent = rc > 0 ? rc : 0; + } + + if (send - prev_send == sent) { + complete = 1; + } + + c->sent += sent; + + for (cl = in; cl; cl = cl->next) { + + if (ngx_buf_special(cl->buf)) { + continue; + } + + if (sent == 0) { + break; + } + + size = ngx_buf_size(cl->buf); + + if (sent >= size) { + sent -= size; + + if (ngx_buf_in_memory(cl->buf)) { + cl->buf->pos = cl->buf->last; + } + + if (cl->buf->in_file) { + cl->buf->file_pos = cl->buf->file_last; + } + + continue; + } + + if (ngx_buf_in_memory(cl->buf)) { + cl->buf->pos += (size_t) sent; + } + + if (cl->buf->in_file) { + cl->buf->file_pos += sent; + } + + break; + } + + if (eintr) { + continue; + } + + if (!complete) { + wev->ready = 0; + return cl; + } + + if (send >= limit || cl == NULL) { + return cl; + } + + in = cl; + } +} diff -Nru nginx-0.5.33/src/os/unix/ngx_errno.c nginx-0.8.53/src/os/unix/ngx_errno.c --- nginx-0.5.33/src/os/unix/ngx_errno.c 2006-08-30 10:39:17.000000000 +0000 +++ nginx-0.8.53/src/os/unix/ngx_errno.c 2008-11-25 11:26:32.000000000 +0000 @@ -10,10 +10,11 @@ #if (NGX_HAVE_STRERROR_R) -u_char *ngx_strerror_r(int err, u_char *errstr, size_t size) +u_char * +ngx_strerror_r(int err, u_char *errstr, size_t size) { if (size == 0) { - return 0; + return errstr; } errstr[0] = '\0'; @@ -32,12 +33,13 @@ /* Linux strerror_r() */ -u_char *ngx_strerror_r(int err, u_char *errstr, size_t size) +u_char * +ngx_strerror_r(int err, u_char *errstr, size_t size) { char *str; if (size == 0) { - return 0; + return errstr; } errstr[0] = '\0'; diff -Nru nginx-0.5.33/src/os/unix/ngx_errno.h nginx-0.8.53/src/os/unix/ngx_errno.h --- nginx-0.5.33/src/os/unix/ngx_errno.h 2006-08-30 10:39:17.000000000 +0000 +++ nginx-0.8.53/src/os/unix/ngx_errno.h 2010-07-03 20:16:55.000000000 +0000 @@ -16,6 +16,7 @@ #define NGX_EPERM EPERM #define NGX_ENOENT ENOENT +#define NGX_ENOPATH ENOENT #define NGX_ESRCH ESRCH #define NGX_EINTR EINTR #define NGX_ECHILD ECHILD @@ -23,12 +24,12 @@ #define NGX_EACCES EACCES #define NGX_EBUSY EBUSY #define NGX_EEXIST EEXIST +#define NGX_EXDEV EXDEV #define NGX_ENOTDIR ENOTDIR #define NGX_EISDIR EISDIR #define NGX_EINVAL EINVAL #define NGX_ENOSPC ENOSPC #define NGX_EPIPE EPIPE -#define NGX_EAGAIN EAGAIN #define NGX_EINPROGRESS EINPROGRESS #define NGX_EADDRINUSE EADDRINUSE #define NGX_ECONNABORTED ECONNABORTED @@ -37,11 +38,20 @@ #define NGX_ETIMEDOUT ETIMEDOUT #define NGX_ECONNREFUSED ECONNREFUSED #define NGX_ENAMETOOLONG ENAMETOOLONG +#define NGX_ENETDOWN ENETDOWN +#define NGX_ENETUNREACH ENETUNREACH +#define NGX_EHOSTDOWN EHOSTDOWN #define NGX_EHOSTUNREACH EHOSTUNREACH #define NGX_ENOSYS ENOSYS #define NGX_ECANCELED ECANCELED +#define NGX_EILSEQ EILSEQ #define NGX_ENOMOREFILES 0 +#if (__hpux__) +#define NGX_EAGAIN EWOULDBLOCK +#else +#define NGX_EAGAIN EAGAIN +#endif #define ngx_errno errno @@ -58,10 +68,22 @@ /* Solaris and Tru64 UNIX have thread-safe strerror() */ -#define ngx_strerror_r(err, errstr, size) \ +#define ngx_strerror_r(err, errstr, size) \ ngx_cpystrn(errstr, (u_char *) strerror(err), size) #endif +#if (NGX_HAVE_SYS_ERRLIST) + +#define ngx_sigsafe_strerror(err) \ + (err > 0 && err < sys_nerr) ? sys_errlist[err] : "Unknown error" + +#else + +#define ngx_sigsafe_strerror(err) "" + +#endif + + #endif /* _NGX_ERRNO_H_INCLUDED_ */ diff -Nru nginx-0.5.33/src/os/unix/ngx_file_aio_read.c nginx-0.8.53/src/os/unix/ngx_file_aio_read.c --- nginx-0.5.33/src/os/unix/ngx_file_aio_read.c 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/src/os/unix/ngx_file_aio_read.c 2009-11-05 13:12:30.000000000 +0000 @@ -0,0 +1,213 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include + + +/* + * FreeBSD file AIO features and quirks: + * + * if an asked data are already in VM cache, then aio_error() returns 0, + * and the data are already copied in buffer; + * + * aio_read() preread in VM cache as minimum 16K (probably BKVASIZE); + * the first AIO preload may be up to 128K; + * + * aio_read/aio_error() may return EINPROGRESS for just written data; + * + * kqueue EVFILT_AIO filter is level triggered only: an event repeats + * until aio_return() will be called; + * + * aio_cancel() can not cancel file AIO: it returns AIO_NOTCANCELED always. + */ + + +extern int ngx_kqueue; + + +static ssize_t ngx_file_aio_result(ngx_file_t *file, ngx_event_aio_t *aio, + ngx_event_t *ev); +static void ngx_file_aio_event_handler(ngx_event_t *ev); + + +ssize_t +ngx_file_aio_read(ngx_file_t *file, u_char *buf, size_t size, off_t offset, + ngx_pool_t *pool) +{ + int n; + ngx_event_t *ev; + ngx_event_aio_t *aio; + + if (!ngx_file_aio) { + return ngx_read_file(file, buf, size, offset); + } + + aio = file->aio; + + if (aio == NULL) { + aio = ngx_pcalloc(pool, sizeof(ngx_event_aio_t)); + if (aio == NULL) { + return NGX_ERROR; + } + + aio->file = file; + aio->fd = file->fd; + aio->event.data = aio; + aio->event.ready = 1; + aio->event.log = file->log; +#if (NGX_HAVE_AIO_SENDFILE) + aio->last_offset = -1; +#endif + file->aio = aio; + } + + ev = &aio->event; + + if (!ev->ready) { + ngx_log_error(NGX_LOG_ALERT, file->log, 0, + "second aio post for \"%V\"", &file->name); + return NGX_AGAIN; + } + + ngx_log_debug4(NGX_LOG_DEBUG_CORE, file->log, 0, + "aio complete:%d @%O:%z %V", + ev->complete, offset, size, &file->name); + + if (ev->complete) { + ev->complete = 0; + ngx_set_errno(aio->err); + + if (aio->err == 0) { + return aio->nbytes; + } + + return NGX_ERROR; + } + + ngx_memzero(&aio->aiocb, sizeof(struct aiocb)); + + aio->aiocb.aio_fildes = file->fd; + aio->aiocb.aio_offset = offset; + aio->aiocb.aio_buf = buf; + aio->aiocb.aio_nbytes = size; +#if (NGX_HAVE_KQUEUE) + aio->aiocb.aio_sigevent.sigev_notify_kqueue = ngx_kqueue; + aio->aiocb.aio_sigevent.sigev_notify = SIGEV_KEVENT; + aio->aiocb.aio_sigevent.sigev_value.sigval_ptr = ev; +#endif + ev->handler = ngx_file_aio_event_handler; + + n = aio_read(&aio->aiocb); + + if (n == -1) { + n = ngx_errno; + + if (n == NGX_EAGAIN) { + return ngx_read_file(file, buf, size, offset); + } + + ngx_log_error(NGX_LOG_CRIT, file->log, n, + "aio_read(\"%V\") failed", &file->name); + + if (n == NGX_ENOSYS) { + ngx_file_aio = 0; + return ngx_read_file(file, buf, size, offset); + } + + return NGX_ERROR; + } + + ngx_log_debug2(NGX_LOG_DEBUG_CORE, file->log, 0, + "aio_read: fd:%d %d", file->fd, n); + + ev->active = 1; + ev->ready = 0; + ev->complete = 0; + + return ngx_file_aio_result(aio->file, aio, ev); +} + + +static ssize_t +ngx_file_aio_result(ngx_file_t *file, ngx_event_aio_t *aio, ngx_event_t *ev) +{ + int n; + ngx_err_t err; + + n = aio_error(&aio->aiocb); + + ngx_log_debug2(NGX_LOG_DEBUG_CORE, file->log, 0, + "aio_error: fd:%d %d", file->fd, n); + + if (n == -1) { + err = ngx_errno; + aio->err = err; + + ngx_log_error(NGX_LOG_ALERT, file->log, err, + "aio_error(\"%V\") failed", &file->name); + return NGX_ERROR; + } + + if (n != 0) { + if (n == NGX_EINPROGRESS) { + if (ev->ready) { + ev->ready = 0; + ngx_log_error(NGX_LOG_ALERT, file->log, n, + "aio_read(\"%V\") still in progress", + &file->name); + } + + return NGX_AGAIN; + } + + aio->err = n; + ev->ready = 0; + + ngx_log_error(NGX_LOG_CRIT, file->log, n, + "aio_read(\"%V\") failed", &file->name); + return NGX_ERROR; + } + + n = aio_return(&aio->aiocb); + + if (n == -1) { + err = ngx_errno; + aio->err = err; + ev->ready = 0; + + ngx_log_error(NGX_LOG_ALERT, file->log, err, + "aio_return(\"%V\") failed", &file->name); + return NGX_ERROR; + } + + aio->err = 0; + aio->nbytes = n; + ev->ready = 1; + ev->active = 0; + + ngx_log_debug2(NGX_LOG_DEBUG_CORE, file->log, 0, + "aio_return: fd:%d %d", file->fd, n); + + return n; +} + + +static void +ngx_file_aio_event_handler(ngx_event_t *ev) +{ + ngx_event_aio_t *aio; + + aio = ev->data; + + ngx_log_debug2(NGX_LOG_DEBUG_CORE, ev->log, 0, + "aio event handler fd:%d %V", aio->fd, &aio->file->name); + + if (ngx_file_aio_result(aio->file, aio, ev) != NGX_AGAIN) { + aio->handler(ev); + } +} diff -Nru nginx-0.5.33/src/os/unix/ngx_files.c nginx-0.8.53/src/os/unix/ngx_files.c --- nginx-0.5.33/src/os/unix/ngx_files.c 2007-01-18 19:52:18.000000000 +0000 +++ nginx-0.8.53/src/os/unix/ngx_files.c 2010-06-30 10:33:02.000000000 +0000 @@ -8,6 +8,13 @@ #include +#if (NGX_HAVE_FILE_AIO) + +ngx_uint_t ngx_file_aio = 1; + +#endif + + ssize_t ngx_read_file(ngx_file_t *file, u_char *buf, size_t size, off_t offset) { @@ -22,7 +29,7 @@ if (n == -1) { ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno, - "pread() failed, file \"%s\"", file->name.data); + "pread() \"%s\" failed", file->name.data); return NGX_ERROR; } @@ -30,7 +37,8 @@ if (file->sys_offset != offset) { if (lseek(file->fd, offset, SEEK_SET) == -1) { - ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno, "lseek() failed"); + ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno, + "lseek() \"%s\" failed", file->name.data); return NGX_ERROR; } @@ -40,7 +48,8 @@ n = read(file->fd, buf, size); if (n == -1) { - ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno, "read() failed"); + ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno, + "read() \"%s\" failed", file->name.data); return NGX_ERROR; } @@ -57,57 +66,66 @@ ssize_t ngx_write_file(ngx_file_t *file, u_char *buf, size_t size, off_t offset) { - ssize_t n; + ssize_t n, written; ngx_log_debug4(NGX_LOG_DEBUG_CORE, file->log, 0, "write: %d, %p, %uz, %O", file->fd, buf, size, offset); + written = 0; + #if (NGX_HAVE_PWRITE) - n = pwrite(file->fd, buf, size, offset); + for ( ;; ) { + n = pwrite(file->fd, buf + written, size, offset); - if (n == -1) { - ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno, "pwrite() failed"); - return NGX_ERROR; - } + if (n == -1) { + ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno, + "pwrite() \"%s\" failed", file->name.data); + return NGX_ERROR; + } - if ((size_t) n != size) { - ngx_log_error(NGX_LOG_CRIT, file->log, 0, - "pwrite() has written only %z of %uz", n, size); - return NGX_ERROR; + file->offset += n; + written += n; + + if ((size_t) n == size) { + return written; + } + + offset += n; + size -= n; } #else if (file->sys_offset != offset) { if (lseek(file->fd, offset, SEEK_SET) == -1) { - ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno, "lseek() failed"); + ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno, + "lseek() \"%s\" failed", file->name.data); return NGX_ERROR; } file->sys_offset = offset; } - n = write(file->fd, buf, size); + for ( ;; ) { + n = write(file->fd, buf + written, size); - if (n == -1) { - ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno, "write() failed"); - return NGX_ERROR; - } + if (n == -1) { + ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno, + "write() \"%s\" failed", file->name.data); + return NGX_ERROR; + } - if ((size_t) n != size) { - ngx_log_error(NGX_LOG_CRIT, file->log, 0, - "write() has written only %z of %uz", n, size); - return NGX_ERROR; - } + file->offset += n; + written += n; - file->sys_offset += n; + if ((size_t) n == size) { + return written; + } + size -= n; + } #endif - - file->offset += n; - - return n; } @@ -191,7 +209,7 @@ if (file->sys_offset != offset) { if (lseek(file->fd, offset, SEEK_SET) == -1) { ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno, - "lseek() failed"); + "lseek() \"%s\" failed", file->name.data); return NGX_ERROR; } @@ -202,13 +220,14 @@ if (n == -1) { ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno, - "writev() failed"); + "writev() \"%s\" failed", file->name.data); return NGX_ERROR; } if ((size_t) n != size) { ngx_log_error(NGX_LOG_CRIT, file->log, 0, - "writev() has written only %z of %uz", n, size); + "writev() \"%s\" has written only %z of %uz", + file->name.data, n, size); return NGX_ERROR; } @@ -240,6 +259,58 @@ ngx_int_t +ngx_create_file_mapping(ngx_file_mapping_t *fm) +{ + fm->fd = ngx_open_file(fm->name, NGX_FILE_RDWR, NGX_FILE_TRUNCATE, + NGX_FILE_DEFAULT_ACCESS); + if (fm->fd == NGX_INVALID_FILE) { + ngx_log_error(NGX_LOG_CRIT, fm->log, ngx_errno, + ngx_open_file_n " \"%s\" failed", fm->name); + return NGX_ERROR; + } + + if (ftruncate(fm->fd, fm->size) == -1) { + ngx_log_error(NGX_LOG_CRIT, fm->log, ngx_errno, + "ftruncate() \"%s\" failed", fm->name); + goto failed; + } + + fm->addr = mmap(NULL, fm->size, PROT_READ|PROT_WRITE, MAP_SHARED, + fm->fd, 0); + if (fm->addr != MAP_FAILED) { + return NGX_OK; + } + + ngx_log_error(NGX_LOG_CRIT, fm->log, ngx_errno, + "mmap(%uz) \"%s\" failed", fm->size, fm->name); + +failed: + + if (ngx_close_file(fm->fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, fm->log, ngx_errno, + ngx_close_file_n " \"%s\" failed", fm->name); + } + + return NGX_ERROR; +} + + +void +ngx_close_file_mapping(ngx_file_mapping_t *fm) +{ + if (munmap(fm->addr, fm->size) == -1) { + ngx_log_error(NGX_LOG_CRIT, fm->log, ngx_errno, + "munmap(%uz) \"%s\" failed", fm->size, fm->name); + } + + if (ngx_close_file(fm->fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, fm->log, ngx_errno, + ngx_close_file_n " \"%s\" failed", fm->name); + } +} + + +ngx_int_t ngx_open_dir(ngx_str_t *name, ngx_dir_t *dir) { dir->dir = opendir((const char *) name->data); @@ -255,12 +326,42 @@ ngx_int_t +ngx_read_dir(ngx_dir_t *dir) +{ + dir->de = readdir(dir->dir); + + if (dir->de) { +#if (NGX_HAVE_D_TYPE) + dir->type = dir->de->d_type; +#else + dir->type = 0; +#endif + return NGX_OK; + } + + return NGX_ERROR; +} + + +ngx_int_t ngx_open_glob(ngx_glob_t *gl) { - if (glob((char *) gl->pattern, GLOB_NOSORT, NULL, &gl->pglob) == 0) { + int n; + + n = glob((char *) gl->pattern, GLOB_NOSORT, NULL, &gl->pglob); + + if (n == 0) { return NGX_OK; } +#ifdef GLOB_NOMATCH + + if (n == GLOB_NOMATCH && gl->test) { + return NGX_OK; + } + +#endif + return NGX_ERROR; } @@ -268,7 +369,15 @@ ngx_int_t ngx_read_glob(ngx_glob_t *gl, ngx_str_t *name) { - if (gl->n < (size_t) gl->pglob.gl_pathc) { + size_t count; + +#ifdef GLOB_NOMATCH + count = (size_t) gl->pglob.gl_pathc; +#else + count = (size_t) gl->pglob.gl_matchc; +#endif + + if (gl->n < count) { name->len = (size_t) ngx_strlen(gl->pglob.gl_pathv[gl->n]); name->data = (u_char *) gl->pglob.gl_pathv[gl->n]; @@ -343,3 +452,104 @@ return 0; } + + +#if (NGX_HAVE_POSIX_FADVISE) + +ngx_int_t +ngx_read_ahead(ngx_fd_t fd, size_t n) +{ + int err; + + err = posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL); + + if (err == 0) { + return 0; + } + + ngx_set_errno(err); + return NGX_FILE_ERROR; +} + +#endif + + +#if (NGX_HAVE_O_DIRECT) + +ngx_int_t +ngx_directio_on(ngx_fd_t fd) +{ + int flags; + + flags = fcntl(fd, F_GETFL); + + if (flags == -1) { + return NGX_FILE_ERROR; + } + + return fcntl(fd, F_SETFL, flags | O_DIRECT); +} + + +ngx_int_t +ngx_directio_off(ngx_fd_t fd) +{ + int flags; + + flags = fcntl(fd, F_GETFL); + + if (flags == -1) { + return NGX_FILE_ERROR; + } + + return fcntl(fd, F_SETFL, flags & ~O_DIRECT); +} + +#endif + + +#if (NGX_HAVE_STATFS) + +size_t +ngx_fs_bsize(u_char *name) +{ + struct statfs fs; + + if (statfs((char *) name, &fs) == -1) { + return 512; + } + + if ((fs.f_bsize % 512) != 0) { + return 512; + } + + return (size_t) fs.f_bsize; +} + +#elif (NGX_HAVE_STATVFS) + +size_t +ngx_fs_bsize(u_char *name) +{ + struct statvfs fs; + + if (statvfs((char *) name, &fs) == -1) { + return 512; + } + + if ((fs.f_frsize % 512) != 0) { + return 512; + } + + return (size_t) fs.f_frsize; +} + +#else + +size_t +ngx_fs_bsize(u_char *name) +{ + return 512; +} + +#endif diff -Nru nginx-0.5.33/src/os/unix/ngx_files.h nginx-0.8.53/src/os/unix/ngx_files.h --- nginx-0.5.33/src/os/unix/ngx_files.h 2007-01-29 12:25:44.000000000 +0000 +++ nginx-0.8.53/src/os/unix/ngx_files.h 2010-06-29 15:18:50.000000000 +0000 @@ -12,13 +12,58 @@ #include +typedef int ngx_fd_t; +typedef struct stat ngx_file_info_t; +typedef ino_t ngx_file_uniq_t; + + +typedef struct { + u_char *name; + size_t size; + void *addr; + ngx_fd_t fd; + ngx_log_t *log; +} ngx_file_mapping_t; + + +typedef struct { + DIR *dir; + struct dirent *de; + struct stat info; + + unsigned type:8; + unsigned valid_info:1; +} ngx_dir_t; + + +typedef struct { + size_t n; + glob_t pglob; + u_char *pattern; + ngx_log_t *log; + ngx_uint_t test; +} ngx_glob_t; + + #define NGX_INVALID_FILE -1 #define NGX_FILE_ERROR -1 +#ifdef __CYGWIN__ + +#define NGX_HAVE_CASELESS_FILESYSTEM 1 + +#define ngx_open_file(name, mode, create, access) \ + open((const char *) name, mode|create|O_BINARY, access) + +#else + #define ngx_open_file(name, mode, create, access) \ open((const char *) name, mode|create, access) + +#endif + #define ngx_open_file_n "open()" #define NGX_FILE_RDONLY O_RDONLY @@ -26,10 +71,12 @@ #define NGX_FILE_RDWR O_RDWR #define NGX_FILE_CREATE_OR_OPEN O_CREAT #define NGX_FILE_OPEN 0 -#define NGX_FILE_TRUNCATE O_TRUNC -#define NGX_FILE_APPEND O_APPEND +#define NGX_FILE_TRUNCATE O_CREAT|O_TRUNC +#define NGX_FILE_APPEND O_WRONLY|O_APPEND +#define NGX_FILE_NONBLOCK O_NONBLOCK #define NGX_FILE_DEFAULT_ACCESS 0644 +#define NGX_FILE_OWNER_ACCESS 0600 #define ngx_close_file close @@ -46,7 +93,11 @@ ssize_t ngx_read_file(ngx_file_t *file, u_char *buf, size_t size, off_t offset); +#if (NGX_HAVE_PREAD) +#define ngx_read_file_n "pread()" +#else #define ngx_read_file_n "read()" +#endif ssize_t ngx_write_file(ngx_file_t *file, u_char *buf, size_t size, off_t offset); @@ -58,9 +109,23 @@ #define ngx_read_fd read #define ngx_read_fd_n "read()" -#define ngx_write_fd write +/* + * we use inlined function instead of simple #define + * because glibc 2.3 sets warn_unused_result attribute for write() + * and in this case gcc 4.3 ignores (void) cast + */ +static ngx_inline ssize_t +ngx_write_fd(ngx_fd_t fd, void *buf, size_t n) +{ + return write(fd, buf, n); +} + #define ngx_write_fd_n "write()" + +#define ngx_write_console ngx_write_fd + + #define ngx_linefeed(p) *p++ = LF; #define NGX_LINEFEED_SIZE 1 @@ -83,19 +148,40 @@ #define ngx_fd_info(fd, sb) fstat(fd, sb) #define ngx_fd_info_n "fstat()" +#define ngx_link_info(file, sb) lstat((const char *) file, sb) +#define ngx_link_info_n "lstat()" + #define ngx_is_dir(sb) (S_ISDIR((sb)->st_mode)) #define ngx_is_file(sb) (S_ISREG((sb)->st_mode)) #define ngx_is_link(sb) (S_ISLNK((sb)->st_mode)) -#define ngx_is_exec(sb) ((sb)->st_mode & S_IXUSR) +#define ngx_is_exec(sb) (((sb)->st_mode & S_IXUSR) == S_IXUSR) #define ngx_file_access(sb) ((sb)->st_mode & 0777) #define ngx_file_size(sb) (sb)->st_size #define ngx_file_mtime(sb) (sb)->st_mtime #define ngx_file_uniq(sb) (sb)->st_ino +ngx_int_t ngx_create_file_mapping(ngx_file_mapping_t *fm); +void ngx_close_file_mapping(ngx_file_mapping_t *fm); + + +#if (NGX_HAVE_CASELESS_FILESYSTEM) + +#define ngx_filename_cmp(s1, s2, n) strncasecmp((char *) s1, (char *) s2, n) + +#else + +#define ngx_filename_cmp ngx_memcmp + +#endif + -#define ngx_getcwd(buf, size) (getcwd(buf, size) != NULL) +#define ngx_realpath(p, r) realpath((char *) p, (char *) r) +#define ngx_realpath_n "realpath()" +#define ngx_getcwd(buf, size) (getcwd((char *) buf, size) != NULL) #define ngx_getcwd_n "getcwd()" +#define ngx_path_separator(c) ((c) == '/') + #define NGX_MAX_PATH PATH_MAX #define NGX_DIR_MASK_LEN 0 @@ -109,8 +195,7 @@ #define ngx_close_dir_n "closedir()" -#define ngx_read_dir(d) \ - (((d)->de = readdir((d)->dir)) ? NGX_OK : NGX_ERROR) +ngx_int_t ngx_read_dir(ngx_dir_t *dir); #define ngx_read_dir_n "readdir()" @@ -126,31 +211,50 @@ #define ngx_de_name(dir) ((u_char *) (dir)->de->d_name) -#if (NGX_FREEBSD) +#if (NGX_HAVE_D_NAMLEN) #define ngx_de_namelen(dir) (dir)->de->d_namlen #else #define ngx_de_namelen(dir) ngx_strlen((dir)->de->d_name) #endif -#define ngx_de_info(name, dir) stat((const char *) name, &(dir)->info) + +static ngx_inline ngx_int_t +ngx_de_info(u_char *name, ngx_dir_t *dir) +{ + dir->type = 0; + return stat((const char *) name, &dir->info); +} + #define ngx_de_info_n "stat()" #define ngx_de_link_info(name, dir) lstat((const char *) name, &(dir)->info) #define ngx_de_link_info_n "lstat()" + +#if (NGX_HAVE_D_TYPE) + +/* + * some file systems (e.g. XFS on Linux and CD9660 on FreeBSD) + * do not set dirent.d_type + */ + +#define ngx_de_is_dir(dir) \ + (((dir)->type) ? ((dir)->type == DT_DIR) : (S_ISDIR((dir)->info.st_mode))) +#define ngx_de_is_file(dir) \ + (((dir)->type) ? ((dir)->type == DT_REG) : (S_ISREG((dir)->info.st_mode))) +#define ngx_de_is_link(dir) \ + (((dir)->type) ? ((dir)->type == DT_LNK) : (S_ISLNK((dir)->info.st_mode))) + +#else + #define ngx_de_is_dir(dir) (S_ISDIR((dir)->info.st_mode)) #define ngx_de_is_file(dir) (S_ISREG((dir)->info.st_mode)) #define ngx_de_is_link(dir) (S_ISLNK((dir)->info.st_mode)) + +#endif + #define ngx_de_access(dir) (((dir)->info.st_mode) & 0777) #define ngx_de_size(dir) (dir)->info.st_size #define ngx_de_mtime(dir) (dir)->info.st_mtime -typedef struct { - size_t n; - glob_t pglob; - u_char *pattern; - ngx_log_t *log; -} ngx_glob_t; - - ngx_int_t ngx_open_glob(ngx_glob_t *gl); #define ngx_open_glob_n "glob()" ngx_int_t ngx_read_glob(ngx_glob_t *gl, ngx_str_t *name); @@ -166,4 +270,69 @@ #define ngx_unlock_fd_n "fcntl(F_SETLK, F_UNLCK)" +#if (NGX_HAVE_F_READAHEAD) + +#define NGX_HAVE_READ_AHEAD 1 + +#define ngx_read_ahead(fd, n) fcntl(fd, F_READAHEAD, (int) n) +#define ngx_read_ahead_n "fcntl(fd, F_READAHEAD)" + +#elif (NGX_HAVE_POSIX_FADVISE) + +#define NGX_HAVE_READ_AHEAD 1 + +ngx_int_t ngx_read_ahead(ngx_fd_t fd, size_t n); +#define ngx_read_ahead_n "posix_fadvise(POSIX_FADV_SEQUENTIAL)" + +#else + +#define ngx_read_ahead(fd, n) 0 +#define ngx_read_ahead_n "ngx_read_ahead_n" + +#endif + + +#if (NGX_HAVE_O_DIRECT) + +ngx_int_t ngx_directio_on(ngx_fd_t fd); +#define ngx_directio_on_n "fcntl(O_DIRECT)" + +ngx_int_t ngx_directio_off(ngx_fd_t fd); +#define ngx_directio_off_n "fcntl(!O_DIRECT)" + +#elif (NGX_HAVE_F_NOCACHE) + +#define ngx_directio_on(fd) fcntl(fd, F_NOCACHE, 1) +#define ngx_directio_on_n "fcntl(F_NOCACHE, 1)" + +#elif (NGX_HAVE_DIRECTIO) + +#define ngx_directio_on(fd) directio(fd, DIRECTIO_ON) +#define ngx_directio_on_n "directio(DIRECTIO_ON)" + +#else + +#define ngx_directio_on(fd) 0 +#define ngx_directio_on_n "ngx_directio_on_n" + +#endif + +size_t ngx_fs_bsize(u_char *name); + + +#define ngx_stderr STDERR_FILENO +#define ngx_set_stderr(fd) dup2(fd, STDERR_FILENO) +#define ngx_set_stderr_n "dup2(STDERR_FILENO)" + + +#if (NGX_HAVE_FILE_AIO) + +ssize_t ngx_file_aio_read(ngx_file_t *file, u_char *buf, size_t size, + off_t offset, ngx_pool_t *pool); + +extern ngx_uint_t ngx_file_aio; + +#endif + + #endif /* _NGX_FILES_H_INCLUDED_ */ diff -Nru nginx-0.5.33/src/os/unix/ngx_freebsd_config.h nginx-0.8.53/src/os/unix/ngx_freebsd_config.h --- nginx-0.5.33/src/os/unix/ngx_freebsd_config.h 2007-11-07 13:54:40.000000000 +0000 +++ nginx-0.8.53/src/os/unix/ngx_freebsd_config.h 2009-08-28 08:12:35.000000000 +0000 @@ -22,6 +22,8 @@ #include #include #include +#include /* ALIGN() */ +#include /* statfs() */ #include /* FIONBIO */ #include @@ -43,16 +45,20 @@ #include /* setproctitle() before 4.1 */ #include #include -#include /* ALIGN() */ #if __FreeBSD_version < 400017 -/* FreeBSD 3.x has no CMSG_SPACE() at all and has the broken CMSG_DATA() */ +/* + * FreeBSD 3.x has no CMSG_SPACE() and CMSG_LEN() and has the broken CMSG_DATA() + */ #undef CMSG_SPACE #define CMSG_SPACE(l) (ALIGN(sizeof(struct cmsghdr)) + ALIGN(l)) +#undef CMSG_LEN +#define CMSG_LEN(l) (ALIGN(sizeof(struct cmsghdr)) + (l)) + #undef CMSG_DATA #define CMSG_DATA(cmsg) ((u_char *)(cmsg) + ALIGN(sizeof(struct cmsghdr))) @@ -67,13 +73,14 @@ #endif -#if (NGX_HAVE_AIO) -#include +#if (NGX_HAVE_KQUEUE) +#include #endif -#if (NGX_HAVE_KQUEUE) -#include +#if (NGX_HAVE_FILE_AIO || NGX_HAVE_AIO) +#include +typedef struct aiocb ngx_aiocb_t; #endif diff -Nru nginx-0.5.33/src/os/unix/ngx_freebsd.h nginx-0.8.53/src/os/unix/ngx_freebsd.h --- nginx-0.5.33/src/os/unix/ngx_freebsd.h 2006-08-30 10:39:17.000000000 +0000 +++ nginx-0.8.53/src/os/unix/ngx_freebsd.h 2008-12-17 20:47:18.000000000 +0000 @@ -11,13 +11,13 @@ ngx_chain_t *ngx_freebsd_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit); -extern int ngx_freebsd_kern_osreldate; -extern int ngx_freebsd_hw_ncpu; -extern u_long ngx_freebsd_net_inet_tcp_sendspace; -extern int ngx_freebsd_kern_ipc_zero_copy_send; +extern int ngx_freebsd_kern_osreldate; +extern int ngx_freebsd_hw_ncpu; +extern u_long ngx_freebsd_net_inet_tcp_sendspace; -extern ngx_uint_t ngx_freebsd_sendfile_nbytes_bug; -extern ngx_uint_t ngx_freebsd_use_tcp_nopush; +extern ngx_uint_t ngx_freebsd_sendfile_nbytes_bug; +extern ngx_uint_t ngx_freebsd_use_tcp_nopush; +extern ngx_uint_t ngx_freebsd_debug_malloc; #endif /* _NGX_FREEBSD_H_INCLUDED_ */ diff -Nru nginx-0.5.33/src/os/unix/ngx_freebsd_init.c nginx-0.8.53/src/os/unix/ngx_freebsd_init.c --- nginx-0.5.33/src/os/unix/ngx_freebsd_init.c 2006-08-30 10:39:17.000000000 +0000 +++ nginx-0.8.53/src/os/unix/ngx_freebsd_init.c 2008-12-17 20:47:18.000000000 +0000 @@ -19,17 +19,16 @@ /* FreeBSD 4.9 */ int ngx_freebsd_machdep_hlt_logical_cpus; -/* FreeBSD 5.0 */ -int ngx_freebsd_kern_ipc_zero_copy_send; - -ngx_uint_t ngx_freebsd_sendfile_nbytes_bug; -ngx_uint_t ngx_freebsd_use_tcp_nopush; +ngx_uint_t ngx_freebsd_sendfile_nbytes_bug; +ngx_uint_t ngx_freebsd_use_tcp_nopush; +ngx_uint_t ngx_freebsd_debug_malloc; static ngx_os_io_t ngx_freebsd_io = { ngx_unix_recv, ngx_readv_chain, + ngx_udp_unix_recv, ngx_unix_send, #if (NGX_HAVE_SENDFILE) ngx_freebsd_sendfile_chain, @@ -66,10 +65,6 @@ &ngx_freebsd_kern_ipc_somaxconn, sizeof(ngx_freebsd_kern_ipc_somaxconn), 0 }, - { "kern.ipc.zero_copy.send", - &ngx_freebsd_kern_ipc_zero_copy_send, - sizeof(ngx_freebsd_kern_ipc_zero_copy_send), 0 }, - { NULL, NULL, 0, 0 } }; @@ -85,6 +80,16 @@ malloc_options = "J"; #endif + ngx_freebsd_debug_malloc = 1; + +#else + char *mo; + + mo = getenv("MALLOC_OPTIONS"); + + if (mo && ngx_strchr(mo, 'J')) { + ngx_freebsd_debug_malloc = 1; + } #endif } diff -Nru nginx-0.5.33/src/os/unix/ngx_freebsd_rfork_thread.h nginx-0.8.53/src/os/unix/ngx_freebsd_rfork_thread.h --- nginx-0.5.33/src/os/unix/ngx_freebsd_rfork_thread.h 2006-08-30 10:39:17.000000000 +0000 +++ nginx-0.8.53/src/os/unix/ngx_freebsd_rfork_thread.h 2009-11-02 12:41:56.000000000 +0000 @@ -118,5 +118,4 @@ typedef int (*ngx_rfork_thread_func_pt)(void *arg); - #endif /* _NGX_FREEBSD_RFORK_THREAD_H_INCLUDED_ */ diff -Nru nginx-0.5.33/src/os/unix/ngx_freebsd_sendfile_chain.c nginx-0.8.53/src/os/unix/ngx_freebsd_sendfile_chain.c --- nginx-0.5.33/src/os/unix/ngx_freebsd_sendfile_chain.c 2007-11-07 13:54:40.000000000 +0000 +++ nginx-0.8.53/src/os/unix/ngx_freebsd_sendfile_chain.c 2009-09-04 09:53:09.000000000 +0000 @@ -40,7 +40,7 @@ ngx_chain_t * ngx_freebsd_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit) { - int rc; + int rc, flags; u_char *prev; off_t size, send, prev_send, aligned, sent, fprev; size_t header_size, file_size; @@ -78,6 +78,7 @@ send = 0; eagain = 0; + flags = 0; header.elts = headers; header.size = sizeof(struct iovec); @@ -261,36 +262,46 @@ sent = 0; +#if (NGX_HAVE_AIO_SENDFILE) + flags = c->aio_sendfile ? SF_NODISKIO : 0; +#endif + rc = sendfile(file->file->fd, c->fd, file->file_pos, - file_size + header_size, &hdtr, &sent, 0); + file_size + header_size, &hdtr, &sent, flags); if (rc == -1) { err = ngx_errno; - if (err == NGX_EAGAIN || err == NGX_EINTR) { - if (err == NGX_EINTR) { - eintr = 1; + switch (err) { + case NGX_EAGAIN: + eagain = 1; + break; - } else { - eagain = 1; - } + case NGX_EINTR: + eintr = 1; + break; - ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, err, - "sendfile() sent only %O bytes", sent); +#if (NGX_HAVE_AIO_SENDFILE) + case NGX_EBUSY: + c->busy_sendfile = file; + break; +#endif - } else { + default: wev->error = 1; (void) ngx_connection_error(c, err, "sendfile() failed"); return NGX_CHAIN_ERROR; } - } + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, err, + "sendfile() sent only %O bytes", sent); /* * sendfile() in FreeBSD 3.x-4.x may return value >= 0 * on success, although only 0 is documented */ - if (rc >= 0 && sent == 0) { + } else if (rc >= 0 && sent == 0) { /* * if rc is OK and sent equal to zero, then someone @@ -299,8 +310,8 @@ */ ngx_log_error(NGX_LOG_ALERT, c->log, 0, - "sendfile() reported that \"%s\" was truncated", - file->file->name.data); + "sendfile() reported that \"%s\" was truncated at %O", + file->file->name.data, file->file_pos); return NGX_CHAIN_ERROR; } @@ -318,19 +329,22 @@ if (rc == -1) { err = ngx_errno; - if (err == NGX_EAGAIN || err == NGX_EINTR) { - if (err == NGX_EINTR) { - eintr = 1; - } + switch (err) { + case NGX_EAGAIN: + break; - ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err, - "writev() not ready"); + case NGX_EINTR: + eintr = 1; + break; - } else { + default: wev->error = 1; ngx_connection_error(c, err, "writev() failed"); return NGX_CHAIN_ERROR; } + + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err, + "writev() not ready"); } sent = rc > 0 ? rc : 0; @@ -379,6 +393,12 @@ break; } +#if (NGX_HAVE_AIO_SENDFILE) + if (c->busy_sendfile) { + return cl; + } +#endif + if (eagain) { /* diff -Nru nginx-0.5.33/src/os/unix/ngx_gcc_atomic_x86.h nginx-0.8.53/src/os/unix/ngx_gcc_atomic_x86.h --- nginx-0.5.33/src/os/unix/ngx_gcc_atomic_x86.h 2007-01-11 16:07:38.000000000 +0000 +++ nginx-0.8.53/src/os/unix/ngx_gcc_atomic_x86.h 2009-11-25 17:56:53.000000000 +0000 @@ -122,5 +122,5 @@ #define ngx_memory_barrier() __asm__ volatile ("" ::: "memory") -/* old as does not support "pause" opcode */ +/* old "as" does not support "pause" opcode */ #define ngx_cpu_pause() __asm__ (".byte 0xf3, 0x90") diff -Nru nginx-0.5.33/src/os/unix/ngx_linux_aio_read.c nginx-0.8.53/src/os/unix/ngx_linux_aio_read.c --- nginx-0.5.33/src/os/unix/ngx_linux_aio_read.c 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/src/os/unix/ngx_linux_aio_read.c 2010-10-12 12:11:11.000000000 +0000 @@ -0,0 +1,134 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include + + +extern int ngx_eventfd; +extern aio_context_t ngx_aio_ctx; + + +static void ngx_file_aio_event_handler(ngx_event_t *ev); + + +static long +io_submit(aio_context_t ctx, long n, struct iocb **paiocb) +{ + return syscall(SYS_io_submit, ctx, n, paiocb); +} + + +ssize_t +ngx_file_aio_read(ngx_file_t *file, u_char *buf, size_t size, off_t offset, + ngx_pool_t *pool) +{ + long n; + struct iocb *piocb[1]; + ngx_event_t *ev; + ngx_event_aio_t *aio; + + if (!ngx_file_aio) { + return ngx_read_file(file, buf, size, offset); + } + + aio = file->aio; + + if (aio == NULL) { + aio = ngx_pcalloc(pool, sizeof(ngx_event_aio_t)); + if (aio == NULL) { + return NGX_ERROR; + } + + aio->file = file; + aio->fd = file->fd; + aio->event.data = aio; + aio->event.ready = 1; + aio->event.log = file->log; + file->aio = aio; + } + + ev = &aio->event; + + if (!ev->ready) { + ngx_log_error(NGX_LOG_ALERT, file->log, 0, + "second aio post for \"%V\"", &file->name); + return NGX_AGAIN; + } + + ngx_log_debug4(NGX_LOG_DEBUG_CORE, file->log, 0, + "aio complete:%d @%O:%z %V", + ev->complete, offset, size, &file->name); + + if (ev->complete) { + ev->active = 0; + ev->complete = 0; + + if (aio->res >= 0) { + ngx_set_errno(0); + return aio->res; + } + + ngx_set_errno(-aio->res); + return NGX_ERROR; + } + + ngx_memzero(&aio->aiocb, sizeof(struct iocb)); + + aio->aiocb.aio_data = (uint64_t) (uintptr_t) ev; + aio->aiocb.aio_lio_opcode = IOCB_CMD_PREAD; + aio->aiocb.aio_fildes = file->fd; + aio->aiocb.aio_buf = (uint64_t) (uintptr_t) buf; + aio->aiocb.aio_nbytes = size; + aio->aiocb.aio_offset = offset; + aio->aiocb.aio_flags = IOCB_FLAG_RESFD; + aio->aiocb.aio_resfd = ngx_eventfd; + + ev->handler = ngx_file_aio_event_handler; + + piocb[0] = &aio->aiocb; + + n = io_submit(ngx_aio_ctx, 1, piocb); + + if (n == 1) { + ev->active = 1; + ev->ready = 0; + ev->complete = 0; + + return NGX_AGAIN; + } + + n = -n; + + if (n == NGX_EAGAIN) { + return ngx_read_file(file, buf, size, offset); + } + + ngx_log_error(NGX_LOG_CRIT, file->log, n, + "io_submit(\"%V\") failed", &file->name); + + if (n == NGX_ENOSYS) { + ngx_file_aio = 0; + return ngx_read_file(file, buf, size, offset); + } + + return NGX_ERROR; +} + + +static void +ngx_file_aio_event_handler(ngx_event_t *ev) +{ + ngx_event_aio_t *aio; + + aio = ev->data; + + ngx_log_debug2(NGX_LOG_DEBUG_CORE, ev->log, 0, + "aio event handler fd:%d %V", aio->fd, &aio->file->name); + + aio->handler(ev); +} diff -Nru nginx-0.5.33/src/os/unix/ngx_linux_config.h nginx-0.8.53/src/os/unix/ngx_linux_config.h --- nginx-0.5.33/src/os/unix/ngx_linux_config.h 2007-09-22 18:59:05.000000000 +0000 +++ nginx-0.8.53/src/os/unix/ngx_linux_config.h 2009-08-28 08:12:35.000000000 +0000 @@ -28,6 +28,7 @@ #include #include #include +#include /* statfs() */ #include #include @@ -47,9 +48,11 @@ #include /* tzset() */ #include /* memalign() */ +#include /* IOV_MAX */ #include #include #include +#include /* uname() */ #include @@ -68,7 +71,7 @@ #endif -#if (NGX_HAVE_POLL) +#if (NGX_HAVE_POLL || NGX_HAVE_RTSIG) #include #endif @@ -78,6 +81,13 @@ #endif +#if (NGX_HAVE_FILE_AIO) +#include +#include +typedef struct iocb ngx_aiocb_t; +#endif + + #define NGX_LISTEN_BACKLOG 511 @@ -92,11 +102,6 @@ #endif -#ifndef NGX_HAVE_GNU_CRYPT_R -#define NGX_HAVE_GNU_CRYPT_R 1 -#endif - - #ifndef NGX_HAVE_INHERITED_NONBLOCK #define NGX_HAVE_INHERITED_NONBLOCK 0 #endif diff -Nru nginx-0.5.33/src/os/unix/ngx_linux_init.c nginx-0.8.53/src/os/unix/ngx_linux_init.c --- nginx-0.5.33/src/os/unix/ngx_linux_init.c 2006-12-26 11:50:56.000000000 +0000 +++ nginx-0.8.53/src/os/unix/ngx_linux_init.c 2008-03-10 14:35:21.000000000 +0000 @@ -8,19 +8,16 @@ #include -static ngx_int_t ngx_linux_procfs(char *name, char *buf, size_t len, - ngx_log_t *log); +u_char ngx_linux_kern_ostype[50]; +u_char ngx_linux_kern_osrelease[50]; - -char ngx_linux_kern_ostype[50]; -char ngx_linux_kern_osrelease[50]; - -int ngx_linux_rtsig_max; +int ngx_linux_rtsig_max; static ngx_os_io_t ngx_linux_io = { ngx_unix_recv, ngx_readv_chain, + ngx_udp_unix_recv, ngx_unix_send, #if (NGX_HAVE_SENDFILE) ngx_linux_sendfile_chain, @@ -35,26 +32,24 @@ ngx_int_t ngx_os_specific_init(ngx_log_t *log) { - int name[2]; - size_t len; - ngx_err_t err; + struct utsname u; - if (ngx_linux_procfs("/proc/sys/kernel/ostype", - ngx_linux_kern_ostype, - sizeof(ngx_linux_kern_ostype), log) - == -1) - { + if (uname(&u) == -1) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, "uname() failed"); return NGX_ERROR; } - if (ngx_linux_procfs("/proc/sys/kernel/osrelease", - ngx_linux_kern_osrelease, - sizeof(ngx_linux_kern_osrelease), log) - == -1) - { - return NGX_ERROR; - } + (void) ngx_cpystrn(ngx_linux_kern_ostype, (u_char *) u.sysname, + sizeof(ngx_linux_kern_ostype)); + + (void) ngx_cpystrn(ngx_linux_kern_osrelease, (u_char *) u.release, + sizeof(ngx_linux_kern_osrelease)); +#if (NGX_HAVE_RTSIG) + { + int name[2]; + size_t len; + ngx_err_t err; name[0] = CTL_KERN; name[1] = KERN_RTSIGMAX; @@ -73,6 +68,8 @@ ngx_linux_rtsig_max = 0; } + } +#endif ngx_os_io = ngx_linux_io; @@ -86,39 +83,8 @@ ngx_log_error(NGX_LOG_NOTICE, log, 0, "OS: %s %s", ngx_linux_kern_ostype, ngx_linux_kern_osrelease); +#if (NGX_HAVE_RTSIG) ngx_log_error(NGX_LOG_NOTICE, log, 0, "sysctl(KERN_RTSIGMAX): %d", ngx_linux_rtsig_max); -} - - -static ngx_int_t -ngx_linux_procfs(char *name, char *buf, size_t len, ngx_log_t *log) -{ - int n; - ngx_fd_t fd; - - fd = open(name, O_RDONLY); - - if (fd == NGX_INVALID_FILE) { - ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, - "open(\"%s\") failed", name); - - return NGX_ERROR; - } - - n = read(fd, buf, len); - - if (n == -1) { - ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, - "read(\"%s\") failed", name); - - } else { - if (buf[n - 1] == '\n') { - buf[--n] = '\0'; - } - } - - ngx_close_file(fd); - - return n; +#endif } diff -Nru nginx-0.5.33/src/os/unix/ngx_linux_sendfile_chain.c nginx-0.8.53/src/os/unix/ngx_linux_sendfile_chain.c --- nginx-0.5.33/src/os/unix/ngx_linux_sendfile_chain.c 2007-11-07 13:54:40.000000000 +0000 +++ nginx-0.8.53/src/os/unix/ngx_linux_sendfile_chain.c 2009-08-30 09:42:29.000000000 +0000 @@ -263,19 +263,22 @@ if (rc == -1) { err = ngx_errno; - if (err == NGX_EAGAIN || err == NGX_EINTR) { - if (err == NGX_EINTR) { - eintr = 1; - } + switch (err) { + case NGX_EAGAIN: + break; + + case NGX_EINTR: + eintr = 1; + break; - ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err, - "sendfile() is not ready"); - - } else { + default: wev->error = 1; ngx_connection_error(c, err, "sendfile() failed"); return NGX_CHAIN_ERROR; } + + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err, + "sendfile() is not ready"); } sent = rc > 0 ? rc : 0; @@ -290,19 +293,22 @@ if (rc == -1) { err = ngx_errno; - if (err == NGX_EAGAIN || err == NGX_EINTR) { - if (err == NGX_EINTR) { - eintr = 1; - } + switch (err) { + case NGX_EAGAIN: + break; + + case NGX_EINTR: + eintr = 1; + break; - ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err, - "writev() not ready"); - - } else { + default: wev->error = 1; ngx_connection_error(c, err, "writev() failed"); return NGX_CHAIN_ERROR; } + + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err, + "writev() not ready"); } sent = rc > 0 ? rc : 0; diff -Nru nginx-0.5.33/src/os/unix/ngx_os.h nginx-0.8.53/src/os/unix/ngx_os.h --- nginx-0.5.33/src/os/unix/ngx_os.h 2006-08-30 10:39:17.000000000 +0000 +++ nginx-0.8.53/src/os/unix/ngx_os.h 2009-08-25 09:09:13.000000000 +0000 @@ -13,7 +13,6 @@ #define NGX_IO_SENDFILE 1 -#define NGX_IO_ZEROCOPY 2 typedef ssize_t (*ngx_recv_pt)(ngx_connection_t *c, u_char *buf, size_t size); @@ -25,6 +24,7 @@ typedef struct { ngx_recv_pt recv; ngx_recv_chain_pt recv_chain; + ngx_recv_pt udp_recv; ngx_send_pt send; ngx_send_chain_pt send_chain; ngx_uint_t flags; @@ -37,14 +37,24 @@ ngx_int_t ngx_os_specific_init(ngx_log_t *log); void ngx_os_specific_status(ngx_log_t *log); ngx_int_t ngx_daemon(ngx_log_t *log); +ngx_int_t ngx_os_signal_process(ngx_cycle_t *cycle, char *sig, ngx_int_t pid); ssize_t ngx_unix_recv(ngx_connection_t *c, u_char *buf, size_t size); ssize_t ngx_readv_chain(ngx_connection_t *c, ngx_chain_t *entry); +ssize_t ngx_udp_unix_recv(ngx_connection_t *c, u_char *buf, size_t size); ssize_t ngx_unix_send(ngx_connection_t *c, u_char *buf, size_t size); ngx_chain_t *ngx_writev_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit); +#if (NGX_HAVE_AIO) +ssize_t ngx_aio_read(ngx_connection_t *c, u_char *buf, size_t size); +ssize_t ngx_aio_read_chain(ngx_connection_t *c, ngx_chain_t *cl); +ssize_t ngx_aio_write(ngx_connection_t *c, u_char *buf, size_t size); +ngx_chain_t *ngx_aio_write_chain(ngx_connection_t *c, ngx_chain_t *in, + off_t limit); +#endif + extern ngx_os_io_t ngx_os_io; extern ngx_int_t ngx_ncpu; @@ -52,7 +62,6 @@ extern ngx_uint_t ngx_inherited_nonblocking; extern ngx_uint_t ngx_tcp_nodelay_and_tcp_nopush; -#define ngx_stderr_fileno STDERR_FILENO #if (NGX_FREEBSD) #include @@ -64,6 +73,10 @@ #elif (NGX_SOLARIS) #include + + +#elif (NGX_DARWIN) +#include #endif diff -Nru nginx-0.5.33/src/os/unix/ngx_posix_config.h nginx-0.8.53/src/os/unix/ngx_posix_config.h --- nginx-0.5.33/src/os/unix/ngx_posix_config.h 2007-09-22 18:59:05.000000000 +0000 +++ nginx-0.8.53/src/os/unix/ngx_posix_config.h 2009-08-28 08:12:35.000000000 +0000 @@ -19,6 +19,12 @@ #endif +#ifdef __CYGWIN__ +#define timezonevar /* timezone is variable */ +#define NGX_BROKEN_SCM_RIGHTS 1 +#endif + + #include #include #if (NGX_HAVE_UNISTD_H) @@ -38,6 +44,15 @@ #include #include #include +#if (NGX_HAVE_SYS_PARAM_H) +#include /* statfs() */ +#endif +#if (NGX_HAVE_SYS_MOUNT_H) +#include /* statfs() */ +#endif +#if (NGX_HAVE_SYS_STATVFS_H) +#include /* statvfs() */ +#endif #if (NGX_HAVE_SYS_FILIO_H) #include /* FIONBIO */ @@ -64,6 +79,15 @@ #include /* IOV_MAX */ #endif +#ifdef __CYGWIN__ +#include /* memalign() */ +#endif + +#if (NGX_HAVE_CRYPT_H) +#include +#endif + + #ifndef IOV_MAX #define IOV_MAX 16 #endif @@ -88,6 +112,12 @@ #endif +#if (NGX_HAVE_FILE_AIO) +#include +typedef struct aiocb ngx_aiocb_t; +#endif + + #define NGX_LISTEN_BACKLOG 511 @@ -95,11 +125,16 @@ #include /* ALIGN() */ -/* FreeBSD 3.x has no CMSG_SPACE() at all and has the broken CMSG_DATA() */ +/* + * FreeBSD 3.x has no CMSG_SPACE() and CMSG_LEN() and has the broken CMSG_DATA() + */ #undef CMSG_SPACE #define CMSG_SPACE(l) (ALIGN(sizeof(struct cmsghdr)) + ALIGN(l)) +#undef CMSG_LEN +#define CMSG_LEN(l) (ALIGN(sizeof(struct cmsghdr)) + (l)) + #undef CMSG_DATA #define CMSG_DATA(cmsg) ((u_char *)(cmsg) + ALIGN(sizeof(struct cmsghdr))) diff -Nru nginx-0.5.33/src/os/unix/ngx_posix_init.c nginx-0.8.53/src/os/unix/ngx_posix_init.c --- nginx-0.5.33/src/os/unix/ngx_posix_init.c 2006-11-20 08:51:45.000000000 +0000 +++ nginx-0.8.53/src/os/unix/ngx_posix_init.c 2009-11-05 17:09:02.000000000 +0000 @@ -21,7 +21,8 @@ ngx_os_io_t ngx_os_io = { ngx_unix_recv, ngx_readv_chain, - NULL, + ngx_udp_unix_recv, + ngx_unix_send, ngx_writev_chain, 0 }; @@ -43,8 +44,6 @@ ngx_pagesize = getpagesize(); ngx_cacheline_size = NGX_CPU_CACHE_LINE; - n = ngx_pagesize; - for (n = ngx_pagesize; n >>= 1; ngx_pagesize_shift++) { /* void */ } if (ngx_ncpu == 0) { diff -Nru nginx-0.5.33/src/os/unix/ngx_process.c nginx-0.8.53/src/os/unix/ngx_process.c --- nginx-0.5.33/src/os/unix/ngx_process.c 2006-12-23 15:05:26.000000000 +0000 +++ nginx-0.8.53/src/os/unix/ngx_process.c 2010-07-08 13:49:21.000000000 +0000 @@ -11,9 +11,10 @@ typedef struct { - int signo; - char *signame; - void (*handler)(int signo); + int signo; + char *signame; + char *name; + void (*handler)(int signo); } ngx_signal_t; @@ -36,39 +37,47 @@ ngx_signal_t signals[] = { { ngx_signal_value(NGX_RECONFIGURE_SIGNAL), "SIG" ngx_value(NGX_RECONFIGURE_SIGNAL), + "reload", ngx_signal_handler }, { ngx_signal_value(NGX_REOPEN_SIGNAL), "SIG" ngx_value(NGX_REOPEN_SIGNAL), + "reopen", ngx_signal_handler }, { ngx_signal_value(NGX_NOACCEPT_SIGNAL), "SIG" ngx_value(NGX_NOACCEPT_SIGNAL), + "", ngx_signal_handler }, { ngx_signal_value(NGX_TERMINATE_SIGNAL), "SIG" ngx_value(NGX_TERMINATE_SIGNAL), + "stop", ngx_signal_handler }, { ngx_signal_value(NGX_SHUTDOWN_SIGNAL), "SIG" ngx_value(NGX_SHUTDOWN_SIGNAL), + "quit", ngx_signal_handler }, { ngx_signal_value(NGX_CHANGEBIN_SIGNAL), "SIG" ngx_value(NGX_CHANGEBIN_SIGNAL), + "", ngx_signal_handler }, - { SIGALRM, "SIGALRM", ngx_signal_handler }, + { SIGALRM, "SIGALRM", "", ngx_signal_handler }, - { SIGINT, "SIGINT", ngx_signal_handler }, + { SIGINT, "SIGINT", "", ngx_signal_handler }, - { SIGIO, "SIGIO", ngx_signal_handler }, + { SIGIO, "SIGIO", "", ngx_signal_handler }, - { SIGCHLD, "SIGCHLD", ngx_signal_handler }, + { SIGCHLD, "SIGCHLD", "", ngx_signal_handler }, - { SIGPIPE, "SIGPIPE, SIG_IGN", SIG_IGN }, + { SIGSYS, "SIGSYS, SIG_IGN", "", SIG_IGN }, - { 0, NULL, NULL } + { SIGPIPE, "SIGPIPE, SIG_IGN", "", SIG_IGN }, + + { 0, NULL, "", NULL } }; @@ -207,21 +216,33 @@ switch (respawn) { + case NGX_PROCESS_NORESPAWN: + ngx_processes[s].respawn = 0; + ngx_processes[s].just_spawn = 0; + ngx_processes[s].detached = 0; + break; + + case NGX_PROCESS_JUST_SPAWN: + ngx_processes[s].respawn = 0; + ngx_processes[s].just_spawn = 1; + ngx_processes[s].detached = 0; + break; + case NGX_PROCESS_RESPAWN: ngx_processes[s].respawn = 1; - ngx_processes[s].just_respawn = 0; + ngx_processes[s].just_spawn = 0; ngx_processes[s].detached = 0; break; case NGX_PROCESS_JUST_RESPAWN: ngx_processes[s].respawn = 1; - ngx_processes[s].just_respawn = 1; + ngx_processes[s].just_spawn = 1; ngx_processes[s].detached = 0; break; case NGX_PROCESS_DETACHED: ngx_processes[s].respawn = 0; - ngx_processes[s].just_respawn = 0; + ngx_processes[s].just_spawn = 0; ngx_processes[s].detached = 1; break; } @@ -296,7 +317,7 @@ } } - ngx_time_update(0, 0); + ngx_time_sigsafe_update(); action = ""; @@ -352,6 +373,7 @@ break; case SIGALRM: + ngx_sigalrm = 1; break; case SIGIO: @@ -366,6 +388,7 @@ break; case NGX_PROCESS_WORKER: + case NGX_PROCESS_HELPER: switch (signo) { case ngx_signal_value(NGX_NOACCEPT_SIGNAL): @@ -452,20 +475,21 @@ * * When several processes exit at the same time FreeBSD may * erroneously call the signal handler for exited process - * despite waitpid() may be already called for this process + * despite waitpid() may be already called for this process. */ if (err == NGX_ECHILD) { - ngx_log_error(NGX_LOG_INFO, ngx_cycle->log, errno, - "waitpid() failed"); + ngx_log_error(NGX_LOG_INFO, ngx_cycle->log, 0, + "waitpid() failed (%d: %s)", + err, ngx_sigsafe_strerror(err)); return; } #endif - ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, errno, - "waitpid() failed"); - + ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, + "waitpid() failed (%d: %s)", + err, ngx_sigsafe_strerror(err)); return; } @@ -494,10 +518,16 @@ } if (WTERMSIG(status)) { +#ifdef WCOREDUMP ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "%s %P exited on signal %d%s", process, pid, WTERMSIG(status), WCOREDUMP(status) ? " (core dumped)" : ""); +#else + ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, + "%s %P exited on signal %d", + process, pid, WTERMSIG(status)); +#endif } else { ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0, @@ -507,8 +537,9 @@ if (WEXITSTATUS(status) == 2 && ngx_processes[i].respawn) { ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, - "%s %P exited with fatal code %d and could not respawn", - process, pid, WEXITSTATUS(status)); + "%s %P exited with fatal code %d " + "and can not be respawn", + process, pid, WEXITSTATUS(status)); ngx_processes[i].respawn = 0; } } @@ -533,3 +564,23 @@ ngx_abort(); } } + + +ngx_int_t +ngx_os_signal_process(ngx_cycle_t *cycle, char *name, ngx_int_t pid) +{ + ngx_signal_t *sig; + + for (sig = signals; sig->signo != 0; sig++) { + if (ngx_strcmp(name, sig->name) == 0) { + if (kill(pid, sig->signo) != -1) { + return 0; + } + + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "kill(%P, %d) failed", pid, sig->signo); + } + } + + return 1; +} diff -Nru nginx-0.5.33/src/os/unix/ngx_process_cycle.c nginx-0.8.53/src/os/unix/ngx_process_cycle.c --- nginx-0.5.33/src/os/unix/ngx_process_cycle.c 2007-09-23 19:01:48.000000000 +0000 +++ nginx-0.8.53/src/os/unix/ngx_process_cycle.c 2010-09-15 15:24:21.000000000 +0000 @@ -12,9 +12,11 @@ static void ngx_start_worker_processes(ngx_cycle_t *cycle, ngx_int_t n, ngx_int_t type); -static void ngx_start_garbage_collector(ngx_cycle_t *cycle, ngx_int_t type); +static void ngx_start_cache_manager_processes(ngx_cycle_t *cycle, + ngx_uint_t respawn); +static void ngx_pass_open_channel(ngx_cycle_t *cycle, ngx_channel_t *ch); static void ngx_signal_worker_processes(ngx_cycle_t *cycle, int signo); -static ngx_uint_t ngx_reap_childs(ngx_cycle_t *cycle); +static ngx_uint_t ngx_reap_children(ngx_cycle_t *cycle); static void ngx_master_process_exit(ngx_cycle_t *cycle); static void ngx_worker_process_cycle(ngx_cycle_t *cycle, void *data); static void ngx_worker_process_init(ngx_cycle_t *cycle, ngx_uint_t priority); @@ -24,9 +26,9 @@ static void ngx_wakeup_worker_threads(ngx_cycle_t *cycle); static ngx_thread_value_t ngx_worker_thread_cycle(void *data); #endif -#if 0 -static void ngx_garbage_collector_cycle(ngx_cycle_t *cycle, void *data); -#endif +static void ngx_cache_manager_process_cycle(ngx_cycle_t *cycle, void *data); +static void ngx_cache_manager_process_handler(ngx_event_t *ev); +static void ngx_cache_loader_process_handler(ngx_event_t *ev); ngx_uint_t ngx_process; @@ -35,6 +37,7 @@ sig_atomic_t ngx_reap; sig_atomic_t ngx_sigio; +sig_atomic_t ngx_sigalrm; sig_atomic_t ngx_terminate; sig_atomic_t ngx_quit; sig_atomic_t ngx_debug_quit; @@ -62,6 +65,15 @@ static u_char master_process[] = "master process"; +static ngx_cache_manager_ctx_t ngx_cache_manager_ctx = { + ngx_cache_manager_process_handler, "cache manager process", 0 +}; + +static ngx_cache_manager_ctx_t ngx_cache_loader_ctx = { + ngx_cache_loader_process_handler, "cache loader process", 60000 +}; + + static ngx_cycle_t ngx_exit_cycle; static ngx_log_t ngx_exit_log; static ngx_open_file_t ngx_exit_log_file; @@ -74,7 +86,7 @@ u_char *p; size_t size; ngx_int_t i; - ngx_uint_t n; + ngx_uint_t n, sigio; sigset_t set; struct itimerval itv; ngx_uint_t live; @@ -108,7 +120,7 @@ size += ngx_strlen(ngx_argv[i]) + 1; } - title = ngx_palloc(cycle->pool, size); + title = ngx_pnalloc(cycle->pool, size); p = ngx_cpymem(title, master_process, sizeof(master_process) - 1); for (i = 0; i < ngx_argc; i++) { @@ -123,18 +135,23 @@ ngx_start_worker_processes(cycle, ccf->worker_processes, NGX_PROCESS_RESPAWN); - ngx_start_garbage_collector(cycle, NGX_PROCESS_RESPAWN); + ngx_start_cache_manager_processes(cycle, 0); ngx_new_binary = 0; delay = 0; + sigio = 0; live = 1; for ( ;; ) { if (delay) { - delay *= 2; + if (ngx_sigalrm) { + sigio = 0; + delay *= 2; + ngx_sigalrm = 0; + } ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, - "temination cycle: %d", delay); + "termination cycle: %d", delay); itv.it_interval.tv_sec = 0; itv.it_interval.tv_usec = 0; @@ -151,15 +168,16 @@ sigsuspend(&set); - ngx_time_update(0, 0); + ngx_time_update(); - ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "wake up"); + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, + "wake up, sigio %i", sigio); if (ngx_reap) { ngx_reap = 0; - ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "reap childs"); + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "reap children"); - live = ngx_reap_childs(cycle); + live = ngx_reap_children(cycle); } if (!live && (ngx_terminate || ngx_quit)) { @@ -171,6 +189,13 @@ delay = 50; } + if (sigio) { + sigio--; + continue; + } + + sigio = ccf->worker_processes + 2 /* cache processes */; + if (delay > 1000) { ngx_signal_worker_processes(cycle, SIGKILL); } else { @@ -204,7 +229,7 @@ if (ngx_new_binary) { ngx_start_worker_processes(cycle, ccf->worker_processes, NGX_PROCESS_RESPAWN); - ngx_start_garbage_collector(cycle, NGX_PROCESS_RESPAWN); + ngx_start_cache_manager_processes(cycle, 0); ngx_noaccepting = 0; continue; @@ -223,7 +248,7 @@ ngx_core_module); ngx_start_worker_processes(cycle, ccf->worker_processes, NGX_PROCESS_JUST_RESPAWN); - ngx_start_garbage_collector(cycle, NGX_PROCESS_JUST_RESPAWN); + ngx_start_cache_manager_processes(cycle, 1); live = 1; ngx_signal_worker_processes(cycle, ngx_signal_value(NGX_SHUTDOWN_SIGNAL)); @@ -233,7 +258,7 @@ ngx_restart = 0; ngx_start_worker_processes(cycle, ccf->worker_processes, NGX_PROCESS_RESPAWN); - ngx_start_garbage_collector(cycle, NGX_PROCESS_RESPAWN); + ngx_start_cache_manager_processes(cycle, 0); live = 1; } @@ -266,8 +291,6 @@ { ngx_uint_t i; - ngx_init_temp_number(); - for (i = 0; ngx_modules[i]; i++) { if (ngx_modules[i]->init_process) { if (ngx_modules[i]->init_process(cycle) == NGX_ERROR) { @@ -318,7 +341,7 @@ static void ngx_start_worker_processes(ngx_cycle_t *cycle, ngx_int_t n, ngx_int_t type) { - ngx_int_t i, s; + ngx_int_t i; ngx_channel_t ch; ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "start worker processes"); @@ -336,48 +359,70 @@ ch.slot = ngx_process_slot; ch.fd = ngx_processes[ngx_process_slot].channel[0]; - for (s = 0; s < ngx_last_process; s++) { + ngx_pass_open_channel(cycle, &ch); + } +} - if (s == ngx_process_slot - || ngx_processes[s].pid == -1 - || ngx_processes[s].channel[0] == -1) - { - continue; - } - ngx_log_debug6(NGX_LOG_DEBUG_CORE, cycle->log, 0, - "pass channel s:%d pid:%P fd:%d to s:%i pid:%P fd:%d", - ch.slot, ch.pid, ch.fd, - s, ngx_processes[s].pid, - ngx_processes[s].channel[0]); +static void +ngx_start_cache_manager_processes(ngx_cycle_t *cycle, ngx_uint_t respawn) +{ + ngx_uint_t i, manager, loader; + ngx_path_t **path; + ngx_channel_t ch; - /* TODO: NGX_AGAIN */ + manager = 0; + loader = 0; - ngx_write_channel(ngx_processes[s].channel[0], - &ch, sizeof(ngx_channel_t), cycle->log); + path = ngx_cycle->pathes.elts; + for (i = 0; i < ngx_cycle->pathes.nelts; i++) { + + if (path[i]->manager) { + manager = 1; } - } -} + if (path[i]->loader) { + loader = 1; + } + } -static void -ngx_start_garbage_collector(ngx_cycle_t *cycle, ngx_int_t type) -{ -#if 0 - ngx_int_t i; - ngx_channel_t ch; + if (manager == 0) { + return; + } - ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "start garbage collector"); + ngx_spawn_process(cycle, ngx_cache_manager_process_cycle, + &ngx_cache_manager_ctx, "cache manager process", + respawn ? NGX_PROCESS_JUST_RESPAWN : NGX_PROCESS_RESPAWN); ch.command = NGX_CMD_OPEN_CHANNEL; + ch.pid = ngx_processes[ngx_process_slot].pid; + ch.slot = ngx_process_slot; + ch.fd = ngx_processes[ngx_process_slot].channel[0]; + + ngx_pass_open_channel(cycle, &ch); + + if (loader == 0) { + return; + } - ngx_spawn_process(cycle, ngx_garbage_collector_cycle, NULL, - "garbage collector", type); + ngx_spawn_process(cycle, ngx_cache_manager_process_cycle, + &ngx_cache_loader_ctx, "cache loader process", + respawn ? NGX_PROCESS_JUST_SPAWN : NGX_PROCESS_NORESPAWN); + ch.command = NGX_CMD_OPEN_CHANNEL; ch.pid = ngx_processes[ngx_process_slot].pid; ch.slot = ngx_process_slot; ch.fd = ngx_processes[ngx_process_slot].channel[0]; + ngx_pass_open_channel(cycle, &ch); +} + + +static void +ngx_pass_open_channel(ngx_cycle_t *cycle, ngx_channel_t *ch) +{ + ngx_int_t i; + for (i = 0; i < ngx_last_process; i++) { if (i == ngx_process_slot @@ -389,16 +434,15 @@ ngx_log_debug6(NGX_LOG_DEBUG_CORE, cycle->log, 0, "pass channel s:%d pid:%P fd:%d to s:%i pid:%P fd:%d", - ch.slot, ch.pid, ch.fd, + ch->slot, ch->pid, ch->fd, i, ngx_processes[i].pid, ngx_processes[i].channel[0]); /* TODO: NGX_AGAIN */ ngx_write_channel(ngx_processes[i].channel[0], - &ch, sizeof(ngx_channel_t), cycle->log); + ch, sizeof(ngx_channel_t), cycle->log); } -#endif } @@ -409,6 +453,12 @@ ngx_err_t err; ngx_channel_t ch; +#if (NGX_BROKEN_SCM_RIGHTS) + + ch.command = 0; + +#else + switch (signo) { case ngx_signal_value(NGX_SHUTDOWN_SIGNAL): @@ -427,6 +477,8 @@ ch.command = 0; } +#endif + ch.fd = -1; @@ -440,14 +492,14 @@ ngx_processes[i].exited, ngx_processes[i].detached, ngx_processes[i].respawn, - ngx_processes[i].just_respawn); + ngx_processes[i].just_spawn); if (ngx_processes[i].detached || ngx_processes[i].pid == -1) { continue; } - if (ngx_processes[i].just_respawn) { - ngx_processes[i].just_respawn = 0; + if (ngx_processes[i].just_spawn) { + ngx_processes[i].just_spawn = 0; continue; } @@ -476,8 +528,7 @@ if (kill(ngx_processes[i].pid, signo) == -1) { err = ngx_errno; ngx_log_error(NGX_LOG_ALERT, cycle->log, err, - "kill(%P, %d) failed", - ngx_processes[i].pid, signo); + "kill(%P, %d) failed", ngx_processes[i].pid, signo); if (err == NGX_ESRCH) { ngx_processes[i].exited = 1; @@ -496,7 +547,7 @@ static ngx_uint_t -ngx_reap_childs(ngx_cycle_t *cycle) +ngx_reap_children(ngx_cycle_t *cycle) { ngx_int_t i, n; ngx_uint_t live; @@ -517,7 +568,7 @@ ngx_processes[i].exited, ngx_processes[i].detached, ngx_processes[i].respawn, - ngx_processes[i].just_respawn); + ngx_processes[i].just_spawn); if (ngx_processes[i].pid == -1) { continue; @@ -574,26 +625,7 @@ ch.slot = ngx_process_slot; ch.fd = ngx_processes[ngx_process_slot].channel[0]; - for (n = 0; n < ngx_last_process; n++) { - - if (n == ngx_process_slot - || ngx_processes[n].pid == -1 - || ngx_processes[n].channel[0] == -1) - { - continue; - } - - ngx_log_debug6(NGX_LOG_DEBUG_CORE, cycle->log, 0, - "pass channel s:%d pid:%P fd:%d to s:%i pid:%P fd:%d", - ch.slot, ch.pid, ch.fd, - n, ngx_processes[n].pid, - ngx_processes[n].channel[0]); - - /* TODO: NGX_AGAIN */ - - ngx_write_channel(ngx_processes[n].channel[0], - &ch, sizeof(ngx_channel_t), cycle->log); - } + ngx_pass_open_channel(cycle, &ch); live = 1; @@ -653,6 +685,8 @@ } } + ngx_close_listening_sockets(cycle); + /* * Copy ngx_cycle->log related data to the special static exit cycle, * log, and log file structures enough to allow a signal handler to log. @@ -679,17 +713,18 @@ { ngx_uint_t i; ngx_connection_t *c; -#if (NGX_THREADS) - ngx_int_t n; - ngx_err_t err; - ngx_core_conf_t *ccf; -#endif + + ngx_process = NGX_PROCESS_WORKER; ngx_worker_process_init(cycle, 1); ngx_setproctitle("worker process"); #if (NGX_THREADS) + { + ngx_int_t n; + ngx_err_t err; + ngx_core_conf_t *ccf; ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module); @@ -728,7 +763,7 @@ } } } - + } #endif for ( ;; ) { @@ -796,8 +831,6 @@ ngx_core_conf_t *ccf; ngx_listening_t *ls; - ngx_process = NGX_PROCESS_WORKER; - if (ngx_set_environment(cycle, NULL) == NULL) { /* fatal */ exit(2); @@ -823,13 +856,13 @@ } } - if (ccf->rlimit_core != NGX_CONF_UNSET_SIZE) { + if (ccf->rlimit_core != NGX_CONF_UNSET) { rlmt.rlim_cur = (rlim_t) ccf->rlimit_core; rlmt.rlim_max = (rlim_t) ccf->rlimit_core; if (setrlimit(RLIMIT_CORE, &rlmt) == -1) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, - "setrlimit(RLIMIT_CORE, %i) failed", + "setrlimit(RLIMIT_CORE, %O) failed", ccf->rlimit_core); } } @@ -910,8 +943,6 @@ "sigprocmask() failed"); } - ngx_init_temp_number(); - /* * disable deleting previous events for the listening sockets because * in the worker processes there are no events at all at this point @@ -987,23 +1018,24 @@ } } - if (ngx_quit) { + if (ngx_exiting) { c = cycle->connections; for (i = 0; i < cycle->connection_n; i++) { if (c[i].fd != -1 && c[i].read && !c[i].read->accept - && !c[i].read->channel) + && !c[i].read->channel + && !c[i].read->resolver) { ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, - "open socket #%d left in %ui connection, " - "aborting", + "open socket #%d left in connection %ui", c[i].fd, i); - ngx_debug_point(); + ngx_debug_quit = 1; } } if (ngx_debug_quit) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, "aborting"); ngx_debug_point(); } } @@ -1035,7 +1067,6 @@ ngx_channel_handler(ngx_event_t *ev) { ngx_int_t n; - ngx_socket_t fd; ngx_channel_t ch; ngx_connection_t *c; @@ -1048,75 +1079,74 @@ ngx_log_debug0(NGX_LOG_DEBUG_CORE, ev->log, 0, "channel handler"); - n = ngx_read_channel(c->fd, &ch, sizeof(ngx_channel_t), ev->log); + for ( ;; ) { - ngx_log_debug1(NGX_LOG_DEBUG_CORE, ev->log, 0, "channel: %i", n); + n = ngx_read_channel(c->fd, &ch, sizeof(ngx_channel_t), ev->log); - if (n == NGX_ERROR) { + ngx_log_debug1(NGX_LOG_DEBUG_CORE, ev->log, 0, "channel: %i", n); - ngx_free_connection(c); + if (n == NGX_ERROR) { - fd = c->fd; - c->fd = (ngx_socket_t) -1; + if (ngx_event_flags & NGX_USE_EPOLL_EVENT) { + ngx_del_conn(c, 0); + } - if (close(fd) == -1) { - ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno, - "close() channel failed"); + ngx_close_connection(c); + return; } - return; - } + if (ngx_event_flags & NGX_USE_EVENTPORT_EVENT) { + if (ngx_add_event(ev, NGX_READ_EVENT, 0) == NGX_ERROR) { + return; + } + } - if (ngx_event_flags & NGX_USE_EVENTPORT_EVENT) { - if (ngx_add_event(ev, NGX_READ_EVENT, 0) == NGX_ERROR) { + if (n == NGX_AGAIN) { return; } - } - if (n == NGX_AGAIN) { - return; - } + ngx_log_debug1(NGX_LOG_DEBUG_CORE, ev->log, 0, + "channel command: %d", ch.command); - ngx_log_debug1(NGX_LOG_DEBUG_CORE, ev->log, 0, - "channel command: %d", ch.command); + switch (ch.command) { - switch (ch.command) { + case NGX_CMD_QUIT: + ngx_quit = 1; + break; - case NGX_CMD_QUIT: - ngx_quit = 1; - break; + case NGX_CMD_TERMINATE: + ngx_terminate = 1; + break; - case NGX_CMD_TERMINATE: - ngx_terminate = 1; - break; + case NGX_CMD_REOPEN: + ngx_reopen = 1; + break; - case NGX_CMD_REOPEN: - ngx_reopen = 1; - break; + case NGX_CMD_OPEN_CHANNEL: - case NGX_CMD_OPEN_CHANNEL: + ngx_log_debug3(NGX_LOG_DEBUG_CORE, ev->log, 0, + "get channel s:%i pid:%P fd:%d", + ch.slot, ch.pid, ch.fd); - ngx_log_debug3(NGX_LOG_DEBUG_CORE, ev->log, 0, - "get channel s:%i pid:%P fd:%d", ch.slot, ch.pid, ch.fd); + ngx_processes[ch.slot].pid = ch.pid; + ngx_processes[ch.slot].channel[0] = ch.fd; + break; - ngx_processes[ch.slot].pid = ch.pid; - ngx_processes[ch.slot].channel[0] = ch.fd; - break; + case NGX_CMD_CLOSE_CHANNEL: - case NGX_CMD_CLOSE_CHANNEL: + ngx_log_debug4(NGX_LOG_DEBUG_CORE, ev->log, 0, + "close channel s:%i pid:%P our:%P fd:%d", + ch.slot, ch.pid, ngx_processes[ch.slot].pid, + ngx_processes[ch.slot].channel[0]); - ngx_log_debug4(NGX_LOG_DEBUG_CORE, ev->log, 0, - "close channel s:%i pid:%P our:%P fd:%d", - ch.slot, ch.pid, ngx_processes[ch.slot].pid, - ngx_processes[ch.slot].channel[0]); + if (close(ngx_processes[ch.slot].channel[0]) == -1) { + ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno, + "close() channel failed"); + } - if (close(ngx_processes[ch.slot].channel[0]) == -1) { - ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno, - "close() channel failed"); + ngx_processes[ch.slot].channel[0] = -1; + break; } - - ngx_processes[ch.slot].channel[0] = -1; - break; } } @@ -1249,27 +1279,33 @@ #endif -#if 0 - static void -ngx_garbage_collector_cycle(ngx_cycle_t *cycle, void *data) +ngx_cache_manager_process_cycle(ngx_cycle_t *cycle, void *data) { - ngx_uint_t i; - ngx_gc_t ctx; - ngx_path_t **path; - ngx_event_t *ev; + ngx_cache_manager_ctx_t *ctx = data; + + void *ident[4]; + ngx_event_t ev; + + cycle->connection_n = 512; + + ngx_process = NGX_PROCESS_HELPER; ngx_worker_process_init(cycle, 0); - ev = &cycle->read_events0[ngx_channel]; + ngx_close_listening_sockets(cycle); - ngx_accept_mutex = NULL; + ngx_memzero(&ev, sizeof(ngx_event_t)); + ev.handler = ctx->handler; + ev.data = ident; + ev.log = cycle->log; + ident[3] = (void *) -1; - ngx_setproctitle("garbage collector"); + ngx_use_accept_mutex = 0; -#if 0 - ngx_add_timer(ev, 60 * 1000); -#endif + ngx_setproctitle(ctx->name); + + ngx_add_timer(&ev, ctx->delay); for ( ;; ) { @@ -1284,19 +1320,61 @@ ngx_reopen_files(cycle, -1); } - path = cycle->pathes.elts; - for (i = 0; i < cycle->pathes.nelts; i++) { - ctx.path = path[i]; - ctx.log = cycle->log; - ctx.handler = path[i]->cleaner; + ngx_process_events_and_timers(cycle); + } +} - ngx_collect_garbage(&ctx, &path[i]->name, 0); - } - ngx_add_timer(ev, 60 * 60 * 1000); +static void +ngx_cache_manager_process_handler(ngx_event_t *ev) +{ + time_t next, n; + ngx_uint_t i; + ngx_path_t **path; - ngx_process_events_and_timers(cycle); + next = 60 * 60; + + path = ngx_cycle->pathes.elts; + for (i = 0; i < ngx_cycle->pathes.nelts; i++) { + + if (path[i]->manager) { + n = path[i]->manager(path[i]->data); + + next = (n <= next) ? n : next; + + ngx_time_update(); + } + } + + if (next == 0) { + next = 1; } + + ngx_add_timer(ev, next * 1000); } -#endif + +static void +ngx_cache_loader_process_handler(ngx_event_t *ev) +{ + ngx_uint_t i; + ngx_path_t **path; + ngx_cycle_t *cycle; + + cycle = (ngx_cycle_t *) ngx_cycle; + + path = cycle->pathes.elts; + for (i = 0; i < cycle->pathes.nelts; i++) { + + if (ngx_terminate || ngx_quit) { + break; + } + + if (path[i]->loader) { + path[i]->loader(path[i]->data); + ngx_time_update(); + } + } + + exit(0); +} diff -Nru nginx-0.5.33/src/os/unix/ngx_process_cycle.h nginx-0.8.53/src/os/unix/ngx_process_cycle.h --- nginx-0.5.33/src/os/unix/ngx_process_cycle.h 2006-08-30 10:39:17.000000000 +0000 +++ nginx-0.8.53/src/os/unix/ngx_process_cycle.h 2009-11-23 15:46:21.000000000 +0000 @@ -19,9 +19,18 @@ #define NGX_CMD_REOPEN 5 -#define NGX_PROCESS_SINGLE 0 -#define NGX_PROCESS_MASTER 1 -#define NGX_PROCESS_WORKER 2 +#define NGX_PROCESS_SINGLE 0 +#define NGX_PROCESS_MASTER 1 +#define NGX_PROCESS_SIGNALLER 2 +#define NGX_PROCESS_WORKER 3 +#define NGX_PROCESS_HELPER 4 + + +typedef struct { + ngx_event_handler_pt handler; + char *name; + ngx_msec_t delay; +} ngx_cache_manager_ctx_t; void ngx_master_process_cycle(ngx_cycle_t *cycle); @@ -38,6 +47,7 @@ extern sig_atomic_t ngx_reap; extern sig_atomic_t ngx_sigio; +extern sig_atomic_t ngx_sigalrm; extern sig_atomic_t ngx_quit; extern sig_atomic_t ngx_debug_quit; extern sig_atomic_t ngx_terminate; diff -Nru nginx-0.5.33/src/os/unix/ngx_process.h nginx-0.8.53/src/os/unix/ngx_process.h --- nginx-0.5.33/src/os/unix/ngx_process.h 2006-08-30 10:39:17.000000000 +0000 +++ nginx-0.8.53/src/os/unix/ngx_process.h 2009-08-10 13:07:15.000000000 +0000 @@ -27,7 +27,7 @@ char *name; unsigned respawn:1; - unsigned just_respawn:1; + unsigned just_spawn:1; unsigned detached:1; unsigned exiting:1; unsigned exited:1; @@ -45,9 +45,10 @@ #define NGX_MAX_PROCESSES 1024 #define NGX_PROCESS_NORESPAWN -1 -#define NGX_PROCESS_RESPAWN -2 -#define NGX_PROCESS_JUST_RESPAWN -3 -#define NGX_PROCESS_DETACHED -4 +#define NGX_PROCESS_JUST_SPAWN -2 +#define NGX_PROCESS_RESPAWN -3 +#define NGX_PROCESS_JUST_RESPAWN -4 +#define NGX_PROCESS_DETACHED -5 #define ngx_getpid getpid diff -Nru nginx-0.5.33/src/os/unix/ngx_readv_chain.c nginx-0.8.53/src/os/unix/ngx_readv_chain.c --- nginx-0.5.33/src/os/unix/ngx_readv_chain.c 2006-08-30 10:39:17.000000000 +0000 +++ nginx-0.8.53/src/os/unix/ngx_readv_chain.c 2010-06-23 16:34:54.000000000 +0000 @@ -158,7 +158,7 @@ rev->ready = 0; - if (n == NGX_ERROR){ + if (n == NGX_ERROR) { c->read->error = 1; } @@ -247,7 +247,7 @@ rev->ready = 0; - if (n == NGX_ERROR){ + if (n == NGX_ERROR) { c->read->error = 1; } diff -Nru nginx-0.5.33/src/os/unix/ngx_recv.c nginx-0.8.53/src/os/unix/ngx_recv.c --- nginx-0.5.33/src/os/unix/ngx_recv.c 2006-08-30 10:39:17.000000000 +0000 +++ nginx-0.8.53/src/os/unix/ngx_recv.c 2010-06-23 16:34:54.000000000 +0000 @@ -11,7 +11,8 @@ #if (NGX_HAVE_KQUEUE) -ssize_t ngx_unix_recv(ngx_connection_t *c, u_char *buf, size_t size) +ssize_t +ngx_unix_recv(ngx_connection_t *c, u_char *buf, size_t size) { ssize_t n; ngx_err_t err; @@ -40,6 +41,7 @@ return 0; } else { + rev->ready = 0; return NGX_AGAIN; } } @@ -77,12 +79,6 @@ * even if kqueue reported about available data */ -#if 0 - ngx_log_error(NGX_LOG_ALERT, c->log, 0, - "recv() returned 0 while kevent() reported " - "%d available bytes", rev->available); -#endif - rev->eof = 1; rev->available = 0; } @@ -117,7 +113,7 @@ rev->ready = 0; - if (n == NGX_ERROR){ + if (n == NGX_ERROR) { rev->error = 1; } @@ -126,7 +122,8 @@ #else /* ! NGX_HAVE_KQUEUE */ -ssize_t ngx_unix_recv(ngx_connection_t *c, u_char *buf, size_t size) +ssize_t +ngx_unix_recv(ngx_connection_t *c, u_char *buf, size_t size) { ssize_t n; ngx_err_t err; @@ -172,7 +169,7 @@ rev->ready = 0; - if (n == NGX_ERROR){ + if (n == NGX_ERROR) { rev->error = 1; } diff -Nru nginx-0.5.33/src/os/unix/ngx_send.c nginx-0.8.53/src/os/unix/ngx_send.c --- nginx-0.5.33/src/os/unix/ngx_send.c 2006-08-30 10:39:17.000000000 +0000 +++ nginx-0.8.53/src/os/unix/ngx_send.c 2009-04-18 19:13:53.000000000 +0000 @@ -9,7 +9,8 @@ #include -ssize_t ngx_unix_send(ngx_connection_t *c, u_char *buf, size_t size) +ssize_t +ngx_unix_send(ngx_connection_t *c, u_char *buf, size_t size) { ssize_t n; ngx_err_t err; @@ -39,6 +40,8 @@ wev->ready = 0; } + c->sent += n; + return n; } diff -Nru nginx-0.5.33/src/os/unix/ngx_setproctitle.h nginx-0.8.53/src/os/unix/ngx_setproctitle.h --- nginx-0.5.33/src/os/unix/ngx_setproctitle.h 2006-08-30 10:39:17.000000000 +0000 +++ nginx-0.8.53/src/os/unix/ngx_setproctitle.h 2009-11-03 16:28:21.000000000 +0000 @@ -13,7 +13,7 @@ /* FreeBSD, NetBSD, OpenBSD */ #define ngx_init_setproctitle(log) -#define ngx_setproctitle setproctitle +#define ngx_setproctitle(title) setproctitle("%s", title) #else /* !NGX_HAVE_SETPROCTITLE */ diff -Nru nginx-0.5.33/src/os/unix/ngx_shmem.h nginx-0.8.53/src/os/unix/ngx_shmem.h --- nginx-0.5.33/src/os/unix/ngx_shmem.h 2006-09-15 10:25:32.000000000 +0000 +++ nginx-0.8.53/src/os/unix/ngx_shmem.h 2009-04-18 19:27:28.000000000 +0000 @@ -15,7 +15,9 @@ typedef struct { u_char *addr; size_t size; + ngx_str_t name; ngx_log_t *log; + ngx_uint_t exists; /* unsigned exists:1; */ } ngx_shm_t; diff -Nru nginx-0.5.33/src/os/unix/ngx_solaris_config.h nginx-0.8.53/src/os/unix/ngx_solaris_config.h --- nginx-0.5.33/src/os/unix/ngx_solaris_config.h 2007-09-22 18:59:05.000000000 +0000 +++ nginx-0.8.53/src/os/unix/ngx_solaris_config.h 2009-08-28 08:12:35.000000000 +0000 @@ -28,6 +28,7 @@ #include #include #include +#include /* statvfs() */ #include /* FIONBIO */ #include @@ -61,16 +62,6 @@ #endif -#if (NGX_HAVE_SENDFILE) -#include -#endif - - -#if (NGX_HAVE_AIO) -#include -#endif - - #if (NGX_HAVE_DEVPOLL) #include #include @@ -82,6 +73,11 @@ #endif +#if (NGX_HAVE_SENDFILE) +#include +#endif + + #define NGX_LISTEN_BACKLOG 511 diff -Nru nginx-0.5.33/src/os/unix/ngx_solaris_init.c nginx-0.8.53/src/os/unix/ngx_solaris_init.c --- nginx-0.5.33/src/os/unix/ngx_solaris_init.c 2006-08-30 10:39:17.000000000 +0000 +++ nginx-0.8.53/src/os/unix/ngx_solaris_init.c 2007-12-03 16:46:46.000000000 +0000 @@ -16,6 +16,7 @@ static ngx_os_io_t ngx_solaris_io = { ngx_unix_recv, ngx_readv_chain, + ngx_udp_unix_recv, ngx_unix_send, #if (NGX_HAVE_SENDFILE) ngx_solaris_sendfilev_chain, @@ -57,7 +58,7 @@ ngx_os_io = ngx_solaris_io; - return NGX_OK;; + return NGX_OK; } diff -Nru nginx-0.5.33/src/os/unix/ngx_solaris_sendfilev_chain.c nginx-0.8.53/src/os/unix/ngx_solaris_sendfilev_chain.c --- nginx-0.5.33/src/os/unix/ngx_solaris_sendfilev_chain.c 2007-11-07 13:54:40.000000000 +0000 +++ nginx-0.8.53/src/os/unix/ngx_solaris_sendfilev_chain.c 2009-08-30 09:42:29.000000000 +0000 @@ -168,19 +168,22 @@ if (n == -1) { err = ngx_errno; - if (err == NGX_EAGAIN || err == NGX_EINTR) { - if (err == NGX_EINTR) { - eintr = 1; - } + switch (err) { + case NGX_EAGAIN: + break; - ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, err, - "sendfilev() sent only %uz bytes", sent); + case NGX_EINTR: + eintr = 1; + break; - } else { + default: wev->error = 1; ngx_connection_error(c, err, "sendfilev() failed"); return NGX_CHAIN_ERROR; } + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, err, + "sendfilev() sent only %uz bytes", sent); } ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, diff -Nru nginx-0.5.33/src/os/unix/ngx_sunpro_amd64.il nginx-0.8.53/src/os/unix/ngx_sunpro_amd64.il --- nginx-0.5.33/src/os/unix/ngx_sunpro_amd64.il 2007-07-22 08:47:45.000000000 +0000 +++ nginx-0.8.53/src/os/unix/ngx_sunpro_amd64.il 2010-03-26 13:38:41.000000000 +0000 @@ -31,7 +31,12 @@ / ngx_cpu_pause() +/ +/ the "rep; nop" is used instead of "pause" to avoid the "[ PAUSE ]" hardware +/ capability added by linker because Solaris/amd64 does not know about it: +/ +/ ld.so.1: nginx: fatal: hardware capability unsupported: 0x2000 [ PAUSE ] .inline ngx_cpu_pause,0 - pause + rep; nop .end diff -Nru nginx-0.5.33/src/os/unix/ngx_sunpro_x86.il nginx-0.8.53/src/os/unix/ngx_sunpro_x86.il --- nginx-0.5.33/src/os/unix/ngx_sunpro_x86.il 2007-07-22 08:47:45.000000000 +0000 +++ nginx-0.8.53/src/os/unix/ngx_sunpro_x86.il 2009-05-08 09:41:43.000000000 +0000 @@ -5,7 +5,7 @@ / ngx_atomic_uint_t ngx_atomic_cmp_set(ngx_atomic_t *lock, / ngx_atomic_uint_t old, ngx_atomic_uint_t set); / -/ the arguments are passed on the stack (%esp), 4(%esp), 8(%esp) +/ the arguments are passed on stack (%esp), 4(%esp), 8(%esp) .inline ngx_atomic_cmp_set,0 movl (%esp), %ecx @@ -21,7 +21,7 @@ / ngx_atomic_int_t ngx_atomic_fetch_add(ngx_atomic_t *value, / ngx_atomic_int_t add); / -/ the arguments are passed on the stack (%esp), 4(%esp) +/ the arguments are passed on stack (%esp), 4(%esp) .inline ngx_atomic_fetch_add,0 movl (%esp), %ecx @@ -32,7 +32,12 @@ / ngx_cpu_pause() +/ +/ the "rep; nop" is used instead of "pause" to avoid the "[ PAUSE ]" hardware +/ capability added by linker because Solaris/i386 does not know about it: +/ +/ ld.so.1: nginx: fatal: hardware capability unsupported: 0x2000 [ PAUSE ] .inline ngx_cpu_pause,0 - pause + rep; nop .end diff -Nru nginx-0.5.33/src/os/unix/ngx_sunpro_x86.map nginx-0.8.53/src/os/unix/ngx_sunpro_x86.map --- nginx-0.5.33/src/os/unix/ngx_sunpro_x86.map 2006-10-09 15:38:59.000000000 +0000 +++ nginx-0.8.53/src/os/unix/ngx_sunpro_x86.map 1970-01-01 00:00:00.000000000 +0000 @@ -1,2 +0,0 @@ -# disable { PAUSE ] hwcap for Sun Studio 11 -hwcap_1 = OVERRIDE; diff -Nru nginx-0.5.33/src/os/unix/ngx_thread.h nginx-0.8.53/src/os/unix/ngx_thread.h --- nginx-0.5.33/src/os/unix/ngx_thread.h 2006-08-30 10:39:17.000000000 +0000 +++ nginx-0.8.53/src/os/unix/ngx_thread.h 2009-11-02 12:41:56.000000000 +0000 @@ -124,5 +124,4 @@ #endif - #endif /* _NGX_THREAD_H_INCLUDED_ */ diff -Nru nginx-0.5.33/src/os/unix/ngx_time.c nginx-0.8.53/src/os/unix/ngx_time.c --- nginx-0.5.33/src/os/unix/ngx_time.c 2006-08-30 10:39:17.000000000 +0000 +++ nginx-0.8.53/src/os/unix/ngx_time.c 2008-08-30 19:52:07.000000000 +0000 @@ -8,6 +8,50 @@ #include +/* + * FreeBSD does not test /etc/localtime change, however, we can workaround it + * by calling tzset() with TZ and then without TZ to update timezone. + * The trick should work since FreeBSD 2.1.0. + * + * Linux does not test /etc/localtime change in localtime(), + * but may stat("/etc/localtime") several times in every strftime(), + * therefore we use it to update timezone. + * + * Solaris does not test /etc/TIMEZONE change too and no workaround available. + */ + +void +ngx_timezone_update(void) +{ +#if (NGX_FREEBSD) + + if (getenv("TZ")) { + return; + } + + putenv("TZ=UTC"); + + tzset(); + + unsetenv("TZ"); + + tzset(); + +#elif (NGX_LINUX) + time_t s; + struct tm *t; + char buf[4]; + + s = time(0); + + t = localtime(&s); + + strftime(buf, 4, "%H", t); + +#endif +} + + void ngx_localtime(time_t s, ngx_tm_t *tm) { diff -Nru nginx-0.5.33/src/os/unix/ngx_time.h nginx-0.8.53/src/os/unix/ngx_time.h --- nginx-0.5.33/src/os/unix/ngx_time.h 2006-08-30 10:39:17.000000000 +0000 +++ nginx-0.8.53/src/os/unix/ngx_time.h 2008-08-30 19:52:07.000000000 +0000 @@ -52,6 +52,7 @@ #endif +void ngx_timezone_update(void); void ngx_localtime(time_t s, ngx_tm_t *tm); void ngx_libc_localtime(time_t s, struct tm *tm); void ngx_libc_gmtime(time_t s, struct tm *tm); diff -Nru nginx-0.5.33/src/os/unix/ngx_types.h nginx-0.8.53/src/os/unix/ngx_types.h --- nginx-0.5.33/src/os/unix/ngx_types.h 2006-08-30 10:39:17.000000000 +0000 +++ nginx-0.8.53/src/os/unix/ngx_types.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,27 +0,0 @@ - -/* - * Copyright (C) Igor Sysoev - */ - - -#ifndef _NGX_TYPES_H_INCLUDED_ -#define _NGX_TYPES_H_INCLUDED_ - - -#include - - -typedef int ngx_fd_t; -typedef struct stat ngx_file_info_t; -typedef ino_t ngx_file_uniq_t; - -typedef struct { - DIR *dir; - struct dirent *de; - struct stat info; - - ngx_uint_t valid_info:1; /* unsigned valid_info:1; */ -} ngx_dir_t; - - -#endif /* _NGX_TYPES_H_INCLUDED_ */ diff -Nru nginx-0.5.33/src/os/unix/ngx_udp_recv.c nginx-0.8.53/src/os/unix/ngx_udp_recv.c --- nginx-0.5.33/src/os/unix/ngx_udp_recv.c 1970-01-01 00:00:00.000000000 +0000 +++ nginx-0.8.53/src/os/unix/ngx_udp_recv.c 2010-06-23 16:34:54.000000000 +0000 @@ -0,0 +1,114 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include + + +#if (NGX_HAVE_KQUEUE) + +ssize_t +ngx_udp_unix_recv(ngx_connection_t *c, u_char *buf, size_t size) +{ + ssize_t n; + ngx_err_t err; + ngx_event_t *rev; + + rev = c->read; + + do { + n = recv(c->fd, buf, size, 0); + + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, + "recv: fd:%d %d of %d", c->fd, n, size); + + if (n >= 0) { + if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) { + rev->available -= n; + + /* + * rev->available may be negative here because some additional + * bytes may be received between kevent() and recv() + */ + + if (rev->available <= 0) { + rev->ready = 0; + rev->available = 0; + } + } + + return n; + } + + err = ngx_socket_errno; + + if (err == NGX_EAGAIN || err == NGX_EINTR) { + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err, + "recv() not ready"); + n = NGX_AGAIN; + + } else { + n = ngx_connection_error(c, err, "recv() failed"); + break; + } + + } while (err == NGX_EINTR); + + rev->ready = 0; + + if (n == NGX_ERROR) { + rev->error = 1; + } + + return n; +} + +#else /* ! NGX_HAVE_KQUEUE */ + +ssize_t +ngx_udp_unix_recv(ngx_connection_t *c, u_char *buf, size_t size) +{ + ssize_t n; + ngx_err_t err; + ngx_event_t *rev; + + rev = c->read; + + do { + n = recv(c->fd, buf, size, 0); + + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, + "recv: fd:%d %d of %d", c->fd, n, size); + + if (n >= 0) { + return n; + } + + err = ngx_socket_errno; + + if (err == NGX_EAGAIN || err == NGX_EINTR) { + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err, + "recv() not ready"); + n = NGX_AGAIN; + + } else { + n = ngx_connection_error(c, err, "recv() failed"); + break; + } + + } while (err == NGX_EINTR); + + rev->ready = 0; + + if (n == NGX_ERROR) { + rev->error = 1; + } + + return n; +} + +#endif /* NGX_HAVE_KQUEUE */ diff -Nru nginx-0.5.33/src/os/unix/ngx_user.c nginx-0.8.53/src/os/unix/ngx_user.c --- nginx-0.5.33/src/os/unix/ngx_user.c 2006-08-30 10:39:17.000000000 +0000 +++ nginx-0.8.53/src/os/unix/ngx_user.c 2008-06-17 15:00:30.000000000 +0000 @@ -43,7 +43,7 @@ if (err == 0) { len = ngx_strlen(value); - *encrypted = ngx_palloc(pool, len); + *encrypted = ngx_pnalloc(pool, len); if (*encrypted) { ngx_memcpy(*encrypted, value, len + 1); return NGX_OK; @@ -81,7 +81,7 @@ if (value) { len = ngx_strlen(value); - *encrypted = ngx_palloc(pool, len); + *encrypted = ngx_pnalloc(pool, len); if (*encrypted) { ngx_memcpy(*encrypted, value, len + 1); } diff -Nru nginx-0.5.33/src/os/unix/ngx_user.h nginx-0.8.53/src/os/unix/ngx_user.h --- nginx-0.5.33/src/os/unix/ngx_user.h 2006-08-30 10:39:17.000000000 +0000 +++ nginx-0.8.53/src/os/unix/ngx_user.h 2009-11-02 12:41:56.000000000 +0000 @@ -20,5 +20,4 @@ u_char **encrypted); - #endif /* _NGX_USER_H_INCLUDED_ */ diff -Nru nginx-0.5.33/src/os/unix/ngx_writev_chain.c nginx-0.8.53/src/os/unix/ngx_writev_chain.c --- nginx-0.5.33/src/os/unix/ngx_writev_chain.c 2007-11-07 13:54:40.000000000 +0000 +++ nginx-0.8.53/src/os/unix/ngx_writev_chain.c 2009-08-30 09:42:29.000000000 +0000 @@ -110,19 +110,22 @@ if (n == -1) { err = ngx_errno; - if (err == NGX_EAGAIN || err == NGX_EINTR) { - if (err == NGX_EINTR) { - eintr = 1; - } + switch (err) { + case NGX_EAGAIN: + break; - ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err, - "writev() not ready"); + case NGX_EINTR: + eintr = 1; + break; - } else { + default: wev->error = 1; (void) ngx_connection_error(c, err, "writev() failed"); return NGX_CHAIN_ERROR; } + + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err, + "writev() not ready"); } sent = n > 0 ? n : 0;