Add new cookie into headers_in

michalkraus nginx-forum at nginx.us
Mon Jan 24 18:07:04 MSK 2011


Hello,
I have a filter module in rewrite phase. The module tests if a cookie is
in the request. If not the module generates one (if cookie is found the
module modifies it) and then send it back to the client via
headers_out.
But I need one more functionality. I want the generated/modified cookie
to be sent to the proxy module. I have used the headers_more_module for
this purpose so far and it worked perfectly. I generate an
$ap_filter_cookie_sid variable in my module that contains the cookie. I
would like my module to be able to modify headers_in on its own.
I have managed to modify existing cookie in the headers_in.headers. But
when no cookie is in headers_in.headers my module is not able to store
newly generated cookie to headers_in.headers. It seems OK in my module
but before running proxy module the Nginx stops processing the request.
When the first request contains a cookie the Nginx server processes it
correctly and each next request (also withnou cookie) as well. This
behaviour is a bit confusing and I have no idea how to fix it.

Can anybody help me?
Thank you, Michal

Complete code:

typedef u_char *(*ngx_http_ap_filter_op_run_pt) (ngx_http_request_t *r,
u_char *buf,
    ngx_http_ap_filter_op_t *op);

typedef size_t (*ngx_http_ap_filter_op_getlen_pt) (ngx_http_request_t
*r, uintptr_t data);
#define COOKIE_EXPIRES_TIME 2592000

typedef struct {
    ngx_str_t                      cookie_sid;
    ngx_str_t                      sessionid;
} ngx_http_ap_filter_ctx_t;


typedef struct {
    ngx_flag_t   enable;
    ngx_str_t    sessionid;
} ngx_http_ap_filter_loc_conf_t;

static ngx_int_t ngx_http_ap_filter_add_variables(ngx_conf_t *cf);
static ngx_int_t
ngx_http_ap_filter_cookie_sid_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
static void*     ngx_http_ap_filter_create_loc_conf(ngx_conf_t *cf);
static ngx_int_t ngx_http_ap_filter_init(ngx_conf_t *cf);
static char*     ngx_http_ap_filter_merge_loc_conf(ngx_conf_t *cf, void
*parent, void *child);
static ngx_int_t ngx_http_ap_filter(ngx_http_request_t *r,
ngx_http_ap_filter_ctx_t *ctx);
static ngx_int_t ngx_http_ap_filter_handler(ngx_http_request_t *r);

static ngx_int_t ngx_http_ap_filter_get_cookie(ngx_http_request_t *r,
ngx_str_t *cookie_value);
static ngx_int_t ngx_http_ap_filter_set_cookie(ngx_http_request_t *r,
ngx_str_t* cookie_value, ngx_http_ap_filter_ctx_t *ctx);

static ngx_command_t  ngx_http_ap_filter_commands[] = {
    { ngx_string("ap_filter"),
     
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_FLAG,
      ngx_conf_set_flag_slot,
      NGX_HTTP_LOC_CONF_OFFSET,
      offsetof(ngx_http_ap_filter_loc_conf_t, enable),
      NULL },
    { ngx_string("ap_filter_sessionid"),
     
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
      ngx_conf_set_str_slot,
      NGX_HTTP_LOC_CONF_OFFSET,
      offsetof(ngx_http_ap_filter_loc_conf_t, sessionid),
      NULL },
      ngx_null_command
};

static ngx_http_module_t  ngx_http_ap_filter_module_ctx = {
    ngx_http_ap_filter_add_variables, /* preconfiguration */
    ngx_http_ap_filter_init,          /* postconfiguration */
    NULL,                             /* create main configuration */
    NULL,                             /* init main configuration */
    NULL,                             /* create server configuration */
    NULL,                             /* merge server configuration */
    ngx_http_ap_filter_create_loc_conf,  /* create location
configuration */
    ngx_http_ap_filter_merge_loc_conf    /* merge location configuration
*/
};

ngx_module_t  ngx_http_ap_filter_module = {
    NGX_MODULE_V1,
    &ngx_http_ap_filter_module_ctx,/* module context */
    ngx_http_ap_filter_commands,   /* module directives */
    NGX_HTTP_MODULE,               /* module type */
    NULL,                          /* init master */
    NULL,                          /* init module */
    NULL,                          /* init process */
    NULL,                          /* init thread */
    NULL,                          /* exit thread */
    NULL,                          /* exit process */
    NULL,                          /* exit master */
    NGX_MODULE_V1_PADDING
};

