# HG changeset patch # User Igor Sysoev # Date 1159214400 -14400 # Node ID c982febb7588131fa955a82c35f533ff138ebfb5 # Parent f622c719b711abfbefa540f1a1542f2dfa113015 nginx 0.4.3 *) Change: now the 499 error could not be redirected using an "error_page" directive. *) Feature: the Solaris 10 event ports support. *) Feature: the ngx_http_browser_module. *) Bugfix: a segmentation fault may occur while redirecting the 400 error to the proxied server using an "proxy_pass" directive. *) Bugfix: a segmentation fault occurred if an unix domain socket was used in an "proxy_pass" directive; bug appeared in 0.3.47. *) Bugfix: SSI did work with memcached and nonbuffered responses. *) Workaround: of the Sun Studio PAUSE hardware capability bug. diff --git a/CHANGES b/CHANGES --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,24 @@ +Changes with nginx 0.4.3 26 Sep 2006 + + *) Change: now the 499 error could not be redirected using an + "error_page" directive. + + *) Feature: the Solaris 10 event ports support. + + *) Feature: the ngx_http_browser_module. + + *) Bugfix: a segmentation fault may occur while redirecting the 400 + error to the proxied server using an "proxy_pass" directive. + + *) Bugfix: a segmentation fault occurred if an unix domain socket was + used in an "proxy_pass" directive; bug appeared in 0.3.47. + + *) Bugfix: SSI did work with memcached and nonbuffered responses. + + *) Workaround: of the Sun Studio PAUSE hardware capability bug. + + Changes with nginx 0.4.2 14 Sep 2006 *) Bugfix: the O_NOATIME flag support on Linux was canceled; bug diff --git a/CHANGES.ru b/CHANGES.ru --- a/CHANGES.ru +++ b/CHANGES.ru @@ -1,4 +1,26 @@ +Изменения в nginx 0.4.3 26.09.2006 + + *) Изменение: ошибку 499 теперь нельзя перенаправить с помощью + директивы error_page. + + *) Добавление: поддержка Solaris 10 event ports. + + *) Добавление: модуль ngx_http_browser_module. + + *) Исправление: при перенаправлении ошибки 400 проксированному серверу + помощью директивы error_page мог произойти segmentation fault. + + *) Исправление: происходил segmentation fault, если в директиве + proxy_pass использовался unix domain сокет; ошибка появилась в + 0.3.47. + + *) Исправление: SSI не работал с ответами memcached и + небуферизированными проксированными ответами. + + *) Изменение: обход ошибки PAUSE hardware capability в Sun Studio. + + Изменения в nginx 0.4.2 14.09.2006 *) Исправление: убрана поддержка флага O_NOATIME на Linux; ошибка diff --git a/auto/cc/name b/auto/cc/name --- a/auto/cc/name +++ b/auto/cc/name @@ -8,7 +8,7 @@ echo $ngx_n "checking for C compiler ... if [ "$CC" = cl ]; then if `$NGX_WINE $CC -v 2>&1 \ | grep '^Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 13' \ - 2>&1 >/dev/null`; then + >/dev/null 2>&1`; then NGX_CC_NAME=msvc7 echo " Microsoft Visual C++ 7 compiler" @@ -29,27 +29,27 @@ if [ "$CC" = bcc32 ]; then echo " Borland C++ compiler" else -if `$CC -v 2>&1 | grep 'gcc version' 2>&1 >/dev/null`; then +if `$CC -v 2>&1 | grep 'gcc version' >/dev/null 2>&1`; then NGX_CC_NAME=gcc echo " GNU C compiler" else -if `$CC -V 2>&1 | grep '^Intel(R) C' 2>&1 >/dev/null`; then +if `$CC -V 2>&1 | grep '^Intel(R) C' >/dev/null 2>&1`; then NGX_CC_NAME=icc echo " Intel C++ compiler" else -if `$CC -V 2>&1 | grep 'Sun C' 2>&1 >/dev/null`; then +if `$CC -V 2>&1 | grep 'Sun C' >/dev/null 2>&1`; then NGX_CC_NAME=sunc echo " Sun C compiler" else -if `$CC -V 2>&1 | grep '^Compaq C' 2>&1 >/dev/null`; then +if `$CC -V 2>&1 | grep '^Compaq C' >/dev/null 2>&1`; then NGX_CC_NAME=ccc echo " Compaq C compiler" else -if `$CC -V 2>&1 | grep '^aCC: ' 2>&1 >/dev/null`; then +if `$CC -V 2>&1 | grep '^aCC: ' >/dev/null 2>&1`; then NGX_CC_NAME=acc echo " HP aC++ compiler" diff --git a/auto/cc/sunc b/auto/cc/sunc --- a/auto/cc/sunc +++ b/auto/cc/sunc @@ -3,9 +3,10 @@ # Sun C 5.7 Patch 117837-04 2005/05/11 +# Sun C 5.8 2005/10/13 NGX_SUNC_VER=`$CC -V 2>&1 | grep 'Sun C' 2>&1 \ - | sed -e 's/^.* Sun C \(.*\)/\1/'` + | sed -e 's/^.* Sun C \(.*\)/\1/'` echo " + Sun C version: $NGX_SUNC_VER" @@ -15,6 +16,20 @@ have=NGX_COMPILER value="\"Sun C $NGX_SU case "$NGX_PLATFORM" in *:i86pc) + ngx_feature="PAUSE hardware capability bug" + ngx_feature_name= + ngx_feature_run=bug + ngx_feature_incs= + ngx_feature_libs= + ngx_feature_test='__asm ("pause")' + + . auto/feature + + if [ $ngx_found = yes ]; then + # disable [ PAUSE ] hwcap for Sun Studio 11 + CORE_LINK="$CORE_LINK -Msrc/os/unix/ngx_sunpro_x86.map" + fi + NGX_AUX=" src/os/unix/ngx_sunpro_x86.il" ;; diff --git a/auto/endianess b/auto/endianess --- a/auto/endianess +++ b/auto/endianess @@ -26,7 +26,7 @@ ngx_test="$CC $CC_TEST_FLAGS $CC_AUX_FLA eval "$ngx_test >> $NGX_AUTOCONF_ERR 2>&1" if [ -x $NGX_AUTOTEST ]; then - if $NGX_AUTOTEST 2>&1 > /dev/null; then + if $NGX_AUTOTEST >/dev/null 2>&1; then echo " little endianess" have=NGX_HAVE_LITTLE_ENDIAN . auto/have else diff --git a/auto/feature b/auto/feature --- a/auto/feature +++ b/auto/feature @@ -43,7 +43,8 @@ if [ -x $NGX_AUTOTEST ]; then case "$ngx_feature_run" in yes) - if $NGX_AUTOTEST 2>&1 > /dev/null; then + # /bin/sh is used to intercept "Killed" or "Abort trap" messages + if /bin/sh -c $NGX_AUTOTEST >/dev/null 2>&1; then echo " found" ngx_found=yes @@ -57,7 +58,8 @@ if [ -x $NGX_AUTOTEST ]; then ;; bug) - if $NGX_AUTOTEST 2>&1 > /dev/null; then + # /bin/sh is used to intercept "Killed" or "Abort trap" messages + if /bin/sh -c $NGX_AUTOTEST >/dev/null 2>&1; then echo " not found" else diff --git a/auto/modules b/auto/modules --- a/auto/modules +++ b/auto/modules @@ -31,6 +31,14 @@ if [ $NGX_TEST_BUILD_DEVPOLL = YES ]; th CORE_SRCS="$CORE_SRCS $DEVPOLL_SRCS" fi + +if [ $NGX_TEST_BUILD_EVENTPORT = YES ]; then + have=NGX_HAVE_EVENTPORT . auto/have + have=NGX_TEST_BUILD_EVENTPORT . auto/have + EVENT_MODULES="$EVENT_MODULES $EVENTPORT_MODULE" + CORE_SRCS="$CORE_SRCS $EVENTPORT_SRCS" +fi + if [ $NGX_TEST_BUILD_EPOLL = YES ]; then have=NGX_HAVE_EPOLL . auto/have have=NGX_TEST_BUILD_EPOLL . auto/have @@ -237,6 +245,11 @@ if [ $HTTP_EMPTY_GIF = YES ]; then HTTP_SRCS="$HTTP_SRCS $HTTP_EMPTY_GIF_SRCS" fi +if [ $HTTP_BROWSER = YES ]; then + HTTP_MODULES="$HTTP_MODULES $HTTP_BROWSER_MODULE" + HTTP_SRCS="$HTTP_SRCS $HTTP_BROWSER_SRCS" +fi + # STUB #USE_MD5=YES #HTTP_SRCS="$HTTP_SRCS $HTPP_CACHE_SRCS" diff --git a/auto/options b/auto/options --- a/auto/options +++ b/auto/options @@ -23,6 +23,7 @@ NGX_LD_OPT= CPU=NO NGX_TEST_BUILD_DEVPOLL=NO +NGX_TEST_BUILD_EVENTPORT=NO NGX_TEST_BUILD_EPOLL=NO NGX_TEST_BUILD_RTSIG=NO NGX_TEST_BUILD_SOLARIS_SENDFILEV=NO @@ -68,6 +69,7 @@ HTTP_FASTCGI=YES HTTP_PERL=NO HTTP_MEMCACHED=YES HTTP_EMPTY_GIF=YES +HTTP_BROWSER=YES # STUB HTTP_STUB_STATUS=NO @@ -165,6 +167,7 @@ do --without-http_fastcgi_module) HTTP_FASTCGI=NO ;; --without-http_memcached_module) HTTP_MEMCACHED=NO ;; --without-http_empty_gif_module) HTTP_EMPTY_GIF=NO ;; + --without-http_browser_module) HTTP_BROWSER=NO ;; --with-http_perl_module) HTTP_PERL=YES ;; --with-perl_modules_path=*) NGX_PERL_MODULES="$value" ;; @@ -205,6 +208,7 @@ do --with-zlib-asm=*) ZLIB_ASM="$value" ;; --test-build-devpoll) NGX_TEST_BUILD_DEVPOLL=YES ;; + --test-build-eventport) NGX_TEST_BUILD_EVENTPORT=YES ;; --test-build-epoll) NGX_TEST_BUILD_EPOLL=YES ;; --test-build-rtsig) NGX_TEST_BUILD_RTSIG=YES ;; --test-build-solaris-sendfilev) NGX_TEST_BUILD_SOLARIS_SENDFILEV=YES ;; diff --git a/auto/os/solaris b/auto/os/solaris --- a/auto/os/solaris +++ b/auto/os/solaris @@ -39,3 +39,17 @@ if [ $ngx_found = yes ]; then CORE_SRCS="$CORE_SRCS $SOLARIS_SENDFILEV_SRCS" CORE_LIBS="$CORE_LIBS -lsendfile" fi + + +ngx_feature="event ports" +ngx_feature_name="NGX_HAVE_EVENTPORT" +ngx_feature_run=no +ngx_feature_incs="#include " +ngx_feature_libs= +ngx_feature_test="int n = port_create()" +. auto/feature + +if [ $ngx_found = yes ]; then + CORE_SRCS="$CORE_SRCS $EVENTPORT_SRCS" + EVENT_MODULES="$EVENT_MODULES $EVENTPORT_MODULE" +fi diff --git a/auto/sources b/auto/sources --- a/auto/sources +++ b/auto/sources @@ -95,6 +95,9 @@ KQUEUE_SRCS=src/event/modules/ngx_kqueue DEVPOLL_MODULE=ngx_devpoll_module DEVPOLL_SRCS=src/event/modules/ngx_devpoll_module.c +EVENTPORT_MODULE=ngx_eventport_module +EVENTPORT_SRCS=src/event/modules/ngx_eventport_module.c + EPOLL_MODULE=ngx_epoll_module EPOLL_SRCS=src/event/modules/ngx_epoll_module.c @@ -386,6 +389,10 @@ HTTP_EMPTY_GIF_MODULE=ngx_http_empty_gif HTTP_EMPTY_GIF_SRCS=src/http/modules/ngx_http_empty_gif_module.c +HTTP_BROWSER_MODULE=ngx_http_browser_module +HTTP_BROWSER_SRCS=src/http/modules/ngx_http_browser_module.c + + IMAP_INCS="src/imap" IMAP_DEPS="src/imap/ngx_imap.h" diff --git a/conf/nginx.conf b/conf/nginx.conf --- a/conf/nginx.conf +++ b/conf/nginx.conf @@ -18,8 +18,8 @@ http { include conf/mime.types; default_type application/octet-stream; - #log_format main '$remote_addr - $remote_user [$time_local] $status ' - # '"$request" $body_bytes_sent "$http_referer" ' + #log_format main '$remote_addr - $remote_user [$time_local] $request ' + # '"$status" $body_bytes_sent "$http_referer" ' # '"$http_user_agent" "$http_x_forwarded_for"'; #access_log logs/access.log main; 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_VER "nginx/0.4.2" +#define NGINX_VER "nginx/0.4.3" #define NGINX_VAR "NGINX" #define NGX_OLDPID_EXT ".oldbin" diff --git a/src/core/ngx_inet.c b/src/core/ngx_inet.c --- a/src/core/ngx_inet.c +++ b/src/core/ngx_inet.c @@ -243,6 +243,9 @@ ngx_parse_url(ngx_conf_t *cf, ngx_url_t p += 5; len -= 5; + u->uri.len = len; + u->uri.data = p; + if (u->uri_part) { for (i = 0; i < len; i++) { @@ -284,7 +287,8 @@ ngx_parse_url(ngx_conf_t *cf, ngx_url_t u->peers->peer[0].sockaddr = (struct sockaddr *) saun; u->peers->peer[0].socklen = sizeof(struct sockaddr_un); - u->peers->peer[0].name = u->url; + u->peers->peer[0].name.len = len + 5; + u->peers->peer[0].name.data = u->url.data; u->peers->peer[0].uri_separator = ":"; u->host_header.len = sizeof("localhost") - 1; diff --git a/src/event/modules/ngx_eventport_module.c b/src/event/modules/ngx_eventport_module.c new file mode 100644 --- /dev/null +++ b/src/event/modules/ngx_eventport_module.c @@ -0,0 +1,593 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include + + +#if (NGX_TEST_BUILD_EVENTPORT) + +#define ushort_t u_short +#define uint_t u_int + +/* Solaris declarations */ + +#define PORT_SOURCE_AIO 1 +#define PORT_SOURCE_TIMER 2 +#define PORT_SOURCE_USER 3 +#define PORT_SOURCE_FD 4 +#define PORT_SOURCE_ALERT 5 +#define PORT_SOURCE_MQ 6 + +#define ETIME 64 + +#define SIGEV_PORT 4 + +typedef struct { + int portev_events; /* event data is source specific */ + ushort_t portev_source; /* event source */ + ushort_t portev_pad; /* port internal use */ + uintptr_t portev_object; /* source specific object */ + void *portev_user; /* user cookie */ +} port_event_t; + +typedef struct port_notify { + int portnfy_port; /* bind request(s) to port */ + void *portnfy_user; /* user defined */ +} port_notify_t; + +typedef struct itimerspec { /* definition per POSIX.4 */ + struct timespec it_interval; /* timer period */ + struct timespec it_value; /* timer expiration */ +} itimerspec_t; + +int port_create(void) +{ + return -1; +} + +int port_associate(int port, int source, uintptr_t object, int events, + void *user) +{ + return -1; +} + +int port_dissociate(int port, int source, uintptr_t object) +{ + return -1; +} + +int port_getn(int port, port_event_t list[], uint_t max, uint_t *nget, + struct timespec *timeout) +{ + return -1; +} + +int timer_create(clockid_t clock_id, struct sigevent *evp, timer_t *timerid) +{ + return -1; +} + +int timer_settime(timer_t timerid, int flags, const struct itimerspec *value, + struct itimerspec *ovalue) +{ + return -1; +} + +int timer_delete(timer_t timerid) +{ + return -1; +} + +#endif + + +typedef struct { + u_int events; +} ngx_eventport_conf_t; + + +static ngx_int_t ngx_eventport_init(ngx_cycle_t *cycle, ngx_msec_t timer); +static void ngx_eventport_done(ngx_cycle_t *cycle); +static ngx_int_t ngx_eventport_add_event(ngx_event_t *ev, int event, + u_int flags); +static ngx_int_t ngx_eventport_del_event(ngx_event_t *ev, int event, + u_int flags); +static ngx_int_t ngx_eventport_process_events(ngx_cycle_t *cycle, + ngx_msec_t timer, ngx_uint_t flags); + +static void *ngx_eventport_create_conf(ngx_cycle_t *cycle); +static char *ngx_eventport_init_conf(ngx_cycle_t *cycle, void *conf); + +static int ep = -1; +static port_event_t *event_list; +static u_int nevents; +static timer_t event_timer = -1; + +static ngx_str_t eventport_name = ngx_string("eventport"); + + +static ngx_command_t ngx_eventport_commands[] = { + + { ngx_string("eventport_events"), + NGX_EVENT_CONF|NGX_CONF_TAKE1, + ngx_conf_set_num_slot, + 0, + offsetof(ngx_eventport_conf_t, events), + NULL }, + + ngx_null_command +}; + + +ngx_event_module_t ngx_eventport_module_ctx = { + &eventport_name, + ngx_eventport_create_conf, /* create configuration */ + ngx_eventport_init_conf, /* init configuration */ + + { + ngx_eventport_add_event, /* add an event */ + ngx_eventport_del_event, /* delete an event */ + ngx_eventport_add_event, /* enable an event */ + ngx_eventport_del_event, /* disable an event */ + NULL, /* add an connection */ + NULL, /* delete an connection */ + NULL, /* process the changes */ + ngx_eventport_process_events, /* process the events */ + ngx_eventport_init, /* init the events */ + ngx_eventport_done, /* done the events */ + } + +}; + +ngx_module_t ngx_eventport_module = { + NGX_MODULE_V1, + &ngx_eventport_module_ctx, /* module context */ + ngx_eventport_commands, /* module directives */ + NGX_EVENT_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; + + +static ngx_int_t +ngx_eventport_init(ngx_cycle_t *cycle, ngx_msec_t timer) +{ + port_notify_t pn; + struct itimerspec its; + struct sigevent sev; + ngx_eventport_conf_t *epcf; + + epcf = ngx_event_get_conf(cycle->conf_ctx, ngx_eventport_module); + + if (ep == -1) { + ep = port_create(); + + if (ep == -1) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, + "port_create() failed"); + return NGX_ERROR; + } + } + + if (nevents < epcf->events) { + if (event_list) { + ngx_free(event_list); + } + + event_list = ngx_alloc(sizeof(port_event_t) * epcf->events, + cycle->log); + if (event_list == NULL) { + return NGX_ERROR; + } + } + + ngx_event_flags = NGX_USE_EVENTPORT_EVENT; + + if (timer) { + ngx_memzero(&pn, sizeof(port_notify_t)); + pn.portnfy_port = ep; + + ngx_memzero(&sev, sizeof(struct sigevent)); + sev.sigev_notify = SIGEV_PORT; +#if !(NGX_TEST_BUILD_EVENTPORT) + sev.sigev_value.sival_ptr = &pn; +#endif + + if (timer_create(CLOCK_REALTIME, &sev, &event_timer) == -1) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, + "timer_create() failed"); + return NGX_ERROR; + } + + its.it_interval.tv_sec = timer / 1000; + its.it_interval.tv_nsec = (timer % 1000) * 1000000; + its.it_value.tv_sec = timer / 1000; + its.it_value.tv_nsec = (timer % 1000) * 1000000; + + if (timer_settime(event_timer, 0, &its, NULL) == -1) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, + "timer_settime() failed"); + return NGX_ERROR; + } + + ngx_event_flags |= NGX_USE_TIMER_EVENT; + } + + nevents = epcf->events; + + ngx_io = ngx_os_io; + + ngx_event_actions = ngx_eventport_module_ctx.actions; + + return NGX_OK; +} + + +static void +ngx_eventport_done(ngx_cycle_t *cycle) +{ + if (event_timer != -1) { + if (timer_delete(event_timer) == -1) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "timer_delete() failed"); + } + + event_timer = -1; + } + + if (close(ep) == -1) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "close() event port failed"); + } + + ep = -1; + + ngx_free(event_list); + + event_list = NULL; + nevents = 0; +} + + +static ngx_int_t +ngx_eventport_add_event(ngx_event_t *ev, int event, u_int flags) +{ + int events, prev; + ngx_event_t *e; + ngx_connection_t *c; + + c = ev->data; + + events = event; + + if (event == NGX_READ_EVENT) { + e = c->write; + prev = POLLOUT; +#if (NGX_READ_EVENT != POLLIN) + events = POLLIN; +#endif + + } else { + e = c->read; + prev = POLLIN; +#if (NGX_WRITE_EVENT != POLLOUT) + events = POLLOUT; +#endif + } + + if (e->oneshot) { + events |= prev; + } + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0, + "eventport add event: fd:%d ev:%04Xd", c->fd, events); + + if (port_associate(ep, PORT_SOURCE_FD, c->fd, events, + (void *) ((uintptr_t) ev | ev->instance)) + == -1) + { + ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno, + "port_associate() failed"); + return NGX_ERROR; + } + + ev->active = 1; + ev->oneshot = 1; + + return NGX_OK; +} + + +static ngx_int_t +ngx_eventport_del_event(ngx_event_t *ev, int event, u_int flags) +{ + ngx_event_t *e; + ngx_connection_t *c; + + /* + * when the file descriptor is closed, the event port automatically + * dissociates it from the port, so we do not need to dissociate explicity + * the event before the closing the file descriptor + */ + + if (flags & NGX_CLOSE_EVENT) { + ev->active = 0; + ev->oneshot = 0; + return NGX_OK; + } + + c = ev->data; + + if (event == NGX_READ_EVENT) { + e = c->write; + event = POLLOUT; + + } else { + e = c->read; + event = POLLIN; + } + + if (e->oneshot) { + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0, + "eventport change event: fd:%d ev:%04Xd", c->fd, event); + + if (port_associate(ep, PORT_SOURCE_FD, c->fd, event, + (void *) ((uintptr_t) ev | ev->instance)) + == -1) + { + ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno, + "port_associate() failed"); + return NGX_ERROR; + } + + } else { + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ev->log, 0, + "eventport del event: fd:%d", c->fd); + + if (port_dissociate(ep, PORT_SOURCE_FD, c->fd) == -1) { + ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno, + "port_dissociate() failed"); + return NGX_ERROR; + } + } + + ev->active = 0; + ev->oneshot = 0; + + return NGX_OK; +} + + +ngx_int_t +ngx_eventport_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, + ngx_uint_t flags) +{ + int n, revents; + u_int events; + ngx_err_t err; + ngx_int_t instance; + ngx_uint_t i, level; + ngx_event_t *ev, *rev, *wev, **queue; + ngx_connection_t *c; + struct timespec ts, *tp; + + if (timer == NGX_TIMER_INFINITE) { + tp = NULL; + + } else { + ts.tv_sec = timer / 1000; + ts.tv_nsec = (timer % 1000) * 1000000; + tp = &ts; + } + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, + "eventport timer: %M", timer); + + events = 1; + + n = port_getn(ep, event_list, nevents, &events, tp); + + err = ngx_errno; + + if (flags & NGX_UPDATE_TIME) { + ngx_time_update(0, 0); + } + + if (n == -1) { + if (err == ETIME) { + if (timer != NGX_TIMER_INFINITE) { + return NGX_OK; + } + + ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, + "port_getn() returned no events without timeout"); + return NGX_ERROR; + } + + level = (err == NGX_EINTR) ? NGX_LOG_INFO : NGX_LOG_ALERT; + ngx_log_error(level, cycle->log, err, "port_getn() failed"); + return NGX_ERROR; + } + + if (events == 0) { + if (timer != NGX_TIMER_INFINITE) { + return NGX_OK; + } + + ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, + "port_getn() returned no events without timeout"); + return NGX_ERROR; + } + + ngx_mutex_lock(ngx_posted_events_mutex); + + for (i = 0; i < events; i++) { + + if (event_list[i].portev_source == PORT_SOURCE_TIMER) { + ngx_time_update(0, 0); + continue; + } + + ev = event_list[i].portev_user; + + switch (event_list[i].portev_source) { + + case PORT_SOURCE_FD: + + instance = (uintptr_t) ev & 1; + ev = (ngx_event_t *) ((uintptr_t) ev & (uintptr_t) ~1); + + if (ev->closed || ev->instance != instance) { + + /* + * the stale event from a file descriptor + * that was just closed in this iteration + */ + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, + "eventport: stale event %p", ev); + continue; + } + + revents = event_list[i].portev_events; + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0, + "eventport: fd:%d, ev:%04Xd", + event_list[i].portev_object, revents); + + if (revents & (POLLERR|POLLHUP|POLLNVAL)) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, + "prot_getn() error fd:%d ev:%04Xd", + event_list[i].portev_object, revents); + } + + if (revents & ~(POLLIN|POLLOUT|POLLERR|POLLHUP|POLLNVAL)) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, + "strange port_getn() events fd:%d ev:%04Xd", + event_list[i].portev_object, revents); + } + + if ((revents & (POLLERR|POLLHUP|POLLNVAL)) + && (revents & (POLLIN|POLLOUT)) == 0) + { + /* + * if the error events were returned without POLLIN or POLLOUT, + * then add these flags to handle the events at least in one + * active handler + */ + + revents |= POLLIN|POLLOUT; + } + + c = ev->data; + rev = c->read; + wev = c->write; + + rev->active = 0; + wev->active = 0; + + if (revents & POLLIN) { + + if ((flags & NGX_POST_THREAD_EVENTS) && !rev->accept) { + rev->posted_ready = 1; + + } else { + rev->ready = 1; + } + + if (flags & NGX_POST_EVENTS) { + queue = (ngx_event_t **) (rev->accept ? + &ngx_posted_accept_events : &ngx_posted_events); + + ngx_locked_post_event(rev, queue); + + } else { + rev->handler(rev); + } + + if (rev->accept) { + if (ngx_use_accept_mutex) { + ngx_accept_events = 1; + continue; + } + + if (port_associate(ep, PORT_SOURCE_FD, c->fd, POLLIN, + (void *) ((uintptr_t) ev | ev->instance)) + == -1) + { + ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno, + "port_associate() failed"); + return NGX_ERROR; + } + } + } + + if (revents & POLLOUT) { + + if (flags & NGX_POST_THREAD_EVENTS) { + wev->posted_ready = 1; + + } else { + wev->ready = 1; + } + + if (flags & NGX_POST_EVENTS) { + ngx_locked_post_event(wev, &ngx_posted_events); + + } else { + wev->handler(wev); + } + } + + continue; + + default: + ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, + "unexpected even_port object %d", + event_list[i].portev_object); + continue; + } + } + + ngx_mutex_unlock(ngx_posted_events_mutex); + + return NGX_OK; +} + + +static void * +ngx_eventport_create_conf(ngx_cycle_t *cycle) +{ + ngx_eventport_conf_t *epcf; + + epcf = ngx_palloc(cycle->pool, sizeof(ngx_eventport_conf_t)); + if (epcf == NULL) { + return NGX_CONF_ERROR; + } + + epcf->events = NGX_CONF_UNSET; + + return epcf; +} + + +static char * +ngx_eventport_init_conf(ngx_cycle_t *cycle, void *conf) +{ + ngx_eventport_conf_t *epcf = conf; + + ngx_conf_init_uint_value(epcf->events, 32); + + return NGX_CONF_OK; +} diff --git a/src/event/modules/ngx_poll_module.c b/src/event/modules/ngx_poll_module.c --- a/src/event/modules/ngx_poll_module.c +++ b/src/event/modules/ngx_poll_module.c @@ -155,8 +155,6 @@ ngx_poll_add_event(ngx_event_t *ev, int ev->index = e->index; } - ev->oneshot = (flags & NGX_ONESHOT_EVENT) ? 1 : 0; - return NGX_OK; } diff --git a/src/event/modules/ngx_select_module.c b/src/event/modules/ngx_select_module.c --- a/src/event/modules/ngx_select_module.c +++ b/src/event/modules/ngx_select_module.c @@ -180,7 +180,6 @@ ngx_select_add_event(ngx_event_t *ev, in #endif ev->active = 1; - ev->oneshot = (u_char) ((flags & NGX_ONESHOT_EVENT) ? 1 : 0); event_index[nevents] = ev; ev->index = nevents; diff --git a/src/event/ngx_event.c b/src/event/ngx_event.c --- a/src/event/ngx_event.c +++ b/src/event/ngx_event.c @@ -13,6 +13,7 @@ extern ngx_module_t ngx_kqueue_module; +extern ngx_module_t ngx_eventport_module; extern ngx_module_t ngx_devpoll_module; extern ngx_module_t ngx_epoll_module; extern ngx_module_t ngx_rtsig_module; @@ -49,6 +50,7 @@ ngx_atomic_t *ngx_connection_cou ngx_atomic_t *ngx_accept_mutex_ptr; ngx_shmtx_t ngx_accept_mutex; ngx_uint_t ngx_use_accept_mutex; +ngx_uint_t ngx_accept_events; ngx_uint_t ngx_accept_mutex_held; ngx_msec_t ngx_accept_mutex_delay; ngx_int_t ngx_accept_disabled; @@ -314,19 +316,25 @@ ngx_handle_read_event(ngx_event_t *rev, return NGX_OK; } - } else if (ngx_event_flags & NGX_USE_ONESHOT_EVENT) { + } else if (ngx_event_flags & NGX_USE_EVENTPORT_EVENT) { /* event ports */ - if (!rev->active) { - if (ngx_add_event(rev, NGX_READ_EVENT, NGX_ONESHOT_EVENT) - == NGX_ERROR) - { + if (!rev->active && !rev->ready) { + if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) { return NGX_ERROR; } + + return NGX_OK; } - return NGX_OK; + if (rev->oneshot && !rev->ready) { + if (ngx_del_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) { + return NGX_ERROR; + } + + return NGX_OK; + } } /* aio, iocp, rtsig */ @@ -341,7 +349,7 @@ ngx_handle_write_event(ngx_event_t *wev, ngx_connection_t *c; if (lowat) { - c = (ngx_connection_t *) wev->data; + c = wev->data; if (ngx_send_lowat(c, lowat) == NGX_ERROR) { return NGX_ERROR; @@ -387,19 +395,25 @@ ngx_handle_write_event(ngx_event_t *wev, return NGX_OK; } - } else if (ngx_event_flags & NGX_USE_ONESHOT_EVENT) { + } else if (ngx_event_flags & NGX_USE_EVENTPORT_EVENT) { /* event ports */ - if (!wev->active) { - if (ngx_add_event(wev, NGX_WRITE_EVENT, NGX_ONESHOT_EVENT) - == NGX_ERROR) - { + if (!wev->active && !wev->ready) { + if (ngx_add_event(wev, NGX_WRITE_EVENT, 0) == NGX_ERROR) { return NGX_ERROR; } + + return NGX_OK; } - return NGX_OK; + if (wev->oneshot && wev->ready) { + if (ngx_del_event(wev, NGX_WRITE_EVENT, 0) == NGX_ERROR) { + return NGX_ERROR; + } + + return NGX_OK; + } } /* aio, iocp, rtsig */ @@ -1198,7 +1212,7 @@ ngx_event_init_conf(ngx_cycle_t *cycle, event_module = ngx_modules[i]->ctx; if (ngx_strcmp(event_module->name->data, event_core_name.data) - == 0) + == 0) { continue; } diff --git a/src/event/ngx_event.h b/src/event/ngx_event.h --- a/src/event/ngx_event.h +++ b/src/event/ngx_event.h @@ -41,8 +41,6 @@ struct ngx_event_s { unsigned accept:1; - unsigned oneshot:1; - /* used to detect the stale events in kqueue, rt signals and epoll */ unsigned instance:1; @@ -57,6 +55,8 @@ struct ngx_event_s { /* the ready event; in aio mode 0 means that no operation can be posted */ unsigned ready:1; + unsigned oneshot:1; + /* aio operation is complete */ unsigned complete:1; @@ -224,7 +224,7 @@ extern ngx_event_actions_t ngx_event_a /* * The event filter is deleted after a notification without an additional - * syscall: kqueue, epoll, Solaris 10's event ports. + * syscall: kqueue, epoll. */ #define NGX_USE_ONESHOT_EVENT 0x00000002 @@ -286,14 +286,17 @@ extern ngx_event_actions_t ngx_event_a */ #define NGX_USE_TIMER_EVENT 0x00000800 +/* + * All event filters on file descriptor are deleted after a notification: + * Solaris 10's event ports. + */ +#define NGX_USE_EVENTPORT_EVENT 0x00001000 + /* * The event filter is deleted before the closing file. - * Has no meaning for select, poll, epoll. - * - * kqueue: kqueue deletes event filters for file that closed - * so we need only to delete filters in user-level batch array + * Has no meaning for select, poll, kqueue, epoll. * /dev/poll: we need to flush POLLREMOVE event before closing file */ @@ -335,7 +338,7 @@ extern ngx_event_actions_t ngx_event_a #define NGX_DISABLE_EVENT EV_DISABLE -#elif (NGX_HAVE_DEVPOLL) +#elif (NGX_HAVE_DEVPOLL || NGX_HAVE_EVENTPORT) #define NGX_READ_EVENT POLLIN #define NGX_WRITE_EVENT POLLOUT @@ -446,6 +449,7 @@ extern ngx_atomic_t *ngx_connec extern ngx_atomic_t *ngx_accept_mutex_ptr; extern ngx_shmtx_t ngx_accept_mutex; extern ngx_uint_t ngx_use_accept_mutex; +extern ngx_uint_t ngx_accept_events; extern ngx_uint_t ngx_accept_mutex_held; extern ngx_msec_t ngx_accept_mutex_delay; extern ngx_int_t ngx_accept_disabled; diff --git a/src/event/ngx_event_accept.c b/src/event/ngx_event_accept.c --- a/src/event/ngx_event_accept.c +++ b/src/event/ngx_event_accept.c @@ -21,7 +21,7 @@ static void ngx_close_accepted_connectio void ngx_event_accept(ngx_event_t *ev) { - socklen_t sl; + socklen_t socklen; ngx_err_t err; ngx_log_t *log; ngx_socket_t s; @@ -48,14 +48,16 @@ ngx_event_accept(ngx_event_t *ev) "accept on %V, ready: %d", &ls->addr_text, ev->available); do { - sl = NGX_SOCKLEN; + socklen = NGX_SOCKLEN; - s = accept(lc->fd, (struct sockaddr *) sa, &sl); + s = accept(lc->fd, (struct sockaddr *) sa, &socklen); if (s == -1) { err = ngx_socket_errno; if (err == NGX_EAGAIN) { + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, err, + "accept() not ready"); return; } @@ -104,13 +106,13 @@ ngx_event_accept(ngx_event_t *ev) return; } - c->sockaddr = ngx_palloc(c->pool, sl); + c->sockaddr = ngx_palloc(c->pool, socklen); if (c->sockaddr == NULL) { ngx_close_accepted_connection(c); return; } - ngx_memcpy(c->sockaddr, sa, sl); + ngx_memcpy(c->sockaddr, sa, socklen); log = ngx_palloc(c->pool, sizeof(ngx_log_t)); if (log == NULL) { @@ -152,7 +154,7 @@ ngx_event_accept(ngx_event_t *ev) c->pool->log = log; c->listening = ls; - c->socklen = sl; + c->socklen = socklen; c->unexpected_eof = 1; @@ -264,7 +266,10 @@ ngx_trylock_accept_mutex(ngx_cycle_t *cy ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "accept mutex locked"); - if (ngx_accept_mutex_held && !(ngx_event_flags & NGX_USE_RTSIG_EVENT)) { + if (ngx_accept_mutex_held + && ngx_accept_events == 0 + && !(ngx_event_flags & NGX_USE_RTSIG_EVENT)) + { return NGX_OK; } @@ -273,11 +278,15 @@ ngx_trylock_accept_mutex(ngx_cycle_t *cy return NGX_ERROR; } + ngx_accept_events = 0; ngx_accept_mutex_held = 1; return NGX_OK; } + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, + "accept mutex lock failed: %ui", ngx_accept_mutex_held); + if (ngx_accept_mutex_held) { if (ngx_disable_accept_events(cycle) == NGX_ERROR) { return NGX_ERROR; diff --git a/src/http/modules/ngx_http_browser_module.c b/src/http/modules/ngx_http_browser_module.c new file mode 100644 --- /dev/null +++ b/src/http/modules/ngx_http_browser_module.c @@ -0,0 +1,706 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include + + +/* + * The module can check browser versions conforming to the following formats: + * X, X.X, X.X.X, and X.X.X.X. The maximum values of each format may be + * 4000, 4000.99, 4000.99.99, and 4000.99.99.99. + */ + + +#define NGX_HTTP_MODERN_BROWSER 0 +#define NGX_HTTP_ANCIENT_BROWSER 1 + + +typedef struct { + u_char browser[12]; + size_t skip; + size_t add; + u_char name[12]; +} ngx_http_modern_browser_mask_t; + + +typedef struct { + ngx_uint_t version; + size_t skip; + size_t add; + u_char name[12]; +} ngx_http_modern_browser_t; + + +typedef struct { + ngx_str_t name; + ngx_http_get_variable_pt handler; + uintptr_t data; +} ngx_http_browser_variable_t; + + +typedef struct { + ngx_array_t *modern_browsers; + ngx_array_t *ancient_browsers; + ngx_http_variable_value_t *modern_browser_value; + ngx_http_variable_value_t *ancient_browser_value; + + unsigned modern_unlisted_browsers:1; + unsigned netscape4:1; +} ngx_http_browser_conf_t; + + +static ngx_int_t ngx_http_msie_variable(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_browser_variable(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); + +static ngx_uint_t ngx_http_browser(ngx_http_request_t *r, + ngx_http_browser_conf_t *cf); + +static ngx_int_t ngx_http_browser_add_variable(ngx_conf_t *cf); +static void *ngx_http_browser_create_conf(ngx_conf_t *cf); +static char *ngx_http_browser_merge_conf(ngx_conf_t *cf, void *parent, + void *child); +static int ngx_libc_cdecl ngx_http_modern_browser_sort(const void *one, + const void *two); +static char *ngx_http_modern_browser(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static char *ngx_http_ancient_browser(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static char *ngx_http_modern_browser_value(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static char *ngx_http_ancient_browser_value(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); + + +static ngx_command_t ngx_http_browser_commands[] = { + + { ngx_string("modern_browser"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12, + ngx_http_modern_browser, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("ancient_browser"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE, + ngx_http_ancient_browser, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("modern_browser_value"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_http_modern_browser_value, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("ancient_browser_value"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_http_ancient_browser_value, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + NULL }, + + ngx_null_command +}; + + +static ngx_http_module_t ngx_http_browser_module_ctx = { + ngx_http_browser_add_variable, /* preconfiguration */ + NULL, /* postconfiguration */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + NULL, /* create server configuration */ + NULL, /* merge server configuration */ + + ngx_http_browser_create_conf, /* create location configuration */ + ngx_http_browser_merge_conf /* merge location configuration */ +}; + + +ngx_module_t ngx_http_browser_module = { + NGX_MODULE_V1, + &ngx_http_browser_module_ctx, /* module context */ + ngx_http_browser_commands, /* module directives */ + NGX_HTTP_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; + + +static ngx_http_modern_browser_mask_t ngx_http_modern_browser_masks[] = { + + /* Opera must be the first browser to check */ + + /* + * "Opera/7.50 (X11; FreeBSD i386; U) [en]" + * "Mozilla/5.0 (X11; FreeBSD i386; U) Opera 7.50 [en]" + * "Mozilla/4.0 (compatible; MSIE 6.0; X11; FreeBSD i386) Opera 7.50 [en]" + * "Opera/8.0 (Windows NT 5.1; U; ru)" + * "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; en) Opera 8.0" + * "Opera/9.01 (X11; FreeBSD 6 i386; U; en)" + */ + + { "opera", + 0, + sizeof("Opera ") - 1, + "Opera"}, + + /* "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)" */ + + { "msie", + sizeof("Mozilla/4.0 (compatible; ") - 1, + sizeof("MSIE ") - 1, + "MSIE "}, + + /* + * "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.0.0) Gecko/20020610" + * "Mozilla/5.0 (Windows; U; Windows NT 5.1; ru-RU; rv:1.5) Gecko/20031006" + * "Mozilla/5.0 (Windows; U; Windows NT 5.1; ru-RU; rv:1.6) Gecko/20040206 + * Firefox/0.8" + * "Mozilla/5.0 (Windows; U; Windows NT 5.1; ru-RU; rv:1.7.8) + * Gecko/20050511 Firefox/1.0.4" + * "Mozilla/5.0 (X11; U; FreeBSD i386; en-US; rv:1.8.0.5) Gecko/20060729 + * Firefox/1.5.0.5" + */ + + { "gecko", + sizeof("Mozilla/5.0 (") - 1, + sizeof("rv:") - 1, + "rv:"}, + + /* + * "Mozilla/5.0 (Macintosh; U; PPC Mac OS X; ru-ru) AppleWebKit/125.2 + * (KHTML, like Gecko) Safari/125.7" + * "Mozilla/5.0 (SymbianOS/9.1; U; en-us) AppleWebKit/413 + * (KHTML, like Gecko) Safari/413" + * "Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/418 + * (KHTML, like Gecko) Safari/417.9.3" + * "Mozilla/5.0 (Macintosh; U; PPC Mac OS X; ru-ru) AppleWebKit/418.8 + * (KHTML, like Gecko) Safari/419.3" + */ + + { "safari", + sizeof("Mozilla/5.0 (") - 1, + sizeof("Safari/") - 1, + "Safari/"}, + + /* + * "Mozilla/5.0 (compatible; Konqueror/3.1; Linux)" + * "Mozilla/5.0 (compatible; Konqueror/3.4; Linux) KHTML/3.4.2 (like Gecko)" + * "Mozilla/5.0 (compatible; Konqueror/3.5; FreeBSD) KHTML/3.5.1 + * (like Gecko)" + */ + + { "konqueror", + sizeof("Mozilla/5.0 (compatible; ") - 1, + sizeof("Konqueror/") - 1, + "Konqueror/"}, + + { "", 0, 0, "" } + +}; + + +static ngx_http_browser_variable_t ngx_http_browsers[] = { + { ngx_string("msie"), ngx_http_msie_variable, 0 }, + { ngx_string("modern_browser"), ngx_http_browser_variable, + NGX_HTTP_MODERN_BROWSER }, + { ngx_string("ancient_browser"), ngx_http_browser_variable, + NGX_HTTP_ANCIENT_BROWSER }, + { ngx_null_string, NULL, 0 } +}; + + +static ngx_int_t +ngx_http_browser_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, + uintptr_t data) +{ + ngx_uint_t rc; + ngx_http_browser_conf_t *cf; + + cf = ngx_http_get_module_loc_conf(r, ngx_http_browser_module); + + rc = ngx_http_browser(r, cf); + + if (data == NGX_HTTP_MODERN_BROWSER && rc == NGX_HTTP_MODERN_BROWSER) { + *v = *cf->modern_browser_value; + return NGX_OK; + } + + if (data == NGX_HTTP_ANCIENT_BROWSER && rc == NGX_HTTP_ANCIENT_BROWSER) { + *v = *cf->ancient_browser_value; + return NGX_OK; + } + + *v = ngx_http_variable_null_value; + return NGX_OK; +} + + +static ngx_uint_t +ngx_http_browser(ngx_http_request_t *r, ngx_http_browser_conf_t *cf) +{ + size_t len; + u_char *name, *ua, *last, c; + ngx_str_t *ancient; + ngx_uint_t i, version, ver, scale; + ngx_http_modern_browser_t *modern; + + if (r->headers_in.user_agent == NULL) { + if (cf->modern_unlisted_browsers) { + return NGX_HTTP_MODERN_BROWSER; + } + + return NGX_HTTP_ANCIENT_BROWSER; + } + + ua = r->headers_in.user_agent->value.data; + len = r->headers_in.user_agent->value.len; + last = ua + len; + + if (cf->modern_browsers) { + modern = cf->modern_browsers->elts; + + for (i = 0; i < cf->modern_browsers->nelts; i++) { + name = ua + modern[i].skip; + + if (name >= last) { + continue; + } + + name = (u_char *) ngx_strstr(name, modern[i].name); + + if (name == NULL) { + continue; + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "browser: \"%s\"", name); + + name += modern[i].add; + + if (name >= last) { + continue; + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "version: \"%ui\" \"%s\"", modern[i].version, name); + + version = 0; + ver = 0; + scale = 1000000; + + while (name < last) { + + c = *name++; + + if (c >= '0' && c <= '9') { + ver = ver * 10 + (c - '0'); + continue; + } + + if (c == '.') { + version += ver * scale; + + if (version > modern[i].version) { + return NGX_HTTP_MODERN_BROWSER; + } + + ver = 0; + scale /= 100; + continue; + } + + break; + } + + version += ver * scale; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "version: \"%ui\" \"%ui\"", + modern[i].version, version); + + if (version >= modern[i].version) { + return NGX_HTTP_MODERN_BROWSER; + } + } + + if (!cf->modern_unlisted_browsers) { + return NGX_HTTP_ANCIENT_BROWSER; + } + } + + if (cf->netscape4) { + if (len > sizeof("Mozilla/4.72 ") - 1 + && ngx_strncmp(ua, "Mozilla/", sizeof("Mozilla/") - 1) == 0 + && ua[8] > '0' && ua[8] < '5') + { + return NGX_HTTP_ANCIENT_BROWSER; + } + } + + if (cf->ancient_browsers) { + ancient = cf->ancient_browsers->elts; + + for (i = 0; i < cf->ancient_browsers->nelts; i++) { + if (len >= ancient[i].len + && ngx_strstr(ua, ancient[i].data) != NULL) + { + return NGX_HTTP_ANCIENT_BROWSER; + } + } + } + + if (cf->modern_unlisted_browsers) { + return NGX_HTTP_MODERN_BROWSER; + } + + return NGX_HTTP_ANCIENT_BROWSER; +} + + +static ngx_int_t +ngx_http_msie_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, + uintptr_t data) +{ + if (r->headers_in.msie) { + *v = ngx_http_variable_true_value; + return NGX_OK; + } + + *v = ngx_http_variable_null_value; + return NGX_OK; +} + + +static ngx_int_t +ngx_http_browser_add_variable(ngx_conf_t *cf) +{ + ngx_http_browser_variable_t *var; + ngx_http_variable_t *v; + + for (var = ngx_http_browsers; var->name.len; var++) { + + v = ngx_http_add_variable(cf, &var->name, NGX_HTTP_VAR_CHANGABLE); + if (v == NULL) { + return NGX_ERROR; + } + + v->get_handler = var->handler; + v->data = var->data; + } + + return NGX_OK; +} + + +static void * +ngx_http_browser_create_conf(ngx_conf_t *cf) +{ + ngx_http_browser_conf_t *conf; + + conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_browser_conf_t)); + if (conf == NULL) { + return NGX_CONF_ERROR; + } + + /* + * set by ngx_pcalloc(): + * + * conf->modern_browsers = NULL; + * conf->ancient_browsers = NULL; + * conf->modern_browser_value = NULL; + * conf->ancient_browser_value = NULL; + * + * conf->modern_unlisted_browsers = 0; + * conf->netscape4 = 0; + */ + + return conf; +} + + +static char * +ngx_http_browser_merge_conf(ngx_conf_t *cf, void *parent, void *child) +{ + ngx_http_browser_conf_t *prev = parent; + ngx_http_browser_conf_t *conf = child; + + ngx_uint_t i, n; + ngx_http_modern_browser_t *browsers, *opera; + + /* + * At the merge the skip field is used to store the browser slot, + * it will be used in sorting and then will overwritten + * with a real skip value. The zero value means Opera. + */ + + if (conf->modern_browsers == NULL) { + conf->modern_browsers = prev->modern_browsers; + + } else { + browsers = conf->modern_browsers->elts; + + for (i = 0; i < conf->modern_browsers->nelts; i++) { + if (browsers[i].skip == 0) { + goto found; + } + } + + /* + * Opera may contain MSIE string, so if Opera was not enumerated + * as modern browsers, then add it and set a unreachable version + */ + + opera = ngx_array_push(conf->modern_browsers); + if (opera == NULL) { + return NGX_CONF_ERROR; + } + + opera->skip = 0; + opera->version = 4001000000U; + + browsers = conf->modern_browsers->elts; + +found: + + ngx_qsort(browsers, (size_t) conf->modern_browsers->nelts, + sizeof(ngx_http_modern_browser_t), + ngx_http_modern_browser_sort); + + for (i = 0; i < conf->modern_browsers->nelts; i++) { + n = browsers[i].skip; + + browsers[i].skip = ngx_http_modern_browser_masks[n].skip; + browsers[i].add = ngx_http_modern_browser_masks[n].add; + (void) ngx_cpystrn(browsers[i].name, + ngx_http_modern_browser_masks[n].name, 12); + } + } + + if (conf->ancient_browsers == NULL) { + conf->ancient_browsers = prev->ancient_browsers; + } + + if (conf->modern_browser_value == NULL) { + conf->modern_browser_value = prev->modern_browser_value; + } + + if (conf->modern_browser_value == NULL) { + conf->modern_browser_value = &ngx_http_variable_true_value; + } + + if (conf->ancient_browser_value == NULL) { + conf->ancient_browser_value = prev->ancient_browser_value; + } + + if (conf->ancient_browser_value == NULL) { + conf->ancient_browser_value = &ngx_http_variable_true_value; + } + + return NGX_CONF_OK; +} + + +static int ngx_libc_cdecl +ngx_http_modern_browser_sort(const void *one, const void *two) +{ + ngx_http_modern_browser_t *first = (ngx_http_modern_browser_t *) one; + ngx_http_modern_browser_t *second = (ngx_http_modern_browser_t *) two; + + return (first->skip - second->skip); +} + + +static char * +ngx_http_modern_browser(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_http_browser_conf_t *bcf = conf; + + u_char c; + ngx_str_t *value; + ngx_uint_t i, n, version, ver, scale; + ngx_http_modern_browser_t *browser; + ngx_http_modern_browser_mask_t *mask; + + value = cf->args->elts; + + if (cf->args->nelts == 2) { + if (ngx_strcmp(value[1].data, "unlisted") == 0) { + bcf->modern_unlisted_browsers = 1; + return NGX_CONF_OK; + } + + return NGX_CONF_ERROR; + } + + if (bcf->modern_browsers == NULL) { + bcf->modern_browsers = ngx_array_create(cf->pool, 5, + sizeof(ngx_http_modern_browser_t)); + if (bcf->modern_browsers == NULL) { + return NGX_CONF_ERROR; + } + } + + browser = ngx_array_push(bcf->modern_browsers); + if (browser == NULL) { + return NGX_CONF_ERROR; + } + + mask = ngx_http_modern_browser_masks; + + for (n = 0; mask[n].browser[0] != '\0'; n++) { + if (ngx_strcasecmp(mask[n].browser, value[1].data) == 0) { + goto found; + } + } + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "unknown browser name \"%V\"", &value[1]); + + return NGX_CONF_ERROR; + +found: + + /* + * at this stage the skip field is used to store the browser slot, + * it will be used in sorting in merge stage and then will overwritten + * with a real value + */ + + browser->skip = n; + + version = 0; + ver = 0; + scale = 1000000; + + for (i = 0; i < value[2].len; i++) { + + c = value[2].data[i]; + + if (c >= '0' && c <= '9') { + ver = ver * 10 + (c - '0'); + continue; + } + + if (c == '.') { + version += ver * scale; + ver = 0; + scale /= 100; + continue; + } + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid browser version \"%V\"", &value[2]); + + return NGX_CONF_ERROR; + } + + version += ver * scale; + + browser->version = version; + + return NGX_CONF_OK; +} + + +static char * +ngx_http_ancient_browser(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_http_browser_conf_t *bcf = conf; + + ngx_str_t *value, *browser; + ngx_uint_t i; + + value = cf->args->elts; + + for (i = 1; i < cf->args->nelts; i++) { + if (ngx_strcmp(value[i].data, "netscape4") == 0) { + bcf->netscape4 = 1; + continue; + } + + if (bcf->ancient_browsers == NULL) { + bcf->ancient_browsers = ngx_array_create(cf->pool, 4, + sizeof(ngx_str_t)); + if (bcf->ancient_browsers == NULL) { + return NGX_CONF_ERROR; + } + } + + browser = ngx_array_push(bcf->ancient_browsers); + if (browser == NULL) { + return NGX_CONF_ERROR; + } + + *browser = value[i]; + } + + return NGX_CONF_OK; +} + + +static char * +ngx_http_modern_browser_value(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_http_browser_conf_t *bcf = conf; + + ngx_str_t *value; + + bcf->modern_browser_value = ngx_palloc(cf->pool, + sizeof(ngx_http_variable_value_t)); + if (bcf->modern_browser_value == NULL) { + return NGX_CONF_ERROR; + } + + value = cf->args->elts; + + bcf->modern_browser_value->len = value[1].len; + bcf->modern_browser_value->valid = 1; + bcf->modern_browser_value->no_cachable = 0; + bcf->modern_browser_value->not_found = 0; + bcf->modern_browser_value->data = value[1].data; + + return NGX_CONF_OK; +} + + +static char * +ngx_http_ancient_browser_value(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_http_browser_conf_t *bcf = conf; + + ngx_str_t *value; + + bcf->ancient_browser_value = ngx_palloc(cf->pool, + sizeof(ngx_http_variable_value_t)); + if (bcf->ancient_browser_value == NULL) { + return NGX_CONF_ERROR; + } + + value = cf->args->elts; + + bcf->ancient_browser_value->len = value[1].len; + bcf->ancient_browser_value->valid = 1; + bcf->ancient_browser_value->no_cachable = 0; + bcf->ancient_browser_value->not_found = 0; + bcf->ancient_browser_value->data = value[1].data; + + return NGX_CONF_OK; +} diff --git a/src/http/modules/ngx_http_headers_filter_module.c b/src/http/modules/ngx_http_headers_filter_module.c --- a/src/http/modules/ngx_http_headers_filter_module.c +++ b/src/http/modules/ngx_http_headers_filter_module.c @@ -207,7 +207,6 @@ ngx_http_headers_filter(ngx_http_request cc->value.len = ngx_sprintf(cc->value.data, "max-age=%T", conf->expires) - cc->value.data; - } } } 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 @@ -843,7 +843,7 @@ ngx_http_ssi_body_filter(ngx_http_reques continue; } - if (ctx->buf->last_buf || ctx->buf->recycled) { + if (ctx->buf->last_buf || ngx_buf_in_memory(ctx->buf)) { if (b == NULL) { if (ctx->free) { cl = ctx->free; 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 @@ -514,12 +514,7 @@ ngx_http_handler(ngx_http_request_t *r) } else { r->lingering_close = 0; } - } - - r->valid_unparsed_uri = 1; - r->valid_location = 1; - - if (!r->internal) { + r->phase_handler = 0; } else { @@ -527,6 +522,12 @@ ngx_http_handler(ngx_http_request_t *r) r->phase_handler = cmcf->phase_engine.server_rewrite_index; } + if (r->unparsed_uri.len) { + r->valid_unparsed_uri = 1; + } + + r->valid_location = 1; + r->write_event_handler = ngx_http_core_run_phases; ngx_http_core_run_phases(r); } @@ -2884,7 +2885,7 @@ ngx_http_core_error_page(ngx_conf_t *cf, err->status = ngx_atoi(value[i].data, value[i].len); - if (err->status == NGX_ERROR) { + if (err->status == NGX_ERROR || err->status == 499) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid value \"%V\"", &value[i]); return NGX_CONF_ERROR; 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 @@ -979,7 +979,7 @@ ngx_http_read_request_header(ngx_http_re c->error = rev->error; c->log->action = "sending response to client"; - ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); + ngx_http_finalize_request(r, NGX_HTTP_CLIENT_CLOSED_REQUEST); return NGX_ERROR; } diff --git a/src/http/ngx_http_special_response.c b/src/http/ngx_http_special_response.c --- a/src/http/ngx_http_special_response.c +++ b/src/http/ngx_http_special_response.c @@ -299,6 +299,9 @@ static ngx_str_t error_pages[] = { }; +static ngx_str_t ngx_http_get_name = { 3, (u_char *) "GET " }; + + ngx_int_t ngx_http_special_response_handler(ngx_http_request_t *r, ngx_int_t error) { @@ -364,6 +367,7 @@ ngx_http_special_response_handler(ngx_ht r->err_status = err_page[i].overwrite; r->method = NGX_HTTP_GET; + r->method_name = ngx_http_get_name; uri = &err_page[i].uri; diff --git a/src/os/unix/ngx_process.c b/src/os/unix/ngx_process.c --- a/src/os/unix/ngx_process.c +++ b/src/os/unix/ngx_process.c @@ -338,7 +338,7 @@ ngx_signal_handler(int signo) /* * Ignore the signal in the new binary if its parent is * not the init process, i.e. the old binary's process - * is still running. Or ingore the signal in the old binary's + * is still running. Or ignore the signal in the old binary's * process if the new binary's process is already running. */ diff --git a/src/os/unix/ngx_shmem.h b/src/os/unix/ngx_shmem.h --- a/src/os/unix/ngx_shmem.h +++ b/src/os/unix/ngx_shmem.h @@ -4,8 +4,8 @@ */ -#ifndef _NGX_SHARED_H_INCLUDED_ -#define _NGX_SHARED_H_INCLUDED_ +#ifndef _NGX_SHMEM_H_INCLUDED_ +#define _NGX_SHMEM_H_INCLUDED_ #include @@ -23,4 +23,4 @@ ngx_int_t ngx_shm_alloc(ngx_shm_t *shm); void ngx_shm_free(ngx_shm_t *shm); -#endif /* _NGX_SHARED_H_INCLUDED_ */ +#endif /* _NGX_SHMEM_H_INCLUDED_ */ diff --git a/src/os/unix/ngx_socket.c b/src/os/unix/ngx_socket.c --- a/src/os/unix/ngx_socket.c +++ b/src/os/unix/ngx_socket.c @@ -9,9 +9,9 @@ /* - * ioctl(FIONBIO) sets a blocking mode with the single syscall - * while fcntl(F_SETFL, !O_NONBLOCK) needs to learn before - * the previous state using fcntl(F_GETFL). + * ioctl(FIONBIO) sets a non-blocking mode with the single syscall + * while fcntl(F_SETFL, O_NONBLOCK) needs to learn the current state + * using fcntl(F_GETFL). * * ioctl() and fcntl() are syscalls at least in FreeBSD 2.x, Linux 2.2 * and Solaris 7. diff --git a/src/os/unix/ngx_solaris_config.h b/src/os/unix/ngx_solaris_config.h --- a/src/os/unix/ngx_solaris_config.h +++ b/src/os/unix/ngx_solaris_config.h @@ -74,6 +74,11 @@ #endif +#if (NGX_HAVE_EVENTPORT) +#include +#endif + + #ifndef NGX_HAVE_INHERITED_NONBLOCK #define NGX_HAVE_INHERITED_NONBLOCK 1 #endif diff --git a/src/os/unix/ngx_sunpro_x86.map b/src/os/unix/ngx_sunpro_x86.map new file mode 100644 --- /dev/null +++ b/src/os/unix/ngx_sunpro_x86.map @@ -0,0 +1,2 @@ +# disable { PAUSE ] hwcap for Sun Studio 11 +hwcap_1 = OVERRIDE;