From 286dfcc5b1e7b24117132c7bff5b8e64ac6ac5d0 Mon Sep 17 00:00:00 2001 From: Michael Wallner Date: Tue, 7 Jun 2016 11:59:39 +0200 Subject: [PATCH] modularize curl event handler --- src/php_http_client_curl.c | 320 ++++------------------------- src/php_http_client_curl.h | 35 ++++ src/php_http_client_curl_event.c | 334 +++++++++++++++++++++++++++++++ src/php_http_client_curl_event.h | 28 +++ tests/client018.phpt | 36 ++-- tests/client022.phpt | 2 +- tests/helper/pipeline.inc | 4 +- tests/skipif.inc | 18 +- 8 files changed, 464 insertions(+), 313 deletions(-) create mode 100644 src/php_http_client_curl_event.c create mode 100644 src/php_http_client_curl_event.h diff --git a/src/php_http_client_curl.c b/src/php_http_client_curl.c index d4f0517..d3f5200 100644 --- a/src/php_http_client_curl.c +++ b/src/php_http_client_curl.c @@ -12,30 +12,10 @@ #include "php_http_api.h" #include "php_http_client.h" +#include "php_http_client_curl_event.h" #if PHP_HTTP_HAVE_CURL -#if PHP_HTTP_HAVE_EVENT -# if !PHP_HTTP_HAVE_EVENT2 && /* just be really sure */ !(LIBEVENT_VERSION_NUMBER >= 0x02000000) -# include -# define event_base_new event_init -# define event_assign(e, b, s, a, cb, d) do {\ - event_set(e, s, a, cb, d); \ - event_base_set(b, e); \ - } while(0) -# else -# if PHP_HTTP_HAVE_EVENT2 -# include -# include -# else -# error "libevent presence is unknown" -# endif -# endif -# ifndef DBG_EVENTS -# define DBG_EVENTS 0 -# endif -#endif - #ifdef PHP_HTTP_HAVE_OPENSSL # include #endif @@ -43,23 +23,6 @@ # include #endif -typedef struct php_http_client_curl_handle { - CURLM *multi; - CURLSH *share; -} php_http_client_curl_handle_t; - -typedef struct php_http_client_curl { - php_http_client_curl_handle_t *handle; - - int unfinished; /* int because of curl_multi_perform() */ - -#if PHP_HTTP_HAVE_EVENT - struct event_base *evbase; - struct event *timeout; - unsigned useevents:1; -#endif -} php_http_client_curl_t; - typedef struct php_http_client_curl_handler { CURL *handle; php_resource_factory_t *rf; @@ -651,7 +614,7 @@ static php_http_message_t *php_http_curlm_responseparser(php_http_client_curl_ha return response; } -static void php_http_curlm_responsehandler(php_http_client_t *context) +void php_http_client_curl_responsehandler(php_http_client_t *context) { int err_count = 0, remaining = 0; php_http_curle_storage_t *st, *err = NULL; @@ -704,161 +667,6 @@ static void php_http_curlm_responsehandler(php_http_client_t *context) } } -#if PHP_HTTP_HAVE_EVENT - -typedef struct php_http_curlm_event { - struct event evnt; - php_http_client_t *context; -} php_http_curlm_event_t; - -static inline int etoca(short action) { - switch (action & (EV_READ|EV_WRITE)) { - case EV_READ: - return CURL_CSELECT_IN; - break; - case EV_WRITE: - return CURL_CSELECT_OUT; - break; - case EV_READ|EV_WRITE: - return CURL_CSELECT_IN|CURL_CSELECT_OUT; - break; - default: - return 0; - } -} - -static void php_http_curlm_timeout_callback(int socket, short action, void *event_data) -{ - php_http_client_t *context = event_data; - php_http_client_curl_t *curl = context->ctx; - -#if DBG_EVENTS - fprintf(stderr, "T"); -#endif - if (curl->useevents) { - CURLMcode rc; - TSRMLS_FETCH_FROM_CTX(context->ts); - - /* ignore and use -1,0 on timeout */ - (void) socket; - (void) action; - - while (CURLM_CALL_MULTI_PERFORM == (rc = curl_multi_socket_action(curl->handle->multi, CURL_SOCKET_TIMEOUT, 0, &curl->unfinished))); - - if (CURLM_OK != rc) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", curl_multi_strerror(rc)); - } - - php_http_curlm_responsehandler(context); - } -} - -static void php_http_curlm_event_callback(int socket, short action, void *event_data) -{ - php_http_client_t *context = event_data; - php_http_client_curl_t *curl = context->ctx; - -#if DBG_EVENTS - fprintf(stderr, "E"); -#endif - if (curl->useevents) { - CURLMcode rc = CURLM_OK; - TSRMLS_FETCH_FROM_CTX(context->ts); - - while (CURLM_CALL_MULTI_PERFORM == (rc = curl_multi_socket_action(curl->handle->multi, socket, etoca(action), &curl->unfinished))); - - if (CURLM_OK != rc) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", curl_multi_strerror(rc)); - } - - php_http_curlm_responsehandler(context); - - /* remove timeout if there are no transfers left */ - if (!curl->unfinished && event_initialized(curl->timeout) && event_pending(curl->timeout, EV_TIMEOUT, NULL)) { - event_del(curl->timeout); - } - } -} - -static int php_http_curlm_socket_callback(CURL *easy, curl_socket_t sock, int action, void *socket_data, void *assign_data) -{ - php_http_client_t *context = socket_data; - php_http_client_curl_t *curl = context->ctx; - -#if DBG_EVENTS - fprintf(stderr, "S"); -#endif - if (curl->useevents) { - int events = EV_PERSIST; - php_http_curlm_event_t *ev = assign_data; - TSRMLS_FETCH_FROM_CTX(context->ts); - - if (!ev) { - ev = ecalloc(1, sizeof(php_http_curlm_event_t)); - ev->context = context; - curl_multi_assign(curl->handle->multi, sock, ev); - } else { - event_del(&ev->evnt); - } - - switch (action) { - case CURL_POLL_IN: - events |= EV_READ; - break; - case CURL_POLL_OUT: - events |= EV_WRITE; - break; - case CURL_POLL_INOUT: - events |= EV_READ|EV_WRITE; - break; - - case CURL_POLL_REMOVE: - efree(ev); - /* no break */ - case CURL_POLL_NONE: - return 0; - - default: - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown socket action %d", action); - return -1; - } - - event_assign(&ev->evnt, curl->evbase, sock, events, php_http_curlm_event_callback, context); - event_add(&ev->evnt, NULL); - } - - return 0; -} - -static void php_http_curlm_timer_callback(CURLM *multi, long timeout_ms, void *timer_data) -{ - php_http_client_t *context = timer_data; - php_http_client_curl_t *curl = context->ctx; - -#if DBG_EVENTS - fprintf(stderr, "\ntimer <- timeout_ms: %ld\n", timeout_ms); -#endif - if (curl->useevents) { - - if (timeout_ms < 0) { - php_http_curlm_timeout_callback(CURL_SOCKET_TIMEOUT, /*EV_READ|EV_WRITE*/0, context); - } else if (timeout_ms > 0 || !event_initialized(curl->timeout) || !event_pending(curl->timeout, EV_TIMEOUT, NULL)) { - struct timeval timeout; - - if (!event_initialized(curl->timeout)) { - event_assign(curl->timeout, curl->evbase, CURL_SOCKET_TIMEOUT, 0, php_http_curlm_timeout_callback, context); - } - - timeout.tv_sec = timeout_ms / 1000; - timeout.tv_usec = (timeout_ms % 1000) * 1000; - - event_add(curl->timeout, &timeout); - } - } -} - -#endif /* HAVE_EVENT */ - /* curl options */ static php_http_options_t php_http_curle_options, php_http_curlm_options; @@ -1731,22 +1539,24 @@ static inline ZEND_RESULT_CODE php_http_curlm_use_eventloop(php_http_client_t *h { php_http_client_curl_t *curl = h->ctx; - if ((curl->useevents = enable)) { - if (!curl->evbase) { - curl->evbase = event_base_new(); + if (enable) { + if (!curl->ev_ops) { + if (!(curl->ev_ops = php_http_client_curl_event_ops_get())) { + return FAILURE; + } } - if (!curl->timeout) { - curl->timeout = ecalloc(1, sizeof(struct event)); + if (curl->ev_ops && !curl->ev_ctx) { + if (!(curl->ev_ctx = curl->ev_ops->init(h))) { + return FAILURE; + } } - curl_multi_setopt(curl->handle->multi, CURLMOPT_SOCKETDATA, h); - curl_multi_setopt(curl->handle->multi, CURLMOPT_SOCKETFUNCTION, php_http_curlm_socket_callback); - curl_multi_setopt(curl->handle->multi, CURLMOPT_TIMERDATA, h); - curl_multi_setopt(curl->handle->multi, CURLMOPT_TIMERFUNCTION, php_http_curlm_timer_callback); } else { - curl_multi_setopt(curl->handle->multi, CURLMOPT_SOCKETDATA, NULL); - curl_multi_setopt(curl->handle->multi, CURLMOPT_SOCKETFUNCTION, NULL); - curl_multi_setopt(curl->handle->multi, CURLMOPT_TIMERDATA, NULL); - curl_multi_setopt(curl->handle->multi, CURLMOPT_TIMERFUNCTION, NULL); + if (curl->ev_ops) { + if (curl->ev_ctx) { + curl->ev_ops->dtor(&curl->ev_ctx); + } + curl->ev_ops = NULL; + } } return SUCCESS; @@ -1756,7 +1566,10 @@ static ZEND_RESULT_CODE php_http_curlm_option_set_use_eventloop(php_http_option_ { php_http_client_t *client = userdata; - return php_http_curlm_use_eventloop(client, value && Z_BVAL_P(value)); + if (Z_TYPE_P(value) == IS_OBJECT /* && instanceof_function */) { + abort(); + } + return php_http_curlm_use_eventloop(client, value && z_is_true(value)); } #endif @@ -2192,19 +2005,9 @@ static void php_http_client_curl_dtor(php_http_client_t *h) php_http_client_curl_t *curl = h->ctx; TSRMLS_FETCH_FROM_CTX(h->ts); -#if PHP_HTTP_HAVE_EVENT - if (curl->timeout) { - if (event_initialized(curl->timeout) && event_pending(curl->timeout, EV_TIMEOUT, NULL)) { - event_del(curl->timeout); - } - efree(curl->timeout); - curl->timeout = NULL; + if (curl->ev_ops) { + curl->ev_ops->dtor(&curl->ev_ctx); } - if (curl->evbase) { - event_base_free(curl->evbase); - curl->evbase = NULL; - } -#endif curl->unfinished = 0; php_resource_factory_handle_dtor(h->rf, curl->handle TSRMLS_CC); @@ -2340,17 +2143,6 @@ static void php_http_client_curl_reset(php_http_client_t *h) } } -static inline void php_http_client_curl_get_timeout(php_http_client_curl_t *curl, long max_tout, struct timeval *timeout) -{ - if ((CURLM_OK == curl_multi_timeout(curl->handle->multi, &max_tout)) && (max_tout > 0)) { - timeout->tv_sec = max_tout / 1000; - timeout->tv_usec = (max_tout % 1000) * 1000; - } else { - timeout->tv_sec = 0; - timeout->tv_usec = 1000; - } -} - #ifdef PHP_WIN32 # define SELECT_ERROR SOCKET_ERROR #else @@ -2364,22 +2156,9 @@ static ZEND_RESULT_CODE php_http_client_curl_wait(php_http_client_t *h, struct t struct timeval timeout; php_http_client_curl_t *curl = h->ctx; -#if PHP_HTTP_HAVE_EVENT - if (curl->useevents) { - if (!event_initialized(curl->timeout)) { - event_assign(curl->timeout, curl->evbase, CURL_SOCKET_TIMEOUT, 0, php_http_curlm_timeout_callback, h); - } else if (custom_timeout && timerisset(custom_timeout)) { - event_add(curl->timeout, custom_timeout); - } else if (!event_pending(curl->timeout, EV_TIMEOUT, NULL)) { - php_http_client_curl_get_timeout(curl, 1000, &timeout); - event_add(curl->timeout, &timeout); - } - - event_base_loop(curl->evbase, EVLOOP_ONCE); - - return SUCCESS; + if (curl->ev_ops) { + return curl->ev_ops->wait(curl->ev_ctx, custom_timeout); } -#endif FD_ZERO(&R); FD_ZERO(&W); @@ -2406,14 +2185,13 @@ static int php_http_client_curl_once(php_http_client_t *h) { php_http_client_curl_t *curl = h->ctx; -#if PHP_HTTP_HAVE_EVENT - if (curl->useevents) { - event_base_loop(curl->evbase, EVLOOP_NONBLOCK); - } else -#endif - while (CURLM_CALL_MULTI_PERFORM == curl_multi_perform(curl->handle->multi, &curl->unfinished)); + if (curl->ev_ops) { + curl->ev_ops->once(curl->ev_ctx); + } else { + while (CURLM_CALL_MULTI_PERFORM == curl_multi_perform(curl->handle->multi, &curl->unfinished)); + } - php_http_curlm_responsehandler(h); + php_http_client_curl_responsehandler(h); return curl->unfinished; @@ -2421,39 +2199,22 @@ static int php_http_client_curl_once(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; -#endif TSRMLS_FETCH_FROM_CTX(h->ts); -#if PHP_HTTP_HAVE_EVENT - if (curl->useevents) { - php_http_curlm_timeout_callback(CURL_SOCKET_TIMEOUT, /*EV_READ|EV_WRITE*/0, h); - do { - int ev_rc = event_base_dispatch(curl->evbase); - -#if DBG_EVENTS - fprintf(stderr, "%c", "X.0"[ev_rc+1]); -#endif + if (curl->ev_ops) { + return curl->ev_ops->exec(curl->ev_ctx); + } - if (ev_rc < 0) { - php_error_docref(NULL TSRMLS_CC, E_ERROR, "Error in event_base_dispatch()"); - return FAILURE; - } - } while (curl->unfinished && !EG(exception)); - } else -#endif - { - while (php_http_client_curl_once(h) && !EG(exception)) { - if (SUCCESS != php_http_client_curl_wait(h, NULL)) { + while (php_http_client_curl_once(h) && !EG(exception)) { + if (SUCCESS != php_http_client_curl_wait(h, NULL)) { #ifdef PHP_WIN32 - /* see http://msdn.microsoft.com/library/en-us/winsock/winsock/windows_sockets_error_codes_2.asp */ - php_error_docref(NULL TSRMLS_CC, E_WARNING, "WinSock error: %d", WSAGetLastError()); + /* see http://msdn.microsoft.com/library/en-us/winsock/winsock/windows_sockets_error_codes_2.asp */ + php_error_docref(NULL TSRMLS_CC, E_WARNING, "WinSock error: %d", WSAGetLastError()); #else - php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", strerror(errno)); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", strerror(errno)); #endif - return FAILURE; - } + return FAILURE; } } @@ -2579,6 +2340,7 @@ php_http_client_ops_t *php_http_client_curl_get_ops(void) return &php_http_client_curl_ops; } + PHP_MINIT_FUNCTION(http_client_curl) { curl_version_info_data *info; diff --git a/src/php_http_client_curl.h b/src/php_http_client_curl.h index c82a09c..f84107f 100644 --- a/src/php_http_client_curl.h +++ b/src/php_http_client_curl.h @@ -15,6 +15,41 @@ #if PHP_HTTP_HAVE_CURL +typedef struct php_http_client_curl_handle { + CURLM *multi; + CURLSH *share; +} php_http_client_curl_handle_t; + +typedef struct php_http_client_curl_ops { + void *(*init)(); + void (*dtor)(void **ctx_ptr); + ZEND_RESULT_CODE (*once)(void *ctx); + ZEND_RESULT_CODE (*wait)(void *ctx, struct timeval *custom_timeout); + ZEND_RESULT_CODE (*exec)(void *ctx); +} php_http_client_curl_ops_t; + +typedef struct php_http_client_curl { + php_http_client_curl_handle_t *handle; + + int unfinished; /* int because of curl_multi_perform() */ + + void *ev_ctx; + php_http_client_curl_ops_t *ev_ops; +} php_http_client_curl_t; + +static inline void php_http_client_curl_get_timeout(php_http_client_curl_t *curl, long max_tout, struct timeval *timeout) +{ + if ((CURLM_OK == curl_multi_timeout(curl->handle->multi, &max_tout)) && (max_tout > 0)) { + timeout->tv_sec = max_tout / 1000; + timeout->tv_usec = (max_tout % 1000) * 1000; + } else { + timeout->tv_sec = 0; + timeout->tv_usec = 1000; + } +} + +PHP_HTTP_API void php_http_client_curl_responsehandler(php_http_client_t *client); + PHP_MINIT_FUNCTION(http_client_curl); PHP_MSHUTDOWN_FUNCTION(http_client_curl); #endif /* PHP_HTTP_HAVE_CURL */ diff --git a/src/php_http_client_curl_event.c b/src/php_http_client_curl_event.c new file mode 100644 index 0000000..9b1808b --- /dev/null +++ b/src/php_http_client_curl_event.c @@ -0,0 +1,334 @@ +/* + +--------------------------------------------------------------------+ + | PECL :: http | + +--------------------------------------------------------------------+ + | Redistribution and use in source and binary forms, with or without | + | modification, are permitted provided that the conditions mentioned | + | in the accompanying LICENSE file are met. | + +--------------------------------------------------------------------+ + | Copyright (c) 2004-2014, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +#include "php_http_api.h" +#include "php_http_client.h" +#include "php_http_client_curl.h" + +#if PHP_HTTP_HAVE_CURL +#if PHP_HTTP_HAVE_EVENT +# if !PHP_HTTP_HAVE_EVENT2 && /* just be really sure */ !(LIBEVENT_VERSION_NUMBER >= 0x02000000) +# include +# define event_base_new event_init +# define event_assign(e, b, s, a, cb, d) do {\ + event_set(e, s, a, cb, d); \ + event_base_set(b, e); \ + } while(0) +# else +# if PHP_HTTP_HAVE_EVENT2 +# include +# include +# else +# error "libevent presence is unknown" +# endif +# endif +# ifndef DBG_EVENTS +# define DBG_EVENTS 0 +# endif + +typedef struct php_http_client_curl_event_context { + php_http_client_t *client; + struct event_base *evbase; + struct event *timeout; +} php_http_client_curl_event_context_t; + +typedef struct php_http_client_curl_event_ev { + struct event evnt; + php_http_client_curl_event_context_t *context; +} php_http_client_curl_event_ev_t; + +static inline int etoca(short action) { + switch (action & (EV_READ|EV_WRITE)) { + case EV_READ: + return CURL_CSELECT_IN; + break; + case EV_WRITE: + return CURL_CSELECT_OUT; + break; + case EV_READ|EV_WRITE: + return CURL_CSELECT_IN|CURL_CSELECT_OUT; + break; + default: + return 0; + } +} + +static void php_http_client_curl_event_handler(void *context, curl_socket_t s, int curl_action) +{ + CURLMcode rc; + php_http_client_curl_event_context_t *ctx = context; + php_http_client_curl_t *curl = ctx->client->ctx; + TSRMLS_FETCH_FROM_CTX(ctx->client->ts); + +#if DBG_EVENTS + fprintf(stderr, "H"); +#endif + + do { + rc = curl_multi_socket_action(curl->handle->multi, s, curl_action, &curl->unfinished); + } while (CURLM_CALL_MULTI_PERFORM == rc); + + if (CURLM_OK != rc) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", curl_multi_strerror(rc)); + } + + php_http_client_curl_responsehandler(ctx->client); +} + +static void php_http_client_curl_event_timeout_callback(int socket, short action, void *event_data) +{ +#if DBG_EVENTS + fprintf(stderr, "T"); +#endif + + /* ignore and use -1,0 on timeout */ + (void) socket; + (void) action; + + php_http_client_curl_event_handler(event_data, CURL_SOCKET_TIMEOUT, 0); +} + +static void php_http_client_curl_event_timer(CURLM *multi, long timeout_ms, void *timer_data) +{ + php_http_client_curl_event_context_t *context = timer_data; + +#if DBG_EVENTS + fprintf(stderr, "\ntimer <- timeout_ms: %ld\n", timeout_ms); +#endif + + if (timeout_ms < 0) { + php_http_client_curl_event_handler(context, CURL_SOCKET_TIMEOUT, 0); + } else if (timeout_ms > 0 || !event_initialized(context->timeout) || !event_pending(context->timeout, EV_TIMEOUT, NULL)) { + struct timeval timeout; + + if (!event_initialized(context->timeout)) { + event_assign(context->timeout, context->evbase, CURL_SOCKET_TIMEOUT, 0, php_http_client_curl_event_timeout_callback, context); + } + + timeout.tv_sec = timeout_ms / 1000; + timeout.tv_usec = (timeout_ms % 1000) * 1000; + + event_add(context->timeout, &timeout); + } +} + +static void php_http_client_curl_event_callback(int socket, short action, void *event_data) +{ + php_http_client_curl_event_context_t *ctx = event_data; + php_http_client_curl_t *curl = ctx->client->ctx; + +#if DBG_EVENTS + fprintf(stderr, "E"); +#endif + + php_http_client_curl_event_handler(event_data, socket, etoca(action)); + + /* remove timeout if there are no transfers left */ + if (!curl->unfinished && event_initialized(ctx->timeout) && event_pending(ctx->timeout, EV_TIMEOUT, NULL)) { + event_del(ctx->timeout); + } +} + +static int php_http_client_curl_event_socket(CURL *easy, curl_socket_t sock, int action, void *socket_data, void *assign_data) +{ + php_http_client_curl_event_context_t *ctx = socket_data; + php_http_client_curl_t *curl = ctx->client->ctx; + int events = EV_PERSIST; + php_http_client_curl_event_ev_t *ev = assign_data; + TSRMLS_FETCH_FROM_CTX(ctx->client->ts); + +#if DBG_EVENTS + fprintf(stderr, "S"); +#endif + + if (!ev) { + ev = ecalloc(1, sizeof(*ev)); + ev->context = ctx; + curl_multi_assign(curl->handle->multi, sock, ev); + } else { + event_del(&ev->evnt); + } + + switch (action) { + case CURL_POLL_IN: + events |= EV_READ; + break; + case CURL_POLL_OUT: + events |= EV_WRITE; + break; + case CURL_POLL_INOUT: + events |= EV_READ|EV_WRITE; + break; + + case CURL_POLL_REMOVE: + efree(ev); + /* no break */ + case CURL_POLL_NONE: + return 0; + + default: + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown socket action %d", action); + return -1; + } + + event_assign(&ev->evnt, ctx->evbase, sock, events, php_http_client_curl_event_callback, ctx); + event_add(&ev->evnt, NULL); + + return 0; +} + +static ZEND_RESULT_CODE php_http_client_curl_event_once(void *context) +{ + php_http_client_curl_event_context_t *ctx = context; + +#if DBG_EVENTS + fprintf(stderr, "O"); +#endif + + if (0 > event_base_loop(ctx->evbase, EVLOOP_NONBLOCK)) { + return FAILURE; + } + return SUCCESS; +} + +static ZEND_RESULT_CODE php_http_client_curl_event_wait(void *context, struct timeval *custom_timeout) +{ + php_http_client_curl_event_context_t *ctx = context; + struct timeval timeout; + +#if DBG_EVENTS + fprintf(stderr, "W"); +#endif + + if (!event_initialized(ctx->timeout)) { + if (0 > event_assign(ctx->timeout, ctx->evbase, CURL_SOCKET_TIMEOUT, 0, php_http_client_curl_event_timeout_callback, ctx)) { + return FAILURE; + } + } else if (custom_timeout && timerisset(custom_timeout)) { + if (0 > event_add(ctx->timeout, custom_timeout)) { + return FAILURE; + } + } else if (!event_pending(ctx->timeout, EV_TIMEOUT, NULL)) { + php_http_client_curl_get_timeout(ctx->client->ctx, 1000, &timeout); + if (0 > event_add(ctx->timeout, &timeout)) { + return FAILURE; + } + } + + if (0 > event_base_loop(ctx->evbase, EVLOOP_ONCE)) { + return FAILURE; + } + + return SUCCESS; +} + +static ZEND_RESULT_CODE php_http_client_curl_event_exec(void *context) +{ + php_http_client_curl_event_context_t *ctx = context; + php_http_client_curl_t *curl = ctx->client->ctx; + TSRMLS_FETCH_FROM_CTX(ctx->client->ts); + +#if DBG_EVENTS + fprintf(stderr, "E"); +#endif + + /* kickstart */ + php_http_client_curl_event_handler(ctx, CURL_SOCKET_TIMEOUT, 0); + + do { + if (0 > event_base_dispatch(ctx->evbase)) { + return FAILURE; + } + } while (curl->unfinished && !EG(exception)); + + return SUCCESS; +} + +static void *php_http_client_curl_event_init(php_http_client_t *client) +{ + php_http_client_curl_t *curl = client->ctx; + php_http_client_curl_event_context_t *ctx; + struct event_base *evb = event_base_new(); + +#if DBG_EVENTS + fprintf(stderr, "I"); +#endif + + if (!evb) { + return NULL; + } + + ctx = ecalloc(1, sizeof(*ctx)); + ctx->client = client; + ctx->evbase = evb; + ctx->timeout = ecalloc(1, sizeof(struct event)); + + curl_multi_setopt(curl->handle->multi, CURLMOPT_SOCKETDATA, ctx); + curl_multi_setopt(curl->handle->multi, CURLMOPT_SOCKETFUNCTION, php_http_client_curl_event_socket); + curl_multi_setopt(curl->handle->multi, CURLMOPT_TIMERDATA, ctx); + curl_multi_setopt(curl->handle->multi, CURLMOPT_TIMERFUNCTION, php_http_client_curl_event_timer); + + return ctx; +} + +static void php_http_client_curl_event_dtor(void **context) +{ + php_http_client_curl_event_context_t *ctx = *context; + php_http_client_curl_t *curl; + +#if DBG_EVENTS + fprintf(stderr, "D"); +#endif + + ZEND_ASSERT(ctx); + + curl = ctx->client->ctx; + + curl_multi_setopt(curl->handle->multi, CURLMOPT_SOCKETDATA, NULL); + curl_multi_setopt(curl->handle->multi, CURLMOPT_SOCKETFUNCTION, NULL); + curl_multi_setopt(curl->handle->multi, CURLMOPT_TIMERDATA, NULL); + curl_multi_setopt(curl->handle->multi, CURLMOPT_TIMERFUNCTION, NULL); + + if (event_initialized(ctx->timeout) && event_pending(ctx->timeout, EV_TIMEOUT, NULL)) { + event_del(ctx->timeout); + } + efree(ctx->timeout); + event_base_free(ctx->evbase); + + efree(ctx); + *context = NULL; +} + +static php_http_client_curl_ops_t php_http_client_curl_event_ops = { + &php_http_client_curl_event_init, + &php_http_client_curl_event_dtor, + &php_http_client_curl_event_once, + &php_http_client_curl_event_wait, + &php_http_client_curl_event_exec, +}; + +php_http_client_curl_ops_t *php_http_client_curl_event_ops_get() +{ + return &php_http_client_curl_event_ops; +} + +#endif /* PHP_HTTP_HAVE_EVENT */ +#endif /* PHP_HTTP_HAVE_CURL */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/src/php_http_client_curl_event.h b/src/php_http_client_curl_event.h new file mode 100644 index 0000000..d3b0928 --- /dev/null +++ b/src/php_http_client_curl_event.h @@ -0,0 +1,28 @@ +/* + +--------------------------------------------------------------------+ + | PECL :: http | + +--------------------------------------------------------------------+ + | Redistribution and use in source and binary forms, with or without | + | modification, are permitted provided that the conditions mentioned | + | in the accompanying LICENSE file are met. | + +--------------------------------------------------------------------+ + | Copyright (c) 2004-2014, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +#if PHP_HTTP_HAVE_CURL +#if PHP_HTTP_HAVE_EVENT + +php_http_client_curl_ops_t *php_http_client_curl_event_ops_get(); + +#endif +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/tests/client018.phpt b/tests/client018.phpt index c3ca9f9..884a3c6 100644 --- a/tests/client018.phpt +++ b/tests/client018.phpt @@ -12,6 +12,10 @@ include "helper/server.inc"; echo "Test\n"; +function dump($response) { + echo $response->getHeader("X-Req"),"\n"; +} + server("pipeline.inc", function($port, $stdin, $stdout, $stderr) { /* tell the server we're about to send 3 pipelined messages */ fputs($stdin, "3\n"); @@ -20,37 +24,21 @@ server("pipeline.inc", function($port, $stdin, $stdout, $stderr) { $client->configure(array("pipelining" => true, "max_host_connections" => 0)); /* this is just to let curl know the server may be capable of pipelining */ - $client->enqueue(new http\Client\Request("GET", "http://localhost:$port")); + $client->enqueue(new http\Client\Request("GET", "http://localhost:$port"), "dump"); $client->send(); - $client->enqueue(new http\Client\Request("GET", "http://localhost:$port/1")); - $client->enqueue(new http\Client\Request("GET", "http://localhost:$port/2")); - $client->enqueue(new http\Client\Request("GET", "http://localhost:$port/3")); + $client->enqueue(new http\Client\Request("GET", "http://localhost:$port/1"), "dump"); + $client->enqueue(new http\Client\Request("GET", "http://localhost:$port/2"), "dump"); + $client->enqueue(new http\Client\Request("GET", "http://localhost:$port/3"), "dump"); $client->send(); - - while (($response = $client->getResponse())) { - echo $response; - } }); ?> ===DONE=== --EXPECT-- Test -HTTP/1.1 200 OK -X-Req: /3 -Etag: "" -X-Original-Transfer-Encoding: chunked -HTTP/1.1 200 OK -X-Req: /2 -Etag: "" -X-Original-Transfer-Encoding: chunked -HTTP/1.1 200 OK -X-Req: /1 -Etag: "" -X-Original-Transfer-Encoding: chunked -HTTP/1.1 200 OK -X-Req: / -Etag: "" -X-Original-Transfer-Encoding: chunked +/ +/1 +/2 +/3 ===DONE=== diff --git a/tests/client022.phpt b/tests/client022.phpt index 255de08..e3f7f11 100644 --- a/tests/client022.phpt +++ b/tests/client022.phpt @@ -18,7 +18,7 @@ nghttpd(function($port) { $client->setOptions([ "protocol" => http\Client\Curl\HTTP_VERSION_2_0, "ssl" => [ - "cainfo" => __DIR__."/helper/http2.crt", + "cafile" => __DIR__."/helper/http2.crt", ] ]); $client->enqueue(new http\Client\Request("GET", "https://localhost:$port")); diff --git a/tests/helper/pipeline.inc b/tests/helper/pipeline.inc index a6b1699..6f3674c 100644 --- a/tests/helper/pipeline.inc +++ b/tests/helper/pipeline.inc @@ -23,8 +23,8 @@ serve(function($client) { /* pipelined messages */ $req = array(); for ($i=0; $i < $count; ++ $i) { - $req[] = new http\Message($client, false); - logger("Read request no. %d from client %d", $i+1, (int) $client); + $req[] = $m = new http\Message($client, false); + logger("Read request no. %d from client %d (%s)", $i+1, (int) $client, $m->getRequestUrl()); } foreach ($req as $i => $msg) { respond($client, $msg); diff --git a/tests/skipif.inc b/tests/skipif.inc index 4ab631e..4844def 100644 --- a/tests/skipif.inc +++ b/tests/skipif.inc @@ -20,13 +20,17 @@ function skip_client_test($message = "skip need a client driver\n") { } } -function skip_http2_test($message = "skip need http2 support (nghttpd in PATH)\n") { - if (defined("http\\Client\\Curl\\HTTP_VERSION_2_0")) { - foreach (explode(":", $_ENV["PATH"]) as $path) { - if (is_executable($path . "/nghttpd")) { - return; - } +function skip_http2_test($message = "skip need http2 support") { + if (!defined("http\\Client\\Curl\\HTTP_VERSION_2_0")) { + die("$message (HTTP_VERSION_2_0)\n"); + } + if (!(http\Client\Curl\FEATURES & http\Client\Curl\Features\HTTP2)) { + die("$message (FEATURES & HTTP2)\n"); + } + foreach (explode(":", $_ENV["PATH"]) as $path) { + if (is_executable($path . "/nghttpd")) { + return; } } - die($message); + die("$message (nghttpd in PATH)\n"); } \ No newline at end of file -- 2.30.2