static ngx_http_variable_t  ngx_http_ap_filter_vars[] = {
      // $ap_mod_cookie_sid - modified/generated cookie
    { ngx_string("ap_filter_cookie_sid"), NULL,
ngx_http_ap_filter_cookie_sid_variable, 0,
     
NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0
},

    { ngx_null_string, NULL, NULL, 0, 0, 0 }
};

static ngx_int_t ngx_http_ap_filter_add_variables(ngx_conf_t *cf) {
    ngx_http_variable_t  *var, *v;

    for (v = ngx_http_ap_filter_vars; v->name.len; v++) {
        var = ngx_http_add_variable(cf, &v->name, v->flags);
        if (var == NULL) {
            return NGX_ERROR;
        }

        var->get_handler = v->get_handler;
        var->data = v->data;
    }

    return NGX_OK;
}

static ngx_int_t
ngx_http_ap_filter_cookie_sid_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data) {
    ngx_http_ap_filter_ctx_t  *ctx;

    ctx = reinterpret_cast(ngx_http_get_module_ctx(r,
ngx_http_ap_filter_module));

    if (ctx == NULL) {
        v->not_found = 1;
        return NGX_OK;
    }

    v->len = ctx->cookie_sid.len;
    v->valid = 1;
    v->no_cacheable = 0;
    v->not_found = 0;
    v->data = ctx->cookie_sid.data;

    return NGX_OK;
}

static void *ngx_http_ap_filter_create_loc_conf(ngx_conf_t *cf) {
    ngx_http_ap_filter_loc_conf_t  *conf;

    conf = reinterpret_cast(ngx_pcalloc(cf->pool,
sizeof(ngx_http_ap_filter_loc_conf_t)));
    if (conf == NULL) {
        return NGX_CONF_ERROR;
    }
    conf->enable = NGX_CONF_UNSET_UINT;
    return conf;
}

static char *ngx_http_ap_filter_merge_loc_conf(ngx_conf_t *cf, void
*parent, void *child) {
    ngx_http_ap_filter_loc_conf_t *prev = reinterpret_cast(parent);
    ngx_http_ap_filter_loc_conf_t *conf = reinterpret_cast(child);

    ngx_conf_merge_value(conf->enable, prev->enable, 0);
    ngx_conf_merge_str_value(conf->sessionid, prev->sessionid, "url");
    return NGX_CONF_OK;
}

static ngx_int_t ngx_http_ap_filter_init(ngx_conf_t *cf) {
    ngx_http_handler_pt         *h;
    ngx_http_core_main_conf_t   *cmcf;

    cmcf = reinterpret_cast(ngx_http_conf_get_module_main_conf(cf,
ngx_http_core_module));
    h =
reinterpret_cast(ngx_array_push(&cmcf->phases[NGX_HTTP_REWRITE_PHASE].handlers));
    if (h == NULL) {
        return NGX_ERROR;
    }
    *h = ngx_http_ap_filter_handler;

    return NGX_OK;
}

static ngx_int_t ngx_http_ap_filter_handler(ngx_http_request_t *r) {
    ngx_int_t                       rc;
    ngx_http_ap_filter_ctx_t       *ctx;
    ctx = reinterpret_cast(ngx_http_get_module_ctx(r,
ngx_http_ap_filter_module));
    if (ctx == NULL) {
        ctx = reinterpret_cast(ngx_pcalloc(r->pool,
sizeof(ngx_http_ap_filter_module)));
        if (ctx == NULL) {
            return NGX_HTTP_INTERNAL_SERVER_ERROR;
        }

        ngx_http_set_ctx(r, ctx, ngx_http_ap_filter_module);
    }
    rc = ngx_http_ap_filter(r, ctx);
    if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) {
        return rc;
    }
    if (rc == NGX_AGAIN) {
        return NGX_DONE;
    }
    if (rc != NGX_OK) {
        return rc;
    }
    return NGX_DECLINED;
}

static ngx_int_t ngx_http_ap_filter_get_cookie(ngx_http_request_t *r,
ngx_str_t *cookie_value) {
    ngx_int_t   n;
    ngx_str_t   cookie_key;
    cookie_key.len = sizeof("sid")-1;
    ngx_str_set(&cookie_key, "sid");
    n = ngx_http_parse_multi_header_lines(&r->headers_in.cookies,
&cookie_key, cookie_value);
    if (n != NGX_DECLINED) {
        // ziskal jsem cookie
        return NGX_OK;
    } else {
        return NGX_DECLINED;
    }
}

