From b6eeba772d69a317ee541c95c43bd0459444fec8 Mon Sep 17 00:00:00 2001 From: Michael Wallner Date: Thu, 2 Sep 2021 20:35:32 +0200 Subject: [PATCH] explicit client requeue --- src/php_http_client.c | 49 ++++++++++++++++++++----- src/php_http_client.h | 2 + src/php_http_client_curl.c | 75 +++++++++++++++++++++++++++++++++----- tests/client009.phpt | 37 ++++++++++++------- tests/client027.phpt | 1 + 5 files changed, 132 insertions(+), 32 deletions(-) diff --git a/src/php_http_client.c b/src/php_http_client.c index 3267935..a272fa4 100644 --- a/src/php_http_client.c +++ b/src/php_http_client.c @@ -254,6 +254,20 @@ ZEND_RESULT_CODE php_http_client_dequeue(php_http_client_t *h, php_http_message_ return FAILURE; } +ZEND_RESULT_CODE php_http_client_requeue(php_http_client_t *h, php_http_message_t *request) +{ + if (h->ops->dequeue) { + php_http_client_enqueue_t *enqueue = php_http_client_enqueued(h, request, NULL); + + if (!enqueue) { + php_error_docref(NULL, E_WARNING, "Failed to requeue request; request not in queue"); + return FAILURE; + } + return h->ops->requeue(h, enqueue); + } + return FAILURE; +} + php_http_client_enqueue_t *php_http_client_enqueued(php_http_client_t *h, void *compare_arg, php_http_client_enqueue_cmp_func_t compare_func) { zend_llist_element *el = NULL; @@ -632,9 +646,8 @@ static PHP_METHOD(HttpClient, reset) RETVAL_ZVAL(getThis(), 1, 0); } -static HashTable *combined_options(zval *client, zval *request) +static HashTable *combined_options(HashTable *options, zval *client, zval *request) { - HashTable *options; unsigned num_options = 0; zval z_roptions, z_options_tmp, *z_coptions = zend_read_property(php_http_client_class_entry, Z_OBJ_P(client), ZEND_STRL("options"), 0, &z_options_tmp); @@ -649,8 +662,12 @@ static HashTable *combined_options(zval *client, zval *request) num_options = num; } } - ALLOC_HASHTABLE(options); - ZEND_INIT_SYMTABLE_EX(options, num_options, 0); + if (options) { + zend_hash_clean(options); + } else { + ALLOC_HASHTABLE(options); + ZEND_INIT_SYMTABLE_EX(options, num_options, 0); + } if (Z_TYPE_P(z_coptions) == IS_ARRAY) { array_copy(Z_ARRVAL_P(z_coptions), options); } @@ -713,7 +730,7 @@ static PHP_METHOD(HttpClient, enqueue) Z_ADDREF_P(request); q.request = msg_obj->message; - q.options = combined_options(getThis(), request); + q.options = combined_options(NULL, getThis(), request); q.dtor = msg_queue_dtor; q.opaque = msg_obj; q.closure.fci = fci; @@ -769,19 +786,33 @@ static PHP_METHOD(HttpClient, requeue) zend_fcall_info_cache fcc = empty_fcall_info_cache; php_http_client_object_t *obj; php_http_message_object_t *msg_obj; - php_http_client_enqueue_t q; + php_http_client_enqueue_t q, *e; php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "O|f", &request, php_http_get_client_request_class_entry(), &fci, &fcc), invalid_arg, return); obj = PHP_HTTP_OBJ(NULL, getThis()); msg_obj = PHP_HTTP_OBJ(NULL, request); - if (php_http_client_enqueued(obj->client, msg_obj->message, NULL)) { - php_http_expect(SUCCESS == php_http_client_dequeue(obj->client, msg_obj->message), runtime, return); + if ((e = php_http_client_enqueued(obj->client, msg_obj->message, NULL))) { + combined_options(e->options, getThis(), request); + php_http_expect(SUCCESS == php_http_client_requeue(obj->client, msg_obj->message), runtime, return); + if (fci.size) { + if (e->closure.fci.size) { + zval_ptr_dtor(&e->closure.fci.function_name); + if (e->closure.fci.object) { + zend_object_release(e->closure.fci.object); + } + } + Z_TRY_ADDREF(fci.function_name); + if (fci.object) { + GC_ADDREF(fci.object); + } + } + RETURN_ZVAL(getThis(), 1, 0); } q.request = msg_obj->message; - q.options = combined_options(getThis(), request); + q.options = combined_options(NULL, getThis(), request); q.dtor = msg_queue_dtor; q.opaque = msg_obj; q.closure.fci = fci; diff --git a/src/php_http_client.h b/src/php_http_client.h index fd77204..da53b4d 100644 --- a/src/php_http_client.h +++ b/src/php_http_client.h @@ -47,6 +47,7 @@ typedef int (*php_http_client_once_func_t)(struct php_http_client *p); typedef ZEND_RESULT_CODE (*php_http_client_wait_func_t)(struct php_http_client *p, struct timeval *custom_timeout); typedef ZEND_RESULT_CODE (*php_http_client_enqueue_func_t)(struct php_http_client *p, php_http_client_enqueue_t *enqueue); typedef ZEND_RESULT_CODE (*php_http_client_dequeue_func_t)(struct php_http_client *p, php_http_client_enqueue_t *enqueue); +typedef ZEND_RESULT_CODE (*php_http_client_requeue_func_t)(struct php_http_client *p, php_http_client_enqueue_t *enqueue); typedef ZEND_RESULT_CODE (*php_http_client_setopt_func_t)(struct php_http_client *p, php_http_client_setopt_opt_t opt, void *arg); typedef ZEND_RESULT_CODE (*php_http_client_getopt_func_t)(struct php_http_client *h, php_http_client_getopt_opt_t opt, void *arg, void **res); @@ -61,6 +62,7 @@ typedef struct php_http_client_ops { php_http_client_once_func_t once; php_http_client_enqueue_func_t enqueue; php_http_client_dequeue_func_t dequeue; + php_http_client_requeue_func_t requeue; php_http_client_setopt_func_t setopt; php_http_client_getopt_func_t getopt; } php_http_client_ops_t; diff --git a/src/php_http_client_curl.c b/src/php_http_client_curl.c index 8898b5d..daadf69 100644 --- a/src/php_http_client_curl.c +++ b/src/php_http_client_curl.c @@ -863,14 +863,14 @@ static ZEND_RESULT_CODE php_http_curle_option_set_cookiestore(php_http_option_t } #if DEBUG_COOKIES - fprintf(stderr, "CURLOPT_COOKIEFILE: %s\n", cookiestore); + fprintf(stderr, "CURLOPT_COOKIEFILE: %s\n", storage->cookiestore); #endif // does NOT enable ch->data.cookies until transfer; adds to ch->stsate.cookielist if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_COOKIEFILE, storage->cookiestore ? storage->cookiestore : "")) { return FAILURE; } #if DEBUG_COOKIES - fprintf(stderr, "CURLOPT_COOKIEJAR: %s\n", cookiestore); + fprintf(stderr, "CURLOPT_COOKIEJAR: %s\n", storage->cookiestore); #endif // enables ch->data.cookies if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_COOKIEJAR, storage->cookiestore)) { @@ -2121,6 +2121,12 @@ static ZEND_RESULT_CODE php_http_client_curl_handler_reset(php_http_client_curl_ php_http_buffer_reset(&handler->options.cookies); php_http_buffer_reset(&handler->options.ranges); + if (php_http_message_body_size(handler->response.body)) { + php_http_message_body_free(&handler->response.body); + handler->response.body = php_http_message_body_init(NULL, NULL); + } + php_http_buffer_reset(&handler->response.headers); + #if ZTS curl_easy_setopt(ch, CURLOPT_NOSIGNAL, 1L); #endif @@ -2296,18 +2302,18 @@ static void php_http_client_curl_handler_clear(php_http_client_curl_handler_t *h curl_easy_setopt(handler->handle, CURLOPT_VERBOSE, 0L); curl_easy_setopt(handler->handle, CURLOPT_DEBUGFUNCTION, NULL); /* see gh issue #84 */ -#if PHP_HTTP_CURL_VERSION(7,63,0) && !PHP_HTTP_CURL_VERSION(7,65,0) - { - php_http_curle_storage_t *st = php_http_curle_get_storage(handler->handle); - curl_easy_setopt(handler->handle, CURLOPT_COOKIEJAR, st ? st->cookiestore : NULL); - } -#endif #if DEBUG_COOKIES fprintf(stderr, "CURLOPT_COOKIELIST: FLUSH\n"); fprintf(stderr, "CURLOPT_SHARE: (null)\n"); #endif curl_easy_setopt(handler->handle, CURLOPT_COOKIELIST, "FLUSH"); curl_easy_setopt(handler->handle, CURLOPT_SHARE, NULL); +#if PHP_HTTP_CURL_VERSION(7,63,0) && !PHP_HTTP_CURL_VERSION(7,65,0) + { + php_http_curle_storage_t *st = php_http_curle_get_storage(handler->handle); + curl_easy_setopt(handler->handle, CURLOPT_COOKIEJAR, st ? st->cookiestore : NULL); + } +#endif } static void php_http_client_curl_handler_dtor(php_http_client_curl_handler_t *handler) @@ -2387,6 +2393,17 @@ static void queue_dtor(php_http_client_enqueue_t *e) php_http_client_curl_handler_dtor(handler); } +static void retire_ch(php_persistent_handle_factory_t *f, void **handle) +{ + CURL *ch = *handle; + /* erase all cookies */ + if (ch) { + curl_easy_reset(ch); + curl_easy_setopt(ch, CURLOPT_COOKIELIST, "ALL"); + curl_easy_setopt(ch, CURLOPT_COOKIEFILE, NULL); + } +} + static php_resource_factory_t *create_rf(php_http_client_t *h, php_http_client_enqueue_t *enqueue) { php_persistent_handle_factory_t *pf = NULL; @@ -2417,7 +2434,7 @@ static php_resource_factory_t *create_rf(php_http_client_t *h, php_http_client_e id_len = spprintf(&id_str, 0, "%.*s:%s:%d", (int) phf->ident->len, phf->ident->val, STR_PTR(url->host), port); id = php_http_cs2zs(id_str, id_len); - pf = php_persistent_handle_concede(NULL, PHP_HTTP_G->client.curl.driver.request_name, id, NULL, NULL); + pf = php_persistent_handle_concede(NULL, PHP_HTTP_G->client.curl.driver.request_name, id, NULL, retire_ch); zend_string_release(id); } @@ -2475,6 +2492,43 @@ static ZEND_RESULT_CODE php_http_client_curl_enqueue(php_http_client_t *h, php_h return SUCCESS; } +static ZEND_RESULT_CODE php_http_client_curl_requeue(php_http_client_t *h, php_http_client_enqueue_t *enqueue) +{ + CURLMcode rs; + php_http_client_curl_t *curl = h->ctx; + php_http_client_curl_handler_t *handler = enqueue->opaque; + php_http_client_progress_state_t *progress; + + if (SUCCESS != php_http_client_curl_handler_reset(handler)) { + return FAILURE; + } + + if (SUCCESS != php_http_client_curl_handler_prepare(handler, enqueue)) { + return FAILURE; + } + + if (CURLM_OK != (rs = curl_multi_remove_handle(curl->handle->multi, handler->handle))) { + php_error_docref(NULL, E_WARNING, "Could not dequeue request: %s", curl_multi_strerror(rs)); + return FAILURE; + } + + if (CURLM_OK != (rs = curl_multi_add_handle(curl->handle->multi, handler->handle))) { + zend_llist_del_element(&h->requests, handler->handle, (int (*)(void *, void *)) compare_queue); + php_error_docref(NULL, E_WARNING, "Could not enqueue request: %s", curl_multi_strerror(rs)); + return FAILURE; + } + + ++curl->unfinished; + + if (h->callback.progress.func && SUCCESS == php_http_client_getopt(h, PHP_HTTP_CLIENT_OPT_PROGRESS_INFO, enqueue->request, &progress)) { + progress->info = "start"; + h->callback.progress.func(h->callback.progress.arg, h, &handler->queue, progress); + progress->started = 1; + } + + return SUCCESS; +} + static ZEND_RESULT_CODE php_http_client_curl_dequeue(php_http_client_t *h, php_http_client_enqueue_t *enqueue) { CURLMcode rs; @@ -2591,6 +2645,8 @@ static ZEND_RESULT_CODE php_http_client_curl_setopt(php_http_client_t *h, php_ht { php_http_client_curl_t *curl = h->ctx; + (void) curl; + switch (opt) { case PHP_HTTP_CLIENT_OPT_CONFIGURATION: return php_http_options_apply(&php_http_curlm_options, (HashTable *) arg, h); @@ -2695,6 +2751,7 @@ static php_http_client_ops_t php_http_client_curl_ops = { php_http_client_curl_once, php_http_client_curl_enqueue, php_http_client_curl_dequeue, + php_http_client_curl_requeue, php_http_client_curl_setopt, php_http_client_curl_getopt }; diff --git a/tests/client009.phpt b/tests/client009.phpt index 6aea225..0eb08f7 100644 --- a/tests/client009.phpt +++ b/tests/client009.phpt @@ -19,24 +19,33 @@ function x($a) { } server("env.inc", function($port) { + $client = new http\Client; + $client->setCookies(array("test" => "bar")); + $client->addCookies(array("foo" => "test")); $request = new http\Client\Request("GET", "http://localhost:$port"); + $client->enqueue($request); + $client->send(); + echo $client->getResponse()->getBody()->toString(); - foreach (http\Client::getAvailableDrivers() as $driver) { - $client = new http\Client($driver); - $client->setCookies(array("test" => "bar")); - $client->addCookies(array("foo" => "test")); - $client->enqueue($request); - $client->send(); - var_dump($client->getResponse()->getBody()->toString()); - $request->setOptions(array("cookies" => x($client->getCookies()))); - $client->requeue($request); - $client->send(); - var_dump($client->getResponse()->getBody()->toString()); - } + $request->setOptions(array("cookies" => x($client->getCookies()))); + $client->requeue($request); + $client->send(); + + echo $client->getResponse()->getBody()->toString(); }); ?> Done ---EXPECTREGEX-- +--EXPECT-- Test -(?:string\(46\) "Array\n\(\n \[test\] \=\> bar\n \[foo\] \=\> test\n\)\n"\nstring\(46\) "Array\n\(\n \[test\] \=\> test\n \[foo\] \=\> bar\n\)\n"\n)+Done +Array +( + [test] => bar + [foo] => test +) +Array +( + [test] => test + [foo] => bar +) +Done diff --git a/tests/client027.phpt b/tests/client027.phpt index 19c7bd4..b87fa65 100644 --- a/tests/client027.phpt +++ b/tests/client027.phpt @@ -25,6 +25,7 @@ server("cookie.inc", function($port) { $client->requeue($request); $client->send(); dump_responses($client, ["counter" => 2]); + $client->dequeue($request); $request = new http\Client\Request("GET", "http://localhost:$port?r2"); $client->enqueue($request); -- 2.30.2