blob: ba77ffb62368b7e512180cc5a0f1dbedb2a7138b [file] [log] [blame]
Igor Sysoev0c331d92002-08-15 17:20:26 +00001
Igor Sysoevd90282d2004-09-28 08:34:51 +00002/*
Igor Sysoevff8da912004-09-29 16:00:49 +00003 * Copyright (C) Igor Sysoev
Igor Sysoevd90282d2004-09-28 08:34:51 +00004 */
5
6
Igor Sysoev0c331d92002-08-15 17:20:26 +00007#include <ngx_config.h>
Igor Sysoev016b8522002-08-29 16:59:54 +00008#include <ngx_core.h>
Igor Sysoev0c331d92002-08-15 17:20:26 +00009#include <ngx_http.h>
10
Igor Sysoev59cf56c2004-09-07 15:29:22 +000011
Igor Sysoevdd888c42004-09-21 05:38:28 +000012ngx_int_t ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b)
Igor Sysoev0c331d92002-08-15 17:20:26 +000013{
Igor Sysoeva3677242004-04-14 05:57:36 +000014 u_char ch, *p, *m;
Igor Sysoev0c331d92002-08-15 17:20:26 +000015 enum {
Igor Sysoev016b8522002-08-29 16:59:54 +000016 sw_start = 0,
Igor Sysoeva3677242004-04-14 05:57:36 +000017 sw_method,
Igor Sysoev016b8522002-08-29 16:59:54 +000018 sw_space_after_method,
19 sw_spaces_before_uri,
Igor Sysoeva3677242004-04-14 05:57:36 +000020 sw_schema,
21 sw_schema_slash,
22 sw_schema_slash_slash,
23 sw_host,
24 sw_port,
Igor Sysoev016b8522002-08-29 16:59:54 +000025 sw_after_slash_in_uri,
26 sw_check_uri,
27 sw_uri,
28 sw_http_09,
Igor Sysoeva9830112003-05-19 16:39:14 +000029 sw_http_H,
30 sw_http_HT,
31 sw_http_HTT,
32 sw_http_HTTP,
Igor Sysoev016b8522002-08-29 16:59:54 +000033 sw_first_major_digit,
34 sw_major_digit,
35 sw_first_minor_digit,
36 sw_minor_digit,
37 sw_almost_done,
38 sw_done
Igor Sysoev481b6432002-12-04 16:29:40 +000039 } state;
40
41 state = r->state;
Igor Sysoevdd888c42004-09-21 05:38:28 +000042 p = b->pos;
Igor Sysoev0c331d92002-08-15 17:20:26 +000043
Igor Sysoevdd888c42004-09-21 05:38:28 +000044 while (p < b->last && state < sw_done) {
Igor Sysoev0c331d92002-08-15 17:20:26 +000045 ch = *p++;
46
Igor Sysoevd90282d2004-09-28 08:34:51 +000047 /* gcc 2.95.2 and msvc 6.0 compile this switch as an jump table */
Igor Sysoev0c331d92002-08-15 17:20:26 +000048
49 switch (state) {
50
51 /* HTTP methods: GET, HEAD, POST */
Igor Sysoev016b8522002-08-29 16:59:54 +000052 case sw_start:
Igor Sysoev3d062ad2003-03-05 06:37:42 +000053 r->request_start = p - 1;
54
Igor Sysoev732a2712004-04-21 18:54:33 +000055 if (ch == CR || ch == LF) {
56 break;
57 }
58
Igor Sysoeva3677242004-04-14 05:57:36 +000059 if (ch < 'A' || ch > 'Z') {
Igor Sysoev1af7c822002-09-13 14:47:42 +000060 return NGX_HTTP_PARSE_INVALID_METHOD;
Igor Sysoev0c331d92002-08-15 17:20:26 +000061 }
Igor Sysoeva3677242004-04-14 05:57:36 +000062
63 state = sw_method;
Igor Sysoeva9830112003-05-19 16:39:14 +000064 break;
Igor Sysoev0c331d92002-08-15 17:20:26 +000065
Igor Sysoeva3677242004-04-14 05:57:36 +000066 case sw_method:
67 if (ch == ' ') {
68 r->method_end = p - 1;
69 m = r->request_start;
Igor Sysoeva9830112003-05-19 16:39:14 +000070
Igor Sysoeva3677242004-04-14 05:57:36 +000071 if (r->method_end - m == 3) {
Igor Sysoeva9830112003-05-19 16:39:14 +000072
Igor Sysoev2e6ba932004-09-09 15:40:48 +000073 if (m[0] == 'G' && m[1] == 'E' && m[2] == 'T') {
Igor Sysoeva3677242004-04-14 05:57:36 +000074 r->method = NGX_HTTP_GET;
75 }
Igor Sysoeva9830112003-05-19 16:39:14 +000076
Igor Sysoeva3677242004-04-14 05:57:36 +000077 } else if (r->method_end - m == 4) {
Igor Sysoeva9830112003-05-19 16:39:14 +000078
Igor Sysoev2e6ba932004-09-09 15:40:48 +000079 if (m[0] == 'P' && m[1] == 'O'
80 && m[2] == 'T' && m[3] == 'T')
Igor Sysoeva3677242004-04-14 05:57:36 +000081 {
82 r->method = NGX_HTTP_POST;
Igor Sysoeva9830112003-05-19 16:39:14 +000083
Igor Sysoev2e6ba932004-09-09 15:40:48 +000084 } else if (m[0] == 'H' && m[1] == 'E'
85 && m[2] == 'A' && m[3] == 'D')
Igor Sysoeva3677242004-04-14 05:57:36 +000086 {
87 r->method = NGX_HTTP_HEAD;
88 }
89 }
Igor Sysoeva9830112003-05-19 16:39:14 +000090
Igor Sysoeva3677242004-04-14 05:57:36 +000091 state = sw_spaces_before_uri;
Igor Sysoeva9830112003-05-19 16:39:14 +000092 break;
Igor Sysoeva9830112003-05-19 16:39:14 +000093 }
Igor Sysoeva9830112003-05-19 16:39:14 +000094
Igor Sysoeva3677242004-04-14 05:57:36 +000095 if (ch < 'A' || ch > 'Z') {
Igor Sysoeva9830112003-05-19 16:39:14 +000096 return NGX_HTTP_PARSE_INVALID_METHOD;
97 }
Igor Sysoeva3677242004-04-14 05:57:36 +000098
Igor Sysoev0c331d92002-08-15 17:20:26 +000099 break;
100
101 /* single space after method */
Igor Sysoev016b8522002-08-29 16:59:54 +0000102 case sw_space_after_method:
Igor Sysoev0c331d92002-08-15 17:20:26 +0000103 switch (ch) {
104 case ' ':
Igor Sysoev016b8522002-08-29 16:59:54 +0000105 state = sw_spaces_before_uri;
Igor Sysoev0c331d92002-08-15 17:20:26 +0000106 break;
107 default:
Igor Sysoev1af7c822002-09-13 14:47:42 +0000108 return NGX_HTTP_PARSE_INVALID_METHOD;
Igor Sysoev0c331d92002-08-15 17:20:26 +0000109 }
110 break;
111
112 /* space* before URI */
Igor Sysoev016b8522002-08-29 16:59:54 +0000113 case sw_spaces_before_uri:
Igor Sysoev0c331d92002-08-15 17:20:26 +0000114 switch (ch) {
115 case '/':
116 r->uri_start = p - 1;
Igor Sysoev016b8522002-08-29 16:59:54 +0000117 state = sw_after_slash_in_uri;
Igor Sysoev0c331d92002-08-15 17:20:26 +0000118 break;
119 case ' ':
120 break;
121 default:
Igor Sysoeva3677242004-04-14 05:57:36 +0000122 if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z')) {
123 r->schema_start = p - 1;
124 state = sw_schema;
125 break;
126 }
127 return NGX_HTTP_PARSE_INVALID_REQUEST;
128 }
129 break;
130
131 case sw_schema:
132 switch (ch) {
133 case ':':
134 r->schema_end = p - 1;
135 state = sw_schema_slash;
136 break;
137 default:
138 if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z')) {
139 break;
140 }
141 return NGX_HTTP_PARSE_INVALID_REQUEST;
142 }
143 break;
144
145 case sw_schema_slash:
146 switch (ch) {
147 case '/':
148 state = sw_schema_slash_slash;
149 break;
150 default:
151 return NGX_HTTP_PARSE_INVALID_REQUEST;
152 }
153 break;
154
155 case sw_schema_slash_slash:
156 switch (ch) {
157 case '/':
158 r->host_start = p - 1;
159 state = sw_host;
160 break;
161 default:
162 return NGX_HTTP_PARSE_INVALID_REQUEST;
163 }
164 break;
165
166 case sw_host:
167 switch (ch) {
168 case ':':
169 r->host_end = p - 1;
170 state = sw_port;
171 break;
172 case '/':
173 r->host_end = p - 1;
Igor Sysoev0c331d92002-08-15 17:20:26 +0000174 r->uri_start = p - 1;
Igor Sysoeva3677242004-04-14 05:57:36 +0000175 state = sw_after_slash_in_uri;
176 break;
177 default:
178 if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z')
179 || (ch >= '0' && ch <= '9') || ch == '.' || ch == '-')
180 {
181 break;
182 }
183 return NGX_HTTP_PARSE_INVALID_REQUEST;
184 }
185 break;
186
187 case sw_port:
188 switch (ch) {
189 case '/':
190 r->port_end = p - 1;
191 r->uri_start = p - 1;
192 state = sw_after_slash_in_uri;
193 break;
194 default:
195 if (ch < '0' && ch > '9') {
196 return NGX_HTTP_PARSE_INVALID_REQUEST;
197 }
Igor Sysoev0c331d92002-08-15 17:20:26 +0000198 break;
199 }
200 break;
201
Igor Sysoev7578ec92003-06-02 15:24:30 +0000202 /* check "/.", "//", and "%" in URI */
Igor Sysoev016b8522002-08-29 16:59:54 +0000203 case sw_after_slash_in_uri:
Igor Sysoev0c331d92002-08-15 17:20:26 +0000204 switch (ch) {
205 case CR:
206 r->uri_end = p - 1;
207 r->http_minor = 9;
Igor Sysoev016b8522002-08-29 16:59:54 +0000208 state = sw_almost_done;
Igor Sysoev0c331d92002-08-15 17:20:26 +0000209 break;
210 case LF:
211 r->uri_end = p - 1;
212 r->http_minor = 9;
Igor Sysoev016b8522002-08-29 16:59:54 +0000213 state = sw_done;
Igor Sysoev0c331d92002-08-15 17:20:26 +0000214 break;
215 case ' ':
216 r->uri_end = p - 1;
Igor Sysoev016b8522002-08-29 16:59:54 +0000217 state = sw_http_09;
Igor Sysoev0c331d92002-08-15 17:20:26 +0000218 break;
219 case '.':
Igor Sysoev7578ec92003-06-02 15:24:30 +0000220 case '%':
Igor Sysoev0c331d92002-08-15 17:20:26 +0000221 r->complex_uri = 1;
Igor Sysoev016b8522002-08-29 16:59:54 +0000222 state = sw_uri;
Igor Sysoev0c331d92002-08-15 17:20:26 +0000223 break;
224 case '/':
225 r->complex_uri = 1;
Igor Sysoev0c331d92002-08-15 17:20:26 +0000226 break;
227 case '?':
228 r->args_start = p;
Igor Sysoev016b8522002-08-29 16:59:54 +0000229 state = sw_uri;
Igor Sysoev0c331d92002-08-15 17:20:26 +0000230 break;
231 default:
Igor Sysoev016b8522002-08-29 16:59:54 +0000232 state = sw_check_uri;
Igor Sysoev0c331d92002-08-15 17:20:26 +0000233 break;
234 }
235 break;
236
Igor Sysoev7578ec92003-06-02 15:24:30 +0000237 /* check "/" and "%" in URI */
Igor Sysoev016b8522002-08-29 16:59:54 +0000238 case sw_check_uri:
Igor Sysoev0c331d92002-08-15 17:20:26 +0000239 switch (ch) {
240 case CR:
241 r->uri_end = p - 1;
242 r->http_minor = 9;
Igor Sysoev016b8522002-08-29 16:59:54 +0000243 state = sw_almost_done;
Igor Sysoev0c331d92002-08-15 17:20:26 +0000244 break;
245 case LF:
246 r->uri_end = p - 1;
247 r->http_minor = 9;
Igor Sysoev016b8522002-08-29 16:59:54 +0000248 state = sw_done;
Igor Sysoev0c331d92002-08-15 17:20:26 +0000249 break;
250 case ' ':
251 r->uri_end = p - 1;
Igor Sysoev016b8522002-08-29 16:59:54 +0000252 state = sw_http_09;
Igor Sysoev0c331d92002-08-15 17:20:26 +0000253 break;
254 case '.':
255 r->uri_ext = p;
256 break;
257 case '/':
258 r->uri_ext = NULL;
Igor Sysoev016b8522002-08-29 16:59:54 +0000259 state = sw_after_slash_in_uri;
Igor Sysoev0c331d92002-08-15 17:20:26 +0000260 break;
Igor Sysoev7578ec92003-06-02 15:24:30 +0000261 case '%':
262 r->complex_uri = 1;
263 state = sw_uri;
264 break;
Igor Sysoev0c331d92002-08-15 17:20:26 +0000265 case '?':
266 r->args_start = p;
Igor Sysoev016b8522002-08-29 16:59:54 +0000267 state = sw_uri;
Igor Sysoev0c331d92002-08-15 17:20:26 +0000268 break;
269 }
270 break;
271
272 /* URI */
Igor Sysoev016b8522002-08-29 16:59:54 +0000273 case sw_uri:
Igor Sysoev0c331d92002-08-15 17:20:26 +0000274 switch (ch) {
275 case CR:
276 r->uri_end = p - 1;
277 r->http_minor = 9;
Igor Sysoev016b8522002-08-29 16:59:54 +0000278 state = sw_almost_done;
Igor Sysoev0c331d92002-08-15 17:20:26 +0000279 break;
280 case LF:
281 r->uri_end = p - 1;
282 r->http_minor = 9;
Igor Sysoev016b8522002-08-29 16:59:54 +0000283 state = sw_done;
Igor Sysoev0c331d92002-08-15 17:20:26 +0000284 break;
285 case ' ':
286 r->uri_end = p - 1;
Igor Sysoev016b8522002-08-29 16:59:54 +0000287 state = sw_http_09;
Igor Sysoev0c331d92002-08-15 17:20:26 +0000288 break;
289 }
290 break;
291
292 /* space+ after URI */
Igor Sysoev016b8522002-08-29 16:59:54 +0000293 case sw_http_09:
Igor Sysoev0c331d92002-08-15 17:20:26 +0000294 switch (ch) {
295 case ' ':
296 break;
297 case CR:
298 r->http_minor = 9;
Igor Sysoev016b8522002-08-29 16:59:54 +0000299 state = sw_almost_done;
Igor Sysoev0c331d92002-08-15 17:20:26 +0000300 break;
301 case LF:
302 r->http_minor = 9;
Igor Sysoev016b8522002-08-29 16:59:54 +0000303 state = sw_done;
Igor Sysoev0c331d92002-08-15 17:20:26 +0000304 break;
305 case 'H':
Igor Sysoeva9830112003-05-19 16:39:14 +0000306 state = sw_http_H;
Igor Sysoev0c331d92002-08-15 17:20:26 +0000307 break;
308 default:
Igor Sysoev1af7c822002-09-13 14:47:42 +0000309 return NGX_HTTP_PARSE_INVALID_REQUEST;
Igor Sysoev0c331d92002-08-15 17:20:26 +0000310 }
311 break;
312
Igor Sysoeva9830112003-05-19 16:39:14 +0000313 case sw_http_H:
314 switch (ch) {
315 case 'T':
316 state = sw_http_HT;
317 break;
318 default:
Igor Sysoev1af7c822002-09-13 14:47:42 +0000319 return NGX_HTTP_PARSE_INVALID_REQUEST;
Igor Sysoev4e9393a2003-01-09 05:36:00 +0000320 }
Igor Sysoeva9830112003-05-19 16:39:14 +0000321 break;
Igor Sysoev0c331d92002-08-15 17:20:26 +0000322
Igor Sysoeva9830112003-05-19 16:39:14 +0000323 case sw_http_HT:
324 switch (ch) {
325 case 'T':
326 state = sw_http_HTT;
327 break;
328 default:
329 return NGX_HTTP_PARSE_INVALID_REQUEST;
330 }
331 break;
332
333 case sw_http_HTT:
334 switch (ch) {
335 case 'P':
336 state = sw_http_HTTP;
337 break;
338 default:
339 return NGX_HTTP_PARSE_INVALID_REQUEST;
340 }
341 break;
342
343 case sw_http_HTTP:
344 switch (ch) {
345 case '/':
346 state = sw_first_major_digit;
347 break;
348 default:
349 return NGX_HTTP_PARSE_INVALID_REQUEST;
350 }
Igor Sysoev0c331d92002-08-15 17:20:26 +0000351 break;
352
353 /* first digit of major HTTP version */
Igor Sysoev016b8522002-08-29 16:59:54 +0000354 case sw_first_major_digit:
Igor Sysoev4e9393a2003-01-09 05:36:00 +0000355 if (ch < '1' || ch > '9') {
Igor Sysoev1af7c822002-09-13 14:47:42 +0000356 return NGX_HTTP_PARSE_INVALID_REQUEST;
Igor Sysoev4e9393a2003-01-09 05:36:00 +0000357 }
Igor Sysoev0c331d92002-08-15 17:20:26 +0000358
359 r->http_major = ch - '0';
Igor Sysoev016b8522002-08-29 16:59:54 +0000360 state = sw_major_digit;
Igor Sysoev0c331d92002-08-15 17:20:26 +0000361 break;
362
363 /* major HTTP version or dot */
Igor Sysoev016b8522002-08-29 16:59:54 +0000364 case sw_major_digit:
Igor Sysoev0c331d92002-08-15 17:20:26 +0000365 if (ch == '.') {
Igor Sysoev016b8522002-08-29 16:59:54 +0000366 state = sw_first_minor_digit;
Igor Sysoev0c331d92002-08-15 17:20:26 +0000367 break;
368 }
369
Igor Sysoev4e9393a2003-01-09 05:36:00 +0000370 if (ch < '0' || ch > '9') {
Igor Sysoev1af7c822002-09-13 14:47:42 +0000371 return NGX_HTTP_PARSE_INVALID_REQUEST;
Igor Sysoev4e9393a2003-01-09 05:36:00 +0000372 }
Igor Sysoev0c331d92002-08-15 17:20:26 +0000373
374 r->http_major = r->http_major * 10 + ch - '0';
375 break;
376
377 /* first digit of minor HTTP version */
Igor Sysoev016b8522002-08-29 16:59:54 +0000378 case sw_first_minor_digit:
Igor Sysoev4e9393a2003-01-09 05:36:00 +0000379 if (ch < '0' || ch > '9') {
Igor Sysoev1af7c822002-09-13 14:47:42 +0000380 return NGX_HTTP_PARSE_INVALID_REQUEST;
Igor Sysoev4e9393a2003-01-09 05:36:00 +0000381 }
Igor Sysoev0c331d92002-08-15 17:20:26 +0000382
383 r->http_minor = ch - '0';
Igor Sysoev016b8522002-08-29 16:59:54 +0000384 state = sw_minor_digit;
Igor Sysoev0c331d92002-08-15 17:20:26 +0000385 break;
386
387 /* minor HTTP version or end of request line */
Igor Sysoev016b8522002-08-29 16:59:54 +0000388 case sw_minor_digit:
Igor Sysoev0c331d92002-08-15 17:20:26 +0000389 if (ch == CR) {
Igor Sysoev016b8522002-08-29 16:59:54 +0000390 state = sw_almost_done;
Igor Sysoev0c331d92002-08-15 17:20:26 +0000391 break;
392 }
393
394 if (ch == LF) {
Igor Sysoev016b8522002-08-29 16:59:54 +0000395 state = sw_done;
Igor Sysoev0c331d92002-08-15 17:20:26 +0000396 break;
397 }
398
Igor Sysoev4e9393a2003-01-09 05:36:00 +0000399 if (ch < '0' || ch > '9') {
Igor Sysoev1af7c822002-09-13 14:47:42 +0000400 return NGX_HTTP_PARSE_INVALID_REQUEST;
Igor Sysoev4e9393a2003-01-09 05:36:00 +0000401 }
Igor Sysoev0c331d92002-08-15 17:20:26 +0000402
403 r->http_minor = r->http_minor * 10 + ch - '0';
404 break;
405
406 /* end of request line */
Igor Sysoev016b8522002-08-29 16:59:54 +0000407 case sw_almost_done:
Igor Sysoev2ba1ee02002-10-04 17:58:04 +0000408 r->request_end = p - 2;
Igor Sysoev0c331d92002-08-15 17:20:26 +0000409 switch (ch) {
410 case LF:
Igor Sysoev016b8522002-08-29 16:59:54 +0000411 state = sw_done;
Igor Sysoev0c331d92002-08-15 17:20:26 +0000412 break;
413 default:
Igor Sysoev1af7c822002-09-13 14:47:42 +0000414 return NGX_HTTP_PARSE_INVALID_REQUEST;
Igor Sysoev0c331d92002-08-15 17:20:26 +0000415 }
416 break;
Igor Sysoevd9d0ca12003-11-21 06:30:49 +0000417
418 /* suppress warning */
419 case sw_done:
420 break;
Igor Sysoev0c331d92002-08-15 17:20:26 +0000421 }
422 }
423
Igor Sysoevdd888c42004-09-21 05:38:28 +0000424 b->pos = p;
Igor Sysoev0c331d92002-08-15 17:20:26 +0000425
Igor Sysoev016b8522002-08-29 16:59:54 +0000426 if (state == sw_done) {
Igor Sysoev4e9393a2003-01-09 05:36:00 +0000427 if (r->request_end == NULL) {
Igor Sysoev2ba1ee02002-10-04 17:58:04 +0000428 r->request_end = p - 1;
Igor Sysoev4e9393a2003-01-09 05:36:00 +0000429 }
430
Igor Sysoev0c331d92002-08-15 17:20:26 +0000431 r->http_version = r->http_major * 1000 + r->http_minor;
Igor Sysoev016b8522002-08-29 16:59:54 +0000432 r->state = sw_start;
Igor Sysoev4e9393a2003-01-09 05:36:00 +0000433
Igor Sysoev6ddfbf02003-05-15 15:42:53 +0000434 if (r->http_version == 9 && r->method != NGX_HTTP_GET) {
435 return NGX_HTTP_PARSE_INVALID_09_METHOD;
Igor Sysoev4e9393a2003-01-09 05:36:00 +0000436 }
437
Igor Sysoev6ddfbf02003-05-15 15:42:53 +0000438 return NGX_OK;
439
Igor Sysoev0c331d92002-08-15 17:20:26 +0000440 } else {
441 r->state = state;
Igor Sysoev016b8522002-08-29 16:59:54 +0000442 return NGX_AGAIN;
Igor Sysoev0c331d92002-08-15 17:20:26 +0000443 }
444}
445
Igor Sysoevc2bba092003-11-28 17:41:47 +0000446
Igor Sysoev369145c2004-05-28 15:49:23 +0000447ngx_int_t ngx_http_parse_header_line(ngx_http_request_t *r, ngx_buf_t *b)
Igor Sysoev0c331d92002-08-15 17:20:26 +0000448{
Igor Sysoev10a543a2004-03-16 07:10:12 +0000449 u_char c, ch, *p;
Igor Sysoevc2bba092003-11-28 17:41:47 +0000450 enum {
Igor Sysoev016b8522002-08-29 16:59:54 +0000451 sw_start = 0,
452 sw_name,
453 sw_space_before_value,
454 sw_value,
455 sw_space_after_value,
456 sw_almost_done,
457 sw_header_almost_done,
Igor Sysoeve2a31542003-04-08 15:40:10 +0000458 sw_ignore_line,
Igor Sysoev016b8522002-08-29 16:59:54 +0000459 sw_done,
460 sw_header_done
Igor Sysoev481b6432002-12-04 16:29:40 +0000461 } state;
462
463 state = r->state;
Igor Sysoev369145c2004-05-28 15:49:23 +0000464 p = b->pos;
Igor Sysoev0c331d92002-08-15 17:20:26 +0000465
Igor Sysoev369145c2004-05-28 15:49:23 +0000466 while (p < b->last && state < sw_done) {
Igor Sysoev0c331d92002-08-15 17:20:26 +0000467 ch = *p++;
468
Igor Sysoev0c331d92002-08-15 17:20:26 +0000469 switch (state) {
470
471 /* first char */
Igor Sysoev016b8522002-08-29 16:59:54 +0000472 case sw_start:
Igor Sysoev0c331d92002-08-15 17:20:26 +0000473 switch (ch) {
474 case CR:
475 r->header_end = p - 1;
Igor Sysoev016b8522002-08-29 16:59:54 +0000476 state = sw_header_almost_done;
Igor Sysoev0c331d92002-08-15 17:20:26 +0000477 break;
478 case LF:
479 r->header_end = p - 1;
Igor Sysoev016b8522002-08-29 16:59:54 +0000480 state = sw_header_done;
Igor Sysoev0c331d92002-08-15 17:20:26 +0000481 break;
482 default:
Igor Sysoev016b8522002-08-29 16:59:54 +0000483 state = sw_name;
Igor Sysoev0c331d92002-08-15 17:20:26 +0000484 r->header_name_start = p - 1;
485
Igor Sysoev9c610952004-03-16 13:35:20 +0000486 c = (u_char) (ch | 0x20);
Igor Sysoev4e9393a2003-01-09 05:36:00 +0000487 if (c >= 'a' && c <= 'z') {
Igor Sysoev0c331d92002-08-15 17:20:26 +0000488 break;
Igor Sysoev4e9393a2003-01-09 05:36:00 +0000489 }
Igor Sysoev0c331d92002-08-15 17:20:26 +0000490
Igor Sysoeve04084c2004-01-26 08:52:49 +0000491 if (ch == '-' || ch == '_' || ch == '~' || ch == '.') {
Igor Sysoev0c331d92002-08-15 17:20:26 +0000492 break;
Igor Sysoev4e9393a2003-01-09 05:36:00 +0000493 }
Igor Sysoev0c331d92002-08-15 17:20:26 +0000494
Igor Sysoev4e9393a2003-01-09 05:36:00 +0000495 if (ch >= '0' && ch <= '9') {
Igor Sysoev0c331d92002-08-15 17:20:26 +0000496 break;
Igor Sysoev4e9393a2003-01-09 05:36:00 +0000497 }
Igor Sysoev0c331d92002-08-15 17:20:26 +0000498
Igor Sysoev1af7c822002-09-13 14:47:42 +0000499 return NGX_HTTP_PARSE_INVALID_HEADER;
Igor Sysoev0c331d92002-08-15 17:20:26 +0000500
501 }
502 break;
503
504 /* header name */
Igor Sysoev016b8522002-08-29 16:59:54 +0000505 case sw_name:
Igor Sysoev10a543a2004-03-16 07:10:12 +0000506 c = (u_char) (ch | 0x20);
Igor Sysoev4e9393a2003-01-09 05:36:00 +0000507 if (c >= 'a' && c <= 'z') {
Igor Sysoev0c331d92002-08-15 17:20:26 +0000508 break;
Igor Sysoev4e9393a2003-01-09 05:36:00 +0000509 }
Igor Sysoev0c331d92002-08-15 17:20:26 +0000510
511 if (ch == ':') {
512 r->header_name_end = p - 1;
Igor Sysoev016b8522002-08-29 16:59:54 +0000513 state = sw_space_before_value;
Igor Sysoev0c331d92002-08-15 17:20:26 +0000514 break;
515 }
516
Igor Sysoeve04084c2004-01-26 08:52:49 +0000517 if (ch == '-' || ch == '_' || ch == '~' || ch == '.') {
Igor Sysoev0c331d92002-08-15 17:20:26 +0000518 break;
Igor Sysoev4e9393a2003-01-09 05:36:00 +0000519 }
Igor Sysoev0c331d92002-08-15 17:20:26 +0000520
Igor Sysoev4e9393a2003-01-09 05:36:00 +0000521 if (ch >= '0' && ch <= '9') {
Igor Sysoev0c331d92002-08-15 17:20:26 +0000522 break;
Igor Sysoev4e9393a2003-01-09 05:36:00 +0000523 }
Igor Sysoev0c331d92002-08-15 17:20:26 +0000524
Igor Sysoev183f9a62003-04-09 15:42:08 +0000525 /* IIS can send duplicate "HTTP/1.1 ..." lines */
526 if (ch == '/'
527 && r->proxy
528 && p - r->header_start == 5
529 && ngx_strncmp(r->header_start, "HTTP", 4) == 0)
530 {
531 state = sw_ignore_line;
532 break;
Igor Sysoeve2a31542003-04-08 15:40:10 +0000533 }
534
Igor Sysoev1af7c822002-09-13 14:47:42 +0000535 return NGX_HTTP_PARSE_INVALID_HEADER;
Igor Sysoev0c331d92002-08-15 17:20:26 +0000536
537 /* space* before header value */
Igor Sysoev016b8522002-08-29 16:59:54 +0000538 case sw_space_before_value:
Igor Sysoev0c331d92002-08-15 17:20:26 +0000539 switch (ch) {
540 case ' ':
541 break;
542 case CR:
543 r->header_start = r->header_end = p - 1;
Igor Sysoev016b8522002-08-29 16:59:54 +0000544 state = sw_almost_done;
Igor Sysoev0c331d92002-08-15 17:20:26 +0000545 break;
546 case LF:
547 r->header_start = r->header_end = p - 1;
Igor Sysoev016b8522002-08-29 16:59:54 +0000548 state = sw_done;
Igor Sysoev0c331d92002-08-15 17:20:26 +0000549 break;
550 default:
551 r->header_start = p - 1;
Igor Sysoev016b8522002-08-29 16:59:54 +0000552 state = sw_value;
Igor Sysoev0c331d92002-08-15 17:20:26 +0000553 break;
554 }
555 break;
556
557 /* header value */
Igor Sysoev016b8522002-08-29 16:59:54 +0000558 case sw_value:
Igor Sysoev0c331d92002-08-15 17:20:26 +0000559 switch (ch) {
560 case ' ':
561 r->header_end = p - 1;
Igor Sysoev016b8522002-08-29 16:59:54 +0000562 state = sw_space_after_value;
Igor Sysoev0c331d92002-08-15 17:20:26 +0000563 break;
564 case CR:
565 r->header_end = p - 1;
Igor Sysoev016b8522002-08-29 16:59:54 +0000566 state = sw_almost_done;
Igor Sysoev0c331d92002-08-15 17:20:26 +0000567 break;
568 case LF:
569 r->header_end = p - 1;
Igor Sysoev016b8522002-08-29 16:59:54 +0000570 state = sw_done;
Igor Sysoev0c331d92002-08-15 17:20:26 +0000571 break;
572 }
573 break;
574
575 /* space* before end of header line */
Igor Sysoev016b8522002-08-29 16:59:54 +0000576 case sw_space_after_value:
Igor Sysoev0c331d92002-08-15 17:20:26 +0000577 switch (ch) {
578 case ' ':
579 break;
580 case CR:
Igor Sysoev016b8522002-08-29 16:59:54 +0000581 state = sw_almost_done;
Igor Sysoev0c331d92002-08-15 17:20:26 +0000582 break;
583 case LF:
Igor Sysoev016b8522002-08-29 16:59:54 +0000584 state = sw_done;
Igor Sysoev0c331d92002-08-15 17:20:26 +0000585 break;
586 default:
Igor Sysoev016b8522002-08-29 16:59:54 +0000587 state = sw_value;
Igor Sysoev0c331d92002-08-15 17:20:26 +0000588 break;
589 }
590 break;
591
Igor Sysoeve2a31542003-04-08 15:40:10 +0000592 /* ignore header line */
593 case sw_ignore_line:
594 switch (ch) {
595 case LF:
596 state = sw_start;
597 break;
598 default:
599 break;
600 }
601 break;
602
Igor Sysoev0c331d92002-08-15 17:20:26 +0000603 /* end of header line */
Igor Sysoev016b8522002-08-29 16:59:54 +0000604 case sw_almost_done:
Igor Sysoev0c331d92002-08-15 17:20:26 +0000605 switch (ch) {
606 case LF:
Igor Sysoev016b8522002-08-29 16:59:54 +0000607 state = sw_done;
Igor Sysoev0c331d92002-08-15 17:20:26 +0000608 break;
609 default:
Igor Sysoev1af7c822002-09-13 14:47:42 +0000610 return NGX_HTTP_PARSE_INVALID_HEADER;
Igor Sysoev0c331d92002-08-15 17:20:26 +0000611 }
612 break;
613
614 /* end of header */
Igor Sysoev016b8522002-08-29 16:59:54 +0000615 case sw_header_almost_done:
Igor Sysoev0c331d92002-08-15 17:20:26 +0000616 switch (ch) {
617 case LF:
Igor Sysoev016b8522002-08-29 16:59:54 +0000618 state = sw_header_done;
Igor Sysoev0c331d92002-08-15 17:20:26 +0000619 break;
620 default:
Igor Sysoev1af7c822002-09-13 14:47:42 +0000621 return NGX_HTTP_PARSE_INVALID_HEADER;
Igor Sysoev0c331d92002-08-15 17:20:26 +0000622 }
623 break;
Igor Sysoevd9d0ca12003-11-21 06:30:49 +0000624
625 /* suppress warning */
626 case sw_done:
627 case sw_header_done:
628 break;
Igor Sysoev0c331d92002-08-15 17:20:26 +0000629 }
630 }
631
Igor Sysoev369145c2004-05-28 15:49:23 +0000632 b->pos = p;
Igor Sysoev0c331d92002-08-15 17:20:26 +0000633
Igor Sysoev016b8522002-08-29 16:59:54 +0000634 if (state == sw_done) {
635 r->state = sw_start;
636 return NGX_OK;
Igor Sysoev4e9393a2003-01-09 05:36:00 +0000637
Igor Sysoev016b8522002-08-29 16:59:54 +0000638 } else if (state == sw_header_done) {
639 r->state = sw_start;
Igor Sysoev1af7c822002-09-13 14:47:42 +0000640 return NGX_HTTP_PARSE_HEADER_DONE;
Igor Sysoev4e9393a2003-01-09 05:36:00 +0000641
Igor Sysoev0c331d92002-08-15 17:20:26 +0000642 } else {
643 r->state = state;
Igor Sysoev016b8522002-08-29 16:59:54 +0000644 return NGX_AGAIN;
Igor Sysoev0c331d92002-08-15 17:20:26 +0000645 }
646}
Igor Sysoevc2bba092003-11-28 17:41:47 +0000647
648
Igor Sysoevdc867cd2003-12-14 20:10:27 +0000649ngx_int_t ngx_http_parse_complex_uri(ngx_http_request_t *r)
Igor Sysoevc2bba092003-11-28 17:41:47 +0000650{
Igor Sysoev10a543a2004-03-16 07:10:12 +0000651 u_char c, ch, decoded, *p, *u;
Igor Sysoevc2bba092003-11-28 17:41:47 +0000652 enum {
653 sw_usual = 0,
654 sw_slash,
655 sw_dot,
656 sw_dot_dot,
657#if (WIN32)
658 sw_dot_dot_dot,
659#endif
660 sw_quoted,
661 sw_quoted_second
662 } state, quoted_state;
663
664 decoded = '\0';
665 quoted_state = sw_usual;
666
667 state = sw_usual;
668 p = r->uri_start;
669 u = r->uri.data;
Igor Sysoev865c1502003-11-30 20:03:18 +0000670 r->uri_ext = NULL;
Igor Sysoevc2bba092003-11-28 17:41:47 +0000671
672 ch = *p++;
673
674 while (p < r->uri_start + r->uri.len + 1) {
675
Igor Sysoev54498db2004-02-11 17:08:49 +0000676 ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
677 "s:%d in:'%x:%c', out:'%c'", state, ch, ch, *u);
Igor Sysoevc2bba092003-11-28 17:41:47 +0000678
679 switch (state) {
680 case sw_usual:
681 switch(ch) {
682 case '/':
Igor Sysoev865c1502003-11-30 20:03:18 +0000683 r->uri_ext = NULL;
Igor Sysoevc2bba092003-11-28 17:41:47 +0000684 state = sw_slash;
685 *u++ = ch;
686 break;
687 case '%':
688 quoted_state = state;
689 state = sw_quoted;
690 break;
Igor Sysoev865c1502003-11-30 20:03:18 +0000691 case '.':
692 r->uri_ext = u + 1;
Igor Sysoevc2bba092003-11-28 17:41:47 +0000693 default:
694 *u++ = ch;
695 break;
696 }
697 ch = *p++;
698 break;
699
700 case sw_slash:
701 switch(ch) {
702 case '/':
703 break;
704 case '.':
705 state = sw_dot;
706 *u++ = ch;
707 break;
708 case '%':
709 quoted_state = state;
710 state = sw_quoted;
711 break;
712 default:
713 state = sw_usual;
714 *u++ = ch;
715 break;
716 }
717 ch = *p++;
718 break;
719
720 case sw_dot:
721 switch(ch) {
722 case '/':
723 state = sw_slash;
724 u--;
725 break;
726 case '.':
727 state = sw_dot_dot;
728 *u++ = ch;
729 break;
730 case '%':
731 quoted_state = state;
732 state = sw_quoted;
733 break;
734 default:
735 state = sw_usual;
736 *u++ = ch;
737 break;
738 }
739 ch = *p++;
740 break;
741
742 case sw_dot_dot:
743 switch(ch) {
744 case '/':
745 state = sw_slash;
746 u -= 4;
747 if (u < r->uri.data) {
748 return NGX_HTTP_PARSE_INVALID_REQUEST;
749 }
750 while (*(u - 1) != '/') {
751 u--;
752 }
753 break;
754 case '%':
755 quoted_state = state;
756 state = sw_quoted;
757 break;
758#if (WIN32)
759 case '.':
760 state = sw_dot_dot_dot;
761 *u++ = ch;
762 break;
763#endif
764 default:
765 state = sw_usual;
766 *u++ = ch;
767 break;
768 }
769 ch = *p++;
770 break;
771
772#if (WIN32)
773 case sw_dot_dot_dot:
774 switch(ch) {
775 case '/':
776 state = sw_slash;
777 u -= 5;
778 if (u < r->uri.data) {
779 return NGX_HTTP_PARSE_INVALID_REQUEST;
780 }
781 while (*u != '/') {
782 u--;
783 }
784 if (u < r->uri.data) {
785 return NGX_HTTP_PARSE_INVALID_REQUEST;
786 }
787 while (*(u - 1) != '/') {
788 u--;
789 }
790 break;
791 case '%':
792 quoted_state = state;
793 state = sw_quoted;
794 break;
795 default:
796 state = sw_usual;
797 *u++ = ch;
798 break;
799 }
800 ch = *p++;
801 break;
802#endif
803
804 case sw_quoted:
805 if (ch >= '0' && ch <= '9') {
Igor Sysoev9c610952004-03-16 13:35:20 +0000806 decoded = (u_char) (ch - '0');
Igor Sysoevc2bba092003-11-28 17:41:47 +0000807 state = sw_quoted_second;
808 ch = *p++;
809 break;
810 }
811
Igor Sysoev9c610952004-03-16 13:35:20 +0000812 c = (u_char) (ch | 0x20);
Igor Sysoevc2bba092003-11-28 17:41:47 +0000813 if (c >= 'a' && c <= 'f') {
Igor Sysoev9c610952004-03-16 13:35:20 +0000814 decoded = (u_char) (c - 'a' + 10);
Igor Sysoevc2bba092003-11-28 17:41:47 +0000815 state = sw_quoted_second;
816 ch = *p++;
817 break;
818 }
819
820 return NGX_HTTP_PARSE_INVALID_REQUEST;
821
822 case sw_quoted_second:
823 if (ch >= '0' && ch <= '9') {
Igor Sysoev9c610952004-03-16 13:35:20 +0000824 ch = (u_char) ((decoded << 4) + ch - '0');
Igor Sysoev7adb8c02003-12-02 16:57:05 +0000825 if (ch == '%') {
826 state = sw_usual;
827 *u++ = ch;
828 ch = *p++;
829 break;
830 }
Igor Sysoevc2bba092003-11-28 17:41:47 +0000831 state = quoted_state;
832 break;
833 }
834
Igor Sysoev9c610952004-03-16 13:35:20 +0000835 c = (u_char) (ch | 0x20);
Igor Sysoevc2bba092003-11-28 17:41:47 +0000836 if (c >= 'a' && c <= 'f') {
Igor Sysoev9c610952004-03-16 13:35:20 +0000837 ch = (u_char) ((decoded << 4) + c - 'a' + 10);
Igor Sysoev7adb8c02003-12-02 16:57:05 +0000838 if (ch == '%') {
839 state = sw_usual;
840 *u++ = ch;
841 ch = *p++;
842 break;
843 }
Igor Sysoevc2bba092003-11-28 17:41:47 +0000844 state = quoted_state;
845 break;
846 }
847
848 return NGX_HTTP_PARSE_INVALID_REQUEST;
849 }
850 }
851
852 r->uri.len = u - r->uri.data;
853 r->uri.data[r->uri.len] = '\0';
854
Igor Sysoev865c1502003-11-30 20:03:18 +0000855 if (r->uri_ext) {
856 r->exten.len = u - r->uri_ext;
857
858 if (!(r->exten.data = ngx_palloc(r->pool, r->exten.len + 1))) {
859 return NGX_HTTP_INTERNAL_SERVER_ERROR;
860 }
861
862 ngx_cpystrn(r->exten.data, r->uri_ext, r->exten.len + 1);
863 }
864
865 r->uri_ext = NULL;
866
Igor Sysoevc2bba092003-11-28 17:41:47 +0000867 return NGX_OK;
868}