- win32 build now has libcurl v7.16.0
[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-2006, 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 "zend_interfaces.h"
21
22 #include "php_http_api.h"
23 #include "php_http_exception_object.h"
24 #include "php_http_request_api.h"
25 #include "php_http_request_object.h"
26 #include "php_http_request_pool_api.h"
27 #include "php_http_requestpool_object.h"
28
29 #if defined(HAVE_SPL) && !defined(WONKY)
30 /* SPL doesn't install its headers */
31 extern PHPAPI zend_class_entry *spl_ce_Countable;
32 #endif
33
34 #define HTTP_BEGIN_ARGS(method, req_args) HTTP_BEGIN_ARGS_EX(HttpRequestPool, method, 0, req_args)
35 #define HTTP_EMPTY_ARGS(method) HTTP_EMPTY_ARGS_EX(HttpRequestPool, method, 0)
36 #define HTTP_REQPOOL_ME(method, visibility) PHP_ME(HttpRequestPool, method, HTTP_ARGS(HttpRequestPool, method), visibility)
37
38 HTTP_BEGIN_ARGS(__construct, 0)
39 HTTP_ARG_OBJ(HttpRequest, request0, 0)
40 HTTP_ARG_OBJ(HttpRequest, request1, 0)
41 HTTP_ARG_OBJ(HttpRequest, requestN, 0)
42 HTTP_END_ARGS;
43
44 HTTP_EMPTY_ARGS(__destruct);
45 HTTP_EMPTY_ARGS(reset);
46
47 HTTP_BEGIN_ARGS(attach, 1)
48 HTTP_ARG_OBJ(HttpRequest, request, 0)
49 HTTP_END_ARGS;
50
51 HTTP_BEGIN_ARGS(detach, 1)
52 HTTP_ARG_OBJ(HttpRequest, request, 0)
53 HTTP_END_ARGS;
54
55 HTTP_EMPTY_ARGS(send);
56 HTTP_EMPTY_ARGS(socketPerform);
57 HTTP_EMPTY_ARGS(socketSelect);
58
59 HTTP_EMPTY_ARGS(valid);
60 HTTP_EMPTY_ARGS(current);
61 HTTP_EMPTY_ARGS(key);
62 HTTP_EMPTY_ARGS(next);
63 HTTP_EMPTY_ARGS(rewind);
64
65 HTTP_EMPTY_ARGS(count);
66
67 HTTP_EMPTY_ARGS(getAttachedRequests);
68 HTTP_EMPTY_ARGS(getFinishedRequests);
69
70 HTTP_BEGIN_ARGS(enablePipelining, 0)
71 HTTP_ARG_VAL(enable, 0)
72 HTTP_END_ARGS;
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 Iterator */
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 /* implmenents Countable */
94 HTTP_REQPOOL_ME(count, ZEND_ACC_PUBLIC)
95
96 HTTP_REQPOOL_ME(getAttachedRequests, ZEND_ACC_PUBLIC)
97 HTTP_REQPOOL_ME(getFinishedRequests, ZEND_ACC_PUBLIC)
98
99 HTTP_REQPOOL_ME(enablePipelining, ZEND_ACC_PUBLIC)
100
101 EMPTY_FUNCTION_ENTRY
102 };
103 static zend_object_handlers http_requestpool_object_handlers;
104
105 PHP_MINIT_FUNCTION(http_requestpool_object)
106 {
107 HTTP_REGISTER_CLASS_EX(HttpRequestPool, http_requestpool_object, NULL, 0);
108 http_requestpool_object_handlers.clone_obj = NULL;
109
110 #if defined(HAVE_SPL) && !defined(WONKY)
111 zend_class_implements(http_requestpool_object_ce TSRMLS_CC, 2, spl_ce_Countable, zend_ce_iterator);
112 #else
113 zend_class_implements(http_requestpool_object_ce TSRMLS_CC, 1, zend_ce_iterator);
114 #endif
115
116 return SUCCESS;
117 }
118
119 zend_object_value _http_requestpool_object_new(zend_class_entry *ce TSRMLS_DC)
120 {
121 zend_object_value ov;
122 http_requestpool_object *o;
123
124 o = ecalloc(1, sizeof(http_requestpool_object));
125 o->zo.ce = ce;
126
127 http_request_pool_init(&o->pool);
128
129 ALLOC_HASHTABLE(OBJ_PROP(o));
130 zend_hash_init(OBJ_PROP(o), zend_hash_num_elements(&ce->default_properties), NULL, ZVAL_PTR_DTOR, 0);
131 zend_hash_copy(OBJ_PROP(o), &ce->default_properties, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
132
133 ov.handle = putObject(http_requestpool_object, o);
134 ov.handlers = &http_requestpool_object_handlers;
135
136 return ov;
137 }
138
139 void _http_requestpool_object_free(zend_object *object TSRMLS_DC)
140 {
141 http_requestpool_object *o = (http_requestpool_object *) object;
142
143 http_request_pool_dtor(&o->pool);
144 freeObject(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 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 Iterator interface.
163 *
164 * Accepts virtual infinite optional parameters each referencing an
165 * HttpRequest object.
166 *
167 * Throws HttpRequestPoolException (HttpRequestException, 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 SET_EH_THROW_HTTP();
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 SET_EH_NORMAL();
209 http_final(HTTP_EX_CE(request_pool));
210 }
211 /* }}} */
212
213 /* {{{ proto void HttpRequestPool::__destruct()
214 *
215 * Clean up HttpRequestPool object.
216 */
217 PHP_METHOD(HttpRequestPool, __destruct)
218 {
219 getObject(http_requestpool_object, obj);
220
221 NO_ARGS;
222
223 http_request_pool_detach_all(&obj->pool);
224 }
225 /* }}} */
226
227 /* {{{ proto void HttpRequestPool::reset()
228 *
229 * Detach all attached HttpRequest objects.
230 */
231 PHP_METHOD(HttpRequestPool, reset)
232 {
233 getObject(http_requestpool_object, obj);
234
235 NO_ARGS;
236
237 obj->iterator.pos = 0;
238 http_request_pool_detach_all(&obj->pool);
239 }
240
241 /* {{{ proto bool HttpRequestPool::attach(HttpRequest request)
242 *
243 * Attach an HttpRequest object to this HttpRequestPool.
244 * WARNING: set all options prior attaching!
245 *
246 * Expects the parameter to be an HttpRequest object not already attached to
247 * antother HttpRequestPool object.
248 *
249 * Returns TRUE on success, or FALSE on failure.
250 *
251 * Throws HttpInvalidParamException, HttpRequestException,
252 * HttpRequestPoolException, HttpEncodingException.
253 */
254 PHP_METHOD(HttpRequestPool, attach)
255 {
256 zval *request;
257 STATUS status = FAILURE;
258 getObject(http_requestpool_object, obj);
259
260 SET_EH_THROW_HTTP();
261 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &request, http_request_object_ce)) {
262 if (obj->iterator.pos > 0 && obj->iterator.pos < zend_llist_count(&obj->pool.handles)) {
263 http_error(HE_THROW, HTTP_E_REQUEST_POOL, "Cannot attach to the HttpRequestPool while the iterator is active");
264 } else {
265 status = http_request_pool_attach(&obj->pool, request);
266 }
267 }
268 SET_EH_NORMAL();
269 RETURN_SUCCESS(status);
270 }
271 /* }}} */
272
273 /* {{{ proto bool HttpRequestPool::detach(HttpRequest request)
274 *
275 * Detach an HttpRequest object from this HttpRequestPool.
276 *
277 * Expects the parameter to be an HttpRequest object attached to this
278 * HttpRequestPool object.
279 *
280 * Returns TRUE on success, or FALSE on failure.
281 *
282 * Throws HttpInvalidParamException, HttpRequestPoolException.
283 */
284 PHP_METHOD(HttpRequestPool, detach)
285 {
286 zval *request;
287 STATUS status = FAILURE;
288 getObject(http_requestpool_object, obj);
289
290 SET_EH_THROW_HTTP();
291 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &request, http_request_object_ce)) {
292 obj->iterator.pos = -1;
293 status = http_request_pool_detach(&obj->pool, request);
294 }
295 SET_EH_NORMAL();
296 RETURN_SUCCESS(status);
297 }
298 /* }}} */
299
300 /* {{{ proto bool HttpRequestPool::send()
301 *
302 * Send all attached HttpRequest objects in parallel.
303 *
304 * Returns TRUE on success, or FALSE on failure.
305 *
306 * Throws HttpRequestPoolException (HttpSocketException, HttpRequestException, HttpMalformedHeaderException).
307 */
308 PHP_METHOD(HttpRequestPool, send)
309 {
310 STATUS status;
311 getObject(http_requestpool_object, obj);
312
313 NO_ARGS;
314
315 SET_EH_THROW_HTTP();
316 status = http_request_pool_send(&obj->pool);
317 SET_EH_NORMAL();
318
319 /* rethrow as HttpRequestPoolException */
320 http_final(HTTP_EX_CE(request_pool));
321
322 RETURN_SUCCESS(status);
323 }
324 /* }}} */
325
326 /* {{{ proto protected bool HttpRequestPool::socketPerform()
327 *
328 * Returns TRUE until each request has finished its transaction.
329 *
330 * Usage:
331 * <pre>
332 * <?php
333 * class MyPool extends HttpRequestPool
334 * {
335 * public function send()
336 * {
337 * while ($this->socketPerform()) {
338 * if (!$this->socketSelect()) {
339 * throw new HttpSocketExcpetion;
340 * }
341 * }
342 * }
343 *
344 * protected final function socketPerform()
345 * {
346 * $result = parent::socketPerform();
347 * foreach ($this->getFinishedRequests() as $r) {
348 * $this->detach($r);
349 * // handle response of finished request
350 * }
351 * return $result;
352 * }
353 * }
354 * ?>
355 * </pre>
356 */
357 PHP_METHOD(HttpRequestPool, socketPerform)
358 {
359 getObject(http_requestpool_object, obj);
360
361 NO_ARGS;
362
363 if (0 < http_request_pool_perform(&obj->pool)) {
364 RETURN_TRUE;
365 } else {
366 RETURN_FALSE;
367 }
368 }
369 /* }}} */
370
371 /* {{{ proto protected bool HttpRequestPool::socketSelect()
372 *
373 * See HttpRequestPool::socketPerform().
374 *
375 * Returns TRUE on success, or FALSE on failure.
376 */
377 PHP_METHOD(HttpRequestPool, socketSelect)
378 {
379 getObject(http_requestpool_object, obj);
380
381 NO_ARGS;
382
383 RETURN_SUCCESS(http_request_pool_select(&obj->pool));
384 }
385 /* }}} */
386
387 /* implements Iterator */
388
389 /* {{{ proto bool HttpRequestPool::valid()
390 *
391 * Implements Iterator::valid().
392 */
393 PHP_METHOD(HttpRequestPool, valid)
394 {
395 NO_ARGS;
396
397 if (return_value_used) {
398 getObject(http_requestpool_object, obj);
399 RETURN_BOOL(obj->iterator.pos >= 0 && obj->iterator.pos < zend_llist_count(&obj->pool.handles));
400 }
401 }
402 /* }}} */
403
404 /* {{{ proto HttpRequest HttpRequestPool::current()
405 *
406 * Implements Iterator::current().
407 */
408 PHP_METHOD(HttpRequestPool, current)
409 {
410 NO_ARGS;
411
412 if (return_value_used) {
413 long pos = 0;
414 zval **current = NULL;
415 zend_llist_position lpos;
416 getObject(http_requestpool_object, obj);
417
418 if (obj->iterator.pos < zend_llist_count(&obj->pool.handles)) {
419 for ( current = zend_llist_get_first_ex(&obj->pool.handles, &lpos);
420 current && obj->iterator.pos != pos++;
421 current = zend_llist_get_next_ex(&obj->pool.handles, &lpos));
422 if (current) {
423 RETURN_OBJECT(*current, 1);
424 }
425 }
426 RETURN_NULL();
427 }
428 }
429 /* }}} */
430
431 /* {{{ proto int HttpRequestPool::key()
432 *
433 * Implements Iterator::key().
434 */
435 PHP_METHOD(HttpRequestPool, key)
436 {
437 NO_ARGS;
438
439 if (return_value_used) {
440 getObject(http_requestpool_object, obj);
441 RETURN_LONG(obj->iterator.pos);
442 }
443 }
444 /* }}} */
445
446 /* {{{ proto void HttpRequestPool::next()
447 *
448 * Implements Iterator::next().
449 */
450 PHP_METHOD(HttpRequestPool, next)
451 {
452 NO_ARGS {
453 getObject(http_requestpool_object, obj);
454 ++(obj->iterator.pos);
455 }
456 }
457 /* }}} */
458
459 /* {{{ proto void HttpRequestPool::rewind()
460 *
461 * Implements Iterator::rewind().
462 */
463 PHP_METHOD(HttpRequestPool, rewind)
464 {
465 NO_ARGS {
466 getObject(http_requestpool_object, obj);
467 obj->iterator.pos = 0;
468 }
469 }
470 /* }}} */
471
472 /* {{{ proto int HttpRequestPool::count()
473 *
474 * Implements Countable.
475 *
476 * Returns the number of attached HttpRequest objects.
477 */
478 PHP_METHOD(HttpRequestPool, count)
479 {
480 NO_ARGS {
481 getObject(http_requestpool_object, obj);
482 RETURN_LONG((long) zend_llist_count(&obj->pool.handles));
483 }
484 }
485 /* }}} */
486
487 /* {{{ proto array HttpRequestPool::getAttachedRequests()
488 *
489 * Get attached HttpRequest objects.
490 *
491 * Returns an array containing all currently attached HttpRequest objects.
492 */
493 PHP_METHOD(HttpRequestPool, getAttachedRequests)
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.handles,
501 (llist_apply_with_arg_func_t) http_requestpool_object_llist2array,
502 return_value TSRMLS_CC);
503 }
504 /* }}} */
505
506 /* {{{ proto array HttpRequestPool::getFinishedRequests()
507 *
508 * Get attached HttpRequest objects that already have finished their work.
509 *
510 * Returns an array containing all attached HttpRequest objects that
511 * already have finished their work.
512 */
513 PHP_METHOD(HttpRequestPool, getFinishedRequests)
514 {
515 getObject(http_requestpool_object, obj);
516
517 NO_ARGS;
518
519 array_init(return_value);
520 zend_llist_apply_with_argument(&obj->pool.finished,
521 (llist_apply_with_arg_func_t) http_requestpool_object_llist2array,
522 return_value TSRMLS_CC);
523 }
524 /* }}} */
525
526 /* {{{ proto bool HttpRequest::enablePiplelinig([bool enable = true])
527 *
528 * Enables pipelining support for all attached requests if support in libcurl is given.
529 *
530 * Returns TRUE on success, or FALSE on failure.
531 */
532 PHP_METHOD(HttpRequestPool, enablePipelining)
533 {
534 zend_bool enable = 1;
535 getObject(http_requestpool_object, obj);
536
537 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &enable)) {
538 RETURN_FALSE;
539 }
540 #if defined(HAVE_CURL_MULTI_SETOPT) && HTTP_CURL_VERSION(7,16,0)
541 if (CURLM_OK == curl_multi_setopt(obj->pool.ch, CURLMOPT_PIPELINING, (long) enable)) {
542 RETURN_TRUE;
543 }
544 #endif
545 RETURN_FALSE;
546 }
547 /* }}} */
548
549 #endif /* ZEND_ENGINE_2 && HTTP_HAVE_CURL */
550
551 /*
552 * Local variables:
553 * tab-width: 4
554 * c-basic-offset: 4
555 * End:
556 * vim600: noet sw=4 ts=4 fdm=marker
557 * vim<600: noet sw=4 ts=4
558 */
559