cleanups & includes
[m6w6/ext-http] / php_http_request_pool.c
1
2 #include "php_http.h"
3
4 #ifdef PHP_HTTP_HAVE_EVENT
5 # include <event.h>
6 #endif
7
8 #include <ext/spl/spl_iterators.h>
9 #include <Zend/zend_interfaces.h>
10
11 #ifndef PHP_HTTP_DEBUG_REQPOOLS
12 # define PHP_HTTP_DEBUG_REQPOOLS 0
13 #endif
14
15 #ifdef PHP_HTTP_HAVE_EVENT
16 typedef struct php_http_request_pool_event {
17 struct event evnt;
18 php_http_request_pool_t *pool;
19 } php_http_request_pool_event_t;
20
21 static void php_http_request_pool_timeout_callback(int socket, short action, void *event_data);
22 static void php_http_request_pool_event_callback(int socket, short action, void *event_data);
23 static int php_http_request_pool_socket_callback(CURL *easy, curl_socket_t s, int action, void *, void *);
24 static void php_http_request_pool_timer_callback(CURLM *multi, long timeout_ms, void *timer_data);
25 #endif
26
27 static int php_http_request_pool_compare_handles(void *h1, void *h2);
28
29 PHP_HTTP_API php_http_request_pool_t *php_http_request_pool_init(php_http_request_pool_t *pool TSRMLS_DC)
30 {
31 zend_bool free_pool;
32
33 #if PHP_HTTP_DEBUG_REQPOOLS
34 fprintf(stderr, "Initializing request pool %p\n", pool);
35 #endif
36
37 if ((free_pool = (!pool))) {
38 pool = emalloc(sizeof(php_http_request_pool_t));
39 pool->ch = NULL;
40 }
41
42 if (SUCCESS != php_http_persistent_handle_acquire(ZEND_STRL("http_request_pool"), &pool->ch TSRMLS_CC)) {
43 if (free_pool) {
44 efree(pool);
45 }
46 return NULL;
47 }
48
49 TSRMLS_SET_CTX(pool->ts);
50
51 #ifdef PHP_HTTP_HAVE_EVENT
52 pool->timeout = ecalloc(1, sizeof(struct event));
53 curl_multi_setopt(pool->ch, CURLMOPT_SOCKETDATA, pool);
54 curl_multi_setopt(pool->ch, CURLMOPT_SOCKETFUNCTION, php_http_request_pool_socket_callback);
55 curl_multi_setopt(pool->ch, CURLMOPT_TIMERDATA, pool);
56 curl_multi_setopt(pool->ch, CURLMOPT_TIMERFUNCTION, php_http_request_pool_timer_callback);
57 #endif
58
59 pool->unfinished = 0;
60 zend_llist_init(&pool->finished, sizeof(zval *), (llist_dtor_func_t) ZVAL_PTR_DTOR, 0);
61 zend_llist_init(&pool->handles, sizeof(zval *), (llist_dtor_func_t) ZVAL_PTR_DTOR, 0);
62
63 #if PHP_HTTP_DEBUG_REQPOOLS
64 fprintf(stderr, "Initialized request pool %p\n", pool);
65 #endif
66
67 return pool;
68 }
69
70 PHP_HTTP_API STATUS php_http_request_pool_attach(php_http_request_pool_t *pool, zval *request)
71 {
72 #ifdef ZTS
73 TSRMLS_FETCH_FROM_CTX(pool->ts);
74 #endif
75 php_http_request_object_t *req = zend_object_store_get_object(request TSRMLS_CC);
76
77 #if PHP_HTTP_DEBUG_REQPOOLS
78 fprintf(stderr, "Attaching HttpRequest(#%d) %p to pool %p\n", Z_OBJ_HANDLE_P(request), req, pool);
79 #endif
80
81 if (req->pool) {
82 php_http_error(HE_WARNING, PHP_HTTP_E_INVALID_PARAM, "HttpRequest object(#%d) is already member of %s HttpRequestPool", Z_OBJ_HANDLE_P(request), req->pool == pool ? "this" : "another");
83 } else if (SUCCESS != php_http_request_object_requesthandler(req, request)) {
84 php_http_error(HE_WARNING, PHP_HTTP_E_REQUEST, "Could not initialize HttpRequest object(#%d) for attaching to the HttpRequestPool", Z_OBJ_HANDLE_P(request));
85 } else {
86 CURLMcode code = curl_multi_add_handle(pool->ch, req->request->ch);
87
88 if (CURLM_OK != code) {
89 php_http_error(HE_WARNING, PHP_HTTP_E_REQUEST_POOL, "Could not attach HttpRequest object(#%d) to the HttpRequestPool: %s", Z_OBJ_HANDLE_P(request), curl_multi_strerror(code));
90 } else {
91 req->pool = pool;
92
93 Z_ADDREF_P(request);
94 zend_llist_add_element(&pool->handles, &request);
95 ++pool->unfinished;
96
97 #if PHP_HTTP_DEBUG_REQPOOLS
98 fprintf(stderr, "> %d HttpRequests attached to pool %p\n", zend_llist_count(&pool->handles), pool);
99 #endif
100 return SUCCESS;
101 }
102 }
103 return FAILURE;
104 }
105
106 PHP_HTTP_API STATUS php_http_request_pool_detach(php_http_request_pool_t *pool, zval *request)
107 {
108 CURLMcode code;
109 #ifdef ZTS
110 TSRMLS_FETCH_FROM_CTX(pool->ts);
111 #endif
112 php_http_request_object_t *req = zend_object_store_get_object(request TSRMLS_CC);
113
114 #if PHP_HTTP_DEBUG_REQPOOLS
115 fprintf(stderr, "Detaching HttpRequest(#%d) %p from pool %p\n", Z_OBJ_HANDLE_P(request), req, pool);
116 #endif
117
118 if (!req->pool) {
119 /* not attached to any pool */
120 #if PHP_HTTP_DEBUG_REQPOOLS
121 fprintf(stderr, "HttpRequest object(#%d) %p is not attached to any HttpRequestPool\n", Z_OBJ_HANDLE_P(request), req);
122 #endif
123 } else if (req->pool != pool) {
124 php_http_error(HE_WARNING, PHP_HTTP_E_INVALID_PARAM, "HttpRequest object(#%d) is not attached to this HttpRequestPool", Z_OBJ_HANDLE_P(request));
125 } else if (req->request->_progress.in_cb) {
126 php_http_error(HE_WARNING, PHP_HTTP_E_REQUEST_POOL, "HttpRequest object(#%d) cannot be detached from the HttpRequestPool while executing the progress callback", Z_OBJ_HANDLE_P(request));
127 } else if (CURLM_OK != (code = curl_multi_remove_handle(pool->ch, req->request->ch))) {
128 php_http_error(HE_WARNING, PHP_HTTP_E_REQUEST_POOL, "Could not detach HttpRequest object(#%d) from the HttpRequestPool: %s", Z_OBJ_HANDLE_P(request), curl_multi_strerror(code));
129 } else {
130 req->pool = NULL;
131 zend_llist_del_element(&pool->finished, request, php_http_request_pool_compare_handles);
132 zend_llist_del_element(&pool->handles, request, php_http_request_pool_compare_handles);
133
134 #if PHP_HTTP_DEBUG_REQPOOLS
135 fprintf(stderr, "> %d HttpRequests remaining in pool %p\n", zend_llist_count(&pool->handles), pool);
136 #endif
137
138 return SUCCESS;
139 }
140 return FAILURE;
141 }
142
143 PHP_HTTP_API void php_http_request_pool_apply(php_http_request_pool_t *pool, php_http_request_pool_apply_func_t cb)
144 {
145 int count = zend_llist_count(&pool->handles);
146
147 if (count) {
148 int i = 0;
149 zend_llist_position pos;
150 zval **handle, **handles = emalloc(count * sizeof(zval *));
151
152 for (handle = zend_llist_get_first_ex(&pool->handles, &pos); handle; handle = zend_llist_get_next_ex(&pool->handles, &pos)) {
153 handles[i++] = *handle;
154 }
155
156 /* should never happen */
157 if (i != count) {
158 zend_error(E_ERROR, "number of fetched request handles do not match overall count");
159 count = i;
160 }
161
162 for (i = 0; i < count; ++i) {
163 if (cb(pool, handles[i])) {
164 break;
165 }
166 }
167 efree(handles);
168 }
169 }
170
171 PHP_HTTP_API void php_http_request_pool_apply_with_arg(php_http_request_pool_t *pool, php_http_request_pool_apply_with_arg_func_t cb, void *arg)
172 {
173 int count = zend_llist_count(&pool->handles);
174
175 if (count) {
176 int i = 0;
177 zend_llist_position pos;
178 zval **handle, **handles = emalloc(count * sizeof(zval *));
179
180 for (handle = zend_llist_get_first_ex(&pool->handles, &pos); handle; handle = zend_llist_get_next_ex(&pool->handles, &pos)) {
181 handles[i++] = *handle;
182 }
183
184 /* should never happen */
185 if (i != count) {
186 zend_error(E_ERROR, "number of fetched request handles do not match overall count");
187 count = i;
188 }
189
190 for (i = 0; i < count; ++i) {
191 if (cb(pool, handles[i], arg)) {
192 break;
193 }
194 }
195 efree(handles);
196 }
197 }
198
199 PHP_HTTP_API void php_http_request_pool_detach_all(php_http_request_pool_t *pool)
200 {
201 #if PHP_HTTP_DEBUG_REQPOOLS
202 fprintf(stderr, "Detaching %d requests from pool %p\n", zend_llist_count(&pool->handles), pool);
203 #endif
204 php_http_request_pool_apply(pool, php_http_request_pool_detach);
205 }
206
207 PHP_HTTP_API STATUS php_http_request_pool_send(php_http_request_pool_t *pool)
208 {
209 TSRMLS_FETCH_FROM_CTX(pool->ts);
210
211 #if PHP_HTTP_DEBUG_REQPOOLS
212 fprintf(stderr, "Attempt to send %d requests of pool %p\n", zend_llist_count(&pool->handles), pool);
213 #endif
214
215 #ifdef PHP_HTTP_HAVE_EVENT
216 if (pool->useevents) {
217 do {
218 #if PHP_HTTP_DEBUG_REQPOOLS
219 fprintf(stderr, "& Starting event dispatcher of pool %p\n", pool);
220 #endif
221 event_base_dispatch(PHP_HTTP_G->request_pool.event_base);
222 } while (pool->unfinished);
223 } else
224 #endif
225 {
226 while (php_http_request_pool_perform(pool)) {
227 if (SUCCESS != php_http_request_pool_select(pool, NULL)) {
228 #ifdef PHP_WIN32
229 /* see http://msdn.microsoft.com/library/en-us/winsock/winsock/windows_sockets_error_codes_2.asp */
230 php_http_error(HE_WARNING, PHP_HTTP_E_SOCKET, "WinSock error: %d", WSAGetLastError());
231 #else
232 php_http_error(HE_WARNING, PHP_HTTP_E_SOCKET, strerror(errno));
233 #endif
234 return FAILURE;
235 }
236 }
237 }
238
239 #if PHP_HTTP_DEBUG_REQPOOLS
240 fprintf(stderr, "Finished sending %d HttpRequests of pool %p (still unfinished: %d)\n", zend_llist_count(&pool->handles), pool, pool->unfinished);
241 #endif
242
243 return SUCCESS;
244 }
245
246 PHP_HTTP_API void php_http_request_pool_dtor(php_http_request_pool_t *pool)
247 {
248 TSRMLS_FETCH_FROM_CTX(pool->ts);
249
250 #if PHP_HTTP_DEBUG_REQPOOLS
251 fprintf(stderr, "Destructing request pool %p\n", pool);
252 #endif
253
254 #ifdef PHP_HTTP_HAVE_EVENT
255 efree(pool->timeout);
256 #endif
257
258 php_http_request_pool_detach_all(pool);
259
260 pool->unfinished = 0;
261 zend_llist_clean(&pool->finished);
262 zend_llist_clean(&pool->handles);
263 php_http_persistent_handle_release(ZEND_STRL("php_http_request_pool_t"), &pool->ch TSRMLS_CC);
264 }
265
266 PHP_HTTP_API void php_http_request_pool_free(php_http_request_pool_t **pool) {
267 if (*pool) {
268 php_http_request_pool_dtor(*pool);
269 efree(*pool);
270 *pool = NULL;
271 }
272 }
273
274 #ifdef PHP_WIN32
275 # define SELECT_ERROR SOCKET_ERROR
276 #else
277 # define SELECT_ERROR -1
278 #endif
279
280 PHP_HTTP_API STATUS php_http_request_pool_select(php_http_request_pool_t *pool, struct timeval *custom_timeout)
281 {
282 int MAX;
283 fd_set R, W, E;
284 struct timeval timeout;
285
286 #ifdef PHP_HTTP_HAVE_EVENT
287 if (pool->useevents) {
288 TSRMLS_FETCH_FROM_CTX(pool->ts);
289 php_http_error(HE_WARNING, PHP_HTTP_E_RUNTIME, "not implemented; use HttpRequest callbacks");
290 return FAILURE;
291 }
292 #endif
293
294 if (custom_timeout && timerisset(custom_timeout)) {
295 timeout = *custom_timeout;
296 } else {
297 php_http_request_pool_timeout(pool, &timeout);
298 }
299
300 FD_ZERO(&R);
301 FD_ZERO(&W);
302 FD_ZERO(&E);
303
304 if (CURLM_OK == curl_multi_fdset(pool->ch, &R, &W, &E, &MAX)) {
305 if (MAX == -1) {
306 php_http_sleep((double) timeout.tv_sec + (double) (timeout.tv_usec / PHP_HTTP_MCROSEC));
307 return SUCCESS;
308 } else if (SELECT_ERROR != select(MAX + 1, &R, &W, &E, &timeout)) {
309 return SUCCESS;
310 }
311 }
312 return FAILURE;
313 }
314
315 PHP_HTTP_API int php_http_request_pool_perform(php_http_request_pool_t *pool)
316 {
317 TSRMLS_FETCH_FROM_CTX(pool->ts);
318
319 #ifdef PHP_HTTP_HAVE_EVENT
320 if (pool->useevents) {
321 php_http_error(HE_WARNING, PHP_HTTP_E_RUNTIME, "not implemented; use HttpRequest callbacks");
322 return FAILURE;
323 }
324 #endif
325
326 while (CURLM_CALL_MULTI_PERFORM == curl_multi_perform(pool->ch, &pool->unfinished));
327
328 #if PHP_HTTP_DEBUG_REQPOOLS
329 fprintf(stderr, "%u unfinished requests of pool %p remaining\n", pool->unfinished, pool);
330 #endif
331
332 php_http_request_pool_responsehandler(pool);
333
334 return pool->unfinished;
335 }
336
337 void php_http_request_pool_responsehandler(php_http_request_pool_t *pool)
338 {
339 CURLMsg *msg;
340 int remaining = 0;
341 TSRMLS_FETCH_FROM_CTX(pool->ts);
342
343 do {
344 msg = curl_multi_info_read(pool->ch, &remaining);
345 if (msg && CURLMSG_DONE == msg->msg) {
346 if (CURLE_OK != msg->data.result) {
347 php_http_request_storage_t *st = php_http_request_storage_get(msg->easy_handle);
348 php_http_error(HE_WARNING, PHP_HTTP_E_REQUEST, "%s; %s (%s)", curl_easy_strerror(msg->data.result), STR_PTR(st->errorbuffer), STR_PTR(st->url));
349 }
350 php_http_request_pool_apply_with_arg(pool, php_http_request_pool_apply_responsehandler, msg->easy_handle);
351 }
352 } while (remaining);
353 }
354
355 int php_http_request_pool_apply_responsehandler(php_http_request_pool_t *pool, zval *req, void *ch)
356 {
357 #ifdef ZTS
358 TSRMLS_FETCH_FROM_CTX(pool->ts);
359 #endif
360 php_http_request_object_t *obj = zend_object_store_get_object(req TSRMLS_CC);
361
362 if ((!ch) || obj->request->ch == (CURL *) ch) {
363
364 #if PHP_HTTP_DEBUG_REQPOOLS
365 fprintf(stderr, "Fetching data from HttpRequest(#%d) %p of pool %p\n", Z_OBJ_HANDLE_P(req), obj, obj->pool);
366 #endif
367
368 Z_ADDREF_P(req);
369 zend_llist_add_element(&obj->pool->finished, &req);
370 php_http_request_object_responsehandler(obj, req);
371 return 1;
372 }
373 return 0;
374 }
375
376 struct timeval *php_http_request_pool_timeout(php_http_request_pool_t *pool, struct timeval *timeout)
377 {
378 #ifdef HAVE_CURL_MULTI_TIMEOUT
379 long max_tout = 1000;
380
381 if ((CURLM_OK == curl_multi_timeout(pool->ch, &max_tout)) && (max_tout > 0)) {
382 timeout->tv_sec = max_tout / 1000;
383 timeout->tv_usec = (max_tout % 1000) * 1000;
384 } else {
385 #endif
386 timeout->tv_sec = 0;
387 timeout->tv_usec = 1000;
388 #ifdef HAVE_CURL_MULTI_TIMEOUT
389 }
390 #endif
391
392 #if PHP_HTTP_DEBUG_REQPOOLS
393 fprintf(stderr, "Calculating timeout (%lu, %lu) of pool %p\n", (ulong) timeout->tv_sec, (ulong) timeout->tv_usec, pool);
394 #endif
395
396 return timeout;
397 }
398
399 /*#*/
400
401 static int php_http_request_pool_compare_handles(void *h1, void *h2)
402 {
403 return (Z_OBJ_HANDLE_PP((zval **) h1) == Z_OBJ_HANDLE_P((zval *) h2));
404 }
405
406 #ifdef PHP_HTTP_HAVE_EVENT
407
408 static void php_http_request_pool_timeout_callback(int socket, short action, void *event_data)
409 {
410 php_http_request_pool_t *pool = event_data;
411
412 if (pool->useevents) {
413 CURLMcode rc;
414 TSRMLS_FETCH_FROM_CTX(pool->ts);
415
416 #if PHP_HTTP_DEBUG_REQPOOLS
417 fprintf(stderr, "Timeout occurred of pool %p\n", pool);
418 #endif
419
420 while (CURLM_CALL_MULTI_PERFORM == (rc = curl_multi_socket(pool->ch, CURL_SOCKET_TIMEOUT, &pool->unfinished)));
421
422 if (CURLM_OK != rc) {
423 php_http_error(HE_WARNING, PHP_HTTP_E_SOCKET, curl_multi_strerror(rc));
424 }
425
426 php_http_request_pool_responsehandler(pool);
427 }
428 }
429
430 static void php_http_request_pool_event_callback(int socket, short action, void *event_data)
431 {
432 php_http_request_pool_event_t *ev = event_data;
433 php_http_request_pool_t *pool = ev->pool;
434
435 if (pool->useevents) {
436 CURLMcode rc = CURLE_OK;
437 TSRMLS_FETCH_FROM_CTX(ev->pool->ts);
438
439 #if PHP_HTTP_DEBUG_REQPOOLS
440 {
441 static const char event_strings[][20] = {"NONE","TIMEOUT","READ","TIMEOUT|READ","WRITE","TIMEOUT|WRITE","READ|WRITE","TIMEOUT|READ|WRITE","SIGNAL"};
442 fprintf(stderr, "Event on socket %d (%s) event %p of pool %p\n", socket, event_strings[action], ev, pool);
443 }
444 #endif
445
446 /* don't use 'ev' below this loop as it might 've been freed in the socket callback */
447 do {
448 #ifdef HAVE_CURL_MULTI_SOCKET_ACTION
449 switch (action & (EV_READ|EV_WRITE)) {
450 case EV_READ:
451 rc = curl_multi_socket_action(pool->ch, socket, CURL_CSELECT_IN, &pool->unfinished);
452 break;
453 case EV_WRITE:
454 rc = curl_multi_socket_action(pool->ch, socket, CURL_CSELECT_OUT, &pool->unfinished);
455 break;
456 case EV_READ|EV_WRITE:
457 rc = curl_multi_socket_action(pool->ch, socket, CURL_CSELECT_IN|CURL_CSELECT_OUT, &pool->unfinished);
458 break;
459 default:
460 php_http_error(HE_WARNING, PHP_HTTP_E_SOCKET, "Unknown event %d", (int) action);
461 return;
462 }
463 #else
464 rc = curl_multi_socket(pool->ch, socket, &pool->unfinished);
465 #endif
466 } while (CURLM_CALL_MULTI_PERFORM == rc);
467
468 switch (rc) {
469 case CURLM_BAD_SOCKET:
470 #if 0
471 fprintf(stderr, "!!! Bad socket: %d (%d)\n", socket, (int) action);
472 #endif
473 case CURLM_OK:
474 break;
475 default:
476 php_http_error(HE_WARNING, PHP_HTTP_E_SOCKET, curl_multi_strerror(rc));
477 break;
478 }
479
480 php_http_request_pool_responsehandler(pool);
481
482 /* remove timeout if there are no transfers left */
483 if (!pool->unfinished && event_initialized(pool->timeout) && event_pending(pool->timeout, EV_TIMEOUT, NULL)) {
484 event_del(pool->timeout);
485 #if PHP_HTTP_DEBUG_REQPOOLS
486 fprintf(stderr, "Removed timeout of pool %p\n", pool);
487 #endif
488 }
489 }
490 }
491
492 static int php_http_request_pool_socket_callback(CURL *easy, curl_socket_t sock, int action, void *socket_data, void *assign_data)
493 {
494 php_http_request_pool_t *pool = socket_data;
495
496 if (pool->useevents) {
497 int events = EV_PERSIST;
498 php_http_request_pool_event_t *ev = assign_data;
499 TSRMLS_FETCH_FROM_CTX(pool->ts);
500
501 if (!ev) {
502 ev = ecalloc(1, sizeof(php_http_request_pool_event_t));
503 ev->pool = pool;
504 curl_multi_assign(pool->ch, sock, ev);
505 event_base_set(PHP_HTTP_G->request_pool.event_base, &ev->evnt);
506 } else {
507 event_del(&ev->evnt);
508 }
509
510 #if PHP_HTTP_DEBUG_REQPOOLS
511 {
512 static const char action_strings[][8] = {"NONE", "IN", "OUT", "INOUT", "REMOVE"};
513 php_http_request_t *r;
514 curl_easy_getinfo(easy, CURLINFO_PRIVATE, &r);
515 fprintf(stderr, "Callback on socket %2d (%8s) event %p of pool %p (%d)\n", (int) sock, action_strings[action], ev, pool, pool->unfinished);
516 }
517 #endif
518
519 switch (action) {
520 case CURL_POLL_IN:
521 events |= EV_READ;
522 break;
523 case CURL_POLL_OUT:
524 events |= EV_WRITE;
525 break;
526 case CURL_POLL_INOUT:
527 events |= EV_READ|EV_WRITE;
528 break;
529
530 case CURL_POLL_REMOVE:
531 efree(ev);
532 case CURL_POLL_NONE:
533 return 0;
534
535 default:
536 php_http_error(HE_WARNING, PHP_HTTP_E_SOCKET, "Unknown socket action %d", action);
537 return -1;
538 }
539
540 event_set(&ev->evnt, sock, events, php_http_request_pool_event_callback, ev);
541 event_add(&ev->evnt, NULL);
542 }
543
544 return 0;
545 }
546
547 static void php_http_request_pool_timer_callback(CURLM *multi, long timeout_ms, void *timer_data)
548 {
549 php_http_request_pool_t *pool = timer_data;
550
551 if (pool->useevents) {
552 TSRMLS_FETCH_FROM_CTX(pool->ts);
553 struct timeval timeout;
554
555 if (!event_initialized(pool->timeout)) {
556 event_set(pool->timeout, -1, 0, php_http_request_pool_timeout_callback, pool);
557 event_base_set(PHP_HTTP_G->request_pool.event_base, pool->timeout);
558 } else if (event_pending(pool->timeout, EV_TIMEOUT, NULL)) {
559 event_del(pool->timeout);
560 }
561
562 if (timeout_ms > 0) {
563 timeout.tv_sec = timeout_ms / 1000;
564 timeout.tv_usec = (timeout_ms % 1000) * 1000;
565 } else {
566 php_http_request_pool_timeout(pool, &timeout);
567 }
568
569 event_add(pool->timeout, &timeout);
570
571 #if PHP_HTTP_DEBUG_REQPOOLS
572 fprintf(stderr, "Updating timeout %lu (%lu, %lu) of pool %p\n", (ulong) timeout_ms, (ulong) timeout.tv_sec, (ulong) timeout.tv_usec, pool);
573 #endif
574 }
575 }
576 #endif /* HAVE_EVENT */
577
578 #define PHP_HTTP_BEGIN_ARGS(method, req_args) PHP_HTTP_BEGIN_ARGS_EX(HttpRequestPool, method, 0, req_args)
579 #define PHP_HTTP_EMPTY_ARGS(method) PHP_HTTP_EMPTY_ARGS_EX(HttpRequestPool, method, 0)
580 #define PHP_HTTP_REQPOOL_ME(method, visibility) PHP_ME(HttpRequestPool, method, PHP_HTTP_ARGS(HttpRequestPool, method), visibility)
581
582 PHP_HTTP_EMPTY_ARGS(__construct);
583
584 PHP_HTTP_EMPTY_ARGS(__destruct);
585 PHP_HTTP_EMPTY_ARGS(reset);
586
587 PHP_HTTP_BEGIN_ARGS(attach, 1)
588 PHP_HTTP_ARG_OBJ(http\\Request, request, 0)
589 PHP_HTTP_END_ARGS;
590
591 PHP_HTTP_BEGIN_ARGS(detach, 1)
592 PHP_HTTP_ARG_OBJ(http\\Request, request, 0)
593 PHP_HTTP_END_ARGS;
594
595 PHP_HTTP_EMPTY_ARGS(send);
596 PHP_HTTP_EMPTY_ARGS(socketPerform);
597 PHP_HTTP_BEGIN_ARGS(socketSelect, 0)
598 PHP_HTTP_ARG_VAL(timeout, 0)
599 PHP_HTTP_END_ARGS;
600
601 PHP_HTTP_EMPTY_ARGS(valid);
602 PHP_HTTP_EMPTY_ARGS(current);
603 PHP_HTTP_EMPTY_ARGS(key);
604 PHP_HTTP_EMPTY_ARGS(next);
605 PHP_HTTP_EMPTY_ARGS(rewind);
606
607 PHP_HTTP_EMPTY_ARGS(count);
608
609 PHP_HTTP_EMPTY_ARGS(getAttachedRequests);
610 PHP_HTTP_EMPTY_ARGS(getFinishedRequests);
611
612 PHP_HTTP_BEGIN_ARGS(enablePipelining, 0)
613 PHP_HTTP_ARG_VAL(enable, 0)
614 PHP_HTTP_END_ARGS;
615
616 PHP_HTTP_BEGIN_ARGS(enableEvents, 0)
617 PHP_HTTP_ARG_VAL(enable, 0)
618 PHP_HTTP_END_ARGS;
619
620 zend_class_entry *php_http_request_pool_class_entry;
621 zend_function_entry php_http_request_pool_method_entry[] = {
622 PHP_HTTP_REQPOOL_ME(__construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
623 PHP_HTTP_REQPOOL_ME(__destruct, ZEND_ACC_PUBLIC|ZEND_ACC_DTOR)
624 PHP_HTTP_REQPOOL_ME(attach, ZEND_ACC_PUBLIC)
625 PHP_HTTP_REQPOOL_ME(detach, ZEND_ACC_PUBLIC)
626 PHP_HTTP_REQPOOL_ME(send, ZEND_ACC_PUBLIC)
627 PHP_HTTP_REQPOOL_ME(reset, ZEND_ACC_PUBLIC)
628
629 PHP_HTTP_REQPOOL_ME(socketPerform, ZEND_ACC_PROTECTED)
630 PHP_HTTP_REQPOOL_ME(socketSelect, ZEND_ACC_PROTECTED)
631
632 /* implements Iterator */
633 PHP_HTTP_REQPOOL_ME(valid, ZEND_ACC_PUBLIC)
634 PHP_HTTP_REQPOOL_ME(current, ZEND_ACC_PUBLIC)
635 PHP_HTTP_REQPOOL_ME(key, ZEND_ACC_PUBLIC)
636 PHP_HTTP_REQPOOL_ME(next, ZEND_ACC_PUBLIC)
637 PHP_HTTP_REQPOOL_ME(rewind, ZEND_ACC_PUBLIC)
638
639 /* implmenents Countable */
640 PHP_HTTP_REQPOOL_ME(count, ZEND_ACC_PUBLIC)
641
642 PHP_HTTP_REQPOOL_ME(getAttachedRequests, ZEND_ACC_PUBLIC)
643 PHP_HTTP_REQPOOL_ME(getFinishedRequests, ZEND_ACC_PUBLIC)
644
645 PHP_HTTP_REQPOOL_ME(enablePipelining, ZEND_ACC_PUBLIC)
646 PHP_HTTP_REQPOOL_ME(enableEvents, ZEND_ACC_PUBLIC)
647
648 EMPTY_FUNCTION_ENTRY
649 };
650 static zend_object_handlers php_http_request_pool_object_handlers;
651
652 zend_object_value php_http_request_pool_object_new(zend_class_entry *ce TSRMLS_DC)
653 {
654 zend_object_value ov;
655 php_http_request_pool_object_t *o;
656
657 o = ecalloc(1, sizeof(php_http_request_pool_object_t));
658 zend_object_std_init((zend_object *) o, ce TSRMLS_CC);
659 object_properties_init((zend_object *) o, ce);
660
661 php_http_request_pool_init(&o->pool TSRMLS_CC);
662
663 ov.handle = zend_objects_store_put(o, NULL, php_http_request_pool_object_free, NULL TSRMLS_CC);
664 ov.handlers = &php_http_request_pool_object_handlers;
665
666 return ov;
667 }
668
669 void php_http_request_pool_object_free(void *object TSRMLS_DC)
670 {
671 php_http_request_pool_object_t *o = (php_http_request_pool_object_t *) object;
672
673 php_http_request_pool_dtor(&o->pool);
674 zend_object_std_dtor((zend_object *) o TSRMLS_CC);
675 efree(o);
676 }
677
678 static void php_http_request_pool_object_llist2array(zval **req, zval *array TSRMLS_DC)
679 {
680 Z_ADDREF_P(*req);
681 add_next_index_zval(array, *req);
682 }
683
684 /* ### USERLAND ### */
685
686 PHP_METHOD(HttpRequestPool, __construct)
687 {
688 with_error_handling(EH_THROW, PHP_HTTP_EX_CE(runtime)) {
689 int argc;
690 zval ***argv;
691
692 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "*", &argv, &argc)) {
693 with_error_handling(EH_THROW, PHP_HTTP_EX_CE(request_pool)) {
694 int i;
695 php_http_request_pool_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
696
697 for (i = 0; i < argc; ++i) {
698 if (Z_TYPE_PP(argv[i]) == IS_OBJECT && instanceof_function(Z_OBJCE_PP(argv[i]), php_http_request_class_entry TSRMLS_CC)) {
699 php_http_request_pool_attach(&obj->pool, *(argv[i]));
700 }
701 }
702 } end_error_handling();
703 }
704 } end_error_handling();
705 }
706
707 PHP_METHOD(HttpRequestPool, __destruct)
708 {
709 php_http_request_pool_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
710
711 if (SUCCESS != zend_parse_parameters_none()) {
712 ; /* we always want to clean up */
713 }
714
715 php_http_request_pool_detach_all(&obj->pool);
716 }
717
718 PHP_METHOD(HttpRequestPool, reset)
719 {
720 if (SUCCESS == zend_parse_parameters_none()) {
721 php_http_request_pool_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
722
723 obj->iterator.pos = 0;
724 php_http_request_pool_detach_all(&obj->pool);
725 RETURN_TRUE;
726 }
727 RETURN_FALSE;
728 }
729
730 PHP_METHOD(HttpRequestPool, attach)
731 {
732 RETVAL_FALSE;
733
734 with_error_handling(EH_THROW, PHP_HTTP_EX_CE(runtime)) {
735 zval *request;
736
737 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &request, php_http_request_class_entry)) {
738 with_error_handling(EH_THROW, PHP_HTTP_EX_CE(runtime)) {
739 php_http_request_pool_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
740
741 if (obj->iterator.pos > 0 && obj->iterator.pos < zend_llist_count(&obj->pool.handles)) {
742 php_http_error(HE_THROW, PHP_HTTP_E_REQUEST_POOL, "Cannot attach to the HttpRequestPool while the iterator is active");
743 } else {
744 RETVAL_SUCCESS(php_http_request_pool_attach(&obj->pool, request));
745 }
746 } end_error_handling();
747 }
748 } end_error_handling();
749 }
750
751 PHP_METHOD(HttpRequestPool, detach)
752 {
753 RETVAL_FALSE;
754
755 with_error_handling(EH_THROW, PHP_HTTP_EX_CE(runtime)) {
756 zval *request;
757
758 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &request, php_http_request_class_entry)) {
759 with_error_handling(EH_THROW, PHP_HTTP_EX_CE(request_pool)) {
760 php_http_request_pool_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
761
762 obj->iterator.pos = -1;
763 RETVAL_SUCCESS(php_http_request_pool_detach(&obj->pool, request));
764 } end_error_handling();
765 }
766 } end_error_handling();
767 }
768
769 PHP_METHOD(HttpRequestPool, send)
770 {
771 RETVAL_FALSE;
772
773 with_error_handling(EH_THROW, PHP_HTTP_EX_CE(runtime)) {
774 if (SUCCESS == zend_parse_parameters_none()) {
775 with_error_handling(EH_THROW, PHP_HTTP_EX_CE(request_pool)) {
776 php_http_request_pool_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
777
778 RETVAL_SUCCESS(php_http_request_pool_send(&obj->pool));
779 } end_error_handling();
780 }
781 } end_error_handling();
782 }
783
784 PHP_METHOD(HttpRequestPool, socketPerform)
785 {
786 if (SUCCESS == zend_parse_parameters_none()) {
787 php_http_request_pool_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
788
789 if (0 < php_http_request_pool_perform(&obj->pool)) {
790 RETURN_TRUE;
791 }
792 }
793 RETURN_FALSE;
794 }
795
796 PHP_METHOD(HttpRequestPool, socketSelect)
797 {
798 double timeout = 0;
799
800 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|d", &timeout)) {
801 struct timeval timeout_val;
802 php_http_request_pool_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
803
804 timeout_val.tv_sec = (time_t) timeout;
805 timeout_val.tv_usec = PHP_HTTP_USEC(timeout) % PHP_HTTP_MCROSEC;
806
807 RETURN_SUCCESS(php_http_request_pool_select(&obj->pool, timeout ? &timeout_val : NULL));
808 }
809 RETURN_FALSE;
810 }
811
812 PHP_METHOD(HttpRequestPool, valid)
813 {
814 if (SUCCESS == zend_parse_parameters_none()) {
815 php_http_request_pool_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
816
817 RETURN_BOOL(obj->iterator.pos >= 0 && obj->iterator.pos < zend_llist_count(&obj->pool.handles));
818 }
819 RETURN_FALSE;
820 }
821
822 PHP_METHOD(HttpRequestPool, current)
823 {
824 if (SUCCESS == zend_parse_parameters_none()) {
825 php_http_request_pool_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
826
827 if (obj->iterator.pos < zend_llist_count(&obj->pool.handles)) {
828 long pos = 0;
829 zval **current = NULL;
830 zend_llist_position lpos;
831
832 for ( current = zend_llist_get_first_ex(&obj->pool.handles, &lpos);
833 current && obj->iterator.pos != pos++;
834 current = zend_llist_get_next_ex(&obj->pool.handles, &lpos));
835 if (current) {
836 RETURN_OBJECT(*current, 1);
837 }
838 }
839 }
840 RETURN_FALSE;
841 }
842
843 PHP_METHOD(HttpRequestPool, key)
844 {
845 if (SUCCESS == zend_parse_parameters_none()) {
846 php_http_request_pool_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
847
848 RETURN_LONG(obj->iterator.pos);
849 }
850 RETURN_FALSE;
851 }
852
853 PHP_METHOD(HttpRequestPool, next)
854 {
855 if (SUCCESS == zend_parse_parameters_none()) {
856 php_http_request_pool_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
857
858 ++obj->iterator.pos;
859 }
860 }
861
862 PHP_METHOD(HttpRequestPool, rewind)
863 {
864 if (SUCCESS == zend_parse_parameters_none()) {
865 php_http_request_pool_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
866
867 obj->iterator.pos = 0;
868 }
869 }
870
871 PHP_METHOD(HttpRequestPool, count)
872 {
873 if (SUCCESS == zend_parse_parameters_none()) {
874 php_http_request_pool_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
875
876 RETURN_LONG((long) zend_llist_count(&obj->pool.handles));
877 }
878 }
879
880 PHP_METHOD(HttpRequestPool, getAttachedRequests)
881 {
882 if (SUCCESS == zend_parse_parameters_none()) {
883 php_http_request_pool_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
884
885 array_init(return_value);
886 zend_llist_apply_with_argument(&obj->pool.handles,
887 (llist_apply_with_arg_func_t) php_http_request_pool_object_llist2array,
888 return_value TSRMLS_CC);
889 return;
890 }
891 RETURN_FALSE;
892 }
893
894 PHP_METHOD(HttpRequestPool, getFinishedRequests)
895 {
896 if (SUCCESS == zend_parse_parameters_none()) {
897 php_http_request_pool_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
898
899 array_init(return_value);
900 zend_llist_apply_with_argument(&obj->pool.finished,
901 (llist_apply_with_arg_func_t) php_http_request_pool_object_llist2array,
902 return_value TSRMLS_CC);
903 return;
904 }
905 RETURN_FALSE;
906 }
907
908 PHP_METHOD(HttpRequestPool, enablePipelining)
909 {
910 zend_bool enable = 1;
911
912 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &enable)) {
913 php_http_request_pool_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
914
915 if (CURLM_OK == curl_multi_setopt(obj->pool.ch, CURLMOPT_PIPELINING, (long) enable)) {
916 RETURN_TRUE;
917 }
918 }
919 RETURN_FALSE;
920 }
921
922 PHP_METHOD(HttpRequestPool, enableEvents)
923 {
924 zend_bool enable = 1;
925
926 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &enable)) {
927 #if PHP_HTTP_HAVE_EVENT
928 php_http_request_pool_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
929
930 obj->pool.useevents = enable;
931 RETURN_TRUE;
932 #endif
933 }
934 RETURN_FALSE;
935 }
936
937 PHP_MINIT_FUNCTION(http_request_pool)
938 {
939 if (SUCCESS != php_http_persistent_handle_provide(ZEND_STRL("http_request_pool"), curl_multi_init, (php_http_persistent_handle_dtor_t) curl_multi_cleanup, NULL TSRMLS_CC)) {
940 return FAILURE;
941 }
942
943 PHP_HTTP_REGISTER_CLASS(http\\request, Pool, http_request_pool, php_http_object_class_entry, 0);
944 php_http_request_pool_class_entry->create_object = php_http_request_pool_object_new;
945 memcpy(&php_http_request_pool_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
946 php_http_request_pool_object_handlers.clone_obj = NULL;
947
948 zend_class_implements(php_http_request_pool_class_entry TSRMLS_CC, 2, spl_ce_Countable, zend_ce_iterator);
949
950 return SUCCESS;
951 }
952
953 PHP_RINIT_FUNCTION(http_request_pool)
954 {
955 #ifdef PHP_HTTP_HAVE_EVENT
956 if (!PHP_HTTP_G->request_pool.event_base && !(PHP_HTTP_G->request_pool.event_base = event_init())) {
957 return FAILURE;
958 }
959 #endif
960
961 return SUCCESS;
962 }
963
964