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