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