Skip to content

Commit

Permalink
Introduce downloads.h/cpp
Browse files Browse the repository at this point in the history
  • Loading branch information
alexkaratarakis committed Jun 20, 2018
1 parent 84d6584 commit 3e76baa
Show file tree
Hide file tree
Showing 5 changed files with 169 additions and 135 deletions.
16 changes: 16 additions & 0 deletions toolsrc/include/vcpkg/base/downloads.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#pragma once

#include <vcpkg/base/files.h>

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);
}
142 changes: 142 additions & 0 deletions toolsrc/src/vcpkg/base/downloads.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
#include "pch.h"

#include <vcpkg/base/downloads.h>
#include <vcpkg/base/util.h>
#include <vcpkg/commands.h>

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<char> 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());
}

}
138 changes: 3 additions & 135 deletions toolsrc/src/vcpkg/commands.fetch.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "pch.h"

#include <vcpkg/base/checks.h>
#include <vcpkg/base/downloads.h>
#include <vcpkg/base/sortedvector.h>
#include <vcpkg/base/strings.h>
#include <vcpkg/base/system.h>
Expand Down Expand Up @@ -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<char> 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<int, 3>& version = tool_data.version;
Expand All @@ -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)
Expand Down
2 changes: 2 additions & 0 deletions toolsrc/vcpkglib/vcpkglib.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@
<ClInclude Include="..\include\vcpkg\base\chrono.h" />
<ClInclude Include="..\include\vcpkg\base\cofffilereader.h" />
<ClInclude Include="..\include\vcpkg\base\cstringview.h" />
<ClInclude Include="..\include\vcpkg\base\downloads.h" />
<ClInclude Include="..\include\vcpkg\base\enums.h" />
<ClInclude Include="..\include\vcpkg\base\expected.h" />
<ClInclude Include="..\include\vcpkg\base\files.h" />
Expand Down Expand Up @@ -195,6 +196,7 @@
<ClCompile Include="..\src\vcpkg\base\checks.cpp" />
<ClCompile Include="..\src\vcpkg\base\chrono.cpp" />
<ClCompile Include="..\src\vcpkg\base\cofffilereader.cpp" />
<ClCompile Include="..\src\vcpkg\base\downloads.cpp" />
<ClCompile Include="..\src\vcpkg\base\enums.cpp" />
<ClCompile Include="..\src\vcpkg\base\files.cpp" />
<ClCompile Include="..\src\vcpkg\base\lineinfo.cpp" />
Expand Down
6 changes: 6 additions & 0 deletions toolsrc/vcpkglib/vcpkglib.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,9 @@
<ClCompile Include="..\src\vcpkg\commands.fetch.cpp">
<Filter>Source Files\vcpkg</Filter>
</ClCompile>
<ClCompile Include="..\src\vcpkg\base\downloads.cpp">
<Filter>Source Files\vcpkg\base</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\include\pch.h">
Expand Down Expand Up @@ -347,5 +350,8 @@
<ClInclude Include="..\include\vcpkg\base\stringliteral.h">
<Filter>Header Files\vcpkg\base</Filter>
</ClInclude>
<ClInclude Include="..\include\vcpkg\base\downloads.h">
<Filter>Header Files\vcpkg\base</Filter>
</ClInclude>
</ItemGroup>
</Project>

0 comments on commit 3e76baa

Please sign in to comment.