From c4ba815534424c3e28709b7417c724a5e928117e Mon Sep 17 00:00:00 2001 From: Billy Robert O'Neal III Date: Thu, 12 Oct 2023 19:18:44 -0700 Subject: [PATCH] urlencode spaces before calling curl Alternate resolution of https://github.com/microsoft/vcpkg/pull/34441 The URL is supposed to contain query parameters and similar, so we can't go all the way to full urlencode(). But spaces should do the same thing with curl as they do for WinHTTP and get encoded correctly. --- include/vcpkg/base/downloads.h | 8 ++++++++ src/vcpkg-test/downloads.cpp | 14 ++++++++++++++ src/vcpkg/base/downloads.cpp | 16 +++++++++------- 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/include/vcpkg/base/downloads.h b/include/vcpkg/base/downloads.h index c2adcd7176..09b43d4761 100644 --- a/include/vcpkg/base/downloads.h +++ b/include/vcpkg/base/downloads.h @@ -115,4 +115,12 @@ namespace vcpkg }; Optional try_parse_curl_progress_data(StringView curl_progress_line); + + // Replaces spaces with %20 for purposes of including in a URL. + // This is typically used to filter a command line passed to `x-download` or similar which + // might contain spaces that we, in turn, pass to curl. + // + // Notably, callers of this function can't use Strings::percent_encode because the URL + // is likely to contain query parameters or similar. + std::string url_encode_spaces(StringView url); } diff --git a/src/vcpkg-test/downloads.cpp b/src/vcpkg-test/downloads.cpp index 02c9be3f69..f510aa4e7a 100644 --- a/src/vcpkg-test/downloads.cpp +++ b/src/vcpkg-test/downloads.cpp @@ -164,3 +164,17 @@ TEST_CASE ("try_parse_curl_progress_data", "[downloads]") REQUIRE(out.current_speed == 683 * 1024); } } + +TEST_CASE ("url_encode_spaces", "[downloads]") +{ + REQUIRE(url_encode_spaces("https://example.com?query=value&query2=value2") == + "https://example.com?query=value&query2=value2"); + REQUIRE(url_encode_spaces("https://example.com/a/b?query=value&query2=value2") == + "https://example.com/a/b?query=value&query2=value2"); + REQUIRE(url_encode_spaces("https://example.com/a%20space/b?query=value&query2=value2") == + "https://example.com/a%20space/b?query=value&query2=value2"); + REQUIRE(url_encode_spaces("https://example.com/a space/b?query=value&query2=value2") == + "https://example.com/a%20space/b?query=value&query2=value2"); + REQUIRE(url_encode_spaces("https://example.com/a space/b?query=value&query2=value2") == + "https://example.com/a%20%20space/b?query=value&query2=value2"); +} diff --git a/src/vcpkg/base/downloads.cpp b/src/vcpkg/base/downloads.cpp index 32be45258c..3baa677c76 100644 --- a/src/vcpkg/base/downloads.cpp +++ b/src/vcpkg/base/downloads.cpp @@ -394,7 +394,7 @@ namespace vcpkg } for (auto&& url : urls) { - cmd.string_arg(url); + cmd.string_arg(url_encode_spaces(url)); } std::vector lines; @@ -465,7 +465,7 @@ namespace vcpkg } for (auto&& url : url_pairs) { - cmd.string_arg(url.first).string_arg("-o").string_arg(url.second); + cmd.string_arg(url_encode_spaces(url.first)).string_arg("-o").string_arg(url.second); } auto res = cmd_execute_and_stream_lines(cmd, [out](StringView line) { @@ -536,8 +536,8 @@ namespace vcpkg std::string res = "Authorization: Bearer " + github_token; cmd.string_arg("-H").string_arg(res); cmd.string_arg("-H").string_arg("X-GitHub-Api-Version: 2022-11-28"); - cmd.string_arg( - Strings::concat("https://api.github.com/repos/", github_repository, "/dependency-graph/snapshots")); + cmd.string_arg(Strings::concat( + "https://api.github.com/repos/", url_encode_spaces(github_repository), "/dependency-graph/snapshots")); cmd.string_arg("-d").string_arg("@-"); int code = 0; auto result = cmd_execute_and_stream_lines( @@ -579,7 +579,7 @@ namespace vcpkg // HTTP headers are ignored for FTP clients Command cmd; cmd.string_arg("curl"); - cmd.string_arg(url); + cmd.string_arg(url_encode_spaces(url)); cmd.string_arg("-T").string_arg(file); auto maybe_res = cmd_execute_and_capture_output(cmd); if (auto res = maybe_res.get()) @@ -664,7 +664,7 @@ namespace vcpkg cmd.string_arg("--data-raw").string_arg(data); } - cmd.string_arg(url); + cmd.string_arg(url_encode_spaces(url)); return flatten_out(cmd_execute_and_capture_output(cmd), "curl"); } @@ -838,7 +838,7 @@ namespace vcpkg cmd.string_arg("curl") .string_arg("--fail") .string_arg("-L") - .string_arg(url) + .string_arg(url_encode_spaces(url)) .string_arg("--create-dirs") .string_arg("--output") .string_arg(download_path_part_path); @@ -1192,4 +1192,6 @@ namespace vcpkg return result; } + + std::string url_encode_spaces(StringView url) { return Strings::replace_all(url, StringLiteral{" "}, "%20"); } }