From 231872ddb0bb86a039e78e0b2f10d354a71938b2 Mon Sep 17 00:00:00 2001 From: "m.klimenko" Date: Tue, 15 Feb 2022 21:44:53 +0300 Subject: file(DOWNLOAD): Add options to download a range --- Help/command/file.rst | 12 ++++++++ Help/release/dev/file-download-range.rst | 6 ++++ Source/cmFileCommand.cxx | 31 +++++++++++++++++++ Tests/CMakeTests/FileDownloadTest.cmake.in | 48 ++++++++++++++++++++++++++++++ 4 files changed, 97 insertions(+) create mode 100644 Help/release/dev/file-download-range.rst diff --git a/Help/command/file.rst b/Help/command/file.rst index 799b6ff..2769577 100644 --- a/Help/command/file.rst +++ b/Help/command/file.rst @@ -1128,6 +1128,18 @@ Additional options to ``DOWNLOAD`` are: Historical short-hand for ``EXPECTED_HASH MD5=``. It is an error to specify this if ``DOWNLOAD`` is not given a ````. +``RANGE_START `` + .. versionadded:: 3.24 + + Offset of the start of the range in file in bytes. Could be omitted to + download up to the specified ``RANGE_END``. + +``RANGE_END `` + .. versionadded:: 3.24 + + Offset of the end of the range in file in bytes. Could be omitted to + download everything from the specified ``RANGE_START`` to the end of file. + Locking ^^^^^^^ diff --git a/Help/release/dev/file-download-range.rst b/Help/release/dev/file-download-range.rst new file mode 100644 index 0000000..194100d --- /dev/null +++ b/Help/release/dev/file-download-range.rst @@ -0,0 +1,6 @@ +file-download-range +------------------- + +* Add the fields ``RANGE_START`` and ``RANGE_END`` to ``file(DOWNLOAD)``. + Those fields provide a convenient way to specify the range, passed to the + libcurl, which can be useful for downloading parts of big binary files. diff --git a/Source/cmFileCommand.cxx b/Source/cmFileCommand.cxx index da2f15f..09e5015 100644 --- a/Source/cmFileCommand.cxx +++ b/Source/cmFileCommand.cxx @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -1778,6 +1779,7 @@ bool HandleDownloadCommand(std::vector const& args, std::string userpwd; std::vector curl_headers; + std::vector>> curl_ranges; while (i != args.end()) { if (*i == "TIMEOUT") { @@ -1890,6 +1892,27 @@ bool HandleDownloadCommand(std::vector const& args, return false; } curl_headers.push_back(*i); + } else if (*i == "RANGE_START") { + ++i; + if (i == args.end()) { + status.SetError("DOWNLOAD missing value for RANGE_START."); + return false; + } + curl_ranges.emplace_back(*i, cm::nullopt); + } else if (*i == "RANGE_END") { + ++i; + if (curl_ranges.empty()) { + curl_ranges.emplace_back("0", *i); + } else { + auto& last_range = curl_ranges.back(); + if (!last_range.second.has_value()) { + last_range.second = *i; + } else { + status.SetError("Multiple RANGE_END values is provided without " + "the corresponding RANGE_START."); + return false; + } + } } else if (file.empty()) { file = *i; } else { @@ -1899,6 +1922,7 @@ bool HandleDownloadCommand(std::vector const& args, } ++i; } + // Can't calculate hash if we don't save the file. // TODO Incrementally calculate hash in the write callback as the file is // being downloaded so this check can be relaxed. @@ -1984,6 +2008,13 @@ bool HandleDownloadCommand(std::vector const& args, check_curl_result(res, "DOWNLOAD cannot set TLS/SSL Verify off: "); } + for (const auto& range : curl_ranges) { + std::string curl_range = range.first + '-' + + (range.second.has_value() ? range.second.value() : ""); + res = ::curl_easy_setopt(curl, CURLOPT_RANGE, curl_range.c_str()); + check_curl_result(res, "DOWNLOAD cannot set range: "); + } + // check to see if a CAINFO file has been specified // command arg comes first std::string const& cainfo_err = cmCurlSetCAInfo(curl, cainfo); diff --git a/Tests/CMakeTests/FileDownloadTest.cmake.in b/Tests/CMakeTests/FileDownloadTest.cmake.in index e0ce99a..255909d 100644 --- a/Tests/CMakeTests/FileDownloadTest.cmake.in +++ b/Tests/CMakeTests/FileDownloadTest.cmake.in @@ -179,3 +179,51 @@ if(EXISTS TIMEOUT) message(SEND_ERROR "TIMEOUT argument was incorrectly interpreted as a filename") endif() message(STATUS "${status}") + +message(STATUS "FileDownload:14") +file(DOWNLOAD + ${url} + ${dir}/file14.bin + TIMEOUT ${timeout} + STATUS status + RANGE_START 0 + EXPECTED_MD5 dbd330d52f4dbd60115d4191904ded92 + ) +__reportIfWrongStatus("${status}" 0) + +message(STATUS "FileDownload:15") +file(DOWNLOAD + ${url} + ${dir}/file15.bin + TIMEOUT ${timeout} + STATUS status + RANGE_END 50 + EXPECTED_MD5 8592e5665b839b5d23825dc84c135b61 + ) +__reportIfWrongStatus("${status}" 0) + +message(STATUS "FileDownload:16") +file(DOWNLOAD + ${url} + ${dir}/file16.bin + TIMEOUT ${timeout} + STATUS status + RANGE_START 10 + RANGE_END 50 + EXPECTED_MD5 36cd52681e6c6c8fef85fcd9e86fc30d + ) +__reportIfWrongStatus("${status}" 0) + +message(STATUS "FileDownload:17") +file(DOWNLOAD + ${url} + ${dir}/file17.bin + TIMEOUT ${timeout} + STATUS status + RANGE_START 0 + RANGE_END 50 + RANGE_START 60 + RANGE_END 100 + EXPECTED_MD5 c5c9e74e82d493dd901eecccd659cebc + ) +__reportIfWrongStatus("${status}" 0) -- cgit v0.12