X-Git-Url: https://git.m6w6.name/?p=m6w6%2Fext-http;a=blobdiff_plain;f=php_http_message_parser.c;h=3e46674cf3dfa728a62b95a2d93f99fcbd4eb315;hp=fce24ed2cffc4e49eae22f457f66c5dad3303f9f;hb=25f0c16244fc5f8b2c9d9bfddab8a541d2521789;hpb=a07b79b1871054ca17e48b69445b4dc201f24662 diff --git a/php_http_message_parser.c b/php_http_message_parser.c index fce24ed..3e46674 100644 --- a/php_http_message_parser.c +++ b/php_http_message_parser.c @@ -1,4 +1,20 @@ -#include "php_http.h" +/* + +--------------------------------------------------------------------+ + | PECL :: http | + +--------------------------------------------------------------------+ + | Redistribution and use in source and binary forms, with or without | + | modification, are permitted provided that the conditions mentioned | + | in the accompanying LICENSE file are met. | + +--------------------------------------------------------------------+ + | Copyright (c) 2004-2011, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +#include "php_http_api.h" + +#ifndef DBG_PARSER +# define DBG_PARSER 0 +#endif typedef struct php_http_message_parser_state_spec { php_http_message_parser_state_t state; @@ -17,6 +33,17 @@ static const php_http_message_parser_state_spec_t php_http_message_parser_states {PHP_HTTP_MESSAGE_PARSER_STATE_DONE, 0} }; +#if DBG_PARSER +const char *php_http_message_parser_state_name(php_http_message_parser_state_t state) { + const char *states[] = {"START", "HEADER", "HEADER_DONE", "BODY", "BODY_DUMB", "BODY_LENGTH", "BODY_CHUNK", "BODY_DONE", "DONE"}; + + if (state < 0 || state > (sizeof(states)/sizeof(char*))-1) { + return "FAILURE"; + } + return states[state]; +} +#endif + PHP_HTTP_API php_http_message_parser_t *php_http_message_parser_init(php_http_message_parser_t *parser TSRMLS_DC) { if (!parser) { @@ -34,11 +61,11 @@ PHP_HTTP_API php_http_message_parser_t *php_http_message_parser_init(php_http_me PHP_HTTP_API php_http_message_parser_state_t php_http_message_parser_state_push(php_http_message_parser_t *parser, unsigned argc, ...) { + php_http_message_parser_state_t state; va_list va_args; unsigned i; - va_start(va_args, argc); - php_http_message_parser_state_t state; + va_start(va_args, argc); for (i = 0; i < argc; ++i) { state = va_arg(va_args, php_http_message_parser_state_t); zend_stack_push(&parser->stack, &state, sizeof(state)); @@ -90,18 +117,90 @@ PHP_HTTP_API void php_http_message_parser_free(php_http_message_parser_t **parse } } +PHP_HTTP_API php_http_message_parser_state_t php_http_message_parser_parse_stream(php_http_message_parser_t *parser, php_stream *s, unsigned flags, php_http_message_t **message) +{ + php_http_buffer_t buf; + php_http_message_parser_state_t state = PHP_HTTP_MESSAGE_PARSER_STATE_START; + TSRMLS_FETCH_FROM_CTX(parser->ts); + + php_http_buffer_init_ex(&buf, 0x1000, PHP_HTTP_BUFFER_INIT_PREALLOC); + + while (!php_stream_eof(s)) { + size_t len = 0; +#if DBG_PARSER + fprintf(stderr, "#SP: %s (f:%u)\n", php_http_message_parser_state_name(state), flags); +#endif + switch (state) { + case PHP_HTTP_MESSAGE_PARSER_STATE_START: + case PHP_HTTP_MESSAGE_PARSER_STATE_HEADER: + case PHP_HTTP_MESSAGE_PARSER_STATE_HEADER_DONE: + /* read line */ + php_stream_get_line(s, buf.data + buf.used, buf.free, &len); + php_http_buffer_account(&buf, len); + break; + + case PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DUMB: + /* read all */ + php_http_buffer_account(&buf, php_stream_read(s, buf.data + buf.used, buf.free)); + break; + + case PHP_HTTP_MESSAGE_PARSER_STATE_BODY_LENGTH: + /* read body_length */ + php_http_buffer_account(&buf, php_stream_read(s, buf.data + buf.used, MIN(buf.free, parser->body_length))); + break; + + case PHP_HTTP_MESSAGE_PARSER_STATE_BODY_CHUNKED: + /* duh, this is very naive */ + if (len) { + size_t read = php_stream_read(s, buf.data + buf.used, MIN(len, buf.free)); + + php_http_buffer_account(&buf, read); + + len -= read; + } else { + php_http_buffer_resize(&buf, 24); + php_stream_get_line(s, buf.data, buf.free, &len); + php_http_buffer_account(&buf, len); + + len = strtoul(buf.data + buf.used - len, NULL, 16); + } + break; + + case PHP_HTTP_MESSAGE_PARSER_STATE_BODY: + case PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE: + /* should not occur */ + abort(); + break; + + case PHP_HTTP_MESSAGE_PARSER_STATE_DONE: + case PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE: + php_http_buffer_dtor(&buf); + return php_http_message_parser_state_is(parser); + } + + state = php_http_message_parser_parse(parser, &buf, flags, message); + } + + php_http_buffer_dtor(&buf); + return PHP_HTTP_MESSAGE_PARSER_STATE_DONE; +} + PHP_HTTP_API 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) { - TSRMLS_FETCH_FROM_CTX(parser->ts); char *str = NULL; size_t len = 0; size_t cut = 0; + TSRMLS_FETCH_FROM_CTX(parser->ts); while (buffer->used || !php_http_message_parser_states[php_http_message_parser_state_is(parser)].need_data) { -#if 0 - const char *state[] = {"START", "HEADER", "HEADER_DONE", "BODY", "BODY_DUMB", "BODY_LENGTH", "BODY_CHUNK", "BODY_DONE", "DONE"}; - fprintf(stderr, "#MP: %s (%d)\n", php_http_message_parser_state_is(parser) < 0 ? "FAILURE" : state[php_http_message_parser_state_is(parser)], (*message)->type); +#if DBG_PARSER + fprintf(stderr, "#MP: %s (f: %u, t:%d, l:%zu)\n", + php_http_message_parser_state_name(php_http_message_parser_state_is(parser)), + flags, + message && *message ? (*message)->type : -1, + buffer->used + ); _dpf(0, buffer->data, buffer->used); #endif @@ -130,7 +229,7 @@ PHP_HTTP_API php_http_message_parser_state_t php_http_message_parser_parse(php_h { unsigned header_parser_flags = (flags & PHP_HTTP_MESSAGE_PARSER_CLEANUP) ? PHP_HTTP_HEADER_PARSER_CLEANUP : 0; - switch (php_http_header_parser_parse(&parser->header, buffer, header_parser_flags, &(*message)->hdrs, (php_http_info_callback_t) php_http_message_info_callback, message)) { + 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)) { case PHP_HTTP_HEADER_PARSER_STATE_FAILURE: return PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE; @@ -222,10 +321,16 @@ PHP_HTTP_API php_http_message_parser_state_t php_http_message_parser_parse(php_h if (h_cl) { char *stop; - parser->body_length = strtoul(Z_STRVAL_PP(h_cl), &stop, 10); + if (Z_TYPE_PP(h_cl) == IS_STRING) { + parser->body_length = strtoul(Z_STRVAL_PP(h_cl), &stop, 10); - if (stop != Z_STRVAL_PP(h_cl)) { - php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_BODY_LENGTH); + if (stop != Z_STRVAL_PP(h_cl)) { + 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); + break; + } + } else if (Z_TYPE_PP(h_cl) == IS_LONG) { + parser->body_length = Z_LVAL_PP(h_cl); + 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); break; } } @@ -251,7 +356,7 @@ PHP_HTTP_API php_http_message_parser_state_t php_http_message_parser_parse(php_h if (end >= start && (!total || end < total)) { parser->body_length = end + 1 - start; - php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_BODY_LENGTH); + 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); break; } } @@ -316,7 +421,7 @@ PHP_HTTP_API php_http_message_parser_state_t php_http_message_parser_parse(php_h len = buffer->used; cut = len; - php_http_message_parser_state_push(parser, 2, !buffer->used?PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE:PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DUMB, PHP_HTTP_MESSAGE_PARSER_STATE_BODY); + php_http_message_parser_state_push(parser, 2, PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE, PHP_HTTP_MESSAGE_PARSER_STATE_BODY); break; } @@ -353,7 +458,7 @@ PHP_HTTP_API php_http_message_parser_state_t php_http_message_parser_parse(php_h len = dec_len; if (php_http_encoding_stream_done(parser->dechunk)) { - cut = buffer->used - PHP_HTTP_BUFFER_LEN(parser->dechunk->ctx); + cut = buffer->used - PHP_HTTP_BUFFER(parser->dechunk->ctx)->used; php_http_message_parser_state_push(parser, 2, PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE, PHP_HTTP_MESSAGE_PARSER_STATE_BODY); } else { cut = buffer->used; @@ -394,6 +499,10 @@ PHP_HTTP_API php_http_message_parser_state_t php_http_message_parser_parse(php_h } php_http_buffer_cut(buffer, 0, ptr - buffer->data); + + if (!(flags & PHP_HTTP_MESSAGE_PARSER_GREEDY)) { + return PHP_HTTP_MESSAGE_PARSER_STATE_DONE; + } break; } } @@ -401,3 +510,13 @@ PHP_HTTP_API php_http_message_parser_state_t php_http_message_parser_parse(php_h return php_http_message_parser_state_is(parser); } + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ +