changeset 408:d6e2b445c1b8

nginx-0.0.10-2004-08-27-19:40:59 import
author Igor Sysoev <igor@sysoev.ru>
date Fri, 27 Aug 2004 15:40:59 +0000
parents 35fe251cd231
children 8ac40cae79f0
files auto/modules auto/options auto/sources src/core/nginx.c src/core/ngx_conf_file.h src/core/ngx_output_chain.c src/core/ngx_string.c src/core/ngx_string.h src/http/modules/ngx_http_access_handler.c src/http/modules/ngx_http_userid_handler.c src/http/modules/proxy/ngx_http_proxy_upstream.c src/http/ngx_http.c src/http/ngx_http_core_module.h src/http/ngx_http_request.c src/http/ngx_http_request.h
diffstat 15 files changed, 616 insertions(+), 39 deletions(-) [+]
line wrap: on
line diff
--- a/auto/modules
+++ b/auto/modules
@@ -87,6 +87,11 @@ if [ $HTTP_ACCESS = YES ]; then
     HTTP_SRCS="$HTTP_SRCS $HTTP_ACCESS_SRCS"
 fi
 
+if [ $HTTP_USERID = YES ]; then
+    HTTP_MODULES="$HTTP_MODULES $HTTP_USERID_MODULE"
+    HTTP_SRCS="$HTTP_SRCS $HTTP_USERID_SRCS"
+fi
+
 if [ $HTTP_STATUS = YES ]; then
     have=NGX_HTTP_STATUS . auto/have
     HTTP_MODULES="$HTTP_MODULES $HTTP_STATUS_MODULE"
--- a/auto/options
+++ b/auto/options
@@ -34,6 +34,7 @@ HTTP_GZIP=YES
 HTTP_SSL=NO
 HTTP_SSI=YES
 HTTP_ACCESS=YES
+HTTP_USERID=YES
 HTTP_STATUS=YES
 HTTP_REWRITE=YES
 HTTP_PROXY=YES
@@ -92,6 +93,7 @@ do
         --without-http_gzip_module)      HTTP_GZIP=NO               ;;
         --without-http_ssi_module)       HTTP_SSI=NO                ;;
         --without-http_access_module)    HTTP_ACCESS=NO             ;;
+        --without-http_userid_module)    HTTP_USERID=NO             ;;
         --without-http_status_module)    HTTP_STATUS=NO             ;;
         --without-http_rewrite_module)   HTTP_REWRITE=NO            ;;
         --without-http_proxy_module)     HTTP_PROXY=NO              ;;
--- a/auto/sources
+++ b/auto/sources
@@ -261,6 +261,10 @@ HTTP_ACCESS_MODULE=ngx_http_access_modul
 HTTP_ACCESS_SRCS=src/http/modules/ngx_http_access_handler.c
 
 
+HTTP_USERID_MODULE=ngx_http_userid_module
+HTTP_USERID_SRCS=src/http/modules/ngx_http_userid_handler.c
+
+
 HTTP_STATUS_MODULE=ngx_http_status_module
 HTTP_STATUS_SRCS=src/http/modules/ngx_http_status_handler.c
 
--- a/src/core/nginx.c
+++ b/src/core/nginx.c
@@ -140,6 +140,65 @@ int main(int argc, char *const *argv)
         return 1;
     }
 
