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 2513498266Sopenharmony_ci#include "curl_setup.h" 2613498266Sopenharmony_ci 2713498266Sopenharmony_ci#include "urldata.h" 2813498266Sopenharmony_ci#include "sendf.h" 2913498266Sopenharmony_ci#include "multiif.h" 3013498266Sopenharmony_ci#include "progress.h" 3113498266Sopenharmony_ci#include "timeval.h" 3213498266Sopenharmony_ci#include "curl_printf.h" 3313498266Sopenharmony_ci 3413498266Sopenharmony_ci/* check rate limits within this many recent milliseconds, at minimum. */ 3513498266Sopenharmony_ci#define MIN_RATE_LIMIT_PERIOD 3000 3613498266Sopenharmony_ci 3713498266Sopenharmony_ci#ifndef CURL_DISABLE_PROGRESS_METER 3813498266Sopenharmony_ci/* Provide a string that is 2 + 1 + 2 + 1 + 2 = 8 letters long (plus the zero 3913498266Sopenharmony_ci byte) */ 4013498266Sopenharmony_cistatic void time2str(char *r, curl_off_t seconds) 4113498266Sopenharmony_ci{ 4213498266Sopenharmony_ci curl_off_t h; 4313498266Sopenharmony_ci if(seconds <= 0) { 4413498266Sopenharmony_ci strcpy(r, "--:--:--"); 4513498266Sopenharmony_ci return; 4613498266Sopenharmony_ci } 4713498266Sopenharmony_ci h = seconds / CURL_OFF_T_C(3600); 4813498266Sopenharmony_ci if(h <= CURL_OFF_T_C(99)) { 4913498266Sopenharmony_ci curl_off_t m = (seconds - (h*CURL_OFF_T_C(3600))) / CURL_OFF_T_C(60); 5013498266Sopenharmony_ci curl_off_t s = (seconds - (h*CURL_OFF_T_C(3600))) - (m*CURL_OFF_T_C(60)); 5113498266Sopenharmony_ci msnprintf(r, 9, "%2" CURL_FORMAT_CURL_OFF_T ":%02" CURL_FORMAT_CURL_OFF_T 5213498266Sopenharmony_ci ":%02" CURL_FORMAT_CURL_OFF_T, h, m, s); 5313498266Sopenharmony_ci } 5413498266Sopenharmony_ci else { 5513498266Sopenharmony_ci /* this equals to more than 99 hours, switch to a more suitable output 5613498266Sopenharmony_ci format to fit within the limits. */ 5713498266Sopenharmony_ci curl_off_t d = seconds / CURL_OFF_T_C(86400); 5813498266Sopenharmony_ci h = (seconds - (d*CURL_OFF_T_C(86400))) / CURL_OFF_T_C(3600); 5913498266Sopenharmony_ci if(d <= CURL_OFF_T_C(999)) 6013498266Sopenharmony_ci msnprintf(r, 9, "%3" CURL_FORMAT_CURL_OFF_T 6113498266Sopenharmony_ci "d %02" CURL_FORMAT_CURL_OFF_T "h", d, h); 6213498266Sopenharmony_ci else 6313498266Sopenharmony_ci msnprintf(r, 9, "%7" CURL_FORMAT_CURL_OFF_T "d", d); 6413498266Sopenharmony_ci } 6513498266Sopenharmony_ci} 6613498266Sopenharmony_ci 6713498266Sopenharmony_ci/* The point of this function would be to return a string of the input data, 6813498266Sopenharmony_ci but never longer than 5 columns (+ one zero byte). 6913498266Sopenharmony_ci Add suffix k, M, G when suitable... */ 7013498266Sopenharmony_cistatic char *max5data(curl_off_t bytes, char *max5) 7113498266Sopenharmony_ci{ 7213498266Sopenharmony_ci#define ONE_KILOBYTE CURL_OFF_T_C(1024) 7313498266Sopenharmony_ci#define ONE_MEGABYTE (CURL_OFF_T_C(1024) * ONE_KILOBYTE) 7413498266Sopenharmony_ci#define ONE_GIGABYTE (CURL_OFF_T_C(1024) * ONE_MEGABYTE) 7513498266Sopenharmony_ci#define ONE_TERABYTE (CURL_OFF_T_C(1024) * ONE_GIGABYTE) 7613498266Sopenharmony_ci#define ONE_PETABYTE (CURL_OFF_T_C(1024) * ONE_TERABYTE) 7713498266Sopenharmony_ci 7813498266Sopenharmony_ci if(bytes < CURL_OFF_T_C(100000)) 7913498266Sopenharmony_ci msnprintf(max5, 6, "%5" CURL_FORMAT_CURL_OFF_T, bytes); 8013498266Sopenharmony_ci 8113498266Sopenharmony_ci else if(bytes < CURL_OFF_T_C(10000) * ONE_KILOBYTE) 8213498266Sopenharmony_ci msnprintf(max5, 6, "%4" CURL_FORMAT_CURL_OFF_T "k", bytes/ONE_KILOBYTE); 8313498266Sopenharmony_ci 8413498266Sopenharmony_ci else if(bytes < CURL_OFF_T_C(100) * ONE_MEGABYTE) 8513498266Sopenharmony_ci /* 'XX.XM' is good as long as we're less than 100 megs */ 8613498266Sopenharmony_ci msnprintf(max5, 6, "%2" CURL_FORMAT_CURL_OFF_T ".%0" 8713498266Sopenharmony_ci CURL_FORMAT_CURL_OFF_T "M", bytes/ONE_MEGABYTE, 8813498266Sopenharmony_ci (bytes%ONE_MEGABYTE) / (ONE_MEGABYTE/CURL_OFF_T_C(10)) ); 8913498266Sopenharmony_ci 9013498266Sopenharmony_ci else if(bytes < CURL_OFF_T_C(10000) * ONE_MEGABYTE) 9113498266Sopenharmony_ci /* 'XXXXM' is good until we're at 10000MB or above */ 9213498266Sopenharmony_ci msnprintf(max5, 6, "%4" CURL_FORMAT_CURL_OFF_T "M", bytes/ONE_MEGABYTE); 9313498266Sopenharmony_ci 9413498266Sopenharmony_ci else if(bytes < CURL_OFF_T_C(100) * ONE_GIGABYTE) 9513498266Sopenharmony_ci /* 10000 MB - 100 GB, we show it as XX.XG */ 9613498266Sopenharmony_ci msnprintf(max5, 6, "%2" CURL_FORMAT_CURL_OFF_T ".%0" 9713498266Sopenharmony_ci CURL_FORMAT_CURL_OFF_T "G", bytes/ONE_GIGABYTE, 9813498266Sopenharmony_ci (bytes%ONE_GIGABYTE) / (ONE_GIGABYTE/CURL_OFF_T_C(10)) ); 9913498266Sopenharmony_ci 10013498266Sopenharmony_ci else if(bytes < CURL_OFF_T_C(10000) * ONE_GIGABYTE) 10113498266Sopenharmony_ci /* up to 10000GB, display without decimal: XXXXG */ 10213498266Sopenharmony_ci msnprintf(max5, 6, "%4" CURL_FORMAT_CURL_OFF_T "G", bytes/ONE_GIGABYTE); 10313498266Sopenharmony_ci 10413498266Sopenharmony_ci else if(bytes < CURL_OFF_T_C(10000) * ONE_TERABYTE) 10513498266Sopenharmony_ci /* up to 10000TB, display without decimal: XXXXT */ 10613498266Sopenharmony_ci msnprintf(max5, 6, "%4" CURL_FORMAT_CURL_OFF_T "T", bytes/ONE_TERABYTE); 10713498266Sopenharmony_ci 10813498266Sopenharmony_ci else 10913498266Sopenharmony_ci /* up to 10000PB, display without decimal: XXXXP */ 11013498266Sopenharmony_ci msnprintf(max5, 6, "%4" CURL_FORMAT_CURL_OFF_T "P", bytes/ONE_PETABYTE); 11113498266Sopenharmony_ci 11213498266Sopenharmony_ci /* 16384 petabytes (16 exabytes) is the maximum a 64 bit unsigned number can 11313498266Sopenharmony_ci hold, but our data type is signed so 8192PB will be the maximum. */ 11413498266Sopenharmony_ci 11513498266Sopenharmony_ci return max5; 11613498266Sopenharmony_ci} 11713498266Sopenharmony_ci#endif 11813498266Sopenharmony_ci 11913498266Sopenharmony_ci/* 12013498266Sopenharmony_ci 12113498266Sopenharmony_ci New proposed interface, 9th of February 2000: 12213498266Sopenharmony_ci 12313498266Sopenharmony_ci pgrsStartNow() - sets start time 12413498266Sopenharmony_ci pgrsSetDownloadSize(x) - known expected download size 12513498266Sopenharmony_ci pgrsSetUploadSize(x) - known expected upload size 12613498266Sopenharmony_ci pgrsSetDownloadCounter() - amount of data currently downloaded 12713498266Sopenharmony_ci pgrsSetUploadCounter() - amount of data currently uploaded 12813498266Sopenharmony_ci pgrsUpdate() - show progress 12913498266Sopenharmony_ci pgrsDone() - transfer complete 13013498266Sopenharmony_ci 13113498266Sopenharmony_ci*/ 13213498266Sopenharmony_ci 13313498266Sopenharmony_ciint Curl_pgrsDone(struct Curl_easy *data) 13413498266Sopenharmony_ci{ 13513498266Sopenharmony_ci int rc; 13613498266Sopenharmony_ci data->progress.lastshow = 0; 13713498266Sopenharmony_ci rc = Curl_pgrsUpdate(data); /* the final (forced) update */ 13813498266Sopenharmony_ci if(rc) 13913498266Sopenharmony_ci return rc; 14013498266Sopenharmony_ci 14113498266Sopenharmony_ci if(!(data->progress.flags & PGRS_HIDE) && 14213498266Sopenharmony_ci !data->progress.callback) 14313498266Sopenharmony_ci /* only output if we don't use a progress callback and we're not 14413498266Sopenharmony_ci * hidden */ 14513498266Sopenharmony_ci fprintf(data->set.err, "\n"); 14613498266Sopenharmony_ci 14713498266Sopenharmony_ci data->progress.speeder_c = 0; /* reset the progress meter display */ 14813498266Sopenharmony_ci return 0; 14913498266Sopenharmony_ci} 15013498266Sopenharmony_ci 15113498266Sopenharmony_ci/* reset the known transfer sizes */ 15213498266Sopenharmony_civoid Curl_pgrsResetTransferSizes(struct Curl_easy *data) 15313498266Sopenharmony_ci{ 15413498266Sopenharmony_ci Curl_pgrsSetDownloadSize(data, -1); 15513498266Sopenharmony_ci Curl_pgrsSetUploadSize(data, -1); 15613498266Sopenharmony_ci} 15713498266Sopenharmony_ci 15813498266Sopenharmony_ci/* 15913498266Sopenharmony_ci * 16013498266Sopenharmony_ci * Curl_pgrsTimeWas(). Store the timestamp time at the given label. 16113498266Sopenharmony_ci */ 16213498266Sopenharmony_civoid Curl_pgrsTimeWas(struct Curl_easy *data, timerid timer, 16313498266Sopenharmony_ci struct curltime timestamp) 16413498266Sopenharmony_ci{ 16513498266Sopenharmony_ci timediff_t *delta = NULL; 16613498266Sopenharmony_ci 16713498266Sopenharmony_ci switch(timer) { 16813498266Sopenharmony_ci default: 16913498266Sopenharmony_ci case TIMER_NONE: 17013498266Sopenharmony_ci /* mistake filter */ 17113498266Sopenharmony_ci break; 17213498266Sopenharmony_ci case TIMER_STARTOP: 17313498266Sopenharmony_ci /* This is set at the start of a transfer */ 17413498266Sopenharmony_ci data->progress.t_startop = timestamp; 17513498266Sopenharmony_ci break; 17613498266Sopenharmony_ci case TIMER_STARTSINGLE: 17713498266Sopenharmony_ci /* This is set at the start of each single transfer */ 17813498266Sopenharmony_ci data->progress.t_startsingle = timestamp; 17913498266Sopenharmony_ci data->progress.is_t_startransfer_set = false; 18013498266Sopenharmony_ci break; 18113498266Sopenharmony_ci case TIMER_POSTQUEUE: 18213498266Sopenharmony_ci /* Set when the transfer starts (after potentially having been brought 18313498266Sopenharmony_ci back from the waiting queue). It needs to count from t_startop and not 18413498266Sopenharmony_ci t_startsingle since the latter is reset when a connection is brought 18513498266Sopenharmony_ci back from the pending queue. */ 18613498266Sopenharmony_ci data->progress.t_postqueue = 18713498266Sopenharmony_ci Curl_timediff_us(timestamp, data->progress.t_startop); 18813498266Sopenharmony_ci break; 18913498266Sopenharmony_ci case TIMER_STARTACCEPT: 19013498266Sopenharmony_ci data->progress.t_acceptdata = timestamp; 19113498266Sopenharmony_ci break; 19213498266Sopenharmony_ci case TIMER_NAMELOOKUP: 19313498266Sopenharmony_ci delta = &data->progress.t_nslookup; 19413498266Sopenharmony_ci break; 19513498266Sopenharmony_ci case TIMER_CONNECT: 19613498266Sopenharmony_ci delta = &data->progress.t_connect; 19713498266Sopenharmony_ci break; 19813498266Sopenharmony_ci case TIMER_APPCONNECT: 19913498266Sopenharmony_ci delta = &data->progress.t_appconnect; 20013498266Sopenharmony_ci break; 20113498266Sopenharmony_ci case TIMER_PRETRANSFER: 20213498266Sopenharmony_ci delta = &data->progress.t_pretransfer; 20313498266Sopenharmony_ci break; 20413498266Sopenharmony_ci case TIMER_STARTTRANSFER: 20513498266Sopenharmony_ci delta = &data->progress.t_starttransfer; 20613498266Sopenharmony_ci /* prevent updating t_starttransfer unless: 20713498266Sopenharmony_ci * 1) this is the first time we're setting t_starttransfer 20813498266Sopenharmony_ci * 2) a redirect has occurred since the last time t_starttransfer was set 20913498266Sopenharmony_ci * This prevents repeated invocations of the function from incorrectly 21013498266Sopenharmony_ci * changing the t_starttransfer time. 21113498266Sopenharmony_ci */ 21213498266Sopenharmony_ci if(data->progress.is_t_startransfer_set) { 21313498266Sopenharmony_ci return; 21413498266Sopenharmony_ci } 21513498266Sopenharmony_ci else { 21613498266Sopenharmony_ci data->progress.is_t_startransfer_set = true; 21713498266Sopenharmony_ci break; 21813498266Sopenharmony_ci } 21913498266Sopenharmony_ci case TIMER_POSTRANSFER: 22013498266Sopenharmony_ci /* this is the normal end-of-transfer thing */ 22113498266Sopenharmony_ci break; 22213498266Sopenharmony_ci case TIMER_REDIRECT: 22313498266Sopenharmony_ci data->progress.t_redirect = Curl_timediff_us(timestamp, 22413498266Sopenharmony_ci data->progress.start); 22513498266Sopenharmony_ci break; 22613498266Sopenharmony_ci } 22713498266Sopenharmony_ci if(delta) { 22813498266Sopenharmony_ci timediff_t us = Curl_timediff_us(timestamp, data->progress.t_startsingle); 22913498266Sopenharmony_ci if(us < 1) 23013498266Sopenharmony_ci us = 1; /* make sure at least one microsecond passed */ 23113498266Sopenharmony_ci *delta += us; 23213498266Sopenharmony_ci } 23313498266Sopenharmony_ci} 23413498266Sopenharmony_ci 23513498266Sopenharmony_ci/* 23613498266Sopenharmony_ci * 23713498266Sopenharmony_ci * Curl_pgrsTime(). Store the current time at the given label. This fetches a 23813498266Sopenharmony_ci * fresh "now" and returns it. 23913498266Sopenharmony_ci * 24013498266Sopenharmony_ci * @unittest: 1399 24113498266Sopenharmony_ci */ 24213498266Sopenharmony_cistruct curltime Curl_pgrsTime(struct Curl_easy *data, timerid timer) 24313498266Sopenharmony_ci{ 24413498266Sopenharmony_ci struct curltime now = Curl_now(); 24513498266Sopenharmony_ci 24613498266Sopenharmony_ci Curl_pgrsTimeWas(data, timer, now); 24713498266Sopenharmony_ci return now; 24813498266Sopenharmony_ci} 24913498266Sopenharmony_ci 25013498266Sopenharmony_civoid Curl_pgrsStartNow(struct Curl_easy *data) 25113498266Sopenharmony_ci{ 25213498266Sopenharmony_ci data->progress.speeder_c = 0; /* reset the progress meter display */ 25313498266Sopenharmony_ci data->progress.start = Curl_now(); 25413498266Sopenharmony_ci data->progress.is_t_startransfer_set = false; 25513498266Sopenharmony_ci data->progress.ul_limit_start = data->progress.start; 25613498266Sopenharmony_ci data->progress.dl_limit_start = data->progress.start; 25713498266Sopenharmony_ci data->progress.ul_limit_size = 0; 25813498266Sopenharmony_ci data->progress.dl_limit_size = 0; 25913498266Sopenharmony_ci data->progress.downloaded = 0; 26013498266Sopenharmony_ci data->progress.uploaded = 0; 26113498266Sopenharmony_ci /* clear all bits except HIDE and HEADERS_OUT */ 26213498266Sopenharmony_ci data->progress.flags &= PGRS_HIDE|PGRS_HEADERS_OUT; 26313498266Sopenharmony_ci Curl_ratelimit(data, data->progress.start); 26413498266Sopenharmony_ci} 26513498266Sopenharmony_ci 26613498266Sopenharmony_ci/* 26713498266Sopenharmony_ci * This is used to handle speed limits, calculating how many milliseconds to 26813498266Sopenharmony_ci * wait until we're back under the speed limit, if needed. 26913498266Sopenharmony_ci * 27013498266Sopenharmony_ci * The way it works is by having a "starting point" (time & amount of data 27113498266Sopenharmony_ci * transferred by then) used in the speed computation, to be used instead of 27213498266Sopenharmony_ci * the start of the transfer. This starting point is regularly moved as 27313498266Sopenharmony_ci * transfer goes on, to keep getting accurate values (instead of average over 27413498266Sopenharmony_ci * the entire transfer). 27513498266Sopenharmony_ci * 27613498266Sopenharmony_ci * This function takes the current amount of data transferred, the amount at 27713498266Sopenharmony_ci * the starting point, the limit (in bytes/s), the time of the starting point 27813498266Sopenharmony_ci * and the current time. 27913498266Sopenharmony_ci * 28013498266Sopenharmony_ci * Returns 0 if no waiting is needed or when no waiting is needed but the 28113498266Sopenharmony_ci * starting point should be reset (to current); or the number of milliseconds 28213498266Sopenharmony_ci * to wait to get back under the speed limit. 28313498266Sopenharmony_ci */ 28413498266Sopenharmony_citimediff_t Curl_pgrsLimitWaitTime(curl_off_t cursize, 28513498266Sopenharmony_ci curl_off_t startsize, 28613498266Sopenharmony_ci curl_off_t limit, 28713498266Sopenharmony_ci struct curltime start, 28813498266Sopenharmony_ci struct curltime now) 28913498266Sopenharmony_ci{ 29013498266Sopenharmony_ci curl_off_t size = cursize - startsize; 29113498266Sopenharmony_ci timediff_t minimum; 29213498266Sopenharmony_ci timediff_t actual; 29313498266Sopenharmony_ci 29413498266Sopenharmony_ci if(!limit || !size) 29513498266Sopenharmony_ci return 0; 29613498266Sopenharmony_ci 29713498266Sopenharmony_ci /* 29813498266Sopenharmony_ci * 'minimum' is the number of milliseconds 'size' should take to download to 29913498266Sopenharmony_ci * stay below 'limit'. 30013498266Sopenharmony_ci */ 30113498266Sopenharmony_ci if(size < CURL_OFF_T_MAX/1000) 30213498266Sopenharmony_ci minimum = (timediff_t) (CURL_OFF_T_C(1000) * size / limit); 30313498266Sopenharmony_ci else { 30413498266Sopenharmony_ci minimum = (timediff_t) (size / limit); 30513498266Sopenharmony_ci if(minimum < TIMEDIFF_T_MAX/1000) 30613498266Sopenharmony_ci minimum *= 1000; 30713498266Sopenharmony_ci else 30813498266Sopenharmony_ci minimum = TIMEDIFF_T_MAX; 30913498266Sopenharmony_ci } 31013498266Sopenharmony_ci 31113498266Sopenharmony_ci /* 31213498266Sopenharmony_ci * 'actual' is the time in milliseconds it took to actually download the 31313498266Sopenharmony_ci * last 'size' bytes. 31413498266Sopenharmony_ci */ 31513498266Sopenharmony_ci actual = Curl_timediff_ceil(now, start); 31613498266Sopenharmony_ci if(actual < minimum) { 31713498266Sopenharmony_ci /* if it downloaded the data faster than the limit, make it wait the 31813498266Sopenharmony_ci difference */ 31913498266Sopenharmony_ci return (minimum - actual); 32013498266Sopenharmony_ci } 32113498266Sopenharmony_ci 32213498266Sopenharmony_ci return 0; 32313498266Sopenharmony_ci} 32413498266Sopenharmony_ci 32513498266Sopenharmony_ci/* 32613498266Sopenharmony_ci * Set the number of downloaded bytes so far. 32713498266Sopenharmony_ci */ 32813498266Sopenharmony_ciCURLcode Curl_pgrsSetDownloadCounter(struct Curl_easy *data, curl_off_t size) 32913498266Sopenharmony_ci{ 33013498266Sopenharmony_ci data->progress.downloaded = size; 33113498266Sopenharmony_ci return CURLE_OK; 33213498266Sopenharmony_ci} 33313498266Sopenharmony_ci 33413498266Sopenharmony_ci/* 33513498266Sopenharmony_ci * Update the timestamp and sizestamp to use for rate limit calculations. 33613498266Sopenharmony_ci */ 33713498266Sopenharmony_civoid Curl_ratelimit(struct Curl_easy *data, struct curltime now) 33813498266Sopenharmony_ci{ 33913498266Sopenharmony_ci /* don't set a new stamp unless the time since last update is long enough */ 34013498266Sopenharmony_ci if(data->set.max_recv_speed) { 34113498266Sopenharmony_ci if(Curl_timediff(now, data->progress.dl_limit_start) >= 34213498266Sopenharmony_ci MIN_RATE_LIMIT_PERIOD) { 34313498266Sopenharmony_ci data->progress.dl_limit_start = now; 34413498266Sopenharmony_ci data->progress.dl_limit_size = data->progress.downloaded; 34513498266Sopenharmony_ci } 34613498266Sopenharmony_ci } 34713498266Sopenharmony_ci if(data->set.max_send_speed) { 34813498266Sopenharmony_ci if(Curl_timediff(now, data->progress.ul_limit_start) >= 34913498266Sopenharmony_ci MIN_RATE_LIMIT_PERIOD) { 35013498266Sopenharmony_ci data->progress.ul_limit_start = now; 35113498266Sopenharmony_ci data->progress.ul_limit_size = data->progress.uploaded; 35213498266Sopenharmony_ci } 35313498266Sopenharmony_ci } 35413498266Sopenharmony_ci} 35513498266Sopenharmony_ci 35613498266Sopenharmony_ci/* 35713498266Sopenharmony_ci * Set the number of uploaded bytes so far. 35813498266Sopenharmony_ci */ 35913498266Sopenharmony_civoid Curl_pgrsSetUploadCounter(struct Curl_easy *data, curl_off_t size) 36013498266Sopenharmony_ci{ 36113498266Sopenharmony_ci data->progress.uploaded = size; 36213498266Sopenharmony_ci} 36313498266Sopenharmony_ci 36413498266Sopenharmony_civoid Curl_pgrsSetDownloadSize(struct Curl_easy *data, curl_off_t size) 36513498266Sopenharmony_ci{ 36613498266Sopenharmony_ci if(size >= 0) { 36713498266Sopenharmony_ci data->progress.size_dl = size; 36813498266Sopenharmony_ci data->progress.flags |= PGRS_DL_SIZE_KNOWN; 36913498266Sopenharmony_ci } 37013498266Sopenharmony_ci else { 37113498266Sopenharmony_ci data->progress.size_dl = 0; 37213498266Sopenharmony_ci data->progress.flags &= ~PGRS_DL_SIZE_KNOWN; 37313498266Sopenharmony_ci } 37413498266Sopenharmony_ci} 37513498266Sopenharmony_ci 37613498266Sopenharmony_civoid Curl_pgrsSetUploadSize(struct Curl_easy *data, curl_off_t size) 37713498266Sopenharmony_ci{ 37813498266Sopenharmony_ci if(size >= 0) { 37913498266Sopenharmony_ci data->progress.size_ul = size; 38013498266Sopenharmony_ci data->progress.flags |= PGRS_UL_SIZE_KNOWN; 38113498266Sopenharmony_ci } 38213498266Sopenharmony_ci else { 38313498266Sopenharmony_ci data->progress.size_ul = 0; 38413498266Sopenharmony_ci data->progress.flags &= ~PGRS_UL_SIZE_KNOWN; 38513498266Sopenharmony_ci } 38613498266Sopenharmony_ci} 38713498266Sopenharmony_ci 38813498266Sopenharmony_ci/* returns the average speed in bytes / second */ 38913498266Sopenharmony_cistatic curl_off_t trspeed(curl_off_t size, /* number of bytes */ 39013498266Sopenharmony_ci curl_off_t us) /* microseconds */ 39113498266Sopenharmony_ci{ 39213498266Sopenharmony_ci if(us < 1) 39313498266Sopenharmony_ci return size * 1000000; 39413498266Sopenharmony_ci else if(size < CURL_OFF_T_MAX/1000000) 39513498266Sopenharmony_ci return (size * 1000000) / us; 39613498266Sopenharmony_ci else if(us >= 1000000) 39713498266Sopenharmony_ci return size / (us / 1000000); 39813498266Sopenharmony_ci else 39913498266Sopenharmony_ci return CURL_OFF_T_MAX; 40013498266Sopenharmony_ci} 40113498266Sopenharmony_ci 40213498266Sopenharmony_ci/* returns TRUE if it's time to show the progress meter */ 40313498266Sopenharmony_cistatic bool progress_calc(struct Curl_easy *data, struct curltime now) 40413498266Sopenharmony_ci{ 40513498266Sopenharmony_ci bool timetoshow = FALSE; 40613498266Sopenharmony_ci struct Progress * const p = &data->progress; 40713498266Sopenharmony_ci 40813498266Sopenharmony_ci /* The time spent so far (from the start) in microseconds */ 40913498266Sopenharmony_ci p->timespent = Curl_timediff_us(now, p->start); 41013498266Sopenharmony_ci p->dlspeed = trspeed(p->downloaded, p->timespent); 41113498266Sopenharmony_ci p->ulspeed = trspeed(p->uploaded, p->timespent); 41213498266Sopenharmony_ci 41313498266Sopenharmony_ci /* Calculations done at most once a second, unless end is reached */ 41413498266Sopenharmony_ci if(p->lastshow != now.tv_sec) { 41513498266Sopenharmony_ci int countindex; /* amount of seconds stored in the speeder array */ 41613498266Sopenharmony_ci int nowindex = p->speeder_c% CURR_TIME; 41713498266Sopenharmony_ci p->lastshow = now.tv_sec; 41813498266Sopenharmony_ci timetoshow = TRUE; 41913498266Sopenharmony_ci 42013498266Sopenharmony_ci /* Let's do the "current speed" thing, with the dl + ul speeds 42113498266Sopenharmony_ci combined. Store the speed at entry 'nowindex'. */ 42213498266Sopenharmony_ci p->speeder[ nowindex ] = p->downloaded + p->uploaded; 42313498266Sopenharmony_ci 42413498266Sopenharmony_ci /* remember the exact time for this moment */ 42513498266Sopenharmony_ci p->speeder_time [ nowindex ] = now; 42613498266Sopenharmony_ci 42713498266Sopenharmony_ci /* advance our speeder_c counter, which is increased every time we get 42813498266Sopenharmony_ci here and we expect it to never wrap as 2^32 is a lot of seconds! */ 42913498266Sopenharmony_ci p->speeder_c++; 43013498266Sopenharmony_ci 43113498266Sopenharmony_ci /* figure out how many index entries of data we have stored in our speeder 43213498266Sopenharmony_ci array. With N_ENTRIES filled in, we have about N_ENTRIES-1 seconds of 43313498266Sopenharmony_ci transfer. Imagine, after one second we have filled in two entries, 43413498266Sopenharmony_ci after two seconds we've filled in three entries etc. */ 43513498266Sopenharmony_ci countindex = ((p->speeder_c >= CURR_TIME)? CURR_TIME:p->speeder_c) - 1; 43613498266Sopenharmony_ci 43713498266Sopenharmony_ci /* first of all, we don't do this if there's no counted seconds yet */ 43813498266Sopenharmony_ci if(countindex) { 43913498266Sopenharmony_ci int checkindex; 44013498266Sopenharmony_ci timediff_t span_ms; 44113498266Sopenharmony_ci curl_off_t amount; 44213498266Sopenharmony_ci 44313498266Sopenharmony_ci /* Get the index position to compare with the 'nowindex' position. 44413498266Sopenharmony_ci Get the oldest entry possible. While we have less than CURR_TIME 44513498266Sopenharmony_ci entries, the first entry will remain the oldest. */ 44613498266Sopenharmony_ci checkindex = (p->speeder_c >= CURR_TIME)? p->speeder_c%CURR_TIME:0; 44713498266Sopenharmony_ci 44813498266Sopenharmony_ci /* Figure out the exact time for the time span */ 44913498266Sopenharmony_ci span_ms = Curl_timediff(now, p->speeder_time[checkindex]); 45013498266Sopenharmony_ci if(0 == span_ms) 45113498266Sopenharmony_ci span_ms = 1; /* at least one millisecond MUST have passed */ 45213498266Sopenharmony_ci 45313498266Sopenharmony_ci /* Calculate the average speed the last 'span_ms' milliseconds */ 45413498266Sopenharmony_ci amount = p->speeder[nowindex]- p->speeder[checkindex]; 45513498266Sopenharmony_ci 45613498266Sopenharmony_ci if(amount > CURL_OFF_T_C(4294967) /* 0xffffffff/1000 */) 45713498266Sopenharmony_ci /* the 'amount' value is bigger than would fit in 32 bits if 45813498266Sopenharmony_ci multiplied with 1000, so we use the double math for this */ 45913498266Sopenharmony_ci p->current_speed = (curl_off_t) 46013498266Sopenharmony_ci ((double)amount/((double)span_ms/1000.0)); 46113498266Sopenharmony_ci else 46213498266Sopenharmony_ci /* the 'amount' value is small enough to fit within 32 bits even 46313498266Sopenharmony_ci when multiplied with 1000 */ 46413498266Sopenharmony_ci p->current_speed = amount*CURL_OFF_T_C(1000)/span_ms; 46513498266Sopenharmony_ci } 46613498266Sopenharmony_ci else 46713498266Sopenharmony_ci /* the first second we use the average */ 46813498266Sopenharmony_ci p->current_speed = p->ulspeed + p->dlspeed; 46913498266Sopenharmony_ci 47013498266Sopenharmony_ci } /* Calculations end */ 47113498266Sopenharmony_ci return timetoshow; 47213498266Sopenharmony_ci} 47313498266Sopenharmony_ci 47413498266Sopenharmony_ci#ifndef CURL_DISABLE_PROGRESS_METER 47513498266Sopenharmony_cistatic void progress_meter(struct Curl_easy *data) 47613498266Sopenharmony_ci{ 47713498266Sopenharmony_ci char max5[6][10]; 47813498266Sopenharmony_ci curl_off_t dlpercen = 0; 47913498266Sopenharmony_ci curl_off_t ulpercen = 0; 48013498266Sopenharmony_ci curl_off_t total_percen = 0; 48113498266Sopenharmony_ci curl_off_t total_transfer; 48213498266Sopenharmony_ci curl_off_t total_expected_transfer; 48313498266Sopenharmony_ci char time_left[10]; 48413498266Sopenharmony_ci char time_total[10]; 48513498266Sopenharmony_ci char time_spent[10]; 48613498266Sopenharmony_ci curl_off_t ulestimate = 0; 48713498266Sopenharmony_ci curl_off_t dlestimate = 0; 48813498266Sopenharmony_ci curl_off_t total_estimate; 48913498266Sopenharmony_ci curl_off_t timespent = 49013498266Sopenharmony_ci (curl_off_t)data->progress.timespent/1000000; /* seconds */ 49113498266Sopenharmony_ci 49213498266Sopenharmony_ci if(!(data->progress.flags & PGRS_HEADERS_OUT)) { 49313498266Sopenharmony_ci if(data->state.resume_from) { 49413498266Sopenharmony_ci fprintf(data->set.err, 49513498266Sopenharmony_ci "** Resuming transfer from byte position %" 49613498266Sopenharmony_ci CURL_FORMAT_CURL_OFF_T "\n", data->state.resume_from); 49713498266Sopenharmony_ci } 49813498266Sopenharmony_ci fprintf(data->set.err, 49913498266Sopenharmony_ci " %% Total %% Received %% Xferd Average Speed " 50013498266Sopenharmony_ci "Time Time Time Current\n" 50113498266Sopenharmony_ci " Dload Upload " 50213498266Sopenharmony_ci "Total Spent Left Speed\n"); 50313498266Sopenharmony_ci data->progress.flags |= PGRS_HEADERS_OUT; /* headers are shown */ 50413498266Sopenharmony_ci } 50513498266Sopenharmony_ci 50613498266Sopenharmony_ci /* Figure out the estimated time of arrival for the upload */ 50713498266Sopenharmony_ci if((data->progress.flags & PGRS_UL_SIZE_KNOWN) && 50813498266Sopenharmony_ci (data->progress.ulspeed > CURL_OFF_T_C(0))) { 50913498266Sopenharmony_ci ulestimate = data->progress.size_ul / data->progress.ulspeed; 51013498266Sopenharmony_ci 51113498266Sopenharmony_ci if(data->progress.size_ul > CURL_OFF_T_C(10000)) 51213498266Sopenharmony_ci ulpercen = data->progress.uploaded / 51313498266Sopenharmony_ci (data->progress.size_ul/CURL_OFF_T_C(100)); 51413498266Sopenharmony_ci else if(data->progress.size_ul > CURL_OFF_T_C(0)) 51513498266Sopenharmony_ci ulpercen = (data->progress.uploaded*100) / 51613498266Sopenharmony_ci data->progress.size_ul; 51713498266Sopenharmony_ci } 51813498266Sopenharmony_ci 51913498266Sopenharmony_ci /* ... and the download */ 52013498266Sopenharmony_ci if((data->progress.flags & PGRS_DL_SIZE_KNOWN) && 52113498266Sopenharmony_ci (data->progress.dlspeed > CURL_OFF_T_C(0))) { 52213498266Sopenharmony_ci dlestimate = data->progress.size_dl / data->progress.dlspeed; 52313498266Sopenharmony_ci 52413498266Sopenharmony_ci if(data->progress.size_dl > CURL_OFF_T_C(10000)) 52513498266Sopenharmony_ci dlpercen = data->progress.downloaded / 52613498266Sopenharmony_ci (data->progress.size_dl/CURL_OFF_T_C(100)); 52713498266Sopenharmony_ci else if(data->progress.size_dl > CURL_OFF_T_C(0)) 52813498266Sopenharmony_ci dlpercen = (data->progress.downloaded*100) / 52913498266Sopenharmony_ci data->progress.size_dl; 53013498266Sopenharmony_ci } 53113498266Sopenharmony_ci 53213498266Sopenharmony_ci /* Now figure out which of them is slower and use that one for the 53313498266Sopenharmony_ci total estimate! */ 53413498266Sopenharmony_ci total_estimate = ulestimate>dlestimate?ulestimate:dlestimate; 53513498266Sopenharmony_ci 53613498266Sopenharmony_ci /* create the three time strings */ 53713498266Sopenharmony_ci time2str(time_left, total_estimate > 0?(total_estimate - timespent):0); 53813498266Sopenharmony_ci time2str(time_total, total_estimate); 53913498266Sopenharmony_ci time2str(time_spent, timespent); 54013498266Sopenharmony_ci 54113498266Sopenharmony_ci /* Get the total amount of data expected to get transferred */ 54213498266Sopenharmony_ci total_expected_transfer = 54313498266Sopenharmony_ci ((data->progress.flags & PGRS_UL_SIZE_KNOWN)? 54413498266Sopenharmony_ci data->progress.size_ul:data->progress.uploaded)+ 54513498266Sopenharmony_ci ((data->progress.flags & PGRS_DL_SIZE_KNOWN)? 54613498266Sopenharmony_ci data->progress.size_dl:data->progress.downloaded); 54713498266Sopenharmony_ci 54813498266Sopenharmony_ci /* We have transferred this much so far */ 54913498266Sopenharmony_ci total_transfer = data->progress.downloaded + data->progress.uploaded; 55013498266Sopenharmony_ci 55113498266Sopenharmony_ci /* Get the percentage of data transferred so far */ 55213498266Sopenharmony_ci if(total_expected_transfer > CURL_OFF_T_C(10000)) 55313498266Sopenharmony_ci total_percen = total_transfer / 55413498266Sopenharmony_ci (total_expected_transfer/CURL_OFF_T_C(100)); 55513498266Sopenharmony_ci else if(total_expected_transfer > CURL_OFF_T_C(0)) 55613498266Sopenharmony_ci total_percen = (total_transfer*100) / total_expected_transfer; 55713498266Sopenharmony_ci 55813498266Sopenharmony_ci fprintf(data->set.err, 55913498266Sopenharmony_ci "\r" 56013498266Sopenharmony_ci "%3" CURL_FORMAT_CURL_OFF_T " %s " 56113498266Sopenharmony_ci "%3" CURL_FORMAT_CURL_OFF_T " %s " 56213498266Sopenharmony_ci "%3" CURL_FORMAT_CURL_OFF_T " %s %s %s %s %s %s %s", 56313498266Sopenharmony_ci total_percen, /* 3 letters */ /* total % */ 56413498266Sopenharmony_ci max5data(total_expected_transfer, max5[2]), /* total size */ 56513498266Sopenharmony_ci dlpercen, /* 3 letters */ /* rcvd % */ 56613498266Sopenharmony_ci max5data(data->progress.downloaded, max5[0]), /* rcvd size */ 56713498266Sopenharmony_ci ulpercen, /* 3 letters */ /* xfer % */ 56813498266Sopenharmony_ci max5data(data->progress.uploaded, max5[1]), /* xfer size */ 56913498266Sopenharmony_ci max5data(data->progress.dlspeed, max5[3]), /* avrg dl speed */ 57013498266Sopenharmony_ci max5data(data->progress.ulspeed, max5[4]), /* avrg ul speed */ 57113498266Sopenharmony_ci time_total, /* 8 letters */ /* total time */ 57213498266Sopenharmony_ci time_spent, /* 8 letters */ /* time spent */ 57313498266Sopenharmony_ci time_left, /* 8 letters */ /* time left */ 57413498266Sopenharmony_ci max5data(data->progress.current_speed, max5[5]) 57513498266Sopenharmony_ci ); 57613498266Sopenharmony_ci 57713498266Sopenharmony_ci /* we flush the output stream to make it appear as soon as possible */ 57813498266Sopenharmony_ci fflush(data->set.err); 57913498266Sopenharmony_ci} 58013498266Sopenharmony_ci#else 58113498266Sopenharmony_ci /* progress bar disabled */ 58213498266Sopenharmony_ci#define progress_meter(x) Curl_nop_stmt 58313498266Sopenharmony_ci#endif 58413498266Sopenharmony_ci 58513498266Sopenharmony_ci 58613498266Sopenharmony_ci/* 58713498266Sopenharmony_ci * Curl_pgrsUpdate() returns 0 for success or the value returned by the 58813498266Sopenharmony_ci * progress callback! 58913498266Sopenharmony_ci */ 59013498266Sopenharmony_ciint Curl_pgrsUpdate(struct Curl_easy *data) 59113498266Sopenharmony_ci{ 59213498266Sopenharmony_ci struct curltime now = Curl_now(); /* what time is it */ 59313498266Sopenharmony_ci bool showprogress = progress_calc(data, now); 59413498266Sopenharmony_ci if(!(data->progress.flags & PGRS_HIDE)) { 59513498266Sopenharmony_ci if(data->set.fxferinfo) { 59613498266Sopenharmony_ci int result; 59713498266Sopenharmony_ci /* There's a callback set, call that */ 59813498266Sopenharmony_ci Curl_set_in_callback(data, true); 59913498266Sopenharmony_ci result = data->set.fxferinfo(data->set.progress_client, 60013498266Sopenharmony_ci data->progress.size_dl, 60113498266Sopenharmony_ci data->progress.downloaded, 60213498266Sopenharmony_ci data->progress.size_ul, 60313498266Sopenharmony_ci data->progress.uploaded); 60413498266Sopenharmony_ci Curl_set_in_callback(data, false); 60513498266Sopenharmony_ci if(result != CURL_PROGRESSFUNC_CONTINUE) { 60613498266Sopenharmony_ci if(result) 60713498266Sopenharmony_ci failf(data, "Callback aborted"); 60813498266Sopenharmony_ci return result; 60913498266Sopenharmony_ci } 61013498266Sopenharmony_ci } 61113498266Sopenharmony_ci else if(data->set.fprogress) { 61213498266Sopenharmony_ci int result; 61313498266Sopenharmony_ci /* The older deprecated callback is set, call that */ 61413498266Sopenharmony_ci Curl_set_in_callback(data, true); 61513498266Sopenharmony_ci result = data->set.fprogress(data->set.progress_client, 61613498266Sopenharmony_ci (double)data->progress.size_dl, 61713498266Sopenharmony_ci (double)data->progress.downloaded, 61813498266Sopenharmony_ci (double)data->progress.size_ul, 61913498266Sopenharmony_ci (double)data->progress.uploaded); 62013498266Sopenharmony_ci Curl_set_in_callback(data, false); 62113498266Sopenharmony_ci if(result != CURL_PROGRESSFUNC_CONTINUE) { 62213498266Sopenharmony_ci if(result) 62313498266Sopenharmony_ci failf(data, "Callback aborted"); 62413498266Sopenharmony_ci return result; 62513498266Sopenharmony_ci } 62613498266Sopenharmony_ci } 62713498266Sopenharmony_ci 62813498266Sopenharmony_ci if(showprogress) 62913498266Sopenharmony_ci progress_meter(data); 63013498266Sopenharmony_ci } 63113498266Sopenharmony_ci 63213498266Sopenharmony_ci return 0; 63313498266Sopenharmony_ci} 634