Path: blob/main/external/curl/tests/http/clients/h2-serverpush.c
2066 views
/***************************************************************************1* _ _ ____ _2* Project ___| | | | _ \| |3* / __| | | | |_) | |4* | (__| |_| | _ <| |___5* \___|\___/|_| \_\_____|6*7* Copyright (C) Daniel Stenberg, <[email protected]>, et al.8*9* This software is licensed as described in the file COPYING, which10* you should have received as part of this distribution. The terms11* are also available at https://curl.se/docs/copyright.html.12*13* You may opt to use, copy, modify, merge, publish, distribute and/or sell14* copies of the Software, and permit persons to whom the Software is15* furnished to do so, under the terms of the COPYING file.16*17* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY18* KIND, either express or implied.19*20* SPDX-License-Identifier: curl21*22***************************************************************************/23/* <DESC>24* HTTP/2 server push25* </DESC>26*/27/* curl stuff */28#include <curl/curl.h>2930#include <stdio.h>31#include <stdlib.h>32#include <string.h>3334#ifndef CURLPIPE_MULTIPLEX35#error "too old libcurl, cannot do HTTP/2 server push!"36#endif3738static39void dump(const char *text, unsigned char *ptr, size_t size,40char nohex)41{42size_t i;43size_t c;4445unsigned int width = 0x10;4647if(nohex)48/* without the hex output, we can fit more on screen */49width = 0x40;5051fprintf(stderr, "%s, %lu bytes (0x%lx)\n",52text, (unsigned long)size, (unsigned long)size);5354for(i = 0; i < size; i += width) {5556fprintf(stderr, "%4.4lx: ", (unsigned long)i);5758if(!nohex) {59/* hex not disabled, show it */60for(c = 0; c < width; c++)61if(i + c < size)62fprintf(stderr, "%02x ", ptr[i + c]);63else64fputs(" ", stderr);65}6667for(c = 0; (c < width) && (i + c < size); c++) {68/* check for 0D0A; if found, skip past and start a new line of output */69if(nohex && (i + c + 1 < size) && ptr[i + c] == 0x0D &&70ptr[i + c + 1] == 0x0A) {71i += (c + 2 - width);72break;73}74fprintf(stderr, "%c",75(ptr[i + c] >= 0x20) && (ptr[i + c] < 0x80) ? ptr[i + c] : '.');76/* check again for 0D0A, to avoid an extra \n if it's at width */77if(nohex && (i + c + 2 < size) && ptr[i + c + 1] == 0x0D &&78ptr[i + c + 2] == 0x0A) {79i += (c + 3 - width);80break;81}82}83fputc('\n', stderr); /* newline */84}85}8687static88int my_trace(CURL *handle, curl_infotype type,89char *data, size_t size,90void *userp)91{92const char *text;93(void)handle; /* prevent compiler warning */94(void)userp;95switch(type) {96case CURLINFO_TEXT:97fprintf(stderr, "== Info: %s", data);98return 0;99case CURLINFO_HEADER_OUT:100text = "=> Send header";101break;102case CURLINFO_DATA_OUT:103text = "=> Send data";104break;105case CURLINFO_SSL_DATA_OUT:106text = "=> Send SSL data";107break;108case CURLINFO_HEADER_IN:109text = "<= Recv header";110break;111case CURLINFO_DATA_IN:112text = "<= Recv data";113break;114case CURLINFO_SSL_DATA_IN:115text = "<= Recv SSL data";116break;117default: /* in case a new one is introduced to shock us */118return 0;119}120121dump(text, (unsigned char *)data, size, 1);122return 0;123}124125#define OUTPUTFILE "download_0.data"126127static int setup(CURL *hnd, const char *url)128{129FILE *out = fopen(OUTPUTFILE, "wb");130if(!out)131/* failed */132return 1;133134curl_easy_setopt(hnd, CURLOPT_URL, url);135curl_easy_setopt(hnd, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0);136curl_easy_setopt(hnd, CURLOPT_SSL_VERIFYPEER, 0L);137curl_easy_setopt(hnd, CURLOPT_SSL_VERIFYHOST, 0L);138139curl_easy_setopt(hnd, CURLOPT_WRITEDATA, out);140141/* please be verbose */142curl_easy_setopt(hnd, CURLOPT_VERBOSE, 1L);143curl_easy_setopt(hnd, CURLOPT_DEBUGFUNCTION, my_trace);144145#if (CURLPIPE_MULTIPLEX > 0)146/* wait for pipe connection to confirm */147curl_easy_setopt(hnd, CURLOPT_PIPEWAIT, 1L);148#endif149return 0; /* all is good */150}151152/* called when there's an incoming push */153static int server_push_callback(CURL *parent,154CURL *easy,155size_t num_headers,156struct curl_pushheaders *headers,157void *userp)158{159char *headp;160size_t i;161int *transfers = (int *)userp;162char filename[128];163FILE *out;164static unsigned int count = 0;165int rv;166167(void)parent; /* we have no use for this */168curl_msnprintf(filename, sizeof(filename)-1, "push%u", count++);169170/* here's a new stream, save it in a new file for each new push */171out = fopen(filename, "wb");172if(!out) {173/* if we cannot save it, deny it */174fprintf(stderr, "Failed to create output file for push\n");175rv = CURL_PUSH_DENY;176goto out;177}178179/* write to this file */180curl_easy_setopt(easy, CURLOPT_WRITEDATA, out);181182fprintf(stderr, "**** push callback approves stream %u, got %lu headers!\n",183count, (unsigned long)num_headers);184185for(i = 0; i < num_headers; i++) {186headp = curl_pushheader_bynum(headers, i);187fprintf(stderr, "**** header %lu: %s\n", (unsigned long)i, headp);188}189190headp = curl_pushheader_byname(headers, ":path");191if(headp) {192fprintf(stderr, "**** The PATH is %s\n", headp /* skip :path + colon */);193}194195(*transfers)++; /* one more */196rv = CURL_PUSH_OK;197198out:199return rv;200}201202203/*204* Download a file over HTTP/2, take care of server push.205*/206int main(int argc, char *argv[])207{208CURL *easy;209CURLM *multi_handle;210int transfers = 1; /* we start with one */211struct CURLMsg *m;212const char *url;213214if(argc != 2) {215fprintf(stderr, "need URL as argument\n");216return 2;217}218url = argv[1];219220multi_handle = curl_multi_init();221curl_multi_setopt(multi_handle, CURLMOPT_PIPELINING, CURLPIPE_MULTIPLEX);222curl_multi_setopt(multi_handle, CURLMOPT_PUSHFUNCTION, server_push_callback);223curl_multi_setopt(multi_handle, CURLMOPT_PUSHDATA, &transfers);224225easy = curl_easy_init();226if(setup(easy, url)) {227fprintf(stderr, "failed\n");228return 1;229}230231curl_multi_add_handle(multi_handle, easy);232do {233int still_running; /* keep number of running handles */234CURLMcode mc = curl_multi_perform(multi_handle, &still_running);235236if(still_running)237/* wait for activity, timeout or "nothing" */238mc = curl_multi_poll(multi_handle, NULL, 0, 1000, NULL);239240if(mc)241break;242243/*244* A little caution when doing server push is that libcurl itself has245* created and added one or more easy handles but we need to clean them up246* when we are done.247*/248do {249int msgq = 0;250m = curl_multi_info_read(multi_handle, &msgq);251if(m && (m->msg == CURLMSG_DONE)) {252CURL *e = m->easy_handle;253transfers--;254curl_multi_remove_handle(multi_handle, e);255curl_easy_cleanup(e);256}257} while(m);258259} while(transfers); /* as long as we have transfers going */260261curl_multi_cleanup(multi_handle);262263return 0;264}265266267