xref: /curl/tests/libtest/lib2405.c (revision 25cbc2f7)
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  *
32  *  It is also expected that all transfers run by multi-handle should complete
33  *  successfully.
34  */
35 
36 #include "test.h"
37 
38 #include "testutil.h"
39 #include "warnless.h"
40 #include "memdebug.h"
41 
42 
43  /* ---------------------------------------------------------------- */
44 
45 #define test_check(expected_fds) \
46   if(res != CURLE_OK) { \
47     fprintf(stderr, "test failed with code: %d\n", res); \
48     goto test_cleanup; \
49   } \
50   else if(fd_count != expected_fds) { \
51     fprintf(stderr, "Max number of waitfds: %d not as expected: %d\n", \
52       fd_count, expected_fds); \
53     res = TEST_ERR_FAILURE; \
54     goto test_cleanup; \
55   }
56 
57 #define test_run_check(option, expected_fds) do { \
58   res = test_run(URL, option, &fd_count); \
59   test_check(expected_fds); \
60 } while(0)
61 
62  /* ---------------------------------------------------------------- */
63 
64 enum {
65   TEST_USE_HTTP1 = 0,
66   TEST_USE_HTTP2,
67   TEST_USE_HTTP2_MPLEX
68 };
69 
emptyWriteFunc(void * ptr,size_t size,size_t nmemb,void * data)70 static size_t emptyWriteFunc(void *ptr, size_t size, size_t nmemb,
71     void *data) {
72   (void)ptr; (void)data;
73   return size * nmemb;
74 }
75 
set_easy(char * URL,CURL * easy,long option)76 static CURLcode set_easy(char *URL, CURL *easy, long option)
77 {
78   CURLcode res = CURLE_OK;
79 
80   /* First set the URL that is about to receive our POST. */
81   easy_setopt(easy, CURLOPT_URL, URL);
82 
83   /* get verbose debug output please */
84   easy_setopt(easy, CURLOPT_VERBOSE, 1L);
85 
86   switch(option) {
87   case TEST_USE_HTTP1:
88     /* go http1 */
89     easy_setopt(easy, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
90     break;
91 
92   case TEST_USE_HTTP2:
93     /* go http2 */
94     easy_setopt(easy, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0);
95     break;
96 
97   case TEST_USE_HTTP2_MPLEX:
98     /* go http2 with multiplexing */
99     easy_setopt(easy, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0);
100     easy_setopt(easy, CURLOPT_PIPEWAIT, 1L);
101     break;
102   }
103 
104   /* no peer verify */
105   easy_setopt(easy, CURLOPT_SSL_VERIFYPEER, 0L);
106   easy_setopt(easy, CURLOPT_SSL_VERIFYHOST, 0L);
107 
108   /* include headers */
109   easy_setopt(easy, CURLOPT_HEADER, 1L);
110 
111   /* empty write function */
112   easy_setopt(easy, CURLOPT_WRITEFUNCTION, emptyWriteFunc);
113 
114 test_cleanup:
115   return res;
116 }
117 
test_run(char * URL,long option,unsigned int * max_fd_count)118 static CURLcode test_run(char *URL, long option, unsigned int *max_fd_count)
119 {
120   CURLMcode mc = CURLM_OK;
121   CURLM *multi = NULL;
122   CURLM *multi1 = NULL;
123 
124   CURL *easy1 = NULL;
125   CURL *easy2 = NULL;
126 
127   unsigned int max_count = 0;
128 
129   int still_running; /* keep number of running handles */
130   CURLMsg *msg; /* for picking up messages with the transfer status */
131   int msgs_left; /* how many messages are left */
132 
133   CURLcode result;
134   CURLcode res = CURLE_OK;
135 
136   struct curl_waitfd ufds[10];
137   struct curl_waitfd ufds1[10];
138   int numfds;
139 
140   easy_init(easy1);
141   easy_init(easy2);
142 
143   if(set_easy(URL, easy1, option) != CURLE_OK)
144     goto test_cleanup;
145 
146   if(set_easy(URL, easy2, option) != CURLE_OK)
147     goto test_cleanup;
148 
149   multi_init(multi);
150   multi_init(multi1);
151 
152   if(option == TEST_USE_HTTP2_MPLEX)
153     multi_setopt(multi, CURLMOPT_PIPELINING, CURLPIPE_MULTIPLEX);
154 
155   multi_add_handle(multi, easy1);
156   multi_add_handle(multi, easy2);
157 
158   while(!mc) {
159     /* get the count of file descriptors from the transfers */
160     unsigned int fd_count = 0;
161 
162     mc = curl_multi_perform(multi, &still_running);
163     if(!still_running || mc != CURLM_OK)
164       break;
165 
166     mc = curl_multi_waitfds(multi, ufds, 10, &fd_count);
167 
168     if(mc != CURLM_OK) {
169       fprintf(stderr, "curl_multi_waitfds() failed, code %d.\n", mc);
170       res = TEST_ERR_FAILURE;
171       break;
172     }
173 
174     if(!fd_count)
175       continue; /* no descriptors yet */
176 
177     /* checking case when we don't have enough space for waitfds */
178     mc = curl_multi_waitfds(multi, ufds1, fd_count - 1, NULL);
179 
180     if(mc != CURLM_OUT_OF_MEMORY) {
181       fprintf(stderr, "curl_multi_waitfds() return code %d instead of "
182         "CURLM_OUT_OF_MEMORY.\n", mc);
183       res = TEST_ERR_FAILURE;
184       break;
185     }
186 
187     if(fd_count > max_count)
188       max_count = fd_count;
189 
190     /* Do polling on descriptors in ufds in Multi 1 */
191     mc = curl_multi_poll(multi1, ufds, fd_count, 500, &numfds);
192 
193     if(mc != CURLM_OK) {
194       fprintf(stderr, "curl_multi_poll() failed, code %d.\\n", mc);
195       res = TEST_ERR_FAILURE;
196       break;
197     }
198   }
199 
200   for(;;) {
201     msg = curl_multi_info_read(multi, &msgs_left);
202     if(!msg)
203       break;
204     if(msg->msg == CURLMSG_DONE) {
205       result = msg->data.result;
206 
207       if(!res)
208         res = result;
209     }
210   }
211 
212   curl_multi_remove_handle(multi, easy1);
213   curl_multi_remove_handle(multi, easy2);
214 
215 test_cleanup:
216   curl_easy_cleanup(easy1);
217   curl_easy_cleanup(easy2);
218 
219   curl_multi_cleanup(multi);
220   curl_multi_cleanup(multi1);
221 
222   if(max_fd_count)
223     *max_fd_count = max_count;
224 
225   return res;
226 }
227 
empty_multi_test(void)228 static CURLcode empty_multi_test(void)
229 {
230   CURLMcode mc = CURLM_OK;
231   CURLM *multi = NULL;
232   CURL *easy = NULL;
233 
234   struct curl_waitfd ufds[10];
235 
236   CURLcode res = CURLE_OK;
237   unsigned int fd_count = 0;
238 
239   multi_init(multi);
240 
241   /* calling curl_multi_waitfds() on an empty multi handle.  */
242   mc = curl_multi_waitfds(multi, ufds, 10, &fd_count);
243 
244   if(mc != CURLM_OK) {
245     fprintf(stderr, "curl_multi_waitfds() failed, code %d.\n", mc);
246     res = TEST_ERR_FAILURE;
247     goto test_cleanup;
248   }
249   else if(fd_count > 0) {
250     fprintf(stderr, "curl_multi_waitfds() returned non-zero count of "
251         "waitfds: %d.\n", fd_count);
252     res = TEST_ERR_FAILURE;
253     goto test_cleanup;
254   }
255 
256   /* calling curl_multi_waitfds() on multi handle with added easy handle. */
257   easy_init(easy);
258 
259   if(set_easy((char *)"http://example.com", easy, TEST_USE_HTTP1) != CURLE_OK)
260     goto test_cleanup;
261 
262   multi_add_handle(multi, easy);
263 
264   mc = curl_multi_waitfds(multi, ufds, 10, &fd_count);
265 
266   if(mc != CURLM_OK) {
267     fprintf(stderr, "curl_multi_waitfds() failed, code %d.\n", mc);
268     res = TEST_ERR_FAILURE;
269     goto test_cleanup;
270   }
271   else if(fd_count > 0) {
272     fprintf(stderr, "curl_multi_waitfds() returned non-zero count of "
273         "waitfds: %d.\n", fd_count);
274     res = TEST_ERR_FAILURE;
275     goto test_cleanup;
276   }
277 
278   curl_multi_remove_handle(multi, easy);
279 
280 test_cleanup:
281   curl_easy_cleanup(easy);
282   curl_multi_cleanup(multi);
283   return res;
284 }
285 
test(char * URL)286 CURLcode test(char *URL)
287 {
288   CURLcode res = CURLE_OK;
289   unsigned int fd_count = 0;
290 
291   global_init(CURL_GLOBAL_ALL);
292 
293   /* Testing curl_multi_waitfds on empty and not started handles */
294   res = empty_multi_test();
295   if(res != CURLE_OK)
296     goto test_cleanup;
297 
298   /* HTTP1, expected 2 waitfds - one for each transfer */
299   test_run_check(TEST_USE_HTTP1, 2);
300 
301   /* HTTP2, expected 2 waitfds - one for each transfer */
302   test_run_check(TEST_USE_HTTP2, 2);
303 
304   /* HTTP2 with multiplexing, expected 1 waitfds - one for all transfers */
305   test_run_check(TEST_USE_HTTP2_MPLEX, 1);
306 
307 test_cleanup:
308   curl_global_cleanup();
309   return res;
310 }
311