Groonga 3.0.9 Source Code Document
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
ngx_http_parse.c
Go to the documentation of this file.
1 
2 /*
3  * Copyright (C) Igor Sysoev
4  * Copyright (C) Nginx, Inc.
5  */
6 
7 
8 #include <ngx_config.h>
9 #include <ngx_core.h>
10 #include <ngx_http.h>
11 
12 
13 static uint32_t usual[] = {
14  0xffffdbfe, /* 1111 1111 1111 1111 1101 1011 1111 1110 */
15 
16  /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */
17  0x7fff37d6, /* 0111 1111 1111 1111 0011 0111 1101 0110 */
18 
19  /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */
20 #if (NGX_WIN32)
21  0xefffffff, /* 1110 1111 1111 1111 1111 1111 1111 1111 */
22 #else
23  0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
24 #endif
25 
26  /* ~}| {zyx wvut srqp onml kjih gfed cba` */
27  0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
28 
29  0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
30  0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
31  0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
32  0xffffffff /* 1111 1111 1111 1111 1111 1111 1111 1111 */
33 };
34 
35 
36 #if (NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED)
37 
38 #define ngx_str3_cmp(m, c0, c1, c2, c3) \
39  *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)
40 
41 #define ngx_str3Ocmp(m, c0, c1, c2, c3) \
42  *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)
43 
44 #define ngx_str4cmp(m, c0, c1, c2, c3) \
45  *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)
46 
47 #define ngx_str5cmp(m, c0, c1, c2, c3, c4) \
48  *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0) \
49  && m[4] == c4
50 
51 #define ngx_str6cmp(m, c0, c1, c2, c3, c4, c5) \
52  *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0) \
53  && (((uint32_t *) m)[1] & 0xffff) == ((c5 << 8) | c4)
54 
55 #define ngx_str7_cmp(m, c0, c1, c2, c3, c4, c5, c6, c7) \
56  *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0) \
57  && ((uint32_t *) m)[1] == ((c7 << 24) | (c6 << 16) | (c5 << 8) | c4)
58 
59 #define ngx_str8cmp(m, c0, c1, c2, c3, c4, c5, c6, c7) \
60  *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0) \
61  && ((uint32_t *) m)[1] == ((c7 << 24) | (c6 << 16) | (c5 << 8) | c4)
62 
63 #define ngx_str9cmp(m, c0, c1, c2, c3, c4, c5, c6, c7, c8) \
64  *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0) \
65  && ((uint32_t *) m)[1] == ((c7 << 24) | (c6 << 16) | (c5 << 8) | c4) \
66  && m[8] == c8
67 
68 #else /* !(NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED) */
69 
70 #define ngx_str3_cmp(m, c0, c1, c2, c3) \
71  m[0] == c0 && m[1] == c1 && m[2] == c2
72 
73 #define ngx_str3Ocmp(m, c0, c1, c2, c3) \
74  m[0] == c0 && m[2] == c2 && m[3] == c3
75 
76 #define ngx_str4cmp(m, c0, c1, c2, c3) \
77  m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3
78 
79 #define ngx_str5cmp(m, c0, c1, c2, c3, c4) \
80  m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3 && m[4] == c4
81 
82 #define ngx_str6cmp(m, c0, c1, c2, c3, c4, c5) \
83  m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3 \
84  && m[4] == c4 && m[5] == c5
85 
86 #define ngx_str7_cmp(m, c0, c1, c2, c3, c4, c5, c6, c7) \
87  m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3 \
88  && m[4] == c4 && m[5] == c5 && m[6] == c6
89 
90 #define ngx_str8cmp(m, c0, c1, c2, c3, c4, c5, c6, c7) \
91  m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3 \
92  && m[4] == c4 && m[5] == c5 && m[6] == c6 && m[7] == c7
93 
94 #define ngx_str9cmp(m, c0, c1, c2, c3, c4, c5, c6, c7, c8) \
95  m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3 \
96  && m[4] == c4 && m[5] == c5 && m[6] == c6 && m[7] == c7 && m[8] == c8
97 
98 #endif
99 
100 
101 /* gcc, icc, msvc and others compile these switches as an jump table */
102 
103 ngx_int_t
105 {
106  u_char c, ch, *p, *m;
107  enum {
108  sw_start = 0,
109  sw_method,
110  sw_spaces_before_uri,
111  sw_schema,
112  sw_schema_slash,
113  sw_schema_slash_slash,
114  sw_host_start,
115  sw_host,
116  sw_host_end,
117  sw_host_ip_literal,
118  sw_port,
119  sw_host_http_09,
120  sw_after_slash_in_uri,
121  sw_check_uri,
122  sw_check_uri_http_09,
123  sw_uri,
124  sw_http_09,
125  sw_http_H,
126  sw_http_HT,
127  sw_http_HTT,
128  sw_http_HTTP,
129  sw_first_major_digit,
130  sw_major_digit,
131  sw_first_minor_digit,
132  sw_minor_digit,
133  sw_spaces_after_digit,
134  sw_almost_done
135  } state;
136 
137  state = r->state;
138 
139  for (p = b->pos; p < b->last; p++) {
140  ch = *p;
141 
142  switch (state) {
143 
144  /* HTTP methods: GET, HEAD, POST */
145  case sw_start:
146  r->request_start = p;
147 
148  if (ch == CR || ch == LF) {
149  break;
150  }
151 
152  if ((ch < 'A' || ch > 'Z') && ch != '_') {
154  }
155 
156  state = sw_method;
157  break;
158 
159  case sw_method:
160  if (ch == ' ') {
161  r->method_end = p - 1;
162  m = r->request_start;
163 
164  switch (p - m) {
165 
166  case 3:
167  if (ngx_str3_cmp(m, 'G', 'E', 'T', ' ')) {
168  r->method = NGX_HTTP_GET;
169  break;
170  }
171 
172  if (ngx_str3_cmp(m, 'P', 'U', 'T', ' ')) {
173  r->method = NGX_HTTP_PUT;
174  break;
175  }
176 
177  break;
178 
179  case 4:
180  if (m[1] == 'O') {
181 
182  if (ngx_str3Ocmp(m, 'P', 'O', 'S', 'T')) {
183  r->method = NGX_HTTP_POST;
184  break;
185  }
186 
187  if (ngx_str3Ocmp(m, 'C', 'O', 'P', 'Y')) {
188  r->method = NGX_HTTP_COPY;
189  break;
190  }
191 
192  if (ngx_str3Ocmp(m, 'M', 'O', 'V', 'E')) {
193  r->method = NGX_HTTP_MOVE;
194  break;
195  }
196 
197  if (ngx_str3Ocmp(m, 'L', 'O', 'C', 'K')) {
198  r->method = NGX_HTTP_LOCK;
199  break;
200  }
201 
202  } else {
203 
204  if (ngx_str4cmp(m, 'H', 'E', 'A', 'D')) {
205  r->method = NGX_HTTP_HEAD;
206  break;
207  }
208  }
209 
210  break;
211 
212  case 5:
213  if (ngx_str5cmp(m, 'M', 'K', 'C', 'O', 'L')) {
214  r->method = NGX_HTTP_MKCOL;
215  }
216 
217  if (ngx_str5cmp(m, 'P', 'A', 'T', 'C', 'H')) {
218  r->method = NGX_HTTP_PATCH;
219  }
220 
221  if (ngx_str5cmp(m, 'T', 'R', 'A', 'C', 'E')) {
222  r->method = NGX_HTTP_TRACE;
223  }
224 
225  break;
226 
227  case 6:
228  if (ngx_str6cmp(m, 'D', 'E', 'L', 'E', 'T', 'E')) {
229  r->method = NGX_HTTP_DELETE;
230  break;
231  }
232 
233  if (ngx_str6cmp(m, 'U', 'N', 'L', 'O', 'C', 'K')) {
234  r->method = NGX_HTTP_UNLOCK;
235  break;
236  }
237 
238  break;
239 
240  case 7:
241  if (ngx_str7_cmp(m, 'O', 'P', 'T', 'I', 'O', 'N', 'S', ' '))
242  {
244  }
245 
246  break;
247 
248  case 8:
249  if (ngx_str8cmp(m, 'P', 'R', 'O', 'P', 'F', 'I', 'N', 'D'))
250  {
252  }
253 
254  break;
255 
256  case 9:
257  if (ngx_str9cmp(m,
258  'P', 'R', 'O', 'P', 'P', 'A', 'T', 'C', 'H'))
259  {
261  }
262 
263  break;
264  }
265 
266  state = sw_spaces_before_uri;
267  break;
268  }
269 
270  if ((ch < 'A' || ch > 'Z') && ch != '_') {
272  }
273 
274  break;
275 
276  /* space* before URI */
277  case sw_spaces_before_uri:
278 
279  if (ch == '/') {
280  r->uri_start = p;
281  state = sw_after_slash_in_uri;
282  break;
283  }
284 
285  c = (u_char) (ch | 0x20);
286  if (c >= 'a' && c <= 'z') {
287  r->schema_start = p;
288  state = sw_schema;
289  break;
290  }
291 
292  switch (ch) {
293  case ' ':
294  break;
295  default:
297  }
298  break;
299 
300  case sw_schema:
301 
302  c = (u_char) (ch | 0x20);
303  if (c >= 'a' && c <= 'z') {
304  break;
305  }
306 
307  switch (ch) {
308  case ':':
309  r->schema_end = p;
310  state = sw_schema_slash;
311  break;
312  default:
314  }
315  break;
316 
317  case sw_schema_slash:
318  switch (ch) {
319  case '/':
320  state = sw_schema_slash_slash;
321  break;
322  default:
324  }
325  break;
326 
327  case sw_schema_slash_slash:
328  switch (ch) {
329  case '/':
330  state = sw_host_start;
331  break;
332  default:
334  }
335  break;
336 
337  case sw_host_start:
338 
339  r->host_start = p;
340 
341  if (ch == '[') {
342  state = sw_host_ip_literal;
343  break;
344  }
345 
346  state = sw_host;
347 
348  /* fall through */
349 
350  case sw_host:
351 
352  c = (u_char) (ch | 0x20);
353  if (c >= 'a' && c <= 'z') {
354  break;
355  }
356 
357  if ((ch >= '0' && ch <= '9') || ch == '.' || ch == '-') {
358  break;
359  }
360 
361  /* fall through */
362 
363  case sw_host_end:
364 
365  r->host_end = p;
366 
367  switch (ch) {
368  case ':':
369  state = sw_port;
370  break;
371  case '/':
372  r->uri_start = p;
373  state = sw_after_slash_in_uri;
374  break;
375  case ' ':
376  /*
377  * use single "/" from request line to preserve pointers,
378  * if request line will be copied to large client buffer
379  */
380  r->uri_start = r->schema_end + 1;
381  r->uri_end = r->schema_end + 2;
382  state = sw_host_http_09;
383  break;
384  default:
386  }
387  break;
388 
389  case sw_host_ip_literal:
390 
391  if (ch >= '0' && ch <= '9') {
392  break;
393  }
394 
395  c = (u_char) (ch | 0x20);
396  if (c >= 'a' && c <= 'z') {
397  break;
398  }
399 
400  switch (ch) {
401  case ':':
402  break;
403  case ']':
404  state = sw_host_end;
405  break;
406  case '-':
407  case '.':
408  case '_':
409  case '~':
410  /* unreserved */
411  break;
412  case '!':
413  case '$':
414  case '&':
415  case '\'':
416  case '(':
417  case ')':
418  case '*':
419  case '+':
420  case ',':
421  case ';':
422  case '=':
423  /* sub-delims */
424  break;
425  default:
427  }
428  break;
429 
430  case sw_port:
431  if (ch >= '0' && ch <= '9') {
432  break;
433  }
434 
435  switch (ch) {
436  case '/':
437  r->port_end = p;
438  r->uri_start = p;
439  state = sw_after_slash_in_uri;
440  break;
441  case ' ':
442  r->port_end = p;
443  /*
444  * use single "/" from request line to preserve pointers,
445  * if request line will be copied to large client buffer
446  */
447  r->uri_start = r->schema_end + 1;
448  r->uri_end = r->schema_end + 2;
449  state = sw_host_http_09;
450  break;
451  default:
453  }
454  break;
455 
456  /* space+ after "http://host[:port] " */
457  case sw_host_http_09:
458  switch (ch) {
459  case ' ':
460  break;
461  case CR:
462  r->http_minor = 9;
463  state = sw_almost_done;
464  break;
465  case LF:
466  r->http_minor = 9;
467  goto done;
468  case 'H':
469  r->http_protocol.data = p;
470  state = sw_http_H;
471  break;
472  default:
474  }
475  break;
476 
477 
478  /* check "/.", "//", "%", and "\" (Win32) in URI */
479  case sw_after_slash_in_uri:
480 
481  if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
482  state = sw_check_uri;
483  break;
484  }
485 
486  switch (ch) {
487  case ' ':
488  r->uri_end = p;
489  state = sw_check_uri_http_09;
490  break;
491  case CR:
492  r->uri_end = p;
493  r->http_minor = 9;
494  state = sw_almost_done;
495  break;
496  case LF:
497  r->uri_end = p;
498  r->http_minor = 9;
499  goto done;
500  case '.':
501  r->complex_uri = 1;
502  state = sw_uri;
503  break;
504  case '%':
505  r->quoted_uri = 1;
506  state = sw_uri;
507  break;
508  case '/':
509  r->complex_uri = 1;
510  state = sw_uri;
511  break;
512 #if (NGX_WIN32)
513  case '\\':
514  r->complex_uri = 1;
515  state = sw_uri;
516  break;
517 #endif
518  case '?':
519  r->args_start = p + 1;
520  state = sw_uri;
521  break;
522  case '#':
523  r->complex_uri = 1;
524  state = sw_uri;
525  break;
526  case '+':
527  r->plus_in_uri = 1;
528  break;
529  case '\0':
531  default:
532  state = sw_check_uri;
533  break;
534  }
535  break;
536 
537  /* check "/", "%" and "\" (Win32) in URI */
538  case sw_check_uri:
539 
540  if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
541  break;
542  }
543 
544  switch (ch) {
545  case '/':
546 #if (NGX_WIN32)
547  if (r->uri_ext == p) {
548  r->complex_uri = 1;
549  state = sw_uri;
550  break;
551  }
552 #endif
553  r->uri_ext = NULL;
554  state = sw_after_slash_in_uri;
555  break;
556  case '.':
557  r->uri_ext = p + 1;
558  break;
559  case ' ':
560  r->uri_end = p;
561  state = sw_check_uri_http_09;
562  break;
563  case CR:
564  r->uri_end = p;
565  r->http_minor = 9;
566  state = sw_almost_done;
567  break;
568  case LF:
569  r->uri_end = p;
570  r->http_minor = 9;
571  goto done;
572 #if (NGX_WIN32)
573  case '\\':
574  r->complex_uri = 1;
575  state = sw_after_slash_in_uri;
576  break;
577 #endif
578  case '%':
579  r->quoted_uri = 1;
580  state = sw_uri;
581  break;
582  case '?':
583  r->args_start = p + 1;
584  state = sw_uri;
585  break;
586  case '#':
587  r->complex_uri = 1;
588  state = sw_uri;
589  break;
590  case '+':
591  r->plus_in_uri = 1;
592  break;
593  case '\0':
595  }
596  break;
597 
598  /* space+ after URI */
599  case sw_check_uri_http_09:
600  switch (ch) {
601  case ' ':
602  break;
603  case CR:
604  r->http_minor = 9;
605  state = sw_almost_done;
606  break;
607  case LF:
608  r->http_minor = 9;
609  goto done;
610  case 'H':
611  r->http_protocol.data = p;
612  state = sw_http_H;
613  break;
614  default:
615  r->space_in_uri = 1;
616  state = sw_check_uri;
617  break;
618  }
619  break;
620 
621 
622  /* URI */
623  case sw_uri:
624 
625  if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
626  break;
627  }
628 
629  switch (ch) {
630  case ' ':
631  r->uri_end = p;
632  state = sw_http_09;
633  break;
634  case CR:
635  r->uri_end = p;
636  r->http_minor = 9;
637  state = sw_almost_done;
638  break;
639  case LF:
640  r->uri_end = p;
641  r->http_minor = 9;
642  goto done;
643  case '#':
644  r->complex_uri = 1;
645  break;
646  case '\0':
648  }
649  break;
650 
651  /* space+ after URI */
652  case sw_http_09:
653  switch (ch) {
654  case ' ':
655  break;
656  case CR:
657  r->http_minor = 9;
658  state = sw_almost_done;
659  break;
660  case LF:
661  r->http_minor = 9;
662  goto done;
663  case 'H':
664  r->http_protocol.data = p;
665  state = sw_http_H;
666  break;
667  default:
668  r->space_in_uri = 1;
669  state = sw_uri;
670  break;
671  }
672  break;
673 
674  case sw_http_H:
675  switch (ch) {
676  case 'T':
677  state = sw_http_HT;
678  break;
679  default:
681  }
682  break;
683 
684  case sw_http_HT:
685  switch (ch) {
686  case 'T':
687  state = sw_http_HTT;
688  break;
689  default:
691  }
692  break;
693 
694  case sw_http_HTT:
695  switch (ch) {
696  case 'P':
697  state = sw_http_HTTP;
698  break;
699  default:
701  }
702  break;
703 
704  case sw_http_HTTP:
705  switch (ch) {
706  case '/':
707  state = sw_first_major_digit;
708  break;
709  default:
711  }
712  break;
713 
714  /* first digit of major HTTP version */
715  case sw_first_major_digit:
716  if (ch < '1' || ch > '9') {
718  }
719 
720  r->http_major = ch - '0';
721  state = sw_major_digit;
722  break;
723 
724  /* major HTTP version or dot */
725  case sw_major_digit:
726  if (ch == '.') {
727  state = sw_first_minor_digit;
728  break;
729  }
730 
731  if (ch < '0' || ch > '9') {
733  }
734 
735  r->http_major = r->http_major * 10 + ch - '0';
736  break;
737 
738  /* first digit of minor HTTP version */
739  case sw_first_minor_digit:
740  if (ch < '0' || ch > '9') {
742  }
743 
744  r->http_minor = ch - '0';
745  state = sw_minor_digit;
746  break;
747 
748  /* minor HTTP version or end of request line */
749  case sw_minor_digit:
750  if (ch == CR) {
751  state = sw_almost_done;
752  break;
753  }
754 
755  if (ch == LF) {
756  goto done;
757  }
758 
759  if (ch == ' ') {
760  state = sw_spaces_after_digit;
761  break;
762  }
763 
764  if (ch < '0' || ch > '9') {
766  }
767 
768  r->http_minor = r->http_minor * 10 + ch - '0';
769  break;
770 
771  case sw_spaces_after_digit:
772  switch (ch) {
773  case ' ':
774  break;
775  case CR:
776  state = sw_almost_done;
777  break;
778  case LF:
779  goto done;
780  default:
782  }
783  break;
784 
785  /* end of request line */
786  case sw_almost_done:
787  r->request_end = p - 1;
788  switch (ch) {
789  case LF:
790  goto done;
791  default:
793  }
794  }
795  }
796 
797  b->pos = p;
798  r->state = state;
799 
800  return NGX_AGAIN;
801 
802 done:
803 
804  b->pos = p + 1;
805 
806  if (r->request_end == NULL) {
807  r->request_end = p;
808  }
809 
810  r->http_version = r->http_major * 1000 + r->http_minor;
811  r->state = sw_start;
812 
813  if (r->http_version == 9 && r->method != NGX_HTTP_GET) {
815  }
816 
817  return NGX_OK;
818 }
819 
820 
821 ngx_int_t
823  ngx_uint_t allow_underscores)
824 {
825  u_char c, ch, *p;
826  ngx_uint_t hash, i;
827  enum {
828  sw_start = 0,
829  sw_name,
830  sw_space_before_value,
831  sw_value,
832  sw_space_after_value,
833  sw_ignore_line,
834  sw_almost_done,
835  sw_header_almost_done
836  } state;
837 
838  /* the last '\0' is not needed because string is zero terminated */
839 
840  static u_char lowcase[] =
841  "\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"
842  "\0\0\0\0\0\0\0\0\0\0\0\0\0-\0\0" "0123456789\0\0\0\0\0\0"
843  "\0abcdefghijklmnopqrstuvwxyz\0\0\0\0\0"
844  "\0abcdefghijklmnopqrstuvwxyz\0\0\0\0\0"
845  "\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"
846  "\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"
847  "\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"
848  "\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";
849 
850  state = r->state;
851  hash = r->header_hash;
852  i = r->lowcase_index;
853 
854  for (p = b->pos; p < b->last; p++) {
855  ch = *p;
856 
857  switch (state) {
858 
859  /* first char */
860  case sw_start:
861  r->header_name_start = p;
862  r->invalid_header = 0;
863 
864  switch (ch) {
865  case CR:
866  r->header_end = p;
867  state = sw_header_almost_done;
868  break;
869  case LF:
870  r->header_end = p;
871  goto header_done;
872  default:
873  state = sw_name;
874 
875  c = lowcase[ch];
876 
877  if (c) {
878  hash = ngx_hash(0, c);
879  r->lowcase_header[0] = c;
880  i = 1;
881  break;
882  }
883 
884  if (ch == '\0') {
886  }
887 
888  r->invalid_header = 1;
889 
890  break;
891 
892  }
893  break;
894 
895  /* header name */
896  case sw_name:
897  c = lowcase[ch];
898 
899  if (c) {
900  hash = ngx_hash(hash, c);
901  r->lowcase_header[i++] = c;
902  i &= (NGX_HTTP_LC_HEADER_LEN - 1);
903  break;
904  }
905 
906  if (ch == '_') {
907  if (allow_underscores) {
908  hash = ngx_hash(hash, ch);
909  r->lowcase_header[i++] = ch;
910  i &= (NGX_HTTP_LC_HEADER_LEN - 1);
911 
912  } else {
913  r->invalid_header = 1;
914  }
915 
916  break;
917  }
918 
919  if (ch == ':') {
920  r->header_name_end = p;
921  state = sw_space_before_value;
922  break;
923  }
924 
925  if (ch == CR) {
926  r->header_name_end = p;
927  r->header_start = p;
928  r->header_end = p;
929  state = sw_almost_done;
930  break;
931  }
932 
933  if (ch == LF) {
934  r->header_name_end = p;
935  r->header_start = p;
936  r->header_end = p;
937  goto done;
938  }
939 
940  /* IIS may send the duplicate "HTTP/1.1 ..." lines */
941  if (ch == '/'
942  && r->upstream
943  && p - r->header_name_start == 4
944  && ngx_strncmp(r->header_name_start, "HTTP", 4) == 0)
945  {
946  state = sw_ignore_line;
947  break;
948  }
949 
950  if (ch == '\0') {
952  }
953 
954  r->invalid_header = 1;
955 
956  break;
957 
958  /* space* before header value */
959  case sw_space_before_value:
960  switch (ch) {
961  case ' ':
962  break;
963  case CR:
964  r->header_start = p;
965  r->header_end = p;
966  state = sw_almost_done;
967  break;
968  case LF:
969  r->header_start = p;
970  r->header_end = p;
971  goto done;
972  case '\0':
974  default:
975  r->header_start = p;
976  state = sw_value;
977  break;
978  }
979  break;
980 
981  /* header value */
982  case sw_value:
983  switch (ch) {
984  case ' ':
985  r->header_end = p;
986  state = sw_space_after_value;
987  break;
988  case CR:
989  r->header_end = p;
990  state = sw_almost_done;
991  break;
992  case LF:
993  r->header_end = p;
994  goto done;
995  case '\0':
997  }
998  break;
999 
1000  /* space* before end of header line */
1001  case sw_space_after_value:
1002  switch (ch) {
1003  case ' ':
1004  break;
1005  case CR:
1006  state = sw_almost_done;
1007  break;
1008  case LF:
1009  goto done;
1010  case '\0':
1012  default:
1013  state = sw_value;
1014  break;
1015  }
1016  break;
1017 
1018  /* ignore header line */
1019  case sw_ignore_line:
1020  switch (ch) {
1021  case LF:
1022  state = sw_start;
1023  break;
1024  default:
1025  break;
1026  }
1027  break;
1028 
1029  /* end of header line */
1030  case sw_almost_done:
1031  switch (ch) {
1032  case LF:
1033  goto done;
1034  case CR:
1035  break;
1036  default:
1038  }
1039  break;
1040 
1041  /* end of header */
1042  case sw_header_almost_done:
1043  switch (ch) {
1044  case LF:
1045  goto header_done;
1046  default:
1048  }
1049  }
1050  }
1051 
1052  b->pos = p;
1053  r->state = state;
1054  r->header_hash = hash;
1055  r->lowcase_index = i;
1056 
1057  return NGX_AGAIN;
1058 
1059 done:
1060 
1061  b->pos = p + 1;
1062  r->state = sw_start;
1063  r->header_hash = hash;
1064  r->lowcase_index = i;
1065 
1066  return NGX_OK;
1067 
1068 header_done:
1069 
1070  b->pos = p + 1;
1071  r->state = sw_start;
1072 
1074 }
1075 
1076 
1077 ngx_int_t
1079 {
1080  u_char *p, ch;
1081  enum {
1082  sw_start = 0,
1083  sw_after_slash_in_uri,
1084  sw_check_uri,
1085  sw_uri
1086  } state;
1087 
1088  state = sw_start;
1089 
1090  for (p = r->uri_start; p != r->uri_end; p++) {
1091 
1092  ch = *p;
1093 
1094  switch (state) {
1095 
1096  case sw_start:
1097 
1098  if (ch != '/') {
1099  return NGX_ERROR;
1100  }
1101 
1102  state = sw_after_slash_in_uri;
1103  break;
1104 
1105  /* check "/.", "//", "%", and "\" (Win32) in URI */
1106  case sw_after_slash_in_uri:
1107 
1108  if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
1109  state = sw_check_uri;
1110  break;
1111  }
1112 
1113  switch (ch) {
1114  case ' ':
1115  r->space_in_uri = 1;
1116  state = sw_check_uri;
1117  break;
1118  case '.':
1119  r->complex_uri = 1;
1120  state = sw_uri;
1121  break;
1122  case '%':
1123  r->quoted_uri = 1;
1124  state = sw_uri;
1125  break;
1126  case '/':
1127  r->complex_uri = 1;
1128  state = sw_uri;
1129  break;
1130 #if (NGX_WIN32)
1131  case '\\':
1132  r->complex_uri = 1;
1133  state = sw_uri;
1134  break;
1135 #endif
1136  case '?':
1137  r->args_start = p + 1;
1138  state = sw_uri;
1139  break;
1140  case '#':
1141  r->complex_uri = 1;
1142  state = sw_uri;
1143  break;
1144  case '+':
1145  r->plus_in_uri = 1;
1146  break;
1147  default:
1148  state = sw_check_uri;
1149  break;
1150  }
1151  break;
1152 
1153  /* check "/", "%" and "\" (Win32) in URI */
1154  case sw_check_uri:
1155 
1156  if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
1157  break;
1158  }
1159 
1160  switch (ch) {
1161  case '/':
1162 #if (NGX_WIN32)
1163  if (r->uri_ext == p) {
1164  r->complex_uri = 1;
1165  state = sw_uri;
1166  break;
1167  }
1168 #endif
1169  r->uri_ext = NULL;
1170  state = sw_after_slash_in_uri;
1171  break;
1172  case '.':
1173  r->uri_ext = p + 1;
1174  break;
1175  case ' ':
1176  r->space_in_uri = 1;
1177  break;
1178 #if (NGX_WIN32)
1179  case '\\':
1180  r->complex_uri = 1;
1181  state = sw_after_slash_in_uri;
1182  break;
1183 #endif
1184  case '%':
1185  r->quoted_uri = 1;
1186  state = sw_uri;
1187  break;
1188  case '?':
1189  r->args_start = p + 1;
1190  state = sw_uri;
1191  break;
1192  case '#':
1193  r->complex_uri = 1;
1194  state = sw_uri;
1195  break;
1196  case '+':
1197  r->plus_in_uri = 1;
1198  break;
1199  }
1200  break;
1201 
1202  /* URI */
1203  case sw_uri:
1204 
1205  if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
1206  break;
1207  }
1208 
1209  switch (ch) {
1210  case ' ':
1211  r->space_in_uri = 1;
1212  break;
1213  case '#':
1214  r->complex_uri = 1;
1215  break;
1216  }
1217  break;
1218  }
1219  }
1220 
1221  return NGX_OK;
1222 }
1223 
1224 
1225 ngx_int_t
1227 {
1228  u_char c, ch, decoded, *p, *u;
1229  enum {
1230  sw_usual = 0,
1231  sw_slash,
1232  sw_dot,
1233  sw_dot_dot,
1234  sw_quoted,
1235  sw_quoted_second
1236  } state, quoted_state;
1237 
1238 #if (NGX_SUPPRESS_WARN)
1239  decoded = '\0';
1240  quoted_state = sw_usual;
1241 #endif
1242 
1243  state = sw_usual;
1244  p = r->uri_start;
1245  u = r->uri.data;
1246  r->uri_ext = NULL;
1247  r->args_start = NULL;
1248 
1249  ch = *p++;
1250 
1251  while (p <= r->uri_end) {
1252 
1253  /*
1254  * we use "ch = *p++" inside the cycle, but this operation is safe,
1255  * because after the URI there is always at least one character:
1256  * the line feed
1257  */
1258 
1260  "s:%d in:'%Xd:%c', out:'%c'", state, ch, ch, *u);
1261 
1262  switch (state) {
1263 
1264  case sw_usual:
1265 
1266  if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
1267  *u++ = ch;
1268  ch = *p++;
1269  break;
1270  }
1271 
1272  switch(ch) {
1273 #if (NGX_WIN32)
1274  case '\\':
1275  if (u - 2 >= r->uri.data
1276  && *(u - 1) == '.' && *(u - 2) != '.')
1277  {
1278  u--;
1279  }
1280 
1281  r->uri_ext = NULL;
1282 
1283  if (p == r->uri_start + r->uri.len) {
1284 
1285  /*
1286  * we omit the last "\" to cause redirect because
1287  * the browsers do not treat "\" as "/" in relative URL path
1288  */
1289 
1290  break;
1291  }
1292 
1293  state = sw_slash;
1294  *u++ = '/';
1295  break;
1296 #endif
1297  case '/':
1298 #if (NGX_WIN32)
1299  if (u - 2 >= r->uri.data
1300  && *(u - 1) == '.' && *(u - 2) != '.')
1301  {
1302  u--;
1303  }
1304 #endif
1305  r->uri_ext = NULL;
1306  state = sw_slash;
1307  *u++ = ch;
1308  break;
1309  case '%':
1310  quoted_state = state;
1311  state = sw_quoted;
1312  break;
1313  case '?':
1314  r->args_start = p;
1315  goto args;
1316  case '#':
1317  goto done;
1318  case '.':
1319  r->uri_ext = u + 1;
1320  *u++ = ch;
1321  break;
1322  case '+':
1323  r->plus_in_uri = 1;
1324  /* fall through */
1325  default:
1326  *u++ = ch;
1327  break;
1328  }
1329 
1330  ch = *p++;
1331  break;
1332 
1333  case sw_slash:
1334 
1335  if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
1336  state = sw_usual;
1337  *u++ = ch;
1338  ch = *p++;
1339  break;
1340  }
1341 
1342  switch(ch) {
1343 #if (NGX_WIN32)
1344  case '\\':
1345  break;
1346 #endif
1347  case '/':
1348  if (!merge_slashes) {
1349  *u++ = ch;
1350  }
1351  break;
1352  case '.':
1353  state = sw_dot;
1354  *u++ = ch;
1355  break;
1356  case '%':
1357  quoted_state = state;
1358  state = sw_quoted;
1359  break;
1360  case '?':
1361  r->args_start = p;
1362  goto args;
1363  case '#':
1364  goto done;
1365  case '+':
1366  r->plus_in_uri = 1;
1367  default:
1368  state = sw_usual;
1369  *u++ = ch;
1370  break;
1371  }
1372 
1373  ch = *p++;
1374  break;
1375 
1376  case sw_dot:
1377 
1378  if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
1379  state = sw_usual;
1380  *u++ = ch;
1381  ch = *p++;
1382  break;
1383  }
1384 
1385  switch(ch) {
1386 #if (NGX_WIN32)
1387  case '\\':
1388 #endif
1389  case '/':
1390  state = sw_slash;
1391  u--;
1392  break;
1393  case '.':
1394  state = sw_dot_dot;
1395  *u++ = ch;
1396  break;
1397  case '%':
1398  quoted_state = state;
1399  state = sw_quoted;
1400  break;
1401  case '?':
1402  r->args_start = p;
1403  goto args;
1404  case '#':
1405  goto done;
1406  case '+':
1407  r->plus_in_uri = 1;
1408  default:
1409  state = sw_usual;
1410  *u++ = ch;
1411  break;
1412  }
1413 
1414  ch = *p++;
1415  break;
1416 
1417  case sw_dot_dot:
1418 
1419  if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
1420  state = sw_usual;
1421  *u++ = ch;
1422  ch = *p++;
1423  break;
1424  }
1425 
1426  switch(ch) {
1427 #if (NGX_WIN32)
1428  case '\\':
1429 #endif
1430  case '/':
1431  state = sw_slash;
1432  u -= 5;
1433  for ( ;; ) {
1434  if (u < r->uri.data) {
1436  }
1437  if (*u == '/') {
1438  u++;
1439  break;
1440  }
1441  u--;
1442  }
1443  break;
1444  case '%':
1445  quoted_state = state;
1446  state = sw_quoted;
1447  break;
1448  case '?':
1449  r->args_start = p;
1450  goto args;
1451  case '#':
1452  goto done;
1453  case '+':
1454  r->plus_in_uri = 1;
1455  default:
1456  state = sw_usual;
1457  *u++ = ch;
1458  break;
1459  }
1460 
1461  ch = *p++;
1462  break;
1463 
1464  case sw_quoted:
1465  r->quoted_uri = 1;
1466 
1467  if (ch >= '0' && ch <= '9') {
1468  decoded = (u_char) (ch - '0');
1469  state = sw_quoted_second;
1470  ch = *p++;
1471  break;
1472  }
1473 
1474  c = (u_char) (ch | 0x20);
1475  if (c >= 'a' && c <= 'f') {
1476  decoded = (u_char) (c - 'a' + 10);
1477  state = sw_quoted_second;
1478  ch = *p++;
1479  break;
1480  }
1481 
1483 
1484  case sw_quoted_second:
1485  if (ch >= '0' && ch <= '9') {
1486  ch = (u_char) ((decoded << 4) + ch - '0');
1487 
1488  if (ch == '%' || ch == '#') {
1489  state = sw_usual;
1490  *u++ = ch;
1491  ch = *p++;
1492  break;
1493 
1494  } else if (ch == '\0') {
1496  }
1497 
1498  state = quoted_state;
1499  break;
1500  }
1501 
1502  c = (u_char) (ch | 0x20);
1503  if (c >= 'a' && c <= 'f') {
1504  ch = (u_char) ((decoded << 4) + c - 'a' + 10);
1505 
1506  if (ch == '?') {
1507  state = sw_usual;
1508  *u++ = ch;
1509  ch = *p++;
1510  break;
1511 
1512  } else if (ch == '+') {
1513  r->plus_in_uri = 1;
1514  }
1515 
1516  state = quoted_state;
1517  break;
1518  }
1519 
1521  }
1522  }
1523 
1524 done:
1525 
1526  r->uri.len = u - r->uri.data;
1527 
1528  if (r->uri_ext) {
1529  r->exten.len = u - r->uri_ext;
1530  r->exten.data = r->uri_ext;
1531  }
1532 
1533  r->uri_ext = NULL;
1534 
1535  return NGX_OK;
1536 
1537 args:
1538 
1539  while (p < r->uri_end) {
1540  if (*p++ != '#') {
1541  continue;
1542  }
1543 
1544  r->args.len = p - 1 - r->args_start;
1545  r->args.data = r->args_start;
1546  r->args_start = NULL;
1547 
1548  break;
1549  }
1550 
1551  r->uri.len = u - r->uri.data;
1552 
1553  if (r->uri_ext) {
1554  r->exten.len = u - r->uri_ext;
1555  r->exten.data = r->uri_ext;
1556  }
1557 
1558  r->uri_ext = NULL;
1559 
1560  return NGX_OK;
1561 }
1562 
1563 
1564 ngx_int_t
1566  ngx_http_status_t *status)
1567 {
1568  u_char ch;
1569  u_char *p;
1570  enum {
1571  sw_start = 0,
1572  sw_H,
1573  sw_HT,
1574  sw_HTT,
1575  sw_HTTP,
1576  sw_first_major_digit,
1577  sw_major_digit,
1578  sw_first_minor_digit,
1579  sw_minor_digit,
1580  sw_status,
1581  sw_space_after_status,
1582  sw_status_text,
1583  sw_almost_done
1584  } state;
1585 
1586  state = r->state;
1587 
1588  for (p = b->pos; p < b->last; p++) {
1589  ch = *p;
1590 
1591  switch (state) {
1592 
1593  /* "HTTP/" */
1594  case sw_start:
1595  switch (ch) {
1596  case 'H':
1597  state = sw_H;
1598  break;
1599  default:
1600  return NGX_ERROR;
1601  }
1602  break;
1603 
1604  case sw_H:
1605  switch (ch) {
1606  case 'T':
1607  state = sw_HT;
1608  break;
1609  default:
1610  return NGX_ERROR;
1611  }
1612  break;
1613 
1614  case sw_HT:
1615  switch (ch) {
1616  case 'T':
1617  state = sw_HTT;
1618  break;
1619  default:
1620  return NGX_ERROR;
1621  }
1622  break;
1623 
1624  case sw_HTT:
1625  switch (ch) {
1626  case 'P':
1627  state = sw_HTTP;
1628  break;
1629  default:
1630  return NGX_ERROR;
1631  }
1632  break;
1633 
1634  case sw_HTTP:
1635  switch (ch) {
1636  case '/':
1637  state = sw_first_major_digit;
1638  break;
1639  default:
1640  return NGX_ERROR;
1641  }
1642  break;
1643 
1644  /* the first digit of major HTTP version */
1645  case sw_first_major_digit:
1646  if (ch < '1' || ch > '9') {
1647  return NGX_ERROR;
1648  }
1649 
1650  r->http_major = ch - '0';
1651  state = sw_major_digit;
1652  break;
1653 
1654  /* the major HTTP version or dot */
1655  case sw_major_digit:
1656  if (ch == '.') {
1657  state = sw_first_minor_digit;
1658  break;
1659  }
1660 
1661  if (ch < '0' || ch > '9') {
1662  return NGX_ERROR;
1663  }
1664 
1665  r->http_major = r->http_major * 10 + ch - '0';
1666  break;
1667 
1668  /* the first digit of minor HTTP version */
1669  case sw_first_minor_digit:
1670  if (ch < '0' || ch > '9') {
1671  return NGX_ERROR;
1672  }
1673 
1674  r->http_minor = ch - '0';
1675  state = sw_minor_digit;
1676  break;
1677 
1678  /* the minor HTTP version or the end of the request line */
1679  case sw_minor_digit:
1680  if (ch == ' ') {
1681  state = sw_status;
1682  break;
1683  }
1684 
1685  if (ch < '0' || ch > '9') {
1686  return NGX_ERROR;
1687  }
1688 
1689  r->http_minor = r->http_minor * 10 + ch - '0';
1690  break;
1691 
1692  /* HTTP status code */
1693  case sw_status:
1694  if (ch == ' ') {
1695  break;
1696  }
1697 
1698  if (ch < '0' || ch > '9') {
1699  return NGX_ERROR;
1700  }
1701 
1702  status->code = status->code * 10 + ch - '0';
1703 
1704  if (++status->count == 3) {
1705  state = sw_space_after_status;
1706  status->start = p - 2;
1707  }
1708 
1709  break;
1710 
1711  /* space or end of line */
1712  case sw_space_after_status:
1713  switch (ch) {
1714  case ' ':
1715  state = sw_status_text;
1716  break;
1717  case '.': /* IIS may send 403.1, 403.2, etc */
1718  state = sw_status_text;
1719  break;
1720  case CR:
1721  state = sw_almost_done;
1722  break;
1723  case LF:
1724  goto done;
1725  default:
1726  return NGX_ERROR;
1727  }
1728  break;
1729 
1730  /* any text until end of line */
1731  case sw_status_text:
1732  switch (ch) {
1733  case CR:
1734  state = sw_almost_done;
1735 
1736  break;
1737  case LF:
1738  goto done;
1739  }
1740  break;
1741 
1742  /* end of status line */
1743  case sw_almost_done:
1744  status->end = p - 1;
1745  switch (ch) {
1746  case LF:
1747  goto done;
1748  default:
1749  return NGX_ERROR;
1750  }
1751  }
1752  }
1753 
1754  b->pos = p;
1755  r->state = state;
1756 
1757  return NGX_AGAIN;
1758 
1759 done:
1760 
1761  b->pos = p + 1;
1762 
1763  if (status->end == NULL) {
1764  status->end = p;
1765  }
1766 
1767  status->http_version = r->http_major * 1000 + r->http_minor;
1768  r->state = sw_start;
1769 
1770  return NGX_OK;
1771 }
1772 
1773 
1774 ngx_int_t
1776  ngx_str_t *args, ngx_uint_t *flags)
1777 {
1778  u_char ch, *p;
1779  size_t len;
1780 
1781  len = uri->len;
1782  p = uri->data;
1783 
1784  if (len == 0 || p[0] == '?') {
1785  goto unsafe;
1786  }
1787 
1788  if (p[0] == '.' && len == 3 && p[1] == '.' && (ngx_path_separator(p[2]))) {
1789  goto unsafe;
1790  }
1791 
1792  for ( /* void */ ; len; len--) {
1793 
1794  ch = *p++;
1795 
1796  if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
1797  continue;
1798  }
1799 
1800  if (ch == '?') {
1801  args->len = len - 1;
1802  args->data = p;
1803  uri->len -= len;
1804 
1805  return NGX_OK;
1806  }
1807 
1808  if (ch == '\0') {
1809  goto unsafe;
1810  }
1811 
1812  if (ngx_path_separator(ch) && len > 2) {
1813 
1814  /* detect "/../" */
1815 
1816  if (p[0] == '.' && p[1] == '.' && ngx_path_separator(p[2])) {
1817  goto unsafe;
1818  }
1819  }
1820  }
1821 
1822  return NGX_OK;
1823 
1824 unsafe:
1825 
1826  if (*flags & NGX_HTTP_LOG_UNSAFE) {
1828  "unsafe URI \"%V\" was detected", uri);
1829  }
1830 
1831  return NGX_ERROR;
1832 }
1833 
1834 
1835 ngx_int_t
1837  ngx_str_t *value)
1838 {
1839  ngx_uint_t i;
1840  u_char *start, *last, *end, ch;
1841  ngx_table_elt_t **h;
1842 
1843  h = headers->elts;
1844 
1845  for (i = 0; i < headers->nelts; i++) {
1846 
1847  ngx_log_debug2(NGX_LOG_DEBUG_HTTP, headers->pool->log, 0,
1848  "parse header: \"%V: %V\"", &h[i]->key, &h[i]->value);
1849 
1850  if (name->len > h[i]->value.len) {
1851  continue;
1852  }
1853 
1854  start = h[i]->value.data;
1855  end = h[i]->value.data + h[i]->value.len;
1856 
1857  while (start < end) {
1858 
1859  if (ngx_strncasecmp(start, name->data, name->len) != 0) {
1860  goto skip;
1861  }
1862 
1863  for (start += name->len; start < end && *start == ' '; start++) {
1864  /* void */
1865  }
1866 
1867  if (value == NULL) {
1868  if (start == end || *start == ',') {
1869  return i;
1870  }
1871 
1872  goto skip;
1873  }
1874 
1875  if (start == end || *start++ != '=') {
1876  /* the invalid header value */
1877  goto skip;
1878  }
1879 
1880  while (start < end && *start == ' ') { start++; }
1881 
1882  for (last = start; last < end && *last != ';'; last++) {
1883  /* void */
1884  }
1885 
1886  value->len = last - start;
1887  value->data = start;
1888 
1889  return i;
1890 
1891  skip:
1892 
1893  while (start < end) {
1894  ch = *start++;
1895  if (ch == ';' || ch == ',') {
1896  break;
1897  }
1898  }
1899 
1900  while (start < end && *start == ' ') { start++; }
1901  }
1902  }
1903 
1904  return NGX_DECLINED;
1905 }
1906 
1907 
1908 ngx_int_t
1909 ngx_http_arg(ngx_http_request_t *r, u_char *name, size_t len, ngx_str_t *value)
1910 {
1911  u_char *p, *last;
1912 
1913  if (r->args.len == 0) {
1914  return NGX_DECLINED;
1915  }
1916 
1917  p = r->args.data;
1918  last = p + r->args.len;
1919 
1920  for ( /* void */ ; p < last; p++) {
1921 
1922  /* we need '=' after name, so drop one char from last */
1923 
1924  p = ngx_strlcasestrn(p, last - 1, name, len - 1);
1925 
1926  if (p == NULL) {
1927  return NGX_DECLINED;
1928  }
1929 
1930  if ((p == r->args.data || *(p - 1) == '&') && *(p + len) == '=') {
1931 
1932  value->data = p + len + 1;
1933 
1934  p = ngx_strlchr(p, last, '&');
1935 
1936  if (p == NULL) {
1937  p = r->args.data + r->args.len;
1938  }
1939 
1940  value->len = p - value->data;
1941 
1942  return NGX_OK;
1943  }
1944  }
1945 
1946  return NGX_DECLINED;
1947 }
1948 
1949 
1950 void
1952 {
1953  u_char *p, *last;
1954 
1955  last = uri->data + uri->len;
1956 
1957  p = ngx_strlchr(uri->data, last, '?');
1958 
1959  if (p) {
1960  uri->len = p - uri->data;
1961  p++;
1962  args->len = last - p;
1963  args->data = p;
1964 
1965  } else {
1966  args->len = 0;
1967  }
1968 }
1969 
1970 
1971 ngx_int_t
1973  ngx_http_chunked_t *ctx)
1974 {
1975  u_char *pos, ch, c;
1976  ngx_int_t rc;
1977  enum {
1978  sw_chunk_start = 0,
1979  sw_chunk_size,
1980  sw_chunk_extension,
1981  sw_chunk_extension_almost_done,
1982  sw_chunk_data,
1983  sw_after_data,
1984  sw_after_data_almost_done,
1985  sw_last_chunk_extension,
1986  sw_last_chunk_extension_almost_done,
1987  sw_trailer,
1988  sw_trailer_almost_done,
1989  sw_trailer_header,
1990  sw_trailer_header_almost_done
1991  } state;
1992 
1993  state = ctx->state;
1994 
1995  if (state == sw_chunk_data && ctx->size == 0) {
1996  state = sw_after_data;
1997  }
1998 
1999  rc = NGX_AGAIN;
2000 
2001  for (pos = b->pos; pos < b->last; pos++) {
2002 
2003  ch = *pos;
2004 
2006  "http chunked byte: %02Xd s:%d", ch, state);
2007 
2008  switch (state) {
2009 
2010  case sw_chunk_start:
2011  if (ch >= '0' && ch <= '9') {
2012  state = sw_chunk_size;
2013  ctx->size = ch - '0';
2014  break;
2015  }
2016 
2017  c = (u_char) (ch | 0x20);
2018 
2019  if (c >= 'a' && c <= 'f') {
2020  state = sw_chunk_size;
2021  ctx->size = c - 'a' + 10;
2022  break;
2023  }
2024 
2025  goto invalid;
2026 
2027  case sw_chunk_size:
2028  if (ch >= '0' && ch <= '9') {
2029  ctx->size = ctx->size * 16 + (ch - '0');
2030  break;
2031  }
2032 
2033  c = (u_char) (ch | 0x20);
2034 
2035  if (c >= 'a' && c <= 'f') {
2036  ctx->size = ctx->size * 16 + (c - 'a' + 10);
2037  break;
2038  }
2039 
2040  if (ctx->size == 0) {
2041 
2042  switch (ch) {
2043  case CR:
2044  state = sw_last_chunk_extension_almost_done;
2045  break;
2046  case LF:
2047  state = sw_trailer;
2048  break;
2049  case ';':
2050  case ' ':
2051  case '\t':
2052  state = sw_last_chunk_extension;
2053  break;
2054  default:
2055  goto invalid;
2056  }
2057 
2058  break;
2059  }
2060 
2061  switch (ch) {
2062  case CR:
2063  state = sw_chunk_extension_almost_done;
2064  break;
2065  case LF:
2066  state = sw_chunk_data;
2067  break;
2068  case ';':
2069  case ' ':
2070  case '\t':
2071  state = sw_chunk_extension;
2072  break;
2073  default:
2074  goto invalid;
2075  }
2076 
2077  break;
2078 
2079  case sw_chunk_extension:
2080  switch (ch) {
2081  case CR:
2082  state = sw_chunk_extension_almost_done;
2083  break;
2084  case LF:
2085  state = sw_chunk_data;
2086  }
2087  break;
2088 
2089  case sw_chunk_extension_almost_done:
2090  if (ch == LF) {
2091  state = sw_chunk_data;
2092  break;
2093  }
2094  goto invalid;
2095 
2096  case sw_chunk_data:
2097  rc = NGX_OK;
2098  goto data;
2099 
2100  case sw_after_data:
2101  switch (ch) {
2102  case CR:
2103  state = sw_after_data_almost_done;
2104  break;
2105  case LF:
2106  state = sw_chunk_start;
2107  }
2108  break;
2109 
2110  case sw_after_data_almost_done:
2111  if (ch == LF) {
2112  state = sw_chunk_start;
2113  break;
2114  }
2115  goto invalid;
2116 
2117  case sw_last_chunk_extension:
2118  switch (ch) {
2119  case CR:
2120  state = sw_last_chunk_extension_almost_done;
2121  break;
2122  case LF:
2123  state = sw_trailer;
2124  }
2125  break;
2126 
2127  case sw_last_chunk_extension_almost_done:
2128  if (ch == LF) {
2129  state = sw_trailer;
2130  break;
2131  }
2132  goto invalid;
2133 
2134  case sw_trailer:
2135  switch (ch) {
2136  case CR:
2137  state = sw_trailer_almost_done;
2138  break;
2139  case LF:
2140  goto done;
2141  default:
2142  state = sw_trailer_header;
2143  }
2144  break;
2145 
2146  case sw_trailer_almost_done:
2147  if (ch == LF) {
2148  goto done;
2149  }
2150  goto invalid;
2151 
2152  case sw_trailer_header:
2153  switch (ch) {
2154  case CR:
2155  state = sw_trailer_header_almost_done;
2156  break;
2157  case LF:
2158  state = sw_trailer;
2159  }
2160  break;
2161 
2162  case sw_trailer_header_almost_done:
2163  if (ch == LF) {
2164  state = sw_trailer;
2165  break;
2166  }
2167  goto invalid;
2168 
2169  }
2170  }
2171 
2172 data:
2173 
2174  ctx->state = state;
2175  b->pos = pos;
2176 
2177  switch (state) {
2178 
2179  case sw_chunk_start:
2180  ctx->length = 3 /* "0" LF LF */;
2181  break;
2182  case sw_chunk_size:
2183  ctx->length = 2 /* LF LF */
2184  + (ctx->size ? ctx->size + 4 /* LF "0" LF LF */ : 0);
2185  break;
2186  case sw_chunk_extension:
2187  case sw_chunk_extension_almost_done:
2188  ctx->length = 1 /* LF */ + ctx->size + 4 /* LF "0" LF LF */;
2189  break;
2190  case sw_chunk_data:
2191  ctx->length = ctx->size + 4 /* LF "0" LF LF */;
2192  break;
2193  case sw_after_data:
2194  case sw_after_data_almost_done:
2195  ctx->length = 4 /* LF "0" LF LF */;
2196  break;
2197  case sw_last_chunk_extension:
2198  case sw_last_chunk_extension_almost_done:
2199  ctx->length = 2 /* LF LF */;
2200  break;
2201  case sw_trailer:
2202  case sw_trailer_almost_done:
2203  ctx->length = 1 /* LF */;
2204  break;
2205  case sw_trailer_header:
2206  case sw_trailer_header_almost_done:
2207  ctx->length = 2 /* LF LF */;
2208  break;
2209 
2210  }
2211 
2212  if (ctx->size < 0 || ctx->length < 0) {
2213  goto invalid;
2214  }
2215 
2216  return rc;
2217 
2218 done:
2219 
2220  ctx->state = 0;
2221  b->pos = pos + 1;
2222 
2223  return NGX_DONE;
2224 
2225 invalid:
2226 
2227  return NGX_ERROR;
2228 }