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