X-Git-Url: https://git.m6w6.name/?p=m6w6%2Fext-http;a=blobdiff_plain;f=php_http_client_curl.c;h=18f1dec89ab2d27159219f08fba2013576366edd;hp=c6e80b45c237a033a048c248693db84b8362d069;hb=refs%2Fheads%2Fv2.4.x;hpb=22d8bd9cce8969e37269f1e817da0904a6af522c diff --git a/php_http_client_curl.c b/php_http_client_curl.c index c6e80b4..18f1dec 100644 --- a/php_http_client_curl.c +++ b/php_http_client_curl.c @@ -218,23 +218,18 @@ static int php_http_curle_progress_callback(void *ctx, double dltotal, double dl return 0; } -static curlioerr php_http_curle_ioctl_callback(CURL *ch, curliocmd cmd, void *ctx) +static int php_http_curle_seek_callback(void *userdata, curl_off_t offset, int origin) { - php_http_message_body_t *body = ctx; + php_http_message_body_t *body = userdata; + TSRMLS_FETCH_FROM_CTX(body->ts); - if (cmd != CURLIOCMD_RESTARTREAD) { - return CURLIOE_UNKNOWNCMD; + if (!body) { + return 1; } - - if (body) { - TSRMLS_FETCH_FROM_CTX(body->ts); - - if (SUCCESS == php_stream_rewind(php_http_message_body_stream(body))) { - return CURLIOE_OK; - } + if (0 == php_stream_seek(php_http_message_body_stream(body), offset, origin)) { + return 0; } - - return CURLIOE_FAILRESTART; + return 2; } static int php_http_curle_raw_callback(CURL *ch, curl_infotype type, char *data, size_t length, void *ctx) @@ -319,7 +314,7 @@ static int php_http_curle_body_callback(char *data, size_t n, size_t l, void *ar return php_http_message_body_append(h->response.body, data, n*l); } -static STATUS php_http_curle_get_info(CURL *ch, HashTable *info) +static ZEND_RESULT_CODE php_http_curle_get_info(CURL *ch, HashTable *info) { char *c; long l; @@ -415,17 +410,6 @@ static STATUS php_http_curle_get_info(CURL *ch, HashTable *info) add_assoc_zval_ex(&array, "ssl_engines", sizeof("ssl_engines"), subarray); curl_slist_free_all(s); } - if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_COOKIELIST, &s)) { - MAKE_STD_ZVAL(subarray); - array_init(subarray); - for (p = s; p; p = p->next) { - if (p->data) { - add_next_index_string(subarray, p->data, 1); - } - } - add_assoc_zval_ex(&array, "cookies", sizeof("cookies"), subarray); - curl_slist_free_all(s); - } if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_REDIRECT_URL, &c)) { add_assoc_string_ex(&array, "redirect_url", sizeof("redirect_url"), c ? c : "", 1); } @@ -822,13 +806,13 @@ static void php_http_curlm_timer_callback(CURLM *multi, long timeout_ms, void *t /* curl options */ -static php_http_options_t php_http_curle_options; +static php_http_options_t php_http_curle_options, php_http_curlm_options; #define PHP_HTTP_CURLE_OPTION_CHECK_STRLEN 0x0001 #define PHP_HTTP_CURLE_OPTION_CHECK_BASEDIR 0x0002 #define PHP_HTTP_CURLE_OPTION_TRANSFORM_MS 0x0004 -static STATUS php_http_curle_option_set_ssl_verifyhost(php_http_option_t *opt, zval *val, void *userdata) +static ZEND_RESULT_CODE php_http_curle_option_set_ssl_verifyhost(php_http_option_t *opt, zval *val, void *userdata) { php_http_client_curl_handler_t *curl = userdata; CURL *ch = curl->handle; @@ -839,7 +823,7 @@ static STATUS php_http_curle_option_set_ssl_verifyhost(php_http_option_t *opt, z return SUCCESS; } -static STATUS php_http_curle_option_set_cookiestore(php_http_option_t *opt, zval *val, void *userdata) +static ZEND_RESULT_CODE php_http_curle_option_set_cookiestore(php_http_option_t *opt, zval *val, void *userdata) { php_http_client_curl_handler_t *curl = userdata; CURL *ch = curl->handle; @@ -861,7 +845,7 @@ static STATUS php_http_curle_option_set_cookiestore(php_http_option_t *opt, zval return SUCCESS; } -static STATUS php_http_curle_option_set_cookies(php_http_option_t *opt, zval *val, void *userdata) +static ZEND_RESULT_CODE php_http_curle_option_set_cookies(php_http_option_t *opt, zval *val, void *userdata) { php_http_client_curl_handler_t *curl = userdata; CURL *ch = curl->handle; @@ -908,7 +892,7 @@ static STATUS php_http_curle_option_set_cookies(php_http_option_t *opt, zval *va return SUCCESS; } -static STATUS php_http_curle_option_set_encodecookies(php_http_option_t *opt, zval *val, void *userdata) +static ZEND_RESULT_CODE php_http_curle_option_set_encodecookies(php_http_option_t *opt, zval *val, void *userdata) { php_http_client_curl_handler_t *curl = userdata; @@ -916,7 +900,7 @@ static STATUS php_http_curle_option_set_encodecookies(php_http_option_t *opt, zv return SUCCESS; } -static STATUS php_http_curle_option_set_lastmodified(php_http_option_t *opt, zval *val, void *userdata) +static ZEND_RESULT_CODE php_http_curle_option_set_lastmodified(php_http_option_t *opt, zval *val, void *userdata) { php_http_client_curl_handler_t *curl = userdata; CURL *ch = curl->handle; @@ -945,17 +929,21 @@ static STATUS php_http_curle_option_set_lastmodified(php_http_option_t *opt, zva return SUCCESS; } -static STATUS php_http_curle_option_set_compress(php_http_option_t *opt, zval *val, void *userdata) +static ZEND_RESULT_CODE php_http_curle_option_set_compress(php_http_option_t *opt, zval *val, void *userdata) { php_http_client_curl_handler_t *curl = userdata; + CURL *ch = curl->handle; - if (Z_BVAL_P(val)) { - curl->options.headers = curl_slist_append(curl->options.headers, "Accept-Encoding: gzip;q=1.0,deflate;q=0.5"); +#if !PHP_HTTP_CURL_VERSION(7,21,6) +# define CURLOPT_ACCEPT_ENCODING CURLOPT_ENCODING +#endif + if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_ACCEPT_ENCODING, Z_BVAL_P(val) ? "" : NULL)) { + return FAILURE; } return SUCCESS; } -static STATUS php_http_curle_option_set_etag(php_http_option_t *opt, zval *val, void *userdata) +static ZEND_RESULT_CODE php_http_curle_option_set_etag(php_http_option_t *opt, zval *val, void *userdata) { php_http_client_curl_handler_t *curl = userdata; php_http_buffer_t header; @@ -971,7 +959,7 @@ static STATUS php_http_curle_option_set_etag(php_http_option_t *opt, zval *val, return SUCCESS; } -static STATUS php_http_curle_option_set_range(php_http_option_t *opt, zval *val, void *userdata) +static ZEND_RESULT_CODE php_http_curle_option_set_range(php_http_option_t *opt, zval *val, void *userdata) { php_http_client_curl_handler_t *curl = userdata; CURL *ch = curl->handle; @@ -1015,7 +1003,7 @@ static STATUS php_http_curle_option_set_range(php_http_option_t *opt, zval *val, return SUCCESS; } -static STATUS php_http_curle_option_set_resume(php_http_option_t *opt, zval *val, void *userdata) +static ZEND_RESULT_CODE php_http_curle_option_set_resume(php_http_option_t *opt, zval *val, void *userdata) { php_http_client_curl_handler_t *curl = userdata; CURL *ch = curl->handle; @@ -1029,7 +1017,7 @@ static STATUS php_http_curle_option_set_resume(php_http_option_t *opt, zval *val return SUCCESS; } -static STATUS php_http_curle_option_set_retrydelay(php_http_option_t *opt, zval *val, void *userdata) +static ZEND_RESULT_CODE php_http_curle_option_set_retrydelay(php_http_option_t *opt, zval *val, void *userdata) { php_http_client_curl_handler_t *curl = userdata; @@ -1037,7 +1025,7 @@ static STATUS php_http_curle_option_set_retrydelay(php_http_option_t *opt, zval return SUCCESS; } -static STATUS php_http_curle_option_set_retrycount(php_http_option_t *opt, zval *val, void *userdata) +static ZEND_RESULT_CODE php_http_curle_option_set_retrycount(php_http_option_t *opt, zval *val, void *userdata) { php_http_client_curl_handler_t *curl = userdata; @@ -1045,7 +1033,7 @@ static STATUS php_http_curle_option_set_retrycount(php_http_option_t *opt, zval return SUCCESS; } -static STATUS php_http_curle_option_set_redirect(php_http_option_t *opt, zval *val, void *userdata) +static ZEND_RESULT_CODE php_http_curle_option_set_redirect(php_http_option_t *opt, zval *val, void *userdata) { php_http_client_curl_handler_t *curl = userdata; CURL *ch = curl->handle; @@ -1058,7 +1046,7 @@ static STATUS php_http_curle_option_set_redirect(php_http_option_t *opt, zval *v return SUCCESS; } -static STATUS php_http_curle_option_set_portrange(php_http_option_t *opt, zval *val, void *userdata) +static ZEND_RESULT_CODE php_http_curle_option_set_portrange(php_http_option_t *opt, zval *val, void *userdata) { php_http_client_curl_handler_t *curl = userdata; CURL *ch = curl->handle; @@ -1097,7 +1085,7 @@ static STATUS php_http_curle_option_set_portrange(php_http_option_t *opt, zval * } #if PHP_HTTP_CURL_VERSION(7,37,0) -static STATUS php_http_curle_option_set_proxyheader(php_http_option_t *opt, zval *val, void *userdata) +static ZEND_RESULT_CODE php_http_curle_option_set_proxyheader(php_http_option_t *opt, zval *val, void *userdata) { php_http_client_curl_handler_t *curl = userdata; TSRMLS_FETCH_FROM_CTX(curl->client->ts); @@ -1134,7 +1122,7 @@ static STATUS php_http_curle_option_set_proxyheader(php_http_option_t *opt, zval #endif #if PHP_HTTP_CURL_VERSION(7,21,3) -static STATUS php_http_curle_option_set_resolve(php_http_option_t *opt, zval *val, void *userdata) +static ZEND_RESULT_CODE php_http_curle_option_set_resolve(php_http_option_t *opt, zval *val, void *userdata) { php_http_client_curl_handler_t *curl = userdata; CURL *ch = curl->handle; @@ -1163,6 +1151,30 @@ static STATUS php_http_curle_option_set_resolve(php_http_option_t *opt, zval *va } #endif +#if PHP_HTTP_CURL_VERSION(7,21,4) && defined(PHP_HTTP_CURL_TLSAUTH_SRP) +static ZEND_RESULT_CODE php_http_curle_option_set_ssl_tlsauthtype(php_http_option_t *opt, zval *val, void *userdata) +{ + php_http_client_curl_handler_t *curl = userdata; + CURL *ch = curl->handle; + + if (val && Z_LVAL_P(val)) { + switch (Z_LVAL_P(val)) { + case CURL_TLSAUTH_SRP: + if (CURLE_OK == curl_easy_setopt(ch, CURLOPT_TLSAUTH_TYPE, PHP_HTTP_CURL_TLSAUTH_SRP)) { + return SUCCESS; + } + /* no break */ + default: + return FAILURE; + } + } + if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_TLSAUTH_TYPE, PHP_HTTP_CURL_TLSAUTH_DEF)) { + return FAILURE; + } + return SUCCESS; +} +#endif + static void php_http_curle_options_init(php_http_options_t *registry TSRMLS_DC) { php_http_option_t *opt; @@ -1190,6 +1202,13 @@ static void php_http_curle_options_init(php_http_options_t *registry TSRMLS_DC) } #endif +#if PHP_HTTP_CURL_VERSION(7,40,0) + if ((opt = php_http_option_register(registry, ZEND_STRL("unix_socket_path"), CURLOPT_UNIX_SOCKET_PATH, IS_STRING))) { + opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN; + opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_BASEDIR; + } +#endif + /* dns */ if ((opt = php_http_option_register(registry, ZEND_STRL("dns_cache_timeout"), CURLOPT_DNS_CACHE_TIMEOUT, IS_LONG))) { Z_LVAL(opt->defval) = 60; @@ -1402,6 +1421,9 @@ static void php_http_curle_options_init(php_http_options_t *registry TSRMLS_DC) ZVAL_BOOL(&opt->defval, 1); opt->setter = php_http_curle_option_set_ssl_verifyhost; } +#if PHP_HTTP_CURL_VERSION(7,41,0) + php_http_option_register(registry, ZEND_STRL("verifystatus"), CURLOPT_SSL_VERIFYSTATUS, IS_BOOL); +#endif php_http_option_register(registry, ZEND_STRL("cipher_list"), CURLOPT_SSL_CIPHER_LIST, IS_STRING); if ((opt = php_http_option_register(registry, ZEND_STRL("cainfo"), CURLOPT_CAINFO, IS_STRING))) { opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN; @@ -1450,6 +1472,17 @@ static void php_http_curle_options_init(php_http_options_t *registry TSRMLS_DC) opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN; opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_BASEDIR; } +#endif +#if PHP_HTTP_CURL_VERSION(7,21,4) && defined(PHP_HTTP_CURL_TLSAUTH_SRP) + if ((opt = php_http_option_register(registry, ZEND_STRL("tlsauthtype"), CURLOPT_TLSAUTH_TYPE, IS_LONG))) { + opt->setter = php_http_curle_option_set_ssl_tlsauthtype; + } + if ((opt = php_http_option_register(registry, ZEND_STRL("tlsauthuser"), CURLOPT_TLSAUTH_USERNAME, IS_STRING))) { + opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN; + } + if ((opt = php_http_option_register(registry, ZEND_STRL("tlsauthpass"), CURLOPT_TLSAUTH_PASSWORD, IS_STRING))) { + opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN; + } #endif } } @@ -1466,13 +1499,13 @@ static zval *php_http_curle_get_option(php_http_option_t *opt, HashTable *option return option; } -static STATUS php_http_curle_set_option(php_http_option_t *opt, zval *val, void *userdata) +static ZEND_RESULT_CODE php_http_curle_set_option(php_http_option_t *opt, zval *val, void *userdata) { php_http_client_curl_handler_t *curl = userdata; CURL *ch = curl->handle; zval tmp; CURLcode rc = CURLE_OK; - STATUS rv = SUCCESS; + ZEND_RESULT_CODE rv = SUCCESS; TSRMLS_FETCH_FROM_CTX(curl->client->ts); if (!val) { @@ -1547,10 +1580,186 @@ static STATUS php_http_curle_set_option(php_http_option_t *opt, zval *val, void return rv; } +#if PHP_HTTP_CURL_VERSION(7,30,0) +static ZEND_RESULT_CODE php_http_curlm_option_set_pipelining_bl(php_http_option_t *opt, zval *value, void *userdata) +{ + php_http_client_t *client = userdata; + php_http_client_curl_t *curl = client->ctx; + CURLM *ch = curl->handle; + HashTable tmp_ht; + char **bl = NULL; + TSRMLS_FETCH_FROM_CTX(client->ts); + + /* array of char *, ending with a NULL */ + if (value && Z_TYPE_P(value) != IS_NULL) { + zval **entry; + HashPosition pos; + HashTable *ht = HASH_OF(value); + int c = zend_hash_num_elements(ht); + char **ptr = ecalloc(c + 1, sizeof(char *)); + + bl = ptr; + + zend_hash_init(&tmp_ht, c, NULL, ZVAL_PTR_DTOR, 0); + array_join(ht, &tmp_ht, 0, ARRAY_JOIN_STRINGIFY); + + FOREACH_HASH_VAL(pos, &tmp_ht, entry) { + *ptr++ = Z_STRVAL_PP(entry); + } + } + + if (CURLM_OK != curl_multi_setopt(ch, opt->option, bl)) { + if (bl) { + efree(bl); + zend_hash_destroy(&tmp_ht); + } + return FAILURE; + } + + if (bl) { + efree(bl); + zend_hash_destroy(&tmp_ht); + } + return SUCCESS; +} +#endif + +#if PHP_HTTP_HAVE_EVENT +static inline ZEND_RESULT_CODE php_http_curlm_use_eventloop(php_http_client_t *h, zend_bool enable) +{ + php_http_client_curl_t *curl = h->ctx; + + if ((curl->useevents = enable)) { + if (!curl->evbase) { + curl->evbase = event_base_new(); + } + if (!curl->timeout) { + curl->timeout = ecalloc(1, sizeof(struct event)); + } + curl_multi_setopt(curl->handle, CURLMOPT_SOCKETDATA, h); + curl_multi_setopt(curl->handle, CURLMOPT_SOCKETFUNCTION, php_http_curlm_socket_callback); + curl_multi_setopt(curl->handle, CURLMOPT_TIMERDATA, h); + curl_multi_setopt(curl->handle, CURLMOPT_TIMERFUNCTION, php_http_curlm_timer_callback); + } else { + curl_multi_setopt(curl->handle, CURLMOPT_SOCKETDATA, NULL); + curl_multi_setopt(curl->handle, CURLMOPT_SOCKETFUNCTION, NULL); + curl_multi_setopt(curl->handle, CURLMOPT_TIMERDATA, NULL); + curl_multi_setopt(curl->handle, CURLMOPT_TIMERFUNCTION, NULL); + } + + return SUCCESS; +} + +static ZEND_RESULT_CODE php_http_curlm_option_set_use_eventloop(php_http_option_t *opt, zval *value, void *userdata) +{ + php_http_client_t *client = userdata; + + return php_http_curlm_use_eventloop(client, value && Z_BVAL_P(value)); +} +#endif + +static void php_http_curlm_options_init(php_http_options_t *registry TSRMLS_DC) +{ + php_http_option_t *opt; + + /* set size of connection cache */ + if ((opt = php_http_option_register(registry, ZEND_STRL("maxconnects"), CURLMOPT_MAXCONNECTS, IS_LONG))) { + /* -1 == default, 0 == unlimited */ + ZVAL_LONG(&opt->defval, -1); + } + /* set max number of connections to a single host */ +#if PHP_HTTP_CURL_VERSION(7,30,0) + php_http_option_register(registry, ZEND_STRL("max_host_connections"), CURLMOPT_MAX_HOST_CONNECTIONS, IS_LONG); +#endif + /* maximum number of requests in a pipeline */ +#if PHP_HTTP_CURL_VERSION(7,30,0) + if ((opt = php_http_option_register(registry, ZEND_STRL("max_pipeline_length"), CURLMOPT_MAX_PIPELINE_LENGTH, IS_LONG))) { + ZVAL_LONG(&opt->defval, 5); + } +#endif + /* max simultaneously open connections */ +#if PHP_HTTP_CURL_VERSION(7,30,0) + php_http_option_register(registry, ZEND_STRL("max_total_connections"), CURLMOPT_MAX_TOTAL_CONNECTIONS, IS_LONG); +#endif + /* enable/disable HTTP pipelining */ + php_http_option_register(registry, ZEND_STRL("pipelining"), CURLMOPT_PIPELINING, IS_BOOL); + /* chunk length threshold for pipelining */ +#if PHP_HTTP_CURL_VERSION(7,30,0) + php_http_option_register(registry, ZEND_STRL("chunk_length_penalty_size"), CURLMOPT_CHUNK_LENGTH_PENALTY_SIZE, IS_LONG); +#endif + /* size threshold for pipelining penalty */ +#if PHP_HTTP_CURL_VERSION(7,30,0) + php_http_option_register(registry, ZEND_STRL("content_length_penalty_size"), CURLMOPT_CONTENT_LENGTH_PENALTY_SIZE, IS_LONG); +#endif + /* pipelining server blacklist */ +#if PHP_HTTP_CURL_VERSION(7,30,0) + if ((opt = php_http_option_register(registry, ZEND_STRL("pipelining_server_bl"), CURLMOPT_PIPELINING_SERVER_BL, IS_ARRAY))) { + opt->setter = php_http_curlm_option_set_pipelining_bl; + } +#endif + /* pipelining host blacklist */ +#if PHP_HTTP_CURL_VERSION(7,30,0) + if ((opt = php_http_option_register(registry, ZEND_STRL("pipelining_site_bl"), CURLMOPT_PIPELINING_SITE_BL, IS_ARRAY))) { + opt->setter = php_http_curlm_option_set_pipelining_bl; + } +#endif + /* events */ +#if PHP_HTTP_HAVE_EVENT + if ((opt = php_http_option_register(registry, ZEND_STRL("use_eventloop"), 0, IS_BOOL))) { + opt->setter = php_http_curlm_option_set_use_eventloop; + } +#endif +} + +static ZEND_RESULT_CODE php_http_curlm_set_option(php_http_option_t *opt, zval *val, void *userdata) +{ + php_http_client_t *client = userdata; + php_http_client_curl_t *curl = client->ctx; + CURLM *ch = curl->handle; + zval *orig = val; + CURLMcode rc = CURLM_UNKNOWN_OPTION; + ZEND_RESULT_CODE rv = SUCCESS; + TSRMLS_FETCH_FROM_CTX(client->ts); + + if (!val) { + val = &opt->defval; + } else if (opt->type && Z_TYPE_P(val) != opt->type && !(Z_TYPE_P(val) == IS_NULL && opt->type == IS_ARRAY)) { + val = php_http_ztyp(opt->type, val); + } + + if (opt->setter) { + rv = opt->setter(opt, val, client); + } else { + switch (opt->type) { + case IS_BOOL: + if (CURLM_OK != (rc = curl_multi_setopt(ch, opt->option, (long) Z_BVAL_P(val)))) { + rv = FAILURE; + } + break; + case IS_LONG: + if (CURLM_OK != (rc = curl_multi_setopt(ch, opt->option, Z_LVAL_P(val)))) { + rv = FAILURE; + } + break; + default: + rv = FAILURE; + break; + } + } + + if (val && val != orig && val != &opt->defval) { + zval_ptr_dtor(&val); + } + + if (rv != SUCCESS) { + php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Could not set option %s (%s)", opt->name.s, curl_easy_strerror(rc)); + } + return rv; +} /* client ops */ -static STATUS php_http_client_curl_handler_reset(php_http_client_curl_handler_t *curl) +static ZEND_RESULT_CODE php_http_client_curl_handler_reset(php_http_client_curl_handler_t *curl) { CURL *ch = curl->handle; php_http_curle_storage_t *st; @@ -1638,7 +1847,7 @@ static php_http_client_curl_handler_t *php_http_client_curl_handler_init(php_htt curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, php_http_curle_body_callback); curl_easy_setopt(handle, CURLOPT_DEBUGFUNCTION, php_http_curle_raw_callback); curl_easy_setopt(handle, CURLOPT_READFUNCTION, php_http_curle_read_callback); - curl_easy_setopt(handle, CURLOPT_IOCTLFUNCTION, php_http_curle_ioctl_callback); + curl_easy_setopt(handle, CURLOPT_SEEKFUNCTION, php_http_curle_seek_callback); #if PHP_HTTP_CURL_VERSION(7,32,0) curl_easy_setopt(handle, CURLOPT_XFERINFOFUNCTION, php_http_curle_xferinfo_callback); curl_easy_setopt(handle, CURLOPT_XFERINFODATA, handler); @@ -1656,7 +1865,7 @@ static php_http_client_curl_handler_t *php_http_client_curl_handler_init(php_htt } -static STATUS php_http_client_curl_handler_prepare(php_http_client_curl_handler_t *curl, php_http_client_enqueue_t *enqueue) +static ZEND_RESULT_CODE php_http_client_curl_handler_prepare(php_http_client_curl_handler_t *curl, php_http_client_enqueue_t *enqueue) { size_t body_size; php_http_message_t *msg = enqueue->request; @@ -1704,6 +1913,9 @@ static STATUS php_http_client_curl_handler_prepare(php_http_client_curl_handler_ } } + /* apply options */ + php_http_options_apply(&php_http_curle_options, enqueue->options, curl); + /* request headers */ php_http_message_update_headers(msg); if (zend_hash_num_elements(&msg->hdrs)) { @@ -1748,19 +1960,17 @@ static STATUS php_http_client_curl_handler_prepare(php_http_client_curl_handler_ * does not allow a request body. */ php_stream_rewind(php_http_message_body_stream(msg->body)); - curl_easy_setopt(curl->handle, CURLOPT_IOCTLDATA, msg->body); + curl_easy_setopt(curl->handle, CURLOPT_SEEKDATA, msg->body); curl_easy_setopt(curl->handle, CURLOPT_READDATA, msg->body); curl_easy_setopt(curl->handle, CURLOPT_INFILESIZE, body_size); curl_easy_setopt(curl->handle, CURLOPT_POSTFIELDSIZE, body_size); } else { - curl_easy_setopt(curl->handle, CURLOPT_IOCTLDATA, NULL); + curl_easy_setopt(curl->handle, CURLOPT_SEEKDATA, NULL); curl_easy_setopt(curl->handle, CURLOPT_READDATA, NULL); curl_easy_setopt(curl->handle, CURLOPT_INFILESIZE, 0L); curl_easy_setopt(curl->handle, CURLOPT_POSTFIELDSIZE, 0L); } - php_http_options_apply(&php_http_curle_options, enqueue->options, curl); - return SUCCESS; } @@ -1854,33 +2064,48 @@ static void queue_dtor(php_http_client_enqueue_t *e) php_http_client_curl_handler_dtor(handler); } -static php_resource_factory_t *create_rf(php_http_url_t *url TSRMLS_DC) +static php_resource_factory_t *create_rf(php_http_client_t *h, php_http_client_enqueue_t *enqueue TSRMLS_DC) { - php_persistent_handle_factory_t *pf; + php_persistent_handle_factory_t *pf = NULL; php_resource_factory_t *rf = NULL; - char *id_str = NULL; - size_t id_len; + php_http_url_t *url = enqueue->request->http.info.request.url; if (!url || (!url->host && !url->path)) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot request empty URL"); return NULL; } - id_len = spprintf(&id_str, 0, "%s:%d", STR_PTR(url->host), url->port ? url->port : 80); + /* only if the client itself is setup for persistence */ + if (h->rf->dtor == (void (*)(void*)) php_persistent_handle_abandon) { + char *id_str = NULL; + size_t id_len; + int port = url->port ? url->port : 80; + zval **zport; + + if (SUCCESS == zend_hash_find(enqueue->options, ZEND_STRS("port"), (void *) &zport)) { + zval *zcpy = php_http_ztyp(IS_LONG, *zport); + + if (Z_LVAL_P(zcpy)) { + port = Z_LVAL_P(zcpy); + } + zval_ptr_dtor(&zcpy); + } + + id_len = spprintf(&id_str, 0, "%s:%d", STR_PTR(url->host), port); + pf = php_persistent_handle_concede(NULL, ZEND_STRL("http\\Client\\Curl\\Request"), id_str, id_len, NULL, NULL TSRMLS_CC); + efree(id_str); + } - pf = php_persistent_handle_concede(NULL, ZEND_STRL("http\\Client\\Curl\\Request"), id_str, id_len, NULL, NULL TSRMLS_CC); if (pf) { rf = php_resource_factory_init(NULL, php_persistent_handle_get_resource_factory_ops(), pf, (void (*)(void*)) php_persistent_handle_abandon); } else { rf = php_resource_factory_init(NULL, &php_http_curle_resource_factory_ops, NULL, NULL); } - efree(id_str); - return rf; } -static STATUS php_http_client_curl_enqueue(php_http_client_t *h, php_http_client_enqueue_t *enqueue) +static ZEND_RESULT_CODE php_http_client_curl_enqueue(php_http_client_t *h, php_http_client_enqueue_t *enqueue) { CURLMcode rs; php_http_client_curl_t *curl = h->ctx; @@ -1889,7 +2114,7 @@ static STATUS php_http_client_curl_enqueue(php_http_client_t *h, php_http_client php_resource_factory_t *rf; TSRMLS_FETCH_FROM_CTX(h->ts); - rf = create_rf(enqueue->request->http.info.request.url TSRMLS_CC); + rf = create_rf(h, enqueue TSRMLS_CC); if (!rf) { return FAILURE; } @@ -1925,7 +2150,7 @@ static STATUS php_http_client_curl_enqueue(php_http_client_t *h, php_http_client } } -static STATUS php_http_client_curl_dequeue(php_http_client_t *h, php_http_client_enqueue_t *enqueue) +static ZEND_RESULT_CODE php_http_client_curl_dequeue(php_http_client_t *h, php_http_client_enqueue_t *enqueue) { CURLMcode rs; php_http_client_curl_t *curl = h->ctx; @@ -1970,7 +2195,7 @@ static inline void php_http_client_curl_get_timeout(php_http_client_curl_t *curl # define SELECT_ERROR -1 #endif -static STATUS php_http_client_curl_wait(php_http_client_t *h, struct timeval *custom_timeout) +static ZEND_RESULT_CODE php_http_client_curl_wait(php_http_client_t *h, struct timeval *custom_timeout) { int MAX; fd_set R, W, E; @@ -2032,7 +2257,7 @@ static int php_http_client_curl_once(php_http_client_t *h) } -static STATUS php_http_client_curl_exec(php_http_client_t *h) +static ZEND_RESULT_CODE php_http_client_curl_exec(php_http_client_t *h) { #if PHP_HTTP_HAVE_EVENT php_http_client_curl_t *curl = h->ctx; @@ -2073,11 +2298,15 @@ static STATUS php_http_client_curl_exec(php_http_client_t *h) return SUCCESS; } -static STATUS php_http_client_curl_setopt(php_http_client_t *h, php_http_client_setopt_opt_t opt, void *arg) +static ZEND_RESULT_CODE php_http_client_curl_setopt(php_http_client_t *h, php_http_client_setopt_opt_t opt, void *arg) { php_http_client_curl_t *curl = h->ctx; switch (opt) { + case PHP_HTTP_CLIENT_OPT_CONFIGURATION: + return php_http_options_apply(&php_http_curlm_options, (HashTable *) arg, h); + break; + case PHP_HTTP_CLIENT_OPT_ENABLE_PIPELINING: if (CURLM_OK != curl_multi_setopt(curl->handle, CURLMOPT_PIPELINING, (long) *((zend_bool *) arg))) { return FAILURE; @@ -2086,23 +2315,7 @@ static STATUS php_http_client_curl_setopt(php_http_client_t *h, php_http_client_ case PHP_HTTP_CLIENT_OPT_USE_EVENTS: #if PHP_HTTP_HAVE_EVENT - if ((curl->useevents = *((zend_bool *) arg))) { - if (!curl->evbase) { - curl->evbase = event_base_new(); - } - if (!curl->timeout) { - curl->timeout = ecalloc(1, sizeof(struct event)); - } - curl_multi_setopt(curl->handle, CURLMOPT_SOCKETDATA, h); - curl_multi_setopt(curl->handle, CURLMOPT_SOCKETFUNCTION, php_http_curlm_socket_callback); - curl_multi_setopt(curl->handle, CURLMOPT_TIMERDATA, h); - curl_multi_setopt(curl->handle, CURLMOPT_TIMERFUNCTION, php_http_curlm_timer_callback); - } else { - curl_multi_setopt(curl->handle, CURLMOPT_SOCKETDATA, NULL); - curl_multi_setopt(curl->handle, CURLMOPT_SOCKETFUNCTION, NULL); - curl_multi_setopt(curl->handle, CURLMOPT_TIMERDATA, NULL); - curl_multi_setopt(curl->handle, CURLMOPT_TIMERFUNCTION, NULL); - } + return php_http_curlm_use_eventloop(h, *(zend_bool *) arg); break; #endif @@ -2112,9 +2325,43 @@ static STATUS php_http_client_curl_setopt(php_http_client_t *h, php_http_client_ return SUCCESS; } -static STATUS php_http_client_curl_getopt(php_http_client_t *h, php_http_client_getopt_opt_t opt, void *arg, void **res) +static int apply_available_options(void *pDest TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) +{ + php_http_option_t *opt = pDest; + HashTable *ht; + zval *entry; + int c; + + ht = va_arg(args, HashTable*); + + MAKE_STD_ZVAL(entry); + + if ((c = zend_hash_num_elements(&opt->suboptions.options))) { + array_init_size(entry, c); + zend_hash_apply_with_arguments(&opt->suboptions.options TSRMLS_CC, apply_available_options, 1, Z_ARRVAL_P(entry)); + } else { + /* catch deliberate NULL options */ + if (Z_TYPE(opt->defval) == IS_STRING && !Z_STRVAL(opt->defval)) { + ZVAL_NULL(entry); + } else { + ZVAL_COPY_VALUE(entry, &opt->defval); + zval_copy_ctor(entry); + } + } + + if (hash_key->nKeyLength) { + zend_hash_quick_update(ht, hash_key->arKey, hash_key->nKeyLength, hash_key->h, (void *) &entry, sizeof(zval *), NULL); + } else { + zend_hash_index_update(ht, hash_key->h, (void *) &entry, sizeof(zval *), NULL); + } + + return ZEND_HASH_APPLY_KEEP; +} + +static ZEND_RESULT_CODE php_http_client_curl_getopt(php_http_client_t *h, php_http_client_getopt_opt_t opt, void *arg, void **res) { php_http_client_enqueue_t *enqueue; + TSRMLS_FETCH_FROM_CTX(h->ts); switch (opt) { case PHP_HTTP_CLIENT_OPT_PROGRESS_INFO: @@ -2135,6 +2382,14 @@ static STATUS php_http_client_curl_getopt(php_http_client_t *h, php_http_client_ } break; + case PHP_HTTP_CLIENT_OPT_AVAILABLE_OPTIONS: + zend_hash_apply_with_arguments(&php_http_curle_options.options TSRMLS_CC, apply_available_options, 1, *(HashTable **) res); + break; + + case PHP_HTTP_CLIENT_OPT_AVAILABLE_CONFIGURATION: + zend_hash_apply_with_arguments(&php_http_curlm_options.options TSRMLS_CC, apply_available_options, 1, *(HashTable **) res); + break; + default: break; } @@ -2171,8 +2426,8 @@ PHP_MINIT_FUNCTION(http_client_curl) }; if (SUCCESS != php_http_client_driver_add(&driver)) { - return FAILURE; - } + return FAILURE; + } if (SUCCESS != php_persistent_handle_provide(ZEND_STRL("http\\Client\\Curl"), &php_http_curlm_resource_factory_ops, NULL, NULL TSRMLS_CC)) { return FAILURE; @@ -2187,6 +2442,12 @@ PHP_MINIT_FUNCTION(http_client_curl) php_http_curle_options_init(options TSRMLS_CC); } + if ((options = php_http_options_init(&php_http_curlm_options, 1))) { + options->getter = php_http_option_get; + options->setter = php_http_curlm_set_option; + + php_http_curlm_options_init(options TSRMLS_CC); + } /* * HTTP Protocol Version Constants @@ -2210,6 +2471,9 @@ PHP_MINIT_FUNCTION(http_client_curl) REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "SSL_VERSION_SSLv2", CURL_SSLVERSION_SSLv2, CONST_CS|CONST_PERSISTENT); REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "SSL_VERSION_SSLv3", CURL_SSLVERSION_SSLv3, CONST_CS|CONST_PERSISTENT); REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "SSL_VERSION_ANY", CURL_SSLVERSION_DEFAULT, CONST_CS|CONST_PERSISTENT); +#if PHP_HTTP_CURL_VERSION(7,21,4) && defined(PHP_HTTP_CURL_TLSAUTH_SRP) + REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "TLSAUTH_SRP", CURL_TLSAUTH_SRP, CONST_CS|CONST_PERSISTENT); +#endif /* * DNS IPvX resolving @@ -2266,6 +2530,7 @@ PHP_MSHUTDOWN_FUNCTION(http_client_curl) php_persistent_handle_cleanup(ZEND_STRL("http\\Client\\Curl\\Request"), NULL, 0 TSRMLS_CC); php_http_options_dtor(&php_http_curle_options); + php_http_options_dtor(&php_http_curlm_options); return SUCCESS; }