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