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