X-Git-Url: https://git.m6w6.name/?p=m6w6%2Fext-http;a=blobdiff_plain;f=src%2Fphp_http_client_curl_user.c;fp=src%2Fphp_http_client_curl_user.c;h=a30d666098489d81559aece92aee42247163fd38;hp=0000000000000000000000000000000000000000;hb=43a9a6f8cb56e25c3770d652ce77045f89f68ca6;hpb=18871cafc58e15054412aea57b2456ffc0d49713 diff --git a/src/php_http_client_curl_user.c b/src/php_http_client_curl_user.c new file mode 100644 index 0000000..a30d666 --- /dev/null +++ b/src/php_http_client_curl_user.c @@ -0,0 +1,323 @@ +/* + +--------------------------------------------------------------------+ + | 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_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 + */