113498266Sopenharmony_ci/*************************************************************************** 213498266Sopenharmony_ci * _ _ ____ _ 313498266Sopenharmony_ci * Project ___| | | | _ \| | 413498266Sopenharmony_ci * / __| | | | |_) | | 513498266Sopenharmony_ci * | (__| |_| | _ <| |___ 613498266Sopenharmony_ci * \___|\___/|_| \_\_____| 713498266Sopenharmony_ci * 813498266Sopenharmony_ci * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. 913498266Sopenharmony_ci * 1013498266Sopenharmony_ci * This software is licensed as described in the file COPYING, which 1113498266Sopenharmony_ci * you should have received as part of this distribution. The terms 1213498266Sopenharmony_ci * are also available at https://curl.se/docs/copyright.html. 1313498266Sopenharmony_ci * 1413498266Sopenharmony_ci * You may opt to use, copy, modify, merge, publish, distribute and/or sell 1513498266Sopenharmony_ci * copies of the Software, and permit persons to whom the Software is 1613498266Sopenharmony_ci * furnished to do so, under the terms of the COPYING file. 1713498266Sopenharmony_ci * 1813498266Sopenharmony_ci * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 1913498266Sopenharmony_ci * KIND, either express or implied. 2013498266Sopenharmony_ci * 2113498266Sopenharmony_ci * SPDX-License-Identifier: curl 2213498266Sopenharmony_ci * 2313498266Sopenharmony_ci ***************************************************************************/ 2413498266Sopenharmony_ci/* <DESC> 2513498266Sopenharmony_ci * Multiplexed HTTP/2 downloads over a single connection 2613498266Sopenharmony_ci * </DESC> 2713498266Sopenharmony_ci */ 2813498266Sopenharmony_ci#include <stdio.h> 2913498266Sopenharmony_ci#include <stdlib.h> 3013498266Sopenharmony_ci#include <string.h> 3113498266Sopenharmony_ci#include <errno.h> 3213498266Sopenharmony_ci 3313498266Sopenharmony_ci/* somewhat unix-specific */ 3413498266Sopenharmony_ci#include <sys/time.h> 3513498266Sopenharmony_ci#include <unistd.h> 3613498266Sopenharmony_ci 3713498266Sopenharmony_ci/* curl stuff */ 3813498266Sopenharmony_ci#include <curl/curl.h> 3913498266Sopenharmony_ci#include <curl/mprintf.h> 4013498266Sopenharmony_ci 4113498266Sopenharmony_ci#ifndef CURLPIPE_MULTIPLEX 4213498266Sopenharmony_ci/* This little trick will just make sure that we do not enable pipelining for 4313498266Sopenharmony_ci libcurls old enough to not have this symbol. It is _not_ defined to zero in 4413498266Sopenharmony_ci a recent libcurl header. */ 4513498266Sopenharmony_ci#define CURLPIPE_MULTIPLEX 0 4613498266Sopenharmony_ci#endif 4713498266Sopenharmony_ci 4813498266Sopenharmony_cistruct transfer { 4913498266Sopenharmony_ci CURL *easy; 5013498266Sopenharmony_ci unsigned int num; 5113498266Sopenharmony_ci FILE *out; 5213498266Sopenharmony_ci}; 5313498266Sopenharmony_ci 5413498266Sopenharmony_ci#define NUM_HANDLES 1000 5513498266Sopenharmony_ci 5613498266Sopenharmony_cistatic 5713498266Sopenharmony_civoid dump(const char *text, unsigned int num, unsigned char *ptr, size_t size, 5813498266Sopenharmony_ci char nohex) 5913498266Sopenharmony_ci{ 6013498266Sopenharmony_ci size_t i; 6113498266Sopenharmony_ci size_t c; 6213498266Sopenharmony_ci 6313498266Sopenharmony_ci unsigned int width = 0x10; 6413498266Sopenharmony_ci 6513498266Sopenharmony_ci if(nohex) 6613498266Sopenharmony_ci /* without the hex output, we can fit more on screen */ 6713498266Sopenharmony_ci width = 0x40; 6813498266Sopenharmony_ci 6913498266Sopenharmony_ci fprintf(stderr, "%u %s, %lu bytes (0x%lx)\n", 7013498266Sopenharmony_ci num, text, (unsigned long)size, (unsigned long)size); 7113498266Sopenharmony_ci 7213498266Sopenharmony_ci for(i = 0; i<size; i += width) { 7313498266Sopenharmony_ci 7413498266Sopenharmony_ci fprintf(stderr, "%4.4lx: ", (unsigned long)i); 7513498266Sopenharmony_ci 7613498266Sopenharmony_ci if(!nohex) { 7713498266Sopenharmony_ci /* hex not disabled, show it */ 7813498266Sopenharmony_ci for(c = 0; c < width; c++) 7913498266Sopenharmony_ci if(i + c < size) 8013498266Sopenharmony_ci fprintf(stderr, "%02x ", ptr[i + c]); 8113498266Sopenharmony_ci else 8213498266Sopenharmony_ci fputs(" ", stderr); 8313498266Sopenharmony_ci } 8413498266Sopenharmony_ci 8513498266Sopenharmony_ci for(c = 0; (c < width) && (i + c < size); c++) { 8613498266Sopenharmony_ci /* check for 0D0A; if found, skip past and start a new line of output */ 8713498266Sopenharmony_ci if(nohex && (i + c + 1 < size) && ptr[i + c] == 0x0D && 8813498266Sopenharmony_ci ptr[i + c + 1] == 0x0A) { 8913498266Sopenharmony_ci i += (c + 2 - width); 9013498266Sopenharmony_ci break; 9113498266Sopenharmony_ci } 9213498266Sopenharmony_ci fprintf(stderr, "%c", 9313498266Sopenharmony_ci (ptr[i + c] >= 0x20) && (ptr[i + c]<0x80)?ptr[i + c]:'.'); 9413498266Sopenharmony_ci /* check again for 0D0A, to avoid an extra \n if it's at width */ 9513498266Sopenharmony_ci if(nohex && (i + c + 2 < size) && ptr[i + c + 1] == 0x0D && 9613498266Sopenharmony_ci ptr[i + c + 2] == 0x0A) { 9713498266Sopenharmony_ci i += (c + 3 - width); 9813498266Sopenharmony_ci break; 9913498266Sopenharmony_ci } 10013498266Sopenharmony_ci } 10113498266Sopenharmony_ci fputc('\n', stderr); /* newline */ 10213498266Sopenharmony_ci } 10313498266Sopenharmony_ci} 10413498266Sopenharmony_ci 10513498266Sopenharmony_cistatic 10613498266Sopenharmony_ciint my_trace(CURL *handle, curl_infotype type, 10713498266Sopenharmony_ci char *data, size_t size, 10813498266Sopenharmony_ci void *userp) 10913498266Sopenharmony_ci{ 11013498266Sopenharmony_ci const char *text; 11113498266Sopenharmony_ci struct transfer *t = (struct transfer *)userp; 11213498266Sopenharmony_ci unsigned int num = t->num; 11313498266Sopenharmony_ci (void)handle; /* prevent compiler warning */ 11413498266Sopenharmony_ci 11513498266Sopenharmony_ci switch(type) { 11613498266Sopenharmony_ci case CURLINFO_TEXT: 11713498266Sopenharmony_ci fprintf(stderr, "== %u Info: %s", num, data); 11813498266Sopenharmony_ci return 0; 11913498266Sopenharmony_ci case CURLINFO_HEADER_OUT: 12013498266Sopenharmony_ci text = "=> Send header"; 12113498266Sopenharmony_ci break; 12213498266Sopenharmony_ci case CURLINFO_DATA_OUT: 12313498266Sopenharmony_ci text = "=> Send data"; 12413498266Sopenharmony_ci break; 12513498266Sopenharmony_ci case CURLINFO_SSL_DATA_OUT: 12613498266Sopenharmony_ci text = "=> Send SSL data"; 12713498266Sopenharmony_ci break; 12813498266Sopenharmony_ci case CURLINFO_HEADER_IN: 12913498266Sopenharmony_ci text = "<= Recv header"; 13013498266Sopenharmony_ci break; 13113498266Sopenharmony_ci case CURLINFO_DATA_IN: 13213498266Sopenharmony_ci text = "<= Recv data"; 13313498266Sopenharmony_ci break; 13413498266Sopenharmony_ci case CURLINFO_SSL_DATA_IN: 13513498266Sopenharmony_ci text = "<= Recv SSL data"; 13613498266Sopenharmony_ci break; 13713498266Sopenharmony_ci default: /* in case a new one is introduced to shock us */ 13813498266Sopenharmony_ci return 0; 13913498266Sopenharmony_ci } 14013498266Sopenharmony_ci 14113498266Sopenharmony_ci dump(text, num, (unsigned char *)data, size, 1); 14213498266Sopenharmony_ci return 0; 14313498266Sopenharmony_ci} 14413498266Sopenharmony_ci 14513498266Sopenharmony_cistatic void setup(struct transfer *t, int num) 14613498266Sopenharmony_ci{ 14713498266Sopenharmony_ci char filename[128]; 14813498266Sopenharmony_ci CURL *hnd; 14913498266Sopenharmony_ci 15013498266Sopenharmony_ci hnd = t->easy = curl_easy_init(); 15113498266Sopenharmony_ci 15213498266Sopenharmony_ci curl_msnprintf(filename, 128, "dl-%d", num); 15313498266Sopenharmony_ci 15413498266Sopenharmony_ci t->out = fopen(filename, "wb"); 15513498266Sopenharmony_ci if(!t->out) { 15613498266Sopenharmony_ci fprintf(stderr, "error: could not open file %s for writing: %s\n", 15713498266Sopenharmony_ci filename, strerror(errno)); 15813498266Sopenharmony_ci exit(1); 15913498266Sopenharmony_ci } 16013498266Sopenharmony_ci 16113498266Sopenharmony_ci /* write to this file */ 16213498266Sopenharmony_ci curl_easy_setopt(hnd, CURLOPT_WRITEDATA, t->out); 16313498266Sopenharmony_ci 16413498266Sopenharmony_ci /* set the same URL */ 16513498266Sopenharmony_ci curl_easy_setopt(hnd, CURLOPT_URL, "https://localhost:8443/index.html"); 16613498266Sopenharmony_ci 16713498266Sopenharmony_ci /* please be verbose */ 16813498266Sopenharmony_ci curl_easy_setopt(hnd, CURLOPT_VERBOSE, 1L); 16913498266Sopenharmony_ci curl_easy_setopt(hnd, CURLOPT_DEBUGFUNCTION, my_trace); 17013498266Sopenharmony_ci curl_easy_setopt(hnd, CURLOPT_DEBUGDATA, t); 17113498266Sopenharmony_ci 17213498266Sopenharmony_ci /* enlarge the receive buffer for potentially higher transfer speeds */ 17313498266Sopenharmony_ci curl_easy_setopt(hnd, CURLOPT_BUFFERSIZE, 100000L); 17413498266Sopenharmony_ci 17513498266Sopenharmony_ci /* HTTP/2 please */ 17613498266Sopenharmony_ci curl_easy_setopt(hnd, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0); 17713498266Sopenharmony_ci 17813498266Sopenharmony_ci#if (CURLPIPE_MULTIPLEX > 0) 17913498266Sopenharmony_ci /* wait for pipe connection to confirm */ 18013498266Sopenharmony_ci curl_easy_setopt(hnd, CURLOPT_PIPEWAIT, 1L); 18113498266Sopenharmony_ci#endif 18213498266Sopenharmony_ci} 18313498266Sopenharmony_ci 18413498266Sopenharmony_ci/* 18513498266Sopenharmony_ci * Download many transfers over HTTP/2, using the same connection! 18613498266Sopenharmony_ci */ 18713498266Sopenharmony_ciint main(int argc, char **argv) 18813498266Sopenharmony_ci{ 18913498266Sopenharmony_ci struct transfer trans[NUM_HANDLES]; 19013498266Sopenharmony_ci CURLM *multi_handle; 19113498266Sopenharmony_ci int i; 19213498266Sopenharmony_ci int still_running = 0; /* keep number of running handles */ 19313498266Sopenharmony_ci int num_transfers; 19413498266Sopenharmony_ci if(argc > 1) { 19513498266Sopenharmony_ci /* if given a number, do that many transfers */ 19613498266Sopenharmony_ci num_transfers = atoi(argv[1]); 19713498266Sopenharmony_ci if((num_transfers < 1) || (num_transfers > NUM_HANDLES)) 19813498266Sopenharmony_ci num_transfers = 3; /* a suitable low default */ 19913498266Sopenharmony_ci } 20013498266Sopenharmony_ci else 20113498266Sopenharmony_ci num_transfers = 3; /* suitable default */ 20213498266Sopenharmony_ci 20313498266Sopenharmony_ci /* init a multi stack */ 20413498266Sopenharmony_ci multi_handle = curl_multi_init(); 20513498266Sopenharmony_ci 20613498266Sopenharmony_ci for(i = 0; i < num_transfers; i++) { 20713498266Sopenharmony_ci setup(&trans[i], i); 20813498266Sopenharmony_ci 20913498266Sopenharmony_ci /* add the individual transfer */ 21013498266Sopenharmony_ci curl_multi_add_handle(multi_handle, trans[i].easy); 21113498266Sopenharmony_ci } 21213498266Sopenharmony_ci 21313498266Sopenharmony_ci curl_multi_setopt(multi_handle, CURLMOPT_PIPELINING, CURLPIPE_MULTIPLEX); 21413498266Sopenharmony_ci 21513498266Sopenharmony_ci do { 21613498266Sopenharmony_ci CURLMcode mc = curl_multi_perform(multi_handle, &still_running); 21713498266Sopenharmony_ci 21813498266Sopenharmony_ci if(still_running) 21913498266Sopenharmony_ci /* wait for activity, timeout or "nothing" */ 22013498266Sopenharmony_ci mc = curl_multi_poll(multi_handle, NULL, 0, 1000, NULL); 22113498266Sopenharmony_ci 22213498266Sopenharmony_ci if(mc) 22313498266Sopenharmony_ci break; 22413498266Sopenharmony_ci } while(still_running); 22513498266Sopenharmony_ci 22613498266Sopenharmony_ci for(i = 0; i < num_transfers; i++) { 22713498266Sopenharmony_ci curl_multi_remove_handle(multi_handle, trans[i].easy); 22813498266Sopenharmony_ci curl_easy_cleanup(trans[i].easy); 22913498266Sopenharmony_ci } 23013498266Sopenharmony_ci 23113498266Sopenharmony_ci curl_multi_cleanup(multi_handle); 23213498266Sopenharmony_ci 23313498266Sopenharmony_ci return 0; 23413498266Sopenharmony_ci} 235