changeset 13:2aba961a1d34

nginx-0.0.1-2002-09-16-19:01:44 import
author Igor Sysoev <igor@sysoev.ru>
date Mon, 16 Sep 2002 15:01:44 +0000
parents 055ed05235ae
children f8a0d0f31a24
files src/core/ngx_config_command.c src/core/ngx_config_command.h src/core/ngx_connection.h src/event/modules/ngx_select_module.c src/http/modules/ngx_http_static_handler.c src/http/ngx_http.c src/http/ngx_http.h src/http/ngx_http_core.c src/http/ngx_http_core.h src/http/ngx_http_event.c src/http/ngx_http_header_filter.c src/http/ngx_http_modules.c src/os/win32/ngx_socket.h
diffstat 13 files changed, 320 insertions(+), 146 deletions(-) [+]
line wrap: on
line diff
--- a/src/core/ngx_config_command.c
+++ b/src/core/ngx_config_command.c
@@ -12,3 +12,15 @@ char *ngx_conf_set_size_slot(char *conf,
     *(int *) (conf + offset) = size;
     return NULL;
 }
+
+char *ngx_conf_set_time_slot(char *conf, int offset, char *value)
+{
+    int size;
+
+    size = atoi(value);
+    if (size < 0)
+        return "value must be greater or equal to zero";
+
+    *(int *) (conf + offset) = size;
+    return NULL;
+}
--- a/src/core/ngx_config_command.h
+++ b/src/core/ngx_config_command.h
@@ -17,6 +17,7 @@ typedef struct {
 } ngx_command_t;
 
 char *ngx_conf_set_size_slot(char *conf, int offset, char *value);
+char *ngx_conf_set_time_slot(char *conf, int offset, char *value);
 
 
 #endif _NGX_HTTP_CONFIG_COMMAND_H_INCLUDED_
--- a/src/core/ngx_connection.h
+++ b/src/core/ngx_connection.h
@@ -3,6 +3,7 @@
 
 #include <ngx_socket.h>
 #include <ngx_log.h>
+#include <ngx_hunk.h>
 #include <ngx_alloc.h>
 #include <ngx_server.h>
 
@@ -36,6 +37,7 @@ struct ngx_connection_s {
     char             *addr_text;
     size_t            addr_textlen;
 
+    ngx_hunk_t       *buffer;
     unsigned int      post_accept_timeout;
 
     unsigned          unexpected_eof:1;
--- a/src/event/modules/ngx_select_module.c
+++ b/src/event/modules/ngx_select_module.c
@@ -186,7 +186,7 @@ int ngx_select_process_events(ngx_log_t 
     }
 #endif
 
-    ngx_log_debug(log, "ngx_select_process_events: timer: %d" _ timer);
+    ngx_log_debug(log, "select timer: %d" _ timer);
 
 #if (WIN32)
     if ((ready = select(0, &work_read_fd_set, &work_write_fd_set, NULL, tp))
@@ -195,24 +195,21 @@ int ngx_select_process_events(ngx_log_t 
                         NULL, tp))
 #endif
                == -1) {
-        ngx_log_error(NGX_LOG_ALERT, log, ngx_socket_errno,
-                     "ngx_select_process_events: select failed");
+        ngx_log_error(NGX_LOG_ALERT, log, ngx_socket_errno, "select() failed");
         return NGX_ERROR;
     }
 
-    ngx_log_debug(log, "ngx_select_process_events: ready %d" _ ready);
+    ngx_log_debug(log, "select ready %d" _ ready);
 
     if (timer) {
         delta = ngx_msec() - delta;
 
     } else {
         ngx_assert((ready != 0), return NGX_ERROR, log,
-                   "ngx_select_process_events: "
-                   "select returns no events without timeout");
+                   "select() returns no events without timeout");
     }
 
