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