prepare v4.2.5
[m6w6/ext-http] / src / php_http_message_parser.c
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, 0},
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_UPDATE_CL, 0},
34 {PHP_HTTP_MESSAGE_PARSER_STATE_DONE, 0}
35 };
36
37 #if DBG_PARSER
38 const char *php_http_message_parser_state_name(php_http_message_parser_state_t state) {
39 const char *states[] = {"START", "HEADER", "HEADER_DONE", "BODY", "BODY_DUMB", "BODY_LENGTH", "BODY_CHUNK", "BODY_DONE", "UPDATE_CL", "DONE"};
40
41 if (state < 0 || state > (sizeof(states)/sizeof(char*))-1) {
42 return "FAILURE";
43 }
44 return states[state];
45 }
46 #endif
47
48 php_http_message_parser_t *php_http_message_parser_init(php_http_message_parser_t *parser)
49 {
50 if (!parser) {
51 parser = emalloc(sizeof(*parser));
52 }
53 memset(parser, 0, sizeof(*parser));
54
55 php_http_header_parser_init(&parser->header);
56
57 return parser;
58 }
59
60 #define php_http_message_parser_state_push(parser, state) zend_ptr_stack_push(&(parser)->stack, (void *) (state)), (state)
61 #define php_http_message_parser_state_pop(parser) ((parser)->stack.top \
62 ? (php_http_message_parser_state_t) zend_ptr_stack_pop(&parser->stack) \
63 : PHP_HTTP_MESSAGE_PARSER_STATE_START)
64 #define php_http_message_parser_state_is_ex(parser) ((parser)->stack.top \
65 ? (php_http_message_parser_state_t) (parser)->stack.elements[(parser)->stack.top - 1] \
66 : PHP_HTTP_MESSAGE_PARSER_STATE_START)
67 php_http_message_parser_state_t php_http_message_parser_state_is(php_http_message_parser_t *parser)
68 {
69 return php_http_message_parser_state_is_ex(parser);
70 }
71
72 void php_http_message_parser_dtor(php_http_message_parser_t *parser)
73 {
74 php_http_header_parser_dtor(&parser->header);
75 zend_ptr_stack_destroy(&parser->stack);
76 php_http_message_free(&parser->message);
77 if (parser->dechunk) {
78 php_http_encoding_stream_free(&parser->dechunk);
79 }
80 if (parser->inflate) {
81 php_http_encoding_stream_free(&parser->inflate);
82 }
83 }
84
85 void php_http_message_parser_free(php_http_message_parser_t **parser)
86 {
87 if (*parser) {
88 php_http_message_parser_dtor(*parser);
89 efree(*parser);
90 *parser = NULL;
91 }
92 }
93
94 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)
95 {
96 php_http_message_parser_state_t state = PHP_HTTP_MESSAGE_PARSER_STATE_START;
97
98 if (!buf->data) {
99 php_http_buffer_resize_ex(buf, 0x1000, 1, 0);
100 }
101 while (1) {
102 size_t justread = 0;
103 #if DBG_PARSER
104 fprintf(stderr, "#SP: %s (f:%u)\n", php_http_message_parser_state_name(state), flags);
105 #endif
106 /* resize if needed */
107 if (buf->free < 0x1000) {
108 php_http_buffer_resize_ex(buf, 0x1000, 1, 0);
109 }
110 switch (state) {
111 case PHP_HTTP_MESSAGE_PARSER_STATE_START:
112 case PHP_HTTP_MESSAGE_PARSER_STATE_HEADER:
113 case PHP_HTTP_MESSAGE_PARSER_STATE_HEADER_DONE:
114 /* read line */
115 php_stream_get_line(s, buf->data + buf->used, buf->free, &justread);
116 /* if we fail reading a whole line, try a single char */
117 if (!justread) {
118 int c = php_stream_getc(s);
119
120 if (c != EOF) {
121 char s[1] = {c};
122 justread = php_http_buffer_append(buf, s, 1);
123 }
124 }
125 php_http_buffer_account(buf, justread);
126 break;
127
128 case PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DUMB:
129 /* read all */
130 justread = php_stream_read(s, buf->data + buf->used, buf->free);
131 php_http_buffer_account(buf, justread);
132 break;
133
134 case PHP_HTTP_MESSAGE_PARSER_STATE_BODY_LENGTH:
135 /* read body_length */
136 justread = php_stream_read(s, buf->data + buf->used, MIN(buf->free, parser->body_length));
137 php_http_buffer_account(buf, justread);
138 break;
139
140 case PHP_HTTP_MESSAGE_PARSER_STATE_BODY_CHUNKED:
141 /* duh, this is very naive */
142 if (parser->body_length) {
143 justread = php_stream_read(s, buf->data + buf->used, MIN(parser->body_length, buf->free));
144
145 php_http_buffer_account(buf, justread);
146
147 parser->body_length -= justread;
148 } else {
149 php_http_buffer_resize(buf, 24);
150 php_stream_get_line(s, buf->data, buf->free, &justread);
151 php_http_buffer_account(buf, justread);
152
153 parser->body_length = strtoul(buf->data + buf->used - justread, NULL, 16);
154 }
155 break;
156
157 case PHP_HTTP_MESSAGE_PARSER_STATE_BODY:
158 case PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE:
159 case PHP_HTTP_MESSAGE_PARSER_STATE_UPDATE_CL:
160 /* should not occur */
161 abort();
162 break;
163
164 case PHP_HTTP_MESSAGE_PARSER_STATE_DONE:
165 case PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE:
166 return php_http_message_parser_state_is_ex(parser);
167 }
168
169 if (justread) {
170 state = php_http_message_parser_parse(parser, buf, flags, message);
171 } else if (php_stream_eof(s)) {
172 return php_http_message_parser_parse(parser, buf, flags | PHP_HTTP_MESSAGE_PARSER_CLEANUP, message);
173 } else {
174 return state;
175 }
176 }
177
178 return PHP_HTTP_MESSAGE_PARSER_STATE_DONE;
179 }
180
181
182 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)
183 {
184 char *str = NULL;
185 size_t len = 0;
186 size_t cut = 0;
187
188 while (buffer->used || !php_http_message_parser_states[php_http_message_parser_state_is_ex(parser)].need_data) {
189 #if DBG_PARSER
190 fprintf(stderr, "#MP: %s (f: %u, t:%d, l:%zu)\n",
191 php_http_message_parser_state_name(php_http_message_parser_state_is(parser)),
192 flags,
193 message && *message ? (*message)->type : -1,
194 buffer->used
195 );
196 _dpf(0, buffer->data, buffer->used);
197 #endif
198
199 switch (php_http_message_parser_state_pop(parser))
200 {
201 case PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE:
202 return php_http_message_parser_state_push(parser, PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE);
203
204 case PHP_HTTP_MESSAGE_PARSER_STATE_START:
205 {
206 char *ptr = buffer->data;
207
208 while (ptr - buffer->data < buffer->used && PHP_HTTP_IS_CTYPE(space, *ptr)) {
209 ++ptr;
210 }
211
212 php_http_buffer_cut(buffer, 0, ptr - buffer->data);
213
214 if (buffer->used) {
215 php_http_message_parser_state_push(parser, PHP_HTTP_MESSAGE_PARSER_STATE_HEADER);
216 }
217 break;
218 }
219
220 case PHP_HTTP_MESSAGE_PARSER_STATE_HEADER:
221 {
222 unsigned header_parser_flags = (flags & PHP_HTTP_MESSAGE_PARSER_CLEANUP) ? PHP_HTTP_HEADER_PARSER_CLEANUP : 0;
223
224 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)) {
225 case PHP_HTTP_HEADER_PARSER_STATE_FAILURE:
226 return PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE;
227
228 case PHP_HTTP_HEADER_PARSER_STATE_DONE:
229 php_http_message_parser_state_push(parser, PHP_HTTP_MESSAGE_PARSER_STATE_HEADER_DONE);
230 break;
231
232 default:
233 if (buffer->used || !(flags & PHP_HTTP_MESSAGE_PARSER_CLEANUP)) {
234 return php_http_message_parser_state_push(parser, PHP_HTTP_MESSAGE_PARSER_STATE_HEADER);
235 } else {
236 php_http_message_parser_state_push(parser, PHP_HTTP_MESSAGE_PARSER_STATE_HEADER_DONE);
237 }
238 }
239 break;
240 }
241
242 case PHP_HTTP_MESSAGE_PARSER_STATE_HEADER_DONE:
243 {
244 zval h, *h_ptr, *h_loc = NULL, *h_con = NULL, *h_ce;
245 zend_bool chunked = 0;
246 zend_long content_length = -1;
247 zend_string *content_range = NULL;
248
249 /* Content-Range has higher precedence than Content-Length,
250 * and content-length denotes the original length of the entity,
251 * so let's *NOT* remove CR/CL, because that would fundamentally
252 * change the meaning of the whole message
253 */
254 if ((h_ptr = php_http_message_header(*message, ZEND_STRL("Transfer-Encoding")))) {
255 zend_string *zs = zval_get_string(h_ptr);
256
257 chunked = zend_string_equals_literal(zs, "chunked");
258 zend_string_release(zs);
259
260 Z_TRY_ADDREF_P(h_ptr);
261 zend_hash_str_update(&(*message)->hdrs, "X-Original-Transfer-Encoding", lenof("X-Original-Transfer-Encoding"), h_ptr);
262 zend_hash_str_del(&(*message)->hdrs, "Transfer-Encoding", lenof("Transfer-Encoding"));
263
264 /* reset */
265 ZVAL_LONG(&h, 0);
266 zend_hash_str_update(&(*message)->hdrs, "Content-Length", lenof("Content-Length"), &h);
267 } else if ((h_ptr = php_http_message_header(*message, ZEND_STRL("Content-Length")))) {
268 content_length = zval_get_long(h_ptr);
269 Z_TRY_ADDREF_P(h_ptr);
270 zend_hash_str_update(&(*message)->hdrs, "X-Original-Content-Length", lenof("X-Original-Content-Length"), h_ptr);
271 }
272
273 if ((content_range = php_http_message_header_string(*message, ZEND_STRL("Content-Range")))) {
274 ZVAL_STR_COPY(&h, content_range);
275 zend_hash_str_update(&(*message)->hdrs, "Content-Range", lenof("Content-Range"), &h);
276 }
277
278 /* so, if curl sees a 3xx code, a Location header and a Connection:close header
279 * it decides not to read the response body.
280 */
281 if ((flags & PHP_HTTP_MESSAGE_PARSER_EMPTY_REDIRECTS)
282 && (*message)->type == PHP_HTTP_RESPONSE
283 && (*message)->http.info.response.code/100 == 3
284 && (h_loc = php_http_message_header(*message, ZEND_STRL("Location")))
285 && (h_con = php_http_message_header(*message, ZEND_STRL("Connection")))
286 ) {
287 zend_string *con = zval_get_string(h_con);
288
289 if (php_http_match(con->val, "close", PHP_HTTP_MATCH_WORD)) {
290 zend_string_release(con);
291 php_http_message_parser_state_push(parser, PHP_HTTP_MESSAGE_PARSER_STATE_DONE);
292 break;
293 }
294 zend_string_release(con);
295 }
296
297 if ((h_ce = php_http_message_header(*message, ZEND_STRL("Content-Encoding")))) {
298 zend_string *ce = zval_get_string(h_ce);
299
300 if (php_http_match(ce->val, "gzip", PHP_HTTP_MATCH_WORD)
301 || php_http_match(ce->val, "x-gzip", PHP_HTTP_MATCH_WORD)
302 || php_http_match(ce->val, "deflate", PHP_HTTP_MATCH_WORD)
303 ) {
304 if (parser->inflate) {
305 php_http_encoding_stream_reset(&parser->inflate);
306 } else {
307 parser->inflate = php_http_encoding_stream_init(NULL, php_http_encoding_stream_get_inflate_ops(), 0);
308 }
309 Z_TRY_ADDREF_P(h_ce);
310 zend_hash_str_update(&(*message)->hdrs, "X-Original-Content-Encoding", lenof("X-Original-Content-Encoding"), h_ce);
311 zend_hash_str_del(&(*message)->hdrs, "Content-Encoding", lenof("Content-Encoding"));
312 }
313 zend_string_release(ce);
314 }
315
316 if ((flags & PHP_HTTP_MESSAGE_PARSER_DUMB_BODIES)) {
317 php_http_message_parser_state_push(parser, PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DUMB);
318 } else {
319 if (chunked) {
320 parser->dechunk = php_http_encoding_stream_init(parser->dechunk, php_http_encoding_stream_get_dechunk_ops(), 0);
321 php_http_message_parser_state_push(parser, PHP_HTTP_MESSAGE_PARSER_STATE_BODY_CHUNKED);
322 break;
323 }
324
325 if (content_range) {
326 unsigned long total = 0, start = 0, end = 0;
327
328 if (!strncasecmp(content_range->val, "bytes", lenof("bytes"))
329 && ( content_range->val[lenof("bytes")] == ':'
330 || content_range->val[lenof("bytes")] == ' '
331 || content_range->val[lenof("bytes")] == '='
332 )
333 ) {
334 char *total_at = NULL, *end_at = NULL;
335 char *start_at = content_range->val + sizeof("bytes");
336
337 start = strtoul(start_at, &end_at, 10);
338 if (end_at) {
339 end = strtoul(end_at + 1, &total_at, 10);
340 if (total_at && strncmp(total_at + 1, "*", 1)) {
341 total = strtoul(total_at + 1, NULL, 10);
342 }
343
344 if (end >= start && (!total || end <= total)) {
345 parser->body_length = end + 1 - start;
346 if (parser->body_length) {
347 php_http_message_parser_state_push(parser, PHP_HTTP_MESSAGE_PARSER_STATE_BODY_LENGTH);
348 } else {
349 php_http_message_parser_state_push(parser, PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE);
350 }
351 zend_string_release(content_range);
352 break;
353 }
354 }
355 }
356
357 zend_string_release(content_range);
358 }
359
360 if (content_length >= 0) {
361 parser->body_length = content_length;
362 if (parser->body_length) {
363 php_http_message_parser_state_push(parser, PHP_HTTP_MESSAGE_PARSER_STATE_BODY_LENGTH);
364 } else {
365 php_http_message_parser_state_push(parser, PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE);
366 }
367 break;
368 }
369
370 if ((*message)->type == PHP_HTTP_REQUEST) {
371 php_http_message_parser_state_push(parser, PHP_HTTP_MESSAGE_PARSER_STATE_DONE);
372 } else {
373 php_http_message_parser_state_push(parser, PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DUMB);
374 }
375 }
376 break;
377 }
378
379 case PHP_HTTP_MESSAGE_PARSER_STATE_BODY:
380 {
381 if (len) {
382 if (parser->inflate) {
383 char *dec_str = NULL;
384 size_t dec_len;
385
386 if (SUCCESS != php_http_encoding_stream_update(parser->inflate, str, len, &dec_str, &dec_len)) {
387 return php_http_message_parser_state_push(parser, PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE);
388 }
389
390 if (str != buffer->data) {
391 PTR_FREE(str);
392 }
393 str = dec_str;
394 len = dec_len;
395 }
396
397 php_stream_write(php_http_message_body_stream((*message)->body), str, len);
398 }
399
400 if (cut) {
401 php_http_buffer_cut(buffer, 0, cut);
402 }
403
404 if (str != buffer->data) {
405 PTR_FREE(str);
406 }
407
408 str = NULL;
409 len = 0;
410 cut = 0;
411 break;
412 }
413
414 case PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DUMB:
415 {
416 str = buffer->data;
417 len = buffer->used;
418 cut = len;
419
420 php_http_message_parser_state_push(parser, PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE);
421 php_http_message_parser_state_push(parser, PHP_HTTP_MESSAGE_PARSER_STATE_BODY);
422 break;
423 }
424
425 case PHP_HTTP_MESSAGE_PARSER_STATE_BODY_LENGTH:
426 {
427 len = MIN(parser->body_length, buffer->used);
428 str = buffer->data;
429 cut = len;
430
431 parser->body_length -= len;
432
433 if (parser->body_length) {
434 php_http_message_parser_state_push(parser, PHP_HTTP_MESSAGE_PARSER_STATE_BODY_LENGTH);
435 } else {
436 php_http_message_parser_state_push(parser, PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE);
437 }
438 php_http_message_parser_state_push(parser, PHP_HTTP_MESSAGE_PARSER_STATE_BODY);
439 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 char *dec_str = NULL;
453 size_t dec_len;
454
455 if (SUCCESS != php_http_encoding_stream_update(parser->dechunk, buffer->data, buffer->used, &dec_str, &dec_len)) {
456 return PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE;
457 }
458
459 str = dec_str;
460 len = dec_len;
461
462 if (php_http_encoding_stream_done(parser->dechunk)) {
463 cut = buffer->used - PHP_HTTP_BUFFER(parser->dechunk->ctx)->used;
464 php_http_message_parser_state_push(parser, PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE);
465 php_http_message_parser_state_push(parser, PHP_HTTP_MESSAGE_PARSER_STATE_BODY);
466 } else {
467 cut = buffer->used;
468 php_http_message_parser_state_push(parser, PHP_HTTP_MESSAGE_PARSER_STATE_BODY_CHUNKED);
469 php_http_message_parser_state_push(parser, PHP_HTTP_MESSAGE_PARSER_STATE_BODY);
470 }
471 break;
472 }
473
474 case PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE:
475 {
476 php_http_message_parser_state_push(parser, PHP_HTTP_MESSAGE_PARSER_STATE_DONE);
477
478 if (parser->dechunk && parser->dechunk->ctx) {
479 char *dec_str = NULL;
480 size_t dec_len;
481
482 if (SUCCESS != php_http_encoding_stream_finish(parser->dechunk, &dec_str, &dec_len)) {
483 return php_http_message_parser_state_push(parser, PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE);
484 }
485 php_http_encoding_stream_dtor(parser->dechunk);
486
487 if (dec_str && dec_len) {
488 str = dec_str;
489 len = dec_len;
490 cut = 0;
491 php_http_message_parser_state_push(parser, PHP_HTTP_MESSAGE_PARSER_STATE_UPDATE_CL);
492 php_http_message_parser_state_push(parser, PHP_HTTP_MESSAGE_PARSER_STATE_BODY);
493 }
494 }
495
496 break;
497 }
498
499 case PHP_HTTP_MESSAGE_PARSER_STATE_UPDATE_CL:
500 {
501 zval zcl;
502
503 ZVAL_LONG(&zcl, php_http_message_body_size((*message)->body));
504 zend_hash_str_update(&(*message)->hdrs, "Content-Length", lenof("Content-Length"), &zcl);
505 break;
506 }
507
508 case PHP_HTTP_MESSAGE_PARSER_STATE_DONE:
509 {
510 char *ptr = buffer->data;
511
512 while (ptr - buffer->data < buffer->used && PHP_HTTP_IS_CTYPE(space, *ptr)) {
513 ++ptr;
514 }
515
516 php_http_buffer_cut(buffer, 0, ptr - buffer->data);
517
518 if (!(flags & PHP_HTTP_MESSAGE_PARSER_GREEDY)) {
519 return PHP_HTTP_MESSAGE_PARSER_STATE_DONE;
520 }
521 break;
522 }
523 }
524 }
525
526 return php_http_message_parser_state_is_ex(parser);
527 }
528
529 static zend_class_entry *php_http_message_parser_class_entry;
530 zend_class_entry *php_http_get_message_parser_class_entry(void)
531 {
532 return php_http_message_parser_class_entry;
533 }
534 static zend_object_handlers php_http_message_parser_object_handlers;
535
536 zend_object *php_http_message_parser_object_new(zend_class_entry *ce)
537 {
538 return &php_http_message_parser_object_new_ex(ce, NULL)->zo;
539 }
540
541 php_http_message_parser_object_t *php_http_message_parser_object_new_ex(zend_class_entry *ce, php_http_message_parser_t *parser)
542 {
543 php_http_message_parser_object_t *o;
544
545 o = ecalloc(1, sizeof(*o) + zend_object_properties_size(ce));
546 zend_object_std_init(&o->zo, ce);
547 object_properties_init(&o->zo, ce);
548
549 if (parser) {
550 o->parser = parser;
551 } else {
552 o->parser = php_http_message_parser_init(NULL);
553 }
554 php_http_buffer_init(&o->buffer);
555 o->zo.handlers = &php_http_message_parser_object_handlers;
556
557 return o;
558 }
559
560 void php_http_message_parser_object_free(zend_object *object)
561 {
562 php_http_message_parser_object_t *o = PHP_HTTP_OBJ(object, NULL);
563
564 if (o->parser) {
565 php_http_message_parser_free(&o->parser);
566 }
567 php_http_buffer_dtor(&o->buffer);
568 zend_object_std_dtor(object);
569 }
570
571 ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageParser_getState, 0, 0, 0)
572 ZEND_END_ARG_INFO();
573 static PHP_METHOD(HttpMessageParser, getState)
574 {
575 php_http_message_parser_object_t *parser_obj = PHP_HTTP_OBJ(NULL, getThis());
576
577 zend_parse_parameters_none();
578 /* always return the real state */
579 RETVAL_LONG(php_http_message_parser_state_is_ex(parser_obj->parser));
580 }
581
582 ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageParser_parse, 0, 0, 3)
583 ZEND_ARG_INFO(0, data)
584 ZEND_ARG_INFO(0, flags)
585 ZEND_ARG_INFO(1, message)
586 ZEND_END_ARG_INFO();
587 static PHP_METHOD(HttpMessageParser, parse)
588 {
589 php_http_message_parser_object_t *parser_obj;
590 zval *zmsg;
591 char *data_str;
592 size_t data_len;
593 zend_long flags;
594
595 php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "slz", &data_str, &data_len, &flags, &zmsg), invalid_arg, return);
596
597 parser_obj = PHP_HTTP_OBJ(NULL, getThis());
598 php_http_buffer_append(&parser_obj->buffer, data_str, data_len);
599 RETVAL_LONG(php_http_message_parser_parse(parser_obj->parser, &parser_obj->buffer, flags, &parser_obj->parser->message));
600
601 ZVAL_DEREF(zmsg);
602 zval_dtor(zmsg);
603 ZVAL_NULL(zmsg);
604 if (parser_obj->parser->message) {
605 php_http_message_t *msg_cpy = php_http_message_copy(parser_obj->parser->message, NULL);
606 php_http_message_object_t *msg_obj = php_http_message_object_new_ex(php_http_message_get_class_entry(), msg_cpy);
607 ZVAL_OBJ(zmsg, &msg_obj->zo);
608 }
609 }
610
611 ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageParser_stream, 0, 0, 3)
612 ZEND_ARG_INFO(0, stream)
613 ZEND_ARG_INFO(0, flags)
614 ZEND_ARG_INFO(1, message)
615 ZEND_END_ARG_INFO();
616 static PHP_METHOD(HttpMessageParser, stream)
617 {
618 php_http_message_parser_object_t *parser_obj;
619 zend_error_handling zeh;
620 zval *zmsg, *zstream;
621 php_stream *s;
622 zend_long flags;
623
624 php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "rlz", &zstream, &flags, &zmsg), invalid_arg, return);
625
626 zend_replace_error_handling(EH_THROW, php_http_get_exception_unexpected_val_class_entry(), &zeh);
627 php_stream_from_zval(s, zstream);
628 zend_restore_error_handling(&zeh);
629
630 parser_obj = PHP_HTTP_OBJ(NULL, getThis());
631 RETVAL_LONG(php_http_message_parser_parse_stream(parser_obj->parser, &parser_obj->buffer, s, flags, &parser_obj->parser->message));
632
633 ZVAL_DEREF(zmsg);
634 zval_dtor(zmsg);
635 ZVAL_NULL(zmsg);
636 if (parser_obj->parser->message) {
637 php_http_message_t *msg_cpy = php_http_message_copy(parser_obj->parser->message, NULL);
638 php_http_message_object_t *msg_obj = php_http_message_object_new_ex(php_http_message_get_class_entry(), msg_cpy);
639 ZVAL_OBJ(zmsg, &msg_obj->zo);
640 }
641 }
642
643 static zend_function_entry php_http_message_parser_methods[] = {
644 PHP_ME(HttpMessageParser, getState, ai_HttpMessageParser_getState, ZEND_ACC_PUBLIC)
645 PHP_ME(HttpMessageParser, parse, ai_HttpMessageParser_parse, ZEND_ACC_PUBLIC)
646 PHP_ME(HttpMessageParser, stream, ai_HttpMessageParser_stream, ZEND_ACC_PUBLIC)
647 {NULL, NULL, NULL}
648 };
649
650 PHP_MINIT_FUNCTION(http_message_parser)
651 {
652 zend_class_entry ce;
653
654 INIT_NS_CLASS_ENTRY(ce, "http\\Message", "Parser", php_http_message_parser_methods);
655 php_http_message_parser_class_entry = zend_register_internal_class(&ce);
656 memcpy(&php_http_message_parser_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
657 php_http_message_parser_class_entry->create_object = php_http_message_parser_object_new;
658 php_http_message_parser_object_handlers.clone_obj = NULL;
659 php_http_message_parser_object_handlers.free_obj = php_http_message_parser_object_free;
660 php_http_message_parser_object_handlers.offset = XtOffsetOf(php_http_message_parser_object_t, zo);
661
662 zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("CLEANUP"), PHP_HTTP_MESSAGE_PARSER_CLEANUP);
663 zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("DUMB_BODIES"), PHP_HTTP_MESSAGE_PARSER_DUMB_BODIES);
664 zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("EMPTY_REDIRECTS"), PHP_HTTP_MESSAGE_PARSER_EMPTY_REDIRECTS);
665 zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("GREEDY"), PHP_HTTP_MESSAGE_PARSER_GREEDY);
666
667 zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_FAILURE"), PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE);
668 zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_START"), PHP_HTTP_MESSAGE_PARSER_STATE_START);
669 zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_HEADER"), PHP_HTTP_MESSAGE_PARSER_STATE_HEADER);
670 zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_HEADER_DONE"), PHP_HTTP_MESSAGE_PARSER_STATE_HEADER_DONE);
671 zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_BODY"), PHP_HTTP_MESSAGE_PARSER_STATE_BODY);
672 zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_BODY_DUMB"), PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DUMB);
673 zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_BODY_LENGTH"), PHP_HTTP_MESSAGE_PARSER_STATE_BODY_LENGTH);
674 zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_BODY_CHUNKED"), PHP_HTTP_MESSAGE_PARSER_STATE_BODY_CHUNKED);
675 zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_BODY_DONE"), PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE);
676 zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_UPDATE_CL"), PHP_HTTP_MESSAGE_PARSER_STATE_UPDATE_CL);
677 zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_DONE"), PHP_HTTP_MESSAGE_PARSER_STATE_DONE);
678
679 return SUCCESS;
680 }
681
682 /*
683 * Local variables:
684 * tab-width: 4
685 * c-basic-offset: 4
686 * End:
687 * vim600: noet sw=4 ts=4 fdm=marker
688 * vim<600: noet sw=4 ts=4
689 */
690