summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIgor Sysoev <igor@sysoev.ru>2004-10-05 15:39:18 +0000
committerJonathan Kolb <jon@b0g.us>2004-10-05 15:39:18 +0000
commit7d66b1f69bb81e48c7efb79884771ea66b3685ca (patch)
treee0cf023d6eebb4e422e62c5403e74e8218dc3192
downloadnginx-7d66b1f69bb81e48c7efb79884771ea66b3685ca.tar.gz
The first public versionv0.1.0
-rw-r--r--LICENSE25
-rw-r--r--README3
-rw-r--r--auto/cc376
-rw-r--r--auto/define11
-rw-r--r--auto/endianess41
-rw-r--r--auto/feature56
-rw-r--r--auto/fmt/fmt80
-rw-r--r--auto/fmt/ptrfmt72
-rw-r--r--auto/fmt/xfmt11
-rw-r--r--auto/func43
-rw-r--r--auto/have11
-rw-r--r--auto/headers6
-rw-r--r--auto/inc35
-rw-r--r--auto/init38
-rw-r--r--auto/install65
-rw-r--r--auto/lib/conf19
-rw-r--r--auto/lib/make19
-rw-r--r--auto/lib/md5/conf105
-rw-r--r--auto/lib/md5/make98
-rw-r--r--auto/lib/md5/makefile.bcc19
-rw-r--r--auto/lib/md5/makefile.msvc19
-rw-r--r--auto/lib/md5/makefile.owc9
-rw-r--r--auto/lib/openssl/conf43
-rw-r--r--auto/lib/openssl/make16
-rw-r--r--auto/lib/pcre/conf86
-rw-r--r--auto/lib/pcre/make65
-rw-r--r--auto/lib/pcre/makefile.bcc20
-rw-r--r--auto/lib/pcre/makefile.msvc22
-rw-r--r--auto/lib/pcre/makefile.owc19
-rw-r--r--auto/lib/pcre/patch.config.in11
-rw-r--r--auto/lib/pcre/patch.pcre.in15
-rw-r--r--auto/lib/test27
-rw-r--r--auto/lib/zlib/conf60
-rw-r--r--auto/lib/zlib/make107
-rw-r--r--auto/lib/zlib/makefile.bcc11
-rw-r--r--auto/lib/zlib/makefile.msvc11
-rw-r--r--auto/lib/zlib/makefile.owc9
-rw-r--r--auto/make360
-rw-r--r--auto/modules167
-rw-r--r--auto/nohave11
-rw-r--r--auto/options260
-rw-r--r--auto/os/conf57
-rw-r--r--auto/os/freebsd89
-rw-r--r--auto/os/linux82
-rw-r--r--auto/os/solaris83
-rw-r--r--auto/sources307
-rw-r--r--auto/summary98
-rw-r--r--auto/threads63
-rw-r--r--auto/types/sizeof62
-rw-r--r--auto/types/typedef60
-rw-r--r--auto/types/uintptr_t40
-rw-r--r--auto/types/value9
-rwxr-xr-xauto/unix199
-rw-r--r--conf/koi-win85
-rw-r--r--conf/mime.types24
-rw-r--r--conf/nginx.conf37
-rwxr-xr-xconfigure54
-rw-r--r--html/index.html8
-rw-r--r--src/core/nginx.c503
-rw-r--r--src/core/nginx.h17
-rw-r--r--src/core/ngx_array.c74
-rw-r--r--src/core/ngx_array.h54
-rw-r--r--src/core/ngx_buf.c162
-rw-r--r--src/core/ngx_buf.h153
-rw-r--r--src/core/ngx_conf_file.c1012
-rw-r--r--src/core/ngx_conf_file.h295
-rw-r--r--src/core/ngx_config.h131
-rw-r--r--src/core/ngx_connection.c453
-rw-r--r--src/core/ngx_connection.h152
-rw-r--r--src/core/ngx_core.h78
-rw-r--r--src/core/ngx_crc.h33
-rw-r--r--src/core/ngx_cycle.c767
-rw-r--r--src/core/ngx_cycle.h79
-rw-r--r--src/core/ngx_file.c237
-rw-r--r--src/core/ngx_file.h82
-rw-r--r--src/core/ngx_garbage_collector.c279
-rw-r--r--src/core/ngx_garbage_collector.h30
-rw-r--r--src/core/ngx_inet.c273
-rw-r--r--src/core/ngx_inet.h24
-rw-r--r--src/core/ngx_list.c41
-rw-r--r--src/core/ngx_list.h79
-rw-r--r--src/core/ngx_log.c410
-rw-r--r--src/core/ngx_log.h210
-rw-r--r--src/core/ngx_output_chain.c312
-rw-r--r--src/core/ngx_palloc.c215
-rw-r--r--src/core/ngx_palloc.h55
-rw-r--r--src/core/ngx_parse.c212
-rw-r--r--src/core/ngx_parse.h22
-rw-r--r--src/core/ngx_rbtree.c349
-rw-r--r--src/core/ngx_rbtree.h43
-rw-r--r--src/core/ngx_regex.c125
-rw-r--r--src/core/ngx_regex.h30
-rw-r--r--src/core/ngx_spinlock.c48
-rw-r--r--src/core/ngx_string.c284
-rw-r--r--src/core/ngx_string.h94
-rw-r--r--src/core/ngx_table.h27
-rw-r--r--src/core/ngx_times.c346
-rw-r--r--src/core/ngx_times.h58
-rw-r--r--src/event/modules/ngx_aio_module.c209
-rw-r--r--src/event/modules/ngx_aio_module.h20
-rw-r--r--src/event/modules/ngx_devpoll_module.c578
-rw-r--r--src/event/modules/ngx_epoll_module.c657
-rw-r--r--src/event/modules/ngx_kqueue_module.c800
-rw-r--r--src/event/modules/ngx_kqueue_module.h17
-rw-r--r--src/event/modules/ngx_poll_module.c605
-rw-r--r--src/event/modules/ngx_rtsig_module.c814
-rw-r--r--src/event/modules/ngx_select_module.c616
-rw-r--r--src/event/ngx_event.c805
-rw-r--r--src/event/ngx_event.h637
-rw-r--r--src/event/ngx_event_accept.c475
-rw-r--r--src/event/ngx_event_busy_lock.c309
-rw-r--r--src/event/ngx_event_busy_lock.h64
-rw-r--r--src/event/ngx_event_connect.c379
-rw-r--r--src/event/ngx_event_connect.h67
-rw-r--r--src/event/ngx_event_mutex.c70
-rw-r--r--src/event/ngx_event_openssl.c378
-rw-r--r--src/event/ngx_event_openssl.h57
-rw-r--r--src/event/ngx_event_pipe.c771
-rw-r--r--src/event/ngx_event_pipe.h93
-rw-r--r--src/event/ngx_event_posted.c169
-rw-r--r--src/event/ngx_event_posted.h55
-rw-r--r--src/event/ngx_event_timer.c165
-rw-r--r--src/event/ngx_event_timer.h120
-rw-r--r--src/http/modules/ngx_http_access_handler.c213
-rw-r--r--src/http/modules/ngx_http_charset_filter.c553
-rw-r--r--src/http/modules/ngx_http_chunked_filter.c156
-rw-r--r--src/http/modules/ngx_http_gzip_filter.c1036
-rw-r--r--src/http/modules/ngx_http_headers_filter.c239
-rw-r--r--src/http/modules/ngx_http_index_handler.c529
-rw-r--r--src/http/modules/ngx_http_not_modified_filter.c85
-rw-r--r--src/http/modules/ngx_http_range_filter.c523
-rw-r--r--src/http/modules/ngx_http_rewrite_handler.c466
-rw-r--r--src/http/modules/ngx_http_ssl_module.c160
-rw-r--r--src/http/modules/ngx_http_ssl_module.h36
-rw-r--r--src/http/modules/ngx_http_static_handler.c584
-rw-r--r--src/http/modules/ngx_http_userid_filter.c588
-rw-r--r--src/http/modules/proxy/ngx_http_proxy_cache.c629
-rw-r--r--src/http/modules/proxy/ngx_http_proxy_handler.c1280
-rw-r--r--src/http/modules/proxy/ngx_http_proxy_handler.h260
-rw-r--r--src/http/modules/proxy/ngx_http_proxy_header.c198
-rw-r--r--src/http/modules/proxy/ngx_http_proxy_parse.c213
-rw-r--r--src/http/modules/proxy/ngx_http_proxy_upstream.c1492
-rw-r--r--src/http/ngx_http.c642
-rw-r--r--src/http/ngx_http.h112
-rw-r--r--src/http/ngx_http_busy_lock.c300
-rw-r--r--src/http/ngx_http_busy_lock.h53
-rw-r--r--src/http/ngx_http_cache.c480
-rw-r--r--src/http/ngx_http_cache.h131
-rw-r--r--src/http/ngx_http_config.h70
-rw-r--r--src/http/ngx_http_copy_filter.c132
-rw-r--r--src/http/ngx_http_core_module.c1819
-rw-r--r--src/http/ngx_http_core_module.h214
-rw-r--r--src/http/ngx_http_file_cache.c239
-rw-r--r--src/http/ngx_http_header_filter.c459
-rw-r--r--src/http/ngx_http_log_handler.c978
-rw-r--r--src/http/ngx_http_log_handler.h65
-rw-r--r--src/http/ngx_http_parse.c868
-rw-r--r--src/http/ngx_http_parse_time.c287
-rw-r--r--src/http/ngx_http_request.c2149
-rw-r--r--src/http/ngx_http_request.h372
-rw-r--r--src/http/ngx_http_request_body.c227
-rw-r--r--src/http/ngx_http_special_response.c358
-rw-r--r--src/http/ngx_http_write_filter.c166
-rw-r--r--src/os/unix/ngx_aio.h21
-rw-r--r--src/os/unix/ngx_aio_read.c117
-rw-r--r--src/os/unix/ngx_aio_read_chain.c78
-rw-r--r--src/os/unix/ngx_aio_write.c117
-rw-r--r--src/os/unix/ngx_aio_write_chain.c96
-rw-r--r--src/os/unix/ngx_alloc.c80
-rw-r--r--src/os/unix/ngx_alloc.h42
-rw-r--r--src/os/unix/ngx_atomic.h155
-rw-r--r--src/os/unix/ngx_channel.c237
-rw-r--r--src/os/unix/ngx_channel.h33
-rw-r--r--src/os/unix/ngx_daemon.c68
-rw-r--r--src/os/unix/ngx_errno.c64
-rw-r--r--src/os/unix/ngx_errno.h60
-rw-r--r--src/os/unix/ngx_files.c268
-rw-r--r--src/os/unix/ngx_files.h115
-rw-r--r--src/os/unix/ngx_freebsd.h24
-rw-r--r--src/os/unix/ngx_freebsd_config.h124
-rw-r--r--src/os/unix/ngx_freebsd_init.c225
-rw-r--r--src/os/unix/ngx_freebsd_rfork_thread.c738
-rw-r--r--src/os/unix/ngx_freebsd_rfork_thread.h121
-rw-r--r--src/os/unix/ngx_freebsd_sendfile_chain.c380
-rw-r--r--src/os/unix/ngx_linux.h17
-rw-r--r--src/os/unix/ngx_linux_config.h101
-rw-r--r--src/os/unix/ngx_linux_init.c80
-rw-r--r--src/os/unix/ngx_linux_sendfile_chain.c285
-rw-r--r--src/os/unix/ngx_os.h84
-rw-r--r--src/os/unix/ngx_posix_config.h62
-rw-r--r--src/os/unix/ngx_posix_init.c299
-rw-r--r--src/os/unix/ngx_process.c283
-rw-r--r--src/os/unix/ngx_process.h67
-rw-r--r--src/os/unix/ngx_process_cycle.c970
-rw-r--r--src/os/unix/ngx_process_cycle.h56
-rw-r--r--src/os/unix/ngx_pthread_thread.c273
-rw-r--r--src/os/unix/ngx_readv_chain.c219
-rw-r--r--src/os/unix/ngx_recv.c173
-rw-r--r--src/os/unix/ngx_send.c70
-rw-r--r--src/os/unix/ngx_shared.c96
-rw-r--r--src/os/unix/ngx_shared.h18
-rw-r--r--src/os/unix/ngx_socket.c104
-rw-r--r--src/os/unix/ngx_socket.h60
-rw-r--r--src/os/unix/ngx_solaris.h15
-rw-r--r--src/os/unix/ngx_solaris_config.h86
-rw-r--r--src/os/unix/ngx_solaris_init.c70
-rw-r--r--src/os/unix/ngx_solaris_sendfilev_chain.c206
-rw-r--r--src/os/unix/ngx_thread.h128
-rw-r--r--src/os/unix/ngx_time.c31
-rw-r--r--src/os/unix/ngx_time.h56
-rw-r--r--src/os/unix/ngx_types.h26
-rw-r--r--src/os/unix/ngx_user.h19
-rw-r--r--src/os/unix/ngx_writev_chain.c160
-rw-r--r--src/os/unix/rfork_thread.S75
214 files changed, 47329 insertions, 0 deletions
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 000000000..f057c0b8e
--- /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
index 000000000..626f0a53e
--- /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
index 000000000..eff642125
--- /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
index 000000000..971f99df2
--- /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
index 000000000..4df7d01f1
--- /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
index 000000000..bd5b08158
--- /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 <sys/types.h>
+$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
index 000000000..980641ffa
--- /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 <sys/types.h>
+#include <sys/time.h>
+#include <stdio.h>
+#include <sys/resource.h>
+$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
index 000000000..163ca59e2
--- /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
index 000000000..957e59030
--- /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
index 000000000..970d63403
--- /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 <sys/types.h>
+$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
index 000000000..1b644ad32
--- /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
index 000000000..2bfa685c9
--- /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
index 000000000..867102223
--- /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
index 000000000..ebca44424
--- /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
index 000000000..b838b9b2c
--- /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
index 000000000..275bea9cf
--- /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
index 000000000..ef571c5b8
--- /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
index 000000000..748bea450
--- /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 <sys/types.h>
+#include <md5.h>"
+ 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 <openssl/md5.h>"
+ 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
index 000000000..f718e6c2c
--- /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
index 000000000..837f2dff7
--- /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
index 000000000..68f5cc49b
--- /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
index 000000000..5fffa1707
--- /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
index 000000000..37c084f00
--- /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 <openssl/ssl.h>"
+ 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
index 000000000..5e931cbbe
--- /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
index 000000000..16feffa6b
--- /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 <pcre.h>"
+ 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
index 000000000..fa7bf27d0
--- /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
index 000000000..411945260
--- /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
index 000000000..7c61d31f7
--- /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
index 000000000..227fd90af
--- /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
index 000000000..d28b6feb1
--- /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
index 000000000..c49a68f7b
--- /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
index 000000000..7dfaafef1
--- /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
index 000000000..a01eebe71
--- /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 <zlib.h>"
+ 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
index 000000000..0ced9a25c
--- /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
index 000000000..95e8800ad
--- /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
index 000000000..304b986cd
--- /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
index 000000000..71ba512dc
--- /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
index 000000000..fee8a6f24
--- /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 <ngx_config.h>" > $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
index 000000000..6c5111a28
--- /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_config.h>" > $NGX_MODULES_C
+echo "#include <ngx_core.h>" >> $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
index 000000000..bb5632aaf
--- /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
index 000000000..259c049ad
--- /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
index 000000000..abffbe3d8
--- /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
index 000000000..90cb49702
--- /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
index 000000000..8576ea680
--- /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 <sys/epoll.h>"
+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 <sys/sendfile.h>"
+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 <sys/prctl.h>"
+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
index 000000000..29497ff5f
--- /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 <sys/sendfile.h>"
+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
index 000000000..40fbad223
--- /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
index 000000000..45c68e0d5
--- /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=<path> 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=<path> 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
index 000000000..9aaea5158
--- /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
index 000000000..ac22e759d
--- /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 <sys/types.h>
+#include <sys/time.h>
+$NGX_UNISTD_H
+#include <signal.h>
+#include <sys/resource.h>
+$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
index 000000000..9762f4812
--- /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 <sys/types.h>
+#include <signal.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <netinet/in.h>
+$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
index 000000000..a1bb78a89
--- /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 <sys/types.h>
+$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
index 000000000..d823ab323
--- /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
index 000000000..4ba7d605f
--- /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 <string.h>"
+#ngx_func_test="char *s = strsignal(1)"
+#. auto/func
+
+
+ngx_func="strerror_r()"
+ngx_func_inc="#include <string.h>"
+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 <string.h>"
+ngx_func_test="char buf[20], *str; str = strerror_r(1, buf, 20)"
+. auto/func
+
+
+ngx_func="localtime_r()"
+ngx_func_inc="#include <time.h>"
+ngx_func_test="struct tm t; time_t c=0; localtime_r(&c, &t)"
+. auto/func
+
+
+ngx_func="posix_memalign()"
+ngx_func_inc="#include <stdlib.h>"
+ngx_func_test="void *p; int n; n = posix_memalign(&p, 4096, 4096)"
+. auto/func
+
+
+ngx_func="memalign()"
+ngx_func_inc="#include <stdlib.h>"
+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 <sys/mman.h>"
+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 <sys/mman.h>
+#include <sys/stat.h>
+#include <fcntl.h>"
+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 <sys/ipc.h>
+#include <sys/shm.h>"
+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 <sys/socket.h>
+#include <netinet/in.h>"
+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 <sys/socket.h>"
+ngx_feature_test="struct msghdr msg; msg.msg_control = NULL"
+. auto/feature
+
+
+case $PLATFORM in
+ Linux:*)
+ ngx_feature_inc="#include <sys/ioctl.h>"
+ ;;
+
+ *)
+ ngx_feature_inc="#include <sys/filio.h>"
+ ;;
+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 <time.h>"
+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
index 000000000..92cabb140
--- /dev/null
+++ b/conf/koi-win
@@ -0,0 +1,85 @@
+
+charset_map koi8-r windows-1251 {
+
+ 95 95 ; # bullet
+
+ 9A A0 ; # &nbsp;
+
+ 9C B0 ; # &deg;
+
+ 9E B7 ; # &middot;
+
+ 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
index 000000000..f8df51cb2
--- /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
index 000000000..00025101a
--- /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
index 000000000..6a81555d1
--- /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
index 000000000..1f7927539
--- /dev/null
+++ b/html/index.html
@@ -0,0 +1,8 @@
+<html>
+<head>
+<title>Welcome to nginx!</title>
+</head>
+<body bgcolor="white" text="black">
+<center><h1>Welcome to nginx!</h1></center>
+</body>
+</html>
diff --git a/src/core/nginx.c b/src/core/nginx.c
new file mode 100644
index 000000000..18f3cccb4
--- /dev/null
+++ b/src/core/nginx.c
@@ -0,0 +1,503 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+#include <nginx.h>
+
+
+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
index 000000000..66b5c3356
--- /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
index 000000000..6eae76ffd
--- /dev/null
+++ b/src/core/ngx_array.c
@@ -0,0 +1,74 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+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
index 000000000..826395de6
--- /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 <ngx_config.h>
+#include <ngx_core.h>
+
+
+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
index 000000000..72b558bc8
--- /dev/null
+++ b/src/core/ngx_buf.c
@@ -0,0 +1,162 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+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
index 000000000..3e014d17a
--- /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 <ngx_config.h>
+#include <ngx_core.h>
+
+
+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
index 000000000..90a09d231
--- /dev/null
+++ b/src/core/ngx_conf_file.c
@@ -0,0 +1,1012 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+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
index 000000000..fc0533760
--- /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 <ngx_config.h>
+#include <ngx_core.h>
+
+
+/*
+ * 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
index 000000000..7e3e6828c
--- /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 <ngx_freebsd_config.h>
+
+
+#elif defined __linux__
+#include <ngx_linux_config.h>
+
+
+ /* Solaris */
+#elif defined sun && (defined __svr4__ || defined __SVR4)
+#include <ngx_solaris_config.h>
+
+
+#elif defined _WIN32
+#include <ngx_win32_config.h>
+
+
+#else /* posix */
+#include <ngx_posix_config.h>
+
+#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
index 000000000..698342fbd
--- /dev/null
+++ b/src/core/ngx_connection.c
@@ -0,0 +1,453 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+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
index 000000000..7a0345099
--- /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 <ngx_config.h>
+#include <ngx_core.h>
+
+
+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
index 000000000..02a4a17b9
--- /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 <ngx_atomic.h>
+#include <ngx_time.h>
+#include <ngx_socket.h>
+#include <ngx_errno.h>
+#include <ngx_types.h>
+#include <ngx_shared.h>
+#include <ngx_process.h>
+#include <ngx_thread.h>
+#include <ngx_user.h>
+#include <ngx_string.h>
+#include <ngx_parse.h>
+#include <ngx_log.h>
+#include <ngx_alloc.h>
+#include <ngx_palloc.h>
+#include <ngx_buf.h>
+#include <ngx_array.h>
+#include <ngx_list.h>
+#include <ngx_table.h>
+#include <ngx_file.h>
+#include <ngx_files.h>
+#include <ngx_crc.h>
+#if (HAVE_PCRE)
+#include <ngx_regex.h>
+#endif
+#include <ngx_rbtree.h>
+#include <ngx_times.h>
+#include <ngx_inet.h>
+#include <ngx_cycle.h>
+#include <ngx_process_cycle.h>
+#include <ngx_conf_file.h>
+#include <ngx_os.h>
+#if (NGX_OPENSSL)
+#include <ngx_event_openssl.h>
+#endif
+#include <ngx_connection.h>
+
+
+#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
index 000000000..b78f479e4
--- /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
index 000000000..ed4d60f89
--- /dev/null
+++ b/src/core/ngx_cycle.c
@@ -0,0 +1,767 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+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
index 000000000..45b7b23b0
--- /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 <ngx_config.h>
+#include <ngx_core.h>
+
+
+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
index 000000000..c7ee51372
--- /dev/null
+++ b/src/core/ngx_file.c
@@ -0,0 +1,237 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+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
index 000000000..9a6fe6efa
--- /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 <ngx_config.h>
+#include <ngx_core.h>
+
+typedef struct ngx_path_s ngx_path_t;
+
+#include <ngx_garbage_collector.h>
+
+
+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
index 000000000..d73ca959f
--- /dev/null
+++ b/src/core/ngx_garbage_collector.c
@@ -0,0 +1,279 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_garbage_collector.h>
+
+
+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
index 000000000..72f97604d
--- /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
index 000000000..bf8a788ea
--- /dev/null
+++ b/src/core/ngx_inet.c
@@ -0,0 +1,273 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+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
index 000000000..d0e1f8dfb
--- /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
index 000000000..236eff4f9
--- /dev/null
+++ b/src/core/ngx_list.c
@@ -0,0 +1,41 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+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
index 000000000..046bdee16
--- /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 <ngx_config.h>
+#include <ngx_core.h>
+
+
+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
index 000000000..9cf1d5605
--- /dev/null
+++ b/src/core/ngx_log.c
@@ -0,0 +1,410 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+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
index 000000000..e82995e75
--- /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 <ngx_config.h>
+#include <ngx_core.h>
+
+
+#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
index 000000000..f0d66cb93
--- /dev/null
+++ b/src/core/ngx_output_chain.c
@@ -0,0 +1,312 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+#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
index 000000000..ee4d224ba
--- /dev/null
+++ b/src/core/ngx_palloc.c
@@ -0,0 +1,215 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+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
index 000000000..53448f4f3
--- /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 <ngx_config.h>
+#include <ngx_core.h>
+
+
+/*
+ * 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
index 000000000..dad9efcbd
--- /dev/null
+++ b/src/core/ngx_parse.c
@@ -0,0 +1,212 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+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
index 000000000..44fff2e95
--- /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 <ngx_config.h>
+#include <ngx_core.h>
+
+
+#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
index 000000000..c94db13e8
--- /dev/null
+++ b/src/core/ngx_rbtree.c
@@ -0,0 +1,349 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+/*
+ * 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
index 000000000..aa69556f8
--- /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 <ngx_config.h>
+#include <ngx_core.h>
+
+
+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
index 000000000..27dad8d06
--- /dev/null
+++ b/src/core/ngx_regex.c
@@ -0,0 +1,125 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+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
index 000000000..f9bf8580f
--- /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 <ngx_config.h>
+#include <ngx_core.h>
+
+#include <pcre.h>
+
+
+#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
index 000000000..dfa7da411
--- /dev/null
+++ b/src/core/ngx_spinlock.c
@@ -0,0 +1,48 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+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
index 000000000..3d6a9eb1d
--- /dev/null
+++ b/src/core/ngx_string.c
@@ -0,0 +1,284 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+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
index 000000000..fbc88751a
--- /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 <ngx_config.h>
+#include <ngx_core.h>
+
+
+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
index 000000000..5e2f3a94f
--- /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 <ngx_config.h>
+#include <ngx_core.h>
+
+
+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
index 000000000..2ca31acc4
--- /dev/null
+++ b/src/core/ngx_times.c
@@ -0,0 +1,346 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+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
index 000000000..e1d1515ee
--- /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 <ngx_config.h>
+#include <ngx_core.h>
+
+
+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
index 000000000..483d6752b
--- /dev/null
+++ b/src/event/modules/ngx_aio_module.c
@@ -0,0 +1,209 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+#include <ngx_aio.h>
+
+#if (HAVE_KQUEUE)
+#include <ngx_kqueue_module.h>
+#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
index 000000000..185a9dfb6
--- /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 <ngx_types.h>
+#include <ngx_log.h>
+#include <ngx_event.h>
+
+
+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
index 000000000..7d6888415
--- /dev/null
+++ b/src/event/modules/ngx_devpoll_module.c
@@ -0,0 +1,578 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+#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
index 000000000..4067fd886
--- /dev/null
+++ b/src/event/modules/ngx_epoll_module.c
@@ -0,0 +1,657 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+#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
index 000000000..db5b75683
--- /dev/null
+++ b/src/event/modules/ngx_kqueue_module.c
@@ -0,0 +1,800 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+#include <ngx_kqueue_module.h>
+
+
+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
index 000000000..2e000c4ec
--- /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
index 000000000..bf78e8f37
--- /dev/null
+++ b/src/event/modules/ngx_poll_module.c
@@ -0,0 +1,605 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+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
index 000000000..26b2c8140
--- /dev/null
+++ b/src/event/modules/ngx_rtsig_module.c
@@ -0,0 +1,814 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+#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
index 000000000..06ca4b32d
--- /dev/null
+++ b/src/event/modules/ngx_select_module.c
@@ -0,0 +1,616 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+
+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
index 000000000..3d7e15cfb
--- /dev/null
+++ b/src/event/ngx_event.c
@@ -0,0 +1,805 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+#define DEFAULT_CONNECTIONS 512
+
+
+extern ngx_module_t ngx_select_module;
+extern ngx_event_module_t ngx_select_module_ctx;
+
+#if (HAVE_KQUEUE)
+#include <ngx_kqueue_module.h>
+#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 <ngx_aio_module.h>
+#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
index 000000000..7237bd4ec
--- /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 <ngx_config.h>
+#include <ngx_core.h>
+
+
+#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 <ngx_event_timer.h>
+#include <ngx_event_posted.h>
+#include <ngx_event_busy_lock.h>
+
+#if (WIN32)
+#include <ngx_iocp_module.h>
+#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
index 000000000..5a5e0e170
--- /dev/null
+++ b/src/event/ngx_event_accept.c
@@ -0,0 +1,475 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+#include <nginx.h>
+
+
+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
index 000000000..56a8d921c
--- /dev/null
+++ b/src/event/ngx_event_busy_lock.c
@@ -0,0 +1,309 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+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
index 000000000..ce600026e
--- /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 <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+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
index 000000000..c1e6c1935
--- /dev/null
+++ b/src/event/ngx_event_connect.c
@@ -0,0 +1,379 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+#include <ngx_event_connect.h>
+#include <nginx.h>
+
+
+/* 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
index 000000000..025b91657
--- /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 <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+#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
index 000000000..ab4e6abc3
--- /dev/null
+++ b/src/event/ngx_event_mutex.c
@@ -0,0 +1,70 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+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
index 000000000..8c48a7afa
--- /dev/null
+++ b/src/event/ngx_event_openssl.c
@@ -0,0 +1,378 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+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
index 000000000..69d8d1288
--- /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 <ngx_config.h>
+#include <ngx_core.h>
+
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+
+
+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
index 000000000..c1982fd40
--- /dev/null
+++ b/src/event/ngx_event_pipe.c
@@ -0,0 +1,771 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+#include <ngx_event_pipe.h>
+
+
+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
index 000000000..961f0666b
--- /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 <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+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
index 000000000..eb064f5ad
--- /dev/null
+++ b/src/event/ngx_event_posted.c
@@ -0,0 +1,169 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+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
index 000000000..5e29edcf0
--- /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 <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+#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
index 000000000..a09f07f73
--- /dev/null
+++ b/src/event/ngx_event_timer.c
@@ -0,0 +1,165 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+#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
index 000000000..f40e57234
--- /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 <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+#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
index 000000000..3a323b149
--- /dev/null
+++ b/src/http/modules/ngx_http_access_handler.c
@@ -0,0 +1,213 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+/* 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
index 000000000..d22a86b5b
--- /dev/null
+++ b/src/http/modules/ngx_http_charset_filter.c
@@ -0,0 +1,553 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+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
index 000000000..a71839a29
--- /dev/null
+++ b/src/http/modules/ngx_http_chunked_filter.c
@@ -0,0 +1,156 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+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
index 000000000..72ccb0a0f
--- /dev/null
+++ b/src/http/modules/ngx_http_gzip_filter.c
@@ -0,0 +1,1036 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+#include <zlib.h>
+
+
+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
index 000000000..f7fe52c8e
--- /dev/null
+++ b/src/http/modules/ngx_http_headers_filter.c
@@ -0,0 +1,239 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+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
index 000000000..68a9d3627
--- /dev/null
+++ b/src/http/modules/ngx_http_index_handler.c
@@ -0,0 +1,529 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+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
index 000000000..04d1fde6b
--- /dev/null
+++ b/src/http/modules/ngx_http_not_modified_filter.c
@@ -0,0 +1,85 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+
+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
index 000000000..a08e25f7a
--- /dev/null
+++ b/src/http/modules/ngx_http_range_filter.c
@@ -0,0 +1,523 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+/*
+ * 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
index 000000000..aa3a65648
--- /dev/null
+++ b/src/http/modules/ngx_http_rewrite_handler.c
@@ -0,0 +1,466 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+#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
index 000000000..35ab2c46c
--- /dev/null
+++ b/src/http/modules/ngx_http_ssl_module.c
@@ -0,0 +1,160 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+#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
index 000000000..eaca2a6c5
--- /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 <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+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
index 000000000..c4ffde3b2
--- /dev/null
+++ b/src/http/modules/ngx_http_static_handler.c
@@ -0,0 +1,584 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+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
index 000000000..6cbad26c7
--- /dev/null
+++ b/src/http/modules/ngx_http_userid_filter.c
@@ -0,0 +1,588 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+#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
index 000000000..0a2a20025
--- /dev/null
+++ b/src/http/modules/proxy/ngx_http_proxy_cache.c
@@ -0,0 +1,629 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+#include <ngx_http_proxy_handler.h>
+
+
+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
index 000000000..3fa2e0bf3
--- /dev/null
+++ b/src/http/modules/proxy/ngx_http_proxy_handler.c
@@ -0,0 +1,1280 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+#include <ngx_http_proxy_handler.h>
+
+
+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
index 000000000..4dcc65387
--- /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 <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+#include <ngx_event_connect.h>
+#include <ngx_event_pipe.h>
+#include <ngx_http.h>
+
+
+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
index 000000000..038001240
--- /dev/null
+++ b/src/http/modules/proxy/ngx_http_proxy_header.c
@@ -0,0 +1,198 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+#include <ngx_http_proxy_handler.h>
+
+
+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
index 000000000..3718ab050
--- /dev/null
+++ b/src/http/modules/proxy/ngx_http_proxy_parse.c
@@ -0,0 +1,213 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+#include <ngx_http_proxy_handler.h>
+
+
+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
index 000000000..c1a8fb621
--- /dev/null
+++ b/src/http/modules/proxy/ngx_http_proxy_upstream.c
@@ -0,0 +1,1492 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+#include <ngx_event_connect.h>
+#include <ngx_event_pipe.h>
+#include <ngx_http.h>
+#include <ngx_http_proxy_handler.h>
+
+
+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
index 000000000..a37ffc6eb
--- /dev/null
+++ b/src/http/ngx_http.c
@@ -0,0 +1,642 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+#include <ngx_http.h>
+
+
+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
index 000000000..303a4daef
--- /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 <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_garbage_collector.h>
+
+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 <ngx_http_cache.h>
+#endif
+/* STUB */
+#include <ngx_http_cache.h>
+
+#include <ngx_http_request.h>
+#include <ngx_http_config.h>
+#include <ngx_http_busy_lock.h>
+#include <ngx_http_log_handler.h>
+#include <ngx_http_core_module.h>
+
+#if (NGX_HTTP_SSL)
+#include <ngx_http_ssl_module.h>
+#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
index 000000000..2b3ee105d
--- /dev/null
+++ b/src/http/ngx_http_busy_lock.c
@@ -0,0 +1,300 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+
+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
index 000000000..05e2667b9
--- /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 <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+#include <ngx_http.h>
+
+
+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
index 000000000..22572a58b
--- /dev/null
+++ b/src/http/ngx_http_cache.c
@@ -0,0 +1,480 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+
+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
index 000000000..40f4852dd
--- /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 <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+/*
+ * 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
index 000000000..be0052e59
--- /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 <ngx_alloc.h>
+#include <ngx_http.h>
+
+
+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
index 000000000..d015c2144
--- /dev/null
+++ b/src/http/ngx_http_copy_filter.c
@@ -0,0 +1,132 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+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
index 000000000..6df6edf45
--- /dev/null
+++ b/src/http/ngx_http_core_module.c
@@ -0,0 +1,1819 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+#include <ngx_http.h>
+#include <nginx.h>
+
+/* 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
index 000000000..7468db59c
--- /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 <ngx_string.h>
+#include <ngx_array.h>
+#include <ngx_http.h>
+
+
+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
index 000000000..acd8dbd3b
--- /dev/null
+++ b/src/http/ngx_http_file_cache.c
@@ -0,0 +1,239 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+#if (HAVE_OPENSSL_MD5_H)
+#include <openssl/md5.h>
+#else
+#include <md5.h>
+#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
index 000000000..9876a05da
--- /dev/null
+++ b/src/http/ngx_http_header_filter.c
@@ -0,0 +1,459 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+#include <nginx.h>
+
+
+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=<N>" 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
index 000000000..51166cf0c
--- /dev/null
+++ b/src/http/ngx_http_log_handler.c
@@ -0,0 +1,978 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+#include <nginx.h>
+
+
+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
index 000000000..8eb74ba1c
--- /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 <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+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
index 000000000..ba77ffb62
--- /dev/null
+++ b/src/http/ngx_http_parse.c
@@ -0,0 +1,868 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+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
index 000000000..38bbe2e85
--- /dev/null
+++ b/src/http/ngx_http_parse_time.c
@@ -0,0 +1,287 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_types.h>
+
+
+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
index 000000000..e889449ae
--- /dev/null
+++ b/src/http/ngx_http_request.c
@@ -0,0 +1,2149 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+#include <ngx_http.h>
+
+
+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
index 000000000..ef169f587
--- /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
index 000000000..ee8e3685b
--- /dev/null
+++ b/src/http/ngx_http_request_body.c
@@ -0,0 +1,227 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+#include <ngx_http.h>
+
+
+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
index 000000000..1af8746e5
--- /dev/null
+++ b/src/http/ngx_http_special_response.c
@@ -0,0 +1,358 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+#include <nginx.h>
+
+
+static u_char error_tail[] =
+"<hr><center>" NGINX_VER "</center>" CRLF
+"</body>" CRLF
+"</html>" CRLF
+;
+
+
+static u_char msie_stub[] =
+"<!-- The padding to disable MSIE's friendly error page -->" CRLF
+"<!-- The padding to disable MSIE's friendly error page -->" CRLF
+"<!-- The padding to disable MSIE's friendly error page -->" CRLF
+"<!-- The padding to disable MSIE's friendly error page -->" CRLF
+"<!-- The padding to disable MSIE's friendly error page -->" CRLF
+"<!-- The padding to disable MSIE's friendly error page -->" CRLF
+;
+
+
+static char error_301_page[] =
+"<html>" CRLF
+"<head><title>301 Moved Permanently</title></head>" CRLF
+"<body bgcolor=\"white\">" CRLF
+"<center><h1>301 Moved Permanently</h1></center>" CRLF
+;
+
+
+static char error_302_page[] =
+"<html>" CRLF
+"<head><title>302 Found</title></head>" CRLF
+"<body bgcolor=\"white\">" CRLF
+"<center><h1>302 Found</h1></center>" CRLF
+;
+
+
+static char error_400_page[] =
+"<html>" CRLF
+"<head><title>400 Bad Request</title></head>" CRLF
+"<body bgcolor=\"white\">" CRLF
+"<center><h1>400 Bad Request</h1></center>" CRLF
+;
+
+
+static char error_403_page[] =
+"<html>" CRLF
+"<head><title>403 Forbidden</title></head>" CRLF
+"<body bgcolor=\"white\">" CRLF
+"<center><h1>403 Forbidden</h1></center>" CRLF
+;
+
+
+static char error_404_page[] =
+"<html>" CRLF
+"<head><title>404 Not Found</title></head>" CRLF
+"<body bgcolor=\"white\">" CRLF
+"<center><h1>404 Not Found</h1></center>" CRLF
+;
+
+
+static char error_405_page[] =
+"<html>" CRLF
+"<head><title>405 Not Allowed</title></head>" CRLF
+"<body bgcolor=\"white\">" CRLF
+"<center><h1>405 Not Allowed</h1></center>" CRLF
+;
+
+
+static char error_408_page[] =
+"<html>" CRLF
+"<head><title>408 Request Time-out</title></head>" CRLF
+"<body bgcolor=\"white\">" CRLF
+"<center><h1>408 Request Time-out</h1></center>" CRLF
+;
+
+
+static char error_413_page[] =
+"<html>" CRLF
+"<head><title>413 Request Entity Too Large</title></head>" CRLF
+"<body bgcolor=\"white\">" CRLF
+"<center><h1>413 Request Entity Too Large</h1></center>" CRLF
+;
+
+
+static char error_414_page[] =
+"<html>" CRLF
+"<head><title>414 Request-URI Too Large</title></head>" CRLF
+"<body bgcolor=\"white\">" CRLF
+"<center><h1>414 Request-URI Too Large</h1></center>" CRLF
+;
+
+
+static char error_416_page[] =
+"<html>" CRLF
+"<head><title>416 Requested Range Not Satisfiable</title></head>" CRLF
+"<body bgcolor=\"white\">" CRLF
+"<center><h1>416 Requested Range Not Satisfiable</h1></center>" CRLF
+;
+
+
+static char error_497_page[] =
+"<html>" CRLF
+"<head><title>400 The plain HTTP request was sent to HTTPS port</title></head>"
+CRLF
+"<body bgcolor=\"white\">" CRLF
+"<center><h1>400 Bad Request</h1></center>" CRLF
+"<center>The plain HTTP request was sent to HTTPS port</center>" CRLF
+;
+
+
+static char error_500_page[] =
+"<html>" CRLF
+"<head><title>500 Internal Server Error</title></head>" CRLF
+"<body bgcolor=\"white\">" CRLF
+"<center><h1>500 Internal Server Error</h1></center>" CRLF
+;
+
+
+static char error_501_page[] =
+"<html>" CRLF
+"<head><title>501 Method Not Implemented</title></head>" CRLF
+"<body bgcolor=\"white\">" CRLF
+"<center><h1>500 Method Not Implemented</h1></center>" CRLF
+;
+
+
+static char error_502_page[] =
+"<html>" CRLF
+"<head><title>502 Bad Gateway</title></head>" CRLF
+"<body bgcolor=\"white\">" CRLF
+"<center><h1>502 Bad Gateway</h1></center>" CRLF
+;
+
+
+static char error_503_page[] =
+"<html>" CRLF
+"<head><title>503 Service Temporarily Unavailable</title></head>" CRLF
+"<body bgcolor=\"white\">" CRLF
+"<center><h1>503 Service Temporarily Unavailable</h1></center>" CRLF
+;
+
+
+static char error_504_page[] =
+"<html>" CRLF
+"<head><title>504 Gateway Time-out</title></head>" CRLF
+"<body bgcolor=\"white\">" CRLF
+"<center><h1>504 Gateway Time-out</h1></center>" 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
index 000000000..a8c68947f
--- /dev/null
+++ b/src/http/ngx_http_write_filter.c
@@ -0,0 +1,166 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+#include <ngx_http.h>
+
+
+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
index 000000000..c286dbd60
--- /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 <ngx_core.h>
+
+
+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
index 000000000..d01105412
--- /dev/null
+++ b/src/os/unix/ngx_aio_read.c
@@ -0,0 +1,117 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+#include <ngx_aio.h>
+
+#if (HAVE_KQUEUE)
+#include <ngx_kqueue_module.h>
+#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
index 000000000..31793d5a4
--- /dev/null
+++ b/src/os/unix/ngx_aio_read_chain.c
@@ -0,0 +1,78 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+#include <ngx_aio.h>
+
+
+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
index 000000000..f102ba338
--- /dev/null
+++ b/src/os/unix/ngx_aio_write.c
@@ -0,0 +1,117 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+#include <ngx_aio.h>
+
+#if (HAVE_KQUEUE)
+#include <ngx_kqueue_module.h>
+#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
index 000000000..b8c13356c
--- /dev/null
+++ b/src/os/unix/ngx_aio_write_chain.c
@@ -0,0 +1,96 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+#include <ngx_aio.h>
+
+
+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
index 000000000..dc3609206
--- /dev/null
+++ b/src/os/unix/ngx_alloc.c
@@ -0,0 +1,80 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+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
index 000000000..0084c2c54
--- /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 <ngx_config.h>
+#include <ngx_core.h>
+
+
+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
index 000000000..2de61cf81
--- /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 <ngx_config.h>
+#include <ngx_core.h>
+
+
+#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
index 000000000..3d09d68bb
--- /dev/null
+++ b/src/os/unix/ngx_channel.c
@@ -0,0 +1,237 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_channel.h>
+
+
+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
index 000000000..a8c86630e
--- /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 <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+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
index 000000000..b9a9b404c
--- /dev/null
+++ b/src/os/unix/ngx_daemon.c
@@ -0,0 +1,68 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+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
index 000000000..0d03fa9df
--- /dev/null
+++ b/src/os/unix/ngx_errno.c
@@ -0,0 +1,64 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+#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
index 000000000..7218bb291
--- /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 <ngx_config.h>
+#include <ngx_core.h>
+
+
+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
index 000000000..8603ab2d9
--- /dev/null
+++ b/src/os/unix/ngx_files.c
@@ -0,0 +1,268 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+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
index 000000000..3ba167004
--- /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 <ngx_config.h>
+#include <ngx_core.h>
+
+
+#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
index 000000000..9c8910586
--- /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
index 000000000..f4616dbe6
--- /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 <sys/types.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <stddef.h> /* offsetof() */
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <signal.h>
+#include <pwd.h>
+#include <grp.h>
+#include <dirent.h>
+
+#include <sys/uio.h>
+#include <sys/filio.h> /* FIONBIO */
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <sys/wait.h>
+#include <sys/mman.h>
+#include <sys/resource.h>
+#include <sched.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include <libutil.h> /* setproctitle() before 4.1 */
+#include <osreldate.h>
+#include <sys/sysctl.h>
+#include <netinet/tcp.h> /* TCP_NOPUSH */
+
+
+#if __FreeBSD_version < 400017
+
+#include <sys/param.h> /* 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 <ngx_auto_config.h>
+
+
+#ifndef HAVE_SELECT
+#define HAVE_SELECT 1
+#endif
+
+
+#ifndef HAVE_POLL
+#define HAVE_POLL 1
+#endif
+#if (HAVE_POLL)
+#include <poll.h>
+#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 <aio.h>
+#endif
+
+
+#if defined SO_ACCEPTFILTER && !defined HAVE_DEFERRED_ACCEPT
+#define HAVE_DEFERRED_ACCEPT 1
+#endif
+
+
+#if (HAVE_KQUEUE)
+#include <sys/event.h>
+#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
index 000000000..dcf432b65
--- /dev/null
+++ b/src/os/unix/ngx_freebsd_init.c
@@ -0,0 +1,225 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+/* 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
index 000000000..774adb938
--- /dev/null
+++ b/src/os/unix/ngx_freebsd_rfork_thread.c
@@ -0,0 +1,738 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+/*
+ * 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
index 000000000..bef230a64
--- /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 <sys/ipc.h>
+#include <sys/sem.h>
+#include <sched.h>
+
+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
index 000000000..d29debfbc
--- /dev/null
+++ b/src/os/unix/ngx_freebsd_sendfile_chain.c
@@ -0,0 +1,380 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+/*
+ * 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
index 000000000..24034c04a
--- /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
index 000000000..2b23702a1
--- /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 <sys/types.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <stddef.h> /* offsetof() */
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <signal.h>
+#include <pwd.h>
+#include <grp.h>
+#include <dirent.h>
+
+#include <sys/uio.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <sys/wait.h>
+#include <sys/mman.h>
+#include <sys/resource.h>
+#include <sched.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include <time.h> /* tzset() */
+#include <sys/ioctl.h>
+#include <sys/sysctl.h>
+#include <netinet/tcp.h> /* TCP_CORK */
+
+
+#include <ngx_auto_config.h>
+
+
+#if (HAVE_PRCTL)
+#include <sys/prctl.h>
+#endif
+
+#if (HAVE_SENDFILE64)
+#include <sys/sendfile.h>
+#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 <poll.h>
+#endif
+
+#if (HAVE_EPOLL)
+#include <sys/epoll.h>
+#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
index 000000000..4946e153f
--- /dev/null
+++ b/src/os/unix/ngx_linux_init.c
@@ -0,0 +1,80 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+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
index 000000000..38a2a96d2
--- /dev/null
+++ b/src/os/unix/ngx_linux_sendfile_chain.c
@@ -0,0 +1,285 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+/*
+ * On Linux up to 2.4.21 sendfile() (syscall #187) works with 32-bit
+ * offsets only and the including <sys/sendfile.h> 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
index 000000000..b28a73ffb
--- /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 <ngx_config.h>
+#include <ngx_core.h>
+
+
+#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 <ngx_freebsd.h>
+#endif
+
+
+#ifdef __linux__
+#include <ngx_linux.h>
+#endif
+
+
+#ifdef SOLARIS
+#include <ngx_solaris.h>
+#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
index 000000000..bf0f5d842
--- /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 <sys/types.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <stddef.h> /* offsetof() */
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <signal.h>
+#include <pwd.h>
+#include <grp.h>
+#include <dirent.h>
+
+#include <sys/uio.h>
+#include <sys/filio.h> /* FIONBIO */
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <sys/wait.h>
+#include <sys/mman.h>
+#include <sys/resource.h>
+#include <sched.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include <ngx_auto_config.h>
+
+
+#ifndef HAVE_SELECT
+#define HAVE_SELECT 1
+#endif
+
+
+#ifndef HAVE_POLL
+#define HAVE_POLL 1
+#endif
+#if (HAVE_POLL)
+#include <poll.h>
+#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
index 000000000..134c27691
--- /dev/null
+++ b/src/os/unix/ngx_posix_init.c
@@ -0,0 +1,299 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+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
index 000000000..dba949c7d
--- /dev/null
+++ b/src/os/unix/ngx_process.c
@@ -0,0 +1,283 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+#include <ngx_channel.h>
+
+
+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
index 000000000..816b6a664
--- /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
index 000000000..4d4db54c6
--- /dev/null
+++ b/src/os/unix/ngx_process_cycle.c
@@ -0,0 +1,970 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+#include <ngx_channel.h>
+
+
+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
index 000000000..71aa1789e
--- /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 <ngx_config.h>
+#include <ngx_core.h>
+
+
+#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
index 000000000..ad7987b81
--- /dev/null
+++ b/src/os/unix/ngx_pthread_thread.c
@@ -0,0 +1,273 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+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
index 000000000..a529b3d3f
--- /dev/null
+++ b/src/os/unix/ngx_readv_chain.c
@@ -0,0 +1,219 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+#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
index 000000000..6e5b8c06d
--- /dev/null
+++ b/src/os/unix/ngx_recv.c
@@ -0,0 +1,173 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+#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
index 000000000..f09dadb50
--- /dev/null
+++ b/src/os/unix/ngx_send.c
@@ -0,0 +1,70 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+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
index 000000000..60f2de029
--- /dev/null
+++ b/src/os/unix/ngx_shared.c
@@ -0,0 +1,96 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+#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 <sys/ipc.h>
+#include <sys/shm.h>
+
+
+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
index 000000000..29e4a33d5
--- /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 <ngx_config.h>
+#include <ngx_core.h>
+
+
+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
index 000000000..f97ab41f9
--- /dev/null
+++ b/src/os/unix/ngx_socket.c
@@ -0,0 +1,104 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+/*
+ * 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
index 000000000..67d0d41de
--- /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 <ngx_config.h>
+
+
+#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
index 000000000..36f3cb958
--- /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
index 000000000..6cbfcc2ec
--- /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 <sys/types.h> */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <stddef.h> /* offsetof() */
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <signal.h>
+#include <pwd.h>
+#include <grp.h>
+#include <dirent.h>
+
+#include <sys/uio.h>
+#include <sys/filio.h> /* FIONBIO */
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <sys/wait.h>
+#include <sys/mman.h>
+#include <sys/resource.h>
+#include <sched.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include <sys/sendfile.h>
+#include <sys/systeminfo.h>
+#include <limits.h> /* IOV_MAX */
+#include <inttypes.h>
+
+#include <ngx_auto_config.h>
+
+
+#ifndef HAVE_SELECT
+#define HAVE_SELECT 1
+#endif
+
+
+#ifndef HAVE_POLL
+#define HAVE_POLL 1
+#endif
+#if (HAVE_POLL)
+#include <poll.h>
+#endif
+
+
+#if (HAVE_AIO)
+#include <aio.h>
+#endif
+
+
+#if (HAVE_DEVPOLL)
+#include <sys/ioctl.h>
+#include <sys/devpoll.h>
+#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
index 000000000..a743ab525
--- /dev/null
+++ b/src/os/unix/ngx_solaris_init.c
@@ -0,0 +1,70 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+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
index 000000000..a060dac2d
--- /dev/null
+++ b/src/os/unix/ngx_solaris_sendfilev_chain.c
@@ -0,0 +1,206 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+#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
index 000000000..7b5dc239c
--- /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 <ngx_config.h>
+#include <ngx_core.h>
+
+#if (NGX_THREADS)
+
+#define NGX_MAX_THREADS 128
+
+#if (NGX_USE_RFORK)
+#include <ngx_freebsd_rfork_thread.h>
+
+
+#else /* use pthreads */
+
+#include <pthread.h>
+
+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
index 000000000..c4389503a
--- /dev/null
+++ b/src/os/unix/ngx_time.c
@@ -0,0 +1,31 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+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
index 000000000..793e32e7c
--- /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 <ngx_config.h>
+#include <ngx_core.h>
+
+
+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
index 000000000..49fb17de8
--- /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 <ngx_config.h>
+
+
+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
index 000000000..f939b84b4
--- /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 <ngx_config.h>
+#include <ngx_core.h>
+
+
+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
index 000000000..a3595ff0c
--- /dev/null
+++ b/src/os/unix/ngx_writev_chain.c
@@ -0,0 +1,160 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+#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
index 000000000..42630a78a
--- /dev/null
+++ b/src/os/unix/rfork_thread.S
@@ -0,0 +1,75 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+
+#include <sys/syscall.h>
+#include <machine/asm.h>
+
+/*
+ * 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