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