Path: blob/main/external/curl/tests/http/clients/h2-pausing.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 download pausing25* </DESC>26*/27/* This is based on the PoC client of issue #1198228*/29#include <curl/curl.h>3031#include <assert.h>32#include <stdio.h>33#include <string.h>34#include <stdlib.h>3536#ifndef _MSC_VER37/* somewhat Unix-specific */38#include <unistd.h> /* getopt() */39#endif4041#ifndef _MSC_VER42#define HANDLECOUNT 24344static void log_line_start(FILE *log, const char *idsbuf, curl_infotype type)45{46/*47* This is the trace look that is similar to what libcurl makes on its48* own.49*/50static const char * const s_infotype[] = {51"* ", "< ", "> ", "{ ", "} ", "{ ", "} "52};53if(idsbuf && *idsbuf)54fprintf(log, "%s%s", idsbuf, s_infotype[type]);55else56fputs(s_infotype[type], log);57}5859#define TRC_IDS_FORMAT_IDS_1 "[%" CURL_FORMAT_CURL_OFF_T "-x] "60#define TRC_IDS_FORMAT_IDS_2 "[%" CURL_FORMAT_CURL_OFF_T "-%" \61CURL_FORMAT_CURL_OFF_T "] "62/*63** callback for CURLOPT_DEBUGFUNCTION64*/65static int debug_cb(CURL *handle, curl_infotype type,66char *data, size_t size,67void *userdata)68{69FILE *output = stderr;70static int newl = 0;71static int traced_data = 0;72char idsbuf[60];73curl_off_t xfer_id, conn_id;7475(void)handle; /* not used */76(void)userdata;7778if(!curl_easy_getinfo(handle, CURLINFO_XFER_ID, &xfer_id) && xfer_id >= 0) {79if(!curl_easy_getinfo(handle, CURLINFO_CONN_ID, &conn_id) &&80conn_id >= 0) {81curl_msnprintf(idsbuf, sizeof(idsbuf), TRC_IDS_FORMAT_IDS_2, xfer_id,82conn_id);83}84else {85curl_msnprintf(idsbuf, sizeof(idsbuf), TRC_IDS_FORMAT_IDS_1, xfer_id);86}87}88else89idsbuf[0] = 0;9091switch(type) {92case CURLINFO_HEADER_OUT:93if(size > 0) {94size_t st = 0;95size_t i;96for(i = 0; i < size - 1; i++) {97if(data[i] == '\n') { /* LF */98if(!newl) {99log_line_start(output, idsbuf, type);100}101(void)fwrite(data + st, i - st + 1, 1, output);102st = i + 1;103newl = 0;104}105}106if(!newl)107log_line_start(output, idsbuf, type);108(void)fwrite(data + st, i - st + 1, 1, output);109}110newl = (size && (data[size - 1] != '\n')) ? 1 : 0;111traced_data = 0;112break;113case CURLINFO_TEXT:114case CURLINFO_HEADER_IN:115if(!newl)116log_line_start(output, idsbuf, type);117(void)fwrite(data, size, 1, output);118newl = (size && (data[size - 1] != '\n')) ? 1 : 0;119traced_data = 0;120break;121case CURLINFO_DATA_OUT:122case CURLINFO_DATA_IN:123case CURLINFO_SSL_DATA_IN:124case CURLINFO_SSL_DATA_OUT:125if(!traced_data) {126if(!newl)127log_line_start(output, idsbuf, type);128fprintf(output, "[%ld bytes data]\n", (long)size);129newl = 0;130traced_data = 1;131}132break;133default: /* nada */134newl = 0;135traced_data = 1;136break;137}138139return 0;140}141142static void usage(const char *msg)143{144if(msg)145fprintf(stderr, "%s\n", msg);146fprintf(stderr,147"usage: [options] url\n"148" pause downloads with following options:\n"149" -V http_version (http/1.1, h2, h3) http version to use\n"150);151}152153struct handle154{155int idx;156int paused;157int resumed;158int errored;159int fail_write;160CURL *h;161};162163static size_t cb(char *data, size_t size, size_t nmemb, void *clientp)164{165size_t realsize = size * nmemb;166struct handle *handle = (struct handle *) clientp;167curl_off_t totalsize;168169(void)data;170if(curl_easy_getinfo(handle->h, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T,171&totalsize) == CURLE_OK)172fprintf(stderr, "INFO: [%d] write, Content-Length %"CURL_FORMAT_CURL_OFF_T173"\n", handle->idx, totalsize);174175if(!handle->resumed) {176++handle->paused;177fprintf(stderr, "INFO: [%d] write, PAUSING %d time on %lu bytes\n",178handle->idx, handle->paused, (long)realsize);179assert(handle->paused == 1);180return CURL_WRITEFUNC_PAUSE;181}182if(handle->fail_write) {183++handle->errored;184fprintf(stderr, "INFO: [%d] FAIL write of %lu bytes, %d time\n",185handle->idx, (long)realsize, handle->errored);186return CURL_WRITEFUNC_ERROR;187}188fprintf(stderr, "INFO: [%d] write, accepting %lu bytes\n",189handle->idx, (long)realsize);190return realsize;191}192193#define ERR() \194do { \195fprintf(stderr, "something unexpected went wrong - bailing out!\n"); \196return 2; \197} while(0)198199#endif /* !_MSC_VER */200201int main(int argc, char *argv[])202{203#ifndef _MSC_VER204struct handle handles[HANDLECOUNT];205CURLM *multi_handle;206int i, still_running = 1, msgs_left, numfds;207CURLMsg *msg;208int rounds = 0;209int rc = 0;210CURLU *cu;211struct curl_slist *resolve = NULL;212char resolve_buf[1024];213char *url, *host = NULL, *port = NULL;214int all_paused = 0;215int resume_round = -1;216int http_version = CURL_HTTP_VERSION_2_0;217int ch;218219while((ch = getopt(argc, argv, "hV:")) != -1) {220switch(ch) {221case 'h':222usage(NULL);223return 2;224case 'V': {225if(!strcmp("http/1.1", optarg))226http_version = CURL_HTTP_VERSION_1_1;227else if(!strcmp("h2", optarg))228http_version = CURL_HTTP_VERSION_2_0;229else if(!strcmp("h3", optarg))230http_version = CURL_HTTP_VERSION_3ONLY;231else {232usage("invalid http version");233return 1;234}235break;236}237default:238usage("invalid option");239return 1;240}241}242argc -= optind;243argv += optind;244245if(argc != 1) {246fprintf(stderr, "ERROR: need URL as argument\n");247return 2;248}249url = argv[0];250251curl_global_init(CURL_GLOBAL_DEFAULT);252curl_global_trace("ids,time,http/2,http/3");253254cu = curl_url();255if(!cu) {256fprintf(stderr, "out of memory\n");257return 1;258}259if(curl_url_set(cu, CURLUPART_URL, url, 0)) {260fprintf(stderr, "not a URL: '%s'\n", url);261return 1;262}263if(curl_url_get(cu, CURLUPART_HOST, &host, 0)) {264fprintf(stderr, "could not get host of '%s'\n", url);265return 1;266}267if(curl_url_get(cu, CURLUPART_PORT, &port, 0)) {268fprintf(stderr, "could not get port of '%s'\n", url);269return 1;270}271memset(&resolve, 0, sizeof(resolve));272curl_msnprintf(resolve_buf, sizeof(resolve_buf)-1, "%s:%s:127.0.0.1",273host, port);274resolve = curl_slist_append(resolve, resolve_buf);275276for(i = 0; i < HANDLECOUNT; i++) {277handles[i].idx = i;278handles[i].paused = 0;279handles[i].resumed = 0;280handles[i].errored = 0;281handles[i].fail_write = 1;282handles[i].h = curl_easy_init();283if(!handles[i].h ||284curl_easy_setopt(handles[i].h, CURLOPT_WRITEFUNCTION, cb) != CURLE_OK ||285curl_easy_setopt(handles[i].h, CURLOPT_WRITEDATA, &handles[i])286!= CURLE_OK ||287curl_easy_setopt(handles[i].h, CURLOPT_FOLLOWLOCATION, 1L) != CURLE_OK ||288curl_easy_setopt(handles[i].h, CURLOPT_VERBOSE, 1L) != CURLE_OK ||289curl_easy_setopt(handles[i].h, CURLOPT_DEBUGFUNCTION, debug_cb)290!= CURLE_OK ||291curl_easy_setopt(handles[i].h, CURLOPT_SSL_VERIFYPEER, 0L) != CURLE_OK ||292curl_easy_setopt(handles[i].h, CURLOPT_RESOLVE, resolve) != CURLE_OK ||293curl_easy_setopt(handles[i].h, CURLOPT_PIPEWAIT, 1L) ||294curl_easy_setopt(handles[i].h, CURLOPT_URL, url) != CURLE_OK) {295ERR();296}297curl_easy_setopt(handles[i].h, CURLOPT_HTTP_VERSION, (long)http_version);298}299300multi_handle = curl_multi_init();301if(!multi_handle)302ERR();303304for(i = 0; i < HANDLECOUNT; i++) {305if(curl_multi_add_handle(multi_handle, handles[i].h) != CURLM_OK)306ERR();307}308309for(rounds = 0;; rounds++) {310fprintf(stderr, "INFO: multi_perform round %d\n", rounds);311if(curl_multi_perform(multi_handle, &still_running) != CURLM_OK)312ERR();313314if(!still_running) {315int as_expected = 1;316fprintf(stderr, "INFO: no more handles running\n");317for(i = 0; i < HANDLECOUNT; i++) {318if(!handles[i].paused) {319fprintf(stderr, "ERROR: [%d] NOT PAUSED\n", i);320as_expected = 0;321}322else if(handles[i].paused != 1) {323fprintf(stderr, "ERROR: [%d] PAUSED %d times!\n",324i, handles[i].paused);325as_expected = 0;326}327else if(!handles[i].resumed) {328fprintf(stderr, "ERROR: [%d] NOT resumed!\n", i);329as_expected = 0;330}331else if(handles[i].errored != 1) {332fprintf(stderr, "ERROR: [%d] NOT errored once, %d instead!\n",333i, handles[i].errored);334as_expected = 0;335}336}337if(!as_expected) {338fprintf(stderr, "ERROR: handles not in expected state "339"after %d rounds\n", rounds);340rc = 1;341}342break;343}344345if(curl_multi_poll(multi_handle, NULL, 0, 100, &numfds) != CURLM_OK)346ERR();347348/* !checksrc! disable EQUALSNULL 1 */349while((msg = curl_multi_info_read(multi_handle, &msgs_left)) != NULL) {350if(msg->msg == CURLMSG_DONE) {351for(i = 0; i < HANDLECOUNT; i++) {352if(msg->easy_handle == handles[i].h) {353if(handles[i].paused != 1 || !handles[i].resumed) {354fprintf(stderr, "ERROR: [%d] done, pauses=%d, resumed=%d, "355"result %d - wtf?\n", i, handles[i].paused,356handles[i].resumed, msg->data.result);357rc = 1;358goto out;359}360}361}362}363}364365/* Successfully paused? */366if(!all_paused) {367for(i = 0; i < HANDLECOUNT; i++) {368if(!handles[i].paused) {369break;370}371}372all_paused = (i == HANDLECOUNT);373if(all_paused) {374fprintf(stderr, "INFO: all transfers paused\n");375/* give transfer some rounds to mess things up */376resume_round = rounds + 2;377}378}379if(resume_round > 0 && rounds == resume_round) {380/* time to resume */381for(i = 0; i < HANDLECOUNT; i++) {382fprintf(stderr, "INFO: [%d] resumed\n", i);383handles[i].resumed = 1;384curl_easy_pause(handles[i].h, CURLPAUSE_CONT);385}386}387}388389out:390for(i = 0; i < HANDLECOUNT; i++) {391curl_multi_remove_handle(multi_handle, handles[i].h);392curl_easy_cleanup(handles[i].h);393}394395396curl_slist_free_all(resolve);397curl_free(host);398curl_free(port);399curl_url_cleanup(cu);400curl_multi_cleanup(multi_handle);401curl_global_cleanup();402403return rc;404#else405(void)argc;406(void)argv;407fprintf(stderr, "Not supported with this compiler.\n");408return 1;409#endif /* !_MSC_VER */410}411412413