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 4613498266Sopenharmony_civoid dump(const char *text, unsigned char *ptr, size_t size, 4713498266Sopenharmony_ci char nohex) 4813498266Sopenharmony_ci{ 4913498266Sopenharmony_ci size_t i; 5013498266Sopenharmony_ci size_t c; 5113498266Sopenharmony_ci 5213498266Sopenharmony_ci unsigned int width = 0x10; 5313498266Sopenharmony_ci 5413498266Sopenharmony_ci if(nohex) 5513498266Sopenharmony_ci /* without the hex output, we can fit more on screen */ 5613498266Sopenharmony_ci width = 0x40; 5713498266Sopenharmony_ci 5813498266Sopenharmony_ci fprintf(stderr, "%s, %lu bytes (0x%lx)\n", 5913498266Sopenharmony_ci text, (unsigned long)size, (unsigned long)size); 6013498266Sopenharmony_ci 6113498266Sopenharmony_ci for(i = 0; i<size; i += width) { 6213498266Sopenharmony_ci 6313498266Sopenharmony_ci fprintf(stderr, "%4.4lx: ", (unsigned long)i); 6413498266Sopenharmony_ci 6513498266Sopenharmony_ci if(!nohex) { 6613498266Sopenharmony_ci /* hex not disabled, show it */ 6713498266Sopenharmony_ci for(c = 0; c < width; c++) 6813498266Sopenharmony_ci if(i + c < size) 6913498266Sopenharmony_ci fprintf(stderr, "%02x ", ptr[i + c]); 7013498266Sopenharmony_ci else 7113498266Sopenharmony_ci fputs(" ", stderr); 7213498266Sopenharmony_ci } 7313498266Sopenharmony_ci 7413498266Sopenharmony_ci for(c = 0; (c < width) && (i + c < size); c++) { 7513498266Sopenharmony_ci /* check for 0D0A; if found, skip past and start a new line of output */ 7613498266Sopenharmony_ci if(nohex && (i + c + 1 < size) && ptr[i + c] == 0x0D && 7713498266Sopenharmony_ci ptr[i + c + 1] == 0x0A) { 7813498266Sopenharmony_ci i += (c + 2 - width); 7913498266Sopenharmony_ci break; 8013498266Sopenharmony_ci } 8113498266Sopenharmony_ci fprintf(stderr, "%c", 8213498266Sopenharmony_ci (ptr[i + c] >= 0x20) && (ptr[i + c]<0x80)?ptr[i + c]:'.'); 8313498266Sopenharmony_ci /* check again for 0D0A, to avoid an extra \n if it's at width */ 8413498266Sopenharmony_ci if(nohex && (i + c + 2 < size) && ptr[i + c + 1] == 0x0D && 8513498266Sopenharmony_ci ptr[i + c + 2] == 0x0A) { 8613498266Sopenharmony_ci i += (c + 3 - width); 8713498266Sopenharmony_ci break; 8813498266Sopenharmony_ci } 8913498266Sopenharmony_ci } 9013498266Sopenharmony_ci fputc('\n', stderr); /* newline */ 9113498266Sopenharmony_ci } 9213498266Sopenharmony_ci} 9313498266Sopenharmony_ci 9413498266Sopenharmony_cistatic 9513498266Sopenharmony_ciint my_trace(CURL *handle, curl_infotype type, 9613498266Sopenharmony_ci char *data, size_t size, 9713498266Sopenharmony_ci void *userp) 9813498266Sopenharmony_ci{ 9913498266Sopenharmony_ci const char *text; 10013498266Sopenharmony_ci (void)handle; /* prevent compiler warning */ 10113498266Sopenharmony_ci (void)userp; 10213498266Sopenharmony_ci switch(type) { 10313498266Sopenharmony_ci case CURLINFO_TEXT: 10413498266Sopenharmony_ci fprintf(stderr, "== Info: %s", data); 10513498266Sopenharmony_ci return 0; 10613498266Sopenharmony_ci case CURLINFO_HEADER_OUT: 10713498266Sopenharmony_ci text = "=> Send header"; 10813498266Sopenharmony_ci break; 10913498266Sopenharmony_ci case CURLINFO_DATA_OUT: 11013498266Sopenharmony_ci text = "=> Send data"; 11113498266Sopenharmony_ci break; 11213498266Sopenharmony_ci case CURLINFO_SSL_DATA_OUT: 11313498266Sopenharmony_ci text = "=> Send SSL data"; 11413498266Sopenharmony_ci break; 11513498266Sopenharmony_ci case CURLINFO_HEADER_IN: 11613498266Sopenharmony_ci text = "<= Recv header"; 11713498266Sopenharmony_ci break; 11813498266Sopenharmony_ci case CURLINFO_DATA_IN: 11913498266Sopenharmony_ci text = "<= Recv data"; 12013498266Sopenharmony_ci break; 12113498266Sopenharmony_ci case CURLINFO_SSL_DATA_IN: 12213498266Sopenharmony_ci text = "<= Recv SSL data"; 12313498266Sopenharmony_ci break; 12413498266Sopenharmony_ci default: /* in case a new one is introduced to shock us */ 12513498266Sopenharmony_ci return 0; 12613498266Sopenharmony_ci } 12713498266Sopenharmony_ci 12813498266Sopenharmony_ci dump(text, (unsigned char *)data, size, 1); 12913498266Sopenharmony_ci return 0; 13013498266Sopenharmony_ci} 13113498266Sopenharmony_ci 13213498266Sopenharmony_ci#define OUTPUTFILE "download_0.data" 13313498266Sopenharmony_ci 13413498266Sopenharmony_cistatic int setup(CURL *hnd, const char *url) 13513498266Sopenharmony_ci{ 13613498266Sopenharmony_ci FILE *out = fopen(OUTPUTFILE, "wb"); 13713498266Sopenharmony_ci if(!out) 13813498266Sopenharmony_ci /* failed */ 13913498266Sopenharmony_ci return 1; 14013498266Sopenharmony_ci 14113498266Sopenharmony_ci curl_easy_setopt(hnd, CURLOPT_URL, url); 14213498266Sopenharmony_ci curl_easy_setopt(hnd, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0); 14313498266Sopenharmony_ci curl_easy_setopt(hnd, CURLOPT_SSL_VERIFYPEER, 0L); 14413498266Sopenharmony_ci curl_easy_setopt(hnd, CURLOPT_SSL_VERIFYHOST, 0L); 14513498266Sopenharmony_ci 14613498266Sopenharmony_ci curl_easy_setopt(hnd, CURLOPT_WRITEDATA, out); 14713498266Sopenharmony_ci 14813498266Sopenharmony_ci /* please be verbose */ 14913498266Sopenharmony_ci curl_easy_setopt(hnd, CURLOPT_VERBOSE, 1L); 15013498266Sopenharmony_ci curl_easy_setopt(hnd, CURLOPT_DEBUGFUNCTION, my_trace); 15113498266Sopenharmony_ci 15213498266Sopenharmony_ci#if (CURLPIPE_MULTIPLEX > 0) 15313498266Sopenharmony_ci /* wait for pipe connection to confirm */ 15413498266Sopenharmony_ci curl_easy_setopt(hnd, CURLOPT_PIPEWAIT, 1L); 15513498266Sopenharmony_ci#endif 15613498266Sopenharmony_ci return 0; /* all is good */ 15713498266Sopenharmony_ci} 15813498266Sopenharmony_ci 15913498266Sopenharmony_ci/* called when there's an incoming push */ 16013498266Sopenharmony_cistatic int server_push_callback(CURL *parent, 16113498266Sopenharmony_ci CURL *easy, 16213498266Sopenharmony_ci size_t num_headers, 16313498266Sopenharmony_ci struct curl_pushheaders *headers, 16413498266Sopenharmony_ci void *userp) 16513498266Sopenharmony_ci{ 16613498266Sopenharmony_ci char *headp; 16713498266Sopenharmony_ci size_t i; 16813498266Sopenharmony_ci int *transfers = (int *)userp; 16913498266Sopenharmony_ci char filename[128]; 17013498266Sopenharmony_ci FILE *out; 17113498266Sopenharmony_ci static unsigned int count = 0; 17213498266Sopenharmony_ci int rv; 17313498266Sopenharmony_ci 17413498266Sopenharmony_ci (void)parent; /* we have no use for this */ 17513498266Sopenharmony_ci curl_msnprintf(filename, sizeof(filename)-1, "push%u", count++); 17613498266Sopenharmony_ci 17713498266Sopenharmony_ci /* here's a new stream, save it in a new file for each new push */ 17813498266Sopenharmony_ci out = fopen(filename, "wb"); 17913498266Sopenharmony_ci if(!out) { 18013498266Sopenharmony_ci /* if we cannot save it, deny it */ 18113498266Sopenharmony_ci fprintf(stderr, "Failed to create output file for push\n"); 18213498266Sopenharmony_ci rv = CURL_PUSH_DENY; 18313498266Sopenharmony_ci goto out; 18413498266Sopenharmony_ci } 18513498266Sopenharmony_ci 18613498266Sopenharmony_ci /* write to this file */ 18713498266Sopenharmony_ci curl_easy_setopt(easy, CURLOPT_WRITEDATA, out); 18813498266Sopenharmony_ci 18913498266Sopenharmony_ci fprintf(stderr, "**** push callback approves stream %u, got %lu headers!\n", 19013498266Sopenharmony_ci count, (unsigned long)num_headers); 19113498266Sopenharmony_ci 19213498266Sopenharmony_ci for(i = 0; i<num_headers; i++) { 19313498266Sopenharmony_ci headp = curl_pushheader_bynum(headers, i); 19413498266Sopenharmony_ci fprintf(stderr, "**** header %lu: %s\n", (unsigned long)i, headp); 19513498266Sopenharmony_ci } 19613498266Sopenharmony_ci 19713498266Sopenharmony_ci headp = curl_pushheader_byname(headers, ":path"); 19813498266Sopenharmony_ci if(headp) { 19913498266Sopenharmony_ci fprintf(stderr, "**** The PATH is %s\n", headp /* skip :path + colon */); 20013498266Sopenharmony_ci } 20113498266Sopenharmony_ci 20213498266Sopenharmony_ci (*transfers)++; /* one more */ 20313498266Sopenharmony_ci rv = CURL_PUSH_OK; 20413498266Sopenharmony_ci 20513498266Sopenharmony_ciout: 20613498266Sopenharmony_ci return rv; 20713498266Sopenharmony_ci} 20813498266Sopenharmony_ci 20913498266Sopenharmony_ci 21013498266Sopenharmony_ci/* 21113498266Sopenharmony_ci * Download a file over HTTP/2, take care of server push. 21213498266Sopenharmony_ci */ 21313498266Sopenharmony_ciint main(int argc, char *argv[]) 21413498266Sopenharmony_ci{ 21513498266Sopenharmony_ci CURL *easy; 21613498266Sopenharmony_ci CURLM *multi_handle; 21713498266Sopenharmony_ci int transfers = 1; /* we start with one */ 21813498266Sopenharmony_ci struct CURLMsg *m; 21913498266Sopenharmony_ci const char *url; 22013498266Sopenharmony_ci 22113498266Sopenharmony_ci if(argc != 2) { 22213498266Sopenharmony_ci fprintf(stderr, "need URL as argument\n"); 22313498266Sopenharmony_ci return 2; 22413498266Sopenharmony_ci } 22513498266Sopenharmony_ci url = argv[1]; 22613498266Sopenharmony_ci 22713498266Sopenharmony_ci multi_handle = curl_multi_init(); 22813498266Sopenharmony_ci curl_multi_setopt(multi_handle, CURLMOPT_PIPELINING, CURLPIPE_MULTIPLEX); 22913498266Sopenharmony_ci curl_multi_setopt(multi_handle, CURLMOPT_PUSHFUNCTION, server_push_callback); 23013498266Sopenharmony_ci curl_multi_setopt(multi_handle, CURLMOPT_PUSHDATA, &transfers); 23113498266Sopenharmony_ci 23213498266Sopenharmony_ci easy = curl_easy_init(); 23313498266Sopenharmony_ci if(setup(easy, url)) { 23413498266Sopenharmony_ci fprintf(stderr, "failed\n"); 23513498266Sopenharmony_ci return 1; 23613498266Sopenharmony_ci } 23713498266Sopenharmony_ci 23813498266Sopenharmony_ci curl_multi_add_handle(multi_handle, easy); 23913498266Sopenharmony_ci do { 24013498266Sopenharmony_ci int still_running; /* keep number of running handles */ 24113498266Sopenharmony_ci CURLMcode mc = curl_multi_perform(multi_handle, &still_running); 24213498266Sopenharmony_ci 24313498266Sopenharmony_ci if(still_running) 24413498266Sopenharmony_ci /* wait for activity, timeout or "nothing" */ 24513498266Sopenharmony_ci mc = curl_multi_poll(multi_handle, NULL, 0, 1000, NULL); 24613498266Sopenharmony_ci 24713498266Sopenharmony_ci if(mc) 24813498266Sopenharmony_ci break; 24913498266Sopenharmony_ci 25013498266Sopenharmony_ci /* 25113498266Sopenharmony_ci * A little caution when doing server push is that libcurl itself has 25213498266Sopenharmony_ci * created and added one or more easy handles but we need to clean them up 25313498266Sopenharmony_ci * when we are done. 25413498266Sopenharmony_ci */ 25513498266Sopenharmony_ci do { 25613498266Sopenharmony_ci int msgq = 0; 25713498266Sopenharmony_ci m = curl_multi_info_read(multi_handle, &msgq); 25813498266Sopenharmony_ci if(m && (m->msg == CURLMSG_DONE)) { 25913498266Sopenharmony_ci CURL *e = m->easy_handle; 26013498266Sopenharmony_ci transfers--; 26113498266Sopenharmony_ci curl_multi_remove_handle(multi_handle, e); 26213498266Sopenharmony_ci curl_easy_cleanup(e); 26313498266Sopenharmony_ci } 26413498266Sopenharmony_ci } while(m); 26513498266Sopenharmony_ci 26613498266Sopenharmony_ci } while(transfers); /* as long as we have transfers going */ 26713498266Sopenharmony_ci 26813498266Sopenharmony_ci curl_multi_cleanup(multi_handle); 26913498266Sopenharmony_ci 27013498266Sopenharmony_ci return 0; 27113498266Sopenharmony_ci} 272