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 * Multiplexed HTTP/2 downloads over a single connection 26 * </DESC> 27 */ 28#include <stdio.h> 29#include <stdlib.h> 30#include <string.h> 31#include <errno.h> 32 33/* somewhat unix-specific */ 34#include <sys/time.h> 35#include <unistd.h> 36 37/* curl stuff */ 38#include <curl/curl.h> 39#include <curl/mprintf.h> 40 41#ifndef CURLPIPE_MULTIPLEX 42/* This little trick will just make sure that we do not enable pipelining for 43 libcurls old enough to not have this symbol. It is _not_ defined to zero in 44 a recent libcurl header. */ 45#define CURLPIPE_MULTIPLEX 0 46#endif 47 48struct transfer { 49 CURL *easy; 50 unsigned int num; 51 FILE *out; 52}; 53 54#define NUM_HANDLES 1000 55 56static 57void dump(const char *text, unsigned int num, unsigned char *ptr, size_t size, 58 char nohex) 59{ 60 size_t i; 61 size_t c; 62 63 unsigned int width = 0x10; 64 65 if(nohex) 66 /* without the hex output, we can fit more on screen */ 67 width = 0x40; 68 69 fprintf(stderr, "%u %s, %lu bytes (0x%lx)\n", 70 num, text, (unsigned long)size, (unsigned long)size); 71 72 for(i = 0; i<size; i += width) { 73 74 fprintf(stderr, "%4.4lx: ", (unsigned long)i); 75 76 if(!nohex) { 77 /* hex not disabled, show it */ 78 for(c = 0; c < width; c++) 79 if(i + c < size) 80 fprintf(stderr, "%02x ", ptr[i + c]); 81 else 82 fputs(" ", stderr); 83 } 84 85 for(c = 0; (c < width) && (i + c < size); c++) { 86 /* check for 0D0A; if found, skip past and start a new line of output */ 87 if(nohex && (i + c + 1 < size) && ptr[i + c] == 0x0D && 88 ptr[i + c + 1] == 0x0A) { 89 i += (c + 2 - width); 90 break; 91 } 92 fprintf(stderr, "%c", 93 (ptr[i + c] >= 0x20) && (ptr[i + c]<0x80)?ptr[i + c]:'.'); 94 /* check again for 0D0A, to avoid an extra \n if it's at width */ 95 if(nohex && (i + c + 2 < size) && ptr[i + c + 1] == 0x0D && 96 ptr[i + c + 2] == 0x0A) { 97 i += (c + 3 - width); 98 break; 99 } 100 } 101 fputc('\n', stderr); /* newline */ 102 } 103} 104 105static 106int my_trace(CURL *handle, curl_infotype type, 107 char *data, size_t size, 108 void *userp) 109{ 110 const char *text; 111 struct transfer *t = (struct transfer *)userp; 112 unsigned int num = t->num; 113 (void)handle; /* prevent compiler warning */ 114 115 switch(type) { 116 case CURLINFO_TEXT: 117 fprintf(stderr, "== %u Info: %s", num, data); 118 return 0; 119 case CURLINFO_HEADER_OUT: 120 text = "=> Send header"; 121 break; 122 case CURLINFO_DATA_OUT: 123 text = "=> Send data"; 124 break; 125 case CURLINFO_SSL_DATA_OUT: 126 text = "=> Send SSL data"; 127 break; 128 case CURLINFO_HEADER_IN: 129 text = "<= Recv header"; 130 break; 131 case CURLINFO_DATA_IN: 132 text = "<= Recv data"; 133 break; 134 case CURLINFO_SSL_DATA_IN: 135 text = "<= Recv SSL data"; 136 break; 137 default: /* in case a new one is introduced to shock us */ 138 return 0; 139 } 140 141 dump(text, num, (unsigned char *)data, size, 1); 142 return 0; 143} 144 145static void setup(struct transfer *t, int num) 146{ 147 char filename[128]; 148 CURL *hnd; 149 150 hnd = t->easy = curl_easy_init(); 151 152 curl_msnprintf(filename, 128, "dl-%d", num); 153 154 t->out = fopen(filename, "wb"); 155 if(!t->out) { 156 fprintf(stderr, "error: could not open file %s for writing: %s\n", 157 filename, strerror(errno)); 158 exit(1); 159 } 160 161 /* write to this file */ 162 curl_easy_setopt(hnd, CURLOPT_WRITEDATA, t->out); 163 164 /* set the same URL */ 165 curl_easy_setopt(hnd, CURLOPT_URL, "https://localhost:8443/index.html"); 166 167 /* please be verbose */ 168 curl_easy_setopt(hnd, CURLOPT_VERBOSE, 1L); 169 curl_easy_setopt(hnd, CURLOPT_DEBUGFUNCTION, my_trace); 170 curl_easy_setopt(hnd, CURLOPT_DEBUGDATA, t); 171 172 /* enlarge the receive buffer for potentially higher transfer speeds */ 173 curl_easy_setopt(hnd, CURLOPT_BUFFERSIZE, 100000L); 174 175 /* HTTP/2 please */ 176 curl_easy_setopt(hnd, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0); 177 178#if (CURLPIPE_MULTIPLEX > 0) 179 /* wait for pipe connection to confirm */ 180 curl_easy_setopt(hnd, CURLOPT_PIPEWAIT, 1L); 181#endif 182} 183 184/* 185 * Download many transfers over HTTP/2, using the same connection! 186 */ 187int main(int argc, char **argv) 188{ 189 struct transfer trans[NUM_HANDLES]; 190 CURLM *multi_handle; 191 int i; 192 int still_running = 0; /* keep number of running handles */ 193 int num_transfers; 194 if(argc > 1) { 195 /* if given a number, do that many transfers */ 196 num_transfers = atoi(argv[1]); 197 if((num_transfers < 1) || (num_transfers > NUM_HANDLES)) 198 num_transfers = 3; /* a suitable low default */ 199 } 200 else 201 num_transfers = 3; /* suitable default */ 202 203 /* init a multi stack */ 204 multi_handle = curl_multi_init(); 205 206 for(i = 0; i < num_transfers; i++) { 207 setup(&trans[i], i); 208 209 /* add the individual transfer */ 210 curl_multi_add_handle(multi_handle, trans[i].easy); 211 } 212 213 curl_multi_setopt(multi_handle, CURLMOPT_PIPELINING, CURLPIPE_MULTIPLEX); 214 215 do { 216 CURLMcode mc = curl_multi_perform(multi_handle, &still_running); 217 218 if(still_running) 219 /* wait for activity, timeout or "nothing" */ 220 mc = curl_multi_poll(multi_handle, NULL, 0, 1000, NULL); 221 222 if(mc) 223 break; 224 } while(still_running); 225 226 for(i = 0; i < num_transfers; i++) { 227 curl_multi_remove_handle(multi_handle, trans[i].easy); 228 curl_easy_cleanup(trans[i].easy); 229 } 230 231 curl_multi_cleanup(multi_handle); 232 233 return 0; 234} 235