Back to home page

Nginx displayed by LXR

Source navigation ]
Diff markup ]
Identifier search ]
general search ]
 
 
Version: nginx-1.15.12 ]​[ nginx-1.16.0 ]​

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