- don't trap into any other transfer encoding than chunked
[m6w6/ext-http] / http_requestpool_object.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
19 #ifdef HAVE_CONFIG_H
20 # include "config.h"
21 #endif
22 #include "php.h"
23
24 #if defined(ZEND_ENGINE_2) && defined(HTTP_HAVE_CURL)
25
26 #include "php_http_std_defs.h"
27 #include "php_http_api.h"
28 #include "php_http_requestpool_object.h"
29 #include "php_http_request_pool_api.h"
30 #include "php_http_request_object.h"
31 #include "php_http_exception_object.h"
32
33 #include "zend_interfaces.h"
34
35 #ifdef PHP_WIN32
36 # include <winsock2.h>
37 #endif
38 #include <curl/curl.h>
39
40 #define HTTP_BEGIN_ARGS(method, req_args) HTTP_BEGIN_ARGS_EX(HttpRequestPool, method, 0, req_args)
41 #define HTTP_EMPTY_ARGS(method, ret_ref) HTTP_EMPTY_ARGS_EX(HttpRequestPool, method, ret_ref)
42 #define HTTP_REQPOOL_ME(method, visibility) PHP_ME(HttpRequestPool, method, HTTP_ARGS(HttpRequestPool, method), visibility)
43
44 HTTP_BEGIN_ARGS_AR(HttpRequestPool, __construct, 0, 0)
45 HTTP_ARG_OBJ(HttpRequest, request0, 0)
46 HTTP_ARG_OBJ(HttpRequest, request1, 0)
47 HTTP_ARG_OBJ(HttpRequest, requestN, 0)
48 HTTP_END_ARGS;
49
50 HTTP_EMPTY_ARGS(__destruct, 0);
51 HTTP_EMPTY_ARGS(reset, 0);
52
53 HTTP_BEGIN_ARGS(attach, 1)
54 HTTP_ARG_OBJ(HttpRequest, request, 0)
55 HTTP_END_ARGS;
56
57 HTTP_BEGIN_ARGS(detach, 1)
58 HTTP_ARG_OBJ(HttpRequest, request, 0)
59 HTTP_END_ARGS;
60
61 HTTP_EMPTY_ARGS(send, 0);
62 HTTP_EMPTY_ARGS(socketPerform, 0);
63 HTTP_EMPTY_ARGS(socketSelect, 0);
64
65 HTTP_EMPTY_ARGS(valid, 0);
66 HTTP_EMPTY_ARGS(current, 1);
67 HTTP_EMPTY_ARGS(key, 0);
68 HTTP_EMPTY_ARGS(next, 0);
69 HTTP_EMPTY_ARGS(rewind, 0);
70
71 #define http_requestpool_object_declare_default_properties() _http_requestpool_object_declare_default_properties(TSRMLS_C)
72 static inline void _http_requestpool_object_declare_default_properties(TSRMLS_D);
73
74 zend_class_entry *http_requestpool_object_ce;
75 zend_function_entry http_requestpool_object_fe[] = {
76 HTTP_REQPOOL_ME(__construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
77 HTTP_REQPOOL_ME(__destruct, ZEND_ACC_PUBLIC|ZEND_ACC_DTOR)
78 HTTP_REQPOOL_ME(attach, ZEND_ACC_PUBLIC)
79 HTTP_REQPOOL_ME(detach, ZEND_ACC_PUBLIC)
80 HTTP_REQPOOL_ME(send, ZEND_ACC_PUBLIC)
81 HTTP_REQPOOL_ME(reset, ZEND_ACC_PUBLIC)
82
83 HTTP_REQPOOL_ME(socketPerform, ZEND_ACC_PROTECTED)
84 HTTP_REQPOOL_ME(socketSelect, ZEND_ACC_PROTECTED)
85
86 /* implements Interator */
87 HTTP_REQPOOL_ME(valid, ZEND_ACC_PUBLIC)
88 HTTP_REQPOOL_ME(current, ZEND_ACC_PUBLIC)
89 HTTP_REQPOOL_ME(key, ZEND_ACC_PUBLIC)
90 HTTP_REQPOOL_ME(next, ZEND_ACC_PUBLIC)
91 HTTP_REQPOOL_ME(rewind, ZEND_ACC_PUBLIC)
92
93 EMPTY_FUNCTION_ENTRY
94 };
95 static zend_object_handlers http_requestpool_object_handlers;
96
97 void _http_requestpool_object_init(INIT_FUNC_ARGS)
98 {
99 HTTP_REGISTER_CLASS_EX(HttpRequestPool, http_requestpool_object, NULL, 0);
100 zend_class_implements(http_requestpool_object_ce TSRMLS_CC, 1, zend_ce_iterator);
101 }
102
103 zend_object_value _http_requestpool_object_new(zend_class_entry *ce TSRMLS_DC)
104 {
105 zend_object_value ov;
106 http_requestpool_object *o;
107
108 o = ecalloc(1, sizeof(http_requestpool_object));
109 o->zo.ce = ce;
110
111 http_request_pool_init(&o->pool);
112
113 ALLOC_HASHTABLE(OBJ_PROP(o));
114 zend_hash_init(OBJ_PROP(o), 0, NULL, ZVAL_PTR_DTOR, 0);
115 zend_hash_copy(OBJ_PROP(o), &ce->default_properties, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
116
117 ov.handle = putObject(http_requestpool_object, o);
118 ov.handlers = &http_requestpool_object_handlers;
119
120 return ov;
121 }
122
123 static inline void _http_requestpool_object_declare_default_properties(TSRMLS_D)
124 {
125 zend_class_entry *ce = http_requestpool_object_ce;
126
127 DCL_PROP_N(PROTECTED, pool);
128 }
129
130 void _http_requestpool_object_free(zend_object *object TSRMLS_DC)
131 {
132 http_requestpool_object *o = (http_requestpool_object *) object;
133
134 if (OBJ_PROP(o)) {
135 zend_hash_destroy(OBJ_PROP(o));
136 FREE_HASHTABLE(OBJ_PROP(o));
137 }
138 http_request_pool_dtor(&o->pool);
139 efree(o);
140 }
141
142 /* ### USERLAND ### */
143
144 /* {{{ proto void HttpRequestPool::__construct([HttpRequest request[, ...]])
145 *
146 * Instantiate a new HttpRequestPool object. An HttpRequestPool is
147 * able to send several HttpRequests in parallel.
148 *
149 * WARNING: Don't attach/detach HttpRequest objects to the HttpRequestPool
150 * object while you're using the implemented Interator interface.
151 *
152 * Accepts virtual infinite optional parameters each referencing an
153 * HttpRequest object.
154 *
155 * Throws HttpRequestException, HttpRequestPoolException, HttpInvalidParamException.
156 *
157 * Example:
158 * <pre>
159 * <?php
160 * try {
161 * $pool = new HttpRequestPool(
162 * new HttpRequest('http://www.google.com/', HttpRequest::METH_HEAD),
163 * new HttpRequest('http://www.php.net/', HttpRequest::METH_HEAD)
164 * );
165 * $pool->send();
166 * foreach($pool as $request) {
167 * printf("%s is %s (%d)\n",
168 * $request->getUrl(),
169 * $request->getResponseCode() ? 'alive' : 'not alive',
170 * $request->getResponseCode()
171 * );
172 * }
173 * } catch (HttpException $e) {
174 * echo $e;
175 * }
176 * ?>
177 * </pre>
178 */
179 PHP_METHOD(HttpRequestPool, __construct)
180 {
181 int argc = ZEND_NUM_ARGS();
182 zval ***argv = safe_emalloc(argc, sizeof(zval *), 0);
183 getObject(http_requestpool_object, obj);
184
185 if (SUCCESS == zend_get_parameters_array_ex(argc, argv)) {
186 int i;
187
188 for (i = 0; i < argc; ++i) {
189 if (Z_TYPE_PP(argv[i]) == IS_OBJECT && instanceof_function(Z_OBJCE_PP(argv[i]), http_request_object_ce TSRMLS_CC)) {
190 http_request_pool_attach(&obj->pool, *(argv[i]));
191 }
192 }
193 }
194 efree(argv);
195 }
196 /* }}} */
197
198 /* {{{ proto void HttpRequestPool::__destruct()
199 *
200 * Clean up HttpRequestPool object.
201 */
202 PHP_METHOD(HttpRequestPool, __destruct)
203 {
204 getObject(http_requestpool_object, obj);
205
206 NO_ARGS;
207
208 http_request_pool_detach_all(&obj->pool);
209 }
210 /* }}} */
211
212 /* {{{ proto void HttpRequestPool::reset()
213 *
214 * Detach all attached HttpRequest objects.
215 */
216 PHP_METHOD(HttpRequestPool, reset)
217 {
218 getObject(http_requestpool_object, obj);
219
220 NO_ARGS;
221
222 obj->iterator.pos = 0;
223 http_request_pool_detach_all(&obj->pool);
224 }
225
226 /* {{{ proto bool HttpRequestPool::attach(HttpRequest request)
227 *
228 * Attach an HttpRequest object to this HttpRequestPool.
229 * WARNING: set all options prior attaching!
230 *
231 * Expects the parameter to be an HttpRequest object not alread attached to
232 * antother HttpRequestPool object.
233 *
234 * Returns TRUE on success, or FALSE on failure.
235 *
236 * Throws HttpInvalidParamException, HttpRequestException,
237 * HttpRequestPoolException, HttpEncodingException.
238 */
239 PHP_METHOD(HttpRequestPool, attach)
240 {
241 zval *request;
242 STATUS status = FAILURE;
243 getObject(http_requestpool_object, obj);
244
245 SET_EH_THROW_HTTP();
246 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &request, http_request_object_ce)) {
247 if (obj->iterator.pos > 0 && obj->iterator.pos < zend_llist_count(&obj->pool.handles)) {
248 http_error(HE_THROW, HTTP_E_REQUEST_POOL, "Cannot attach to the HttpRequestPool while the iterator is active");
249 } else {
250 status = http_request_pool_attach(&obj->pool, request);
251 }
252 }
253 SET_EH_NORMAL();
254 RETURN_SUCCESS(status);
255 }
256 /* }}} */
257
258 /* {{{ proto bool HttpRequestPool::detach(HttpRequest request)
259 *
260 * Detach an HttpRequest object from this HttpRequestPool.
261 *
262 * Expects the parameter to be an HttpRequest object attached to this
263 * HttpRequestPool object.
264 *
265 * Returns TRUE on success, or FALSE on failure.
266 *
267 * Throws HttpInvalidParamException, HttpRequestPoolException.
268 */
269 PHP_METHOD(HttpRequestPool, detach)
270 {
271 zval *request;
272 STATUS status = FAILURE;
273 getObject(http_requestpool_object, obj);
274
275 SET_EH_THROW_HTTP();
276 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &request, http_request_object_ce)) {
277 obj->iterator.pos = -1;
278 status = http_request_pool_detach(&obj->pool, request);
279 }
280 SET_EH_NORMAL();
281 RETURN_SUCCESS(status);
282 }
283 /* }}} */
284
285 /* {{{ proto bool HttpRequestPool::send()
286 *
287 * Send all attached HttpRequest objects in parallel.
288 *
289 * Returns TRUE on success, or FALSE on failure.
290 *
291 * Throws HttpSocketException, HttpRequestException,
292 * HttpRequestPoolException, HttpMalformedHeaderException.
293 */
294 PHP_METHOD(HttpRequestPool, send)
295 {
296 STATUS status;
297 getObject(http_requestpool_object, obj);
298
299 NO_ARGS;
300
301 SET_EH_THROW_HTTP();
302 status = http_request_pool_send(&obj->pool);
303 SET_EH_NORMAL();
304
305 RETURN_SUCCESS(status);
306 }
307 /* }}} */
308
309 /* {{{ proto protected bool HttpRequestPool::socketPerform()
310 *
311 * Returns TRUE until each request has finished its transaction.
312 *
313 * Usage:
314 * <pre>
315 * <?php
316 * while ($pool->socketPerform()) {
317 * do_something_else();
318 * if (!$pool->socketSelect()) {
319 * die('Socket error');
320 * }
321 * }
322 * ?>
323 * </pre>
324 */
325 PHP_METHOD(HttpRequestPool, socketPerform)
326 {
327 getObject(http_requestpool_object, obj);
328
329 NO_ARGS;
330
331 if (0 < http_request_pool_perform(&obj->pool)) {
332 RETURN_TRUE;
333 } else {
334 zend_llist_apply(&obj->pool.handles, (llist_apply_func_t) http_request_pool_responsehandler TSRMLS_CC);
335 RETURN_FALSE;
336 }
337 }
338 /* }}} */
339
340 /* {{{ proto protected bool HttpRequestPool::socketSelect()
341 *
342 * See HttpRequestPool::socketPerform().
343 *
344 * Returns TRUE on success, or FALSE on failure.
345 */
346 PHP_METHOD(HttpRequestPool, socketSelect)
347 {
348 getObject(http_requestpool_object, obj);
349
350 NO_ARGS;
351
352 RETURN_SUCCESS(http_request_pool_select(&obj->pool));
353 }
354 /* }}} */
355
356 /* implements Iterator */
357
358 /* {{{ proto bool HttpRequestPool::valid()
359 *
360 * Implements Iterator::valid().
361 */
362 PHP_METHOD(HttpRequestPool, valid)
363 {
364 NO_ARGS;
365
366 IF_RETVAL_USED {
367 getObject(http_requestpool_object, obj);
368 RETURN_BOOL(obj->iterator.pos >= 0 && obj->iterator.pos < zend_llist_count(&obj->pool.handles));
369 }
370 }
371 /* }}} */
372
373 /* {{{ proto HttpRequest HttpRequestPool::current()
374 *
375 * Implements Iterator::current().
376 */
377 PHP_METHOD(HttpRequestPool, current)
378 {
379 NO_ARGS;
380
381 IF_RETVAL_USED {
382 long pos = 0;
383 zval **current = NULL;
384 zend_llist_position lpos;
385 getObject(http_requestpool_object, obj);
386
387 if (obj->iterator.pos < zend_llist_count(&obj->pool.handles)) {
388 for ( current = zend_llist_get_first_ex(&obj->pool.handles, &lpos);
389 current && obj->iterator.pos != pos++;
390 current = zend_llist_get_next_ex(&obj->pool.handles, &lpos));
391 if (current) {
392 RETURN_OBJECT(*current);
393 }
394 }
395 RETURN_NULL();
396 }
397 }
398 /* }}} */
399
400 /* {{{ proto int HttpRequestPool::key()
401 *
402 * Implements Iterator::key().
403 */
404 PHP_METHOD(HttpRequestPool, key)
405 {
406 NO_ARGS;
407
408 IF_RETVAL_USED {
409 getObject(http_requestpool_object, obj);
410 RETURN_LONG(obj->iterator.pos);
411 }
412 }
413 /* }}} */
414
415 /* {{{ proto void HttpRequestPool::next()
416 *
417 * Implements Iterator::next().
418 */
419 PHP_METHOD(HttpRequestPool, next)
420 {
421 NO_ARGS {
422 getObject(http_requestpool_object, obj);
423 ++(obj->iterator.pos);
424 }
425 }
426 /* }}} */
427
428 /* {{{ proto void HttpRequestPool::rewind()
429 *
430 * Implements Iterator::rewind().
431 */
432 PHP_METHOD(HttpRequestPool, rewind)
433 {
434 NO_ARGS {
435 getObject(http_requestpool_object, obj);
436 obj->iterator.pos = 0;
437 }
438 }
439 /* }}} */
440
441 #endif /* ZEND_ENGINE_2 && HTTP_HAVE_CURL */
442
443 /*
444 * Local variables:
445 * tab-width: 4
446 * c-basic-offset: 4
447 * End:
448 * vim600: noet sw=4 ts=4 fdm=marker
449 * vim<600: noet sw=4 ts=4
450 */
451