diff options
Diffstat (limited to 'app/nginx/src/http/ngx_http_parse.c')
-rw-r--r-- | app/nginx/src/http/ngx_http_parse.c | 2379 |
1 files changed, 2379 insertions, 0 deletions
diff --git a/app/nginx/src/http/ngx_http_parse.c b/app/nginx/src/http/ngx_http_parse.c new file mode 100644 index 0000000..9f99473 --- /dev/null +++ b/app/nginx/src/http/ngx_http_parse.c @@ -0,0 +1,2379 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include <ngx_config.h> +#include <ngx_core.h> +#include <ngx_http.h> + + +static uint32_t usual[] = { + 0xffffdbfe, /* 1111 1111 1111 1111 1101 1011 1111 1110 */ + + /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */ + 0x7fff37d6, /* 0111 1111 1111 1111 0011 0111 1101 0110 */ + + /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */ +#if (NGX_WIN32) + 0xefffffff, /* 1110 1111 1111 1111 1111 1111 1111 1111 */ +#else + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ +#endif + + /* ~}| {zyx wvut srqp onml kjih gfed cba` */ + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff /* 1111 1111 1111 1111 1111 1111 1111 1111 */ +}; + + +#if (NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED) + +#define ngx_str3_cmp(m, c0, c1, c2, c3) \ + *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0) + +#define ngx_str3Ocmp(m, c0, c1, c2, c3) \ + *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0) + +#define ngx_str4cmp(m, c0, c1, c2, c3) \ + *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0) + +#define ngx_str5cmp(m, c0, c1, c2, c3, c4) \ + *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0) \ + && m[4] == c4 + +#define ngx_str6cmp(m, c0, c1, c2, c3, c4, c5) \ + *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0) \ + && (((uint32_t *) m)[1] & 0xffff) == ((c5 << 8) | c4) + +#define ngx_str7_cmp(m, c0, c1, c2, c3, c4, c5, c6, c7) \ + *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0) \ + && ((uint32_t *) m)[1] == ((c7 << 24) | (c6 << 16) | (c5 << 8) | c4) + +#define ngx_str8cmp(m, c0, c1, c2, c3, c4, c5, c6, c7) \ + *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0) \ + && ((uint32_t *) m)[1] == ((c7 << 24) | (c6 << 16) | (c5 << 8) | c4) + +#define ngx_str9cmp(m, c0, c1, c2, c3, c4, c5, c6, c7, c8) \ + *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0) \ + && ((uint32_t *) m)[1] == ((c7 << 24) | (c6 << 16) | (c5 << 8) | c4) \ + && m[8] == c8 + +#else /* !(NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED) */ + +#define ngx_str3_cmp(m, c0, c1, c2, c3) \ + m[0] == c0 && m[1] == c1 && m[2] == c2 + +#define ngx_str3Ocmp(m, c0, c1, c2, c3) \ + m[0] == c0 && m[2] == c2 && m[3] == c3 + +#define ngx_str4cmp(m, c0, c1, c2, c3) \ + m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3 + +#define ngx_str5cmp(m, c0, c1, c2, c3, c4) \ + m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3 && m[4] == c4 + +#define ngx_str6cmp(m, c0, c1, c2, c3, c4, c5) \ + m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3 \ + && m[4] == c4 && m[5] == c5 + +#define ngx_str7_cmp(m, c0, c1, c2, c3, c4, c5, c6, c7) \ + m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3 \ + && m[4] == c4 && m[5] == c5 && m[6] == c6 + +#define ngx_str8cmp(m, c0, c1, c2, c3, c4, c5, c6, c7) \ + m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3 \ + && m[4] == c4 && m[5] == c5 && m[6] == c6 && m[7] == c7 + +#define ngx_str9cmp(m, c0, c1, c2, c3, c4, c5, c6, c7, c8) \ + m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3 \ + && m[4] == c4 && m[5] == c5 && m[6] == c6 && m[7] == c7 && m[8] == c8 + +#endif + + +/* gcc, icc, msvc and others compile these switches as an jump table */ + +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_spaces_before_uri, + sw_schema, + sw_schema_slash, + sw_schema_slash_slash, + sw_host_start, + sw_host, + sw_host_end, + sw_host_ip_literal, + sw_port, + sw_host_http_09, + sw_after_slash_in_uri, + sw_check_uri, + sw_check_uri_http_09, + 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_spaces_after_digit, + sw_almost_done + } state; + + state = r->state; + + for (p = b->pos; p < b->last; p++) { + ch = *p; + + 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') && ch != '_' && ch != '-') { + return NGX_HTTP_PARSE_INVALID_METHOD; + } + + state = sw_method; + break; + + case sw_method: + if (ch == ' ') { + r->method_end = p - 1; + m = r->request_start; + + switch (p - m) { + + case 3: + if (ngx_str3_cmp(m, 'G', 'E', 'T', ' ')) { + r->method = NGX_HTTP_GET; + break; + } + + if (ngx_str3_cmp(m, 'P', 'U', 'T', ' ')) { + r->method = NGX_HTTP_PUT; + break; + } + + break; + + case 4: + if (m[1] == 'O') { + + if (ngx_str3Ocmp(m, 'P', 'O', 'S', 'T')) { + r->method = NGX_HTTP_POST; + break; + } + + if (ngx_str3Ocmp(m, 'C', 'O', 'P', 'Y')) { + r->method = NGX_HTTP_COPY; + break; + } + + if (ngx_str3Ocmp(m, 'M', 'O', 'V', 'E')) { + r->method = NGX_HTTP_MOVE; + break; + } + + if (ngx_str3Ocmp(m, 'L', 'O', 'C', 'K')) { + r->method = NGX_HTTP_LOCK; + break; + } + + } else { + + if (ngx_str4cmp(m, 'H', 'E', 'A', 'D')) { + r->method = NGX_HTTP_HEAD; + break; + } + } + + break; + + case 5: + if (ngx_str5cmp(m, 'M', 'K', 'C', 'O', 'L')) { + r->method = NGX_HTTP_MKCOL; + break; + } + + if (ngx_str5cmp(m, 'P', 'A', 'T', 'C', 'H')) { + r->method = NGX_HTTP_PATCH; + break; + } + + if (ngx_str5cmp(m, 'T', 'R', 'A', 'C', 'E')) { + r->method = NGX_HTTP_TRACE; + break; + } + + break; + + case 6: + if (ngx_str6cmp(m, 'D', 'E', 'L', 'E', 'T', 'E')) { + r->method = NGX_HTTP_DELETE; + break; + } + + if (ngx_str6cmp(m, 'U', 'N', 'L', 'O', 'C', 'K')) { + r->method = NGX_HTTP_UNLOCK; + break; + } + + break; + + case 7: + if (ngx_str7_cmp(m, 'O', 'P', 'T', 'I', 'O', 'N', 'S', ' ')) + { + r->method = NGX_HTTP_OPTIONS; + } + + break; + + case 8: + if (ngx_str8cmp(m, 'P', 'R', 'O', 'P', 'F', 'I', 'N', 'D')) + { + r->method = NGX_HTTP_PROPFIND; + } + + break; + + case 9: + if (ngx_str9cmp(m, + 'P', 'R', 'O', 'P', 'P', 'A', 'T', 'C', 'H')) + { + r->method = NGX_HTTP_PROPPATCH; + } + + break; + } + + state = sw_spaces_before_uri; + break; + } + + if ((ch < 'A' || ch > 'Z') && ch != '_' && ch != '-') { + return NGX_HTTP_PARSE_INVALID_METHOD; + } + + break; + + /* space* before URI */ + case sw_spaces_before_uri: + + if (ch == '/') { + r->uri_start = p; + state = sw_after_slash_in_uri; + break; + } + + c = (u_char) (ch | 0x20); + if (c >= 'a' && c <= 'z') { + r->schema_start = p; + state = sw_schema; + break; + } + + switch (ch) { + 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 '/': + state = sw_host_start; + break; + default: + return NGX_HTTP_PARSE_INVALID_REQUEST; + } + break; + + case sw_host_start: + + r->host_start = p; + + if (ch == '[') { + state = sw_host_ip_literal; + break; + } + + state = sw_host; + + /* fall through */ + + case sw_host: + + c = (u_char) (ch | 0x20); + if (c >= 'a' && c <= 'z') { + break; + } + + if ((ch >= '0' && ch <= '9') || ch == '.' || ch == '-') { + break; + } + + /* fall through */ + + case sw_host_end: + + r->host_end = p; + + switch (ch) { + case ':': + state = sw_port; + break; + case '/': + r->uri_start = p; + state = sw_after_slash_in_uri; + break; + case ' ': + /* + * use single "/" from request line to preserve pointers, + * if request line will be copied to large client buffer + */ + r->uri_start = r->schema_end + 1; + r->uri_end = r->schema_end + 2; + state = sw_host_http_09; + break; + default: + return NGX_HTTP_PARSE_INVALID_REQUEST; + } + break; + + case sw_host_ip_literal: + + if (ch >= '0' && ch <= '9') { + break; + } + + c = (u_char) (ch | 0x20); + if (c >= 'a' && c <= 'z') { + break; + } + + switch (ch) { + case ':': + break; + case ']': + state = sw_host_end; + break; + case '-': + case '.': + case '_': + case '~': + /* unreserved */ + break; + case '!': + case '$': + case '&': + case '\'': + case '(': + case ')': + case '*': + case '+': + case ',': + case ';': + case '=': + /* sub-delims */ + 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; + case ' ': + r->port_end = p; + /* + * use single "/" from request line to preserve pointers, + * if request line will be copied to large client buffer + */ + r->uri_start = r->schema_end + 1; + r->uri_end = r->schema_end + 2; + state = sw_host_http_09; + break; + default: + return NGX_HTTP_PARSE_INVALID_REQUEST; + } + break; + + /* space+ after "http://host[:port] " */ + case sw_host_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; + + + /* check "/.", "//", "%", and "\" (Win32) in URI */ + case sw_after_slash_in_uri: + + if (usual[ch >> 5] & (1U << (ch & 0x1f))) { + state = sw_check_uri; + break; + } + + switch (ch) { + case ' ': + r->uri_end = p; + state = sw_check_uri_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->complex_uri = 1; + state = sw_uri; + break; + case '+': + r->plus_in_uri = 1; + break; + case '\0': + return NGX_HTTP_PARSE_INVALID_REQUEST; + default: + state = sw_check_uri; + break; + } + break; + + /* check "/", "%" and "\" (Win32) in URI */ + case sw_check_uri: + + if (usual[ch >> 5] & (1U << (ch & 0x1f))) { + break; + } + + switch (ch) { + case '/': +#if (NGX_WIN32) + if (r->uri_ext == p) { + r->complex_uri = 1; + state = sw_uri; + break; + } +#endif + 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_check_uri_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->args_start = p + 1; + state = sw_uri; + break; + case '#': + r->complex_uri = 1; + state = sw_uri; + break; + case '+': + r->plus_in_uri = 1; + break; + case '\0': + return NGX_HTTP_PARSE_INVALID_REQUEST; + } + break; + + /* space+ after URI */ + case sw_check_uri_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: + r->space_in_uri = 1; + state = sw_check_uri; + p--; + break; + } + break; + + + /* URI */ + case sw_uri: + + if (usual[ch >> 5] & (1U << (ch & 0x1f))) { + 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; + break; + case '\0': + return NGX_HTTP_PARSE_INVALID_REQUEST; + } + 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: + r->space_in_uri = 1; + state = sw_uri; + p--; + break; + } + 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; + } + + if (r->http_major > 99) { + 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 == ' ') { + state = sw_spaces_after_digit; + break; + } + + if (ch < '0' || ch > '9') { + return NGX_HTTP_PARSE_INVALID_REQUEST; + } + + if (r->http_minor > 99) { + return NGX_HTTP_PARSE_INVALID_REQUEST; + } + + r->http_minor = r->http_minor * 10 + ch - '0'; + break; + + case sw_spaces_after_digit: + switch (ch) { + case ' ': + break; + case CR: + state = sw_almost_done; + break; + case LF: + goto done; + default: + return NGX_HTTP_PARSE_INVALID_REQUEST; + } + 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, + ngx_uint_t allow_underscores) +{ + u_char c, ch, *p; + ngx_uint_t hash, i; + 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; + + /* the last '\0' is not needed because string is zero terminated */ + + static u_char lowcase[] = + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0-\0\0" "0123456789\0\0\0\0\0\0" + "\0abcdefghijklmnopqrstuvwxyz\0\0\0\0\0" + "\0abcdefghijklmnopqrstuvwxyz\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; + + state = r->state; + hash = r->header_hash; + i = r->lowcase_index; + + for (p = b->pos; p < b->last; p++) { + ch = *p; + + switch (state) { + + /* first char */ + case sw_start: + r->header_name_start = p; + 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; + + c = lowcase[ch]; + + if (c) { + hash = ngx_hash(0, c); + r->lowcase_header[0] = c; + i = 1; + break; + } + + if (ch == '_') { + if (allow_underscores) { + hash = ngx_hash(0, ch); + r->lowcase_header[0] = ch; + i = 1; + + } else { + r->invalid_header = 1; + } + + break; + } + + if (ch == '\0') { + return NGX_HTTP_PARSE_INVALID_HEADER; + } + + r->invalid_header = 1; + + break; + + } + break; + + /* header name */ + case sw_name: + c = lowcase[ch]; + + if (c) { + hash = ngx_hash(hash, c); + r->lowcase_header[i++] = c; + i &= (NGX_HTTP_LC_HEADER_LEN - 1); + break; + } + + if (ch == '_') { + if (allow_underscores) { + hash = ngx_hash(hash, ch); + r->lowcase_header[i++] = ch; + i &= (NGX_HTTP_LC_HEADER_LEN - 1); + + } else { + r->invalid_header = 1; + } + + break; + } + + if (ch == ':') { + r->header_name_end = p; + state = sw_space_before_value; + 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; + } + + if (ch == '\0') { + return NGX_HTTP_PARSE_INVALID_HEADER; + } + + 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; + case '\0': + return NGX_HTTP_PARSE_INVALID_HEADER; + 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; + case '\0': + return NGX_HTTP_PARSE_INVALID_HEADER; + } + 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; + case '\0': + return NGX_HTTP_PARSE_INVALID_HEADER; + 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 LF: + goto done; + case CR: + break; + default: + return NGX_HTTP_PARSE_INVALID_HEADER; + } + break; + + /* 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; + r->lowcase_index = i; + + return NGX_AGAIN; + +done: + + b->pos = p + 1; + r->state = sw_start; + r->header_hash = hash; + r->lowcase_index = i; + + 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_uri(ngx_http_request_t *r) +{ + u_char *p, ch; + enum { + sw_start = 0, + sw_after_slash_in_uri, + sw_check_uri, + sw_uri + } state; + + state = sw_start; + + for (p = r->uri_start; p != r->uri_end; p++) { + + ch = *p; + + switch (state) { + + case sw_start: + + if (ch != '/') { + return NGX_ERROR; + } + + state = sw_after_slash_in_uri; + break; + + /* check "/.", "//", "%", and "\" (Win32) in URI */ + case sw_after_slash_in_uri: + + if (usual[ch >> 5] & (1U << (ch & 0x1f))) { + state = sw_check_uri; + break; + } + + switch (ch) { + case ' ': + r->space_in_uri = 1; + state = sw_check_uri; + break; + 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->complex_uri = 1; + state = sw_uri; + break; + case '+': + r->plus_in_uri = 1; + break; + default: + state = sw_check_uri; + break; + } + break; + + /* check "/", "%" and "\" (Win32) in URI */ + case sw_check_uri: + + if (usual[ch >> 5] & (1U << (ch & 0x1f))) { + break; + } + + switch (ch) { + case '/': +#if (NGX_WIN32) + if (r->uri_ext == p) { + r->complex_uri = 1; + state = sw_uri; + break; + } +#endif + r->uri_ext = NULL; + state = sw_after_slash_in_uri; + break; + case '.': + r->uri_ext = p + 1; + break; + case ' ': + r->space_in_uri = 1; + break; +#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->args_start = p + 1; + state = sw_uri; + break; + case '#': + r->complex_uri = 1; + state = sw_uri; + break; + case '+': + r->plus_in_uri = 1; + break; + } + break; + + /* URI */ + case sw_uri: + + if (usual[ch >> 5] & (1U << (ch & 0x1f))) { + break; + } + + switch (ch) { + case ' ': + r->space_in_uri = 1; + break; + case '#': + r->complex_uri = 1; + break; + } + break; + } + } + + return NGX_OK; +} + + +ngx_int_t +ngx_http_parse_complex_uri(ngx_http_request_t *r, ngx_uint_t merge_slashes) +{ + u_char c, ch, decoded, *p, *u; + enum { + sw_usual = 0, + sw_slash, + sw_dot, + sw_dot_dot, + 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 character: + * the line feed + */ + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "s:%d in:'%Xd:%c'", state, ch, ch); + + switch (state) { + + case sw_usual: + + if (usual[ch >> 5] & (1U << (ch & 0x1f))) { + *u++ = ch; + ch = *p++; + break; + } + + switch (ch) { +#if (NGX_WIN32) + case '\\': + if (u - 2 >= r->uri.data + && *(u - 1) == '.' && *(u - 2) != '.') + { + u--; + } + + 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 '/': +#if (NGX_WIN32) + if (u - 2 >= r->uri.data + && *(u - 1) == '.' && *(u - 2) != '.') + { + u--; + } +#endif + r->uri_ext = NULL; + state = sw_slash; + *u++ = ch; + break; + case '%': + quoted_state = state; + state = sw_quoted; + break; + case '?': + r->args_start = p; + goto args; + case '#': + goto done; + case '.': + r->uri_ext = u + 1; + *u++ = ch; + break; + case '+': + r->plus_in_uri = 1; + /* fall through */ + default: + *u++ = ch; + break; + } + + ch = *p++; + break; + + case sw_slash: + + if (usual[ch >> 5] & (1U << (ch & 0x1f))) { + state = sw_usual; + *u++ = ch; + ch = *p++; + break; + } + + switch (ch) { +#if (NGX_WIN32) + case '\\': + break; +#endif + case '/': + if (!merge_slashes) { + *u++ = ch; + } + break; + case '.': + state = sw_dot; + *u++ = ch; + break; + case '%': + quoted_state = state; + state = sw_quoted; + break; + case '?': + r->args_start = p; + goto args; + case '#': + goto done; + case '+': + r->plus_in_uri = 1; + default: + state = sw_usual; + *u++ = ch; + break; + } + + ch = *p++; + break; + + case sw_dot: + + if (usual[ch >> 5] & (1U << (ch & 0x1f))) { + state = sw_usual; + *u++ = ch; + ch = *p++; + break; + } + + 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 args; + case '#': + goto done; + case '+': + r->plus_in_uri = 1; + default: + state = sw_usual; + *u++ = ch; + break; + } + + ch = *p++; + break; + + case sw_dot_dot: + + if (usual[ch >> 5] & (1U << (ch & 0x1f))) { + state = sw_usual; + *u++ = ch; + ch = *p++; + break; + } + + switch (ch) { +#if (NGX_WIN32) + case '\\': +#endif + case '/': + state = sw_slash; + u -= 5; + for ( ;; ) { + if (u < r->uri.data) { + return NGX_HTTP_PARSE_INVALID_REQUEST; + } + if (*u == '/') { + u++; + break; + } + u--; + } + break; + case '%': + quoted_state = state; + state = sw_quoted; + break; + case '?': + r->args_start = p; + goto args; + case '#': + goto done; + case '+': + r->plus_in_uri = 1; + default: + state = sw_usual; + *u++ = ch; + break; + } + + ch = *p++; + break; + + case sw_quoted: + r->quoted_uri = 1; + + 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 == '%' || ch == '#') { + state = sw_usual; + *u++ = ch; + ch = *p++; + break; + + } else if (ch == '\0') { + return NGX_HTTP_PARSE_INVALID_REQUEST; + } + + state = quoted_state; + break; + } + + c = (u_char) (ch | 0x20); + if (c >= 'a' && c <= 'f') { + ch = (u_char) ((decoded << 4) + c - 'a' + 10); + + if (ch == '?') { + state = sw_usual; + *u++ = ch; + ch = *p++; + break; + + } else if (ch == '+') { + r->plus_in_uri = 1; + } + + state = quoted_state; + break; + } + + return NGX_HTTP_PARSE_INVALID_REQUEST; + } + } + +done: + + r->uri.len = u - r->uri.data; + + if (r->uri_ext) { + r->exten.len = u - r->uri_ext; + r->exten.data = r->uri_ext; + } + + r->uri_ext = NULL; + + return NGX_OK; + +args: + + while (p < r->uri_end) { + if (*p++ != '#') { + continue; + } + + r->args.len = p - 1 - r->args_start; + r->args.data = r->args_start; + r->args_start = NULL; + + break; + } + + r->uri.len = u - r->uri.data; + + 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_status_line(ngx_http_request_t *r, ngx_buf_t *b, + ngx_http_status_t *status) +{ + u_char ch; + u_char *p; + enum { + sw_start = 0, + sw_H, + sw_HT, + sw_HTT, + sw_HTTP, + sw_first_major_digit, + sw_major_digit, + sw_first_minor_digit, + sw_minor_digit, + sw_status, + sw_space_after_status, + sw_status_text, + sw_almost_done + } state; + + state = r->state; + + for (p = b->pos; p < b->last; p++) { + ch = *p; + + switch (state) { + + /* "HTTP/" */ + case sw_start: + switch (ch) { + case 'H': + state = sw_H; + break; + default: + return NGX_ERROR; + } + break; + + case sw_H: + switch (ch) { + case 'T': + state = sw_HT; + break; + default: + return NGX_ERROR; + } + break; + + case sw_HT: + switch (ch) { + case 'T': + state = sw_HTT; + break; + default: + return NGX_ERROR; + } + break; + + case sw_HTT: + switch (ch) { + case 'P': + state = sw_HTTP; + break; + default: + return NGX_ERROR; + } + break; + + case sw_HTTP: + switch (ch) { + case '/': + state = sw_first_major_digit; + break; + default: + return NGX_ERROR; + } + break; + + /* the first digit of major HTTP version */ + case sw_first_major_digit: + if (ch < '1' || ch > '9') { + return NGX_ERROR; + } + + r->http_major = ch - '0'; + state = sw_major_digit; + break; + + /* the major HTTP version or dot */ + case sw_major_digit: + if (ch == '.') { + state = sw_first_minor_digit; + break; + } + + if (ch < '0' || ch > '9') { + return NGX_ERROR; + } + + if (r->http_major > 99) { + return NGX_ERROR; + } + + r->http_major = r->http_major * 10 + ch - '0'; + break; + + /* the first digit of minor HTTP version */ + case sw_first_minor_digit: + if (ch < '0' || ch > '9') { + return NGX_ERROR; + } + + r->http_minor = ch - '0'; + state = sw_minor_digit; + break; + + /* the minor HTTP version or the end of the request line */ + case sw_minor_digit: + if (ch == ' ') { + state = sw_status; + break; + } + + if (ch < '0' || ch > '9') { + return NGX_ERROR; + } + + if (r->http_minor > 99) { + return NGX_ERROR; + } + + r->http_minor = r->http_minor * 10 + ch - '0'; + break; + + /* HTTP status code */ + case sw_status: + if (ch == ' ') { + break; + } + + if (ch < '0' || ch > '9') { + return NGX_ERROR; + } + + status->code = status->code * 10 + ch - '0'; + + if (++status->count == 3) { + state = sw_space_after_status; + status->start = p - 2; + } + + break; + + /* space or end of line */ + case sw_space_after_status: + switch (ch) { + case ' ': + state = sw_status_text; + break; + case '.': /* IIS may send 403.1, 403.2, etc */ + state = sw_status_text; + break; + case CR: + state = sw_almost_done; + break; + case LF: + goto done; + default: + return NGX_ERROR; + } + break; + + /* any text until end of line */ + case sw_status_text: + switch (ch) { + case CR: + state = sw_almost_done; + + break; + case LF: + goto done; + } + break; + + /* end of status line */ + case sw_almost_done: + status->end = p - 1; + switch (ch) { + case LF: + goto done; + default: + return NGX_ERROR; + } + } + } + + b->pos = p; + r->state = state; + + return NGX_AGAIN; + +done: + + b->pos = p + 1; + + if (status->end == NULL) { + status->end = p; + } + + status->http_version = r->http_major * 1000 + r->http_minor; + r->state = sw_start; + + 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, *src, *dst; + size_t len; + ngx_uint_t quoted; + + len = uri->len; + p = uri->data; + quoted = 0; + + if (len == 0 || p[0] == '?') { + goto unsafe; + } + + if (p[0] == '.' && len > 1 && p[1] == '.' + && (len == 2 || ngx_path_separator(p[2]))) + { + goto unsafe; + } + + for ( /* void */ ; len; len--) { + + ch = *p++; + + if (ch == '%') { + quoted = 1; + continue; + } + + if (usual[ch >> 5] & (1U << (ch & 0x1f))) { + continue; + } + + if (ch == '?') { + args->len = len - 1; + args->data = p; + uri->len -= len; + + break; + } + + if (ch == '\0') { + goto unsafe; + } + + if (ngx_path_separator(ch) && len > 2) { + + /* detect "/../" and "/.." */ + + if (p[0] == '.' && p[1] == '.' + && (len == 3 || ngx_path_separator(p[2]))) + { + goto unsafe; + } + } + } + + if (quoted) { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "escaped URI: \"%V\"", uri); + + src = uri->data; + + dst = ngx_pnalloc(r->pool, uri->len); + if (dst == NULL) { + return NGX_ERROR; + } + + uri->data = dst; + + ngx_unescape_uri(&dst, &src, uri->len, 0); + + uri->len = dst - uri->data; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "unescaped URI: \"%V\"", uri); + + len = uri->len; + p = uri->data; + + if (p[0] == '.' && len > 1 && p[1] == '.' + && (len == 2 || ngx_path_separator(p[2]))) + { + goto unsafe; + } + + for ( /* void */ ; len; len--) { + + ch = *p++; + + if (ch == '\0') { + goto unsafe; + } + + if (ngx_path_separator(ch) && len > 2) { + + /* detect "/../" and "/.." */ + + if (p[0] == '.' && p[1] == '.' + && (len == 3 || ngx_path_separator(p[2]))) + { + goto unsafe; + } + } + } + } + + return NGX_OK; + +unsafe: + + if (*flags & NGX_HTTP_LOG_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; +} + + +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) +{ + u_char *p, *last; + + if (r->args.len == 0) { + return NGX_DECLINED; + } + + p = r->args.data; + last = p + r->args.len; + + for ( /* void */ ; p < last; p++) { + + /* we need '=' after name, so drop one char from last */ + + p = ngx_strlcasestrn(p, last - 1, name, len - 1); + + if (p == NULL) { + return NGX_DECLINED; + } + + if ((p == r->args.data || *(p - 1) == '&') && *(p + len) == '=') { + + value->data = p + len + 1; + + p = ngx_strlchr(p, last, '&'); + + if (p == NULL) { + p = r->args.data + r->args.len; + } + + value->len = p - value->data; + + return NGX_OK; + } + } + + return NGX_DECLINED; +} + + +void +ngx_http_split_args(ngx_http_request_t *r, ngx_str_t *uri, ngx_str_t *args) +{ + u_char *p, *last; + + last = uri->data + uri->len; + + p = ngx_strlchr(uri->data, last, '?'); + + if (p) { + uri->len = p - uri->data; + p++; + args->len = last - p; + args->data = p; + + } else { + args->len = 0; + } +} + + +ngx_int_t +ngx_http_parse_chunked(ngx_http_request_t *r, ngx_buf_t *b, + ngx_http_chunked_t *ctx) +{ + u_char *pos, ch, c; + ngx_int_t rc; + enum { + sw_chunk_start = 0, + sw_chunk_size, + sw_chunk_extension, + sw_chunk_extension_almost_done, + sw_chunk_data, + sw_after_data, + sw_after_data_almost_done, + sw_last_chunk_extension, + sw_last_chunk_extension_almost_done, + sw_trailer, + sw_trailer_almost_done, + sw_trailer_header, + sw_trailer_header_almost_done + } state; + + state = ctx->state; + + if (state == sw_chunk_data && ctx->size == 0) { + state = sw_after_data; + } + + rc = NGX_AGAIN; + + for (pos = b->pos; pos < b->last; pos++) { + + ch = *pos; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http chunked byte: %02Xd s:%d", ch, state); + + switch (state) { + + case sw_chunk_start: + if (ch >= '0' && ch <= '9') { + state = sw_chunk_size; + ctx->size = ch - '0'; + break; + } + + c = (u_char) (ch | 0x20); + + if (c >= 'a' && c <= 'f') { + state = sw_chunk_size; + ctx->size = c - 'a' + 10; + break; + } + + goto invalid; + + case sw_chunk_size: + if (ctx->size > NGX_MAX_OFF_T_VALUE / 16) { + goto invalid; + } + + if (ch >= '0' && ch <= '9') { + ctx->size = ctx->size * 16 + (ch - '0'); + break; + } + + c = (u_char) (ch | 0x20); + + if (c >= 'a' && c <= 'f') { + ctx->size = ctx->size * 16 + (c - 'a' + 10); + break; + } + + if (ctx->size == 0) { + + switch (ch) { + case CR: + state = sw_last_chunk_extension_almost_done; + break; + case LF: + state = sw_trailer; + break; + case ';': + case ' ': + case '\t': + state = sw_last_chunk_extension; + break; + default: + goto invalid; + } + + break; + } + + switch (ch) { + case CR: + state = sw_chunk_extension_almost_done; + break; + case LF: + state = sw_chunk_data; + break; + case ';': + case ' ': + case '\t': + state = sw_chunk_extension; + break; + default: + goto invalid; + } + + break; + + case sw_chunk_extension: + switch (ch) { + case CR: + state = sw_chunk_extension_almost_done; + break; + case LF: + state = sw_chunk_data; + } + break; + + case sw_chunk_extension_almost_done: + if (ch == LF) { + state = sw_chunk_data; + break; + } + goto invalid; + + case sw_chunk_data: + rc = NGX_OK; + goto data; + + case sw_after_data: + switch (ch) { + case CR: + state = sw_after_data_almost_done; + break; + case LF: + state = sw_chunk_start; + } + break; + + case sw_after_data_almost_done: + if (ch == LF) { + state = sw_chunk_start; + break; + } + goto invalid; + + case sw_last_chunk_extension: + switch (ch) { + case CR: + state = sw_last_chunk_extension_almost_done; + break; + case LF: + state = sw_trailer; + } + break; + + case sw_last_chunk_extension_almost_done: + if (ch == LF) { + state = sw_trailer; + break; + } + goto invalid; + + case sw_trailer: + switch (ch) { + case CR: + state = sw_trailer_almost_done; + break; + case LF: + goto done; + default: + state = sw_trailer_header; + } + break; + + case sw_trailer_almost_done: + if (ch == LF) { + goto done; + } + goto invalid; + + case sw_trailer_header: + switch (ch) { + case CR: + state = sw_trailer_header_almost_done; + break; + case LF: + state = sw_trailer; + } + break; + + case sw_trailer_header_almost_done: + if (ch == LF) { + state = sw_trailer; + break; + } + goto invalid; + + } + } + +data: + + ctx->state = state; + b->pos = pos; + + if (ctx->size > NGX_MAX_OFF_T_VALUE - 5) { + goto invalid; + } + + switch (state) { + + case sw_chunk_start: + ctx->length = 3 /* "0" LF LF */; + break; + case sw_chunk_size: + ctx->length = 1 /* LF */ + + (ctx->size ? ctx->size + 4 /* LF "0" LF LF */ + : 1 /* LF */); + break; + case sw_chunk_extension: + case sw_chunk_extension_almost_done: + ctx->length = 1 /* LF */ + ctx->size + 4 /* LF "0" LF LF */; + break; + case sw_chunk_data: + ctx->length = ctx->size + 4 /* LF "0" LF LF */; + break; + case sw_after_data: + case sw_after_data_almost_done: + ctx->length = 4 /* LF "0" LF LF */; + break; + case sw_last_chunk_extension: + case sw_last_chunk_extension_almost_done: + ctx->length = 2 /* LF LF */; + break; + case sw_trailer: + case sw_trailer_almost_done: + ctx->length = 1 /* LF */; + break; + case sw_trailer_header: + case sw_trailer_header_almost_done: + ctx->length = 2 /* LF LF */; + break; + + } + + return rc; + +done: + + ctx->state = 0; + b->pos = pos + 1; + + return NGX_DONE; + +invalid: + + return NGX_ERROR; +} |