nginx-0.1.1-RELEASE import

    *) Feature: the gzip_types directive.

    *) Feature: the tcp_nodelay directive.

    *) Feature: the send_lowat directive is working not only on OSes that
       support kqueue NOTE_LOWAT, but also on OSes that support SO_SNDLOWAT.

    *) Feature: the setproctitle() emulation for Linux and Solaris.

    *) Bugfix: the "Location" header rewrite bug fixed while the proxying.

    *) Bugfix: the ngx_http_chunked_module module may get caught in an
       endless loop.

    *) Bugfix: the /dev/poll module bugs fixed.

    *) Bugfix: the responses were corrupted when the temporary files were
       used while the proxying.

    *) Bugfix: the unescaped requests were passed to the backend.

    *) Bugfix: while the build configuration on Linux 2.4 the
       --with-poll_module parameter was required.
diff --git a/src/http/modules/ngx_http_chunked_filter.c b/src/http/modules/ngx_http_chunked_filter.c
index a71839a..2112461 100644
--- a/src/http/modules/ngx_http_chunked_filter.c
+++ b/src/http/modules/ngx_http_chunked_filter.c
@@ -83,10 +83,12 @@
 
         size += ngx_buf_size(cl->buf);
 
-        ngx_test_null(tl, ngx_alloc_chain_link(r->pool), NGX_ERROR);
-        tl->buf = cl->buf;
-        *ll = tl;
-        ll = &tl->next;
+        if (cl->buf->flush || ngx_buf_in_memory(cl->buf) || cl->buf->in_file) {
+            ngx_test_null(tl, ngx_alloc_chain_link(r->pool), NGX_ERROR);
+            tl->buf = cl->buf;
+            *ll = tl;
+            ll = &tl->next;
+        }
 
         if (cl->next == NULL) {
             break;
diff --git a/src/http/modules/ngx_http_gzip_filter.c b/src/http/modules/ngx_http_gzip_filter.c
index 72ccb0a..4089ffb 100644
--- a/src/http/modules/ngx_http_gzip_filter.c
+++ b/src/http/modules/ngx_http_gzip_filter.c
@@ -15,6 +15,8 @@
     ngx_flag_t           enable;
     ngx_flag_t           no_buffer;
 
+    ngx_array_t         *types;     /* array of ngx_http_gzip_type_t */
+
     ngx_bufs_t           bufs;
 
     ngx_uint_t           http_version;
@@ -27,6 +29,12 @@
 } ngx_http_gzip_conf_t;
 
 
+typedef struct {
+    ngx_str_t            name;
+    ngx_uint_t           enable;
+} ngx_http_gzip_type_t;
+
+
 #define NGX_HTTP_GZIP_PROXIED_OFF       0x0002
 #define NGX_HTTP_GZIP_PROXIED_EXPIRED   0x0004
 #define NGX_HTTP_GZIP_PROXIED_NO_CACHE  0x0008
@@ -57,10 +65,6 @@
     unsigned             flush:4;
     unsigned             redo:1;
     unsigned             done:1;
-#if 0
-    unsigned             pass:1;
-    unsigned             blocked:1;
-#endif
 
     size_t               zin;
     size_t               zout;
@@ -86,6 +90,8 @@
 static void *ngx_http_gzip_create_conf(ngx_conf_t *cf);
 static char *ngx_http_gzip_merge_conf(ngx_conf_t *cf,
                                       void *parent, void *child);
+static char *ngx_http_gzip_set_types(ngx_conf_t *cf, ngx_command_t *cmd,
+                                     void *conf);
 static char *ngx_http_gzip_set_window(ngx_conf_t *cf, void *post, void *data);
 static char *ngx_http_gzip_set_hash(ngx_conf_t *cf, void *post, void *data);
 
@@ -138,6 +144,13 @@
       offsetof(ngx_http_gzip_conf_t, bufs),
       NULL },
 
