From 0fa2672890a4fe58c3418670e57a511b3ea7189c Mon Sep 17 00:00:00 2001 From: Michael Wallner Date: Wed, 10 Aug 2016 15:14:41 +0200 Subject: [PATCH] Fix #50 Disallow dequeuing requests while executing callbacks, because libcurl still expects the handle to be attached when returning from the callback. --- src/php_http_client.c | 7 ++++++- src/php_http_client.h | 1 + src/php_http_client_curl.c | 10 ++++++---- tests/gh-issue50.phpt | 39 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 52 insertions(+), 5 deletions(-) create mode 100644 tests/gh-issue50.phpt diff --git a/src/php_http_client.c b/src/php_http_client.c index f1b1fc5..05414b5 100644 --- a/src/php_http_client.c +++ b/src/php_http_client.c @@ -435,7 +435,9 @@ static ZEND_RESULT_CODE handle_response(void *arg, php_http_client_t *client, ph zend_fcall_info_argn(&e->closure.fci TSRMLS_CC, 1, &zresponse); zend_replace_error_handling(EH_NORMAL, NULL, &zeh TSRMLS_CC); + ++client->callback.depth; zend_fcall_info_call(&e->closure.fci, &e->closure.fcc, &retval, NULL TSRMLS_CC); + --client->callback.depth; zend_restore_error_handling(&zeh TSRMLS_CC); zend_fcall_info_argn(&e->closure.fci TSRMLS_CC, 0); @@ -490,7 +492,9 @@ static void handle_progress(void *arg, php_http_client_t *client, php_http_clien args[1] = &zprogress; zend_replace_error_handling(EH_NORMAL, NULL, &zeh TSRMLS_CC); + ++client->callback.depth; php_http_object_method_call(&client_obj->notify, zclient, NULL, 2, args TSRMLS_CC); + --client->callback.depth; zend_restore_error_handling(&zeh TSRMLS_CC); zval_ptr_dtor(&zclient); @@ -516,7 +520,9 @@ static void handle_debug(void *arg, php_http_client_t *client, php_http_client_e zend_replace_error_handling(EH_NORMAL, NULL, &zeh TSRMLS_CC); if (SUCCESS == zend_fcall_info_argn(&client_obj->debug.fci TSRMLS_CC, 4, &zclient, &zreq, &ztype, &zdata)) { + ++client_obj->client->callback.depth; zend_fcall_info_call(&client_obj->debug.fci, &client_obj->debug.fcc, NULL, NULL TSRMLS_CC); + --client_obj->client->callback.depth; zend_fcall_info_args_clear(&client_obj->debug.fci, 0); } zend_restore_error_handling(&zeh TSRMLS_CC); @@ -525,7 +531,6 @@ static void handle_debug(void *arg, php_http_client_t *client, php_http_client_e zval_ptr_dtor(&zreq); zval_ptr_dtor(&ztype); zval_ptr_dtor(&zdata); - } static void response_dtor(void *data) diff --git a/src/php_http_client.h b/src/php_http_client.h index ecaa304..8c526bd 100644 --- a/src/php_http_client.h +++ b/src/php_http_client.h @@ -116,6 +116,7 @@ typedef struct php_http_client { php_http_client_debug_callback_t func; void *arg; } debug; + unsigned depth; } callback; zend_llist requests; diff --git a/src/php_http_client_curl.c b/src/php_http_client_curl.c index 18d75d7..1ec6624 100644 --- a/src/php_http_client_curl.c +++ b/src/php_http_client_curl.c @@ -182,22 +182,19 @@ static int php_http_curle_progress_callback(void *ctx, double dltotal, double dl #endif { php_http_client_curl_handler_t *h = ctx; - zend_bool update = 0; if (h->progress.dl.total != dltotal || h->progress.dl.now != dlnow || h->progress.ul.total != ultotal || h->progress.ul.now != ulnow ) { - update = 1; - h->progress.dl.total = dltotal; h->progress.dl.now = dlnow; h->progress.ul.total = ultotal; h->progress.ul.now = ulnow; } - if (update && h->client->callback.progress.func) { + if (h->client->callback.progress.func) { h->client->callback.progress.func(h->client->callback.progress.arg, h->client, &h->queue, &h->progress); } @@ -2168,6 +2165,11 @@ static ZEND_RESULT_CODE php_http_client_curl_dequeue(php_http_client_t *h, php_h php_http_client_curl_handler_t *handler = enqueue->opaque; TSRMLS_FETCH_FROM_CTX(h->ts); + if (h->callback.depth) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not dequeue request while executing callbacks"); + return FAILURE; + } + php_http_client_curl_handler_clear(handler); if (CURLM_OK == (rs = curl_multi_remove_handle(curl->handle->multi, handler->handle))) { zend_llist_del_element(&h->requests, handler->handle, (int (*)(void *, void *)) compare_queue); diff --git a/tests/gh-issue50.phpt b/tests/gh-issue50.phpt new file mode 100644 index 0000000..61e0865 --- /dev/null +++ b/tests/gh-issue50.phpt @@ -0,0 +1,39 @@ +--TEST-- +segfault with Client::setDebug and Client::dequeue() +--SKIPIF-- + +--FILE-- +enqueue(new http\Client\Request("GET", "http://example.com")); + +$c->setDebug(function(http\Client $c, http\Client\Request $r, $type, $data) { + if ($type & http\Client::DEBUG_HEADER) { + $c->dequeue($r); + } +}); + +try { + $c->send(); +} catch (Exception $e) { + echo $e; +} + +?> + +===DONE=== +--EXPECTF-- +Test +exception 'http\Exception\RuntimeException' with message 'http\Client::dequeue(): Could not dequeue request while executing callbacks' in %sgh-issue50.php:9 +Stack trace: +#0 %sgh-issue50.php(9): http\Client->dequeue(Object(http\Client\Request)) +#1 [internal function]: {closure}(Object(http\Client), Object(http\Client\Request), 18, 'GET / HTTP/1.1\r...') +#2 %sgh-issue50.php(14): http\Client->send() +#3 {main} +===DONE=== -- 2.30.2