2 +--------------------------------------------------------------------+
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-2011, Michael Wallner <mike@php.net> |
10 +--------------------------------------------------------------------+
13 #include "php_http_api.h"
15 #if PHP_HTTP_HAVE_CURL
17 #if PHP_HTTP_HAVE_EVENT
19 # if !PHP_HTTP_HAVE_EVENT2 && /* just be really sure */ !(LIBEVENT_VERSION_NUMBER >= 0x02000000)
20 # define event_base_new event_init
21 # define event_assign(e, b, s, a, cb, d) do {\
22 event_set(e, s, a, cb, d); \
23 event_base_set(b, e); \
28 typedef struct php_http_curl_client_pool
{
31 int unfinished
; /* int because of curl_multi_perform() */
33 #if PHP_HTTP_HAVE_EVENT
34 struct event
*timeout
;
38 } php_http_curl_client_pool_t
;
40 static void *php_http_curlm_ctor(void *opaque TSRMLS_DC
)
42 return curl_multi_init();
45 static void php_http_curlm_dtor(void *opaque
, void *handle TSRMLS_DC
)
47 curl_multi_cleanup(handle
);
51 static void php_http_curl_client_pool_responsehandler(php_http_client_pool_t
*pool
)
55 php_http_curl_client_pool_t
*curl
= pool
->ctx
;
56 TSRMLS_FETCH_FROM_CTX(pool
->ts
);
59 CURLMsg
*msg
= curl_multi_info_read(curl
->handle
, &remaining
);
61 if (msg
&& CURLMSG_DONE
== msg
->msg
) {
64 if (CURLE_OK
!= msg
->data
.result
) {
65 php_http_curl_client_storage_t
*st
= php_http_curl_client_get_storage(msg
->easy_handle
);
66 php_http_error(HE_WARNING
, PHP_HTTP_E_CLIENT
, "%s; %s (%s)", curl_easy_strerror(msg
->data
.result
), STR_PTR(st
->errorbuffer
), STR_PTR(st
->url
));
69 php_http_client_pool_requests(pool
, &requests
, NULL
);
70 for (request
= requests
; *request
; ++request
) {
71 php_http_client_object_t
*obj
= zend_object_store_get_object(*request TSRMLS_CC
);
73 if (msg
->easy_handle
== ((php_http_curl_client_t
*) (obj
->client
->ctx
))->handle
) {
75 zend_llist_add_element(&pool
->clients
.finished
, request
);
76 php_http_client_object_handle_response(*request TSRMLS_CC
);
79 zval_ptr_dtor(request
);
87 #if PHP_HTTP_HAVE_EVENT
89 typedef struct php_http_client_pool_event
{
91 php_http_client_pool_t
*pool
;
92 } php_http_client_pool_event_t
;
94 static inline int etoca(short action
) {
95 switch (action
& (EV_READ
|EV_WRITE
)) {
97 return CURL_CSELECT_IN
;
100 return CURL_CSELECT_OUT
;
102 case EV_READ
|EV_WRITE
:
103 return CURL_CSELECT_IN
|CURL_CSELECT_OUT
;
110 static void php_http_curl_client_pool_timeout_callback(int socket
, short action
, void *event_data
)
112 php_http_client_pool_t
*pool
= event_data
;
113 php_http_curl_client_pool_t
*curl
= pool
->ctx
;
116 fprintf(stderr
, "T");
118 if (curl
->useevents
) {
120 TSRMLS_FETCH_FROM_CTX(pool
->ts
);
122 while (CURLM_CALL_MULTI_PERFORM
== (rc
= curl_multi_socket_action(curl
->handle
, socket
, etoca(action
), &curl
->unfinished
)));
124 if (CURLM_OK
!= rc
) {
125 php_http_error(HE_WARNING
, PHP_HTTP_E_SOCKET
, "%s", curl_multi_strerror(rc
));
128 php_http_curl_client_pool_responsehandler(pool
);
132 static void php_http_curl_client_pool_event_callback(int socket
, short action
, void *event_data
)
134 php_http_client_pool_t
*pool
= event_data
;
135 php_http_curl_client_pool_t
*curl
= pool
->ctx
;
138 fprintf(stderr
, "E");
140 if (curl
->useevents
) {
141 CURLMcode rc
= CURLE_OK
;
142 TSRMLS_FETCH_FROM_CTX(pool
->ts
);
144 while (CURLM_CALL_MULTI_PERFORM
== (rc
= curl_multi_socket_action(curl
->handle
, socket
, etoca(action
), &curl
->unfinished
)));
146 if (CURLM_OK
!= rc
) {
147 php_http_error(HE_WARNING
, PHP_HTTP_E_SOCKET
, "%s", curl_multi_strerror(rc
));
150 php_http_curl_client_pool_responsehandler(pool
);
152 /* remove timeout if there are no transfers left */
153 if (!curl
->unfinished
&& event_initialized(curl
->timeout
) && event_pending(curl
->timeout
, EV_TIMEOUT
, NULL
)) {
154 event_del(curl
->timeout
);
159 static int php_http_curl_client_pool_socket_callback(CURL
*easy
, curl_socket_t sock
, int action
, void *socket_data
, void *assign_data
)
161 php_http_client_pool_t
*pool
= socket_data
;
162 php_http_curl_client_pool_t
*curl
= pool
->ctx
;
165 fprintf(stderr
, "S");
167 if (curl
->useevents
) {
168 int events
= EV_PERSIST
;
169 php_http_client_pool_event_t
*ev
= assign_data
;
170 TSRMLS_FETCH_FROM_CTX(pool
->ts
);
173 ev
= ecalloc(1, sizeof(php_http_client_pool_event_t
));
175 curl_multi_assign(curl
->handle
, sock
, ev
);
177 event_del(&ev
->evnt
);
187 case CURL_POLL_INOUT
:
188 events
|= EV_READ
|EV_WRITE
;
191 case CURL_POLL_REMOVE
:
198 php_http_error(HE_WARNING
, PHP_HTTP_E_SOCKET
, "Unknown socket action %d", action
);
202 event_assign(&ev
->evnt
, PHP_HTTP_G
->curl
.event_base
, sock
, events
, php_http_curl_client_pool_event_callback
, pool
);
203 event_add(&ev
->evnt
, NULL
);
209 static void php_http_curl_client_pool_timer_callback(CURLM
*multi
, long timeout_ms
, void *timer_data
)
211 php_http_client_pool_t
*pool
= timer_data
;
212 php_http_curl_client_pool_t
*curl
= pool
->ctx
;
215 fprintf(stderr
, "\ntimer <- timeout_ms: %ld\n", timeout_ms
);
217 if (curl
->useevents
) {
219 if (timeout_ms
< 0) {
220 php_http_curl_client_pool_timeout_callback(CURL_SOCKET_TIMEOUT
, EV_READ
|EV_WRITE
, pool
);
221 } else if (timeout_ms
> 0 || !event_initialized(curl
->timeout
) || !event_pending(curl
->timeout
, EV_TIMEOUT
, NULL
)) {
222 struct timeval timeout
;
223 TSRMLS_FETCH_FROM_CTX(pool
->ts
);
225 if (!event_initialized(curl
->timeout
)) {
226 event_assign(curl
->timeout
, PHP_HTTP_G
->curl
.event_base
, CURL_SOCKET_TIMEOUT
, 0, php_http_curl_client_pool_timeout_callback
, pool
);
227 } else if (event_pending(curl
->timeout
, EV_TIMEOUT
, NULL
)) {
228 event_del(curl
->timeout
);
231 timeout
.tv_sec
= timeout_ms
/ 1000;
232 timeout
.tv_usec
= (timeout_ms
% 1000) * 1000;
234 event_add(curl
->timeout
, &timeout
);
239 #endif /* HAVE_EVENT */
242 /* pool handler ops */
244 static php_http_client_pool_t
*php_http_curl_client_pool_init(php_http_client_pool_t
*h
, void *handle
)
246 php_http_curl_client_pool_t
*curl
;
247 TSRMLS_FETCH_FROM_CTX(h
->ts
);
249 if (!handle
&& !(handle
= php_http_resource_factory_handle_ctor(h
->rf TSRMLS_CC
))) {
250 php_http_error(HE_WARNING
, PHP_HTTP_E_CLIENT_POOL
, "could not initialize curl pool handle");
254 curl
= ecalloc(1, sizeof(*curl
));
255 curl
->handle
= handle
;
256 curl
->unfinished
= 0;
262 static void php_http_curl_client_pool_dtor(php_http_client_pool_t
*h
)
264 php_http_curl_client_pool_t
*curl
= h
->ctx
;
265 TSRMLS_FETCH_FROM_CTX(h
->ts
);
267 #if PHP_HTTP_HAVE_EVENT
269 efree(curl
->timeout
);
270 curl
->timeout
= NULL
;
273 curl
->unfinished
= 0;
274 php_http_client_pool_reset(h
);
276 php_http_resource_factory_handle_dtor(h
->rf
, curl
->handle TSRMLS_CC
);
282 static STATUS
php_http_curl_client_pool_attach(php_http_client_pool_t
*h
, php_http_client_t
*r
, php_http_message_t
*m
)
284 php_http_curl_client_pool_t
*curl
= h
->ctx
;
285 php_http_curl_client_t
*recurl
= r
->ctx
;
287 TSRMLS_FETCH_FROM_CTX(h
->ts
);
289 if (r
->ops
!= php_http_curl_client_get_ops()) {
290 php_http_error(HE_WARNING
, PHP_HTTP_E_CLIENT_POOL
, "Cannot attach a non-curl client to this pool");
294 if (SUCCESS
!= php_http_curl_client_prepare(r
, m
)) {
298 if (CURLM_OK
== (rs
= curl_multi_add_handle(curl
->handle
, recurl
->handle
))) {
302 php_http_error(HE_WARNING
, PHP_HTTP_E_CLIENT_POOL
, "Could not attach request to pool: %s", curl_multi_strerror(rs
));
307 static STATUS
php_http_curl_client_pool_detach(php_http_client_pool_t
*h
, php_http_client_t
*r
)
309 php_http_curl_client_pool_t
*curl
= h
->ctx
;
310 php_http_curl_client_t
*recurl
= r
->ctx
;
312 TSRMLS_FETCH_FROM_CTX(h
->ts
);
314 if (r
->ops
!= php_http_curl_client_get_ops()) {
315 php_http_error(HE_WARNING
, PHP_HTTP_E_CLIENT_POOL
, "Cannot attach a non-curl client to this pool");
319 if (CURLM_OK
== (rs
= curl_multi_remove_handle(curl
->handle
, recurl
->handle
))) {
322 php_http_error(HE_WARNING
, PHP_HTTP_E_CLIENT_POOL
, "Could not detach request from pool: %s", curl_multi_strerror(rs
));
328 # define SELECT_ERROR SOCKET_ERROR
330 # define SELECT_ERROR -1
333 static STATUS
php_http_curl_client_pool_wait(php_http_client_pool_t
*h
, struct timeval
*custom_timeout
)
337 struct timeval timeout
;
338 php_http_curl_client_pool_t
*curl
= h
->ctx
;
340 #if PHP_HTTP_HAVE_EVENT
341 if (curl
->useevents
) {
342 TSRMLS_FETCH_FROM_CTX(h
->ts
);
344 php_http_error(HE_WARNING
, PHP_HTTP_E_RUNTIME
, "not implemented");
349 if (custom_timeout
&& timerisset(custom_timeout
)) {
350 timeout
= *custom_timeout
;
352 long max_tout
= 1000;
354 if ((CURLM_OK
== curl_multi_timeout(curl
->handle
, &max_tout
)) && (max_tout
> 0)) {
355 timeout
.tv_sec
= max_tout
/ 1000;
356 timeout
.tv_usec
= (max_tout
% 1000) * 1000;
359 timeout
.tv_usec
= 1000;
367 if (CURLM_OK
== curl_multi_fdset(curl
->handle
, &R
, &W
, &E
, &MAX
)) {
369 php_http_sleep((double) timeout
.tv_sec
+ (double) (timeout
.tv_usec
/ PHP_HTTP_MCROSEC
));
371 } else if (SELECT_ERROR
!= select(MAX
+ 1, &R
, &W
, &E
, &timeout
)) {
378 static int php_http_curl_client_pool_once(php_http_client_pool_t
*h
)
380 php_http_curl_client_pool_t
*curl
= h
->ctx
;
382 #if PHP_HTTP_HAVE_EVENT
383 if (curl
->useevents
) {
384 TSRMLS_FETCH_FROM_CTX(h
->ts
);
385 php_http_error(HE_WARNING
, PHP_HTTP_E_RUNTIME
, "not implemented");
390 while (CURLM_CALL_MULTI_PERFORM
== curl_multi_perform(curl
->handle
, &curl
->unfinished
));
392 php_http_curl_client_pool_responsehandler(h
);
394 return curl
->unfinished
;
398 static STATUS
php_http_curl_client_pool_exec(php_http_client_pool_t
*h
)
400 TSRMLS_FETCH_FROM_CTX(h
->ts
);
402 #if PHP_HTTP_HAVE_EVENT
403 php_http_curl_client_pool_t
*curl
= h
->ctx
;
405 if (curl
->useevents
) {
406 php_http_curl_client_pool_timeout_callback(CURL_SOCKET_TIMEOUT
, EV_READ
|EV_WRITE
, h
);
408 int ev_rc
= event_base_dispatch(PHP_HTTP_G
->curl
.event_base
);
411 fprintf(stderr
, "%c", "X.0"[ev_rc
+1]);
415 php_http_error(HE_ERROR
, PHP_HTTP_E_RUNTIME
, "Error in event_base_dispatch()");
418 } while (curl
->unfinished
);
422 while (php_http_curl_client_pool_once(h
)) {
423 if (SUCCESS
!= php_http_curl_client_pool_wait(h
, NULL
)) {
425 /* see http://msdn.microsoft.com/library/en-us/winsock/winsock/windows_sockets_error_codes_2.asp */
426 php_http_error(HE_WARNING
, PHP_HTTP_E_SOCKET
, "WinSock error: %d", WSAGetLastError());
428 php_http_error(HE_WARNING
, PHP_HTTP_E_SOCKET
, strerror(errno
));
438 static STATUS
php_http_curl_client_pool_setopt(php_http_client_pool_t
*h
, php_http_client_pool_setopt_opt_t opt
, void *arg
)
440 php_http_curl_client_pool_t
*curl
= h
->ctx
;
443 case PHP_HTTP_CLIENT_POOL_OPT_ENABLE_PIPELINING
:
444 if (CURLM_OK
!= curl_multi_setopt(curl
->handle
, CURLMOPT_PIPELINING
, (long) *((zend_bool
*) arg
))) {
449 case PHP_HTTP_CLIENT_POOL_OPT_USE_EVENTS
:
450 #if PHP_HTTP_HAVE_EVENT
451 if ((curl
->useevents
= *((zend_bool
*) arg
))) {
452 if (!curl
->timeout
) {
453 curl
->timeout
= ecalloc(1, sizeof(struct event
));
455 curl_multi_setopt(curl
->handle
, CURLMOPT_SOCKETDATA
, h
);
456 curl_multi_setopt(curl
->handle
, CURLMOPT_SOCKETFUNCTION
, php_http_curl_client_pool_socket_callback
);
457 curl_multi_setopt(curl
->handle
, CURLMOPT_TIMERDATA
, h
);
458 curl_multi_setopt(curl
->handle
, CURLMOPT_TIMERFUNCTION
, php_http_curl_client_pool_timer_callback
);
460 curl_multi_setopt(curl
->handle
, CURLMOPT_SOCKETDATA
, NULL
);
461 curl_multi_setopt(curl
->handle
, CURLMOPT_SOCKETFUNCTION
, NULL
);
462 curl_multi_setopt(curl
->handle
, CURLMOPT_TIMERDATA
, NULL
);
463 curl_multi_setopt(curl
->handle
, CURLMOPT_TIMERFUNCTION
, NULL
);
474 static php_http_resource_factory_ops_t php_http_curlm_resource_factory_ops
= {
480 static php_http_client_pool_ops_t php_http_curl_client_pool_ops
= {
481 &php_http_curlm_resource_factory_ops
,
482 php_http_curl_client_pool_init
,
484 php_http_curl_client_pool_dtor
,
486 php_http_curl_client_pool_exec
,
487 php_http_curl_client_pool_wait
,
488 php_http_curl_client_pool_once
,
489 php_http_curl_client_pool_attach
,
490 php_http_curl_client_pool_detach
,
491 php_http_curl_client_pool_setopt
,
492 (php_http_new_t
) php_http_curl_client_pool_object_new_ex
,
493 php_http_curl_client_pool_get_class_entry
496 PHP_HTTP_API php_http_client_pool_ops_t
*php_http_curl_client_pool_get_ops(void)
498 return &php_http_curl_client_pool_ops
;
501 #define PHP_HTTP_BEGIN_ARGS(method, req_args) PHP_HTTP_BEGIN_ARGS_EX(HttpClientCURL, method, 0, req_args)
502 #define PHP_HTTP_EMPTY_ARGS(method) PHP_HTTP_EMPTY_ARGS_EX(HttpClientCURL, method, 0)
503 #define PHP_HTTP_CURL_ME(method, visibility) PHP_ME(HttpClientCURL, method, PHP_HTTP_ARGS(HttpClientCURL, method), visibility)
504 #define PHP_HTTP_CURL_ALIAS(method, func) PHP_HTTP_STATIC_ME_ALIAS(method, func, PHP_HTTP_ARGS(HttpClientCURL, method))
505 #define PHP_HTTP_CURL_MALIAS(me, al, vis) ZEND_FENTRY(me, ZEND_MN(HttpClientCURL_##al), PHP_HTTP_ARGS(HttpClientCURL, al), vis)
507 static zend_class_entry
*php_http_curl_client_pool_class_entry
;
509 zend_class_entry
*php_http_curl_client_pool_get_class_entry(void)
511 return php_http_curl_client_pool_class_entry
;
514 static zend_function_entry php_http_curl_client_pool_method_entry
[] = {
518 zend_object_value
php_http_curl_client_pool_object_new(zend_class_entry
*ce TSRMLS_DC
)
520 return php_http_curl_client_pool_object_new_ex(ce
, NULL
, NULL TSRMLS_CC
);
523 zend_object_value
php_http_curl_client_pool_object_new_ex(zend_class_entry
*ce
, php_http_client_pool_t
*p
, php_http_client_pool_object_t
**ptr TSRMLS_DC
)
525 zend_object_value ov
;
526 php_http_client_pool_object_t
*o
;
528 o
= ecalloc(1, sizeof(php_http_client_pool_object_t
));
529 zend_object_std_init((zend_object
*) o
, ce TSRMLS_CC
);
530 object_properties_init((zend_object
*) o
, ce
);
532 if (!(o
->pool
= p
)) {
533 o
->pool
= php_http_client_pool_init(NULL
, &php_http_curl_client_pool_ops
, NULL
, NULL TSRMLS_CC
);
540 ov
.handle
= zend_objects_store_put(o
, NULL
, php_http_client_pool_object_free
, NULL TSRMLS_CC
);
541 ov
.handlers
= php_http_client_pool_get_object_handlers();
547 PHP_MINIT_FUNCTION(http_curl_client_pool
)
549 if (SUCCESS
!= php_http_persistent_handle_provide(ZEND_STRL("http_client_pool.curl"), &php_http_curlm_resource_factory_ops
, NULL
, NULL
)) {
553 PHP_HTTP_REGISTER_CLASS(http
\\Curl
\\Client
, Pool
, http_curl_client_pool
, php_http_client_pool_get_class_entry(), 0);
554 php_http_curl_client_pool_class_entry
->create_object
= php_http_curl_client_pool_object_new
;
559 #if PHP_HTTP_HAVE_EVENT
560 PHP_RINIT_FUNCTION(http_curl_client_pool
)
562 if (!PHP_HTTP_G
->curl
.event_base
&& !(PHP_HTTP_G
->curl
.event_base
= event_base_new())) {
569 #if PHP_HTTP_HAVE_EVENT
570 PHP_RSHUTDOWN_FUNCTION(http_curl_client_pool
)
572 if (PHP_HTTP_G
->curl
.event_base
) {
573 event_base_free(PHP_HTTP_G
->curl
.event_base
);
579 #endif /* PHP_HTTP_HAVE_CURL */
586 * vim600: noet sw=4 ts=4 fdm=marker
587 * vim<600: noet sw=4 ts=4