+{
+    ngx_str_t  d, s;
+
+    s.data = "12";
+    s.len = sizeof("12") - 1;
+
+    if (ngx_encode_base64(init_cycle.pool, &s, &d) != NGX_OK) {
+        ngx_log_error(NGX_LOG_ERR, log, 0, "ngx_encode_base64() failed");
+    } else {
+        ngx_log_error(NGX_LOG_NOTICE, log, 0, "BASE64: %d:\"%s\"", d.len, d.data);
+    }
+
+    s.data = "123";
+    s.len = sizeof("123") - 1;
+
+    if (ngx_encode_base64(init_cycle.pool, &s, &d) != NGX_OK) {
+        ngx_log_error(NGX_LOG_ERR, log, 0, "ngx_encode_base64() failed");
+    } else {
+        ngx_log_error(NGX_LOG_NOTICE, log, 0, "BASE64: %d:\"%s\"", d.len, d.data);
+    }
+
+    s.data = "1234";
+    s.len = sizeof("1234") - 1;
+
+    if (ngx_encode_base64(init_cycle.pool, &s, &d) != NGX_OK) {
+        ngx_log_error(NGX_LOG_ERR, log, 0, "ngx_encode_base64() failed");
+    } else {
+        ngx_log_error(NGX_LOG_NOTICE, log, 0, "BASE64: %d:\"%s\"", d.len, d.data);
+    }
+
+    s.data = "12345";
+    s.len = sizeof("12345") - 1;
+
+    if (ngx_encode_base64(init_cycle.pool, &s, &d) != NGX_OK) {
+        ngx_log_error(NGX_LOG_ERR, log, 0, "ngx_encode_base64() failed");
+    } else {
+        ngx_log_error(NGX_LOG_NOTICE, log, 0, "BASE64: %d:\"%s\"", d.len, d.data);
+    }
+
+    s.data = "123456";
+    s.len = sizeof("123456") - 1;
+
+    if (ngx_encode_base64(init_cycle.pool, &s, &d) != NGX_OK) {
+        ngx_log_error(NGX_LOG_ERR, log, 0, "ngx_encode_base64() failed");
+    } else {
+        ngx_log_error(NGX_LOG_NOTICE, log, 0, "BASE64: %d:\"%s\"", d.len, d.data);
+    }
+
+    s.data = "12345678901234567890";
+    s.len = sizeof("12345678901234567890") - 1;
+
+    if (ngx_encode_base64(init_cycle.pool, &s, &d) != NGX_OK) {
+        ngx_log_error(NGX_LOG_ERR, log, 0, "ngx_encode_base64() failed");
+    } else {
+        ngx_log_error(NGX_LOG_NOTICE, log, 0, "BASE64: %d:\"%s\"", d.len, d.data);
+    }
+
+}
+
     if (ngx_add_inherited_sockets(&init_cycle) == NGX_ERROR) {
         return 1;
     }
