Path: blob/main/external/curl/tests/libtest/cli_h2_serverpush.c
2649 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#include "first.h"2425#include "testtrace.h"26#include "memdebug.h"2728static FILE *out_download = NULL;2930static int setup_h2_serverpush(CURL *curl, const char *url)31{32out_download = curlx_fopen("download_0.data", "wb");33if(!out_download)34return 1; /* failed */3536curl_easy_setopt(curl, CURLOPT_URL, url);37curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0);38curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);39curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);4041curl_easy_setopt(curl, CURLOPT_WRITEDATA, out_download);4243/* please be verbose */44curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);45curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, libtest_debug_cb);46curl_easy_setopt(curl, CURLOPT_DEBUGDATA, &debug_config);4748/* wait for pipe connection to confirm */49curl_easy_setopt(curl, CURLOPT_PIPEWAIT, 1L);5051return 0; /* all is good */52}5354static FILE *out_push = NULL;5556/* called when there is an incoming push */57static int server_push_callback(CURL *parent,58CURL *curl,59size_t num_headers,60struct curl_pushheaders *headers,61void *userp)62{63char *headp;64size_t i;65int *transfers = (int *)userp;66char filename[128];67static unsigned int count = 0;6869(void)parent;7071curl_msnprintf(filename, sizeof(filename) - 1, "push%u", count++);7273/* here's a new stream, save it in a new file for each new push */74out_push = curlx_fopen(filename, "wb");75if(!out_push) {76/* if we cannot save it, deny it */77curl_mfprintf(stderr, "Failed to create output file for push\n");78return CURL_PUSH_DENY;79}8081/* write to this file */82curl_easy_setopt(curl, CURLOPT_WRITEDATA, out_push);8384curl_mfprintf(stderr, "**** push callback approves stream %u, "85"got %zu headers!\n", count, num_headers);8687for(i = 0; i < num_headers; i++) {88headp = curl_pushheader_bynum(headers, i);89curl_mfprintf(stderr, "**** header %zu: %s\n", i, headp);90}9192headp = curl_pushheader_byname(headers, ":path");93if(headp) {94curl_mfprintf(stderr, "**** The PATH is %s\n",95headp /* skip :path + colon */);96}9798(*transfers)++; /* one more */99100return CURL_PUSH_OK;101}102103/*104* Download a file over HTTP/2, take care of server push.105*/106static CURLcode test_cli_h2_serverpush(const char *URL)107{108CURL *curl = NULL;109CURLM *multi;110int transfers = 1; /* we start with one */111CURLcode result = CURLE_OK;112113debug_config.nohex = TRUE;114debug_config.tracetime = FALSE;115116if(!URL) {117curl_mfprintf(stderr, "need URL as argument\n");118return (CURLcode)2;119}120121if(curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) {122curl_mfprintf(stderr, "curl_global_init() failed\n");123return (CURLcode)3;124}125126multi = curl_multi_init();127if(!multi) {128result = (CURLcode)1;129goto cleanup;130}131132curl = curl_easy_init();133if(!curl) {134result = (CURLcode)1;135goto cleanup;136}137138if(setup_h2_serverpush(curl, URL)) {139curl_mfprintf(stderr, "failed\n");140result = (CURLcode)1;141goto cleanup;142}143144curl_multi_setopt(multi, CURLMOPT_PIPELINING, CURLPIPE_MULTIPLEX);145curl_multi_setopt(multi, CURLMOPT_PUSHFUNCTION, server_push_callback);146curl_multi_setopt(multi, CURLMOPT_PUSHDATA, &transfers);147148curl_multi_add_handle(multi, curl);149150do {151struct CURLMsg *m;152int still_running; /* keep number of running handles */153CURLMcode mc = curl_multi_perform(multi, &still_running);154155if(still_running)156/* wait for activity, timeout or "nothing" */157mc = curl_multi_poll(multi, NULL, 0, 1000, NULL);158159if(mc)160break;161162/*163* A little caution when doing server push is that libcurl itself has164* created and added one or more easy handles but we need to clean them up165* when we are done.166*/167do {168int msgq = 0;169m = curl_multi_info_read(multi, &msgq);170if(m && (m->msg == CURLMSG_DONE)) {171CURL *easy = m->easy_handle;172transfers--;173curl_multi_remove_handle(multi, easy);174curl_easy_cleanup(easy);175}176} while(m);177178} while(transfers); /* as long as we have transfers going */179180cleanup:181182curl_multi_cleanup(multi);183184if(out_download)185curlx_fclose(out_download);186if(out_push)187curlx_fclose(out_push);188189curl_global_cleanup();190191return result;192}193194195