summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIgor Sysoev <igor@sysoev.ru>2006-03-21 08:17:50 +0000
committerJonathan Kolb <jon@b0g.us>2006-03-21 08:17:50 +0000
commit437664267d265bc54e9c2e8f0da138a11bc6a9c8 (patch)
tree2c345def8bbf9ab5bd0f53c24c2e0e5a09a2ab7f
parentf57d14e9495647d319513ede96aa27140d34152b (diff)
downloadnginx-437664267d265bc54e9c2e8f0da138a11bc6a9c8.tar.gz
Changes with nginx 0.3.34 21 Mar 2006v0.3.34
*) Feature: the "add_header" directive supports the variables.
-rw-r--r--CHANGES5
-rw-r--r--CHANGES.ru5
-rw-r--r--src/core/nginx.h2
-rw-r--r--src/http/modules/ngx_http_headers_filter_module.c68
-rw-r--r--src/http/ngx_http_core_module.c1
-rw-r--r--src/http/ngx_http_parse.c1
-rw-r--r--src/http/ngx_http_parse.c.orig1166
7 files changed, 1236 insertions, 12 deletions
diff --git a/CHANGES b/CHANGES
index 29c689c69..afa60b62c 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,4 +1,9 @@
+Changes with nginx 0.3.34 21 Mar 2006
+
+ *) Feature: the "add_header" directive supports the variables.
+
+
Changes with nginx 0.3.33 15 Mar 2006
*) Feature: the "http_503" parameter of the "proxy_next_upstream" or
diff --git a/CHANGES.ru b/CHANGES.ru
index 9e4442b1f..012065570 100644
--- a/CHANGES.ru
+++ b/CHANGES.ru
@@ -1,4 +1,9 @@
+Изменения в nginx 0.3.34 21.03.2006
+
+ *) Добавление: директива add_header поддерживает переменные.
+
+
Изменения в nginx 0.3.33 15.03.2006
*) Добавление: параметр http_503 в директивах proxy_next_upstream или
diff --git a/src/core/nginx.h b/src/core/nginx.h
index c5842582a..ebd959d44 100644
--- a/src/core/nginx.h
+++ b/src/core/nginx.h
@@ -8,7 +8,7 @@
#define _NGINX_H_INCLUDED_
-#define NGINX_VER "nginx/0.3.33"
+#define NGINX_VER "nginx/0.3.34"
#define NGINX_VAR "NGINX"
#define NGX_OLDPID_EXT ".oldbin"
diff --git a/src/http/modules/ngx_http_headers_filter_module.c b/src/http/modules/ngx_http_headers_filter_module.c
index b0bd0a5ce..8ced8e024 100644
--- a/src/http/modules/ngx_http_headers_filter_module.c
+++ b/src/http/modules/ngx_http_headers_filter_module.c
@@ -10,9 +10,16 @@
typedef struct {
- time_t expires;
- ngx_str_t cache_control;
- ngx_array_t *headers;
+ ngx_table_elt_t value;
+ ngx_array_t *lengths;
+ ngx_array_t *values;
+} ngx_http_header_val_t;
+
+
+typedef struct {
+ time_t expires;
+ ngx_str_t cache_control;
+ ngx_array_t *headers;
} ngx_http_headers_conf_t;
@@ -92,7 +99,8 @@ ngx_http_headers_filter(ngx_http_request_t *r)
{
size_t len;
ngx_uint_t i;
- ngx_table_elt_t *expires, *cc, **ccp, *h, *out;
+ ngx_table_elt_t *expires, *cc, **ccp, *out;
+ ngx_http_header_val_t *h;
ngx_http_headers_conf_t *conf;
if ((r->headers_out.status != NGX_HTTP_OK
@@ -242,7 +250,20 @@ ngx_http_headers_filter(ngx_http_request_t *r)
return NGX_ERROR;
}
- *out = h[i];
+ out->hash = h[i].value.hash;
+ out->key = h[i].value.key;
+
+ if (h[i].lengths == NULL) {
+ out->value = h[i].value.value;
+ continue;
+ }
+
+ if (ngx_http_script_run(r, &out->value, h[i].lengths->elts, 0,
+ h[i].values->elts)
+ == NULL)
+ {
+ return NGX_ERROR;
+ }
}
}
@@ -368,8 +389,10 @@ ngx_http_headers_add(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_headers_conf_t *hcf = conf;
- ngx_str_t *value;
- ngx_table_elt_t *h;
+ ngx_int_t n;
+ ngx_str_t *value;
+ ngx_http_header_val_t *h;
+ ngx_http_script_compile_t sc;
value = cf->args->elts;
@@ -379,7 +402,8 @@ ngx_http_headers_add(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
}
if (hcf->headers == NULL) {
- hcf->headers = ngx_array_create(cf->pool, 1, sizeof(ngx_table_elt_t));
+ hcf->headers = ngx_array_create(cf->pool, 1,
+ sizeof(ngx_http_header_val_t));
if (hcf->headers == NULL) {
return NGX_CONF_ERROR;
}
@@ -390,9 +414,31 @@ ngx_http_headers_add(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
return NGX_CONF_ERROR;
}
- h->hash = 1;
- h->key = value[1];
- h->value = value[2];
+ h->value.hash = 1;
+ h->value.key = value[1];
+ h->value.value = value[2];
+ h->lengths = NULL;
+ h->values = NULL;
+
+ n = ngx_http_script_variables_count(&value[2]);
+
+ if (n == 0) {
+ return NGX_CONF_OK;
+ }
+
+ ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
+
+ sc.cf = cf;
+ sc.source = &value[2];
+ sc.lengths = &h->lengths;
+ sc.values = &h->values;
+ sc.variables = n;
+ sc.complete_lengths = 1;
+ sc.complete_values = 1;
+
+ if (ngx_http_script_compile(&sc) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
return NGX_CONF_OK;
}
diff --git a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c
index 8e9d0b3f0..6e55c4074 100644
--- a/src/http/ngx_http_core_module.c
+++ b/src/http/ngx_http_core_module.c
@@ -2012,6 +2012,7 @@ ngx_http_core_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
if (conf->root.data == NULL) {
+ conf->alias = prev->alias;
conf->root = prev->root;
conf->root_lengths = prev->root_lengths;
conf->root_values = prev->root_values;
diff --git a/src/http/ngx_http_parse.c b/src/http/ngx_http_parse.c
index 2ec6979d0..91722881e 100644
--- a/src/http/ngx_http_parse.c
+++ b/src/http/ngx_http_parse.c
@@ -689,6 +689,7 @@ ngx_http_parse_header_line(ngx_http_request_t *r, ngx_buf_t *b)
default:
return NGX_HTTP_PARSE_INVALID_HEADER;
}
+ break;
/* end of header */
case sw_header_almost_done:
diff --git a/src/http/ngx_http_parse.c.orig b/src/http/ngx_http_parse.c.orig
new file mode 100644
index 000000000..2ec6979d0
--- /dev/null
+++ b/src/http/ngx_http_parse.c.orig
@@ -0,0 +1,1166 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+ngx_int_t
+ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b)
+{
+ u_char c, ch, *p, *m;
+ enum {
+ sw_start = 0,
+ sw_method,
+ sw_space_after_method,
+ sw_spaces_before_uri,
+ sw_schema,
+ sw_schema_slash,
+ sw_schema_slash_slash,
+ sw_host,
+ sw_port,
+ sw_after_slash_in_uri,
+ sw_check_uri,
+ sw_uri,
+ sw_http_09,
+ sw_http_H,
+ sw_http_HT,
+ sw_http_HTT,
+ sw_http_HTTP,
+ sw_first_major_digit,
+ sw_major_digit,
+ sw_first_minor_digit,
+ sw_minor_digit,
+ sw_almost_done
+ } state;
+
+ state = r->state;
+
+ for (p = b->pos; p < b->last; p++) {
+ ch = *p;
+
+ /* gcc 2.95.2 and msvc 6.0 compile this switch as an jump table */
+
+ switch (state) {
+
+ /* HTTP methods: GET, HEAD, POST */
+ case sw_start:
+ r->request_start = p;
+
+ if (ch == CR || ch == LF) {
+ break;
+ }
+
+ if (ch < 'A' || ch > 'Z') {
+ return NGX_HTTP_PARSE_INVALID_METHOD;
+ }
+
+ state = sw_method;
+ break;
+
+ case sw_method:
+ if (ch == ' ') {
+ r->method_end = p - 1;
+ m = r->request_start;
+
+ if (p - m == 3) {
+
+ if (m[0] == 'G' && m[1] == 'E' && m[2] == 'T') {
+ r->method = NGX_HTTP_GET;
+ }
+
+ } else if (p - m == 4) {
+
+ if (m[0] == 'P' && m[1] == 'O'
+ && m[2] == 'S' && m[3] == 'T')
+ {
+ r->method = NGX_HTTP_POST;
+
+ } else if (m[0] == 'H' && m[1] == 'E'
+ && m[2] == 'A' && m[3] == 'D')
+ {
+ r->method = NGX_HTTP_HEAD;
+ }
+ }
+
+ state = sw_spaces_before_uri;
+ break;
+ }
+
+ if (ch < 'A' || ch > 'Z') {
+ return NGX_HTTP_PARSE_INVALID_METHOD;
+ }
+
+ break;
+
+ /* single space after method */
+ case sw_space_after_method:
+ switch (ch) {
+ case ' ':
+ state = sw_spaces_before_uri;
+ break;
+ default:
+ return NGX_HTTP_PARSE_INVALID_METHOD;
+ }
+ break;
+
+ /* space* before URI */
+ case sw_spaces_before_uri:
+
+ c = (u_char) (ch | 0x20);
+ if (c >= 'a' && c <= 'z') {
+ r->schema_start = p;
+ state = sw_schema;
+ break;
+ }
+
+ switch (ch) {
+ case '/':
+ r->uri_start = p;
+ state = sw_after_slash_in_uri;
+ break;
+ case ' ':
+ break;
+ default:
+ return NGX_HTTP_PARSE_INVALID_REQUEST;
+ }
+ break;
+
+ case sw_schema:
+
+ c = (u_char) (ch | 0x20);
+ if (c >= 'a' && c <= 'z') {
+ break;
+ }
+
+ switch (ch) {
+ case ':':
+ r->schema_end = p;
+ state = sw_schema_slash;
+ break;
+ default:
+ return NGX_HTTP_PARSE_INVALID_REQUEST;
+ }
+ break;
+
+ case sw_schema_slash:
+ switch (ch) {
+ case '/':
+ state = sw_schema_slash_slash;
+ break;
+ default:
+ return NGX_HTTP_PARSE_INVALID_REQUEST;
+ }
+ break;
+
+ case sw_schema_slash_slash:
+ switch (ch) {
+ case '/':
+ r->host_start = p;
+ state = sw_host;
+ break;
+ default:
+ return NGX_HTTP_PARSE_INVALID_REQUEST;
+ }
+ break;
+
+ case sw_host:
+
+ c = (u_char) (ch | 0x20);
+ if (c >= 'a' && c <= 'z') {
+ break;
+ }
+
+ if ((ch >= '0' && ch <= '9') || ch == '.' || ch == '-')
+ {
+ break;
+ }
+
+ switch (ch) {
+ case ':':
+ r->host_end = p;
+ state = sw_port;
+ break;
+ case '/':
+ r->host_end = p;
+ r->uri_start = p;
+ state = sw_after_slash_in_uri;
+ break;
+ default:
+ return NGX_HTTP_PARSE_INVALID_REQUEST;
+ }
+ break;
+
+ case sw_port:
+ if (ch >= '0' && ch <= '9') {
+ break;
+ }
+
+ switch (ch) {
+ case '/':
+ r->port_end = p;
+ r->uri_start = p;
+ state = sw_after_slash_in_uri;
+ break;
+ default:
+ return NGX_HTTP_PARSE_INVALID_REQUEST;
+ }
+ break;
+
+ /* check "/.", "//", "%", and "\" (Win32) in URI */
+ case sw_after_slash_in_uri:
+
+ c = (u_char) (ch | 0x20);
+ if (c >= 'a' && c <= 'z') {
+ state = sw_check_uri;
+ break;
+ }
+
+ if (ch >= '0' && ch <= '9') {
+ state = sw_check_uri;
+ break;
+ }
+
+ switch (ch) {
+ case ' ':
+ r->uri_end = p;
+ state = sw_http_09;
+ break;
+ case CR:
+ r->uri_end = p;
+ r->http_minor = 9;
+ state = sw_almost_done;
+ break;
+ case LF:
+ r->uri_end = p;
+ r->http_minor = 9;
+ goto done;
+ case '.':
+ r->complex_uri = 1;
+ state = sw_uri;
+ break;
+ case '%':
+ r->quoted_uri = 1;
+ state = sw_uri;
+ break;
+ case '/':
+ r->complex_uri = 1;
+ state = sw_uri;
+ break;
+#if (NGX_WIN32)
+ case '\\':
+ r->complex_uri = 1;
+ state = sw_uri;
+ break;
+#endif
+ case '?':
+ r->args_start = p + 1;
+ state = sw_uri;
+ break;
+ case '+':
+ r->plus_in_uri = 1;
+ break;
+ case '\0':
+ r->zero_in_uri = 1;
+ break;
+ default:
+ state = sw_check_uri;
+ break;
+ }
+ break;
+
+ /* check "/", "%" and "\" (Win32) in URI */
+ case sw_check_uri:
+
+ c = (u_char) (ch | 0x20);
+ if (c >= 'a' && c <= 'z') {
+ break;
+ }
+
+ if (ch >= '0' && ch <= '9') {
+ break;
+ }
+
+ switch (ch) {
+ case '/':
+ r->uri_ext = NULL;
+ state = sw_after_slash_in_uri;
+ break;
+ case '.':
+ r->uri_ext = p + 1;
+ break;
+ case ' ':
+ r->uri_end = p;
+ state = sw_http_09;
+ break;
+ case CR:
+ r->uri_end = p;
+ r->http_minor = 9;
+ state = sw_almost_done;
+ break;
+ case LF:
+ r->uri_end = p;
+ r->http_minor = 9;
+ goto done;
+#if (NGX_WIN32)
+ case '\\':
+ r->complex_uri = 1;
+ state = sw_after_slash_in_uri;
+ break;
+#endif
+ case '%':
+ r->quoted_uri = 1;
+ state = sw_uri;
+ break;
+ case '+':
+ r->plus_in_uri = 1;
+ break;
+ case '?':
+ r->args_start = p + 1;
+ state = sw_uri;
+ break;
+ case '\0':
+ r->zero_in_uri = 1;
+ break;
+ }
+ break;
+
+ /* URI */
+ case sw_uri:
+ switch (ch) {
+ case ' ':
+ r->uri_end = p;
+ state = sw_http_09;
+ break;
+ case CR:
+ r->uri_end = p;
+ r->http_minor = 9;
+ state = sw_almost_done;
+ break;
+ case LF:
+ r->uri_end = p;
+ r->http_minor = 9;
+ goto done;
+ case '+':
+ r->plus_in_uri = 1;
+ break;
+ case '\0':
+ r->zero_in_uri = 1;
+ break;
+ }
+ break;
+
+ /* space+ after URI */
+ case sw_http_09:
+ switch (ch) {
+ case ' ':
+ break;
+ case CR:
+ r->http_minor = 9;
+ state = sw_almost_done;
+ break;
+ case LF:
+ r->http_minor = 9;
+ goto done;
+ case 'H':
+ r->http_protocol.data = p;
+ state = sw_http_H;
+ break;
+ default:
+ return NGX_HTTP_PARSE_INVALID_REQUEST;
+ }
+ break;
+
+ case sw_http_H:
+ switch (ch) {
+ case 'T':
+ state = sw_http_HT;
+ break;
+ default:
+ return NGX_HTTP_PARSE_INVALID_REQUEST;
+ }
+ break;
+
+ case sw_http_HT:
+ switch (ch) {
+ case 'T':
+ state = sw_http_HTT;
+ break;
+ default:
+ return NGX_HTTP_PARSE_INVALID_REQUEST;
+ }
+ break;
+
+ case sw_http_HTT:
+ switch (ch) {
+ case 'P':
+ state = sw_http_HTTP;
+ break;
+ default:
+ return NGX_HTTP_PARSE_INVALID_REQUEST;
+ }
+ break;
+
+ case sw_http_HTTP:
+ switch (ch) {
+ case '/':
+ state = sw_first_major_digit;
+ break;
+ default:
+ return NGX_HTTP_PARSE_INVALID_REQUEST;
+ }
+ break;
+
+ /* first digit of major HTTP version */
+ case sw_first_major_digit:
+ if (ch < '1' || ch > '9') {
+ return NGX_HTTP_PARSE_INVALID_REQUEST;
+ }
+
+ r->http_major = ch - '0';
+ state = sw_major_digit;
+ break;
+
+ /* major HTTP version or dot */
+ case sw_major_digit:
+ if (ch == '.') {
+ state = sw_first_minor_digit;
+ break;
+ }
+
+ if (ch < '0' || ch > '9') {
+ return NGX_HTTP_PARSE_INVALID_REQUEST;
+ }
+
+ r->http_major = r->http_major * 10 + ch - '0';
+ break;
+
+ /* first digit of minor HTTP version */
+ case sw_first_minor_digit:
+ if (ch < '0' || ch > '9') {
+ return NGX_HTTP_PARSE_INVALID_REQUEST;
+ }
+
+ r->http_minor = ch - '0';
+ state = sw_minor_digit;
+ break;
+
+ /* minor HTTP version or end of request line */
+ case sw_minor_digit:
+ if (ch == CR) {
+ state = sw_almost_done;
+ break;
+ }
+
+ if (ch == LF) {
+ goto done;
+ }
+
+ if (ch < '0' || ch > '9') {
+ return NGX_HTTP_PARSE_INVALID_REQUEST;
+ }
+
+ r->http_minor = r->http_minor * 10 + ch - '0';
+ break;
+
+ /* end of request line */
+ case sw_almost_done:
+ r->request_end = p - 1;
+ switch (ch) {
+ case LF:
+ goto done;
+ default:
+ return NGX_HTTP_PARSE_INVALID_REQUEST;
+ }
+ }
+ }
+
+ b->pos = p;
+ r->state = state;
+
+ return NGX_AGAIN;
+
+done:
+
+ b->pos = p + 1;
+
+ if (r->request_end == NULL) {
+ r->request_end = p;
+ }
+
+ r->http_version = r->http_major * 1000 + r->http_minor;
+ r->state = sw_start;
+
+ if (r->http_version == 9 && r->method != NGX_HTTP_GET) {
+ return NGX_HTTP_PARSE_INVALID_09_METHOD;
+ }
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_http_parse_header_line(ngx_http_request_t *r, ngx_buf_t *b)
+{
+ u_char c, ch, *p;
+ ngx_uint_t hash;
+ enum {
+ sw_start = 0,
+ sw_name,
+ sw_space_before_value,
+ sw_value,
+ sw_space_after_value,
+ sw_ignore_line,
+ sw_almost_done,
+ sw_header_almost_done
+ } state;
+
+ state = r->state;
+ hash = r->header_hash;
+
+ for (p = b->pos; p < b->last; p++) {
+ ch = *p;
+
+ switch (state) {
+
+ /* first char */
+ case sw_start:
+ r->invalid_header = 0;
+
+ switch (ch) {
+ case CR:
+ r->header_end = p;
+ state = sw_header_almost_done;
+ break;
+ case LF:
+ r->header_end = p;
+ goto header_done;
+ default:
+ state = sw_name;
+ r->header_name_start = p;
+
+ c = (u_char) (ch | 0x20);
+ if (c >= 'a' && c <= 'z') {
+ hash = c;
+ break;
+ }
+
+ if (ch >= '0' && ch <= '9') {
+ hash = ch;
+ break;
+ }
+
+ r->invalid_header = 1;
+
+ break;
+
+ }
+ break;
+
+ /* header name */
+ case sw_name:
+ c = (u_char) (ch | 0x20);
+ if (c >= 'a' && c <= 'z') {
+ hash += c;
+ break;
+ }
+
+ if (ch == ':') {
+ r->header_name_end = p;
+ state = sw_space_before_value;
+ break;
+ }
+
+ if (ch == '-') {
+ hash += ch;
+ break;
+ }
+
+ if (ch >= '0' && ch <= '9') {
+ hash += ch;
+ break;
+ }
+
+ if (ch == CR) {
+ r->header_name_end = p;
+ r->header_start = p;
+ r->header_end = p;
+ state = sw_almost_done;
+ break;
+ }
+
+ if (ch == LF) {
+ r->header_name_end = p;
+ r->header_start = p;
+ r->header_end = p;
+ goto done;
+ }
+
+ /* IIS may send the duplicate "HTTP/1.1 ..." lines */
+ if (ch == '/'
+ && r->upstream
+ && p - r->header_name_start == 4
+ && ngx_strncmp(r->header_name_start, "HTTP", 4) == 0)
+ {
+ state = sw_ignore_line;
+ break;
+ }
+
+ r->invalid_header = 1;
+
+ break;
+
+ /* space* before header value */
+ case sw_space_before_value:
+ switch (ch) {
+ case ' ':
+ break;
+ case CR:
+ r->header_start = p;
+ r->header_end = p;
+ state = sw_almost_done;
+ break;
+ case LF:
+ r->header_start = p;
+ r->header_end = p;
+ goto done;
+ default:
+ r->header_start = p;
+ state = sw_value;
+ break;
+ }
+ break;
+
+ /* header value */
+ case sw_value:
+ switch (ch) {
+ case ' ':
+ r->header_end = p;
+ state = sw_space_after_value;
+ break;
+ case CR:
+ r->header_end = p;
+ state = sw_almost_done;
+ break;
+ case LF:
+ r->header_end = p;
+ goto done;
+ }
+ break;
+
+ /* space* before end of header line */
+ case sw_space_after_value:
+ switch (ch) {
+ case ' ':
+ break;
+ case CR:
+ state = sw_almost_done;
+ break;
+ case LF:
+ goto done;
+ default:
+ state = sw_value;
+ break;
+ }
+ break;
+
+ /* ignore header line */
+ case sw_ignore_line:
+ switch (ch) {
+ case LF:
+ state = sw_start;
+ break;
+ default:
+ break;
+ }
+ break;
+
+ /* end of header line */
+ case sw_almost_done:
+ switch (ch) {
+ case CR:
+ break;
+ case LF:
+ goto done;
+ default:
+ return NGX_HTTP_PARSE_INVALID_HEADER;
+ }
+
+ /* end of header */
+ case sw_header_almost_done:
+ switch (ch) {
+ case LF:
+ goto header_done;
+ default:
+ return NGX_HTTP_PARSE_INVALID_HEADER;
+ }
+ }
+ }
+
+ b->pos = p;
+ r->state = state;
+ r->header_hash = hash;
+
+ return NGX_AGAIN;
+
+done:
+
+ b->pos = p + 1;
+ r->state = sw_start;
+ r->header_hash = hash;
+
+ return NGX_OK;
+
+header_done:
+
+ b->pos = p + 1;
+ r->state = sw_start;
+
+ return NGX_HTTP_PARSE_HEADER_DONE;
+}
+
+
+ngx_int_t
+ngx_http_parse_complex_uri(ngx_http_request_t *r)
+{
+ u_char c, ch, decoded, *p, *u;
+ enum {
+ sw_usual = 0,
+ sw_slash,
+ sw_dot,
+ sw_dot_dot,
+#if (NGX_WIN32)
+ sw_dot_dot_dot,
+#endif
+ sw_quoted,
+ sw_quoted_second
+ } state, quoted_state;
+
+#if (NGX_SUPPRESS_WARN)
+ decoded = '\0';
+ quoted_state = sw_usual;
+#endif
+
+ state = sw_usual;
+ p = r->uri_start;
+ u = r->uri.data;
+ r->uri_ext = NULL;
+ r->args_start = NULL;
+
+ ch = *p++;
+
+ while (p <= r->uri_end) {
+
+ /*
+ * we use "ch = *p++" inside the cycle, but this operation is safe,
+ * because after the URI there is always at least one charcter:
+ * the line feed
+ */
+
+ ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "s:%d in:'%Xd:%c', out:'%c'", state, ch, ch, *u);
+
+ switch (state) {
+
+ case sw_usual:
+ switch(ch) {
+#if (NGX_WIN32)
+ case '\\':
+ r->uri_ext = NULL;
+
+ if (p == r->uri_start + r->uri.len) {
+
+ /*
+ * we omit the last "\" to cause redirect because
+ * the browsers do not treat "\" as "/" in relative URL path
+ */
+
+ break;
+ }
+
+ state = sw_slash;
+ *u++ = '/';
+ break;
+#endif
+ case '/':
+ r->uri_ext = NULL;
+ state = sw_slash;
+ *u++ = ch;
+ break;
+ case '%':
+ quoted_state = state;
+ state = sw_quoted;
+ break;
+ case '?':
+ r->args_start = p;
+ goto done;
+ case '.':
+ r->uri_ext = u + 1;
+ *u++ = ch;
+ break;
+ default:
+ *u++ = ch;
+ break;
+ }
+ ch = *p++;
+ break;
+
+ case sw_slash:
+ switch(ch) {
+#if (NGX_WIN32)
+ case '\\':
+#endif
+ case '/':
+ break;
+ case '.':
+ state = sw_dot;
+ *u++ = ch;
+ break;
+ case '%':
+ quoted_state = state;
+ state = sw_quoted;
+ break;
+ case '?':
+ r->args_start = p;
+ goto done;
+ default:
+ state = sw_usual;
+ *u++ = ch;
+ break;
+ }
+ ch = *p++;
+ break;
+
+ case sw_dot:
+ switch(ch) {
+#if (NGX_WIN32)
+ case '\\':
+#endif
+ case '/':
+ state = sw_slash;
+ u--;
+ break;
+ case '.':
+ state = sw_dot_dot;
+ *u++ = ch;
+ break;
+ case '%':
+ quoted_state = state;
+ state = sw_quoted;
+ break;
+ case '?':
+ r->args_start = p;
+ goto done;
+ default:
+ state = sw_usual;
+ *u++ = ch;
+ break;
+ }
+ ch = *p++;
+ break;
+
+ case sw_dot_dot:
+ switch(ch) {
+#if (NGX_WIN32)
+ case '\\':
+#endif
+ case '/':
+ state = sw_slash;
+ u -= 4;
+ if (u < r->uri.data) {
+ return NGX_HTTP_PARSE_INVALID_REQUEST;
+ }
+ while (*(u - 1) != '/') {
+ u--;
+ }
+ break;
+ case '%':
+ quoted_state = state;
+ state = sw_quoted;
+ break;
+ case '?':
+ r->args_start = p;
+ goto done;
+#if (NGX_WIN32)
+ case '.':
+ state = sw_dot_dot_dot;
+ *u++ = ch;
+ break;
+#endif
+ default:
+ state = sw_usual;
+ *u++ = ch;
+ break;
+ }
+ ch = *p++;
+ break;
+
+#if (NGX_WIN32)
+ case sw_dot_dot_dot:
+ switch(ch) {
+ case '\\':
+ case '/':
+ state = sw_slash;
+ u -= 5;
+ if (u < r->uri.data) {
+ return NGX_HTTP_PARSE_INVALID_REQUEST;
+ }
+ while (*u != '/') {
+ u--;
+ }
+ if (u < r->uri.data) {
+ return NGX_HTTP_PARSE_INVALID_REQUEST;
+ }
+ while (*(u - 1) != '/') {
+ u--;
+ }
+ break;
+ case '%':
+ quoted_state = state;
+ state = sw_quoted;
+ break;
+ case '?':
+ r->args_start = p;
+ goto done;
+ default:
+ state = sw_usual;
+ *u++ = ch;
+ break;
+ }
+ ch = *p++;
+ break;
+#endif
+
+ case sw_quoted:
+ if (ch >= '0' && ch <= '9') {
+ decoded = (u_char) (ch - '0');
+ state = sw_quoted_second;
+ ch = *p++;
+ break;
+ }
+
+ c = (u_char) (ch | 0x20);
+ if (c >= 'a' && c <= 'f') {
+ decoded = (u_char) (c - 'a' + 10);
+ state = sw_quoted_second;
+ ch = *p++;
+ break;
+ }
+
+ return NGX_HTTP_PARSE_INVALID_REQUEST;
+
+ case sw_quoted_second:
+ if (ch >= '0' && ch <= '9') {
+ ch = (u_char) ((decoded << 4) + ch - '0');
+
+ if (ch == '%') {
+ state = sw_usual;
+ *u++ = ch;
+ ch = *p++;
+ break;
+ }
+
+ if (ch == '\0') {
+ r->zero_in_uri = 1;
+ *u++ = ch;
+ ch = *p++;
+ }
+
+ state = quoted_state;
+ break;
+ }
+
+ c = (u_char) (ch | 0x20);
+ if (c >= 'a' && c <= 'f') {
+ ch = (u_char) ((decoded << 4) + c - 'a' + 10);
+ if (ch == '?') {
+ *u++ = ch;
+ ch = *p++;
+ }
+ state = quoted_state;
+ break;
+ }
+
+ return NGX_HTTP_PARSE_INVALID_REQUEST;
+ }
+ }
+
+done:
+
+ r->uri.len = u - r->uri.data;
+ r->uri.data[r->uri.len] = '\0';
+
+ if (r->uri_ext) {
+ r->exten.len = u - r->uri_ext;
+ r->exten.data = r->uri_ext;
+ }
+
+ r->uri_ext = NULL;
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_http_parse_unsafe_uri(ngx_http_request_t *r, ngx_str_t *uri,
+ ngx_str_t *args, ngx_uint_t *flags)
+{
+ u_char ch, *p;
+ size_t len;
+
+ len = uri->len;
+ p = uri->data;
+
+ if (len == 0 || p[0] == '?') {
+ goto unsafe;
+ }
+
+ if (p[0] == '.' && len == 3 && p[1] == '.' && (p[2] == '/'
+#if (NGX_WIN32)
+ || p[2] == '\\'
+#endif
+ ))
+ {
+ goto unsafe;
+ }
+
+ for ( /* void */ ; len; len--) {
+
+ ch = *p++;
+
+ if (ch == '?') {
+ args->len = len - 1;
+ args->data = p;
+ uri->len -= len;
+
+ return NGX_OK;
+ }
+
+ if (ch == '\0') {
+ *flags |= NGX_HTTP_ZERO_IN_URI;
+ continue;
+ }
+
+ if (ch != '/'
+#if (NGX_WIN32)
+ && ch != '\\'
+#endif
+ )
+ {
+ continue;
+ }
+
+ if (len > 2) {
+
+ /* detect "/../" */
+
+ if (p[0] == '.' && p[1] == '.' && p[2] == '/') {
+ goto unsafe;
+ }
+
+#if (NGX_WIN32)
+
+ if (p[2] == '\\') {
+ goto unsafe;
+ }
+
+ if (len > 3) {
+
+ /* detect "/.../" */
+
+ if (p[0] == '.' && p[1] == '.' && p[2] == '.'
+ && (p[3] == '/' || p[3] == '\\'))
+ {
+ goto unsafe;
+ }
+ }
+#endif
+ }
+ }
+
+ return NGX_OK;
+
+unsafe:
+
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "unsafe URI \"%V\" was detected", uri);
+
+ return NGX_ERROR;
+}
+
+
+ngx_int_t
+ngx_http_parse_multi_header_lines(ngx_array_t *headers, ngx_str_t *name,
+ ngx_str_t *value)
+{
+ ngx_uint_t i;
+ u_char *start, *last, *end, ch;
+ 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;
+
+ while (start < end) {
+
+ if (ngx_strncasecmp(start, name->data, name->len) != 0) {
+ goto skip;
+ }
+
+ for (start += name->len; start < end && *start == ' '; start++) {
+ /* void */
+ }
+
+ if (value == NULL) {
+ if (start == end || *start == ',') {
+ return i;
+ }
+
+ goto skip;
+ }
+
+ if (start == end || *start++ != '=') {
+ /* the invalid header value */
+ goto skip;
+ }
+
+ while (start < end && *start == ' ') { start++; }
+
+ for (last = start; last < end && *last != ';'; last++) {
+ /* void */
+ }
+
+ value->len = last - start;
+ value->data = start;
+
+ return i;
+
+ skip:
+
+ while (start < end) {
+ ch = *start++;
+ if (ch == ';' || ch == ',') {
+ break;
+ }
+ }
+
+ while (start < end && *start == ' ') { start++; }
+ }
+ }
+
+ return NGX_DECLINED;
+}