4 #ifdef PHP_HTTP_HAVE_EVENT
8 #include <ext/spl/spl_iterators.h>
9 #include <Zend/zend_interfaces.h>
11 #ifndef PHP_HTTP_DEBUG_REQPOOLS
12 # define PHP_HTTP_DEBUG_REQPOOLS 0
15 #ifdef PHP_HTTP_HAVE_EVENT
16 typedef struct php_http_request_pool_event
{
18 php_http_request_pool_t
*pool
;
19 } php_http_request_pool_event_t
;
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
);
27 static int php_http_request_pool_compare_handles(void *h1
, void *h2
);
29 PHP_HTTP_API php_http_request_pool_t
*php_http_request_pool_init(php_http_request_pool_t
*pool TSRMLS_DC
)
33 #if PHP_HTTP_DEBUG_REQPOOLS
34 fprintf(stderr
, "Initializing request pool %p\n", pool
);
37 if ((free_pool
= (!pool
))) {
38 pool
= emalloc(sizeof(php_http_request_pool_t
));
42 if (SUCCESS
!= php_http_persistent_handle_acquire(ZEND_STRL("http_request_pool"), &pool
->ch TSRMLS_CC
)) {
49 TSRMLS_SET_CTX(pool
->ts
);
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
);
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);
63 #if PHP_HTTP_DEBUG_REQPOOLS
64 fprintf(stderr
, "Initialized request pool %p\n", pool
);
70 PHP_HTTP_API STATUS
php_http_request_pool_attach(php_http_request_pool_t
*pool
, zval
*request
)
73 TSRMLS_FETCH_FROM_CTX(pool
->ts
);
75 php_http_request_object_t
*req
= zend_object_store_get_object(request TSRMLS_CC
);
77 #if PHP_HTTP_DEBUG_REQPOOLS
78 fprintf(stderr
, "Attaching HttpRequest(#%d) %p to pool %p\n", Z_OBJ_HANDLE_P(request
), 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
));
86 CURLMcode code
= curl_multi_add_handle(pool
->ch
, req
->request
->ch
);
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
));
94 zend_llist_add_element(&pool
->handles
, &request
);
97 #if PHP_HTTP_DEBUG_REQPOOLS
98 fprintf(stderr
, "> %d HttpRequests attached to pool %p\n", zend_llist_count(&pool
->handles
), pool
);
106 PHP_HTTP_API STATUS
php_http_request_pool_detach(php_http_request_pool_t
*pool
, zval
*request
)
110 TSRMLS_FETCH_FROM_CTX(pool
->ts
);
112 php_http_request_object_t
*req
= zend_object_store_get_object(request TSRMLS_CC
);
114 #if PHP_HTTP_DEBUG_REQPOOLS
115 fprintf(stderr
, "Detaching HttpRequest(#%d) %p from pool %p\n", Z_OBJ_HANDLE_P(request
), 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
);
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
));
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
);
134 #if PHP_HTTP_DEBUG_REQPOOLS
135 fprintf(stderr
, "> %d HttpRequests remaining in pool %p\n", zend_llist_count(&pool
->handles
), pool
);
143 PHP_HTTP_API
void php_http_request_pool_apply(php_http_request_pool_t
*pool
, php_http_request_pool_apply_func_t cb
)
145 int count
= zend_llist_count(&pool
->handles
);
149 zend_llist_position pos
;
150 zval
**handle
, **handles
= emalloc(count
* sizeof(zval
*));
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
;
156 /* should never happen */
158 zend_error(E_ERROR
, "number of fetched request handles do not match overall count");
162 for (i
= 0; i
< count
; ++i
) {
163 if (cb(pool
, handles
[i
])) {
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
)
173 int count
= zend_llist_count(&pool
->handles
);
177 zend_llist_position pos
;
178 zval
**handle
, **handles
= emalloc(count
* sizeof(zval
*));
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
;
184 /* should never happen */
186 zend_error(E_ERROR
, "number of fetched request handles do not match overall count");
190 for (i
= 0; i
< count
; ++i
) {
191 if (cb(pool
, handles
[i
], arg
)) {
199 PHP_HTTP_API
void php_http_request_pool_detach_all(php_http_request_pool_t
*pool
)
201 #if PHP_HTTP_DEBUG_REQPOOLS
202 fprintf(stderr
, "Detaching %d requests from pool %p\n", zend_llist_count(&pool
->handles
), pool
);
204 php_http_request_pool_apply(pool
, php_http_request_pool_detach
);
207 PHP_HTTP_API STATUS
php_http_request_pool_send(php_http_request_pool_t
*pool
)
209 TSRMLS_FETCH_FROM_CTX(pool
->ts
);
211 #if PHP_HTTP_DEBUG_REQPOOLS
212 fprintf(stderr
, "Attempt to send %d requests of pool %p\n", zend_llist_count(&pool
->handles
), pool
);
215 #ifdef PHP_HTTP_HAVE_EVENT
216 if (pool
->useevents
) {
218 #if PHP_HTTP_DEBUG_REQPOOLS
219 fprintf(stderr
, "& Starting event dispatcher of pool %p\n", pool
);
221 event_base_dispatch(PHP_HTTP_G
->request_pool
.event_base
);
222 } while (pool
->unfinished
);
226 while (php_http_request_pool_perform(pool
)) {
227 if (SUCCESS
!= php_http_request_pool_select(pool
, NULL
)) {
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());
232 php_http_error(HE_WARNING
, PHP_HTTP_E_SOCKET
, strerror(errno
));
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
);
246 PHP_HTTP_API
void php_http_request_pool_dtor(php_http_request_pool_t
*pool
)
248 TSRMLS_FETCH_FROM_CTX(pool
->ts
);
250 #if PHP_HTTP_DEBUG_REQPOOLS
251 fprintf(stderr
, "Destructing request pool %p\n", pool
);
254 #ifdef PHP_HTTP_HAVE_EVENT
255 efree(pool
->timeout
);
258 php_http_request_pool_detach_all(pool
);
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
);
266 PHP_HTTP_API
void php_http_request_pool_free(php_http_request_pool_t
**pool
) {
268 php_http_request_pool_dtor(*pool
);
275 # define SELECT_ERROR SOCKET_ERROR
277 # define SELECT_ERROR -1
280 PHP_HTTP_API STATUS
php_http_request_pool_select(php_http_request_pool_t
*pool
, struct timeval
*custom_timeout
)
284 struct timeval timeout
;
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");
294 if (custom_timeout
&& timerisset(custom_timeout
)) {
295 timeout
= *custom_timeout
;
297 php_http_request_pool_timeout(pool
, &timeout
);
304 if (CURLM_OK
== curl_multi_fdset(pool
->ch
, &R
, &W
, &E
, &MAX
)) {
306 php_http_sleep((double) timeout
.tv_sec
+ (double) (timeout
.tv_usec
/ PHP_HTTP_MCROSEC
));
308 } else if (SELECT_ERROR
!= select(MAX
+ 1, &R
, &W
, &E
, &timeout
)) {
315 PHP_HTTP_API
int php_http_request_pool_perform(php_http_request_pool_t
*pool
)
317 TSRMLS_FETCH_FROM_CTX(pool
->ts
);
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");
326 while (CURLM_CALL_MULTI_PERFORM
== curl_multi_perform(pool
->ch
, &pool
->unfinished
));
328 #if PHP_HTTP_DEBUG_REQPOOLS
329 fprintf(stderr
, "%u unfinished requests of pool %p remaining\n", pool
->unfinished
, pool
);
332 php_http_request_pool_responsehandler(pool
);
334 return pool
->unfinished
;
337 void php_http_request_pool_responsehandler(php_http_request_pool_t
*pool
)
341 TSRMLS_FETCH_FROM_CTX(pool
->ts
);
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
));
350 php_http_request_pool_apply_with_arg(pool
, php_http_request_pool_apply_responsehandler
, msg
->easy_handle
);
355 int php_http_request_pool_apply_responsehandler(php_http_request_pool_t
*pool
, zval
*req
, void *ch
)
358 TSRMLS_FETCH_FROM_CTX(pool
->ts
);
360 php_http_request_object_t
*obj
= zend_object_store_get_object(req TSRMLS_CC
);
362 if ((!ch
) || obj
->request
->ch
== (CURL
*) ch
) {
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
);
369 zend_llist_add_element(&obj
->pool
->finished
, &req
);
370 php_http_request_object_responsehandler(obj
, req
);
376 struct timeval
*php_http_request_pool_timeout(php_http_request_pool_t
*pool
, struct timeval
*timeout
)
378 #ifdef HAVE_CURL_MULTI_TIMEOUT
379 long max_tout
= 1000;
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;
387 timeout
->tv_usec
= 1000;
388 #ifdef HAVE_CURL_MULTI_TIMEOUT
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
);
401 static int php_http_request_pool_compare_handles(void *h1
, void *h2
)
403 return (Z_OBJ_HANDLE_PP((zval
**) h1
) == Z_OBJ_HANDLE_P((zval
*) h2
));
406 #ifdef PHP_HTTP_HAVE_EVENT
408 static void php_http_request_pool_timeout_callback(int socket
, short action
, void *event_data
)
410 php_http_request_pool_t
*pool
= event_data
;
412 if (pool
->useevents
) {
414 TSRMLS_FETCH_FROM_CTX(pool
->ts
);
416 #if PHP_HTTP_DEBUG_REQPOOLS
417 fprintf(stderr
, "Timeout occurred of pool %p\n", pool
);
420 while (CURLM_CALL_MULTI_PERFORM
== (rc
= curl_multi_socket(pool
->ch
, CURL_SOCKET_TIMEOUT
, &pool
->unfinished
)));
422 if (CURLM_OK
!= rc
) {
423 php_http_error(HE_WARNING
, PHP_HTTP_E_SOCKET
, curl_multi_strerror(rc
));
426 php_http_request_pool_responsehandler(pool
);
430 static void php_http_request_pool_event_callback(int socket
, short action
, void *event_data
)
432 php_http_request_pool_event_t
*ev
= event_data
;
433 php_http_request_pool_t
*pool
= ev
->pool
;
435 if (pool
->useevents
) {
436 CURLMcode rc
= CURLE_OK
;
437 TSRMLS_FETCH_FROM_CTX(ev
->pool
->ts
);
439 #if PHP_HTTP_DEBUG_REQPOOLS
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
);
446 /* don't use 'ev' below this loop as it might 've been freed in the socket callback */
448 #ifdef HAVE_CURL_MULTI_SOCKET_ACTION
449 switch (action
& (EV_READ
|EV_WRITE
)) {
451 rc
= curl_multi_socket_action(pool
->ch
, socket
, CURL_CSELECT_IN
, &pool
->unfinished
);
454 rc
= curl_multi_socket_action(pool
->ch
, socket
, CURL_CSELECT_OUT
, &pool
->unfinished
);
456 case EV_READ
|EV_WRITE
:
457 rc
= curl_multi_socket_action(pool
->ch
, socket
, CURL_CSELECT_IN
|CURL_CSELECT_OUT
, &pool
->unfinished
);
460 php_http_error(HE_WARNING
, PHP_HTTP_E_SOCKET
, "Unknown event %d", (int) action
);
464 rc
= curl_multi_socket(pool
->ch
, socket
, &pool
->unfinished
);
466 } while (CURLM_CALL_MULTI_PERFORM
== rc
);
469 case CURLM_BAD_SOCKET
:
471 fprintf(stderr
, "!!! Bad socket: %d (%d)\n", socket
, (int) action
);
476 php_http_error(HE_WARNING
, PHP_HTTP_E_SOCKET
, curl_multi_strerror(rc
));
480 php_http_request_pool_responsehandler(pool
);
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
);
492 static int php_http_request_pool_socket_callback(CURL
*easy
, curl_socket_t sock
, int action
, void *socket_data
, void *assign_data
)
494 php_http_request_pool_t
*pool
= socket_data
;
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
);
502 ev
= ecalloc(1, sizeof(php_http_request_pool_event_t
));
504 curl_multi_assign(pool
->ch
, sock
, ev
);
505 event_base_set(PHP_HTTP_G
->request_pool
.event_base
, &ev
->evnt
);
507 event_del(&ev
->evnt
);
510 #if PHP_HTTP_DEBUG_REQPOOLS
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
);
526 case CURL_POLL_INOUT
:
527 events
|= EV_READ
|EV_WRITE
;
530 case CURL_POLL_REMOVE
:
536 php_http_error(HE_WARNING
, PHP_HTTP_E_SOCKET
, "Unknown socket action %d", action
);
540 event_set(&ev
->evnt
, sock
, events
, php_http_request_pool_event_callback
, ev
);
541 event_add(&ev
->evnt
, NULL
);
547 static void php_http_request_pool_timer_callback(CURLM
*multi
, long timeout_ms
, void *timer_data
)
549 php_http_request_pool_t
*pool
= timer_data
;
551 if (pool
->useevents
) {
552 TSRMLS_FETCH_FROM_CTX(pool
->ts
);
553 struct timeval timeout
;
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
);
562 if (timeout_ms
> 0) {
563 timeout
.tv_sec
= timeout_ms
/ 1000;
564 timeout
.tv_usec
= (timeout_ms
% 1000) * 1000;
566 php_http_request_pool_timeout(pool
, &timeout
);
569 event_add(pool
->timeout
, &timeout
);
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
);
576 #endif /* HAVE_EVENT */
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)
582 PHP_HTTP_EMPTY_ARGS(__construct
);
584 PHP_HTTP_EMPTY_ARGS(__destruct
);
585 PHP_HTTP_EMPTY_ARGS(reset
);
587 PHP_HTTP_BEGIN_ARGS(attach
, 1)
588 PHP_HTTP_ARG_OBJ(http
\\Request
, request
, 0)
591 PHP_HTTP_BEGIN_ARGS(detach
, 1)
592 PHP_HTTP_ARG_OBJ(http
\\Request
, request
, 0)
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)
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
);
607 PHP_HTTP_EMPTY_ARGS(count
);
609 PHP_HTTP_EMPTY_ARGS(getAttachedRequests
);
610 PHP_HTTP_EMPTY_ARGS(getFinishedRequests
);
612 PHP_HTTP_BEGIN_ARGS(enablePipelining
, 0)
613 PHP_HTTP_ARG_VAL(enable
, 0)
616 PHP_HTTP_BEGIN_ARGS(enableEvents
, 0)
617 PHP_HTTP_ARG_VAL(enable
, 0)
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
)
629 PHP_HTTP_REQPOOL_ME(socketPerform
, ZEND_ACC_PROTECTED
)
630 PHP_HTTP_REQPOOL_ME(socketSelect
, ZEND_ACC_PROTECTED
)
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
)
639 /* implmenents Countable */
640 PHP_HTTP_REQPOOL_ME(count
, ZEND_ACC_PUBLIC
)
642 PHP_HTTP_REQPOOL_ME(getAttachedRequests
, ZEND_ACC_PUBLIC
)
643 PHP_HTTP_REQPOOL_ME(getFinishedRequests
, ZEND_ACC_PUBLIC
)
645 PHP_HTTP_REQPOOL_ME(enablePipelining
, ZEND_ACC_PUBLIC
)
646 PHP_HTTP_REQPOOL_ME(enableEvents
, ZEND_ACC_PUBLIC
)
650 static zend_object_handlers php_http_request_pool_object_handlers
;
652 zend_object_value
php_http_request_pool_object_new(zend_class_entry
*ce TSRMLS_DC
)
654 zend_object_value ov
;
655 php_http_request_pool_object_t
*o
;
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
);
661 php_http_request_pool_init(&o
->pool TSRMLS_CC
);
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
;
669 void php_http_request_pool_object_free(void *object TSRMLS_DC
)
671 php_http_request_pool_object_t
*o
= (php_http_request_pool_object_t
*) object
;
673 php_http_request_pool_dtor(&o
->pool
);
674 zend_object_std_dtor((zend_object
*) o TSRMLS_CC
);
678 static void php_http_request_pool_object_llist2array(zval
**req
, zval
*array TSRMLS_DC
)
681 add_next_index_zval(array
, *req
);
684 /* ### USERLAND ### */
686 PHP_METHOD(HttpRequestPool
, __construct
)
688 with_error_handling(EH_THROW
, PHP_HTTP_EX_CE(runtime
)) {
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
)) {
695 php_http_request_pool_object_t
*obj
= zend_object_store_get_object(getThis() TSRMLS_CC
);
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
]));
702 } end_error_handling();
704 } end_error_handling();
707 PHP_METHOD(HttpRequestPool
, __destruct
)
709 php_http_request_pool_object_t
*obj
= zend_object_store_get_object(getThis() TSRMLS_CC
);
711 if (SUCCESS
!= zend_parse_parameters_none()) {
712 ; /* we always want to clean up */
715 php_http_request_pool_detach_all(&obj
->pool
);
718 PHP_METHOD(HttpRequestPool
, reset
)
720 if (SUCCESS
== zend_parse_parameters_none()) {
721 php_http_request_pool_object_t
*obj
= zend_object_store_get_object(getThis() TSRMLS_CC
);
723 obj
->iterator
.pos
= 0;
724 php_http_request_pool_detach_all(&obj
->pool
);
730 PHP_METHOD(HttpRequestPool
, attach
)
734 with_error_handling(EH_THROW
, PHP_HTTP_EX_CE(runtime
)) {
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
);
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");
744 RETVAL_SUCCESS(php_http_request_pool_attach(&obj
->pool
, request
));
746 } end_error_handling();
748 } end_error_handling();
751 PHP_METHOD(HttpRequestPool
, detach
)
755 with_error_handling(EH_THROW
, PHP_HTTP_EX_CE(runtime
)) {
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
);
762 obj
->iterator
.pos
= -1;
763 RETVAL_SUCCESS(php_http_request_pool_detach(&obj
->pool
, request
));
764 } end_error_handling();
766 } end_error_handling();
769 PHP_METHOD(HttpRequestPool
, send
)
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
);
778 RETVAL_SUCCESS(php_http_request_pool_send(&obj
->pool
));
779 } end_error_handling();
781 } end_error_handling();
784 PHP_METHOD(HttpRequestPool
, socketPerform
)
786 if (SUCCESS
== zend_parse_parameters_none()) {
787 php_http_request_pool_object_t
*obj
= zend_object_store_get_object(getThis() TSRMLS_CC
);
789 if (0 < php_http_request_pool_perform(&obj
->pool
)) {
796 PHP_METHOD(HttpRequestPool
, socketSelect
)
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
);
804 timeout_val
.tv_sec
= (time_t) timeout
;
805 timeout_val
.tv_usec
= PHP_HTTP_USEC(timeout
) % PHP_HTTP_MCROSEC
;
807 RETURN_SUCCESS(php_http_request_pool_select(&obj
->pool
, timeout
? &timeout_val
: NULL
));
812 PHP_METHOD(HttpRequestPool
, valid
)
814 if (SUCCESS
== zend_parse_parameters_none()) {
815 php_http_request_pool_object_t
*obj
= zend_object_store_get_object(getThis() TSRMLS_CC
);
817 RETURN_BOOL(obj
->iterator
.pos
>= 0 && obj
->iterator
.pos
< zend_llist_count(&obj
->pool
.handles
));
822 PHP_METHOD(HttpRequestPool
, current
)
824 if (SUCCESS
== zend_parse_parameters_none()) {
825 php_http_request_pool_object_t
*obj
= zend_object_store_get_object(getThis() TSRMLS_CC
);
827 if (obj
->iterator
.pos
< zend_llist_count(&obj
->pool
.handles
)) {
829 zval
**current
= NULL
;
830 zend_llist_position lpos
;
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
));
836 RETURN_OBJECT(*current
, 1);
843 PHP_METHOD(HttpRequestPool
, key
)
845 if (SUCCESS
== zend_parse_parameters_none()) {
846 php_http_request_pool_object_t
*obj
= zend_object_store_get_object(getThis() TSRMLS_CC
);
848 RETURN_LONG(obj
->iterator
.pos
);
853 PHP_METHOD(HttpRequestPool
, next
)
855 if (SUCCESS
== zend_parse_parameters_none()) {
856 php_http_request_pool_object_t
*obj
= zend_object_store_get_object(getThis() TSRMLS_CC
);
862 PHP_METHOD(HttpRequestPool
, rewind
)
864 if (SUCCESS
== zend_parse_parameters_none()) {
865 php_http_request_pool_object_t
*obj
= zend_object_store_get_object(getThis() TSRMLS_CC
);
867 obj
->iterator
.pos
= 0;
871 PHP_METHOD(HttpRequestPool
, count
)
873 if (SUCCESS
== zend_parse_parameters_none()) {
874 php_http_request_pool_object_t
*obj
= zend_object_store_get_object(getThis() TSRMLS_CC
);
876 RETURN_LONG((long) zend_llist_count(&obj
->pool
.handles
));
880 PHP_METHOD(HttpRequestPool
, getAttachedRequests
)
882 if (SUCCESS
== zend_parse_parameters_none()) {
883 php_http_request_pool_object_t
*obj
= zend_object_store_get_object(getThis() TSRMLS_CC
);
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
);
894 PHP_METHOD(HttpRequestPool
, getFinishedRequests
)
896 if (SUCCESS
== zend_parse_parameters_none()) {
897 php_http_request_pool_object_t
*obj
= zend_object_store_get_object(getThis() TSRMLS_CC
);
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
);
908 PHP_METHOD(HttpRequestPool
, enablePipelining
)
910 zend_bool enable
= 1;
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
);
915 if (CURLM_OK
== curl_multi_setopt(obj
->pool
.ch
, CURLMOPT_PIPELINING
, (long) enable
)) {
922 PHP_METHOD(HttpRequestPool
, enableEvents
)
924 zend_bool enable
= 1;
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
);
930 obj
->pool
.useevents
= enable
;
937 PHP_MINIT_FUNCTION(http_request_pool
)
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
)) {
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
;
948 zend_class_implements(php_http_request_pool_class_entry TSRMLS_CC
, 2, spl_ce_Countable
, zend_ce_iterator
);
953 PHP_RINIT_FUNCTION(http_request_pool
)
955 #ifdef PHP_HTTP_HAVE_EVENT
956 if (!PHP_HTTP_G
->request_pool
.event_base
&& !(PHP_HTTP_G
->request_pool
.event_base
= event_init())) {