- API juggling
[m6w6/ext-http] / http_request_pool_api.c
1 /*
2 +----------------------------------------------------------------------+
3 | PECL :: http |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 3.0 of the PHP license, that |
6 | is bundled with this package in the file LICENSE, and is available |
7 | through the world-wide-web at http://www.php.net/license/3_0.txt. |
8 | If you did not receive a copy of the PHP license and are unable to |
9 | obtain it through the world-wide-web, please send a note to |
10 | license@php.net so we can mail you a copy immediately. |
11 +----------------------------------------------------------------------+
12 | Copyright (c) 2004-2005 Michael Wallner <mike@php.net> |
13 +----------------------------------------------------------------------+
14 */
15
16 /* $Id$ */
17
18 #ifdef HAVE_CONFIG_H
19 # include "config.h"
20 #endif
21 #include "php.h"
22
23 #if defined(ZEND_ENGINE_2) && defined(HTTP_HAVE_CURL)
24
25 #include "php_http.h"
26 #include "php_http_std_defs.h"
27 #include "php_http_api.h"
28 #include "php_http_request_api.h"
29 #include "php_http_request_pool_api.h"
30 #include "php_http_request_object.h"
31 #include "php_http_requestpool_object.h"
32
33 #ifndef HTTP_DEBUG_REQPOOLS
34 # define HTTP_DEBUG_REQPOOLS 0
35 #endif
36
37 static void http_request_pool_freebody(http_request_body **body);
38 static int http_request_pool_compare_handles(void *h1, void *h2);
39
40 /* {{{ http_request_pool *http_request_pool_init(http_request_pool *) */
41 PHP_HTTP_API http_request_pool *_http_request_pool_init(http_request_pool *pool TSRMLS_DC)
42 {
43 zend_bool free_pool;
44 #if HTTP_DEBUG_REQPOOLS
45 fprintf(stderr, "Initializing request pool %p\n", pool);
46 #endif
47 if ((free_pool = (!pool))) {
48 pool = emalloc(sizeof(http_request_pool));
49 pool->ch = NULL;
50 }
51
52 if (!pool->ch) {
53 if (!(pool->ch = curl_multi_init())) {
54 http_error(E_WARNING, HTTP_E_CURL, "Could not initialize curl");
55 if (free_pool) {
56 efree(pool);
57 }
58 return NULL;
59 }
60 }
61
62 pool->unfinished = 0;
63 zend_llist_init(&pool->handles, sizeof(zval *), (llist_dtor_func_t) ZVAL_PTR_DTOR, 0);
64 zend_llist_init(&pool->bodies, sizeof(http_request_body *), (llist_dtor_func_t) http_request_pool_freebody, 0);
65 #if HTTP_DEBUG_REQPOOLS
66 fprintf(stderr, "Initialized request pool %p\n", pool);
67 #endif
68 return pool;
69 }
70 /* }}} */
71
72 /* {{{ STATUS http_request_pool_attach(http_request_pool *, zval *) */
73 PHP_HTTP_API STATUS _http_request_pool_attach(http_request_pool *pool, zval *request TSRMLS_DC)
74 {
75 getObjectEx(http_request_object, req, request);
76 #if HTTP_DEBUG_REQPOOLS
77 fprintf(stderr, "Attaching HttpRequest(#%d) %p to pool %p\n", Z_OBJ_HANDLE_P(request), req, pool);
78 #endif
79 if (req->pool) {
80 http_error_ex(E_WARNING, HTTP_E_CURL, "HttpRequest object(#%d) is already member of %s HttpRequestPool", Z_OBJ_HANDLE_P(request), req->pool == pool ? "this" : "another");
81 } else {
82 http_request_body *body = http_request_body_new();
83 zval *info = GET_PROP_EX(req, request, responseInfo);
84
85 if (SUCCESS != http_request_object_requesthandler(req, request, body)) {
86 http_error_ex(E_WARNING, HTTP_E_CURL, "Could not initialize HttpRequest object for attaching to the HttpRequestPool");
87 } else {
88 CURLMcode code = curl_multi_add_handle(pool->ch, req->ch);
89
90 if ((CURLM_OK != code) && (CURLM_CALL_MULTI_PERFORM != code)) {
91 http_error_ex(E_WARNING, HTTP_E_CURL, "Could not attach HttpRequest object to the HttpRequestPool: %s", curl_multi_strerror(code));
92 } else {
93 req->pool = pool;
94
95 zend_llist_add_element(&pool->handles, &request);
96 zend_llist_add_element(&pool->bodies, &body);
97
98 zval_add_ref(&request);
99 zend_objects_store_add_ref(request TSRMLS_CC);
100
101 #if HTTP_DEBUG_REQPOOLS
102 fprintf(stderr, "> %d HttpRequests attached to pool %p\n", zend_llist_count(&pool->handles), pool);
103 #endif
104 return SUCCESS;
105 }
106 }
107 efree(body);
108 }
109 return FAILURE;
110 }
111 /* }}} */
112
113 /* {{{ STATUS http_request_pool_detach(http_request_pool *, zval *) */
114 PHP_HTTP_API STATUS _http_request_pool_detach(http_request_pool *pool, zval *request TSRMLS_DC)
115 {
116 getObjectEx(http_request_object, req, request);
117 #if HTTP_DEBUG_REQPOOLS
118 fprintf(stderr, "Detaching HttpRequest(#%d) %p from pool %p\n", Z_OBJ_HANDLE_P(request), req, pool);
119 #endif
120 if (!req->pool) {
121 /* not attached to any pool */
122 #if HTTP_DEBUG_REQPOOLS
123 fprintf(stderr, "HttpRequest object(#%d) %p is not attached to any HttpRequestPool\n", Z_OBJ_HANDLE_P(request), req);
124 #endif
125 } else if (req->pool != pool) {
126 http_error_ex(E_WARNING, HTTP_E_CURL, "HttpRequest object(#%d) is not attached to this HttpRequestPool", Z_OBJ_HANDLE_P(request));
127 } else {
128 CURLMcode code;
129
130 req->pool = NULL;
131 zend_llist_del_element(&pool->handles, request, http_request_pool_compare_handles);
132 #if HTTP_DEBUG_REQPOOLS
133 fprintf(stderr, "> %d HttpRequests remaining in pool %p\n", zend_llist_count(&pool->handles), pool);
134 #endif
135 if (CURLM_OK != (code = curl_multi_remove_handle(pool->ch, req->ch))) {
136 http_error_ex(E_WARNING, HTTP_E_CURL, "Could not detach HttpRequest object from the HttpRequestPool: %s", curl_multi_strerror(code));
137 } else {
138 return SUCCESS;
139 }
140 }
141 return FAILURE;
142 }
143 /* }}} */
144
145 /* {{{ void http_request_pool_detach_all(http_request_pool *) */
146 PHP_HTTP_API void _http_request_pool_detach_all(http_request_pool *pool TSRMLS_DC)
147 {
148 int count = zend_llist_count(&pool->handles);
149 #if HTTP_DEBUG_REQPOOLS
150 fprintf(stderr, "Detaching %d requests from pool %p\n", count, pool);
151 #endif
152 /*
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
156 */
157 if (count) {
158 int i = 0;
159 zend_llist_position pos;
160 zval **handle, **handles = emalloc(count * sizeof(zval *));
161
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;
164 }
165 for (i = 0; i < count; ++i) {
166 http_request_pool_detach(pool, handles[i]);
167 }
168 efree(handles);
169 }
170 }
171
172
173 /* {{{ STATUS http_request_pool_send(http_request_pool *) */
174 PHP_HTTP_API STATUS _http_request_pool_send(http_request_pool *pool TSRMLS_DC)
175 {
176 #if HTTP_DEBUG_REQPOOLS
177 fprintf(stderr, "Attempt to send %d requests of pool %p\n", zend_llist_count(&pool->handles), pool);
178 #endif
179 while (http_request_pool_perform(pool)) {
180 #if HTTP_DEBUG_REQPOOLS
181 fprintf(stderr, "> %d unfinished requests of pool %p remaining\n", pool->unfinished, pool);
182 #endif
183 if (SUCCESS != http_request_pool_select(pool)) {
184 http_error(E_WARNING, HTTP_E_CURL, "Socket error");
185 return FAILURE;
186 }
187 }
188 #if HTTP_DEBUG_REQPOOLS
189 fprintf(stderr, "Finished sending %d HttpRequests of pool %p (still unfinished: %d)\n", zend_llist_count(&pool->handles), pool, pool->unfinished);
190 #endif
191 zend_llist_apply(&pool->handles, (llist_apply_func_t) http_request_pool_responsehandler TSRMLS_CC);
192 return SUCCESS;
193 }
194 /* }}} */
195
196 /* {{{ void http_request_pool_dtor(http_request_pool *) */
197 PHP_HTTP_API void _http_request_pool_dtor(http_request_pool *pool TSRMLS_DC)
198 {
199 #if HTTP_DEBUG_REQPOOLS
200 fprintf(stderr, "Destructing request pool %p\n", pool);
201 #endif
202 pool->unfinished = 0;
203 zend_llist_clean(&pool->handles);
204 zend_llist_clean(&pool->bodies);
205 curl_multi_cleanup(pool->ch);
206 }
207 /* }}} */
208
209 /* {{{ STATUS http_request_pool_select(http_request_pool *) */
210 PHP_HTTP_API STATUS _http_request_pool_select(http_request_pool *pool)
211 {
212 int MAX;
213 fd_set R, W, E;
214 struct timeval timeout = {1, 0};
215
216 FD_ZERO(&R);
217 FD_ZERO(&W);
218 FD_ZERO(&E);
219
220 curl_multi_fdset(pool->ch, &R, &W, &E, &MAX);
221 return (-1 != select(MAX + 1, &R, &W, &E, &timeout)) ? SUCCESS : FAILURE;
222 }
223 /* }}} */
224
225 /* {{{ int http_request_pool_perform(http_request_pool *) */
226 PHP_HTTP_API int _http_request_pool_perform(http_request_pool *pool)
227 {
228 while (CURLM_CALL_MULTI_PERFORM == curl_multi_perform(pool->ch, &pool->unfinished));
229 return pool->unfinished;
230 }
231 /* }}} */
232
233 /* {{{ void http_request_pool_responsehandler(zval **) */
234 void _http_request_pool_responsehandler(zval **req TSRMLS_DC)
235 {
236 getObjectEx(http_request_object, obj, *req);
237 #if HTTP_DEBUG_REQPOOLS
238 fprintf(stderr, "Fetching data from HttpRequest(#%d) %p of pool %p\n", Z_OBJ_HANDLE_PP(req), obj, obj->pool);
239 #endif
240 http_request_object_responsehandler(obj, *req);
241 }
242 /* }}} */
243
244 /*#*/
245
246 /* {{{ static void http_request_pool_freebody(http_request_body **) */
247 static void http_request_pool_freebody(http_request_body **body)
248 {
249 TSRMLS_FETCH();
250 http_request_body_free(*body);
251 }
252 /* }}} */
253
254 /* {{{ static int http_request_pool_compare_handles(void *, void *) */
255 static int http_request_pool_compare_handles(void *h1, void *h2)
256 {
257 int match = (Z_OBJ_HANDLE_PP((zval **) h1) == Z_OBJ_HANDLE_P((zval *) h2));
258 #if HTTP_DEBUG_REQPOOLS
259 /* if(match) fprintf(stderr, "OK\n"); */
260 #endif
261 return match;
262 }
263 /* }}} */
264
265 #endif /* ZEND_ENGINE_2 && HTTP_HAVE_CURL */
266
267
268 /*
269 * Local variables:
270 * tab-width: 4
271 * c-basic-offset: 4
272 * End:
273 * vim600: noet sw=4 ts=4 fdm=marker
274 * vim<600: noet sw=4 ts=4
275 */
276