+    { ngx_string("gzip_types"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
+      ngx_http_gzip_set_types,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      0,
+      NULL },
+
     { ngx_string("gzip_comp_level"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
       ngx_conf_set_num_slot,
@@ -211,7 +224,7 @@
     ngx_http_gzip_filter_commands,         /* module directives */
     NGX_HTTP_MODULE,                       /* module type */
     ngx_http_gzip_filter_init,             /* init module */
-    NULL                                   /* init child */
+    NULL                                   /* init process */
 };
 
 
@@ -247,15 +260,20 @@
 
 static ngx_int_t ngx_http_gzip_header_filter(ngx_http_request_t *r)
 {
+    ngx_uint_t             i, found;
     ngx_http_gzip_ctx_t   *ctx;
     ngx_http_gzip_conf_t  *conf;
+    ngx_http_gzip_type_t  *type;
 
     conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module);
 
     if (!conf->enable
-        || r->headers_out.status != NGX_HTTP_OK
+        || (r->headers_out.status != NGX_HTTP_OK
+            && r->headers_out.status != NGX_HTTP_FORBIDDEN
+            && r->headers_out.status != NGX_HTTP_NOT_FOUND)
         || r->header_only
         || r->http_version < conf->http_version
+        || r->headers_out.content_type == NULL
         || (r->headers_out.content_encoding
             && r->headers_out.content_encoding->value.len)
         || r->headers_in.accept_encoding == NULL
@@ -267,11 +285,21 @@
         return ngx_http_next_header_filter(r);
     }
 
-    /* TODO: "text/html" -> custom types */
-    if (r->headers_out.content_type
-        && ngx_strncasecmp(r->headers_out.content_type->value.data,
-                                                          "text/html", 9) != 0)
-    {
+
+    found = 0;
+    type = conf->types->elts;
+
+    for (i = 0; i < conf->types->nelts; i++) {
+        if (r->headers_out.content_type->value.len >= type[i].name.len
+            && ngx_strncasecmp(r->headers_out.content_type->value.data, 
+                               type[i].name.data, type[i].name.len) == 0)
+        {
+            found = 1;
+            break;
+        }
+    }
+
+    if (!found) {
         return ngx_http_next_header_filter(r);
     }
 
@@ -572,15 +600,9 @@
                     ctx->bufs++;
 
                 } else {
-#if 0
-                    ctx->blocked = 1;
-#endif
                     break;
                 }
 
-#if 0
-                ctx->blocked = 0;
-#endif
                 ctx->zstream.next_out = ctx->out_buf->pos;
                 ctx->zstream.avail_out = conf->bufs.size;
             }
@@ -646,10 +668,6 @@
                 *ctx->last_out = cl;
                 ctx->last_out = &cl->next;
 
-#if 0
-                ctx->pass = 1;
-#endif
-
                 break;
             }
 
@@ -712,9 +730,6 @@
                 ctx->zstream.avail_out = 0;
 
                 ctx->done = 1;
-#if 0
-                ctx->pass = 1;
-#endif
 
                 break;
             }
@@ -725,47 +740,10 @@
                 *ctx->last_out = cl;
                 ctx->last_out = &cl->next;
 
-#if 0
-                ctx->pass = 1;
-#endif
-
                 break;
             }
         }
 
-#if 0
-
-        /* OLD CODE */
-
-        if (ctx->out) {
-            if (ctx->pass) {
-                ctx->pass = 0;
-
-            } else if (last == NGX_AGAIN) {
-                return last;
-            }
-
-        } else if (ctx->busy->buf && ngx_buf_size(ctx->busy->buf)) {
-            if (last != NGX_NONE) {
-                return last;
-            }
-
-        } else if (ctx->blocked) {
-            if (last != NGX_NONE) {
-                return last;
-            }
-
-        } else {
-            if (last == NGX_NONE) {
-                return NGX_OK;
-            }
-
-            return last;
-        }
-#endif
-
-        /* NEW CODE */
-
         if (last == NGX_AGAIN) {
             return NGX_AGAIN;
         }
@@ -774,8 +752,6 @@
             return NGX_OK;
         }
 
-        /**/
-
         last = ngx_http_next_body_filter(r, ctx->out);
 
         /*
@@ -866,7 +842,7 @@
                               (float) ctx->zin / ctx->zout);
 #endif
 
-    /* we prefer do not use FPU */
+    /* we prefer do not use the FPU */
 
     zint = (ngx_uint_t) (ctx->zin / ctx->zout);
     zfrac = (ngx_uint_t) ((ctx->zin * 100 / ctx->zout) % 100);
@@ -947,6 +923,8 @@
     conf->bufs.num = 0;
     conf->proxied = 0;
 
+    conf->types = NULL;
+
      */
 
     conf->enable = NGX_CONF_UNSET;
@@ -969,6 +947,8 @@
     ngx_http_gzip_conf_t *prev = parent;
     ngx_http_gzip_conf_t *conf = child;
 
+    ngx_http_gzip_type_t  *type;
+
     ngx_conf_merge_value(conf->enable, prev->enable, 0);
 
     ngx_conf_merge_bufs_value(conf->bufs, prev->bufs, 4, ngx_pagesize);
@@ -986,6 +966,77 @@
     ngx_conf_merge_value(conf->min_length, prev->min_length, 0);
     ngx_conf_merge_value(conf->no_buffer, prev->no_buffer, 0);
 
