diff --git a/bundle-uri.c b/bundle-uri.c index 744257c49c1328f249c9b611c66084ded75cbe24..0ef96cd00f390a2c9852b6bdce7fc04dfc9a43e8 100644 --- a/bundle-uri.c +++ b/bundle-uri.c @@ -298,7 +298,6 @@ static int download_https_uri_to_file(const char *file, const char *uri) int found_get = 0; strvec_pushl(&cp.args, "git-remote-https", uri, NULL); - cp.err = -1; cp.in = -1; cp.out = -1; @@ -333,6 +332,7 @@ static int download_https_uri_to_file(const char *file, const char *uri) goto cleanup; } + fprintf(child_in, "option progress true\n"); fprintf(child_in, "get %s %s\n\n", uri, file); cleanup: diff --git a/http.c b/http.c index f4504133e88869dadb3ebad0b38902498d3cb5a1..5c0c6ef20476860e0c4ef41b246ec00177213bf2 100644 --- a/http.c +++ b/http.c @@ -13,6 +13,7 @@ #include "credential.h" #include "version.h" #include "pkt-line.h" +#include "progress.h" #include "gettext.h" #include "trace.h" #include "transport.h" @@ -1245,6 +1246,8 @@ static CURL *get_curl_handle(void) set_curl_keepalive(result); + curl_easy_setopt(result, CURLOPT_NOSIGNAL, 1); + return result; } @@ -1502,6 +1505,9 @@ struct active_request_slot *get_active_slot(void) curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1); curl_easy_setopt(slot->curl, CURLOPT_FAILONERROR, 1); curl_easy_setopt(slot->curl, CURLOPT_RANGE, NULL); + curl_easy_setopt(slot->curl, CURLOPT_NOPROGRESS, 1L); + curl_easy_setopt(slot->curl, CURLOPT_XFERINFODATA, NULL); + curl_easy_setopt(slot->curl, CURLOPT_XFERINFOFUNCTION, NULL); /* * Default following to off unless "ALWAYS" is configured; this gives @@ -2066,6 +2072,21 @@ static void http_opt_request_remainder(CURL *curl, off_t pos) #define HTTP_REQUEST_STRBUF 0 #define HTTP_REQUEST_FILE 1 +static int http_progress_callback(void *clientp, curl_off_t dltotal, + curl_off_t dlnow, curl_off_t ultotal UNUSED, + curl_off_t ulnow UNUSED) +{ + struct progress *progress = clientp; + + if (progress) { + progress_set_total(progress, dltotal); + display_progress(progress, dlnow); + display_throughput(progress, dlnow); + } + + return 0; +} + static int http_request(const char *url, void *result, int target, const struct http_get_options *options) @@ -2074,6 +2095,7 @@ static int http_request(const char *url, struct slot_results results; struct curl_slist *headers = http_copy_default_headers(); struct strbuf buf = STRBUF_INIT; + struct progress *progress = NULL; const char *accept_language; int ret; @@ -2110,6 +2132,16 @@ static int http_request(const char *url, if (options && options->initial_request && http_follow_config == HTTP_FOLLOW_INITIAL) curl_easy_setopt(slot->curl, CURLOPT_FOLLOWLOCATION, 1); + if (options && options->progress) { + progress = start_progress(the_repository, + _("Downloading via HTTP"), 0); + progress_set_fd(progress, fileno(stderr)); + freopen("/dev/null", "w", stderr); + + curl_easy_setopt(slot->curl, CURLOPT_NOPROGRESS, 0L); + curl_easy_setopt(slot->curl, CURLOPT_XFERINFODATA, progress); + curl_easy_setopt(slot->curl, CURLOPT_XFERINFOFUNCTION, &http_progress_callback); + } headers = curl_slist_append(headers, buf.buf); @@ -2132,6 +2164,13 @@ static int http_request(const char *url, ret = run_one_slot(slot, &results); + if (progress) { + curl_easy_setopt(slot->curl, CURLOPT_NOPROGRESS, 1L); + curl_easy_setopt(slot->curl, CURLOPT_XFERINFODATA, NULL); + curl_easy_setopt(slot->curl, CURLOPT_XFERINFOFUNCTION, NULL); + stop_progress(&progress); + } + if (options && options->content_type) { struct strbuf raw = STRBUF_INIT; curlinfo_strbuf(slot->curl, CURLINFO_CONTENT_TYPE, &raw); diff --git a/http.h b/http.h index 36202139f451ffa1e5b2cbaeae49d0875a4f76a7..09ebbdfefe8afa9d1a2f96510484a7377df94afc 100644 --- a/http.h +++ b/http.h @@ -146,6 +146,11 @@ struct http_get_options { * request has completed. */ struct string_list *extra_headers; + + /* + * If not zero, display the progress. + */ + int progress; }; /* Return values for http_get_*() */ diff --git a/progress.c b/progress.c index 8d5ae70f3a9ec7042bf12782190f6514014b0287..1955262000e45eba950cd62eb30f5a164a9b2a11 100644 --- a/progress.c +++ b/progress.c @@ -40,6 +40,7 @@ struct progress { const char *title; uint64_t last_value; uint64_t total; + int fd; unsigned last_percent; unsigned delay; unsigned sparse; @@ -135,12 +136,18 @@ static void display(struct progress *progress, uint64_t n, const char *done) } } else if (progress_update) { strbuf_reset(counters_sb); - strbuf_addf(counters_sb, "%"PRIuMAX"%s", (uintmax_t)n, tp); + if (n > 0) + strbuf_addf(counters_sb, "%" PRIuMAX "%s", + (uintmax_t)n, tp); + else + strbuf_addstr(counters_sb, tp); show_update = 1; } if (show_update) { - if (is_foreground_fd(fileno(stderr)) || done) { + int fd = progress->fd ? progress->fd : fileno(stderr); + + if (is_foreground_fd(fd) || done) { const char *eol = done ? done : "\r"; size_t clear_len = counters_sb->len < last_count_len ? last_count_len - counters_sb->len + 1 : @@ -151,17 +158,17 @@ static void display(struct progress *progress, uint64_t n, const char *done) int cols = term_columns(); if (progress->split) { - fprintf(stderr, " %s%*s", counters_sb->buf, + dprintf(fd, " %s%*s", counters_sb->buf, (int) clear_len, eol); } else if (!done && cols < progress_line_len) { clear_len = progress->title_len + 1 < cols ? cols - progress->title_len - 1 : 0; - fprintf(stderr, "%s:%*s\n %s%s", + dprintf(fd, "%s:%*s\n %s%s", progress->title, (int) clear_len, "", counters_sb->buf, eol); progress->split = 1; } else { - fprintf(stderr, "%s: %s%*s", progress->title, + dprintf(fd, "%s: %s%*s", progress->title, counters_sb->buf, (int) clear_len, eol); } fflush(stderr); @@ -170,11 +177,12 @@ static void display(struct progress *progress, uint64_t n, const char *done) } } -static void throughput_string(struct strbuf *buf, uint64_t total, - unsigned int rate) +static void throughput_string(struct progress *progress, struct strbuf *buf, + uint64_t total, unsigned int rate) { strbuf_reset(buf); - strbuf_addstr(buf, ", "); + if (progress->total || progress->last_value > 0) + strbuf_addstr(buf, ", "); strbuf_humanise_bytes(buf, total); strbuf_addstr(buf, " | "); strbuf_humanise_rate(buf, rate * 1024); @@ -243,7 +251,7 @@ void display_throughput(struct progress *progress, uint64_t total) tp->last_misecs[tp->idx] = misecs; tp->idx = (tp->idx + 1) % TP_IDX_MAX; - throughput_string(&tp->display, total, rate); + throughput_string(progress, &tp->display, total, rate); if (progress->last_value != -1 && progress_update) display(progress, progress->last_value, NULL); } @@ -276,6 +284,18 @@ static struct progress *start_progress_delay(struct repository *r, return progress; } +void progress_set_total(struct progress *progress, uint64_t total) +{ + if (progress) + progress->total = total; +} + +void progress_set_fd(struct progress *progress, int fd) +{ + if (progress) + progress->fd = fd; +} + static int get_default_delay(void) { static int delay_in_secs = -1; @@ -337,7 +357,7 @@ static void force_last_update(struct progress *progress, const char *msg) unsigned int misecs, rate; misecs = ((now_ns - progress->start_ns) * 4398) >> 32; rate = tp->curr_total / (misecs ? misecs : 1); - throughput_string(&tp->display, tp->curr_total, rate); + throughput_string(progress, &tp->display, tp->curr_total, rate); } progress_update = 1; buf = xstrfmt(", %s.\n", msg); diff --git a/progress.h b/progress.h index ed068c7bab845b17f55d4ed8f0907497ad23fd47..f12c82adc4215e20e13186b3c2c269e6376248cf 100644 --- a/progress.h +++ b/progress.h @@ -15,6 +15,8 @@ void progress_test_force_update(void); void display_throughput(struct progress *progress, uint64_t total); void display_progress(struct progress *progress, uint64_t n); +void progress_set_total(struct progress *progress, uint64_t total); +void progress_set_fd(struct progress *progress, int fd); struct progress *start_progress(struct repository *r, const char *title, uint64_t total); struct progress *start_sparse_progress(struct repository *r, diff --git a/remote-curl.c b/remote-curl.c index 1273507a96cae97eeaac9a95a47618e5e4f72850..f710d6b3cb8cdc3226f0abcf6f3d3f9824d7e982 100644 --- a/remote-curl.c +++ b/remote-curl.c @@ -1317,6 +1317,7 @@ static void parse_get(const char *arg) { struct strbuf url = STRBUF_INIT; struct strbuf path = STRBUF_INIT; + struct http_get_options http_options = {0}; const char *space; space = strchr(arg, ' '); @@ -1327,7 +1328,12 @@ static void parse_get(const char *arg) strbuf_add(&url, arg, space - arg); strbuf_addstr(&path, space + 1); - if (http_get_file(url.buf, path.buf, NULL)) + http_options.initial_request = 1; + + if (options.progress) + http_options.progress = 1; + + if (http_get_file(url.buf, path.buf, &http_options)) die(_("failed to download file at URL '%s'"), url.buf); strbuf_release(&url); diff --git a/t/helper/test-progress.c b/t/helper/test-progress.c index 1f75b7bd199aff9e332c2601e444ce7b165e78c2..3a73d6fe0ab2997454403f591b2644ffb811646d 100644 --- a/t/helper/test-progress.c +++ b/t/helper/test-progress.c @@ -74,6 +74,11 @@ int cmd__progress(int argc, const char **argv) if (*end != '\0') die("invalid input: '%s'", line.buf); display_progress(progress, item_count); + } else if (skip_prefix(line.buf, "total ", (const char **) &end)) { + uint64_t total = strtoull(end, &end, 10); + if (*end != '\0') + die("invalid input: '%s'\n", line.buf); + progress_set_total(progress, total); } else if (skip_prefix(line.buf, "throughput ", (const char **) &end)) { uint64_t byte_count, test_ms; diff --git a/t/t0500-progress-display.sh b/t/t0500-progress-display.sh index d1a498a216fb522eefa57ac7cb2c63d0ebc21f47..582b9fa899b553e974a286e901b8cd8219c78f8c 100755 --- a/t/t0500-progress-display.sh +++ b/t/t0500-progress-display.sh @@ -55,6 +55,30 @@ test_expect_success 'progress display with total' ' test_cmp expect out ' +test_expect_success 'progress display modify total' ' + cat >expect <<-\EOF && + Working hard: 1 + Working hard: 66% (2/3) + Working hard: 100% (3/3) + Working hard: 100% (3/3), done. + EOF + + cat >in <<-\EOF && + start 0 + update + progress 1 + update + total 3 + progress 2 + progress 3 + stop + EOF + test-tool progress stderr && + + show_cr out && + test_cmp expect out +' + test_expect_success 'progress display breaks long lines #1' ' sed -e "s/Z$//" >expect <<\EOF && Working hard.......2.........3.........4.........5.........6: 0% (100/100000) @@ -239,6 +263,37 @@ test_expect_success 'progress display with throughput and total' ' test_cmp expect out ' +test_expect_success 'progress display throughput without total' ' + cat >expect <<-\EOF && + Working hard: + Working hard: 200.00 KiB | 100.00 KiB/s + Working hard: 300.00 KiB | 100.00 KiB/s + Working hard: 400.00 KiB | 100.00 KiB/s + Working hard: 400.00 KiB | 100.00 KiB/s, done. + EOF + + cat >in <<-\EOF && + start 0 + throughput 102400 1000 + update + progress 0 + throughput 204800 2000 + update + progress 0 + throughput 307200 3000 + update + progress 0 + throughput 409600 4000 + update + progress 0 + stop + EOF + test-tool progress stderr && + + show_cr out && + test_cmp expect out +' + test_expect_success 'cover up after throughput shortens' ' cat >expect <<-\EOF && Working hard: 1 diff --git a/t/t5557-http-get.sh b/t/t5557-http-get.sh index 67fcc23f1105c12798c16ca01522f507b54b4b73..41f3d16ef9f4a40a1c7d2537b3d4d1884b4b64f3 100755 --- a/t/t5557-http-get.sh +++ b/t/t5557-http-get.sh @@ -35,4 +35,19 @@ test_expect_success 'get by URL: 200' ' test_cmp "$HTTPD_DOCUMENT_ROOT_PATH/exists.txt" file2 ' +test_expect_success 'get by URL with progress' ' + echo hello >"$HTTPD_DOCUMENT_ROOT_PATH/hello.txt" && + + url="$HTTPD_URL/hello.txt" && + cat >input <<-EOF && + capabilities + option progress true + get $url file3 + + EOF + + git remote-http $url err && + test_grep "^Downloading via HTTP: 100%" err +' + test_done