| /*************************************************************************** |
| * _ _ ____ _ |
| * Project ___| | | | _ \| | |
| * / __| | | | |_) | | |
| * | (__| |_| | _ <| |___ |
| * \___|\___/|_| \_\_____| |
| * |
| * Copyright (C) Dmitry Karpov <dkarpov1970@gmail.com> |
| * |
| * This software is licensed as described in the file COPYING, which |
| * you should have received as part of this distribution. The terms |
| * are also available at https://curl.se/docs/copyright.html. |
| * |
| * You may opt to use, copy, modify, merge, publish, distribute and/or sell |
| * copies of the Software, and permit persons to whom the Software is |
| * furnished to do so, under the terms of the COPYING file. |
| * |
| * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY |
| * KIND, either express or implied. |
| * |
| * SPDX-License-Identifier: curl |
| * |
| ***************************************************************************/ |
| |
| /* |
| * The purpose of this test is to test behavior of curl_multi_waitfds |
| * function in different scenarios: |
| * empty multi handle (expected zero descriptors), |
| * HTTP1 amd HTTP2 (no multiplexing) two transfers (expected two descriptors), |
| * HTTP2 with multiplexing (expected one descriptors) |
| * |
| * It is also expected that all transfers run by multi-handle should complete |
| * successfully. |
| */ |
| |
| #include "test.h" |
| |
| #include "testutil.h" |
| #include "warnless.h" |
| #include "memdebug.h" |
| |
| |
| /* ---------------------------------------------------------------- */ |
| |
| #define test_check(expected_fds) \ |
| if(res != CURLE_OK) { \ |
| fprintf(stderr, "test failed with code: %d\n", res); \ |
| goto test_cleanup; \ |
| } \ |
| else if(fd_count != expected_fds) { \ |
| fprintf(stderr, "Max number of waitfds: %d not as expected: %d\n", \ |
| fd_count, expected_fds); \ |
| res = TEST_ERR_FAILURE; \ |
| goto test_cleanup; \ |
| } |
| |
| #define test_run_check(option, expected_fds) do { \ |
| res = test_run(URL, option, &fd_count); \ |
| test_check(expected_fds); \ |
| } while(0) |
| |
| /* ---------------------------------------------------------------- */ |
| |
| enum { |
| TEST_USE_HTTP1 = 0, |
| TEST_USE_HTTP2, |
| TEST_USE_HTTP2_MPLEX |
| }; |
| |
| static size_t emptyWriteFunc(void *ptr, size_t size, size_t nmemb, |
| void *data) { |
| (void)ptr; (void)data; |
| return size * nmemb; |
| } |
| |
| static CURLcode set_easy(char *URL, CURL *easy, long option) |
| { |
| CURLcode res = CURLE_OK; |
| |
| /* First set the URL that is about to receive our POST. */ |
| easy_setopt(easy, CURLOPT_URL, URL); |
| |
| /* get verbose debug output please */ |
| easy_setopt(easy, CURLOPT_VERBOSE, 1L); |
| |
| switch(option) { |
| case TEST_USE_HTTP1: |
| /* go http1 */ |
| easy_setopt(easy, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); |
| break; |
| |
| case TEST_USE_HTTP2: |
| /* go http2 */ |
| easy_setopt(easy, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0); |
| break; |
| |
| case TEST_USE_HTTP2_MPLEX: |
| /* go http2 with multiplexing */ |
| easy_setopt(easy, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0); |
| easy_setopt(easy, CURLOPT_PIPEWAIT, 1L); |
| break; |
| } |
| |
| /* no peer verify */ |
| easy_setopt(easy, CURLOPT_SSL_VERIFYPEER, 0L); |
| easy_setopt(easy, CURLOPT_SSL_VERIFYHOST, 0L); |
| |
| /* include headers */ |
| easy_setopt(easy, CURLOPT_HEADER, 1L); |
| |
| /* empty write function */ |
| easy_setopt(easy, CURLOPT_WRITEFUNCTION, emptyWriteFunc); |
| |
| test_cleanup: |
| return res; |
| } |
| |
| static CURLcode test_run(char *URL, long option, unsigned int *max_fd_count) |
| { |
| CURLMcode mc = CURLM_OK; |
| CURLM *multi = NULL; |
| CURLM *multi1 = NULL; |
| |
| CURL *easy1 = NULL; |
| CURL *easy2 = NULL; |
| |
| unsigned int max_count = 0; |
| |
| int still_running; /* keep number of running handles */ |
| CURLMsg *msg; /* for picking up messages with the transfer status */ |
| int msgs_left; /* how many messages are left */ |
| |
| CURLcode result; |
| CURLcode res = CURLE_OK; |
| |
| struct curl_waitfd ufds[10]; |
| struct curl_waitfd ufds1[10]; |
| int numfds; |
| |
| easy_init(easy1); |
| easy_init(easy2); |
| |
| if(set_easy(URL, easy1, option) != CURLE_OK) |
| goto test_cleanup; |
| |
| if(set_easy(URL, easy2, option) != CURLE_OK) |
| goto test_cleanup; |
| |
| multi_init(multi); |
| multi_init(multi1); |
| |
| if(option == TEST_USE_HTTP2_MPLEX) |
| multi_setopt(multi, CURLMOPT_PIPELINING, CURLPIPE_MULTIPLEX); |
| |
| multi_add_handle(multi, easy1); |
| multi_add_handle(multi, easy2); |
| |
| while(!mc) { |
| /* get the count of file descriptors from the transfers */ |
| unsigned int fd_count = 0; |
| |
| mc = curl_multi_perform(multi, &still_running); |
| if(!still_running || mc != CURLM_OK) |
| break; |
| |
| mc = curl_multi_waitfds(multi, ufds, 10, &fd_count); |
| |
| if(mc != CURLM_OK) { |
| fprintf(stderr, "curl_multi_waitfds() failed, code %d.\n", mc); |
| res = TEST_ERR_FAILURE; |
| break; |
| } |
| |
| if(!fd_count) |
| continue; /* no descriptors yet */ |
| |
| /* checking case when we don't have enough space for waitfds */ |
| mc = curl_multi_waitfds(multi, ufds1, fd_count - 1, NULL); |
| |
| if(mc != CURLM_OUT_OF_MEMORY) { |
| fprintf(stderr, "curl_multi_waitfds() return code %d instead of " |
| "CURLM_OUT_OF_MEMORY.\n", mc); |
| res = TEST_ERR_FAILURE; |
| break; |
| } |
| |
| if(fd_count > max_count) |
| max_count = fd_count; |
| |
| /* Do polling on descriptors in ufds in Multi 1 */ |
| mc = curl_multi_poll(multi1, ufds, fd_count, 500, &numfds); |
| |
| if(mc != CURLM_OK) { |
| fprintf(stderr, "curl_multi_poll() failed, code %d.\\n", mc); |
| res = TEST_ERR_FAILURE; |
| break; |
| } |
| } |
| |
| for(;;) { |
| msg = curl_multi_info_read(multi, &msgs_left); |
| if(!msg) |
| break; |
| if(msg->msg == CURLMSG_DONE) { |
| result = msg->data.result; |
| |
| if(!res) |
| res = result; |
| } |
| } |
| |
| curl_multi_remove_handle(multi, easy1); |
| curl_multi_remove_handle(multi, easy2); |
| |
| test_cleanup: |
| curl_easy_cleanup(easy1); |
| curl_easy_cleanup(easy2); |
| |
| curl_multi_cleanup(multi); |
| curl_multi_cleanup(multi1); |
| |
| if(max_fd_count) |
| *max_fd_count = max_count; |
| |
| return res; |
| } |
| |
| static CURLcode empty_multi_test(void) |
| { |
| CURLMcode mc = CURLM_OK; |
| CURLM *multi = NULL; |
| CURL *easy = NULL; |
| |
| struct curl_waitfd ufds[10]; |
| |
| CURLcode res = CURLE_OK; |
| unsigned int fd_count = 0; |
| |
| multi_init(multi); |
| |
| /* calling curl_multi_waitfds() on an empty multi handle. */ |
| mc = curl_multi_waitfds(multi, ufds, 10, &fd_count); |
| |
| if(mc != CURLM_OK) { |
| fprintf(stderr, "curl_multi_waitfds() failed, code %d.\n", mc); |
| res = TEST_ERR_FAILURE; |
| goto test_cleanup; |
| } |
| else if(fd_count > 0) { |
| fprintf(stderr, "curl_multi_waitfds() returned non-zero count of " |
| "waitfds: %d.\n", fd_count); |
| res = TEST_ERR_FAILURE; |
| goto test_cleanup; |
| } |
| |
| /* calling curl_multi_waitfds() on multi handle with added easy handle. */ |
| easy_init(easy); |
| |
| if(set_easy((char *)"http://example.com", easy, TEST_USE_HTTP1) != CURLE_OK) |
| goto test_cleanup; |
| |
| multi_add_handle(multi, easy); |
| |
| mc = curl_multi_waitfds(multi, ufds, 10, &fd_count); |
| |
| if(mc != CURLM_OK) { |
| fprintf(stderr, "curl_multi_waitfds() failed, code %d.\n", mc); |
| res = TEST_ERR_FAILURE; |
| goto test_cleanup; |
| } |
| else if(fd_count > 0) { |
| fprintf(stderr, "curl_multi_waitfds() returned non-zero count of " |
| "waitfds: %d.\n", fd_count); |
| res = TEST_ERR_FAILURE; |
| goto test_cleanup; |
| } |
| |
| curl_multi_remove_handle(multi, easy); |
| |
| test_cleanup: |
| curl_easy_cleanup(easy); |
| curl_multi_cleanup(multi); |
| return res; |
| } |
| |
| CURLcode test(char *URL) |
| { |
| CURLcode res = CURLE_OK; |
| unsigned int fd_count = 0; |
| |
| global_init(CURL_GLOBAL_ALL); |
| |
| /* Testing curl_multi_waitfds on empty and not started handles */ |
| res = empty_multi_test(); |
| if(res != CURLE_OK) |
| goto test_cleanup; |
| |
| /* HTTP1, expected 2 waitfds - one for each transfer */ |
| test_run_check(TEST_USE_HTTP1, 2); |
| |
| /* HTTP2, expected 2 waitfds - one for each transfer */ |
| test_run_check(TEST_USE_HTTP2, 2); |
| |
| /* HTTP2 with multiplexing, expected 1 waitfds - one for all transfers */ |
| test_run_check(TEST_USE_HTTP2_MPLEX, 1); |
| |
| test_cleanup: |
| curl_global_cleanup(); |
| return res; |
| } |