1/*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. 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#include "test.h" 25 26#include <limits.h> 27#include <assert.h> 28 29#include "testutil.h" 30#include "warnless.h" 31#include "memdebug.h" 32 33#define TEST_HANG_TIMEOUT 60 * 1000 34#define MAX_EASY_HANDLES 3 35 36static int counter[MAX_EASY_HANDLES]; 37static CURL *easy[MAX_EASY_HANDLES]; 38static curl_socket_t sockets[MAX_EASY_HANDLES]; 39static int res = 0; 40 41static size_t callback(char *ptr, size_t size, size_t nmemb, void *data) 42{ 43 ssize_t idx = ((CURL **) data) - easy; 44 curl_socket_t sock; 45 long longdata; 46 CURLcode code; 47 const size_t failure = (size && nmemb) ? 0 : 1; 48 (void)ptr; 49 50 counter[idx] += (int)(size * nmemb); 51 52 /* Get socket being used for this easy handle, otherwise CURL_SOCKET_BAD */ 53 CURL_IGNORE_DEPRECATION( 54 code = curl_easy_getinfo(easy[idx], CURLINFO_LASTSOCKET, &longdata); 55 ) 56 if(CURLE_OK != code) { 57 fprintf(stderr, "%s:%d curl_easy_getinfo() failed, " 58 "with code %d (%s)\n", 59 __FILE__, __LINE__, (int)code, curl_easy_strerror(code)); 60 res = TEST_ERR_MAJOR_BAD; 61 return failure; 62 } 63 if(longdata == -1L) 64 sock = CURL_SOCKET_BAD; 65 else 66 sock = (curl_socket_t)longdata; 67 68 if(sock != CURL_SOCKET_BAD) { 69 /* Track relationship between this easy handle and the socket. */ 70 if(sockets[idx] == CURL_SOCKET_BAD) { 71 /* An easy handle without previous socket, record the socket. */ 72 sockets[idx] = sock; 73 } 74 else if(sock != sockets[idx]) { 75 /* An easy handle with a socket different to previously 76 tracked one, log and fail right away. Known bug #37. */ 77 fprintf(stderr, "Handle %d started on socket %d and moved to %d\n", 78 curlx_sztosi(idx), (int)sockets[idx], (int)sock); 79 res = TEST_ERR_MAJOR_BAD; 80 return failure; 81 } 82 } 83 return size * nmemb; 84} 85 86enum HandleState { 87 ReadyForNewHandle, 88 NeedSocketForNewHandle, 89 NoMoreHandles 90}; 91 92int test(char *url) 93{ 94 CURLM *multi = NULL; 95 int running; 96 int i; 97 int num_handles = 0; 98 enum HandleState state = ReadyForNewHandle; 99 size_t urllen = strlen(url) + 4 + 1; 100 char *full_url = malloc(urllen); 101 102 start_test_timing(); 103 104 if(!full_url) { 105 fprintf(stderr, "Not enough memory for full url\n"); 106 return TEST_ERR_MAJOR_BAD; 107 } 108 109 for(i = 0; i < MAX_EASY_HANDLES; ++i) { 110 easy[i] = NULL; 111 sockets[i] = CURL_SOCKET_BAD; 112 } 113 114 res_global_init(CURL_GLOBAL_ALL); 115 if(res) { 116 free(full_url); 117 return res; 118 } 119 120 multi_init(multi); 121 122 for(;;) { 123 struct timeval interval; 124 fd_set fdread; 125 fd_set fdwrite; 126 fd_set fdexcep; 127 long timeout = -99; 128 int maxfd = -99; 129 bool found_new_socket = FALSE; 130 131 /* Start a new handle if we aren't at the max */ 132 if(state == ReadyForNewHandle) { 133 easy_init(easy[num_handles]); 134 135 if(num_handles % 3 == 2) { 136 msnprintf(full_url, urllen, "%s0200", url); 137 easy_setopt(easy[num_handles], CURLOPT_HTTPAUTH, CURLAUTH_NTLM); 138 } 139 else { 140 msnprintf(full_url, urllen, "%s0100", url); 141 easy_setopt(easy[num_handles], CURLOPT_HTTPAUTH, CURLAUTH_BASIC); 142 } 143 easy_setopt(easy[num_handles], CURLOPT_FRESH_CONNECT, 1L); 144 easy_setopt(easy[num_handles], CURLOPT_URL, full_url); 145 easy_setopt(easy[num_handles], CURLOPT_VERBOSE, 1L); 146 easy_setopt(easy[num_handles], CURLOPT_HTTPGET, 1L); 147 easy_setopt(easy[num_handles], CURLOPT_USERPWD, "testuser:testpass"); 148 easy_setopt(easy[num_handles], CURLOPT_WRITEFUNCTION, callback); 149 easy_setopt(easy[num_handles], CURLOPT_WRITEDATA, easy + num_handles); 150 easy_setopt(easy[num_handles], CURLOPT_HEADER, 1L); 151 152 multi_add_handle(multi, easy[num_handles]); 153 num_handles += 1; 154 state = NeedSocketForNewHandle; 155 } 156 157 multi_perform(multi, &running); 158 159 fprintf(stderr, "%s:%d running %d state %d\n", 160 __FILE__, __LINE__, running, state); 161 162 abort_on_test_timeout(); 163 164 if(!running && state == NoMoreHandles) 165 break; /* done */ 166 167 FD_ZERO(&fdread); 168 FD_ZERO(&fdwrite); 169 FD_ZERO(&fdexcep); 170 171 multi_fdset(multi, &fdread, &fdwrite, &fdexcep, &maxfd); 172 173 /* At this point, maxfd is guaranteed to be greater or equal than -1. */ 174 175 if(state == NeedSocketForNewHandle) { 176 if(maxfd != -1 && !found_new_socket) { 177 fprintf(stderr, "Warning: socket did not open immediately for new " 178 "handle (trying again)\n"); 179 continue; 180 } 181 state = num_handles < MAX_EASY_HANDLES ? ReadyForNewHandle 182 : NoMoreHandles; 183 fprintf(stderr, "%s:%d new state %d\n", 184 __FILE__, __LINE__, state); 185 } 186 187 multi_timeout(multi, &timeout); 188 189 /* At this point, timeout is guaranteed to be greater or equal than -1. */ 190 191 fprintf(stderr, "%s:%d num_handles %d timeout %ld running %d\n", 192 __FILE__, __LINE__, num_handles, timeout, running); 193 194 if(timeout != -1L) { 195 int itimeout; 196#if LONG_MAX > INT_MAX 197 itimeout = (timeout > (long)INT_MAX) ? INT_MAX : (int)timeout; 198#else 199 itimeout = (int)timeout; 200#endif 201 interval.tv_sec = itimeout/1000; 202 interval.tv_usec = (itimeout%1000)*1000; 203 } 204 else { 205 interval.tv_sec = 0; 206 interval.tv_usec = 5000; 207 208 /* if there's no timeout and we get here on the last handle, we may 209 already have read the last part of the stream so waiting makes no 210 sense */ 211 if(!running && num_handles == MAX_EASY_HANDLES) { 212 break; 213 } 214 } 215 216 select_test(maxfd + 1, &fdread, &fdwrite, &fdexcep, &interval); 217 218 abort_on_test_timeout(); 219 } 220 221test_cleanup: 222 223 /* proper cleanup sequence - type PB */ 224 225 for(i = 0; i < MAX_EASY_HANDLES; i++) { 226 printf("Data connection %d: %d\n", i, counter[i]); 227 curl_multi_remove_handle(multi, easy[i]); 228 curl_easy_cleanup(easy[i]); 229 } 230 231 curl_multi_cleanup(multi); 232 curl_global_cleanup(); 233 234 free(full_url); 235 236 return res; 237} 238