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