#if PHP_HTTP_HAVE_CURL
|| SUCCESS != PHP_MINIT_CALL(http_curl)
|| SUCCESS != PHP_MINIT_CALL(http_client_curl)
+ || SUCCESS != PHP_MINIT_CALL(http_client_curl_user)
#endif
|| SUCCESS != PHP_MINIT_CALL(http_url)
|| SUCCESS != PHP_MINIT_CALL(http_env)
#include "php_http_client_request.h"
#include "php_http_client_response.h"
#include "php_http_client_curl.h"
+#include "php_http_client_curl_user.h"
+#include "php_http_client_curl_event.h"
#include "php_http_url.h"
#include "php_http_version.h"
php_http_client_object_t *obj = PHP_HTTP_OBJ(NULL, object);
zend_llist_element *el = NULL;
HashTable *props = Z_OBJPROP_P(object);
- uint32_t count = zend_hash_num_elements(props) + zend_llist_count(&obj->client->responses) + zend_llist_count(&obj->client->requests);
+ uint32_t count = zend_hash_num_elements(props) + zend_llist_count(&obj->client->responses) + zend_llist_count(&obj->client->requests) + 1;
zval *val;
*n = 0;
*table = obj->gc = erealloc(obj->gc, sizeof(zval) * count);
+#if PHP_HTTP_HAVE_CURL
+ if (obj->client->ops == php_http_client_curl_get_ops()) {
+ php_http_client_curl_t *curl = obj->client->ctx;
+
+ if (curl->ev_ops == php_http_client_curl_user_ops_get()) {
+ php_http_client_curl_user_context_t *ctx = curl->ev_ctx;
+
+ ZVAL_COPY_VALUE(&obj->gc[(*n)++], &ctx->user);
+ }
+ }
+#endif
+
for (el = obj->client->responses.head; el; el = el->next) {
php_http_message_object_t *response_obj = *(php_http_message_object_t **) el->data;
ZVAL_OBJ(&obj->gc[(*n)++], &response_obj->zo);
}
for (el = obj->client->requests.head; el; el = el->next) {
- php_http_client_enqueue_t *q = *(php_http_client_enqueue_t **) el->data;
+ php_http_client_enqueue_t *q = (php_http_client_enqueue_t *) el->data;
php_http_message_object_t *request_obj = q->opaque; /* FIXME */
ZVAL_OBJ(&obj->gc[(*n)++], &request_obj->zo);
}
#include "php_http_api.h"
#include "php_http_client.h"
+#include "php_http_client_curl_event.h"
+#include "php_http_client_curl_user.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;
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;
}
}
-#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;
-
- /* 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, 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)
+void php_http_client_curl_loop(php_http_client_t *client, curl_socket_t s, int curl_action)
{
- 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;
-
- 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, 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;
+ CURLMcode rc;
+ php_http_client_curl_t *curl = client->ctx;
#if DBG_EVENTS
- fprintf(stderr, "S");
+ fprintf(stderr, "H");
#endif
- if (curl->useevents) {
- int events = EV_PERSIST;
- php_http_curlm_event_t *ev = assign_data;
-
- 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, E_WARNING, "Unknown socket action %d", action);
- return -1;
- }
+ do {
+ rc = curl_multi_socket_action(curl->handle->multi, s, curl_action, &curl->unfinished);
+ } while (CURLM_CALL_MULTI_PERFORM == rc);
- event_assign(&ev->evnt, curl->evbase, sock, events, php_http_curlm_event_callback, context);
- event_add(&ev->evnt, NULL);
+ if (CURLM_OK != rc) {
+ php_error_docref(NULL, E_WARNING, "%s", curl_multi_strerror(rc));
}
- return 0;
+ php_http_client_curl_responsehandler(client);
}
-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;
}
#endif
-#if PHP_HTTP_HAVE_EVENT
-static inline ZEND_RESULT_CODE php_http_curlm_use_eventloop(php_http_client_t *h, zend_bool enable)
+static inline ZEND_RESULT_CODE php_http_curlm_use_eventloop(php_http_client_t *h, php_http_client_curl_ops_t *ev_ops, zval *init_data)
{
php_http_client_curl_t *curl = h->ctx;
+ void *ev_ctx;
- if ((curl->useevents = enable)) {
- if (!curl->evbase) {
- curl->evbase = event_base_new();
- }
- if (!curl->timeout) {
- curl->timeout = ecalloc(1, sizeof(struct event));
+ if (ev_ops) {
+ if (!(ev_ctx = ev_ops->init(h, init_data))) {
+ 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);
+ curl->ev_ctx = ev_ctx;
+ curl->ev_ops = ev_ops;
} 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;
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;
+ php_http_client_curl_ops_t *ev_ops = NULL;
- return php_http_curlm_use_eventloop(client, value && Z_TYPE_P(value) == IS_TRUE);
-}
+ if (Z_TYPE_P(value) == IS_OBJECT && instanceof_function(Z_OBJCE_P(value), php_http_client_curl_user_get_class_entry())) {
+ ev_ops = php_http_client_curl_user_ops_get();
+#if PHP_HTTP_HAVE_EVENT
+ } else if (value && zend_is_true(value)) {
+ ev_ops = php_http_client_curl_event_ops_get();
#endif
+ }
+
+ return php_http_curlm_use_eventloop(client, ev_ops, value);
+}
static ZEND_RESULT_CODE php_http_curlm_option_set_share_cookies(php_http_option_t *opt, zval *value, void *userdata)
{
}
#endif
/* events */
-#if PHP_HTTP_HAVE_EVENT
- if ((opt = php_http_option_register(registry, ZEND_STRL("use_eventloop"), 0, _IS_BOOL))) {
+ if ((opt = php_http_option_register(registry, ZEND_STRL("use_eventloop"), 0, 0))) {
opt->setter = php_http_curlm_option_set_use_eventloop;
}
-#endif
/* share */
if ((opt = php_http_option_register(registry, ZEND_STRL("share_cookies"), 0, _IS_BOOL))) {
opt->setter = php_http_curlm_option_set_share_cookies;
{
php_http_client_curl_t *curl = h->ctx;
-#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->evbase) {
- event_base_free(curl->evbase);
- curl->evbase = NULL;
+ if (curl->ev_ops) {
+ curl->ev_ops->dtor(&curl->ev_ctx);
}
-#endif
curl->unfinished = 0;
php_resource_factory_handle_dtor(h->rf, curl->handle);
}
}
-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
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);
{
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;
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;
- 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, 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, 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, E_WARNING, "WinSock error: %d", WSAGetLastError());
#else
- php_error_docref(NULL, E_WARNING, "%s", strerror(errno));
+ php_error_docref(NULL, E_WARNING, "%s", strerror(errno));
#endif
- return FAILURE;
- }
+ return FAILURE;
}
}
case PHP_HTTP_CLIENT_OPT_USE_EVENTS:
#if PHP_HTTP_HAVE_EVENT
- return php_http_curlm_use_eventloop(h, *(zend_bool *) arg);
+ return php_http_curlm_use_eventloop(h, (*(zend_bool *) arg)
+ ? php_http_client_curl_event_ops_get()
+ : NULL, NULL);
break;
#endif
return &php_http_client_curl_ops;
}
+
PHP_MINIT_FUNCTION(http_client_curl)
{
curl_version_info_data *info;
php_http_client_driver_t driver;
};
+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_HTTP_API void php_http_client_curl_loop(php_http_client_t *client, curl_socket_t s, int curl_action);
+PHP_HTTP_API php_http_client_ops_t *php_http_client_curl_get_ops(void);
+
PHP_MINIT_FUNCTION(http_client_curl);
PHP_MSHUTDOWN_FUNCTION(http_client_curl);
+
#endif /* PHP_HTTP_HAVE_CURL */
#endif /* PHP_HTTP_CLIENT_CURL_H */
--- /dev/null
+/*
+ +--------------------------------------------------------------------+
+ | 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"
+
+#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;
+
+#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, 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;
+
+#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, 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;
+
+#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
+
+ 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
+ */
--- /dev/null
+/*
+ +--------------------------------------------------------------------+
+ | 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> |
+ +--------------------------------------------------------------------+
+*/
+
+#ifndef PHP_HTTP_CLIENT_CURL_EVENT_H
+#define PHP_HTTP_CLIENT_CURL_EVENT_H
+
+#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
+
+#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
+ */
--- /dev/null
+/*
+ +--------------------------------------------------------------------+
+ | 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_network.h"
+#include "zend_closures.h"
+
+#if PHP_HTTP_HAVE_CURL
+
+typedef struct php_http_client_curl_user_ev {
+ php_stream *socket;
+ php_http_client_curl_user_context_t *context;
+} php_http_client_curl_user_ev_t;
+
+static void php_http_client_curl_user_handler(INTERNAL_FUNCTION_PARAMETERS)
+{
+ zval *zstream = NULL, *zclient = NULL;
+ php_stream *stream = NULL;
+ long action = 0;
+ php_socket_t fd = CURL_SOCKET_TIMEOUT;
+ php_http_client_object_t *client = NULL;
+
+ if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS(), "O|rl", &zclient, php_http_client_get_class_entry(), &zstream, &action)) {
+ return;
+ }
+
+ client = PHP_HTTP_OBJ(NULL, zclient);
+ if (zstream) {
+ php_stream_from_zval(stream, zstream);
+
+ if (SUCCESS != php_stream_cast(stream, PHP_STREAM_AS_SOCKETD, (void *) &fd, 1)) {
+ return;
+ }
+ }
+ php_http_client_curl_loop(client->client, fd, action);
+}
+
+static void php_http_client_curl_user_timer(CURLM *multi, long timeout_ms, void *timer_data)
+{
+ php_http_client_curl_user_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_loop(context->client, CURL_SOCKET_TIMEOUT, 0);
+ } else if (timeout_ms > 0) {
+ zval args[1], *ztimeout = &args[0];
+
+ ZVAL_LONG(ztimeout, timeout_ms);
+ php_http_object_method_call(&context->timer, &context->user, NULL, 1, args);
+ zval_ptr_dtor(ztimeout);
+ }
+}
+
+static int php_http_client_curl_user_socket(CURL *easy, curl_socket_t sock, int action, void *socket_data, void *assign_data)
+{
+ php_http_client_curl_user_context_t *ctx = socket_data;
+ php_http_client_curl_t *curl = ctx->client->ctx;
+ php_http_client_curl_user_ev_t *ev = assign_data;
+ zval args[2], *zaction = &args[1], *zsocket = &args[0];
+
+#if DBG_EVENTS
+ fprintf(stderr, "S");
+#endif
+
+ if (!ev) {
+ ev = ecalloc(1, sizeof(*ev));
+ ev->context = ctx;
+ ev->socket = php_stream_sock_open_from_socket(sock, NULL);
+
+ curl_multi_assign(curl->handle->multi, sock, ev);
+ }
+
+ switch (action) {
+ case CURL_POLL_IN:
+ case CURL_POLL_OUT:
+ case CURL_POLL_INOUT:
+ case CURL_POLL_REMOVE:
+ case CURL_POLL_NONE:
+ php_stream_to_zval(ev->socket, zsocket);
+ Z_TRY_ADDREF_P(zsocket);
+ ZVAL_LONG(zaction, action);
+ php_http_object_method_call(&ctx->socket, &ctx->user, NULL, 2, args);
+ zval_ptr_dtor(zsocket);
+ zval_ptr_dtor(zaction);
+ break;
+
+ default:
+ php_error_docref(NULL, E_WARNING, "Unknown socket action %d", action);
+ return -1;
+ }
+
+ if (action == CURL_POLL_REMOVE && ev) {
+ php_stream_close(ev->socket);
+ efree(ev);
+ curl_multi_assign(curl->handle->multi, sock, NULL);
+ }
+ return 0;
+}
+
+static ZEND_RESULT_CODE php_http_client_curl_user_once(void *context)
+{
+ php_http_client_curl_user_context_t *ctx = context;
+
+#if DBG_EVENTS
+ fprintf(stderr, "O");
+#endif
+
+ return php_http_object_method_call(&ctx->once, &ctx->user, NULL, 0, NULL);
+}
+
+static ZEND_RESULT_CODE php_http_client_curl_user_wait(void *context, struct timeval *custom_timeout)
+{
+ php_http_client_curl_user_context_t *ctx = context;
+ struct timeval timeout;
+ zval args[1], *ztimeout = &args[0];
+ ZEND_RESULT_CODE rv;
+
+#if DBG_EVENTS
+ fprintf(stderr, "W");
+#endif
+
+ if (!custom_timeout || !timerisset(custom_timeout)) {
+ php_http_client_curl_get_timeout(ctx->client->ctx, 1000, &timeout);
+ custom_timeout = &timeout;
+ }
+
+ ZVAL_LONG(ztimeout, custom_timeout->tv_sec * 1000 + custom_timeout->tv_usec / 1000);
+ rv = php_http_object_method_call(&ctx->wait, &ctx->user, NULL, 1, args);
+ zval_ptr_dtor(ztimeout);
+
+ return rv;
+}
+
+static ZEND_RESULT_CODE php_http_client_curl_user_exec(void *context)
+{
+ php_http_client_curl_user_context_t *ctx = context;
+ php_http_client_curl_t *curl = ctx->client->ctx;
+
+#if DBG_EVENTS
+ fprintf(stderr, "E");
+#endif
+
+ /* kickstart */
+ php_http_client_curl_loop(ctx->client, CURL_SOCKET_TIMEOUT, 0);
+
+ do {
+ if (SUCCESS != php_http_object_method_call(&ctx->send, &ctx->user, NULL, 0, NULL)) {
+ return FAILURE;
+ }
+ } while (curl->unfinished && !EG(exception));
+
+ return SUCCESS;
+}
+
+static void *php_http_client_curl_user_init(php_http_client_t *client, void *user_data)
+{
+ php_http_client_curl_t *curl = client->ctx;
+ php_http_client_curl_user_context_t *ctx;
+ php_http_object_method_t init;
+ zval args[1], *zclosure = &args[0];
+
+#if DBG_EVENTS
+ fprintf(stderr, "I");
+#endif
+
+ ctx = ecalloc(1, sizeof(*ctx));
+ ctx->client = client;
+ ZVAL_COPY(&ctx->user, user_data);
+
+ memset(&ctx->closure, 0, sizeof(ctx->closure));
+ ctx->closure.common.type = ZEND_INTERNAL_FUNCTION;
+ ctx->closure.common.function_name = zend_string_init(ZEND_STRL("php_http_client_curl_user_handler"), 0);
+ ctx->closure.internal_function.handler = php_http_client_curl_user_handler;
+
+ zend_create_closure(zclosure, &ctx->closure, NULL, NULL, NULL);
+
+ php_http_object_method_init(&init, &ctx->user, ZEND_STRL("init"));
+ php_http_object_method_call(&init, &ctx->user, NULL, 1, args);
+ php_http_object_method_dtor(&init);
+ zval_ptr_dtor(zclosure);
+
+ php_http_object_method_init(&ctx->timer, &ctx->user, ZEND_STRL("timer"));
+ php_http_object_method_init(&ctx->socket, &ctx->user, ZEND_STRL("socket"));
+ php_http_object_method_init(&ctx->once, &ctx->user, ZEND_STRL("once"));
+ php_http_object_method_init(&ctx->wait, &ctx->user, ZEND_STRL("wait"));
+ php_http_object_method_init(&ctx->send, &ctx->user, ZEND_STRL("send"));
+
+ curl_multi_setopt(curl->handle->multi, CURLMOPT_SOCKETDATA, ctx);
+ curl_multi_setopt(curl->handle->multi, CURLMOPT_SOCKETFUNCTION, php_http_client_curl_user_socket);
+ curl_multi_setopt(curl->handle->multi, CURLMOPT_TIMERDATA, ctx);
+ curl_multi_setopt(curl->handle->multi, CURLMOPT_TIMERFUNCTION, php_http_client_curl_user_timer);
+
+ return ctx;
+}
+
+static void php_http_client_curl_user_dtor(void **context)
+{
+ php_http_client_curl_user_context_t *ctx = *context;
+ php_http_client_curl_t *curl;
+
+#if DBG_EVENTS
+ fprintf(stderr, "D");
+#endif
+
+ 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);
+
+ php_http_object_method_dtor(&ctx->timer);
+ php_http_object_method_dtor(&ctx->socket);
+ php_http_object_method_dtor(&ctx->once);
+ php_http_object_method_dtor(&ctx->wait);
+ php_http_object_method_dtor(&ctx->send);
+
+ zend_string_release(ctx->closure.common.function_name);
+ zval_ptr_dtor(&ctx->user);
+
+ efree(ctx);
+ *context = NULL;
+}
+
+static php_http_client_curl_ops_t php_http_client_curl_user_ops = {
+ &php_http_client_curl_user_init,
+ &php_http_client_curl_user_dtor,
+ &php_http_client_curl_user_once,
+ &php_http_client_curl_user_wait,
+ &php_http_client_curl_user_exec,
+};
+
+php_http_client_curl_ops_t *php_http_client_curl_user_ops_get()
+{
+ return &php_http_client_curl_user_ops;
+}
+
+static zend_class_entry *php_http_client_curl_user_class_entry;
+
+zend_class_entry *php_http_client_curl_user_get_class_entry()
+{
+ return php_http_client_curl_user_class_entry;
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_init, 0, 0, 1)
+ ZEND_ARG_TYPE_INFO(0, run, IS_CALLABLE, 0)
+ZEND_END_ARG_INFO();
+ZEND_BEGIN_ARG_INFO_EX(ai_timer, 0, 0, 1)
+#if PHP_VERSION_ID >= 70000
+ ZEND_ARG_TYPE_INFO(0, timeout_ms, IS_LONG, 0)
+#else
+ ZEND_ARG_INFO(0, timeout_ms)
+#endif
+ZEND_END_ARG_INFO();
+ZEND_BEGIN_ARG_INFO_EX(ai_socket, 0, 0, 2)
+ ZEND_ARG_INFO(0, socket)
+#if PHP_VERSION_ID >= 70000
+ ZEND_ARG_TYPE_INFO(0, action, IS_LONG, 0)
+#else
+ ZEND_ARG_INFO(0, action)
+#endif
+ZEND_END_ARG_INFO();
+ZEND_BEGIN_ARG_INFO_EX(ai_once, 0, 0, 0)
+ZEND_END_ARG_INFO();
+ZEND_BEGIN_ARG_INFO_EX(ai_wait, 0, 0, 0)
+#if PHP_VERSION_ID >= 70000
+ ZEND_ARG_TYPE_INFO(0, timeout_ms, IS_LONG, 0)
+#else
+ ZEND_ARG_INFO(0, timeout_ms)
+#endif
+ZEND_END_ARG_INFO();
+ZEND_BEGIN_ARG_INFO_EX(ai_send, 0, 0, 0)
+ZEND_END_ARG_INFO();
+
+static zend_function_entry php_http_client_curl_user_methods[] = {
+ PHP_ABSTRACT_ME(HttpClientCurlUser, init, ai_init)
+ PHP_ABSTRACT_ME(HttpClientCurlUser, timer, ai_timer)
+ PHP_ABSTRACT_ME(HttpClientCurlUser, socket, ai_socket)
+ PHP_ABSTRACT_ME(HttpClientCurlUser, once, ai_once)
+ PHP_ABSTRACT_ME(HttpClientCurlUser, wait, ai_wait)
+ PHP_ABSTRACT_ME(HttpClientCulrUser, send, ai_send)
+ {0}
+};
+
+PHP_MINIT_FUNCTION(http_client_curl_user)
+{
+ zend_class_entry ce = {0};
+
+ INIT_NS_CLASS_ENTRY(ce, "http\\Client\\Curl", "User", php_http_client_curl_user_methods);
+ php_http_client_curl_user_class_entry = zend_register_internal_interface(&ce);
+
+ zend_declare_class_constant_long(php_http_client_curl_user_class_entry, ZEND_STRL("POLL_NONE"), CURL_POLL_NONE);
+ zend_declare_class_constant_long(php_http_client_curl_user_class_entry, ZEND_STRL("POLL_IN"), CURL_POLL_IN);
+ zend_declare_class_constant_long(php_http_client_curl_user_class_entry, ZEND_STRL("POLL_OUT"), CURL_POLL_OUT);
+ zend_declare_class_constant_long(php_http_client_curl_user_class_entry, ZEND_STRL("POLL_INOUT"), CURL_POLL_INOUT);
+ zend_declare_class_constant_long(php_http_client_curl_user_class_entry, ZEND_STRL("POLL_REMOVE"), CURL_POLL_REMOVE);
+
+ return SUCCESS;
+}
+
+#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
+ */
--- /dev/null
+/*
+ +--------------------------------------------------------------------+
+ | 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> |
+ +--------------------------------------------------------------------+
+*/
+
+#ifndef PHP_HTTP_CLIENT_CURL_USER_H
+#define PHP_HTTP_CLIENT_CURL_USER_H
+
+#if PHP_HTTP_HAVE_CURL
+
+typedef struct php_http_client_curl_user_context {
+ php_http_client_t *client;
+ zval user;
+ zend_function closure;
+ php_http_object_method_t timer;
+ php_http_object_method_t socket;
+ php_http_object_method_t once;
+ php_http_object_method_t wait;
+ php_http_object_method_t send;
+} php_http_client_curl_user_context_t;
+
+PHP_HTTP_API zend_class_entry *php_http_client_curl_user_get_class_entry();
+PHP_HTTP_API php_http_client_curl_ops_t *php_http_client_curl_user_ops_get();
+PHP_MINIT_FUNCTION(http_client_curl_user);
+
+#endif
+
+#if 0
+<?php
+
+interface http\Client\Curl\User
+{
+ const POLL_NONE = 0;
+ const POLL_IN = 1;
+ const POLL_OUT = 2;
+ const POLL_INOUT = 3;
+ const POLL_REMOVE = 4;
+
+ /**
+ * Initialize the loop
+ *
+ * The callback should be run when:
+ * - timeout occurs
+ * - a watched socket needs action
+ *
+ * @param callable $run callback as function(http\Client $client, resource $socket = null, int $action = http\Client\Curl\User::POLL_NONE)
+ */
+ function init(callable $run);
+
+ /**
+ * Register a timeout watcher
+ * @param int $timeout_ms desired timeout with milliseconds resolution
+ */
+ function timer(int $timeout_ms);
+
+ /**
+ * (Un-)Register a socket watcher
+ * @param resource $socket the fd to watch
+ * @param int $poll http\Client\Curl\Loop::POLL_* constant
+ */
+ function socket($socket, int $poll);
+
+ /**
+ * Run the loop as long as it does not block
+ *
+ * Called by http\Client::once()
+ */
+ function once();
+
+ /**
+ * Wait/poll/select (block the loop) until events fire
+ *
+ * Called by http\Client::wait()
+ *
+ * @param int $timeout_ms block for maximal $timeout_ms milliseconds
+ */
+ function wait(int $timeout_ms = null);
+
+ /**
+ * Run the loop
+ *
+ * Called by http\Client::send() while there are unfinished requests and
+ * no exception has occurred
+ */
+ function send();
+}
+#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
+ */
if (stream) {
body->res = stream->res;
- ++GC_REFCOUNT(body->res);
} else {
body->res = php_stream_temp_create(TEMP_STREAM_DEFAULT, 0xffff)->res;
}
+ ++GC_REFCOUNT(body->res);
if (body_ptr) {
*body_ptr = body;
#include "php_http_api.h"
+#ifndef PHP_HTTP_DEBUG_NEG
+# define PHP_HTTP_DEBUG_NEG 0
+#endif
+
static int php_http_negotiate_sort(const void *first, const void *second)
{
Bucket *b1 = (Bucket *) first, *b2 = (Bucket *) second;
#define M_PRI 5
#define M_SEC 2
-#define M_ANY 1
+#define M_ANY -1
#define M_NOT 0
#define M_ALL ~0
static inline unsigned php_http_negotiate_match(const char *param_str, size_t param_len, const char *supported_str, size_t supported_len, const char *sep_str, size_t sep_len)
match += M_PRI;
}
- if (param_sec && supported_sec && !strcasecmp(param_sec, supported_sec)) {
+ if (param_sec && supported_sec
+ && ((*(param_sec + sep_len) == '*' || *(supported_sec + sep_len) == '*')
+ || !strcasecmp(param_sec, supported_sec)
+ )
+ ) {
match += M_SEC;
}
match += M_ANY;
}
}
-#if 0
+#if PHP_HTTP_DEBUG_NEG
fprintf(stderr, "match: %s == %s => %u\n", supported_str, param_str, match);
#endif
return match;
static int php_http_negotiate_reduce(zval *p, int num_args, va_list args, zend_hash_key *hash_key)
{
unsigned best_match = 0;
+ double q = 0;
php_http_arrkey_t key;
- zval *value, *q = NULL;
+ zval *value;
zend_string *supported = zval_get_string(p);
HashTable *params = va_arg(args, HashTable *);
HashTable *result = va_arg(args, HashTable *);
{
unsigned match;
+#if PHP_HTTP_DEBUG_NEG
+ fprintf(stderr, "match(%u) > best_match(%u) = %u (q=%f)\n", match, best_match, match>best_match, Z_DVAL_PP(val));
+#endif
php_http_arrkey_stringify(&key, NULL);
match = php_http_negotiate_match(key.key->val, key.key->len, supported->val, supported->len, sep_str, sep_len);
if (match > best_match) {
best_match = match;
- q = value;
+ q = Z_DVAL_P(value) - 0.1 / match;
}
php_http_arrkey_dtor(&key);
}
ZEND_HASH_FOREACH_END();
- if (q && Z_DVAL_P(q) > 0) {
- Z_TRY_ADDREF_P(q);
- zend_hash_update(result, supported, q);
+ if (q > 0) {
+ zval tmp;
+
+ ZVAL_DOUBLE(&tmp, q);
+ zend_hash_update(result, supported, &tmp);
}
zend_string_release(supported);
php_http_params_opts_default_get(&opts);
opts.input.str = estrndup(value_str, value_len);
opts.input.len = value_len;
+ opts.flags &= ~PHP_HTTP_PARAMS_RFC5987;
php_http_params_parse(¶ms, &opts);
efree(opts.input.str);
&& (zq = zend_hash_str_find(Z_ARRVAL_P(arg), ZEND_STRL("q")))) {
q = zval_get_double(zq);
} else {
- q = 1.0 - ++i / 100.0;
+ q = 1.0 - (((double) ++i) / 100.0);
}
#if 0
}
ZEND_HASH_FOREACH_END();
-#if 0
+#if PHP_HTTP_DEBUG_NEG
zend_print_zval_r(&arr, 1);
#endif
return;
}
- eos = &Z_STRVAL_P(zv)[Z_STRLEN_P(zv)-1];
- if (*eos == '*') {
- *eos = '\0';
- *rfc5987 = 1;
- Z_STRLEN_P(zv) -= 1;
+ if (flags & PHP_HTTP_PARAMS_RFC5987) {
+ eos = &Z_STRVAL_P(zv)[Z_STRLEN_P(zv)-1];
+ if (*eos == '*') {
+ *eos = '\0';
+ *rfc5987 = 1;
+ Z_STRLEN_P(zv) -= 1;
+ }
}
if (flags & PHP_HTTP_PARAMS_URLENCODED) {
if (state->val.str) {
if (0 < (state->val.len = state->input.str - state->val.str)) {
sanitize_value(opts->flags, state->val.str, state->val.len, state->current.val, state->rfc5987);
+ } else {
+ ZVAL_EMPTY_STRING(state->current.val);
}
state->rfc5987 = 0;
} else if (state->arg.str) {
return ZEND_HASH_APPLY_KEEP;
}
+static int apply_querystring_filter(zval *val)
+{
+ switch (Z_TYPE_P(val)) {
+ case IS_NULL:
+ return ZEND_HASH_APPLY_REMOVE;
+ case IS_ARRAY:
+ case IS_OBJECT:
+ zend_hash_apply(HASH_OF(val), apply_querystring_filter);
+ if (!zend_hash_num_elements(HASH_OF(val))) {
+ return ZEND_HASH_APPLY_REMOVE;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return ZEND_HASH_APPLY_KEEP;
+}
+
ZEND_RESULT_CODE php_http_querystring_parse(HashTable *ht, const char *str, size_t len)
{
ZEND_RESULT_CODE rv = FAILURE;
zval_ptr_dtor(&arr);
}
- ZVAL_NULL(&opts.defval);
+ ZVAL_TRUE(&opts.defval);
if (php_http_params_parse(ht, &opts)) {
zend_hash_apply(ht, apply_querystring);
}
/* modify qarray */
- if (params) {
+ if (!params) {
+ zend_hash_apply(Z_ARRVAL_P(qarray), apply_querystring_filter);
+ } else {
HashTable *ht;
php_http_arrkey_t key;
zval zv, *params_entry, *qarray_entry;
ZVAL_DUP(entry, qarray_entry);
convert_to_array(entry);
php_http_querystring_update(entry, params_entry, NULL);
- } else if ((FAILURE == is_equal_function(&equal, qarray_entry, params_entry)) || Z_TYPE(equal) != IS_TRUE) {
+ } else if ((FAILURE == is_identical_function(&equal, qarray_entry, params_entry)) || Z_TYPE(equal) != IS_TRUE) {
Z_TRY_ADDREF_P(params_entry);
entry = params_entry;
}
--EXPECT--
http://www.example.com/foobar?bar.baz=blah&utm_source=google&utm_campaign=somethingelse&blat
-http://www.example.com/foobar?bar.baz=blah&utm_source=changed&utm_campaign=somethingelse
+http://www.example.com/foobar?bar.baz=blah&utm_source=changed&utm_campaign=somethingelse&blat
http://www.google.com/foobar?bar.baz=blah&utm_source=google&utm_campaign=somethingelse&blat
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");
$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===
$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"));
--- /dev/null
+--TEST--
+client curl user handler
+--SKIPIF--
+<?php
+include "skipif.inc";
+skip_client_test();
+?>
+--FILE--
+<?php
+echo "Test\n";
+
+class UserHandler implements http\Client\Curl\User
+{
+ private $client;
+ private $run;
+ private $fds = [
+ "R" => [],
+ "W" => []
+ ];
+ private $R = [];
+ private $W = [];
+ private $timeout = 1000;
+
+ function __construct(http\Client $client) {
+ $this->client = $client;
+ }
+
+ function init(callable $run) {
+ $this->run = $run;
+ }
+
+ function timer(int $timeout_ms) {
+ echo "T";
+ $this->timeout = $timeout_ms;
+ }
+
+ function socket($socket, int $action) {
+ echo "S";
+
+ switch ($action) {
+ case self::POLL_NONE:
+ break;
+ case self::POLL_REMOVE:
+ if (false !== ($r = array_search($socket, $this->fds["R"], true))) {
+ echo "U";
+ unset($this->fds["R"][$r]);
+ }
+
+ if (false !== ($w = array_search($socket, $this->fds["W"], true))) {
+ echo "U";
+ unset($this->fds["W"][$w]);
+ }
+
+ break;
+
+ default:
+ if ($action & self::POLL_IN) {
+ if (!in_array($socket, $this->fds["R"], true)) {
+ $this->fds["R"][] = $socket;
+ }
+ }
+ if ($action & self::POLL_OUT) {
+ if (!in_array($socket, $this->fds["W"], true)) {
+ $this->fds["W"][] = $socket;
+ }
+ }
+ break;
+ }
+ }
+
+ function once() {
+ echo "O";
+
+ foreach ($this->W as $w) {
+ call_user_func($this->run, $this->client, $w, self::POLL_OUT);
+ }
+ foreach ($this->R as $r) {
+ call_user_func($this->run, $this->client, $r, self::POLL_IN);
+ }
+ return count($this->client);
+ }
+
+ function wait(int $timeout_ms = null) {
+ echo "W";
+
+ if ($timeout_ms === null) {
+ $timeout_ms = $this->timeout;
+ }
+ $ts = floor($timeout_ms / 1000);
+ $tu = ($timeout_ms % 1000) * 1000;
+
+ extract($this->fds);
+
+ if (($wfds = count($R) + count($W))) {
+ $nfds = stream_select($R, $W, $E, $ts, $tu);
+ } else {
+ $nfds = 0;
+ }
+ $this->R = (array) $R;
+ $this->W = (array) $W;
+
+ if ($nfds === false) {
+ return false;
+ }
+ if (!$nfds) {
+ if (!$wfds) {
+ echo "S";
+ time_nanosleep($ts, $tu*1000);
+ }
+ call_user_func($this->run, $this->client);
+ }
+
+ return true;
+ }
+
+ function send() {
+ $this->wait();
+ $this->once();
+ }
+}
+
+
+include "helper/server.inc";
+
+server("proxy.inc", function($port) {
+ $client = new http\Client;
+ $client->configure([
+ "use_eventloop" => new UserHandler($client)
+ ]);
+ $client->enqueue(new http\Client\Request("GET", "http://localhost:$port/"), function($r) {
+ var_dump($r->getResponseCode());
+ });
+ $client->send();
+});
+
+?>
+===DONE===
+--EXPECTREGEX--
+Test
+T[WST]*(O[WST]*)+U+int\(200\)
+===DONE===
echo "Test\n";
+function r($v) { return round($v, 2); }
+
$a = new http\Header("Accept", "text/html, text/plain;q=0.5, */*;q=0");
var_dump("text/html" === $a->negotiate(array("text/plain","text/html")));
var_dump("text/html" === $a->negotiate(array("text/plain","text/html"), $rs));
-var_dump(array("text/html"=>0.99, "text/plain"=>0.5) === $rs);
+var_dump(array("text/html"=>0.99, "text/plain"=>0.5) === array_map("r", $rs));
var_dump("text/plain" === $a->negotiate(array("foo/bar", "text/plain"), $rs));
-var_dump(array("text/plain"=>0.5) === $rs);
+var_dump(array("text/plain"=>0.5) === array_map("r", $rs));
var_dump("foo/bar" === $a->negotiate(array("foo/bar"), $rs));
var_dump(array() === $rs);
/* 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);
CONTENT TYPE
<?php
+
+function dump($ctr) {
+ print_r(array_map(function($v){return round($v,2);}, $ctr));
+}
+
$ct = http\Env::negotiateContentType(array("text/html","text/xml","text/json"), $ctr);
-echo "$ct: "; print_r($ctr);
+echo "$ct: "; dump($ctr);
$ct = http\Env::negotiateContentType(array("text/xml","text/json"), $ctr);
-echo "$ct: "; print_r($ctr);
+echo "$ct: "; dump($ctr);
$ct = http\Env::negotiateContentType(array("text/json"), $ctr);
-echo "$ct: "; print_r($ctr);
+echo "$ct: "; dump($ctr);
?>
CHARSET
<?php
$cs = http\Env::negotiateCharset(array("utf-8", "iso-8859-1", "iso-8859-15"), $csr);
-echo "$cs: "; print_r($csr);
+echo "$cs: "; dump($csr);
$cs = http\Env::negotiateCharset(array("iso-8859-1", "iso-8859-15"), $csr);
-echo "$cs: "; print_r($csr);
+echo "$cs: "; dump($csr);
$cs = http\Env::negotiateCharset(array("utf-16", "iso-8859-15", "iso-8859-2"), $csr);
-echo "$cs: "; print_r($csr);
+echo "$cs: "; dump($csr);
?>
ENCODING
<?php
$ce = http\Env::negotiateEncoding(array("gzip", "deflate", "sdch"), $cer);
-echo "$ce: "; print_r($cer);
+echo "$ce: "; dump($cer);
$ce = http\Env::negotiateEncoding(array("", "sdch"), $cer);
-echo "$ce: "; print_r($cer);
+echo "$ce: "; dump($cer);
?>
LANGUAGE
<?php
$ln = http\Env::negotiateLanguage(array("de", "en", "fr"), $lnr);
-echo "$ln: "; print_r($lnr);
+echo "$ln: "; dump($lnr);
$ln = http\Env::negotiateLanguage(array("de-DE", "de-AT", "en"), $lnr);
-echo "$ln: "; print_r($lnr);
+echo "$ln: "; dump($lnr);
$ln = http\Env::negotiateLanguage(array("nl", "fr", "en"), $lnr);
-echo "$ln: "; print_r($lnr);
+echo "$ln: "; dump($lnr);
?>
CUSTOM
<?php
$cc = http\Env::negotiate("a, a.b;q=0.9, c.d;q=0, *.* ; q=0.1",
array("a.x", "c.d", "c.e", "a.b"), ".", $ccr);
-echo "$cc: "; print_r($ccr);
+echo "$cc: "; dump($ccr);
?>
DONE
--EXPECT--
de: Array
(
- [de] => 0.99
+ [de] => 0.97
[en] => 0.8
)
de-DE: Array
a.b: Array
(
[a.b] => 0.9
- [a.x] => 0.1
- [c.e] => 0.1
+ [a.x] => 0.08
+ [c.e] => 0.08
)
DONE
http\Params::DEF_PARAM_SEP,
http\Params::DEF_ARG_SEP,
http\Params::DEF_VAL_SEP,
- http\Params::PARSE_RFC5988 | http\Params::PARSE_ESCAPED
+ http\Params::PARSE_RFC5987 | http\Params::PARSE_RFC5988 | http\Params::PARSE_ESCAPED
);
var_dump($p->params);
var_dump((string)$p);
var_dump($u === (string) new http\Url($u));
$url = new http\Url($u,
- array("path" => "changed", "query" => "foo=&added=this"),
+ array("path" => "changed", "query" => "bar&foo=&added=this"),
http\Url::JOIN_PATH |
http\Url::JOIN_QUERY |
http\Url::STRIP_AUTH |
string(15) "www.example.com"
int(8080)
string(13) "/path/changed"
-string(38) "more%5B0%5D=1&more%5B1%5D=2&added=this"
+string(47) "foo=&more%5B0%5D=1&more%5B1%5D=2&bar&added=this"
NULL
DONE