From ad15e1ccdabc678103e356535919e829ba9a0281 Mon Sep 17 00:00:00 2001 From: Davey Shafik Date: Fri, 2 Oct 2015 09:31:10 +0200 Subject: [PATCH] Implement curl server push --- ext/curl/interface.c | 13 ++++-- ext/curl/multi.c | 106 +++++++++++++++++++++++++++++++++++++++++++ ext/curl/php_curl.h | 10 +++- 3 files changed, 124 insertions(+), 5 deletions(-) diff --git a/ext/curl/interface.c b/ext/curl/interface.c index e1f616e4b02..94490221198 100644 --- a/ext/curl/interface.c +++ b/ext/curl/interface.c @@ -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) { diff --git a/ext/curl/multi.c b/ext/curl/multi.c index 7bc3ba7f059..c802edf46dd 100644 --- a/ext/curl/multi.c +++ b/ext/curl/multi.c @@ -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; ifunc_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; diff --git a/ext/curl/php_curl.h b/ext/curl/php_curl.h index 523f8032f60..4d015f15251 100644 --- a/ext/curl/php_curl.h +++ b/ext/curl/php_curl.h @@ -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);