- add missing zval type check
[m6w6/ext-http] / http_request_pool_api.c
1 /*
2 +--------------------------------------------------------------------+
3 | PECL :: http |
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-2007, Michael Wallner <mike@php.net> |
10 +--------------------------------------------------------------------+
11 */
12
13 /* $Id$ */
14
15 #define HTTP_WANT_CURL
16 #include "php_http.h"
17
18 #if defined(ZEND_ENGINE_2) && defined(HTTP_HAVE_CURL)
19
20 #include "php_http_api.h"
21 #include "php_http_exception_object.h"
22 #include "php_http_persistent_handle_api.h"
23 #include "php_http_request_api.h"
24 #include "php_http_request_object.h"
25 #include "php_http_request_pool_api.h"
26 #include "php_http_requestpool_object.h"
27
28 #ifndef HTTP_DEBUG_REQPOOLS
29 # define HTTP_DEBUG_REQPOOLS 0
30 #endif
31
32 static int http_request_pool_compare_handles(void *h1, void *h2);
33
34 PHP_MINIT_FUNCTION(http_request_pool)
35 {
36 if (SUCCESS != http_persistent_handle_provide("http_request_pool", curl_multi_init, (http_persistent_handle_dtor) curl_multi_cleanup, NULL)) {
37 return FAILURE;
38 }
39 return SUCCESS;
40 }
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
47 #if HTTP_DEBUG_REQPOOLS
48 fprintf(stderr, "Initializing request pool %p\n", pool);
49 #endif
50
51 if ((free_pool = (!pool))) {
52 pool = emalloc(sizeof(http_request_pool));
53 pool->ch = NULL;
54 }
55
56 if (SUCCESS != http_persistent_handle_acquire("http_request_pool", &pool->ch)) {
57 if (free_pool) {
58 efree(pool);
59 }
60 return NULL;
61 }
62
63 pool->unfinished = 0;
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);
66
67 #if HTTP_DEBUG_REQPOOLS
68 fprintf(stderr, "Initialized request pool %p\n", pool);
69 #endif
70
71 return pool;
72 }
73 /* }}} */
74
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)
77 {
78 getObjectEx(http_request_object, req, request);
79
80 #if HTTP_DEBUG_REQPOOLS
81 fprintf(stderr, "Attaching HttpRequest(#%d) %p to pool %p\n", Z_OBJ_HANDLE_P(request), req, pool);
82 #endif
83
84 if (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(#%d) for attaching to the HttpRequestPool", Z_OBJ_HANDLE_P(request));
88 } else {
89 CURLMcode code = curl_multi_add_handle(pool->ch, req->request->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(#%d) to the HttpRequestPool: %s", Z_OBJ_HANDLE_P(request), curl_multi_strerror(code));
93 } else {
94 req->pool = pool;
95
96 ZVAL_ADDREF(request);
97 zend_llist_add_element(&pool->handles, &request);
98
99 #if HTTP_DEBUG_REQPOOLS
100 fprintf(stderr, "> %d HttpRequests attached to pool %p\n", zend_llist_count(&pool->handles), pool);
101 #endif
102 return SUCCESS;
103 }
104 }
105 return FAILURE;
106 }
107 /* }}} */
108
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)
111 {
112 CURLMcode code;
113 getObjectEx(http_request_object, req, request);
114
115 #if HTTP_DEBUG_REQPOOLS
116 fprintf(stderr, "Detaching HttpRequest(#%d) %p from pool %p\n", Z_OBJ_HANDLE_P(request), req, pool);
117 #endif
118
119 if (!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);
123 #endif
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 (req->request->_in_progress_cb) {
127 http_error_ex(HE_WARNING, HTTP_E_REQUEST_POOL, "HttpRequest object(#%d) cannot be detached from the HttpRequestPool while executing the progress callback", Z_OBJ_HANDLE_P(request));
128 } else if (CURLM_OK != (code = curl_multi_remove_handle(pool->ch, req->request->ch))) {
129 http_error_ex(HE_WARNING, HTTP_E_REQUEST_POOL, "Could not detach HttpRequest object(#%d) from the HttpRequestPool: %s", Z_OBJ_HANDLE_P(request), curl_multi_strerror(code));
130 } else {
131 req->pool = NULL;
132 zend_llist_del_element(&pool->finished, request, http_request_pool_compare_handles);
133 zend_llist_del_element(&pool->handles, request, http_request_pool_compare_handles);
134
135 #if HTTP_DEBUG_REQPOOLS
136 fprintf(stderr, "> %d HttpRequests remaining in pool %p\n", zend_llist_count(&pool->handles), pool);
137 #endif
138
139 return SUCCESS;
140 }
141 return FAILURE;
142 }
143 /* }}} */
144
145 /* {{{ void http_request_pool_apply(http_request_pool *, http_request_pool_apply_func) */
146 PHP_HTTP_API void _http_request_pool_apply(http_request_pool *pool, http_request_pool_apply_func cb TSRMLS_DC)
147 {
148 int count = zend_llist_count(&pool->handles);
149
150 if (count) {
151 int i = 0;
152 zend_llist_position pos;
153 zval **handle, **handles = emalloc(count * sizeof(zval *));
154
155 for (handle = zend_llist_get_first_ex(&pool->handles, &pos); handle; handle = zend_llist_get_next_ex(&pool->handles, &pos)) {
156 handles[i++] = *handle;
157 }
158
159 /* should never happen */
160 if (i != count) {
161 zend_error(E_ERROR, "number of fetched request handles do not match overall count");
162 count = i;
163 }
164
165 for (i = 0; i < count; ++i) {
166 if (cb(pool, handles[i] TSRMLS_CC)) {
167 break;
168 }
169 }
170 efree(handles);
171 }
172 }
173 /* }}} */
174
175 /* {{{ void http_request_pool_apply_with_arg(http_request_pool *, http_request_pool_apply_with_arg_func, void *) */
176 PHP_HTTP_API void _http_request_pool_apply_with_arg(http_request_pool *pool, http_request_pool_apply_with_arg_func cb, void *arg TSRMLS_DC)
177 {
178 int count = zend_llist_count(&pool->handles);
179
180 if (count) {
181 int i = 0;
182 zend_llist_position pos;
183 zval **handle, **handles = emalloc(count * sizeof(zval *));
184
185 for (handle = zend_llist_get_first_ex(&pool->handles, &pos); handle; handle = zend_llist_get_next_ex(&pool->handles, &pos)) {
186 handles[i++] = *handle;
187 }
188
189 /* should never happen */
190 if (i != count) {
191 zend_error(E_ERROR, "number of fetched request handles do not match overall count");
192 count = i;
193 }
194
195 for (i = 0; i < count; ++i) {
196 if (cb(pool, handles[i], arg TSRMLS_CC)) {
197 break;
198 }
199 }
200 efree(handles);
201 }
202 }
203 /* }}} */
204
205 /* {{{ void http_request_pool_detach_all(http_request_pool *) */
206 PHP_HTTP_API void _http_request_pool_detach_all(http_request_pool *pool TSRMLS_DC)
207 {
208 #if HTTP_DEBUG_REQPOOLS
209 fprintf(stderr, "Detaching %d requests from pool %p\n", zend_llist_count(&pool->handles), pool);
210 #endif
211 http_request_pool_apply(pool, _http_request_pool_detach);
212 }
213 /* }}} */
214
215 /* {{{ STATUS http_request_pool_send(http_request_pool *) */
216 PHP_HTTP_API STATUS _http_request_pool_send(http_request_pool *pool TSRMLS_DC)
217 {
218 #if HTTP_DEBUG_REQPOOLS
219 fprintf(stderr, "Attempt to send %d requests of pool %p\n", zend_llist_count(&pool->handles), pool);
220 #endif
221
222 while (http_request_pool_perform(pool)) {
223 if (SUCCESS != http_request_pool_select(pool)) {
224 #ifdef PHP_WIN32
225 /* see http://msdn.microsoft.com/library/en-us/winsock/winsock/windows_sockets_error_codes_2.asp */
226 http_error_ex(HE_WARNING, HTTP_E_SOCKET, "WinSock error: %d", WSAGetLastError());
227 #else
228 http_error(HE_WARNING, HTTP_E_SOCKET, strerror(errno));
229 #endif
230 return FAILURE;
231 }
232 }
233
234 #if HTTP_DEBUG_REQPOOLS
235 fprintf(stderr, "Finished sending %d HttpRequests of pool %p (still unfinished: %d)\n", zend_llist_count(&pool->handles), pool, pool->unfinished);
236 #endif
237
238 return SUCCESS;
239 }
240 /* }}} */
241
242 /* {{{ void http_request_pool_dtor(http_request_pool *) */
243 PHP_HTTP_API void _http_request_pool_dtor(http_request_pool *pool TSRMLS_DC)
244 {
245 #if HTTP_DEBUG_REQPOOLS
246 fprintf(stderr, "Destructing request pool %p\n", pool);
247 #endif
248
249 pool->unfinished = 0;
250 zend_llist_clean(&pool->finished);
251 zend_llist_clean(&pool->handles);
252 http_persistent_handle_release("http_request_pool", &pool->ch);
253 }
254 /* }}} */
255
256 #ifdef PHP_WIN32
257 # define SELECT_ERROR SOCKET_ERROR
258 #else
259 # define SELECT_ERROR -1
260 #endif
261
262 /* {{{ STATUS http_request_pool_select(http_request_pool *) */
263 PHP_HTTP_API STATUS _http_request_pool_select(http_request_pool *pool)
264 {
265 int MAX;
266 fd_set R, W, E;
267 struct timeval timeout = {1, 0};
268 #ifdef HAVE_CURL_MULTI_TIMEOUT
269 long max_tout = 1000;
270
271 if ((CURLM_OK == curl_multi_timeout(pool->ch, &max_tout)) && (max_tout != -1)) {
272 timeout.tv_sec = max_tout / 1000;
273 timeout.tv_usec = (max_tout % 1000) * 1000;
274 }
275 #endif
276
277 FD_ZERO(&R);
278 FD_ZERO(&W);
279 FD_ZERO(&E);
280
281 if (CURLM_OK == curl_multi_fdset(pool->ch, &R, &W, &E, &MAX)) {
282 if (MAX == -1) {
283 http_sleep((double) timeout.tv_sec + (double) (timeout.tv_usec / HTTP_MCROSEC));
284 return SUCCESS;
285 } else if (SELECT_ERROR != select(MAX + 1, &R, &W, &E, &timeout)) {
286 return SUCCESS;
287 }
288 }
289 return FAILURE;
290 }
291 /* }}} */
292
293 /* {{{ int http_request_pool_perform(http_request_pool *) */
294 PHP_HTTP_API int _http_request_pool_perform(http_request_pool *pool TSRMLS_DC)
295 {
296 CURLMsg *msg;
297 int remaining = 0;
298
299 while (CURLM_CALL_MULTI_PERFORM == curl_multi_perform(pool->ch, &pool->unfinished));
300
301 while ((msg = curl_multi_info_read(pool->ch, &remaining))) {
302 if (CURLMSG_DONE == msg->msg) {
303 if (CURLE_OK != msg->data.result) {
304 http_request *r = NULL;
305 curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, &r);
306 http_error_ex(HE_WARNING, HTTP_E_REQUEST, "%s; %s (%s)", curl_easy_strerror(msg->data.result), r?r->_error:"", r?r->url:"");
307 }
308 http_request_pool_apply_with_arg(pool, _http_request_pool_responsehandler, msg->easy_handle);
309 }
310 }
311
312 return pool->unfinished;
313 }
314 /* }}} */
315
316 /* {{{ void http_request_pool_responsehandler(http_request_pool *, zval *, void *) */
317 int _http_request_pool_responsehandler(http_request_pool *pool, zval *req, void *ch TSRMLS_DC)
318 {
319 getObjectEx(http_request_object, obj, req);
320
321 if ((!ch) || obj->request->ch == (CURL *) ch) {
322
323 #if HTTP_DEBUG_REQPOOLS
324 fprintf(stderr, "Fetching data from HttpRequest(#%d) %p of pool %p\n", Z_OBJ_HANDLE_P(req), obj, obj->pool);
325 #endif
326
327 ZVAL_ADDREF(req);
328 zend_llist_add_element(&obj->pool->finished, &req);
329 http_request_object_responsehandler(obj, req);
330 return 1;
331 }
332 return 0;
333 }
334 /* }}} */
335
336 /*#*/
337
338 /* {{{ static int http_request_pool_compare_handles(void *, void *) */
339 static int http_request_pool_compare_handles(void *h1, void *h2)
340 {
341 return (Z_OBJ_HANDLE_PP((zval **) h1) == Z_OBJ_HANDLE_P((zval *) h2));
342 }
343 /* }}} */
344
345 #endif /* ZEND_ENGINE_2 && HTTP_HAVE_CURL */
346
347
348 /*
349 * Local variables:
350 * tab-width: 4
351 * c-basic-offset: 4
352 * End:
353 * vim600: noet sw=4 ts=4 fdm=marker
354 * vim<600: noet sw=4 ts=4
355 */
356