--- a/src/core/ngx_conf_file.h
+++ b/src/core/ngx_conf_file.h
@@ -276,7 +276,6 @@ char *ngx_conf_set_num_slot(ngx_conf_t *
 char *ngx_conf_set_size_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
 char *ngx_conf_set_msec_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
 char *ngx_conf_set_sec_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
-char *ngx_conf_set_time_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
 char *ngx_conf_set_bufs_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
 char *ngx_conf_set_enum_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
 char *ngx_conf_set_bitmask_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
--- a/src/core/ngx_output_chain.c
+++ b/src/core/ngx_output_chain.c
@@ -275,6 +275,10 @@ ngx_int_t ngx_chain_writer(void *data, n
 
 
     for (/* void */; in; in = in->next) {
+
+        ngx_log_debug1(NGX_LOG_DEBUG_CORE, ctx->connection->log, 0,
+                       "WRITER buf: %d", ngx_buf_size(in->buf));
+
         ngx_alloc_link_and_set_buf(cl, in->buf, ctx->pool, NGX_ERROR);
         *ctx->last = cl;
         ctx->last = &cl->next;
--- a/src/core/ngx_string.c
+++ b/src/core/ngx_string.c
@@ -126,7 +126,7 @@ void ngx_md5_text(u_char *text, u_char *
 ngx_int_t ngx_encode_base64(ngx_pool_t *pool, ngx_str_t *src, ngx_str_t *dst)
 {
     u_char         *d, *s;
-    ngx_uint_t      i;
+    size_t          len;
     static u_char   basis64[] =
             "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
 
@@ -136,24 +136,28 @@ ngx_int_t ngx_encode_base64(ngx_pool_t *
 
     dst->data = d;
     s = src->data;
+    len = src->len;
 
-    for (i = 0; i < src->len - 2; i += 3) {
-        *d++ = basis64[(s[i] >> 2) & 0x3f];
-        *d++ = basis64[((s[i] & 3) << 4) | (s[i + 1] >> 4)];
-        *d++ = basis64[((s[i + 1] & 0x0f) << 2) | (s[i + 2] >> 6)];
-        *d++ = basis64[s[i + 2] & 0x3f];
+    while (len > 2) {
+        *d++ = basis64[(s[0] >> 2) & 0x3f];
+        *d++ = basis64[((s[0] & 3) << 4) | (s[1] >> 4)];
+        *d++ = basis64[((s[1] & 0x0f) << 2) | (s[2] >> 6)];
+        *d++ = basis64[s[2] & 0x3f];
+
+        s += 3;
+        len -= 3;
     }
 
-    if (i < src->len) {
-        *d++ = basis64[(s[i] >> 2) & 0x3f];
+    if (len) {
+        *d++ = basis64[(s[0] >> 2) & 0x3f];
 
-        if (i == src->len - 1) {
-            *d++ = basis64[(s[i] & 3) << 4];
+        if (len == 1) {
+            *d++ = basis64[(s[0] & 3) << 4];
             *d++ = '=';
 
         } else {
-            *d++ = basis64[((s[i] & 3) << 4) | (s[i + 1] >> 4)];
-            *d++ = basis64[(s[i + 1] & 0x0f) << 2];
+            *d++ = basis64[((s[0] & 3) << 4) | (s[1] >> 4)];
+            *d++ = basis64[(s[1] & 0x0f) << 2];
         }
 
         *d++ = '=';
@@ -168,33 +172,68 @@ ngx_int_t ngx_encode_base64(ngx_pool_t *
 
 ngx_int_t ngx_decode_base64(ngx_pool_t *pool, ngx_str_t *src, ngx_str_t *dst)
 {
-    u_char  *d, *s, c;
+    size_t          len;
+    u_char         *d, *s;
+    static u_char   basis64[] =
+        { 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
+          77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
+          77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 62, 77, 77, 77, 63,
+          52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 77, 77, 77, 77, 77, 77,
+          77,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
+          15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 77, 77, 77, 77, 77,
+          77, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+          41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 77, 77, 77, 77, 77,
 
-    if (!(d = ngx_palloc(pool, ((src->len + 3) / 4) * 3))) {
+          77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
+          77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
+          77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
+          77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
+          77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
+          77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
+          77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
+          77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77 };
+
+    for (len = 0; len < src->len; len++) {
+        if (src->data[len] == '=') {
+            break;
+        }
+
+        if (basis64[src->data[len]] == 77) {
+            return NGX_ERROR;
+        }
+    }
+
+    if (len % 4 == 1) {
+        return NGX_ERROR;
+    }
+
+    if (!(d = ngx_palloc(pool, ((len + 3) / 4) * 3 + 1))) {
         return NGX_ABORT;
     }
 
     dst->data = d;
+
     s = src->data;
 
-    if (*s == '+') {
-        c = 62;
+    while (len > 3) {
+        *d++ = basis64[s[0]] << 2 | basis64[s[1]] >> 4;
+        *d++ = basis64[s[1]] << 4 | basis64[s[2]] >> 2;
+        *d++ = basis64[s[2]] << 6 | basis64[s[3]];
 
-    } else if (*s == '/') {
-        c = 63;
-
-    } else if (*s >= '0' && *s <= '9') {
-        c = *s - '0' + 52;
+        s += 4;
+        len -= 4;
+    }
 
-    } else if (*s >= 'A' && *s <= 'Z') {
-        c = *s - 'A';
+    if (len > 1) {
+        *d++ = basis64[s[0]] << 2 | basis64[s[1]] >> 4;
+    }
 
-    } else if (*s >= 'a' && *s <= 'z') {
-        c = *s - 'a' + 26;
+    if (len > 2) {
+        *d++ = basis64[s[1]] << 4 | basis64[s[2]] >> 2;
+    }
 
-    } else {
-        return NGX_ERROR;
-    }
+    dst->len = d - dst->data;
+    *d++ = '\0';
 
     return NGX_OK;
 }
--- a/src/core/ngx_string.h
+++ b/src/core/ngx_string.h
@@ -71,6 +71,9 @@ ngx_int_t ngx_hextoi(u_char *line, size_
 
 void ngx_md5_text(u_char *text, u_char *md5);
 
+ngx_int_t ngx_encode_base64(ngx_pool_t *pool, ngx_str_t *src, ngx_str_t *dst);
+ngx_int_t ngx_decode_base64(ngx_pool_t *pool, ngx_str_t *src, ngx_str_t *dst);
+
 
 #define  ngx_qsort                qsort
 
--- a/src/http/modules/ngx_http_access_handler.c
+++ b/src/http/modules/ngx_http_access_handler.c
@@ -68,7 +68,7 @@ ngx_module_t  ngx_http_access_module = {
     ngx_http_access_commands,              /* module directives */
     NGX_HTTP_MODULE,                       /* module type */
     ngx_http_access_init,                  /* init module */
-    NULL                                   /* init child */
+    NULL                                   /* init process */
 };
 
 
new file mode 100644
--- /dev/null
+++ b/src/http/modules/ngx_http_userid_handler.c
@@ -0,0 +1,421 @@
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+#define NGX_HTTP_USERID_OFF       0x0002
+#define NGX_HTTP_USERID_ON        0x0004
+#define NGX_HTTP_USERID_LOGONLY   0x0008
+#define NGX_HTTP_USERID_TIME      0x0010
+
+
+typedef struct {
+    ngx_flag_t  enable;
+
+    ngx_int_t   version;
+    ngx_int_t   service;
+
+    ngx_str_t   name;
+    ngx_str_t   domain;
+    ngx_str_t   path;
+    time_t      expires;
+
+    ngx_int_t   p3p;
+    ngx_str_t   p3p_string;
+} ngx_http_userid_conf_t;
+
+
+typedef struct {
+    uint32_t          uid_got[4];
+    uint32_t          uid_set[4];
+    struct timeval    tv;
+} ngx_http_userid_ctx_t;
+
+
+static ngx_int_t ngx_http_userid_get_uid(ngx_http_request_t *r,
+                                         ngx_http_userid_ctx_t *ctx,
+                                         ngx_http_userid_conf_t *conf);
+
+static u_char *ngx_http_userid_log_uid_got(ngx_http_request_t *r, u_char *buf,
+                                           uintptr_t data);
+static u_char *ngx_http_userid_log_uid_set(ngx_http_request_t *r, u_char *buf,
+                                           uintptr_t data);
+static u_char *ngx_http_userid_log_uid_time(ngx_http_request_t *r, u_char *buf,
+                                            uintptr_t data);
+
+static ngx_int_t ngx_http_userid_pre_conf(ngx_conf_t *cf);
+static void *ngx_http_userid_create_conf(ngx_conf_t *cf);
+static char *ngx_http_userid_merge_conf(ngx_conf_t *cf, void *parent,
+                                        void *child);
+static ngx_int_t ngx_http_userid_init(ngx_cycle_t *cycle);
+
+
+static ngx_conf_enum_t  ngx_http_userid_mask[] = {
+    { ngx_string("off"), NGX_HTTP_USERID_OFF },
+    { ngx_string("on"), NGX_HTTP_USERID_ON },
+    { ngx_string("logonly"), NGX_HTTP_USERID_LOGONLY },
+    { ngx_string("time"), NGX_HTTP_USERID_TIME },
+    { ngx_null_string, 0 }
+};
+
+
+static ngx_command_t  ngx_http_userid_commands[] = {
+
+    { ngx_string("userid"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_ANY,
+      ngx_conf_set_bitmask_slot,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      offsetof(ngx_http_userid_conf_t, enable),
+      ngx_http_userid_mask},
+
+    { ngx_string("userid_service"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+      ngx_conf_set_num_slot,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      offsetof(ngx_http_userid_conf_t, service),
+      NULL},
+
+    { ngx_string("userid_name"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+      ngx_conf_set_str_slot,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      offsetof(ngx_http_userid_conf_t, name),
+      NULL},
+
+    { ngx_string("userid_domain"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+      ngx_conf_set_str_slot,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      offsetof(ngx_http_userid_conf_t, domain),
+      NULL},
+
+    { ngx_string("userid_path"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+      ngx_conf_set_str_slot,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      offsetof(ngx_http_userid_conf_t, path),
+      NULL},
+
+    { ngx_string("userid_expires"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+      ngx_conf_set_sec_slot,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      offsetof(ngx_http_userid_conf_t, expires),
+      NULL},
+
+    ngx_null_command
+};
+
+
+ngx_http_module_t  ngx_http_userid_module_ctx = {
+    ngx_http_userid_pre_conf,              /* pre conf */
+
+    NULL,                                  /* create main configuration */
+    NULL,                                  /* init main configuration */
+
+    NULL,                                  /* create server configuration */
+    NULL,                                  /* merge server configuration */
+
+    ngx_http_userid_create_conf,           /* create location configration */
+    ngx_http_userid_merge_conf             /* merge location configration */
+};
+
+
+ngx_module_t  ngx_http_userid_module = {
+    NGX_MODULE,
+    &ngx_http_userid_module_ctx,           /* module context */
+    ngx_http_userid_commands,              /* module directives */
+    NGX_HTTP_MODULE,                       /* module type */
+    ngx_http_userid_init,                  /* init module */
+    NULL                                   /* init process */
+};
+
+
+static ngx_http_log_op_name_t ngx_http_userid_log_fmt_ops[] = {
+    { ngx_string("uid_got"), 0, ngx_http_userid_log_uid_got },
+    { ngx_string("uid_set"), 0, ngx_http_userid_log_uid_set },
+    { ngx_string("uid_time"), TIME_T_LEN + 4, ngx_http_userid_log_uid_time },
+    { ngx_null_string, 0, NULL }
+};
+
+
+static ngx_int_t ngx_http_userid_handler(ngx_http_request_t *r)
+{
+    ngx_int_t                rc;
+    struct timeval           tv;
+    ngx_http_userid_ctx_t   *ctx;
+    ngx_http_userid_conf_t  *conf;
+
+    conf = ngx_http_get_module_loc_conf(r, ngx_http_userid_module);
+
+    if (conf->enable & NGX_HTTP_USERID_OFF) {
+        return NGX_OK;
+    }
+
+    ctx = ngx_http_get_module_ctx(r, ngx_http_userid_module);
+
+    if (ctx) {
+        return NGX_OK;
+    }
+
+    ngx_http_create_ctx(r, ctx, ngx_http_userid_module,
+                        sizeof(ngx_http_userid_ctx_t),
+                        NGX_HTTP_INTERNAL_SERVER_ERROR);
+
+    if (conf->enable & (NGX_HTTP_USERID_ON|NGX_HTTP_USERID_LOGONLY)) {
+        rc = ngx_http_userid_get_uid(r, ctx, conf);
+
+        if (rc != NGX_OK) {
+            return rc;
+        }
+    }
+
+    if (conf->enable & NGX_HTTP_USERID_TIME) {
+        ngx_gettimeofday(&ctx->tv);
+    }
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t ngx_http_userid_get_uid(ngx_http_request_t *r,
+                                         ngx_http_userid_ctx_t *ctx,
+                                         ngx_http_userid_conf_t *conf)
+{
+    u_char           *start, *last, *end;
+    uint32_t         *uid;
+    ngx_int_t         rc;
+    ngx_uint_t       *cookies, i;
+    ngx_str_t         src, dst;
+    ngx_table_elt_t  *headers;
+
+    headers = r->headers_in.headers.elts;
+    cookies = r->headers_in.cookies.elts;
+
+    for (i = 0; i < r->headers_in.cookies.nelts; i++) {
+        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                       "cookie: %d:\"%s\"",
+                       cookies[i],
+                       headers[cookies[i]].value.data);
+
+        end = headers[cookies[i]].value.data + headers[cookies[i]].value.len;
+
+        for (start = headers[cookies[i]].value.data; start < end; /* void */) {
+
+            if (conf->name.len >= headers[cookies[i]].value.len
+                || ngx_strncmp(start, conf->name.data, conf->name.len) != 0)
+            {
+                start += conf->name.len;
+                while (start < end && *start++ != ';') { /* void */ }
+
+                for (/* void */; start < end && *start == ' '; start++) { /**/ }
+
+                continue;
+            }
+
+            for (start += conf->name.len; start < end && *start == ' '; start++)
+            {
+                /* void */
+            }
+
+            if (*start != '=') {
+                break;
+            }
+
+            for (start++; start < end && *start == ' '; start++) { /* void */ }
+
+            for (last = start; last < end && *last != ';'; last++) { /**/ }
+
+            if (last - start < 22) {
+                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                              "client sent too short userid cookie \"%s\"",
+                              headers[cookies[i]].value.data);
+                break;
+            }
+
+            /*
+             * we have to limit encoded string to 22 characters
+             * because there are already the millions cookies with a garbage
+             * instead of the correct base64 trail "=="
+             */
+
+            src.len = 22;
+            src.data = start;
+
+            rc = ngx_decode_base64(r->pool, &src, &dst);
+
+            if (rc == NGX_ABORT) {
+                return NGX_HTTP_INTERNAL_SERVER_ERROR;
+            }
+
+            if (rc == NGX_ERROR) {
+                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                              "client sent invalid userid cookie \"%s\"",
+                              headers[cookies[i]].value.data);
+                break;
+            }
+
+            uid = (uint32_t *) dst.data;
+            ctx->uid_got[0] = uid[0];
+            ctx->uid_got[1] = uid[1];
+            ctx->uid_got[2] = uid[2];
+            ctx->uid_got[3] = uid[3];
+
+            ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                           "uid: %08X%08X%08X%08X",
+                           uid[0], uid[1], uid[2], uid[3]);
+
+            return NGX_OK;
+        }
+    }
+
+    return NGX_OK;
+}
+
+
+static u_char *ngx_http_userid_log_uid_got(ngx_http_request_t *r, u_char *buf,
+                                           uintptr_t data)
+{
+    ngx_http_userid_ctx_t   *ctx;
+    ngx_http_userid_conf_t  *conf;
+
+    ctx = ngx_http_get_module_ctx(r, ngx_http_userid_module);
+
+    if (ctx == NULL || ctx->uid_got[3] == 0) {
+        if (buf == NULL) {
+            return (u_char *) 1;
+        }
+
+        *buf = '-';
+        return buf + 1;
+    }
+
+    conf = ngx_http_get_module_loc_conf(r, ngx_http_userid_module);
+
+    if (buf == NULL) {
+        return (u_char *) (conf->name.len + 1 + 32);
+    }
+
+    buf = ngx_cpymem(buf, conf->name.data, conf->name.len);
+
+    *buf++ = '=';
+
+    return buf + ngx_snprintf((char *) buf, 33, "%08X%08X%08X%08X",
+                              ctx->uid_got[0], ctx->uid_got[1],
+                              ctx->uid_got[2], ctx->uid_got[3]);
+}
+
+
+static u_char *ngx_http_userid_log_uid_set(ngx_http_request_t *r, u_char *buf,
+                                           uintptr_t data)
+{
+    if (buf == NULL) {
+        return (u_char *) 1;
+    }
+
+    *buf = '-';
+
+    return buf + 1;
+}
+
+
+static u_char *ngx_http_userid_log_uid_time(ngx_http_request_t *r, u_char *buf,
+                                            uintptr_t data)
+{
+    ngx_http_userid_ctx_t   *ctx;
+
+    ctx = ngx_http_get_module_ctx(r, ngx_http_userid_module);
+
+    if (ctx == NULL || ctx->tv.tv_sec == 0) {
+        *buf = '-';
+        return buf + 1;
+    }
+
+    return buf + ngx_snprintf((char *) buf, TIME_T_LEN + 5,
+                              "%ld.%03ld",
+                              ctx->tv.tv_sec, ctx->tv.tv_usec / 1000);
+}
+
+
+static ngx_int_t ngx_http_userid_pre_conf(ngx_conf_t *cf)
+{
+    ngx_http_log_op_name_t  *op;
+
+    for (op = ngx_http_userid_log_fmt_ops; op->name.len; op++) { /* void */ }
+    op->op = NULL;
+
+    op = ngx_http_log_fmt_ops;
+
+    for (op = ngx_http_log_fmt_ops; op->op; op++) {
+        if (op->name.len == 0) {
+            op = (ngx_http_log_op_name_t *) op->op;
+        }
+    }
+
+    op->op = (ngx_http_log_op_pt) ngx_http_userid_log_fmt_ops;
+
+    return NGX_OK;
+}
+
+
+static void *ngx_http_userid_create_conf(ngx_conf_t *cf)
+{   
+    ngx_http_userid_conf_t  *conf;
+
+    if (!(conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_userid_conf_t)))) {
+        return NGX_CONF_ERROR;
+    }
+
+    /* set by ngx_pcalloc():
+
+    conf->enable = 0;
+
+    conf->name.len = 0;
+    conf->name.date = NULL;
+    conf->domain.len = 0;
+    conf->domain.date = NULL;
+    conf->path.len = 0;
+    conf->path.date = NULL;
+
+    */
+
+
+    return conf;
+}   
+
+
+static char *ngx_http_userid_merge_conf(ngx_conf_t *cf, void *parent,
+                                        void *child)
+{
+    ngx_http_userid_conf_t *prev = parent;
+    ngx_http_userid_conf_t *conf = child;
+
+    ngx_conf_merge_bitmask_value(conf->enable, prev->enable,
+                                 (NGX_CONF_BITMASK_SET
+                                  |NGX_HTTP_USERID_OFF));
+
+    ngx_conf_merge_str_value(conf->name, prev->name, "uid");
+    ngx_conf_merge_str_value(conf->domain, prev->domain, ".");
+    ngx_conf_merge_str_value(conf->path, prev->path, "/");
+
+    return NGX_CONF_OK;
+}   
+
+
+static ngx_int_t ngx_http_userid_init(ngx_cycle_t *cycle)
+{
+    ngx_http_handler_pt        *h;
+    ngx_http_core_main_conf_t  *cmcf;
+
+    cmcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_core_module);
+
+    h = ngx_push_array(&cmcf->phases[NGX_HTTP_MISC_PHASE].handlers);
+    if (h == NULL) {
+        return NGX_ERROR;
+    }
+
+    *h = ngx_http_userid_handler;
+
+    return NGX_OK;
+}
--- a/src/http/modules/proxy/ngx_http_proxy_upstream.c
+++ b/src/http/modules/proxy/ngx_http_proxy_upstream.c
@@ -627,6 +627,8 @@ static void ngx_http_proxy_connect(ngx_h
         output->allocated = 1;
 
         r->request_body->buf->pos = r->request_body->buf->start;
+        r->request_body->buf->last = r->request_body->buf->start;
+        r->request_body->buf->tag = (ngx_buf_tag_t) &ngx_http_proxy_module;
     }
 
     p->request_sent = 0;
--- a/src/http/ngx_http.c
+++ b/src/http/ngx_http.c
@@ -257,6 +257,11 @@ static char *ngx_http_block(ngx_conf_t *
     *h = ngx_http_find_location_config;
 
 
+    ngx_init_array(cmcf->phases[NGX_HTTP_MISC_PHASE].handlers,
+                   cf->cycle->pool, 10, sizeof(ngx_http_handler_pt),
+                   NGX_CONF_ERROR);
+    cmcf->phases[NGX_HTTP_MISC_PHASE].type = NGX_DECLINED;
+
     ngx_init_array(cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers,
                    cf->cycle->pool, 10, sizeof(ngx_http_handler_pt),
                    NGX_CONF_ERROR);
--- a/src/http/ngx_http_core_module.h
+++ b/src/http/ngx_http_core_module.h
@@ -23,6 +23,7 @@ typedef enum {
 
     NGX_HTTP_FIND_CONFIG_PHASE,
 
+    NGX_HTTP_MISC_PHASE,
     NGX_HTTP_ACCESS_PHASE,
     NGX_HTTP_CONTENT_PHASE,
 
--- a/src/http/ngx_http_request.c
+++ b/src/http/ngx_http_request.c
@@ -617,12 +617,28 @@ static void ngx_http_process_request_lin
             return;
         }
 
-        /* r->headers_in.headers.elts = NULL; */
         /* r->headers_in.headers.nelts = 0; */
         r->headers_in.headers.size = sizeof(ngx_table_elt_t);
         r->headers_in.headers.nalloc = 20;
         r->headers_in.headers.pool = r->pool;
 
+
+        /* init the r->headers_in.cookies array */
+
+        r->headers_in.cookies.elts = ngx_pcalloc(r->pool,
+                                                 5 * sizeof(ngx_uint_t));
+        if (r->headers_in.cookies.elts == NULL) {
+            ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+            ngx_http_close_connection(c);
+            return;
+        }
+
+        /* r->headers_in.cookies.nelts = 0; */
+        r->headers_in.cookies.size = sizeof(ngx_uint_t);
+        r->headers_in.cookies.nalloc = 5;
+        r->headers_in.cookies.pool = r->pool;
+
+
         ctx = c->log->data;
         ctx->action = "reading client request headers";
         ctx->url = r->unparsed_uri.data;
@@ -715,6 +731,7 @@ static void ngx_http_process_request_hea
 {
     ssize_t                    n;
     ngx_int_t                  rc, i, offset;
+    ngx_uint_t                *cookie;
     ngx_table_elt_t           *h;
     ngx_connection_t          *c;
     ngx_http_request_t        *r;
@@ -783,17 +800,31 @@ static void ngx_http_process_request_hea
                 h->value.data[h->value.len] = '\0';
             }
 
-            for (i = 0; ngx_http_headers_in[i].name.len != 0; i++) {
-                if (ngx_http_headers_in[i].name.len != h->key.len) {
-                    continue;
+            if (h->key.len == sizeof("Cookie") - 1
+                && ngx_strcasecmp(h->key.data, "Cookie") == 0)
+            {
+                if (!(cookie = ngx_push_array(&r->headers_in.cookies))) {
+                    ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+                    ngx_http_close_connection(c);
+                    return;
                 }
 
-                if (ngx_strcasecmp(ngx_http_headers_in[i].name.data,
-                                   h->key.data) == 0)
-                {
-                    *((ngx_table_elt_t **) ((char *) &r->headers_in
+                *cookie = r->headers_in.headers.nelts - 1;
+
+            } else {
+
+                for (i = 0; ngx_http_headers_in[i].name.len != 0; i++) {
+                    if (ngx_http_headers_in[i].name.len != h->key.len) {
+                        continue;
+                    }
+
+                    if (ngx_strcasecmp(ngx_http_headers_in[i].name.data,
+                                       h->key.data) == 0)
+                    {
+                        *((ngx_table_elt_t **) ((char *) &r->headers_in
                                          + ngx_http_headers_in[i].offset)) = h;
-                    break;
+                        break;
+                    }
                 }
             }
 
--- a/src/http/ngx_http_request.h
+++ b/src/http/ngx_http_request.h
@@ -138,6 +138,8 @@ typedef struct {
     ngx_table_elt_t  *x_forwarded_for;
 #endif
 
+    ngx_array_t       cookies;
+
     size_t            host_name_len;
     ssize_t           content_length_n;
     size_t            connection_type;