changeset 236:c982febb7588 NGINX_0_4_3

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.
author Igor Sysoev <http://sysoev.ru>
date Tue, 26 Sep 2006 00:00:00 +0400
parents f622c719b711
children 302a8e8b4ae7
files CHANGES CHANGES.ru auto/cc/name auto/cc/sunc auto/endianess auto/feature auto/modules auto/options auto/os/solaris auto/sources conf/nginx.conf src/core/nginx.h src/core/ngx_inet.c src/event/modules/ngx_eventport_module.c src/event/modules/ngx_poll_module.c src/event/modules/ngx_select_module.c src/event/ngx_event.c src/event/ngx_event.h src/event/ngx_event_accept.c src/http/modules/ngx_http_browser_module.c src/http/modules/ngx_http_headers_filter_module.c src/http/modules/ngx_http_ssi_filter_module.c src/http/ngx_http_core_module.c src/http/ngx_http_request.c src/http/ngx_http_special_response.c src/os/unix/ngx_process.c src/os/unix/ngx_shmem.h src/os/unix/ngx_socket.c src/os/unix/ngx_solaris_config.h src/os/unix/ngx_sunpro_x86.map
diffstat 30 files changed, 1498 insertions(+), 63 deletions(-) [+]
line wrap: on
line diff
--- 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 
--- 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; ошибка 
--- 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"
 
--- 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"
     ;;
 
--- 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
--- 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
--- 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"
--- 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 ;;
--- 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 <port.h>"
+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
--- 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"
--- 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;
--- 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"
--- 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;
new file mode 100644
--- /dev/null
+++ b/src/event/modules/ngx_eventport_module.c
@@ -0,0 +1,593 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+#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;
+}
--- 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;
 }
 
--- 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;
--- 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;
                 }
--- 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;
--- 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;
new file mode 100644
--- /dev/null
+++ b/src/http/modules/ngx_http_browser_module.c
@@ -0,0 +1,706 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+/*
+ * 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;
+}
--- 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;
-
                 }
             }
         }
--- 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;
--- 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;
--- 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;
     }
 
--- 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;
 
--- 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.
                  */
 
--- 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 <ngx_config.h>
@@ -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_ */
--- 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.
--- 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 <port.h>
+#endif
+
+
 #ifndef NGX_HAVE_INHERITED_NONBLOCK
 #define NGX_HAVE_INHERITED_NONBLOCK  1
 #endif
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;