xref: /PHP-5.6/sapi/cli/php_http_parser.c (revision 2d4ad66f)
1 /* Copyright 2009,2010 Ryan Dahl <ry@tinyclouds.org>
2  *
3  * Permission is hereby granted, free of charge, to any person obtaining a copy
4  * of this software and associated documentation files (the "Software"), to
5  * deal in the Software without restriction, including without limitation the
6  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7  * sell copies of the Software, and to permit persons to whom the Software is
8  * furnished to do so, subject to the following conditions:
9  *
10  * The above copyright notice and this permission notice shall be included in
11  * all copies or substantial portions of the Software.
12  *
13  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
19  * IN THE SOFTWARE.
20  */
21 #include <assert.h>
22 #include <stddef.h>
23 #include "php_http_parser.h"
24 
25 
26 #ifndef MIN
27 # define MIN(a,b) ((a) < (b) ? (a) : (b))
28 #endif
29 
30 
31 #define CALLBACK2(FOR)                                               \
32 do {                                                                 \
33   if (settings->on_##FOR) {                                          \
34     if (0 != settings->on_##FOR(parser)) return (p - data);          \
35   }                                                                  \
36 } while (0)
37 
38 
39 #define MARK(FOR)                                                    \
40 do {                                                                 \
41   FOR##_mark = p;                                                    \
42 } while (0)
43 
44 #define CALLBACK_NOCLEAR(FOR)                                        \
45 do {                                                                 \
46   if (FOR##_mark) {                                                  \
47     if (settings->on_##FOR) {                                        \
48       if (0 != settings->on_##FOR(parser,                            \
49                                  FOR##_mark,                         \
50                                  p - FOR##_mark))                    \
51       {                                                              \
52         return (p - data);                                           \
53       }                                                              \
54     }                                                                \
55   }                                                                  \
56 } while (0)
57 
58 #ifdef PHP_WIN32
59 # undef CALLBACK
60 #endif
61 #define CALLBACK(FOR)                                                \
62 do {                                                                 \
63   CALLBACK_NOCLEAR(FOR);                                             \
64   FOR##_mark = NULL;                                                 \
65 } while (0)
66 
67 
68 #define PROXY_CONNECTION "proxy-connection"
69 #define CONNECTION "connection"
70 #define CONTENT_LENGTH "content-length"
71 #define TRANSFER_ENCODING "transfer-encoding"
72 #define UPGRADE "upgrade"
73 #define CHUNKED "chunked"
74 #define KEEP_ALIVE "keep-alive"
75 #define CLOSE "close"
76 
77 
78 static const char *method_strings[] =
79   { "DELETE"
80   , "GET"
81   , "HEAD"
82   , "POST"
83   , "PUT"
84   , "PATCH"
85   , "CONNECT"
86   , "OPTIONS"
87   , "TRACE"
88   , "COPY"
89   , "LOCK"
90   , "MKCOL"
91   , "MOVE"
92   , "MKCALENDAR"
93   , "PROPFIND"
94   , "PROPPATCH"
95   , "UNLOCK"
96   , "REPORT"
97   , "MKACTIVITY"
98   , "CHECKOUT"
99   , "MERGE"
100   , "M-SEARCH"
101   , "NOTIFY"
102   , "SUBSCRIBE"
103   , "UNSUBSCRIBE"
104   , "NOTIMPLEMENTED"
105   };
106 
107 
108 /* Tokens as defined by rfc 2616. Also lowercases them.
109  *        token       = 1*<any CHAR except CTLs or separators>
110  *     separators     = "(" | ")" | "<" | ">" | "@"
111  *                    | "," | ";" | ":" | "\" | <">
112  *                    | "/" | "[" | "]" | "?" | "="
113  *                    | "{" | "}" | SP | HT
114  */
115 static const char tokens[256] = {
116 /*   0 nul    1 soh    2 stx    3 etx    4 eot    5 enq    6 ack    7 bel  */
117         0,       0,       0,       0,       0,       0,       0,       0,
118 /*   8 bs     9 ht    10 nl    11 vt    12 np    13 cr    14 so    15 si   */
119         0,       0,       0,       0,       0,       0,       0,       0,
120 /*  16 dle   17 dc1   18 dc2   19 dc3   20 dc4   21 nak   22 syn   23 etb */
121         0,       0,       0,       0,       0,       0,       0,       0,
122 /*  24 can   25 em    26 sub   27 esc   28 fs    29 gs    30 rs    31 us  */
123         0,       0,       0,       0,       0,       0,       0,       0,
124 /*  32 sp    33  !    34  "    35  #    36  $    37  %    38  &    39  '  */
125        ' ',      '!',     '"',     '#',     '$',     '%',     '&',    '\'',
126 /*  40  (    41  )    42  *    43  +    44  ,    45  -    46  .    47  /  */
127         0,       0,      '*',     '+',      0,      '-',     '.',     '/',
128 /*  48  0    49  1    50  2    51  3    52  4    53  5    54  6    55  7  */
129        '0',     '1',     '2',     '3',     '4',     '5',     '6',     '7',
130 /*  56  8    57  9    58  :    59  ;    60  <    61  =    62  >    63  ?  */
131        '8',     '9',      0,       0,       0,       0,       0,       0,
132 /*  64  @    65  A    66  B    67  C    68  D    69  E    70  F    71  G  */
133         0,      'a',     'b',     'c',     'd',     'e',     'f',     'g',
134 /*  72  H    73  I    74  J    75  K    76  L    77  M    78  N    79  O  */
135        'h',     'i',     'j',     'k',     'l',     'm',     'n',     'o',
136 /*  80  P    81  Q    82  R    83  S    84  T    85  U    86  V    87  W  */
137        'p',     'q',     'r',     's',     't',     'u',     'v',     'w',
138 /*  88  X    89  Y    90  Z    91  [    92  \    93  ]    94  ^    95  _  */
139        'x',     'y',     'z',      0,       0,       0,      '^',     '_',
140 /*  96  `    97  a    98  b    99  c   100  d   101  e   102  f   103  g  */
141        '`',     'a',     'b',     'c',     'd',     'e',     'f',     'g',
142 /* 104  h   105  i   106  j   107  k   108  l   109  m   110  n   111  o  */
143        'h',     'i',     'j',     'k',     'l',     'm',     'n',     'o',
144 /* 112  p   113  q   114  r   115  s   116  t   117  u   118  v   119  w  */
145        'p',     'q',     'r',     's',     't',     'u',     'v',     'w',
146 /* 120  x   121  y   122  z   123  {   124  |   125  }   126  ~   127 del */
147        'x',     'y',     'z',      0,      '|',     '}',     '~',       0 };
148 
149 
150 static const int8_t unhex[256] =
151   {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
152   ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
153   ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
154   , 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1
155   ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1
156   ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
157   ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1
158   ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
159   };
160 
161 
162 static const uint8_t normal_url_char[256] = {
163 /*   0 nul    1 soh    2 stx    3 etx    4 eot    5 enq    6 ack    7 bel  */
164         0,       0,       0,       0,       0,       0,       0,       0,
165 /*   8 bs     9 ht    10 nl    11 vt    12 np    13 cr    14 so    15 si   */
166         0,       0,       0,       0,       0,       0,       0,       0,
167 /*  16 dle   17 dc1   18 dc2   19 dc3   20 dc4   21 nak   22 syn   23 etb */
168         0,       0,       0,       0,       0,       0,       0,       0,
169 /*  24 can   25 em    26 sub   27 esc   28 fs    29 gs    30 rs    31 us  */
170         0,       0,       0,       0,       0,       0,       0,       0,
171 /*  32 sp    33  !    34  "    35  #    36  $    37  %    38  &    39  '  */
172         0,       1,       1,       0,       1,       1,       1,       1,
173 /*  40  (    41  )    42  *    43  +    44  ,    45  -    46  .    47  /  */
174         1,       1,       1,       1,       1,       1,       1,       1,
175 /*  48  0    49  1    50  2    51  3    52  4    53  5    54  6    55  7  */
176         1,       1,       1,       1,       1,       1,       1,       1,
177 /*  56  8    57  9    58  :    59  ;    60  <    61  =    62  >    63  ?  */
178         1,       1,       1,       1,       1,       1,       1,       0,
179 /*  64  @    65  A    66  B    67  C    68  D    69  E    70  F    71  G  */
180         1,       1,       1,       1,       1,       1,       1,       1,
181 /*  72  H    73  I    74  J    75  K    76  L    77  M    78  N    79  O  */
182         1,       1,       1,       1,       1,       1,       1,       1,
183 /*  80  P    81  Q    82  R    83  S    84  T    85  U    86  V    87  W  */
184         1,       1,       1,       1,       1,       1,       1,       1,
185 /*  88  X    89  Y    90  Z    91  [    92  \    93  ]    94  ^    95  _  */
186         1,       1,       1,       1,       1,       1,       1,       1,
187 /*  96  `    97  a    98  b    99  c   100  d   101  e   102  f   103  g  */
188         1,       1,       1,       1,       1,       1,       1,       1,
189 /* 104  h   105  i   106  j   107  k   108  l   109  m   110  n   111  o  */
190         1,       1,       1,       1,       1,       1,       1,       1,
191 /* 112  p   113  q   114  r   115  s   116  t   117  u   118  v   119  w  */
192         1,       1,       1,       1,       1,       1,       1,       1,
193 /* 120  x   121  y   122  z   123  {   124  |   125  }   126  ~   127 del */
194         1,       1,       1,       1,       1,       1,       1,       0 };
195 
196 
197 enum state
198   { s_dead = 1 /* important that this is > 0 */
199 
200   , s_start_req_or_res
201   , s_res_or_resp_H
202   , s_start_res
203   , s_res_H
204   , s_res_HT
205   , s_res_HTT
206   , s_res_HTTP
207   , s_res_first_http_major
208   , s_res_http_major
209   , s_res_first_http_minor
210   , s_res_http_minor
211   , s_res_first_status_code
212   , s_res_status_code
213   , s_res_status
214   , s_res_line_almost_done
215 
216   , s_start_req
217 
218   , s_req_method
219   , s_req_spaces_before_url
220   , s_req_schema
221   , s_req_schema_slash
222   , s_req_schema_slash_slash
223   , s_req_host
224   , s_req_port
225   , s_req_path
226   , s_req_query_string_start
227   , s_req_query_string
228   , s_req_fragment_start
229   , s_req_fragment
230   , s_req_http_start
231   , s_req_http_H
232   , s_req_http_HT
233   , s_req_http_HTT
234   , s_req_http_HTTP
235   , s_req_first_http_major
236   , s_req_http_major
237   , s_req_first_http_minor
238   , s_req_http_minor
239   , s_req_line_almost_done
240 
241   , s_header_field_start
242   , s_header_field
243   , s_header_value_start
244   , s_header_value
245 
246   , s_header_almost_done
247 
248   , s_headers_almost_done
249   /* Important: 's_headers_almost_done' must be the last 'header' state. All
250    * states beyond this must be 'body' states. It is used for overflow
251    * checking. See the PARSING_HEADER() macro.
252    */
253   , s_chunk_size_start
254   , s_chunk_size
255   , s_chunk_size_almost_done
256   , s_chunk_parameters
257   , s_chunk_data
258   , s_chunk_data_almost_done
259   , s_chunk_data_done
260 
261   , s_body_identity
262   , s_body_identity_eof
263   };
264 
265 
266 #define PARSING_HEADER(state) (state <= s_headers_almost_done && 0 == (parser->flags & F_TRAILING))
267 
268 
269 enum header_states
270   { h_general = 0
271   , h_C
272   , h_CO
273   , h_CON
274 
275   , h_matching_connection
276   , h_matching_proxy_connection
277   , h_matching_content_length
278   , h_matching_transfer_encoding
279   , h_matching_upgrade
280 
281   , h_connection
282   , h_content_length
283   , h_transfer_encoding
284   , h_upgrade
285 
286   , h_matching_transfer_encoding_chunked
287   , h_matching_connection_keep_alive
288   , h_matching_connection_close
289 
290   , h_transfer_encoding_chunked
291   , h_connection_keep_alive
292   , h_connection_close
293   };
294 
295 
296 enum flags
297   { F_CHUNKED               = 1 << 0
298   , F_CONNECTION_KEEP_ALIVE = 1 << 1
299   , F_CONNECTION_CLOSE      = 1 << 2
300   , F_TRAILING              = 1 << 3
301   , F_UPGRADE               = 1 << 4
302   , F_SKIPBODY              = 1 << 5
303   };
304 
305 
306 #define CR '\r'
307 #define LF '\n'
308 #define LOWER(c) (unsigned char)(c | 0x20)
309 #define TOKEN(c) tokens[(unsigned char)c]
310 
311 
312 #define start_state (parser->type == PHP_HTTP_REQUEST ? s_start_req : s_start_res)
313 
314 
315 #if HTTP_PARSER_STRICT
316 # define STRICT_CHECK(cond) if (cond) goto error
317 # define NEW_MESSAGE() (http_should_keep_alive(parser) ? start_state : s_dead)
318 #else
319 # define STRICT_CHECK(cond)
320 # define NEW_MESSAGE() start_state
321 #endif
322 
323 
php_http_parser_execute(php_http_parser * parser,const php_http_parser_settings * settings,const char * data,size_t len)324 size_t php_http_parser_execute (php_http_parser *parser,
325                             const php_http_parser_settings *settings,
326                             const char *data,
327                             size_t len)
328 {
329   char c, ch;
330   const char *p = data, *pe;
331   size_t to_read;
332 
333   enum state state = (enum state) parser->state;
334   enum header_states header_state = (enum header_states) parser->header_state;
335   uint32_t index = parser->index;
336   uint32_t nread = parser->nread;
337 
338   /* technically we could combine all of these (except for url_mark) into one
339      variable, saving stack space, but it seems more clear to have them
340      separated. */
341   const char *header_field_mark = 0;
342   const char *header_value_mark = 0;
343   const char *fragment_mark = 0;
344   const char *query_string_mark = 0;
345   const char *path_mark = 0;
346   const char *url_mark = 0;
347 
348   if (len == 0) {
349     if (state == s_body_identity_eof) {
350       CALLBACK2(message_complete);
351     }
352     return 0;
353   }
354 
355   if (state == s_header_field)
356     header_field_mark = data;
357   if (state == s_header_value)
358     header_value_mark = data;
359   if (state == s_req_fragment)
360     fragment_mark = data;
361   if (state == s_req_query_string)
362     query_string_mark = data;
363   if (state == s_req_path)
364     path_mark = data;
365   if (state == s_req_path || state == s_req_schema || state == s_req_schema_slash
366       || state == s_req_schema_slash_slash || state == s_req_port
367       || state == s_req_query_string_start || state == s_req_query_string
368       || state == s_req_host
369       || state == s_req_fragment_start || state == s_req_fragment)
370     url_mark = data;
371 
372   for (p=data, pe=data+len; p != pe; p++) {
373     ch = *p;
374 
375     if (PARSING_HEADER(state)) {
376       ++nread;
377       /* Buffer overflow attack */
378       if (nread > PHP_HTTP_MAX_HEADER_SIZE) goto error;
379     }
380 
381     switch (state) {
382 
383       case s_dead:
384         /* this state is used after a 'Connection: close' message
385          * the parser will error out if it reads another message
386          */
387         goto error;
388 
389       case s_start_req_or_res:
390       {
391         if (ch == CR || ch == LF)
392           break;
393         parser->flags = 0;
394         parser->content_length = -1;
395 
396         CALLBACK2(message_begin);
397 
398         if (ch == 'H')
399           state = s_res_or_resp_H;
400         else {
401           parser->type = PHP_HTTP_REQUEST;
402           goto start_req_method_assign;
403         }
404         break;
405       }
406 
407       case s_res_or_resp_H:
408         if (ch == 'T') {
409           parser->type = PHP_HTTP_RESPONSE;
410           state = s_res_HT;
411         } else {
412           if (ch != 'E') goto error;
413           parser->type = PHP_HTTP_REQUEST;
414           parser->method = PHP_HTTP_HEAD;
415           index = 2;
416           state = s_req_method;
417         }
418         break;
419 
420       case s_start_res:
421       {
422         parser->flags = 0;
423         parser->content_length = -1;
424 
425         CALLBACK2(message_begin);
426 
427         switch (ch) {
428           case 'H':
429             state = s_res_H;
430             break;
431 
432           case CR:
433           case LF:
434             break;
435 
436           default:
437             goto error;
438         }
439         break;
440       }
441 
442       case s_res_H:
443         STRICT_CHECK(ch != 'T');
444         state = s_res_HT;
445         break;
446 
447       case s_res_HT:
448         STRICT_CHECK(ch != 'T');
449         state = s_res_HTT;
450         break;
451 
452       case s_res_HTT:
453         STRICT_CHECK(ch != 'P');
454         state = s_res_HTTP;
455         break;
456 
457       case s_res_HTTP:
458         STRICT_CHECK(ch != '/');
459         state = s_res_first_http_major;
460         break;
461 
462       case s_res_first_http_major:
463         if (ch < '1' || ch > '9') goto error;
464         parser->http_major = ch - '0';
465         state = s_res_http_major;
466         break;
467 
468       /* major HTTP version or dot */
469       case s_res_http_major:
470       {
471         if (ch == '.') {
472           state = s_res_first_http_minor;
473           break;
474         }
475 
476         if (ch < '0' || ch > '9') goto error;
477 
478         parser->http_major *= 10;
479         parser->http_major += ch - '0';
480 
481         if (parser->http_major > 999) goto error;
482         break;
483       }
484 
485       /* first digit of minor HTTP version */
486       case s_res_first_http_minor:
487         if (ch < '0' || ch > '9') goto error;
488         parser->http_minor = ch - '0';
489         state = s_res_http_minor;
490         break;
491 
492       /* minor HTTP version or end of request line */
493       case s_res_http_minor:
494       {
495         if (ch == ' ') {
496           state = s_res_first_status_code;
497           break;
498         }
499 
500         if (ch < '0' || ch > '9') goto error;
501 
502         parser->http_minor *= 10;
503         parser->http_minor += ch - '0';
504 
505         if (parser->http_minor > 999) goto error;
506         break;
507       }
508 
509       case s_res_first_status_code:
510       {
511         if (ch < '0' || ch > '9') {
512           if (ch == ' ') {
513             break;
514           }
515           goto error;
516         }
517         parser->status_code = ch - '0';
518         state = s_res_status_code;
519         break;
520       }
521 
522       case s_res_status_code:
523       {
524         if (ch < '0' || ch > '9') {
525           switch (ch) {
526             case ' ':
527               state = s_res_status;
528               break;
529             case CR:
530               state = s_res_line_almost_done;
531               break;
532             case LF:
533               state = s_header_field_start;
534               break;
535             default:
536               goto error;
537           }
538           break;
539         }
540 
541         parser->status_code *= 10;
542         parser->status_code += ch - '0';
543 
544         if (parser->status_code > 999) goto error;
545         break;
546       }
547 
548       case s_res_status:
549         /* the human readable status. e.g. "NOT FOUND"
550          * we are not humans so just ignore this */
551         if (ch == CR) {
552           state = s_res_line_almost_done;
553           break;
554         }
555 
556         if (ch == LF) {
557           state = s_header_field_start;
558           break;
559         }
560         break;
561 
562       case s_res_line_almost_done:
563         STRICT_CHECK(ch != LF);
564         state = s_header_field_start;
565         break;
566 
567       case s_start_req:
568       {
569         if (ch == CR || ch == LF)
570           break;
571         parser->flags = 0;
572         parser->content_length = -1;
573 
574         CALLBACK2(message_begin);
575 
576         if (ch < 'A' || 'Z' < ch) goto error;
577 
578       start_req_method_assign:
579         parser->method = (enum php_http_method) 0;
580         index = 1;
581         switch (ch) {
582           case 'C': parser->method = PHP_HTTP_CONNECT; /* or COPY, CHECKOUT */ break;
583           case 'D': parser->method = PHP_HTTP_DELETE; break;
584           case 'G': parser->method = PHP_HTTP_GET; break;
585           case 'H': parser->method = PHP_HTTP_HEAD; break;
586           case 'L': parser->method = PHP_HTTP_LOCK; break;
587           case 'M': parser->method = PHP_HTTP_MKCOL; /* or MOVE, MKCALENDAR, MKACTIVITY, MERGE, M-SEARCH */ break;
588           case 'N': parser->method = PHP_HTTP_NOTIFY; break;
589           case 'O': parser->method = PHP_HTTP_OPTIONS; break;
590           case 'P': parser->method = PHP_HTTP_POST; /* or PROPFIND or PROPPATCH or PUT */ break;
591           case 'R': parser->method = PHP_HTTP_REPORT; break;
592           case 'S': parser->method = PHP_HTTP_SUBSCRIBE; break;
593           case 'T': parser->method = PHP_HTTP_TRACE; break;
594           case 'U': parser->method = PHP_HTTP_UNLOCK; /* or UNSUBSCRIBE */ break;
595           default: parser->method = PHP_HTTP_NOT_IMPLEMENTED; break;
596         }
597         state = s_req_method;
598         break;
599       }
600 
601       case s_req_method:
602       {
603         const char *matcher;
604         if (ch == '\0')
605           goto error;
606 
607         matcher = method_strings[parser->method];
608         if (ch == ' ') {
609           if (parser->method != PHP_HTTP_NOT_IMPLEMENTED && matcher[index] != '\0') {
610             parser->method = PHP_HTTP_NOT_IMPLEMENTED;
611           }
612           state = s_req_spaces_before_url;
613         } else if (parser->method == PHP_HTTP_NOT_IMPLEMENTED || ch == matcher[index]) {
614           ; /* nada */
615         } else if (parser->method == PHP_HTTP_CONNECT) {
616           if (index == 1 && ch == 'H') {
617             parser->method = PHP_HTTP_CHECKOUT;
618           } else if (index == 2  && ch == 'P') {
619             parser->method = PHP_HTTP_COPY;
620           } else {
621             parser->method = PHP_HTTP_NOT_IMPLEMENTED;
622           }
623         } else if (parser->method == PHP_HTTP_MKCOL) {
624           if (index == 1 && ch == 'O') {
625             parser->method = PHP_HTTP_MOVE;
626           } else if (index == 3 && ch == 'A') {
627             parser->method = PHP_HTTP_MKCALENDAR;
628           } else if (index == 1 && ch == 'E') {
629             parser->method = PHP_HTTP_MERGE;
630           } else if (index == 1 && ch == '-') {
631             parser->method = PHP_HTTP_MSEARCH;
632           } else if (index == 2 && ch == 'A') {
633             parser->method = PHP_HTTP_MKACTIVITY;
634           } else {
635             parser->method = PHP_HTTP_NOT_IMPLEMENTED;
636           }
637         } else if (index == 1 && parser->method == PHP_HTTP_POST && ch == 'R') {
638           parser->method = PHP_HTTP_PROPFIND; /* or HTTP_PROPPATCH */
639         } else if (index == 1 && parser->method == PHP_HTTP_POST && ch == 'U') {
640           parser->method = PHP_HTTP_PUT;
641         } else if (index == 1 && parser->method == PHP_HTTP_POST && ch == 'A') {
642           parser->method = PHP_HTTP_PATCH;
643         } else if (index == 2 && parser->method == PHP_HTTP_UNLOCK && ch == 'S') {
644           parser->method = PHP_HTTP_UNSUBSCRIBE;
645         } else if (index == 4 && parser->method == PHP_HTTP_PROPFIND && ch == 'P') {
646           parser->method = PHP_HTTP_PROPPATCH;
647         } else {
648           parser->method = PHP_HTTP_NOT_IMPLEMENTED;
649         }
650 
651         ++index;
652         break;
653       }
654       case s_req_spaces_before_url:
655       {
656         if (ch == ' ') break;
657 
658         if (ch == '/' || ch == '*') {
659           MARK(url);
660           MARK(path);
661           state = s_req_path;
662           break;
663         }
664 
665         c = LOWER(ch);
666 
667         if (c >= 'a' && c <= 'z') {
668           MARK(url);
669           state = s_req_schema;
670           break;
671         }
672 
673         goto error;
674       }
675 
676       case s_req_schema:
677       {
678         c = LOWER(ch);
679 
680         if (c >= 'a' && c <= 'z') break;
681 
682         if (ch == ':') {
683           state = s_req_schema_slash;
684           break;
685         } else if (ch == '.') {
686           state = s_req_host;
687           break;
688         } else if ('0' <= ch && ch <= '9') {
689           state = s_req_host;
690           break;
691         }
692 
693         goto error;
694       }
695 
696       case s_req_schema_slash:
697         STRICT_CHECK(ch != '/');
698         state = s_req_schema_slash_slash;
699         break;
700 
701       case s_req_schema_slash_slash:
702         STRICT_CHECK(ch != '/');
703         state = s_req_host;
704         break;
705 
706       case s_req_host:
707       {
708         c = LOWER(ch);
709         if (c >= 'a' && c <= 'z') break;
710         if ((ch >= '0' && ch <= '9') || ch == '.' || ch == '-') break;
711         switch (ch) {
712           case ':':
713             state = s_req_port;
714             break;
715           case '/':
716             MARK(path);
717             state = s_req_path;
718             break;
719           case ' ':
720             /* The request line looks like:
721              *   "GET http://foo.bar.com HTTP/1.1"
722              * That is, there is no path.
723              */
724             CALLBACK(url);
725             state = s_req_http_start;
726             break;
727           default:
728             goto error;
729         }
730         break;
731       }
732 
733       case s_req_port:
734       {
735         if (ch >= '0' && ch <= '9') break;
736         switch (ch) {
737           case '/':
738             MARK(path);
739             state = s_req_path;
740             break;
741           case ' ':
742             /* The request line looks like:
743              *   "GET http://foo.bar.com:1234 HTTP/1.1"
744              * That is, there is no path.
745              */
746             CALLBACK(url);
747             state = s_req_http_start;
748             break;
749           default:
750             goto error;
751         }
752         break;
753       }
754 
755       case s_req_path:
756       {
757         if (normal_url_char[(unsigned char)ch]) break;
758 
759         switch (ch) {
760           case ' ':
761             CALLBACK(url);
762             CALLBACK(path);
763             state = s_req_http_start;
764             break;
765           case CR:
766             CALLBACK(url);
767             CALLBACK(path);
768             parser->http_major = 0;
769             parser->http_minor = 9;
770             state = s_req_line_almost_done;
771             break;
772           case LF:
773             CALLBACK(url);
774             CALLBACK(path);
775             parser->http_major = 0;
776             parser->http_minor = 9;
777             state = s_header_field_start;
778             break;
779           case '?':
780             CALLBACK(path);
781             state = s_req_query_string_start;
782             break;
783           case '#':
784             CALLBACK(path);
785             state = s_req_fragment_start;
786             break;
787           default:
788             goto error;
789         }
790         break;
791       }
792 
793       case s_req_query_string_start:
794       {
795         if (normal_url_char[(unsigned char)ch]) {
796           MARK(query_string);
797           state = s_req_query_string;
798           break;
799         }
800 
801         switch (ch) {
802           case '?':
803             break; /* XXX ignore extra '?' ... is this right? */
804           case ' ':
805             CALLBACK(url);
806             state = s_req_http_start;
807             break;
808           case CR:
809             CALLBACK(url);
810             parser->http_major = 0;
811             parser->http_minor = 9;
812             state = s_req_line_almost_done;
813             break;
814           case LF:
815             CALLBACK(url);
816             parser->http_major = 0;
817             parser->http_minor = 9;
818             state = s_header_field_start;
819             break;
820           case '#':
821             state = s_req_fragment_start;
822             break;
823           default:
824             goto error;
825         }
826         break;
827       }
828 
829       case s_req_query_string:
830       {
831         if (normal_url_char[(unsigned char)ch]) break;
832 
833         switch (ch) {
834           case '?':
835             /* allow extra '?' in query string */
836             break;
837           case ' ':
838             CALLBACK(url);
839             CALLBACK(query_string);
840             state = s_req_http_start;
841             break;
842           case CR:
843             CALLBACK(url);
844             CALLBACK(query_string);
845             parser->http_major = 0;
846             parser->http_minor = 9;
847             state = s_req_line_almost_done;
848             break;
849           case LF:
850             CALLBACK(url);
851             CALLBACK(query_string);
852             parser->http_major = 0;
853             parser->http_minor = 9;
854             state = s_header_field_start;
855             break;
856           case '#':
857             CALLBACK(query_string);
858             state = s_req_fragment_start;
859             break;
860           default:
861             goto error;
862         }
863         break;
864       }
865 
866       case s_req_fragment_start:
867       {
868         if (normal_url_char[(unsigned char)ch]) {
869           MARK(fragment);
870           state = s_req_fragment;
871           break;
872         }
873 
874         switch (ch) {
875           case ' ':
876             CALLBACK(url);
877             state = s_req_http_start;
878             break;
879           case CR:
880             CALLBACK(url);
881             parser->http_major = 0;
882             parser->http_minor = 9;
883             state = s_req_line_almost_done;
884             break;
885           case LF:
886             CALLBACK(url);
887             parser->http_major = 0;
888             parser->http_minor = 9;
889             state = s_header_field_start;
890             break;
891           case '?':
892             MARK(fragment);
893             state = s_req_fragment;
894             break;
895           case '#':
896             break;
897           default:
898             goto error;
899         }
900         break;
901       }
902 
903       case s_req_fragment:
904       {
905         if (normal_url_char[(unsigned char)ch]) break;
906 
907         switch (ch) {
908           case ' ':
909             CALLBACK(url);
910             CALLBACK(fragment);
911             state = s_req_http_start;
912             break;
913           case CR:
914             CALLBACK(url);
915             CALLBACK(fragment);
916             parser->http_major = 0;
917             parser->http_minor = 9;
918             state = s_req_line_almost_done;
919             break;
920           case LF:
921             CALLBACK(url);
922             CALLBACK(fragment);
923             parser->http_major = 0;
924             parser->http_minor = 9;
925             state = s_header_field_start;
926             break;
927           case '?':
928           case '#':
929             break;
930           default:
931             goto error;
932         }
933         break;
934       }
935 
936       case s_req_http_start:
937         switch (ch) {
938           case 'H':
939             state = s_req_http_H;
940             break;
941           case ' ':
942             break;
943           default:
944             goto error;
945         }
946         break;
947 
948       case s_req_http_H:
949         STRICT_CHECK(ch != 'T');
950         state = s_req_http_HT;
951         break;
952 
953       case s_req_http_HT:
954         STRICT_CHECK(ch != 'T');
955         state = s_req_http_HTT;
956         break;
957 
958       case s_req_http_HTT:
959         STRICT_CHECK(ch != 'P');
960         state = s_req_http_HTTP;
961         break;
962 
963       case s_req_http_HTTP:
964         STRICT_CHECK(ch != '/');
965         state = s_req_first_http_major;
966         break;
967 
968       /* first digit of major HTTP version */
969       case s_req_first_http_major:
970         if (ch < '1' || ch > '9') goto error;
971         parser->http_major = ch - '0';
972         state = s_req_http_major;
973         break;
974 
975       /* major HTTP version or dot */
976       case s_req_http_major:
977       {
978         if (ch == '.') {
979           state = s_req_first_http_minor;
980           break;
981         }
982 
983         if (ch < '0' || ch > '9') goto error;
984 
985         parser->http_major *= 10;
986         parser->http_major += ch - '0';
987 
988         if (parser->http_major > 999) goto error;
989         break;
990       }
991 
992       /* first digit of minor HTTP version */
993       case s_req_first_http_minor:
994         if (ch < '0' || ch > '9') goto error;
995         parser->http_minor = ch - '0';
996         state = s_req_http_minor;
997         break;
998 
999       /* minor HTTP version or end of request line */
1000       case s_req_http_minor:
1001       {
1002         if (ch == CR) {
1003           state = s_req_line_almost_done;
1004           break;
1005         }
1006 
1007         if (ch == LF) {
1008           state = s_header_field_start;
1009           break;
1010         }
1011 
1012         /* XXX allow spaces after digit? */
1013 
1014         if (ch < '0' || ch > '9') goto error;
1015 
1016         parser->http_minor *= 10;
1017         parser->http_minor += ch - '0';
1018 
1019         if (parser->http_minor > 999) goto error;
1020         break;
1021       }
1022 
1023       /* end of request line */
1024       case s_req_line_almost_done:
1025       {
1026         if (ch != LF) goto error;
1027         state = s_header_field_start;
1028         break;
1029       }
1030 
1031       case s_header_field_start:
1032       {
1033         if (ch == CR) {
1034           state = s_headers_almost_done;
1035           break;
1036         }
1037 
1038         if (ch == LF) {
1039           /* they might be just sending \n instead of \r\n so this would be
1040            * the second \n to denote the end of headers*/
1041           state = s_headers_almost_done;
1042           goto headers_almost_done;
1043         }
1044 
1045         c = TOKEN(ch);
1046 
1047         if (!c) goto error;
1048 
1049         MARK(header_field);
1050 
1051         index = 0;
1052         state = s_header_field;
1053 
1054         switch (c) {
1055           case 'c':
1056             header_state = h_C;
1057             break;
1058 
1059           case 'p':
1060             header_state = h_matching_proxy_connection;
1061             break;
1062 
1063           case 't':
1064             header_state = h_matching_transfer_encoding;
1065             break;
1066 
1067           case 'u':
1068             header_state = h_matching_upgrade;
1069             break;
1070 
1071           default:
1072             header_state = h_general;
1073             break;
1074         }
1075         break;
1076       }
1077 
1078       case s_header_field:
1079       {
1080         c = TOKEN(ch);
1081 
1082         if (c) {
1083           switch (header_state) {
1084             case h_general:
1085               break;
1086 
1087             case h_C:
1088               index++;
1089               header_state = (c == 'o' ? h_CO : h_general);
1090               break;
1091 
1092             case h_CO:
1093               index++;
1094               header_state = (c == 'n' ? h_CON : h_general);
1095               break;
1096 
1097             case h_CON:
1098               index++;
1099               switch (c) {
1100                 case 'n':
1101                   header_state = h_matching_connection;
1102                   break;
1103                 case 't':
1104                   header_state = h_matching_content_length;
1105                   break;
1106                 default:
1107                   header_state = h_general;
1108                   break;
1109               }
1110               break;
1111 
1112             /* connection */
1113 
1114             case h_matching_connection:
1115               index++;
1116               if (index > sizeof(CONNECTION)-1
1117                   || c != CONNECTION[index]) {
1118                 header_state = h_general;
1119               } else if (index == sizeof(CONNECTION)-2) {
1120                 header_state = h_connection;
1121               }
1122               break;
1123 
1124             /* proxy-connection */
1125 
1126             case h_matching_proxy_connection:
1127               index++;
1128               if (index > sizeof(PROXY_CONNECTION)-1
1129                   || c != PROXY_CONNECTION[index]) {
1130                 header_state = h_general;
1131               } else if (index == sizeof(PROXY_CONNECTION)-2) {
1132                 header_state = h_connection;
1133               }
1134               break;
1135 
1136             /* content-length */
1137 
1138             case h_matching_content_length:
1139               index++;
1140               if (index > sizeof(CONTENT_LENGTH)-1
1141                   || c != CONTENT_LENGTH[index]) {
1142                 header_state = h_general;
1143               } else if (index == sizeof(CONTENT_LENGTH)-2) {
1144                 header_state = h_content_length;
1145               }
1146               break;
1147 
1148             /* transfer-encoding */
1149 
1150             case h_matching_transfer_encoding:
1151               index++;
1152               if (index > sizeof(TRANSFER_ENCODING)-1
1153                   || c != TRANSFER_ENCODING[index]) {
1154                 header_state = h_general;
1155               } else if (index == sizeof(TRANSFER_ENCODING)-2) {
1156                 header_state = h_transfer_encoding;
1157               }
1158               break;
1159 
1160             /* upgrade */
1161 
1162             case h_matching_upgrade:
1163               index++;
1164               if (index > sizeof(UPGRADE)-1
1165                   || c != UPGRADE[index]) {
1166                 header_state = h_general;
1167               } else if (index == sizeof(UPGRADE)-2) {
1168                 header_state = h_upgrade;
1169               }
1170               break;
1171 
1172             case h_connection:
1173             case h_content_length:
1174             case h_transfer_encoding:
1175             case h_upgrade:
1176               if (ch != ' ') header_state = h_general;
1177               break;
1178 
1179             default:
1180               assert(0 && "Unknown header_state");
1181               break;
1182           }
1183           break;
1184         }
1185 
1186         if (ch == ':') {
1187           CALLBACK(header_field);
1188           state = s_header_value_start;
1189           break;
1190         }
1191 
1192         if (ch == CR) {
1193           state = s_header_almost_done;
1194           CALLBACK(header_field);
1195           break;
1196         }
1197 
1198         if (ch == LF) {
1199           CALLBACK(header_field);
1200           state = s_header_field_start;
1201           break;
1202         }
1203 
1204         goto error;
1205       }
1206 
1207       case s_header_value_start:
1208       {
1209         if (ch == ' ') break;
1210 
1211         MARK(header_value);
1212 
1213         state = s_header_value;
1214         index = 0;
1215 
1216         c = LOWER(ch);
1217 
1218         if (ch == CR) {
1219           CALLBACK(header_value);
1220           header_state = h_general;
1221           state = s_header_almost_done;
1222           break;
1223         }
1224 
1225         if (ch == LF) {
1226           CALLBACK(header_value);
1227           state = s_header_field_start;
1228           break;
1229         }
1230 
1231         switch (header_state) {
1232           case h_upgrade:
1233             parser->flags |= F_UPGRADE;
1234             header_state = h_general;
1235             break;
1236 
1237           case h_transfer_encoding:
1238             /* looking for 'Transfer-Encoding: chunked' */
1239             if ('c' == c) {
1240               header_state = h_matching_transfer_encoding_chunked;
1241             } else {
1242               header_state = h_general;
1243             }
1244             break;
1245 
1246           case h_content_length:
1247             if (ch < '0' || ch > '9') goto error;
1248             parser->content_length = ch - '0';
1249             break;
1250 
1251           case h_connection:
1252             /* looking for 'Connection: keep-alive' */
1253             if (c == 'k') {
1254               header_state = h_matching_connection_keep_alive;
1255             /* looking for 'Connection: close' */
1256             } else if (c == 'c') {
1257               header_state = h_matching_connection_close;
1258             } else {
1259               header_state = h_general;
1260             }
1261             break;
1262 
1263           default:
1264             header_state = h_general;
1265             break;
1266         }
1267         break;
1268       }
1269 
1270       case s_header_value:
1271       {
1272         c = LOWER(ch);
1273 
1274         if (ch == CR) {
1275           CALLBACK(header_value);
1276           state = s_header_almost_done;
1277           break;
1278         }
1279 
1280         if (ch == LF) {
1281           CALLBACK(header_value);
1282           goto header_almost_done;
1283         }
1284 
1285         switch (header_state) {
1286           case h_general:
1287             break;
1288 
1289           case h_connection:
1290           case h_transfer_encoding:
1291             assert(0 && "Shouldn't get here.");
1292             break;
1293 
1294           case h_content_length:
1295             if (ch == ' ') break;
1296             if (ch < '0' || ch > '9') goto error;
1297             parser->content_length *= 10;
1298             parser->content_length += ch - '0';
1299             break;
1300 
1301           /* Transfer-Encoding: chunked */
1302           case h_matching_transfer_encoding_chunked:
1303             index++;
1304             if (index > sizeof(CHUNKED)-1
1305                 || c != CHUNKED[index]) {
1306               header_state = h_general;
1307             } else if (index == sizeof(CHUNKED)-2) {
1308               header_state = h_transfer_encoding_chunked;
1309             }
1310             break;
1311 
1312           /* looking for 'Connection: keep-alive' */
1313           case h_matching_connection_keep_alive:
1314             index++;
1315             if (index > sizeof(KEEP_ALIVE)-1
1316                 || c != KEEP_ALIVE[index]) {
1317               header_state = h_general;
1318             } else if (index == sizeof(KEEP_ALIVE)-2) {
1319               header_state = h_connection_keep_alive;
1320             }
1321             break;
1322 
1323           /* looking for 'Connection: close' */
1324           case h_matching_connection_close:
1325             index++;
1326             if (index > sizeof(CLOSE)-1 || c != CLOSE[index]) {
1327               header_state = h_general;
1328             } else if (index == sizeof(CLOSE)-2) {
1329               header_state = h_connection_close;
1330             }
1331             break;
1332 
1333           case h_transfer_encoding_chunked:
1334           case h_connection_keep_alive:
1335           case h_connection_close:
1336             if (ch != ' ') header_state = h_general;
1337             break;
1338 
1339           default:
1340             state = s_header_value;
1341             header_state = h_general;
1342             break;
1343         }
1344         break;
1345       }
1346 
1347       case s_header_almost_done:
1348       header_almost_done:
1349       {
1350         STRICT_CHECK(ch != LF);
1351 
1352         state = s_header_field_start;
1353 
1354         switch (header_state) {
1355           case h_connection_keep_alive:
1356             parser->flags |= F_CONNECTION_KEEP_ALIVE;
1357             break;
1358           case h_connection_close:
1359             parser->flags |= F_CONNECTION_CLOSE;
1360             break;
1361           case h_transfer_encoding_chunked:
1362             parser->flags |= F_CHUNKED;
1363             break;
1364           default:
1365             break;
1366         }
1367         break;
1368       }
1369 
1370       case s_headers_almost_done:
1371       headers_almost_done:
1372       {
1373         STRICT_CHECK(ch != LF);
1374 
1375         if (parser->flags & F_TRAILING) {
1376           /* End of a chunked request */
1377           CALLBACK2(message_complete);
1378           state = NEW_MESSAGE();
1379           break;
1380         }
1381 
1382         nread = 0;
1383 
1384         if (parser->flags & F_UPGRADE || parser->method == PHP_HTTP_CONNECT) {
1385           parser->upgrade = 1;
1386         }
1387 
1388         /* Here we call the headers_complete callback. This is somewhat
1389          * different than other callbacks because if the user returns 1, we
1390          * will interpret that as saying that this message has no body. This
1391          * is needed for the annoying case of receiving a response to a HEAD
1392          * request.
1393          */
1394         if (settings->on_headers_complete) {
1395           switch (settings->on_headers_complete(parser)) {
1396             case 0:
1397               break;
1398 
1399             case 1:
1400               parser->flags |= F_SKIPBODY;
1401               break;
1402 
1403             default:
1404               return p - data; /* Error */
1405           }
1406         }
1407 
1408         /* Exit, the rest of the connect is in a different protocol. */
1409         if (parser->upgrade) {
1410           CALLBACK2(message_complete);
1411           return (p - data);
1412         }
1413 
1414         if (parser->flags & F_SKIPBODY) {
1415           CALLBACK2(message_complete);
1416           state = NEW_MESSAGE();
1417         } else if (parser->flags & F_CHUNKED) {
1418           /* chunked encoding - ignore Content-Length header */
1419           state = s_chunk_size_start;
1420         } else {
1421           if (parser->content_length == 0) {
1422             /* Content-Length header given but zero: Content-Length: 0\r\n */
1423             CALLBACK2(message_complete);
1424             state = NEW_MESSAGE();
1425           } else if (parser->content_length > 0) {
1426             /* Content-Length header given and non-zero */
1427             state = s_body_identity;
1428           } else {
1429             if (parser->type == PHP_HTTP_REQUEST || php_http_should_keep_alive(parser)) {
1430               /* Assume content-length 0 - read the next */
1431               CALLBACK2(message_complete);
1432               state = NEW_MESSAGE();
1433             } else {
1434               /* Read body until EOF */
1435               state = s_body_identity_eof;
1436             }
1437           }
1438         }
1439 
1440         break;
1441       }
1442 
1443       case s_body_identity:
1444         to_read = MIN(pe - p, (size_t)parser->content_length);
1445         if (to_read > 0) {
1446           if (settings->on_body) settings->on_body(parser, p, to_read);
1447           p += to_read - 1;
1448           parser->content_length -= to_read;
1449           if (parser->content_length == 0) {
1450             CALLBACK2(message_complete);
1451             state = NEW_MESSAGE();
1452           }
1453         }
1454         break;
1455 
1456       /* read until EOF */
1457       case s_body_identity_eof:
1458         to_read = pe - p;
1459         if (to_read > 0) {
1460           if (settings->on_body) settings->on_body(parser, p, to_read);
1461           p += to_read - 1;
1462         }
1463         break;
1464 
1465       case s_chunk_size_start:
1466       {
1467         assert(parser->flags & F_CHUNKED);
1468 
1469         c = unhex[(unsigned char)ch];
1470         if (c == -1) goto error;
1471         parser->content_length = c;
1472         state = s_chunk_size;
1473         break;
1474       }
1475 
1476       case s_chunk_size:
1477       {
1478         assert(parser->flags & F_CHUNKED);
1479 
1480         if (ch == CR) {
1481           state = s_chunk_size_almost_done;
1482           break;
1483         }
1484 
1485         c = unhex[(unsigned char)ch];
1486 
1487         if (c == -1) {
1488           if (ch == ';' || ch == ' ') {
1489             state = s_chunk_parameters;
1490             break;
1491           }
1492           goto error;
1493         }
1494 
1495         parser->content_length *= 16;
1496         parser->content_length += c;
1497         break;
1498       }
1499 
1500       case s_chunk_parameters:
1501       {
1502         assert(parser->flags & F_CHUNKED);
1503         /* just ignore this shit. TODO check for overflow */
1504         if (ch == CR) {
1505           state = s_chunk_size_almost_done;
1506           break;
1507         }
1508         break;
1509       }
1510 
1511       case s_chunk_size_almost_done:
1512       {
1513         assert(parser->flags & F_CHUNKED);
1514         STRICT_CHECK(ch != LF);
1515 
1516         if (parser->content_length == 0) {
1517           parser->flags |= F_TRAILING;
1518           state = s_header_field_start;
1519         } else {
1520           state = s_chunk_data;
1521         }
1522         break;
1523       }
1524 
1525       case s_chunk_data:
1526       {
1527         assert(parser->flags & F_CHUNKED);
1528 
1529         to_read = MIN(pe - p, (size_t)(parser->content_length));
1530 
1531         if (to_read > 0) {
1532           if (settings->on_body) settings->on_body(parser, p, to_read);
1533           p += to_read - 1;
1534         }
1535 
1536         if (to_read == parser->content_length) {
1537           state = s_chunk_data_almost_done;
1538         }
1539 
1540         parser->content_length -= to_read;
1541         break;
1542       }
1543 
1544       case s_chunk_data_almost_done:
1545         assert(parser->flags & F_CHUNKED);
1546         STRICT_CHECK(ch != CR);
1547         state = s_chunk_data_done;
1548         break;
1549 
1550       case s_chunk_data_done:
1551         assert(parser->flags & F_CHUNKED);
1552         STRICT_CHECK(ch != LF);
1553         state = s_chunk_size_start;
1554         break;
1555 
1556       default:
1557         assert(0 && "unhandled state");
1558         goto error;
1559     }
1560   }
1561 
1562   CALLBACK_NOCLEAR(header_field);
1563   CALLBACK_NOCLEAR(header_value);
1564   CALLBACK_NOCLEAR(fragment);
1565   CALLBACK_NOCLEAR(query_string);
1566   CALLBACK_NOCLEAR(path);
1567   CALLBACK_NOCLEAR(url);
1568 
1569   parser->state = state;
1570   parser->header_state = header_state;
1571   parser->index = index;
1572   parser->nread = nread;
1573 
1574   return len;
1575 
1576 error:
1577   parser->state = s_dead;
1578   return (p - data);
1579 }
1580 
1581 
1582 int
php_http_should_keep_alive(php_http_parser * parser)1583 php_http_should_keep_alive (php_http_parser *parser)
1584 {
1585   if (parser->http_major > 0 && parser->http_minor > 0) {
1586     /* HTTP/1.1 */
1587     if (parser->flags & F_CONNECTION_CLOSE) {
1588       return 0;
1589     } else {
1590       return 1;
1591     }
1592   } else {
1593     /* HTTP/1.0 or earlier */
1594     if (parser->flags & F_CONNECTION_KEEP_ALIVE) {
1595       return 1;
1596     } else {
1597       return 0;
1598     }
1599   }
1600 }
1601 
1602 
php_http_method_str(enum php_http_method m)1603 const char * php_http_method_str (enum php_http_method m)
1604 {
1605   return method_strings[m];
1606 }
1607 
1608 
1609 void
php_http_parser_init(php_http_parser * parser,enum php_http_parser_type t)1610 php_http_parser_init (php_http_parser *parser, enum php_http_parser_type t)
1611 {
1612   parser->type = t;
1613   parser->state = (t == PHP_HTTP_REQUEST ? s_start_req : (t == PHP_HTTP_RESPONSE ? s_start_res : s_start_req_or_res));
1614   parser->nread = 0;
1615   parser->upgrade = 0;
1616   parser->flags = 0;
1617   parser->method = 0;
1618 }
1619