src/http/ngx_http_parse.c - nginx-1.7.10

Global variables 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 uint32_t  usual[] = {
  9.     0xffffdbfe, /* 1111 1111 1111 1111  1101 1011 1111 1110 */

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

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

  18.                 /*  ~}| {zyx wvut srqp  onml kjih gfed cba` */
  19.     0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */

  20.     0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
  21.     0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
  22.     0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
  23.     0xffffffff  /* 1111 1111 1111 1111  1111 1111 1111 1111 */
  24. };


  25. #if (NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED)

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

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

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

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

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

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

  41. #define ngx_str8cmp(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_str9cmp(m, c0, c1, c2, c3, c4, c5, c6, c7, c8)                    \
  45.     *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)             \
  46.         && ((uint32_t *) m)[1] == ((c7 << 24) | (c6 << 16) | (c5 << 8) | c4)  \
  47.         && m[8] == c8

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

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

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

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

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

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

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

  63. #define ngx_str8cmp(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 && m[7] == c7

  66. #define ngx_str9cmp(m, c0, c1, c2, c3, c4, c5, c6, c7, c8)                    \
  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 && m[8] == c8

  69. #endif


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

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

  104.     state = r->state;

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

  107.         switch (state) {

  108.         /* HTTP methods: GET, HEAD, POST */
  109.         case sw_start:
  110.             r->request_start = p;

  111.             if (ch == CR || ch == LF) {
  112.                 break;
  113.             }

  114.             if ((ch < 'A' || ch > 'Z') && ch != '_') {
  115.                 return NGX_HTTP_PARSE_INVALID_METHOD;
  116.             }

  117.             state = sw_method;
  118.             break;

  119.         case sw_method:
  120.             if (ch == ' ') {
  121.                 r->method_end = p - 1;
  122.                 m = r->request_start;

  123.                 switch (p - m) {

  124.                 case 3:
  125.                     if (ngx_str3_cmp(m, 'G', 'E', 'T', ' ')) {
  126.                         r->method = NGX_HTTP_GET;
  127.                         break;
  128.                     }

  129.                     if (ngx_str3_cmp(m, 'P', 'U', 'T', ' ')) {
  130.                         r->method = NGX_HTTP_PUT;
  131.                         break;
  132.                     }

  133.                     break;

  134.                 case 4:
  135.                     if (m[1] == 'O') {

  136.                         if (ngx_str3Ocmp(m, 'P', 'O', 'S', 'T')) {
  137.                             r->method = NGX_HTTP_POST;
  138.                             break;
  139.                         }

  140.                         if (ngx_str3Ocmp(m, 'C', 'O', 'P', 'Y')) {
  141.                             r->method = NGX_HTTP_COPY;
  142.                             break;
  143.                         }

  144.                         if (ngx_str3Ocmp(m, 'M', 'O', 'V', 'E')) {
  145.                             r->method = NGX_HTTP_MOVE;
  146.                             break;
  147.                         }

  148.                         if (ngx_str3Ocmp(m, 'L', 'O', 'C', 'K')) {
  149.                             r->method = NGX_HTTP_LOCK;
  150.                             break;
  151.                         }

  152.                     } else {

  153.                         if (ngx_str4cmp(m, 'H', 'E', 'A', 'D')) {
  154.                             r->method = NGX_HTTP_HEAD;
  155.                             break;
  156.                         }
  157.                     }

  158.                     break;

  159.                 case 5:
  160.                     if (ngx_str5cmp(m, 'M', 'K', 'C', 'O', 'L')) {
  161.                         r->method = NGX_HTTP_MKCOL;
  162.                         break;
  163.                     }

  164.                     if (ngx_str5cmp(m, 'P', 'A', 'T', 'C', 'H')) {
  165.                         r->method = NGX_HTTP_PATCH;
  166.                         break;
  167.                     }

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

  172.                     break;

  173.                 case 6:
  174.                     if (ngx_str6cmp(m, 'D', 'E', 'L', 'E', 'T', 'E')) {
  175.                         r->method = NGX_HTTP_DELETE;
  176.                         break;
  177.                     }

  178.                     if (ngx_str6cmp(m, 'U', 'N', 'L', 'O', 'C', 'K')) {
  179.                         r->method = NGX_HTTP_UNLOCK;
  180.                         break;
  181.                     }

  182.                     break;

  183.                 case 7:
  184.                     if (ngx_str7_cmp(m, 'O', 'P', 'T', 'I', 'O', 'N', 'S', ' '))
  185.                     {
  186.                         r->method = NGX_HTTP_OPTIONS;
  187.                     }

  188.                     break;

  189.                 case 8:
  190.                     if (ngx_str8cmp(m, 'P', 'R', 'O', 'P', 'F', 'I', 'N', 'D'))
  191.                     {
  192.                         r->method = NGX_HTTP_PROPFIND;
  193.                     }

  194.                     break;

  195.                 case 9:
  196.                     if (ngx_str9cmp(m,
  197.                             'P', 'R', 'O', 'P', 'P', 'A', 'T', 'C', 'H'))
  198.                     {
  199.                         r->method = NGX_HTTP_PROPPATCH;
  200.                     }

  201.                     break;
  202.                 }

  203.                 state = sw_spaces_before_uri;
  204.                 break;
  205.             }

  206.             if ((ch < 'A' || ch > 'Z') && ch != '_') {
  207.                 return NGX_HTTP_PARSE_INVALID_METHOD;
  208.             }

  209.             break;

  210.         /* space* before URI */
  211.         case sw_spaces_before_uri:

  212.             if (ch == '/') {
  213.                 r->uri_start = p;
  214.                 state = sw_after_slash_in_uri;
  215.                 break;
  216.             }

  217.             c = (u_char) (ch | 0x20);
  218.             if (c >= 'a' && c <= 'z') {
  219.                 r->schema_start = p;
  220.                 state = sw_schema;
  221.                 break;
  222.             }

  223.             switch (ch) {
  224.             case ' ':
  225.                 break;
  226.             default:
  227.                 return NGX_HTTP_PARSE_INVALID_REQUEST;
  228.             }
  229.             break;

  230.         case sw_schema:

  231.             c = (u_char) (ch | 0x20);
  232.             if (c >= 'a' && c <= 'z') {
  233.                 break;
  234.             }

  235.             switch (ch) {
  236.             case ':':
  237.                 r->schema_end = p;
  238.                 state = sw_schema_slash;
  239.                 break;
  240.             default:
  241.                 return NGX_HTTP_PARSE_INVALID_REQUEST;
  242.             }
  243.             break;

  244.         case sw_schema_slash:
  245.             switch (ch) {
  246.             case '/':
  247.                 state = sw_schema_slash_slash;
  248.                 break;
  249.             default:
  250.                 return NGX_HTTP_PARSE_INVALID_REQUEST;
  251.             }
  252.             break;

  253.         case sw_schema_slash_slash:
  254.             switch (ch) {
  255.             case '/':
  256.                 state = sw_host_start;
  257.                 break;
  258.             default:
  259.                 return NGX_HTTP_PARSE_INVALID_REQUEST;
  260.             }
  261.             break;

  262.         case sw_host_start:

  263.             r->host_start = p;

  264.             if (ch == '[') {
  265.                 state = sw_host_ip_literal;
  266.                 break;
  267.             }

  268.             state = sw_host;

  269.             /* fall through */

  270.         case sw_host:

  271.             c = (u_char) (ch | 0x20);
  272.             if (c >= 'a' && c <= 'z') {
  273.                 break;
  274.             }

  275.             if ((ch >= '0' && ch <= '9') || ch == '.' || ch == '-') {
  276.                 break;
  277.             }

  278.             /* fall through */

  279.         case sw_host_end:

  280.             r->host_end = p;

  281.             switch (ch) {
  282.             case ':':
  283.                 state = sw_port;
  284.                 break;
  285.             case '/':
  286.                 r->uri_start = p;
  287.                 state = sw_after_slash_in_uri;
  288.                 break;
  289.             case ' ':
  290.                 /*
  291.                  * use single "/" from request line to preserve pointers,
  292.                  * if request line will be copied to large client buffer
  293.                  */
  294.                 r->uri_start = r->schema_end + 1;
  295.                 r->uri_end = r->schema_end + 2;
  296.                 state = sw_host_http_09;
  297.                 break;
  298.             default:
  299.                 return NGX_HTTP_PARSE_INVALID_REQUEST;
  300.             }
  301.             break;

  302.         case sw_host_ip_literal:

  303.             if (ch >= '0' && ch <= '9') {
  304.                 break;
  305.             }

  306.             c = (u_char) (ch | 0x20);
  307.             if (c >= 'a' && c <= 'z') {
  308.                 break;
  309.             }

  310.             switch (ch) {
  311.             case ':':
  312.                 break;
  313.             case ']':
  314.                 state = sw_host_end;
  315.                 break;
  316.             case '-':
  317.             case '.':
  318.             case '_':
  319.             case '~':
  320.                 /* unreserved */
  321.                 break;
  322.             case '!':
  323.             case '$':
  324.             case '&':
  325.             case '\'':
  326.             case '(':
  327.             case ')':
  328.             case '*':
  329.             case '+':
  330.             case ',':
  331.             case ';':
  332.             case '=':
  333.                 /* sub-delims */
  334.                 break;
  335.             default:
  336.                 return NGX_HTTP_PARSE_INVALID_REQUEST;
  337.             }
  338.             break;

  339.         case sw_port:
  340.             if (ch >= '0' && ch <= '9') {
  341.                 break;
  342.             }

  343.             switch (ch) {
  344.             case '/':
  345.                 r->port_end = p;
  346.                 r->uri_start = p;
  347.                 state = sw_after_slash_in_uri;
  348.                 break;
  349.             case ' ':
  350.                 r->port_end = p;
  351.                 /*
  352.                  * use single "/" from request line to preserve pointers,
  353.                  * if request line will be copied to large client buffer
  354.                  */
  355.                 r->uri_start = r->schema_end + 1;
  356.                 r->uri_end = r->schema_end + 2;
  357.                 state = sw_host_http_09;
  358.                 break;
  359.             default:
  360.                 return NGX_HTTP_PARSE_INVALID_REQUEST;
  361.             }
  362.             break;

  363.         /* space+ after "http://host[:port] " */
  364.         case sw_host_http_09:
  365.             switch (ch) {
  366.             case ' ':
  367.                 break;
  368.             case CR:
  369.                 r->http_minor = 9;
  370.                 state = sw_almost_done;
  371.                 break;
  372.             case LF:
  373.                 r->http_minor = 9;
  374.                 goto done;
  375.             case 'H':
  376.                 r->http_protocol.data = p;
  377.                 state = sw_http_H;
  378.                 break;
  379.             default:
  380.                 return NGX_HTTP_PARSE_INVALID_REQUEST;
  381.             }
  382.             break;


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

  385.             if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
  386.                 state = sw_check_uri;
  387.                 break;
  388.             }

  389.             switch (ch) {
  390.             case ' ':
  391.                 r->uri_end = p;
  392.                 state = sw_check_uri_http_09;
  393.                 break;
  394.             case CR:
  395.                 r->uri_end = p;
  396.                 r->http_minor = 9;
  397.                 state = sw_almost_done;
  398.                 break;
  399.             case LF:
  400.                 r->uri_end = p;
  401.                 r->http_minor = 9;
  402.                 goto done;
  403.             case '.':
  404.                 r->complex_uri = 1;
  405.                 state = sw_uri;
  406.                 break;
  407.             case '%':
  408.                 r->quoted_uri = 1;
  409.                 state = sw_uri;
  410.                 break;
  411.             case '/':
  412.                 r->complex_uri = 1;
  413.                 state = sw_uri;
  414.                 break;
  415. #if (NGX_WIN32)
  416.             case '\\':
  417.                 r->complex_uri = 1;
  418.                 state = sw_uri;
  419.                 break;
  420. #endif
  421.             case '?':
  422.                 r->args_start = p + 1;
  423.                 state = sw_uri;
  424.                 break;
  425.             case '#':
  426.                 r->complex_uri = 1;
  427.                 state = sw_uri;
  428.                 break;
  429.             case '+':
  430.                 r->plus_in_uri = 1;
  431.                 break;
  432.             case '\0':
  433.                 return NGX_HTTP_PARSE_INVALID_REQUEST;
  434.             default:
  435.                 state = sw_check_uri;
  436.                 break;
  437.             }
  438.             break;

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

  441.             if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
  442.                 break;
  443.             }

  444.             switch (ch) {
  445.             case '/':
  446. #if (NGX_WIN32)
  447.                 if (r->uri_ext == p) {
  448.                     r->complex_uri = 1;
  449.                     state = sw_uri;
  450.                     break;
  451.                 }
  452. #endif
  453.                 r->uri_ext = NULL;
  454.                 state = sw_after_slash_in_uri;
  455.                 break;
  456.             case '.':
  457.                 r->uri_ext = p + 1;
  458.                 break;
  459.             case ' ':
  460.                 r->uri_end = p;
  461.                 state = sw_check_uri_http_09;
  462.                 break;
  463.             case CR:
  464.                 r->uri_end = p;
  465.                 r->http_minor = 9;
  466.                 state = sw_almost_done;
  467.                 break;
  468.             case LF:
  469.                 r->uri_end = p;
  470.                 r->http_minor = 9;
  471.                 goto done;
  472. #if (NGX_WIN32)
  473.             case '\\':
  474.                 r->complex_uri = 1;
  475.                 state = sw_after_slash_in_uri;
  476.                 break;
  477. #endif
  478.             case '%':
  479.                 r->quoted_uri = 1;
  480.                 state = sw_uri;
  481.                 break;
  482.             case '?':
  483.                 r->args_start = p + 1;
  484.                 state = sw_uri;
  485.                 break;
  486.             case '#':
  487.                 r->complex_uri = 1;
  488.                 state = sw_uri;
  489.                 break;
  490.             case '+':
  491.                 r->plus_in_uri = 1;
  492.                 break;
  493.             case '\0':
  494.                 return NGX_HTTP_PARSE_INVALID_REQUEST;
  495.             }
  496.             break;

  497.         /* space+ after URI */
  498.         case sw_check_uri_http_09:
  499.             switch (ch) {
  500.             case ' ':
  501.                 break;
  502.             case CR:
  503.                 r->http_minor = 9;
  504.                 state = sw_almost_done;
  505.                 break;
  506.             case LF:
  507.                 r->http_minor = 9;
  508.                 goto done;
  509.             case 'H':
  510.                 r->http_protocol.data = p;
  511.                 state = sw_http_H;
  512.                 break;
  513.             default:
  514.                 r->space_in_uri = 1;
  515.                 state = sw_check_uri;
  516.                 p--;
  517.                 break;
  518.             }
  519.             break;


  520.         /* URI */
  521.         case sw_uri:

  522.             if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
  523.                 break;
  524.             }

  525.             switch (ch) {
  526.             case ' ':
  527.                 r->uri_end = p;
  528.                 state = sw_http_09;
  529.                 break;
  530.             case CR:
  531.                 r->uri_end = p;
  532.                 r->http_minor = 9;
  533.                 state = sw_almost_done;
  534.                 break;
  535.             case LF:
  536.                 r->uri_end = p;
  537.                 r->http_minor = 9;
  538.                 goto done;
  539.             case '#':
  540.                 r->complex_uri = 1;
  541.                 break;
  542.             case '\0':
  543.                 return NGX_HTTP_PARSE_INVALID_REQUEST;
  544.             }
  545.             break;

  546.         /* space+ after URI */
  547.         case sw_http_09:
  548.             switch (ch) {
  549.             case ' ':
  550.                 break;
  551.             case CR:
  552.                 r->http_minor = 9;
  553.                 state = sw_almost_done;
  554.                 break;
  555.             case LF:
  556.                 r->http_minor = 9;
  557.                 goto done;
  558.             case 'H':
  559.                 r->http_protocol.data = p;
  560.                 state = sw_http_H;
  561.                 break;
  562.             default:
  563.                 r->space_in_uri = 1;
  564.                 state = sw_uri;
  565.                 p--;
  566.                 break;
  567.             }
  568.             break;

  569.         case sw_http_H:
  570.             switch (ch) {
  571.             case 'T':
  572.                 state = sw_http_HT;
  573.                 break;
  574.             default:
  575.                 return NGX_HTTP_PARSE_INVALID_REQUEST;
  576.             }
  577.             break;

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

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

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

  605.         /* first digit of major HTTP version */
  606.         case sw_first_major_digit:
  607.             if (ch < '1' || ch > '9') {
  608.                 return NGX_HTTP_PARSE_INVALID_REQUEST;
  609.             }

  610.             r->http_major = ch - '0';
  611.             state = sw_major_digit;
  612.             break;

  613.         /* major HTTP version or dot */
  614.         case sw_major_digit:
  615.             if (ch == '.') {
  616.                 state = sw_first_minor_digit;
  617.                 break;
  618.             }

  619.             if (ch < '0' || ch > '9') {
  620.                 return NGX_HTTP_PARSE_INVALID_REQUEST;
  621.             }

  622.             r->http_major = r->http_major * 10 + ch - '0';
  623.             break;

  624.         /* first digit of minor HTTP version */
  625.         case sw_first_minor_digit:
  626.             if (ch < '0' || ch > '9') {
  627.                 return NGX_HTTP_PARSE_INVALID_REQUEST;
  628.             }

  629.             r->http_minor = ch - '0';
  630.             state = sw_minor_digit;
  631.             break;

  632.         /* minor HTTP version or end of request line */
  633.         case sw_minor_digit:
  634.             if (ch == CR) {
  635.                 state = sw_almost_done;
  636.                 break;
  637.             }

  638.             if (ch == LF) {
  639.                 goto done;
  640.             }

  641.             if (ch == ' ') {
  642.                 state = sw_spaces_after_digit;
  643.                 break;
  644.             }

  645.             if (ch < '0' || ch > '9') {
  646.                 return NGX_HTTP_PARSE_INVALID_REQUEST;
  647.             }

  648.             r->http_minor = r->http_minor * 10 + ch - '0';
  649.             break;

  650.         case sw_spaces_after_digit:
  651.             switch (ch) {
  652.             case ' ':
  653.                 break;
  654.             case CR:
  655.                 state = sw_almost_done;
  656.                 break;
  657.             case LF:
  658.                 goto done;
  659.             default:
  660.                 return NGX_HTTP_PARSE_INVALID_REQUEST;
  661.             }
  662.             break;

  663.         /* end of request line */
  664.         case sw_almost_done:
  665.             r->request_end = p - 1;
  666.             switch (ch) {
  667.             case LF:
  668.                 goto done;
  669.             default:
  670.                 return NGX_HTTP_PARSE_INVALID_REQUEST;
  671.             }
  672.         }
  673.     }

  674.     b->pos = p;
  675.     r->state = state;

  676.     return NGX_AGAIN;

  677. done:

  678.     b->pos = p + 1;

  679.     if (r->request_end == NULL) {
  680.         r->request_end = p;
  681.     }

  682.     r->http_version = r->http_major * 1000 + r->http_minor;
  683.     r->state = sw_start;

  684.     if (r->http_version == 9 && r->method != NGX_HTTP_GET) {
  685.         return NGX_HTTP_PARSE_INVALID_09_METHOD;
  686.     }

  687.     return NGX_OK;
  688. }


  689. ngx_int_t
  690. ngx_http_parse_header_line(ngx_http_request_t *r, ngx_buf_t *b,
  691.     ngx_uint_t allow_underscores)
  692. {
  693.     u_char      c, ch, *p;
  694.     ngx_uint_t  hash, i;
  695.     enum {
  696.         sw_start = 0,
  697.         sw_name,
  698.         sw_space_before_value,
  699.         sw_value,
  700.         sw_space_after_value,
  701.         sw_ignore_line,
  702.         sw_almost_done,
  703.         sw_header_almost_done
  704.     } state;

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

  706.     static u_char  lowcase[] =
  707.         "\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"
  708.         "\0\0\0\0\0\0\0\0\0\0\0\0\0-\0\0" "0123456789\0\0\0\0\0\0"
  709.         "\0abcdefghijklmnopqrstuvwxyz\0\0\0\0\0"
  710.         "\0abcdefghijklmnopqrstuvwxyz\0\0\0\0\0"
  711.         "\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"
  712.         "\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"
  713.         "\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"
  714.         "\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";

  715.     state = r->state;
  716.     hash = r->header_hash;
  717.     i = r->lowcase_index;

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

  720.         switch (state) {

  721.         /* first char */
  722.         case sw_start:
  723.             r->header_name_start = p;
  724.             r->invalid_header = 0;

  725.             switch (ch) {
  726.             case CR:
  727.                 r->header_end = p;
  728.                 state = sw_header_almost_done;
  729.                 break;
  730.             case LF:
  731.                 r->header_end = p;
  732.                 goto header_done;
  733.             default:
  734.                 state = sw_name;

  735.                 c = lowcase[ch];

  736.                 if (c) {
  737.                     hash = ngx_hash(0, c);
  738.                     r->lowcase_header[0] = c;
  739.                     i = 1;
  740.                     break;
  741.                 }

  742.                 if (ch == '_') {
  743.                     if (allow_underscores) {
  744.                         hash = ngx_hash(0, ch);
  745.                         r->lowcase_header[0] = ch;
  746.                         i = 1;

  747.                     } else {
  748.                         r->invalid_header = 1;
  749.                     }

  750.                     break;
  751.                 }

  752.                 if (ch == '\0') {
  753.                     return NGX_HTTP_PARSE_INVALID_HEADER;
  754.                 }

  755.                 r->invalid_header = 1;

  756.                 break;

  757.             }
  758.             break;

  759.         /* header name */
  760.         case sw_name:
  761.             c = lowcase[ch];

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

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

  773.                 } else {
  774.                     r->invalid_header = 1;
  775.                 }

  776.                 break;
  777.             }

  778.             if (ch == ':') {
  779.                 r->header_name_end = p;
  780.                 state = sw_space_before_value;
  781.                 break;
  782.             }

  783.             if (ch == CR) {
  784.                 r->header_name_end = p;
  785.                 r->header_start = p;
  786.                 r->header_end = p;
  787.                 state = sw_almost_done;
  788.                 break;
  789.             }

  790.             if (ch == LF) {
  791.                 r->header_name_end = p;
  792.                 r->header_start = p;
  793.                 r->header_end = p;
  794.                 goto done;
  795.             }

  796.             /* IIS may send the duplicate "HTTP/1.1 ..." lines */
  797.             if (ch == '/'
  798.                 && r->upstream
  799.                 && p - r->header_name_start == 4
  800.                 && ngx_strncmp(r->header_name_start, "HTTP", 4) == 0)
  801.             {
  802.                 state = sw_ignore_line;
  803.                 break;
  804.             }

  805.             if (ch == '\0') {
  806.                 return NGX_HTTP_PARSE_INVALID_HEADER;
  807.             }

  808.             r->invalid_header = 1;

  809.             break;

  810.         /* space* before header value */
  811.         case sw_space_before_value:
  812.             switch (ch) {
  813.             case ' ':
  814.                 break;
  815.             case CR:
  816.                 r->header_start = p;
  817.                 r->header_end = p;
  818.                 state = sw_almost_done;
  819.                 break;
  820.             case LF:
  821.                 r->header_start = p;
  822.                 r->header_end = p;
  823.                 goto done;
  824.             case '\0':
  825.                 return NGX_HTTP_PARSE_INVALID_HEADER;
  826.             default:
  827.                 r->header_start = p;
  828.                 state = sw_value;
  829.                 break;
  830.             }
  831.             break;

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

  850.         /* space* before end of header line */
  851.         case sw_space_after_value:
  852.             switch (ch) {
  853.             case ' ':
  854.                 break;
  855.             case CR:
  856.                 state = sw_almost_done;
  857.                 break;
  858.             case LF:
  859.                 goto done;
  860.             case '\0':
  861.                 return NGX_HTTP_PARSE_INVALID_HEADER;
  862.             default:
  863.                 state = sw_value;
  864.                 break;
  865.             }
  866.             break;

  867.         /* ignore header line */
  868.         case sw_ignore_line:
  869.             switch (ch) {
  870.             case LF:
  871.                 state = sw_start;
  872.                 break;
  873.             default:
  874.                 break;
  875.             }
  876.             break;

  877.         /* end of header line */
  878.         case sw_almost_done:
  879.             switch (ch) {
  880.             case LF:
  881.                 goto done;
  882.             case CR:
  883.                 break;
  884.             default:
  885.                 return NGX_HTTP_PARSE_INVALID_HEADER;
  886.             }
  887.             break;

  888.         /* end of header */
  889.         case sw_header_almost_done:
  890.             switch (ch) {
  891.             case LF:
  892.                 goto header_done;
  893.             default:
  894.                 return NGX_HTTP_PARSE_INVALID_HEADER;
  895.             }
  896.         }
  897.     }

  898.     b->pos = p;
  899.     r->state = state;
  900.     r->header_hash = hash;
  901.     r->lowcase_index = i;

  902.     return NGX_AGAIN;

  903. done:

  904.     b->pos = p + 1;
  905.     r->state = sw_start;
  906.     r->header_hash = hash;
  907.     r->lowcase_index = i;

  908.     return NGX_OK;

  909. header_done:

  910.     b->pos = p + 1;
  911.     r->state = sw_start;

  912.     return NGX_HTTP_PARSE_HEADER_DONE;
  913. }


  914. ngx_int_t
  915. ngx_http_parse_uri(ngx_http_request_t *r)
  916. {
  917.     u_char  *p, ch;
  918.     enum {
  919.         sw_start = 0,
  920.         sw_after_slash_in_uri,
  921.         sw_check_uri,
  922.         sw_uri
  923.     } state;

  924.     state = sw_start;

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

  926.         ch = *p;

  927.         switch (state) {

  928.         case sw_start:

  929.             if (ch != '/') {
  930.                 return NGX_ERROR;
  931.             }

  932.             state = sw_after_slash_in_uri;
  933.             break;

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

  936.             if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
  937.                 state = sw_check_uri;
  938.                 break;
  939.             }

  940.             switch (ch) {
  941.             case ' ':
  942.                 r->space_in_uri = 1;
  943.                 state = sw_check_uri;
  944.                 break;
  945.             case '.':
  946.                 r->complex_uri = 1;
  947.                 state = sw_uri;
  948.                 break;
  949.             case '%':
  950.                 r->quoted_uri = 1;
  951.                 state = sw_uri;
  952.                 break;
  953.             case '/':
  954.                 r->complex_uri = 1;
  955.                 state = sw_uri;
  956.                 break;
  957. #if (NGX_WIN32)
  958.             case '\\':
  959.                 r->complex_uri = 1;
  960.                 state = sw_uri;
  961.                 break;
  962. #endif
  963.             case '?':
  964.                 r->args_start = p + 1;
  965.                 state = sw_uri;
  966.                 break;
  967.             case '#':
  968.                 r->complex_uri = 1;
  969.                 state = sw_uri;
  970.                 break;
  971.             case '+':
  972.                 r->plus_in_uri = 1;
  973.                 break;
  974.             default:
  975.                 state = sw_check_uri;
  976.                 break;
  977.             }
  978.             break;

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

  981.             if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
  982.                 break;
  983.             }

  984.             switch (ch) {
  985.             case '/':
  986. #if (NGX_WIN32)
  987.                 if (r->uri_ext == p) {
  988.                     r->complex_uri = 1;
  989.                     state = sw_uri;
  990.                     break;
  991.                 }
  992. #endif
  993.                 r->uri_ext = NULL;
  994.                 state = sw_after_slash_in_uri;
  995.                 break;
  996.             case '.':
  997.                 r->uri_ext = p + 1;
  998.                 break;
  999.             case ' ':
  1000.                 r->space_in_uri = 1;
  1001.                 break;
  1002. #if (NGX_WIN32)
  1003.             case '\\':
  1004.                 r->complex_uri = 1;
  1005.                 state = sw_after_slash_in_uri;
  1006.                 break;
  1007. #endif
  1008.             case '%':
  1009.                 r->quoted_uri = 1;
  1010.                 state = sw_uri;
  1011.                 break;
  1012.             case '?':
  1013.                 r->args_start = p + 1;
  1014.                 state = sw_uri;
  1015.                 break;
  1016.             case '#':
  1017.                 r->complex_uri = 1;
  1018.                 state = sw_uri;
  1019.                 break;
  1020.             case '+':
  1021.                 r->plus_in_uri = 1;
  1022.                 break;
  1023.             }
  1024.             break;

  1025.         /* URI */
  1026.         case sw_uri:

  1027.             if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
  1028.                 break;
  1029.             }

  1030.             switch (ch) {
  1031.             case ' ':
  1032.                 r->space_in_uri = 1;
  1033.                 break;
  1034.             case '#':
  1035.                 r->complex_uri = 1;
  1036.                 break;
  1037.             }
  1038.             break;
  1039.         }
  1040.     }

  1041.     return NGX_OK;
  1042. }


  1043. ngx_int_t
  1044. ngx_http_parse_complex_uri(ngx_http_request_t *r, ngx_uint_t merge_slashes)
  1045. {
  1046.     u_char  c, ch, decoded, *p, *u;
  1047.     enum {
  1048.         sw_usual = 0,
  1049.         sw_slash,
  1050.         sw_dot,
  1051.         sw_dot_dot,
  1052.         sw_quoted,
  1053.         sw_quoted_second
  1054.     } state, quoted_state;

  1055. #if (NGX_SUPPRESS_WARN)
  1056.     decoded = '\0';
  1057.     quoted_state = sw_usual;
  1058. #endif

  1059.     state = sw_usual;
  1060.     p = r->uri_start;
  1061.     u = r->uri.data;
  1062.     r->uri_ext = NULL;
  1063.     r->args_start = NULL;

  1064.     ch = *p++;

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

  1066.         /*
  1067.          * we use "ch = *p++" inside the cycle, but this operation is safe,
  1068.          * because after the URI there is always at least one character:
  1069.          * the line feed
  1070.          */

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

  1073.         switch (state) {

  1074.         case sw_usual:

  1075.             if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
  1076.                 *u++ = ch;
  1077.                 ch = *p++;
  1078.                 break;
  1079.             }

  1080.             switch (ch) {
  1081. #if (NGX_WIN32)
  1082.             case '\\':
  1083.                 if (u - 2 >= r->uri.data
  1084.                     && *(u - 1) == '.' && *(u - 2) != '.')
  1085.                 {
  1086.                     u--;
  1087.                 }

  1088.                 r->uri_ext = NULL;

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

  1090.                     /*
  1091.                      * we omit the last "\" to cause redirect because
  1092.                      * the browsers do not treat "\" as "/" in relative URL path
  1093.                      */

  1094.                     break;
  1095.                 }

  1096.                 state = sw_slash;
  1097.                 *u++ = '/';
  1098.                 break;
  1099. #endif
  1100.             case '/':
  1101. #if (NGX_WIN32)
  1102.                 if (u - 2 >= r->uri.data
  1103.                     && *(u - 1) == '.' && *(u - 2) != '.')
  1104.                 {
  1105.                     u--;
  1106.                 }
  1107. #endif
  1108.                 r->uri_ext = NULL;
  1109.                 state = sw_slash;
  1110.                 *u++ = ch;
  1111.                 break;
  1112.             case '%':
  1113.                 quoted_state = state;
  1114.                 state = sw_quoted;
  1115.                 break;
  1116.             case '?':
  1117.                 r->args_start = p;
  1118.                 goto args;
  1119.             case '#':
  1120.                 goto done;
  1121.             case '.':
  1122.                 r->uri_ext = u + 1;
  1123.                 *u++ = ch;
  1124.                 break;
  1125.             case '+':
  1126.                 r->plus_in_uri = 1;
  1127.                 /* fall through */
  1128.             default:
  1129.                 *u++ = ch;
  1130.                 break;
  1131.             }

  1132.             ch = *p++;
  1133.             break;

  1134.         case sw_slash:

  1135.             if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
  1136.                 state = sw_usual;
  1137.                 *u++ = ch;
  1138.                 ch = *p++;
  1139.                 break;
  1140.             }

  1141.             switch (ch) {
  1142. #if (NGX_WIN32)
  1143.             case '\\':
  1144.                 break;
  1145. #endif
  1146.             case '/':
  1147.                 if (!merge_slashes) {
  1148.                     *u++ = ch;
  1149.                 }
  1150.                 break;
  1151.             case '.':
  1152.                 state = sw_dot;
  1153.                 *u++ = ch;
  1154.                 break;
  1155.             case '%':
  1156.                 quoted_state = state;
  1157.                 state = sw_quoted;
  1158.                 break;
  1159.             case '?':
  1160.                 r->args_start = p;
  1161.                 goto args;
  1162.             case '#':
  1163.                 goto done;
  1164.             case '+':
  1165.                 r->plus_in_uri = 1;
  1166.             default:
  1167.                 state = sw_usual;
  1168.                 *u++ = ch;
  1169.                 break;
  1170.             }

  1171.             ch = *p++;
  1172.             break;

  1173.         case sw_dot:

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

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

  1208.             ch = *p++;
  1209.             break;

  1210.         case sw_dot_dot:

  1211.             if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
  1212.                 state = sw_usual;
  1213.                 *u++ = ch;
  1214.                 ch = *p++;
  1215.                 break;
  1216.             }

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

  1251.             ch = *p++;
  1252.             break;

  1253.         case sw_quoted:
  1254.             r->quoted_uri = 1;

  1255.             if (ch >= '0' && ch <= '9') {
  1256.                 decoded = (u_char) (ch - '0');
  1257.                 state = sw_quoted_second;
  1258.                 ch = *p++;
  1259.                 break;
  1260.             }

  1261.             c = (u_char) (ch | 0x20);
  1262.             if (c >= 'a' && c <= 'f') {
  1263.                 decoded = (u_char) (c - 'a' + 10);
  1264.                 state = sw_quoted_second;
  1265.                 ch = *p++;
  1266.                 break;
  1267.             }

  1268.             return NGX_HTTP_PARSE_INVALID_REQUEST;

  1269.         case sw_quoted_second:
  1270.             if (ch >= '0' && ch <= '9') {
  1271.                 ch = (u_char) ((decoded << 4) + ch - '0');

  1272.                 if (ch == '%' || ch == '#') {
  1273.                     state = sw_usual;
  1274.                     *u++ = ch;
  1275.                     ch = *p++;
  1276.                     break;

  1277.                 } else if (ch == '\0') {
  1278.                     return NGX_HTTP_PARSE_INVALID_REQUEST;
  1279.                 }

  1280.                 state = quoted_state;
  1281.                 break;
  1282.             }

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

  1286.                 if (ch == '?') {
  1287.                     state = sw_usual;
  1288.                     *u++ = ch;
  1289.                     ch = *p++;
  1290.                     break;

  1291.                 } else if (ch == '+') {
  1292.                     r->plus_in_uri = 1;
  1293.                 }

  1294.                 state = quoted_state;
  1295.                 break;
  1296.             }

  1297.             return NGX_HTTP_PARSE_INVALID_REQUEST;
  1298.         }
  1299.     }

  1300. done:

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

  1302.     if (r->uri_ext) {
  1303.         r->exten.len = u - r->uri_ext;
  1304.         r->exten.data = r->uri_ext;
  1305.     }

  1306.     r->uri_ext = NULL;

  1307.     return NGX_OK;

  1308. args:

  1309.     while (p < r->uri_end) {
  1310.         if (*p++ != '#') {
  1311.             continue;
  1312.         }

  1313.         r->args.len = p - 1 - r->args_start;
  1314.         r->args.data = r->args_start;
  1315.         r->args_start = NULL;

  1316.         break;
  1317.     }

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

  1319.     if (r->uri_ext) {
  1320.         r->exten.len = u - r->uri_ext;
  1321.         r->exten.data = r->uri_ext;
  1322.     }

  1323.     r->uri_ext = NULL;

  1324.     return NGX_OK;
  1325. }


  1326. ngx_int_t
  1327. ngx_http_parse_status_line(ngx_http_request_t *r, ngx_buf_t *b,
  1328.     ngx_http_status_t *status)
  1329. {
  1330.     u_char   ch;
  1331.     u_char  *p;
  1332.     enum {
  1333.         sw_start = 0,
  1334.         sw_H,
  1335.         sw_HT,
  1336.         sw_HTT,
  1337.         sw_HTTP,
  1338.         sw_first_major_digit,
  1339.         sw_major_digit,
  1340.         sw_first_minor_digit,
  1341.         sw_minor_digit,
  1342.         sw_status,
  1343.         sw_space_after_status,
  1344.         sw_status_text,
  1345.         sw_almost_done
  1346.     } state;

  1347.     state = r->state;

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

  1350.         switch (state) {

  1351.         /* "HTTP/" */
  1352.         case sw_start:
  1353.             switch (ch) {
  1354.             case 'H':
  1355.                 state = sw_H;
  1356.                 break;
  1357.             default:
  1358.                 return NGX_ERROR;
  1359.             }
  1360.             break;

  1361.         case sw_H:
  1362.             switch (ch) {
  1363.             case 'T':
  1364.                 state = sw_HT;
  1365.                 break;
  1366.             default:
  1367.                 return NGX_ERROR;
  1368.             }
  1369.             break;

  1370.         case sw_HT:
  1371.             switch (ch) {
  1372.             case 'T':
  1373.                 state = sw_HTT;
  1374.                 break;
  1375.             default:
  1376.                 return NGX_ERROR;
  1377.             }
  1378.             break;

  1379.         case sw_HTT:
  1380.             switch (ch) {
  1381.             case 'P':
  1382.                 state = sw_HTTP;
  1383.                 break;
  1384.             default:
  1385.                 return NGX_ERROR;
  1386.             }
  1387.             break;

  1388.         case sw_HTTP:
  1389.             switch (ch) {
  1390.             case '/':
  1391.                 state = sw_first_major_digit;
  1392.                 break;
  1393.             default:
  1394.                 return NGX_ERROR;
  1395.             }
  1396.             break;

  1397.         /* the first digit of major HTTP version */
  1398.         case sw_first_major_digit:
  1399.             if (ch < '1' || ch > '9') {
  1400.                 return NGX_ERROR;
  1401.             }

  1402.             r->http_major = ch - '0';
  1403.             state = sw_major_digit;
  1404.             break;

  1405.         /* the major HTTP version or dot */
  1406.         case sw_major_digit:
  1407.             if (ch == '.') {
  1408.                 state = sw_first_minor_digit;
  1409.                 break;
  1410.             }

  1411.             if (ch < '0' || ch > '9') {
  1412.                 return NGX_ERROR;
  1413.             }

  1414.             r->http_major = r->http_major * 10 + ch - '0';
  1415.             break;

  1416.         /* the first digit of minor HTTP version */
  1417.         case sw_first_minor_digit:
  1418.             if (ch < '0' || ch > '9') {
  1419.                 return NGX_ERROR;
  1420.             }

  1421.             r->http_minor = ch - '0';
  1422.             state = sw_minor_digit;
  1423.             break;

  1424.         /* the minor HTTP version or the end of the request line */
  1425.         case sw_minor_digit:
  1426.             if (ch == ' ') {
  1427.                 state = sw_status;
  1428.                 break;
  1429.             }

  1430.             if (ch < '0' || ch > '9') {
  1431.                 return NGX_ERROR;
  1432.             }

  1433.             r->http_minor = r->http_minor * 10 + ch - '0';
  1434.             break;

  1435.         /* HTTP status code */
  1436.         case sw_status:
  1437.             if (ch == ' ') {
  1438.                 break;
  1439.             }

  1440.             if (ch < '0' || ch > '9') {
  1441.                 return NGX_ERROR;
  1442.             }

  1443.             status->code = status->code * 10 + ch - '0';

  1444.             if (++status->count == 3) {
  1445.                 state = sw_space_after_status;
  1446.                 status->start = p - 2;
  1447.             }

  1448.             break;

  1449.         /* space or end of line */
  1450.         case sw_space_after_status:
  1451.             switch (ch) {
  1452.             case ' ':
  1453.                 state = sw_status_text;
  1454.                 break;
  1455.             case '.':                    /* IIS may send 403.1, 403.2, etc */
  1456.                 state = sw_status_text;
  1457.                 break;
  1458.             case CR:
  1459.                 state = sw_almost_done;
  1460.                 break;
  1461.             case LF:
  1462.                 goto done;
  1463.             default:
  1464.                 return NGX_ERROR;
  1465.             }
  1466.             break;

  1467.         /* any text until end of line */
  1468.         case sw_status_text:
  1469.             switch (ch) {
  1470.             case CR:
  1471.                 state = sw_almost_done;

  1472.                 break;
  1473.             case LF:
  1474.                 goto done;
  1475.             }
  1476.             break;

  1477.         /* end of status line */
  1478.         case sw_almost_done:
  1479.             status->end = p - 1;
  1480.             switch (ch) {
  1481.             case LF:
  1482.                 goto done;
  1483.             default:
  1484.                 return NGX_ERROR;
  1485.             }
  1486.         }
  1487.     }

  1488.     b->pos = p;
  1489.     r->state = state;

  1490.     return NGX_AGAIN;

  1491. done:

  1492.     b->pos = p + 1;

  1493.     if (status->end == NULL) {
  1494.         status->end = p;
  1495.     }

  1496.     status->http_version = r->http_major * 1000 + r->http_minor;
  1497.     r->state = sw_start;

  1498.     return NGX_OK;
  1499. }


  1500. ngx_int_t
  1501. ngx_http_parse_unsafe_uri(ngx_http_request_t *r, ngx_str_t *uri,
  1502.     ngx_str_t *args, ngx_uint_t *flags)
  1503. {
  1504.     u_char      ch, *p, *src, *dst;
  1505.     size_t      len;
  1506.     ngx_uint_t  quoted;

  1507.     len = uri->len;
  1508.     p = uri->data;
  1509.     quoted = 0;

  1510.     if (len == 0 || p[0] == '?') {
  1511.         goto unsafe;
  1512.     }

  1513.     if (p[0] == '.' && len > 1 && p[1] == '.'
  1514.         && (len == 2 || ngx_path_separator(p[2])))
  1515.     {
  1516.         goto unsafe;
  1517.     }

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

  1519.         ch = *p++;

  1520.         if (ch == '%') {
  1521.             quoted = 1;
  1522.             continue;
  1523.         }

  1524.         if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
  1525.             continue;
  1526.         }

  1527.         if (ch == '?') {
  1528.             args->len = len - 1;
  1529.             args->data = p;
  1530.             uri->len -= len;

  1531.             break;
  1532.         }

  1533.         if (ch == '\0') {
  1534.             goto unsafe;
  1535.         }

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

  1537.             /* detect "/../" and "/.." */

  1538.             if (p[0] == '.' && p[1] == '.'
  1539.                 && (len == 3 || ngx_path_separator(p[2])))
  1540.             {
  1541.                 goto unsafe;
  1542.             }
  1543.         }
  1544.     }

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

  1548.         src = uri->data;

  1549.         dst = ngx_pnalloc(r->pool, uri->len);
  1550.         if (dst == NULL) {
  1551.             return NGX_ERROR;
  1552.         }

  1553.         uri->data = dst;

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

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

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

  1558.         len = uri->len;
  1559.         p = uri->data;

  1560.         if (p[0] == '.' && len > 1 && p[1] == '.'
  1561.             && (len == 2 || ngx_path_separator(p[2])))
  1562.         {
  1563.             goto unsafe;
  1564.         }

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

  1566.             ch = *p++;

  1567.             if (ch == '\0') {
  1568.                 goto unsafe;
  1569.             }

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

  1571.                 /* detect "/../" and "/.." */

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

  1580.     return NGX_OK;

  1581. unsafe:

  1582.     if (*flags & NGX_HTTP_LOG_UNSAFE) {
  1583.         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  1584.                       "unsafe URI \"%V\" was detected", uri);
  1585.     }

  1586.     return NGX_ERROR;
  1587. }


  1588. ngx_int_t
  1589. ngx_http_parse_multi_header_lines(ngx_array_t *headers, ngx_str_t *name,
  1590.     ngx_str_t *value)
  1591. {
  1592.     ngx_uint_t         i;
  1593.     u_char            *start, *last, *end, ch;
  1594.     ngx_table_elt_t  **h;

  1595.     h = headers->elts;

  1596.     for (i = 0; i < headers->nelts; i++) {

  1597.         ngx_log_debug2(NGX_LOG_DEBUG_HTTP, headers->pool->log, 0,
  1598.                        "parse header: \"%V: %V\"", &h[i]->key, &h[i]->value);

  1599.         if (name->len > h[i]->value.len) {
  1600.             continue;
  1601.         }

  1602.         start = h[i]->value.data;
  1603.         end = h[i]->value.data + h[i]->value.len;

  1604.         while (start < end) {

  1605.             if (ngx_strncasecmp(start, name->data, name->len) != 0) {
  1606.                 goto skip;
  1607.             }

  1608.             for (start += name->len; start < end && *start == ' '; start++) {
  1609.                 /* void */
  1610.             }

  1611.             if (value == NULL) {
  1612.                 if (start == end || *start == ',') {
  1613.                     return i;
  1614.                 }

  1615.                 goto skip;
  1616.             }

  1617.             if (start == end || *start++ != '=') {
  1618.                 /* the invalid header value */
  1619.                 goto skip;
  1620.             }

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

  1622.             for (last = start; last < end && *last != ';'; last++) {
  1623.                 /* void */
  1624.             }

  1625.             value->len = last - start;
  1626.             value->data = start;

  1627.             return i;

  1628.         skip:

  1629.             while (start < end) {
  1630.                 ch = *start++;
  1631.                 if (ch == ';' || ch == ',') {
  1632.                     break;
  1633.                 }
  1634.             }

  1635.             while (start < end && *start == ' ') { start++; }
  1636.         }
  1637.     }

  1638.     return NGX_DECLINED;
  1639. }


  1640. ngx_int_t
  1641. ngx_http_parse_set_cookie_lines(ngx_array_t *headers, ngx_str_t *name,
  1642.     ngx_str_t *value)
  1643. {
  1644.     ngx_uint_t         i;
  1645.     u_char            *start, *last, *end;
  1646.     ngx_table_elt_t  **h;

  1647.     h = headers->elts;

  1648.     for (i = 0; i < headers->nelts; i++) {

  1649.         ngx_log_debug2(NGX_LOG_DEBUG_HTTP, headers->pool->log, 0,
  1650.                        "parse header: \"%V: %V\"", &h[i]->key, &h[i]->value);

  1651.         if (name->len >= h[i]->value.len) {
  1652.             continue;
  1653.         }

  1654.         start = h[i]->value.data;
  1655.         end = h[i]->value.data + h[i]->value.len;

  1656.         if (ngx_strncasecmp(start, name->data, name->len) != 0) {
  1657.             continue;
  1658.         }

  1659.         for (start += name->len; start < end && *start == ' '; start++) {
  1660.             /* void */
  1661.         }

  1662.         if (start == end || *start++ != '=') {
  1663.             /* the invalid header value */
  1664.             continue;
  1665.         }

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

  1667.         for (last = start; last < end && *last != ';'; last++) {
  1668.             /* void */
  1669.         }

  1670.         value->len = last - start;
  1671.         value->data = start;

  1672.         return i;
  1673.     }

  1674.     return NGX_DECLINED;
  1675. }


  1676. ngx_int_t
  1677. ngx_http_arg(ngx_http_request_t *r, u_char *name, size_t len, ngx_str_t *value)
  1678. {
  1679.     u_char  *p, *last;

  1680.     if (r->args.len == 0) {
  1681.         return NGX_DECLINED;
  1682.     }

  1683.     p = r->args.data;
  1684.     last = p + r->args.len;

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

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

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

  1688.         if (p == NULL) {
  1689.             return NGX_DECLINED;
  1690.         }

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

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

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

  1694.             if (p == NULL) {
  1695.                 p = r->args.data + r->args.len;
  1696.             }

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

  1698.             return NGX_OK;
  1699.         }
  1700.     }

  1701.     return NGX_DECLINED;
  1702. }


  1703. void
  1704. ngx_http_split_args(ngx_http_request_t *r, ngx_str_t *uri, ngx_str_t *args)
  1705. {
  1706.     u_char  *p, *last;

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

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

  1709.     if (p) {
  1710.         uri->len = p - uri->data;
  1711.         p++;
  1712.         args->len = last - p;
  1713.         args->data = p;

  1714.     } else {
  1715.         args->len = 0;
  1716.     }
  1717. }


  1718. ngx_int_t
  1719. ngx_http_parse_chunked(ngx_http_request_t *r, ngx_buf_t *b,
  1720.     ngx_http_chunked_t *ctx)
  1721. {
  1722.     u_char     *pos, ch, c;
  1723.     ngx_int_t   rc;
  1724.     enum {
  1725.         sw_chunk_start = 0,
  1726.         sw_chunk_size,
  1727.         sw_chunk_extension,
  1728.         sw_chunk_extension_almost_done,
  1729.         sw_chunk_data,
  1730.         sw_after_data,
  1731.         sw_after_data_almost_done,
  1732.         sw_last_chunk_extension,
  1733.         sw_last_chunk_extension_almost_done,
  1734.         sw_trailer,
  1735.         sw_trailer_almost_done,
  1736.         sw_trailer_header,
  1737.         sw_trailer_header_almost_done
  1738.     } state;

  1739.     state = ctx->state;

  1740.     if (state == sw_chunk_data && ctx->size == 0) {
  1741.         state = sw_after_data;
  1742.     }

  1743.     rc = NGX_AGAIN;

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

  1745.         ch = *pos;

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

  1748.         switch (state) {

  1749.         case sw_chunk_start:
  1750.             if (ch >= '0' && ch <= '9') {
  1751.                 state = sw_chunk_size;
  1752.                 ctx->size = ch - '0';
  1753.                 break;
  1754.             }

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

  1756.             if (c >= 'a' && c <= 'f') {
  1757.                 state = sw_chunk_size;
  1758.                 ctx->size = c - 'a' + 10;
  1759.                 break;
  1760.             }

  1761.             goto invalid;

  1762.         case sw_chunk_size:
  1763.             if (ch >= '0' && ch <= '9') {
  1764.                 ctx->size = ctx->size * 16 + (ch - '0');
  1765.                 break;
  1766.             }

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

  1768.             if (c >= 'a' && c <= 'f') {
  1769.                 ctx->size = ctx->size * 16 + (c - 'a' + 10);
  1770.                 break;
  1771.             }

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

  1773.                 switch (ch) {
  1774.                 case CR:
  1775.                     state = sw_last_chunk_extension_almost_done;
  1776.                     break;
  1777.                 case LF:
  1778.                     state = sw_trailer;
  1779.                     break;
  1780.                 case ';':
  1781.                 case ' ':
  1782.                 case '\t':
  1783.                     state = sw_last_chunk_extension;
  1784.                     break;
  1785.                 default:
  1786.                     goto invalid;
  1787.                 }

  1788.                 break;
  1789.             }

  1790.             switch (ch) {
  1791.             case CR:
  1792.                 state = sw_chunk_extension_almost_done;
  1793.                 break;
  1794.             case LF:
  1795.                 state = sw_chunk_data;
  1796.                 break;
  1797.             case ';':
  1798.             case ' ':
  1799.             case '\t':
  1800.                 state = sw_chunk_extension;
  1801.                 break;
  1802.             default:
  1803.                 goto invalid;
  1804.             }

  1805.             break;

  1806.         case sw_chunk_extension:
  1807.             switch (ch) {
  1808.             case CR:
  1809.                 state = sw_chunk_extension_almost_done;
  1810.                 break;
  1811.             case LF:
  1812.                 state = sw_chunk_data;
  1813.             }
  1814.             break;

  1815.         case sw_chunk_extension_almost_done:
  1816.             if (ch == LF) {
  1817.                 state = sw_chunk_data;
  1818.                 break;
  1819.             }
  1820.             goto invalid;

  1821.         case sw_chunk_data:
  1822.             rc = NGX_OK;
  1823.             goto data;

  1824.         case sw_after_data:
  1825.             switch (ch) {
  1826.             case CR:
  1827.                 state = sw_after_data_almost_done;
  1828.                 break;
  1829.             case LF:
  1830.                 state = sw_chunk_start;
  1831.             }
  1832.             break;

  1833.         case sw_after_data_almost_done:
  1834.             if (ch == LF) {
  1835.                 state = sw_chunk_start;
  1836.                 break;
  1837.             }
  1838.             goto invalid;

  1839.         case sw_last_chunk_extension:
  1840.             switch (ch) {
  1841.             case CR:
  1842.                 state = sw_last_chunk_extension_almost_done;
  1843.                 break;
  1844.             case LF:
  1845.                 state = sw_trailer;
  1846.             }
  1847.             break;

  1848.         case sw_last_chunk_extension_almost_done:
  1849.             if (ch == LF) {
  1850.                 state = sw_trailer;
  1851.                 break;
  1852.             }
  1853.             goto invalid;

  1854.         case sw_trailer:
  1855.             switch (ch) {
  1856.             case CR:
  1857.                 state = sw_trailer_almost_done;
  1858.                 break;
  1859.             case LF:
  1860.                 goto done;
  1861.             default:
  1862.                 state = sw_trailer_header;
  1863.             }
  1864.             break;

  1865.         case sw_trailer_almost_done:
  1866.             if (ch == LF) {
  1867.                 goto done;
  1868.             }
  1869.             goto invalid;

  1870.         case sw_trailer_header:
  1871.             switch (ch) {
  1872.             case CR:
  1873.                 state = sw_trailer_header_almost_done;
  1874.                 break;
  1875.             case LF:
  1876.                 state = sw_trailer;
  1877.             }
  1878.             break;

  1879.         case sw_trailer_header_almost_done:
  1880.             if (ch == LF) {
  1881.                 state = sw_trailer;
  1882.                 break;
  1883.             }
  1884.             goto invalid;

  1885.         }
  1886.     }

  1887. data:

  1888.     ctx->state = state;
  1889.     b->pos = pos;

  1890.     switch (state) {

  1891.     case sw_chunk_start:
  1892.         ctx->length = 3 /* "0" LF LF */;
  1893.         break;
  1894.     case sw_chunk_size:
  1895.         ctx->length = 1 /* LF */
  1896.                       + (ctx->size ? ctx->size + 4 /* LF "0" LF LF */
  1897.                                    : 1 /* LF */);
  1898.         break;
  1899.     case sw_chunk_extension:
  1900.     case sw_chunk_extension_almost_done:
  1901.         ctx->length = 1 /* LF */ + ctx->size + 4 /* LF "0" LF LF */;
  1902.         break;
  1903.     case sw_chunk_data:
  1904.         ctx->length = ctx->size + 4 /* LF "0" LF LF */;
  1905.         break;
  1906.     case sw_after_data:
  1907.     case sw_after_data_almost_done:
  1908.         ctx->length = 4 /* LF "0" LF LF */;
  1909.         break;
  1910.     case sw_last_chunk_extension:
  1911.     case sw_last_chunk_extension_almost_done:
  1912.         ctx->length = 2 /* LF LF */;
  1913.         break;
  1914.     case sw_trailer:
  1915.     case sw_trailer_almost_done:
  1916.         ctx->length = 1 /* LF */;
  1917.         break;
  1918.     case sw_trailer_header:
  1919.     case sw_trailer_header_almost_done:
  1920.         ctx->length = 2 /* LF LF */;
  1921.         break;

  1922.     }

  1923.     if (ctx->size < 0 || ctx->length < 0) {
  1924.         goto invalid;
  1925.     }

  1926.     return rc;

  1927. done:

  1928.     ctx->state = 0;
  1929.     b->pos = pos + 1;

  1930.     return NGX_DONE;

  1931. invalid:

  1932.     return NGX_ERROR;
  1933. }