]> granicus.if.org Git - php/commitdiff
Implement curl server push
authorDavey Shafik <me@daveyshafik.com>
Fri, 2 Oct 2015 07:31:10 +0000 (09:31 +0200)
committerDavey Shafik <me@daveyshafik.com>
Mon, 27 Jun 2016 08:17:32 +0000 (01:17 -0700)
ext/curl/interface.c
ext/curl/multi.c
ext/curl/php_curl.h

index e1f616e4b027153a480b0a6679c44b7a81ee1992..9449022119812996d6c579d497ca7e2bd134fa7f 100644 (file)
@@ -1337,6 +1337,9 @@ PHP_MINIT_FUNCTION(curl)
 
 #if LIBCURL_VERSION_NUM >= 0x072e00 /* Available since 7.46.0 */
        REGISTER_CURL_CONSTANT(CURLOPT_STREAM_WEIGHT);
+       REGISTER_CURL_CONSTANT(CURLMOPT_PUSHFUNCTION);
+       REGISTER_CURL_CONSTANT(CURL_PUSH_OK);
+       REGISTER_CURL_CONSTANT(CURL_PUSH_DENY);
 #endif
 
 #if LIBCURL_VERSION_NUM >= 0x072f00 /* Available since 7.47.0 */
@@ -1853,7 +1856,7 @@ PHP_FUNCTION(curl_version)
 
 /* {{{ alloc_curl_handle
  */
