# HG changeset patch # User Igor Sysoev # Date 1096833600 -14400 # Node ID f0b350454894f8568a4227e3ac754a191594f702 nginx 0.1.0 *) The first public version. diff --git a/LICENSE b/LICENSE new file mode 100644 --- /dev/null +++ b/LICENSE @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2002-2004 Igor Sysoev + * + * 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. + * + */ diff --git a/README b/README new file mode 100644 --- /dev/null +++ b/README @@ -0,0 +1,3 @@ + +Documentation is available at http://sysoev.ru/nginx/ only. + diff --git a/auto/cc b/auto/cc new file mode 100644 --- /dev/null +++ b/auto/cc @@ -0,0 +1,376 @@ + +# Copyright (C) Igor Sysoev + + +CFLAGS="$CFLAGS $CC_OPT" + +case $CC in + + *gcc*) + # gcc 2.7.2.3, 2.8.1, 2.95.4, + # 3.0.4, 3.1.1, 3.2.3, 3.3.2, 3.3.3, 3.3.4, 3.4 + + # optimizations + #CFLAGS="$CFLAGS -O2 -fomit-frame-pointer" + + case $CPU in + pentium) + # optimize for Pentium and Athlon + CPU_OPT="-march=pentium" + ;; + + pentiumpro) + # optimize for Pentium Pro, Pentium II and Pentium III + CPU_OPT="-march=pentiumpro" + ;; + + pentium4) + # optimize for Pentium 4, gcc 3.x + CPU_OPT="-march=pentium4" + ;; + esac + + # STUB for batch builds + if [ $CC = gcc27 ]; then CPU_OPT=; fi + + CFLAGS="$CFLAGS $PIPE $CPU_OPT" + + if [ ".$PCRE_OPT" = "." ]; then + PCRE_OPT="-O2 -fomit-frame-pointer $PIPE $CPU_OPT" + else + PCRE_OPT="$PCRE_OPT $PIPE" + fi + + if [ ".$MD5_OPT" = "." ]; then + MD5_OPT="-O2 -fomit-frame-pointer $PIPE $CPU_OPT" + else + MD5_OPT="$MD5_OPT $PIPE" + fi + + if [ ".$ZLIB_OPT" = "." ]; then + ZLIB_OPT="-O2 -fomit-frame-pointer $PIPE $CPU_OPT" + else + ZLIB_OPT="$ZLIB_OPT $PIPE" + fi + + # warnings + CFLAGS="$CFLAGS -O -W" + CFLAGS="$CFLAGS -Wall -Wpointer-arith" + #CFLAGS="$CFLAGS -Wconversion" + #CFLAGS="$CFLAGS -Winline" + + # we have a lot of the unused function arguments + CFLAGS="$CFLAGS -Wno-unused" + + # stop on warning + CFLAGS="$CFLAGS -Werror" + + # ANSI C warnings + #CFLAGS="$CFLAGS -pedantic" + + # debug + CFLAGS="$CFLAGS -g" + + # DragonFly's gcc3 generates DWARF + #CFLAGS="$CFLAGS -g -gstabs" + + have=HAVE_GCC_VARIADIC_MACROS . auto/have + + if [ ".$CPP" = "." ]; then + CPP="$CC -E" + fi + + LINK="\$(CC)" + + INCOPT="-I " + COMPOPT="-c" + OBJOUT="-o " + BINOUT="-o " + OBJEXT="o" + + CC_STRONG="$CC -Wall -Werror" + ;; + + + *icc) + # Intel C++ compiler 7.1, 8.0 + + # optimizations + CFLAGS="$CFLAGS -O" + # inline functions declared with __inline + #CFLAGS="$CFLAGS -Ob1" + # inline any function, at the compiler's discretion + CFLAGS="$CFLAGS -Ob2" + + # single-file IP optimizations + #IPO="-ip" + # multi-file IP optimizations + IPO="-ipo -ipo_obj" + CFLAGS="$CFLAGS $IPO" + CORE_LINK="$CORE_LINK $IPO" + CORE_LINK="$CORE_LINK -opt_report_file=$OBJS/opt_report_file" + + case $CPU in + pentium) + # optimize for Pentium and Athlon + CPU_OPT="-march=pentium" + ;; + + pentiumpro) + # optimize for Pentium Pro, Pentium II and Pentium III + CPU_OPT="-mcpu=pentiumpro -march=pentiumpro" + ;; + + pentium4) + # optimize for Pentium 4, default + CPU_OPT="-march=pentium4" + ;; + esac + + CFLAGS="$CFLAGS $CPU_OPT" + + if [ ".$PCRE_OPT" = "." ]; then + PCRE_OPT="-O $IPO $CPU_OPT" + fi + + if [ ".$MD5_OPT" = "." ]; then + MD5_OPT="-O $IPO $CPU_OPT" + fi + + if [ ".$ZLIB_OPT" = "." ]; then + ZLIB_OPT="-O $IPO $CPU_OPT" + fi + + # warnings + CFLAGS="$CFLAGS -w1" + #CFLAGS="$CFLAGS -w2" + + # stop on warning + CFLAGS="$CFLAGS -Werror" + + # debug + CFLAGS="$CFLAGS -g" + + have=HAVE_C99_VARIADIC_MACROS . auto/have + + LINK="\$(CC)" + + INCOPT="-I " + COMPOPT="-c" + OBJOUT="-o " + BINOUT="-o " + OBJEXT="o" + + CC_STRONG="$CC -w1 -Werror" + ;; + + + cl) + # MSVC 6.0 SP2 + + # optimizations + + # maximize speed + CFLAGS="$CFLAGS -O2" + # enable global optimization + CFLAGS="$CFLAGS -Og" + # enable intrinsic functions + CFLAGS="$CFLAGS -Oi" + # inline expansion + CFLAGS="$CFLAGS -Ob1" + # enable frame pointer omission + CFLAGS="$CFLAGS -Oy" + # disable stack checking calls + CFLAGS="$CFLAGS -Gs" + + case $CPU in + pentium) + # optimize for Pentium and Athlon + CPU_OPT="-G5" + ;; + + pentiumpro) + # optimize for Pentium Pro, Pentium II and Pentium III + CPU_OPT="-G6" + ;; + + pentium4) + # optimize for Pentium 4 + #CPU_OPT="-G7" + ;; + esac + + CFLAGS="$CFLAGS $CPU_OPT" + + # warnings + #CFLAGS="$CFLAGS -W3" + CFLAGS="$CFLAGS -W4" + + # stop on warning + CFLAGS="$CFLAGS -WX" + + # link with libcmt.lib, multithreaded + #LIBC="-MT" + # link with msvcrt.dll + LIBC="-MD" + + CFLAGS="$CFLAGS $LIBC" + + # disable logo + CFLAGS="$CFLAGS -nologo" + + LINK="\$(CC)" + + # link flags + CORE_LINK="$CORE_LINK -link -verbose:lib" + + # debug + CFLAGS="$CFLAGS -Yd" + CORE_LINK="$CORE_LINK -debug -debugtype:coff" + + # precompiled headers + CORE_DEPS="$CORE_DEPS ngx_config.pch" + PCH="ngx_config.pch" + BUILDPCH="-Ycngx_config.h" + USEPCH="-Yungx_config.h" + + INCOPT="-I " + COMPOPT="-c" + OBJOUT="-Fo" + BINOUT="-Fe" + OBJEXT="obj" + BINEXT=".exe" + #DIRSEP='\\' + ;; + + + wcl386) + # Open Watcom C 1.0, 1.2 + + # optimizations + + # maximize speed + CFLAGS="$CFLAGS -ot" + # reorder instructions for best pipeline usage + CFLAGS="$CFLAGS -op" + # inline intrinsic functions + CFLAGS="$CFLAGS -oi" + # inline expansion + CFLAGS="$CFLAGS -oe" + # disable stack checking calls + CFLAGS="$CFLAGS -s" + + case $CPU in + pentium) + # optimize for Pentium and Athlon + # register-based arguments passing conventions + CPU_OPT="-5r" + # stack-based arguments passing conventions + #CPU_OPT="-5s" + ;; + + pentiumpro) + # optimize for Pentium Pro, Pentium II and Pentium III + # register-based arguments passing conventions + CPU_OPT="-6r" + # stack-based arguments passing conventions + #CPU_OPT="-6s" + ;; + esac + + CFLAGS="$CFLAGS $CPU_OPT" + + # warnings + #CFLAGS="$CFLAGS -w3" + CFLAGS="$CFLAGS -wx" + + # stop on warning + CFLAGS="$CFLAGS -we" + + # built target is NT + CFLAGS="$CFLAGS -bt=nt" + + # multithreaded + CFLAGS="$CFLAGS -bm" + + # debug + CFLAGS="$CFLAGS -d2" + + # quiet + CFLAGS="$CFLAGS -zq" + + # Open Watcom C 1.2 + #have=HAVE_C99_VARIADIC_MACROS . auto/have + + # precompiled headers + CORE_DEPS="$CORE_DEPS $OBJS\\ngx_config.pch" + PCH="$OBJS\\ngx_config.pch" + BUILDPCH="-fhq=$OBJS\\ngx_config.pch" + USEPCH="-fh=$OBJS\\ngx_config.pch" + + LINK="\$(CC)" + + # link flags + CORE_LINK="$CORE_LINK -l=nt" + + INCOPT="-i=" + COMPOPT="-c" + OBJOUT="-fo" + BINOUT="-fe=" + OBJEXT="obj" + BINEXT=".exe" + DIRSEP='\\' + + MAKE_SL=YES + ;; + + + bcc32) + # Borland C++ 5.5 + + # optimizations + + # maximize speed + CFLAGS="$CFLAGS -O2" + + case $CPU in + pentium) + # optimize for Pentium and Athlon + CPU_OPT="-5" + ;; + + pentiumpro) + # optimize for Pentium Pro, Pentium II and Pentium III + CPU_OPT="-6" + ;; + esac + + CFLAGS="$CFLAGS $CPU_OPT" + + # multithreaded + CFLAGS="$CFLAGS -tWM" + + # stop on warning + CFLAGS="$CFLAGS -w!" + + # disable logo + CFLAGS="$CFLAGS -q" + + # precompiled headers + CORE_DEPS="$CORE_DEPS $OBJS\\ngx_config.csm" + PCH="$OBJS\\ngx_config.csm" + BUILDPCH="-H=$OBJS\\ngx_config.csm" + USEPCH="-Hu -H=$OBJS\\ngx_config.csm" + + LINK="\$(CC)" + + INCOPT="-I" + COMPOPT="-c" + OBJOUT="-o" + BINOUT="-e" + OBJEXT="obj" + BINEXT=".exe" + DIRSEP='\\' + ;; + +esac diff --git a/auto/define b/auto/define new file mode 100644 --- /dev/null +++ b/auto/define @@ -0,0 +1,11 @@ + +# Copyright (C) Igor Sysoev + + +cat << END >> $NGX_AUTO_CONFIG_H + +#ifndef $have +#define $have $value +#endif + +END diff --git a/auto/endianess b/auto/endianess new file mode 100644 --- /dev/null +++ b/auto/endianess @@ -0,0 +1,41 @@ + +# Copyright (C) Igor Sysoev + + +echo $ngx_n "checking for system endianess ..." $ngx_c +echo >> $NGX_ERR +echo "checking for system endianess" >> $NGX_ERR + + +cat << END > $NGX_AUTOTEST.c + +int main() { + int i = 0x11223344; + char *p; + + p = (char *) &i; + if (*p == 0x44) return 0; + return 1; +} + +END + +eval "${CC} -o $NGX_AUTOTEST $NGX_AUTOTEST.c >> $NGX_ERR 2>&1" + +if [ -x $NGX_AUTOTEST ]; then + if $NGX_AUTOTEST 2>&1 > /dev/null; then + echo " little endianess" + have=HAVE_LITTLE_ENDIAN . auto/have + else + echo " big endianess" + fi + + rm $NGX_AUTOTEST* + +else + rm $NGX_AUTOTEST* + + echo + echo "$0: error: can not detect system endianess" + exit 1 +fi diff --git a/auto/feature b/auto/feature new file mode 100644 --- /dev/null +++ b/auto/feature @@ -0,0 +1,56 @@ + +# Copyright (C) Igor Sysoev + + +echo $ngx_n "checking for $ngx_feature ..." $ngx_c +echo >> $NGX_ERR +echo "checking for $ngx_feature" >> $NGX_ERR + +ngx_found=no + +feature=`echo $ngx_feature_name | tr '[a-z]' '[A-Z]'` + +cat << END > $NGX_AUTOTEST.c + +#include +$NGX_UNISTD_H +$ngx_feature_inc + +int main() { + $ngx_feature_test; + return 0; +} + +END + +test="$CC_WARN $CC_TEST_FLAGS -o $NGX_AUTOTEST $NGX_AUTOTEST.c \ + $ngx_feature_libs" +eval "$test >> $NGX_ERR 2>&1" + +if [ -x $NGX_AUTOTEST ]; then + + if [ $ngx_feature_run = yes ]; then + if $NGX_AUTOTEST 2>&1 > /dev/null; then + echo " found" + have=HAVE_$feature . auto/have + ngx_found=yes + else + echo " found but is not working" + fi + + else + echo " found" + have=HAVE_$feature . auto/have + ngx_found=yes + fi + +else + echo " not found" + echo "---------" >> $NGX_ERR + cat $NGX_AUTOTEST.c >> $NGX_ERR + echo "---------" >> $NGX_ERR + echo $test >> $NGX_ERR + echo "---------" >> $NGX_ERR +fi + +rm $NGX_AUTOTEST* diff --git a/auto/fmt/fmt b/auto/fmt/fmt new file mode 100644 --- /dev/null +++ b/auto/fmt/fmt @@ -0,0 +1,80 @@ + +# Copyright (C) Igor Sysoev + + +echo $ngx_n "checking for $ngx_type printf() format ..." $ngx_c +echo >> $NGX_ERR +echo "checking for $ngx_type printf() format" >> $NGX_ERR + +ngx_fmt=no +comma= + +for fmt in $ngx_formats +do + + cat << END > $NGX_AUTOTEST.c + +#include +#include +#include +#include +$NGX_INTTYPES_H +$NGX_AUTO_CONFIG + +int main() { + printf("$fmt", ($ngx_type) $ngx_max_value); + return 0; +} + +END + + eval "$CC_WARN $CC_TEST_FLAGS -o $NGX_AUTOTEST $NGX_AUTOTEST.c \ + >> $NGX_ERR 2>&1" + + max_value=`echo $ngx_max_value | sed -e "s/L*\$//"` + + if [ -x $NGX_AUTOTEST ]; then + if [ "`$NGX_AUTOTEST`" = $max_value ]; then + if [ $ngx_fmt_collect = yes ]; then + echo $ngx_n "$comma \"${fmt}\" is appropriate" $ngx_c + else + echo $ngx_n "$comma \"${fmt}\" used" $ngx_c + fi + ngx_fmt=$fmt + fi + fi + + rm $NGX_AUTOTEST* + + if [ $ngx_fmt != no ]; then + if [ $ngx_fmt_collect = yes ]; then + eval "ngx_${ngx_size}_fmt=\"\${ngx_${ngx_size}_fmt} \$ngx_fmt\"" + comma="," + continue + else + break + fi + fi + + echo $ngx_n "$comma \"${fmt}\" is not appropriate" $ngx_c + comma="," +done + +echo + +if [ $ngx_fmt = no ]; then + echo "$0: error: printf() $ngx_type format not found" + exit 1 +fi + + +if [ $ngx_fmt_collect = no ]; then + cat << END >> $NGX_AUTO_CONFIG_H + +#ifndef $ngx_fmt_name +#define $ngx_fmt_name "$ngx_fmt" +#endif + +END + +fi diff --git a/auto/fmt/ptrfmt b/auto/fmt/ptrfmt new file mode 100644 --- /dev/null +++ b/auto/fmt/ptrfmt @@ -0,0 +1,72 @@ + +# Copyright (C) Igor Sysoev + + +echo $ngx_n "checking for $ngx_type printf() format ..." $ngx_c +echo >> $NGX_ERR +echo "checking for $ngx_type printf() format" >> $NGX_ERR + +ngx_fmt=no +comma= +fmtX= + +for fmt in $ngx_formats +do + + cat << END > $NGX_AUTOTEST.c + +int main() { + printf("$fmt", ($ngx_type) $ngx_max_value); + return 0; +} + +END + + eval "$CC_WARN $CC_TEST_FLAGS -o $NGX_AUTOTEST $NGX_AUTOTEST.c \ + >> $NGX_ERR 2>&1" + + max_value=`echo $ngx_max_value | sed -e "s/L*\$//"` + + if [ -x $NGX_AUTOTEST ]; then + if [ "`$NGX_AUTOTEST`" = $max_value ]; then + ngx_fmt=$fmt + fi + fi + + rm $NGX_AUTOTEST* + + if [ $ngx_fmt != no ]; then + break + fi + + fmtX=`echo $fmt | sed -e "s/d/X/"` + + echo $ngx_n "$comma \"${fmtX}\" is not appropriate" $ngx_c + comma="," +done + + +if [ $ngx_fmt = no ]; then + echo "$0: error: printf() $ngx_type format not found" + exit 1 +fi + + +if [ $ngx_ptr_size = 4 ]; then + fmtX="%0`expr 2 \* $ngx_ptr_size`" +else + fmtX="%" +fi + +ngx_fmt=`echo $ngx_fmt | sed -e "s/d/X/" -e "s/^%/$fmtX/"` + +echo "$comma \"${ngx_fmt}\" used" + + +cat << END >> $NGX_AUTO_CONFIG_H + +#ifndef $ngx_fmt_name +#define $ngx_fmt_name "$ngx_fmt" +#endif + +END diff --git a/auto/fmt/xfmt b/auto/fmt/xfmt new file mode 100644 --- /dev/null +++ b/auto/fmt/xfmt @@ -0,0 +1,11 @@ + +# Copyright (C) Igor Sysoev + + +cat << END | sed -e 's/d"$/x"/' >> $NGX_AUTO_CONFIG_H + +#ifndef $ngx_fmt_name +#define $ngx_fmt_name "$ngx_fmt" +#endif + +END diff --git a/auto/func b/auto/func new file mode 100644 --- /dev/null +++ b/auto/func @@ -0,0 +1,43 @@ + +# Copyright (C) Igor Sysoev + + +echo $ngx_n "checking for $ngx_func ..." $ngx_c +echo >> $NGX_ERR +echo "checking for $ngx_func" >> $NGX_ERR + +ngx_found=no + +func=`echo $ngx_func | sed -e 's/()$//' | tr '[a-z]' '[A-Z]'` + +cat << END > $NGX_AUTOTEST.c + +#include +$NGX_UNISTD_H +$ngx_func_inc + +int main() { + $ngx_func_test; + return 0; +} + +END + +test="$CC_WARN $CC_TEST_FLAGS -o $NGX_AUTOTEST $NGX_AUTOTEST.c $ngx_func_libs" +eval "$test >> $NGX_ERR 2>&1" + +if [ -x $NGX_AUTOTEST ]; then + echo " found" + have=HAVE_$func . auto/have + ngx_found=yes + +else + echo " not found" + echo "---------" >> $NGX_ERR + cat $NGX_AUTOTEST.c >> $NGX_ERR + echo "---------" >> $NGX_ERR + echo $test >> $NGX_ERR + echo "---------" >> $NGX_ERR +fi + +rm $NGX_AUTOTEST* diff --git a/auto/have b/auto/have new file mode 100644 --- /dev/null +++ b/auto/have @@ -0,0 +1,11 @@ + +# Copyright (C) Igor Sysoev + + +cat << END >> $NGX_AUTO_CONFIG_H + +#ifndef $have +#define $have 1 +#endif + +END diff --git a/auto/headers b/auto/headers new file mode 100644 --- /dev/null +++ b/auto/headers @@ -0,0 +1,6 @@ + +# Copyright (C) Igor Sysoev + + +ngx_inc="unistd.h"; . auto/inc +ngx_inc="inttypes.h"; . auto/inc diff --git a/auto/inc b/auto/inc new file mode 100644 --- /dev/null +++ b/auto/inc @@ -0,0 +1,35 @@ + +# Copyright (C) Igor Sysoev + + +echo $ngx_n "checking for $ngx_inc ..." $ngx_c +echo >> $NGX_ERR +echo "checking for $ngx_inc" >> $NGX_ERR + +ngx_found=no + +inc=`echo $ngx_inc | sed -e 's/\./_/' | sed -e 's/\//_/' | tr '[a-z]' '[A-Z]'` + +cat << END > $NGX_AUTOTEST.c + +#include <$ngx_inc> + +int main() { + return 0; +} + +END + +eval "${CC} -o $NGX_AUTOTEST $NGX_AUTOTEST.c >> $NGX_ERR 2>&1" + +if [ -x $NGX_AUTOTEST ]; then + echo " found" + have=HAVE_$inc . auto/have + eval "NGX_$inc='#include <$ngx_inc>'" + ngx_found=yes + +else + echo " not found" +fi + +rm $NGX_AUTOTEST* diff --git a/auto/init b/auto/init new file mode 100644 --- /dev/null +++ b/auto/init @@ -0,0 +1,38 @@ + +# Copyright (C) Igor Sysoev + + +MAKEFILE=$OBJS/Makefile + +NGX_AUTO_CONFIG_H=$OBJS/ngx_auto_config.h +NGX_MODULES_C=$OBJS/ngx_modules.c + +NGX_AUTOTEST=$OBJS/autotest +NGX_ERR=$OBJS/autoconf.err + +CC_WARN=$CC + +PCH=NO +USEPCH= + +OBJEXT= +BINEXT= +DIRSEP='\/' +MAKE_SL=NO + + +# checking echo's "-n" option and "\c" capabilties + +if echo "test\c" | grep c >/dev/null; then + if echo -n test | grep n >/dev/null; then + ngx_n= + ngx_c= + else + ngx_n=-n + ngx_c= + fi + +else + ngx_n= + ngx_c='\c' +fi diff --git a/auto/install b/auto/install new file mode 100644 --- /dev/null +++ b/auto/install @@ -0,0 +1,65 @@ + +# Copyright (C) Igor Sysoev + + +cat << END >> $MAKEFILE +install: + test -d $PREFIX || mkdir -p $PREFIX + + test -d `dirname $SBIN_PATH` || mkdir -p `dirname $SBIN_PATH` + test ! -f $SBIN_PATH || mv $SBIN_PATH $SBIN_PATH.old + cp nginx $SBIN_PATH + + test -d `dirname $CONF_PATH` || mkdir -p `dirname $CONF_PATH` + + cp conf/koi-win `dirname $CONF_PATH` + + test -f `dirname $CONF_PATH`/mime.types || \ + cp conf/mime.types `dirname $CONF_PATH`/mime.types + cp conf/mime.types `dirname $CONF_PATH`/mime.types.default + + test -f $CONF_PATH || cp conf/nginx.conf $CONF_PATH + cp conf/nginx.conf `dirname $CONF_PATH`/nginx.conf.default + + test -d `dirname $PID_PATH` || mkdir -p `dirname $PID_PATH` + + test -d `dirname $HTTP_LOG_PATH` || mkdir -p `dirname $HTTP_LOG_PATH` + + test -d $PREFIX/html || cp -r html $PREFIX + + #test -d $PREFIX/temp || mkdir -p $PREFIX/temp +END + + +if [ ".$ERROR_LOG_PATH" != "." ]; then + cat << END >> $MAKEFILE + + test -d `dirname $ERROR_LOG_PATH` || mkdir -p `dirname $ERROR_LOG_PATH` +END + +fi + + +if test ! -f Makefile; then + + cat << END > Makefile + +build: + \$(MAKE) -f $OBJS/Makefile + +install: + \$(MAKE) -f $OBJS/Makefile install + +clean: + rm -rf Makefile $OBJS + +upgrade: + $SBIN_PATH -t + kill -USR2 \`cat $PID_PATH\` + sleep 1 + test -f $PID_PATH.newbin + kill -WINCH \`cat $PID_PATH\` + +END + +fi diff --git a/auto/lib/conf b/auto/lib/conf new file mode 100644 --- /dev/null +++ b/auto/lib/conf @@ -0,0 +1,19 @@ + +# Copyright (C) Igor Sysoev + + +if [ $USE_PCRE = YES ]; then + . auto/lib/pcre/conf +fi + +if [ $USE_MD5 = YES ]; then + . auto/lib/md5/conf +fi + +if [ $USE_OPENSSL = YES ]; then + . auto/lib/openssl/conf +fi + +if [ $USE_ZLIB = YES ]; then + . auto/lib/zlib/conf +fi diff --git a/auto/lib/make b/auto/lib/make new file mode 100644 --- /dev/null +++ b/auto/lib/make @@ -0,0 +1,19 @@ + +# Copyright (C) Igor Sysoev + + +if [ $PCRE != NONE -a $PCRE != NO -a $PCRE != YES ]; then + . auto/lib/pcre/make +fi + +if [ $MD5 != NONE -a $MD5 != NO -a $MD5 != YES ]; then + . auto/lib/md5/make +fi + +if [ $OPENSSL != NONE -a $OPENSSL != NO -a $OPENSSL != YES ]; then + . auto/lib/openssl/make +fi + +if [ $ZLIB != NONE -a $ZLIB != NO -a $ZLIB != YES ]; then + . auto/lib/zlib/make +fi diff --git a/auto/lib/md5/conf b/auto/lib/md5/conf new file mode 100644 --- /dev/null +++ b/auto/lib/md5/conf @@ -0,0 +1,105 @@ + +# Copyright (C) Igor Sysoev + + +if [ $MD5 != NONE ]; then + + if grep MD5_Init $MD5/md5.h >/dev/null; then + # OpenSSL md5 + OPENSSL_MD5=YES + have=HAVE_OPENSSL_MD5 . auto/have + else + # rsaref md5 + OPENSSL_MD5=NO + fi + + CORE_INCS="$CORE_INCS $MD5" + + case "$CC" in + + cl | wcl386 | bcc32) + LINK_DEPS="$LINK_DEPS $MD5/md5.lib" + CORE_LIBS="$CORE_LIBS $MD5/md5.lib" + ;; + + *icc) + LINK_DEPS="$LINK_DEPS $MD5/libmd5.a" + + # to allow -ipo optimization we link with the *.o but not library + CORE_LIBS="$CORE_LIBS $MD5/md5_dgst.o" + + if [ $MD5_ASM = YES ]; then + CORE_LIBS="$CORE_LIBS $MD5/asm/mx86-elf.o" + fi + ;; + + *) + LINK_DEPS="$LINK_DEPS $MD5/libmd5.a" + CORE_LIBS="$CORE_LIBS $MD5/libmd5.a" + #CORE_LIBS="$CORE_LIBS -L $MD5 -lmd5" + ;; + + esac + +else + + if [ $PLATFORM != win32 ]; then + MD5=NO + ngx_lib_cflags= + + # Solaris 8/9 + + ngx_lib_inc="#include +#include " + ngx_lib="rsaref md5 library" + ngx_lib_test="MD5_CTX md5; MD5Init(&md5)" + ngx_libs=-lmd5 + . auto/lib/test + + + if [ $ngx_found = yes ]; then + CORE_LIBS="$CORE_LIBS $ngx_libs" + MD5=YES + MD5_LIB=md5 + ngx_found=no + + else + # FreeBSD + + ngx_lib="rsaref md library" + ngx_lib_test="MD5_CTX md5; MD5Init(&md5)" + ngx_libs=-lmd + . auto/lib/test + fi + + + if [ $ngx_found = yes ]; then + CORE_LIBS="$CORE_LIBS $ngx_libs" + MD5=YES + MD5_LIB=md + ngx_found=no + + else + if [ $MD5 = NO ]; then + + # OpenSSL crypto library + + ngx_lib_inc="#include " + ngx_lib="OpenSSL md5 crypto library" + ngx_lib_test="MD5_CTX md5; MD5_Init(&md5)" + ngx_libs=-lcrypto + . auto/lib/test + fi + fi + + + if [ $ngx_found = yes ]; then + have=HAVE_OPENSSL_MD5 . auto/have + have=HAVE_OPENSSL_MD5_H . auto/have + CORE_LIBS="$CORE_LIBS $ngx_libs" + MD5=YES + MD5_LIB=crypto + fi + fi + +fi diff --git a/auto/lib/md5/make b/auto/lib/md5/make new file mode 100644 --- /dev/null +++ b/auto/lib/md5/make @@ -0,0 +1,98 @@ + +# Copyright (C) Igor Sysoev + + +case "$CC" in + + cl) + makefile=makefile.msvc + opt="CPU_OPT=$CPU_OPT LIBC=$LIBC MD5_ASM=$MD5_ASM" + ;; + + wcl386) + makefile=makefile.owc + opt="CPU_OPT=$CPU_OPT" + ;; + + bcc32) + makefile=makefile.bcc + opt="-DCPU_OPT=$CPU_OPT -DMD5_ASM=$MD5_ASM" + ;; + +esac + + +case $PLATFORM in + + win32) + line=`echo $MD5/md5.lib: | sed -e "s/\//$DIRSEP/g"` + echo "$line" >> $MAKEFILE + ;; + + *) + echo "$MD5/libmd5.a:" >> $MAKEFILE + ;; + +esac + + +done=NO + + +case $PLATFORM in + + win32) + md5=`echo $MD5 | sed -e "s/\//$DIRSEP/g"` + + cp auto/lib/md5/$makefile $MD5 + echo " cd $md5" >> $MAKEFILE + echo " \$(MAKE) -f $makefile $opt" >> $MAKEFILE + echo " cd ..\\..\\.." >> $MAKEFILE + + done=YES + ;; + + SunOS:*:i86pc) + if [ $MD5_ASM = YES ]; then + + MD5_OPT="CFLAGS=\"$MD5_OPT -DSOL -DMD5_ASM -DL_ENDIAN\"" + + echo " cd $MD5 \\" >> $MAKEFILE + echo " && \$(MAKE) $MD5_OPT \\" >> $MAKEFILE + echo " MD5_ASM_OBJ=asm/mx86-sol.o \\" >> $MAKEFILE + echo " CC=\"\$(CC)\" CPP=\"\$(CPP)\" \\" >> $MAKEFILE + echo " libmd5.a" >> $MAKEFILE + + done=YES + fi + ;; + + # FreeBSD: i386 + # Linux: i686 + + *:i386 | *:i686) + if [ $MD5_ASM = YES ]; then + + MD5_OPT="CFLAGS=\"$MD5_OPT -DELF -DMD5_ASM -DL_ENDIAN\"" + + echo " cd $MD5 \\" >> $MAKEFILE + echo " && \$(MAKE) $MD5_OPT \\" >> $MAKEFILE + echo " MD5_ASM_OBJ=asm/mx86-elf.o \\" >> $MAKEFILE + echo " CC=\"\$(CC)\" CPP=\"\$(CPP)\" \\" >> $MAKEFILE + echo " libmd5.a" >> $MAKEFILE + + done=YES + fi + ;; + +esac + + +if [ $done = NO ]; then + echo " cd $MD5 \\" >> $MAKEFILE + echo " && \$(MAKE) CFLAGS=\"$MD5_OPT\" \\" >> $MAKEFILE + echo " MD5_ASM_OBJ= CC=\"\$(CC)\" libmd5.a" >> $MAKEFILE +fi + + +echo >> $MAKEFILE diff --git a/auto/lib/md5/makefile.bcc b/auto/lib/md5/makefile.bcc new file mode 100644 --- /dev/null +++ b/auto/lib/md5/makefile.bcc @@ -0,0 +1,19 @@ + +# Copyright (C) Igor Sysoev + + +CFLAGS = -q -O2 -tWM $(CPU_OPT) -DL_ENDIAN + +!if "$(MD5_ASM)" == "YES" + +md5.lib: + bcc32 -c $(CFLAGS) -DMD5_ASM md5_dgst.c + tlib md5.lib +md5_dgst.obj +asm/m-win32.obj + +!else + +md5.lib: + bcc32 -c $(CFLAGS) md5_dgst.c + tlib md5.lib +md5_dgst.obj + +!endif diff --git a/auto/lib/md5/makefile.msvc b/auto/lib/md5/makefile.msvc new file mode 100644 --- /dev/null +++ b/auto/lib/md5/makefile.msvc @@ -0,0 +1,19 @@ + +# Copyright (C) Igor Sysoev + + +CFLAGS = -nologo -MT -O2 -Ob1 -Oi -Gs $(LIBC) $(CPU_OPT) -D L_ENDIAN + +!if "$(MD5_ASM)" == "YES" + +md5.lib: + cl -c $(CFLAGS) -D MD5_ASM md5_dgst.c + link -lib -out:md5.lib md5_dgst.obj asm/m-win32.obj + +!else + +md5.lib: + cl -c $(CFLAGS) md5_dgst.c + link -lib -out:md5.lib md5_dgst.obj + +!endif diff --git a/auto/lib/md5/makefile.owc b/auto/lib/md5/makefile.owc new file mode 100644 --- /dev/null +++ b/auto/lib/md5/makefile.owc @@ -0,0 +1,9 @@ + +# Copyright (C) Igor Sysoev + + +CFLAGS = -zq -bt=nt -bm -ot -op -oi -oe -s $(CPU_OPT) + +md5.lib: + wcl386 -c $(CFLAGS) -dL_ENDIAN md5_dgst.c + wlib -n md5.lib md5_dgst.obj diff --git a/auto/lib/openssl/conf b/auto/lib/openssl/conf new file mode 100644 --- /dev/null +++ b/auto/lib/openssl/conf @@ -0,0 +1,43 @@ + +# Copyright (C) Igor Sysoev + + +if [ $OPENSSL != NONE ]; then + CORE_INCS="$CORE_INCS $OPENSSL/include" + CORE_DEPS="$CORE_DEPS $OPENSSL_DEPS" + CORE_SRCS="$CORE_SRCS $OPENSSL_SRCS" + + case "$CC" in + *) + have=NGX_OPENSSL . auto/have + LINK_DEPS="$LINK_DEPS $OPENSSL/libssl.a $OPENSSL/libcrypto.a" + CORE_LIBS="$CORE_LIBS $OPENSSL/libssl.a $OPENSSL/libcrypto.a" + ;; + + esac + +else + + if [ $PLATFORM != win32 ]; then + OPENSSL=NO + ngx_lib_cflags= + + ngx_lib_inc="#include " + ngx_lib="OpenSSL library" + ngx_lib_test="SSL_library_init()" + ngx_libs="-lssl -lcrypto" + . auto/lib/test + + + if [ $ngx_found = yes ]; then + have=NGX_OPENSSL . auto/have + CORE_DEPS="$CORE_DEPS $OPENSSL_DEPS" + CORE_SRCS="$CORE_SRCS $OPENSSL_SRCS" + CORE_LIBS="$CORE_LIBS $ngx_libs" + OPENSSL=YES + ngx_found=no + fi + + fi + +fi diff --git a/auto/lib/openssl/make b/auto/lib/openssl/make new file mode 100644 --- /dev/null +++ b/auto/lib/openssl/make @@ -0,0 +1,16 @@ + +# Copyright (C) Igor Sysoev + + +case $PLATFORM in + *) + echo "$OPENSSL/libssl.a:" >> $MAKEFILE + echo " cd $OPENSSL \\" >> $MAKEFILE + echo " && CC=\"\$(CC)\" \\" >> $MAKEFILE + echo " ./config threads no-shared \\" >> $MAKEFILE + echo " && \$(MAKE)" >> $MAKEFILE + ;; + +esac + +echo >> $MAKEFILE diff --git a/auto/lib/pcre/conf b/auto/lib/pcre/conf new file mode 100644 --- /dev/null +++ b/auto/lib/pcre/conf @@ -0,0 +1,86 @@ + +# Copyright (C) Igor Sysoev + + +if [ $PCRE != NONE ]; then + CORE_INCS="$CORE_INCS $PCRE" + CORE_DEPS="$CORE_DEPS $REGEX_DEPS" + CORE_SRCS="$CORE_SRCS $REGEX_SRCS" + + case "$CC" in + + cl | wcl386 | bcc32) + have=HAVE_PCRE . auto/have + have=PCRE_STATIC . auto/have + CORE_DEPS="$CORE_DEPS $PCRE/pcre.h" + LINK_DEPS="$LINK_DEPS $PCRE/pcre.lib" + CORE_LIBS="$CORE_LIBS $PCRE/pcre.lib" + ;; + + *icc) + have=HAVE_PCRE . auto/have + CORE_DEPS="$CORE_DEPS $PCRE/pcre.h" + + LINK_DEPS="$LINK_DEPS $PCRE/.libs/libpcre.a" + + # to allow -ipo optimization we link with the *.o but not library + CORE_LIBS="$CORE_LIBS $PCRE/maketables.o" + CORE_LIBS="$CORE_LIBS $PCRE/get.o" + CORE_LIBS="$CORE_LIBS $PCRE/study.o" + CORE_LIBS="$CORE_LIBS $PCRE/pcre.o" + ;; + + *) + have=HAVE_PCRE . auto/have + CORE_DEPS="$CORE_DEPS $PCRE/pcre.h" + LINK_DEPS="$LINK_DEPS $PCRE/.libs/libpcre.a" + CORE_LIBS="$CORE_LIBS $PCRE/.libs/libpcre.a" + #CORE_LIBS="$CORE_LIBS -L $PCRE/.libs -lpcre" + ;; + + esac + +else + + if [ $PLATFORM != win32 ]; then + PCRE=NO + ngx_lib_cflags= + + # Linux + + ngx_lib_inc="#include " + ngx_lib="PCRE library" + ngx_lib_test="pcre *re; re = pcre_compile(NULL, 0, NULL, 0, NULL)" + ngx_libs="-lpcre" + . auto/lib/test + + + if [ $ngx_found = yes ]; then + have=HAVE_PCRE . auto/have + CORE_DEPS="$CORE_DEPS $REGEX_DEPS" + CORE_SRCS="$CORE_SRCS $REGEX_SRCS" + CORE_LIBS="$CORE_LIBS $ngx_libs" + PCRE=YES + ngx_found=no + + else + # FreeBSD PCRE port. + + ngx_lib="PCRE library in /usr/local/" + ngx_lib_cflags="-I /usr/local/include" + ngx_libs="-L /usr/local/lib -lpcre" + . auto/lib/test + fi + + + if [ $ngx_found = yes ]; then + have=HAVE_PCRE . auto/have + CORE_DEPS="$CORE_DEPS $REGEX_DEPS" + CORE_INCS="$CORE_INCS /usr/local/include" + CORE_SRCS="$CORE_SRCS $REGEX_SRCS" + CORE_LIBS="$CORE_LIBS $ngx_libs" + PCRE=YES + fi + fi + +fi diff --git a/auto/lib/pcre/make b/auto/lib/pcre/make new file mode 100644 --- /dev/null +++ b/auto/lib/pcre/make @@ -0,0 +1,65 @@ + +# Copyright (C) Igor Sysoev + + +case "$CC" in + + cl) + makefile=makefile.msvc + opt="CPU_OPT=$CPU_OPT LIBC=$LIBC" + ;; + + wcl386) + makefile=makefile.owc + opt="CPU_OPT=$CPU_OPT" + ;; + + bcc32) + makefile=makefile.bcc + opt="-DCPU_OPT=$CPU_OPT" + ;; + +esac + + +case $PLATFORM in + + win32) + cp auto/lib/pcre/patch.pcre.in $PCRE + cp auto/lib/pcre/patch.config.in $PCRE + cp auto/lib/pcre/$makefile $PCRE + + pcre=`echo $PCRE | sed -e "s/\//$DIRSEP/g"` + line=`echo $PCRE/pcre.h: | sed -e "s/\//$DIRSEP/g"` + + echo "$line" >> $MAKEFILE + echo " cd $pcre" >> $MAKEFILE + echo " \$(MAKE) -f $makefile pcre.h" >> $MAKEFILE + echo " cd ..\\..\\.." >> $MAKEFILE + echo >> $MAKEFILE + + line="$PCRE/pcre.lib: $PCRE/pcre.h" + line=`echo $line | sed -e "s/\//$DIRSEP/g"` + + echo "$line" >> $MAKEFILE + echo " cd $pcre" >> $MAKEFILE + echo " \$(MAKE) -f $makefile $opt" >> $MAKEFILE + echo " cd ..\\..\\.." >> $MAKEFILE + ;; + + *) + PCRE_OPT="CFLAGS=\"$PCRE_OPT\"" + + echo "$PCRE/pcre.h:" >> $MAKEFILE + echo " cd $PCRE \\" >> $MAKEFILE + echo " && CC=\"\$(CC)\" $PCRE_OPT \\" >> $MAKEFILE + echo " ./configure --disable-shared" >> $MAKEFILE + echo >> $MAKEFILE + echo "$PCRE/.libs/libpcre.a: $PCRE/pcre.h" >> $MAKEFILE + echo " cd $PCRE \\" >> $MAKEFILE + echo " && \$(MAKE) libpcre.la" >> $MAKEFILE + ;; + +esac + +echo >> $MAKEFILE diff --git a/auto/lib/pcre/makefile.bcc b/auto/lib/pcre/makefile.bcc new file mode 100644 --- /dev/null +++ b/auto/lib/pcre/makefile.bcc @@ -0,0 +1,20 @@ + +# Copyright (C) Igor Sysoev + + +CFLAGS = -q -O2 -tWM $(CPU_OPT) +PCREFLAGS = -DPCRE_STATIC -DPOSIX_MALLOC_THRESHOLD=10 + + +pcre.lib: pcre.h + bcc32 -q -edftables dftables.c + + dftables > chartables.c + + bcc32 -c $(CFLAGS) $(PCREFLAGS) maketables.c get.c study.c pcre.c + + tlib pcre.lib +maketables.obj +get.obj +study.obj +pcre.obj + +pcre.h: + patch -o pcre.h pcre.in patch.pcre.in + patch -o config.h config.in patch.config.in diff --git a/auto/lib/pcre/makefile.msvc b/auto/lib/pcre/makefile.msvc new file mode 100644 --- /dev/null +++ b/auto/lib/pcre/makefile.msvc @@ -0,0 +1,22 @@ + +# Copyright (C) Igor Sysoev + + +CFLAGS = -O2 -Ob1 -Oi -Gs $(LIBC) $(CPU_OPT) +PCREFLAGS = -DPCRE_STATIC -DPOSIX_MALLOC_THRESHOLD=10 + + +pcre.lib: pcre.h + cl -Fedftables dftables.c + + dftables > chartables.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 + +pcre.h: + patch -o pcre.h pcre.in patch.pcre.in + patch -o config.h config.in patch.config.in diff --git a/auto/lib/pcre/makefile.owc b/auto/lib/pcre/makefile.owc new file mode 100644 --- /dev/null +++ b/auto/lib/pcre/makefile.owc @@ -0,0 +1,19 @@ + +# Copyright (C) Igor Sysoev + + +CFLAGS = -c -zq -bt=nt -ot -op -oi -oe -s -bm $(CPU_OPT) +PCREFLAGS = -DPCRE_STATIC -DPOSIX_MALLOC_THRESHOLD=10 + + +pcre.lib: pcre.h + wcl386 -zq -bt=nt -l=nt -fe=dftables dftables.c + dftables > chartables.c + + wcl386 $(CFLAGS) $(PCREFLAGS) maketables.c get.c study.c pcre.c + wlib -n pcre.lib maketables.obj get.obj study.obj pcre.obj + + +pcre.h: + patch -o pcre.h pcre.in patch.pcre.in + patch -o config.h config.in patch.config.in diff --git a/auto/lib/pcre/patch.config.in b/auto/lib/pcre/patch.config.in new file mode 100644 --- /dev/null +++ b/auto/lib/pcre/patch.config.in @@ -0,0 +1,11 @@ +--- 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 --git a/auto/lib/pcre/patch.pcre.in b/auto/lib/pcre/patch.pcre.in new file mode 100644 --- /dev/null +++ b/auto/lib/pcre/patch.pcre.in @@ -0,0 +1,15 @@ +--- pcre.in Thu Aug 21 14:43:07 2003 ++++ pcre.in Sun Mar 7 02:10:11 2004 +@@ -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 --git a/auto/lib/test b/auto/lib/test new file mode 100644 --- /dev/null +++ b/auto/lib/test @@ -0,0 +1,27 @@ + +# Copyright (C) Igor Sysoev + + +echo $ngx_n "checking for $ngx_lib ..." $ngx_c +echo >> $NGX_ERR +echo "checking for $ngx_lib library" >> $NGX_ERR + +ngx_found=no + +echo "$ngx_lib_inc" > $NGX_AUTOTEST.c +echo "int main() { $ngx_lib_test; return 0; }" >> $NGX_AUTOTEST.c + +eval "$CC $cc_test_flags $ngx_lib_cflags \ + -o $NGX_AUTOTEST $NGX_AUTOTEST.c $ngx_libs \ + >> $NGX_ERR 2>&1" + +if [ -x $NGX_AUTOTEST ]; then + echo " found" + + ngx_found=yes + +else + echo " not found" +fi + +rm $NGX_AUTOTEST* diff --git a/auto/lib/zlib/conf b/auto/lib/zlib/conf new file mode 100644 --- /dev/null +++ b/auto/lib/zlib/conf @@ -0,0 +1,60 @@ + +# Copyright (C) Igor Sysoev + + +if [ $ZLIB != NONE ]; then + CORE_INCS="$CORE_INCS $ZLIB" + + case "$CC" in + + cl | wcl386 | bcc32) + LINK_DEPS="$LINK_DEPS $ZLIB/zlib.lib" + CORE_LIBS="$CORE_LIBS $ZLIB/zlib.lib" + ;; + + *icc) + LINK_DEPS="$LINK_DEPS $ZLIB/libz.a" + + # to allow -ipo optimization we link with the *.o but not library + CORE_LIBS="$CORE_LIBS $ZLIB/adler32.o" + CORE_LIBS="$CORE_LIBS $ZLIB/crc32.o" + CORE_LIBS="$CORE_LIBS $ZLIB/deflate.o" + CORE_LIBS="$CORE_LIBS $ZLIB/trees.o" + CORE_LIBS="$CORE_LIBS $ZLIB/zutil.o" + + if [ $ZLIB_ASM != NO ]; then + CORE_LIBS="$CORE_LIBS $ZLIB/match.o" + fi + ;; + + *) + LINK_DEPS="$LINK_DEPS $ZLIB/libz.a" + CORE_LIBS="$CORE_LIBS $ZLIB/libz.a" + #CORE_LIBS="$CORE_LIBS -L $ZLIB -lz" + ;; + + esac + +else + + if [ $PLATFORM != win32 ]; then + + # FreeBSD, Solaris, Linux + + ngx_lib_cflags= + ngx_lib_inc="#include " + ngx_lib="zlib library" + ngx_lib_test="z_stream z; deflate(&z, Z_NO_FLUSH)" + ngx_libs=-lz + . auto/lib/test + + + if [ $ngx_found = yes ]; then + CORE_LIBS="$CORE_LIBS $ngx_libs" + ZLIB=YES + else + ZLIB=NO + fi + fi + +fi diff --git a/auto/lib/zlib/make b/auto/lib/zlib/make new file mode 100644 --- /dev/null +++ b/auto/lib/zlib/make @@ -0,0 +1,107 @@ + +# Copyright (C) Igor Sysoev + + +case "$CC" in + + cl) + makefile=makefile.msvc + opt="CPU_OPT=$CPU_OPT LIBC=$LIBC" + + ;; + + wcl386) + makefile=makefile.owc + opt="CPU_OPT=$CPU_OPT" + ;; + + bcc32) + makefile=makefile.bcc + opt="-DCPU_OPT=$CPU_OPT" + ;; + +esac + + +case $PLATFORM in + + win32) + line=`echo $ZLIB/zlib.lib: | sed -e "s/\//$DIRSEP/g"` + echo "$line" >> $MAKEFILE + ;; + + *) + echo "$ZLIB/libz.a:" >> $MAKEFILE + ;; + +esac + + +done=NO + + +case $PLATFORM in + + win32) + zlib=`echo $ZLIB | sed -e "s/\//$DIRSEP/g"` + + cp auto/lib/zlib/$makefile $ZLIB + echo " cd $zlib" >> $MAKEFILE + echo " \$(MAKE) -f $makefile $opt" >> $MAKEFILE + echo " cd ..\\..\\.." >> $MAKEFILE + + done=YES + ;; + + # FreeBSD: i386 + # Linux: i686 + + *:i386 | *:i686) + case $ZLIB_ASM in + pentium) + echo " cd $ZLIB \\" >> $MAKEFILE + echo " && cp contrib/asm586/match.S . \\" >> $MAKEFILE + echo " && CFLAGS=\"$ZLIB_OPT -DASMV\" \\" >> $MAKEFILE + echo " CC=\"\$(CC)\" \\" >> $MAKEFILE + echo " ./configure \\" >> $MAKEFILE + echo " && \$(MAKE) OBJA=match.o libz.a" >> $MAKEFILE + + done=YES + ;; + + pentiumpro) + echo " cd $ZLIB \\" >> $MAKEFILE + echo " && cp contrib/asm686/match.S . \\" >> $MAKEFILE + echo " && CFLAGS=\"$ZLIB_OPT -DASMV\" \\" >> $MAKEFILE + echo " CC=\"\$(CC)\" \\" >> $MAKEFILE + echo " ./configure \\" >> $MAKEFILE + echo " && \$(MAKE) OBJA=match.o libz.a" >> $MAKEFILE + + done=YES + ;; + + NO) + ;; + + *) + echo "$0: error: invalid --with-zlib-asm=$ZLIB_ASM option." + echo "The valid values are \"pentium\" and \"pentiumpro\" only". + echo + + exit 1; + ;; + esac + ;; + +esac + + +if [ $done = NO ]; then + echo " cd $ZLIB \\" >> $MAKEFILE + echo " && CFLAGS=\"$ZLIB_OPT\" CC=\"\$(CC)\" \\" >> $MAKEFILE + echo " ./configure \\" >> $MAKEFILE + echo " && \$(MAKE) libz.a" >> $MAKEFILE +fi + + +echo >> $MAKEFILE diff --git a/auto/lib/zlib/makefile.bcc b/auto/lib/zlib/makefile.bcc new file mode 100644 --- /dev/null +++ b/auto/lib/zlib/makefile.bcc @@ -0,0 +1,11 @@ + +# Copyright (C) Igor Sysoev + + +CFLAGS = -q -O2 -tWM $(CPU_OPT) + +zlib.lib: + bcc32 -c $(CFLAGS) adler32.c crc32.c deflate.c trees.c zutil.c + + tlib zlib.lib +adler32.obj +crc32.obj +deflate.obj \ + +trees.obj +zutil.obj diff --git a/auto/lib/zlib/makefile.msvc b/auto/lib/zlib/makefile.msvc new file mode 100644 --- /dev/null +++ b/auto/lib/zlib/makefile.msvc @@ -0,0 +1,11 @@ + +# Copyright (C) Igor Sysoev + + +CFLAGS = -nologo -O2 -Ob1 -Oi -Gs $(LIBC) $(CPU_OPT) + +zlib.lib: + cl -c $(CFLAGS) adler32.c crc32.c deflate.c trees.c zutil.c + + link -lib -out:zlib.lib adler32.obj crc32.obj deflate.obj \ + trees.obj zutil.obj diff --git a/auto/lib/zlib/makefile.owc b/auto/lib/zlib/makefile.owc new file mode 100644 --- /dev/null +++ b/auto/lib/zlib/makefile.owc @@ -0,0 +1,9 @@ + +# Copyright (C) Igor Sysoev + + +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 + wlib -n zlib.lib adler32.obj crc32.obj deflate.obj trees.obj zutil.obj diff --git a/auto/make b/auto/make new file mode 100644 --- /dev/null +++ b/auto/make @@ -0,0 +1,360 @@ + +# Copyright (C) Igor Sysoev + + +mkdir -p $OBJS/src/core $OBJS/src/event $OBJS/src/event/modules \ + $OBJS/src/os/unix $OBJS/src/os/win32 \ + $OBJS/src/http $OBJS/src/http/modules $OBJS/src/http/modules/proxy \ + $OBJS/src/imap + + +echo "CC = $CC" > $MAKEFILE +echo "CPP = $CPP" >> $MAKEFILE +echo "LINK = $LINK" >> $MAKEFILE +if [ "$CC" = wcl386 ]; then + echo MAKE = wmake >> $MAKEFILE +fi +echo "CFLAGS = $CFLAGS" >> $MAKEFILE +echo >> $MAKEFILE + + +if [ $MAKE_SL = YES ]; then + echo >> $MAKEFILE +fi + +all_inc="$CORE_INCS $OBJS $HTTP_INCS $IMAP_INCS" +all_inc=`echo " $all_inc" | sed -e "s/ \([^ ]\)/ $INCOPT\1/g"` +all_inc=`echo $all_inc | sed -e "s/\//$DIRSEP/g"` + +echo "ALL_INCS = $all_inc" >> $MAKEFILE +echo >> $MAKEFILE + +all_srcs="$CORE_SRCS" + + +# CORE_DEPS + +if [ $MAKE_SL = YES ]; then + echo $ngx_n "CORE_DEPS =" $ngx_c >> $MAKEFILE +else + echo "CORE_DEPS = \\" >> $MAKEFILE +fi + +for dep in $CORE_DEPS +do + dep=`echo $dep | sed -e "s/\//$DIRSEP/g"` + + if [ $MAKE_SL = YES ]; then + echo $ngx_n " $dep" $ngx_c >> $MAKEFILE + else + echo " $dep \\" >> $MAKEFILE + fi +done +echo >> $MAKEFILE + + +# CORE_INCS + +if [ $MAKE_SL = YES ]; then + echo >> $MAKEFILE +fi + +inc="$CORE_INCS $OBJS" +inc=`echo " $inc" | sed -e "s/ \([^ ]\)/ $INCOPT\1/g" -e "s/\//$DIRSEP/g"` + +echo "CORE_INCS = $inc" >> $MAKEFILE +echo >> $MAKEFILE + + +if [ $HTTP = YES ]; then + + all_srcs="$all_srcs $HTTP_SRCS" + + # HTTP_DEPS + + if [ $MAKE_SL = YES ]; then + echo $ngx_n "HTTP_DEPS =" $ngx_c >> $MAKEFILE + else + echo "HTTP_DEPS = \\" >> $MAKEFILE + fi + + for dep in $HTTP_DEPS + do + dep=`echo $dep | sed -e "s/\//$DIRSEP/g"` + + if [ $MAKE_SL = YES ]; then + echo $ngx_n " $dep" $ngx_c >> $MAKEFILE + else + echo " $dep \\" >> $MAKEFILE + fi + done + echo >> $MAKEFILE + + + # HTTP_INCS + + if [ $MAKE_SL = YES ]; then + echo >> $MAKEFILE + fi + + inc="$HTTP_INCS $OBJS" + inc=`echo " $inc" | sed -e "s/ \([^ ]\)/ $INCOPT\1/g" -e "s/\//$DIRSEP/g"` + + echo "HTTP_INCS = $inc" >> $MAKEFILE + echo >> $MAKEFILE + +fi + + +if [ $IMAP = YES ]; then + + all_srcs="$all_srcs $IMAP_SRCS" + + # IMAP_DEPS + + if [ $MAKE_SL = YES ]; then + echo $ngx_n "IMAP_DEPS =" $ngx_c >> $MAKEFILE + else + echo "IMAP_DEPS = \\" >> $MAKEFILE + fi + + for dep in $IMAP_DEPS + do + dep=`echo $dep | sed -e "s/\//$DIRSEP/g"` + + if [ $MAKE_SL = YES ]; then + echo $ngx_n " $dep" $ngx_c >> $MAKEFILE + else + echo " $dep \\" >> $MAKEFILE + fi + done + echo >> $MAKEFILE + + + # IMAP_INCS + + if [ $MAKE_SL = YES ]; then + echo >> $MAKEFILE + fi + + inc="$IMAP_INCS $OBJS" + inc=`echo " $inc" | sed -e "s/ \([^ ]\)/ $INCOPT\1/g" -e "s/\//$DIRSEP/g"` + + echo "IMAP_INCS = $inc" >> $MAKEFILE + echo >> $MAKEFILE + +fi + + +# nginx + +if [ $MAKE_SL = YES ]; then + echo $ngx_n "nginx$BINEXT: " $ngx_c >> $MAKEFILE +else + echo "nginx$BINEXT: \\" >> $MAKEFILE +fi + + +# nginx deps + +for src in $all_srcs +do + obj=`echo $src | sed -e "s/\.c\$/.$OBJEXT/" -e "s/\.S\$/.$OBJEXT/"` + obj=`echo $OBJS/$obj | sed -e "s/\//$DIRSEP/g"` + + if [ $MAKE_SL = YES ]; then + echo $ngx_n " $obj" $ngx_c >> $MAKEFILE + else + echo " $obj \\" >> $MAKEFILE + fi +done + +for src in $NGX_MODULES_C $LINK_DEPS +do + obj=`echo $src | sed -e "s/\.c\$/.$OBJEXT/"` + obj=`echo $obj | sed -e "s/\//$DIRSEP/g"` + + if [ $MAKE_SL = YES ]; then + echo $ngx_n " $obj" $ngx_c >> $MAKEFILE + else + echo " $obj \\" >> $MAKEFILE + fi +done +echo >> $MAKEFILE + + +# nginx build + +if [ $MAKE_SL = YES ]; then + echo $ngx_n " \$(LINK) ${BINOUT}nginx" $ngx_c >> $MAKEFILE +else + echo " \$(LINK) ${BINOUT}nginx \\" >> $MAKEFILE +fi + + +# nginx build sources + +for src in $all_srcs +do + obj=`echo $src | sed -e "s/\.c\$/.$OBJEXT/" -e "s/\.S\$/.$OBJEXT/"` + obj=`echo $OBJS/$obj | sed -e "s/\//$DIRSEP/g"` + + if [ $MAKE_SL = YES ]; then + echo $ngx_n " $obj" $ngx_c >> $MAKEFILE + else + echo " $obj \\" >> $MAKEFILE + fi +done + + +# nginx build ngx_modules.c and libs + +obj=`echo $NGX_MODULES_C | sed -e "s/\.c\$/.$OBJEXT/" -e "s/\//$DIRSEP/g"` +src=`echo $NGX_MODULES_C | sed -e "s/\//$DIRSEP/g"` +libs=`echo $CORE_LIBS | sed -e "s/\.c\$/.$OBJEXT/" -e "s/\//$DIRSEP/g"` +link=`echo $CORE_LINK | sed -e "s/\.c\$/.$OBJEXT/" -e "s/\//$DIRSEP/g"` + +if [ $MAKE_SL = YES ]; then + echo " $obj $libs $CORE_LINK" >> $MAKEFILE + echo >> $MAKEFILE +else + echo " $obj \\" >> $MAKEFILE + echo " $libs \\" >> $MAKEFILE + echo " $link" >> $MAKEFILE + echo >> $MAKEFILE +fi + + +# ngx_modules.c + +deps="\$(CORE_DEPS)" + +if [ $PCH != NO ]; then + args="\$(CFLAGS) $USEPCH \$(ALL_INCS)" +else + args="\$(CFLAGS) $USEPCH \$(CORE_INCS)" +fi + +if [ $MAKE_SL = YES ]; then + echo "$obj: $NGX_MODULES_C $deps" >> $MAKEFILE + echo $ngx_n " \$(CC) $COMPOPT $args" $ngx_c >> $MAKEFILE + echo " $OBJOUT$obj $src" >> $MAKEFILE + echo >> $MAKEFILE +else + echo "$obj: \\" >> $MAKEFILE + echo " $NGX_MODULES_C $deps" >> $MAKEFILE + echo " \$(CC) $COMPOPT $args \\" >> $MAKEFILE + echo " $OBJOUT$obj \\" >> $MAKEFILE + echo " $src" >> $MAKEFILE + echo >> $MAKEFILE +fi + + +# core sources + +for src in $CORE_SRCS +do + obj=`echo $src | sed -e "s/\.c\$/.$OBJEXT/" -e "s/\.S\$/.$OBJEXT/"` + obj=`echo $OBJS/$obj | sed -e "s/\//$DIRSEP/g"` + src=`echo $src | sed -e "s/\//$DIRSEP/g"` + + if [ $MAKE_SL = YES ]; then + echo "$obj: $src $deps" >> $MAKEFILE + echo " \$(CC) $COMPOPT $args $OBJOUT$obj $src" >> $MAKEFILE + echo >> $MAKEFILE + else + echo "$obj: \\" >> $MAKEFILE + echo " $src $deps" >> $MAKEFILE + echo " \$(CC) $COMPOPT $args \\" >> $MAKEFILE + echo " $OBJOUT$obj \\" >> $MAKEFILE + echo " $src" >> $MAKEFILE + echo >> $MAKEFILE + fi +done + + +# http sources + +if [ $HTTP = YES ]; then + + deps="\$(CORE_DEPS) \$(HTTP_DEPS)" + + if [ $PCH != NO ]; then + args="\$(CFLAGS) $USEPCH \$(ALL_INCS)" + else + args="\$(CFLAGS) $USEPCH \$(CORE_INCS) \$(HTTP_INCS)" + fi + + for src in $HTTP_SRCS + do + obj=`echo $src | sed -e "s/\.c\$/.$OBJEXT/"` + obj=`echo $OBJS/$obj | sed -e "s/\//$DIRSEP/g"` + src=`echo $src | sed -e "s/\//$DIRSEP/g"` + + if [ $MAKE_SL = YES ]; then + echo "$obj: $src $deps" >> $MAKEFILE + echo " \$(CC) $COMPOPT $args $OBJOUT$obj $src" >> $MAKEFILE + echo >> $MAKEFILE + else + echo "$obj: \\" >> $MAKEFILE + echo " $src $deps" >> $MAKEFILE + echo " \$(CC) $COMPOPT $args \\" >> $MAKEFILE + echo " $OBJOUT$obj \\" >> $MAKEFILE + echo " $src" >> $MAKEFILE + echo >> $MAKEFILE + fi + done + +fi + + +# imap sources + +if [ $IMAP = YES ]; then + + deps="\$(CORE_DEPS) \$(IMAP_DEPS)" + + if [ $PCH != NO ]; then + args="\$(CFLAGS) $USEPCH \$(ALL_INCS)" + else + args="\$(CFLAGS) $USEPCH \$(CORE_INCS) \$(IMAP_INCS)" + fi + + for src in $IMAP_SRCS + do + obj=`echo $src | sed -e "s/\.c\$/.$OBJEXT/"` + obj=`echo $OBJS/$obj | sed -e "s/\//$DIRSEP/g"` + src=`echo $src | sed -e "s/\//$DIRSEP/g"` + + if [ $MAKE_SL = YES ]; then + echo "$obj: $src $deps" >> $MAKEFILE + echo " \$(CC) $COMPOPT $args $OBJOUT$obj $src" >> $MAKEFILE + echo >> $MAKEFILE + else + echo "$obj: \\" >> $MAKEFILE + echo " $src $deps" >> $MAKEFILE + echo " \$(CC) $COMPOPT $args \\" >> $MAKEFILE + echo " $OBJOUT$obj \\" >> $MAKEFILE + echo " $src" >> $MAKEFILE + echo >> $MAKEFILE + fi + done + +fi + + +# precompiled headers + +if [ $PCH != NO ]; then + echo "#include " > $OBJS/pch.c + + pch="$PCH: src/core/ngx_config.h $OS_CONFIG $OBJS/ngx_auto_config.h" + pch=`echo $pch | sed -e "s/\//$DIRSEP/g"` + src="\$(CC) \$(CFLAGS) $BUILDPCH $COMPOPT \$(ALL_INCS)" + src="$src $OBJOUT$OBJS/pch.obj $OBJS/pch.c" + src=`echo $src | sed -e "s/\//$DIRSEP/g"` + + echo "$pch" >> $MAKEFILE + echo " $src" >> $MAKEFILE + echo >> $MAKEFILE +fi diff --git a/auto/modules b/auto/modules new file mode 100644 --- /dev/null +++ b/auto/modules @@ -0,0 +1,167 @@ + +# Copyright (C) Igor Sysoev + + +if [ $EVENT_SELECT = NO -a $EVENT_FOUND = NO ]; then + EVENT_SELECT=YES +fi + +if [ $EVENT_SELECT = YES ]; then + CORE_SRCS="$CORE_SRCS $SELECT_SRCS" + EVENT_MODULES="$EVENT_MODULES $SELECT_MODULE" +fi + + +if [ $EVENT_POLL = NO -a $EVENT_FOUND = NO ]; then + EVENT_POLL=YES +fi + +if [ $EVENT_POLL = YES ]; then + CORE_SRCS="$CORE_SRCS $POLL_SRCS" + EVENT_MODULES="$EVENT_MODULES $POLL_MODULE" +fi + + +if [ $TEST_BUILD_DEVPOLL = YES ]; then + have=HAVE_DEVPOLL . auto/have + have=TEST_BUILD_DEVPOLL . auto/have + EVENT_MODULES="$EVENT_MODULES $DEVPOLL_MODULE" + CORE_SRCS="$CORE_SRCS $DEVPOLL_SRCS" +fi + +if [ $TEST_BUILD_EPOLL = YES ]; then + have=HAVE_EPOLL . auto/have + have=TEST_BUILD_EPOLL . auto/have + EVENT_MODULES="$EVENT_MODULES $EPOLL_MODULE" + CORE_SRCS="$CORE_SRCS $EPOLL_SRCS" +fi + +if [ $TEST_BUILD_RTSIG = YES ]; then + have=HAVE_RTSIG . auto/have + have=TEST_BUILD_RTSIG . auto/have + EVENT_MODULES="$EVENT_MODULES $RTSIG_MODULE" + CORE_SRCS="$CORE_SRCS $RTSIG_SRCS" +fi + + +# the filter order is important +# ngx_http_write_filter +# ngx_http_header_filter +# ngx_http_chunked_filter +# ngx_http_range_header_filter +# ngx_http_ssl_filter +# ngx_http_gzip_filter +# ngx_http_charset_filter +# ngx_http_ssi_filter +# ngx_http_headers_filter +# ngx_http_copy_filter +# ngx_http_range_body_filter +# ngx_http_not_modified_filter + +HTTP_FILTER_MODULES="$HTTP_WRITE_FILTER_MODULE \ + $HTTP_HEADER_FILTER_MODULE \ + $HTTP_CHUNKED_FILTER_MODULE \ + $HTTP_RANGE_HEADER_FILTER_MODULE" + +if [ $HTTP_GZIP = YES ]; then + have=NGX_HTTP_GZIP . auto/have + USE_ZLIB=YES + HTTP_FILTER_MODULES="$HTTP_FILTER_MODULES $HTTP_GZIP_FILTER_MODULE" + HTTP_SRCS="$HTTP_SRCS $HTTP_GZIP_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" +fi + +if [ $HTTP_SSI = YES ]; then + have=NGX_HTTP_SSI . auto/have + HTTP_FILTER_MODULES="$HTTP_FILTER_MODULES $HTTP_SSI_FILTER_MODULE" + HTTP_SRCS="$HTTP_SRCS $HTTP_SSI_SRCS" +fi + +if [ $HTTP_USERID = YES ]; then + HTTP_FILTER_MODULES="$HTTP_FILTER_MODULES $HTTP_USERID_FILTER_MODULE" + HTTP_SRCS="$HTTP_SRCS $HTTP_USERID_SRCS" +fi + +HTTP_MODULES="$HTTP_MODULES $HTTP_STATIC_MODULE $HTTP_INDEX_MODULE" + +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 + +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 + +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" +fi + +if [ $HTTP_SSL = YES ]; then + USE_OPENSSL=YES + have=NGX_HTTP_SSL . auto/have + HTTP_MODULES="$HTTP_MODULES $HTTP_SSL_MODULE" + HTTP_DEPS="$HTTP_DEPS $HTTP_SSL_DEPS" + HTTP_SRCS="$HTTP_SRCS $HTTP_SSL_SRCS" +fi + +if [ $HTTP_PROXY = YES ]; then + have=NGX_HTTP_PROXY . auto/have + #USE_MD5=YES + HTTP_MODULES="$HTTP_MODULES $HTTP_PROXY_MODULE" + HTTP_INCS="$HTTP_INCS $HTTP_PROXY_INCS" + HTTP_DEPS="$HTTP_DEPS $HTTP_PROXY_DEPS" + HTTP_SRCS="$HTTP_SRCS $HTTP_PROXY_SRCS" +fi + +if [ -r $OBJS/auto ]; then + . $OBJS/auto +fi + +modules="$CORE_MODULES $EVENT_MODULES" + +if [ $HTTP = YES ]; then + modules="$modules $HTTP_MODULES $HTTP_FILTER_MODULES \ + $HTTP_HEADERS_FILTER_MODULE \ + $HTTP_COPY_FILTER_MODULE \ + $HTTP_RANGE_BODY_FILTER_MODULE \ + $HTTP_NOT_MODIFIED_FILTER_MODULE" +fi + +IMAP_MODULES=$IMAP_MODULE + +if [ $IMAP = YES ]; then + modules="$modules $IMAP_MODULES" +fi + + +echo "#include " > $NGX_MODULES_C +echo "#include " >> $NGX_MODULES_C +echo >> $NGX_MODULES_C + +for mod in $modules +do + echo "extern ngx_module_t $mod;" >> $NGX_MODULES_C +done + +echo >> $NGX_MODULES_C +echo 'ngx_module_t *ngx_modules[] = {' >> $NGX_MODULES_C + +for mod in $modules +do + echo " &$mod," >> $NGX_MODULES_C +done + +echo " NULL" >> $NGX_MODULES_C +echo "};" >> $NGX_MODULES_C diff --git a/auto/nohave b/auto/nohave new file mode 100644 --- /dev/null +++ b/auto/nohave @@ -0,0 +1,11 @@ + +# Copyright (C) Igor Sysoev + + +cat << END >> $NGX_AUTO_CONFIG_H + +#ifndef $have +#define $have 0 +#endif + +END diff --git a/auto/options b/auto/options new file mode 100644 --- /dev/null +++ b/auto/options @@ -0,0 +1,260 @@ + +# Copyright (C) Igor Sysoev + +help=no + +PREFIX= +SBIN_PATH= +CONF_PATH= +HTTP_LOG_PATH= +ERROR_LOG_PATH= +PID_PATH= + +CC=gcc +CPP= +OBJS=objs + +DEBUG=NO +CC_OPT= +CPU=NO + +TEST_BUILD_DEVPOLL=NO +TEST_BUILD_EPOLL=NO +TEST_BUILD_RTSIG=NO + +EVENT_FOUND=NO + +EVENT_RTSIG=NO +EVENT_SELECT=NO +EVENT_POLL=NO +EVENT_AIO=NO + +USE_THREADS=NO + +HTTP=YES +HTTP_CHARSET=YES +HTTP_GZIP=YES +HTTP_SSL=NO +HTTP_SSI=NO +HTTP_ACCESS=YES +HTTP_USERID=YES +HTTP_STATUS=NO +HTTP_REWRITE=YES +HTTP_PROXY=YES + +IMAP=NO + +USE_PCRE=NO +PCRE=NONE +PCRE_OPT= + +USE_OPENSSL=NO +OPENSSL=NONE + +USE_MD5=NO +MD5=NONE +MD5_OPT= +MD5_ASM=NO + +USE_ZLIB=NO +ZLIB=NONE +ZLIB_OPT= +ZLIB_ASM=NO + + +for option +do + case "$option" in + -*=*) value=`echo "$option" | sed -e 's/[-_a-zA-Z0-9]*=//'` ;; + *) value="" ;; + esac + + case "$option" in + --help) help=yes ;; + + --prefix=*) PREFIX="$value" ;; + --sbin-path=*) SBIN_PATH="$value" ;; + --conf-path=*) CONF_PATH="$value" ;; + --error-log-path=*) ERROR_LOG_PATH="$value" ;; + --pid-path=*) PID_PATH="$value" ;; + + --crossbuild=*) PLATFORM="$value" ;; + + --builddir=*) OBJS="$value" ;; + + --with-rtsig_module) EVENT_RTSIG=YES ;; + --with-select_module) EVENT_SELECT=YES ;; + --without-select_module) EVENT_SELECT=NONE ;; + --with-poll_module) EVENT_POLL=YES ;; + --without-poll_module) EVENT_POLL=NONE ;; + --with-aio_module) EVENT_AIO=YES ;; + + --with-threads=*) USE_THREADS="$value" ;; + --with-threads) USE_THREADS="pthreads" ;; + + --without-http) HTTP=NO ;; + --http-log-path=*) HTTP_LOG_PATH="$value" ;; + + --with-http_ssl_module) HTTP_SSL=YES ;; + --without-http_charset_module) HTTP_CHARSET=NO ;; + --without-http_gzip_module) HTTP_GZIP=NO ;; + --without-http_ssi_module) HTTP_SSI=NO ;; + --without-http_userid_module) HTTP_USERID=NO ;; + --without-http_access_module) HTTP_ACCESS=NO ;; + --without-http_status_module) HTTP_STATUS=NO ;; + --without-http_rewrite_module) HTTP_REWRITE=NO ;; + --without-http_proxy_module) HTTP_PROXY=NO ;; + + --with-imap) IMAP=YES ;; + + --with-cc=*) CC="$value" ;; + --with-cpp=*) CPP="$value" ;; + --with-cc-opt=*) CC_OPT="$value" ;; + --with-cpu-opt=*) CPU="$value" ;; + --with-debug) DEBUG=YES ;; + + --without-pcre) USE_PCRE=DISABLED ;; + --with-pcre=*) PCRE="$value" ;; + --with-pcre-opt=*) PCRE_OPT="$value" ;; + + --with-openssl=*) OPENSSL="$value" ;; + + --with-md5=*) MD5="$value" ;; + --with-md5-opt=*) MD5_OPT="$value" ;; + --with-md5-asm) MD5_ASM=YES ;; + + --with-zlib=*) ZLIB="$value" ;; + --with-zlib-opt=*) ZLIB_OPT="$value" ;; + --with-zlib-asm=*) ZLIB_ASM="$value" ;; + + --test-build-devpoll) TEST_BUILD_DEVPOLL=YES ;; + --test-build-epoll) TEST_BUILD_EPOLL=YES ;; + --test-build-rtsig) TEST_BUILD_RTSIG=YES ;; + + *) + echo "$0: error: invalid option \"$option\"" + exit 1 + ;; + esac +done + + +if [ $help = yes ]; then + echo + echo " --help this message" + echo + + echo " --without-select_module disable select_module" + echo " --without-poll_module disable poll_module" + + echo " --without-http_rewrite_module disable http_rewrite_module" + echo " --without-http_gzip_module disable http_gzip_module" + echo " --without-http_proxy_module disable http_proxy_module" + + echo " --with-cc=NAME name of or path to C compiler" + echo + + echo " --with-pcre=DIR path to PCRE library" + echo " --with-md5=DIR path to md5 library" + echo " --with-zlib=DIR path to zlib library" + echo + + exit 1 +fi + + +if [ $HTTP = NO ]; then + HTTP_CHARSET=NO + HTTP_GZIP=NO + HTTP_SSI=NO + HTTP_USERID=NO + HTTP_ACCESS=NO + HTTP_STATUS=NO + HTTP_REWRITE=NO + HTTP_PROXY=NO +fi + + +if [ ".$PLATFORM" = ".win32" -a $EVENT_POLL = YES ]; then + EVENT_POLL=NO + echo "$0: warning: --with-poll_module option is ignored for win32" +fi + + +if [ ".$PREFIX" = "." ]; then + PREFIX=/usr/local/nginx +fi + + +case ".$SBIN_PATH" in + ./*) + ;; + + .) + SBIN_PATH=$PREFIX/sbin/nginx + ;; + + *) + SBIN_PATH=$PREFIX/$SBIN_PATH + ;; +esac + + +case ".$CONF_PATH" in + ./*) + ;; + + .) + CONF_PATH=$PREFIX/conf/nginx.conf + ;; + + *) + CONF_PATH=$PREFIX/$CONF_PATH + ;; +esac + + +case ".$PID_PATH" in + ./*) + ;; + + .) + PID_PATH=$PREFIX/logs/nginx.pid + ;; + + *) + PID_PATH=$PREFIX/$PID_PATH + ;; +esac + + +case ".$ERROR_LOG_PATH" in + ./*) + ;; + + .) + ERROR_LOG_PATH=$PREFIX/logs/error.log + ;; + + .stderr) + ERROR_LOG_PATH= + ;; + + *) + ERROR_LOG_PATH=$PREFIX/$ERROR_LOG_PATH + ;; +esac + + +case ".$HTTP_LOG_PATH" in + ./*) + ;; + + .) + HTTP_LOG_PATH=$PREFIX/logs/access.log + ;; + + *) + HTTP_LOG_PATH=$PREFIX/$HTTP_LOG_PATH + ;; +esac diff --git a/auto/os/conf b/auto/os/conf new file mode 100644 --- /dev/null +++ b/auto/os/conf @@ -0,0 +1,57 @@ + +# Copyright (C) Igor Sysoev + + +if [ ".$PLATFORM" = "." ]; then + echo "checking for OS" + + SYSTEM=`uname -s 2>/dev/null` + RELEASE=`uname -r 2>/dev/null` + MACHINE=`uname -m 2>/dev/null` + + echo " + $SYSTEM $RELEASE $MACHINE" + + PLATFORM="$SYSTEM:$RELEASE:$MACHINE"; +else + echo "building for $PLATFORM" +fi + +case $PLATFORM in + + FreeBSD:* | DragonFly:*) + . auto/os/freebsd + ;; + + Linux:*) + . auto/os/linux + ;; + + SunOS:*) + . auto/os/solaris + ;; + + win32) + CORE_INCS="$WIN32_INCS" + CORE_DEPS="$WIN32_DEPS" + CORE_SRCS="$WIN32_SRCS $IOCP_SRCS" + OS_CONFIG="$WIN32_CONFIG" + EVENT_MODULES="$EVENT_MODULES $IOCP_MODULE" + EVENT_FOUND=YES + + if [ $EVENT_SELECT = NO ]; then + CORE_SRCS="$CORE_SRCS $SELECT_SRCS" + EVENT_MODULES="$EVENT_MODULES $SELECT_MODULE" + fi + + have=HAVE_AIO . auto/have + have=HAVE_IOCP . auto/have + CORE_LIBS="$CORE_LIBS ws2_32.lib" + ;; + + *) + CORE_INCS="$UNIX_INCS" + CORE_DEPS="$UNIX_DEPS $POSIX_DEPS" + CORE_SRCS="$UNIX_SRCS" + ;; + +esac diff --git a/auto/os/freebsd b/auto/os/freebsd new file mode 100644 --- /dev/null +++ b/auto/os/freebsd @@ -0,0 +1,89 @@ + +# Copyright (C) Igor Sysoev + + +CORE_INCS="$UNIX_INCS" +CORE_DEPS="$UNIX_DEPS $FREEBSD_DEPS" +CORE_SRCS="$UNIX_SRCS $FREEBSD_SRCS" + +PIPE="-pipe" + + +# __FreeBSD_version is the best way to determine whether +# some capability exists and is safe to use + +version=`grep "#define __FreeBSD_version" /usr/include/osreldate.h \ + | sed -e 's/^.* \(.*\)$/\1/'` + + +# setproctitle() in libutil + +if [ \( $version -ge 500000 -a $version -lt 500012 \) \ + -o $version -lt 410002 ] +then + echo " + setproctitle() in libutil" + + CORE_LIBS="$CORE_LIBS -lutil" +fi + +# sendfile + +if [ $version -gt 300007 ]; then + echo " + using sendfile()" + + have=HAVE_SENDFILE . auto/have + CORE_SRCS="$CORE_SRCS $FREEBSD_SENDFILE_SRCS" +fi + + +# kqueue + +if [ \( $version -lt 500000 -a $version -ge 410000 \) \ + -o $version -ge 500011 ] +then + echo " + using kqueue" + + have=HAVE_KQUEUE . auto/have + have=HAVE_CLEAR_EVENT . auto/have + EVENT_MODULES="$EVENT_MODULES $KQUEUE_MODULE" + CORE_SRCS="$CORE_SRCS $KQUEUE_SRCS" + EVENT_FOUND=YES +fi + + +# kqueue's NOTE_LAWAT + +if [ \( $version -lt 500000 -a $version -ge 430000 \) \ + -o $version -ge 500018 ] +then + echo " + using kqueue's NOTE_LOWAT" + have=HAVE_LOWAT_EVENT . auto/have +fi + + +if [ $USE_THREADS = "rfork" ]; then + + echo " + using rfork()" + +# # kqueue's EVFILT_SIGNAL is safe +# +# if [ $version -gt 460101 ]; then +# echo " + kqueue's EVFILT_SIGNAL is safe" +# have=HAVE_SAFE_EVFILT_SIGNAL . auto/have +# else +# echo "$0: error: the kqueue's EVFILT_SIGNAL is unsafe on this" +# echo "FreeBSD version, so --with-threads=rfork could not be used" +# echo +# +# exit 1 +# fi +fi + + +if [ $EVENT_AIO = YES ]; then + have=HAVE_AIO . auto/have + EVENT_MODULES="$EVENT_MODULES $AIO_MODULE" + CORE_SRCS="$CORE_SRCS $AIO_SRCS" +else + have=HAVE_AIO . auto/nohave +fi diff --git a/auto/os/linux b/auto/os/linux new file mode 100644 --- /dev/null +++ b/auto/os/linux @@ -0,0 +1,82 @@ + +# Copyright (C) Igor Sysoev + + +CORE_INCS="$UNIX_INCS" +CORE_DEPS="$UNIX_DEPS $LINUX_DEPS" +CORE_SRCS="$UNIX_SRCS $LINUX_SRCS" +EVENT_MODULES="$EVENT_MODULES" + +PIPE="-pipe" + + +CC_TEST_FLAGS="-D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE" + +# Linux kernel version + +version=`grep "#define LINUX_VERSION_CODE" /usr/include/linux/version.h \ + | sed -e 's/^.* \(.*\)$/\1/'` + + +# enable the rt signals on Linux 2.2.19 and onward + +if [ $version -ge 131609 -o $EVENT_RTSIG = YES ]; then + echo " + using rt signals" + have=HAVE_RTSIG . auto/have + EVENT_MODULES="$EVENT_MODULES $RTSIG_MODULE" + CORE_SRCS="$CORE_SRCS $RTSIG_SRCS" + EVENT_FOUND=YES +fi + + +# epoll, EPOLLET version + +ngx_func="epoll"; +ngx_func_inc="#include " +ngx_func_test="int efd = 0, fd = 1, n; + struct epoll_event ee; + ee.events = EPOLLIN|EPOLLOUT|EPOLLET; + ee.data.ptr = NULL; + n = epoll_ctl(efd, EPOLL_CTL_ADD, fd, &ee)" +. auto/func + +if [ $ngx_found = yes ]; then + have=HAVE_EPOLL . auto/have + have=HAVE_CLEAR_EVENT . auto/have + CORE_SRCS="$CORE_SRCS $EPOLL_SRCS" + EVENT_MODULES="$EVENT_MODULES $EPOLL_MODULE" + EVENT_FOUND=YES +fi + + +# sendfile() + +CC_TEST_FLAGS="-D_GNU_SOURCE" +ngx_func="sendfile()"; +ngx_func_inc="#include " +ngx_func_test="int s = 0, fd = 1; + ssize_t n; off_t off = 0; + n = sendfile(s, fd, &off, 1)" +. auto/func + +if [ $ngx_found = yes ]; then + CORE_SRCS="$CORE_SRCS $LINUX_SENDFILE_SRCS" +fi + + +# sendfile64() + +CC_TEST_FLAGS="-D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE" +ngx_func="sendfile64()"; . auto/func + + +# prctl(PR_SET_DUMPABLE) + +ngx_func="prctl()"; +ngx_func_inc="#include " +ngx_func_test="prctl(PR_SET_DUMPABLE, 1, 0, 0, 0)" +. auto/func + +if [ $ngx_found = yes ]; then + have=HAVE_PR_SET_DUMPABLE . auto/have +fi diff --git a/auto/os/solaris b/auto/os/solaris new file mode 100644 --- /dev/null +++ b/auto/os/solaris @@ -0,0 +1,83 @@ + +# Copyright (C) Igor Sysoev + + +CORE_INCS="$UNIX_INCS" +CORE_DEPS="$UNIX_DEPS $SOLARIS_DEPS" +CORE_SRCS="$UNIX_SRCS $SOLARIS_SRCS " +EVENT_MODULES="$EVENT_MODULES" + +CORE_LIBS="$CORE_LIBS -lsocket -lnsl -lrt" + +# the Solaris's make support +MAKE_SL=YES + + +CC_TEST_FLAGS="-D_FILE_OFFSET_BITS=64" + +case $PLATFORM in + + SunOS:5.[89]:* | SunOS:5.10:*) + PIPE="-pipe" + ;; + + *) + # Solaris 7's /usr/ccs/bin/as does not support "-pipe" + ;; + +esac + + +case $PLATFORM in + + *:sun4u) + CFLAGS="$CFLAGS -mcpu=v9" + + if [ ".$CPU" = ".sparc64" ]; then + CFLAGS="$CFLAGS -m64" + CPU_OPT="-m64" + CORE_LINK="$CORE_LINK -m64" + + CC_TEST_FLAGS="$CC_TEST_FLAGS -mcpu=v9 -m64" + fi + ;; + + *) + ;; + +esac + + +if [ $ZLIB_ASM != NO ]; then + echo "$0: error: the --with-zlib-asm=CPU option is not supported" + echo "on that platform" + echo + + exit 1 +fi + + +ngx_inc="sys/devpoll.h"; . auto/inc + +if [ $ngx_found = yes ]; then + have=HAVE_DEVPOLL . auto/have + CORE_SRCS="$CORE_SRCS $DEVPOLL_SRCS" + EVENT_MODULES="$EVENT_MODULES $DEVPOLL_MODULE" + EVENT_FOUND=YES +fi + + +ngx_func="sendfilev()"; +ngx_func_inc="#include " +ngx_func_libs="-lsendfile" +ngx_func_test="int fd = 1; sendfilevec_t vec[1]; + size_t sent; ssize_t n; + n = sendfilev(fd, vec, 1, &sent)" +. auto/func + + +if [ $ngx_found = yes ]; then + have=HAVE_SENDFILE . auto/have + CORE_SRCS="$CORE_SRCS $SOLARIS_SENDFILEV_SRCS" + CORE_LIBS="$CORE_LIBS -lsendfile" +fi diff --git a/auto/sources b/auto/sources new file mode 100644 --- /dev/null +++ b/auto/sources @@ -0,0 +1,307 @@ + +# Copyright (C) Igor Sysoev + + +CORE_MODULES="ngx_core_module ngx_errlog_module ngx_conf_module" + +CORE_INCS="src/core" + +CORE_DEPS="src/core/nginx.h \ + src/core/ngx_config.h \ + src/core/ngx_core.h \ + src/core/ngx_log.h \ + src/core/ngx_palloc.h \ + src/core/ngx_array.h \ + src/core/ngx_list.h \ + src/core/ngx_table.h \ + src/core/ngx_buf.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_rbtree.h \ + src/core/ngx_times.h \ + 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_radix_tree.h \ +# src/core/ngx_radix_tree.c \ + +CORE_SRCS="src/core/nginx.c \ + src/core/ngx_log.c \ + src/core/ngx_palloc.c \ + src/core/ngx_array.c \ + src/core/ngx_list.c \ + src/core/ngx_buf.c \ + src/core/ngx_output_chain.c \ + src/core/ngx_string.c \ + src/core/ngx_parse.c \ + src/core/ngx_inet.c \ + src/core/ngx_file.c \ + src/core/ngx_rbtree.c \ + src/core/ngx_times.c \ + src/core/ngx_connection.c \ + src/core/ngx_cycle.c \ + src/core/ngx_spinlock.c \ + src/core/ngx_conf_file.c \ + src/core/ngx_garbage_collector.c" + + +REGEX_DEPS=src/core/ngx_regex.h +REGEX_SRCS=src/core/ngx_regex.c + + +EVENT_MODULES="ngx_events_module ngx_event_core_module" + +EVENT_INCS="src/event src/event/modules" + +EVENT_DEPS="src/event/ngx_event.h \ + src/event/ngx_event_timer.h \ + src/event/ngx_event_posted.h \ + src/event/ngx_event_busy_lock.h \ + src/event/ngx_event_connect.h \ + src/event/ngx_event_pipe.h" + +EVENT_SRCS="src/event/ngx_event.c \ + src/event/ngx_event_timer.c \ + src/event/ngx_event_posted.c \ + src/event/ngx_event_busy_lock.c \ + src/event/ngx_event_accept.c \ + src/event/ngx_event_connect.c \ + src/event/ngx_event_pipe.c" + + +SELECT_MODULE=ngx_select_module +SELECT_SRCS=src/event/modules/ngx_select_module.c + +POLL_MODULE=ngx_poll_module +POLL_SRCS=src/event/modules/ngx_poll_module.c + +KQUEUE_MODULE=ngx_kqueue_module +KQUEUE_SRCS=src/event/modules/ngx_kqueue_module.c + +DEVPOLL_MODULE=ngx_devpoll_module +DEVPOLL_SRCS=src/event/modules/ngx_devpoll_module.c + +EPOLL_MODULE=ngx_epoll_module +EPOLL_SRCS=src/event/modules/ngx_epoll_module.c + +RTSIG_MODULE=ngx_rtsig_module +RTSIG_SRCS=src/event/modules/ngx_rtsig_module.c + +IOCP_MODULE=ngx_iocp_module +IOCP_SRCS=src/event/modules/ngx_iocp_module.c + +AIO_MODULE=ngx_aio_module +AIO_SRCS="src/event/modules/ngx_aio_module.c \ + src/os/unix/ngx_aio_read.c \ + src/os/unix/ngx_aio_write.c \ + src/os/unix/ngx_aio_read_chain.c \ + src/os/unix/ngx_aio_write_chain.c" + + +OPENSSL_DEPS=src/event/ngx_event_openssl.h +OPENSSL_SRCS=src/event/ngx_event_openssl.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 \ + src/os/unix/ngx_channel.h \ + src/os/unix/ngx_shared.h \ + src/os/unix/ngx_process.h \ + src/os/unix/ngx_atomic.h \ + src/os/unix/ngx_thread.h \ + src/os/unix/ngx_socket.h \ + src/os/unix/ngx_os.h \ + src/os/unix/ngx_process_cycle.h" + +UNIX_SRCS="$CORE_SRCS $EVENT_SRCS \ + src/os/unix/ngx_time.c \ + src/os/unix/ngx_errno.c \ + src/os/unix/ngx_alloc.c \ + src/os/unix/ngx_files.c \ + src/os/unix/ngx_socket.c \ + src/os/unix/ngx_recv.c \ + src/os/unix/ngx_readv_chain.c \ + src/os/unix/ngx_send.c \ + src/os/unix/ngx_writev_chain.c \ + src/os/unix/ngx_channel.c \ + src/os/unix/ngx_shared.c \ + src/os/unix/ngx_process.c \ + src/os/unix/ngx_daemon.c \ + src/os/unix/ngx_posix_init.c \ + src/os/unix/ngx_process_cycle.c" + +POSIX_DEPS=src/os/unix/ngx_posix_config.h + +FREEBSD_DEPS=src/os/unix/ngx_freebsd_config.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" +FREEBSD_RFORK_SRCS="src/os/unix/ngx_freebsd_rfork_thread.c" +FREEBSD_RFORK_THREAD_SRCS="src/os/unix/rfork_thread.S" + +PTHREAD_SRCS="src/os/unix/ngx_pthread_thread.c" + +LINUX_DEPS=src/os/unix/ngx_linux_config.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_SRCS=src/os/unix/ngx_solaris_init.c +SOLARIS_SENDFILEV_SRCS=src/os/unix/ngx_solaris_sendfilev_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 \ + src/os/win32/ngx_shared.h \ + src/os/win32/ngx_process.h \ + src/os/win32/ngx_atomic.h \ + src/os/win32/ngx_socket.h \ + src/os/win32/ngx_os.h \ + src/os/win32/ngx_process_cycle.h" + +WIN32_CONFIG=src/os/win32/ngx_win32_config.h + +WIN32_SRCS="$CORE_SRCS $EVENT_SRCS \ + src/os/win32/ngx_errno.c \ + src/os/win32/ngx_alloc.c \ + src/os/win32/ngx_files.c \ + src/os/win32/ngx_time.c \ + src/os/win32/ngx_process.c \ + src/os/win32/ngx_socket.c \ + src/os/win32/ngx_wsarecv.c \ + src/os/win32/ngx_wsarecv_chain.c \ + src/os/win32/ngx_wsasend_chain.c \ + src/os/win32/ngx_win32_init.c \ + src/os/win32/ngx_process_cycle.c \ + src/event/ngx_event_acceptex.c" + + +HTTP_MODULES="ngx_http_module \ + ngx_http_core_module \ + ngx_http_log_module" + +HTTP_FILE_CACHE_MODULE=ngx_http_cache_module + +HTTP_WRITE_FILTER_MODULE="ngx_http_write_filter_module" +HTTP_HEADER_FILTER_MODULE="ngx_http_header_filter_module" + +HTTP_CHUNKED_FILTER_MODULE=ngx_http_chunked_filter_module +HTTP_HEADERS_FILTER_MODULE=ngx_http_headers_filter_module +HTTP_COPY_FILTER_MODULE=ngx_http_copy_filter_module + +HTTP_RANGE_HEADER_FILTER_MODULE=ngx_http_range_header_filter_module +HTTP_RANGE_BODY_FILTER_MODULE=ngx_http_range_body_filter_module + +HTTP_NOT_MODIFIED_FILTER_MODULE=ngx_http_not_modified_filter_module + +HTTP_STATIC_MODULE=ngx_http_static_module +HTTP_INDEX_MODULE=ngx_http_index_module + +HTTP_INCS="src/http src/http/modules" + +HTTP_DEPS="src/http/ngx_http.h \ + src/http/ngx_http_request.h \ + src/http/ngx_http_config.h \ + src/http/ngx_http_core_module.h \ + src/http/ngx_http_cache.h \ + src/http/ngx_http_busy_lock.h \ + src/http/ngx_http_log_handler.h" + +HTTP_SRCS="src/http/ngx_http.c \ + src/http/ngx_http_core_module.c \ + src/http/ngx_http_special_response.c \ + src/http/ngx_http_request.c \ + src/http/ngx_http_parse.c \ + src/http/ngx_http_header_filter.c \ + src/http/ngx_http_write_filter.c \ + src/http/ngx_http_copy_filter.c \ + src/http/ngx_http_log_handler.c \ + src/http/ngx_http_request_body.c \ + src/http/ngx_http_parse_time.c \ + src/http/modules/ngx_http_static_handler.c \ + src/http/modules/ngx_http_index_handler.c \ + src/http/modules/ngx_http_chunked_filter.c \ + src/http/modules/ngx_http_range_filter.c \ + src/http/modules/ngx_http_headers_filter.c \ + src/http/modules/ngx_http_not_modified_filter.c" + +# STUB +HTTP_SRCS="$HTTP_SRCS src/http/ngx_http_busy_lock.c" + +HTPP_CACHE_SRCS=src/http/ngx_http_cache.c +HTPP_FILE_CACHE_SRCS=src/http/ngx_http_file_cache.c + + +HTTP_CHARSET_FILTER_MODULE=ngx_http_charset_filter_module +HTTP_CHARSET_SRCS=src/http/modules/ngx_http_charset_filter.c + + +HTTP_GZIP_FILTER_MODULE=ngx_http_gzip_filter_module +HTTP_GZIP_SRCS=src/http/modules/ngx_http_gzip_filter.c + + +HTTP_SSI_FILTER_MODULE=ngx_http_ssi_filter_module +HTTP_SSI_SRCS=src/http/modules/ngx_http_ssi_filter.c + + +HTTP_USERID_FILTER_MODULE=ngx_http_userid_filter_module +HTTP_USERID_SRCS=src/http/modules/ngx_http_userid_filter.c + + +HTTP_ACCESS_MODULE=ngx_http_access_module +HTTP_ACCESS_SRCS=src/http/modules/ngx_http_access_handler.c + + +HTTP_STATUS_MODULE=ngx_http_status_module +HTTP_STATUS_SRCS=src/http/modules/ngx_http_status_handler.c + + +HTTP_REWRITE_MODULE=ngx_http_rewrite_module +HTTP_REWRITE_SRCS=src/http/modules/ngx_http_rewrite_handler.c + + +HTTP_SSL_MODULE=ngx_http_ssl_module +HTTP_SSL_DEPS=src/http/modules/ngx_http_ssl_module.h +HTTP_SSL_SRCS=src/http/modules/ngx_http_ssl_module.c + + +HTTP_PROXY_MODULE=ngx_http_proxy_module +HTTP_PROXY_INCS="src/http/modules/proxy" +HTTP_PROXY_DEPS=src/http/modules/proxy/ngx_http_proxy_handler.h +HTTP_PROXY_SRCS="src/http/modules/proxy/ngx_http_proxy_handler.c \ + src/http/modules/proxy/ngx_http_proxy_upstream.c \ + src/http/modules/proxy/ngx_http_proxy_parse.c \ + src/http/modules/proxy/ngx_http_proxy_header.c" + +# STUB +# src/http/modules/proxy/ngx_http_proxy_cache.c \ + + +IMAP_INCS="src/imap" + +IMAP_DEPS="src/imap/ngx_imap.h" + +IMAP_MODULE=ngx_imap_module +IMAP_SRCS="src/imap/ngx_imap.c \ + src/imap/ngx_imap_handler.c \ + src/imap/ngx_imap_parse.c \ + src/imap/ngx_imap_proxy.c" diff --git a/auto/summary b/auto/summary new file mode 100644 --- /dev/null +++ b/auto/summary @@ -0,0 +1,98 @@ + +# Copyright (C) Igor Sysoev + + +echo +echo "Configuration summary" + + +if [ $USE_PCRE = DISABLED ]; then + echo " + PCRE library is disabled" + +else + 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 + +case $MD5 in + YES) echo " + md5: using system $MD5_LIB library" ;; + NONE) echo " + md5 library is not used" ;; + NO) echo " + md5 library is not found" ;; + *) echo " + using md5 library: $MD5" ;; +esac + +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 $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 + + +echo " nginx path prefix: $PREFIX" +echo " nginx binary file: $SBIN_PATH" +echo " nginx configuration file: $CONF_PATH" +echo " nginx pid file: $PID_PATH" +if [ ".$ERROR_LOG_PATH" != "." ]; then + echo " nginx error log file: $ERROR_LOG_PATH" +else + echo " nginx logs errors to stderr" +fi +echo " nginx http access log file: $HTTP_LOG_PATH" +echo diff --git a/auto/threads b/auto/threads new file mode 100644 --- /dev/null +++ b/auto/threads @@ -0,0 +1,63 @@ + +# Copyright (C) Igor Sysoev + + +case $USE_THREADS in + rfork) + have=NGX_THREADS . auto/have + have=NGX_USE_RFORK . auto/have + CORE_DEPS="$CORE_DEPS $FREEBSD_RFORK_DEPS" + CORE_SRCS="$CORE_SRCS $FREEBSD_RFORK_SRCS" + + case $PLATFORM in + *:i386) + if [ \( $version -gt 500000 -a $version -lt 501000 \) \ + -o $version -lt 491000 ] + then + CORE_SRCS="$CORE_SRCS $FREEBSD_RFORK_THREAD_SRCS" + fi + ;; + esac + ;; + + pthread) + have=NGX_THREADS . auto/have + CORE_SRCS="$CORE_SRCS $PTHREAD_SRCS" + CORE_LIBS="$CORE_LIBS -lpthread" + ;; + + freebsd4) + have=NGX_THREADS . auto/have + CFLAGS="$CFLAGS -pthread" + CORE_SRCS="$CORE_SRCS $PTHREAD_SRCS" + CORE_LIBS="$CORE_LIBS -pthread" + ;; + + linuxthreads) + have=NGX_THREADS . auto/have + have=NGX_LINUXTHREADS . auto/have + CFLAGS="$CFLAGS -D_THREAD_SAFE" + CFLAGS="$CFLAGS -I /usr/local/include/pthread/linuxthreads" + CORE_SRCS="$CORE_SRCS $PTHREAD_SRCS" + CORE_LIBS="$CORE_LIBS -L /usr/local/lib -llthread -llgcc_r" + ;; + + lc_r) + have=NGX_THREADS . auto/have + CORE_SRCS="$CORE_SRCS $PTHREAD_SRCS" + CORE_LIBS="$CORE_LIBS -lc_r" + ;; + + lthr) + have=NGX_THREADS . auto/have + CORE_SRCS="$CORE_SRCS $PTHREAD_SRCS" + CORE_LIBS="$CORE_LIBS -lthr" + ;; + + lkse) + have=NGX_THREADS . auto/have + CORE_SRCS="$CORE_SRCS $PTHREAD_SRCS" + CORE_LIBS="$CORE_LIBS -lkse" + ;; + +esac diff --git a/auto/types/sizeof b/auto/types/sizeof new file mode 100644 --- /dev/null +++ b/auto/types/sizeof @@ -0,0 +1,62 @@ + +# Copyright (C) Igor Sysoev + + +echo $ngx_n "checking for $ngx_type size ..." $ngx_c +echo >> $NGX_ERR +echo "checking for $ngx_type size" >> $NGX_ERR + +ngx_size= + +cat << END > $NGX_AUTOTEST.c + +#include +#include +$NGX_UNISTD_H +#include +#include +$NGX_INTTYPES_H +$NGX_AUTO_CONFIG + +int main() { + printf("%d", sizeof($ngx_type)); + return 0; +} + +END + +eval "$CC $CC_TEST_FLAGS -o $NGX_AUTOTEST $NGX_AUTOTEST.c >> $NGX_ERR 2>&1" + +if [ -x $NGX_AUTOTEST ]; then + ngx_size=`$NGX_AUTOTEST` + echo " $ngx_size bytes" +fi + +rm $NGX_AUTOTEST* + +case $ngx_size in + 4) + if [ "$ngx_type"="long" ]; then + ngx_max_value=2147483647L + else + ngx_max_value=2147483647 + fi + + ngx_max_len='sizeof("-2147483648") - 1' + ;; + + 8) + if [ "$ngx_type"="long long" ]; then + ngx_max_value=9223372036854775807LL + else + ngx_max_value=9223372036854775807L + fi + + ngx_max_len='sizeof("-9223372036854775808") - 1' + ;; + + *) + echo + echo "$0: error: can not detect $ngx_type size" + exit 1 +esac diff --git a/auto/types/typedef b/auto/types/typedef new file mode 100644 --- /dev/null +++ b/auto/types/typedef @@ -0,0 +1,60 @@ + +# Copyright (C) Igor Sysoev + + +echo $ngx_n "checking for $ngx_type ..." $ngx_c +echo >> $NGX_ERR +echo "checking for $ngx_type" >> $NGX_ERR + +found=no + +for type in $ngx_type $ngx_types +do + + cat << END > $NGX_AUTOTEST.c + +#include +#include +#include +#include +#include +#include +$NGX_INTTYPES_H + +int main() { + $type i = 0; + return 0; +} + +END + + eval "$CC -o $NGX_AUTOTEST $NGX_AUTOTEST.c >> $NGX_ERR 2>&1" + + if [ -x $NGX_AUTOTEST ]; then + if [ $type = $ngx_type ]; then + echo " found" + found=yes + else + echo ", $type used" + found=$type + fi + fi + + rm $NGX_AUTOTEST* + + if [ $found = no ]; then + echo $ngx_n " $type not found" $ngx_c + else + break + fi +done + +if [ $found = no ]; then + echo + echo "$0: error: can not define $ngx_type" + exit 1 +fi + +if [ $found != yes ]; then + echo "typedef $found $ngx_type;" >> $NGX_AUTO_CONFIG_H +fi diff --git a/auto/types/uintptr_t b/auto/types/uintptr_t new file mode 100644 --- /dev/null +++ b/auto/types/uintptr_t @@ -0,0 +1,40 @@ + +# Copyright (C) Igor Sysoev + + +echo $ngx_n "checking for uintptr_t ... " $ngx_c +echo >> $NGX_ERR +echo "checking for uintptr_t" >> $NGX_ERR + +found=no + +cat << END > $NGX_AUTOTEST.c + +#include +$NGX_INTTYPES_H + +int main() { + uintptr_t i = 0; + return 0; +} + +END + +eval "$CC -o $NGX_AUTOTEST $NGX_AUTOTEST.c >> $NGX_ERR 2>&1" + +if [ -x $NGX_AUTOTEST ]; then + echo " uintptr_t found" + found=yes +else + echo $ngx_n " uintptr_t not found" $ngx_c +fi + +rm $NGX_AUTOTEST* + + +if [ $found = no ]; then + found="uint`expr 8 \* $ngx_ptr_size`_t" + echo ", $found used" + + echo "typedef $found uintptr_t;" >> $NGX_AUTO_CONFIG_H +fi diff --git a/auto/types/value b/auto/types/value new file mode 100644 --- /dev/null +++ b/auto/types/value @@ -0,0 +1,9 @@ + +# Copyright (C) Igor Sysoev + + +cat << END >> $NGX_AUTO_CONFIG_H + +#ifndef $ngx_param +#define $ngx_param $ngx_value +#endif diff --git a/auto/unix b/auto/unix new file mode 100755 --- /dev/null +++ b/auto/unix @@ -0,0 +1,199 @@ + +# Copyright (C) Igor Sysoev + + +CC_WARN=$CC +ngx_fmt_collect=yes + +# C types + +ngx_type="int"; . auto/types/sizeof +ngx_formats="%d"; . auto/fmt/fmt + +ngx_type="long"; . auto/types/sizeof +ngx_formats="%ld"; . auto/fmt/fmt + +ngx_type="long long"; . auto/types/sizeof +ngx_formats="%lld %qd"; . auto/fmt/fmt + +ngx_type="void *"; . auto/types/sizeof; ngx_ptr_size=$ngx_size +ngx_fmt_name=PTR_FMT; +eval ngx_formats=\${ngx_${ngx_ptr_size}_fmt}; . auto/fmt/ptrfmt + + +# POSIX types + +NGX_AUTO_CONFIG="#include \"../$NGX_AUTO_CONFIG_H\"" + +ngx_type="uint64_t"; ngx_types="u_int64_t"; . auto/types/typedef + +ngx_type="sig_atomic_t"; ngx_types="int"; . auto/types/typedef +. auto/types/sizeof +ngx_param=SIG_ATOMIC_T_SIZE; ngx_value=$ngx_size; . auto/types/value + +ngx_type="socklen_t"; ngx_types="uint32_t"; . auto/types/typedef + +ngx_type="in_addr_t"; ngx_types="uint32_t"; . auto/types/typedef + +ngx_type="in_port_t"; ngx_types="u_short"; . auto/types/typedef + +ngx_type="rlim_t"; ngx_types="int"; . auto/types/typedef + +. auto/types/uintptr_t + +. auto/endianess + + +# printf() formats + +CC_WARN=$CC_STRONG +ngx_fmt_collect=no + +ngx_fmt_name=OFF_T_FMT; ngx_type="off_t"; . auto/types/sizeof +ngx_param=OFF_T_MAX_VALUE; ngx_value=$ngx_max_value; . auto/types/value +eval ngx_formats=\${ngx_${ngx_size}_fmt}; . auto/fmt/fmt + +ngx_fmt_name=TIME_T_FMT; ngx_type="time_t"; . auto/types/sizeof +ngx_param=TIME_T_SIZE; ngx_value=$ngx_size; . auto/types/value +ngx_param=TIME_T_LEN; ngx_value=$ngx_max_len; . auto/types/value +eval ngx_formats=\${ngx_${ngx_size}_fmt}; . auto/fmt/fmt + +ngx_fmt_name=SIZE_T_FMT; ngx_type="size_t"; . auto/types/sizeof +eval ngx_formats=\${ngx_${ngx_size}_fmt}; . auto/fmt/fmt + +ngx_fmt_name=SIZE_T_X_FMT; . auto/fmt/xfmt + +ngx_fmt_name=PID_T_FMT; ngx_type="pid_t"; . auto/types/sizeof +eval ngx_formats=\${ngx_${ngx_size}_fmt}; . auto/fmt/fmt + +ngx_fmt_name=RLIM_T_FMT; ngx_type="rlim_t"; . auto/types/sizeof +eval ngx_formats=\${ngx_${ngx_size}_fmt}; . auto/fmt/fmt + + +# syscalls, libc calls and some features + +ngx_feature_libs= +ngx_func_libs= + + +ngx_func="pread()" +ngx_func_inc= +ngx_func_test="char buf[1]; ssize_t n; n = pread(0, buf, 1, 0)" +. auto/func + + +ngx_func="pwrite()" +ngx_func_inc= +ngx_func_test="char buf[1]; ssize_t n; n = pwrite(1, buf, 1, 0)" +. auto/func + + +#ngx_func="strsignal()" +#ngx_func_inc="#include " +#ngx_func_test="char *s = strsignal(1)" +#. auto/func + + +ngx_func="strerror_r()" +ngx_func_inc="#include " +ngx_func_test="char buf[20]; int n; n = strerror_r(1, buf, 20)" +. auto/func + + +ngx_func="gnu_strerror_r()" +ngx_func_inc="#include " +ngx_func_test="char buf[20], *str; str = strerror_r(1, buf, 20)" +. auto/func + + +ngx_func="localtime_r()" +ngx_func_inc="#include " +ngx_func_test="struct tm t; time_t c=0; localtime_r(&c, &t)" +. auto/func + + +ngx_func="posix_memalign()" +ngx_func_inc="#include " +ngx_func_test="void *p; int n; n = posix_memalign(&p, 4096, 4096)" +. auto/func + + +ngx_func="memalign()" +ngx_func_inc="#include " +ngx_func_test="void *p; p = memalign(4096, 4096)" +. auto/func + + + +ngx_feature="mmap(MAP_ANON|MAP_SHARED)" +ngx_feature_name="MAP_ANON" +ngx_feature_inc="#include " +ngx_feature_test="void *p; + p = mmap(NULL, 4096, PROT_READ|PROT_WRITE, + MAP_ANON|MAP_SHARED, -1, 0); + if (p == MAP_FAILED) return 1;" +ngx_feature_run=yes +. auto/feature + + +ngx_feature='mmap("/dev/zero", MAP_SHARED)' +ngx_feature_name="MAP_DEVZERO" +ngx_feature_inc="#include +#include +#include " +ngx_feature_test='void *p; int fd; + fd = open("/dev/zero", O_RDWR); + p = mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); + if (p == MAP_FAILED) return 1;' +. auto/feature + + +ngx_feature="System V shared memory" +ngx_feature_name="SYSVSHM" +ngx_feature_inc="#include +#include " +ngx_feature_test="int id; + id = shmget(IPC_PRIVATE, 4096, (SHM_R|SHM_W|IPC_CREAT)); + if (id == -1) return 1; + shmctl(id, IPC_RMID, NULL);" +. auto/feature + + + +ngx_feature="struct sockaddr_in.sin_len" +ngx_feature_name="sin_len" +ngx_feature_inc="#include +#include " +ngx_feature_test="struct sockaddr_in sa; sa.sin_len = 5" +ngx_feature_run=no +. auto/feature + + +ngx_feature="struct msghdr.msg_control" +ngx_feature_name="msghdr_msg_control" +ngx_feature_inc="#include " +ngx_feature_test="struct msghdr msg; msg.msg_control = NULL" +. auto/feature + + +case $PLATFORM in + Linux:*) + ngx_feature_inc="#include " + ;; + + *) + ngx_feature_inc="#include " + ;; +esac + +ngx_feature="ioctl(FIONBIO)" +ngx_feature_name="FIONBIO" +ngx_feature_test="int i; i = FIONBIO" +. auto/feature + + +ngx_feature="struct tm.tm_gmtoff" +ngx_feature_name="gmtoff" +ngx_feature_inc="#include " +ngx_feature_test="struct tm tm; tm.tm_gmtoff = 0" +. auto/feature diff --git a/conf/koi-win b/conf/koi-win new file mode 100644 --- /dev/null +++ b/conf/koi-win @@ -0,0 +1,85 @@ + +charset_map koi8-r windows-1251 { + + 95 95 ; # bullet + + 9A A0 ; #   + + 9C B0 ; # ° + + 9E B7 ; # · + + A3 B8 ; # small yo + + B3 A8 ; # capital YO + + 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 --git a/conf/mime.types b/conf/mime.types new file mode 100644 --- /dev/null +++ b/conf/mime.types @@ -0,0 +1,24 @@ + +types { + text/html html htm shtml; + text/xml xml rss; + text/css css; + text/plain txt; + + image/gif gif; + image/png png; + image/jpeg jpeg jpg; + image/x-icon ico; + + application/pdf pdf; + application/x-shockwave-flash swf; + application/x-javascript js; + + audio/mpeg mp3; + audio/x-realaudio ra; + + video/mpeg mpeg mpg; + video/quicktime mov; + video/x-msvideo avi; + video/x-ms-wmv wmv; +} diff --git a/conf/nginx.conf b/conf/nginx.conf new file mode 100644 --- /dev/null +++ b/conf/nginx.conf @@ -0,0 +1,37 @@ + +user nobody; +worker_processes 3; + +#error_log logs/error.log; +#pid logs/nginx.pid; + + +events { + connections 1024; +} + + +http { + include conf/mime.types; + default_type application/octet-stream; + + sendfile on; + + #gzip on; + + server { + listen 80; + + charset on; + source_charset koi8-r; + + #access_log logs/access.log; + + location / { + root html; + index index.html index.htm; + } + + } + +} diff --git a/configure b/configure new file mode 100755 --- /dev/null +++ b/configure @@ -0,0 +1,54 @@ +#!/bin/sh + +# Copyright (C) Igor Sysoev + + +. auto/options +. auto/init +. auto/sources + +test -d $OBJS || mkdir $OBJS +echo > $NGX_AUTO_CONFIG_H + +if [ $DEBUG = YES ]; then + have=NGX_DEBUG . auto/have +fi + +have=NGX_USE_HTTP_FILE_CACHE_UNIQ . auto/have +have=NGX_SUPPRESS_WARN . auto/have + + +if [ "$PLATFORM" != win32 ]; then + . auto/headers +fi + +. auto/os/conf +. auto/modules + +. auto/cc +. auto/lib/conf + +if [ "$PLATFORM" != win32 ]; then + . auto/threads +fi + +. auto/make +. auto/lib/make +. auto/install + +if [ "$PLATFORM" != win32 ]; then + . auto/unix +fi + +have=NGX_SMP . auto/have + +have=NGX_PREFIX value="\"$PREFIX/\"" . auto/define +have=NGX_SBIN_PATH value="\"$SBIN_PATH\"" . auto/define +have=NGX_CONF_PATH value="\"$CONF_PATH\"" . auto/define +have=NGX_PID_PATH value="\"$PID_PATH\"" . auto/define +if [ ".$ERROR_LOG_PATH" != "." ]; then + have=NGX_ERROR_LOG_PATH value="\"$ERROR_LOG_PATH\"" . auto/define +fi +have=NGX_HTTP_LOG_PATH value="\"$HTTP_LOG_PATH\"" . auto/define + +. auto/summary diff --git a/html/index.html b/html/index.html new file mode 100644 --- /dev/null +++ b/html/index.html @@ -0,0 +1,8 @@ + + +Welcome to nginx! + + +

Welcome to nginx!

+ + diff --git a/src/core/nginx.c b/src/core/nginx.c new file mode 100644 --- /dev/null +++ b/src/core/nginx.c @@ -0,0 +1,503 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include +#include + + +static ngx_int_t ngx_add_inherited_sockets(ngx_cycle_t *cycle); +static ngx_int_t ngx_getopt(ngx_master_ctx_t *ctx, ngx_cycle_t *cycle); +static void *ngx_core_module_create_conf(ngx_cycle_t *cycle); +static char *ngx_core_module_init_conf(ngx_cycle_t *cycle, void *conf); +static char *ngx_set_user(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); + + +static ngx_command_t ngx_core_commands[] = { + + { ngx_string("daemon"), + NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1, + ngx_conf_set_flag_slot, + 0, + offsetof(ngx_core_conf_t, daemon), + NULL }, + + { ngx_string("master_process"), + NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1, + ngx_conf_set_flag_slot, + 0, + offsetof(ngx_core_conf_t, master), + NULL }, + + { ngx_string("worker_processes"), + NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1, + ngx_conf_set_num_slot, + 0, + offsetof(ngx_core_conf_t, worker_processes), + NULL }, + +#if (NGX_THREADS) + + { ngx_string("worker_threads"), + NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1, + ngx_conf_set_num_slot, + 0, + offsetof(ngx_core_conf_t, worker_threads), + NULL }, + + { ngx_string("thread_stack_size"), + NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1, + ngx_conf_set_size_slot, + 0, + offsetof(ngx_core_conf_t, thread_stack_size), + NULL }, + +#endif + + { ngx_string("user"), + NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE12, + ngx_set_user, + 0, + 0, + NULL }, + + { ngx_string("pid"), + NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1, + ngx_conf_set_str_slot, + 0, + offsetof(ngx_core_conf_t, pid), + NULL }, + + ngx_null_command +}; + + +static ngx_core_module_t ngx_core_module_ctx = { + ngx_string("core"), + ngx_core_module_create_conf, + ngx_core_module_init_conf +}; + + +ngx_module_t ngx_core_module = { + NGX_MODULE, + &ngx_core_module_ctx, /* module context */ + ngx_core_commands, /* module directives */ + NGX_CORE_MODULE, /* module type */ + NULL, /* init module */ + NULL /* init process */ +}; + + +ngx_uint_t ngx_max_module; + + +int main(int argc, char *const *argv) +{ + ngx_int_t i; + ngx_log_t *log; + ngx_cycle_t *cycle, init_cycle; + ngx_core_conf_t *ccf; + ngx_master_ctx_t ctx; + +#if defined __FreeBSD__ + ngx_debug_init(); +#endif + + /* TODO */ ngx_max_sockets = -1; + + ngx_time_init(); + +#if (HAVE_PCRE) + ngx_regex_init(); +#endif + + ngx_pid = ngx_getpid(); + + if (!(log = ngx_log_init_stderr())) { + return 1; + } + +#if (NGX_OPENSSL) + ngx_ssl_init(log); +#endif + + /* init_cycle->log is required for signal handlers and ngx_getopt() */ + + ngx_memzero(&init_cycle, sizeof(ngx_cycle_t)); + init_cycle.log = log; + ngx_cycle = &init_cycle; + + ngx_memzero(&ctx, sizeof(ngx_master_ctx_t)); + ctx.argc = argc; + ctx.argv = argv; + + if (!(init_cycle.pool = ngx_create_pool(1024, log))) { + return 1; + } + + if (ngx_getopt(&ctx, &init_cycle) == NGX_ERROR) { + return 1; + } + + if (ngx_test_config) { + log->log_level = NGX_LOG_INFO; + } + + if (ngx_os_init(log) == NGX_ERROR) { + return 1; + } + + if (ngx_add_inherited_sockets(&init_cycle) == NGX_ERROR) { + return 1; + } + + ngx_max_module = 0; + for (i = 0; ngx_modules[i]; i++) { + ngx_modules[i]->index = ngx_max_module++; + } + + 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); + } + + return 1; + } + + if (ngx_test_config) { + ngx_log_error(NGX_LOG_INFO, log, 0, + "the configuration file %s was tested successfully", + init_cycle.conf_file.data); + return 0; + } + + 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 (WIN32) + +#if 0 + + TODO: + + if (ccf->run_as_service) { + if (ngx_service(cycle->log) == NGX_ERROR) { + return 1; + } + + return 0; + } +#endif + +#else + + if (!ngx_inherited && ccf->daemon) { + if (ngx_daemon(cycle->log) == NGX_ERROR) { + return 1; + } + + ngx_daemonized = 1; + } + + if (ngx_create_pidfile(cycle, NULL) == NGX_ERROR) { + return 1; + } + +#endif + + if (ngx_process == NGX_PROCESS_MASTER) { + ngx_master_process_cycle(cycle, &ctx); + + } else { + ngx_single_process_cycle(cycle, &ctx); + } + + return 0; +} + + +static ngx_int_t ngx_add_inherited_sockets(ngx_cycle_t *cycle) +{ + u_char *p, *v, *inherited; + ngx_socket_t s; + ngx_listening_t *ls; + + inherited = (u_char *) getenv(NGINX_VAR); + + if (inherited == NULL) { + return NGX_OK; + } + + ngx_log_error(NGX_LOG_INFO, cycle->log, 0, + "using inherited sockets from \"%s\"", inherited); + + ngx_init_array(cycle->listening, cycle->pool, + 10, sizeof(ngx_listening_t), NGX_ERROR); + + for (p = inherited, v = p; *p; p++) { + if (*p == ':' || *p == ';') { + s = ngx_atoi(v, p - v); + if (s == NGX_ERROR) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, + "invalid socket number \"%s\" in " + NGINX_VAR " enviroment variable, " + "ignoring the rest of the variable", v); + break; + } + + v = p + 1; + + if (!(ls = ngx_push_array(&cycle->listening))) { + return NGX_ERROR; + } + + ls->fd = s; + } + } + + ngx_inherited = 1; + + return ngx_set_inherited_sockets(cycle); +} + + +ngx_pid_t ngx_exec_new_binary(ngx_cycle_t *cycle, char *const *argv) +{ + char *env[2], *var, *p; + ngx_uint_t i; + ngx_pid_t pid; + ngx_exec_ctx_t ctx; + ngx_listening_t *ls; + + ctx.path = argv[0]; + ctx.name = "new binary process"; + ctx.argv = argv; + + var = ngx_alloc(sizeof(NGINX_VAR) + + cycle->listening.nelts * (NGX_INT32_LEN + 1) + 2, + cycle->log); + + p = (char *) ngx_cpymem(var, NGINX_VAR "=", sizeof(NGINX_VAR)); + + ls = cycle->listening.elts; + for (i = 0; i < cycle->listening.nelts; i++) { + p += ngx_snprintf(p, NGX_INT32_LEN + 2, "%u;", ls[i].fd); + } + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, cycle->log, 0, "inherited: %s", var); + + env[0] = var; + env[1] = NULL; + ctx.envp = (char *const *) &env; + + pid = ngx_execute(cycle, &ctx); + + ngx_free(var); + + return pid; +} + + +static ngx_int_t ngx_getopt(ngx_master_ctx_t *ctx, ngx_cycle_t *cycle) +{ + ngx_int_t i; + + for (i = 1; i < ctx->argc; i++) { + if (ctx->argv[i][0] != '-') { + ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, + "invalid option: \"%s\"", ctx->argv[i]); + return NGX_ERROR; + } + + switch (ctx->argv[i][1]) { + + case 't': + ngx_test_config = 1; + break; + + case 'c': + if (ctx->argv[i + 1] == NULL) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, + "the option: \"%s\" requires file name", + ctx->argv[i]); + return NGX_ERROR; + } + + cycle->conf_file.data = (u_char *) ctx->argv[++i]; + cycle->conf_file.len = ngx_strlen(cycle->conf_file.data); + break; + + default: + ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, + "invalid option: \"%s\"", ctx->argv[i]); + 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; + } + + if (ngx_conf_full_name(cycle, &cycle->conf_file) == NGX_ERROR) { + return NGX_ERROR; + } + + return NGX_OK; +} + + +static void *ngx_core_module_create_conf(ngx_cycle_t *cycle) +{ + ngx_core_conf_t *ccf; + + if (!(ccf = ngx_pcalloc(cycle->pool, sizeof(ngx_core_conf_t)))) { + return NULL; + } + /* set by pcalloc() + * + * ccf->pid = NULL; + * ccf->newpid = NULL; + */ + ccf->daemon = NGX_CONF_UNSET; + ccf->master = NGX_CONF_UNSET; + ccf->worker_processes = NGX_CONF_UNSET; +#if (NGX_THREADS) + ccf->worker_threads = NGX_CONF_UNSET; + ccf->thread_stack_size = NGX_CONF_UNSET; +#endif + ccf->user = (ngx_uid_t) NGX_CONF_UNSET; + ccf->group = (ngx_gid_t) NGX_CONF_UNSET; + + return ccf; +} + + +static char *ngx_core_module_init_conf(ngx_cycle_t *cycle, void *conf) +{ + ngx_core_conf_t *ccf = conf; + +#if !(WIN32) + struct passwd *pwd; + struct group *grp; +#endif + + ngx_conf_init_value(ccf->daemon, 1); + ngx_conf_init_value(ccf->master, 1); + ngx_conf_init_value(ccf->worker_processes, 1); + +#if (NGX_THREADS) + ngx_conf_init_value(ccf->worker_threads, 0); + ngx_threads_n = ccf->worker_threads; + ngx_conf_init_size_value(ccf->thread_stack_size, 2 * 1024 * 1024); +#endif + +#if !(WIN32) + + if (ccf->user == (uid_t) NGX_CONF_UNSET) { + + pwd = getpwnam("nobody"); + if (pwd == NULL) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, + "getpwnam(\"nobody\") failed"); + return NGX_CONF_ERROR; + } + + ccf->user = pwd->pw_uid; + + grp = getgrnam("nobody"); + if (grp == NULL) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, + "getgrnam(\"nobody\") failed"); + return NGX_CONF_ERROR; + } + + ccf->group = grp->gr_gid; + } + + if (ccf->pid.len == 0) { + ccf->pid.len = sizeof(NGX_PID_PATH) - 1; + ccf->pid.data = NGX_PID_PATH; + } + + if (ngx_conf_full_name(cycle, &ccf->pid) == NGX_ERROR) { + return NGX_CONF_ERROR; + } + + ccf->newpid.len = ccf->pid.len + sizeof(NGX_NEWPID_EXT); + + if (!(ccf->newpid.data = ngx_palloc(cycle->pool, ccf->newpid.len))) { + return NGX_CONF_ERROR; + } + + ngx_memcpy(ngx_cpymem(ccf->newpid.data, ccf->pid.data, ccf->pid.len), + NGX_NEWPID_EXT, sizeof(NGX_NEWPID_EXT)); + +#endif + + return NGX_CONF_OK; +} + + +static char *ngx_set_user(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ +#if (WIN32) + + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "\"user\" is not supported, ignored"); + + return NGX_CONF_OK; + +#else + + ngx_core_conf_t *ccf = conf; + + struct passwd *pwd; + struct group *grp; + ngx_str_t *value; + + if (ccf->user != (uid_t) NGX_CONF_UNSET) { + return "is duplicate"; + } + + value = (ngx_str_t *) cf->args->elts; + + pwd = getpwnam((const char *) value[1].data); + if (pwd == NULL) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno, + "getpwnam(%s) failed", value[1].data); + return NGX_CONF_ERROR; + } + + ccf->user = pwd->pw_uid; + + if (cf->args->nelts == 2) { + return NGX_CONF_OK; + } + + grp = getgrnam((const char *) value[2].data); + if (grp == NULL) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno, + "getgrnam(%s) failed", value[1].data); + return NGX_CONF_ERROR; + } + + ccf->group = grp->gr_gid; + + return NGX_CONF_OK; + +#endif +} diff --git a/src/core/nginx.h b/src/core/nginx.h new file mode 100644 --- /dev/null +++ b/src/core/nginx.h @@ -0,0 +1,17 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#ifndef _NGINX_H_INCLUDED_ +#define _NGINX_H_INCLUDED_ + + +#define NGINX_VER "nginx/0.1.0" + +#define NGINX_VAR "NGINX" +#define NGX_NEWPID_EXT ".newbin" + + +#endif /* _NGINX_H_INCLUDED_ */ diff --git a/src/core/ngx_array.c b/src/core/ngx_array.c new file mode 100644 --- /dev/null +++ b/src/core/ngx_array.c @@ -0,0 +1,74 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include + + +ngx_array_t *ngx_create_array(ngx_pool_t *p, ngx_uint_t n, size_t size) +{ + ngx_array_t *a; + + ngx_test_null(a, ngx_palloc(p, sizeof(ngx_array_t)), NULL); + + ngx_test_null(a->elts, ngx_palloc(p, n * size), NULL); + + a->pool = p; + a->nelts = 0; + a->nalloc = n; + a->size = size; + + return a; +} + + +void ngx_destroy_array(ngx_array_t *a) +{ + ngx_pool_t *p; + + p = a->pool; + + if ((char *) a->elts + a->size * a->nalloc == p->last) { + p->last -= a->size * a->nalloc; + } + + if ((char *) a + sizeof(ngx_array_t) == p->last) { + p->last = (char *) a; + } +} + + +void *ngx_push_array(ngx_array_t *a) +{ + void *elt, *new; + ngx_pool_t *p; + + /* array is full */ + if (a->nelts == a->nalloc) { + p = a->pool; + + /* array allocation is the last in the pool */ + if ((char *) a->elts + a->size * a->nelts == p->last + && (unsigned) (p->end - p->last) >= a->size) + { + p->last += a->size; + a->nalloc++; + + /* allocate new array */ + } else { + ngx_test_null(new, ngx_palloc(p, 2 * a->nalloc * a->size), NULL); + + ngx_memcpy(new, a->elts, a->nalloc * a->size); + a->elts = new; + a->nalloc *= 2; + } + } + + elt = (char *) a->elts + a->size * a->nelts; + a->nelts++; + + return elt; +} diff --git a/src/core/ngx_array.h b/src/core/ngx_array.h new file mode 100644 --- /dev/null +++ b/src/core/ngx_array.h @@ -0,0 +1,54 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#ifndef _NGX_ARRAY_H_INCLUDED_ +#define _NGX_ARRAY_H_INCLUDED_ + + +#include +#include + + +struct ngx_array_s { + void *elts; + ngx_uint_t nelts; + size_t size; + ngx_uint_t nalloc; + ngx_pool_t *pool; +}; + + +ngx_array_t *ngx_create_array(ngx_pool_t *p, ngx_uint_t n, size_t size); +void ngx_destroy_array(ngx_array_t *a); +void *ngx_push_array(ngx_array_t *a); + + +ngx_inline static ngx_int_t ngx_array_init(ngx_array_t *array, ngx_pool_t *pool, + ngx_uint_t n, size_t size) +{ + if (!(array->elts = ngx_palloc(pool, n * size))) { + return NGX_ERROR; + } + + array->nelts = 0; + array->size = size; + array->nalloc = n; + array->pool = pool; + + return NGX_OK; +} + + + +#define ngx_init_array(a, p, n, s, rc) \ + ngx_test_null(a.elts, ngx_palloc(p, n * s), rc); \ + a.nelts = 0; a.size = s; a.nalloc = n; a.pool = p; + +#define ngx_array_create ngx_create_array +#define ngx_array_push ngx_push_array + + +#endif /* _NGX_ARRAY_H_INCLUDED_ */ diff --git a/src/core/ngx_buf.c b/src/core/ngx_buf.c new file mode 100644 --- /dev/null +++ b/src/core/ngx_buf.c @@ -0,0 +1,162 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include + + +ngx_buf_t *ngx_create_temp_buf(ngx_pool_t *pool, size_t size) +{ + ngx_buf_t *b; + + if (!(b = ngx_calloc_buf(pool))) { + return NULL; + } + + if (!(b->start = ngx_palloc(pool, size))) { + return NULL; + } + + b->pos = b->start; + b->last = b->start; + b->end = b->last + size; + b->temporary = 1; + + /* + + b->file_pos = 0; + b->file_last = 0; + + b->file = NULL; + b->shadow = NULL; + + b->tag = 0; + + */ + + return b; +} + + +ngx_chain_t *ngx_create_chain_of_bufs(ngx_pool_t *pool, ngx_bufs_t *bufs) +{ + u_char *p; + ngx_int_t i; + ngx_buf_t *b; + ngx_chain_t *chain, *cl, **ll; + + if (!(p = ngx_palloc(pool, bufs->num * bufs->size))) { + return NULL; + } + + ll = &chain; + + for (i = 0; i < bufs->num; i++) { + if (!(b = ngx_calloc_buf(pool))) { + return NULL; + } + + b->pos = p; + b->last = p; + b->temporary = 1; + + b->start = p; + p += bufs->size; + b->end = p; + + /* + b->file_pos = 0; + b->file_last = 0; + + b->file = NULL; + b->shadow = NULL; + b->tag = 0; + */ + + if (!(cl = ngx_alloc_chain_link(pool))) { + return NULL; + } + + cl->buf = b; + *ll = cl; + ll = &cl->next; + } + + *ll = NULL; + + return chain; +} + + +ngx_int_t ngx_chain_add_copy(ngx_pool_t *pool, ngx_chain_t **chain, + ngx_chain_t *in) +{ + ngx_chain_t *cl, **ll; + + ll = chain; + + for (cl = *chain; cl; cl = cl->next) { + ll = &cl->next; + } + + while (in) { + ngx_test_null(cl, ngx_alloc_chain_link(pool), NGX_ERROR); + + cl->buf = in->buf; + *ll = cl; + ll = &cl->next; + in = in->next; + } + + *ll = NULL; + + return NGX_OK; +} + + +void ngx_chain_update_chains(ngx_chain_t **free, ngx_chain_t **busy, + ngx_chain_t **out, ngx_buf_tag_t tag) +{ + ngx_chain_t *tl; + + if (*busy == NULL) { + *busy = *out; + + } else { + for (tl = *busy; /* void */ ; tl = tl->next) { + if (tl->next == NULL) { + tl->next = *out; + break; + } + } + } + + *out = NULL; + + while (*busy) { + if (ngx_buf_size((*busy)->buf) != 0) { + break; + } + +#if (HAVE_WRITE_ZEROCOPY) + if ((*busy)->buf->zerocopy_busy) { + break; + } +#endif + + if ((*busy)->buf->tag != tag) { + *busy = (*busy)->next; + continue; + } + + (*busy)->buf->pos = (*busy)->buf->last = (*busy)->buf->start; + + tl = *busy; + *busy = (*busy)->next; + tl->next = *free; + *free = tl; + } +} diff --git a/src/core/ngx_buf.h b/src/core/ngx_buf.h new file mode 100644 --- /dev/null +++ b/src/core/ngx_buf.h @@ -0,0 +1,153 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#ifndef _NGX_BUF_H_INCLUDED_ +#define _NGX_BUF_H_INCLUDED_ + + +#include +#include + + +typedef void * ngx_buf_tag_t; + +typedef struct ngx_buf_s ngx_buf_t; + +struct ngx_buf_s { + u_char *pos; + u_char *last; + off_t file_pos; + off_t file_last; + + int type; + u_char *start; /* start of buffer */ + u_char *end; /* end of buffer */ + ngx_buf_tag_t tag; + ngx_file_t *file; + ngx_buf_t *shadow; + + + /* the buf's content could be changed */ + unsigned temporary:1; + + /* + * the buf's content is in a memory cache or in a read only memory + * and must not be changed + */ + unsigned memory:1; + + /* the buf's content is mmap()ed and must not be changed */ + unsigned mmap:1; + + unsigned recycled:1; + unsigned in_file:1; + unsigned flush:1; + unsigned last_buf:1; + + unsigned last_shadow:1; + unsigned temp_file:1; + + unsigned zerocopy_busy:1; + + /* STUB */ int num; +}; + + +typedef struct ngx_chain_s ngx_chain_t; + +struct ngx_chain_s { + ngx_buf_t *buf; + ngx_chain_t *next; +}; + + +typedef struct { + ngx_int_t num; + size_t size; +} ngx_bufs_t; + + +typedef int (*ngx_output_chain_filter_pt)(void *ctx, ngx_chain_t *out); + +typedef struct { + 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; + + ngx_pool_t *pool; + ngx_int_t allocated; + ngx_bufs_t bufs; + ngx_buf_tag_t tag; + + ngx_output_chain_filter_pt output_filter; + void *filter_ctx; +} ngx_output_chain_ctx_t; + + +typedef struct { + ngx_chain_t *out; + ngx_chain_t **last; + ngx_connection_t *connection; + ngx_pool_t *pool; + off_t limit; +} ngx_chain_writer_ctx_t; + + +#define NGX_CHAIN_ERROR (ngx_chain_t *) NGX_ERROR + + +#define ngx_buf_in_memory(b) (b->temporary || b->memory || b->mmap) +#define ngx_buf_in_memory_only(b) (ngx_buf_in_memory(b) && !b->in_file) +#define ngx_buf_special(b) \ + ((b->flush || b->last_buf) && !ngx_buf_in_memory(b) && !b->in_file) + +#define ngx_buf_size(b) \ + (ngx_buf_in_memory(b) ? (size_t) (b->last - b->pos): \ + (size_t) (b->file_last - b->file_pos)) + +ngx_buf_t *ngx_create_temp_buf(ngx_pool_t *pool, size_t size); +ngx_chain_t *ngx_create_chain_of_bufs(ngx_pool_t *pool, ngx_bufs_t *bufs); + + +#define ngx_alloc_buf(pool) ngx_palloc(pool, sizeof(ngx_buf_t)) +#define ngx_calloc_buf(pool) ngx_pcalloc(pool, sizeof(ngx_buf_t)) + + +#define ngx_alloc_chain_link(pool) ngx_palloc(pool, sizeof(ngx_chain_t)) + + +#define ngx_alloc_link_and_set_buf(chain, b, pool, error) \ + do { \ + ngx_test_null(chain, ngx_alloc_chain_link(pool), error); \ + chain->buf = b; \ + chain->next = NULL; \ + } while (0); + + +#define ngx_chain_add_link(chain, last, cl) \ + if (chain) { \ + *last = cl; \ + } else { \ + chain = cl; \ + } \ + last = &cl->next + + +ngx_int_t ngx_output_chain(ngx_output_chain_ctx_t *ctx, ngx_chain_t *in); +ngx_int_t ngx_chain_writer(void *data, ngx_chain_t *in); + +ngx_int_t ngx_chain_add_copy(ngx_pool_t *pool, ngx_chain_t **chain, + ngx_chain_t *in); +void ngx_chain_update_chains(ngx_chain_t **free, ngx_chain_t **busy, + ngx_chain_t **out, ngx_buf_tag_t tag); + + +#endif /* _NGX_BUF_H_INCLUDED_ */ diff --git a/src/core/ngx_conf_file.c b/src/core/ngx_conf_file.c new file mode 100644 --- /dev/null +++ b/src/core/ngx_conf_file.c @@ -0,0 +1,1012 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include + + +static char *ngx_conf_include(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); + + +static ngx_command_t ngx_conf_commands[] = { + + { ngx_string("include"), + NGX_ANY_CONF|NGX_CONF_TAKE1, + ngx_conf_include, + 0, + 0, + NULL }, + + ngx_null_command +}; + + +ngx_module_t ngx_conf_module = { + NGX_MODULE, + NULL, /* module context */ + ngx_conf_commands, /* module directives */ + NGX_CONF_MODULE, /* module type */ + NULL, /* init module */ + NULL /* init child */ +}; + + + +/* The ten fixed arguments */ + +static int argument_number[] = { + NGX_CONF_NOARGS, + NGX_CONF_TAKE1, + NGX_CONF_TAKE2, + NGX_CONF_TAKE3, + NGX_CONF_TAKE4, + NGX_CONF_TAKE5, + NGX_CONF_TAKE6, + NGX_CONF_TAKE7 +}; + +static int ngx_conf_read_token(ngx_conf_t *cf); + + +char *ngx_conf_parse(ngx_conf_t *cf, ngx_str_t *filename) +{ + int m, rc, found, valid; + char *rv; + void *conf, **confp; + ngx_fd_t fd; + ngx_str_t *name; + ngx_conf_file_t *prev; + ngx_command_t *cmd; + +#if (NGX_SUPPRESS_WARN) + fd = NGX_INVALID_FILE; + prev = NULL; +#endif + + if (filename) { + + /* open configuration file */ + + fd = ngx_open_file(filename->data, NGX_FILE_RDONLY, NGX_FILE_OPEN); + if (fd == NGX_INVALID_FILE) { + ngx_log_error(NGX_LOG_EMERG, cf->log, ngx_errno, + ngx_open_file_n " %s failed", filename->data); + return NGX_CONF_ERROR; + } + + prev = cf->conf_file; + if (!(cf->conf_file = ngx_palloc(cf->pool, sizeof(ngx_conf_file_t)))) { + return NGX_CONF_ERROR; + } + + 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); + } + + if (!(cf->conf_file->buffer = ngx_create_temp_buf(cf->pool, 1024))) { + return NGX_CONF_ERROR; + } + + 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->line = 1; + } + + for ( ;; ) { + rc = ngx_conf_read_token(cf); + + /* + * ngx_conf_read_token() returns NGX_OK, NGX_ERROR, + * NGX_CONF_FILE_DONE or NGX_CONF_BLOCK_DONE + */ + +#if 0 +ngx_log_debug(cf->log, "token %d" _ rc); +#endif + + if (rc == NGX_ERROR) { + break; + } + + if (rc != NGX_OK) { + break; + } + + if (cf->handler) { + + /* custom handler, i.e. used in http "types { ... }" directive */ + + rv = (*cf->handler)(cf, NULL, cf->handler_conf); + if (rv == NGX_CONF_OK) { + continue; + + } else if (rv == NGX_CONF_ERROR) { + rc = NGX_ERROR; + break; + + } else { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "%s in %s:%d", + rv, + cf->conf_file->file.name.data, + cf->conf_file->line); + rc = NGX_ERROR; + break; + } + } + + name = (ngx_str_t *) cf->args->elts; + found = 0; + + for (m = 0; rc != NGX_ERROR && !found && ngx_modules[m]; m++) { + + /* look up the directive in the appropriate modules */ + + if (ngx_modules[m]->type != NGX_CONF_MODULE + && ngx_modules[m]->type != cf->module_type) + { + continue; + } + + cmd = ngx_modules[m]->commands; + if (cmd == NULL) { + continue; + } + + while (cmd->name.len) { + if (name->len == cmd->name.len + && ngx_strcmp(name->data, cmd->name.data) == 0) + { + + found = 1; +#if 0 +ngx_log_debug(cf->log, "command '%s'" _ cmd->name.data); +#endif + /* is the directive's location right ? */ + + if ((cmd->type & cf->cmd_type) == 0) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "directive \"%s\" in %s:%d " + "is not allowed here", + name->data, + cf->conf_file->file.name.data, + cf->conf_file->line); + rc = NGX_ERROR; + break; + } + + /* is the directive's argument count right ? */ + + if (cmd->type & NGX_CONF_ANY) { + valid = 1; + + } else if (cmd->type & NGX_CONF_FLAG) { + + if (cf->args->nelts == 2) { + valid = 1; + } else { + valid = 0; + } + + } else if (cmd->type & NGX_CONF_1MORE) { + + if (cf->args->nelts > 1) { + valid = 1; + } else { + valid = 0; + } + + } else if (cmd->type & NGX_CONF_2MORE) { + + if (cf->args->nelts > 2) { + valid = 1; + } else { + valid = 0; + } + + } else if (cf->args->nelts <= 10 + && (cmd->type + & argument_number[cf->args->nelts - 1])) + { + valid = 1; + + } else { + valid = 0; + } + + if (!valid) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "invalid number arguments in " + "directive \"%s\" in %s:%d", + name->data, + cf->conf_file->file.name.data, + cf->conf_file->line); + rc = NGX_ERROR; + break; + } + + /* set up the directive's configuration context */ + + conf = NULL; + + if (cmd->type & NGX_DIRECT_CONF) { + conf = ((void **) cf->ctx)[ngx_modules[m]->index]; + + } else if (cmd->type & NGX_MAIN_CONF) { + conf = &(((void **) cf->ctx)[ngx_modules[m]->index]); + + } else if (cf->ctx) { + confp = *(void **) ((char *) cf->ctx + cmd->conf); + + if (confp) { + conf = confp[ngx_modules[m]->ctx_index]; + } + } + + rv = cmd->set(cf, cmd, conf); + +#if 0 +ngx_log_debug(cf->log, "rv: %d" _ rv); +#endif + + if (rv == NGX_CONF_OK) { + break; + + } else if (rv == NGX_CONF_ERROR) { + rc = NGX_ERROR; + break; + + } else { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "the \"%s\" directive %s in %s:%d", + name->data, rv, + cf->conf_file->file.name.data, + cf->conf_file->line); + + rc = NGX_ERROR; + break; + } + } + + cmd++; + } + } + + if (!found) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "unknown directive \"%s\" in %s:%d", + name->data, + cf->conf_file->file.name.data, + cf->conf_file->line); + + rc = NGX_ERROR; + break; + } + + if (rc == NGX_ERROR) { + break; + } + } + + if (filename) { + cf->conf_file = prev; + + 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); + return NGX_CONF_ERROR; + } + } + + if (rc == NGX_ERROR) { + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; +} + + +static int ngx_conf_read_token(ngx_conf_t *cf) +{ + u_char *start, ch, *src, *dst; + int len; + int found, need_space, last_space, sharp_comment; + int quoted, s_quoted, d_quoted; + ssize_t n; + ngx_str_t *word; + ngx_buf_t *b; + + found = 0; + need_space = 0; + last_space = 1; + sharp_comment = 0; + quoted = s_quoted = d_quoted = 0; + + cf->args->nelts = 0; + b = cf->conf_file->buffer; + start = b->pos; + +#if 0 +ngx_log_debug(cf->log, "TOKEN START"); +#endif + + for ( ;; ) { + + if (b->pos >= b->last) { + if (cf->conf_file->file.offset + >= ngx_file_size(&cf->conf_file->file.info)) { + return NGX_CONF_FILE_DONE; + } + + if (b->pos - start) { + ngx_memcpy(b->start, start, b->pos - start); + } + + n = ngx_read_file(&cf->conf_file->file, + b->start + (b->pos - start), + b->end - (b->start + (b->pos - start)), + cf->conf_file->file.offset); + + if (n == NGX_ERROR) { + return NGX_ERROR; + } + + b->pos = b->start + (b->pos - start); + start = b->start; + b->last = b->pos + n; + } + + ch = *b->pos++; + +#if 0 +ngx_log_debug(cf->log, "%d:%d:%d:%d:%d '%c'" _ + last_space _ need_space _ + quoted _ s_quoted _ d_quoted _ ch); +#endif + + if (ch == LF) { + cf->conf_file->line++; + + if (sharp_comment) { + sharp_comment = 0; + } + } + + if (sharp_comment) { + continue; + } + + if (quoted) { + quoted = 0; + continue; + } + + if (need_space) { + if (ch == ' ' || ch == '\t' || ch == CR || ch == LF) { + last_space = 1; + need_space = 0; + continue; + } + + if (ch == ';' || ch == '{') { + return NGX_OK; + } + + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "unexpected '%c' in %s:%d", + ch, cf->conf_file->file.name.data, + cf->conf_file->line); + + return NGX_ERROR; + } + + if (last_space) { + if (ch == ' ' || ch == '\t' || ch == CR || ch == LF) { + continue; + } + + start = b->pos - 1; + + switch (ch) { + + case ';': + case '{': + if (cf->args->nelts == 0) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "unexpected '%c' in %s:%d", + ch, cf->conf_file->file.name.data, + cf->conf_file->line); + return NGX_ERROR; + } + + return NGX_OK; + + case '}': + if (cf->args->nelts > 0) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "unexpected '}' in %s:%d", + cf->conf_file->file.name.data, + cf->conf_file->line); + return NGX_ERROR; + } + + return NGX_CONF_BLOCK_DONE; + + case '#': + sharp_comment = 1; + continue; + + case '\\': + quoted = 1; + last_space = 0; + continue; + + case '"': + start++; + d_quoted = 1; + last_space = 0; + continue; + + case '\'': + start++; + s_quoted = 1; + last_space = 0; + continue; + + default: + last_space = 0; + } + + } else { + if (ch == '\\') { + quoted = 1; + continue; + } + + if (d_quoted) { + if (ch == '"') { + d_quoted = 0; + need_space = 1; + found = 1; + } + + } else if (s_quoted) { + if (ch == '\'') { + s_quoted = 0; + need_space = 1; + found = 1; + } + + } else if (ch == ' ' || ch == '\t' || ch == CR || ch == LF + || ch == ';' || ch == '{') { + last_space = 1; + found = 1; + } + + if (found) { + if (!(word = ngx_push_array(cf->args))) { + return NGX_ERROR; + } + + if (!(word->data = ngx_palloc(cf->pool, b->pos - start + 1))) { + return NGX_ERROR; + } + + for (dst = word->data, src = start, len = 0; + src < b->pos - 1; + len++) + { + if (*src == '\\') { + switch (src[1]) { + case '"': + case '\'': + case '\\': + src++; + break; + + case 't': + *dst++ = '\t'; + src += 2; + continue; + + case 'r': + *dst++ = '\r'; + src += 2; + continue; + + case 'n': + *dst++ = '\n'; + src += 2; + continue; + } + + } + *dst++ = *src++; + } + *dst = '\0'; + word->len = len; + +#if 0 +ngx_log_debug(cf->log, "FOUND %d:'%s'" _ word->len _ word->data); +#endif + + if (ch == ';' || ch == '{') { + return NGX_OK; + } + + found = 0; + } + } + } +} + + +static char *ngx_conf_include(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_str_t *value, file; + + value = cf->args->elts; + file = value[1]; + + if (ngx_conf_full_name(cf->cycle, &file) == NGX_ERROR){ + return NGX_CONF_ERROR; + } + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data); + + return ngx_conf_parse(cf, &file); +} + + +ngx_int_t ngx_conf_full_name(ngx_cycle_t *cycle, ngx_str_t *name) +{ + u_char *p; + ngx_str_t old; + + if (name->data[0] == '/') { + return NGX_OK; + } + + old = *name; + + name->len = cycle->root.len + old.len; + + if (!(name->data = ngx_palloc(cycle->pool, name->len + 1))) { + return NGX_ERROR; + } + + p = ngx_cpymem(name->data, cycle->root.data, cycle->root.len), + ngx_cpystrn(p, old.data, old.len + 1); + + return NGX_OK; +} + + +ngx_open_file_t *ngx_conf_open_file(ngx_cycle_t *cycle, ngx_str_t *name) +{ + ngx_str_t full; + ngx_uint_t i; + ngx_list_part_t *part; + ngx_open_file_t *file; + +#if (NGX_SUPPRESS_WARN) + full.len = 0; + full.data = NULL; +#endif + + if (name) { + full = *name; + + if (ngx_conf_full_name(cycle, &full) == NGX_ERROR) { + return NULL; + } + + 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 (full.len != file[i].name.len) { + continue; + } + + if (ngx_strcmp(full.data, file[i].name.data) == 0) { + return &file[i]; + } + } + } + + if (!(file = ngx_list_push(&cycle->open_files))) { + return NULL; + } + + if (name) { + file->fd = NGX_INVALID_FILE; + file->name = full; + + } else { + file->fd = ngx_stderr_fileno; + file->name.len = 0; + file->name.data = NULL; + } + + return file; +} + + +void ngx_conf_log_error(ngx_uint_t level, ngx_conf_t *cf, ngx_err_t err, + char *fmt, ...) +{ + int len; + char errstr[NGX_MAX_CONF_ERRSTR]; + va_list args; + + va_start(args, fmt); + len = ngx_vsnprintf(errstr, sizeof(errstr) - 1, fmt, args); + va_end(args); + + if (err) { + len += ngx_snprintf(errstr + len, sizeof(errstr) - len - 1, + " (%d: ", err); + len += ngx_strerror_r(err, errstr + len, sizeof(errstr) - len - 1); + errstr[len++] = ')'; + errstr[len] = '\0'; + } + + ngx_log_error(level, cf->log, 0, "%s in %s:%d", + errstr, cf->conf_file->file.name.data, cf->conf_file->line); +} + + +char *ngx_conf_set_flag_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + char *p = conf; + + ngx_str_t *value; + ngx_flag_t *fp; + ngx_conf_post_t *post; + + fp = (ngx_flag_t *) (p + cmd->offset); + + if (*fp != NGX_CONF_UNSET) { + return "is duplicate"; + } + + value = cf->args->elts; + + if (ngx_strcasecmp(value[1].data, "on") == 0) { + *fp = 1; + + } else if (ngx_strcasecmp(value[1].data, "off") == 0) { + *fp = 0; + + } else { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid value \"%s\" in \"%s\" directive, " + "it must be \"on\" or \"off\"", + value[1].data, cmd->name.data); + return NGX_CONF_ERROR; + } + + if (cmd->post) { + post = cmd->post; + return post->post_handler(cf, post, fp); + } + + return NGX_CONF_OK; +} + + +char *ngx_conf_set_str_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + char *p = conf; + + ngx_str_t *field, *value; + ngx_conf_post_t *post; + + field = (ngx_str_t *) (p + cmd->offset); + + if (field->data) { + return "is duplicate"; + } + + value = cf->args->elts; + + *field = value[1]; + + if (cmd->post) { + post = cmd->post; + return post->post_handler(cf, post, field); + } + + return NGX_CONF_OK; +} + + +char *ngx_conf_set_num_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + char *p = conf; + + ngx_int_t *np; + ngx_str_t *value; + ngx_conf_post_t *post; + + + np = (ngx_int_t *) (p + cmd->offset); + + if (*np != NGX_CONF_UNSET) { + return "is duplicate"; + } + + value = cf->args->elts; + *np = ngx_atoi(value[1].data, value[1].len); + if (*np == NGX_ERROR) { + return "invalid number"; + } + + if (cmd->post) { + post = cmd->post; + return post->post_handler(cf, post, np); + } + + return NGX_CONF_OK; +} + + +char *ngx_conf_set_size_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + char *p = conf; + + size_t *sp; + ngx_str_t *value; + ngx_conf_post_t *post; + + + sp = (size_t *) (p + cmd->offset); + if (*sp != NGX_CONF_UNSET_SIZE) { + return "is duplicate"; + } + + value = cf->args->elts; + + *sp = ngx_parse_size(&value[1]); + if (*sp == (size_t) NGX_ERROR) { + return "invalid value"; + } + + if (cmd->post) { + post = cmd->post; + return post->post_handler(cf, post, sp); + } + + return NGX_CONF_OK; +} + + +char *ngx_conf_set_msec_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + char *p = conf; + + ngx_msec_t *msp; + ngx_str_t *value; + ngx_conf_post_t *post; + + + msp = (ngx_msec_t *) (p + cmd->offset); + if (*msp != NGX_CONF_UNSET_MSEC) { + return "is duplicate"; + } + + value = cf->args->elts; + + *msp = ngx_parse_time(&value[1], 0); + if (*msp == (ngx_msec_t) NGX_ERROR) { + return "invalid value"; + } + + if (*msp == (ngx_msec_t) NGX_PARSE_LARGE_TIME) { + return "value must be less than 597 hours"; + } + + if (cmd->post) { + post = cmd->post; + return post->post_handler(cf, post, msp); + } + + return NGX_CONF_OK; +} + + +char *ngx_conf_set_sec_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + char *p = conf; + + time_t *sp; + ngx_str_t *value; + ngx_conf_post_t *post; + + + sp = (time_t *) (p + cmd->offset); + if (*sp != NGX_CONF_UNSET) { + return "is duplicate"; + } + + value = cf->args->elts; + + *sp = ngx_parse_time(&value[1], 1); + if (*sp == NGX_ERROR) { + return "invalid value"; + } + + if (*sp == NGX_PARSE_LARGE_TIME) { + return "value must be less than 68 years"; + } + + if (cmd->post) { + post = cmd->post; + return post->post_handler(cf, post, sp); + } + + return NGX_CONF_OK; +} + + +char *ngx_conf_set_bufs_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + char *p = conf; + + ngx_str_t *value; + ngx_bufs_t *bufs; + + + bufs = (ngx_bufs_t *) (p + cmd->offset); + if (bufs->num) { + return "is duplicate"; + } + + value = cf->args->elts; + + bufs->num = ngx_atoi(value[1].data, value[1].len); + if (bufs->num == NGX_ERROR || bufs->num == 0) { + return "invalid value"; + } + + bufs->size = ngx_parse_size(&value[2]); + if (bufs->size == (size_t) NGX_ERROR || bufs->size == 0) { + return "invalid value"; + } + + return NGX_CONF_OK; +} + + +char *ngx_conf_set_enum_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + char *p = conf; + + ngx_uint_t *np, i; + ngx_str_t *value; + ngx_conf_enum_t *e; + + np = (ngx_uint_t *) (p + cmd->offset); + + if (*np != NGX_CONF_UNSET_UINT) { + return "is duplicate"; + } + + value = cf->args->elts; + e = cmd->post; + + for (i = 0; e[i].name.len != 0; i++) { + if (e[i].name.len != value[1].len + || ngx_strcasecmp(e[i].name.data, value[1].data) != 0) + { + continue; + } + + *np = e[i].value; + + return NGX_CONF_OK; + } + + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "invalid value \"%s\"", value[1].data); + + return NGX_CONF_ERROR; +} + + +char *ngx_conf_set_bitmask_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + char *p = conf; + + ngx_uint_t *np, i, m; + ngx_str_t *value; + ngx_conf_bitmask_t *mask; + + + np = (ngx_uint_t *) (p + cmd->offset); + value = cf->args->elts; + mask = cmd->post; + + for (i = 1; i < cf->args->nelts; i++) { + for (m = 0; mask[m].name.len != 0; m++) { + + if (mask[m].name.len != value[i].len + || ngx_strcasecmp(mask[m].name.data, value[i].data) != 0) + { + continue; + } + + if (*np & mask[m].mask) { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "duplicate value \"%s\"", value[i].data); + + } else { + *np |= mask[m].mask; + } + + break; + } + + if (mask[m].name.len == 0) { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "invalid value \"%s\"", value[i].data); + + return NGX_CONF_ERROR; + } + } + + return NGX_CONF_OK; +} + + +char *ngx_conf_unsupported(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + return "unsupported on this platform"; +} + + +char *ngx_conf_check_num_bounds(ngx_conf_t *cf, void *post, void *data) +{ + ngx_conf_num_bounds_t *bounds = post; + ngx_int_t *np = data; + + if (bounds->high == -1) { + if (*np >= bounds->low) { + return NGX_CONF_OK; + } + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "value must be equal or more than %d", bounds->low); + + return NGX_CONF_ERROR; + } + + if (*np >= bounds->low && *np <= bounds->high) { + return NGX_CONF_OK; + } + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "value must be between %d and %d", + bounds->low, bounds->high); + + return NGX_CONF_ERROR; +} diff --git a/src/core/ngx_conf_file.h b/src/core/ngx_conf_file.h new file mode 100644 --- /dev/null +++ b/src/core/ngx_conf_file.h @@ -0,0 +1,295 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#ifndef _NGX_HTTP_CONF_FILE_H_INCLUDED_ +#define _NGX_HTTP_CONF_FILE_H_INCLUDED_ + + +#include +#include + + +/* + * AAAA number of agruments + * FF command flags + * TT command type, i.e. HTTP "location" or "server" command + */ + +#define NGX_CONF_NOARGS 0x00000001 +#define NGX_CONF_TAKE1 0x00000002 +#define NGX_CONF_TAKE2 0x00000004 +#define NGX_CONF_TAKE3 0x00000008 +#define NGX_CONF_TAKE4 0x00000010 +#define NGX_CONF_TAKE5 0x00000020 +#define NGX_CONF_TAKE6 0x00000040 +#define NGX_CONF_TAKE7 0x00000080 + +#define NGX_CONF_TAKE12 (NGX_CONF_TAKE1|NGX_CONF_TAKE2) +#define NGX_CONF_TAKE13 (NGX_CONF_TAKE1|NGX_CONF_TAKE3) + +#define NGX_CONF_TAKE23 (NGX_CONF_TAKE2|NGX_CONF_TAKE3) + +#define NGX_CONF_TAKE1234 (NGX_CONF_TAKE1|NGX_CONF_TAKE2|NGX_CONF_TAKE3 \ + |NGX_CONF_TAKE4) + +#define NGX_CONF_ARGS_NUMBER 0x000000ff +#define NGX_CONF_BLOCK 0x00000100 +#define NGX_CONF_FLAG 0x00000200 +#define NGX_CONF_ANY 0x00000400 +#define NGX_CONF_1MORE 0x00000800 +#define NGX_CONF_2MORE 0x00001000 + +#define NGX_DIRECT_CONF 0x00010000 + +#define NGX_MAIN_CONF 0x01000000 +#define NGX_ANY_CONF 0x0F000000 + + + +#define NGX_CONF_UNSET -1 +#define NGX_CONF_UNSET_UINT (ngx_uint_t) -1 +#define NGX_CONF_UNSET_PTR (void *) -1 +#define NGX_CONF_UNSET_SIZE (size_t) -1 +#define NGX_CONF_UNSET_MSEC (ngx_msec_t) -1 + + +#define NGX_CONF_OK NULL +#define NGX_CONF_ERROR (void *) -1 + +#define NGX_CONF_BLOCK_DONE 1 +#define NGX_CONF_FILE_DONE 2 + +#define NGX_MODULE 0, 0 + +#define NGX_CORE_MODULE 0x45524F43 /* "CORE" */ +#define NGX_CONF_MODULE 0x464E4F43 /* "CONF" */ + + +#define NGX_MAX_CONF_ERRSTR 256 + + +struct ngx_command_s { + ngx_str_t name; + int type; + char *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); + int conf; + int offset; + void *post; +}; + +#define ngx_null_command { ngx_null_string, 0, NULL, 0, 0, NULL } + + +struct ngx_open_file_s { + ngx_fd_t fd; + ngx_str_t name; +#if 0 + /* e.g. append mode, error_log */ + int flags; + /* e.g. reopen db file */ + int (*handler)(void *data, ngx_open_file_t *file); + void *data; +#endif +}; + + +struct ngx_module_s { + ngx_uint_t ctx_index; + ngx_uint_t index; + void *ctx; + ngx_command_t *commands; + ngx_uint_t type; + ngx_int_t (*init_module)(ngx_cycle_t *cycle); + ngx_int_t (*init_process)(ngx_cycle_t *cycle); +#if 0 + ngx_int_t (*init_thread)(ngx_cycle_t *cycle); +#endif +}; + + +typedef struct { + ngx_str_t name; + void *(*create_conf)(ngx_cycle_t *cycle); + char *(*init_conf)(ngx_cycle_t *cycle, void *conf); +} ngx_core_module_t; + + +typedef struct { + ngx_file_t file; + ngx_buf_t *buffer; + ngx_uint_t line; +} ngx_conf_file_t; + + +typedef char *(*ngx_conf_handler_pt)(ngx_conf_t *cf, + ngx_command_t *dummy, void *conf); + + +struct ngx_conf_s { + char *name; + ngx_array_t *args; + + ngx_cycle_t *cycle; + ngx_pool_t *pool; + ngx_conf_file_t *conf_file; + ngx_log_t *log; + + void *ctx; + ngx_uint_t module_type; + ngx_uint_t cmd_type; + + ngx_conf_handler_pt handler; + char *handler_conf; +}; + + +typedef char *(*ngx_conf_post_handler_pt) (ngx_conf_t *cf, + void *data, void *conf); + +typedef struct { + ngx_conf_post_handler_pt post_handler; +} ngx_conf_post_t; + + +typedef struct { + ngx_conf_post_handler_pt post_handler; + int low; + int high; +} ngx_conf_num_bounds_t; + + +typedef struct { + ngx_str_t name; + ngx_uint_t value; +} ngx_conf_enum_t; + + +#define NGX_CONF_BITMASK_SET 1 + +typedef struct { + ngx_str_t name; + ngx_uint_t mask; +} ngx_conf_bitmask_t; + + +char *ngx_conf_check_num_bounds(ngx_conf_t *cf, void *post, void *data); + + +#define ngx_get_conf(conf_ctx, module) conf_ctx[module.index] + + + +#define ngx_conf_init_value(conf, default) \ + if (conf == NGX_CONF_UNSET) { \ + conf = default; \ + } + +#define ngx_conf_init_ptr_value(conf, default) \ + if (conf == NGX_CONF_UNSET_PTR) { \ + conf = default; \ + } + +#define ngx_conf_init_unsigned_value(conf, default) \ + if (conf == (unsigned) NGX_CONF_UNSET) { \ + conf = default; \ + } + +#define ngx_conf_init_size_value(conf, default) \ + if (conf == NGX_CONF_UNSET_SIZE) { \ + conf = default; \ + } + +#define ngx_conf_init_msec_value(conf, default) \ + if (conf == NGX_CONF_UNSET_MSEC) { \ + conf = default; \ + } + +#define ngx_conf_merge_value(conf, prev, default) \ + if (conf == NGX_CONF_UNSET) { \ + conf = (prev == NGX_CONF_UNSET) ? default : prev; \ + } + +#define ngx_conf_merge_ptr_value(conf, prev, default) \ + if (conf == NULL) { \ + conf = (prev == NULL) ? default : prev; \ + } + +#define ngx_conf_merge_unsigned_value(conf, prev, default) \ + if (conf == NGX_CONF_UNSET_UINT) { \ + conf = (prev == NGX_CONF_UNSET_UINT) ? default : prev; \ + } + +#define ngx_conf_merge_msec_value(conf, prev, default) \ + if (conf == NGX_CONF_UNSET_MSEC) { \ + conf = (prev == NGX_CONF_UNSET_MSEC) ? default : prev; \ + } + +#define ngx_conf_merge_sec_value(conf, prev, default) \ + if (conf == NGX_CONF_UNSET) { \ + conf = (prev == NGX_CONF_UNSET) ? default : prev; \ + } + +#define ngx_conf_merge_size_value(conf, prev, default) \ + if (conf == NGX_CONF_UNSET_SIZE) { \ + conf = (prev == NGX_CONF_UNSET_SIZE) ? default : prev; \ + } + +#define ngx_conf_merge_str_value(conf, prev, default) \ + if (conf.len == 0) { \ + if (prev.len) { \ + conf.len = prev.len; \ + conf.data = prev.data; \ + } else { \ + conf.len = sizeof(default) - 1; \ + conf.data = (u_char *) default; \ + } \ + } + +#define ngx_conf_merge_bufs_value(conf, prev, default_num, default_size) \ + if (conf.num == 0) { \ + if (prev.num) { \ + conf.num = prev.num; \ + conf.size = prev.size; \ + } else { \ + conf.num = default_num; \ + conf.size = default_size; \ + } \ + } + +#define ngx_conf_merge_bitmask_value(conf, prev, default) \ + if (conf == 0) { \ + conf = (prev == 0) ? default : prev; \ + } + + +#define addressof(addr) ((int) &addr) + + +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_open_file_t *ngx_conf_open_file(ngx_cycle_t *cycle, ngx_str_t *name); +void ngx_conf_log_error(ngx_uint_t level, ngx_conf_t *cf, ngx_err_t err, + char *fmt, ...); + + +char *ngx_conf_set_flag_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +char *ngx_conf_set_str_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +char *ngx_conf_set_num_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +char *ngx_conf_set_size_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +char *ngx_conf_set_msec_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +char *ngx_conf_set_sec_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +char *ngx_conf_set_bufs_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +char *ngx_conf_set_enum_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +char *ngx_conf_set_bitmask_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); + + +extern ngx_uint_t ngx_max_module; +extern ngx_module_t *ngx_modules[]; + + +#endif /* _NGX_HTTP_CONF_FILE_H_INCLUDED_ */ diff --git a/src/core/ngx_config.h b/src/core/ngx_config.h new file mode 100644 --- /dev/null +++ b/src/core/ngx_config.h @@ -0,0 +1,131 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#ifndef _NGX_CONFIG_H_INCLUDED_ +#define _NGX_CONFIG_H_INCLUDED_ + + +#if defined __DragonFly__ && !defined __FreeBSD__ +#define __FreeBSD__ 4 +#define __FreeBSD_version 480101 +#endif + + +#if defined __FreeBSD__ +#include + + +#elif defined __linux__ +#include + + + /* Solaris */ +#elif defined sun && (defined __svr4__ || defined __SVR4) +#include + + +#elif defined _WIN32 +#include + + +#else /* posix */ +#include + +#endif + + +#if !(WIN32) + +#define ngx_signal_helper(n) SIG##n +#define ngx_signal_value(n) ngx_signal_helper(n) + +/* TODO: #ifndef */ +#define NGX_SHUTDOWN_SIGNAL QUIT +#define NGX_TERMINATE_SIGNAL TERM +#define NGX_NOACCEPT_SIGNAL WINCH +#define NGX_RECONFIGURE_SIGNAL HUP + +#if (NGX_LINUXTHREADS) +#define NGX_REOPEN_SIGNAL INFO +#define NGX_CHANGEBIN_SIGNAL XCPU +#else +#define NGX_REOPEN_SIGNAL USR1 +#define NGX_CHANGEBIN_SIGNAL USR2 +#endif + +#endif + + + +/* TODO: platform specific: array[NGX_INVALID_ARRAY_INDEX] must cause SIGSEGV */ +#define NGX_INVALID_ARRAY_INDEX 0x80000000 + + +#if 1 +/* STUB: autoconf */ +typedef int ngx_int_t; +typedef u_int ngx_uint_t; +typedef int ngx_flag_t; +#define NGX_INT_T_LEN sizeof("-2147483648") - 1 +#define NGX_INT_T_FMT "d" +#define NGX_UINT_T_FMT "u" + +#else + +typedef long ngx_int_t; +typedef u_long ngx_uint_t; +typedef long ngx_flag_t; +#define NGX_INT_T_LEN sizeof("-9223372036854775808") - 1 +#define NGX_INT_T_FMT "lld" +#define NGX_UINT_T_FMT "llu" + +#endif + +/* TODO: auto */ +#define NGX_INT32_LEN sizeof("-2147483648") - 1 +#define NGX_INT64_LEN sizeof("-9223372036854775808") - 1 +#define NGX_OFF_T_LEN sizeof("-9223372036854775808") - 1 + + +#if (SOLARIS) + +/* TODO: auto_conf */ +#define NGX_ALIGN (_MAX_ALIGNMENT - 1) /* platform word */ +#define NGX_ALIGN_CAST (unsigned long) /* size of the pointer */ + +#else + +/* TODO: auto_conf */ +#define NGX_ALIGN (sizeof(unsigned long) - 1) /* platform word */ +#define NGX_ALIGN_CAST (unsigned long) /* size of the pointer */ + +#endif + +#define ngx_align(p) (char *) ((NGX_ALIGN_CAST p + NGX_ALIGN) & ~NGX_ALIGN) + + +/* TODO: auto_conf: ngx_inline inline __inline __inline__ */ +#ifndef ngx_inline +#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 +#endif + +#define NGX_MAXHOSTNAMELEN 64 +/* +#define NGX_MAXHOSTNAMELEN MAXHOSTNAMELEN +*/ + + +#endif /* _NGX_CONFIG_H_INCLUDED_ */ diff --git a/src/core/ngx_connection.c b/src/core/ngx_connection.c new file mode 100644 --- /dev/null +++ b/src/core/ngx_connection.c @@ -0,0 +1,453 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include + + +ngx_os_io_t ngx_io; + + +ngx_listening_t *ngx_listening_inet_stream_socket(ngx_conf_t *cf, + in_addr_t addr, + in_port_t port) +{ + size_t len; + ngx_listening_t *ls; + struct sockaddr_in *addr_in; + + if (!(ls = ngx_array_push(&cf->cycle->listening))) { + return NULL; + } + + ngx_memzero(ls, sizeof(ngx_listening_t)); + + if (!(addr_in = ngx_pcalloc(cf->pool, sizeof(struct sockaddr_in)))) { + return NULL; + } + +#if (HAVE_SIN_LEN) + addr_in->sin_len = sizeof(struct sockaddr_in); +#endif + addr_in->sin_family = AF_INET; + addr_in->sin_addr.s_addr = addr; + addr_in->sin_port = htons(port); + + if (!(ls->addr_text.data = ngx_palloc(cf->pool, INET_ADDRSTRLEN + 6))) { + return NULL; + } + + len = ngx_inet_ntop(AF_INET, &addr, ls->addr_text.data, INET_ADDRSTRLEN); + ls->addr_text.len = ngx_snprintf((char *) ls->addr_text.data + len, + 6, ":%d", port); + + ls->fd = (ngx_socket_t) -1; + ls->family = AF_INET; + ls->type = SOCK_STREAM; + ls->protocol = IPPROTO_IP; +#if (WIN32) + ls->flags = WSA_FLAG_OVERLAPPED; +#endif + ls->sockaddr = (struct sockaddr *) addr_in; + ls->socklen = sizeof(struct sockaddr_in); + ls->addr = offsetof(struct sockaddr_in, sin_addr); + ls->addr_text_max_len = INET_ADDRSTRLEN; + + return ls; +} + + +ngx_int_t ngx_set_inherited_sockets(ngx_cycle_t *cycle) +{ + ngx_uint_t i; + ngx_listening_t *ls; + struct sockaddr_in *addr_in; + + 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)); + if (ls[i].sockaddr == NULL) { + return NGX_ERROR; + } + + ls[i].socklen = sizeof(struct sockaddr_in); + 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 " + "socket #%d failed", ls[i].fd); + ls[i].ignore = 1; + continue; + } + + addr_in = (struct sockaddr_in *) ls[i].sockaddr; + + if (addr_in->sin_family != AF_INET) { + ngx_log_error(NGX_LOG_CRIT, cycle->log, ngx_socket_errno, + "the inherited socket #%d has " + "unsupported 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, ls[i].addr_text_max_len); + if (ls[i].addr_text.data == NULL) { + return NGX_ERROR; + } + + ls[i].family = addr_in->sin_family; + ls[i].addr_text.len = ngx_sock_ntop(ls[i].family, ls[i].sockaddr, + ls[i].addr_text.data, + ls[i].addr_text_max_len); + if (ls[i].addr_text.len == 0) { + return NGX_ERROR; + } + } + + return NGX_OK; +} + + +ngx_int_t ngx_open_listening_sockets(ngx_cycle_t *cycle) +{ + ngx_uint_t tries, failed, reuseaddr, i; + ngx_err_t err; + ngx_log_t *log; + ngx_socket_t s; + ngx_listening_t *ls; + + reuseaddr = 1; +#if (NGX_SUPPRESS_WARN) + failed = 0; +#endif + + log = cycle->log; + + /* TODO: tries configurable */ + + for (tries = /* STUB */ 5; tries; tries--) { + failed = 0; + + /* for each listening socket */ + + ls = cycle->listening.elts; + for (i = 0; i < cycle->listening.nelts; i++) { + + if (ls[i].ignore) { + continue; + } + + if (ls[i].fd != -1) { + continue; + } + + if (ls[i].inherited) { + + /* TODO: close on exit */ + /* TODO: nonblocking */ + /* TODO: deferred accept */ + + continue; + } + + s = ngx_socket(ls[i].family, ls[i].type, ls[i].protocol, + ls[i].flags); + + if (s == -1) { + ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno, + ngx_socket_n " %s failed", ls[i].addr_text.data); + return NGX_ERROR; + } + +#if (WIN32) + /* + * Winsock assignes a socket number divisible by 4 + * so to find a connection we divide a socket number by 4. + */ + + if (s % 4) { + ngx_log_error(NGX_LOG_EMERG, ls->log, 0, + ngx_socket_n " created socket %d", s); + return NGX_ERROR; + } +#endif + + if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, + (const void *) &reuseaddr, sizeof(int)) == -1) { + ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno, + "setsockopt(SO_REUSEADDR) %s failed", + ls[i].addr_text.data); + return NGX_ERROR; + } + + /* TODO: close on exit */ + + if (!(ngx_event_flags & NGX_USE_AIO_EVENT)) { + if (ngx_nonblocking(s) == -1) { + ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno, + ngx_nonblocking_n " %s failed", + ls[i].addr_text.data); + return NGX_ERROR; + } + } + +#if 0 + if (ls[i].nonblocking) { + if (ngx_nonblocking(s) == -1) { + ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno, + ngx_nonblocking_n " %s failed", + ls[i].addr_text.data); + return NGX_ERROR; + } + } +#endif + + if (bind(s, ls[i].sockaddr, ls[i].socklen) == -1) { + err = ngx_socket_errno; + ngx_log_error(NGX_LOG_EMERG, log, err, + "bind() to %s failed", ls[i].addr_text.data); + + if (err != NGX_EADDRINUSE) + return NGX_ERROR; + + if (ngx_close_socket(s) == -1) + ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno, + ngx_close_socket_n " %s failed", + ls[i].addr_text.data); + + failed = 1; + continue; + } + + if (listen(s, ls[i].backlog) == -1) { + ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno, + "listen() to %s failed", ls[i].addr_text.data); + return NGX_ERROR; + } + + /* TODO: deferred accept */ + + ls[i].fd = s; + } + + if (!failed) + break; + + /* TODO: delay configurable */ + + ngx_log_error(NGX_LOG_NOTICE, log, 0, + "try again to bind() after 500ms"); + ngx_msleep(500); + } + + if (failed) { + ngx_log_error(NGX_LOG_EMERG, log, 0, "still can not bind()"); + return NGX_ERROR; + } + + return NGX_OK; +} + + +void ngx_close_listening_sockets(ngx_cycle_t *cycle) +{ + ngx_uint_t i; + ngx_socket_t fd; + ngx_listening_t *ls; + + if (ngx_event_flags & NGX_USE_IOCP_EVENT) { + return; + } + + ngx_accept_mutex_held = 0; + ngx_accept_mutex = NULL; + + ls = cycle->listening.elts; + for (i = 0; i < cycle->listening.nelts; i++) { + fd = ls[i].fd; + +#if (WIN32) + /* + * Winsock assignes a socket number divisible by 4 + * so to find a connection we divide a socket number by 4. + */ + + fd /= 4; +#endif + + if (ngx_event_flags & NGX_USE_RTSIG_EVENT) { + if (cycle->connections[fd].read->active) { + ngx_del_conn(&cycle->connections[fd], NGX_CLOSE_EVENT); + } + + } else { + if (cycle->read_events[fd].active) { + ngx_del_event(&cycle->read_events[fd], + NGX_READ_EVENT, NGX_CLOSE_EVENT); + } + } + + if (ngx_close_socket(ls[i].fd) == -1) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_socket_errno, + ngx_close_socket_n " %s failed", + ls[i].addr_text.data); + } + + cycle->connections[fd].fd = (ngx_socket_t) -1; + } +} + + +void ngx_close_connection(ngx_connection_t *c) +{ + ngx_socket_t fd; + + if (c->pool == NULL) { + ngx_log_error(NGX_LOG_ALERT, c->log, 0, "connection already closed"); + return; + } + +#if (NGX_OPENSSL) + + if (c->ssl) { + if (ngx_ssl_shutdown(c) == NGX_AGAIN) { + c->read->event_handler = ngx_ssl_close_handler; + c->write->event_handler = ngx_ssl_close_handler; + return; + } + } + +#endif + + if (c->read->timer_set) { + ngx_del_timer(c->read); + } + + if (c->write->timer_set) { + ngx_del_timer(c->write); + } + + if (ngx_del_conn) { + ngx_del_conn(c, NGX_CLOSE_EVENT); + + } else { + if (c->read->active || c->read->disabled) { + ngx_del_event(c->read, NGX_READ_EVENT, NGX_CLOSE_EVENT); + } + + if (c->write->active || c->write->disabled) { + ngx_del_event(c->write, NGX_WRITE_EVENT, NGX_CLOSE_EVENT); + } + } + +#if (NGX_THREADS) + + /* + * we have to clean the connection information before the closing + * because another thread may reopen the same file descriptor + * before we clean the connection + */ + + if (ngx_mutex_lock(ngx_posted_events_mutex) == NGX_OK) { + + if (c->read->prev) { + ngx_delete_posted_event(c->read); + } + + if (c->write->prev) { + ngx_delete_posted_event(c->write); + } + + c->read->closed = 1; + c->write->closed = 1; + + if (c->single_connection) { + ngx_unlock(&c->lock); + c->read->locked = 0; + c->write->locked = 0; + } + + ngx_mutex_unlock(ngx_posted_events_mutex); + } + +#else + + if (c->read->prev) { + ngx_delete_posted_event(c->read); + } + + if (c->write->prev) { + ngx_delete_posted_event(c->write); + } + + c->read->closed = 1; + c->write->closed = 1; + +#endif + + fd = c->fd; + c->fd = (ngx_socket_t) -1; + c->data = NULL; + + ngx_destroy_pool(c->pool); + + if (ngx_close_socket(fd) == -1) { + + /* 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_close_socket_n " failed"); + } +} + + + +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) + { + return 0; + } + + if (err == NGX_ECONNRESET +#if !(WIN32) + || err == NGX_EPIPE +#endif + || err == NGX_ENOTCONN + || err == NGX_ECONNREFUSED + || err == NGX_EHOSTUNREACH) + { + + 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: + level = NGX_LOG_CRIT; + } + + } else { + level = NGX_LOG_CRIT; + } + + ngx_log_error(level, c->log, err, text); + + return NGX_ERROR; +} diff --git a/src/core/ngx_connection.h b/src/core/ngx_connection.h new file mode 100644 --- /dev/null +++ b/src/core/ngx_connection.h @@ -0,0 +1,152 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#ifndef _NGX_CONNECTION_H_INCLUDED_ +#define _NGX_CONNECTION_H_INCLUDED_ + + +#include +#include + + +typedef struct { + ngx_socket_t fd; + + struct sockaddr *sockaddr; + socklen_t socklen; /* size of sockaddr */ + int addr; /* offset to address in sockaddr */ + int addr_text_max_len; + ngx_str_t addr_text; + + int family; + int type; + int protocol; + int flags; /* Winsock2 flags */ + + void (*handler)(ngx_connection_t *c); /* handler of accepted + connection */ + void *ctx; /* ngx_http_conf_ctx_t, for example */ + void *servers; /* array of ngx_http_in_addr_t, for example */ + + ngx_log_t *log; + int backlog; + + size_t pool_size; + size_t post_accept_buffer_size; /* should be here because + of the AcceptEx() preread */ + time_t post_accept_timeout; /* should be here because + of the deferred accept */ + + unsigned new:1; + unsigned remain:1; + unsigned ignore:1; + + unsigned bound:1; /* already bound */ + unsigned inherited:1; /* inherited from previous process */ + unsigned nonblocking_accept:1; + unsigned nonblocking:1; +#if 0 + unsigned overlapped:1; /* Winsock2 overlapped */ +#endif + unsigned shared:1; /* shared between threads or processes */ +#if (HAVE_DEFERRED_ACCEPT) + unsigned deferred_accept:1; +#endif + + unsigned addr_ntop:1; +} ngx_listening_t; + + +typedef enum { + NGX_ERROR_CRIT = 0, + NGX_ERROR_ERR, + NGX_ERROR_INFO, + NGX_ERROR_IGNORE_ECONNRESET +} ngx_connection_log_error_e; + + +typedef enum { + NGX_TCP_NOPUSH_DISABLED = -1, + NGX_TCP_NOPUSH_UNSET = 0, + NGX_TCP_NOPUSH_SET +} ngx_connection_tcp_nopush_e; + + +struct ngx_connection_s { + void *data; + ngx_event_t *read; + ngx_event_t *write; + + ngx_socket_t fd; + + ngx_recv_pt recv; + ngx_send_chain_pt send_chain; + + ngx_listening_t *listening; + + off_t sent; + + void *ctx; + void *servers; + + + ngx_log_t *log; + + ngx_pool_t *pool; + + struct sockaddr *sockaddr; + socklen_t socklen; + ngx_str_t addr_text; + +#if (NGX_OPENSSL) + ngx_ssl_t *ssl; +#endif + +#if (HAVE_IOCP) + struct sockaddr *local_sockaddr; + socklen_t local_socklen; +#endif + + ngx_buf_t *buffer; + + ngx_uint_t number; + + unsigned log_error:2; /* ngx_connection_log_error_e */ + + unsigned buffered:1; + unsigned single_connection:1; + unsigned unexpected_eof:1; + unsigned timedout:1; + signed tcp_nopush:2; +#if (HAVE_IOCP) + unsigned accept_context_updated:1; +#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_int_t ngx_set_inherited_sockets(ngx_cycle_t *cycle); +ngx_int_t ngx_open_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_error(ngx_connection_t *c, ngx_err_t err, char *text); + + +extern ngx_os_io_t ngx_io; + + +#endif /* _NGX_CONNECTION_H_INCLUDED_ */ diff --git a/src/core/ngx_core.h b/src/core/ngx_core.h new file mode 100644 --- /dev/null +++ b/src/core/ngx_core.h @@ -0,0 +1,78 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#ifndef _NGX_CORE_H_INCLUDED_ +#define _NGX_CORE_H_INCLUDED_ + + +typedef struct ngx_module_s ngx_module_t; +typedef struct ngx_conf_s ngx_conf_t; +typedef struct ngx_cycle_s ngx_cycle_t; +typedef struct ngx_pool_s ngx_pool_t; +typedef struct ngx_log_s ngx_log_t; +typedef struct ngx_array_s ngx_array_t; +typedef struct ngx_open_file_s ngx_open_file_t; +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_connection_s ngx_connection_t; + +typedef void (*ngx_event_handler_pt)(ngx_event_t *ev); + + + +#define NGX_OK 0 +#define NGX_ERROR -1 +#define NGX_AGAIN -2 +#define NGX_BUSY -3 +#define NGX_DONE -4 +#define NGX_DECLINED -5 +#define NGX_ABORT -6 + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if (HAVE_PCRE) +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#if (NGX_OPENSSL) +#include +#endif +#include + + +#define LF (u_char) 10 +#define CR (u_char) 13 +#define CRLF "\x0d\x0a" + + +#endif /* _NGX_CORE_H_INCLUDED_ */ diff --git a/src/core/ngx_crc.h b/src/core/ngx_crc.h new file mode 100644 --- /dev/null +++ b/src/core/ngx_crc.h @@ -0,0 +1,33 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#ifndef _NGX_CRC_H_INCLUDED_ +#define _NGX_CRC_H_INCLUDED_ + + +/* 32-bit crc16 */ + +ngx_inline static uint32_t ngx_crc(char *data, size_t len) +{ + uint32_t sum; + + for (sum = 0; len; len--) { + + /* + * gcc 2.95.2 x86 and icc 7.1.006 compile that operator + * into the single "rol" opcode. + * msvc 6.0sp2 compiles it into four opcodes. + */ + sum = sum >> 1 | sum << 31; + + sum += *data++; + } + + return sum; +} + + +#endif /* _NGX_CRC_H_INCLUDED_ */ diff --git a/src/core/ngx_cycle.c b/src/core/ngx_cycle.c new file mode 100644 --- /dev/null +++ b/src/core/ngx_cycle.c @@ -0,0 +1,767 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include + + +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 */ + +#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; + ngx_uint_t i, n, failed; + ngx_log_t *log; + ngx_conf_t conf; + ngx_pool_t *pool; + ngx_cycle_t *cycle, **old; + ngx_socket_t fd; + ngx_list_part_t *part; + ngx_open_file_t *file; + ngx_listening_t *ls, *nls; + ngx_core_module_t *module; + + log = old_cycle->log; + + if (!(pool = ngx_create_pool(16 * 1024, log))) { + return NULL; + } + pool->log = log; + + if (!(cycle = ngx_pcalloc(pool, sizeof(ngx_cycle_t)))) { + ngx_destroy_pool(pool); + return NULL; + } + cycle->pool = pool; + cycle->log = log; + cycle->old_cycle = old_cycle; + cycle->conf_file = old_cycle->conf_file; + cycle->root.len = sizeof(NGX_PREFIX) - 1; + cycle->root.data = (u_char *) NGX_PREFIX; + + + n = old_cycle->pathes.nelts ? old_cycle->pathes.nelts : 10; + if (!(cycle->pathes.elts = ngx_pcalloc(pool, n * sizeof(ngx_path_t *)))) { + 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_ERROR) + { + ngx_destroy_pool(pool); + return NULL; + } + + + if (!(cycle->new_log = ngx_log_create_errlog(cycle, 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)); + 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; + } + + + 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 == NGX_CONF_ERROR) { + ngx_destroy_pool(pool); + return NULL; + } + cycle->conf_ctx[ngx_modules[i]->index] = rv; + } + } + + + ngx_memzero(&conf, sizeof(ngx_conf_t)); + /* STUB: init array ? */ + conf.args = ngx_create_array(pool, 10, sizeof(ngx_str_t)); + if (conf.args == 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 (ngx_conf_parse(&conf, &cycle->conf_file) != NGX_CONF_OK) { + ngx_destroy_pool(pool); + 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); + } + + + 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) + { + ngx_destroy_pool(pool); + return NULL; + } + } + } + + + failed = 0; + + +#if !(WIN32) + if (ngx_create_pidfile(cycle, old_cycle) == NGX_ERROR) { + failed = 1; + } +#endif + + + if (!failed) { + + 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.data == NULL) { + continue; + } + + file[i].fd = ngx_open_file(file[i].name.data, + NGX_FILE_RDWR, + NGX_FILE_CREATE_OR_OPEN|NGX_FILE_APPEND); + +#if 0 + log->log_level = NGX_LOG_DEBUG_ALL; +#endif + ngx_log_debug3(NGX_LOG_DEBUG_CORE, log, 0, + "log: %0X %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); + failed = 1; + break; + } + +#if (WIN32) + if (ngx_file_append_mode(file[i].fd) == NGX_ERROR) { + ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, + ngx_file_append_mode_n " \"%s\" failed", + file[i].name.data); + failed = 1; + break; + } +#else + 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); + failed = 1; + break; + } +#endif + } + } + + cycle->log = cycle->new_log; + pool->log = cycle->new_log; + + if (cycle->log->log_level == 0) { + cycle->log->log_level = NGX_LOG_ERR; + } + + if (!failed) { + 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_memcmp(nls[n].sockaddr, + ls[i].sockaddr, ls[i].socklen) == 0) + { + fd = ls[i].fd; +#if (WIN32) + /* + * Winsock assignes a socket number divisible by 4 so + * to find a connection we divide a socket number by 4. + */ + + fd /= 4; +#endif + if (fd >= (ngx_socket_t) cycle->connection_n) { + ngx_log_error(NGX_LOG_EMERG, log, 0, + "%d connections is not enough to hold " + "an open listening socket on %s, " + "required at least %d connections", + cycle->connection_n, + ls[i].addr_text.data, fd); + failed = 1; + break; + } + + nls[n].fd = ls[i].fd; + nls[i].remain = 1; + ls[i].remain = 1; + break; + } + } + + if (nls[n].fd == -1) { + nls[n].new = 1; + } + } + + } else { + ls = cycle->listening.elts; + for (i = 0; i < cycle->listening.nelts; i++) { + ls[i].new = 1; + } + } + + if (!ngx_test_config && !failed) { + if (ngx_open_listening_sockets(cycle) == NGX_ERROR) { + failed = 1; + } + } + } + + if (failed) { + + /* 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_fileno) + { + 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_pool(pool); + return NULL; + } + + ls = cycle->listening.elts; + for (i = 0; i < cycle->listening.nelts; i++) { + if (ls[i].fd == -1 || !ls[i].new) { + continue; + } + + if (ngx_close_socket(ls[i].fd) == -1) { + ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno, + ngx_close_socket_n " %s failed", + ls[i].addr_text.data); + } + } + + ngx_destroy_pool(pool); + return NULL; + } + + + /* commit the new cycle configuration */ + +#if !(WIN32) + + if (!ngx_test_config && cycle->log->file->fd != STDERR_FILENO) { + + ngx_log_debug3(NGX_LOG_DEBUG_CORE, log, 0, + "dup2: %0X %d \"%s\"", + cycle->log->file, + cycle->log->file->fd, cycle->log->file->name.data); + + if (dup2(cycle->log->file->fd, STDERR_FILENO) == NGX_ERROR) { + ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, + "dup2(STDERR) failed"); + /* fatal */ + exit(1); + } + } + +#endif + + 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_ERROR) { + /* fatal */ + exit(1); + } + } + } + + /* close and delete stuff that lefts from an old cycle */ + + /* close the unneeded listening sockets */ + + ls = old_cycle->listening.elts; + for (i = 0; i < old_cycle->listening.nelts; i++) { + if (ls[i].remain) { + continue; + } + + if (ngx_close_socket(ls[i].fd) == -1) { + ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno, + ngx_close_socket_n " %s failed", + ls[i].addr_text.data); + } + } + + + /* close the unneeded 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_fileno) { + 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 (old_cycle->connections == NULL) { + /* an old cycle is an init cycle */ + ngx_destroy_pool(old_cycle->pool); + return cycle; + } + + if (ngx_process == NGX_PROCESS_MASTER) { + ngx_destroy_pool(old_cycle->pool); + 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.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_push_array(&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; +} + + +#if !(WIN32) + +ngx_int_t ngx_create_pidfile(ngx_cycle_t *cycle, ngx_cycle_t *old_cycle) +{ + ngx_uint_t trunc; + size_t len; + u_char *name, pid[NGX_INT64_LEN + 1]; + ngx_file_t file; + ngx_core_conf_t *ccf, *old_ccf; + + if (!ngx_test_config && old_cycle && old_cycle->conf_ctx == NULL) { + + /* + * do not create the pid file in the first ngx_init_cycle() call + * because we need to write the demonized process pid + */ + + return NGX_OK; + } + + ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module); + + if (!ngx_test_config && old_cycle) { + 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) + { + + /* pid file name is the same */ + + return NGX_OK; + } + } + + len = ngx_snprintf((char *) pid, NGX_INT64_LEN + 1, PID_T_FMT, ngx_pid); + + ngx_memzero(&file, sizeof(ngx_file_t)); + file.name = (ngx_inherited && getppid() > 1) ? ccf->newpid : ccf->pid; + file.log = cycle->log; + + trunc = ngx_test_config ? 0: NGX_FILE_TRUNCATE; + + file.fd = ngx_open_file(file.name.data, NGX_FILE_RDWR, + NGX_FILE_CREATE_OR_OPEN|trunc); + + if (file.fd == NGX_INVALID_FILE) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, + ngx_open_file_n " \"%s\" failed", file.name.data); + return NGX_ERROR; + } + + if (!ngx_test_config) { + 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, cycle->log, ngx_errno, + ngx_close_file_n " \"%s\" failed", file.name.data); + } + + ngx_delete_pidfile(old_cycle); + + return NGX_OK; +} + + +void ngx_delete_pidfile(ngx_cycle_t *cycle) +{ + u_char *name; + ngx_core_conf_t *ccf; + + if (cycle == NULL || cycle->conf_ctx == NULL) { + return; + } + + ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module); + + if (ngx_inherited && getppid() > 1) { + name = ccf->newpid.data; + + } else { + name = 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); + } +} + +#endif + + +void ngx_reopen_files(ngx_cycle_t *cycle, ngx_uid_t user) +{ + 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; + i = 0; + } + + if (file[i].name.data == NULL) { + continue; + } + + fd = ngx_open_file(file[i].name.data, NGX_FILE_RDWR, + NGX_FILE_CREATE_OR_OPEN|NGX_FILE_APPEND); + + 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 (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 (user != (ngx_uid_t) -1) { + if (chown((const char *) file[i].name.data, user, -1) == -1) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, + "chown \"%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 !(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 +} + + +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 --git a/src/core/ngx_cycle.h b/src/core/ngx_cycle.h new file mode 100644 --- /dev/null +++ b/src/core/ngx_cycle.h @@ -0,0 +1,79 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#ifndef _NGX_CYCLE_H_INCLUDED_ +#define _NGX_CYCLE_H_INCLUDED_ + + +#include +#include + + +struct ngx_cycle_s { + void ****conf_ctx; + ngx_pool_t *pool; + + ngx_log_t *log; + ngx_log_t *new_log; + + ngx_array_t listening; + ngx_array_t pathes; + ngx_list_t open_files; + + ngx_uint_t connection_n; + ngx_connection_t *connections; + ngx_event_t *read_events; + ngx_event_t *write_events; + + ngx_cycle_t *old_cycle; + + ngx_str_t conf_file; + ngx_str_t root; +}; + + +typedef struct { + ngx_flag_t daemon; + ngx_flag_t master; + + ngx_int_t worker_processes; + + ngx_uid_t user; + ngx_gid_t group; + + ngx_str_t pid; + ngx_str_t newpid; + +#if (NGX_THREADS) + ngx_int_t worker_threads; + size_t thread_stack_size; +#endif + +} ngx_core_conf_t; + + +typedef struct { + ngx_pool_t *pool; /* pcre's malloc() pool */ +} ngx_core_tls_t; + + +ngx_cycle_t *ngx_init_cycle(ngx_cycle_t *old_cycle); +ngx_int_t ngx_create_pidfile(ngx_cycle_t *cycle, ngx_cycle_t *old_cycle); +void ngx_delete_pidfile(ngx_cycle_t *cycle); +void ngx_reopen_files(ngx_cycle_t *cycle, ngx_uid_t user); +ngx_pid_t ngx_exec_new_binary(ngx_cycle_t *cycle, char *const *argv); + + +extern volatile ngx_cycle_t *ngx_cycle; +extern ngx_array_t ngx_old_cycles; +extern ngx_module_t ngx_core_module; +extern ngx_uint_t ngx_test_config; +#if (NGX_THREADS) +extern ngx_tls_key_t ngx_core_tls_key; +#endif + + +#endif /* _NGX_CYCLE_H_INCLUDED_ */ diff --git a/src/core/ngx_file.c b/src/core/ngx_file.c new file mode 100644 --- /dev/null +++ b/src/core/ngx_file.c @@ -0,0 +1,237 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include + + +static ngx_uint_t ngx_temp_number; +static ngx_uint_t ngx_random; + + +int ngx_write_chain_to_temp_file(ngx_temp_file_t *tf, ngx_chain_t *chain) +{ + int rc; + + if (tf->file.fd == NGX_INVALID_FILE) { + rc = ngx_create_temp_file(&tf->file, tf->path, tf->pool, + tf->persistent); + + if (rc == NGX_ERROR || rc == NGX_AGAIN) { + return rc; + } + + if (!tf->persistent && tf->warn) { + ngx_log_error(NGX_LOG_WARN, tf->file.log, 0, tf->warn); + } + } + + return ngx_write_chain_to_file(&tf->file, chain, tf->offset, tf->pool); +} + + +int ngx_create_temp_file(ngx_file_t *file, ngx_path_t *path, + ngx_pool_t *pool, int persistent) +{ + int num; + ngx_err_t err; + + file->name.len = path->name.len + 1 + path->len + 10; + + ngx_test_null(file->name.data, ngx_palloc(pool, file->name.len + 1), + NGX_ERROR); + +#if 0 + for (i = 0; i < file->name.len; i++) { + file->name.data[i] = 'X'; + } +#endif + + ngx_memcpy(file->name.data, path->name.data, path->name.len); + + num = ngx_next_temp_number(0); + + for ( ;; ) { + ngx_snprintf((char *) + (file->name.data + path->name.len + 1 + path->len), + 11, "%010u", num); + + ngx_create_hashed_filename(file, path); + +#if 1 + file->fd = ngx_open_tempfile(file->name.data, persistent); +#else + file->fd = ngx_open_tempfile(file->name.data, 1); +#endif + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, file->log, 0, + "temp fd:%d", file->fd); + + if (file->fd != NGX_INVALID_FILE) { + return NGX_OK; + } + + err = ngx_errno; + + if (err == NGX_EEXIST) { + num = ngx_next_temp_number(1); + continue; + } + + if ((path->level[0] == 0) + || (err != NGX_ENOENT +#if (WIN32) + && err != NGX_ENOTDIR +#endif + )) { + ngx_log_error(NGX_LOG_CRIT, file->log, err, + ngx_open_tempfile_n " \"%s\" failed", + file->name.data); + return NGX_ERROR; + } + + if (ngx_create_path(file, path) == NGX_ERROR) { + return NGX_ERROR; + } + } +} + + +void ngx_create_hashed_filename(ngx_file_t *file, ngx_path_t *path) +{ + int i, name, pos; + size_t level; + + name = file->name.len; + pos = path->name.len + 1; + + file->name.data[path->name.len + path->len] = '/'; + + for (i = 0; i < 3; i++) { + level = path->level[i]; + + 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; + } + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, file->log, 0, + "hashed path: %s", file->name.data); +} + + +int ngx_create_path(ngx_file_t *file, ngx_path_t *path) +{ + int i, pos; + ngx_err_t err; + + pos = path->name.len; + + for (i = 0; i < 3; i++) { + if (path->level[i] == 0) { + break; + } + + pos += path->level[i] + 1; + + file->name.data[pos] = '\0'; + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, file->log, 0, + "temp file: \"%s\"", file->name.data); + + if (ngx_create_dir(file->name.data) == NGX_FILE_ERROR) { + err = ngx_errno; + if (err != NGX_EEXIST) { + ngx_log_error(NGX_LOG_CRIT, file->log, err, + ngx_create_dir_n " \"%s\" failed", + file->name.data); + return NGX_ERROR; + } + } + + file->name.data[pos] = '/'; + } + + return NGX_OK; +} + + +void ngx_init_temp_number() +{ + ngx_random = 0; + + ngx_temp_number = ngx_random; + + while (ngx_random < 10000) { + ngx_random = 123456; + } +} + + +ngx_uint_t ngx_next_temp_number(ngx_uint_t collision) +{ + if (collision) { + ngx_temp_number += ngx_random; + } + + return ngx_temp_number++; +} + + +char *ngx_conf_set_path_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + char *p = conf; + + ngx_int_t level; + ngx_uint_t i, n; + ngx_str_t *value; + ngx_path_t *path, **pp; + + pp = (ngx_path_t **) (p + cmd->offset); + + if (*pp) { + return "is duplicate"; + } + + /* TODO: check duplicate in cf->cycle->pathes */ + + ngx_test_null(path, ngx_pcalloc(cf->pool, sizeof(ngx_path_t)), + NGX_CONF_ERROR); + + *pp = path; + + ngx_test_null(pp, ngx_push_array(&cf->cycle->pathes), NGX_CONF_ERROR); + *pp = path; + + value = (ngx_str_t *) cf->args->elts; + + path->name = value[1]; + + path->len = 0; + + for (i = 0, n = 2; n < cf->args->nelts; i++, n++) { + level = ngx_atoi(value[n].data, value[n].len); + if (level == NGX_ERROR || level == 0) { + return "invalid value"; + } + + path->level[i] = level; + path->len += level + 1; + } + + while (i < 3) { + path->level[i++] = 0; + } + + path->gc_handler = (ngx_gc_handler_pt) cmd->post; + + return NGX_CONF_OK; +} diff --git a/src/core/ngx_file.h b/src/core/ngx_file.h new file mode 100644 --- /dev/null +++ b/src/core/ngx_file.h @@ -0,0 +1,82 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#ifndef _NGX_FILE_H_INCLUDED_ +#define _NGX_FILE_H_INCLUDED_ + + +#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; + + off_t offset; + off_t sys_offset; + + ngx_log_t *log; + + unsigned info_valid:1; +}; + +#define NGX_MAX_PATH_LEVEL 3 + +struct ngx_path_s { + ngx_str_t name; + u_int len; + u_int level[3]; + ngx_gc_handler_pt gc_handler; +}; + + +typedef struct { + ngx_file_t file; + off_t offset; + ngx_path_t *path; + ngx_pool_t *pool; + char *warn; + + unsigned persistent:1; +} ngx_temp_file_t; + + +int ngx_write_chain_to_temp_file(ngx_temp_file_t *tf, ngx_chain_t *chain); +int ngx_create_temp_file(ngx_file_t *file, ngx_path_t *path, + ngx_pool_t *pool, int persistent); +void ngx_create_hashed_filename(ngx_file_t *file, ngx_path_t *path); +int ngx_create_path(ngx_file_t *file, ngx_path_t *path); + +void ngx_init_temp_number(); +ngx_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); + + +#define ngx_conf_merge_path_value(conf, prev, path, l1, l2, l3, pool) \ + if (conf == NULL) { \ + if (prev == NULL) { \ + ngx_test_null(conf, ngx_palloc(pool, sizeof(ngx_path_t)), NULL); \ + conf->name.len = sizeof(path) - 1; \ + conf->name.data = (u_char *) path; \ + conf->level[0] = l1; \ + conf->level[1] = l2; \ + conf->level[2] = l3; \ + conf->len = l1 + l2 + l3 + (l1 ? 1:0) + (l2 ? 1:0) + (l3 ? 1:0); \ + } else { \ + conf = prev; \ + } \ + } + + + +#endif /* _NGX_FILE_H_INCLUDED_ */ diff --git a/src/core/ngx_garbage_collector.c b/src/core/ngx_garbage_collector.c new file mode 100644 --- /dev/null +++ b/src/core/ngx_garbage_collector.c @@ -0,0 +1,279 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include + + +int ngx_garbage_collector_temp_handler(ngx_gc_t *ctx, ngx_str_t *name, + ngx_dir_t *dir); + + +static int ngx_collect_garbage(ngx_gc_t *ctx, ngx_str_t *dname, int level); + + + +#if 0 + +{ + ngx_test_null(cycle->timer_events, + ngx_alloc(sizeof(ngx_event_t) * TIMERS, cycle->log), + NGX_ERROR); + + ngx_event_timer_init(cycle); +} + + +void garbage_collector() +{ + ngx_msec_t timer; + struct timeval tv; + ngx_epoch_msec_t delta; + + for ( ;; ) { + timer = ngx_event_find_timer(); + + ngx_gettimeofday(&tv); + delta = tv.tv_sec * 1000 + tv.tv_usec / 1000; + + msleep(timer); + + ngx_gettimeofday(&tv); + + ngx_cached_time = tv.tv_sec; + ngx_time_update(); + + delta = tv.tv_sec * 1000 + tv.tv_usec / 1000 - delta; + + ngx_event_expire_timers((ngx_msec_t) delta); + } +} + +#endif + + +void stub_init(ngx_cycle_t *cycle) +{ + ngx_uint_t i; + ngx_gc_t ctx; + ngx_path_t **path; + + path = cycle->pathes.elts; + for (i = 0; i < cycle->pathes.nelts; i++) { + ctx.path = path[i]; + ctx.log = cycle->log; + ctx.handler = path[i]->gc_handler; + + ngx_collect_garbage(&ctx, &path[i]->name, 0); + } +} + + +static int ngx_collect_garbage(ngx_gc_t *ctx, ngx_str_t *dname, int 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; + + if (!(buf.data = ngx_alloc(buf.len + 1, ctx->log))) { + 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.info_valid) { + 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, + "\"%s\" has unknown file 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; +} + + +int 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 stale temporary \"%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 --git a/src/core/ngx_garbage_collector.h b/src/core/ngx_garbage_collector.h new file mode 100644 --- /dev/null +++ b/src/core/ngx_garbage_collector.h @@ -0,0 +1,30 @@ + +/* + * 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 int (*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; +}; + + +int ngx_garbage_collector_temp_handler(ngx_gc_t *ctx, ngx_str_t *name, + ngx_dir_t *dir); + + +#endif /* _NGX_GARBAGE_COLLECTOR_H_INCLUDED_ */ diff --git a/src/core/ngx_inet.c b/src/core/ngx_inet.c new file mode 100644 --- /dev/null +++ b/src/core/ngx_inet.c @@ -0,0 +1,273 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + + +#include +#include + + +ngx_inline static size_t ngx_sprint_uchar(u_char *text, u_char c, size_t len) +{ + size_t n; + ngx_uint_t c1, c2; + + n = 0; + + if (len == n) { + return n; + } + + c1 = c / 100; + + if (c1) { + *text++ = (u_char) (c1 + '0'); + n++; + + if (len == n) { + return n; + } + } + + c2 = (c % 100) / 10; + + if (c1 || c2) { + *text++ = (u_char) (c2 + '0'); + n++; + + if (len == n) { + return n; + } + } + + c2 = c % 10; + + *text++ = (u_char) (c2 + '0'); + n++; + + return n; +} + + +/* AF_INET only */ + +size_t ngx_sock_ntop(int family, struct sockaddr *addr, u_char *text, + size_t len) +{ + u_char *p; + size_t n; + ngx_uint_t i; + struct sockaddr_in *addr_in; + + if (len == 0) { + return 0; + } + + if (family != AF_INET) { + return 0; + } + + addr_in = (struct sockaddr_in *) addr; + p = (u_char *) &addr_in->sin_addr; + + if (len > INET_ADDRSTRLEN) { + len = INET_ADDRSTRLEN; + } + + n = ngx_sprint_uchar(text, p[0], len); + + i = 1; + + do { + if (len == n) { + text[n - 1] = '\0'; + return n; + } + + text[n++] = '.'; + + if (len == n) { + text[n - 1] = '\0'; + return n; + } + + n += ngx_sprint_uchar(&text[n], p[i++], len - n); + + } while (i < 4); + + if (len == n) { + text[n] = '\0'; + return n; + } + + text[n] = '\0'; + + return n; + +#if 0 + return ngx_snprintf((char *) text, + len > INET_ADDRSTRLEN ? INET_ADDRSTRLEN : len, + "%u.%u.%u.%u", p[0], p[1], p[2], p[3]); +#endif +} + + +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; + + if (len == 0) { + return 0; + } + + if (family != AF_INET) { + return 0; + } + + p = (u_char *) addr; + + if (len > INET_ADDRSTRLEN) { + len = INET_ADDRSTRLEN; + } + + n = ngx_sprint_uchar(text, p[0], len); + + i = 1; + + do { + if (len == n) { + text[n - 1] = '\0'; + return n; + } + + text[n++] = '.'; + + if (len == n) { + text[n - 1] = '\0'; + return n; + } + + n += ngx_sprint_uchar(&text[n], p[i++], len - n); + + } while (i < 4); + + if (len == n) { + text[n] = '\0'; + return n; + } + + text[n] = '\0'; + + return n; + +#if 0 + return ngx_snprintf((char *) text, + len > INET_ADDRSTRLEN ? INET_ADDRSTRLEN : len, + "%u.%u.%u.%u", p[0], p[1], p[2], p[3]); +#endif +} + + +/* AF_INET only */ + +ngx_int_t ngx_ptocidr(ngx_str_t *text, void *cidr) +{ + ngx_int_t m; + ngx_uint_t i; + ngx_inet_cidr_t *in_cidr; + + in_cidr = cidr; + + for (i = 0; i < text->len; i++) { + if (text->data[i] == '/') { + break; + } + } + + if (i == text->len) { + return NGX_ERROR; + } + + text->data[i] = '\0'; + in_cidr->addr = inet_addr((char *) text->data); + text->data[i] = '/'; + if (in_cidr->addr == INADDR_NONE) { + return NGX_ERROR; + } + + m = ngx_atoi(&text->data[i + 1], text->len - (i + 1)); + if (m == NGX_ERROR) { + return NGX_ERROR; + } + + if (m == 0) { + + /* the x86 compilers use the shl instruction that shifts by modulo 32 */ + + in_cidr->mask = 0; + return NGX_OK; + } + + in_cidr->mask = htonl((ngx_uint_t) (0 - (1 << (32 - m)))); + + return NGX_OK; +} + + +#if 0 + +ngx_int_t ngx_inet_addr_port(ngx_conf_t *cf, ngx_command_t *cmd, + ngx_str_t *addr_port) +{ + u_char *host; + ngx_int_t port; + ngx_uint_t p; + struct hostent *h; + + for (p = 0; p < addr_port->len; p++) { + if (addr_port->data[p] == ':') { + break; + } + } + + in_addr->host.len = p; + if (!(in_addr->host.data = ngx_palloc(pool, p + 1))) { + return NGX_ERROR; + } + + ngx_cpystrn(in_addr->host.data, addr_port->data, p + 1); + + if (p == addr_port->len) { + p = 0; + } + + port = ngx_atoi(&addr[p], args[1].len - p); + if (port == NGX_ERROR && p == 0) { + + /* default port */ + iap->port = 0; + + } else if ((port == NGX_ERROR && p != 0) /* "listen host:NONNUMBER" */ + || (port < 1 || port > 65536)) { /* "listen 99999" */ + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid port \"%s\" in \"%s\" directive, " + "it must be a number between 1 and 65535", + &addr[p], cmd->name.data); + + return NGX_CONF_ERROR; + + } else if (p == 0) { + ls->addr = INADDR_ANY; + ls->port = (in_port_t) port; + return NGX_CONF_OK; + } + + return NGX_OK; +} + +#endif diff --git a/src/core/ngx_inet.h b/src/core/ngx_inet.h new file mode 100644 --- /dev/null +++ b/src/core/ngx_inet.h @@ -0,0 +1,24 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#ifndef _NGX_INET_H_INCLUDED_ +#define _NGX_INET_H_INCLUDED_ + + +typedef struct { + in_addr_t addr; + in_addr_t mask; +} ngx_inet_cidr_t; + + +size_t ngx_sock_ntop(int family, struct sockaddr *addr, u_char *text, + size_t len); +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); + + +#endif /* _NGX_INET_H_INCLUDED_ */ diff --git a/src/core/ngx_list.c b/src/core/ngx_list.c new file mode 100644 --- /dev/null +++ b/src/core/ngx_list.c @@ -0,0 +1,41 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include + + +void *ngx_list_push(ngx_list_t *l) +{ + void *elt; + ngx_list_part_t *last; + + last = l->last; + + if (last->nelts == l->nalloc) { + + /* the last part is full, allocate a new list part */ + + if (!(last = ngx_palloc(l->pool, sizeof(ngx_list_part_t)))) { + return NULL; + } + + if (!(last->elts = ngx_palloc(l->pool, l->nalloc * l->size))) { + return NULL; + } + + last->nelts = 0; + last->next = NULL; + + l->last->next = last; + l->last = last; + } + + elt = (char *) last->elts + l->size * last->nelts; + last->nelts++; + + return elt; +} diff --git a/src/core/ngx_list.h b/src/core/ngx_list.h new file mode 100644 --- /dev/null +++ b/src/core/ngx_list.h @@ -0,0 +1,79 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#ifndef _NGX_LIST_H_INCLUDED_ +#define _NGX_LIST_H_INCLUDED_ + + +#include +#include + + +typedef struct ngx_list_part_s ngx_list_part_t; + +struct ngx_list_part_s { + void *elts; + ngx_uint_t nelts; + ngx_list_part_t *next; +}; + + +typedef struct { + ngx_list_part_t *last; + ngx_list_part_t part; + size_t size; + ngx_uint_t nalloc; + ngx_pool_t *pool; +} ngx_list_t; + + +ngx_inline static ngx_int_t ngx_list_init(ngx_list_t *list, ngx_pool_t *pool, + ngx_uint_t n, size_t size) +{ + if (!(list->part.elts = ngx_palloc(pool, n * size))) { + return NGX_ERROR; + } + + list->part.nelts = 0; + list->part.next = NULL; + list->last = &list->part; + list->size = size; + list->nalloc = n; + list->pool = pool; + + return NGX_OK; +} + + +/* + * + * the iteration through the list: + * + * part = &list.part; + * data = part->elts; + * + * for (i = 0 ;; i++) { + * + * if (i >= part->nelts) { + * if (part->next == NULL) { + * break; + * } + * + * part = part->next; + * data = part->elts; + * i = 0; + * } + * + * ... data[i] ... + * + * } + */ + + +void *ngx_list_push(ngx_list_t *list); + + +#endif /* _NGX_LIST_H_INCLUDED_ */ diff --git a/src/core/ngx_log.c b/src/core/ngx_log.c new file mode 100644 --- /dev/null +++ b/src/core/ngx_log.c @@ -0,0 +1,410 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include + + +static void ngx_log_write(ngx_log_t *log, char *errstr, size_t len); +static char *ngx_set_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, + 0, + 0, + NULL}, + + ngx_null_command +}; + + +static ngx_core_module_t ngx_errlog_module_ctx = { + ngx_string("errlog"), + NULL, + NULL +}; + + +ngx_module_t ngx_errlog_module = { + NGX_MODULE, + &ngx_errlog_module_ctx, /* module context */ + ngx_errlog_commands, /* module directives */ + NGX_CORE_MODULE, /* module type */ + NULL, /* init module */ + NULL /* init child */ +}; + + +static ngx_log_t ngx_log; +static ngx_open_file_t ngx_stderr; + + +static const char *err_levels[] = { + "stderr", "emerg", "alert", "crit", "error", + "warn", "notice", "info", "debug" +}; + +static const char *debug_levels[] = { + "debug_core", "debug_alloc", "debug_mutex", "debug_event", + "debug_http", "debug_imap" +}; + + +#if (HAVE_VARIADIC_MACROS) +void ngx_log_error_core(ngx_uint_t level, ngx_log_t *log, ngx_err_t err, + const char *fmt, ...) +#else +void ngx_log_error_core(ngx_uint_t level, ngx_log_t *log, ngx_err_t err, + const char *fmt, va_list args) +#endif +{ + char errstr[MAX_ERROR_STR]; + size_t len, max; +#if (HAVE_VARIADIC_MACROS) + va_list args; +#endif + + if (log->file->fd == NGX_INVALID_FILE) { + return; + } + + ngx_memcpy(errstr, ngx_cached_err_log_time.data, + ngx_cached_err_log_time.len); + +#if (WIN32) + max = MAX_ERROR_STR - 2; +#else + max = MAX_ERROR_STR - 1; +#endif + + len = ngx_cached_err_log_time.len; + + len += ngx_snprintf(errstr + len, max - len, " [%s] ", err_levels[level]); + + /* pid#tid */ + len += ngx_snprintf(errstr + len, max - len, + PID_T_FMT "#" TID_T_FMT ": ", ngx_log_pid, ngx_log_tid); + + if (log->data && *(int *) log->data != -1) { + len += ngx_snprintf(errstr + len, max - len, + "*%u ", *(u_int *) log->data); + } + +#if (HAVE_VARIADIC_MACROS) + + va_start(args, fmt); + len += ngx_vsnprintf(errstr + len, max - len, fmt, args); + va_end(args); + +#else + + len += ngx_vsnprintf(errstr + len, max - len, fmt, args); + +#endif + + if (err) { + + if (len > max - 50) { + + /* leave a space for an error code */ + + len = max - 50; + errstr[len++] = '.'; + errstr[len++] = '.'; + errstr[len++] = '.'; + } + +#if (WIN32) + if ((unsigned) err >= 0x80000000) { + len += ngx_snprintf(errstr + len, max - len, " (%X: ", err); + } else { + len += ngx_snprintf(errstr + len, max - len, " (%d: ", err); + } +#else + len += ngx_snprintf(errstr + len, max - len, " (%d: ", err); +#endif + + if (len >= max) { + ngx_log_write(log, errstr, max); + return; + } + + len += ngx_strerror_r(err, errstr + len, max - len); + + if (len >= max) { + ngx_log_write(log, errstr, max); + return; + } + + errstr[len++] = ')'; + + if (len >= max) { + ngx_log_write(log, errstr, max); + return; + } + + } else { + if (len >= max) { + ngx_log_write(log, errstr, max); + return; + } + } + + if (level != NGX_LOG_DEBUG && log->handler) { + len += log->handler(log->data, errstr + len, max - len); + + if (len >= max) { + len = max; + } + } + + ngx_log_write(log, errstr, len); +} + + +static void ngx_log_write(ngx_log_t *log, char *errstr, size_t len) +{ +#if (WIN32) + u_long written; + + errstr[len++] = CR; + errstr[len++] = LF; + WriteFile(log->file->fd, errstr, len, &written, NULL); + +#else + + errstr[len++] = LF; + write(log->file->fd, errstr, len); + +#endif +} + + +#if !(HAVE_VARIADIC_MACROS) + +void ngx_log_error(ngx_uint_t level, ngx_log_t *log, ngx_err_t err, + const char *fmt, ...) +{ + va_list args; + + if (log->log_level >= level) { + va_start(args, fmt); + ngx_log_error_core(level, log, err, fmt, args); + va_end(args); + } +} + + +void ngx_log_debug_core(ngx_log_t *log, ngx_err_t err, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + ngx_log_error_core(NGX_LOG_DEBUG, log, err, fmt, args); + va_end(args); +} + + +void ngx_assert_core(ngx_log_t *log, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + ngx_log_error_core(NGX_LOG_ALERT, log, 0, fmt, args); + va_end(args); +} + +#endif + + +ngx_log_t *ngx_log_init_stderr() +{ +#if (WIN32) + + ngx_stderr_fileno = GetStdHandle(STD_ERROR_HANDLE); + ngx_stderr.fd = ngx_stderr_fileno; + + if (ngx_stderr_fileno == NGX_INVALID_FILE) { + + /* TODO: where can we log error ? */ + + return NULL; + + } else if (ngx_stderr_fileno == NULL) { + + /* there are no associated standard handles */ + + /* TODO: where can we can log possible errors ? */ + + ngx_stderr.fd = NGX_INVALID_FILE; + } + +#else + + ngx_stderr.fd = STDERR_FILENO; + +#endif + + ngx_log.file = &ngx_stderr; + ngx_log.log_level = NGX_LOG_ERR; + + return &ngx_log; +} + + +#if 0 + +ngx_int_t ngx_log_init_error_log() +{ + ngx_fd_t fd; + +#ifdef NGX_ERROR_LOG_PATH + + fd = ngx_open_file(NGX_ERROR_LOG_PATH, NGX_FILE_RDWR, + NGX_FILE_CREATE_OR_OPEN|NGX_FILE_APPEND); + + if (fd == NGX_INVALID_FILE) { + ngx_log_error(NGX_LOG_EMERG, (&ngx_log), ngx_errno, + ngx_open_file_n " \"" NGX_ERROR_LOG_PATH "\" failed"); + return NGX_ERROR; + } + +#if (WIN32) + + if (ngx_file_append_mode(fd) == NGX_ERROR) { + ngx_log_error(NGX_LOG_EMERG, (&ngx_log), ngx_errno, + ngx_file_append_mode_n " \"" NGX_ERROR_LOG_PATH + "\" failed"); + return NGX_ERROR; + } + +#else + + if (dup2(fd, STDERR_FILENO) == NGX_ERROR) { + ngx_log_error(NGX_LOG_EMERG, (&ngx_log), ngx_errno, + "dup2(STDERR) failed"); + return NGX_ERROR; + } + +#endif + +#else /* no NGX_ERROR_LOG_PATH */ + + ngx_log.log_level = NGX_LOG_INFO; + +#endif + + return NGX_OK; +} + +#endif + + +ngx_log_t *ngx_log_create_errlog(ngx_cycle_t *cycle, ngx_array_t *args) +{ + ngx_log_t *log; + ngx_str_t *value, *name; + + if (args) { + value = args->elts; + name = &value[1]; + + } else { + name = NULL; + } + + if (!(log = ngx_pcalloc(cycle->pool, sizeof(ngx_log_t)))) { + return NULL; + } + + if (!(log->file = ngx_conf_open_file(cycle, name))) { + return NULL; + } + + return log; +} + + +char *ngx_set_error_log_levels(ngx_conf_t *cf, ngx_log_t *log) +{ + ngx_uint_t i, n, d; + ngx_str_t *value; + + value = cf->args->elts; + + 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 (log->log_level != 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid log level \"%s\"", + value[i].data); + return NGX_CONF_ERROR; + } + + log->log_level = n; + continue; + } + } + + for (n = 0, d = NGX_LOG_DEBUG_FIRST; d <= NGX_LOG_DEBUG_LAST; d <<= 1) { + 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); + return NGX_CONF_ERROR; + } + + log->log_level |= d; + } + } + + + if (log->log_level == 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid log level \"%s\"", value[i].data); + return NGX_CONF_ERROR; + } + } + + if (log->log_level == NGX_LOG_DEBUG) { + log->log_level = NGX_LOG_DEBUG_ALL; + } + + return NGX_CONF_OK; +} + + +static char *ngx_set_error_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_str_t *value; + + 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; + + } else { + cf->cycle->new_log->file->name = value[1]; + + if (ngx_conf_full_name(cf->cycle, &cf->cycle->new_log->file->name) + == NGX_ERROR) + { + return NGX_CONF_ERROR; + } + } + + return ngx_set_error_log_levels(cf, cf->cycle->new_log); +} diff --git a/src/core/ngx_log.h b/src/core/ngx_log.h new file mode 100644 --- /dev/null +++ b/src/core/ngx_log.h @@ -0,0 +1,210 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#ifndef _NGX_LOG_H_INCLUDED_ +#define _NGX_LOG_H_INCLUDED_ + + +#include +#include + + +#define NGX_LOG_STDERR 0 +#define NGX_LOG_EMERG 1 +#define NGX_LOG_ALERT 2 +#define NGX_LOG_CRIT 3 +#define NGX_LOG_ERR 4 +#define NGX_LOG_WARN 5 +#define NGX_LOG_NOTICE 6 +#define NGX_LOG_INFO 7 +#define NGX_LOG_DEBUG 8 + +#define NGX_LOG_DEBUG_CORE 0x010 +#define NGX_LOG_DEBUG_ALLOC 0x020 +#define NGX_LOG_DEBUG_MUTEX 0x040 +#define NGX_LOG_DEBUG_EVENT 0x080 +#define NGX_LOG_DEBUG_HTTP 0x100 +#define NGX_LOG_DEBUG_IMAP 0x200 + +/* + * do not forget to update debug_levels[] in src/core/ngx_log.c + * after the adding a new debug level + */ + +#define NGX_LOG_DEBUG_FIRST NGX_LOG_DEBUG_CORE +#define NGX_LOG_DEBUG_LAST NGX_LOG_DEBUG_IMAP +#define NGX_LOG_DEBUG_CONNECTION 0x80000000 +#define NGX_LOG_DEBUG_ALL 0x7ffffff0 + + +typedef size_t (*ngx_log_handler_pt) (void *ctx, char *buf, size_t len); + + +struct ngx_log_s { + ngx_uint_t log_level; + ngx_open_file_t *file; + void *data; + ngx_log_handler_pt handler; +}; + +#define MAX_ERROR_STR 2048 + + +/*********************************/ + +#if (HAVE_GCC_VARIADIC_MACROS) + +#define HAVE_VARIADIC_MACROS 1 + +#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, ...); + +/*********************************/ + +#elif (HAVE_C99_VARIADIC_MACROS) + +#define HAVE_VARIADIC_MACROS 1 + +#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, ...); + +/*********************************/ + +#else /* NO VARIADIC MACROS */ + +#define HAVE_VARIADIC_MACROS 0 + +void ngx_log_error(ngx_uint_t level, ngx_log_t *log, ngx_err_t err, + const char *fmt, ...); +void ngx_log_error_core(ngx_uint_t level, ngx_log_t *log, ngx_err_t err, + const char *fmt, va_list args); +void ngx_log_debug_core(ngx_log_t *log, ngx_err_t err, const char *fmt, ...); +void ngx_assert_core(ngx_log_t *log, const char *fmt, ...); + + +#endif /* VARIADIC MACROS */ + + +/*********************************/ + +#if (NGX_DEBUG) + +#if (HAVE_VARIADIC_MACROS) + +#define ngx_log_debug0(level, log, err, fmt) \ + if (log->log_level & level) \ + ngx_log_error_core(NGX_LOG_DEBUG, log, err, fmt) + +#define ngx_log_debug1(level, log, err, fmt, arg1) \ + if (log->log_level & level) \ + ngx_log_error_core(NGX_LOG_DEBUG, log, err, fmt, arg1) + +#define ngx_log_debug2(level, log, err, fmt, arg1, arg2) \ + if (log->log_level & level) \ + ngx_log_error_core(NGX_LOG_DEBUG, log, err, fmt, arg1, arg2) + +#define ngx_log_debug3(level, log, err, fmt, arg1, arg2, arg3) \ + if (log->log_level & level) \ + ngx_log_error_core(NGX_LOG_DEBUG, log, err, fmt, arg1, arg2, arg3) + +#define ngx_log_debug4(level, log, err, fmt, arg1, arg2, arg3, arg4) \ + if (log->log_level & level) \ + ngx_log_error_core(NGX_LOG_DEBUG, log, err, fmt, arg1, arg2, arg3, arg4) + +#define ngx_log_debug5(level, log, err, fmt, arg1, arg2, arg3, arg4, arg5) \ + if (log->log_level & level) \ + ngx_log_error_core(NGX_LOG_DEBUG, log, err, fmt, \ + arg1, arg2, arg3, arg4, arg5) + +#define ngx_log_debug6(level, log, err, fmt, \ + arg1, arg2, arg3, arg4, arg5, arg6) \ + if (log->log_level & level) \ + ngx_log_error_core(NGX_LOG_DEBUG, log, err, fmt, \ + arg1, arg2, arg3, arg4, arg5, arg6) + +#define ngx_log_debug7(level, log, err, fmt, \ + arg1, arg2, arg3, arg4, arg5, arg6, arg7) \ + if (log->log_level & level) \ + ngx_log_error_core(NGX_LOG_DEBUG, log, err, fmt, \ + arg1, arg2, arg3, arg4, arg5, arg6, arg7) + +#else /* NO VARIADIC MACROS */ + +#define ngx_log_debug0(level, log, err, fmt) \ + if (log->log_level & level) \ + ngx_log_debug_core(log, err, fmt) + +#define ngx_log_debug1(level, log, err, fmt, arg1) \ + if (log->log_level & level) \ + ngx_log_debug_core(log, err, fmt, arg1) + +#define ngx_log_debug2(level, log, err, fmt, arg1, arg2) \ + if (log->log_level & level) \ + ngx_log_debug_core(log, err, fmt, arg1, arg2) + +#define ngx_log_debug3(level, log, err, fmt, arg1, arg2, arg3) \ + if (log->log_level & level) \ + ngx_log_debug_core(log, err, fmt, arg1, arg2, arg3) + +#define ngx_log_debug4(level, log, err, fmt, arg1, arg2, arg3, arg4) \ + if (log->log_level & level) \ + ngx_log_debug_core(log, err, fmt, arg1, arg2, arg3, arg4) + +#define ngx_log_debug5(level, log, err, fmt, arg1, arg2, arg3, arg4, arg5) \ + if (log->log_level & level) \ + ngx_log_debug_core(log, err, fmt, arg1, arg2, arg3, arg4, arg5) + +#define ngx_log_debug6(level, log, err, fmt, \ + arg1, arg2, arg3, arg4, arg5, arg6) \ + if (log->log_level & level) \ + ngx_log_debug_core(log, err, fmt, arg1, arg2, arg3, arg4, arg5, arg6) + +#define ngx_log_debug7(level, log, err, fmt, \ + arg1, arg2, arg3, arg4, arg5, arg6, arg7) \ + if (log->log_level & level) \ + ngx_log_debug_core(log, err, fmt, \ + arg1, arg2, arg3, arg4, arg5, arg6, arg7) + +#endif + +#else /* NO NGX_DEBUG */ + +#define ngx_log_debug0(level, log, err, fmt) +#define ngx_log_debug1(level, log, err, fmt, arg1) +#define ngx_log_debug2(level, log, err, fmt, arg1, arg2) +#define ngx_log_debug3(level, log, err, fmt, arg1, arg2, arg3) +#define ngx_log_debug4(level, log, err, fmt, arg1, arg2, arg3, arg4) +#define ngx_log_debug5(level, log, err, fmt, arg1, arg2, arg3, arg4, arg5) +#define ngx_log_debug6(level, log, err, fmt, arg1, arg2, arg3, arg4, arg5, arg6) +#define ngx_log_debug7(level, log, err, fmt, arg1, arg2, arg3, arg4, arg5, \ + arg6, arg7) + +#endif + +/*********************************/ + +#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_stderr(); +#if 0 +ngx_int_t ngx_log_init_error_log(); +#endif +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); + + + +extern ngx_module_t ngx_errlog_module; + + +#endif /* _NGX_LOG_H_INCLUDED_ */ diff --git a/src/core/ngx_output_chain.c b/src/core/ngx_output_chain.c new file mode 100644 --- /dev/null +++ b/src/core/ngx_output_chain.c @@ -0,0 +1,312 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include + + +#define NGX_NONE 1 + + +ngx_inline static ngx_int_t + ngx_output_chain_need_to_copy(ngx_output_chain_ctx_t *ctx, ngx_buf_t *buf); +static ngx_int_t ngx_output_chain_copy_buf(ngx_buf_t *dst, ngx_buf_t *src, + ngx_uint_t sendfile); + + +ngx_int_t ngx_output_chain(ngx_output_chain_ctx_t *ctx, ngx_chain_t *in) +{ + int rc, last; + size_t size, bsize; + ngx_chain_t *cl, *out, **last_out; + + /* + * the short path for the case when the ctx->in chain is empty + * and the incoming chain is empty too or it has the single buf + * that does not require the copy + */ + + if (ctx->in == NULL) { + + if (in == NULL) { + return ctx->output_filter(ctx->filter_ctx, in); + } + + if (in->next == NULL + && (!ngx_output_chain_need_to_copy(ctx, in->buf))) + { + return ctx->output_filter(ctx->filter_ctx, in); + } + } + + /* add the incoming buf to the chain ctx->in */ + + if (in) { + if (ngx_chain_add_copy(ctx->pool, &ctx->in, in) == NGX_ERROR) { + return NGX_ERROR; + } + } + + last = NGX_NONE; + out = NULL; + last_out = &out; + + for ( ;; ) { + + while (ctx->in) { + + /* + * cycle while there are the ctx->in bufs + * or there are the free output bufs to copy in + */ + + bsize = ngx_buf_size(ctx->in->buf); + + if (bsize == 0 && !ngx_buf_special(ctx->in->buf)) { + + ngx_log_error(NGX_LOG_ALERT, ctx->pool->log, 0, + "zero size buf"); + + ctx->in = ctx->in->next; + + continue; + } + + if (!ngx_output_chain_need_to_copy(ctx, ctx->in->buf)) { + + /* move the chain link to the output chain */ + + cl = ctx->in; + ctx->in = cl->next; + + *last_out = cl; + last_out = &cl->next; + cl->next = NULL; + + continue; + } + + if (ctx->buf == NULL) { + + /* get the free buf */ + + if (ctx->free) { + ctx->buf = ctx->free->buf; + ctx->free = ctx->free->next; + + } else if (out || ctx->allocated == ctx->bufs.num) { + + break; + + } else { + + size = ctx->bufs.size; + + if (ctx->in->buf->last_buf) { + + if (bsize < ctx->bufs.size) { + + /* + * allocate small temp buf for the small last buf + * or its small last part + */ + + size = bsize; + + } else if (ctx->bufs.num == 1 + && (bsize < 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 = bsize; + } + } + + if (!(ctx->buf = ngx_create_temp_buf(ctx->pool, size))) { + return NGX_ERROR; + } + + ctx->buf->tag = ctx->tag; + ctx->buf->recycled = 1; + ctx->allocated++; + } + } + + rc = ngx_output_chain_copy_buf(ctx->buf, ctx->in->buf, + ctx->sendfile); + + if (rc == NGX_ERROR) { + return rc; + } + + if (rc == NGX_AGAIN) { + if (out) { + break; + } + + return rc; + } + + /* delete the completed buf from the ctx->in chain */ + + if (ngx_buf_size(ctx->in->buf) == 0) { + ctx->in = ctx->in->next; + } + + ngx_alloc_link_and_set_buf(cl, ctx->buf, ctx->pool, NGX_ERROR); + *last_out = cl; + last_out = &cl->next; + ctx->buf = NULL; + } + + if (out == NULL && last != NGX_NONE) { + return last; + } + + last = ctx->output_filter(ctx->filter_ctx, out); + + ngx_chain_update_chains(&ctx->free, &ctx->busy, &out, ctx->tag); + last_out = &out; + + if (last == NGX_ERROR) { + return last; + } + } +} + + +ngx_inline static ngx_int_t + ngx_output_chain_need_to_copy(ngx_output_chain_ctx_t *ctx, ngx_buf_t *buf) +{ + if (ngx_buf_special(buf)) { + return 0; + } + + if (!ctx->sendfile) { + if (!ngx_buf_in_memory(buf)) { + return 1; + } + + buf->in_file = 0; + } + + if (ctx->need_in_memory && !ngx_buf_in_memory(buf)) { + return 1; + } + + if (ctx->need_in_temp && (buf->memory || buf->mmap)) { + return 1; + } + + return 0; +} + + +static ngx_int_t ngx_output_chain_copy_buf(ngx_buf_t *dst, ngx_buf_t *src, + ngx_uint_t sendfile) +{ + size_t size; + ssize_t n; + + size = ngx_buf_size(src); + + if (size > (size_t) (dst->end - dst->pos)) { + size = dst->end - dst->pos; + } + + if (ngx_buf_in_memory(src)) { + ngx_memcpy(dst->pos, src->pos, size); + src->pos += size; + dst->last += size; + + if (src->in_file) { + src->file_pos += size; + } + + if (src->last_buf && src->pos == src->last) { + dst->last_buf = 1; + } + + } else { + n = ngx_read_file(src->file, dst->pos, size, src->file_pos); + + if (n == NGX_ERROR) { + return n; + } + +#if (NGX_FILE_AIO_READ) + if (n == NGX_AGAIN) { + return n; + } +#endif + + if ((size_t) n != size) { + ngx_log_error(NGX_LOG_ALERT, src->file->log, 0, + ngx_read_file_n " reads only %d of %d from file", + n, size); + if (n == 0) { + return NGX_ERROR; + } + } + + src->file_pos += n; + dst->last += n; + + if (!sendfile) { + dst->in_file = 0; + } + + if (src->last_buf && src->file_pos == src->file_last) { + dst->last_buf = 1; + } + } + + return NGX_OK; +} + + +ngx_int_t ngx_chain_writer(void *data, ngx_chain_t *in) +{ + ngx_chain_writer_ctx_t *ctx = data; + + ngx_chain_t *cl; + + + for (/* void */; in; in = in->next) { + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, ctx->connection->log, 0, + "WRITER buf: %d", ngx_buf_size(in->buf)); + + ngx_alloc_link_and_set_buf(cl, in->buf, ctx->pool, NGX_ERROR); + *ctx->last = cl; + ctx->last = &cl->next; + } + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, ctx->connection->log, 0, + "WRITER0: %X", ctx->out); + + ctx->out = ngx_send_chain(ctx->connection, ctx->out, ctx->limit); + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, ctx->connection->log, 0, + "WRITER1: %X", ctx->out); + + if (ctx->out == NGX_CHAIN_ERROR) { + return NGX_ERROR; + } + + if (ctx->out == NULL) { + ctx->last = &ctx->out; + return NGX_OK; + } + + return NGX_AGAIN; +} diff --git a/src/core/ngx_palloc.c b/src/core/ngx_palloc.c new file mode 100644 --- /dev/null +++ b/src/core/ngx_palloc.c @@ -0,0 +1,215 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include + + +ngx_pool_t *ngx_create_pool(size_t size, ngx_log_t *log) +{ + ngx_pool_t *p; + + if (!(p = ngx_alloc(size, log))) { + return NULL; + } + + p->last = (char *) p + sizeof(ngx_pool_t); + p->end = (char *) p + size; + p->next = NULL; + p->large = NULL; + p->log = log; + + return p; +} + + +void ngx_destroy_pool(ngx_pool_t *pool) +{ + ngx_pool_t *p, *n; + ngx_pool_large_t *l; + + for (l = pool->large; l; l = l->next) { + + ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, 0, + "free: " PTR_FMT, l->alloc); + + if (l->alloc) { + free(l->alloc); + } + } + +#if (NGX_DEBUG) + + /* + * we could allocate the pool->log from this pool + * 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) { + ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, pool->log, 0, + "free: " PTR_FMT ", unused: " SIZE_T_FMT, + p, p->end - p->last); + + if (n == NULL) { + break; + } + } + +#endif + + for (p = pool, n = pool->next; /* void */; p = n, n = n->next) { + free(p); + + if (n == NULL) { + break; + } + } +} + + +void *ngx_palloc(ngx_pool_t *pool, size_t size) +{ + char *m; + ngx_pool_t *p, *n; + ngx_pool_large_t *large, *last; + + if (size <= (size_t) NGX_MAX_ALLOC_FROM_POOL + && size <= (size_t) (pool->end - (char *) pool) - sizeof(ngx_pool_t)) + { + for (p = pool, n = pool->next; /* void */; p = n, n = n->next) { + m = ngx_align(p->last); + + if ((size_t) (p->end - m) >= size) { + p->last = m + size ; + + return m; + } + + if (n == NULL) { + break; + } + } + + /* allocate a new pool block */ + + if (!(n = ngx_create_pool((size_t) (p->end - (char *) p), p->log))) { + return NULL; + } + + p->next = n; + m = n->last; + n->last += size; + + return m; + } + + /* allocate a large block */ + + large = NULL; + last = NULL; + + if (pool->large) { + for (last = pool->large; /* void */ ; last = last->next) { + if (last->alloc == NULL) { + large = last; + last = NULL; + break; + } + + if (last->next == NULL) { + break; + } + } + } + + if (large == NULL) { + if (!(large = ngx_palloc(pool, sizeof(ngx_pool_large_t)))) { + return NULL; + } + + large->next = NULL; + } + +#if 0 + if (!(p = ngx_memalign(ngx_pagesize, size, pool->log))) { + return NULL; + } +#else + if (!(p = ngx_alloc(size, pool->log))) { + return NULL; + } +#endif + + if (pool->large == NULL) { + pool->large = large; + + } else if (last) { + last->next = large; + } + + large->alloc = p; + + return p; +} + + +ngx_int_t ngx_pfree(ngx_pool_t *pool, void *p) +{ + ngx_pool_large_t *l; + + for (l = pool->large; l; l = l->next) { + if (p == l->alloc) { + ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, 0, + "free: " PTR_FMT, l->alloc); + free(l->alloc); + l->alloc = NULL; + + return NGX_OK; + } + } + + return NGX_DECLINED; +} + + +void *ngx_pcalloc(ngx_pool_t *pool, size_t size) +{ + void *p; + + p = ngx_palloc(pool, size); + if (p) { + ngx_memzero(p, size); + } + + return p; +} + +#if 0 + +static void *ngx_get_cached_block(size_t size) +{ + void *p; + ngx_cached_block_slot_t *slot; + + if (ngx_cycle->cache == NULL) { + return NULL; + } + + slot = &ngx_cycle->cache[(size + ngx_pagesize - 1) / ngx_pagesize]; + + slot->tries++; + + if (slot->number) { + p = slot->block; + slot->block = slot->block->next; + slot->number--; + return p; + } + + return NULL; +} + +#endif diff --git a/src/core/ngx_palloc.h b/src/core/ngx_palloc.h new file mode 100644 --- /dev/null +++ b/src/core/ngx_palloc.h @@ -0,0 +1,55 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#ifndef _NGX_PALLOC_H_INCLUDED_ +#define _NGX_PALLOC_H_INCLUDED_ + + +#include +#include + + +/* + * NGX_MAX_ALLOC_FROM_POOL should be (ngx_page_size - 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_test_null(p, alloc, rc) if ((p = alloc) == NULL) { return rc; } + + +typedef struct ngx_pool_large_s ngx_pool_large_t; + +struct ngx_pool_large_s { + ngx_pool_large_t *next; + void *alloc; +}; + + +struct ngx_pool_s { + char *last; + char *end; + ngx_pool_t *next; + ngx_pool_large_t *large; + ngx_log_t *log; +}; + + +void *ngx_alloc(size_t size, ngx_log_t *log); +void *ngx_calloc(size_t size, ngx_log_t *log); + +ngx_pool_t *ngx_create_pool(size_t size, ngx_log_t *log); +void ngx_destroy_pool(ngx_pool_t *pool); + +void *ngx_palloc(ngx_pool_t *pool, size_t size); +void *ngx_pcalloc(ngx_pool_t *pool, size_t size); +ngx_int_t ngx_pfree(ngx_pool_t *pool, void *p); + + +#endif /* _NGX_PALLOC_H_INCLUDED_ */ diff --git a/src/core/ngx_parse.c b/src/core/ngx_parse.c new file mode 100644 --- /dev/null +++ b/src/core/ngx_parse.c @@ -0,0 +1,212 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include + + +ngx_int_t ngx_parse_size(ngx_str_t *line) +{ + u_char last; + size_t len; + ngx_int_t scale, size; + + len = line->len; + last = line->data[len - 1]; + + switch (last) { + case 'K': + case 'k': + len--; + scale = 1024; + break; + + case 'M': + case 'm': + len--; + scale = 1024 * 1024; + break; + + default: + scale = 1; + } + + size = ngx_atoi(line->data, len); + if (size == NGX_ERROR) { + return NGX_ERROR; + } + + size *= scale; + + return size; +} + + +ngx_int_t ngx_parse_time(ngx_str_t *line, ngx_int_t sec) +{ + size_t len; + u_char *start, last; + ngx_int_t value, total, scale; + ngx_uint_t max, i; + enum { + st_start = 0, + st_year, + st_month, + st_week, + st_day, + st_hour, + st_min, + st_sec, + st_msec, + st_last + } step; + + + start = line->data; + len = 0; + total = 0; + step = sec ? st_start : st_month; + + for (i = 0; /* void */ ; i++) { + + if (i < line->len) { + if (line->data[i] != ' ') { + len++; + continue; + } + + if (line->data[i] == ' ' && len == 0) { + start = &line->data[i + 1]; + continue; + } + } + + if (len == 0) { + break; + } + + last = line->data[i - 1]; + + switch (last) { + case 'y': + if (step > st_start) { + return NGX_ERROR; + } + step = st_year; + len--; + max = 68; + scale = 60 * 60 * 24 * 365; + break; + + case 'M': + if (step > st_year) { + return NGX_ERROR; + } + step = st_month; + len--; + max = 828; + scale = 60 * 60 * 24 * 30; + break; + + case 'w': + if (step > st_month) { + return NGX_ERROR; + } + step = st_week; + len--; + max = 3550; + scale = 60 * 60 * 24 * 7; + break; + + case 'd': + if (step > st_week) { + return NGX_ERROR; + } + step = st_day; + len--; + max = 24855; + scale = 60 * 60 * 24; + break; + + case 'h': + if (step > st_day) { + 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 (sec || step > st_sec) { + return NGX_ERROR; + } + step = st_msec; + len--; + max = 2147483647; + scale = 1; + break; + } + + if (step > st_min) { + return NGX_ERROR; + } + + step = st_sec; + max = 2147483647; + scale = 1; + break; + + default: + step = st_last; + max = 2147483647; + scale = 1; + } + + value = ngx_atoi(start, len); + if (value == NGX_ERROR) { + return NGX_ERROR; + } + + if (step != st_msec && !sec) { + scale *= 1000; + max /= 1000; + } + + if ((u_int) value > max) { + return NGX_PARSE_LARGE_TIME; + } + + total += value * scale; + + if ((u_int) total > 2147483647) { + return NGX_PARSE_LARGE_TIME; + } + + if (i >= line->len) { + break; + } + + len = 0; + start = &line->data[i + 1]; + } + + return total; +} diff --git a/src/core/ngx_parse.h b/src/core/ngx_parse.h new file mode 100644 --- /dev/null +++ b/src/core/ngx_parse.h @@ -0,0 +1,22 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#ifndef _NGX_PARSE_H_INCLUDED_ +#define _NGX_PARSE_H_INCLUDED_ + + +#include +#include + + +#define NGX_PARSE_LARGE_TIME -2 + + +ngx_int_t ngx_parse_size(ngx_str_t *line); +ngx_int_t ngx_parse_time(ngx_str_t *line, ngx_int_t sec); + + +#endif /* _NGX_PARSE_H_INCLUDED_ */ diff --git a/src/core/ngx_rbtree.c b/src/core/ngx_rbtree.c new file mode 100644 --- /dev/null +++ b/src/core/ngx_rbtree.c @@ -0,0 +1,349 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include + + +/* + * The code is based on the algorithm described in the "Introduction + * to Algorithms" by Cormen, Leiserson and Rivest. + */ + +#define ngx_rbt_red(node) ((node)->color = 1) +#define ngx_rbt_black(node) ((node)->color = 0) +#define ngx_rbt_is_red(node) ((node)->color) +#define ngx_rbt_is_black(node) (!ngx_rbt_is_red(node)) +#define ngx_rbt_copy_color(n1, n2) (n1->color = n2->color) + + +ngx_inline void ngx_rbtree_left_rotate(ngx_rbtree_t **root, + ngx_rbtree_t *sentinel, + ngx_rbtree_t *node); +ngx_inline void ngx_rbtree_right_rotate(ngx_rbtree_t **root, + ngx_rbtree_t *sentinel, + ngx_rbtree_t *node); + + +void ngx_rbtree_insert(ngx_rbtree_t **root, ngx_rbtree_t *sentinel, + ngx_rbtree_t *node) +{ + ngx_rbtree_t *temp; + + /* a binary tree insert */ + + if (*root == sentinel) { + node->parent = NULL; + node->left = sentinel; + node->right = sentinel; + ngx_rbt_black(node); + *root = node; + + return; + } + + temp = *root; + + for ( ;; ) { + if (node->key < temp->key) { + if (temp->left == sentinel) { + temp->left = node; + break; + } + + temp = temp->left; + continue; + } + + if (temp->right == sentinel) { + temp->right = node; + break; + } + + temp = temp->right; + continue; + } + + node->parent = temp; + node->left = sentinel; + node->right = sentinel; + + + /* re-balance tree */ + + ngx_rbt_red(node); + + while (node != *root && ngx_rbt_is_red(node->parent)) { + + if (node->parent == node->parent->parent->left) { + temp = node->parent->parent->right; + + if (ngx_rbt_is_red(temp)) { + ngx_rbt_black(node->parent); + ngx_rbt_black(temp); + ngx_rbt_red(node->parent->parent); + node = node->parent->parent; + + } else { + if (node == node->parent->right) { + node = node->parent; + ngx_rbtree_left_rotate(root, sentinel, node); + } + + ngx_rbt_black(node->parent); + ngx_rbt_red(node->parent->parent); + ngx_rbtree_right_rotate(root, sentinel, node->parent->parent); + } + + } else { + temp = node->parent->parent->left; + + if (ngx_rbt_is_red(temp)) { + ngx_rbt_black(node->parent); + ngx_rbt_black(temp); + ngx_rbt_red(node->parent->parent); + node = node->parent->parent; + + } else { + if (node == node->parent->left) { + node = node->parent; + ngx_rbtree_right_rotate(root, sentinel, node); + } + + ngx_rbt_black(node->parent); + ngx_rbt_red(node->parent->parent); + ngx_rbtree_left_rotate(root, sentinel, node->parent->parent); + } + } + + } + + ngx_rbt_black(*root); +} + + +void ngx_rbtree_delete(ngx_rbtree_t **root, ngx_rbtree_t *sentinel, + ngx_rbtree_t *node) +{ + ngx_int_t is_red; + ngx_rbtree_t *subst, *temp, *w; + + /* a binary tree delete */ + + if (node->left == sentinel) { + temp = node->right; + subst = node; + + } else if (node->right == sentinel) { + temp = node->left; + subst = node; + + } else { + subst = ngx_rbtree_min(node->right, sentinel); + + if (subst->left != sentinel) { + temp = subst->left; + } else { + temp = subst->right; + } + } + + if (subst == *root) { + *root = temp; + ngx_rbt_black(temp); + + /* DEBUG stuff */ + node->left = NULL; + node->right = NULL; + node->parent = NULL; + node->key = 0; + + return; + } + + is_red = ngx_rbt_is_red(subst); + + if (subst == subst->parent->left) { + subst->parent->left = temp; + + } else { + subst->parent->right = temp; + } + + if (subst == node) { + + temp->parent = subst->parent; + + } else { + + if (subst->parent == node) { + temp->parent = subst; + + } else { + temp->parent = subst->parent; + } + + subst->left = node->left; + subst->right = node->right; + subst->parent = node->parent; + ngx_rbt_copy_color(subst, node); + + if (node == *root) { + *root = subst; + + } else { + if (node == node->parent->left) { + node->parent->left = subst; + } else { + node->parent->right = subst; + } + } + + if (subst->left != sentinel) { + subst->left->parent = subst; + } + + if (subst->right != sentinel) { + subst->right->parent = subst; + } + + /* DEBUG stuff */ + node->left = NULL; + node->right = NULL; + node->parent = NULL; + node->key = 0; + } + + if (is_red) { + return; + } + + /* a delete fixup */ + + while (temp != *root && ngx_rbt_is_black(temp)) { + + if (temp == temp->parent->left) { + w = temp->parent->right; + + if (ngx_rbt_is_red(w)) { + ngx_rbt_black(w); + ngx_rbt_red(temp->parent); + ngx_rbtree_left_rotate(root, sentinel, temp->parent); + w = temp->parent->right; + } + + if (ngx_rbt_is_black(w->left) && ngx_rbt_is_black(w->right)) { + ngx_rbt_red(w); + temp = temp->parent; + + } else { + if (ngx_rbt_is_black(w->right)) { + ngx_rbt_black(w->left); + ngx_rbt_red(w); + ngx_rbtree_right_rotate(root, sentinel, w); + w = temp->parent->right; + } + + ngx_rbt_copy_color(w, temp->parent); + ngx_rbt_black(temp->parent); + ngx_rbt_black(w->right); + ngx_rbtree_left_rotate(root, sentinel, temp->parent); + temp = *root; + } + + } else { + w = temp->parent->left; + + if (ngx_rbt_is_red(w)) { + ngx_rbt_black(w); + ngx_rbt_red(temp->parent); + ngx_rbtree_right_rotate(root, sentinel, temp->parent); + w = temp->parent->left; + } + + if (ngx_rbt_is_black(w->left) && ngx_rbt_is_black(w->right)) { + ngx_rbt_red(w); + temp = temp->parent; + + } else { + if (ngx_rbt_is_black(w->left)) { + ngx_rbt_black(w->right); + ngx_rbt_red(w); + ngx_rbtree_left_rotate(root, sentinel, w); + w = temp->parent->left; + } + + ngx_rbt_copy_color(w, temp->parent); + ngx_rbt_black(temp->parent); + ngx_rbt_black(w->left); + ngx_rbtree_right_rotate(root, sentinel, temp->parent); + temp = *root; + } + } + } + + ngx_rbt_black(temp); +} + + +ngx_inline void ngx_rbtree_left_rotate(ngx_rbtree_t **root, + ngx_rbtree_t *sentinel, + ngx_rbtree_t *node) +{ + ngx_rbtree_t *temp; + + temp = node->right; + node->right = temp->left; + + if (temp->left != sentinel) { + temp->left->parent = node; + } + + temp->parent = node->parent; + + if (node == *root) { + *root = temp; + + } else if (node == node->parent->left) { + node->parent->left = temp; + + } else { + node->parent->right = temp; + } + + temp->left = node; + node->parent = temp; +} + + +ngx_inline void ngx_rbtree_right_rotate(ngx_rbtree_t **root, + ngx_rbtree_t *sentinel, + ngx_rbtree_t *node) +{ + ngx_rbtree_t *temp; + + temp = node->left; + node->left = temp->right; + + if (temp->right != sentinel) { + temp->right->parent = node; + } + + temp->parent = node->parent; + + if (node == *root) { + *root = temp; + + } else if (node == node->parent->right) { + node->parent->right = temp; + + } else { + node->parent->left = temp; + } + + temp->right = node; + node->parent = temp; +} diff --git a/src/core/ngx_rbtree.h b/src/core/ngx_rbtree.h new file mode 100644 --- /dev/null +++ b/src/core/ngx_rbtree.h @@ -0,0 +1,43 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#ifndef _NGX_RBTREE_H_INCLUDED_ +#define _NGX_RBTREE_H_INCLUDED_ + + +#include +#include + + +typedef struct ngx_rbtree_s ngx_rbtree_t; + +struct ngx_rbtree_s { + ngx_int_t key; + ngx_rbtree_t *left; + ngx_rbtree_t *right; + ngx_rbtree_t *parent; + char color; +}; + + +void ngx_rbtree_insert(ngx_rbtree_t **root, ngx_rbtree_t *sentinel, + ngx_rbtree_t *node); +void ngx_rbtree_delete(ngx_rbtree_t **root, ngx_rbtree_t *sentinel, + ngx_rbtree_t *node); + + +ngx_inline static ngx_rbtree_t *ngx_rbtree_min(ngx_rbtree_t *node, + ngx_rbtree_t *sentinel) +{ + while (node->left != sentinel) { + node = node->left; + } + + return node; +} + + +#endif /* _NGX_RBTREE_H_INCLUDED_ */ diff --git a/src/core/ngx_regex.c b/src/core/ngx_regex.c new file mode 100644 --- /dev/null +++ b/src/core/ngx_regex.c @@ -0,0 +1,125 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include + + +static void *ngx_regex_malloc(size_t size); +static void ngx_regex_free(void *p); + + +static ngx_pool_t *ngx_pcre_pool; + + +void ngx_regex_init() +{ + pcre_malloc = ngx_regex_malloc; + pcre_free = ngx_regex_free; +} + + +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; +#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; + } + +#else + + ngx_pcre_pool = pool; + +#endif + + re = pcre_compile((const char *) pattern->data, (int) options, + &errstr, &erroff, NULL); + + if (re == NULL) { + if ((size_t) erroff == pattern->len) { + ngx_snprintf((char *) err->data, err->len - 1, + "pcre_compile() failed: %s in \"%s\"", + errstr, pattern->data); + } else { + ngx_snprintf((char *) err->data, err->len - 1, + "pcre_compile() failed: %s in \"%s\" at \"%s\"", + errstr, pattern->data, pattern->data + erroff); + } + } + + /* ensure that there is no current pool */ + +#if (NGX_THREADS) + if (ngx_threaded) { + tls->pool = NULL; + } else { + ngx_pcre_pool = NULL; + } +#else + ngx_pcre_pool = NULL; +#endif + + return re; +} + + +ngx_int_t ngx_regex_exec(ngx_regex_t *re, ngx_str_t *s, + int *matches, ngx_int_t size) +{ + int rc; + + rc = pcre_exec(re, NULL, (const char *) s->data, s->len, 0, 0, + matches, size); + + if (rc == -1) { + return NGX_DECLINED; + } + + return rc; +} + + +static void *ngx_regex_malloc(size_t size) +{ + ngx_pool_t *pool; +#if (NGX_THREADS) + ngx_core_tls_t *tls; + + 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) { + return ngx_palloc(pool, size); + } + + return NULL; +} + + +static void ngx_regex_free(void *p) +{ + return; +} diff --git a/src/core/ngx_regex.h b/src/core/ngx_regex.h new file mode 100644 --- /dev/null +++ b/src/core/ngx_regex.h @@ -0,0 +1,30 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#ifndef _NGX_REGEX_H_INCLUDED_ +#define _NGX_REGEX_H_INCLUDED_ + + +#include +#include + +#include + + +#define NGX_REGEX_CASELESS PCRE_CASELESS + +typedef pcre ngx_regex_t; + +void ngx_regex_init(); +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_exec(ngx_regex_t *re, ngx_str_t *s, + int *matches, ngx_int_t size); + +#define ngx_regex_exec_n "pcre_exec()" + + +#endif /* _NGX_REGEX_H_INCLUDED_ */ diff --git a/src/core/ngx_spinlock.c b/src/core/ngx_spinlock.c new file mode 100644 --- /dev/null +++ b/src/core/ngx_spinlock.c @@ -0,0 +1,48 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include + + +void ngx_spinlock(ngx_atomic_t *lock, ngx_uint_t spin) +{ + +#if (NGX_HAVE_ATOMIC_OPS) + + ngx_uint_t tries; + + tries = 0; + + for ( ;; ) { + + if (*lock) { + if (ngx_ncpu > 1 && tries++ < spin) { + continue; + } + + ngx_sched_yield(); + + tries = 0; + + } else { + if (ngx_atomic_cmp_set(lock, 0, 1)) { + return; + } + } + } + +#else + +#if (NGX_THREADS) + +#error ngx_spinlock() or ngx_atomic_cmp_set() are not defined ! + +#endif + +#endif + +} diff --git a/src/core/ngx_string.c b/src/core/ngx_string.c new file mode 100644 --- /dev/null +++ b/src/core/ngx_string.c @@ -0,0 +1,284 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include + + +u_char *ngx_cpystrn(u_char *dst, u_char *src, size_t n) +{ + if (n == 0) { + return dst; + } + + for (/* void */; --n; dst++, src++) { + *dst = *src; + + if (*dst == '\0') { + return dst; + } + } + + *dst = '\0'; + + return dst; +} + + +ngx_int_t ngx_rstrncmp(u_char *s1, u_char *s2, size_t n) +{ + if (n == 0) { + return 0; + } + + n--; + + for ( ;; ) { + if (s1[n] != s2[n]) { + return s1[n] - s2[n]; + } + + if (n == 0) { + return 0; + } + + n--; + } +} + + +ngx_int_t ngx_atoi(u_char *line, size_t n) +{ + ngx_int_t value; + + if (n == 0) { + return NGX_ERROR; + } + + for (value = 0; n--; line++) { + if (*line < '0' || *line > '9') { + return NGX_ERROR; + } + + value = value * 10 + (*line - '0'); + } + + if (value < 0) { + return NGX_ERROR; + + } else { + return value; + } +} + + +ngx_int_t ngx_hextoi(u_char *line, size_t n) +{ + u_char ch; + ngx_int_t value; + + if (n == 0) { + return NGX_ERROR; + } + + for (value = 0; n--; line++) { + ch = *line; + + if (ch >= '0' && ch <= '9') { + value = value * 16 + (ch - '0'); + continue; + } + + if (ch >= 'A' && ch <= 'F') { + value = value * 16 + (ch - 'A' + 10); + continue; + } + + if (ch >= 'a' && ch <= 'f') { + value = value * 16 + (ch - 'a' + 10); + continue; + } + + return NGX_ERROR; + } + + if (value < 0) { + return NGX_ERROR; + + } else { + return value; + } +} + + +void ngx_md5_text(u_char *text, u_char *md5) +{ + int i; + static u_char hex[] = "0123456789abcdef"; + + for (i = 0; i < 16; i++) { + *text++ = hex[md5[i] >> 4]; + *text++ = hex[md5[i] & 0xf]; + } + + *text = '\0'; +} + + +void ngx_encode_base64(ngx_str_t *src, ngx_str_t *dst) +{ + u_char *d, *s; + size_t len; + static u_char basis64[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + len = src->len; + s = src->data; + d = dst->data; + + while (len > 2) { + *d++ = basis64[(s[0] >> 2) & 0x3f]; + *d++ = basis64[((s[0] & 3) << 4) | (s[1] >> 4)]; + *d++ = basis64[((s[1] & 0x0f) << 2) | (s[2] >> 6)]; + *d++ = basis64[s[2] & 0x3f]; + + s += 3; + len -= 3; + } + + if (len) { + *d++ = basis64[(s[0] >> 2) & 0x3f]; + + if (len == 1) { + *d++ = basis64[(s[0] & 3) << 4]; + *d++ = '='; + + } else { + *d++ = basis64[((s[0] & 3) << 4) | (s[1] >> 4)]; + *d++ = basis64[(s[1] & 0x0f) << 2]; + } + + *d++ = '='; + } + + dst->len = d - dst->data; +} + + +ngx_int_t ngx_decode_base64(ngx_str_t *src, ngx_str_t *dst) +{ + 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, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 62, 77, 77, 77, 63, + 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, 77, + 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 }; + + for (len = 0; len < src->len; len++) { + if (src->data[len] == '=') { + break; + } + + if (basis64[src->data[len]] == 77) { + return NGX_ERROR; + } + } + + if (len % 4 == 1) { + return NGX_ERROR; + } + + s = src->data; + 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]]); + + s += 4; + len -= 4; + } + + if (len > 1) { + *d++ = (u_char) (basis64[s[0]] << 2 | basis64[s[1]] >> 4); + } + + if (len > 2) { + *d++ = (u_char) (basis64[s[1]] << 4 | basis64[s[2]] >> 2); + } + + dst->len = d - dst->data; + + return NGX_OK; +} + + +#if 0 +char *ngx_psprintf(ngx_pool_t *p, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + + while (*fmt) { + switch(*fmt++) { + case '%': + switch(*fmt++) { + case 's': + s = va_arg(args, char *); + n += ngx_strlen(s); + break; + + default: + n++; + } + default: + n++; + } + } + + str = ngx_palloc(p, n); + + va_start(args, fmt); + + for (i = 0; i < n; i++) { + switch(*fmt++) { + case '%': + switch(*fmt++) { + case 's': + s = va_arg(args, char *); + while (str[i++] = s); + break; + + default: + n++; + } + default: + str[i] = *fmt; + } + } + + len += ngx_vsnprintf(errstr + len, sizeof(errstr) - len - 1, fmt, args); + + va_end(args); + +} +#endif diff --git a/src/core/ngx_string.h b/src/core/ngx_string.h new file mode 100644 --- /dev/null +++ b/src/core/ngx_string.h @@ -0,0 +1,94 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#ifndef _NGX_STRING_H_INCLUDED_ +#define _NGX_STRING_H_INCLUDED_ + + +#include +#include + + +typedef struct { + size_t len; + u_char *data; +} ngx_str_t; + + +#define ngx_string(str) { sizeof(str) - 1, (u_char *) str } +#define ngx_null_string { 0, NULL } + + +#if (WIN32) + +#define ngx_strncasecmp(s1, s2, n) \ + strnicmp((const char *) s1, (const char *) s2, n) +#define ngx_strcasecmp(s1, s2) \ + stricmp((const char *) s1, (const char *) s2) + +#define ngx_snprintf _snprintf +#define ngx_vsnprintf _vsnprintf + +#else + +#define ngx_strncasecmp(s1, s2, n) \ + strncasecmp((const char *) s1, (const char *) s2, n) +#define ngx_strcasecmp(s1, s2) \ + strcasecmp((const char *) s1, (const char *) s2) + +#define ngx_snprintf snprintf +#define ngx_vsnprintf vsnprintf + +#endif + + +#define ngx_strncmp(s1, s2, n) \ + strncmp((const char *) s1, (const char *) s2, n) + +/* msvc and icc compile strcmp() to inline loop */ +#define ngx_strcmp(s1, s2) strcmp((const char *) s1, (const char *) s2) + +#define ngx_strstr(s1, s2) strstr((const char *) s1, (const char *) s2) +#define ngx_strlen(s) strlen((const char *) s) + +/* + * msvc and icc compile memset() to the inline "rep stos" + * while ZeroMemory() and bzero() are the calls. + * icc may also inline several mov's of a zeroed register for small blocks. + */ +#define ngx_memzero(buf, n) memset(buf, 0, n) + +/* msvc and icc compile memcpy() to the inline "rep movs" */ +#define ngx_memcpy(dst, src, n) memcpy(dst, src, n) +#define ngx_cpymem(dst, src, n) ((u_char *) memcpy(dst, src, n)) + n + +/* msvc and icc compile memcmp() to the inline loop */ +#define ngx_memcmp memcmp + +u_char *ngx_cpystrn(u_char *dst, u_char *src, size_t n); +ngx_int_t ngx_rstrncmp(u_char *s1, u_char *s2, size_t n); + +ngx_int_t ngx_atoi(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); + + +#define ngx_base64_encoded_length(len) (((len + 2) / 3) * 4) +#define ngx_base64_decoded_length(len) (((len + 3) / 4) * 3) + +void ngx_encode_base64(ngx_str_t *src, ngx_str_t *dst); +ngx_int_t ngx_decode_base64(ngx_str_t *src, ngx_str_t *dst); + + +#define ngx_qsort qsort + + +#define ngx_value_helper(n) #n +#define ngx_value(n) ngx_value_helper(n) + + +#endif /* _NGX_STRING_H_INCLUDED_ */ diff --git a/src/core/ngx_table.h b/src/core/ngx_table.h new file mode 100644 --- /dev/null +++ b/src/core/ngx_table.h @@ -0,0 +1,27 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#ifndef _NGX_TABLE_H_INCLUDED_ +#define _NGX_TABLE_H_INCLUDED_ + + +#include +#include + + +typedef ngx_array_t ngx_table_t; + +typedef struct { + ngx_str_t key; + ngx_str_t value; +} ngx_table_elt_t; + + +#define ngx_create_table(p, n) ngx_create_array(p, n, 2 * sizeof(ngx_str_t)) +#define ngx_push_table(t) ngx_push_array(t) + + +#endif /* _NGX_TABLE_H_INCLUDED_ */ diff --git a/src/core/ngx_times.c b/src/core/ngx_times.c new file mode 100644 --- /dev/null +++ b/src/core/ngx_times.c @@ -0,0 +1,346 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include + + +ngx_epoch_msec_t ngx_elapsed_msec; +ngx_epoch_msec_t ngx_old_elapsed_msec; +ngx_epoch_msec_t ngx_start_msec; + +static ngx_tm_t ngx_cached_gmtime; +static ngx_int_t ngx_gmtoff; + + +/* + * In the threaded mode only one thread updates the cached time and strings + * and these operations are protected by the mutex. The reading of the cached + * time and strings is not protected by the mutex. To avoid the race + * conditions for non-atomic values we use the NGX_TIME_SLOTS slots to store + * time value and strings. Thus thread may get the corrupted values only + * if it is preempted while copying and then it is not scheduled to run + * more than NGX_TIME_SLOTS seconds. + */ + +#if (NGX_THREADS) + +#define NGX_TIME_SLOTS 60 +static ngx_uint_t slot = NGX_TIME_SLOTS; + +static ngx_mutex_t *ngx_time_mutex; + +#else + +#define NGX_TIME_SLOTS 1 +#define slot 0 + +#endif + + +#if (NGX_THREADS && (TIME_T_SIZE > SIG_ATOMIC_T_SIZE)) + +volatile time_t *ngx_cached_time; +static time_t cached_time[NGX_TIME_SLOTS]; + +#else + +volatile time_t ngx_cached_time; + +#endif + + +ngx_thread_volatile ngx_str_t ngx_cached_err_log_time; +ngx_thread_volatile ngx_str_t ngx_cached_http_time; +ngx_thread_volatile ngx_str_t ngx_cached_http_log_time; + + +static u_char cached_err_log_time[NGX_TIME_SLOTS] + [sizeof("1970/09/28 12:00:00")]; +static u_char cached_http_time[NGX_TIME_SLOTS] + [sizeof("Mon, 28 Sep 1970 06:00:00 GMT")]; +static u_char cached_http_log_time[NGX_TIME_SLOTS] + [sizeof("28/Sep/1970:12:00:00 +0600")]; + + +static char *week[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; +static char *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; + + +void ngx_time_init() +{ + struct timeval tv; + + ngx_memzero(&ngx_cached_gmtime, sizeof(ngx_tm_t)); +#ifdef ngx_tm_zone + ngx_cached_gmtime.ngx_tm_zone = "GMT"; +#endif + + ngx_cached_err_log_time.len = sizeof("1970/09/28 12:00:00") - 1; + ngx_cached_http_time.len = sizeof("Mon, 28 Sep 1970 06:00:00 GMT") - 1; + ngx_cached_http_log_time.len = sizeof("28/Sep/1970:12:00:00 +0600") - 1; + +#if (NGX_THREADS && (TIME_T_SIZE > SIG_ATOMIC_T_SIZE)) + ngx_cached_time = &cached_time[0]; +#endif + + ngx_gettimeofday(&tv); + + ngx_start_msec = (ngx_epoch_msec_t) tv.tv_sec * 1000 + tv.tv_usec / 1000; + ngx_old_elapsed_msec = 0; + ngx_elapsed_msec = 0; + +#if !(WIN32) + tzset(); +#endif + + ngx_time_update(tv.tv_sec); +} + + +#if (NGX_THREADS) + +ngx_int_t ngx_time_mutex_init(ngx_log_t *log) +{ + if (!(ngx_time_mutex = ngx_mutex_init(log, NGX_MUTEX_LIGHT))) { + return NGX_ERROR; + } + + return NGX_OK; +} + +#endif + + +void ngx_time_update(time_t s) +{ + u_char *p; + ngx_tm_t tm; + + if (ngx_time() == s) { + return; + } + +#if (NGX_THREADS) + + if (ngx_mutex_trylock(ngx_time_mutex) != NGX_OK) { + return; + } + + if (slot == NGX_TIME_SLOTS) { + slot = 0; + } else { + slot++; + } + +#if (NGX_THREADS && (TIME_T_SIZE > SIG_ATOMIC_T_SIZE)) + ngx_cached_time = &cached_time[slot]; +#endif + +#endif + + ngx_time() = s; + + ngx_gmtime(s, &ngx_cached_gmtime); + + + p = cached_http_time[slot]; + + ngx_snprintf((char *) p, sizeof("Mon, 28 Sep 1970 06:00:00 GMT"), + "%s, %02d %s %4d %02d:%02d:%02d GMT", + week[ngx_cached_gmtime.ngx_tm_wday], + ngx_cached_gmtime.ngx_tm_mday, + months[ngx_cached_gmtime.ngx_tm_mon - 1], + ngx_cached_gmtime.ngx_tm_year, + ngx_cached_gmtime.ngx_tm_hour, + ngx_cached_gmtime.ngx_tm_min, + ngx_cached_gmtime.ngx_tm_sec); + + ngx_cached_http_time.data = p; + + +#if (HAVE_GETTIMEZONE) + + ngx_gmtoff = ngx_gettimezone(); + ngx_gmtime(s + ngx_gmtoff * 60, &tm); + +#elif (HAVE_GMTOFF) + + ngx_localtime(&tm); + ngx_gmtoff = tm.ngx_tm_gmtoff / 60; + +#else + + ngx_localtime(&tm); + ngx_gmtoff = ngx_timezone(tm.ngx_tm_isdst); + +#endif + + + p = cached_err_log_time[slot]; + + ngx_snprintf((char *) p, sizeof("1970/09/28 12:00:00"), + "%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_cached_err_log_time.data = p; + + + p = cached_http_log_time[slot]; + + ngx_snprintf((char *) p, sizeof("28/Sep/1970:12:00:00 +0600"), + "%02d/%s/%d:%02d:%02d:%02d %c%02d%02d", + tm.ngx_tm_mday, months[tm.ngx_tm_mon - 1], + tm.ngx_tm_year, tm.ngx_tm_hour, + tm.ngx_tm_min, tm.ngx_tm_sec, + ngx_gmtoff < 0 ? '-' : '+', + abs(ngx_gmtoff / 60), abs(ngx_gmtoff % 60)); + + ngx_cached_http_log_time.data = p; + + +#if (NGX_THREADS) + ngx_mutex_unlock(ngx_time_mutex); +#endif + +} + + +size_t ngx_http_time(u_char *buf, time_t t) +{ + ngx_tm_t tm; + + ngx_gmtime(t, &tm); + + return ngx_snprintf((char *) buf, sizeof("Mon, 28 Sep 1970 06:00:00 GMT"), + "%s, %02d %s %4d %02d:%02d:%02d GMT", + week[tm.ngx_tm_wday], + tm.ngx_tm_mday, + months[tm.ngx_tm_mon - 1], + tm.ngx_tm_year, + tm.ngx_tm_hour, + tm.ngx_tm_min, + tm.ngx_tm_sec); +} + + +size_t ngx_http_cookie_time(u_char *buf, time_t t) +{ + ngx_tm_t tm; + + ngx_gmtime(t, &tm); + + /* + * Netscape 3.x does not understand 4-digit years at all and + * 2-digit years more than "37" + */ + + if (tm.ngx_tm_year > 2037) { + return ngx_snprintf((char *) buf, + sizeof("Mon, 28-Sep-1970 06:00:00 GMT"), + "%s, %02d-%s-%d %02d:%02d:%02d GMT", + week[tm.ngx_tm_wday], + tm.ngx_tm_mday, + months[tm.ngx_tm_mon - 1], + tm.ngx_tm_year, + tm.ngx_tm_hour, + tm.ngx_tm_min, + tm.ngx_tm_sec); + } else { + return ngx_snprintf((char *) buf, + sizeof("Mon, 28-Sep-70 06:00:00 GMT"), + "%s, %02d-%s-%02d %02d:%02d:%02d GMT", + week[tm.ngx_tm_wday], + tm.ngx_tm_mday, + months[tm.ngx_tm_mon - 1], + tm.ngx_tm_year % 100, + tm.ngx_tm_hour, + tm.ngx_tm_min, + tm.ngx_tm_sec); + } +} + + +void ngx_gmtime(time_t t, ngx_tm_t *tp) +{ + ngx_int_t sec, min, hour, mday, mon, year, wday, yday, days; + + days = t / 86400; + + /* Jaunary 1, 1970 was Thursday */ + wday = (4 + days) % 7; + + t %= 86400; + hour = t / 3600; + t %= 3600; + min = t / 60; + sec = t % 60; + + /* the algorithm based on Gauss's formula */ + + days = days - (31 + 28) + 719527; + + year = days * 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); + + mon += 2; + + if (yday >= 306) { + + /* + * there is no "yday" in Win32 SYSTEMTIME + * + * yday -= 306; + */ + + year++; + mon -= 12; + + 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++; + * } + */ + } + + tp->ngx_tm_sec = (ngx_tm_sec_t) sec; + tp->ngx_tm_min = (ngx_tm_min_t) min; + tp->ngx_tm_hour = (ngx_tm_hour_t) hour; + tp->ngx_tm_mday = (ngx_tm_mday_t) mday; + tp->ngx_tm_mon = (ngx_tm_mon_t) mon; + tp->ngx_tm_year = (ngx_tm_year_t) year; + tp->ngx_tm_wday = (ngx_tm_wday_t) wday; +} diff --git a/src/core/ngx_times.h b/src/core/ngx_times.h new file mode 100644 --- /dev/null +++ b/src/core/ngx_times.h @@ -0,0 +1,58 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#ifndef _NGX_TIMES_H_INCLUDED_ +#define _NGX_TIMES_H_INCLUDED_ + + +#include +#include + + +void ngx_time_init(); +void ngx_time_update(time_t s); +size_t ngx_http_time(u_char *buf, time_t t); +size_t ngx_http_cookie_time(u_char *buf, time_t t); +void ngx_gmtime(time_t t, ngx_tm_t *tp); + +#if (NGX_THREADS) +ngx_int_t ngx_time_mutex_init(ngx_log_t *log); +#endif + +#if (NGX_THREADS && (TIME_T_SIZE > SIG_ATOMIC_T_SIZE)) + +#define ngx_time() *ngx_cached_time +extern volatile time_t *ngx_cached_time; + +#else + +#define ngx_time() ngx_cached_time +extern volatile time_t ngx_cached_time; + +#endif + + +extern ngx_thread_volatile ngx_str_t ngx_cached_err_log_time; +extern ngx_thread_volatile ngx_str_t ngx_cached_http_time; +extern ngx_thread_volatile ngx_str_t ngx_cached_http_log_time; + +extern ngx_epoch_msec_t ngx_start_msec; + +/* + * msecs elapsed since ngx_start_msec in the current event cycle, + * used in ngx_event_add_timer() and ngx_event_find_timer() + */ +extern ngx_epoch_msec_t ngx_elapsed_msec; + +/* + * msecs elapsed since ngx_start_msec in the previous event cycle, + * used in ngx_event_expire_timers() + */ +extern ngx_epoch_msec_t ngx_old_elapsed_msec; + + + +#endif /* _NGX_TIMES_H_INCLUDED_ */ diff --git a/src/event/modules/ngx_aio_module.c b/src/event/modules/ngx_aio_module.c new file mode 100644 --- /dev/null +++ b/src/event/modules/ngx_aio_module.c @@ -0,0 +1,209 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include +#include + +#if (HAVE_KQUEUE) +#include +#endif + + +static int ngx_aio_init(ngx_cycle_t *cycle); +static void ngx_aio_done(ngx_cycle_t *cycle); +static int ngx_aio_add_event(ngx_event_t *ev, int event, u_int flags); +static int ngx_aio_del_event(ngx_event_t *ev, int event, u_int flags); +static int ngx_aio_del_connection(ngx_connection_t *c, u_int flags); +static int ngx_aio_process_events(ngx_cycle_t *cycle); + + +ngx_os_io_t ngx_os_aio = { + ngx_aio_read, + ngx_aio_read_chain, + ngx_aio_write, + ngx_aio_write_chain, + NGX_HAVE_ZEROCOPY +}; + + +static ngx_str_t aio_name = ngx_string("aio"); + +ngx_event_module_t ngx_aio_module_ctx = { + &aio_name, + NULL, /* create configuration */ + NULL, /* init configuration */ + + { + ngx_aio_add_event, /* add an event */ + ngx_aio_del_event, /* delete an event */ + NULL, /* enable an event */ + NULL, /* disable an event */ + NULL, /* add an connection */ + ngx_aio_del_connection, /* delete an connection */ + NULL, /* process the changes */ + ngx_aio_process_events, /* process the events */ + ngx_aio_init, /* init the events */ + ngx_aio_done /* done the events */ + } + +}; + +ngx_module_t ngx_aio_module = { + NGX_MODULE, + &ngx_aio_module_ctx, /* module context */ + NULL, /* module directives */ + NGX_EVENT_MODULE, /* module type */ + NULL, /* init module */ + NULL /* init process */ +}; + + + +#if (HAVE_KQUEUE) + +static int ngx_aio_init(ngx_cycle_t *cycle) +{ + if (ngx_kqueue_module_ctx.actions.init(cycle) == NGX_ERROR) { + return NGX_ERROR; + } + + ngx_io = ngx_os_aio; + + ngx_event_flags = NGX_USE_AIO_EVENT; + ngx_event_actions = ngx_aio_module_ctx.actions; + + + return NGX_OK; +} + + +static void ngx_aio_done(ngx_cycle_t *cycle) +{ + ngx_kqueue_module_ctx.actions.done(cycle); +} + + +/* The event adding and deleting are needed for the listening sockets */ + +static int ngx_aio_add_event(ngx_event_t *ev, int event, u_int flags) +{ + return ngx_kqueue_module_ctx.actions.add(ev, event, flags); +} + + +static int ngx_aio_del_event(ngx_event_t *ev, int event, u_int flags) +{ + return ngx_kqueue_module_ctx.actions.del(ev, event, flags); +} + + +static int ngx_aio_del_connection(ngx_connection_t *c, u_int flags) +{ + int rc; + + if (c->read->active == 0 && c->write->active == 0) { + return NGX_OK; + } + + rc = aio_cancel(c->fd, NULL); + + 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; + return NGX_OK; + } + + if (rc == AIO_ALLDONE) { + c->read->active = c->write->active = 0; + ngx_log_error(NGX_LOG_ALERT, c->log, 0, + "aio_cancel() returned AIO_ALLDONE"); + return NGX_OK; + } + + if (rc == -1) { + ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno, + "aio_cancel() failed"); + return NGX_ERROR; + } + + if (rc == AIO_NOTCANCELED) { + ngx_log_error(NGX_LOG_ALERT, c->log, 0, + "aio_cancel() returned AIO_NOTCANCELED"); + + return NGX_ERROR; + } + + return NGX_OK; +} + + +static int ngx_aio_process_events(ngx_cycle_t *cycle) +{ + return ngx_kqueue_module_ctx.actions.process_events(cycle); +} + +#endif /* HAVE_KQUEUE */ + + +#if 0 + +/* 1 */ +int ngx_posix_aio_process_events(ngx_log_t *log) +{ + listen via SIGIO; + aio_* via SIGxxx; + + sigsuspend()/sigwaitinfo()/sigtimedwait(); +} + +/* 2 */ +int ngx_posix_aio_process_events(ngx_log_t *log) +{ + unmask signal + + listen via SIGIO; + + /* BUG: SIGIO can be delivered before aio_*() */ + + aio_suspend()/aiowait()/aio_waitcomplete() with timeout + + mask signal + + if (ngx_socket_errno == NGX_EINTR) + look listen + select()/accept() nb listen sockets + else + aio +} + +/* 3 */ +int ngx_posix_aio_process_events(ngx_log_t *log) +{ +#if 0 + unmask signal + + /* BUG: AIO signal can be delivered before select() */ + + select(listen); + + mask signal +#endif + + pselect(listen, mask); + + if (ngx_socket_errno == NGX_EINTR) + look ready array +} + +void aio_sig_handler(int signo, siginfo_t *siginfo, void *context) +{ + push siginfo->si_value.sival_ptr +} + +#endif diff --git a/src/event/modules/ngx_aio_module.h b/src/event/modules/ngx_aio_module.h new file mode 100644 --- /dev/null +++ b/src/event/modules/ngx_aio_module.h @@ -0,0 +1,20 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#ifndef _NGX_AIO_MODULE_H_INCLUDED_ +#define _NGX_AIO_MODULE_H_INCLUDED_ + + +#include +#include +#include + + +int ngx_aio_init(int max_connections, ngx_log_t *log); +int ngx_aio_process_events(ngx_log_t *log); + + +#endif /* _NGX_AIO_MODULE_H_INCLUDED_ */ diff --git a/src/event/modules/ngx_devpoll_module.c b/src/event/modules/ngx_devpoll_module.c new file mode 100644 --- /dev/null +++ b/src/event/modules/ngx_devpoll_module.c @@ -0,0 +1,578 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include + + +#if (TEST_BUILD_DEVPOLL) + +/* Solaris declarations */ + +#define POLLREMOVE 0x0800 +#define DP_POLL 0xD001 + +struct dvpoll { + struct pollfd *dp_fds; + int dp_nfds; + int dp_timeout; +}; + +#endif + + +typedef struct { + u_int changes; + u_int events; +} ngx_devpoll_conf_t; + + +static int ngx_devpoll_init(ngx_cycle_t *cycle); +static void ngx_devpoll_done(ngx_cycle_t *cycle); +static int ngx_devpoll_add_event(ngx_event_t *ev, int event, u_int flags); +static int ngx_devpoll_del_event(ngx_event_t *ev, int event, u_int flags); +static int ngx_devpoll_set_event(ngx_event_t *ev, int event, u_int flags); +static int ngx_devpoll_process_events(ngx_cycle_t *cycle); + +static void *ngx_devpoll_create_conf(ngx_cycle_t *cycle); +static char *ngx_devpoll_init_conf(ngx_cycle_t *cycle, void *conf); + +static int dp = -1; +static struct pollfd *change_list, *event_list; +static u_int nchanges, max_changes, nevents; + +static ngx_event_t **change_index; + + +static ngx_str_t devpoll_name = ngx_string("/dev/poll"); + +static ngx_command_t ngx_devpoll_commands[] = { + + {ngx_string("devpoll_changes"), + NGX_EVENT_CONF|NGX_CONF_TAKE1, + ngx_conf_set_num_slot, + 0, + offsetof(ngx_devpoll_conf_t, changes), + NULL}, + + {ngx_string("devpoll_events"), + NGX_EVENT_CONF|NGX_CONF_TAKE1, + ngx_conf_set_num_slot, + 0, + offsetof(ngx_devpoll_conf_t, events), + NULL}, + + ngx_null_command +}; + + +ngx_event_module_t ngx_devpoll_module_ctx = { + &devpoll_name, + ngx_devpoll_create_conf, /* create configuration */ + ngx_devpoll_init_conf, /* init configuration */ + + { + ngx_devpoll_add_event, /* add an event */ + ngx_devpoll_del_event, /* delete an event */ + ngx_devpoll_add_event, /* enable an event */ + ngx_devpoll_del_event, /* disable an event */ + NULL, /* add an connection */ + NULL, /* delete an connection */ + NULL, /* process the changes */ + ngx_devpoll_process_events, /* process the events */ + ngx_devpoll_init, /* init the events */ + ngx_devpoll_done, /* done the events */ + } + +}; + +ngx_module_t ngx_devpoll_module = { + NGX_MODULE, + &ngx_devpoll_module_ctx, /* module context */ + ngx_devpoll_commands, /* module directives */ + NGX_EVENT_MODULE, /* module type */ + NULL, /* init module */ + NULL /* init process */ +}; + + +static int ngx_devpoll_init(ngx_cycle_t *cycle) +{ + size_t n; + ngx_devpoll_conf_t *dpcf; + + dpcf = ngx_event_get_conf(cycle->conf_ctx, ngx_devpoll_module); + + if (dp == -1) { + dp = open("/dev/poll", O_RDWR); + + if (dp == -1) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, + "open(/dev/poll) failed"); + return NGX_ERROR; + } + } + + if (max_changes < dpcf->changes) { + if (nchanges) { + n = nchanges * sizeof(struct pollfd); + if (write(dp, change_list, n) != (ssize_t) n) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "write(/dev/poll) failed"); + return NGX_ERROR; + } + + nchanges = 0; + } + + if (change_list) { + ngx_free(change_list); + } + + ngx_test_null(change_list, + ngx_alloc(sizeof(struct pollfd) * dpcf->changes, + cycle->log), + NGX_ERROR); + + if (change_index) { + ngx_free(change_index); + } + + ngx_test_null(change_index, + ngx_alloc(sizeof(ngx_event_t *) * dpcf->changes, + cycle->log), + NGX_ERROR); + } + + max_changes = dpcf->changes; + + if (nevents < dpcf->events) { + if (event_list) { + ngx_free(event_list); + } + + ngx_test_null(event_list, + ngx_alloc(sizeof(struct pollfd) * dpcf->events, + cycle->log), + NGX_ERROR); + } + + nevents = dpcf->events; + + ngx_io = ngx_os_io; + + ngx_event_actions = ngx_devpoll_module_ctx.actions; + + ngx_event_flags = NGX_USE_LEVEL_EVENT; + + return NGX_OK; +} + + +static void ngx_devpoll_done(ngx_cycle_t *cycle) +{ + if (close(dp) == -1) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "close(/dev/poll) failed"); + } + + dp = -1; + + ngx_free(change_list); + ngx_free(event_list); + ngx_free(change_index); + + change_list = NULL; + event_list = NULL; + change_index = NULL; + max_changes = 0; + nchanges = 0; + nevents = 0; +} + + +static int ngx_devpoll_add_event(ngx_event_t *ev, int event, u_int flags) +{ +#if (NGX_DEBUG) + ngx_connection_t *c; +#endif + +#if (NGX_READ_EVENT != POLLIN) + if (event == NGX_READ_EVENT) { + event = POLLOUT; +#if (NGX_WRITE_EVENT != POLLOUT) + } else { + event = POLLIN; +#endif + } +#endif + +#if (NGX_DEBUG) + c = ev->data; + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0, + "devpoll add event: fd:%d ev:%04X", c->fd, event); +#endif + + ev->active = 1; + return ngx_devpoll_set_event(ev, event, 0); +} + + +static int ngx_devpoll_del_event(ngx_event_t *ev, int event, u_int flags) +{ + ngx_event_t *e; + ngx_connection_t *c; + + c = ev->data; + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0, + "devpoll del event: fd:%d ev:%04X", c->fd, event); + + if (ngx_devpoll_set_event(ev, POLLREMOVE, flags) == NGX_ERROR) { + return NGX_ERROR; + } + + ev->active = 0; + + if (flags & NGX_CLOSE_EVENT) { + return NGX_OK; + } + + /* we need to restore the second event if it exists */ + + if (event == NGX_READ_EVENT) { + if (ev->accept) { + return NGX_OK; + } + + e = c->write; + event = POLLOUT; + + } else { + e = c->read; + event = POLLIN; + } + + if (e) { + return ngx_devpoll_set_event(e, event, 0); + } + + return NGX_OK; +} + + +static int ngx_devpoll_set_event(ngx_event_t *ev, int event, u_int flags) +{ + size_t n; + ngx_connection_t *c; + + c = ev->data; + + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ev->log, 0, + "devpoll fd:%d ev:%d fl:%d", c->fd, event, flags); + + if (nchanges >= max_changes) { + ngx_log_error(NGX_LOG_WARN, ev->log, 0, + "/dev/pool change list is filled up"); + + n = nchanges * sizeof(struct pollfd); + if (write(dp, change_list, n) != (ssize_t) n) { + ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno, + "write(/dev/poll) failed"); + return NGX_ERROR; + } + + nchanges = 0; + } + + change_list[nchanges].fd = c->fd; + change_list[nchanges].events = event; + change_list[nchanges].revents = 0; + + change_index[nchanges] = ev; + ev->index = nchanges; + + nchanges++; + + if (flags & NGX_CLOSE_EVENT) { + n = nchanges * sizeof(struct pollfd); + if (write(dp, change_list, n) != (ssize_t) n) { + ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno, + "write(/dev/poll) failed"); + return NGX_ERROR; + } + + nchanges = 0; + } + + return NGX_OK; +} + + +int ngx_devpoll_process_events(ngx_cycle_t *cycle) +{ + int events; + ngx_int_t i; + ngx_uint_t j, lock, accept_lock, expire; + size_t n; + ngx_msec_t timer; + ngx_err_t err; + ngx_cycle_t **old_cycle; + ngx_event_t *rev, *wev; + ngx_connection_t *c; + ngx_epoch_msec_t delta; + struct dvpoll dvp; + struct timeval tv; + + for ( ;; ) { + timer = ngx_event_find_timer(); + + if (timer != 0) { + break; + } + + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, + "devpoll expired timer"); + + ngx_event_expire_timers((ngx_msec_t) + (ngx_elapsed_msec - ngx_old_elapsed_msec)); + } + + /* NGX_TIMER_INFINITE == INFTIM */ + + if (timer == NGX_TIMER_INFINITE) { + expire = 0; + + } else { + expire = 1; + } + + ngx_old_elapsed_msec = ngx_elapsed_msec; + accept_lock = 0; + + if (ngx_accept_mutex) { + if (ngx_accept_disabled > 0) { + ngx_accept_disabled--; + + } else { + if (ngx_trylock_accept_mutex(cycle) == NGX_ERROR) { + return NGX_ERROR; + } + + if (ngx_accept_mutex_held) { + accept_lock = 1; + + } else if (timer == NGX_TIMER_INFINITE + || timer > ngx_accept_mutex_delay) + { + timer = ngx_accept_mutex_delay; + expire = 0; + } + } + } + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, + "devpoll timer: %d", timer); + + if (nchanges) { + n = nchanges * sizeof(struct pollfd); + if (write(dp, change_list, n) != (ssize_t) n) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "write(/dev/poll) failed"); + ngx_accept_mutex_unlock(); + return NGX_ERROR; + } + } + + dvp.dp_fds = event_list; + dvp.dp_nfds = nevents; + dvp.dp_timeout = timer; + events = ioctl(dp, DP_POLL, &dvp); + + if (events == -1) { + err = ngx_errno; + } else { + err = 0; + } + + nchanges = 0; + + ngx_gettimeofday(&tv); + ngx_time_update(tv.tv_sec); + + delta = ngx_elapsed_msec; + ngx_elapsed_msec = (ngx_epoch_msec_t) tv.tv_sec * 1000 + + tv.tv_usec / 1000 - ngx_start_msec; + + if (err) { + ngx_log_error((err == NGX_EINTR) ? NGX_LOG_INFO : NGX_LOG_ALERT, + cycle->log, err, "ioctl(DP_POLL) failed"); + ngx_accept_mutex_unlock(); + return NGX_ERROR; + } + + if (timer != NGX_TIMER_INFINITE) { + delta = ngx_elapsed_msec - delta; + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0, + "devpoll timer: %d, delta: %d", timer, (int) delta); + } else { + if (events == 0) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, + "ioctl(DP_POLL) returned no events without timeout"); + ngx_accept_mutex_unlock(); + return NGX_ERROR; + } + } + + if (ngx_mutex_lock(ngx_posted_events_mutex) == NGX_ERROR) { + ngx_accept_mutex_unlock(); + return NGX_ERROR; + } + + lock = 1; + + for (i = 0; i < events; i++) { + c = &ngx_cycle->connections[event_list[i].fd]; + + if (c->fd == -1) { + old_cycle = ngx_old_cycles.elts; + for (j = 0; j < ngx_old_cycles.nelts; j++) { + if (old_cycle[j] == NULL) { + continue; + } + c = &old_cycle[j]->connections[event_list[i].fd]; + if (c->fd != -1) { + break; + } + } + } + + if (c->fd == -1) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, "unknown cycle"); + exit(1); + } + + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, cycle->log, 0, + "devpoll: fd:%d, ev:%04X, rev:%04X", + event_list[i].fd, + event_list[i].events, event_list[i].revents); + + if (event_list[i].revents & (POLLERR|POLLHUP|POLLNVAL)) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, + "ioctl(DP_POLL) error fd:%d ev:%04X rev:%04X", + event_list[i].fd, + event_list[i].events, event_list[i].revents); + } + + if (event_list[i].revents & ~(POLLIN|POLLOUT|POLLERR|POLLHUP|POLLNVAL)) + { + ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, + "strange ioctl(DP_POLL) events " + "fd:%d ev:%04X rev:%04X", + event_list[i].fd, + event_list[i].events, event_list[i].revents); + } + + wev = c->write; + + if ((event_list[i].events & (POLLOUT|POLLERR|POLLHUP)) && wev->active) { + wev->ready = 1; + + if (!ngx_threaded && !ngx_accept_mutex_held) { + wev->event_handler(wev); + + } else { + ngx_post_event(wev); + } + } + + /* + * POLLIN must be handled after POLLOUT because we use + * the optimization to avoid the unnecessary mutex locking/unlocking + * if the accept event is the last one. + */ + + rev = c->read; + + if ((event_list[i].events & (POLLIN|POLLERR|POLLHUP)) && rev->active) { + rev->ready = 1; + + if (!ngx_threaded && !ngx_accept_mutex_held) { + rev->event_handler(rev); + + } else if (!rev->accept) { + ngx_post_event(rev); + + } else if (ngx_accept_disabled <= 0) { + ngx_mutex_unlock(ngx_posted_events_mutex); + + c->read->event_handler(rev); + + if (ngx_accept_disabled > 0) { + ngx_accept_mutex_unlock(); + accept_lock = 0; + } + + if (i + 1 == events) { + lock = 0; + break; + } + + if (ngx_mutex_lock(ngx_posted_events_mutex) == NGX_ERROR) { + if (accept_lock) { + ngx_accept_mutex_unlock(); + } + return NGX_ERROR; + } + } + } + } + + if (accept_lock) { + ngx_accept_mutex_unlock(); + } + + if (lock) { + ngx_mutex_unlock(ngx_posted_events_mutex); + } + + if (expire && delta) { + ngx_event_expire_timers((ngx_msec_t) delta); + } + + if (!ngx_threaded) { + ngx_event_process_posted(cycle); + } + + return NGX_OK; +} + + +static void *ngx_devpoll_create_conf(ngx_cycle_t *cycle) +{ + ngx_devpoll_conf_t *dpcf; + + ngx_test_null(dpcf, ngx_palloc(cycle->pool, sizeof(ngx_devpoll_conf_t)), + NGX_CONF_ERROR); + + dpcf->changes = NGX_CONF_UNSET; + dpcf->events = NGX_CONF_UNSET; + + return dpcf; +} + + +static char *ngx_devpoll_init_conf(ngx_cycle_t *cycle, void *conf) +{ + ngx_devpoll_conf_t *dpcf = conf; + + ngx_conf_init_unsigned_value(dpcf->changes, 512); + ngx_conf_init_unsigned_value(dpcf->events, 512); + + return NGX_CONF_OK; +} diff --git a/src/event/modules/ngx_epoll_module.c b/src/event/modules/ngx_epoll_module.c new file mode 100644 --- /dev/null +++ b/src/event/modules/ngx_epoll_module.c @@ -0,0 +1,657 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include + + +#if (TEST_BUILD_EPOLL) + +/* epoll declarations */ + +#define EPOLLIN 0x001 +#define EPOLLPRI 0x002 +#define EPOLLOUT 0x004 +#define EPOLLRDNORM 0x040 +#define EPOLLRDBAND 0x080 +#define EPOLLWRNORM 0x100 +#define EPOLLWRBAND 0x200 +#define EPOLLMSG 0x400 +#define EPOLLERR 0x008 +#define EPOLLHUP 0x010 + +#define EPOLLET 0x80000000 +#define EPOLLONESHOT 0x40000000 + +#define EPOLL_CTL_ADD 1 +#define EPOLL_CTL_DEL 2 +#define EPOLL_CTL_MOD 3 + +typedef union epoll_data { + void *ptr; + int fd; + uint32_t u32; + uint64_t u64; +} epoll_data_t; + +struct epoll_event { + uint32_t events; + 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; +} + +int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event) +{ + return -1; +} + +int epoll_wait(int epfd, struct epoll_event *events, int nevents, int timeout) +{ + return -1; +} + +#endif + + +typedef struct { + u_int events; +} ngx_epoll_conf_t; + + +static int ngx_epoll_init(ngx_cycle_t *cycle); +static void ngx_epoll_done(ngx_cycle_t *cycle); +static int ngx_epoll_add_event(ngx_event_t *ev, int event, u_int flags); +static int ngx_epoll_del_event(ngx_event_t *ev, int event, u_int flags); +static int ngx_epoll_add_connection(ngx_connection_t *c); +static int ngx_epoll_del_connection(ngx_connection_t *c, u_int flags); +static int ngx_epoll_process_events(ngx_cycle_t *cycle); + +static void *ngx_epoll_create_conf(ngx_cycle_t *cycle); +static char *ngx_epoll_init_conf(ngx_cycle_t *cycle, void *conf); + +static int ep = -1; +static struct epoll_event *event_list; +static u_int nevents; + + +static ngx_str_t epoll_name = ngx_string("epoll"); + +static ngx_command_t ngx_epoll_commands[] = { + + {ngx_string("epoll_events"), + NGX_EVENT_CONF|NGX_CONF_TAKE1, + ngx_conf_set_num_slot, + 0, + offsetof(ngx_epoll_conf_t, events), + NULL}, + + ngx_null_command +}; + + +ngx_event_module_t ngx_epoll_module_ctx = { + &epoll_name, + ngx_epoll_create_conf, /* create configuration */ + ngx_epoll_init_conf, /* init configuration */ + + { + ngx_epoll_add_event, /* add an event */ + ngx_epoll_del_event, /* delete an event */ + ngx_epoll_add_event, /* enable an event */ + ngx_epoll_del_event, /* disable an event */ + ngx_epoll_add_connection, /* add an connection */ + ngx_epoll_del_connection, /* delete an connection */ + NULL, /* process the changes */ + ngx_epoll_process_events, /* process the events */ + ngx_epoll_init, /* init the events */ + ngx_epoll_done, /* done the events */ + } +}; + +ngx_module_t ngx_epoll_module = { + NGX_MODULE, + &ngx_epoll_module_ctx, /* module context */ + ngx_epoll_commands, /* module directives */ + NGX_EVENT_MODULE, /* module type */ + NULL, /* init module */ + NULL /* init process */ +}; + + +static int ngx_epoll_init(ngx_cycle_t *cycle) +{ + size_t n; + 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); + + if (ep == -1) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, + "epoll_create() failed"); + return NGX_ERROR; + } + } + + if (nevents < epcf->events) { + if (event_list) { + ngx_free(event_list); + } + + event_list = ngx_alloc(sizeof(struct epoll_event) * epcf->events, + cycle->log); + if (event_list == NULL) { + return NGX_ERROR; + } + } + + nevents = epcf->events; + + ngx_io = ngx_os_io; + + ngx_event_actions = ngx_epoll_module_ctx.actions; + +#if (HAVE_CLEAR_EVENT) + ngx_event_flags = NGX_USE_CLEAR_EVENT +#else + ngx_event_flags = NGX_USE_LEVEL_EVENT +#endif + |NGX_HAVE_GREEDY_EVENT + |NGX_USE_EPOLL_EVENT; + + return NGX_OK; +} + + +static void ngx_epoll_done(ngx_cycle_t *cycle) +{ + if (close(ep) == -1) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "epoll close() failed"); + } + + ep = -1; + + ngx_free(event_list); + + event_list = NULL; + nevents = 0; +} + + +static int ngx_epoll_add_event(ngx_event_t *ev, int event, u_int flags) +{ + int op, prev; + ngx_event_t *e; + ngx_connection_t *c; + struct epoll_event ee; + + c = ev->data; + + if (event == NGX_READ_EVENT) { + e = c->write; + prev = EPOLLOUT; +#if (NGX_READ_EVENT != EPOLLIN) + event = EPOLLIN; +#endif + + } else { + e = c->read; + prev = EPOLLIN; +#if (NGX_WRITE_EVENT != EPOLLOUT) + event = EPOLLOUT; +#endif + } + + if (e->active) { + op = EPOLL_CTL_MOD; + event |= prev; + + } else { + op = EPOLL_CTL_ADD; + } + + ee.events = event | flags; + ee.data.ptr = (void *) ((uintptr_t) c | ev->instance); + + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ev->log, 0, + "epoll add event: fd:%d op:%d ev:%08X", + c->fd, op, ee.events); + + if (epoll_ctl(ep, op, c->fd, &ee) == -1) { + ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno, + "epoll_ctl(%d, %d) failed", op, c->fd); + return NGX_ERROR; + } + + ev->active = 1; +#if 0 + ev->oneshot = (flags & NGX_ONESHOT_EVENT) ? 1 : 0; +#endif + + return NGX_OK; +} + + +static int ngx_epoll_del_event(ngx_event_t *ev, int event, u_int flags) +{ + int op, prev; + ngx_event_t *e; + ngx_connection_t *c; + struct epoll_event ee; + + /* + * when the file descriptor is closed the epoll automatically deletes + * it from its queue so we do not need to delete explicity the event + * before the closing the file descriptor + */ + + if (flags & NGX_CLOSE_EVENT) { + ev->active = 0; + return NGX_OK; + } + + c = ev->data; + + if (event == NGX_READ_EVENT) { + e = c->write; + prev = EPOLLOUT; + + } else { + e = c->read; + prev = EPOLLIN; + } + + if (e->active) { + op = EPOLL_CTL_MOD; + ee.events = prev | flags; + ee.data.ptr = (void *) ((uintptr_t) c | ev->instance); + + } else { + op = EPOLL_CTL_DEL; + ee.events = 0; + ee.data.ptr = NULL; + } + + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ev->log, 0, + "epoll del event: fd:%d op:%d ev:%08X", + c->fd, op, ee.events); + + if (epoll_ctl(ep, op, c->fd, &ee) == -1) { + ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno, + "epoll_ctl(%d, %d) failed", op, c->fd); + return NGX_ERROR; + } + + ev->active = 0; + + return NGX_OK; +} + + +static int ngx_epoll_add_connection(ngx_connection_t *c) +{ + struct epoll_event ee; + + ee.events = EPOLLIN|EPOLLOUT|EPOLLET; + ee.data.ptr = (void *) ((uintptr_t) c | c->read->instance); + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, + "epoll add connection: fd:%d ev:%08X", c->fd, ee.events); + + if (epoll_ctl(ep, EPOLL_CTL_ADD, c->fd, &ee) == -1) { + ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno, + "epoll_ctl(EPOLL_CTL_ADD, %d) failed", c->fd); + return NGX_ERROR; + } + + c->read->active = 1; + c->write->active = 1; + + return NGX_OK; +} + + +static int ngx_epoll_del_connection(ngx_connection_t *c, u_int flags) +{ + int op; + struct epoll_event ee; + + /* + * when the file descriptor is closed the epoll automatically deletes + * it from its queue so we do not need to delete explicity the event + * before the closing the file descriptor + */ + + if (flags & NGX_CLOSE_EVENT) { + c->read->active = 0; + c->write->active = 0; + return NGX_OK; + } + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, + "epoll del connection: fd:%d", c->fd); + + op = EPOLL_CTL_DEL; + ee.events = 0; + ee.data.ptr = NULL; + + if (epoll_ctl(ep, op, c->fd, &ee) == -1) { + ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno, + "epoll_ctl(%d, %d) failed", op, c->fd); + return NGX_ERROR; + } + + c->read->active = 0; + c->write->active = 0; + + return NGX_OK; +} + + +int ngx_epoll_process_events(ngx_cycle_t *cycle) +{ + int events; + size_t n; + ngx_int_t instance, i; + ngx_uint_t lock, accept_lock, expire; + ngx_err_t err; + ngx_log_t *log; + ngx_msec_t timer; + ngx_event_t *rev, *wev; + struct timeval tv; + ngx_connection_t *c; + ngx_epoch_msec_t delta; + + for ( ;; ) { + timer = ngx_event_find_timer(); + +#if (NGX_THREADS) + + if (timer == NGX_TIMER_ERROR) { + return NGX_ERROR; + } + + if (timer == NGX_TIMER_INFINITE || timer > 500) { + timer = 500; + break; + } + +#endif + + if (timer != 0) { + break; + } + + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, + "epoll expired timer"); + + ngx_event_expire_timers((ngx_msec_t) + (ngx_elapsed_msec - ngx_old_elapsed_msec)); + + if (ngx_posted_events && ngx_threaded) { + ngx_wakeup_worker_thread(cycle); + } + } + + /* NGX_TIMER_INFINITE == INFTIM */ + + if (timer == NGX_TIMER_INFINITE) { + expire = 0; + + } else { + expire = 1; + } + + ngx_old_elapsed_msec = ngx_elapsed_msec; + accept_lock = 0; + + if (ngx_accept_mutex) { + if (ngx_accept_disabled > 0) { + ngx_accept_disabled--; + + } else { + if (ngx_trylock_accept_mutex(cycle) == NGX_ERROR) { + return NGX_ERROR; + } + + if (ngx_accept_mutex_held) { + accept_lock = 1; + + } else if (timer == NGX_TIMER_INFINITE + || timer > ngx_accept_mutex_delay) + { + timer = ngx_accept_mutex_delay; + expire = 0; + } + } + } + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, + "epoll timer: %d", timer); + + events = epoll_wait(ep, event_list, nevents, timer); + + if (events == -1) { + err = ngx_errno; + } else { + err = 0; + } + + ngx_gettimeofday(&tv); + ngx_time_update(tv.tv_sec); + + delta = ngx_elapsed_msec; + ngx_elapsed_msec = (ngx_epoch_msec_t) tv.tv_sec * 1000 + + tv.tv_usec / 1000 - ngx_start_msec; + + if (timer != NGX_TIMER_INFINITE) { + delta = ngx_elapsed_msec - delta; + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0, + "epoll timer: %d, delta: %d", timer, (int) delta); + } else { + if (events == 0) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, + "epoll_wait() returned no events without timeout"); + ngx_accept_mutex_unlock(); + return NGX_ERROR; + } + } + + if (err) { + ngx_log_error((err == NGX_EINTR) ? NGX_LOG_INFO : NGX_LOG_ALERT, + cycle->log, err, "epoll_wait() failed"); + ngx_accept_mutex_unlock(); + return NGX_ERROR; + } + + if (events > 0) { + if (ngx_mutex_lock(ngx_posted_events_mutex) == NGX_ERROR) { + ngx_accept_mutex_unlock(); + return NGX_ERROR; + } + + lock = 1; + + } else { + lock =0; + } + + log = cycle->log; + + for (i = 0; i < events; i++) { + c = event_list[i].data.ptr; + + instance = (uintptr_t) c & 1; + c = (ngx_connection_t *) ((uintptr_t) c & (uintptr_t) ~1); + + rev = c->read; + + if (c->fd == -1 || rev->instance != instance) { + + /* + * the stale event from a file descriptor + * that was just closed in this iteration + */ + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, + "epoll: stale event " PTR_FMT, c); + continue; + } + +#if (NGX_DEBUG0) + log = c->log ? c->log : cycle->log; +#endif + + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, log, 0, + "epoll: fd:%d ev:%04X d:" PTR_FMT, + c->fd, event_list[i].events, event_list[i].data); + + if (event_list[i].events & (EPOLLERR|EPOLLHUP)) { + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, log, 0, + "epoll_wait() error on fd:%d ev:%04X", + c->fd, event_list[i].events); + } + + if (event_list[i].events & ~(EPOLLIN|EPOLLOUT|EPOLLERR|EPOLLHUP)) { + ngx_log_error(NGX_LOG_ALERT, log, 0, + "strange epoll_wait() events fd:%d ev:%04X", + c->fd, event_list[i].events); + } + + wev = c->write; + + if ((event_list[i].events & (EPOLLOUT|EPOLLERR|EPOLLHUP)) + && wev->active) + { + if (ngx_threaded) { + wev->posted_ready = 1; + ngx_post_event(wev); + + } else { + wev->ready = 1; + + if (!ngx_accept_mutex_held) { + wev->event_handler(wev); + + } else { + ngx_post_event(wev); + } + } + } + + /* + * EPOLLIN must be handled after EPOLLOUT because we use + * the optimization to avoid the unnecessary mutex locking/unlocking + * if the accept event is the last one. + */ + + if ((event_list[i].events & (EPOLLIN|EPOLLERR|EPOLLHUP)) + && rev->active) + { + if (ngx_threaded && !rev->accept) { + rev->posted_ready = 1; + + ngx_post_event(rev); + + continue; + } + + rev->ready = 1; + + if (!ngx_threaded && !ngx_accept_mutex_held) { + rev->event_handler(rev); + + } else if (!rev->accept) { + ngx_post_event(rev); + + } else if (ngx_accept_disabled <= 0) { + + ngx_mutex_unlock(ngx_posted_events_mutex); + + rev->event_handler(rev); + + if (ngx_accept_disabled > 0) { + ngx_accept_mutex_unlock(); + accept_lock = 0; + } + + if (i + 1 == events) { + lock = 0; + break; + } + + if (ngx_mutex_lock(ngx_posted_events_mutex) == NGX_ERROR) { + if (accept_lock) { + ngx_accept_mutex_unlock(); + } + return NGX_ERROR; + } + } + } + } + + if (accept_lock) { + ngx_accept_mutex_unlock(); + } + + if (lock) { + ngx_mutex_unlock(ngx_posted_events_mutex); + } + + if (expire && delta) { + ngx_event_expire_timers((ngx_msec_t) delta); + } + + if (ngx_posted_events) { + if (ngx_threaded) { + ngx_wakeup_worker_thread(cycle); + + } else { + ngx_event_process_posted(cycle); + } + } + + return NGX_OK; +} + + +static void *ngx_epoll_create_conf(ngx_cycle_t *cycle) +{ + ngx_epoll_conf_t *epcf; + + ngx_test_null(epcf, ngx_palloc(cycle->pool, sizeof(ngx_epoll_conf_t)), + NGX_CONF_ERROR); + + epcf->events = NGX_CONF_UNSET; + + return epcf; +} + + +static char *ngx_epoll_init_conf(ngx_cycle_t *cycle, void *conf) +{ + ngx_epoll_conf_t *epcf = conf; + + ngx_conf_init_unsigned_value(epcf->events, 512); + + return NGX_CONF_OK; +} diff --git a/src/event/modules/ngx_kqueue_module.c b/src/event/modules/ngx_kqueue_module.c new file mode 100644 --- /dev/null +++ b/src/event/modules/ngx_kqueue_module.c @@ -0,0 +1,800 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include +#include + + +typedef struct { + int changes; + int events; +} ngx_kqueue_conf_t; + + +static ngx_int_t ngx_kqueue_init(ngx_cycle_t *cycle); +static void ngx_kqueue_done(ngx_cycle_t *cycle); +static ngx_int_t ngx_kqueue_add_event(ngx_event_t *ev, int event, u_int flags); +static ngx_int_t ngx_kqueue_del_event(ngx_event_t *ev, int event, u_int flags); +static ngx_int_t ngx_kqueue_set_event(ngx_event_t *ev, int filter, u_int flags); +static ngx_int_t ngx_kqueue_process_changes(ngx_cycle_t *cycle, ngx_uint_t try); +static ngx_int_t ngx_kqueue_process_events(ngx_cycle_t *cycle); +static ngx_inline void ngx_kqueue_dump_event(ngx_log_t *log, + struct kevent *kev); + +static void *ngx_kqueue_create_conf(ngx_cycle_t *cycle); +static char *ngx_kqueue_init_conf(ngx_cycle_t *cycle, void *conf); + + +int ngx_kqueue = -1; + +/* + * The "change_list" should be declared as ngx_thread_volatile. + * However, the use of the change_list is localized in kqueue functions and + * is protected by the mutex so even the "icc -ipo" should not build the code + * with the race condition. Thus we avoid the declaration to make a more + * readable code. + */ + +static struct kevent *change_list, *change_list0, *change_list1; +static struct kevent *event_list; +static int max_changes, nchanges, nevents; + +#if (NGX_THREADS) +static ngx_mutex_t *list_mutex; +static ngx_mutex_t *kevent_mutex; +#endif + + + +static ngx_str_t kqueue_name = ngx_string("kqueue"); + +static ngx_command_t ngx_kqueue_commands[] = { + + {ngx_string("kqueue_changes"), + NGX_EVENT_CONF|NGX_CONF_TAKE1, + ngx_conf_set_num_slot, + 0, + offsetof(ngx_kqueue_conf_t, changes), + NULL}, + + {ngx_string("kqueue_events"), + NGX_EVENT_CONF|NGX_CONF_TAKE1, + ngx_conf_set_num_slot, + 0, + offsetof(ngx_kqueue_conf_t, events), + NULL}, + + ngx_null_command +}; + + +ngx_event_module_t ngx_kqueue_module_ctx = { + &kqueue_name, + ngx_kqueue_create_conf, /* create configuration */ + ngx_kqueue_init_conf, /* init configuration */ + + { + ngx_kqueue_add_event, /* add an event */ + ngx_kqueue_del_event, /* delete an event */ + ngx_kqueue_add_event, /* enable an event */ + ngx_kqueue_del_event, /* disable an event */ + NULL, /* add an connection */ + NULL, /* delete an connection */ + ngx_kqueue_process_changes, /* process the changes */ + ngx_kqueue_process_events, /* process the events */ + ngx_kqueue_init, /* init the events */ + ngx_kqueue_done /* done the events */ + } + +}; + +ngx_module_t ngx_kqueue_module = { + NGX_MODULE, + &ngx_kqueue_module_ctx, /* module context */ + ngx_kqueue_commands, /* module directives */ + NGX_EVENT_MODULE, /* module type */ + NULL, /* init module */ + NULL /* init process */ +}; + + + +static ngx_int_t ngx_kqueue_init(ngx_cycle_t *cycle) +{ + struct timespec ts; + ngx_kqueue_conf_t *kcf; + + kcf = ngx_event_get_conf(cycle->conf_ctx, ngx_kqueue_module); + + if (ngx_kqueue == -1) { + ngx_kqueue = kqueue(); + + if (ngx_kqueue == -1) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, + "kqueue() failed"); + return NGX_ERROR; + } + +#if (NGX_THREADS) + + if (!(list_mutex = ngx_mutex_init(cycle->log, 0))) { + return NGX_ERROR; + } + + if (!(kevent_mutex = ngx_mutex_init(cycle->log, 0))) { + return NGX_ERROR; + } + +#endif + } + + if (max_changes < kcf->changes) { + if (nchanges) { + ts.tv_sec = 0; + ts.tv_nsec = 0; + + if (kevent(ngx_kqueue, change_list, nchanges, NULL, 0, &ts) == -1) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "kevent() failed"); + return NGX_ERROR; + } + nchanges = 0; + } + + if (change_list0) { + ngx_free(change_list0); + } + + change_list0 = ngx_alloc(kcf->changes * sizeof(struct kevent), + cycle->log); + if (change_list0 == NULL) { + return NGX_ERROR; + } + + if (change_list1) { + ngx_free(change_list1); + } + + change_list1 = ngx_alloc(kcf->changes * sizeof(struct kevent), + cycle->log); + if (change_list1 == NULL) { + return NGX_ERROR; + } + + change_list = change_list0; + } + + max_changes = kcf->changes; + + if (nevents < kcf->events) { + if (event_list) { + ngx_free(event_list); + } + + event_list = ngx_alloc(kcf->events * sizeof(struct kevent), cycle->log); + if (event_list == NULL) { + return NGX_ERROR; + } + } + + nevents = kcf->events; + + ngx_io = ngx_os_io; + + ngx_event_actions = ngx_kqueue_module_ctx.actions; + + ngx_event_flags = NGX_USE_ONESHOT_EVENT +#if (HAVE_CLEAR_EVENT) + |NGX_USE_CLEAR_EVENT +#else + |NGX_USE_LEVEL_EVENT +#endif +#if (HAVE_LOWAT_EVENT) + |NGX_HAVE_LOWAT_EVENT +#endif + |NGX_HAVE_KQUEUE_EVENT; + + return NGX_OK; +} + + +static void ngx_kqueue_done(ngx_cycle_t *cycle) +{ + if (close(ngx_kqueue) == -1) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "kqueue close() failed"); + } + + ngx_kqueue = -1; + +#if (NGX_THREADS) + ngx_mutex_destroy(kevent_mutex); + ngx_mutex_destroy(list_mutex); +#endif + + ngx_free(change_list1); + ngx_free(change_list0); + ngx_free(event_list); + + change_list1 = NULL; + change_list0 = NULL; + change_list = NULL; + event_list = NULL; + max_changes = 0; + nchanges = 0; + nevents = 0; +} + + +static ngx_int_t ngx_kqueue_add_event(ngx_event_t *ev, int event, u_int flags) +{ + ngx_int_t rc; + ngx_event_t *e; + ngx_connection_t *c; + + ev->active = 1; + ev->disabled = 0; + ev->oneshot = (flags & NGX_ONESHOT_EVENT) ? 1 : 0; + + if (ngx_mutex_lock(list_mutex) == NGX_ERROR) { + return NGX_ERROR; + } + + if (nchanges > 0 + && ev->index < (u_int) nchanges + && ((uintptr_t) change_list[ev->index].udata & (uintptr_t) ~1) + == (uintptr_t) ev) + { + if (change_list[ev->index].flags == EV_DISABLE) { + + /* + * if the EV_DISABLE is still not passed to a kernel + * we will not pass it + */ + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0, + "kevent activated: %d: ft:%d", + ngx_event_ident(ev->data), event); + + if (ev->index < (u_int) --nchanges) { + e = (ngx_event_t *) change_list[nchanges].udata; + change_list[ev->index] = change_list[nchanges]; + e->index = ev->index; + } + + ngx_mutex_unlock(list_mutex); + + return NGX_OK; + } + + c = ev->data; + + ngx_log_error(NGX_LOG_ALERT, ev->log, 0, + "previous event on #%d were not passed in kernel", c->fd); + + ngx_mutex_unlock(list_mutex); + + return NGX_ERROR; + } + + rc = ngx_kqueue_set_event(ev, event, EV_ADD|EV_ENABLE|flags); + + ngx_mutex_unlock(list_mutex); + + return rc; +} + + +static ngx_int_t ngx_kqueue_del_event(ngx_event_t *ev, int event, u_int flags) +{ + ngx_int_t rc; + ngx_event_t *e; + + ev->active = 0; + ev->disabled = 0; + + if (ngx_mutex_lock(list_mutex) == NGX_ERROR) { + return NGX_ERROR; + } + + if (nchanges > 0 + && ev->index < (u_int) nchanges + && ((uintptr_t) change_list[ev->index].udata & (uintptr_t) ~1) + == (uintptr_t) ev) + { + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0, + "kevent deleted: %d: ft:%d", + ngx_event_ident(ev->data), event); + + /* if the event is still not passed to a kernel we will not pass it */ + + if (ev->index < (u_int) --nchanges) { + e = (ngx_event_t *) change_list[nchanges].udata; + change_list[ev->index] = change_list[nchanges]; + e->index = ev->index; + } + + ngx_mutex_unlock(list_mutex); + + return NGX_OK; + } + + /* + * when the file descriptor is closed the kqueue automatically deletes + * its filters so we do not need to delete explicity the event + * before the closing the file descriptor. + */ + + if (flags & NGX_CLOSE_EVENT) { + ngx_mutex_unlock(list_mutex); + return NGX_OK; + } + + if (flags & NGX_DISABLE_EVENT) { + ev->disabled = 1; + } + + rc = ngx_kqueue_set_event(ev, event, + flags & NGX_DISABLE_EVENT ? EV_DISABLE : EV_DELETE); + + ngx_mutex_unlock(list_mutex); + + return rc; +} + + +static ngx_int_t ngx_kqueue_set_event(ngx_event_t *ev, int filter, u_int flags) +{ + struct kevent *kev; + struct timespec ts; + ngx_connection_t *c; + + c = ev->data; + + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, + "kevent set event: %d: ft:%d fl:%04X", + c->fd, filter, flags); + + if (nchanges >= max_changes) { + ngx_log_error(NGX_LOG_WARN, ev->log, 0, + "kqueue change list is filled up"); + + ts.tv_sec = 0; + ts.tv_nsec = 0; + + if (kevent(ngx_kqueue, change_list, nchanges, NULL, 0, &ts) == -1) { + ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno, "kevent() failed"); + return NGX_ERROR; + } + + nchanges = 0; + } + + kev = &change_list[nchanges]; + + kev->ident = c->fd; + kev->filter = filter; + kev->flags = flags; + kev->udata = (void *) ((uintptr_t) ev | ev->instance); + + if (filter == EVFILT_VNODE) { + kev->fflags = NOTE_DELETE|NOTE_WRITE|NOTE_EXTEND + |NOTE_ATTRIB|NOTE_RENAME +#if (__FreeBSD__ == 4 && __FreeBSD_version >= 430000) \ + || __FreeBSD_version >= 500018 + |NOTE_REVOKE +#endif + ; + kev->data = 0; + + } else { +#if (HAVE_LOWAT_EVENT) + if (flags & NGX_LOWAT_EVENT) { + kev->fflags = NOTE_LOWAT; + kev->data = ev->available; + + } else { + kev->fflags = 0; + kev->data = 0; + } +#else + kev->fflags = 0; + kev->data = 0; +#endif + } + + ev->index = nchanges; + nchanges++; + + return NGX_OK; +} + + +static ngx_int_t ngx_kqueue_process_events(ngx_cycle_t *cycle) +{ + int events, n; + ngx_int_t i, instance; + ngx_uint_t lock, accept_lock, expire; + ngx_err_t err; + ngx_msec_t timer; + ngx_event_t *ev; + ngx_epoch_msec_t delta; + struct timeval tv; + struct timespec ts, *tp; + + for ( ;; ) { + timer = ngx_event_find_timer(); + +#if (NGX_THREADS) + + if (timer == NGX_TIMER_ERROR) { + return NGX_ERROR; + } + + if (timer == NGX_TIMER_INFINITE || timer > 500) { + timer = 500; + break; + } + +#endif + + if (timer != 0) { + break; + } + + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, + "kevent expired timer"); + + ngx_event_expire_timers((ngx_msec_t) + (ngx_elapsed_msec - ngx_old_elapsed_msec)); + + if (ngx_posted_events && ngx_threaded) { + ngx_wakeup_worker_thread(cycle); + } + } + + ngx_old_elapsed_msec = ngx_elapsed_msec; + expire = 1; + accept_lock = 0; + + if (ngx_accept_mutex) { + if (ngx_accept_disabled > 0) { + ngx_accept_disabled--; + + } else { + if (ngx_trylock_accept_mutex(cycle) == NGX_ERROR) { + return NGX_ERROR; + } + + if (ngx_accept_mutex_held) { + accept_lock = 1; + + } else if (timer == NGX_TIMER_INFINITE + || timer > ngx_accept_mutex_delay) + { + timer = ngx_accept_mutex_delay; + expire = 0; + } + } + } + + if (ngx_threaded) { + if (ngx_kqueue_process_changes(cycle, 0) == NGX_ERROR) { + ngx_accept_mutex_unlock(); + return NGX_ERROR; + } + + n = 0; + + } else { + n = nchanges; + nchanges = 0; + } + + if (timer == NGX_TIMER_INFINITE) { + tp = NULL; + expire = 0; + + } else { + ts.tv_sec = timer / 1000; + ts.tv_nsec = (timer % 1000) * 1000000; + tp = &ts; + } + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0, + "kevent timer: %d, changes: %d", timer, n); + + events = kevent(ngx_kqueue, change_list, n, event_list, nevents, tp); + + if (events == -1) { + err = ngx_errno; + } else { + err = 0; + } + + ngx_gettimeofday(&tv); + ngx_time_update(tv.tv_sec); + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, + "kevent events: %d", events); + + delta = ngx_elapsed_msec; + ngx_elapsed_msec = (ngx_epoch_msec_t) tv.tv_sec * 1000 + + tv.tv_usec / 1000 - ngx_start_msec; + + if (err) { + ngx_log_error((err == NGX_EINTR) ? NGX_LOG_INFO : NGX_LOG_ALERT, + cycle->log, err, "kevent() failed"); + ngx_accept_mutex_unlock(); + return NGX_ERROR; + } + + if (timer != NGX_TIMER_INFINITE) { + delta = ngx_elapsed_msec - delta; + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0, + "kevent timer: %d, delta: %d", timer, (int) delta); + + } else { + if (events == 0) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, + "kevent() returned no events without timeout"); + ngx_accept_mutex_unlock(); + return NGX_ERROR; + } + } + + if (events > 0) { + if (ngx_mutex_lock(ngx_posted_events_mutex) == NGX_ERROR) { + ngx_accept_mutex_unlock(); + return NGX_ERROR; + } + + lock = 1; + + } else { + lock =0; + } + + for (i = 0; i < events; i++) { + + ngx_kqueue_dump_event(cycle->log, &event_list[i]); + + if (event_list[i].flags & EV_ERROR) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, event_list[i].data, + "kevent() error on %d", event_list[i].ident); + continue; + } + + ev = (ngx_event_t *) event_list[i].udata; + + switch (event_list[i].filter) { + + case EVFILT_READ: + case EVFILT_WRITE: + + instance = (uintptr_t) ev & 1; + ev = (ngx_event_t *) ((uintptr_t) ev & (uintptr_t) ~1); + + if (ev->closed || ev->instance != instance) { + + /* + * the stale event from a file descriptor + * that was just closed in this iteration + */ + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, + "kevent: stale event " PTR_FMT, ev); + continue; + } + + if (ev->log && (ev->log->log_level & NGX_LOG_DEBUG_CONNECTION)) { + ngx_kqueue_dump_event(ev->log, &event_list[i]); + } + +#if (NGX_THREADS) + + if (ngx_threaded && !ev->accept) { + ev->posted_ready = 1; + ev->posted_available = event_list[i].data; + + if (event_list[i].flags & EV_EOF) { + ev->posted_eof = 1; + ev->posted_errno = event_list[i].fflags; + } + + ngx_post_event(ev); + + continue; + } + +#endif + + ev->available = event_list[i].data; + + if (event_list[i].flags & EV_EOF) { + ev->pending_eof = 1; + ev->kq_errno = event_list[i].fflags; + } + + ev->ready = 1; + + break; + + case EVFILT_VNODE: + ev->kq_vnode = 1; + + break; + + case EVFILT_AIO: + ev->complete = 1; + ev->ready = 1; + + break; + + default: + ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, + "unexpected kevent() filter %d", + event_list[i].filter); + continue; + } + + if (!ngx_threaded && !ngx_accept_mutex_held) { + ev->event_handler(ev); + continue; + } + + if (!ev->accept) { + ngx_post_event(ev); + continue; + } + + if (ngx_accept_disabled > 0) { + continue; + } + + ngx_mutex_unlock(ngx_posted_events_mutex); + + ev->event_handler(ev); + + if (ngx_accept_disabled > 0) { + ngx_accept_mutex_unlock(); + accept_lock = 0; + } + + if (i + 1 == events) { + lock = 0; + break; + } + + if (ngx_mutex_lock(ngx_posted_events_mutex) == NGX_ERROR) { + if (accept_lock) { + ngx_accept_mutex_unlock(); + } + return NGX_ERROR; + } + } + + if (accept_lock) { + ngx_accept_mutex_unlock(); + } + + if (lock) { + ngx_mutex_unlock(ngx_posted_events_mutex); + } + + if (expire && delta) { + ngx_event_expire_timers((ngx_msec_t) delta); + } + + if (ngx_posted_events) { + if (ngx_threaded) { + ngx_wakeup_worker_thread(cycle); + + } else { + ngx_event_process_posted(cycle); + } + } + + return NGX_OK; +} + + +static ngx_int_t ngx_kqueue_process_changes(ngx_cycle_t *cycle, ngx_uint_t try) +{ + int n; + ngx_int_t rc; + ngx_err_t err; + struct timespec ts; + struct kevent *changes; + + if (ngx_mutex_lock(kevent_mutex) == NGX_ERROR) { + return NGX_ERROR; + } + + if (ngx_mutex_lock(list_mutex) == NGX_ERROR) { + ngx_mutex_unlock(kevent_mutex); + return NGX_ERROR; + } + + if (nchanges == 0) { + ngx_mutex_unlock(list_mutex); + ngx_mutex_unlock(kevent_mutex); + return NGX_OK; + } + + changes = change_list; + if (change_list == change_list0) { + change_list = change_list1; + } else { + change_list = change_list0; + } + + n = nchanges; + nchanges = 0; + + ngx_mutex_unlock(list_mutex); + + ts.tv_sec = 0; + ts.tv_nsec = 0; + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, + "kevent changes: %d", n); + + if (kevent(ngx_kqueue, changes, n, NULL, 0, &ts) == -1) { + err = ngx_errno; + ngx_log_error((err == NGX_EINTR) ? NGX_LOG_INFO : NGX_LOG_ALERT, + cycle->log, err, "kevent() failed"); + rc = NGX_ERROR; + + } else { + rc = NGX_OK; + } + + ngx_mutex_unlock(kevent_mutex); + + return rc; +} + + +static ngx_inline void ngx_kqueue_dump_event(ngx_log_t *log, struct kevent *kev) +{ + ngx_log_debug6(NGX_LOG_DEBUG_EVENT, log, 0, + (kev->ident > 0x8000000 && kev->ident != (unsigned) -1) ? + "kevent: " PTR_FMT ": ft:%d fl:%04X ff:%08X d:%d ud:" + PTR_FMT: + "kevent: %d: ft:%d fl:%04X ff:%08X d:%d ud:" PTR_FMT, + kev->ident, kev->filter, + kev->flags, kev->fflags, + kev->data, kev->udata); +} + + +static void *ngx_kqueue_create_conf(ngx_cycle_t *cycle) +{ + ngx_kqueue_conf_t *kcf; + + ngx_test_null(kcf, ngx_palloc(cycle->pool, sizeof(ngx_kqueue_conf_t)), + NGX_CONF_ERROR); + + kcf->changes = NGX_CONF_UNSET; + kcf->events = NGX_CONF_UNSET; + + return kcf; +} + + +static char *ngx_kqueue_init_conf(ngx_cycle_t *cycle, void *conf) +{ + ngx_kqueue_conf_t *kcf = conf; + + ngx_conf_init_value(kcf->changes, 512); + ngx_conf_init_value(kcf->events, 512); + + return NGX_CONF_OK; +} diff --git a/src/event/modules/ngx_kqueue_module.h b/src/event/modules/ngx_kqueue_module.h new file mode 100644 --- /dev/null +++ b/src/event/modules/ngx_kqueue_module.h @@ -0,0 +1,17 @@ + +/* + * 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 --git a/src/event/modules/ngx_poll_module.c b/src/event/modules/ngx_poll_module.c new file mode 100644 --- /dev/null +++ b/src/event/modules/ngx_poll_module.c @@ -0,0 +1,605 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include + + +static ngx_int_t ngx_poll_init(ngx_cycle_t *cycle); +static void ngx_poll_done(ngx_cycle_t *cycle); +static ngx_int_t ngx_poll_add_event(ngx_event_t *ev, int event, u_int flags); +static ngx_int_t ngx_poll_del_event(ngx_event_t *ev, int event, u_int flags); +static ngx_int_t ngx_poll_process_events(ngx_cycle_t *cycle); +static char *ngx_poll_init_conf(ngx_cycle_t *cycle, void *conf); + + +static struct pollfd *event_list; +static int nevents; + +#if 0 +static ngx_event_t **ready_index; +#endif + +static ngx_event_t *accept_events; + + +static ngx_str_t poll_name = ngx_string("poll"); + +ngx_event_module_t ngx_poll_module_ctx = { + &poll_name, + NULL, /* create configuration */ + ngx_poll_init_conf, /* init configuration */ + + { + ngx_poll_add_event, /* add an event */ + ngx_poll_del_event, /* delete an event */ + ngx_poll_add_event, /* enable an event */ + ngx_poll_del_event, /* disable an event */ + NULL, /* add an connection */ + NULL, /* delete an connection */ + NULL, /* process the changes */ + ngx_poll_process_events, /* process the events */ + ngx_poll_init, /* init the events */ + ngx_poll_done /* done the events */ + } + +}; + +ngx_module_t ngx_poll_module = { + NGX_MODULE, + &ngx_poll_module_ctx, /* module context */ + NULL, /* module directives */ + NGX_EVENT_MODULE, /* module type */ + NULL, /* init module */ + NULL /* init process */ +}; + + + +static ngx_int_t ngx_poll_init(ngx_cycle_t *cycle) +{ + struct pollfd *list; + + if (event_list == NULL) { + nevents = 0; + } + + if (ngx_process == NGX_PROCESS_WORKER + || cycle->old_cycle == NULL + || cycle->old_cycle->connection_n < cycle->connection_n) + { + ngx_test_null(list, + ngx_alloc(sizeof(struct pollfd) * cycle->connection_n, + cycle->log), + NGX_ERROR); + + if (event_list) { + ngx_memcpy(list, event_list, sizeof(ngx_event_t *) * nevents); + ngx_free(event_list); + } + + event_list = list; + +#if 0 + if (ready_index) { + ngx_free(ready_index); + } + + ngx_test_null(ready_index, + ngx_alloc(sizeof(ngx_event_t *) * 2 * cycle->connection_n, + cycle->log), + NGX_ERROR); +#endif + } + + ngx_io = ngx_os_io; + + ngx_event_actions = ngx_poll_module_ctx.actions; + + ngx_event_flags = NGX_USE_LEVEL_EVENT|NGX_USE_ONESHOT_EVENT; + + return NGX_OK; +} + + +static void ngx_poll_done(ngx_cycle_t *cycle) +{ + ngx_free(event_list); +#if 0 + ngx_free(ready_index); +#endif + + event_list = NULL; +#if 0 + ready_index = NULL; +#endif +} + + +static ngx_int_t ngx_poll_add_event(ngx_event_t *ev, int event, u_int flags) +{ + ngx_event_t *e; + ngx_connection_t *c; + + c = ev->data; + + ev->active = 1; + + if (ev->index != NGX_INVALID_INDEX) { + ngx_log_error(NGX_LOG_ALERT, ev->log, 0, + "poll event fd:%d ev:%d is already set", c->fd, event); + return NGX_OK; + } + + if (event == NGX_READ_EVENT) { + e = c->write; +#if (NGX_READ_EVENT != POLLIN) + event = POLLIN; +#endif + + } else { + e = c->read; +#if (NGX_WRITE_EVENT != POLLOUT) + event = POLLOUT; +#endif + } + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0, + "poll add event: fd:%d ev:%d", c->fd, event); + + if (e == NULL || e->index == NGX_INVALID_INDEX) { + event_list[nevents].fd = c->fd; + event_list[nevents].events = event; + event_list[nevents].revents = 0; + + ev->index = nevents; + nevents++; + + } else { + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ev->log, 0, + "poll add index: %d", e->index); + + event_list[e->index].events |= event; + ev->index = e->index; + } + + ev->oneshot = (flags & NGX_ONESHOT_EVENT) ? 1 : 0; + + return NGX_OK; +} + + +static ngx_int_t ngx_poll_del_event(ngx_event_t *ev, int event, u_int flags) +{ + ngx_uint_t i; + ngx_cycle_t **cycle; + ngx_event_t *e; + ngx_connection_t *c; + + c = ev->data; + + ev->active = 0; + + if (ev->index == NGX_INVALID_INDEX) { + ngx_log_error(NGX_LOG_ALERT, ev->log, 0, + "poll event fd:%d ev:%d is already deleted", + c->fd, event); + return NGX_OK; + } + + if (event == NGX_READ_EVENT) { + e = c->write; +#if (NGX_READ_EVENT != POLLIN) + event = POLLIN; +#endif + + } else { + e = c->read; +#if (NGX_WRITE_EVENT != POLLOUT) + event = POLLOUT; +#endif + } + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0, + "poll del event: fd:%d ev:%d", c->fd, event); + + if (e == NULL || e->index == NGX_INVALID_INDEX) { + nevents--; + + if (ev->index < (u_int) nevents) { + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0, + "index: copy event %d to %d", nevents, ev->index); + + event_list[ev->index] = event_list[nevents]; + + c = &ngx_cycle->connections[event_list[nevents].fd]; + + if (c->fd == -1) { + cycle = ngx_old_cycles.elts; + for (i = 0; i < ngx_old_cycles.nelts; i++) { + if (cycle[i] == NULL) { + continue; + } + c = &cycle[i]->connections[event_list[nevents].fd]; + if (c->fd != -1) { + break; + } + } + } + + if (c->fd == -1) { + ngx_log_error(NGX_LOG_ALERT, ev->log, 0, + "unexpected last event"); + + } else { + if (c->read->index == (u_int) nevents) { + c->read->index = ev->index; + } + + if (c->write->index == (u_int) nevents) { + c->write->index = ev->index; + } + } + } + + } else { + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ev->log, 0, + "poll del index: %d", e->index); + + event_list[e->index].events &= ~event; + } + + ev->index = NGX_INVALID_INDEX; + + return NGX_OK; +} + + +static ngx_int_t ngx_poll_process_events(ngx_cycle_t *cycle) +{ + int ready; + ngx_int_t i, nready; + ngx_uint_t n, found, lock, expire; + ngx_msec_t timer; + ngx_err_t err; + ngx_cycle_t **old_cycle; + ngx_event_t *ev; + ngx_epoch_msec_t delta; + ngx_connection_t *c; + struct timeval tv; + + for ( ;; ) { + timer = ngx_event_find_timer(); + + if (timer != 0) { + break; + } + + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, + "poll expired timer"); + + ngx_event_expire_timers((ngx_msec_t) + (ngx_elapsed_msec - ngx_old_elapsed_msec)); + } + + /* NGX_TIMER_INFINITE == INFTIM */ + + if (timer == NGX_TIMER_INFINITE) { + expire = 0; + + } else { + expire = 1; + } + + ngx_old_elapsed_msec = ngx_elapsed_msec; + +#if (NGX_DEBUG0) + if (cycle->log->log_level & NGX_LOG_DEBUG_ALL) { + for (i = 0; i < nevents; i++) { + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, cycle->log, 0, + "poll: %d: fd:%d ev:%04X", + i, event_list[i].fd, event_list[i].events); + } + } +#endif + + if (ngx_accept_mutex) { + if (ngx_accept_disabled > 0) { + ngx_accept_disabled--; + + } else { + if (ngx_trylock_accept_mutex(cycle) == NGX_ERROR) { + return NGX_ERROR; + } + + if (ngx_accept_mutex_held == 0 + && (timer == NGX_TIMER_INFINITE + || timer > ngx_accept_mutex_delay)) + { + timer = ngx_accept_mutex_delay; + expire = 0; + } + } + } + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "poll timer: %d", timer); + + ready = poll(event_list, (u_int) nevents, (int) timer); + + if (ready == -1) { + err = ngx_errno; + } else { + err = 0; + } + + ngx_gettimeofday(&tv); + ngx_time_update(tv.tv_sec); + + delta = ngx_elapsed_msec; + ngx_elapsed_msec = (ngx_epoch_msec_t) tv.tv_sec * 1000 + + tv.tv_usec / 1000 - ngx_start_msec; + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0, + "poll ready %d of %d", ready, nevents); + + if (err) { + ngx_log_error((err == NGX_EINTR) ? NGX_LOG_INFO : NGX_LOG_ALERT, + cycle->log, err, "poll() failed"); + ngx_accept_mutex_unlock(); + return NGX_ERROR; + } + + if (timer != NGX_TIMER_INFINITE) { + delta = ngx_elapsed_msec - delta; + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0, + "poll timer: %d, delta: %d", timer, (int) delta); + } else { + if (ready == 0) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, + "poll() returned no events without timeout"); + ngx_accept_mutex_unlock(); + return NGX_ERROR; + } + } + + if (ngx_mutex_lock(ngx_posted_events_mutex) == NGX_ERROR) { + ngx_accept_mutex_unlock(); + return NGX_ERROR; + } + + lock = 1; + nready = 0; + + for (i = 0; i < nevents && ready; i++) { + +#if 0 + ngx_log_debug4(NGX_LOG_DEBUG_EVENT, cycle->log, 0, + "poll: %d: fd:%d ev:%04X rev:%04X", + i, event_list[i].fd, + event_list[i].events, event_list[i].revents); +#else + if (event_list[i].revents) { + ngx_log_debug4(NGX_LOG_DEBUG_EVENT, cycle->log, 0, + "poll: %d: fd:%d ev:%04X rev:%04X", + i, event_list[i].fd, + event_list[i].events, event_list[i].revents); + } +#endif + + if (event_list[i].revents & POLLNVAL) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, + "poll() error fd:%d ev:%04X rev:%04X", + event_list[i].fd, + event_list[i].events, event_list[i].revents); + } + + if (event_list[i].revents & ~(POLLIN|POLLOUT|POLLERR|POLLHUP|POLLNVAL)) + { + ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, + "strange poll() events fd:%d ev:%04X rev:%04X", + event_list[i].fd, + event_list[i].events, event_list[i].revents); + } + + if (event_list[i].fd == -1) { + /* + * the disabled event, a workaround for our possible bug, + * see the comment below + */ + continue; + } + + c = &ngx_cycle->connections[event_list[i].fd]; + + if (c->fd == -1) { + old_cycle = ngx_old_cycles.elts; + for (n = 0; n < ngx_old_cycles.nelts; n++) { + if (old_cycle[n] == NULL) { + continue; + } + c = &old_cycle[n]->connections[event_list[i].fd]; + if (c->fd != -1) { + break; + } + } + } + + if (c->fd == -1) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, "unexpected event"); + + /* + * it is certainly our fault and it should be investigated, + * in the meantime we disable this event to avoid a CPU spinning + */ + + if (i == nevents - 1) { + nevents--; + } else { + event_list[i].fd = -1; + } + + continue; + } + + found = 0; + + if (event_list[i].revents & (POLLIN|POLLERR|POLLHUP|POLLNVAL)) { + found = 1; + + ev = c->read; + ev->ready = 1; + + if (ev->oneshot) { + if (ev->timer_set) { + ngx_del_timer(ev); + } + ngx_poll_del_event(ev, NGX_READ_EVENT, 0); + } + + if (ev->accept) { + ev->next = accept_events; + accept_events = ev; + } else { + ngx_post_event(ev); + } + +#if 0 + ready_index[nready++] = c->read; +#endif + } + + if (event_list[i].revents & (POLLOUT|POLLERR|POLLHUP|POLLNVAL)) { + found = 1; + ev = c->write; + ev->ready = 1; + + if (ev->oneshot) { + if (ev->timer_set) { + ngx_del_timer(ev); + } + ngx_poll_del_event(ev, NGX_WRITE_EVENT, 0); + } + + ngx_post_event(ev); +#if 0 + ready_index[nready++] = c->write; +#endif + } + + if (found) { + ready--; + continue; + } + } + +#if 0 + for (i = 0; i < nready; i++) { + ev = ready_index[i]; + + if (!ev->active) { + continue; + } + + ev->ready = 1; + + if (ev->oneshot) { + if (ev->timer_set) { + ngx_del_timer(ev); + } + + if (ev->write) { + ngx_poll_del_event(ev, NGX_WRITE_EVENT, 0); + } else { + ngx_poll_del_event(ev, NGX_READ_EVENT, 0); + } + } + + ev->event_handler(ev); + } +#endif + + ev = accept_events; + + for ( ;; ) { + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, + "accept event " PTR_FMT, ev); + + if (ev == NULL) { + break; + } + + ngx_mutex_unlock(ngx_posted_events_mutex); + + ev->event_handler(ev); + + if (ngx_accept_disabled > 0) { + lock = 0; + break; + } + + ev = ev->next; + + if (ev == NULL) { + lock = 0; + break; + } + + if (ngx_mutex_lock(ngx_posted_events_mutex) == NGX_ERROR) { + ngx_accept_mutex_unlock(); + return NGX_ERROR; + } + + } + + ngx_accept_mutex_unlock(); + accept_events = NULL; + + if (lock) { + ngx_mutex_unlock(ngx_posted_events_mutex); + } + + if (ready != 0) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, "poll ready != events"); + } + + if (expire && delta) { + ngx_event_expire_timers((ngx_msec_t) delta); + } + + if (!ngx_threaded) { + ngx_event_process_posted(cycle); + } + + return nready; +} + + +static char *ngx_poll_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_poll_module.ctx_index) { + return NGX_CONF_OK; + } + +#if (NGX_THREADS) + + ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, + "poll() is not supported in the threaded mode"); + return NGX_CONF_ERROR; + +#else + + return NGX_CONF_OK; + +#endif +} diff --git a/src/event/modules/ngx_rtsig_module.c b/src/event/modules/ngx_rtsig_module.c new file mode 100644 --- /dev/null +++ b/src/event/modules/ngx_rtsig_module.c @@ -0,0 +1,814 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include + + +#if (TEST_BUILD_RTSIG) + +#define F_SETSIG 10 +#define SIGRTMIN 33 +#define si_fd __spare__[0] +#define KERN_RTSIGNR 30 +#define KERN_RTSIGMAX 31 + +int sigtimedwait(const sigset_t *set, siginfo_t *info, + const struct timespec *timeout) +{ + return -1; +} + +int ngx_linux_rtsig_max; + +#endif + + +typedef struct { + int signo; + ngx_int_t overflow_events; + ngx_int_t overflow_test; + ngx_int_t overflow_threshold; +} ngx_rtsig_conf_t; + + +extern ngx_event_module_t ngx_poll_module_ctx; + +static ngx_int_t ngx_rtsig_init(ngx_cycle_t *cycle); +static void ngx_rtsig_done(ngx_cycle_t *cycle); +static ngx_int_t ngx_rtsig_add_connection(ngx_connection_t *c); +static ngx_int_t ngx_rtsig_del_connection(ngx_connection_t *c, u_int flags); +static ngx_int_t ngx_rtsig_process_events(ngx_cycle_t *cycle); +static ngx_int_t ngx_rtsig_process_overflow(ngx_cycle_t *cycle); + +static void *ngx_rtsig_create_conf(ngx_cycle_t *cycle); +static char *ngx_rtsig_init_conf(ngx_cycle_t *cycle, void *conf); +static char *ngx_check_ngx_overflow_threshold_bounds(ngx_conf_t *cf, + void *post, void *data); + + +static sigset_t set; +static ngx_uint_t overflow, overflow_current; +static struct pollfd *overflow_list; + + +static ngx_str_t rtsig_name = ngx_string("rtsig"); + +static ngx_conf_num_bounds_t ngx_overflow_threshold_bounds = { + ngx_check_ngx_overflow_threshold_bounds, 2, 10 +}; + + +static ngx_command_t ngx_rtsig_commands[] = { + + {ngx_string("rtsig_signo"), + NGX_EVENT_CONF|NGX_CONF_TAKE1, + ngx_conf_set_num_slot, + 0, + offsetof(ngx_rtsig_conf_t, signo), + NULL}, + + {ngx_string("rtsig_overflow_events"), + NGX_EVENT_CONF|NGX_CONF_TAKE1, + ngx_conf_set_num_slot, + 0, + offsetof(ngx_rtsig_conf_t, overflow_events), + NULL}, + + {ngx_string("rtsig_overflow_test"), + NGX_EVENT_CONF|NGX_CONF_TAKE1, + ngx_conf_set_num_slot, + 0, + offsetof(ngx_rtsig_conf_t, overflow_test), + NULL}, + + {ngx_string("rtsig_overflow_threshold"), + NGX_EVENT_CONF|NGX_CONF_TAKE1, + ngx_conf_set_num_slot, + 0, + offsetof(ngx_rtsig_conf_t, overflow_threshold), + &ngx_overflow_threshold_bounds}, + + ngx_null_command +}; + + +ngx_event_module_t ngx_rtsig_module_ctx = { + &rtsig_name, + ngx_rtsig_create_conf, /* create configuration */ + ngx_rtsig_init_conf, /* init configuration */ + + { + NULL, /* add an event */ + NULL, /* delete an event */ + NULL, /* enable an event */ + NULL, /* disable an event */ + ngx_rtsig_add_connection, /* add an connection */ + ngx_rtsig_del_connection, /* delete an connection */ + NULL, /* process the changes */ + ngx_rtsig_process_events, /* process the events */ + ngx_rtsig_init, /* init the events */ + ngx_rtsig_done, /* done the events */ + } + +}; + +ngx_module_t ngx_rtsig_module = { + NGX_MODULE, + &ngx_rtsig_module_ctx, /* module context */ + ngx_rtsig_commands, /* module directives */ + NGX_EVENT_MODULE, /* module type */ + NULL, /* init module */ + NULL /* init process */ +}; + + +static ngx_int_t ngx_rtsig_init(ngx_cycle_t *cycle) +{ + ngx_rtsig_conf_t *rtscf; + + if (ngx_poll_module_ctx.actions.init(cycle) == NGX_ERROR) { + return NGX_ERROR; + } + + rtscf = ngx_event_get_conf(cycle->conf_ctx, ngx_rtsig_module); + + sigemptyset(&set); + sigaddset(&set, rtscf->signo); + sigaddset(&set, rtscf->signo + 1); + sigaddset(&set, SIGIO); + + if (sigprocmask(SIG_BLOCK, &set, NULL) == -1) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, + "sigprocmask() failed"); + return NGX_ERROR; + } + + if (overflow_list) { + ngx_free(overflow_list); + } + + overflow_list = ngx_alloc(sizeof(struct pollfd) * rtscf->overflow_events, + cycle->log); + if (overflow_list == NULL) { + return NGX_ERROR; + } + + ngx_io = ngx_os_io; + + ngx_event_actions = ngx_rtsig_module_ctx.actions; + + ngx_event_flags = NGX_USE_RTSIG_EVENT|NGX_HAVE_GREEDY_EVENT; + + return NGX_OK; +} + + +static void ngx_rtsig_done(ngx_cycle_t *cycle) +{ + ngx_poll_module_ctx.actions.done(cycle); +} + + +static ngx_int_t ngx_rtsig_add_connection(ngx_connection_t *c) +{ + int signo; + ngx_rtsig_conf_t *rtscf; + + if (c->read->accept && c->read->disabled) { + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, + "rtsig enable connection: fd:%d", c->fd); + + if (fcntl(c->fd, F_SETOWN, ngx_pid) == -1) { + ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno, + "fcntl(F_SETOWN) failed"); + return NGX_ERROR; + } + + c->read->active = 1; + c->read->disabled = 0; + } + + rtscf = ngx_event_get_conf(ngx_cycle->conf_ctx, ngx_rtsig_module); + + signo = rtscf->signo + c->read->instance; + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, + "rtsig add connection: fd:%d signo:%d", c->fd, signo); + + if (fcntl(c->fd, F_SETFL, O_RDWR|O_NONBLOCK|O_ASYNC) == -1) { + ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno, + "fcntl(O_RDWR|O_NONBLOCK|O_ASYNC) failed"); + return NGX_ERROR; + } + + if (fcntl(c->fd, F_SETSIG, signo) == -1) { + ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno, + "fcntl(F_SETSIG) failed"); + return NGX_ERROR; + } + + if (fcntl(c->fd, F_SETOWN, ngx_pid) == -1) { + ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno, + "fcntl(F_SETOWN) failed"); + return NGX_ERROR; + } + +#if (HAVE_ONESIGFD) + if (fcntl(c->fd, F_SETAUXFL, O_ONESIGFD) == -1) { + ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno, + "fcntl(F_SETAUXFL) failed"); + return NGX_ERROR; + } +#endif + + c->read->active = 1; + c->write->active = 1; + + return NGX_OK; +} + + +static ngx_int_t ngx_rtsig_del_connection(ngx_connection_t *c, u_int flags) +{ + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, + "rtsig del connection: fd:%d", c->fd); + + if ((flags & NGX_DISABLE_EVENT) && c->read->accept) { + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, + "rtsig disable connection: fd:%d", c->fd); + + c->read->active = 0; + c->read->disabled = 1; + return NGX_OK; + } + + if (flags & NGX_CLOSE_EVENT) { + c->read->active = 0; + c->write->active = 0; + return NGX_OK; + } + + if (fcntl(c->fd, F_SETFL, O_RDWR|O_NONBLOCK) == -1) { + ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno, + "fcntl(O_RDWR|O_NONBLOCK) failed"); + return NGX_ERROR; + } + + c->read->active = 0; + c->write->active = 0; + + return NGX_OK; +} + + +ngx_int_t ngx_rtsig_process_events(ngx_cycle_t *cycle) +{ + int signo; + ngx_int_t instance, i; + ngx_uint_t expire; + size_t n; + ngx_msec_t timer; + ngx_err_t err; + siginfo_t si; + ngx_event_t *rev, *wev; + struct timeval tv; + struct timespec ts, *tp; + struct sigaction sa; + ngx_epoch_msec_t delta; + ngx_connection_t *c; + ngx_rtsig_conf_t *rtscf; + + if (overflow) { + timer = 0; + expire = 0; + + } else { + for ( ;; ) { + timer = ngx_event_find_timer(); + +#if (NGX_THREADS) + + if (timer == NGX_TIMER_ERROR) { + return NGX_ERROR; + } + + if (timer == NGX_TIMER_INFINITE || timer > 500) { + timer = 500; + break; + } + +#endif + + if (timer != 0) { + break; + } + + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, + "rtsig expired timer"); + + ngx_event_expire_timers((ngx_msec_t) + (ngx_elapsed_msec - ngx_old_elapsed_msec)); + + if (ngx_posted_events && ngx_threaded) { + ngx_wakeup_worker_thread(cycle); + } + } + + expire = 1; + + if (ngx_accept_mutex) { + if (ngx_accept_disabled > 0) { + ngx_accept_disabled--; + + } else { + ngx_accept_mutex_held = 0; + + if (ngx_trylock_accept_mutex(cycle) == NGX_ERROR) { + return NGX_ERROR; + } + + if (ngx_accept_mutex_held == 0 + && (timer == NGX_TIMER_INFINITE + || timer > ngx_accept_mutex_delay)) + { + timer = ngx_accept_mutex_delay; + expire = 0; + } + } + } + } + + if (timer == NGX_TIMER_INFINITE) { + tp = NULL; + expire = 0; + + } else { + ts.tv_sec = timer / 1000; + ts.tv_nsec = (timer % 1000) * 1000000; + tp = &ts; + } + + ngx_old_elapsed_msec = ngx_elapsed_msec; + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, + "rtsig timer: %d", timer); + + /* Linux's sigwaitinfo() is sigtimedwait() with the NULL timeout pointer */ + + signo = sigtimedwait(&set, &si, tp); + + if (signo == -1) { + err = ngx_errno; + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, err, + "rtsig signo:%d", signo); + + if (err == NGX_EAGAIN) { + + if (timer == NGX_TIMER_INFINITE) { + ngx_accept_mutex_unlock(); + ngx_log_error(NGX_LOG_ALERT, cycle->log, err, + "sigtimedwait() returned EAGAIN without timeout"); + return NGX_ERROR; + } + + err = 0; + } + + } else { + err = 0; + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, cycle->log, 0, + "rtsig signo:%d fd:%d band:%X", + signo, si.si_fd, si.si_band); + } + + ngx_gettimeofday(&tv); + ngx_time_update(tv.tv_sec); + + delta = ngx_elapsed_msec; + ngx_elapsed_msec = (ngx_epoch_msec_t) tv.tv_sec * 1000 + + tv.tv_usec / 1000 - ngx_start_msec; + + if (err) { + ngx_accept_mutex_unlock(); + ngx_log_error((err == NGX_EINTR) ? NGX_LOG_INFO : NGX_LOG_ALERT, + cycle->log, err, "sigtimedwait() failed"); + return NGX_ERROR; + } + + if (timer != NGX_TIMER_INFINITE) { + delta = ngx_elapsed_msec - delta; + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0, + "rtsig timer: %d, delta: %d", timer, (int) delta); + } + + rtscf = ngx_event_get_conf(ngx_cycle->conf_ctx, ngx_rtsig_module); + + if (signo == rtscf->signo || signo == rtscf->signo + 1) { + + if (overflow && (ngx_uint_t) si.si_fd > overflow_current) { + return NGX_OK; + } + + /* TODO: old_cycles */ + + c = &ngx_cycle->connections[si.si_fd]; + + instance = signo - rtscf->signo; + + rev = c->read; + + if (c->read->instance != instance) { + + /* + * the stale event from a file descriptor + * that was just closed in this iteration + */ + + ngx_accept_mutex_unlock(); + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, + "rtsig: stale event " PTR_FMT, c); + + return NGX_OK; + } + + if (si.si_band & (POLLIN|POLLHUP|POLLERR)) { + if (rev->active) { + + if (ngx_threaded && !rev->accept) { + if (ngx_mutex_lock(ngx_posted_events_mutex) == NGX_ERROR) { + ngx_accept_mutex_unlock(); + return NGX_ERROR; + } + + rev->posted_ready = 1; + ngx_post_event(rev); + + ngx_mutex_unlock(ngx_posted_events_mutex); + + } else { + rev->ready = 1; + + if (!ngx_threaded && !ngx_accept_mutex_held) { + rev->event_handler(rev); + + } else if (rev->accept) { + if (ngx_accept_disabled <= 0) { + rev->event_handler(rev); + } + + } else { + ngx_post_event(rev); + } + } + } + } + + wev = c->write; + + if (si.si_band & (POLLOUT|POLLHUP|POLLERR)) { + if (wev->active) { + + if (ngx_threaded) { + if (ngx_mutex_lock(ngx_posted_events_mutex) == NGX_ERROR) { + ngx_accept_mutex_unlock(); + return NGX_ERROR; + } + + wev->posted_ready = 1; + ngx_post_event(wev); + + ngx_mutex_unlock(ngx_posted_events_mutex); + + } else { + wev->ready = 1; + + if (!ngx_threaded && !ngx_accept_mutex_held) { + wev->event_handler(wev); + + } else { + ngx_post_event(wev); + } + } + } + } + + } else if (signo == SIGIO) { + ngx_accept_mutex_unlock(); + + ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, + "rt signal queue overflowed"); + + /* flush the RT signal queue */ + + ngx_memzero(&sa, sizeof(struct sigaction)); + sa.sa_handler = SIG_DFL; + sigemptyset(&sa.sa_mask); + + if (sigaction(rtscf->signo, &sa, NULL) == -1) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "sigaction(%d, SIG_DFL) failed", rtscf->signo); + } + + if (sigaction(rtscf->signo + 1, &sa, NULL) == -1) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "sigaction(%d, SIG_DFL) failed", rtscf->signo + 1); + } + + overflow = 1; + overflow_current = 0; + ngx_event_actions.process_events = ngx_rtsig_process_overflow; + + return NGX_ERROR; + + } else if (signo != -1) { + ngx_accept_mutex_unlock(); + + ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, + "sigtimedwait() returned unexpected signal: %d", signo); + + return NGX_ERROR; + } + + ngx_accept_mutex_unlock(); + + if (expire && delta) { + ngx_event_expire_timers((ngx_msec_t) delta); + } + + if (ngx_posted_events) { + if (ngx_threaded) { + ngx_wakeup_worker_thread(cycle); + + } else { + ngx_event_process_posted(cycle); + } + } + + if (signo == -1) { + return NGX_AGAIN; + } else { + return NGX_OK; + } +} + + +/* TODO: old cylces */ + +static ngx_int_t ngx_rtsig_process_overflow(ngx_cycle_t *cycle) +{ + int name[2], rtsig_max, rtsig_nr, events, ready; + size_t len; + ngx_int_t tested, n, i; + ngx_err_t err; + ngx_event_t *rev, *wev; + ngx_connection_t *c; + ngx_rtsig_conf_t *rtscf; + + rtscf = ngx_event_get_conf(ngx_cycle->conf_ctx, ngx_rtsig_module); + + tested = 0; + + for ( ;; ) { + + n = 0; + while (n < rtscf->overflow_events) { + + if (overflow_current == cycle->connection_n) { + break; + } + + c = &cycle->connections[overflow_current++]; + + if (c->fd == -1) { + continue; + } + + events = 0; + + if (c->read->active && c->read->event_handler) { + events |= POLLIN; + } + + if (c->write->active && c->write->event_handler) { + events |= POLLOUT; + } + + if (events == 0) { + continue; + } + + overflow_list[n].fd = c->fd; + overflow_list[n].events = events; + overflow_list[n].revents = 0; + n++; + } + + if (n == 0) { + break; + } + + for ( ;; ) { + ready = poll(overflow_list, n, 0); + + if (ready == -1) { + err = ngx_errno; + ngx_log_error((err == NGX_EINTR) ? NGX_LOG_INFO : NGX_LOG_ALERT, + cycle->log, 0, + "poll() failed while the overflow recover"); + + if (err == NGX_EINTR) { + continue; + } + } + + break; + } + + if (ready <= 0) { + continue; + } + + if (ngx_mutex_lock(ngx_posted_events_mutex) == NGX_ERROR) { + return NGX_ERROR; + } + + for (i = 0; i < n; i++) { + c = &cycle->connections[overflow_list[i].fd]; + + rev = c->read; + + if (rev->active + && !rev->closed + && rev->event_handler + && (overflow_list[i].revents + & (POLLIN|POLLERR|POLLHUP|POLLNVAL))) + { + tested++; + + if (ngx_threaded) { + rev->posted_ready = 1; + ngx_post_event(rev); + + } else { + rev->ready = 1; + rev->event_handler(rev); + } + } + + wev = c->write; + + if (wev->active + && !wev->closed + && wev->event_handler + && (overflow_list[i].revents + & (POLLOUT|POLLERR|POLLHUP|POLLNVAL))) + { + tested++; + + if (ngx_threaded) { + wev->posted_ready = 1; + ngx_post_event(wev); + + } else { + wev->ready = 1; + wev->event_handler(wev); + } + } + } + + ngx_mutex_unlock(ngx_posted_events_mutex); + + if (tested >= rtscf->overflow_test) { + + if (ngx_linux_rtsig_max) { + + /* + * Check the current rt queue length to prevent + * the new overflow. + * + * Learn the /proc/sys/kernel/rtsig-max value because + * it can be changed sisnce the last checking. + */ + + name[0] = CTL_KERN; + name[1] = KERN_RTSIGMAX; + len = sizeof(rtsig_max); + if (sysctl(name, sizeof(name), &rtsig_max, &len, NULL, 0) == -1) + { + ngx_log_error(NGX_LOG_ALERT, cycle->log, errno, + "sysctl(KERN_RTSIGMAX) failed"); + return NGX_ERROR; + } + + name[0] = CTL_KERN; + name[1] = KERN_RTSIGNR; + len = sizeof(rtsig_nr); + if (sysctl(name, sizeof(name), &rtsig_nr, &len, NULL, 0) == -1) + { + ngx_log_error(NGX_LOG_ALERT, cycle->log, errno, + "sysctl(KERN_RTSIGNR) failed"); + return NGX_ERROR; + } + + /* + * drain the rt signal queue if the /proc/sys/kernel/rtsig-nr + * is bigger than + * /proc/sys/kernel/rtsig-max / rtsig_overflow_threshold + */ + + if (rtsig_max / rtscf->overflow_threshold < rtsig_nr) { + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0, + "rtsig queue state: %d/%d", + rtsig_nr, rtsig_max); + while (ngx_rtsig_process_events(cycle) == NGX_OK) { + /* void */ + } + } + + } else { + + /* + * Linux has not KERN_RTSIGMAX since 2.6.6-mm2 + * so drain the rt signal queue unconditionally + */ + + while (ngx_rtsig_process_events(cycle) == NGX_OK) { /* void */ } + } + + tested = 0; + } + } + + if (ngx_posted_events) { + if (ngx_threaded) { + ngx_wakeup_worker_thread(cycle); + + } else { + ngx_event_process_posted(cycle); + } + } + + ngx_log_error(NGX_LOG_INFO, cycle->log, 0, + "rt signal queue overflow recovered"); + + overflow = 0; + ngx_event_actions.process_events = ngx_rtsig_process_events; + + return NGX_OK; +} + + +static void *ngx_rtsig_create_conf(ngx_cycle_t *cycle) +{ + ngx_rtsig_conf_t *rtscf; + + ngx_test_null(rtscf, ngx_palloc(cycle->pool, sizeof(ngx_rtsig_conf_t)), + NGX_CONF_ERROR); + + rtscf->signo = NGX_CONF_UNSET; + rtscf->overflow_events = NGX_CONF_UNSET; + rtscf->overflow_test = NGX_CONF_UNSET; + rtscf->overflow_threshold = NGX_CONF_UNSET; + + return rtscf; +} + + +static char *ngx_rtsig_init_conf(ngx_cycle_t *cycle, void *conf) +{ + ngx_rtsig_conf_t *rtscf = conf; + + /* LinuxThreads use the first 3 RT signals */ + ngx_conf_init_value(rtscf->signo, SIGRTMIN + 10); + + ngx_conf_init_value(rtscf->overflow_events, 16); + ngx_conf_init_value(rtscf->overflow_test, 32); + ngx_conf_init_value(rtscf->overflow_threshold, 10); + + return NGX_CONF_OK; +} + + +static char *ngx_check_ngx_overflow_threshold_bounds(ngx_conf_t *cf, + void *post, void *data) +{ + if (ngx_linux_rtsig_max) { + return ngx_conf_check_num_bounds(cf, post, data); + } + + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "\"rtsig_overflow_threshold\" is not supported " + "since Linux 2.6.6-mm2, ignored"); + + return NGX_CONF_OK; +} diff --git a/src/event/modules/ngx_select_module.c b/src/event/modules/ngx_select_module.c new file mode 100644 --- /dev/null +++ b/src/event/modules/ngx_select_module.c @@ -0,0 +1,616 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include + + + +static ngx_int_t ngx_select_init(ngx_cycle_t *cycle); +static void ngx_select_done(ngx_cycle_t *cycle); +static ngx_int_t ngx_select_add_event(ngx_event_t *ev, int event, u_int flags); +static ngx_int_t ngx_select_del_event(ngx_event_t *ev, int event, u_int flags); +static ngx_int_t ngx_select_process_events(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; + +#if (WIN32) +static int max_read; +static int max_write; +#else +static int max_fd; +#endif + +static ngx_uint_t nevents; + +static ngx_event_t **event_index; +#if 0 +static ngx_event_t **ready_index; +#endif + +static ngx_event_t *accept_events; + + +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, + &ngx_select_module_ctx, /* module context */ + NULL, /* module directives */ + NGX_EVENT_MODULE, /* module type */ + NULL, /* init module */ + NULL /* init process */ +}; + + +static ngx_int_t ngx_select_init(ngx_cycle_t *cycle) +{ + 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) + { + ngx_test_null(index, + ngx_alloc(sizeof(ngx_event_t *) * 2 * cycle->connection_n, + cycle->log), + NGX_ERROR); + + if (event_index) { + ngx_memcpy(index, event_index, sizeof(ngx_event_t *) * nevents); + ngx_free(event_index); + } + event_index = index; + +#if 0 + if (ready_index) { + ngx_free(ready_index); + } + ngx_test_null(ready_index, + ngx_alloc(sizeof(ngx_event_t *) * 2 * cycle->connection_n, + cycle->log), + NGX_ERROR); +#endif + } + + ngx_io = ngx_os_io; + + ngx_event_actions = ngx_select_module_ctx.actions; + + ngx_event_flags = NGX_USE_LEVEL_EVENT|NGX_USE_ONESHOT_EVENT; + +#if (WIN32) + max_read = max_write = 0; +#else + max_fd = -1; +#endif + + return NGX_OK; +} + + +static void ngx_select_done(ngx_cycle_t *cycle) +{ + ngx_free(event_index); +#if 0 + ngx_free(ready_index); +#endif + + event_index = NULL; +} + + +static ngx_int_t ngx_select_add_event(ngx_event_t *ev, int event, u_int flags) +{ + ngx_connection_t *c; + + c = ev->data; + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0, + "select add event fd:%d ev:%d", c->fd, event); + + if (ev->index != NGX_INVALID_INDEX) { + ngx_log_error(NGX_LOG_ALERT, ev->log, 0, + "select event fd:%d ev:%d is already set", c->fd, event); + return NGX_OK; + } + +#if (WIN32) + + 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++; + } + +#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); + } + + if (max_fd != -1 && max_fd < c->fd) { + max_fd = c->fd; + } + +#endif + + ev->active = 1; + ev->oneshot = (u_char) ((flags & NGX_ONESHOT_EVENT) ? 1 : 0); + + event_index[nevents] = ev; + ev->index = nevents; + nevents++; + + return NGX_OK; +} + + +static ngx_int_t ngx_select_del_event(ngx_event_t *ev, int event, u_int flags) +{ + 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:%d", c->fd, event); + +#if (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); + + } else if (event == NGX_WRITE_EVENT) { + FD_CLR(c->fd, &master_write_fd_set); + } + + if (max_fd == c->fd) { + max_fd = -1; + } + +#endif + + if (ev->index < (u_int) --nevents) { + event_index[ev->index] = event_index[nevents]; + event_index[ev->index]->index = ev->index; + } + + ev->index = NGX_INVALID_INDEX; + + return NGX_OK; +} + + +static ngx_int_t ngx_select_process_events(ngx_cycle_t *cycle) +{ + int ready, nready; + ngx_uint_t i, found, lock, expire; + ngx_err_t err; + ngx_msec_t timer; + ngx_event_t *ev; + ngx_connection_t *c; + ngx_epoch_msec_t delta; + struct timeval tv, *tp; +#if (HAVE_SELECT_CHANGE_TIMEOUT) + static ngx_epoch_msec_t deltas = 0; +#endif + + for ( ;; ) { + timer = ngx_event_find_timer(); + + if (timer != 0) { + break; + } + + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, + "select expired timer"); + + ngx_event_expire_timers((ngx_msec_t) + (ngx_elapsed_msec - ngx_old_elapsed_msec)); + } + + ngx_old_elapsed_msec = ngx_elapsed_msec; + + expire = 1; + +#if !(WIN32) + + if (ngx_accept_mutex) { + if (ngx_accept_disabled > 0) { + ngx_accept_disabled--; + + } else { + if (ngx_trylock_accept_mutex(cycle) == NGX_ERROR) { + return NGX_ERROR; + } + + if (ngx_accept_mutex_held == 0 + && (timer == NGX_TIMER_INFINITE + || timer > ngx_accept_mutex_delay)) + { + timer = ngx_accept_mutex_delay; + expire = 0; + } + } + } + + if (max_fd == -1) { + for (i = 0; i < nevents; i++) { + c = event_index[i]->data; + if (max_fd < c->fd) { + max_fd = c->fd; + } + } + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, + "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++) { + 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); + } + +#if !(WIN32) + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, + "max_fd: %d", max_fd); +#endif + } +#endif + + if (timer == NGX_TIMER_INFINITE) { + tp = NULL; + expire = 0; + + } else { + tv.tv_sec = timer / 1000; + tv.tv_usec = (timer % 1000) * 1000; + tp = &tv; + } + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, + "select timer: %d", timer); + + work_read_fd_set = master_read_fd_set; + work_write_fd_set = master_write_fd_set; + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, + "select read fd_set: %08X", *(int *) &work_read_fd_set); + +#if (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 + + if (ready == -1) { + err = ngx_socket_errno; + } else { + err = 0; + } + +#if (HAVE_SELECT_CHANGE_TIMEOUT) + + if (timer != NGX_TIMER_INFINITE) { + delta = timer - (tv.tv_sec * 1000 + tv.tv_usec / 1000); + + /* + * learn the real time and update the cached time + * if the sum of the last deltas overcomes 1 second + */ + + deltas += delta; + if (deltas > 1000) { + ngx_gettimeofday(&tv); + ngx_time_update(tv.tv_sec); + deltas = tv.tv_usec / 1000; + + ngx_elapsed_msec = (ngx_epoch_msec_t) tv.tv_sec * 1000 + + tv.tv_usec / 1000 - ngx_start_msec; + } else { + ngx_elapsed_msec += delta; + } + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0, + "select timer: %d, delta: %d", timer, (int) delta); + + } else { + delta = 0; + ngx_gettimeofday(&tv); + ngx_time_update(tv.tv_sec); + + ngx_elapsed_msec = (ngx_epoch_msec_t) tv.tv_sec * 1000 + + tv.tv_usec / 1000 - ngx_start_msec; + + if (ready == 0) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, + "select() returned no events without timeout"); + ngx_accept_mutex_unlock(); + return NGX_ERROR; + } + } + +#else /* !(HAVE_SELECT_CHANGE_TIMEOUT) */ + + ngx_gettimeofday(&tv); + ngx_time_update(tv.tv_sec); + + delta = ngx_elapsed_msec; + ngx_elapsed_msec = (ngx_epoch_msec_t) tv.tv_sec * 1000 + + tv.tv_usec / 1000 - ngx_start_msec; + + if (timer != NGX_TIMER_INFINITE) { + delta = ngx_elapsed_msec - delta; + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0, + "select timer: %d, delta: %d", timer, (int) delta); + + } else { + if (ready == 0) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, + "select() returned no events without timeout"); + ngx_accept_mutex_unlock(); + return NGX_ERROR; + } + } + +#endif /* HAVE_SELECT_CHANGE_TIMEOUT */ + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, + "select ready %d", ready); + + if (err) { +#if (WIN32) + ngx_log_error(NGX_LOG_ALERT, cycle->log, err, "select() failed"); +#else + ngx_log_error((err == NGX_EINTR) ? NGX_LOG_INFO : NGX_LOG_ALERT, + cycle->log, err, "select() failed"); +#endif + ngx_accept_mutex_unlock(); + return NGX_ERROR; + } + + + if (ngx_mutex_lock(ngx_posted_events_mutex) == NGX_ERROR) { + ngx_accept_mutex_unlock(); + return NGX_ERROR; + } + + lock = 1; + 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; + + if (ev->oneshot) { + if (ev->timer_set) { + ngx_del_timer(ev); + } + + if (ev->write) { + ngx_select_del_event(ev, NGX_WRITE_EVENT, 0); + } else { + ngx_select_del_event(ev, NGX_READ_EVENT, 0); + } + } + + if (ev->accept) { + ev->next = accept_events; + accept_events = ev; + } else { + ngx_post_event(ev); + } + + nready++; + +#if 0 + ready_index[nready++] = ev; +#endif + } + } + +#if 0 + for (i = 0; i < nready; i++) { + ev = ready_index[i]; + ready--; + + if (!ev->active) { + continue; + } + + ev->ready = 1; + + if (ev->oneshot) { + if (ev->timer_set) { + ngx_del_timer(ev); + } + + if (ev->write) { + ngx_select_del_event(ev, NGX_WRITE_EVENT, 0); + } else { + ngx_select_del_event(ev, NGX_READ_EVENT, 0); + } + } + + ev->event_handler(ev); + } +#endif + + ev = accept_events; + + for ( ;; ) { + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, + "accept event " PTR_FMT, ev); + + if (ev == NULL) { + break; + } + + ngx_mutex_unlock(ngx_posted_events_mutex); + + ev->event_handler(ev); + + if (ngx_accept_disabled > 0) { + lock = 0; + break; + } + + ev = ev->next; + + if (ev == NULL) { + lock = 0; + break; + } + + if (ngx_mutex_lock(ngx_posted_events_mutex) == NGX_ERROR) { + ngx_accept_mutex_unlock(); + return NGX_ERROR; + } + } + + ngx_accept_mutex_unlock(); + accept_events = NULL; + + if (lock) { + ngx_mutex_unlock(ngx_posted_events_mutex); + } + + if (ready != nready) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, "select ready != events"); + } + + if (expire && delta) { + ngx_event_expire_timers((ngx_msec_t) delta); + } + + if (!ngx_threaded) { + ngx_event_process_posted(cycle); + } + + return NGX_OK; +} + + +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; + } + + /* disable warning: the default FD_SETSIZE is 1024U in FreeBSD 5.x */ + +#if !(WIN32) + if ((unsigned) ecf->connections > FD_SETSIZE) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, + "the maximum number of files " + "supported by select() is " ngx_value(FD_SETSIZE)); + return NGX_CONF_ERROR; + } +#endif + +#if (NGX_THREADS) + ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, + "select() is not supported in the threaded mode"); + return NGX_CONF_ERROR; +#else + return NGX_CONF_OK; +#endif +} diff --git a/src/event/ngx_event.c b/src/event/ngx_event.c new file mode 100644 --- /dev/null +++ b/src/event/ngx_event.c @@ -0,0 +1,805 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include + + +#define DEFAULT_CONNECTIONS 512 + + +extern ngx_module_t ngx_select_module; +extern ngx_event_module_t ngx_select_module_ctx; + +#if (HAVE_KQUEUE) +#include +#endif + +#if (HAVE_DEVPOLL) +extern ngx_module_t ngx_devpoll_module; +extern ngx_event_module_t ngx_devpoll_module_ctx; +#endif + +#if (HAVE_EPOLL) +extern ngx_module_t ngx_epoll_module; +extern ngx_event_module_t ngx_epoll_module_ctx; +#endif + +#if (HAVE_RTSIG) +extern ngx_module_t ngx_rtsig_module; +extern ngx_event_module_t ngx_rtsig_module_ctx; +#endif + +#if (HAVE_AIO) +#include +#endif + +static ngx_int_t ngx_event_module_init(ngx_cycle_t *cycle); +static ngx_int_t ngx_event_process_init(ngx_cycle_t *cycle); +static char *ngx_events_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); + +static char *ngx_event_connections(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static char *ngx_event_use(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static char *ngx_event_debug_connection(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); + +static void *ngx_event_create_conf(ngx_cycle_t *cycle); +static char *ngx_event_init_conf(ngx_cycle_t *cycle, void *conf); +static char *ngx_accept_mutex_check(ngx_conf_t *cf, void *post, void *data); + + +static ngx_uint_t ngx_event_max_module; + +ngx_uint_t ngx_event_flags; +ngx_event_actions_t ngx_event_actions; + + +ngx_atomic_t connection_counter; +ngx_atomic_t *ngx_connection_counter = &connection_counter; + + +ngx_atomic_t *ngx_accept_mutex_ptr; +ngx_atomic_t *ngx_accept_mutex; +ngx_uint_t ngx_accept_mutex_held; +ngx_msec_t ngx_accept_mutex_delay; +ngx_int_t ngx_accept_disabled; + + +#if (NGX_STAT_STUB) + +ngx_atomic_t ngx_stat_accepted0; +ngx_atomic_t *ngx_stat_accepted = &ngx_stat_accepted0; +ngx_atomic_t ngx_stat_requests0; +ngx_atomic_t *ngx_stat_requests = &ngx_stat_requests0; +ngx_atomic_t ngx_stat_active0; +ngx_atomic_t *ngx_stat_active = &ngx_stat_active0; +ngx_atomic_t ngx_stat_reading0; +ngx_atomic_t *ngx_stat_reading = &ngx_stat_reading0; +ngx_atomic_t ngx_stat_writing0; +ngx_atomic_t *ngx_stat_writing = &ngx_stat_reading0; + +#endif + + + +static ngx_command_t ngx_events_commands[] = { + + { ngx_string("events"), + NGX_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS, + ngx_events_block, + 0, + 0, + NULL }, + + ngx_null_command +}; + + +static ngx_core_module_t ngx_events_module_ctx = { + ngx_string("events"), + NULL, + NULL +}; + + +ngx_module_t ngx_events_module = { + NGX_MODULE, + &ngx_events_module_ctx, /* module context */ + ngx_events_commands, /* module directives */ + NGX_CORE_MODULE, /* module type */ + NULL, /* init module */ + NULL /* init process */ +}; + + +static ngx_str_t event_core_name = ngx_string("event_core"); + +static ngx_conf_post_t ngx_accept_mutex_post = { ngx_accept_mutex_check } ; + + +static ngx_command_t ngx_event_core_commands[] = { + + { ngx_string("connections"), + NGX_EVENT_CONF|NGX_CONF_TAKE1, + ngx_event_connections, + 0, + 0, + NULL }, + + { ngx_string("use"), + NGX_EVENT_CONF|NGX_CONF_TAKE1, + ngx_event_use, + 0, + 0, + NULL }, + + { ngx_string("multi_accept"), + NGX_EVENT_CONF|NGX_CONF_TAKE1, + ngx_conf_set_flag_slot, + 0, + offsetof(ngx_event_conf_t, multi_accept), + NULL }, + + { ngx_string("accept_mutex"), + NGX_EVENT_CONF|NGX_CONF_TAKE1, + ngx_conf_set_flag_slot, + 0, + offsetof(ngx_event_conf_t, accept_mutex), + &ngx_accept_mutex_post }, + + { ngx_string("accept_mutex_delay"), + NGX_EVENT_CONF|NGX_CONF_TAKE1, + ngx_conf_set_msec_slot, + 0, + offsetof(ngx_event_conf_t, accept_mutex_delay), + NULL }, + + { ngx_string("debug_connection"), + NGX_EVENT_CONF|NGX_CONF_TAKE1, + ngx_event_debug_connection, + 0, + 0, + NULL }, + + ngx_null_command +}; + + +ngx_event_module_t ngx_event_core_module_ctx = { + &event_core_name, + ngx_event_create_conf, /* create configuration */ + ngx_event_init_conf, /* init configuration */ + + { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL } +}; + + +ngx_module_t ngx_event_core_module = { + NGX_MODULE, + &ngx_event_core_module_ctx, /* module context */ + ngx_event_core_commands, /* module directives */ + NGX_EVENT_MODULE, /* module type */ + ngx_event_module_init, /* init module */ + ngx_event_process_init /* init process */ +}; + + +static ngx_int_t ngx_event_module_init(ngx_cycle_t *cycle) +{ +#if !(WIN32) + + size_t size; + char *shared; + ngx_core_conf_t *ccf; + ngx_event_conf_t *ecf; + + ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module); + + if (ccf->master == 0 || ngx_accept_mutex_ptr) { + return NGX_OK; + } + + ecf = ngx_event_get_conf(cycle->conf_ctx, ngx_event_core_module); + + + /* TODO: 128 is cache line size */ + + size = 128 /* ngx_accept_mutex */ + + 128; /* ngx_connection_counter */ + +#if (NGX_STAT_STUB) + + size += 128 /* ngx_stat_accepted */ + + 128 /* ngx_stat_requests */ + + 128 /* ngx_stat_active */ + + 128 /* ngx_stat_reading */ + + 128; /* ngx_stat_writing */ + +#endif + + if (!(shared = ngx_create_shared_memory(size, cycle->log))) { + return NGX_ERROR; + } + + ngx_accept_mutex_ptr = (ngx_atomic_t *) shared; + ngx_connection_counter = (ngx_atomic_t *) (shared + 128); + +#if (NGX_STAT_STUB) + + ngx_stat_accepted = (ngx_atomic_t *) (shared + 2 * 128); + ngx_stat_requests = (ngx_atomic_t *) (shared + 3 * 128); + ngx_stat_active = (ngx_atomic_t *) (shared + 4 * 128); + ngx_stat_reading = (ngx_atomic_t *) (shared + 5 * 128); + ngx_stat_writing = (ngx_atomic_t *) (shared + 6 * 128); + +#endif + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0, + "counter: " PTR_FMT ", %d", + ngx_connection_counter, *ngx_connection_counter); + +#endif + + return NGX_OK; +} + + +static ngx_int_t ngx_event_process_init(ngx_cycle_t *cycle) +{ + ngx_uint_t m, i; + ngx_socket_t fd; + ngx_event_t *rev, *wev; + ngx_listening_t *s; + ngx_connection_t *c; + ngx_core_conf_t *ccf; + ngx_event_conf_t *ecf; + ngx_event_module_t *module; +#if (WIN32) + ngx_iocp_conf_t *iocpcf; +#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); + + if (ngx_accept_mutex_ptr && ccf->worker_processes > 1 && ecf->accept_mutex) + { + ngx_accept_mutex = ngx_accept_mutex_ptr; + ngx_accept_mutex_held = 0; + ngx_accept_mutex_delay = ecf->accept_mutex_delay; + } + +#if (NGX_THREADS) + if (!(ngx_posted_events_mutex = ngx_mutex_init(cycle->log, 0))) { + return NGX_ERROR; + } +#endif + + if (ngx_event_timer_init(cycle->log) == NGX_ERROR) { + 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_ERROR) { + /* fatal */ + exit(2); + } + break; + } + } + + cycle->connections = ngx_alloc(sizeof(ngx_connection_t) * ecf->connections, + cycle->log); + if (cycle->connections == NULL) { + return NGX_ERROR; + } + + c = cycle->connections; + for (i = 0; i < cycle->connection_n; i++) { + c[i].fd = (ngx_socket_t) -1; + c[i].data = NULL; +#if (NGX_THREADS) + c[i].lock = 0; +#endif + } + + cycle->read_events = ngx_alloc(sizeof(ngx_event_t) * ecf->connections, + cycle->log); + if (cycle->read_events == NULL) { + return NGX_ERROR; + } + + rev = cycle->read_events; + for (i = 0; i < cycle->connection_n; i++) { + rev[i].closed = 1; +#if (NGX_THREADS) + rev[i].lock = &c[i].lock; + rev[i].own_lock = &c[i].lock; +#endif + } + + cycle->write_events = ngx_alloc(sizeof(ngx_event_t) * ecf->connections, + cycle->log); + if (cycle->write_events == NULL) { + return NGX_ERROR; + } + + wev = cycle->write_events; + for (i = 0; i < cycle->connection_n; i++) { + wev[i].closed = 1; +#if (NGX_THREADS) + wev[i].lock = &c[i].lock; + wev[i].own_lock = &c[i].lock; +#endif + } + + /* for each listening socket */ + + s = cycle->listening.elts; + for (i = 0; i < cycle->listening.nelts; i++) { + + fd = s[i].fd; + +#if (WIN32) + /* + * Winsock assignes a socket number divisible by 4 + * so to find a connection we divide a socket number by 4. + */ + + fd /= 4; +#endif + + c = &cycle->connections[fd]; + rev = &cycle->read_events[fd]; + wev = &cycle->write_events[fd]; + + ngx_memzero(c, sizeof(ngx_connection_t)); + ngx_memzero(rev, sizeof(ngx_event_t)); + + c->fd = s[i].fd; + c->listening = &s[i]; + + c->ctx = s[i].ctx; + c->servers = s[i].servers; + c->log = s[i].log; + c->read = rev; + + /* required by iocp in "c->write->active = 1" */ + c->write = wev; + + /* required by poll */ + wev->index = NGX_INVALID_INDEX; + + rev->log = c->log; + rev->data = c; + rev->index = NGX_INVALID_INDEX; + + rev->available = 0; + + rev->accept = 1; + +#if (HAVE_DEFERRED_ACCEPT) + rev->deferred_accept = s[i].deferred_accept; +#endif + + if (!(ngx_event_flags & NGX_USE_IOCP_EVENT)) { + if (s[i].remain) { + + /* + * delete the old accept events that were bound to + * the old cycle read events array + */ + + if (ngx_del_event(&cycle->old_cycle->read_events[fd], + NGX_READ_EVENT, NGX_CLOSE_EVENT) == NGX_ERROR) + { + return NGX_ERROR; + } + + cycle->old_cycle->connections[fd].fd = (ngx_socket_t) -1; + } + } + +#if (WIN32) + + if (ngx_event_flags & NGX_USE_IOCP_EVENT) { + rev->event_handler = &ngx_event_acceptex; + + if (ngx_add_event(rev, 0, NGX_IOCP_ACCEPT) == NGX_ERROR) { + return NGX_ERROR; + } + + iocpcf = ngx_event_get_conf(cycle->conf_ctx, ngx_iocp_module); + if (ngx_event_post_acceptex(&s[i], iocpcf->post_acceptex) + == NGX_ERROR) + { + return NGX_ERROR; + } + + } else { + rev->event_handler = &ngx_event_accept; + if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) { + return NGX_ERROR; + } + } + +#else + + rev->event_handler = &ngx_event_accept; + + if (ngx_accept_mutex) { + continue; + } + + if (ngx_event_flags & NGX_USE_RTSIG_EVENT) { + if (ngx_add_conn(c) == NGX_ERROR) { + return NGX_ERROR; + } + + } else { + if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) { + return NGX_ERROR; + } + } + +#endif + } + + return NGX_OK; +} + + +static char *ngx_events_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + int m; + char *rv; + void ***ctx; + ngx_conf_t pcf; + ngx_event_module_t *module; + + /* count the number of the event modules and set up their indices */ + + ngx_event_max_module = 0; + for (m = 0; ngx_modules[m]; m++) { + if (ngx_modules[m]->type != NGX_EVENT_MODULE) { + continue; + } + + ngx_modules[m]->ctx_index = ngx_event_max_module++; + } + + ngx_test_null(ctx, ngx_pcalloc(cf->pool, sizeof(void *)), NGX_CONF_ERROR); + + ngx_test_null(*ctx, + ngx_pcalloc(cf->pool, ngx_event_max_module * sizeof(void *)), + NGX_CONF_ERROR); + + *(void **) conf = ctx; + + for (m = 0; ngx_modules[m]; m++) { + if (ngx_modules[m]->type != NGX_EVENT_MODULE) { + continue; + } + + module = ngx_modules[m]->ctx; + + if (module->create_conf) { + ngx_test_null((*ctx)[ngx_modules[m]->ctx_index], + module->create_conf(cf->cycle), + NGX_CONF_ERROR); + } + } + + pcf = *cf; + cf->ctx = ctx; + cf->module_type = NGX_EVENT_MODULE; + cf->cmd_type = NGX_EVENT_CONF; + rv = ngx_conf_parse(cf, NULL); + *cf = pcf; + + if (rv != NGX_CONF_OK) + return rv; + + for (m = 0; ngx_modules[m]; m++) { + if (ngx_modules[m]->type != NGX_EVENT_MODULE) { + continue; + } + + module = ngx_modules[m]->ctx; + + if (module->init_conf) { + rv = module->init_conf(cf->cycle, + (*ctx)[ngx_modules[m]->ctx_index]); + if (rv != NGX_CONF_OK) { + return rv; + } + } + } + + return NGX_CONF_OK; +} + + +static char *ngx_event_connections(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf) +{ + ngx_event_conf_t *ecf = conf; + + ngx_str_t *value; + + if (ecf->connections != NGX_CONF_UNSET_UINT) { + return "is duplicate" ; + } + + value = cf->args->elts; + ecf->connections = ngx_atoi(value[1].data, value[1].len); + if (ecf->connections == (ngx_uint_t) NGX_ERROR) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid number \"%s\"", value[1].data); + + return NGX_CONF_ERROR; + } + + cf->cycle->connection_n = ecf->connections; + + return NGX_CONF_OK; +} + + +static char *ngx_event_use(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_event_conf_t *ecf = conf; + + ngx_int_t m; + ngx_str_t *value; + ngx_event_conf_t *old_ecf; + ngx_event_module_t *module; + + if (ecf->use != NGX_CONF_UNSET_UINT) { + return "is duplicate" ; + } + + value = cf->args->elts; + + if (cf->cycle->old_cycle->conf_ctx) { + old_ecf = ngx_event_get_conf(cf->cycle->old_cycle->conf_ctx, + ngx_event_core_module); + } else { + old_ecf = NULL; + } + + + for (m = 0; ngx_modules[m]; m++) { + if (ngx_modules[m]->type != NGX_EVENT_MODULE) { + continue; + } + + module = ngx_modules[m]->ctx; + if (module->name->len == value[1].len) { + if (ngx_strcmp(module->name->data, value[1].data) == 0) { + ecf->use = ngx_modules[m]->ctx_index; + ecf->name = module->name->data; + + if (ngx_process == NGX_PROCESS_SINGLE + && old_ecf + && old_ecf->use != ecf->use) + { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "when the server runs without a master process " + "the \"%s\" event type must be the same as " + "in previous configuration - \"%s\" " + "and it can not be changed on the fly, " + "to change it you need to stop server " + "and start it again", + value[1].data, old_ecf->name); + + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; + } + } + } + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid event type \"%s\"", value[1].data); + + return NGX_CONF_ERROR; +} + + +static char *ngx_event_debug_connection(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf) +{ +#if (NGX_DEBUG) + ngx_event_conf_t *ecf = conf; + + in_addr_t *addr; + ngx_str_t *value; + struct hostent *h; + + value = cf->args->elts; + + /* AF_INET only */ + + if (!(addr = ngx_push_array(&ecf->debug_connection))) { + return NGX_CONF_ERROR; + } + + *addr = inet_addr((char *) value[1].data); + + if (*addr != INADDR_NONE) { + return NGX_OK; + } + + h = gethostbyname((char *) value[1].data); + + if (h == NULL || h->h_addr_list[0] == NULL) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "host %s not found", value[1].data); + return NGX_CONF_ERROR; + } + + *addr = *(in_addr_t *)(h->h_addr_list[0]); + +#else + + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "\"debug_connection\" is ignored, you need to rebuild " + "nginx using --with-debug option to enable it"); + +#endif + + return NGX_OK; +} + + +static void *ngx_event_create_conf(ngx_cycle_t *cycle) +{ + ngx_event_conf_t *ecf; + + ngx_test_null(ecf, ngx_palloc(cycle->pool, sizeof(ngx_event_conf_t)), + NGX_CONF_ERROR); + + ecf->connections = NGX_CONF_UNSET_UINT; + ecf->use = NGX_CONF_UNSET_UINT; + ecf->multi_accept = NGX_CONF_UNSET; + ecf->accept_mutex = NGX_CONF_UNSET; + ecf->accept_mutex_delay = NGX_CONF_UNSET_MSEC; + ecf->name = (void *) NGX_CONF_UNSET; + +#if (NGX_DEBUG) + ngx_init_array(ecf->debug_connection, cycle->pool, 5, sizeof(in_addr_t), + NGX_CONF_ERROR); +#endif + + return ecf; +} + + +static char *ngx_event_init_conf(ngx_cycle_t *cycle, void *conf) +{ + ngx_event_conf_t *ecf = conf; +#if (HAVE_RTSIG) + ngx_core_conf_t *ccf; +#endif + +#if (HAVE_KQUEUE) + + ngx_conf_init_unsigned_value(ecf->connections, DEFAULT_CONNECTIONS); + ngx_conf_init_unsigned_value(ecf->use, ngx_kqueue_module.ctx_index); + ngx_conf_init_ptr_value(ecf->name, ngx_kqueue_module_ctx.name->data); + +#elif (HAVE_DEVPOLL) + + ngx_conf_init_unsigned_value(ecf->connections, DEFAULT_CONNECTIONS); + ngx_conf_init_unsigned_value(ecf->use, ngx_devpoll_module.ctx_index); + ngx_conf_init_ptr_value(ecf->name, ngx_devpoll_module_ctx.name->data); + +#elif (HAVE_EPOLL) + + ngx_conf_init_unsigned_value(ecf->connections, DEFAULT_CONNECTIONS); + ngx_conf_init_unsigned_value(ecf->use, ngx_epoll_module.ctx_index); + ngx_conf_init_ptr_value(ecf->name, ngx_epoll_module_ctx.name->data); + +#elif (HAVE_RTSIG) + + ngx_conf_init_unsigned_value(ecf->connections, DEFAULT_CONNECTIONS); + ngx_conf_init_unsigned_value(ecf->use, ngx_rtsig_module.ctx_index); + ngx_conf_init_ptr_value(ecf->name, ngx_rtsig_module_ctx.name->data); + +#elif (HAVE_SELECT) + +#if (WIN32) + ngx_conf_init_unsigned_value(ecf->connections, DEFAULT_CONNECTIONS); +#else + ngx_conf_init_unsigned_value(ecf->connections, + FD_SETSIZE < DEFAULT_CONNECTIONS ? FD_SETSIZE : DEFAULT_CONNECTIONS); +#endif + + ngx_conf_init_unsigned_value(ecf->use, ngx_select_module.ctx_index); + ngx_conf_init_ptr_value(ecf->name, ngx_select_module_ctx.name->data); + +#else + + ngx_int_t i, m; + ngx_event_module_t *module; + + m = -1; + module = NULL; + + for (i = 0; ngx_modules[i]; i++) { + if (ngx_modules[i]->type == NGX_EVENT_MODULE) { + module = ngx_modules[i]->ctx; + + if (ngx_strcmp(module->name->data, event_core_name.data) == 0) { + continue; + } + + m = ngx_modules[i]->ctx_index; + break; + } + } + + if (m == -1) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, "no events module found"); + return NGX_CONF_ERROR; + } + + ngx_conf_init_unsigned_value(ecf->connections, DEFAULT_CONNECTIONS); + + ngx_conf_init_unsigned_value(ecf->use, m); + ngx_conf_init_ptr_value(ecf->name, module->name->data); + +#endif + + cycle->connection_n = ecf->connections; + + ngx_conf_init_value(ecf->multi_accept, 0); + ngx_conf_init_value(ecf->accept_mutex, 1); + ngx_conf_init_msec_value(ecf->accept_mutex_delay, 500); + +#if (HAVE_RTSIG) + if (ecf->use == ngx_rtsig_module.ctx_index && ecf->accept_mutex == 0) { + ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, + ngx_core_module); + if (ccf->worker_processes) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, + "the \"rtsig\" method requires " + "\"accept_mutex\" to be on"); + return NGX_CONF_ERROR; + } + } +#endif + + return NGX_CONF_OK; +} + + +static char *ngx_accept_mutex_check(ngx_conf_t *cf, void *post, void *data) +{ +#if !(NGX_HAVE_ATOMIC_OPS) + + ngx_flag_t *fp = data; + + *fp = 0; + + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "\"accept_mutex\" is not supported, ignored"); + +#endif + + return NGX_CONF_OK; +} diff --git a/src/event/ngx_event.h b/src/event/ngx_event.h new file mode 100644 --- /dev/null +++ b/src/event/ngx_event.h @@ -0,0 +1,637 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#ifndef _NGX_EVENT_H_INCLUDED_ +#define _NGX_EVENT_H_INCLUDED_ + + +#include +#include + + +#define NGX_INVALID_INDEX 0xd0d0d0d0 + + +#if (HAVE_IOCP) + +typedef struct { + WSAOVERLAPPED ovlp; + ngx_event_t *event; + int error; +} ngx_event_ovlp_t; + +#endif + + +typedef struct { + ngx_uint_t lock; + + ngx_event_t *events; + ngx_event_t *last; +} ngx_event_mutex_t; + + +struct ngx_event_s { + void *data; + + unsigned write:1; + + unsigned accept:1; + + unsigned oneshot:1; + + /* used to detect the stale events in kqueue, rt signals and epoll */ + unsigned instance:1; + + /* + * the event was passed or would be passed to a kernel; + * in aio mode - operation was posted. + */ + unsigned active:1; + + unsigned disabled:1; + + /* the ready event; in aio mode 0 means that no operation can be posted */ + unsigned ready:1; + + /* aio operation is complete */ + unsigned complete:1; + + unsigned eof:1; + unsigned error:1; + + unsigned timedout:1; + unsigned timer_set:1; + + unsigned delayed:1; + + unsigned read_discarded:1; + + unsigned unexpected_eof:1; + + unsigned deferred_accept:1; + + /* the pending eof reported by kqueue or in aio chain operation */ + unsigned pending_eof:1; + +#if !(NGX_THREADS) + unsigned posted_ready:1; +#endif + +#if (WIN32) + /* setsockopt(SO_UPDATE_ACCEPT_CONTEXT) was succesfull */ + unsigned accept_context_updated:1; +#endif + +#if (HAVE_KQUEUE) + unsigned kq_vnode:1; + + /* the pending errno reported by kqueue */ + int kq_errno; +#endif + + /* + * kqueue only: + * accept: number of sockets that wait to be accepted + * read: bytes to read when event is ready + * or lowat when event is set with NGX_LOWAT_EVENT flag + * write: available space in buffer when event is ready + * or lowat when event is set with NGX_LOWAT_EVENT flag + * + * iocp: TODO + * + * otherwise: + * accept: 1 if accept many, 0 otherwise + */ + +#if (HAVE_KQUEUE) || (HAVE_IOCP) + int available; +#else + unsigned available:1; +#endif + + /* TODO rename to handler */ + ngx_event_handler_pt event_handler; + + +#if (HAVE_AIO) + +#if (HAVE_IOCP) + ngx_event_ovlp_t ovlp; +#else + struct aiocb aiocb; +#endif + +#endif + + u_int index; + + ngx_log_t *log; + + /* TODO: threads: padding to cache line */ + + /* + * STUB: The inline of "ngx_rbtree_t rbtree;" + */ + + ngx_int_t rbtree_key; + void *rbtree_left; + void *rbtree_right; + void *rbtree_parent; + char rbtree_color; + + + unsigned closed:1; + +#if (NGX_THREADS) + + unsigned locked:1; + + unsigned posted_ready:1; + unsigned posted_timedout:1; + unsigned posted_eof:1; + +#if (HAVE_KQUEUE) + /* the pending errno reported by kqueue */ + int posted_errno; +#endif + +#if (HAVE_KQUEUE) || (HAVE_IOCP) + int posted_available; +#else + unsigned posted_available:1; +#endif + + ngx_atomic_t *lock; + ngx_atomic_t *own_lock; + +#endif + + /* the links of the posted queue */ + ngx_event_t *next; + ngx_event_t **prev; + + +#if 0 + + /* the threads support */ + + /* + * the event thread context, we store it here + * if $(CC) does not understand __thread declaration + * and pthread_getspecific() is too costly + */ + + void *thr_ctx; + +#if (NGX_EVENT_T_PADDING) + + /* event should not cross cache line in SMP */ + + int padding[NGX_EVENT_T_PADDING]; +#endif +#endif +}; + + +typedef struct { + ngx_int_t (*add)(ngx_event_t *ev, int event, u_int flags); + ngx_int_t (*del)(ngx_event_t *ev, int event, u_int flags); + + ngx_int_t (*enable)(ngx_event_t *ev, int event, u_int flags); + ngx_int_t (*disable)(ngx_event_t *ev, int event, u_int flags); + + ngx_int_t (*add_conn)(ngx_connection_t *c); + ngx_int_t (*del_conn)(ngx_connection_t *c, u_int flags); + + ngx_int_t (*process_changes)(ngx_cycle_t *cycle, ngx_uint_t try); + ngx_int_t (*process_events)(ngx_cycle_t *cycle); + + ngx_int_t (*init)(ngx_cycle_t *cycle); + void (*done)(ngx_cycle_t *cycle); +} ngx_event_actions_t; + + +extern ngx_event_actions_t ngx_event_actions; + + +/* + * The event filter requires to read/write the whole data - + * select, poll, /dev/poll, kqueue, epoll. + */ +#define NGX_USE_LEVEL_EVENT 0x00000001 + +/* + * The event filter is deleted after a notification without an additional + * syscall - select, poll, kqueue, epoll, Solaris 10's event ports. + */ +#define NGX_USE_ONESHOT_EVENT 0x00000002 + +/* + * The event filter notifies only the changes and an initial level - + * kqueue, epoll. + */ +#define NGX_USE_CLEAR_EVENT 0x00000004 + +/* + * The event filter has kqueue features - the eof flag, errno, + * available data, etc. + */ +#define NGX_HAVE_KQUEUE_EVENT 0x00000008 + +/* + * The event filter supports low water mark - kqueue's NOTE_LOWAT. + * kqueue in FreeBSD 4.1-4.2 has no NOTE_LOWAT so we need a separate flag. + */ +#define NGX_HAVE_LOWAT_EVENT 0x00000010 + +/* + * The event filter requires to do i/o operation until EAGAIN - + * epoll, rt signals. + */ +#define NGX_HAVE_GREEDY_EVENT 0x00000020 + +/* + * The event filter is epoll, + */ +#define NGX_USE_EPOLL_EVENT 0x00000040 + +/* + * No need to add or delete the event filters - rt signals. + */ +#define NGX_USE_RTSIG_EVENT 0x00000080 + +/* + * No need to add or delete the event filters - overlapped, aio_read, + * aioread, io_submit. + */ +#define NGX_USE_AIO_EVENT 0x00000100 + +/* + * Need to add socket or handle only once - i/o completion port. + * It also requires HAVE_AIO and NGX_USE_AIO_EVENT to be set. + */ +#define NGX_USE_IOCP_EVENT 0x00000200 + + + +/* + * The event filter is deleted before the closing file. + * Has no meaning for select, poll, epoll. + * + * kqueue: kqueue deletes event filters for file that closed + * so we need only to delete filters in user-level batch array + * /dev/poll: we need to flush POLLREMOVE event before closing file + */ + +#define NGX_CLOSE_EVENT 1 +#define NGX_DISABLE_EVENT 2 + + +/* these flags have a meaning only for kqueue */ +#define NGX_LOWAT_EVENT 0 +#define NGX_VNODE_EVENT 0 + + +#if (HAVE_KQUEUE) + +#define NGX_READ_EVENT EVFILT_READ +#define NGX_WRITE_EVENT EVFILT_WRITE + +#undef NGX_VNODE_EVENT +#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. + */ +#undef NGX_CLOSE_EVENT +#define NGX_CLOSE_EVENT EV_EOF + +#undef NGX_LOWAT_EVENT +#define NGX_LOWAT_EVENT EV_FLAG1 + +#define NGX_LEVEL_EVENT 0 +#define NGX_ONESHOT_EVENT EV_ONESHOT +#define NGX_CLEAR_EVENT EV_CLEAR + +#undef NGX_DISABLE_EVENT +#define NGX_DISABLE_EVENT EV_DISABLE + + +#elif (HAVE_DEVPOLL) + +#define NGX_READ_EVENT POLLIN +#define NGX_WRITE_EVENT POLLOUT + +#define NGX_LEVEL_EVENT 0 +#define NGX_ONESHOT_EVENT 1 + + +#elif (HAVE_EPOLL) + +#define NGX_READ_EVENT EPOLLIN +#define NGX_WRITE_EVENT EPOLLOUT + +#define NGX_LEVEL_EVENT 0 +#define NGX_CLEAR_EVENT EPOLLET +#define NGX_ONESHOT_EVENT 0x70000000 +#if 0 +#define NGX_ONESHOT_EVENT EPOLLONESHOT +#endif + + +#elif (HAVE_POLL) + +#define NGX_READ_EVENT POLLIN +#define NGX_WRITE_EVENT POLLOUT + +#define NGX_LEVEL_EVENT 0 +#define NGX_ONESHOT_EVENT 1 + + +#else /* select */ + +#define NGX_READ_EVENT 0 +#define NGX_WRITE_EVENT 1 + +#define NGX_LEVEL_EVENT 0 +#define NGX_ONESHOT_EVENT 1 + +#endif /* HAVE_KQUEUE */ + + +#if (HAVE_IOCP) +#define NGX_IOCP_ACCEPT 0 +#define NGX_IOCP_IO 1 +#define NGX_IOCP_CONNECT 2 +#endif + + +#ifndef NGX_CLEAR_EVENT +#define NGX_CLEAR_EVENT 0 /* dummy declaration */ +#endif + + +#define ngx_process_changes ngx_event_actions.process_changes +#define ngx_process_events ngx_event_actions.process_events +#define ngx_done_events ngx_event_actions.done + +#define ngx_add_event ngx_event_actions.add +#define ngx_del_event ngx_event_actions.del +#define ngx_add_conn ngx_event_actions.add_conn +#define ngx_del_conn ngx_event_actions.del_conn + +#define ngx_add_timer ngx_event_add_timer +#define ngx_del_timer ngx_event_del_timer + + +#define ngx_recv ngx_io.recv +#define ngx_recv_chain ngx_io.recv_chain +#define ngx_send ngx_io.send +#define ngx_send_chain ngx_io.send_chain + + + +#define NGX_EVENT_MODULE 0x544E5645 /* "EVNT" */ +#define NGX_EVENT_CONF 0x02000000 + + +typedef struct { + ngx_uint_t connections; + ngx_uint_t use; + + ngx_flag_t multi_accept; + ngx_flag_t accept_mutex; + + ngx_msec_t accept_mutex_delay; + + u_char *name; + +#if (NGX_DEBUG) + ngx_array_t debug_connection; +#endif +} ngx_event_conf_t; + + +typedef struct { + ngx_str_t *name; + + void *(*create_conf)(ngx_cycle_t *cycle); + char *(*init_conf)(ngx_cycle_t *cycle, void *conf); + + ngx_event_actions_t actions; +} ngx_event_module_t; + + +extern ngx_atomic_t *ngx_connection_counter; + +extern ngx_atomic_t *ngx_accept_mutex_ptr; +extern ngx_atomic_t *ngx_accept_mutex; +extern ngx_uint_t ngx_accept_mutex_held; +extern ngx_msec_t ngx_accept_mutex_delay; +extern ngx_int_t ngx_accept_disabled; + + +#if (NGX_STAT_STUB) + +extern ngx_atomic_t *ngx_stat_accepted; +extern ngx_atomic_t *ngx_stat_requests; +extern ngx_atomic_t *ngx_stat_active; +extern ngx_atomic_t *ngx_stat_reading; +extern ngx_atomic_t *ngx_stat_writing; + +#endif + + + +#define ngx_accept_mutex_unlock() \ + if (ngx_accept_mutex_held) { \ + *ngx_accept_mutex = 0; \ + } + + +extern ngx_uint_t ngx_event_flags; +extern ngx_module_t ngx_events_module; +extern ngx_module_t ngx_event_core_module; + + +#define ngx_event_get_conf(conf_ctx, module) \ + (*(ngx_get_conf(conf_ctx, ngx_events_module))) [module.ctx_index]; + + + +void ngx_event_accept(ngx_event_t *ev); +ngx_int_t ngx_trylock_accept_mutex(ngx_cycle_t *cycle); +ngx_int_t ngx_disable_accept_events(ngx_cycle_t *cycle); +ngx_int_t ngx_enable_accept_events(ngx_cycle_t *cycle); + + +#if (WIN32) +void ngx_event_acceptex(ngx_event_t *ev); +int ngx_event_post_acceptex(ngx_listening_t *ls, int n); +#endif + + +/* used in ngx_log_debugX() */ +#define ngx_event_ident(p) ((ngx_connection_t *) (p))->fd + + +#include +#include +#include + +#if (WIN32) +#include +#endif + + + +ngx_inline static int ngx_handle_read_event(ngx_event_t *rev, u_int flags) +{ + if (ngx_event_flags & NGX_USE_CLEAR_EVENT) { + + /* kqueue */ + + if (!rev->active && !rev->ready) { + if (ngx_add_event(rev, NGX_READ_EVENT, NGX_CLEAR_EVENT) + == NGX_ERROR) { + return NGX_ERROR; + } + } + + return NGX_OK; + + } else if (ngx_event_flags & NGX_USE_LEVEL_EVENT) { + + /* select, poll, /dev/poll */ + + if (!rev->active && !rev->ready) { + if (ngx_add_event(rev, NGX_READ_EVENT, NGX_LEVEL_EVENT) + == NGX_ERROR) + { + return NGX_ERROR; + } + + return NGX_OK; + } + + if (rev->active && (rev->ready || (flags & NGX_CLOSE_EVENT))) { + if (ngx_del_event(rev, NGX_READ_EVENT, flags) == NGX_ERROR) { + return NGX_ERROR; + } + + return NGX_OK; + } + } + + /* aio, iocp, epoll, rtsig */ + + return NGX_OK; +} + + +ngx_inline static int ngx_handle_level_read_event(ngx_event_t *rev) +{ + if (ngx_event_flags & NGX_USE_LEVEL_EVENT) { + if (!rev->active && !rev->ready) { + if (ngx_add_event(rev, NGX_READ_EVENT, NGX_LEVEL_EVENT) + == NGX_ERROR) + { + return NGX_ERROR; + } + + return NGX_OK; + } + + if (rev->active && rev->ready) { + if (ngx_del_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) { + return NGX_ERROR; + } + + return NGX_OK; + } + } + + return NGX_OK; +} + + +ngx_inline static int ngx_handle_write_event(ngx_event_t *wev, u_int flags) +{ + if (ngx_event_flags & NGX_USE_CLEAR_EVENT) { + + /* kqueue */ + + if (!wev->active && !wev->ready) { + if (ngx_add_event(wev, NGX_WRITE_EVENT, NGX_CLEAR_EVENT|flags) + == NGX_ERROR) + { + return NGX_ERROR; + } + } + + return NGX_OK; + + } else if (ngx_event_flags & NGX_USE_LEVEL_EVENT) { + + /* select, poll, /dev/poll */ + + if (!wev->active && !wev->ready) { + if (ngx_add_event(wev, NGX_WRITE_EVENT, NGX_LEVEL_EVENT) + == NGX_ERROR) + { + return NGX_ERROR; + } + + return NGX_OK; + } + + if (wev->active && wev->ready) { + if (ngx_del_event(wev, NGX_WRITE_EVENT, 0) == NGX_ERROR) { + return NGX_ERROR; + } + + return NGX_OK; + } + } + + /* aio, iocp, epoll, rtsig */ + + return NGX_OK; +} + + +ngx_inline static int ngx_handle_level_write_event(ngx_event_t *wev) +{ + if (ngx_event_flags & NGX_USE_LEVEL_EVENT) { + if (!wev->active && !wev->ready) { + if (ngx_add_event(wev, NGX_WRITE_EVENT, NGX_LEVEL_EVENT) + == NGX_ERROR) + { + return NGX_ERROR; + } + + return NGX_OK; + } + + if (wev->active && wev->ready) { + if (ngx_del_event(wev, NGX_WRITE_EVENT, 0) == NGX_ERROR) { + return NGX_ERROR; + } + + return NGX_OK; + } + } + + return NGX_OK; +} + + +#endif /* _NGX_EVENT_H_INCLUDED_ */ diff --git a/src/event/ngx_event_accept.c b/src/event/ngx_event_accept.c new file mode 100644 --- /dev/null +++ b/src/event/ngx_event_accept.c @@ -0,0 +1,475 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include +#include + + +typedef struct { + int flag; + u_char *name; +} ngx_accept_log_ctx_t; + + +static void ngx_close_accepted_socket(ngx_socket_t s, ngx_log_t *log); +static size_t ngx_accept_log_error(void *data, char *buf, size_t len); + + +void ngx_event_accept(ngx_event_t *ev) +{ + ngx_uint_t instance, accepted; + socklen_t len; + struct sockaddr *sa; + ngx_err_t err; + ngx_log_t *log; + ngx_pool_t *pool; + ngx_socket_t s; + ngx_event_t *rev, *wev; + ngx_connection_t *c, *ls; + ngx_event_conf_t *ecf; + ngx_accept_log_ctx_t *ctx; + + ecf = ngx_event_get_conf(ngx_cycle->conf_ctx, ngx_event_core_module); + + if (ngx_event_flags & NGX_USE_RTSIG_EVENT) { + ev->available = 1; + + } else if (!(ngx_event_flags & NGX_HAVE_KQUEUE_EVENT)) { + ev->available = ecf->multi_accept; + } + + ls = ev->data; + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0, + "accept on %s, ready: %d", + ls->listening->addr_text.data, ev->available); + + ev->ready = 0; + accepted = 0; + pool = NULL; + + do { + + if (pool == NULL) { + + /* + * Create the pool before accept() to avoid the copying of + * the sockaddr. Although accept() can fail it is uncommon + * case and besides the pool can be got from the free pool list + */ + + if (!(pool = ngx_create_pool(ls->listening->pool_size, ev->log))) { + return; + } + } + + if (!(sa = ngx_palloc(pool, ls->listening->socklen))) { + ngx_destroy_pool(pool); + return; + } + + if (!(log = ngx_palloc(pool, sizeof(ngx_log_t)))) { + ngx_destroy_pool(pool); + return; + } + + ngx_memcpy(log, ls->log, sizeof(ngx_log_t)); + pool->log = log; + + if (!(ctx = ngx_palloc(pool, sizeof(ngx_accept_log_ctx_t)))) { + ngx_destroy_pool(pool); + return; + } + + /* -1 disables the connection number logging */ + ctx->flag = -1; + ctx->name = ls->listening->addr_text.data; + + log->data = ctx; + log->handler = ngx_accept_log_error; + + len = ls->listening->socklen; + + s = accept(ls->fd, sa, &len); + if (s == -1) { + err = ngx_socket_errno; + + if (err == NGX_EAGAIN) { +#if 0 + if (!(ngx_event_flags & NGX_USE_RTSIG_EVENT)) + { + ngx_log_error(NGX_LOG_NOTICE, log, err, + "EAGAIN after %d accepted connection(s)", + accepted); + } +#endif + + ngx_destroy_pool(pool); + return; + } + + ngx_log_error(NGX_LOG_ALERT, ev->log, err, + "accept() on %s failed", + ls->listening->addr_text.data); + + if (err == NGX_ECONNABORTED) { + if (ngx_event_flags & NGX_HAVE_KQUEUE_EVENT) { + ev->available--; + } + + if (ev->available) { + /* reuse the previously allocated pool */ + continue; + } + } + + ngx_destroy_pool(pool); + return; + } + +#if (NGX_STAT_STUB) + (*ngx_stat_accepted)++; +#endif + + ngx_accept_disabled = (ngx_uint_t) s + NGX_ACCEPT_THRESHOLD + - ecf->connections; + + /* disable warning: Win32 SOCKET is u_int while UNIX socket is int */ + + if ((ngx_uint_t) s >= ecf->connections) { + + ngx_log_error(NGX_LOG_ALERT, ev->log, 0, + "accept() on %s returned socket #%d while " + "only %d connections was configured, " + "closing the connection", + ls->listening->addr_text.data, s, ecf->connections); + + ngx_close_accepted_socket(s, log); + ngx_destroy_pool(pool); + return; + } + +#if (NGX_STAT_STUB) + (*ngx_stat_active)++; +#endif + + /* set a blocking mode for aio and non-blocking mode for the others */ + + if (ngx_inherited_nonblocking) { + if ((ngx_event_flags & NGX_USE_AIO_EVENT)) { + if (ngx_blocking(s) == -1) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_socket_errno, + ngx_blocking_n " failed"); + + ngx_close_accepted_socket(s, log); + ngx_destroy_pool(pool); + return; + } + } + + } else { + if (!(ngx_event_flags & (NGX_USE_AIO_EVENT|NGX_USE_RTSIG_EVENT))) { + if (ngx_nonblocking(s) == -1) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_socket_errno, + ngx_nonblocking_n " failed"); + + ngx_close_accepted_socket(s, log); + ngx_destroy_pool(pool); + return; + } + } + } + +#if (WIN32) + /* + * Winsock assignes a socket number divisible by 4 + * so to find a connection we divide a socket number by 4. + */ + + if (s % 4) { + ngx_log_error(NGX_LOG_EMERG, ev->log, 0, + "accept() on %s returned socket #%d, " + "not divisible by 4", + ls->listening->addr_text.data, s); + exit(1); + } + + c = &ngx_cycle->connections[s / 4]; + rev = &ngx_cycle->read_events[s / 4]; + wev = &ngx_cycle->write_events[s / 4]; +#else + c = &ngx_cycle->connections[s]; + rev = &ngx_cycle->read_events[s]; + wev = &ngx_cycle->write_events[s]; +#endif + + instance = rev->instance; + +#if (NGX_THREADS) + + if (*(&c->lock)) { + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ev->log, 0, + "spinlock in accept, fd:%", s); + ngx_spinlock(&c->lock, 1000); + ngx_unlock(&c->lock); + } + +#endif + + ngx_memzero(rev, sizeof(ngx_event_t)); + ngx_memzero(wev, sizeof(ngx_event_t)); + ngx_memzero(c, sizeof(ngx_connection_t)); + + c->pool = pool; + + c->listening = ls->listening; + c->sockaddr = sa; + c->socklen = len; + + rev->instance = !instance; + wev->instance = !instance; + + rev->index = NGX_INVALID_INDEX; + wev->index = NGX_INVALID_INDEX; + + rev->data = c; + wev->data = c; + + c->read = rev; + c->write = wev; + + c->fd = s; + c->unexpected_eof = 1; + + wev->write = 1; + wev->ready = 1; + + if (ngx_event_flags & (NGX_USE_AIO_EVENT|NGX_USE_RTSIG_EVENT)) { + /* epoll, rtsig, aio, iocp */ + rev->ready = 1; + } + + if (ev->deferred_accept) { + rev->ready = 1; + } + + c->ctx = ls->ctx; + c->servers = ls->servers; + + c->recv = ngx_recv; + c->send_chain = ngx_send_chain; + + c->log = log; + rev->log = log; + wev->log = log; + + /* + * TODO: MT: - atomic increment (x86: lock xadd) + * or protection by critical section or light mutex + * + * TODO: MP: - allocated in a shared memory + * - atomic increment (x86: lock xadd) + * or protection by critical section or light mutex + */ + + c->number = ngx_atomic_inc(ngx_connection_counter); + +#if (NGX_THREADS) + rev->lock = &c->lock; + wev->lock = &c->lock; + rev->own_lock = &c->lock; + wev->own_lock = &c->lock; +#endif + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0, + "accept: fd:%d c:%d", s, c->number); + + if (c->listening->addr_ntop) { + c->addr_text.data = ngx_palloc(c->pool, + c->listening->addr_text_max_len); + if (c->addr_text.data == NULL) { + ngx_close_accepted_socket(s, log); + ngx_destroy_pool(pool); + return; + } + + c->addr_text.len = ngx_sock_ntop(c->listening->family, c->sockaddr, + c->addr_text.data, + c->listening->addr_text_max_len); + if (c->addr_text.len == 0) { + ngx_close_accepted_socket(s, log); + ngx_destroy_pool(pool); + return; + } + } + +#if (NGX_DEBUG) + { + + uint32_t *addr; + in_addr_t i; + struct sockaddr_in *addr_in; + + addr_in = (struct sockaddr_in *) sa; + addr = ecf->debug_connection.elts; + for (i = 0; i < ecf->debug_connection.nelts; i++) { + if (addr[i] == addr_in->sin_addr.s_addr) { + log->log_level = NGX_LOG_DEBUG_CONNECTION|NGX_LOG_DEBUG_ALL; + break; + } + } + + } +#endif + + if (ngx_add_conn && (ngx_event_flags & NGX_USE_EPOLL_EVENT) == 0) { + if (ngx_add_conn(c) == NGX_ERROR) { + ngx_close_accepted_socket(s, log); + ngx_destroy_pool(pool); + return; + } + } + + pool = NULL; + + log->data = NULL; + log->handler = NULL; + + ls->listening->handler(c); + + if (ngx_event_flags & NGX_HAVE_KQUEUE_EVENT) { + ev->available--; + } + + accepted++; + + } while (ev->available); +} + + +ngx_int_t ngx_trylock_accept_mutex(ngx_cycle_t *cycle) +{ + if (*ngx_accept_mutex == 0 + && ngx_atomic_cmp_set(ngx_accept_mutex, 0, ngx_pid)) + { + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, + "accept mutex locked"); + + if (!ngx_accept_mutex_held) { + if (ngx_enable_accept_events(cycle) == NGX_ERROR) { + *ngx_accept_mutex = 0; + return NGX_ERROR; + } + + ngx_accept_mutex_held = 1; + } + + return NGX_OK; + } + + if (ngx_accept_mutex_held) { + if (ngx_disable_accept_events(cycle) == NGX_ERROR) { + return NGX_ERROR; + } + + ngx_accept_mutex_held = 0; + } + + return NGX_OK; +} + + +ngx_int_t ngx_enable_accept_events(ngx_cycle_t *cycle) +{ + ngx_uint_t i; + ngx_listening_t *s; + + s = cycle->listening.elts; + for (i = 0; i < cycle->listening.nelts; i++) { + + /* + * we do not need to handle the Winsock sockets here (divide a socket + * number by 4) because this function would never called + * in the Winsock environment + */ + + if (ngx_event_flags & NGX_USE_RTSIG_EVENT) { + if (ngx_add_conn(&cycle->connections[s[i].fd]) == NGX_ERROR) { + return NGX_ERROR; + } + + } else { + if (ngx_add_event(&cycle->read_events[s[i].fd], NGX_READ_EVENT, 0) + == NGX_ERROR) + { + return NGX_ERROR; + } + } + } + + return NGX_OK; +} + + +ngx_int_t ngx_disable_accept_events(ngx_cycle_t *cycle) +{ + ngx_uint_t i; + ngx_listening_t *s; + + s = cycle->listening.elts; + for (i = 0; i < cycle->listening.nelts; i++) { + + /* + * we do not need to handle the Winsock sockets here (divide a socket + * number by 4) because this function would never called + * in the Winsock environment + */ + + if (ngx_event_flags & NGX_USE_RTSIG_EVENT) { + if (!cycle->connections[s[i].fd].read->active) { + continue; + } + + if (ngx_del_conn(&cycle->connections[s[i].fd], NGX_DISABLE_EVENT) + == NGX_ERROR) + { + return NGX_ERROR; + } + + } else { + if (!cycle->read_events[s[i].fd].active) { + continue; + } + + if (ngx_del_event(&cycle->read_events[s[i].fd], NGX_READ_EVENT, + NGX_DISABLE_EVENT) == NGX_ERROR) + { + return NGX_ERROR; + } + } + } + + return NGX_OK; +} + + +static void ngx_close_accepted_socket(ngx_socket_t s, ngx_log_t *log) +{ + if (ngx_close_socket(s) == -1) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_socket_errno, + ngx_close_socket_n " failed"); + } +} + + +static size_t ngx_accept_log_error(void *data, char *buf, size_t len) +{ + ngx_accept_log_ctx_t *ctx = data; + + return ngx_snprintf(buf, len, " while accept() on %s", ctx->name); +} diff --git a/src/event/ngx_event_busy_lock.c b/src/event/ngx_event_busy_lock.c new file mode 100644 --- /dev/null +++ b/src/event/ngx_event_busy_lock.c @@ -0,0 +1,309 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include + + +static int ngx_event_busy_lock_look_cachable(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); + + +/* + * NGX_OK: the busy lock is held + * NGX_AGAIN: the all busy locks are held but we will wait the specified time + * NGX_BUSY: ctx->timer == 0: there are many the busy locks + * ctx->timer != 0: there are many the waiting locks + * NGX_ERROR: an error occured while the mutex locking + */ + +ngx_int_t ngx_event_busy_lock(ngx_event_busy_lock_t *bl, + ngx_event_busy_lock_ctx_t *ctx) +{ + ngx_int_t rc; + + if (ngx_mutex_lock(bl->mutex) == NGX_ERROR) { + return NGX_ERROR; + } + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ctx->event->log, 0, + "event busy lock: b:%d mb:%d", + bl->busy, bl->max_busy); + + if (bl->busy < bl->max_busy) { + bl->busy++; + rc = NGX_OK; + + } else if (ctx->timer && bl->waiting < bl->max_waiting) { + bl->waiting++; + ngx_add_timer(ctx->event, ctx->timer); + ctx->event->event_handler = ngx_event_busy_lock_handler; + + if (bl->events) { + bl->last->next = ctx; + + } else { + bl->events = ctx; + } + + bl->last = ctx; + + rc = NGX_AGAIN; + + } else { + rc = NGX_BUSY; + } + + ngx_mutex_unlock(bl->mutex); + + return rc; +} + + +ngx_int_t ngx_event_busy_lock_cachable(ngx_event_busy_lock_t *bl, + ngx_event_busy_lock_ctx_t *ctx) +{ + ngx_int_t rc; + + if (ngx_mutex_lock(bl->mutex) == NGX_ERROR) { + return NGX_ERROR; + } + + rc = ngx_event_busy_lock_look_cachable(bl, ctx); + + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ctx->event->log, 0, + "event busy lock: %d w:%d mw:%d", + rc, bl->waiting, bl->max_waiting); + + /* + * NGX_OK: no the same request, there is free slot and we locked it + * NGX_BUSY: no the same request and there is no free slot + * NGX_AGAIN: the same request is processing + */ + + if (rc == NGX_AGAIN) { + + if (ctx->timer && bl->waiting < bl->max_waiting) { + bl->waiting++; + ngx_add_timer(ctx->event, ctx->timer); + ctx->event->event_handler = ngx_event_busy_lock_handler; + + if (bl->events == NULL) { + bl->events = ctx; + } else { + bl->last->next = ctx; + } + bl->last = ctx; + + } else { + rc = NGX_BUSY; + } + } + + ngx_mutex_unlock(bl->mutex); + + return rc; +} + + +ngx_int_t ngx_event_busy_unlock(ngx_event_busy_lock_t *bl, + ngx_event_busy_lock_ctx_t *ctx) +{ + ngx_event_t *ev; + ngx_event_busy_lock_ctx_t *wakeup; + + if (ngx_mutex_lock(bl->mutex) == NGX_ERROR) { + return NGX_ERROR; + } + + if (bl->events) { + wakeup = bl->events; + bl->events = bl->events->next; + + } else { + wakeup = NULL; + bl->busy--; + } + + /* + * MP: all ctx's and their queue must be in shared memory, + * each ctx has pid to wake up + */ + + if (wakeup == NULL) { + ngx_mutex_unlock(bl->mutex); + return NGX_OK; + } + + if (ctx->md5) { + for (wakeup = bl->events; wakeup; wakeup = wakeup->next) { + if (wakeup->md5 == NULL || wakeup->slot != ctx->slot) { + continue; + } + + wakeup->handler = ngx_event_busy_lock_posted_handler; + wakeup->cache_updated = 1; + + ev = wakeup->event; + + if (ngx_mutex_lock(ngx_posted_events_mutex) == NGX_ERROR) { + return NGX_ERROR; + } + + ngx_post_event(ev); + + ngx_mutex_unlock(ngx_posted_events_mutex); + } + + ngx_mutex_unlock(bl->mutex); + + } else { + bl->waiting--; + + ngx_mutex_unlock(bl->mutex); + + wakeup->handler = ngx_event_busy_lock_posted_handler; + wakeup->locked = 1; + + ev = wakeup->event; + + if (ev->timer_set) { + ngx_del_timer(ev); + } + + if (ngx_mutex_lock(ngx_posted_events_mutex) == NGX_ERROR) { + return NGX_ERROR; + } + + ngx_post_event(ev); + + ngx_mutex_unlock(ngx_posted_events_mutex); + } + + return NGX_OK; +} + + +ngx_int_t ngx_event_busy_lock_cancel(ngx_event_busy_lock_t *bl, + ngx_event_busy_lock_ctx_t *ctx) +{ + ngx_event_busy_lock_ctx_t *c, *p; + + if (ngx_mutex_lock(bl->mutex) == NGX_ERROR) { + return NGX_ERROR; + } + + bl->waiting--; + + if (ctx == bl->events) { + bl->events = ctx->next; + + } else { + p = bl->events; + for (c = bl->events->next; c; c = c->next) { + if (c == ctx) { + p->next = ctx->next; + break; + } + p = c; + } + } + + ngx_mutex_unlock(bl->mutex); + + return NGX_OK; +} + + +static int ngx_event_busy_lock_look_cachable(ngx_event_busy_lock_t *bl, + ngx_event_busy_lock_ctx_t *ctx) +{ + ngx_int_t free; + ngx_uint_t i, bit, cachable, mask; + + bit = 0; + cachable = 0; + free = -1; + +#if (NGX_SUPPRESS_WARN) + mask = 0; +#endif + + for (i = 0; i < bl->max_busy; i++) { + + if ((bit & 7) == 0) { + mask = bl->md5_mask[i / 8]; + } + + if (mask & 1) { + if (ngx_memcmp(&bl->md5[i * 16], ctx->md5, 16) == 0) { + ctx->waiting = 1; + ctx->slot = i; + return NGX_AGAIN; + } + cachable++; + + } else if (free == -1) { + free = i; + } + + if (cachable == bl->cachable) { + if (free == -1 && cachable < bl->max_busy) { + free = i + 1; + } + + break; + } + + mask >>= 1; + bit++; + } + + if (free == -1) { + return NGX_BUSY; + } + +#if 0 + if (bl->busy == bl->max_busy) { + return NGX_BUSY; + } +#endif + + ngx_memcpy(&bl->md5[free * 16], ctx->md5, 16); + bl->md5_mask[free / 8] |= 1 << (free & 7); + ctx->slot = free; + + bl->cachable++; + bl->busy++; + + return NGX_OK; +} + + +static void ngx_event_busy_lock_handler(ngx_event_t *ev) +{ + if (ngx_mutex_lock(ngx_posted_events_mutex) == NGX_ERROR) { + return; + } + + ngx_post_event(ev); + + ngx_mutex_unlock(ngx_posted_events_mutex); + + ev->event_handler = ngx_event_busy_lock_posted_handler; +} + + +static void ngx_event_busy_lock_posted_handler(ngx_event_t *ev) +{ + ngx_event_busy_lock_ctx_t *ctx; + + ctx = ev->data; + ctx->handler(ev); +} diff --git a/src/event/ngx_event_busy_lock.h b/src/event/ngx_event_busy_lock.h new file mode 100644 --- /dev/null +++ b/src/event/ngx_event_busy_lock.h @@ -0,0 +1,64 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#ifndef _NGX_EVENT_BUSY_LOCK_H_INCLUDED_ +#define _NGX_EVENT_BUSY_LOCK_H_INCLUDED_ + + +#include +#include +#include + +typedef struct ngx_event_busy_lock_ctx_s ngx_event_busy_lock_ctx_t; + +struct ngx_event_busy_lock_ctx_s { + ngx_event_t *event; + ngx_event_handler_pt handler; + void *data; + ngx_msec_t timer; + + unsigned locked:1; + unsigned waiting:1; + unsigned cache_updated:1; + + char *md5; + ngx_int_t slot; + + ngx_event_busy_lock_ctx_t *next; +}; + + +typedef struct { + u_char *md5_mask; + char *md5; + ngx_uint_t cachable; + + ngx_uint_t busy; + ngx_uint_t max_busy; + + ngx_uint_t waiting; + ngx_uint_t max_waiting; + + ngx_event_busy_lock_ctx_t *events; + ngx_event_busy_lock_ctx_t *last; + +#if (NGX_THREADS) + ngx_mutex_t *mutex; +#endif +} ngx_event_busy_lock_t; + + +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_event_busy_lock_ctx_t *ctx); +ngx_int_t ngx_event_busy_unlock(ngx_event_busy_lock_t *bl, + ngx_event_busy_lock_ctx_t *ctx); +ngx_int_t ngx_event_busy_lock_cancel(ngx_event_busy_lock_t *bl, + ngx_event_busy_lock_ctx_t *ctx); + + +#endif /* _NGX_EVENT_BUSY_LOCK_H_INCLUDED_ */ diff --git a/src/event/ngx_event_connect.c b/src/event/ngx_event_connect.c new file mode 100644 --- /dev/null +++ b/src/event/ngx_event_connect.c @@ -0,0 +1,379 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include +#include +#include + + +/* AF_INET only */ + +int ngx_event_connect_peer(ngx_peer_connection_t *pc) +{ + int rc; + ngx_uint_t instance; + u_int event; + time_t now; + ngx_err_t err; + ngx_peer_t *peer; + ngx_socket_t s; + ngx_event_t *rev, *wev; + ngx_connection_t *c; + ngx_event_conf_t *ecf; + struct sockaddr_in addr; + + now = ngx_time(); + + /* ngx_lock_mutex(pc->peers->mutex); */ + + if (pc->peers->last_cached) { + + /* cached connection */ + + c = pc->peers->cached[pc->peers->last_cached]; + pc->peers->last_cached--; + + /* ngx_unlock_mutex(pc->peers->mutex); */ + +#if (NGX_THREADS) + c->read->lock = c->read->own_lock; + c->write->lock = c->write->own_lock; +#endif + + pc->connection = c; + pc->cached = 1; + return NGX_OK; + } + + pc->cached = 0; + pc->connection = NULL; + + if (pc->peers->number == 1) { + peer = &pc->peers->peers[0]; + + } else { + + /* there are several peers */ + + if (pc->tries == pc->peers->number) { + + /* it's a first try - get a current peer */ + + pc->cur_peer = pc->peers->current++; + + if (pc->peers->current >= pc->peers->number) { + pc->peers->current = 0; + } + } + + if (pc->peers->max_fails == 0) { + peer = &pc->peers->peers[pc->cur_peer]; + + } else { + + /* the peers support a fault tolerance */ + + for ( ;; ) { + peer = &pc->peers->peers[pc->cur_peer]; + + if (peer->fails <= pc->peers->max_fails + || (now - peer->accessed > pc->peers->fail_timeout)) + { + break; + } + + pc->cur_peer++; + + if (pc->cur_peer >= pc->peers->number) { + pc->cur_peer = 0; + } + + pc->tries--; + + if (pc->tries == 0) { + /* ngx_unlock_mutex(pc->peers->mutex); */ + + return NGX_ERROR; + } + } + } + } + + /* ngx_unlock_mutex(pc->peers->mutex); */ + + + s = ngx_socket(AF_INET, SOCK_STREAM, IPPROTO_IP, 0); + + if (s == -1) { + ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno, + ngx_socket_n " failed"); + return NGX_ERROR; + } + + + ecf = ngx_event_get_conf(ngx_cycle->conf_ctx, ngx_event_core_module); + + /* disable warning: Win32 SOCKET is u_int while UNIX socket is int */ + + if ((ngx_uint_t) s >= ecf->connections) { + + ngx_log_error(NGX_LOG_ALERT, pc->log, 0, + "socket() returned socket #%d while only %d " + "connections was configured, closing the socket", + s, ecf->connections); + + if (ngx_close_socket(s) == -1) { + ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno, + ngx_close_socket_n "failed"); + } + + /* TODO: sleep for some time */ + + return NGX_ERROR; + } + + + if (pc->rcvbuf) { + if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, + (const void *) &pc->rcvbuf, sizeof(int)) == -1) { + ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno, + "setsockopt(SO_RCVBUF) failed"); + + 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; + } + } + + if (ngx_nonblocking(s) == -1) { + ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno, + ngx_nonblocking_n " failed"); + + 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; + } + +#if (WIN32) + /* + * Winsock assignes a socket number divisible by 4 + * so to find a connection we divide a socket number by 4. + */ + + if (s % 4) { + ngx_log_error(NGX_LOG_EMERG, pc->log, 0, + ngx_socket_n + " created socket %d, not divisible by 4", s); + exit(1); + } + + c = &ngx_cycle->connections[s / 4]; + rev = &ngx_cycle->read_events[s / 4]; + wev = &ngx_cycle->write_events[s / 4]; + +#else + + c = &ngx_cycle->connections[s]; + rev = &ngx_cycle->read_events[s]; + wev = &ngx_cycle->write_events[s]; + +#endif + + instance = rev->instance; + +#if (NGX_THREADS) + + if (*(&c->lock)) { + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, pc->log, 0, + "spinlock in connect, fd:%d", s); + ngx_spinlock(&c->lock, 1000); + ngx_unlock(&c->lock); + } + +#endif + + ngx_memzero(c, sizeof(ngx_connection_t)); + ngx_memzero(rev, sizeof(ngx_event_t)); + ngx_memzero(wev, sizeof(ngx_event_t)); + + rev->instance = !instance; + wev->instance = !instance; + + rev->index = NGX_INVALID_INDEX; + wev->index = NGX_INVALID_INDEX; + + rev->data = c; + wev->data = c; + + c->read = rev; + c->write = wev; + wev->write = 1; + + c->log = pc->log; + rev->log = pc->log; + wev->log = pc->log; + + c->fd = s; + + c->log_error = pc->log_error; + + pc->connection = c; + + /* + * TODO: MT: - atomic increment (x86: lock xadd) + * or protection by critical section or mutex + * + * TODO: MP: - allocated in a shared memory + * - atomic increment (x86: lock xadd) + * or protection by critical section or mutex + */ + + c->number = ngx_atomic_inc(ngx_connection_counter); + +#if (NGX_THREADS) + 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; + } + } + + ngx_memzero(&addr, sizeof(struct sockaddr_in)); + + addr.sin_family = AF_INET; + addr.sin_port = peer->port; + addr.sin_addr.s_addr = peer->addr; + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, pc->log, 0, + "connect to %s, #%d", peer->addr_port_text.data, c->number); + + rc = connect(s, (struct sockaddr *) &addr, sizeof(struct sockaddr_in)); + + if (rc == -1) { + err = ngx_socket_errno; + + /* Winsock returns WSAEWOULDBLOCK (NGX_EAGAIN) */ + + if (err != NGX_EINPROGRESS && err != NGX_EAGAIN) { + ngx_connection_error(c, err, "connect() failed"); + + if (ngx_close_socket(s) == -1) { + ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno, + ngx_close_socket_n " failed"); + } + + c->fd = (ngx_socket_t) -1; + + return NGX_CONNECT_ERROR; + } + } + + if (ngx_add_conn) { + if (rc == -1) { + /* NGX_EINPROGRESS */ + return NGX_AGAIN; + } + + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, pc->log, 0, "connected"); + return NGX_OK; + } + + if (ngx_event_flags & NGX_USE_AIO_EVENT) { + + /* aio, iocp */ + + if (ngx_blocking(s) == -1) { + ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno, + ngx_blocking_n " failed"); + + 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; + } + + /* + * aio allows to post operation on non-connected socket + * at least in FreeBSD. + * NT does not support it. + * + * TODO: check in Win32, etc. As workaround we can use NGX_ONESHOT_EVENT + */ + + rev->ready = 1; + wev->ready = 1; + + return NGX_OK; + } + + if (ngx_event_flags & NGX_USE_CLEAR_EVENT) { /* kqueue */ + event = NGX_CLEAR_EVENT; + + } else { /* select, poll, /dev/poll */ + event = NGX_LEVEL_EVENT; + } + + if (ngx_add_event(rev, NGX_READ_EVENT, event) != NGX_OK) { + return NGX_ERROR; + } + + if (rc == -1) { + + /* NGX_EINPROGRESS */ + + if (ngx_add_event(wev, NGX_WRITE_EVENT, event) != NGX_OK) { + return NGX_ERROR; + } + + return NGX_AGAIN; + } + + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, pc->log, 0, "connected"); + + wev->ready = 1; + + return NGX_OK; +} + + +void ngx_event_connect_peer_failed(ngx_peer_connection_t *pc) +{ + time_t now; + + now = ngx_time(); + + /* ngx_lock_mutex(pc->peers->mutex); */ + + pc->peers->peers[pc->cur_peer].fails++; + pc->peers->peers[pc->cur_peer].accessed = now; + + /* ngx_unlock_mutex(pc->peers->mutex); */ + + pc->cur_peer++; + + if (pc->cur_peer >= pc->peers->number) { + pc->cur_peer = 0; + } + + pc->tries--; + + return; +} diff --git a/src/event/ngx_event_connect.h b/src/event/ngx_event_connect.h new file mode 100644 --- /dev/null +++ b/src/event/ngx_event_connect.h @@ -0,0 +1,67 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#ifndef _NGX_EVENT_CONNECT_H_INCLUDED_ +#define _NGX_EVENT_CONNECT_H_INCLUDED_ + + +#include +#include +#include + + +#define NGX_CONNECT_ERROR -10 + + +typedef struct { + in_addr_t addr; + ngx_str_t host; + in_port_t port; + ngx_str_t addr_port_text; + + ngx_int_t fails; + time_t accessed; +} ngx_peer_t; + + +typedef struct { + ngx_int_t current; + ngx_int_t number; + ngx_int_t max_fails; + ngx_int_t fail_timeout; + ngx_int_t last_cached; + + /* ngx_mutex_t *mutex; */ + ngx_connection_t **cached; + + ngx_peer_t peers[1]; +} ngx_peers_t; + + +typedef struct { + ngx_peers_t *peers; + ngx_int_t cur_peer; + ngx_int_t tries; + + ngx_connection_t *connection; +#if (NGX_THREADS) + ngx_atomic_t *lock; +#endif + + int rcvbuf; + + ngx_log_t *log; + + unsigned cached:1; + unsigned log_error:2; /* ngx_connection_log_error_e */ +} ngx_peer_connection_t; + + +int ngx_event_connect_peer(ngx_peer_connection_t *pc); +void ngx_event_connect_peer_failed(ngx_peer_connection_t *pc); + + +#endif /* _NGX_EVENT_CONNECT_H_INCLUDED_ */ diff --git a/src/event/ngx_event_mutex.c b/src/event/ngx_event_mutex.c new file mode 100644 --- /dev/null +++ b/src/event/ngx_event_mutex.c @@ -0,0 +1,70 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include + + +ngx_int_t ngx_event_mutex_timedlock(ngx_event_mutex_t *m, ngx_msec_t timer, + ngx_event_t *ev) +{ + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0, + "lock event mutex " PTR_FMT " lock:%X", m, m->lock); + + if (m->lock) { + + if (m->events == NULL) { + m->events = ev; + + } else { + m->last->next = ev; + } + + m->last = ev; + ev->next = NULL; + +#if (NGX_THREADS0) + ev->light = 1; +#endif + + ngx_add_timer(ev, timer); + + return NGX_AGAIN; + } + + m->lock = 1; + + return NGX_OK; +} + + +ngx_int_t ngx_event_mutex_unlock(ngx_event_mutex_t *m, ngx_log_t *log) +{ + ngx_event_t *ev; + + if (m->lock == 0) { + ngx_log_error(NGX_LOG_ALERT, log, 0, + "tring to unlock the free event mutex " PTR_FMT, m); + return NGX_ERROR; + } + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, log, 0, + "unlock event mutex " PTR_FMT ", next event: " PTR_FMT, + m, m->events); + + m->lock = 0; + + if (m->events) { + ev = m->events; + m->events = ev->next; + + ev->next = (ngx_event_t *) ngx_posted_events; + ngx_posted_events = ev; + } + + return NGX_OK; +} diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c new file mode 100644 --- /dev/null +++ b/src/event/ngx_event_openssl.c @@ -0,0 +1,378 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include + + +static ngx_int_t ngx_ssl_write(ngx_connection_t *c, u_char *data, size_t size); + + +ngx_int_t ngx_ssl_init(ngx_log_t *log) +{ + SSL_library_init(); + SSL_load_error_strings(); + + return NGX_OK; +} + + +ngx_int_t ngx_ssl_create_session(ngx_ssl_ctx_t *ssl_ctx, ngx_connection_t *c, + ngx_uint_t flags) +{ + ngx_ssl_t *ssl; + + if (!(ssl = ngx_pcalloc(c->pool, sizeof(ngx_ssl_t)))) { + return NGX_ERROR; + } + + if (!(ssl->buf = ngx_create_temp_buf(c->pool, NGX_SSL_BUFSIZE))) { + return NGX_ERROR; + } + + if (flags & NGX_SSL_BUFFER) { + ssl->buffer = 1; + } + + ssl->ssl = SSL_new(ssl_ctx); + + if (ssl->ssl == NULL) { + ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "SSL_new() failed"); + return NGX_ERROR; + } + + if (SSL_set_fd(ssl->ssl, c->fd) == 0) { + ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "SSL_set_fd() failed"); + return NGX_ERROR; + } + + SSL_set_accept_state(ssl->ssl); + + c->ssl = ssl; + + return NGX_OK; +} + + +ngx_int_t ngx_ssl_recv(ngx_connection_t *c, u_char *buf, size_t size) +{ + int n, sslerr; + ngx_err_t err; + char *handshake; + + n = SSL_read(c->ssl->ssl, buf, size); + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_read: %d", n); + + if (n > 0) { + return n; + } + + sslerr = SSL_get_error(c->ssl->ssl, n); + + err = (sslerr == SSL_ERROR_SYSCALL) ? ngx_errno : 0; + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_get_error: %d", sslerr); + + if (sslerr == SSL_ERROR_WANT_READ) { + return NGX_AGAIN; + } + + if (sslerr == SSL_ERROR_WANT_WRITE) { + ngx_log_error(NGX_LOG_ALERT, c->log, err, + "SSL wants to write%s", handshake); + return NGX_ERROR; +#if 0 + return NGX_AGAIN; +#endif + } + + if (!SSL_is_init_finished(c->ssl->ssl)) { + handshake = "in SSL handshake"; + + } else { + handshake = ""; + } + + c->ssl->no_rcv_shut = 1; + + if (sslerr == SSL_ERROR_ZERO_RETURN || ERR_peek_error() == 0) { + ngx_log_error(NGX_LOG_INFO, c->log, err, + "client closed connection%s", handshake); + + return NGX_ERROR; + } + + ngx_ssl_error(NGX_LOG_ALERT, c->log, err, + "SSL_read() failed%s", handshake); + + return NGX_ERROR; +} + + +/* + * OpenSSL has no SSL_writev() so we copy several bufs into our 16K buffer + * before SSL_write() call to decrease a SSL overhead. + * + * Besides for protocols such as HTTP it is possible to always buffer + * the output to decrease a SSL overhead some more. + */ + +ngx_chain_t *ngx_ssl_send_chain(ngx_connection_t *c, ngx_chain_t *in, + off_t limit) +{ + int n; + ngx_uint_t flush; + ssize_t send, size; + ngx_buf_t *buf; + + buf = c->ssl->buf; + + if (in && in->next == NULL && !c->buffered && !c->ssl->buffer) { + + /* + * we avoid a buffer copy if the incoming buf is a single, + * our buffer is empty, and we do not need to buffer the output + */ + + n = ngx_ssl_write(c, in->buf->pos, in->buf->last - in->buf->pos); + + if (n == NGX_ERROR) { + return NGX_CHAIN_ERROR; + } + + if (n < 0) { + n = 0; + } + + in->buf->pos += n; + + return in; + } + + send = 0; + flush = (in == NULL) ? 1 : 0; + + for ( ;; ) { + + while (in && buf->last < buf->end) { + if (in->buf->last_buf) { + flush = 1; + } + + if (ngx_buf_special(in->buf)) { + in = in->next; + continue; + } + + size = in->buf->last - in->buf->pos; + + if (size > buf->end - buf->last) { + size = buf->end - buf->last; + } + + /* + * TODO: the taking in->buf->flush into account can be + * implemented using the limit on the higher level + */ + + if (send + size > limit) { + size = limit - send; + flush = 1; + } + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, + "SSL buf copy: %d", size); + + ngx_memcpy(buf->last, in->buf->pos, size); + + buf->last += size; + + in->buf->pos += size; + if (in->buf->pos == in->buf->last) { + in = in->next; + } + } + + size = buf->last - buf->pos; + + if (!flush && buf->last < buf->end && c->ssl->buffer) { + break; + } + + n = ngx_ssl_write(c, buf->pos, size); + + if (n == NGX_ERROR) { + return NGX_CHAIN_ERROR; + } + + if (n < 0) { + n = 0; + } + + buf->pos += n; + send += n; + c->sent += n; + + if (n < size) { + break; + } + + if (buf->pos == buf->last) { + buf->pos = buf->start; + buf->last = buf->start; + } + + if (in == NULL || send == limit) { + break; + } + } + + c->buffered = (buf->pos < buf->last) ? 1 : 0; + + return in; +} + + +static ngx_int_t ngx_ssl_write(ngx_connection_t *c, u_char *data, size_t size) +{ + int n, sslerr; + ngx_err_t err; + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL to write: %d", size); + + n = SSL_write(c->ssl->ssl, data, size); + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_write: %d", n); + + if (n > 0) { + return n; + } + + sslerr = SSL_get_error(c->ssl->ssl, n); + + err = (sslerr == SSL_ERROR_SYSCALL) ? ngx_errno : 0; + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_get_error: %d", sslerr); + + if (sslerr == SSL_ERROR_WANT_WRITE) { + c->write->ready = 0; + return NGX_AGAIN; + } + + if (sslerr == SSL_ERROR_WANT_READ) { + ngx_log_error(NGX_LOG_ALERT, c->log, err, + "SSL wants to read%s", handshake); + return NGX_ERROR; +#if 0 + return NGX_AGAIN; + } +#endif + + c->ssl->no_rcv_shut = 1; + + ngx_ssl_error(NGX_LOG_ALERT, c->log, err, "SSL_write() failed"); + + return NGX_ERROR; +} + + +ngx_int_t ngx_ssl_shutdown(ngx_connection_t *c) +{ + int n, sslerr; + ngx_uint_t again; + + if (c->timedout) { + SSL_set_shutdown(c->ssl->ssl, SSL_RECEIVED_SHUTDOWN|SSL_SENT_SHUTDOWN); + + } else { + if (c->ssl->no_rcv_shut) { + SSL_set_shutdown(c->ssl->ssl, SSL_RECEIVED_SHUTDOWN); + } + + if (c->ssl->no_send_shut) { + SSL_set_shutdown(c->ssl->ssl, SSL_SENT_SHUTDOWN); + } + } + + again = 0; + + for ( ;; ) { + n = SSL_shutdown(c->ssl->ssl); + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_shutdown: %d", n); + + if (n == 0) { + again = 1; + break; + } + + if (n == 1) { + SSL_free(c->ssl->ssl); + c->ssl = NULL; + return NGX_OK; + } + + break; + } + + if (!again) { + sslerr = SSL_get_error(c->ssl->ssl, n); + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, + "SSL_get_error: %d", sslerr); + } + + if (again || sslerr == SSL_ERROR_WANT_READ) { + + ngx_add_timer(c->read, 10000); + + if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) { + return NGX_ERROR; + } + + return NGX_AGAIN; + } + + if (sslerr == SSL_ERROR_WANT_WRITE) { + + if (ngx_handle_write_event(c->write, 0) == NGX_ERROR) { + return NGX_ERROR; + } + + return NGX_AGAIN; + } + + ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "SSL_shutdown() failed"); + + return NGX_ERROR; +} + + +void ngx_ssl_error(ngx_uint_t level, ngx_log_t *log, ngx_err_t err, + char *fmt, ...) +{ + int len; + char errstr[NGX_MAX_CONF_ERRSTR]; + va_list args; + + va_start(args, fmt); + len = ngx_vsnprintf(errstr, sizeof(errstr) - 1, fmt, args); + va_end(args); + + errstr[len++] = ' '; + errstr[len++] = '('; + errstr[len++] = 'S'; + errstr[len++] = 'S'; + errstr[len++] = 'L'; + errstr[len++] = ':'; + errstr[len++] = ' '; + + ERR_error_string_n(ERR_get_error(), errstr + len, sizeof(errstr) - len - 1); + + ngx_log_error(level, log, err, "%s)", errstr); +} diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h new file mode 100644 --- /dev/null +++ b/src/event/ngx_event_openssl.h @@ -0,0 +1,57 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#ifndef _NGX_EVENT_OPENSSL_H_INCLUDED_ +#define _NGX_EVENT_OPENSSL_H_INCLUDED_ + + +#include +#include + +#include +#include + + +typedef struct { + SSL *ssl; + ngx_buf_t *buf; + ngx_event_handler_pt saved_handler; + + unsigned buffer:1; + unsigned no_rcv_shut:1; + unsigned no_send_shut:1; +} ngx_ssl_t; + + +typedef SSL_CTX ngx_ssl_ctx_t; + + +#define NGX_SSL_BUFFER 1 + + +#define NGX_SSL_BUFSIZE 16384 + + +ngx_int_t ngx_ssl_init(ngx_log_t *log); +ngx_int_t ngx_ssl_create_session(ngx_ssl_ctx_t *ctx, ngx_connection_t *c, + ngx_uint_t flags); + +#define ngx_ssl_handshake(c) NGX_OK + +ngx_int_t ngx_ssl_recv(ngx_connection_t *c, u_char *buf, size_t size); +ngx_chain_t *ngx_ssl_send_chain(ngx_connection_t *c, ngx_chain_t *in, + off_t limit); +ngx_int_t ngx_ssl_shutdown(ngx_connection_t *c); +void ngx_ssl_error(ngx_uint_t level, ngx_log_t *log, ngx_err_t err, + char *fmt, ...); + +#define ngx_ssl_set_nosendshut(ssl) \ + if (ssl) { \ + ssl->no_send_shut = 1; \ + } + + +#endif /* _NGX_EVENT_OPENSSL_H_INCLUDED_ */ diff --git a/src/event/ngx_event_pipe.c b/src/event/ngx_event_pipe.c new file mode 100644 --- /dev/null +++ b/src/event/ngx_event_pipe.c @@ -0,0 +1,771 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include +#include + + +static ngx_int_t ngx_event_pipe_read_upstream(ngx_event_pipe_t *p); +static ngx_int_t ngx_event_pipe_write_to_downstream(ngx_event_pipe_t *p); + +static ngx_int_t ngx_event_pipe_write_chain_to_temp_file(ngx_event_pipe_t *p); +ngx_inline static void ngx_event_pipe_remove_shadow_links(ngx_buf_t *buf); +ngx_inline static void ngx_event_pipe_free_shadow_raw_buf(ngx_chain_t **free, + ngx_buf_t *buf); +ngx_inline static void ngx_event_pipe_add_free_buf(ngx_chain_t **chain, + ngx_chain_t *cl); +static ngx_int_t ngx_event_pipe_drain_chains(ngx_event_pipe_t *p); + + +ngx_int_t ngx_event_pipe(ngx_event_pipe_t *p, int do_write) +{ + u_int flags; + ngx_event_t *rev, *wev; + + for ( ;; ) { + if (do_write) { + if (ngx_event_pipe_write_to_downstream(p) == NGX_ABORT) { + return NGX_ABORT; + } + } + + p->read = 0; + p->upstream_blocked = 0; + + if (ngx_event_pipe_read_upstream(p) == NGX_ABORT) { + return NGX_ABORT; + } + + if (!p->read && !p->upstream_blocked) { + break; + } + + do_write = 1; + } + + if (p->upstream->fd != -1) { + rev = p->upstream->read; + + flags = (rev->eof || rev->error) ? NGX_CLOSE_EVENT : 0; + + if (ngx_handle_read_event(rev, flags) == NGX_ERROR) { + return NGX_ABORT; + } + + if (rev->active) { + ngx_add_timer(rev, p->read_timeout); + } + } + + if (p->downstream->fd != -1) { + wev = p->downstream->write; + wev->available = p->send_lowat; + if (ngx_handle_write_event(wev, NGX_LOWAT_EVENT) == NGX_ERROR) { + return NGX_ABORT; + } + + if (wev->active) { + ngx_add_timer(wev, p->send_timeout); + } + } + + return NGX_OK; +} + + +ngx_int_t ngx_event_pipe_read_upstream(ngx_event_pipe_t *p) +{ + int n, rc, size; + ngx_buf_t *b; + ngx_chain_t *chain, *cl, *tl; + + if (p->upstream_eof || p->upstream_error || p->upstream_done) { + return NGX_OK; + } + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0, + "pipe read upstream: %d", p->upstream->read->ready); + + for ( ;; ) { + + if (p->upstream_eof || p->upstream_error || p->upstream_done) { + break; + } + + if (p->preread_bufs == NULL && !p->upstream->read->ready) { + break; + } + + if (p->preread_bufs) { + + /* use the pre-read bufs if they exist */ + + chain = p->preread_bufs; + p->preread_bufs = NULL; + n = p->preread_size; + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0, + "pipe preread: %d", n); + + if (n) { + p->read = 1; + } + + } else { + + /* + * kqueue notifies about the end of file or a pending error. + * This test allows not to allocate a buf on these conditions + * and not to call ngx_recv_chain(). + */ + + if (p->upstream->read->available == 0 + && p->upstream->read->pending_eof) + { + p->upstream->read->ready = 0; + p->upstream->read->eof = 0; + p->upstream_eof = 1; + p->read = 1; + +#if (HAVE_KQUEUE) + if (p->upstream->read->kq_errno) { + p->upstream->read->error = 1; + p->upstream_error = 1; + p->upstream_eof = 0; + + ngx_log_error(NGX_LOG_ERR, p->log, + p->upstream->read->kq_errno, + "readv() failed"); + } +#endif + + break; + } + + if (p->free_raw_bufs) { + + /* use the free bufs if they exist */ + + chain = p->free_raw_bufs; + if (p->single_buf) { + p->free_raw_bufs = p->free_raw_bufs->next; + chain->next = NULL; + } else { + p->free_raw_bufs = NULL; + } + + } else if (p->allocated < p->bufs.num) { + + /* allocate a new buf if it's still allowed */ + + if (!(b = ngx_create_temp_buf(p->pool, p->bufs.size))) { + return NGX_ABORT; + } + + p->allocated++; + + ngx_alloc_link_and_set_buf(tl, b, p->pool, NGX_ABORT); + chain = tl; + + } else if (!p->cachable && p->downstream->write->ready) { + + /* + * if the bufs are not needed to be saved in a cache and + * a downstream is ready then write the bufs to a downstream + */ + + p->upstream_blocked = 1; + + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, p->log, 0, + "pipe downstream ready"); + + break; + + } else if (p->cachable + || p->temp_file->offset < p->max_temp_file_size) + { + + /* + * if it's allowed then save some bufs from r->in + * to a temporary file, and add them to a r->out chain + */ + + rc = ngx_event_pipe_write_chain_to_temp_file(p); + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0, + "pipe temp offset: %d", p->temp_file->offset); + + if (rc == NGX_AGAIN) { + if (ngx_event_flags & NGX_USE_LEVEL_EVENT + && p->upstream->read->active + && p->upstream->read->ready) + { + if (ngx_del_event(p->upstream->read, NGX_READ_EVENT, 0) + == NGX_ERROR) + { + return NGX_ABORT; + } + } + } + + if (rc != NGX_OK) { + return rc; + } + + chain = p->free_raw_bufs; + if (p->single_buf) { + p->free_raw_bufs = p->free_raw_bufs->next; + chain->next = NULL; + } else { + p->free_raw_bufs = NULL; + } + + } else { + + /* if there're no bufs to read in then disable a level event */ + + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, p->log, 0, + "no pipe bufs to read in"); + + break; + } + + n = ngx_recv_chain(p->upstream, chain); + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0, + "pipe recv chain: %d", n); + + if (p->free_raw_bufs) { + chain->next = p->free_raw_bufs; + } + p->free_raw_bufs = chain; + + if (n == NGX_ERROR) { + p->upstream_error = 1; + return NGX_ERROR; + } + + if (n == NGX_AGAIN) { + if (p->single_buf) { + ngx_event_pipe_remove_shadow_links(chain->buf); + } + + break; + } + + p->read = 1; + + if (n == 0) { + p->upstream_eof = 1; + break; + } + } + + p->read_length += n; + cl = chain; + + while (cl && n > 0) { + + ngx_event_pipe_remove_shadow_links(cl->buf); + + size = cl->buf->end - cl->buf->last; + + if (n >= size) { + cl->buf->last = cl->buf->end; + + /* STUB */ cl->buf->num = p->num++; + + if (p->input_filter(p, cl->buf) == NGX_ERROR) { + return NGX_ABORT; + } + + n -= size; + cl = cl->next; + + } else { + cl->buf->last += n; + n = 0; + } + } + + p->free_raw_bufs = cl; + } + +#if (NGX_DEBUG) + + if (p->in || p->busy || p->free_raw_bufs) { + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, p->log, 0, "pipe buf"); + } + + for (cl = p->in; cl; cl = cl->next) { + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, p->log, 0, + "pipe buf in " PTR_FMT ", pos " PTR_FMT ", size: %d", + cl->buf->start, cl->buf->pos, + cl->buf->last - cl->buf->pos); + } + + for (cl = p->busy; cl; cl = cl->next) { + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, p->log, 0, + "pipe buf busy " PTR_FMT ", pos " PTR_FMT ", size: %d", + cl->buf->start, cl->buf->pos, + cl->buf->last - cl->buf->pos); + } + + for (cl = p->free_raw_bufs; cl; cl = cl->next) { + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, p->log, 0, + "pipe buf free " PTR_FMT ", last " PTR_FMT ", size: %d", + cl->buf->start, cl->buf->last, + cl->buf->end - cl->buf->last); + } + +#endif + + if ((p->upstream_eof || p->upstream_error) && p->free_raw_bufs) { + + /* STUB */ p->free_raw_bufs->buf->num = p->num++; + + if (p->input_filter(p, p->free_raw_bufs->buf) == NGX_ERROR) { + return NGX_ABORT; + } + + p->free_raw_bufs = p->free_raw_bufs->next; + + if (p->free_bufs) { + for (cl = p->free_raw_bufs; cl; cl = cl->next) { + ngx_pfree(p->pool, cl->buf->start); + } + } + } + + if (p->cachable && p->in) { + if (ngx_event_pipe_write_chain_to_temp_file(p) == NGX_ABORT) { + return NGX_ABORT; + } + } + + return NGX_OK; +} + + +ngx_int_t ngx_event_pipe_write_to_downstream(ngx_event_pipe_t *p) +{ + size_t bsize; + ngx_uint_t flush; + ngx_buf_t *b; + ngx_chain_t *out, **ll, *cl, *tl; + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0, + "pipe write downstream: %d", p->downstream->write->ready); + + for ( ;; ) { + if (p->downstream_error) { + return ngx_event_pipe_drain_chains(p); + } + + if (p->upstream_eof || p->upstream_error || p->upstream_done) { + + /* pass the p->out and p->in chains to the output filter */ + + if (p->out) { + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, p->log, 0, + "pipe write downstream flush out"); + + if (p->output_filter(p->output_ctx, p->out) == NGX_ERROR) { + p->downstream_error = 1; + return ngx_event_pipe_drain_chains(p); + } + + p->out = NULL; + } + + if (p->in) { + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, p->log, 0, + "pipe write downstream flush in"); + + if (p->output_filter(p->output_ctx, p->in) == NGX_ERROR) { + p->downstream_error = 1; + return ngx_event_pipe_drain_chains(p); + } + + p->in = NULL; + } + + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, p->log, 0, + "pipe write downstream done"); + + /* TODO: free unused bufs */ + + p->downstream_done = 1; + break; + } + + if (!p->downstream->write->ready) { + break; + } + + /* bsize is the size of the busy bufs */ + + bsize = 0; + + for (cl = p->busy; cl; cl = cl->next) { + bsize += cl->buf->end - cl->buf->start; + } + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0, + "pipe write busy: " SIZE_T_FMT, bsize); + + out = NULL; + ll = NULL; + flush = 0; + + for ( ;; ) { + if (p->out) { + cl = p->out; + + if (bsize + ngx_buf_size(cl->buf) > p->busy_size) { + flush = 1; + break; + } + + p->out = p->out->next; + ngx_event_pipe_free_shadow_raw_buf(&p->free_raw_bufs, + cl->buf); + + } else if (!p->cachable && p->in) { + cl = p->in; + + if (bsize + ngx_buf_size(cl->buf) > p->busy_size) { + flush = 1; + break; + } + + p->in = p->in->next; + + } else { + break; + } + + bsize += ngx_buf_size(cl->buf); + cl->next = NULL; + ngx_chain_add_link(out, ll, cl); + } + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, p->log, 0, + "pipe write: out:" PTR_FMT ", f:%d", out, flush); + + if (out == NULL && !flush) { + break; + } + + if (p->output_filter(p->output_ctx, out) == NGX_ERROR) { + p->downstream_error = 1; + return ngx_event_pipe_drain_chains(p); + } + + ngx_chain_update_chains(&p->free, &p->busy, &out, p->tag); + + for (cl = p->free; cl; cl = cl->next) { + + if (cl->buf->temp_file) { + if (p->cachable || !p->cyclic_temp_file) { + continue; + } + + /* reset p->temp_offset if all bufs had been sent */ + + if (cl->buf->file_last == p->temp_file->offset) { + p->temp_file->offset = 0; + } + } + + /* TODO: free buf if p->free_bufs && upstream done */ + + /* add the free shadow raw buf to p->free_raw_bufs */ + + if (cl->buf->last_shadow) { + b = cl->buf->shadow; + b->pos = b->last = b->start; + b->shadow = NULL; + ngx_alloc_link_and_set_buf(tl, b, p->pool, NGX_ABORT); + ngx_event_pipe_add_free_buf(&p->free_raw_bufs, tl); + + cl->buf->last_shadow = 0; + } + + cl->buf->shadow = NULL; + } + } + + return NGX_OK; +} + + +static ngx_int_t ngx_event_pipe_write_chain_to_temp_file(ngx_event_pipe_t *p) +{ + ssize_t size, bsize; + ngx_buf_t *b; + ngx_chain_t *cl, *tl, *next, *out, **ll, **last_free, fl; + + if (p->buf_to_file) { + fl.buf = p->buf_to_file; + fl.next = p->in; + out = &fl; + + } else { + out = p->in; + } + + if (!p->cachable) { + + size = 0; + cl = out; + ll = NULL; + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0, + "pipe offset: %d", p->temp_file->offset); + + do { + bsize = cl->buf->last - cl->buf->pos; + + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, p->log, 0, + "pipe buf " PTR_FMT ", pos " PTR_FMT ", size: %d", + cl->buf->start, cl->buf->pos, bsize); + + if ((size + bsize > p->temp_file_write_size) + || (p->temp_file->offset + size + bsize > p->max_temp_file_size)) + { + break; + } + + size += bsize; + ll = &cl->next; + cl = cl->next; + + } while (cl); + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0, "size: %d", size); + + if (cl) { + p->in = cl; + *ll = NULL; + + } else { + p->in = NULL; + p->last_in = &p->in; + } + + } else { + p->in = NULL; + p->last_in = &p->in; + } + + if (ngx_write_chain_to_temp_file(p->temp_file, out) == NGX_ERROR) { + return NGX_ABORT; + } + + for (last_free = &p->free_raw_bufs; + *last_free != NULL; + last_free = &(*last_free)->next) + { + /* void */ + } + + if (p->buf_to_file) { + p->temp_file->offset = p->buf_to_file->last - p->buf_to_file->pos; + p->buf_to_file = NULL; + out = out->next; + } + + for (cl = out; cl; cl = next) { + next = cl->next; + cl->next = NULL; + + b = cl->buf; + b->file = &p->temp_file->file; + b->file_pos = p->temp_file->offset; + p->temp_file->offset += b->last - b->pos; + b->file_last = p->temp_file->offset; + + b->in_file = 1; + b->temp_file = 1; + + ngx_chain_add_link(p->out, p->last_out, cl); + + if (b->last_shadow) { + b->shadow->last = b->shadow->pos = b->shadow->start; + ngx_alloc_link_and_set_buf(tl, b->shadow, p->pool, NGX_ABORT); + *last_free = tl; + last_free = &tl->next; + } + } + + return NGX_OK; +} + + +/* the copy input filter */ + +ngx_int_t ngx_event_pipe_copy_input_filter(ngx_event_pipe_t *p, ngx_buf_t *buf) +{ + ngx_buf_t *b; + ngx_chain_t *cl; + + if (buf->pos == buf->last) { + return NGX_OK; + } + + if (p->free) { + b = p->free->buf; + p->free = p->free->next; + + } else { + if (!(b = ngx_alloc_buf(p->pool))) { + return NGX_ERROR; + } + } + + ngx_memcpy(b, buf, sizeof(ngx_buf_t)); + b->shadow = buf; + b->tag = p->tag; + b->last_shadow = 1; + b->recycled = 1; + buf->shadow = b; + + ngx_alloc_link_and_set_buf(cl, b, p->pool, NGX_ERROR); + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0, "buf #%d", b->num); + + ngx_chain_add_link(p->in, p->last_in, cl); + + return NGX_OK; +} + + +ngx_inline static void ngx_event_pipe_remove_shadow_links(ngx_buf_t *buf) +{ + ngx_buf_t *b, *next; + + if (buf->shadow == NULL) { + return; + } + + b = buf->shadow; + + while (!b->last_shadow) { + next = b->shadow; + + b->in_file = 0; + b->temp_file = 0; + b->flush = 0; + b->zerocopy_busy = 0; + + b->shadow = NULL; + b = next; + } + + b->in_file = 0; + b->temp_file = 0; + b->flush = 0; + b->zerocopy_busy = 0; + b->last_shadow = 0; + + b->shadow = NULL; + + buf->shadow = NULL; +} + + +ngx_inline static void ngx_event_pipe_free_shadow_raw_buf(ngx_chain_t **free, + ngx_buf_t *buf) +{ + ngx_buf_t *s; + ngx_chain_t *cl, **ll; + + if (buf->shadow == NULL) { + return; + } + + for (s = buf->shadow; !s->last_shadow; s = s->shadow) { /* void */ } + + ll = free; + + for (cl = *free ; cl; cl = cl->next) { + if (cl->buf == s) { + *ll = cl->next; + break; + } + + if (cl->buf->shadow) { + break; + } + + ll = &cl->next; + } +} + + +ngx_inline static void ngx_event_pipe_add_free_buf(ngx_chain_t **chain, + ngx_chain_t *cl) +{ + if (*chain == NULL) { + *chain = cl; + return; + } + + if ((*chain)->buf->pos != (*chain)->buf->last) { + cl->next = (*chain)->next; + (*chain)->next = cl; + + } else { + cl->next = (*chain); + (*chain) = cl; + } +} + + +static ngx_int_t ngx_event_pipe_drain_chains(ngx_event_pipe_t *p) +{ + ngx_buf_t *b; + ngx_chain_t *cl, *tl; + + for ( ;; ) { + if (p->busy) { + cl = p->busy; + p->busy = NULL; + + } else if (p->out) { + cl = p->out; + p->out = NULL; + + } else if (p->in) { + cl = p->in; + p->in = NULL; + + } else { + return NGX_OK; + } + + while (cl) { + if (cl->buf->last_shadow) { + b = cl->buf->shadow; + b->pos = b->last = b->start; + b->shadow = NULL; + ngx_alloc_link_and_set_buf(tl, b, p->pool, NGX_ABORT); + ngx_event_pipe_add_free_buf(&p->free_raw_bufs, tl); + + cl->buf->last_shadow = 0; + } + + cl->buf->shadow = NULL; + tl = cl->next; + cl->next = p->free; + p->free = cl; + cl = tl; + } + } +} diff --git a/src/event/ngx_event_pipe.h b/src/event/ngx_event_pipe.h new file mode 100644 --- /dev/null +++ b/src/event/ngx_event_pipe.h @@ -0,0 +1,93 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#ifndef _NGX_EVENT_PIPE_H_INCLUDED_ +#define _NGX_EVENT_PIPE_H_INCLUDED_ + + +#include +#include +#include + + +typedef struct ngx_event_pipe_s ngx_event_pipe_t; + +typedef ngx_int_t (*ngx_event_pipe_input_filter_pt)(ngx_event_pipe_t *p, + ngx_buf_t *buf); +typedef ngx_int_t (*ngx_event_pipe_output_filter_pt)(void *data, + ngx_chain_t *chain); + + +struct ngx_event_pipe_s { + ngx_connection_t *upstream; + ngx_connection_t *downstream; + + ngx_chain_t *free_raw_bufs; + ngx_chain_t *in; + ngx_chain_t **last_in; + + ngx_chain_t *out; + ngx_chain_t **last_out; + + ngx_chain_t *free; + ngx_chain_t *busy; + + /* + * the input filter i.e. that moves HTTP/1.1 chunks + * from the raw bufs to an incoming chain + */ + + ngx_event_pipe_input_filter_pt input_filter; + void *input_ctx; + + ngx_event_pipe_output_filter_pt output_filter; + void *output_ctx; + + unsigned read:1; + unsigned cachable:1; + unsigned single_buf:1; + unsigned free_bufs:1; + unsigned upstream_done:1; + unsigned upstream_error:1; + unsigned upstream_eof:1; + unsigned upstream_blocked:1; + unsigned downstream_done:1; + unsigned downstream_error:1; + unsigned cyclic_temp_file:1; + + ngx_int_t allocated; + ngx_bufs_t bufs; + ngx_buf_tag_t tag; + + size_t busy_size; + + off_t read_length; + + off_t max_temp_file_size; + ssize_t temp_file_write_size; + + ngx_msec_t read_timeout; + ngx_msec_t send_timeout; + ssize_t send_lowat; + + ngx_pool_t *pool; + ngx_log_t *log; + + ngx_chain_t *preread_bufs; + size_t preread_size; + ngx_buf_t *buf_to_file; + + ngx_temp_file_t *temp_file; + + /* STUB */ int num; +}; + + +ngx_int_t ngx_event_pipe(ngx_event_pipe_t *p, int do_write); +ngx_int_t ngx_event_pipe_copy_input_filter(ngx_event_pipe_t *p, ngx_buf_t *buf); + + +#endif /* _NGX_EVENT_PIPE_H_INCLUDED_ */ diff --git a/src/event/ngx_event_posted.c b/src/event/ngx_event_posted.c new file mode 100644 --- /dev/null +++ b/src/event/ngx_event_posted.c @@ -0,0 +1,169 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include + + +ngx_thread_volatile ngx_event_t *ngx_posted_events; + +#if (NGX_THREADS) +ngx_mutex_t *ngx_posted_events_mutex; +#endif + + +void ngx_event_process_posted(ngx_cycle_t *cycle) +{ + ngx_event_t *ev; + + for ( ;; ) { + + ev = (ngx_event_t *) ngx_posted_events; + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, + "posted event " PTR_FMT, ev); + + if (ev == NULL) { + return; + } + + ngx_delete_posted_event(ev); + + ev->event_handler(ev); + } +} + + +#if (NGX_THREADS) + +void ngx_wakeup_worker_thread(ngx_cycle_t *cycle) +{ + ngx_int_t i; + ngx_uint_t busy; + ngx_event_t *ev; + +#if 0 + busy = 1; + + if (ngx_mutex_lock(ngx_posted_events_mutex) == NGX_ERROR) { + return; + } + + for (ev = (ngx_event_t *) ngx_posted_events; ev; ev = ev->next) { + if (*(ev->lock) == 0) { + busy = 0; + break; + } + } + + ngx_mutex_unlock(ngx_posted_events_mutex); + + if (busy) { + return; + } +#endif + + for (i = 0; i < ngx_threads_n; i++) { + if (ngx_threads[i].state == NGX_THREAD_FREE) { + ngx_cond_signal(ngx_threads[i].cv); + return; + } + } +} + + +ngx_int_t ngx_event_thread_process_posted(ngx_cycle_t *cycle) +{ + ngx_event_t *ev; + + for ( ;; ) { + + ev = (ngx_event_t *) ngx_posted_events; + + for ( ;; ) { + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, + "posted event " PTR_FMT, ev); + + if (ev == NULL) { + return NGX_OK; + } + + if (ngx_trylock(ev->lock) == 0) { + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, + "posted event " PTR_FMT " is busy", ev); + + ev = ev->next; + continue; + } + + if (ev->lock != ev->own_lock) { + if (*(ev->own_lock)) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, + "the own lock of the posted event " + PTR_FMT " is busy", ev); + ngx_unlock(ev->lock); + ev = ev->next; + continue; + } + *(ev->own_lock) = 1; + } + + ngx_delete_posted_event(ev); + + ev->locked = 1; + + ev->ready |= ev->posted_ready; + ev->timedout |= ev->posted_timedout; + ev->pending_eof |= ev->posted_eof; +#if (HAVE_KQUEUE) + ev->kq_errno |= ev->posted_errno; +#endif + if (ev->posted_available) { + ev->available = ev->posted_available; + } + + ev->posted_ready = 0; + ev->posted_timedout = 0; + ev->posted_eof = 0; +#if (HAVE_KQUEUE) + ev->posted_errno = 0; +#endif + ev->posted_available = 0; + + ngx_mutex_unlock(ngx_posted_events_mutex); + + ev->event_handler(ev); + + if (ngx_mutex_lock(ngx_posted_events_mutex) == NGX_ERROR) { + return NGX_ERROR; + } + + if (ev->locked) { + ngx_unlock(ev->lock); + + if (ev->lock != ev->own_lock) { + ngx_unlock(ev->own_lock); + } + } + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, + "posted event " PTR_FMT " is done", ev); + + break; + } + } +} + +#else + +void ngx_wakeup_worker_thread(ngx_cycle_t *cycle) +{ +} + +#endif diff --git a/src/event/ngx_event_posted.h b/src/event/ngx_event_posted.h new file mode 100644 --- /dev/null +++ b/src/event/ngx_event_posted.h @@ -0,0 +1,55 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#ifndef _NGX_EVENT_POSTED_H_INCLUDED_ +#define _NGX_EVENT_POSTED_H_INCLUDED_ + + +#include +#include +#include + + +#define ngx_post_event(ev) \ + if (ev->prev == NULL) { \ + ev->next = (ngx_event_t *) ngx_posted_events; \ + ev->prev = (ngx_event_t **) &ngx_posted_events; \ + ngx_posted_events = ev; \ + if (ev->next) { \ + ev->next->prev = &ev->next; \ + } \ + ngx_log_debug1(NGX_LOG_DEBUG_CORE, ev->log, 0, \ + "post event " PTR_FMT, ev); \ + } else { \ + ngx_log_debug1(NGX_LOG_DEBUG_CORE, ev->log, 0, \ + "update posted event " PTR_FMT, ev); \ + } + +#define ngx_delete_posted_event(ev) \ + *(ev->prev) = ev->next; \ + if (ev->next) { \ + ev->next->prev = ev->prev; \ + } \ + ev->prev = NULL; \ + ngx_log_debug1(NGX_LOG_DEBUG_CORE, ev->log, 0, \ + "delete posted event " PTR_FMT, ev); + + + +void ngx_event_process_posted(ngx_cycle_t *cycle); +void ngx_wakeup_worker_thread(ngx_cycle_t *cycle); + +extern ngx_thread_volatile ngx_event_t *ngx_posted_events; + + +#if (NGX_THREADS) +ngx_int_t ngx_event_thread_process_posted(ngx_cycle_t *cycle); + +extern ngx_mutex_t *ngx_posted_events_mutex; +#endif + + +#endif /* _NGX_EVENT_POSTED_H_INCLUDED_ */ diff --git a/src/event/ngx_event_timer.c b/src/event/ngx_event_timer.c new file mode 100644 --- /dev/null +++ b/src/event/ngx_event_timer.c @@ -0,0 +1,165 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include + + +#if (NGX_THREADS) +ngx_mutex_t *ngx_event_timer_mutex; +#endif + + +ngx_thread_volatile ngx_rbtree_t *ngx_event_timer_rbtree; +ngx_rbtree_t ngx_event_timer_sentinel; + + +ngx_int_t ngx_event_timer_init(ngx_log_t *log) +{ + if (ngx_event_timer_rbtree) { +#if (NGX_THREADS) + ngx_event_timer_mutex->log = log; +#endif + return NGX_OK; + } + + ngx_event_timer_rbtree = &ngx_event_timer_sentinel; + +#if (NGX_THREADS) + if (!(ngx_event_timer_mutex = ngx_mutex_init(log, 0))) { + return NGX_ERROR; + } +#endif + + return NGX_OK; +} + + +ngx_msec_t ngx_event_find_timer(void) +{ + ngx_msec_t timer; + ngx_rbtree_t *node; + + if (ngx_event_timer_rbtree == &ngx_event_timer_sentinel) { + return NGX_TIMER_INFINITE; + } + + if (ngx_mutex_lock(ngx_event_timer_mutex) == NGX_ERROR) { + return NGX_TIMER_ERROR; + } + + node = ngx_rbtree_min((ngx_rbtree_t *) ngx_event_timer_rbtree, + &ngx_event_timer_sentinel); + + ngx_mutex_unlock(ngx_event_timer_mutex); + + timer = (ngx_msec_t) + (node->key * NGX_TIMER_RESOLUTION - + ngx_elapsed_msec / NGX_TIMER_RESOLUTION * NGX_TIMER_RESOLUTION); +#if 0 + (node->key * NGX_TIMER_RESOLUTION - ngx_elapsed_msec); +#endif + + return timer > 0 ? timer: 0 ; +} + + +void ngx_event_expire_timers(ngx_msec_t timer) +{ + ngx_event_t *ev; + ngx_rbtree_t *node; + + if (timer < 0) { + /* avoid the endless loop if the time goes backward for some reason */ + timer = 0; + } + + for ( ;; ) { + + if (ngx_event_timer_rbtree == &ngx_event_timer_sentinel) { + return; + } + + if (ngx_mutex_lock(ngx_event_timer_mutex) == NGX_ERROR) { + return; + } + + node = ngx_rbtree_min((ngx_rbtree_t *) ngx_event_timer_rbtree, + &ngx_event_timer_sentinel); + + if (node->key <= (ngx_msec_t) + (ngx_old_elapsed_msec + timer) / NGX_TIMER_RESOLUTION) + { + ev = (ngx_event_t *) + ((char *) node - offsetof(ngx_event_t, rbtree_key)); + +#if (NGX_THREADS) + + if (ngx_threaded && ngx_trylock(ev->lock) == 0) { + + /* + * We can not change the timer of the event that is been + * handling by another thread. And we can not easy walk + * the rbtree to find a next expired timer so we exit the loop. + * However it should be rare case when the event that is + * been handling has expired timer. + */ + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ev->log, 0, + "event " PTR_FMT " is busy in expire timers", + ev); + break; + } +#endif + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0, + "event timer del: %d: %d", + ngx_event_ident(ev->data), ev->rbtree_key); + + ngx_rbtree_delete((ngx_rbtree_t **) &ngx_event_timer_rbtree, + &ngx_event_timer_sentinel, + (ngx_rbtree_t *) &ev->rbtree_key); + + ngx_mutex_unlock(ngx_event_timer_mutex); + +#if (NGX_DEBUG) + ev->rbtree_left = NULL; + ev->rbtree_right = NULL; + ev->rbtree_parent = NULL; +#endif + + ev->timer_set = 0; + +#if (NGX_THREADS) + if (ngx_threaded) { + if (ngx_mutex_lock(ngx_posted_events_mutex) == NGX_ERROR) { + return; + } + + ev->posted_timedout = 1; + ngx_post_event(ev); + + ngx_mutex_unlock(ngx_posted_events_mutex); + + ngx_unlock(ev->lock); + + continue; + } +#endif + + ev->timedout = 1; + + ev->event_handler(ev); + + continue; + } + + break; + } + + ngx_mutex_unlock(ngx_event_timer_mutex); +} diff --git a/src/event/ngx_event_timer.h b/src/event/ngx_event_timer.h new file mode 100644 --- /dev/null +++ b/src/event/ngx_event_timer.h @@ -0,0 +1,120 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#ifndef _NGX_EVENT_TIMER_H_INCLUDED_ +#define _NGX_EVENT_TIMER_H_INCLUDED_ + + +#include +#include +#include + + +#define NGX_TIMER_INFINITE -1 +#define NGX_TIMER_ERROR -2 + +/* + * 32 bit timer key value resolution + * + * 1 msec - 24 days + * 10 msec - 8 months + * 50 msec - 3 years 5 months + * 100 msec - 6 years 10 months + */ + +#define NGX_TIMER_RESOLUTION 1 + + +ngx_int_t ngx_event_timer_init(ngx_log_t *log); +ngx_msec_t ngx_event_find_timer(void); +void ngx_event_expire_timers(ngx_msec_t timer); + + +#if (NGX_THREADS) +extern ngx_mutex_t *ngx_event_timer_mutex; +#endif + + +extern ngx_thread_volatile ngx_rbtree_t *ngx_event_timer_rbtree; +extern ngx_rbtree_t ngx_event_timer_sentinel; + + +ngx_inline static void ngx_event_del_timer(ngx_event_t *ev) +{ + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0, + "event timer del: %d: %d", + ngx_event_ident(ev->data), ev->rbtree_key); + + if (ngx_mutex_lock(ngx_event_timer_mutex) == NGX_ERROR) { + return; + } + + ngx_rbtree_delete((ngx_rbtree_t **) &ngx_event_timer_rbtree, + &ngx_event_timer_sentinel, + (ngx_rbtree_t *) &ev->rbtree_key); + + ngx_mutex_unlock(ngx_event_timer_mutex); + +#if (NGX_DEBUG) + ev->rbtree_left = NULL; + ev->rbtree_right = NULL; + ev->rbtree_parent = NULL; +#endif + + ev->timer_set = 0; +} + + +ngx_inline static void ngx_event_add_timer(ngx_event_t *ev, ngx_msec_t timer) +{ + ngx_int_t key; + + key = (ngx_int_t) + (ngx_elapsed_msec / NGX_TIMER_RESOLUTION * NGX_TIMER_RESOLUTION + + timer) / NGX_TIMER_RESOLUTION; +#if 0 + (ngx_elapsed_msec + timer) / NGX_TIMER_RESOLUTION; +#endif + + if (ev->timer_set) { + + /* + * Use the previous timer value if a difference between them is less + * then 100 milliseconds. It allows to minimize the rbtree operations + * for the fast connections. + */ + + if (abs(key - ev->rbtree_key) < 100 / NGX_TIMER_RESOLUTION) { + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ev->log, 0, + "event timer: %d, old: %d, new: %d", + ngx_event_ident(ev->data), ev->rbtree_key, key); + return; + } + + ngx_del_timer(ev); + } + + ev->rbtree_key = key; + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0, + "event timer add: %d: %d", + ngx_event_ident(ev->data), ev->rbtree_key); + + if (ngx_mutex_lock(ngx_event_timer_mutex) == NGX_ERROR) { + return; + } + + ngx_rbtree_insert((ngx_rbtree_t **) &ngx_event_timer_rbtree, + &ngx_event_timer_sentinel, + (ngx_rbtree_t *) &ev->rbtree_key); + + ngx_mutex_unlock(ngx_event_timer_mutex); + + ev->timer_set = 1; +} + + +#endif /* _NGX_EVENT_TIMER_H_INCLUDED_ */ diff --git a/src/http/modules/ngx_http_access_handler.c b/src/http/modules/ngx_http_access_handler.c new file mode 100644 --- /dev/null +++ b/src/http/modules/ngx_http_access_handler.c @@ -0,0 +1,213 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include + + +/* AF_INET only */ + +typedef struct { + in_addr_t mask; + in_addr_t addr; + unsigned deny; +} ngx_http_access_rule_t; + + +typedef struct { + ngx_array_t *rules; /* array of ngx_http_access_rule_t */ +} ngx_http_access_loc_conf_t; + + +static ngx_int_t ngx_http_access_handler(ngx_http_request_t *r); +static char *ngx_http_access_rule(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static void *ngx_http_access_create_loc_conf(ngx_conf_t *cf); +static char *ngx_http_access_merge_loc_conf(ngx_conf_t *cf, + void *parent, void *child); +static ngx_int_t ngx_http_access_init(ngx_cycle_t *cycle); + + +static ngx_command_t ngx_http_access_commands[] = { + + { ngx_string("allow"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_http_access_rule, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("deny"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_http_access_rule, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + NULL }, + + ngx_null_command +}; + + + +ngx_http_module_t ngx_http_access_module_ctx = { + NULL, /* pre conf */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + NULL, /* create server configuration */ + NULL, /* merge server configuration */ + + ngx_http_access_create_loc_conf, /* create location configuration */ + ngx_http_access_merge_loc_conf /* merge location configuration */ +}; + + +ngx_module_t ngx_http_access_module = { + NGX_MODULE, + &ngx_http_access_module_ctx, /* module context */ + ngx_http_access_commands, /* module directives */ + NGX_HTTP_MODULE, /* module type */ + ngx_http_access_init, /* init module */ + NULL /* init process */ +}; + + +static ngx_int_t ngx_http_access_handler(ngx_http_request_t *r) +{ + ngx_uint_t i; + struct sockaddr_in *addr_in; + ngx_http_access_rule_t *rule; + 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; + } + + /* AF_INET only */ + + addr_in = (struct sockaddr_in *) r->connection->sockaddr; + + rule = alcf->rules->elts; + for (i = 0; i < alcf->rules->nelts; i++) { + +ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "%08X %08X %08X", + addr_in->sin_addr.s_addr, rule[i].mask, rule[i].addr); + + if ((addr_in->sin_addr.s_addr & rule[i].mask) == rule[i].addr) { + if (rule[i].deny) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "access forbidden by rule"); + + return NGX_HTTP_FORBIDDEN; + } + + return NGX_OK; + } + } + + return NGX_OK; +} + + +static char *ngx_http_access_rule(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf) +{ + ngx_http_access_loc_conf_t *alcf = conf; + + ngx_str_t *value; + ngx_inet_cidr_t in_cidr; + ngx_http_access_rule_t *rule; + + if (alcf->rules == NULL) { + alcf->rules = ngx_create_array(cf->pool, 5, + sizeof(ngx_http_access_rule_t)); + if (alcf->rules == NULL) { + return NGX_CONF_ERROR; + } + } + + if (!(rule = ngx_push_array(alcf->rules))) { + return NGX_CONF_ERROR; + } + + value = cf->args->elts; + + rule->deny = (value[0].data[0] == 'd') ? 1 : 0; + + if (value[1].len == 3 && ngx_strcmp(value[1].data, "all") == 0) { + rule->mask = 0; + rule->addr = 0; + + return NGX_CONF_OK; + } + + rule->addr = inet_addr((char *) value[1].data); + + if (rule->addr != INADDR_NONE) { + rule->mask = 0xffffffff; + + return NGX_CONF_OK; + } + + if (ngx_ptocidr(&value[1], &in_cidr) == NGX_ERROR) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid paramter \"%s\"", + value[1].data); + return NGX_CONF_ERROR; + } + + rule->mask = in_cidr.mask; + rule->addr = in_cidr.addr; + + return NGX_CONF_OK; +} + + +static void *ngx_http_access_create_loc_conf(ngx_conf_t *cf) +{ + ngx_http_access_loc_conf_t *conf; + + if (!(conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_access_loc_conf_t)))) { + return NGX_CONF_ERROR; + } + + return conf; +} + + +static char *ngx_http_access_merge_loc_conf(ngx_conf_t *cf, + void *parent, void *child) +{ + ngx_http_access_loc_conf_t *prev = parent; + ngx_http_access_loc_conf_t *conf = child; + + if (conf->rules == NULL) { + conf->rules = prev->rules; + } + + return NGX_CONF_OK; +} + + +static ngx_int_t ngx_http_access_init(ngx_cycle_t *cycle) +{ + ngx_http_handler_pt *h; + ngx_http_core_main_conf_t *cmcf; + + cmcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_core_module); + + h = ngx_push_array(&cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers); + if (h == NULL) { + return NGX_ERROR; + } + + *h = ngx_http_access_handler; + + return NGX_OK; +} diff --git a/src/http/modules/ngx_http_charset_filter.c b/src/http/modules/ngx_http_charset_filter.c new file mode 100644 --- /dev/null +++ b/src/http/modules/ngx_http_charset_filter.c @@ -0,0 +1,553 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include + + +typedef struct { + char **tables; + ngx_str_t name; + unsigned server; +} ngx_http_charset_t; + + +typedef struct { + ngx_int_t src; + ngx_int_t dst; + char *src2dst; + char *dst2src; +} ngx_http_charset_tables_t; + + +typedef struct { + ngx_array_t charsets; /* ngx_http_charset_t */ + ngx_array_t tables; /* ngx_http_charset_tables_t */ +} ngx_http_charset_main_conf_t; + + +typedef struct { + ngx_flag_t enable; + ngx_flag_t autodetect; + + ngx_int_t default_charset; + ngx_int_t source_charset; +} ngx_http_charset_loc_conf_t; + + +typedef struct { + ngx_int_t server; + ngx_int_t client; +} ngx_http_charset_ctx_t; + + +static void ngx_charset_recode(ngx_buf_t *b, char *table); + +static char *ngx_charset_map_block(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static char *ngx_charset_map(ngx_conf_t *cf, ngx_command_t *dummy, void *conf); + +static char *ngx_http_set_charset_slot(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static ngx_int_t ngx_http_add_charset(ngx_array_t *charsets, ngx_str_t *name); + +static ngx_int_t ngx_http_charset_filter_init(ngx_cycle_t *cycle); + +static void *ngx_http_charset_create_main_conf(ngx_conf_t *cf); +static char *ngx_http_charset_init_main_conf(ngx_conf_t *cf, void *conf); +static void *ngx_http_charset_create_loc_conf(ngx_conf_t *cf); +static char *ngx_http_charset_merge_loc_conf(ngx_conf_t *cf, + void *parent, void *child); + + +static ngx_command_t ngx_http_charset_filter_commands[] = { + + { ngx_string("charset_map"), + NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE2, + ngx_charset_map_block, + NGX_HTTP_MAIN_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("default_charset"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_http_set_charset_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_charset_loc_conf_t, default_charset), + NULL }, + + { ngx_string("source_charset"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_http_set_charset_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_charset_loc_conf_t, source_charset), + NULL }, + + { ngx_string("charset"), + 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_charset_loc_conf_t, enable), + NULL }, + + { ngx_string("autodetect_charset"), + 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_charset_loc_conf_t, autodetect), + NULL }, + + ngx_null_command +}; + + +static ngx_http_module_t ngx_http_charset_filter_module_ctx = { + NULL, /* pre conf */ + + ngx_http_charset_create_main_conf, /* create main configuration */ + ngx_http_charset_init_main_conf, /* init main configuration */ + + NULL, /* create server configuration */ + NULL, /* merge server configuration */ + + ngx_http_charset_create_loc_conf, /* create location configuration */ + ngx_http_charset_merge_loc_conf /* merge location configuration */ +}; + + +ngx_module_t ngx_http_charset_filter_module = { + NGX_MODULE, + &ngx_http_charset_filter_module_ctx, /* module context */ + ngx_http_charset_filter_commands, /* module directives */ + NGX_HTTP_MODULE, /* module type */ + ngx_http_charset_filter_init, /* init module */ + NULL /* init child */ +}; + + +static ngx_http_output_header_filter_pt ngx_http_next_header_filter; +static ngx_http_output_body_filter_pt ngx_http_next_body_filter; + + +static ngx_int_t ngx_http_charset_header_filter(ngx_http_request_t *r) +{ + ngx_http_charset_t *charsets; + ngx_http_charset_ctx_t *ctx; + ngx_http_charset_loc_conf_t *lcf; + ngx_http_charset_main_conf_t *mcf; + + mcf = ngx_http_get_module_main_conf(r, ngx_http_charset_filter_module); + lcf = ngx_http_get_module_loc_conf(r, ngx_http_charset_filter_module); + + if (lcf->enable == 0) { + return ngx_http_next_header_filter(r); + } + +#if 0 + if (lcf->default_charset.len == 0) { + return ngx_http_next_header_filter(r); + } +#endif + + if (r->headers_out.content_type == NULL) { + return ngx_http_next_header_filter(r); + } + + if (ngx_strncasecmp(r->headers_out.content_type->value.data, + "text/", 5) != 0 + && ngx_strncasecmp(r->headers_out.content_type->value.data, + "application/x-javascript", 24) != 0) + { + return ngx_http_next_header_filter(r); + } + + if (ngx_strstr(r->headers_out.content_type->value.data, "charset") != NULL) + { + return ngx_http_next_header_filter(r); + } + + if (r->headers_out.status == NGX_HTTP_MOVED_PERMANENTLY + && r->headers_out.status == NGX_HTTP_MOVED_TEMPORARILY) + { + /* + * do not set charset for the redirect because NN 4.x uses this + * charset instead of the next page charset + */ + + r->headers_out.charset.len = 0; + return ngx_http_next_header_filter(r); + } + + if (r->headers_out.charset.len) { + return ngx_http_next_header_filter(r); + } + + charsets = mcf->charsets.elts; + r->headers_out.charset = charsets[lcf->default_charset].name; + + if (lcf->default_charset == lcf->source_charset) { + return ngx_http_next_header_filter(r); + } + + ngx_http_create_ctx(r, ctx, ngx_http_charset_filter_module, + sizeof(ngx_http_charset_ctx_t), NGX_ERROR); + + r->filter_need_in_memory = 1; + + return ngx_http_next_header_filter(r); +} + + +static ngx_int_t ngx_http_charset_body_filter(ngx_http_request_t *r, + ngx_chain_t *in) +{ + char *table; + ngx_chain_t *cl; + ngx_http_charset_t *charsets; + ngx_http_charset_ctx_t *ctx; + ngx_http_charset_loc_conf_t *lcf; + ngx_http_charset_main_conf_t *mcf; + + ctx = ngx_http_get_module_ctx(r, ngx_http_charset_filter_module); + + if (ctx == NULL) { + return ngx_http_next_body_filter(r, in); + } + + mcf = ngx_http_get_module_main_conf(r, ngx_http_charset_filter_module); + lcf = ngx_http_get_module_loc_conf(r, ngx_http_charset_filter_module); + + charsets = mcf->charsets.elts; + table = charsets[lcf->source_charset].tables[lcf->default_charset]; + + for (cl = in; cl; cl = cl->next) { + ngx_charset_recode(cl->buf, table); + } + + return ngx_http_next_body_filter(r, in); +} + + +static void ngx_charset_recode(ngx_buf_t *b, char *table) +{ + u_char *p, c; + + for (p = b->pos; p < b->last; p++) { + c = *p; + *p = table[c]; + } +} + + +static char *ngx_charset_map_block(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf) +{ + ngx_http_charset_main_conf_t *mcf = conf; + + char *rv; + ngx_int_t src, dst; + ngx_uint_t i; + ngx_str_t *value; + ngx_conf_t pvcf; + ngx_http_charset_tables_t *table; + + value = cf->args->elts; + + src = ngx_http_add_charset(&mcf->charsets, &value[1]); + if (src == NGX_ERROR) { + return NGX_CONF_ERROR; + } + + dst = ngx_http_add_charset(&mcf->charsets, &value[2]); + if (dst == NGX_ERROR) { + return NGX_CONF_ERROR; + } + + if (src == dst) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "\"charset_map\" between the same charsets " + "\"%s\" and \"%s\"", + value[1].data, value[2].data); + return NGX_CONF_ERROR; + } + + table = mcf->tables.elts; + for (i = 0; i < mcf->tables.nelts; i++) { + if ((src == table->src && dst == table->dst) + || (src == table->dst && dst == table->src)) + { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "duplicate \"charset_map\" between " + "\"%s\" and \"%s\"", + value[1].data, value[2].data); + return NGX_CONF_ERROR; + } + } + + if (!(table = ngx_push_array(&mcf->tables))) { + return NGX_CONF_ERROR; + } + + table->src = src; + table->dst = dst; + + if (!(table->src2dst = ngx_palloc(cf->pool, 256))) { + return NGX_CONF_ERROR; + } + + if (!(table->dst2src = ngx_palloc(cf->pool, 256))) { + return NGX_CONF_ERROR; + } + + for (i = 0; i < 128; i++) { + table->src2dst[i] = (char) i; + table->dst2src[i] = (char) i; + } + + for (/* void */; i < 256; i++) { + table->src2dst[i] = '?'; + table->dst2src[i] = '?'; + } + + pvcf = *cf; + cf->ctx = table; + cf->handler = ngx_charset_map; + cf->handler_conf = conf; + rv = ngx_conf_parse(cf, NULL); + *cf = pvcf; + + return rv; +} + + +static char *ngx_charset_map(ngx_conf_t *cf, ngx_command_t *dummy, void *conf) +{ + ngx_int_t src, dst; + ngx_str_t *value; + ngx_http_charset_tables_t *table; + + if (cf->args->nelts != 2) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameters number"); + return NGX_CONF_ERROR; + } + + value = cf->args->elts; + + src = ngx_hextoi(value[0].data, value[0].len); + if (src == NGX_ERROR || src > 255) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid value \"%s\"", value[0].data); + return NGX_CONF_ERROR; + } + + dst = ngx_hextoi(value[1].data, value[1].len); + if (dst == NGX_ERROR || dst > 255) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid value \"%s\"", value[1].data); + return NGX_CONF_ERROR; + } + + table = cf->ctx; + + table->src2dst[src] = (char) dst; + table->dst2src[dst] = (char) src; + + return NGX_CONF_OK; +} + + +static char *ngx_http_set_charset_slot(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf) +{ + char *p = conf; + + ngx_int_t *cp; + ngx_str_t *value; + ngx_http_charset_t *charset; + ngx_http_charset_main_conf_t *mcf; + + cp = (ngx_int_t *) (p + cmd->offset); + + if (*cp != NGX_CONF_UNSET) { + return "is duplicate"; + } + + mcf = ngx_http_conf_get_module_main_conf(cf, + ngx_http_charset_filter_module); + + value = cf->args->elts; + + *cp = ngx_http_add_charset(&mcf->charsets, &value[1]); + if (*cp == NGX_ERROR) { + return NGX_CONF_ERROR; + } + + if (cmd->offset == offsetof(ngx_http_charset_loc_conf_t, source_charset)) { + charset = mcf->charsets.elts; + charset[*cp].server = 1; + } + + return NGX_CONF_OK; +} + + +static ngx_int_t ngx_http_add_charset(ngx_array_t *charsets, ngx_str_t *name) +{ + ngx_uint_t i; + ngx_http_charset_t *c; + + c = charsets->elts; + for (i = 0; i < charsets->nelts; i++) { + if (name->len != c[i].name.len) { + continue; + } + + if (ngx_strcasecmp(name->data, c[i].name.data) == 0) { + break; + } + } + + if (i < charsets->nelts) { + return i; + } + + if (!(c = ngx_push_array(charsets))) { + return NGX_ERROR; + } + + c->name = *name; + + return i; +} + + +static ngx_int_t ngx_http_charset_filter_init(ngx_cycle_t *cycle) +{ + ngx_http_next_header_filter = ngx_http_top_header_filter; + ngx_http_top_header_filter = ngx_http_charset_header_filter; + + ngx_http_next_body_filter = ngx_http_top_body_filter; + ngx_http_top_body_filter = ngx_http_charset_body_filter; + + return NGX_OK; +} + + +static void *ngx_http_charset_create_main_conf(ngx_conf_t *cf) +{ + ngx_http_charset_main_conf_t *mcf; + + if (!(mcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_charset_main_conf_t)))) { + return NGX_CONF_ERROR; + } + + ngx_init_array(mcf->charsets, cf->pool, 5, sizeof(ngx_http_charset_t), + NGX_CONF_ERROR); + + ngx_init_array(mcf->tables, cf->pool, 10, sizeof(ngx_http_charset_tables_t), + NGX_CONF_ERROR); + + return mcf; +} + + +static char *ngx_http_charset_init_main_conf(ngx_conf_t *cf, void *conf) +{ + ngx_http_charset_main_conf_t *mcf = conf; + + ngx_uint_t i, n; + ngx_http_charset_t *charset; + ngx_http_charset_tables_t *tables; + + tables = mcf->tables.elts; + charset = mcf->charsets.elts; + + for (i = 0; i < mcf->charsets.nelts; i++) { + if (!charset[i].server) { + continue; + } + + charset[i].tables = ngx_pcalloc(cf->pool, + sizeof(char *) * mcf->charsets.nelts); + + if (charset[i].tables == NULL) { + return NGX_CONF_ERROR; + } + + for (n = 0; n < mcf->tables.nelts; n++) { + if ((ngx_int_t) i == tables[n].src) { + charset[i].tables[tables[n].dst] = tables[n].src2dst; + continue; + } + + if ((ngx_int_t) i == tables[n].dst) { + charset[i].tables[tables[n].src] = tables[n].dst2src; + } + } + } + + for (i = 0; i < mcf->charsets.nelts; i++) { + if (!charset[i].server) { + continue; + } + + for (n = 0; n < mcf->charsets.nelts; n++) { + if (i == n) { + continue; + } + + if (charset[i].tables[n]) { + continue; + } + + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + " no \"charset_map\" between the charsets " + "\"%s\" and \"%s\"", + charset[i].name.data, charset[n].name.data); + return NGX_CONF_ERROR; + } + } + + return NGX_CONF_OK; +} + + +static void *ngx_http_charset_create_loc_conf(ngx_conf_t *cf) +{ + ngx_http_charset_loc_conf_t *lcf; + + if (!(lcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_charset_loc_conf_t)))) { + return NGX_CONF_ERROR; + } + + lcf->enable = NGX_CONF_UNSET; + lcf->autodetect = NGX_CONF_UNSET; + lcf->default_charset = NGX_CONF_UNSET; + lcf->source_charset = NGX_CONF_UNSET; + + return lcf; +} + + +static char *ngx_http_charset_merge_loc_conf(ngx_conf_t *cf, + void *parent, void *child) +{ + ngx_http_charset_loc_conf_t *prev = parent; + ngx_http_charset_loc_conf_t *conf = child; + + ngx_conf_merge_value(conf->enable, prev->enable, 0); + ngx_conf_merge_value(conf->autodetect, prev->autodetect, 0); + + if (conf->source_charset == NGX_CONF_UNSET) { + conf->source_charset = prev->source_charset; + } + + ngx_conf_merge_value(conf->default_charset, prev->default_charset, + conf->source_charset); + + return NGX_CONF_OK; +} diff --git a/src/http/modules/ngx_http_chunked_filter.c b/src/http/modules/ngx_http_chunked_filter.c new file mode 100644 --- /dev/null +++ b/src/http/modules/ngx_http_chunked_filter.c @@ -0,0 +1,156 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include + + +static ngx_int_t ngx_http_chunked_filter_init(ngx_cycle_t *cycle); + + +static ngx_http_module_t ngx_http_chunked_filter_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_chunked_filter_module = { + NGX_MODULE, + &ngx_http_chunked_filter_module_ctx, /* module context */ + NULL, /* module directives */ + NGX_HTTP_MODULE, /* module type */ + ngx_http_chunked_filter_init, /* init module */ + NULL /* init child */ +}; + + +static ngx_http_output_header_filter_pt ngx_http_next_header_filter; +static ngx_http_output_body_filter_pt ngx_http_next_body_filter; + + +static ngx_int_t ngx_http_chunked_header_filter(ngx_http_request_t *r) +{ + if (r->headers_out.status == NGX_HTTP_NOT_MODIFIED) { + return ngx_http_next_header_filter(r); + } + + if (r->headers_out.content_length_n == -1) { + if (r->http_version < NGX_HTTP_VERSION_11) { + r->keepalive = 0; + + } else { + r->chunked = 1; + } + } + + return ngx_http_next_header_filter(r); +} + + +static ngx_int_t ngx_http_chunked_body_filter(ngx_http_request_t *r, + ngx_chain_t *in) +{ + u_char *chunk; + size_t size, len; + ngx_buf_t *b; + ngx_chain_t out, tail, *cl, *tl, **ll; + + if (in == NULL || !r->chunked) { + return ngx_http_next_body_filter(r, in); + } + + out.buf = NULL; + ll = &out.next; + + size = 0; + cl = in; + + for ( ;; ) { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http chunk: %d", ngx_buf_size(cl->buf)); + + size += ngx_buf_size(cl->buf); + + ngx_test_null(tl, ngx_alloc_chain_link(r->pool), NGX_ERROR); + tl->buf = cl->buf; + *ll = tl; + ll = &tl->next; + + if (cl->next == NULL) { + break; + } + + cl = cl->next; + } + + if (size) { + ngx_test_null(chunk, ngx_palloc(r->pool, 11), NGX_ERROR); + len = ngx_snprintf((char *) chunk, 11, SIZE_T_X_FMT CRLF, size); + + ngx_test_null(b, ngx_calloc_buf(r->pool), NGX_ERROR); + b->temporary = 1; + b->pos = chunk; + b->last = chunk + len; + + out.buf = b; + } + + if (cl->buf->last_buf) { + ngx_test_null(b, ngx_calloc_buf(r->pool), NGX_ERROR); + b->memory = 1; + b->last_buf = 1; + b->pos = (u_char *) CRLF "0" CRLF CRLF; + b->last = b->pos + 7; + + cl->buf->last_buf = 0; + + if (size == 0) { + b->pos += 2; + out.buf = b; + out.next = NULL; + + return ngx_http_next_body_filter(r, &out); + } + + } else { + if (size == 0) { + *ll = NULL; + return ngx_http_next_body_filter(r, out.next); + } + + ngx_test_null(b, ngx_calloc_buf(r->pool), NGX_ERROR); + b->memory = 1; + b->pos = (u_char *) CRLF; + b->last = b->pos + 2; + } + + tail.buf = b; + tail.next = NULL; + *ll = &tail; + + return ngx_http_next_body_filter(r, &out); +} + + +static ngx_int_t ngx_http_chunked_filter_init(ngx_cycle_t *cycle) +{ + ngx_http_next_header_filter = ngx_http_top_header_filter; + ngx_http_top_header_filter = ngx_http_chunked_header_filter; + + ngx_http_next_body_filter = ngx_http_top_body_filter; + ngx_http_top_body_filter = ngx_http_chunked_body_filter; + + return NGX_OK; +} diff --git a/src/http/modules/ngx_http_gzip_filter.c b/src/http/modules/ngx_http_gzip_filter.c new file mode 100644 --- /dev/null +++ b/src/http/modules/ngx_http_gzip_filter.c @@ -0,0 +1,1036 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include + +#include + + +typedef struct { + ngx_flag_t enable; + ngx_flag_t no_buffer; + + ngx_bufs_t bufs; + + ngx_uint_t http_version; + ngx_uint_t proxied; + + int 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 + + +typedef struct { + ngx_chain_t *in; + ngx_chain_t *free; + ngx_chain_t *busy; + ngx_chain_t *out; + ngx_chain_t **last_out; + ngx_buf_t *in_buf; + ngx_buf_t *out_buf; + ngx_int_t bufs; + + off_t length; + + void *preallocated; + char *free_mem; + ngx_uint_t allocated; + + unsigned flush:4; + unsigned redo:1; + unsigned done:1; +#if 0 + unsigned pass:1; + unsigned blocked:1; +#endif + + size_t zin; + size_t zout; + + uint32_t crc32; + z_stream zstream; + ngx_http_request_t *request; +} ngx_http_gzip_ctx_t; + + +static ngx_int_t ngx_http_gzip_proxied(ngx_http_request_t *r, + ngx_http_gzip_conf_t *conf); +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); +ngx_inline static int ngx_http_gzip_error(ngx_http_gzip_ctx_t *ctx); + +static u_char *ngx_http_gzip_log_ratio(ngx_http_request_t *r, u_char *buf, + uintptr_t data); + +static ngx_int_t ngx_http_gzip_pre_conf(ngx_conf_t *cf); +static ngx_int_t ngx_http_gzip_filter_init(ngx_cycle_t *cycle); +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_set_window(ngx_conf_t *cf, void *post, void *data); +static char *ngx_http_gzip_set_hash(ngx_conf_t *cf, void *post, void *data); + + +static ngx_conf_num_bounds_t ngx_http_gzip_comp_level_bounds = { + ngx_conf_check_num_bounds, 1, 9 +}; + +static ngx_conf_post_handler_pt ngx_http_gzip_set_window_p = + ngx_http_gzip_set_window; +static ngx_conf_post_handler_pt ngx_http_gzip_set_hash_p = + ngx_http_gzip_set_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"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_gzip_conf_t, enable), + NULL }, + + { ngx_string("gzip_buffers"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2, + ngx_conf_set_bufs_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_gzip_conf_t, bufs), + NULL }, + + { ngx_string("gzip_comp_level"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_conf_set_num_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_gzip_conf_t, level), + &ngx_http_gzip_comp_level_bounds }, + + { ngx_string("gzip_window"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_conf_set_size_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_gzip_conf_t, wbits), + &ngx_http_gzip_set_window_p }, + + { ngx_string("gzip_hash"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_conf_set_size_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_gzip_conf_t, memlevel), + &ngx_http_gzip_set_hash_p }, + + { ngx_string("gzip_no_buffer"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_gzip_conf_t, no_buffer), + NULL }, + + { ngx_string("gzip_http_version"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_ANY, + 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_ANY, + 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, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_gzip_conf_t, min_length), + NULL }, + + ngx_null_command +}; + + +static ngx_http_module_t ngx_http_gzip_filter_module_ctx = { + ngx_http_gzip_pre_conf, /* pre conf */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + NULL, /* create server configuration */ + NULL, /* merge server configuration */ + + ngx_http_gzip_create_conf, /* create location configuration */ + ngx_http_gzip_merge_conf, /* merge location configuration */ +}; + + +ngx_module_t ngx_http_gzip_filter_module = { + NGX_MODULE, + &ngx_http_gzip_filter_module_ctx, /* module context */ + ngx_http_gzip_filter_commands, /* module directives */ + NGX_HTTP_MODULE, /* module type */ + ngx_http_gzip_filter_init, /* init module */ + NULL /* init child */ +}; + + +static ngx_http_log_op_name_t ngx_http_gzip_log_fmt_ops[] = { + { ngx_string("gzip_ratio"), NGX_INT32_LEN + 3, ngx_http_gzip_log_ratio }, + { ngx_null_string, 0, NULL } +}; + + + +static u_char gzheader[10] = { 0x1f, 0x8b, Z_DEFLATED, 0, 0, 0, 0, 0, 0, 3 }; + +#if (HAVE_LITTLE_ENDIAN) + +struct gztrailer { + uint32_t crc32; + uint32_t zlen; +}; + +#else /* HAVE_BIG_ENDIAN */ + +struct gztrailer { + u_char crc32[4]; + u_char zlen[4]; +}; + +#endif + + +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_gzip_header_filter(ngx_http_request_t *r) +{ + ngx_http_gzip_ctx_t *ctx; + ngx_http_gzip_conf_t *conf; + + conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module); + + if (!conf->enable + || r->headers_out.status != NGX_HTTP_OK + || r->header_only + || r->http_version < conf->http_version + || (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_strstr(r->headers_in.accept_encoding->value.data, "gzip") == NULL + ) + { + return ngx_http_next_header_filter(r); + } + + /* TODO: "text/html" -> custom types */ + if (r->headers_out.content_type + && ngx_strncasecmp(r->headers_out.content_type->value.data, + "text/html", 9) != 0) + { + return ngx_http_next_header_filter(r); + } + + + if (r->headers_in.via) { + if (conf->proxied & NGX_HTTP_GZIP_PROXIED_OFF) { + return ngx_http_next_header_filter(r); + } + + if (!(conf->proxied & NGX_HTTP_GZIP_PROXIED_ANY) + && ngx_http_gzip_proxied(r, conf) == NGX_DECLINED) + { + 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) { + return ngx_http_next_header_filter(r); + } + + + ngx_http_create_ctx(r, ctx, ngx_http_gzip_filter_module, + sizeof(ngx_http_gzip_ctx_t), NGX_ERROR); + ctx->request = r; + + r->headers_out.content_encoding = ngx_list_push(&r->headers_out.headers); + if (r->headers_out.content_encoding == NULL) { + return NGX_ERROR; + } + + 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; + r->headers_out.content_length_n = -1; + if (r->headers_out.content_length) { + r->headers_out.content_length->key.len = 0; + r->headers_out.content_length = NULL; + } + r->filter_need_in_memory = 1; + + return ngx_http_next_header_filter(r); +} + + +static ngx_int_t ngx_http_gzip_proxied(ngx_http_request_t *r, + ngx_http_gzip_conf_t *conf) +{ + time_t date, expires; + + if (r->headers_in.authorization + && (conf->proxied & NGX_HTTP_GZIP_PROXIED_AUTH)) + { + return NGX_OK; + } + + if (r->headers_out.expires) { + + if (!(conf->proxied & NGX_HTTP_GZIP_PROXIED_EXPIRED)) { + return NGX_DECLINED; + } + + expires = ngx_http_parse_time(r->headers_out.expires->value.data, + r->headers_out.expires->value.len); + if (expires == NGX_ERROR) { + return NGX_DECLINED; + } + + 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; + } + + } else { + date = ngx_time(); + } + + if (expires < date) { + return NGX_OK; + } + + return NGX_DECLINED; + } + + if (r->headers_out.cache_control) { + + if ((conf->proxied & NGX_HTTP_GZIP_PROXIED_NO_CACHE) + && ngx_strstr(r->headers_out.cache_control->value.data, "no-cache")) + { + return NGX_OK; + } + + if ((conf->proxied & NGX_HTTP_GZIP_PROXIED_NO_STORE) + && ngx_strstr(r->headers_out.cache_control->value.data, "no-store")) + { + return NGX_OK; + } + + if ((conf->proxied & NGX_HTTP_GZIP_PROXIED_PRIVATE) + && ngx_strstr(r->headers_out.cache_control->value.data, "private")) + { + return NGX_OK; + } + + return NGX_DECLINED; + } + + if ((conf->proxied & NGX_HTTP_GZIP_PROXIED_NO_LM) + && r->headers_out.last_modified) + { + return NGX_DECLINED; + } + + if ((conf->proxied & NGX_HTTP_GZIP_PROXIED_NO_ETAG) + && r->headers_out.etag) + { + return NGX_DECLINED; + } + + return NGX_OK; +} + + +static ngx_int_t ngx_http_gzip_body_filter(ngx_http_request_t *r, + ngx_chain_t *in) +{ + int rc, wbits, memlevel, last; + struct gztrailer *trailer; + ngx_buf_t *b; + ngx_chain_t *cl; + ngx_http_gzip_ctx_t *ctx; + ngx_http_gzip_conf_t *conf; + + 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); + } + + conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module); + + if (ctx->preallocated == NULL) { + wbits = conf->wbits; + memlevel = conf->memlevel; + + if (ctx->length > 0) { + + /* the actual zlib window size is smaller by 262 bytes */ + + while (ctx->length < ((1 << (wbits - 1)) - 262)) { + wbits--; + memlevel--; + } + } + + /* + * We preallocate a memory for zlib in one buffer (200K-400K), this + * dicreases a number of malloc() and free() calls and also probably + * dicreases a number of syscalls (sbrk() or so). + * 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 x86 and sparc64 (32-bit mode) + * * 5920 bytes on amd64 and sparc64 + */ + + ctx->allocated = 8192 + (1 << (wbits + 2)) + (1 << (memlevel + 9)); + + if (!(ctx->preallocated = ngx_palloc(r->pool, ctx->allocated))) { + 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, 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); + return ngx_http_gzip_error(ctx); + } + + if (!(b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t)))) { + return ngx_http_gzip_error(ctx); + } + + b->memory = 1; + b->pos = gzheader; + b->last = b->pos + 10; + + ngx_alloc_link_and_set_buf(cl, b, r->pool, ngx_http_gzip_error(ctx)); + ctx->out = cl; + ctx->last_out = &cl->next; + + ctx->crc32 = crc32(0L, Z_NULL, 0); + ctx->flush = Z_NO_FLUSH; + } + + if (in) { + if (ngx_chain_add_copy(r->pool, &ctx->in, in) == NGX_ERROR) { + return ngx_http_gzip_error(ctx); + } + } + + last = NGX_NONE; + + for ( ;; ) { + + for ( ;; ) { + + /* does zlib need a new data ? */ + + 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: " PTR_FMT, 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:" PTR_FMT " ni:" PTR_FMT " ai:%d", + 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); + } + } + + + /* is there a space for the gzipped data ? */ + + if (ctx->zstream.avail_out == 0) { + + 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) { + return ngx_http_gzip_error(ctx); + } + + ctx->out_buf->tag = (ngx_buf_tag_t) + &ngx_http_gzip_filter_module; + ctx->out_buf->recycled = 1; + ctx->bufs++; + + } else { +#if 0 + ctx->blocked = 1; +#endif + break; + } + +#if 0 + ctx->blocked = 0; +#endif + ctx->zstream.next_out = ctx->out_buf->pos; + ctx->zstream.avail_out = conf->bufs.size; + } + + ngx_log_debug6(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "deflate in: ni:%X no:%X ai:%d ao:%d 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_http_gzip_error(ctx); + } + + ngx_log_debug5(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "deflate out: ni:%X no:%X ai:%d ao:%d 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:" PTR_FMT " pos:" PTR_FMT, + ctx->in_buf, ctx->in_buf->pos); + + + if (ctx->zstream.next_in) { + ctx->in_buf->pos = ctx->zstream.next_in; + + if (ctx->zstream.avail_in == 0) { + ctx->zstream.next_in = NULL; + } + } + + ctx->out_buf->last = ctx->zstream.next_out; + + if (ctx->zstream.avail_out == 0) { + + /* zlib wants to output some more gzipped data */ + + ngx_alloc_link_and_set_buf(cl, ctx->out_buf, r->pool, + ngx_http_gzip_error(ctx)); + *ctx->last_out = cl; + ctx->last_out = &cl->next; + + ctx->redo = 1; + + continue; + } + + ctx->redo = 0; + + if (ctx->flush == Z_SYNC_FLUSH) { + + ctx->out_buf->flush = 0; + ctx->flush = Z_NO_FLUSH; + + ngx_alloc_link_and_set_buf(cl, ctx->out_buf, r->pool, + ngx_http_gzip_error(ctx)); + *ctx->last_out = cl; + ctx->last_out = &cl->next; + +#if 0 + ctx->pass = 1; +#endif + + break; + } + + if (rc == Z_STREAM_END) { + + 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_http_gzip_error(ctx); + } + + ngx_pfree(r->pool, ctx->preallocated); + + ngx_alloc_link_and_set_buf(cl, ctx->out_buf, r->pool, + ngx_http_gzip_error(ctx)); + *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 { + if (!(b = ngx_create_temp_buf(r->pool, 8))) { + return ngx_http_gzip_error(ctx); + } + + b->last_buf = 1; + + ngx_alloc_link_and_set_buf(cl, b, r->pool, + ngx_http_gzip_error(ctx)); + *ctx->last_out = cl; + ctx->last_out = &cl->next; + trailer = (struct gztrailer *) b->pos; + b->last += 8; + } + +#if (HAVE_LITTLE_ENDIAN) + trailer->crc32 = ctx->crc32; + trailer->zlen = ctx->zin; +#else + trailer->crc32[0] = ctx->crc32 & 0xff; + trailer->crc32[1] = (ctx->crc32 >> 8) & 0xff; + trailer->crc32[2] = (ctx->crc32 >> 16) & 0xff; + trailer->crc32[3] = (ctx->crc32 >> 24) & 0xff; + + trailer->zlen[0] = ctx->zin & 0xff; + trailer->zlen[1] = (ctx->zin >> 8) & 0xff; + trailer->zlen[2] = (ctx->zin >> 16) & 0xff; + trailer->zlen[3] = (ctx->zin >> 24) & 0xff; +#endif + + ctx->zstream.avail_in = 0; + ctx->zstream.avail_out = 0; + + ctx->done = 1; +#if 0 + ctx->pass = 1; +#endif + + break; + } + + if (conf->no_buffer && ctx->in == NULL) { + ngx_alloc_link_and_set_buf(cl, ctx->out_buf, r->pool, + ngx_http_gzip_error(ctx)); + *ctx->last_out = cl; + ctx->last_out = &cl->next; + +#if 0 + ctx->pass = 1; +#endif + + break; + } + } + +#if 0 + + /* OLD CODE */ + + if (ctx->out) { + if (ctx->pass) { + ctx->pass = 0; + + } else if (last == NGX_AGAIN) { + return last; + } + + } else if (ctx->busy->buf && ngx_buf_size(ctx->busy->buf)) { + if (last != NGX_NONE) { + return last; + } + + } else if (ctx->blocked) { + if (last != NGX_NONE) { + return last; + } + + } else { + if (last == NGX_NONE) { + return NGX_OK; + } + + return last; + } +#endif + + /* NEW CODE */ + + if (last == NGX_AGAIN) { + return NGX_AGAIN; + } + + if (ctx->out == NULL && ctx->busy == NULL) { + return NGX_OK; + } + + /**/ + + last = ngx_http_next_body_filter(r, ctx->out); + + /* + * we do not check NGX_AGAIN here because the downstream filters + * may free some buffers and zlib may compress some data into them + */ + + if (last == NGX_ERROR) { + return ngx_http_gzip_error(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; + + if (ctx->done) { + return last; + } + } +} + + +static void *ngx_http_gzip_filter_alloc(void *opaque, u_int items, u_int size) +{ + ngx_http_gzip_ctx_t *ctx = opaque; + + void *p; + ngx_uint_t alloc; + + alloc = items * size; + if (alloc % 512 != 0) { + + /* + * the zlib deflate_state allocation, it takes about 6K, we allocate 8K + */ + + alloc = (alloc + ngx_pagesize - 1) & ~(ngx_pagesize - 1); + } + + if (alloc <= ctx->allocated) { + p = ctx->free_mem; + ctx->free_mem += alloc; + ctx->allocated -= alloc; + + ngx_log_debug4(NGX_LOG_DEBUG_HTTP, ctx->request->connection->log, 0, + "gzip alloc: n:%d s:%d a:%d p:" PTR_FMT, + items, size, alloc, p); + + return p; + } + + ngx_log_error(NGX_LOG_ALERT, ctx->request->connection->log, 0, + "gzip filter failed to use preallocated memory: %d of %d", + items * size, ctx->allocated); + + p = ngx_palloc(ctx->request->pool, items * size); + + return p; +} + + +static void ngx_http_gzip_filter_free(void *opaque, void *address) +{ +#if 0 + ngx_http_gzip_ctx_t *ctx = opaque; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->request->connection->log, 0, + "gzip free: %X", address); +#endif +} + + +static u_char *ngx_http_gzip_log_ratio(ngx_http_request_t *r, u_char *buf, + uintptr_t data) +{ + ngx_uint_t zint, zfrac; + ngx_http_gzip_ctx_t *ctx; + + ctx = ngx_http_get_module_ctx(r, ngx_http_gzip_filter_module); + + if (ctx == NULL || ctx->zout == 0) { + *buf = '-'; + return buf + 1; + } + +#if 0 + return buf + ngx_snprintf((char *) buf, NGX_INT32_LEN + 4, "%.2f", + (float) ctx->zin / ctx->zout); +#endif + + /* we prefer do not use FPU */ + + zint = (ngx_uint_t) (ctx->zin / ctx->zout); + zfrac = (ngx_uint_t) ((ctx->zin * 100 / ctx->zout) % 100); + + if ((ctx->zin * 1000 / ctx->zout) %10 > 4) { + if (++zfrac > 99) { + zint++; + zfrac = 0; + } + } + + return buf + ngx_snprintf((char *) buf, NGX_INT32_LEN + 4, + "%" NGX_UINT_T_FMT ".%02" NGX_UINT_T_FMT, + zint, zfrac); +} + + +ngx_inline static int ngx_http_gzip_error(ngx_http_gzip_ctx_t *ctx) +{ + deflateEnd(&ctx->zstream); + + ngx_pfree(ctx->request->pool, ctx->preallocated); + + ctx->zstream.avail_in = 0; + ctx->zstream.avail_out = 0; + + ctx->done = 1; + + return NGX_ERROR; +} + + +static ngx_int_t ngx_http_gzip_pre_conf(ngx_conf_t *cf) +{ + ngx_http_log_op_name_t *op; + + for (op = ngx_http_gzip_log_fmt_ops; op->name.len; op++) { /* void */ } + op->op = NULL; + + op = ngx_http_log_fmt_ops; + + for (op = ngx_http_log_fmt_ops; op->op; op++) { + if (op->name.len == 0) { + op = (ngx_http_log_op_name_t *) op->op; + } + } + + op->op = (ngx_http_log_op_pt) ngx_http_gzip_log_fmt_ops; + + return NGX_OK; +} + + +static ngx_int_t ngx_http_gzip_filter_init(ngx_cycle_t *cycle) +{ + ngx_http_next_header_filter = ngx_http_top_header_filter; + ngx_http_top_header_filter = ngx_http_gzip_header_filter; + + ngx_http_next_body_filter = ngx_http_top_body_filter; + ngx_http_top_body_filter = ngx_http_gzip_body_filter; + + return NGX_OK; +} + + +static void *ngx_http_gzip_create_conf(ngx_conf_t *cf) +{ + ngx_http_gzip_conf_t *conf; + + if (!(conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_gzip_conf_t)))) { + return NGX_CONF_ERROR; + } + + /* + + set by ngx_pcalloc(): + + conf->bufs.num = 0; + conf->proxied = 0; + + */ + + conf->enable = NGX_CONF_UNSET; + conf->no_buffer = NGX_CONF_UNSET; + + conf->http_version = NGX_CONF_UNSET_UINT; + + conf->level = NGX_CONF_UNSET; + conf->wbits = (size_t) NGX_CONF_UNSET; + conf->memlevel = (size_t) NGX_CONF_UNSET; + conf->min_length = NGX_CONF_UNSET; + + return conf; +} + + +static char *ngx_http_gzip_merge_conf(ngx_conf_t *cf, + void *parent, void *child) +{ + ngx_http_gzip_conf_t *prev = parent; + ngx_http_gzip_conf_t *conf = child; + + ngx_conf_merge_value(conf->enable, prev->enable, 0); + + ngx_conf_merge_bufs_value(conf->bufs, prev->bufs, 4, ngx_pagesize); + + ngx_conf_merge_unsigned_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_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, 0); + ngx_conf_merge_value(conf->no_buffer, prev->no_buffer, 0); + + return NGX_CONF_OK; +} + + +static char *ngx_http_gzip_set_window(ngx_conf_t *cf, void *post, void *data) +{ + int *np = data; + + int wbits, wsize; + + wbits = 15; + + for (wsize = 32 * 1024; wsize > 256; wsize >>= 1) { + + if (wsize == *np) { + *np = wbits; + + return NGX_CONF_OK; + } + + wbits--; + } + + return "must be 512, 1k, 2k, 4k, 8k, 16k, or 32k"; +} + + +static char *ngx_http_gzip_set_hash(ngx_conf_t *cf, void *post, void *data) +{ + int *np = data; + + int memlevel, hsize; + + memlevel = 9; + + for (hsize = 128 * 1024; hsize > 256; hsize >>= 1) { + + if (hsize == *np) { + *np = memlevel; + + return NGX_CONF_OK; + } + + memlevel--; + } + + return "must be 512, 1k, 2k, 4k, 8k, 16k, 32k, 64k, or 128k"; +} diff --git a/src/http/modules/ngx_http_headers_filter.c b/src/http/modules/ngx_http_headers_filter.c new file mode 100644 --- /dev/null +++ b/src/http/modules/ngx_http_headers_filter.c @@ -0,0 +1,239 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include + + +typedef struct { + time_t expires; +} ngx_http_headers_conf_t; + + +#define NGX_HTTP_EXPIRES_UNSET -2147483647 +#define NGX_HTTP_EXPIRES_OFF -2147483646 +#define NGX_HTTP_EXPIRES_EPOCH -2147483645 + + +static ngx_int_t ngx_http_headers_filter_init(ngx_cycle_t *cycle); +static void *ngx_http_headers_create_conf(ngx_conf_t *cf); +static char *ngx_http_headers_merge_conf(ngx_conf_t *cf, + void *parent, void *child); +char *ngx_http_headers_expires(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); + + +static ngx_command_t ngx_http_headers_filter_commands[] = { + + { ngx_string("expires"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_http_headers_expires, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + NULL}, + + ngx_null_command +}; + + +static ngx_http_module_t ngx_http_headers_filter_module_ctx = { + NULL, /* pre conf */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + NULL, /* create server configuration */ + NULL, /* merge server configuration */ + + ngx_http_headers_create_conf, /* create location configuration */ + ngx_http_headers_merge_conf /* merge location configuration */ +}; + + +ngx_module_t ngx_http_headers_filter_module = { + NGX_MODULE, + &ngx_http_headers_filter_module_ctx, /* module context */ + ngx_http_headers_filter_commands, /* module directives */ + NGX_HTTP_MODULE, /* module type */ + ngx_http_headers_filter_init, /* init module */ + NULL /* init child */ +}; + + +static ngx_http_output_header_filter_pt ngx_http_next_header_filter; + + +static ngx_int_t ngx_http_headers_filter(ngx_http_request_t *r) +{ + size_t len; + ngx_table_elt_t *expires, *cc; + ngx_http_headers_conf_t *conf; + + if (r->headers_out.status != NGX_HTTP_OK) { + return ngx_http_next_header_filter(r); + } + + conf = ngx_http_get_module_loc_conf(r, ngx_http_headers_filter_module); + + if (conf->expires != NGX_HTTP_EXPIRES_OFF) { + + if (!(expires = ngx_list_push(&r->headers_out.headers))) { + return NGX_ERROR; + } + + r->headers_out.expires = expires; + + if (!(cc = ngx_list_push(&r->headers_out.headers))) { + return NGX_ERROR; + } + + r->headers_out.cache_control = cc; + + len = sizeof("Mon, 28 Sep 1970 06:00:00 GMT"); + + expires->key.len = sizeof("Expires") - 1; + expires->key.data = (u_char *) "Expires"; + expires->value.len = len - 1; + + cc->key.len = sizeof("Cache-Control") - 1; + cc->key.data = (u_char *) "Cache-Control"; + + 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"; + + } else { + expires->value.data = ngx_palloc(r->pool, len); + if (expires->value.data == NULL) { + return NGX_ERROR; + } + + if (conf->expires == 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"; + + } else { + ngx_http_time(expires->value.data, ngx_time() + conf->expires); + + if (conf->expires < 0) { + cc->value.len = sizeof("no-cache") - 1; + cc->value.data = (u_char *) "no-cache"; + + } else { + cc->value.data = ngx_palloc(r->pool, + sizeof("max-age=") + TIME_T_LEN + 1); + if (cc->value.data == NULL) { + return NGX_ERROR; + } + + cc->value.len = ngx_snprintf((char *) cc->value.data, + sizeof("max-age=") + TIME_T_LEN, + "max-age=" TIME_T_FMT, + conf->expires); + } + } + } + } + + return ngx_http_next_header_filter(r); +} + + +static ngx_int_t ngx_http_headers_filter_init(ngx_cycle_t *cycle) +{ + ngx_http_next_header_filter = ngx_http_top_header_filter; + ngx_http_top_header_filter = ngx_http_headers_filter; + + return NGX_OK; +} + + +static void *ngx_http_headers_create_conf(ngx_conf_t *cf) +{ + ngx_http_headers_conf_t *conf; + + if (!(conf = ngx_palloc(cf->pool, sizeof(ngx_http_headers_conf_t)))) { + return NGX_CONF_ERROR; + } + + conf->expires = NGX_HTTP_EXPIRES_UNSET; + + return conf; +} + + +static char *ngx_http_headers_merge_conf(ngx_conf_t *cf, + void *parent, void *child) +{ + ngx_http_headers_conf_t *prev = parent; + ngx_http_headers_conf_t *conf = child; + + if (conf->expires == NGX_HTTP_EXPIRES_UNSET) { + conf->expires = (prev->expires == NGX_HTTP_EXPIRES_UNSET) ? + NGX_HTTP_EXPIRES_OFF : prev->expires; + } + + return NGX_CONF_OK; +} + + +char *ngx_http_headers_expires(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_http_headers_conf_t *hcf = conf; + + ngx_uint_t minus; + ngx_str_t *value; + + if (hcf->expires != NGX_HTTP_EXPIRES_UNSET) { + 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 (ngx_strcmp(value[1].data, "off") == 0) { + hcf->expires = NGX_HTTP_EXPIRES_OFF; + return NGX_CONF_OK; + } + + if (value[1].data[0] == '+') { + value[1].data++; + value[1].len--; + minus = 0; + + } else if (value[1].data[0] == '-') { + value[1].data++; + value[1].len--; + minus = 1; + + } else { + minus = 0; + } + + hcf->expires = ngx_parse_time(&value[1], 1); + if (hcf->expires == NGX_ERROR) { + return "invalid value"; + } + + if (hcf->expires == NGX_PARSE_LARGE_TIME) { + return "value must be less than 68 years"; + } + + if (minus) { + hcf->expires = - hcf->expires; + } + + return NGX_CONF_OK; +} diff --git a/src/http/modules/ngx_http_index_handler.c b/src/http/modules/ngx_http_index_handler.c new file mode 100644 --- /dev/null +++ b/src/http/modules/ngx_http_index_handler.c @@ -0,0 +1,529 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include + + +typedef struct { + ngx_array_t indices; + size_t max_index_len; + ngx_http_cache_hash_t *index_cache; +} ngx_http_index_loc_conf_t; + + +typedef struct { + ngx_uint_t index; + u_char *last; + ngx_str_t path; + ngx_str_t redirect; + ngx_http_cache_t *cache; + 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); +static ngx_int_t ngx_http_index_error(ngx_http_request_t *r, + ngx_http_index_ctx_t *ctx, ngx_err_t err); + +static ngx_int_t ngx_http_index_init(ngx_cycle_t *cycle); +static void *ngx_http_index_create_loc_conf(ngx_conf_t *cf); +static char *ngx_http_index_merge_loc_conf(ngx_conf_t *cf, + void *parent, void *child); +static char *ngx_http_index_set_index(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); + + +static ngx_command_t ngx_http_index_commands[] = { + + { ngx_string("index"), + NGX_HTTP_LOC_CONF|NGX_CONF_1MORE, + ngx_http_index_set_index, + NGX_HTTP_LOC_CONF_OFFSET, + 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 +}; + + +ngx_http_module_t ngx_http_index_module_ctx = { + NULL, /* pre conf */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + NULL, /* create server configuration */ + NULL, /* merge server configuration */ + + ngx_http_index_create_loc_conf, /* create location configration */ + ngx_http_index_merge_loc_conf /* merge location configration */ +}; + + +ngx_module_t ngx_http_index_module = { + NGX_MODULE, + &ngx_http_index_module_ctx, /* module context */ + ngx_http_index_commands, /* module directives */ + NGX_HTTP_MODULE, /* module type */ + ngx_http_index_init, /* init module */ + NULL /* init child */ +}; + + +/* + * 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() failed 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 shows only + * that path contains the usual file in place of the directory. + */ + +ngx_int_t ngx_http_index_handler(ngx_http_request_t *r) +{ + u_char *name; + ngx_fd_t fd; + ngx_int_t rc; + ngx_str_t *index; + ngx_err_t err; + ngx_log_t *log; + ngx_http_index_ctx_t *ctx; + ngx_http_core_loc_conf_t *clcf; + ngx_http_index_loc_conf_t *ilcf; +#if (NGX_HTTP_CACHE0) + /* crc must be in ctx !! */ + uint32_t crc; +#endif + + if (r->uri.data[r->uri.len - 1] != '/') { + return NGX_DECLINED; + } + + log = r->connection->log; + + /* + * we use context because the handler supports an async file opening + * and thus can be called several times + */ + + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + ilcf = ngx_http_get_module_loc_conf(r, ngx_http_index_module); + + ctx = ngx_http_get_module_ctx(r, ngx_http_index_module); + if (ctx == NULL) { + ngx_http_create_ctx(r, ctx, ngx_http_index_module, + sizeof(ngx_http_index_ctx_t), + NGX_HTTP_INTERNAL_SERVER_ERROR); + +#if (NGX_HTTP_CACHE) + + if (ilcf->index_cache) { + ctx->cache = ngx_http_cache_get(ilcf->index_cache, NULL, + &r->uri, &crc); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, + "http index cache get: " PTR_FMT, ctx->cache); + + if (ctx->cache && !ctx->cache->expired) { + + ctx->cache->accessed = ngx_cached_time; + + ctx->redirect.len = ctx->cache->data.value.len; + ctx->redirect.data = ngx_palloc(r->pool, ctx->redirect.len + 1); + if (ctx->redirect.data == NULL) { + ngx_http_cache_unlock(ilcf->index_cache, ctx->cache, log); + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + ngx_memcpy(ctx->redirect.data, ctx->cache->data.value.data, + ctx->redirect.len + 1); + ngx_http_cache_unlock(ilcf->index_cache, ctx->cache, log); + + return ngx_http_internal_redirect(r, &ctx->redirect, NULL); + } + } + +#endif + +#if 0 + ctx->path.data = ngx_palloc(r->pool, clcf->root.len + r->uri.len + + ilcf->max_index_len + - clcf->alias * clcf->name.len); + if (ctx->path.data == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + ctx->redirect.data = ngx_cpymem(ctx->path.data, clcf->root.data, + clcf->root.len); +#endif + + if (clcf->alias) { + ctx->path.data = ngx_palloc(r->pool, clcf->root.len + + r->uri.len + 1 - clcf->name.len + + ilcf->max_index_len); + if (ctx->path.data == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + ctx->redirect.data = ngx_palloc(r->pool, r->uri.len + + ilcf->max_index_len); + if (ctx->redirect.data == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + ngx_memcpy(ctx->path.data, clcf->root.data, clcf->root.len); + + ctx->last = ngx_cpystrn(ctx->path.data + clcf->root.len, + r->uri.data + clcf->name.len, + r->uri.len + 1 - clcf->name.len); + +#if 0 + /* + * aliases usually have trailling "/", + * set it in the start of the possible redirect + */ + + if (*ctx->redirect.data != '/') { + ctx->redirect.data--; + } +#endif + + } else { + ctx->path.data = ngx_palloc(r->pool, clcf->root.len + r->uri.len + + ilcf->max_index_len); + if (ctx->path.data == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + ctx->redirect.data = ngx_cpymem(ctx->path.data, clcf->root.data, + clcf->root.len); + + ctx->last = ngx_cpystrn(ctx->redirect.data, r->uri.data, + r->uri.len + 1); + } + } + + ctx->path.len = ctx->last - ctx->path.data; + + index = ilcf->indices.elts; + for (/* void */; ctx->index < ilcf->indices.nelts; ctx->index++) { + + if (index[ctx->index].data[0] == '/') { + name = index[ctx->index].data; + + } else { + ngx_memcpy(ctx->last, index[ctx->index].data, + index[ctx->index].len + 1); + name = ctx->path.data; + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, + "open index \"%s\"", name); + + fd = ngx_open_file(name, NGX_FILE_RDONLY, NGX_FILE_OPEN); + + if (fd == (ngx_fd_t) NGX_AGAIN) { + return NGX_AGAIN; + } + + if (fd == NGX_INVALID_FILE) { + err = ngx_errno; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, err, + ngx_open_file_n " %s failed", name); + + if (err == NGX_ENOTDIR) { + return ngx_http_index_error(r, ctx, err); + + } else if (err == NGX_EACCES) { + return ngx_http_index_error(r, ctx, err); + } + + if (!ctx->tested) { + rc = ngx_http_index_test_dir(r, ctx); + + if (rc != NGX_OK) { + return rc; + } + + ctx->tested = 1; + } + + if (err == NGX_ENOENT) { + continue; + } + + ngx_log_error(NGX_LOG_ERR, log, err, + ngx_open_file_n " %s failed", name); + + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + + /* STUB: open file cache */ + + r->file.name.data = name; + r->file.fd = fd; + + if (index[ctx->index].data[0] == '/') { + r->file.name.len = index[ctx->index].len; + ctx->redirect.len = index[ctx->index].len; + ctx->redirect.data = index[ctx->index].data; + + } else { + if (clcf->alias) { + name = ngx_cpymem(ctx->redirect.data, r->uri.data, r->uri.len); + ngx_memcpy(name, index[ctx->index].data, + index[ctx->index].len + 1); + } + + ctx->redirect.len = r->uri.len + index[ctx->index].len; + r->file.name.len = clcf->root.len + r->uri.len + - clcf->alias * clcf->name.len + + index[ctx->index].len; + } + + /**/ + + +#if (NGX_HTTP_CACHE) + + if (ilcf->index_cache) { + + if (ctx->cache) { + if (ctx->redirect.len == ctx->cache->data.value.len + && ngx_memcmp(ctx->cache->data.value.data, + ctx->redirect.data, ctx->redirect.len) == 0) + { + ctx->cache->accessed = ngx_cached_time; + ctx->cache->updated = ngx_cached_time; + ngx_http_cache_unlock(ilcf->index_cache, ctx->cache, log); + + return ngx_http_internal_redirect(r, &ctx->redirect, NULL); + } + } + + ctx->redirect.len++; + ctx->cache = ngx_http_cache_alloc(ilcf->index_cache, ctx->cache, + NULL, &r->uri, crc, + &ctx->redirect, log); + ctx->redirect.len--; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, + "http index cache alloc: " PTR_FMT, ctx->cache); + + if (ctx->cache) { + ctx->cache->fd = NGX_INVALID_FILE; + ctx->cache->accessed = ngx_cached_time; + ctx->cache->last_modified = 0; + ctx->cache->updated = ngx_cached_time; + ctx->cache->memory = 1; + ngx_http_cache_unlock(ilcf->index_cache, ctx->cache, log); + } + } + +#endif + + return ngx_http_internal_redirect(r, &ctx->redirect, NULL); + } + + return NGX_DECLINED; +} + + +static ngx_int_t ngx_http_index_test_dir(ngx_http_request_t *r, + ngx_http_index_ctx_t *ctx) +{ + ngx_err_t err; + + ctx->path.data[ctx->path.len - 1] = '\0'; + ctx->path.data[ctx->path.len] = '\0'; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http check dir: \"%s\"", ctx->path.data); + + if (ngx_file_info(ctx->path.data, &r->file.info) == -1) { + + err = ngx_errno; + + if (err == NGX_ENOENT) { + ctx->path.data[ctx->path.len - 1] = '/'; + return ngx_http_index_error(r, ctx, err); + } + + ngx_log_error(NGX_LOG_CRIT, r->connection->log, err, + ngx_file_info_n " %s failed", ctx->path.data); + + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + ctx->path.data[ctx->path.len - 1] = '/'; + + if (ngx_is_dir(&r->file.info)) { + return NGX_OK; + } + + /* THINK: not reached ??? */ + return ngx_http_index_error(r, ctx, 0); +} + + +static ngx_int_t ngx_http_index_error(ngx_http_request_t *r, + ngx_http_index_ctx_t *ctx, ngx_err_t err) +{ + if (err == NGX_EACCES) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, err, + "\"%s\" is forbidden", ctx->path.data); + + return NGX_HTTP_FORBIDDEN; + } + + ngx_log_error(NGX_LOG_ERR, r->connection->log, err, + "\"%s\" is not found", ctx->path.data); + return NGX_HTTP_NOT_FOUND; +} + + +static ngx_int_t ngx_http_index_init(ngx_cycle_t *cycle) +{ + ngx_http_handler_pt *h; + ngx_http_core_main_conf_t *cmcf; + + cmcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_core_module); + + h = ngx_push_array(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers); + if (h == NULL) { + return NGX_ERROR; + } + + *h = ngx_http_index_handler; + + return NGX_OK; +} + + +static void *ngx_http_index_create_loc_conf(ngx_conf_t *cf) +{ + ngx_http_index_loc_conf_t *conf; + + ngx_test_null(conf, ngx_palloc(cf->pool, sizeof(ngx_http_index_loc_conf_t)), + NGX_CONF_ERROR); + + ngx_init_array(conf->indices, cf->pool, 3, sizeof(ngx_str_t), + NGX_CONF_ERROR); + conf->max_index_len = 0; + + conf->index_cache = NULL; + + return conf; +} + + +/* TODO: remove duplicate indices */ + +static char *ngx_http_index_merge_loc_conf(ngx_conf_t *cf, + void *parent, void *child) +{ + ngx_http_index_loc_conf_t *prev = parent; + ngx_http_index_loc_conf_t *conf = child; + + ngx_uint_t i; + ngx_str_t *index, *prev_index; + + if (conf->max_index_len == 0) { + if (prev->max_index_len != 0) { + ngx_memcpy(conf, prev, sizeof(ngx_http_index_loc_conf_t)); + return NGX_CONF_OK; + } + + ngx_test_null(index, ngx_push_array(&conf->indices), NGX_CONF_ERROR); + index->len = sizeof(NGX_HTTP_DEFAULT_INDEX) - 1; + index->data = (u_char *) NGX_HTTP_DEFAULT_INDEX; + conf->max_index_len = sizeof(NGX_HTTP_DEFAULT_INDEX); + + return NGX_CONF_OK; + } + + if (prev->max_index_len != 0) { + + prev_index = prev->indices.elts; + for (i = 0; i < prev->indices.nelts; i++) { + ngx_test_null(index, ngx_push_array(&conf->indices), + NGX_CONF_ERROR); + index->len = prev_index[i].len; + index->data = prev_index[i].data; + } + } + + if (conf->max_index_len < prev->max_index_len) { + conf->max_index_len = prev->max_index_len; + } + + if (conf->index_cache == NULL) { + conf->index_cache = prev->index_cache; + } + + return NGX_CONF_OK; +} + + +/* TODO: warn about duplicate indices */ + +static char *ngx_http_index_set_index(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf) +{ + ngx_http_index_loc_conf_t *ilcf = conf; + + ngx_uint_t i; + ngx_str_t *index, *value; + + value = cf->args->elts; + + if (value[1].data[0] == '/' && ilcf->indices.nelts == 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "first index \"%s\" in \"%s\" directive " + "must not be absolute", + value[1].data, cmd->name.data); + return NGX_CONF_ERROR; + } + + for (i = 1; i < cf->args->nelts; i++) { + if (value[i].len == 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "index \"%s\" in \"%s\" directive is invalid", + value[1].data, cmd->name.data); + return NGX_CONF_ERROR; + } + + ngx_test_null(index, ngx_push_array(&ilcf->indices), NGX_CONF_ERROR); + index->len = value[i].len; + index->data = value[i].data; + + if (ilcf->max_index_len < index->len + 1) { + ilcf->max_index_len = index->len + 1; + } + } + + return NGX_CONF_OK; +} diff --git a/src/http/modules/ngx_http_not_modified_filter.c b/src/http/modules/ngx_http_not_modified_filter.c new file mode 100644 --- /dev/null +++ b/src/http/modules/ngx_http_not_modified_filter.c @@ -0,0 +1,85 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include + + + +static ngx_int_t ngx_http_not_modified_filter_init(ngx_cycle_t *cycle); + + +static ngx_http_module_t ngx_http_not_modified_filter_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_not_modified_filter_module = { + NGX_MODULE, + &ngx_http_not_modified_filter_module_ctx, /* module context */ + NULL, /* module directives */ + NGX_HTTP_MODULE, /* module type */ + ngx_http_not_modified_filter_init, /* init module */ + NULL /* init child */ +}; + + +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) +{ + time_t ims; + + if (r->headers_out.status != NGX_HTTP_OK + || r->headers_in.if_modified_since == NULL + || r->headers_out.last_modified_time == -1) + { + 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->key.len = 0; + r->headers_out.content_type = NULL; + r->headers_out.content_length_n = -1; + r->headers_out.content_length = NULL; +#if 0 + r->headers_out.accept_ranges->key.len = 0; +#endif + } + + return ngx_http_next_header_filter(r); +} + + +static ngx_int_t ngx_http_not_modified_filter_init(ngx_cycle_t *cycle) +{ + ngx_http_next_header_filter = ngx_http_top_header_filter; + ngx_http_top_header_filter = ngx_http_not_modified_header_filter; + + return NGX_OK; +} diff --git a/src/http/modules/ngx_http_range_filter.c b/src/http/modules/ngx_http_range_filter.c new file mode 100644 --- /dev/null +++ b/src/http/modules/ngx_http_range_filter.c @@ -0,0 +1,523 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include + + +/* + * the single part format: + * + * "HTTP/1.0 206 Partial Content" CRLF + * ... header ... + * "Content-Type: image/jpeg" CRLF + * "Content-Length: SIZE" CRLF + * "Content-Range: bytes START-END/SIZE" CRLF + * CRLF + * ... data ... + * + * + * the mutlipart format: + * + * "HTTP/1.0 206 Partial Content" CRLF + * ... header ... + * "Content-Type: multipart/byteranges; boundary=0123456789" CRLF + * CRLF + * CRLF + * "--0123456789" CRLF + * "Content-Type: image/jpeg" CRLF + * "Content-Range: bytes START0-END0/SIZE" CRLF + * CRLF + * ... data ... + * CRLF + * "--0123456789" CRLF + * "Content-Type: image/jpeg" CRLF + * "Content-Range: bytes START1-END1/SIZE" CRLF + * CRLF + * ... data ... + * CRLF + * "--0123456789--" CRLF + */ + + +typedef struct { + ngx_str_t boundary_header; +} ngx_http_range_filter_ctx_t; + + +static ngx_int_t ngx_http_range_header_filter_init(ngx_cycle_t *cycle); +static ngx_int_t ngx_http_range_body_filter_init(ngx_cycle_t *cycle); + + +static ngx_http_module_t ngx_http_range_header_filter_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_range_header_filter_module = { + NGX_MODULE, + &ngx_http_range_header_filter_module_ctx, /* module context */ + NULL, /* module directives */ + NGX_HTTP_MODULE, /* module type */ + ngx_http_range_header_filter_init, /* init module */ + NULL /* init child */ +}; + + +static ngx_http_module_t ngx_http_range_body_filter_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_range_body_filter_module = { + NGX_MODULE, + &ngx_http_range_body_filter_module_ctx, /* module context */ + NULL, /* module directives */ + NGX_HTTP_MODULE, /* module type */ + ngx_http_range_body_filter_init, /* init module */ + NULL /* init child */ +}; + + +static ngx_http_output_header_filter_pt ngx_http_next_header_filter; +static ngx_http_output_body_filter_pt ngx_http_next_body_filter; + + +static ngx_int_t ngx_http_range_header_filter(ngx_http_request_t *r) +{ + ngx_int_t rc; + ngx_uint_t boundary, suffix, i; + u_char *p; + size_t len; + off_t start, end; + ngx_http_range_t *range; + ngx_http_range_filter_ctx_t *ctx; + + if (r->http_version < NGX_HTTP_VERSION_10 + || r->headers_out.status != NGX_HTTP_OK + || r->headers_out.content_length_n == -1 + || !r->filter_allow_ranges) + { + return ngx_http_next_header_filter(r); + } + + if (r->headers_in.range == NULL + || r->headers_in.range->value.len < 7 + || ngx_strncasecmp(r->headers_in.range->value.data, "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; + } + + 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"; + + return ngx_http_next_header_filter(r); + } + + ngx_init_array(r->headers_out.ranges, r->pool, 5, sizeof(ngx_http_range_t), + NGX_ERROR); + + rc = 0; + range = NULL; + p = r->headers_in.range->value.data + 6; + + for ( ;; ) { + start = 0; + end = 0; + suffix = 0; + + while (*p == ' ') { p++; } + + if (*p != '-') { + if (*p < '0' || *p > '9') { + rc = NGX_HTTP_RANGE_NOT_SATISFIABLE; + break; + } + + while (*p >= '0' && *p <= '9') { + start = start * 10 + *p++ - '0'; + } + + while (*p == ' ') { p++; } + + if (*p++ != '-') { + rc = NGX_HTTP_RANGE_NOT_SATISFIABLE; + break; + } + + if (start >= r->headers_out.content_length_n) { + rc = NGX_HTTP_RANGE_NOT_SATISFIABLE; + break; + } + + while (*p == ' ') { p++; } + + if (*p == ',' || *p == '\0') { + ngx_test_null(range, ngx_push_array(&r->headers_out.ranges), + NGX_ERROR); + range->start = start; + range->end = r->headers_out.content_length_n; + + if (*p++ != ',') { + break; + } + + continue; + } + + } else { + suffix = 1; + p++; + } + + if (*p < '0' || *p > '9') { + rc = NGX_HTTP_RANGE_NOT_SATISFIABLE; + break; + } + + while (*p >= '0' && *p <= '9') { + end = end * 10 + *p++ - '0'; + } + + while (*p == ' ') { p++; } + + if (*p != ',' && *p != '\0') { + rc = NGX_HTTP_RANGE_NOT_SATISFIABLE; + break; + } + + if (suffix) { + start = r->headers_out.content_length_n - end; + end = r->headers_out.content_length_n - 1; + } + + if (start > end) { + rc = NGX_HTTP_RANGE_NOT_SATISFIABLE; + break; + } + + ngx_test_null(range, ngx_push_array(&r->headers_out.ranges), NGX_ERROR); + range->start = start; + + if (end >= r->headers_out.content_length_n) { + /* + * Download Accelerator sends the last byte position + * that equals to the file length + */ + range->end = r->headers_out.content_length_n; + + } else { + range->end = end + 1; + } + + if (*p++ != ',') { + break; + } + } + + if (rc) { + + /* rc == NGX_HTTP_RANGE_NOT_SATISFIABLE */ + + r->headers_out.status = rc; + r->headers_out.ranges.nelts = 0; + + r->headers_out.content_range = ngx_list_push(&r->headers_out.headers); + if (r->headers_out.content_range == NULL) { + return NGX_ERROR; + } + + r->headers_out.content_range->key.len = sizeof("Content-Range") - 1; + r->headers_out.content_range->key.data = (u_char *) "Content-Range"; + + r->headers_out.content_range->value.data = + ngx_palloc(r->pool, 8 + 20 + 1); + if (r->headers_out.content_range->value.data == NULL) { + return NGX_ERROR; + } + + r->headers_out.content_range->value.len = + ngx_snprintf((char *) r->headers_out.content_range->value.data, + 8 + 20 + 1, "bytes */" OFF_T_FMT, + r->headers_out.content_length_n); + + r->headers_out.content_length_n = -1; + if (r->headers_out.content_length) { + r->headers_out.content_length->key.len = 0; + r->headers_out.content_length = NULL; + } + + return rc; + + } else { + r->headers_out.status = NGX_HTTP_PARTIAL_CONTENT; + + if (r->headers_out.ranges.nelts == 1) { + + r->headers_out.content_range = + ngx_list_push(&r->headers_out.headers); + if (r->headers_out.content_range == NULL) { + return NGX_ERROR; + } + + r->headers_out.content_range->key.len = sizeof("Content-Range") - 1; + r->headers_out.content_range->key.data = (u_char *) "Content-Range"; + + ngx_test_null(r->headers_out.content_range->value.data, + ngx_palloc(r->pool, 6 + 20 + 1 + 20 + 1 + 20 + 1), + NGX_ERROR); + + /* "Content-Range: bytes SSSS-EEEE/TTTT" header */ + + r->headers_out.content_range->value.len = + ngx_snprintf((char *) + r->headers_out.content_range->value.data, + 6 + 20 + 1 + 20 + 1 + 20 + 1, + "bytes " OFF_T_FMT "-" OFF_T_FMT "/" OFF_T_FMT, + range->start, range->end - 1, + r->headers_out.content_length_n); + + r->headers_out.content_length_n = range->end - range->start; + + } else { + +#if 0 + /* TODO: what if no content_type ?? */ + + if (!(r->headers_out.content_type = + ngx_http_add_header(&r->headers_out, ngx_http_headers_out))) + { + return NGX_ERROR; + } +#endif + + ngx_http_create_ctx(r, ctx, ngx_http_range_body_filter_module, + sizeof(ngx_http_range_filter_ctx_t), NGX_ERROR); + + len = 4 + 10 + 2 + 14 + r->headers_out.content_type->value.len + + 2 + 21 + 1; + + if (r->headers_out.charset.len) { + len += 10 + r->headers_out.charset.len; + } + + ngx_test_null(ctx->boundary_header.data, ngx_palloc(r->pool, len), + NGX_ERROR); + + boundary = ngx_next_temp_number(0); + + /* + * The boundary header of the range: + * CRLF + * "--0123456789" CRLF + * "Content-Type: image/jpeg" CRLF + * "Content-Range: bytes " + */ + + if (r->headers_out.charset.len) { + ctx->boundary_header.len = + ngx_snprintf((char *) ctx->boundary_header.data, len, + CRLF "--%010" NGX_UINT_T_FMT CRLF + "Content-Type: %s; charset=%s" CRLF + "Content-Range: bytes ", + boundary, + r->headers_out.content_type->value.data, + r->headers_out.charset.data); + + r->headers_out.charset.len = 0; + + } else { + ctx->boundary_header.len = + ngx_snprintf((char *) ctx->boundary_header.data, len, + CRLF "--%010" NGX_UINT_T_FMT CRLF + "Content-Type: %s" CRLF + "Content-Range: bytes ", + boundary, + r->headers_out.content_type->value.data); + } + + ngx_test_null(r->headers_out.content_type->value.data, + ngx_palloc(r->pool, 31 + 10 + 1), + NGX_ERROR); + + /* "Content-Type: multipart/byteranges; boundary=0123456789" */ + + r->headers_out.content_type->value.len = + ngx_snprintf((char *) + r->headers_out.content_type->value.data, + 31 + 10 + 1, + "multipart/byteranges; boundary=%010" + NGX_UINT_T_FMT, + boundary); + + /* the size of the last boundary CRLF "--0123456789--" CRLF */ + len = 4 + 10 + 4; + + range = r->headers_out.ranges.elts; + for (i = 0; i < r->headers_out.ranges.nelts; i++) { + ngx_test_null(range[i].content_range.data, + ngx_palloc(r->pool, 20 + 1 + 20 + 1 + 20 + 5), + NGX_ERROR); + + /* the size of the range: "SSSS-EEEE/TTTT" CRLF CRLF */ + + range[i].content_range.len = + ngx_snprintf((char *) range[i].content_range.data, + 20 + 1 + 20 + 1 + 20 + 5, + OFF_T_FMT "-" OFF_T_FMT "/" OFF_T_FMT CRLF CRLF, + range[i].start, range[i].end - 1, + r->headers_out.content_length_n); + + len += ctx->boundary_header.len + range[i].content_range.len + + (size_t) (range[i].end - range[i].start); + } + + r->headers_out.content_length_n = len; + r->headers_out.content_length = NULL; + } + } + + return ngx_http_next_header_filter(r); +} + + +static ngx_int_t ngx_http_range_body_filter(ngx_http_request_t *r, + ngx_chain_t *in) +{ + ngx_uint_t i; + ngx_buf_t *b; + ngx_chain_t *out, *hcl, *rcl, *dcl, **ll; + ngx_http_range_t *range; + ngx_http_range_filter_ctx_t *ctx; + + if (r->headers_out.ranges.nelts == 0) { + return ngx_http_next_body_filter(r, in); + } + + /* + * the optimized version for the static files only + * that are passed in the single file buf + */ + + if (in && in->buf->in_file && in->buf->last_buf) { + range = r->headers_out.ranges.elts; + + if (r->headers_out.ranges.nelts == 1) { + in->buf->file_pos = range->start; + in->buf->file_last = range->end; + + return ngx_http_next_body_filter(r, in); + } + + ctx = ngx_http_get_module_ctx(r, ngx_http_range_body_filter_module); + ll = &out; + + for (i = 0; i < r->headers_out.ranges.nelts; i++) { + + /* + * The boundary header of the range: + * CRLF + * "--0123456789" CRLF + * "Content-Type: image/jpeg" CRLF + * "Content-Range: bytes " + */ + + ngx_test_null(b, ngx_calloc_buf(r->pool), NGX_ERROR); + b->memory = 1; + b->pos = ctx->boundary_header.data; + b->last = ctx->boundary_header.data + ctx->boundary_header.len; + + ngx_test_null(hcl, ngx_alloc_chain_link(r->pool), NGX_ERROR); + hcl->buf = b; + + /* "SSSS-EEEE/TTTT" CRLF CRLF */ + + ngx_test_null(b, ngx_calloc_buf(r->pool), NGX_ERROR); + b->temporary = 1; + b->pos = range[i].content_range.data; + b->last = range[i].content_range.data + range[i].content_range.len; + + ngx_test_null(rcl, ngx_alloc_chain_link(r->pool), NGX_ERROR); + rcl->buf = b; + + /* the range data */ + + ngx_test_null(b, ngx_calloc_buf(r->pool), NGX_ERROR); + b->in_file = 1; + b->file_pos = range[i].start; + b->file_last = range[i].end; + b->file = in->buf->file; + + ngx_alloc_link_and_set_buf(dcl, b, r->pool, NGX_ERROR); + + *ll = hcl; + hcl->next = rcl; + rcl->next = dcl; + ll = &dcl->next; + } + + /* the last boundary CRLF "--0123456789--" CRLF */ + + ngx_test_null(b, ngx_calloc_buf(r->pool), NGX_ERROR); + b->temporary = 1; + b->last_buf = 1; + ngx_test_null(b->pos, ngx_palloc(r->pool, 4 + 10 + 4), NGX_ERROR); + b->last = ngx_cpymem(b->pos, ctx->boundary_header.data, 4 + 10); + *b->last++ = '-'; *b->last++ = '-'; + *b->last++ = CR; *b->last++ = LF; + + ngx_alloc_link_and_set_buf(hcl, b, r->pool, NGX_ERROR); + *ll = hcl; + + return ngx_http_next_body_filter(r, out); + } + + /* TODO: alert */ + + return ngx_http_next_body_filter(r, in); +} + + +static ngx_int_t ngx_http_range_header_filter_init(ngx_cycle_t *cycle) +{ + ngx_http_next_header_filter = ngx_http_top_header_filter; + ngx_http_top_header_filter = ngx_http_range_header_filter; + + return NGX_OK; +} + + +static ngx_int_t ngx_http_range_body_filter_init(ngx_cycle_t *cycle) +{ + ngx_http_next_body_filter = ngx_http_top_body_filter; + ngx_http_top_body_filter = ngx_http_range_body_filter; + + return NGX_OK; +} diff --git a/src/http/modules/ngx_http_rewrite_handler.c b/src/http/modules/ngx_http_rewrite_handler.c new file mode 100644 --- /dev/null +++ b/src/http/modules/ngx_http_rewrite_handler.c @@ -0,0 +1,466 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include + + +#define NGX_HTTP_REWRITE_COPY_MATCH 0 +#define NGX_HTTP_REWRITE_COPY_SHORT 1 +#define NGX_HTTP_REWRITE_COPY_LONG 2 + + +typedef struct { + ngx_int_t op; + size_t len; + uintptr_t data; +} ngx_http_rewrite_op_t; + + +typedef struct { + ngx_regex_t *regex; + ngx_uint_t msize; + + ngx_array_t ops; + ngx_uint_t size; + + ngx_str_t re_name; + ngx_str_t s_name; + + ngx_uint_t status; + unsigned last:1; +} ngx_http_rewrite_rule_t; + + +typedef struct { + ngx_array_t rules; + ngx_flag_t log; +} ngx_http_rewrite_srv_conf_t; + + +typedef struct { + ngx_str_t redirect; +} ngx_http_rewrite_loc_conf_t; + + +static void *ngx_http_rewrite_create_srv_conf(ngx_conf_t *cf); +static char *ngx_http_rewrite_merge_srv_conf(ngx_conf_t *cf, + void *parent, void *child); +static void *ngx_http_rewrite_create_loc_conf(ngx_conf_t *cf); +static char *ngx_http_rewrite_rule(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static char *ngx_http_redirect(ngx_conf_t *cf, void *post, void *data); +static ngx_int_t ngx_http_rewrite_init(ngx_cycle_t *cycle); + + +static ngx_conf_post_handler_pt ngx_http_redirect_p = ngx_http_redirect; + + +static ngx_command_t ngx_http_rewrite_commands[] = { + + { ngx_string("rewrite"), + NGX_HTTP_SRV_CONF|NGX_CONF_TAKE23, + ngx_http_rewrite_rule, + NGX_HTTP_SRV_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("redirect"), + NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_conf_set_str_slot, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + &ngx_http_redirect_p }, + + { ngx_string("rewrite_log"), + NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_flag_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_http_rewrite_srv_conf_t, log), + NULL }, + + ngx_null_command +}; + + +ngx_http_module_t ngx_http_rewrite_module_ctx = { + NULL, /* pre conf */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + ngx_http_rewrite_create_srv_conf, /* create server configuration */ + ngx_http_rewrite_merge_srv_conf, /* merge server configuration */ + + ngx_http_rewrite_create_loc_conf, /* create location configration */ + NULL, /* merge location configration */ +}; + + +ngx_module_t ngx_http_rewrite_module = { + NGX_MODULE, + &ngx_http_rewrite_module_ctx, /* module context */ + ngx_http_rewrite_commands, /* module directives */ + NGX_HTTP_MODULE, /* module type */ + ngx_http_rewrite_init, /* init module */ + NULL /* init child */ +}; + + +static ngx_int_t ngx_http_rewrite_handler(ngx_http_request_t *r) +{ + int *matches; + u_char *p; + size_t len; + uintptr_t data; + ngx_int_t rc; + ngx_uint_t i, m, n; + ngx_str_t uri; + ngx_http_rewrite_op_t *op; + ngx_http_rewrite_rule_t *rule; + ngx_http_rewrite_srv_conf_t *scf; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http rewrite handler"); + + scf = ngx_http_get_module_srv_conf(r, ngx_http_rewrite_module); + + rule = scf->rules.elts; + for (i = 0; i < scf->rules.nelts; i++) { + + if (rule[i].msize) { + if (!(matches = ngx_palloc(r->pool, rule[i].msize * sizeof(int)))) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + } else { + matches = NULL; + } + + rc = ngx_regex_exec(rule[i].regex, &r->uri, matches, rule[i].msize); + + if (rc == NGX_DECLINED) { + if (scf->log) { + ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0, + "\"%s\" does not match \"%s\"", + rule[i].re_name.data, r->uri.data); + } + + continue; + } + + if (rc < 0) { + ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, + ngx_regex_exec_n + " failed: %d on \"%s\" using \"%s\"", + rc, r->uri.data, rule[i].re_name.data); + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + if (scf->log) { + ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0, + "\"%s\" matches \"%s\"", + rule[i].re_name.data, r->uri.data); + } + + if (rule[i].status) { + return rule[i].status; + } + + uri.len = rule[i].size; + + for (n = 1; n < (ngx_uint_t) rc; n++) { + uri.len += matches[2 * n + 1] - matches[2 * n]; + } + + if (!(uri.data = ngx_palloc(r->pool, uri.len + 1))) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + p = uri.data; + + op = rule[i].ops.elts; + for (n = 0; n < rule[i].ops.nelts; n++) { + if (op[n].op == NGX_HTTP_REWRITE_COPY_SHORT) { + len = op[n].len; + data = op[n].data; + while (len--) { + *p++ = (char) (data & 0xff); + data >>= 8; + } + + } else if (op[n].op == NGX_HTTP_REWRITE_COPY_LONG) { + p = ngx_cpymem(p, (void *) op[n].data, op[n].len); + + } else { /* NGX_HTTP_REWRITE_COPY_MATCH */ + m = 2 * op[n].data; + p = ngx_cpymem(p, &r->uri.data[matches[m]], + matches[m + 1] - matches[m]); + } + } + + *p = '\0'; + + if (scf->log) { + ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0, + "rewritten uri: \"%s\"", uri.data); + } + + r->uri = uri; + + if (ngx_http_set_exten(r) != NGX_OK) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + if (rule[i].last) { + return NGX_DECLINED; + } + } + + return NGX_DECLINED; +} + + +static ngx_int_t ngx_http_redirect_handler(ngx_http_request_t *r) +{ + u_char *p; + ngx_http_rewrite_loc_conf_t *rlcf; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http redirect handler"); + + rlcf = ngx_http_get_module_loc_conf(r, ngx_http_rewrite_module); + + r->headers_out.location = ngx_list_push(&r->headers_out.headers); + if (r->headers_out.location == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + if (rlcf->redirect.data[0] != '/') { + r->headers_out.location->key.len = sizeof("Location") - 1; + r->headers_out.location->key.data = (u_char *) "Location"; + } + + r->headers_out.location->value.len = rlcf->redirect.len + + r->unparsed_uri.len; + r->headers_out.location->value.data = ngx_palloc(r->pool, + r->headers_out.location->value.len); + + if (r->headers_out.location->value.data == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + p = ngx_cpymem(r->headers_out.location->value.data, rlcf->redirect.data, + rlcf->redirect.len); + p = ngx_cpystrn(p, r->unparsed_uri.data + 1, r->unparsed_uri.len); + + return NGX_HTTP_MOVED_TEMPORARILY; +} + + +static void *ngx_http_rewrite_create_srv_conf(ngx_conf_t *cf) +{ + ngx_http_rewrite_srv_conf_t *conf; + + if (!(conf = ngx_palloc(cf->pool, sizeof(ngx_http_rewrite_srv_conf_t)))) { + return NGX_CONF_ERROR; + } + + ngx_init_array(conf->rules, cf->pool, 5, sizeof(ngx_http_rewrite_rule_t), + NGX_CONF_ERROR); + + conf->log = NGX_CONF_UNSET; + + return conf; +} + + +static char *ngx_http_rewrite_merge_srv_conf(ngx_conf_t *cf, + void *parent, void *child) +{ + ngx_http_rewrite_srv_conf_t *prev = parent; + ngx_http_rewrite_srv_conf_t *conf = child; + + ngx_conf_merge_value(conf->log, prev->log, 0); + + return NGX_CONF_OK; +} + + +static void *ngx_http_rewrite_create_loc_conf(ngx_conf_t *cf) +{ + ngx_http_rewrite_loc_conf_t *conf; + + if (!(conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_rewrite_loc_conf_t)))) { + return NGX_CONF_ERROR; + } + + return conf; +} + + +static char *ngx_http_rewrite_rule(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf) +{ + ngx_http_rewrite_srv_conf_t *scf = conf; + + u_char *data, *p; + size_t len; + ngx_str_t *value, err; + ngx_uint_t i; + ngx_http_rewrite_op_t *op; + ngx_http_rewrite_rule_t *rule; + u_char errstr[NGX_MAX_CONF_ERRSTR]; + + if (!(rule = ngx_push_array(&scf->rules))) { + return NGX_CONF_ERROR; + } + + ngx_init_array(rule->ops, cf->pool, 5, sizeof(ngx_http_rewrite_op_t), + NGX_CONF_ERROR); + + rule->msize = 0; + rule->size = 0; + rule->status = 0; + rule->last = 0; + + value = cf->args->elts; + + /* STUB */ { + err.len = NGX_MAX_CONF_ERRSTR; + err.data = errstr; + + rule->regex = ngx_regex_compile(&value[1], 0, cf->pool, &err); + + if (rule->regex == NULL) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%s", err.data); + return NGX_CONF_ERROR; + } + + rule->re_name = value[1]; + rule->s_name = value[2]; + + if (ngx_strcasecmp(value[2].data, "forbidden:") == 0) { + + if (cf->args->nelts == 3) { + rule->status = NGX_HTTP_FORBIDDEN; + rule->last = 1; + return NGX_CONF_OK; + } + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid parameter \"%s\"", value[3].data); + return NGX_CONF_ERROR; + } + + for (i = 0; i < value[2].len; /* void */) { + + if (!(op = ngx_push_array(&rule->ops))) { + return NGX_CONF_ERROR; + } + + data = &value[2].data[i]; + + if (value[2].data[i] == '$' + && i < value[2].len + && value[2].data[i + 1] >= '1' + && value[2].data[i + 1] <= '9') + { + op->op = NGX_HTTP_REWRITE_COPY_MATCH; + op->data = value[2].data[++i] - '0'; + + if (rule->msize < op->data) { + rule->msize = op->data; + } + + i++; + + } else { + i++; + + while (i < value[2].len && value[2].data[i] != '$') { + i++; + } + + len = &value[2].data[i] - data; + rule->size += len; + + if (len) { + + op->len = len; + + if (len <= sizeof(uintptr_t)) { + op->op = NGX_HTTP_REWRITE_COPY_SHORT; + op->data = 0; + + while (len--) { + op->data <<= 8; + op->data |= data[len]; + } + + } else { + op->op = NGX_HTTP_REWRITE_COPY_LONG; + + if (!(p = ngx_palloc(cf->pool, len))) { + return NGX_CONF_ERROR; + } + + ngx_memcpy(p, data, len); + op->data = (uintptr_t) p; + } + } + } + } + + if (rule->msize) { + rule->msize++; + rule->msize *= 3; + } + + if (cf->args->nelts > 3) { + if (ngx_strcmp(value[3].data, "last") == 0) { + rule->last = 1; + + } else { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid parameter \"%s\"", value[3].data); + return NGX_CONF_ERROR; + } + } + } + + return NGX_CONF_OK; +} + + +static char *ngx_http_redirect(ngx_conf_t *cf, void *post, void *data) +{ + ngx_http_core_loc_conf_t *clcf; + + clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); + clcf->handler = ngx_http_redirect_handler; + + return NGX_CONF_OK; +} + + +static ngx_int_t ngx_http_rewrite_init(ngx_cycle_t *cycle) +{ + ngx_http_handler_pt *h; + ngx_http_core_main_conf_t *cmcf; + + cmcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_core_module); + + h = ngx_push_array(&cmcf->phases[NGX_HTTP_REWRITE_PHASE].handlers); + if (h == NULL) { + return NGX_ERROR; + } + + *h = ngx_http_rewrite_handler; + + return NGX_OK; +} diff --git a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c new file mode 100644 --- /dev/null +++ b/src/http/modules/ngx_http_ssl_module.c @@ -0,0 +1,160 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include + + +#define NGX_DEFLAUT_CERTIFICATE "cert.pem" +#define NGX_DEFLAUT_CERTIFICATE_KEY "cert.pem" + + +static void *ngx_http_ssl_create_srv_conf(ngx_conf_t *cf); +static char *ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, + void *parent, void *child); + + +static ngx_command_t ngx_http_ssl_commands[] = { + + { ngx_string("ssl"), + NGX_HTTP_SRV_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_http_ssl_srv_conf_t, enable), + NULL }, + + { ngx_string("ssl_certificate"), + NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_str_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_http_ssl_srv_conf_t, certificate), + NULL }, + + { ngx_string("ssl_certificate_key"), + NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_str_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_http_ssl_srv_conf_t, certificate_key), + NULL }, + + ngx_null_command +}; + + +static ngx_http_module_t ngx_http_ssl_module_ctx = { + NULL, /* pre conf */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + ngx_http_ssl_create_srv_conf, /* create server configuration */ + ngx_http_ssl_merge_srv_conf, /* merge server configuration */ + + NULL, /* create location configuration */ + NULL, /* merge location configuration */ +}; + + +ngx_module_t ngx_http_ssl_module = { + NGX_MODULE, + &ngx_http_ssl_module_ctx, /* module context */ + ngx_http_ssl_commands, /* module directives */ + NGX_HTTP_MODULE, /* module type */ + NULL, /* init module */ + NULL /* init process */ +}; + + +static void *ngx_http_ssl_create_srv_conf(ngx_conf_t *cf) +{ + ngx_http_ssl_srv_conf_t *scf; + + if (!(scf = ngx_pcalloc(cf->pool, sizeof(ngx_http_ssl_srv_conf_t)))) { + return NGX_CONF_ERROR; + } + + scf->enable = NGX_CONF_UNSET; + + return scf; +} + + +static char *ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, + void *parent, void *child) +{ + ngx_http_ssl_srv_conf_t *prev = parent; + ngx_http_ssl_srv_conf_t *conf = child; + + ngx_conf_merge_value(conf->enable, prev->enable, 0); + + if (conf->enable == 0) { + return NGX_CONF_OK; + } + + ngx_conf_merge_str_value(conf->certificate, prev->certificate, + NGX_DEFLAUT_CERTIFICATE); + + ngx_conf_merge_str_value(conf->certificate_key, prev->certificate_key, + NGX_DEFLAUT_CERTIFICATE_KEY); + + /* TODO: configure methods */ + + conf->ssl_ctx = SSL_CTX_new(SSLv23_server_method()); + + if (conf->ssl_ctx == NULL) { + ngx_ssl_error(NGX_LOG_EMERG, cf->log, 0, "SSL_CTX_new() failed"); + return NGX_CONF_ERROR; + } + + if (SSL_CTX_use_certificate_file(conf->ssl_ctx, + (char *) conf->certificate.data, + SSL_FILETYPE_PEM) == 0) { + ngx_ssl_error(NGX_LOG_EMERG, cf->log, 0, + "SSL_CTX_use_certificate_file(\"%s\") failed", + conf->certificate.data); + return NGX_CONF_ERROR; + } + + if (SSL_CTX_use_PrivateKey_file(conf->ssl_ctx, + (char *) conf->certificate_key.data, + SSL_FILETYPE_PEM) == 0) { + ngx_ssl_error(NGX_LOG_EMERG, cf->log, 0, + "SSL_CTX_use_PrivateKey_file(\"%s\") failed", + conf->certificate_key.data); + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; +} + + +#if 0 + +static ngx_int_t ngx_http_ssl_init_process(ngx_cycle_t *cycle) +{ + ngx_uint_t i; + ngx_http_ssl_srv_conf_t *sscf; + ngx_http_core_srv_conf_t **cscfp; + ngx_http_core_main_conf_t *cmcf; + + cmcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_core_module); + + cscfp = cmcf->servers.elts; + + for (i = 0; i < cmcf->servers.nelts; i++) { + sscf = cscfp[i]->ctx->srv_conf[ngx_http_ssl_module.ctx_index]; + + if (sscf->enable) { + cscfp[i]->recv = ngx_ssl_recv; + cscfp[i]->send_chain = ngx_ssl_send_chain; + } + } + + return NGX_OK; +} + +#endif diff --git a/src/http/modules/ngx_http_ssl_module.h b/src/http/modules/ngx_http_ssl_module.h new file mode 100644 --- /dev/null +++ b/src/http/modules/ngx_http_ssl_module.h @@ -0,0 +1,36 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#ifndef _NGX_HTTP_SSL_H_INCLUDED_ +#define _NGX_HTTP_SSL_H_INCLUDED_ + + +#include +#include +#include + + +typedef struct { + ngx_flag_t enable; + ngx_str_t certificate; + ngx_str_t certificate_key; + + ngx_ssl_ctx_t *ssl_ctx; +} ngx_http_ssl_srv_conf_t; + + +ngx_int_t ngx_http_ssl_read(ngx_http_request_t *r, u_char *buf, size_t size); +ngx_int_t ngx_http_ssl_shutdown(ngx_http_request_t *r); +ngx_chain_t *ngx_http_ssl_write(ngx_connection_t *c, ngx_chain_t *in, + off_t limit); + +void ngx_http_ssl_close_connection(SSL *ssl, ngx_log_t *log); + + +extern ngx_module_t ngx_http_ssl_module; + + +#endif /* _NGX_HTTP_SSL_H_INCLUDED_ */ diff --git a/src/http/modules/ngx_http_static_handler.c b/src/http/modules/ngx_http_static_handler.c new file mode 100644 --- /dev/null +++ b/src/http/modules/ngx_http_static_handler.c @@ -0,0 +1,584 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#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_cycle_t *cycle); + + +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, /* pre conf */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + 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 */ +}; + + +ngx_module_t ngx_http_static_module = { + NGX_MODULE, + &ngx_http_static_module_ctx, /* module context */ + ngx_http_static_commands, /* module directives */ + NGX_HTTP_MODULE, /* module type */ + ngx_http_static_init, /* init module */ + NULL /* init child */ +}; + + +static ngx_int_t ngx_http_static_handler(ngx_http_request_t *r) +{ + u_char *last; + ngx_fd_t fd; + ngx_int_t rc; + ngx_uint_t level; + ngx_str_t name, location; + ngx_err_t err; + ngx_log_t *log; + ngx_buf_t *b; + ngx_chain_t out; + ngx_file_info_t fi; + ngx_http_cleanup_t *file_cleanup, *redirect_cleanup; + ngx_http_log_ctx_t *ctx; + ngx_http_core_loc_conf_t *clcf; + ngx_http_static_loc_conf_t *slcf; +#if (NGX_HTTP_CACHE) + uint32_t file_crc, redirect_crc; + ngx_http_cache_t *file, *redirect; +#endif + + if (r->uri.data[r->uri.len - 1] == '/') { + return NGX_DECLINED; + } + + if (r->method != NGX_HTTP_GET && r->method != NGX_HTTP_HEAD) { + return NGX_HTTP_NOT_ALLOWED; + } + + rc = ngx_http_discard_body(r); + + if (rc != NGX_OK && rc != NGX_AGAIN) { + return rc; + } + +#if (NGX_HTTP_CACHE) + + /* + * there is a valid cached open file, i.e by the index handler, + * and it should be already registered in r->cleanup + */ + + if (r->cache && !r->cache->expired) { + return ngx_http_send_cached(r); + } + +#endif + + log = r->connection->log; + + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + /* + * make a file name, reserve 2 bytes for a trailing '/' + * in a possible redirect and for the last '\0' + */ + + if (clcf->alias) { + name.data = ngx_palloc(r->pool, clcf->root.len + r->uri.len + 2 + - clcf->name.len); + if (name.data == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + last = ngx_cpymem(name.data, clcf->root.data, clcf->root.len); + last = ngx_cpystrn(last, r->uri.data + clcf->name.len, + r->uri.len + 1 - clcf->name.len); + + name.len = last - name.data; + + location.data = ngx_palloc(r->pool, r->uri.len + 2); + if (location.data == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + last = ngx_cpystrn(location.data, r->uri.data, r->uri.len + 1); + +#if 0 + /* + * aliases usually have trailling "/", + * set it in the start of the possible redirect + */ + + if (*location.data != '/') { + location.data--; + } +#endif + + location.len = last - location.data + 1; + + } else { + name.data = ngx_palloc(r->pool, clcf->root.len + r->uri.len + 2); + if (name.data == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + location.data = ngx_cpymem(name.data, clcf->root.data, clcf->root.len); + last = ngx_cpystrn(location.data, r->uri.data, r->uri.len + 1); + + name.len = last - name.data; + location.len = last - location.data + 1; + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, + "http filename: \"%s\"", name.data); + + + /* allocate cleanups */ + + if (!(file_cleanup = ngx_push_array(&r->cleanup))) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + file_cleanup->valid = 0; + + slcf = ngx_http_get_module_loc_conf(r, ngx_http_static_module); + if (slcf->redirect_cache) { + if (!(redirect_cleanup = ngx_push_array(&r->cleanup))) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + redirect_cleanup->valid = 0; + + } else { + redirect_cleanup = NULL; + } + +#if (NGX_HTTP_CACHE) + + /* look up an open files cache */ + + if (clcf->open_files) { + file = ngx_http_cache_get(clcf->open_files, file_cleanup, + &name, &file_crc); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, + "http open file cache get: " PTR_FMT, file); + + if (file && !file->expired) { + r->cache = file; + return ngx_http_send_cached(r); + } + + } else { + file = NULL; + } + + + /* look up an redirect cache */ + + if (slcf->redirect_cache) { + redirect = ngx_http_cache_get(slcf->redirect_cache, redirect_cleanup, + &name, &redirect_crc); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, + "http redirect cache get: " PTR_FMT, redirect); + + if (redirect && !redirect->expired) { + + /* + * We do not copy a cached value so the cache entry is locked + * until the end of the request. In a single threaded model + * the redirected request should complete before other event + * will be processed. In a multithreaded model this locking + * should keep more popular redirects in cache. + */ + + if (!(r->headers_out.location = + ngx_http_add_header(&r->headers_out, ngx_http_headers_out))) + { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + r->headers_out.location->value = redirect->data.value; + + return NGX_HTTP_MOVED_PERMANENTLY; + } + + } else { + redirect = NULL; + } + +#endif + + /* open file */ + +#if (WIN9X) + + /* TODO: redirect cache */ + + if (ngx_win32_version < NGX_WIN_NT) { + + /* + * there is no way to open a file or a directory in Win9X with + * one syscall because Win9X has no FILE_FLAG_BACKUP_SEMANTICS flag + * so we need to check its type before the opening + */ + + if (ngx_file_info(name.data, &fi) == NGX_FILE_ERROR) { + err = ngx_errno; + ngx_log_error(NGX_LOG_ERR, log, err, + ngx_file_info_n " \"%s\" failed", name.data); + + if (err == NGX_ENOENT || err == NGX_ENOTDIR) { + return NGX_HTTP_NOT_FOUND; + + } else if (err == NGX_EACCES) { + return NGX_HTTP_FORBIDDEN; + + } else { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + } + + if (ngx_is_dir(&fi)) { + ngx_log_debug(log, "HTTP DIR: '%s'" _ name.data); + + if (!(r->headers_out.location = + ngx_http_add_header(&r->headers_out, ngx_http_headers_out))) + { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + *last++ = '/'; + *last = '\0'; + r->headers_out.location->value.len = last - location; + r->headers_out.location->value.data = location; + + return NGX_HTTP_MOVED_PERMANENTLY; + } + } + +#endif + + + fd = ngx_open_file(name.data, NGX_FILE_RDONLY, NGX_FILE_OPEN); + + if (fd == NGX_INVALID_FILE) { + err = ngx_errno; + + if (err == NGX_ENOENT || err == NGX_ENOTDIR) { + 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, log, err, + ngx_open_file_n " \"%s\" failed", name.data); + + return rc; + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, "http static fd: %d", fd); + + 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.data); + + if (ngx_close_file(fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + ngx_close_file_n " \"%s\" failed", name.data); + } + + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + if (ngx_is_dir(&fi)) { + + 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", name.data); + } + + *last++ = '/'; + *last = '\0'; + + 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->value = location; + +#if (NGX_HTTP_CACHE) + + if (slcf->redirect_cache) { + if (redirect) { + if (location.len == redirect->data.value.len + && ngx_memcmp(redirect->data.value.data, location.data, + location.len) == 0) + { + redirect->accessed = ngx_cached_time; + redirect->updated = ngx_cached_time; + + /* + * we can unlock the cache entry because + * we have the local copy anyway + */ + + ngx_http_cache_unlock(slcf->redirect_cache, redirect, log); + redirect_cleanup->valid = 0; + + return NGX_HTTP_MOVED_PERMANENTLY; + } + } + + location.len++; + redirect = ngx_http_cache_alloc(slcf->redirect_cache, redirect, + redirect_cleanup, + &name, redirect_crc, + &location, log); + location.len--; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, + "http redirect cache alloc: " PTR_FMT, redirect); + + if (redirect) { + redirect->fd = NGX_INVALID_FILE; + redirect->accessed = ngx_cached_time; + redirect->last_modified = 0; + redirect->updated = ngx_cached_time; + redirect->memory = 1; + ngx_http_cache_unlock(slcf->redirect_cache, redirect, log); + redirect_cleanup->valid = 0; + } + + } + +#endif + + return NGX_HTTP_MOVED_PERMANENTLY; + } + +#if !(WIN32) /* the not regular files are probably Unix specific */ + + if (!ngx_is_file(&fi)) { + ngx_log_error(NGX_LOG_CRIT, log, ngx_errno, + "%s is not a regular file", name.data); + + if (ngx_close_file(fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + ngx_close_file_n " \"%s\" failed", name.data); + } + + return NGX_HTTP_NOT_FOUND; + } + +#endif + + +#if (NGX_HTTP_CACHE) + + if (clcf->open_files) { + +#if (NGX_USE_HTTP_FILE_CACHE_UNIQ) + + if (file && file->uniq == ngx_file_uniq(&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.data); + } + file->accessed = ngx_cached_time; + file->updated = ngx_cached_time; + file->expired = 0; + r->cache = file; + + return ngx_http_send_cached(r); + + } else { + if (file) { + ngx_http_cache_unlock(clcf->open_files, file, log); + file = NULL; + } + + file = ngx_http_cache_alloc(clcf->open_files, file, + file_cleanup, + &name, file_crc, NULL, log); + if (file) { + file->uniq = ngx_file_uniq(&fi); + } + } + +#else + file = ngx_http_cache_alloc(clcf->open_files, file, + file_cleanup, + &name, file_crc, NULL, log); +#endif + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, + "http open file cache alloc: " PTR_FMT, file); + + if (file) { + file->fd = fd; + file->data.size = ngx_file_size(&fi); + file->accessed = ngx_cached_time; + file->last_modified = ngx_file_mtime(&fi); + file->updated = ngx_cached_time; + r->cache = file; + } + + return ngx_http_send_cached(r); + } + +#endif + + ctx = log->data; + ctx->action = "sending response to client"; + + file_cleanup->data.file.fd = fd; + file_cleanup->data.file.name = name.data; + file_cleanup->valid = 1; + file_cleanup->cache = 0; + + 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); + + if (r->headers_out.content_length_n == 0) { + r->header_only = 1; + } + + if (ngx_http_set_content_type(r) != NGX_OK) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + +#if (NGX_SUPPRESS_WARN) + b = NULL; +#endif + + if (!r->header_only) { + /* we need to allocate all before the header would be sent */ + + if (!(b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t)))) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + if (!(b->file = ngx_pcalloc(r->pool, sizeof(ngx_file_t)))) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + r->filter_allow_ranges = 1; + } + + rc = ngx_http_send_header(r); + + if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) { + return rc; + } + + b->in_file = 1; + + if (!r->main) { + b->last_buf = 1; + } + + b->file_pos = 0; + b->file_last = ngx_file_size(&fi); + + b->file->fd = fd; + b->file->log = log; + + out.buf = b; + out.next = NULL; + + return ngx_http_output_filter(r, &out); +} + + +static void *ngx_http_static_create_loc_conf(ngx_conf_t *cf) +{ + ngx_http_static_loc_conf_t *conf; + + if (!(conf = ngx_palloc(cf->pool, sizeof(ngx_http_static_loc_conf_t)))) { + 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_cycle_t *cycle) +{ + ngx_http_handler_pt *h; + ngx_http_core_main_conf_t *cmcf; + + cmcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_core_module); + + h = ngx_push_array(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers); + if (h == NULL) { + return NGX_ERROR; + } + + *h = ngx_http_static_handler; + + return NGX_OK; +} diff --git a/src/http/modules/ngx_http_userid_filter.c b/src/http/modules/ngx_http_userid_filter.c new file mode 100644 --- /dev/null +++ b/src/http/modules/ngx_http_userid_filter.c @@ -0,0 +1,588 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include + + +#define NGX_HTTP_USERID_OFF 0 +#define NGX_HTTP_USERID_LOG 1 +#define NGX_HTTP_USERID_V1 2 +#define NGX_HTTP_USERID_ON 3 + +/* 31 Dec 2037 23:55:55 GMT */ +#define NGX_HTTP_USERID_MAX_EXPIRES 2145916555 + + +typedef struct { + ngx_flag_t enable; + + ngx_int_t service; + + ngx_str_t name; + ngx_str_t domain; + ngx_str_t path; + time_t expires; + + ngx_int_t p3p; + ngx_str_t p3p_string; +} ngx_http_userid_conf_t; + + +typedef struct { + uint32_t uid_got[4]; + uint32_t uid_set[4]; +} ngx_http_userid_ctx_t; + + +static ngx_int_t ngx_http_userid_get_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_set_uid(ngx_http_request_t *r, + ngx_http_userid_ctx_t *ctx, + ngx_http_userid_conf_t *conf); + +static u_char *ngx_http_userid_log_uid_got(ngx_http_request_t *r, u_char *buf, + uintptr_t data); +static u_char *ngx_http_userid_log_uid_set(ngx_http_request_t *r, u_char *buf, + uintptr_t data); + +static ngx_int_t ngx_http_userid_init(ngx_cycle_t *cycle); +static ngx_int_t ngx_http_userid_pre_conf(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, + void *child); +char *ngx_conf_check_domain(ngx_conf_t *cf, void *post, void *data); +char *ngx_http_userid_expires(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); + + +static uint32_t sequencer_v1 = 1; +static uint32_t sequencer_v2 = 0x03030302; + + +static u_char expires[] = "; expires=Thu, 31-Dec-37 23:55:55 GMT"; + + +static ngx_http_output_header_filter_pt ngx_http_next_header_filter; + + +static ngx_conf_enum_t ngx_http_userid_state[] = { + { ngx_string("off"), NGX_HTTP_USERID_OFF }, + { ngx_string("log"), NGX_HTTP_USERID_LOG }, + { ngx_string("v1"), NGX_HTTP_USERID_V1 }, + { ngx_string("on"), NGX_HTTP_USERID_ON }, + { ngx_null_string, 0 } +}; + + +static ngx_conf_post_handler_pt ngx_conf_check_domain_p = + ngx_conf_check_domain; + + +static ngx_command_t ngx_http_userid_commands[] = { + + { ngx_string("userid"), + 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_userid_conf_t, enable), + ngx_http_userid_state }, + + { ngx_string("userid_service"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, + ngx_conf_set_num_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_userid_conf_t, service), + NULL }, + + { ngx_string("userid_name"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, + ngx_conf_set_str_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_userid_conf_t, name), + NULL }, + + { ngx_string("userid_domain"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, + ngx_conf_set_str_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_userid_conf_t, domain), + &ngx_conf_check_domain_p }, + + { ngx_string("userid_path"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, + ngx_conf_set_str_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_userid_conf_t, path), + NULL }, + + { ngx_string("userid_expires"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_http_userid_expires, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + NULL }, + + ngx_null_command +}; + + +ngx_http_module_t ngx_http_userid_filter_module_ctx = { + ngx_http_userid_pre_conf, /* pre conf */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + NULL, /* create server configuration */ + NULL, /* merge server configuration */ + + ngx_http_userid_create_conf, /* create location configration */ + ngx_http_userid_merge_conf /* merge location configration */ +}; + + +ngx_module_t ngx_http_userid_filter_module = { + NGX_MODULE, + &ngx_http_userid_filter_module_ctx, /* module context */ + ngx_http_userid_commands, /* module directives */ + NGX_HTTP_MODULE, /* module type */ + ngx_http_userid_init, /* init module */ + NULL /* init process */ +}; + + +static ngx_http_log_op_name_t ngx_http_userid_log_fmt_ops[] = { + { ngx_string("uid_got"), 0, ngx_http_userid_log_uid_got }, + { ngx_string("uid_set"), 0, ngx_http_userid_log_uid_set }, + { ngx_null_string, 0, NULL } +}; + + +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; + + conf = ngx_http_get_module_loc_conf(r, ngx_http_userid_filter_module); + + if (conf->enable == NGX_HTTP_USERID_OFF) { + return ngx_http_next_header_filter(r); + } + + ngx_http_create_ctx(r, ctx, ngx_http_userid_filter_module, + sizeof(ngx_http_userid_ctx_t), NGX_ERROR); + + rc = ngx_http_userid_get_uid(r, ctx, conf); + + if (rc != NGX_OK) { + return rc; + } + + if (conf->enable == NGX_HTTP_USERID_LOG || ctx->uid_got[3] != 0) { + return ngx_http_next_header_filter(r); + } + + rc = ngx_http_userid_set_uid(r, ctx, conf); + + if (rc != NGX_OK) { + return rc; + } + + return ngx_http_next_header_filter(r); +} + + +static ngx_int_t ngx_http_userid_get_uid(ngx_http_request_t *r, + ngx_http_userid_ctx_t *ctx, + ngx_http_userid_conf_t *conf) +{ + u_char *start, *last, *end; + ngx_uint_t i; + ngx_str_t src, dst; + ngx_table_elt_t **cookies; + + cookies = r->headers_in.cookies.elts; + + for (i = 0; i < r->headers_in.cookies.nelts; i++) { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "cookie: \"%s\"", cookies[i]->value.data); + + end = cookies[i]->value.data + cookies[i]->value.len; + + for (start = cookies[i]->value.data; start < end; /* void */) { + + if (conf->name.len >= cookies[i]->value.len + || ngx_strncmp(start, conf->name.data, conf->name.len) != 0) + { + start += conf->name.len; + while (start < end && *start++ != ';') { /* void */ } + + for (/* void */; start < end && *start == ' '; start++) { /**/ } + + continue; + } + + for (start += conf->name.len; start < end && *start == ' '; start++) + { + /* void */ + } + + if (*start != '=') { + break; + } + + for (start++; start < end && *start == ' '; start++) { /* void */ } + + for (last = start; last < end && *last != ';'; last++) { /**/ } + + if (last - start < 22) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "client sent too short userid cookie \"%s\"", + cookies[i]->value.data); + break; + } + + /* + * we have to limit encoded string to 22 characters + * because there are already the millions cookies with a garbage + * instead of the correct base64 trail "==" + */ + + src.len = 22; + src.data = start; + dst.data = (u_char *) ctx->uid_got; + + if (ngx_decode_base64(&src, &dst) == NGX_ERROR) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "client sent invalid userid cookie \"%s\"", + cookies[i]->value.data); + break; + } + + ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "uid: %08X%08X%08X%08X", + ctx->uid_got[0], ctx->uid_got[1], + ctx->uid_got[2], ctx->uid_got[3]); + + return NGX_OK; + } + } + + return NGX_OK; +} + + +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) + +{ + u_char *cookie, *p; + size_t len; + socklen_t slen; + struct sockaddr_in addr_in; + ngx_str_t src, dst; + ngx_table_elt_t *set_cookie; + + /* TODO: mutex for sequencers */ + + if (conf->enable == NGX_HTTP_USERID_V1) { + if (conf->service == NGX_CONF_UNSET) { + ctx->uid_set[0] = 0; + } else { + ctx->uid_set[0] = htonl(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 *) &addr_in, &slen) == -1) + { + ngx_log_error(NGX_LOG_CRIT, r->connection->log, + ngx_socket_errno, + "getsockname() failed"); + } + + r->in_addr = addr_in.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; + } + } + + len = conf->name.len + 1 + ngx_base64_encoded_length(16) + 1; + + if (conf->expires) { + len += sizeof(expires) - 1 + 2; + } + + if (conf->domain.len > 1) { + len += sizeof("; domain=") - 1 + conf->domain.len; + } + + if (conf->path.len) { + len += sizeof("; path=") - 1 + conf->path.len; + } + + if (!(cookie = ngx_palloc(r->pool, len))) { + return NGX_ERROR; + } + + p = ngx_cpymem(cookie, conf->name.data, conf->name.len); + *p++ = '='; + + src.len = 16; + src.data = (u_char *) ctx->uid_set; + dst.data = p; + + ngx_encode_base64(&src, &dst); + + p += dst.len; + + if (conf->expires == NGX_HTTP_USERID_MAX_EXPIRES) { + p = ngx_cpymem(p, expires, sizeof(expires) - 1); + + } else if (conf->expires) { + p = ngx_cpymem(p, expires, sizeof("; expires=") - 1); + p += ngx_http_cookie_time(p, ngx_time() + conf->expires); + } + + if (conf->domain.len > 1) { + p = ngx_cpymem(p, "; domain=", sizeof("; domain=") - 1); + p = ngx_cpymem(p, conf->domain.data, conf->domain.len); + } + + if (conf->path.len) { + p = ngx_cpymem(p, "; path=", sizeof("; path=") - 1); + p = ngx_cpymem(p, conf->path.data, conf->path.len); + } + + *p = '\0'; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "uid cookie: \"%s\"", cookie); + + if (!(set_cookie = ngx_list_push(&r->headers_out.headers))) { + return NGX_ERROR; + } + + set_cookie->key.len = sizeof("Set-Cookie") - 1; + set_cookie->key.data = (u_char *) "Set-Cookie"; + set_cookie->value.len = p - cookie; + set_cookie->value.data = cookie; + + return NGX_OK; +} + + +static u_char *ngx_http_userid_log_uid_got(ngx_http_request_t *r, u_char *buf, + uintptr_t data) +{ + ngx_http_userid_ctx_t *ctx; + ngx_http_userid_conf_t *conf; + + ctx = ngx_http_get_module_ctx(r, ngx_http_userid_filter_module); + + if (ctx == NULL || ctx->uid_got[3] == 0) { + if (buf == NULL) { + return (u_char *) 1; + } + + *buf = '-'; + return buf + 1; + } + + conf = ngx_http_get_module_loc_conf(r, ngx_http_userid_filter_module); + + if (buf == NULL) { + return (u_char *) (conf->name.len + 1 + 32); + } + + buf = ngx_cpymem(buf, conf->name.data, conf->name.len); + + *buf++ = '='; + + return buf + ngx_snprintf((char *) buf, 33, "%08X%08X%08X%08X", + ctx->uid_got[0], ctx->uid_got[1], + ctx->uid_got[2], ctx->uid_got[3]); +} + + +static u_char *ngx_http_userid_log_uid_set(ngx_http_request_t *r, u_char *buf, + uintptr_t data) +{ + ngx_http_userid_ctx_t *ctx; + ngx_http_userid_conf_t *conf; + + ctx = ngx_http_get_module_ctx(r, ngx_http_userid_filter_module); + + if (ctx == NULL || ctx->uid_set[3] == 0) { + if (buf == NULL) { + return (u_char *) 1; + } + + *buf = '-'; + return buf + 1; + } + + conf = ngx_http_get_module_loc_conf(r, ngx_http_userid_filter_module); + + if (buf == NULL) { + return (u_char *) (conf->name.len + 1 + 32); + } + + buf = ngx_cpymem(buf, conf->name.data, conf->name.len); + + *buf++ = '='; + + return buf + ngx_snprintf((char *) buf, 33, "%08X%08X%08X%08X", + ctx->uid_set[0], ctx->uid_set[1], + ctx->uid_set[2], ctx->uid_set[3]); +} + + +static ngx_int_t ngx_http_userid_init(ngx_cycle_t *cycle) +{ + ngx_http_next_header_filter = ngx_http_top_header_filter; + ngx_http_top_header_filter = ngx_http_userid_filter; + + return NGX_OK; +} + + +static ngx_int_t ngx_http_userid_pre_conf(ngx_conf_t *cf) +{ + ngx_http_log_op_name_t *op; + + for (op = ngx_http_userid_log_fmt_ops; op->name.len; op++) { /* void */ } + op->op = NULL; + + op = ngx_http_log_fmt_ops; + + for (op = ngx_http_log_fmt_ops; op->op; op++) { + if (op->name.len == 0) { + op = (ngx_http_log_op_name_t *) op->op; + } + } + + op->op = (ngx_http_log_op_pt) ngx_http_userid_log_fmt_ops; + + return NGX_OK; +} + + +static void *ngx_http_userid_create_conf(ngx_conf_t *cf) +{ + ngx_http_userid_conf_t *conf; + + if (!(conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_userid_conf_t)))) { + return NGX_CONF_ERROR; + } + + /* 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->enable = NGX_CONF_UNSET; + conf->service = NGX_CONF_UNSET; + conf->expires = NGX_CONF_UNSET; + + return conf; +} + + +static char *ngx_http_userid_merge_conf(ngx_conf_t *cf, void *parent, + void *child) +{ + ngx_http_userid_conf_t *prev = parent; + ngx_http_userid_conf_t *conf = child; + + ngx_conf_merge_value(conf->enable, prev->enable, NGX_HTTP_USERID_OFF); + + ngx_conf_merge_str_value(conf->name, prev->name, "uid"); + ngx_conf_merge_str_value(conf->domain, prev->domain, "."); + ngx_conf_merge_str_value(conf->path, prev->path, "/"); + + ngx_conf_merge_value(conf->service, prev->service, NGX_CONF_UNSET); + ngx_conf_merge_sec_value(conf->expires, prev->expires, 0); + + return NGX_CONF_OK; +} + + +char *ngx_conf_check_domain(ngx_conf_t *cf, void *post, void *data) +{ + ngx_str_t *domain = data; + + if (domain->len == 4 && ngx_strcmp(domain->data, "none") == 0) { + domain->len = 1; + domain->data = (u_char *) "."; + } + + return NGX_CONF_OK; +} + + +char *ngx_http_userid_expires(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_http_userid_conf_t *ucf = conf; + + ngx_str_t *value; + + if (ucf->expires != NGX_CONF_UNSET) { + return "is duplicate"; + } + + value = cf->args->elts; + + if (ngx_strcmp(value[1].data, "max") == 0) { + ucf->expires = NGX_HTTP_USERID_MAX_EXPIRES; + return NGX_CONF_OK; + } + + if (ngx_strcmp(value[1].data, "off") == 0) { + ucf->expires = 0; + return NGX_CONF_OK; + } + + ucf->expires = ngx_parse_time(&value[1], 1); + if (ucf->expires == NGX_ERROR) { + return "invalid value"; + } + + if (ucf->expires == NGX_PARSE_LARGE_TIME) { + return "value must be less than 68 years"; + } + + return NGX_CONF_OK; +} diff --git a/src/http/modules/proxy/ngx_http_proxy_cache.c b/src/http/modules/proxy/ngx_http_proxy_cache.c new file mode 100644 --- /dev/null +++ b/src/http/modules/proxy/ngx_http_proxy_cache.c @@ -0,0 +1,629 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include +#include + + +static int ngx_http_proxy_process_cached_response(ngx_http_proxy_ctx_t *p, + int rc); +static int ngx_http_proxy_process_cached_header(ngx_http_proxy_ctx_t *p); +static void ngx_http_proxy_cache_look_complete_request(ngx_http_proxy_ctx_t *p); + + +int ngx_http_proxy_get_cached_response(ngx_http_proxy_ctx_t *p) +{ + char *last; + ngx_http_request_t *r; + ngx_http_proxy_cache_t *c; + ngx_http_proxy_upstream_conf_t *u; + + r = p->request; + + if (!(c = ngx_pcalloc(r->pool, sizeof(ngx_http_proxy_cache_t)))) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + p->cache = c; + + c->ctx.file.fd = NGX_INVALID_FILE; + c->ctx.file.log = r->connection->log; + c->ctx.path = p->lcf->cache_path; + + u = p->lcf->upstream; + + c->ctx.key.len = u->url.len + r->uri.len - u->location->len + r->args.len; + if (!(c->ctx.key.data = ngx_palloc(r->pool, c->ctx.key.len + 1))) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + last = ngx_cpymem(c->ctx.key.data, u->url.data, u->url.len); + + last = ngx_cpymem(last, r->uri.data + u->location->len, + r->uri.len - u->location->len); + + if (r->args.len > 0) { + *(last++) = '?'; + last = ngx_cpymem(last, r->args.data, r->args.len); + } + *last = '\0'; + + p->header_in = ngx_create_temp_hunk(r->pool, p->lcf->header_buffer_size); + if (p->header_in == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + p->header_in->tag = (ngx_hunk_tag_t) &ngx_http_proxy_module; + + c->ctx.buf = p->header_in; + c->ctx.log = r->connection->log; + + return ngx_http_proxy_process_cached_response(p, + ngx_http_cache_get_file(r, &c->ctx)); +} + + +static int ngx_http_proxy_process_cached_response(ngx_http_proxy_ctx_t *p, + int rc) +{ + if (rc == NGX_OK) { + p->state->cache_state = NGX_HTTP_PROXY_CACHE_HIT; + p->header_in->pos = p->header_in->start + p->cache->ctx.header_size; + + if (ngx_http_proxy_process_cached_header(p) == NGX_ERROR) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + p->valid_header_in = 1; + + return ngx_http_proxy_send_cached_response(p); + } + + if (rc == NGX_HTTP_CACHE_STALE) { + p->state->cache_state = NGX_HTTP_PROXY_CACHE_EXPR; + + } else if (rc == NGX_HTTP_CACHE_AGED) { + p->state->cache_state = NGX_HTTP_PROXY_CACHE_AGED; + } + + if (rc == NGX_HTTP_CACHE_STALE || rc == NGX_HTTP_CACHE_AGED) { + p->state->expired = ngx_time() - p->cache->ctx.expires; + p->header_in->pos = p->header_in->start + p->cache->ctx.header_size; + + if (ngx_http_proxy_process_cached_header(p) == NGX_ERROR) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + p->header_in->pos = p->header_in->start + p->cache->ctx.header_size; + p->header_in->last = p->header_in->pos; + + p->stale = 1; + p->valid_header_in = 1; + + } else if (rc == NGX_DECLINED) { + p->state->cache_state = NGX_HTTP_PROXY_CACHE_MISS; + p->header_in->pos = p->header_in->start + p->cache->ctx.header_size; + p->header_in->last = p->header_in->pos; + } + + if (p->lcf->busy_lock) { + p->try_busy_lock = 1; + + p->header_in->pos = p->header_in->start; + p->header_in->last = p->header_in->start; + + p->busy_lock.time = 0; + p->busy_lock.event = p->request->connection->read; + p->busy_lock.event_handler = ngx_http_proxy_busy_lock_handler; + p->busy_lock.md5 = p->cache->ctx.md5; + + ngx_http_proxy_cache_busy_lock(p); + return NGX_DONE; + } + + return ngx_http_proxy_request_upstream(p); +} + + +static int ngx_http_proxy_process_cached_header(ngx_http_proxy_ctx_t *p) +{ + int rc, i; + ngx_table_elt_t *h; + ngx_http_request_t *r; + ngx_http_proxy_cache_t *c; + + rc = ngx_http_proxy_parse_status_line(p); + + c = p->cache; + r = p->request; + + if (rc == NGX_AGAIN) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "\"proxy_header_buffer_size\" " + "is too small to read header from \"%s\"", + c->ctx.file.name.data); + return NGX_ERROR; + } + + if (rc == NGX_HTTP_PROXY_PARSE_NO_HEADER) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "no valid HTTP/1.0 header in \"%s\"", + c->ctx.file.name.data); + return NGX_ERROR; + } + + /* rc == NGX_OK */ + + c->status = p->status; + c->status_line.len = p->status_end - p->status_start; + c->status_line.data = ngx_palloc(r->pool, c->status_line.len + 1); + if (c->status_line.data == NULL) { + return NGX_ERROR; + } + + /* reset for the possible parsing the upstream header */ + + p->status = 0; + p->status_count = 0; + + ngx_cpystrn(c->status_line.data, p->status_start, c->status_line.len + 1); + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http cache status %d \"%s\"", + c->status, c->status_line.data); + + /* TODO: ngx_init_table */ + c->headers_in.headers = ngx_create_table(r->pool, 20); + + for ( ;; ) { + rc = ngx_http_parse_header_line(r, p->header_in); + + if (rc == NGX_OK) { + + /* a header line has been parsed successfully */ + + h = ngx_http_add_header(&c->headers_in, ngx_http_proxy_headers_in); + if (h == NULL) { + return NGX_ERROR; + } + + 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); + if (h->key.data == NULL) { + return NGX_ERROR; + } + + h->value.data = h->key.data + h->key.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); + + for (i = 0; ngx_http_proxy_headers_in[i].name.len != 0; i++) { + if (ngx_http_proxy_headers_in[i].name.len != h->key.len) { + continue; + } + + if (ngx_strcasecmp(ngx_http_proxy_headers_in[i].name.data, + h->key.data) == 0) + { + *((ngx_table_elt_t **) ((char *) &c->headers_in + + ngx_http_proxy_headers_in[i].offset)) = h; + break; + } + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http cache header: \"%s: %s\"", + h->key.data, h->value.data); + + continue; + + } else 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 cache header done"); + + c->ctx.file_start = p->header_in->pos - p->header_in->start; + + return NGX_OK; + + } else if (rc == NGX_HTTP_PARSE_INVALID_HEADER) { + + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "invalid header in \"%s\"", + c->ctx.file.name.data); + return NGX_ERROR; + } + + /* rc == NGX_AGAIN || rc == NGX_HTTP_PARSE_TOO_LONG_HEADER */ + + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "\"proxy_header_buffer_size\" " + "is too small to read header from \"%s\"", + c->ctx.file.name.data); + return NGX_ERROR; + } +} + + +void ngx_http_proxy_cache_busy_lock(ngx_http_proxy_ctx_t *p) +{ + int rc, ft_type; + + rc = ngx_http_busy_lock_cachable(p->lcf->busy_lock, &p->busy_lock, + p->try_busy_lock); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, p->request->connection->log, 0, + "http cache busy lock cachable: %d", rc); + + if (rc == NGX_OK) { + if (p->try_busy_lock) { + p->busy_locked = 1; + p->header_in->pos = p->header_in->start + p->cache->ctx.header_size; + p->header_in->last = p->header_in->pos; + + ngx_http_proxy_request_upstream(p); + return; + } + + ngx_http_proxy_cache_look_complete_request(p); + return; + } + + p->try_busy_lock = 0; + + if (p->cache->ctx.file.fd != NGX_INVALID_FILE + && !p->cache->ctx.file.info_valid) + { + if (ngx_fd_info(p->cache->ctx.file.fd, &p->cache->ctx.file.info) + == NGX_FILE_ERROR) + { + ngx_log_error(NGX_LOG_CRIT, p->request->connection->log, ngx_errno, + ngx_fd_info_n " \"%s\" failed", + p->cache->ctx.file.name.data); + ngx_http_proxy_finalize_request(p, NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + p->cache->ctx.file.info_valid = 1; + } + + if (rc == NGX_AGAIN) { + + if ((ngx_event_flags & (NGX_USE_CLEAR_EVENT|NGX_HAVE_KQUEUE_EVENT)) + && !p->request->connection->write->active) + { + /* + * kqueue allows to detect when client closes prematurely + * connection + */ + + p->request->connection->write->event_handler = + ngx_http_proxy_check_broken_connection; + + if (ngx_add_event(p->request->connection->write, NGX_WRITE_EVENT, + NGX_CLEAR_EVENT) == NGX_ERROR) + { + ngx_http_proxy_finalize_request(p, + NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + } + + return; + } + + ngx_http_busy_unlock(p->lcf->busy_lock, &p->busy_lock); + + if (rc == NGX_DONE) { + ft_type = NGX_HTTP_PROXY_FT_BUSY_LOCK; + + } else { + /* rc == NGX_ERROR */ + ft_type = NGX_HTTP_PROXY_FT_MAX_WAITING; + } + + if (p->stale && (p->lcf->use_stale & ft_type)) { + ngx_http_proxy_finalize_request(p, + ngx_http_proxy_send_cached_response(p)); + return; + } + + p->state->status = NGX_HTTP_SERVICE_UNAVAILABLE; + ngx_http_proxy_finalize_request(p, NGX_HTTP_SERVICE_UNAVAILABLE); +} + + +static void ngx_http_proxy_cache_look_complete_request(ngx_http_proxy_ctx_t *p) +{ + int rc; + ngx_http_cache_ctx_t *ctx; + + if (!(ctx = ngx_pcalloc(p->request->pool, sizeof(ngx_http_cache_ctx_t)))) { + ngx_http_proxy_finalize_request(p, NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + *ctx = p->cache->ctx; + + rc = ngx_http_cache_open_file(ctx, ngx_file_uniq(&p->cache->ctx.file.info)); + + if (rc == NGX_DECLINED || rc == NGX_HTTP_CACHE_THE_SAME) { + p->try_busy_lock = 1; + p->busy_lock.time = 0; + ngx_http_proxy_cache_busy_lock(p); + return; + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, p->request->connection->log, 0, + "http cache old fd:%d, new fd:%d", + p->cache->ctx.file.fd, ctx->file.fd); + + if (p->cache->ctx.file.fd != NGX_INVALID_FILE) { + if (ngx_close_file(p->cache->ctx.file.fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, p->request->connection->log, ngx_errno, + ngx_close_file_n " \"%s\" failed", + p->cache->ctx.file.name.data); + } + } + + p->cache->ctx = *ctx; + + p->status = 0; + p->status_count = 0; + + ngx_http_proxy_finalize_request(p, + ngx_http_proxy_process_cached_response(p, rc)); +} + + +int ngx_http_proxy_send_cached_response(ngx_http_proxy_ctx_t *p) +{ + int rc, len, i; + off_t rest; + ngx_hunk_t *h0, *h1; + ngx_chain_t out[2]; + ngx_http_request_t *r; + + r = p->request; + + r->headers_out.status = p->cache->status; + +#if 0 + r->headers_out.content_length_n = -1; + r->headers_out.content_length = NULL; +#endif + + /* copy an cached header to r->headers_out */ + + if (ngx_http_proxy_copy_header(p, &p->cache->headers_in) == NGX_ERROR) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + /* we need to allocate all before the header would be sent */ + + len = p->header_in->end - (p->header_in->start + p->cache->ctx.file_start); + + h0 = NULL; + h1 = NULL; + + if (len) { + if (!((h0 = ngx_calloc_hunk(r->pool)))) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + if (!((h0->file = ngx_pcalloc(r->pool, sizeof(ngx_file_t))))) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + } + + if (len < p->cache->ctx.length) { + if (!((h1 = ngx_calloc_hunk(r->pool)))) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + if (!((h1->file = ngx_pcalloc(r->pool, sizeof(ngx_file_t))))) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + } + + rc = ngx_http_send_header(r); + + /* NEEDED ??? */ p->header_sent = 1; + + if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) { + return rc; + } + + rest = p->cache->ctx.length; + + if (len) { + if (p->valid_header_in) { + h0->pos = p->header_in->start + p->cache->ctx.file_start; + + if (len > p->cache->ctx.length) { + h0->last = h0->pos + p->cache->ctx.length; + + } else { + h0->last = p->header_in->end; + } + + h0->type = NGX_HUNK_IN_MEMORY|NGX_HUNK_TEMP; + } + + h0->type |= NGX_HUNK_FILE; + h0->file_pos = p->cache->ctx.file_start; + + h0->file->fd = p->cache->ctx.file.fd; + h0->file->log = r->connection->log; + + if (len > p->cache->ctx.length) { + h0->file_last = h0->file_pos + p->cache->ctx.length; + rest = 0; + + } else { + h0->file_last = h0->file_pos + len; + rest -= len; + } + + out[0].hunk = h0; + out[0].next = &out[1]; + i = 0; + + } else { + i = -1; + } + + if (rest) { + h1->file_pos = p->cache->ctx.file_start + len; + h1->file_last = h1->file_pos + rest; + h1->type = NGX_HUNK_FILE; + + h1->file->fd = p->cache->ctx.file.fd; + h1->file->log = r->connection->log; + + out[++i].hunk = h1; + } + + out[i].next = NULL; + if (!r->main) { + out[i].hunk->type |= NGX_HUNK_LAST; + } + + r->file.fd = p->cache->ctx.file.fd; + + return ngx_http_output_filter(r, out); +} + + +int ngx_http_proxy_is_cachable(ngx_http_proxy_ctx_t *p) +{ + time_t date, last_modified, expires, t; + ngx_http_proxy_headers_in_t *h; + + switch (p->upstream->status) { + case NGX_HTTP_OK: + case NGX_HTTP_MOVED_PERMANENTLY: + case NGX_HTTP_MOVED_TEMPORARILY: + break; + +#if 0 + case NGX_HTTP_NOT_MODIFIED: + return 1; +#endif + + default: + return 0; + } + + h = &p->upstream->headers_in; + + date = NGX_ERROR; + if (h->date) { + date = ngx_http_parse_time(h->date->value.data, h->date->value.len); + } + if (date == NGX_ERROR) { + date = ngx_time(); + } + p->cache->ctx.date = date; + + last_modified = NGX_ERROR; + if (h->last_modified) { + last_modified = ngx_http_parse_time(h->last_modified->value.data, + h->last_modified->value.len); + p->cache->ctx.last_modified = last_modified; + } + + if (h->x_accel_expires) { + expires = ngx_atoi(h->x_accel_expires->value.data, + h->x_accel_expires->value.len); + if (expires != NGX_ERROR) { + p->state->reason = NGX_HTTP_PROXY_CACHE_XAE; + p->state->expires = expires; + p->cache->ctx.expires = date + expires; + return (expires > 0); + } + } + + if (!p->lcf->ignore_expires) { + + /* TODO: Cache-Control: no-cache, max-age= */ + + if (h->expires) { + expires = ngx_http_parse_time(h->expires->value.data, + h->expires->value.len); + if (expires != NGX_ERROR) { + p->state->reason = NGX_HTTP_PROXY_CACHE_EXP; + p->state->expires = expires - date; + p->cache->ctx.expires = expires; + return (date < expires); + } + } + } + + if (p->upstream->status == NGX_HTTP_MOVED_PERMANENTLY) { + p->state->reason = NGX_HTTP_PROXY_CACHE_MVD; + p->state->expires = /* STUB: 1 hour */ 60 * 60; + p->cache->ctx.expires = /* STUB: 1 hour */ 60 * 60; + return 1; + } + + if (p->upstream->status == NGX_HTTP_MOVED_TEMPORARILY) { + return 1; + } + + if (last_modified != NGX_ERROR && p->lcf->lm_factor > 0) { + + /* FIXME: time_t == int_64_t, we can use fpu */ + + p->state->reason = NGX_HTTP_PROXY_CACHE_LMF; + t = (time_t) + ((((int64_t) (date - last_modified)) * p->lcf->lm_factor) / 100); + p->state->expires = t; + p->cache->ctx.expires = ngx_time() + t; + return 1; + } + + if (p->lcf->default_expires > 0) { + p->state->reason = NGX_HTTP_PROXY_CACHE_PDE; + p->state->expires = p->lcf->default_expires; + p->cache->ctx.expires = ngx_time() + p->lcf->default_expires; + return 1; + } + + return 0; +} + + +int ngx_http_proxy_update_cache(ngx_http_proxy_ctx_t *p) +{ + ngx_event_pipe_t *ep; + + if (p->cache == NULL) { + return NGX_OK; + } + + ep = p->upstream->event_pipe; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, p->request->connection->log, 0, + "http cache update len: " OFF_T_FMT ":" OFF_T_FMT, + p->cache->ctx.length, ep->read_length); + + if (p->cache->ctx.length == -1) { + /* TODO: test rc */ + ngx_write_file(&ep->temp_file->file, + (char *) &ep->read_length, sizeof(off_t), + offsetof(ngx_http_cache_header_t, length)); + } + + return ngx_http_cache_update_file(p->request, &p->cache->ctx, + &ep->temp_file->file.name); +} diff --git a/src/http/modules/proxy/ngx_http_proxy_handler.c b/src/http/modules/proxy/ngx_http_proxy_handler.c new file mode 100644 --- /dev/null +++ b/src/http/modules/proxy/ngx_http_proxy_handler.c @@ -0,0 +1,1280 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include +#include + + +static ngx_int_t ngx_http_proxy_handler(ngx_http_request_t *r); + +static u_char *ngx_http_proxy_log_proxy_state(ngx_http_request_t *r, + u_char *buf, uintptr_t data); +static u_char *ngx_http_proxy_log_cache_state(ngx_http_request_t *r, + u_char *buf, uintptr_t data); +static u_char *ngx_http_proxy_log_reason(ngx_http_request_t *r, u_char *buf, + uintptr_t data); + +static ngx_int_t ngx_http_proxy_pre_conf(ngx_conf_t *cf); +static void *ngx_http_proxy_create_loc_conf(ngx_conf_t *cf); +static char *ngx_http_proxy_merge_loc_conf(ngx_conf_t *cf, + void *parent, void *child); + +static char *ngx_http_proxy_set_pass(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static char *ngx_http_proxy_parse_upstream(ngx_str_t *url, + ngx_http_proxy_upstream_conf_t *u); + + +static ngx_conf_bitmask_t next_upstream_masks[] = { + { ngx_string("error"), NGX_HTTP_PROXY_FT_ERROR }, + { ngx_string("timeout"), NGX_HTTP_PROXY_FT_TIMEOUT }, + { ngx_string("invalid_header"), NGX_HTTP_PROXY_FT_INVALID_HEADER }, + { ngx_string("http_500"), NGX_HTTP_PROXY_FT_HTTP_500 }, + { ngx_string("http_404"), NGX_HTTP_PROXY_FT_HTTP_404 }, + { ngx_null_string, 0 } +}; + + +static ngx_conf_bitmask_t use_stale_masks[] = { + { ngx_string("error"), NGX_HTTP_PROXY_FT_ERROR }, + { ngx_string("timeout"), NGX_HTTP_PROXY_FT_TIMEOUT }, + { ngx_string("invalid_header"), NGX_HTTP_PROXY_FT_INVALID_HEADER }, + { ngx_string("http_500"), NGX_HTTP_PROXY_FT_HTTP_500 }, + { ngx_string("busy_lock"), NGX_HTTP_PROXY_FT_BUSY_LOCK }, + { ngx_string("max_waiting"), NGX_HTTP_PROXY_FT_MAX_WAITING }, + { ngx_null_string, 0 } +}; + + +static ngx_conf_num_bounds_t ngx_http_proxy_lm_factor_bounds = { + ngx_conf_check_num_bounds, 0, 100 +}; + + +static ngx_command_t ngx_http_proxy_commands[] = { + + { ngx_string("proxy_pass"), + NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_http_proxy_set_pass, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("proxy_connect_timeout"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_conf_set_msec_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_proxy_loc_conf_t, connect_timeout), + NULL }, + + { ngx_string("proxy_send_timeout"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_conf_set_msec_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_proxy_loc_conf_t, send_timeout), + NULL }, + + { ngx_string("proxy_preserve_host"), + 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, preserve_host), + NULL }, + + { ngx_string("proxy_set_x_real_ip"), + 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, set_x_real_ip), + NULL }, + + { ngx_string("proxy_add_x_forwarded_for"), + 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, add_x_forwarded_for), + 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, header_buffer_size), + NULL }, + + { ngx_string("proxy_read_timeout"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_conf_set_msec_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_proxy_loc_conf_t, read_timeout), + NULL }, + + { ngx_string("proxy_buffers"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2, + ngx_conf_set_bufs_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_proxy_loc_conf_t, bufs), + NULL }, + + { ngx_string("proxy_busy_buffers_size"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_conf_set_size_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_proxy_loc_conf_t, busy_buffers_size), + NULL }, + +#if (NGX_HTTP_FILE_CACHE) + + { ngx_string("proxy_cache_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, cache_path), + ngx_garbage_collector_http_cache_handler }, + +#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, temp_path), + (void *) ngx_garbage_collector_temp_handler }, + + { ngx_string("proxy_temp_file_write_size"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_conf_set_size_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_proxy_loc_conf_t, temp_file_write_size), + NULL }, + + { ngx_string("proxy_cache"), + 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, cache), + NULL }, + + + { ngx_string("proxy_busy_lock"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE13, + ngx_http_set_busy_lock_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_proxy_loc_conf_t, busy_lock), + NULL }, + + + { ngx_string("proxy_pass_server"), + 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, pass_server), + NULL }, + + { ngx_string("proxy_pass_x_accel_expires"), + 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, pass_x_accel_expires), + NULL }, + + { ngx_string("proxy_ignore_expires"), + 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, ignore_expires), + NULL }, + + { ngx_string("proxy_lm_factor"), + 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, lm_factor), + &ngx_http_proxy_lm_factor_bounds }, + + { ngx_string("proxy_default_expires"), + 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_proxy_loc_conf_t, default_expires), + NULL }, + + + { ngx_string("proxy_next_upstream"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_ANY, + ngx_conf_set_bitmask_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_proxy_loc_conf_t, next_upstream), + &next_upstream_masks }, + + { ngx_string("proxy_use_stale"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_ANY, + ngx_conf_set_bitmask_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_proxy_loc_conf_t, use_stale), + &use_stale_masks }, + + ngx_null_command +}; + + +ngx_http_module_t ngx_http_proxy_module_ctx = { + ngx_http_proxy_pre_conf, /* pre conf */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + NULL, /* create server configuration */ + NULL, /* merge server configuration */ + + ngx_http_proxy_create_loc_conf, /* create location configration */ + ngx_http_proxy_merge_loc_conf /* merge location configration */ +}; + + +ngx_module_t ngx_http_proxy_module = { + NGX_MODULE, + &ngx_http_proxy_module_ctx, /* module context */ + ngx_http_proxy_commands, /* module directives */ + NGX_HTTP_MODULE, /* module type */ + NULL, /* init module */ + NULL /* init child */ +}; + + + +static ngx_http_log_op_name_t ngx_http_proxy_log_fmt_ops[] = { + { ngx_string("proxy"), /* STUB */ 100, + ngx_http_proxy_log_proxy_state }, + { ngx_string("proxy_cache_state"), sizeof("BYPASS") - 1, + ngx_http_proxy_log_cache_state }, + { ngx_string("proxy_reason"), sizeof("BPS") - 1, + ngx_http_proxy_log_reason }, + { ngx_null_string, 0, NULL } +}; + + + +ngx_http_header_t ngx_http_proxy_headers_in[] = { + { ngx_string("Date"), offsetof(ngx_http_proxy_headers_in_t, date) }, + { ngx_string("Server"), offsetof(ngx_http_proxy_headers_in_t, server) }, + + { ngx_string("Expires"), offsetof(ngx_http_proxy_headers_in_t, expires) }, + { ngx_string("Cache-Control"), + offsetof(ngx_http_proxy_headers_in_t, cache_control) }, + { ngx_string("ETag"), offsetof(ngx_http_proxy_headers_in_t, etag) }, + { ngx_string("X-Accel-Expires"), + offsetof(ngx_http_proxy_headers_in_t, x_accel_expires) }, + + { ngx_string("Connection"), + offsetof(ngx_http_proxy_headers_in_t, connection) }, + { ngx_string("Content-Type"), + offsetof(ngx_http_proxy_headers_in_t, content_type) }, + { ngx_string("Content-Length"), + offsetof(ngx_http_proxy_headers_in_t, content_length) }, + { ngx_string("Last-Modified"), + offsetof(ngx_http_proxy_headers_in_t, last_modified) }, + { ngx_string("Location"), + offsetof(ngx_http_proxy_headers_in_t, location) }, + { ngx_string("Accept-Ranges"), + offsetof(ngx_http_proxy_headers_in_t, accept_ranges) }, + { ngx_string("X-Pad"), offsetof(ngx_http_proxy_headers_in_t, x_pad) }, + + { ngx_null_string, 0 } +}; + + +static ngx_str_t cache_states[] = { + ngx_string("PASS"), + ngx_string("BYPASS"), + ngx_string("AUTH"), + ngx_string("PGNC"), + ngx_string("MISS"), + ngx_string("EXPR"), + ngx_string("AGED"), + ngx_string("HIT") +}; + + +static ngx_str_t cache_reasons[] = { + ngx_string("BPS"), + ngx_string("XAE"), + ngx_string("CTL"), + ngx_string("EXP"), + ngx_string("MVD"), + ngx_string("LMF"), + ngx_string("PDE") +}; + + +static ngx_int_t ngx_http_proxy_handler(ngx_http_request_t *r) +{ + ngx_http_proxy_ctx_t *p; + + ngx_http_create_ctx(r, p, ngx_http_proxy_module, + sizeof(ngx_http_proxy_ctx_t), + NGX_HTTP_INTERNAL_SERVER_ERROR); + + p->lcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module); + p->request = r; + + /* TODO: we currently support reverse proxy only */ + p->accel = 1; + + ngx_init_array(p->states, r->pool, p->lcf->peers->number, + sizeof(ngx_http_proxy_state_t), + NGX_HTTP_INTERNAL_SERVER_ERROR); + + if (!(p->state = ngx_push_array(&p->states))) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + ngx_memzero(p->state, sizeof(ngx_http_proxy_state_t)); + +#if (NGX_HTTP_FILE_CACHE) + + if (!p->lcf->cache + || (r->method != NGX_HTTP_GET && r->method != NGX_HTTP_HEAD)) + { + p->state->cache_state = NGX_HTTP_PROXY_CACHE_PASS; + + } else if (r->bypass_cache) { + p->state->cache_state = NGX_HTTP_PROXY_CACHE_BYPASS; + + } else if (r->headers_in.authorization) { + p->state->cache_state = NGX_HTTP_PROXY_CACHE_AUTH; + + } else if (r->no_cache) { + p->state->cache_state = NGX_HTTP_PROXY_CACHE_PGNC; + p->cachable = 1; + + } else { + p->cachable = 1; + } + + + if (p->state->cache_state != 0) { + return ngx_http_proxy_request_upstream(p); + } + + return ngx_http_proxy_get_cached_response(p); + +#else + + p->state->cache_state = NGX_HTTP_PROXY_CACHE_PASS; + + return ngx_http_proxy_request_upstream(p); + +#endif +} + + +void ngx_http_proxy_check_broken_connection(ngx_event_t *ev) +{ + int n; + char buf[1]; + ngx_err_t err; + ngx_connection_t *c; + ngx_http_request_t *r; + ngx_http_proxy_ctx_t *p; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ev->log, 0, + "http proxy check client, write event:%d", ev->write); + +#if (HAVE_KQUEUE) + + if (ngx_event_flags & NGX_HAVE_KQUEUE_EVENT) { + + if (!ev->pending_eof) { + return; + } + + c = ev->data; + r = c->data; + p = ngx_http_get_module_ctx(r, ngx_http_proxy_module); + + ev->eof = 1; + + if (ev->kq_errno) { + ev->error = 1; + } + + if (!p->cachable && p->upstream->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_proxy_finalize_request(p, 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 (p->upstream == NULL || p->upstream->peer.connection == NULL) { + ngx_http_proxy_finalize_request(p, NGX_HTTP_CLIENT_CLOSED_REQUEST); + } + + return; + } + +#endif + + c = ev->data; + r = c->data; + p = ngx_http_get_module_ctx(r, ngx_http_proxy_module); + + n = recv(c->fd, buf, 1, MSG_PEEK); + + err = ngx_socket_errno; + + /* + * we do not need to disable the write event because + * that event has NGX_USE_CLEAR_EVENT type + */ + + if (ev->write && (n >= 0 || err == NGX_EAGAIN)) { + return; + } + + if ((ngx_event_flags & NGX_USE_LEVEL_EVENT) && ev->active) { + if (ngx_del_event(ev, NGX_READ_EVENT, 0) == NGX_ERROR) { + ngx_http_proxy_finalize_request(p, NGX_HTTP_INTERNAL_SERVER_ERROR); + } + } + + if (n > 0) { + return; + } + + ev->eof = 1; + + if (n == -1) { + if (err == NGX_EAGAIN) { + return; + } + + ev->error = 1; + + } else { + /* n == 0 */ + err = 0; + } + + if (!p->cachable && p->upstream->peer.connection) { + ngx_log_error(NGX_LOG_INFO, ev->log, err, + "client closed prematurely connection, " + "so upstream connection is closed too"); + ngx_http_proxy_finalize_request(p, NGX_HTTP_CLIENT_CLOSED_REQUEST); + return; + } + + ngx_log_error(NGX_LOG_INFO, ev->log, err, + "client closed prematurely connection"); + + if (p->upstream == NULL || p->upstream->peer.connection == NULL) { + ngx_http_proxy_finalize_request(p, NGX_HTTP_CLIENT_CLOSED_REQUEST); + } +} + + +void ngx_http_proxy_busy_lock_handler(ngx_event_t *rev) +{ + ngx_connection_t *c; + ngx_http_request_t *r; + ngx_http_proxy_ctx_t *p; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "http proxy busy lock"); + + c = rev->data; + r = c->data; + p = ngx_http_get_module_ctx(r, ngx_http_proxy_module); + p->action = "waiting upstream in busy lock"; + + if (p->request->connection->write->eof) { + ngx_http_busy_unlock(p->lcf->busy_lock, &p->busy_lock); + ngx_http_proxy_finalize_request(p, NGX_HTTP_CLIENT_CLOSED_REQUEST); + return; + } + + if (rev->timedout) { + rev->timedout = 0; + p->busy_lock.time++; + p->state->bl_time = p->busy_lock.time; + +#if (NGX_HTTP_FILE_CACHE) + + if (p->state->cache_state < NGX_HTTP_PROXY_CACHE_MISS) { + ngx_http_proxy_upstream_busy_lock(p); + + } else { + ngx_http_proxy_cache_busy_lock(p); + } +#else + + ngx_http_proxy_upstream_busy_lock(p); + +#endif + + return; + } + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, + "http proxy: client sent while busy lock"); + + /* + * TODO: kevent() notify about error, otherwise we need to + * call ngx_peek(): recv(MSG_PEEK) to get errno. THINK about aio. + * if there's no error we need to disable event. + */ + +#if 0 +#if (HAVE_KQUEUE) + + if ((ngx_event_flags & NGX_HAVE_KQUEUE_EVENT) && rev->kq_eof) { + ngx_http_busy_unlock(p->lcf->busy_lock, &p->busy_lock); + + ngx_del_timer(rev); + + ngx_log_error(NGX_LOG_ERR, c->log, rev->kq_errno, + "client() closed connection"); + + if (ngx_del_event(rev, NGX_READ_EVENT, NGX_CLOSE_EVENT) == NGX_ERROR) { + ngx_http_proxy_finalize_request(p, NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + ngx_http_proxy_finalize_request(p, NGX_HTTP_CLIENT_CLOSED_REQUEST); + return; + } + +#endif +#endif + +} + + +void ngx_http_proxy_finalize_request(ngx_http_proxy_ctx_t *p, int rc) +{ + ngx_http_request_t *r; + + r = p->request; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "finalize http proxy request"); + + if (p->upstream && p->upstream->peer.connection) { + ngx_http_proxy_close_connection(p); + } + + if (p->header_sent + && (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE)) + { + rc = 0; + } + + if (p->saved_ctx) { + r->connection->log->data = p->saved_ctx; + r->connection->log->handler = p->saved_handler; + } + + if (p->upstream && p->upstream->event_pipe) { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http proxy temp fd: %d", + p->upstream->event_pipe->temp_file->file.fd); + } + + if (p->cache) { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http proxy cache fd: %d", + p->cache->ctx.file.fd); + } + + if (p->upstream && p->upstream->event_pipe) { + r->file.fd = p->upstream->event_pipe->temp_file->file.fd; + + } else if (p->cache) { + r->file.fd = p->cache->ctx.file.fd; + } + + if (rc == 0 && r->main == NULL) { + rc = ngx_http_send_last(r); + } + + ngx_http_finalize_request(r, rc); +} + + +void ngx_http_proxy_close_connection(ngx_http_proxy_ctx_t *p) +{ + ngx_socket_t fd; + ngx_connection_t *c; + + c = p->upstream->peer.connection; + p->upstream->peer.connection = NULL; + + if (p->lcf->busy_lock) { + p->lcf->busy_lock->busy--; + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http proxy close connection: %d", c->fd); + + if (c->fd == -1) { +#if 0 + ngx_log_error(NGX_LOG_ALERT, c->log, 0, "connection already closed"); +#endif + return; + } + + if (c->read->timer_set) { + ngx_del_timer(c->read); + } + + if (c->write->timer_set) { + ngx_del_timer(c->write); + } + + /* TODO: move connection to the connection pool */ + + if (ngx_del_conn) { + ngx_del_conn(c, NGX_CLOSE_EVENT); + + } else { + if (c->read->active || c->read->disabled) { + ngx_del_event(c->read, NGX_READ_EVENT, NGX_CLOSE_EVENT); + } + + if (c->write->active || c->read->disabled) { + ngx_del_event(c->write, NGX_WRITE_EVENT, NGX_CLOSE_EVENT); + } + } + + /* + * we have to clean the connection information before the closing + * because another thread may reopen the same file descriptor + * before we clean the connection + */ + + if (ngx_mutex_lock(ngx_posted_events_mutex) == NGX_OK) { + + if (c->read->prev) { + ngx_delete_posted_event(c->read); + } + + if (c->write->prev) { + ngx_delete_posted_event(c->write); + } + + c->read->closed = 1; + c->write->closed = 1; + + ngx_mutex_unlock(ngx_posted_events_mutex); + } + + fd = c->fd; + c->fd = (ngx_socket_t) -1; + c->data = NULL; + + if (ngx_close_socket(fd) == -1) { + ngx_log_error(NGX_LOG_ALERT, c->log, ngx_socket_errno, + ngx_close_socket_n " failed"); + } +} + + +size_t ngx_http_proxy_log_error(void *data, char *buf, size_t len) +{ + ngx_http_proxy_log_ctx_t *ctx = data; + + ngx_http_request_t *r; + ngx_peer_connection_t *peer; + + r = ctx->proxy->request; + peer = &ctx->proxy->upstream->peer; + + return ngx_snprintf(buf, len, + " while %s, client: %s, URL: %s, upstream: %s%s%s%s%s", + ctx->proxy->action, + r->connection->addr_text.data, + r->unparsed_uri.data, + peer->peers->peers[peer->cur_peer].addr_port_text.data, + ctx->proxy->lcf->upstream->uri.data, + r->uri.data + ctx->proxy->lcf->upstream->location->len, + r->args.len ? "?" : "", + r->args.len ? r->args.data : (u_char *) ""); +} + + +static u_char *ngx_http_proxy_log_proxy_state(ngx_http_request_t *r, + u_char *buf, uintptr_t data) +{ + ngx_http_proxy_ctx_t *p; + + p = ngx_http_get_module_err_ctx(r, ngx_http_proxy_module); + + if (p == NULL) { + *buf = '-'; + return buf + 1; + } + + if (p->state->cache_state == 0) { + *buf++ = '-'; + + } else { + buf = ngx_cpymem(buf, cache_states[p->state->cache_state - 1].data, + cache_states[p->state->cache_state - 1].len); + } + + *buf++ = '/'; + + if (p->state->expired == 0) { + *buf++ = '-'; + + } else { + buf += ngx_snprintf((char *) buf, TIME_T_LEN, + TIME_T_FMT, p->state->expired); + } + + *buf++ = '/'; + + if (p->state->bl_time == 0) { + *buf++ = '-'; + + } else { + buf += ngx_snprintf((char *) buf, TIME_T_LEN, + TIME_T_FMT, p->state->bl_time); + } + + *buf++ = '/'; + + *buf++ = '*'; + + *buf++ = ' '; + + if (p->state->status == 0) { + *buf++ = '-'; + + } else { + buf += ngx_snprintf((char *) buf, 4, "%" NGX_UINT_T_FMT, + p->state->status); + } + + *buf++ = '/'; + + if (p->state->reason == 0) { + *buf++ = '-'; + + } else { + buf = ngx_cpymem(buf, cache_reasons[p->state->reason - 1].data, + cache_reasons[p->state->reason - 1].len); + } + + *buf++ = '/'; + + if (p->state->reason < NGX_HTTP_PROXY_CACHE_XAE) { + *buf++ = '-'; + + } else { + buf += ngx_snprintf((char *) buf, TIME_T_LEN, + TIME_T_FMT, p->state->expires); + } + + *buf++ = ' '; + *buf++ = '*'; + + return buf; +} + + +static u_char *ngx_http_proxy_log_cache_state(ngx_http_request_t *r, + u_char *buf, uintptr_t data) +{ + ngx_http_proxy_ctx_t *p; + + p = ngx_http_get_module_err_ctx(r, ngx_http_proxy_module); + + if (p == NULL || p->state->cache_state == 0) { + *buf = '-'; + return buf + 1; + } + + return ngx_cpymem(buf, cache_states[p->state->cache_state - 1].data, + cache_states[p->state->cache_state - 1].len); +} + + +static u_char *ngx_http_proxy_log_reason(ngx_http_request_t *r, u_char *buf, + uintptr_t data) +{ + ngx_http_proxy_ctx_t *p; + + p = ngx_http_get_module_err_ctx(r, ngx_http_proxy_module); + + if (p == NULL || p->state->reason == 0) { + *buf = '-'; + return buf + 1; + } + + return ngx_cpymem(buf, cache_reasons[p->state->reason - 1].data, + cache_reasons[p->state->reason - 1].len); +} + + +static ngx_int_t ngx_http_proxy_pre_conf(ngx_conf_t *cf) +{ + ngx_http_log_op_name_t *op; + + for (op = ngx_http_proxy_log_fmt_ops; op->name.len; op++) { /* void */ } + op->op = NULL; + + op = ngx_http_log_fmt_ops; + + for (op = ngx_http_log_fmt_ops; op->op; op++) { + if (op->name.len == 0) { + op = (ngx_http_log_op_name_t *) op->op; + } + } + + op->op = (ngx_http_log_op_pt) ngx_http_proxy_log_fmt_ops; + + return NGX_OK; +} + + +static void *ngx_http_proxy_create_loc_conf(ngx_conf_t *cf) +{ + ngx_http_proxy_loc_conf_t *conf; + + ngx_test_null(conf, + ngx_pcalloc(cf->pool, sizeof(ngx_http_proxy_loc_conf_t)), + NGX_CONF_ERROR); + + /* set by ngx_pcalloc(): + + conf->bufs.num = 0; + + conf->path = NULL; + + conf->next_upstream = 0; + conf->use_stale = 0; + + conf->upstreams = NULL; + conf->peers = NULL; + + conf->cache_path = NULL; + conf->temp_path = NULL; + + conf->busy_lock = NULL; + + */ + + conf->connect_timeout = NGX_CONF_UNSET_MSEC; + conf->send_timeout = NGX_CONF_UNSET_MSEC; + + conf->preserve_host = NGX_CONF_UNSET; + conf->set_x_real_ip = NGX_CONF_UNSET; + conf->add_x_forwarded_for = NGX_CONF_UNSET; + + conf->header_buffer_size = NGX_CONF_UNSET_SIZE; + conf->read_timeout = NGX_CONF_UNSET_MSEC; + conf->busy_buffers_size = NGX_CONF_UNSET_SIZE; + + /* + * "proxy_max_temp_file_size" is hardcoded to 1G for reverse proxy, + * it should be configurable in the generic proxy + */ + conf->max_temp_file_size = 1024 * 1024 * 1024; + + conf->temp_file_write_size = NGX_CONF_UNSET_SIZE; + + /* "proxy_cyclic_temp_file" is disabled */ + conf->cyclic_temp_file = 0; + + conf->cache = NGX_CONF_UNSET; + + conf->pass_server = NGX_CONF_UNSET; + conf->pass_x_accel_expires = NGX_CONF_UNSET; + conf->ignore_expires = NGX_CONF_UNSET; + conf->lm_factor = NGX_CONF_UNSET; + conf->default_expires = NGX_CONF_UNSET; + + return conf; +} + + +static char *ngx_http_proxy_merge_loc_conf(ngx_conf_t *cf, + void *parent, void *child) +{ + ngx_http_proxy_loc_conf_t *prev = parent; + ngx_http_proxy_loc_conf_t *conf = child; + + size_t size; + + ngx_conf_merge_msec_value(conf->connect_timeout, + prev->connect_timeout, 60000); + ngx_conf_merge_msec_value(conf->send_timeout, prev->send_timeout, 60000); + + ngx_conf_merge_value(conf->preserve_host, prev->preserve_host, 0); + ngx_conf_merge_value(conf->set_x_real_ip, prev->set_x_real_ip, 0); + ngx_conf_merge_value(conf->add_x_forwarded_for, + prev->add_x_forwarded_for, 0); + + ngx_conf_merge_msec_value(conf->read_timeout, prev->read_timeout, 60000); + + ngx_conf_merge_size_value(conf->header_buffer_size, + prev->header_buffer_size, (size_t) ngx_pagesize); + + ngx_conf_merge_bufs_value(conf->bufs, prev->bufs, 8, ngx_pagesize); + + if (conf->bufs.num < 2) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "there must be at least 2 \"proxy_buffers\""); + return NGX_CONF_ERROR; + } + + size = conf->header_buffer_size; + if (size < conf->bufs.size) { + size = conf->bufs.size; + } + + + ngx_conf_merge_size_value(conf->busy_buffers_size, + prev->busy_buffers_size, NGX_CONF_UNSET_SIZE); + + if (conf->busy_buffers_size == NGX_CONF_UNSET_SIZE) { + conf->busy_buffers_size = 2 * size; + + } else if (conf->busy_buffers_size < size) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "\"proxy_busy_buffers_size\" must be equal or bigger than " + "maximum of the value of \"proxy_header_buffer_size\" and " + "one of the \"proxy_buffers\""); + + return NGX_CONF_ERROR; + + } else if (conf->busy_buffers_size > (conf->bufs.num - 1) * conf->bufs.size) + { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "\"proxy_busy_buffers_size\" must be less than " + "the size of all \"proxy_buffers\" minus one buffer"); + + return NGX_CONF_ERROR; + } + + + ngx_conf_merge_size_value(conf->temp_file_write_size, + prev->temp_file_write_size, NGX_CONF_UNSET_SIZE); + + if (conf->temp_file_write_size == NGX_CONF_UNSET_SIZE) { + conf->temp_file_write_size = 2 * size; + + } else if (conf->temp_file_write_size < size) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "\"proxy_temp_file_write_size\" must be equal or bigger than " + "maximum of the value of \"proxy_header_buffer_size\" and " + "one of the \"proxy_buffers\""); + + return NGX_CONF_ERROR; + } + + + ngx_conf_merge_size_value(conf->max_temp_file_size, + prev->max_temp_file_size, NGX_CONF_UNSET_SIZE); + + if (conf->max_temp_file_size == NGX_CONF_UNSET_SIZE) { + conf->max_temp_file_size = 2 * size; + + } else if (conf->max_temp_file_size < size) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "\"proxy_max_temp_file_size\" must be equal or bigger than " + "maximum of the value of \"proxy_header_buffer_size\" and " + "one of the \"proxy_buffers\""); + + return NGX_CONF_ERROR; + } + + + ngx_conf_merge_bitmask_value(conf->next_upstream, prev->next_upstream, + (NGX_CONF_BITMASK_SET + |NGX_HTTP_PROXY_FT_ERROR + |NGX_HTTP_PROXY_FT_TIMEOUT)); + + ngx_conf_merge_bitmask_value(conf->use_stale, prev->use_stale, + NGX_CONF_BITMASK_SET); + + ngx_conf_merge_path_value(conf->cache_path, prev->cache_path, + "cache", 1, 2, 0, cf->pool); + + ngx_conf_merge_path_value(conf->temp_path, prev->temp_path, + "temp", 1, 2, 0, cf->pool); + + ngx_conf_merge_value(conf->cache, prev->cache, 0); + + + /* conf->cache must be merged */ + + if (conf->busy_lock == NULL) { + conf->busy_lock = prev->busy_lock; + } + + if (conf->busy_lock && conf->cache && conf->busy_lock->md5 == NULL) { + + /* ngx_calloc_shared() */ + conf->busy_lock->md5_mask = + ngx_pcalloc(cf->pool, (conf->busy_lock->max_busy + 7) / 8); + if (conf->busy_lock->md5_mask == NULL) { + return NGX_CONF_ERROR; + } + + /* 16 bytes are 128 bits of the md5 */ + + /* ngx_alloc_shared() */ + conf->busy_lock->md5 = ngx_palloc(cf->pool, + 16 * conf->busy_lock->max_busy); + if (conf->busy_lock->md5 == NULL) { + return NGX_CONF_ERROR; + } + } + + + ngx_conf_merge_value(conf->pass_server, prev->pass_server, 0); + ngx_conf_merge_value(conf->pass_x_accel_expires, + prev->pass_x_accel_expires, 0); + ngx_conf_merge_value(conf->ignore_expires, prev->ignore_expires, 0); + ngx_conf_merge_value(conf->lm_factor, prev->lm_factor, 0); + ngx_conf_merge_sec_value(conf->default_expires, prev->default_expires, 0); + + return NULL; +} + + + +static char *ngx_http_proxy_set_pass(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf) +{ + ngx_http_proxy_loc_conf_t *lcf = conf; + + ngx_uint_t i, len; + char *err; + u_char *host; + in_addr_t addr; + ngx_str_t *value; + struct hostent *h; + ngx_http_core_loc_conf_t *clcf; + + + value = cf->args->elts; + + if (ngx_strncasecmp(value[1].data, "http://", 7) != 0) { + return "invalid URL prefix"; + } + + ngx_test_null(lcf->upstream, + ngx_pcalloc(cf->pool, sizeof(ngx_http_proxy_upstream_conf_t)), + NGX_CONF_ERROR); + + lcf->upstream->url.len = value[1].len; + if (!(lcf->upstream->url.data = ngx_palloc(cf->pool, value[1].len + 1))) { + return NGX_CONF_ERROR; + } + ngx_cpystrn(lcf->upstream->url.data, value[1].data, value[1].len + 1); + + value[1].data += 7; + value[1].len -= 7; + + err = ngx_http_proxy_parse_upstream(&value[1], lcf->upstream); + + if (err) { + return err; + } + + ngx_test_null(host, ngx_palloc(cf->pool, lcf->upstream->host.len + 1), + NGX_CONF_ERROR); + ngx_cpystrn(host, lcf->upstream->host.data, lcf->upstream->host.len + 1); + + /* AF_INET only */ + + addr = inet_addr((char *) host); + + if (addr == INADDR_NONE) { + h = gethostbyname((char *) host); + + if (h == NULL || h->h_addr_list[0] == NULL) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "host %s not found", host); + return NGX_CONF_ERROR; + } + + for (i = 0; h->h_addr_list[i] != NULL; i++) { /* void */ } + + /* MP: ngx_shared_palloc() */ + + ngx_test_null(lcf->peers, + ngx_pcalloc(cf->pool, + sizeof(ngx_peers_t) + + sizeof(ngx_peer_t) * (i - 1)), + NGX_CONF_ERROR); + + lcf->peers->number = i; + + for (i = 0; h->h_addr_list[i] != NULL; i++) { + lcf->peers->peers[i].host.data = host; + lcf->peers->peers[i].host.len = lcf->upstream->host.len; + lcf->peers->peers[i].addr = *(in_addr_t *)(h->h_addr_list[i]); + lcf->peers->peers[i].port = lcf->upstream->port; + + len = INET_ADDRSTRLEN + lcf->upstream->port_text.len + 1; + ngx_test_null(lcf->peers->peers[i].addr_port_text.data, + ngx_palloc(cf->pool, len), + NGX_CONF_ERROR); + + len = ngx_inet_ntop(AF_INET, + &lcf->peers->peers[i].addr, + lcf->peers->peers[i].addr_port_text.data, + len); + + lcf->peers->peers[i].addr_port_text.data[len++] = ':'; + + ngx_cpystrn(lcf->peers->peers[i].addr_port_text.data + len, + lcf->upstream->port_text.data, + lcf->upstream->port_text.len + 1); + + lcf->peers->peers[i].addr_port_text.len = + len + lcf->upstream->port_text.len + 1; + } + + } else { + + /* MP: ngx_shared_palloc() */ + + ngx_test_null(lcf->peers, ngx_pcalloc(cf->pool, sizeof(ngx_peers_t)), + NGX_CONF_ERROR); + + lcf->peers->number = 1; + + lcf->peers->peers[0].host.data = host; + lcf->peers->peers[0].host.len = lcf->upstream->host.len; + lcf->peers->peers[0].addr = addr; + lcf->peers->peers[0].port = lcf->upstream->port; + + len = lcf->upstream->host.len + lcf->upstream->port_text.len + 1; + + ngx_test_null(lcf->peers->peers[0].addr_port_text.data, + ngx_palloc(cf->pool, len + 1), + NGX_CONF_ERROR); + + len = lcf->upstream->host.len; + + ngx_memcpy(lcf->peers->peers[0].addr_port_text.data, + lcf->upstream->host.data, len); + + lcf->peers->peers[0].addr_port_text.data[len++] = ':'; + + ngx_cpystrn(lcf->peers->peers[0].addr_port_text.data + len, + lcf->upstream->port_text.data, + lcf->upstream->port_text.len + 1); + } + + clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); + + lcf->upstream->location = &clcf->name; + clcf->handler = ngx_http_proxy_handler; + + if (clcf->name.data[clcf->name.len - 1] == '/') { + clcf->auto_redirect = 1; + } + + return NULL; +} + + +static char *ngx_http_proxy_parse_upstream(ngx_str_t *url, + ngx_http_proxy_upstream_conf_t *u) +{ + size_t i; + + if (url->data[0] == ':' || url->data[0] == '/') { + return "invalid upstream URL"; + } + + u->host.data = url->data; + u->host_header.data = url->data; + + for (i = 1; i < url->len; i++) { + if (url->data[i] == ':') { + u->port_text.data = &url->data[i] + 1; + u->host.len = i; + } + + if (url->data[i] == '/') { + u->uri.data = &url->data[i]; + u->uri.len = url->len - i; + u->host_header.len = i; + + if (u->host.len == 0) { + u->host.len = i; + } + + if (u->port_text.data == NULL) { + u->default_port = 1; + u->port = htons(80); + u->port_text.len = 2; + u->port_text.data = (u_char *) "80"; + return NULL; + } + + u->port_text.len = &url->data[i] - u->port_text.data; + + if (u->port_text.len > 0) { + u->port = (in_port_t) ngx_atoi(u->port_text.data, + u->port_text.len); + if (u->port > 0) { + + if (u->port == 80) { + u->default_port = 1; + } + + u->port = htons(u->port); + return NULL; + } + } + + return "invalid port in upstream URL"; + } + } + + if (u->host.len == 0) { + u->host.len = i; + } + + u->host_header.len = i; + + u->uri.data = (u_char *) "/"; + u->uri.len = 1; + + if (u->port_text.data == NULL) { + u->default_port = 1; + u->port = htons(80); + u->port_text.len = 2; + u->port_text.data = (u_char *) "80"; + return NULL; + } + + u->port_text.len = &url->data[i] - u->port_text.data; + + if (u->port_text.len > 0) { + u->port = (in_port_t) ngx_atoi(u->port_text.data, u->port_text.len); + if (u->port > 0) { + u->port = htons(u->port); + return NULL; + } + } + + return "invalid port in upstream URL"; +} diff --git a/src/http/modules/proxy/ngx_http_proxy_handler.h b/src/http/modules/proxy/ngx_http_proxy_handler.h new file mode 100644 --- /dev/null +++ b/src/http/modules/proxy/ngx_http_proxy_handler.h @@ -0,0 +1,260 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#ifndef _NGX_HTTP_PROXY_HANDLER_H_INCLUDED_ +#define _NGX_HTTP_PROXY_HANDLER_H_INCLUDED_ + + +#include +#include +#include +#include +#include +#include + + +typedef enum { + NGX_HTTP_PROXY_CACHE_PASS = 1, + NGX_HTTP_PROXY_CACHE_BYPASS, + NGX_HTTP_PROXY_CACHE_AUTH, + NGX_HTTP_PROXY_CACHE_PGNC, + NGX_HTTP_PROXY_CACHE_MISS, + NGX_HTTP_PROXY_CACHE_EXPR, + NGX_HTTP_PROXY_CACHE_AGED, + NGX_HTTP_PROXY_CACHE_HIT +} ngx_http_proxy_state_e; + + +typedef enum { + NGX_HTTP_PROXY_CACHE_BPS = 1, + NGX_HTTP_PROXY_CACHE_XAE, + NGX_HTTP_PROXY_CACHE_CTL, + NGX_HTTP_PROXY_CACHE_EXP, + NGX_HTTP_PROXY_CACHE_MVD, + NGX_HTTP_PROXY_CACHE_LMF, + NGX_HTTP_PROXY_CACHE_PDE +} ngx_http_proxy_reason_e; + + +typedef struct { + ngx_str_t url; + ngx_str_t host; + ngx_str_t uri; + ngx_str_t host_header; + ngx_str_t port_text; + ngx_str_t *location; + + in_port_t port; + + unsigned default_port:1; +} ngx_http_proxy_upstream_conf_t; + + +typedef struct { + size_t header_buffer_size; + size_t busy_buffers_size; + size_t max_temp_file_size; + size_t temp_file_write_size; + + ngx_msec_t connect_timeout; + ngx_msec_t send_timeout; + ngx_msec_t read_timeout; + time_t default_expires; + + ngx_int_t lm_factor; + + ngx_uint_t next_upstream; + ngx_uint_t use_stale; + + ngx_bufs_t bufs; + + ngx_flag_t cyclic_temp_file; + ngx_flag_t cache; + ngx_flag_t preserve_host; + ngx_flag_t set_x_real_ip; + ngx_flag_t add_x_forwarded_for; + ngx_flag_t pass_server; + ngx_flag_t pass_x_accel_expires; + ngx_flag_t ignore_expires; + + ngx_path_t *cache_path; + ngx_path_t *temp_path; + + ngx_http_busy_lock_t *busy_lock; + + ngx_http_proxy_upstream_conf_t *upstream; + ngx_peers_t *peers; +} ngx_http_proxy_loc_conf_t; + + +/* + * "EXPR/10/5/- 200/EXP/60 4" + * "MISS/-/-/B 503/-/- -" + * "EXPR/10/20/SB HIT/-/- -" + * "EXPR/10/15/NB HIT/-/- -" + */ + +typedef struct { + ngx_http_proxy_state_e cache_state; + time_t expired; + time_t bl_time; + ngx_uint_t bl_state; + + ngx_uint_t status; + ngx_http_proxy_reason_e reason; + time_t time; + time_t expires; + + ngx_str_t *peer; +} ngx_http_proxy_state_t; + + +typedef struct { + ngx_list_t headers; +#if 0 + ngx_table_t headers; /* it must be first field */ +#endif + + ngx_table_elt_t *date; + ngx_table_elt_t *server; + + ngx_table_elt_t *expires; + ngx_table_elt_t *cache_control; + ngx_table_elt_t *etag; + ngx_table_elt_t *x_accel_expires; + + ngx_table_elt_t *connection; + 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 *x_pad; + + off_t content_length_n; +} ngx_http_proxy_headers_in_t; + + +typedef struct { + ngx_http_cache_ctx_t ctx; + ngx_uint_t status; + ngx_str_t status_line; + + ngx_http_proxy_headers_in_t headers_in; +} ngx_http_proxy_cache_t; + + +typedef struct { + ngx_peer_connection_t peer; + ngx_uint_t status; + ngx_str_t status_line; + ngx_uint_t method; + + ngx_output_chain_ctx_t *output_chain_ctx; + ngx_event_pipe_t *event_pipe; + + ngx_http_proxy_headers_in_t headers_in; +} ngx_http_proxy_upstream_t; + + +typedef struct ngx_http_proxy_ctx_s ngx_http_proxy_ctx_t; + +struct ngx_http_proxy_ctx_s { + ngx_http_request_t *request; + ngx_http_proxy_loc_conf_t *lcf; + ngx_http_proxy_upstream_t *upstream; + ngx_http_proxy_cache_t *cache; + + ngx_buf_t *header_in; + + ngx_http_busy_lock_ctx_t busy_lock; + + unsigned accel:1; + + unsigned cachable:1; + unsigned stale:1; + unsigned try_busy_lock:1; + unsigned busy_locked:1; + unsigned valid_header_in:1; + + unsigned request_sent:1; + unsigned header_sent:1; + + + /* used to parse an upstream HTTP header */ + ngx_uint_t status; + u_char *status_start; + u_char *status_end; + ngx_uint_t status_count; + ngx_uint_t parse_state; + + ngx_http_proxy_state_t *state; + ngx_array_t states; /* of ngx_http_proxy_state_t */ + + /* + * we declare "action" as "char *" because the actions are usually + * the static strings and in the "u_char *" case we have to override + * all the time their types + */ + + char *action; + ngx_http_log_ctx_t *saved_ctx; + ngx_log_handler_pt saved_handler; +}; + + +typedef struct { + ngx_uint_t connection; + ngx_http_proxy_ctx_t *proxy; +} ngx_http_proxy_log_ctx_t; + + +#define NGX_HTTP_PROXY_PARSE_NO_HEADER 30 + + +#define NGX_HTTP_PROXY_FT_ERROR 0x02 +#define NGX_HTTP_PROXY_FT_TIMEOUT 0x04 +#define NGX_HTTP_PROXY_FT_INVALID_HEADER 0x08 +#define NGX_HTTP_PROXY_FT_HTTP_500 0x10 +#define NGX_HTTP_PROXY_FT_HTTP_404 0x20 +#define NGX_HTTP_PROXY_FT_BUSY_LOCK 0x40 +#define NGX_HTTP_PROXY_FT_MAX_WAITING 0x80 + + +int ngx_http_proxy_request_upstream(ngx_http_proxy_ctx_t *p); + +#if (NGX_HTTP_FILE_CACHE) + +int ngx_http_proxy_get_cached_response(ngx_http_proxy_ctx_t *p); +int ngx_http_proxy_send_cached_response(ngx_http_proxy_ctx_t *p); +int ngx_http_proxy_is_cachable(ngx_http_proxy_ctx_t *p); +int ngx_http_proxy_update_cache(ngx_http_proxy_ctx_t *p); + +void ngx_http_proxy_cache_busy_lock(ngx_http_proxy_ctx_t *p); + +#endif + +void ngx_http_proxy_check_broken_connection(ngx_event_t *ev); + +void ngx_http_proxy_busy_lock_handler(ngx_event_t *rev); +void ngx_http_proxy_upstream_busy_lock(ngx_http_proxy_ctx_t *p); + +size_t ngx_http_proxy_log_error(void *data, char *buf, size_t len); +void ngx_http_proxy_finalize_request(ngx_http_proxy_ctx_t *p, int rc); +void ngx_http_proxy_close_connection(ngx_http_proxy_ctx_t *p); + +int ngx_http_proxy_parse_status_line(ngx_http_proxy_ctx_t *p); +int ngx_http_proxy_copy_header(ngx_http_proxy_ctx_t *p, + ngx_http_proxy_headers_in_t *headers_in); + + + +extern ngx_module_t ngx_http_proxy_module; +extern ngx_http_header_t ngx_http_proxy_headers_in[]; + + + +#endif /* _NGX_HTTP_PROXY_HANDLER_H_INCLUDED_ */ diff --git a/src/http/modules/proxy/ngx_http_proxy_header.c b/src/http/modules/proxy/ngx_http_proxy_header.c new file mode 100644 --- /dev/null +++ b/src/http/modules/proxy/ngx_http_proxy_header.c @@ -0,0 +1,198 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include +#include + + +static int ngx_http_proxy_rewrite_location_header(ngx_http_proxy_ctx_t *p, + ngx_table_elt_t *loc); + +int ngx_http_proxy_copy_header(ngx_http_proxy_ctx_t *p, + ngx_http_proxy_headers_in_t *headers_in) +{ + ngx_uint_t i; + ngx_list_part_t *part; + ngx_table_elt_t *ho, *h; + ngx_http_request_t *r; + + r = p->request; + + part = &headers_in->headers.part; + h = part->elts; + +#if 0 + h = headers_in->headers.elts; + for (i = 0; i < headers_in->headers.nelts; i++) { +#endif + + for (i = 0 ; /* void */; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + h = part->elts; + i = 0; + } + + /* ignore some headers */ + + if (&h[i] == headers_in->connection) { + continue; + } + + if (&h[i] == headers_in->x_pad) { + continue; + } + + if (p->accel) { + if (&h[i] == headers_in->date + || &h[i] == headers_in->accept_ranges) { + continue; + } + + if (&h[i] == headers_in->x_accel_expires + && !p->lcf->pass_x_accel_expires) + { + continue; + } + + if (&h[i] == headers_in->server && !p->lcf->pass_server) { + continue; + } + + if (&h[i] == headers_in->location) { + if (ngx_http_proxy_rewrite_location_header(p, &h[i]) + == NGX_ERROR) + { + return NGX_ERROR; + } + + continue; + } + } + + + /* "Content-Type" is handled specially */ + + if (&h[i] == headers_in->content_type) { + r->headers_out.content_type = &h[i]; + r->headers_out.content_type->key.len = 0; + continue; + } + + + /* copy some header pointers and set up r->headers_out */ + + if (!(ho = ngx_list_push(&r->headers_out.headers))) { + return NGX_ERROR; + } + + *ho = h[i]; + + if (&h[i] == headers_in->expires) { + r->headers_out.expires = ho; + continue; + } + + if (&h[i] == headers_in->cache_control) { + r->headers_out.cache_control = ho; + continue; + } + + if (&h[i] == headers_in->etag) { + r->headers_out.etag = ho; + continue; + } + + if (&h[i] == headers_in->last_modified) { + r->headers_out.last_modified = ho; + /* TODO: update r->headers_out.last_modified_time */ + continue; + } + + /* + * ngx_http_header_filter() passes the following headers as is + * and does not handle them specially if they are set: + * r->headers_out.server, + * r->headers_out.date, + * r->headers_out.content_length + */ + + if (&h[i] == headers_in->server) { + r->headers_out.server = ho; + continue; + } + + if (&h[i] == headers_in->date) { + r->headers_out.date = ho; + continue; + } + + if (&h[i] == headers_in->content_length) { + r->headers_out.content_length = ho; + r->headers_out.content_length_n = ngx_atoi(ho->value.data, + ho->value.len); + continue; + } + } + + return NGX_OK; +} + + +static int ngx_http_proxy_rewrite_location_header(ngx_http_proxy_ctx_t *p, + ngx_table_elt_t *loc) +{ + u_char *last; + ngx_table_elt_t *location; + ngx_http_request_t *r; + ngx_http_proxy_upstream_conf_t *uc; + + r = p->request; + uc = p->lcf->upstream; + + if (!(location = ngx_list_push(&r->headers_out.headers))) { + return NGX_ERROR; + } + + /* + * we do not set r->headers_out.location to avoid the handling + * the local redirects without a host name by ngx_http_header_filter() + */ + +#if 0 + r->headers_out.location = location; +#endif + + if (uc->url.len > loc->value.len + || ngx_rstrncmp(loc->value.data, uc->url.data, uc->url.len) != 0) + { + *location = *loc; + return NGX_OK; + } + + /* TODO: proxy_reverse */ + + location->value.len = uc->location->len + + (loc->value.len - uc->url.len) + 1; + if (!(location->value.data = ngx_palloc(r->pool, location->value.len))) { + return NGX_ERROR; + } + + last = ngx_cpymem(location->value.data, + uc->location->data, uc->location->len); + + ngx_cpystrn(last, loc->value.data + uc->url.len, + loc->value.len - uc->url.len + 1); + + return NGX_OK; +} diff --git a/src/http/modules/proxy/ngx_http_proxy_parse.c b/src/http/modules/proxy/ngx_http_proxy_parse.c new file mode 100644 --- /dev/null +++ b/src/http/modules/proxy/ngx_http_proxy_parse.c @@ -0,0 +1,213 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include +#include + + +int ngx_http_proxy_parse_status_line(ngx_http_proxy_ctx_t *p) +{ + u_char ch; + u_char *pos; + 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, + sw_done + } state; + + state = p->parse_state; + pos = p->header_in->pos; + + while (pos < p->header_in->last && state < sw_done) { + 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 < '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 - 3; + } + + break; + + /* space or end of line */ + case sw_space_after_status: + switch (ch) { + case ' ': + state = sw_status_text; + break; + case CR: + state = sw_almost_done; + break; + case LF: + state = sw_done; + break; + 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: + state = sw_done; + break; + } + break; + + /* end of request line */ + case sw_almost_done: + p->status_end = pos - 2; + switch (ch) { + case LF: + state = sw_done; + break; + default: + return NGX_HTTP_PROXY_PARSE_NO_HEADER; + } + break; + + /* suppress warning */ + case sw_done: + break; + } + } + + p->header_in->pos = pos; + + if (state == sw_done) { + if (p->status_end == NULL) { + p->status_end = pos - 1; + } + + p->parse_state = sw_start; + return NGX_OK; + } + + p->parse_state = state; + return NGX_AGAIN; +} diff --git a/src/http/modules/proxy/ngx_http_proxy_upstream.c b/src/http/modules/proxy/ngx_http_proxy_upstream.c new file mode 100644 --- /dev/null +++ b/src/http/modules/proxy/ngx_http_proxy_upstream.c @@ -0,0 +1,1492 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include +#include +#include +#include +#include + + +static ngx_chain_t *ngx_http_proxy_create_request(ngx_http_proxy_ctx_t *p); +static void ngx_http_proxy_init_upstream(void *data); +static void ngx_http_proxy_reinit_upstream(ngx_http_proxy_ctx_t *p); +static void ngx_http_proxy_connect(ngx_http_proxy_ctx_t *p); +static void ngx_http_proxy_send_request(ngx_http_proxy_ctx_t *p); +static void ngx_http_proxy_send_request_handler(ngx_event_t *wev); +static void ngx_http_proxy_dummy_handler(ngx_event_t *wev); +static void ngx_http_proxy_process_upstream_status_line(ngx_event_t *rev); +static void ngx_http_proxy_process_upstream_headers(ngx_event_t *rev); +static ssize_t ngx_http_proxy_read_upstream_header(ngx_http_proxy_ctx_t *); +static void ngx_http_proxy_send_response(ngx_http_proxy_ctx_t *p); +static void ngx_http_proxy_process_body(ngx_event_t *ev); +static void ngx_http_proxy_next_upstream(ngx_http_proxy_ctx_t *p, int ft_type); + + +static ngx_str_t http_methods[] = { + ngx_string("GET "), + ngx_string("HEAD "), + ngx_string("POST ") +}; + + +static char *upstream_header_errors[] = { + "upstream sent invalid header", + "upstream sent too long header line" +}; + + +static char http_version[] = " HTTP/1.0" CRLF; +static char host_header[] = "Host: "; +static char x_real_ip_header[] = "X-Real-IP: "; +static char x_forwarded_for_header[] = "X-Forwarded-For: "; +static char connection_close_header[] = "Connection: close" CRLF; + + +int ngx_http_proxy_request_upstream(ngx_http_proxy_ctx_t *p) +{ + int rc; + ngx_temp_file_t *tf; + ngx_http_request_t *r; + ngx_http_request_body_t *rb; + ngx_http_proxy_upstream_t *u; + + r = p->request; + + if (!(u = ngx_pcalloc(r->pool, sizeof(ngx_http_proxy_upstream_t)))) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + p->upstream = u; + + u->peer.log_error = NGX_ERROR_ERR; + u->peer.peers = p->lcf->peers; + u->peer.tries = p->lcf->peers->number; +#if (NGX_THREADS) + u->peer.lock = &r->connection->lock; +#endif + + u->method = r->method; + + if (!(rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t)))) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + r->request_body = rb; + + if (r->headers_in.content_length_n <= 0) { + ngx_http_proxy_init_upstream(p); + return NGX_DONE; + } + + if (!(tf = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t)))) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + tf->file.fd = NGX_INVALID_FILE; + tf->file.log = r->connection->log; + tf->path = p->lcf->temp_path; + tf->pool = r->pool; + tf->warn = "a client request body is buffered to a temporary file"; + /* tf->persistent = 0; */ + + rb->handler = ngx_http_proxy_init_upstream; + rb->data = p; + /* rb->bufs = NULL; */ + /* rb->buf = NULL; */ + /* rb->rest = 0; */ + + rb->temp_file = tf; + + rc = ngx_http_read_client_request_body(r); + + if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { + return rc; + } + + return NGX_DONE; +} + + +static ngx_chain_t *ngx_http_proxy_create_request(ngx_http_proxy_ctx_t *p) +{ + size_t len; + ngx_uint_t i; + ngx_buf_t *b; + ngx_chain_t *chain; + ngx_list_part_t *part; + ngx_table_elt_t *header; + ngx_http_request_t *r; + ngx_http_proxy_upstream_conf_t *uc; + + r = p->request; + uc = p->lcf->upstream; + + if (p->upstream->method) { + len = http_methods[p->upstream->method - 1].len; + + } else { + len = r->method_name.len; + } + + len += uc->uri.len + + r->uri.len - uc->location->len + + 1 + r->args.len /* 1 is for "?" */ + + sizeof(http_version) - 1 + + sizeof(connection_close_header) - 1 + + 2; /* 2 is for "\r\n" at the header end */ + + + if (p->lcf->preserve_host && r->headers_in.host) { + len += sizeof(host_header) - 1 + + r->headers_in.host_name_len + + 1 /* 1 is for ":" */ + + uc->port_text.len + + 2; /* 2 is for "\r\n" */ + } else { /* 2 is for "\r\n" */ + len += sizeof(host_header) - 1 + uc->host_header.len + 2; + } + + + if (p->lcf->set_x_real_ip) { /* 2 is for "\r\n" */ + len += sizeof(x_real_ip_header) - 1 + INET_ADDRSTRLEN - 1 + 2; + } + + + if (p->lcf->add_x_forwarded_for) { + if (r->headers_in.x_forwarded_for) { + len += sizeof(x_forwarded_for_header) - 1 + + r->headers_in.x_forwarded_for->value.len + + 2 /* 2 is ofr ", " */ + + INET_ADDRSTRLEN - 1 + + 2; /* 2 is for "\r\n" */ + } else { + len += sizeof(x_forwarded_for_header) - 1 + INET_ADDRSTRLEN - 1 + 2; + /* 2 is for "\r\n" */ + } + } + + + 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 (&header[i] == r->headers_in.host) { + continue; + } + + if (&header[i] == r->headers_in.connection) { + continue; + } + + /* 2 is for ": " and 2 is for "\r\n" */ + len += header[i].key.len + 2 + header[i].value.len + 2; + } + +#if (NGX_DEBUG) + len++; +#endif + + ngx_test_null(b, ngx_create_temp_buf(r->pool, len), NULL); + ngx_alloc_link_and_set_buf(chain, b, r->pool, NULL); + + + /* the request line */ + + if (p->upstream->method) { + b->last = ngx_cpymem(b->last, + http_methods[p->upstream->method - 1].data, + http_methods[p->upstream->method - 1].len); + } else { + b->last = ngx_cpymem(b->last, r->method_name.data, r->method_name.len); + } + + b->last = ngx_cpymem(b->last, uc->uri.data, uc->uri.len); + + b->last = ngx_cpymem(b->last, + r->uri.data + uc->location->len, + r->uri.len - uc->location->len); + + if (r->args.len > 0) { + *(b->last++) = '?'; + b->last = ngx_cpymem(b->last, r->args.data, r->args.len); + } + + b->last = ngx_cpymem(b->last, http_version, sizeof(http_version) - 1); + + + /* the "Connection: close" header */ + + b->last = ngx_cpymem(b->last, connection_close_header, + sizeof(connection_close_header) - 1); + + + /* the "Host" header */ + + b->last = ngx_cpymem(b->last, host_header, sizeof(host_header) - 1); + + if (p->lcf->preserve_host && r->headers_in.host) { + b->last = ngx_cpymem(b->last, r->headers_in.host->value.data, + r->headers_in.host_name_len); + + if (!uc->default_port) { + *(b->last++) = ':'; + b->last = ngx_cpymem(b->last, uc->port_text.data, + uc->port_text.len); + } + + } else { + b->last = ngx_cpymem(b->last, uc->host_header.data, + uc->host_header.len); + } + *(b->last++) = CR; *(b->last++) = LF; + + + /* the "X-Real-IP" header */ + + if (p->lcf->set_x_real_ip) { + b->last = ngx_cpymem(b->last, x_real_ip_header, + sizeof(x_real_ip_header) - 1); + b->last = ngx_cpymem(b->last, r->connection->addr_text.data, + r->connection->addr_text.len); + *(b->last++) = CR; *(b->last++) = LF; + } + + + /* the "X-Forwarded-For" header */ + + if (p->lcf->add_x_forwarded_for) { + if (r->headers_in.x_forwarded_for) { + b->last = ngx_cpymem(b->last, x_forwarded_for_header, + sizeof(x_forwarded_for_header) - 1); + + b->last = ngx_cpymem(b->last, + r->headers_in.x_forwarded_for->value.data, + r->headers_in.x_forwarded_for->value.len); + + *(b->last++) = ','; *(b->last++) = ' '; + + } else { + b->last = ngx_cpymem(b->last, x_forwarded_for_header, + sizeof(x_forwarded_for_header) - 1); + } + + b->last = ngx_cpymem(b->last, r->connection->addr_text.data, + r->connection->addr_text.len); + *(b->last++) = CR; *(b->last++) = LF; + } + + + 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 (&header[i] == r->headers_in.host) { + continue; + } + + if (&header[i] == r->headers_in.connection) { + continue; + } + + if (&header[i] == r->headers_in.keep_alive) { + continue; + } + + if (&header[i] == r->headers_in.x_forwarded_for + && p->lcf->add_x_forwarded_for) + { + continue; + } + + b->last = ngx_cpymem(b->last, header[i].key.data, header[i].key.len); + + *(b->last++) = ':'; *(b->last++) = ' '; + + b->last = ngx_cpymem(b->last, header[i].value.data, + header[i].value.len); + + *(b->last++) = CR; *(b->last++) = LF; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http proxy header: \"%s: %s\"", + header[i].key.data, header[i].value.data); + } + + /* add "\r\n" at the header end */ + *(b->last++) = CR; *(b->last++) = LF; + +#if (NGX_DEBUG) + *(b->last) = '\0'; + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http proxy header:\n\"%s\"", b->pos); +#endif + + return chain; +} + + +static void ngx_http_proxy_init_upstream(void *data) +{ + ngx_http_proxy_ctx_t *p = data; + + ngx_chain_t *cl; + ngx_http_request_t *r; + ngx_output_chain_ctx_t *output; + ngx_chain_writer_ctx_t *writer; + ngx_http_proxy_log_ctx_t *ctx; + + r = p->request; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http proxy init upstream, client timer: %d", + r->connection->read->timer_set); + + if (r->connection->read->timer_set) { + ngx_del_timer(r->connection->read); + } + + r->connection->read->event_handler = ngx_http_proxy_check_broken_connection; + + if (ngx_event_flags & NGX_USE_CLEAR_EVENT) { + + r->connection->write->event_handler = + ngx_http_proxy_check_broken_connection; + + if (!r->connection->write->active) { + if (ngx_add_event(r->connection->write, NGX_WRITE_EVENT, + NGX_CLEAR_EVENT) == NGX_ERROR) + { + ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + } + } + + + if (!(cl = ngx_http_proxy_create_request(p))) { + ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + if (r->request_body->bufs) { + cl->next = r->request_body->bufs; + } + + r->request_body->bufs = cl; + + if (!(ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_proxy_log_ctx_t)))) { + ngx_http_proxy_finalize_request(p, NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + ctx->connection = r->connection->number; + ctx->proxy = p; + + p->upstream->peer.log = r->connection->log; + p->saved_ctx = r->connection->log->data; + p->saved_handler = r->connection->log->handler; + r->connection->log->data = ctx; + r->connection->log->handler = ngx_http_proxy_log_error; + p->action = "connecting to upstream"; + + if (!(output = ngx_pcalloc(r->pool, sizeof(ngx_output_chain_ctx_t)))) { + ngx_http_proxy_finalize_request(p, NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + p->upstream->output_chain_ctx = output; + + output->sendfile = r->sendfile; + output->pool = r->pool; + output->bufs.num = 1; + output->tag = (ngx_buf_tag_t) &ngx_http_proxy_module; + output->output_filter = (ngx_output_chain_filter_pt) ngx_chain_writer; + + if (!(writer = ngx_palloc(r->pool, sizeof(ngx_chain_writer_ctx_t)))) { + ngx_http_proxy_finalize_request(p, NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + output->filter_ctx = writer; + writer->pool = r->pool; + +#if 0 + if (p->lcf->busy_lock && p->busy_lock == NULL) { +#else + if (p->lcf->busy_lock && !p->busy_locked) { +#endif + ngx_http_proxy_upstream_busy_lock(p); + } else { + ngx_http_proxy_connect(p); + } +} + + +static void ngx_http_proxy_reinit_upstream(ngx_http_proxy_ctx_t *p) +{ + ngx_chain_t *cl; + ngx_output_chain_ctx_t *output; + + /* reinit the request chain */ + + for (cl = p->request->request_body->bufs; cl; cl = cl->next) { + cl->buf->pos = cl->buf->start; + cl->buf->file_pos = 0; + } + + /* reinit the ngx_output_chain() context */ + + output = p->upstream->output_chain_ctx; + + output->buf = NULL; + output->in = NULL; + output->free = NULL; + output->busy = NULL; + + /* reinit r->header_in buffer */ + + if (p->header_in) { + if (p->cache) { + p->header_in->pos = p->header_in->start + p->cache->ctx.header_size; + p->header_in->last = p->header_in->pos; + + } else { + p->header_in->pos = p->header_in->start; + p->header_in->last = p->header_in->start; + } + } + + /* add one more state */ + + if (!(p->state = ngx_push_array(&p->states))) { + ngx_http_proxy_finalize_request(p, NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + p->status = 0; + p->status_count = 0; +} + + +#if 0 + +void ngx_http_proxy_upstream_busy_lock(ngx_http_proxy_ctx_t *p) +{ + ngx_int_t rc; + + rc = ngx_event_busy_lock(p->lcf->busy_lock, p->busy_lock); + + if (rc == NGX_AGAIN) { + return; + } + + if (rc == NGX_OK) { + ngx_http_proxy_connect(p); + return; + } + + if (rc == NGX_ERROR) { + p->state->status = NGX_HTTP_INTERNAL_SERVER_ERROR; + ngx_http_proxy_finalize_request(p, NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + /* rc == NGX_BUSY */ + +#if (NGX_HTTP_CACHE) + + if (p->busy_lock->timer) { + ft_type = NGX_HTTP_PROXY_FT_MAX_WAITING; + } else { + ft_type = NGX_HTTP_PROXY_FT_BUSY_LOCK; + } + + if (p->stale && (p->lcf->use_stale & ft_type)) { + ngx_http_proxy_finalize_request(p, + ngx_http_proxy_send_cached_response(p)); + return; + } + +#endif + + p->state->status = NGX_HTTP_SERVICE_UNAVAILABLE; + ngx_http_proxy_finalize_request(p, NGX_HTTP_SERVICE_UNAVAILABLE); +} + +#endif + + +#if 1 + +void ngx_http_proxy_upstream_busy_lock(ngx_http_proxy_ctx_t *p) +{ + ngx_int_t rc; +#if (NGX_HTTP_CACHE) + ngx_int_t ft_type; +#endif + + if (p->busy_lock.time == 0) { + p->busy_lock.event = p->request->connection->read; + p->busy_lock.event_handler = ngx_http_proxy_busy_lock_handler; + } + + rc = ngx_http_busy_lock(p->lcf->busy_lock, &p->busy_lock); + + if (rc == NGX_AGAIN) { + return; + } + + if (rc == NGX_OK) { + ngx_http_proxy_connect(p); + return; + } + + ngx_http_busy_unlock(p->lcf->busy_lock, &p->busy_lock); + +#if (NGX_HTTP_CACHE) + + if (rc == NGX_DONE) { + ft_type = NGX_HTTP_PROXY_FT_BUSY_LOCK; + + } else { + /* rc == NGX_ERROR */ + ft_type = NGX_HTTP_PROXY_FT_MAX_WAITING; + } + + if (p->stale && (p->lcf->use_stale & ft_type)) { + ngx_http_proxy_finalize_request(p, + ngx_http_proxy_send_cached_response(p)); + return; + } + +#endif + + p->state->status = NGX_HTTP_SERVICE_UNAVAILABLE; + ngx_http_proxy_finalize_request(p, NGX_HTTP_SERVICE_UNAVAILABLE); +} + +#endif + + +static void ngx_http_proxy_connect(ngx_http_proxy_ctx_t *p) +{ + int rc; + ngx_connection_t *c; + ngx_http_request_t *r; + ngx_output_chain_ctx_t *output; + ngx_chain_writer_ctx_t *writer; + + p->action = "connecting to upstream"; + + p->request->connection->single_connection = 0; + + rc = ngx_event_connect_peer(&p->upstream->peer); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, p->request->connection->log, 0, + "http proxy connect: %d", rc); + + if (rc == NGX_ERROR) { + ngx_http_proxy_finalize_request(p, NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + p->state->peer = + &p->upstream->peer.peers->peers[p->upstream->peer.cur_peer].addr_port_text; + + if (rc == NGX_CONNECT_ERROR) { + ngx_http_proxy_next_upstream(p, NGX_HTTP_PROXY_FT_ERROR); + return; + } + + r = p->request; + c = p->upstream->peer.connection; + + c->data = p; + c->write->event_handler = ngx_http_proxy_send_request_handler; + c->read->event_handler = ngx_http_proxy_process_upstream_status_line; + + c->pool = r->pool; + c->read->log = c->write->log = c->log = r->connection->log; + + /* init or reinit the ngx_output_chain() and ngx_chain_writer() contexts */ + + output = p->upstream->output_chain_ctx; + writer = output->filter_ctx; + writer->out = NULL; + writer->last = &writer->out; + writer->connection = c; + writer->limit = OFF_T_MAX_VALUE; + + if (p->upstream->peer.tries > 1 && p->request_sent) { + ngx_http_proxy_reinit_upstream(p); + } + + if (r->request_body->buf) { + if (r->request_body->temp_file->file.fd != NGX_INVALID_FILE) { + + if (!(output->free = ngx_alloc_chain_link(r->pool))) { + ngx_http_proxy_finalize_request(p, + NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + output->free->buf = r->request_body->buf; + output->free->next = NULL; + output->allocated = 1; + + r->request_body->buf->pos = r->request_body->buf->start; + r->request_body->buf->last = r->request_body->buf->start; + r->request_body->buf->tag = (ngx_buf_tag_t) &ngx_http_proxy_module; + + } else { + r->request_body->buf->pos = r->request_body->buf->start; + } + } + + p->request_sent = 0; + + if (rc == NGX_AGAIN) { + ngx_add_timer(c->write, p->lcf->connect_timeout); + return; + } + + /* rc == NGX_OK */ + +#if 1 /* test only, see below about "post aio operation" */ + + if (c->read->ready) { + /* post aio operation */ + ngx_http_proxy_process_upstream_status_line(c->read); + return; + } + +#endif + + ngx_http_proxy_send_request(p); +} + + +static void ngx_http_proxy_send_request(ngx_http_proxy_ctx_t *p) +{ + int rc; + ngx_connection_t *c; + + c = p->upstream->peer.connection; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http proxy send request"); + +#if (HAVE_KQUEUE) + + if ((ngx_event_flags & NGX_HAVE_KQUEUE_EVENT) + && !p->request_sent + && c->write->pending_eof) + { + ngx_log_error(NGX_LOG_ERR, c->log, c->write->kq_errno, + "connect() failed"); + + ngx_http_proxy_next_upstream(p, NGX_HTTP_PROXY_FT_ERROR); + return; + } + +#endif + + p->action = "sending request to upstream"; + + rc = ngx_output_chain(p->upstream->output_chain_ctx, + p->request_sent ? NULL: + p->request->request_body->bufs); + + if (rc == NGX_ERROR) { + ngx_http_proxy_next_upstream(p, NGX_HTTP_PROXY_FT_ERROR); + return; + } + + p->request_sent = 1; + + if (c->write->timer_set) { + ngx_del_timer(c->write); + } + + if (rc == NGX_AGAIN) { + ngx_add_timer(c->write, p->lcf->send_timeout); + + c->write->available = /* STUB: lowat */ 0; + if (ngx_handle_write_event(c->write, NGX_LOWAT_EVENT) == NGX_ERROR) { + ngx_http_proxy_finalize_request(p, NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + return; + } + + /* rc == NGX_OK */ + + if (c->tcp_nopush == NGX_TCP_NOPUSH_SET) { + if (ngx_tcp_push(c->fd) == NGX_ERROR) { + ngx_log_error(NGX_LOG_CRIT, c->log, + ngx_socket_errno, + ngx_tcp_push_n " failed"); + ngx_http_proxy_finalize_request(p, NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + c->tcp_nopush = NGX_TCP_NOPUSH_UNSET; + return; + } + + ngx_add_timer(c->read, p->lcf->read_timeout); + +#if 0 + if (c->read->ready) { + + /* post aio operation */ + + /* + * although we can post aio operation just in the end + * of ngx_http_proxy_connect() CHECK IT !!! + * it's better to do here because we postpone header buffer allocation + */ + + ngx_http_proxy_process_upstream_status_line(c->read); + return; + } +#endif + + c->write->event_handler = ngx_http_proxy_dummy_handler; + + if (ngx_handle_level_write_event(c->write) == NGX_ERROR) { + ngx_http_proxy_finalize_request(p, NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } +} + + +static void ngx_http_proxy_send_request_handler(ngx_event_t *wev) +{ + ngx_connection_t *c; + ngx_http_proxy_ctx_t *p; + + c = wev->data; + p = c->data; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, wev->log, 0, + "http proxy send request handler"); + + if (wev->timedout) { + p->action = "sending request to upstream"; + ngx_http_proxy_next_upstream(p, NGX_HTTP_PROXY_FT_TIMEOUT); + return; + } + + if (p->request->connection->write->eof + && (!p->cachable || !p->request_sent)) + { + ngx_http_proxy_finalize_request(p, NGX_HTTP_CLIENT_CLOSED_REQUEST); + return; + } + + ngx_http_proxy_send_request(p); +} + + +static void ngx_http_proxy_dummy_handler(ngx_event_t *wev) +{ + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, wev->log, 0, "http proxy dummy handler"); +} + + +static void ngx_http_proxy_process_upstream_status_line(ngx_event_t *rev) +{ + int rc; + ssize_t n; + ngx_connection_t *c; + ngx_http_proxy_ctx_t *p; + + c = rev->data; + p = c->data; + p->action = "reading upstream status line"; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, + "http proxy process status line"); + + if (rev->timedout) { + ngx_http_proxy_next_upstream(p, NGX_HTTP_PROXY_FT_TIMEOUT); + return; + } + + if (p->header_in == NULL) { + p->header_in = ngx_create_temp_buf(p->request->pool, + p->lcf->header_buffer_size); + if (p->header_in == NULL) { + ngx_http_proxy_finalize_request(p, NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + p->header_in->tag = (ngx_buf_tag_t) &ngx_http_proxy_module; + + if (p->cache) { + p->header_in->pos += p->cache->ctx.header_size; + p->header_in->last = p->header_in->pos; + } + } + + n = ngx_http_proxy_read_upstream_header(p); + + if (n == NGX_AGAIN) { + return; + } + + if (n == 0) { + ngx_log_error(NGX_LOG_ERR, rev->log, 0, + "upstream prematurely closed connection"); + } + + if (n == NGX_ERROR || n == 0) { + ngx_http_proxy_next_upstream(p, NGX_HTTP_PROXY_FT_ERROR); + return; + } + + p->valid_header_in = 0; + + p->upstream->peer.cached = 0; + + rc = ngx_http_proxy_parse_status_line(p); + + if (rc == NGX_AGAIN) { + if (p->header_in->pos == p->header_in->last) { + ngx_log_error(NGX_LOG_ERR, rev->log, 0, + "upstream sent too long status line"); + ngx_http_proxy_next_upstream(p, NGX_HTTP_PROXY_FT_INVALID_HEADER); + } + return; + } + + if (rc == NGX_HTTP_PROXY_PARSE_NO_HEADER) { + ngx_log_error(NGX_LOG_ERR, rev->log, 0, + "upstream sent no valid HTTP/1.0 header"); + + if (p->accel) { + ngx_http_proxy_next_upstream(p, NGX_HTTP_PROXY_FT_INVALID_HEADER); + + } else { + p->request->http_version = NGX_HTTP_VERSION_9; + p->upstream->status = NGX_HTTP_OK; + ngx_http_proxy_send_response(p); + } + + return; + } + + /* rc == NGX_OK */ + + p->upstream->status = p->status; + p->state->status = p->status; + + if (p->status == NGX_HTTP_INTERNAL_SERVER_ERROR) { + + if (p->upstream->peer.tries > 1 + && (p->lcf->next_upstream & NGX_HTTP_PROXY_FT_HTTP_500)) + { + ngx_http_proxy_next_upstream(p, NGX_HTTP_PROXY_FT_HTTP_500); + return; + } + +#if (NGX_HTTP_CACHE) + + if (p->upstream->peer.tries == 0 + && p->stale + && (p->lcf->use_stale & NGX_HTTP_PROXY_FT_HTTP_500)) + { + ngx_http_proxy_finalize_request(p, + ngx_http_proxy_send_cached_response(p)); + + return; + } + +#endif + } + + if (p->status == NGX_HTTP_NOT_FOUND + && p->upstream->peer.tries > 1 + && p->lcf->next_upstream & NGX_HTTP_PROXY_FT_HTTP_404) + { + ngx_http_proxy_next_upstream(p, NGX_HTTP_PROXY_FT_HTTP_404); + return; + } + + /* TODO: "proxy_error_page" */ + + p->upstream->status_line.len = p->status_end - p->status_start; + p->upstream->status_line.data = ngx_palloc(p->request->pool, + p->upstream->status_line.len + 1); + if (p->upstream->status_line.data == NULL) { + ngx_http_proxy_finalize_request(p, NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + ngx_cpystrn(p->upstream->status_line.data, p->status_start, + p->upstream->status_line.len + 1); + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, rev->log, 0, + "http proxy status %d \"%s\"", + p->upstream->status, p->upstream->status_line.data); + + + /* init or reinit the p->upstream->headers_in.headers table */ + + if (p->upstream->headers_in.headers.part.elts) { + p->upstream->headers_in.headers.part.nelts = 0; + p->upstream->headers_in.headers.part.next = NULL; + p->upstream->headers_in.headers.last = + &p->upstream->headers_in.headers.part; + + ngx_memzero(&p->upstream->headers_in.date, + sizeof(ngx_http_proxy_headers_in_t) - sizeof(ngx_list_t)); + + } else { + if (ngx_list_init(&p->upstream->headers_in.headers, p->request->pool, + 20, sizeof(ngx_table_elt_t)) == NGX_ERROR) + { + ngx_http_proxy_finalize_request(p, NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + } + + + c->read->event_handler = ngx_http_proxy_process_upstream_headers; + ngx_http_proxy_process_upstream_headers(rev); +} + + +static void ngx_http_proxy_process_upstream_headers(ngx_event_t *rev) +{ + int i, rc; + ssize_t n; + ngx_table_elt_t *h; + ngx_connection_t *c; + ngx_http_request_t *r; + ngx_http_proxy_ctx_t *p; + + c = rev->data; + p = c->data; + r = p->request; + p->action = "reading upstream headers"; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, + "http proxy process header line"); + + if (rev->timedout) { + ngx_http_proxy_next_upstream(p, NGX_HTTP_PROXY_FT_TIMEOUT); + return; + } + + rc = NGX_AGAIN; + + for ( ;; ) { + if (rc == NGX_AGAIN) { + n = ngx_http_proxy_read_upstream_header(p); + + if (n == 0) { + ngx_log_error(NGX_LOG_ERR, rev->log, 0, + "upstream prematurely closed connection"); + } + + if (n == NGX_ERROR || n == 0) { + ngx_http_proxy_next_upstream(p, NGX_HTTP_PROXY_FT_ERROR); + return; + } + + if (n == NGX_AGAIN) { + return; + } + } + + rc = ngx_http_parse_header_line(p->request, p->header_in); + + if (rc == NGX_OK) { + + /* a header line has been parsed successfully */ + + if (!(h = ngx_list_push(&p->upstream->headers_in.headers))) { + ngx_http_proxy_finalize_request(p, + NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + 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(p->request->pool, + h->key.len + 1 + h->value.len + 1); + if (h->key.data == NULL) { + ngx_http_proxy_finalize_request(p, + NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + h->value.data = h->key.data + h->key.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); + + for (i = 0; ngx_http_proxy_headers_in[i].name.len != 0; i++) { + if (ngx_http_proxy_headers_in[i].name.len != h->key.len) { + continue; + } + + if (ngx_strcasecmp(ngx_http_proxy_headers_in[i].name.data, + h->key.data) == 0) + { + *((ngx_table_elt_t **) ((char *) &p->upstream->headers_in + + ngx_http_proxy_headers_in[i].offset)) = h; + break; + } + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http proxy header: \"%s: %s\"", + h->key.data, h->value.data); + + continue; + + } else if (rc == NGX_HTTP_PARSE_HEADER_DONE) { + + /* a whole header has been parsed successfully */ + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http proxy header done"); + + /* TODO: hook to process the upstream header */ + +#if (NGX_HTTP_CACHE) + + if (p->cachable) { + p->cachable = ngx_http_proxy_is_cachable(p); + } + +#endif + + ngx_http_proxy_send_response(p); + return; + + } else if (rc != NGX_AGAIN) { + + /* there was error while a header line parsing */ + + ngx_log_error(NGX_LOG_ERR, rev->log, 0, + upstream_header_errors[rc - NGX_HTTP_PARSE_HEADER_ERROR]); + + ngx_http_proxy_next_upstream(p, NGX_HTTP_PROXY_FT_INVALID_HEADER); + return; + } + + /* rc == NGX_AGAIN: a header line parsing is still not complete */ + + if (p->header_in->last == p->header_in->end) { + ngx_log_error(NGX_LOG_ERR, rev->log, 0, + "upstream sent too big header"); + + ngx_http_proxy_next_upstream(p, NGX_HTTP_PROXY_FT_INVALID_HEADER); + return; + } + } +} + + +static ssize_t ngx_http_proxy_read_upstream_header(ngx_http_proxy_ctx_t *p) +{ + ssize_t n; + ngx_event_t *rev; + + rev = p->upstream->peer.connection->read; + + n = p->header_in->last - p->header_in->pos; + + if (n > 0) { + return n; + } + + n = ngx_recv(p->upstream->peer.connection, p->header_in->last, + p->header_in->end - p->header_in->last); + + if (n == NGX_AGAIN) { +#if 0 + ngx_add_timer(rev, p->lcf->read_timeout); +#endif + + if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { + ngx_http_proxy_finalize_request(p, NGX_HTTP_INTERNAL_SERVER_ERROR); + return NGX_ERROR; + } + + return NGX_AGAIN; + } + + if (n == 0) { + ngx_log_error(NGX_LOG_ERR, rev->log, 0, + "upstream closed prematurely connection"); + } + + if (n == 0 || n == NGX_ERROR) { + return NGX_ERROR; + } + + p->header_in->last += n; + + return n; +} + + +static void ngx_http_proxy_send_response(ngx_http_proxy_ctx_t *p) +{ + int rc; + ngx_event_pipe_t *ep; + ngx_http_request_t *r; + ngx_http_cache_header_t *header; + ngx_http_core_loc_conf_t *clcf; + + r = p->request; + + r->headers_out.status = p->upstream->status; + +#if 0 + r->headers_out.content_length_n = -1; + r->headers_out.content_length = NULL; +#endif + + /* copy an upstream header to r->headers_out */ + + if (ngx_http_proxy_copy_header(p, &p->upstream->headers_in) == NGX_ERROR) { + ngx_http_proxy_finalize_request(p, NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + /* TODO: preallocate event_pipe bufs, look "Content-Length" */ + + rc = ngx_http_send_header(r); + + p->header_sent = 1; + + if (p->cache && p->cache->ctx.file.fd != NGX_INVALID_FILE) { + if (ngx_close_file(p->cache->ctx.file.fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno, + ngx_close_file_n " \"%s\" failed", + p->cache->ctx.file.name.data); + } + } + + if (p->cachable) { + header = (ngx_http_cache_header_t *) p->header_in->start; + + header->expires = p->cache->ctx.expires; + header->last_modified = p->cache->ctx.last_modified; + header->date = p->cache->ctx.date; + header->length = r->headers_out.content_length_n; + p->cache->ctx.length = r->headers_out.content_length_n; + + header->key_len = p->cache->ctx.key.len; + ngx_memcpy(&header->key, p->cache->ctx.key.data, header->key_len); + header->key[header->key_len] = LF; + } + + ep = ngx_pcalloc(r->pool, sizeof(ngx_event_pipe_t)); + if (ep == NULL) { + ngx_http_proxy_finalize_request(p, 0); + return; + } + + p->upstream->event_pipe = ep; + + ep->input_filter = ngx_event_pipe_copy_input_filter; + ep->output_filter = (ngx_event_pipe_output_filter_pt) + ngx_http_output_filter; + ep->output_ctx = r; + ep->tag = (ngx_buf_tag_t) &ngx_http_proxy_module; + ep->bufs = p->lcf->bufs; + ep->busy_size = p->lcf->busy_buffers_size; + ep->upstream = p->upstream->peer.connection; + ep->downstream = r->connection; + ep->pool = r->pool; + ep->log = r->connection->log; + + ep->cachable = p->cachable; + + if (!(ep->temp_file = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t)))) { + ngx_http_proxy_finalize_request(p, 0); + return; + } + + ep->temp_file->file.fd = NGX_INVALID_FILE; + ep->temp_file->file.log = r->connection->log; + ep->temp_file->path = p->lcf->temp_path; + ep->temp_file->pool = r->pool; + + if (p->cachable) { + ep->temp_file->persistent = 1; + } else { + ep->temp_file->warn = "an upstream response is buffered " + "to a temporary file"; + } + + ep->max_temp_file_size = p->lcf->max_temp_file_size; + ep->temp_file_write_size = p->lcf->temp_file_write_size; + + if (!(ep->preread_bufs = ngx_alloc_chain_link(r->pool))) { + ngx_http_proxy_finalize_request(p, 0); + return; + } + ep->preread_bufs->buf = p->header_in; + ep->preread_bufs->next = NULL; + + ep->preread_size = p->header_in->last - p->header_in->pos; + + if (p->cachable) { + ep->buf_to_file = ngx_calloc_buf(r->pool); + if (ep->buf_to_file == NULL) { + ngx_http_proxy_finalize_request(p, 0); + return; + } + ep->buf_to_file->pos = p->header_in->start; + ep->buf_to_file->last = p->header_in->pos; + ep->buf_to_file->temporary = 1; + } + + if (ngx_event_flags & NGX_USE_AIO_EVENT) { + /* the posted aio operation can currupt a shadow buffer */ + ep->single_buf = 1; + } + + /* TODO: ep->free_bufs = 0 if use ngx_create_chain_of_bufs() */ + ep->free_bufs = 1; + + /* + * event_pipe would do p->header_in->last += ep->preread_size + * as though these bytes were read. + */ + p->header_in->last = p->header_in->pos; + + if (p->lcf->cyclic_temp_file) { + + /* + * we need to disable the use of sendfile() if we use cyclic temp file + * because the writing a new data can interfere with sendfile() + * that uses the same kernel file pages (at least on FreeBSD) + */ + + ep->cyclic_temp_file = 1; + r->sendfile = 0; + + } else { + ep->cyclic_temp_file = 0; + r->sendfile = 1; + } + + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + ep->read_timeout = p->lcf->read_timeout; + ep->send_timeout = clcf->send_timeout; + ep->send_lowat = clcf->send_lowat; + + p->upstream->peer.connection->read->event_handler = + ngx_http_proxy_process_body; + r->connection->write->event_handler = ngx_http_proxy_process_body; + + ngx_http_proxy_process_body(p->upstream->peer.connection->read); + + return; +} + + +static void ngx_http_proxy_process_body(ngx_event_t *ev) +{ + ngx_connection_t *c; + ngx_http_request_t *r; + ngx_http_proxy_ctx_t *p; + ngx_event_pipe_t *ep; + + c = ev->data; + + if (ev->write) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ev->log, 0, + "http proxy process downstream"); + r = c->data; + p = ngx_http_get_module_ctx(r, ngx_http_proxy_module); + p->action = "sending to client"; + + } else { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ev->log, 0, + "http proxy process upstream"); + p = c->data; + r = p->request; + p->action = "reading upstream body"; + } + + ep = p->upstream->event_pipe; + + if (ev->timedout) { + if (ev->write) { + ep->downstream_error = 1; + ngx_log_error(NGX_LOG_ERR, c->log, NGX_ETIMEDOUT, + "client timed out"); + + } else { + ep->upstream_error = 1; + ngx_log_error(NGX_LOG_ERR, c->log, NGX_ETIMEDOUT, + "upstream timed out"); + } + + } else { + if (ngx_event_pipe(ep, ev->write) == NGX_ABORT) { + ngx_http_proxy_finalize_request(p, 0); + return; + } + } + + if (p->upstream->peer.connection) { + +#if (NGX_HTTP_FILE_CACHE) + + if (ep->upstream_done && p->cachable) { + if (ngx_http_proxy_update_cache(p) == NGX_ERROR) { + ngx_http_busy_unlock(p->lcf->busy_lock, &p->busy_lock); + ngx_http_proxy_finalize_request(p, 0); + return; + } + + } else if (ep->upstream_eof && p->cachable) { + + /* TODO: check length & update cache */ + + if (ngx_http_proxy_update_cache(p) == NGX_ERROR) { + ngx_http_busy_unlock(p->lcf->busy_lock, &p->busy_lock); + ngx_http_proxy_finalize_request(p, 0); + return; + } + } + +#endif + + if (ep->upstream_done || ep->upstream_eof || ep->upstream_error) { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ev->log, 0, + "http proxy upstream exit: " PTR_FMT, ep->out); + ngx_http_busy_unlock(p->lcf->busy_lock, &p->busy_lock); + ngx_http_proxy_finalize_request(p, 0); + return; + } + } + + if (ep->downstream_error) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ev->log, 0, + "http proxy downstream error"); + if (!p->cachable && p->upstream->peer.connection) { + ngx_http_proxy_finalize_request(p, 0); + } + } +} + + +static void ngx_http_proxy_next_upstream(ngx_http_proxy_ctx_t *p, int ft_type) +{ + int status; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, p->request->connection->log, 0, + "http proxy next upstream: %d", ft_type); + + ngx_http_busy_unlock(p->lcf->busy_lock, &p->busy_lock); + + if (ft_type != NGX_HTTP_PROXY_FT_HTTP_404) { + ngx_event_connect_peer_failed(&p->upstream->peer); + } + + if (ft_type == NGX_HTTP_PROXY_FT_TIMEOUT) { + ngx_log_error(NGX_LOG_ERR, p->request->connection->log, NGX_ETIMEDOUT, + "upstream timed out"); + } + + if (p->upstream->peer.cached && ft_type == NGX_HTTP_PROXY_FT_ERROR) { + status = 0; + + } else { + switch(ft_type) { + case NGX_HTTP_PROXY_FT_TIMEOUT: + status = NGX_HTTP_GATEWAY_TIME_OUT; + break; + + case NGX_HTTP_PROXY_FT_HTTP_500: + status = NGX_HTTP_INTERNAL_SERVER_ERROR; + break; + + case NGX_HTTP_PROXY_FT_HTTP_404: + status = NGX_HTTP_NOT_FOUND; + break; + + /* + * NGX_HTTP_PROXY_FT_BUSY_LOCK and NGX_HTTP_PROXY_FT_MAX_WAITING + * never reach here + */ + + default: + status = NGX_HTTP_BAD_GATEWAY; + } + } + + if (p->upstream->peer.connection) { + ngx_http_proxy_close_connection(p); + } + + if (p->request->connection->write->eof) { + ngx_http_proxy_finalize_request(p, NGX_HTTP_CLIENT_CLOSED_REQUEST); + return; + } + + if (status) { + p->state->status = status; + + if (p->upstream->peer.tries == 0 || !(p->lcf->next_upstream & ft_type)) + { + +#if (NGX_HTTP_CACHE) + + if (p->stale && (p->lcf->use_stale & ft_type)) { + ngx_http_proxy_finalize_request(p, + ngx_http_proxy_send_cached_response(p)); + return; + } + +#endif + + ngx_http_proxy_finalize_request(p, status); + return; + } + } + + if (p->lcf->busy_lock && !p->busy_locked) { + ngx_http_proxy_upstream_busy_lock(p); + } else { + ngx_http_proxy_connect(p); + } +} diff --git a/src/http/ngx_http.c b/src/http/ngx_http.c new file mode 100644 --- /dev/null +++ b/src/http/ngx_http.c @@ -0,0 +1,642 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include +#include + + +static char *ngx_http_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +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); + +int ngx_http_max_module; + +ngx_uint_t ngx_http_total_requests; +uint64_t ngx_http_total_sent; + + +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); + + +static ngx_command_t ngx_http_commands[] = { + + {ngx_string("http"), + NGX_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS, + ngx_http_block, + 0, + 0, + NULL}, + + ngx_null_command +}; + + +static ngx_core_module_t ngx_http_module_ctx = { + ngx_string("http"), + NULL, + NULL +}; + + +ngx_module_t ngx_http_module = { + NGX_MODULE, + &ngx_http_module_ctx, /* module context */ + ngx_http_commands, /* module directives */ + NGX_CORE_MODULE, /* module type */ + NULL, /* init module */ + NULL /* init child */ +}; + + +static char *ngx_http_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + char *rv; + ngx_uint_t mi, m, s, l, p, a, n; + ngx_uint_t port_found, addr_found, virtual_names; + ngx_conf_t pcf; + ngx_array_t in_ports; + ngx_listening_t *ls; + ngx_http_listen_t *lscf; + ngx_http_module_t *module; + ngx_http_handler_pt *h; + ngx_http_conf_ctx_t *ctx; + ngx_http_in_port_t *in_port, *inport; + ngx_http_in_addr_t *in_addr, *inaddr; + ngx_http_server_name_t *s_name, *name; + ngx_http_core_srv_conf_t **cscfp, *cscf; + ngx_http_core_loc_conf_t *clcf; + ngx_http_core_main_conf_t *cmcf; +#if (WIN32) + ngx_iocp_conf_t *iocpcf; +#endif + + /* the main http context */ + ngx_test_null(ctx, + ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t)), + NGX_CONF_ERROR); + + *(ngx_http_conf_ctx_t **) conf = ctx; + + /* count the number of the http modules and set up their indices */ + + ngx_http_max_module = 0; + for (m = 0; ngx_modules[m]; m++) { + if (ngx_modules[m]->type != NGX_HTTP_MODULE) { + continue; + } + + ngx_modules[m]->ctx_index = ngx_http_max_module++; + } + + /* the main http main_conf, it's the same in the all http contexts */ + ngx_test_null(ctx->main_conf, + ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module), + NGX_CONF_ERROR); + + /* the http null srv_conf, it's used to merge the server{}s' srv_conf's */ + ngx_test_null(ctx->srv_conf, + ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module), + NGX_CONF_ERROR); + + /* the http null loc_conf, it's used to merge the server{}s' loc_conf's */ + ngx_test_null(ctx->loc_conf, + ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module), + NGX_CONF_ERROR); + + + /* create the main_conf, srv_conf and loc_conf in all http modules */ + + 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->pre_conf) { + if (module->pre_conf(cf) != NGX_OK) { + return NGX_CONF_ERROR; + } + } + + if (module->create_main_conf) { + ngx_test_null(ctx->main_conf[mi], module->create_main_conf(cf), + NGX_CONF_ERROR); + } + + if (module->create_srv_conf) { + ngx_test_null(ctx->srv_conf[mi], module->create_srv_conf(cf), + NGX_CONF_ERROR); + } + + if (module->create_loc_conf) { + ngx_test_null(ctx->loc_conf[mi], module->create_loc_conf(cf), + NGX_CONF_ERROR); + } + } + + /* parse inside the http{} block */ + + pcf = *cf; + cf->ctx = ctx; + cf->module_type = NGX_HTTP_MODULE; + cf->cmd_type = NGX_HTTP_MAIN_CONF; + rv = ngx_conf_parse(cf, NULL); + + if (rv != NGX_CONF_OK) { + *cf = pcf; + return rv; + } + + /* + * init http{} main_conf's, merge the server{}s' srv_conf's + * and its location{}s' loc_conf's + */ + + cmcf = ctx->main_conf[ngx_http_core_module.ctx_index]; + cscfp = cmcf->servers.elts; + + 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; + + /* init http{} main_conf's */ + + if (module->init_main_conf) { + rv = module->init_main_conf(cf, ctx->main_conf[mi]); + if (rv != NGX_CONF_OK) { + *cf = pcf; + return rv; + } + } + + for (s = 0; s < cmcf->servers.nelts; s++) { + + /* merge the server{}s' srv_conf's */ + + 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; + } + } + + if (module->merge_loc_conf) { + + /* merge the server{}'s loc_conf */ + + 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; + } + + /* 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 0 + clcfp = (ngx_http_core_loc_conf_t **) cscfp[s]->locations.elts; + + for (l = 0; l < cscfp[s]->locations.nelts; l++) { + rv = module->merge_loc_conf(cf, + cscfp[s]->ctx->loc_conf[mi], + clcfp[l]->loc_conf[mi]); + if (rv != NGX_CONF_OK) { + *cf = pcf; + return rv; + } + } +#endif + } + } + } + + /* we needed "http"'s cf->ctx while merging configuration */ + *cf = pcf; + + /* init lists of the handlers */ + + ngx_init_array(cmcf->phases[NGX_HTTP_REWRITE_PHASE].handlers, + cf->cycle->pool, 10, sizeof(ngx_http_handler_pt), + NGX_CONF_ERROR); + cmcf->phases[NGX_HTTP_REWRITE_PHASE].type = NGX_OK; + + + /* the special find config phase for single handler */ + + ngx_init_array(cmcf->phases[NGX_HTTP_FIND_CONFIG_PHASE].handlers, + cf->cycle->pool, 1, sizeof(ngx_http_handler_pt), + NGX_CONF_ERROR); + cmcf->phases[NGX_HTTP_FIND_CONFIG_PHASE].type = NGX_OK; + + ngx_test_null(h, ngx_push_array( + &cmcf->phases[NGX_HTTP_FIND_CONFIG_PHASE].handlers), + NGX_CONF_ERROR); + *h = ngx_http_find_location_config; + + + ngx_init_array(cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers, + cf->cycle->pool, 10, sizeof(ngx_http_handler_pt), + NGX_CONF_ERROR); + cmcf->phases[NGX_HTTP_ACCESS_PHASE].type = NGX_DECLINED; + + + ngx_init_array(cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers, + cf->cycle->pool, 10, sizeof(ngx_http_handler_pt), + NGX_CONF_ERROR); + cmcf->phases[NGX_HTTP_CONTENT_PHASE].type = NGX_OK; + + + /* + * create the lists of the ports, the addresses and the server names + * to allow quickly find the server core module configuration at run-time + */ + + ngx_init_array(in_ports, cf->pool, 10, sizeof(ngx_http_in_port_t), + NGX_CONF_ERROR); + + /* "server" directives */ + cscfp = cmcf->servers.elts; + for (s = 0; s < cmcf->servers.nelts; s++) { + + /* "listen" directives */ + lscf = cscfp[s]->listen.elts; + for (l = 0; l < cscfp[s]->listen.nelts; l++) { + + port_found = 0; + + /* AF_INET only */ + + in_port = in_ports.elts; + for (p = 0; p < in_ports.nelts; p++) { + + if (lscf[l].port == in_port[p].port) { + + /* the port is already in the port list */ + + port_found = 1; + addr_found = 0; + + in_addr = in_port[p].addrs.elts; + for (a = 0; a < in_port[p].addrs.nelts; a++) { + + if (lscf[l].addr == in_addr[a].addr) { + + /* the address is already bound to this port */ + + /* "server_name" directives */ + s_name = cscfp[s]->server_names.elts; + for (n = 0; n < cscfp[s]->server_names.nelts; n++) { + + /* + * add the server name and server core module + * configuration to the address:port + */ + + /* TODO: duplicate names can be checked here */ + + ngx_test_null(name, + ngx_push_array(&in_addr[a].names), + NGX_CONF_ERROR); + + name->name = s_name[n].name; + name->core_srv_conf = s_name[n].core_srv_conf; + } + + /* + * check duplicate "default" server that + * serves this address:port + */ + + if (lscf[l].default_server) { + if (in_addr[a].default_server) { + ngx_log_error(NGX_LOG_ERR, cf->log, 0, + "duplicate default server in %s:%d", + lscf[l].file_name.data, + lscf[l].line); + + return NGX_CONF_ERROR; + } + + in_addr[a].core_srv_conf = cscfp[s]; + in_addr[a].default_server = 1; + } + + addr_found = 1; + + break; + + } else if (in_addr[a].addr == INADDR_ANY) { + + /* + * "*:port" must be the last resort so move it + * to the end of the address list and add + * the new address at its place + */ + + ngx_test_null(inaddr, + ngx_push_array(&in_port[p].addrs), + NGX_CONF_ERROR); + + ngx_memcpy(inaddr, &in_addr[a], + sizeof(ngx_http_in_addr_t)); + + in_addr[a].addr = lscf[l].addr; + in_addr[a].default_server = lscf[l].default_server; + in_addr[a].core_srv_conf = cscfp[s]; + + /* + * create the empty list of the server names that + * can be served on this address:port + */ + + ngx_init_array(inaddr->names, cf->pool, 10, + sizeof(ngx_http_server_name_t), + NGX_CONF_ERROR); + + addr_found = 1; + + break; + } + } + + if (!addr_found) { + + /* + * add the address to the addresses list that + * bound to this port + */ + + ngx_test_null(inaddr, + ngx_push_array(&in_port[p].addrs), + NGX_CONF_ERROR); + + inaddr->addr = lscf[l].addr; + inaddr->default_server = lscf[l].default_server; + inaddr->core_srv_conf = cscfp[s]; + + /* + * create the empty list of the server names that + * can be served on this address:port + */ + + ngx_init_array(inaddr->names, cf->pool, 10, + sizeof(ngx_http_server_name_t), + NGX_CONF_ERROR); + } + } + } + + if (!port_found) { + + /* add the port to the in_port list */ + + ngx_test_null(in_port, + ngx_push_array(&in_ports), + NGX_CONF_ERROR); + + in_port->port = lscf[l].port; + + ngx_test_null(in_port->port_text.data, ngx_palloc(cf->pool, 7), + NGX_CONF_ERROR); + in_port->port_text.len = ngx_snprintf((char *) + in_port->port_text.data, + 7, ":%d", + in_port->port); + + /* create list of the addresses that bound to this port ... */ + + ngx_init_array(in_port->addrs, cf->pool, 10, + sizeof(ngx_http_in_addr_t), + NGX_CONF_ERROR); + + ngx_test_null(inaddr, ngx_push_array(&in_port->addrs), + NGX_CONF_ERROR); + + /* ... and add the address to this list */ + + inaddr->addr = lscf[l].addr; + inaddr->default_server = lscf[l].default_server; + inaddr->core_srv_conf = cscfp[s]; + + /* + * create the empty list of the server names that + * can be served on this address:port + */ + + ngx_init_array(inaddr->names, cf->pool, 10, + sizeof(ngx_http_server_name_t), + NGX_CONF_ERROR); + } + } + } + + /* optimize the lists of the ports, the addresses and the server names */ + + /* AF_INET only */ + + in_port = in_ports.elts; + for (p = 0; p < in_ports.nelts; p++) { + + /* check whether the all server names point to the same server */ + + in_addr = in_port[p].addrs.elts; + for (a = 0; a < in_port[p].addrs.nelts; a++) { + + virtual_names = 0; + + name = in_addr[a].names.elts; + for (n = 0; n < in_addr[a].names.nelts; n++) { + if (in_addr[a].core_srv_conf != name[n].core_srv_conf) { + virtual_names = 1; + break; + } + } + + /* + * if the all server names point to the same server + * then we do not need to check them at run-time + */ + + if (!virtual_names) { + in_addr[a].names.nelts = 0; + } + } + + /* + * if there's the binding to "*:port" then we need to bind() + * to "*:port" only and ignore the other bindings + */ + + if (in_addr[a - 1].addr == INADDR_ANY) { + a--; + + } else { + a = 0; + } + + in_addr = in_port[p].addrs.elts; + while (a < in_port[p].addrs.nelts) { + + ls = ngx_listening_inet_stream_socket(cf, in_addr[a].addr, + in_port[p].port); + if (ls == NULL) { + return NGX_CONF_ERROR; + } + + ls->backlog = -1; +#if 0 +#if 0 + ls->nonblocking = 1; +#else + ls->nonblocking = 0; +#endif +#endif + ls->addr_ntop = 1; + + ls->handler = ngx_http_init_connection; + + cscf = in_addr[a].core_srv_conf; + ls->pool_size = cscf->connection_pool_size; + ls->post_accept_timeout = cscf->post_accept_timeout; + + clcf = cscf->ctx->loc_conf[ngx_http_core_module.ctx_index]; + ls->log = clcf->err_log; + +#if (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; + } +#endif + + ls->ctx = ctx; + + if (in_port[p].addrs.nelts > 1) { + + in_addr = in_port[p].addrs.elts; + if (in_addr[in_port[p].addrs.nelts - 1].addr != INADDR_ANY) { + + /* + * if this port has not the "*:port" binding then create + * the separate ngx_http_in_port_t for the all bindings + */ + + ngx_test_null(inport, + ngx_palloc(cf->pool, + sizeof(ngx_http_in_port_t)), + NGX_CONF_ERROR); + + inport->port = in_port[p].port; + inport->port_text = in_port[p].port_text; + + /* init list of the addresses ... */ + + ngx_init_array(inport->addrs, cf->pool, 1, + sizeof(ngx_http_in_addr_t), + NGX_CONF_ERROR); + + /* ... and set up it with the first address */ + + inport->addrs.nelts = 1; + inport->addrs.elts = in_port[p].addrs.elts; + + ls->servers = inport; + + /* prepare for the next cycle */ + + in_port[p].addrs.elts = (char *) in_port[p].addrs.elts + + in_port[p].addrs.size; + in_port[p].addrs.nelts--; + + in_addr = (ngx_http_in_addr_t *) in_port[p].addrs.elts; + a = 0; + + continue; + } + } + + ls->servers = &in_port[p]; + a++; + } + } + +#if (NGX_DEBUG) + 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 %08x", in_port[p].port, &in_port[p]); + in_addr = in_port[p].addrs.elts; + for (a = 0; a < in_port[p].addrs.nelts; a++) { + u_char ip[20]; + ngx_inet_ntop(AF_INET, &in_addr[a].addr, ip, 20); + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, cf->log, 0, + "%s %08x", ip, in_addr[a].core_srv_conf); + s_name = in_addr[a].names.elts; + for (n = 0; n < in_addr[a].names.nelts; n++) { + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, cf->log, 0, + "%s %08x", s_name[n].name.data, + s_name[n].core_srv_conf); + } + } + } +#endif + + return NGX_CONF_OK; +} + + +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 *rv; + ngx_uint_t i; + ngx_http_core_loc_conf_t **clcfp; + + clcfp = /* (ngx_http_core_loc_conf_t **) */ locations->elts; + + 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; + } + + rv = ngx_http_merge_locations(cf, &clcfp[i]->locations, + clcfp[i]->loc_conf, module, ctx_index); + if (rv != NGX_CONF_OK) { + return rv; + } + } + + return NGX_CONF_OK; +} diff --git a/src/http/ngx_http.h b/src/http/ngx_http.h new file mode 100644 --- /dev/null +++ b/src/http/ngx_http.h @@ -0,0 +1,112 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#ifndef _NGX_HTTP_H_INCLUDED_ +#define _NGX_HTTP_H_INCLUDED_ + + +#include +#include +#include + +typedef struct ngx_http_request_s ngx_http_request_t; +typedef struct ngx_http_cleanup_s ngx_http_cleanup_t; + +#if (NGX_HTTP_CACHE) +#include +#endif +/* STUB */ +#include + +#include +#include +#include +#include +#include + +#if (NGX_HTTP_SSL) +#include +#endif + + +typedef struct { + u_int connection; + + /* + * we declare "action" as "char *" because the actions are usually + * the static strings and in the "u_char *" case we have to override + * all the time their types + */ + + char *action; + u_char *client; + u_char *url; +} ngx_http_log_ctx_t; + + +#define ngx_http_get_module_ctx(r, module) r->ctx[module.ctx_index] +#define ngx_http_get_module_err_ctx(r, module) \ + (r->err_ctx ? r->err_ctx[module.ctx_index] : r->ctx[module.ctx_index]) + +#define ngx_http_create_ctx(r, cx, module, size, error) \ + do { \ + ngx_test_null(cx, ngx_pcalloc(r->pool, size), error); \ + r->ctx[module.ctx_index] = cx; \ + } while (0) + +#define ngx_http_delete_ctx(r, module) \ + r->ctx[module.ctx_index] = NULL; + + +void ngx_http_init_connection(ngx_connection_t *c); + +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_header_line(ngx_http_request_t *r, ngx_buf_t *b); + +ngx_int_t ngx_http_find_server_conf(ngx_http_request_t *r); +void ngx_http_handler(ngx_http_request_t *r); +void ngx_http_finalize_request(ngx_http_request_t *r, int error); +void ngx_http_writer(ngx_event_t *wev); + +void ngx_http_empty_handler(ngx_event_t *wev); + +ngx_int_t ngx_http_send_last(ngx_http_request_t *r); +void ngx_http_close_request(ngx_http_request_t *r, int error); +void ngx_http_close_connection(ngx_connection_t *c); + + +ngx_int_t ngx_http_read_client_request_body(ngx_http_request_t *r); + +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, int error); + + +time_t ngx_http_parse_time(u_char *value, size_t len); +size_t ngx_http_get_time(char *buf, time_t t); + + + +ngx_int_t ngx_http_discard_body(ngx_http_request_t *r); + + +extern ngx_module_t ngx_http_module; + + +extern ngx_uint_t ngx_http_total_requests; +extern uint64_t ngx_http_total_sent; + + +extern ngx_http_output_header_filter_pt ngx_http_top_header_filter; +extern ngx_http_output_body_filter_pt ngx_http_top_body_filter; + + +/* STUB */ +ngx_int_t ngx_http_log_handler(ngx_http_request_t *r); +/**/ + + +#endif /* _NGX_HTTP_H_INCLUDED_ */ diff --git a/src/http/ngx_http_busy_lock.c b/src/http/ngx_http_busy_lock.c new file mode 100644 --- /dev/null +++ b/src/http/ngx_http_busy_lock.c @@ -0,0 +1,300 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include + + + +static int ngx_http_busy_lock_look_cachable(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) +{ + if (bl->busy < bl->max_busy) { + bl->busy++; + + if (bc->time) { + bc->time = 0; + bl->waiting--; + } + + return NGX_OK; + } + + if (bc->time) { + if (bc->time < bl->timeout) { + ngx_add_timer(bc->event, 1000); + return NGX_AGAIN; + } + + bl->waiting--; + return NGX_DONE; + + } + + if (bl->timeout == 0) { + return NGX_DONE; + } + + if (bl->waiting < bl->max_waiting) { + bl->waiting++; + + ngx_add_timer(bc->event, 1000); + bc->event->event_handler = bc->event_handler; + + /* TODO: ngx_handle_level_read_event() */ + + return NGX_AGAIN; + } + + return NGX_ERROR; +} + + +int ngx_http_busy_lock_cachable(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); + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, bc->event->log, 0, + "http busylock: %d w:%d mw::%d", + rc, bl->waiting, bl->max_waiting); + + if (rc == NGX_OK) { /* no the same request, there's free slot */ + return NGX_OK; + } + + if (rc == NGX_ERROR && !lock) { /* no the same request, no free slot */ + return NGX_OK; + } + + /* rc == NGX_AGAIN: the same request */ + + if (bc->time) { + if (bc->time < bl->timeout) { + ngx_add_timer(bc->event, 1000); + return NGX_AGAIN; + } + + bl->waiting--; + return NGX_DONE; + + } + + if (bl->timeout == 0) { + return NGX_DONE; + } + + if (bl->waiting < bl->max_waiting) { + bl->waiting++; + ngx_add_timer(bc->event, 1000); + bc->event->event_handler = bc->event_handler; + + /* TODO: ngx_handle_level_read_event() */ + + return NGX_AGAIN; + } + + return NGX_ERROR; +} + + +void ngx_http_busy_unlock(ngx_http_busy_lock_t *bl, + ngx_http_busy_lock_ctx_t *bc) +{ + if (bl == NULL) { + return; + } + + if (bl->md5) { + bl->md5_mask[bc->slot / 8] &= ~(1 << (bc->slot & 7)); + bl->cachable--; + } + + 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) +{ + int i, b, cachable, free; + u_int mask; + + b = 0; + cachable = 0; + free = -1; + +#if (NGX_SUPPRESS_WARN) + mask = 0; +#endif + + for (i = 0; i < bl->max_busy; i++) { + + if ((b & 7) == 0) { + mask = bl->md5_mask[i / 8]; + } + + if (mask & 1) { + if (ngx_memcmp(&bl->md5[i * 16], bc->md5, 16) == 0) { + return NGX_AGAIN; + } + cachable++; + + } else if (free == -1) { + free = i; + } + +#if 1 + if (cachable == bl->cachable) { + if (free == -1 && cachable < bl->max_busy) { + free = i + 1; + } + + break; + } +#endif + + mask >>= 1; + b++; + } + + if (free == -1) { + return NGX_ERROR; + } + + if (lock) { + if (bl->busy == bl->max_busy) { + return NGX_ERROR; + } + + ngx_memcpy(&bl->md5[free * 16], bc->md5, 16); + bl->md5_mask[free / 8] |= 1 << (free & 7); + bc->slot = free; + + bl->cachable++; + bl->busy++; + } + + return NGX_OK; +} + + +char *ngx_http_set_busy_lock_slot(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf) +{ + char *p = conf; + + ngx_uint_t i, dup, invalid; + ngx_str_t *value, line; + ngx_http_busy_lock_t *bl, **blp; + + blp = (ngx_http_busy_lock_t **) (p + cmd->offset); + if (*blp) { + return "is duplicate"; + } + + /* ngx_calloc_shared() */ + if (!(bl = ngx_pcalloc(cf->pool, sizeof(ngx_http_busy_lock_t)))) { + return NGX_CONF_ERROR; + } + *blp = bl; + + /* ngx_calloc_shared() */ + if (!(bl->mutex = ngx_pcalloc(cf->pool, sizeof(ngx_event_mutex_t)))) { + return NGX_CONF_ERROR; + } + + 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 'b': + if (bl->max_busy) { + dup = 1; + break; + } + + bl->max_busy = ngx_atoi(value[i].data + 2, value[i].len - 2); + if (bl->max_busy == NGX_ERROR) { + invalid = 1; + break; + } + + continue; + + case 'w': + if (bl->max_waiting) { + dup = 1; + break; + } + + bl->max_waiting = ngx_atoi(value[i].data + 2, value[i].len - 2); + if (bl->max_waiting == NGX_ERROR) { + invalid = 1; + break; + } + + continue; + + case 't': + if (bl->timeout) { + dup = 1; + break; + } + + line.len = value[i].len - 2; + line.data = value[i].data + 2; + + bl->timeout = ngx_parse_time(&line, 1); + if (bl->timeout == NGX_ERROR) { + 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; + } + } + + if (bl->timeout == 0 && bl->max_waiting) { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "busy lock waiting is useless with zero timeout, ignoring"); + } + + return NGX_CONF_OK; +} diff --git a/src/http/ngx_http_busy_lock.h b/src/http/ngx_http_busy_lock.h new file mode 100644 --- /dev/null +++ b/src/http/ngx_http_busy_lock.h @@ -0,0 +1,53 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#ifndef _NGX_HTTP_BUSY_LOCK_H_INCLUDED_ +#define _NGX_HTTP_BUSY_LOCK_H_INCLUDED_ + + +#include +#include +#include +#include + + +typedef struct { + u_char *md5_mask; + char *md5; + int cachable; + + int busy; + int max_busy; + + int waiting; + int max_waiting; + + time_t timeout; + + ngx_event_mutex_t *mutex; +} ngx_http_busy_lock_t; + + +typedef struct { + time_t time; + ngx_event_t *event; + void (*event_handler)(ngx_event_t *ev); + u_char *md5; + int slot; +} ngx_http_busy_lock_ctx_t; + + +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); +void ngx_http_busy_unlock(ngx_http_busy_lock_t *bl, + ngx_http_busy_lock_ctx_t *bc); + +char *ngx_http_set_busy_lock_slot(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); + + +#endif /* _NGX_HTTP_BUSY_LOCK_H_INCLUDED_ */ diff --git a/src/http/ngx_http_cache.c b/src/http/ngx_http_cache.c new file mode 100644 --- /dev/null +++ b/src/http/ngx_http_cache.c @@ -0,0 +1,480 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include + + + +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 child */ +}; + + +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_HAVE_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; +} diff --git a/src/http/ngx_http_cache.h b/src/http/ngx_http_cache.h new file mode 100644 --- /dev/null +++ b/src/http/ngx_http_cache.h @@ -0,0 +1,131 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#ifndef _NGX_HTTP_CACHE_H_INCLUDED_ +#define _NGX_HTTP_CACHE_H_INCLUDED_ + + +#include +#include +#include + + +/* + * The 7 uses before an allocation. + * We can use maximum 7 bits, i.e up to the 127 uses. + */ +#define NGX_HTTP_CACHE_LAZY_ALLOCATION_BITS 3 + +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_t; + + +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; + + +#define NGX_HTTP_CACHE_HASH 7 +#define NGX_HTTP_CACHE_NELTS 4 + +typedef struct { + ngx_http_cache_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; + + +typedef struct { + ngx_http_cache_hash_t *hash; + ngx_http_cache_t *cache; + ngx_file_t file; + ngx_str_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; + ssize_t header_size; + size_t file_start; + ngx_log_t *log; +} ngx_http_cache_ctx_t; + + + +#define NGX_HTTP_CACHE_STALE 1 +#define NGX_HTTP_CACHE_AGED 2 +#define NGX_HTTP_CACHE_THE_SAME 3 + + +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_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); + +int ngx_http_cache_get_file(ngx_http_request_t *r, ngx_http_cache_ctx_t *ctx); +int ngx_http_cache_open_file(ngx_http_cache_ctx_t *ctx, ngx_file_uniq_t uniq); +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); + + +int ngx_garbage_collector_http_cache_handler(ngx_gc_t *gc, ngx_str_t *name, + ngx_dir_t *dir); + +char *ngx_http_set_cache_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); + + +#endif /* _NGX_HTTP_CACHE_H_INCLUDED_ */ diff --git a/src/http/ngx_http_config.h b/src/http/ngx_http_config.h new file mode 100644 --- /dev/null +++ b/src/http/ngx_http_config.h @@ -0,0 +1,70 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#ifndef _NGX_HTTP_CONFIG_H_INCLUDED_ +#define _NGX_HTTP_CONFIG_H_INCLUDED_ + + +#include +#include + + +typedef struct { + void **main_conf; + void **srv_conf; + void **loc_conf; +} ngx_http_conf_ctx_t; + + +typedef struct { + ngx_int_t (*pre_conf)(ngx_conf_t *cf); + + void *(*create_main_conf)(ngx_conf_t *cf); + char *(*init_main_conf)(ngx_conf_t *cf, void *conf); + + void *(*create_srv_conf)(ngx_conf_t *cf); + char *(*merge_srv_conf)(ngx_conf_t *cf, void *prev, void *conf); + + void *(*create_loc_conf)(ngx_conf_t *cf); + char *(*merge_loc_conf)(ngx_conf_t *cf, void *prev, void *conf); +} ngx_http_module_t; + + +#define NGX_HTTP_MODULE 0x50545448 /* "HTTP" */ + +#define NGX_HTTP_MAIN_CONF 0x02000000 +#define NGX_HTTP_SRV_CONF 0x04000000 +#define NGX_HTTP_LOC_CONF 0x08000000 + + +#define NGX_HTTP_MAIN_CONF_OFFSET offsetof(ngx_http_conf_ctx_t, main_conf) +#define NGX_HTTP_SRV_CONF_OFFSET offsetof(ngx_http_conf_ctx_t, srv_conf) +#define NGX_HTTP_LOC_CONF_OFFSET offsetof(ngx_http_conf_ctx_t, loc_conf) + + +#define ngx_http_get_module_main_conf(r, module) r->main_conf[module.ctx_index] +#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] +#define ngx_http_conf_get_module_srv_conf(cf, module) \ + ((ngx_http_conf_ctx_t *) cf->ctx)->srv_conf[module.ctx_index] +#define ngx_http_conf_get_module_loc_conf(cf, module) \ + ((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] + + + +#endif /* _NGX_HTTP_CONFIG_H_INCLUDED_ */ diff --git a/src/http/ngx_http_copy_filter.c b/src/http/ngx_http_copy_filter.c new file mode 100644 --- /dev/null +++ b/src/http/ngx_http_copy_filter.c @@ -0,0 +1,132 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include + + +typedef struct { + ngx_bufs_t bufs; +} ngx_http_copy_filter_conf_t; + + +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); +static ngx_int_t ngx_http_copy_filter_init(ngx_cycle_t *cycle); + + +static ngx_command_t ngx_http_copy_filter_commands[] = { + + {ngx_string("output_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_copy_filter_conf_t, bufs), + NULL}, + + ngx_null_command +}; + + +static ngx_http_module_t ngx_http_copy_filter_module_ctx = { + NULL, /* pre conf */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + NULL, /* create server configuration */ + NULL, /* merge server configuration */ + + ngx_http_copy_filter_create_conf, /* create location configuration */ + ngx_http_copy_filter_merge_conf /* merge location configuration */ +}; + + +ngx_module_t ngx_http_copy_filter_module = { + NGX_MODULE, + &ngx_http_copy_filter_module_ctx, /* module context */ + ngx_http_copy_filter_commands, /* module directives */ + NGX_HTTP_MODULE, /* module type */ + ngx_http_copy_filter_init, /* init module */ + NULL /* init process */ +}; + + +static ngx_http_output_body_filter_pt ngx_http_next_filter; + + +ngx_int_t ngx_http_copy_filter(ngx_http_request_t *r, ngx_chain_t *in) +{ + ngx_output_chain_ctx_t *ctx; + ngx_http_copy_filter_conf_t *conf; + + if (r->connection->write->error) { + return NGX_ERROR; + } + + ctx = ngx_http_get_module_ctx(r->main ? r->main : r, + ngx_http_copy_filter_module); + + if (ctx == NULL) { + conf = ngx_http_get_module_loc_conf(r->main ? r->main : r, + ngx_http_copy_filter_module); + + ngx_http_create_ctx(r, ctx, ngx_http_copy_filter_module, + sizeof(ngx_output_chain_ctx_t), NGX_ERROR); + + ctx->sendfile = r->sendfile; + ctx->need_in_memory = r->filter_need_in_memory; + ctx->need_in_temp = r->filter_need_temporary; + + ctx->pool = r->pool; + ctx->bufs = conf->bufs; + ctx->tag = (ngx_buf_tag_t) &ngx_http_copy_filter_module; + + ctx->output_filter = (ngx_output_chain_filter_pt) ngx_http_next_filter; + ctx->filter_ctx = r; + + } + + return ngx_output_chain(ctx, in); +} + + +static void *ngx_http_copy_filter_create_conf(ngx_conf_t *cf) +{ + ngx_http_copy_filter_conf_t *conf; + + ngx_test_null(conf, + ngx_palloc(cf->pool, sizeof(ngx_http_copy_filter_conf_t)), + NULL); + + conf->bufs.num = 0; + + return conf; +} + + +static char *ngx_http_copy_filter_merge_conf(ngx_conf_t *cf, + void *parent, void *child) +{ + ngx_http_copy_filter_conf_t *prev = parent; + ngx_http_copy_filter_conf_t *conf = child; + + ngx_conf_merge_bufs_value(conf->bufs, prev->bufs, 1, 32768); + + return NULL; +} + + +static ngx_int_t ngx_http_copy_filter_init(ngx_cycle_t *cycle) +{ + ngx_http_next_filter = ngx_http_top_body_filter; + ngx_http_top_body_filter = ngx_http_copy_filter; + + return NGX_OK; +} + diff --git a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c new file mode 100644 --- /dev/null +++ b/src/http/ngx_http_core_module.c @@ -0,0 +1,1819 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include +#include +#include + +/* STUB */ +#define NGX_HTTP_LOCATION_EXACT 1 +#define NGX_HTTP_LOCATION_AUTO_REDIRECT 2 +#define NGX_HTTP_LOCATION_REGEX 3 + + +static void ngx_http_phase_event_handler(ngx_event_t *rev); +static void ngx_http_run_phases(ngx_http_request_t *r); +static ngx_int_t ngx_http_find_location(ngx_http_request_t *r, + ngx_array_t *locations, size_t len); + +static void *ngx_http_core_create_main_conf(ngx_conf_t *cf); +static char *ngx_http_core_init_main_conf(ngx_conf_t *cf, void *conf); +static void *ngx_http_core_create_srv_conf(ngx_conf_t *cf); +static char *ngx_http_core_merge_srv_conf(ngx_conf_t *cf, + void *parent, void *child); +static void *ngx_http_core_create_loc_conf(ngx_conf_t *cf); +static char *ngx_http_core_merge_loc_conf(ngx_conf_t *cf, + void *parent, void *child); + +static char *ngx_server_block(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy); +static int ngx_cmp_locations(const void *first, const void *second); +static char *ngx_location_block(ngx_conf_t *cf, ngx_command_t *cmd, + void *dummy); +static char *ngx_types_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static char *ngx_set_type(ngx_conf_t *cf, ngx_command_t *dummy, void *conf); +static char *ngx_set_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static char *ngx_set_server_name(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static char *ngx_set_root(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static char *ngx_set_error_page(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static char *ngx_set_error_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static char *ngx_set_keepalive(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); + +static char *ngx_http_lowat_check(ngx_conf_t *cf, void *post, void *data); + +static ngx_conf_post_t ngx_http_lowat_post = { ngx_http_lowat_check } ; + + +static ngx_conf_enum_t ngx_http_restrict_host_names[] = { + { ngx_string("off"), NGX_HTTP_RESTRICT_HOST_OFF }, + { ngx_string("on"), NGX_HTTP_RESTRICT_HOST_ON }, + { ngx_string("close"), NGX_HTTP_RESTRICT_HOST_CLOSE }, + { ngx_null_string, 0 } +}; + + +static ngx_command_t ngx_http_core_commands[] = { + + { ngx_string("server"), + NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS, + ngx_server_block, + 0, + 0, + NULL }, + + { ngx_string("connection_pool_size"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_size_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_http_core_srv_conf_t, connection_pool_size), + NULL }, + + { ngx_string("post_accept_timeout"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_msec_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_http_core_srv_conf_t, post_accept_timeout), + NULL }, + + { ngx_string("request_pool_size"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_size_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_http_core_srv_conf_t, request_pool_size), + NULL }, + + { ngx_string("client_header_timeout"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_msec_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_http_core_srv_conf_t, client_header_timeout), + NULL }, + + { ngx_string("client_header_buffer_size"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_size_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_http_core_srv_conf_t, client_header_buffer_size), + NULL }, + + { ngx_string("large_client_header_buffers"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE2, + ngx_conf_set_bufs_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_http_core_srv_conf_t, large_client_header_buffers), + NULL }, + + { ngx_string("restrict_host_names"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_enum_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_http_core_srv_conf_t, restrict_host_names), + &ngx_http_restrict_host_names }, + + { ngx_string("location"), + NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE12, + ngx_location_block, + NGX_HTTP_SRV_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("listen"), +#if 0 + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, +#else + NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, +#endif + ngx_set_listen, + NGX_HTTP_SRV_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("server_name"), + NGX_HTTP_SRV_CONF|NGX_CONF_1MORE, + ngx_set_server_name, + NGX_HTTP_SRV_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("types"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF + |NGX_CONF_BLOCK|NGX_CONF_NOARGS, + ngx_types_block, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("default_type"), + 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_core_loc_conf_t, default_type), + NULL }, + + { ngx_string("root"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_set_root, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("alias"), + NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_set_root, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("client_max_body_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_core_loc_conf_t, client_max_body_size), + NULL }, + + { ngx_string("client_body_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_core_loc_conf_t, client_body_buffer_size), + NULL }, + + { ngx_string("client_body_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, client_body_timeout), + NULL }, + + { ngx_string("sendfile"), + 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, sendfile), + 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, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_core_loc_conf_t, tcp_nopush), + NULL }, + + { ngx_string("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_core_loc_conf_t, send_timeout), + NULL }, + + { ngx_string("send_lowat"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_conf_set_size_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_core_loc_conf_t, send_lowat), + &ngx_http_lowat_post }, + + { ngx_string("postpone_output"), + 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, postpone_output), + NULL }, + + { ngx_string("limit_rate"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_conf_set_size_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_core_loc_conf_t, limit_rate), + NULL }, + + { ngx_string("keepalive_timeout"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12, + ngx_set_keepalive, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("lingering_time"), + 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, lingering_time), + NULL }, + + { ngx_string("lingering_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, lingering_timeout), + NULL }, + + { ngx_string("reset_timedout_connection"), + 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, reset_timedout_connection), + NULL }, + + { ngx_string("msie_padding"), + 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, msie_padding), + NULL }, + + { ngx_string("error_page"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_2MORE, + ngx_set_error_page, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("error_log"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE, + ngx_set_error_log, + NGX_HTTP_LOC_CONF_OFFSET, + 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_LOC_CONF_OFFSET, + offsetof(ngx_http_core_loc_conf_t, open_files), + NULL }, + +#endif + + ngx_null_command +}; + + +ngx_http_module_t ngx_http_core_module_ctx = { + NULL, /* pre conf */ + + ngx_http_core_create_main_conf, /* create main configuration */ + ngx_http_core_init_main_conf, /* init main configuration */ + + ngx_http_core_create_srv_conf, /* create server configuration */ + ngx_http_core_merge_srv_conf, /* merge server configuration */ + + ngx_http_core_create_loc_conf, /* create location configuration */ + ngx_http_core_merge_loc_conf /* merge location configuration */ +}; + + +ngx_module_t ngx_http_core_module = { + NGX_MODULE, + &ngx_http_core_module_ctx, /* module context */ + ngx_http_core_commands, /* module directives */ + NGX_HTTP_MODULE, /* module type */ + NULL, /* init module */ + NULL /* init process */ +}; + + +void ngx_http_handler(ngx_http_request_t *r) +{ + ngx_http_log_ctx_t *lcx; + + r->connection->unexpected_eof = 0; + + lcx = r->connection->log->data; + lcx->action = NULL; + + switch (r->headers_in.connection_type) { + case 0: + if (r->http_version > NGX_HTTP_VERSION_10) { + r->keepalive = 1; + } else { + r->keepalive = 0; + } + break; + + case NGX_HTTP_CONNECTION_CLOSE: + r->keepalive = 0; + break; + + case NGX_HTTP_CONNECTION_KEEP_ALIVE: + r->keepalive = 1; + break; + } + + if (r->keepalive && r->headers_in.msie && r->method == NGX_HTTP_POST) { + + /* + * MSIE may wait for some time if the response for the POST request + * is sent over the keepalive connection + */ + + r->keepalive = 0; + } + +#if 0 + /* TEST STUB */ r->http_version = NGX_HTTP_VERSION_10; + /* TEST STUB */ r->keepalive = 0; +#endif + + if (r->headers_in.content_length_n > 0) { + r->lingering_close = 1; + + } else { + r->lingering_close = 0; + } + +#if 0 + /* TEST STUB */ r->lingering_close = 1; +#endif + + r->connection->write->event_handler = ngx_http_phase_event_handler; + + ngx_http_run_phases(r); + + return; +} + + +static void ngx_http_phase_event_handler(ngx_event_t *ev) +{ + ngx_connection_t *c; + ngx_http_request_t *r; + + c = ev->data; + r = c->data; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ev->log, 0, "phase event handler"); + + ngx_http_run_phases(r); + + return; +} + + +static void ngx_http_run_phases(ngx_http_request_t *r) +{ + char *path; + ngx_int_t rc; + ngx_http_handler_pt *h; + ngx_http_core_loc_conf_t *clcf; + ngx_http_core_main_conf_t *cmcf; + + cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); + + for (/* void */; r->phase < NGX_HTTP_LAST_PHASE; r->phase++) { + + if (r->phase == NGX_HTTP_CONTENT_PHASE && r->content_handler) { + r->connection->write->event_handler = ngx_http_empty_handler; + rc = r->content_handler(r); + ngx_http_finalize_request(r, rc); + return; + } + + h = cmcf->phases[r->phase].handlers.elts; + for (r->phase_handler = cmcf->phases[r->phase].handlers.nelts - 1; + r->phase_handler >= 0; + r->phase_handler--) + { + rc = h[r->phase_handler](r); + + if (rc == NGX_DONE) { + + /* + * we should never use r here because + * it could point to already freed data + */ + + return; + } + + if (rc == NGX_DECLINED) { + continue; + } + + if (rc >= NGX_HTTP_SPECIAL_RESPONSE || rc == NGX_ERROR) { + ngx_http_finalize_request(r, rc); + return; + } + + if (r->phase == NGX_HTTP_CONTENT_PHASE) { + ngx_http_finalize_request(r, rc); + return; + } + + if (rc == NGX_AGAIN) { + return; + } + + if (rc == NGX_OK && cmcf->phases[r->phase].type == NGX_OK) { + break; + } + } + } + + + if (r->uri.data[r->uri.len - 1] == '/') { + + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + if (!(path = ngx_palloc(r->pool, clcf->root.len + r->uri.len))) { + ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + ngx_cpystrn(ngx_cpymem(path, clcf->root.data, clcf->root.len), + r->uri.data, r->uri.len + 1); + + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "directory index of \"%s\" is forbidden", path); + + ngx_http_finalize_request(r, NGX_HTTP_FORBIDDEN); + return; + } + + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "no handler found"); + + ngx_http_finalize_request(r, NGX_HTTP_NOT_FOUND); + return; +} + + +ngx_int_t ngx_http_find_location_config(ngx_http_request_t *r) +{ + ngx_int_t rc; + ngx_http_core_loc_conf_t *clcf; + ngx_http_core_srv_conf_t *cscf; + + cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); + + rc = ngx_http_find_location(r, &cscf->locations, 0); + + if (rc == NGX_HTTP_INTERNAL_SERVER_ERROR) { + return rc; + } + + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + r->connection->log->file = clcf->err_log->file; + if (!(r->connection->log->log_level & NGX_LOG_DEBUG_CONNECTION)) { + r->connection->log->log_level = clcf->err_log->log_level; + } + + if (!(ngx_io.flags & NGX_IO_SENDFILE) || !clcf->sendfile) { + r->sendfile = 0; + + } else { + r->sendfile = 1; + } + + if (!clcf->tcp_nopush) { + /* disable TCP_NOPUSH/TCP_CORK use */ + r->connection->tcp_nopush = NGX_TCP_NOPUSH_DISABLED; + } + + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http cl: " SIZE_T_FMT " max: " SIZE_T_FMT, + r->headers_in.content_length_n, + clcf->client_max_body_size); + + if (r->headers_in.content_length_n != -1 + && clcf->client_max_body_size + && clcf->client_max_body_size < (size_t) r->headers_in.content_length_n) + { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "client intented to send too large body: " + SIZE_T_FMT " bytes", + r->headers_in.content_length_n); + + return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE; + } + + + if (rc == NGX_HTTP_LOCATION_AUTO_REDIRECT) { + 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->value = clcf->name; + + return NGX_HTTP_MOVED_PERMANENTLY; + } + + if (clcf->handler) { + r->content_handler = clcf->handler; + } + + return NGX_OK; +} + + +static ngx_int_t ngx_http_find_location(ngx_http_request_t *r, + ngx_array_t *locations, size_t len) +{ + ngx_int_t n, rc; + ngx_uint_t i, found; + ngx_http_core_loc_conf_t *clcf, **clcfp; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "find location"); + + found = 0; + + clcfp = locations->elts; + for (i = 0; i < locations->nelts; i++) { + +#if (HAVE_PCRE) + if (clcfp[i]->regex) { + break; + } +#endif + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "find location: %s\"%s\"", + clcfp[i]->exact_match ? "= " : "", + clcfp[i]->name.data); + + 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 */ + + r->loc_conf = clcfp[i]->loc_conf; + + return NGX_HTTP_LOCATION_AUTO_REDIRECT; + } + + if (r->uri.len < clcfp[i]->name.len) { + continue; + } + + n = ngx_strncmp(r->uri.data, clcfp[i]->name.data, clcfp[i]->name.len); + + if (n < 0) { + /* the locations are lexicographically sorted */ + break; + } + + if (n == 0) { + if (clcfp[i]->exact_match && r->uri.len == clcfp[i]->name.len) { + r->loc_conf = clcfp[i]->loc_conf; + return NGX_HTTP_LOCATION_EXACT; + } + + if (len > clcfp[i]->name.len) { + /* the previous match is longer */ + break; + } + + r->loc_conf = clcfp[i]->loc_conf; + found = 1; + } + } + + if (found) { + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + if (clcf->locations.nelts) { + rc = ngx_http_find_location(r, &clcf->locations, len); + + if (rc != NGX_OK) { + return rc; + } + } + } + +#if (HAVE_PCRE) + + /* regex matches */ + + for (/* void */; i < locations->nelts; i++) { + + if (!clcfp[i]->regex) { + continue; + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "find location: ~ \"%s\"", + clcfp[i]->name.data); + + n = ngx_regex_exec(clcfp[i]->regex, &r->uri, NULL, 0); + + if (n == NGX_DECLINED) { + continue; + } + + if (n < 0) { + ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, + ngx_regex_exec_n + " failed: %d on \"%s\" using \"%s\"", + n, r->uri.data, clcfp[i]->name.data); + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + /* match */ + + r->loc_conf = clcfp[i]->loc_conf; + + return NGX_HTTP_LOCATION_REGEX; + } + +#endif /* HAVE_PCRE */ + + return NGX_OK; +} + + +ngx_int_t ngx_http_set_content_type(ngx_http_request_t *r) +{ + uint32_t key; + ngx_uint_t i; + ngx_http_type_t *type; + ngx_http_core_loc_conf_t *clcf; + + r->headers_out.content_type = ngx_list_push(&r->headers_out.headers); + if (r->headers_out.content_type == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + r->headers_out.content_type->key.len = 0; + r->headers_out.content_type->key.data = NULL; + r->headers_out.content_type->value.len = 0; + r->headers_out.content_type->value.data = NULL; + + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + if (r->exten.len) { +#if 0 + key = ngx_crc(r->exten.data, r->exten.key); +#endif + ngx_http_types_hash_key(key, r->exten); + + type = clcf->types[key].elts; + for (i = 0; i < clcf->types[key].nelts; i++) { + if (r->exten.len != type[i].exten.len) { + continue; + } + + if (ngx_memcmp(r->exten.data, type[i].exten.data, r->exten.len) + == 0) + { + r->headers_out.content_type->value = type[i].type; + break; + } + } + } + + if (r->headers_out.content_type->value.len == 0) { + r->headers_out.content_type->value = clcf->default_type; + } + + return NGX_OK; +} + + +ngx_int_t ngx_http_send_header(ngx_http_request_t *r) +{ + if (r->main) { + return NGX_OK; + } + + if (r->err_ctx) { + r->headers_out.status = r->err_status; + r->headers_out.status_line.len = 0; + } + + return (*ngx_http_top_header_filter)(r); +} + + +ngx_int_t ngx_http_output_filter(ngx_http_request_t *r, ngx_chain_t *in) +{ + ngx_int_t rc; + + if (r->connection->write->error) { + return NGX_ERROR; + } + + rc = ngx_http_top_body_filter(r, in); + + if (rc == NGX_ERROR) { + + /* NGX_ERROR could be returned by any filter */ + + r->connection->write->error = 1; + } + + return rc; +} + + +int ngx_http_redirect(ngx_http_request_t *r, int redirect) +{ + /* STUB */ + + /* log request */ + + ngx_http_close_request(r, 0); + return NGX_OK; +} + + +ngx_int_t ngx_http_set_exten(ngx_http_request_t *r) +{ + ngx_int_t i; + + r->exten.len = 0; + r->exten.data = NULL; + + for (i = r->uri.len - 1; i > 1; i--) { + if (r->uri.data[i] == '.' && r->uri.data[i - 1] != '/') { + r->exten.len = r->uri.len - i - 1; + + if (r->exten.len > 0) { + if (!(r->exten.data = ngx_palloc(r->pool, r->exten.len + 1))) { + return NGX_ERROR; + } + + ngx_cpystrn(r->exten.data, &r->uri.data[i + 1], + r->exten.len + 1); + } + + break; + + } else if (r->uri.data[i] == '/') { + break; + } + } + + return NGX_OK; +} + + +ngx_int_t ngx_http_internal_redirect(ngx_http_request_t *r, + ngx_str_t *uri, ngx_str_t *args) +{ + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "internal redirect: \"%s\"", uri->data); + + r->uri.len = uri->len; + r->uri.data = uri->data; + + if (args) { + r->args.len = args->len; + r->args.data = args->data; + } + + if (ngx_http_set_exten(r) != NGX_OK) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + if (r->err_ctx) { + + /* allocate the new modules contexts */ + + r->ctx = ngx_pcalloc(r->pool, sizeof(void *) * ngx_http_max_module); + if (r->ctx == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + } else { + + /* clear the modules contexts */ + + ngx_memzero(r->ctx, sizeof(void *) * ngx_http_max_module); + } + + r->phase = 0; + r->phase_handler = 0; + + ngx_http_handler(r); + + return NGX_DONE; +} + + +#if 0 /* STUB: test the delay http handler */ + +int ngx_http_delay_handler(ngx_http_request_t *r) +{ + static int on; + + if (on++ == 0) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http set delay"); + ngx_add_timer(r->connection->write, 10000); + return NGX_AGAIN; + } + + r->connection->write->timedout = 0; + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http reset delay"); + return NGX_DECLINED; +} + +#endif + + +#if 0 + +static ngx_int_t ngx_http_core_init_process(ngx_cycle_t *cycle) +{ + ngx_uint_t i; + ngx_http_core_srv_conf_t **cscfp; + ngx_http_core_main_conf_t *cmcf; + + cmcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_core_module); + +#if 0 + ngx_http_core_init_module: + + ngx_http_handler_pt *h; + + ngx_test_null(h, ngx_push_array( + &cmcf->phases[NGX_HTTP_TRANSLATE_PHASE].handlers), + NGX_ERROR); + *h = ngx_http_delay_handler; +#endif + + cscfp = cmcf->servers.elts; + + for (i = 0; i < cmcf->servers.nelts; i++) { + if (cscfp[i]->recv == NULL) { + cscfp[i]->recv = ngx_io.recv; + cscfp[i]->send_chain = ngx_io.send_chain; + } + } + + return NGX_OK; +} + +#endif + + +static char *ngx_server_block(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy) +{ + int m; + char *rv; + ngx_http_module_t *module; + ngx_conf_t pvcf; + ngx_http_conf_ctx_t *ctx, *http_ctx; + ngx_http_core_main_conf_t *cmcf; + ngx_http_core_srv_conf_t *cscf, **cscfp; + + ngx_test_null(ctx, + ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t)), + NGX_CONF_ERROR); + + http_ctx = cf->ctx; + ctx->main_conf = http_ctx->main_conf; + + /* the server{}'s srv_conf */ + + ngx_test_null(ctx->srv_conf, + ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module), + NGX_CONF_ERROR); + + /* the server{}'s loc_conf */ + + ngx_test_null(ctx->loc_conf, + ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module), + 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->create_srv_conf) { + ngx_test_null(ctx->srv_conf[ngx_modules[m]->ctx_index], + module->create_srv_conf(cf), + NGX_CONF_ERROR); + } + + if (module->create_loc_conf) { + ngx_test_null(ctx->loc_conf[ngx_modules[m]->ctx_index], + module->create_loc_conf(cf), + NGX_CONF_ERROR); + } + } + + /* create links of the srv_conf's */ + + cscf = ctx->srv_conf[ngx_http_core_module.ctx_index]; + cscf->ctx = ctx; + + cmcf = ctx->main_conf[ngx_http_core_module.ctx_index]; + ngx_test_null(cscfp, ngx_push_array(&cmcf->servers), NGX_CONF_ERROR); + *cscfp = cscf; + + /* parse inside server{} */ + + pvcf = *cf; + cf->ctx = ctx; + cf->cmd_type = NGX_HTTP_SRV_CONF; + rv = ngx_conf_parse(cf, NULL); + *cf = pvcf; + + if (rv != NGX_CONF_OK) { + return rv; + } + + ngx_qsort(cscf->locations.elts, (size_t) cscf->locations.nelts, + sizeof(ngx_http_core_loc_conf_t *), ngx_cmp_locations); + + return rv; +} + + +static int ngx_cmp_locations(const void *one, const void *two) +{ + 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 (HAVE_PCRE) + + 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 && second->exact_match) { + /* an exact match must be before the same inclusive one */ + return 1; + } + + return rc; +} + + +static char *ngx_location_block(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy) +{ + char *rv; + ngx_int_t m; + ngx_str_t *value; + ngx_conf_t pcf; + 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 (HAVE_PCRE) + ngx_str_t err; + u_char errstr[NGX_MAX_CONF_ERRSTR]; +#endif + + if (!(ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t)))) { + return NGX_CONF_ERROR; + } + + pctx = cf->ctx; + ctx->main_conf = pctx->main_conf; + ctx->srv_conf = pctx->srv_conf; + + ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module); + if (ctx->loc_conf == NULL) { + return NGX_CONF_ERROR; + } + + for (m = 0; ngx_modules[m]; m++) { + if (ngx_modules[m]->type != NGX_HTTP_MODULE) { + continue; + } + + module = ngx_modules[m]->ctx; + + if (module->create_loc_conf) { + ctx->loc_conf[ngx_modules[m]->ctx_index] = + module->create_loc_conf(cf); + if (ctx->loc_conf[ngx_modules[m]->ctx_index] == NULL) { + return NGX_CONF_ERROR; + } + } + } + + clcf = ctx->loc_conf[ngx_http_core_module.ctx_index]; + clcf->loc_conf = ctx->loc_conf; + + value = cf->args->elts; + + if (cf->args->nelts == 3) { + if (value[1].len == 1 && value[1].data[0] == '=') { + clcf->name.len = value[2].len; + clcf->name.data = value[2].data; + clcf->exact_match = 1; + + } else if ((value[1].len == 1 && value[1].data[0] == '~') + || (value[1].len == 2 + && value[1].data[0] == '~' + && value[1].data[1] == '*')) + { +#if (HAVE_PCRE) + err.len = NGX_MAX_CONF_ERRSTR; + err.data = errstr; + + clcf->regex = ngx_regex_compile(&value[2], + value[1].len == 2 ? NGX_REGEX_CASELESS: 0, + cf->pool, &err); + + if (clcf->regex == NULL) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%s", err.data); + return NGX_CONF_ERROR; + } + + clcf->name = value[2]; +#else + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "the using of the regex \"%s\" " + "requires PCRE library", + value[2].data); + return NGX_CONF_ERROR; +#endif + + } else { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid location modifier \"%s\"", + value[1].data); + return NGX_CONF_ERROR; + } + + } else { + clcf->name.len = value[1].len; + clcf->name.data = value[1].data; + } + + 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 (!(clcfp = ngx_push_array(&cscf->locations))) { + return NGX_CONF_ERROR; + } + + } else { + clcf->prev_location = pclcf; + + if (pclcf->exact_match) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "location \"%s\" could not be inside " + "the exact location \"%s\"", + clcf->name.data, pclcf->name.data); + return NGX_CONF_ERROR; + } + +#if (HAVE_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 + { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "location \"%s\" is outside location \"%s\"", + clcf->name.data, pclcf->name.data); + return NGX_CONF_ERROR; + } + + if (pclcf->locations.elts == NULL) { + ngx_init_array(pclcf->locations, cf->pool, 5, sizeof(void *), + NGX_CONF_ERROR); + } + + if (!(clcfp = ngx_push_array(&pclcf->locations))) { + return NGX_CONF_ERROR; + } + } + + *clcfp = clcf; + + pcf = *cf; + cf->ctx = ctx; + cf->cmd_type = NGX_HTTP_LOC_CONF; + rv = ngx_conf_parse(cf, NULL); + *cf = pcf; + + return rv; +} + + +static char *ngx_types_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + char *rv; + ngx_conf_t pcf; + + pcf = *cf; + cf->handler = ngx_set_type; + cf->handler_conf = conf; + rv = ngx_conf_parse(cf, NULL); + *cf = pcf; + + return rv; +} + + +static char *ngx_set_type(ngx_conf_t *cf, ngx_command_t *dummy, void *conf) +{ + ngx_http_core_loc_conf_t *lcf = conf; + + uint32_t key; + ngx_uint_t i; + ngx_str_t *args; + ngx_http_type_t *type; + + if (lcf->types == NULL) { + lcf->types = ngx_palloc(cf->pool, NGX_HTTP_TYPES_HASH_PRIME + * sizeof(ngx_array_t)); + if (lcf->types == NULL) { + return NGX_CONF_ERROR; + } + + for (i = 0; i < NGX_HTTP_TYPES_HASH_PRIME; i++) { + if (ngx_array_init(&lcf->types[i], cf->pool, 5, + sizeof(ngx_http_type_t)) == NGX_ERROR) + { + return NGX_CONF_ERROR; + } + } + } + + args = (ngx_str_t *) cf->args->elts; + + for (i = 1; i < cf->args->nelts; i++) { + ngx_http_types_hash_key(key, args[i]); + + if (!(type = ngx_array_push(&lcf->types[key]))) { + return NGX_CONF_ERROR; + } + + type->exten = args[i]; + type->type = args[0]; + } + + return NGX_CONF_OK; +} + + +static void *ngx_http_core_create_main_conf(ngx_conf_t *cf) +{ + ngx_http_core_main_conf_t *cmcf; + + ngx_test_null(cmcf, + ngx_pcalloc(cf->pool, sizeof(ngx_http_core_main_conf_t)), + NGX_CONF_ERROR); + + ngx_init_array(cmcf->servers, cf->pool, + 5, sizeof(ngx_http_core_srv_conf_t *), + NGX_CONF_ERROR); + + return cmcf; +} + + +static char *ngx_http_core_init_main_conf(ngx_conf_t *cf, void *conf) +{ +#if 0 + ngx_http_core_main_conf_t *cmcf = conf; + + /* TODO: remove it if no directives */ +#endif + + return NGX_CONF_OK; +} + + +static void *ngx_http_core_create_srv_conf(ngx_conf_t *cf) +{ + ngx_http_core_srv_conf_t *cscf; + + ngx_test_null(cscf, + ngx_pcalloc(cf->pool, sizeof(ngx_http_core_srv_conf_t)), + NGX_CONF_ERROR); + + /* + + set by ngx_pcalloc(): + + conf->client_large_buffers.num = 0; + + */ + + + ngx_init_array(cscf->locations, cf->pool, + 5, sizeof(void *), NGX_CONF_ERROR); + ngx_init_array(cscf->listen, cf->pool, 5, sizeof(ngx_http_listen_t), + NGX_CONF_ERROR); + ngx_init_array(cscf->server_names, cf->pool, + 5, sizeof(ngx_http_server_name_t), NGX_CONF_ERROR); + + cscf->connection_pool_size = NGX_CONF_UNSET_SIZE; + cscf->post_accept_timeout = NGX_CONF_UNSET_MSEC; + 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->restrict_host_names = NGX_CONF_UNSET_UINT; + + return cscf; +} + + +static char *ngx_http_core_merge_srv_conf(ngx_conf_t *cf, + void *parent, void *child) +{ + ngx_http_core_srv_conf_t *prev = parent; + ngx_http_core_srv_conf_t *conf = child; + + ngx_http_listen_t *l; + ngx_http_server_name_t *n; + ngx_http_core_main_conf_t *cmcf; + + /* TODO: it does not merge, it inits only */ + + if (conf->listen.nelts == 0) { + ngx_test_null(l, ngx_push_array(&conf->listen), NGX_CONF_ERROR); + l->addr = INADDR_ANY; +#if (WIN32) + l->port = 80; +#else + /* STUB: getuid() should be cached */ + l->port = (getuid() == 0) ? 80 : 8000; +#endif + l->family = AF_INET; + } + + if (conf->server_names.nelts == 0) { + ngx_test_null(n, ngx_push_array(&conf->server_names), NGX_CONF_ERROR); + ngx_test_null(n->name.data, ngx_palloc(cf->pool, NGX_MAXHOSTNAMELEN), + NGX_CONF_ERROR); + + if (gethostname((char *) n->name.data, NGX_MAXHOSTNAMELEN) == -1) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno, + "gethostname() failed"); + return NGX_CONF_ERROR; + } + + n->name.len = ngx_strlen(n->name.data); + n->core_srv_conf = conf; + +#if 0 + ctx = (ngx_http_conf_ctx_t *) + cf->cycle->conf_ctx[ngx_http_module.index]; + cmcf = ctx->main_conf[ngx_http_core_module.ctx_index]; +#endif + cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); + + if (cmcf->max_server_name_len < n->name.len) { + cmcf->max_server_name_len = n->name.len; + } + } + + ngx_conf_merge_size_value(conf->connection_pool_size, + prev->connection_pool_size, 256); + ngx_conf_merge_msec_value(conf->post_accept_timeout, + prev->post_accept_timeout, 60000); + ngx_conf_merge_size_value(conf->request_pool_size, + prev->request_pool_size, 4096); + ngx_conf_merge_msec_value(conf->client_header_timeout, + prev->client_header_timeout, 60000); + ngx_conf_merge_size_value(conf->client_header_buffer_size, + prev->client_header_buffer_size, 1024); + ngx_conf_merge_bufs_value(conf->large_client_header_buffers, + prev->large_client_header_buffers, + 4, ngx_pagesize); + + if (conf->large_client_header_buffers.size < conf->connection_pool_size) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "the \"large_client_header_buffers\" size must be " + "equal to or bigger than \"connection_pool_size\""); + return NGX_CONF_ERROR; + } + + ngx_conf_merge_unsigned_value(conf->restrict_host_names, + prev->restrict_host_names, 0); + + return NGX_CONF_OK; +} + + +static void *ngx_http_core_create_loc_conf(ngx_conf_t *cf) +{ + ngx_http_core_loc_conf_t *lcf; + + ngx_test_null(lcf, + ngx_pcalloc(cf->pool, sizeof(ngx_http_core_loc_conf_t)), + NGX_CONF_ERROR); + + /* set by ngx_pcalloc(): + + lcf->root.len = 0; + lcf->root.data = NULL; + lcf->types = NULL; + lcf->default_type.len = 0; + lcf->default_type.data = NULL; + lcf->err_log = NULL; + lcf->error_pages = NULL; + + lcf->regex = NULL; + lcf->exact_match = 0; + lcf->auto_redirect = 0; + lcf->alias = 0; + + */ + + lcf->client_max_body_size = NGX_CONF_UNSET_SIZE; + lcf->client_body_buffer_size = NGX_CONF_UNSET_SIZE; + lcf->client_body_timeout = NGX_CONF_UNSET_MSEC; + lcf->sendfile = NGX_CONF_UNSET; + lcf->tcp_nopush = 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->msie_padding = NGX_CONF_UNSET; + + return lcf; +} + + +static ngx_http_type_t default_types[] = { + { ngx_string("html"), ngx_string("text/html") }, + { ngx_string("gif"), ngx_string("image/gif") }, + { ngx_string("jpg"), ngx_string("image/jpeg") }, + { ngx_null_string, ngx_null_string } +}; + + +static char *ngx_http_core_merge_loc_conf(ngx_conf_t *cf, + void *parent, void *child) +{ + ngx_http_core_loc_conf_t *prev = parent; + ngx_http_core_loc_conf_t *conf = child; + + int i, key; + ngx_http_type_t *t; + + ngx_conf_merge_str_value(conf->root, prev->root, "html"); + + if (ngx_conf_full_name(cf->cycle, &conf->root) == NGX_ERROR) { + return NGX_CONF_ERROR; + } + + if (conf->types == NULL) { + if (prev->types) { + conf->types = prev->types; + + } else { + ngx_test_null(conf->types, + ngx_palloc(cf->pool, NGX_HTTP_TYPES_HASH_PRIME + * sizeof(ngx_array_t)), + NGX_CONF_ERROR); + + for (i = 0; i < NGX_HTTP_TYPES_HASH_PRIME; i++) { + ngx_init_array(conf->types[i], cf->pool, + 5, sizeof(ngx_http_type_t), NGX_CONF_ERROR); + } + + for (i = 0; default_types[i].exten.len; i++) { + ngx_http_types_hash_key(key, default_types[i].exten); + + ngx_test_null(t, ngx_push_array(&conf->types[key]), + NGX_CONF_ERROR); + t->exten.len = default_types[i].exten.len; + t->exten.data = default_types[i].exten.data; + t->type.len = default_types[i].type.len; + t->type.data = default_types[i].type.data; + } + } + } + + if (conf->err_log == NULL) { + if (prev->err_log) { + conf->err_log = prev->err_log; + } else { + conf->err_log = cf->cycle->new_log; + } + } + + if (conf->error_pages == NULL && prev->error_pages) { + conf->error_pages = prev->error_pages; + } + + ngx_conf_merge_str_value(conf->default_type, + prev->default_type, "text/plain"); + + ngx_conf_merge_size_value(conf->client_max_body_size, + prev->client_max_body_size, 1 * 1024 * 1024); + ngx_conf_merge_size_value(conf->client_body_buffer_size, + prev->client_body_buffer_size, + (size_t) 2 * ngx_pagesize); + ngx_conf_merge_msec_value(conf->client_body_timeout, + prev->client_body_timeout, 60000); + ngx_conf_merge_value(conf->sendfile, prev->sendfile, 0); + ngx_conf_merge_value(conf->tcp_nopush, prev->tcp_nopush, 0); + ngx_conf_merge_msec_value(conf->send_timeout, prev->send_timeout, 60000); + ngx_conf_merge_size_value(conf->send_lowat, prev->send_lowat, 0); + 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_msec_value(conf->keepalive_timeout, + prev->keepalive_timeout, 75000); + ngx_conf_merge_sec_value(conf->keepalive_header, + prev->keepalive_header, 0); + 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_value(conf->reset_timedout_connection, + prev->reset_timedout_connection, 0); + ngx_conf_merge_value(conf->msie_padding, prev->msie_padding, 1); + + if (conf->open_files == NULL) { + conf->open_files = prev->open_files; + } + + return NGX_CONF_OK; +} + + +static char *ngx_set_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_http_core_srv_conf_t *scf = conf; + + u_char *addr; + ngx_int_t port; + ngx_uint_t p; + struct hostent *h; + ngx_str_t *args; + ngx_http_listen_t *ls; + + /* + * TODO: check duplicate 'listen' directives, + * add resolved name to server names ??? + */ + + if (!(ls = ngx_array_push(&scf->listen))) { + return NGX_CONF_ERROR; + } + + /* AF_INET only */ + + ls->family = AF_INET; + ls->default_server = 0; + ls->file_name = cf->conf_file->file.name; + ls->line = cf->conf_file->line; + + args = cf->args->elts; + addr = args[1].data; + + for (p = 0; p < args[1].len; p++) { + if (addr[p] == ':') { + addr[p++] = '\0'; + break; + } + } + + if (p == args[1].len) { + /* no ":" in the "listen" */ + p = 0; + } + + port = ngx_atoi(&addr[p], args[1].len - p); + + if (port == NGX_ERROR && p == 0) { + + /* "listen host" */ + ls->port = 80; + + } else if ((port == NGX_ERROR && p != 0) /* "listen host:NONNUMBER" */ + || (port < 1 || port > 65536)) { /* "listen 99999" */ + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid port \"%s\" in \"%s\" directive, " + "it must be a number between 1 and 65535", + &addr[p], cmd->name.data); + + return NGX_CONF_ERROR; + + } else if (p == 0) { + ls->addr = INADDR_ANY; + ls->port = (in_port_t) port; + return NGX_CONF_OK; + + } else { + ls->port = (in_port_t) port; + } + + ls->addr = inet_addr((const char *) addr); + if (ls->addr == INADDR_NONE) { + h = gethostbyname((const char *) addr); + + if (h == NULL || h->h_addr_list[0] == NULL) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "can not resolve host \"%s\" " + "in \"%s\" directive", addr, cmd->name.data); + return NGX_CONF_ERROR; + } + + ls->addr = *(in_addr_t *)(h->h_addr_list[0]); + } + + return NGX_CONF_OK; +} + + +static char *ngx_set_server_name(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_http_core_srv_conf_t *scf = conf; + + ngx_uint_t i; + ngx_str_t *value; + ngx_http_server_name_t *sn; + ngx_http_core_main_conf_t *cmcf; + + /* TODO: several names */ + /* TODO: warn about duplicate 'server_name' directives */ + +#if 0 + ctx = (ngx_http_conf_ctx_t *) cf->cycle->conf_ctx[ngx_http_module.index]; + cmcf = ctx->main_conf[ngx_http_core_module.ctx_index]; +#endif + cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); + + value = cf->args->elts; + + for (i = 1; i < cf->args->nelts; i++) { + if (value[i].len == 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "server name \"%s\" is invalid " + "in \"%s\" directive", + value[i].data, cmd->name.data); + return NGX_CONF_ERROR; + } + + ngx_test_null(sn, ngx_push_array(&scf->server_names), NGX_CONF_ERROR); + + sn->name.len = value[i].len; + sn->name.data = value[i].data; + sn->core_srv_conf = scf; + + if (cmcf->max_server_name_len < sn->name.len) { + cmcf->max_server_name_len = sn->name.len; + } + } + + return NGX_CONF_OK; +} + + +static char *ngx_set_root(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_http_core_loc_conf_t *lcf = conf; + + ngx_uint_t alias; + ngx_str_t *value; + + 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 ((ngx_uint_t) lcf->alias == alias) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "\"%s\" directive is duplicate", + cmd->name.data); + } else { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "\"%s\" directive is duplicate, " + "\"%s\" directive is specified before", + cmd->name.data, lcf->alias ? "alias" : "root"); + } + + return NGX_CONF_ERROR; + } + + value = cf->args->elts; + + lcf->alias = alias; + lcf->root = value[1]; + + if (!alias && lcf->root.data[lcf->root.len - 1] == '/') { + lcf->root.len--; + } + + return NGX_CONF_OK; +} + + +static char *ngx_set_error_page(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_http_core_loc_conf_t *lcf = conf; + + int overwrite; + ngx_uint_t i, n; + ngx_str_t *value; + ngx_http_err_page_t *err; + + if (lcf->error_pages == NULL) { + lcf->error_pages = ngx_create_array(cf->pool, 5, + sizeof(ngx_http_err_page_t)); + if (lcf->error_pages == NULL) { + return NGX_CONF_ERROR; + } + } + + value = cf->args->elts; + + i = cf->args->nelts - 2; + + if (value[i].data[0] == '=') { + if (i == 1) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid value \"%s\"", value[i].data); + return NGX_CONF_ERROR; + } + + overwrite = ngx_atoi(&value[i].data[1], value[i].len - 1); + + if (overwrite == NGX_ERROR) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid value \"%s\"", value[i].data); + return NGX_CONF_ERROR; + } + + n = 2; + + } else { + overwrite = 0; + n = 1; + } + + for (i = 1; i < cf->args->nelts - n; i++) { + if (!(err = ngx_push_array(lcf->error_pages))) { + return NGX_CONF_ERROR; + } + + err->status = ngx_atoi(value[i].data, value[i].len); + if (err->status == NGX_ERROR) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid value \"%s\"", value[i].data); + return NGX_CONF_ERROR; + } + + if (err->status < 400 || err->status > 599) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "value \"%s\" must be between 400 and 599", + value[i].data); + return NGX_CONF_ERROR; + } + + err->overwrite = overwrite; + err->uri = value[cf->args->nelts - 1]; + } + + return NGX_CONF_OK; +} + + +static char *ngx_set_keepalive(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_http_core_loc_conf_t *lcf = conf; + + ngx_str_t *value; + + if (lcf->keepalive_timeout != NGX_CONF_UNSET_MSEC) { + return "is duplicate"; + } + + value = cf->args->elts; + + lcf->keepalive_timeout = ngx_parse_time(&value[1], 0); + if (lcf->keepalive_timeout == (ngx_msec_t) NGX_ERROR) { + return "invalid value"; + } + + if (lcf->keepalive_timeout == (ngx_msec_t) NGX_PARSE_LARGE_TIME) { + return "value must be less than 597 hours"; + } + + if (cf->args->nelts == 2) { + return NGX_CONF_OK; + } + + lcf->keepalive_header = ngx_parse_time(&value[2], 1); + if (lcf->keepalive_header == NGX_ERROR) { + return "invalid value"; + } + + if (lcf->keepalive_header == NGX_PARSE_LARGE_TIME) { + return "value must be less than 68 years"; + } + + return NGX_CONF_OK; +} + + +static char *ngx_set_error_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_http_core_loc_conf_t *lcf = conf; + + if (!(lcf->err_log = ngx_log_create_errlog(cf->cycle, cf->args))) { + return NGX_CONF_ERROR; + } + + return ngx_set_error_log_levels(cf, lcf->err_log); +} + + +static char *ngx_http_lowat_check(ngx_conf_t *cf, void *post, void *data) +{ +#if (HAVE_LOWAT_EVENT) + + ssize_t *np = data; + + if (*np >= ngx_freebsd_net_inet_tcp_sendspace) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "\"send_lowat\" must be less than %d " + "(sysctl net.inet.tcp.sendspace)", + ngx_freebsd_net_inet_tcp_sendspace); + + return NGX_CONF_ERROR; + } + +#else + + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "\"send_lowat\" is not supported, ignored"); + +#endif + + return NGX_CONF_OK; +} diff --git a/src/http/ngx_http_core_module.h b/src/http/ngx_http_core_module.h new file mode 100644 --- /dev/null +++ b/src/http/ngx_http_core_module.h @@ -0,0 +1,214 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#ifndef _NGX_HTTP_CORE_H_INCLUDED_ +#define _NGX_HTTP_CORE_H_INCLUDED_ + + +#include +#include +#include + + +typedef struct { + in_addr_t addr; + in_port_t port; + int family; + ngx_str_t file_name; + int line; + + unsigned default_server:1; +} ngx_http_listen_t; + + +typedef enum { + NGX_HTTP_REWRITE_PHASE = 0, + + NGX_HTTP_FIND_CONFIG_PHASE, + + NGX_HTTP_ACCESS_PHASE, + NGX_HTTP_CONTENT_PHASE, + + NGX_HTTP_LAST_PHASE +} ngx_http_phases; + + +typedef struct { + ngx_array_t handlers; + ngx_int_t type; /* NGX_OK, NGX_DECLINED */ +} ngx_http_phase_t; + + +typedef struct { + ngx_array_t servers; /* array of ngx_http_core_srv_conf_t */ + + ngx_http_phase_t phases[NGX_HTTP_LAST_PHASE]; + ngx_array_t index_handlers; + + size_t max_server_name_len; +} ngx_http_core_main_conf_t; + + +typedef struct { + /* + * array of ngx_http_core_loc_conf_t, used in the translation handler + * and in the merge phase + */ + ngx_array_t locations; + + /* "listen", array of ngx_http_listen_t */ + ngx_array_t listen; + + /* "server_name", array of ngx_http_server_name_t */ + ngx_array_t server_names; + + /* server ctx */ + ngx_http_conf_ctx_t *ctx; + + size_t connection_pool_size; + size_t request_pool_size; + size_t client_header_buffer_size; + + ngx_bufs_t large_client_header_buffers; + + ngx_msec_t post_accept_timeout; + ngx_msec_t client_header_timeout; + + ngx_uint_t restrict_host_names; +} ngx_http_core_srv_conf_t; + + +/* list of structures to find core_srv_conf quickly at run time */ + +typedef struct { + in_port_t port; + ngx_str_t port_text; + ngx_array_t addrs; /* array of ngx_http_in_addr_t */ +} ngx_http_in_port_t; + + +typedef struct { + in_addr_t addr; + ngx_array_t names; /* array of ngx_http_server_name_t */ + ngx_http_core_srv_conf_t *core_srv_conf; /* default server conf + for this address:port */ + + unsigned default_server:1; +} ngx_http_in_addr_t; + + +typedef struct { + ngx_str_t name; + ngx_http_core_srv_conf_t *core_srv_conf; /* virtual name server conf */ +} ngx_http_server_name_t; + + +#define NGX_HTTP_TYPES_HASH_PRIME 13 + +#define ngx_http_types_hash_key(key, ext) \ + { \ + u_int n; \ + for (key = 0, n = 0; n < ext.len; n++) { \ + key += ext.data[n]; \ + } \ + key %= NGX_HTTP_TYPES_HASH_PRIME; \ + } + +typedef struct { + ngx_str_t exten; + ngx_str_t type; +} ngx_http_type_t; + + +typedef struct { + ngx_int_t status; + ngx_int_t overwrite; + ngx_str_t uri; +} ngx_http_err_page_t; + + +typedef struct ngx_http_core_loc_conf_s ngx_http_core_loc_conf_t; + +struct ngx_http_core_loc_conf_s { + ngx_str_t name; /* location name */ + +#if (HAVE_PCRE) + ngx_regex_t *regex; +#endif + + unsigned exact_match:1; + unsigned auto_redirect:1; + unsigned alias:1; + + /* array of inclusive ngx_http_core_loc_conf_t */ + ngx_array_t locations; + + /* pointer to the modules' loc_conf */ + void **loc_conf ; + + ngx_http_handler_pt handler; + + ngx_str_t root; /* root, alias */ + + ngx_array_t *types; + ngx_str_t default_type; + + size_t client_max_body_size; /* client_max_body_size */ + 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 */ + + 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 */ + + time_t keepalive_header; /* keepalive_timeout */ + + ngx_flag_t sendfile; /* sendfile */ + ngx_flag_t tcp_nopush; /* tcp_nopush */ + ngx_flag_t reset_timedout_connection; /* reset_timedout_connection */ + ngx_flag_t msie_padding; /* msie_padding */ + + ngx_array_t *error_pages; /* error_page */ + + ngx_http_cache_hash_t *open_files; + + ngx_log_t *err_log; + + ngx_http_core_loc_conf_t *prev_location; +}; + + +extern ngx_http_module_t ngx_http_core_module_ctx; +extern ngx_module_t ngx_http_core_module; + +extern int ngx_http_max_module; + + + +ngx_int_t ngx_http_find_location_config(ngx_http_request_t *r); +ngx_int_t ngx_http_core_translate_handler(ngx_http_request_t *r); + +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); + +ngx_int_t ngx_http_internal_redirect(ngx_http_request_t *r, + ngx_str_t *uri, ngx_str_t *args); + + +typedef ngx_int_t (*ngx_http_output_header_filter_pt)(ngx_http_request_t *r); +typedef ngx_int_t (*ngx_http_output_body_filter_pt) + (ngx_http_request_t *r, ngx_chain_t *chain); + + +ngx_int_t ngx_http_output_filter(ngx_http_request_t *r, ngx_chain_t *chain); +ngx_int_t ngx_http_write_filter(ngx_http_request_t *r, ngx_chain_t *chain); + + +#endif /* _NGX_HTTP_CORE_H_INCLUDED_ */ diff --git a/src/http/ngx_http_file_cache.c b/src/http/ngx_http_file_cache.c new file mode 100644 --- /dev/null +++ b/src/http/ngx_http_file_cache.c @@ -0,0 +1,239 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include + + +#if (HAVE_OPENSSL_MD5_H) +#include +#else +#include +#endif + +#if (HAVE_OPENSSL_MD5) +#define MD5Init MD5_Init +#define MD5Update MD5_Update +#define MD5Final MD5_Final +#endif + + + +int ngx_http_cache_get_file(ngx_http_request_t *r, ngx_http_cache_ctx_t *ctx) +{ + MD5_CTX md5; + + /* we use offsetof() because sizeof() pads struct size to int size */ + ctx->header_size = offsetof(ngx_http_cache_header_t, key) + + ctx->key.len + 1; + + ctx->file.name.len = ctx->path->name.len + 1 + ctx->path->len + 32; + if (!(ctx->file.name.data = ngx_palloc(r->pool, ctx->file.name.len + 1))) { + return NGX_ERROR; + } + + ngx_memcpy(ctx->file.name.data, ctx->path->name.data, ctx->path->name.len); + + MD5Init(&md5); + MD5Update(&md5, (u_char *) ctx->key.data, ctx->key.len); + MD5Final(ctx->md5, &md5); + + ngx_md5_text(ctx->file.name.data + ctx->path->name.len + 1 + ctx->path->len, + ctx->md5); + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "file cache uri: %s, md5: %s", ctx->key.data, + ctx->file.name.data + ctx->path->name.len + 1 + ctx->path->len); + + ngx_create_hashed_filename(&ctx->file, ctx->path); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "file cache name: %s", ctx->file.name.data); + + /* TODO: look open files cache */ + + return ngx_http_cache_open_file(ctx, 0); +} + + +int ngx_http_cache_open_file(ngx_http_cache_ctx_t *ctx, ngx_file_uniq_t uniq) +{ + ssize_t n; + ngx_err_t err; + ngx_http_cache_header_t *h; + + ctx->file.fd = ngx_open_file(ctx->file.name.data, + NGX_FILE_RDONLY, NGX_FILE_OPEN); + + if (ctx->file.fd == NGX_INVALID_FILE) { + err = ngx_errno; + + if (err == NGX_ENOENT || err == NGX_ENOTDIR) { + return NGX_DECLINED; + } + + ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno, + ngx_open_file_n " \"%s\" failed", ctx->file.name.data); + return NGX_ERROR; + } + + if (uniq) { + if (ngx_fd_info(ctx->file.fd, &ctx->file.info) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno, + ngx_fd_info_n " \"%s\" failed", ctx->file.name.data); + + return NGX_ERROR; + } + + if (ngx_file_uniq(&ctx->file.info) == uniq) { + if (ngx_close_file(ctx->file.fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, ctx->log, ngx_errno, + ngx_close_file_n " \"%s\" failed", + ctx->file.name.data); + } + + return NGX_HTTP_CACHE_THE_SAME; + } + } + + n = ngx_read_file(&ctx->file, ctx->buf->pos, + ctx->buf->end - ctx->buf->last, 0); + + if (n == NGX_ERROR || n == NGX_AGAIN) { + return n; + } + + if (n <= ctx->header_size) { + ngx_log_error(NGX_LOG_CRIT, ctx->log, 0, + "cache file \"%s\" is too small", ctx->file.name.data); + return NGX_ERROR; + } + + h = (ngx_http_cache_header_t *) ctx->buf->pos; + ctx->expires = h->expires; + ctx->last_modified= h->last_modified; + ctx->date = h->date; + ctx->length = h->length; + + if (h->key_len > (size_t) (ctx->buf->end - ctx->buf->pos)) { + ngx_log_error(NGX_LOG_ALERT, ctx->log, 0, + "cache file \"%s\" is probably invalid", + ctx->file.name.data); + return NGX_DECLINED; + } + + if (ctx->key.len + && (h->key_len != ctx->key.len + || ngx_strncmp(h->key, ctx->key.data, h->key_len) != 0)) + { + h->key[h->key_len] = '\0'; + ngx_log_error(NGX_LOG_ALERT, ctx->log, 0, + "md5 collision: \"%s\" and \"%s\"", + h->key, ctx->key.data); + return NGX_DECLINED; + } + + ctx->buf->last += n; + + if (ctx->expires < ngx_time()) { + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->log, 0, + "http file cache expired"); + + return NGX_HTTP_CACHE_STALE; + } + + /* TODO: NGX_HTTP_CACHE_AGED */ + + return NGX_OK; +} + + +int ngx_http_cache_update_file(ngx_http_request_t *r, ngx_http_cache_ctx_t *ctx, + ngx_str_t *temp_file) +{ + int retry; + ngx_err_t err; + + retry = 0; + + for ( ;; ) { + if (ngx_rename_file(temp_file->data, ctx->file.name.data) == NGX_OK) { + return NGX_OK; + } + + err = ngx_errno; + +#if (WIN32) + if (err == NGX_EEXIST) { + if (ngx_win32_rename_file(temp_file, &ctx->file.name, r->pool) + == NGX_ERROR) + { + return NGX_ERROR; + } + } +#endif + + if (retry || (err != NGX_ENOENT && err != NGX_ENOTDIR)) { + ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno, + ngx_rename_file_n "(\"%s\", \"%s\") failed", + temp_file->data, ctx->file.name.data); + + return NGX_ERROR; + } + + if (ngx_create_path(&ctx->file, ctx->path) == NGX_ERROR) { + return NGX_ERROR; + } + + retry = 1; + } +} + + +int ngx_garbage_collector_http_cache_handler(ngx_gc_t *gc, ngx_str_t *name, + ngx_dir_t *dir) +{ + int rc; + char data[sizeof(ngx_http_cache_header_t)]; + ngx_hunk_t buf; + ngx_http_cache_ctx_t ctx; + + ctx.file.fd = NGX_INVALID_FILE; + ctx.file.name = *name; + ctx.file.log = gc->log; + + ctx.header_size = sizeof(ngx_http_cache_header_t); + ctx.buf = &buf; + ctx.log = gc->log; + ctx.key.len = 0; + + buf.type = NGX_HUNK_IN_MEMORY|NGX_HUNK_TEMP; + buf.pos = data; + buf.last = data; + buf.start = data; + buf.end = data + sizeof(ngx_http_cache_header_t); + + rc = ngx_http_cache_open_file(&ctx, 0); + + /* TODO: NGX_AGAIN */ + + if (rc != NGX_ERROR && rc != NGX_DECLINED && rc != NGX_HTTP_CACHE_STALE) { + return NGX_OK; + } + + if (ngx_delete_file(name->data) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_CRIT, gc->log, ngx_errno, + ngx_delete_file_n " \"%s\" failed", name->data); + return NGX_ERROR; + } + + gc->deleted++; + gc->freed += ngx_de_size(dir); + + return NGX_OK; +} diff --git a/src/http/ngx_http_header_filter.c b/src/http/ngx_http_header_filter.c new file mode 100644 --- /dev/null +++ b/src/http/ngx_http_header_filter.c @@ -0,0 +1,459 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include +#include + + +static ngx_int_t ngx_http_header_filter_init(ngx_cycle_t *cycle); +static ngx_int_t ngx_http_header_filter(ngx_http_request_t *r); + + +static ngx_http_module_t ngx_http_header_filter_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_header_filter_module = { + NGX_MODULE, + &ngx_http_header_filter_module_ctx, /* module context */ + NULL, /* module directives */ + NGX_HTTP_MODULE, /* module type */ + ngx_http_header_filter_init, /* init module */ + NULL /* init child */ +}; + + +static char server_string[] = "Server: " NGINX_VER CRLF; + + +static ngx_str_t http_codes[] = { + + ngx_string("200 OK"), + ngx_null_string, /* "201 Created" */ + ngx_null_string, /* "202 Accepted" */ + ngx_null_string, /* "203 Non-Authoritative Information" */ + ngx_null_string, /* "204 No Content" */ + ngx_null_string, /* "205 Reset Content" */ + ngx_string("206 Partial Content"), + ngx_null_string, /* "207 Multi-Status" */ + +#if 0 + ngx_null_string, /* "300 Multiple Choices" */ +#endif + + ngx_string("301 Moved Permanently"), +#if 0 + ngx_string("302 Moved Temporarily"), +#else + ngx_string("302 Found"), +#endif + ngx_null_string, /* "303 See Other" */ + ngx_string("304 Not Modified"), + + ngx_string("400 Bad Request"), + ngx_string("401 Unauthorized"), + ngx_null_string, /* "402 Payment Required" */ + ngx_string("403 Forbidden"), + ngx_string("404 Not Found"), + ngx_string("405 Not Allowed"), + ngx_null_string, /* "406 Not Acceptable" */ + ngx_null_string, /* "407 Proxy Authentication Required" */ + ngx_string("408 Request Time-out"), + ngx_null_string, /* "409 Conflict" */ + ngx_null_string, /* "410 Gone" */ + ngx_string("411 Length Required"), + ngx_null_string, /* "412 Precondition Failed" */ + ngx_string("413 Request Entity Too Large"), + ngx_null_string, /* "414 Request-URI Too Large" but we never send it + * because we treat such requests as the HTTP/0.9 + * requests and send only a body without a header + */ + ngx_null_string, /* "415 Unsupported Media Type" */ + ngx_string("416 Requested Range Not Satisfiable"), + + ngx_string("500 Internal Server Error"), + ngx_string("501 Method Not Implemented"), + ngx_string("502 Bad Gateway"), + ngx_string("503 Service Temporarily Unavailable"), + ngx_string("504 Gateway Time-out") +}; + + +ngx_http_header_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) }, + { ngx_string("Content-Type"), + offsetof(ngx_http_headers_out_t, content_type) }, + { ngx_string("Content-Length"), + offsetof(ngx_http_headers_out_t, content_length) }, + { ngx_string("Content-Encoding"), + offsetof(ngx_http_headers_out_t, content_encoding) }, + { ngx_string("Location"), offsetof(ngx_http_headers_out_t, location) }, + { ngx_string("Last-Modified"), + offsetof(ngx_http_headers_out_t, last_modified) }, + { ngx_string("Accept-Ranges"), + offsetof(ngx_http_headers_out_t, accept_ranges) }, + { ngx_string("Expires"), offsetof(ngx_http_headers_out_t, expires) }, + { ngx_string("Cache-Control"), + offsetof(ngx_http_headers_out_t, cache_control) }, + { ngx_string("ETag"), offsetof(ngx_http_headers_out_t, etag) }, + + { ngx_null_string, 0 } +}; + + +static ngx_int_t ngx_http_header_filter(ngx_http_request_t *r) +{ + u_char *p; + size_t len; + ngx_uint_t status, i; + ngx_buf_t *b; + ngx_chain_t *ln; + ngx_list_part_t *part; + ngx_table_elt_t *header; + ngx_http_core_loc_conf_t *clcf; + + if (r->http_version < NGX_HTTP_VERSION_10) { + return NGX_OK; + } + + if (r->method == NGX_HTTP_HEAD) { + r->header_only = 1; + } + + if (r->headers_out.last_modified_time != -1) { + if (r->headers_out.status != NGX_HTTP_OK + && r->headers_out.status != NGX_HTTP_NOT_MODIFIED + && r->headers_out.status != NGX_HTTP_PARTIAL_CONTENT) + { + r->headers_out.last_modified_time = -1; + r->headers_out.last_modified = NULL; + } + } + + /* 2 is for trailing "\r\n" and 2 is for "\r\n" in the end of header */ + len = sizeof("HTTP/1.x ") - 1 + 2 + 2; + + /* status line */ + if (r->headers_out.status_line.len) { + len += r->headers_out.status_line.len; +#if (NGX_SUPPRESS_WARN) + status = NGX_INVALID_ARRAY_INDEX; +#endif + + } else { + + if (r->headers_out.status < NGX_HTTP_MOVED_PERMANENTLY) { + /* 2XX */ + status = r->headers_out.status - NGX_HTTP_OK; + + } else if (r->headers_out.status < NGX_HTTP_BAD_REQUEST) { + /* 3XX */ + status = r->headers_out.status - NGX_HTTP_MOVED_PERMANENTLY + 8; + + if (r->headers_out.status == NGX_HTTP_NOT_MODIFIED) { + r->header_only = 1; + } + + } else if (r->headers_out.status < NGX_HTTP_INTERNAL_SERVER_ERROR) { + /* 4XX */ + status = r->headers_out.status - NGX_HTTP_BAD_REQUEST + 8 + 4; + + } else { + /* 5XX */ + status = r->headers_out.status + - NGX_HTTP_INTERNAL_SERVER_ERROR + 8 + 4 + 17; + } + + len += http_codes[status].len; + } + + if (r->headers_out.server && r->headers_out.server->key.len) { + len += r->headers_out.server->key.len + + r->headers_out.server->value.len + 2; + } else { + len += sizeof(server_string) - 1; + } + + if (r->headers_out.date && r->headers_out.date->key.len) { + len += r->headers_out.date->key.len + + r->headers_out.date->value.len + 2; + } else { + len += sizeof("Date: Mon, 28 Sep 1970 06:00:00 GMT" CRLF) - 1; + } + + if (r->headers_out.content_length == NULL) { + if (r->headers_out.content_length_n >= 0) { + len += sizeof("Content-Length: ") - 1 + NGX_OFF_T_LEN + 2; + } + } + + if (r->headers_out.content_type && r->headers_out.content_type->value.len) { + r->headers_out.content_type->key.len = 0; + len += sizeof("Content-Type: ") - 1 + + r->headers_out.content_type->value.len + 2; + + if (r->headers_out.charset.len) { + len += sizeof("; charset=") - 1 + r->headers_out.charset.len; + } + } + + if (r->headers_out.location + && r->headers_out.location->value.len + && r->headers_out.location->value.data[0] == '/') + { + r->headers_out.location->key.len = 0; + len += sizeof("Location: http://") - 1 + + r->server_name->len + r->headers_out.location->value.len + 2; + + if (r->port != 80) { + len += r->port_text->len; + } + } + + if (r->headers_out.last_modified && r->headers_out.last_modified->key.len) { + len += r->headers_out.last_modified->key.len + + r->headers_out.last_modified->value.len + 2; + + } else if (r->headers_out.last_modified_time != -1) { + len += sizeof("Last-Modified: Mon, 28 Sep 1970 06:00:00 GMT" CRLF) - 1; + } + + if (r->chunked) { + len += sizeof("Transfer-Encoding: chunked" CRLF) - 1; + } + + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + if (r->keepalive) { + len += sizeof("Connection: keep-alive" CRLF) - 1; + + /* + * MSIE and Opera ignore the "Keep-Alive: timeout=" header. + * MSIE keeps the connection alive for about 60-65 seconds. + * Opera keeps the connection alive very long. + * Mozilla keeps the connection alive for N plus about 1-10 seconds. + * Konqueror keeps the connection alive for about N seconds. + */ + + if (clcf->keepalive_header + && (r->headers_in.gecko || r->headers_in.konqueror)) + { + len += sizeof("Keep-Alive: timeout=") - 1 + TIME_T_LEN + 2; + } + + } else { + len += sizeof("Connection: closed" CRLF) - 1; + } + + part = &r->headers_out.headers.part; + header = part->elts; + + for (i = 0; /* void */; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + header = part->elts; + i = 0; + } + + if (header[i].key.len == 0) { + continue; + } + + /* 2 is for ": " and 2 is for "\r\n" */ + len += header[i].key.len + 2 + header[i].value.len + 2; + } + + if (!(b = ngx_create_temp_buf(r->pool, len))) { + return NGX_ERROR; + } + + /* "HTTP/1.x " */ + 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_cpymem(b->last, r->headers_out.status_line.data, + r->headers_out.status_line.len); + + } else { + b->last = ngx_cpymem(b->last, http_codes[status].data, + http_codes[status].len); + } + *(b->last++) = CR; *(b->last++) = LF; + + if (!(r->headers_out.server && r->headers_out.server->key.len)) { + b->last = ngx_cpymem(b->last, server_string, sizeof(server_string) - 1); + } + + if (!(r->headers_out.date && r->headers_out.date->key.len)) { + b->last = ngx_cpymem(b->last, "Date: ", sizeof("Date: ") - 1); + b->last = ngx_cpymem(b->last, ngx_cached_http_time.data, + ngx_cached_http_time.len); + + *(b->last++) = CR; *(b->last++) = LF; + } + + if (r->headers_out.content_length == NULL) { + if (r->headers_out.content_length_n >= 0) { + b->last += ngx_snprintf((char *) b->last, + sizeof("Content-Length: ") + NGX_OFF_T_LEN + 2, + "Content-Length: " OFF_T_FMT CRLF, + r->headers_out.content_length_n); + } + } + + if (r->headers_out.content_type && r->headers_out.content_type->value.len) { + b->last = ngx_cpymem(b->last, "Content-Type: ", + sizeof("Content-Type: ") - 1); + p = b->last; + b->last = ngx_cpymem(b->last, r->headers_out.content_type->value.data, + r->headers_out.content_type->value.len); + + if (r->headers_out.charset.len) { + b->last = ngx_cpymem(b->last, "; charset=", + sizeof("; charset=") - 1); + b->last = ngx_cpymem(b->last, r->headers_out.charset.data, + r->headers_out.charset.len); + + r->headers_out.content_type->value.len = b->last - p; + r->headers_out.content_type->value.data = p; + } + + *(b->last++) = CR; *(b->last++) = LF; + } + + if (r->headers_out.location + && r->headers_out.location->value.len + && r->headers_out.location->value.data[0] == '/') + { + p = b->last + sizeof("Location: ") - 1; + b->last = ngx_cpymem(b->last, "Location: http://", + sizeof("Location: http://") - 1); + b->last = ngx_cpymem(b->last, r->server_name->data, + r->server_name->len); + if (r->port != 80) { + b->last = ngx_cpymem(b->last, r->port_text->data, + r->port_text->len); + } + + b->last = ngx_cpymem(b->last, r->headers_out.location->value.data, + r->headers_out.location->value.len); + + r->headers_out.location->value.len = b->last - p; + r->headers_out.location->value.data = p; + + *(b->last++) = CR; *(b->last++) = LF; + } + + if (!(r->headers_out.last_modified && r->headers_out.last_modified->key.len) + && r->headers_out.last_modified_time != -1) + { + b->last = ngx_cpymem(b->last, "Last-Modified: ", + sizeof("Last-Modified: ") - 1); + b->last += ngx_http_time(b->last, r->headers_out.last_modified_time); + + *(b->last++) = CR; *(b->last++) = LF; + } + + if (r->chunked) { + b->last = ngx_cpymem(b->last, "Transfer-Encoding: chunked" CRLF, + sizeof("Transfer-Encoding: chunked" CRLF) - 1); + } + + if (r->keepalive) { + b->last = ngx_cpymem(b->last, "Connection: keep-alive" CRLF, + sizeof("Connection: keep-alive" CRLF) - 1); + + if (clcf->keepalive_header + && (r->headers_in.gecko || r->headers_in.konqueror)) + { + b->last += ngx_snprintf((char *) b->last, + sizeof("Keep-Alive: timeout=") + TIME_T_LEN + 2, + "Keep-Alive: timeout=" TIME_T_FMT CRLF, + clcf->keepalive_header); + } + + } else { + b->last = ngx_cpymem(b->last, "Connection: close" CRLF, + sizeof("Connection: close" CRLF) - 1); + } + + part = &r->headers_out.headers.part; + header = part->elts; + + for (i = 0; /* void */; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + header = part->elts; + i = 0; + } + + if (header[i].key.len == 0) { + continue; + } + + b->last = ngx_cpymem(b->last, header[i].key.data, header[i].key.len); + *(b->last++) = ':' ; *(b->last++) = ' ' ; + + b->last = ngx_cpymem(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 + + /* the end of HTTP header */ + *(b->last++) = CR; *(b->last++) = LF; + + r->header_size = b->last - b->pos; + + if (r->header_only) { + b->last_buf = 1; + } + + if (!(ln = ngx_alloc_chain_link(r->pool))) { + return NGX_ERROR; + } + + ln->buf = b; + ln->next = NULL; + + return ngx_http_write_filter(r, ln); +} + + +static ngx_int_t ngx_http_header_filter_init(ngx_cycle_t *cycle) +{ + ngx_http_top_header_filter = ngx_http_header_filter; + + return NGX_OK; +} diff --git a/src/http/ngx_http_log_handler.c b/src/http/ngx_http_log_handler.c new file mode 100644 --- /dev/null +++ b/src/http/ngx_http_log_handler.c @@ -0,0 +1,978 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include +#include + + +static u_char *ngx_http_log_addr(ngx_http_request_t *r, u_char *buf, + uintptr_t data); +static u_char *ngx_http_log_connection(ngx_http_request_t *r, u_char *buf, + uintptr_t data); +static u_char *ngx_http_log_pipe(ngx_http_request_t *r, u_char *buf, + uintptr_t data); +static u_char *ngx_http_log_time(ngx_http_request_t *r, u_char *buf, + uintptr_t data); +static u_char *ngx_http_log_msec(ngx_http_request_t *r, u_char *buf, + uintptr_t data); +static u_char *ngx_http_log_request(ngx_http_request_t *r, u_char *buf, + uintptr_t data); +static u_char *ngx_http_log_status(ngx_http_request_t *r, u_char *buf, + uintptr_t data); +static u_char *ngx_http_log_length(ngx_http_request_t *r, u_char *buf, + uintptr_t data); +static u_char *ngx_http_log_apache_length(ngx_http_request_t *r, u_char *buf, + uintptr_t data); +static u_char *ngx_http_log_header_in(ngx_http_request_t *r, u_char *buf, + uintptr_t data); +static u_char *ngx_http_log_connection_header_out(ngx_http_request_t *r, + u_char *buf, uintptr_t data); +static u_char *ngx_http_log_transfer_encoding_header_out(ngx_http_request_t *r, + u_char *buf, + uintptr_t data); +static u_char *ngx_http_log_unknown_header_in(ngx_http_request_t *r, + u_char *buf, uintptr_t data); +static u_char *ngx_http_log_header_out(ngx_http_request_t *r, u_char *buf, + uintptr_t data); +static u_char *ngx_http_log_unknown_header_out(ngx_http_request_t *r, u_char *buf, + uintptr_t data); + +static ngx_int_t ngx_http_log_pre_conf(ngx_conf_t *cf); +static void *ngx_http_log_create_main_conf(ngx_conf_t *cf); +static void *ngx_http_log_create_loc_conf(ngx_conf_t *cf); +static char *ngx_http_log_merge_loc_conf(ngx_conf_t *cf, void *parent, + void *child); +static char *ngx_http_log_set_log(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static char *ngx_http_log_set_format(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static ngx_int_t ngx_http_log_parse_format(ngx_conf_t *cf, ngx_array_t *ops, + ngx_str_t *line); + + +static ngx_command_t ngx_http_log_commands[] = { + + {ngx_string("log_format"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_2MORE, + ngx_http_log_set_format, + NGX_HTTP_MAIN_CONF_OFFSET, + 0, + NULL}, + + {ngx_string("access_log"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12, + ngx_http_log_set_log, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + NULL}, + + ngx_null_command +}; + + +ngx_http_module_t ngx_http_log_module_ctx = { + ngx_http_log_pre_conf, /* pre conf */ + + ngx_http_log_create_main_conf, /* create main configuration */ + NULL, /* init main configuration */ + + NULL, /* create server configuration */ + NULL, /* merge server configuration */ + + ngx_http_log_create_loc_conf, /* create location configration */ + ngx_http_log_merge_loc_conf /* merge location configration */ +}; + + +ngx_module_t ngx_http_log_module = { + NGX_MODULE, + &ngx_http_log_module_ctx, /* module context */ + ngx_http_log_commands, /* module directives */ + NGX_HTTP_MODULE, /* module type */ + NULL, /* init module */ + NULL /* init child */ +}; + + +static ngx_str_t http_access_log = ngx_string(NGX_HTTP_LOG_PATH); + + +static ngx_str_t ngx_http_combined_fmt = + ngx_string("%addr - - [%time] \"%request\" %status %apache_length " + "\"%{Referer}i\" \"%{User-Agent}i\""); + + +ngx_http_log_op_name_t ngx_http_log_fmt_ops[] = { + { ngx_string("addr"), INET_ADDRSTRLEN - 1, ngx_http_log_addr }, + { ngx_string("conn"), NGX_INT32_LEN, ngx_http_log_connection }, + { ngx_string("pipe"), 1, ngx_http_log_pipe }, + { ngx_string("time"), sizeof("28/Sep/1970:12:00:00") - 1, + ngx_http_log_time }, + { ngx_string("msec"), TIME_T_LEN + 4, ngx_http_log_msec }, + { ngx_string("request"), 0, ngx_http_log_request }, + { ngx_string("status"), 3, ngx_http_log_status }, + { ngx_string("length"), NGX_OFF_T_LEN, ngx_http_log_length }, + { ngx_string("apache_length"), NGX_OFF_T_LEN, ngx_http_log_apache_length }, + { ngx_string("i"), NGX_HTTP_LOG_ARG, ngx_http_log_header_in }, + { ngx_string("o"), NGX_HTTP_LOG_ARG, ngx_http_log_header_out }, + { ngx_null_string, 0, NULL } +}; + + +ngx_int_t ngx_http_log_handler(ngx_http_request_t *r) +{ + ngx_uint_t i, l; + uintptr_t data; + u_char *line, *p; + size_t len; + ngx_http_log_t *log; + ngx_http_log_op_t *op; + ngx_http_log_loc_conf_t *lcf; +#if (WIN32) + u_long written; +#endif + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http log handler"); + + lcf = ngx_http_get_module_loc_conf(r, ngx_http_log_module); + + if (lcf->off) { + return NGX_OK; + } + + log = lcf->logs->elts; + for (l = 0; l < lcf->logs->nelts; l++) { + + len = 0; + op = log[l].ops->elts; + for (i = 0; i < log[l].ops->nelts; i++) { + if (op[i].len == 0) { + len += (size_t) op[i].op(r, NULL, op[i].data); + + } else { + len += op[i].len; + } + } + +#if (WIN32) + len += 2; +#else + len++; +#endif + + ngx_test_null(line, ngx_palloc(r->pool, len), NGX_ERROR); + p = line; + + for (i = 0; i < log[l].ops->nelts; i++) { + if (op[i].op == NGX_HTTP_LOG_COPY_SHORT) { + len = op[i].len; + data = op[i].data; + while (len--) { + *p++ = (char) (data & 0xff); + data >>= 8; + } + + } else if (op[i].op == NGX_HTTP_LOG_COPY_LONG) { + p = ngx_cpymem(p, (void *) op[i].data, op[i].len); + + } else { + p = op[i].op(r, p, op[i].data); + } + } + +#if (WIN32) + *p++ = CR; *p++ = LF; + WriteFile(log[l].file->fd, line, p - line, &written, NULL); +#else + *p++ = LF; + write(log[l].file->fd, line, p - line); +#endif + } + + return NGX_OK; +} + + +static u_char *ngx_http_log_addr(ngx_http_request_t *r, u_char *buf, + uintptr_t data) +{ + return ngx_cpymem(buf, r->connection->addr_text.data, + r->connection->addr_text.len); +} + + +static u_char *ngx_http_log_connection(ngx_http_request_t *r, u_char *buf, + uintptr_t data) +{ + return buf + ngx_snprintf((char *) buf, NGX_INT_T_LEN + 1, + "%" NGX_UINT_T_FMT, + r->connection->number); +} + + +static u_char *ngx_http_log_pipe(ngx_http_request_t *r, u_char *buf, + uintptr_t data) +{ + if (r->pipeline) { + *buf = 'p'; + } else { + *buf = '.'; + } + + return buf + 1; +} + + +static u_char *ngx_http_log_time(ngx_http_request_t *r, u_char *buf, + uintptr_t data) +{ + return ngx_cpymem(buf, ngx_cached_http_log_time.data, + ngx_cached_http_log_time.len); +} + + +static u_char *ngx_http_log_msec(ngx_http_request_t *r, u_char *buf, + uintptr_t data) +{ + struct timeval tv; + + ngx_gettimeofday(&tv); + + return buf + ngx_snprintf((char *) buf, TIME_T_LEN + 5, "%ld.%03ld", + tv.tv_sec, tv.tv_usec / 1000); +} + + +static u_char *ngx_http_log_request(ngx_http_request_t *r, u_char *buf, + uintptr_t data) +{ + if (buf == NULL) { + /* find the request line length */ + return (u_char *) r->request_line.len; + } + + return ngx_cpymem(buf, r->request_line.data, r->request_line.len); +} + + +static u_char *ngx_http_log_status(ngx_http_request_t *r, u_char *buf, + uintptr_t data) +{ + return buf + ngx_snprintf((char *) buf, 4, "%" NGX_UINT_T_FMT, + r->err_status ? r->err_status : r->headers_out.status); +} + + +static u_char *ngx_http_log_length(ngx_http_request_t *r, u_char *buf, + uintptr_t data) +{ + return buf + ngx_snprintf((char *) buf, NGX_OFF_T_LEN + 1, OFF_T_FMT, + r->connection->sent); +} + + +static u_char *ngx_http_log_apache_length(ngx_http_request_t *r, u_char *buf, + uintptr_t data) +{ + return buf + ngx_snprintf((char *) buf, NGX_OFF_T_LEN + 1, OFF_T_FMT, + r->connection->sent - r->header_size); +} + + +static u_char *ngx_http_log_header_in(ngx_http_request_t *r, u_char *buf, + uintptr_t data) +{ + ngx_uint_t i; + ngx_str_t *s; + ngx_table_elt_t *h; + ngx_http_log_op_t *op; + + if (r) { + h = *(ngx_table_elt_t **) ((char *) &r->headers_in + data); + + if (h == NULL) { + + /* no header */ + + if (buf) { + *buf = '-'; + } + + return buf + 1; + } + + if (buf == NULL) { + /* find the header length */ + return (u_char *) h->value.len; + } + + return ngx_cpymem(buf, h->value.data, h->value.len); + } + + /* find an offset while a format string compilation */ + + op = (ngx_http_log_op_t *) buf; + s = (ngx_str_t *) data; + + op->len = 0; + + for (i = 0; ngx_http_headers_in[i].name.len != 0; i++) { + if (ngx_http_headers_in[i].name.len != s->len) { + continue; + } + + if (ngx_strncasecmp(ngx_http_headers_in[i].name.data, s->data, s->len) + == 0) + { + op->op = ngx_http_log_header_in; + op->data = ngx_http_headers_in[i].offset; + return NULL; + } + } + + op->op = ngx_http_log_unknown_header_in; + op->data = (uintptr_t) s; + + return NULL; +} + + +static u_char *ngx_http_log_unknown_header_in(ngx_http_request_t *r, + u_char *buf, uintptr_t data) +{ + ngx_uint_t i; + ngx_str_t *s; + ngx_list_part_t *part; + ngx_table_elt_t *h; + + s = (ngx_str_t *) data; + + part = &r->headers_in.headers.part; + h = part->elts; + + for (i = 0; /* void */; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + h = part->elts; + i = 0; + } + + if (h[i].key.len != s->len) { + continue; + } + + if (ngx_strncasecmp(h[i].key.data, s->data, s->len) == 0) { + if (buf == NULL) { + /* find the header length */ + return (u_char *) h[i].value.len; + } + + return ngx_cpymem(buf, h[i].value.data, h[i].value.len); + } + } + + /* no header */ + + if (buf) { + *buf = '-'; + } + + return buf + 1; +} + + +static u_char *ngx_http_log_header_out(ngx_http_request_t *r, u_char *buf, + uintptr_t data) +{ + ngx_uint_t i; + ngx_str_t *s; + ngx_table_elt_t *h; + ngx_http_log_op_t *op; + + if (r) { + + /* run-time execution */ + + if (r->http_version < NGX_HTTP_VERSION_10) { + if (buf) { + *buf = '-'; + } + + return buf + 1; + } + + h = *(ngx_table_elt_t **) ((char *) &r->headers_out + data); + + if (h == NULL) { + + /* + * No header pointer was found. + * However, some headers: "Date", "Server", "Content-Length", + * and "Last-Modified" have a special handling in the header filter + * but we do not set up their pointers in the filter because + * they are too seldom needed to be logged. + */ + + if (data == offsetof(ngx_http_headers_out_t, date)) { + if (buf == NULL) { + return (u_char *) ngx_cached_http_time.len; + } + return ngx_cpymem(buf, ngx_cached_http_time.data, + ngx_cached_http_time.len); + } + + if (data == offsetof(ngx_http_headers_out_t, server)) { + if (buf == NULL) { + return (u_char *) (sizeof(NGINX_VER) - 1); + } + return ngx_cpymem(buf, NGINX_VER, sizeof(NGINX_VER) - 1); + } + + if (data == offsetof(ngx_http_headers_out_t, content_length)) { + if (r->headers_out.content_length_n == -1) { + if (buf) { + *buf = '-'; + } + return buf + 1; + } + + if (buf == NULL) { + return (u_char *) NGX_OFF_T_LEN; + } + return buf + ngx_snprintf((char *) buf, + NGX_OFF_T_LEN + 2, OFF_T_FMT, + r->headers_out.content_length_n); + } + + if (data == offsetof(ngx_http_headers_out_t, last_modified)) { + if (r->headers_out.last_modified_time == -1) { + if (buf) { + *buf = '-'; + } + return buf + 1; + } + + if (buf == NULL) { + return (u_char *) + sizeof("Mon, 28 Sep 1970 06:00:00 GMT") - 1; + } + return buf + ngx_http_time(buf, + r->headers_out.last_modified_time); + } + + if (buf) { + *buf = '-'; + } + + return buf + 1; + } + + if (buf == NULL) { + /* find the header length */ + return (u_char *) h->value.len; + } + + return ngx_cpymem(buf, h->value.data, h->value.len); + } + + /* find an offset while a format string compilation */ + + op = (ngx_http_log_op_t *) buf; + s = (ngx_str_t *) data; + + op->len = 0; + + for (i = 0; ngx_http_headers_out[i].name.len != 0; i++) { + if (ngx_http_headers_out[i].name.len != s->len) { + continue; + } + + if (ngx_strncasecmp(ngx_http_headers_out[i].name.data, s->data, s->len) + == 0) + { + op->op = ngx_http_log_header_out; + op->data = ngx_http_headers_out[i].offset; + return NULL; + } + } + + if (s->len == sizeof("Connection") - 1 + && ngx_strncasecmp(s->data, "Connection", s->len) == 0) + { + op->op = ngx_http_log_connection_header_out; + op->data = (uintptr_t) NULL; + return NULL; + } + + if (s->len == sizeof("Transfer-Encoding") - 1 + && ngx_strncasecmp(s->data, "Transfer-Encoding", s->len) == 0) { + op->op = ngx_http_log_transfer_encoding_header_out; + op->data = (uintptr_t) NULL; + return NULL; + } + + op->op = ngx_http_log_unknown_header_out; + op->data = (uintptr_t) s; + + return NULL; +} + + +static u_char *ngx_http_log_connection_header_out(ngx_http_request_t *r, + u_char *buf, uintptr_t data) +{ + if (buf == NULL) { + return (u_char *) ((r->keepalive) ? sizeof("keep-alive") - 1: + sizeof("close") - 1); + } + + if (r->keepalive) { + return ngx_cpymem(buf, "keep-alive", sizeof("keep-alive") - 1); + + } else { + return ngx_cpymem(buf, "close", sizeof("close") - 1); + } +} + + +static u_char *ngx_http_log_transfer_encoding_header_out(ngx_http_request_t *r, + u_char *buf, + uintptr_t data) +{ + if (buf == NULL) { + return (u_char *) ((r->chunked) ? sizeof("chunked") - 1 : 1); + } + + if (r->chunked) { + return ngx_cpymem(buf, "chunked", sizeof("chunked") - 1); + } + + *buf = '-'; + + return buf + 1; +} + + +static u_char *ngx_http_log_unknown_header_out(ngx_http_request_t *r, + u_char *buf, + uintptr_t data) +{ + ngx_uint_t i; + ngx_str_t *s; + ngx_list_part_t *part; + ngx_table_elt_t *h; + + s = (ngx_str_t *) data; + + part = &r->headers_out.headers.part; + h = part->elts; + + for (i = 0; /* void */; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + h = part->elts; + i = 0; + } + + if (h[i].key.len != s->len) { + continue; + } + + if (ngx_strncasecmp(h[i].key.data, s->data, s->len) == 0) { + if (buf == NULL) { + /* find the header length */ + return (u_char *) h[i].value.len; + } + + return ngx_cpymem(buf, h[i].value.data, h[i].value.len); + } + } + + /* no header */ + + if (buf) { + *buf = '-'; + } + + return buf + 1; +} + + +static ngx_int_t ngx_http_log_pre_conf(ngx_conf_t *cf) +{ + ngx_http_log_op_name_t *op; + + for (op = ngx_http_log_fmt_ops; op->name.len; op++) { /* void */ } + op->op = NULL; + + return NGX_OK; +} + + +static void *ngx_http_log_create_main_conf(ngx_conf_t *cf) +{ + ngx_http_log_main_conf_t *conf; + + char *rc; + ngx_str_t *value; + + if (!(conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_log_main_conf_t)))) { + return NGX_CONF_ERROR; + } + + ngx_init_array(conf->formats, cf->pool, 5, sizeof(ngx_http_log_fmt_t), + NGX_CONF_ERROR); + + cf->args->nelts = 0; + + if (!(value = ngx_push_array(cf->args))) { + return NGX_CONF_ERROR; + } + + if (!(value = ngx_push_array(cf->args))) { + return NGX_CONF_ERROR; + } + + value->len = sizeof("combined") - 1; + value->data = (u_char *) "combined"; + + if (!(value = ngx_push_array(cf->args))) { + return NGX_CONF_ERROR; + } + + *value = ngx_http_combined_fmt; + + rc = ngx_http_log_set_format(cf, NULL, conf); + if (rc != NGX_CONF_OK) { + return NULL; + } + + return conf; +} + + +static void *ngx_http_log_create_loc_conf(ngx_conf_t *cf) +{ + ngx_http_log_loc_conf_t *conf; + + if (!(conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_log_loc_conf_t)))) { + return NGX_CONF_ERROR; + } + + return conf; +} + + +static char *ngx_http_log_merge_loc_conf(ngx_conf_t *cf, void *parent, + void *child) +{ + ngx_http_log_loc_conf_t *prev = parent; + ngx_http_log_loc_conf_t *conf = child; + + ngx_http_log_t *log; + ngx_http_log_fmt_t *fmt; + ngx_http_log_main_conf_t *lmcf; + + if (conf->logs == NULL) { + + if (conf->off) { + return NGX_CONF_OK; + } + + if (prev->logs) { + conf->logs = prev->logs; + + } else { + + if (prev->off) { + conf->off = prev->off; + return NGX_CONF_OK; + } + + conf->logs = ngx_array_create(cf->pool, 2, sizeof(ngx_http_log_t)); + if (conf->logs == NULL) { + return NGX_CONF_ERROR; + } + + if (!(log = ngx_array_push(conf->logs))) { + return NGX_CONF_ERROR; + } + + log->file = ngx_conf_open_file(cf->cycle, &http_access_log); + if (log->file == NULL) { + return NGX_CONF_ERROR; + } + + lmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_log_module); + fmt = lmcf->formats.elts; + + /* the default "combined" format */ + log->ops = fmt[0].ops; + } + } + + return NGX_CONF_OK; +} + + +static char *ngx_http_log_set_log(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf) +{ + ngx_http_log_loc_conf_t *llcf = conf; + + 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; + + value = cf->args->elts; + + if (ngx_strcmp(value[1].data, "off") == 0) { + llcf->off = 1; + return NGX_CONF_OK; + } + + if (llcf->logs == NULL) { + llcf->logs = ngx_array_create(cf->pool, 2, sizeof(ngx_http_log_t)); + if (llcf->logs == NULL) { + return NGX_CONF_ERROR; + } + } + + lmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_log_module); + + if (!(log = ngx_array_push(llcf->logs))) { + return NGX_CONF_ERROR; + } + + if (!(log->file = ngx_conf_open_file(cf->cycle, &value[1]))) { + return NGX_CONF_ERROR; + } + + if (cf->args->nelts == 3) { + name = value[2]; + } else { + name.len = sizeof("combined") - 1; + name.data = (u_char *) "combined"; + } + + fmt = lmcf->formats.elts; + for (i = 0; i < lmcf->formats.nelts; i++) { + if (fmt[i].name.len == name.len + && ngx_strcasecmp(fmt[i].name.data, name.data) == 0) + { + log->ops = fmt[i].ops; + return NGX_CONF_OK; + } + } + + return NGX_CONF_OK; +} + + +static char *ngx_http_log_set_format(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf) +{ + ngx_http_log_main_conf_t *lmcf = conf; + + ngx_uint_t s, f, invalid; + u_char *data, *p, *fname; + size_t i, len, fname_len; + ngx_str_t *value, arg, *a; + ngx_http_log_op_t *op; + ngx_http_log_fmt_t *fmt; + ngx_http_log_op_name_t *name; + + value = cf->args->elts; + + fmt = lmcf->formats.elts; + for (f = 0; f < lmcf->formats.nelts; f++) { + if (fmt[f].name.len == value[1].len + && ngx_strcmp(fmt->name.data, value[1].data) == 0) + { + return "duplicate \"log_format\" name"; + } + } + + if (!(fmt = ngx_push_array(&lmcf->formats))) { + return NGX_CONF_ERROR; + } + + fmt->name = value[1]; + + if (!(fmt->ops = ngx_create_array(cf->pool, 20, + sizeof(ngx_http_log_op_t)))) { + return NGX_CONF_ERROR; + } + + invalid = 0; + data = NULL; + + for (s = 2; s < cf->args->nelts && !invalid; s++) { + + i = 0; + + while (i < value[s].len) { + + if (!(op = ngx_push_array(fmt->ops))) { + return NGX_CONF_ERROR; + } + + data = &value[s].data[i]; + + if (value[s].data[i] == '%') { + i++; + + if (i == value[s].len) { + invalid = 1; + break; + } + + if (value[s].data[i] == '{') { + i++; + + arg.data = &value[s].data[i]; + + while (i < value[s].len && value[s].data[i] != '}') { + i++; + } + + arg.len = &value[s].data[i] - arg.data; + + if (i == value[s].len || arg.len == 0) { + invalid = 1; + break; + } + + i++; + + } else { + arg.len = 0; + } + + fname = &value[s].data[i]; + + while (i < value[s].len + && ((value[s].data[i] >= 'a' && value[s].data[i] <= 'z') + || value[s].data[i] == '_')) + { + i++; + } + + fname_len = &value[s].data[i] - fname; + + if (fname_len == 0) { + invalid = 1; + break; + } + + for (name = ngx_http_log_fmt_ops; name->op; name++) { + if (name->name.len == 0) { + name = (ngx_http_log_op_name_t *) name->op; + } + + if (name->name.len == fname_len + && ngx_strncmp(name->name.data, fname, fname_len) == 0) + { + if (name->len != NGX_HTTP_LOG_ARG) { + if (arg.len) { + fname[fname_len] = '\0'; + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "\"%s\" must not have argument", + data); + return NGX_CONF_ERROR; + } + + op->len = name->len; + op->op = name->op; + op->data = 0; + + break; + } + + if (arg.len == 0) { + fname[fname_len] = '\0'; + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "\"%s\" requires argument", + data); + return NGX_CONF_ERROR; + } + + if (!(a = ngx_palloc(cf->pool, sizeof(ngx_str_t)))) { + return NGX_CONF_ERROR; + } + + *a = arg; + name->op(NULL, (u_char *) op, (uintptr_t) a); + + break; + } + } + + if (name->name.len == 0) { + invalid = 1; + break; + } + + } else { + i++; + + while (i < value[s].len && value[s].data[i] != '%') { + i++; + } + + len = &value[s].data[i] - data; + + if (len) { + + op->len = len; + + if (len <= sizeof(uintptr_t)) { + op->op = NGX_HTTP_LOG_COPY_SHORT; + op->data = 0; + + while (len--) { + op->data <<= 8; + op->data |= data[len]; + } + + } else { + op->op = NGX_HTTP_LOG_COPY_LONG; + + if (!(p = ngx_palloc(cf->pool, len))) { + return NGX_CONF_ERROR; + } + + ngx_memcpy(p, data, len); + op->data = (uintptr_t) p; + } + } + } + } + } + + if (invalid) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid parameter \"%s\"", data); + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; +} diff --git a/src/http/ngx_http_log_handler.h b/src/http/ngx_http_log_handler.h new file mode 100644 --- /dev/null +++ b/src/http/ngx_http_log_handler.h @@ -0,0 +1,65 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#ifndef _NGX_HTTP_LOG_HANDLER_H_INCLUDED_ +#define _NGX_HTTP_LOG_HANDLER_H_INCLUDED_ + + +#include +#include +#include + + +typedef u_char *(*ngx_http_log_op_pt) (ngx_http_request_t *r, u_char *buf, + uintptr_t data); + +#define NGX_HTTP_LOG_COPY_SHORT (ngx_http_log_op_pt) 0 +#define NGX_HTTP_LOG_COPY_LONG (ngx_http_log_op_pt) -1 + +#define NGX_HTTP_LOG_ARG (u_int) -1 + + +typedef struct { + size_t len; + ngx_http_log_op_pt op; + uintptr_t data; +} ngx_http_log_op_t; + + +typedef struct { + ngx_str_t name; + ngx_array_t *ops; /* array of ngx_http_log_op_t */ +} ngx_http_log_fmt_t; + + +typedef struct { + ngx_str_t name; + size_t len; + ngx_http_log_op_pt op; +} ngx_http_log_op_name_t; + + +typedef struct { + ngx_array_t formats; /* array of ngx_http_log_fmt_t */ +} ngx_http_log_main_conf_t; + + +typedef struct { + ngx_open_file_t *file; + ngx_array_t *ops; /* array of ngx_http_log_op_t */ +} ngx_http_log_t; + + +typedef struct { + ngx_array_t *logs; /* array of ngx_http_log_t */ + ngx_uint_t off; /* unsigned off:1 */ +} ngx_http_log_loc_conf_t; + + +extern ngx_http_log_op_name_t ngx_http_log_fmt_ops[]; + + +#endif /* _NGX_HTTP_LOG_HANDLER_H_INCLUDED_ */ diff --git a/src/http/ngx_http_parse.c b/src/http/ngx_http_parse.c new file mode 100644 --- /dev/null +++ b/src/http/ngx_http_parse.c @@ -0,0 +1,868 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include + + +ngx_int_t ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b) +{ + u_char ch, *p, *m; + enum { + sw_start = 0, + sw_method, + sw_space_after_method, + sw_spaces_before_uri, + sw_schema, + sw_schema_slash, + sw_schema_slash_slash, + sw_host, + sw_port, + sw_after_slash_in_uri, + sw_check_uri, + sw_uri, + sw_http_09, + sw_http_H, + sw_http_HT, + sw_http_HTT, + sw_http_HTTP, + sw_first_major_digit, + sw_major_digit, + sw_first_minor_digit, + sw_minor_digit, + sw_almost_done, + sw_done + } state; + + state = r->state; + p = b->pos; + + while (p < b->last && state < sw_done) { + ch = *p++; + + /* gcc 2.95.2 and msvc 6.0 compile this switch as an jump table */ + + switch (state) { + + /* HTTP methods: GET, HEAD, POST */ + case sw_start: + r->request_start = p - 1; + + if (ch == CR || ch == LF) { + break; + } + + if (ch < 'A' || ch > 'Z') { + return NGX_HTTP_PARSE_INVALID_METHOD; + } + + state = sw_method; + break; + + case sw_method: + if (ch == ' ') { + r->method_end = p - 1; + m = r->request_start; + + if (r->method_end - m == 3) { + + if (m[0] == 'G' && m[1] == 'E' && m[2] == 'T') { + r->method = NGX_HTTP_GET; + } + + } else if (r->method_end - m == 4) { + + if (m[0] == 'P' && m[1] == 'O' + && m[2] == 'T' && m[3] == 'T') + { + r->method = NGX_HTTP_POST; + + } else if (m[0] == 'H' && m[1] == 'E' + && m[2] == 'A' && m[3] == 'D') + { + r->method = NGX_HTTP_HEAD; + } + } + + state = sw_spaces_before_uri; + break; + } + + if (ch < 'A' || ch > 'Z') { + return NGX_HTTP_PARSE_INVALID_METHOD; + } + + break; + + /* single space after method */ + case sw_space_after_method: + switch (ch) { + case ' ': + state = sw_spaces_before_uri; + break; + default: + return NGX_HTTP_PARSE_INVALID_METHOD; + } + break; + + /* space* before URI */ + case sw_spaces_before_uri: + switch (ch) { + case '/': + r->uri_start = p - 1; + state = sw_after_slash_in_uri; + break; + case ' ': + break; + default: + if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z')) { + r->schema_start = p - 1; + state = sw_schema; + break; + } + return NGX_HTTP_PARSE_INVALID_REQUEST; + } + break; + + case sw_schema: + switch (ch) { + case ':': + r->schema_end = p - 1; + state = sw_schema_slash; + break; + default: + if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z')) { + break; + } + return NGX_HTTP_PARSE_INVALID_REQUEST; + } + break; + + case sw_schema_slash: + switch (ch) { + case '/': + state = sw_schema_slash_slash; + break; + default: + return NGX_HTTP_PARSE_INVALID_REQUEST; + } + break; + + case sw_schema_slash_slash: + switch (ch) { + case '/': + r->host_start = p - 1; + state = sw_host; + break; + default: + return NGX_HTTP_PARSE_INVALID_REQUEST; + } + break; + + case sw_host: + switch (ch) { + case ':': + r->host_end = p - 1; + state = sw_port; + break; + case '/': + r->host_end = p - 1; + r->uri_start = p - 1; + state = sw_after_slash_in_uri; + break; + default: + if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') + || (ch >= '0' && ch <= '9') || ch == '.' || ch == '-') + { + break; + } + return NGX_HTTP_PARSE_INVALID_REQUEST; + } + break; + + case sw_port: + switch (ch) { + case '/': + r->port_end = p - 1; + r->uri_start = p - 1; + state = sw_after_slash_in_uri; + break; + default: + if (ch < '0' && ch > '9') { + return NGX_HTTP_PARSE_INVALID_REQUEST; + } + break; + } + break; + + /* check "/.", "//", and "%" in URI */ + case sw_after_slash_in_uri: + switch (ch) { + case CR: + r->uri_end = p - 1; + r->http_minor = 9; + state = sw_almost_done; + break; + case LF: + r->uri_end = p - 1; + r->http_minor = 9; + state = sw_done; + break; + case ' ': + r->uri_end = p - 1; + state = sw_http_09; + break; + case '.': + case '%': + r->complex_uri = 1; + state = sw_uri; + break; + case '/': + r->complex_uri = 1; + break; + case '?': + r->args_start = p; + state = sw_uri; + break; + default: + state = sw_check_uri; + break; + } + break; + + /* check "/" and "%" in URI */ + case sw_check_uri: + switch (ch) { + case CR: + r->uri_end = p - 1; + r->http_minor = 9; + state = sw_almost_done; + break; + case LF: + r->uri_end = p - 1; + r->http_minor = 9; + state = sw_done; + break; + case ' ': + r->uri_end = p - 1; + state = sw_http_09; + break; + case '.': + r->uri_ext = p; + break; + case '/': + r->uri_ext = NULL; + state = sw_after_slash_in_uri; + break; + case '%': + r->complex_uri = 1; + state = sw_uri; + break; + case '?': + r->args_start = p; + state = sw_uri; + break; + } + break; + + /* URI */ + case sw_uri: + switch (ch) { + case CR: + r->uri_end = p - 1; + r->http_minor = 9; + state = sw_almost_done; + break; + case LF: + r->uri_end = p - 1; + r->http_minor = 9; + state = sw_done; + break; + case ' ': + r->uri_end = p - 1; + state = sw_http_09; + break; + } + break; + + /* space+ after URI */ + case sw_http_09: + switch (ch) { + case ' ': + break; + case CR: + r->http_minor = 9; + state = sw_almost_done; + break; + case LF: + r->http_minor = 9; + state = sw_done; + break; + case 'H': + state = sw_http_H; + break; + default: + return NGX_HTTP_PARSE_INVALID_REQUEST; + } + break; + + case sw_http_H: + switch (ch) { + case 'T': + state = sw_http_HT; + break; + default: + return NGX_HTTP_PARSE_INVALID_REQUEST; + } + break; + + case sw_http_HT: + switch (ch) { + case 'T': + state = sw_http_HTT; + break; + default: + return NGX_HTTP_PARSE_INVALID_REQUEST; + } + break; + + case sw_http_HTT: + switch (ch) { + case 'P': + state = sw_http_HTTP; + break; + default: + return NGX_HTTP_PARSE_INVALID_REQUEST; + } + break; + + case sw_http_HTTP: + switch (ch) { + case '/': + state = sw_first_major_digit; + break; + default: + return NGX_HTTP_PARSE_INVALID_REQUEST; + } + break; + + /* first digit of major HTTP version */ + case sw_first_major_digit: + if (ch < '1' || ch > '9') { + return NGX_HTTP_PARSE_INVALID_REQUEST; + } + + r->http_major = ch - '0'; + state = sw_major_digit; + break; + + /* 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_PARSE_INVALID_REQUEST; + } + + r->http_major = r->http_major * 10 + ch - '0'; + break; + + /* first digit of minor HTTP version */ + case sw_first_minor_digit: + if (ch < '0' || ch > '9') { + return NGX_HTTP_PARSE_INVALID_REQUEST; + } + + r->http_minor = ch - '0'; + state = sw_minor_digit; + break; + + /* minor HTTP version or end of request line */ + case sw_minor_digit: + if (ch == CR) { + state = sw_almost_done; + break; + } + + if (ch == LF) { + state = sw_done; + break; + } + + if (ch < '0' || ch > '9') { + return NGX_HTTP_PARSE_INVALID_REQUEST; + } + + r->http_minor = r->http_minor * 10 + ch - '0'; + break; + + /* end of request line */ + case sw_almost_done: + r->request_end = p - 2; + switch (ch) { + case LF: + state = sw_done; + break; + default: + return NGX_HTTP_PARSE_INVALID_REQUEST; + } + break; + + /* suppress warning */ + case sw_done: + break; + } + } + + b->pos = p; + + if (state == sw_done) { + if (r->request_end == NULL) { + r->request_end = p - 1; + } + + r->http_version = r->http_major * 1000 + r->http_minor; + r->state = sw_start; + + if (r->http_version == 9 && r->method != NGX_HTTP_GET) { + return NGX_HTTP_PARSE_INVALID_09_METHOD; + } + + return NGX_OK; + + } else { + r->state = state; + return NGX_AGAIN; + } +} + + +ngx_int_t ngx_http_parse_header_line(ngx_http_request_t *r, ngx_buf_t *b) +{ + u_char c, ch, *p; + enum { + sw_start = 0, + sw_name, + sw_space_before_value, + sw_value, + sw_space_after_value, + sw_almost_done, + sw_header_almost_done, + sw_ignore_line, + sw_done, + sw_header_done + } state; + + state = r->state; + p = b->pos; + + while (p < b->last && state < sw_done) { + ch = *p++; + + switch (state) { + + /* first char */ + case sw_start: + switch (ch) { + case CR: + r->header_end = p - 1; + state = sw_header_almost_done; + break; + case LF: + r->header_end = p - 1; + state = sw_header_done; + break; + default: + state = sw_name; + r->header_name_start = p - 1; + + c = (u_char) (ch | 0x20); + if (c >= 'a' && c <= 'z') { + break; + } + + if (ch == '-' || ch == '_' || ch == '~' || ch == '.') { + break; + } + + if (ch >= '0' && ch <= '9') { + break; + } + + return NGX_HTTP_PARSE_INVALID_HEADER; + + } + break; + + /* header name */ + case sw_name: + c = (u_char) (ch | 0x20); + if (c >= 'a' && c <= 'z') { + break; + } + + if (ch == ':') { + r->header_name_end = p - 1; + state = sw_space_before_value; + break; + } + + if (ch == '-' || ch == '_' || ch == '~' || ch == '.') { + break; + } + + if (ch >= '0' && ch <= '9') { + break; + } + + /* IIS can send duplicate "HTTP/1.1 ..." lines */ + if (ch == '/' + && r->proxy + && p - r->header_start == 5 + && ngx_strncmp(r->header_start, "HTTP", 4) == 0) + { + state = sw_ignore_line; + break; + } + + return NGX_HTTP_PARSE_INVALID_HEADER; + + /* space* before header value */ + case sw_space_before_value: + switch (ch) { + case ' ': + break; + case CR: + r->header_start = r->header_end = p - 1; + state = sw_almost_done; + break; + case LF: + r->header_start = r->header_end = p - 1; + state = sw_done; + break; + default: + r->header_start = p - 1; + state = sw_value; + break; + } + break; + + /* header value */ + case sw_value: + switch (ch) { + case ' ': + r->header_end = p - 1; + state = sw_space_after_value; + break; + case CR: + r->header_end = p - 1; + state = sw_almost_done; + break; + case LF: + r->header_end = p - 1; + state = sw_done; + break; + } + break; + + /* space* before end of header line */ + case sw_space_after_value: + switch (ch) { + case ' ': + break; + case CR: + state = sw_almost_done; + break; + case LF: + state = sw_done; + break; + default: + state = sw_value; + break; + } + break; + + /* ignore header line */ + case sw_ignore_line: + switch (ch) { + case LF: + state = sw_start; + break; + default: + break; + } + break; + + /* end of header line */ + case sw_almost_done: + switch (ch) { + case LF: + state = sw_done; + break; + default: + return NGX_HTTP_PARSE_INVALID_HEADER; + } + break; + + /* end of header */ + case sw_header_almost_done: + switch (ch) { + case LF: + state = sw_header_done; + break; + default: + return NGX_HTTP_PARSE_INVALID_HEADER; + } + break; + + /* suppress warning */ + case sw_done: + case sw_header_done: + break; + } + } + + b->pos = p; + + if (state == sw_done) { + r->state = sw_start; + return NGX_OK; + + } else if (state == sw_header_done) { + r->state = sw_start; + return NGX_HTTP_PARSE_HEADER_DONE; + + } else { + r->state = state; + return NGX_AGAIN; + } +} + + +ngx_int_t ngx_http_parse_complex_uri(ngx_http_request_t *r) +{ + u_char c, ch, decoded, *p, *u; + enum { + sw_usual = 0, + sw_slash, + sw_dot, + sw_dot_dot, +#if (WIN32) + sw_dot_dot_dot, +#endif + sw_quoted, + sw_quoted_second + } state, quoted_state; + + decoded = '\0'; + quoted_state = sw_usual; + + state = sw_usual; + p = r->uri_start; + u = r->uri.data; + r->uri_ext = NULL; + + ch = *p++; + + while (p < r->uri_start + r->uri.len + 1) { + + ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "s:%d in:'%x:%c', out:'%c'", state, ch, ch, *u); + + switch (state) { + case sw_usual: + switch(ch) { + case '/': + r->uri_ext = NULL; + state = sw_slash; + *u++ = ch; + break; + case '%': + quoted_state = state; + state = sw_quoted; + break; + case '.': + r->uri_ext = u + 1; + default: + *u++ = ch; + break; + } + ch = *p++; + break; + + case sw_slash: + switch(ch) { + case '/': + break; + case '.': + state = sw_dot; + *u++ = ch; + break; + case '%': + quoted_state = state; + state = sw_quoted; + break; + default: + state = sw_usual; + *u++ = ch; + break; + } + ch = *p++; + break; + + case sw_dot: + switch(ch) { + case '/': + state = sw_slash; + u--; + break; + case '.': + state = sw_dot_dot; + *u++ = ch; + break; + case '%': + quoted_state = state; + state = sw_quoted; + break; + default: + state = sw_usual; + *u++ = ch; + break; + } + ch = *p++; + break; + + case sw_dot_dot: + switch(ch) { + 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; +#if (WIN32) + case '.': + state = sw_dot_dot_dot; + *u++ = ch; + break; +#endif + default: + state = sw_usual; + *u++ = ch; + break; + } + ch = *p++; + break; + +#if (WIN32) + case sw_dot_dot_dot: + switch(ch) { + 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) != '/') { + u--; + } + break; + case '%': + quoted_state = state; + state = sw_quoted; + break; + default: + state = sw_usual; + *u++ = ch; + break; + } + ch = *p++; + break; +#endif + + case sw_quoted: + if (ch >= '0' && ch <= '9') { + decoded = (u_char) (ch - '0'); + state = sw_quoted_second; + ch = *p++; + break; + } + + c = (u_char) (ch | 0x20); + if (c >= 'a' && c <= 'f') { + decoded = (u_char) (c - 'a' + 10); + state = sw_quoted_second; + ch = *p++; + break; + } + + return NGX_HTTP_PARSE_INVALID_REQUEST; + + case sw_quoted_second: + if (ch >= '0' && ch <= '9') { + ch = (u_char) ((decoded << 4) + ch - '0'); + if (ch == '%') { + state = sw_usual; + *u++ = ch; + ch = *p++; + break; + } + state = quoted_state; + break; + } + + c = (u_char) (ch | 0x20); + if (c >= 'a' && c <= 'f') { + ch = (u_char) ((decoded << 4) + c - 'a' + 10); + if (ch == '%') { + state = sw_usual; + *u++ = ch; + ch = *p++; + break; + } + state = quoted_state; + break; + } + + return NGX_HTTP_PARSE_INVALID_REQUEST; + } + } + + r->uri.len = u - r->uri.data; + r->uri.data[r->uri.len] = '\0'; + + if (r->uri_ext) { + r->exten.len = u - r->uri_ext; + + if (!(r->exten.data = ngx_palloc(r->pool, r->exten.len + 1))) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + ngx_cpystrn(r->exten.data, r->uri_ext, r->exten.len + 1); + } + + r->uri_ext = NULL; + + return NGX_OK; +} diff --git a/src/http/ngx_http_parse_time.c b/src/http/ngx_http_parse_time.c new file mode 100644 --- /dev/null +++ b/src/http/ngx_http_parse_time.c @@ -0,0 +1,287 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include + + +static int 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) +{ + u_char *p, *end; + int day, month, year, hour, min, sec; + enum { + no = 0, + rfc822, /* Tue 10 Nov 2002 23:50:13 */ + rfc850, /* Tuesday, 10-Dec-02 23:50:13 */ + isoc /* Tue Dec 10 23:50:13 2002 */ + } fmt; + + fmt = 0; + end = value + len; + +#if (NGX_SUPPRESS_WARN) + day = 32; + year = 2038; +#endif + + for (p = value; p < end; p++) { + if (*p == ',') { + break; + } + + if (*p == ' ') { + fmt = isoc; + break; + } + } + + for (p++; p < end; p++) + if (*p != ' ') { + break; + } + + if (end - p < 18) { + return NGX_ERROR; + } + + if (fmt != isoc) { + if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9') { + return NGX_ERROR; + } + + day = (*p - '0') * 10 + *(p + 1) - '0'; + p += 2; + + if (*p == ' ') { + if (end - p < 18) { + return NGX_ERROR; + } + fmt = rfc822; + + } else if (*p == '-') { + fmt = rfc850; + + } else { + return NGX_ERROR; + } + + p++; + } + + switch (*p) { + + case 'J': + month = *(p + 1) == 'a' ? 0 : *(p + 2) == 'n' ? 5 : 6; + break; + + case 'F': + month = 1; + break; + + case 'M': + month = *(p + 2) == 'r' ? 2 : 4; + break; + + case 'A': + month = *(p + 1) == 'p' ? 3 : 7; + break; + + case 'S': + month = 8; + break; + + case 'O': + month = 9; + break; + + case 'N': + month = 10; + break; + + case 'D': + month = 11; + break; + + default: + return NGX_ERROR; + } + + p += 3; + + if ((fmt == rfc822 && *p != ' ') || (fmt == rfc850 && *p != '-')) { + return NGX_ERROR; + } + + p++; + + if (fmt == rfc822) { + if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9' + || *(p + 2) < '0' || *(p + 2) > '9' + || *(p + 3) < '0' || *(p + 3) > '9') + { + return NGX_ERROR; + } + + year = (*p - '0') * 1000 + (*(p + 1) - '0') * 100 + + (*(p + 2) - '0') * 10 + *(p + 3) - '0'; + p += 4; + + } else if (fmt == rfc850) { + if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9') { + return NGX_ERROR; + } + + year = (*p - '0') * 10 + *(p + 1) - '0'; + year += (year < 70) ? 2000 : 1900; + p += 2; + } + + if (fmt == isoc) { + if (*p == ' ') { + p++; + } + + if (*p < '0' || *p > '9') { + return NGX_ERROR; + } + + day = *p++ - '0'; + + if (*p != ' ') { + if (*p < '0' || *p > '9') { + return NGX_ERROR; + } + + day = day * 10 + *p++ - '0'; + } + + if (end - p < 14) { + return NGX_ERROR; + } + } + + if (*p++ != ' ') { + return NGX_ERROR; + } + + if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9') { + return NGX_ERROR; + } + + hour = (*p - '0') * 10 + *(p + 1) - '0'; + p += 2; + + if (*p++ != ':') { + return NGX_ERROR; + } + + if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9') { + return NGX_ERROR; + } + + min = (*p - '0') * 10 + *(p + 1) - '0'; + p += 2; + + if (*p++ != ':') { + return NGX_ERROR; + } + + if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9') { + return NGX_ERROR; + } + + sec = (*p - '0') * 10 + *(p + 1) - '0'; + + if (fmt == isoc) { + p += 2; + + if (*p++ != ' ') { + return NGX_ERROR; + } + + if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9' + || *(p + 2) < '0' || *(p + 2) > '9' + || *(p + 3) < '0' || *(p + 3) > '9') + { + return NGX_ERROR; + } + + year = (*p - '0') * 1000 + (*(p + 1) - '0') * 100 + + (*(p + 2) - '0') * 10 + *(p + 3) - '0'; + } + +#if 0 + printf("%d.%d.%d %d:%d:%d\n", day, month + 1, year, hour, min, sec); +#endif + + if (hour > 23 || min > 59 || sec > 59) { + return NGX_ERROR; + } + + if (day == 29 && month == 1) { + if ((year & 3) || ((year % 100 == 0) && (year % 400) != 0)) { + return NGX_ERROR; + } + + } else if (day > mday[month]) { + return NGX_ERROR; + } + + if (sizeof(time_t) <= 4 && year >= 2038) { + return NGX_ERROR; + } + + /* + * shift new year to March 1 and start months from 1 (not 0), + * it's needed for Gauss's formula + */ + + if (--month <= 0) { + month += 12; + year -= 1; + } + + /* Gauss's formula for Grigorian days from 1 March 1 BC */ + + return (365 * year + year / 4 - year / 100 + year / 400 + + 367 * month / 12 - 31 + + day + + /* + * 719527 days were between March 1, 1 BC and March 1, 1970, + * 31 and 28 days in January and February 1970 + */ + + - 719527 + 31 + 28) * 86400 + hour * 3600 + min * 60 + sec; +} + +#if 0 +char zero[] = "Sun, 01 Jan 1970 08:49:30"; +char one[] = "Sunday, 11-Dec-02 08:49:30"; +char two[] = "Sun Mar 1 08:49:37 2000"; +char thr[] = "Sun Dec 11 08:49:37 2002"; + +main() +{ + int rc; + + rc = ngx_http_parse_time(zero, sizeof(zero) - 1); + printf("rc: %d\n", rc); + + rc = ngx_http_parse_time(one, sizeof(one) - 1); + printf("rc: %d\n", rc); + + rc = ngx_http_parse_time(two, sizeof(two) - 1); + printf("rc: %d\n", rc); + + rc = ngx_http_parse_time(thr, sizeof(thr) - 1); + printf("rc: %d\n", rc); +} + +#endif diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c new file mode 100644 --- /dev/null +++ b/src/http/ngx_http_request.c @@ -0,0 +1,2149 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include +#include + + +static void ngx_http_init_request(ngx_event_t *ev); +#if (NGX_HTTP_SSL) +static void ngx_http_ssl_handshake(ngx_event_t *rev); +#endif +static void ngx_http_process_request_line(ngx_event_t *rev); +static void ngx_http_process_request_headers(ngx_event_t *rev); +static ssize_t ngx_http_read_request_header(ngx_http_request_t *r); +static ngx_int_t ngx_http_alloc_large_header_buffer(ngx_http_request_t *r, + ngx_uint_t request_line); +static ngx_int_t ngx_http_process_request_header(ngx_http_request_t *r); + +static void ngx_http_set_write_handler(ngx_http_request_t *r); + +static void ngx_http_block_read(ngx_event_t *ev); +static void ngx_http_read_discarded_body_event(ngx_event_t *rev); +static ngx_int_t ngx_http_read_discarded_body(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 void ngx_http_client_error(ngx_http_request_t *r, + int client_error, int error); +static size_t ngx_http_log_error(void *data, char *buf, size_t len); + + +/* NGX_HTTP_PARSE_... errors */ + +static char *client_header_errors[] = { + "client %s sent invalid method", + "client %s sent invalid request", + "client %s sent too long URI", + "client %s sent invalid method in HTTP/0.9 request", + + "client %s sent invalid header, URL: %s", + "client %s sent too long header line, URL: %s", + "client %s sent HTTP/1.1 request without \"Host\" header, URL: %s", + "client %s sent invalid \"Content-Length\" header, URL: %s", + "client %s sent POST method without \"Content-Length\" header, URL: %s", + "client %s sent plain HTTP request to HTTPS port, URL: %s", + "client %s sent invalid \"Host\" header \"%s\", URL: %s" +}; + + +ngx_http_header_t ngx_http_headers_in[] = { + { ngx_string("Host"), offsetof(ngx_http_headers_in_t, host) }, + { ngx_string("Connection"), offsetof(ngx_http_headers_in_t, connection) }, + { ngx_string("If-Modified-Since"), + offsetof(ngx_http_headers_in_t, if_modified_since) }, + { ngx_string("User-Agent"), offsetof(ngx_http_headers_in_t, user_agent) }, + { ngx_string("Referer"), offsetof(ngx_http_headers_in_t, referer) }, + { ngx_string("Content-Length"), + offsetof(ngx_http_headers_in_t, content_length) }, + + { ngx_string("Range"), offsetof(ngx_http_headers_in_t, range) }, +#if 0 + { ngx_string("If-Range"), offsetof(ngx_http_headers_in_t, if_range) }, +#endif + +#if (NGX_HTTP_GZIP) + { ngx_string("Accept-Encoding"), + offsetof(ngx_http_headers_in_t, accept_encoding) }, + { ngx_string("Via"), offsetof(ngx_http_headers_in_t, via) }, +#endif + + { ngx_string("Authorization"), + offsetof(ngx_http_headers_in_t, authorization) }, + + { ngx_string("Keep-Alive"), offsetof(ngx_http_headers_in_t, keep_alive) }, + +#if (NGX_HTTP_PROXY) + { ngx_string("X-Forwarded-For"), + offsetof(ngx_http_headers_in_t, x_forwarded_for) }, +#endif + + { ngx_null_string, 0 } +}; + + +#if 0 +static void ngx_http_dummy(ngx_event_t *wev) +{ + return; +} +#endif + + +void ngx_http_init_connection(ngx_connection_t *c) +{ + ngx_event_t *rev; + ngx_http_log_ctx_t *ctx; + + if (!(ctx = ngx_pcalloc(c->pool, sizeof(ngx_http_log_ctx_t)))) { + ngx_http_close_connection(c); + return; + } + + ctx->connection = c->number; + ctx->client = c->addr_text.data; + ctx->action = "reading client request line"; + c->log->data = ctx; + c->log->handler = ngx_http_log_error; + c->log_error = NGX_ERROR_INFO; + + rev = c->read; + rev->event_handler = ngx_http_init_request; + + /* STUB: epoll edge */ c->write->event_handler = ngx_http_empty_handler; + + if (rev->ready) { + /* the deferred accept(), rtsig, aio, iocp */ + + if (ngx_accept_mutex) { + if (ngx_mutex_lock(ngx_posted_events_mutex) == NGX_ERROR) { + ngx_http_close_connection(c); + return; + } + + ngx_post_event(rev); + + ngx_mutex_unlock(ngx_posted_events_mutex); + return; + } + +#if (NGX_STAT_STUB) + (*ngx_stat_reading)++; +#endif + + ngx_http_init_request(rev); + return; + } + + ngx_add_timer(rev, c->listening->post_accept_timeout); + + if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { + ngx_http_close_connection(c); + return; + } + +#if 0 + /* TODO: learn SO_SNDBUF (to use in zerocopy) via kqueue's EV_CLEAR event */ + + c->write->ready = 0; + c->write->event_handler = ngx_http_dummy; + + if (ngx_handle_write_event(c->write, 0) == NGX_ERROR) { + ngx_http_close_connection(c); + return; + } +#endif + +#if (NGX_STAT_STUB) + (*ngx_stat_reading)++; +#endif + +} + + +static void ngx_http_init_request(ngx_event_t *rev) +{ + ngx_uint_t i; + socklen_t len; + struct sockaddr_in addr_in; + ngx_connection_t *c; + ngx_http_request_t *r; + ngx_http_in_port_t *in_port; + ngx_http_in_addr_t *in_addr; + ngx_http_connection_t *hc; + ngx_http_server_name_t *server_name; + ngx_http_core_srv_conf_t *cscf; + ngx_http_core_loc_conf_t *clcf; +#if (NGX_HTTP_SSL) + ngx_http_ssl_srv_conf_t *sscf; +#endif + + c = rev->data; + + if (rev->timedout) { + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); + +#if (NGX_STAT_STUB) + (*ngx_stat_reading)--; +#endif + + ngx_http_close_connection(c); + return; + } + + hc = c->data; + + if (hc) { + +#if (NGX_STAT_STUB) + (*ngx_stat_reading)++; +#endif + + } else { + if (!(hc = ngx_pcalloc(c->pool, sizeof(ngx_http_connection_t)))) { + +#if (NGX_STAT_STUB) + (*ngx_stat_reading)--; +#endif + + ngx_http_close_connection(c); + return; + } + } + + r = hc->request; + + if (r) { + ngx_memzero(r, sizeof(ngx_http_request_t)); + + r->pipeline = hc->pipeline; + + if (hc->nbusy) { + r->header_in = hc->busy[0]; + } + + } else { + if (!(r = ngx_pcalloc(c->pool, sizeof(ngx_http_request_t)))) { + +#if (NGX_STAT_STUB) + (*ngx_stat_reading)--; +#endif + + ngx_http_close_connection(c); + return; + } + + hc->request = r; + } + +#if (NGX_STAT_STUB) + r->stat_reading = 1; +#endif + + c->data = r; + r->http_connection = hc; + + c->sent = 0; + r->signature = NGX_HTTP_MODULE; + + /* find the server configuration for the address:port */ + + /* AF_INET only */ + + in_port = c->servers; + in_addr = in_port->addrs.elts; + + r->port = in_port->port; + r->port_text = &in_port->port_text; + + i = 0; + + if (in_port->addrs.nelts > 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. + */ + +#if (WIN32) + if (c->local_sockaddr) { + r->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 *) &addr_in, &len) == -1) { + ngx_connection_error(c, ngx_socket_errno, + "getsockname() failed"); + ngx_http_close_connection(c); + return; + } + + r->in_addr = addr_in.sin_addr.s_addr; + +#if (WIN32) + } +#endif + + /* the last in_port->addrs address is "*" */ + + for ( /* void */ ; i < in_port->addrs.nelts - 1; i++) { + if (in_addr[i].addr == r->in_addr) { + break; + } + } + + } else { + r->in_addr = in_addr[0].addr; + } + + r->virtual_names = &in_addr[i].names; + + /* the default server configuration for the address:port */ + cscf = in_addr[i].core_srv_conf; + + r->main_conf = cscf->ctx->main_conf; + r->srv_conf = cscf->ctx->srv_conf; + r->loc_conf = cscf->ctx->loc_conf; + + rev->event_handler = ngx_http_process_request_line; + +#if (NGX_HTTP_SSL) + + sscf = ngx_http_get_module_srv_conf(r, ngx_http_ssl_module); + if (sscf->enable) { + + if (c->ssl == NULL) { + if (ngx_ssl_create_session(sscf->ssl_ctx, c, NGX_SSL_BUFFER) + == NGX_ERROR) + { + ngx_http_close_connection(c); + return; + } + + /* + * The majority of browsers do not send the "close notify" alert. + * Among them are MSIE, Mozilla, Netscape 4, Konqueror, and Links. + * And what is more MSIE ignores the server's alert. + * + * Opera always sends the alert. + */ + + c->ssl->no_rcv_shut = 1; + rev->event_handler = ngx_http_ssl_handshake; + } + + r->filter_need_in_memory = 1; + } + +#endif + + server_name = cscf->server_names.elts; + r->server_name = &server_name->name; + + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + c->log->file = clcf->err_log->file; + if (!(c->log->log_level & NGX_LOG_DEBUG_CONNECTION)) { + c->log->log_level = clcf->err_log->log_level; + } + + if (c->buffer == NULL) { + c->buffer = ngx_create_temp_buf(c->pool, + cscf->client_header_buffer_size); + if (c->buffer == NULL) { + ngx_http_close_connection(c); + return; + } + } + + if (r->header_in == NULL) { + r->header_in = c->buffer; + } + + if (!(r->pool = ngx_create_pool(cscf->request_pool_size, c->log))) { + ngx_http_close_connection(c); + return; + } + + if (ngx_array_init(&r->cleanup, r->pool, 5, sizeof(ngx_http_cleanup_t)) + == NGX_ERROR) + { + ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + ngx_http_close_connection(c); + return; + } + + + if (ngx_list_init(&r->headers_out.headers, r->pool, 20, + sizeof(ngx_table_elt_t)) == NGX_ERROR) + { + ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + 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_http_close_connection(c); + return; + } + + c->single_connection = 1; + r->connection = c; + + r->file.fd = NGX_INVALID_FILE; + + r->headers_in.content_length_n = -1; + r->headers_in.keep_alive_n = -1; + r->headers_out.content_length_n = -1; + r->headers_out.last_modified_time = -1; + + r->http_state = NGX_HTTP_READING_REQUEST_STATE; + +#if (NGX_STAT_STUB) + (*ngx_stat_requests)++; +#endif + + rev->event_handler(rev); +} + + +#if (NGX_HTTP_SSL) + +static void ngx_http_ssl_handshake(ngx_event_t *rev) +{ + int n; + ngx_int_t rc; + u_char buf[1]; + ngx_connection_t *c; + ngx_http_request_t *r; + + c = rev->data; + r = c->data; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, + "http check ssl handshake"); + + if (rev->timedout) { + ngx_http_client_error(r, 0, NGX_HTTP_REQUEST_TIME_OUT); + return; + } + + n = recv(c->fd, buf, 1, MSG_PEEK); + + if (n == -1 && ngx_socket_errno == NGX_EAGAIN) { + return; + } + + if (n == 1) { + if (buf[0] == 0x80 /* SSLv2 */ || buf[0] == 0x16 /* SSLv3/TLSv1 */) { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, rev->log, 0, + "https ssl handshake: 0x%X", buf[0]); + + c->recv = ngx_ssl_recv; + c->send_chain = ngx_ssl_send_chain; + + rc = ngx_ssl_handshake(c); + + if (rc == NGX_ERROR) { + ngx_http_close_request(r, NGX_HTTP_BAD_REQUEST); + ngx_http_close_connection(r->connection); + return; + } + + if (rc != NGX_OK) { + return; + } + + } else { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, + "plain http"); + + r->plain_http = 1; + } + } + + rev->event_handler = ngx_http_process_request_line; + ngx_http_process_request_line(rev); +} + +#endif + + +static void ngx_http_process_request_line(ngx_event_t *rev) +{ + u_char *p; + ssize_t n; + ngx_int_t rc, rv; + ngx_connection_t *c; + ngx_http_request_t *r; + ngx_http_log_ctx_t *ctx; + + c = rev->data; + r = c->data; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, + "http process request line"); + + if (rev->timedout) { + ngx_http_client_error(r, 0, NGX_HTTP_REQUEST_TIME_OUT); + return; + } + + rc = NGX_AGAIN; + + for ( ;; ) { + + if (rc == NGX_AGAIN) { + n = ngx_http_read_request_header(r); + + if (n == NGX_AGAIN || n == NGX_ERROR) { + return; + } + } + + rc = ngx_http_parse_request_line(r, r->header_in); + + if (rc == NGX_OK) { + + /* the request line has been parsed successfully */ + + /* copy unparsed URI */ + + r->unparsed_uri.len = r->uri_end - r->uri_start; + r->unparsed_uri.data = ngx_palloc(r->pool, r->unparsed_uri.len + 1); + if (r->unparsed_uri.data == NULL) { + ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + ngx_http_close_connection(c); + return; + } + + ngx_cpystrn(r->unparsed_uri.data, r->uri_start, + r->unparsed_uri.len + 1); + + + /* copy URI */ + + if (r->args_start) { + r->uri.len = r->args_start - 1 - r->uri_start; + } else { + r->uri.len = r->uri_end - r->uri_start; + } + + if (!(r->uri.data = ngx_palloc(r->pool, r->uri.len + 1))) { + ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + ngx_http_close_connection(c); + return; + } + + if (r->complex_uri) { + rc = ngx_http_parse_complex_uri(r); + + if (rc == NGX_HTTP_INTERNAL_SERVER_ERROR) { + ngx_http_close_request(r, rc); + ngx_http_close_connection(c); + return; + } + + if (rc != NGX_OK) { + r->request_line.len = r->request_end - r->request_start; + r->request_line.data = r->request_start; + + ngx_http_client_error(r, rc, NGX_HTTP_BAD_REQUEST); + return; + } + + } else { + ngx_cpystrn(r->uri.data, r->uri_start, r->uri.len + 1); + } + + + r->request_line.len = r->request_end - r->request_start; + r->request_line.data = r->request_start; + r->request_line.data[r->request_line.len] = '\0'; + + if (r->method == 0) { + r->method_name.len = r->method_end - r->request_start + 1; + r->method_name.data = r->request_line.data; + } + + if (r->uri_ext) { + + /* copy URI extention */ + + if (r->args_start) { + r->exten.len = r->args_start - 1 - r->uri_ext; + } else { + r->exten.len = r->uri_end - r->uri_ext; + } + + if (!(r->exten.data = ngx_palloc(r->pool, r->exten.len + 1))) { + ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + ngx_http_close_connection(c); + return; + } + + ngx_cpystrn(r->exten.data, r->uri_ext, r->exten.len + 1); + } + + if (r->args_start && r->uri_end > r->args_start) { + + /* copy URI arguments */ + + r->args.len = r->uri_end - r->args_start; + + if (!(r->args.data = ngx_palloc(r->pool, r->args.len + 1))) { + ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + ngx_http_close_connection(c); + return; + } + + ngx_cpystrn(r->args.data, r->args_start, r->args.len + 1); + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http request line: \"%s\"", r->request_line.data); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http uri: \"%s\"", r->uri.data); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http args: \"%s\"", + r->args.data ? r->args.data : (u_char *) ""); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http exten: \"%s\"", + r->exten.data ? r->exten.data : (u_char *) ""); + + if (r->http_version < NGX_HTTP_VERSION_10) { + rev->event_handler = ngx_http_block_read; + ngx_http_handler(r); + return; + } + + + if (ngx_list_init(&r->headers_in.headers, r->pool, 20, + sizeof(ngx_table_elt_t)) == NGX_ERROR) + { + ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + ngx_http_close_connection(c); + return; + } + + + if (ngx_array_init(&r->headers_in.cookies, r->pool, 5, + sizeof(ngx_table_elt_t *)) == NGX_ERROR) + { + ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + ngx_http_close_connection(c); + return; + } + + + ctx = c->log->data; + ctx->action = "reading client request headers"; + ctx->url = r->unparsed_uri.data; + + rev->event_handler = ngx_http_process_request_headers; + ngx_http_process_request_headers(rev); + + return; + + } else if (rc != NGX_AGAIN) { + + /* there was error while a request line parsing */ + + 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 (rc == NGX_HTTP_PARSE_INVALID_METHOD) { + r->http_version = NGX_HTTP_VERSION_10; + } + + ngx_http_client_error(r, rc, + (rc == NGX_HTTP_PARSE_INVALID_METHOD) ? + NGX_HTTP_NOT_IMPLEMENTED: + NGX_HTTP_BAD_REQUEST); + return; + } + + /* NGX_AGAIN: a request line parsing is still incomplete */ + + if (r->header_in->pos == r->header_in->end) { + + rv = ngx_http_alloc_large_header_buffer(r, 1); + + if (rv == NGX_ERROR) { + ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + ngx_http_close_connection(c); + return; + } + + if (rv == NGX_DECLINED) { + ngx_http_client_error(r, NGX_HTTP_PARSE_TOO_LONG_URI, + NGX_HTTP_REQUEST_URI_TOO_LARGE); + return; + } + } + } +} + + +static void ngx_http_process_request_headers(ngx_event_t *rev) +{ + ssize_t n; + ngx_int_t rc, rv, i; + ngx_table_elt_t *h, **cookie; + ngx_connection_t *c; + ngx_http_request_t *r; + + c = rev->data; + r = c->data; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, + "http process request header line"); + + if (rev->timedout) { + ngx_http_client_error(r, 0, NGX_HTTP_REQUEST_TIME_OUT); + return; + } + + rc = NGX_AGAIN; + + for ( ;; ) { + + if (rc == NGX_AGAIN) { + + if (r->header_in->pos == r->header_in->end) { + + rv = ngx_http_alloc_large_header_buffer(r, 0); + + if (rv == NGX_ERROR) { + ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + ngx_http_close_connection(c); + return; + } + + if (rv == NGX_DECLINED) { + ngx_http_client_error(r, NGX_HTTP_PARSE_TOO_LONG_HEADER, + NGX_HTTP_BAD_REQUEST); + return; + } + } + + n = ngx_http_read_request_header(r); + + if (n == NGX_AGAIN || n == NGX_ERROR) { + return; + } + } + + rc = ngx_http_parse_header_line(r, r->header_in); + + if (rc == NGX_OK) { + + /* a header line has been parsed successfully */ + + r->headers_n++; + + if (!(h = ngx_list_push(&r->headers_in.headers))) { + ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + ngx_http_close_connection(c); + return; + } + + h->key.len = r->header_name_end - r->header_name_start; + h->key.data = r->header_name_start; + h->key.data[h->key.len] = '\0'; + + h->value.len = r->header_end - r->header_start; + h->value.data = r->header_start; + h->value.data[h->value.len] = '\0'; + + if (h->key.len == sizeof("Cookie") - 1 + && ngx_strcasecmp(h->key.data, "Cookie") == 0) + { + if (!(cookie = ngx_array_push(&r->headers_in.cookies))) { + ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + ngx_http_close_connection(c); + return; + } + + *cookie = h; + + } else { + + for (i = 0; ngx_http_headers_in[i].name.len != 0; i++) { + if (ngx_http_headers_in[i].name.len != h->key.len) { + continue; + } + + if (ngx_strcasecmp(ngx_http_headers_in[i].name.data, + h->key.data) == 0) + { + *((ngx_table_elt_t **) ((char *) &r->headers_in + + ngx_http_headers_in[i].offset)) = h; + break; + } + } + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http header: \"%s: %s\"", + h->key.data, h->value.data); + + continue; + + } else if (rc == NGX_HTTP_PARSE_HEADER_DONE) { + + /* a whole header has been parsed successfully */ + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http header done"); + + r->http_state = NGX_HTTP_PROCESS_REQUEST_STATE; + + rc = ngx_http_process_request_header(r); + + if (rc != NGX_OK) { + ngx_http_client_error(r, rc, NGX_HTTP_BAD_REQUEST); + return; + } + + if (rev->timer_set) { + ngx_del_timer(rev); + } + +#if (NGX_STAT_STUB) + (*ngx_stat_reading)--; + r->stat_reading = 0; + (*ngx_stat_writing)++; + r->stat_writing = 1; +#endif + + rev->event_handler = ngx_http_block_read; + ngx_http_handler(r); + return; + + } else if (rc != NGX_AGAIN) { + + /* there was error while a header line parsing */ + +#if (NGX_DEBUG) + if (rc == NGX_HTTP_PARSE_INVALID_HEADER + && (rev->log->log_level & NGX_LOG_DEBUG_HTTP)) + { + u_char *p; + for (p = r->header_name_start; + p < r->header_in->last - 1; + p++) + { + if (*p == LF) { + break; + } + } + *p = '\0'; + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, rev->log, 0, + "http invalid header: \"%s\"", + r->header_name_start); + } +#endif + + ngx_http_client_error(r, rc, NGX_HTTP_BAD_REQUEST); + return; + } + + /* NGX_AGAIN: a header line parsing is still not complete */ + + } +} + + +static ssize_t ngx_http_read_request_header(ngx_http_request_t *r) +{ + ssize_t n; + ngx_event_t *rev; + ngx_http_core_srv_conf_t *cscf; + + rev = r->connection->read; + + n = r->header_in->last - r->header_in->pos; + + if (n > 0) { + return n; + } + + if (!rev->ready) { + return NGX_AGAIN; + } + + n = r->connection->recv(r->connection, r->header_in->last, + r->header_in->end - r->header_in->last); + + if (n == NGX_AGAIN) { + if (!r->header_timeout_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) { + ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + ngx_http_close_connection(r->connection); + return NGX_ERROR; + } + + return NGX_AGAIN; + } + + if (n == 0) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client closed prematurely connection"); + } + + if (n == 0 || n == NGX_ERROR) { + ngx_http_close_request(r, NGX_HTTP_BAD_REQUEST); + ngx_http_close_connection(r->connection); + return NGX_ERROR; + } + + r->header_in->last += n; + + return n; +} + + +static ngx_int_t ngx_http_alloc_large_header_buffer(ngx_http_request_t *r, + ngx_uint_t request_line) +{ + u_char *old, *new; + ngx_buf_t *b; + ngx_http_connection_t *hc; + ngx_http_core_srv_conf_t *cscf; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http alloc large header buffer"); + + if (request_line && r->state == 0) { + + /* the client fills up the buffer with "\r\n" */ + + r->header_in->pos = r->header_in->start; + r->header_in->last = r->header_in->start; + + return NGX_OK; + } + + old = request_line ? r->request_start : r->header_name_start; + + cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); + + if (r->state != 0 + && (size_t) (r->header_in->pos - old) + >= cscf->large_client_header_buffers.size) + { + return NGX_DECLINED; + } + + hc = r->http_connection; + + if (hc->nfree) { + b = hc->free[--hc->nfree]; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http large header free: " PTR_FMT " " SIZE_T_FMT, + b->pos, b->end - b->last); + + } else if (hc->nbusy < cscf->large_client_header_buffers.num) { + + if (hc->busy == NULL) { + hc->busy = ngx_palloc(r->connection->pool, + cscf->large_client_header_buffers.num * sizeof(ngx_buf_t *)); + if (hc->busy == NULL) { + return NGX_ERROR; + } + } + + b = ngx_create_temp_buf(r->connection->pool, + cscf->large_client_header_buffers.size); + if (b == NULL) { + return NGX_ERROR; + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http large header alloc: " PTR_FMT " " SIZE_T_FMT, + b->pos, b->end - b->last); + + } else { + return NGX_DECLINED; + } + + hc->busy[hc->nbusy++] = b; + + if (r->state == 0) { + /* + * r->state == 0 means that a header line was parsed successfully + * and we do not need to copy incomplete header line and + * to relocate the parser header pointers + */ + + r->header_in = b; + + return NGX_OK; + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http large header copy: %d", r->header_in->pos - old); + + new = b->start; + + ngx_memcpy(new, old, r->header_in->pos - old); + + b->pos = new + (r->header_in->pos - old); + b->last = new + (r->header_in->pos - old); + + if (request_line) { + r->request_start = new; + + if (r->request_end) { + r->request_end = new + (r->request_end - old); + } + + r->method_end = new + (r->method_end - old); + + r->uri_start = new + (r->uri_start - old); + r->uri_end = new + (r->uri_end - old); + + if (r->schema_start) { + r->schema_start = new + (r->schema_start - old); + r->schema_end = new + (r->schema_end - old); + } + + if (r->host_start) { + r->host_start = new + (r->host_start - old); + r->host_end = new + (r->host_end - old); + } + + if (r->port_start) { + r->port_start = new + (r->port_start - old); + r->port_end = new + (r->port_end - old); + } + + if (r->uri_ext) { + r->uri_ext = new + (r->uri_ext - old); + } + + if (r->args_start) { + r->args_start = new + (r->args_start - old); + } + + } else { + r->header_name_start = new; + r->header_name_end = new + (r->header_name_end - old); + r->header_start = new + (r->header_start - old); + r->header_end = new + (r->header_end - old); + } + + r->header_in = b; + + return NGX_OK; +} + + +static ngx_int_t ngx_http_process_request_header(ngx_http_request_t *r) +{ + u_char *ua, *user_agent; + size_t len; + ngx_uint_t i; + ngx_http_server_name_t *name; + ngx_http_core_srv_conf_t *cscf; + ngx_http_core_loc_conf_t *clcf; + + if (r->headers_in.host) { + for (len = 0; len < r->headers_in.host->value.len; len++) { + if (r->headers_in.host->value.data[len] == ':') { + break; + } + } + r->headers_in.host_name_len = len; + + /* find the name based server configuration */ + + name = r->virtual_names->elts; + for (i = 0; i < r->virtual_names->nelts; i++) { + if (r->headers_in.host_name_len != name[i].name.len) { + continue; + } + + if (ngx_strncasecmp(r->headers_in.host->value.data, + name[i].name.data, + r->headers_in.host_name_len) == 0) + { + r->srv_conf = name[i].core_srv_conf->ctx->srv_conf; + r->loc_conf = name[i].core_srv_conf->ctx->loc_conf; + r->server_name = &name[i].name; + + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + r->connection->log->file = clcf->err_log->file; + if (!(r->connection->log->log_level & NGX_LOG_DEBUG_CONNECTION)) + { + r->connection->log->log_level = clcf->err_log->log_level; + } + + break; + } + } + + if (i == r->virtual_names->nelts) { + cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); + + if (cscf->restrict_host_names != NGX_HTTP_RESTRICT_HOST_OFF) { + return NGX_HTTP_PARSE_INVALID_HOST; + } + } + + } else { + if (r->http_version > NGX_HTTP_VERSION_10) { + return NGX_HTTP_PARSE_NO_HOST_HEADER; + } + r->headers_in.host_name_len = 0; + } + + if (r->headers_in.content_length) { + r->headers_in.content_length_n = + ngx_atoi(r->headers_in.content_length->value.data, + r->headers_in.content_length->value.len); + + if (r->headers_in.content_length_n == NGX_ERROR) { + return NGX_HTTP_PARSE_INVALID_CL_HEADER; + } + } + + if (r->method == NGX_HTTP_POST && r->headers_in.content_length_n <= 0) { + return NGX_HTTP_PARSE_POST_WO_CL_HEADER; + } + + if (r->plain_http) { + return NGX_HTTP_PARSE_HTTP_TO_HTTPS; + } + + if (r->headers_in.connection) { + if (r->headers_in.connection->value.len == 5 + && ngx_strcasecmp(r->headers_in.connection->value.data, "close") + == 0) + { + r->headers_in.connection_type = NGX_HTTP_CONNECTION_CLOSE; + + } else if (r->headers_in.connection->value.len == 10 + && ngx_strcasecmp(r->headers_in.connection->value.data, + "keep-alive") == 0) + { + r->headers_in.connection_type = NGX_HTTP_CONNECTION_KEEP_ALIVE; + + if (r->headers_in.keep_alive) { + r->headers_in.keep_alive_n = + ngx_atoi(r->headers_in.keep_alive->value.data, + r->headers_in.keep_alive->value.len); + } + } + } + + if (r->headers_in.user_agent) { + + /* + * check some widespread browsers while the headers are still + * in CPU cache + */ + + user_agent = r->headers_in.user_agent->value.data; + + ua = (u_char *) ngx_strstr(user_agent, "MSIE"); + + if (ua && ua + 8 < user_agent + r->headers_in.user_agent->value.len) { + + r->headers_in.msie = 1; + + if (ua[4] == ' ' && ua[5] == '4' && ua[6] == '.') { + r->headers_in.msie4 = 1; + } + +#if 0 + /* MSIE ignores the SSL "close notify" alert */ + + ngx_ssl_set_nosendshut(r->connection->ssl); +#endif + } + + if (ngx_strstr(user_agent, "Opera")) { + 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_strstr(user_agent, "Gecko/")) { + r->headers_in.gecko = 1; + + } else if (ngx_strstr(user_agent, "Konqueror")) { + r->headers_in.konqueror = 1; + } + } + } + + return NGX_OK; +} + + +void ngx_http_finalize_request(ngx_http_request_t *r, int rc) +{ + ngx_http_core_loc_conf_t *clcf; + + /* r can be already destroyed when rc == NGX_DONE */ + + if (rc == NGX_DONE || r->main) { + return; + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http finalize request: %d", rc); + + if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { + + if (r->connection->read->timer_set) { + ngx_del_timer(r->connection->read); + } + + if (r->connection->write->timer_set) { + ngx_del_timer(r->connection->write); + } + + if (rc == NGX_HTTP_CLIENT_CLOSED_REQUEST || r->closed) { + ngx_http_close_request(r, 0); + ngx_http_close_connection(r->connection); + return; + } + + ngx_http_finalize_request(r, ngx_http_special_response_handler(r, rc)); + + return; + + } else if (rc == NGX_ERROR) { + ngx_http_close_request(r, 0); + ngx_http_close_connection(r->connection); + return; + + } else if (rc == NGX_AGAIN) { + ngx_http_set_write_handler(r); + return; + } + + if (r->connection->read->timer_set) { + ngx_del_timer(r->connection->read); + } + + if (r->connection->write->timer_set) { + r->connection->write->delayed = 0; + ngx_del_timer(r->connection->write); + } + + if (r->connection->read->pending_eof) { +#if (NGX_KQUEUE) + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, + r->connection->read->kq_errno, + "kevent() reported about an closed connection"); +#endif + ngx_http_close_request(r, 0); + ngx_http_close_connection(r->connection); + return; + } + + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + if (!ngx_terminate + && !ngx_exiting + && r->keepalive != 0 + && clcf->keepalive_timeout > 0) + { + ngx_http_set_keepalive(r); + return; + + } else if (r->lingering_close && clcf->lingering_timeout > 0) { + ngx_http_set_lingering_close(r); + return; + } + + ngx_http_close_request(r, 0); + ngx_http_close_connection(r->connection); +} + + +static void ngx_http_set_write_handler(ngx_http_request_t *r) +{ + ngx_event_t *wev; + ngx_http_core_loc_conf_t *clcf; + + wev = r->connection->write; + wev->event_handler = ngx_http_writer; + + r->http_state = NGX_HTTP_WRITING_REQUEST_STATE; + + if (wev->ready && wev->delayed) { + return; + } + + clcf = ngx_http_get_module_loc_conf(r->main ? r->main : r, + ngx_http_core_module); + if (!wev->delayed) { + ngx_add_timer(wev, clcf->send_timeout); + } + + wev->available = clcf->send_lowat; + if (ngx_handle_write_event(wev, NGX_LOWAT_EVENT) == NGX_ERROR) { + ngx_http_close_request(r, 0); + ngx_http_close_connection(r->connection); + } + + return; +} + + +void ngx_http_writer(ngx_event_t *wev) +{ + int rc; + ngx_connection_t *c; + ngx_http_request_t *r; + ngx_http_core_loc_conf_t *clcf; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, wev->log, 0, "http writer handler"); + + c = wev->data; + r = c->data; + + if (wev->timedout) { + if (!wev->delayed) { + ngx_http_client_error(r, 0, NGX_HTTP_REQUEST_TIME_OUT); + return; + } + + wev->timedout = 0; + wev->delayed = 0; + + if (!wev->ready) { + clcf = ngx_http_get_module_loc_conf(r->main ? r->main : r, + ngx_http_core_module); + ngx_add_timer(wev, clcf->send_timeout); + + wev->available = clcf->send_lowat; + + if (ngx_handle_write_event(wev, NGX_LOWAT_EVENT) == NGX_ERROR) { + ngx_http_close_request(r, 0); + ngx_http_close_connection(r->connection); + } + + return; + } + + } else { + if (wev->delayed) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, wev->log, 0, + "http writer delayed"); + + clcf = ngx_http_get_module_loc_conf(r->main ? r->main : r, + ngx_http_core_module); + wev->available = clcf->send_lowat; + + if (ngx_handle_write_event(wev, NGX_LOWAT_EVENT) == NGX_ERROR) { + ngx_http_close_request(r, 0); + ngx_http_close_connection(r->connection); + } + + return; + } + } + + rc = ngx_http_output_filter(r, NULL); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http writer output filter: %d", rc); + + if (rc == NGX_AGAIN) { + clcf = ngx_http_get_module_loc_conf(r->main ? r->main : r, + ngx_http_core_module); + if (!wev->ready && !wev->delayed) { + ngx_add_timer(wev, clcf->send_timeout); + } + + wev->available = clcf->send_lowat; + + if (ngx_handle_write_event(wev, NGX_LOWAT_EVENT) == NGX_ERROR) { + ngx_http_close_request(r, 0); + ngx_http_close_connection(r->connection); + } + + return; + } + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http writer done"); + + ngx_http_finalize_request(r, rc); +} + + +static void ngx_http_block_read(ngx_event_t *rev) +{ + ngx_connection_t *c; + ngx_http_request_t *r; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "http read blocked"); + + /* aio does not call this handler */ + + if ((ngx_event_flags & NGX_USE_LEVEL_EVENT) && rev->active) { + if (ngx_del_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) { + c = rev->data; + r = c->data; + ngx_http_close_request(r, 0); + ngx_http_close_connection(c); + } + } +} + + +ngx_int_t ngx_http_discard_body(ngx_http_request_t *r) +{ + ssize_t size; + ngx_event_t *rev; + + rev = r->connection->read; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "http set discard body"); + + if (rev->timer_set) { + ngx_del_timer(rev); + } + + if (r->headers_in.content_length_n <= 0) { + return NGX_OK; + } + + size = r->header_in->last - r->header_in->pos; + + if (size) { + if (r->headers_in.content_length_n > size) { + r->headers_in.content_length_n -= size; + + } else { + r->header_in->pos += r->headers_in.content_length_n; + r->headers_in.content_length_n = 0; + return NGX_OK; + } + } + + rev->event_handler = ngx_http_read_discarded_body_event; + + if (ngx_handle_level_read_event(rev) == NGX_ERROR) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + return ngx_http_read_discarded_body(r); +} + + +static void ngx_http_read_discarded_body_event(ngx_event_t *rev) +{ + ngx_int_t rc; + ngx_connection_t *c; + ngx_http_request_t *r; + + c = rev->data; + r = c->data; + + rc = ngx_http_read_discarded_body(r); + + if (rc == NGX_AGAIN) { + if (ngx_handle_level_read_event(rev) == NGX_ERROR) { + ngx_http_close_request(r, rc); + ngx_http_close_connection(c); + return; + } + } + + if (rc != NGX_OK) { + ngx_http_close_request(r, rc); + ngx_http_close_connection(c); + } +} + + +static ngx_int_t ngx_http_read_discarded_body(ngx_http_request_t *r) +{ + ssize_t size, n; + u_char buffer[NGX_HTTP_DISCARD_BUFFER_SIZE]; + + 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; + } + + + size = r->headers_in.content_length_n; + + if (size > NGX_HTTP_DISCARD_BUFFER_SIZE) { + size = NGX_HTTP_DISCARD_BUFFER_SIZE; + } + + n = r->connection->recv(r->connection, buffer, size); + + if (n == NGX_ERROR) { + + r->closed = 1; + + /* + * if a client request body is discarded then we already set + * some HTTP response code for client and we can ignore the error + */ + + return NGX_OK; + } + + if (n == NGX_AGAIN) { + return NGX_AGAIN; + } + + r->headers_in.content_length_n -= n; + + return NGX_OK; +} + + +static void ngx_http_set_keepalive(ngx_http_request_t *r) +{ + ngx_int_t i; + ngx_buf_t *b, *f; + ngx_event_t *rev, *wev; + ngx_connection_t *c; + ngx_http_connection_t *hc; + ngx_http_log_ctx_t *ctx; + ngx_http_core_srv_conf_t *cscf; + ngx_http_core_loc_conf_t *clcf; + + c = r->connection; + rev = c->read; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "set http keepalive handler"); + + ctx = (ngx_http_log_ctx_t *) c->log->data; + ctx->action = "closing request"; + + hc = r->http_connection; + b = r->header_in; + + if (b->pos < b->last) { + + /* the pipelined request */ + + if (b != c->buffer) { + + /* move the large header buffers to the free list */ + + cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); + + if (hc->free == NULL) { + hc->free = ngx_palloc(c->pool, + cscf->large_client_header_buffers.num * sizeof(ngx_buf_t *)); + + if (hc->free == NULL) { + ngx_http_close_connection(c); + return; + } + } + + for (i = 0; i < hc->nbusy - 1; i++) { + f = hc->busy[i]; + hc->free[hc->nfree++] = f; + f->pos = f->start; + f->last = f->start; + } + + hc->busy[0] = b; + hc->nbusy = 1; + } + } + + ngx_http_close_request(r, 0); + c->data = hc; + + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + ngx_add_timer(rev, clcf->keepalive_timeout); + + if (ngx_handle_level_read_event(rev) == NGX_ERROR) { + ngx_http_close_connection(c); + return; + } + + wev = c->write; + wev->event_handler = ngx_http_empty_handler; + + if (b->pos < b->last) { + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "pipelined request"); + + hc->pipeline = 1; + ctx->action = "reading client pipelined request line"; + ngx_http_init_request(rev); + return; + } + + hc->pipeline = 0; + + if (ngx_pfree(c->pool, r) == NGX_OK) { + hc->request = NULL; + } + + b = c->buffer; + + if (ngx_pfree(c->pool, b->start) == NGX_OK) { + b->pos = NULL; + + } else { + b->pos = b->start; + b->last = b->start; + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "hc free: " PTR_FMT " %d", + hc->free, hc->nfree); + + if (hc->free) { + for (i = 0; i < hc->nfree; i++) { + ngx_pfree(c->pool, hc->free[i]); + hc->free[i] = NULL; + } + + hc->nfree = 0; + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "hc busy: " PTR_FMT " %d", + hc->busy, hc->nbusy); + + if (hc->busy) { + for (i = 0; i < hc->nbusy; i++) { + ngx_pfree(c->pool, hc->busy[i]); + hc->busy[i] = NULL; + } + + hc->nbusy = 0; + } + + rev->event_handler = ngx_http_keepalive_handler; + + if (wev->active) { + if (ngx_event_flags & NGX_HAVE_KQUEUE_EVENT) { + if (ngx_del_event(wev, NGX_WRITE_EVENT, NGX_DISABLE_EVENT) + == NGX_ERROR) + { + ngx_http_close_connection(c); + return; + } + + } else if (ngx_event_flags & NGX_USE_LEVEL_EVENT) { + if (ngx_del_event(wev, NGX_WRITE_EVENT, 0) == NGX_ERROR) { + ngx_http_close_connection(c); + return; + } + } + } + + ctx->action = "keepalive"; + + if (c->tcp_nopush == NGX_TCP_NOPUSH_SET) { + if (ngx_tcp_push(c->fd) == NGX_ERROR) { + ngx_connection_error(c, ngx_socket_errno, ngx_tcp_push_n " failed"); + ngx_http_close_connection(c); + return; + } + c->tcp_nopush = NGX_TCP_NOPUSH_UNSET; + } + +#if 0 + /* if "keepalive_buffers off" then we need some other place */ + r->http_state = NGX_HTTP_KEEPALIVE_STATE; +#endif + + if (rev->ready) { + ngx_http_keepalive_handler(rev); + } +} + + +static void ngx_http_keepalive_handler(ngx_event_t *rev) +{ + size_t size; + ssize_t n; + ngx_buf_t *b; + ngx_connection_t *c; + ngx_http_log_ctx_t *ctx; + ngx_http_connection_t *hc; + + c = rev->data; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http keepalive handler"); + + if (rev->timedout) { + ngx_http_close_connection(c); + return; + } + + ctx = (ngx_http_log_ctx_t *) rev->log->data; + +#if (HAVE_KQUEUE) + + if (ngx_event_flags & NGX_HAVE_KQUEUE_EVENT) { + if (rev->pending_eof) { + ngx_log_error(NGX_LOG_INFO, c->log, rev->kq_errno, + "kevent() reported that client %s closed " + "keepalive connection", ctx->client); + ngx_http_close_connection(c); + return; + } + } + +#endif + + hc = c->data; + b = c->buffer; + size = b->end - b->start; + + if (b->pos == NULL) { + if (!(b->pos = ngx_palloc(c->pool, size))) { + ngx_http_close_connection(c); + return; + } + + b->start = b->pos; + b->last = b->pos; + b->end = b->pos + size; + } + + /* + * MSIE closes a keepalive connection with RST flag + * so we ignore ECONNRESET here. + */ + + c->log_error = NGX_ERROR_IGNORE_ECONNRESET; + ngx_set_socket_errno(0); + + n = c->recv(c, b->last, size); + c->log_error = NGX_ERROR_INFO; + + if (n == NGX_AGAIN) { + return; + } + + if (n == NGX_ERROR) { + ngx_http_close_connection(c); + return; + } + + rev->log->handler = NULL; + + if (n == 0) { + ngx_log_error(NGX_LOG_INFO, c->log, ngx_socket_errno, + "client %s closed keepalive connection", ctx->client); + ngx_http_close_connection(c); + return; + } + + b->last += n; + rev->log->handler = ngx_http_log_error; + ctx->action = "reading client request line"; + + ngx_http_init_request(rev); +} + + +static void ngx_http_set_lingering_close(ngx_http_request_t *r) +{ + ngx_event_t *rev, *wev; + ngx_connection_t *c; + ngx_http_core_loc_conf_t *clcf; + + c = r->connection; + + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + rev = c->read; + rev->event_handler = ngx_http_lingering_close_handler; + + r->lingering_time = ngx_time() + clcf->lingering_time / 1000; + ngx_add_timer(rev, clcf->lingering_timeout); + + if (ngx_handle_level_read_event(rev) == NGX_ERROR) { + ngx_http_close_request(r, 0); + ngx_http_close_connection(c); + return; + } + + wev = c->write; + wev->event_handler = ngx_http_empty_handler; + + if (wev->active) { + if (ngx_event_flags & NGX_HAVE_KQUEUE_EVENT) { + if (ngx_del_event(wev, NGX_WRITE_EVENT, NGX_DISABLE_EVENT) + == NGX_ERROR) + { + ngx_http_close_connection(c); + return; + } + + } else if (ngx_event_flags & NGX_USE_LEVEL_EVENT) { + if (ngx_del_event(wev, NGX_WRITE_EVENT, 0) == NGX_ERROR) { + ngx_http_close_connection(c); + return; + } + } + } + + if (ngx_shutdown_socket(c->fd, NGX_WRITE_SHUTDOWN) == -1) { + ngx_connection_error(c, ngx_socket_errno, + ngx_shutdown_socket_n " failed"); + ngx_http_close_request(r, 0); + ngx_http_close_connection(c); + return; + } + + if (rev->ready) { + ngx_http_lingering_close_handler(rev); + } +} + + +static void ngx_http_lingering_close_handler(ngx_event_t *rev) +{ + ssize_t n; + ngx_msec_t timer; + ngx_connection_t *c; + ngx_http_request_t *r; + ngx_http_core_loc_conf_t *clcf; + u_char buffer[NGX_HTTP_LINGERING_BUFFER_SIZE]; + + c = rev->data; + r = c->data; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http lingering close handler"); + + if (rev->timedout) { + ngx_http_close_request(r, 0); + ngx_http_close_connection(c); + return; + } + + timer = r->lingering_time - ngx_time(); + if (timer <= 0) { + ngx_http_close_request(r, 0); + ngx_http_close_connection(c); + return; + } + + do { + n = c->recv(c, buffer, NGX_HTTP_LINGERING_BUFFER_SIZE); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "lingering read: %d", n); + + if (n == NGX_ERROR || n == 0) { + ngx_http_close_request(r, 0); + ngx_http_close_connection(c); + return; + } + + } while (rev->ready); + + 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); + + return; +} + + +void ngx_http_empty_handler(ngx_event_t *wev) +{ + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, wev->log, 0, "http empty handler"); + + return; +} + + +ngx_int_t ngx_http_send_last(ngx_http_request_t *r) +{ + ngx_buf_t *b; + ngx_chain_t out; + + if (!(b = ngx_calloc_buf(r->pool))) { + return NGX_ERROR; + } + + b->last_buf = 1; + out.buf = b; + out.next = NULL; + + return ngx_http_output_filter(r, &out); +} + + +void ngx_http_close_request(ngx_http_request_t *r, int error) +{ + ngx_uint_t i; + ngx_log_t *log; + ngx_http_log_ctx_t *ctx; + ngx_http_cleanup_t *cleanup; + ngx_http_core_loc_conf_t *clcf; + struct linger l; + + log = r->connection->log; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, log, 0, "http close request"); + + if (r->pool == NULL) { + ngx_log_error(NGX_LOG_ALERT, log, 0, + "http request already closed"); + return; + } + +#if (NGX_STAT_STUB) + if (r->stat_reading) { + (*ngx_stat_reading)--; + } + + if (r->stat_writing) { + (*ngx_stat_writing)--; + } +#endif + + if (error && r->headers_out.status == 0) { + r->headers_out.status = error; + } + + ngx_http_log_handler(r); + + cleanup = r->cleanup.elts; + for (i = 0; i < r->cleanup.nelts; i++) { + if (!cleanup[i].valid) { + continue; + } + +#if (NGX_HTTP_CACHE) + + if (cleanup[i].cache) { + ngx_http_cache_unlock(cleanup[i].data.cache.hash, + cleanup[i].data.cache.cache, log); + continue; + } + +#endif + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, "http cleanup fd: %d", + cleanup[i].data.file.fd); + + if (ngx_close_file(cleanup[i].data.file.fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + ngx_close_file_n " \"%s\" failed", + cleanup[i].data.file.name); + } + } + + /* STUB */ + if (r->file.fd != NGX_INVALID_FILE) { + if (ngx_close_file(r->file.fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + ngx_close_file_n " \"%s\" failed", r->file.name.data); + } + } + + if (r->request_body + && r->request_body->temp_file + && r->request_body->temp_file->file.fd != NGX_INVALID_FILE) + { + if (ngx_close_file(r->request_body->temp_file->file.fd) + == NGX_FILE_ERROR) + { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + ngx_close_file_n " deleted file \"%s\" failed", + r->request_body->temp_file->file.name.data); + } + } + + if (r->connection->timedout) { + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + if (clcf->reset_timedout_connection) { + l.l_onoff = 1; + l.l_linger = 0; + + if (setsockopt(r->connection->fd, SOL_SOCKET, SO_LINGER, + (const void *) &l, sizeof(struct linger)) == -1) + { + ngx_log_error(NGX_LOG_ALERT, log, ngx_socket_errno, + "setsockopt(SO_LINGER) failed"); + } + } + } + + /* ctx->url was allocated from r->pool */ + ctx = log->data; + ctx->url = NULL; + + r->request_line.len = 0; + + ngx_destroy_pool(r->pool); + + return; +} + + +#if (NGX_HTTP_SSL) + +void ngx_ssl_close_handler(ngx_event_t *ev) +{ + ngx_connection_t *c; + + c = ev->data; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ev->log, 0, "http ssl close handler"); + + if (ngx_ssl_shutdown(c) == NGX_AGAIN) { + return; + } + + ngx_http_close_connection(c); +} + +#endif + + +void ngx_http_close_connection(ngx_connection_t *c) +{ + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "close http connection: %d", c->fd); + +#if (NGX_STAT_STUB) + (*ngx_stat_active)--; +#endif + + ngx_close_connection(c); +} + + +static void ngx_http_client_error(ngx_http_request_t *r, + int client_error, int error) +{ + ngx_http_log_ctx_t *ctx; + ngx_http_core_srv_conf_t *cscf; + + ctx = r->connection->log->data; + + if (error == NGX_HTTP_REQUEST_TIME_OUT) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, NGX_ETIMEDOUT, + "client timed out"); + r->connection->timedout = 1; + ngx_http_close_request(r, error); + ngx_http_close_connection(r->connection); + return; + } + + r->connection->log->handler = NULL; + + if (ctx->url) { + switch (client_error) { + + case NGX_HTTP_PARSE_INVALID_HOST: + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + client_header_errors[client_error - NGX_HTTP_CLIENT_ERROR], + ctx->client, r->headers_in.host->value.data, ctx->url); + + error = NGX_HTTP_INVALID_HOST; + + cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); + + if (cscf->restrict_host_names == NGX_HTTP_RESTRICT_HOST_CLOSE) { + ngx_http_close_request(r, error); + ngx_http_close_connection(r->connection); + return; + } + + break; + + case NGX_HTTP_PARSE_HTTP_TO_HTTPS: + error = NGX_HTTP_TO_HTTPS; + + /* fall through */ + + default: + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + client_header_errors[client_error - NGX_HTTP_CLIENT_ERROR], + ctx->client, ctx->url); + } + + } else { + if (error == NGX_HTTP_REQUEST_URI_TOO_LARGE) { + r->request_line.len = r->header_in->end - r->request_start; + r->request_line.data = r->request_start; + } + + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + client_header_errors[client_error - NGX_HTTP_CLIENT_ERROR], + ctx->client); + } + + r->connection->log->handler = ngx_http_log_error; + + ngx_http_finalize_request(r, error); +} + + +static size_t ngx_http_log_error(void *data, char *buf, size_t len) +{ + ngx_http_log_ctx_t *ctx = data; + + if (ctx->action && ctx->url) { + return ngx_snprintf(buf, len, " while %s, client: %s, URL: %s", + ctx->action, ctx->client, ctx->url); + + } else if (ctx->action == NULL && ctx->url) { + return ngx_snprintf(buf, len, ", client: %s, URL: %s", + ctx->client, ctx->url); + + } else { + return ngx_snprintf(buf, len, " while %s, client: %s", + ctx->action, ctx->client); + } +} diff --git a/src/http/ngx_http_request.h b/src/http/ngx_http_request.h new file mode 100644 --- /dev/null +++ b/src/http/ngx_http_request.h @@ -0,0 +1,372 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#ifndef _NGX_HTTP_REQUEST_H_INCLUDED_ +#define _NGX_HTTP_REQUEST_H_INCLUDED_ + + +#define NGX_HTTP_DISCARD_BUFFER_SIZE 4096 +#define NGX_HTTP_LINGERING_BUFFER_SIZE 4096 + + +#define NGX_HTTP_VERSION_9 9 +#define NGX_HTTP_VERSION_10 1000 +#define NGX_HTTP_VERSION_11 1001 + +#define NGX_HTTP_GET 1 +#define NGX_HTTP_HEAD 2 +#define NGX_HTTP_POST 3 + +#define NGX_HTTP_CONNECTION_CLOSE 1 +#define NGX_HTTP_CONNECTION_KEEP_ALIVE 2 + + +#define NGX_NONE 1 + + +#define NGX_HTTP_PARSE_HEADER_DONE 1 + +#define NGX_HTTP_CLIENT_ERROR 10 +#define NGX_HTTP_PARSE_INVALID_METHOD 10 +#define NGX_HTTP_PARSE_INVALID_REQUEST 11 +#define NGX_HTTP_PARSE_TOO_LONG_URI 12 +#define NGX_HTTP_PARSE_INVALID_09_METHOD 13 + +#define NGX_HTTP_PARSE_HEADER_ERROR 14 +#define NGX_HTTP_PARSE_INVALID_HEADER 14 +#define NGX_HTTP_PARSE_TOO_LONG_HEADER 15 +#define NGX_HTTP_PARSE_NO_HOST_HEADER 17 +#define NGX_HTTP_PARSE_INVALID_CL_HEADER 18 +#define NGX_HTTP_PARSE_POST_WO_CL_HEADER 19 +#define NGX_HTTP_PARSE_HTTP_TO_HTTPS 20 +#define NGX_HTTP_PARSE_INVALID_HOST 21 + + +#define NGX_HTTP_OK 200 +#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_NOT_MODIFIED 304 + +#define NGX_HTTP_BAD_REQUEST 400 +#define NGX_HTTP_FORBIDDEN 403 +#define NGX_HTTP_NOT_FOUND 404 +#define NGX_HTTP_NOT_ALLOWED 405 +#define NGX_HTTP_REQUEST_TIME_OUT 408 +#define NGX_HTTP_REQUEST_ENTITY_TOO_LARGE 413 +#define NGX_HTTP_REQUEST_URI_TOO_LARGE 414 +#define NGX_HTTP_RANGE_NOT_SATISFIABLE 416 + + +/* Our own HTTP codes */ + +#define NGX_HTTP_NGX_CODES NGX_HTTP_TO_HTTPS + +/* + * We use the special code for the plain HTTP requests that are sent to + * HTTPS port to distinguish it from 4XX in an error page redirection + */ +#define NGX_HTTP_TO_HTTPS 497 + +/* + * We use the special code for the requests with invalid host name + * to distinguish it from 4XX in an error page redirection + */ +#define NGX_HTTP_INVALID_HOST 498 + +/* + * HTTP does not define the code for the case when a client closed + * the connection while we are processing its request so we introduce + * own code to log such situation when a client has closed the connection + * before we even try to send the HTTP header to it + */ +#define NGX_HTTP_CLIENT_CLOSED_REQUEST 499 + + +#define NGX_HTTP_INTERNAL_SERVER_ERROR 500 +#define NGX_HTTP_NOT_IMPLEMENTED 501 +#define NGX_HTTP_BAD_GATEWAY 502 +#define NGX_HTTP_SERVICE_UNAVAILABLE 503 +#define NGX_HTTP_GATEWAY_TIME_OUT 504 + + +typedef enum { + NGX_HTTP_RESTRICT_HOST_OFF = 0, + NGX_HTTP_RESTRICT_HOST_ON, + NGX_HTTP_RESTRICT_HOST_CLOSE +} ngx_http_restrict_host_e; + + +typedef enum { + NGX_HTTP_INITING_REQUEST_STATE = 0, + NGX_HTTP_READING_REQUEST_STATE, + NGX_HTTP_PROCESS_REQUEST_STATE, + + NGX_HTTP_CONNECT_UPSTREAM_STATE, + NGX_HTTP_WRITING_UPSTREAM_STATE, + NGX_HTTP_READING_UPSTREAM_STATE, + + NGX_HTTP_WRITING_REQUEST_STATE, + NGX_HTTP_LINGERING_CLOSE_STATE, + NGX_HTTP_KEEPALIVE_STATE +} ngx_http_state_e; + + +typedef struct { + ngx_str_t name; + ngx_uint_t offset; +} ngx_http_header_t; + + +typedef struct { + ngx_list_t headers; + + ngx_table_elt_t *host; + ngx_table_elt_t *connection; + ngx_table_elt_t *if_modified_since; + ngx_table_elt_t *user_agent; + ngx_table_elt_t *referer; + ngx_table_elt_t *content_length; + + ngx_table_elt_t *range; + +#if (NGX_HTTP_GZIP) + ngx_table_elt_t *accept_encoding; + ngx_table_elt_t *via; +#endif + + ngx_table_elt_t *authorization; + + ngx_table_elt_t *keep_alive; + +#if (NGX_HTTP_PROXY) + ngx_table_elt_t *x_forwarded_for; +#endif + + ngx_array_t cookies; + + size_t host_name_len; + ssize_t content_length_n; + size_t connection_type; + ssize_t keep_alive_n; + + unsigned msie:1; + unsigned msie4:1; + unsigned opera:1; + unsigned gecko:1; + unsigned konqueror:1; +} ngx_http_headers_in_t; + + +typedef struct { + off_t start; + off_t end; + ngx_str_t content_range; +} ngx_http_range_t; + + +typedef struct { + ngx_list_t headers; + + ngx_uint_t status; + ngx_str_t status_line; + + ngx_table_elt_t *server; + ngx_table_elt_t *date; + ngx_table_elt_t *content_type; + ngx_table_elt_t *content_length; + ngx_table_elt_t *content_encoding; + ngx_table_elt_t *location; + ngx_table_elt_t *last_modified; + ngx_table_elt_t *content_range; + ngx_table_elt_t *accept_ranges; + ngx_table_elt_t *expires; + ngx_table_elt_t *cache_control; + ngx_table_elt_t *etag; + + ngx_str_t charset; + ngx_array_t ranges; + + off_t content_length_n; + time_t date_time; + time_t last_modified_time; +} ngx_http_headers_out_t; + + +typedef struct { + ngx_temp_file_t *temp_file; + ngx_chain_t *bufs; + ngx_buf_t *buf; + size_t rest; + void (*handler) (void *data); + void *data; +} ngx_http_request_body_t; + + +struct ngx_http_cleanup_s { + union { + struct { + ngx_fd_t fd; + u_char *name; + } file; + + struct { + ngx_http_cache_hash_t *hash; + ngx_http_cache_t *cache; + } cache; + } data; + + unsigned valid:1; + unsigned cache:1; +}; + + +typedef struct { + ngx_http_request_t *request; + + ngx_buf_t **busy; + ngx_int_t nbusy; + + ngx_buf_t **free; + ngx_int_t nfree; + + ngx_uint_t pipeline; /* unsigned pipeline:1; */ +} ngx_http_connection_t; + + +typedef ngx_int_t (*ngx_http_handler_pt)(ngx_http_request_t *r); + +struct ngx_http_request_s { + uint32_t signature; /* "HTTP" */ + + ngx_connection_t *connection; + + void **ctx; + void **main_conf; + void **srv_conf; + void **loc_conf; + + ngx_http_cache_t *cache; + + ngx_file_t file; + + ngx_pool_t *pool; + ngx_buf_t *header_in; + + ngx_http_headers_in_t headers_in; + ngx_http_headers_out_t headers_out; + + ngx_http_request_body_t *request_body; + + time_t lingering_time; + + ngx_uint_t method; + ngx_uint_t http_version; + ngx_uint_t http_major; + ngx_uint_t http_minor; + + ngx_str_t request_line; + ngx_str_t uri; + ngx_str_t args; + ngx_str_t exten; + ngx_str_t unparsed_uri; + + ngx_str_t method_name; + + ngx_http_request_t *main; + + uint32_t in_addr; + ngx_uint_t port; + ngx_str_t *port_text; /* ":80" */ + ngx_str_t *server_name; + ngx_array_t *virtual_names; + + ngx_uint_t phase; + ngx_int_t phase_handler; + ngx_http_handler_pt content_handler; + + ngx_array_t cleanup; + + /* used to learn the Apache compatible response length without a header */ + size_t header_size; + + u_char *discarded_buffer; + void **err_ctx; + ngx_uint_t err_status; + + ngx_http_connection_t *http_connection; + + unsigned http_state:4; + +#if 0 + /* URI is not started with '/' - "GET http://" */ + unsigned unusual_uri:1; +#endif + /* URI with "/.", "%" and on Win32 with "//" */ + unsigned complex_uri:1; + unsigned header_timeout_set:1; + + unsigned proxy:1; + unsigned bypass_cache:1; + unsigned no_cache:1; + +#if 0 + unsigned cachable:1; +#endif + unsigned pipeline:1; + + /* can we use sendfile ? */ + unsigned sendfile:1; + + unsigned plain_http:1; + unsigned chunked:1; + unsigned header_only:1; + unsigned keepalive:1; + unsigned lingering_close:1; + unsigned closed:1; + + unsigned filter_need_in_memory:1; + unsigned filter_ssi_need_in_memory:1; + unsigned filter_need_temporary:1; + unsigned filter_allow_ranges:1; + +#if (NGX_STAT_STUB) + unsigned stat_reading:1; + unsigned stat_writing:1; +#endif + + ngx_uint_t headers_n; + + /* used to parse HTTP headers */ + ngx_uint_t state; + u_char *uri_start; + u_char *uri_end; + u_char *uri_ext; + u_char *args_start; + u_char *request_start; + u_char *request_end; + u_char *method_end; + u_char *schema_start; + u_char *schema_end; + u_char *host_start; + u_char *host_end; + u_char *port_start; + u_char *port_end; + u_char *header_name_start; + u_char *header_name_end; + u_char *header_start; + u_char *header_end; +}; + + +extern ngx_http_header_t ngx_http_headers_in[]; +extern ngx_http_header_t ngx_http_headers_out[]; + + + +#endif /* _NGX_HTTP_REQUEST_H_INCLUDED_ */ diff --git a/src/http/ngx_http_request_body.c b/src/http/ngx_http_request_body.c new file mode 100644 --- /dev/null +++ b/src/http/ngx_http_request_body.c @@ -0,0 +1,227 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include +#include + + +static void ngx_http_read_client_request_body_handler(ngx_event_t *rev); +static ngx_int_t ngx_http_do_read_client_request_body(ngx_http_request_t *r); + + +ngx_int_t ngx_http_read_client_request_body(ngx_http_request_t *r) +{ + ssize_t size; + ngx_buf_t *b; + ngx_chain_t *cl; + ngx_http_core_loc_conf_t *clcf; + + size = r->header_in->last - r->header_in->pos; + + if (size) { + + /* there is the pre-read part of the request body */ + + ngx_test_null(b, ngx_calloc_buf(r->pool), + NGX_HTTP_INTERNAL_SERVER_ERROR); + + b->temporary = 1; + b->start = b->pos = r->header_in->pos; + b->end = b->last = r->header_in->last; + + ngx_alloc_link_and_set_buf(r->request_body->bufs, b, r->pool, + NGX_HTTP_INTERNAL_SERVER_ERROR); + + if (size >= r->headers_in.content_length_n) { + + /* the whole request body was pre-read */ + + r->header_in->pos += r->headers_in.content_length_n; + + r->request_body->handler(r->request_body->data); + + return NGX_OK; + } + + r->header_in->pos = r->header_in->last; + } + + + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + r->request_body->rest = r->headers_in.content_length_n - size; + + if (r->request_body->rest + < clcf->client_body_buffer_size + + (clcf->client_body_buffer_size >> 2)) + { + size = r->request_body->rest; + + } else { + size = clcf->client_body_buffer_size; + } + + ngx_test_null(r->request_body->buf, ngx_create_temp_buf(r->pool, size), + NGX_HTTP_INTERNAL_SERVER_ERROR); + + ngx_alloc_link_and_set_buf(cl, r->request_body->buf, r->pool, + NGX_HTTP_INTERNAL_SERVER_ERROR); + + if (r->request_body->bufs) { + r->request_body->bufs->next = cl; + + } else { + r->request_body->bufs = cl; + } + + r->connection->read->event_handler = + ngx_http_read_client_request_body_handler; + + return ngx_http_do_read_client_request_body(r); +} + + +static void ngx_http_read_client_request_body_handler(ngx_event_t *rev) +{ + ngx_int_t rc; + ngx_connection_t *c; + ngx_http_request_t *r; + + c = rev->data; + r = c->data; + + if (rev->timedout) { + ngx_http_finalize_request(r, NGX_HTTP_REQUEST_TIME_OUT); + return; + } + + rc = ngx_http_do_read_client_request_body(r); + + if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { + ngx_http_finalize_request(r, rc); + } +} + + +static ngx_int_t ngx_http_do_read_client_request_body(ngx_http_request_t *r) +{ + size_t size; + ssize_t n; + ngx_buf_t *b; + ngx_connection_t *c; + ngx_http_core_loc_conf_t *clcf; + + c = r->connection; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http read client request body"); + + for ( ;; ) { + if (r->request_body->buf->last == r->request_body->buf->end) { + n = ngx_write_chain_to_temp_file(r->request_body->temp_file, + r->request_body->bufs->next ? r->request_body->bufs->next: + r->request_body->bufs); + + /* TODO: n == 0 or not complete and level event */ + + if (n == NGX_ERROR) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + r->request_body->temp_file->offset += n; + + r->request_body->buf->pos = r->request_body->buf->start; + r->request_body->buf->last = r->request_body->buf->start; + } + + size = r->request_body->buf->end - r->request_body->buf->last; + + if (size > r->request_body->rest) { + size = r->request_body->rest; + } + + n = c->recv(c, r->request_body->buf->last, size); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http client request body recv " SIZE_T_FMT, n); + + if (n == NGX_AGAIN) { + 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) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + return NGX_AGAIN; + } + + if (n == 0) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "client closed prematurely connection"); + } + + if (n == 0 || n == NGX_ERROR) { + r->closed = 1; + return NGX_HTTP_BAD_REQUEST; + } + + r->request_body->buf->last += n; + r->request_body->rest -= n; + + if (r->request_body->rest == 0) { + break; + } + + if (r->request_body->buf->last < r->request_body->buf->end) { + break; + } + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http client request body rest " SIZE_T_FMT, + r->request_body->rest); + + if (r->request_body->rest) { + return NGX_AGAIN; + } + + if (r->request_body->temp_file->file.fd != NGX_INVALID_FILE) { + + /* save the last part */ + n = ngx_write_chain_to_temp_file(r->request_body->temp_file, + r->request_body->bufs->next ? r->request_body->bufs->next: + r->request_body->bufs); + + /* TODO: n == 0 or not complete and level event */ + + if (n == NGX_ERROR) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + if (!(b = ngx_calloc_buf(r->pool))) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + b->in_file = 1; + b->file_pos = 0; + b->file_last = r->request_body->temp_file->file.offset; + b->file = &r->request_body->temp_file->file; + + if (r->request_body->bufs->next) { + r->request_body->bufs->next->buf = b; + + } else { + r->request_body->bufs->buf = b; + } + } + + r->request_body->handler(r->request_body->data); + + return NGX_OK; +} diff --git a/src/http/ngx_http_special_response.c b/src/http/ngx_http_special_response.c new file mode 100644 --- /dev/null +++ b/src/http/ngx_http_special_response.c @@ -0,0 +1,358 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include +#include + + +static u_char error_tail[] = +"
" NGINX_VER "
" CRLF +"" CRLF +"" CRLF +; + + +static u_char msie_stub[] = +"" CRLF +"" CRLF +"" CRLF +"" CRLF +"" CRLF +"" CRLF +; + + +static char error_301_page[] = +"" CRLF +"301 Moved Permanently" CRLF +"" CRLF +"

301 Moved Permanently

" CRLF +; + + +static char error_302_page[] = +"" CRLF +"302 Found" CRLF +"" CRLF +"

302 Found

" CRLF +; + + +static char error_400_page[] = +"" CRLF +"400 Bad Request" CRLF +"" CRLF +"

400 Bad Request

" CRLF +; + + +static char error_403_page[] = +"" CRLF +"403 Forbidden" CRLF +"" CRLF +"

403 Forbidden

" CRLF +; + + +static char error_404_page[] = +"" CRLF +"404 Not Found" CRLF +"" CRLF +"

404 Not Found

" CRLF +; + + +static char error_405_page[] = +"" CRLF +"405 Not Allowed" CRLF +"" CRLF +"

405 Not Allowed

" CRLF +; + + +static char error_408_page[] = +"" CRLF +"408 Request Time-out" CRLF +"" CRLF +"

408 Request Time-out

" CRLF +; + + +static char error_413_page[] = +"" CRLF +"413 Request Entity Too Large" CRLF +"" CRLF +"

413 Request Entity Too Large

" CRLF +; + + +static char error_414_page[] = +"" CRLF +"414 Request-URI Too Large" CRLF +"" CRLF +"

414 Request-URI Too Large

" CRLF +; + + +static char error_416_page[] = +"" CRLF +"416 Requested Range Not Satisfiable" CRLF +"" CRLF +"

416 Requested Range Not Satisfiable

" CRLF +; + + +static char error_497_page[] = +"" CRLF +"400 The plain HTTP request was sent to HTTPS port" +CRLF +"" CRLF +"

400 Bad Request

" CRLF +"
The plain HTTP request was sent to HTTPS port
" CRLF +; + + +static char error_500_page[] = +"" CRLF +"500 Internal Server Error" CRLF +"" CRLF +"

500 Internal Server Error

" CRLF +; + + +static char error_501_page[] = +"" CRLF +"501 Method Not Implemented" CRLF +"" CRLF +"

500 Method Not Implemented

" CRLF +; + + +static char error_502_page[] = +"" CRLF +"502 Bad Gateway" CRLF +"" CRLF +"

502 Bad Gateway

" CRLF +; + + +static char error_503_page[] = +"" CRLF +"503 Service Temporarily Unavailable" CRLF +"" CRLF +"

503 Service Temporarily Unavailable

" CRLF +; + + +static char error_504_page[] = +"" CRLF +"504 Gateway Time-out" CRLF +"" CRLF +"

504 Gateway Time-out

" CRLF +; + + +static ngx_str_t error_pages[] = { + /* ngx_null_string, */ /* 300 */ + ngx_string(error_301_page), + ngx_string(error_302_page), + ngx_null_string, /* 303 */ + + ngx_string(error_400_page), + ngx_null_string, /* 401 */ + ngx_null_string, /* 402 */ + ngx_string(error_403_page), + ngx_string(error_404_page), + ngx_string(error_405_page), + ngx_null_string, /* 406 */ + ngx_null_string, /* 407 */ + ngx_string(error_408_page), + ngx_null_string, /* 409 */ + ngx_null_string, /* 410 */ + ngx_null_string, /* 411 */ + ngx_null_string, /* 412 */ + ngx_string(error_413_page), + ngx_string(error_414_page), + ngx_null_string, /* 415 */ + ngx_string(error_416_page), + + ngx_string(error_497_page), /* 497, http to https */ + ngx_string(error_404_page), /* 498, invalid host name */ + ngx_null_string, /* 499, client 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_int_t ngx_http_special_response_handler(ngx_http_request_t *r, int error) +{ + ngx_int_t rc; + ngx_uint_t err, i, msie_padding; + ngx_buf_t *b; + ngx_chain_t *out, **ll, *cl; + ngx_http_err_page_t *err_page; + ngx_http_core_loc_conf_t *clcf; + + rc = ngx_http_discard_body(r); + + if (rc == NGX_HTTP_INTERNAL_SERVER_ERROR) { + error = NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + r->headers_out.status = error; + + if (r->keepalive != 0) { + switch (error) { + case NGX_HTTP_BAD_REQUEST: + case NGX_HTTP_REQUEST_ENTITY_TOO_LARGE: + case NGX_HTTP_REQUEST_URI_TOO_LARGE: + case NGX_HTTP_TO_HTTPS: + case NGX_HTTP_INTERNAL_SERVER_ERROR: + r->keepalive = 0; + } + } + + if (r->lingering_close == 1) { + switch (error) { + case NGX_HTTP_BAD_REQUEST: + case NGX_HTTP_TO_HTTPS: + r->lingering_close = 0; + } + } + + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + if (r->err_ctx == NULL && clcf->error_pages) { + err_page = clcf->error_pages->elts; + for (i = 0; i < clcf->error_pages->nelts; i++) { + if (err_page[i].status == error) { + if (err_page[i].overwrite) { + r->err_status = err_page[i].overwrite; + } else { + r->err_status = error; + } + r->err_ctx = r->ctx; + return ngx_http_internal_redirect(r, &err_page[i].uri, NULL); + } + } + } + + if (error < NGX_HTTP_BAD_REQUEST) { + /* 3XX */ + err = error - NGX_HTTP_MOVED_PERMANENTLY; + + } else if (error < NGX_HTTP_NGX_CODES) { + /* 4XX */ + err = error - NGX_HTTP_BAD_REQUEST + 3; + + } else { + /* 49X, 5XX */ + err = error - NGX_HTTP_NGX_CODES + 3 + 17; + + switch (error) { + case NGX_HTTP_TO_HTTPS: + r->headers_out.status = NGX_HTTP_BAD_REQUEST; + error = NGX_HTTP_BAD_REQUEST; + break; + + case NGX_HTTP_INVALID_HOST: + r->headers_out.status = NGX_HTTP_NOT_FOUND; + error = NGX_HTTP_NOT_FOUND; + break; + } + } + + msie_padding = 0; + + 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(msie_stub) - 1; + msie_padding = 1; + } + + r->headers_out.content_type = ngx_list_push(&r->headers_out.headers); + if (r->headers_out.content_type == NULL) { + return NGX_ERROR; + } + + r->headers_out.content_type->key.len = sizeof("Content-Type") - 1; + r->headers_out.content_type->key.data = (u_char *) "Content-Type"; + r->headers_out.content_type->value.len = sizeof("text/html") - 1; + r->headers_out.content_type->value.data = (u_char *) "text/html"; + + } else { + r->headers_out.content_length_n = -1; + } + + if (r->headers_out.content_length) { + r->headers_out.content_length->key.len = 0; + r->headers_out.content_length = NULL; + } + + rc = ngx_http_send_header(r); + + if (rc == NGX_ERROR || r->header_only) { + return rc; + } + + if (error_pages[err].len == 0) { + return NGX_OK; + } + + out = NULL; + ll = NULL; + + if (!(b = ngx_calloc_buf(r->pool))) { + return NGX_ERROR; + } + b->memory = 1; + b->pos = error_pages[err].data; + b->last = error_pages[err].data + error_pages[err].len; + + ngx_alloc_link_and_set_buf(cl, b, r->pool, NGX_ERROR); + ngx_chain_add_link(out, ll, cl); + + + if (!(b = ngx_calloc_buf(r->pool))) { + return NGX_ERROR; + } + b->memory = 1; + b->pos = error_tail; + b->last = error_tail + sizeof(error_tail) - 1; + + ngx_alloc_link_and_set_buf(cl, b, r->pool, NGX_ERROR); + ngx_chain_add_link(out, ll, cl); + + if (msie_padding) { + if (!(b = ngx_calloc_buf(r->pool))) { + return NGX_ERROR; + } + b->memory = 1; + b->pos = msie_stub; + b->last = msie_stub + sizeof(msie_stub) - 1; + + ngx_alloc_link_and_set_buf(cl, b, r->pool, NGX_ERROR); + ngx_chain_add_link(out, ll, cl); + } + + b->last_buf = 1; + + return ngx_http_output_filter(r, out); +} diff --git a/src/http/ngx_http_write_filter.c b/src/http/ngx_http_write_filter.c new file mode 100644 --- /dev/null +++ b/src/http/ngx_http_write_filter.c @@ -0,0 +1,166 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include +#include + + +typedef struct { + ngx_chain_t *out; +} ngx_http_write_filter_ctx_t; + + +static ngx_int_t ngx_http_write_filter_init(ngx_cycle_t *cycle); + + +ngx_http_module_t ngx_http_write_filter_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_write_filter_module = { + NGX_MODULE, + &ngx_http_write_filter_module_ctx, /* module context */ + NULL, /* module directives */ + NGX_HTTP_MODULE, /* module type */ + ngx_http_write_filter_init, /* init module */ + NULL /* init process */ +}; + + +ngx_int_t ngx_http_write_filter(ngx_http_request_t *r, ngx_chain_t *in) +{ + int last; + off_t size, flush, sent; + ngx_chain_t *cl, *ln, **ll, *chain; + ngx_connection_t *c; + ngx_http_core_loc_conf_t *clcf; + ngx_http_write_filter_ctx_t *ctx; + + ctx = ngx_http_get_module_ctx(r->main ? r->main : r, + ngx_http_write_filter_module); + + if (ctx == NULL) { + ngx_http_create_ctx(r, ctx, ngx_http_write_filter_module, + sizeof(ngx_http_write_filter_ctx_t), NGX_ERROR); + } + + size = 0; + flush = 0; + last = 0; + ll = &ctx->out; + + /* find the size, the flush point and the last link of the saved chain */ + + for (cl = ctx->out; cl; cl = cl->next) { + ll = &cl->next; + + size += ngx_buf_size(cl->buf); + + if (cl->buf->flush || cl->buf->recycled) { + flush = size; + } + + if (cl->buf->last_buf) { + last = 1; + } + } + + /* add the new chain to the existent one */ + + for (ln = in; ln; ln = ln->next) { + ngx_alloc_link_and_set_buf(cl, ln->buf, r->pool, NGX_ERROR); + *ll = cl; + ll = &cl->next; + + size += ngx_buf_size(cl->buf); + + if (cl->buf->flush || cl->buf->recycled) { + flush = size; + } + + if (cl->buf->last_buf) { + last = 1; + } + } + + c = r->connection; + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http write filter: l:%d f:" OFF_T_FMT " s:" OFF_T_FMT, + last, flush, size); + + clcf = ngx_http_get_module_loc_conf(r->main ? r->main : r, + ngx_http_core_module); + + /* + * avoid the output if there is no last buf, no flush point, + * there are the incoming bufs and the size of all bufs + * is smaller than "postpone_output" directive + */ + + if (!last && flush == 0 && in && size < (off_t) clcf->postpone_output) { + return NGX_OK; + } + + if (c->write->delayed) { + return NGX_AGAIN; + } + + if (size == 0 && !c->buffered) { + if (!last) { + ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, + "the http output chain is empty"); + } + return NGX_OK; + } + + sent = c->sent; + + chain = c->send_chain(c, ctx->out, + clcf->limit_rate ? clcf->limit_rate: OFF_T_MAX_VALUE); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http write filter %X", chain); + + if (clcf->limit_rate) { + sent = c->sent - sent; + c->write->delayed = 1; + ngx_add_timer(r->connection->write, + (ngx_msec_t) (sent * 1000 / clcf->limit_rate)); + } + + if (chain == NGX_CHAIN_ERROR) { + return NGX_ERROR; + } + + ctx->out = chain; + + if (chain || c->buffered) { + return NGX_AGAIN; + } + + return NGX_OK; +} + + +static ngx_int_t ngx_http_write_filter_init(ngx_cycle_t *cycle) +{ + ngx_http_top_body_filter = ngx_http_write_filter; + + return NGX_OK; +} diff --git a/src/os/unix/ngx_aio.h b/src/os/unix/ngx_aio.h new file mode 100644 --- /dev/null +++ b/src/os/unix/ngx_aio.h @@ -0,0 +1,21 @@ + +/* + * 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 --git a/src/os/unix/ngx_aio_read.c b/src/os/unix/ngx_aio_read.c new file mode 100644 --- /dev/null +++ b/src/os/unix/ngx_aio_read.c @@ -0,0 +1,117 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include +#include + +#if (HAVE_KQUEUE) +#include +#endif + + +/* + * 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) +{ + int n; + ngx_event_t *rev; + + rev = c->read; + + if (!rev->ready) { + ngx_log_error(NGX_LOG_ALERT, c->log, 0, "second aio post"); + return NGX_AGAIN; + } + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, + "rev->complete: %d", rev->complete); + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, + "aio size: %d", size); + + if (!rev->complete) { + ngx_memzero(&rev->aiocb, sizeof(struct aiocb)); + + rev->aiocb.aio_fildes = c->fd; + rev->aiocb.aio_buf = buf; + rev->aiocb.aio_nbytes = size; + +#if (HAVE_KQUEUE) + rev->aiocb.aio_sigevent.sigev_notify_kqueue = ngx_kqueue; + rev->aiocb.aio_sigevent.sigev_notify = SIGEV_KEVENT; + rev->aiocb.aio_sigevent.sigev_value.sigval_ptr = rev; +#endif + + if (aio_read(&rev->aiocb) == -1) { + ngx_log_error(NGX_LOG_CRIT, rev->log, ngx_errno, + "aio_read() failed"); + rev->error = 1; + return NGX_ERROR; + } + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, + "aio_read: #%d OK", c->fd); + + rev->active = 1; + rev->ready = 0; + } + + rev->complete = 0; + + n = aio_error(&rev->aiocb); + if (n == -1) { + ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno, "aio_error() failed"); + rev->error = 1; + return NGX_ERROR; + } + + if (n != 0) { + if (n == NGX_EINPROGRESS) { + if (rev->ready) { + ngx_log_error(NGX_LOG_ALERT, c->log, n, + "aio_read() still in progress"); + rev->ready = 0; + } + return NGX_AGAIN; + } + + ngx_log_error(NGX_LOG_CRIT, c->log, n, "aio_read() failed"); + rev->error = 1; + rev->ready = 0; + return NGX_ERROR; + } + + n = aio_return(&rev->aiocb); + if (n == -1) { + ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno, + "aio_return() failed"); + + rev->error = 1; + rev->ready = 0; + return NGX_ERROR; + } + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, rev->log, 0, + "aio_read: #%d %d", c->fd, n); + + if (n == 0) { + rev->eof = 1; + rev->ready = 0; + } else { + rev->ready = 1; + } + + rev->active = 0; + + return n; +} diff --git a/src/os/unix/ngx_aio_read_chain.c b/src/os/unix/ngx_aio_read_chain.c new file mode 100644 --- /dev/null +++ b/src/os/unix/ngx_aio_read_chain.c @@ -0,0 +1,78 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include +#include + + +ssize_t ngx_aio_read_chain(ngx_connection_t *c, ngx_chain_t *cl) +{ + int n; + u_char *buf, *prev; + size_t size; + ssize_t total; + ngx_err_t err; + + if (c->read->pending_eof) { + c->read->ready = 0; + return 0; + } + + total = 0; + + while (cl) { + + /* we can post the single aio operation only */ + + if (!c->read->ready) { + return total ? total : NGX_AGAIN; + } + + buf = cl->buf->last; + prev = cl->buf->last; + size = 0; + + /* coalesce the neighbouring bufs */ + + while (cl && prev == cl->buf->last) { + size += cl->buf->end - cl->buf->last; + prev = cl->buf->end; + cl = cl->next; + } + + n = ngx_aio_read(c, buf, size); + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "aio_read: %d", n); + + if (n == NGX_AGAIN) { + return total ? total : NGX_AGAIN; + } + + if (n == NGX_ERROR) { + return NGX_ERROR; + } + + if (n == 0) { + c->read->pending_eof = 1; + if (total) { + c->read->eof = 0; + c->read->ready = 1; + } + return total; + } + + if (n > 0) { + total += n; + } + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, + "aio_read total: %d", total); + } + + return total ? total : NGX_AGAIN; +} diff --git a/src/os/unix/ngx_aio_write.c b/src/os/unix/ngx_aio_write.c new file mode 100644 --- /dev/null +++ b/src/os/unix/ngx_aio_write.c @@ -0,0 +1,117 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include +#include + +#if (HAVE_KQUEUE) +#include +#endif + + +/* + * 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) +{ + int n; + ngx_event_t *wev; + + wev = c->write; + + if (!wev->ready) { + return NGX_AGAIN; + } + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, wev->log, 0, + "aio: wev->complete: %d", wev->complete); + + if (!wev->complete) { + ngx_memzero(&wev->aiocb, sizeof(struct aiocb)); + + wev->aiocb.aio_fildes = c->fd; + wev->aiocb.aio_buf = buf; + wev->aiocb.aio_nbytes = size; + +#if (HAVE_KQUEUE) + wev->aiocb.aio_sigevent.sigev_notify_kqueue = ngx_kqueue; + wev->aiocb.aio_sigevent.sigev_notify = SIGEV_KEVENT; + wev->aiocb.aio_sigevent.sigev_value.sigval_ptr = wev; +#endif + + if (aio_write(&wev->aiocb) == -1) { + ngx_log_error(NGX_LOG_CRIT, wev->log, ngx_errno, + "aio_write() failed"); + return NGX_ERROR; + } + + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, wev->log, 0, "aio_write: OK"); + + wev->active = 1; + wev->ready = 0; + } + + wev->complete = 0; + + n = aio_error(&wev->aiocb); + if (n == -1) { + ngx_log_error(NGX_LOG_CRIT, wev->log, ngx_errno, "aio_error() failed"); + wev->error = 1; + return NGX_ERROR; + } + + if (n != 0) { + if (n == NGX_EINPROGRESS) { + if (wev->ready) { + ngx_log_error(NGX_LOG_ALERT, wev->log, n, + "aio_write() still in progress"); + wev->ready = 0; + } + return NGX_AGAIN; + } + + ngx_log_error(NGX_LOG_CRIT, wev->log, n, "aio_write() failed"); + wev->error = 1; + wev->ready = 0; + +#if 1 + n = aio_return(&wev->aiocb); + if (n == -1) { + ngx_log_error(NGX_LOG_ALERT, wev->log, ngx_errno, + "aio_return() failed"); + } + + ngx_log_error(NGX_LOG_CRIT, wev->log, n, "aio_return() %d", n); +#endif + + return NGX_ERROR; + } + + n = aio_return(&wev->aiocb); + if (n == -1) { + ngx_log_error(NGX_LOG_ALERT, wev->log, ngx_errno, + "aio_return() failed"); + + wev->error = 1; + wev->ready = 0; + return NGX_ERROR; + } + + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, wev->log, 0, "aio_write: %d", n); + + wev->active = 0; + wev->ready = 1; + + return n; +} diff --git a/src/os/unix/ngx_aio_write_chain.c b/src/os/unix/ngx_aio_write_chain.c new file mode 100644 --- /dev/null +++ b/src/os/unix/ngx_aio_write_chain.c @@ -0,0 +1,96 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include +#include + + +ngx_chain_t *ngx_aio_write_chain(ngx_connection_t *c, ngx_chain_t *in, + off_t limit) +{ + int n; + u_char *buf, *prev; + off_t send, sent; + size_t len; + ssize_t size; + ngx_err_t err; + ngx_chain_t *cl; + + send = 0; + sent = 0; + cl = in; + + while (cl) { + + if (cl->buf->pos == cl->buf->last) { + cl = cl->next; + continue; + } + + /* we can post the single aio operation only */ + + if (!c->write->ready) { + return cl; + } + + buf = cl->buf->pos; + prev = buf; + len = 0; + + /* coalesce the neighbouring bufs */ + + while (cl && prev == cl->buf->pos && send < limit) { + if (ngx_buf_special(cl->buf)) { + continue; + } + + size = cl->buf->last - cl->buf->pos; + + if (send + size > limit) { + size = limit - send; + } + + len += size; + prev = cl->buf->pos + size; + send += size; + cl = cl->next; + } + + n = ngx_aio_write(c, buf, len); + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "aio_write: %d", n); + + if (n == NGX_ERROR) { + return NGX_CHAIN_ERROR; + } + + if (n > 0) { + sent += n; + c->sent += n; + } + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, + "aio_write sent: " OFF_T_FMT, c->sent); + + for (cl = in; cl; cl = cl->next) { + + if (sent >= cl->buf->last - cl->buf->pos) { + sent -= cl->buf->last - cl->buf->pos; + cl->buf->pos = cl->buf->last; + + continue; + } + + cl->buf->pos += sent; + + break; + } + } + + return cl; +} diff --git a/src/os/unix/ngx_alloc.c b/src/os/unix/ngx_alloc.c new file mode 100644 --- /dev/null +++ b/src/os/unix/ngx_alloc.c @@ -0,0 +1,80 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include + + +int ngx_pagesize; + + +void *ngx_alloc(size_t size, ngx_log_t *log) +{ + void *p; + + if (!(p = malloc(size))) { + ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, + "malloc() " SIZE_T_FMT " bytes failed", size); + } + + ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, log, 0, + "malloc: " PTR_FMT ":" SIZE_T_FMT, p, size); + + return p; +} + + +void *ngx_calloc(size_t size, ngx_log_t *log) +{ + void *p; + + p = ngx_alloc(size, log); + + if (p) { + ngx_memzero(p, size); + } + + return p; +} + + +#if (HAVE_POSIX_MEMALIGN) + +void *ngx_memalign(size_t alignment, size_t size, ngx_log_t *log) +{ + void *p; + + if (posix_memalign(&p, alignment, size) == -1) { + ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, + "posix_memalign() " SIZE_T_FMT " bytes aligned to " + SIZE_T_FMT " failed", size, alignment); + } + + ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, log, 0, + "posix_memalign: " PTR_FMT ":" SIZE_T_FMT, p, size); + + return p; +} + +#elif (HAVE_MEMALIGN) + +void *ngx_memalign(size_t alignment, size_t size, ngx_log_t *log) +{ + void *p; + + if (!(p = memalign(alignment, size))) { + ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, + "memalign() " SIZE_T_FMT " bytes aligned to " + SIZE_T_FMT " failed", size, alignment); + } + + ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, log, 0, + "memalign: " PTR_FMT ":" SIZE_T_FMT, p, size); + + return p; +} + +#endif diff --git a/src/os/unix/ngx_alloc.h b/src/os/unix/ngx_alloc.h new file mode 100644 --- /dev/null +++ b/src/os/unix/ngx_alloc.h @@ -0,0 +1,42 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#ifndef _NGX_ALLOC_H_INCLUDED_ +#define _NGX_ALLOC_H_INCLUDED_ + + +#include +#include + + +void *ngx_alloc(size_t size, ngx_log_t *log); +void *ngx_calloc(size_t size, ngx_log_t *log); + +#define ngx_free free + + +/* + * 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. + */ + +#if (HAVE_POSIX_MEMALIGN || HAVE_MEMALIGN) + +void *ngx_memalign(size_t alignment, size_t size, ngx_log_t *log); + +#else + +#define ngx_memalign(alignment, size, log) ngx_alloc(size, log) + +#endif + + +extern int ngx_pagesize; + + +#endif /* _NGX_ALLOC_H_INCLUDED_ */ diff --git a/src/os/unix/ngx_atomic.h b/src/os/unix/ngx_atomic.h new file mode 100644 --- /dev/null +++ b/src/os/unix/ngx_atomic.h @@ -0,0 +1,155 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#ifndef _NGX_ATOMIC_H_INCLUDED_ +#define _NGX_ATOMIC_H_INCLUDED_ + + +#include +#include + + +#if ( __i386__ || __amd64__ ) + +#define NGX_HAVE_ATOMIC_OPS 1 + +typedef volatile uint32_t ngx_atomic_t; + +#if (NGX_SMP) +#define NGX_SMP_LOCK "lock;" +#else +#define NGX_SMP_LOCK +#endif + + +static ngx_inline uint32_t ngx_atomic_inc(ngx_atomic_t *value) +{ + uint32_t old; + + __asm__ volatile ( + + NGX_SMP_LOCK + " xaddl %0, %2; " + " incl %0; " + + : "=q" (old) : "0" (1), "m" (*value)); + + return old; +} + + +#if 0 + +static ngx_inline uint32_t ngx_atomic_dec(ngx_atomic_t *value) +{ + uint32_t old; + + __asm__ volatile ( + + NGX_SMP_LOCK + " xaddl %0, %1; " + " decl %0; " + + : "=q" (old) : "0" (-1), "m" (*value)); + + return old; +} + +#endif + + +static ngx_inline uint32_t ngx_atomic_cmp_set(ngx_atomic_t *lock, + ngx_atomic_t old, + ngx_atomic_t set) +{ + uint32_t res; + + __asm__ volatile ( + + NGX_SMP_LOCK + " cmpxchgl %3, %1; " + " setz %%al; " + " movzbl %%al, %0; " + + : "=a" (res) : "m" (*lock), "a" (old), "q" (set)); + + return res; +} + + +#elif ( __sparc__ ) + +#define NGX_HAVE_ATOMIC_OPS 1 + +typedef volatile uint32_t ngx_atomic_t; + + +static ngx_inline uint32_t ngx_atomic_inc(ngx_atomic_t *value) +{ + uint32_t old, new, res; + + old = *value; + + for ( ;; ) { + + new = old + 1; + res = new; + + __asm__ volatile ( + + "casa [%1] 0x80, %2, %0" + + : "+r" (res) : "r" (value), "r" (old)); + + if (res == old) { + return new; + } + + old = res; + } +} + + +static ngx_inline uint32_t ngx_atomic_cmp_set(ngx_atomic_t *lock, + ngx_atomic_t old, + ngx_atomic_t set) +{ + uint32_t res = (uint32_t) set; + + __asm__ volatile ( + + "casa [%1] 0x80, %2, %0" + + : "+r" (res) : "r" (lock), "r" (old)); + + return (res == old); +} + +#else + +#define NGX_HAVE_ATOMIC_OPS 0 + +typedef volatile uint32_t ngx_atomic_t; + +#define ngx_atomic_inc(x) ++(*(x)); + +static ngx_inline uint32_t ngx_atomic_cmp_set(ngx_atomic_t *lock, + ngx_atomic_t old, + ngx_atomic_t set) +{ + return 1; +} + +#endif + + +void ngx_spinlock(ngx_atomic_t *lock, ngx_uint_t spin); + +#define ngx_trylock(lock) (*(lock) == 0 && ngx_atomic_cmp_set(lock, 0, 1)) +#define ngx_unlock(lock) *(lock) = 0 + + +#endif /* _NGX_ATOMIC_H_INCLUDED_ */ diff --git a/src/os/unix/ngx_channel.c b/src/os/unix/ngx_channel.c new file mode 100644 --- /dev/null +++ b/src/os/unix/ngx_channel.c @@ -0,0 +1,237 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include + + +ngx_int_t ngx_write_channel(ngx_socket_t s, ngx_channel_t *ch, size_t size, + ngx_log_t *log) +{ + ssize_t n; + ngx_err_t err; + struct iovec iov[1]; + struct msghdr msg; + +#if (HAVE_MSGHDR_MSG_CONTROL) + + union { + struct cmsghdr cm; + char space[CMSG_SPACE(sizeof(int))]; + } cmsg; + + if (ch->fd == -1) { + msg.msg_control = NULL; + msg.msg_controllen = 0; + + } else { + msg.msg_control = (caddr_t) &cmsg; + msg.msg_controllen = sizeof(cmsg); + + cmsg.cm.cmsg_len = sizeof(cmsg); + cmsg.cm.cmsg_level = SOL_SOCKET; + cmsg.cm.cmsg_type = SCM_RIGHTS; + *(int *) CMSG_DATA(&cmsg.cm) = ch->fd; + } + +#else + + if (ch->fd == -1) { + msg.msg_accrights = NULL; + msg.msg_accrightslen = 0; + + } else { + msg.msg_accrights = (caddr_t) &ch->fd; + msg.msg_accrightslen = sizeof(int); + } + +#endif + + iov[0].iov_base = (char *) ch; + iov[0].iov_len = size; + + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + n = sendmsg(s, &msg, 0); + + if (n == -1) { + err = ngx_errno; + if (err == NGX_EAGAIN) { + return NGX_AGAIN; + } + + ngx_log_error(NGX_LOG_ALERT, log, err, "sendmsg() failed"); + return NGX_ERROR; + } + + return NGX_OK; +} + + +ngx_int_t ngx_read_channel(ngx_socket_t s, ngx_channel_t *ch, size_t size, + ngx_log_t *log) +{ + ssize_t n; + ngx_err_t err; + struct iovec iov[1]; + struct msghdr msg; + +#if (HAVE_MSGHDR_MSG_CONTROL) + union { + struct cmsghdr cm; + char space[CMSG_SPACE(sizeof(int))]; + } cmsg; +#else + int fd; +#endif + + iov[0].iov_base = (char *) ch; + iov[0].iov_len = size; + + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_iov = iov; + msg.msg_iovlen = 1; + +#if (HAVE_MSGHDR_MSG_CONTROL) + msg.msg_control = (caddr_t) &cmsg; + msg.msg_controllen = sizeof(cmsg); +#else + msg.msg_accrights = (caddr_t) &fd; + msg.msg_accrightslen = sizeof(int); +#endif + + n = recvmsg(s, &msg, 0); + + if (n == -1) { + err = ngx_errno; + if (err == NGX_EAGAIN) { + return NGX_AGAIN; + } + + ngx_log_error(NGX_LOG_ALERT, log, err, "recvmsg() failed"); + return NGX_ERROR; + } + + if ((size_t) n < sizeof(ngx_channel_t)) { + ngx_log_error(NGX_LOG_ALERT, log, 0, + "recvmsg() returned not enough data"); + return NGX_ERROR; + } + +#if (HAVE_MSGHDR_MSG_CONTROL) + + if (ch->command == NGX_CMD_OPEN_CHANNEL) { + + if (cmsg.cm.cmsg_len < sizeof(cmsg)) { + ngx_log_error(NGX_LOG_ALERT, log, 0, + "recvmsg() returned too small ancillary data"); + return NGX_ERROR; + } + + if (cmsg.cm.cmsg_level != SOL_SOCKET || cmsg.cm.cmsg_type != SCM_RIGHTS) + { + ngx_log_error(NGX_LOG_ALERT, log, 0, + "recvmsg() returned invalid ancillary data " + "level %d or type %d", + cmsg.cm.cmsg_level, cmsg.cm.cmsg_type); + return NGX_ERROR; + } + + ch->fd = *(int *) CMSG_DATA(&cmsg.cm); + } + + if (msg.msg_flags & (MSG_TRUNC|MSG_CTRUNC)) { + ngx_log_error(NGX_LOG_ALERT, log, 0, + "recvmsg() truncated data"); + } + +#else + + if (ch->command == NGX_CMD_OPEN_CHANNEL) { + if (msg.msg_accrightslen != sizeof(int)) { + ngx_log_error(NGX_LOG_ALERT, log, 0, + "recvmsg() returned no ancillary data"); + return NGX_ERROR; + } + + ch->fd = fd; + } + +#endif + + return n; +} + + +ngx_int_t ngx_add_channel_event(ngx_cycle_t *cycle, ngx_fd_t fd, + ngx_int_t event, ngx_event_handler_pt handler) +{ + ngx_event_t *ev, *rev, *wev; + ngx_connection_t *c; + + c = &cycle->connections[fd]; + rev = &cycle->read_events[fd]; + wev = &cycle->write_events[fd]; + + ngx_memzero(c, sizeof(ngx_connection_t)); + ngx_memzero(rev, sizeof(ngx_event_t)); + ngx_memzero(wev, sizeof(ngx_event_t)); + + c->fd = fd; + c->pool = cycle->pool; + + c->read = rev; + c->write = wev; + + c->log = cycle->log; + rev->log = cycle->log; + wev->log = cycle->log; + rev->index = NGX_INVALID_INDEX; + wev->index = NGX_INVALID_INDEX; + rev->data = c; + wev->data = c; + +#if (NGX_THREADS) + rev->lock = &c->lock; + wev->lock = &c->lock; + rev->own_lock = &c->lock; + wev->own_lock = &c->lock; +#endif + + ev = (event == NGX_READ_EVENT) ? rev : wev; + + ev->event_handler = handler; + + if (ngx_add_conn && (ngx_event_flags & NGX_USE_EPOLL_EVENT) == 0) { + if (ngx_add_conn(c) == NGX_ERROR) { + return NGX_ERROR; + } + + } else { + if (ngx_add_event(ev, event, 0) == NGX_ERROR) { + return NGX_ERROR; + } + } + + return NGX_OK; +} + + +void ngx_close_channel(ngx_fd_t *fd, ngx_log_t *log) +{ + if (close(fd[0]) == -1) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, "close() failed"); + } + + if (close(fd[1]) == -1) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, "close() failed"); + } +} diff --git a/src/os/unix/ngx_channel.h b/src/os/unix/ngx_channel.h new file mode 100644 --- /dev/null +++ b/src/os/unix/ngx_channel.h @@ -0,0 +1,33 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#ifndef _NGX_CHANNEL_H_INCLUDED_ +#define _NGX_CHANNEL_H_INCLUDED_ + + +#include +#include +#include + + +typedef struct { + ngx_uint_t command; + ngx_pid_t pid; + ngx_int_t slot; + ngx_fd_t fd; +} ngx_channel_t; + + +ngx_int_t ngx_write_channel(ngx_socket_t s, ngx_channel_t *ch, size_t size, + ngx_log_t *log); +ngx_int_t ngx_read_channel(ngx_socket_t s, ngx_channel_t *ch, size_t size, + ngx_log_t *log); +ngx_int_t ngx_add_channel_event(ngx_cycle_t *cycle, ngx_fd_t fd, + ngx_int_t event, ngx_event_handler_pt handler); +void ngx_close_channel(ngx_fd_t *fd, ngx_log_t *log); + + +#endif /* _NGX_CHANNEL_H_INCLUDED_ */ diff --git a/src/os/unix/ngx_daemon.c b/src/os/unix/ngx_daemon.c new file mode 100644 --- /dev/null +++ b/src/os/unix/ngx_daemon.c @@ -0,0 +1,68 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include + + +int ngx_daemon(ngx_log_t *log) +{ + int fd; + + switch (fork()) { + case -1: + ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "fork() failed"); + return NGX_ERROR; + + case 0: + break; + + default: + exit(0); + } + + ngx_pid = ngx_getpid(); + + if (setsid() == -1) { + ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "setsid() failed"); + return NGX_ERROR; + } + + umask(0); + + fd = open("/dev/null", O_RDWR); + if (fd == -1) { + ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, + "open(\"/dev/null\") failed"); + return NGX_ERROR; + } + + if (dup2(fd, STDIN_FILENO) == -1) { + ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "dup2(STDIN) failed"); + return NGX_ERROR; + } + + if (dup2(fd, STDOUT_FILENO) == -1) { + ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "dup2(STDOUT) failed"); + return NGX_ERROR; + } + +#if 0 + if (dup2(fd, STDERR_FILENO) == -1) { + ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "dup2(STDERR) failed"); + return NGX_ERROR; + } +#endif + + if (fd > STDERR_FILENO) { + if (close(fd) == -1) { + ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "close() failed"); + return NGX_ERROR; + } + } + + return NGX_OK; +} diff --git a/src/os/unix/ngx_errno.c b/src/os/unix/ngx_errno.c new file mode 100644 --- /dev/null +++ b/src/os/unix/ngx_errno.c @@ -0,0 +1,64 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include + + +#if (HAVE_STRERROR_R) + +ngx_int_t ngx_strerror_r(int err, char *errstr, size_t size) +{ + size_t len; + + if (size == 0) { + return 0; + } + + errstr[0] = '\0'; + + strerror_r(err, errstr, size); + + for (len = 0; len < size; len++) { + if (errstr[len] == '\0') { + break; + } + } + + return len; +} + +#elif (HAVE_GNU_STRERROR_R) + +/* Linux strerror_r() */ + +ngx_int_t ngx_strerror_r(int err, char *errstr, size_t size) +{ + char *str; + size_t len; + + if (size == 0) { + return 0; + } + + errstr[0] = '\0'; + + str = strerror_r(err, errstr, size); + + if (str != errstr) { + return ngx_cpystrn(errstr, str, size) - (u_char *) errstr; + } + + for (len = 0; len < size; len++) { + if (errstr[len] == '\0') { + break; + } + } + + return len; +} + +#endif diff --git a/src/os/unix/ngx_errno.h b/src/os/unix/ngx_errno.h new file mode 100644 --- /dev/null +++ b/src/os/unix/ngx_errno.h @@ -0,0 +1,60 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#ifndef _NGX_ERRNO_H_INCLUDED_ +#define _NGX_ERRNO_H_INCLUDED_ + + +#include +#include + + +typedef int ngx_err_t; + +#define NGX_ENOENT ENOENT +#define NGX_ESRCH ESRCH +#define NGX_EINTR EINTR +#define NGX_ECHILD ECHILD +#define NGX_ENOMEM ENOMEM +#define NGX_EACCES EACCES +#define NGX_EBUSY EBUSY +#define NGX_EEXIST EEXIST +#define NGX_ENOTDIR ENOTDIR +#define NGX_EINVAL EINVAL +#define NGX_EPIPE EPIPE +#define NGX_EAGAIN EWOULDBLOCK +#define NGX_EINPROGRESS EINPROGRESS +#define NGX_EADDRINUSE EADDRINUSE +#define NGX_ECONNABORTED ECONNABORTED +#define NGX_ECONNRESET ECONNRESET +#define NGX_ENOTCONN ENOTCONN +#define NGX_ETIMEDOUT ETIMEDOUT +#define NGX_ECONNREFUSED ECONNREFUSED +#define NGX_EHOSTUNREACH EHOSTUNREACH +#define NGX_ECANCELED ECANCELED +#define NGX_ENOMOREFILES 0 + + + +#define ngx_errno errno +#define ngx_socket_errno errno +#define ngx_set_errno(err) errno = err +#define ngx_set_socket_errno(err) errno = err + + +#if (HAVE_STRERROR_R || HAVE_GNU_STRERROR_R) + +ngx_int_t ngx_strerror_r(int err, char *errstr, size_t size); + +#else + +#define ngx_strerror_r(err, errstr, size) \ + (char *) ngx_cpystrn(errstr, strerror(err), size) - (errstr) + +#endif + + +#endif /* _NGX_ERRNO_H_INCLUDED_ */ diff --git a/src/os/unix/ngx_files.c b/src/os/unix/ngx_files.c new file mode 100644 --- /dev/null +++ b/src/os/unix/ngx_files.c @@ -0,0 +1,268 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include + + +ssize_t ngx_read_file(ngx_file_t *file, u_char *buf, size_t size, off_t offset) +{ + ssize_t n; + + ngx_log_debug4(NGX_LOG_DEBUG_CORE, file->log, 0, + "read: %d, %X, %d, " OFF_T_FMT, file->fd, buf, size, offset); + +#if (HAVE_PREAD) + + n = pread(file->fd, buf, size, offset); + + if (n == -1) { + ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno, + "pread() failed, file \"%s\"", file->name.data); + return NGX_ERROR; + } + +#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"); + return NGX_ERROR; + } + + file->sys_offset = offset; + } + + n = read(file->fd, buf, size); + + if (n == -1) { + ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno, "read() failed"); + return NGX_ERROR; + } + + file->sys_offset += n; + +#endif + + file->offset += n; + + return n; +} + + +ssize_t ngx_write_file(ngx_file_t *file, u_char *buf, size_t size, off_t offset) +{ + ssize_t n; + +#if (HAVE_PWRITE) + + n = pwrite(file->fd, buf, size, offset); + + if (n == -1) { + ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno, "pwrite() failed"); + return NGX_ERROR; + } + + if ((size_t) n != size) { + ngx_log_error(NGX_LOG_CRIT, file->log, 0, + "pwrite() has written only %d of %d", n, size); + return NGX_ERROR; + } + +#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"); + return NGX_ERROR; + } + + file->sys_offset = offset; + } + + n = write(file->fd, buf, size); + + if (n == -1) { + ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno, "write() failed"); + return NGX_ERROR; + } + + if ((size_t) n != size) { + ngx_log_error(NGX_LOG_CRIT, file->log, 0, + "write() has written only %d of %d", n, size); + return NGX_ERROR; + } + + file->sys_offset += n; + +#endif + + file->offset += n; + + return n; +} + + +int ngx_open_tempfile(u_char *name, ngx_uint_t persistent) +{ + ngx_fd_t fd; + + fd = open((const char *) name, O_CREAT|O_EXCL|O_RDWR, 0600); + + if (fd != -1 && !persistent) { + unlink((const char *) name); + } + + return fd; +} + + +ssize_t ngx_write_chain_to_file(ngx_file_t *file, ngx_chain_t *cl, + off_t offset, ngx_pool_t *pool) +{ + u_char *prev; + size_t size; + ssize_t n; + struct iovec *iov; + ngx_err_t err; + ngx_array_t io; + + /* use pwrite() if there's the only buf in a chain */ + + if (cl->next == NULL) { + return ngx_write_file(file, cl->buf->pos, + (size_t) (cl->buf->last - cl->buf->pos), + offset); + } + + prev = NULL; + iov = NULL; + size = 0; + + ngx_init_array(io, pool, 10, sizeof(struct iovec), NGX_ERROR); + + /* create the iovec and coalesce the neighbouring bufs */ + + while (cl) { + if (prev == cl->buf->pos) { + iov->iov_len += cl->buf->last - cl->buf->pos; + + } else { + ngx_test_null(iov, ngx_push_array(&io), NGX_ERROR); + iov->iov_base = (void *) cl->buf->pos; + iov->iov_len = cl->buf->last - cl->buf->pos; + } + + size += cl->buf->last - cl->buf->pos; + prev = cl->buf->last; + cl = cl->next; + } + + /* use pwrite() if there's the only iovec buffer */ + + if (io.nelts == 1) { + iov = io.elts; + return ngx_write_file(file, (u_char *) iov[0].iov_base, iov[0].iov_len, + offset); + } + + 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"); + return NGX_ERROR; + } + + file->sys_offset = offset; + } + + n = writev(file->fd, io.elts, io.nelts); + + if (n == -1) { + ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno, "writev() failed"); + return NGX_ERROR; + } + + if ((size_t) n != size) { + ngx_log_error(NGX_LOG_CRIT, file->log, 0, + "writev() has written only %d of %d", n, size); + return NGX_ERROR; + } + + file->sys_offset += n; + file->offset += n; + + return n; +} + + +int ngx_open_dir(ngx_str_t *name, ngx_dir_t *dir) +{ + dir->dir = opendir((const char *) name->data); + + if (dir->dir == NULL) { + return NGX_ERROR; + } + + dir->info_valid = 0; + + return NGX_OK; +} + + +#if 0 + +ssize_t ngx_read_file(ngx_file_t *file, char *buf, size_t size, off_t offset) +{ + if (!file->read->ready) { + + ngx_memzero(&file->iocb, sizeof(iocb)); + file->iocb.aio_fildes = file->fd; + file->iocb.aio_buf = buf; + file->iocb.aio_nbytes = size; + file->iocb.aio_offset = offset; +#if (USE_AIO_KQUEUE) + file->iocb.aio_sigevent.sigev_notify = SIGEV_KEVENT; + file->iocb.aio_sigevent.sigev_notify_kqueue = tid->kq; + file->iocb.aio_sigevent.sigev_value = (union sigval) file; +#endif +#if (USE_AIO_SIGNAL) + file->iocb.aio_sigevent.sigev_notify = SIGEV_SIGNAL; + file->iocb.aio_sigevent.sigev_signo = NGX_SIGAIO; +#ifndef __FreeBSD__ + file->iocb.aio_sigevent.sigev_value.sival_ptr = file; +#endif +#endif + + if (aio_read(&file->iocb) == -1) { + ngx_log_error(NGX_LOG_ERR, file->log, ngx_errno, + "aio_read() failed"); + return NGX_ERROR; + + n = aio_error(&file->iocb); + if (n == EINPROGRESS) + return NGX_AGAIN; + + if (n == -1) { + ngx_log_error(NGX_LOG_ERR, file->log, ngx_errno, + "aio_read() failed"); + return NGX_ERROR; + } + } + + ngx_assert(file->iocb.aio_buf == buf), return NGX_ERROR, + "ngx_aio_read_file: another buffer is passed"); + + n = aio_return(&file->iocb); + if (n == -1) { + ngx_log_error(NGX_LOG_ERR, file->log, ngx_errno, + "aio_read() failed"); + return NGX_ERROR; + } + + return n; +} + +#endif diff --git a/src/os/unix/ngx_files.h b/src/os/unix/ngx_files.h new file mode 100644 --- /dev/null +++ b/src/os/unix/ngx_files.h @@ -0,0 +1,115 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#ifndef _NGX_FILES_H_INCLUDED_ +#define _NGX_FILES_H_INCLUDED_ + + +#include +#include + + +#define NGX_INVALID_FILE -1 +#define NGX_FILE_ERROR -1 + + + +#define ngx_open_file(name, access, create) \ + open((const char *) name, access|create, 0644) +#define ngx_open_file_n "open()" + +#define NGX_FILE_RDONLY O_RDONLY +#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_close_file close +#define ngx_close_file_n "close()" + + +#define ngx_delete_file(name) unlink((const char *) name) +#define ngx_delete_file_n "unlink()" + + +int ngx_open_tempfile(u_char *name, ngx_uint_t persistent); +#define ngx_open_tempfile_n "open()" + + +ssize_t ngx_read_file(ngx_file_t *file, u_char *buf, size_t size, off_t offset); +#define ngx_read_file_n "read()" + + +ssize_t ngx_write_file(ngx_file_t *file, u_char *buf, size_t size, + off_t offset); + +ssize_t ngx_write_chain_to_file(ngx_file_t *file, ngx_chain_t *ce, + off_t offset, ngx_pool_t *pool); + + +#define ngx_rename_file rename +#define ngx_rename_file_n "rename" + + +#define ngx_file_info(file, sb) stat((const char *) file, sb) +#define ngx_file_info_n "stat()" + +#define ngx_fd_info(fd, sb) fstat(fd, sb) +#define ngx_fd_info_n "fstat()" + +#define ngx_is_dir(sb) (S_ISDIR((sb)->st_mode)) +#define ngx_is_file(sb) (S_ISREG((sb)->st_mode)) +#define ngx_file_size(sb) (sb)->st_size +#define ngx_file_mtime(sb) (sb)->st_mtime +#define ngx_file_uniq(sb) (sb)->st_ino + + + +#define ngx_getcwd(buf, size) (getcwd(buf, size) != NULL) +#define ngx_getcwd_n "getcwd()" +#define NGX_MAX_PATH PATH_MAX + +#define NGX_DIR_MASK_LEN 0 + + +int ngx_open_dir(ngx_str_t *name, ngx_dir_t *dir); +#define ngx_open_dir_n "opendir()" + + +#define ngx_close_dir(d) closedir((d)->dir) +#define ngx_close_dir_n "closedir()" + + +#define ngx_read_dir(d) \ + (((d)->de = readdir((d)->dir)) ? NGX_OK : NGX_ERROR) +#define ngx_read_dir_n "readdir()" + + +#define ngx_create_dir(name) mkdir((const char *) name, 0700) +#define ngx_create_dir_n "mkdir()" + + +#define ngx_delete_dir(name) rmdir((const char *) name) +#define ngx_delete_dir_n "rmdir()" + + +#define ngx_de_name(dir) (dir)->de->d_name +#ifdef __FreeBSD__ +#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) +#define ngx_de_info_n "stat()" +#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_size(dir) (dir)->info.st_size +#define ngx_de_mtime(dir) (dir)->info.st_mtime + + +#endif /* _NGX_FILES_H_INCLUDED_ */ diff --git a/src/os/unix/ngx_freebsd.h b/src/os/unix/ngx_freebsd.h new file mode 100644 --- /dev/null +++ b/src/os/unix/ngx_freebsd.h @@ -0,0 +1,24 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#ifndef _NGX_FREEBSD_H_INCLUDED_ +#define _NGX_FREEBSD_H_INCLUDED_ + + +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 int ngx_freebsd_net_inet_tcp_sendspace; +extern int ngx_freebsd_kern_ipc_zero_copy_send; + +extern ngx_uint_t ngx_freebsd_sendfile_nbytes_bug; +extern ngx_uint_t ngx_freebsd_use_tcp_nopush; + + +#endif /* _NGX_FREEBSD_H_INCLUDED_ */ diff --git a/src/os/unix/ngx_freebsd_config.h b/src/os/unix/ngx_freebsd_config.h new file mode 100644 --- /dev/null +++ b/src/os/unix/ngx_freebsd_config.h @@ -0,0 +1,124 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#ifndef _NGX_FREEBSD_CONFIG_H_INCLUDED_ +#define _NGX_FREEBSD_CONFIG_H_INCLUDED_ + + +#include +#include +#include +#include +#include /* offsetof() */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include /* FIONBIO */ +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include /* setproctitle() before 4.1 */ +#include +#include +#include /* TCP_NOPUSH */ + + +#if __FreeBSD_version < 400017 + +#include /* ALIGN() */ + +/* FreeBSD 3.x has no CMSG_SPACE() at all and has the broken CMSG_DATA() */ + +#undef CMSG_SPACE +#define CMSG_SPACE(l) (ALIGN(sizeof(struct cmsghdr)) + ALIGN(l)) + +#undef CMSG_DATA +#define CMSG_DATA(cmsg) ((u_char *)(cmsg) + ALIGN(sizeof(struct cmsghdr))) + +#endif + + +#include + + +#ifndef HAVE_SELECT +#define HAVE_SELECT 1 +#endif + + +#ifndef HAVE_POLL +#define HAVE_POLL 1 +#endif +#if (HAVE_POLL) +#include +#endif + + /* FreeBSD aio supported via kqueue */ + +#if (__FreeBSD__ == 4 && __FreeBSD_version >= 430000) \ + || __FreeBSD_version >= 500014 + +#ifndef HAVE_AIO +#define HAVE_AIO 1 +#endif + +#endif + +#if (HAVE_AIO) +#include +#endif + + +#if defined SO_ACCEPTFILTER && !defined HAVE_DEFERRED_ACCEPT +#define HAVE_DEFERRED_ACCEPT 1 +#endif + + +#if (HAVE_KQUEUE) +#include +#endif + + +#if (__FreeBSD_version < 430000 || __FreeBSD_version < 500012) + +pid_t rfork_thread(int flags, void *stack, int (*func)(void *arg), void *arg); + +#endif + +#ifndef IOV_MAX +#define IOV_MAX 1024 +#endif + + +#ifndef HAVE_INHERITED_NONBLOCK +#define HAVE_INHERITED_NONBLOCK 1 +#endif + + +#define ngx_setproctitle setproctitle + + +extern char *malloc_options; + + +#endif /* _NGX_FREEBSD_CONFIG_H_INCLUDED_ */ diff --git a/src/os/unix/ngx_freebsd_init.c b/src/os/unix/ngx_freebsd_init.c new file mode 100644 --- /dev/null +++ b/src/os/unix/ngx_freebsd_init.c @@ -0,0 +1,225 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include + + +/* FreeBSD 3.0 at least */ +char ngx_freebsd_kern_ostype[20]; +char ngx_freebsd_kern_osrelease[20]; +int ngx_freebsd_kern_osreldate; +int ngx_freebsd_hw_ncpu; +int ngx_freebsd_net_inet_tcp_sendspace; + +/* 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_os_io_t ngx_os_io = { + ngx_unix_recv, + ngx_readv_chain, + ngx_unix_send, +#if (HAVE_SENDFILE) + ngx_freebsd_sendfile_chain, + NGX_IO_SENDFILE +#else + ngx_writev_chain, + 0 +#endif +}; + + +typedef struct { + char *name; + int *value; + size_t size; + ngx_uint_t exists; +} sysctl_t; + + +sysctl_t sysctls[] = { + { "hw.ncpu", + &ngx_freebsd_hw_ncpu, + sizeof(int), 0 }, + + { "machdep.hlt_logical_cpus", + &ngx_freebsd_machdep_hlt_logical_cpus, + sizeof(int), 0 }, + + { "net.inet.tcp.sendspace", + &ngx_freebsd_net_inet_tcp_sendspace, + sizeof(int), 0 }, + + { "kern.ipc.zero_copy.send", + &ngx_freebsd_kern_ipc_zero_copy_send, + sizeof(int), 0 }, + + { NULL, NULL, 0, 0 } +}; + + +void ngx_debug_init() +{ +#if (NGX_DEBUG && !NGX_NO_DEBUG_MALLOC) + +#if __FreeBSD_version >= 500014 + _malloc_options = "J"; +#else + malloc_options = "J"; +#endif + +#endif +} + + +ngx_int_t ngx_os_init(ngx_log_t *log) +{ + int version; + size_t size; + ngx_err_t err; + ngx_uint_t i; + + size = sizeof(ngx_freebsd_kern_ostype); + if (sysctlbyname("kern.ostype", + ngx_freebsd_kern_ostype, &size, NULL, 0) == -1) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + "sysctlbyname(kern.ostype) failed"); + return NGX_ERROR; + } + + size = sizeof(ngx_freebsd_kern_osrelease); + if (sysctlbyname("kern.osrelease", + ngx_freebsd_kern_osrelease, &size, NULL, 0) == -1) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + "sysctlbyname(kern.osrelease) failed"); + return NGX_ERROR; + } + + + size = sizeof(int); + if (sysctlbyname("kern.osreldate", + &ngx_freebsd_kern_osreldate, &size, NULL, 0) == -1) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + "sysctlbyname(kern.osreldate) failed"); + return NGX_ERROR; + } + + version = ngx_freebsd_kern_osreldate; + + +#if (HAVE_SENDFILE) + + /* + * The determination of the sendfile() "nbytes bug" is complex enough. + * There are two sendfile() syscalls: a new #393 has no bug while + * an old #336 has the bug in some versions and has not in others. + * Besides libc_r wrapper also emulates the bug in some versions. + * There is no way to say exactly if syscall #336 in FreeBSD circa 4.6 + * has the bug. We use the algorithm that is correct at least for + * RELEASEs and for syscalls only (not libc_r wrapper). + * + * 4.6.1-RELEASE and below have the bug + * 4.6.2-RELEASE and above have the new syscall + * + * We detect the new sendfile() syscall available at the compile time + * to allow an old binary to run correctly on an updated FreeBSD system. + */ + +#if (__FreeBSD__ == 4 && __FreeBSD_version >= 460102) \ + || __FreeBSD_version == 460002 || __FreeBSD_version >= 500039 + + /* a new syscall without the bug */ + + ngx_freebsd_sendfile_nbytes_bug = 0; + +#else + + /* an old syscall that may have the bug */ + + ngx_freebsd_sendfile_nbytes_bug = 1; + +#endif + +#endif /* HAVE_SENDFILE */ + + + if ((version < 500000 && version >= 440003) || version >= 500017) { + ngx_freebsd_use_tcp_nopush = 1; + } + + + for (i = 0; sysctls[i].name; i++) { + *sysctls[i].value = 0; + 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; + } + +#if 0 + if (sysctls[i].value == &ngx_freebsd_machdep_hlt_logical_cpus) { + continue; + } +#endif + + ngx_log_error(NGX_LOG_ALERT, log, err, + "sysctlbyname(%s) failed", sysctls[i].name); + return NGX_ERROR; + } + + if (ngx_freebsd_machdep_hlt_logical_cpus) { + ngx_ncpu = ngx_freebsd_hw_ncpu / 2; + } else { + ngx_ncpu = ngx_freebsd_hw_ncpu; + } + + return ngx_posix_init(log); +} + + +void ngx_os_status(ngx_log_t *log) +{ + ngx_uint_t i; + + ngx_log_error(NGX_LOG_INFO, log, 0, "OS: %s %s", + ngx_freebsd_kern_ostype, ngx_freebsd_kern_osrelease); + +#ifdef __DragonFly_version + ngx_log_error(NGX_LOG_INFO, log, 0, + "kern.osreldate: %d, built on %d", + ngx_freebsd_kern_osreldate, __DragonFly_version); +#else + ngx_log_error(NGX_LOG_INFO, log, 0, + "kern.osreldate: %d, built on %d", + ngx_freebsd_kern_osreldate, __FreeBSD_version); +#endif + + for (i = 0; sysctls[i].name; i++) { + if (sysctls[i].exists) { + ngx_log_error(NGX_LOG_INFO, log, 0, "%s: %d", + sysctls[i].name, *sysctls[i].value); + } + } + + ngx_posix_status(log); +} diff --git a/src/os/unix/ngx_freebsd_rfork_thread.c b/src/os/unix/ngx_freebsd_rfork_thread.c new file mode 100644 --- /dev/null +++ b/src/os/unix/ngx_freebsd_rfork_thread.c @@ -0,0 +1,738 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include + +/* + * The threads implementation uses the rfork(RFPROC|RFTHREAD|RFMEM) syscall + * to create threads. All threads use the stacks of the same size mmap()ed + * below the main stack. Thus the current thread id is determinated via + * the stack pointer value. + * + * The mutex implementation uses the ngx_atomic_cmp_set() operation + * to acquire a mutex and the SysV semaphore to wait on a mutex and to wake up + * the waiting threads. The light mutex does not use semaphore, so after + * spinning in the lock the thread calls sched_yield(). However the light + * mutecies are intended to be used with the "trylock" operation only. + * The SysV semop() is a cheap syscall, particularly if it has little sembuf's + * and does not use SEM_UNDO. + * + * The condition variable implementation uses signal #64. The signal handler + * is SIG_IGN so the kill() is a cheap syscall. The thread waits a signal + * in kevent(). The use of the EVFILT_SIGNAL is safe since FreeBSD 4.7. + * + * This threads implementation currently works on i386 (486+) and amd64 + * platforms only. + */ + + +char *ngx_freebsd_kern_usrstack; +size_t ngx_thread_stack_size; + + +static size_t rz_size; +static size_t usable_stack_size; +static char *last_stack; + +static ngx_uint_t nthreads; +static ngx_uint_t max_threads; + +static ngx_uint_t nkeys; +static ngx_tid_t *tids; /* the threads tids array */ +void **ngx_tls; /* the threads tls's array */ + +/* the thread-safe libc errno */ + +static int errno0; /* the main thread's errno */ +static int *errnos; /* the threads errno's array */ + +int *__error() +{ + int tid; + + tid = ngx_gettid(); + + return tid ? &errnos[tid - 1] : &errno0; +} + + +/* + * __isthreaded enables the spinlocks in some libc functions, i.e. in malloc() + * and some other places. Nevertheless we protect our malloc()/free() calls + * by own mutex that is more efficient than the spinlock. + * + * _spinlock() is a weak referenced stub in src/lib/libc/gen/_spinlock_stub.c + * that does nothing. + */ + +extern int __isthreaded; + +void _spinlock(ngx_atomic_t *lock) +{ + ngx_int_t tries; + + tries = 0; + + for ( ;; ) { + + if (*lock) { + if (ngx_ncpu > 1 && tries++ < 1000) { + continue; + } + + sched_yield(); + tries = 0; + + } else { + if (ngx_atomic_cmp_set(lock, 0, 1)) { + return; + } + } + } +} + + +/* + * Before FreeBSD 5.1 _spinunlock() is a simple #define in + * src/lib/libc/include/spinlock.h that zeroes lock. + * + * Since FreeBSD 5.1 _spinunlock() is a weak referenced stub in + * src/lib/libc/gen/_spinlock_stub.c that does nothing. + */ + +#ifndef _spinunlock + +void _spinunlock(ngx_atomic_t *lock) +{ + *lock = 0; +} + +#endif + + +int ngx_create_thread(ngx_tid_t *tid, void* (*func)(void *arg), void *arg, + ngx_log_t *log) +{ + int id, err; + char *stack, *stack_top; + + if (nthreads >= max_threads) { + ngx_log_error(NGX_LOG_CRIT, log, 0, + "no more than %d threads can be created", max_threads); + return NGX_ERROR; + } + + last_stack -= ngx_thread_stack_size; + + stack = mmap(last_stack, usable_stack_size, PROT_READ|PROT_WRITE, + MAP_STACK, -1, 0); + + if (stack == MAP_FAILED) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + "mmap(" PTR_FMT ":" SIZE_T_FMT + ", MAP_STACK) thread stack failed", + last_stack, usable_stack_size); + return NGX_ERROR; + } + + if (stack != last_stack) { + ngx_log_error(NGX_LOG_ALERT, log, 0, "stack address was changed"); + } + + stack_top = stack + usable_stack_size; + + ngx_log_debug2(NGX_LOG_DEBUG_CORE, log, 0, + "thread stack: " PTR_FMT "-" PTR_FMT, stack, stack_top); + + ngx_set_errno(0); + + id = rfork_thread(RFPROC|RFTHREAD|RFMEM, stack_top, + (ngx_rfork_thread_func_pt) func, arg); + + err = ngx_errno; + + if (id == -1) { + ngx_log_error(NGX_LOG_ALERT, log, err, "rfork() failed"); + + } else { + *tid = id; + nthreads = (ngx_freebsd_kern_usrstack - stack_top) + / ngx_thread_stack_size; + tids[nthreads] = id; + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, log, 0, "rfork()ed thread: %d", id); + } + + return err; +} + + +ngx_int_t ngx_init_threads(int n, size_t size, ngx_cycle_t *cycle) +{ + char *red_zone, *zone; + size_t len; + ngx_int_t i; + struct sigaction sa; + + max_threads = n + 1; + + for (i = 0; i < n; i++) { + ngx_memzero(&sa, sizeof(struct sigaction)); + sa.sa_handler = SIG_IGN; + sigemptyset(&sa.sa_mask); + if (sigaction(NGX_CV_SIGNAL, &sa, NULL) == -1) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "sigaction(%d, SIG_IGN) failed", NGX_CV_SIGNAL); + return NGX_ERROR; + } + } + + len = sizeof(ngx_freebsd_kern_usrstack); + if (sysctlbyname("kern.usrstack", &ngx_freebsd_kern_usrstack, &len, + NULL, 0) == -1) + { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "sysctlbyname(kern.usrstack) failed"); + return NGX_ERROR; + } + + /* the main thread stack red zone */ + rz_size = ngx_pagesize; + red_zone = ngx_freebsd_kern_usrstack - (size + rz_size); + + ngx_log_debug2(NGX_LOG_DEBUG_CORE, cycle->log, 0, + "usrstack: " PTR_FMT " red zone: " PTR_FMT, + ngx_freebsd_kern_usrstack, red_zone); + + zone = mmap(red_zone, rz_size, PROT_NONE, MAP_ANON, -1, 0); + if (zone == MAP_FAILED) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "mmap(" PTR_FMT ":" SIZE_T_FMT + ", PROT_NONE, MAP_ANON) red zone failed", + red_zone, rz_size); + return NGX_ERROR; + } + + if (zone != red_zone) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, + "red zone address was changed"); + } + + /* create the threads errno's array */ + + if (!(errnos = ngx_calloc(n * sizeof(int), cycle->log))) { + return NGX_ERROR; + } + + /* create the threads tids array */ + + if (!(tids = ngx_calloc((n + 1) * sizeof(ngx_tid_t), cycle->log))) { + return NGX_ERROR; + } + + tids[0] = ngx_pid; + + /* create the threads tls's array */ + + ngx_tls = ngx_calloc(NGX_THREAD_KEYS_MAX * (n + 1) * sizeof(void *), + cycle->log); + if (ngx_tls == NULL) { + return NGX_ERROR; + } + + nthreads = 1; + + last_stack = zone + rz_size; + usable_stack_size = size; + ngx_thread_stack_size = size + rz_size; + + /* allow the spinlock in libc malloc() */ + __isthreaded = 1; + + ngx_threaded = 1; + + return NGX_OK; +} + + +ngx_tid_t ngx_thread_self() +{ + int tid; + ngx_tid_t pid; + + tid = ngx_gettid(); + + if (tids == NULL) { + return ngx_pid; + } + + return tids[tid]; +} + + +ngx_int_t ngx_thread_key_create(ngx_tls_key_t *key) +{ + if (nkeys >= NGX_THREAD_KEYS_MAX) { + return NGX_ENOMEM; + } + + *key = nkeys++; + + return 0; +} + + +ngx_int_t ngx_thread_set_tls(ngx_tls_key_t key, void *value) +{ + if (key >= NGX_THREAD_KEYS_MAX) { + return NGX_EINVAL; + } + + ngx_tls[key * NGX_THREAD_KEYS_MAX + ngx_gettid()] = value; + return 0; +} + + +ngx_mutex_t *ngx_mutex_init(ngx_log_t *log, uint flags) +{ + ngx_mutex_t *m; + union semun op; + + if (!(m = ngx_alloc(sizeof(ngx_mutex_t), log))) { + return NULL; + } + + m->lock = 0; + m->log = log; + + if (flags & NGX_MUTEX_LIGHT) { + m->semid = -1; + return m; + } + + m->semid = semget(IPC_PRIVATE, 1, SEM_R|SEM_A); + if (m->semid == -1) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, "semget() failed"); + return NULL; + } + + op.val = 0; + + if (semctl(m->semid, 0, SETVAL, op) == -1) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, "semctl(SETVAL) failed"); + + if (semctl(m->semid, 0, IPC_RMID) == -1) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + "semctl(IPC_RMID) failed"); + } + + return NULL; + } + + return m; +} + + +void ngx_mutex_destroy(ngx_mutex_t *m) +{ + if (semctl(m->semid, 0, IPC_RMID) == -1) { + ngx_log_error(NGX_LOG_ALERT, m->log, ngx_errno, + "semctl(IPC_RMID) failed"); + } + + ngx_free((void *) m); +} + + +ngx_int_t ngx_mutex_dolock(ngx_mutex_t *m, ngx_int_t try) +{ + uint32_t lock, new, old; + ngx_uint_t tries; + struct sembuf op; + + if (!ngx_threaded) { + return NGX_OK; + } + +#if (NGX_DEBUG) + if (try) { + ngx_log_debug2(NGX_LOG_DEBUG_MUTEX, m->log, 0, + "try lock mutex " PTR_FMT " lock:%X", m, m->lock); + } else { + ngx_log_debug2(NGX_LOG_DEBUG_MUTEX, m->log, 0, + "lock mutex " PTR_FMT " lock:%X", m, m->lock); + } +#endif + + old = m->lock; + tries = 0; + + for ( ;; ) { + if (old & NGX_MUTEX_LOCK_BUSY) { + + if (try) { + return NGX_AGAIN; + } + + if (ngx_freebsd_hw_ncpu > 1 && tries++ < 1000) { + + /* the spinlock is used only on the SMP system */ + + old = m->lock; + continue; + } + + if (m->semid == -1) { + sched_yield(); + + tries = 0; + old = m->lock; + continue; + } + + ngx_log_debug2(NGX_LOG_DEBUG_MUTEX, m->log, 0, + "mutex " PTR_FMT " lock:%X", m, m->lock); + + /* + * The mutex is locked so we increase a number + * of the threads that are waiting on the mutex + */ + + lock = old + 1; + + if ((lock & ~NGX_MUTEX_LOCK_BUSY) > nthreads) { + ngx_log_error(NGX_LOG_ALERT, m->log, ngx_errno, + "%d threads wait for mutex " PTR_FMT + ", while only %d threads are available", + lock & ~NGX_MUTEX_LOCK_BUSY, m, nthreads); + return NGX_ERROR; + } + + if (ngx_atomic_cmp_set(&m->lock, old, lock)) { + + ngx_log_debug2(NGX_LOG_DEBUG_MUTEX, m->log, 0, + "wait mutex " PTR_FMT " lock:%X", m, m->lock); + + /* + * The number of the waiting threads has been increased + * and we would wait on the SysV semaphore. + * A semaphore should wake up us more efficiently than + * a simple sched_yield() or usleep(). + */ + + op.sem_num = 0; + op.sem_op = -1; + op.sem_flg = 0; + + if (semop(m->semid, &op, 1) == -1) { + ngx_log_error(NGX_LOG_ALERT, m->log, ngx_errno, + "semop() failed while waiting " + "on mutex " PTR_FMT, m); + return NGX_ERROR; + } + + ngx_log_debug2(NGX_LOG_DEBUG_MUTEX, m->log, 0, + "mutex waked up " PTR_FMT " lock:%X", + m, m->lock); + + tries = 0; + old = m->lock; + continue; + } + + old = m->lock; + + } else { + lock = old | NGX_MUTEX_LOCK_BUSY; + + if (ngx_atomic_cmp_set(&m->lock, old, lock)) { + + /* we locked the mutex */ + + break; + } + + old = m->lock; + } + + if (tries++ > 1000) { + + ngx_log_debug1(NGX_LOG_DEBUG_MUTEX, m->log, 0, + "mutex " PTR_FMT " is contested", m); + + /* the mutex is probably contested so we are giving up now */ + + sched_yield(); + + tries = 0; + old = m->lock; + } + } + + ngx_log_debug2(NGX_LOG_DEBUG_MUTEX, m->log, 0, + "mutex " PTR_FMT " is locked, lock:%X", m, m->lock); + + return NGX_OK; +} + + +ngx_int_t ngx_mutex_unlock(ngx_mutex_t *m) +{ + uint32_t lock, new, old; + struct sembuf op; + + if (!ngx_threaded) { + return NGX_OK; + } + + old = m->lock; + + if (!(old & NGX_MUTEX_LOCK_BUSY)) { + ngx_log_error(NGX_LOG_ALERT, m->log, 0, + "trying to unlock the free mutex " PTR_FMT, m); + return NGX_ERROR; + } + + /* free the mutex */ + +#if 0 + ngx_log_debug2(NGX_LOG_DEBUG_MUTEX, m->log, 0, + "unlock mutex " PTR_FMT " lock:%X", m, old); +#endif + + for ( ;; ) { + lock = old & ~NGX_MUTEX_LOCK_BUSY; + + if (ngx_atomic_cmp_set(&m->lock, old, lock)) { + break; + } + + old = m->lock; + } + + if (m->semid == -1) { + ngx_log_debug1(NGX_LOG_DEBUG_MUTEX, m->log, 0, + "mutex " PTR_FMT " is unlocked", m); + + return NGX_OK; + } + + /* check whether we need to wake up a waiting thread */ + + old = m->lock; + + for ( ;; ) { + if (old & NGX_MUTEX_LOCK_BUSY) { + + /* the mutex is just locked by another thread */ + + break; + } + + if (old == 0) { + break; + } + + /* there are the waiting threads */ + + lock = old - 1; + + if (ngx_atomic_cmp_set(&m->lock, old, lock)) { + + /* wake up the thread that waits on semaphore */ + + ngx_log_debug1(NGX_LOG_DEBUG_MUTEX, m->log, 0, + "wake up mutex " PTR_FMT "", m); + + op.sem_num = 0; + op.sem_op = 1; + op.sem_flg = 0; + + if (semop(m->semid, &op, 1) == -1) { + ngx_log_error(NGX_LOG_ALERT, m->log, ngx_errno, + "semop() failed while waking up on mutex " + PTR_FMT, m); + return NGX_ERROR; + } + + break; + } + + old = m->lock; + } + + ngx_log_debug1(NGX_LOG_DEBUG_MUTEX, m->log, 0, + "mutex " PTR_FMT " is unlocked", m); + + return NGX_OK; +} + + +ngx_cond_t *ngx_cond_init(ngx_log_t *log) +{ + ngx_cond_t *cv; + + if (!(cv = ngx_alloc(sizeof(ngx_cond_t), log))) { + return NULL; + } + + cv->signo = NGX_CV_SIGNAL; + cv->tid = 0; + cv->log = log; + cv->kq = -1; + + return cv; +} + + +void ngx_cond_destroy(ngx_cond_t *cv) +{ + if (close(cv->kq) == -1) { + ngx_log_error(NGX_LOG_ALERT, cv->log, ngx_errno, + "kqueue close() failed"); + } + + ngx_free(cv); +} + + +ngx_int_t ngx_cond_wait(ngx_cond_t *cv, ngx_mutex_t *m) +{ + int n; + ngx_err_t err; + struct kevent kev; + struct timespec ts; + + if (cv->kq == -1) { + + /* + * We have to add the EVFILT_SIGNAL filter in the rfork()ed thread. + * Otherwise the thread would not get a signal event. + * + * However, we have not to open the kqueue in the thread, + * it is simply handy do it together. + */ + + cv->kq = kqueue(); + if (cv->kq == -1) { + ngx_log_error(NGX_LOG_ALERT, cv->log, ngx_errno, "kqueue() failed"); + return NGX_ERROR; + } + + ngx_log_debug2(NGX_LOG_DEBUG_CORE, cv->log, 0, + "cv kq:%d signo:%d", cv->kq, cv->signo); + + kev.ident = cv->signo; + kev.filter = EVFILT_SIGNAL; + kev.flags = EV_ADD; + kev.fflags = 0; + kev.data = 0; + kev.udata = NULL; + + ts.tv_sec = 0; + ts.tv_nsec = 0; + + if (kevent(cv->kq, &kev, 1, NULL, 0, &ts) == -1) { + ngx_log_error(NGX_LOG_ALERT, cv->log, ngx_errno, "kevent() failed"); + return NGX_ERROR; + } + } + + if (ngx_mutex_unlock(m) == NGX_ERROR) { + return NGX_ERROR; + } + + ngx_log_debug3(NGX_LOG_DEBUG_CORE, cv->log, 0, + "cv " PTR_FMT " wait, kq:%d, signo:%d", + cv, cv->kq, cv->signo); + + for ( ;; ) { + n = kevent(cv->kq, NULL, 0, &kev, 1, NULL); + + ngx_log_debug2(NGX_LOG_DEBUG_CORE, cv->log, 0, + "cv " PTR_FMT " kevent: %d", cv, n); + + if (n == -1) { + err = ngx_errno; + ngx_log_error((err == NGX_EINTR) ? NGX_LOG_INFO : NGX_LOG_ALERT, + cv->log, ngx_errno, + "kevent() failed while waiting condition variable " + PTR_FMT, cv); + + if (err == NGX_EINTR) { + break; + } + + return NGX_ERROR; + } + + if (n == 0) { + ngx_log_error(NGX_LOG_ALERT, cv->log, 0, + "kevent() returned no events " + "while waiting condition variable " PTR_FMT, + cv); + continue; + } + + if (kev.filter != EVFILT_SIGNAL) { + ngx_log_error(NGX_LOG_ALERT, cv->log, 0, + "kevent() returned unexpected events: %d " + "while waiting condition variable " PTR_FMT, + kev.filter, cv); + continue; + } + + if (kev.ident != (uintptr_t) cv->signo) { + ngx_log_error(NGX_LOG_ALERT, cv->log, 0, + "kevent() returned unexpected signal: %d ", + "while waiting condition variable " PTR_FMT, + kev.ident, cv); + continue; + } + + break; + } + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, cv->log, 0, + "cv " PTR_FMT " is waked up", cv); + + if (ngx_mutex_lock(m) == NGX_ERROR) { + return NGX_ERROR; + } + + return NGX_OK; +} + + +ngx_int_t ngx_cond_signal(ngx_cond_t *cv) +{ + ngx_err_t err; + + ngx_log_debug3(NGX_LOG_DEBUG_CORE, cv->log, 0, + "cv " PTR_FMT " to signal " PID_T_FMT " %d", + cv, cv->tid, cv->signo); + + if (kill(cv->tid, cv->signo) == -1) { + + err = ngx_errno; + + ngx_log_error(NGX_LOG_ALERT, cv->log, err, + "kill() failed while signaling condition variable " + PTR_FMT, cv); + + if (err == NGX_ESRCH) { + cv->tid = -1; + } + + return NGX_ERROR; + } + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, cv->log, 0, + "cv " PTR_FMT " is signaled", cv); + + return NGX_OK; +} diff --git a/src/os/unix/ngx_freebsd_rfork_thread.h b/src/os/unix/ngx_freebsd_rfork_thread.h new file mode 100644 --- /dev/null +++ b/src/os/unix/ngx_freebsd_rfork_thread.h @@ -0,0 +1,121 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#ifndef _NGX_FREEBSD_RFORK_THREAD_H_INCLUDED_ +#define _NGX_FREEBSD_RFORK_THREAD_H_INCLUDED_ + + +#include +#include +#include + +typedef pid_t ngx_tid_t; + +#undef ngx_log_pid +#define ngx_log_pid ngx_thread_self() +#define ngx_log_tid 0 + +#define TID_T_FMT PID_T_FMT + + +#define NGX_MUTEX_LIGHT 1 + +#define NGX_MUTEX_LOCK_BUSY 0x80000000 + +typedef volatile struct { + ngx_atomic_t lock; + ngx_log_t *log; + int semid; +} ngx_mutex_t; + + +#define NGX_CV_SIGNAL 64 + +typedef struct { + int signo; + int kq; + ngx_tid_t tid; + ngx_log_t *log; +} ngx_cond_t; + + +#define ngx_thread_sigmask(how, set, oset) \ + (sigprocmask(how, set, oset) == -1) ? ngx_errno : 0 + +#define ngx_thread_sigmask_n "sigprocmask()" + +#define ngx_thread_join(t, p) + +#define ngx_setthrtitle(n) setproctitle(n) + + +extern char *ngx_freebsd_kern_usrstack; +extern size_t ngx_thread_stack_size; + + +static inline int ngx_gettid() +{ + char *sp; + + if (ngx_thread_stack_size == 0) { + return 0; + } + +#if ( __i386__ ) + + __asm__ volatile ("mov %%esp, %0" : "=q" (sp)); + +#elif ( __amd64__ ) + + __asm__ volatile ("mov %%rsp, %0" : "=q" (sp)); + +#else + +#error "rfork()ed threads are not supported on this platform" + +#endif + + return (ngx_freebsd_kern_usrstack - sp) / ngx_thread_stack_size; +} + + +ngx_tid_t ngx_thread_self(); + + +typedef ngx_uint_t ngx_tls_key_t; + +#define NGX_THREAD_KEYS_MAX 16 + +extern void **ngx_tls; + +ngx_int_t ngx_thread_key_create(ngx_tls_key_t *key); +#define ngx_thread_key_create_n "the tls key creation" + +ngx_int_t ngx_thread_set_tls(ngx_tls_key_t key, void *value); +#define ngx_thread_set_tls_n "the tls key setting" + + +static void *ngx_thread_get_tls(ngx_tls_key_t key) +{ + if (key >= NGX_THREAD_KEYS_MAX) { + return NULL; + } + + return ngx_tls[key * NGX_THREAD_KEYS_MAX + ngx_gettid()]; +} + + +#define ngx_mutex_trylock(m) ngx_mutex_dolock(m, 1) +#define ngx_mutex_lock(m) ngx_mutex_dolock(m, 0) +ngx_int_t ngx_mutex_dolock(ngx_mutex_t *m, ngx_int_t try); +ngx_int_t ngx_mutex_unlock(ngx_mutex_t *m); + + +typedef int (*ngx_rfork_thread_func_pt)(void *arg); + + + +#endif /* _NGX_FREEBSD_RFORK_THREAD_H_INCLUDED_ */ diff --git a/src/os/unix/ngx_freebsd_sendfile_chain.c b/src/os/unix/ngx_freebsd_sendfile_chain.c new file mode 100644 --- /dev/null +++ b/src/os/unix/ngx_freebsd_sendfile_chain.c @@ -0,0 +1,380 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include + + +/* + * Although FreeBSD sendfile() allows to pass a header and a trailer + * it can not send a header with a part of the file in one packet until + * FreeBSD 5.3. Besides over the fast ethernet connection sendfile() + * may send the partially filled packets, i.e. the 8 file pages may be sent + * as the 11 full 1460-bytes packets, then one incomplete 324-bytes packet, + * and then again the 11 full 1460-bytes packets. + * + * So we use the TCP_NOPUSH option (similar to Linux's TCP_CORK) + * to postpone the sending - it not only sends a header and the first part + * of the file in one packet but also sends the file pages in the full packets. + * + * But until FreeBSD 4.5 the turning TCP_NOPUSH off does not flush a pending + * data that less than MSS so that data may be sent with 5 second delay. + * So we do not use TCP_NOPUSH on FreeBSD prior to 4.5 although it can be used + * for non-keepalive HTTP connections. + */ + + +#define NGX_HEADERS 8 +#define NGX_TRAILERS 4 + + +ngx_chain_t *ngx_freebsd_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, + off_t limit) +{ + int rc; + u_char *prev; + off_t fprev, sent, send, sprev, aligned; + size_t hsize, fsize; + ssize_t size; + ngx_uint_t eintr, eagain, 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 (HAVE_KQUEUE) + + if ((ngx_event_flags & NGX_HAVE_KQUEUE_EVENT) && wev->pending_eof) { + ngx_log_error(NGX_LOG_INFO, c->log, wev->kq_errno, + "kevent() reported about an closed connection"); + + wev->error = 1; + return NGX_CHAIN_ERROR; + } + +#endif + + send = 0; + eagain = 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; + fsize = 0; + hsize = 0; + eintr = 0; + complete = 0; + sprev = 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; + + } else { + if (!(iov = ngx_array_push(&header))) { + return NGX_CHAIN_ERROR; + } + + iov->iov_base = (void *) cl->buf->pos; + iov->iov_len = size; + } + + prev = cl->buf->pos + size; + hsize += size; + send += size; + } + + /* get the file buf */ + + if (cl && cl->buf->in_file && send < limit) { + file = cl->buf; + + /* coalesce the neighbouring file bufs */ + + do { + size = (size_t) (cl->buf->file_last - cl->buf->file_pos); + + if (send + size > limit) { + size = limit - send; + + aligned = (cl->buf->file_pos + size + ngx_pagesize - 1) + & ~(ngx_pagesize - 1); + + if (aligned <= cl->buf->file_last) { + size = aligned - cl->buf->file_pos; + } + } + + fsize += 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) { + /* create the tailer iovec and coalesce the neighbouring bufs */ + + prev = NULL; + iov = NULL; + + for (/* void */; + 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; + + } else { + if (!(iov = ngx_array_push(&trailer))) { + return NGX_CHAIN_ERROR; + } + + iov->iov_base = (void *) cl->buf->pos; + iov->iov_len = size; + } + + prev = cl->buf->pos + size; + send += size; + } + } + + if (file) { + + if (ngx_freebsd_use_tcp_nopush + && c->tcp_nopush == NGX_TCP_NOPUSH_UNSET) + { + + if (ngx_tcp_nopush(c->fd) == NGX_ERROR) { + err = ngx_errno; + + /* + * there is a tiny chance to be interrupted, however + * we continue a processing without the TCP_NOPUSH + */ + + if (err != NGX_EINTR) { + wev->error = 1; + ngx_connection_error(c, err, + ngx_tcp_nopush_n " failed"); + return NGX_CHAIN_ERROR; + } + + } else { + c->tcp_nopush = NGX_TCP_NOPUSH_SET; + + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, + "tcp_nopush"); + } + } + + hdtr.headers = (struct iovec *) header.elts; + hdtr.hdr_cnt = header.nelts; + hdtr.trailers = (struct iovec *) trailer.elts; + hdtr.trl_cnt = trailer.nelts; + + /* + * the "nbytes bug" of the old sendfile() syscall: + * http://www.freebsd.org/cgi/query-pr.cgi?pr=33771 + */ + + if (ngx_freebsd_sendfile_nbytes_bug == 0) { + hsize = 0; + } + + sent = 0; + + rc = sendfile(file->file->fd, c->fd, file->file_pos, + fsize + hsize, &hdtr, &sent, 0); + + if (rc == -1) { + err = ngx_errno; + + if (err == NGX_EAGAIN || err == NGX_EINTR) { + if (err == NGX_EINTR) { + eintr = 1; + + } else { + eagain = 1; + } + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, err, + "sendfile() sent only " OFF_T_FMT " bytes", + sent); + + } else { + wev->error = 1; + ngx_connection_error(c, err, "sendfile() failed"); + return NGX_CHAIN_ERROR; + } + } + + ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0, + "sendfile: %d, @" OFF_T_FMT " " OFF_T_FMT ":%d", + rc, file->file_pos, sent, fsize + hsize); + + } else { + rc = writev(c->fd, header.elts, header.nelts); + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, + "writev: %d of " SIZE_T_FMT, rc, hsize); + + if (rc == -1) { + err = ngx_errno; + + if (err == NGX_EAGAIN || err == NGX_EINTR) { + if (err == NGX_EINTR) { + eintr = 1; + } + + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err, + "writev() not ready"); + + } else { + wev->error = 1; + ngx_connection_error(c, err, "writev() failed"); + return NGX_CHAIN_ERROR; + } + } + + sent = rc > 0 ? rc : 0; + } + + if (send - sprev == 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 += sent; + } + + if (cl->buf->in_file) { + cl->buf->file_pos += sent; + } + + break; + } + + if (eagain) { + + /* + * sendfile() can return EAGAIN even if it has sent + * a whole file part but the successive sendfile() call would + * return EAGAIN right away and would not send anything. + * We use it as a hint. + */ + + wev->ready = 0; + return cl; + } + + if (eintr) { + continue; + } + + if (!complete) { + wev->ready = 0; + return cl; + } + + if (send >= limit || cl == NULL) { + return cl; + } + + in = cl; + } +} diff --git a/src/os/unix/ngx_linux.h b/src/os/unix/ngx_linux.h new file mode 100644 --- /dev/null +++ b/src/os/unix/ngx_linux.h @@ -0,0 +1,17 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#ifndef _NGX_LINUX_H_INCLUDED_ +#define _NGX_LINUX_H_INCLUDED_ + + +ngx_chain_t *ngx_linux_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, + off_t limit); + +extern int ngx_linux_rtsig_max; + + +#endif /* _NGX_LINUX_H_INCLUDED_ */ diff --git a/src/os/unix/ngx_linux_config.h b/src/os/unix/ngx_linux_config.h new file mode 100644 --- /dev/null +++ b/src/os/unix/ngx_linux_config.h @@ -0,0 +1,101 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#ifndef _NGX_LINUX_CONFIG_H_INCLUDED_ +#define _NGX_LINUX_CONFIG_H_INCLUDED_ + + +#define _GNU_SOURCE /* pread(), pwrite(), gethostname() */ + +#define _FILE_OFFSET_BITS 64 +#define _LARGEFILE_SOURCE + + +#include +#include +#include +#include +#include /* offsetof() */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include /* tzset() */ +#include +#include +#include /* TCP_CORK */ + + +#include + + +#if (HAVE_PRCTL) +#include +#endif + +#if (HAVE_SENDFILE64) +#include +#else +extern ssize_t sendfile(int s, int fd, int32_t *offset, size_t size); +#endif + + + +#ifndef HAVE_SELECT +#define HAVE_SELECT 1 +#endif + + +#ifndef HAVE_POLL +#define HAVE_POLL 1 +#endif +#if (HAVE_POLL) +#include +#endif + +#if (HAVE_EPOLL) +#include +#endif /* HAVE_EPOLL */ + + +#if defined TCP_DEFER_ACCEPT && !defined HAVE_DEFERRED_ACCEPT +#define HAVE_DEFERRED_ACCEPT 1 +#endif + + +#ifndef HAVE_INHERITED_NONBLOCK +#define HAVE_INHERITED_NONBLOCK 0 +#endif + + +#ifndef HAVE_SELECT_CHANGE_TIMEOUT +#define HAVE_SELECT_CHANGE_TIMEOUT 1 +#endif + + +#define ngx_setproctitle(title) + + +#endif /* _NGX_LINUX_CONFIG_H_INCLUDED_ */ diff --git a/src/os/unix/ngx_linux_init.c b/src/os/unix/ngx_linux_init.c new file mode 100644 --- /dev/null +++ b/src/os/unix/ngx_linux_init.c @@ -0,0 +1,80 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include + + +char ngx_linux_kern_ostype[50]; +char ngx_linux_kern_osrelease[20]; + +int ngx_linux_rtsig_max; + + +ngx_os_io_t ngx_os_io = { + ngx_unix_recv, + ngx_readv_chain, + ngx_unix_send, +#if (HAVE_SENDFILE) + ngx_linux_sendfile_chain, + NGX_IO_SENDFILE +#else + ngx_writev_chain, + 0 +#endif +}; + + +ngx_int_t ngx_os_init(ngx_log_t *log) +{ + int name[2], len; + + name[0] = CTL_KERN; + name[1] = KERN_OSTYPE; + len = sizeof(ngx_linux_kern_ostype); + if (sysctl(name, sizeof(name), ngx_linux_kern_ostype, &len, NULL, 0) + == -1) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + "sysctl(KERN_OSTYPE) failed"); + return NGX_ERROR; + } + + name[0] = CTL_KERN; + name[1] = KERN_OSRELEASE; + len = sizeof(ngx_linux_kern_osrelease); + if (sysctl(name, sizeof(name), ngx_linux_kern_osrelease, &len, NULL, 0) + == -1) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + "sysctl(KERN_OSRELEASE) failed"); + return NGX_ERROR; + } + + + name[0] = CTL_KERN; + name[1] = KERN_RTSIGMAX; + len = sizeof(ngx_linux_rtsig_max); + if (sysctl(name, sizeof(name), &ngx_linux_rtsig_max, &len, NULL, 0) == -1) { + ngx_log_error(NGX_LOG_INFO, log, ngx_errno, + "sysctl(KERN_RTSIGMAX) failed"); + ngx_linux_rtsig_max = 0; + + } + + + return ngx_posix_init(log); +} + + +void ngx_os_status(ngx_log_t *log) +{ + ngx_log_error(NGX_LOG_INFO, log, 0, "OS: %s %s", + ngx_linux_kern_ostype, ngx_linux_kern_osrelease); + + ngx_log_error(NGX_LOG_INFO, log, 0, "sysctl(KERN_RTSIGMAX): %d", + ngx_linux_rtsig_max); + + ngx_posix_status(log); +} diff --git a/src/os/unix/ngx_linux_sendfile_chain.c b/src/os/unix/ngx_linux_sendfile_chain.c new file mode 100644 --- /dev/null +++ b/src/os/unix/ngx_linux_sendfile_chain.c @@ -0,0 +1,285 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include + + +/* + * On Linux up to 2.4.21 sendfile() (syscall #187) works with 32-bit + * offsets only and the including breaks the compiling + * if off_t is 64 bit wide. So we use own sendfile() definition where offset + * parameter is int32_t and use sendfile() with the file parts below 2G. + * + * Linux 2.4.21 has a new sendfile64() syscall #239. + */ + + +#define NGX_HEADERS 8 + + +ngx_chain_t *ngx_linux_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, + off_t limit) +{ + int rc; + u_char *prev; + off_t fprev, send, sprev, aligned; + size_t fsize; + ssize_t size, sent; + ngx_uint_t eintr, complete; + ngx_err_t err; + ngx_buf_t *file; + ngx_array_t header; + ngx_event_t *wev; + ngx_chain_t *cl; + struct iovec *iov, headers[NGX_HEADERS]; +#if (HAVE_SENDFILE64) + off_t offset; +#else + int32_t offset; +#endif + + wev = c->write; + + if (!wev->ready) { + return in; + } + + send = 0; + + header.elts = headers; + header.size = sizeof(struct iovec); + header.nalloc = NGX_HEADERS; + header.pool = c->pool; + + for ( ;; ) { + file = NULL; + fsize = 0; + eintr = 0; + complete = 0; + sprev = send; + + header.nelts = 0; + + prev = NULL; + iov = NULL; + + /* create the iovec and coalesce the neighbouring bufs */ + + 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; + + } else { + if (!(iov = ngx_array_push(&header))) { + return NGX_CHAIN_ERROR; + } + + iov->iov_base = (void *) cl->buf->pos; + iov->iov_len = size; + } + + prev = cl->buf->pos + size; + send += size; + } + + /* set TCP_CORK if there is a header before a file */ + + if (c->tcp_nopush == NGX_TCP_NOPUSH_UNSET + && header.nelts != 0 + && cl + && cl->buf->in_file) + { + if (ngx_tcp_nopush(c->fd) == NGX_ERROR) { + err = ngx_errno; + + /* + * there is a tiny chance to be interrupted, however + * we continue a processing without the TCP_CORK + */ + + if (err != NGX_EINTR) { + wev->error = 1; + ngx_connection_error(c, err, ngx_tcp_nopush_n " failed"); + return NGX_CHAIN_ERROR; + } + + } else { + c->tcp_nopush = NGX_TCP_NOPUSH_SET; + + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, + "tcp_nopush"); + } + } + + /* get the file buf */ + + if (header.nelts == 0 && cl && cl->buf->in_file && send < limit) { + file = cl->buf; + + /* coalesce the neighbouring file bufs */ + + do { + size = (size_t) (cl->buf->file_last - cl->buf->file_pos); + + if (send + size > limit) { + size = limit - send; + + aligned = (cl->buf->file_pos + size + ngx_pagesize - 1) + & ~(ngx_pagesize - 1); + + if (aligned <= cl->buf->file_last) { + size = aligned - cl->buf->file_pos; + } + } + + fsize += 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) { +#if (HAVE_SENDFILE64) + offset = file->file_pos; +#else + offset = (int32_t) file->file_pos; +#endif + rc = sendfile(c->fd, file->file->fd, &offset, fsize); + + if (rc == -1) { + err = ngx_errno; + + if (err == NGX_EAGAIN || err == NGX_EINTR) { + if (err == NGX_EINTR) { + eintr = 1; + } + + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err, + "sendfile() is not ready"); + + } else { + wev->error = 1; + ngx_connection_error(c, err, "sendfile() failed"); + return NGX_CHAIN_ERROR; + } + } + + sent = rc > 0 ? rc : 0; + + ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0, + "sendfile: %d, @" OFF_T_FMT " %d:%d", + rc, file->file_pos, sent, fsize); + + } else { + rc = writev(c->fd, header.elts, header.nelts); + + if (rc == -1) { + err = ngx_errno; + + if (err == NGX_EAGAIN || err == NGX_EINTR) { + if (err == NGX_EINTR) { + eintr = 1; + } + + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err, + "writev() not ready"); + + } else { + wev->error = 1; + ngx_connection_error(c, err, "writev() failed"); + return NGX_CHAIN_ERROR; + } + } + + sent = rc > 0 ? rc : 0; + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "writev: %d", sent); + } + + if (send - sprev == 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 += 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 --git a/src/os/unix/ngx_os.h b/src/os/unix/ngx_os.h new file mode 100644 --- /dev/null +++ b/src/os/unix/ngx_os.h @@ -0,0 +1,84 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#ifndef _NGX_OS_H_INCLUDED_ +#define _NGX_OS_H_INCLUDED_ + + +#include +#include + + +#define NGX_IO_SENDFILE 1 +#define NGX_IO_ZEROCOPY 2 + +#if (HAVE_SENDFILE) +#define NGX_HAVE_SENDFILE NGX_IO_SENDFILE +#else +#define NGX_HAVE_SENDFILE 0 +#endif + +#if (HAVE_ZEROCOPY) +#define NGX_HAVE_ZEROCOPY NGX_IO_ZEROCOPY +#else +#define NGX_HAVE_ZEROCOPY 0 +#endif + + +typedef ssize_t (*ngx_recv_pt)(ngx_connection_t *c, u_char *buf, size_t size); +typedef ssize_t (*ngx_recv_chain_pt)(ngx_connection_t *c, ngx_chain_t *in); +typedef ssize_t (*ngx_send_pt)(ngx_connection_t *c, u_char *buf, size_t size); +typedef ngx_chain_t *(*ngx_send_chain_pt)(ngx_connection_t *c, ngx_chain_t *in, + off_t limit); + +typedef struct { + ngx_recv_pt recv; + ngx_recv_chain_pt recv_chain; + ngx_send_pt send; + ngx_send_chain_pt send_chain; + ngx_uint_t flags; +} ngx_os_io_t; + + +void ngx_debug_init(); +ngx_int_t ngx_os_init(ngx_log_t *log); +void ngx_os_status(ngx_log_t *log); +ngx_int_t ngx_daemon(ngx_log_t *log); +ngx_int_t ngx_posix_init(ngx_log_t *log); +void ngx_posix_status(ngx_log_t *log); +ngx_int_t ngx_posix_post_conf_init(ngx_log_t *log); + + +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_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); + + +extern ngx_os_io_t ngx_os_io; +extern ngx_int_t ngx_ncpu; +extern ngx_int_t ngx_max_sockets; +extern ngx_int_t ngx_inherited_nonblocking; + +#define ngx_stderr_fileno STDERR_FILENO + +#ifdef __FreeBSD__ +#include +#endif + + +#ifdef __linux__ +#include +#endif + + +#ifdef SOLARIS +#include +#endif + + +#endif /* _NGX_OS_H_INCLUDED_ */ diff --git a/src/os/unix/ngx_posix_config.h b/src/os/unix/ngx_posix_config.h new file mode 100644 --- /dev/null +++ b/src/os/unix/ngx_posix_config.h @@ -0,0 +1,62 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#ifndef _NGX_POSIX_CONFIG_H_INCLUDED_ +#define _NGX_POSIX_CONFIG_H_INCLUDED_ + + +#include +#include +#include +#include +#include /* offsetof() */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include /* FIONBIO */ +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + + +#ifndef HAVE_SELECT +#define HAVE_SELECT 1 +#endif + + +#ifndef HAVE_POLL +#define HAVE_POLL 1 +#endif +#if (HAVE_POLL) +#include +#endif + + +#define ngx_setproctitle(title) + + +#define NGX_POSIX_IO 1 + + +#endif /* _NGX_POSIX_CONFIG_H_INCLUDED_ */ diff --git a/src/os/unix/ngx_posix_init.c b/src/os/unix/ngx_posix_init.c new file mode 100644 --- /dev/null +++ b/src/os/unix/ngx_posix_init.c @@ -0,0 +1,299 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include + + +ngx_int_t ngx_ncpu; +ngx_int_t ngx_max_sockets; +ngx_int_t ngx_inherited_nonblocking; + + +struct rlimit rlmt; + + +#if (NGX_POSIX_IO) + +ngx_os_io_t ngx_os_io = { + ngx_unix_recv, + ngx_readv_chain, + NULL, + ngx_writev_chain, + 0 +}; + + +int ngx_os_init(ngx_log_t *log) +{ + return ngx_posix_init(log); +} + + +#endif + + +void ngx_signal_handler(int signo); + + +typedef struct { + int signo; + char *signame; + void (*handler)(int signo); +} ngx_signal_t; + + +ngx_signal_t signals[] = { + { ngx_signal_value(NGX_RECONFIGURE_SIGNAL), + "SIG" ngx_value(NGX_RECONFIGURE_SIGNAL), + ngx_signal_handler }, + + { ngx_signal_value(NGX_REOPEN_SIGNAL), + "SIG" ngx_value(NGX_REOPEN_SIGNAL), + 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), + ngx_signal_handler }, + + { ngx_signal_value(NGX_SHUTDOWN_SIGNAL), + "SIG" ngx_value(NGX_SHUTDOWN_SIGNAL), + ngx_signal_handler }, + + { ngx_signal_value(NGX_CHANGEBIN_SIGNAL), + "SIG" ngx_value(NGX_CHANGEBIN_SIGNAL), + ngx_signal_handler }, + + { SIGALRM, "SIGALRM", ngx_signal_handler }, + + { SIGINT, "SIGINT", ngx_signal_handler }, + + { SIGIO, "SIGIO", ngx_signal_handler }, + + { SIGCHLD, "SIGCHLD", ngx_signal_handler }, + + { SIGPIPE, "SIGPIPE, SIG_IGN", SIG_IGN }, + + { 0, NULL, NULL } +}; + + +ngx_int_t ngx_posix_init(ngx_log_t *log) +{ + ngx_signal_t *sig; + struct sigaction sa; + + ngx_pagesize = getpagesize(); + + if (ngx_ncpu == 0) { + ngx_ncpu = 1; + } + + for (sig = signals; sig->signo != 0; sig++) { + ngx_memzero(&sa, sizeof(struct sigaction)); + sa.sa_handler = sig->handler; + sigemptyset(&sa.sa_mask); + if (sigaction(sig->signo, &sa, NULL) == -1) { + ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, + "sigaction(%s) failed", sig->signame); + return NGX_ERROR; + } + } + + if (getrlimit(RLIMIT_NOFILE, &rlmt) == -1) { + ngx_log_error(NGX_LOG_ALERT, log, errno, + "getrlimit(RLIMIT_NOFILE) failed)"); + return NGX_ERROR; + } + + ngx_max_sockets = rlmt.rlim_cur; + +#if (HAVE_INHERITED_NONBLOCK) + ngx_inherited_nonblocking = 1; +#else + ngx_inherited_nonblocking = 0; +#endif + + return NGX_OK; +} + + +void ngx_posix_status(ngx_log_t *log) +{ + ngx_log_error(NGX_LOG_INFO, log, 0, + "getrlimit(RLIMIT_NOFILE): " RLIM_T_FMT ":" RLIM_T_FMT, + rlmt.rlim_cur, rlmt.rlim_max); +} + + +void ngx_signal_handler(int signo) +{ + char *action; + struct timeval tv; + ngx_int_t ignore; + ngx_err_t err; + ngx_signal_t *sig; + + ignore = 0; + + err = ngx_errno; + + for (sig = signals; sig->signo != 0; sig++) { + if (sig->signo == signo) { + break; + } + } + + ngx_gettimeofday(&tv); + ngx_time_update(tv.tv_sec); + + action = ""; + + switch (ngx_process) { + + case NGX_PROCESS_MASTER: + case NGX_PROCESS_SINGLE: + switch (signo) { + + case ngx_signal_value(NGX_SHUTDOWN_SIGNAL): + ngx_quit = 1; + action = ", shutting down"; + break; + + case ngx_signal_value(NGX_TERMINATE_SIGNAL): + case SIGINT: + ngx_terminate = 1; + action = ", exiting"; + break; + + case ngx_signal_value(NGX_NOACCEPT_SIGNAL): + ngx_noaccept = 1; + action = ", stop the accepting connections"; + break; + + case ngx_signal_value(NGX_RECONFIGURE_SIGNAL): + ngx_reconfigure = 1; + action = ", reconfiguring"; + break; + + case ngx_signal_value(NGX_REOPEN_SIGNAL): + ngx_reopen = 1; + action = ", reopen logs"; + break; + + case ngx_signal_value(NGX_CHANGEBIN_SIGNAL): + if (getppid() > 1 || ngx_new_binary > 0) { + + /* + * Ignore the signal in the new binary if its parent is + * not the init process, i.e. the old binary's process + * is still running. Or ingore the signal in the old binary's + * process if the new binary's process is already running. + */ + + action = ", ignoring"; + ignore = 1; + break; + } + + ngx_change_binary = 1; + action = ", changing binary"; + break; + + case SIGALRM: + if (!ngx_terminate) { + ngx_timer = 1; + action = ", shutting down old worker processes"; + } + + break; + + case SIGIO: + ngx_sigio = 1; + break; + + case SIGCHLD: + ngx_reap = 1; + break; + } + + break; + + case NGX_PROCESS_WORKER: + switch (signo) { + + case ngx_signal_value(NGX_SHUTDOWN_SIGNAL): + ngx_quit = 1; + action = ", shutting down"; + break; + + case ngx_signal_value(NGX_TERMINATE_SIGNAL): + case SIGINT: + ngx_terminate = 1; + action = ", exiting"; + break; + + case ngx_signal_value(NGX_REOPEN_SIGNAL): + ngx_reopen = 1; + action = ", reopen logs"; + break; + + case ngx_signal_value(NGX_RECONFIGURE_SIGNAL): + case ngx_signal_value(NGX_NOACCEPT_SIGNAL): + case ngx_signal_value(NGX_CHANGEBIN_SIGNAL): + case SIGIO: + action = ", ignoring"; + break; + } + + break; + } + + ngx_log_error(NGX_LOG_INFO, ngx_cycle->log, 0, + "signal %d (%s) received%s", signo, sig->signame, action); + + if (ignore) { + ngx_log_error(NGX_LOG_CRIT, ngx_cycle->log, 0, + "the changing binary signal is ignored: " + "you should shutdown or terminate " + "before either old or new binary's process"); + } + + if (signo == SIGCHLD) { + ngx_process_get_status(); + } + + ngx_set_errno(err); +} + + +int ngx_posix_post_conf_init(ngx_log_t *log) +{ + ngx_fd_t pp[2]; + + if (pipe(pp) == -1) { + ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "pipe() failed"); + return NGX_ERROR; + } + + if (dup2(pp[1], STDERR_FILENO) == -1) { + ngx_log_error(NGX_LOG_EMERG, log, errno, "dup2(STDERR) failed"); + return NGX_ERROR; + } + + if (pp[1] > STDERR_FILENO) { + if (close(pp[1]) == -1) { + ngx_log_error(NGX_LOG_EMERG, log, errno, "close() failed"); + return NGX_ERROR; + } + } + + return NGX_OK; +} diff --git a/src/os/unix/ngx_process.c b/src/os/unix/ngx_process.c new file mode 100644 --- /dev/null +++ b/src/os/unix/ngx_process.c @@ -0,0 +1,283 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include +#include + + +static void ngx_execute_proc(ngx_cycle_t *cycle, void *data); + +ngx_int_t ngx_process_slot; +ngx_socket_t ngx_channel; +ngx_int_t ngx_last_process; +ngx_process_t ngx_processes[NGX_MAX_PROCESSES]; + + +ngx_pid_t ngx_spawn_process(ngx_cycle_t *cycle, + ngx_spawn_proc_pt proc, void *data, + char *name, ngx_int_t respawn) +{ + u_long on; + ngx_pid_t pid; + ngx_int_t s; + + if (respawn >= 0) { + s = respawn; + + } else { + for (s = 0; s < ngx_last_process; s++) { + if (ngx_processes[s].pid == -1) { + break; + } + } + + if (s == NGX_MAX_PROCESSES) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, + "no more than %d processes can be spawned", + NGX_MAX_PROCESSES); + return NGX_ERROR; + } + } + + + if (respawn != NGX_PROCESS_DETACHED) { + + /* Solaris 9 still has no AF_LOCAL */ + + if (socketpair(AF_UNIX, SOCK_STREAM, 0, ngx_processes[s].channel) == -1) + { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "socketpair() failed while spawning \"%s\"", name); + return NGX_ERROR; + } + + ngx_log_debug2(NGX_LOG_DEBUG_CORE, cycle->log, 0, + "channel %d:%d", + ngx_processes[s].channel[0], + ngx_processes[s].channel[1]); + + if (ngx_nonblocking(ngx_processes[s].channel[0]) == -1) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + ngx_nonblocking_n " failed while spawning \"%s\"", + name); + ngx_close_channel(ngx_processes[s].channel, cycle->log); + return NGX_ERROR; + } + + if (ngx_nonblocking(ngx_processes[s].channel[1]) == -1) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + ngx_nonblocking_n " failed while spawning \"%s\"", + name); + ngx_close_channel(ngx_processes[s].channel, cycle->log); + return NGX_ERROR; + } + + on = 1; + if (ioctl(ngx_processes[s].channel[0], FIOASYNC, &on) == -1) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "ioctl(FIOASYNC) failed while spawning \"%s\"", name); + ngx_close_channel(ngx_processes[s].channel, cycle->log); + return NGX_ERROR; + } + + if (fcntl(ngx_processes[s].channel[0], F_SETOWN, ngx_pid) == -1) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "fcntl(F_SETOWN) failed while spawning \"%s\"", name); + ngx_close_channel(ngx_processes[s].channel, cycle->log); + return NGX_ERROR; + } + + if (fcntl(ngx_processes[s].channel[0], F_SETFD, FD_CLOEXEC) == -1) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "fcntl(FD_CLOEXEC) failed while spawning \"%s\"", + name); + ngx_close_channel(ngx_processes[s].channel, cycle->log); + return NGX_ERROR; + } + + if (fcntl(ngx_processes[s].channel[1], F_SETFD, FD_CLOEXEC) == -1) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "fcntl(FD_CLOEXEC) failed while spawning \"%s\"", + name); + ngx_close_channel(ngx_processes[s].channel, cycle->log); + return NGX_ERROR; + } + + ngx_channel = ngx_processes[s].channel[1]; + + } else { + ngx_processes[s].channel[0] = -1; + ngx_processes[s].channel[1] = -1; + } + + ngx_process_slot = s; + + + pid = fork(); + + switch (pid) { + + case -1: + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "fork() failed while spawning \"%s\"", name); + ngx_close_channel(ngx_processes[s].channel, cycle->log); + return NGX_ERROR; + + case 0: + ngx_pid = ngx_getpid(); + proc(cycle, data); + break; + + default: + break; + } + + ngx_log_debug2(NGX_LOG_DEBUG_CORE, cycle->log, 0, + "spawn %s: " PID_T_FMT, name, pid); + + ngx_processes[s].pid = pid; + ngx_processes[s].exited = 0; + + if (respawn >= 0) { + return pid; + } + + ngx_processes[s].proc = proc; + ngx_processes[s].data = data; + ngx_processes[s].name = name; + ngx_processes[s].exiting = 0; + + switch (respawn) { + + case NGX_PROCESS_RESPAWN: + ngx_processes[s].respawn = 1; + ngx_processes[s].just_respawn = 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].detached = 0; + break; + + case NGX_PROCESS_DETACHED: + ngx_processes[s].respawn = 0; + ngx_processes[s].just_respawn = 0; + ngx_processes[s].detached = 1; + break; + } + + if (s == ngx_last_process) { + ngx_last_process++; + } + + return pid; +} + + +ngx_pid_t ngx_execute(ngx_cycle_t *cycle, ngx_exec_ctx_t *ctx) +{ + return ngx_spawn_process(cycle, ngx_execute_proc, ctx, ctx->name, + NGX_PROCESS_DETACHED); +} + + +static void ngx_execute_proc(ngx_cycle_t *cycle, void *data) +{ + ngx_exec_ctx_t *ctx = data; + + if (execve(ctx->path, ctx->argv, ctx->envp) == -1) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "execve() failed while executing %s \"%s\"", + ctx->name, ctx->path); + } + + exit(1); +} + + +void ngx_process_get_status() +{ + int status; + char *process; + ngx_pid_t pid; + ngx_err_t err; + ngx_int_t i; + ngx_uint_t one; + struct timeval tv; + one = 0; + + for ( ;; ) { + pid = waitpid(-1, &status, WNOHANG); + + if (pid == 0) { + return; + } + + if (pid == -1) { + err = ngx_errno; + + if (err == NGX_EINTR) { + continue; + } + + if (err == NGX_ECHILD && one) { + return; + } + + ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, errno, + "waitpid() failed"); + return; + } + + + if (ngx_accept_mutex_ptr) { + + /* + * unlock the accept mutex if the abnormally exited process + * held it + */ + + ngx_atomic_cmp_set(ngx_accept_mutex_ptr, pid, 0); + } + + + one = 1; + process = "unknown process"; + + for (i = 0; i < ngx_last_process; i++) { + if (ngx_processes[i].pid == pid) { + ngx_processes[i].status = status; + ngx_processes[i].exited = 1; + process = ngx_processes[i].name; + break; + } + } + + if (WTERMSIG(status)) { + ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, + "%s " PID_T_FMT " exited on signal %d%s", + process, pid, WTERMSIG(status), + WCOREDUMP(status) ? " (core dumped)" : ""); + + } else { + ngx_log_error(NGX_LOG_INFO, ngx_cycle->log, 0, + "%s " PID_T_FMT " exited with code %d", + process, pid, WEXITSTATUS(status)); + } + + if (WEXITSTATUS(status) == 2 && ngx_processes[i].respawn) { + ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, + "%s " PID_T_FMT + " exited with fatal code %d and could not respawn", + process, pid, WEXITSTATUS(status)); + ngx_processes[i].respawn = 0; + } + } +} diff --git a/src/os/unix/ngx_process.h b/src/os/unix/ngx_process.h new file mode 100644 --- /dev/null +++ b/src/os/unix/ngx_process.h @@ -0,0 +1,67 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#ifndef _NGX_PROCESS_H_INCLUDED_ +#define _NGX_PROCESS_H_INCLUDED_ + + +typedef pid_t ngx_pid_t; + +typedef void (*ngx_spawn_proc_pt) (ngx_cycle_t *cycle, void *data); + +typedef struct { + ngx_pid_t pid; + int status; + ngx_socket_t channel[2]; + + ngx_spawn_proc_pt proc; + void *data; + char *name; + + unsigned respawn:1; + unsigned just_respawn:1; + unsigned detached:1; + unsigned exiting:1; + unsigned exited:1; +} ngx_process_t; + + +typedef struct { + char *path; + char *name; + char *const *argv; + char *const *envp; +} ngx_exec_ctx_t; + + +#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_getpid getpid +#define ngx_log_pid ngx_pid + +ngx_pid_t ngx_spawn_process(ngx_cycle_t *cycle, + ngx_spawn_proc_pt proc, void *data, + char *name, ngx_int_t respawn); +ngx_pid_t ngx_execute(ngx_cycle_t *cycle, ngx_exec_ctx_t *ctx); +void ngx_process_get_status(void); + +#define ngx_sched_yield() sched_yield() + + +extern ngx_pid_t ngx_pid; +extern ngx_socket_t ngx_channel; +extern ngx_int_t ngx_process_slot; +extern ngx_int_t ngx_last_process; +extern ngx_process_t ngx_processes[NGX_MAX_PROCESSES]; + + +#endif /* _NGX_PROCESS_H_INCLUDED_ */ diff --git a/src/os/unix/ngx_process_cycle.c b/src/os/unix/ngx_process_cycle.c new file mode 100644 --- /dev/null +++ b/src/os/unix/ngx_process_cycle.c @@ -0,0 +1,970 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include +#include + + +static void ngx_start_worker_processes(ngx_cycle_t *cycle, ngx_int_t n, + ngx_int_t type); +static void ngx_signal_worker_processes(ngx_cycle_t *cycle, int signo); +static ngx_uint_t ngx_reap_childs(ngx_cycle_t *cycle); +static void ngx_master_exit(ngx_cycle_t *cycle, ngx_master_ctx_t *ctx); +static void ngx_worker_process_cycle(ngx_cycle_t *cycle, void *data); +static void ngx_channel_handler(ngx_event_t *ev); +#if (NGX_THREADS) +static void ngx_wakeup_worker_threads(ngx_cycle_t *cycle); +static void *ngx_worker_thread_cycle(void *data); +#endif + + +ngx_uint_t ngx_process; +ngx_pid_t ngx_pid; +ngx_uint_t ngx_threaded; + +sig_atomic_t ngx_reap; +sig_atomic_t ngx_timer; +sig_atomic_t ngx_sigio; +sig_atomic_t ngx_terminate; +sig_atomic_t ngx_quit; +ngx_uint_t ngx_exiting; +sig_atomic_t ngx_reconfigure; +sig_atomic_t ngx_reopen; + +sig_atomic_t ngx_change_binary; +ngx_pid_t ngx_new_binary; +ngx_uint_t ngx_inherited; +ngx_uint_t ngx_daemonized; + +sig_atomic_t ngx_noaccept; +ngx_uint_t ngx_noaccepting; +ngx_uint_t ngx_restart; + + +#if (NGX_THREADS) +volatile ngx_thread_t ngx_threads[NGX_MAX_THREADS]; +ngx_int_t ngx_threads_n; +#endif + + +u_char master_process[] = "master process"; + + +void ngx_master_process_cycle(ngx_cycle_t *cycle, ngx_master_ctx_t *ctx) +{ + char *title; + u_char *p; + size_t size; + ngx_int_t i; + sigset_t set; + struct timeval tv; + struct itimerval itv; + ngx_uint_t live; + ngx_msec_t delay; + ngx_core_conf_t *ccf; + + sigemptyset(&set); + sigaddset(&set, SIGCHLD); + sigaddset(&set, SIGALRM); + sigaddset(&set, SIGIO); + sigaddset(&set, SIGINT); + sigaddset(&set, ngx_signal_value(NGX_RECONFIGURE_SIGNAL)); + sigaddset(&set, ngx_signal_value(NGX_REOPEN_SIGNAL)); + sigaddset(&set, ngx_signal_value(NGX_NOACCEPT_SIGNAL)); + sigaddset(&set, ngx_signal_value(NGX_TERMINATE_SIGNAL)); + sigaddset(&set, ngx_signal_value(NGX_SHUTDOWN_SIGNAL)); + sigaddset(&set, ngx_signal_value(NGX_CHANGEBIN_SIGNAL)); + + if (sigprocmask(SIG_BLOCK, &set, NULL) == -1) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "sigprocmask() failed"); + } + + sigemptyset(&set); + + + size = sizeof(master_process); + + for (i = 0; i < ctx->argc; i++) { + size += ngx_strlen(ctx->argv[i]) + 1; + } + + title = ngx_palloc(cycle->pool, size); + + p = ngx_cpymem(title, master_process, sizeof(master_process) - 1); + for (i = 0; i < ctx->argc; i++) { + *p++ = ' '; + p = ngx_cpystrn(p, (u_char *) ctx->argv[i], size); + } + + ngx_setproctitle(title); + + + ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module); + + ngx_start_worker_processes(cycle, ccf->worker_processes, + NGX_PROCESS_RESPAWN); + + ngx_new_binary = 0; + delay = 0; + live = 1; + + for ( ;; ) { + if (delay) { + delay *= 2; + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, + "temination cycle: %d", delay); + + itv.it_interval.tv_sec = 0; + itv.it_interval.tv_usec = 0; + itv.it_value.tv_sec = delay / 1000; + itv.it_value.tv_usec = (delay % 1000 ) * 1000; + + if (setitimer(ITIMER_REAL, &itv, NULL) == -1) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "setitimer() failed"); + } + } + + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "sigsuspend"); + + sigsuspend(&set); + + ngx_gettimeofday(&tv); + ngx_time_update(tv.tv_sec); + + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "wake up"); + + if (ngx_reap) { + ngx_reap = 0; + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "reap childs"); + + live = ngx_reap_childs(cycle); + } + + if (!live && (ngx_terminate || ngx_quit)) { + ngx_master_exit(cycle, ctx); + } + + if (ngx_terminate) { + if (delay == 0) { + delay = 50; + } + + if (delay > 1000) { + ngx_signal_worker_processes(cycle, SIGKILL); + } else { + ngx_signal_worker_processes(cycle, + ngx_signal_value(NGX_TERMINATE_SIGNAL)); + } + + continue; + } + + if (ngx_quit) { + ngx_signal_worker_processes(cycle, + ngx_signal_value(NGX_SHUTDOWN_SIGNAL)); + continue; + } + + if (ngx_timer) { + ngx_timer = 0; + ngx_start_worker_processes(cycle, ccf->worker_processes, + NGX_PROCESS_JUST_RESPAWN); + live = 1; + ngx_signal_worker_processes(cycle, + ngx_signal_value(NGX_SHUTDOWN_SIGNAL)); + } + + if (ngx_reconfigure) { + ngx_reconfigure = 0; + + if (ngx_new_binary) { + ngx_log_error(NGX_LOG_INFO, cycle->log, 0, "start new workers"); + + ngx_start_worker_processes(cycle, ccf->worker_processes, + NGX_PROCESS_RESPAWN); + ngx_noaccepting = 0; + + continue; + } + + ngx_log_error(NGX_LOG_INFO, cycle->log, 0, "reconfiguring"); + + cycle = ngx_init_cycle(cycle); + if (cycle == NULL) { + cycle = (ngx_cycle_t *) ngx_cycle; + continue; + } + + ngx_cycle = cycle; + ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, + ngx_core_module); + ngx_start_worker_processes(cycle, ccf->worker_processes, + NGX_PROCESS_JUST_RESPAWN); + live = 1; + ngx_signal_worker_processes(cycle, + ngx_signal_value(NGX_SHUTDOWN_SIGNAL)); + } + + if (ngx_restart) { + ngx_restart = 0; + ngx_start_worker_processes(cycle, ccf->worker_processes, + NGX_PROCESS_RESPAWN); + live = 1; + } + + if (ngx_reopen) { + ngx_reopen = 0; + ngx_log_error(NGX_LOG_INFO, cycle->log, 0, "reopening logs"); + ngx_reopen_files(cycle, ccf->user); + ngx_signal_worker_processes(cycle, + ngx_signal_value(NGX_REOPEN_SIGNAL)); + } + + if (ngx_change_binary) { + ngx_change_binary = 0; + ngx_log_error(NGX_LOG_INFO, cycle->log, 0, "changing binary"); + ngx_new_binary = ngx_exec_new_binary(cycle, ctx->argv); + } + + if (ngx_noaccept) { + ngx_noaccept = 0; + ngx_noaccepting = 1; + ngx_signal_worker_processes(cycle, + ngx_signal_value(NGX_SHUTDOWN_SIGNAL)); + } + } +} + + +void ngx_single_process_cycle(ngx_cycle_t *cycle, ngx_master_ctx_t *ctx) +{ + ngx_uint_t i; + +#if 0 + ngx_setproctitle("single worker process"); +#endif + + 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) { + /* fatal */ + exit(2); + } + } + } + + for ( ;; ) { + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "worker cycle"); + + ngx_process_events(cycle); + + if (ngx_terminate || ngx_quit) { + ngx_master_exit(cycle, ctx); + } + + if (ngx_reconfigure) { + ngx_reconfigure = 0; + ngx_log_error(NGX_LOG_INFO, cycle->log, 0, "reconfiguring"); + + cycle = ngx_init_cycle(cycle); + if (cycle == NULL) { + cycle = (ngx_cycle_t *) ngx_cycle; + continue; + } + + ngx_cycle = cycle; + } + + if (ngx_reopen) { + ngx_reopen = 0; + ngx_log_error(NGX_LOG_INFO, cycle->log, 0, "reopening logs"); + ngx_reopen_files(cycle, (ngx_uid_t) -1); + } + } +} + + +static void ngx_start_worker_processes(ngx_cycle_t *cycle, ngx_int_t n, + ngx_int_t type) +{ + ngx_int_t i; + ngx_channel_t ch; + struct itimerval itv; + + ngx_log_error(NGX_LOG_INFO, cycle->log, 0, "start worker processes"); + + ch.command = NGX_CMD_OPEN_CHANNEL; + + while (n--) { + ngx_spawn_process(cycle, ngx_worker_process_cycle, NULL, + "worker process", type); + + ch.pid = ngx_processes[ngx_process_slot].pid; + ch.slot = ngx_process_slot; + ch.fd = ngx_processes[ngx_process_slot].channel[0]; + + for (i = 0; i < ngx_last_process; i++) { + + if (i == ngx_process_slot + || ngx_processes[i].pid == -1 + || ngx_processes[i].channel[0] == -1) + { + continue; + } + + ngx_log_debug6(NGX_LOG_DEBUG_CORE, cycle->log, 0, + "pass channel s:%d pid:" PID_T_FMT + " fd:%d to s:%d pid:" PID_T_FMT " fd:%d", + 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); + } + } + + /* + * we have to limit the maximum life time of the worker processes + * by 10 days because our millisecond event timer is limited + * by 24 days on 32-bit platforms + */ + + itv.it_interval.tv_sec = 0; + itv.it_interval.tv_usec = 0; + itv.it_value.tv_sec = 10 * 24 * 60 * 60; + itv.it_value.tv_usec = 0; + + if (setitimer(ITIMER_REAL, &itv, NULL) == -1) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "setitimer() failed"); + } +} + + +static void ngx_signal_worker_processes(ngx_cycle_t *cycle, int signo) +{ + ngx_int_t i; + ngx_err_t err; + ngx_channel_t ch; + + + switch (signo) { + + case ngx_signal_value(NGX_SHUTDOWN_SIGNAL): + ch.command = NGX_CMD_QUIT; + break; + + case ngx_signal_value(NGX_TERMINATE_SIGNAL): + ch.command = NGX_CMD_TERMINATE; + break; + + case ngx_signal_value(NGX_REOPEN_SIGNAL): + ch.command = NGX_CMD_REOPEN; + break; + + default: + ch.command = 0; + } + + ch.fd = -1; + + + for (i = 0; i < ngx_last_process; i++) { + + ngx_log_debug7(NGX_LOG_DEBUG_EVENT, cycle->log, 0, + "child: %d " PID_T_FMT " e:%d t:%d d:%d r:%d j:%d", + i, + ngx_processes[i].pid, + ngx_processes[i].exiting, + ngx_processes[i].exited, + ngx_processes[i].detached, + ngx_processes[i].respawn, + ngx_processes[i].just_respawn); + + if (ngx_processes[i].detached || ngx_processes[i].pid == -1) { + continue; + } + + if (ngx_processes[i].just_respawn) { + ngx_processes[i].just_respawn = 0; + continue; + } + + if (ngx_processes[i].exiting + && signo == ngx_signal_value(NGX_SHUTDOWN_SIGNAL)) + { + continue; + } + + if (ch.command) { + if (ngx_write_channel(ngx_processes[i].channel[0], + &ch, sizeof(ngx_channel_t), cycle->log) == NGX_OK) + { + if (signo != ngx_signal_value(NGX_REOPEN_SIGNAL)) { + ngx_processes[i].exiting = 1; + } + + continue; + } + } + + ngx_log_debug2(NGX_LOG_DEBUG_CORE, cycle->log, 0, + "kill (" PID_T_FMT ", %d)" , + ngx_processes[i].pid, signo); + + if (kill(ngx_processes[i].pid, signo) == -1) { + err = ngx_errno; + ngx_log_error(NGX_LOG_ALERT, cycle->log, err, + "kill(%d, %d) failed", + ngx_processes[i].pid, signo); + + if (err == NGX_ESRCH) { + ngx_processes[i].exited = 1; + ngx_processes[i].exiting = 0; + ngx_reap = 1; + } + + continue; + } + + if (signo != ngx_signal_value(NGX_REOPEN_SIGNAL)) { + ngx_processes[i].exiting = 1; + } + } +} + + +static ngx_uint_t ngx_reap_childs(ngx_cycle_t *cycle) +{ + ngx_int_t i, n; + ngx_uint_t live; + ngx_channel_t ch; + + ch.command = NGX_CMD_CLOSE_CHANNEL; + ch.fd = -1; + + live = 0; + for (i = 0; i < ngx_last_process; i++) { + + ngx_log_debug7(NGX_LOG_DEBUG_EVENT, cycle->log, 0, + "child: %d " PID_T_FMT " e:%d t:%d d:%d r:%d j:%d", + i, + ngx_processes[i].pid, + ngx_processes[i].exiting, + ngx_processes[i].exited, + ngx_processes[i].detached, + ngx_processes[i].respawn, + ngx_processes[i].just_respawn); + + if (ngx_processes[i].pid == -1) { + continue; + } + + if (ngx_processes[i].exited) { + + if (!ngx_processes[i].detached) { + ngx_close_channel(ngx_processes[i].channel, cycle->log); + + ngx_processes[i].channel[0] = -1; + ngx_processes[i].channel[1] = -1; + + ch.pid = ngx_processes[i].pid; + ch.slot = i; + + for (n = 0; n < ngx_last_process; n++) { + if (ngx_processes[n].exited + || ngx_processes[n].pid == -1 + || ngx_processes[n].channel[0] == -1) + { + continue; + } + + ngx_log_debug3(NGX_LOG_DEBUG_CORE, cycle->log, 0, + "pass close channel s:%d pid:" PID_T_FMT + " to:" PID_T_FMT, ch.slot, ch.pid, ngx_processes[n].pid); + + /* TODO: NGX_AGAIN */ + + ngx_write_channel(ngx_processes[n].channel[0], + &ch, sizeof(ngx_channel_t), cycle->log); + } + } + + if (ngx_processes[i].respawn + && !ngx_processes[i].exiting + && !ngx_terminate + && !ngx_quit) + { + if (ngx_spawn_process(cycle, ngx_processes[i].proc, + ngx_processes[i].data, + ngx_processes[i].name, i) + == NGX_ERROR) + { + ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, + "can not respawn %s", ngx_processes[i].name); + continue; + } + + live = 1; + + continue; + } + + if (ngx_processes[i].pid == ngx_new_binary) { + ngx_new_binary = 0; + if (ngx_noaccepting) { + ngx_restart = 1; + ngx_noaccepting = 0; + } + } + + if (i == ngx_last_process - 1) { + ngx_last_process--; + + } else { + ngx_processes[i].pid = -1; + } + + } else if (ngx_processes[i].exiting || !ngx_processes[i].detached) { + live = 1; + } + } + + return live; +} + + +static void ngx_master_exit(ngx_cycle_t *cycle, ngx_master_ctx_t *ctx) +{ + ngx_delete_pidfile(cycle); + + ngx_log_error(NGX_LOG_INFO, cycle->log, 0, "exit"); + + ngx_destroy_pool(cycle->pool); + + exit(0); +} + + +static void ngx_worker_process_cycle(ngx_cycle_t *cycle, void *data) +{ + sigset_t set; + ngx_err_t err; + ngx_int_t n; + ngx_uint_t i; + struct timeval tv; + ngx_listening_t *ls; + ngx_core_conf_t *ccf; + ngx_connection_t *c; + + + ngx_gettimeofday(&tv); + + ngx_start_msec = (ngx_epoch_msec_t) tv.tv_sec * 1000 + tv.tv_usec / 1000; + ngx_old_elapsed_msec = 0; + ngx_elapsed_msec = 0; + + + ngx_process = NGX_PROCESS_WORKER; + + ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module); + + if (ccf->group != (gid_t) NGX_CONF_UNSET) { + if (setgid(ccf->group) == -1) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, + "setgid(%d) failed", ccf->group); + /* fatal */ + exit(2); + } + } + + if (ccf->user != (uid_t) NGX_CONF_UNSET) { + if (setuid(ccf->user) == -1) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, + "setuid(%d) failed", ccf->user); + /* fatal */ + exit(2); + } + } + +#if (HAVE_PR_SET_DUMPABLE) + + /* allow coredump after setuid() in Linux 2.4.x */ + + if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) == -1) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "prctl(PR_SET_DUMPABLE) failed"); + } + +#endif + + sigemptyset(&set); + + if (sigprocmask(SIG_SETMASK, &set, NULL) == -1) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "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 + */ + ls = cycle->listening.elts; + for (i = 0; i < cycle->listening.nelts; i++) { + ls[i].remain = 0; + } + + for (i = 0; ngx_modules[i]; i++) { + if (ngx_modules[i]->init_process) { + if (ngx_modules[i]->init_process(cycle) == NGX_ERROR) { + /* fatal */ + exit(2); + } + } + } + + for (n = 0; n < ngx_last_process; n++) { + + if (ngx_processes[n].pid == -1) { + continue; + } + + if (n == ngx_process_slot) { + continue; + } + + if (ngx_processes[n].channel[1] == -1) { + continue; + } + + if (close(ngx_processes[n].channel[1]) == -1) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "close() failed"); + } + } + + if (close(ngx_processes[ngx_process_slot].channel[0]) == -1) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "close() failed"); + } + +#if 0 + ngx_last_process = 0; +#endif + + if (ngx_add_channel_event(cycle, ngx_channel, NGX_READ_EVENT, + ngx_channel_handler) == NGX_ERROR) + { + /* fatal */ + exit(2); + } + + ngx_setproctitle("worker process"); + +#if (NGX_THREADS) + + if (ngx_time_mutex_init(cycle->log) == NGX_ERROR) { + /* fatal */ + exit(2); + } + + if (ngx_threads_n) { + if (ngx_init_threads(ngx_threads_n, + ccf->thread_stack_size, cycle) == NGX_ERROR) + { + /* fatal */ + exit(2); + } + + err = ngx_thread_key_create(&ngx_core_tls_key); + if (err != 0) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, err, + ngx_thread_key_create_n " failed"); + /* fatal */ + exit(2); + } + + for (n = 0; n < ngx_threads_n; n++) { + + if (!(ngx_threads[n].cv = ngx_cond_init(cycle->log))) { + /* fatal */ + exit(2); + } + + if (ngx_create_thread((ngx_tid_t *) &ngx_threads[n].tid, + ngx_worker_thread_cycle, + (void *) &ngx_threads[n], cycle->log) != 0) + { + /* fatal */ + exit(2); + } + } + } + +#endif + + for ( ;; ) { + if (ngx_exiting + && ngx_event_timer_rbtree == &ngx_event_timer_sentinel) + { + ngx_log_error(NGX_LOG_INFO, cycle->log, 0, "exiting"); + + +#if (NGX_THREADS) + ngx_terminate = 1; + + ngx_wakeup_worker_threads(cycle); +#endif + + /* + * we do not destroy cycle->pool here because a signal handler + * that uses cycle->log can be called at this point + */ + exit(0); + } + + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "worker cycle"); + + ngx_process_events(cycle); + + if (ngx_terminate) { + ngx_log_error(NGX_LOG_INFO, cycle->log, 0, "exiting"); + +#if (NGX_THREADS) + ngx_wakeup_worker_threads(cycle); +#endif + + /* + * we do not destroy cycle->pool here because a signal handler + * that uses cycle->log can be called at this point + */ + exit(0); + } + + if (ngx_quit) { + ngx_quit = 0; + ngx_log_error(NGX_LOG_INFO, cycle->log, 0, + "gracefully shutting down"); + ngx_setproctitle("worker process is shutting down"); + + if (!ngx_exiting) { + ngx_close_listening_sockets(cycle); + ngx_exiting = 1; + } + } + + if (ngx_reopen) { + ngx_reopen = 0; + ngx_log_error(NGX_LOG_INFO, cycle->log, 0, "reopen logs"); + ngx_reopen_files(cycle, -1); + } + } +} + + +static void ngx_channel_handler(ngx_event_t *ev) +{ + ngx_int_t n; + ngx_channel_t ch; + ngx_connection_t *c; + + c = ev->data; + + 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); + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, ev->log, 0, "channel: %d", n); + + if (n <= 0) { + return; + } + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, ev->log, 0, + "channel command: %d", ch.command); + + switch (ch.command) { + + case NGX_CMD_QUIT: + ngx_quit = 1; + break; + + case NGX_CMD_TERMINATE: + ngx_terminate = 1; + break; + + case NGX_CMD_REOPEN: + ngx_reopen = 1; + break; + + case NGX_CMD_OPEN_CHANNEL: + + ngx_log_debug3(NGX_LOG_DEBUG_CORE, ev->log, 0, + "get channel s:%d pid:" PID_T_FMT " fd:%d", + ch.slot, ch.pid, ch.fd); + + ngx_processes[ch.slot].pid = ch.pid; + ngx_processes[ch.slot].channel[0] = ch.fd; + break; + + case NGX_CMD_CLOSE_CHANNEL: + + ngx_log_debug4(NGX_LOG_DEBUG_CORE, ev->log, 0, + "close channel s:%d pid:" PID_T_FMT " our:" PID_T_FMT + " 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() failed"); + } + + ngx_processes[ch.slot].channel[0] = -1; + break; + } +} + + +#if (NGX_THREADS) + +static void ngx_wakeup_worker_threads(ngx_cycle_t *cycle) +{ + ngx_int_t i; + ngx_uint_t live; + + for ( ;; ) { + + live = 0; + + for (i = 0; i < ngx_threads_n; i++) { + if (ngx_threads[i].state < NGX_THREAD_EXIT) { + ngx_cond_signal(ngx_threads[i].cv); + + if (ngx_threads[i].cv->tid == (ngx_tid_t) -1) { + ngx_threads[i].state = NGX_THREAD_DONE; + } else { + live = 1; + } + } + + if (ngx_threads[i].state == NGX_THREAD_EXIT) { + ngx_thread_join(ngx_threads[i].tid, NULL); + ngx_threads[i].state = NGX_THREAD_DONE; + } + } + + if (live == 0) { + ngx_log_debug0(NGX_LOG_DEBUG_CORE, cycle->log, 0, + "all worker threads are joined"); + + /* STUB */ + ngx_done_events(cycle); + ngx_mutex_destroy(ngx_event_timer_mutex); + ngx_mutex_destroy(ngx_posted_events_mutex); + + return; + } + + ngx_sched_yield(); + } +} + + +static void* ngx_worker_thread_cycle(void *data) +{ + ngx_thread_t *thr = data; + + sigset_t set; + ngx_err_t err; + ngx_core_tls_t *tls; + ngx_cycle_t *cycle; + struct timeval tv; + + thr->cv->tid = ngx_thread_self(); + + cycle = (ngx_cycle_t *) ngx_cycle; + + sigemptyset(&set); + sigaddset(&set, ngx_signal_value(NGX_RECONFIGURE_SIGNAL)); + sigaddset(&set, ngx_signal_value(NGX_REOPEN_SIGNAL)); + sigaddset(&set, ngx_signal_value(NGX_CHANGEBIN_SIGNAL)); + + err = ngx_thread_sigmask(SIG_BLOCK, &set, NULL); + if (err) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, err, + ngx_thread_sigmask_n " failed"); + return (void *) 1; + } + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, cycle->log, 0, + "thread " TID_T_FMT " started", ngx_thread_self()); + + ngx_setthrtitle("worker thread"); + + if (!(tls = ngx_calloc(sizeof(ngx_core_tls_t), cycle->log))) { + return (void *) 1; + } + + err = ngx_thread_set_tls(ngx_core_tls_key, tls); + if (err != 0) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, err, + ngx_thread_set_tls_n " failed"); + return (void *) 1; + } + + if (ngx_mutex_lock(ngx_posted_events_mutex) == NGX_ERROR) { + return (void *) 1; + } + + for ( ;; ) { + thr->state = NGX_THREAD_FREE; + + if (ngx_cond_wait(thr->cv, ngx_posted_events_mutex) == NGX_ERROR) { + return (void *) 1; + } + + if (ngx_terminate) { + thr->state = NGX_THREAD_EXIT; + + ngx_mutex_unlock(ngx_posted_events_mutex); + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, cycle->log, 0, + "thread %d is done", ngx_thread_self()); + + return (void *) 0; + } + + thr->state = NGX_THREAD_BUSY; + + if (ngx_event_thread_process_posted(cycle) == NGX_ERROR) { + return (void *) 1; + } + + if (ngx_event_thread_process_posted(cycle) == NGX_ERROR) { + return (void *) 1; + } + + if (ngx_process_changes) { + if (ngx_process_changes(cycle, 1) == NGX_ERROR) { + return (void *) 1; + } + } + } +} + +#endif diff --git a/src/os/unix/ngx_process_cycle.h b/src/os/unix/ngx_process_cycle.h new file mode 100644 --- /dev/null +++ b/src/os/unix/ngx_process_cycle.h @@ -0,0 +1,56 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#ifndef _NGX_PROCESS_CYCLE_H_INCLUDED_ +#define _NGX_PROCESS_CYCLE_H_INCLUDED_ + + +#include +#include + + +#define NGX_CMD_OPEN_CHANNEL 1 +#define NGX_CMD_CLOSE_CHANNEL 2 +#define NGX_CMD_QUIT 3 +#define NGX_CMD_TERMINATE 4 +#define NGX_CMD_REOPEN 5 + + +typedef struct { + int argc; + char *const *argv; +} ngx_master_ctx_t; + + +#define NGX_PROCESS_SINGLE 0 +#define NGX_PROCESS_MASTER 1 +#define NGX_PROCESS_WORKER 2 + + +void ngx_master_process_cycle(ngx_cycle_t *cycle, ngx_master_ctx_t *ctx); +void ngx_single_process_cycle(ngx_cycle_t *cycle, ngx_master_ctx_t *ctx); + + +extern ngx_uint_t ngx_process; +extern ngx_pid_t ngx_pid; +extern ngx_pid_t ngx_new_binary; +extern ngx_uint_t ngx_inherited; +extern ngx_uint_t ngx_daemonized; +extern ngx_uint_t ngx_threaded; +extern ngx_uint_t ngx_exiting; + +extern sig_atomic_t ngx_reap; +extern sig_atomic_t ngx_timer; +extern sig_atomic_t ngx_sigio; +extern sig_atomic_t ngx_quit; +extern sig_atomic_t ngx_terminate; +extern sig_atomic_t ngx_noaccept; +extern sig_atomic_t ngx_reconfigure; +extern sig_atomic_t ngx_reopen; +extern sig_atomic_t ngx_change_binary; + + +#endif /* _NGX_PROCESS_CYCLE_H_INCLUDED_ */ diff --git a/src/os/unix/ngx_pthread_thread.c b/src/os/unix/ngx_pthread_thread.c new file mode 100644 --- /dev/null +++ b/src/os/unix/ngx_pthread_thread.c @@ -0,0 +1,273 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include + + +static ngx_uint_t nthreads; +static ngx_uint_t max_threads; + + +static pthread_attr_t thr_attr; + + +int ngx_create_thread(ngx_tid_t *tid, void* (*func)(void *arg), void *arg, + ngx_log_t *log) +{ + int err; + + if (nthreads >= max_threads) { + ngx_log_error(NGX_LOG_CRIT, log, 0, + "no more than %d threads can be created", max_threads); + return NGX_ERROR; + } + + err = pthread_create(tid, &thr_attr, func, arg); + + if (err != 0) { + ngx_log_error(NGX_LOG_ALERT, log, err, "pthread_create() failed"); + return err; + } + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, log, 0, + "thread is created: " TID_T_FMT, *tid); + + nthreads++; + + return err; +} + + +ngx_int_t ngx_init_threads(int n, size_t size, ngx_cycle_t *cycle) +{ + int err; + + max_threads = n; + + err = pthread_attr_init(&thr_attr); + + if (err != 0) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, err, + "pthread_attr_init() failed"); + return NGX_ERROR; + } + + err = pthread_attr_setstacksize(&thr_attr, size); + + if (err != 0) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, err, + "pthread_attr_setstacksize() failed"); + return NGX_ERROR; + } + + ngx_threaded = 1; + + return NGX_OK; +} + + +ngx_mutex_t *ngx_mutex_init(ngx_log_t *log, uint flags) +{ + int err; + ngx_mutex_t *m; + + if (!(m = ngx_alloc(sizeof(ngx_mutex_t), log))) { + return NULL; + } + + m->log = log; + + err = pthread_mutex_init(&m->mutex, NULL); + + if (err != 0) { + ngx_log_error(NGX_LOG_ALERT, m->log, err, + "pthread_mutex_init() failed"); + return NULL; + } + + return m; +} + + +void ngx_mutex_destroy(ngx_mutex_t *m) +{ + int err; + + err = pthread_mutex_destroy(&m->mutex); + + if (err != 0) { + ngx_log_error(NGX_LOG_ALERT, m->log, err, + "pthread_mutex_destroy(" PTR_FMT ") failed", m); + } + + ngx_free(m); +} + + +ngx_int_t ngx_mutex_lock(ngx_mutex_t *m) +{ + int err; + + if (!ngx_threaded) { + return NGX_OK; + } + + ngx_log_debug1(NGX_LOG_DEBUG_MUTEX, m->log, 0, "lock mutex " PTR_FMT, m); + + err = pthread_mutex_lock(&m->mutex); + + if (err != 0) { + ngx_log_error(NGX_LOG_ALERT, m->log, err, + "pthread_mutex_lock(" PTR_FMT ") failed", m); + return NGX_ERROR; + } + + ngx_log_debug1(NGX_LOG_DEBUG_MUTEX, m->log, 0, + "mutex " PTR_FMT " is locked", m); + + return NGX_OK; +} + + +ngx_int_t ngx_mutex_trylock(ngx_mutex_t *m) +{ + int err; + + if (!ngx_threaded) { + return NGX_OK; + } + + ngx_log_debug1(NGX_LOG_DEBUG_MUTEX, m->log, 0, + "try lock mutex " PTR_FMT, m); + + err = pthread_mutex_trylock(&m->mutex); + + if (err == NGX_EBUSY) { + return NGX_AGAIN; + } + + if (err != 0) { + ngx_log_error(NGX_LOG_ALERT, m->log, err, + "pthread_mutex_trylock(" PTR_FMT ") failed", m); + return NGX_ERROR; + } + + ngx_log_debug1(NGX_LOG_DEBUG_MUTEX, m->log, 0, + "mutex " PTR_FMT " is locked", m); + + return NGX_OK; +} + + +ngx_int_t ngx_mutex_unlock(ngx_mutex_t *m) +{ + int err; + + if (!ngx_threaded) { + return NGX_OK; + } + + ngx_log_debug1(NGX_LOG_DEBUG_MUTEX, m->log, 0, "unlock mutex " PTR_FMT, m); + + err = pthread_mutex_unlock(&m->mutex); + + if (err != 0) { + ngx_log_error(NGX_LOG_ALERT, m->log, err, + "pthread_mutex_unlock(" PTR_FMT ") failed", m); + return NGX_ERROR; + } + + ngx_log_debug1(NGX_LOG_DEBUG_MUTEX, m->log, 0, + "mutex " PTR_FMT " is unlocked", m); + + return NGX_OK; +} + + +ngx_cond_t *ngx_cond_init(ngx_log_t *log) +{ + int err; + ngx_cond_t *cv; + + if (!(cv = ngx_alloc(sizeof(ngx_cond_t), log))) { + return NULL; + } + + cv->log = log; + + err = pthread_cond_init(&cv->cond, NULL); + + if (err != 0) { + ngx_log_error(NGX_LOG_ALERT, cv->log, err, + "pthread_cond_init() failed"); + return NULL; + } + + return cv; +} + + +void ngx_cond_destroy(ngx_cond_t *cv) +{ + int err; + + err = pthread_cond_destroy(&cv->cond); + + if (err != 0) { + ngx_log_error(NGX_LOG_ALERT, cv->log, err, + "pthread_cond_destroy(" PTR_FMT ") failed", cv); + } + + ngx_free(cv); +} + + +ngx_int_t ngx_cond_wait(ngx_cond_t *cv, ngx_mutex_t *m) +{ + int err; + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, cv->log, 0, + "cv " PTR_FMT " wait", cv); + + err = pthread_cond_wait(&cv->cond, &m->mutex); + + if (err != 0) { + ngx_log_error(NGX_LOG_ALERT, cv->log, err, + "pthread_cond_wait(" PTR_FMT ") failed", cv); + return NGX_ERROR; + } + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, cv->log, 0, + "cv " PTR_FMT " is waked up", cv); + + ngx_log_debug1(NGX_LOG_DEBUG_MUTEX, m->log, 0, + "mutex " PTR_FMT " is locked", m); + + return NGX_OK; +} + + +ngx_int_t ngx_cond_signal(ngx_cond_t *cv) +{ + int err; + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, cv->log, 0, + "cv " PTR_FMT " to signal", cv); + + err = pthread_cond_signal(&cv->cond); + + if (err != 0) { + ngx_log_error(NGX_LOG_ALERT, cv->log, err, + "pthread_cond_signal(" PTR_FMT ") failed", cv); + return NGX_ERROR; + } + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, cv->log, 0, + "cv " PTR_FMT " is signaled", cv); + + return NGX_OK; +} diff --git a/src/os/unix/ngx_readv_chain.c b/src/os/unix/ngx_readv_chain.c new file mode 100644 --- /dev/null +++ b/src/os/unix/ngx_readv_chain.c @@ -0,0 +1,219 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include + + +#if (HAVE_KQUEUE) + +ssize_t ngx_readv_chain(ngx_connection_t *c, ngx_chain_t *chain) +{ + u_char *prev; + ssize_t n, size; + ngx_err_t err; + ngx_array_t io; + ngx_event_t *rev; + struct iovec *iov; + + rev = c->read; + + if (ngx_event_flags & NGX_HAVE_KQUEUE_EVENT) { + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, + "readv: eof:%d, avail:%d, err:%d", + rev->pending_eof, rev->available, rev->kq_errno); + + if (rev->available == 0) { + if (rev->pending_eof) { + rev->ready = 0; + rev->eof = 1; + + ngx_log_error(NGX_LOG_INFO, c->log, rev->kq_errno, + "kevent() reported about an closed connection"); + + if (rev->kq_errno) { + rev->error = 1; + ngx_set_socket_errno(rev->kq_errno); + return NGX_ERROR; + } + + return 0; + + } else { + return NGX_AGAIN; + } + } + } + + prev = NULL; + iov = NULL; + size = 0; + + ngx_init_array(io, c->pool, 10, sizeof(struct iovec), NGX_ERROR); + + /* coalesce the neighbouring bufs */ + + while (chain) { + if (prev == chain->buf->last) { + iov->iov_len += chain->buf->end - chain->buf->last; + + } else { + ngx_test_null(iov, ngx_push_array(&io), NGX_ERROR); + iov->iov_base = (void *) chain->buf->last; + iov->iov_len = chain->buf->end - chain->buf->last; + } + + size += chain->buf->end - chain->buf->last; + prev = chain->buf->end; + chain = chain->next; + } + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, + "readv: %d, last:%d", io.nelts, iov->iov_len); + + rev = c->read; + + do { + n = readv(c->fd, (struct iovec *) io.elts, io.nelts); + + if (n >= 0) { + if (ngx_event_flags & NGX_HAVE_KQUEUE_EVENT) { + rev->available -= n; + + /* + * rev->available can be negative here because some additional + * bytes can be received between kevent() and recv() + */ + + if (rev->available <= 0) { + if (!rev->pending_eof) { + rev->ready = 0; + } + + if (rev->available < 0) { + rev->available = 0; + } + } + + return n; + } + + if (n < size) { + rev->ready = 0; + } + + if (n == 0) { + rev->eof = 1; + } + + return n; + } + + err = ngx_socket_errno; + + if (err == NGX_EAGAIN || err == NGX_EINTR) { + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err, + "readv() not ready"); + n = NGX_AGAIN; + + } else { + n = ngx_connection_error(c, err, "readv() failed"); + break; + } + + } while (err == NGX_EINTR); + + rev->ready = 0; + + if (n == NGX_ERROR){ + c->read->error = 1; + } + + return n; +} + +#else /* ! NAVE_KQUEUE */ + +ssize_t ngx_readv_chain(ngx_connection_t *c, ngx_chain_t *chain) +{ + u_char *prev; + ssize_t n, size; + ngx_err_t err; + ngx_array_t io; + ngx_event_t *rev; + struct iovec *iov; + + prev = NULL; + iov = NULL; + size = 0; + + ngx_init_array(io, c->pool, 10, sizeof(struct iovec), NGX_ERROR); + + /* coalesce the neighbouring bufs */ + + while (chain) { + if (prev == chain->buf->last) { + iov->iov_len += chain->buf->end - chain->buf->last; + + } else { + ngx_test_null(iov, ngx_push_array(&io), NGX_ERROR); + iov->iov_base = chain->buf->last; + iov->iov_len = chain->buf->end - chain->buf->last; + } + + size += chain->buf->end - chain->buf->last; + prev = chain->buf->end; + chain = chain->next; + } + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, + "readv: %d:%d", io.nelts, iov->iov_len); + + rev = c->read; + + do { + n = readv(c->fd, (struct iovec *) io.elts, io.nelts); + + if (n == 0) { + rev->ready = 0; + rev->eof = 1; + + return n; + + } else if (n > 0) { + + if (n < size && !(ngx_event_flags & NGX_HAVE_GREEDY_EVENT)) { + rev->ready = 0; + } + + return n; + } + + err = ngx_socket_errno; + + if (err == NGX_EAGAIN || err == NGX_EINTR) { + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err, + "readv() not ready"); + n = NGX_AGAIN; + + } else { + n = ngx_connection_error(c, err, "readv() failed"); + break; + } + + } while (err == NGX_EINTR); + + rev->ready = 0; + + if (n == NGX_ERROR){ + c->read->error = 1; + } + + return n; +} + +#endif /* NAVE_KQUEUE */ diff --git a/src/os/unix/ngx_recv.c b/src/os/unix/ngx_recv.c new file mode 100644 --- /dev/null +++ b/src/os/unix/ngx_recv.c @@ -0,0 +1,173 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include + + +#if (HAVE_KQUEUE) + +ssize_t ngx_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; + + if (ngx_event_flags & NGX_HAVE_KQUEUE_EVENT) { + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, + "recv: eof:%d, avail:%d, err:%d", + rev->pending_eof, rev->available, rev->kq_errno); + + if (rev->available == 0) { + if (rev->pending_eof) { + rev->ready = 0; + rev->eof = 1; + + ngx_log_error(NGX_LOG_INFO, c->log, rev->kq_errno, + "kevent() reported about an closed connection"); + + if (rev->kq_errno) { + rev->error = 1; + ngx_set_socket_errno(rev->kq_errno); + + if (rev->kq_errno == NGX_ECONNRESET + && c->log_error == NGX_ERROR_IGNORE_ECONNRESET) + { + return 0; + } + + return NGX_ERROR; + } + + return 0; + + } else { + return NGX_AGAIN; + } + } + } + + 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_HAVE_KQUEUE_EVENT) { + rev->available -= n; + + /* + * rev->available can be negative here because some additional + * bytes can be received between kevent() and recv() + */ + + if (rev->available <= 0) { + if (!rev->pending_eof) { + rev->ready = 0; + } + + if (rev->available < 0) { + rev->available = 0; + } + } + + return n; + } + + if ((size_t) n < size) { + rev->ready = 0; + } + + if (n == 0) { + rev->eof = 1; + } + + 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 /* ! NAVE_KQUEUE */ + +ssize_t ngx_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) { + rev->ready = 0; + rev->eof = 1; + return n; + + } else if (n > 0) { + + if ((size_t) n < size + && !(ngx_event_flags & NGX_HAVE_GREEDY_EVENT)) + { + rev->ready = 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 /* NAVE_KQUEUE */ diff --git a/src/os/unix/ngx_send.c b/src/os/unix/ngx_send.c new file mode 100644 --- /dev/null +++ b/src/os/unix/ngx_send.c @@ -0,0 +1,70 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include + + +ssize_t ngx_unix_send(ngx_connection_t *c, u_char *buf, size_t size) +{ + ssize_t n; + ngx_err_t err; + ngx_event_t *wev; + + wev = c->write; + +#if (HAVE_KQUEUE) + + if ((ngx_event_flags & NGX_HAVE_KQUEUE_EVENT) && wev->pending_eof) { + ngx_log_error(NGX_LOG_INFO, c->log, wev->kq_errno, + "kevent() reported about an closed connection"); + + wev->error = 1; + return NGX_ERROR; + } + +#endif + + for ( ;; ) { + n = send(c->fd, buf, size, 0); + + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, + "send: fd:%d %d of %d", c->fd, n, size); + + if (n > 0) { + if (n < (ssize_t) size) { + wev->ready = 0; + } + + return n; + } + + err = ngx_socket_errno; + + if (n == 0) { + ngx_log_error(NGX_LOG_ALERT, c->log, err, "send() returned zero"); + wev->ready = 0; + return n; + } + + if (err == NGX_EAGAIN || err == NGX_EINTR) { + wev->ready = 0; + + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err, + "send() not ready"); + + if (err == NGX_EAGAIN) { + return NGX_AGAIN; + } + + } else { + wev->error = 1; + ngx_connection_error(c, err, "recv() failed"); + return NGX_ERROR; + } + } +} diff --git a/src/os/unix/ngx_shared.c b/src/os/unix/ngx_shared.c new file mode 100644 --- /dev/null +++ b/src/os/unix/ngx_shared.c @@ -0,0 +1,96 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include + + +#if (HAVE_MAP_ANON) + +void *ngx_create_shared_memory(size_t size, ngx_log_t *log) +{ + void *p; + + p = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_ANON|MAP_SHARED, -1, 0); + + if (p == MAP_FAILED) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + "mmap(MAP_ANON|MAP_SHARED, " SIZE_T_FMT ") failed", + size); + return NULL; + } + + return p; +} + +#elif (HAVE_MAP_DEVZERO) + +void *ngx_create_shared_memory(size_t size, ngx_log_t *log) +{ + void *p; + ngx_fd_t fd; + + fd = open("/dev/zero", O_RDWR); + + if (fd == -1) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + "open(/dev/zero) failed"); + return NULL; + } + + p = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); + + if (p == MAP_FAILED) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + "mmap(/dev/zero, MAP_SHARED, " SIZE_T_FMT ") failed", + size); + p = NULL; + } + + if (close(fd) == -1) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, "close() failed"); + } + + return p; +} + +#elif (HAVE_SYSVSHM) + +#include +#include + + +void *ngx_create_shared_memory(size_t size, ngx_log_t *log) +{ + int id; + void *p; + + id = shmget(IPC_PRIVATE, size, (SHM_R|SHM_W|IPC_CREAT)); + + if (id == -1) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + "shmget(" SIZE_T_FMT ") failed", size); + return NULL; + } + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, log, 0, "shmget id: %d", id); + + p = shmat(id, NULL, 0); + + if (p == (void *) -1) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, "shmat() failed"); + p = NULL; + } + + if (shmctl(id, IPC_RMID, NULL) == -1) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, "shmctl(IPC_RMID) failed"); + p = NULL; + } + + return p; +} + +#endif diff --git a/src/os/unix/ngx_shared.h b/src/os/unix/ngx_shared.h new file mode 100644 --- /dev/null +++ b/src/os/unix/ngx_shared.h @@ -0,0 +1,18 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#ifndef _NGX_SHARED_H_INCLUDED_ +#define _NGX_SHARED_H_INCLUDED_ + + +#include +#include + + +void *ngx_create_shared_memory(size_t size, ngx_log_t *log); + + +#endif /* _NGX_SHARED_H_INCLUDED_ */ diff --git a/src/os/unix/ngx_socket.c b/src/os/unix/ngx_socket.c new file mode 100644 --- /dev/null +++ b/src/os/unix/ngx_socket.c @@ -0,0 +1,104 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include + + +/* + * ioctl(FIONBIO) sets a blocking mode with the single syscall + * while fcntl(F_SETFL, ~O_NONBLOCK) needs to learn before + * the previous state using fcntl(F_GETFL). + * + * ioctl() and fcntl() are syscalls on at least FreeBSD 2.x, Linux 2.2 + * and Solaris 7. + * + * ioctl() in Linux 2.4 and 2.6 uses BKL, however fcntl(F_SETFL) uses it too. + */ + + +#if (HAVE_FIONBIO) + +int ngx_nonblocking(ngx_socket_t s) +{ + u_long nb; + + nb = 1; + + return ioctl(s, FIONBIO, &nb); +} + + +int ngx_blocking(ngx_socket_t s) +{ + u_long nb; + + nb = 0; + + return ioctl(s, FIONBIO, &nb); +} + +#endif + + +#ifdef __FreeBSD__ + +int ngx_tcp_nopush(ngx_socket_t s) +{ + int tcp_nopush; + + tcp_nopush = 1; + + return setsockopt(s, IPPROTO_TCP, TCP_NOPUSH, + (const void *) &tcp_nopush, sizeof(int)); +} + + +int ngx_tcp_push(ngx_socket_t s) +{ + int tcp_nopush; + + tcp_nopush = 0; + + return setsockopt(s, IPPROTO_TCP, TCP_NOPUSH, + (const void *) &tcp_nopush, sizeof(int)); +} + +#elif __linux__ + +int ngx_tcp_nopush(ngx_socket_t s) +{ + int cork; + + cork = 1; + + return setsockopt(s, IPPROTO_TCP, TCP_CORK, + (const void *) &cork, sizeof(int)); +} + +int ngx_tcp_push(ngx_socket_t s) +{ + int cork; + + cork = 0; + + return setsockopt(s, IPPROTO_TCP, TCP_CORK, + (const void *) &cork, sizeof(int)); +} + +#else + +int ngx_tcp_nopush(ngx_socket_t s) +{ + return NGX_OK; +} + +int ngx_tcp_push(ngx_socket_t s) +{ + return NGX_OK; +} + +#endif diff --git a/src/os/unix/ngx_socket.h b/src/os/unix/ngx_socket.h new file mode 100644 --- /dev/null +++ b/src/os/unix/ngx_socket.h @@ -0,0 +1,60 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#ifndef _NGX_SOCKET_H_INCLUDED_ +#define _NGX_SOCKET_H_INCLUDED_ + + +#include + + +#define NGX_WRITE_SHUTDOWN SHUT_WR + +typedef int ngx_socket_t; + +#define ngx_socket(af, type, proto, flags) socket(af, type, proto) +#define ngx_socket_n "socket()" + + +#if (HAVE_FIONBIO) + +int ngx_nonblocking(ngx_socket_t s); +int ngx_blocking(ngx_socket_t s); + +#define ngx_nonblocking_n "ioctl(FIONBIO)" +#define ngx_blocking_n "ioctl(!FIONBIO)" + +#else + +#define ngx_nonblocking(s) fcntl(s, F_SETFL, O_NONBLOCK) +#define ngx_nonblocking_n "fcntl(O_NONBLOCK)" + +#endif + +int ngx_tcp_nopush(ngx_socket_t s); +int ngx_tcp_push(ngx_socket_t s); + +#ifdef __linux__ + +#define ngx_tcp_nopush_n "setsockopt(TCP_CORK)" +#define ngx_tcp_push_n "setsockopt(!TCP_CORK)" + +#else + +#define ngx_tcp_nopush_n "setsockopt(TCP_NOPUSH)" +#define ngx_tcp_push_n "setsockopt(!TCP_NOPUSH)" + +#endif + + +#define ngx_shutdown_socket shutdown +#define ngx_shutdown_socket_n "shutdown()" + +#define ngx_close_socket close +#define ngx_close_socket_n "close()" + + +#endif /* _NGX_SOCKET_H_INCLUDED_ */ diff --git a/src/os/unix/ngx_solaris.h b/src/os/unix/ngx_solaris.h new file mode 100644 --- /dev/null +++ b/src/os/unix/ngx_solaris.h @@ -0,0 +1,15 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#ifndef _NGX_SOLARIS_H_INCLUDED_ +#define _NGX_SOLARIS_H_INCLUDED_ + + +ngx_chain_t *ngx_solaris_sendfilev_chain(ngx_connection_t *c, ngx_chain_t *in, + off_t limit); + + +#endif /* _NGX_SOLARIS_H_INCLUDED_ */ diff --git a/src/os/unix/ngx_solaris_config.h b/src/os/unix/ngx_solaris_config.h new file mode 100644 --- /dev/null +++ b/src/os/unix/ngx_solaris_config.h @@ -0,0 +1,86 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#ifndef _NGX_SOLARIS_CONFIG_H_INCLUDED_ +#define _NGX_SOLARIS_CONFIG_H_INCLUDED_ + + +#define SOLARIS 1 + +#define _REENTRANT + +#define _FILE_OFFSET_BITS 64 /* must be before */ + +#include +#include +#include +#include +#include /* offsetof() */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include /* FIONBIO */ +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include /* IOV_MAX */ +#include + +#include + + +#ifndef HAVE_SELECT +#define HAVE_SELECT 1 +#endif + + +#ifndef HAVE_POLL +#define HAVE_POLL 1 +#endif +#if (HAVE_POLL) +#include +#endif + + +#if (HAVE_AIO) +#include +#endif + + +#if (HAVE_DEVPOLL) +#include +#include +#endif + + +#ifndef HAVE_INHERITED_NONBLOCK +#define HAVE_INHERITED_NONBLOCK 1 +#endif + + +#define ngx_setproctitle(title) + + +#endif /* _NGX_SOLARIS_CONFIG_H_INCLUDED_ */ diff --git a/src/os/unix/ngx_solaris_init.c b/src/os/unix/ngx_solaris_init.c new file mode 100644 --- /dev/null +++ b/src/os/unix/ngx_solaris_init.c @@ -0,0 +1,70 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include + + +char ngx_solaris_sysname[20]; +char ngx_solaris_release[10]; +char ngx_solaris_version[50]; + + +ngx_os_io_t ngx_os_io = { + ngx_unix_recv, + ngx_readv_chain, + ngx_unix_send, +#if (HAVE_SENDFILE) + ngx_solaris_sendfilev_chain, + NGX_IO_SENDFILE +#else + ngx_writev_chain, + 0 +#endif +}; + + +ngx_int_t ngx_os_init(ngx_log_t *log) +{ + if (sysinfo(SI_SYSNAME, ngx_solaris_sysname, sizeof(ngx_solaris_sysname)) + == -1) + { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + "sysinfo(SI_SYSNAME) failed"); + return NGX_ERROR; + } + + if (sysinfo(SI_RELEASE, ngx_solaris_release, sizeof(ngx_solaris_release)) + == -1) + { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + "sysinfo(SI_RELEASE) failed"); + return NGX_ERROR; + } + + if (sysinfo(SI_VERSION, ngx_solaris_version, sizeof(ngx_solaris_version)) + == -1) + { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + "sysinfo(SI_SYSNAME) failed"); + return NGX_ERROR; + } + + return ngx_posix_init(log); +} + + +void ngx_os_status(ngx_log_t *log) +{ + + ngx_log_error(NGX_LOG_INFO, log, 0, "OS: %s %s", + ngx_solaris_sysname, ngx_solaris_release); + + ngx_log_error(NGX_LOG_INFO, log, 0, "version: %s", + ngx_solaris_version); + + ngx_posix_status(log); +} diff --git a/src/os/unix/ngx_solaris_sendfilev_chain.c b/src/os/unix/ngx_solaris_sendfilev_chain.c new file mode 100644 --- /dev/null +++ b/src/os/unix/ngx_solaris_sendfilev_chain.c @@ -0,0 +1,206 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include + + +#define NGX_SENDFILEVECS 16 + + +ngx_chain_t *ngx_solaris_sendfilev_chain(ngx_connection_t *c, ngx_chain_t *in, + off_t limit) +{ + int fd; + u_char *prev; + off_t fprev, sprev, send, aligned; + ssize_t size, sent, n; + ngx_int_t eintr, complete; + ngx_err_t err; + sendfilevec_t *sfv, sfvs[NGX_SENDFILEVECS]; + ngx_array_t vec; + ngx_event_t *wev; + ngx_chain_t *cl, *tail; + + wev = c->write; + + if (!wev->ready) { + return in; + } + + send = 0; + complete = 0; + + vec.elts = sfvs; + vec.size = sizeof(sendfilevec_t); + vec.nalloc = NGX_SENDFILEVECS; + vec.pool = c->pool; + + for ( ;; ) { + fd = SFV_FD_SELF; + prev = NULL; + fprev = 0; + sfv = NULL; + eintr = 0; + sent = 0; + sprev = send; + + vec.nelts = 0; + + /* create the sendfilevec and coalesce the neighbouring bufs */ + + for (cl = in; cl && vec.nelts < IOV_MAX && send < limit; cl = cl->next) + { + if (ngx_buf_special(cl->buf)) { + continue; + } + + if (ngx_buf_in_memory_only(cl->buf)) { + fd = SFV_FD_SELF; + + size = cl->buf->last - cl->buf->pos; + + if (send + size > limit) { + size = limit - send; + } + + if (prev == cl->buf->pos) { + sfv->sfv_len += size; + + } else { + if (!(sfv = ngx_array_push(&vec))) { + return NGX_CHAIN_ERROR; + } + + sfv->sfv_fd = SFV_FD_SELF; + sfv->sfv_flag = 0; + sfv->sfv_off = (off_t) (uintptr_t) cl->buf->pos; + sfv->sfv_len = size; + } + + prev = cl->buf->pos + size; + send += size; + + } else { + prev = NULL; + + size = (size_t) (cl->buf->file_last - cl->buf->file_pos); + + if (send + size > limit) { + size = limit - send; + + aligned = (cl->buf->file_pos + size + ngx_pagesize - 1) + & ~(ngx_pagesize - 1); + + if (aligned <= cl->buf->file_last) { + size = aligned - cl->buf->file_pos; + } + } + + if (fd == cl->buf->file->fd && fprev == cl->buf->file_pos) { + sfv->sfv_len += size; + + } else { + if (!(sfv = ngx_array_push(&vec))) { + return NGX_CHAIN_ERROR; + } + + fd = cl->buf->file->fd; + sfv->sfv_fd = fd; + sfv->sfv_flag = 0; + sfv->sfv_off = cl->buf->file_pos; + sfv->sfv_len = size; + } + + fprev = cl->buf->file_pos + size; + send += size; + } + } + + n = sendfilev(c->fd, vec.elts, vec.nelts, &sent); + + if (n == -1) { + err = ngx_errno; + + if (err == NGX_EAGAIN || err == NGX_EINTR) { + if (err == NGX_EINTR) { + eintr = 1; + } + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, err, + "sendfilev() sent only " SIZE_T_FMT " bytes", + sent); + + } else { + wev->error = 1; + ngx_connection_error(c, err, "sendfilev() failed"); + return NGX_CHAIN_ERROR; + } + } + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, + "sendfilev: %d " SIZE_T_FMT, n, sent); + + if (send - sprev == 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 += 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 --git a/src/os/unix/ngx_thread.h b/src/os/unix/ngx_thread.h new file mode 100644 --- /dev/null +++ b/src/os/unix/ngx_thread.h @@ -0,0 +1,128 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#ifndef _NGX_THREAD_H_INCLUDED_ +#define _NGX_THREAD_H_INCLUDED_ + + +#include +#include + +#if (NGX_THREADS) + +#define NGX_MAX_THREADS 128 + +#if (NGX_USE_RFORK) +#include + + +#else /* use pthreads */ + +#include + +typedef pthread_t ngx_tid_t; + +#define ngx_thread_self() pthread_self() +#define ngx_log_tid (int) ngx_thread_self() + +#if defined(__FreeBSD__) && !defined(NGX_LINUXTHREADS) +#define TID_T_FMT PTR_FMT +#else +#define TID_T_FMT "%d" +#endif + + +typedef pthread_key_t ngx_tls_key_t; + +#define ngx_thread_key_create(key) pthread_key_create(key, NULL) +#define ngx_thread_key_create_n "pthread_key_create()" +#define ngx_thread_set_tls pthread_setspecific +#define ngx_thread_set_tls_n "pthread_setspecific()" +#define ngx_thread_get_tls pthread_getspecific + + +#define NGX_MUTEX_LIGHT 0 + +typedef struct { + pthread_mutex_t mutex; + ngx_log_t *log; +} ngx_mutex_t; + +typedef struct { + pthread_cond_t cond; + ngx_tid_t tid; + ngx_log_t *log; +} ngx_cond_t; + +#define ngx_thread_sigmask pthread_sigmask +#define ngx_thread_sigmask_n "pthread_sigmask()" + +#define ngx_thread_join(t, p) pthread_join(t, p) + +#define ngx_setthrtitle(n) + + + +ngx_int_t ngx_mutex_trylock(ngx_mutex_t *m); +ngx_int_t ngx_mutex_lock(ngx_mutex_t *m); +ngx_int_t ngx_mutex_unlock(ngx_mutex_t *m); + +#endif + + +#define ngx_thread_volatile volatile + + +typedef struct { + ngx_tid_t tid; + ngx_cond_t *cv; + ngx_uint_t state; +} ngx_thread_t; + +#define NGX_THREAD_FREE 1 +#define NGX_THREAD_BUSY 2 +#define NGX_THREAD_EXIT 3 +#define NGX_THREAD_DONE 4 + +extern ngx_int_t ngx_threads_n; +extern volatile ngx_thread_t ngx_threads[NGX_MAX_THREADS]; + + +ngx_int_t ngx_init_threads(int n, size_t size, ngx_cycle_t *cycle); +int ngx_create_thread(ngx_tid_t *tid, void* (*func)(void *arg), void *arg, + ngx_log_t *log); + + +ngx_mutex_t *ngx_mutex_init(ngx_log_t *log, uint flags); +void ngx_mutex_destroy(ngx_mutex_t *m); + + +ngx_cond_t *ngx_cond_init(ngx_log_t *log); +void ngx_cond_destroy(ngx_cond_t *cv); +ngx_int_t ngx_cond_wait(ngx_cond_t *cv, ngx_mutex_t *m); +ngx_int_t ngx_cond_signal(ngx_cond_t *cv); + + +#else /* !NGX_THREADS */ + +#define ngx_thread_volatile + +#define ngx_log_tid 0 +#define TID_T_FMT "%d" + +#define ngx_mutex_trylock(m) NGX_OK +#define ngx_mutex_lock(m) NGX_OK +#define ngx_mutex_unlock(m) + +#define ngx_cond_signal(cv) + +#define ngx_thread_main() 1 + +#endif + + + +#endif /* _NGX_THREAD_H_INCLUDED_ */ diff --git a/src/os/unix/ngx_time.c b/src/os/unix/ngx_time.c new file mode 100644 --- /dev/null +++ b/src/os/unix/ngx_time.c @@ -0,0 +1,31 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include + + +void ngx_localtime(ngx_tm_t *tm) +{ +#if (HAVE_LOCALTIME_R) + time_t now; + + now = ngx_time(); + localtime_r(&now, tm); + +#else + time_t now; + ngx_tm_t *t; + + now = ngx_time(); + t = localtime(&now); + *tm = *t; + +#endif + + tm->ngx_tm_mon++; + tm->ngx_tm_year += 1900; +} diff --git a/src/os/unix/ngx_time.h b/src/os/unix/ngx_time.h new file mode 100644 --- /dev/null +++ b/src/os/unix/ngx_time.h @@ -0,0 +1,56 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#ifndef _NGX_TIME_H_INCLUDED_ +#define _NGX_TIME_H_INCLUDED_ + + +#include +#include + + +typedef uint64_t ngx_epoch_msec_t; + +typedef ngx_int_t ngx_msec_t; + +typedef struct tm ngx_tm_t; + +#define ngx_tm_sec tm_sec +#define ngx_tm_min tm_min +#define ngx_tm_hour tm_hour +#define ngx_tm_mday tm_mday +#define ngx_tm_mon tm_mon +#define ngx_tm_year tm_year +#define ngx_tm_wday tm_wday +#define ngx_tm_isdst tm_isdst + +#define ngx_tm_sec_t int +#define ngx_tm_min_t int +#define ngx_tm_hour_t int +#define ngx_tm_mday_t int +#define ngx_tm_mon_t int +#define ngx_tm_year_t int +#define ngx_tm_wday_t int + + +#if (HAVE_GMTOFF) +#define ngx_tm_gmtoff tm_gmtoff +#define ngx_tm_zone tm_zone +#endif + + +#if (SOLARIS) +#define ngx_timezone(isdst) (- (isdst ? altzone : timezone) / 60) +#endif + + +void ngx_localtime(ngx_tm_t *tm); + +#define ngx_gettimeofday(tp) gettimeofday(tp, NULL); +#define ngx_msleep(ms) usleep(ms * 1000) + + +#endif /* _NGX_TIME_H_INCLUDED_ */ diff --git a/src/os/unix/ngx_types.h b/src/os/unix/ngx_types.h new file mode 100644 --- /dev/null +++ b/src/os/unix/ngx_types.h @@ -0,0 +1,26 @@ + +/* + * 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; + unsigned info_valid; +} ngx_dir_t; + + +#endif /* _NGX_TYPES_H_INCLUDED_ */ diff --git a/src/os/unix/ngx_user.h b/src/os/unix/ngx_user.h new file mode 100644 --- /dev/null +++ b/src/os/unix/ngx_user.h @@ -0,0 +1,19 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#ifndef _NGX_USER_H_INCLUDED_ +#define _NGX_USER_H_INCLUDED_ + + +#include +#include + + +typedef uid_t ngx_uid_t; +typedef gid_t ngx_gid_t; + + +#endif /* _NGX_USER_H_INCLUDED_ */ diff --git a/src/os/unix/ngx_writev_chain.c b/src/os/unix/ngx_writev_chain.c new file mode 100644 --- /dev/null +++ b/src/os/unix/ngx_writev_chain.c @@ -0,0 +1,160 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include + + +#define NGX_IOVS 8 + + +ngx_chain_t *ngx_writev_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit) +{ + u_char *prev; + ssize_t n, size; + off_t send, sprev, sent; + ngx_uint_t eintr, complete; + ngx_err_t err; + ngx_array_t vec; + ngx_chain_t *cl; + ngx_event_t *wev; + struct iovec *iov, iovs[NGX_IOVS]; + + wev = c->write; + + if (!wev->ready) { + return in; + } + +#if (HAVE_KQUEUE) + + if ((ngx_event_flags & NGX_HAVE_KQUEUE_EVENT) && wev->pending_eof) { + ngx_log_error(NGX_LOG_INFO, c->log, wev->kq_errno, + "kevent() reported about an closed connection"); + + wev->error = 1; + return NGX_CHAIN_ERROR; + } + +#endif + + send = 0; + complete = 0; + + vec.elts = iovs; + vec.size = sizeof(struct iovec); + vec.nalloc = NGX_IOVS; + vec.pool = c->pool; + + for ( ;; ) { + prev = NULL; + iov = NULL; + eintr = 0; + sprev = send; + + vec.nelts = 0; + + /* create the iovec and coalesce the neighbouring bufs */ + + for (cl = in; cl && vec.nelts < IOV_MAX && send < limit; cl = cl->next) + { + if (ngx_buf_special(cl->buf)) { + continue; + } + + size = cl->buf->last - cl->buf->pos; + + if (send + size > limit) { + size = limit - send; + } + + if (prev == cl->buf->pos) { + iov->iov_len += size; + + } else { + if (!(iov = ngx_array_push(&vec))) { + return NGX_CHAIN_ERROR; + } + + iov->iov_base = (void *) cl->buf->pos; + iov->iov_len = size; + } + + prev = cl->buf->pos + size; + send += size; + } + + n = writev(c->fd, vec.elts, vec.nelts); + + if (n == -1) { + err = ngx_errno; + + if (err == NGX_EAGAIN || err == NGX_EINTR) { + if (err == NGX_EINTR) { + eintr = 1; + } + + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err, + "writev() not ready"); + + } else { + wev->error = 1; + ngx_connection_error(c, err, "writev() failed"); + return NGX_CHAIN_ERROR; + } + } + + sent = n > 0 ? n : 0; + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, + "writev: " OFF_T_FMT, sent); + + if (send - sprev == sent) { + complete = 1; + } + + c->sent += sent; + + for (cl = in; cl && sent > 0; cl = cl->next) { + if (ngx_buf_special(cl->buf)) { + continue; + } + + if (sent == 0) { + break; + } + + size = cl->buf->last - cl->buf->pos; + + if (sent >= size) { + sent -= size; + cl->buf->pos = cl->buf->last; + + continue; + } + + cl->buf->pos += sent; + + break; + } + + if (eintr) { + continue; + } + + if (!complete) { + wev->ready = 0; + return cl; + } + + if (send >= limit || cl == NULL) { + return cl; + } + + in = cl; + } +} diff --git a/src/os/unix/rfork_thread.S b/src/os/unix/rfork_thread.S new file mode 100644 --- /dev/null +++ b/src/os/unix/rfork_thread.S @@ -0,0 +1,75 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + + +#include +#include + +/* + * rfork_thread(3) - rfork_thread(flags, stack, func, arg); + */ + +#define KERNCALL int $0x80 + +ENTRY(rfork_thread) + push %ebp + mov %esp, %ebp + push %esi + + mov 12(%ebp), %esi # the stack address + + sub $4, %esi + mov 20(%ebp), %eax # the thread argument + mov %eax, (%esi) + + sub $4, %esi + mov 16(%ebp), %eax # the start thread address + mov %eax, (%esi) + + push 8(%ebp) # rfork(2) flags + push $0 + mov $SYS_rfork, %eax + KERNCALL + jc error + + cmp $0, %edx + jne child + +parent: + add $8, %esp + pop %esi + mov %ebp, %esp + pop %ebp + ret + +child: + mov %esi, %esp + pop %eax + call *%eax # call a thread start address ... + add $4, %esp + + push %eax + push $0 + mov $SYS_exit, %eax # ... and exit(2) after a thread would return + KERNCALL + +error: + add $8, %esp + pop %esi + mov %ebp, %esp + pop %ebp + PIC_PROLOGUE + + /* libc's cerror: jmp PIC_PLT(HIDENAME(cerror)) */ + + push %eax + call PIC_PLT(CNAME(__error)) + pop %ecx + PIC_EPILOGUE + mov %ecx, (%eax) + mov $-1, %eax + mov $-1, %edx + ret