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 * HTTP/2 Upgrade test 26 * </DESC> 27 */ 28#include <stdio.h> 29#include <stdlib.h> 30#include <stdint.h> 31#include <inttypes.h> 32/* #include <error.h> */ 33#include <errno.h> 34#include <curl/curl.h> 35#include <curl/mprintf.h> 36 37 38static void log_line_start(FILE *log, const char *idsbuf, curl_infotype type) 39{ 40 /* 41 * This is the trace look that is similar to what libcurl makes on its 42 * own. 43 */ 44 static const char * const s_infotype[] = { 45 "* ", "< ", "> ", "{ ", "} ", "{ ", "} " 46 }; 47 if(idsbuf && *idsbuf) 48 fprintf(log, "%s%s", idsbuf, s_infotype[type]); 49 else 50 fputs(s_infotype[type], log); 51} 52 53#define TRC_IDS_FORMAT_IDS_1 "[%" CURL_FORMAT_CURL_OFF_T "-x] " 54#define TRC_IDS_FORMAT_IDS_2 "[%" CURL_FORMAT_CURL_OFF_T "-%" \ 55 CURL_FORMAT_CURL_OFF_T "] " 56/* 57** callback for CURLOPT_DEBUGFUNCTION 58*/ 59static int debug_cb(CURL *handle, curl_infotype type, 60 char *data, size_t size, 61 void *userdata) 62{ 63 FILE *output = stderr; 64 static int newl = 0; 65 static int traced_data = 0; 66 char idsbuf[60]; 67 curl_off_t xfer_id, conn_id; 68 69 (void)handle; /* not used */ 70 (void)userdata; 71 72 if(!curl_easy_getinfo(handle, CURLINFO_XFER_ID, &xfer_id) && xfer_id >= 0) { 73 if(!curl_easy_getinfo(handle, CURLINFO_CONN_ID, &conn_id) && 74 conn_id >= 0) { 75 curl_msnprintf(idsbuf, sizeof(idsbuf), TRC_IDS_FORMAT_IDS_2, 76 xfer_id, conn_id); 77 } 78 else { 79 curl_msnprintf(idsbuf, sizeof(idsbuf), TRC_IDS_FORMAT_IDS_1, xfer_id); 80 } 81 } 82 else 83 idsbuf[0] = 0; 84 85 switch(type) { 86 case CURLINFO_HEADER_OUT: 87 if(size > 0) { 88 size_t st = 0; 89 size_t i; 90 for(i = 0; i < size - 1; i++) { 91 if(data[i] == '\n') { /* LF */ 92 if(!newl) { 93 log_line_start(output, idsbuf, type); 94 } 95 (void)fwrite(data + st, i - st + 1, 1, output); 96 st = i + 1; 97 newl = 0; 98 } 99 } 100 if(!newl) 101 log_line_start(output, idsbuf, type); 102 (void)fwrite(data + st, i - st + 1, 1, output); 103 } 104 newl = (size && (data[size - 1] != '\n')) ? 1 : 0; 105 traced_data = 0; 106 break; 107 case CURLINFO_TEXT: 108 case CURLINFO_HEADER_IN: 109 if(!newl) 110 log_line_start(output, idsbuf, type); 111 (void)fwrite(data, size, 1, output); 112 newl = (size && (data[size - 1] != '\n')) ? 1 : 0; 113 traced_data = 0; 114 break; 115 case CURLINFO_DATA_OUT: 116 case CURLINFO_DATA_IN: 117 case CURLINFO_SSL_DATA_IN: 118 case CURLINFO_SSL_DATA_OUT: 119 if(!traced_data) { 120 if(!newl) 121 log_line_start(output, idsbuf, type); 122 fprintf(output, "[%ld bytes data]\n", (long)size); 123 newl = 0; 124 traced_data = 1; 125 } 126 break; 127 default: /* nada */ 128 newl = 0; 129 traced_data = 1; 130 break; 131 } 132 133 return 0; 134} 135 136static size_t write_cb(char *ptr, size_t size, size_t nmemb, void *opaque) 137{ 138 (void)ptr; 139 (void)opaque; 140 return size * nmemb; 141} 142 143int main(int argc, char *argv[]) 144{ 145 const char *url; 146 CURLM *multi; 147 CURL *easy; 148 CURLMcode mc; 149 int running_handles = 0, start_count, numfds; 150 CURLMsg *msg; 151 int msgs_in_queue; 152 char range[128]; 153 154 if(argc != 2) { 155 fprintf(stderr, "%s URL\n", argv[0]); 156 exit(2); 157 } 158 159 url = argv[1]; 160 multi = curl_multi_init(); 161 if(!multi) { 162 fprintf(stderr, "curl_multi_init failed\n"); 163 exit(1); 164 } 165 166 start_count = 200; 167 do { 168 if(start_count) { 169 easy = curl_easy_init(); 170 if(!easy) { 171 fprintf(stderr, "curl_easy_init failed\n"); 172 exit(1); 173 } 174 curl_easy_setopt(easy, CURLOPT_VERBOSE, 1L); 175 curl_easy_setopt(easy, CURLOPT_DEBUGFUNCTION, debug_cb); 176 curl_easy_setopt(easy, CURLOPT_URL, url); 177 curl_easy_setopt(easy, CURLOPT_NOSIGNAL, 1L); 178 curl_easy_setopt(easy, CURLOPT_AUTOREFERER, 1L); 179 curl_easy_setopt(easy, CURLOPT_FAILONERROR, 1L); 180 curl_easy_setopt(easy, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0); 181 curl_easy_setopt(easy, CURLOPT_WRITEFUNCTION, write_cb); 182 curl_easy_setopt(easy, CURLOPT_WRITEDATA, NULL); 183 curl_easy_setopt(easy, CURLOPT_HTTPGET, 1L); 184 curl_msnprintf(range, sizeof(range), "%" PRIu64 "-%" PRIu64, 185 UINT64_C(0), UINT64_C(16384)); 186 curl_easy_setopt(easy, CURLOPT_RANGE, range); 187 188 mc = curl_multi_add_handle(multi, easy); 189 if(mc != CURLM_OK) { 190 fprintf(stderr, "curl_multi_add_handle: %s\n", 191 curl_multi_strerror(mc)); 192 exit(1); 193 } 194 --start_count; 195 } 196 197 mc = curl_multi_perform(multi, &running_handles); 198 if(mc != CURLM_OK) { 199 fprintf(stderr, "curl_multi_perform: %s\n", 200 curl_multi_strerror(mc)); 201 exit(1); 202 } 203 204 if(running_handles) { 205 mc = curl_multi_poll(multi, NULL, 0, 1000000, &numfds); 206 if(mc != CURLM_OK) { 207 fprintf(stderr, "curl_multi_poll: %s\n", 208 curl_multi_strerror(mc)); 209 exit(1); 210 } 211 } 212 213 /* Check for finished handles and remove. */ 214 while((msg = curl_multi_info_read(multi, &msgs_in_queue))) { 215 if(msg->msg == CURLMSG_DONE) { 216 long status = 0; 217 curl_off_t xfer_id; 218 curl_easy_getinfo(msg->easy_handle, CURLINFO_XFER_ID, &xfer_id); 219 curl_easy_getinfo(msg->easy_handle, CURLINFO_RESPONSE_CODE, &status); 220 if(msg->data.result == CURLE_SEND_ERROR || 221 msg->data.result == CURLE_RECV_ERROR) { 222 /* We get these if the server had a GOAWAY in transit on 223 * re-using a connection */ 224 } 225 else if(msg->data.result) { 226 fprintf(stderr, "transfer #%" CURL_FORMAT_CURL_OFF_T 227 ": failed with %d\n", xfer_id, msg->data.result); 228 exit(1); 229 } 230 else if(status != 206) { 231 fprintf(stderr, "transfer #%" CURL_FORMAT_CURL_OFF_T 232 ": wrong http status %ld (expected 206)\n", xfer_id, status); 233 exit(1); 234 } 235 curl_multi_remove_handle(multi, msg->easy_handle); 236 curl_easy_cleanup(msg->easy_handle); 237 fprintf(stderr, "transfer #%" CURL_FORMAT_CURL_OFF_T" retiring " 238 "(%d now running)\n", xfer_id, running_handles); 239 } 240 } 241 242 fprintf(stderr, "running_handles=%d, yet_to_start=%d\n", 243 running_handles, start_count); 244 245 } while(running_handles > 0 || start_count); 246 247 fprintf(stderr, "exiting\n"); 248 exit(EXIT_SUCCESS); 249} 250