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