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