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