changeset 409:8ac40cae79f0

nginx-0.0.10-2004-08-29-07:55:41 import
author Igor Sysoev <igor@sysoev.ru>
date Sun, 29 Aug 2004 03:55:41 +0000
parents d6e2b445c1b8
children 48b9ad5ca1fc
files auto/modules auto/options auto/sources src/core/nginx.c src/core/ngx_string.c src/core/ngx_string.h src/core/ngx_times.c src/core/ngx_times.h src/http/modules/ngx_http_userid_filter.c src/http/modules/ngx_http_userid_handler.c src/http/ngx_http_log_handler.c
diffstat 10 files changed, 267 insertions(+), 197 deletions(-) [+]
line wrap: on
line diff
--- a/auto/modules
+++ b/auto/modules
@@ -79,6 +79,11 @@ if [ $HTTP_SSI = YES ]; then
     HTTP_SRCS="$HTTP_SRCS $HTTP_SSI_SRCS"
 fi
 
+if [ $HTTP_USERID = YES ]; then
+    HTTP_FILTER_MODULES="$HTTP_FILTER_MODULES $HTTP_USERID_FILTER_MODULE"
+    HTTP_SRCS="$HTTP_SRCS $HTTP_USERID_SRCS"
+fi
+
 HTTP_MODULES="$HTTP_MODULES $HTTP_STATIC_MODULE $HTTP_INDEX_MODULE"
 
 if [ $HTTP_ACCESS = YES ]; then
@@ -87,11 +92,6 @@ 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
@@ -92,8 +92,8 @@ do
         --without-http_charset_module)   HTTP_CHARSET=NO            ;;
         --without-http_gzip_module)      HTTP_GZIP=NO               ;;
         --without-http_ssi_module)       HTTP_SSI=NO                ;;
+        --without-http_userid_module)    HTTP_USERID=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
@@ -257,14 +257,14 @@ HTTP_SSI_FILTER_MODULE=ngx_http_ssi_filt
 HTTP_SSI_SRCS=src/http/modules/ngx_http_ssi_filter.c
 
 
+HTTP_USERID_FILTER_MODULE=ngx_http_userid_filter_module
+HTTP_USERID_SRCS=src/http/modules/ngx_http_userid_filter.c
+
+
 HTTP_ACCESS_MODULE=ngx_http_access_module
 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,65 +140,6 @@ 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_string.c
+++ b/src/core/ngx_string.c
@@ -123,20 +123,16 @@ 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)
+void ngx_encode_base64(ngx_str_t *src, ngx_str_t *dst)
 {
     u_char         *d, *s;
     size_t          len;
     static u_char   basis64[] =
             "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
 
-    if (!(d = ngx_palloc(pool, ((src->len + 2) / 3) * 4 + 1))) {
-        return NGX_ERROR;
-    }
-
-    dst->data = d;
+    len = src->len;
     s = src->data;
-    len = src->len;
+    d = dst->data;
 
     while (len > 2) {
         *d++ = basis64[(s[0] >> 2) & 0x3f];
@@ -164,13 +160,10 @@ ngx_int_t ngx_encode_base64(ngx_pool_t *
     }
 
     dst->len = d - dst->data;
-    *d++ = '\0';
-
-    return NGX_OK;
 }
 
 
-ngx_int_t ngx_decode_base64(ngx_pool_t *pool, ngx_str_t *src, ngx_str_t *dst)
+ngx_int_t ngx_decode_base64(ngx_str_t *src, ngx_str_t *dst)
 {
     size_t          len;
     u_char         *d, *s;
@@ -207,33 +200,27 @@ ngx_int_t ngx_decode_base64(ngx_pool_t *
         return NGX_ERROR;
     }
 
-    if (!(d = ngx_palloc(pool, ((len + 3) / 4) * 3 + 1))) {
-        return NGX_ABORT;
-    }
-
-    dst->data = d;
-
     s = src->data;
+    d = dst->data;
 
     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]];
+        *d++ = (u_char) (basis64[s[0]] << 2 | basis64[s[1]] >> 4);
+        *d++ = (u_char) (basis64[s[1]] << 4 | basis64[s[2]] >> 2);
+        *d++ = (u_char) (basis64[s[2]] << 6 | basis64[s[3]]);
 
         s += 4;
         len -= 4;
     }
 
     if (len > 1) {
-        *d++ = basis64[s[0]] << 2 | basis64[s[1]] >> 4;
+        *d++ = (u_char) (basis64[s[0]] << 2 | basis64[s[1]] >> 4);
     }
 
     if (len > 2) {
-        *d++ = basis64[s[1]] << 4 | basis64[s[2]] >> 2;
+        *d++ = (u_char) (basis64[s[1]] << 4 | basis64[s[2]] >> 2);
     }
 
     dst->len = d - dst->data;
