diff --git a/toolsrc/include/vcpkg/base/downloads.h b/toolsrc/include/vcpkg/base/downloads.h new file mode 100644 index 00000000000000..f30e865a679227 --- /dev/null +++ b/toolsrc/include/vcpkg/base/downloads.h @@ -0,0 +1,16 @@ +#pragma once + +#include + +namespace vcpkg::Downloads +{ + void verify_downloaded_file_hash(const Files::Filesystem& fs, + const std::string& url, + const fs::path& path, + const std::string& sha512); + + void download_file(vcpkg::Files::Filesystem& fs, + const std::string& url, + const fs::path& download_path, + const std::string& sha512); +} diff --git a/toolsrc/src/vcpkg/base/downloads.cpp b/toolsrc/src/vcpkg/base/downloads.cpp new file mode 100644 index 00000000000000..a5ee053912fbf6 --- /dev/null +++ b/toolsrc/src/vcpkg/base/downloads.cpp @@ -0,0 +1,142 @@ +#include "pch.h" + +#include +#include +#include + +namespace vcpkg::Downloads +{ +#if defined(_WIN32) + static void winhttp_download_file(Files::Filesystem& fs, + CStringView target_file_path, + CStringView hostname, + CStringView url_path) + { + // Make sure the directories are present, otherwise fopen_s fails + const auto dir = fs::path(target_file_path.c_str()).parent_path(); + std::error_code ec; + fs.create_directories(dir, ec); + Checks::check_exit(VCPKG_LINE_INFO, !ec, "Could not create directories %s", dir.u8string()); + + FILE* f = nullptr; + const errno_t err = fopen_s(&f, target_file_path.c_str(), "wb"); + Checks::check_exit(VCPKG_LINE_INFO, + !err, + "Could not download https://%s%s. Failed to open file %s. Error code was %s", + hostname, + url_path, + target_file_path, + std::to_string(err)); + + auto hSession = WinHttpOpen( + L"vcpkg/1.0", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0); + Checks::check_exit(VCPKG_LINE_INFO, hSession, "WinHttpOpen() failed: %d", GetLastError()); + + // Use Windows 10 defaults on Windows 7 + DWORD secure_protocols(WINHTTP_FLAG_SECURE_PROTOCOL_SSL3 | WINHTTP_FLAG_SECURE_PROTOCOL_TLS1 | + WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1 | WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2); + WinHttpSetOption(hSession, WINHTTP_OPTION_SECURE_PROTOCOLS, &secure_protocols, sizeof(secure_protocols)); + + // Specify an HTTP server. + auto hConnect = WinHttpConnect(hSession, Strings::to_utf16(hostname).c_str(), INTERNET_DEFAULT_HTTPS_PORT, 0); + Checks::check_exit(VCPKG_LINE_INFO, hConnect, "WinHttpConnect() failed: %d", GetLastError()); + + // Create an HTTP request handle. + auto hRequest = WinHttpOpenRequest(hConnect, + L"GET", + Strings::to_utf16(url_path).c_str(), + nullptr, + WINHTTP_NO_REFERER, + WINHTTP_DEFAULT_ACCEPT_TYPES, + WINHTTP_FLAG_SECURE); + Checks::check_exit(VCPKG_LINE_INFO, hRequest, "WinHttpOpenRequest() failed: %d", GetLastError()); + + // Send a request. + auto bResults = + WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0); + Checks::check_exit(VCPKG_LINE_INFO, bResults, "WinHttpSendRequest() failed: %d", GetLastError()); + + // End the request. + bResults = WinHttpReceiveResponse(hRequest, NULL); + Checks::check_exit(VCPKG_LINE_INFO, bResults, "WinHttpReceiveResponse() failed: %d", GetLastError()); + + std::vector buf; + + size_t total_downloaded_size = 0; + DWORD dwSize = 0; + do + { + DWORD downloaded_size = 0; + bResults = WinHttpQueryDataAvailable(hRequest, &dwSize); + Checks::check_exit(VCPKG_LINE_INFO, bResults, "WinHttpQueryDataAvailable() failed: %d", GetLastError()); + + if (buf.size() < dwSize) buf.resize(dwSize * 2); + + bResults = WinHttpReadData(hRequest, (LPVOID)buf.data(), dwSize, &downloaded_size); + Checks::check_exit(VCPKG_LINE_INFO, bResults, "WinHttpReadData() failed: %d", GetLastError()); + fwrite(buf.data(), 1, downloaded_size, f); + + total_downloaded_size += downloaded_size; + } while (dwSize > 0); + + WinHttpCloseHandle(hSession); + WinHttpCloseHandle(hConnect); + WinHttpCloseHandle(hRequest); + fflush(f); + fclose(f); + } +#endif + + void verify_downloaded_file_hash(const Files::Filesystem& fs, + const std::string& url, + const fs::path& path, + const std::string& sha512) + { + const std::string actual_hash = Commands::Hash::get_file_hash(fs, path, "SHA512"); + Checks::check_exit(VCPKG_LINE_INFO, + sha512 == actual_hash, + "File does not have the expected hash:\n" + " url : [ %s ]\n" + " File path : [ %s ]\n" + " Expected hash : [ %s ]\n" + " Actual hash : [ %s ]\n", + url, + path.u8string(), + sha512, + actual_hash); + } + + void download_file(vcpkg::Files::Filesystem& fs, + const std::string& url, + const fs::path& download_path, + const std::string& sha512) + { + const std::string download_path_part = download_path.u8string() + ".part"; + std::error_code ec; + fs.remove(download_path, ec); + fs.remove(download_path_part, ec); +#if defined(_WIN32) + auto url_no_proto = url.substr(8); // drop https:// + auto path_begin = Util::find(url_no_proto, '/'); + std::string hostname(url_no_proto.begin(), path_begin); + std::string path(path_begin, url_no_proto.end()); + + winhttp_download_file(fs, download_path_part.c_str(), hostname, path); +#else + const auto code = System::cmd_execute( + Strings::format(R"(curl -L '%s' --create-dirs --output '%s')", url, download_path_part)); + Checks::check_exit(VCPKG_LINE_INFO, code == 0, "Could not download %s", url); +#endif + + verify_downloaded_file_hash(fs, url, download_path_part, sha512); + fs.rename(download_path_part, download_path, ec); + Checks::check_exit(VCPKG_LINE_INFO, + !ec, + "Failed to do post-download rename-in-place.\n" + "fs.rename(%s, %s, %s)", + download_path_part, + download_path.u8string(), + ec.message()); + } + +} diff --git a/toolsrc/src/vcpkg/commands.fetch.cpp b/toolsrc/src/vcpkg/commands.fetch.cpp index 03f7a8a4562cfc..0656989c56f848 100644 --- a/toolsrc/src/vcpkg/commands.fetch.cpp +++ b/toolsrc/src/vcpkg/commands.fetch.cpp @@ -1,6 +1,7 @@ #include "pch.h" #include +#include #include #include #include @@ -335,139 +336,6 @@ namespace vcpkg::Commands::Fetch ec.message()); } - static void verify_hash(const Files::Filesystem& fs, - const std::string& url, - const fs::path& path, - const std::string& sha512) - { - const std::string actual_hash = Hash::get_file_hash(fs, path, "SHA512"); - Checks::check_exit(VCPKG_LINE_INFO, - sha512 == actual_hash, - "File does not have the expected hash:\n" - " url : [ %s ]\n" - " File path : [ %s ]\n" - " Expected hash : [ %s ]\n" - " Actual hash : [ %s ]\n", - url, - path.u8string(), - sha512, - actual_hash); - } - -#if defined(_WIN32) - static void winhttp_download_file(Files::Filesystem& fs, - CStringView target_file_path, - CStringView hostname, - CStringView url_path) - { - // Make sure the directories are present, otherwise fopen_s fails - const auto dir = fs::path(target_file_path.c_str()).parent_path(); - std::error_code ec; - fs.create_directories(dir, ec); - Checks::check_exit(VCPKG_LINE_INFO, !ec, "Could not create directories %s", dir.u8string()); - - FILE* f = nullptr; - const errno_t err = fopen_s(&f, target_file_path.c_str(), "wb"); - Checks::check_exit(VCPKG_LINE_INFO, - !err, - "Could not download https://%s%s. Failed to open file %s. Error code was %s", - hostname, - url_path, - target_file_path, - std::to_string(err)); - - auto hSession = WinHttpOpen( - L"vcpkg/1.0", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0); - Checks::check_exit(VCPKG_LINE_INFO, hSession, "WinHttpOpen() failed: %d", GetLastError()); - - // Use Windows 10 defaults on Windows 7 - DWORD secure_protocols(WINHTTP_FLAG_SECURE_PROTOCOL_SSL3 | WINHTTP_FLAG_SECURE_PROTOCOL_TLS1 | - WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1 | WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2); - WinHttpSetOption(hSession, WINHTTP_OPTION_SECURE_PROTOCOLS, &secure_protocols, sizeof(secure_protocols)); - - // Specify an HTTP server. - auto hConnect = WinHttpConnect(hSession, Strings::to_utf16(hostname).c_str(), INTERNET_DEFAULT_HTTPS_PORT, 0); - Checks::check_exit(VCPKG_LINE_INFO, hConnect, "WinHttpConnect() failed: %d", GetLastError()); - - // Create an HTTP request handle. - auto hRequest = WinHttpOpenRequest(hConnect, - L"GET", - Strings::to_utf16(url_path).c_str(), - nullptr, - WINHTTP_NO_REFERER, - WINHTTP_DEFAULT_ACCEPT_TYPES, - WINHTTP_FLAG_SECURE); - Checks::check_exit(VCPKG_LINE_INFO, hRequest, "WinHttpOpenRequest() failed: %d", GetLastError()); - - // Send a request. - auto bResults = - WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0); - Checks::check_exit(VCPKG_LINE_INFO, bResults, "WinHttpSendRequest() failed: %d", GetLastError()); - - // End the request. - bResults = WinHttpReceiveResponse(hRequest, NULL); - Checks::check_exit(VCPKG_LINE_INFO, bResults, "WinHttpReceiveResponse() failed: %d", GetLastError()); - - std::vector buf; - - size_t total_downloaded_size = 0; - DWORD dwSize = 0; - do - { - DWORD downloaded_size = 0; - bResults = WinHttpQueryDataAvailable(hRequest, &dwSize); - Checks::check_exit(VCPKG_LINE_INFO, bResults, "WinHttpQueryDataAvailable() failed: %d", GetLastError()); - - if (buf.size() < dwSize) buf.resize(dwSize * 2); - - bResults = WinHttpReadData(hRequest, (LPVOID)buf.data(), dwSize, &downloaded_size); - Checks::check_exit(VCPKG_LINE_INFO, bResults, "WinHttpReadData() failed: %d", GetLastError()); - fwrite(buf.data(), 1, downloaded_size, f); - - total_downloaded_size += downloaded_size; - } while (dwSize > 0); - - WinHttpCloseHandle(hSession); - WinHttpCloseHandle(hConnect); - WinHttpCloseHandle(hRequest); - fflush(f); - fclose(f); - } -#endif - - static void download_file(Files::Filesystem& fs, - const std::string& url, - const fs::path& download_path, - const std::string& sha512) - { - const std::string download_path_part = download_path.u8string() + ".part"; - std::error_code ec; - fs.remove(download_path, ec); - fs.remove(download_path_part, ec); -#if defined(_WIN32) - auto url_no_proto = url.substr(8); // drop https:// - auto path_begin = Util::find(url_no_proto, '/'); - std::string hostname(url_no_proto.begin(), path_begin); - std::string path(path_begin, url_no_proto.end()); - - winhttp_download_file(fs, download_path_part.c_str(), hostname, path); -#else - const auto code = System::cmd_execute( - Strings::format(R"(curl -L '%s' --create-dirs --output '%s')", url, download_path_part)); - Checks::check_exit(VCPKG_LINE_INFO, code == 0, "Could not download %s", url); -#endif - - verify_hash(fs, url, download_path_part, sha512); - fs.rename(download_path_part, download_path, ec); - Checks::check_exit(VCPKG_LINE_INFO, - !ec, - "Failed to do post-download rename-in-place.\n" - "fs.rename(%s, %s, %s)", - download_path_part, - download_path.u8string(), - ec.message()); - } - static fs::path fetch_tool(const VcpkgPaths& paths, const std::string& tool_name, const ToolData& tool_data) { const std::array& version = tool_data.version; @@ -488,12 +356,12 @@ namespace vcpkg::Commands::Fetch if (!fs.exists(tool_data.download_path)) { System::println("Downloading %s...", tool_name); - download_file(fs, tool_data.url, tool_data.download_path, tool_data.sha512); + Downloads::download_file(fs, tool_data.url, tool_data.download_path, tool_data.sha512); System::println("Downloading %s... done.", tool_name); } else { - verify_hash(fs, tool_data.url, tool_data.download_path, tool_data.sha512); + Downloads::verify_downloaded_file_hash(fs, tool_data.url, tool_data.download_path, tool_data.sha512); } if (tool_data.is_archive) diff --git a/toolsrc/vcpkglib/vcpkglib.vcxproj b/toolsrc/vcpkglib/vcpkglib.vcxproj index 871d0c58deb716..720a1043fd95eb 100644 --- a/toolsrc/vcpkglib/vcpkglib.vcxproj +++ b/toolsrc/vcpkglib/vcpkglib.vcxproj @@ -141,6 +141,7 @@ + @@ -195,6 +196,7 @@ + diff --git a/toolsrc/vcpkglib/vcpkglib.vcxproj.filters b/toolsrc/vcpkglib/vcpkglib.vcxproj.filters index 6a23a09b5837a1..2990179292971d 100644 --- a/toolsrc/vcpkglib/vcpkglib.vcxproj.filters +++ b/toolsrc/vcpkglib/vcpkglib.vcxproj.filters @@ -201,6 +201,9 @@ Source Files\vcpkg + + Source Files\vcpkg\base + @@ -347,5 +350,8 @@ Header Files\vcpkg\base + + Header Files\vcpkg\base + \ No newline at end of file