Skip to content

Commit

Permalink
Add a default user agent to REST source calls (microsoft#1839)
Browse files Browse the repository at this point in the history
Provides a default user agent header in the http helper class that the REST source code uses.  It can be overridden by providing the user agent header in the headers argument.

The default format is:
```
winget-cli WindowsPackageManager/1.2.3 DesktopAppInstaller/10.11.12
```
where `1.2.3` is the client version as would be output by `winget -v` (without the `v` at the beginning) and `10.11.12` is the package version of the MSIX.

Also updates the WinINET downloader to use the same user agent string, but it is not overridable.
  • Loading branch information
JohnMcPMS authored Jan 20, 2022
1 parent 9dcbf99 commit f33afcb
Show file tree
Hide file tree
Showing 9 changed files with 83 additions and 3 deletions.
1 change: 0 additions & 1 deletion src/AppInstallerCLI.sln
Original file line number Diff line number Diff line change
Expand Up @@ -650,7 +650,6 @@ Global
{1622DA16-914F-4F57-A259-D5169003CC8C}.Debug|ARM.ActiveCfg = Fuzzing|x64
{1622DA16-914F-4F57-A259-D5169003CC8C}.Debug|ARM64.ActiveCfg = Fuzzing|x64
{1622DA16-914F-4F57-A259-D5169003CC8C}.Debug|x64.ActiveCfg = Debug|x64
{1622DA16-914F-4F57-A259-D5169003CC8C}.Debug|x64.Build.0 = Debug|x64
{1622DA16-914F-4F57-A259-D5169003CC8C}.Debug|x86.ActiveCfg = Fuzzing|x64
{1622DA16-914F-4F57-A259-D5169003CC8C}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|x64
{1622DA16-914F-4F57-A259-D5169003CC8C}.Fuzzing|ARM.ActiveCfg = Fuzzing|x64
Expand Down
31 changes: 31 additions & 0 deletions src/AppInstallerCLITests/HttpClientHelper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,12 @@
#include "TestRestRequestHandler.h"
#include <AppInstallerErrors.h>
#include <Rest/Schema/HttpClientHelper.h>
#include <AppInstallerRuntime.h>
#include <AppInstallerStrings.h>

using namespace AppInstaller::Repository::Rest::Schema;
using namespace AppInstaller::Runtime;
using namespace AppInstaller::Utility;

TEST_CASE("ExtractJsonResponse_UnsupportedMimeType", "[RestSource][RestSearch]")
{
Expand All @@ -25,3 +29,30 @@ TEST_CASE("ValidateAndExtractResponse_NotFound", "[RestSource]")
HttpClientHelper helper{ GetTestRestRequestHandler(web::http::status_codes::NotFound) };
REQUIRE_THROWS_HR(helper.HandleGet(L"https://testUri"), APPINSTALLER_CLI_ERROR_RESTSOURCE_ENDPOINT_NOT_FOUND);
}

TEST_CASE("EnsureDefaultUserAgent", "[RestSource]")
{
HttpClientHelper helper{ GetTestRestRequestHandler([](const web::http::http_request& request)
{
auto itr = request.headers().find(web::http::header_names::user_agent);
if (itr != request.headers().end() &&
itr->second.find(ConvertToUTF16(GetClientVersion())) != utility::string_t::npos &&
itr->second.find(ConvertToUTF16(GetPackageVersion())) != utility::string_t::npos)
{
return web::http::status_codes::OK;
}
else
{
return web::http::status_codes::BadRequest;
}
}) };

SECTION("GET")
{
REQUIRE_NOTHROW(helper.HandleGet(L"https://testUri"));
}
SECTION("POST")
{
REQUIRE_NOTHROW(helper.HandlePost(L"https://testUri", {}));
}
}
15 changes: 15 additions & 0 deletions src/AppInstallerCLITests/TestRestRequestHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,18 @@ std::shared_ptr<TestRestRequestHandler> GetTestRestRequestHandler(
return pplx::task_from_result(response);
});
}

std::shared_ptr<TestRestRequestHandler> GetTestRestRequestHandler(
std::function<web::http::status_code(const web::http::http_request& request)> handler)
{
return std::make_shared<TestRestRequestHandler>([handler = std::move(handler)](web::http::http_request request) ->
pplx::task<web::http::http_response>
{
web::http::http_response response;
response.set_body(utf16string{});

response.headers().set_content_type(web::http::details::mime_types::application_json);
response.set_status_code(handler(request));
return pplx::task_from_result(response);
});
}
3 changes: 3 additions & 0 deletions src/AppInstallerCLITests/TestRestRequestHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,6 @@ class TestRestRequestHandler : public web::http::http_pipeline_stage

