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 * HTTP/2 server push 2613498266Sopenharmony_ci * </DESC> 2713498266Sopenharmony_ci */ 2813498266Sopenharmony_ci 2913498266Sopenharmony_ci/* curl stuff */ 3013498266Sopenharmony_ci#include <curl/curl.h> 3113498266Sopenharmony_ci#include <curl/mprintf.h> 3213498266Sopenharmony_ci 3313498266Sopenharmony_ci#include <stdio.h> 3413498266Sopenharmony_ci#include <stdlib.h> 3513498266Sopenharmony_ci#include <string.h> 3613498266Sopenharmony_ci 3713498266Sopenharmony_ci/* somewhat unix-specific */ 3813498266Sopenharmony_ci#include <sys/time.h> 3913498266Sopenharmony_ci#include <unistd.h> 4013498266Sopenharmony_ci 4113498266Sopenharmony_ci#ifndef CURLPIPE_MULTIPLEX 4213498266Sopenharmony_ci#error "too old libcurl, cannot do HTTP/2 server push!" 4313498266Sopenharmony_ci#endif 4413498266Sopenharmony_ci 4513498266Sopenharmony_cistatic int verbose = 1; 4613498266Sopenharmony_ci 4713498266Sopenharmony_cistatic 4813498266Sopenharmony_ciint my_trace(CURL *handle, curl_infotype type, 4913498266Sopenharmony_ci char *data, size_t size, 5013498266Sopenharmony_ci void *userp) 5113498266Sopenharmony_ci{ 5213498266Sopenharmony_ci const char *text; 5313498266Sopenharmony_ci (void)handle; /* prevent compiler warning */ 5413498266Sopenharmony_ci (void)userp; 5513498266Sopenharmony_ci 5613498266Sopenharmony_ci switch(type) { 5713498266Sopenharmony_ci case CURLINFO_TEXT: 5813498266Sopenharmony_ci fprintf(stderr, "== Info: %s", data); 5913498266Sopenharmony_ci return 0; 6013498266Sopenharmony_ci case CURLINFO_HEADER_OUT: 6113498266Sopenharmony_ci text = "=> Send header"; 6213498266Sopenharmony_ci break; 6313498266Sopenharmony_ci case CURLINFO_DATA_OUT: 6413498266Sopenharmony_ci if(verbose <= 1) 6513498266Sopenharmony_ci return 0; 6613498266Sopenharmony_ci text = "=> Send data"; 6713498266Sopenharmony_ci break; 6813498266Sopenharmony_ci case CURLINFO_HEADER_IN: 6913498266Sopenharmony_ci text = "<= Recv header"; 7013498266Sopenharmony_ci break; 7113498266Sopenharmony_ci case CURLINFO_DATA_IN: 7213498266Sopenharmony_ci if(verbose <= 1) 7313498266Sopenharmony_ci return 0; 7413498266Sopenharmony_ci text = "<= Recv data"; 7513498266Sopenharmony_ci break; 7613498266Sopenharmony_ci default: /* in case a new one is introduced to shock us */ 7713498266Sopenharmony_ci return 0; 7813498266Sopenharmony_ci } 7913498266Sopenharmony_ci 8013498266Sopenharmony_ci fprintf(stderr, "%s, %lu bytes (0x%lx)\n", 8113498266Sopenharmony_ci text, (unsigned long)size, (unsigned long)size); 8213498266Sopenharmony_ci return 0; 8313498266Sopenharmony_ci} 8413498266Sopenharmony_ci 8513498266Sopenharmony_cistruct transfer { 8613498266Sopenharmony_ci int idx; 8713498266Sopenharmony_ci CURL *easy; 8813498266Sopenharmony_ci char filename[128]; 8913498266Sopenharmony_ci FILE *out; 9013498266Sopenharmony_ci curl_off_t recv_size; 9113498266Sopenharmony_ci curl_off_t pause_at; 9213498266Sopenharmony_ci int started; 9313498266Sopenharmony_ci int paused; 9413498266Sopenharmony_ci int resumed; 9513498266Sopenharmony_ci int done; 9613498266Sopenharmony_ci}; 9713498266Sopenharmony_ci 9813498266Sopenharmony_cistatic size_t transfer_count = 1; 9913498266Sopenharmony_cistatic struct transfer *transfers; 10013498266Sopenharmony_ci 10113498266Sopenharmony_cistatic struct transfer *get_transfer_for_easy(CURL *easy) 10213498266Sopenharmony_ci{ 10313498266Sopenharmony_ci size_t i; 10413498266Sopenharmony_ci for(i = 0; i < transfer_count; ++i) { 10513498266Sopenharmony_ci if(easy == transfers[i].easy) 10613498266Sopenharmony_ci return &transfers[i]; 10713498266Sopenharmony_ci } 10813498266Sopenharmony_ci return NULL; 10913498266Sopenharmony_ci} 11013498266Sopenharmony_ci 11113498266Sopenharmony_cistatic size_t my_write_cb(char *buf, size_t nitems, size_t buflen, 11213498266Sopenharmony_ci void *userdata) 11313498266Sopenharmony_ci{ 11413498266Sopenharmony_ci struct transfer *t = userdata; 11513498266Sopenharmony_ci size_t nwritten; 11613498266Sopenharmony_ci 11713498266Sopenharmony_ci if(!t->resumed && 11813498266Sopenharmony_ci t->recv_size < t->pause_at && 11913498266Sopenharmony_ci ((t->recv_size + (curl_off_t)(nitems * buflen)) >= t->pause_at)) { 12013498266Sopenharmony_ci fprintf(stderr, "[t-%d] PAUSE\n", t->idx); 12113498266Sopenharmony_ci t->paused = 1; 12213498266Sopenharmony_ci return CURL_WRITEFUNC_PAUSE; 12313498266Sopenharmony_ci } 12413498266Sopenharmony_ci 12513498266Sopenharmony_ci if(!t->out) { 12613498266Sopenharmony_ci curl_msnprintf(t->filename, sizeof(t->filename)-1, "download_%u.data", 12713498266Sopenharmony_ci t->idx); 12813498266Sopenharmony_ci t->out = fopen(t->filename, "wb"); 12913498266Sopenharmony_ci if(!t->out) 13013498266Sopenharmony_ci return 0; 13113498266Sopenharmony_ci } 13213498266Sopenharmony_ci 13313498266Sopenharmony_ci nwritten = fwrite(buf, nitems, buflen, t->out); 13413498266Sopenharmony_ci if(nwritten < buflen) { 13513498266Sopenharmony_ci fprintf(stderr, "[t-%d] write failure\n", t->idx); 13613498266Sopenharmony_ci return 0; 13713498266Sopenharmony_ci } 13813498266Sopenharmony_ci t->recv_size += (curl_off_t)nwritten; 13913498266Sopenharmony_ci return (size_t)nwritten; 14013498266Sopenharmony_ci} 14113498266Sopenharmony_ci 14213498266Sopenharmony_cistatic int setup(CURL *hnd, const char *url, struct transfer *t) 14313498266Sopenharmony_ci{ 14413498266Sopenharmony_ci curl_easy_setopt(hnd, CURLOPT_URL, url); 14513498266Sopenharmony_ci curl_easy_setopt(hnd, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0); 14613498266Sopenharmony_ci curl_easy_setopt(hnd, CURLOPT_SSL_VERIFYPEER, 0L); 14713498266Sopenharmony_ci curl_easy_setopt(hnd, CURLOPT_SSL_VERIFYHOST, 0L); 14813498266Sopenharmony_ci 14913498266Sopenharmony_ci curl_easy_setopt(hnd, CURLOPT_WRITEFUNCTION, my_write_cb); 15013498266Sopenharmony_ci curl_easy_setopt(hnd, CURLOPT_WRITEDATA, t); 15113498266Sopenharmony_ci 15213498266Sopenharmony_ci /* please be verbose */ 15313498266Sopenharmony_ci if(verbose) { 15413498266Sopenharmony_ci curl_easy_setopt(hnd, CURLOPT_VERBOSE, 1L); 15513498266Sopenharmony_ci curl_easy_setopt(hnd, CURLOPT_DEBUGFUNCTION, my_trace); 15613498266Sopenharmony_ci } 15713498266Sopenharmony_ci 15813498266Sopenharmony_ci#if (CURLPIPE_MULTIPLEX > 0) 15913498266Sopenharmony_ci /* wait for pipe connection to confirm */ 16013498266Sopenharmony_ci curl_easy_setopt(hnd, CURLOPT_PIPEWAIT, 1L); 16113498266Sopenharmony_ci#endif 16213498266Sopenharmony_ci return 0; /* all is good */ 16313498266Sopenharmony_ci} 16413498266Sopenharmony_ci 16513498266Sopenharmony_cistatic void usage(const char *msg) 16613498266Sopenharmony_ci{ 16713498266Sopenharmony_ci if(msg) 16813498266Sopenharmony_ci fprintf(stderr, "%s\n", msg); 16913498266Sopenharmony_ci fprintf(stderr, 17013498266Sopenharmony_ci "usage: [options] url\n" 17113498266Sopenharmony_ci " download a url with following options:\n" 17213498266Sopenharmony_ci " -m number max parallel downloads\n" 17313498266Sopenharmony_ci " -n number total downloads\n" 17413498266Sopenharmony_ci " -P number pause transfer after `number` response bytes\n" 17513498266Sopenharmony_ci ); 17613498266Sopenharmony_ci} 17713498266Sopenharmony_ci 17813498266Sopenharmony_ci/* 17913498266Sopenharmony_ci * Download a file over HTTP/2, take care of server push. 18013498266Sopenharmony_ci */ 18113498266Sopenharmony_ciint main(int argc, char *argv[]) 18213498266Sopenharmony_ci{ 18313498266Sopenharmony_ci CURLM *multi_handle; 18413498266Sopenharmony_ci struct CURLMsg *m; 18513498266Sopenharmony_ci const char *url; 18613498266Sopenharmony_ci size_t i, n, max_parallel = 1; 18713498266Sopenharmony_ci size_t active_transfers; 18813498266Sopenharmony_ci size_t pause_offset = 0; 18913498266Sopenharmony_ci int abort_paused = 0; 19013498266Sopenharmony_ci struct transfer *t; 19113498266Sopenharmony_ci int ch; 19213498266Sopenharmony_ci 19313498266Sopenharmony_ci while((ch = getopt(argc, argv, "ahm:n:P:")) != -1) { 19413498266Sopenharmony_ci switch(ch) { 19513498266Sopenharmony_ci case 'h': 19613498266Sopenharmony_ci usage(NULL); 19713498266Sopenharmony_ci return 2; 19813498266Sopenharmony_ci case 'a': 19913498266Sopenharmony_ci abort_paused = 1; 20013498266Sopenharmony_ci break; 20113498266Sopenharmony_ci case 'm': 20213498266Sopenharmony_ci max_parallel = (size_t)strtol(optarg, NULL, 10); 20313498266Sopenharmony_ci break; 20413498266Sopenharmony_ci case 'n': 20513498266Sopenharmony_ci transfer_count = (size_t)strtol(optarg, NULL, 10); 20613498266Sopenharmony_ci break; 20713498266Sopenharmony_ci case 'P': 20813498266Sopenharmony_ci pause_offset = (size_t)strtol(optarg, NULL, 10); 20913498266Sopenharmony_ci break; 21013498266Sopenharmony_ci default: 21113498266Sopenharmony_ci usage("invalid option"); 21213498266Sopenharmony_ci return 1; 21313498266Sopenharmony_ci } 21413498266Sopenharmony_ci } 21513498266Sopenharmony_ci argc -= optind; 21613498266Sopenharmony_ci argv += optind; 21713498266Sopenharmony_ci 21813498266Sopenharmony_ci if(argc != 1) { 21913498266Sopenharmony_ci usage("not enough arguments"); 22013498266Sopenharmony_ci return 2; 22113498266Sopenharmony_ci } 22213498266Sopenharmony_ci url = argv[0]; 22313498266Sopenharmony_ci 22413498266Sopenharmony_ci transfers = calloc(transfer_count, sizeof(*transfers)); 22513498266Sopenharmony_ci if(!transfers) { 22613498266Sopenharmony_ci fprintf(stderr, "error allocating transfer structs\n"); 22713498266Sopenharmony_ci return 1; 22813498266Sopenharmony_ci } 22913498266Sopenharmony_ci 23013498266Sopenharmony_ci multi_handle = curl_multi_init(); 23113498266Sopenharmony_ci curl_multi_setopt(multi_handle, CURLMOPT_PIPELINING, CURLPIPE_MULTIPLEX); 23213498266Sopenharmony_ci 23313498266Sopenharmony_ci active_transfers = 0; 23413498266Sopenharmony_ci for(i = 0; i < transfer_count; ++i) { 23513498266Sopenharmony_ci t = &transfers[i]; 23613498266Sopenharmony_ci t->idx = (int)i; 23713498266Sopenharmony_ci t->pause_at = (curl_off_t)(pause_offset * i); 23813498266Sopenharmony_ci } 23913498266Sopenharmony_ci 24013498266Sopenharmony_ci n = (max_parallel < transfer_count)? max_parallel : transfer_count; 24113498266Sopenharmony_ci for(i = 0; i < n; ++i) { 24213498266Sopenharmony_ci t = &transfers[i]; 24313498266Sopenharmony_ci t->easy = curl_easy_init(); 24413498266Sopenharmony_ci if(!t->easy || setup(t->easy, url, t)) { 24513498266Sopenharmony_ci fprintf(stderr, "[t-%d] FAILED setup\n", (int)i); 24613498266Sopenharmony_ci return 1; 24713498266Sopenharmony_ci } 24813498266Sopenharmony_ci curl_multi_add_handle(multi_handle, t->easy); 24913498266Sopenharmony_ci t->started = 1; 25013498266Sopenharmony_ci ++active_transfers; 25113498266Sopenharmony_ci fprintf(stderr, "[t-%d] STARTED\n", t->idx); 25213498266Sopenharmony_ci } 25313498266Sopenharmony_ci 25413498266Sopenharmony_ci do { 25513498266Sopenharmony_ci int still_running; /* keep number of running handles */ 25613498266Sopenharmony_ci CURLMcode mc = curl_multi_perform(multi_handle, &still_running); 25713498266Sopenharmony_ci 25813498266Sopenharmony_ci if(still_running) { 25913498266Sopenharmony_ci /* wait for activity, timeout or "nothing" */ 26013498266Sopenharmony_ci mc = curl_multi_poll(multi_handle, NULL, 0, 1000, NULL); 26113498266Sopenharmony_ci fprintf(stderr, "curl_multi_poll() -> %d\n", mc); 26213498266Sopenharmony_ci } 26313498266Sopenharmony_ci 26413498266Sopenharmony_ci if(mc) 26513498266Sopenharmony_ci break; 26613498266Sopenharmony_ci 26713498266Sopenharmony_ci do { 26813498266Sopenharmony_ci int msgq = 0; 26913498266Sopenharmony_ci m = curl_multi_info_read(multi_handle, &msgq); 27013498266Sopenharmony_ci if(m && (m->msg == CURLMSG_DONE)) { 27113498266Sopenharmony_ci CURL *e = m->easy_handle; 27213498266Sopenharmony_ci active_transfers--; 27313498266Sopenharmony_ci curl_multi_remove_handle(multi_handle, e); 27413498266Sopenharmony_ci t = get_transfer_for_easy(e); 27513498266Sopenharmony_ci if(t) { 27613498266Sopenharmony_ci t->done = 1; 27713498266Sopenharmony_ci } 27813498266Sopenharmony_ci else 27913498266Sopenharmony_ci curl_easy_cleanup(e); 28013498266Sopenharmony_ci } 28113498266Sopenharmony_ci else { 28213498266Sopenharmony_ci /* nothing happening, maintenance */ 28313498266Sopenharmony_ci if(abort_paused) { 28413498266Sopenharmony_ci /* abort paused transfers */ 28513498266Sopenharmony_ci for(i = 0; i < transfer_count; ++i) { 28613498266Sopenharmony_ci t = &transfers[i]; 28713498266Sopenharmony_ci if(!t->done && t->paused && t->easy) { 28813498266Sopenharmony_ci curl_multi_remove_handle(multi_handle, t->easy); 28913498266Sopenharmony_ci t->done = 1; 29013498266Sopenharmony_ci active_transfers--; 29113498266Sopenharmony_ci fprintf(stderr, "[t-%d] ABORTED\n", t->idx); 29213498266Sopenharmony_ci } 29313498266Sopenharmony_ci } 29413498266Sopenharmony_ci } 29513498266Sopenharmony_ci else { 29613498266Sopenharmony_ci /* resume one paused transfer */ 29713498266Sopenharmony_ci for(i = 0; i < transfer_count; ++i) { 29813498266Sopenharmony_ci t = &transfers[i]; 29913498266Sopenharmony_ci if(!t->done && t->paused) { 30013498266Sopenharmony_ci t->resumed = 1; 30113498266Sopenharmony_ci t->paused = 0; 30213498266Sopenharmony_ci curl_easy_pause(t->easy, CURLPAUSE_CONT); 30313498266Sopenharmony_ci fprintf(stderr, "[t-%d] RESUMED\n", t->idx); 30413498266Sopenharmony_ci break; 30513498266Sopenharmony_ci } 30613498266Sopenharmony_ci } 30713498266Sopenharmony_ci } 30813498266Sopenharmony_ci 30913498266Sopenharmony_ci while(active_transfers < max_parallel) { 31013498266Sopenharmony_ci for(i = 0; i < transfer_count; ++i) { 31113498266Sopenharmony_ci t = &transfers[i]; 31213498266Sopenharmony_ci if(!t->started) { 31313498266Sopenharmony_ci t->easy = curl_easy_init(); 31413498266Sopenharmony_ci if(!t->easy || setup(t->easy, url, t)) { 31513498266Sopenharmony_ci fprintf(stderr, "[t-%d] FAILEED setup\n", (int)i); 31613498266Sopenharmony_ci return 1; 31713498266Sopenharmony_ci } 31813498266Sopenharmony_ci curl_multi_add_handle(multi_handle, t->easy); 31913498266Sopenharmony_ci t->started = 1; 32013498266Sopenharmony_ci ++active_transfers; 32113498266Sopenharmony_ci fprintf(stderr, "[t-%d] STARTED\n", t->idx); 32213498266Sopenharmony_ci break; 32313498266Sopenharmony_ci } 32413498266Sopenharmony_ci } 32513498266Sopenharmony_ci /* all started */ 32613498266Sopenharmony_ci if(i == transfer_count) 32713498266Sopenharmony_ci break; 32813498266Sopenharmony_ci } 32913498266Sopenharmony_ci } 33013498266Sopenharmony_ci } while(m); 33113498266Sopenharmony_ci 33213498266Sopenharmony_ci } while(active_transfers); /* as long as we have transfers going */ 33313498266Sopenharmony_ci 33413498266Sopenharmony_ci for(i = 0; i < transfer_count; ++i) { 33513498266Sopenharmony_ci t = &transfers[i]; 33613498266Sopenharmony_ci if(t->out) { 33713498266Sopenharmony_ci fclose(t->out); 33813498266Sopenharmony_ci t->out = NULL; 33913498266Sopenharmony_ci } 34013498266Sopenharmony_ci if(t->easy) { 34113498266Sopenharmony_ci curl_easy_cleanup(t->easy); 34213498266Sopenharmony_ci t->easy = NULL; 34313498266Sopenharmony_ci } 34413498266Sopenharmony_ci } 34513498266Sopenharmony_ci free(transfers); 34613498266Sopenharmony_ci 34713498266Sopenharmony_ci curl_multi_cleanup(multi_handle); 34813498266Sopenharmony_ci 34913498266Sopenharmony_ci return 0; 35013498266Sopenharmony_ci} 351