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-2005, Michael Wallner <mike@php.net> |
10 +--------------------------------------------------------------------+
19 #define HTTP_WANT_CURL
22 #if defined(ZEND_ENGINE_2) && defined(HTTP_HAVE_CURL)
24 #include "php_http_api.h"
25 #include "php_http_exception_object.h"
26 #include "php_http_request_api.h"
27 #include "php_http_request_object.h"
28 #include "php_http_request_pool_api.h"
29 #include "php_http_requestpool_object.h"
31 #ifndef HTTP_DEBUG_REQPOOLS
32 # define HTTP_DEBUG_REQPOOLS 0
35 #ifndef HAVE_CURL_MULTI_STRERROR
36 # define curl_multi_strerror(dummy) "unknown error"
39 static int http_request_pool_compare_handles(void *h1
, void *h2
);
41 /* {{{ http_request_pool *http_request_pool_init(http_request_pool *) */
42 PHP_HTTP_API http_request_pool
*_http_request_pool_init(http_request_pool
*pool TSRMLS_DC
)
46 #if HTTP_DEBUG_REQPOOLS
47 fprintf(stderr
, "Initializing request pool %p\n", pool
);
50 if ((free_pool
= (!pool
))) {
51 pool
= emalloc(sizeof(http_request_pool
));
55 HTTP_CHECK_CURL_INIT(pool
->ch
, curl_multi_init(), ;);
64 zend_llist_init(&pool
->finished
, sizeof(zval
*), (llist_dtor_func_t
) ZVAL_PTR_DTOR
, 0);
65 zend_llist_init(&pool
->handles
, sizeof(zval
*), (llist_dtor_func_t
) ZVAL_PTR_DTOR
, 0);
67 #if HTTP_DEBUG_REQPOOLS
68 fprintf(stderr
, "Initialized request pool %p\n", pool
);
75 /* {{{ STATUS http_request_pool_attach(http_request_pool *, zval *) */
76 PHP_HTTP_API STATUS
_http_request_pool_attach(http_request_pool
*pool
, zval
*request TSRMLS_DC
)
78 getObjectEx(http_request_object
, req
, request
);
80 #if HTTP_DEBUG_REQPOOLS
81 fprintf(stderr
, "Attaching HttpRequest(#%d) %p to pool %p\n", Z_OBJ_HANDLE_P(request
), req
, pool
);
85 http_error_ex(HE_WARNING
, HTTP_E_INVALID_PARAM
, "HttpRequest object(#%d) is already member of %s HttpRequestPool", Z_OBJ_HANDLE_P(request
), req
->pool
== pool
? "this" : "another");
86 } else if (SUCCESS
!= http_request_object_requesthandler(req
, request
)) {
87 http_error_ex(HE_WARNING
, HTTP_E_REQUEST
, "Could not initialize HttpRequest object for attaching to the HttpRequestPool");
89 CURLMcode code
= curl_multi_add_handle(pool
->ch
, req
->request
->ch
);
91 if ((CURLM_OK
!= code
) && (CURLM_CALL_MULTI_PERFORM
!= code
)) {
92 http_error_ex(HE_WARNING
, HTTP_E_REQUEST_POOL
, "Could not attach HttpRequest object to the HttpRequestPool: %s", curl_multi_strerror(code
));
97 zend_llist_add_element(&pool
->handles
, &request
);
99 #if HTTP_DEBUG_REQPOOLS
100 fprintf(stderr
, "> %d HttpRequests attached to pool %p\n", zend_llist_count(&pool
->handles
), pool
);
109 /* {{{ STATUS http_request_pool_detach(http_request_pool *, zval *) */
110 PHP_HTTP_API STATUS
_http_request_pool_detach(http_request_pool
*pool
, zval
*request TSRMLS_DC
)
113 getObjectEx(http_request_object
, req
, request
);
115 #if HTTP_DEBUG_REQPOOLS
116 fprintf(stderr
, "Detaching HttpRequest(#%d) %p from pool %p\n", Z_OBJ_HANDLE_P(request
), req
, pool
);
120 /* not attached to any pool */
121 #if HTTP_DEBUG_REQPOOLS
122 fprintf(stderr
, "HttpRequest object(#%d) %p is not attached to any HttpRequestPool\n", Z_OBJ_HANDLE_P(request
), req
);
124 } else if (req
->pool
!= pool
) {
125 http_error_ex(HE_WARNING
, HTTP_E_INVALID_PARAM
, "HttpRequest object(#%d) is not attached to this HttpRequestPool", Z_OBJ_HANDLE_P(request
));
126 } else if (CURLM_OK
!= (code
= curl_multi_remove_handle(pool
->ch
, req
->request
->ch
))) {
127 http_error_ex(HE_WARNING
, HTTP_E_REQUEST_POOL
, "Could not detach HttpRequest object from the HttpRequestPool: %s", curl_multi_strerror(code
));
130 zend_llist_del_element(&pool
->finished
, request
, http_request_pool_compare_handles
);
131 zend_llist_del_element(&pool
->handles
, request
, http_request_pool_compare_handles
);
133 #if HTTP_DEBUG_REQPOOLS
134 fprintf(stderr
, "> %d HttpRequests remaining in pool %p\n", zend_llist_count(&pool
->handles
), pool
);
143 /* {{{ void http_request_pool_detach_all(http_request_pool *) */
144 PHP_HTTP_API
void _http_request_pool_detach_all(http_request_pool
*pool TSRMLS_DC
)
146 int count
= zend_llist_count(&pool
->handles
);
148 #if HTTP_DEBUG_REQPOOLS
149 fprintf(stderr
, "Detaching %d requests from pool %p\n", count
, pool
);
153 * we cannot apply a function to the llist which actually detaches
154 * the curl handle *and* removes the llist element --
155 * so let's get our hands dirty
159 zend_llist_position pos
;
160 zval
**handle
, **handles
= emalloc(count
* sizeof(zval
*));
162 for (handle
= zend_llist_get_first_ex(&pool
->handles
, &pos
); handle
; handle
= zend_llist_get_next_ex(&pool
->handles
, &pos
)) {
163 handles
[i
++] = *handle
;
166 /* should never happen */
168 zend_error(E_ERROR
, "number of fetched request handles do not match overall count");
172 for (i
= 0; i
< count
; ++i
) {
173 http_request_pool_detach(pool
, handles
[i
]);
179 /* {{{ STATUS http_request_pool_send(http_request_pool *) */
180 PHP_HTTP_API STATUS
_http_request_pool_send(http_request_pool
*pool TSRMLS_DC
)
182 #if HTTP_DEBUG_REQPOOLS
183 fprintf(stderr
, "Attempt to send %d requests of pool %p\n", zend_llist_count(&pool
->handles
), pool
);
186 while (http_request_pool_perform(pool
)) {
187 if (SUCCESS
!= http_request_pool_select(pool
)) {
189 http_error(HE_WARNING
, HTTP_E_SOCKET
, WSAGetLastError());
191 http_error(HE_WARNING
, HTTP_E_SOCKET
, strerror(errno
));
197 #if HTTP_DEBUG_REQPOOLS
198 fprintf(stderr
, "Finished sending %d HttpRequests of pool %p (still unfinished: %d)\n", zend_llist_count(&pool
->handles
), pool
, pool
->unfinished
);
205 /* {{{ void http_request_pool_dtor(http_request_pool *) */
206 PHP_HTTP_API
void _http_request_pool_dtor(http_request_pool
*pool TSRMLS_DC
)
208 #if HTTP_DEBUG_REQPOOLS
209 fprintf(stderr
, "Destructing request pool %p\n", pool
);
212 pool
->unfinished
= 0;
213 zend_llist_clean(&pool
->finished
);
214 zend_llist_clean(&pool
->handles
);
215 curl_multi_cleanup(pool
->ch
);
220 # define SELECT_ERROR SOCKET_ERROR
222 # define SELECT_ERROR -1
225 /* {{{ STATUS http_request_pool_select(http_request_pool *) */
226 PHP_HTTP_API STATUS
_http_request_pool_select(http_request_pool
*pool
)
230 struct timeval timeout
= {1, 0};
236 if (CURLM_OK
== curl_multi_fdset(pool
->ch
, &R
, &W
, &E
, &MAX
)) {
237 if (MAX
== -1 || SELECT_ERROR
!= select(MAX
+ 1, &R
, &W
, &E
, &timeout
)) {
245 /* {{{ int http_request_pool_perform(http_request_pool *) */
246 PHP_HTTP_API
int _http_request_pool_perform(http_request_pool
*pool TSRMLS_DC
)
251 while (CURLM_CALL_MULTI_PERFORM
== curl_multi_perform(pool
->ch
, &pool
->unfinished
));
253 while ((msg
= curl_multi_info_read(pool
->ch
, &remaining
))) {
254 if (CURLMSG_DONE
== msg
->msg
) {
255 if (CURLE_OK
!= msg
->data
.result
) {
256 http_request_pool_try
{
257 http_request
*r
= NULL
;
258 curl_easy_getinfo(msg
->easy_handle
, CURLINFO_PRIVATE
, &r
);
259 http_error_ex(HE_WARNING
, HTTP_E_REQUEST
, "%s; %s (%s)", curl_easy_strerror(msg
->data
.result
), r
?r
->_error
:"", r
?r
->url
:"");
260 } http_request_pool_catch();
262 http_request_pool_try
{
263 zend_llist_apply_with_argument(&pool
->handles
, (llist_apply_with_arg_func_t
) http_request_pool_responsehandler
, msg
->easy_handle TSRMLS_CC
);
264 } http_request_pool_catch();
267 http_request_pool_final();
269 return pool
->unfinished
;
273 /* {{{ void http_request_pool_responsehandler(zval **) */
274 void _http_request_pool_responsehandler(zval
**req
, CURL
*ch TSRMLS_DC
)
276 getObjectEx(http_request_object
, obj
, *req
);
278 if (obj
->request
->ch
== ch
) {
280 #if HTTP_DEBUG_REQPOOLS
281 fprintf(stderr
, "Fetching data from HttpRequest(#%d) %p of pool %p\n", Z_OBJ_HANDLE_PP(req
), obj
, obj
->pool
);
285 zend_llist_add_element(&obj
->pool
->finished
, req
);
286 http_request_object_responsehandler(obj
, *req
);
291 static void move_backtrace_args(zval
*from
, zval
*to TSRMLS_DC
)
293 zval
**args
, **trace_0
, *old_trace_0
, *trace
= NULL
;
295 if ((trace
= zend_read_property(zend_exception_get_default(), from
, "trace", lenof("trace"), 0 TSRMLS_CC
))) {
296 if (SUCCESS
== zend_hash_index_find(Z_ARRVAL_P(trace
), 0, (void **) &trace_0
)) {
297 old_trace_0
= *trace_0
;
298 if (SUCCESS
== zend_hash_find(Z_ARRVAL_PP(trace_0
), "args", sizeof("args"), (void **) &args
)) {
299 if ((trace
= zend_read_property(zend_exception_get_default(), to
, "trace", lenof("trace"), 0 TSRMLS_CC
))) {
300 if (SUCCESS
== zend_hash_index_find(Z_ARRVAL_P(trace
), 0, (void **) &trace_0
)) {
302 add_assoc_zval(*trace_0
, "args", *args
);
303 zend_hash_del(Z_ARRVAL_P(old_trace_0
), "args", sizeof("args"));
310 /* {{{ void http_request_pool_wrap_exception(zval *, zval *) */
311 void _http_request_pool_wrap_exception(zval
*old_exception
, zval
*new_exception TSRMLS_DC
)
313 zend_class_entry
*ce
= HTTP_EX_CE(request_pool
);
315 /* if old_exception is already an HttpRequestPoolException append the new one,
316 else create a new HttpRequestPoolException and append the old and new exceptions */
317 if (old_exception
&& Z_OBJCE_P(old_exception
) == ce
) {
318 zval
*old_exprop
, *new_exprop
;
320 MAKE_STD_ZVAL(new_exprop
);
321 array_init(new_exprop
);
322 old_exprop
= zend_read_property(ce
, old_exception
, "exceptionStack", lenof("exceptionStack"), 0 TSRMLS_CC
);
323 if (Z_TYPE_P(old_exprop
) == IS_ARRAY
) {
324 array_copy(old_exprop
, new_exprop
);
326 add_next_index_zval(new_exprop
, new_exception
);
327 zend_update_property(ce
, old_exception
, "exceptionStack", lenof("exceptionStack"), new_exprop TSRMLS_CC
);
328 zval_ptr_dtor(&new_exprop
);
330 EG(exception
) = old_exception
;
331 } else if (new_exception
&& Z_OBJCE_P(new_exception
) != ce
){
332 zval
*exval
, *exprop
;
334 MAKE_STD_ZVAL(exval
);
335 object_init_ex(exval
, ce
);
336 MAKE_STD_ZVAL(exprop
);
340 add_next_index_zval(exprop
, old_exception
);
342 move_backtrace_args(new_exception
, exval TSRMLS_CC
);
343 zend_update_property_long(ce
, exval
, "code", lenof("code"), HTTP_E_REQUEST_POOL TSRMLS_CC
);
344 zend_update_property_string(ce
, exval
, "message", lenof("message"), "See exceptionStack property" TSRMLS_CC
);
345 add_next_index_zval(exprop
, new_exception
);
346 zend_update_property(ce
, exval
, "exceptionStack", lenof("exceptionStack"), exprop TSRMLS_CC
);
347 zval_ptr_dtor(&exprop
);
349 EG(exception
) = exval
;
356 /* {{{ static int http_request_pool_compare_handles(void *, void *) */
357 static int http_request_pool_compare_handles(void *h1
, void *h2
)
359 return (Z_OBJ_HANDLE_PP((zval
**) h1
) == Z_OBJ_HANDLE_P((zval
*) h2
));
363 #endif /* ZEND_ENGINE_2 && HTTP_HAVE_CURL */
371 * vim600: noet sw=4 ts=4 fdm=marker
372 * vim<600: noet sw=4 ts=4