diff --git a/.gitmodules b/.gitmodules index 35f9a76..050c654 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,7 +1,3 @@ -[submodule "libuv"] - path = libuv - url = https://github.com/joyent/libuv.git - ignore = dirty [submodule "http-parser"] path = http-parser url = https://github.com/joyent/http-parser.git diff --git a/.travis.yml b/.travis.yml index 3118cbb..14e70fa 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,13 +1,15 @@ language: php php: - - 5.3 - 5.4 - 5.5 + - 5.6 before_script: - - cd libuv && make libuv.a CFLAGS=-fPIC -s && cd .. - - phpize && ./configure && make && sudo make install + - sudo apt-add-repository -y ppa:linuxjedi/ppa + - sudo apt-get update -qq + - sudo apt-get -y install libuv-dev + - phpize && ./configure --with-uv --enable-httpparser && make && sudo make install - echo "extension=uv.so" >> `php --ini | grep "Loaded Configuration" | sed -e "s|.*:\s*||"` notifications: diff --git a/config.m4 b/config.m4 index c1d0916..8ca9311 100644 --- a/config.m4 +++ b/config.m4 @@ -1,11 +1,11 @@ -PHP_ARG_ENABLE(uv, Whether to enable the "uv" extension, -[ --enable-uv Enable "uv" extension support]) +PHP_ARG_WITH(uv, Whether to include "uv" support, +[ --with-uv[=DIR] Include "uv" support]) PHP_ARG_ENABLE(httpparser, Whether to enable the "httpparser" module, - [ --enable-httpparser Enable "httpparser" module support]) + [ --enable-httpparser Enable "httpparser" module support], no, no) PHP_ARG_ENABLE(uv-debug, for uv debug support, - [ --enable-uv-debug Enable enable uv deubg support], no, no) + [ --enable-uv-debug Enable enable uv debug support], no, no) PHP_ARG_ENABLE(dtrace, Whether to enable the "dtrace" debug, [ --enable-dtrace Enable "dtrace" support], no, no) @@ -44,32 +44,49 @@ if test $PHP_UV != "no"; then SOURCES="" if test $PHP_HTTPPARSER != "no"; then - SOURCES=" http-parser/http_parser.c" + SOURCES=" uv_http_parser.c http-parser/http_parser.c" AC_DEFINE([ENABLE_HTTPPARSER], [1], [ Enable http parser]) fi PHP_NEW_EXTENSION(uv, php_uv.c uv.c $SOURCES, $ext_shared) + PHP_ADD_EXTENSION_DEP(uv, sockets) + if test $PHP_HTTPPARSER != "no"; then PHP_ADD_INCLUDE([$ext_srcdir/http-parser]) fi - PHP_ADD_INCLUDE([$ext_srcdir/libuv/include]) - CFLAGS=" $CFLAGS -Wunused-variable -Wpointer-sign -Wimplicit-function-declaration -Winline -Wunused-macros -Wredundant-decls -Wstrict-aliasing=2 -Wswitch-enum -Wdeclaration-after-statement -Wl,libuv/libuv.a" + SEARCH_PATH="/usr/local /usr" + SEARCH_FOR="/include/uv.h" + if test -r $PHP_UV/$SEARCH_FOR; then # path given as parameter + UV_DIR=$PHP_UV + else # search default path list + AC_MSG_CHECKING([for libuv files in default path]) + for i in $SEARCH_PATH ; do + if test -r $i/$SEARCH_FOR; then + UV_DIR=$i + AC_MSG_RESULT(found in $i) + fi + done + fi - case $host in - *darwin*) - dnl these macro does not work. why? - dnl - dnl PHP_ADD_FRAMEWORK(CoreServices) - dnl PHP_ADD_FRAMEWORK(Carbon) + PHP_ADD_INCLUDE($UV_DIR/include) - CFLAGS="$CFLAGS -framework CoreServices -framework Carbon" - ;; + PHP_CHECK_LIBRARY(uv, uv_version, + [ + PHP_ADD_LIBRARY_WITH_PATH(uv, $UV_DIR/lib, UV_SHARED_LIBADD) + AC_DEFINE(HAVE_UVLIB,1,[ ]) + ],[ + AC_MSG_ERROR([wrong uv library version or library not found]) + ],[ + -L$UV_DIR/lib -lm + ]) + + case $host in *linux*) CFLAGS="$CFLAGS -lrt" esac + PHP_SUBST([CFLAGS]) PHP_SUBST(UV_SHARED_LIBADD) - PHP_SUBST([CFLAGS]) fi diff --git a/libuv b/libuv deleted file mode 160000 index 375ebce..0000000 --- a/libuv +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 375ebce068555f0ca8151b562edb5f1b263022db diff --git a/php_uv.c b/php_uv.c index 99e4b8b..6d82127 100644 --- a/php_uv.c +++ b/php_uv.c @@ -230,11 +230,8 @@ static int uv_sockaddr_handle; static int uv_lock_handle; -static int uv_httpparser_handle; - static int uv_stdio_handle; - char *php_uv_resource_map[IS_UV_MAX] = { "uv_tcp", "uv_udp", @@ -2322,20 +2319,6 @@ static inline uv_stream_t* php_uv_get_current_stream(php_uv_t *uv) return stream; } -void static destruct_httpparser(zend_rsrc_list_entry *rsrc TSRMLS_DC) -{ - php_http_parser_context *obj = (php_http_parser_context *)rsrc->ptr; - - if (obj->headers) { - zval_ptr_dtor(&obj->headers); - } - if (obj->data) { - zval_ptr_dtor(&obj->data); - } - - efree(obj); -} - void static destruct_uv_stdio(zend_rsrc_list_entry *rsrc TSRMLS_DC) { php_uv_stdio_t *obj = (php_uv_stdio_t *)rsrc->ptr; @@ -2348,134 +2331,6 @@ void static destruct_uv_stdio(zend_rsrc_list_entry *rsrc TSRMLS_DC) efree(obj); } -/* http parser callbacks */ -static int on_message_begin(http_parser *p) -{ - return 0; -} - -static int on_headers_complete(http_parser *p) -{ - return 0; -} - -static int on_message_complete(http_parser *p) -{ - php_http_parser_context *result = p->data; - result->finished = 1; - - if (result->tmp != NULL) { - efree(result->tmp); - result->tmp = NULL; - result->tmp_len = 0; - } - - return 0; -} - -#define PHP_HTTP_PARSER_PARSE_URL(flag, name) \ - if (result->handle.field_set & (1 << flag)) { \ - const char *tmp_name = at+result->handle.field_data[flag].off; \ - int length = result->handle.field_data[flag].len; \ - add_assoc_stringl(data, #name, (char*)tmp_name, length, 1); \ - } - -static int on_url_cb(http_parser *p, const char *at, size_t len) -{ - php_http_parser_context *result = p->data; - zval *data = result->data; - - http_parser_parse_url(at, len, 0, &result->handle); - add_assoc_stringl(data, "QUERY_STRING", (char*)at, len, 1); - - PHP_HTTP_PARSER_PARSE_URL(UF_SCHEMA, SCHEME); - PHP_HTTP_PARSER_PARSE_URL(UF_HOST, HOST); - PHP_HTTP_PARSER_PARSE_URL(UF_PORT, PORT); - PHP_HTTP_PARSER_PARSE_URL(UF_PATH, PATH); - PHP_HTTP_PARSER_PARSE_URL(UF_QUERY, QUERY); - PHP_HTTP_PARSER_PARSE_URL(UF_FRAGMENT, FRAGMENT); - - return 0; -} - -static int on_status_cb(http_parser *p, const char *at, size_t len) -{ - return 0; -} - -char *php_uv_strtoupper(char *s, size_t len) -{ - unsigned char *c, *e; - - c = (unsigned char *)s; - e = (unsigned char *)c+len; - - while (c < e) { - *c = toupper(*c); - if (*c == '-') *c = '_'; - c++; - } - return s; -} - - -static int header_field_cb(http_parser *p, const char *at, size_t len) -{ - php_http_parser_context *result = p->data; - - if (result->was_header_value) { - if (result->tmp != NULL) { - efree(result->tmp); - result->tmp = NULL; - result->tmp_len = 0; - } - result->tmp = estrndup(at, len); - php_uv_strtoupper(result->tmp, len); - result->tmp_len = len; - } else { - result->tmp = erealloc(result->tmp, len + result->tmp_len + 1); - memcpy(result->tmp + result->tmp_len, at, len); - result->tmp[result->tmp_len + len] = '\0'; - result->tmp_len = result->tmp_len + len; - } - - result->was_header_value = 0; - return 0; -} - -static int header_value_cb(http_parser *p, const char *at, size_t len) -{ - php_http_parser_context *result = p->data; - zval *data = result->headers; - - if (result->was_header_value) { - zval **element; - - if (zend_hash_find(Z_ARRVAL_P(data), result->tmp, result->tmp_len+1, (void **)&element) == SUCCESS) { - Z_STRVAL_PP(element) = erealloc(Z_STRVAL_PP(element), Z_STRLEN_PP(element) + len + 1); - memcpy(Z_STRVAL_PP(element) + Z_STRLEN_PP(element), at, len); - - Z_STRVAL_PP(element)[Z_STRLEN_PP(element)+len] = '\0'; - Z_STRLEN_PP(element) = Z_STRLEN_PP(element) + len; - } - } else { - add_assoc_stringl(data, result->tmp, (char*)at, len, 1); - } - - result->was_header_value = 1; - return 0; -} - -static int on_body_cb(http_parser *p, const char *at, size_t len) -{ - php_http_parser_context *result = p->data; - zval *data = result->headers; - - add_assoc_stringl(data, "BODY", (char*)at, len, 1); - - return 0; -} -/* end of callback */ /* common functions */ @@ -2717,9 +2572,12 @@ PHP_MINIT_FUNCTION(uv) uv_loop_handle = zend_register_list_destructors_ex(destruct_uv_loop, NULL, PHP_UV_LOOP_RESOURCE_NAME, module_number); uv_sockaddr_handle = zend_register_list_destructors_ex(destruct_uv_sockaddr, NULL, PHP_UV_SOCKADDR_RESOURCE_NAME, module_number); uv_lock_handle = zend_register_list_destructors_ex(destruct_uv_lock, NULL, PHP_UV_LOCK_RESOURCE_NAME, module_number); - uv_httpparser_handle = zend_register_list_destructors_ex(destruct_httpparser, NULL, PHP_UV_HTTPPARSER_RESOURCE_NAME, module_number); uv_stdio_handle = zend_register_list_destructors_ex(destruct_uv_stdio, NULL, PHP_UV_STDIO_RESOURCE_NAME, module_number); +#ifdef ENABLE_HTTPPARSER + register_httpparser(module_number); +#endif + return SUCCESS; } @@ -5095,13 +4953,13 @@ PHP_FUNCTION(uv_hrtime) */ PHP_FUNCTION(uv_exepath) { - char buffer[1024] = {0}; - size_t buffer_sz; - - buffer_sz = sizeof(buffer); - uv_exepath(buffer, &buffer_sz); - buffer[buffer_sz] = '\0'; - + char buffer[MAXPATHLEN]; + size_t buffer_sz = sizeof(buffer) / sizeof(buffer[0]); + + if (uv_exepath(buffer, &buffer_sz) == UV_EINVAL) { + RETURN_FALSE; + } + RETURN_STRINGL(buffer, buffer_sz, 1); } /* }}} */ @@ -5110,12 +4968,14 @@ PHP_FUNCTION(uv_exepath) */ PHP_FUNCTION(uv_cwd) { - char buffer[1024] = {0}; - size_t buffer_sz = sizeof(buffer); - - uv_cwd(buffer, buffer_sz); - buffer[buffer_sz] = '\0'; - + char buffer[MAXPATHLEN]; + + if (zend_parse_parameters_none() == FAILURE) { + return; + } + + uv_cwd(buffer, MAXPATHLEN); + RETURN_STRING(buffer, 1); } /* }}} */ @@ -6475,125 +6335,6 @@ PHP_FUNCTION(uv_fs_poll_stop) /* }}} */ -/* HTTP PARSER */ -ZEND_BEGIN_ARG_INFO_EX(arginfo_uv_http_parser_init, 0, 0, 1) - ZEND_ARG_INFO(0, target) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_uv_http_parser_execute, 0, 0, 3) - ZEND_ARG_INFO(0, resource) - ZEND_ARG_INFO(0, buffer) - ZEND_ARG_INFO(0, setting) -ZEND_END_ARG_INFO() - -/* {{{ proto resource uv_http_parser_init(long $target = UV::HTTP_REQUEST) -*/ -PHP_FUNCTION(uv_http_parser_init) -{ - long target = HTTP_REQUEST; - zval *header, *result; - php_http_parser_context *ctx = NULL; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, - "|l",&target) == FAILURE) { - return; - } - - ctx = emalloc(sizeof(php_http_parser_context)); - http_parser_init(&ctx->parser, target); - - MAKE_STD_ZVAL(header); - array_init(header); - - MAKE_STD_ZVAL(result); - array_init(result); - - ctx->data = result; - ctx->headers = header; - ctx->finished = 0; - ctx->was_header_value = 1; - ctx->tmp = NULL; - ctx->tmp_len = 0; - - if (target == HTTP_RESPONSE) { - ctx->is_response = 1; - } else { - ctx->is_response = 0; - } - - memset(&ctx->handle, 0, sizeof(struct http_parser_url)); - - /* setup callback */ - ctx->settings.on_message_begin = on_message_begin; - ctx->settings.on_header_field = header_field_cb; - ctx->settings.on_header_value = header_value_cb; - ctx->settings.on_url = on_url_cb; - ctx->settings.on_status = on_status_cb; - ctx->settings.on_body = on_body_cb; - ctx->settings.on_headers_complete = on_headers_complete; - ctx->settings.on_message_complete = on_message_complete; - - ZEND_REGISTER_RESOURCE(return_value, ctx, uv_httpparser_handle); -} - -/* {{{ proto bool uv_http_parser_execute(resource $parser, string $body, array &$result) -*/ -PHP_FUNCTION(uv_http_parser_execute) -{ - zval *z_parser = NULL, *result = NULL, *version = NULL, *headers = NULL; - php_http_parser_context *context; - char *body; - int body_len; - char version_buffer[4] = {0}; - size_t nparsed = 0; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, - "rs/a",&z_parser, &body, &body_len, &result) == FAILURE) { - return; - } - - ZEND_FETCH_RESOURCE(context, php_http_parser_context*, &z_parser, -1, PHP_UV_HTTPPARSER_RESOURCE_NAME, uv_httpparser_handle); - - if (context->finished == 1) { - php_error_docref(NULL TSRMLS_CC, E_NOTICE, "passed uv_parser resource has already finished."); - RETURN_FALSE; - } - - context->parser.data = context; - nparsed = http_parser_execute(&context->parser, &context->settings, body, body_len); - - if (result) { - zval_dtor(result); - } - - if (nparsed != body_len) { - zend_throw_exception_ex(zend_exception_get_default(TSRMLS_C), 0 TSRMLS_CC, "parse failed"); - return; - } - - ZVAL_ZVAL(result, context->data, 1, 0); - if (context->is_response == 0) { - add_assoc_string(result, "REQUEST_METHOD", (char*)http_method_str(context->parser.method), 1); - } else { - add_assoc_long(result, "STATUS_CODE", (long)context->parser.status_code); - } - add_assoc_long(result, "UPGRADE", (long)context->parser.upgrade); - - MAKE_STD_ZVAL(version); - snprintf(version_buffer, 4, "%d.%d", context->parser.http_major, context->parser.http_minor); - ZVAL_STRING(version, version_buffer, 1); - - MAKE_STD_ZVAL(headers); - ZVAL_ZVAL(headers, context->headers, 1, 0); - add_assoc_zval(headers, "VERSION", version); - add_assoc_zval(result, "HEADERS", headers); - - if (context->finished == 1) { - RETURN_TRUE; - } else { - RETURN_FALSE; - } -} static zend_function_entry uv_functions[] = { @@ -6770,27 +6511,28 @@ static zend_function_entry uv_functions[] = { PHP_FE(uv_signal_init, arginfo_uv_signal_init) PHP_FE(uv_signal_start, arginfo_uv_signal_start) PHP_FE(uv_signal_stop, arginfo_uv_signal_stop) +#ifdef ENABLE_HTTPPARSER /* http parser */ PHP_FE(uv_http_parser_init, arginfo_uv_http_parser_init) PHP_FE(uv_http_parser_execute, arginfo_uv_http_parser_execute) +#endif {NULL, NULL, NULL} }; - PHP_MINFO_FUNCTION(uv) { char uv_version[20]; char http_parser_version[20]; sprintf(uv_version, "%d.%d",UV_VERSION_MAJOR, UV_VERSION_MINOR); - sprintf(http_parser_version, "%d.%d",HTTP_PARSER_VERSION_MAJOR, HTTP_PARSER_VERSION_MINOR); +// sprintf(http_parser_version, "%d.%d",HTTP_PARSER_VERSION_MAJOR, HTTP_PARSER_VERSION_MINOR); php_printf("PHP libuv Extension\n"); php_info_print_table_start(); php_info_print_table_header(2,"libuv Support", "enabled"); php_info_print_table_row(2,"Version", PHP_UV_EXTVER); php_info_print_table_row(2,"libuv Version", uv_version); - php_info_print_table_row(2,"http-parser Version", http_parser_version); +// php_info_print_table_row(2,"http-parser Version", http_parser_version); php_info_print_table_end(); } diff --git a/php_uv.h b/php_uv.h index ce5b5a8..cc6dba1 100755 --- a/php_uv.h +++ b/php_uv.h @@ -31,7 +31,10 @@ #include "php.h" #include "uv.h" -#include "http_parser.h" + +#ifdef ENABLE_HTTPPARSER +#include "uv_http_parser.h" +#endif #include "php_network.h" #include "php_streams.h" @@ -189,22 +192,6 @@ typedef struct { int flags; } php_uv_stdio_t; - -typedef struct { - struct http_parser parser; - struct http_parser_url handle; - struct http_parser_settings settings; - int is_response; - int was_header_value; - int finished; - zval *data; - zval *headers; - char *tmp; - size_t tmp_len; -} php_http_parser_context; - -#define PHP_UV_HTTPPARSER_RESOURCE_NAME "uv_httpparser" - #define PHP_UV_RESOURCE_NAME "uv" #define PHP_UV_SOCKADDR_RESOURCE_NAME "uv_sockaddr" #define PHP_UV_LOOP_RESOURCE_NAME "uv_loop" diff --git a/tests/010-uv_ip4_addr.phpt b/tests/010-uv_ip4_addr.phpt index 8f128d9..de6fc4d 100644 --- a/tests/010-uv_ip4_addr.phpt +++ b/tests/010-uv_ip4_addr.phpt @@ -3,5 +3,5 @@ Check for uv_ip4_addr --FILE-- --FILE-- int(1) ["HEADERS"]=> - array(5) { + array(6) { ["UPGRADE"]=> string(9) "WebSocket" ["CONNECTION"]=> @@ -89,5 +91,7 @@ array(5) { string(18) "http://example.com" ["WEBSOCKET_PROTOCOL"]=> string(6) "sample" + ["VERSION"]=> + string(3) "1.1" } -} \ No newline at end of file +} diff --git a/uv.c b/uv.c index 9281f71..a64b9d7 100755 --- a/uv.c +++ b/uv.c @@ -139,10 +139,6 @@ static int php_uv_class_init(TSRMLS_D) zend_declare_class_constant_long(uv_class_entry, "LEAVE_GROUP", sizeof("LEAVE_GROUP")-1, UV_LEAVE_GROUP TSRMLS_CC); zend_declare_class_constant_long(uv_class_entry, "JOIN_GROUP", sizeof("JOIN_GROUP")-1, UV_JOIN_GROUP TSRMLS_CC); - zend_declare_class_constant_long(uv_class_entry, "HTTP_BOTH", sizeof("HTTP_BOTH")-1, HTTP_BOTH TSRMLS_CC); - zend_declare_class_constant_long(uv_class_entry, "HTTP_REQUEST", sizeof("HTTP_REQUEST")-1, HTTP_REQUEST TSRMLS_CC); - zend_declare_class_constant_long(uv_class_entry, "HTTP_RESPONSE", sizeof("HTTP_RESPONSE")-1, HTTP_RESPONSE TSRMLS_CC); - /* for uv_handle_type */ zend_declare_class_constant_long(uv_class_entry, "IS_UV_TCP", sizeof("IS_UV_TCP")-1, IS_UV_TCP TSRMLS_CC); zend_declare_class_constant_long(uv_class_entry, "IS_UV_UDP", sizeof("IS_UV_UDP")-1, IS_UV_UDP TSRMLS_CC); @@ -187,6 +183,13 @@ static int php_uv_class_init(TSRMLS_D) zend_declare_class_constant_long(uv_class_entry, "PROCESS_WINDOWS_VERBATIM_ARGUMENTS", sizeof("PROCESS_WINDOWS_VERBATIM_ARGUMENTS")-1, UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS TSRMLS_CC); zend_declare_class_constant_long(uv_class_entry, "PROCESS_DETACHED", sizeof("PROCESS_DETACHED")-1, UV_PROCESS_DETACHED TSRMLS_CC); +#ifdef ENABLE_HTTPPARSER + /* http parser */ + zend_declare_class_constant_long(uv_class_entry, "HTTP_BOTH", sizeof("HTTP_BOTH")-1, HTTP_BOTH TSRMLS_CC); + zend_declare_class_constant_long(uv_class_entry, "HTTP_REQUEST", sizeof("HTTP_REQUEST")-1, HTTP_REQUEST TSRMLS_CC); + zend_declare_class_constant_long(uv_class_entry, "HTTP_RESPONSE", sizeof("HTTP_RESPONSE")-1, HTTP_RESPONSE TSRMLS_CC); +#endif + #define PHP_UV_ERRNO_GEN(code_notused, name, msg_notused) zend_declare_class_constant_long(uv_class_entry, #name, sizeof(#name)-1, UV_##name TSRMLS_CC); UV_ERRNO_MAP(PHP_UV_ERRNO_GEN) #undef PHP_UV_ERRNO_GEN diff --git a/uv_http_parser.c b/uv_http_parser.c new file mode 100644 index 0000000..2d7816b --- /dev/null +++ b/uv_http_parser.c @@ -0,0 +1,258 @@ +#include "uv_http_parser.h" + +static int uv_httpparser_handle; + +void destruct_httpparser(zend_rsrc_list_entry *rsrc TSRMLS_DC) +{ + php_http_parser_context *obj = (php_http_parser_context *)rsrc->ptr; + + if (obj->headers) { + zval_ptr_dtor(&obj->headers); + } + if (obj->data) { + zval_ptr_dtor(&obj->data); + } + + efree(obj); +} + +void register_httpparser(int module_number) +{ + uv_httpparser_handle = zend_register_list_destructors_ex(destruct_httpparser, NULL, PHP_UV_HTTPPARSER_RESOURCE_NAME, module_number); +} + +/* http parser callbacks */ +static int on_message_begin(http_parser *p) +{ + return 0; +} + +static int on_headers_complete(http_parser *p) +{ + return 0; +} + +static int on_message_complete(http_parser *p) +{ + php_http_parser_context *result = p->data; + result->finished = 1; + + if (result->tmp != NULL) { + efree(result->tmp); + result->tmp = NULL; + result->tmp_len = 0; + } + + return 0; +} + +#define PHP_HTTP_PARSER_PARSE_URL(flag, name) \ + if (result->handle.field_set & (1 << flag)) { \ + const char *tmp_name = at+result->handle.field_data[flag].off; \ + int length = result->handle.field_data[flag].len; \ + add_assoc_stringl(data, #name, (char*)tmp_name, length, 1); \ + } + +static int on_url_cb(http_parser *p, const char *at, size_t len) +{ + php_http_parser_context *result = p->data; + zval *data = result->data; + + http_parser_parse_url(at, len, 0, &result->handle); + add_assoc_stringl(data, "QUERY_STRING", (char*)at, len, 1); + + PHP_HTTP_PARSER_PARSE_URL(UF_SCHEMA, SCHEME); + PHP_HTTP_PARSER_PARSE_URL(UF_HOST, HOST); + PHP_HTTP_PARSER_PARSE_URL(UF_PORT, PORT); + PHP_HTTP_PARSER_PARSE_URL(UF_PATH, PATH); + PHP_HTTP_PARSER_PARSE_URL(UF_QUERY, QUERY); + PHP_HTTP_PARSER_PARSE_URL(UF_FRAGMENT, FRAGMENT); + + return 0; +} + +static int on_status_cb(http_parser *p, const char *at, size_t len) +{ + return 0; +} + +char *php_uv_strtoupper(char *s, size_t len) +{ + unsigned char *c, *e; + + c = (unsigned char *)s; + e = (unsigned char *)c+len; + + while (c < e) { + *c = toupper(*c); + if (*c == '-') *c = '_'; + c++; + } + return s; +} + + +static int header_field_cb(http_parser *p, const char *at, size_t len) +{ + php_http_parser_context *result = p->data; + + if (result->was_header_value) { + if (result->tmp != NULL) { + efree(result->tmp); + result->tmp = NULL; + result->tmp_len = 0; + } + result->tmp = estrndup(at, len); + php_uv_strtoupper(result->tmp, len); + result->tmp_len = len; + } else { + result->tmp = erealloc(result->tmp, len + result->tmp_len + 1); + memcpy(result->tmp + result->tmp_len, at, len); + result->tmp[result->tmp_len + len] = '\0'; + result->tmp_len = result->tmp_len + len; + } + + result->was_header_value = 0; + return 0; +} + +static int header_value_cb(http_parser *p, const char *at, size_t len) +{ + php_http_parser_context *result = p->data; + zval *data = result->headers; + + if (result->was_header_value) { + zval **element; + + if (zend_hash_find(Z_ARRVAL_P(data), result->tmp, result->tmp_len+1, (void **)&element) == SUCCESS) { + Z_STRVAL_PP(element) = erealloc(Z_STRVAL_PP(element), Z_STRLEN_PP(element) + len + 1); + memcpy(Z_STRVAL_PP(element) + Z_STRLEN_PP(element), at, len); + + Z_STRVAL_PP(element)[Z_STRLEN_PP(element)+len] = '\0'; + Z_STRLEN_PP(element) = Z_STRLEN_PP(element) + len; + } + } else { + add_assoc_stringl(data, result->tmp, (char*)at, len, 1); + } + + result->was_header_value = 1; + return 0; +} + +static int on_body_cb(http_parser *p, const char *at, size_t len) +{ + php_http_parser_context *result = p->data; + zval *data = result->headers; + + add_assoc_stringl(data, "BODY", (char*)at, len, 1); + + return 0; +} +/* end of callback */ + +/* {{{ proto resource uv_http_parser_init(long $target = UV::HTTP_REQUEST) +*/ +PHP_FUNCTION(uv_http_parser_init) +{ + long target = HTTP_REQUEST; + zval *header, *result; + php_http_parser_context *ctx = NULL; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, + "|l",&target) == FAILURE) { + return; + } + + ctx = emalloc(sizeof(php_http_parser_context)); + http_parser_init(&ctx->parser, target); + + MAKE_STD_ZVAL(header); + array_init(header); + + MAKE_STD_ZVAL(result); + array_init(result); + + ctx->data = result; + ctx->headers = header; + ctx->finished = 0; + ctx->was_header_value = 1; + ctx->tmp = NULL; + ctx->tmp_len = 0; + + if (target == HTTP_RESPONSE) { + ctx->is_response = 1; + } else { + ctx->is_response = 0; + } + + memset(&ctx->handle, 0, sizeof(struct http_parser_url)); + + /* setup callback */ + ctx->settings.on_message_begin = on_message_begin; + ctx->settings.on_header_field = header_field_cb; + ctx->settings.on_header_value = header_value_cb; + ctx->settings.on_url = on_url_cb; + ctx->settings.on_status = on_status_cb; + ctx->settings.on_body = on_body_cb; + ctx->settings.on_headers_complete = on_headers_complete; + ctx->settings.on_message_complete = on_message_complete; + + ZEND_REGISTER_RESOURCE(return_value, ctx, uv_httpparser_handle); +} + +/* {{{ proto bool uv_http_parser_execute(resource $parser, string $body, array &$result) +*/ +PHP_FUNCTION(uv_http_parser_execute) +{ + zval *z_parser = NULL, *result = NULL, *version = NULL, *headers = NULL; + php_http_parser_context *context; + char *body; + int body_len; + char version_buffer[4] = {0}; + size_t nparsed = 0; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, + "rs/a", &z_parser, &body, &body_len, &result) == FAILURE) { + return; + } + + ZEND_FETCH_RESOURCE(context, php_http_parser_context*, &z_parser, -1, PHP_UV_HTTPPARSER_RESOURCE_NAME, uv_httpparser_handle); + + if (context->finished == 1) { + php_error_docref(NULL TSRMLS_CC, E_NOTICE, "passed uv_parser resource has already finished."); + RETURN_FALSE; + } + + context->parser.data = context; + nparsed = http_parser_execute(&context->parser, &context->settings, body, body_len); + + if (result) { + zval_dtor(result); + } + + if (nparsed != body_len) { + zend_throw_exception_ex(zend_exception_get_default(TSRMLS_C), 0 TSRMLS_CC, "parse failed"); + return; + } + + ZVAL_ZVAL(result, context->data, 1, 0); + if (context->is_response == 0) { + add_assoc_string(result, "REQUEST_METHOD", (char*)http_method_str(context->parser.method), 1); + } else { + add_assoc_long(result, "STATUS_CODE", (long)context->parser.status_code); + } + add_assoc_long(result, "UPGRADE", (long)context->parser.upgrade); + + MAKE_STD_ZVAL(version); + snprintf(version_buffer, 4, "%d.%d", context->parser.http_major, context->parser.http_minor); + ZVAL_STRING(version, version_buffer, 1); + + MAKE_STD_ZVAL(headers); + ZVAL_ZVAL(headers, context->headers, 1, 0); + + add_assoc_zval(headers, "VERSION", version); + add_assoc_zval(result, "HEADERS", headers); + + RETURN_BOOL(context->finished); +} + diff --git a/uv_http_parser.h b/uv_http_parser.h new file mode 100644 index 0000000..0379773 --- /dev/null +++ b/uv_http_parser.h @@ -0,0 +1,40 @@ +#ifndef UV_HTTPPARSER_H +#define UV_HTTPPARSER_H + +#include "php.h" +#include "zend_exceptions.h" + +#include "http_parser.h" + +typedef struct { + struct http_parser parser; + struct http_parser_url handle; + struct http_parser_settings settings; + int is_response; + int was_header_value; + int finished; + zval *data; + zval *headers; + char *tmp; + size_t tmp_len; +} php_http_parser_context; + +#define PHP_UV_HTTPPARSER_RESOURCE_NAME "uv_httpparser" + +void register_httpparser(int module_number); + +/* HTTP PARSER */ +ZEND_BEGIN_ARG_INFO_EX(arginfo_uv_http_parser_init, 0, 0, 1) + ZEND_ARG_INFO(0, target) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_uv_http_parser_execute, 0, 0, 3) + ZEND_ARG_INFO(0, resource) + ZEND_ARG_INFO(0, buffer) + ZEND_ARG_INFO(0, setting) +ZEND_END_ARG_INFO() + +PHP_FUNCTION(uv_http_parser_init); +PHP_FUNCTION(uv_http_parser_execute); + +#endif