-    *d++ = '\0';
 
     return NGX_OK;
 }
--- a/src/core/ngx_string.h
+++ b/src/core/ngx_string.h
@@ -71,8 +71,12 @@ 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_base64_encoded_length(len)  (((len + 2) / 3) * 4)
+#define ngx_base64_decoded_length(len)  (((len + 3) / 4) * 3)
+
+void ngx_encode_base64(ngx_str_t *src, ngx_str_t *dst);
+ngx_int_t ngx_decode_base64(ngx_str_t *src, ngx_str_t *dst);
 
 
 #define  ngx_qsort                qsort
--- a/src/core/ngx_times.c
+++ b/src/core/ngx_times.c
@@ -214,14 +214,51 @@ size_t ngx_http_time(u_char *buf, time_t
     ngx_gmtime(t, &tm);
 
     return ngx_snprintf((char *) buf, sizeof("Mon, 28 Sep 1970 06:00:00 GMT"),
-                                       "%s, %02d %s %4d %02d:%02d:%02d GMT",
-                                       week[tm.ngx_tm_wday],
-                                       tm.ngx_tm_mday,
-                                       months[tm.ngx_tm_mon - 1],
-                                       tm.ngx_tm_year,
-                                       tm.ngx_tm_hour,
-                                       tm.ngx_tm_min,
-                                       tm.ngx_tm_sec);
+                                      "%s, %02d %s %4d %02d:%02d:%02d GMT",
+                                      week[tm.ngx_tm_wday],
+                                      tm.ngx_tm_mday,
+                                      months[tm.ngx_tm_mon - 1],
+                                      tm.ngx_tm_year,
+                                      tm.ngx_tm_hour,
+                                      tm.ngx_tm_min,
+                                      tm.ngx_tm_sec);
+}
+
+
+size_t ngx_http_cookie_time(u_char *buf, time_t t)
+{
+    ngx_tm_t  tm;
+
+    ngx_gmtime(t, &tm);
+
+    /*
+     * Netscape 3.x does not understand 4-digit years at all and
+     * 2-digit years more than "37"
+     */
+
+    if (tm.ngx_tm_year > 2037) {
+        return ngx_snprintf((char *) buf,
+                                      sizeof("Mon, 28-Sep-1970 06:00:00 GMT"),
+                                      "%s, %02d-%s-%d %02d:%02d:%02d GMT",
+                                      week[tm.ngx_tm_wday],
+                                      tm.ngx_tm_mday,
+                                      months[tm.ngx_tm_mon - 1],
+                                      tm.ngx_tm_year,
+                                      tm.ngx_tm_hour,
+                                      tm.ngx_tm_min,
+                                      tm.ngx_tm_sec);
+    } else {
+        return ngx_snprintf((char *) buf,
+                                      sizeof("Mon, 28-Sep-70 06:00:00 GMT"),
+                                      "%s, %02d-%s-%02d %02d:%02d:%02d GMT",
+                                      week[tm.ngx_tm_wday],
+                                      tm.ngx_tm_mday,
+                                      months[tm.ngx_tm_mon - 1],
+                                      tm.ngx_tm_year % 100,
+                                      tm.ngx_tm_hour,
+                                      tm.ngx_tm_min,
+                                      tm.ngx_tm_sec);
+    }
 }
 
 
--- a/src/core/ngx_times.h
+++ b/src/core/ngx_times.h
@@ -9,6 +9,7 @@
 void ngx_time_init();
 void ngx_time_update(time_t s);
 size_t ngx_http_time(u_char *buf, time_t t);
+size_t ngx_http_cookie_time(u_char *buf, time_t t);
 void ngx_gmtime(time_t t, ngx_tm_t *tp);
 
 #if (NGX_THREADS)
rename from src/http/modules/ngx_http_userid_handler.c
rename to src/http/modules/ngx_http_userid_filter.c
--- a/src/http/modules/ngx_http_userid_handler.c
+++ b/src/http/modules/ngx_http_userid_filter.c
@@ -4,16 +4,18 @@
 #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
+#define NGX_HTTP_USERID_OFF   0
+#define NGX_HTTP_USERID_LOG   1
+#define NGX_HTTP_USERID_V1    2
+#define NGX_HTTP_USERID_ON    3
+
+/* 31 Dec 2037 23:55:55 GMT */
+#define NGX_HTTP_USERID_MAX_EXPIRES  2145916555
 
 
 typedef struct {
     ngx_flag_t  enable;
 
-    ngx_int_t   version;
     ngx_int_t   service;
 
     ngx_str_t   name;
@@ -29,20 +31,20 @@ typedef struct {
 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 ngx_int_t ngx_http_userid_set_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);
@@ -51,11 +53,21 @@ static char *ngx_http_userid_merge_conf(
 static ngx_int_t ngx_http_userid_init(ngx_cycle_t *cycle);
 
 
-static ngx_conf_enum_t  ngx_http_userid_mask[] = {
+static uint32_t  sequencer_v1 = 1;
+static uint32_t  sequencer_v2 = 0x03030302;
+
+
+static u_char expires[] = "; expires=Thu, 31-Dec-37 23:55:55 GMT";
+
+
+static ngx_http_output_header_filter_pt  ngx_http_next_header_filter;
+
+
+static ngx_conf_enum_t  ngx_http_userid_state[] = {
     { ngx_string("off"), NGX_HTTP_USERID_OFF },
+    { ngx_string("log"), NGX_HTTP_USERID_LOG },
+    { ngx_string("v1"), NGX_HTTP_USERID_V1 },
     { 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 }
 };
 
@@ -63,11 +75,11 @@ static ngx_conf_enum_t  ngx_http_userid_
 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_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+      ngx_conf_set_enum_slot,
       NGX_HTTP_LOC_CONF_OFFSET,
       offsetof(ngx_http_userid_conf_t, enable),
-      ngx_http_userid_mask},
+      ngx_http_userid_state},
 
     { ngx_string("userid_service"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
@@ -98,7 +110,7 @@ static ngx_command_t  ngx_http_userid_co
       NULL},
 
     { ngx_string("userid_expires"),
-      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
       ngx_conf_set_sec_slot,
       NGX_HTTP_LOC_CONF_OFFSET,
       offsetof(ngx_http_userid_conf_t, expires),
@@ -108,7 +120,7 @@ static ngx_command_t  ngx_http_userid_co
 };
 
 
-ngx_http_module_t  ngx_http_userid_module_ctx = {
+ngx_http_module_t  ngx_http_userid_filter_module_ctx = {
     ngx_http_userid_pre_conf,              /* pre conf */
 
     NULL,                                  /* create main configuration */
@@ -122,9 +134,9 @@ ngx_http_module_t  ngx_http_userid_modul
 };
 
 
-ngx_module_t  ngx_http_userid_module = {
+ngx_module_t  ngx_http_userid_filter_module = {
     NGX_MODULE,
-    &ngx_http_userid_module_ctx,           /* module context */
+    &ngx_http_userid_filter_module_ctx,    /* module context */
     ngx_http_userid_commands,              /* module directives */
     NGX_HTTP_MODULE,                       /* module type */
     ngx_http_userid_init,                  /* init module */
@@ -135,47 +147,43 @@ ngx_module_t  ngx_http_userid_module = {
 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)
+static ngx_int_t ngx_http_userid_filter(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);
+    conf = ngx_http_get_module_loc_conf(r, ngx_http_userid_filter_module);
+
+    if (conf->enable == NGX_HTTP_USERID_OFF) {
+        return ngx_http_next_header_filter(r);
+    }
 
-    if (conf->enable & NGX_HTTP_USERID_OFF) {
+    ngx_http_create_ctx(r, ctx, ngx_http_userid_filter_module,
+                        sizeof(ngx_http_userid_ctx_t),
+                        NGX_HTTP_INTERNAL_SERVER_ERROR);
+
+    rc = ngx_http_userid_get_uid(r, ctx, conf);
+
+    if (rc != NGX_OK) {
+        return rc;
+    }
+
+    if (conf->enable == NGX_HTTP_USERID_LOG /* || ctx->uid_got[3] != 0 */) {
         return NGX_OK;
     }
 
-    ctx = ngx_http_get_module_ctx(r, ngx_http_userid_module);
+    rc = ngx_http_userid_set_uid(r, ctx, conf);
 
-    if (ctx) {
-        return NGX_OK;
+    if (rc != NGX_OK) {
+        return rc;
     }
 
-    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;
+    return ngx_http_next_header_filter(r);
 }
 
 
@@ -184,8 +192,6 @@ static ngx_int_t ngx_http_userid_get_uid
                                          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;
@@ -242,29 +248,19 @@ static ngx_int_t ngx_http_userid_get_uid
 
             src.len = 22;
             src.data = start;
-
-            rc = ngx_decode_base64(r->pool, &src, &dst);
+            dst.data = (u_char *) ctx->uid_got;
 
-            if (rc == NGX_ABORT) {
-                return NGX_HTTP_INTERNAL_SERVER_ERROR;
-            }
-
-            if (rc == NGX_ERROR) {
+            if (ngx_decode_base64(&src, &dst) == 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]);
+                           ctx->uid_got[0], ctx->uid_got[1],
+                           ctx->uid_got[2], ctx->uid_got[3]);
 
             return NGX_OK;
         }
@@ -274,13 +270,109 @@ static ngx_int_t ngx_http_userid_get_uid
 }
 
 
+static ngx_int_t ngx_http_userid_set_uid(ngx_http_request_t *r,
+                                         ngx_http_userid_ctx_t *ctx,
+                                         ngx_http_userid_conf_t *conf)
+
+{
+    u_char           *cookie, *p;
+    size_t            len;
+    ngx_str_t         src, dst;
+    ngx_table_elt_t  *set_cookie;
+
+    /* TODO: mutex for sequencers */
+
+    if (conf->enable == NGX_HTTP_USERID_V1) {
+        ctx->uid_set[0] = conf->service;
+        ctx->uid_set[1] = ngx_time();
+        ctx->uid_set[2] = ngx_pid;
+        ctx->uid_set[3] = sequencer_v1;
+        sequencer_v1 += 0x100;
+
+    } else {
+        ctx->uid_set[0] = htonl(conf->service);
+        ctx->uid_set[1] = htonl(ngx_time());
+        ctx->uid_set[2] = htonl(ngx_pid);
+        ctx->uid_set[3] = htonl(sequencer_v2);
+        sequencer_v2 += 0x100;
+        if (sequencer_v2 < 0x03030302) {
+            sequencer_v2 = 0x03030302;
+        }
+    }
+
+    len = conf->name.len + 1 + ngx_base64_encoded_length(16) + 1;
+
+    if (conf->expires) {
+        len += sizeof(expires) - 1 + 2;
+    }
+
+    if (conf->domain.len > 1) {
+        len += sizeof("; domain=") - 1 + conf->domain.len;
+    }
+
+    if (conf->path.len) {
+        len += sizeof("; path=") - 1 + conf->path.len;
+    }
+
+    if (!(cookie = ngx_palloc(r->pool, len))) {
+        return NGX_HTTP_INTERNAL_SERVER_ERROR;
+    }
+
+    p = ngx_cpymem(cookie, conf->name.data, conf->name.len);
+    *p++ = '=';
+
+    src.len = 16;
+    src.data = (u_char *) ctx->uid_set;
+    dst.data = p;
+
+    ngx_encode_base64(&src, &dst);
+
+    p += dst.len;
+
+    if (conf->expires == NGX_HTTP_USERID_MAX_EXPIRES) {
+        p = ngx_cpymem(p, expires, sizeof(expires) - 1);
+
+    } else if (conf->expires) {
+        p = ngx_cpymem(p, expires, sizeof("; expires=") - 1);
+        p += ngx_http_cookie_time(p, ngx_time() + conf->expires);
+    }
+
+    if (conf->domain.len > 1) {
+        p = ngx_cpymem(p, "; domain=", sizeof("; domain=") - 1);
+        p = ngx_cpymem(p, conf->domain.data, conf->domain.len);
+    }
+
+    if (conf->path.len) {
+        p = ngx_cpymem(p, "; path=", sizeof("; path=") - 1);
+        p = ngx_cpymem(p, conf->path.data, conf->path.len);
+    }
+
+    *p = '\0';
+
+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "uid cookie: \"%s\"", cookie);
+
+    set_cookie = ngx_http_add_header(&r->headers_out, ngx_http_headers_out);
+    if (set_cookie == NULL) {
+        return NGX_HTTP_INTERNAL_SERVER_ERROR;
+    }
+
+    set_cookie->key.len = sizeof("Set-Cookie") - 1;
+    set_cookie->key.data = (u_char *) "Set-Cookie";
+    set_cookie->value.len = p - cookie;
+    set_cookie->value.data = cookie;
+
+    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);
+    ctx = ngx_http_get_module_ctx(r, ngx_http_userid_filter_module);
 
     if (ctx == NULL || ctx->uid_got[3] == 0) {
         if (buf == NULL) {
@@ -291,7 +383,7 @@ static u_char *ngx_http_userid_log_uid_g
         return buf + 1;
     }
 
-    conf = ngx_http_get_module_loc_conf(r, ngx_http_userid_module);
+    conf = ngx_http_get_module_loc_conf(r, ngx_http_userid_filter_module);
 
     if (buf == NULL) {
         return (u_char *) (conf->name.len + 1 + 32);
@@ -310,31 +402,33 @@ static u_char *ngx_http_userid_log_uid_g
 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;
-    }
+    ngx_http_userid_ctx_t   *ctx;
+    ngx_http_userid_conf_t  *conf;
 
-    *buf = '-';
-
-    return buf + 1;
-}
+    ctx = ngx_http_get_module_ctx(r, ngx_http_userid_filter_module);
 
+    if (ctx == NULL || ctx->uid_set[3] == 0) {
+        if (buf == NULL) {
+            return (u_char *) 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);
+    conf = ngx_http_get_module_loc_conf(r, ngx_http_userid_filter_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_set[0], ctx->uid_set[1],
+                              ctx->uid_set[2], ctx->uid_set[3]);
 }
 
 
@@ -369,8 +463,6 @@ static void *ngx_http_userid_create_conf
 
     /* set by ngx_pcalloc():
 
-    conf->enable = 0;
-
     conf->name.len = 0;
     conf->name.date = NULL;
     conf->domain.len = 0;
@@ -380,6 +472,8 @@ static void *ngx_http_userid_create_conf
 
     */
 
+    conf->enable = NGX_CONF_UNSET;
+    conf->expires = NGX_CONF_UNSET;
 
     return conf;
 }   
@@ -391,31 +485,22 @@ static char *ngx_http_userid_merge_conf(
     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_value(conf->enable, prev->enable, 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, "/");
 
+    ngx_conf_merge_sec_value(conf->expires, prev->expires, 0);
+
     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;
+    ngx_http_next_header_filter = ngx_http_top_header_filter;
+    ngx_http_top_header_filter = ngx_http_userid_filter;
 
     return NGX_OK;
 }
--- a/src/http/ngx_http_log_handler.c
+++ b/src/http/ngx_http_log_handler.c
@@ -13,6 +13,8 @@ static u_char *ngx_http_log_pipe(ngx_htt
                                  uintptr_t data);
 static u_char *ngx_http_log_time(ngx_http_request_t *r, u_char *buf,
                                  uintptr_t data);
+static u_char *ngx_http_log_msec(ngx_http_request_t *r, u_char *buf,
+                                 uintptr_t data);
 static u_char *ngx_http_log_request(ngx_http_request_t *r, u_char *buf,
                                     uintptr_t data);
 static u_char *ngx_http_log_status(ngx_http_request_t *r, u_char *buf,
@@ -106,6 +108,7 @@ ngx_http_log_op_name_t ngx_http_log_fmt_
     { ngx_string("pipe"), 1, ngx_http_log_pipe },
     { ngx_string("time"), sizeof("28/Sep/1970:12:00:00") - 1,
                           ngx_http_log_time },
+    { ngx_string("msec"), TIME_T_LEN + 4, ngx_http_log_msec },
     { ngx_string("request"), 0, ngx_http_log_request },
     { ngx_string("status"), 3, ngx_http_log_status },
     { ngx_string("length"), NGX_OFF_T_LEN, ngx_http_log_length },
@@ -225,6 +228,18 @@ static u_char *ngx_http_log_time(ngx_htt
 }
 
 
+static u_char *ngx_http_log_msec(ngx_http_request_t *r, u_char *buf,
+                                 uintptr_t data)
+{
+    struct timeval  tv;
+
+    ngx_gettimeofday(&tv);
+
+    return buf + ngx_snprintf((char *) buf, TIME_T_LEN + 5, "%ld.%03ld",
+                              tv.tv_sec, tv.tv_usec / 1000);
+}
+
+
 static u_char *ngx_http_log_request(ngx_http_request_t *r, u_char *buf,
                                     uintptr_t data)
 {