• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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