-static php_curl *alloc_curl_handle()
+php_curl *alloc_curl_handle()
 {
        php_curl *ch               = ecalloc(1, sizeof(php_curl));
        ch->to_free                = ecalloc(1, sizeof(struct _php_curl_free));
@@ -3399,10 +3402,12 @@ static void _php_curl_close_ex(php_curl *ch)
         *
         * Libcurl commit d021f2e8a00 fix this issue and should be part of 7.28.2
         */
-       curl_easy_setopt(ch->cp, CURLOPT_HEADERFUNCTION, curl_write_nothing);
-       curl_easy_setopt(ch->cp, CURLOPT_WRITEFUNCTION, curl_write_nothing);
+       if (ch->cp != NULL) {
+               curl_easy_setopt(ch->cp, CURLOPT_HEADERFUNCTION, curl_write_nothing);
+               curl_easy_setopt(ch->cp, CURLOPT_WRITEFUNCTION, curl_write_nothing);
 
-       curl_easy_cleanup(ch->cp);
+               curl_easy_cleanup(ch->cp);
+       }
 
        /* cURL destructors should be invoked only by last curl handle */
        if (--(*ch->clone) == 0) {
index 7bc3ba7f059314fd6453480ee2243fe6ea54eeb1..c802edf46ddb84f409707de26fc491c3fe1ac335 100644 (file)
@@ -63,6 +63,7 @@ PHP_FUNCTION(curl_multi_init)
 
        mh = ecalloc(1, sizeof(php_curlm));
        mh->multi = curl_multi_init();
+       mh->handlers = ecalloc(1, sizeof(php_curlm_handlers));
 
        zend_llist_init(&mh->easyh, sizeof(zval), _php_curl_multi_cleanup_list, 0);
 
@@ -400,6 +401,12 @@ void _php_curl_multi_close(zend_resource *rsrc) /* {{{ */
 
                curl_multi_cleanup(mh->multi);
                zend_llist_clean(&mh->easyh);
+               if (mh->handlers->server_push) {
+                       efree(mh->handlers->server_push);
+               }
+               if (mh->handlers) {
+                       efree(mh->handlers);
+               }
                efree(mh);
                rsrc->ptr = NULL;
        }
@@ -447,6 +454,83 @@ PHP_FUNCTION(curl_multi_strerror)
 /* }}} */
 #endif
 
+#if LIBCURL_VERSION_NUM >= 0x072C00 /* Available since 7.44.0 */
+
+static int _php_server_push_callback(CURL *parent_ch, CURL *easy, size_t num_headers, struct curl_pushheaders *push_headers, void *userp) /* {{{ */
+{
+       php_curl                                *ch;
+       php_curl                                *parent;
+       php_curlm                               *mh                     = (php_curlm *)userp;
+       size_t                                  rval                    = CURL_PUSH_DENY;
+       php_curlm_server_push   *t                              = mh->handlers->server_push;
+       zval                                    *pz_parent_ch   = NULL;
+       zval                                    pz_ch;
+       zval                                    headers;
+       zval                                    retval;
+       zend_resource                   *res;
+       char                                    *header;
+       int                                     error;
+       zend_fcall_info                 fci                     = empty_fcall_info;
+
+       pz_parent_ch = _php_curl_multi_find_easy_handle(mh, parent_ch);
+       if (pz_parent_ch == NULL) {
+               return rval;
+       }
+
+       parent = (php_curl*)zend_fetch_resource(Z_RES_P(pz_parent_ch), le_curl_name, le_curl);
+
+       ch = alloc_curl_handle();
+       ch->cp = easy;
+       _php_setup_easy_copy_handlers(ch, parent);
+
+       Z_ADDREF_P(pz_parent_ch);
+
+       res = zend_register_resource(ch, le_curl);
+       ZVAL_RES(&pz_ch, res);
+
+       size_t i;
+       array_init(&headers);
+       for(i=0; i<num_headers; i++) {
+               header = curl_pushheader_bynum(push_headers, i);
+               add_next_index_string(&headers, header);
+       }
+
+       zend_fcall_info_init(&t->func_name, 0, &fci, &t->fci_cache, NULL, NULL);
+
+       zend_fcall_info_argn(
+               &fci, 3,
+               pz_parent_ch,
+               &pz_ch,
+               &headers
+       );
+
+       fci.retval = &retval;
+
+       error = zend_call_function(&fci, &t->fci_cache);
+       zend_fcall_info_args_clear(&fci, 1);
+       zval_dtor(&headers);
+
+       if (error == FAILURE) {
+               php_error_docref(NULL, E_WARNING, "Cannot call the CURLMOPT_PUSHFUNCTION");
+       } else if (!Z_ISUNDEF(retval)) {
+               if (CURL_PUSH_DENY != zval_get_long(&retval)) {
+                   rval = CURL_PUSH_OK;
+
+                       /* we want to create a copy of this zval that we store in the multihandle structure element "easyh" */
+                       zval tmp_val;
+                       ZVAL_DUP(&tmp_val, &pz_ch);
+                       zend_llist_add_element(&mh->easyh, &tmp_val);
+               } else {
+                       /* libcurl will free this easy handle, avoid double free */
+                       ch->cp = NULL;
+               }
+       }
+
+       return rval;
+}
+
+#endif
+
 #if LIBCURL_VERSION_NUM >= 0x070f04 /* 7.15.4 */
 static int _php_curl_multi_setopt(php_curlm *mh, zend_long option, zval *zvalue, zval *return_value) /* {{{ */
 {
@@ -468,7 +552,29 @@ static int _php_curl_multi_setopt(php_curlm *mh, zend_long option, zval *zvalue,
 #endif
                        error = curl_multi_setopt(mh->multi, option, zval_get_long(zvalue));
                        break;
+#if LIBCURL_VERSION_NUM > 0x072D00 /* Available since 7.46.0 */
+               case CURLMOPT_PUSHFUNCTION:
+                       if (mh->handlers->server_push == NULL) {
+                               mh->handlers->server_push = ecalloc(1, sizeof(php_curlm_server_push));
+                       } else if (!Z_ISUNDEF(mh->handlers->server_push->func_name)) {
+                               zval_ptr_dtor(&mh->handlers->server_push->func_name);
+                               mh->handlers->server_push->fci_cache = empty_fcall_info_cache;
+                       }
+
+                       ZVAL_COPY(&mh->handlers->server_push->func_name, zvalue);
+                       mh->handlers->server_push->method = PHP_CURL_USER;
+                       if (!Z_ISUNDEF(mh->handlers->server_push->func_name)) {
+                               zval_ptr_dtor(&mh->handlers->server_push->func_name);
+                               mh->handlers->server_push->fci_cache = empty_fcall_info_cache;
 
+                       }
+                       error = curl_multi_setopt(mh->multi, option, _php_server_push_callback);
+                       if (error != CURLM_OK) {
+                               return 0;
+                       }
+                       error = curl_multi_setopt(mh->multi, CURLMOPT_PUSHDATA, mh);
+                       break;
+#endif
                default:
                        php_error_docref(NULL, E_WARNING, "Invalid curl multi configuration option");
                        error = CURLM_UNKNOWN_OPTION;
index 523f8032f60eca18662f5922e796129a06f4b88a..4d015f1525107c296aeb0e97d2967b88d12769f0 100644 (file)
@@ -66,6 +66,8 @@ extern int  le_curl_multi_handle;
 #define le_curl_multi_handle_name "cURL Multi Handle"
 extern int  le_curl_share_handle;
 #define le_curl_share_handle_name "cURL Share Handle"
+//extern int  le_curl_pushheaders;
+//#define le_curl_pushheaders "cURL Push Headers"
 
 PHP_MINIT_FUNCTION(curl);
 PHP_MSHUTDOWN_FUNCTION(curl);
@@ -146,7 +148,7 @@ typedef struct {
        zval                  func_name;
        zend_fcall_info_cache fci_cache;
        int                   method;
-} php_curl_progress, php_curl_fnmatch;
+} php_curl_progress, php_curl_fnmatch, php_curlm_server_push;
 
 typedef struct {
        php_curl_write    *write;
@@ -190,10 +192,15 @@ typedef struct {
 
 #define CURLOPT_SAFE_UPLOAD -1
 
+typedef struct {
+       php_curlm_server_push   *server_push;
+} php_curlm_handlers;
+
 typedef struct {
        int         still_running;
        CURLM      *multi;
        zend_llist  easyh;
+       php_curlm_handlers      *handlers;
        struct {
                int no;
        } err;
@@ -206,6 +213,7 @@ typedef struct {
        } err;
 } php_curlsh;
 
+php_curl *alloc_curl_handle();
 void _php_curl_cleanup_handle(php_curl *);
 void _php_curl_multi_cleanup_list(void *data);
 void _php_curl_verify_handlers(php_curl *ch, int reporterror);