Path: blob/main/external/curl/tests/libtest/cli_hx_upload.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 int verbose_u = 1;2930struct transfer_u {31size_t idx;32CURL *curl;33const char *method;34char filename[128];35curl_mime *mime;36FILE *out;37curl_off_t send_total;38curl_off_t recv_size;39curl_off_t send_size;40curl_off_t fail_at;41curl_off_t pause_at;42curl_off_t abort_at;43int started;44int paused;45int resumed;46int done;47};4849static size_t transfer_count_u = 1;50static struct transfer_u *transfer_u;51static int forbid_reuse_u = 0;5253static struct transfer_u *get_transfer_for_easy_u(CURL *curl)54{55size_t i;56for(i = 0; i < transfer_count_u; ++i) {57if(curl == transfer_u[i].curl)58return &transfer_u[i];59}60return NULL;61}6263static size_t my_write_u_cb(char *buf, size_t nitems, size_t buflen,64void *userdata)65{66struct transfer_u *t = userdata;67size_t blen = (nitems * buflen);68size_t nwritten;6970curl_mfprintf(stderr, "[t-%zu] RECV %zu bytes, "71"total=%" CURL_FORMAT_CURL_OFF_T ", "72"pause_at=%" CURL_FORMAT_CURL_OFF_T "\n",73t->idx, blen, t->recv_size, t->pause_at);74if(!t->out) {75curl_msnprintf(t->filename, sizeof(t->filename)-1, "download_%zu.data",76t->idx);77t->out = curlx_fopen(t->filename, "wb");78if(!t->out)79return 0;80}8182nwritten = fwrite(buf, nitems, buflen, t->out);83if(nwritten < blen) {84curl_mfprintf(stderr, "[t-%zu] write failure\n", t->idx);85return 0;86}87t->recv_size += (curl_off_t)nwritten;88return (size_t)nwritten;89}9091static size_t my_read_cb(char *buf, size_t nitems, size_t buflen,92void *userdata)93{94struct transfer_u *t = userdata;95size_t blen = (nitems * buflen);96size_t nread;9798if(t->send_total <= t->send_size)99nread = 0;100else if((t->send_total - t->send_size) < (curl_off_t)blen)101nread = (size_t)(t->send_total - t->send_size);102else103nread = blen;104105curl_mfprintf(stderr, "[t-%zu] SEND %zu bytes, "106"total=%" CURL_FORMAT_CURL_OFF_T ", "107"pause_at=%" CURL_FORMAT_CURL_OFF_T "\n",108t->idx, nread, t->send_total, t->pause_at);109110if(!t->resumed &&111t->send_size < t->pause_at &&112((t->send_size + (curl_off_t)blen) >= t->pause_at)) {113curl_mfprintf(stderr, "[t-%zu] PAUSE\n", t->idx);114t->paused = 1;115return CURL_READFUNC_PAUSE;116}117118memset(buf, 'x', nread);119t->send_size += (curl_off_t)nread;120if(t->fail_at > 0 && t->send_size >= t->fail_at) {121curl_mfprintf(stderr, "[t-%zu] ABORT by read callback at "122"%" CURL_FORMAT_CURL_OFF_T " bytes\n", t->idx, t->send_size);123return CURL_READFUNC_ABORT;124}125return (size_t)nread;126}127128static int my_progress_u_cb(void *userdata,129curl_off_t dltotal, curl_off_t dlnow,130curl_off_t ultotal, curl_off_t ulnow)131{132struct transfer_u *t = userdata;133(void)ultotal;134(void)dlnow;135(void)dltotal;136if(t->abort_at > 0 && ulnow >= t->abort_at) {137curl_mfprintf(stderr, "[t-%zu] ABORT by progress_cb at "138"%" CURL_FORMAT_CURL_OFF_T " bytes sent\n", t->idx, ulnow);139return 1;140}141return 0;142}143144static int setup_hx_upload(CURL *curl, const char *url, struct transfer_u *t,145long http_version, struct curl_slist *host,146CURLSH *share, int use_earlydata,147int announce_length)148{149curl_easy_setopt(curl, CURLOPT_SHARE, share);150curl_easy_setopt(curl, CURLOPT_URL, url);151curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, http_version);152curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);153curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);154curl_easy_setopt(curl, CURLOPT_BUFFERSIZE, (long)(128 * 1024));155curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, CURLFOLLOW_OBEYCODE);156curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, my_write_u_cb);157curl_easy_setopt(curl, CURLOPT_WRITEDATA, t);158if(use_earlydata)159curl_easy_setopt(curl, CURLOPT_SSL_OPTIONS, CURLSSLOPT_EARLYDATA);160161if(!strcmp("MIME", t->method)) {162curl_mimepart *part;163t->mime = curl_mime_init(curl);164part = curl_mime_addpart(t->mime);165curl_mime_name(part, "file");166curl_mime_data_cb(part, -1, my_read_cb, NULL, NULL, t);167curl_easy_setopt(curl, CURLOPT_MIMEPOST, t->mime);168}169else {170if(!t->method || !strcmp("PUT", t->method))171curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);172else if(!strcmp("POST", t->method))173curl_easy_setopt(curl, CURLOPT_POST, 1L);174else {175curl_mfprintf(stderr, "unsupported method '%s'\n", t->method);176return 1;177}178curl_easy_setopt(curl, CURLOPT_READFUNCTION, my_read_cb);179curl_easy_setopt(curl, CURLOPT_READDATA, t);180if(announce_length)181curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, t->send_total);182}183184curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);185curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, my_progress_u_cb);186curl_easy_setopt(curl, CURLOPT_XFERINFODATA, t);187if(forbid_reuse_u)188curl_easy_setopt(curl, CURLOPT_FORBID_REUSE, 1L);189if(host)190curl_easy_setopt(curl, CURLOPT_RESOLVE, host);191192/* please be verbose */193if(verbose_u) {194curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);195curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, cli_debug_cb);196}197198/* wait for pipe connection to confirm */199curl_easy_setopt(curl, CURLOPT_PIPEWAIT, 1L);200201return 0; /* all is good */202}203204static void usage_hx_upload(const char *msg)205{206if(msg)207curl_mfprintf(stderr, "%s\n", msg);208curl_mfprintf(stderr,209"usage: [options] url\n"210" upload to a url with following options:\n"211" -a abort paused transfer\n"212" -e use TLS earlydata\n"213" -m number max parallel uploads\n"214" -n number total uploads\n"215" -A number abort transfer after `number` request body bytes\n"216" -F number fail reading request body after `number` of bytes\n"217" -P number pause transfer after `number` request body bytes\n"218" -r <host>:<port>:<addr> resolve information\n"219" -S number size to upload\n"220" -V http_version (http/1.1, h2, h3) http version to use\n"221);222}223224/*225* Download a file over HTTP/2, take care of server push.226*/227static CURLcode test_cli_hx_upload(const char *URL)228{229CURLM *multi;230CURLSH *share;231const char *url;232const char *method = "PUT";233size_t i, n, max_parallel = 1;234size_t active_transfers;235size_t pause_offset = 0;236size_t abort_offset = 0;237size_t fail_offset = 0;238size_t send_total = (128 * 1024);239int abort_paused = 0;240int reuse_easy = 0;241int use_earlydata = 0;242int announce_length = 0;243struct transfer_u *t = NULL;244long http_version = CURL_HTTP_VERSION_2_0;245struct curl_slist *host = NULL;246const char *resolve = NULL;247int ch;248CURLcode result = CURLE_OK;249250(void)URL;251252while((ch = cgetopt(test_argc, test_argv, "aefhlm:n:A:F:M:P:r:RS:V:"))253!= -1) {254switch(ch) {255case 'h':256usage_hx_upload(NULL);257return (CURLcode)2;258case 'a':259abort_paused = 1;260break;261case 'e':262use_earlydata = 1;263break;264case 'f':265forbid_reuse_u = 1;266break;267case 'l':268announce_length = 1;269break;270case 'm':271max_parallel = (size_t)atol(coptarg);272break;273case 'n':274transfer_count_u = (size_t)atol(coptarg);275break;276case 'A':277abort_offset = (size_t)atol(coptarg);278break;279case 'F':280fail_offset = (size_t)atol(coptarg);281break;282case 'M':283method = coptarg;284break;285case 'P':286pause_offset = (size_t)atol(coptarg);287break;288case 'r':289resolve = coptarg;290break;291case 'R':292reuse_easy = 1;293break;294case 'S':295send_total = (size_t)atol(coptarg);296break;297case 'V': {298if(!strcmp("http/1.1", coptarg))299http_version = CURL_HTTP_VERSION_1_1;300else if(!strcmp("h2", coptarg))301http_version = CURL_HTTP_VERSION_2_0;302else if(!strcmp("h3", coptarg))303http_version = CURL_HTTP_VERSION_3ONLY;304else {305usage_hx_upload("invalid http version");306return (CURLcode)1;307}308break;309}310default:311usage_hx_upload("invalid option");312return (CURLcode)1;313}314}315test_argc -= coptind;316test_argv += coptind;317318if(max_parallel > 1 && reuse_easy) {319usage_hx_upload("cannot mix -R and -P");320return (CURLcode)2;321}322323if(test_argc != 1) {324usage_hx_upload("not enough arguments");325return (CURLcode)2;326}327url = test_argv[0];328329if(curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) {330curl_mfprintf(stderr, "curl_global_init() failed\n");331return (CURLcode)3;332}333334curl_global_trace("ids,time,http/2,http/3");335336if(resolve)337host = curl_slist_append(NULL, resolve);338339share = curl_share_init();340if(!share) {341curl_mfprintf(stderr, "error allocating share\n");342result = (CURLcode)1;343goto cleanup;344}345curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_COOKIE);346curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS);347curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_SSL_SESSION);348curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_CONNECT);349curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_PSL);350curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_HSTS);351352transfer_u = calloc(transfer_count_u, sizeof(*transfer_u));353if(!transfer_u) {354curl_mfprintf(stderr, "error allocating transfer structs\n");355result = (CURLcode)1;356goto cleanup;357}358359active_transfers = 0;360for(i = 0; i < transfer_count_u; ++i) {361t = &transfer_u[i];362t->idx = i;363t->method = method;364t->send_total = (curl_off_t)send_total;365t->abort_at = (curl_off_t)abort_offset;366t->fail_at = (curl_off_t)fail_offset;367t->pause_at = (curl_off_t)pause_offset;368}369370if(reuse_easy) {371CURL *curl = curl_easy_init();372if(!curl) {373curl_mfprintf(stderr, "failed to init easy handle\n");374result = (CURLcode)1;375goto cleanup;376}377for(i = 0; i < transfer_count_u; ++i) {378CURLcode rc;379t = &transfer_u[i];380t->curl = curl;381if(setup_hx_upload(t->curl, url, t, http_version, host, share,382use_earlydata, announce_length)) {383curl_mfprintf(stderr, "[t-%zu] FAILED setup\n", i);384result = (CURLcode)1;385goto cleanup;386}387388curl_mfprintf(stderr, "[t-%zu] STARTING\n", t->idx);389rc = curl_easy_perform(curl);390curl_mfprintf(stderr, "[t-%zu] DONE -> %d\n", t->idx, rc);391t->curl = NULL;392curl_easy_reset(curl);393}394curl_easy_cleanup(curl);395}396else {397multi = curl_multi_init();398curl_multi_setopt(multi, CURLMOPT_PIPELINING, CURLPIPE_MULTIPLEX);399400n = (max_parallel < transfer_count_u) ? max_parallel : transfer_count_u;401for(i = 0; i < n; ++i) {402t = &transfer_u[i];403t->curl = curl_easy_init();404if(!t->curl || setup_hx_upload(t->curl, url, t, http_version, host,405share, use_earlydata, announce_length)) {406curl_mfprintf(stderr, "[t-%zu] FAILED setup\n", i);407result = (CURLcode)1;408goto cleanup;409}410curl_multi_add_handle(multi, t->curl);411t->started = 1;412++active_transfers;413curl_mfprintf(stderr, "[t-%zu] STARTED\n", t->idx);414}415416do {417int still_running; /* keep number of running handles */418CURLMcode mc = curl_multi_perform(multi, &still_running);419struct CURLMsg *m;420421if(still_running) {422/* wait for activity, timeout or "nothing" */423mc = curl_multi_poll(multi, NULL, 0, 1000, NULL);424}425426if(mc)427break;428429do {430int msgq = 0;431m = curl_multi_info_read(multi, &msgq);432if(m && (m->msg == CURLMSG_DONE)) {433CURL *easy = m->easy_handle;434--active_transfers;435curl_multi_remove_handle(multi, easy);436t = get_transfer_for_easy_u(easy);437if(t) {438long res_status;439curl_easy_getinfo(easy, CURLINFO_RESPONSE_CODE, &res_status);440t->done = 1;441curl_mfprintf(stderr, "[t-%zu] FINISHED, "442"result=%d, response=%ld\n",443t->idx, m->data.result, res_status);444if(use_earlydata) {445curl_off_t sent;446curl_easy_getinfo(easy, CURLINFO_EARLYDATA_SENT_T, &sent);447curl_mfprintf(stderr, "[t-%zu] EarlyData: "448"%" CURL_FORMAT_CURL_OFF_T "\n", t->idx, sent);449}450}451else {452curl_easy_cleanup(easy);453curl_mfprintf(stderr, "unknown FINISHED???\n");454}455}456457458/* nothing happening, maintenance */459if(abort_paused) {460/* abort paused transfers */461for(i = 0; i < transfer_count_u; ++i) {462t = &transfer_u[i];463if(!t->done && t->paused && t->curl) {464curl_multi_remove_handle(multi, t->curl);465t->done = 1;466active_transfers--;467curl_mfprintf(stderr, "[t-%zu] ABORTED\n", t->idx);468}469}470}471else {472/* resume one paused transfer */473for(i = 0; i < transfer_count_u; ++i) {474t = &transfer_u[i];475if(!t->done && t->paused) {476t->resumed = 1;477t->paused = 0;478curl_easy_pause(t->curl, CURLPAUSE_CONT);479curl_mfprintf(stderr, "[t-%zu] RESUMED\n", t->idx);480break;481}482}483}484485while(active_transfers < max_parallel) {486for(i = 0; i < transfer_count_u; ++i) {487t = &transfer_u[i];488if(!t->started) {489t->curl = curl_easy_init();490if(!t->curl || setup_hx_upload(t->curl, url, t, http_version,491host, share, use_earlydata,492announce_length)) {493curl_mfprintf(stderr, "[t-%zu] FAILED setup\n", i);494result = (CURLcode)1;495goto cleanup;496}497curl_multi_add_handle(multi, t->curl);498t->started = 1;499++active_transfers;500curl_mfprintf(stderr, "[t-%zu] STARTED\n", t->idx);501break;502}503}504/* all started */505if(i == transfer_count_u)506break;507}508} while(m);509510} while(active_transfers); /* as long as we have transfers going */511512curl_mfprintf(stderr, "all transfers done, cleanup multi\n");513curl_multi_cleanup(multi);514}515516cleanup:517518if(transfer_u) {519for(i = 0; i < transfer_count_u; ++i) {520t = &transfer_u[i];521if(t->out) {522curlx_fclose(t->out);523t->out = NULL;524}525if(t->curl) {526curl_easy_cleanup(t->curl);527t->curl = NULL;528}529if(t->mime) {530curl_mime_free(t->mime);531}532}533free(transfer_u);534}535536curl_share_cleanup(share);537curl_slist_free_all(host);538curl_global_cleanup();539540return result;541}542543544