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 download pausing 2613498266Sopenharmony_ci * </DESC> 2713498266Sopenharmony_ci */ 2813498266Sopenharmony_ci/* This is based on the poc client of issue #11982 2913498266Sopenharmony_ci */ 3013498266Sopenharmony_ci#include <stdio.h> 3113498266Sopenharmony_ci#include <string.h> 3213498266Sopenharmony_ci#include <sys/time.h> 3313498266Sopenharmony_ci#include <unistd.h> 3413498266Sopenharmony_ci#include <stdlib.h> 3513498266Sopenharmony_ci#include <curl/curl.h> 3613498266Sopenharmony_ci#include <curl/mprintf.h> 3713498266Sopenharmony_ci 3813498266Sopenharmony_ci#define HANDLECOUNT 2 3913498266Sopenharmony_ci 4013498266Sopenharmony_cistatic void log_line_start(FILE *log, const char *idsbuf, curl_infotype type) 4113498266Sopenharmony_ci{ 4213498266Sopenharmony_ci /* 4313498266Sopenharmony_ci * This is the trace look that is similar to what libcurl makes on its 4413498266Sopenharmony_ci * own. 4513498266Sopenharmony_ci */ 4613498266Sopenharmony_ci static const char * const s_infotype[] = { 4713498266Sopenharmony_ci "* ", "< ", "> ", "{ ", "} ", "{ ", "} " 4813498266Sopenharmony_ci }; 4913498266Sopenharmony_ci if(idsbuf && *idsbuf) 5013498266Sopenharmony_ci fprintf(log, "%s%s", idsbuf, s_infotype[type]); 5113498266Sopenharmony_ci else 5213498266Sopenharmony_ci fputs(s_infotype[type], log); 5313498266Sopenharmony_ci} 5413498266Sopenharmony_ci 5513498266Sopenharmony_ci#define TRC_IDS_FORMAT_IDS_1 "[%" CURL_FORMAT_CURL_OFF_T "-x] " 5613498266Sopenharmony_ci#define TRC_IDS_FORMAT_IDS_2 "[%" CURL_FORMAT_CURL_OFF_T "-%" \ 5713498266Sopenharmony_ci CURL_FORMAT_CURL_OFF_T "] " 5813498266Sopenharmony_ci/* 5913498266Sopenharmony_ci** callback for CURLOPT_DEBUGFUNCTION 6013498266Sopenharmony_ci*/ 6113498266Sopenharmony_cistatic int debug_cb(CURL *handle, curl_infotype type, 6213498266Sopenharmony_ci char *data, size_t size, 6313498266Sopenharmony_ci void *userdata) 6413498266Sopenharmony_ci{ 6513498266Sopenharmony_ci FILE *output = stderr; 6613498266Sopenharmony_ci static int newl = 0; 6713498266Sopenharmony_ci static int traced_data = 0; 6813498266Sopenharmony_ci char idsbuf[60]; 6913498266Sopenharmony_ci curl_off_t xfer_id, conn_id; 7013498266Sopenharmony_ci 7113498266Sopenharmony_ci (void)handle; /* not used */ 7213498266Sopenharmony_ci (void)userdata; 7313498266Sopenharmony_ci 7413498266Sopenharmony_ci if(!curl_easy_getinfo(handle, CURLINFO_XFER_ID, &xfer_id) && xfer_id >= 0) { 7513498266Sopenharmony_ci if(!curl_easy_getinfo(handle, CURLINFO_CONN_ID, &conn_id) && 7613498266Sopenharmony_ci conn_id >= 0) { 7713498266Sopenharmony_ci curl_msnprintf(idsbuf, sizeof(idsbuf), TRC_IDS_FORMAT_IDS_2, 7813498266Sopenharmony_ci xfer_id, conn_id); 7913498266Sopenharmony_ci } 8013498266Sopenharmony_ci else { 8113498266Sopenharmony_ci curl_msnprintf(idsbuf, sizeof(idsbuf), TRC_IDS_FORMAT_IDS_1, xfer_id); 8213498266Sopenharmony_ci } 8313498266Sopenharmony_ci } 8413498266Sopenharmony_ci else 8513498266Sopenharmony_ci idsbuf[0] = 0; 8613498266Sopenharmony_ci 8713498266Sopenharmony_ci switch(type) { 8813498266Sopenharmony_ci case CURLINFO_HEADER_OUT: 8913498266Sopenharmony_ci if(size > 0) { 9013498266Sopenharmony_ci size_t st = 0; 9113498266Sopenharmony_ci size_t i; 9213498266Sopenharmony_ci for(i = 0; i < size - 1; i++) { 9313498266Sopenharmony_ci if(data[i] == '\n') { /* LF */ 9413498266Sopenharmony_ci if(!newl) { 9513498266Sopenharmony_ci log_line_start(output, idsbuf, type); 9613498266Sopenharmony_ci } 9713498266Sopenharmony_ci (void)fwrite(data + st, i - st + 1, 1, output); 9813498266Sopenharmony_ci st = i + 1; 9913498266Sopenharmony_ci newl = 0; 10013498266Sopenharmony_ci } 10113498266Sopenharmony_ci } 10213498266Sopenharmony_ci if(!newl) 10313498266Sopenharmony_ci log_line_start(output, idsbuf, type); 10413498266Sopenharmony_ci (void)fwrite(data + st, i - st + 1, 1, output); 10513498266Sopenharmony_ci } 10613498266Sopenharmony_ci newl = (size && (data[size - 1] != '\n')) ? 1 : 0; 10713498266Sopenharmony_ci traced_data = 0; 10813498266Sopenharmony_ci break; 10913498266Sopenharmony_ci case CURLINFO_TEXT: 11013498266Sopenharmony_ci case CURLINFO_HEADER_IN: 11113498266Sopenharmony_ci if(!newl) 11213498266Sopenharmony_ci log_line_start(output, idsbuf, type); 11313498266Sopenharmony_ci (void)fwrite(data, size, 1, output); 11413498266Sopenharmony_ci newl = (size && (data[size - 1] != '\n')) ? 1 : 0; 11513498266Sopenharmony_ci traced_data = 0; 11613498266Sopenharmony_ci break; 11713498266Sopenharmony_ci case CURLINFO_DATA_OUT: 11813498266Sopenharmony_ci case CURLINFO_DATA_IN: 11913498266Sopenharmony_ci case CURLINFO_SSL_DATA_IN: 12013498266Sopenharmony_ci case CURLINFO_SSL_DATA_OUT: 12113498266Sopenharmony_ci if(!traced_data) { 12213498266Sopenharmony_ci if(!newl) 12313498266Sopenharmony_ci log_line_start(output, idsbuf, type); 12413498266Sopenharmony_ci fprintf(output, "[%ld bytes data]\n", (long)size); 12513498266Sopenharmony_ci newl = 0; 12613498266Sopenharmony_ci traced_data = 1; 12713498266Sopenharmony_ci } 12813498266Sopenharmony_ci break; 12913498266Sopenharmony_ci default: /* nada */ 13013498266Sopenharmony_ci newl = 0; 13113498266Sopenharmony_ci traced_data = 1; 13213498266Sopenharmony_ci break; 13313498266Sopenharmony_ci } 13413498266Sopenharmony_ci 13513498266Sopenharmony_ci return 0; 13613498266Sopenharmony_ci} 13713498266Sopenharmony_ci 13813498266Sopenharmony_cistatic int err(void) 13913498266Sopenharmony_ci{ 14013498266Sopenharmony_ci fprintf(stderr, "something unexpected went wrong - bailing out!\n"); 14113498266Sopenharmony_ci exit(2); 14213498266Sopenharmony_ci} 14313498266Sopenharmony_ci 14413498266Sopenharmony_cistruct handle 14513498266Sopenharmony_ci{ 14613498266Sopenharmony_ci int idx; 14713498266Sopenharmony_ci int paused; 14813498266Sopenharmony_ci int resumed; 14913498266Sopenharmony_ci CURL *h; 15013498266Sopenharmony_ci}; 15113498266Sopenharmony_ci 15213498266Sopenharmony_cistatic size_t cb(void *data, size_t size, size_t nmemb, void *clientp) 15313498266Sopenharmony_ci{ 15413498266Sopenharmony_ci size_t realsize = size * nmemb; 15513498266Sopenharmony_ci struct handle *handle = (struct handle *) clientp; 15613498266Sopenharmony_ci curl_off_t totalsize; 15713498266Sopenharmony_ci 15813498266Sopenharmony_ci (void)data; 15913498266Sopenharmony_ci if(curl_easy_getinfo(handle->h, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, 16013498266Sopenharmony_ci &totalsize) == CURLE_OK) 16113498266Sopenharmony_ci fprintf(stderr, "INFO: [%d] write, Content-Length %"CURL_FORMAT_CURL_OFF_T 16213498266Sopenharmony_ci "\n", handle->idx, totalsize); 16313498266Sopenharmony_ci 16413498266Sopenharmony_ci if(!handle->resumed) { 16513498266Sopenharmony_ci ++handle->paused; 16613498266Sopenharmony_ci fprintf(stderr, "INFO: [%d] write, PAUSING %d time on %lu bytes\n", 16713498266Sopenharmony_ci handle->idx, handle->paused, (long)realsize); 16813498266Sopenharmony_ci return CURL_WRITEFUNC_PAUSE; 16913498266Sopenharmony_ci } 17013498266Sopenharmony_ci fprintf(stderr, "INFO: [%d] write, accepting %lu bytes\n", 17113498266Sopenharmony_ci handle->idx, (long)realsize); 17213498266Sopenharmony_ci return realsize; 17313498266Sopenharmony_ci} 17413498266Sopenharmony_ci 17513498266Sopenharmony_ciint main(int argc, char *argv[]) 17613498266Sopenharmony_ci{ 17713498266Sopenharmony_ci struct handle handles[HANDLECOUNT]; 17813498266Sopenharmony_ci CURLM *multi_handle; 17913498266Sopenharmony_ci int i, still_running = 1, msgs_left, numfds; 18013498266Sopenharmony_ci CURLMsg *msg; 18113498266Sopenharmony_ci int rounds = 0; 18213498266Sopenharmony_ci int rc = 0; 18313498266Sopenharmony_ci CURLU *cu; 18413498266Sopenharmony_ci struct curl_slist *resolve = NULL; 18513498266Sopenharmony_ci char resolve_buf[1024]; 18613498266Sopenharmony_ci char *url, *host = NULL, *port = NULL; 18713498266Sopenharmony_ci int all_paused = 0; 18813498266Sopenharmony_ci int resume_round = -1; 18913498266Sopenharmony_ci 19013498266Sopenharmony_ci if(argc != 2) { 19113498266Sopenharmony_ci fprintf(stderr, "ERROR: need URL as argument\n"); 19213498266Sopenharmony_ci return 2; 19313498266Sopenharmony_ci } 19413498266Sopenharmony_ci url = argv[1]; 19513498266Sopenharmony_ci 19613498266Sopenharmony_ci curl_global_init(CURL_GLOBAL_DEFAULT); 19713498266Sopenharmony_ci curl_global_trace("ids,time,http/2"); 19813498266Sopenharmony_ci 19913498266Sopenharmony_ci cu = curl_url(); 20013498266Sopenharmony_ci if(!cu) { 20113498266Sopenharmony_ci fprintf(stderr, "out of memory\n"); 20213498266Sopenharmony_ci exit(1); 20313498266Sopenharmony_ci } 20413498266Sopenharmony_ci if(curl_url_set(cu, CURLUPART_URL, url, 0)) { 20513498266Sopenharmony_ci fprintf(stderr, "not a URL: '%s'\n", url); 20613498266Sopenharmony_ci exit(1); 20713498266Sopenharmony_ci } 20813498266Sopenharmony_ci if(curl_url_get(cu, CURLUPART_HOST, &host, 0)) { 20913498266Sopenharmony_ci fprintf(stderr, "could not get host of '%s'\n", url); 21013498266Sopenharmony_ci exit(1); 21113498266Sopenharmony_ci } 21213498266Sopenharmony_ci if(curl_url_get(cu, CURLUPART_PORT, &port, 0)) { 21313498266Sopenharmony_ci fprintf(stderr, "could not get port of '%s'\n", url); 21413498266Sopenharmony_ci exit(1); 21513498266Sopenharmony_ci } 21613498266Sopenharmony_ci memset(&resolve, 0, sizeof(resolve)); 21713498266Sopenharmony_ci curl_msnprintf(resolve_buf, sizeof(resolve_buf)-1, 21813498266Sopenharmony_ci "%s:%s:127.0.0.1", host, port); 21913498266Sopenharmony_ci resolve = curl_slist_append(resolve, resolve_buf); 22013498266Sopenharmony_ci 22113498266Sopenharmony_ci for(i = 0; i<HANDLECOUNT; i++) { 22213498266Sopenharmony_ci handles[i].idx = i; 22313498266Sopenharmony_ci handles[i].paused = 0; 22413498266Sopenharmony_ci handles[i].resumed = 0; 22513498266Sopenharmony_ci handles[i].h = curl_easy_init(); 22613498266Sopenharmony_ci if(!handles[i].h || 22713498266Sopenharmony_ci curl_easy_setopt(handles[i].h, CURLOPT_WRITEFUNCTION, cb) != CURLE_OK || 22813498266Sopenharmony_ci curl_easy_setopt(handles[i].h, CURLOPT_WRITEDATA, &handles[i]) 22913498266Sopenharmony_ci != CURLE_OK || 23013498266Sopenharmony_ci curl_easy_setopt(handles[i].h, CURLOPT_FOLLOWLOCATION, 1L) != CURLE_OK || 23113498266Sopenharmony_ci curl_easy_setopt(handles[i].h, CURLOPT_VERBOSE, 1L) != CURLE_OK || 23213498266Sopenharmony_ci curl_easy_setopt(handles[i].h, CURLOPT_DEBUGFUNCTION, debug_cb) 23313498266Sopenharmony_ci != CURLE_OK || 23413498266Sopenharmony_ci curl_easy_setopt(handles[i].h, CURLOPT_SSL_VERIFYPEER, 0L) != CURLE_OK || 23513498266Sopenharmony_ci curl_easy_setopt(handles[i].h, CURLOPT_RESOLVE, resolve) != CURLE_OK || 23613498266Sopenharmony_ci curl_easy_setopt(handles[i].h, CURLOPT_URL, url) != CURLE_OK) { 23713498266Sopenharmony_ci err(); 23813498266Sopenharmony_ci } 23913498266Sopenharmony_ci } 24013498266Sopenharmony_ci 24113498266Sopenharmony_ci multi_handle = curl_multi_init(); 24213498266Sopenharmony_ci if(!multi_handle) 24313498266Sopenharmony_ci err(); 24413498266Sopenharmony_ci 24513498266Sopenharmony_ci for(i = 0; i<HANDLECOUNT; i++) { 24613498266Sopenharmony_ci if(curl_multi_add_handle(multi_handle, handles[i].h) != CURLM_OK) 24713498266Sopenharmony_ci err(); 24813498266Sopenharmony_ci } 24913498266Sopenharmony_ci 25013498266Sopenharmony_ci for(rounds = 0;; rounds++) { 25113498266Sopenharmony_ci fprintf(stderr, "INFO: multi_perform round %d\n", rounds); 25213498266Sopenharmony_ci if(curl_multi_perform(multi_handle, &still_running) != CURLM_OK) 25313498266Sopenharmony_ci err(); 25413498266Sopenharmony_ci 25513498266Sopenharmony_ci if(!still_running) { 25613498266Sopenharmony_ci int as_expected = 1; 25713498266Sopenharmony_ci fprintf(stderr, "INFO: no more handles running\n"); 25813498266Sopenharmony_ci for(i = 0; i<HANDLECOUNT; i++) { 25913498266Sopenharmony_ci if(!handles[i].paused) { 26013498266Sopenharmony_ci fprintf(stderr, "ERROR: [%d] NOT PAUSED\n", i); 26113498266Sopenharmony_ci as_expected = 0; 26213498266Sopenharmony_ci } 26313498266Sopenharmony_ci else if(handles[i].paused != 1) { 26413498266Sopenharmony_ci fprintf(stderr, "ERROR: [%d] PAUSED %d times!\n", 26513498266Sopenharmony_ci i, handles[i].paused); 26613498266Sopenharmony_ci as_expected = 0; 26713498266Sopenharmony_ci } 26813498266Sopenharmony_ci else if(!handles[i].resumed) { 26913498266Sopenharmony_ci fprintf(stderr, "ERROR: [%d] NOT resumed!\n", i); 27013498266Sopenharmony_ci as_expected = 0; 27113498266Sopenharmony_ci } 27213498266Sopenharmony_ci } 27313498266Sopenharmony_ci if(!as_expected) { 27413498266Sopenharmony_ci fprintf(stderr, "ERROR: handles not in expected state " 27513498266Sopenharmony_ci "after %d rounds\n", rounds); 27613498266Sopenharmony_ci rc = 1; 27713498266Sopenharmony_ci } 27813498266Sopenharmony_ci break; 27913498266Sopenharmony_ci } 28013498266Sopenharmony_ci 28113498266Sopenharmony_ci if(curl_multi_poll(multi_handle, NULL, 0, 100, &numfds) != CURLM_OK) 28213498266Sopenharmony_ci err(); 28313498266Sopenharmony_ci 28413498266Sopenharmony_ci while((msg = curl_multi_info_read(multi_handle, &msgs_left))) { 28513498266Sopenharmony_ci if(msg->msg == CURLMSG_DONE) { 28613498266Sopenharmony_ci for(i = 0; i<HANDLECOUNT; i++) { 28713498266Sopenharmony_ci if(msg->easy_handle == handles[i].h) { 28813498266Sopenharmony_ci if(handles[i].paused != 1 || !handles[i].resumed) { 28913498266Sopenharmony_ci fprintf(stderr, "ERROR: [%d] done, pauses=%d, resumed=%d, " 29013498266Sopenharmony_ci "result %d - wtf?\n", i, handles[i].paused, 29113498266Sopenharmony_ci handles[i].resumed, msg->data.result); 29213498266Sopenharmony_ci rc = 1; 29313498266Sopenharmony_ci goto out; 29413498266Sopenharmony_ci } 29513498266Sopenharmony_ci } 29613498266Sopenharmony_ci } 29713498266Sopenharmony_ci } 29813498266Sopenharmony_ci } 29913498266Sopenharmony_ci 30013498266Sopenharmony_ci /* Successfully paused? */ 30113498266Sopenharmony_ci if(!all_paused) { 30213498266Sopenharmony_ci for(i = 0; i<HANDLECOUNT; i++) { 30313498266Sopenharmony_ci if(!handles[i].paused) { 30413498266Sopenharmony_ci break; 30513498266Sopenharmony_ci } 30613498266Sopenharmony_ci } 30713498266Sopenharmony_ci all_paused = (i == HANDLECOUNT); 30813498266Sopenharmony_ci if(all_paused) { 30913498266Sopenharmony_ci fprintf(stderr, "INFO: all transfers paused\n"); 31013498266Sopenharmony_ci /* give transfer some rounds to mess things up */ 31113498266Sopenharmony_ci resume_round = rounds + 3; 31213498266Sopenharmony_ci } 31313498266Sopenharmony_ci } 31413498266Sopenharmony_ci if(resume_round > 0 && rounds == resume_round) { 31513498266Sopenharmony_ci /* time to resume */ 31613498266Sopenharmony_ci for(i = 0; i<HANDLECOUNT; i++) { 31713498266Sopenharmony_ci fprintf(stderr, "INFO: [%d] resumed\n", i); 31813498266Sopenharmony_ci handles[i].resumed = 1; 31913498266Sopenharmony_ci curl_easy_pause(handles[i].h, CURLPAUSE_CONT); 32013498266Sopenharmony_ci } 32113498266Sopenharmony_ci } 32213498266Sopenharmony_ci } 32313498266Sopenharmony_ci 32413498266Sopenharmony_ciout: 32513498266Sopenharmony_ci for(i = 0; i<HANDLECOUNT; i++) { 32613498266Sopenharmony_ci curl_multi_remove_handle(multi_handle, handles[i].h); 32713498266Sopenharmony_ci curl_easy_cleanup(handles[i].h); 32813498266Sopenharmony_ci } 32913498266Sopenharmony_ci 33013498266Sopenharmony_ci 33113498266Sopenharmony_ci curl_slist_free_all(resolve); 33213498266Sopenharmony_ci curl_free(host); 33313498266Sopenharmony_ci curl_free(port); 33413498266Sopenharmony_ci curl_url_cleanup(cu); 33513498266Sopenharmony_ci curl_multi_cleanup(multi_handle); 33613498266Sopenharmony_ci curl_global_cleanup(); 33713498266Sopenharmony_ci 33813498266Sopenharmony_ci return rc; 33913498266Sopenharmony_ci} 340