changeset 97:70d2345a903f

nginx-0.0.1-2003-05-29-17:02:09 import
author Igor Sysoev <igor@sysoev.ru>
date Thu, 29 May 2003 13:02:09 +0000
parents a23d010f356d
children c9b243802a17
files src/core/nginx.c src/core/nginx.h src/core/ngx_string.h src/event/ngx_event.c src/event/ngx_event.h src/event/ngx_event_accept.c src/http/modules/ngx_http_index_handler.c src/http/ngx_http_cache.c src/http/ngx_http_core_module.c src/http/ngx_http_core_module.h src/os/unix/ngx_errno.h src/os/unix/ngx_freebsd_init.c src/os/unix/ngx_linux_config.h src/os/unix/ngx_linux_sendfile_chain.c src/os/unix/ngx_solaris_config.h
diffstat 15 files changed, 519 insertions(+), 112 deletions(-) [+]
line wrap: on
line diff
--- a/src/core/nginx.c
+++ b/src/core/nginx.c
@@ -90,8 +90,8 @@ int main(int argc, char *const *argv)
         conf.module_type = NGX_CORE_MODULE;
         conf.cmd_type = NGX_MAIN_CONF;
 
-        conf_file.len = sizeof("nginx.conf") - 1;
-        conf_file.data = "nginx.conf";
+        conf_file.len = sizeof(NGINX_CONF) - 1;
+        conf_file.data = NGINX_CONF;
 
         if (ngx_conf_parse(&conf, &conf_file) != NGX_CONF_OK) {
             return 1;
--- a/src/core/nginx.h
+++ b/src/core/nginx.h
@@ -2,7 +2,8 @@
 #define _NGINX_H_INCLUDED_
 
 
-#define  NGINX_VER  "nginx/0.0.1"
+#define  NGINX_VER   "nginx/0.0.1"
+#define  NGINX_CONF  "nginx.conf"
 
 
 extern int ngx_max_module;
--- a/src/core/ngx_string.h
+++ b/src/core/ngx_string.h
@@ -52,9 +52,11 @@ char *ngx_cpystrn(char *dst, char *src, 
 int ngx_rstrncmp(char *s1, char *s2, size_t n);
 int ngx_atoi(char *line, size_t n);
 
+#define  ngx_qsort                qsort
 
-#define  ngx_value_helper(n)   #n
-#define  ngx_value(n)          ngx_value_helper(n)
+
+#define  ngx_value_helper(n)      #n
+#define  ngx_value(n)             ngx_value_helper(n)
 
 
 #endif /* _NGX_STRING_H_INCLUDED_ */
--- a/src/event/ngx_event.c
+++ b/src/event/ngx_event.c
@@ -9,7 +9,7 @@
 #define DEF_CONNECTIONS  1024
 
 
-extern ngx_event_module_t ngx_select_module_ctx;
+extern ngx_module_t ngx_select_module;
 
 #if (HAVE_KQUEUE)
 #include <ngx_kqueue_module.h>
@@ -122,12 +122,11 @@ ngx_module_t  ngx_event_module = {
 
 int ngx_pre_thread(ngx_array_t *ls, ngx_pool_t *pool, ngx_log_t *log)
 {
-    int  m, i, fd;
-
-    ngx_listen_t      *s;
-    ngx_event_t       *ev;
-    ngx_connection_t  *c;
-    ngx_event_conf_t  *ecf;
+    int                  m, i, fd;
+    ngx_listen_t        *s;
+    ngx_event_t         *ev;
+    ngx_connection_t    *c;
+    ngx_event_conf_t    *ecf;
     ngx_event_module_t  *module;
 
     ecf = ngx_event_get_conf(ngx_event_module);
--- a/src/event/ngx_event.h
+++ b/src/event/ngx_event.h
@@ -90,9 +90,7 @@ struct ngx_event_s {
     unsigned         ignore_econnreset:1;
     unsigned         unexpected_eof:1;
 
-#if (HAVE_DEFERRED_ACCEPT)
     unsigned         deferred_accept:1;
-#endif
 
 #if (HAVE_KQUEUE)
     unsigned         eof:1;
@@ -277,6 +275,7 @@ typedef struct {
 #define ngx_process_events   ngx_event_actions.process
 #define ngx_add_event        ngx_event_actions.add
 #define ngx_del_event        ngx_event_actions.del
+#define ngx_add_conn         ngx_event_actions.add_conn
 #define ngx_del_conn         ngx_event_actions.del_conn
 
 #if 0
--- a/src/event/ngx_event_accept.c
+++ b/src/event/ngx_event_accept.c
@@ -1,11 +1,9 @@
+
+#include <ngx_config.h>
+#include <ngx_core.h>
 
 #include <nginx.h>
 
-#include <ngx_config.h>
-#include <ngx_core.h>
-#include <ngx_types.h>
-#include <ngx_log.h>
-#include <ngx_connection.h>
 #include <ngx_event.h>
 
 
@@ -19,18 +17,17 @@ void ngx_event_accept(ngx_event_t *ev)
     ngx_socket_t       s;
     ngx_event_t       *rev, *wev;
     ngx_connection_t  *c, *ls;
+    ngx_event_conf_t  *ecf;
 
-    ls = (ngx_connection_t *) ev->data;
+    ecf = ngx_event_get_conf(ngx_event_module);
+
+    ls = ev->data;
 
     ngx_log_debug(ev->log, "ngx_event_accept: accept ready: %d" _
                   ev->available);
 
     ev->ready = 0;
 
-#if 0
-/* DEBUG */ ev->available++;
-#endif
-
     do {
 
         /* Create the pool before accept() to avoid copy the sockaddr.
@@ -52,16 +49,37 @@ void ngx_event_accept(ngx_event_t *ev)
         s = accept(ls->fd, sa, &len);
         if (s == -1) {
             err = ngx_socket_errno;
-            ngx_destroy_pool(pool);
 
             if (err == NGX_EAGAIN) {
                 ngx_log_error(NGX_LOG_NOTICE, ev->log, err,
-                              "EAGAIN while accept %s", ls->addr_text.data);
+                              "EAGAIN while accept() %s", ls->addr_text.data);
                 return;
             }
 
             ngx_log_error(NGX_LOG_ALERT, ev->log, err,
-                          "accept %s failed", ls->addr_text.data);
+                          "accept() %s failed", ls->addr_text.data);
+
+            ngx_destroy_pool(pool);
+            return;
+        }
+
+        if (s >= ecf->connections) {
+
+            ngx_log_error(NGX_LOG_ALERT, ev->log, 0,
+                          "accept() %s returned socket #%d while "
+                          "only %d connections was configured, "
+                          "sleeping for 1 second",
+                          ls->addr_text.data, s, ecf->connections);
+
+            if (ngx_close_socket(s) == -1) {
+                ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_socket_errno,
+                              ngx_close_socket_n " %s failed",
+                              ls->addr_text.data);
+            }
+
+            sleep(1);
+
+            ngx_destroy_pool(pool);
             return;
         }
 
@@ -73,6 +91,14 @@ void ngx_event_accept(ngx_event_t *ev)
                     ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_socket_errno,
                                   ngx_blocking_n " %s failed",
                                   ls->addr_text.data);
+
+                    if (ngx_close_socket(s) == -1) {
+                        ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_socket_errno,
+                                      ngx_close_socket_n " %s failed",
+                                      ls->addr_text.data);
+                    }
+
+                    ngx_destroy_pool(pool);
                     return;
                 }
             }
@@ -83,6 +109,14 @@ void ngx_event_accept(ngx_event_t *ev)
                     ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_socket_errno,
                                   ngx_nonblocking_n " %s failed",
                                   ls->addr_text.data);
+
+                    if (ngx_close_socket(s) == -1) {
+                        ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_socket_errno,
+                                      ngx_close_socket_n " %s failed",
+                                      ls->addr_text.data);
+                    }
+
+                    ngx_destroy_pool(pool);
                     return;
                 }
             }
@@ -133,28 +167,28 @@ void ngx_event_accept(ngx_event_t *ev)
         ngx_memcpy(c->log, ev->log, sizeof(ngx_log_t));
         rev->log = wev->log = c->log;
 
-        /* STUB: x86: SP: xadd ?, MT: lock xadd, MP: lock xadd, shared */
+        /* STUB: x86: MT: lock xadd, MP: lock xadd, shared */
         c->number = ngx_connection_counter++;
 
-        ngx_log_debug(ev->log, "ngx_event_accept: accept: %d, %d" _
-                      s _ c->number);
+        ngx_log_debug(ev->log, "accept: %d, %d" _ s _ c->number);
 
-#if (HAVE_DEFERRED_ACCEPT)
-        if (ev->accept_filter) {
+        if (ev->deferred_accept) {
             rev->ready = 1;
         }
-#endif
 
-#if (HAVE_EDGE_EVENT) /* epoll */
+        if (ngx_add_conn) {
+            if (ngx_add_conn(c) == NGX_ERROR) {
+                if (ngx_close_socket(s) == -1) {
+                    ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_socket_errno,
+                                  ngx_close_socket_n " %s failed",
+                                  ls->addr_text.data);
+                }
 
-        if (ngx_event_flags & NGX_HAVE_EDGE_EVENT) {
-            if (ngx_edge_add_event(ev) == NGX_ERROR) {
+                ngx_destroy_pool(pool);
                 return;
             }
         }
 
-#endif
-
         ls->handler(c);
 
         if (ngx_event_flags & NGX_HAVE_KQUEUE_EVENT) {
--- a/src/http/modules/ngx_http_index_handler.c
+++ b/src/http/modules/ngx_http_index_handler.c
@@ -59,7 +59,7 @@ int ngx_http_index_handler(ngx_http_requ
 {
     int                        i, rc, test_dir;
     char                      *name, *file;
-    ngx_str_t                  loc, *index;
+    ngx_str_t                  redirect, *index;
     ngx_err_t                  err;
     ngx_fd_t                   fd;
     ngx_http_index_conf_t     *icf;
@@ -74,14 +74,14 @@ int ngx_http_index_handler(ngx_http_requ
                              + icf->max_index_len),
                   NGX_HTTP_INTERNAL_SERVER_ERROR);
 
-    loc.data = ngx_cpystrn(r->path.data, clcf->doc_root.data,
-                           clcf->doc_root.len + 1);
-    file = ngx_cpystrn(loc.data, r->uri.data, r->uri.len + 1);
+    redirect.data = ngx_cpymem(r->path.data, clcf->doc_root.data,
+                               clcf->doc_root.len);
+    file = ngx_cpystrn(redirect.data, r->uri.data, r->uri.len + 1);
     r->path.len = file - r->path.data;
 
     test_dir = 1;
 
-    index = (ngx_str_t *) icf->indices.elts;
+    index = icf->indices.elts;
     for (i = 0; i < icf->indices.nelts; i++) {
 
         if (index[i].data[0] != '/') {
@@ -136,18 +136,15 @@ ngx_log_error(NGX_LOG_DEBUG, r->connecti
 
         if (index[i].data[0] == '/') {
             r->file.name.len = index[i].len;
-            loc.len = index[i].len;
-            loc.data = index[i].data;
+            redirect.len = index[i].len;
+            redirect.data = index[i].data;
 
         } else {
-            loc.len = r->uri.len + index[i].len;
-            r->file.name.len = clcf->doc_root.len + r->uri.len
-                               + index[i].len;
+            redirect.len = r->uri.len + index[i].len;
+            r->file.name.len = clcf->doc_root.len + r->uri.len + index[i].len;
         }
 
-/* STUB */ r->exten.len = 4; r->exten.data = "html";
-
-        return ngx_http_internal_redirect(r, loc);
+        return ngx_http_internal_redirect(r, &redirect, NULL);
     }
 
     return NGX_DECLINED;
@@ -177,7 +174,6 @@ ngx_log_debug(r->connection->log, "IS_DI
         }
 
         ngx_log_error(NGX_LOG_CRIT, r->connection->log, r->path_err,
-                      "ngx_http_index_test_dir: "
                       ngx_file_type_n " %s failed", r->path.data);
 
         return NGX_HTTP_INTERNAL_SERVER_ERROR;
@@ -263,7 +259,7 @@ static char *ngx_http_index_merge_conf(n
 }
 
 
-/* TODO: check duplicate indices */
+/* TODO: warn about duplicate indices */
 
 static char *ngx_http_index_set_index(ngx_conf_t *cf, ngx_command_t *cmd,
                                       void *conf)
@@ -283,7 +279,9 @@ static char *ngx_http_index_set_index(ng
 
     for (i = 1; i < cf->args->nelts; i++) {
         if (value[i].len == 0) {
-            return "is invalid";
+            ngx_snprintf(ngx_conf_errstr, sizeof(ngx_conf_errstr) - 1,
+                         "index \"%s\" is invalid", value[1].data);
+            return ngx_conf_errstr;
         }
 
         ngx_test_null(index, ngx_push_array(&icf->indices), NGX_CONF_ERROR);
@@ -295,5 +293,5 @@ static char *ngx_http_index_set_index(ng
         }
     }
 
-    return NULL;
+    return NGX_CONF_OK;
 }
new file mode 100644
--- /dev/null
+++ b/src/http/ngx_http_cache.c
@@ -0,0 +1,63 @@
+
+
+
+#define NGX_HTTP_CACHE_ENTRY_DELETED  0x00000001
+#define NGX_HTTP_CACHE_ENTRY_MMAPED   0x00000002
+
+/* "/" -> "/index.html" in ngx_http_index_handler */
+#define NGX_HTTP_CACHE_ENTRY_URI      0x00000004
+
+#define NGX_HTTP_CACHE_FILTER_FLAGS   0xFFFF0000
+
+
+typedef struct {
+    ngx_fd_t   fd;
+    off_t      size;
+    void      *data;
+    time_t     accessed;
+    time_t     last_modified;
+    time_t     updated;      /* no needed with kqueue */
+    int        refs;
+    int        flags;
+} ngx_http_cache_entry_t;
+
+
+typedef struct {
+    u_int32_t          crc;
+    ngx_str_t          uri;
+    ngx_http_cache_t  *cache;
+} ngx_http_cache_hash_entry_t;
+
+
+typedef struct {
+    ngx_http_cache_t  *cache;
+    u_int32_t          crc;
+    int                n;
+} ngx_http_cache_handle_t; 
+
+
+int ngx_http_cache_get(ngx_http_cache_hash_t *cache_hash,
+                       ngx_str_t *uri, ngx_http_cache_handle_t *h)
+{
+    int                           hi;
+    ngx_http_cache_hash_entry_t  *entry;
+
+    h->crc = ngx_crc32(uri->data, uri->len);
+
+    hi = h->crc % cache_hash->size;
+    entry = cache_hash[hi].elts;
+
+    for (i = 0; i < cache_hash[hi].nelts; i++) {
+        if (entry[i].crc == crc
+            && entry[i].uri.len == uri->len
+            && ngx_strncmp(entry[i].uri.data, uri->data, uri->len) == 0
+        {
+            h->cache = entry[i].cache;
+            h->cache->refs++;
+            h->n = hi;
+            return NGX_OK;
+        }
+    }
+
+    return NGX_ERROR;
+}
--- a/src/http/ngx_http_core_module.c
+++ b/src/http/ngx_http_core_module.c
@@ -28,10 +28,13 @@ static char *ngx_http_core_merge_loc_con
 
 static int ngx_http_core_init(ngx_pool_t *pool);
 static char *ngx_server_block(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy);
+static int ngx_cmp_locations(const void *first, const void *second);
 static char *ngx_location_block(ngx_conf_t *cf, ngx_command_t *cmd,
                                                                   void *dummy);
 static char *ngx_types_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
 static char *ngx_set_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
+static char *ngx_set_server_name(ngx_conf_t *cf, ngx_command_t *cmd,
+                                                                   void *conf);
 
 
 static ngx_command_t  ngx_http_core_commands[] = {
@@ -41,7 +44,7 @@ static ngx_command_t  ngx_http_core_comm
      ngx_server_block,
      0,
      0,
-     NULL,},
+     NULL},
 
     {ngx_string("connection_pool_size"),
      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,
@@ -99,6 +102,13 @@ static ngx_command_t  ngx_http_core_comm
      0,
      NULL},
 
+    {ngx_string("server_name"),
+     NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_ANY,
+     ngx_set_server_name,
+     NGX_HTTP_SRV_CONF_OFFSET,
+     0,
+     NULL},
+
     {ngx_string("types"),
      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF
                                          |NGX_CONF_BLOCK|NGX_CONF_NOARGS,
@@ -215,8 +225,10 @@ ngx_log_debug(r->connection->log, "trans
              continue;
          }
 
-         rc = ngx_rstrncmp(r->uri.data, clcfp[i]->name.data,
-                           clcfp[i]->name.len);
+         rc = ngx_strncmp(r->uri.data, clcfp[i]->name.data,
+                          clcfp[i]->name.len);
+
+ngx_log_debug(r->connection->log, "rc: %d" _ rc);
 
          if (rc < 0) {
              break;
@@ -235,9 +247,10 @@ ngx_log_debug(r->connection->log, "trans
 
     /* run translation phase */
 
-    h = (ngx_http_handler_pt *) ngx_http_translate_handlers.elts;
-    for (i = ngx_http_translate_handlers.nelts; i > 0; /* void */) {
-        rc = h[--i](r);
+    h = ngx_http_translate_handlers.elts;
+    for (i = ngx_http_translate_handlers.nelts - 1; i >= 0; i--) {
+
+        rc = h[i](r);
 
         if (rc == NGX_DECLINED) {
             continue;
@@ -457,21 +470,46 @@ int ngx_http_error(ngx_http_request_t *r
 }
 
 
-int ngx_http_internal_redirect(ngx_http_request_t *r, ngx_str_t uri)
+int ngx_http_internal_redirect(ngx_http_request_t *r,
+                               ngx_str_t *uri, ngx_str_t *args)
 {
-    ngx_log_debug(r->connection->log, "internal redirect: '%s'" _ uri.data);
+    int  i;
+
+    ngx_log_debug(r->connection->log, "internal redirect: '%s'" _ uri->data);
+
+    r->uri.len = uri->len;
+    r->uri.data = uri->data;
+
+    if (args) {
+        r->args.len = args->len;
+        r->args.data = args->data;
+    }
+
+    r->exten.len = 0;
+    r->exten.data = NULL;
 
-    r->uri.len = uri.len;
-    r->uri.data = uri.data;
+    for (i = uri->len - 1; i > 1; i--) {
+        if (uri->data[i] == '.' && uri->data[i - 1] != '/') {
+            r->exten.len = uri->len - i - 1;
+
+            if (r->exten.len > 0) {
+                ngx_test_null(r->exten.data,
+                              ngx_palloc(r->pool, r->exten.len + 1),
+                              NGX_HTTP_INTERNAL_SERVER_ERROR);
 
-    /* BROKEN, NEEDED ? */
-    /* r->exten */
-    r->uri_start = uri.data;
-    r->uri_end = uri.data + uri.len;
-    /**/
+                ngx_cpystrn(r->exten.data, &uri->data[i + 1], r->exten.len + 1);
+            }
+
+            break;
+
+        } else if (uri->data[i] == '/') {
+            break;
+        }
+    }
 
     ngx_http_handler(r);
-    return 0;
+
+    return NGX_OK;
 }
 
 
@@ -554,10 +592,26 @@ static char *ngx_server_block(ngx_conf_t
     rv = ngx_conf_parse(cf, NULL);
     *cf = pcf;
 
+    if (rv != NGX_CONF_OK) {
+        return rv;
+    }
+
+    ngx_qsort(cscf->locations.elts, cscf->locations.nelts,
+              sizeof(void *), ngx_cmp_locations);
+
     return rv;
 }
 
 
+static int ngx_cmp_locations(const void *first, const void *second)
+{
+    ngx_http_core_loc_conf_t *one = *(ngx_http_core_loc_conf_t **) first;
+    ngx_http_core_loc_conf_t *two = *(ngx_http_core_loc_conf_t **) second;
+
+    return ngx_strcmp(one->name.data, two->name.data);
+}
+
+
 static char *ngx_location_block(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy)
 {
     int                        m;
@@ -724,6 +778,7 @@ static char *ngx_http_core_merge_srv_con
     ngx_http_core_srv_conf_t *prev = (ngx_http_core_srv_conf_t *) parent;
     ngx_http_core_srv_conf_t *conf = (ngx_http_core_srv_conf_t *) child;
 
+    ngx_err_t                 err;
     ngx_http_listen_t        *l;
     ngx_http_server_name_t   *n;
 
@@ -740,10 +795,15 @@ static char *ngx_http_core_merge_srv_con
         ngx_test_null(n, ngx_push_array(&conf->server_names), NGX_CONF_ERROR);
         ngx_test_null(n->name.data, ngx_palloc(pool, NGX_MAXHOSTNAMELEN),
                       NGX_CONF_ERROR);
+
         if (gethostname(n->name.data, NGX_MAXHOSTNAMELEN) == -1) {
-            /* TODO: need ngx_errno here */
-            return "gethostname() failed";
+            err = ngx_errno;
+            ngx_snprintf(ngx_conf_errstr, sizeof(ngx_conf_errstr) - 1,
+                         "gethostname() failed (%d: %s)",
+                         err, ngx_strerror(err));
+            return ngx_conf_errstr;
         }
+
         n->name.len = ngx_strlen(n->name.data);
         n->core_srv_conf = conf;
     }
@@ -868,7 +928,7 @@ static char *ngx_set_listen(ngx_conf_t *
     ngx_http_listen_t  *ls;
 
     /* TODO: check duplicate 'listen' directives, 
-             add resolved name to server names */
+             add resolved name to server names ??? */
 
     ngx_test_null(ls, ngx_push_array(&scf->listen), NGX_CONF_ERROR);
 
@@ -879,37 +939,75 @@ static char *ngx_set_listen(ngx_conf_t *
     ls->file_name = cf->conf_file->file.name;
     ls->line = cf->conf_file->line;
 
-    args = (ngx_str_t *) cf->args->elts;
+    args = cf->args->elts;
     addr = args[1].data;
 
     for (p = 0; p < args[1].len; p++) {
         if (addr[p] == ':') {
             addr[p++] = '\0';
-
-            ls->addr = inet_addr(addr);
-            if (ls->addr == INADDR_NONE) {
-                h = gethostbyname(addr);
-
-                if (h == NULL || h->h_addr_list[0] == NULL) {
-                    return "can not resolve host name";
-                }
-
-                ls->addr = *(u_int32_t *)(h->h_addr_list[0]);
-            }
-
             break;
         }
     }
 
     if (p == args[1].len) {
-        ls->addr = INADDR_ANY;
+        /* no ":" in the "listen" */
         p = 0;
     }
 
     ls->port = ngx_atoi(&addr[p], args[1].len - p);
-    if (ls->port < 1 || ls->port > 65536) {
-        return "port must be between 1 and 65535";
+    if (ls->port == NGX_ERROR && p == 0) {
+
+        /* "listen host" */
+        ls->port = 80;
+
+    } else if ((ls->port == NGX_ERROR && p != 0) /* "listen host:NONNUMBER" */
+               || (ls->port < 1 || ls->port > 65536)) { /* "listen 99999" */
+
+         ngx_snprintf(ngx_conf_errstr, sizeof(ngx_conf_errstr) - 1,
+                      "invalid port \"%s\", "
+                      "it must be a number between 1 and 65535",
+                      &addr[p]);
+         return ngx_conf_errstr;
+
+    } else if (p == 0) {
+        ls->addr = INADDR_ANY;
+        return NGX_CONF_OK;
+    }
+
+    ls->addr = inet_addr(addr);
+    if (ls->addr == INADDR_NONE) {
+        h = gethostbyname(addr);
+
+        if (h == NULL || h->h_addr_list[0] == NULL) {
+            ngx_snprintf(ngx_conf_errstr, sizeof(ngx_conf_errstr) - 1,
+                         "can not resolve host \"%s\"", addr);
+            return ngx_conf_errstr;
+        }
+
+        ls->addr = *(u_int32_t *)(h->h_addr_list[0]);
     }
 
     return NGX_CONF_OK;
 }
+
+
+static char *ngx_set_server_name(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+    ngx_http_core_srv_conf_t *scf = (ngx_http_core_srv_conf_t *) conf;
+
+    ngx_str_t               *args;
+    ngx_http_server_name_t  *sn;
+
+    /* TODO: several names */
+    /* TODO: warn about duplicate 'server_name' directives */
+
+    ngx_test_null(sn, ngx_push_array(&scf->server_names), NGX_CONF_ERROR);
+
+    args = cf->args->elts;
+
+    sn->name.len = args[1].len;
+    sn->name.data = args[1].data;
+    sn->core_srv_conf = scf;
+
+    return NGX_CONF_OK;
+}
--- a/src/http/ngx_http_core_module.h
+++ b/src/http/ngx_http_core_module.h
@@ -138,7 +138,8 @@ extern int ngx_http_max_module;
 
 int ngx_http_core_translate_handler(ngx_http_request_t *r);
 
-int ngx_http_internal_redirect(ngx_http_request_t *r, ngx_str_t uri);
+int ngx_http_internal_redirect(ngx_http_request_t *r,
+                               ngx_str_t *uri, ngx_str_t *args);
 int ngx_http_error(ngx_http_request_t *r, int error);
 
 
--- a/src/os/unix/ngx_errno.h
+++ b/src/os/unix/ngx_errno.h
@@ -24,6 +24,8 @@ typedef int               ngx_err_t;
 #define ngx_socket_errno           errno
 #define ngx_set_socket_errno(err)  errno = err
 
+#define ngx_strerror(err)          strerror(err)
+
 #define ngx_strerror_r(err, errstr, size)  \
              ngx_cpystrn(errstr, strerror(err), size) - (errstr)
 
--- a/src/os/unix/ngx_freebsd_init.c
+++ b/src/os/unix/ngx_freebsd_init.c
@@ -2,6 +2,7 @@
 #include <ngx_freebsd_init.h>
 
 
+/* FreeBSD 3.4 at least */
 char ngx_freebsd_kern_ostype[20];
 char ngx_freebsd_kern_osrelease[20];
 int ngx_freebsd_kern_osreldate;
@@ -9,6 +10,9 @@ int ngx_freebsd_hw_ncpu;
 int ngx_freebsd_net_inet_tcp_sendspace;
 int ngx_freebsd_sendfile_nbytes_bug;
 
+/* FreeBSD 5.0 */
+int ngx_freebsd_kern_ipc_zero_copy_send;
+
 
 ngx_os_io_t ngx_os_io = {
     ngx_unix_recv,
@@ -19,9 +23,35 @@ ngx_os_io_t ngx_os_io = {
 };
 
 
+typedef struct {
+    char    *name;
+    int     *value;
+    size_t   size;
+} sysctl_t;
+
+
+sysctl_t sysctls[] = {
+    {"hw.ncpu",
+     &ngx_freebsd_hw_ncpu,
+     sizeof(int)},
+
+    {"net.inet.tcp.sendspace",
+     &ngx_freebsd_net_inet_tcp_sendspace,
+     sizeof(int)},
+
+    {"kern.ipc.zero_copy.send",
+     &ngx_freebsd_kern_ipc_zero_copy_send,
+     sizeof(int)},
+
+    {NULL, NULL, 0}
+};
+
+
 int ngx_os_init(ngx_log_t *log)
 {
-    size_t  size;
+    int        i;
+    size_t     size;
+    ngx_err_t  err;
 
     size = 20;
     if (sysctlbyname("kern.ostype",
@@ -85,28 +115,23 @@ int ngx_os_init(ngx_log_t *log)
 #endif /* HAVE_FREEBSD_SENDFILE */
 
 
-    size = 4;
-    if (sysctlbyname("hw.ncpu", &ngx_freebsd_hw_ncpu, &size, NULL, 0) == -1) {
-        ngx_log_error(NGX_LOG_ALERT, log, errno,
-                      "sysctlbyname(hw.ncpu) failed");
-        return NGX_ERROR;
-    }
-
-    ngx_log_error(NGX_LOG_INFO, log, 0, "hw.ncpu: %d", ngx_freebsd_hw_ncpu);
-
+    for (i = 0; sysctls[i].name; i++) {
+        *sysctls[i].value = 0;
+        size = sysctls[i].size;
+        if (sysctlbyname(sysctls[i].name, sysctls[i].value, &size, NULL, 0)
+                                                                       == -1) {
+            err = errno;
+            if (err != NGX_ENOENT) {
+                ngx_log_error(NGX_LOG_ALERT, log, err,
+                              "sysctlbyname(%s) failed", sysctls[i].name);
+                return NGX_ERROR;
+            }
 
-    size = 4;
-    if (sysctlbyname("net.inet.tcp.sendspace",
-                     &ngx_freebsd_net_inet_tcp_sendspace,
-                     &size, NULL, 0) == -1)
-    {
-        ngx_log_error(NGX_LOG_ALERT, log, errno,
-                      "sysctlbyname(net.inet.tcp.sendspace) failed");
-        return NGX_ERROR;
+        } else {
+            ngx_log_error(NGX_LOG_INFO, log, 0, "%s: %d",
+                          sysctls[i].name, *sysctls[i].value);
+        }
     }
 
-    ngx_log_error(NGX_LOG_INFO, log, 0, "net.inet.tcp.sendspace: %d",
-                  ngx_freebsd_net_inet_tcp_sendspace);
-
     return ngx_posix_init(log);
 }
--- a/src/os/unix/ngx_linux_config.h
+++ b/src/os/unix/ngx_linux_config.h
@@ -26,6 +26,7 @@
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
+#include <netdb.h>
 
 
 typedef unsigned int   u_int;
new file mode 100644
--- /dev/null
+++ b/src/os/unix/ngx_linux_sendfile_chain.c
@@ -0,0 +1,183 @@
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_linux_init.h>
+
+
+ngx_chain_t *ngx_linux_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in)
+{
+    int              rc, on, off;
+    char            *prev;
+    size_t           hsize, size;
+    ssize_t          sent;
+    struct iovec    *iov;
+    struct sf_hdtr   hdtr;
+    ngx_err_t        err;
+    ngx_array_t      header, trailer;
+    ngx_hunk_t      *file;
+    ngx_chain_t     *ce;
+
+    ce = in;
+    file = NULL;
+    hsize = 0;
+
+    on = 1;
+    off = 0;
+
+    ngx_init_array(header, c->pool, 10, sizeof(struct iovec), NGX_CHAIN_ERROR);
+    ngx_init_array(trailer, c->pool, 10, sizeof(struct iovec), NGX_CHAIN_ERROR);
+
+    /* create the header iovec */
+    if (ngx_hunk_in_memory_only(ce->hunk)) {
+        prev = NULL;
+        iov = NULL;
+
+        /* create the iovec and coalesce the neighbouring chain entries */
+        while (ce && ngx_hunk_in_memory_only(ce->hunk)) {
+
+            if (prev == ce->hunk->pos) {
+                iov->iov_len += ce->hunk->last - ce->hunk->pos;
+                prev = ce->hunk->last;
+
+            } else {
+                ngx_test_null(iov, ngx_push_array(&header), NGX_CHAIN_ERROR);
+                iov->iov_base = ce->hunk->pos;
+                iov->iov_len = ce->hunk->last - ce->hunk->pos;
+                prev = ce->hunk->last;
+            }
+
+            if (ngx_freebsd_sendfile_nbytes_bug) {
+                hsize += ce->hunk->last - ce->hunk->pos;
+            }
+
+            ce = ce->next;
+        }
+    }
+
+    /* TODO: coalesce the neighbouring file hunks */
+    if (ce && (ce->hunk->type & NGX_HUNK_FILE)) {
+        file = ce->hunk;
+        ce = ce->next;
+    }
+
+    /* create the trailer iovec */
+    if (ce && ngx_hunk_in_memory_only(ce->hunk)) {
+        prev = NULL;
+        iov = NULL;
+
+        /* create the iovec and coalesce the neighbouring chain entries */
+        while (ce && ngx_hunk_in_memory_only(ce->hunk)) {
+
+            if (prev == ce->hunk->pos) {
+                iov->iov_len += ce->hunk->last - ce->hunk->pos;
+                prev = ce->hunk->last;
+
+            } else {
+                ngx_test_null(iov, ngx_push_array(&trailer), NGX_CHAIN_ERROR);
+                iov->iov_base = ce->hunk->pos;
+                iov->iov_len = ce->hunk->last - ce->hunk->pos;
+                prev = ce->hunk->last;
+            }
+
+            ce = ce->next;
+        }
+    }
+
+    if (file) {
+        if (setsockopt(c->fd, IPPROTO_TCP, TCP_CORK,
+                       (const void *) &on, sizeof(int)) == -1) {
+            ngx_log_error(NGX_LOG_CRIT, c->log, err,
+                          "setsockopt(TCP_CORK, 1) failed");
+            return NGX_CHAIN_ERROR;
+        }
+
+
+        rc = sendfile(c->fd, file->file->fd, file->file_pos,
+                        (size_t) (file->file_last - file->file_pos));
+
+        if (rc == -1) {
+            err = ngx_errno;
+            if (err == NGX_EAGAIN) {
+                ngx_log_error(NGX_LOG_INFO, c->log, err, "senfile() EAGAIN");
+
+            } else if (err == NGX_EINTR) {
+                ngx_log_error(NGX_LOG_INFO, c->log, err, "senfile() EINTR");
+
+            } else {
+                ngx_log_error(NGX_LOG_CRIT, c->log, err, "sendfile() failed");
+                return NGX_CHAIN_ERROR;
+            }
+        }
+
+        sent = rc > 0 ? rc : 0;
+
+#if (NGX_DEBUG_WRITE_CHAIN)
+        ngx_log_debug(c->log, "sendfile: %d, @%qd %d:%d" _
+                      rc _ file->file_pos _ sent _
+                      (size_t) (file->file_last - file->file_pos));
+#endif
+
+    } else {
+        rc = writev(c->fd, (struct iovec *) header.elts, header.nelts);
+
+        if (rc == -1) {
+            err = ngx_errno;
+            if (err == NGX_EAGAIN) {
+                ngx_log_error(NGX_LOG_INFO, c->log, err, "writev() EAGAIN");
+
+            } else if (err == NGX_EINTR) {
+                ngx_log_error(NGX_LOG_INFO, c->log, err, "writev() EINTR");
+
+            } else {
+                ngx_log_error(NGX_LOG_CRIT, c->log, err, "writev() failed");
+                return NGX_CHAIN_ERROR;
+            }
+        }
+
+        sent = rc > 0 ? rc : 0;
+
+#if (NGX_DEBUG_WRITE_CHAIN)
+        ngx_log_debug(c->log, "writev: %d" _ sent);
+#endif
+    }
+
+    c->sent += sent;
+
+    for (ce = in; ce && sent > 0; ce = ce->next) {
+
+        if (ce->hunk->type & NGX_HUNK_IN_MEMORY) {
+            size = ce->hunk->last - ce->hunk->pos;
+        } else {
+            size = ce->hunk->file_last - ce->hunk->file_pos;
+        }
+
+        if (sent >= size) {
+            sent -= size;
+
+            if (ce->hunk->type & NGX_HUNK_IN_MEMORY) {
+                ce->hunk->pos = ce->hunk->last;
+            }
+
+            if (ce->hunk->type & NGX_HUNK_FILE) {
+                ce->hunk->file_pos = ce->hunk->file_last;
+            }
+
+            continue;
+        }
+
+        if (ce->hunk->type & NGX_HUNK_IN_MEMORY) {
+            ce->hunk->pos += sent;
+        }
+
+        if (ce->hunk->type & NGX_HUNK_FILE) {
+            ce->hunk->file_pos += sent;
+        }
+
+        break;
+    }
+
+    ngx_destroy_array(&trailer);
+    ngx_destroy_array(&header);
+
+    return ce;
+}
--- a/src/os/unix/ngx_solaris_config.h
+++ b/src/os/unix/ngx_solaris_config.h
@@ -22,6 +22,7 @@
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
+#include <netdb.h>
 
 
 typedef uint32_t  u_int32_t;