- http_redirect(): proper check for ext/session; fix possible mem-leaks
[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 ZEND_EXTERN_MODULE_GLOBALS(http);
38
39 static void http_request_pool_freebody(http_request_body **body);
40 static int http_request_pool_compare_handles(void *h1, void *h2);
41
42 /* {{{ http_request_pool *http_request_pool_init(http_request_pool *) */
43 PHP_HTTP_API http_request_pool *_http_request_pool_init(http_request_pool *pool TSRMLS_DC)
44 {
45 zend_bool free_pool;
46 #if HTTP_DEBUG_REQPOOLS
47 fprintf(stderr, "Initializing request pool %p\n", pool);
48 #endif
49 if ((free_pool = (!pool))) {
50 pool = emalloc(sizeof(http_request_pool));
51 pool->ch = NULL;
52 }
53
54 if (!pool->ch) {
55 if (!(pool->ch = curl_multi_init())) {
56 http_error(HE_WARNING, HTTP_E_REQUEST, "Could not initialize curl");
57 if (free_pool) {
58 efree(pool);
59 }
60 return NULL;
61 }
62 }
63
64 pool->unfinished = 0;
65 zend_llist_init(&pool->handles, sizeof(zval *), (llist_dtor_func_t) ZVAL_PTR_DTOR, 0);
66 zend_llist_init(&pool->bodies, sizeof(http_request_body *), (llist_dtor_func_t) http_request_pool_freebody, 0);
67 #if HTTP_DEBUG_REQPOOLS
68 fprintf(stderr, "Initialized request pool %p\n", pool);
69 #endif
70 return pool;
71 }
72 /* }}} */
73
74 /* {{{ STATUS http_request_pool_attach(http_request_pool *, zval *) */
75 PHP_HTTP_API STATUS _http_request_pool_attach(http_request_pool *pool, zval *request TSRMLS_DC)
76 {
77 getObjectEx(http_request_object, req, request);
78 #if HTTP_DEBUG_REQPOOLS
79 fprintf(stderr, "Attaching HttpRequest(#%d) %p to pool %p\n", Z_OBJ_HANDLE_P(request), req, pool);
80 #endif
81 if (req->pool) {
82 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");
83 } else {
84 http_request_body *body = http_request_body_new();
85
86 if (SUCCESS != http_request_pool_requesthandler(request, body)) {
87 http_error_ex(HE_WARNING, HTTP_E_REQUEST, "Could not initialize HttpRequest object for attaching to the HttpRequestPool");
88 } else {
89 CURLMcode code = curl_multi_add_handle(pool->ch, req->ch);
90
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));
93 } else {
94 req->pool = pool;
95
96 zend_llist_add_element(&pool->handles, &request);
97 zend_llist_add_element(&pool->bodies, &body);
98
99 zval_add_ref(&request);
100 zend_objects_store_add_ref(request TSRMLS_CC);
101
102 #if HTTP_DEBUG_REQPOOLS
103 fprintf(stderr, "> %d HttpRequests attached to pool %p\n", zend_llist_count(&pool->handles), pool);
104 #endif
105 return SUCCESS;
106 }
107 }
108 efree(body);
109 }
110 return FAILURE;
111 }
112 /* }}} */
113
114 /* {{{ STATUS http_request_pool_detach(http_request_pool *, zval *) */
115 PHP_HTTP_API STATUS _http_request_pool_detach(http_request_pool *pool, zval *request TSRMLS_DC)
116 {
117 getObjectEx(http_request_object, req, request);
118 #if HTTP_DEBUG_REQPOOLS
119 fprintf(stderr, "Detaching HttpRequest(#%d) %p from pool %p\n", Z_OBJ_HANDLE_P(request), req, pool);
120 #endif
121 if (!req->pool) {
122 /* not attached to any pool */
123 #if HTTP_DEBUG_REQPOOLS
124 fprintf(stderr, "HttpRequest object(#%d) %p is not attached to any HttpRequestPool\n", Z_OBJ_HANDLE_P(request), req);
125 #endif
126 } else if (req->pool != pool) {
127 http_error_ex(HE_WARNING, HTTP_E_INVALID_PARAM, "HttpRequest object(#%d) is not attached to this HttpRequestPool", Z_OBJ_HANDLE_P(request));
128 } else {
129 CURLMcode code;
130
131 req->pool = NULL;
132 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);
135 #endif
136 if (CURLM_OK != (code = curl_multi_remove_handle(pool->ch, req->ch))) {
137 http_error_ex(HE_WARNING, HTTP_E_REQUEST_POOL, "Could not detach HttpRequest object from the HttpRequestPool: %s", curl_multi_strerror(code));
138 } else {
139 return SUCCESS;
140 }
141 }
142 return FAILURE;
143 }
144 /* }}} */
145
146 /* {{{ void http_request_pool_detach_all(http_request_pool *) */
147 PHP_HTTP_API void _http_request_pool_detach_all(http_request_pool *pool TSRMLS_DC)
148 {
149 int count = zend_llist_count(&pool->handles);
150 #if HTTP_DEBUG_REQPOOLS
151 fprintf(stderr, "Detaching %d requests from pool %p\n", count, pool);
152 #endif
153 /*
154 * we cannot apply a function to the llist which actually detaches
155 * the curl handle *and* removes the llist element --
156 * so let's get our hands dirty
157 */
158 if (count) {
159 int i = 0;
160 zend_llist_position pos;
161 zval **handle, **handles = emalloc(count * sizeof(zval *));
162
163 for (handle = zend_llist_get_first_ex(&pool->handles, &pos); handle; handle = zend_llist_get_next_ex(&pool->handles, &pos)) {
164 handles[i++] = *handle;
165 }
166 for (i = 0; i < count; ++i) {
167 http_request_pool_detach(pool, handles[i]);
168 }
169 efree(handles);
170 }
171 }
172
173
174 /* {{{ STATUS http_request_pool_send(http_request_pool *) */
175 PHP_HTTP_API STATUS _http_request_pool_send(http_request_pool *pool TSRMLS_DC)
176 {
177 #if HTTP_DEBUG_REQPOOLS
178 fprintf(stderr, "Attempt to send %d requests of pool %p\n", zend_llist_count(&pool->handles), pool);
179 #endif
180 while (http_request_pool_perform(pool)) {
181 #if HTTP_DEBUG_REQPOOLS
182 fprintf(stderr, "> %d unfinished requests of pool %p remaining\n", pool->unfinished, pool);
183 #endif
184 if (SUCCESS != http_request_pool_select(pool)) {
185 http_error(HE_WARNING, HTTP_E_SOCKET, "Socket error");
186 return FAILURE;
187 }
188 }
189 #if HTTP_DEBUG_REQPOOLS
190 fprintf(stderr, "Finished sending %d HttpRequests of pool %p (still unfinished: %d)\n", zend_llist_count(&pool->handles), pool, pool->unfinished);
191 #endif
192 zend_llist_apply(&pool->handles, (llist_apply_func_t) http_request_pool_responsehandler TSRMLS_CC);
193 return SUCCESS;
194 }
195 /* }}} */
196
197 /* {{{ void http_request_pool_dtor(http_request_pool *) */
198 PHP_HTTP_API void _http_request_pool_dtor(http_request_pool *pool TSRMLS_DC)
199 {
200 #if HTTP_DEBUG_REQPOOLS
201 fprintf(stderr, "Destructing request pool %p\n", pool);
202 #endif
203 pool->unfinished = 0;
204 zend_llist_clean(&pool->handles);
205 zend_llist_clean(&pool->bodies);
206 curl_multi_cleanup(pool->ch);
207 }
208 /* }}} */
209
210 /* {{{ STATUS http_request_pool_select(http_request_pool *) */
211 PHP_HTTP_API STATUS _http_request_pool_select(http_request_pool *pool)
212 {
213 int MAX;
214 fd_set R, W, E;
215 struct timeval timeout = {1, 0};
216
217 FD_ZERO(&R);
218 FD_ZERO(&W);
219 FD_ZERO(&E);
220
221 curl_multi_fdset(pool->ch, &R, &W, &E, &MAX);
222 return (-1 != select(MAX + 1, &R, &W, &E, &timeout)) ? SUCCESS : FAILURE;
223 }
224 /* }}} */
225
226 /* {{{ int http_request_pool_perform(http_request_pool *) */
227 PHP_HTTP_API int _http_request_pool_perform(http_request_pool *pool)
228 {
229 while (CURLM_CALL_MULTI_PERFORM == curl_multi_perform(pool->ch, &pool->unfinished));
230 return pool->unfinished;
231 }
232 /* }}} */
233
234 /* {{{ STATUS http_request_pool_requesthandler(zval *, http_request_body *) */
235 STATUS _http_request_pool_requesthandler(zval *request, http_request_body *body TSRMLS_DC)
236 {
237 getObjectEx(http_request_object, req, request);
238 if (SUCCESS == http_request_object_requesthandler(req, request, body)) {
239 http_request_conv(req->ch, &req->response, &req->request);
240 return SUCCESS;
241 }
242 return FAILURE;
243 }
244 /* }}} */
245
246 /* {{{ void http_request_pool_responsehandler(zval **) */
247 void _http_request_pool_responsehandler(zval **req TSRMLS_DC)
248 {
249 getObjectEx(http_request_object, obj, *req);
250 #if HTTP_DEBUG_REQPOOLS
251 fprintf(stderr, "Fetching data from HttpRequest(#%d) %p of pool %p\n", Z_OBJ_HANDLE_PP(req), obj, obj->pool);
252 #endif
253 http_request_object_responsehandler(obj, *req);
254 }
255 /* }}} */
256
257 /*#*/
258
259 /* {{{ static void http_request_pool_freebody(http_request_body **) */
260 static void http_request_pool_freebody(http_request_body **body)
261 {
262 TSRMLS_FETCH();
263 http_request_body_free(*body);
264 }
265 /* }}} */
266
267 /* {{{ static int http_request_pool_compare_handles(void *, void *) */
268 static int http_request_pool_compare_handles(void *h1, void *h2)
269 {
270 int match = (Z_OBJ_HANDLE_PP((zval **) h1) == Z_OBJ_HANDLE_P((zval *) h2));
271 #if HTTP_DEBUG_REQPOOLS
272 /* if(match) fprintf(stderr, "OK\n"); */
273 #endif
274 return match;
275 }
276 /* }}} */
277
278 #endif /* ZEND_ENGINE_2 && HTTP_HAVE_CURL */
279
280
281 /*
282 * Local variables:
283 * tab-width: 4
284 * c-basic-offset: 4
285 * End:
286 * vim600: noet sw=4 ts=4 fdm=marker
287 * vim<600: noet sw=4 ts=4
288 */
289