# HG changeset patch # User Maxim Dounin # Date 1217887971 -14400 # Node ID 77df966111120a3c0634b440ca8dd02a8c4ca144 # Parent 1d9bef53cd8e9461c73ed537a953f3a7bae6dbd2# Parent 040b8c84d040db5be92f1d9aef30c4a34e102418 Merge with current. diff --git a/.hgtags b/.hgtags --- a/.hgtags +++ b/.hgtags @@ -193,3 +193,5 @@ 984bb0b1399bffe332ad1fe5cd8d4d7862fa18d8 12defd37f57883e4d59ef36837d03c9b68c3c3ad NGINX_0_7_4 1172e6d6f40f26a5130a15f5b7adbd902216faf2 NGINX_0_7_5 6de24473fa708acc3b2f13611f5c44b0e3a292c9 NGINX_0_7_6 +0b6053502c552a3021db417cb2dc6caac7ba1bdd NGINX_0_7_7 +34fb3a5735483bd22e77f90f305103307a813fc4 NGINX_0_7_8 diff --git a/CHANGES b/CHANGES --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,33 @@ +Changes with nginx 0.7.7 30 Jul 2008 + + *) Change: now the EAGAIN error returned by connect() is not considered + as temporary error. + + *) Change: now the $ssl_client_cert variable value is a certificate + with TAB character intended before each line except first one; an + unchanged certificate is available in the $ssl_client_raw_cert + variable. + + *) Feature: the "ask" parameter in the "ssl_verify_client" directive. + + *) Feature: byte-range processing improvements. + Thanks to Maxim Dounin. + + *) Feature: the "directio" directive. + + *) Feature: MacOSX 1.5 sendfile() support. + + *) Bugfix: now in MacOSX and Cygwin locations are tested in case + insensitive mode; however, the compare is provided by single-byte + locales only. + + *) Bugfix: mail proxy SSL connections hanged, if select, poll, or + /dev/poll methods were used. + + *) Bugfix: UTF-8 encoding usage in the ngx_http_autoindex_module. + + Changes with nginx 0.7.6 07 Jul 2008 *) Bugfix: now if variables are used in the "access_log" directive a diff --git a/CHANGES.ru b/CHANGES.ru --- a/CHANGES.ru +++ b/CHANGES.ru @@ -1,4 +1,34 @@ +Изменения в nginx 0.7.7 30.07.2008 + + *) Изменение: теперь ошибка EAGAIN при вызове connect() не считается + временной. + + *) Изменение: значением переменной $ssl_client_cert теперь является + сертификат, перед каждой строкой которого, кроме первой, вставляется + символ табуляции; неизменённый сертификат доступен через переменную + $ssl_client_raw_cert. + + *) Добавление: параметр ask директивы ssl_verify_client. + + *) Добавление: улучшения в обработке byte-range. + Спасибо Максиму Дунину. + + *) Добавление: директива directio. + + *) Добавление: поддержка sendfile() в MacOSX 1.5. + + *) Исправление: в MacOSX и Cygwin при проверке location'ов теперь + делается сравнение без учёта регистра символов; однако, сравнение + ограничено только однобайтными locale'ями. + + *) Исправление: соединения почтового прокси-сервера зависали в режиме + SSL, если использовались методы select, poll или /dev/poll. + + *) Исправление: ошибки при использовании кодировки UTF-8 в + ngx_http_autoindex_module. + + Изменения в nginx 0.7.6 07.07.2008 *) Исправление: теперь при использовании переменных в директиве diff --git a/auto/feature b/auto/feature --- a/auto/feature +++ b/auto/feature @@ -19,7 +19,9 @@ if test -n "$ngx_feature_name"; then fi if test -n "$ngx_feature_path"; then - ngx_feature_inc_path="-I $ngx_feature_path" + for ngx_temp in $ngx_feature_path; do + ngx_feature_inc_path="$ngx_feature_inc_path -I $ngx_temp" + done fi cat << END > $NGX_AUTOTEST.c diff --git a/auto/lib/conf b/auto/lib/conf --- a/auto/lib/conf +++ b/auto/lib/conf @@ -41,6 +41,10 @@ if [ $USE_ZLIB = YES ]; then . auto/lib/zlib/conf fi +if [ $USE_LIBXSLT = YES ]; then + . auto/lib/libxslt/conf +fi + if [ $USE_PERL = YES ]; then . auto/lib/perl/conf fi diff --git a/auto/lib/libxslt/conf b/auto/lib/libxslt/conf new file mode 100644 --- /dev/null +++ b/auto/lib/libxslt/conf @@ -0,0 +1,78 @@ + +# Copyright (C) Igor Sysoev + + + ngx_feature="libxslt" + ngx_feature_name= + ngx_feature_run=no + ngx_feature_incs="#include + #include + #include + #include + #include + #include " + ngx_feature_path="/usr/include/libxml2" + ngx_feature_libs="-lxml2 -lxslt" + ngx_feature_test="xmlParserCtxtPtr ctxt = NULL; + xsltStylesheetPtr sheet = NULL; + xmlDocPtr doc; + doc = xmlParseChunk(ctxt, NULL, 0, 0); + xsltApplyStylesheet(sheet, doc, NULL);" + . auto/feature + + +if [ $ngx_found = no ]; then + + # FreeBSD port + + ngx_feature="libxslt in /usr/local/" + ngx_feature_path="/usr/local/include/libxml2 /usr/local/include" + + if [ $NGX_RPATH = YES ]; then + ngx_feature_libs="-R/usr/local/lib -L/usr/local/lib -lxml2 -lxslt" + else + ngx_feature_libs="-L/usr/local/lib -lxml2 -lxslt" + fi + + . auto/feature +fi + + +if [ $ngx_found = no ]; then + + # NetBSD port + + ngx_feature="libxslt in /usr/pkg/" + ngx_feature_path="/usr/pkg/include/libxml2 /usr/pkg/include" + + if [ $NGX_RPATH = YES ]; then + ngx_feature_libs="-R/usr/pkg/lib -L/usr/pkg/lib -lxml2 -lxslt" + else + ngx_feature_libs="-L/usr/pkg/lib -lxml2 -lxslt" + fi + + . auto/feature +fi + + +if [ $ngx_found = no ]; then + + # MacPorts + + ngx_feature="libxslt in /opt/local/" + ngx_feature_path="/opt/local/include/libxml2 /opt/local/include" + + if [ $NGX_RPATH = YES ]; then + ngx_feature_libs="-R/opt/local/lib -L/opt/local/lib -lxml2 -lxslt" + else + ngx_feature_libs="-L/opt/local/lib -lxml2 -lxslt" + fi + + . auto/feature +fi + + +if [ $ngx_found = yes ]; then + CORE_INCS="$CORE_INCS $ngx_feature_path" + CORE_LIBS="$CORE_LIBS $ngx_feature_libs" +fi diff --git a/auto/lib/pcre/conf b/auto/lib/pcre/conf --- a/auto/lib/pcre/conf +++ b/auto/lib/pcre/conf @@ -114,7 +114,7 @@ else if [ $ngx_found = no ]; then - # Linux package + # RedHat RPM, Solaris package ngx_feature="PCRE library in /usr/include/pcre/" ngx_feature_path="/usr/include/pcre" diff --git a/auto/modules b/auto/modules --- a/auto/modules +++ b/auto/modules @@ -76,6 +76,9 @@ fi # the module order is important +# ngx_http_static_module +# ngx_http_gzip_static_module +# ngx_http_dav_module # ngx_http_autoindex_module # ngx_http_index_module # @@ -92,6 +95,8 @@ fi # ngx_http_postpone_filter # ngx_http_charset_filter # ngx_http_ssi_filter +# ngx_http_xslt_filter +# ngx_http_sub_filter # ngx_http_addition_filter # ngx_http_userid_filter # ngx_http_headers_filter @@ -131,6 +136,12 @@ if [ $HTTP_SSI = YES ]; then HTTP_SRCS="$HTTP_SRCS $HTTP_SSI_SRCS" fi +if [ $HTTP_XSLT = YES ]; then + USE_LIBXSLT=YES + HTTP_FILTER_MODULES="$HTTP_FILTER_MODULES $HTTP_XSLT_FILTER_MODULE" + HTTP_SRCS="$HTTP_SRCS $HTTP_XSLT_SRCS" +fi + if [ $HTTP_SUB = YES ]; then HTTP_FILTER_MODULES="$HTTP_FILTER_MODULES $HTTP_SUB_FILTER_MODULE" HTTP_SRCS="$HTTP_SRCS $HTTP_SUB_SRCS" diff --git a/auto/options b/auto/options --- a/auto/options +++ b/auto/options @@ -56,6 +56,7 @@ HTTP_SSL=NO HTTP_SSI=YES HTTP_POSTPONE=NO HTTP_REALIP=NO +HTTP_XSLT=NO HTTP_SUB=NO HTTP_ADDITION=NO HTTP_DAV=NO @@ -115,6 +116,8 @@ ZLIB_ASM=NO USE_PERL=NO NGX_PERL=perl +USE_LIBXSLT=NO + NGX_GOOGLE_PERFTOOLS=NO NGX_CPU_CACHE_LINE= @@ -162,6 +165,7 @@ do --with-http_ssl_module) HTTP_SSL=YES ;; --with-http_realip_module) HTTP_REALIP=YES ;; --with-http_addition_module) HTTP_ADDITION=YES ;; + --with-http_xslt_module) HTTP_XSLT=YES ;; --with-http_sub_module) HTTP_SUB=YES ;; --with-http_dav_module) HTTP_DAV=YES ;; --with-http_flv_module) HTTP_FLV=YES ;; @@ -276,6 +280,7 @@ cat << END --with-http_ssl_module enable ngx_http_ssl_module --with-http_realip_module enable ngx_http_realip_module --with-http_addition_module enable ngx_http_addition_module + --with-http_xslt_module enable ngx_http_xslt_module --with-http_sub_module enable ngx_http_sub_module --with-http_dav_module enable ngx_http_dav_module --with-http_flv_module enable ngx_http_flv_module diff --git a/auto/os/conf b/auto/os/conf --- a/auto/os/conf +++ b/auto/os/conf @@ -18,6 +18,10 @@ case "$NGX_PLATFORM" in . auto/os/solaris ;; + Darwin:*) + . auto/os/darwin + ;; + win32) . auto/os/win32 ;; @@ -36,24 +40,6 @@ case "$NGX_PLATFORM" in ' ;; - Darwin:*) - have=NGX_DARWIN . auto/have_headers - have=NGX_HAVE_INHERITED_NONBLOCK . auto/have - CORE_INCS="$UNIX_INCS" - CORE_DEPS="$UNIX_DEPS $POSIX_DEPS" - CORE_SRCS="$UNIX_SRCS" - - ngx_feature="atomic(3)" - ngx_feature_name=NGX_DARWIN_ATOMIC - ngx_feature_run=no - ngx_feature_incs="#include " - ngx_feature_path= - ngx_feature_libs= - ngx_feature_test="int32_t lock, n; - n = OSAtomicCompareAndSwap32Barrier(0, 1, lock)" - . auto/feature - ;; - HP-UX:*) # HP/UX have=NGX_HPUX . auto/have_headers diff --git a/auto/os/darwin b/auto/os/darwin new file mode 100644 --- /dev/null +++ b/auto/os/darwin @@ -0,0 +1,115 @@ + +# Copyright (C) Igor Sysoev + + +have=NGX_DARWIN . auto/have_headers + +CORE_INCS="$UNIX_INCS" +CORE_DEPS="$UNIX_DEPS $DARWIN_DEPS" +CORE_SRCS="$UNIX_SRCS $DARWIN_SRCS" + + + +ngx_spacer=' +' + +# kqueue + +echo " + kqueue found" +have=NGX_HAVE_KQUEUE . auto/have +have=NGX_HAVE_CLEAR_EVENT . auto/have +EVENT_MODULES="$EVENT_MODULES $KQUEUE_MODULE" +CORE_SRCS="$CORE_SRCS $KQUEUE_SRCS" +EVENT_FOUND=YES +NGX_KQUEUE_CHECKED=YES + +ngx_feature="kqueue's EVFILT_TIMER" +ngx_feature_name="NGX_HAVE_TIMER_EVENT" +ngx_feature_run=yes +ngx_feature_incs="#include + #include " +ngx_feature_path= +ngx_feature_libs= +ngx_feature_test="int kq; + struct kevent kev; + struct timespec ts; + + if ((kq = kqueue()) == -1) return 1; + + kev.ident = 0; + kev.filter = EVFILT_TIMER; + kev.flags = EV_ADD|EV_ENABLE; + kev.fflags = 0; + kev.data = 1000; + kev.udata = 0; + + ts.tv_sec = 0; + ts.tv_nsec = 0; + + if (kevent(kq, &kev, 1, &kev, 1, &ts) == -1) return 1; + + if (kev.flags & EV_ERROR) return 1;" + +. auto/feature + + +ngx_feature="Darwin 64-bit kqueue millisecond timeout bug" +ngx_feature_name=NGX_DARWIN_KEVENT_BUG +ngx_feature_run=bug +ngx_feature_incs="#include + #include " +ngx_feature_path= +ngx_feature_libs= +ngx_feature_test="int kq; + struct kevent kev; + struct timespec ts; + struct timeval tv, tv0; + + kq = kqueue(); + + ts.tv_sec = 0; + ts.tv_nsec = 999000000; + + gettimeofday(&tv, 0); + kevent(kq, NULL, 0, &kev, 1, &ts); + gettimeofday(&tv0, 0); + timersub(&tv0, &tv, &tv); + + if (tv.tv_sec * 1000000 + tv.tv_usec < 900000) return 1;" + +. auto/feature + + +# sendfile() + +CC_AUX_FLAGS="$CC_AUX_FLAGS" +ngx_feature="sendfile()" +ngx_feature_name="NGX_HAVE_SENDFILE" +ngx_feature_run=yes +ngx_feature_incs="#include + #include + #include + #include " +ngx_feature_path= +ngx_feature_libs= +ngx_feature_test="int s = 0, fd = 1; + off_t n; off_t off = 0; + n = sendfile(s, fd, off, &n, NULL, 0); + if (n == -1 && errno == ENOSYS) return 1" +. auto/feature + +if [ $ngx_found = yes ]; then + have=NGX_HAVE_SENDFILE . auto/have + CORE_SRCS="$CORE_SRCS $DARWIN_SENDFILE_SRCS" +fi + + +ngx_feature="atomic(3)" +ngx_feature_name=NGX_DARWIN_ATOMIC +ngx_feature_run=no +ngx_feature_incs="#include " +ngx_feature_path= +ngx_feature_libs= +ngx_feature_test="int32_t lock, n; + n = OSAtomicCompareAndSwap32Barrier(0, 1, lock)" +. auto/feature diff --git a/auto/os/features b/auto/os/features --- a/auto/os/features +++ b/auto/os/features @@ -122,36 +122,6 @@ if test -z "$NGX_KQUEUE_CHECKED"; then if (kev.flags & EV_ERROR) return 1;" . auto/feature - - - if [ "$NGX_SYSTEM" = "Darwin" ]; then - - ngx_feature="Darwin 64-bit kqueue millisecond timeout bug" - ngx_feature_name=NGX_DARWIN_KEVENT_BUG - ngx_feature_run=bug - ngx_feature_incs="#include -#include " - ngx_feature_path= - ngx_feature_libs= - ngx_feature_test="int kq; - struct kevent kev; - struct timespec ts; - struct timeval tv, tv0; - - kq = kqueue(); - - ts.tv_sec = 0; - ts.tv_nsec = 999000000; - - gettimeofday(&tv, 0); - kevent(kq, NULL, 0, &kev, 1, &ts); - gettimeofday(&tv0, 0); - timersub(&tv0, &tv, &tv); - - if (tv.tv_sec * 1000000 + tv.tv_usec < 900000) return 1;" - - . auto/feature - fi fi fi @@ -200,3 +170,34 @@ if [ $ngx_found = no ]; then CRYPT_LIB="-lcrypt" fi fi + + +ngx_feature="O_DIRECT" +ngx_feature_name="NGX_HAVE_O_DIRECT" +ngx_feature_run=no +ngx_feature_incs="#include " +ngx_feature_path= +ngx_feature_libs= +ngx_feature_test="fcntl(0, F_SETFL, O_DIRECT);" +. auto/feature + + +ngx_feature="F_NOCACHE" +ngx_feature_name="NGX_HAVE_F_NOCACHE" +ngx_feature_run=no +ngx_feature_incs="#include " +ngx_feature_path= +ngx_feature_libs= +ngx_feature_test="fcntl(0, F_NOCACHE, 1);" +. auto/feature + + +ngx_feature="directio()" +ngx_feature_name="NGX_HAVE_DIRECTIO" +ngx_feature_run=no +ngx_feature_incs="#include + #include " +ngx_feature_path= +ngx_feature_libs= +ngx_feature_test="directio(0, DIRECTIO_ON);" +. auto/feature diff --git a/auto/sources b/auto/sources --- a/auto/sources +++ b/auto/sources @@ -198,6 +198,11 @@ SOLARIS_SRCS=src/os/unix/ngx_solaris_ini SOLARIS_SENDFILEV_SRCS=src/os/unix/ngx_solaris_sendfilev_chain.c +DARWIN_DEPS="src/os/unix/ngx_darwin_config.h src/os/unix/ngx_darwin.h" +DARWIN_SRCS=src/os/unix/ngx_darwin_init.c +DARWIN_SENDFILE_SRCS=src/os/unix/ngx_darwin_sendfile_chain.c + + WIN32_INCS="$CORE_INCS $EVENT_INCS src/os/win32" WIN32_DEPS="$CORE_DEPS $EVENT_DEPS \ @@ -328,6 +333,10 @@ HTTP_SSI_DEPS=src/http/modules/ngx_http_ HTTP_SSI_SRCS=src/http/modules/ngx_http_ssi_filter_module.c +HTTP_XSLT_FILTER_MODULE=ngx_http_xslt_filter_module +HTTP_XSLT_SRCS=src/http/modules/ngx_http_xslt_filter_module.c + + HTTP_SUB_FILTER_MODULE=ngx_http_sub_filter_module HTTP_SUB_SRCS=src/http/modules/ngx_http_sub_filter_module.c diff --git a/src/core/nginx.h b/src/core/nginx.h --- a/src/core/nginx.h +++ b/src/core/nginx.h @@ -8,7 +8,7 @@ #define _NGINX_H_INCLUDED_ -#define NGINX_VERSION "0.7.6" +#define NGINX_VERSION "0.7.8" #define NGINX_VER "nginx/" NGINX_VERSION #define NGINX_VAR "NGINX" diff --git a/src/core/ngx_config.h b/src/core/ngx_config.h --- a/src/core/ngx_config.h +++ b/src/core/ngx_config.h @@ -29,6 +29,10 @@ #include +#elif (NGX_DARWIN) +#include + + #elif (NGX_WIN32) #include diff --git a/src/core/ngx_hash.c b/src/core/ngx_hash.c --- a/src/core/ngx_hash.c +++ b/src/core/ngx_hash.c @@ -390,9 +390,7 @@ found: elt->value = names[n].value; elt->len = (u_char) names[n].key.len; - for (i = 0; i < names[n].key.len; i++) { - elt->name[i] = ngx_tolower(names[n].key.data[i]); - } + ngx_strlow(elt->name, names[n].key.data, names[n].key.len); test[key] = (u_short) (test[key] + NGX_HASH_ELT_SIZE(&names[n])); } @@ -622,6 +620,24 @@ ngx_hash_key_lc(u_char *data, size_t len } +ngx_uint_t +ngx_hash_strlow(u_char *dst, u_char *src, size_t n) +{ + ngx_uint_t key; + + key = 0; + + while (n--) { + *dst = ngx_tolower(*src); + key = ngx_hash(key, *dst); + dst++; + src++; + } + + return key; +} + + ngx_int_t ngx_hash_keys_array_init(ngx_hash_keys_arrays_t *ha, ngx_uint_t type) { @@ -796,12 +812,7 @@ wildcard: /* wildcard hash */ - k = 0; - - for (i = skip; i < last; i++) { - key->data[i] = ngx_tolower(key->data[i]); - k = ngx_hash(k, key->data[i]); - } + k = ngx_hash_strlow(&key->data[skip], &key->data[skip], last - skip); k %= ha->hsize; diff --git a/src/core/ngx_hash.h b/src/core/ngx_hash.h --- a/src/core/ngx_hash.h +++ b/src/core/ngx_hash.h @@ -110,6 +110,8 @@ ngx_int_t ngx_hash_wildcard_init(ngx_has #define ngx_hash(key, c) ((ngx_uint_t) key * 31 + c) ngx_uint_t ngx_hash_key(u_char *data, size_t len); ngx_uint_t ngx_hash_key_lc(u_char *data, size_t len); +ngx_uint_t ngx_hash_strlow(u_char *dst, u_char *src, size_t n); + ngx_int_t ngx_hash_keys_array_init(ngx_hash_keys_arrays_t *ha, ngx_uint_t type); ngx_int_t ngx_hash_add_key(ngx_hash_keys_arrays_t *ha, ngx_str_t *key, diff --git a/src/core/ngx_open_file_cache.c b/src/core/ngx_open_file_cache.c --- a/src/core/ngx_open_file_cache.c +++ b/src/core/ngx_open_file_cache.c @@ -497,6 +497,13 @@ ngx_open_and_stat_file(u_char *name, ngx } else { of->fd = fd; + + if (of->directio <= ngx_file_size(&fi)) { + if (ngx_directio(fd) == -1) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + ngx_directio_n " \"%s\" failed", name); + } + } } done: diff --git a/src/core/ngx_open_file_cache.h b/src/core/ngx_open_file_cache.h --- a/src/core/ngx_open_file_cache.h +++ b/src/core/ngx_open_file_cache.h @@ -17,6 +17,7 @@ typedef struct { ngx_file_uniq_t uniq; time_t mtime; off_t size; + off_t directio; ngx_err_t err; time_t valid; diff --git a/src/core/ngx_output_chain.c b/src/core/ngx_output_chain.c --- a/src/core/ngx_output_chain.c +++ b/src/core/ngx_output_chain.c @@ -32,6 +32,7 @@ ngx_output_chain(ngx_output_chain_ctx_t size_t size; ngx_int_t rc, last; ngx_uint_t recycled; + ngx_buf_t *b; ngx_chain_t *cl, *out, **last_out; if (ctx->in == NULL && ctx->busy == NULL) { @@ -161,13 +162,29 @@ ngx_output_chain(ngx_output_chain_ctx_t } } - ctx->buf = ngx_create_temp_buf(ctx->pool, size); - if (ctx->buf == NULL) { + b = ngx_calloc_buf(ctx->pool); + if (b == NULL) { return NGX_ERROR; } - ctx->buf->tag = ctx->tag; - ctx->buf->recycled = recycled; + /* + * allocate block aligned to a disk sector size + * to enable O_DIRECT + */ + + b->start = ngx_pmemalign(ctx->pool, size, 512); + if (b->start == NULL) { + return NGX_ERROR; + } + + b->pos = b->start; + b->last = b->start; + b->end = b->last + size; + b->temporary = 1; + b->tag = ctx->tag; + b->recycled = recycled; + + ctx->buf = b; ctx->allocated++; } } diff --git a/src/core/ngx_palloc.c b/src/core/ngx_palloc.c --- a/src/core/ngx_palloc.c +++ b/src/core/ngx_palloc.c @@ -195,17 +195,35 @@ ngx_palloc_large(ngx_pool_t *pool, size_ void *p; ngx_pool_large_t *large; -#if 0 - p = ngx_memalign(ngx_pagesize, size, pool->log); - if (p == NULL) { - return NULL; - } -#else p = ngx_alloc(size, pool->log); if (p == NULL) { return NULL; } -#endif + + large = ngx_palloc(pool, sizeof(ngx_pool_large_t)); + if (large == NULL) { + ngx_free(p); + return NULL; + } + + large->alloc = p; + large->next = pool->large; + pool->large = large; + + return p; +} + + +void * +ngx_pmemalign(ngx_pool_t *pool, size_t size, size_t alignment) +{ + void *p; + ngx_pool_large_t *large; + + p = ngx_memalign(alignment, size, pool->log); + if (p == NULL) { + return NULL; + } large = ngx_palloc(pool, sizeof(ngx_pool_large_t)); if (large == NULL) { diff --git a/src/core/ngx_palloc.h b/src/core/ngx_palloc.h --- a/src/core/ngx_palloc.h +++ b/src/core/ngx_palloc.h @@ -77,6 +77,7 @@ void ngx_destroy_pool(ngx_pool_t *pool); void *ngx_palloc(ngx_pool_t *pool, size_t size); void *ngx_pnalloc(ngx_pool_t *pool, size_t size); void *ngx_pcalloc(ngx_pool_t *pool, size_t size); +void *ngx_pmemalign(ngx_pool_t *pool, size_t size, size_t alignment); ngx_int_t ngx_pfree(ngx_pool_t *pool, void *p); diff --git a/src/core/ngx_string.c b/src/core/ngx_string.c --- a/src/core/ngx_string.c +++ b/src/core/ngx_string.c @@ -8,6 +8,17 @@ #include +void +ngx_strlow(u_char *dst, u_char *src, size_t n) +{ + while (n--) { + *dst = ngx_tolower(*src); + dst++; + src++; + } +} + + u_char * ngx_cpystrn(u_char *dst, u_char *src, size_t n) { @@ -952,16 +963,16 @@ ngx_decode_base64(ngx_str_t *dst, ngx_st /* - * ngx_utf_decode() decodes two and more bytes UTF sequences only + * ngx_utf8_decode() decodes two and more bytes UTF sequences only * the return values: * 0x80 - 0x10ffff valid character - * 0x10ffff - 0xfffffffd invalid sequence + * 0x110000 - 0xfffffffd invalid sequence * 0xfffffffe incomplete sequence * 0xffffffff error */ uint32_t -ngx_utf_decode(u_char **p, size_t n) +ngx_utf8_decode(u_char **p, size_t n) { size_t len; uint32_t u, i, valid; @@ -1018,31 +1029,26 @@ ngx_utf_decode(u_char **p, size_t n) size_t -ngx_utf_length(u_char *p, size_t n) +ngx_utf8_length(u_char *p, size_t n) { - u_char c; - size_t len; - ngx_uint_t i; + u_char c, *last; + size_t len; - for (len = 0, i = 0; i < n; len++, i++) { + last = p + n; - c = p[i]; + for (len = 0; p < last; len++) { + + c = *p; if (c < 0x80) { + p++; continue; } - if (c >= 0xc0) { - for (c <<= 1; c & 0x80; c <<= 1) { - i++; - } - - continue; + if (ngx_utf8_decode(&p, n) > 0x10ffff) { + /* invalid UTF-8 */ + return n; } - - /* invalid utf */ - - return n; } return len; @@ -1050,36 +1056,45 @@ ngx_utf_length(u_char *p, size_t n) u_char * -ngx_utf_cpystrn(u_char *dst, u_char *src, size_t n) +ngx_utf8_cpystrn(u_char *dst, u_char *src, size_t n, size_t len) { - u_char c; + u_char c, *next; if (n == 0) { return dst; } - for ( /* void */ ; --n; dst++, src++) { + while (--n) { c = *src; *dst = c; if (c < 0x80) { - if (*dst != '\0') { + + if (c != '\0') { + dst++; + src++; + len--; + continue; } return dst; } - if (c >= 0xc0) { - for (c <<= 1; c & 0x80; c <<= 1) { - *++dst = *++src; - } + next = src; - continue; + if (ngx_utf8_decode(&next, len) > 0x10ffff) { + /* invalid UTF-8 */ + break; } - /* invalid utf */ + len--; + + while (src < next) { + *++dst = *++src; + len--; + } } *dst = '\0'; diff --git a/src/core/ngx_string.h b/src/core/ngx_string.h --- a/src/core/ngx_string.h +++ b/src/core/ngx_string.h @@ -43,6 +43,8 @@ typedef struct { #define ngx_tolower(c) (u_char) ((c >= 'A' && c <= 'Z') ? (c | 0x20) : c) #define ngx_toupper(c) (u_char) ((c >= 'a' && c <= 'z') ? (c & ~0x20) : c) +void ngx_strlow(u_char *dst, u_char *src, size_t n); + #define ngx_strncmp(s1, s2, n) strncmp((const char *) s1, (const char *) s2, n) @@ -151,9 +153,9 @@ u_char *ngx_hex_dump(u_char *dst, u_char void ngx_encode_base64(ngx_str_t *dst, ngx_str_t *src); ngx_int_t ngx_decode_base64(ngx_str_t *dst, ngx_str_t *src); -uint32_t ngx_utf_decode(u_char **p, size_t n); -size_t ngx_utf_length(u_char *p, size_t n); -u_char *ngx_utf_cpystrn(u_char *dst, u_char *src, size_t n); +uint32_t ngx_utf8_decode(u_char **p, size_t n); +size_t ngx_utf8_length(u_char *p, size_t n); +u_char *ngx_utf8_cpystrn(u_char *dst, u_char *src, size_t n, size_t len); #define NGX_ESCAPE_URI 0 diff --git a/src/event/ngx_event_connect.c b/src/event/ngx_event_connect.c --- a/src/event/ngx_event_connect.c +++ b/src/event/ngx_event_connect.c @@ -139,11 +139,22 @@ ngx_event_connect_peer(ngx_peer_connecti if (rc == -1) { err = ngx_socket_errno; - /* Winsock returns WSAEWOULDBLOCK (NGX_EAGAIN) */ - if (err != NGX_EINPROGRESS && err != NGX_EAGAIN) { - + if (err != NGX_EINPROGRESS +#if (NGX_WIN32) + /* Winsock returns WSAEWOULDBLOCK (NGX_EAGAIN) */ + && err != NGX_EAGAIN +#endif + ) + { if (err == NGX_ECONNREFUSED +#if (NGX_LINUX) + /* + * Linux returns EAGAIN instead of ECONNREFUSED + * for unix sockets if listen queue is full + */ + || err == NGX_EAGAIN +#endif || err == NGX_ENETDOWN || err == NGX_ENETUNREACH || err == NGX_EHOSTDOWN diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c --- a/src/event/ngx_event_openssl.c +++ b/src/event/ngx_event_openssl.c @@ -505,6 +505,9 @@ ngx_ssl_handshake(ngx_connection_t *c) if (n == 1) { + c->read->ready = 0; + c->write->ready = 1; + if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) { return NGX_ERROR; } @@ -1884,7 +1887,7 @@ ngx_ssl_get_cipher_name(ngx_connection_t ngx_int_t -ngx_ssl_get_certificate(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s) +ngx_ssl_get_raw_certificate(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s) { size_t len; BIO *bio; @@ -1934,6 +1937,50 @@ failed: ngx_int_t +ngx_ssl_get_certificate(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s) +{ + u_char *p; + size_t len; + ngx_uint_t i; + ngx_str_t cert; + + if (ngx_ssl_get_raw_certificate(c, pool, &cert) != NGX_OK) { + return NGX_ERROR; + } + + if (cert.len == 0) { + s->len = 0; + return NGX_OK; + } + + len = cert.len - 1; + + for (i = 0; i < cert.len - 1; i++) { + if (cert.data[i] == LF) { + len++; + } + } + + s->len = len; + s->data = ngx_pnalloc(pool, len); + if (s->data == NULL) { + return NGX_ERROR; + } + + p = s->data; + + for (i = 0; i < len; i++) { + *p++ = cert.data[i]; + if (cert.data[i] == LF) { + *p++ = '\t'; + } + } + + return NGX_OK; +} + + +ngx_int_t ngx_ssl_get_subject_dn(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s) { char *p; diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h --- a/src/event/ngx_event_openssl.h +++ b/src/event/ngx_event_openssl.h @@ -121,6 +121,8 @@ ngx_int_t ngx_ssl_get_protocol(ngx_conne ngx_str_t *s); ngx_int_t ngx_ssl_get_cipher_name(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s); +ngx_int_t ngx_ssl_get_raw_certificate(ngx_connection_t *c, ngx_pool_t *pool, + ngx_str_t *s); ngx_int_t ngx_ssl_get_certificate(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s); ngx_int_t ngx_ssl_get_subject_dn(ngx_connection_t *c, ngx_pool_t *pool, diff --git a/src/http/modules/ngx_http_autoindex_module.c b/src/http/modules/ngx_http_autoindex_module.c --- a/src/http/modules/ngx_http_autoindex_module.c +++ b/src/http/modules/ngx_http_autoindex_module.c @@ -135,7 +135,7 @@ ngx_http_autoindex_handler(ngx_http_requ { u_char *last, *filename, scale; off_t length; - size_t len, copy, allocated, root; + size_t len, utf_len, allocated, root; ngx_tm_t tm; ngx_err_t err; ngx_buf_t *b; @@ -329,7 +329,7 @@ ngx_http_autoindex_handler(ngx_http_requ NGX_ESCAPE_HTML); if (r->utf8) { - entry->utf_len = ngx_utf_length(entry->name.data, entry->name.len); + entry->utf_len = ngx_utf8_length(entry->name.data, entry->name.len); } else { entry->utf_len = len; } @@ -412,15 +412,16 @@ ngx_http_autoindex_handler(ngx_http_requ len = entry[i].utf_len; - if (entry[i].name.len - len) { + if (entry[i].name.len != len) { if (len > NGX_HTTP_AUTOINDEX_NAME_LEN) { - copy = NGX_HTTP_AUTOINDEX_NAME_LEN - 3 + 1; + utf_len = NGX_HTTP_AUTOINDEX_NAME_LEN - 3 + 1; } else { - copy = NGX_HTTP_AUTOINDEX_NAME_LEN + 1; + utf_len = NGX_HTTP_AUTOINDEX_NAME_LEN + 1; } - b->last = ngx_utf_cpystrn(b->last, entry[i].name.data, copy); + b->last = ngx_utf8_cpystrn(b->last, entry[i].name.data, + utf_len, entry[i].name.len + 1); last = b->last; } else { diff --git a/src/http/modules/ngx_http_charset_filter_module.c b/src/http/modules/ngx_http_charset_filter_module.c --- a/src/http/modules/ngx_http_charset_filter_module.c +++ b/src/http/modules/ngx_http_charset_filter_module.c @@ -642,7 +642,7 @@ ngx_http_charset_recode_from_utf8(ngx_po size = buf->last - src; saved = src; - n = ngx_utf_decode(&saved, size); + n = ngx_utf8_decode(&saved, size); if (n == 0xfffffffe) { /* incomplete UTF-8 symbol */ @@ -710,7 +710,7 @@ ngx_http_charset_recode_from_utf8(ngx_po } saved = ctx->saved; - n = ngx_utf_decode(&saved, i); + n = ngx_utf8_decode(&saved, i); c = '\0'; @@ -818,7 +818,7 @@ recode: len = buf->last - src; - n = ngx_utf_decode(&src, len); + n = ngx_utf8_decode(&src, len); if (n < 0x10000) { @@ -1270,7 +1270,7 @@ ngx_http_charset_map(ngx_conf_t *cf, ngx p = &table->src2dst[src * NGX_UTF_LEN] + 1; - n = ngx_utf_decode(&p, i); + n = ngx_utf8_decode(&p, i); if (n > 0xffff) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, diff --git a/src/http/modules/ngx_http_fastcgi_module.c b/src/http/modules/ngx_http_fastcgi_module.c --- a/src/http/modules/ngx_http_fastcgi_module.c +++ b/src/http/modules/ngx_http_fastcgi_module.c @@ -1120,9 +1120,7 @@ ngx_http_fastcgi_process_header(ngx_http ngx_memcpy(h->lowcase_key, r->lowcase_header, h->key.len); } else { - for (i = 0; i < h->key.len; i++) { - h->lowcase_key[i] = ngx_tolower(h->key.data[i]); - } + ngx_strlow(h->lowcase_key, h->key.data, h->key.len); } hh = ngx_hash_find(&umcf->headers_in_hash, h->hash, @@ -1167,6 +1165,13 @@ ngx_http_fastcgi_process_header(ngx_http u->headers_in.status_n = status; u->headers_in.status_line = *status_line; + } else if (u->headers_in.location) { + u->headers_in.status_n = 302; + u->headers_in.status_line.len = + sizeof("302 Moved Temporarily") - 1; + u->headers_in.status_line.data = + (u_char *) "302 Moved Temporarily"; + } else { u->headers_in.status_n = 200; u->headers_in.status_line.len = sizeof("200 OK") - 1; diff --git a/src/http/modules/ngx_http_flv_module.c b/src/http/modules/ngx_http_flv_module.c --- a/src/http/modules/ngx_http_flv_module.c +++ b/src/http/modules/ngx_http_flv_module.c @@ -107,6 +107,7 @@ ngx_http_flv_handler(ngx_http_request_t ngx_memzero(&of, sizeof(ngx_open_file_info_t)); + of.directio = clcf->directio; of.valid = clcf->open_file_cache_valid; of.min_uses = clcf->open_file_cache_min_uses; of.errors = clcf->open_file_cache_errors; @@ -212,9 +213,6 @@ ngx_http_flv_handler(ngx_http_request_t out[0].buf = b; out[0].next = &out[1]; - - } else { - r->allow_ranges = 1; } @@ -228,6 +226,8 @@ ngx_http_flv_handler(ngx_http_request_t return NGX_HTTP_INTERNAL_SERVER_ERROR; } + r->allow_ranges = 1; + rc = ngx_http_send_header(r); if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) { diff --git a/src/http/modules/ngx_http_gzip_static_module.c b/src/http/modules/ngx_http_gzip_static_module.c --- a/src/http/modules/ngx_http_gzip_static_module.c +++ b/src/http/modules/ngx_http_gzip_static_module.c @@ -121,6 +121,7 @@ ngx_http_gzip_static_handler(ngx_http_re ngx_memzero(&of, sizeof(ngx_open_file_info_t)); + of.directio = clcf->directio; of.valid = clcf->open_file_cache_valid; of.min_uses = clcf->open_file_cache_min_uses; of.errors = clcf->open_file_cache_errors; diff --git a/src/http/modules/ngx_http_index_module.c b/src/http/modules/ngx_http_index_module.c --- a/src/http/modules/ngx_http_index_module.c +++ b/src/http/modules/ngx_http_index_module.c @@ -210,6 +210,7 @@ ngx_http_index_handler(ngx_http_request_ ngx_memzero(&of, sizeof(ngx_open_file_info_t)); + of.directio = clcf->directio; of.valid = clcf->open_file_cache_valid; of.min_uses = clcf->open_file_cache_min_uses; of.errors = clcf->open_file_cache_errors; diff --git a/src/http/modules/ngx_http_map_module.c b/src/http/modules/ngx_http_map_module.c --- a/src/http/modules/ngx_http_map_module.c +++ b/src/http/modules/ngx_http_map_module.c @@ -106,7 +106,7 @@ ngx_http_map_variable(ngx_http_request_t size_t len; u_char *name; - ngx_uint_t key, i; + ngx_uint_t key; ngx_http_variable_value_t *vv, *value; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, @@ -135,11 +135,7 @@ ngx_http_map_variable(ngx_http_request_t return NGX_ERROR; } - key = 0; - for (i = 0; i < len; i++) { - name[i] = ngx_tolower(vv->data[i]); - key = ngx_hash(key, name[i]); - } + key = ngx_hash_strlow(name, vv->data, len); value = ngx_hash_find_combined(&map->hash, key, name, len); diff --git a/src/http/modules/ngx_http_proxy_module.c b/src/http/modules/ngx_http_proxy_module.c --- a/src/http/modules/ngx_http_proxy_module.c +++ b/src/http/modules/ngx_http_proxy_module.c @@ -1196,7 +1196,6 @@ static ngx_int_t ngx_http_proxy_process_header(ngx_http_request_t *r) { ngx_int_t rc; - ngx_uint_t i; ngx_table_elt_t *h; ngx_http_upstream_header_t *hh; ngx_http_upstream_main_conf_t *umcf; @@ -1237,9 +1236,7 @@ ngx_http_proxy_process_header(ngx_http_r ngx_memcpy(h->lowcase_key, r->lowcase_header, h->key.len); } else { - for (i = 0; i < h->key.len; i++) { - h->lowcase_key[i] = ngx_tolower(h->key.data[i]); - } + ngx_strlow(h->lowcase_key, h->key.data, h->key.len); } hh = ngx_hash_find(&umcf->headers_in_hash, h->hash, diff --git a/src/http/modules/ngx_http_range_filter_module.c b/src/http/modules/ngx_http_range_filter_module.c --- a/src/http/modules/ngx_http_range_filter_module.c +++ b/src/http/modules/ngx_http_range_filter_module.c @@ -58,6 +58,22 @@ typedef struct { } ngx_http_range_filter_ctx_t; +ngx_int_t ngx_http_range_parse(ngx_http_request_t *r, + ngx_http_range_filter_ctx_t *ctx); +static ngx_int_t ngx_http_range_singlepart_header(ngx_http_request_t *r, + ngx_http_range_filter_ctx_t *ctx); +static ngx_int_t ngx_http_range_multipart_header(ngx_http_request_t *r, + ngx_http_range_filter_ctx_t *ctx); +static ngx_int_t ngx_http_range_not_satisfiable(ngx_http_request_t *r); +static ngx_int_t ngx_http_range_test_overlapped(ngx_http_request_t *r, + ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in); +static ngx_int_t ngx_http_range_singlepart_body(ngx_http_request_t *r, + ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in, + ngx_http_output_body_filter_pt ngx_http_next_body_filter); +static ngx_int_t ngx_http_range_multipart_body(ngx_http_request_t *r, + ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in, + ngx_http_output_body_filter_pt ngx_http_next_body_filter); + static ngx_int_t ngx_http_range_header_filter_init(ngx_conf_t *cf); static ngx_int_t ngx_http_range_body_filter_init(ngx_conf_t *cf); static ngx_int_t ngx_http_range_late_filter_init(ngx_conf_t *cf); @@ -164,15 +180,8 @@ static ngx_http_output_body_filter_pt static ngx_int_t ngx_http_range_header_filter(ngx_http_request_t *r) { - u_char *p; - size_t len; - off_t start, end; time_t if_range; ngx_int_t rc; - ngx_uint_t suffix, i; - ngx_atomic_uint_t boundary; - ngx_table_elt_t *content_range; - ngx_http_range_t *range; ngx_http_range_filter_ctx_t *ctx; if (r->http_version < NGX_HTTP_VERSION_10 @@ -218,8 +227,54 @@ ngx_http_range_header_filter(ngx_http_re return NGX_ERROR; } - rc = 0; - range = NULL; + rc = ngx_http_range_parse(r, ctx); + + if (rc == NGX_OK) { + + ngx_http_set_ctx(r, ctx, ngx_http_range_body_filter_module); + + r->headers_out.status = NGX_HTTP_PARTIAL_CONTENT; + + if (ctx->ranges.nelts == 1) { + return ngx_http_range_singlepart_header(r, ctx); + } + + return ngx_http_range_multipart_header(r, ctx); + } + + if (rc == NGX_HTTP_RANGE_NOT_SATISFIABLE) { + return ngx_http_range_not_satisfiable(r); + } + + /* rc == NGX_ERROR */ + + return rc; + +next_filter: + + r->headers_out.accept_ranges = ngx_list_push(&r->headers_out.headers); + if (r->headers_out.accept_ranges == NULL) { + return NGX_ERROR; + } + + r->headers_out.accept_ranges->hash = 1; + 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_int_t +ngx_http_range_parse(ngx_http_request_t *r, ngx_http_range_filter_ctx_t *ctx) +{ + u_char *p; + off_t start, end; + ngx_uint_t suffix; + ngx_http_range_t *range; + p = r->headers_in.range->value.data + 6; for ( ;; ) { @@ -231,8 +286,7 @@ ngx_http_range_header_filter(ngx_http_re if (*p != '-') { if (*p < '0' || *p > '9') { - rc = NGX_HTTP_RANGE_NOT_SATISFIABLE; - break; + return NGX_HTTP_RANGE_NOT_SATISFIABLE; } while (*p >= '0' && *p <= '9') { @@ -242,13 +296,11 @@ ngx_http_range_header_filter(ngx_http_re while (*p == ' ') { p++; } if (*p++ != '-') { - rc = NGX_HTTP_RANGE_NOT_SATISFIABLE; - break; + return NGX_HTTP_RANGE_NOT_SATISFIABLE; } if (start >= r->headers_out.content_length_n) { - rc = NGX_HTTP_RANGE_NOT_SATISFIABLE; - break; + return NGX_HTTP_RANGE_NOT_SATISFIABLE; } while (*p == ' ') { p++; } @@ -263,7 +315,7 @@ ngx_http_range_header_filter(ngx_http_re range->end = r->headers_out.content_length_n; if (*p++ != ',') { - break; + return NGX_OK; } continue; @@ -275,8 +327,7 @@ ngx_http_range_header_filter(ngx_http_re } if (*p < '0' || *p > '9') { - rc = NGX_HTTP_RANGE_NOT_SATISFIABLE; - break; + return NGX_HTTP_RANGE_NOT_SATISFIABLE; } while (*p >= '0' && *p <= '9') { @@ -286,8 +337,7 @@ ngx_http_range_header_filter(ngx_http_re while (*p == ' ') { p++; } if (*p != ',' && *p != '\0') { - rc = NGX_HTTP_RANGE_NOT_SATISFIABLE; - break; + return NGX_HTTP_RANGE_NOT_SATISFIABLE; } if (suffix) { @@ -296,8 +346,7 @@ ngx_http_range_header_filter(ngx_http_re } if (start > end) { - rc = NGX_HTTP_RANGE_NOT_SATISFIABLE; - break; + return NGX_HTTP_RANGE_NOT_SATISFIABLE; } range = ngx_array_push(&ctx->ranges); @@ -319,86 +368,65 @@ ngx_http_range_header_filter(ngx_http_re } if (*p++ != ',') { - break; + return NGX_OK; } } +} + + +static ngx_int_t +ngx_http_range_singlepart_header(ngx_http_request_t *r, + ngx_http_range_filter_ctx_t *ctx) +{ + ngx_table_elt_t *content_range; + ngx_http_range_t *range; + + content_range = ngx_list_push(&r->headers_out.headers); + if (content_range == NULL) { + return NGX_ERROR; + } - if (rc) { - - /* rc == NGX_HTTP_RANGE_NOT_SATISFIABLE */ - - r->headers_out.status = rc; - - content_range = ngx_list_push(&r->headers_out.headers); - if (content_range == NULL) { - return NGX_ERROR; - } - - r->headers_out.content_range = content_range; + r->headers_out.content_range = content_range; - content_range->hash = 1; - content_range->key.len = sizeof("Content-Range") - 1; - content_range->key.data = (u_char *) "Content-Range"; + content_range->hash = 1; + content_range->key.len = sizeof("Content-Range") - 1; + content_range->key.data = (u_char *) "Content-Range"; - content_range->value.data = ngx_pnalloc(r->pool, - sizeof("bytes */") - 1 + NGX_OFF_T_LEN); - if (content_range->value.data == NULL) { - return NGX_ERROR; - } - - content_range->value.len = ngx_sprintf(content_range->value.data, - "bytes */%O", - r->headers_out.content_length_n) - - content_range->value.data; - - ngx_http_clear_content_length(r); - - return rc; + content_range->value.data = ngx_pnalloc(r->pool, + sizeof("bytes -/") - 1 + 3 * NGX_OFF_T_LEN); + if (content_range->value.data == NULL) { + return NGX_ERROR; } - ngx_http_set_ctx(r, ctx, ngx_http_range_body_filter_module); - - r->headers_out.status = NGX_HTTP_PARTIAL_CONTENT; - - if (ctx->ranges.nelts == 1) { + /* "Content-Range: bytes SSSS-EEEE/TTTT" header */ - content_range = ngx_list_push(&r->headers_out.headers); - if (content_range == NULL) { - return NGX_ERROR; - } - - r->headers_out.content_range = content_range; - - content_range->hash = 1; - content_range->key.len = sizeof("Content-Range") - 1; - content_range->key.data = (u_char *) "Content-Range"; + range = ctx->ranges.elts; - content_range->value.data = - ngx_pnalloc(r->pool, sizeof("bytes -/") - 1 + 3 * NGX_OFF_T_LEN); - if (content_range->value.data == NULL) { - return NGX_ERROR; - } - - /* "Content-Range: bytes SSSS-EEEE/TTTT" header */ + content_range->value.len = ngx_sprintf(content_range->value.data, + "bytes %O-%O/%O", + range->start, range->end - 1, + r->headers_out.content_length_n) + - content_range->value.data; - content_range->value.len = ngx_sprintf(content_range->value.data, - "bytes %O-%O/%O", - range->start, range->end - 1, - r->headers_out.content_length_n) - - content_range->value.data; + r->headers_out.content_length_n = range->end - range->start; - r->headers_out.content_length_n = range->end - range->start; - - if (r->headers_out.content_length) { - r->headers_out.content_length->hash = 0; - r->headers_out.content_length = NULL; - } - - return ngx_http_next_header_filter(r); + if (r->headers_out.content_length) { + r->headers_out.content_length->hash = 0; + r->headers_out.content_length = NULL; } + return ngx_http_next_header_filter(r); +} - /* TODO: what if no content_type ?? */ + +static ngx_int_t +ngx_http_range_multipart_header(ngx_http_request_t *r, + ngx_http_range_filter_ctx_t *ctx) +{ + size_t len; + ngx_uint_t i; + ngx_http_range_t *range; + ngx_atomic_uint_t boundary; len = sizeof(CRLF "--") - 1 + NGX_ATOMIC_T_LEN + sizeof(CRLF "Content-Type: ") - 1 @@ -436,7 +464,7 @@ ngx_http_range_header_filter(ngx_http_re r->headers_out.charset.len = 0; - } else { + } else if (r->headers_out.content_type.len) { ctx->boundary_header.len = ngx_sprintf(ctx->boundary_header.data, CRLF "--%0muA" CRLF "Content-Type: %V" CRLF @@ -444,6 +472,13 @@ ngx_http_range_header_filter(ngx_http_re boundary, &r->headers_out.content_type) - ctx->boundary_header.data; + + } else { + ctx->boundary_header.len = ngx_sprintf(ctx->boundary_header.data, + CRLF "--%0muA" CRLF + "Content-Range: bytes ", + boundary) + - ctx->boundary_header.data; } r->headers_out.content_type.data = @@ -498,21 +533,41 @@ ngx_http_range_header_filter(ngx_http_re } return ngx_http_next_header_filter(r); +} -next_filter: - r->headers_out.accept_ranges = ngx_list_push(&r->headers_out.headers); - if (r->headers_out.accept_ranges == NULL) { +static ngx_int_t +ngx_http_range_not_satisfiable(ngx_http_request_t *r) +{ + ngx_table_elt_t *content_range; + + r->headers_out.status = NGX_HTTP_RANGE_NOT_SATISFIABLE; + + content_range = ngx_list_push(&r->headers_out.headers); + if (content_range == NULL) { return NGX_ERROR; } - r->headers_out.accept_ranges->hash = 1; - r->headers_out.accept_ranges->key.len = sizeof("Accept-Ranges") - 1; - r->headers_out.accept_ranges->key.data = (u_char *) "Accept-Ranges"; - r->headers_out.accept_ranges->value.len = sizeof("bytes") - 1; - r->headers_out.accept_ranges->value.data = (u_char *) "bytes"; + r->headers_out.content_range = content_range; + + content_range->hash = 1; + content_range->key.len = sizeof("Content-Range") - 1; + content_range->key.data = (u_char *) "Content-Range"; - return ngx_http_next_header_filter(r); + content_range->value.data = ngx_pnalloc(r->pool, + sizeof("bytes */") - 1 + NGX_OFF_T_LEN); + if (content_range->value.data == NULL) { + return NGX_ERROR; + } + + content_range->value.len = ngx_sprintf(content_range->value.data, + "bytes */%O", + r->headers_out.content_length_n) + - content_range->value.data; + + ngx_http_clear_content_length(r); + + return NGX_HTTP_RANGE_NOT_SATISFIABLE; } @@ -520,11 +575,6 @@ static ngx_int_t ngx_http_range_body_generic_filter(ngx_http_request_t *r, ngx_chain_t *in, ngx_http_output_body_filter_pt ngx_http_next_body_filter) { - off_t start, last; - ngx_buf_t *b, *buf; - ngx_uint_t i; - ngx_chain_t *out, *cl, *hcl, *rcl, *dcl, **ll; - ngx_http_range_t *range; ngx_http_range_filter_ctx_t *ctx; if (in == NULL) { @@ -537,25 +587,88 @@ ngx_http_range_body_generic_filter(ngx_h return ngx_http_next_body_filter(r, in); } - buf = in->buf; + if (ctx->ranges.nelts == 1) { + return ngx_http_range_singlepart_body(r, ctx, in, + ngx_http_next_body_filter); + } + + /* + * multipart ranges are supported only if whole body is in a single buffer + */ if (ngx_buf_special(in->buf)) { return ngx_http_next_body_filter(r, in); } - range = ctx->ranges.elts; + if (ngx_http_range_test_overlapped(r, ctx, in) != NGX_OK) { + return NGX_ERROR; + } + + return ngx_http_range_multipart_body(r, ctx, in, + ngx_http_next_body_filter); +} + - if (ctx->ranges.nelts > 1) { - goto multipart; +static ngx_int_t +ngx_http_range_test_overlapped(ngx_http_request_t *r, + ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in) +{ + off_t start, last; + ngx_buf_t *buf; + ngx_uint_t i; + ngx_http_range_t *range; + + if (ctx->offset) { + goto overlapped; } - /* - * the optimized version for the responses - * that are passed in the single buffer - */ + buf = in->buf; + + if (!buf->last_buf) { + + if (buf->in_file) { + start = buf->file_pos + ctx->offset; + last = buf->file_last + ctx->offset; + + } else { + start = buf->pos - buf->start + ctx->offset; + last = buf->last - buf->start + ctx->offset; + } + + range = ctx->ranges.elts; + for (i = 0; i < ctx->ranges.nelts; i++) { + if (start > range[i].start || last < range[i].end) { + goto overlapped; + } + } + } + + ctx->offset = ngx_buf_size(buf); + + return NGX_OK; + +overlapped: + + ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, + "range in overlapped buffers"); + + return NGX_ERROR; +} + + +static ngx_int_t +ngx_http_range_singlepart_body(ngx_http_request_t *r, + ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in, + ngx_http_output_body_filter_pt ngx_http_next_body_filter) +{ + off_t start, last; + ngx_buf_t *buf; + ngx_chain_t *out, *cl, **ll; + ngx_http_range_t *range; out = NULL; ll = &out; + range = ctx->ranges.elts; for (cl = in; cl; cl = cl->next) { @@ -567,44 +680,44 @@ ngx_http_range_body_generic_filter(ngx_h ctx->offset = last; ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "range body filter: %O-%O", start, last); + "http range body buf: %O-%O", start, last); if (ngx_buf_special(buf)) { - /* pass anyway */ - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "range body filter: pass special"); *ll = cl; ll = &cl->next; continue; } if (range->end <= start || range->start >= last) { - /* skip buffer */ + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "range body filter: skip"); + "http range body skip"); + buf->pos = buf->last; continue; } if (range->start > start) { + if (buf->in_file) { buf->file_pos += range->start - start; } + if (ngx_buf_in_memory(buf)) { buf->pos += (size_t) (range->start - start); } } if (range->end <= last) { + if (buf->in_file) { buf->file_last -= last - range->end; } + if (ngx_buf_in_memory(buf)) { buf->last -= (size_t) (last - range->end); } - /* we are done */ - buf->last_buf = 1; *ll = cl; cl->next = NULL; @@ -621,34 +734,22 @@ ngx_http_range_body_generic_filter(ngx_h } return ngx_http_next_body_filter(r, out); - -multipart: +} - if (ctx->offset) { - goto overlapped; - } - - if (!buf->last_buf) { - - if (buf->in_file) { - start = buf->file_pos + ctx->offset; - last = buf->file_last + ctx->offset; - } else { - start = buf->pos - buf->start + ctx->offset; - last = buf->last - buf->start + ctx->offset; - } - - for (i = 0; i < ctx->ranges.nelts; i++) { - if (start > range[i].start || last < range[i].end) { - goto overlapped; - } - } - } - - ctx->offset = ngx_buf_size(buf); +static ngx_int_t +ngx_http_range_multipart_body(ngx_http_request_t *r, + ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in, + ngx_http_output_body_filter_pt ngx_http_next_body_filter) +{ + ngx_buf_t *b, *buf; + ngx_uint_t i; + ngx_chain_t *out, *hcl, *rcl, *dcl, **ll; + ngx_http_range_t *range; ll = &out; + buf = in->buf; + range = ctx->ranges.elts; for (i = 0; i < ctx->ranges.nelts; i++) { @@ -764,13 +865,6 @@ multipart: *ll = hcl; return ngx_http_next_body_filter(r, out); - -overlapped: - - ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, - "range in overlapped buffers"); - - return NGX_ERROR; } diff --git a/src/http/modules/ngx_http_ssi_filter_module.c b/src/http/modules/ngx_http_ssi_filter_module.c --- a/src/http/modules/ngx_http_ssi_filter_module.c +++ b/src/http/modules/ngx_http_ssi_filter_module.c @@ -1605,7 +1605,7 @@ ngx_http_ssi_evaluate_string(ngx_http_re size_t *size, len, prefix, part_len; ngx_str_t var, *val; ngx_int_t key; - ngx_uint_t i, j, n, bracket, quoted; + ngx_uint_t i, n, bracket, quoted; ngx_array_t lengths, values; ngx_http_variable_value_t *vv; @@ -1731,12 +1731,7 @@ ngx_http_ssi_evaluate_string(ngx_http_re goto invalid_variable; } - key = 0; - - for (j = 0; j < var.len; j++) { - var.data[j] = ngx_tolower(var.data[j]); - key = ngx_hash(key, var.data[j]); - } + key = ngx_hash_strlow(var.data, var.data, var.len); val = ngx_http_ssi_get_variable(r, &var, key); @@ -2025,12 +2020,7 @@ ngx_http_ssi_include(ngx_http_request_t } if (set) { - key = 0; - - for (i = 0; i < set->len; i++) { - set->data[i] = ngx_tolower(set->data[i]); - key = ngx_hash(key, set->data[i]); - } + key = ngx_hash_strlow(set->data, set->data, set->len); psr = ngx_palloc(r->pool, sizeof(ngx_http_post_subrequest_t)); if (psr == NULL) { @@ -2141,7 +2131,6 @@ ngx_http_ssi_echo(ngx_http_request_t *r, u_char *p; uintptr_t len; ngx_int_t key; - ngx_uint_t i; ngx_buf_t *b; ngx_str_t *var, *value, *enc, text; ngx_chain_t *cl; @@ -2152,12 +2141,7 @@ ngx_http_ssi_echo(ngx_http_request_t *r, ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ssi echo \"%V\"", var); - key = 0; - - for (i = 0; i < var->len; i++) { - var->data[i] = ngx_tolower(var->data[i]); - key = ngx_hash(key, var->data[i]); - } + key = ngx_hash_strlow(var->data, var->data, var->len); value = ngx_http_ssi_get_variable(r, var, key); @@ -2310,7 +2294,6 @@ ngx_http_ssi_set(ngx_http_request_t *r, ngx_str_t **params) { ngx_int_t key, rc; - ngx_uint_t i; ngx_str_t *name, *value, *vv; ngx_http_ssi_var_t *var; ngx_http_ssi_ctx_t *mctx; @@ -2337,12 +2320,7 @@ ngx_http_ssi_set(ngx_http_request_t *r, return rc; } - key = 0; - - for (i = 0; i < name->len; i++) { - name->data[i] = ngx_tolower(name->data[i]); - key = ngx_hash(key, name->data[i]); - } + key = ngx_hash_strlow(name->data, name->data, name->len); vv = ngx_http_ssi_get_variable(r, name, key); diff --git a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c --- a/src/http/modules/ngx_http_ssl_module.c +++ b/src/http/modules/ngx_http_ssl_module.c @@ -13,9 +13,9 @@ typedef ngx_int_t (*ngx_ssl_variable_han ngx_pool_t *pool, ngx_str_t *s); -#define NGX_DEFLAUT_CERTIFICATE "cert.pem" -#define NGX_DEFLAUT_CERTIFICATE_KEY "cert.pem" -#define NGX_DEFLAUT_CIPHERS "ALL:!ADH:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP" +#define NGX_DEFAULT_CERTIFICATE "cert.pem" +#define NGX_DEFAULT_CERTIFICATE_KEY "cert.pem" +#define NGX_DEFAULT_CIPHERS "ALL:!ADH:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP" static ngx_int_t ngx_http_ssl_static_variable(ngx_http_request_t *r, @@ -49,6 +49,14 @@ static ngx_conf_bitmask_t ngx_http_ssl_ }; +static ngx_conf_enum_t ngx_http_ssl_verify[] = { + { ngx_string("off"), 0 }, + { ngx_string("on"), 1 }, + { ngx_string("ask"), 2 }, + { ngx_null_string, 0 } +}; + + static ngx_command_t ngx_http_ssl_commands[] = { { ngx_string("ssl"), @@ -95,10 +103,10 @@ static ngx_command_t ngx_http_ssl_comma { ngx_string("ssl_verify_client"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG, - ngx_conf_set_flag_slot, + ngx_conf_set_enum_slot, NGX_HTTP_SRV_CONF_OFFSET, offsetof(ngx_http_ssl_srv_conf_t, verify), - NULL }, + &ngx_http_ssl_verify }, { ngx_string("ssl_verify_depth"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_1MORE, @@ -185,6 +193,10 @@ static ngx_http_variable_t ngx_http_ssl { ngx_string("ssl_client_cert"), NULL, ngx_http_ssl_variable, (uintptr_t) ngx_ssl_get_certificate, NGX_HTTP_VAR_CHANGEABLE, 0 }, + { ngx_string("ssl_client_raw_cert"), NULL, ngx_http_ssl_variable, + (uintptr_t) ngx_ssl_get_raw_certificate, + NGX_HTTP_VAR_CHANGEABLE, 0 }, + { ngx_string("ssl_client_s_dn"), NULL, ngx_http_ssl_variable, (uintptr_t) ngx_ssl_get_subject_dn, NGX_HTTP_VAR_CHANGEABLE, 0 }, @@ -307,9 +319,9 @@ ngx_http_ssl_create_srv_conf(ngx_conf_t */ sscf->enable = NGX_CONF_UNSET; + sscf->prefer_server_ciphers = NGX_CONF_UNSET; sscf->verify = NGX_CONF_UNSET; sscf->verify_depth = NGX_CONF_UNSET; - sscf->prefer_server_ciphers = NGX_CONF_UNSET; sscf->builtin_session_cache = NGX_CONF_UNSET; sscf->session_timeout = NGX_CONF_UNSET; @@ -341,21 +353,21 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t * (NGX_CONF_BITMASK_SET |NGX_SSL_SSLv2|NGX_SSL_SSLv3|NGX_SSL_TLSv1)); - ngx_conf_merge_value(conf->verify, prev->verify, 0); - ngx_conf_merge_value(conf->verify_depth, prev->verify_depth, 1); + ngx_conf_merge_uint_value(conf->verify, prev->verify, 0); + ngx_conf_merge_uint_value(conf->verify_depth, prev->verify_depth, 1); ngx_conf_merge_str_value(conf->certificate, prev->certificate, - NGX_DEFLAUT_CERTIFICATE); + NGX_DEFAULT_CERTIFICATE); ngx_conf_merge_str_value(conf->certificate_key, prev->certificate_key, - NGX_DEFLAUT_CERTIFICATE_KEY); + NGX_DEFAULT_CERTIFICATE_KEY); ngx_conf_merge_str_value(conf->dhparam, prev->dhparam, ""); ngx_conf_merge_str_value(conf->client_certificate, prev->client_certificate, ""); - ngx_conf_merge_str_value(conf->ciphers, prev->ciphers, NGX_DEFLAUT_CIPHERS); + ngx_conf_merge_str_value(conf->ciphers, prev->ciphers, NGX_DEFAULT_CIPHERS); conf->ssl.log = cf->log; @@ -402,6 +414,13 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t * } if (conf->verify) { + + if (conf->client_certificate.len == 0) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "no ssl_client_certificate for ssl_client_verify"); + return NGX_CONF_ERROR; + } + if (ngx_ssl_client_certificate(cf, &conf->ssl, &conf->client_certificate, conf->verify_depth) diff --git a/src/http/modules/ngx_http_ssl_module.h b/src/http/modules/ngx_http_ssl_module.h --- a/src/http/modules/ngx_http_ssl_module.h +++ b/src/http/modules/ngx_http_ssl_module.h @@ -22,8 +22,8 @@ typedef struct { ngx_uint_t protocols; - ngx_int_t verify; - ngx_int_t verify_depth; + ngx_uint_t verify; + ngx_uint_t verify_depth; ssize_t builtin_session_cache; diff --git a/src/http/modules/ngx_http_static_module.c b/src/http/modules/ngx_http_static_module.c --- a/src/http/modules/ngx_http_static_module.c +++ b/src/http/modules/ngx_http_static_module.c @@ -98,6 +98,7 @@ ngx_http_static_handler(ngx_http_request ngx_memzero(&of, sizeof(ngx_open_file_info_t)); + of.directio = clcf->directio; of.valid = clcf->open_file_cache_valid; of.min_uses = clcf->open_file_cache_min_uses; of.errors = clcf->open_file_cache_errors; diff --git a/src/http/modules/ngx_http_sub_filter_module.c b/src/http/modules/ngx_http_sub_filter_module.c --- a/src/http/modules/ngx_http_sub_filter_module.c +++ b/src/http/modules/ngx_http_sub_filter_module.c @@ -632,7 +632,6 @@ ngx_http_sub_filter(ngx_conf_t *cf, ngx_ ngx_str_t *value; ngx_int_t n; - ngx_uint_t i; ngx_http_script_compile_t sc; if (slcf->match.len) { @@ -641,11 +640,9 @@ ngx_http_sub_filter(ngx_conf_t *cf, ngx_ value = cf->args->elts; - slcf->match = value[1]; + ngx_strlow(value[1].data, value[1].data, value[1].len); - for (i = 0; i < value[1].len; i++) { - value[1].data[i] = ngx_tolower(value[1].data[i]); - } + slcf->match = value[1]; n = ngx_http_script_variables_count(&value[2]); diff --git a/src/http/modules/ngx_http_xslt_filter_module.c b/src/http/modules/ngx_http_xslt_filter_module.c new file mode 100644 --- /dev/null +++ b/src/http/modules/ngx_http_xslt_filter_module.c @@ -0,0 +1,1109 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + + +#ifndef NGX_HTTP_XSLT_REUSE_DTD +#define NGX_HTTP_XSLT_REUSE_DTD 1 +#endif + + +typedef struct { + ngx_array_t *lengths; + ngx_array_t *values; +} ngx_http_xslt_param_t; + + +typedef struct { + xsltStylesheetPtr stylesheet; + ngx_array_t params; /* ngx_http_xslt_param_t */ +} ngx_http_xslt_sheet_t; + + +typedef struct { + xmlDtdPtr dtd; + ngx_array_t sheets; /* ngx_http_xslt_sheet_t */ + ngx_hash_t types_hash; + ngx_array_t *keys; +} ngx_http_xslt_filter_conf_t; + + +typedef struct { + xmlDocPtr doc; + xmlParserCtxtPtr ctxt; + xmlSAXHandler *sax; + ngx_http_request_t *request; + ngx_array_t params; + unsigned done:1; + unsigned html:1; +} ngx_http_xslt_filter_ctx_t; + + +static ngx_int_t ngx_http_xslt_send(ngx_http_request_t *r, + ngx_http_xslt_filter_ctx_t *ctx, ngx_buf_t *b); +static ngx_int_t ngx_http_xslt_filter_internal_error(ngx_http_request_t *r); +static ngx_int_t ngx_http_xslt_add_chunk(ngx_http_request_t *r, + ngx_http_xslt_filter_ctx_t *ctx, ngx_buf_t *b); + + +static void ngx_http_xslt_sax_start_document(void *data); +static void ngx_http_xslt_sax_end_document(void *data); +static void ngx_http_xslt_sax_internal_subset(void *data, const xmlChar *name, + const xmlChar *externalId, const xmlChar *systemId); +static void ngx_http_xslt_sax_external_subset(void *data, const xmlChar *name, + const xmlChar *externalId, const xmlChar *systemId); +static void ngx_http_xslt_sax_entity_decl(void *data, const xmlChar *name, + int type, const xmlChar *publicId, const xmlChar *systemId, + xmlChar *content); +static void ngx_http_xslt_sax_attribute_decl(void *data, const xmlChar *elem, + const xmlChar *fullname, int type, int def, const xmlChar *defaultValue, + xmlEnumerationPtr tree); +static void ngx_http_xslt_sax_element_decl(void *data, const xmlChar * name, + int type, xmlElementContentPtr content); +static void ngx_http_xslt_sax_notation_decl(void *data, const xmlChar *name, + const xmlChar *publicId, const xmlChar *systemId); +static void ngx_http_xslt_sax_unparsed_entity_decl(void *data, + const xmlChar *name, const xmlChar *publicId, const xmlChar *systemId, + const xmlChar *notationName); +static void ngx_http_xslt_sax_start_element(void *data, + const xmlChar *localname, const xmlChar *prefix, const xmlChar *URI, + int nb_namespaces, const xmlChar **namespaces, int nb_attributes, + int nb_defaulted, const xmlChar **attributes); +static void ngx_http_xslt_sax_end_element(void *data, + const xmlChar * localname ATTRIBUTE_UNUSED, + const xmlChar * prefix ATTRIBUTE_UNUSED, + const xmlChar * URI ATTRIBUTE_UNUSED); +static void ngx_http_xslt_sax_characters(void *data, const xmlChar *p, int len); +static void ngx_http_xslt_sax_cdata_block(void *data, const xmlChar *p, + int len); +static xmlEntityPtr ngx_http_xslt_sax_get_entity(void *data, + const xmlChar *name); +static xmlEntityPtr ngx_http_xslt_sax_get_parameter_entity(void *data, + const xmlChar *name); +static xmlParserInputPtr ngx_http_xslt_sax_resolve_entity(void *data, + const xmlChar *publicId, const xmlChar *systemId); +static void ngx_http_xslt_sax_reference(void *data, const xmlChar *name); +static void ngx_http_xslt_sax_comment(void *data, const xmlChar *value); +static void ngx_http_xslt_sax_processing_instruction(void *data, + const xmlChar *target, const xmlChar *pidata); +static int ngx_http_xslt_sax_is_standalone(void *data); +static int ngx_http_xslt_sax_has_internal_subset(void *data); +static int ngx_http_xslt_sax_has_external_subset(void *data); +static void ngx_cdecl ngx_http_xslt_sax_error(void *data, const char *msg, ...); + + +static ngx_buf_t *ngx_http_xslt_apply_stylesheet(ngx_http_request_t *r, + ngx_http_xslt_filter_ctx_t *ctx); +static ngx_int_t ngx_http_xslt_params(ngx_http_request_t *r, + ngx_http_xslt_filter_ctx_t *ctx, ngx_array_t *params); +static void ngx_http_xslt_cleanup(void *data); + +static char *ngx_http_xslt_entities(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static char *ngx_http_xslt_stylesheet(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static void ngx_http_xslt_cleanup_stylesheet(void *data); +static void *ngx_http_xslt_filter_create_conf(ngx_conf_t *cf); +static char *ngx_http_xslt_filter_merge_conf(ngx_conf_t *cf, void *parent, + void *child); +static ngx_int_t ngx_http_xslt_filter_init(ngx_conf_t *cf); + + +ngx_str_t ngx_http_xslt_default_types[] = { + ngx_string("text/xml"), + ngx_null_string +}; + + +static ngx_command_t ngx_http_xslt_filter_commands[] = { + + { ngx_string("xml_entities"), + NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1, + ngx_http_xslt_entities, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("xslt_stylesheet"), + NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_1MORE, + ngx_http_xslt_stylesheet, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("xslt_types"), + NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_1MORE, + ngx_http_types_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_xslt_filter_conf_t, keys), + &ngx_http_xslt_default_types[0] }, + + ngx_null_command +}; + + +static ngx_http_module_t ngx_http_xslt_filter_module_ctx = { + NULL, /* preconfiguration */ + ngx_http_xslt_filter_init, /* postconfiguration */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + NULL, /* create server configuration */ + NULL, /* merge server configuration */ + + ngx_http_xslt_filter_create_conf, /* create location configuration */ + ngx_http_xslt_filter_merge_conf /* merge location configuration */ +}; + + +ngx_module_t ngx_http_xslt_filter_module = { + NGX_MODULE_V1, + &ngx_http_xslt_filter_module_ctx, /* module context */ + ngx_http_xslt_filter_commands, /* module directives */ + NGX_HTTP_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; + + +static ngx_http_output_header_filter_pt ngx_http_next_header_filter; +static ngx_http_output_body_filter_pt ngx_http_next_body_filter; + + +static ngx_int_t +ngx_http_xslt_header_filter(ngx_http_request_t *r) +{ + ngx_http_xslt_filter_ctx_t *ctx; + ngx_http_xslt_filter_conf_t *conf; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "xslt filter header"); + + if (r->headers_out.status == NGX_HTTP_NOT_MODIFIED) { + return ngx_http_next_header_filter(r); + } + + conf = ngx_http_get_module_loc_conf(r, ngx_http_xslt_filter_module); + + if (conf->sheets.nelts == 0 + || ngx_http_test_content_type(r, &conf->types_hash) == NULL) + { + return ngx_http_next_header_filter(r); + } + + ctx = ngx_http_get_module_ctx(r, ngx_http_xslt_filter_module); + + if (ctx) { + return ngx_http_next_header_filter(r); + } + + ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_xslt_filter_ctx_t)); + if (ctx == NULL) { + return NGX_ERROR; + } + + ngx_http_set_ctx(r, ctx, ngx_http_xslt_filter_module); + + r->main_filter_need_in_memory = 1; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_xslt_body_filter(ngx_http_request_t *r, ngx_chain_t *in) +{ + ngx_chain_t *cl; + ngx_http_xslt_filter_ctx_t *ctx; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "xslt filter body"); + + if (in == NULL) { + return ngx_http_next_body_filter(r, in); + } + + ctx = ngx_http_get_module_ctx(r, ngx_http_xslt_filter_module); + + if (ctx == NULL || ctx->done) { + return ngx_http_next_body_filter(r, in); + } + + for (cl = in; cl; cl = cl->next) { + + if (ngx_http_xslt_add_chunk(r, ctx, cl->buf) != NGX_OK) { + + if (ctx->ctxt->myDoc){ + +#if (NGX_HTTP_XSLT_REUSE_DTD) + ctx->ctxt->myDoc->extSubset = NULL; +#endif + xmlFreeDoc(ctx->ctxt->myDoc); + } + + xmlFreeParserCtxt(ctx->ctxt); + + return ngx_http_xslt_send(r, ctx, NULL); + } + + if (cl->buf->last_buf) { + + ctx->doc = ctx->ctxt->myDoc; + +#if (NGX_HTTP_XSLT_REUSE_DTD) + ctx->doc->extSubset = NULL; +#endif + + xmlFreeParserCtxt(ctx->ctxt); + + if (ctx->ctxt->wellFormed) { + return ngx_http_xslt_send(r, ctx, + ngx_http_xslt_apply_stylesheet(r, ctx)); + } + + xmlFreeDoc(ctx->doc); + + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "not well formed XML document"); + + return ngx_http_xslt_send(r, ctx, NULL); + } + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_xslt_send(ngx_http_request_t *r, ngx_http_xslt_filter_ctx_t *ctx, + ngx_buf_t *b) +{ + ngx_int_t rc; + ngx_chain_t out; + ngx_pool_cleanup_t *cln; + + ctx->done = 1; + + if (b == NULL) { + return ngx_http_xslt_filter_internal_error(r); + } + + cln = ngx_pool_cleanup_add(r->pool, 0); + + if (cln == NULL) { + ngx_free(b->pos); + return ngx_http_special_response_handler(r, + NGX_HTTP_INTERNAL_SERVER_ERROR); + } + + if (ctx->html) { + r->headers_out.content_type_len = sizeof("text/html") - 1; + r->headers_out.content_type.len = sizeof("text/html") - 1; + r->headers_out.content_type.data = (u_char *) "text/html"; + } + + r->headers_out.content_length_n = b->last - b->pos; + + if (r->headers_out.content_length) { + r->headers_out.content_length->hash = 0; + r->headers_out.content_length = NULL; + } + + r->allow_ranges = 1; + + rc = ngx_http_next_header_filter(r); + + if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) { + ngx_free(b->pos); + return rc; + } + + cln->handler = ngx_http_xslt_cleanup; + cln->data = b->pos; + + out.buf = b; + out.next = NULL; + + return ngx_http_next_body_filter(r, &out); +} + + +static ngx_int_t +ngx_http_xslt_filter_internal_error(ngx_http_request_t *r) +{ + ngx_int_t rc; + + /* clear the modules contexts */ + ngx_memzero(r->ctx, sizeof(void *) * ngx_http_max_module); + + rc = ngx_http_special_response_handler(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + + /* NGX_ERROR resets any pending data */ + + return (rc == NGX_OK) ? NGX_ERROR : rc; +} + + +static ngx_int_t +ngx_http_xslt_add_chunk(ngx_http_request_t *r, ngx_http_xslt_filter_ctx_t *ctx, + ngx_buf_t *b) +{ + int err; + xmlSAXHandler *sax; + xmlParserCtxtPtr ctxt; + + if (ctx->ctxt == NULL) { + + ctxt = xmlCreatePushParserCtxt(NULL, NULL, NULL, 0, NULL); + if (ctxt == NULL) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "xmlCreatePushParserCtxt() failed"); + return NGX_ERROR; + } + + ctx->sax = ngx_palloc(r->pool, sizeof(xmlSAXHandler)); + if (ctx->sax == NULL) { + return NGX_ERROR; + } + + sax = ctxt->sax; + + ngx_memcpy(ctx->sax, sax, sizeof(xmlSAXHandler)); + + sax->startDocument = ngx_http_xslt_sax_start_document; + sax->endDocument = ngx_http_xslt_sax_end_document; + + sax->internalSubset = ngx_http_xslt_sax_internal_subset; + sax->externalSubset = ngx_http_xslt_sax_external_subset; + sax->entityDecl = ngx_http_xslt_sax_entity_decl; + sax->attributeDecl = ngx_http_xslt_sax_attribute_decl; + sax->elementDecl = ngx_http_xslt_sax_element_decl; + sax->notationDecl = ngx_http_xslt_sax_notation_decl; + sax->unparsedEntityDecl = ngx_http_xslt_sax_unparsed_entity_decl; + sax->setDocumentLocator = NULL; + + sax->startElementNs = ngx_http_xslt_sax_start_element; + sax->endElementNs = ngx_http_xslt_sax_end_element; + + sax->characters = ngx_http_xslt_sax_characters; + sax->ignorableWhitespace = ngx_http_xslt_sax_characters; + sax->cdataBlock = ngx_http_xslt_sax_cdata_block; + sax->getEntity = ngx_http_xslt_sax_get_entity; + sax->resolveEntity = ngx_http_xslt_sax_resolve_entity; + sax->getParameterEntity = ngx_http_xslt_sax_get_parameter_entity; + sax->reference = ngx_http_xslt_sax_reference; + sax->comment = ngx_http_xslt_sax_comment; + sax->processingInstruction = ngx_http_xslt_sax_processing_instruction; + + sax->isStandalone = ngx_http_xslt_sax_is_standalone; + sax->hasInternalSubset = ngx_http_xslt_sax_has_internal_subset; + sax->hasExternalSubset = ngx_http_xslt_sax_has_external_subset; + + sax->warning = NULL; + sax->error = ngx_http_xslt_sax_error; + sax->fatalError = ngx_http_xslt_sax_error; + + ctxt->userData = ctx; + + ctxt->replaceEntities = 1; + ctxt->loadsubset = 1; + + ctx->ctxt = ctxt; + ctx->request = r; + } + + err = xmlParseChunk(ctx->ctxt, (char *) b->pos, + (int) (b->last - b->pos), b->last_buf); + + if (err == 0) { + b->pos = b->last; + return NGX_OK; + } + + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "xmlParseChunk() failed, error:%d", err); + + return NGX_ERROR; +} + + +static void +ngx_http_xslt_sax_start_document(void *data) +{ + ngx_http_xslt_filter_ctx_t *ctx = data; + + ctx->sax->startDocument(ctx->ctxt); +} + + +static void +ngx_http_xslt_sax_end_document(void *data) +{ + ngx_http_xslt_filter_ctx_t *ctx = data; + + ctx->sax->endDocument(ctx->ctxt); +} + + +static void +ngx_http_xslt_sax_internal_subset(void *data, const xmlChar *name, + const xmlChar *externalId, const xmlChar *systemId) +{ + ngx_http_xslt_filter_ctx_t *ctx = data; + + ctx->sax->internalSubset(ctx->ctxt, name, externalId, systemId); +} + + +static void +ngx_http_xslt_sax_external_subset(void *data, const xmlChar *name, + const xmlChar *externalId, const xmlChar *systemId) +{ + ngx_http_xslt_filter_ctx_t *ctx = data; + + xmlDocPtr doc; + xmlDtdPtr dtd; + ngx_http_request_t *r; + ngx_http_xslt_filter_conf_t *conf; + + r = ctx->request; + + conf = ngx_http_get_module_loc_conf(r, ngx_http_xslt_filter_module); + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "xslt filter extSubset: \"%s\" \"%s\" \"%s\"", + name ? name : (xmlChar *) "", + externalId ? externalId : (xmlChar *) "", + systemId ? systemId : (xmlChar *) ""); + + doc = ctx->ctxt->myDoc; + +#if (NGX_HTTP_XSLT_REUSE_DTD) + + dtd = conf->dtd; + +#else + + dtd = xmlCopyDtd(conf->dtd); + if (dtd == NULL) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "xmlCopyDtd() failed"); + return; + } + + dtd->name = xmlStrdup(name); + + if (doc->children == NULL) { + xmlAddChild((xmlNodePtr) doc, (xmlNodePtr) dtd); + + } else { + xmlAddPrevSibling(doc->children, (xmlNodePtr) dtd); + } + +#endif + + doc->extSubset = dtd; +} + + +static void +ngx_http_xslt_sax_entity_decl(void *data, const xmlChar *name, int type, + const xmlChar *publicId, const xmlChar *systemId, xmlChar *content) +{ + ngx_http_xslt_filter_ctx_t *ctx = data; + + ctx->sax->entityDecl(ctx->ctxt, name, type, publicId, systemId, content); +} + + +static void +ngx_http_xslt_sax_attribute_decl(void *data, const xmlChar *elem, + const xmlChar *fullname, int type, int def, const xmlChar *defaultValue, + xmlEnumerationPtr tree) +{ + ngx_http_xslt_filter_ctx_t *ctx = data; + + ctx->sax->attributeDecl(ctx->ctxt, elem, fullname, type, def, defaultValue, + tree); +} + + +static void +ngx_http_xslt_sax_element_decl(void *data, const xmlChar * name, int type, + xmlElementContentPtr content) +{ + ngx_http_xslt_filter_ctx_t *ctx = data; + + ctx->sax->elementDecl(ctx->ctxt, name, type, content); +} + + +static void +ngx_http_xslt_sax_notation_decl(void *data, const xmlChar *name, + const xmlChar *publicId, const xmlChar *systemId) +{ + ngx_http_xslt_filter_ctx_t *ctx = data; + + ctx->sax->notationDecl(ctx->ctxt, name, publicId, systemId); +} + + +static void +ngx_http_xslt_sax_unparsed_entity_decl(void *data, const xmlChar *name, + const xmlChar *publicId, const xmlChar *systemId, + const xmlChar *notationName) +{ + ngx_http_xslt_filter_ctx_t *ctx = data; + + ctx->sax->unparsedEntityDecl(ctx->ctxt, name, publicId, systemId, + notationName); +} + + +static void +ngx_http_xslt_sax_start_element(void *data, const xmlChar *localname, + const xmlChar *prefix, const xmlChar *URI, int nb_namespaces, + const xmlChar **namespaces, int nb_attributes, int nb_defaulted, + const xmlChar **attributes) +{ + ngx_http_xslt_filter_ctx_t *ctx = data; + + ctx->sax->startElementNs(ctx->ctxt, localname, prefix, URI, nb_namespaces, + namespaces, nb_attributes, nb_defaulted, attributes); +} + + +static void +ngx_http_xslt_sax_end_element(void *data, + const xmlChar * localname ATTRIBUTE_UNUSED, + const xmlChar * prefix ATTRIBUTE_UNUSED, + const xmlChar * URI ATTRIBUTE_UNUSED) +{ + ngx_http_xslt_filter_ctx_t *ctx = data; + + ctx->sax->endElementNs(ctx->ctxt, localname, prefix, URI); +} + + +static void +ngx_http_xslt_sax_characters(void *data, const xmlChar *p, int len) +{ + ngx_http_xslt_filter_ctx_t *ctx = data; + + ctx->sax->characters(ctx->ctxt, p, len); +} + + +static void +ngx_http_xslt_sax_cdata_block(void *data, const xmlChar *p, int len) +{ + ngx_http_xslt_filter_ctx_t *ctx = data; + + ctx->sax->cdataBlock(ctx->ctxt, p, len); +} + + +static xmlEntityPtr +ngx_http_xslt_sax_get_entity(void *data, const xmlChar *name) +{ + ngx_http_xslt_filter_ctx_t *ctx = data; + + return ctx->sax->getEntity(ctx->ctxt, name); +} + + +static xmlEntityPtr +ngx_http_xslt_sax_get_parameter_entity(void *data, const xmlChar *name) +{ + ngx_http_xslt_filter_ctx_t *ctx = data; + + return ctx->sax->getParameterEntity(ctx->ctxt, name); +} + + +static xmlParserInputPtr +ngx_http_xslt_sax_resolve_entity(void *data, const xmlChar *publicId, + const xmlChar *systemId) +{ + ngx_http_xslt_filter_ctx_t *ctx = data; + + return ctx->sax->resolveEntity(ctx->ctxt, publicId, systemId); +} + + +static void +ngx_http_xslt_sax_reference(void *data, const xmlChar *name) +{ + ngx_http_xslt_filter_ctx_t *ctx = data; + + ctx->sax->reference(ctx->ctxt, name); +} + + +static void +ngx_http_xslt_sax_comment(void *data, const xmlChar *value) +{ + ngx_http_xslt_filter_ctx_t *ctx = data; + + ctx->sax->comment(ctx->ctxt, value); +} + + +static void +ngx_http_xslt_sax_processing_instruction(void *data, const xmlChar *target, + const xmlChar *pidata) +{ + ngx_http_xslt_filter_ctx_t *ctx = data; + + ctx->sax->processingInstruction(ctx->ctxt, target, pidata); +} + + +static int +ngx_http_xslt_sax_is_standalone(void *data) +{ + ngx_http_xslt_filter_ctx_t *ctx = data; + + return ctx->sax->isStandalone(ctx->ctxt); +} + + +static int +ngx_http_xslt_sax_has_internal_subset(void *data) +{ + ngx_http_xslt_filter_ctx_t *ctx = data; + + return ctx->sax->hasInternalSubset(ctx->ctxt); +} + + +static int +ngx_http_xslt_sax_has_external_subset(void *data) +{ + ngx_http_xslt_filter_ctx_t *ctx = data; + + return ctx->sax->hasExternalSubset(ctx->ctxt); +} + + +static void ngx_cdecl +ngx_http_xslt_sax_error(void *data, const char *msg, ...) +{ + ngx_http_xslt_filter_ctx_t *ctx = data; + + size_t n; + va_list args; + u_char buf[NGX_MAX_ERROR_STR]; + + buf[0] = '\0'; + + va_start(args, msg); + n = (size_t) vsnprintf((char *) buf, NGX_MAX_ERROR_STR, msg, args); + va_end(args); + + while (--n && (buf[n] == CR || buf[n] == LF)) { /* void */ } + + ngx_log_error(NGX_LOG_ERR, ctx->request->connection->log, 0, + "libxml2 error: \"%*s\"", n, buf); +} + + +static ngx_buf_t * +ngx_http_xslt_apply_stylesheet(ngx_http_request_t *r, + ngx_http_xslt_filter_ctx_t *ctx) +{ + int len, rc; + ngx_buf_t *b; + ngx_uint_t i; + xmlChar *buf; + xmlDocPtr doc, res; + ngx_http_xslt_sheet_t *sheet; + ngx_http_xslt_filter_conf_t *conf; + + conf = ngx_http_get_module_loc_conf(r, ngx_http_xslt_filter_module); + sheet = conf->sheets.elts; + doc = ctx->doc; + + /* preallocate array for 4 params */ + + if (ngx_array_init(&ctx->params, r->pool, 4 * 2 + 1, sizeof(char *)) + != NGX_OK) + { + xmlFreeDoc(doc); + return NULL; + } + + for (i = 0; i < conf->sheets.nelts; i++) { + + if (ngx_http_xslt_params(r, ctx, &sheet[i].params) != NGX_OK) { + xmlFreeDoc(doc); + return NULL; + } + + res = xsltApplyStylesheet(sheet[i].stylesheet, doc, ctx->params.elts); + + xmlFreeDoc(doc); + + if (res == NULL) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "xsltApplyStylesheet() failed"); + return NULL; + } + + doc = res; + + /* reset array elements */ + ctx->params.nelts = 0; + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "xslt filter doc type: %d", doc->type); + + ctx->html = (doc->type == XML_HTML_DOCUMENT_NODE) ? 1 : 0; + + rc = xsltSaveResultToString(&buf, &len, doc, sheet[i - 1].stylesheet); + + xmlFreeDoc(doc); + + if (rc != 0) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "xsltSaveResultToString() failed"); + return NULL; + } + + if (len == 0) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "xsltSaveResultToString() returned zero-length result"); + return NULL; + } + + b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t)); + if (b == NULL) { + ngx_free(buf); + return NULL; + } + + b->pos = buf; + b->last = buf + len; + b->memory = 1; + b->last_buf = 1; + + return b; +} + + +static ngx_int_t +ngx_http_xslt_params(ngx_http_request_t *r, ngx_http_xslt_filter_ctx_t *ctx, + ngx_array_t *params) +{ + u_char *p, *last, *value, *dst, *src, **s; + size_t len; + ngx_uint_t i; + ngx_str_t string; + ngx_http_xslt_param_t *param; + + param = params->elts; + + for (i = 0; i < params->nelts; i++) { + + if (ngx_http_script_run(r, &string, param[i].lengths->elts, 1, + param[i].values->elts) + == NULL) + { + return NGX_ERROR; + } + + last = string.data + string.len - 1; + *last = '\0'; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "xslt filter param: \"%s\"", string.data); + + p = string.data; + + while (p && *p) { + + value = p; + p = (u_char *) ngx_strchr(p, '='); + if (p == NULL) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "invalid libxslt parameter \"%s\"", value); + return NGX_ERROR; + } + *p++ = '\0'; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "xslt filter param name: \"%s\"", value); + + s = ngx_array_push(&ctx->params); + if (s == NULL) { + return NGX_ERROR; + } + + *s = value; + + value = p; + p = (u_char *) ngx_strchr(p, ':'); + + if (p) { + len = p - value; + *p++ = '\0'; + + } else { + len = last - value; + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "xslt filter param value: \"%s\"", value); + + dst = value; + src = value; + + ngx_unescape_uri(&dst, &src, len, 0); + + *dst = '\0'; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "xslt filter param unescaped: \"%s\"", value); + + s = ngx_array_push(&ctx->params); + if (s == NULL) { + return NGX_ERROR; + } + + *s = value; + } + } + + s = ngx_array_push(&ctx->params); + if (s == NULL) { + return NGX_ERROR; + } + + *s = NULL; + + return NGX_OK; +} + + +static void +ngx_http_xslt_cleanup(void *data) +{ + ngx_free(data); +} + + +static char * +ngx_http_xslt_entities(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_http_xslt_filter_conf_t *xlcf = conf; + + ngx_str_t *value; + + if (xlcf->dtd) { + return "is duplicate"; + } + + value = cf->args->elts; + + xlcf->dtd = xmlParseDTD(NULL, (xmlChar *) value[1].data); + + if (xlcf->dtd == NULL) { + ngx_conf_log_error(NGX_LOG_ERR, cf, 0, "xmlParseDTD() failed"); + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; +} + + + +static char * +ngx_http_xslt_stylesheet(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_http_xslt_filter_conf_t *xlcf = conf; + + ngx_str_t *value; + ngx_uint_t i, n; + ngx_pool_cleanup_t *cln; + ngx_http_xslt_sheet_t *sheet; + ngx_http_xslt_param_t *param; + ngx_http_script_compile_t sc; + + value = cf->args->elts; + + if (xlcf->sheets.elts == NULL) { + if (ngx_array_init(&xlcf->sheets, cf->pool, 1, + sizeof(ngx_http_xslt_sheet_t)) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + } + + sheet = ngx_array_push(&xlcf->sheets); + if (sheet == NULL) { + return NGX_CONF_ERROR; + } + + ngx_memzero(sheet, sizeof(ngx_http_xslt_sheet_t)); + + if (ngx_conf_full_name(cf->cycle, &value[1], 0) != NGX_OK) { + return NGX_CONF_ERROR; + } + + cln = ngx_pool_cleanup_add(cf->pool, 0); + if (cln == NULL) { + return NGX_CONF_ERROR; + } + + sheet->stylesheet = xsltParseStylesheetFile(value[1].data); + if (sheet->stylesheet == NULL) { + ngx_conf_log_error(NGX_LOG_ERR, cf, 0, + "xsltParseStylesheetFile(\"%s\") failed", + value[1].data); + return NGX_CONF_ERROR; + } + + cln->handler = ngx_http_xslt_cleanup_stylesheet; + cln->data = sheet->stylesheet; + + n = cf->args->nelts; + + if (n == 2) { + return NGX_CONF_OK; + } + + if (ngx_array_init(&sheet->params, cf->pool, n - 2, + sizeof(ngx_http_xslt_param_t)) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + + for (i = 2; i < n; i++) { + + param = ngx_array_push(&sheet->params); + if (param == NULL) { + return NGX_CONF_ERROR; + } + + param->lengths = NULL; + param->values = NULL; + + ngx_memzero(&sc, sizeof(ngx_http_script_compile_t)); + + sc.cf = cf; + sc.source = &value[i]; + sc.lengths = ¶m->lengths; + sc.values = ¶m->values; + sc.variables = ngx_http_script_variables_count(&value[i]); + sc.complete_lengths = 1; + sc.complete_values = 1; + + if (ngx_http_script_compile(&sc) != NGX_OK) { + return NGX_CONF_ERROR; + } + } + + return NGX_CONF_OK; +} + + +static void +ngx_http_xslt_cleanup_stylesheet(void *data) +{ + xsltStylesheetPtr stylesheet = data; + + xsltFreeStylesheet(stylesheet); +} + + + +static void * +ngx_http_xslt_filter_create_conf(ngx_conf_t *cf) +{ + ngx_http_xslt_filter_conf_t *conf; + + conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_xslt_filter_conf_t)); + if (conf == NULL) { + return NGX_CONF_ERROR; + } + + /* + * set by ngx_pcalloc(): + * + * conf->dtd + * conf->sheets + */ + + return conf; +} + + +static char * +ngx_http_xslt_filter_merge_conf(ngx_conf_t *cf, void *parent, void *child) +{ + ngx_http_xslt_filter_conf_t *prev = parent; + ngx_http_xslt_filter_conf_t *conf = child; + + if (conf->dtd == NULL) { + conf->dtd = prev->dtd; + } + + if (conf->sheets.nelts == 0) { + conf->sheets = prev->sheets; + } + + if (ngx_http_merge_types(cf, conf->keys, &conf->types_hash, prev->keys, + &prev->types_hash, ngx_http_xslt_default_types) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; +} + + +static ngx_int_t +ngx_http_xslt_filter_init(ngx_conf_t *cf) +{ + xmlInitParser(); + + ngx_http_next_header_filter = ngx_http_top_header_filter; + ngx_http_top_header_filter = ngx_http_xslt_header_filter; + + ngx_http_next_body_filter = ngx_http_top_body_filter; + ngx_http_top_body_filter = ngx_http_xslt_body_filter; + + return NGX_OK; +} + + +static void +ngx_http_xslt_filter_exit(ngx_cycle_t *cycle) +{ + xsltCleanupGlobals(); + xmlCleanupParser(); +} diff --git a/src/http/modules/perl/nginx.pm b/src/http/modules/perl/nginx.pm --- a/src/http/modules/perl/nginx.pm +++ b/src/http/modules/perl/nginx.pm @@ -47,7 +47,7 @@ our @EXPORT = qw( HTTP_INSUFFICIENT_STORAGE ); -our $VERSION = '0.7.6'; +our $VERSION = '0.7.8'; require XSLoader; XSLoader::load('nginx', $VERSION); diff --git a/src/http/modules/perl/nginx.xs b/src/http/modules/perl/nginx.xs --- a/src/http/modules/perl/nginx.xs +++ b/src/http/modules/perl/nginx.xs @@ -247,11 +247,7 @@ header_in(r, key) XSRETURN_UNDEF; } - hash = 0; - for (i = 0; i < len; i++) { - lowcase_key[i] = ngx_tolower(p[i]); - hash = ngx_hash(hash, lowcase_key[i]); - } + hash = ngx_hash_strlow(lowcase_key, p, len); cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); @@ -652,6 +648,7 @@ sendfile(r, filename, offset = -1, bytes ngx_memzero(&of, sizeof(ngx_open_file_info_t)); + of.directio = clcf->directio; of.valid = clcf->open_file_cache_valid; of.min_uses = clcf->open_file_cache_min_uses; of.errors = clcf->open_file_cache_errors; @@ -832,11 +829,7 @@ variable(r, name, value = NULL) XSRETURN_UNDEF; } - hash = 0; - for (i = 0; i < len; i++) { - lowcase[i] = ngx_tolower(p[i]); - hash = ngx_hash(hash, lowcase[i]); - } + hash = ngx_hash_strlow(lowcase, p, len); var.len = len; var.data = lowcase; diff --git a/src/http/ngx_http.c b/src/http/ngx_http.c --- a/src/http/ngx_http.c +++ b/src/http/ngx_http.c @@ -1259,7 +1259,7 @@ static ngx_int_t ngx_http_add_names(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf, ngx_http_conf_in_addr_t *in_addr) { - ngx_uint_t i, n; + ngx_uint_t i; ngx_http_server_name_t *server_names, *name; if (in_addr->names.elts == NULL) { @@ -1275,10 +1275,8 @@ ngx_http_add_names(ngx_conf_t *cf, ngx_h for (i = 0; i < cscf->server_names.nelts; i++) { - for (n = 0; n < server_names[i].name.len; n++) { - server_names[i].name.data[n] = - ngx_tolower(server_names[i].name.data[n]); - } + ngx_strlow(server_names[i].name.data, server_names[i].name.data, + server_names[i].name.len); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, cf->log, 0, "name: %V", &server_names[i].name); @@ -1689,3 +1687,135 @@ ngx_http_init_listening(ngx_conf_t *cf, return NGX_OK; } + + +char * +ngx_http_types_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + char *p = conf; + + ngx_array_t **types; + ngx_str_t *value, *default_type; + ngx_uint_t i, n, hash; + ngx_hash_key_t *type; + + types = (ngx_array_t **) (p + cmd->offset); + + default_type = cmd->post; + + if (*types == NULL) { + *types = ngx_array_create(cf->temp_pool, 1, sizeof(ngx_hash_key_t)); + if (*types == NULL) { + return NGX_CONF_ERROR; + } + + if (default_type) { + type = ngx_array_push(*types); + if (type == NULL) { + return NGX_CONF_ERROR; + } + + type->key = *default_type; + type->key_hash = ngx_hash_key(default_type->data, + default_type->len); + type->value = (void *) 4; + } + } + + value = cf->args->elts; + + for (i = 1; i < cf->args->nelts; i++) { + + hash = ngx_hash_strlow(value[i].data, value[i].data, value[i].len); + value[i].data[value[i].len] = '\0'; + + type = (*types)->elts; + for (n = 0; n < (*types)->nelts; n++) { + + if (ngx_strcmp(value[i].data, type[n].key.data) == 0) { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "duplicate MIME type \"%V\"", &value[i]); + continue; + } + } + + type = ngx_array_push(*types); + if (type == NULL) { + return NGX_CONF_ERROR; + } + + type->key = value[i]; + type->key_hash = hash; + type->value = (void *) 4; + } + + return NGX_CONF_OK; +} + + +char * +ngx_http_merge_types(ngx_conf_t *cf, ngx_array_t *keys, ngx_hash_t *types_hash, + ngx_array_t *prev_keys, ngx_hash_t *prev_types_hash, + ngx_str_t *default_types) +{ + ngx_hash_init_t hash; + + if (keys == NULL) { + + if (prev_keys) { + *types_hash = *prev_types_hash; + return NGX_CONF_OK; + } + + if (ngx_http_set_default_types(cf, &keys, default_types) + != NGX_CONF_OK) + { + return NGX_CONF_ERROR; + } + } + + hash.hash = types_hash; + hash.key = NULL; + hash.max_size = 2048; + hash.bucket_size = 64; + hash.name = "test_types_hash"; + hash.pool = cf->pool; + hash.temp_pool = NULL; + + if (ngx_hash_init(&hash, keys->elts, keys->nelts) != NGX_OK) { + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; + +} + + +char * +ngx_http_set_default_types(ngx_conf_t *cf, ngx_array_t **types, + ngx_str_t *default_type) +{ + ngx_hash_key_t *type; + + *types = ngx_array_create(cf->temp_pool, 1, sizeof(ngx_hash_key_t)); + if (*types == NULL) { + return NGX_CONF_ERROR; + } + + while (default_type->len) { + + type = ngx_array_push(*types); + if (type == NULL) { + return NGX_CONF_ERROR; + } + + type->key = *default_type; + type->key_hash = ngx_hash_key(default_type->data, + default_type->len); + type->value = (void *) 4; + + default_type++; + } + + return NGX_CONF_OK; +} diff --git a/src/http/ngx_http.h b/src/http/ngx_http.h --- a/src/http/ngx_http.h +++ b/src/http/ngx_http.h @@ -107,6 +107,14 @@ ngx_int_t ngx_http_discard_request_body( void ngx_http_block_reading(ngx_http_request_t *r); +char *ngx_http_types_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +char *ngx_http_merge_types(ngx_conf_t *cf, ngx_array_t *keys, + ngx_hash_t *types_hash, ngx_array_t *prev_keys, ngx_hash_t *prev_types_hash, + ngx_str_t *default_types); +char *ngx_http_set_default_types(ngx_conf_t *cf, ngx_array_t **types, + ngx_str_t *default_type); + + extern ngx_module_t ngx_http_module; diff --git a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c --- a/src/http/ngx_http_core_module.c +++ b/src/http/ngx_http_core_module.c @@ -54,6 +54,8 @@ static char *ngx_http_core_server_name(n static char *ngx_http_core_root(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_http_core_limit_except(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static char *ngx_http_core_directio(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); static char *ngx_http_core_error_page(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_http_core_open_file_cache(ngx_conf_t *cf, ngx_command_t *cmd, @@ -353,6 +355,13 @@ static ngx_command_t ngx_http_core_comm offsetof(ngx_http_core_loc_conf_t, sendfile_max_chunk), NULL }, + { ngx_string("directio"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_http_core_directio, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + NULL }, + { ngx_string("tcp_nopush"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, ngx_conf_set_flag_slot, @@ -1204,7 +1213,7 @@ ngx_http_core_find_static_location(ngx_h n = (len <= (size_t) node->len) ? len : node->len; - rc = ngx_memcmp(uri, node->name, n); + rc = ngx_filename_cmp(uri, node->name, n); if (rc != 0) { node = (rc < 0) ? node->left : node->right; @@ -1293,10 +1302,48 @@ ngx_http_core_send_continue(ngx_http_req } +void * +ngx_http_test_content_type(ngx_http_request_t *r, ngx_hash_t *types_hash) +{ + u_char c, *p; + ngx_uint_t i, hash; + + if (r->headers_out.content_type.len == 0) { + return NULL; + } + + if (r->headers_out.content_type_lowcase == NULL) { + + p = ngx_pnalloc(r->pool, r->headers_out.content_type_len); + + if (p == NULL) { + return NULL; + } + + r->headers_out.content_type_lowcase = p; + + hash = 0; + + for (i = 0; i < r->headers_out.content_type_len; i++) { + c = ngx_tolower(r->headers_out.content_type.data[i]); + hash = ngx_hash(hash, c); + *p++ = c; + } + + r->headers_out.content_type_hash = hash; + } + + return ngx_hash_find(types_hash, + r->headers_out.content_type_hash, + r->headers_out.content_type_lowcase, + r->headers_out.content_type_len); +} + + ngx_int_t ngx_http_set_content_type(ngx_http_request_t *r) { - u_char c, *p, *exten; + u_char c, *exten; ngx_str_t *type; ngx_uint_t i, hash; ngx_http_core_loc_conf_t *clcf; @@ -1316,19 +1363,12 @@ ngx_http_set_content_type(ngx_http_reque if (c >= 'A' && c <= 'Z') { - p = ngx_pnalloc(r->pool, r->exten.len); - if (p == NULL) { + exten = ngx_pnalloc(r->pool, r->exten.len); + if (exten == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } - hash = 0; - exten = p; - - for (i = 0; i < r->exten.len; i++) { - c = ngx_tolower(r->exten.data[i]); - hash = ngx_hash(hash, c); - *p++ = c; - } + hash = ngx_hash_strlow(exten, r->exten.data, r->exten.len); r->exten.data = exten; @@ -2307,7 +2347,7 @@ ngx_http_core_type(ngx_conf_t *cf, ngx_c ngx_http_core_loc_conf_t *lcf = conf; ngx_str_t *value, *content_type, *old, file; - ngx_uint_t i, n; + ngx_uint_t i, n, hash; ngx_hash_key_t *type; value = cf->args->elts; @@ -2333,9 +2373,7 @@ ngx_http_core_type(ngx_conf_t *cf, ngx_c for (i = 1; i < cf->args->nelts; i++) { - for (n = 0; n < value[i].len; n++) { - value[i].data[n] = ngx_tolower(value[i].data[n]); - } + hash = ngx_hash_strlow(value[i].data, value[i].data, value[i].len); type = lcf->types->elts; for (n = 0; n < lcf->types->nelts; n++) { @@ -2359,7 +2397,7 @@ ngx_http_core_type(ngx_conf_t *cf, ngx_c } type->key = value[i]; - type->key_hash = ngx_hash_key(value[i].data, value[i].len); + type->key_hash = hash; type->value = content_type; } @@ -2586,6 +2624,7 @@ ngx_http_core_create_loc_conf(ngx_conf_t lcf->client_body_in_file_only = NGX_CONF_UNSET; lcf->sendfile = NGX_CONF_UNSET; lcf->sendfile_max_chunk = NGX_CONF_UNSET_SIZE; + lcf->directio = NGX_CONF_UNSET; lcf->tcp_nopush = NGX_CONF_UNSET; lcf->tcp_nodelay = NGX_CONF_UNSET; lcf->send_timeout = NGX_CONF_UNSET_MSEC; @@ -2774,6 +2813,8 @@ ngx_http_core_merge_loc_conf(ngx_conf_t ngx_conf_merge_value(conf->sendfile, prev->sendfile, 0); ngx_conf_merge_size_value(conf->sendfile_max_chunk, prev->sendfile_max_chunk, 0); + ngx_conf_merge_off_value(conf->directio, prev->directio, + NGX_MAX_OFF_T_VALUE); ngx_conf_merge_value(conf->tcp_nopush, prev->tcp_nopush, 0); ngx_conf_merge_value(conf->tcp_nodelay, prev->tcp_nodelay, 1); @@ -3340,6 +3381,33 @@ ngx_http_core_limit_except(ngx_conf_t *c static char * +ngx_http_core_directio(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_http_core_loc_conf_t *clcf = conf; + + ngx_str_t *value; + + if (clcf->directio != NGX_CONF_UNSET) { + return "is duplicate"; + } + + value = cf->args->elts; + + if (ngx_strcmp(value[1].data, "off") == 0) { + clcf->directio = NGX_MAX_OFF_T_VALUE; + return NGX_CONF_OK; + } + + clcf->directio = ngx_parse_offset(&value[1]); + if (clcf->directio == (off_t) NGX_ERROR) { + return "invalid value"; + } + + return NGX_CONF_OK; +} + + +static char * ngx_http_core_error_page(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_http_core_loc_conf_t *lcf = conf; diff --git a/src/http/ngx_http_core_module.h b/src/http/ngx_http_core_module.h --- a/src/http/ngx_http_core_module.h +++ b/src/http/ngx_http_core_module.h @@ -265,6 +265,7 @@ struct ngx_http_core_loc_conf_s { ngx_str_t default_type; off_t client_max_body_size; /* client_max_body_size */ + off_t directio; /* directio */ size_t client_body_buffer_size; /* client_body_buffer_size */ size_t send_lowat; /* send_lowat */ @@ -372,6 +373,8 @@ ngx_int_t ngx_http_core_post_access_phas ngx_int_t ngx_http_core_content_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph); + +void *ngx_http_test_content_type(ngx_http_request_t *r, ngx_hash_t *types_hash); ngx_int_t ngx_http_set_content_type(ngx_http_request_t *r); ngx_int_t ngx_http_set_exten(ngx_http_request_t *r); u_char *ngx_http_map_uri_to_path(ngx_http_request_t *r, ngx_str_t *name, diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -647,6 +647,7 @@ ngx_http_process_request_line(ngx_event_ r->request_line.len = r->request_end - r->request_start; r->request_line.data = r->request_start; + *r->request_end = '\0'; if (r->args_start) { @@ -818,7 +819,6 @@ ngx_http_process_request_headers(ngx_eve ssize_t n; ngx_int_t rc, rv; ngx_str_t header; - ngx_uint_t i; ngx_table_elt_t *h; ngx_connection_t *c; ngx_http_header_t *hh; @@ -928,9 +928,7 @@ ngx_http_process_request_headers(ngx_eve ngx_memcpy(h->lowcase_key, r->lowcase_header, h->key.len); } else { - for (i = 0; i < h->key.len; i++) { - h->lowcase_key[i] = ngx_tolower(h->key.data[i]); - } + ngx_strlow(h->lowcase_key, h->key.data, h->key.len); } hh = ngx_hash_find(&cmcf->headers_in_hash, h->hash, @@ -1446,7 +1444,7 @@ ngx_http_process_request(ngx_http_reques sscf = ngx_http_get_module_srv_conf(r, ngx_http_ssl_module); - if (sscf->verify) { + if (sscf->verify == 1) { rc = SSL_get_verify_result(c->ssl->connection); if (rc != X509_V_OK) { @@ -1552,8 +1550,8 @@ ngx_http_validate_host(u_char *host, siz static ngx_int_t ngx_http_find_virtual_server(ngx_http_request_t *r, u_char *host, size_t len) { - u_char *server, ch; - ngx_uint_t i, hash; + u_char *server; + ngx_uint_t hash; ngx_http_core_loc_conf_t *clcf; ngx_http_core_srv_conf_t *cscf; u_char buf[32]; @@ -1572,16 +1570,7 @@ ngx_http_find_virtual_server(ngx_http_re } } - hash = 0; - - for (i = 0; i < len; i++) { - ch = host[i]; - - ch = ngx_tolower(ch); - server[i] = ch; - - hash = ngx_hash(hash, ch); - } + hash = ngx_hash_strlow(server, host, len); cscf = ngx_hash_find_combined(&r->virtual_names->names, hash, server, len); diff --git a/src/http/ngx_http_request.h b/src/http/ngx_http_request.h --- a/src/http/ngx_http_request.h +++ b/src/http/ngx_http_request.h @@ -244,6 +244,8 @@ typedef struct { size_t content_type_len; ngx_str_t content_type; ngx_str_t charset; + u_char *content_type_lowcase; + ngx_uint_t content_type_hash; ngx_array_t cache_control; diff --git a/src/http/ngx_http_script.c b/src/http/ngx_http_script.c --- a/src/http/ngx_http_script.c +++ b/src/http/ngx_http_script.c @@ -996,6 +996,7 @@ ngx_http_script_file_code(ngx_http_scrip ngx_memzero(&of, sizeof(ngx_open_file_info_t)); + of.directio = clcf->directio; of.valid = clcf->open_file_cache_valid; of.min_uses = clcf->open_file_cache_min_uses; of.errors = clcf->open_file_cache_errors; @@ -1015,69 +1016,69 @@ ngx_http_script_file_code(ngx_http_scrip case ngx_http_script_file_dir: case ngx_http_script_file_exists: case ngx_http_script_file_exec: - goto false; + goto false_value; case ngx_http_script_file_not_plain: case ngx_http_script_file_not_dir: case ngx_http_script_file_not_exists: case ngx_http_script_file_not_exec: - goto true; + goto true_value; } - goto false; + goto false_value; } switch (code->op) { case ngx_http_script_file_plain: if (of.is_file) { - goto true; + goto true_value; } - goto false; + goto false_value; case ngx_http_script_file_not_plain: if (of.is_file) { - goto false; + goto false_value; } - goto true; + goto true_value; case ngx_http_script_file_dir: if (of.is_dir) { - goto true; + goto true_value; } - goto false; + goto false_value; case ngx_http_script_file_not_dir: if (of.is_dir) { - goto false; + goto false_value; } - goto true; + goto true_value; case ngx_http_script_file_exists: if (of.is_file || of.is_dir || of.is_link) { - goto true; + goto true_value; } - goto false; + goto false_value; case ngx_http_script_file_not_exists: if (of.is_file || of.is_dir || of.is_link) { - goto false; + goto false_value; } - goto true; + goto true_value; case ngx_http_script_file_exec: if (of.is_exec) { - goto true; + goto true_value; } - goto false; + goto false_value; case ngx_http_script_file_not_exec: if (of.is_exec) { - goto false; + goto false_value; } - goto true; + goto true_value; } -false: +false_value: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http script file op false"); @@ -1085,7 +1086,7 @@ false: *value = ngx_http_variable_null_value; return; -true: +true_value: *value = ngx_http_variable_true_value; return; diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c --- a/src/http/ngx_http_upstream.c +++ b/src/http/ngx_http_upstream.c @@ -137,7 +137,8 @@ ngx_http_upstream_header_t ngx_http_ups ngx_http_upstream_copy_header_line, 0, 0 }, { ngx_string("Location"), - ngx_http_upstream_ignore_header_line, 0, + ngx_http_upstream_process_header_line, + offsetof(ngx_http_upstream_headers_in_t, location), ngx_http_upstream_rewrite_location, 0, 0 }, { ngx_string("Refresh"), diff --git a/src/http/ngx_http_variables.c b/src/http/ngx_http_variables.c --- a/src/http/ngx_http_variables.c +++ b/src/http/ngx_http_variables.c @@ -26,6 +26,8 @@ static ngx_int_t ngx_http_variable_unkno ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_variable_unknown_header_out(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_argument(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_variable_host(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); @@ -277,9 +279,7 @@ ngx_http_add_variable(ngx_conf_t *cf, ng return NULL; } - for (i = 0; i < name->len; i++) { - v->name.data[i] = ngx_tolower(name->data[i]); - } + ngx_strlow(v->name.data, name->data, name->len); v->set_handler = NULL; v->get_handler = NULL; @@ -344,9 +344,7 @@ ngx_http_get_variable_index(ngx_conf_t * return NGX_ERROR; } - for (i = 0; i < name->len; i++) { - v->name.data[i] = ngx_tolower(name->data[i]); - } + ngx_strlow(v->name.data, name->data, name->len); v->set_handler = NULL; v->get_handler = NULL; @@ -481,6 +479,15 @@ ngx_http_get_variable(ngx_http_request_t return NULL; } + if (ngx_strncmp(name->data, "arg_", 4) == 0) { + + if (ngx_http_variable_argument(r, vv, (uintptr_t) name) == NGX_OK) { + return vv; + } + + return NULL; + } + vv->not_found = 1; if (nowarn == 0) { @@ -712,6 +719,62 @@ ngx_http_variable_unknown_header(ngx_htt static ngx_int_t +ngx_http_variable_argument(ngx_http_request_t *r, ngx_http_variable_value_t *v, + uintptr_t data) +{ + ngx_str_t *name = (ngx_str_t *) data; + + u_char *p, *arg; + size_t len; + + if (r->args.len == 0) { + v->not_found = 1; + return NGX_OK; + } + + len = name->len - 1 - (sizeof("arg_") - 1); + arg = name->data + sizeof("arg_") - 1; + + for (p = r->args.data; *p && *p != ' '; p++) { + + /* + * although r->args.data is not null-terminated by itself, + * however, there is null in the end of request line + */ + + p = ngx_strcasestrn(p, (char *) arg, len); + + if (p == NULL) { + v->not_found = 1; + return NGX_OK; + } + + if ((p == r->args.data || *(p - 1) == '&') && *(p + len + 1) == '=') { + + v->data = p + len + 2; + + p = (u_char *) ngx_strchr(p, '&'); + + if (p == NULL) { + p = r->args.data + r->args.len; + } + + v->len = p - v->data; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + + return NGX_OK; + } + } + + v->not_found = 1; + + return NGX_OK; +} + + +static ngx_int_t ngx_http_variable_host(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) { @@ -1396,6 +1459,13 @@ ngx_http_variables_init_vars(ngx_conf_t continue; } + if (ngx_strncmp(v[i].name.data, "arg_", 4) == 0) { + v[i].get_handler = ngx_http_variable_argument; + v[i].data = (uintptr_t) &v[i].name; + + continue; + } + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, "unknown \"%V\" variable", &v[i].name); diff --git a/src/mail/ngx_mail_ssl_module.c b/src/mail/ngx_mail_ssl_module.c --- a/src/mail/ngx_mail_ssl_module.c +++ b/src/mail/ngx_mail_ssl_module.c @@ -9,9 +9,9 @@ #include -#define NGX_DEFLAUT_CERTIFICATE "cert.pem" -#define NGX_DEFLAUT_CERTIFICATE_KEY "cert.pem" -#define NGX_DEFLAUT_CIPHERS "ALL:!ADH:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP" +#define NGX_DEFAULT_CERTIFICATE "cert.pem" +#define NGX_DEFAULT_CERTIFICATE_KEY "cert.pem" +#define NGX_DEFAULT_CIPHERS "ALL:!ADH:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP" static void *ngx_mail_ssl_create_conf(ngx_conf_t *cf); @@ -214,14 +214,14 @@ ngx_mail_ssl_merge_conf(ngx_conf_t *cf, |NGX_SSL_SSLv2|NGX_SSL_SSLv3|NGX_SSL_TLSv1)); ngx_conf_merge_str_value(conf->certificate, prev->certificate, - NGX_DEFLAUT_CERTIFICATE); + NGX_DEFAULT_CERTIFICATE); ngx_conf_merge_str_value(conf->certificate_key, prev->certificate_key, - NGX_DEFLAUT_CERTIFICATE_KEY); + NGX_DEFAULT_CERTIFICATE_KEY); ngx_conf_merge_str_value(conf->dhparam, prev->dhparam, ""); - ngx_conf_merge_str_value(conf->ciphers, prev->ciphers, NGX_DEFLAUT_CIPHERS); + ngx_conf_merge_str_value(conf->ciphers, prev->ciphers, NGX_DEFAULT_CIPHERS); conf->ssl.log = cf->log; diff --git a/src/os/unix/ngx_alloc.h b/src/os/unix/ngx_alloc.h --- a/src/os/unix/ngx_alloc.h +++ b/src/os/unix/ngx_alloc.h @@ -21,8 +21,8 @@ void *ngx_calloc(size_t size, ngx_log_t /* * Linux has memalign() or posix_memalign() * Solaris has memalign() - * FreeBSD has not memalign() or posix_memalign() but its malloc() alignes - * allocations bigger than page size at the page boundary. + * FreeBSD 7.0 has posix_memalign(), besides, early version's malloc() + * aligns allocations bigger than page size at the page boundary */ #if (NGX_HAVE_POSIX_MEMALIGN || NGX_HAVE_MEMALIGN) diff --git a/src/os/unix/ngx_atomic.h b/src/os/unix/ngx_atomic.h --- a/src/os/unix/ngx_atomic.h +++ b/src/os/unix/ngx_atomic.h @@ -21,13 +21,8 @@ #include -/* "bool" conflicts with perl's CORE/handy.h - * "true" and "false" conflict with nginx, and of course we can rename them, - * but we need to undef "bool" anyway - */ +/* "bool" conflicts with perl's CORE/handy.h */ #undef bool -#undef true -#undef false #define NGX_HAVE_ATOMIC_OPS 1 diff --git a/src/os/unix/ngx_darwin.h b/src/os/unix/ngx_darwin.h new file mode 100644 --- /dev/null +++ b/src/os/unix/ngx_darwin.h @@ -0,0 +1,19 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#ifndef _NGX_DARWIN_H_INCLUDED_ +#define _NGX_DARWIN_H_INCLUDED_ + + +ngx_chain_t *ngx_darwin_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, + off_t limit); + +extern int ngx_darwin_kern_osreldate; +extern int ngx_darwin_hw_ncpu; +extern u_long ngx_darwin_net_inet_tcp_sendspace; + + +#endif /* _NGX_DARWIN_H_INCLUDED_ */ diff --git a/src/os/unix/ngx_darwin_config.h b/src/os/unix/ngx_darwin_config.h new file mode 100644 --- /dev/null +++ b/src/os/unix/ngx_darwin_config.h @@ -0,0 +1,87 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#ifndef _NGX_DARWIN_CONFIG_H_INCLUDED_ +#define _NGX_DARWIN_CONFIG_H_INCLUDED_ + + + +#include +#include +#include +#include +#include +#include /* offsetof() */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include /* FIONBIO */ +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include /* TCP_NODELAY */ +#include +#include +#include + +#include +#include + + +#ifndef IOV_MAX +#define IOV_MAX 64 +#endif + + +#include + + +#if (NGX_HAVE_POLL) +#include +#endif + + +#if (NGX_HAVE_KQUEUE) +#include +#endif + + +#define NGX_LISTEN_BACKLOG -1 + + +#ifndef NGX_HAVE_INHERITED_NONBLOCK +#define NGX_HAVE_INHERITED_NONBLOCK 1 +#endif + + +#ifndef NGX_HAVE_CASELESS_FILESYSTEM +#define NGX_HAVE_CASELESS_FILESYSTEM 1 +#endif + + +#define NGX_HAVE_OS_SPECIFIC_INIT 1 + + +extern char **environ; + + +#endif /* _NGX_DARWIN_CONFIG_H_INCLUDED_ */ diff --git a/src/os/unix/ngx_darwin_init.c b/src/os/unix/ngx_darwin_init.c new file mode 100644 --- /dev/null +++ b/src/os/unix/ngx_darwin_init.c @@ -0,0 +1,155 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include + + +char ngx_darwin_kern_ostype[16]; +char ngx_darwin_kern_osrelease[128]; +int ngx_darwin_hw_ncpu; +int ngx_darwin_kern_ipc_somaxconn; +u_long ngx_darwin_net_inet_tcp_sendspace; + + +static ngx_os_io_t ngx_darwin_io = { + ngx_unix_recv, + ngx_readv_chain, + ngx_udp_unix_recv, + ngx_unix_send, +#if (NGX_HAVE_SENDFILE) + ngx_darwin_sendfile_chain, + NGX_IO_SENDFILE +#else + ngx_writev_chain, + 0 +#endif +}; + + +typedef struct { + char *name; + void *value; + size_t size; + ngx_uint_t exists; +} sysctl_t; + + +sysctl_t sysctls[] = { + { "hw.ncpu", + &ngx_darwin_hw_ncpu, + sizeof(ngx_darwin_hw_ncpu), 0 }, + + { "net.inet.tcp.sendspace", + &ngx_darwin_net_inet_tcp_sendspace, + sizeof(ngx_darwin_net_inet_tcp_sendspace), 0 }, + + { "kern.ipc.somaxconn", + &ngx_darwin_kern_ipc_somaxconn, + sizeof(ngx_darwin_kern_ipc_somaxconn), 0 }, + + { NULL, NULL, 0, 0 } +}; + + +ngx_int_t +ngx_os_specific_init(ngx_log_t *log) +{ + int somaxconn; + size_t size; + ngx_err_t err; + ngx_uint_t i; + + size = sizeof(ngx_darwin_kern_ostype); + if (sysctlbyname("kern.ostype", + ngx_darwin_kern_ostype, &size, NULL, 0) == -1) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + "sysctlbyname(kern.ostype) failed"); + + if (ngx_errno != NGX_ENOMEM) { + return NGX_ERROR; + } + + ngx_darwin_kern_ostype[size - 1] = '\0'; + } + + size = sizeof(ngx_darwin_kern_osrelease); + if (sysctlbyname("kern.osrelease", + ngx_darwin_kern_osrelease, &size, NULL, 0) == -1) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + "sysctlbyname(kern.osrelease) failed"); + + if (ngx_errno != NGX_ENOMEM) { + return NGX_ERROR; + } + + ngx_darwin_kern_osrelease[size - 1] = '\0'; + } + + + for (i = 0; sysctls[i].name; i++) { + size = sysctls[i].size; + + if (sysctlbyname(sysctls[i].name, sysctls[i].value, &size, NULL, 0) + == 0) + { + sysctls[i].exists = 1; + continue; + } + + err = ngx_errno; + + if (err == NGX_ENOENT) { + continue; + } + + ngx_log_error(NGX_LOG_ALERT, log, err, + "sysctlbyname(%s) failed", sysctls[i].name); + return NGX_ERROR; + } + + ngx_ncpu = ngx_darwin_hw_ncpu; + + somaxconn = 32676; + + if (ngx_darwin_kern_ipc_somaxconn > somaxconn) { + ngx_log_error(NGX_LOG_ALERT, log, 0, + "sysctl kern.ipc.somaxconn must be no more than %d", + somaxconn); + return NGX_ERROR; + } + + ngx_tcp_nodelay_and_tcp_nopush = 1; + + ngx_os_io = ngx_darwin_io; + + return NGX_OK; +} + + +void +ngx_os_specific_status(ngx_log_t *log) +{ + u_long value; + ngx_uint_t i; + + ngx_log_error(NGX_LOG_NOTICE, log, 0, "OS: %s %s", + ngx_darwin_kern_ostype, ngx_darwin_kern_osrelease); + + for (i = 0; sysctls[i].name; i++) { + if (sysctls[i].exists) { + if (sysctls[i].size == sizeof(long)) { + value = *(long *) sysctls[i].value; + + } else { + value = *(int *) sysctls[i].value; + } + + ngx_log_error(NGX_LOG_NOTICE, log, 0, "%s: %l", + sysctls[i].name, value); + } + } +} diff --git a/src/os/unix/ngx_darwin_sendfile_chain.c b/src/os/unix/ngx_darwin_sendfile_chain.c new file mode 100644 --- /dev/null +++ b/src/os/unix/ngx_darwin_sendfile_chain.c @@ -0,0 +1,363 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include + + +/* + * It seems that Darwin 9.4 (Mac OS X 1.5) sendfile() has the same + * old bug as early FreeBSD sendfile() syscall: + * http://www.freebsd.org/cgi/query-pr.cgi?pr=33771 + * + * Besides sendfile() has another bug: if one calls sendfile() + * with both a header and a trailer, then sendfile() ignores a file part + * at all and sends only the header and the trailer together. + * For this reason we send a trailer only if there is no a header. + * + * Although sendfile() allows to pass a header or a trailer, + * it may send the header or the trailer and a part of the file + * in different packets. And FreeBSD workaround (TCP_NOPUSH option) + * does not help. + */ + + +#if (IOV_MAX > 64) +#define NGX_HEADERS 64 +#define NGX_TRAILERS 64 +#else +#define NGX_HEADERS IOV_MAX +#define NGX_TRAILERS IOV_MAX +#endif + + +ngx_chain_t * +ngx_darwin_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit) +{ + int rc; + u_char *prev; + off_t size, send, prev_send, aligned, sent, fprev; + off_t header_size, file_size; + ngx_uint_t eintr, 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 (NGX_HAVE_KQUEUE) + + if ((ngx_event_flags & NGX_USE_KQUEUE_EVENT) && wev->pending_eof) { + (void) ngx_connection_error(c, wev->kq_errno, + "kevent() reported about an closed connection"); + wev->error = 1; + return NGX_CHAIN_ERROR; + } + +#endif + + /* the maximum limit size is the maximum size_t value - the page size */ + + if (limit == 0 || limit > (off_t) (NGX_MAX_SIZE_T_VALUE - ngx_pagesize)) { + limit = NGX_MAX_SIZE_T_VALUE - ngx_pagesize; + } + + send = 0; + 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; + file_size = 0; + header_size = 0; + eintr = 0; + complete = 0; + prev_send = send; + + header.nelts = 0; + trailer.nelts = 0; + + /* create the header iovec and coalesce the neighbouring bufs */ + + prev = NULL; + iov = NULL; + + for (cl = in; + cl && header.nelts < IOV_MAX && send < limit; + cl = cl->next) + { + if (ngx_buf_special(cl->buf)) { + continue; + } + + if (!ngx_buf_in_memory_only(cl->buf)) { + break; + } + + size = cl->buf->last - cl->buf->pos; + + if (send + size > limit) { + size = limit - send; + } + + if (prev == cl->buf->pos) { + iov->iov_len += (size_t) size; + + } else { + iov = ngx_array_push(&header); + if (iov == NULL) { + return NGX_CHAIN_ERROR; + } + + iov->iov_base = (void *) cl->buf->pos; + iov->iov_len = (size_t) size; + } + + prev = cl->buf->pos + (size_t) size; + header_size += size; + send += size; + } + + + if (cl && cl->buf->in_file && send < limit) { + file = cl->buf; + + /* coalesce the neighbouring file bufs */ + + do { + size = cl->buf->file_last - cl->buf->file_pos; + + if (send + size > limit) { + size = limit - send; + + aligned = (cl->buf->file_pos + size + ngx_pagesize - 1) + & ~((off_t) ngx_pagesize - 1); + + if (aligned <= cl->buf->file_last) { + size = aligned - cl->buf->file_pos; + } + } + + file_size += size; + send += size; + fprev = cl->buf->file_pos + size; + cl = cl->next; + + } while (cl + && cl->buf->in_file + && send < limit + && file->file->fd == cl->buf->file->fd + && fprev == cl->buf->file_pos); + } + + if (file && header.nelts == 0) { + + /* create the tailer iovec and coalesce the neighbouring bufs */ + + prev = NULL; + iov = NULL; + + while (cl && header.nelts < IOV_MAX && send < limit) { + + if (ngx_buf_special(cl->buf)) { + cl = cl->next; + continue; + } + + if (!ngx_buf_in_memory_only(cl->buf)) { + break; + } + + size = cl->buf->last - cl->buf->pos; + + if (send + size > limit) { + size = limit - send; + } + + if (prev == cl->buf->pos) { + iov->iov_len += (size_t) size; + + } else { + iov = ngx_array_push(&trailer); + if (iov == NULL) { + return NGX_CHAIN_ERROR; + } + + iov->iov_base = (void *) cl->buf->pos; + iov->iov_len = (size_t) size; + } + + prev = cl->buf->pos + (size_t) size; + send += size; + cl = cl->next; + } + } + + if (file) { + + /* + * sendfile() returns EINVAL if sf_hdtr's count is 0, + * but corresponding pointer is not NULL + */ + + hdtr.headers = header.nelts ? (struct iovec *) header.elts: NULL; + hdtr.hdr_cnt = header.nelts; + hdtr.trailers = trailer.nelts ? (struct iovec *) trailer.elts: NULL; + hdtr.trl_cnt = trailer.nelts; + + sent = header_size + file_size; + + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, + "sendfile: @%O %O h:%O", + file->file_pos, sent, header_size); + + rc = sendfile(file->file->fd, c->fd, file->file_pos, + &sent, &hdtr, 0); + + if (rc == -1) { + err = ngx_errno; + + 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 %O bytes", sent); + + } else { + wev->error = 1; + (void) ngx_connection_error(c, err, "sendfile() failed"); + return NGX_CHAIN_ERROR; + } + } + + if (rc == 0 && sent == 0) { + + /* + * if rc and sent equal to zero, then someone + * has truncated the file, so the offset became beyond + * the end of the file + */ + + ngx_log_error(NGX_LOG_ALERT, c->log, 0, + "sendfile() reported that \"%s\" was truncated", + file->file->name.data); + + return NGX_CHAIN_ERROR; + } + + ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0, + "sendfile: %d, @%O %O:%O", + rc, file->file_pos, sent, file_size + header_size); + + } else { + rc = writev(c->fd, header.elts, header.nelts); + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, + "writev: %d of %uz", rc, send); + + if (rc == -1) { + err = ngx_errno; + + 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 - prev_send == sent) { + complete = 1; + } + + c->sent += sent; + + for (cl = in; cl; cl = cl->next) { + + if (ngx_buf_special(cl->buf)) { + continue; + } + + if (sent == 0) { + break; + } + + size = ngx_buf_size(cl->buf); + + if (sent >= size) { + sent -= size; + + if (ngx_buf_in_memory(cl->buf)) { + cl->buf->pos = cl->buf->last; + } + + if (cl->buf->in_file) { + cl->buf->file_pos = cl->buf->file_last; + } + + continue; + } + + if (ngx_buf_in_memory(cl->buf)) { + cl->buf->pos += (size_t) sent; + } + + if (cl->buf->in_file) { + cl->buf->file_pos += sent; + } + + break; + } + + if (eintr) { + continue; + } + + if (!complete) { + wev->ready = 0; + return cl; + } + + if (send >= limit || cl == NULL) { + return cl; + } + + in = cl; + } +} diff --git a/src/os/unix/ngx_files.c b/src/os/unix/ngx_files.c --- a/src/os/unix/ngx_files.c +++ b/src/os/unix/ngx_files.c @@ -351,3 +351,22 @@ ngx_unlock_fd(ngx_fd_t fd) return 0; } + + +#if (NGX_HAVE_O_DIRECT) + +ngx_int_t +ngx_directio(ngx_fd_t fd) +{ + int flags; + + flags = fcntl(fd, F_GETFL); + + if (flags == -1) { + return -1; + } + + return fcntl(fd, F_SETFL, flags | O_DIRECT); +} + +#endif diff --git a/src/os/unix/ngx_files.h b/src/os/unix/ngx_files.h --- a/src/os/unix/ngx_files.h +++ b/src/os/unix/ngx_files.h @@ -19,6 +19,8 @@ #ifdef __CYGWIN__ +#define NGX_HAVE_CASELESS_FILESYSTEM 1 + #define ngx_open_file(name, mode, create, access) \ open((const char *) name, mode|create|O_BINARY, access) @@ -103,6 +105,16 @@ ngx_int_t ngx_set_file_time(u_char *name #define ngx_file_uniq(sb) (sb)->st_ino +#if (NGX_HAVE_CASELESS_FILESYSTEM) + +#define ngx_filename_cmp(s1, s2, n) strncasecmp((char *) s1, (char *) s2, n) + +#else + +#define ngx_filename_cmp ngx_memcmp + +#endif + #define ngx_getcwd(buf, size) (getcwd(buf, size) != NULL) #define ngx_getcwd_n "getcwd()" @@ -177,4 +189,27 @@ ngx_err_t ngx_unlock_fd(ngx_fd_t fd); #define ngx_unlock_fd_n "fcntl(F_SETLK, F_UNLCK)" +#if (NGX_HAVE_O_DIRECT) + +ngx_int_t ngx_directio(ngx_fd_t fd); +#define ngx_directio_n "fcntl(O_DIRECT)" + +#elif (NGX_HAVE_F_NOCACHE) + +#define ngx_directio(fd) fcntl(fd, F_NOCACHE, 1) +#define ngx_directio_n "fcntl(F_NOCACHE)" + +#elif (NGX_HAVE_DIRECTIO) + +#define ngx_directio(fd) directio(fd, DIRECTIO_ON) +#define ngx_directio_n "directio(DIRECTIO_ON)" + +#else + +#define ngx_directio(fd) 0 +#define ngx_directio_n "ngx_directio_n" + +#endif + + #endif /* _NGX_FILES_H_INCLUDED_ */ diff --git a/src/os/unix/ngx_os.h b/src/os/unix/ngx_os.h --- a/src/os/unix/ngx_os.h +++ b/src/os/unix/ngx_os.h @@ -66,6 +66,10 @@ extern ngx_uint_t ngx_tcp_nodelay_and_ #elif (NGX_SOLARIS) #include + + +#elif (NGX_DARWIN) +#include #endif