43941bb84faee94c40b705663d243d444ee8004d
[m6w6/ext-http] / http_requestpool_object.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-2010, Michael Wallner <mike@php.net> |
10 +--------------------------------------------------------------------+
11 */
12
13 /* $Id$ */
14
15 #define HTTP_WANT_CURL
16 #include "php_http.h"
17
18 #if defined(ZEND_ENGINE_2) && defined(HTTP_HAVE_CURL)
19
20 #include "zend_interfaces.h"
21
22 #include "php_http_api.h"
23 #include "php_http_exception_object.h"
24 #include "php_http_request_api.h"
25 #include "php_http_request_object.h"
26 #include "php_http_request_pool_api.h"
27 #include "php_http_requestpool_object.h"
28
29 #if defined(HAVE_SPL) && !defined(WONKY)
30 /* SPL doesn't install its headers */
31 extern PHPAPI zend_class_entry *spl_ce_Countable;
32 #endif
33
34 #define HTTP_BEGIN_ARGS(method, req_args) HTTP_BEGIN_ARGS_EX(HttpRequestPool, method, 0, req_args)
35 #define HTTP_EMPTY_ARGS(method) HTTP_EMPTY_ARGS_EX(HttpRequestPool, method, 0)
36 #define HTTP_REQPOOL_ME(method, visibility) PHP_ME(HttpRequestPool, method, HTTP_ARGS(HttpRequestPool, method), visibility)
37
38 HTTP_EMPTY_ARGS(__construct);
39
40 HTTP_EMPTY_ARGS(__destruct);
41 HTTP_EMPTY_ARGS(reset);
42
43 HTTP_BEGIN_ARGS(attach, 1)
44 HTTP_ARG_OBJ(HttpRequest, request, 0)
45 HTTP_END_ARGS;
46
47 HTTP_BEGIN_ARGS(detach, 1)
48 HTTP_ARG_OBJ(HttpRequest, request, 0)
49 HTTP_END_ARGS;
50
51 HTTP_EMPTY_ARGS(send);
52 HTTP_EMPTY_ARGS(socketPerform);
53 HTTP_BEGIN_ARGS(socketSelect, 0)
54 HTTP_ARG_VAL(timeout, 0)
55 HTTP_END_ARGS;
56
57 HTTP_EMPTY_ARGS(valid);
58 HTTP_EMPTY_ARGS(current);
59 HTTP_EMPTY_ARGS(key);
60 HTTP_EMPTY_ARGS(next);
61 HTTP_EMPTY_ARGS(rewind);
62
63 HTTP_EMPTY_ARGS(count);
64
65 HTTP_EMPTY_ARGS(getAttachedRequests);
66 HTTP_EMPTY_ARGS(getFinishedRequests);
67
68 HTTP_BEGIN_ARGS(enablePipelining, 0)
69 HTTP_ARG_VAL(enable, 0)
70 HTTP_END_ARGS;
71
72 HTTP_BEGIN_ARGS(enableEvents, 0)
73 HTTP_ARG_VAL(enable, 0)
74 HTTP_END_ARGS;
75
76 zend_class_entry *http_requestpool_object_ce;
77 zend_function_entry http_requestpool_object_fe[] = {
78 HTTP_REQPOOL_ME(__construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
79 HTTP_REQPOOL_ME(__destruct, ZEND_ACC_PUBLIC|ZEND_ACC_DTOR)
80 HTTP_REQPOOL_ME(attach, ZEND_ACC_PUBLIC)
81 HTTP_REQPOOL_ME(detach, ZEND_ACC_PUBLIC)
82 HTTP_REQPOOL_ME(send, ZEND_ACC_PUBLIC)
83 HTTP_REQPOOL_ME(reset, ZEND_ACC_PUBLIC)
84
85 HTTP_REQPOOL_ME(socketPerform, ZEND_ACC_PROTECTED)
86 HTTP_REQPOOL_ME(socketSelect, ZEND_ACC_PROTECTED)
87
88 /* implements Iterator */
89 HTTP_REQPOOL_ME(valid, ZEND_ACC_PUBLIC)
90 HTTP_REQPOOL_ME(current, ZEND_ACC_PUBLIC)
91 HTTP_REQPOOL_ME(key, ZEND_ACC_PUBLIC)
92 HTTP_REQPOOL_ME(next, ZEND_ACC_PUBLIC)
93 HTTP_REQPOOL_ME(rewind, ZEND_ACC_PUBLIC)
94
95 /* implmenents Countable */
96 HTTP_REQPOOL_ME(count, ZEND_ACC_PUBLIC)
97
98 HTTP_REQPOOL_ME(getAttachedRequests, ZEND_ACC_PUBLIC)
99 HTTP_REQPOOL_ME(getFinishedRequests, ZEND_ACC_PUBLIC)
100
101 HTTP_REQPOOL_ME(enablePipelining, ZEND_ACC_PUBLIC)
102 HTTP_REQPOOL_ME(enableEvents, ZEND_ACC_PUBLIC)
103
104 EMPTY_FUNCTION_ENTRY
105 };
106 static zend_object_handlers http_requestpool_object_handlers;
107
108 PHP_MINIT_FUNCTION(http_requestpool_object)
109 {
110 HTTP_REGISTER_CLASS_EX(HttpRequestPool, http_requestpool_object, NULL, 0);
111 http_requestpool_object_handlers.clone_obj = NULL;
112
113 #if defined(HAVE_SPL) && !defined(WONKY)
114 zend_class_implements(http_requestpool_object_ce TSRMLS_CC, 2, spl_ce_Countable, zend_ce_iterator);
115 #else
116 zend_class_implements(http_requestpool_object_ce TSRMLS_CC, 1, zend_ce_iterator);
117 #endif
118
119 return SUCCESS;
120 }
121
122 zend_object_value _http_requestpool_object_new(zend_class_entry *ce TSRMLS_DC)
123 {
124 zend_object_value ov;
125 http_requestpool_object *o;
126
127 o = ecalloc(1, sizeof(http_requestpool_object));
128 o->zo.ce = ce;
129
130 http_request_pool_init(&o->pool);
131
132 #if PHP_VERSION_ID < 50399
133 ALLOC_HASHTABLE(OBJ_PROP(o));
134 zend_hash_init(OBJ_PROP(o), zend_hash_num_elements(&ce->default_properties), NULL, ZVAL_PTR_DTOR, 0);
135 zend_hash_copy(OBJ_PROP(o), &ce->default_properties, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
136 #else
137 object_properties_init(&o->zo, ce);
138 #endif
139
140 ov.handle = putObject(http_requestpool_object, o);
141 ov.handlers = &http_requestpool_object_handlers;
142
143 return ov;
144 }
145
146 void _http_requestpool_object_free(zend_object *object TSRMLS_DC)
147 {
148 http_requestpool_object *o = (http_requestpool_object *) object;
149
150 http_request_pool_dtor(&o->pool);
151 freeObject(o);
152 }
153
154 #define http_requestpool_object_llist2array _http_requestpool_object_llist2array
155 static void _http_requestpool_object_llist2array(zval **req, zval *array TSRMLS_DC)
156 {
157 ZVAL_ADDREF(*req);
158 add_next_index_zval(array, *req);
159 }
160
161 /* ### USERLAND ### */
162
163 /* {{{ proto void HttpRequestPool::__construct([HttpRequest request[, ...]])
164 Creates a new HttpRequestPool object instance. */
165 PHP_METHOD(HttpRequestPool, __construct)
166 {
167 int argc = ZEND_NUM_ARGS();
168 zval ***argv = safe_emalloc(argc, sizeof(zval *), 0);
169 getObject(http_requestpool_object, obj);
170
171 SET_EH_THROW_HTTP();
172 if (SUCCESS == zend_get_parameters_array_ex(argc, argv)) {
173 int i;
174
175 for (i = 0; i < argc; ++i) {
176 if (Z_TYPE_PP(argv[i]) == IS_OBJECT && instanceof_function(Z_OBJCE_PP(argv[i]), http_request_object_ce TSRMLS_CC)) {
177 http_request_pool_attach(&obj->pool, *(argv[i]));
178 }
179 }
180 }
181 efree(argv);
182 http_final(HTTP_EX_CE(request_pool));
183 SET_EH_NORMAL();
184 }
185 /* }}} */
186
187 /* {{{ proto void HttpRequestPool::__destruct()
188 Clean up HttpRequestPool object. */
189 PHP_METHOD(HttpRequestPool, __destruct)
190 {
191 getObject(http_requestpool_object, obj);
192
193 NO_ARGS;
194
195 http_request_pool_detach_all(&obj->pool);
196 }
197 /* }}} */
198
199 /* {{{ proto void HttpRequestPool::reset()
200 Detach all attached HttpRequest objects. */
201 PHP_METHOD(HttpRequestPool, reset)
202 {
203 getObject(http_requestpool_object, obj);
204
205 NO_ARGS;
206
207 obj->iterator.pos = 0;
208 http_request_pool_detach_all(&obj->pool);
209 }
210
211 /* {{{ proto bool HttpRequestPool::attach(HttpRequest request)
212 Attach an HttpRequest object to this HttpRequestPool. WARNING: set all options prior attaching! */
213 PHP_METHOD(HttpRequestPool, attach)
214 {
215 zval *request;
216 STATUS status = FAILURE;
217 getObject(http_requestpool_object, obj);
218
219 SET_EH_THROW_HTTP();
220 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &request, http_request_object_ce)) {
221 if (obj->iterator.pos > 0 && obj->iterator.pos < zend_llist_count(&obj->pool.handles)) {
222 http_error(HE_THROW, HTTP_E_REQUEST_POOL, "Cannot attach to the HttpRequestPool while the iterator is active");
223 } else {
224 status = http_request_pool_attach(&obj->pool, request);
225 }
226 }
227 SET_EH_NORMAL();
228 RETURN_SUCCESS(status);
229 }
230 /* }}} */
231
232 /* {{{ proto bool HttpRequestPool::detach(HttpRequest request)
233 Detach an HttpRequest object from this HttpRequestPool. */
234 PHP_METHOD(HttpRequestPool, detach)
235 {
236 zval *request;
237 STATUS status = FAILURE;
238 getObject(http_requestpool_object, obj);
239
240 SET_EH_THROW_HTTP();
241 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &request, http_request_object_ce)) {
242 obj->iterator.pos = -1;
243 status = http_request_pool_detach(&obj->pool, request);
244 }
245 SET_EH_NORMAL();
246 RETURN_SUCCESS(status);
247 }
248 /* }}} */
249
250 /* {{{ proto bool HttpRequestPool::send()
251 Send all attached HttpRequest objects in parallel. */
252 PHP_METHOD(HttpRequestPool, send)
253 {
254 STATUS status;
255 getObject(http_requestpool_object, obj);
256
257 NO_ARGS;
258
259 SET_EH_THROW_HTTP();
260 status = http_request_pool_send(&obj->pool);
261 SET_EH_NORMAL();
262
263 /* rethrow as HttpRequestPoolException */
264 http_final(HTTP_EX_CE(request_pool));
265
266 RETURN_SUCCESS(status);
267 }
268 /* }}} */
269
270 /* {{{ proto protected bool HttpRequestPool::socketPerform()
271 Returns TRUE until each request has finished its transaction. */
272 PHP_METHOD(HttpRequestPool, socketPerform)
273 {
274 getObject(http_requestpool_object, obj);
275
276 NO_ARGS;
277
278 if (0 < http_request_pool_perform(&obj->pool)) {
279 RETURN_TRUE;
280 } else {
281 RETURN_FALSE;
282 }
283 }
284 /* }}} */
285
286 /* {{{ proto protected bool HttpRequestPool::socketSelect([double timeout]) */
287 PHP_METHOD(HttpRequestPool, socketSelect)
288 {
289 double timeout = 0;
290 struct timeval custom_timeout, *custom_timeout_ptr = NULL;
291 getObject(http_requestpool_object, obj);
292
293 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|d", &timeout)) {
294 RETURN_FALSE;
295 }
296 if (ZEND_NUM_ARGS() && timeout > 0) {
297 custom_timeout.tv_sec = (time_t) timeout;
298 custom_timeout.tv_usec = HTTP_USEC(timeout) % HTTP_MCROSEC;
299 custom_timeout_ptr = &custom_timeout;
300 }
301
302 RETURN_SUCCESS(http_request_pool_select_ex(&obj->pool, custom_timeout_ptr));
303 }
304 /* }}} */
305
306 /* {{{ proto bool HttpRequestPool::valid()
307 Implements Iterator::valid(). */
308 PHP_METHOD(HttpRequestPool, valid)
309 {
310 NO_ARGS;
311
312 if (return_value_used) {
313 getObject(http_requestpool_object, obj);
314 RETURN_BOOL(obj->iterator.pos >= 0 && obj->iterator.pos < zend_llist_count(&obj->pool.handles));
315 }
316 }
317 /* }}} */
318
319 /* {{{ proto HttpRequest HttpRequestPool::current()
320 Implements Iterator::current(). */
321 PHP_METHOD(HttpRequestPool, current)
322 {
323 NO_ARGS;
324
325 if (return_value_used) {
326 long pos = 0;
327 zval **current = NULL;
328 zend_llist_position lpos;
329 getObject(http_requestpool_object, obj);
330
331 if (obj->iterator.pos < zend_llist_count(&obj->pool.handles)) {
332 for ( current = zend_llist_get_first_ex(&obj->pool.handles, &lpos);
333 current && obj->iterator.pos != pos++;
334 current = zend_llist_get_next_ex(&obj->pool.handles, &lpos));
335 if (current) {
336 RETURN_OBJECT(*current, 1);
337 }
338 }
339 RETURN_NULL();
340 }
341 }
342 /* }}} */
343
344 /* {{{ proto int HttpRequestPool::key()
345 Implements Iterator::key(). */
346 PHP_METHOD(HttpRequestPool, key)
347 {
348 NO_ARGS;
349
350 if (return_value_used) {
351 getObject(http_requestpool_object, obj);
352 RETURN_LONG(obj->iterator.pos);
353 }
354 }
355 /* }}} */
356
357 /* {{{ proto void HttpRequestPool::next()
358 Implements Iterator::next(). */
359 PHP_METHOD(HttpRequestPool, next)
360 {
361 NO_ARGS {
362 getObject(http_requestpool_object, obj);
363 ++(obj->iterator.pos);
364 }
365 }
366 /* }}} */
367
368 /* {{{ proto void HttpRequestPool::rewind()
369 Implements Iterator::rewind(). */
370 PHP_METHOD(HttpRequestPool, rewind)
371 {
372 NO_ARGS {
373 getObject(http_requestpool_object, obj);
374 obj->iterator.pos = 0;
375 }
376 }
377 /* }}} */
378
379 /* {{{ proto int HttpRequestPool::count()
380 Implements Countable::count(). */
381 PHP_METHOD(HttpRequestPool, count)
382 {
383 NO_ARGS {
384 getObject(http_requestpool_object, obj);
385 RETURN_LONG((long) zend_llist_count(&obj->pool.handles));
386 }
387 }
388 /* }}} */
389
390 /* {{{ proto array HttpRequestPool::getAttachedRequests()
391 Get attached HttpRequest objects. */
392 PHP_METHOD(HttpRequestPool, getAttachedRequests)
393 {
394 getObject(http_requestpool_object, obj);
395
396 NO_ARGS;
397
398 array_init(return_value);
399 zend_llist_apply_with_argument(&obj->pool.handles,
400 (llist_apply_with_arg_func_t) http_requestpool_object_llist2array,
401 return_value TSRMLS_CC);
402 }
403 /* }}} */
404
405 /* {{{ proto array HttpRequestPool::getFinishedRequests()
406 Get attached HttpRequest objects that already have finished their work. */
407 PHP_METHOD(HttpRequestPool, getFinishedRequests)
408 {
409 getObject(http_requestpool_object, obj);
410
411 NO_ARGS;
412
413 array_init(return_value);
414 zend_llist_apply_with_argument(&obj->pool.finished,
415 (llist_apply_with_arg_func_t) http_requestpool_object_llist2array,
416 return_value TSRMLS_CC);
417 }
418 /* }}} */
419
420 /* {{{ proto bool HttpRequestPool::enablePipelining([bool enable = true])
421 Enables pipelining support for all attached requests if support in libcurl is given. */
422 PHP_METHOD(HttpRequestPool, enablePipelining)
423 {
424 zend_bool enable = 1;
425 #if defined(HAVE_CURL_MULTI_SETOPT) && HTTP_CURL_VERSION(7,16,0)
426 getObject(http_requestpool_object, obj);
427 #endif
428
429 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &enable)) {
430 RETURN_FALSE;
431 }
432 #if defined(HAVE_CURL_MULTI_SETOPT) && HTTP_CURL_VERSION(7,16,0)
433 if (CURLM_OK == curl_multi_setopt(obj->pool.ch, CURLMOPT_PIPELINING, (long) enable)) {
434 RETURN_TRUE;
435 }
436 #endif
437 RETURN_FALSE;
438 }
439 /* }}} */
440
441 /* {{{ proto bool HttpRequestPool::enableEvents([bool enable = true])
442 Enables event-driven I/O if support in libcurl is given. */
443 PHP_METHOD(HttpRequestPool, enableEvents)
444 {
445 zend_bool enable = 1;
446 #if defined(HTTP_HAVE_EVENT)
447 getObject(http_requestpool_object, obj);
448 #endif
449
450 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &enable)) {
451 #if defined(HTTP_HAVE_EVENT)
452 obj->pool.useevents = enable;
453 RETURN_TRUE;
454 #endif
455 }
456 RETURN_FALSE;
457 }
458 /* }}} */
459
460 #endif /* ZEND_ENGINE_2 && HTTP_HAVE_CURL */
461
462 /*
463 * Local variables:
464 * tab-width: 4
465 * c-basic-offset: 4
466 * End:
467 * vim600: noet sw=4 ts=4 fdm=marker
468 * vim<600: noet sw=4 ts=4
469 */
470