nginx-0.3.10-RELEASE import

    *) Change: the "valid_referers" directive and the "$invalid_referer"
       variable were moved to the new ngx_http_referer_module from the
       ngx_http_rewrite_module.

    *) Change: the "$apache_bytes_sent" variable name was changed to
       "$body_bytes_sent".

    *) Feature: the "$sent_http_..." variables.

    *) Feature: the "if" directive supports the "=" and "!=" operations.

    *) Feature: the "proxy_pass" directive supports the HTTPS protocol.

    *) Feature: the "proxy_set_body" directive.

    *) Feature: the "post_action" directive.

    *) Feature: the ngx_http_empty_gif_module.

    *) Feature: the "worker_cpu_affinity" directive for Linux.

    *) Bugfix: the "rewrite" directive did not unescape URI part in
       redirect, now it is unescaped except the %00-%25 and %7F-%FF
       characters.

    *) Bugfix: nginx could not be built by the icc 9.0 compiler.

    *) Bugfix: if the SSI was enabled for zero size static file, then the
       chunked response was encoded incorrectly.
diff --git a/src/core/nginx.c b/src/core/nginx.c
index e08ca16..cd15514 100644
--- a/src/core/nginx.c
+++ b/src/core/nginx.c
@@ -17,13 +17,15 @@
 static char *ngx_core_module_init_conf(ngx_cycle_t *cycle, void *conf);
 static char *ngx_set_user(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
 static char *ngx_set_priority(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
+static char *ngx_set_cpu_affinity(ngx_conf_t *cf, ngx_command_t *cmd,
+    void *conf);
 
 
 static ngx_conf_enum_t  ngx_debug_points[] = {
     { ngx_string("stop"), NGX_DEBUG_POINTS_STOP },
     { ngx_string("abort"), NGX_DEBUG_POINTS_ABORT },
     { ngx_null_string, 0 }
-};  
+};
 
 
 static ngx_command_t  ngx_core_commands[] = {
@@ -84,6 +86,13 @@
       0,
       NULL },
 
+    { ngx_string("worker_cpu_affinity"),
+      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_1MORE,
+      ngx_set_cpu_affinity,
+      0,
+      0,
+      NULL },
+
     { ngx_string("worker_rlimit_nofile"),
       NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
       ngx_conf_set_num_slot,
@@ -537,6 +546,8 @@
      *     ccf->pid = NULL;
      *     ccf->oldpid = NULL;
      *     ccf->priority = 0;
+     *     ccf->cpu_affinity_n = 0;
+     *     ccf->cpu_affinity = NULL;
      */
 
     ccf->daemon = NGX_CONF_UNSET;
@@ -578,10 +589,26 @@
     ngx_conf_init_value(ccf->worker_processes, 1);
     ngx_conf_init_value(ccf->debug_points, 0);
 
+#if (NGX_HAVE_SCHED_SETAFFINITY)
+
+    if (ccf->cpu_affinity_n
+        && ccf->cpu_affinity_n != 1
+        && ccf->cpu_affinity_n != (ngx_uint_t) ccf->worker_processes)
+    {
+        ngx_log_error(NGX_LOG_WARN, cycle->log, 0,
+                      "number of the \"worker_processes\" is not equal to "
+                      "the number of the \"worker_cpu_affinity\" mask, "
+                      "using last mask for remaining worker processes");
+    }
+
+#endif
+
 #if (NGX_THREADS)
+
     ngx_conf_init_value(ccf->worker_threads, 0);
     ngx_threads_n = ccf->worker_threads;
     ngx_conf_init_size_value(ccf->thread_stack_size, 2 * 1024 * 1024);
+
 #endif
 
 #if !(NGX_WIN32)
@@ -732,3 +759,95 @@
 
     return NGX_CONF_OK;
 }
+
+
+static char *
+ngx_set_cpu_affinity(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+#if (NGX_HAVE_SCHED_SETAFFINITY)
+    ngx_core_conf_t  *ccf = conf;
+
+    u_char            ch;
+    u_long           *mask;
+    ngx_str_t        *value;
+    ngx_uint_t        i, n;
+
+    if (ccf->cpu_affinity) {
+        return "is duplicate";
+    }
+
+    mask = ngx_palloc(cf->pool, (cf->args->nelts - 1) * sizeof(long));
+    if (mask == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    ccf->cpu_affinity_n = cf->args->nelts - 1;
+    ccf->cpu_affinity = mask;
+
+    value = cf->args->elts;
+
+    for (n = 1; n < cf->args->nelts; n++) {
+
+        if (value[n].len > 32) {
+            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                         "\"worker_cpu_affinity\" supports up to 32 CPU only");
+            return NGX_CONF_ERROR;
+        }
+
+        mask[n - 1] = 0;
+
+        for (i = 0; i < value[n].len; i++) {
+
+            ch = value[n].data[i];
+
+            if (ch == ' ') {
+                continue;
+            }
+
+            mask[n - 1] <<= 1;
+
+            if (ch == '0') {
+                continue;
+            }
+
+            if (ch == '1') {
+                mask[n - 1] |= 1;
+                continue;
+            }
+
+            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                          "invalid character \"%c\" in \"worker_cpu_affinity\"",
+                          ch);
+            return NGX_CONF_ERROR ;
+        }
+    }
+
+#else
+
+    ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+                       "\"worker_cpu_affinity\" is not supported "
+                       "on this platform, ignored");
+#endif
+
+    return NGX_CONF_OK;
+}
+
+
+u_long
+ngx_get_cpu_affinity(ngx_uint_t n)
+{
+    ngx_core_conf_t  *ccf;
+
+    ccf = (ngx_core_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx,
+                                           ngx_core_module);
+
+    if (ccf->cpu_affinity == NULL) {
+        return 0;
+    }
+
+    if (ccf->cpu_affinity_n > n) {
+        return ccf->cpu_affinity[n];
+    }
+
+    return ccf->cpu_affinity[ccf->cpu_affinity_n - 1];
+}
diff --git a/src/core/nginx.h b/src/core/nginx.h
index c498e07..fc5d926 100644
--- a/src/core/nginx.h
+++ b/src/core/nginx.h
@@ -8,7 +8,7 @@
 #define _NGINX_H_INCLUDED_
 
 
-#define NGINX_VER          "nginx/0.3.9"
+#define NGINX_VER          "nginx/0.3.10"
 
 #define NGINX_VAR          "NGINX"
 #define NGX_OLDPID_EXT     ".oldbin"
diff --git a/src/core/ngx_conf_file.h b/src/core/ngx_conf_file.h
index adf203a..2ef595d 100644
--- a/src/core/ngx_conf_file.h
+++ b/src/core/ngx_conf_file.h
@@ -147,7 +147,7 @@
     ngx_str_t             name;
     void               *(*create_conf)(ngx_cycle_t *cycle);
     char               *(*init_conf)(ngx_cycle_t *cycle, void *conf);
-} ngx_core_module_t; 
+} ngx_core_module_t;
 
 
 typedef struct {
diff --git a/src/core/ngx_connection.c b/src/core/ngx_connection.c
index 2e34669..8a8f84a 100644
--- a/src/core/ngx_connection.c
+++ b/src/core/ngx_connection.c
@@ -467,7 +467,7 @@
             }
         }
 
-        if (ls->add_deferred) { 
+        if (ls->add_deferred) {
             ls->deferred_accept = 1;
         }
 
@@ -625,11 +625,11 @@
     if (c->read->timer_set) {
         ngx_del_timer(c->read);
     }
-    
+
     if (c->write->timer_set) {
         ngx_del_timer(c->write);
     }
-    
+
     if (ngx_del_conn) {
         ngx_del_conn(c, NGX_CLOSE_EVENT);
 
diff --git a/src/core/ngx_connection.h b/src/core/ngx_connection.h
index c2b77e0..279efb6 100644
--- a/src/core/ngx_connection.h
+++ b/src/core/ngx_connection.h
@@ -102,6 +102,7 @@
 
     ngx_recv_pt         recv;
     ngx_send_pt         send;
+    ngx_recv_chain_pt   recv_chain;
     ngx_send_chain_pt   send_chain;
 
     ngx_listening_t    *listening;
@@ -120,7 +121,7 @@
     socklen_t           socklen;
     ngx_str_t           addr_text;
 
-#if (NGX_OPENSSL)
+#if (NGX_SSL)
     ngx_ssl_connection_t  *ssl;
 #endif
 
diff --git a/src/core/ngx_core.h b/src/core/ngx_core.h
index 9efbecd..38395cb 100644
--- a/src/core/ngx_core.h
+++ b/src/core/ngx_core.h
@@ -12,7 +12,7 @@
 typedef struct ngx_conf_s        ngx_conf_t;
 typedef struct ngx_cycle_s       ngx_cycle_t;
 typedef struct ngx_pool_s        ngx_pool_t;
-typedef struct ngx_chain_s       ngx_chain_t; 
+typedef struct ngx_chain_s       ngx_chain_t;
 typedef struct ngx_log_s         ngx_log_t;
 typedef struct ngx_array_s       ngx_array_t;
 typedef struct ngx_open_file_s   ngx_open_file_t;
diff --git a/src/core/ngx_cycle.c b/src/core/ngx_cycle.c
index ab19611..da43036 100644
--- a/src/core/ngx_cycle.c
+++ b/src/core/ngx_cycle.c
@@ -615,7 +615,7 @@
 
         /*
          * do not create the pid file in the first ngx_init_cycle() call
-         * because we need to write the demonized process pid 
+         * because we need to write the demonized process pid
          */
 
         return NGX_OK;
@@ -674,7 +674,7 @@
 
 void
 ngx_delete_pidfile(ngx_cycle_t *cycle)
-{   
+{
     u_char           *name;
     ngx_core_conf_t  *ccf;
 
diff --git a/src/core/ngx_cycle.h b/src/core/ngx_cycle.h
index b297994..fc04e9c 100644
--- a/src/core/ngx_cycle.h
+++ b/src/core/ngx_cycle.h
@@ -64,6 +64,9 @@
 
      int                      priority;
 
+     ngx_uint_t               cpu_affinity_n;
+     u_long                  *cpu_affinity;
+
      char                    *username;
      ngx_uid_t                user;
      ngx_gid_t                group;
@@ -91,6 +94,7 @@
 void ngx_delete_pidfile(ngx_cycle_t *cycle);
 void ngx_reopen_files(ngx_cycle_t *cycle, ngx_uid_t user);
 ngx_pid_t ngx_exec_new_binary(ngx_cycle_t *cycle, char *const *argv);
+u_long ngx_get_cpu_affinity(ngx_uint_t n);
 
 
 extern volatile ngx_cycle_t  *ngx_cycle;
diff --git a/src/core/ngx_inet.c b/src/core/ngx_inet.c
index 4be55fd..d44e300 100644
--- a/src/core/ngx_inet.c
+++ b/src/core/ngx_inet.c
@@ -326,7 +326,7 @@
             peers->peer[i].socklen = sizeof(struct sockaddr_in);
 
             len = INET_ADDRSTRLEN - 1 + 1 + u->port_text.len;
-    
+
             peers->peer[i].name.data = ngx_palloc(cf->pool, len);
             if (peers->peer[i].name.data == NULL) {
                 return NULL;
diff --git a/src/core/ngx_log.c b/src/core/ngx_log.c
index 444e943..6cbedde 100644
--- a/src/core/ngx_log.c
+++ b/src/core/ngx_log.c
@@ -26,7 +26,7 @@
 
 static ngx_core_module_t  ngx_errlog_module_ctx = {
     ngx_string("errlog"),
-    NULL,                           
+    NULL,
     NULL
 };
 
diff --git a/src/core/ngx_output_chain.c b/src/core/ngx_output_chain.c
index 03d956d..f2dab9c 100644
--- a/src/core/ngx_output_chain.c
+++ b/src/core/ngx_output_chain.c
@@ -473,7 +473,8 @@
         return NGX_OK;
     }
 
-    ctx->out = ngx_send_chain(ctx->connection, ctx->out, ctx->limit);
+    ctx->out = ctx->connection->send_chain(ctx->connection, ctx->out,
+                                           ctx->limit);
 
     ngx_log_debug1(NGX_LOG_DEBUG_CORE, ctx->connection->log, 0,
                    "chain writer out: %p", ctx->out);
diff --git a/src/core/ngx_string.c b/src/core/ngx_string.c
index a236654..fc37382 100644
--- a/src/core/ngx_string.c
+++ b/src/core/ngx_string.c
@@ -928,3 +928,111 @@
 
     return (uintptr_t) dst;
 }
+
+
+void
+ngx_unescape_uri(u_char **dst, u_char **src, size_t size)
+{
+    u_char  *d, *s, ch, c, decoded;
+    enum {
+        sw_usual = 0,
+        sw_quoted,
+        sw_quoted_second
+    } state;
+
+    d = *dst;
+    s = *src;
+
+    state = 0;
+    decoded = 0;
+
+    while (size--) {
+
+        ch = *s++;
+
+        switch (state) {
+        case sw_usual:
+            if (ch == '?') {
+                *d++ = ch;
+                goto done;
+            }
+
+            if (ch == '%') {
+                state = sw_quoted;
+                break;
+            }
+
+            *d++ = ch;
+            break;
+
+        case sw_quoted:
+
+            if (ch >= '0' && ch <= '9') {
+                decoded = (u_char) (ch - '0');
+                state = sw_quoted_second;
+                break;
+            }
+
+            c = (u_char) (ch | 0x20);
+            if (c >= 'a' && c <= 'f') {
+                decoded = (u_char) (c - 'a' + 10);
+                state = sw_quoted_second;
+                break;
+            }
+
+            /* skip the invalid quoted character */
+
+            s++;
+            size--;
+
+            break;
+
+        case sw_quoted_second:
+
+            if (ch >= '0' && ch <= '9') {
+                ch = (u_char) ((decoded << 4) + ch - '0');
+
+                state = sw_usual;
+
+                if (ch > '%' && ch < 0x7f) {
+                    *d++ = ch;
+                    break;
+                }
+
+                *d++ = '%'; *d++ = *(s - 2); *d++ = *(s - 1);
+
+                break;
+            }
+
+            c = (u_char) (ch | 0x20);
+            if (c >= 'a' && c <= 'f') {
+                ch = (u_char) ((decoded << 4) + c - 'a' + 10);
+
+                if (ch == '?') {
+                    *d++ = ch;
+                    goto done;
+                }
+
+                state = sw_usual;
+
+                if (ch > '%' && ch < 0x7f) {
+                    *d++ = ch;
+                    break;
+                }
+
+                *d++ = '%'; *d++ = *(s - 2); *d++ = *(s - 1);
+
+                break;
+            }
+
+            /* skip the invalid quoted character */
+
+            break;
+        }
+    }
+
+done:
+
+    *dst = d;
+    *src = s;
+}
diff --git a/src/core/ngx_string.h b/src/core/ngx_string.h
index b019e33..0bee98b 100644
--- a/src/core/ngx_string.h
+++ b/src/core/ngx_string.h
@@ -141,6 +141,7 @@
 
 uintptr_t ngx_escape_uri(u_char *dst, u_char *src, size_t size,
     ngx_uint_t type);
+void ngx_unescape_uri(u_char **dst, u_char **src, size_t size);
 
 
 #define  ngx_qsort                qsort