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