src/http/ngx_http_parse.c - nginx-1.31.9 nginx/ @ 2a3a19c6f

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.             /* IIS may send the duplicate "HTTP/1.1 ..." lines */
  815.             if (ch == '/'
  816.                 && r->upstream
  817.                 && p - r->header_name_start == 4
  818.                 && ngx_strncmp(r->header_name_start, "HTTP", 4) == 0)
  819.             {
  820.                 state = sw_ignore_line;
  821.                 break;
  822.             }

  823.             if (ch <= 0x20 || ch == 0x7f) {
  824.                 r->header_end = p;
  825.                 return NGX_HTTP_PARSE_INVALID_HEADER;
  826.             }

  827.             r->invalid_header = 1;

  828.             break;

  829.         /* space* before header value */
  830.         case sw_space_before_value:
  831.             switch (ch) {
  832.             case ' ':
  833.                 break;
  834.             case CR:
  835.                 r->header_start = p;
  836.                 r->header_end = p;
  837.                 state = sw_almost_done;
  838.                 break;
  839.             case LF:
  840.                 r->header_start = p;
  841.                 r->header_end = p;
  842.                 goto done;
  843.             case '\0':
  844.                 r->header_end = p;
  845.                 return NGX_HTTP_PARSE_INVALID_HEADER;
  846.             default:
  847.                 r->header_start = p;
  848.                 state = sw_value;
  849.                 break;
  850.             }
  851.             break;

  852.         /* header value */
  853.         case sw_value:
  854.             switch (ch) {
  855.             case ' ':
  856.                 r->header_end = p;
  857.                 state = sw_space_after_value;
  858.                 break;
  859.             case CR:
  860.                 r->header_end = p;
  861.                 state = sw_almost_done;
  862.                 break;
  863.             case LF:
  864.                 r->header_end = p;
  865.                 goto done;
  866.             case '\0':
  867.                 r->header_end = p;
  868.                 return NGX_HTTP_PARSE_INVALID_HEADER;
  869.             }
  870.             break;

  871.         /* space* before end of header line */
  872.         case sw_space_after_value:
  873.             switch (ch) {
  874.             case ' ':
  875.                 break;
  876.             case CR:
  877.                 state = sw_almost_done;
  878.                 break;
  879.             case LF:
  880.                 goto done;
  881.             case '\0':
  882.                 r->header_end = p;
  883.                 return NGX_HTTP_PARSE_INVALID_HEADER;
  884.             default:
  885.                 state = sw_value;
  886.                 break;
  887.             }
  888.             break;

  889.         /* ignore header line */
  890.         case sw_ignore_line:
  891.             switch (ch) {
  892.             case LF:
  893.                 state = sw_start;
  894.                 break;
  895.             default:
  896.                 break;
  897.             }
  898.             break;

  899.         /* end of header line */
  900.         case sw_almost_done:
  901.             switch (ch) {
  902.             case LF:
  903.                 goto done;
  904.             case CR:
  905.                 break;
  906.             default:
  907.                 return NGX_HTTP_PARSE_INVALID_HEADER;
  908.             }
  909.             break;

  910.         /* end of header */
  911.         case sw_header_almost_done:
  912.             switch (ch) {
  913.             case LF:
  914.                 goto header_done;
  915.             default:
  916.                 return NGX_HTTP_PARSE_INVALID_HEADER;
  917.             }
  918.         }
  919.     }

  920.     b->pos = p;
  921.     r->state = state;
  922.     r->header_hash = hash;
  923.     r->lowcase_index = i;

  924.     return NGX_AGAIN;

  925. done:

  926.     b->pos = p + 1;
  927.     r->state = sw_start;
  928.     r->header_hash = hash;
  929.     r->lowcase_index = i;

  930.     return NGX_OK;

  931. header_done:

  932.     b->pos = p + 1;
  933.     r->state = sw_start;

  934.     return NGX_HTTP_PARSE_HEADER_DONE;
  935. }


  936. ngx_int_t
  937. ‌ngx_http_parse_uri(ngx_http_request_t *r)
  938. {
  939.     u_char  *p, ch;
  940. ‌    enum {
  941.         sw_start = 0,
  942.         sw_after_slash_in_uri,
  943.         sw_check_uri,
  944.         sw_uri
  945.     } state;

  946.     state = sw_start;

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

  948.         ch = *p;

  949.         switch (state) {

  950.         case sw_start:

  951.             if (ch != '/') {
  952.                 return NGX_ERROR;
  953.             }

  954.             state = sw_after_slash_in_uri;
  955.             break;

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

  958.             if (usual[ch >> 5] & (1U << (ch & 0x1f))) {
  959.                 state = sw_check_uri;
  960.                 break;
  961.             }

  962.             switch (ch) {
  963.             case '.':
  964.                 r->complex_uri = 1;
  965.                 state = sw_uri;
  966.                 break;
  967.             case '%':
  968.                 r->quoted_uri = 1;
  969.                 state = sw_uri;
  970.                 break;
  971.             case '/':
  972.                 r->complex_uri = 1;
  973.                 state = sw_uri;
  974.                 break;
  975. #if (NGX_WIN32)
  976.             case '\\':
  977.                 r->complex_uri = 1;
  978.                 state = sw_uri;
  979.                 break;
  980. #endif
  981.             case '?':
  982.                 r->args_start = p + 1;
  983.                 state = sw_uri;
  984.                 break;
  985.             case '#':
  986.                 r->complex_uri = 1;
  987.                 state = sw_uri;
  988.                 break;
  989.             case '+':
  990.                 r->plus_in_uri = 1;
  991.                 break;
  992.             default:
  993.                 if (ch <= 0x20 || ch == 0x7f) {
  994.                     return NGX_ERROR;
  995.                 }
  996.                 state = sw_check_uri;
  997.                 break;
  998.             }
  999.             break;

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

  1002.             if (usual[ch >> 5] & (1U << (ch & 0x1f))) {
  1003.                 break;
  1004.             }

  1005.             switch (ch) {
  1006.             case '/':
  1007. #if (NGX_WIN32)
  1008.                 if (r->uri_ext == p) {
  1009.                     r->complex_uri = 1;
  1010.                     state = sw_uri;
  1011.                     break;
  1012.                 }
  1013. #endif
  1014.                 r->uri_ext = NULL;
  1015.                 state = sw_after_slash_in_uri;
  1016.                 break;
  1017.             case '.':
  1018.                 r->uri_ext = p + 1;
  1019.                 break;
  1020. #if (NGX_WIN32)
  1021.             case '\\':
  1022.                 r->complex_uri = 1;
  1023.                 state = sw_after_slash_in_uri;
  1024.                 break;
  1025. #endif
  1026.             case '%':
  1027.                 r->quoted_uri = 1;
  1028.                 state = sw_uri;
  1029.                 break;
  1030.             case '?':
  1031.                 r->args_start = p + 1;
  1032.                 state = sw_uri;
  1033.                 break;
  1034.             case '#':
  1035.                 r->complex_uri = 1;
  1036.                 state = sw_uri;
  1037.                 break;
  1038.             case '+':
  1039.                 r->plus_in_uri = 1;
  1040.                 break;
  1041.             default:
  1042.                 if (ch <= 0x20 || ch == 0x7f) {
  1043.                     return NGX_ERROR;
  1044.                 }
  1045.                 break;
  1046.             }
  1047.             break;

  1048.         /* URI */
  1049.         case sw_uri:

  1050.             if (usual[ch >> 5] & (1U << (ch & 0x1f))) {
  1051.                 break;
  1052.             }

  1053.             switch (ch) {
  1054.             case '#':
  1055.                 r->complex_uri = 1;
  1056.                 break;
  1057.             default:
  1058.                 if (ch <= 0x20 || ch == 0x7f) {
  1059.                     return NGX_ERROR;
  1060.                 }
  1061.                 break;
  1062.             }
  1063.             break;
  1064.         }
  1065.     }

  1066.     return NGX_OK;
  1067. }


  1068. ngx_int_t
  1069. ‌ngx_http_parse_complex_uri(ngx_http_request_t *r, ngx_uint_t merge_slashes)
  1070. {
  1071.     u_char  c, ch, decoded, *p, *u;
  1072. ‌    enum {
  1073.         sw_usual = 0,
  1074.         sw_slash,
  1075.         sw_dot,
  1076.         sw_dot_dot,
  1077.         sw_quoted,
  1078.         sw_quoted_second
  1079.     } state, quoted_state;

  1080. #if (NGX_SUPPRESS_WARN)
  1081.     decoded = '\0';
  1082.     quoted_state = sw_usual;
  1083. #endif

  1084.     state = sw_usual;
  1085.     p = r->uri_start;
  1086.     u = r->uri.data;
  1087.     r->uri_ext = NULL;
  1088.     r->args_start = NULL;

  1089.     if (r->empty_path_in_uri) {
  1090.         *u++ = '/';
  1091.     }

  1092.     ch = *p++;

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

  1094.         /*
  1095.          * we use "ch = *p++" inside the cycle, but this operation is safe,
  1096.          * because after the URI there is always at least one character:
  1097.          * the line feed
  1098.          */

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

  1101.         switch (state) {

  1102.         case sw_usual:

  1103.             if (usual[ch >> 5] & (1U << (ch & 0x1f))) {
  1104.                 *u++ = ch;
  1105.                 ch = *p++;
  1106.                 break;
  1107.             }

  1108.             switch (ch) {
  1109. #if (NGX_WIN32)
  1110.             case '\\':
  1111.                 if (u - 2 >= r->uri.data
  1112.                     && *(u - 1) == '.' && *(u - 2) != '.')
  1113.                 {
  1114.                     u--;
  1115.                 }

  1116.                 r->uri_ext = NULL;

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

  1118.                     /*
  1119.                      * we omit the last "\" to cause redirect because
  1120.                      * the browsers do not treat "\" as "/" in relative URL path
  1121.                      */

  1122.                     break;
  1123.                 }

  1124.                 state = sw_slash;
  1125.                 *u++ = '/';
  1126.                 break;
  1127. #endif
  1128.             case '/':
  1129. #if (NGX_WIN32)
  1130.                 if (u - 2 >= r->uri.data
  1131.                     && *(u - 1) == '.' && *(u - 2) != '.')
  1132.                 {
  1133.                     u--;
  1134.                 }
  1135. #endif
  1136.                 r->uri_ext = NULL;
  1137.                 state = sw_slash;
  1138.                 *u++ = ch;
  1139.                 break;
  1140.             case '%':
  1141.                 quoted_state = state;
  1142.                 state = sw_quoted;
  1143.                 break;
  1144.             case '?':
  1145.                 r->args_start = p;
  1146.                 goto args;
  1147.             case '#':
  1148.                 goto done;
  1149.             case '.':
  1150.                 r->uri_ext = u + 1;
  1151.                 *u++ = ch;
  1152.                 break;
  1153.             case '+':
  1154.                 r->plus_in_uri = 1;
  1155.                 /* fall through */
  1156.             default:
  1157.                 *u++ = ch;
  1158.                 break;
  1159.             }

  1160.             ch = *p++;
  1161.             break;

  1162.         case sw_slash:

  1163.             if (usual[ch >> 5] & (1U << (ch & 0x1f))) {
  1164.                 state = sw_usual;
  1165.                 *u++ = ch;
  1166.                 ch = *p++;
  1167.                 break;
  1168.             }

  1169.             switch (ch) {
  1170. #if (NGX_WIN32)
  1171.             case '\\':
  1172.                 break;
  1173. #endif
  1174.             case '/':
  1175.                 if (!merge_slashes) {
  1176.                     *u++ = ch;
  1177.                 }
  1178.                 break;
  1179.             case '.':
  1180.                 state = sw_dot;
  1181.                 *u++ = ch;
  1182.                 break;
  1183.             case '%':
  1184.                 quoted_state = state;
  1185.                 state = sw_quoted;
  1186.                 break;
  1187.             case '?':
  1188.                 r->args_start = p;
  1189.                 goto args;
  1190.             case '#':
  1191.                 goto done;
  1192.             case '+':
  1193.                 r->plus_in_uri = 1;
  1194.                 /* fall through */
  1195.             default:
  1196.                 state = sw_usual;
  1197.                 *u++ = ch;
  1198.                 break;
  1199.             }

  1200.             ch = *p++;
  1201.             break;

  1202.         case sw_dot:

  1203.             if (usual[ch >> 5] & (1U << (ch & 0x1f))) {
  1204.                 state = sw_usual;
  1205.                 *u++ = ch;
  1206.                 ch = *p++;
  1207.                 break;
  1208.             }

  1209.             switch (ch) {
  1210. #if (NGX_WIN32)
  1211.             case '\\':
  1212. #endif
  1213.             case '/':
  1214.                 state = sw_slash;
  1215.                 u--;
  1216.                 break;
  1217.             case '.':
  1218.                 state = sw_dot_dot;
  1219.                 *u++ = ch;
  1220.                 break;
  1221.             case '%':
  1222.                 quoted_state = state;
  1223.                 state = sw_quoted;
  1224.                 break;
  1225.             case '?':
  1226.                 u--;
  1227.                 r->args_start = p;
  1228.                 goto args;
  1229.             case '#':
  1230.                 u--;
  1231.                 goto done;
  1232.             case '+':
  1233.                 r->plus_in_uri = 1;
  1234.                 /* fall through */
  1235.             default:
  1236.                 state = sw_usual;
  1237.                 *u++ = ch;
  1238.                 break;
  1239.             }

  1240.             ch = *p++;
  1241.             break;

  1242.         case sw_dot_dot:

  1243.             if (usual[ch >> 5] & (1U << (ch & 0x1f))) {
  1244.                 state = sw_usual;
  1245.                 *u++ = ch;
  1246.                 ch = *p++;
  1247.                 break;
  1248.             }

  1249.             switch (ch) {
  1250. #if (NGX_WIN32)
  1251.             case '\\':
  1252. #endif
  1253.             case '/':
  1254.             case '?':
  1255.             case '#':
  1256.                 u -= 4;
  1257.                 for ( ;; ) {
  1258.                     if (u < r->uri.data) {
  1259.                         return NGX_HTTP_PARSE_INVALID_REQUEST;
  1260.                     }
  1261.                     if (*u == '/') {
  1262.                         u++;
  1263.                         break;
  1264.                     }
  1265.                     u--;
  1266.                 }
  1267.                 if (ch == '?') {
  1268.                     r->args_start = p;
  1269.                     goto args;
  1270.                 }
  1271.                 if (ch == '#') {
  1272.                     goto done;
  1273.                 }
  1274.                 state = sw_slash;
  1275.                 break;
  1276.             case '%':
  1277.                 quoted_state = state;
  1278.                 state = sw_quoted;
  1279.                 break;
  1280.             case '+':
  1281.                 r->plus_in_uri = 1;
  1282.                 /* fall through */
  1283.             default:
  1284.                 state = sw_usual;
  1285.                 *u++ = ch;
  1286.                 break;
  1287.             }

  1288.             ch = *p++;
  1289.             break;

  1290.         case sw_quoted:
  1291.             r->quoted_uri = 1;

  1292.             if (ch >= '0' && ch <= '9') {
  1293.                 decoded = (u_char) (ch - '0');
  1294.                 state = sw_quoted_second;
  1295.                 ch = *p++;
  1296.                 break;
  1297.             }

  1298.             c = (u_char) (ch | 0x20);
  1299.             if (c >= 'a' && c <= 'f') {
  1300.                 decoded = (u_char) (c - 'a' + 10);
  1301.                 state = sw_quoted_second;
  1302.                 ch = *p++;
  1303.                 break;
  1304.             }

  1305.             return NGX_HTTP_PARSE_INVALID_REQUEST;

  1306.         case sw_quoted_second:
  1307.             if (ch >= '0' && ch <= '9') {
  1308.                 ch = (u_char) ((decoded << 4) + (ch - '0'));

  1309.                 if (ch == '%' || ch == '#') {
  1310.                     state = sw_usual;
  1311.                     *u++ = ch;
  1312.                     ch = *p++;
  1313.                     break;

  1314.                 } else if (ch == '\0') {
  1315.                     return NGX_HTTP_PARSE_INVALID_REQUEST;
  1316.                 }

  1317.                 state = quoted_state;
  1318.                 break;
  1319.             }

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

  1323.                 if (ch == '?') {
  1324.                     state = sw_usual;
  1325.                     *u++ = ch;
  1326.                     ch = *p++;
  1327.                     break;

  1328.                 } else if (ch == '+') {
  1329.                     r->plus_in_uri = 1;
  1330.                 }

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

  1334.             return NGX_HTTP_PARSE_INVALID_REQUEST;
  1335.         }
  1336.     }

  1337.     if (state == sw_quoted || state == sw_quoted_second) {
  1338.         return NGX_HTTP_PARSE_INVALID_REQUEST;
  1339.     }

  1340.     if (state == sw_dot) {
  1341.         u--;

  1342.     } else if (state == sw_dot_dot) {
  1343.         u -= 4;

  1344.         for ( ;; ) {
  1345.             if (u < r->uri.data) {
  1346.                 return NGX_HTTP_PARSE_INVALID_REQUEST;
  1347.             }

  1348.             if (*u == '/') {
  1349.                 u++;
  1350.                 break;
  1351.             }

  1352.             u--;
  1353.         }
  1354.     }

  1355. done:

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

  1357.     if (r->uri_ext) {
  1358.         r->exten.len = u - r->uri_ext;
  1359.         r->exten.data = r->uri_ext;
  1360.     }

  1361.     r->uri_ext = NULL;

  1362.     return NGX_OK;

  1363. args:

  1364.     while (p < r->uri_end) {
  1365.         if (*p++ != '#') {
  1366.             continue;
  1367.         }

  1368.         r->args.len = p - 1 - r->args_start;
  1369.         r->args.data = r->args_start;
  1370.         r->args_start = NULL;

  1371.         break;
  1372.     }

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

  1374.     if (r->uri_ext) {
  1375.         r->exten.len = u - r->uri_ext;
  1376.         r->exten.data = r->uri_ext;
  1377.     }

  1378.     r->uri_ext = NULL;

  1379.     return NGX_OK;
  1380. }


  1381. ngx_int_t
  1382. ‌ngx_http_parse_status_line(ngx_http_request_t *r, ngx_buf_t *b,
  1383.     ngx_http_status_t *status)
  1384. {
  1385.     u_char   ch;
  1386.     u_char  *p;
  1387. ‌    enum {
  1388.         sw_start = 0,
  1389.         sw_H,
  1390.         sw_HT,
  1391.         sw_HTT,
  1392.         sw_HTTP,
  1393.         sw_first_major_digit,
  1394.         sw_major_digit,
  1395.         sw_first_minor_digit,
  1396.         sw_minor_digit,
  1397.         sw_status,
  1398.         sw_space_after_status,
  1399.         sw_status_text,
  1400.         sw_almost_done
  1401.     } state;

  1402.     state = r->state;

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

  1405.         switch (state) {

  1406.         /* "HTTP/" */
  1407.         case sw_start:
  1408.             status->line_start = p;

  1409.             switch (ch) {
  1410.             case 'H':
  1411.                 state = sw_H;
  1412.                 break;
  1413.             default:
  1414.                 return NGX_ERROR;
  1415.             }
  1416.             break;

  1417.         case sw_H:
  1418.             switch (ch) {
  1419.             case 'T':
  1420.                 state = sw_HT;
  1421.                 break;
  1422.             default:
  1423.                 return NGX_ERROR;
  1424.             }
  1425.             break;

  1426.         case sw_HT:
  1427.             switch (ch) {
  1428.             case 'T':
  1429.                 state = sw_HTT;
  1430.                 break;
  1431.             default:
  1432.                 return NGX_ERROR;
  1433.             }
  1434.             break;

  1435.         case sw_HTT:
  1436.             switch (ch) {
  1437.             case 'P':
  1438.                 state = sw_HTTP;
  1439.                 break;
  1440.             default:
  1441.                 return NGX_ERROR;
  1442.             }
  1443.             break;

  1444.         case sw_HTTP:
  1445.             switch (ch) {
  1446.             case '/':
  1447.                 state = sw_first_major_digit;
  1448.                 break;
  1449.             default:
  1450.                 return NGX_ERROR;
  1451.             }
  1452.             break;

  1453.         /* the first digit of major HTTP version */
  1454.         case sw_first_major_digit:
  1455.             if (ch < '1' || ch > '9') {
  1456.                 return NGX_ERROR;
  1457.             }

  1458.             r->http_major = ch - '0';
  1459.             state = sw_major_digit;
  1460.             break;

  1461.         /* the major HTTP version or dot */
  1462.         case sw_major_digit:
  1463.             if (ch == '.') {
  1464.                 state = sw_first_minor_digit;
  1465.                 break;
  1466.             }

  1467.             if (ch < '0' || ch > '9') {
  1468.                 return NGX_ERROR;
  1469.             }

  1470.             if (r->http_major > 99) {
  1471.                 return NGX_ERROR;
  1472.             }

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

  1475.         /* the first digit of minor HTTP version */
  1476.         case sw_first_minor_digit:
  1477.             if (ch < '0' || ch > '9') {
  1478.                 return NGX_ERROR;
  1479.             }

  1480.             r->http_minor = ch - '0';
  1481.             state = sw_minor_digit;
  1482.             break;

  1483.         /* the minor HTTP version or the end of the request line */
  1484.         case sw_minor_digit:
  1485.             if (ch == ' ') {
  1486.                 state = sw_status;
  1487.                 break;
  1488.             }

  1489.             if (ch < '0' || ch > '9') {
  1490.                 return NGX_ERROR;
  1491.             }

  1492.             if (r->http_minor > 99) {
  1493.                 return NGX_ERROR;
  1494.             }

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

  1497.         /* HTTP status code */
  1498.         case sw_status:
  1499.             if (ch == ' ') {
  1500.                 break;
  1501.             }

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

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

  1506.             if (++status->count == 3) {
  1507.                 state = sw_space_after_status;
  1508.                 status->start = p - 2;
  1509.             }

  1510.             break;

  1511.         /* space or end of line */
  1512.         case sw_space_after_status:
  1513.             switch (ch) {
  1514.             case ' ':
  1515.                 state = sw_status_text;
  1516.                 break;
  1517.             case '.':                    /* IIS may send 403.1, 403.2, etc */
  1518.                 state = sw_status_text;
  1519.                 break;
  1520.             case CR:
  1521.                 state = sw_almost_done;
  1522.                 break;
  1523.             case LF:
  1524.                 goto done;
  1525.             default:
  1526.                 return NGX_ERROR;
  1527.             }
  1528.             break;

  1529.         /* any text until end of line */
  1530.         case sw_status_text:
  1531.             switch (ch) {
  1532.             case CR:
  1533.                 state = sw_almost_done;

  1534.                 break;
  1535.             case LF:
  1536.                 goto done;
  1537.             }
  1538.             break;

  1539.         /* end of status line */
  1540.         case sw_almost_done:
  1541.             status->end = p - 1;
  1542.             switch (ch) {
  1543.             case LF:
  1544.                 goto done;
  1545.             default:
  1546.                 return NGX_ERROR;
  1547.             }
  1548.         }
  1549.     }

  1550.     b->pos = p;
  1551.     r->state = state;

  1552.     return NGX_AGAIN;

  1553. done:

  1554.     b->pos = p + 1;

  1555.     if (status->end == NULL) {
  1556.         status->end = p;
  1557.     }

  1558.     status->http_version = r->http_major * 1000 + r->http_minor;
  1559.     r->state = sw_start;

  1560.     return NGX_OK;
  1561. }


  1562. ngx_int_t
  1563. ‌ngx_http_parse_unsafe_uri(ngx_http_request_t *r, ngx_str_t *uri,
  1564.     ngx_str_t *args, ngx_uint_t *flags)
  1565. {
  1566.     u_char      ch, *p, *src, *dst;
  1567.     size_t      len;
  1568.     ngx_uint_t  quoted;

  1569.     len = uri->len;
  1570.     p = uri->data;
  1571.     quoted = 0;

  1572.     if (len == 0 || p[0] == '?') {
  1573.         goto unsafe;
  1574.     }

  1575.     if (p[0] == '.' && len > 1 && p[1] == '.'
  1576.         && (len == 2 || ngx_path_separator(p[2])))
  1577.     {
  1578.         goto unsafe;
  1579.     }

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

  1581.         ch = *p++;

  1582.         if (ch == '%') {
  1583.             quoted = 1;
  1584.             continue;
  1585.         }

  1586.         if (usual[ch >> 5] & (1U << (ch & 0x1f))) {
  1587.             continue;
  1588.         }

  1589.         if (ch == '?') {
  1590.             args->len = len - 1;
  1591.             args->data = p;
  1592.             uri->len -= len;

  1593.             break;
  1594.         }

  1595.         if (ch == '\0') {
  1596.             goto unsafe;
  1597.         }

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

  1599.             /* detect "/../" and "/.." */

  1600.             if (p[0] == '.' && p[1] == '.'
  1601.                 && (len == 3 || ngx_path_separator(p[2])))
  1602.             {
  1603.                 goto unsafe;
  1604.             }
  1605.         }
  1606.     }

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

  1610.         src = uri->data;

  1611.         dst = ngx_pnalloc(r->pool, uri->len);
  1612.         if (dst == NULL) {
  1613.             return NGX_ERROR;
  1614.         }

  1615.         uri->data = dst;

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

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

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

  1620.         len = uri->len;
  1621.         p = uri->data;

  1622.         if (p[0] == '.' && len > 1 && p[1] == '.'
  1623.             && (len == 2 || ngx_path_separator(p[2])))
  1624.         {
  1625.             goto unsafe;
  1626.         }

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

  1628.             ch = *p++;

  1629.             if (ch == '\0') {
  1630.                 goto unsafe;
  1631.             }

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

  1633.                 /* detect "/../" and "/.." */

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

  1642.     return NGX_OK;

  1643. unsafe:

  1644.     if (*flags & NGX_HTTP_LOG_UNSAFE) {
  1645.         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  1646.                       "unsafe URI \"%V\" was detected", uri);
  1647.     }

  1648.     return NGX_ERROR;
  1649. }


  1650. ngx_table_elt_t *
  1651. ‌ngx_http_parse_multi_header_lines(ngx_http_request_t *r,
  1652.     ngx_table_elt_t *headers, ngx_str_t *name, ngx_str_t *value)
  1653. {
  1654.     return ngx_http_parse_multi_header_lines_internal(r, headers, name, value,
  1655.                                                       ',');
  1656. }


  1657. ngx_table_elt_t *
  1658. ‌ngx_http_parse_cookie_lines(ngx_http_request_t *r,
  1659.     ngx_table_elt_t *headers, ngx_str_t *name, ngx_str_t *value)
  1660. {
  1661.     return ngx_http_parse_multi_header_lines_internal(r, headers, name, value,
  1662.                                                       ';');
  1663. }


  1664. static ngx_table_elt_t *
  1665. ‌ngx_http_parse_multi_header_lines_internal(ngx_http_request_t *r,
  1666.     ngx_table_elt_t *headers, ngx_str_t *name, ngx_str_t *value, u_char sep)
  1667. {
  1668.     u_char           *start, *last, *end, ch;
  1669.     ngx_table_elt_t  *h;

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

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

  1673.         if (name->len > h->value.len) {
  1674.             continue;
  1675.         }

  1676.         start = h->value.data;
  1677.         end = h->value.data + h->value.len;

  1678.         while (start < end) {

  1679.             if (ngx_strncasecmp(start, name->data, name->len) != 0) {
  1680.                 goto skip;
  1681.             }

  1682.             for (start += name->len; start < end && *start == ' '; start++) {
  1683.                 /* void */
  1684.             }

  1685.             if (value == NULL) {
  1686.                 if (start == end || *start == sep) {
  1687.                     return h;
  1688.                 }

  1689.                 goto skip;
  1690.             }

  1691.             if (start == end || *start != '=') {
  1692.                 /* the invalid header value */
  1693.                 goto skip;
  1694.             }

  1695.             start++;

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

  1697.             for (last = start; last < end && *last != sep; last++) {
  1698.                 /* void */
  1699.             }

  1700.             value->len = last - start;
  1701.             value->data = start;

  1702.             return h;

  1703.         skip:

  1704.             while (start < end) {
  1705.                 ch = *start++;
  1706.                 if (ch == sep) {
  1707.                     break;
  1708.                 }
  1709.             }

  1710.             while (start < end && *start == ' ') { start++; }
  1711.         }
  1712.     }

  1713.     return NULL;
  1714. }


  1715. ngx_table_elt_t *
  1716. ‌ngx_http_parse_set_cookie_lines(ngx_http_request_t *r,
  1717.     ngx_table_elt_t *headers, ngx_str_t *name, ngx_str_t *value)
  1718. {
  1719.     u_char           *start, *last, *end;
  1720.     ngx_table_elt_t  *h;

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

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

  1724.         if (name->len >= h->value.len) {
  1725.             continue;
  1726.         }

  1727.         start = h->value.data;
  1728.         end = h->value.data + h->value.len;

  1729.         if (ngx_strncasecmp(start, name->data, name->len) != 0) {
  1730.             continue;
  1731.         }

  1732.         for (start += name->len; start < end && *start == ' '; start++) {
  1733.             /* void */
  1734.         }

  1735.         if (start == end || *start++ != '=') {
  1736.             /* the invalid header value */
  1737.             continue;
  1738.         }

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

  1740.         for (last = start; last < end && *last != ';'; last++) {
  1741.             /* void */
  1742.         }

  1743.         value->len = last - start;
  1744.         value->data = start;

  1745.         return h;
  1746.     }

  1747.     return NULL;
  1748. }


  1749. ngx_int_t
  1750. ‌ngx_http_arg(ngx_http_request_t *r, u_char *name, size_t len, ngx_str_t *value)
  1751. {
  1752.     u_char  *p, *last;

  1753.     if (r->args.len == 0) {
  1754.         return NGX_DECLINED;
  1755.     }

  1756.     p = r->args.data;
  1757.     last = p + r->args.len;

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

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

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

  1761.         if (p == NULL) {
  1762.             return NGX_DECLINED;
  1763.         }

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

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

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

  1767.             if (p == NULL) {
  1768.                 p = r->args.data + r->args.len;
  1769.             }

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

  1771.             return NGX_OK;
  1772.         }
  1773.     }

  1774.     return NGX_DECLINED;
  1775. }


  1776. void
  1777. ‌ngx_http_split_args(ngx_http_request_t *r, ngx_str_t *uri, ngx_str_t *args)
  1778. {
  1779.     u_char  *p, *last;

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

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

  1782.     if (p) {
  1783.         uri->len = p - uri->data;
  1784.         p++;
  1785.         args->len = last - p;
  1786.         args->data = p;

  1787.     } else {
  1788.         args->len = 0;
  1789.     }
  1790. }


  1791. ngx_int_t
  1792. ‌ngx_http_parse_chunked(ngx_http_request_t *r, ngx_buf_t *b,
  1793.     ngx_http_chunked_t *ctx, ngx_uint_t keep_trailers)
  1794. {
  1795.     u_char     *pos, ch, c;
  1796.     ngx_int_t   rc;
  1797. ‌    enum {
  1798.         sw_chunk_start = 0,
  1799.         sw_chunk_size,
  1800.         sw_chunk_extension,
  1801.         sw_chunk_extension_almost_done,
  1802.         sw_chunk_data,
  1803.         sw_after_data,
  1804.         sw_after_data_almost_done,
  1805.         sw_last_chunk_extension,
  1806.         sw_last_chunk_extension_almost_done,
  1807.         sw_trailer,
  1808.         sw_trailer_almost_done,
  1809.         sw_trailer_header,
  1810.         sw_trailer_header_almost_done
  1811.     } state;

  1812.     state = ctx->state;

  1813.     if (state == sw_chunk_data && ctx->size == 0) {
  1814.         state = sw_after_data;
  1815.     }

  1816.     rc = NGX_AGAIN;

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

  1818.         ch = *pos;

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

  1821.         switch (state) {

  1822.         case sw_chunk_start:
  1823.             if (ch >= '0' && ch <= '9') {
  1824.                 state = sw_chunk_size;
  1825.                 ctx->size = ch - '0';
  1826.                 break;
  1827.             }

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

  1829.             if (c >= 'a' && c <= 'f') {
  1830.                 state = sw_chunk_size;
  1831.                 ctx->size = c - 'a' + 10;
  1832.                 break;
  1833.             }

  1834.             goto invalid;

  1835.         case sw_chunk_size:
  1836.             if (ctx->size > NGX_MAX_OFF_T_VALUE / 16) {
  1837.                 goto invalid;
  1838.             }

  1839.             if (ch >= '0' && ch <= '9') {
  1840.                 ctx->size = ctx->size * 16 + (ch - '0');
  1841.                 break;
  1842.             }

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

  1844.             if (c >= 'a' && c <= 'f') {
  1845.                 ctx->size = ctx->size * 16 + (c - 'a' + 10);
  1846.                 break;
  1847.             }

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

  1849.                 switch (ch) {
  1850.                 case CR:
  1851.                     state = sw_last_chunk_extension_almost_done;
  1852.                     break;
  1853.                 case ';':
  1854.                 case ' ':
  1855.                 case '\t':
  1856.                     state = sw_last_chunk_extension;
  1857.                     break;
  1858.                 default:
  1859.                     goto invalid;
  1860.                 }

  1861.                 break;
  1862.             }

  1863.             switch (ch) {
  1864.             case CR:
  1865.                 state = sw_chunk_extension_almost_done;
  1866.                 break;
  1867.             case ';':
  1868.             case ' ':
  1869.             case '\t':
  1870.                 state = sw_chunk_extension;
  1871.                 break;
  1872.             default:
  1873.                 goto invalid;
  1874.             }

  1875.             break;

  1876.         case sw_chunk_extension:
  1877.             switch (ch) {
  1878.             case CR:
  1879.                 state = sw_chunk_extension_almost_done;
  1880.                 break;
  1881.             case LF:
  1882.                 goto invalid;
  1883.             }
  1884.             break;

  1885.         case sw_chunk_extension_almost_done:
  1886.             if (ch == LF) {
  1887.                 state = sw_chunk_data;
  1888.                 break;
  1889.             }
  1890.             goto invalid;

  1891.         case sw_chunk_data:
  1892.             rc = NGX_OK;
  1893.             goto data;

  1894.         case sw_after_data:
  1895.             switch (ch) {
  1896.             case CR:
  1897.                 state = sw_after_data_almost_done;
  1898.                 break;
  1899.             default:
  1900.                 goto invalid;
  1901.             }
  1902.             break;

  1903.         case sw_after_data_almost_done:
  1904.             if (ch == LF) {
  1905.                 state = sw_chunk_start;
  1906.                 break;
  1907.             }
  1908.             goto invalid;

  1909.         case sw_last_chunk_extension:
  1910.             switch (ch) {
  1911.             case CR:
  1912.                 state = sw_last_chunk_extension_almost_done;
  1913.                 break;
  1914.             case LF:
  1915.                 goto invalid;
  1916.             }
  1917.             break;

  1918.         case sw_last_chunk_extension_almost_done:
  1919.             if (ch == LF) {
  1920.                 if (keep_trailers) {
  1921.                     goto done;
  1922.                 }
  1923.                 state = sw_trailer;
  1924.                 break;
  1925.             }
  1926.             goto invalid;

  1927.         case sw_trailer:
  1928.             switch (ch) {
  1929.             case CR:
  1930.                 state = sw_trailer_almost_done;
  1931.                 break;
  1932.             case LF:
  1933.                 goto invalid;
  1934.             default:
  1935.                 state = sw_trailer_header;
  1936.             }
  1937.             break;

  1938.         case sw_trailer_almost_done:
  1939.             if (ch == LF) {
  1940.                 goto done;
  1941.             }
  1942.             goto invalid;

  1943.         case sw_trailer_header:
  1944.             switch (ch) {
  1945.             case CR:
  1946.                 state = sw_trailer_header_almost_done;
  1947.                 break;
  1948.             case LF:
  1949.                 goto invalid;
  1950.             }
  1951.             break;

  1952.         case sw_trailer_header_almost_done:
  1953.             if (ch == LF) {
  1954.                 state = sw_trailer;
  1955.                 break;
  1956.             }
  1957.             goto invalid;

  1958.         }
  1959.     }

  1960. data:

  1961.     ctx->state = state;
  1962.     b->pos = pos;

  1963.     if (ctx->size > NGX_MAX_OFF_T_VALUE - 9) {
  1964.         goto invalid;
  1965.     }

  1966.     switch (state) {

  1967.     case sw_chunk_start:
  1968.         ctx->length = 5 /* "0" CRLF CRLF */;
  1969.         break;
  1970.     case sw_chunk_size:
  1971.         ctx->length = 2 /* CRLF */
  1972.                       + (ctx->size ? ctx->size + 7 /* CRLF "0" CRLF CRLF */
  1973.                                    : 2 /* CRLF */);
  1974.         break;
  1975.     case sw_chunk_extension:
  1976.         ctx->length = 2 /* CRLF */ + ctx->size + 7 /* CRLF "0" CRLF CRLF */;
  1977.         break;
  1978.     case sw_chunk_extension_almost_done:
  1979.         ctx->length = 1 /* LF */ + ctx->size + 7 /* CRLF "0" CRLF CRLF */;
  1980.         break;
  1981.     case sw_chunk_data:
  1982.         ctx->length = ctx->size + 7 /* CRLF "0" CRLF CRLF */;
  1983.         break;
  1984.     case sw_after_data:
  1985.         ctx->length = 7 /* CRLF "0" CRLF CRLF */;
  1986.         break;
  1987.     case sw_after_data_almost_done:
  1988.         ctx->length = 6 /* LF "0" CRLF CRLF */;
  1989.         break;
  1990.     case sw_last_chunk_extension:
  1991.         ctx->length = 4 /* CRLF CRLF */;
  1992.         break;
  1993.     case sw_last_chunk_extension_almost_done:
  1994.         ctx->length = 3 /* LF CRLF */;
  1995.         break;
  1996.     case sw_trailer:
  1997.         ctx->length = 2 /* CRLF */;
  1998.         break;
  1999.     case sw_trailer_almost_done:
  2000.         ctx->length = 1 /* LF */;
  2001.         break;
  2002.     case sw_trailer_header:
  2003.         ctx->length = 4 /* CRLF CRLF */;
  2004.         break;
  2005.     case sw_trailer_header_almost_done:
  2006.         ctx->length = 3 /* LF CRLF */;
  2007.         break;

  2008.     }

  2009.     return rc;

  2010. done:

  2011.     ctx->state = 0;
  2012.     b->pos = pos + 1;

  2013.     return NGX_DONE;

  2014. invalid:

  2015.     return NGX_ERROR;
  2016. }