7e3e1b802942d80e7f7cf25bc83cef4f4e7a4928
[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);
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 TSRMLS_CC)) {
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 if (h->ops->exec) {
134 return h->ops->exec(h);
135 }
136
137 return FAILURE;
138 }
139
140 static void detach(void *r, void *h TSRMLS_DC)
141 {
142 ((php_http_request_pool_t *) h)->ops->detach(h, ((php_http_request_object_t *) zend_object_store_get_object(*((zval **) r) TSRMLS_CC))->request);
143 }
144
145 PHP_HTTP_API void php_http_request_pool_reset(php_http_request_pool_t *h)
146 {
147 if (h->ops->reset) {
148 h->ops->reset(h);
149 } else if (h->ops->detach) {
150 TSRMLS_FETCH_FROM_CTX(h->ts);
151
152 zend_llist_apply_with_argument(&h->requests.attached, detach, h TSRMLS_CC);
153 }
154
155 zend_llist_clean(&h->requests.attached);
156 zend_llist_clean(&h->requests.finished);
157 }
158
159 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)
160 {
161 if (h->ops->setopt) {
162 return h->ops->setopt(h, opt, arg);
163 }
164
165 return FAILURE;
166 }
167
168 PHP_HTTP_API void php_http_request_pool_requests(php_http_request_pool_t *h, zval ***attached, zval ***finished)
169 {
170 zval **handle;
171 int i, count;
172
173 if (attached) {
174 if ((count = zend_llist_count(&h->requests.attached))) {
175 *attached = ecalloc(count + 1 /* terminating NULL */, sizeof(zval *));
176
177 for (i = 0, handle = zend_llist_get_first(&h->requests.attached); handle; handle = zend_llist_get_next(&h->requests.attached)) {
178 Z_ADDREF_PP(handle);
179 (*attached)[i++] = *handle;
180 }
181 } else {
182 *attached = NULL;
183 }
184 }
185
186 if (finished) {
187 if ((count = zend_llist_count(&h->requests.finished))) {
188 *finished = ecalloc(count + 1 /* terminating NULL */, sizeof(zval *));
189
190 for (i = 0, handle = zend_llist_get_first(&h->requests.finished); handle; handle = zend_llist_get_next(&h->requests.finished)) {
191 Z_ADDREF_PP(handle);
192 (*finished)[i++] = *handle;
193 }
194 } else {
195 *finished = NULL;
196 }
197 }
198 }
199
200 /*#*/
201
202 #define PHP_HTTP_BEGIN_ARGS(method, req_args) PHP_HTTP_BEGIN_ARGS_EX(HttpRequestPool, method, 0, req_args)
203 #define PHP_HTTP_EMPTY_ARGS(method) PHP_HTTP_EMPTY_ARGS_EX(HttpRequestPool, method, 0)
204 #define PHP_HTTP_REQPOOL_ME(method, visibility) PHP_ME(HttpRequestPool, method, PHP_HTTP_ARGS(HttpRequestPool, method), visibility)
205
206 PHP_HTTP_EMPTY_ARGS(__construct);
207
208 PHP_HTTP_EMPTY_ARGS(__destruct);
209 PHP_HTTP_EMPTY_ARGS(reset);
210
211 PHP_HTTP_BEGIN_ARGS(attach, 1)
212 PHP_HTTP_ARG_OBJ(http\\Request, request, 0)
213 PHP_HTTP_END_ARGS;
214
215 PHP_HTTP_BEGIN_ARGS(detach, 1)
216 PHP_HTTP_ARG_OBJ(http\\Request, request, 0)
217 PHP_HTTP_END_ARGS;
218
219 PHP_HTTP_EMPTY_ARGS(send);
220 PHP_HTTP_EMPTY_ARGS(once);
221 PHP_HTTP_BEGIN_ARGS(wait, 0)
222 PHP_HTTP_ARG_VAL(timeout, 0)
223 PHP_HTTP_END_ARGS;
224
225 PHP_HTTP_EMPTY_ARGS(valid);
226 PHP_HTTP_EMPTY_ARGS(current);
227 PHP_HTTP_EMPTY_ARGS(key);
228 PHP_HTTP_EMPTY_ARGS(next);
229 PHP_HTTP_EMPTY_ARGS(rewind);
230
231 PHP_HTTP_EMPTY_ARGS(count);
232
233 PHP_HTTP_EMPTY_ARGS(getAttachedRequests);
234 PHP_HTTP_EMPTY_ARGS(getFinishedRequests);
235
236 PHP_HTTP_BEGIN_ARGS(enablePipelining, 0)
237 PHP_HTTP_ARG_VAL(enable, 0)
238 PHP_HTTP_END_ARGS;
239
240 PHP_HTTP_BEGIN_ARGS(enableEvents, 0)
241 PHP_HTTP_ARG_VAL(enable, 0)
242 PHP_HTTP_END_ARGS;
243
244 zend_class_entry *php_http_request_pool_class_entry;
245 zend_function_entry php_http_request_pool_method_entry[] = {
246 PHP_HTTP_REQPOOL_ME(__construct, ZEND_ACC_PRIVATE|ZEND_ACC_CTOR)
247 PHP_HTTP_REQPOOL_ME(__destruct, ZEND_ACC_PUBLIC|ZEND_ACC_DTOR)
248 PHP_HTTP_REQPOOL_ME(attach, ZEND_ACC_PUBLIC)
249 PHP_HTTP_REQPOOL_ME(detach, ZEND_ACC_PUBLIC)
250 PHP_HTTP_REQPOOL_ME(send, ZEND_ACC_PUBLIC)
251 PHP_HTTP_REQPOOL_ME(reset, ZEND_ACC_PUBLIC)
252
253 PHP_HTTP_REQPOOL_ME(once, ZEND_ACC_PROTECTED)
254 PHP_HTTP_REQPOOL_ME(wait, ZEND_ACC_PROTECTED)
255
256 /* implements Iterator */
257 PHP_HTTP_REQPOOL_ME(valid, ZEND_ACC_PUBLIC)
258 PHP_HTTP_REQPOOL_ME(current, ZEND_ACC_PUBLIC)
259 PHP_HTTP_REQPOOL_ME(key, ZEND_ACC_PUBLIC)
260 PHP_HTTP_REQPOOL_ME(next, ZEND_ACC_PUBLIC)
261 PHP_HTTP_REQPOOL_ME(rewind, ZEND_ACC_PUBLIC)
262
263 /* implmenents Countable */
264 PHP_HTTP_REQPOOL_ME(count, ZEND_ACC_PUBLIC)
265
266 PHP_HTTP_REQPOOL_ME(getAttachedRequests, ZEND_ACC_PUBLIC)
267 PHP_HTTP_REQPOOL_ME(getFinishedRequests, ZEND_ACC_PUBLIC)
268
269 PHP_HTTP_REQPOOL_ME(enablePipelining, ZEND_ACC_PUBLIC)
270 PHP_HTTP_REQPOOL_ME(enableEvents, ZEND_ACC_PUBLIC)
271
272 EMPTY_FUNCTION_ENTRY
273 };
274 static zend_object_handlers php_http_request_pool_object_handlers;
275
276 zend_object_value php_http_request_pool_object_new(zend_class_entry *ce TSRMLS_DC)
277 {
278 return php_http_request_pool_object_new_ex(ce, NULL, NULL TSRMLS_CC);
279 }
280
281 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)
282 {
283 zend_object_value ov;
284 php_http_request_pool_object_t *o;
285
286 o = ecalloc(1, sizeof(php_http_request_pool_object_t));
287 zend_object_std_init((zend_object *) o, ce TSRMLS_CC);
288 object_properties_init((zend_object *) o, ce);
289
290 if (!(o->pool = p)) {
291 o->pool = php_http_request_pool_init(NULL, NULL, NULL, NULL TSRMLS_CC);
292 }
293
294 if (ptr) {
295 *ptr = o;
296 }
297
298 ov.handle = zend_objects_store_put(o, NULL, php_http_request_pool_object_free, NULL TSRMLS_CC);
299 ov.handlers = &php_http_request_pool_object_handlers;
300
301 return ov;
302 }
303
304 void php_http_request_pool_object_free(void *object TSRMLS_DC)
305 {
306 php_http_request_pool_object_t *o = (php_http_request_pool_object_t *) object;
307
308 php_http_request_pool_free(&o->pool);
309 zend_object_std_dtor((zend_object *) o TSRMLS_CC);
310 efree(o);
311 }
312
313 static void php_http_request_pool_object_llist2array(zval **req, zval *array TSRMLS_DC)
314 {
315 Z_ADDREF_P(*req);
316 add_next_index_zval(array, *req);
317 }
318
319
320 PHP_METHOD(HttpRequestPool, __construct)
321 {
322 with_error_handling(EH_THROW, php_http_exception_class_entry) {
323 zend_parse_parameters_none();
324 } end_error_handling();
325 }
326
327 PHP_METHOD(HttpRequestPool, __destruct)
328 {
329 php_http_request_pool_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
330
331 if (SUCCESS != zend_parse_parameters_none()) {
332 ; /* we always want to clean up */
333 }
334
335 php_http_request_pool_reset(obj->pool);
336 }
337
338 PHP_METHOD(HttpRequestPool, reset)
339 {
340 if (SUCCESS == zend_parse_parameters_none()) {
341 php_http_request_pool_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
342
343 obj->iterator.pos = 0;
344 php_http_request_pool_reset(obj->pool);
345 }
346 RETVAL_ZVAL(getThis(), 1, 0);
347 }
348
349 PHP_METHOD(HttpRequestPool, attach)
350 {
351 with_error_handling(EH_THROW, php_http_exception_class_entry) {
352 zval *request;
353
354 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &request, php_http_request_class_entry)) {
355 with_error_handling(EH_THROW, php_http_exception_class_entry) {
356 php_http_request_pool_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
357
358 if (obj->iterator.pos > 0 && obj->iterator.pos < zend_llist_count(&obj->pool->requests.attached)) {
359 php_http_error(HE_THROW, PHP_HTTP_E_REQUEST_POOL, "Cannot attach to the HttpRequestPool while the iterator is active");
360 } else {
361 php_http_request_pool_attach(obj->pool, request);
362 }
363 } end_error_handling();
364 }
365 } end_error_handling();
366
367 RETVAL_ZVAL(getThis(), 1, 0);
368 }
369
370 PHP_METHOD(HttpRequestPool, detach)
371 {
372 RETVAL_FALSE;
373
374 with_error_handling(EH_THROW, php_http_exception_class_entry) {
375 zval *request;
376
377 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &request, php_http_request_class_entry)) {
378 with_error_handling(EH_THROW, php_http_exception_class_entry) {
379 php_http_request_pool_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
380
381 obj->iterator.pos = -1;
382 php_http_request_pool_detach(obj->pool, request);
383 } end_error_handling();
384 }
385 } end_error_handling();
386
387 RETVAL_ZVAL(getThis(), 1, 0);
388 }
389
390 PHP_METHOD(HttpRequestPool, send)
391 {
392 RETVAL_FALSE;
393
394 with_error_handling(EH_THROW, php_http_exception_class_entry) {
395 if (SUCCESS == zend_parse_parameters_none()) {
396 with_error_handling(EH_THROW, php_http_exception_class_entry) {
397 php_http_request_pool_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
398
399 php_http_request_pool_exec(obj->pool);
400 } end_error_handling();
401 }
402 } end_error_handling();
403
404 RETVAL_ZVAL(getThis(), 1, 0);
405 }
406
407 PHP_METHOD(HttpRequestPool, once)
408 {
409 if (SUCCESS == zend_parse_parameters_none()) {
410 php_http_request_pool_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
411
412 if (0 < php_http_request_pool_once(obj->pool)) {
413 RETURN_TRUE;
414 }
415 }
416 RETURN_FALSE;
417 }
418
419 PHP_METHOD(HttpRequestPool, wait)
420 {
421 double timeout = 0;
422
423 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|d", &timeout)) {
424 struct timeval timeout_val;
425 php_http_request_pool_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
426
427 timeout_val.tv_sec = (time_t) timeout;
428 timeout_val.tv_usec = PHP_HTTP_USEC(timeout) % PHP_HTTP_MCROSEC;
429
430 RETURN_SUCCESS(php_http_request_pool_wait(obj->pool, timeout ? &timeout_val : NULL));
431 }
432 RETURN_FALSE;
433 }
434
435 PHP_METHOD(HttpRequestPool, valid)
436 {
437 if (SUCCESS == zend_parse_parameters_none()) {
438 php_http_request_pool_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
439
440 RETURN_BOOL(obj->iterator.pos >= 0 && obj->iterator.pos < zend_llist_count(&obj->pool->requests.attached));
441 }
442 RETURN_FALSE;
443 }
444
445 PHP_METHOD(HttpRequestPool, current)
446 {
447 if (SUCCESS == zend_parse_parameters_none()) {
448 php_http_request_pool_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
449
450 if (obj->iterator.pos < zend_llist_count(&obj->pool->requests.attached)) {
451 long pos = 0;
452 zval **current = NULL;
453 zend_llist_position lpos;
454
455 for ( current = zend_llist_get_first_ex(&obj->pool->requests.attached, &lpos);
456 current && obj->iterator.pos != pos++;
457 current = zend_llist_get_next_ex(&obj->pool->requests.attached, &lpos));
458 if (current) {
459 RETURN_OBJECT(*current, 1);
460 }
461 }
462 }
463 RETURN_FALSE;
464 }
465
466 PHP_METHOD(HttpRequestPool, key)
467 {
468 if (SUCCESS == zend_parse_parameters_none()) {
469 php_http_request_pool_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
470
471 RETURN_LONG(obj->iterator.pos);
472 }
473 RETURN_FALSE;
474 }
475
476 PHP_METHOD(HttpRequestPool, next)
477 {
478 if (SUCCESS == zend_parse_parameters_none()) {
479 php_http_request_pool_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
480
481 ++obj->iterator.pos;
482 }
483 }
484
485 PHP_METHOD(HttpRequestPool, rewind)
486 {
487 if (SUCCESS == zend_parse_parameters_none()) {
488 php_http_request_pool_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
489
490 obj->iterator.pos = 0;
491 }
492 }
493
494 PHP_METHOD(HttpRequestPool, count)
495 {
496 if (SUCCESS == zend_parse_parameters_none()) {
497 php_http_request_pool_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
498
499 RETURN_LONG((long) zend_llist_count(&obj->pool->requests.attached));
500 }
501 }
502
503 PHP_METHOD(HttpRequestPool, getAttachedRequests)
504 {
505 if (SUCCESS == zend_parse_parameters_none()) {
506 php_http_request_pool_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
507
508 array_init(return_value);
509 zend_llist_apply_with_argument(&obj->pool->requests.attached,
510 (llist_apply_with_arg_func_t) php_http_request_pool_object_llist2array,
511 return_value TSRMLS_CC);
512 return;
513 }
514 RETURN_FALSE;
515 }
516
517 PHP_METHOD(HttpRequestPool, getFinishedRequests)
518 {
519 if (SUCCESS == zend_parse_parameters_none()) {
520 php_http_request_pool_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
521
522 array_init(return_value);
523 zend_llist_apply_with_argument(&obj->pool->requests.finished,
524 (llist_apply_with_arg_func_t) php_http_request_pool_object_llist2array,
525 return_value TSRMLS_CC);
526 return;
527 }
528 RETURN_FALSE;
529 }
530
531 PHP_METHOD(HttpRequestPool, enablePipelining)
532 {
533 zend_bool enable = 1;
534
535 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &enable)) {
536 php_http_request_pool_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
537
538 php_http_request_pool_setopt(obj->pool, PHP_HTTP_REQUEST_POOL_OPT_ENABLE_PIPELINING, &enable);
539 }
540 RETVAL_ZVAL(getThis(), 1, 0);
541 }
542
543 PHP_METHOD(HttpRequestPool, enableEvents)
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_USE_EVENTS, &enable);
551 }
552 RETVAL_ZVAL(getThis(), 1, 0);
553 }
554
555 PHP_MINIT_FUNCTION(http_request_pool)
556 {
557 PHP_HTTP_REGISTER_CLASS(http\\Request, Pool, http_request_pool, php_http_object_class_entry, 0);
558 php_http_request_pool_class_entry->create_object = php_http_request_pool_object_new;
559 memcpy(&php_http_request_pool_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
560 php_http_request_pool_object_handlers.clone_obj = NULL;
561
562 zend_class_implements(php_http_request_pool_class_entry TSRMLS_CC, 3, spl_ce_Countable, zend_ce_iterator, php_http_fluently_callable_class_entry);
563
564 return SUCCESS;
565 }
566