1 /***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) Dmitry Karpov <dkarpov1970@gmail.com>
9 *
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at https://curl.se/docs/copyright.html.
13 *
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 * SPDX-License-Identifier: curl
22 *
23 ***************************************************************************/
24
25 /*
26 * The purpose of this test is to test behavior of curl_multi_waitfds
27 * function in different scenarios:
28 * empty multi handle (expected zero descriptors),
29 * HTTP1 amd HTTP2 (no multiplexing) two transfers (expected two descriptors),
30 * HTTP2 with multiplexing (expected one descriptors)
31 * Improper inputs to the API result in CURLM_BAD_FUNCTION_ARGUMENT.
32 * Sending a empty ufds, and size = 0 will return the number of fds needed.
33 * Sending a non-empty ufds, but smaller than the fds needed will result in a
34 * CURLM_OUT_OF_MEMORY, and a number of fds that is >= to the number needed.
35 *
36 * It is also expected that all transfers run by multi-handle should complete
37 * successfully.
38 */
39
40 #include "test.h"
41
42 #include "testutil.h"
43 #include "warnless.h"
44 #include "memdebug.h"
45
46
47 /* ---------------------------------------------------------------- */
48
49 #define test_check(expected_fds) \
50 if(res != CURLE_OK) { \
51 fprintf(stderr, "test failed with code: %d\n", res); \
52 goto test_cleanup; \
53 } \
54 else if(fd_count != expected_fds) { \
55 fprintf(stderr, "Max number of waitfds: %d not as expected: %d\n", \
56 fd_count, expected_fds); \
57 res = TEST_ERR_FAILURE; \
58 goto test_cleanup; \
59 }
60
61 #define test_run_check(option, expected_fds) do { \
62 res = test_run(URL, option, &fd_count); \
63 test_check(expected_fds); \
64 } while(0)
65
66 /* ---------------------------------------------------------------- */
67
68 enum {
69 TEST_USE_HTTP1 = 0,
70 TEST_USE_HTTP2,
71 TEST_USE_HTTP2_MPLEX
72 };
73
emptyWriteFunc(void * ptr,size_t size,size_t nmemb,void * data)74 static size_t emptyWriteFunc(void *ptr, size_t size, size_t nmemb,
75 void *data) {
76 (void)ptr; (void)data;
77 return size * nmemb;
78 }
79
set_easy(char * URL,CURL * easy,long option)80 static CURLcode set_easy(char *URL, CURL *easy, long option)
81 {
82 CURLcode res = CURLE_OK;
83
84 /* First set the URL that is about to receive our POST. */
85 easy_setopt(easy, CURLOPT_URL, URL);
86
87 /* get verbose debug output please */
88 easy_setopt(easy, CURLOPT_VERBOSE, 1L);
89
90 switch(option) {
91 case TEST_USE_HTTP1:
92 /* go http1 */
93 easy_setopt(easy, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
94 break;
95
96 case TEST_USE_HTTP2:
97 /* go http2 */
98 easy_setopt(easy, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0);
99 break;
100
101 case TEST_USE_HTTP2_MPLEX:
102 /* go http2 with multiplexing */
103 easy_setopt(easy, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0);
104 easy_setopt(easy, CURLOPT_PIPEWAIT, 1L);
105 break;
106 }
107
108 /* no peer verify */
109 easy_setopt(easy, CURLOPT_SSL_VERIFYPEER, 0L);
110 easy_setopt(easy, CURLOPT_SSL_VERIFYHOST, 0L);
111
112 /* include headers */
113 easy_setopt(easy, CURLOPT_HEADER, 1L);
114
115 /* empty write function */
116 easy_setopt(easy, CURLOPT_WRITEFUNCTION, emptyWriteFunc);
117
118 test_cleanup:
119 return res;
120 }
121
test_run(char * URL,long option,unsigned int * max_fd_count)122 static CURLcode test_run(char *URL, long option, unsigned int *max_fd_count)
123 {
124 CURLMcode mc = CURLM_OK;
125 CURLM *multi = NULL;
126 CURLM *multi1 = NULL;
127
128 CURL *easy1 = NULL;
129 CURL *easy2 = NULL;
130
131 unsigned int max_count = 0;
132
133 int still_running; /* keep number of running handles */
134 CURLMsg *msg; /* for picking up messages with the transfer status */
135 int msgs_left; /* how many messages are left */
136
137 CURLcode result;
138 CURLcode res = CURLE_OK;
139
140 struct curl_waitfd ufds[10];
141 struct curl_waitfd ufds1[10];
142 int numfds;
143
144 easy_init(easy1);
145 easy_init(easy2);
146
147 if(set_easy(URL, easy1, option) != CURLE_OK)
148 goto test_cleanup;
149
150 if(set_easy(URL, easy2, option) != CURLE_OK)
151 goto test_cleanup;
152
153 multi_init(multi);
154 multi_init(multi1);
155
156 if(option == TEST_USE_HTTP2_MPLEX)
157 multi_setopt(multi, CURLMOPT_PIPELINING, CURLPIPE_MULTIPLEX);
158
159 multi_add_handle(multi, easy1);
160 multi_add_handle(multi, easy2);
161
162 while(!mc) {
163 /* get the count of file descriptors from the transfers */
164 unsigned int fd_count = 0;
165 unsigned int fd_count_chk = 0;
166
167 mc = curl_multi_perform(multi, &still_running);
168 if(!still_running || mc != CURLM_OK)
169 break;
170
171 /* verify improper inputs are treated correctly. */
172 mc = curl_multi_waitfds(multi, NULL, 0, NULL);
173
174 if(mc != CURLM_BAD_FUNCTION_ARGUMENT) {
175 fprintf(stderr, "curl_multi_waitfds() return code %d instead of "
176 "CURLM_BAD_FUNCTION_ARGUMENT.\n", mc);
177 res = TEST_ERR_FAILURE;
178 break;
179 }
180
181 mc = curl_multi_waitfds(multi, NULL, 1, NULL);
182
183 if(mc != CURLM_BAD_FUNCTION_ARGUMENT) {
184 fprintf(stderr, "curl_multi_waitfds() return code %d instead of "
185 "CURLM_BAD_FUNCTION_ARGUMENT.\n", mc);
186 res = TEST_ERR_FAILURE;
187 break;
188 }
189
190 mc = curl_multi_waitfds(multi, NULL, 1, &fd_count);
191
192 if(mc != CURLM_BAD_FUNCTION_ARGUMENT) {
193 fprintf(stderr, "curl_multi_waitfds() return code %d instead of "
194 "CURLM_BAD_FUNCTION_ARGUMENT.\n", mc);
195 res = TEST_ERR_FAILURE;
196 break;
197 }
198
199 mc = curl_multi_waitfds(multi, ufds, 10, &fd_count);
200
201 if(mc != CURLM_OK) {
202 fprintf(stderr, "curl_multi_waitfds() failed, code %d.\n", mc);
203 res = TEST_ERR_FAILURE;
204 break;
205 }
206
207 if(!fd_count)
208 continue; /* no descriptors yet */
209
210 /* verify that sending nothing but the fd_count results in at least the
211 * same number of fds */
212 mc = curl_multi_waitfds(multi, NULL, 0, &fd_count_chk);
213
214 if(mc != CURLM_OK) {
215 fprintf(stderr, "curl_multi_waitfds() failed, code %d.\n", mc);
216 res = TEST_ERR_FAILURE;
217 break;
218 }
219
220 if(fd_count_chk < fd_count) {
221 fprintf(stderr, "curl_multi_waitfds() should return at least the number "
222 "of fds needed\n");
223 res = TEST_ERR_FAILURE;
224 break;
225 }
226
227 /* checking case when we don't have enough space for waitfds */
228 mc = curl_multi_waitfds(multi, ufds1, fd_count - 1, &fd_count_chk);
229
230 if(mc != CURLM_OUT_OF_MEMORY) {
231 fprintf(stderr, "curl_multi_waitfds() return code %d instead of "
232 "CURLM_OUT_OF_MEMORY.\n", mc);
233 res = TEST_ERR_FAILURE;
234 break;
235 }
236
237 if(fd_count_chk < fd_count) {
238 fprintf(stderr, "curl_multi_waitfds() sould return the amount of fds "
239 "needed if enough isn't passed in.\n");
240 res = TEST_ERR_FAILURE;
241 break;
242 }
243
244 /* sending ufds with zero size, is valid */
245 mc = curl_multi_waitfds(multi, ufds, 0, NULL);
246
247 if(mc != CURLM_OUT_OF_MEMORY) {
248 fprintf(stderr, "curl_multi_waitfds() return code %d instead of "
249 "CURLM_OUT_OF_MEMORY.\n", mc);
250 res = TEST_ERR_FAILURE;
251 break;
252 }
253
254 mc = curl_multi_waitfds(multi, ufds, 0, &fd_count_chk);
255
256 if(mc != CURLM_OUT_OF_MEMORY) {
257 fprintf(stderr, "curl_multi_waitfds() return code %d instead of "
258 "CURLM_OUT_OF_MEMORY.\n", mc);
259 res = TEST_ERR_FAILURE;
260 break;
261 }
262
263 if(fd_count_chk < fd_count) {
264 fprintf(stderr, "curl_multi_waitfds() sould return the amount of fds "
265 "needed if enough isn't passed in.\n");
266 res = TEST_ERR_FAILURE;
267 break;
268 }
269
270 if(fd_count > max_count)
271 max_count = fd_count;
272
273 /* Do polling on descriptors in ufds in Multi 1 */
274 mc = curl_multi_poll(multi1, ufds, fd_count, 500, &numfds);
275
276 if(mc != CURLM_OK) {
277 fprintf(stderr, "curl_multi_poll() failed, code %d.\\n", mc);
278 res = TEST_ERR_FAILURE;
279 break;
280 }
281 }
282
283 for(;;) {
284 msg = curl_multi_info_read(multi, &msgs_left);
285 if(!msg)
286 break;
287 if(msg->msg == CURLMSG_DONE) {
288 result = msg->data.result;
289
290 if(!res)
291 res = result;
292 }
293 }
294
295 curl_multi_remove_handle(multi, easy1);
296 curl_multi_remove_handle(multi, easy2);
297
298 test_cleanup:
299 curl_easy_cleanup(easy1);
300 curl_easy_cleanup(easy2);
301
302 curl_multi_cleanup(multi);
303 curl_multi_cleanup(multi1);
304
305 if(max_fd_count)
306 *max_fd_count = max_count;
307
308 return res;
309 }
310
empty_multi_test(void)311 static CURLcode empty_multi_test(void)
312 {
313 CURLMcode mc = CURLM_OK;
314 CURLM *multi = NULL;
315 CURL *easy = NULL;
316
317 struct curl_waitfd ufds[10];
318
319 CURLcode res = CURLE_OK;
320 unsigned int fd_count = 0;
321
322 multi_init(multi);
323
324 /* calling curl_multi_waitfds() on an empty multi handle. */
325 mc = curl_multi_waitfds(multi, ufds, 10, &fd_count);
326
327 if(mc != CURLM_OK) {
328 fprintf(stderr, "curl_multi_waitfds() failed, code %d.\n", mc);
329 res = TEST_ERR_FAILURE;
330 goto test_cleanup;
331 }
332 else if(fd_count > 0) {
333 fprintf(stderr, "curl_multi_waitfds() returned non-zero count of "
334 "waitfds: %d.\n", fd_count);
335 res = TEST_ERR_FAILURE;
336 goto test_cleanup;
337 }
338
339 /* calling curl_multi_waitfds() on multi handle with added easy handle. */
340 easy_init(easy);
341
342 if(set_easy((char *)"http://example.com", easy, TEST_USE_HTTP1) != CURLE_OK)
343 goto test_cleanup;
344
345 multi_add_handle(multi, easy);
346
347 mc = curl_multi_waitfds(multi, ufds, 10, &fd_count);
348
349 if(mc != CURLM_OK) {
350 fprintf(stderr, "curl_multi_waitfds() failed, code %d.\n", mc);
351 res = TEST_ERR_FAILURE;
352 goto test_cleanup;
353 }
354 else if(fd_count > 0) {
355 fprintf(stderr, "curl_multi_waitfds() returned non-zero count of "
356 "waitfds: %d.\n", fd_count);
357 res = TEST_ERR_FAILURE;
358 goto test_cleanup;
359 }
360
361 curl_multi_remove_handle(multi, easy);
362
363 test_cleanup:
364 curl_easy_cleanup(easy);
365 curl_multi_cleanup(multi);
366 return res;
367 }
368
test(char * URL)369 CURLcode test(char *URL)
370 {
371 CURLcode res = CURLE_OK;
372 unsigned int fd_count = 0;
373
374 global_init(CURL_GLOBAL_ALL);
375
376 /* Testing curl_multi_waitfds on empty and not started handles */
377 res = empty_multi_test();
378 if(res != CURLE_OK)
379 goto test_cleanup;
380
381 /* HTTP1, expected 2 waitfds - one for each transfer */
382 test_run_check(TEST_USE_HTTP1, 2);
383
384 /* HTTP2, expected 2 waitfds - one for each transfer */
385 test_run_check(TEST_USE_HTTP2, 2);
386
387 /* HTTP2 with multiplexing, expected 1 waitfds - one for all transfers */
388 test_run_check(TEST_USE_HTTP2_MPLEX, 1);
389
390 test_cleanup:
391 curl_global_cleanup();
392 return res;
393 }
394