let the ctor honor the options array too
[m6w6/ext-http] / php_http_client_pool.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-2011, Michael Wallner <mike@php.net> |
10 +--------------------------------------------------------------------+
11 */
12
13 #include "php_http_api.h"
14
15 PHP_HTTP_API php_http_client_pool_t *php_http_client_pool_init(php_http_client_pool_t *h, php_http_client_pool_ops_t *ops, php_http_resource_factory_t *rf, void *init_arg TSRMLS_DC)
16 {
17 php_http_client_pool_t *free_h = NULL;
18
19 if (!h) {
20 free_h = h = emalloc(sizeof(*h));
21 }
22 memset(h, 0, sizeof(*h));
23
24 h->ops = ops;
25 h->rf = rf ? rf : php_http_resource_factory_init(NULL, h->ops->rsrc, NULL, NULL);
26 zend_llist_init(&h->clients.attached, sizeof(zval *), (llist_dtor_func_t) ZVAL_PTR_DTOR, 0);
27 zend_llist_init(&h->clients.finished, sizeof(zval *), (llist_dtor_func_t) ZVAL_PTR_DTOR, 0);
28 TSRMLS_SET_CTX(h->ts);
29
30 if (h->ops->init) {
31 if (!(h = h->ops->init(h, init_arg))) {
32 php_http_error(HE_WARNING, PHP_HTTP_E_CLIENT_POOL, "Could not initialize request pool");
33 if (free_h) {
34 efree(h);
35 }
36 }
37 }
38
39 return h;
40 }
41
42 PHP_HTTP_API php_http_client_pool_t *php_http_client_pool_copy(php_http_client_pool_t *from, php_http_client_pool_t *to)
43 {
44 if (from->ops->copy) {
45 return from->ops->copy(from, to);
46 }
47
48 return NULL;
49 }
50
51 PHP_HTTP_API void php_http_client_pool_dtor(php_http_client_pool_t *h)
52 {
53 if (h->ops->dtor) {
54 h->ops->dtor(h);
55 }
56
57 zend_llist_clean(&h->clients.finished);
58 zend_llist_clean(&h->clients.attached);
59
60 php_http_resource_factory_free(&h->rf);
61 }
62
63 PHP_HTTP_API void php_http_client_pool_free(php_http_client_pool_t **h) {
64 if (*h) {
65 php_http_client_pool_dtor(*h);
66 efree(*h);
67 *h = NULL;
68 }
69 }
70
71 PHP_HTTP_API STATUS php_http_client_pool_attach(php_http_client_pool_t *h, zval *client)
72 {
73 TSRMLS_FETCH_FROM_CTX(h->ts);
74
75 if (h->ops->attach) {
76 char *url = NULL;
77 char *m = NULL;
78 php_http_message_body_t *body = NULL;
79 php_http_client_object_t *obj = zend_object_store_get_object(client TSRMLS_CC);
80
81 if (SUCCESS != php_http_client_object_requesthandler(obj, client, &m, &url, &body TSRMLS_CC)) {
82 return FAILURE;
83 }
84 if (SUCCESS == h->ops->attach(h, obj->client, m, url, body)) {
85 STR_FREE(url);
86 Z_ADDREF_P(client);
87 zend_llist_add_element(&h->clients.attached, &client);
88 return SUCCESS;
89 }
90 STR_FREE(url);
91 }
92
93 return FAILURE;
94 }
95
96 static int php_http_client_pool_compare_handles(void *h1, void *h2)
97 {
98 return (Z_OBJ_HANDLE_PP((zval **) h1) == Z_OBJ_HANDLE_P((zval *) h2));
99 }
100
101
102 PHP_HTTP_API STATUS php_http_client_pool_detach(php_http_client_pool_t *h, zval *client)
103 {
104 TSRMLS_FETCH_FROM_CTX(h->ts);
105
106 if (h->ops->detach) {
107 php_http_client_object_t *obj = zend_object_store_get_object(client TSRMLS_CC);
108
109 if (SUCCESS == h->ops->detach(h, obj->client)) {
110 zend_llist_del_element(&h->clients.finished, client, php_http_client_pool_compare_handles);
111 zend_llist_del_element(&h->clients.attached, client, php_http_client_pool_compare_handles);
112 return SUCCESS;
113 }
114 }
115
116 return FAILURE;
117 }
118
119 PHP_HTTP_API STATUS php_http_client_pool_wait(php_http_client_pool_t *h, struct timeval *custom_timeout)
120 {
121 if (h->ops->wait) {
122 return h->ops->wait(h, custom_timeout);
123 }
124
125 return FAILURE;
126 }
127
128 PHP_HTTP_API int php_http_client_pool_once(php_http_client_pool_t *h)
129 {
130 if (h->ops->once) {
131 return h->ops->once(h);
132 }
133
134 return FAILURE;
135 }
136
137 PHP_HTTP_API STATUS php_http_client_pool_exec(php_http_client_pool_t *h)
138 {
139 if (h->ops->exec) {
140 return h->ops->exec(h);
141 }
142
143 return FAILURE;
144 }
145
146 static void detach(void *r, void *h TSRMLS_DC)
147 {
148 ((php_http_client_pool_t *) h)->ops->detach(h, ((php_http_client_object_t *) zend_object_store_get_object(*((zval **) r) TSRMLS_CC))->client);
149 }
150
151 PHP_HTTP_API void php_http_client_pool_reset(php_http_client_pool_t *h)
152 {
153 if (h->ops->reset) {
154 h->ops->reset(h);
155 } else if (h->ops->detach) {
156 TSRMLS_FETCH_FROM_CTX(h->ts);
157
158 zend_llist_apply_with_argument(&h->clients.attached, detach, h TSRMLS_CC);
159 }
160
161 zend_llist_clean(&h->clients.attached);
162 zend_llist_clean(&h->clients.finished);
163 }
164
165 PHP_HTTP_API STATUS php_http_client_pool_setopt(php_http_client_pool_t *h, php_http_client_pool_setopt_opt_t opt, void *arg)
166 {
167 if (h->ops->setopt) {
168 return h->ops->setopt(h, opt, arg);
169 }
170
171 return FAILURE;
172 }
173
174 PHP_HTTP_API void php_http_client_pool_requests(php_http_client_pool_t *h, zval ***attached, zval ***finished)
175 {
176 zval **handle;
177 int i, count;
178
179 if (attached) {
180 if ((count = zend_llist_count(&h->clients.attached))) {
181 *attached = ecalloc(count + 1 /* terminating NULL */, sizeof(zval *));
182
183 for (i = 0, handle = zend_llist_get_first(&h->clients.attached); handle; handle = zend_llist_get_next(&h->clients.attached)) {
184 Z_ADDREF_PP(handle);
185 (*attached)[i++] = *handle;
186 }
187 } else {
188 *attached = NULL;
189 }
190 }
191
192 if (finished) {
193 if ((count = zend_llist_count(&h->clients.finished))) {
194 *finished = ecalloc(count + 1 /* terminating NULL */, sizeof(zval *));
195
196 for (i = 0, handle = zend_llist_get_first(&h->clients.finished); handle; handle = zend_llist_get_next(&h->clients.finished)) {
197 Z_ADDREF_PP(handle);
198 (*finished)[i++] = *handle;
199 }
200 } else {
201 *finished = NULL;
202 }
203 }
204 }
205
206 /*#*/
207
208 #define PHP_HTTP_BEGIN_ARGS(method, req_args) PHP_HTTP_BEGIN_ARGS_EX(HttpClientPool, method, 0, req_args)
209 #define PHP_HTTP_EMPTY_ARGS(method) PHP_HTTP_EMPTY_ARGS_EX(HttpClientPool, method, 0)
210 #define PHP_HTTP_REQPOOL_ME(method, visibility) PHP_ME(HttpClientPool, method, PHP_HTTP_ARGS(HttpClientPool, method), visibility)
211
212 PHP_HTTP_EMPTY_ARGS(__destruct);
213 PHP_HTTP_EMPTY_ARGS(reset);
214
215 PHP_HTTP_BEGIN_ARGS(attach, 1)
216 PHP_HTTP_ARG_OBJ(http\\Client, client, 0)
217 PHP_HTTP_END_ARGS;
218
219 PHP_HTTP_BEGIN_ARGS(detach, 1)
220 PHP_HTTP_ARG_OBJ(http\\Client, client, 0)
221 PHP_HTTP_END_ARGS;
222
223 PHP_HTTP_EMPTY_ARGS(send);
224 PHP_HTTP_EMPTY_ARGS(once);
225 PHP_HTTP_BEGIN_ARGS(wait, 0)
226 PHP_HTTP_ARG_VAL(timeout, 0)
227 PHP_HTTP_END_ARGS;
228
229 PHP_HTTP_EMPTY_ARGS(valid);
230 PHP_HTTP_EMPTY_ARGS(current);
231 PHP_HTTP_EMPTY_ARGS(key);
232 PHP_HTTP_EMPTY_ARGS(next);
233 PHP_HTTP_EMPTY_ARGS(rewind);
234
235 PHP_HTTP_EMPTY_ARGS(count);
236
237 PHP_HTTP_EMPTY_ARGS(getAttachedRequests);
238 PHP_HTTP_EMPTY_ARGS(getFinishedRequests);
239
240 PHP_HTTP_BEGIN_ARGS(enablePipelining, 0)
241 PHP_HTTP_ARG_VAL(enable, 0)
242 PHP_HTTP_END_ARGS;
243
244 PHP_HTTP_BEGIN_ARGS(enableEvents, 0)
245 PHP_HTTP_ARG_VAL(enable, 0)
246 PHP_HTTP_END_ARGS;
247
248 zend_class_entry *php_http_client_pool_class_entry;
249 zend_function_entry php_http_client_pool_method_entry[] = {
250 PHP_HTTP_REQPOOL_ME(__destruct, ZEND_ACC_PUBLIC|ZEND_ACC_DTOR)
251 PHP_HTTP_REQPOOL_ME(attach, ZEND_ACC_PUBLIC)
252 PHP_HTTP_REQPOOL_ME(detach, ZEND_ACC_PUBLIC)
253 PHP_HTTP_REQPOOL_ME(send, ZEND_ACC_PUBLIC)
254 PHP_HTTP_REQPOOL_ME(reset, ZEND_ACC_PUBLIC)
255
256 PHP_HTTP_REQPOOL_ME(once, ZEND_ACC_PROTECTED)
257 PHP_HTTP_REQPOOL_ME(wait, ZEND_ACC_PROTECTED)
258
259 /* implements Iterator */
260 PHP_HTTP_REQPOOL_ME(valid, ZEND_ACC_PUBLIC)
261 PHP_HTTP_REQPOOL_ME(current, ZEND_ACC_PUBLIC)
262 PHP_HTTP_REQPOOL_ME(key, ZEND_ACC_PUBLIC)
263 PHP_HTTP_REQPOOL_ME(next, ZEND_ACC_PUBLIC)
264 PHP_HTTP_REQPOOL_ME(rewind, ZEND_ACC_PUBLIC)
265
266 /* implmenents Countable */
267 PHP_HTTP_REQPOOL_ME(count, ZEND_ACC_PUBLIC)
268
269 PHP_HTTP_REQPOOL_ME(getAttachedRequests, ZEND_ACC_PUBLIC)
270 PHP_HTTP_REQPOOL_ME(getFinishedRequests, ZEND_ACC_PUBLIC)
271
272 PHP_HTTP_REQPOOL_ME(enablePipelining, ZEND_ACC_PUBLIC)
273 PHP_HTTP_REQPOOL_ME(enableEvents, ZEND_ACC_PUBLIC)
274
275 EMPTY_FUNCTION_ENTRY
276 };
277
278 static zend_object_handlers php_http_client_pool_object_handlers;
279
280 extern zend_object_handlers *php_http_client_pool_get_object_handlers(void)
281 {
282 return &php_http_client_pool_object_handlers;
283 }
284
285 zend_object_value php_http_client_pool_object_new(zend_class_entry *ce TSRMLS_DC)
286 {
287 return php_http_client_pool_object_new_ex(ce, NULL, NULL TSRMLS_CC);
288 }
289
290 zend_object_value php_http_client_pool_object_new_ex(zend_class_entry *ce, php_http_client_pool_t *p, php_http_client_pool_object_t **ptr TSRMLS_DC)
291 {
292 zend_object_value ov;
293 php_http_client_pool_object_t *o;
294
295 o = ecalloc(1, sizeof(php_http_client_pool_object_t));
296 zend_object_std_init((zend_object *) o, ce TSRMLS_CC);
297 object_properties_init((zend_object *) o, ce);
298
299 if (!(o->pool = p)) {
300 o->pool = php_http_client_pool_init(NULL, NULL, NULL, NULL TSRMLS_CC);
301 }
302
303 if (ptr) {
304 *ptr = o;
305 }
306
307 ov.handle = zend_objects_store_put(o, NULL, php_http_client_pool_object_free, NULL TSRMLS_CC);
308 ov.handlers = &php_http_client_pool_object_handlers;
309
310 return ov;
311 }
312
313 void php_http_client_pool_object_free(void *object TSRMLS_DC)
314 {
315 php_http_client_pool_object_t *o = (php_http_client_pool_object_t *) object;
316
317 php_http_client_pool_free(&o->pool);
318 zend_object_std_dtor((zend_object *) o TSRMLS_CC);
319 efree(o);
320 }
321
322 static void php_http_client_pool_object_llist2array(zval **req, zval *array TSRMLS_DC)
323 {
324 Z_ADDREF_P(*req);
325 add_next_index_zval(array, *req);
326 }
327
328 PHP_METHOD(HttpClientPool, __destruct)
329 {
330 php_http_client_pool_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
331
332 if (SUCCESS != zend_parse_parameters_none()) {
333 ; /* we always want to clean up */
334 }
335 /* FIXME: move to php_http_client_pool_dtor */
336 php_http_client_pool_reset(obj->pool);
337 }
338
339 PHP_METHOD(HttpClientPool, reset)
340 {
341 if (SUCCESS == zend_parse_parameters_none()) {
342 php_http_client_pool_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
343
344 obj->iterator.pos = 0;
345 php_http_client_pool_reset(obj->pool);
346 }
347 RETVAL_ZVAL(getThis(), 1, 0);
348 }
349
350 PHP_METHOD(HttpClientPool, attach)
351 {
352 with_error_handling(EH_THROW, php_http_exception_class_entry) {
353 zval *request;
354
355 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &request, php_http_client_class_entry)) {
356 with_error_handling(EH_THROW, php_http_exception_class_entry) {
357 php_http_client_pool_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
358
359 if (obj->iterator.pos > 0 && obj->iterator.pos < zend_llist_count(&obj->pool->clients.attached)) {
360 php_http_error(HE_THROW, PHP_HTTP_E_CLIENT_POOL, "Cannot attach to the HttpClientPool while the iterator is active");
361 } else {
362 php_http_client_pool_attach(obj->pool, request);
363 }
364 } end_error_handling();
365 }
366 } end_error_handling();
367
368 RETVAL_ZVAL(getThis(), 1, 0);
369 }
370
371 PHP_METHOD(HttpClientPool, detach)
372 {
373 RETVAL_FALSE;
374
375 with_error_handling(EH_THROW, php_http_exception_class_entry) {
376 zval *request;
377
378 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &request, php_http_client_class_entry)) {
379 with_error_handling(EH_THROW, php_http_exception_class_entry) {
380 php_http_client_pool_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
381
382 obj->iterator.pos = -1;
383 php_http_client_pool_detach(obj->pool, request);
384 } end_error_handling();
385 }
386 } end_error_handling();
387
388 RETVAL_ZVAL(getThis(), 1, 0);
389 }
390
391 PHP_METHOD(HttpClientPool, send)
392 {
393 RETVAL_FALSE;
394
395 with_error_handling(EH_THROW, php_http_exception_class_entry) {
396 if (SUCCESS == zend_parse_parameters_none()) {
397 with_error_handling(EH_THROW, php_http_exception_class_entry) {
398 php_http_client_pool_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
399
400 php_http_client_pool_exec(obj->pool);
401 } end_error_handling();
402 }
403 } end_error_handling();
404
405 RETVAL_ZVAL(getThis(), 1, 0);
406 }
407
408 PHP_METHOD(HttpClientPool, once)
409 {
410 if (SUCCESS == zend_parse_parameters_none()) {
411 php_http_client_pool_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
412
413 if (0 < php_http_client_pool_once(obj->pool)) {
414 RETURN_TRUE;
415 }
416 }
417 RETURN_FALSE;
418 }
419
420 PHP_METHOD(HttpClientPool, wait)
421 {
422 double timeout = 0;
423
424 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|d", &timeout)) {
425 struct timeval timeout_val;
426 php_http_client_pool_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
427
428 timeout_val.tv_sec = (time_t) timeout;
429 timeout_val.tv_usec = PHP_HTTP_USEC(timeout) % PHP_HTTP_MCROSEC;
430
431 RETURN_SUCCESS(php_http_client_pool_wait(obj->pool, timeout > 0 ? &timeout_val : NULL));
432 }
433 RETURN_FALSE;
434 }
435
436 PHP_METHOD(HttpClientPool, valid)
437 {
438 if (SUCCESS == zend_parse_parameters_none()) {
439 php_http_client_pool_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
440
441 RETURN_BOOL(obj->iterator.pos >= 0 && obj->iterator.pos < zend_llist_count(&obj->pool->clients.attached));
442 }
443 RETURN_FALSE;
444 }
445
446 PHP_METHOD(HttpClientPool, current)
447 {
448 if (SUCCESS == zend_parse_parameters_none()) {
449 php_http_client_pool_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
450
451 if (obj->iterator.pos < zend_llist_count(&obj->pool->clients.attached)) {
452 long pos = 0;
453 zval **current = NULL;
454 zend_llist_position lpos;
455
456 for ( current = zend_llist_get_first_ex(&obj->pool->clients.attached, &lpos);
457 current && obj->iterator.pos != pos++;
458 current = zend_llist_get_next_ex(&obj->pool->clients.attached, &lpos));
459 if (current) {
460 RETURN_OBJECT(*current, 1);
461 }
462 }
463 }
464 RETURN_FALSE;
465 }
466
467 PHP_METHOD(HttpClientPool, key)
468 {
469 if (SUCCESS == zend_parse_parameters_none()) {
470 php_http_client_pool_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
471
472 RETURN_LONG(obj->iterator.pos);
473 }
474 RETURN_FALSE;
475 }
476
477 PHP_METHOD(HttpClientPool, next)
478 {
479 if (SUCCESS == zend_parse_parameters_none()) {
480 php_http_client_pool_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
481
482 ++obj->iterator.pos;
483 }
484 }
485
486 PHP_METHOD(HttpClientPool, rewind)
487 {
488 if (SUCCESS == zend_parse_parameters_none()) {
489 php_http_client_pool_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
490
491 obj->iterator.pos = 0;
492 }
493 }
494
495 PHP_METHOD(HttpClientPool, count)
496 {
497 if (SUCCESS == zend_parse_parameters_none()) {
498 php_http_client_pool_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
499
500 RETURN_LONG((long) zend_llist_count(&obj->pool->clients.attached));
501 }
502 }
503
504 PHP_METHOD(HttpClientPool, getAttachedRequests)
505 {
506 if (SUCCESS == zend_parse_parameters_none()) {
507 php_http_client_pool_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
508
509 array_init(return_value);
510 zend_llist_apply_with_argument(&obj->pool->clients.attached,
511 (llist_apply_with_arg_func_t) php_http_client_pool_object_llist2array,
512 return_value TSRMLS_CC);
513 return;
514 }
515 RETURN_FALSE;
516 }
517
518 PHP_METHOD(HttpClientPool, getFinishedRequests)
519 {
520 if (SUCCESS == zend_parse_parameters_none()) {
521 php_http_client_pool_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
522
523 array_init(return_value);
524 zend_llist_apply_with_argument(&obj->pool->clients.finished,
525 (llist_apply_with_arg_func_t) php_http_client_pool_object_llist2array,
526 return_value TSRMLS_CC);
527 return;
528 }
529 RETURN_FALSE;
530 }
531
532 PHP_METHOD(HttpClientPool, enablePipelining)
533 {
534 zend_bool enable = 1;
535
536 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &enable)) {
537 php_http_client_pool_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
538
539 php_http_client_pool_setopt(obj->pool, PHP_HTTP_CLIENT_POOL_OPT_ENABLE_PIPELINING, &enable);
540 }
541 RETVAL_ZVAL(getThis(), 1, 0);
542 }
543
544 PHP_METHOD(HttpClientPool, enableEvents)
545 {
546 zend_bool enable = 1;
547
548 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &enable)) {
549 php_http_client_pool_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
550
551 php_http_client_pool_setopt(obj->pool, PHP_HTTP_CLIENT_POOL_OPT_USE_EVENTS, &enable);
552 }
553 RETVAL_ZVAL(getThis(), 1, 0);
554 }
555
556 PHP_MINIT_FUNCTION(http_client_pool)
557 {
558 PHP_HTTP_REGISTER_CLASS(http\\Client\\Pool, AbstractPool, http_client_pool, php_http_object_class_entry, 0);
559 php_http_client_pool_class_entry->create_object = php_http_object_new; //php_http_client_pool_object_new;
560 memcpy(&php_http_client_pool_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
561 php_http_client_pool_object_handlers.clone_obj = NULL;
562
563 zend_class_implements(php_http_client_pool_class_entry TSRMLS_CC, 2, spl_ce_Countable, zend_ce_iterator);
564
565 return SUCCESS;
566 }
567
568
569 /*
570 * Local variables:
571 * tab-width: 4
572 * c-basic-offset: 4
573 * End:
574 * vim600: noet sw=4 ts=4 fdm=marker
575 * vim<600: noet sw=4 ts=4
576 */
577