Line data Source code
1 : /*
2 : +--------------------------------------------------------------------+
3 : | PECL :: http |
4 : +--------------------------------------------------------------------+
5 : | Redistribution and use in source and binary forms, with or without |
6 : | modification, are permitted provided that the conditions mentioned |
7 : | in the accompanying LICENSE file are met. |
8 : +--------------------------------------------------------------------+
9 : | Copyright (c) 2004-2014, Michael Wallner <mike@php.net> |
10 : +--------------------------------------------------------------------+
11 : */
12 :
13 : #include "php_http_api.h"
14 :
15 : #ifndef DBG_PARSER
16 : # define DBG_PARSER 0
17 : #endif
18 :
19 : typedef struct php_http_message_parser_state_spec {
20 : php_http_message_parser_state_t state;
21 : unsigned need_data:1;
22 : } php_http_message_parser_state_spec_t;
23 :
24 : static const php_http_message_parser_state_spec_t php_http_message_parser_states[] = {
25 : {PHP_HTTP_MESSAGE_PARSER_STATE_START, 1},
26 : {PHP_HTTP_MESSAGE_PARSER_STATE_HEADER, 1},
27 : {PHP_HTTP_MESSAGE_PARSER_STATE_HEADER_DONE, 0},
28 : {PHP_HTTP_MESSAGE_PARSER_STATE_BODY, 0},
29 : {PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DUMB, 1},
30 : {PHP_HTTP_MESSAGE_PARSER_STATE_BODY_LENGTH, 1},
31 : {PHP_HTTP_MESSAGE_PARSER_STATE_BODY_CHUNKED, 1},
32 : {PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE, 0},
33 : {PHP_HTTP_MESSAGE_PARSER_STATE_DONE, 0}
34 : };
35 :
36 : #if DBG_PARSER
37 : const char *php_http_message_parser_state_name(php_http_message_parser_state_t state) {
38 : const char *states[] = {"START", "HEADER", "HEADER_DONE", "BODY", "BODY_DUMB", "BODY_LENGTH", "BODY_CHUNK", "BODY_DONE", "DONE"};
39 :
40 : if (state < 0 || state > (sizeof(states)/sizeof(char*))-1) {
41 : return "FAILURE";
42 : }
43 : return states[state];
44 : }
45 : #endif
46 :
47 142 : php_http_message_parser_t *php_http_message_parser_init(php_http_message_parser_t *parser TSRMLS_DC)
48 : {
49 142 : if (!parser) {
50 100 : parser = emalloc(sizeof(*parser));
51 : }
52 142 : memset(parser, 0, sizeof(*parser));
53 :
54 : TSRMLS_SET_CTX(parser->ts);
55 :
56 142 : php_http_header_parser_init(&parser->header TSRMLS_CC);
57 :
58 142 : return parser;
59 : }
60 :
61 833 : php_http_message_parser_state_t php_http_message_parser_state_push(php_http_message_parser_t *parser, unsigned argc, ...)
62 : {
63 : php_http_message_parser_state_t state;
64 : va_list va_args;
65 : unsigned i;
66 :
67 : /* short circuit */
68 833 : ZEND_PTR_STACK_RESIZE_IF_NEEDED((&parser->stack), argc);
69 :
70 833 : va_start(va_args, argc);
71 1790 : for (i = 0; i < argc; ++i) {
72 957 : state = va_arg(va_args, php_http_message_parser_state_t);
73 957 : zend_ptr_stack_push(&parser->stack, (void *) state);
74 : }
75 833 : va_end(va_args);
76 :
77 833 : return state;
78 : }
79 :
80 1261 : php_http_message_parser_state_t php_http_message_parser_state_is(php_http_message_parser_t *parser)
81 : {
82 1261 : if (parser->stack.top) {
83 1211 : return (php_http_message_parser_state_t) parser->stack.elements[parser->stack.top - 1];
84 : }
85 50 : return PHP_HTTP_MESSAGE_PARSER_STATE_START;
86 : }
87 :
88 1048 : php_http_message_parser_state_t php_http_message_parser_state_pop(php_http_message_parser_t *parser)
89 : {
90 1048 : if (parser->stack.top) {
91 933 : return (php_http_message_parser_state_t) zend_ptr_stack_pop(&parser->stack);
92 : }
93 115 : return PHP_HTTP_MESSAGE_PARSER_STATE_START;
94 : }
95 :
96 142 : void php_http_message_parser_dtor(php_http_message_parser_t *parser)
97 : {
98 142 : php_http_header_parser_dtor(&parser->header);
99 142 : zend_ptr_stack_destroy(&parser->stack);
100 142 : php_http_message_free(&parser->message);
101 142 : if (parser->dechunk) {
102 12 : php_http_encoding_stream_free(&parser->dechunk);
103 : }
104 142 : if (parser->inflate) {
105 2 : php_http_encoding_stream_free(&parser->inflate);
106 : }
107 142 : }
108 :
109 100 : void php_http_message_parser_free(php_http_message_parser_t **parser)
110 : {
111 100 : if (*parser) {
112 100 : php_http_message_parser_dtor(*parser);
113 100 : efree(*parser);
114 100 : *parser = NULL;
115 : }
116 100 : }
117 :
118 20 : php_http_message_parser_state_t php_http_message_parser_parse_stream(php_http_message_parser_t *parser, php_http_buffer_t *buf, php_stream *s, unsigned flags, php_http_message_t **message)
119 : {
120 20 : php_http_message_parser_state_t state = PHP_HTTP_MESSAGE_PARSER_STATE_START;
121 : TSRMLS_FETCH_FROM_CTX(parser->ts);
122 :
123 20 : if (!buf->data) {
124 6 : php_http_buffer_resize_ex(buf, 0x1000, 1, 0);
125 : }
126 156 : while (!php_stream_eof(s)) {
127 136 : size_t justread = 0;
128 : #if DBG_PARSER
129 : fprintf(stderr, "#SP: %s (f:%u)\n", php_http_message_parser_state_name(state), flags);
130 : #endif
131 136 : switch (state) {
132 : case PHP_HTTP_MESSAGE_PARSER_STATE_START:
133 : case PHP_HTTP_MESSAGE_PARSER_STATE_HEADER:
134 : case PHP_HTTP_MESSAGE_PARSER_STATE_HEADER_DONE:
135 : /* read line */
136 115 : php_stream_get_line(s, buf->data + buf->used, buf->free, &justread);
137 115 : php_http_buffer_account(buf, justread);
138 115 : break;
139 :
140 : case PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DUMB:
141 : /* read all */
142 0 : justread = php_stream_read(s, buf->data + buf->used, buf->free);
143 0 : php_http_buffer_account(buf, justread);
144 0 : break;
145 :
146 : case PHP_HTTP_MESSAGE_PARSER_STATE_BODY_LENGTH:
147 : /* read body_length */
148 4 : justread = php_stream_read(s, buf->data + buf->used, MIN(buf->free, parser->body_length));
149 4 : php_http_buffer_account(buf, justread);
150 4 : break;
151 :
152 : case PHP_HTTP_MESSAGE_PARSER_STATE_BODY_CHUNKED:
153 : /* duh, this is very naive */
154 11 : if (parser->body_length) {
155 3 : justread = php_stream_read(s, buf->data + buf->used, MIN(parser->body_length, buf->free));
156 :
157 3 : php_http_buffer_account(buf, justread);
158 :
159 3 : parser->body_length -= justread;
160 : } else {
161 8 : php_http_buffer_resize(buf, 24);
162 8 : php_stream_get_line(s, buf->data, buf->free, &justread);
163 8 : php_http_buffer_account(buf, justread);
164 :
165 8 : parser->body_length = strtoul(buf->data + buf->used - justread, NULL, 16);
166 : }
167 11 : break;
168 :
169 : case PHP_HTTP_MESSAGE_PARSER_STATE_BODY:
170 : case PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE:
171 : /* should not occur */
172 0 : abort();
173 : break;
174 :
175 : case PHP_HTTP_MESSAGE_PARSER_STATE_DONE:
176 : case PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE:
177 26 : return php_http_message_parser_state_is(parser);
178 : }
179 :
180 130 : if (justread) {
181 116 : state = php_http_message_parser_parse(parser, buf, flags, message);
182 : } else {
183 14 : return state;
184 : }
185 : }
186 :
187 0 : return PHP_HTTP_MESSAGE_PARSER_STATE_DONE;
188 : }
189 :
190 :
191 599 : php_http_message_parser_state_t php_http_message_parser_parse(php_http_message_parser_t *parser, php_http_buffer_t *buffer, unsigned flags, php_http_message_t **message)
192 : {
193 599 : char *str = NULL;
194 599 : size_t len = 0;
195 599 : size_t cut = 0;
196 : TSRMLS_FETCH_FROM_CTX(parser->ts);
197 :
198 2181 : while (buffer->used || !php_http_message_parser_states[php_http_message_parser_state_is(parser)].need_data) {
199 : #if DBG_PARSER
200 : fprintf(stderr, "#MP: %s (f: %u, t:%d, l:%zu)\n",
201 : php_http_message_parser_state_name(php_http_message_parser_state_is(parser)),
202 : flags,
203 : message && *message ? (*message)->type : -1,
204 : buffer->used
205 : );
206 : _dpf(0, buffer->data, buffer->used);
207 : #endif
208 :
209 1048 : switch (php_http_message_parser_state_pop(parser))
210 : {
211 : case PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE:
212 0 : return php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE);
213 :
214 : case PHP_HTTP_MESSAGE_PARSER_STATE_START:
215 : {
216 115 : char *ptr = buffer->data;
217 :
218 240 : while (ptr - buffer->data < buffer->used && PHP_HTTP_IS_CTYPE(space, *ptr)) {
219 10 : ++ptr;
220 : }
221 :
222 115 : php_http_buffer_cut(buffer, 0, ptr - buffer->data);
223 :
224 115 : if (buffer->used) {
225 109 : php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_HEADER);
226 : }
227 115 : break;
228 : }
229 :
230 : case PHP_HTTP_MESSAGE_PARSER_STATE_HEADER:
231 : {
232 479 : unsigned header_parser_flags = (flags & PHP_HTTP_MESSAGE_PARSER_CLEANUP) ? PHP_HTTP_HEADER_PARSER_CLEANUP : 0;
233 :
234 479 : switch (php_http_header_parser_parse(&parser->header, buffer, header_parser_flags, *message ? &(*message)->hdrs : NULL, (php_http_info_callback_t) php_http_message_info_callback, message)) {
235 : case PHP_HTTP_HEADER_PARSER_STATE_FAILURE:
236 4 : return PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE;
237 :
238 : case PHP_HTTP_HEADER_PARSER_STATE_DONE:
239 82 : php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_HEADER_DONE);
240 82 : break;
241 :
242 : default:
243 393 : php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_HEADER);
244 393 : if (buffer->used) {
245 0 : return PHP_HTTP_MESSAGE_PARSER_STATE_HEADER;
246 : }
247 : }
248 475 : break;
249 : }
250 :
251 : case PHP_HTTP_MESSAGE_PARSER_STATE_HEADER_DONE:
252 : {
253 82 : zval *h, *h_loc = NULL, *h_con = NULL, **h_cl = NULL, **h_cr = NULL, **h_te = NULL;
254 :
255 82 : if ((h = php_http_message_header(*message, ZEND_STRL("Transfer-Encoding"), 1))) {
256 12 : zend_hash_update(&(*message)->hdrs, "X-Original-Transfer-Encoding", sizeof("X-Original-Transfer-Encoding"), &h, sizeof(zval *), (void *) &h_te);
257 12 : zend_hash_del(&(*message)->hdrs, "Transfer-Encoding", sizeof("Transfer-Encoding"));
258 : }
259 82 : if ((h = php_http_message_header(*message, ZEND_STRL("Content-Length"), 1))) {
260 28 : zend_hash_update(&(*message)->hdrs, "X-Original-Content-Length", sizeof("X-Original-Content-Length"), &h, sizeof(zval *), (void *) &h_cl);
261 : }
262 82 : if ((h = php_http_message_header(*message, ZEND_STRL("Content-Range"), 1))) {
263 0 : zend_hash_update(&(*message)->hdrs, "X-Original-Content-Range", sizeof("X-Original-Content-Range"), &h, sizeof(zval *), (void *) &h_cr);
264 0 : zend_hash_del(&(*message)->hdrs, "Content-Range", sizeof("Content-Range"));
265 : }
266 :
267 : /* default */
268 82 : MAKE_STD_ZVAL(h);
269 82 : ZVAL_LONG(h, 0);
270 82 : zend_hash_update(&(*message)->hdrs, "Content-Length", sizeof("Content-Length"), &h, sizeof(zval *), NULL);
271 :
272 : /* so, if curl sees a 3xx code, a Location header and a Connection:close header
273 : * it decides not to read the response body.
274 : */
275 82 : if ((flags & PHP_HTTP_MESSAGE_PARSER_EMPTY_REDIRECTS)
276 0 : && (*message)->type == PHP_HTTP_RESPONSE
277 0 : && (*message)->http.info.response.code/100 == 3
278 0 : && (h_loc = php_http_message_header(*message, ZEND_STRL("Location"), 1))
279 0 : && (h_con = php_http_message_header(*message, ZEND_STRL("Connection"), 1))
280 : ) {
281 0 : if (php_http_match(Z_STRVAL_P(h_con), "close", PHP_HTTP_MATCH_WORD)) {
282 0 : php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_DONE);
283 0 : zval_ptr_dtor(&h_loc);
284 0 : zval_ptr_dtor(&h_con);
285 0 : break;
286 : }
287 : }
288 82 : if (h_loc) {
289 0 : zval_ptr_dtor(&h_loc);
290 : }
291 82 : if (h_con) {
292 0 : zval_ptr_dtor(&h_con);
293 : }
294 :
295 82 : if ((h = php_http_message_header(*message, ZEND_STRL("Content-Encoding"), 1))) {
296 2 : if (php_http_match(Z_STRVAL_P(h), "gzip", PHP_HTTP_MATCH_WORD)
297 0 : || php_http_match(Z_STRVAL_P(h), "x-gzip", PHP_HTTP_MATCH_WORD)
298 0 : || php_http_match(Z_STRVAL_P(h), "deflate", PHP_HTTP_MATCH_WORD)
299 : ) {
300 2 : if (parser->inflate) {
301 0 : php_http_encoding_stream_reset(&parser->inflate);
302 : } else {
303 2 : parser->inflate = php_http_encoding_stream_init(NULL, php_http_encoding_stream_get_inflate_ops(), 0 TSRMLS_CC);
304 : }
305 2 : zend_hash_update(&(*message)->hdrs, "X-Original-Content-Encoding", sizeof("X-Original-Content-Encoding"), &h, sizeof(zval *), NULL);
306 2 : zend_hash_del(&(*message)->hdrs, "Content-Encoding", sizeof("Content-Encoding"));
307 : } else {
308 0 : zval_ptr_dtor(&h);
309 : }
310 : }
311 :
312 82 : if ((flags & PHP_HTTP_MESSAGE_PARSER_DUMB_BODIES)) {
313 0 : php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DUMB);
314 : } else {
315 82 : if (h_te) {
316 12 : if (strstr(Z_STRVAL_PP(h_te), "chunked")) {
317 12 : parser->dechunk = php_http_encoding_stream_init(parser->dechunk, php_http_encoding_stream_get_dechunk_ops(), 0 TSRMLS_CC);
318 12 : php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_BODY_CHUNKED);
319 12 : break;
320 : }
321 : }
322 :
323 70 : if (h_cl) {
324 : char *stop;
325 :
326 28 : if (Z_TYPE_PP(h_cl) == IS_STRING) {
327 28 : parser->body_length = strtoul(Z_STRVAL_PP(h_cl), &stop, 10);
328 :
329 28 : if (stop != Z_STRVAL_PP(h_cl)) {
330 28 : php_http_message_parser_state_push(parser, 1, !parser->body_length?PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE:PHP_HTTP_MESSAGE_PARSER_STATE_BODY_LENGTH);
331 28 : break;
332 : }
333 0 : } else if (Z_TYPE_PP(h_cl) == IS_LONG) {
334 0 : parser->body_length = Z_LVAL_PP(h_cl);
335 0 : php_http_message_parser_state_push(parser, 1, !parser->body_length?PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE:PHP_HTTP_MESSAGE_PARSER_STATE_BODY_LENGTH);
336 0 : break;
337 : }
338 : }
339 :
340 42 : if (h_cr) {
341 0 : ulong total = 0, start = 0, end = 0;
342 :
343 0 : if (!strncasecmp(Z_STRVAL_PP(h_cr), "bytes", lenof("bytes"))
344 0 : && ( Z_STRVAL_P(h)[lenof("bytes")] == ':'
345 0 : || Z_STRVAL_P(h)[lenof("bytes")] == ' '
346 0 : || Z_STRVAL_P(h)[lenof("bytes")] == '='
347 : )
348 : ) {
349 0 : char *total_at = NULL, *end_at = NULL;
350 0 : char *start_at = Z_STRVAL_PP(h_cr) + sizeof("bytes");
351 :
352 0 : start = strtoul(start_at, &end_at, 10);
353 0 : if (end_at) {
354 0 : end = strtoul(end_at + 1, &total_at, 10);
355 0 : if (total_at && strncmp(total_at + 1, "*", 1)) {
356 0 : total = strtoul(total_at + 1, NULL, 10);
357 : }
358 :
359 0 : if (end >= start && (!total || end < total)) {
360 0 : parser->body_length = end + 1 - start;
361 0 : php_http_message_parser_state_push(parser, 1, !parser->body_length?PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE:PHP_HTTP_MESSAGE_PARSER_STATE_BODY_LENGTH);
362 0 : break;
363 : }
364 : }
365 : }
366 : }
367 :
368 :
369 42 : if ((*message)->type == PHP_HTTP_REQUEST) {
370 38 : php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_DONE);
371 : } else {
372 4 : php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DUMB);
373 : }
374 : }
375 42 : break;
376 : }
377 :
378 : case PHP_HTTP_MESSAGE_PARSER_STATE_BODY:
379 : {
380 124 : if (len) {
381 : zval *zcl;
382 :
383 107 : if (parser->inflate) {
384 2 : char *dec_str = NULL;
385 : size_t dec_len;
386 :
387 2 : if (SUCCESS != php_http_encoding_stream_update(parser->inflate, str, len, &dec_str, &dec_len)) {
388 0 : return php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE);
389 : }
390 :
391 2 : if (str != buffer->data) {
392 0 : STR_FREE(str);
393 : }
394 2 : str = dec_str;
395 2 : len = dec_len;
396 : }
397 :
398 107 : php_stream_write(php_http_message_body_stream((*message)->body), str, len);
399 :
400 : /* keep track */
401 107 : MAKE_STD_ZVAL(zcl);
402 107 : ZVAL_LONG(zcl, php_http_message_body_size((*message)->body));
403 107 : zend_hash_update(&(*message)->hdrs, "Content-Length", sizeof("Content-Length"), &zcl, sizeof(zval *), NULL);
404 : }
405 :
406 124 : if (cut) {
407 124 : php_http_buffer_cut(buffer, 0, cut);
408 : }
409 :
410 124 : if (str != buffer->data) {
411 30 : STR_FREE(str);
412 : }
413 :
414 124 : str = NULL;
415 124 : len = 0;
416 124 : cut = 0;
417 124 : break;
418 : }
419 :
420 : case PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DUMB:
421 : {
422 3 : str = buffer->data;
423 3 : len = buffer->used;
424 3 : cut = len;
425 :
426 3 : php_http_message_parser_state_push(parser, 2, PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE, PHP_HTTP_MESSAGE_PARSER_STATE_BODY);
427 3 : break;
428 : }
429 :
430 : case PHP_HTTP_MESSAGE_PARSER_STATE_BODY_LENGTH:
431 : {
432 93 : len = MIN(parser->body_length, buffer->used);
433 93 : str = buffer->data;
434 93 : cut = len;
435 :
436 93 : parser->body_length -= len;
437 :
438 93 : php_http_message_parser_state_push(parser, 2, !parser->body_length?PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE:PHP_HTTP_MESSAGE_PARSER_STATE_BODY_LENGTH, PHP_HTTP_MESSAGE_PARSER_STATE_BODY);
439 93 : break;
440 : }
441 :
442 : case PHP_HTTP_MESSAGE_PARSER_STATE_BODY_CHUNKED:
443 : {
444 : /*
445 : * - pass available data through the dechunk stream
446 : * - pass decoded data along
447 : * - if stream zeroed:
448 : * Y: - cut processed string out of buffer, but leave length of unprocessed dechunk stream data untouched
449 : * - body done
450 : * N: - parse ahaed
451 : */
452 28 : char *dec_str = NULL;
453 : size_t dec_len;
454 :
455 28 : if (SUCCESS != php_http_encoding_stream_update(parser->dechunk, buffer->data, buffer->used, &dec_str, &dec_len)) {
456 0 : return PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE;
457 : }
458 :
459 28 : str = dec_str;
460 28 : len = dec_len;
461 :
462 28 : if (php_http_encoding_stream_done(parser->dechunk)) {
463 12 : cut = buffer->used - PHP_HTTP_BUFFER(parser->dechunk->ctx)->used;
464 12 : php_http_message_parser_state_push(parser, 2, PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE, PHP_HTTP_MESSAGE_PARSER_STATE_BODY);
465 : } else {
466 16 : cut = buffer->used;
467 16 : php_http_message_parser_state_push(parser, 2, PHP_HTTP_MESSAGE_PARSER_STATE_BODY_CHUNKED, PHP_HTTP_MESSAGE_PARSER_STATE_BODY);
468 : }
469 28 : break;
470 : }
471 :
472 : case PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE:
473 : {
474 43 : php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_DONE);
475 :
476 43 : if (parser->dechunk) {
477 12 : char *dec_str = NULL;
478 : size_t dec_len;
479 :
480 12 : if (SUCCESS != php_http_encoding_stream_finish(parser->dechunk, &dec_str, &dec_len)) {
481 0 : return php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE);
482 : }
483 12 : php_http_encoding_stream_dtor(parser->dechunk);
484 :
485 12 : if (dec_str && dec_len) {
486 0 : str = dec_str;
487 0 : len = dec_len;
488 0 : cut = 0;
489 0 : php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_BODY);
490 : }
491 : }
492 :
493 43 : break;
494 : }
495 :
496 : case PHP_HTTP_MESSAGE_PARSER_STATE_DONE: {
497 81 : char *ptr = buffer->data;
498 :
499 164 : while (ptr - buffer->data < buffer->used && PHP_HTTP_IS_CTYPE(space, *ptr)) {
500 2 : ++ptr;
501 : }
502 :
503 81 : php_http_buffer_cut(buffer, 0, ptr - buffer->data);
504 :
505 81 : if (!(flags & PHP_HTTP_MESSAGE_PARSER_GREEDY)) {
506 61 : return PHP_HTTP_MESSAGE_PARSER_STATE_DONE;
507 : }
508 20 : break;
509 : }
510 : }
511 : }
512 :
513 534 : return php_http_message_parser_state_is(parser);
514 : }
515 :
516 : zend_class_entry *php_http_message_parser_class_entry;
517 : static zend_object_handlers php_http_message_parser_object_handlers;
518 :
519 13 : zend_object_value php_http_message_parser_object_new(zend_class_entry *ce TSRMLS_DC)
520 : {
521 13 : return php_http_message_parser_object_new_ex(ce, NULL, NULL TSRMLS_CC);
522 : }
523 :
524 13 : zend_object_value php_http_message_parser_object_new_ex(zend_class_entry *ce, php_http_message_parser_t *parser, php_http_message_parser_object_t **ptr TSRMLS_DC)
525 : {
526 : php_http_message_parser_object_t *o;
527 :
528 13 : o = ecalloc(1, sizeof(php_http_message_parser_object_t));
529 13 : zend_object_std_init((zend_object *) o, ce TSRMLS_CC);
530 13 : object_properties_init((zend_object *) o, ce);
531 :
532 13 : if (ptr) {
533 0 : *ptr = o;
534 : }
535 :
536 13 : if (parser) {
537 0 : o->parser = parser;
538 : } else {
539 13 : o->parser = php_http_message_parser_init(NULL TSRMLS_CC);
540 : }
541 13 : o->buffer = php_http_buffer_new();
542 :
543 13 : o->zv.handle = zend_objects_store_put((zend_object *) o, NULL, php_http_message_parser_object_free, NULL TSRMLS_CC);
544 13 : o->zv.handlers = &php_http_message_parser_object_handlers;
545 :
546 13 : return o->zv;
547 : }
548 :
549 13 : void php_http_message_parser_object_free(void *object TSRMLS_DC)
550 : {
551 13 : php_http_message_parser_object_t *o = (php_http_message_parser_object_t *) object;
552 :
553 13 : if (o->parser) {
554 13 : php_http_message_parser_free(&o->parser);
555 : }
556 13 : if (o->buffer) {
557 13 : php_http_buffer_free(&o->buffer);
558 : }
559 13 : zend_object_std_dtor((zend_object *) o TSRMLS_CC);
560 13 : efree(o);
561 13 : }
562 :
563 : ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageParser_getState, 0, 0, 0)
564 : ZEND_END_ARG_INFO();
565 0 : static PHP_METHOD(HttpMessageParser, getState)
566 : {
567 0 : php_http_message_parser_object_t *parser_obj = zend_object_store_get_object(getThis() TSRMLS_CC);
568 :
569 0 : zend_parse_parameters_none();
570 : /* always return the real state */
571 0 : RETVAL_LONG(php_http_message_parser_state_is(parser_obj->parser));
572 0 : }
573 :
574 : ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageParser_parse, 0, 0, 3)
575 : ZEND_ARG_INFO(0, data)
576 : ZEND_ARG_INFO(0, flags)
577 : ZEND_ARG_INFO(1, message)
578 : ZEND_END_ARG_INFO();
579 82 : static PHP_METHOD(HttpMessageParser, parse)
580 : {
581 : php_http_message_parser_object_t *parser_obj;
582 : zval *zmsg;
583 : char *data_str;
584 : int data_len;
585 : long flags;
586 :
587 164 : php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "slz", &data_str, &data_len, &flags, &zmsg), invalid_arg, return);
588 :
589 82 : parser_obj = zend_object_store_get_object(getThis() TSRMLS_CC);
590 82 : php_http_buffer_append(parser_obj->buffer, data_str, data_len);
591 82 : RETVAL_LONG(php_http_message_parser_parse(parser_obj->parser, parser_obj->buffer, flags, &parser_obj->parser->message));
592 :
593 82 : zval_dtor(zmsg);
594 82 : if (parser_obj->parser->message) {
595 82 : ZVAL_OBJVAL(zmsg, php_http_message_object_new_ex(php_http_message_class_entry, php_http_message_copy(parser_obj->parser->message, NULL), NULL TSRMLS_CC), 0);
596 : }
597 : }
598 :
599 : ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageParser_stream, 0, 0, 3)
600 : ZEND_ARG_INFO(0, stream)
601 : ZEND_ARG_INFO(0, flags)
602 : ZEND_ARG_INFO(1, message)
603 : ZEND_END_ARG_INFO();
604 15 : static PHP_METHOD(HttpMessageParser, stream)
605 : {
606 : php_http_message_parser_object_t *parser_obj;
607 : zend_error_handling zeh;
608 : zval *zmsg, *zstream;
609 : php_stream *s;
610 : long flags;
611 :
612 15 : php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rlz", &zstream, &flags, &zmsg), invalid_arg, return);
613 :
614 15 : zend_replace_error_handling(EH_THROW, php_http_exception_unexpected_val_class_entry, &zeh TSRMLS_CC);
615 15 : php_stream_from_zval(s, &zstream);
616 15 : zend_restore_error_handling(&zeh TSRMLS_CC);
617 :
618 15 : parser_obj = zend_object_store_get_object(getThis() TSRMLS_CC);
619 15 : RETVAL_LONG(php_http_message_parser_parse_stream(parser_obj->parser, parser_obj->buffer, s, flags, &parser_obj->parser->message));
620 :
621 15 : zval_dtor(zmsg);
622 15 : if (parser_obj->parser->message) {
623 14 : ZVAL_OBJVAL(zmsg, php_http_message_object_new_ex(php_http_message_class_entry, php_http_message_copy(parser_obj->parser->message, NULL), NULL TSRMLS_CC), 0);
624 : }
625 : }
626 :
627 : static zend_function_entry php_http_message_parser_methods[] = {
628 : PHP_ME(HttpMessageParser, getState, ai_HttpMessageParser_getState, ZEND_ACC_PUBLIC)
629 : PHP_ME(HttpMessageParser, parse, ai_HttpMessageParser_parse, ZEND_ACC_PUBLIC)
630 : PHP_ME(HttpMessageParser, stream, ai_HttpMessageParser_stream, ZEND_ACC_PUBLIC)
631 : {NULL, NULL, NULL}
632 : };
633 :
634 408 : PHP_MINIT_FUNCTION(http_message_parser)
635 : {
636 : zend_class_entry ce;
637 :
638 408 : INIT_NS_CLASS_ENTRY(ce, "http\\Message", "Parser", php_http_message_parser_methods);
639 408 : php_http_message_parser_class_entry = zend_register_internal_class(&ce TSRMLS_CC);
640 408 : memcpy(&php_http_message_parser_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
641 408 : php_http_message_parser_class_entry->create_object = php_http_message_parser_object_new;
642 408 : php_http_message_parser_object_handlers.clone_obj = NULL;
643 :
644 408 : zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("CLEANUP"), PHP_HTTP_MESSAGE_PARSER_CLEANUP TSRMLS_CC);
645 408 : zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("DUMB_BODIES"), PHP_HTTP_MESSAGE_PARSER_DUMB_BODIES TSRMLS_CC);
646 408 : zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("EMPTY_REDIRECTS"), PHP_HTTP_MESSAGE_PARSER_EMPTY_REDIRECTS TSRMLS_CC);
647 408 : zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("GREEDY"), PHP_HTTP_MESSAGE_PARSER_GREEDY TSRMLS_CC);
648 :
649 408 : zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_FAILURE"), PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE TSRMLS_CC);
650 408 : zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_START"), PHP_HTTP_MESSAGE_PARSER_STATE_START TSRMLS_CC);
651 408 : zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_HEADER"), PHP_HTTP_MESSAGE_PARSER_STATE_HEADER TSRMLS_CC);
652 408 : zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_HEADER_DONE"), PHP_HTTP_MESSAGE_PARSER_STATE_HEADER_DONE TSRMLS_CC);
653 408 : zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_BODY"), PHP_HTTP_MESSAGE_PARSER_STATE_BODY TSRMLS_CC);
654 408 : zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_BODY_DUMB"), PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DUMB TSRMLS_CC);
655 408 : zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_BODY_LENGTH"), PHP_HTTP_MESSAGE_PARSER_STATE_BODY_LENGTH TSRMLS_CC);
656 408 : zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_BODY_CHUNKED"), PHP_HTTP_MESSAGE_PARSER_STATE_BODY_CHUNKED TSRMLS_CC);
657 408 : zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_BODY_DONE"), PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE TSRMLS_CC);
658 408 : zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_DONE"), PHP_HTTP_MESSAGE_PARSER_STATE_DONE TSRMLS_CC);
659 :
660 408 : return SUCCESS;
661 : }
662 :
663 : /*
664 : * Local variables:
665 : * tab-width: 4
666 : * c-basic-offset: 4
667 : * End:
668 : * vim600: noet sw=4 ts=4 fdm=marker
669 : * vim<600: noet sw=4 ts=4
670 : */
671 :
|