+    if (conf->types == NULL) {
+        if (prev->types == NULL) {
+            conf->types = ngx_array_create(cf->pool, 1,
+                                           sizeof(ngx_http_gzip_type_t));
+            if (conf->types == NULL) {
+                return NGX_CONF_ERROR;
+            }
+
+            if (!(type = ngx_array_push(conf->types))) {
+                return NGX_CONF_ERROR;
+            }
+
+            type->name.len = sizeof("text/html") - 1;
+            type->name.data = (u_char *) "text/html";
+            type->enable = 1;
+
+        } else {
+            conf->types = prev->types;
+        }
+    }
+
+    return NGX_CONF_OK;
+}
+
+
+static char *ngx_http_gzip_set_types(ngx_conf_t *cf, ngx_command_t *cmd,
+                                     void *conf)
+{
+    ngx_http_gzip_conf_t *gcf = conf;
+
+    ngx_str_t             *value;
+    ngx_uint_t             i;
+    ngx_http_gzip_type_t  *type;
+
+    if (gcf->types == NULL) {
+        gcf->types = ngx_array_create(cf->pool, 5,
+                                       sizeof(ngx_http_gzip_type_t));
+        if (gcf->types == NULL) {
+            return NGX_CONF_ERROR;
+        }
+
+        if (!(type = ngx_array_push(gcf->types))) {
+            return NGX_CONF_ERROR;
+        }
+
+        type->name.len = sizeof("text/html") - 1;
+        type->name.data = (u_char *) "text/html";
+        type->enable = 1;
+    }
+
+    value = cf->args->elts;
+
+    for (i = 1; i < cf->args->nelts; i++) {
+
+        if (ngx_strcmp(value[i].data, "text/html") == 0) {
+            continue;
+        }
+
+        if (!(type = ngx_array_push(gcf->types))) {
+            return NGX_CONF_ERROR;
+        }
+
+        type->name.len = value[i].len;
+
+        if (!(type->name.data = ngx_palloc(cf->pool, type->name.len + 1))) {
+            return NGX_CONF_ERROR;
+        }
+
+        ngx_cpystrn(type->name.data, value[i].data, type->name.len + 1);
+    }
+
     return NGX_CONF_OK;
 }
 
diff --git a/src/http/modules/ngx_http_userid_filter.c b/src/http/modules/ngx_http_userid_filter.c
index 6cbad26..5f8e452 100644
--- a/src/http/modules/ngx_http_userid_filter.c
+++ b/src/http/modules/ngx_http_userid_filter.c
@@ -257,7 +257,7 @@
             src.data = start;
             dst.data = (u_char *) ctx->uid_got;
 
