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