From 6aca56d611a22eb559098f3c02c31634a6f9ff9f Mon Sep 17 00:00:00 2001 From: Michael Wallner Date: Fri, 25 Aug 2006 14:59:46 +0000 Subject: [PATCH] - disallow detaching requests while executing progress callbacks - hopefully the final fix for nested exceptions --- http_api.c | 18 +++++++++--------- http_exception_object.c | 33 +++++++++++++++++++++++++++------ http_request_api.c | 2 ++ http_request_pool_api.c | 27 +++++++++++---------------- http_requestpool_object.c | 11 ++++++----- package2.xml | 1 + php_http_api.h | 4 +--- php_http_request_api.h | 2 ++ php_http_request_pool_api.h | 8 ++------ tests/HttpRequestPool_006.phpt | 28 +++++++++++++++++++++++----- 10 files changed, 84 insertions(+), 50 deletions(-) diff --git a/http_api.c b/http_api.c index 372265a..a2a7f6c 100644 --- a/http_api.c +++ b/http_api.c @@ -96,15 +96,15 @@ void _http_error_ex(long type TSRMLS_DC, long code, const char *format, ...) va_start(args, format); #ifdef ZEND_ENGINE_2 - if ((type == E_THROW) || (PG(error_handling) == EH_THROW)) { - char *message; - zend_class_entry *ce = http_exception_get_for_code(code); - - http_try { - vspprintf(&message, 0, format, args); - zend_throw_exception(ce, message, code TSRMLS_CC); - efree(message); - } http_catch(ce); + if ((type == E_THROW) || (PG(error_handling) == EH_THROW)) { + char *message; + zend_class_entry *ce = http_exception_get_for_code(code); + + http_try { + vspprintf(&message, 0, format, args); + zend_throw_exception(ce, message, code TSRMLS_CC); + efree(message); + } http_catch(PG(exception_class) ? PG(exception_class) : HTTP_EX_DEF_CE); } else #endif php_verror(NULL, "", type, format, args TSRMLS_CC); diff --git a/http_exception_object.c b/http_exception_object.c index 4562922..80e9952 100644 --- a/http_exception_object.c +++ b/http_exception_object.c @@ -16,7 +16,12 @@ #ifdef ZEND_ENGINE_2 +#ifndef HTTP_DBG_EXCEPTIONS +# define HTTP_DBG_EXCEPTIONS 0 +#endif + #include "zend_interfaces.h" +#include "zend_exceptions.h" #include "php_http_exception_object.h" zend_class_entry *http_exception_object_ce; @@ -34,18 +39,29 @@ zend_class_entry *HTTP_EX_CE(response); zend_class_entry *HTTP_EX_CE(url); zend_class_entry *HTTP_EX_CE(querystring); -#define HTTP_EMPTY_ARGS(method) HTTP_EMPTY_ARGS_EX(HttpException, method, 0) +#define HTTP_EMPTY_ARGS(method) HTTP_EMPTY_ARGS_EX(HttpException, method, 0) #define HTTP_EXCEPTION_ME(method, visibility) PHP_ME(HttpException, method, HTTP_ARGS(HttpException, method), visibility) HTTP_EMPTY_ARGS(__toString); -#define OBJ_PROP_CE http_deflatestream_object_ce zend_function_entry http_exception_object_fe[] = { HTTP_EXCEPTION_ME(__toString, ZEND_ACC_PUBLIC) EMPTY_FUNCTION_ENTRY }; +#if HTTP_DBG_EXCEPTIONS +static void http_exception_hook(zval *ex TSRMLS_DC) +{ + if (ex) { + zval *m = zend_read_property(Z_OBJCE_P(ex), ex, "message", lenof("message"), 0 TSRMLS_CC); + fprintf(stderr, "*** Threw exception '%s'\n", Z_STRVAL_P(m)); + } else { + fprintf(stderr, "*** Threw NULL exception\n"); + } +} +#endif + PHP_MINIT_FUNCTION(http_exception_object) { HTTP_REGISTER_CLASS(HttpException, http_exception_object, ZEND_EXCEPTION_GET_DEFAULT(), 0); @@ -80,6 +96,10 @@ PHP_MINIT_FUNCTION(http_exception_object) HTTP_LONG_CONSTANT("HTTP_E_URL", HTTP_E_URL); HTTP_LONG_CONSTANT("HTTP_E_QUERYSTRING", HTTP_E_QUERYSTRING); +#if HTTP_DBG_EXCEPTIONS + zend_throw_exception_hook=http_exception_hook; +#endif + return SUCCESS; } @@ -122,15 +142,15 @@ PHP_METHOD(HttpException, __toString) do { ce = Z_OBJCE_P(zobj); - if (zobj != getThis()) { - phpstr_appends(&full_str, " inner "); - } - m = zend_read_property(ce, zobj, "message", lenof("message"), 0 TSRMLS_CC); f = zend_read_property(ce, zobj, "file", lenof("file"), 0 TSRMLS_CC); l = zend_read_property(ce, zobj, "line", lenof("line"), 0 TSRMLS_CC); if (m && f && l && Z_TYPE_P(m) == IS_STRING && Z_TYPE_P(f) == IS_STRING && Z_TYPE_P(l) == IS_LONG) { + if (zobj != getThis()) { + phpstr_appends(&full_str, " inner "); + } + phpstr_appendf(&full_str, "exception '%.*s' with message '%.*s' in %.*s:%ld" PHP_EOL, ce->name_length, ce->name, Z_STRLEN_P(m), Z_STRVAL_P(m), Z_STRLEN_P(f), Z_STRVAL_P(f), Z_LVAL_P(l) ); @@ -149,6 +169,7 @@ PHP_METHOD(HttpException, __toString) RETURN_PHPSTR_VAL(&full_str); } + #endif /* diff --git a/http_request_api.c b/http_request_api.c index b6d7b1e..b253a97 100644 --- a/http_request_api.c +++ b/http_request_api.c @@ -899,7 +899,9 @@ static int http_curl_progress_callback(void *ctx, double dltotal, double dlnow, add_assoc_double(param, "ulnow", ulnow); with_error_handling(EH_NORMAL, NULL) { + request->_in_progress_cb = 1; call_user_function(EG(function_table), NULL, request->_progress_callback, &retval, 1, ¶m TSRMLS_CC); + request->_in_progress_cb = 0; } end_error_handling(); zval_ptr_dtor(¶m); diff --git a/http_request_pool_api.c b/http_request_pool_api.c index ae12525..5b04472 100644 --- a/http_request_pool_api.c +++ b/http_request_pool_api.c @@ -80,12 +80,12 @@ PHP_HTTP_API STATUS _http_request_pool_attach(http_request_pool *pool, zval *req if (req->pool) { http_error_ex(HE_WARNING, HTTP_E_INVALID_PARAM, "HttpRequest object(#%d) is already member of %s HttpRequestPool", Z_OBJ_HANDLE_P(request), req->pool == pool ? "this" : "another"); } else if (SUCCESS != http_request_object_requesthandler(req, request)) { - http_error_ex(HE_WARNING, HTTP_E_REQUEST, "Could not initialize HttpRequest object for attaching to the HttpRequestPool"); + http_error_ex(HE_WARNING, HTTP_E_REQUEST, "Could not initialize HttpRequest object(#%d) for attaching to the HttpRequestPool", Z_OBJ_HANDLE_P(request)); } else { CURLMcode code = curl_multi_add_handle(pool->ch, req->request->ch); if ((CURLM_OK != code) && (CURLM_CALL_MULTI_PERFORM != code)) { - http_error_ex(HE_WARNING, HTTP_E_REQUEST_POOL, "Could not attach HttpRequest object to the HttpRequestPool: %s", curl_multi_strerror(code)); + http_error_ex(HE_WARNING, HTTP_E_REQUEST_POOL, "Could not attach HttpRequest object(#%d) to the HttpRequestPool: %s", Z_OBJ_HANDLE_P(request), curl_multi_strerror(code)); } else { req->pool = pool; @@ -119,8 +119,10 @@ PHP_HTTP_API STATUS _http_request_pool_detach(http_request_pool *pool, zval *req #endif } else if (req->pool != pool) { http_error_ex(HE_WARNING, HTTP_E_INVALID_PARAM, "HttpRequest object(#%d) is not attached to this HttpRequestPool", Z_OBJ_HANDLE_P(request)); + } else if (req->request->_in_progress_cb) { + http_error_ex(HE_WARNING, HTTP_E_REQUEST_POOL, "HttpRequest object(#%d) cannot be detached from the HttpRequestPool while executing the progress callback", Z_OBJ_HANDLE_P(request)); } else if (CURLM_OK != (code = curl_multi_remove_handle(pool->ch, req->request->ch))) { - http_error_ex(HE_WARNING, HTTP_E_REQUEST_POOL, "Could not detach HttpRequest object from the HttpRequestPool: %s", curl_multi_strerror(code)); + http_error_ex(HE_WARNING, HTTP_E_REQUEST_POOL, "Could not detach HttpRequest object(#%d) from the HttpRequestPool: %s", Z_OBJ_HANDLE_P(request), curl_multi_strerror(code)); } else { req->pool = NULL; zend_llist_del_element(&pool->finished, request, http_request_pool_compare_handles); @@ -213,7 +215,7 @@ PHP_HTTP_API STATUS _http_request_pool_send(http_request_pool *pool TSRMLS_DC) fprintf(stderr, "Attempt to send %d requests of pool %p\n", zend_llist_count(&pool->handles), pool); #endif - while (http_request_pool_perform(pool, 0)) { + while (http_request_pool_perform(pool)) { if (SUCCESS != http_request_pool_select(pool)) { #ifdef PHP_WIN32 /* see http://msdn.microsoft.com/library/en-us/winsock/winsock/windows_sockets_error_codes_2.asp */ @@ -274,7 +276,7 @@ PHP_HTTP_API STATUS _http_request_pool_select(http_request_pool *pool) /* }}} */ /* {{{ int http_request_pool_perform(http_request_pool *) */ -PHP_HTTP_API int _http_request_pool_perform(http_request_pool *pool, int once TSRMLS_DC) +PHP_HTTP_API int _http_request_pool_perform(http_request_pool *pool TSRMLS_DC) { CURLMsg *msg; int remaining = 0; @@ -284,20 +286,13 @@ PHP_HTTP_API int _http_request_pool_perform(http_request_pool *pool, int once TS while ((msg = curl_multi_info_read(pool->ch, &remaining))) { if (CURLMSG_DONE == msg->msg) { if (CURLE_OK != msg->data.result) { - http_request_pool_try { - http_request *r = NULL; - curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, &r); - http_error_ex(HE_WARNING, HTTP_E_REQUEST, "%s; %s (%s)", curl_easy_strerror(msg->data.result), r?r->_error:"", r?r->url:""); - } http_request_pool_catch(); + http_request *r = NULL; + curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, &r); + http_error_ex(HE_WARNING, HTTP_E_REQUEST, "%s; %s (%s)", curl_easy_strerror(msg->data.result), r?r->_error:"", r?r->url:""); } - http_request_pool_try { - http_request_pool_apply_with_arg(pool, _http_request_pool_responsehandler, msg->easy_handle); - } http_request_pool_catch(); + http_request_pool_apply_with_arg(pool, _http_request_pool_responsehandler, msg->easy_handle); } } - if (once || !pool->unfinished) { - http_request_pool_final(); - } return pool->unfinished; } diff --git a/http_requestpool_object.c b/http_requestpool_object.c index 665a65c..69c3f96 100644 --- a/http_requestpool_object.c +++ b/http_requestpool_object.c @@ -194,15 +194,13 @@ PHP_METHOD(HttpRequestPool, __construct) for (i = 0; i < argc; ++i) { if (Z_TYPE_PP(argv[i]) == IS_OBJECT && instanceof_function(Z_OBJCE_PP(argv[i]), http_request_object_ce TSRMLS_CC)) { - http_request_pool_try { - http_request_pool_attach(&obj->pool, *(argv[i])); - } http_request_pool_catch(); + http_request_pool_attach(&obj->pool, *(argv[i])); } } - http_request_pool_final(); } efree(argv); SET_EH_NORMAL(); + http_final(HTTP_EX_CE(request_pool)); } /* }}} */ @@ -311,6 +309,9 @@ PHP_METHOD(HttpRequestPool, send) SET_EH_THROW_HTTP(); status = http_request_pool_send(&obj->pool); SET_EH_NORMAL(); + + /* rethrow as HttpRequestPoolException */ + http_final(HTTP_EX_CE(request_pool)); RETURN_SUCCESS(status); } @@ -353,7 +354,7 @@ PHP_METHOD(HttpRequestPool, socketPerform) NO_ARGS; - if (0 < http_request_pool_perform(&obj->pool, 1)) { + if (0 < http_request_pool_perform(&obj->pool)) { RETURN_TRUE; } else { RETURN_FALSE; diff --git a/package2.xml b/package2.xml index fe7c749..75e5556 100644 --- a/package2.xml +++ b/package2.xml @@ -40,6 +40,7 @@ support. Parallel requests are available for PHP 5 and greater. BSD, revised detach($this); + static $i = array(); + if (empty($i[$this->getUrl()])) { + $i[$this->getUrl()] = true; + try { + $GLOBALS['p']->detach($this); + } catch (Exception $ex) { + echo $ex, "\n"; + } + } } -} -class r2 extends HttpRequest { function onFinish() { $GLOBALS['p']->detach($this); } } -$p = new HttpRequestPool(new r1("at.php.net"), new r2("de.php.net")); +$p = new HttpRequestPool(new r("http://at.php.net"), new r("http://de.php.net")); $p->send(); var_dump($p->getAttachedRequests()); echo "Done\n"; ?> --EXPECTF-- %sTEST +exception 'HttpRequestPoolException' with message 'HttpRequest object(#%d) cannot be detached from the HttpRequestPool while executing the progress callback' in %sHttpRequestPool_006.php:%d +Stack trace: +#0 %sHttpRequestPool_006.php(%d): HttpRequestPool->detach(Object(r)) +#1 [internal function]: r->onProgress(Array) +#2 %sHttpRequestPool_006.php(%d): HttpRequestPool->send() +#3 {main} +exception 'HttpRequestPoolException' with message 'HttpRequest object(#%d) cannot be detached from the HttpRequestPool while executing the progress callback' in %sHttpRequestPool_006.php:%d +Stack trace: +#0 %sHttpRequestPool_006.php(%d): HttpRequestPool->detach(Object(r)) +#1 [internal function]: r->onProgress(Array) +#2 %sHttpRequestPool_006.php(%d): HttpRequestPool->send() +#3 {main} array(0) { } Done -- 2.30.2