-            if (ngx_decode_base64(&src, &dst) == NGX_ERROR) {
+            if (ngx_decode_base64(&dst, &src) == NGX_ERROR) {
                 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                               "client sent invalid userid cookie \"%s\"",
                               cookies[i]->value.data);
@@ -358,7 +358,7 @@
     src.data = (u_char *) ctx->uid_set;
     dst.data = p;
 
-    ngx_encode_base64(&src, &dst);
+    ngx_encode_base64(&dst, &src);
 
     p += dst.len;
 
diff --git a/src/http/modules/proxy/ngx_http_proxy_handler.c b/src/http/modules/proxy/ngx_http_proxy_handler.c
index 3fa2e0b..f0794f3 100644
--- a/src/http/modules/proxy/ngx_http_proxy_handler.c
+++ b/src/http/modules/proxy/ngx_http_proxy_handler.c
@@ -29,6 +29,11 @@
 static char *ngx_http_proxy_parse_upstream(ngx_str_t *url,
                                            ngx_http_proxy_upstream_conf_t *u);
 
+static char *ngx_http_proxy_lowat_check(ngx_conf_t *cf, void *post, void *data);
+
+static ngx_conf_post_t  ngx_http_proxy_lowat_post =
+                                               { ngx_http_proxy_lowat_check } ;
+
 
 static ngx_conf_bitmask_t  next_upstream_masks[] = {
     { ngx_string("error"), NGX_HTTP_PROXY_FT_ERROR },
@@ -79,6 +84,13 @@
       offsetof(ngx_http_proxy_loc_conf_t, send_timeout),
       NULL },
 
+    { ngx_string("proxy_send_lowat"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+      ngx_conf_set_size_slot,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      offsetof(ngx_http_proxy_loc_conf_t, send_lowat),
+      &ngx_http_proxy_lowat_post },
+
     { ngx_string("proxy_preserve_host"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
       ngx_conf_set_flag_slot,
@@ -877,6 +889,7 @@
 
     conf->connect_timeout = NGX_CONF_UNSET_MSEC;
     conf->send_timeout = NGX_CONF_UNSET_MSEC;
+    conf->send_lowat = NGX_CONF_UNSET_SIZE;
 
     conf->preserve_host = NGX_CONF_UNSET;
     conf->set_x_real_ip = NGX_CONF_UNSET;
@@ -920,6 +933,7 @@
     ngx_conf_merge_msec_value(conf->connect_timeout,
                               prev->connect_timeout, 60000);
     ngx_conf_merge_msec_value(conf->send_timeout, prev->send_timeout, 60000);
+    ngx_conf_merge_size_value(conf->send_lowat, prev->send_lowat, 0);
 
     ngx_conf_merge_value(conf->preserve_host, prev->preserve_host, 0);
     ngx_conf_merge_value(conf->set_x_real_ip, prev->set_x_real_ip, 0);
@@ -1073,17 +1087,21 @@
     value = cf->args->elts;
 
     if (ngx_strncasecmp(value[1].data, "http://", 7) != 0) {
-        return "invalid URL prefix";
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid URL prefix");
+        return NGX_CONF_ERROR;
     }
 
-    ngx_test_null(lcf->upstream,
-                  ngx_pcalloc(cf->pool, sizeof(ngx_http_proxy_upstream_conf_t)),
-                  NGX_CONF_ERROR);
+    lcf->upstream = ngx_pcalloc(cf->pool,
+                                sizeof(ngx_http_proxy_upstream_conf_t));
+    if (lcf->upstream == NULL) {
+        return NGX_CONF_ERROR;
+    }
 
     lcf->upstream->url.len = value[1].len;
     if (!(lcf->upstream->url.data = ngx_palloc(cf->pool, value[1].len + 1))) {
         return NGX_CONF_ERROR;
     }
+
     ngx_cpystrn(lcf->upstream->url.data, value[1].data, value[1].len + 1);
 
     value[1].data += 7;
@@ -1092,11 +1110,14 @@
     err = ngx_http_proxy_parse_upstream(&value[1], lcf->upstream);
 
     if (err) {
-        return err;
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, err);
+        return NGX_CONF_ERROR;
     }
 
-    ngx_test_null(host, ngx_palloc(cf->pool, lcf->upstream->host.len + 1),
-                  NGX_CONF_ERROR);
+    if (!(host = ngx_palloc(cf->pool, lcf->upstream->host.len + 1))) {
+        return NGX_CONF_ERROR;
+    }
+
     ngx_cpystrn(host, lcf->upstream->host.data, lcf->upstream->host.len + 1);
 
     /* AF_INET only */
@@ -1115,11 +1136,12 @@
 
         /* MP: ngx_shared_palloc() */
 
-        ngx_test_null(lcf->peers,
-                      ngx_pcalloc(cf->pool,
-                                  sizeof(ngx_peers_t)
-                                  + sizeof(ngx_peer_t) * (i - 1)),
-                      NGX_CONF_ERROR);
+        lcf->peers = ngx_pcalloc(cf->pool,
+                           sizeof(ngx_peers_t) + sizeof(ngx_peer_t) * (i - 1));
+
+        if (lcf->peers == NULL) {
+            return NGX_CONF_ERROR;
+        }
 
         lcf->peers->number = i;
 
@@ -1130,9 +1152,12 @@
             lcf->peers->peers[i].port = lcf->upstream->port;
 
             len = INET_ADDRSTRLEN + lcf->upstream->port_text.len + 1;
-            ngx_test_null(lcf->peers->peers[i].addr_port_text.data,
-                          ngx_palloc(cf->pool, len),
-                          NGX_CONF_ERROR);
+
+            lcf->peers->peers[i].addr_port_text.data =
+                                                     ngx_palloc(cf->pool, len);
+            if (lcf->peers->peers[i].addr_port_text.data == NULL) {
+                return NGX_CONF_ERROR;
+            }
 
             len = ngx_inet_ntop(AF_INET,
                                 &lcf->peers->peers[i].addr,
@@ -1153,8 +1178,9 @@
 
         /* MP: ngx_shared_palloc() */
 
-        ngx_test_null(lcf->peers, ngx_pcalloc(cf->pool, sizeof(ngx_peers_t)),
-                      NGX_CONF_ERROR);
+        if (!(lcf->peers = ngx_pcalloc(cf->pool, sizeof(ngx_peers_t)))) {
+            return NGX_CONF_ERROR;
+        }
 
         lcf->peers->number = 1;
 
@@ -1165,9 +1191,11 @@
 
         len = lcf->upstream->host.len + lcf->upstream->port_text.len + 1;
 
-        ngx_test_null(lcf->peers->peers[0].addr_port_text.data,
-                      ngx_palloc(cf->pool, len + 1),
-                      NGX_CONF_ERROR);
+        lcf->peers->peers[0].addr_port_text.data =
+                                                 ngx_palloc(cf->pool, len + 1);
+        if (lcf->peers->peers[0].addr_port_text.data == NULL) {
+            return NGX_CONF_ERROR;
+        }
 
         len = lcf->upstream->host.len;
 
@@ -1278,3 +1306,34 @@
 
     return "invalid port in upstream URL";
 }
+
+
+static char *ngx_http_proxy_lowat_check(ngx_conf_t *cf, void *post, void *data)
+{
+#if __FreeBSD__
+
+    ssize_t *np = data;
+
+    if (*np >= ngx_freebsd_net_inet_tcp_sendspace) {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "\"proxy_send_lowat\" must be less than %d "
+                           "(sysctl net.inet.tcp.sendspace)",
+                           ngx_freebsd_net_inet_tcp_sendspace);
+
+        return NGX_CONF_ERROR;
+    }
+
+
+#else
+
+#if 0
+    ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+                       "\"proxy_send_lowat\" is not supported, ignored");
+
+    *np = 0;
+#endif
+
+#endif
+
+    return NGX_CONF_OK;
+}
diff --git a/src/http/modules/proxy/ngx_http_proxy_handler.h b/src/http/modules/proxy/ngx_http_proxy_handler.h
index 4dcc653..728259c 100644
--- a/src/http/modules/proxy/ngx_http_proxy_handler.h
+++ b/src/http/modules/proxy/ngx_http_proxy_handler.h
@@ -54,6 +54,7 @@
 
 
 typedef struct {
+    size_t                           send_lowat;
     size_t                           header_buffer_size;
     size_t                           busy_buffers_size;
     size_t                           max_temp_file_size;
diff --git a/src/http/modules/proxy/ngx_http_proxy_header.c b/src/http/modules/proxy/ngx_http_proxy_header.c
index 0380012..07722fc 100644
--- a/src/http/modules/proxy/ngx_http_proxy_header.c
+++ b/src/http/modules/proxy/ngx_http_proxy_header.c
@@ -164,24 +164,26 @@
         return NGX_ERROR;
     }
 
-    /*
-     * we do not set r->headers_out.location to avoid the handling
-     * the local redirects without a host name by ngx_http_header_filter()
-     */
-
-#if 0
-    r->headers_out.location = location;
-#endif
-
     if (uc->url.len > loc->value.len
         || ngx_rstrncmp(loc->value.data, uc->url.data, uc->url.len) != 0)
     {
+
+       /*
+        * we do not set r->headers_out.location here to avoid the handling
+        * the local redirects without a host name by ngx_http_header_filter()
+        */
+
         *location = *loc;
         return NGX_OK;
     }
 
     /* TODO: proxy_reverse */
 
+    r->headers_out.location = location;
+
+    location->key.len = 0;
+    location->key.data = NULL;
+
     location->value.len = uc->location->len
                                           + (loc->value.len - uc->url.len) + 1;
     if (!(location->value.data = ngx_palloc(r->pool, location->value.len))) {
diff --git a/src/http/modules/proxy/ngx_http_proxy_parse.c b/src/http/modules/proxy/ngx_http_proxy_parse.c
index 3718ab0..c10cf49 100644
--- a/src/http/modules/proxy/ngx_http_proxy_parse.c
+++ b/src/http/modules/proxy/ngx_http_proxy_parse.c
@@ -155,6 +155,9 @@
             case ' ':
                 state = sw_status_text;
                 break;
+            case '.':                    /* IIS may send 403.1, 403.2, etc */
+                state = sw_status_text;
+                break;
             case CR:
                 state = sw_almost_done;
                 break;
diff --git a/src/http/modules/proxy/ngx_http_proxy_upstream.c b/src/http/modules/proxy/ngx_http_proxy_upstream.c
index c1a8fb6..be5d69a 100644
--- a/src/http/modules/proxy/ngx_http_proxy_upstream.c
+++ b/src/http/modules/proxy/ngx_http_proxy_upstream.c
@@ -115,6 +115,7 @@
 static ngx_chain_t *ngx_http_proxy_create_request(ngx_http_proxy_ctx_t *p)
 {
     size_t                           len;
+    ngx_int_t                        escape;
     ngx_uint_t                       i;
     ngx_buf_t                       *b;
     ngx_chain_t                     *chain;
@@ -133,14 +134,20 @@
         len = r->method_name.len;
     }
 
+    if (r->quoted_uri) {
+        escape = 2 * ngx_escape_uri(NULL, r->uri.data + uc->location->len,
+                                    r->uri.len - uc->location->len);
+    } else {
+        escape = 0;
+    }
+
     len += uc->uri.len
-           + r->uri.len - uc->location->len
+           + r->uri.len - uc->location->len + escape
            + 1 + r->args.len                                 /* 1 is for "?" */
            + sizeof(http_version) - 1
            + sizeof(connection_close_header) - 1
            + 2;                         /* 2 is for "\r\n" at the header end */
 
-
     if (p->lcf->preserve_host && r->headers_in.host) {
         len += sizeof(host_header) - 1
                + r->headers_in.host_name_len
@@ -218,9 +225,16 @@
 
     b->last = ngx_cpymem(b->last, uc->uri.data, uc->uri.len);
 
-    b->last = ngx_cpymem(b->last,
-                         r->uri.data + uc->location->len,
-                         r->uri.len - uc->location->len);
+    if (escape) {
+        ngx_escape_uri(b->last, r->uri.data + uc->location->len,
+                       r->uri.len - uc->location->len);
+        b->last += r->uri.len - uc->location->len + escape;
+
+    } else {
+        b->last = ngx_cpymem(b->last,
+                             r->uri.data + uc->location->len,
+                             r->uri.len - uc->location->len);
+    }
 
     if (r->args.len > 0) {
         *(b->last++) = '?';
@@ -422,7 +436,7 @@
 
     p->upstream->output_chain_ctx = output;
 
-    output->sendfile = r->sendfile;
+    output->sendfile = r->connection->sendfile;
     output->pool = r->pool;
     output->bufs.num = 1;
     output->tag = (ngx_buf_tag_t) &ngx_http_proxy_module;
@@ -737,8 +751,7 @@
     if (rc == NGX_AGAIN) {
         ngx_add_timer(c->write, p->lcf->send_timeout);
 
-        c->write->available = /* STUB: lowat */ 0;
-        if (ngx_handle_write_event(c->write, NGX_LOWAT_EVENT) == NGX_ERROR) {
+        if (ngx_handle_write_event(c->write, p->lcf->send_lowat) == NGX_ERROR) {
             ngx_http_proxy_finalize_request(p, NGX_HTTP_INTERNAL_SERVER_ERROR);
             return;
         }
@@ -1172,6 +1185,7 @@
     r = p->request;
 
     r->headers_out.status = p->upstream->status;
+    r->headers_out.status_line = p->upstream->status_line;
 
 #if 0
     r->headers_out.content_length_n = -1;
@@ -1298,11 +1312,10 @@
          */
 
         ep->cyclic_temp_file = 1;
-        r->sendfile = 0;
+        r->connection->sendfile = 0;
 
     } else {
         ep->cyclic_temp_file = 0;
-        r->sendfile = 1;
     }
 
     clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
diff --git a/src/http/ngx_http_copy_filter.c b/src/http/ngx_http_copy_filter.c
index d015c21..10c574f 100644
--- a/src/http/ngx_http_copy_filter.c
+++ b/src/http/ngx_http_copy_filter.c
@@ -79,7 +79,7 @@
         ngx_http_create_ctx(r, ctx, ngx_http_copy_filter_module,
                             sizeof(ngx_output_chain_ctx_t), NGX_ERROR);
 
-        ctx->sendfile = r->sendfile;
+        ctx->sendfile = r->connection->sendfile;
         ctx->need_in_memory = r->filter_need_in_memory;
         ctx->need_in_temp = r->filter_need_temporary;
 
diff --git a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c
index 6df6edf..58021e8 100644
--- a/src/http/ngx_http_core_module.c
+++ b/src/http/ngx_http_core_module.c
@@ -204,6 +204,13 @@
       offsetof(ngx_http_core_loc_conf_t, tcp_nopush),
       NULL },
 
+    { ngx_string("tcp_nodelay"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+      ngx_conf_set_flag_slot,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      offsetof(ngx_http_core_loc_conf_t, tcp_nodelay),
+      NULL },
+
     { ngx_string("send_timeout"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
       ngx_conf_set_msec_slot,
@@ -505,11 +512,11 @@
         r->connection->log->log_level = clcf->err_log->log_level;
     }
 
-    if (!(ngx_io.flags & NGX_IO_SENDFILE) || !clcf->sendfile) {
-        r->sendfile = 0;
+    if ((ngx_io.flags & NGX_IO_SENDFILE) && clcf->sendfile) {
+        r->connection->sendfile = 1;
 
     } else {
-        r->sendfile = 1;
+        r->connection->sendfile = 0;
     }
 
     if (!clcf->tcp_nopush) {
@@ -1387,6 +1394,7 @@
     lcf->client_body_timeout = NGX_CONF_UNSET_MSEC;
     lcf->sendfile = NGX_CONF_UNSET;
     lcf->tcp_nopush = NGX_CONF_UNSET;
+    lcf->tcp_nodelay = NGX_CONF_UNSET;
     lcf->send_timeout = NGX_CONF_UNSET_MSEC;
     lcf->send_lowat = NGX_CONF_UNSET_SIZE;
     lcf->postpone_output = NGX_CONF_UNSET_SIZE;
@@ -1477,6 +1485,7 @@
                               prev->client_body_timeout, 60000);
     ngx_conf_merge_value(conf->sendfile, prev->sendfile, 0);
     ngx_conf_merge_value(conf->tcp_nopush, prev->tcp_nopush, 0);
+    ngx_conf_merge_value(conf->tcp_nodelay, prev->tcp_nodelay, 0);
     ngx_conf_merge_msec_value(conf->send_timeout, prev->send_timeout, 60000);
     ngx_conf_merge_size_value(conf->send_lowat, prev->send_lowat, 0);
     ngx_conf_merge_size_value(conf->postpone_output, prev->postpone_output,
@@ -1795,10 +1804,10 @@
 
 static char *ngx_http_lowat_check(ngx_conf_t *cf, void *post, void *data)
 {
-#if (HAVE_LOWAT_EVENT)
-
     ssize_t *np = data;
 
+#if __FreeBSD__
+
     if (*np >= ngx_freebsd_net_inet_tcp_sendspace) {
         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                            "\"send_lowat\" must be less than %d "
@@ -1808,11 +1817,13 @@
         return NGX_CONF_ERROR;
     }
 
-#else
+#elif !(HAVE_SO_SNDLOWAT)
 
     ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
                        "\"send_lowat\" is not supported, ignored");
 
+    *np = 0;
+
 #endif
 
     return NGX_CONF_OK;
diff --git a/src/http/ngx_http_core_module.h b/src/http/ngx_http_core_module.h
index 7468db5..787f8b8 100644
--- a/src/http/ngx_http_core_module.h
+++ b/src/http/ngx_http_core_module.h
@@ -172,6 +172,7 @@
 
     ngx_flag_t    sendfile;                /* sendfile */
     ngx_flag_t    tcp_nopush;              /* tcp_nopush */
+    ngx_flag_t    tcp_nodelay;             /* tcp_nodelay */
     ngx_flag_t    reset_timedout_connection; /* reset_timedout_connection */
     ngx_flag_t    msie_padding;            /* msie_padding */
 
diff --git a/src/http/ngx_http_header_filter.c b/src/http/ngx_http_header_filter.c
index 9876a05..5b7f3eb 100644
--- a/src/http/ngx_http_header_filter.c
+++ b/src/http/ngx_http_header_filter.c
@@ -150,6 +150,7 @@
     len = sizeof("HTTP/1.x ") - 1 + 2 + 2;
 
     /* status line */
+
     if (r->headers_out.status_line.len) {
         len += r->headers_out.status_line.len;
 #if (NGX_SUPPRESS_WARN)
diff --git a/src/http/ngx_http_parse.c b/src/http/ngx_http_parse.c
index ba77ffb..e70a6ea 100644
--- a/src/http/ngx_http_parse.c
+++ b/src/http/ngx_http_parse.c
@@ -217,10 +217,13 @@
                 state = sw_http_09;
                 break;
             case '.':
-            case '%':
                 r->complex_uri = 1;
                 state = sw_uri;
                 break;
+            case '%':
+                r->quoted_uri = 1;
+                state = sw_uri;
+                break;
             case '/':
                 r->complex_uri = 1;
                 break;
@@ -259,7 +262,7 @@
                 state = sw_after_slash_in_uri;
                 break;
             case '%':
-                r->complex_uri = 1;
+                r->quoted_uri = 1;
                 state = sw_uri;
                 break;
             case '?':
@@ -522,7 +525,7 @@
                 break;
             }
 
-            /* IIS can send duplicate "HTTP/1.1 ..." lines */
+            /* IIS may send the duplicate "HTTP/1.1 ..." lines */
             if (ch == '/'
                 && r->proxy
                 && p - r->header_start == 5
diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c
index e889449..6069847 100644
--- a/src/http/ngx_http_request.c
+++ b/src/http/ngx_http_request.c
@@ -550,7 +550,7 @@
                 return;
             }
 
-            if (r->complex_uri) {
+            if (r->complex_uri || r->quoted_uri) {
                 rc = ngx_http_parse_complex_uri(r);
 
                 if (rc == NGX_HTTP_INTERNAL_SERVER_ERROR) {
@@ -1318,8 +1318,7 @@
         ngx_add_timer(wev, clcf->send_timeout);
     }
 
-    wev->available = clcf->send_lowat;
-    if (ngx_handle_write_event(wev, NGX_LOWAT_EVENT) == NGX_ERROR) {
+    if (ngx_handle_write_event(wev, clcf->send_lowat) == NGX_ERROR) {
         ngx_http_close_request(r, 0);
         ngx_http_close_connection(r->connection);
     }
@@ -1354,9 +1353,7 @@
                                                 ngx_http_core_module);
             ngx_add_timer(wev, clcf->send_timeout);
 
-            wev->available = clcf->send_lowat;
-
-            if (ngx_handle_write_event(wev, NGX_LOWAT_EVENT) == NGX_ERROR) {
+            if (ngx_handle_write_event(wev, clcf->send_lowat) == NGX_ERROR) {
                 ngx_http_close_request(r, 0);
                 ngx_http_close_connection(r->connection);
             }
@@ -1371,9 +1368,8 @@
 
             clcf = ngx_http_get_module_loc_conf(r->main ? r->main : r,
                                                 ngx_http_core_module);
-            wev->available = clcf->send_lowat;
 
-            if (ngx_handle_write_event(wev, NGX_LOWAT_EVENT) == NGX_ERROR) {
+            if (ngx_handle_write_event(wev, clcf->send_lowat) == NGX_ERROR) {
                 ngx_http_close_request(r, 0);
                 ngx_http_close_connection(r->connection);
             }
@@ -1394,9 +1390,7 @@
             ngx_add_timer(wev, clcf->send_timeout);
         }
 
-        wev->available = clcf->send_lowat;
-
-        if (ngx_handle_write_event(wev, NGX_LOWAT_EVENT) == NGX_ERROR) {
+        if (ngx_handle_write_event(wev, clcf->send_lowat) == NGX_ERROR) {
             ngx_http_close_request(r, 0);
             ngx_http_close_connection(r->connection);
         }
@@ -1541,6 +1535,7 @@
 
 static void ngx_http_set_keepalive(ngx_http_request_t *r)
 {
+    int                        tcp_nodelay;
     ngx_int_t                  i;
     ngx_buf_t                 *b, *f;
     ngx_event_t               *rev, *wev;
@@ -1684,7 +1679,26 @@
             ngx_http_close_connection(c);
             return;
         }
+
         c->tcp_nopush = NGX_TCP_NOPUSH_UNSET;
+
+    } else {
+        if (clcf->tcp_nodelay && !c->tcp_nodelay) {
+            tcp_nodelay = 1;
+
+            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "tcp_nodelay");
+
+            if (setsockopt(c->fd, IPPROTO_TCP, TCP_NODELAY,
+                               (const void *) &tcp_nodelay, sizeof(int)) == -1)
+            {
+                ngx_connection_error(c, ngx_socket_errno,
+                                     "setsockopt(TCP_NODELAY) failed");
+                ngx_http_close_connection(c);
+                return;
+            }
+
+            c->tcp_nodelay = 1;
+        }
     }
 
 #if 0
@@ -2055,6 +2069,18 @@
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
                    "close http connection: %d", c->fd);
 
+#if (NGX_OPENSSL)
+
+    if (c->ssl) {
+        if (ngx_ssl_shutdown(c) == NGX_AGAIN) {
+            c->read->event_handler = ngx_ssl_close_handler;
+            c->write->event_handler = ngx_ssl_close_handler;
+            return;
+        }
+    }
+
+#endif
+
 #if (NGX_STAT_STUB)
     (*ngx_stat_active)--;
 #endif
diff --git a/src/http/ngx_http_request.h b/src/http/ngx_http_request.h
index ef169f5..022b8a8 100644
--- a/src/http/ngx_http_request.h
+++ b/src/http/ngx_http_request.h
@@ -307,8 +307,10 @@
     /* URI is not started with '/' - "GET http://" */
     unsigned             unusual_uri:1;
 #endif
-    /* URI with "/.", "%" and on Win32 with "//" */
+    /* URI with "/." and on Win32 with "//" */
     unsigned             complex_uri:1;
+    /* URI with "%" */
+    unsigned             quoted_uri:1;
     unsigned             header_timeout_set:1;
 
     unsigned             proxy:1;
@@ -320,9 +322,6 @@
 #endif
     unsigned             pipeline:1;
 
-    /* can we use sendfile ? */
-    unsigned             sendfile:1;
-
     unsigned             plain_http:1;
     unsigned             chunked:1;
     unsigned             header_only:1;