diff --git a/src/http/ngx_http.h b/src/http/ngx_http.h index d4dc1bd94..0acc23494 100644 --- a/src/http/ngx_http.h +++ b/src/http/ngx_http.h @@ -105,6 +105,8 @@ ngx_int_t ngx_http_parse_header_line(ngx_http_request_t *r, ngx_buf_t *b, ngx_uint_t allow_underscores); ngx_int_t ngx_http_parse_multi_header_lines(ngx_array_t *headers, ngx_str_t *name, ngx_str_t *value); +ngx_int_t ngx_http_parse_set_cookie_lines(ngx_array_t *headers, + ngx_str_t *name, ngx_str_t *value); ngx_int_t ngx_http_arg(ngx_http_request_t *r, u_char *name, size_t len, ngx_str_t *value); void ngx_http_split_args(ngx_http_request_t *r, ngx_str_t *uri, diff --git a/src/http/ngx_http_parse.c b/src/http/ngx_http_parse.c index 02b4a0fd1..f28786946 100644 --- a/src/http/ngx_http_parse.c +++ b/src/http/ngx_http_parse.c @@ -1984,6 +1984,57 @@ ngx_http_parse_multi_header_lines(ngx_array_t *headers, ngx_str_t *name, } +ngx_int_t +ngx_http_parse_set_cookie_lines(ngx_array_t *headers, ngx_str_t *name, + ngx_str_t *value) +{ + ngx_uint_t i; + u_char *start, *last, *end; + ngx_table_elt_t **h; + + h = headers->elts; + + for (i = 0; i < headers->nelts; i++) { + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, headers->pool->log, 0, + "parse header: \"%V: %V\"", &h[i]->key, &h[i]->value); + + if (name->len >= h[i]->value.len) { + continue; + } + + start = h[i]->value.data; + end = h[i]->value.data + h[i]->value.len; + + if (ngx_strncasecmp(start, name->data, name->len) != 0) { + continue; + } + + for (start += name->len; start < end && *start == ' '; start++) { + /* void */ + } + + if (start == end || *start++ != '=') { + /* the invalid header value */ + continue; + } + + while (start < end && *start == ' ') { start++; } + + for (last = start; last < end && *last != ';'; last++) { + /* void */ + } + + value->len = last - start; + value->data = start; + + return i; + } + + return NGX_DECLINED; +} + + ngx_int_t ngx_http_arg(ngx_http_request_t *r, u_char *name, size_t len, ngx_str_t *value) { diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c index af77e50e7..a64538309 100644 --- a/src/http/ngx_http_upstream.c +++ b/src/http/ngx_http_upstream.c @@ -216,7 +216,8 @@ ngx_http_upstream_header_t ngx_http_upstream_headers_in[] = { ngx_http_upstream_rewrite_refresh, 0, 0 }, { ngx_string("Set-Cookie"), - ngx_http_upstream_process_set_cookie, 0, + ngx_http_upstream_process_set_cookie, + offsetof(ngx_http_upstream_headers_in_t, cookies), ngx_http_upstream_rewrite_set_cookie, 0, 1 }, { ngx_string("Content-Disposition"), @@ -3731,11 +3732,28 @@ static ngx_int_t ngx_http_upstream_process_set_cookie(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset) { -#if (NGX_HTTP_CACHE) - ngx_http_upstream_t *u; + ngx_array_t *pa; + ngx_table_elt_t **ph; + ngx_http_upstream_t *u; u = r->upstream; + pa = &u->headers_in.cookies; + if (pa->elts == NULL) { + if (ngx_array_init(pa, r->pool, 1, sizeof(ngx_table_elt_t *)) != NGX_OK) + { + return NGX_ERROR; + } + } + + ph = ngx_array_push(pa); + if (ph == NULL) { + return NGX_ERROR; + } + + *ph = h; + +#if (NGX_HTTP_CACHE) if (!(u->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_SET_COOKIE)) { u->cacheable = 0; } @@ -4657,6 +4675,40 @@ ngx_http_upstream_header_variable(ngx_http_request_t *r, } +ngx_int_t +ngx_http_upstream_cookie_variable(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + ngx_str_t *name = (ngx_str_t *) data; + + ngx_str_t cookie, s; + + if (r->upstream == NULL) { + v->not_found = 1; + return NGX_OK; + } + + s.len = name->len - (sizeof("upstream_cookie_") - 1); + s.data = name->data + sizeof("upstream_cookie_") - 1; + + if (ngx_http_parse_set_cookie_lines(&r->upstream->headers_in.cookies, + &s, &cookie) + == NGX_DECLINED) + { + v->not_found = 1; + return NGX_OK; + } + + v->len = cookie.len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = cookie.data; + + return NGX_OK; +} + + #if (NGX_HTTP_CACHE) ngx_int_t diff --git a/src/http/ngx_http_upstream.h b/src/http/ngx_http_upstream.h index b8998ced8..50b3e6367 100644 --- a/src/http/ngx_http_upstream.h +++ b/src/http/ngx_http_upstream.h @@ -248,6 +248,7 @@ typedef struct { off_t content_length_n; ngx_array_t cache_control; + ngx_array_t cookies; unsigned connection_close:1; unsigned chunked:1; @@ -363,6 +364,8 @@ typedef struct { } ngx_http_upstream_param_t; +ngx_int_t ngx_http_upstream_cookie_variable(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); ngx_int_t ngx_http_upstream_header_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); diff --git a/src/http/ngx_http_variables.c b/src/http/ngx_http_variables.c index f61862297..5bd6b96bd 100644 --- a/src/http/ngx_http_variables.c +++ b/src/http/ngx_http_variables.c @@ -613,6 +613,17 @@ ngx_http_get_variable(ngx_http_request_t *r, ngx_str_t *name, ngx_uint_t key) return NULL; } + if (ngx_strncmp(name->data, "upstream_cookie_", 16) == 0) { + + if (ngx_http_upstream_cookie_variable(r, vv, (uintptr_t) name) + == NGX_OK) + { + return vv; + } + + return NULL; + } + if (ngx_strncmp(name->data, "arg_", 4) == 0) { if (ngx_http_variable_argument(r, vv, (uintptr_t) name) == NGX_OK) { @@ -2528,6 +2539,14 @@ ngx_http_variables_init_vars(ngx_conf_t *cf) continue; } + if (ngx_strncmp(v[i].name.data, "upstream_cookie_", 16) == 0) { + v[i].get_handler = ngx_http_upstream_cookie_variable; + v[i].data = (uintptr_t) &v[i].name; + v[i].flags = NGX_HTTP_VAR_NOCACHEABLE; + + continue; + } + if (ngx_strncmp(v[i].name.data, "arg_", 4) == 0) { v[i].get_handler = ngx_http_variable_argument; v[i].data = (uintptr_t) &v[i].name;