src/http/ngx_http_parse.c - nginx

Global variables defined

Data types defined

Functions defined

Macros defined

Source code


  1. /*
  2. * Copyright (C) Igor Sysoev
  3. * Copyright (C) Nginx, Inc.
  4. */


  5. #include <ngx_config.h>
  6. #include <ngx_core.h>
  7. #include <ngx_http.h>


  8. static ngx_table_elt_t *ngx_http_parse_multi_header_lines_internal(
  9.     ngx_http_request_t *r, ngx_table_elt_t *headers, ngx_str_t *name,
  10.     ngx_str_t *value, u_char sep);

  11. static uint32_t  usual[] = {
  12.     0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */

  13.                 /* ?>=< ;:98 7654 3210  /.-, +*)( '&%$ #"!  */
  14.     0x7fff37d6, /* 0111 1111 1111 1111  0011 0111 1101 0110 */

  15.                 /* _^]\ [ZYX WVUT SRQP  ONML KJIH GFED CBA@ */
  16. #if (NGX_WIN32)
  17.     0xefffffff, /* 1110 1111 1111 1111  1111 1111 1111 1111 */
  18. #else
  19.     0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
  20. #endif

  21.                 /*  ~}| {zyx wvut srqp  onml kjih gfed cba` */
  22.     0x7fffffff, /* 0111 1111 1111 1111  1111 1111 1111 1111 */

  23.     0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
  24.     0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
  25.     0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
  26.     0xffffffff  /* 1111 1111 1111 1111  1111 1111 1111 1111 */
  27. };


  28. #if (NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED)

  29. #define ngx_str3_cmp(m, c0, c1, c2, c3)                                       \
  30.     *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)

  31. #define ngx_str3Ocmp(m, c0, c1, c2, c3)                                       \
  32.     *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)

  33. #define ngx_str4cmp(m, c0, c1, c2, c3)                                        \
  34.     *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)

  35. #define ngx_str5cmp(m, c0, c1, c2, c3, c4)                                    \
  36.     *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)             \
  37.         && m[4] == c4

  38. #define ngx_str6cmp(m, c0, c1, c2, c3, c4, c5)                                \
  39.     *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)             \
  40.         && (((uint32_t *) m)[1] & 0xffff) == ((c5 << 8) | c4)

  41. #define ngx_str7_cmp(m, c0, c1, c2, c3, c4, c5, c6, c7)                       \
  42.     *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)             \
  43.         && ((uint32_t *) m)[1] == ((c7 << 24) | (c6 << 16) | (c5 << 8) | c4)

  44. #define ngx_str8cmp(m, c0, c1, c2, c3, c4, c5, c6, c7)                        \
  45.     *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)             \
  46.         && ((uint32_t *) m)[1] == ((c7 << 24) | (c6 << 16) | (c5 << 8) | c4)

  47. #define ngx_str9cmp(m, c0, c1, c2, c3, c4, c5, c6, c7, c8)                    \
  48.     *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)             \
  49.         && ((uint32_t *) m)[1] == ((c7 << 24) | (c6 << 16) | (c5 << 8) | c4)  \
  50.         && m[8] == c8

  51. #else /* !(NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED) */

  52. #define ngx_str3_cmp(m, c0, c1, c2, c3)                                       \
  53.     m[0] == c0 && m[1] == c1 && m[2] == c2

  54. #define ngx_str3Ocmp(m, c0, c1, c2, c3)                                       \
  55.     m[0] == c0 && m[2] == c2 && m[3] == c3

  56. #define ngx_str4cmp(m, c0, c1, c2, c3)                                        \
  57.     m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3

  58. #define ngx_str5cmp(m, c0, c1, c2, c3, c4)                                    \
  59.     m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3 && m[4] == c4

  60. #define ngx_str6cmp(m, c0, c1, c2, c3, c4, c5)                                \
  61.     m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3                      \
  62.         && m[4] == c4 && m[5] == c5

  63. #define ngx_str7_cmp(m, c0, c1, c2, c3, c4, c5, c6, c7)                       \
  64.     m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3                      \
  65.         && m[4] == c4 && m[5] == c5 && m[6] == c6

  66. #define ngx_str8cmp(m, c0, c1, c2, c3, c4, c5, c6, c7)                        \
  67.     m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3                      \
  68.         && m[4] == c4 && m[5] == c5 && m[6] == c6 && m[7] == c7

  69. #define ngx_str9cmp(m, c0, c1, c2, c3, c4, c5, c6, c7, c8)                    \
  70.     m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3                      \
  71.         && m[4] == c4 && m[5] == c5 && m[6] == c6 && m[7] == c7 && m[8] == c8

  72. #endif


  73. /* gcc, icc, msvc and others compile these switches as an jump table */

  74. ngx_int_t
  75. ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b)
  76. {
  77.     u_char  c, ch, *p, *m;
  78.     enum {
  79.         sw_start = 0,
  80.         sw_method,
  81.         sw_spaces_before_uri,
  82.         sw_schema,
  83.         sw_schema_slash,
  84.         sw_schema_slash_slash,
  85.         sw_spaces_before_host,
  86.         sw_host_start,
  87.         sw_host,
  88.         sw_host_end,
  89.         sw_host_ip_literal,
  90.         sw_port_start,
  91.         sw_port,
  92.         sw_after_slash_in_uri,
  93.         sw_check_uri,
  94.         sw_uri,
  95.         sw_http_09,
  96.         sw_http_H,
  97.         sw_http_HT,
  98.         sw_http_HTT,
  99.         sw_http_HTTP,
  100.         sw_first_major_digit,
  101.         sw_major_digit,
  102.         sw_first_minor_digit,
  103.         sw_minor_digit,
  104.         sw_spaces_after_digit,
  105.         sw_almost_done
  106.     } state;

  107.     state = r->state;

  108.     for (p = b->pos; p < b->last; p++) {
  109.         ch = *p;

  110.         switch (state) {

  111.         /* HTTP methods: GET, HEAD, POST */
  112.         case sw_start:
  113.             r->request_start = p;

  114.             if (ch == CR || ch == LF) {
  115.                 break;
  116.             }

  117.             if ((ch < 'A' || ch > 'Z') && ch != '_' && ch != '-') {
  118.                 return NGX_HTTP_PARSE_INVALID_METHOD;
  119.             }

  120.             state = sw_method;
  121.             break;

  122.         case sw_method:
  123.             if (ch == ' ') {
  124.                 r->method_end = p - 1;
  125.                 m = r->request_start;
  126.                 state = sw_spaces_before_uri;

  127.                 switch (p - m) {

  128.                 case 3:
  129.                     if (ngx_str3_cmp(m, 'G', 'E', 'T', ' ')) {
  130.                         r->method = NGX_HTTP_GET;
  131.                         break;
  132.                     }

  133.                     if (ngx_str3_cmp(m, 'P', 'U', 'T', ' ')) {
  134.                         r->method = NGX_HTTP_PUT;
  135.                         break;
  136.                     }

  137.                     break;

  138.                 case 4:
  139.                     if (m[1] == 'O') {

  140.                         if (ngx_str3Ocmp(m, 'P', 'O', 'S', 'T')) {
  141.                             r->method = NGX_HTTP_POST;
  142.                             break;
  143.                         }

  144.                         if (ngx_str3Ocmp(m, 'C', 'O', 'P', 'Y')) {
  145.                             r->method = NGX_HTTP_COPY;
  146.                             break;
  147.                         }

  148.                         if (ngx_str3Ocmp(m, 'M', 'O', 'V', 'E')) {
  149.                             r->method = NGX_HTTP_MOVE;
  150.                             break;
  151.                         }

  152.                         if (ngx_str3Ocmp(m, 'L', 'O', 'C', 'K')) {
  153.                             r->method = NGX_HTTP_LOCK;
  154.                             break;
  155.                         }

  156.                     } else {

  157.                         if (ngx_str4cmp(m, 'H', 'E', 'A', 'D')) {
  158.                             r->method = NGX_HTTP_HEAD;
  159.                             break;
  160.                         }
  161.                     }

  162.                     break;

  163.                 case 5:
  164.                     if (ngx_str5cmp(m, 'M', 'K', 'C', 'O', 'L')) {
  165.                         r->method = NGX_HTTP_MKCOL;
  166.                         break;
  167.                     }

  168.                     if (ngx_str5cmp(m, 'P', 'A', 'T', 'C', 'H')) {
  169.                         r->method = NGX_HTTP_PATCH;
  170.                         break;
  171.                     }

  172.                     if (ngx_str5cmp(m, 'T', 'R', 'A', 'C', 'E')) {
  173.                         r->method = NGX_HTTP_TRACE;
  174.                         break;
  175.                     }

  176.                     break;

  177.                 case 6:
  178.                     if (ngx_str6cmp(m, 'D', 'E', 'L', 'E', 'T', 'E')) {
  179.                         r->method = NGX_HTTP_DELETE;
  180.                         break;
  181.                     }

  182.                     if (ngx_str6cmp(m, 'U', 'N', 'L', 'O', 'C', 'K')) {
  183.                         r->method = NGX_HTTP_UNLOCK;
  184.                         break;
  185.                     }

  186.                     break;

  187.                 case 7:
  188.                     if (ngx_str7_cmp(m, 'O', 'P', 'T', 'I', 'O', 'N', 'S', ' '))
  189.                     {
  190.                         r->method = NGX_HTTP_OPTIONS;
  191.                     }

  192.                     if (ngx_str7_cmp(m, 'C', 'O', 'N', 'N', 'E', 'C', 'T', ' '))
  193.                     {
  194.                         r->method = NGX_HTTP_CONNECT;
  195.                         state = sw_spaces_before_host;
  196.                     }

  197.                     break;

  198.                 case 8:
  199.                     if (ngx_str8cmp(m, 'P', 'R', 'O', 'P', 'F', 'I', 'N', 'D'))
  200.                     {
  201.                         r->method = NGX_HTTP_PROPFIND;
  202.                     }

  203.                     break;

  204.                 case 9:
  205.                     if (ngx_str9cmp(m,
  206.                             'P', 'R', 'O', 'P', 'P', 'A', 'T', 'C', 'H'))
  207.                     {
  208.                         r->method = NGX_HTTP_PROPPATCH;
  209.                     }

  210.                     break;
  211.                 }

  212.                 break;
  213.             }

  214.             if ((ch < 'A' || ch > 'Z') && ch != '_' && ch != '-') {
  215.                 return NGX_HTTP_PARSE_INVALID_METHOD;
  216.             }

  217.             break;

  218.         /* space* before URI */
  219.         case sw_spaces_before_uri:

  220.             if (ch == '/') {
  221.                 r->uri_start = p;
  222.                 state = sw_after_slash_in_uri;
  223.                 break;
  224.             }

  225.             c = (u_char) (ch | 0x20);
  226.             if (c >= 'a' && c <= 'z') {
  227.                 r->schema_start = p;
  228.                 state = sw_schema;
  229.                 break;
  230.             }

  231.             switch (ch) {
  232.             case ' ':
  233.                 break;
  234.             default:
  235.                 return NGX_HTTP_PARSE_INVALID_REQUEST;
  236.             }
  237.             break;

  238.         case sw_schema:

  239.             c = (u_char) (ch | 0x20);
  240.             if (c >= 'a' && c <= 'z') {
  241.                 break;
  242.             }

  243.             if ((ch >= '0' && ch <= '9') || ch == '+' || ch == '-' || ch == '.')
  244.             {
  245.                 break;
  246.             }

  247.             switch (ch) {
  248.             case ':':
  249.                 r->schema_end = p;
  250.                 state = sw_schema_slash;
  251.                 break;
  252.             default:
  253.                 return NGX_HTTP_PARSE_INVALID_REQUEST;
  254.             }
  255.             break;

  256.         case sw_schema_slash:
  257.             switch (ch) {
  258.             case '/':
  259.                 state = sw_schema_slash_slash;
  260.                 break;
  261.             default:
  262.                 return NGX_HTTP_PARSE_INVALID_REQUEST;
  263.             }
  264.             break;

  265.         case sw_schema_slash_slash:
  266.             switch (ch) {
  267.             case '/':
  268.                 state = sw_host_start;
  269.                 break;
  270.             default:
  271.                 return NGX_HTTP_PARSE_INVALID_REQUEST;
  272.             }
  273.             break;

  274.         case sw_spaces_before_host:

  275.             if (ch == ' ') {
  276.                 break;
  277.             }

  278.             /* fall through */

  279.         case sw_host_start:

  280.             r->host_start = p;

  281.             if (ch == '[') {
  282.                 state = sw_host_ip_literal;
  283.                 break;
  284.             }

  285.             state = sw_host;

  286.             /* fall through */

  287.         case sw_host:

  288.             c = (u_char) (ch | 0x20);
  289.             if (c >= 'a' && c <= 'z') {
  290.                 break;
  291.             }

  292.             if ((ch >= '0' && ch <= '9') || ch == '.' || ch == '-') {
  293.                 break;
  294.             }

  295.             /* fall through */

  296.         case sw_host_end:

  297.             if (ch == ':') {
  298.                 state = sw_port_start;
  299.                 break;
  300.             }

  301.             r->host_end = p;

  302.             if (r->method == NGX_HTTP_CONNECT) {
  303.                 return NGX_HTTP_PARSE_INVALID_REQUEST;
  304.             }

  305.             switch (ch) {
  306.             case '/':
  307.                 r->uri_start = p;
  308.                 state = sw_after_slash_in_uri;
  309.                 break;
  310.             case '?':
  311.                 r->uri_start = p;
  312.                 r->args_start = p + 1;
  313.                 r->empty_path_in_uri = 1;
  314.                 state = sw_uri;
  315.                 break;
  316.             case ' ':
  317.                 /*
  318.                  * use single "/" from request line to preserve pointers,
  319.                  * if request line will be copied to large client buffer
  320.                  */
  321.                 r->uri_start = r->schema_end + 1;
  322.                 r->uri_end = r->schema_end + 2;
  323.                 state = sw_http_09;
  324.                 break;
  325.             default:
  326.                 return NGX_HTTP_PARSE_INVALID_REQUEST;
  327.             }
  328.             break;

  329.         case sw_host_ip_literal:

  330.             if (ch >= '0' && ch <= '9') {
  331.                 break;
  332.             }

  333.             c = (u_char) (ch | 0x20);
  334.             if (c >= 'a' && c <= 'z') {
  335.                 break;
  336.             }

  337.             switch (ch) {
  338.             case ':':
  339.                 break;
  340.             case ']':
  341.                 state = sw_host_end;
  342.                 break;
  343.             case '-':
  344.             case '.':
  345.             case '_':
  346.             case '~':
  347.                 /* unreserved */
  348.                 break;
  349.             case '!':
  350.             case '$':
  351.             case '&':
  352.             case '\'':
  353.             case '(':
  354.             case ')':
  355.             case '*':
  356.             case '+':
  357.             case ',':
  358.             case ';':
  359.             case '=':
  360.                 /* sub-delims */
  361.                 break;
  362.             default:
  363.                 return NGX_HTTP_PARSE_INVALID_REQUEST;
  364.             }
  365.             break;

  366.         case sw_port_start:
  367.             state = sw_port;

  368.             if (ch >= '0' && ch <= '9') {
  369.                 break;
  370.             }

  371.             if (r->method == NGX_HTTP_CONNECT) {
  372.                 return NGX_HTTP_PARSE_INVALID_REQUEST;
  373.             }

  374.             /* fall through */

  375.         case sw_port:
  376.             if (ch >= '0' && ch <= '9') {
  377.                 break;
  378.             }

  379.             r->host_end = p;

  380.             if (r->method == NGX_HTTP_CONNECT) {
  381.                 if (ch == ' ') {
  382.                     state = sw_http_09;
  383.                     break;
  384.                 }

  385.                 return NGX_HTTP_PARSE_INVALID_REQUEST;
  386.             }

  387.             switch (ch) {
  388.             case '/':
  389.                 r->uri_start = p;
  390.                 state = sw_after_slash_in_uri;
  391.                 break;
  392.             case '?':
  393.                 r->uri_start = p;
  394.                 r->args_start = p + 1;
  395.                 r->empty_path_in_uri = 1;
  396.                 state = sw_uri;
  397.                 break;
  398.             case ' ':
  399.                 /*
  400.                  * use single "/" from request line to preserve pointers,
  401.                  * if request line will be copied to large client buffer
  402.                  */
  403.                 r->uri_start = r->schema_end + 1;
  404.                 r->uri_end = r->schema_end + 2;
  405.                 state = sw_http_09;
  406.                 break;
  407.             default:
  408.                 return NGX_HTTP_PARSE_INVALID_REQUEST;
  409.             }
  410.             break;

  411.         /* check "/.", "//", "%", and "\" (Win32) in URI */
  412.         case sw_after_slash_in_uri:

  413.             if (usual[ch >> 5] & (1U << (ch & 0x1f))) {
  414.                 state = sw_check_uri;
  415.                 break;
  416.             }

  417.             switch (ch) {
  418.             case ' ':
  419.                 r->uri_end = p;
  420.                 state = sw_http_09;
  421.                 break;
  422.             case CR:
  423.                 r->uri_end = p;
  424.                 r->http_minor = 9;
  425.                 state = sw_almost_done;
  426.                 break;
  427.             case LF:
  428.                 r->uri_end = p;
  429.                 r->http_minor = 9;
  430.                 goto done;
  431.             case '.':
  432.                 r->complex_uri = 1;
  433.                 state = sw_uri;
  434.                 break;
  435.             case '%':
  436.                 r->quoted_uri = 1;
  437.                 state = sw_uri;
  438.                 break;
  439.             case '/':
  440.                 r->complex_uri = 1;
  441.                 state = sw_uri;
  442.                 break;
  443. #if (NGX_WIN32)
  444.             case '\\':
  445.                 r->complex_uri = 1;
  446.                 state = sw_uri;
  447.                 break;
  448. #endif
  449.             case '?':
  450.                 r->args_start = p + 1;
  451.                 state = sw_uri;
  452.                 break;
  453.             case '#':
  454.                 r->complex_uri = 1;
  455.                 state = sw_uri;
  456.                 break;
  457.             case '+':
  458.                 r->plus_in_uri = 1;
  459.                 break;
  460.             default:
  461.                 if (ch < 0x20 || ch == 0x7f) {
  462.                     return NGX_HTTP_PARSE_INVALID_REQUEST;
  463.                 }
  464.                 state = sw_check_uri;
  465.                 break;
  466.             }
  467.             break;

  468.         /* check "/", "%" and "\" (Win32) in URI */
  469.         case sw_check_uri:

  470.             if (usual[ch >> 5] & (1U << (ch & 0x1f))) {
  471.                 break;
  472.             }

  473.             switch (ch) {
  474.             case '/':
  475. #if (NGX_WIN32)
  476.                 if (r->uri_ext == p) {
  477.                     r->complex_uri = 1;
  478.                     state = sw_uri;
  479.                     break;
  480.                 }
  481. #endif
  482.                 r->uri_ext = NULL;
  483.                 state = sw_after_slash_in_uri;
  484.                 break;
  485.             case '.':
  486.                 r->uri_ext = p + 1;
  487.                 break;
  488.             case ' ':
  489.                 r->uri_end = p;
  490.                 state = sw_http_09;
  491.                 break;
  492.             case CR:
  493.                 r->uri_end = p;
  494.                 r->http_minor = 9;
  495.                 state = sw_almost_done;
  496.                 break;
  497.             case LF:
  498.                 r->uri_end = p;
  499.                 r->http_minor = 9;
  500.                 goto done;
  501. #if (NGX_WIN32)
  502.             case '\\':
  503.                 r->complex_uri = 1;
  504.                 state = sw_after_slash_in_uri;
  505.                 break;
  506. #endif
  507.             case '%':
  508.                 r->quoted_uri = 1;
  509.                 state = sw_uri;
  510.                 break;
  511.             case '?':
  512.                 r->args_start = p + 1;
  513.                 state = sw_uri;
  514.                 break;
  515.             case '#':
  516.                 r->complex_uri = 1;
  517.                 state = sw_uri;
  518.                 break;
  519.             case '+':
  520.                 r->plus_in_uri = 1;
  521.                 break;
  522.             default:
  523.                 if (ch < 0x20 || ch == 0x7f) {
  524.                     return NGX_HTTP_PARSE_INVALID_REQUEST;
  525.                 }
  526.                 break;
  527.             }
  528.             break;

  529.         /* URI */
  530.         case sw_uri:

  531.             if (usual[ch >> 5] & (1U << (ch & 0x1f))) {
  532.                 break;
  533.             }

  534.             switch (ch) {
  535.             case ' ':
  536.                 r->uri_end = p;
  537.                 state = sw_http_09;
  538.                 break;
  539.             case CR:
  540.                 r->uri_end = p;
  541.                 r->http_minor = 9;
  542.                 state = sw_almost_done;
  543.                 break;
  544.             case LF:
  545.                 r->uri_end = p;
  546.                 r->http_minor = 9;
  547.                 goto done;
  548.             case '#':
  549.                 r->complex_uri = 1;
  550.                 break;
  551.             default:
  552.                 if (ch < 0x20 || ch == 0x7f) {
  553.                     return NGX_HTTP_PARSE_INVALID_REQUEST;
  554.                 }
  555.                 break;
  556.             }
  557.             break;

  558.         /* space+ after URI */
  559.         case sw_http_09:
  560.             switch (ch) {
  561.             case ' ':
  562.                 break;
  563.             case CR:
  564.                 r->http_minor = 9;
  565.                 state = sw_almost_done;
  566.                 break;
  567.             case LF:
  568.                 r->http_minor = 9;
  569.                 goto done;
  570.             case 'H':
  571.                 r->http_protocol.data = p;
  572.                 state = sw_http_H;
  573.                 break;
  574.             default:
  575.                 return NGX_HTTP_PARSE_INVALID_REQUEST;
  576.             }
  577.             break;

  578.         case sw_http_H:
  579.             switch (ch) {
  580.             case 'T':
  581.                 state = sw_http_HT;
  582.                 break;
  583.             default:
  584.                 return NGX_HTTP_PARSE_INVALID_REQUEST;
  585.             }
  586.             break;

  587.         case sw_http_HT:
  588.             switch (ch) {
  589.             case 'T':
  590.                 state = sw_http_HTT;
  591.                 break;
  592.             default:
  593.                 return NGX_HTTP_PARSE_INVALID_REQUEST;
  594.             }
  595.             break;

  596.         case sw_http_HTT:
  597.             switch (ch) {
  598.             case 'P':
  599.                 state = sw_http_HTTP;
  600.                 break;
  601.             default:
  602.                 return NGX_HTTP_PARSE_INVALID_REQUEST;
  603.             }
  604.             break;

  605.         case sw_http_HTTP:
  606.             switch (ch) {
  607.             case '/':

  608.                 /*
  609.                  * use single "/" from request line to preserve pointers,
  610.                  * if request line will be copied to large client buffer
  611.                  */
  612.                 if (r->method == NGX_HTTP_CONNECT) {
  613.                     r->uri_start = p;
  614.                     r->uri_end = p + 1;
  615.                 }

  616.                 state = sw_first_major_digit;
  617.                 break;
  618.             default:
  619.                 return NGX_HTTP_PARSE_INVALID_REQUEST;
  620.             }
  621.             break;

  622.         /* first digit of major HTTP version */
  623.         case sw_first_major_digit:
  624.             if (ch < '1' || ch > '9') {
  625.                 return NGX_HTTP_PARSE_INVALID_REQUEST;
  626.             }

  627.             r->http_major = ch - '0';

  628.             if (r->http_major > 1) {
  629.                 return NGX_HTTP_PARSE_INVALID_VERSION;
  630.             }

  631.             state = sw_major_digit;
  632.             break;

  633.         /* major HTTP version or dot */
  634.         case sw_major_digit:
  635.             if (ch == '.') {
  636.                 state = sw_first_minor_digit;
  637.                 break;
  638.             }

  639.             if (ch < '0' || ch > '9') {
  640.                 return NGX_HTTP_PARSE_INVALID_REQUEST;
  641.             }

  642.             r->http_major = r->http_major * 10 + (ch - '0');

  643.             if (r->http_major > 1) {
  644.                 return NGX_HTTP_PARSE_INVALID_VERSION;
  645.             }

  646.             break;

  647.         /* first digit of minor HTTP version */
  648.         case sw_first_minor_digit:
  649.             if (ch < '0' || ch > '9') {
  650.                 return NGX_HTTP_PARSE_INVALID_REQUEST;
  651.             }

  652.             r->http_minor = ch - '0';
  653.             state = sw_minor_digit;
  654.             break;

  655.         /* minor HTTP version or end of request line */
  656.         case sw_minor_digit:
  657.             if (ch == CR) {
  658.                 state = sw_almost_done;
  659.                 break;
  660.             }

  661.             if (ch == LF) {
  662.                 goto done;
  663.             }

  664.             if (ch == ' ') {
  665.                 state = sw_spaces_after_digit;
  666.                 break;
  667.             }

  668.             if (ch < '0' || ch > '9') {
  669.                 return NGX_HTTP_PARSE_INVALID_REQUEST;
  670.             }

  671.             if (r->http_minor > 99) {
  672.                 return NGX_HTTP_PARSE_INVALID_REQUEST;
  673.             }

  674.             r->http_minor = r->http_minor * 10 + (ch - '0');
  675.             break;

  676.         case sw_spaces_after_digit:
  677.             switch (ch) {
  678.             case ' ':
  679.                 break;
  680.             case CR:
  681.                 state = sw_almost_done;
  682.                 break;
  683.             case LF:
  684.                 goto done;
  685.             default:
  686.                 return NGX_HTTP_PARSE_INVALID_REQUEST;
  687.             }
  688.             break;

  689.         /* end of request line */
  690.         case sw_almost_done:
  691.             r->request_end = p - 1;
  692.             switch (ch) {
  693.             case LF:
  694.                 goto done;
  695.             default:
  696.                 return NGX_HTTP_PARSE_INVALID_REQUEST;
  697.             }
  698.         }
  699.     }

  700.     b->pos = p;
  701.     r->state = state;

  702.     return NGX_AGAIN;

  703. done:

  704.     b->pos = p + 1;

  705.     if (r->request_end == NULL) {
  706.         r->request_end = p;
  707.     }

  708.     r->http_version = r->http_major * 1000 + r->http_minor;
  709.     r->state = sw_start;

  710.     if (r->http_version == 9 && r->method != NGX_HTTP_GET) {
  711.         return NGX_HTTP_PARSE_INVALID_09_METHOD;
  712.     }

  713.     return NGX_OK;
  714. }


  715. ngx_int_t
  716. ngx_http_parse_header_line(ngx_http_request_t *r, ngx_buf_t *b,
  717.     ngx_uint_t allow_underscores)
  718. {
  719.     u_char      c, ch, *p;
  720.     ngx_uint_t  hash, i;
  721.     enum {
  722.         sw_start = 0,
  723.         sw_name,
  724.         sw_space_before_value,
  725.         sw_value,
  726.         sw_space_after_value,
  727.         sw_ignore_line,
  728.         sw_almost_done,
  729.         sw_header_almost_done
  730.     } state;

  731.     /* the last '\0' is not needed because string is zero terminated */

  732.     static u_char  lowcase[] =
  733.         "\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"
  734.         "\0\0\0\0\0\0\0\0\0\0\0\0\0-\0\0" "0123456789\0\0\0\0\0\0"
  735.         "\0abcdefghijklmnopqrstuvwxyz\0\0\0\0\0"
  736.         "\0abcdefghijklmnopqrstuvwxyz\0\0\0\0\0"
  737.         "\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"
  738.         "\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"
  739.         "\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"
  740.         "\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";

  741.     state = r->state;
  742.     hash = r->header_hash;
  743.     i = r->lowcase_index;

  744.     for (p = b->pos; p < b->last; p++) {
  745.         ch = *p;

  746.         switch (state) {

  747.         /* first char */
  748.         case sw_start:
  749.             r->header_name_start = p;
  750.             r->invalid_header = 0;

  751.             switch (ch) {
  752.             case CR:
  753.                 r->header_end = p;
  754.                 state = sw_header_almost_done;
  755.                 break;
  756.             case LF:
  757.                 r->header_end = p;
  758.                 goto header_done;
  759.             default:
  760.                 state = sw_name;

  761.                 c = lowcase[ch];

  762.                 if (c) {
  763.                     hash = ngx_hash(0, c);
  764.                     r->lowcase_header[0] = c;
  765.                     i = 1;
  766.                     break;
  767.                 }

  768.                 if (ch == '_') {
  769.                     if (allow_underscores) {
  770.                         hash = ngx_hash(0, ch);
  771.                         r->lowcase_header[0] = ch;
  772.                         i = 1;

  773.                     } else {
  774.                         hash = 0;
  775.                         i = 0;
  776.                         r->invalid_header = 1;
  777.                     }

  778.                     break;
  779.                 }

  780.                 if (ch <= 0x20 || ch == 0x7f || ch == ':') {
  781.                     r->header_end = p;
  782.                     return NGX_HTTP_PARSE_INVALID_HEADER;
  783.                 }

  784.                 hash = 0;
  785.                 i = 0;
  786.                 r->invalid_header = 1;

  787.                 break;

  788.             }
  789.             break;

  790.         /* header name */
  791.         case sw_name:
  792.             c = lowcase[ch];

  793.             if (c) {
  794.                 hash = ngx_hash(hash, c);
  795.                 r->lowcase_header[i++] = c;
  796.                 i &= (NGX_HTTP_LC_HEADER_LEN - 1);
  797.                 break;
  798.             }

  799.             if (ch == '_') {
  800.                 if (allow_underscores) {
  801.                     hash = ngx_hash(hash, ch);
  802.                     r->lowcase_header[i++] = ch;
  803.                     i &= (NGX_HTTP_LC_HEADER_LEN - 1);

  804.                 } else {
  805.                     r->invalid_header = 1;
  806.                 }

  807.                 break;
  808.             }

  809.             if (ch == ':') {
  810.                 r->header_name_end = p;
  811.                 state = sw_space_before_value;
  812.                 break;
  813.             }

  814.             if (ch == CR) {
  815.                 r->header_name_end = p;
  816.                 r->header_start = p;
  817.                 r->header_end = p;
  818.                 state = sw_almost_done;
  819.                 break;
  820.             }

  821.             if (ch == LF) {
  822.                 r->header_name_end = p;
  823.                 r->header_start = p;
  824.                 r->header_end = p;
  825.                 goto done;
  826.             }

  827.             /* IIS may send the duplicate "HTTP/1.1 ..." lines */
  828.             if (ch == '/'
  829.                 && r->upstream
  830.                 && p - r->header_name_start == 4
  831.                 && ngx_strncmp(r->header_name_start, "HTTP", 4) == 0)
  832.             {
  833.                 state = sw_ignore_line;
  834.                 break;
  835.             }

  836.             if (ch <= 0x20 || ch == 0x7f) {
  837.                 r->header_end = p;
  838.                 return NGX_HTTP_PARSE_INVALID_HEADER;
  839.             }

  840.             r->invalid_header = 1;

  841.             break;

  842.         /* space* before header value */
  843.         case sw_space_before_value:
  844.             switch (ch) {
  845.             case ' ':
  846.                 break;
  847.             case CR:
  848.                 r->header_start = p;
  849.                 r->header_end = p;
  850.                 state = sw_almost_done;
  851.                 break;
  852.             case LF:
  853.                 r->header_start = p;
  854.                 r->header_end = p;
  855.                 goto done;
  856.             case '\0':
  857.                 r->header_end = p;
  858.                 return NGX_HTTP_PARSE_INVALID_HEADER;
  859.             default:
  860.                 r->header_start = p;
  861.                 state = sw_value;
  862.                 break;
  863.             }
  864.             break;

  865.         /* header value */
  866.         case sw_value:
  867.             switch (ch) {
  868.             case ' ':
  869.                 r->header_end = p;
  870.                 state = sw_space_after_value;
  871.                 break;
  872.             case CR:
  873.                 r->header_end = p;
  874.                 state = sw_almost_done;
  875.                 break;
  876.             case LF:
  877.                 r->header_end = p;
  878.                 goto done;
  879.             case '\0':
  880.                 r->header_end = p;
  881.                 return NGX_HTTP_PARSE_INVALID_HEADER;
  882.             }
  883.             break;

  884.         /* space* before end of header line */
  885.         case sw_space_after_value:
  886.             switch (ch) {
  887.             case ' ':
  888.                 break;
  889.             case CR:
  890.                 state = sw_almost_done;
  891.                 break;
  892.             case LF:
  893.                 goto done;
  894.             case '\0':
  895.                 r->header_end = p;
  896.                 return NGX_HTTP_PARSE_INVALID_HEADER;
  897.             default:
  898.                 state = sw_value;
  899.                 break;
  900.             }
  901.             break;

  902.         /* ignore header line */
  903.         case sw_ignore_line:
  904.             switch (ch) {
  905.             case LF:
  906.                 state = sw_start;
  907.                 break;
  908.             default:
  909.                 break;
  910.             }
  911.             break;

  912.         /* end of header line */
  913.         case sw_almost_done:
  914.             switch (ch) {
  915.             case LF:
  916.                 goto done;
  917.             case CR:
  918.                 break;
  919.             default:
  920.                 return NGX_HTTP_PARSE_INVALID_HEADER;
  921.             }
  922.             break;

  923.         /* end of header */
  924.         case sw_header_almost_done:
  925.             switch (ch) {
  926.             case LF:
  927.                 goto header_done;
  928.             default:
  929.                 return NGX_HTTP_PARSE_INVALID_HEADER;
  930.             }
  931.         }
  932.     }

  933.     b->pos = p;
  934.     r->state = state;
  935.     r->header_hash = hash;
  936.     r->lowcase_index = i;

  937.     return NGX_AGAIN;

  938. done:

  939.     b->pos = p + 1;
  940.     r->state = sw_start;
  941.     r->header_hash = hash;
  942.     r->lowcase_index = i;

  943.     return NGX_OK;

  944. header_done:

  945.     b->pos = p + 1;
  946.     r->state = sw_start;

  947.     return NGX_HTTP_PARSE_HEADER_DONE;
  948. }


  949. ngx_int_t
  950. ngx_http_parse_uri(ngx_http_request_t *r)
  951. {
  952.     u_char  *p, ch;
  953.     enum {
  954.         sw_start = 0,
  955.         sw_after_slash_in_uri,
  956.         sw_check_uri,
  957.         sw_uri
  958.     } state;

  959.     state = sw_start;

  960.     for (p = r->uri_start; p != r->uri_end; p++) {

  961.         ch = *p;

  962.         switch (state) {

  963.         case sw_start:

  964.             if (ch != '/') {
  965.                 return NGX_ERROR;
  966.             }

  967.             state = sw_after_slash_in_uri;
  968.             break;

  969.         /* check "/.", "//", "%", and "\" (Win32) in URI */
  970.         case sw_after_slash_in_uri:

  971.             if (usual[ch >> 5] & (1U << (ch & 0x1f))) {
  972.                 state = sw_check_uri;
  973.                 break;
  974.             }

  975.             switch (ch) {
  976.             case '.':
  977.                 r->complex_uri = 1;
  978.                 state = sw_uri;
  979.                 break;
  980.             case '%':
  981.                 r->quoted_uri = 1;
  982.                 state = sw_uri;
  983.                 break;
  984.             case '/':
  985.                 r->complex_uri = 1;
  986.                 state = sw_uri;
  987.                 break;
  988. #if (NGX_WIN32)
  989.             case '\\':
  990.                 r->complex_uri = 1;
  991.                 state = sw_uri;
  992.                 break;
  993. #endif
  994.             case '?':
  995.                 r->args_start = p + 1;
  996.                 state = sw_uri;
  997.                 break;
  998.             case '#':
  999.                 r->complex_uri = 1;
  1000.                 state = sw_uri;
  1001.                 break;
  1002.             case '+':
  1003.                 r->plus_in_uri = 1;
  1004.                 break;
  1005.             default:
  1006.                 if (ch <= 0x20 || ch == 0x7f) {
  1007.                     return NGX_ERROR;
  1008.                 }
  1009.                 state = sw_check_uri;
  1010.                 break;
  1011.             }
  1012.             break;

  1013.         /* check "/", "%" and "\" (Win32) in URI */
  1014.         case sw_check_uri:

  1015.             if (usual[ch >> 5] & (1U << (ch & 0x1f))) {
  1016.                 break;
  1017.             }

  1018.             switch (ch) {
  1019.             case '/':
  1020. #if (NGX_WIN32)
  1021.                 if (r->uri_ext == p) {
  1022.                     r->complex_uri = 1;
  1023.                     state = sw_uri;
  1024.                     break;
  1025.                 }
  1026. #endif
  1027.                 r->uri_ext = NULL;
  1028.                 state = sw_after_slash_in_uri;
  1029.                 break;
  1030.             case '.':
  1031.                 r->uri_ext = p + 1;
  1032.                 break;
  1033. #if (NGX_WIN32)
  1034.             case '\\':
  1035.                 r->complex_uri = 1;
  1036.                 state = sw_after_slash_in_uri;
  1037.                 break;
  1038. #endif
  1039.             case '%':
  1040.                 r->quoted_uri = 1;
  1041.                 state = sw_uri;
  1042.                 break;
  1043.             case '?':
  1044.                 r->args_start = p + 1;
  1045.                 state = sw_uri;
  1046.                 break;
  1047.             case '#':
  1048.                 r->complex_uri = 1;
  1049.                 state = sw_uri;
  1050.                 break;
  1051.             case '+':
  1052.                 r->plus_in_uri = 1;
  1053.                 break;
  1054.             default:
  1055.                 if (ch <= 0x20 || ch == 0x7f) {
  1056.                     return NGX_ERROR;
  1057.                 }
  1058.                 break;
  1059.             }
  1060.             break;

  1061.         /* URI */
  1062.         case sw_uri:

  1063.             if (usual[ch >> 5] & (1U << (ch & 0x1f))) {
  1064.                 break;
  1065.             }

  1066.             switch (ch) {
  1067.             case '#':
  1068.                 r->complex_uri = 1;
  1069.                 break;
  1070.             default:
  1071.                 if (ch <= 0x20 || ch == 0x7f) {
  1072.                     return NGX_ERROR;
  1073.                 }
  1074.                 break;
  1075.             }
  1076.             break;
  1077.         }
  1078.     }

  1079.     return NGX_OK;
  1080. }


  1081. ngx_int_t
  1082. ngx_http_parse_complex_uri(ngx_http_request_t *r, ngx_uint_t merge_slashes)
  1083. {
  1084.     u_char  c, ch, decoded, *p, *u;
  1085.     enum {
  1086.         sw_usual = 0,
  1087.         sw_slash,
  1088.         sw_dot,
  1089.         sw_dot_dot,
  1090.         sw_quoted,
  1091.         sw_quoted_second
  1092.     } state, quoted_state;

  1093. #if (NGX_SUPPRESS_WARN)
  1094.     decoded = '\0';
  1095.     quoted_state = sw_usual;
  1096. #endif

  1097.     state = sw_usual;
  1098.     p = r->uri_start;
  1099.     u = r->uri.data;
  1100.     r->uri_ext = NULL;
  1101.     r->args_start = NULL;

  1102.     if (r->empty_path_in_uri) {
  1103.         *u++ = '/';
  1104.     }

  1105.     ch = *p++;

  1106.     while (p <= r->uri_end) {

  1107.         /*
  1108.          * we use "ch = *p++" inside the cycle, but this operation is safe,
  1109.          * because after the URI there is always at least one character:
  1110.          * the line feed
  1111.          */

  1112.         ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  1113.                        "s:%d in:'%Xd:%c'", state, ch, ch);

  1114.         switch (state) {

  1115.         case sw_usual:

  1116.             if (usual[ch >> 5] & (1U << (ch & 0x1f))) {
  1117.                 *u++ = ch;
  1118.                 ch = *p++;
  1119.                 break;
  1120.             }

  1121.             switch (ch) {
  1122. #if (NGX_WIN32)
  1123.             case '\\':
  1124.                 if (u - 2 >= r->uri.data
  1125.                     && *(u - 1) == '.' && *(u - 2) != '.')
  1126.                 {
  1127.                     u--;
  1128.                 }

  1129.                 r->uri_ext = NULL;

  1130.                 if (p == r->uri_start + r->uri.len) {

  1131.                     /*
  1132.                      * we omit the last "\" to cause redirect because
  1133.                      * the browsers do not treat "\" as "/" in relative URL path
  1134.                      */

  1135.                     break;
  1136.                 }

  1137.                 state = sw_slash;
  1138.                 *u++ = '/';
  1139.                 break;
  1140. #endif
  1141.             case '/':
  1142. #if (NGX_WIN32)
  1143.                 if (u - 2 >= r->uri.data
  1144.                     && *(u - 1) == '.' && *(u - 2) != '.')
  1145.                 {
  1146.                     u--;
  1147.                 }
  1148. #endif
  1149.                 r->uri_ext = NULL;
  1150.                 state = sw_slash;
  1151.                 *u++ = ch;
  1152.                 break;
  1153.             case '%':
  1154.                 quoted_state = state;
  1155.                 state = sw_quoted;
  1156.                 break;
  1157.             case '?':
  1158.                 r->args_start = p;
  1159.                 goto args;
  1160.             case '#':
  1161.                 goto done;
  1162.             case '.':
  1163.                 r->uri_ext = u + 1;
  1164.                 *u++ = ch;
  1165.                 break;
  1166.             case '+':
  1167.                 r->plus_in_uri = 1;
  1168.                 /* fall through */
  1169.             default:
  1170.                 *u++ = ch;
  1171.                 break;
  1172.             }

  1173.             ch = *p++;
  1174.             break;

  1175.         case sw_slash:

  1176.             if (usual[ch >> 5] & (1U << (ch & 0x1f))) {
  1177.                 state = sw_usual;
  1178.                 *u++ = ch;
  1179.                 ch = *p++;
  1180.                 break;
  1181.             }

  1182.             switch (ch) {
  1183. #if (NGX_WIN32)
  1184.             case '\\':
  1185.                 break;
  1186. #endif
  1187.             case '/':
  1188.                 if (!merge_slashes) {
  1189.                     *u++ = ch;
  1190.                 }
  1191.                 break;
  1192.             case '.':
  1193.                 state = sw_dot;
  1194.                 *u++ = ch;
  1195.                 break;
  1196.             case '%':
  1197.                 quoted_state = state;
  1198.                 state = sw_quoted;
  1199.                 break;
  1200.             case '?':
  1201.                 r->args_start = p;
  1202.                 goto args;
  1203.             case '#':
  1204.                 goto done;
  1205.             case '+':
  1206.                 r->plus_in_uri = 1;
  1207.                 /* fall through */
  1208.             default:
  1209.                 state = sw_usual;
  1210.                 *u++ = ch;
  1211.                 break;
  1212.             }

  1213.             ch = *p++;
  1214.             break;

  1215.         case sw_dot:

  1216.             if (usual[ch >> 5] & (1U << (ch & 0x1f))) {
  1217.                 state = sw_usual;
  1218.                 *u++ = ch;
  1219.                 ch = *p++;
  1220.                 break;
  1221.             }

  1222.             switch (ch) {
  1223. #if (NGX_WIN32)
  1224.             case '\\':
  1225. #endif
  1226.             case '/':
  1227.                 state = sw_slash;
  1228.                 u--;
  1229.                 break;
  1230.             case '.':
  1231.                 state = sw_dot_dot;
  1232.                 *u++ = ch;
  1233.                 break;
  1234.             case '%':
  1235.                 quoted_state = state;
  1236.                 state = sw_quoted;
  1237.                 break;
  1238.             case '?':
  1239.                 u--;
  1240.                 r->args_start = p;
  1241.                 goto args;
  1242.             case '#':
  1243.                 u--;
  1244.                 goto done;
  1245.             case '+':
  1246.                 r->plus_in_uri = 1;
  1247.                 /* fall through */
  1248.             default:
  1249.                 state = sw_usual;
  1250.                 *u++ = ch;
  1251.                 break;
  1252.             }

  1253.             ch = *p++;
  1254.             break;

  1255.         case sw_dot_dot:

  1256.             if (usual[ch >> 5] & (1U << (ch & 0x1f))) {
  1257.                 state = sw_usual;
  1258.                 *u++ = ch;
  1259.                 ch = *p++;
  1260.                 break;
  1261.             }

  1262.             switch (ch) {
  1263. #if (NGX_WIN32)
  1264.             case '\\':
  1265. #endif
  1266.             case '/':
  1267.             case '?':
  1268.             case '#':
  1269.                 u -= 4;
  1270.                 for ( ;; ) {
  1271.                     if (u < r->uri.data) {
  1272.                         return NGX_HTTP_PARSE_INVALID_REQUEST;
  1273.                     }
  1274.                     if (*u == '/') {
  1275.                         u++;
  1276.                         break;
  1277.                     }
  1278.                     u--;
  1279.                 }
  1280.                 if (ch == '?') {
  1281.                     r->args_start = p;
  1282.                     goto args;
  1283.                 }
  1284.                 if (ch == '#') {
  1285.                     goto done;
  1286.                 }
  1287.                 state = sw_slash;
  1288.                 break;
  1289.             case '%':
  1290.                 quoted_state = state;
  1291.                 state = sw_quoted;
  1292.                 break;
  1293.             case '+':
  1294.                 r->plus_in_uri = 1;
  1295.                 /* fall through */
  1296.             default:
  1297.                 state = sw_usual;
  1298.                 *u++ = ch;
  1299.                 break;
  1300.             }

  1301.             ch = *p++;
  1302.             break;

  1303.         case sw_quoted:
  1304.             r->quoted_uri = 1;

  1305.             if (ch >= '0' && ch <= '9') {
  1306.                 decoded = (u_char) (ch - '0');
  1307.                 state = sw_quoted_second;
  1308.                 ch = *p++;
  1309.                 break;
  1310.             }

  1311.             c = (u_char) (ch | 0x20);
  1312.             if (c >= 'a' && c <= 'f') {
  1313.                 decoded = (u_char) (c - 'a' + 10);
  1314.                 state = sw_quoted_second;
  1315.                 ch = *p++;
  1316.                 break;
  1317.             }

  1318.             return NGX_HTTP_PARSE_INVALID_REQUEST;

  1319.         case sw_quoted_second:
  1320.             if (ch >= '0' && ch <= '9') {
  1321.                 ch = (u_char) ((decoded << 4) + (ch - '0'));

  1322.                 if (ch == '%' || ch == '#') {
  1323.                     state = sw_usual;
  1324.                     *u++ = ch;
  1325.                     ch = *p++;
  1326.                     break;

  1327.                 } else if (ch == '\0') {
  1328.                     return NGX_HTTP_PARSE_INVALID_REQUEST;
  1329.                 }

  1330.                 state = quoted_state;
  1331.                 break;
  1332.             }

  1333.             c = (u_char) (ch | 0x20);
  1334.             if (c >= 'a' && c <= 'f') {
  1335.                 ch = (u_char) ((decoded << 4) + (c - 'a') + 10);

  1336.                 if (ch == '?') {
  1337.                     state = sw_usual;
  1338.                     *u++ = ch;
  1339.                     ch = *p++;
  1340.                     break;

  1341.                 } else if (ch == '+') {
  1342.                     r->plus_in_uri = 1;
  1343.                 }

  1344.                 state = quoted_state;
  1345.                 break;
  1346.             }

  1347.             return NGX_HTTP_PARSE_INVALID_REQUEST;
  1348.         }
  1349.     }

  1350.     if (state == sw_quoted || state == sw_quoted_second) {
  1351.         return NGX_HTTP_PARSE_INVALID_REQUEST;
  1352.     }

  1353.     if (state == sw_dot) {
  1354.         u--;

  1355.     } else if (state == sw_dot_dot) {
  1356.         u -= 4;

  1357.         for ( ;; ) {
  1358.             if (u < r->uri.data) {
  1359.                 return NGX_HTTP_PARSE_INVALID_REQUEST;
  1360.             }

  1361.             if (*u == '/') {
  1362.                 u++;
  1363.                 break;
  1364.             }

  1365.             u--;
  1366.         }
  1367.     }

  1368. done:

  1369.     r->uri.len = u - r->uri.data;

  1370.     if (r->uri_ext) {
  1371.         r->exten.len = u - r->uri_ext;
  1372.         r->exten.data = r->uri_ext;
  1373.     }

  1374.     r->uri_ext = NULL;

  1375.     return NGX_OK;

  1376. args:

  1377.     while (p < r->uri_end) {
  1378.         if (*p++ != '#') {
  1379.             continue;
  1380.         }

  1381.         r->args.len = p - 1 - r->args_start;
  1382.         r->args.data = r->args_start;
  1383.         r->args_start = NULL;

  1384.         break;
  1385.     }

  1386.     r->uri.len = u - r->uri.data;

  1387.     if (r->uri_ext) {
  1388.         r->exten.len = u - r->uri_ext;
  1389.         r->exten.data = r->uri_ext;
  1390.     }

  1391.     r->uri_ext = NULL;

  1392.     return NGX_OK;
  1393. }


  1394. ngx_int_t
  1395. ngx_http_parse_status_line(ngx_http_request_t *r, ngx_buf_t *b,
  1396.     ngx_http_status_t *status)
  1397. {
  1398.     u_char   ch;
  1399.     u_char  *p;
  1400.     enum {
  1401.         sw_start = 0,
  1402.         sw_H,
  1403.         sw_HT,
  1404.         sw_HTT,
  1405.         sw_HTTP,
  1406.         sw_first_major_digit,
  1407.         sw_major_digit,
  1408.         sw_first_minor_digit,
  1409.         sw_minor_digit,
  1410.         sw_status,
  1411.         sw_space_after_status,
  1412.         sw_status_text,
  1413.         sw_almost_done
  1414.     } state;

  1415.     state = r->state;

  1416.     for (p = b->pos; p < b->last; p++) {
  1417.         ch = *p;

  1418.         switch (state) {

  1419.         /* "HTTP/" */
  1420.         case sw_start:
  1421.             status->line_start = p;

  1422.             switch (ch) {
  1423.             case 'H':
  1424.                 state = sw_H;
  1425.                 break;
  1426.             default:
  1427.                 return NGX_ERROR;
  1428.             }
  1429.             break;

  1430.         case sw_H:
  1431.             switch (ch) {
  1432.             case 'T':
  1433.                 state = sw_HT;
  1434.                 break;
  1435.             default:
  1436.                 return NGX_ERROR;
  1437.             }
  1438.             break;

  1439.         case sw_HT:
  1440.             switch (ch) {
  1441.             case 'T':
  1442.                 state = sw_HTT;
  1443.                 break;
  1444.             default:
  1445.                 return NGX_ERROR;
  1446.             }
  1447.             break;

  1448.         case sw_HTT:
  1449.             switch (ch) {
  1450.             case 'P':
  1451.                 state = sw_HTTP;
  1452.                 break;
  1453.             default:
  1454.                 return NGX_ERROR;
  1455.             }
  1456.             break;

  1457.         case sw_HTTP:
  1458.             switch (ch) {
  1459.             case '/':
  1460.                 state = sw_first_major_digit;
  1461.                 break;
  1462.             default:
  1463.                 return NGX_ERROR;
  1464.             }
  1465.             break;

  1466.         /* the first digit of major HTTP version */
  1467.         case sw_first_major_digit:
  1468.             if (ch < '1' || ch > '9') {
  1469.                 return NGX_ERROR;
  1470.             }

  1471.             r->http_major = ch - '0';
  1472.             state = sw_major_digit;
  1473.             break;

  1474.         /* the major HTTP version or dot */
  1475.         case sw_major_digit:
  1476.             if (ch == '.') {
  1477.                 state = sw_first_minor_digit;
  1478.                 break;
  1479.             }

  1480.             if (ch < '0' || ch > '9') {
  1481.                 return NGX_ERROR;
  1482.             }

  1483.             if (r->http_major > 99) {
  1484.                 return NGX_ERROR;
  1485.             }

  1486.             r->http_major = r->http_major * 10 + (ch - '0');
  1487.             break;

  1488.         /* the first digit of minor HTTP version */
  1489.         case sw_first_minor_digit:
  1490.             if (ch < '0' || ch > '9') {
  1491.                 return NGX_ERROR;
  1492.             }

  1493.             r->http_minor = ch - '0';
  1494.             state = sw_minor_digit;
  1495.             break;

  1496.         /* the minor HTTP version or the end of the request line */
  1497.         case sw_minor_digit:
  1498.             if (ch == ' ') {
  1499.                 state = sw_status;
  1500.                 break;
  1501.             }

  1502.             if (ch < '0' || ch > '9') {
  1503.                 return NGX_ERROR;
  1504.             }

  1505.             if (r->http_minor > 99) {
  1506.                 return NGX_ERROR;
  1507.             }

  1508.             r->http_minor = r->http_minor * 10 + (ch - '0');
  1509.             break;

  1510.         /* HTTP status code */
  1511.         case sw_status:
  1512.             if (ch == ' ') {
  1513.                 break;
  1514.             }

  1515.             if (ch < '0' || ch > '9') {
  1516.                 return NGX_ERROR;
  1517.             }

  1518.             status->code = status->code * 10 + (ch - '0');

  1519.             if (++status->count == 3) {
  1520.                 state = sw_space_after_status;
  1521.                 status->start = p - 2;
  1522.             }

  1523.             break;

  1524.         /* space or end of line */
  1525.         case sw_space_after_status:
  1526.             switch (ch) {
  1527.             case ' ':
  1528.                 state = sw_status_text;
  1529.                 break;
  1530.             case '.':                    /* IIS may send 403.1, 403.2, etc */
  1531.                 state = sw_status_text;
  1532.                 break;
  1533.             case CR:
  1534.                 state = sw_almost_done;
  1535.                 break;
  1536.             case LF:
  1537.                 goto done;
  1538.             default:
  1539.                 return NGX_ERROR;
  1540.             }
  1541.             break;

  1542.         /* any text until end of line */
  1543.         case sw_status_text:
  1544.             switch (ch) {
  1545.             case CR:
  1546.                 state = sw_almost_done;

  1547.                 break;
  1548.             case LF:
  1549.                 goto done;
  1550.             }
  1551.             break;

  1552.         /* end of status line */
  1553.         case sw_almost_done:
  1554.             status->end = p - 1;
  1555.             switch (ch) {
  1556.             case LF:
  1557.                 goto done;
  1558.             default:
  1559.                 return NGX_ERROR;
  1560.             }
  1561.         }
  1562.     }

  1563.     b->pos = p;
  1564.     r->state = state;

  1565.     return NGX_AGAIN;

  1566. done:

  1567.     b->pos = p + 1;

  1568.     if (status->end == NULL) {
  1569.         status->end = p;
  1570.     }

  1571.     status->http_version = r->http_major * 1000 + r->http_minor;
  1572.     r->state = sw_start;

  1573.     return NGX_OK;
  1574. }


  1575. ngx_int_t
  1576. ngx_http_parse_unsafe_uri(ngx_http_request_t *r, ngx_str_t *uri,
  1577.     ngx_str_t *args, ngx_uint_t *flags)
  1578. {
  1579.     u_char      ch, *p, *src, *dst;
  1580.     size_t      len;
  1581.     ngx_uint_t  quoted;

  1582.     len = uri->len;
  1583.     p = uri->data;
  1584.     quoted = 0;

  1585.     if (len == 0 || p[0] == '?') {
  1586.         goto unsafe;
  1587.     }

  1588.     if (p[0] == '.' && len > 1 && p[1] == '.'
  1589.         && (len == 2 || ngx_path_separator(p[2])))
  1590.     {
  1591.         goto unsafe;
  1592.     }

  1593.     for ( /* void */ ; len; len--) {

  1594.         ch = *p++;

  1595.         if (ch == '%') {
  1596.             quoted = 1;
  1597.             continue;
  1598.         }

  1599.         if (usual[ch >> 5] & (1U << (ch & 0x1f))) {
  1600.             continue;
  1601.         }

  1602.         if (ch == '?') {
  1603.             args->len = len - 1;
  1604.             args->data = p;
  1605.             uri->len -= len;

  1606.             break;
  1607.         }

  1608.         if (ch == '\0') {
  1609.             goto unsafe;
  1610.         }

  1611.         if (ngx_path_separator(ch) && len > 2) {

  1612.             /* detect "/../" and "/.." */

  1613.             if (p[0] == '.' && p[1] == '.'
  1614.                 && (len == 3 || ngx_path_separator(p[2])))
  1615.             {
  1616.                 goto unsafe;
  1617.             }
  1618.         }
  1619.     }

  1620.     if (quoted) {
  1621.         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  1622.                        "escaped URI: \"%V\"", uri);

  1623.         src = uri->data;

  1624.         dst = ngx_pnalloc(r->pool, uri->len);
  1625.         if (dst == NULL) {
  1626.             return NGX_ERROR;
  1627.         }

  1628.         uri->data = dst;

  1629.         ngx_unescape_uri(&dst, &src, uri->len, 0);

  1630.         uri->len = dst - uri->data;

  1631.         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  1632.                        "unescaped URI: \"%V\"", uri);

  1633.         len = uri->len;
  1634.         p = uri->data;

  1635.         if (p[0] == '.' && len > 1 && p[1] == '.'
  1636.             && (len == 2 || ngx_path_separator(p[2])))
  1637.         {
  1638.             goto unsafe;
  1639.         }

  1640.         for ( /* void */ ; len; len--) {

  1641.             ch = *p++;

  1642.             if (ch == '\0') {
  1643.                 goto unsafe;
  1644.             }

  1645.             if (ngx_path_separator(ch) && len > 2) {

  1646.                 /* detect "/../" and "/.." */

  1647.                 if (p[0] == '.' && p[1] == '.'
  1648.                     && (len == 3 || ngx_path_separator(p[2])))
  1649.                 {
  1650.                     goto unsafe;
  1651.                 }
  1652.             }
  1653.         }
  1654.     }

  1655.     return NGX_OK;

  1656. unsafe:

  1657.     if (*flags & NGX_HTTP_LOG_UNSAFE) {
  1658.         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  1659.                       "unsafe URI \"%V\" was detected", uri);
  1660.     }

  1661.     return NGX_ERROR;
  1662. }


  1663. ngx_table_elt_t *
  1664. ngx_http_parse_multi_header_lines(ngx_http_request_t *r,
  1665.     ngx_table_elt_t *headers, ngx_str_t *name, ngx_str_t *value)
  1666. {
  1667.     return ngx_http_parse_multi_header_lines_internal(r, headers, name, value,
  1668.                                                       ',');
  1669. }


  1670. ngx_table_elt_t *
  1671. ngx_http_parse_cookie_lines(ngx_http_request_t *r,
  1672.     ngx_table_elt_t *headers, ngx_str_t *name, ngx_str_t *value)
  1673. {
  1674.     return ngx_http_parse_multi_header_lines_internal(r, headers, name, value,
  1675.                                                       ';');
  1676. }


  1677. static ngx_table_elt_t *
  1678. ngx_http_parse_multi_header_lines_internal(ngx_http_request_t *r,
  1679.     ngx_table_elt_t *headers, ngx_str_t *name, ngx_str_t *value, u_char sep)
  1680. {
  1681.     u_char           *start, *last, *end, ch;
  1682.     ngx_table_elt_t  *h;

  1683.     for (h = headers; h; h = h->next) {

  1684.         ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  1685.                        "parse header: \"%V: %V\"", &h->key, &h->value);

  1686.         if (name->len > h->value.len) {
  1687.             continue;
  1688.         }

  1689.         start = h->value.data;
  1690.         end = h->value.data + h->value.len;

  1691.         while (start < end) {

  1692.             if (ngx_strncasecmp(start, name->data, name->len) != 0) {
  1693.                 goto skip;
  1694.             }

  1695.             for (start += name->len; start < end && *start == ' '; start++) {
  1696.                 /* void */
  1697.             }

  1698.             if (value == NULL) {
  1699.                 if (start == end || *start == sep) {
  1700.                     return h;
  1701.                 }

  1702.                 goto skip;
  1703.             }

  1704.             if (start == end || *start++ != '=') {
  1705.                 /* the invalid header value */
  1706.                 goto skip;
  1707.             }

  1708.             while (start < end && *start == ' ') { start++; }

  1709.             for (last = start; last < end && *last != sep; last++) {
  1710.                 /* void */
  1711.             }

  1712.             value->len = last - start;
  1713.             value->data = start;

  1714.             return h;

  1715.         skip:

  1716.             while (start < end) {
  1717.                 ch = *start++;
  1718.                 if (ch == sep) {
  1719.                     break;
  1720.                 }
  1721.             }

  1722.             while (start < end && *start == ' ') { start++; }
  1723.         }
  1724.     }

  1725.     return NULL;
  1726. }


  1727. ngx_table_elt_t *
  1728. ngx_http_parse_set_cookie_lines(ngx_http_request_t *r,
  1729.     ngx_table_elt_t *headers, ngx_str_t *name, ngx_str_t *value)
  1730. {
  1731.     u_char           *start, *last, *end;
  1732.     ngx_table_elt_t  *h;

  1733.     for (h = headers; h; h = h->next) {

  1734.         ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  1735.                        "parse header: \"%V: %V\"", &h->key, &h->value);

  1736.         if (name->len >= h->value.len) {
  1737.             continue;
  1738.         }

  1739.         start = h->value.data;
  1740.         end = h->value.data + h->value.len;

  1741.         if (ngx_strncasecmp(start, name->data, name->len) != 0) {
  1742.             continue;
  1743.         }

  1744.         for (start += name->len; start < end && *start == ' '; start++) {
  1745.             /* void */
  1746.         }

  1747.         if (start == end || *start++ != '=') {
  1748.             /* the invalid header value */
  1749.             continue;
  1750.         }

  1751.         while (start < end && *start == ' ') { start++; }

  1752.         for (last = start; last < end && *last != ';'; last++) {
  1753.             /* void */
  1754.         }

  1755.         value->len = last - start;
  1756.         value->data = start;

  1757.         return h;
  1758.     }

  1759.     return NULL;
  1760. }


  1761. ngx_int_t
  1762. ngx_http_arg(ngx_http_request_t *r, u_char *name, size_t len, ngx_str_t *value)
  1763. {
  1764.     u_char  *p, *last;

  1765.     if (r->args.len == 0) {
  1766.         return NGX_DECLINED;
  1767.     }

  1768.     p = r->args.data;
  1769.     last = p + r->args.len;

  1770.     for ( /* void */ ; p < last; p++) {

  1771.         /* we need '=' after name, so drop one char from last */

  1772.         p = ngx_strlcasestrn(p, last - 1, name, len - 1);

  1773.         if (p == NULL) {
  1774.             return NGX_DECLINED;
  1775.         }

  1776.         if ((p == r->args.data || *(p - 1) == '&') && *(p + len) == '=') {

  1777.             value->data = p + len + 1;

  1778.             p = ngx_strlchr(p, last, '&');

  1779.             if (p == NULL) {
  1780.                 p = r->args.data + r->args.len;
  1781.             }

  1782.             value->len = p - value->data;

  1783.             return NGX_OK;
  1784.         }
  1785.     }

  1786.     return NGX_DECLINED;
  1787. }


  1788. void
  1789. ngx_http_split_args(ngx_http_request_t *r, ngx_str_t *uri, ngx_str_t *args)
  1790. {
  1791.     u_char  *p, *last;

  1792.     last = uri->data + uri->len;

  1793.     p = ngx_strlchr(uri->data, last, '?');

  1794.     if (p) {
  1795.         uri->len = p - uri->data;
  1796.         p++;
  1797.         args->len = last - p;
  1798.         args->data = p;

  1799.     } else {
  1800.         args->len = 0;
  1801.     }
  1802. }


  1803. ngx_int_t
  1804. ngx_http_parse_chunked(ngx_http_request_t *r, ngx_buf_t *b,
  1805.     ngx_http_chunked_t *ctx, ngx_uint_t keep_trailers)
  1806. {
  1807.     u_char     *pos, ch, c;
  1808.     ngx_int_t   rc;
  1809.     enum {
  1810.         sw_chunk_start = 0,
  1811.         sw_chunk_size,
  1812.         sw_chunk_extension,
  1813.         sw_chunk_extension_almost_done,
  1814.         sw_chunk_data,
  1815.         sw_after_data,
  1816.         sw_after_data_almost_done,
  1817.         sw_last_chunk_extension,
  1818.         sw_last_chunk_extension_almost_done,
  1819.         sw_trailer,
  1820.         sw_trailer_almost_done,
  1821.         sw_trailer_header,
  1822.         sw_trailer_header_almost_done
  1823.     } state;

  1824.     state = ctx->state;

  1825.     if (state == sw_chunk_data && ctx->size == 0) {
  1826.         state = sw_after_data;
  1827.     }

  1828.     rc = NGX_AGAIN;

  1829.     for (pos = b->pos; pos < b->last; pos++) {

  1830.         ch = *pos;

  1831.         ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  1832.                        "http chunked byte: %02Xd s:%d", ch, state);

  1833.         switch (state) {

  1834.         case sw_chunk_start:
  1835.             if (ch >= '0' && ch <= '9') {
  1836.                 state = sw_chunk_size;
  1837.                 ctx->size = ch - '0';
  1838.                 break;
  1839.             }

  1840.             c = (u_char) (ch | 0x20);

  1841.             if (c >= 'a' && c <= 'f') {
  1842.                 state = sw_chunk_size;
  1843.                 ctx->size = c - 'a' + 10;
  1844.                 break;
  1845.             }

  1846.             goto invalid;

  1847.         case sw_chunk_size:
  1848.             if (ctx->size > NGX_MAX_OFF_T_VALUE / 16) {
  1849.                 goto invalid;
  1850.             }

  1851.             if (ch >= '0' && ch <= '9') {
  1852.                 ctx->size = ctx->size * 16 + (ch - '0');
  1853.                 break;
  1854.             }

  1855.             c = (u_char) (ch | 0x20);

  1856.             if (c >= 'a' && c <= 'f') {
  1857.                 ctx->size = ctx->size * 16 + (c - 'a' + 10);
  1858.                 break;
  1859.             }

  1860.             if (ctx->size == 0) {

  1861.                 switch (ch) {
  1862.                 case CR:
  1863.                     state = sw_last_chunk_extension_almost_done;
  1864.                     break;
  1865.                 case ';':
  1866.                 case ' ':
  1867.                 case '\t':
  1868.                     state = sw_last_chunk_extension;
  1869.                     break;
  1870.                 default:
  1871.                     goto invalid;
  1872.                 }

  1873.                 break;
  1874.             }

  1875.             switch (ch) {
  1876.             case CR:
  1877.                 state = sw_chunk_extension_almost_done;
  1878.                 break;
  1879.             case ';':
  1880.             case ' ':
  1881.             case '\t':
  1882.                 state = sw_chunk_extension;
  1883.                 break;
  1884.             default:
  1885.                 goto invalid;
  1886.             }

  1887.             break;

  1888.         case sw_chunk_extension:
  1889.             switch (ch) {
  1890.             case CR:
  1891.                 state = sw_chunk_extension_almost_done;
  1892.                 break;
  1893.             case LF:
  1894.                 goto invalid;
  1895.             }
  1896.             break;

  1897.         case sw_chunk_extension_almost_done:
  1898.             if (ch == LF) {
  1899.                 state = sw_chunk_data;
  1900.                 break;
  1901.             }
  1902.             goto invalid;

  1903.         case sw_chunk_data:
  1904.             rc = NGX_OK;
  1905.             goto data;

  1906.         case sw_after_data:
  1907.             switch (ch) {
  1908.             case CR:
  1909.                 state = sw_after_data_almost_done;
  1910.                 break;
  1911.             default:
  1912.                 goto invalid;
  1913.             }
  1914.             break;

  1915.         case sw_after_data_almost_done:
  1916.             if (ch == LF) {
  1917.                 state = sw_chunk_start;
  1918.                 break;
  1919.             }
  1920.             goto invalid;

  1921.         case sw_last_chunk_extension:
  1922.             switch (ch) {
  1923.             case CR:
  1924.                 state = sw_last_chunk_extension_almost_done;
  1925.                 break;
  1926.             case LF:
  1927.                 goto invalid;
  1928.             }
  1929.             break;

  1930.         case sw_last_chunk_extension_almost_done:
  1931.             if (ch == LF) {
  1932.                 if (keep_trailers) {
  1933.                     goto done;
  1934.                 }
  1935.                 state = sw_trailer;
  1936.                 break;
  1937.             }
  1938.             goto invalid;

  1939.         case sw_trailer:
  1940.             switch (ch) {
  1941.             case CR:
  1942.                 state = sw_trailer_almost_done;
  1943.                 break;
  1944.             case LF:
  1945.                 goto invalid;
  1946.             default:
  1947.                 state = sw_trailer_header;
  1948.             }
  1949.             break;

  1950.         case sw_trailer_almost_done:
  1951.             if (ch == LF) {
  1952.                 goto done;
  1953.             }
  1954.             goto invalid;

  1955.         case sw_trailer_header:
  1956.             switch (ch) {
  1957.             case CR:
  1958.                 state = sw_trailer_header_almost_done;
  1959.                 break;
  1960.             case LF:
  1961.                 goto invalid;
  1962.             }
  1963.             break;

  1964.         case sw_trailer_header_almost_done:
  1965.             if (ch == LF) {
  1966.                 state = sw_trailer;
  1967.                 break;
  1968.             }
  1969.             goto invalid;

  1970.         }
  1971.     }

  1972. data:

  1973.     ctx->state = state;
  1974.     b->pos = pos;

  1975.     if (ctx->size > NGX_MAX_OFF_T_VALUE - 5) {
  1976.         goto invalid;
  1977.     }

  1978.     switch (state) {

  1979.     case sw_chunk_start:
  1980.         ctx->length = 5 /* "0" CRLF CRLF */;
  1981.         break;
  1982.     case sw_chunk_size:
  1983.         ctx->length = 2 /* CRLF */
  1984.                       + (ctx->size ? ctx->size + 7 /* CRLF "0" CRLF CRLF */
  1985.                                    : 2 /* CRLF */);
  1986.         break;
  1987.     case sw_chunk_extension:
  1988.         ctx->length = 2 /* CRLF */ + ctx->size + 7 /* CRLF "0" CRLF CRLF */;
  1989.         break;
  1990.     case sw_chunk_extension_almost_done:
  1991.         ctx->length = 1 /* LF */ + ctx->size + 7 /* CRLF "0" CRLF CRLF */;
  1992.         break;
  1993.     case sw_chunk_data:
  1994.         ctx->length = ctx->size + 7 /* CRLF "0" CRLF CRLF */;
  1995.         break;
  1996.     case sw_after_data:
  1997.         ctx->length = 7 /* CRLF "0" CRLF CRLF */;
  1998.         break;
  1999.     case sw_after_data_almost_done:
  2000.         ctx->length = 6 /* LF "0" CRLF CRLF */;
  2001.         break;
  2002.     case sw_last_chunk_extension:
  2003.         ctx->length = 4 /* CRLF CRLF */;
  2004.         break;
  2005.     case sw_last_chunk_extension_almost_done:
  2006.         ctx->length = 3 /* LF CRLF */;
  2007.         break;
  2008.     case sw_trailer:
  2009.         ctx->length = 2 /* CRLF */;
  2010.         break;
  2011.     case sw_trailer_almost_done:
  2012.         ctx->length = 1 /* LF */;
  2013.         break;
  2014.     case sw_trailer_header:
  2015.         ctx->length = 4 /* CRLF CRLF */;
  2016.         break;
  2017.     case sw_trailer_header_almost_done:
  2018.         ctx->length = 3 /* LF CRLF */;
  2019.         break;

  2020.     }

  2021.     return rc;

  2022. done:

  2023.     ctx->state = 0;
  2024.     b->pos = pos + 1;

  2025.     return NGX_DONE;

  2026. invalid:

  2027.     return NGX_ERROR;
  2028. }