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