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