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
21 typedef struct php_http_client_pool_curl
{
24 int unfinished
; /* int because of curl_multi_perform() */
26 #if PHP_HTTP_HAVE_EVENT
27 struct event
*timeout
;
31 } php_http_client_pool_curl_t
;
33 static void *php_http_curlm_ctor(void *opaque TSRMLS_DC
)
35 return curl_multi_init();
38 static void php_http_curlm_dtor(void *opaque
, void *handle TSRMLS_DC
)
40 curl_multi_cleanup(handle
);
44 static void php_http_client_pool_curl_responsehandler(php_http_client_pool_t
*pool
)
48 php_http_client_pool_curl_t
*curl
= pool
->ctx
;
49 TSRMLS_FETCH_FROM_CTX(pool
->ts
);
52 CURLMsg
*msg
= curl_multi_info_read(curl
->handle
, &remaining
);
54 if (msg
&& CURLMSG_DONE
== msg
->msg
) {
57 if (CURLE_OK
!= msg
->data
.result
) {
58 php_http_client_curl_storage_t
*st
= get_storage(msg
->easy_handle
);
59 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
));
62 php_http_client_pool_requests(pool
, &requests
, NULL
);
63 for (request
= requests
; *request
; ++request
) {
64 php_http_client_object_t
*obj
= zend_object_store_get_object(*request TSRMLS_CC
);
66 if (msg
->easy_handle
== ((php_http_client_curl_t
*) (obj
->client
->ctx
))->handle
) {
68 zend_llist_add_element(&pool
->clients
.finished
, request
);
69 php_http_client_object_responsehandler(obj
, *request TSRMLS_CC
);
72 zval_ptr_dtor(request
);
80 #if PHP_HTTP_HAVE_EVENT
82 typedef struct php_http_client_pool_event
{
84 php_http_client_pool_t
*pool
;
85 } php_http_client_pool_event_t
;
87 static inline int etoca(short action
) {
88 switch (action
& (EV_READ
|EV_WRITE
)) {
90 return CURL_CSELECT_IN
;
93 return CURL_CSELECT_OUT
;
95 case EV_READ
|EV_WRITE
:
96 return CURL_CSELECT_IN
|CURL_CSELECT_OUT
;
103 static void php_http_client_pool_curl_timeout_callback(int socket
, short action
, void *event_data
)
105 php_http_client_pool_t
*pool
= event_data
;
106 php_http_client_pool_curl_t
*curl
= pool
->ctx
;
109 fprintf(stderr
, "T");
111 if (curl
->useevents
) {
113 TSRMLS_FETCH_FROM_CTX(pool
->ts
);
115 while (CURLM_CALL_MULTI_PERFORM
== (rc
= curl_multi_socket_action(curl
->handle
, socket
, etoca(action
), &curl
->unfinished
)));
117 if (CURLM_OK
!= rc
) {
118 php_http_error(HE_WARNING
, PHP_HTTP_E_SOCKET
, "%s", curl_multi_strerror(rc
));
121 php_http_client_pool_curl_responsehandler(pool
);
125 static void php_http_client_pool_curl_event_callback(int socket
, short action
, void *event_data
)
127 php_http_client_pool_t
*pool
= event_data
;
128 php_http_client_pool_curl_t
*curl
= pool
->ctx
;
131 fprintf(stderr
, "E");
133 if (curl
->useevents
) {
134 CURLMcode rc
= CURLE_OK
;
135 TSRMLS_FETCH_FROM_CTX(pool
->ts
);
137 while (CURLM_CALL_MULTI_PERFORM
== (rc
= curl_multi_socket_action(curl
->handle
, socket
, etoca(action
), &curl
->unfinished
)));
139 if (CURLM_OK
!= rc
) {
140 php_http_error(HE_WARNING
, PHP_HTTP_E_SOCKET
, "%s", curl_multi_strerror(rc
));
143 php_http_client_pool_curl_responsehandler(pool
);
145 /* remove timeout if there are no transfers left */
146 if (!curl
->unfinished
&& event_initialized(curl
->timeout
) && event_pending(curl
->timeout
, EV_TIMEOUT
, NULL
)) {
147 event_del(curl
->timeout
);
152 static int php_http_client_pool_curl_socket_callback(CURL
*easy
, curl_socket_t sock
, int action
, void *socket_data
, void *assign_data
)
154 php_http_client_pool_t
*pool
= socket_data
;
155 php_http_client_pool_curl_t
*curl
= pool
->ctx
;
158 fprintf(stderr
, "S");
160 if (curl
->useevents
) {
161 int events
= EV_PERSIST
;
162 php_http_client_pool_event_t
*ev
= assign_data
;
163 TSRMLS_FETCH_FROM_CTX(pool
->ts
);
166 ev
= ecalloc(1, sizeof(php_http_client_pool_event_t
));
168 curl_multi_assign(curl
->handle
, sock
, ev
);
169 event_base_set(PHP_HTTP_G
->curl
.event_base
, &ev
->evnt
);
171 event_del(&ev
->evnt
);
181 case CURL_POLL_INOUT
:
182 events
|= EV_READ
|EV_WRITE
;
185 case CURL_POLL_REMOVE
:
192 php_http_error(HE_WARNING
, PHP_HTTP_E_SOCKET
, "Unknown socket action %d", action
);
196 event_set(&ev
->evnt
, sock
, events
, php_http_client_pool_curl_event_callback
, pool
);
197 event_add(&ev
->evnt
, NULL
);
203 static void php_http_client_pool_curl_timer_callback(CURLM
*multi
, long timeout_ms
, void *timer_data
)
205 php_http_client_pool_t
*pool
= timer_data
;
206 php_http_client_pool_curl_t
*curl
= pool
->ctx
;
209 fprintf(stderr
, "%ld", timeout_ms
);
211 if (curl
->useevents
) {
213 if (timeout_ms
< 0) {
214 php_http_client_pool_curl_timeout_callback(CURL_SOCKET_TIMEOUT
, CURL_CSELECT_IN
|CURL_CSELECT_OUT
, pool
);
215 } else if (timeout_ms
> 0 || !event_initialized(curl
->timeout
) || !event_pending(curl
->timeout
, EV_TIMEOUT
, NULL
)) {
216 struct timeval timeout
;
217 TSRMLS_FETCH_FROM_CTX(pool
->ts
);
219 if (!event_initialized(curl
->timeout
)) {
220 event_set(curl
->timeout
, -1, 0, php_http_client_pool_curl_timeout_callback
, pool
);
221 event_base_set(PHP_HTTP_G
->curl
.event_base
, curl
->timeout
);
222 } else if (event_pending(curl
->timeout
, EV_TIMEOUT
, NULL
)) {
223 event_del(curl
->timeout
);
226 timeout
.tv_sec
= timeout_ms
/ 1000;
227 timeout
.tv_usec
= (timeout_ms
% 1000) * 1000;
229 event_add(curl
->timeout
, &timeout
);
234 #endif /* HAVE_EVENT */
237 /* pool handler ops */
239 static php_http_client_pool_t
*php_http_client_pool_curl_init(php_http_client_pool_t
*h
, void *handle
)
241 php_http_client_pool_curl_t
*curl
;
242 TSRMLS_FETCH_FROM_CTX(h
->ts
);
244 if (!handle
&& !(handle
= php_http_resource_factory_handle_ctor(h
->rf TSRMLS_CC
))) {
245 php_http_error(HE_WARNING
, PHP_HTTP_E_CLIENT_POOL
, "could not initialize curl pool handle");
249 curl
= ecalloc(1, sizeof(*curl
));
250 curl
->handle
= handle
;
251 curl
->unfinished
= 0;
257 static void php_http_client_pool_curl_dtor(php_http_client_pool_t
*h
)
259 php_http_client_pool_curl_t
*curl
= h
->ctx
;
260 TSRMLS_FETCH_FROM_CTX(h
->ts
);
262 #if PHP_HTTP_HAVE_EVENT
264 efree(curl
->timeout
);
265 curl
->timeout
= NULL
;
268 curl
->unfinished
= 0;
269 php_http_client_pool_reset(h
);
271 php_http_resource_factory_handle_dtor(h
->rf
, curl
->handle TSRMLS_CC
);
277 static STATUS
php_http_client_pool_curl_attach(php_http_client_pool_t
*h
, php_http_client_t
*r
, const char *m
, const char *url
, php_http_message_body_t
*body
)
279 php_http_client_pool_curl_t
*curl
= h
->ctx
;
280 php_http_client_curl_t
*recurl
= r
->ctx
;
282 TSRMLS_FETCH_FROM_CTX(h
->ts
);
284 if (SUCCESS
!= php_http_curl_request_prepare(r
, m
, url
, body
)) {
288 if (CURLM_OK
== (rs
= curl_multi_add_handle(curl
->handle
, recurl
->handle
))) {
292 php_http_error(HE_WARNING
, PHP_HTTP_E_CLIENT_POOL
, "Could not attach request to pool: %s", curl_multi_strerror(rs
));
297 static STATUS
php_http_client_pool_curl_detach(php_http_client_pool_t
*h
, php_http_client_t
*r
)
299 php_http_client_pool_curl_t
*curl
= h
->ctx
;
300 php_http_client_curl_t
*recurl
= r
->ctx
;
301 CURLMcode rs
= curl_multi_remove_handle(curl
->handle
, recurl
->handle
);
302 TSRMLS_FETCH_FROM_CTX(h
->ts
);
304 if (CURLM_OK
== rs
) {
307 php_http_error(HE_WARNING
, PHP_HTTP_E_CLIENT_POOL
, "Could not detach request from pool: %s", curl_multi_strerror(rs
));
313 # define SELECT_ERROR SOCKET_ERROR
315 # define SELECT_ERROR -1
318 static STATUS
php_http_client_pool_curl_wait(php_http_client_pool_t
*h
, struct timeval
*custom_timeout
)
322 struct timeval timeout
;
323 php_http_client_pool_curl_t
*curl
= h
->ctx
;
325 #if PHP_HTTP_HAVE_EVENT
326 if (curl
->useevents
) {
327 TSRMLS_FETCH_FROM_CTX(h
->ts
);
329 php_http_error(HE_WARNING
, PHP_HTTP_E_RUNTIME
, "not implemented");
334 if (custom_timeout
&& timerisset(custom_timeout
)) {
335 timeout
= *custom_timeout
;
337 long max_tout
= 1000;
339 if ((CURLM_OK
== curl_multi_timeout(curl
->handle
, &max_tout
)) && (max_tout
> 0)) {
340 timeout
.tv_sec
= max_tout
/ 1000;
341 timeout
.tv_usec
= (max_tout
% 1000) * 1000;
344 timeout
.tv_usec
= 1000;
352 if (CURLM_OK
== curl_multi_fdset(curl
->handle
, &R
, &W
, &E
, &MAX
)) {
354 php_http_sleep((double) timeout
.tv_sec
+ (double) (timeout
.tv_usec
/ PHP_HTTP_MCROSEC
));
356 } else if (SELECT_ERROR
!= select(MAX
+ 1, &R
, &W
, &E
, &timeout
)) {
363 static int php_http_client_pool_curl_once(php_http_client_pool_t
*h
)
365 php_http_client_pool_curl_t
*curl
= h
->ctx
;
367 #if PHP_HTTP_HAVE_EVENT
368 if (curl
->useevents
) {
369 TSRMLS_FETCH_FROM_CTX(h
->ts
);
370 php_http_error(HE_WARNING
, PHP_HTTP_E_RUNTIME
, "not implemented");
375 while (CURLM_CALL_MULTI_PERFORM
== curl_multi_perform(curl
->handle
, &curl
->unfinished
));
377 php_http_client_pool_curl_responsehandler(h
);
379 return curl
->unfinished
;
382 #if PHP_HTTP_HAVE_EVENT
383 static void dolog(int i
, const char *m
) {
384 fprintf(stderr
, "%d: %s\n", i
, m
);
387 static STATUS
php_http_client_pool_curl_exec(php_http_client_pool_t
*h
)
389 TSRMLS_FETCH_FROM_CTX(h
->ts
);
391 #if PHP_HTTP_HAVE_EVENT
392 php_http_client_pool_curl_t
*curl
= h
->ctx
;
394 if (curl
->useevents
) {
395 event_set_log_callback(dolog
);
398 fprintf(stderr
, "X");
400 event_base_dispatch(PHP_HTTP_G
->curl
.event_base
);
401 } while (curl
->unfinished
);
405 while (php_http_client_pool_curl_once(h
)) {
406 if (SUCCESS
!= php_http_client_pool_curl_wait(h
, NULL
)) {
408 /* see http://msdn.microsoft.com/library/en-us/winsock/winsock/windows_sockets_error_codes_2.asp */
409 php_http_error(HE_WARNING
, PHP_HTTP_E_SOCKET
, "WinSock error: %d", WSAGetLastError());
411 php_http_error(HE_WARNING
, PHP_HTTP_E_SOCKET
, strerror(errno
));
421 static STATUS
php_http_client_pool_curl_setopt(php_http_client_pool_t
*h
, php_http_client_pool_setopt_opt_t opt
, void *arg
)
423 php_http_client_pool_curl_t
*curl
= h
->ctx
;
426 case PHP_HTTP_CLIENT_POOL_OPT_ENABLE_PIPELINING
:
427 if (CURLM_OK
!= curl_multi_setopt(curl
->handle
, CURLMOPT_PIPELINING
, (long) *((zend_bool
*) arg
))) {
432 case PHP_HTTP_CLIENT_POOL_OPT_USE_EVENTS
:
433 #if PHP_HTTP_HAVE_EVENT
434 if ((curl
->useevents
= *((zend_bool
*) arg
))) {
435 if (!curl
->timeout
) {
436 curl
->timeout
= ecalloc(1, sizeof(struct event
));
438 curl_multi_setopt(curl
->handle
, CURLMOPT_SOCKETDATA
, h
);
439 curl_multi_setopt(curl
->handle
, CURLMOPT_SOCKETFUNCTION
, php_http_client_pool_curl_socket_callback
);
440 curl_multi_setopt(curl
->handle
, CURLMOPT_TIMERDATA
, h
);
441 curl_multi_setopt(curl
->handle
, CURLMOPT_TIMERFUNCTION
, php_http_client_pool_curl_timer_callback
);
443 curl_multi_setopt(curl
->handle
, CURLMOPT_SOCKETDATA
, NULL
);
444 curl_multi_setopt(curl
->handle
, CURLMOPT_SOCKETFUNCTION
, NULL
);
445 curl_multi_setopt(curl
->handle
, CURLMOPT_TIMERDATA
, NULL
);
446 curl_multi_setopt(curl
->handle
, CURLMOPT_TIMERFUNCTION
, NULL
);
457 static php_http_resource_factory_ops_t php_http_curlm_resource_factory_ops
= {
463 static php_http_client_pool_ops_t php_http_client_pool_curl_ops
= {
464 &php_http_curlm_resource_factory_ops
,
465 php_http_client_pool_curl_init
,
467 php_http_client_pool_curl_dtor
,
469 php_http_client_pool_curl_exec
,
470 php_http_client_pool_curl_wait
,
471 php_http_client_pool_curl_once
,
472 php_http_client_pool_curl_attach
,
473 php_http_client_pool_curl_detach
,
474 php_http_client_pool_curl_setopt
,
477 PHP_HTTP_API php_http_client_pool_ops_t
*php_http_client_pool_curl_get_ops(void)
479 return &php_http_client_pool_curl_ops
;
482 #define PHP_HTTP_BEGIN_ARGS(method, req_args) PHP_HTTP_BEGIN_ARGS_EX(HttpClientCURL, method, 0, req_args)
483 #define PHP_HTTP_EMPTY_ARGS(method) PHP_HTTP_EMPTY_ARGS_EX(HttpClientCURL, method, 0)
484 #define PHP_HTTP_CURL_ME(method, visibility) PHP_ME(HttpClientCURL, method, PHP_HTTP_ARGS(HttpClientCURL, method), visibility)
485 #define PHP_HTTP_CURL_ALIAS(method, func) PHP_HTTP_STATIC_ME_ALIAS(method, func, PHP_HTTP_ARGS(HttpClientCURL, method))
486 #define PHP_HTTP_CURL_MALIAS(me, al, vis) ZEND_FENTRY(me, ZEND_MN(HttpClientCURL_##al), PHP_HTTP_ARGS(HttpClientCURL, al), vis)
488 zend_class_entry
*php_http_client_pool_curl_class_entry
;
489 zend_function_entry php_http_client_pool_curl_method_entry
[] = {
493 zend_object_value
php_http_client_pool_curl_object_new(zend_class_entry
*ce TSRMLS_DC
)
495 return php_http_client_pool_curl_object_new_ex(ce
, NULL
, NULL TSRMLS_CC
);
498 zend_object_value
php_http_client_pool_curl_object_new_ex(zend_class_entry
*ce
, php_http_client_pool_t
*p
, php_http_client_pool_object_t
**ptr TSRMLS_DC
)
500 zend_object_value ov
;
501 php_http_client_pool_object_t
*o
;
503 o
= ecalloc(1, sizeof(php_http_client_pool_object_t
));
504 zend_object_std_init((zend_object
*) o
, ce TSRMLS_CC
);
505 object_properties_init((zend_object
*) o
, ce
);
507 if (!(o
->pool
= p
)) {
508 o
->pool
= php_http_client_pool_init(NULL
, &php_http_client_pool_curl_ops
, NULL
, NULL TSRMLS_CC
);
515 ov
.handle
= zend_objects_store_put(o
, NULL
, php_http_client_pool_object_free
, NULL TSRMLS_CC
);
516 ov
.handlers
= php_http_client_pool_get_object_handlers();
522 PHP_MINIT_FUNCTION(http_client_pool_curl
)
524 if (SUCCESS
!= php_http_persistent_handle_provide(ZEND_STRL("http_client_pool.curl"), &php_http_curlm_resource_factory_ops
, NULL
, NULL
)) {
528 PHP_HTTP_REGISTER_CLASS(http
\\Client
\\Pool
, CURL
, http_client_pool_curl
, php_http_client_pool_class_entry
, 0);
529 php_http_client_pool_curl_class_entry
->create_object
= php_http_client_pool_curl_object_new
;
534 PHP_RINIT_FUNCTION(http_client_pool_curl
)
536 #if PHP_HTTP_HAVE_EVENT
537 if (!PHP_HTTP_G
->curl
.event_base
&& !(PHP_HTTP_G
->curl
.event_base
= event_init())) {
545 #endif /* PHP_HTTP_HAVE_CURL */
552 * vim600: noet sw=4 ts=4 fdm=marker
553 * vim<600: noet sw=4 ts=4