@@ -762,12 +762,15 @@ public:
762762
763763 // Download URL to file with streaming progress.
764764 // Follows redirects. Calls onProgress periodically during body read.
765+ // isCancelled is checked after each block — return true to abort.
765766 DownloadToFileResult download_to_file (
766767 const std::string& url,
767768 const std::filesystem::path& destFile,
768- DownloadProgressFn onProgress = nullptr )
769+ DownloadProgressFn onProgress = nullptr ,
770+ std::function<bool ()> isCancelled = nullptr)
769771 {
770- return download_to_file_impl (url, destFile, std::move (onProgress), 0 );
772+ return download_to_file_impl (url, destFile, std::move (onProgress),
773+ std::move (isCancelled), 0 );
771774 }
772775
773776 HttpClientConfig& config () { return config_; }
@@ -778,6 +781,7 @@ private:
778781 const std::string& url,
779782 const std::filesystem::path& destFile,
780783 DownloadProgressFn onProgress,
784+ std::function<bool ()> isCancelled,
781785 int redirectCount)
782786 {
783787 DownloadToFileResult result;
@@ -907,7 +911,7 @@ private:
907911 location;
908912 }
909913 return download_to_file_impl (location, destFile, std::move (onProgress),
910- redirectCount + 1 );
914+ std::move (isCancelled), redirectCount + 1 );
911915 }
912916
913917 if (result.statusCode < 200 || result.statusCode >= 300 ) {
@@ -929,9 +933,23 @@ private:
929933 std::int64_t totalBytes = contentLength > 0 ? contentLength : 0 ;
930934 std::int64_t downloaded = 0 ;
931935
936+ // Check cancellation between blocks
937+ auto cancelled = [&]() -> bool {
938+ if (isCancelled && isCancelled ()) {
939+ result.error = " cancelled" ;
940+ ofs.close ();
941+ result.bytesWritten = downloaded;
942+ sock->close ();
943+ pool_.erase (poolKey);
944+ return true ;
945+ }
946+ return false ;
947+ };
948+
932949 // Read body and stream to file
933950 if (chunked) {
934951 while (true ) {
952+ if (cancelled ()) return result;
935953 std::string sizeLine = read_line (*sock, config_.readTimeoutMs );
936954 auto semi = sizeLine.find (' ;' );
937955 if (semi != std::string::npos) sizeLine = sizeLine.substr (0 , semi);
@@ -944,10 +962,10 @@ private:
944962 break ;
945963 }
946964
947- // Read chunk in sub-blocks for progress
948965 int remaining = chunkSize;
949966 char buf[8192 ];
950967 while (remaining > 0 ) {
968+ if (cancelled ()) return result;
951969 int toRead = remaining > static_cast <int >(sizeof (buf))
952970 ? static_cast <int >(sizeof (buf)) : remaining;
953971 if (!read_exact (*sock, buf, toRead, config_.readTimeoutMs )) {
@@ -961,12 +979,13 @@ private:
961979 remaining -= toRead;
962980 if (onProgress) onProgress (totalBytes, downloaded);
963981 }
964- read_line (*sock, config_.readTimeoutMs ); // trailing \r\n
982+ read_line (*sock, config_.readTimeoutMs );
965983 }
966984 } else if (contentLength > 0 ) {
967985 char buf[8192 ];
968986 std::int64_t remaining = contentLength;
969987 while (remaining > 0 ) {
988+ if (cancelled ()) return result;
970989 int toRead = remaining > static_cast <std::int64_t >(sizeof (buf))
971990 ? static_cast <int >(sizeof (buf))
972991 : static_cast <int >(remaining);
@@ -982,10 +1001,10 @@ private:
9821001 if (onProgress) onProgress (totalBytes, downloaded);
9831002 }
9841003 } else {
985- // Read until connection close
9861004 connectionClose = true ;
9871005 char buf[8192 ];
9881006 while (true ) {
1007+ if (cancelled ()) return result;
9891008 if (!sock->wait_readable (config_.readTimeoutMs )) break ;
9901009 int ret = sock->read (buf, sizeof (buf));
9911010 if (ret <= 0 ) break ;
0 commit comments