xref: /PHP-5.4/ext/curl/multi.c (revision 5558d0db)
1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 5                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1997-2014 The PHP Group                                |
6    +----------------------------------------------------------------------+
7    | This source file is subject to version 3.01 of the PHP license,      |
8    | that is bundled with this package in the file LICENSE, and is        |
9    | available through the world-wide-web at the following url:           |
10    | http://www.php.net/license/3_01.txt                                  |
11    | If you did not receive a copy of the PHP license and are unable to   |
12    | obtain it through the world-wide-web, please send a note to          |
13    | license@php.net so we can mail you a copy immediately.               |
14    +----------------------------------------------------------------------+
15    | Author: Sterling Hughes <sterling@php.net>                           |
16    +----------------------------------------------------------------------+
17 */
18 
19 /* $Id$ */
20 
21 #define ZEND_INCLUDE_FULL_WINDOWS_HEADERS
22 
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26 
27 #include "php.h"
28 
29 #if HAVE_CURL
30 
31 #include "php_curl.h"
32 
33 #include <curl/curl.h>
34 #include <curl/multi.h>
35 
36 #ifdef HAVE_SYS_SELECT_H
37 #include <sys/select.h>
38 #endif
39 
40 #ifdef HAVE_SYS_TIME_H
41 #include <sys/time.h>
42 #endif
43 
44 #ifdef HAVE_SYS_TYPES_H
45 #include <sys/types.h>
46 #endif
47 
48 #ifdef HAVE_UNISTD_H
49 #include <unistd.h>
50 #endif
51 
52 /* {{{ proto resource curl_multi_init(void)
53    Returns a new cURL multi handle */
PHP_FUNCTION(curl_multi_init)54 PHP_FUNCTION(curl_multi_init)
55 {
56 	php_curlm *mh;
57 
58 	if (zend_parse_parameters_none() == FAILURE) {
59 		return;
60 	}
61 
62 	mh = ecalloc(1, sizeof(php_curlm));
63 	mh->multi = curl_multi_init();
64 
65 	zend_llist_init(&mh->easyh, sizeof(zval), _php_curl_multi_cleanup_list, 0);
66 
67 	ZEND_REGISTER_RESOURCE(return_value, mh, le_curl_multi_handle);
68 }
69 /* }}} */
70 
71 /* {{{ proto int curl_multi_add_handle(resource mh, resource ch)
72    Add a normal cURL handle to a cURL multi handle */
PHP_FUNCTION(curl_multi_add_handle)73 PHP_FUNCTION(curl_multi_add_handle)
74 {
75 	zval      *z_mh;
76 	zval      *z_ch;
77 	php_curlm *mh;
78 	php_curl  *ch;
79 	zval tmp_val;
80 
81 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rr", &z_mh, &z_ch) == FAILURE) {
82 		return;
83 	}
84 
85 	ZEND_FETCH_RESOURCE(mh, php_curlm *, &z_mh, -1, le_curl_multi_handle_name, le_curl_multi_handle);
86 	ZEND_FETCH_RESOURCE(ch, php_curl *, &z_ch, -1, le_curl_name, le_curl);
87 
88 	_php_curl_cleanup_handle(ch);
89 	ch->uses++;
90 
91 	/* we want to create a copy of this zval that we store in the multihandle structure element "easyh" */
92 	tmp_val = *z_ch;
93 	zval_copy_ctor(&tmp_val);
94 
95 	zend_llist_add_element(&mh->easyh, &tmp_val);
96 
97 	RETURN_LONG((long) curl_multi_add_handle(mh->multi, ch->cp));
98 }
99 /* }}} */
100 
_php_curl_multi_cleanup_list(void * data)101 void _php_curl_multi_cleanup_list(void *data) /* {{{ */
102 {
103 	zval *z_ch = (zval *)data;
104 	php_curl *ch;
105 	TSRMLS_FETCH();
106 
107 	if (!z_ch) {
108 		return;
109 	}
110 
111 	ch = (php_curl *) zend_fetch_resource(&z_ch TSRMLS_CC, -1, le_curl_name, NULL, 1, le_curl);
112 	if (!ch) {
113 		return;
114 	}
115 
116 	if (ch->uses) {
117 		ch->uses--;
118 	} else {
119 		zend_list_delete(Z_LVAL_P(z_ch));
120 	}
121 }
122 /* }}} */
123 
124 /* Used internally as comparison routine passed to zend_list_del_element */
curl_compare_resources(zval * z1,zval ** z2)125 static int curl_compare_resources( zval *z1, zval **z2 ) /* {{{ */
126 {
127 	return (Z_TYPE_P( z1 ) == Z_TYPE_PP( z2 ) &&
128             Z_TYPE_P( z1 ) == IS_RESOURCE     &&
129             Z_LVAL_P( z1 ) == Z_LVAL_PP( z2 ) );
130 }
131 /* }}} */
132 
133 /* {{{ proto int curl_multi_remove_handle(resource mh, resource ch)
134    Remove a multi handle from a set of cURL handles */
PHP_FUNCTION(curl_multi_remove_handle)135 PHP_FUNCTION(curl_multi_remove_handle)
136 {
137 	zval      *z_mh;
138 	zval      *z_ch;
139 	php_curlm *mh;
140 	php_curl  *ch;
141 
142 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rr", &z_mh, &z_ch) == FAILURE) {
143 		return;
144 	}
145 
146 	ZEND_FETCH_RESOURCE(mh, php_curlm *, &z_mh, -1, le_curl_multi_handle_name, le_curl_multi_handle);
147 	ZEND_FETCH_RESOURCE(ch, php_curl *, &z_ch, -1, le_curl_name, le_curl);
148 
149 	--ch->uses;
150 
151 	zend_llist_del_element( &mh->easyh, &z_ch,
152 							(int (*)(void *, void *)) curl_compare_resources );
153 
154 	RETURN_LONG((long) curl_multi_remove_handle(mh->multi, ch->cp));
155 }
156 /* }}} */
157 
_make_timeval_struct(struct timeval * to,double timeout)158 static void _make_timeval_struct(struct timeval *to, double timeout) /* {{{ */
159 {
160 	unsigned long conv;
161 
162 	conv = (unsigned long) (timeout * 1000000.0);
163 	to->tv_sec = conv / 1000000;
164 	to->tv_usec = conv % 1000000;
165 }
166 /* }}} */
167 
168 /* {{{ proto int curl_multi_select(resource mh[, double timeout])
169    Get all the sockets associated with the cURL extension, which can then be "selected" */
PHP_FUNCTION(curl_multi_select)170 PHP_FUNCTION(curl_multi_select)
171 {
172 	zval           *z_mh;
173 	php_curlm      *mh;
174 	fd_set          readfds;
175 	fd_set          writefds;
176 	fd_set          exceptfds;
177 	int             maxfd;
178 	double          timeout = 1.0;
179 	struct timeval  to;
180 
181 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|d", &z_mh, &timeout) == FAILURE) {
182 		return;
183 	}
184 
185 	ZEND_FETCH_RESOURCE(mh, php_curlm *, &z_mh, -1, le_curl_multi_handle_name, le_curl_multi_handle);
186 
187 	_make_timeval_struct(&to, timeout);
188 
189 	FD_ZERO(&readfds);
190 	FD_ZERO(&writefds);
191 	FD_ZERO(&exceptfds);
192 
193 	curl_multi_fdset(mh->multi, &readfds, &writefds, &exceptfds, &maxfd);
194 	if (maxfd == -1) {
195 		RETURN_LONG(-1);
196 	}
197 	RETURN_LONG(select(maxfd + 1, &readfds, &writefds, &exceptfds, &to));
198 }
199 /* }}} */
200 
201 /* {{{ proto int curl_multi_exec(resource mh, int &still_running)
202    Run the sub-connections of the current cURL handle */
PHP_FUNCTION(curl_multi_exec)203 PHP_FUNCTION(curl_multi_exec)
204 {
205 	zval      *z_mh;
206 	zval      *z_still_running;
207 	php_curlm *mh;
208 	int        still_running;
209 	int        result;
210 
211 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rz", &z_mh, &z_still_running) == FAILURE) {
212 		return;
213 	}
214 
215 	ZEND_FETCH_RESOURCE(mh, php_curlm *, &z_mh, -1, le_curl_multi_handle_name, le_curl_multi_handle);
216 
217 	{
218 		zend_llist_position pos;
219 		php_curl *ch;
220 		zval	*pz_ch;
221 
222 		for(pz_ch = (zval *)zend_llist_get_first_ex(&mh->easyh, &pos); pz_ch;
223 			pz_ch = (zval *)zend_llist_get_next_ex(&mh->easyh, &pos)) {
224 
225 			ZEND_FETCH_RESOURCE(ch, php_curl *, &pz_ch, -1, le_curl_name, le_curl);
226 			_php_curl_verify_handlers(ch, 1 TSRMLS_CC);
227 		}
228 	}
229 
230 	convert_to_long_ex(&z_still_running);
231 	still_running = Z_LVAL_P(z_still_running);
232 	result = curl_multi_perform(mh->multi, &still_running);
233 	ZVAL_LONG(z_still_running, still_running);
234 
235 	RETURN_LONG(result);
236 }
237 /* }}} */
238 
239 /* {{{ proto string curl_multi_getcontent(resource ch)
240    Return the content of a cURL handle if CURLOPT_RETURNTRANSFER is set */
PHP_FUNCTION(curl_multi_getcontent)241 PHP_FUNCTION(curl_multi_getcontent)
242 {
243 	zval     *z_ch;
244 	php_curl *ch;
245 
246 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &z_ch) == FAILURE) {
247 		return;
248 	}
249 
250 	ZEND_FETCH_RESOURCE(ch, php_curl *, &z_ch, -1, le_curl_name, le_curl);
251 
252 	if (ch->handlers->write->method == PHP_CURL_RETURN && ch->handlers->write->buf.len > 0) {
253 		smart_str_0(&ch->handlers->write->buf);
254 		RETURN_STRINGL(ch->handlers->write->buf.c, ch->handlers->write->buf.len, 1);
255 	}
256 
257         RETURN_EMPTY_STRING();
258 }
259 /* }}} */
260 
261 /* {{{ proto array curl_multi_info_read(resource mh [, long msgs_in_queue])
262    Get information about the current transfers */
PHP_FUNCTION(curl_multi_info_read)263 PHP_FUNCTION(curl_multi_info_read)
264 {
265 	zval      *z_mh;
266 	php_curlm *mh;
267 	CURLMsg	  *tmp_msg;
268 	int        queued_msgs;
269 	zval      *zmsgs_in_queue = NULL;
270 
271 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|z", &z_mh, &zmsgs_in_queue) == FAILURE) {
272 		return;
273 	}
274 
275 	ZEND_FETCH_RESOURCE(mh, php_curlm *, &z_mh, -1, le_curl_multi_handle_name, le_curl_multi_handle);
276 
277 	tmp_msg = curl_multi_info_read(mh->multi, &queued_msgs);
278 	if (tmp_msg == NULL) {
279 		RETURN_FALSE;
280 	}
281 	if (zmsgs_in_queue) {
282 		zval_dtor(zmsgs_in_queue);
283 		ZVAL_LONG(zmsgs_in_queue, queued_msgs);
284 	}
285 
286 	array_init(return_value);
287 	add_assoc_long(return_value, "msg", tmp_msg->msg);
288 	add_assoc_long(return_value, "result", tmp_msg->data.result);
289 
290 	/* find the original easy curl handle */
291 	{
292 		zend_llist_position pos;
293 		php_curl *ch;
294 		zval	*pz_ch;
295 
296 		/* search the list of easy handles hanging off the multi-handle */
297 		for(pz_ch = (zval *)zend_llist_get_first_ex(&mh->easyh, &pos); pz_ch;
298 			pz_ch = (zval *)zend_llist_get_next_ex(&mh->easyh, &pos)) {
299 			ZEND_FETCH_RESOURCE(ch, php_curl *, &pz_ch, -1, le_curl_name, le_curl);
300 			if (ch->cp == tmp_msg->easy_handle) {
301 
302 				/* we are adding a reference to the underlying php_curl
303 				   resource, so we need to add one to the resource's refcount
304 				   in order to ensure it doesn't get destroyed when the
305 				   underlying curl easy handle goes out of scope.
306 				   Normally you would call zval_copy_ctor( pz_ch ), or
307 				   SEPARATE_ZVAL, but those create new zvals, which is already
308 				   being done in add_assoc_resource */
309 
310 				zend_list_addref( Z_RESVAL_P( pz_ch ) );
311 
312 				/* add_assoc_resource automatically creates a new zval to
313 				   wrap the "resource" represented by the current pz_ch */
314 
315 				add_assoc_resource(return_value, "handle", Z_RESVAL_P(pz_ch));
316 
317 				break;
318 			}
319 		}
320 	}
321 }
322 /* }}} */
323 
324 /* {{{ proto void curl_multi_close(resource mh)
325    Close a set of cURL handles */
PHP_FUNCTION(curl_multi_close)326 PHP_FUNCTION(curl_multi_close)
327 {
328 	zval      *z_mh;
329 	php_curlm *mh;
330 
331 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &z_mh) == FAILURE) {
332 		return;
333 	}
334 
335 	ZEND_FETCH_RESOURCE(mh, php_curlm *, &z_mh, -1, le_curl_multi_handle_name, le_curl_multi_handle);
336 
337 	zend_list_delete(Z_LVAL_P(z_mh));
338 }
339 /* }}} */
340 
_php_curl_multi_close(zend_rsrc_list_entry * rsrc TSRMLS_DC)341 void _php_curl_multi_close(zend_rsrc_list_entry *rsrc TSRMLS_DC) /* {{{ */
342 {
343 	php_curlm *mh = (php_curlm *) rsrc->ptr;
344 	if (mh) {
345 		zend_llist_position pos;
346 		php_curl *ch;
347 		zval	*pz_ch;
348 
349 		for(pz_ch = (zval *)zend_llist_get_first_ex(&mh->easyh, &pos); pz_ch;
350 			pz_ch = (zval *)zend_llist_get_next_ex(&mh->easyh, &pos)) {
351 
352 			ch = (php_curl *) zend_fetch_resource(&pz_ch TSRMLS_CC, -1, le_curl_name, NULL, 1, le_curl);
353 			_php_curl_verify_handlers(ch, 0 TSRMLS_CC);
354 		}
355 
356 		curl_multi_cleanup(mh->multi);
357 		zend_llist_clean(&mh->easyh);
358 		efree(mh);
359 		rsrc->ptr = NULL;
360 	}
361 }
362 /* }}} */
363 
364 #endif
365 
366 /*
367  * Local variables:
368  * tab-width: 4
369  * c-basic-offset: 4
370  * End:
371  * vim600: noet sw=4 ts=4 fdm=marker
372  * vim<600: noet sw=4 ts=4
373  */
374