modularize curl event handler
authorMichael Wallner <mike@php.net>
Tue, 7 Jun 2016 09:59:39 +0000 (11:59 +0200)
committerMichael Wallner <mike@php.net>
Tue, 7 Jun 2016 09:59:39 +0000 (11:59 +0200)
src/php_http_client_curl.c
src/php_http_client_curl.h
src/php_http_client_curl_event.c [new file with mode: 0644]
src/php_http_client_curl_event.h [new file with mode: 0644]
tests/client018.phpt
tests/client022.phpt
tests/helper/pipeline.inc
tests/skipif.inc

index d4f0517..d3f5200 100644 (file)
 
 #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 <event.h>
-#              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 <event2/event.h>
-#                      include <event2/event_struct.h>
-#              else
-#                      error "libevent presence is unknown"
-#              endif
-#      endif
-#      ifndef DBG_EVENTS
-#              define DBG_EVENTS 0
-#      endif
-#endif
-
 #ifdef PHP_HTTP_HAVE_OPENSSL
 #      include <openssl/ssl.h>
 #endif
 #      include <gnutls.h>
 #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;
index c82a09c..f84107f 100644 (file)
 
 #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 (file)
index 0000000..9b1808b
--- /dev/null
@@ -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 <mike@php.net>            |
+    +--------------------------------------------------------------------+
+*/
+
+#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 <event.h>
+#              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 <event2/event.h>
+#                      include <event2/event_struct.h>
+#              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 (file)
index 0000000..d3b0928
--- /dev/null
@@ -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 <mike@php.net>            |
+    +--------------------------------------------------------------------+
+*/
+
+#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
+ */
index c3ca9f9..884a3c6 100644 (file)
@@ -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===
index 255de08..e3f7f11 100644 (file)
@@ -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"));
index a6b1699..6f3674c 100644 (file)
@@ -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);
index 4ab631e..4844def 100644 (file)
@@ -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