static ngx_int_t ngx_http_ap_filter_set_cookie(ngx_http_request_t *r,
ngx_str_t *cookie_value, 
    ngx_http_ap_filter_ctx_t *ctx) { 
    u_char              *cookie, *p;
    size_t               len;
    ngx_table_elt_t     *set_cookie;

    u_char               expires[] = "; expires=Thu, 31-Dec-37 23:55:55
GMT";
    len = 250;
    cookie = reinterpret_cast(ngx_pnalloc(r->pool, len));
    // vlozime nazev cookie - sid
    p = ngx_copy(cookie, "sid", 3);
    *p++ = '=';
    p = ngx_cpymem(p, cookie_value->data, cookie_value->len);	 
    p = ngx_cpymem(p, expires, sizeof("; expires=") - 1);
    p = ngx_http_cookie_time(p, ngx_time() + COOKIE_EXPIRES_TIME);

    set_cookie =
reinterpret_cast(ngx_list_push(&r->headers_out.headers));

     if (set_cookie == NULL) {
        return NGX_ERROR;
    }
    set_cookie->hash = 1;
    ngx_str_set(&set_cookie->key, "Set-Cookie");
    set_cookie->value.len = p - cookie;
    set_cookie->value.data = cookie;
    ctx->cookie_sid = set_cookie->value;
   
    // HEADERS_IN SECTION:
    const u_char cookie_key[] = "COOKIE";
    ngx_table_elt_t *set_cookie_in;
    u_char           ch;
    ngx_uint_t        i,j;
   
    ngx_list_part_t *part = &(r->headers_in.headers.part);
    ngx_table_elt_t *header = reinterpret_cast(part->elts);
    set_cookie_in = NULL;
    // find cookies in headers_in.headers
    for (i = 0; ; ++i) {
        if (i >= part->nelts) {
            if (part->next == NULL) {
                break;
            }
            part = part->next;
            header = reinterpret_cast(part->elts);
            i = 0;
        }
        if (header[i].key.len != (sizeof(cookie_key) - 1)) {
            continue;
        }

        for (j = 0; j < (sizeof(cookie_key) - 1); ++j) {
            ch = header[i].key.data[j];
            if (ch >= 'a' && ch <= 'z') ch &= ~0x20;
            if (ch != cookie_key[j]) break;
        }
        if (j == (sizeof(cookie_key) - 1)) {
            set_cookie_in = &header[i];
        }
    }
    // cookie not found !!!!!!!!!!
    if (set_cookie_in == NULL) {
       
        
        set_cookie_in =
reinterpret_cast(ngx_list_push(&(r->headers_in.headers)));
        if (set_cookie_in == NULL) {
            return 0;
        }
        set_cookie_in->hash = 1;
        ngx_str_set(&set_cookie_in->key, "Cookie");   
        set_cookie_in->value.len = p - cookie;
        set_cookie_in->value.data = cookie;
    // cookie found - everything OK
    } else {
        set_cookie_in->value.len = p - cookie;
        set_cookie_in->value.data = cookie;
    }
   
    ngx_pfree(r->pool, cookie);

    return NGX_OK;
}

static ngx_int_t ngx_http_ap_filter(ngx_http_request_t *r,
ngx_http_ap_filter_ctx_t *ctx) {
    ngx_http_ap_filter_loc_conf_t  *cglcf;
    ngx_str_t                    cookie_value;
    
    cglcf = reinterpret_cast(ngx_http_get_module_loc_conf(r,
ngx_http_ap_filter_module));

    if (cglcf->enable != 0) {
        ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"ap_filter enable");
    } else {
        ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"ap_filter disable");
        return NGX_OK; 
    }

    // a C++ object for processinf the cookie
    ApCookie_t cookieSid;
    if (ngx_http_ap_filter_get_cookie(r, &cookie_value) == NGX_DECLINED)
{
        cookieSid.createCookie(reinterpret_cast(cglcf->salt.data),
cglcf->salt.len, 
            reinterpret_cast(r->args.data), r->args.len); 
    } else {
        cookieSid = ApCookie_t(reinterpret_cast(cookie_value.data),
cookie_value.len, 
                reinterpret_cast(cglcf->salt.data), cglcf->salt.len, 
                  reinterpret_cast(r->args.data), r->args.len);
    }       // else

    ngx_str_t cookieString;
    ngx_str_set(&cookieString,
cookieSid.returnCookie(&(cookieString.len)));

    ngx_int_t n = ngx_http_ap_filter_set_cookie(r, &cookieString, ctx);
   
    if (n != NGX_OK) {
        return NGX_ERROR;

    }
   
    return NGX_OK; 
   
}

Posted at Nginx Forum: http://forum.nginx.org/read.php?2,169118,169118#msg-169118




More information about the nginx mailing list