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/* <DESC> 25 * TLS session reuse 26 * </DESC> 27 */ 28#include <stdio.h> 29#include <stdlib.h> 30#include <stdint.h> 31#include <string.h> 32#include <inttypes.h> 33/* #include <error.h> */ 34#include <errno.h> 35#include <curl/curl.h> 36#include <curl/mprintf.h> 37 38 39static void log_line_start(FILE *log, const char *idsbuf, curl_infotype type) 40{ 41 /* 42 * This is the trace look that is similar to what libcurl makes on its 43 * own. 44 */ 45 static const char * const s_infotype[] = { 46 "* ", "< ", "> ", "{ ", "} ", "{ ", "} " 47 }; 48 if(idsbuf && *idsbuf) 49 fprintf(log, "%s%s", idsbuf, s_infotype[type]); 50 else 51 fputs(s_infotype[type], log); 52} 53 54#define TRC_IDS_FORMAT_IDS_1 "[%" CURL_FORMAT_CURL_OFF_T "-x] " 55#define TRC_IDS_FORMAT_IDS_2 "[%" CURL_FORMAT_CURL_OFF_T "-%" \ 56 CURL_FORMAT_CURL_OFF_T "] " 57/* 58** callback for CURLOPT_DEBUGFUNCTION 59*/ 60static int debug_cb(CURL *handle, curl_infotype type, 61 char *data, size_t size, 62 void *userdata) 63{ 64 FILE *output = stderr; 65 static int newl = 0; 66 static int traced_data = 0; 67 char idsbuf[60]; 68 curl_off_t xfer_id, conn_id; 69 70 (void)handle; /* not used */ 71 (void)userdata; 72 73 if(!curl_easy_getinfo(handle, CURLINFO_XFER_ID, &xfer_id) && xfer_id >= 0) { 74 if(!curl_easy_getinfo(handle, CURLINFO_CONN_ID, &conn_id) && 75 conn_id >= 0) { 76 curl_msnprintf(idsbuf, sizeof(idsbuf), TRC_IDS_FORMAT_IDS_2, 77 xfer_id, conn_id); 78 } 79 else { 80 curl_msnprintf(idsbuf, sizeof(idsbuf), TRC_IDS_FORMAT_IDS_1, xfer_id); 81 } 82 } 83 else 84 idsbuf[0] = 0; 85 86 switch(type) { 87 case CURLINFO_HEADER_OUT: 88 if(size > 0) { 89 size_t st = 0; 90 size_t i; 91 for(i = 0; i < size - 1; i++) { 92 if(data[i] == '\n') { /* LF */ 93 if(!newl) { 94 log_line_start(output, idsbuf, type); 95 } 96 (void)fwrite(data + st, i - st + 1, 1, output); 97 st = i + 1; 98 newl = 0; 99 } 100 } 101 if(!newl) 102 log_line_start(output, idsbuf, type); 103 (void)fwrite(data + st, i - st + 1, 1, output); 104 } 105 newl = (size && (data[size - 1] != '\n')) ? 1 : 0; 106 traced_data = 0; 107 break; 108 case CURLINFO_TEXT: 109 case CURLINFO_HEADER_IN: 110 if(!newl) 111 log_line_start(output, idsbuf, type); 112 (void)fwrite(data, size, 1, output); 113 newl = (size && (data[size - 1] != '\n')) ? 1 : 0; 114 traced_data = 0; 115 break; 116 case CURLINFO_DATA_OUT: 117 case CURLINFO_DATA_IN: 118 case CURLINFO_SSL_DATA_IN: 119 case CURLINFO_SSL_DATA_OUT: 120 if(!traced_data) { 121 if(!newl) 122 log_line_start(output, idsbuf, type); 123 fprintf(output, "[%ld bytes data]\n", (long)size); 124 newl = 0; 125 traced_data = 1; 126 } 127 break; 128 default: /* nada */ 129 newl = 0; 130 traced_data = 1; 131 break; 132 } 133 134 return 0; 135} 136 137static size_t write_cb(char *ptr, size_t size, size_t nmemb, void *opaque) 138{ 139 (void)ptr; 140 (void)opaque; 141 return size * nmemb; 142} 143 144static void add_transfer(CURLM *multi, CURLSH *share, 145 struct curl_slist *resolve, const char *url) 146{ 147 CURL *easy; 148 CURLMcode mc; 149 150 easy = curl_easy_init(); 151 if(!easy) { 152 fprintf(stderr, "curl_easy_init failed\n"); 153 exit(1); 154 } 155 curl_easy_setopt(easy, CURLOPT_VERBOSE, 1L); 156 curl_easy_setopt(easy, CURLOPT_DEBUGFUNCTION, debug_cb); 157 curl_easy_setopt(easy, CURLOPT_URL, url); 158 curl_easy_setopt(easy, CURLOPT_SHARE, share); 159 curl_easy_setopt(easy, CURLOPT_NOSIGNAL, 1L); 160 curl_easy_setopt(easy, CURLOPT_AUTOREFERER, 1L); 161 curl_easy_setopt(easy, CURLOPT_FAILONERROR, 1L); 162 curl_easy_setopt(easy, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); 163 curl_easy_setopt(easy, CURLOPT_WRITEFUNCTION, write_cb); 164 curl_easy_setopt(easy, CURLOPT_WRITEDATA, NULL); 165 curl_easy_setopt(easy, CURLOPT_HTTPGET, 1L); 166 curl_easy_setopt(easy, CURLOPT_SSL_VERIFYPEER, 0L); 167 if(resolve) 168 curl_easy_setopt(easy, CURLOPT_RESOLVE, resolve); 169 170 171 mc = curl_multi_add_handle(multi, easy); 172 if(mc != CURLM_OK) { 173 fprintf(stderr, "curl_multi_add_handle: %s\n", 174 curl_multi_strerror(mc)); 175 exit(1); 176 } 177} 178 179int main(int argc, char *argv[]) 180{ 181 const char *url; 182 CURLM *multi; 183 CURLMcode mc; 184 int running_handles = 0, numfds; 185 CURLMsg *msg; 186 CURLSH *share; 187 CURLU *cu; 188 struct curl_slist resolve; 189 char resolve_buf[1024]; 190 int msgs_in_queue; 191 int add_more, waits, ongoing = 0; 192 char *host, *port; 193 194 if(argc != 2) { 195 fprintf(stderr, "%s URL\n", argv[0]); 196 exit(2); 197 } 198 199 url = argv[1]; 200 cu = curl_url(); 201 if(!cu) { 202 fprintf(stderr, "out of memory\n"); 203 exit(1); 204 } 205 if(curl_url_set(cu, CURLUPART_URL, url, 0)) { 206 fprintf(stderr, "not a URL: '%s'\n", url); 207 exit(1); 208 } 209 if(curl_url_get(cu, CURLUPART_HOST, &host, 0)) { 210 fprintf(stderr, "could not get host of '%s'\n", url); 211 exit(1); 212 } 213 if(curl_url_get(cu, CURLUPART_PORT, &port, 0)) { 214 fprintf(stderr, "could not get port of '%s'\n", url); 215 exit(1); 216 } 217 218 memset(&resolve, 0, sizeof(resolve)); 219 curl_msnprintf(resolve_buf, sizeof(resolve_buf)-1, 220 "%s:%s:127.0.0.1", host, port); 221 curl_slist_append(&resolve, resolve_buf); 222 223 multi = curl_multi_init(); 224 if(!multi) { 225 fprintf(stderr, "curl_multi_init failed\n"); 226 exit(1); 227 } 228 229 share = curl_share_init(); 230 if(!share) { 231 fprintf(stderr, "curl_share_init failed\n"); 232 exit(1); 233 } 234 curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_SSL_SESSION); 235 236 237 add_transfer(multi, share, &resolve, url); 238 ++ongoing; 239 add_more = 6; 240 waits = 3; 241 do { 242 mc = curl_multi_perform(multi, &running_handles); 243 if(mc != CURLM_OK) { 244 fprintf(stderr, "curl_multi_perform: %s\n", 245 curl_multi_strerror(mc)); 246 exit(1); 247 } 248 249 if(running_handles) { 250 mc = curl_multi_poll(multi, NULL, 0, 1000000, &numfds); 251 if(mc != CURLM_OK) { 252 fprintf(stderr, "curl_multi_poll: %s\n", 253 curl_multi_strerror(mc)); 254 exit(1); 255 } 256 } 257 258 if(waits) { 259 --waits; 260 } 261 else { 262 while(add_more) { 263 add_transfer(multi, share, &resolve, url); 264 ++ongoing; 265 --add_more; 266 } 267 } 268 269 /* Check for finished handles and remove. */ 270 while((msg = curl_multi_info_read(multi, &msgs_in_queue))) { 271 if(msg->msg == CURLMSG_DONE) { 272 long status = 0; 273 curl_off_t xfer_id; 274 curl_easy_getinfo(msg->easy_handle, CURLINFO_XFER_ID, &xfer_id); 275 curl_easy_getinfo(msg->easy_handle, CURLINFO_RESPONSE_CODE, &status); 276 if(msg->data.result == CURLE_SEND_ERROR || 277 msg->data.result == CURLE_RECV_ERROR) { 278 /* We get these if the server had a GOAWAY in transit on 279 * re-using a connection */ 280 } 281 else if(msg->data.result) { 282 fprintf(stderr, "transfer #%" CURL_FORMAT_CURL_OFF_T 283 ": failed with %d\n", xfer_id, msg->data.result); 284 exit(1); 285 } 286 else if(status != 200) { 287 fprintf(stderr, "transfer #%" CURL_FORMAT_CURL_OFF_T 288 ": wrong http status %ld (expected 200)\n", xfer_id, status); 289 exit(1); 290 } 291 curl_multi_remove_handle(multi, msg->easy_handle); 292 curl_easy_cleanup(msg->easy_handle); 293 --ongoing; 294 fprintf(stderr, "transfer #%" CURL_FORMAT_CURL_OFF_T" retiring " 295 "(%d now running)\n", xfer_id, running_handles); 296 } 297 } 298 299 fprintf(stderr, "running_handles=%d, yet_to_start=%d\n", 300 running_handles, add_more); 301 302 } while(ongoing || add_more); 303 304 fprintf(stderr, "exiting\n"); 305 exit(EXIT_SUCCESS); 306} 307