administrativa
[m6w6/ext-http] / src / php_http_client_curl_user.c
1 /*
2 +--------------------------------------------------------------------+
3 | PECL :: http |
4 +--------------------------------------------------------------------+
5 | Redistribution and use in source and binary forms, with or without |
6 | modification, are permitted provided that the conditions mentioned |
7 | in the accompanying LICENSE file are met. |
8 +--------------------------------------------------------------------+
9 | Copyright (c) 2004-2014, Michael Wallner <mike@php.net> |
10 +--------------------------------------------------------------------+
11 */
12
13 #include "php_http_api.h"
14 #include "php_http_client.h"
15 #include "php_http_client_curl.h"
16 #include "php_http_client_curl_user.h"
17
18 #include "php_network.h"
19 #include "zend_closures.h"
20
21 #if PHP_HTTP_HAVE_CURL
22
23 typedef struct php_http_client_curl_user_context {
24 php_http_client_t *client;
25 zval *user;
26 zend_function closure;
27 php_http_object_method_t timer;
28 php_http_object_method_t socket;
29 php_http_object_method_t once;
30 php_http_object_method_t wait;
31 php_http_object_method_t send;
32 } php_http_client_curl_user_context_t;
33
34 typedef struct php_http_client_curl_user_ev {
35 php_stream *socket;
36 php_http_client_curl_user_context_t *context;
37 } php_http_client_curl_user_ev_t;
38
39 static void php_http_client_curl_user_handler(INTERNAL_FUNCTION_PARAMETERS)
40 {
41 zval *zstream = NULL, *zclient = NULL;
42 php_stream *stream = NULL;
43 long action = 0;
44 php_socket_t fd = CURL_SOCKET_TIMEOUT;
45 php_http_client_object_t *client = NULL;
46 php_http_client_curl_t *curl;
47
48 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O|rl", &zclient, php_http_client_class_entry, &zstream, &action)) {
49 return;
50 }
51
52 client = zend_object_store_get_object(zclient TSRMLS_CC);
53 if (zstream) {
54 php_stream_from_zval(stream, &zstream);
55
56 if (SUCCESS != php_stream_cast(stream, PHP_STREAM_AS_SOCKETD, (void *) &fd, 1)) {
57 return;
58 }
59 }
60 php_http_client_curl_loop(client->client, fd, action);
61
62 curl = client->client->ctx;
63 RETVAL_LONG(curl->unfinished);
64 }
65
66 static void php_http_client_curl_user_timer(CURLM *multi, long timeout_ms, void *timer_data)
67 {
68 php_http_client_curl_user_context_t *context = timer_data;
69
70 #if DBG_EVENTS
71 fprintf(stderr, "\ntimer <- timeout_ms: %ld\n", timeout_ms);
72 #endif
73
74 if (timeout_ms <= 0) {
75 php_http_client_curl_loop(context->client, CURL_SOCKET_TIMEOUT, 0);
76 } else if (timeout_ms > 0) {
77 zval **args[1], *ztimeout;
78 TSRMLS_FETCH_FROM_CTX(context->client->ts);
79
80 MAKE_STD_ZVAL(ztimeout);
81 ZVAL_LONG(ztimeout, timeout_ms);
82 args[0] = &ztimeout;
83 php_http_object_method_call(&context->timer, context->user, NULL, 1, args TSRMLS_CC);
84 zval_ptr_dtor(&ztimeout);
85 }
86 }
87
88 static int php_http_client_curl_user_socket(CURL *easy, curl_socket_t sock, int action, void *socket_data, void *assign_data)
89 {
90 php_http_client_curl_user_context_t *ctx = socket_data;
91 php_http_client_curl_t *curl = ctx->client->ctx;
92 php_http_client_curl_user_ev_t *ev = assign_data;
93 zval **args[2], *zaction, *zsocket;
94 TSRMLS_FETCH_FROM_CTX(ctx->client->ts);
95
96 #if DBG_EVENTS
97 fprintf(stderr, "S");
98 #endif
99
100 if (!ev) {
101 ev = ecalloc(1, sizeof(*ev));
102 ev->context = ctx;
103 ev->socket = php_stream_sock_open_from_socket(sock, NULL);
104
105 curl_multi_assign(curl->handle->multi, sock, ev);
106 }
107
108 switch (action) {
109 case CURL_POLL_IN:
110 case CURL_POLL_OUT:
111 case CURL_POLL_INOUT:
112 case CURL_POLL_REMOVE:
113 case CURL_POLL_NONE:
114 MAKE_STD_ZVAL(zsocket);
115 php_stream_to_zval(ev->socket, zsocket);
116 args[0] = &zsocket;
117 MAKE_STD_ZVAL(zaction);
118 ZVAL_LONG(zaction, action);
119 args[1] = &zaction;
120 php_http_object_method_call(&ctx->socket, ctx->user, NULL, 2, args TSRMLS_CC);
121 zval_ptr_dtor(&zsocket);
122 zval_ptr_dtor(&zaction);
123 break;
124
125 default:
126 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown socket action %d", action);
127 return -1;
128 }
129
130 if (action == CURL_POLL_REMOVE && ev) {
131 efree(ev);
132 }
133 return 0;
134 }
135
136 static ZEND_RESULT_CODE php_http_client_curl_user_once(void *context)
137 {
138 php_http_client_curl_user_context_t *ctx = context;
139 TSRMLS_FETCH_FROM_CTX(ctx->client->ts);
140
141 #if DBG_EVENTS
142 fprintf(stderr, "O");
143 #endif
144
145 return php_http_object_method_call(&ctx->once, ctx->user, NULL, 0, NULL TSRMLS_CC);
146 }
147
148 static ZEND_RESULT_CODE php_http_client_curl_user_wait(void *context, struct timeval *custom_timeout)
149 {
150 php_http_client_curl_user_context_t *ctx = context;
151 struct timeval timeout;
152 zval **args[1], *ztimeout;
153 ZEND_RESULT_CODE rv;
154 TSRMLS_FETCH_FROM_CTX(ctx->client->ts);
155
156 #if DBG_EVENTS
157 fprintf(stderr, "W");
158 #endif
159
160 if (!custom_timeout || !timerisset(custom_timeout)) {
161 php_http_client_curl_get_timeout(ctx->client->ctx, 1000, &timeout);
162 custom_timeout = &timeout;
163 }
164
165 MAKE_STD_ZVAL(ztimeout);
166 ZVAL_LONG(ztimeout, custom_timeout->tv_sec * 1000 + custom_timeout->tv_usec / 1000);
167 args[0] = &ztimeout;
168 rv = php_http_object_method_call(&ctx->wait, ctx->user, NULL, 1, args TSRMLS_CC);
169 zval_ptr_dtor(&ztimeout);
170
171 return rv;
172 }
173
174 static ZEND_RESULT_CODE php_http_client_curl_user_exec(void *context)
175 {
176 php_http_client_curl_user_context_t *ctx = context;
177 php_http_client_curl_t *curl = ctx->client->ctx;
178 TSRMLS_FETCH_FROM_CTX(ctx->client->ts);
179
180 #if DBG_EVENTS
181 fprintf(stderr, "E");
182 #endif
183
184 /* kickstart */
185 php_http_client_curl_loop(ctx->client, CURL_SOCKET_TIMEOUT, 0);
186
187 do {
188 if (SUCCESS != php_http_object_method_call(&ctx->send, ctx->user, NULL, 0, NULL TSRMLS_CC)) {
189 return FAILURE;
190 }
191 } while (curl->unfinished && !EG(exception));
192
193 return SUCCESS;
194 }
195
196 static void *php_http_client_curl_user_init(php_http_client_t *client, void *user_data)
197 {
198 php_http_client_curl_t *curl = client->ctx;
199 php_http_client_curl_user_context_t *ctx;
200 php_http_object_method_t init;
201 zval *zclosure, **args[1];
202 TSRMLS_FETCH_FROM_CTX(client->ts);
203
204 #if DBG_EVENTS
205 fprintf(stderr, "I");
206 #endif
207
208 ctx = ecalloc(1, sizeof(*ctx));
209 ctx->client = client;
210 ctx->user = user_data;
211 Z_ADDREF_P(ctx->user);
212
213 memset(&ctx->closure, 0, sizeof(ctx->closure));
214 ctx->closure.common.type = ZEND_INTERNAL_FUNCTION;
215 ctx->closure.common.function_name = "php_http_client_curl_user_handler";
216 ctx->closure.internal_function.handler = php_http_client_curl_user_handler;
217
218 MAKE_STD_ZVAL(zclosure);
219 #if PHP_VERSION_ID >= 50400
220 zend_create_closure(zclosure, &ctx->closure, NULL, NULL TSRMLS_CC);
221 #else
222 zend_create_closure(zclosure, &ctx->closure TSRMLS_CC);
223 #endif
224 args[0] = &zclosure;
225
226 php_http_object_method_init(&init, ctx->user, ZEND_STRL("init") TSRMLS_CC);
227 php_http_object_method_call(&init, ctx->user, NULL, 1, args TSRMLS_CC);
228 php_http_object_method_dtor(&init);
229 zval_ptr_dtor(&zclosure);
230
231 php_http_object_method_init(&ctx->timer, ctx->user, ZEND_STRL("timer") TSRMLS_CC);
232 php_http_object_method_init(&ctx->socket, ctx->user, ZEND_STRL("socket") TSRMLS_CC);
233 php_http_object_method_init(&ctx->once, ctx->user, ZEND_STRL("once") TSRMLS_CC);
234 php_http_object_method_init(&ctx->wait, ctx->user, ZEND_STRL("wait") TSRMLS_CC);
235 php_http_object_method_init(&ctx->send, ctx->user, ZEND_STRL("send") TSRMLS_CC);
236
237 curl_multi_setopt(curl->handle->multi, CURLMOPT_SOCKETDATA, ctx);
238 curl_multi_setopt(curl->handle->multi, CURLMOPT_SOCKETFUNCTION, php_http_client_curl_user_socket);
239 curl_multi_setopt(curl->handle->multi, CURLMOPT_TIMERDATA, ctx);
240 curl_multi_setopt(curl->handle->multi, CURLMOPT_TIMERFUNCTION, php_http_client_curl_user_timer);
241
242 return ctx;
243 }
244
245 static void php_http_client_curl_user_dtor(void **context)
246 {
247 php_http_client_curl_user_context_t *ctx = *context;
248 php_http_client_curl_t *curl;
249
250 #if DBG_EVENTS
251 fprintf(stderr, "D");
252 #endif
253
254 curl = ctx->client->ctx;
255
256 curl_multi_setopt(curl->handle->multi, CURLMOPT_SOCKETDATA, NULL);
257 curl_multi_setopt(curl->handle->multi, CURLMOPT_SOCKETFUNCTION, NULL);
258 curl_multi_setopt(curl->handle->multi, CURLMOPT_TIMERDATA, NULL);
259 curl_multi_setopt(curl->handle->multi, CURLMOPT_TIMERFUNCTION, NULL);
260
261 php_http_object_method_dtor(&ctx->timer);
262 php_http_object_method_dtor(&ctx->socket);
263 php_http_object_method_dtor(&ctx->once);
264 php_http_object_method_dtor(&ctx->wait);
265 php_http_object_method_dtor(&ctx->send);
266
267 zval_ptr_dtor(&ctx->user);
268
269 efree(ctx);
270 *context = NULL;
271 }
272
273 static php_http_client_curl_ops_t php_http_client_curl_user_ops = {
274 &php_http_client_curl_user_init,
275 &php_http_client_curl_user_dtor,
276 &php_http_client_curl_user_once,
277 &php_http_client_curl_user_wait,
278 &php_http_client_curl_user_exec,
279 };
280
281 php_http_client_curl_ops_t *php_http_client_curl_user_ops_get()
282 {
283 return &php_http_client_curl_user_ops;
284 }
285
286 zend_class_entry *php_http_client_curl_user_class_entry;
287
288 ZEND_BEGIN_ARG_INFO_EX(ai_init, 0, 0, 1)
289 /* using IS_CALLABLE type hint would create a forwards compatibility break */
290 ZEND_ARG_INFO(0, run)
291 ZEND_END_ARG_INFO();
292 ZEND_BEGIN_ARG_INFO_EX(ai_timer, 0, 0, 1)
293 #if PHP_VERSION_ID >= 70000
294 ZEND_ARG_TYPE_INFO(0, timeout_ms, IS_LONG, 0)
295 #else
296 ZEND_ARG_INFO(0, timeout_ms)
297 #endif
298 ZEND_END_ARG_INFO();
299 ZEND_BEGIN_ARG_INFO_EX(ai_socket, 0, 0, 2)
300 #if PHP_VERSION_ID >= 70000
301 ZEND_ARG_TYPE_INFO(0, socket, IS_RESOURCE, 0)
302 ZEND_ARG_TYPE_INFO(0, action, IS_LONG, 0)
303 #else
304 ZEND_ARG_INFO(0, socket)
305 ZEND_ARG_INFO(0, action)
306 #endif
307 ZEND_END_ARG_INFO();
308 ZEND_BEGIN_ARG_INFO_EX(ai_once, 0, 0, 0)
309 ZEND_END_ARG_INFO();
310 ZEND_BEGIN_ARG_INFO_EX(ai_wait, 0, 0, 0)
311 #if PHP_VERSION_ID >= 70000
312 ZEND_ARG_TYPE_INFO(0, timeout_ms, IS_LONG, 0)
313 #else
314 ZEND_ARG_INFO(0, timeout_ms)
315 #endif
316 ZEND_END_ARG_INFO();
317 ZEND_BEGIN_ARG_INFO_EX(ai_send, 0, 0, 0)
318 ZEND_END_ARG_INFO();
319
320 static zend_function_entry php_http_client_curl_user_methods[] = {
321 PHP_ABSTRACT_ME(HttpClientCurlUser, init, ai_init)
322 PHP_ABSTRACT_ME(HttpClientCurlUser, timer, ai_timer)
323 PHP_ABSTRACT_ME(HttpClientCurlUser, socket, ai_socket)
324 PHP_ABSTRACT_ME(HttpClientCurlUser, once, ai_once)
325 PHP_ABSTRACT_ME(HttpClientCurlUser, wait, ai_wait)
326 PHP_ABSTRACT_ME(HttpClientCulrUser, send, ai_send)
327 {0}
328 };
329
330 PHP_MINIT_FUNCTION(http_client_curl_user)
331 {
332 zend_class_entry ce = {0};
333
334 INIT_NS_CLASS_ENTRY(ce, "http\\Client\\Curl", "User", php_http_client_curl_user_methods);
335 php_http_client_curl_user_class_entry = zend_register_internal_interface(&ce TSRMLS_CC);
336
337 zend_declare_class_constant_long(php_http_client_curl_user_class_entry, ZEND_STRL("POLL_NONE"), CURL_POLL_NONE TSRMLS_CC);
338 zend_declare_class_constant_long(php_http_client_curl_user_class_entry, ZEND_STRL("POLL_IN"), CURL_POLL_IN TSRMLS_CC);
339 zend_declare_class_constant_long(php_http_client_curl_user_class_entry, ZEND_STRL("POLL_OUT"), CURL_POLL_OUT TSRMLS_CC);
340 zend_declare_class_constant_long(php_http_client_curl_user_class_entry, ZEND_STRL("POLL_INOUT"), CURL_POLL_INOUT TSRMLS_CC);
341 zend_declare_class_constant_long(php_http_client_curl_user_class_entry, ZEND_STRL("POLL_REMOVE"), CURL_POLL_REMOVE TSRMLS_CC);
342
343 return SUCCESS;
344 }
345
346 #endif /* PHP_HTTP_HAVE_CURL */
347
348 /*
349 * Local variables:
350 * tab-width: 4
351 * c-basic-offset: 4
352 * End:
353 * vim600: noet sw=4 ts=4 fdm=marker
354 * vim<600: noet sw=4 ts=4
355 */