-    ngx_log_debug(log, "ngx_select_process_events: "
-                       "timer: %d, delta: %d" _ timer _ delta);
+    ngx_log_debug(log, "select timer: %d, delta: %d" _ timer _ delta);
 
     if (timer) {
         if (delta >= timer) {
@@ -234,25 +231,27 @@ int ngx_select_process_events(ngx_log_t 
         }
     }
 
-    for (ev = event_queue.next; ev != &event_queue; ev = ev->next) {
+    for (ev = event_queue.next; ev != &event_queue; /* void */) {
         c = (ngx_connection_t *) ev->data;
         found = 0;
 
         if (ev->write) {
             if (FD_ISSET(c->fd, &work_write_fd_set)) {
-                ngx_log_debug(log, "ngx_select_process_events: write %d" _
+                ngx_log_debug(log, "select write %d" _
                               c->fd);
                 found = 1;
             }
 
         } else {
             if (FD_ISSET(c->fd, &work_read_fd_set)) {
-                ngx_log_debug(log, "ngx_select_process_events: read %d" _
+                ngx_log_debug(log, "select read %d" _
                               c->fd);
                 found = 1;
             }
         }
 
+        nx = ev->next;
+
         if (found) {
             ev->ready = 1;
 
@@ -270,10 +269,10 @@ int ngx_select_process_events(ngx_log_t 
             ready--;
         }
 
+        ev = nx;
     }
 
-    ngx_assert((ready == 0), /* void */ ; , log,
-               "ngx_select_process_events: ready != events");
+    ngx_assert((ready == 0), /* void */ ; , log, "select ready != events");
 
     return NGX_OK;
 }
--- a/src/http/modules/ngx_http_static_handler.c
+++ b/src/http/modules/ngx_http_static_handler.c
@@ -65,7 +65,19 @@ int ngx_http_static_handler(ngx_http_req
 */
 
     /* STUB */
-    r->headers_out->content_type = "text/html";
+    if (r->exten) {
+        if (strcasecmp(r->exten, "html") == 0)
+            r->headers_out->content_type = "text/html; charset=koi8-r";
+        else if (strcasecmp(r->exten, "gif") == 0)
+            r->headers_out->content_type = "image/gif";
+        else if (strcasecmp(r->exten, "jpg") == 0)
+            r->headers_out->content_type = "image/jpeg";
+        else if (strcasecmp(r->exten, "pdf") == 0)
+            r->headers_out->content_type = "application/pdf";
+
+    } else {
+        r->headers_out->content_type = "text/html; charset=koi8-r";
+    }
 
     /* STUB */
     rc = ngx_http_header_filter(r);
--- a/src/http/ngx_http.c
+++ b/src/http/ngx_http.c
@@ -19,15 +19,20 @@ int ngx_http_init(ngx_pool_t *pool, ngx_
 {
     ngx_listen_t  *ls;
 
+    ngx_http_server.connection_pool_size = 16384;
     ngx_http_server.request_pool_size = 16384;
     ngx_http_server.header_timeout = 20000;
     ngx_http_server.header_buffer_size = 1024;
     ngx_http_server.discarded_buffer_size = 1500;
 
+    ngx_http_server.lingering_timeout = 5000;
+    ngx_http_server.lingering_time = 30;
+
 #if (WIN32)
     ngx_http_server.doc_root = "html";
 #else
     ngx_http_server.doc_root = "/home/is/work/xml/site-1.0.0/html";
+    ngx_http_server.doc_root = "/home/is/dox/";
 #endif
     ngx_http_server.doc_root_len = strlen(ngx_http_server.doc_root) + 1;
 
--- a/src/http/ngx_http.h
+++ b/src/http/ngx_http.h
@@ -44,22 +44,18 @@ typedef struct {
     char          *doc_root;
     size_t         doc_root_len;
 
+    size_t         connection_pool_size;
     size_t         request_pool_size;
 
     size_t         header_buffer_size;
     size_t         discarded_buffer_size;
 
-    unsigned int   header_timeout;
+    ngx_msec_t     header_timeout;
+    ngx_msec_t     lingering_timeout;
+    time_t         lingering_time;
 } ngx_http_server_t;
 
 typedef struct {
-    char *buff;
-    char *pos;
-    char *last;
-    char *end;
-} ngx_buff_t;
-
-typedef struct {
     int     status;
     int     connection;
     off_t   content_length;
@@ -105,6 +101,7 @@ struct ngx_http_request_s {
     int    http_minor;
 
     char  *uri;
+    char  *exten;
     ngx_http_request_t *main;
 
     ngx_connection_t  *connection;
@@ -118,6 +115,7 @@ struct ngx_http_request_s {
     unsigned  keepalive:1;
     unsigned  lingering_close:1;
 
+    unsigned  header_read:1;
     unsigned  process_header:1;
     unsigned  header_timeout:1;
 
new file mode 100644
--- /dev/null
+++ b/src/http/ngx_http_core.c
@@ -0,0 +1,49 @@
+
+#include <ngx_config.h>
+#include <ngx_config_command.h>
+#include <ngx_http.h>
+#include <ngx_http_core.h>
+#include <ngx_http_config.h>
+
+
+static void *ngx_http_core_create_conf(ngx_pool_t *pool);
+
+
+static ngx_command_t ngx_http_core_commands[];
+
+
+ngx_http_module_t  ngx_http_core_module = {
+    NGX_HTTP_MODULE,
+    NULL,                                  /* create server config */
+    ngx_http_core_create_conf,             /* create location config */
+    ngx_http_core_commands,                /* module directives */
+    NULL,                                  /* init module */
+    NULL                                   /* init output body filter */
+};
+
+
+static ngx_command_t ngx_http_core_commands[] = {
+
+    {"send_timeout", ngx_conf_set_time_slot,
+     offsetof(ngx_http_core_conf_t, send_timeout),
+     NGX_HTTP_LOC_CONF, NGX_CONF_TAKE1,
+     "set timeout for sending response"},
+
+    {NULL}
+
+};
+
+
+static void *ngx_http_core_create_conf(ngx_pool_t *pool)
+{
+    ngx_http_core_conf_t *conf;
+
+    ngx_test_null(conf,
+                  ngx_pcalloc(pool, sizeof(ngx_http_core_conf_t)),
+                  NULL);
+
+    conf->send_timeout = NGX_CONF_UNSET;
+
+    return conf;
+}
+
new file mode 100644
--- /dev/null
+++ b/src/http/ngx_http_core.h
@@ -0,0 +1,16 @@
+#ifndef _NGX_HTTP_CORE_H_INCLUDED_
+#define _NGX_HTTP_CORE_H_INCLUDED_
+
+
+#include <ngx_http.h>
+
+
+typedef struct {
+    time_t  send_timeout;
+} ngx_http_core_conf_t;
+
+
+extern ngx_http_module_t  ngx_http_core_module;
+
+
+#endif /* _NGX_HTTP_CORE_H_INCLUDED_ */
--- a/src/http/ngx_http_event.c
+++ b/src/http/ngx_http_event.c
@@ -1,6 +1,6 @@
 /*
-   TODO Win32 inet_ntoa
-        ngx_inet_ntop
+   TODO: Win32 inet_ntoa
+         ngx_inet_ntop
 */
 
 #include <ngx_config.h>
@@ -13,6 +13,7 @@
 #include <ngx_connection.h>
 #include <ngx_http.h>
 #include <ngx_http_config.h>
+#include <ngx_http_core.h>
 
 /* STUB */
 #include <ngx_http_output_filter.h>
@@ -20,10 +21,6 @@ int ngx_http_static_handler(ngx_http_req
 int ngx_http_index_handler(ngx_http_request_t *r);
 /* */
 
-/* STUB */
-#define LINGERING_TIMEOUT    2 
-#define SOME_LINGERING_TIME  30
-
 int ngx_http_init_connection(ngx_connection_t *c);
 
 static int ngx_http_init_request(ngx_event_t *ev);
@@ -36,10 +33,12 @@ static int ngx_http_process_request_head
 static int ngx_http_block_read(ngx_event_t *ev);
 static int ngx_http_read_discarded_body(ngx_event_t *ev);
 
+static int ngx_http_event_handler(ngx_http_request_t *r);
 static int ngx_http_handler(ngx_http_request_t *r);
 static int ngx_http_set_default_handler(ngx_http_request_t *r);
 
 static int ngx_http_writer(ngx_event_t *ev);
+static int ngx_http_set_lingering_close(ngx_http_request_t *r);
 static int ngx_http_keepalive_handler(ngx_event_t *ev);
 static int ngx_http_lingering_close(ngx_event_t *ev);
 
@@ -65,10 +64,12 @@ int ngx_http_init_connection(ngx_connect
 {
     ngx_event_t         *ev;
     struct sockaddr     *addr;
+    ngx_http_server_t   *srv;
     ngx_http_log_ctx_t  *ctx;
 
     ev = c->read;
     ev->event_handler = ngx_http_init_request;
+    srv = (ngx_http_server_t *) c->server;
 
     ngx_test_null(c->pool,
                   ngx_create_pool(srv->connection_pool_size, ev->log),
@@ -81,7 +82,8 @@ int ngx_http_init_connection(ngx_connect
     ngx_test_null(c->addr_text, ngx_palloc(c->pool, c->addr_textlen),
                   NGX_ERROR);
 #if (WIN32)
-    c->addr_text = inet_ntoa((struct in_addr) ((char *)c->sockaddr + c->addr));
+    c->addr_text = inet_ntoa((struct in_addr *)
+                                              ((char *)c->sockaddr + c->addr));
 #else
     inet_ntop(c->family, (char *)c->sockaddr + c->addr,
               c->addr_text, c->addr_textlen);
@@ -130,26 +132,27 @@ static int ngx_http_init_request(ngx_eve
     c = (ngx_connection_t *) ev->data;
     srv = (ngx_http_server_t *) c->server;
 
+    ngx_test_null(r, ngx_pcalloc(c->pool, sizeof(ngx_http_request_t)),
+                  NGX_ERROR);
+
+    c->data = r;
+    r->connection = c;
+    r->server = srv;
+
+    r->srv_conf = ngx_srv_conf;
+    r->loc_conf = ngx_loc_conf;
+
     if (c->buffer == NULL) {
         ngx_test_null(c->buffer,
                       ngx_create_temp_hunk(c->pool, srv->header_buffer_size,
                                            0, 0),
                       NGX_ERROR);
     } else {
-        c->buffer->pos.mem = c->buffer->last.mem = c->buffer->start;
+        r->header_read = 1;
     }
 
-    ngx_test_null(r, ngx_pcalloc(c->pool, sizeof(ngx_http_request_t)),
-                  NGX_ERROR);
-
-    c->data = r;
-    r->connection = c;
-    r->server = srv;
     r->header_in = c->buffer;
 
-    r->srv_conf = ngx_srv_conf;
-    r->loc_conf = ngx_loc_conf;
-
     ngx_test_null(r->pool, ngx_create_pool(srv->request_pool_size, ev->log),
                   ngx_http_close_request(r));
 
@@ -177,32 +180,39 @@ static int ngx_http_process_request(ngx_
 
     ngx_log_debug(ev->log, "http process request");
 
-    n = ngx_event_recv(c, r->header_in->last.mem,
-                       r->header_in->end - r->header_in->last.mem);
+    if (r->header_read) {
+        r->header_read = 0;
+        ngx_log_debug(ev->log, "http preread %d" _
+                      r->header_in->last.mem - r->header_in->pos.mem);
 
-    if (n == NGX_AGAIN) {
-        if (r->header_timeout) {
-            r->header_timeout = 0;
-            ngx_del_timer(ev);
-            ngx_add_timer(ev, r->server->header_timeout);
-        }
-        return NGX_AGAIN;
-    }
+    } else {
+        n = ngx_event_recv(c, r->header_in->last.mem,
+                           r->header_in->end - r->header_in->last.mem);
 
-    if (n == NGX_ERROR)
-        return ngx_http_close_request(r);
+        if (n == NGX_AGAIN) {
+            if (r->header_timeout) {
+                r->header_timeout = 0;
+                ngx_del_timer(ev);
+                ngx_add_timer(ev, r->server->header_timeout);
+            }
+            return NGX_AGAIN;
+        }
 
-    ngx_log_debug(ev->log, "http read %d" _ n);
+        if (n == NGX_ERROR)
+            return ngx_http_close_request(r);
 
-    if (n == 0) {
-        if (c->unexpected_eof)
-            ngx_log_error(NGX_LOG_INFO, c->log, 0,
-                          "client prematurely closed connection");
-        return ngx_http_close_request(r);
+        ngx_log_debug(ev->log, "http read %d" _ n);
+
+        if (n == 0) {
+            if (c->unexpected_eof)
+                ngx_log_error(NGX_LOG_INFO, c->log, 0,
+                              "client prematurely closed connection");
+            return ngx_http_close_request(r);
+        }
+
+        r->header_in->last.mem += n;
     }
 
-    r->header_in->last.mem += n;
-
     /* state_handlers are called in following order:
         ngx_http_process_request_line(r)
         ngx_http_process_request_header(r) */
@@ -241,11 +251,18 @@ static int ngx_http_process_request_line
                       ngx_http_close_request(r));
         ngx_cpystrn(r->uri, r->uri_start, r->uri_end - r->uri_start + 1);
 
-        ngx_log_debug(r->connection->log, "HTTP: %d, %d, %s" _
-                      r->method _ r->http_version _ r->uri);
+        if (r->uri_ext) {
+            ngx_test_null(r->exten,
+                          ngx_palloc(r->pool, r->uri_end - r->uri_ext + 1), 
+                          ngx_http_close_request(r));
+            ngx_cpystrn(r->exten, r->uri_ext, r->uri_end - r->uri_ext + 1);
+        }
+
+        ngx_log_debug(r->connection->log, "HTTP: %d, %d, %s %s" _
+                      r->method _ r->http_version _ r->uri _ r->exten);
 
         if (r->http_version == 9)
-            return ngx_http_handler(r);
+            return ngx_http_event_handler(r);
 
         /* TODO: check too long URI - no space for header, compact buffer */
 
@@ -292,7 +309,7 @@ static int ngx_http_process_request_head
 
         } else if (rc == NGX_HTTP_PARSE_HEADER_DONE) {
             ngx_log_debug(r->connection->log, "HTTP header done");
-            return ngx_http_handler(r);
+            return ngx_http_event_handler(r);
 
         } else if (rc == NGX_AGAIN) {
             return NGX_AGAIN;
@@ -397,29 +414,13 @@ static int ngx_http_discarded_read(ngx_e
 
 /* ******************** */
 
-static int ngx_http_handler(ngx_http_request_t *r)
+
+static int ngx_http_event_handler(ngx_http_request_t *r)
 {
-    int  rc;
+    int rc;
     ngx_msec_t  timeout;
 
-    ngx_del_timer(r->connection->read);
-    r->header_timeout = 0;
-
-    r->process_header = 0;
-    r->state_handler = NULL;
-    r->connection->unexpected_eof = 0;
-    r->lingering_close = 1;
-
-    r->connection->read->event_handler = ngx_http_block_read;
-
-    /* STUB: should find handler */
-    r->filter = NGX_HTTP_FILTER_NEED_IN_MEMORY;
-    rc = ngx_http_set_default_handler(r);
-
-    if (rc >= NGX_HTTP_SPECIAL_RESPONSE)
-        return ngx_http_special_response(r, rc);
-
-    rc = r->handler(r);
+    rc = ngx_http_handler(r);
 
     /* transfer not completed */
     if (rc == NGX_AGAIN) {
@@ -460,25 +461,47 @@ static int ngx_http_handler(ngx_http_req
 
     if (!r->keepalive) {
         if (r->lingering_close) {
-            r->lingering_time = ngx_time() + SOME_LINGERING_TIME;
-            r->connection->read->event_handler = ngx_http_lingering_close;
-            ngx_del_timer(r->connection->read);
-            ngx_add_timer(r->connection->read, LINGERING_TIMEOUT * 1000);
-            if (ngx_add_event(r->connection->read, NGX_READ_EVENT,
-                              NGX_ONESHOT_EVENT) == NGX_ERROR) {
-               return ngx_http_close_request(r);
-            }
-            if (ngx_shutdown_socket(r->connection->fd, NGX_WRITE_SHUTDOWN)
-                == NGX_ERROR)
-            {
-                ngx_log_error(NGX_LOG_ERR, r->connection->log, ngx_socket_errno,
-                              ngx_shutdown_socket_n " failed");
-                return ngx_http_close_request(r);
-            }
+            return ngx_http_set_lingering_close(r);
+
         } else {
             return ngx_http_close_request(r);
         }
     }
+
+    /* keepalive */
+
+    ngx_http_close_request(r);
+    r->connection->buffer->pos.mem = r->connection->buffer->last.mem
+                                               = r->connection->buffer->start;
+    r->connection->read->event_handler = ngx_http_keepalive_handler;
+}
+
+static int ngx_http_handler(ngx_http_request_t *r)
+{
+    int  rc;
+
+    ngx_del_timer(r->connection->read);
+    r->header_timeout = 0;
+
+    r->process_header = 0;
+    r->state_handler = NULL;
+    r->connection->unexpected_eof = 0;
+    r->lingering_close = 1;
+
+    r->connection->read->event_handler = ngx_http_block_read;
+
+    /* STUB: should find handler */
+#if 0
+    r->filter = NGX_HTTP_FILTER_NEED_IN_MEMORY;
+#endif
+    rc = ngx_http_set_default_handler(r);
+
+    if (rc >= NGX_HTTP_SPECIAL_RESPONSE)
+        return ngx_http_special_response(r, rc);
+
+    rc = r->handler(r);
+
+    return rc;
 }
 
 int ngx_http_internal_redirect(ngx_http_request_t *r, char *uri)
@@ -548,17 +571,18 @@ static int ngx_http_block_read(ngx_event
 {
     ngx_log_debug(ev->log, "http read blocked");
 
-    ngx_del_event(ev, NGX_READ_EVENT);
     ev->blocked = 1;
+    return ngx_del_event(ev, NGX_READ_EVENT);
 }
 
 
 static int ngx_http_writer(ngx_event_t *ev)
 {
     int rc;
-    unsigned int timeout;
-    ngx_connection_t    *c;
-    ngx_http_request_t  *r;
+    ngx_msec_t             timeout;
+    ngx_connection_t      *c;
+    ngx_http_request_t    *r;
+    ngx_http_core_conf_t  *conf;
 
     c = (ngx_connection_t *) ev->data;
     r = (ngx_http_request_t *) c->data;
@@ -567,14 +591,20 @@ static int ngx_http_writer(ngx_event_t *
 
     rc = ngx_http_output_filter(r, NULL);
 
-    ngx_log_debug(ev->log, "output_filter: %d" _ rc);
+    ngx_log_debug(ev->log, "output filter in writer: %d" _ rc);
 
     if (rc == NGX_AGAIN) {
 
         if (c->sent > 0) {
+            conf = (ngx_http_core_conf_t *)
+                        ngx_get_module_loc_conf(r->main ? r->main : r,
+                                                ngx_http_core_module);
+
+            timeout = (ngx_msec_t) (c->sent * conf->send_timeout);
+
             ngx_log_debug(ev->log, "sent: " QD_FMT _ c->sent);
-            timeout = (ngx_msec_t) (c->sent * 10);
             ngx_log_debug(ev->log, "timeout: %d" _ timeout);
+
             ngx_del_timer(ev);
             ngx_add_timer(ev, timeout);
         }
@@ -582,7 +612,6 @@ static int ngx_http_writer(ngx_event_t *
         if (ev->oneshot)
             if (ngx_add_event(r->connection->write, NGX_WRITE_EVENT,
                               NGX_ONESHOT_EVENT) == NGX_ERROR) {
-            /* log http request */
             return ngx_http_close_request(r);
         }
 
@@ -594,62 +623,88 @@ static int ngx_http_writer(ngx_event_t *
 
     /* rc == NGX_OK */
 
-    ngx_log_debug(ev->log, "ngx_http_writer done");
+    ngx_log_debug(ev->log, "http writer done");
 
     if (!r->keepalive) {
         if (r->lingering_close) {
-            r->lingering_time = ngx_time() + SOME_LINGERING_TIME;
-            r->connection->read->event_handler = ngx_http_lingering_close;
-            ngx_del_timer(r->connection->read);
-            ngx_add_timer(r->connection->read, LINGERING_TIMEOUT * 1000);
-            if (ngx_add_event(r->connection->read, NGX_READ_EVENT,
-                              NGX_ONESHOT_EVENT) == NGX_ERROR) {
-               return ngx_http_close_request(r);
-            }
-            if (ngx_shutdown_socket(r->connection->fd, NGX_WRITE_SHUTDOWN)
-                == NGX_ERROR)
-            {
-                ngx_log_error(NGX_LOG_ERR, r->connection->log, ngx_socket_errno,
-                              ngx_shutdown_socket_n " failed");
-                return ngx_http_close_request(r);
-            }
+            ngx_http_set_lingering_close(r);
+
         } else {
             return ngx_http_close_request(r);
         }
     }
 
     /* keepalive */
-    ev = r->connection->read;
+
     ngx_http_close_request(r);
-    ev->event_handler = ngx_http_init_request;
+    c->buffer->pos.mem = c->buffer->last.mem = c->buffer->start;
+    c->read->event_handler = ngx_http_keepalive_handler;
 }
 
-#if 0
+static int ngx_http_set_lingering_close(ngx_http_request_t *r)
+{
+    r->lingering_time = ngx_time() + r->server->lingering_time;
+    r->connection->read->event_handler = ngx_http_lingering_close;
+
+    ngx_del_timer(r->connection->read);
+    ngx_add_timer(r->connection->read, r->server->lingering_timeout);
+
+#if (HAVE_CLEAR_EVENT)
+    if (ngx_add_event(r->connection->read, NGX_READ_EVENT,
+                      NGX_CLEAR_EVENT) == NGX_ERROR) {
+#else
+    if (ngx_add_event(r->connection->read, NGX_READ_EVENT,
+                      NGX_ONESHOT_EVENT) == NGX_ERROR) {
+#endif
+       return ngx_http_close_request(r);
+    }
+
+    if (ngx_shutdown_socket(r->connection->fd, NGX_WRITE_SHUTDOWN) == NGX_ERROR)
+    {
+        ngx_log_error(NGX_LOG_ERR, r->connection->log, ngx_socket_errno,
+                      ngx_shutdown_socket_n " failed");
+        return ngx_http_close_request(r);
+    }
+
+    return NGX_OK;
+}
+
 
 static int ngx_http_keepalive_handler(ngx_event_t *ev)
 {
+    ssize_t n;
     ngx_connection_t    *c;
     ngx_http_log_ctx_t  *ctx;
 
+    c = (ngx_connection_t *) ev->data;
+
     ngx_log_debug(ev->log, "http keepalive");
 
     if (ev->timedout)
         return NGX_DONE;
 
-    if (closed)
-        /* NGX_LOG_INFO or even silent */
-        return NGX_ERROR;
+    n = ngx_event_recv(c, c->buffer->last.mem,
+                       c->buffer->end - c->buffer->last.mem);
+
+    if (n == NGX_AGAIN || n == NGX_ERROR)
+        return n;
+
+    ctx = (ngx_http_log_ctx_t *) ev->log->data;
+    ev->log->handler = NULL;
 
-    c = (ngx_connection_t *) ev->data;
+    if (n == 0) {
+        ngx_log_error(NGX_LOG_INFO, ev->log, 0,
+                      "client %s closed keepalive connection", ctx->client);
+        return NGX_DONE;
+    }
 
-    ctx = (ngx_http_log_ctx_t *) c->log->data;
+    c->buffer->last.mem += n;
+    ev->log->handler = ngx_http_log_error;
     ctx->action = "reading client request line";
-    c->log->handler = ngx_http_log_error;
 
     return ngx_http_init_request(ev);
 }
 
-#endif
 
 static int ngx_http_lingering_close(ngx_event_t *ev)
 {
@@ -666,15 +721,21 @@ static int ngx_http_lingering_close(ngx_
     if (ev->timedout)
         return NGX_DONE;
 
-    /* STUB */
     timer = r->lingering_time - ngx_time();
     if (timer <= 0)
         return NGX_DONE;
 
-    if (r->discarded_buffer == NULL)
-        ngx_test_null(r->discarded_buffer,
-                      ngx_palloc(r->pool, r->server->discarded_buffer_size),
-                      NGX_ERROR);
+    if (r->discarded_buffer == NULL) {
+        if (r->header_in->end - r->header_in->last.mem
+                                         >= r->server->discarded_buffer_size) {
+            r->discarded_buffer = r->header_in->last.mem;
+
+        } else {
+            ngx_test_null(r->discarded_buffer,
+                          ngx_palloc(c->pool, r->server->discarded_buffer_size),
+                          NGX_ERROR);
+        }
+    }
 
     n = ngx_event_recv(c, r->discarded_buffer,
                        r->server->discarded_buffer_size);
@@ -685,11 +746,12 @@ static int ngx_http_lingering_close(ngx_
     if (n == 0)
         return NGX_DONE;
 
-    if (timer > LINGERING_TIMEOUT)
-        timer = LINGERING_TIMEOUT;
+    timer *= 1000;
+    if (timer > r->server->lingering_timeout)
+        timer = r->server->lingering_timeout;
 
     ngx_del_timer(ev);
-    ngx_add_timer(ev, timer * 1000);
+    ngx_add_timer(ev, timer);
 
     return NGX_OK;
 }
@@ -700,6 +762,7 @@ static int ngx_http_special_response(ngx
     return ngx_http_error(r, error);
 }
 
+
 static int ngx_http_redirect(ngx_http_request_t *r, int redirect)
 {
     /* STUB */
@@ -709,6 +772,7 @@ static int ngx_http_redirect(ngx_http_re
     return ngx_http_close_request(r);
 }
 
+
 static int ngx_http_error(ngx_http_request_t *r, int error)
 {
     /* STUB */
--- a/src/http/ngx_http_header_filter.c
+++ b/src/http/ngx_http_header_filter.c
@@ -31,14 +31,20 @@ int ngx_http_header_filter(ngx_http_requ
 
     status = r->headers_out->status - NGX_HTTP_OK;
 
-    ngx_memcpy(h->last.mem, "HTTP/1.0 ", 9);
+    ngx_memcpy(h->last.mem, "HTTP/1.1 ", 9);
     h->last.mem += 9;
     ngx_memcpy(h->last.mem, http_codes[status].line, http_codes[status].len);
     h->last.mem += http_codes[status].len;
     *(h->last.mem++) = CR; *(h->last.mem++) = LF;
 
+#if 1
+    r->keepalive = 1;
+    ngx_memcpy(h->last.mem, "Connection: keep-alive" CRLF, 24);
+    h->last.mem += 24;
+#endif
+
 /*
-    memcpy(h->last.mem, "Date: ", 6);
+    ngx_memcpy(h->last.mem, "Date: ", 6);
     h->last.mem += 6;
     h->last.mem += ngx_http_get_time(h->last.mem, time(NULL));
     *(h->last.mem++) = CR; *(h->last.mem++) = LF;
@@ -51,17 +57,20 @@ int ngx_http_header_filter(ngx_http_requ
 
     /* check */
 
-    memcpy(h->last.mem, "Server: ", 8);
+    if (r->headers_out->content_type)
+        h->last.mem += ngx_snprintf(h->last.mem, 100, "Content-Type: %s" CRLF,
+                                    r->headers_out->content_type);
+
+    ngx_memcpy(h->last.mem, "Server: ", 8);
     h->last.mem += 8;
     if (r->headers_out->server) {
         h->last.mem = ngx_cpystrn(h->last.mem, r->headers_out->server,
                                   h->end - h->last.mem);
-
         /* check space */
 
     } else {
-        ngx_memcpy(h->last.mem, NGINX_VER, sizeof(NGINX_VER));
-        h->last.mem += sizeof(NGINX_VER);
+        ngx_memcpy(h->last.mem, NGINX_VER, sizeof(NGINX_VER) - 1);
+        h->last.mem += sizeof(NGINX_VER) - 1;
     }
     *(h->last.mem++) = CR; *(h->last.mem++) = LF;
 
--- a/src/http/ngx_http_modules.c
+++ b/src/http/ngx_http_modules.c
@@ -1,13 +1,15 @@
 
 #include <ngx_http.h>
 
+extern ngx_http_module_t ngx_http_write_filter_module;
 extern ngx_http_module_t ngx_http_output_filter_module;
-extern ngx_http_module_t ngx_http_write_filter_module;
+extern ngx_http_module_t ngx_http_core_module;
 extern ngx_http_module_t ngx_http_index_module;
 
 ngx_http_module_t *ngx_http_modules[] = {
     &ngx_http_write_filter_module,
     &ngx_http_output_filter_module,
+    &ngx_http_core_module,
     &ngx_http_index_module,
     NULL
 };
--- a/src/os/win32/ngx_socket.h
+++ b/src/os/win32/ngx_socket.h
@@ -5,6 +5,8 @@
 #include <ngx_config.h>
 #include <ngx_log.h>
 
+#define NGX_WRITE_SHUTDOWN SD_SEND
+
 #define INET_ADDRSTRLEN     16
 
 typedef SOCKET  ngx_socket_t;
@@ -19,6 +21,9 @@ void ngx_init_sockets(ngx_log_t *log);
 int ngx_nonblocking(ngx_socket_t s);
 #define ngx_nonblocking_n   "ioctlsocket(FIONBIO)"
 
+#define ngx_shutdown_socket    shutdown
+#define ngx_shutdown_socket_n  "shutdown()"
+
 #define ngx_close_socket    closesocket
 #define ngx_close_socket_n  "closesocket()"