explicit client requeue
authorMichael Wallner <mike@php.net>
Thu, 2 Sep 2021 18:35:32 +0000 (20:35 +0200)
committerMichael Wallner <mike@php.net>
Tue, 7 Sep 2021 07:30:08 +0000 (09:30 +0200)
src/php_http_client.c
src/php_http_client.h
src/php_http_client_curl.c
tests/client009.phpt
tests/client027.phpt

index 32679352af14b5e3ee05199c1a975c8cc9fca920..a272fa45d0a4f6c2658b20273b828b57f7c0097f 100644 (file)
@@ -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;
index fd77204fc6a53b772931b7ec36931f8486c20835..da53b4d717a744820b4e27129091fe849b100055 100644 (file)
@@ -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;
index 8898b5dda91ec37b0ea8f5c73c8085809342e733..daadf698e5b15f8058f7cd4a590e253fe652e8ba 100644 (file)
@@ -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
 };
index 6aea2255cd35b2654550f248f446597ca7a3c87a..0eb08f73a96f0c2e779a677854da3c3788713522 100644 (file)
@@ -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
index 19c7bd4ff12e8ca0057f52f0756716f02d8fe73f..b87fa653dd108aba9f320e10e185950f72cc08e7 100644 (file)
@@ -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);