std::shared_ptr<TestRestRequestHandler> GetTestRestRequestHandler(
const web::http::status_code statusCode, const utility::string_t& sampleResponseString = {}, const utility::string_t& mimeType = web::http::details::mime_types::application_json);

std::shared_ptr<TestRestRequestHandler> GetTestRestRequestHandler(
std::function<web::http::status_code(const web::http::http_request& request)> handler);
2 changes: 1 addition & 1 deletion src/AppInstallerCommonCore/Downloader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ namespace AppInstaller::Utility
AICLI_LOG(Core, Info, << "WinINet downloading from url: " << url);

wil::unique_hinternet session(InternetOpenA(
"winget-cli",
Runtime::GetDefaultUserAgent().get().c_str(),
INTERNET_OPEN_TYPE_PRECONFIG,
NULL,
NULL,
Expand Down
3 changes: 3 additions & 0 deletions src/AppInstallerCommonCore/Public/AppInstallerRuntime.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,7 @@ namespace AppInstaller::Runtime

// Returns true if this is a release build; false if not.
inline constexpr bool IsReleaseBuild();

// Gets the default user agent string for the Windows Package Manager.
Utility::LocIndString GetDefaultUserAgent();
}
3 changes: 2 additions & 1 deletion src/AppInstallerCommonCore/Public/winget/LocIndependent.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ namespace AppInstaller::Utility

bool empty() const { return m_value.empty(); }

const std::string& get() const { return m_value; }
const std::string& get() const & { return m_value; }
std::string&& get() && { return std::move(m_value); }

operator const std::string& () const { return m_value; }
operator std::string_view() const { return m_value; }
Expand Down
12 changes: 12 additions & 0 deletions src/AppInstallerCommonCore/Runtime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,18 @@ namespace AppInstaller::Runtime
#endif
}

// Using "standard" user agent format
// Keeping `winget-cli` for historical reasons
Utility::LocIndString GetDefaultUserAgent()
{
std::ostringstream strstr;
strstr <<
"winget-cli"
" WindowsPackageManager/" << GetClientVersion() <<
" DesktopAppInstaller/" << GetPackageVersion();
return Utility::LocIndString{ strstr.str() };
}

#ifndef AICLI_DISABLE_TEST_HOOKS
void TestHook_SetPathOverride(PathName target, const std::filesystem::path& path)
{
Expand Down
16 changes: 16 additions & 0 deletions src/AppInstallerRepositoryCore/Rest/Schema/HttpClientHelper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,20 @@

namespace AppInstaller::Repository::Rest::Schema
{
namespace
{
// If the caller does not pass in a user agent header, put the default one on the request.
void EnsureDefaultUserAgent(web::http::http_request& request)
{
static utility::string_t c_defaultUserAgent = Utility::ConvertToUTF16(Runtime::GetDefaultUserAgent());

if (!request.headers().has(web::http::header_names::user_agent))
{
request.headers().add(web::http::header_names::user_agent, c_defaultUserAgent);
}
}
}

HttpClientHelper::HttpClientHelper(std::optional<std::shared_ptr<web::http::http_pipeline_stage>> stage) : m_defaultRequestHandlerStage(stage) {}

pplx::task<web::http::http_response> HttpClientHelper::Post(
Expand All @@ -21,6 +35,7 @@ namespace AppInstaller::Repository::Rest::Schema
{
request.headers().add(pair.first, pair.second);
}
EnsureDefaultUserAgent(request);

AICLI_LOG(Repo, Verbose, << "Http POST request details:\n" << utility::conversions::to_utf8string(request.to_string()));

Expand Down Expand Up @@ -52,6 +67,7 @@ namespace AppInstaller::Repository::Rest::Schema
{
request.headers().add(pair.first, pair.second);
}
EnsureDefaultUserAgent(request);

AICLI_LOG(Repo, Verbose, << "Http GET request details:\n" << utility::conversions::to_utf8string(request.to_string()));

Expand Down

0 comments on commit f33afcb

Please sign in to comment.