Back to home page

Nginx displayed by LXR

Source navigation ]
Diff markup ]
Identifier search ]
general search ]
 
 
Version: nginx-1.13.12 ]​[ nginx-1.12.2 ]​

0001 
0002 /*
0003  * Copyright (C) Igor Sysoev
0004  * Copyright (C) Nginx, Inc.
0005  */
0006 
0007 
0008 #include <ngx_config.h>
0009 #include <ngx_core.h>
0010 #include <ngx_http.h>
0011 
0012 
0013 static uint32_t  usual[] = {
0014     0xffffdbfe, /* 1111 1111 1111 1111  1101 1011 1111 1110 */
0015 
0016                 /* ?>=< ;:98 7654 3210  /.-, +*)( '&%$ #"!  */
0017     0x7fff37d6, /* 0111 1111 1111 1111  0011 0111 1101 0110 */
0018 
0019                 /* _^]\ [ZYX WVUT SRQP  ONML KJIH GFED CBA@ */
0020 #if (NGX_WIN32)
0021     0xefffffff, /* 1110 1111 1111 1111  1111 1111 1111 1111 */
0022 #else
0023     0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
0024 #endif
0025 
0026                 /*  ~}| {zyx wvut srqp  onml kjih gfed cba` */
0027     0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
0028 
0029     0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
0030     0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
0031     0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
0032     0xffffffff  /* 1111 1111 1111 1111  1111 1111 1111 1111 */
0033 };
0034 
0035 
0036 #if (NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED)
0037 
0038 #define ngx_str3_cmp(m, c0, c1, c2, c3)                                       \
0039     *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)
0040 
0041 #define ngx_str3Ocmp(m, c0, c1, c2, c3)                                       \
0042     *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)
0043 
0044 #define ngx_str4cmp(m, c0, c1, c2, c3)                                        \
0045     *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)
0046 
0047 #define ngx_str5cmp(m, c0, c1, c2, c3, c4)                                    \
0048     *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)             \
0049         && m[4] == c4
0050 
0051 #define ngx_str6cmp(m, c0, c1, c2, c3, c4, c5)                                \
0052     *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)             \
0053         && (((uint32_t *) m)[1] & 0xffff) == ((c5 << 8) | c4)
0054 
0055 #define ngx_str7_cmp(m, c0, c1, c2, c3, c4, c5, c6, c7)                       \
0056     *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)             \
0057         && ((uint32_t *) m)[1] == ((c7 << 24) | (c6 << 16) | (c5 << 8) | c4)
0058 
0059 #define ngx_str8cmp(m, c0, c1, c2, c3, c4, c5, c6, c7)                        \
0060     *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)             \
0061         && ((uint32_t *) m)[1] == ((c7 << 24) | (c6 << 16) | (c5 << 8) | c4)
0062 
0063 #define ngx_str9cmp(m, c0, c1, c2, c3, c4, c5, c6, c7, c8)                    \
0064     *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)             \
0065         && ((uint32_t *) m)[1] == ((c7 << 24) | (c6 << 16) | (c5 << 8) | c4)  \
0066         && m[8] == c8
0067 
0068 #else /* !(NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED) */
0069 
0070 #define ngx_str3_cmp(m, c0, c1, c2, c3)                                       \
0071     m[0] == c0 && m[1] == c1 && m[2] == c2
0072 
0073 #define ngx_str3Ocmp(m, c0, c1, c2, c3)                                       \
0074     m[0] == c0 && m[2] == c2 && m[3] == c3
0075 
0076 #define ngx_str4cmp(m, c0, c1, c2, c3)                                        \
0077     m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3
0078 
0079 #define ngx_str5cmp(m, c0, c1, c2, c3, c4)                                    \
0080     m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3 && m[4] == c4
0081 
0082 #define ngx_str6cmp(m, c0, c1, c2, c3, c4, c5)                                \
0083     m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3                      \
0084         && m[4] == c4 && m[5] == c5
0085 
0086 #define ngx_str7_cmp(m, c0, c1, c2, c3, c4, c5, c6, c7)                       \
0087     m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3                      \
0088         && m[4] == c4 && m[5] == c5 && m[6] == c6
0089 
0090 #define ngx_str8cmp(m, c0, c1, c2, c3, c4, c5, c6, c7)                        \
0091     m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3                      \
0092         && m[4] == c4 && m[5] == c5 && m[6] == c6 && m[7] == c7
0093 
0094 #define ngx_str9cmp(m, c0, c1, c2, c3, c4, c5, c6, c7, c8)                    \
0095     m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3                      \
0096         && m[4] == c4 && m[5] == c5 && m[6] == c6 && m[7] == c7 && m[8] == c8
0097 
0098 #endif
0099 
0100 
0101 /* gcc, icc, msvc and others compile these switches as an jump table */
0102 
0103 ngx_int_t
0104 ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b)
0105 {
0106     u_char  c, ch, *p, *m;
0107     enum {
0108         sw_start = 0,
0109         sw_method,
0110         sw_spaces_before_uri,
0111         sw_schema,
0112         sw_schema_slash,
0113         sw_schema_slash_slash,
0114         sw_host_start,
0115         sw_host,
0116         sw_host_end,
0117         sw_host_ip_literal,
0118         sw_port,
0119         sw_host_http_09,
0120         sw_after_slash_in_uri,
0121         sw_check_uri,
0122         sw_check_uri_http_09,
0123         sw_uri,
0124         sw_http_09,
0125         sw_http_H,
0126         sw_http_HT,
0127         sw_http_HTT,
0128         sw_http_HTTP,
0129         sw_first_major_digit,
0130         sw_major_digit,
0131         sw_first_minor_digit,
0132         sw_minor_digit,
0133         sw_spaces_after_digit,
0134         sw_almost_done
0135     } state;
0136 
0137     state = r->state;
0138 
0139     for (p = b->pos; p < b->last; p++) {
0140         ch = *p;
0141 
0142         switch (state) {
0143 
0144         /* HTTP methods: GET, HEAD, POST */
0145         case sw_start:
0146             r->request_start = p;
0147 
0148             if (ch == CR || ch == LF) {
0149                 break;
0150             }
0151 
0152             if ((ch < 'A' || ch > 'Z') && ch != '_' && ch != '-') {
0153                 return NGX_HTTP_PARSE_INVALID_METHOD;
0154             }
0155 
0156             state = sw_method;
0157             break;
0158 
0159         case sw_method:
0160             if (ch == ' ') {
0161                 r->method_end = p - 1;
0162                 m = r->request_start;
0163 
0164                 switch (p - m) {
0165 
0166                 case 3:
0167                     if (ngx_str3_cmp(m, 'G', 'E', 'T', ' ')) {
0168                         r->method = NGX_HTTP_GET;
0169                         break;
0170                     }
0171 
0172                     if (ngx_str3_cmp(m, 'P', 'U', 'T', ' ')) {
0173                         r->method = NGX_HTTP_PUT;
0174                         break;
0175                     }
0176 
0177                     break;
0178 
0179                 case 4:
0180                     if (m[1] == 'O') {
0181 
0182                         if (ngx_str3Ocmp(m, 'P', 'O', 'S', 'T')) {
0183                             r->method = NGX_HTTP_POST;
0184                             break;
0185                         }
0186 
0187                         if (ngx_str3Ocmp(m, 'C', 'O', 'P', 'Y')) {
0188                             r->method = NGX_HTTP_COPY;
0189                             break;
0190                         }
0191 
0192                         if (ngx_str3Ocmp(m, 'M', 'O', 'V', 'E')) {
0193                             r->method = NGX_HTTP_MOVE;
0194                             break;
0195                         }
0196 
0197                         if (ngx_str3Ocmp(m, 'L', 'O', 'C', 'K')) {
0198                             r->method = NGX_HTTP_LOCK;
0199                             break;
0200                         }
0201 
0202                     } else {
0203 
0204                         if (ngx_str4cmp(m, 'H', 'E', 'A', 'D')) {
0205                             r->method = NGX_HTTP_HEAD;
0206                             break;
0207                         }
0208                     }
0209 
0210                     break;
0211 
0212                 case 5:
0213                     if (ngx_str5cmp(m, 'M', 'K', 'C', 'O', 'L')) {
0214                         r->method = NGX_HTTP_MKCOL;
0215                         break;
0216                     }
0217 
0218                     if (ngx_str5cmp(m, 'P', 'A', 'T', 'C', 'H')) {
0219                         r->method = NGX_HTTP_PATCH;
0220                         break;
0221                     }
0222 
0223                     if (ngx_str5cmp(m, 'T', 'R', 'A', 'C', 'E')) {
0224                         r->method = NGX_HTTP_TRACE;
0225                         break;
0226                     }
0227 
0228                     break;
0229 
0230                 case 6:
0231                     if (ngx_str6cmp(m, 'D', 'E', 'L', 'E', 'T', 'E')) {
0232                         r->method = NGX_HTTP_DELETE;
0233                         break;
0234                     }
0235 
0236                     if (ngx_str6cmp(m, 'U', 'N', 'L', 'O', 'C', 'K')) {
0237                         r->method = NGX_HTTP_UNLOCK;
0238                         break;
0239                     }
0240 
0241                     break;
0242 
0243                 case 7:
0244                     if (ngx_str7_cmp(m, 'O', 'P', 'T', 'I', 'O', 'N', 'S', ' '))
0245                     {
0246                         r->method = NGX_HTTP_OPTIONS;
0247                     }
0248 
0249                     break;
0250 
0251                 case 8:
0252                     if (ngx_str8cmp(m, 'P', 'R', 'O', 'P', 'F', 'I', 'N', 'D'))
0253                     {
0254                         r->method = NGX_HTTP_PROPFIND;
0255                     }
0256 
0257                     break;
0258 
0259                 case 9:
0260                     if (ngx_str9cmp(m,
0261                             'P', 'R', 'O', 'P', 'P', 'A', 'T', 'C', 'H'))
0262                     {
0263                         r->method = NGX_HTTP_PROPPATCH;
0264                     }
0265 
0266                     break;
0267                 }
0268 
0269                 state = sw_spaces_before_uri;
0270                 break;
0271             }
0272 
0273             if ((ch < 'A' || ch > 'Z') && ch != '_' && ch != '-') {
0274                 return NGX_HTTP_PARSE_INVALID_METHOD;
0275             }
0276 
0277             break;
0278 
0279         /* space* before URI */
0280         case sw_spaces_before_uri:
0281 
0282             if (ch == '/') {
0283                 r->uri_start = p;
0284                 state = sw_after_slash_in_uri;
0285                 break;
0286             }
0287 
0288             c = (u_char) (ch | 0x20);
0289             if (c >= 'a' && c <= 'z') {
0290                 r->schema_start = p;
0291                 state = sw_schema;
0292                 break;
0293             }
0294 
0295             switch (ch) {
0296             case ' ':
0297                 break;
0298             default:
0299                 return NGX_HTTP_PARSE_INVALID_REQUEST;
0300             }
0301             break;
0302 
0303         case sw_schema:
0304 
0305             c = (u_char) (ch | 0x20);
0306             if (c >= 'a' && c <= 'z') {
0307                 break;
0308             }
0309 
0310             switch (ch) {
0311             case ':':
0312                 r->schema_end = p;
0313                 state = sw_schema_slash;
0314                 break;
0315             default:
0316                 return NGX_HTTP_PARSE_INVALID_REQUEST;
0317             }
0318             break;
0319 
0320         case sw_schema_slash:
0321             switch (ch) {
0322             case '/':
0323                 state = sw_schema_slash_slash;
0324                 break;
0325             default:
0326                 return NGX_HTTP_PARSE_INVALID_REQUEST;
0327             }
0328             break;
0329 
0330         case sw_schema_slash_slash:
0331             switch (ch) {
0332             case '/':
0333                 state = sw_host_start;
0334                 break;
0335             default:
0336                 return NGX_HTTP_PARSE_INVALID_REQUEST;
0337             }
0338             break;
0339 
0340         case sw_host_start:
0341 
0342             r->host_start = p;
0343 
0344             if (ch == '[') {
0345                 state = sw_host_ip_literal;
0346                 break;
0347             }
0348 
0349             state = sw_host;
0350 
0351             /* fall through */
0352 
0353         case sw_host:
0354 
0355             c = (u_char) (ch | 0x20);
0356             if (c >= 'a' && c <= 'z') {
0357                 break;
0358             }
0359 
0360             if ((ch >= '0' && ch <= '9') || ch == '.' || ch == '-') {
0361                 break;
0362             }
0363 
0364             /* fall through */
0365 
0366         case sw_host_end:
0367 
0368             r->host_end = p;
0369 
0370             switch (ch) {
0371             case ':':
0372                 state = sw_port;
0373                 break;
0374             case '/':
0375                 r->uri_start = p;
0376                 state = sw_after_slash_in_uri;
0377                 break;
0378             case ' ':
0379                 /*
0380                  * use single "/" from request line to preserve pointers,
0381                  * if request line will be copied to large client buffer
0382                  */
0383                 r->uri_start = r->schema_end + 1;
0384                 r->uri_end = r->schema_end + 2;
0385                 state = sw_host_http_09;
0386                 break;
0387             default:
0388                 return NGX_HTTP_PARSE_INVALID_REQUEST;
0389             }
0390             break;
0391 
0392         case sw_host_ip_literal:
0393 
0394             if (ch >= '0' && ch <= '9') {
0395                 break;
0396             }
0397 
0398             c = (u_char) (ch | 0x20);
0399             if (c >= 'a' && c <= 'z') {
0400                 break;
0401             }
0402 
0403             switch (ch) {
0404             case ':':
0405                 break;
0406             case ']':
0407                 state = sw_host_end;
0408                 break;
0409             case '-':
0410             case '.':
0411             case '_':
0412             case '~':
0413                 /* unreserved */
0414                 break;
0415             case '!':
0416             case '$':
0417             case '&':
0418             case '\'':
0419             case '(':
0420             case ')':
0421             case '*':
0422             case '+':
0423             case ',':
0424             case ';':
0425             case '=':
0426                 /* sub-delims */
0427                 break;
0428             default:
0429                 return NGX_HTTP_PARSE_INVALID_REQUEST;
0430             }
0431             break;
0432 
0433         case sw_port:
0434             if (ch >= '0' && ch <= '9') {
0435                 break;
0436             }
0437 
0438             switch (ch) {
0439             case '/':
0440                 r->port_end = p;
0441                 r->uri_start = p;
0442                 state = sw_after_slash_in_uri;
0443                 break;
0444             case ' ':
0445                 r->port_end = p;
0446                 /*
0447                  * use single "/" from request line to preserve pointers,
0448                  * if request line will be copied to large client buffer
0449                  */
0450                 r->uri_start = r->schema_end + 1;
0451                 r->uri_end = r->schema_end + 2;
0452                 state = sw_host_http_09;
0453                 break;
0454             default:
0455                 return NGX_HTTP_PARSE_INVALID_REQUEST;
0456             }
0457             break;
0458 
0459         /* space+ after "http://host[:port] " */
0460         case sw_host_http_09:
0461             switch (ch) {
0462             case ' ':
0463                 break;
0464             case CR:
0465                 r->http_minor = 9;
0466                 state = sw_almost_done;
0467                 break;
0468             case LF:
0469                 r->http_minor = 9;
0470                 goto done;
0471             case 'H':
0472                 r->http_protocol.data = p;
0473                 state = sw_http_H;
0474                 break;
0475             default:
0476                 return NGX_HTTP_PARSE_INVALID_REQUEST;
0477             }
0478             break;
0479 
0480 
0481         /* check "/.", "//", "%", and "\" (Win32) in URI */
0482         case sw_after_slash_in_uri:
0483 
0484             if (usual[ch >> 5] & (1U << (ch & 0x1f))) {
0485                 state = sw_check_uri;
0486                 break;
0487             }
0488 
0489             switch (ch) {
0490             case ' ':
0491                 r->uri_end = p;
0492                 state = sw_check_uri_http_09;
0493                 break;
0494             case CR:
0495                 r->uri_end = p;
0496                 r->http_minor = 9;
0497                 state = sw_almost_done;
0498                 break;
0499             case LF:
0500                 r->uri_end = p;
0501                 r->http_minor = 9;
0502                 goto done;
0503             case '.':
0504                 r->complex_uri = 1;
0505                 state = sw_uri;
0506                 break;
0507             case '%':
0508                 r->quoted_uri = 1;
0509                 state = sw_uri;
0510                 break;
0511             case '/':
0512                 r->complex_uri = 1;
0513                 state = sw_uri;
0514                 break;
0515 #if (NGX_WIN32)
0516             case '\\':
0517                 r->complex_uri = 1;
0518                 state = sw_uri;
0519                 break;
0520 #endif
0521             case '?':
0522                 r->args_start = p + 1;
0523                 state = sw_uri;
0524                 break;
0525             case '#':
0526                 r->complex_uri = 1;
0527                 state = sw_uri;
0528                 break;
0529             case '+':
0530                 r->plus_in_uri = 1;
0531                 break;
0532             case '\0':
0533                 return NGX_HTTP_PARSE_INVALID_REQUEST;
0534             default:
0535                 state = sw_check_uri;
0536                 break;
0537             }
0538             break;
0539 
0540         /* check "/", "%" and "\" (Win32) in URI */
0541         case sw_check_uri:
0542 
0543             if (usual[ch >> 5] & (1U << (ch & 0x1f))) {
0544                 break;
0545             }
0546 
0547             switch (ch) {
0548             case '/':
0549 #if (NGX_WIN32)
0550                 if (r->uri_ext == p) {
0551                     r->complex_uri = 1;
0552                     state = sw_uri;
0553                     break;
0554                 }
0555 #endif
0556                 r->uri_ext = NULL;
0557                 state = sw_after_slash_in_uri;
0558                 break;
0559             case '.':
0560                 r->uri_ext = p + 1;
0561                 break;
0562             case ' ':
0563                 r->uri_end = p;
0564                 state = sw_check_uri_http_09;
0565                 break;
0566             case CR:
0567                 r->uri_end = p;
0568                 r->http_minor = 9;
0569                 state = sw_almost_done;
0570                 break;
0571             case LF:
0572                 r->uri_end = p;
0573                 r->http_minor = 9;
0574                 goto done;
0575 #if (NGX_WIN32)
0576             case '\\':
0577                 r->complex_uri = 1;
0578                 state = sw_after_slash_in_uri;
0579                 break;
0580 #endif
0581             case '%':
0582                 r->quoted_uri = 1;
0583                 state = sw_uri;
0584                 break;
0585             case '?':
0586                 r->args_start = p + 1;
0587                 state = sw_uri;
0588                 break;
0589             case '#':
0590                 r->complex_uri = 1;
0591                 state = sw_uri;
0592                 break;
0593             case '+':
0594                 r->plus_in_uri = 1;
0595                 break;
0596             case '\0':
0597                 return NGX_HTTP_PARSE_INVALID_REQUEST;
0598             }
0599             break;
0600 
0601         /* space+ after URI */
0602         case sw_check_uri_http_09:
0603             switch (ch) {
0604             case ' ':
0605                 break;
0606             case CR:
0607                 r->http_minor = 9;
0608                 state = sw_almost_done;
0609                 break;
0610             case LF:
0611                 r->http_minor = 9;
0612                 goto done;
0613             case 'H':
0614                 r->http_protocol.data = p;
0615                 state = sw_http_H;
0616                 break;
0617             default:
0618                 r->space_in_uri = 1;
0619                 state = sw_check_uri;
0620                 p--;
0621                 break;
0622             }
0623             break;
0624 
0625 
0626         /* URI */
0627         case sw_uri:
0628 
0629             if (usual[ch >> 5] & (1U << (ch & 0x1f))) {
0630                 break;
0631             }
0632 
0633             switch (ch) {
0634             case ' ':
0635                 r->uri_end = p;
0636                 state = sw_http_09;
0637                 break;
0638             case CR:
0639                 r->uri_end = p;
0640                 r->http_minor = 9;
0641                 state = sw_almost_done;
0642                 break;
0643             case LF:
0644                 r->uri_end = p;
0645                 r->http_minor = 9;
0646                 goto done;
0647             case '#':
0648                 r->complex_uri = 1;
0649                 break;
0650             case '\0':
0651                 return NGX_HTTP_PARSE_INVALID_REQUEST;
0652             }
0653             break;
0654 
0655         /* space+ after URI */
0656         case sw_http_09:
0657             switch (ch) {
0658             case ' ':
0659                 break;
0660             case CR:
0661                 r->http_minor = 9;
0662                 state = sw_almost_done;
0663                 break;
0664             case LF:
0665                 r->http_minor = 9;
0666                 goto done;
0667             case 'H':
0668                 r->http_protocol.data = p;
0669                 state = sw_http_H;
0670                 break;
0671             default:
0672                 r->space_in_uri = 1;
0673                 state = sw_uri;
0674                 p--;
0675                 break;
0676             }
0677             break;
0678 
0679         case sw_http_H:
0680             switch (ch) {
0681             case 'T':
0682                 state = sw_http_HT;
0683                 break;
0684             default:
0685                 return NGX_HTTP_PARSE_INVALID_REQUEST;
0686             }
0687             break;
0688 
0689         case sw_http_HT:
0690             switch (ch) {
0691             case 'T':
0692                 state = sw_http_HTT;
0693                 break;
0694             default:
0695                 return NGX_HTTP_PARSE_INVALID_REQUEST;
0696             }
0697             break;
0698 
0699         case sw_http_HTT:
0700             switch (ch) {
0701             case 'P':
0702                 state = sw_http_HTTP;
0703                 break;
0704             default:
0705                 return NGX_HTTP_PARSE_INVALID_REQUEST;
0706             }
0707             break;
0708 
0709         case sw_http_HTTP:
0710             switch (ch) {
0711             case '/':
0712                 state = sw_first_major_digit;
0713                 break;
0714             default:
0715                 return NGX_HTTP_PARSE_INVALID_REQUEST;
0716             }
0717             break;
0718 
0719         /* first digit of major HTTP version */
0720         case sw_first_major_digit:
0721             if (ch < '1' || ch > '9') {
0722                 return NGX_HTTP_PARSE_INVALID_REQUEST;
0723             }
0724 
0725             r->http_major = ch - '0';
0726             state = sw_major_digit;
0727             break;
0728 
0729         /* major HTTP version or dot */
0730         case sw_major_digit:
0731             if (ch == '.') {
0732                 state = sw_first_minor_digit;
0733                 break;
0734             }
0735 
0736             if (ch < '0' || ch > '9') {
0737                 return NGX_HTTP_PARSE_INVALID_REQUEST;
0738             }
0739 
0740             if (r->http_major > 99) {
0741                 return NGX_HTTP_PARSE_INVALID_REQUEST;
0742             }
0743 
0744             r->http_major = r->http_major * 10 + ch - '0';
0745             break;
0746 
0747         /* first digit of minor HTTP version */
0748         case sw_first_minor_digit:
0749             if (ch < '0' || ch > '9') {
0750                 return NGX_HTTP_PARSE_INVALID_REQUEST;
0751             }
0752 
0753             r->http_minor = ch - '0';
0754             state = sw_minor_digit;
0755             break;
0756 
0757         /* minor HTTP version or end of request line */
0758         case sw_minor_digit:
0759             if (ch == CR) {
0760                 state = sw_almost_done;
0761                 break;
0762             }
0763 
0764             if (ch == LF) {
0765                 goto done;
0766             }
0767 
0768             if (ch == ' ') {
0769                 state = sw_spaces_after_digit;
0770                 break;
0771             }
0772 
0773             if (ch < '0' || ch > '9') {
0774                 return NGX_HTTP_PARSE_INVALID_REQUEST;
0775             }
0776 
0777             if (r->http_minor > 99) {
0778                 return NGX_HTTP_PARSE_INVALID_REQUEST;
0779             }
0780 
0781             r->http_minor = r->http_minor * 10 + ch - '0';
0782             break;
0783 
0784         case sw_spaces_after_digit:
0785             switch (ch) {
0786             case ' ':
0787                 break;
0788             case CR:
0789                 state = sw_almost_done;
0790                 break;
0791             case LF:
0792                 goto done;
0793             default:
0794                 return NGX_HTTP_PARSE_INVALID_REQUEST;
0795             }
0796             break;
0797 
0798         /* end of request line */
0799         case sw_almost_done:
0800             r->request_end = p - 1;
0801             switch (ch) {
0802             case LF:
0803                 goto done;
0804             default:
0805                 return NGX_HTTP_PARSE_INVALID_REQUEST;
0806             }
0807         }
0808     }
0809 
0810     b->pos = p;
0811     r->state = state;
0812 
0813     return NGX_AGAIN;
0814 
0815 done:
0816 
0817     b->pos = p + 1;
0818 
0819     if (r->request_end == NULL) {
0820         r->request_end = p;
0821     }
0822 
0823     r->http_version = r->http_major * 1000 + r->http_minor;
0824     r->state = sw_start;
0825 
0826     if (r->http_version == 9 && r->method != NGX_HTTP_GET) {
0827         return NGX_HTTP_PARSE_INVALID_09_METHOD;
0828     }
0829 
0830     return NGX_OK;
0831 }
0832 
0833 
0834 ngx_int_t
0835 ngx_http_parse_header_line(ngx_http_request_t *r, ngx_buf_t *b,
0836     ngx_uint_t allow_underscores)
0837 {
0838     u_char      c, ch, *p;
0839     ngx_uint_t  hash, i;
0840     enum {
0841         sw_start = 0,
0842         sw_name,
0843         sw_space_before_value,
0844         sw_value,
0845         sw_space_after_value,
0846         sw_ignore_line,
0847         sw_almost_done,
0848         sw_header_almost_done
0849     } state;
0850 
0851     /* the last '\0' is not needed because string is zero terminated */
0852 
0853     static u_char  lowcase[] =
0854         "\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"
0855         "\0\0\0\0\0\0\0\0\0\0\0\0\0-\0\0" "0123456789\0\0\0\0\0\0"
0856         "\0abcdefghijklmnopqrstuvwxyz\0\0\0\0\0"
0857         "\0abcdefghijklmnopqrstuvwxyz\0\0\0\0\0"
0858         "\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"
0859         "\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"
0860         "\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"
0861         "\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";
0862 
0863     state = r->state;
0864     hash = r->header_hash;
0865     i = r->lowcase_index;
0866 
0867     for (p = b->pos; p < b->last; p++) {
0868         ch = *p;
0869 
0870         switch (state) {
0871 
0872         /* first char */
0873         case sw_start:
0874             r->header_name_start = p;
0875             r->invalid_header = 0;
0876 
0877             switch (ch) {
0878             case CR:
0879                 r->header_end = p;
0880                 state = sw_header_almost_done;
0881                 break;
0882             case LF:
0883                 r->header_end = p;
0884                 goto header_done;
0885             default:
0886                 state = sw_name;
0887 
0888                 c = lowcase[ch];
0889 
0890                 if (c) {
0891                     hash = ngx_hash(0, c);
0892                     r->lowcase_header[0] = c;
0893                     i = 1;
0894                     break;
0895                 }
0896 
0897                 if (ch == '_') {
0898                     if (allow_underscores) {
0899                         hash = ngx_hash(0, ch);
0900                         r->lowcase_header[0] = ch;
0901                         i = 1;
0902 
0903                     } else {
0904                         r->invalid_header = 1;
0905                     }
0906 
0907                     break;
0908                 }
0909 
0910                 if (ch == '\0') {
0911                     return NGX_HTTP_PARSE_INVALID_HEADER;
0912                 }
0913 
0914                 r->invalid_header = 1;
0915 
0916                 break;
0917 
0918             }
0919             break;
0920 
0921         /* header name */
0922         case sw_name:
0923             c = lowcase[ch];
0924 
0925             if (c) {
0926                 hash = ngx_hash(hash, c);
0927                 r->lowcase_header[i++] = c;
0928                 i &= (NGX_HTTP_LC_HEADER_LEN - 1);
0929                 break;
0930             }
0931 
0932             if (ch == '_') {
0933                 if (allow_underscores) {
0934                     hash = ngx_hash(hash, ch);
0935                     r->lowcase_header[i++] = ch;
0936                     i &= (NGX_HTTP_LC_HEADER_LEN - 1);
0937 
0938                 } else {
0939                     r->invalid_header = 1;
0940                 }
0941 
0942                 break;
0943             }
0944 
0945             if (ch == ':') {
0946                 r->header_name_end = p;
0947                 state = sw_space_before_value;
0948                 break;
0949             }
0950 
0951             if (ch == CR) {
0952                 r->header_name_end = p;
0953                 r->header_start = p;
0954                 r->header_end = p;
0955                 state = sw_almost_done;
0956                 break;
0957             }
0958 
0959             if (ch == LF) {
0960                 r->header_name_end = p;
0961                 r->header_start = p;
0962                 r->header_end = p;
0963                 goto done;
0964             }
0965 
0966             /* IIS may send the duplicate "HTTP/1.1 ..." lines */
0967             if (ch == '/'
0968                 && r->upstream
0969                 && p - r->header_name_start == 4
0970                 && ngx_strncmp(r->header_name_start, "HTTP", 4) == 0)
0971             {
0972                 state = sw_ignore_line;
0973                 break;
0974             }
0975 
0976             if (ch == '\0') {
0977                 return NGX_HTTP_PARSE_INVALID_HEADER;
0978             }
0979 
0980             r->invalid_header = 1;
0981 
0982             break;
0983 
0984         /* space* before header value */
0985         case sw_space_before_value:
0986             switch (ch) {
0987             case ' ':
0988                 break;
0989             case CR:
0990                 r->header_start = p;
0991                 r->header_end = p;
0992                 state = sw_almost_done;
0993                 break;
0994             case LF:
0995                 r->header_start = p;
0996                 r->header_end = p;
0997                 goto done;
0998             case '\0':
0999                 return NGX_HTTP_PARSE_INVALID_HEADER;
1000             default:
1001                 r->header_start = p;
1002                 state = sw_value;
1003                 break;
1004             }
1005             break;
1006 
1007         /* header value */
1008         case sw_value:
1009             switch (ch) {
1010             case ' ':
1011                 r->header_end = p;
1012                 state = sw_space_after_value;
1013                 break;
1014             case CR:
1015                 r->header_end = p;
1016                 state = sw_almost_done;
1017                 break;
1018             case LF:
1019                 r->header_end = p;
1020                 goto done;
1021             case '\0':
1022                 return NGX_HTTP_PARSE_INVALID_HEADER;
1023             }
1024             break;
1025 
1026         /* space* before end of header line */
1027         case sw_space_after_value:
1028             switch (ch) {
1029             case ' ':
1030                 break;
1031             case CR:
1032                 state = sw_almost_done;
1033                 break;
1034             case LF:
1035                 goto done;
1036             case '\0':
1037                 return NGX_HTTP_PARSE_INVALID_HEADER;
1038             default:
1039                 state = sw_value;
1040                 break;
1041             }
1042             break;
1043 
1044         /* ignore header line */
1045         case sw_ignore_line:
1046             switch (ch) {
1047             case LF:
1048                 state = sw_start;
1049                 break;
1050             default:
1051                 break;
1052             }
1053             break;
1054 
1055         /* end of header line */
1056         case sw_almost_done:
1057             switch (ch) {
1058             case LF:
1059                 goto done;
1060             case CR:
1061                 break;
1062             default:
1063                 return NGX_HTTP_PARSE_INVALID_HEADER;
1064             }
1065             break;
1066 
1067         /* end of header */
1068         case sw_header_almost_done:
1069             switch (ch) {
1070             case LF:
1071                 goto header_done;
1072             default:
1073                 return NGX_HTTP_PARSE_INVALID_HEADER;
1074             }
1075         }
1076     }
1077 
1078     b->pos = p;
1079     r->state = state;
1080     r->header_hash = hash;
1081     r->lowcase_index = i;
1082 
1083     return NGX_AGAIN;
1084 
1085 done:
1086 
1087     b->pos = p + 1;
1088     r->state = sw_start;
1089     r->header_hash = hash;
1090     r->lowcase_index = i;
1091 
1092     return NGX_OK;
1093 
1094 header_done:
1095 
1096     b->pos = p + 1;
1097     r->state = sw_start;
1098 
1099     return NGX_HTTP_PARSE_HEADER_DONE;
1100 }
1101 
1102 
1103 ngx_int_t
1104 ngx_http_parse_uri(ngx_http_request_t *r)
1105 {
1106     u_char  *p, ch;
1107     enum {
1108         sw_start = 0,
1109         sw_after_slash_in_uri,
1110         sw_check_uri,
1111         sw_uri
1112     } state;
1113 
1114     state = sw_start;
1115 
1116     for (p = r->uri_start; p != r->uri_end; p++) {
1117 
1118         ch = *p;
1119 
1120         switch (state) {
1121 
1122         case sw_start:
1123 
1124             if (ch != '/') {
1125                 return NGX_ERROR;
1126             }
1127 
1128             state = sw_after_slash_in_uri;
1129             break;
1130 
1131         /* check "/.", "//", "%", and "\" (Win32) in URI */
1132         case sw_after_slash_in_uri:
1133 
1134             if (usual[ch >> 5] & (1U << (ch & 0x1f))) {
1135                 state = sw_check_uri;
1136                 break;
1137             }
1138 
1139             switch (ch) {
1140             case ' ':
1141                 r->space_in_uri = 1;
1142                 state = sw_check_uri;
1143                 break;
1144             case '.':
1145                 r->complex_uri = 1;
1146                 state = sw_uri;
1147                 break;
1148             case '%':
1149                 r->quoted_uri = 1;
1150                 state = sw_uri;
1151                 break;
1152             case '/':
1153                 r->complex_uri = 1;
1154                 state = sw_uri;
1155                 break;
1156 #if (NGX_WIN32)
1157             case '\\':
1158                 r->complex_uri = 1;
1159                 state = sw_uri;
1160                 break;
1161 #endif
1162             case '?':
1163                 r->args_start = p + 1;
1164                 state = sw_uri;
1165                 break;
1166             case '#':
1167                 r->complex_uri = 1;
1168                 state = sw_uri;
1169                 break;
1170             case '+':
1171                 r->plus_in_uri = 1;
1172                 break;
1173             default:
1174                 state = sw_check_uri;
1175                 break;
1176             }
1177             break;
1178 
1179         /* check "/", "%" and "\" (Win32) in URI */
1180         case sw_check_uri:
1181 
1182             if (usual[ch >> 5] & (1U << (ch & 0x1f))) {
1183                 break;
1184             }
1185 
1186             switch (ch) {
1187             case '/':
1188 #if (NGX_WIN32)
1189                 if (r->uri_ext == p) {
1190                     r->complex_uri = 1;
1191                     state = sw_uri;
1192                     break;
1193                 }
1194 #endif
1195                 r->uri_ext = NULL;
1196                 state = sw_after_slash_in_uri;
1197                 break;
1198             case '.':
1199                 r->uri_ext = p + 1;
1200                 break;
1201             case ' ':
1202                 r->space_in_uri = 1;
1203                 break;
1204 #if (NGX_WIN32)
1205             case '\\':
1206                 r->complex_uri = 1;
1207                 state = sw_after_slash_in_uri;
1208                 break;
1209 #endif
1210             case '%':
1211                 r->quoted_uri = 1;
1212                 state = sw_uri;
1213                 break;
1214             case '?':
1215                 r->args_start = p + 1;
1216                 state = sw_uri;
1217                 break;
1218             case '#':
1219                 r->complex_uri = 1;
1220                 state = sw_uri;
1221                 break;
1222             case '+':
1223                 r->plus_in_uri = 1;
1224                 break;
1225             }
1226             break;
1227 
1228         /* URI */
1229         case sw_uri:
1230 
1231             if (usual[ch >> 5] & (1U << (ch & 0x1f))) {
1232                 break;
1233             }
1234 
1235             switch (ch) {
1236             case ' ':
1237                 r->space_in_uri = 1;
1238                 break;
1239             case '#':
1240                 r->complex_uri = 1;
1241                 break;
1242             }
1243             break;
1244         }
1245     }
1246 
1247     return NGX_OK;
1248 }
1249 
1250 
1251 ngx_int_t
1252 ngx_http_parse_complex_uri(ngx_http_request_t *r, ngx_uint_t merge_slashes)
1253 {
1254     u_char  c, ch, decoded, *p, *u;
1255     enum {
1256         sw_usual = 0,
1257         sw_slash,
1258         sw_dot,
1259         sw_dot_dot,
1260         sw_quoted,
1261         sw_quoted_second
1262     } state, quoted_state;
1263 
1264 #if (NGX_SUPPRESS_WARN)
1265     decoded = '\0';
1266     quoted_state = sw_usual;
1267 #endif
1268 
1269     state = sw_usual;
1270     p = r->uri_start;
1271     u = r->uri.data;
1272     r->uri_ext = NULL;
1273     r->args_start = NULL;
1274 
1275     ch = *p++;
1276 
1277     while (p <= r->uri_end) {
1278 
1279         /*
1280          * we use "ch = *p++" inside the cycle, but this operation is safe,
1281          * because after the URI there is always at least one character:
1282          * the line feed
1283          */
1284 
1285         ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1286                        "s:%d in:'%Xd:%c'", state, ch, ch);
1287 
1288         switch (state) {
1289 
1290         case sw_usual:
1291 
1292             if (usual[ch >> 5] & (1U << (ch & 0x1f))) {
1293                 *u++ = ch;
1294                 ch = *p++;
1295                 break;
1296             }
1297 
1298             switch (ch) {
1299 #if (NGX_WIN32)
1300             case '\\':
1301                 if (u - 2 >= r->uri.data
1302                     && *(u - 1) == '.' && *(u - 2) != '.')
1303                 {
1304                     u--;
1305                 }
1306 
1307                 r->uri_ext = NULL;
1308 
1309                 if (p == r->uri_start + r->uri.len) {
1310 
1311                     /*
1312                      * we omit the last "\" to cause redirect because
1313                      * the browsers do not treat "\" as "/" in relative URL path
1314                      */
1315 
1316                     break;
1317                 }
1318 
1319                 state = sw_slash;
1320                 *u++ = '/';
1321                 break;
1322 #endif
1323             case '/':
1324 #if (NGX_WIN32)
1325                 if (u - 2 >= r->uri.data
1326                     && *(u - 1) == '.' && *(u - 2) != '.')
1327                 {
1328                     u--;
1329                 }
1330 #endif
1331                 r->uri_ext = NULL;
1332                 state = sw_slash;
1333                 *u++ = ch;
1334                 break;
1335             case '%':
1336                 quoted_state = state;
1337                 state = sw_quoted;
1338                 break;
1339             case '?':
1340                 r->args_start = p;
1341                 goto args;
1342             case '#':
1343                 goto done;
1344             case '.':
1345                 r->uri_ext = u + 1;
1346                 *u++ = ch;
1347                 break;
1348             case '+':
1349                 r->plus_in_uri = 1;
1350                 /* fall through */
1351             default:
1352                 *u++ = ch;
1353                 break;
1354             }
1355 
1356             ch = *p++;
1357             break;
1358 
1359         case sw_slash:
1360 
1361             if (usual[ch >> 5] & (1U << (ch & 0x1f))) {
1362                 state = sw_usual;
1363                 *u++ = ch;
1364                 ch = *p++;
1365                 break;
1366             }
1367 
1368             switch (ch) {
1369 #if (NGX_WIN32)
1370             case '\\':
1371                 break;
1372 #endif
1373             case '/':
1374                 if (!merge_slashes) {
1375                     *u++ = ch;
1376                 }
1377                 break;
1378             case '.':
1379                 state = sw_dot;
1380                 *u++ = ch;
1381                 break;
1382             case '%':
1383                 quoted_state = state;
1384                 state = sw_quoted;
1385                 break;
1386             case '?':
1387                 r->args_start = p;
1388                 goto args;
1389             case '#':
1390                 goto done;
1391             case '+':
1392                 r->plus_in_uri = 1;
1393                 /* fall through */
1394             default:
1395                 state = sw_usual;
1396                 *u++ = ch;
1397                 break;
1398             }
1399 
1400             ch = *p++;
1401             break;
1402 
1403         case sw_dot:
1404 
1405             if (usual[ch >> 5] & (1U << (ch & 0x1f))) {
1406                 state = sw_usual;
1407                 *u++ = ch;
1408                 ch = *p++;
1409                 break;
1410             }
1411 
1412             switch (ch) {
1413 #if (NGX_WIN32)
1414             case '\\':
1415 #endif
1416             case '/':
1417                 state = sw_slash;
1418                 u--;
1419                 break;
1420             case '.':
1421                 state = sw_dot_dot;
1422                 *u++ = ch;
1423                 break;
1424             case '%':
1425                 quoted_state = state;
1426                 state = sw_quoted;
1427                 break;
1428             case '?':
1429                 r->args_start = p;
1430                 goto args;
1431             case '#':
1432                 goto done;
1433             case '+':
1434                 r->plus_in_uri = 1;
1435                 /* fall through */
1436             default:
1437                 state = sw_usual;
1438                 *u++ = ch;
1439                 break;
1440             }
1441 
1442             ch = *p++;
1443             break;
1444 
1445         case sw_dot_dot:
1446 
1447             if (usual[ch >> 5] & (1U << (ch & 0x1f))) {
1448                 state = sw_usual;
1449                 *u++ = ch;
1450                 ch = *p++;
1451                 break;
1452             }
1453 
1454             switch (ch) {
1455 #if (NGX_WIN32)
1456             case '\\':
1457 #endif
1458             case '/':
1459                 state = sw_slash;
1460                 u -= 5;
1461                 for ( ;; ) {
1462                     if (u < r->uri.data) {
1463                         return NGX_HTTP_PARSE_INVALID_REQUEST;
1464                     }
1465                     if (*u == '/') {
1466                         u++;
1467                         break;
1468                     }
1469                     u--;
1470                 }
1471                 break;
1472             case '%':
1473                 quoted_state = state;
1474                 state = sw_quoted;
1475                 break;
1476             case '?':
1477                 r->args_start = p;
1478                 goto args;
1479             case '#':
1480                 goto done;
1481             case '+':
1482                 r->plus_in_uri = 1;
1483                 /* fall through */
1484             default:
1485                 state = sw_usual;
1486                 *u++ = ch;
1487                 break;
1488             }
1489 
1490             ch = *p++;
1491             break;
1492 
1493         case sw_quoted:
1494             r->quoted_uri = 1;
1495 
1496             if (ch >= '0' && ch <= '9') {
1497                 decoded = (u_char) (ch - '0');
1498                 state = sw_quoted_second;
1499                 ch = *p++;
1500                 break;
1501             }
1502 
1503             c = (u_char) (ch | 0x20);
1504             if (c >= 'a' && c <= 'f') {
1505                 decoded = (u_char) (c - 'a' + 10);
1506                 state = sw_quoted_second;
1507                 ch = *p++;
1508                 break;
1509             }
1510 
1511             return NGX_HTTP_PARSE_INVALID_REQUEST;
1512 
1513         case sw_quoted_second:
1514             if (ch >= '0' && ch <= '9') {
1515                 ch = (u_char) ((decoded << 4) + ch - '0');
1516 
1517                 if (ch == '%' || ch == '#') {
1518                     state = sw_usual;
1519                     *u++ = ch;
1520                     ch = *p++;
1521                     break;
1522 
1523                 } else if (ch == '\0') {
1524                     return NGX_HTTP_PARSE_INVALID_REQUEST;
1525                 }
1526 
1527                 state = quoted_state;
1528                 break;
1529             }
1530 
1531             c = (u_char) (ch | 0x20);
1532             if (c >= 'a' && c <= 'f') {
1533                 ch = (u_char) ((decoded << 4) + c - 'a' + 10);
1534 
1535                 if (ch == '?') {
1536                     state = sw_usual;
1537                     *u++ = ch;
1538                     ch = *p++;
1539                     break;
1540 
1541                 } else if (ch == '+') {
1542                     r->plus_in_uri = 1;
1543                 }
1544 
1545                 state = quoted_state;
1546                 break;
1547             }
1548 
1549             return NGX_HTTP_PARSE_INVALID_REQUEST;
1550         }
1551     }
1552 
1553 done:
1554 
1555     r->uri.len = u - r->uri.data;
1556 
1557     if (r->uri_ext) {
1558         r->exten.len = u - r->uri_ext;
1559         r->exten.data = r->uri_ext;
1560     }
1561 
1562     r->uri_ext = NULL;
1563 
1564     return NGX_OK;
1565 
1566 args:
1567 
1568     while (p < r->uri_end) {
1569         if (*p++ != '#') {
1570             continue;
1571         }
1572 
1573         r->args.len = p - 1 - r->args_start;
1574         r->args.data = r->args_start;
1575         r->args_start = NULL;
1576 
1577         break;
1578     }
1579 
1580     r->uri.len = u - r->uri.data;
1581 
1582     if (r->uri_ext) {
1583         r->exten.len = u - r->uri_ext;
1584         r->exten.data = r->uri_ext;
1585     }
1586 
1587     r->uri_ext = NULL;
1588 
1589     return NGX_OK;
1590 }
1591 
1592 
1593 ngx_int_t
1594 ngx_http_parse_status_line(ngx_http_request_t *r, ngx_buf_t *b,
1595     ngx_http_status_t *status)
1596 {
1597     u_char   ch;
1598     u_char  *p;
1599     enum {
1600         sw_start = 0,
1601         sw_H,
1602         sw_HT,
1603         sw_HTT,
1604         sw_HTTP,
1605         sw_first_major_digit,
1606         sw_major_digit,
1607         sw_first_minor_digit,
1608         sw_minor_digit,
1609         sw_status,
1610         sw_space_after_status,
1611         sw_status_text,
1612         sw_almost_done
1613     } state;
1614 
1615     state = r->state;
1616 
1617     for (p = b->pos; p < b->last; p++) {
1618         ch = *p;
1619 
1620         switch (state) {
1621 
1622         /* "HTTP/" */
1623         case sw_start:
1624             switch (ch) {
1625             case 'H':
1626                 state = sw_H;
1627                 break;
1628             default:
1629                 return NGX_ERROR;
1630             }
1631             break;
1632 
1633         case sw_H:
1634             switch (ch) {
1635             case 'T':
1636                 state = sw_HT;
1637                 break;
1638             default:
1639                 return NGX_ERROR;
1640             }
1641             break;
1642 
1643         case sw_HT:
1644             switch (ch) {
1645             case 'T':
1646                 state = sw_HTT;
1647                 break;
1648             default:
1649                 return NGX_ERROR;
1650             }
1651             break;
1652 
1653         case sw_HTT:
1654             switch (ch) {
1655             case 'P':
1656                 state = sw_HTTP;
1657                 break;
1658             default:
1659                 return NGX_ERROR;
1660             }
1661             break;
1662 
1663         case sw_HTTP:
1664             switch (ch) {
1665             case '/':
1666                 state = sw_first_major_digit;
1667                 break;
1668             default:
1669                 return NGX_ERROR;
1670             }
1671             break;
1672 
1673         /* the first digit of major HTTP version */
1674         case sw_first_major_digit:
1675             if (ch < '1' || ch > '9') {
1676                 return NGX_ERROR;
1677             }
1678 
1679             r->http_major = ch - '0';
1680             state = sw_major_digit;
1681             break;
1682 
1683         /* the major HTTP version or dot */
1684         case sw_major_digit:
1685             if (ch == '.') {
1686                 state = sw_first_minor_digit;
1687                 break;
1688             }
1689 
1690             if (ch < '0' || ch > '9') {
1691                 return NGX_ERROR;
1692             }
1693 
1694             if (r->http_major > 99) {
1695                 return NGX_ERROR;
1696             }
1697 
1698             r->http_major = r->http_major * 10 + ch - '0';
1699             break;
1700 
1701         /* the first digit of minor HTTP version */
1702         case sw_first_minor_digit:
1703             if (ch < '0' || ch > '9') {
1704                 return NGX_ERROR;
1705             }
1706 
1707             r->http_minor = ch - '0';
1708             state = sw_minor_digit;
1709             break;
1710 
1711         /* the minor HTTP version or the end of the request line */
1712         case sw_minor_digit:
1713             if (ch == ' ') {
1714                 state = sw_status;
1715                 break;
1716             }
1717 
1718             if (ch < '0' || ch > '9') {
1719                 return NGX_ERROR;
1720             }
1721 
1722             if (r->http_minor > 99) {
1723                 return NGX_ERROR;
1724             }
1725 
1726             r->http_minor = r->http_minor * 10 + ch - '0';
1727             break;
1728 
1729         /* HTTP status code */
1730         case sw_status:
1731             if (ch == ' ') {
1732                 break;
1733             }
1734 
1735             if (ch < '0' || ch > '9') {
1736                 return NGX_ERROR;
1737             }
1738 
1739             status->code = status->code * 10 + ch - '0';
1740 
1741             if (++status->count == 3) {
1742                 state = sw_space_after_status;
1743                 status->start = p - 2;
1744             }
1745 
1746             break;
1747 
1748         /* space or end of line */
1749         case sw_space_after_status:
1750             switch (ch) {
1751             case ' ':
1752                 state = sw_status_text;
1753                 break;
1754             case '.':                    /* IIS may send 403.1, 403.2, etc */
1755                 state = sw_status_text;
1756                 break;
1757             case CR:
1758                 state = sw_almost_done;
1759                 break;
1760             case LF:
1761                 goto done;
1762             default:
1763                 return NGX_ERROR;
1764             }
1765             break;
1766 
1767         /* any text until end of line */
1768         case sw_status_text:
1769             switch (ch) {
1770             case CR:
1771                 state = sw_almost_done;
1772 
1773                 break;
1774             case LF:
1775                 goto done;
1776             }
1777             break;
1778 
1779         /* end of status line */
1780         case sw_almost_done:
1781             status->end = p - 1;
1782             switch (ch) {
1783             case LF:
1784                 goto done;
1785             default:
1786                 return NGX_ERROR;
1787             }
1788         }
1789     }
1790 
1791     b->pos = p;
1792     r->state = state;
1793 
1794     return NGX_AGAIN;
1795 
1796 done:
1797 
1798     b->pos = p + 1;
1799 
1800     if (status->end == NULL) {
1801         status->end = p;
1802     }
1803 
1804     status->http_version = r->http_major * 1000 + r->http_minor;
1805     r->state = sw_start;
1806 
1807     return NGX_OK;
1808 }
1809 
1810 
1811 ngx_int_t
1812 ngx_http_parse_unsafe_uri(ngx_http_request_t *r, ngx_str_t *uri,
1813     ngx_str_t *args, ngx_uint_t *flags)
1814 {
1815     u_char      ch, *p, *src, *dst;
1816     size_t      len;
1817     ngx_uint_t  quoted;
1818 
1819     len = uri->len;
1820     p = uri->data;
1821     quoted = 0;
1822 
1823     if (len == 0 || p[0] == '?') {
1824         goto unsafe;
1825     }
1826 
1827     if (p[0] == '.' && len > 1 && p[1] == '.'
1828         && (len == 2 || ngx_path_separator(p[2])))
1829     {
1830         goto unsafe;
1831     }
1832 
1833     for ( /* void */ ; len; len--) {
1834 
1835         ch = *p++;
1836 
1837         if (ch == '%') {
1838             quoted = 1;
1839             continue;
1840         }
1841 
1842         if (usual[ch >> 5] & (1U << (ch & 0x1f))) {
1843             continue;
1844         }
1845 
1846         if (ch == '?') {
1847             args->len = len - 1;
1848             args->data = p;
1849             uri->len -= len;
1850 
1851             break;
1852         }
1853 
1854         if (ch == '\0') {
1855             goto unsafe;
1856         }
1857 
1858         if (ngx_path_separator(ch) && len > 2) {
1859 
1860             /* detect "/../" and "/.." */
1861 
1862             if (p[0] == '.' && p[1] == '.'
1863                 && (len == 3 || ngx_path_separator(p[2])))
1864             {
1865                 goto unsafe;
1866             }
1867         }
1868     }
1869 
1870     if (quoted) {
1871         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1872                        "escaped URI: \"%V\"", uri);
1873 
1874         src = uri->data;
1875 
1876         dst = ngx_pnalloc(r->pool, uri->len);
1877         if (dst == NULL) {
1878             return NGX_ERROR;
1879         }
1880 
1881         uri->data = dst;
1882 
1883         ngx_unescape_uri(&dst, &src, uri->len, 0);
1884 
1885         uri->len = dst - uri->data;
1886 
1887         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1888                        "unescaped URI: \"%V\"", uri);
1889 
1890         len = uri->len;
1891         p = uri->data;
1892 
1893         if (p[0] == '.' && len > 1 && p[1] == '.'
1894             && (len == 2 || ngx_path_separator(p[2])))
1895         {
1896             goto unsafe;
1897         }
1898 
1899         for ( /* void */ ; len; len--) {
1900 
1901             ch = *p++;
1902 
1903             if (ch == '\0') {
1904                 goto unsafe;
1905             }
1906 
1907             if (ngx_path_separator(ch) && len > 2) {
1908 
1909                 /* detect "/../" and "/.." */
1910 
1911                 if (p[0] == '.' && p[1] == '.'
1912                     && (len == 3 || ngx_path_separator(p[2])))
1913                 {
1914                     goto unsafe;
1915                 }
1916             }
1917         }
1918     }
1919 
1920     return NGX_OK;
1921 
1922 unsafe:
1923 
1924     if (*flags & NGX_HTTP_LOG_UNSAFE) {
1925         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1926                       "unsafe URI \"%V\" was detected", uri);
1927     }
1928 
1929     return NGX_ERROR;
1930 }
1931 
1932 
1933 ngx_int_t
1934 ngx_http_parse_multi_header_lines(ngx_array_t *headers, ngx_str_t *name,
1935     ngx_str_t *value)
1936 {
1937     ngx_uint_t         i;
1938     u_char            *start, *last, *end, ch;
1939     ngx_table_elt_t  **h;
1940 
1941     h = headers->elts;
1942 
1943     for (i = 0; i < headers->nelts; i++) {
1944 
1945         ngx_log_debug2(NGX_LOG_DEBUG_HTTP, headers->pool->log, 0,
1946                        "parse header: \"%V: %V\"", &h[i]->key, &h[i]->value);
1947 
1948         if (name->len > h[i]->value.len) {
1949             continue;
1950         }
1951 
1952         start = h[i]->value.data;
1953         end = h[i]->value.data + h[i]->value.len;
1954 
1955         while (start < end) {
1956 
1957             if (ngx_strncasecmp(start, name->data, name->len) != 0) {
1958                 goto skip;
1959             }
1960 
1961             for (start += name->len; start < end && *start == ' '; start++) {
1962                 /* void */
1963             }
1964 
1965             if (value == NULL) {
1966                 if (start == end || *start == ',') {
1967                     return i;
1968                 }
1969 
1970                 goto skip;
1971             }
1972 
1973             if (start == end || *start++ != '=') {
1974                 /* the invalid header value */
1975                 goto skip;
1976             }
1977 
1978             while (start < end && *start == ' ') { start++; }
1979 
1980             for (last = start; last < end && *last != ';'; last++) {
1981                 /* void */
1982             }
1983 
1984             value->len = last - start;
1985             value->data = start;
1986 
1987             return i;
1988 
1989         skip:
1990 
1991             while (start < end) {
1992                 ch = *start++;
1993                 if (ch == ';' || ch == ',') {
1994                     break;
1995                 }
1996             }
1997 
1998             while (start < end && *start == ' ') { start++; }
1999         }
2000     }
2001 
2002     return NGX_DECLINED;
2003 }
2004 
2005 
2006 ngx_int_t
2007 ngx_http_parse_set_cookie_lines(ngx_array_t *headers, ngx_str_t *name,
2008     ngx_str_t *value)
2009 {
2010     ngx_uint_t         i;
2011     u_char            *start, *last, *end;
2012     ngx_table_elt_t  **h;
2013 
2014     h = headers->elts;
2015 
2016     for (i = 0; i < headers->nelts; i++) {
2017 
2018         ngx_log_debug2(NGX_LOG_DEBUG_HTTP, headers->pool->log, 0,
2019                        "parse header: \"%V: %V\"", &h[i]->key, &h[i]->value);
2020 
2021         if (name->len >= h[i]->value.len) {
2022             continue;
2023         }
2024 
2025         start = h[i]->value.data;
2026         end = h[i]->value.data + h[i]->value.len;
2027 
2028         if (ngx_strncasecmp(start, name->data, name->len) != 0) {
2029             continue;
2030         }
2031 
2032         for (start += name->len; start < end && *start == ' '; start++) {
2033             /* void */
2034         }
2035 
2036         if (start == end || *start++ != '=') {
2037             /* the invalid header value */
2038             continue;
2039         }
2040 
2041         while (start < end && *start == ' ') { start++; }
2042 
2043         for (last = start; last < end && *last != ';'; last++) {
2044             /* void */
2045         }
2046 
2047         value->len = last - start;
2048         value->data = start;
2049 
2050         return i;
2051     }
2052 
2053     return NGX_DECLINED;
2054 }
2055 
2056 
2057 ngx_int_t
2058 ngx_http_arg(ngx_http_request_t *r, u_char *name, size_t len, ngx_str_t *value)
2059 {
2060     u_char  *p, *last;
2061 
2062     if (r->args.len == 0) {
2063         return NGX_DECLINED;
2064     }
2065 
2066     p = r->args.data;
2067     last = p + r->args.len;
2068 
2069     for ( /* void */ ; p < last; p++) {
2070 
2071         /* we need '=' after name, so drop one char from last */
2072 
2073         p = ngx_strlcasestrn(p, last - 1, name, len - 1);
2074 
2075         if (p == NULL) {
2076             return NGX_DECLINED;
2077         }
2078 
2079         if ((p == r->args.data || *(p - 1) == '&') && *(p + len) == '=') {
2080 
2081             value->data = p + len + 1;
2082 
2083             p = ngx_strlchr(p, last, '&');
2084 
2085             if (p == NULL) {
2086                 p = r->args.data + r->args.len;
2087             }
2088 
2089             value->len = p - value->data;
2090 
2091             return NGX_OK;
2092         }
2093     }
2094 
2095     return NGX_DECLINED;
2096 }
2097 
2098 
2099 void
2100 ngx_http_split_args(ngx_http_request_t *r, ngx_str_t *uri, ngx_str_t *args)
2101 {
2102     u_char  *p, *last;
2103 
2104     last = uri->data + uri->len;
2105 
2106     p = ngx_strlchr(uri->data, last, '?');
2107 
2108     if (p) {
2109         uri->len = p - uri->data;
2110         p++;
2111         args->len = last - p;
2112         args->data = p;
2113 
2114     } else {
2115         args->len = 0;
2116     }
2117 }
2118 
2119 
2120 ngx_int_t
2121 ngx_http_parse_chunked(ngx_http_request_t *r, ngx_buf_t *b,
2122     ngx_http_chunked_t *ctx)
2123 {
2124     u_char     *pos, ch, c;
2125     ngx_int_t   rc;
2126     enum {
2127         sw_chunk_start = 0,
2128         sw_chunk_size,
2129         sw_chunk_extension,
2130         sw_chunk_extension_almost_done,
2131         sw_chunk_data,
2132         sw_after_data,
2133         sw_after_data_almost_done,
2134         sw_last_chunk_extension,
2135         sw_last_chunk_extension_almost_done,
2136         sw_trailer,
2137         sw_trailer_almost_done,
2138         sw_trailer_header,
2139         sw_trailer_header_almost_done
2140     } state;
2141 
2142     state = ctx->state;
2143 
2144     if (state == sw_chunk_data && ctx->size == 0) {
2145         state = sw_after_data;
2146     }
2147 
2148     rc = NGX_AGAIN;
2149 
2150     for (pos = b->pos; pos < b->last; pos++) {
2151 
2152         ch = *pos;
2153 
2154         ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
2155                        "http chunked byte: %02Xd s:%d", ch, state);
2156 
2157         switch (state) {
2158 
2159         case sw_chunk_start:
2160             if (ch >= '0' && ch <= '9') {
2161                 state = sw_chunk_size;
2162                 ctx->size = ch - '0';
2163                 break;
2164             }
2165 
2166             c = (u_char) (ch | 0x20);
2167 
2168             if (c >= 'a' && c <= 'f') {
2169                 state = sw_chunk_size;
2170                 ctx->size = c - 'a' + 10;
2171                 break;
2172             }
2173 
2174             goto invalid;
2175 
2176         case sw_chunk_size:
2177             if (ctx->size > NGX_MAX_OFF_T_VALUE / 16) {
2178                 goto invalid;
2179             }
2180 
2181             if (ch >= '0' && ch <= '9') {
2182                 ctx->size = ctx->size * 16 + (ch - '0');
2183                 break;
2184             }
2185 
2186             c = (u_char) (ch | 0x20);
2187 
2188             if (c >= 'a' && c <= 'f') {
2189                 ctx->size = ctx->size * 16 + (c - 'a' + 10);
2190                 break;
2191             }
2192 
2193             if (ctx->size == 0) {
2194 
2195                 switch (ch) {
2196                 case CR:
2197                     state = sw_last_chunk_extension_almost_done;
2198                     break;
2199                 case LF:
2200                     state = sw_trailer;
2201                     break;
2202                 case ';':
2203                 case ' ':
2204                 case '\t':
2205                     state = sw_last_chunk_extension;
2206                     break;
2207                 default:
2208                     goto invalid;
2209                 }
2210 
2211                 break;
2212             }
2213 
2214             switch (ch) {
2215             case CR:
2216                 state = sw_chunk_extension_almost_done;
2217                 break;
2218             case LF:
2219                 state = sw_chunk_data;
2220                 break;
2221             case ';':
2222             case ' ':
2223             case '\t':
2224                 state = sw_chunk_extension;
2225                 break;
2226             default:
2227                 goto invalid;
2228             }
2229 
2230             break;
2231 
2232         case sw_chunk_extension:
2233             switch (ch) {
2234             case CR:
2235                 state = sw_chunk_extension_almost_done;
2236                 break;
2237             case LF:
2238                 state = sw_chunk_data;
2239             }
2240             break;
2241 
2242         case sw_chunk_extension_almost_done:
2243             if (ch == LF) {
2244                 state = sw_chunk_data;
2245                 break;
2246             }
2247             goto invalid;
2248 
2249         case sw_chunk_data:
2250             rc = NGX_OK;
2251             goto data;
2252 
2253         case sw_after_data:
2254             switch (ch) {
2255             case CR:
2256                 state = sw_after_data_almost_done;
2257                 break;
2258             case LF:
2259                 state = sw_chunk_start;
2260             }
2261             break;
2262 
2263         case sw_after_data_almost_done:
2264             if (ch == LF) {
2265                 state = sw_chunk_start;
2266                 break;
2267             }
2268             goto invalid;
2269 
2270         case sw_last_chunk_extension:
2271             switch (ch) {
2272             case CR:
2273                 state = sw_last_chunk_extension_almost_done;
2274                 break;
2275             case LF:
2276                 state = sw_trailer;
2277             }
2278             break;
2279 
2280         case sw_last_chunk_extension_almost_done:
2281             if (ch == LF) {
2282                 state = sw_trailer;
2283                 break;
2284             }
2285             goto invalid;
2286 
2287         case sw_trailer:
2288             switch (ch) {
2289             case CR:
2290                 state = sw_trailer_almost_done;
2291                 break;
2292             case LF:
2293                 goto done;
2294             default:
2295                 state = sw_trailer_header;
2296             }
2297             break;
2298 
2299         case sw_trailer_almost_done:
2300             if (ch == LF) {
2301                 goto done;
2302             }
2303             goto invalid;
2304 
2305         case sw_trailer_header:
2306             switch (ch) {
2307             case CR:
2308                 state = sw_trailer_header_almost_done;
2309                 break;
2310             case LF:
2311                 state = sw_trailer;
2312             }
2313             break;
2314 
2315         case sw_trailer_header_almost_done:
2316             if (ch == LF) {
2317                 state = sw_trailer;
2318                 break;
2319             }
2320             goto invalid;
2321 
2322         }
2323     }
2324 
2325 data:
2326 
2327     ctx->state = state;
2328     b->pos = pos;
2329 
2330     if (ctx->size > NGX_MAX_OFF_T_VALUE - 5) {
2331         goto invalid;
2332     }
2333 
2334     switch (state) {
2335 
2336     case sw_chunk_start:
2337         ctx->length = 3 /* "0" LF LF */;
2338         break;
2339     case sw_chunk_size:
2340         ctx->length = 1 /* LF */
2341                       + (ctx->size ? ctx->size + 4 /* LF "0" LF LF */
2342                                    : 1 /* LF */);
2343         break;
2344     case sw_chunk_extension:
2345     case sw_chunk_extension_almost_done:
2346         ctx->length = 1 /* LF */ + ctx->size + 4 /* LF "0" LF LF */;
2347         break;
2348     case sw_chunk_data:
2349         ctx->length = ctx->size + 4 /* LF "0" LF LF */;
2350         break;
2351     case sw_after_data:
2352     case sw_after_data_almost_done:
2353         ctx->length = 4 /* LF "0" LF LF */;
2354         break;
2355     case sw_last_chunk_extension:
2356     case sw_last_chunk_extension_almost_done:
2357         ctx->length = 2 /* LF LF */;
2358         break;
2359     case sw_trailer:
2360     case sw_trailer_almost_done:
2361         ctx->length = 1 /* LF */;
2362         break;
2363     case sw_trailer_header:
2364     case sw_trailer_header_almost_done:
2365         ctx->length = 2 /* LF LF */;
2366         break;
2367 
2368     }
2369 
2370     return rc;
2371 
2372 done:
2373 
2374     ctx->state = 0;
2375     b->pos = pos + 1;
2376 
2377     return NGX_DONE;
2378 
2379 invalid:
2380 
2381     return NGX_ERROR;
2382 }