winget-cli

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

commit 7209d71ca1198656056346c5f12d5a16ef8a6d10
parent 91ee267ccd1bb6d9f976153e195572a5ee9746d8
Author: Ryan <69221034+ryfu-msft@users.noreply.github.com>
Date:   Fri, 12 Apr 2024 15:07:44 -0700

Refactor HttpClientHelper to CommonCore (#4371)


Diffstat:
M.github/actions/spelling/patterns.txt | 2+-
Msrc/AppInstallerCLITests/AppInstallerCLITests.vcxproj | 2+-
Msrc/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters | 4++--
Msrc/AppInstallerCLITests/CustomHeader.cpp | 4++--
Msrc/AppInstallerCLITests/HttpClientHelper.cpp | 4++--
Asrc/AppInstallerCLITests/Rest.cpp | 56++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/AppInstallerCLITests/RestClient.cpp | 1+
Dsrc/AppInstallerCLITests/RestHelper.cpp | 56--------------------------------------------------------
Msrc/AppInstallerCLITests/RestInterface_1_0.cpp | 4++--
Msrc/AppInstallerCLITests/RestInterface_1_1.cpp | 4++--
Msrc/AppInstallerCLITests/RestInterface_1_4.cpp | 4++--
Msrc/AppInstallerCLITests/RestInterface_1_5.cpp | 1+
Msrc/AppInstallerCLITests/RestInterface_1_6.cpp | 1+
Msrc/AppInstallerCLITests/RestInterface_1_7.cpp | 4++--
Msrc/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj | 21++++++++++++++-------
Msrc/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj.filters | 12++++++++++++
Msrc/AppInstallerCommonCore/Downloader.cpp | 1+
Asrc/AppInstallerCommonCore/HttpClientHelper.cpp | 216+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/AppInstallerCommonCore/Public/winget/HttpClientHelper.h | 39+++++++++++++++++++++++++++++++++++++++
Asrc/AppInstallerCommonCore/Public/winget/Rest.h | 20++++++++++++++++++++
Asrc/AppInstallerCommonCore/Rest.cpp | 72++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/AppInstallerCommonCore/pch.h | 22+++++++++++++---------
Msrc/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj | 7+------
Msrc/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj.filters | 12------------
Msrc/AppInstallerRepositoryCore/Rest/RestClient.cpp | 19++++++++++---------
Msrc/AppInstallerRepositoryCore/Rest/RestClient.h | 12+++++-------
Msrc/AppInstallerRepositoryCore/Rest/RestSourceFactory.cpp | 2+-
Msrc/AppInstallerRepositoryCore/Rest/Schema/1_0/Interface.h | 11+++++------
Msrc/AppInstallerRepositoryCore/Rest/Schema/1_0/Json/ManifestDeserializer_1_0.cpp | 4++--
Msrc/AppInstallerRepositoryCore/Rest/Schema/1_0/Json/SearchResponseDeserializer_1_0.cpp | 8++++----
Msrc/AppInstallerRepositoryCore/Rest/Schema/1_0/RestInterface_1_0.cpp | 38+++++++++++++++++++++++++-------------
Msrc/AppInstallerRepositoryCore/Rest/Schema/1_1/Interface.h | 2+-
Msrc/AppInstallerRepositoryCore/Rest/Schema/1_1/RestInterface_1_1.cpp | 9++++-----
Msrc/AppInstallerRepositoryCore/Rest/Schema/1_4/Interface.h | 2+-
Msrc/AppInstallerRepositoryCore/Rest/Schema/1_4/Json/SearchResponseDeserializer_1_4.cpp | 6+++---
Msrc/AppInstallerRepositoryCore/Rest/Schema/1_4/RestInterface_1_4.cpp | 8++++----
Msrc/AppInstallerRepositoryCore/Rest/Schema/1_5/Interface.h | 2+-
Msrc/AppInstallerRepositoryCore/Rest/Schema/1_5/RestInterface_1_5.cpp | 8++++----
Msrc/AppInstallerRepositoryCore/Rest/Schema/1_6/Interface.h | 2+-
Msrc/AppInstallerRepositoryCore/Rest/Schema/1_6/RestInterface_1_6.cpp | 8++++----
Msrc/AppInstallerRepositoryCore/Rest/Schema/1_7/Interface.h | 4++--
Msrc/AppInstallerRepositoryCore/Rest/Schema/1_7/RestInterface_1_7.cpp | 12++++++------
Dsrc/AppInstallerRepositoryCore/Rest/Schema/HttpClientHelper.cpp | 213-------------------------------------------------------------------------------
Dsrc/AppInstallerRepositoryCore/Rest/Schema/HttpClientHelper.h | 41-----------------------------------------
Msrc/AppInstallerRepositoryCore/Rest/Schema/InformationResponseDeserializer.h | 1-
Dsrc/AppInstallerRepositoryCore/Rest/Schema/RestHelper.cpp | 86-------------------------------------------------------------------------------
Dsrc/AppInstallerRepositoryCore/Rest/Schema/RestHelper.h | 25-------------------------
Msrc/AppInstallerRepositoryCore/pch.h | 16+++++++---------
48 files changed, 553 insertions(+), 555 deletions(-)

diff --git a/.github/actions/spelling/patterns.txt b/.github/actions/spelling/patterns.txt @@ -46,7 +46,7 @@ http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer ^ShortDescription: Le nouveau.*$ # Ignore test patterns -REQUIRE\(RestHelper::GetRestAPIBaseUri\(".*"\) == L".*" +REQUIRE\(Rest::GetRestAPIBaseUri\(".*"\) == L".*" # fabricbot.json "(?:id|user)": "[-A-Za-z0-9_]*" diff --git a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj @@ -243,8 +243,8 @@ <ClCompile Include="Regex.cpp" /> <ClCompile Include="Registry.cpp" /> <ClCompile Include="Resources.cpp" /> + <ClCompile Include="Rest.cpp" /> <ClCompile Include="RestClient.cpp" /> - <ClCompile Include="RestHelper.cpp" /> <ClCompile Include="RestInterface_1_0.cpp" /> <ClCompile Include="RestInterface_1_1.cpp" /> <ClCompile Include="RestInterface_1_4.cpp" /> diff --git a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters @@ -179,10 +179,10 @@ <ClCompile Include="Registry.cpp"> <Filter>Source Files\Common</Filter> </ClCompile> - <ClCompile Include="RestClient.cpp"> + <ClCompile Include="Rest.cpp"> <Filter>Source Files\Repository</Filter> </ClCompile> - <ClCompile Include="RestHelper.cpp"> + <ClCompile Include="RestClient.cpp"> <Filter>Source Files\Repository</Filter> </ClCompile> <ClCompile Include="RestInterface_1_0.cpp"> diff --git a/src/AppInstallerCLITests/CustomHeader.cpp b/src/AppInstallerCLITests/CustomHeader.cpp @@ -13,6 +13,7 @@ using namespace TestCommon; using namespace AppInstaller; +using namespace AppInstaller::Http; using namespace AppInstaller::Settings; using namespace AppInstaller::Repository; using namespace AppInstaller::Repository::Rest; @@ -140,4 +141,4 @@ TEST_CASE("RestClient_DefaultUserAgentHeader", "[RestSource][CustomHeader]") HttpClientHelper helper{ GetHeaderVerificationHandler(web::http::status_codes::OK, sample, header) }; RestClient client = RestClient::Create(utility::conversions::to_utf8string("https://restsource.com/api"), {}, {}, std::move(helper), {}); REQUIRE(client.GetSourceIdentifier() == "Source123"); -}- \ No newline at end of file +} diff --git a/src/AppInstallerCLITests/HttpClientHelper.cpp b/src/AppInstallerCLITests/HttpClientHelper.cpp @@ -4,13 +4,13 @@ #include "TestCommon.h" #include "TestRestRequestHandler.h" #include <AppInstallerErrors.h> -#include <Rest/Schema/HttpClientHelper.h> #include <AppInstallerRuntime.h> #include <AppInstallerStrings.h> #include <winget/Certificates.h> +#include <winget/HttpClientHelper.h> #include <CertificateResources.h> -using namespace AppInstaller::Repository::Rest::Schema; +using namespace AppInstaller::Http; using namespace AppInstaller::Runtime; using namespace AppInstaller::Utility; using namespace AppInstaller::Certificates; diff --git a/src/AppInstallerCLITests/Rest.cpp b/src/AppInstallerCLITests/Rest.cpp @@ -0,0 +1,56 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "TestCommon.h" +#include "cpprest/json.h" +#include <winget/Rest.h> + +using namespace AppInstaller::Rest; + +TEST_CASE("ValidateAndGetRestAPIBaseUri", "[RestSource]") +{ + REQUIRE(GetRestAPIBaseUri("https://restsource.azurewebsites.net/api/ ") == L"https://restsource.azurewebsites.net/api"); + REQUIRE(GetRestAPIBaseUri("http://rest_sourc e.azurewebsites.net/api") == L"http://rest_sourc%20e.azurewebsites.net/api"); + REQUIRE(GetRestAPIBaseUri("http://restsource.azurewebsites.net/v1.0/%v1") == L"http://restsource.azurewebsites.net/v1.0/%25v1"); +} + +TEST_CASE("IsValidUri", "[RestSource]") +{ + REQUIRE(IsValidUri(L"http://rest%20source.azurewebsites.net/api")); + REQUIRE(!IsValidUri(L"http://rest source.azurewebsites.net/api")); +} + +TEST_CASE("AppendPathToUri", "[RestSource]") +{ + REQUIRE(AppendPathToUri(L"http://restsource.azurewebsites.net/api/", L"/path") == L"http://restsource.azurewebsites.net/api/path"); + REQUIRE(AppendPathToUri(L"http://restsource.azurewebsites.net/api/", L"/pat h") == L"http://restsource.azurewebsites.net/api/pat%20%20h"); + REQUIRE(AppendPathToUri(L"http://restsource.azurewebsites.net/api/", L"/path+version") == L"http://restsource.azurewebsites.net/api/path%2Bversion"); +} + +TEST_CASE("AppendQueryParamsToUri", "[RestSource]") +{ + utility::string_t url = L"http://restsource.azurewebsites.net/api"; + std::map<std::string_view, std::string> queryParams; + queryParams.emplace("Version", "1.0 .0"); + queryParams.emplace("Channel", "beta+"); + + REQUIRE(AppendQueryParamsToUri(url, queryParams) == L"http://restsource.azurewebsites.net/api?Channel=beta%2B&Version=1.0%20.0"); +} + +TEST_CASE("GetUniqueItems", "[RestSource]") +{ + std::vector<std::string> list; + REQUIRE(GetUniqueItems(list).size() == 0); + + std::vector<std::string> listWithDuplicates; + listWithDuplicates.emplace_back("object1"); + listWithDuplicates.emplace_back("object1"); + listWithDuplicates.emplace_back("object2"); + listWithDuplicates.emplace_back("object2"); + listWithDuplicates.emplace_back("object3"); + std::vector<std::string> result = GetUniqueItems(listWithDuplicates); + REQUIRE(result.size() == 3); + REQUIRE(result.at(0) == "object1"); + REQUIRE(result.at(1) == "object2"); + REQUIRE(result.at(2) == "object3"); +} diff --git a/src/AppInstallerCLITests/RestClient.cpp b/src/AppInstallerCLITests/RestClient.cpp @@ -10,6 +10,7 @@ #include <AppInstallerRuntime.h> using namespace AppInstaller; +using namespace AppInstaller::Http; using namespace AppInstaller::Utility; using namespace AppInstaller::Repository::Rest; using namespace AppInstaller::Repository::Rest::Schema; diff --git a/src/AppInstallerCLITests/RestHelper.cpp b/src/AppInstallerCLITests/RestHelper.cpp @@ -1,56 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -#include "pch.h" -#include "TestCommon.h" -#include "Rest/Schema/RestHelper.h" -#include "cpprest/json.h" - -using namespace AppInstaller::Repository::Rest::Schema; - -TEST_CASE("ValidateAndGetRestAPIBaseUri", "[RestSource]") -{ - REQUIRE(RestHelper::GetRestAPIBaseUri("https://restsource.azurewebsites.net/api/ ") == L"https://restsource.azurewebsites.net/api"); - REQUIRE(RestHelper::GetRestAPIBaseUri("http://rest_sourc e.azurewebsites.net/api") == L"http://rest_sourc%20e.azurewebsites.net/api"); - REQUIRE(RestHelper::GetRestAPIBaseUri("http://restsource.azurewebsites.net/v1.0/%v1") == L"http://restsource.azurewebsites.net/v1.0/%25v1"); -} - -TEST_CASE("IsValidUri", "[RestSource]") -{ - REQUIRE(RestHelper::IsValidUri(L"http://rest%20source.azurewebsites.net/api")); - REQUIRE(!RestHelper::IsValidUri(L"http://rest source.azurewebsites.net/api")); -} - -TEST_CASE("AppendPathToUri", "[RestSource]") -{ - REQUIRE(RestHelper::AppendPathToUri(L"http://restsource.azurewebsites.net/api/", L"/path") == L"http://restsource.azurewebsites.net/api/path"); - REQUIRE(RestHelper::AppendPathToUri(L"http://restsource.azurewebsites.net/api/", L"/pat h") == L"http://restsource.azurewebsites.net/api/pat%20%20h"); - REQUIRE(RestHelper::AppendPathToUri(L"http://restsource.azurewebsites.net/api/", L"/path+version") == L"http://restsource.azurewebsites.net/api/path%2Bversion"); -} - -TEST_CASE("AppendQueryParamsToUri", "[RestSource]") -{ - utility::string_t url = L"http://restsource.azurewebsites.net/api"; - std::map<std::string_view, std::string> queryParams; - queryParams.emplace("Version", "1.0 .0"); - queryParams.emplace("Channel", "beta+"); - - REQUIRE(RestHelper::AppendQueryParamsToUri(url, queryParams) == L"http://restsource.azurewebsites.net/api?Channel=beta%2B&Version=1.0%20.0"); -} - -TEST_CASE("GetUniqueItems", "[RestSource]") -{ - std::vector<std::string> list; - REQUIRE(RestHelper::GetUniqueItems(list).size() == 0); - - std::vector<std::string> listWithDuplicates; - listWithDuplicates.emplace_back("object1"); - listWithDuplicates.emplace_back("object1"); - listWithDuplicates.emplace_back("object2"); - listWithDuplicates.emplace_back("object2"); - listWithDuplicates.emplace_back("object3"); - std::vector<std::string> result = RestHelper::GetUniqueItems(listWithDuplicates); - REQUIRE(result.size() == 3); - REQUIRE(result.at(0) == "object1"); - REQUIRE(result.at(1) == "object2"); - REQUIRE(result.at(2) == "object3"); -} diff --git a/src/AppInstallerCLITests/RestInterface_1_0.cpp b/src/AppInstallerCLITests/RestInterface_1_0.cpp @@ -11,6 +11,7 @@ #include <AppInstallerSHA256.h> using namespace TestCommon; +using namespace AppInstaller::Http; using namespace AppInstaller::Utility; using namespace AppInstaller::Manifest; using namespace AppInstaller::Repository; @@ -522,4 +523,4 @@ TEST_CASE("GetManifests_GoodResponse_UnknownInstaller", "[RestSource][Interface_ REQUIRE(manifest.Installers.size() == 1); REQUIRE(manifest.Installers.at(0).BaseInstallerType == InstallerTypeEnum::Unknown); REQUIRE(manifest.Installers.at(0).ProductId.empty()); -}- \ No newline at end of file +} diff --git a/src/AppInstallerCLITests/RestInterface_1_1.cpp b/src/AppInstallerCLITests/RestInterface_1_1.cpp @@ -9,6 +9,7 @@ #include <AppInstallerErrors.h> using namespace TestCommon; +using namespace AppInstaller::Http; using namespace AppInstaller::Utility; using namespace AppInstaller::Manifest; using namespace AppInstaller::Repository; @@ -555,4 +556,4 @@ TEST_CASE("GetManifests_GoodResponse_V1_1", "[RestSource][Interface_1_1]") REQUIRE(manifest.ManifestVersion == AppInstaller::Manifest::ManifestVer{ "1.1.0" }); sampleManifest.VerifyLocalizations_AllFields(manifest); sampleManifest.VerifyInstallers_AllFields(manifest); -}- \ No newline at end of file +} diff --git a/src/AppInstallerCLITests/RestInterface_1_4.cpp b/src/AppInstallerCLITests/RestInterface_1_4.cpp @@ -9,6 +9,7 @@ #include <AppInstallerErrors.h> using namespace TestCommon; +using namespace AppInstaller::Http; using namespace AppInstaller::Utility; using namespace AppInstaller::Manifest; using namespace AppInstaller::Repository; @@ -422,4 +423,4 @@ TEST_CASE("Search_GoodResponse_V1_4", "[RestSource][Interface_1_4]") REQUIRE(package.Versions.at(0).ArpVersions.size() == 2); REQUIRE(package.Versions.at(0).ArpVersions.at(0).ToString() == "1.0"); REQUIRE(package.Versions.at(0).ArpVersions.at(1).ToString() == "2.0"); -}- \ No newline at end of file +} diff --git a/src/AppInstallerCLITests/RestInterface_1_5.cpp b/src/AppInstallerCLITests/RestInterface_1_5.cpp @@ -9,6 +9,7 @@ #include <AppInstallerErrors.h> using namespace TestCommon; +using namespace AppInstaller::Http; using namespace AppInstaller::Utility; using namespace AppInstaller::Manifest; using namespace AppInstaller::Repository; diff --git a/src/AppInstallerCLITests/RestInterface_1_6.cpp b/src/AppInstallerCLITests/RestInterface_1_6.cpp @@ -9,6 +9,7 @@ #include <AppInstallerErrors.h> using namespace TestCommon; +using namespace AppInstaller::Http; using namespace AppInstaller::Utility; using namespace AppInstaller::Manifest; using namespace AppInstaller::Repository; diff --git a/src/AppInstallerCLITests/RestInterface_1_7.cpp b/src/AppInstallerCLITests/RestInterface_1_7.cpp @@ -14,6 +14,7 @@ using namespace TestCommon; using namespace AppInstaller; using namespace AppInstaller::Authentication; +using namespace AppInstaller::Http; using namespace AppInstaller::Utility; using namespace AppInstaller::Manifest; using namespace AppInstaller::Repository; @@ -603,4 +604,4 @@ TEST_CASE("GetManifests_GoodResponse_V1_7", "[RestSource][Interface_1_7]") REQUIRE(manifest.ManifestVersion == AppInstaller::Manifest::ManifestVer{ "1.7.0" }); sampleManifest.VerifyLocalizations_AllFields(manifest); sampleManifest.VerifyInstallers_AllFields(manifest); -}- \ No newline at end of file +} diff --git a/src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj b/src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="utf-8"?> +<?xml version="1.0" encoding="utf-8"?> <Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Import Project="$(SolutionDir)\packages\Microsoft.Windows.CppWinRT.2.0.230706.1\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('$(SolutionDir)\packages\Microsoft.Windows.CppWinRT.2.0.230706.1\build\native\Microsoft.Windows.CppWinRT.props')" /> <PropertyGroup Label="Globals"> @@ -246,7 +246,7 @@ <ItemDefinitionGroup Condition="'$(Configuration)'=='Debug'"> <ClCompile> <Optimization>Disabled</Optimization> - <PreprocessorDefinitions>_NO_ASYNCRTIMP;_DEBUG;%(PreprocessorDefinitions);CLICOREDLLBUILD</PreprocessorDefinitions> + <PreprocessorDefinitions>_NO_ASYNCRTIMP;_SILENCE_STDEXT_ARR_ITERS_DEPRECATION_WARNING;_DEBUG;%(PreprocessorDefinitions);CLICOREDLLBUILD</PreprocessorDefinitions> <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">$(ProjectDir);$(ProjectDir)Public;$(ProjectDir)Telemetry;$(ProjectDir)..\AppInstallerSharedLib;$(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\binver;$(ProjectDir)..\YamlCppLib\libyaml\include;$(ProjectDir)..\cpprestsdk\cpprestsdk\Release\include;$(ProjectDir)..\JsonCppLib;$(ProjectDir)..\PureLib\pure;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">$(ProjectDir);$(ProjectDir)Public;$(ProjectDir)Telemetry;$(ProjectDir)..\AppInstallerSharedLib;$(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\binver;$(ProjectDir)..\YamlCppLib\libyaml\include;$(ProjectDir)..\cpprestsdk\cpprestsdk\Release\include;$(ProjectDir)..\JsonCppLib;$(ProjectDir)..\PureLib\pure;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(ProjectDir);$(ProjectDir)Public;$(ProjectDir)Telemetry;$(ProjectDir)..\AppInstallerSharedLib;$(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\binver;$(ProjectDir)..\YamlCppLib\libyaml\include;$(ProjectDir)..\cpprestsdk\cpprestsdk\Release\include;$(ProjectDir)..\JsonCppLib;$(ProjectDir)..\PureLib\pure;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> @@ -272,7 +272,7 @@ </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Platform)'=='Win32'"> <ClCompile> - <PreprocessorDefinitions>_NO_ASYNCRTIMP;WIN32;%(PreprocessorDefinitions);CLICOREDLLBUILD</PreprocessorDefinitions> + <PreprocessorDefinitions>_NO_ASYNCRTIMP;_SILENCE_STDEXT_ARR_ITERS_DEPRECATION_WARNING;WIN32;%(PreprocessorDefinitions);CLICOREDLLBUILD</PreprocessorDefinitions> <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(ProjectDir);$(ProjectDir)Public;$(ProjectDir)Telemetry;$(ProjectDir)..\AppInstallerSharedLib;$(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\binver;$(ProjectDir)..\YamlCppLib\libyaml\include;$(ProjectDir)..\cpprestsdk\cpprestsdk\Release\include;$(ProjectDir)..\JsonCppLib;$(ProjectDir)..\PureLib\pure;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <TreatWarningAsError Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</TreatWarningAsError> <SDLCheck Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</SDLCheck> @@ -288,7 +288,7 @@ <Optimization>MaxSpeed</Optimization> <FunctionLevelLinking>true</FunctionLevelLinking> <IntrinsicFunctions>true</IntrinsicFunctions> - <PreprocessorDefinitions>_NO_ASYNCRTIMP;NDEBUG;%(PreprocessorDefinitions);CLICOREDLLBUILD</PreprocessorDefinitions> + <PreprocessorDefinitions>_NO_ASYNCRTIMP;_SILENCE_STDEXT_ARR_ITERS_DEPRECATION_WARNING;NDEBUG;%(PreprocessorDefinitions);CLICOREDLLBUILD</PreprocessorDefinitions> <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">$(ProjectDir);$(ProjectDir)Public;$(ProjectDir)Telemetry;$(ProjectDir)..\AppInstallerSharedLib;$(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\binver;$(ProjectDir)..\YamlCppLib\libyaml\include;$(ProjectDir)..\cpprestsdk\cpprestsdk\Release\include;$(ProjectDir)..\PureLib\pure;$(ProjectDir)..\JsonCppLib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">$(ProjectDir);$(ProjectDir)Public;$(ProjectDir)Telemetry;$(ProjectDir)..\AppInstallerSharedLib;$(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\binver;$(ProjectDir)..\YamlCppLib\libyaml\include;$(ProjectDir)..\cpprestsdk\cpprestsdk\Release\include;$(ProjectDir)..\PureLib\pure;$(ProjectDir)..\JsonCppLib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(ProjectDir);$(ProjectDir)Public;$(ProjectDir)Telemetry;$(ProjectDir)..\AppInstallerSharedLib;$(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\binver;$(ProjectDir)..\YamlCppLib\libyaml\include;$(ProjectDir)..\cpprestsdk\cpprestsdk\Release\include;$(ProjectDir)..\PureLib\pure;$(ProjectDir)..\JsonCppLib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> @@ -325,7 +325,7 @@ <Optimization>MaxSpeed</Optimization> <FunctionLevelLinking>true</FunctionLevelLinking> <IntrinsicFunctions>true</IntrinsicFunctions> - <PreprocessorDefinitions>_NO_ASYNCRTIMP;NDEBUG;%(PreprocessorDefinitions);CLICOREDLLBUILD</PreprocessorDefinitions> + <PreprocessorDefinitions>_NO_ASYNCRTIMP;_SILENCE_STDEXT_ARR_ITERS_DEPRECATION_WARNING;NDEBUG;%(PreprocessorDefinitions);CLICOREDLLBUILD</PreprocessorDefinitions> <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='ReleaseStatic|ARM'">$(ProjectDir);$(ProjectDir)Public;$(ProjectDir)Telemetry;$(ProjectDir)..\AppInstallerSharedLib;$(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\binver;$(ProjectDir)..\YamlCppLib\libyaml\include;$(ProjectDir)..\cpprestsdk\cpprestsdk\Release\include;$(ProjectDir)..\PureLib\pure;$(ProjectDir)..\JsonCppLib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='ReleaseStatic|ARM64'">$(ProjectDir);$(ProjectDir)Public;$(ProjectDir)Telemetry;$(ProjectDir)..\AppInstallerSharedLib;$(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\binver;$(ProjectDir)..\YamlCppLib\libyaml\include;$(ProjectDir)..\cpprestsdk\cpprestsdk\Release\include;$(ProjectDir)..\PureLib\pure;$(ProjectDir)..\JsonCppLib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='ReleaseStatic|Win32'">$(ProjectDir);$(ProjectDir)Public;$(ProjectDir)Telemetry;$(ProjectDir)..\AppInstallerSharedLib;$(ProjectDir)..\AppInstallerSharedLib\Public;$(ProjectDir)..\binver;$(ProjectDir)..\YamlCppLib\libyaml\include;$(ProjectDir)..\cpprestsdk\cpprestsdk\Release\include;$(ProjectDir)..\PureLib\pure;$(ProjectDir)..\JsonCppLib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> @@ -421,6 +421,9 @@ <ClInclude Include="Public\winget\Archive.h" /> <ClInclude Include="Public\winget\ExperimentalFeature.h" /> <ClInclude Include="Public\winget\ExtensionCatalog.h" /> + <ClInclude Include="Public\winget\HttpClientHelper.h"> + <ExcludedFromBuild Condition="'$(Configuration)'=='Fuzzing'">true</ExcludedFromBuild> + </ClInclude> <ClInclude Include="Public\winget\Locale.h" /> <ClInclude Include="Public\winget\Manifest.h" /> <ClInclude Include="Public\winget\ManifestInstaller.h" /> @@ -440,6 +443,7 @@ <ClInclude Include="Public\winget\Pin.h" /> <ClInclude Include="Public\winget\Reboot.h" /> <ClInclude Include="Public\winget\Regex.h" /> + <ClInclude Include="Public\winget\Rest.h" /> <ClInclude Include="Public\winget\PathVariable.h" /> <ClInclude Include="Public\winget\PortableARPEntry.h" /> <ClInclude Include="Public\winget\PortableFileEntry.h" /> @@ -465,6 +469,9 @@ <ClCompile Include="ExperimentalFeature.cpp" /> <ClCompile Include="ExtensionCatalog.cpp" /> <ClCompile Include="FileLogger.cpp" /> + <ClCompile Include="HttpClientHelper.cpp"> + <ExcludedFromBuild Condition="'$(Configuration)'=='Fuzzing'">true</ExcludedFromBuild> + </ClCompile> <ClCompile Include="HttpStream\HttpClientWrapper.cpp" /> <ClCompile Include="HttpStream\HttpLocalCache.cpp" /> <ClCompile Include="HttpStream\HttpRandomAccessStream.cpp" /> @@ -488,6 +495,7 @@ <ClCompile Include="Progress.cpp" /> <ClCompile Include="Reboot.cpp" /> <ClCompile Include="Regex.cpp" /> + <ClCompile Include="Rest.cpp" /> <ClCompile Include="Runtime.cpp" /> <ClCompile Include="pch.cpp"> <PrecompiledHeader>Create</PrecompiledHeader> @@ -527,4 +535,4 @@ <Error Condition="!Exists('$(SolutionDir)\packages\Microsoft.Windows.CppWinRT.2.0.230706.1\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\packages\Microsoft.Windows.CppWinRT.2.0.230706.1\build\native\Microsoft.Windows.CppWinRT.props'))" /> <Error Condition="!Exists('$(SolutionDir)\packages\Microsoft.Windows.CppWinRT.2.0.230706.1\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\packages\Microsoft.Windows.CppWinRT.2.0.230706.1\build\native\Microsoft.Windows.CppWinRT.targets'))" /> </Target> -</Project>- \ No newline at end of file +</Project> diff --git a/src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj.filters b/src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj.filters @@ -192,6 +192,12 @@ <ClInclude Include="Public\winget\NetworkSettings.h"> <Filter>Public\winget</Filter> </ClInclude> + <ClInclude Include="Public\winget\HttpClientHelper.h"> + <Filter>Public\winget</Filter> + </ClInclude> + <ClInclude Include="Public\winget\Rest.h"> + <Filter>Public\winget</Filter> + </ClInclude> </ItemGroup> <ItemGroup> <ClCompile Include="pch.cpp"> @@ -344,6 +350,12 @@ <ClCompile Include="NetworkSettings.cpp"> <Filter>Source Files</Filter> </ClCompile> + <ClCompile Include="HttpClientHelper.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Rest.cpp"> + <Filter>Source Files</Filter> + </ClCompile> </ItemGroup> <ItemGroup> <None Include="PropertySheet.props" /> diff --git a/src/AppInstallerCommonCore/Downloader.cpp b/src/AppInstallerCommonCore/Downloader.cpp @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" +#include <wininet.h> #include "Public/AppInstallerErrors.h" #include "Public/AppInstallerRuntime.h" #include "Public/AppInstallerDownloader.h" diff --git a/src/AppInstallerCommonCore/HttpClientHelper.cpp b/src/AppInstallerCommonCore/HttpClientHelper.cpp @@ -0,0 +1,216 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include <AppInstallerDownloader.h> +#include <AppInstallerRuntime.h> +#include <winget/HttpClientHelper.h> +#include <winget/NetworkSettings.h> +#include <winhttp.h> + +namespace AppInstaller::Http +{ + 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(AppInstaller::Runtime::GetDefaultUserAgent()); + + if (!request.headers().has(web::http::header_names::user_agent)) + { + request.headers().add(web::http::header_names::user_agent, c_defaultUserAgent); + } + } + + void NativeHandleServerCertificateValidation(web::http::client::native_handle handle, const Certificates::PinningConfiguration& pinningConfiguration) + { + HINTERNET requestHandle = reinterpret_cast<HINTERNET>(handle); + + // Get certificate and pass along to pinning config + wil::unique_cert_context certContext; + DWORD bufferSize = sizeof(&certContext); + THROW_IF_WIN32_BOOL_FALSE(WinHttpQueryOption(requestHandle, WINHTTP_OPTION_SERVER_CERT_CONTEXT, &certContext, &bufferSize)); + + THROW_HR_IF(APPINSTALLER_CLI_ERROR_PINNED_CERTIFICATE_MISMATCH, !pinningConfiguration.Validate(certContext.get())); + } + + std::chrono::seconds GetRetryAfter(const web::http::http_headers& headers) + { + auto retryAfterHeader = headers.find(web::http::header_names::retry_after); + if (retryAfterHeader != headers.end()) + { + return AppInstaller::Utility::GetRetryAfter(retryAfterHeader->second.c_str()); + } + + return 0s; + } + } + + HttpClientHelper::HttpClientHelper(std::shared_ptr<web::http::http_pipeline_stage> stage) + : m_defaultRequestHandlerStage(std::move(stage)) + { + const auto& proxyUri = Settings::Network().GetProxyUri(); + if (proxyUri) + { + AICLI_LOG(Repo, Info, << "Setting proxy for REST HTTP Client helper to " << proxyUri.value()); + m_clientConfig.set_proxy(web::web_proxy{ Utility::ConvertToUTF16(proxyUri.value()) }); + } + else + { + AICLI_LOG(Repo, Info, << "REST HTTP Client helper does not use proxy"); + } + } + + pplx::task<web::http::http_response> HttpClientHelper::Post( + const utility::string_t& uri, + const web::json::value& body, + const HttpClientHelper::HttpRequestHeaders& headers, + const HttpClientHelper::HttpRequestHeaders& authHeaders) const + { + AICLI_LOG(Repo, Info, << "Sending http POST request to: " << utility::conversions::to_utf8string(uri)); + web::http::client::http_client client = GetClient(uri); + web::http::http_request request{ web::http::methods::POST }; + request.headers().set_content_type(web::http::details::mime_types::application_json); + request.set_body(body.serialize()); + + // Add headers + for (auto& pair : headers) + { + 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())); + + // Add auth headers after logging + for (auto& pair : authHeaders) + { + request.headers().add(pair.first, pair.second); + } + + return client.request(request); + } + + std::optional<web::json::value> HttpClientHelper::HandlePost( + const utility::string_t& uri, + const web::json::value& body, + const HttpClientHelper::HttpRequestHeaders& headers, + const HttpClientHelper::HttpRequestHeaders& authHeaders) const + { + web::http::http_response httpResponse; + HttpClientHelper::Post(uri, body, headers, authHeaders).then([&httpResponse](const web::http::http_response& response) + { + httpResponse = response; + }).wait(); + + return ValidateAndExtractResponse(httpResponse); + } + + pplx::task<web::http::http_response> HttpClientHelper::Get( + const utility::string_t& uri, + const HttpClientHelper::HttpRequestHeaders& headers, + const HttpClientHelper::HttpRequestHeaders& authHeaders) const + { + AICLI_LOG(Repo, Info, << "Sending http GET request to: " << utility::conversions::to_utf8string(uri)); + web::http::client::http_client client = GetClient(uri); + web::http::http_request request{ web::http::methods::GET }; + request.headers().set_content_type(web::http::details::mime_types::application_json); + + // Add headers + for (auto& pair : headers) + { + 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())); + + // Add auth headers after logging + for (auto& pair : authHeaders) + { + request.headers().add(pair.first, pair.second); + } + + return client.request(request); + } + + std::optional<web::json::value> HttpClientHelper::HandleGet( + const utility::string_t& uri, + const HttpClientHelper::HttpRequestHeaders& headers, + const HttpClientHelper::HttpRequestHeaders& authHeaders) const + { + web::http::http_response httpResponse; + Get(uri, headers, authHeaders).then([&httpResponse](const web::http::http_response& response) + { + httpResponse = response; + }).wait(); + + return ValidateAndExtractResponse(httpResponse); + } + + void HttpClientHelper::SetPinningConfiguration(const Certificates::PinningConfiguration& configuration) + { + m_clientConfig.set_nativehandle_servercertificate_validation([pinConfig = configuration](web::http::client::native_handle handle) + { + NativeHandleServerCertificateValidation(handle, pinConfig); + }); + } + + web::http::client::http_client HttpClientHelper::GetClient(const utility::string_t& uri) const + { + web::http::client::http_client client{ uri, m_clientConfig }; + + // Add default custom handlers if any. + if (m_defaultRequestHandlerStage) + { + client.add_handler(m_defaultRequestHandlerStage); + } + + return client; + } + + std::optional<web::json::value> HttpClientHelper::ValidateAndExtractResponse(const web::http::http_response& response) const + { + AICLI_LOG(Repo, Info, << "Response status: " << response.status_code()); + // Ensure that we wait for the content to be ready before we log it; otherwise it will be truncated. + AICLI_LOG_LARGE_STRING(Repo, Verbose, << "Response details:", + response.content_ready().then([&](const web::http::http_response&) { return utility::conversions::to_utf8string(response.to_string()); }).get()); + + std::optional<web::json::value> result; + switch (response.status_code()) + { + case web::http::status_codes::OK: + result = ExtractJsonResponse(response); + break; + + case web::http::status_codes::NotFound: + THROW_HR(APPINSTALLER_CLI_ERROR_RESTSOURCE_ENDPOINT_NOT_FOUND); + + case web::http::status_codes::NoContent: + result = {}; + break; + + case web::http::status_codes::BadRequest: + THROW_HR(APPINSTALLER_CLI_ERROR_RESTSOURCE_INTERNAL_ERROR); + + case web::http::status_codes::TooManyRequests: + case web::http::status_codes::ServiceUnavailable: + THROW_EXCEPTION(AppInstaller::Utility::ServiceUnavailableException(GetRetryAfter(response.headers()))); + + default: + THROW_HR(MAKE_HRESULT(SEVERITY_ERROR, FACILITY_HTTP, response.status_code())); + } + + return result; + } + + std::optional<web::json::value> HttpClientHelper::ExtractJsonResponse(const web::http::http_response& response) const + { + utility::string_t contentType = response.headers().content_type(); + + THROW_HR_IF(APPINSTALLER_CLI_ERROR_RESTSOURCE_UNSUPPORTED_MIME_TYPE, + !contentType._Starts_with(web::http::details::mime_types::application_json)); + + return response.extract_json().get(); + } +} diff --git a/src/AppInstallerCommonCore/Public/winget/HttpClientHelper.h b/src/AppInstallerCommonCore/Public/winget/HttpClientHelper.h @@ -0,0 +1,39 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include <winget/Certificates.h> +#include <cpprest/http_client.h> +#include <cpprest/json.h> +#include <optional> +#include <vector> + +namespace AppInstaller::Http +{ + struct HttpClientHelper + { + using HttpRequestHeaders = std::unordered_map<utility::string_t, utility::string_t>; + + HttpClientHelper(std::shared_ptr<web::http::http_pipeline_stage> = {}); + + pplx::task<web::http::http_response> Post(const utility::string_t& uri, const web::json::value& body, const HttpRequestHeaders& headers = {}, const HttpRequestHeaders& authHeaders = {}) const; + + std::optional<web::json::value> HandlePost(const utility::string_t& uri, const web::json::value& body, const HttpRequestHeaders& headers = {}, const HttpRequestHeaders& authHeaders = {}) const; + + pplx::task<web::http::http_response> Get(const utility::string_t& uri, const HttpRequestHeaders& headers = {}, const HttpRequestHeaders& authHeaders = {}) const; + + std::optional<web::json::value> HandleGet(const utility::string_t& uri, const HttpRequestHeaders& headers = {}, const HttpRequestHeaders& authHeaders = {}) const; + + void SetPinningConfiguration(const Certificates::PinningConfiguration& configuration); + + protected: + std::optional<web::json::value> ValidateAndExtractResponse(const web::http::http_response& response) const; + + std::optional<web::json::value> ExtractJsonResponse(const web::http::http_response& response) const; + + private: + web::http::client::http_client GetClient(const utility::string_t& uri) const; + + std::shared_ptr<web::http::http_pipeline_stage> m_defaultRequestHandlerStage; + web::http::client::http_client_config m_clientConfig; + }; +} diff --git a/src/AppInstallerCommonCore/Public/winget/Rest.h b/src/AppInstallerCommonCore/Public/winget/Rest.h @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include <cpprest/json.h> + +namespace AppInstaller::Rest +{ + utility::string_t GetRestAPIBaseUri(std::string restApiUri); + + bool IsValidUri(const utility::string_t& restApiUri); + + utility::string_t AppendPathToUri(const utility::string_t& restApiUri, const utility::string_t& path); + + utility::string_t MakeQueryParam(std::string_view queryName, const std::string& queryValue); + + utility::string_t AppendQueryParamsToUri(const utility::string_t& uri, const std::map<std::string_view, std::string>& queryParameters); + + std::vector<std::string> GetUniqueItems(const std::vector<std::string>& list); +} + diff --git a/src/AppInstallerCommonCore/Rest.cpp b/src/AppInstallerCommonCore/Rest.cpp @@ -0,0 +1,72 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "AppInstallerStrings.h" +#include "winget/Rest.h" +#include <winget/JsonUtil.h> + +namespace AppInstaller::Rest +{ + utility::string_t GetRestAPIBaseUri(std::string restApiUri) + { + // Trim + std::string uri = restApiUri; + if (!uri.empty()) + { + uri = AppInstaller::Utility::Trim(uri); + + // Remove trailing forward slash + if (uri.back() == '/') + { + uri.pop_back(); + } + } + + // Encode the Uri + return web::uri::encode_uri(JSON::GetUtilityString(uri)); + } + + bool IsValidUri(const utility::string_t& restApiUri) + { + return web::uri::validate(restApiUri); + } + + utility::string_t AppendPathToUri(const utility::string_t& restApiUri, const utility::string_t& path) + { + web::uri_builder builder(restApiUri); + builder.append_path(path, true); + return builder.to_string(); + } + + utility::string_t MakeQueryParam(std::string_view queryName, const std::string& queryValue) + { + std::string queryParam; + queryParam.append(queryName).append("=").append(queryValue); + + return utility::conversions::to_string_t(queryParam); + } + + utility::string_t AppendQueryParamsToUri(const utility::string_t& uri, const std::map<std::string_view, std::string>& queryParameters) + { + web::http::uri_builder builder{ uri }; + + for (auto& pair : queryParameters) + { + builder.append_query(MakeQueryParam(pair.first, pair.second), true); + } + + return builder.to_string(); + } + + std::vector<std::string> GetUniqueItems(const std::vector<std::string>& list) + { + std::set<std::string> set; + for (const auto& item : list) + { + set.emplace(item); + } + + std::vector<std::string> result{ set.begin(), set.end() }; + return result; + } +} diff --git a/src/AppInstallerCommonCore/pch.h b/src/AppInstallerCommonCore/pch.h @@ -1,15 +1,14 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #define NOMINMAX #include <Windows.h> #include <AclAPI.h> -#include <appmodel.h> -#include <WinInet.h> +#include <appmodel.h> #include <sddl.h> #include <Shlobj.h> -#include <Shlwapi.h> +#include <Shlwapi.h> #include <wow64apiset.h> #include <icu.h> #include <msi.h> @@ -26,10 +25,6 @@ // TODO: See if we can get down to having just one JSON parser... #include <json/json.h> -#ifndef WINGET_DISABLE_FOR_FUZZING -#include <cpprest/json.h> -#endif - #pragma warning( push ) #pragma warning ( disable : 4458 4100 4702 6031 ) #include <valijson/schema.hpp> @@ -62,7 +57,16 @@ #include <type_traits> #include <unordered_set> #include <vector> -#include <variant> +#include <variant> + +#ifndef WINGET_DISABLE_FOR_FUZZING +#pragma warning( push ) +#pragma warning ( disable : 26495 26439 ) +#include <cpprest/http_client.h> +#include <cpprest/json.h> +#include <cpprest/uri_builder.h> +#pragma warning( pop ) +#endif #pragma warning( push ) #pragma warning ( disable : 6001 6285 6287 6340 6387 6388 26451 26495 28196 ) diff --git a/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj b/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj @@ -437,10 +437,8 @@ <ClInclude Include="Rest\Schema\1_7\Json\ManifestDeserializer.h" /> <ClInclude Include="Rest\Schema\AuthenticationInfoParser.h" /> <ClInclude Include="Rest\Schema\CommonRestConstants.h" /> - <ClInclude Include="Rest\Schema\HttpClientHelper.h" /> <ClInclude Include="Rest\Schema\InformationResponseDeserializer.h" /> <ClInclude Include="Rest\Schema\IRestClient.h" /> - <ClInclude Include="Rest\Schema\RestHelper.h" /> <ClInclude Include="Rest\Schema\SearchRequestComposer.h" /> <ClInclude Include="Rest\Schema\SearchResponseParser.h" /> <ClInclude Include="SourceFactory.h" /> @@ -522,9 +520,7 @@ <ClCompile Include="Rest\Schema\1_7\Json\ManifestDeserializer_1_7.cpp" /> <ClCompile Include="Rest\Schema\1_7\RestInterface_1_7.cpp" /> <ClCompile Include="Rest\Schema\AuthenticationInfoParser.cpp" /> - <ClCompile Include="Rest\Schema\HttpClientHelper.cpp" /> <ClCompile Include="Rest\Schema\InformationResponseDeserializer.cpp" /> - <ClCompile Include="Rest\Schema\RestHelper.cpp" /> <ClCompile Include="Rest\Schema\SearchRequestComposer.cpp" /> <ClCompile Include="Rest\Schema\SearchResponseParser.cpp" /> <ClCompile Include="SourceList.cpp" /> @@ -549,4 +545,4 @@ <Error Condition="!Exists('$(SolutionDir)\packages\Microsoft.Windows.CppWinRT.2.0.230706.1\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\packages\Microsoft.Windows.CppWinRT.2.0.230706.1\build\native\Microsoft.Windows.CppWinRT.props'))" /> <Error Condition="!Exists('$(SolutionDir)\packages\Microsoft.Windows.CppWinRT.2.0.230706.1\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\packages\Microsoft.Windows.CppWinRT.2.0.230706.1\build\native\Microsoft.Windows.CppWinRT.targets'))" /> </Target> -</Project>- \ No newline at end of file +</Project> diff --git a/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj.filters b/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj.filters @@ -210,9 +210,6 @@ <ClInclude Include="Rest\RestSourceFactory.h"> <Filter>Rest</Filter> </ClInclude> - <ClInclude Include="Rest\Schema\RestHelper.h"> - <Filter>Rest\Schema</Filter> - </ClInclude> <ClInclude Include="Rest\Schema\1_0\Json\ManifestDeserializer.h"> <Filter>Rest\Schema\1_0\Json</Filter> </ClInclude> @@ -243,9 +240,6 @@ <ClInclude Include="Rest\Schema\InformationResponseDeserializer.h"> <Filter>Rest\Schema</Filter> </ClInclude> - <ClInclude Include="Rest\Schema\HttpClientHelper.h"> - <Filter>Rest\Schema</Filter> - </ClInclude> <ClInclude Include="Rest\Schema\1_1\Interface.h"> <Filter>Rest\Schema\1_1</Filter> </ClInclude> @@ -488,9 +482,6 @@ <ClCompile Include="Rest\RestSourceFactory.cpp"> <Filter>Rest</Filter> </ClCompile> - <ClCompile Include="Rest\Schema\RestHelper.cpp"> - <Filter>Rest\Schema</Filter> - </ClCompile> <ClCompile Include="Rest\Schema\1_0\Json\ManifestDeserializer_1_0.cpp"> <Filter>Rest\Schema\1_0\Json</Filter> </ClCompile> @@ -515,9 +506,6 @@ <ClCompile Include="Microsoft\PredefinedWriteableSourceFactory.cpp"> <Filter>Microsoft</Filter> </ClCompile> - <ClCompile Include="Rest\Schema\HttpClientHelper.cpp"> - <Filter>Rest\Schema</Filter> - </ClCompile> <ClCompile Include="Rest\Schema\1_1\RestInterface_1_1.cpp"> <Filter>Rest\Schema\1_1</Filter> </ClCompile> diff --git a/src/AppInstallerRepositoryCore/Rest/RestClient.cpp b/src/AppInstallerRepositoryCore/Rest/RestClient.cpp @@ -8,15 +8,16 @@ #include "Rest/Schema/1_5/Interface.h" #include "Rest/Schema/1_6/Interface.h" #include "Rest/Schema/1_7/Interface.h" -#include "Rest/Schema/HttpClientHelper.h" -#include <winget/JsonUtil.h> #include "Rest/Schema/InformationResponseDeserializer.h" #include "Rest/Schema/CommonRestConstants.h" -#include "Rest/Schema/RestHelper.h" +#include <winget/HttpClientHelper.h> +#include <winget/Rest.h> +#include <winget/JsonUtil.h> using namespace AppInstaller::Repository::Rest::Schema; using namespace AppInstaller::Repository::Rest::Schema::V1_0; using namespace AppInstaller::Utility; +using namespace AppInstaller::Http; namespace AppInstaller::Repository::Rest { @@ -59,7 +60,7 @@ namespace AppInstaller::Repository::Rest const utility::string_t& restApi, const HttpClientHelper::HttpRequestHeaders& additionalHeaders, const HttpClientHelper& clientHelper) { // Call information endpoint - utility::string_t endpoint = RestHelper::AppendPathToUri(restApi, JSON::GetUtilityString(InformationGetEndpoint)); + utility::string_t endpoint = AppInstaller::Rest::AppendPathToUri(restApi, JSON::GetUtilityString(InformationGetEndpoint)); std::optional<web::json::value> response = clientHelper.HandleGet(endpoint, additionalHeaders); THROW_HR_IF(APPINSTALLER_CLI_ERROR_UNSUPPORTED_RESTSOURCE, !response); @@ -133,10 +134,10 @@ namespace AppInstaller::Repository::Rest return *commonVersions.rbegin(); } - Schema::IRestClient::Information RestClient::GetInformation(const std::string& restApi, std::optional<std::string> customHeader, std::string_view caller, const Schema::HttpClientHelper& helper) + Schema::IRestClient::Information RestClient::GetInformation(const std::string& restApi, std::optional<std::string> customHeader, std::string_view caller, const HttpClientHelper& helper) { - utility::string_t restEndpoint = RestHelper::GetRestAPIBaseUri(restApi); - THROW_HR_IF(APPINSTALLER_CLI_ERROR_RESTSOURCE_INVALID_URL, !RestHelper::IsValidUri(restEndpoint)); + utility::string_t restEndpoint = AppInstaller::Rest::GetRestAPIBaseUri(restApi); + THROW_HR_IF(APPINSTALLER_CLI_ERROR_RESTSOURCE_INVALID_URL, !AppInstaller::Rest::IsValidUri(restEndpoint)); auto headers = GetHeaders(customHeader, caller); @@ -181,8 +182,8 @@ namespace AppInstaller::Repository::Rest RestClient RestClient::Create(const std::string& restApi, std::optional<std::string> customHeader, std::string_view caller, const HttpClientHelper& helper, const Authentication::AuthenticationArguments& authArgs) { - utility::string_t restEndpoint = RestHelper::GetRestAPIBaseUri(restApi); - THROW_HR_IF(APPINSTALLER_CLI_ERROR_RESTSOURCE_INVALID_URL, !RestHelper::IsValidUri(restEndpoint)); + utility::string_t restEndpoint = AppInstaller::Rest::GetRestAPIBaseUri(restApi); + THROW_HR_IF(APPINSTALLER_CLI_ERROR_RESTSOURCE_INVALID_URL, !AppInstaller::Rest::IsValidUri(restEndpoint)); auto headers = GetHeaders(customHeader, caller); diff --git a/src/AppInstallerRepositoryCore/Rest/RestClient.h b/src/AppInstallerRepositoryCore/Rest/RestClient.h @@ -2,11 +2,9 @@ // Licensed under the MIT License. #pragma once #include <set> -#include <cpprest/json.h> #include "Rest/Schema/IRestClient.h" -#include "Rest/Schema/HttpClientHelper.h" -#include "cpprest/json.h" #include "ISource.h" +#include <winget/HttpClientHelper.h> namespace AppInstaller::Repository::Rest { @@ -30,18 +28,18 @@ namespace AppInstaller::Repository::Rest static std::optional<AppInstaller::Utility::Version> GetLatestCommonVersion(const std::vector<std::string>& serverSupportedVersions, const std::set<AppInstaller::Utility::Version>& wingetSupportedVersions); // Responsible for getting the source information contracts with minimal validation. Does not try to create a rest interface out of it. - static Schema::IRestClient::Information GetInformation(const std::string& restApi, std::optional<std::string> customHeader, std::string_view caller, const Schema::HttpClientHelper& helper); + static Schema::IRestClient::Information GetInformation(const std::string& restApi, std::optional<std::string> customHeader, std::string_view caller, const Http::HttpClientHelper& helper); static std::unique_ptr<Schema::IRestClient> GetSupportedInterface( const std::string& restApi, - const Schema::HttpClientHelper::HttpRequestHeaders& additionalHeaders, + const Http::HttpClientHelper::HttpRequestHeaders& additionalHeaders, const Schema::IRestClient::Information& information, const Authentication::AuthenticationArguments& authArgs, const AppInstaller::Utility::Version& version, - const Schema::HttpClientHelper& helper); + const Http::HttpClientHelper& helper); // Creates the rest client. Full validation performed (just as opening the source) - static RestClient Create(const std::string& restApi, std::optional<std::string> customHeader, std::string_view caller, const Schema::HttpClientHelper& helper, const Authentication::AuthenticationArguments& authArgs = {}); + static RestClient Create(const std::string& restApi, std::optional<std::string> customHeader, std::string_view caller, const Http::HttpClientHelper& helper, const Authentication::AuthenticationArguments& authArgs = {}); private: RestClient(std::unique_ptr<Schema::IRestClient> supportedInterface, std::string sourceIdentifier); diff --git a/src/AppInstallerRepositoryCore/Rest/RestSourceFactory.cpp b/src/AppInstallerRepositoryCore/Rest/RestSourceFactory.cpp @@ -81,7 +81,7 @@ namespace AppInstaller::Repository::Rest } SourceDetails m_details; - Schema::HttpClientHelper m_httpClientHelper; + Http::HttpClientHelper m_httpClientHelper; SourceInformation m_information; std::optional<std::string> m_customHeader; std::string m_caller; diff --git a/src/AppInstallerRepositoryCore/Rest/Schema/1_0/Interface.h b/src/AppInstallerRepositoryCore/Rest/Schema/1_0/Interface.h @@ -2,15 +2,14 @@ // Licensed under the MIT License. #pragma once #include "Rest/Schema/IRestClient.h" -#include "Rest/Schema/HttpClientHelper.h" -#include <cpprest/json.h> +#include <winget/HttpClientHelper.h> namespace AppInstaller::Repository::Rest::Schema::V1_0 { // Interface to this schema version exposed through IRestClient. struct Interface : public IRestClient { - Interface(const std::string& restApi, const Schema::HttpClientHelper& helper); + Interface(const std::string& restApi, const Http::HttpClientHelper& helper); Interface(const Interface&) = delete; Interface& operator=(const Interface&) = delete; @@ -39,13 +38,13 @@ namespace AppInstaller::Repository::Rest::Schema::V1_0 virtual std::vector<Manifest::Manifest> GetParsedManifests(const web::json::value& manifestsResponseObject) const; // Gets auth headers if source requires authentication for access. - virtual HttpClientHelper::HttpRequestHeaders GetAuthHeaders() const; + virtual Http::HttpClientHelper::HttpRequestHeaders GetAuthHeaders() const; - HttpClientHelper::HttpRequestHeaders m_requiredRestApiHeaders; + Http::HttpClientHelper::HttpRequestHeaders m_requiredRestApiHeaders; private: std::string m_restApiUri; utility::string_t m_searchEndpoint; - HttpClientHelper m_httpClientHelper; + Http::HttpClientHelper m_httpClientHelper; }; } diff --git a/src/AppInstallerRepositoryCore/Rest/Schema/1_0/Json/ManifestDeserializer_1_0.cpp b/src/AppInstallerRepositoryCore/Rest/Schema/1_0/Json/ManifestDeserializer_1_0.cpp @@ -2,11 +2,11 @@ // Licensed under the MIT License. #include "pch.h" #include "Rest/Schema/1_0/Interface.h" +#include "Rest/Schema/CommonRestConstants.h" #include "Rest/Schema/IRestClient.h" -#include "Rest/Schema/HttpClientHelper.h" #include "ManifestDeserializer.h" +#include <winget/HttpClientHelper.h> #include <winget/JsonUtil.h> -#include "Rest/Schema/CommonRestConstants.h" using namespace AppInstaller::Manifest; diff --git a/src/AppInstallerRepositoryCore/Rest/Schema/1_0/Json/SearchResponseDeserializer_1_0.cpp b/src/AppInstallerRepositoryCore/Rest/Schema/1_0/Json/SearchResponseDeserializer_1_0.cpp @@ -1,11 +1,11 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" +#include "Rest/Schema/CommonRestConstants.h" #include "Rest/Schema/IRestClient.h" #include "SearchResponseDeserializer.h" #include <winget/JsonUtil.h> -#include "Rest/Schema/RestHelper.h" -#include "Rest/Schema/CommonRestConstants.h" +#include <winget/Rest.h> namespace AppInstaller::Repository::Rest::Schema::V1_0::Json { @@ -116,8 +116,8 @@ namespace AppInstaller::Repository::Rest::Schema::V1_0::Json } std::string channel = JSON::GetRawStringValueFromJsonNode(versionInfoJsonObject, JSON::GetUtilityString(Channel)).value_or(""); - std::vector<std::string> packageFamilyNames = RestHelper::GetUniqueItems(JSON::GetRawStringArrayFromJsonNode(versionInfoJsonObject, JSON::GetUtilityString(PackageFamilyNames))); - std::vector<std::string> productCodes = RestHelper::GetUniqueItems(JSON::GetRawStringArrayFromJsonNode(versionInfoJsonObject, JSON::GetUtilityString(ProductCodes))); + std::vector<std::string> packageFamilyNames = AppInstaller::Rest::GetUniqueItems(JSON::GetRawStringArrayFromJsonNode(versionInfoJsonObject, JSON::GetUtilityString(PackageFamilyNames))); + std::vector<std::string> productCodes = AppInstaller::Rest::GetUniqueItems(JSON::GetRawStringArrayFromJsonNode(versionInfoJsonObject, JSON::GetUtilityString(ProductCodes))); return IRestClient::VersionInfo{ AppInstaller::Utility::VersionAndChannel{std::move(version.value()), std::move(channel)}, diff --git a/src/AppInstallerRepositoryCore/Rest/Schema/1_0/RestInterface_1_0.cpp b/src/AppInstallerRepositoryCore/Rest/Schema/1_0/RestInterface_1_0.cpp @@ -3,12 +3,12 @@ #include "pch.h" #include "Rest/Schema/1_0/Interface.h" #include "Rest/Schema/IRestClient.h" -#include "Rest/Schema/HttpClientHelper.h" +#include <winget/HttpClientHelper.h> #include <winget/JsonUtil.h> +#include <winget/ManifestJSONParser.h> #include <winget/ManifestValidation.h> -#include "Rest/Schema/RestHelper.h" +#include <winget/Rest.h> #include "Rest/Schema/CommonRestConstants.h" -#include "winget/ManifestJSONParser.h" #include "Rest/Schema/SearchResponseParser.h" #include "Rest/Schema/SearchRequestComposer.h" @@ -24,25 +24,37 @@ namespace AppInstaller::Repository::Rest::Schema::V1_0 utility::string_t GetSearchEndpoint(const std::string& restApiUri) { - return RestHelper::AppendPathToUri(AppInstaller::JSON::GetUtilityString(restApiUri), AppInstaller::JSON::GetUtilityString(ManifestSearchPostEndpoint)); + return AppInstaller::Rest::AppendPathToUri(AppInstaller::JSON::GetUtilityString(restApiUri), AppInstaller::JSON::GetUtilityString(ManifestSearchPostEndpoint)); } utility::string_t GetManifestByVersionEndpoint( const std::string& restApiUri, const std::string& packageId, const std::map<std::string_view, std::string>& queryParameters) { - utility::string_t getManifestEndpoint = RestHelper::AppendPathToUri( + utility::string_t getManifestEndpoint = AppInstaller::Rest::AppendPathToUri( AppInstaller::JSON::GetUtilityString(restApiUri), AppInstaller::JSON::GetUtilityString(ManifestByVersionAndChannelGetEndpoint)); - utility::string_t getManifestWithPackageIdPath = RestHelper::AppendPathToUri(getManifestEndpoint, AppInstaller::JSON::GetUtilityString(packageId)); + utility::string_t getManifestWithPackageIdPath = AppInstaller::Rest::AppendPathToUri(getManifestEndpoint, AppInstaller::JSON::GetUtilityString(packageId)); // Create the endpoint with query parameters - return RestHelper::AppendQueryParamsToUri(getManifestWithPackageIdPath, queryParameters); + return AppInstaller::Rest::AppendQueryParamsToUri(getManifestWithPackageIdPath, queryParameters); + } + + std::optional<utility::string_t> GetContinuationToken(const web::json::value& jsonObject) + { + std::optional<std::string> continuationToken = AppInstaller::JSON::GetRawStringValueFromJsonNode(jsonObject, AppInstaller::JSON::GetUtilityString(ContinuationToken)); + + if (continuationToken) + { + return utility::conversions::to_string_t(continuationToken.value()); + } + + return {}; } } - Interface::Interface(const std::string& restApi, const HttpClientHelper& httpClientHelper) : m_restApiUri(restApi), m_httpClientHelper(httpClientHelper) + Interface::Interface(const std::string& restApi, const Http::HttpClientHelper& httpClientHelper) : m_restApiUri(restApi), m_httpClientHelper(httpClientHelper) { - THROW_HR_IF(APPINSTALLER_CLI_ERROR_RESTSOURCE_INVALID_URL, !RestHelper::IsValidUri(AppInstaller::JSON::GetUtilityString(restApi))); + THROW_HR_IF(APPINSTALLER_CLI_ERROR_RESTSOURCE_INVALID_URL, !AppInstaller::Rest::IsValidUri(AppInstaller::JSON::GetUtilityString(restApi))); m_searchEndpoint = GetSearchEndpoint(m_restApiUri); m_requiredRestApiHeaders.emplace(AppInstaller::JSON::GetUtilityString(ContractVersion), AppInstaller::JSON::GetUtilityString(Version_1_0_0.ToString())); @@ -73,7 +85,7 @@ namespace AppInstaller::Repository::Rest::Schema::V1_0 { SearchResult results; utility::string_t continuationToken; - HttpClientHelper::HttpRequestHeaders searchHeaders = m_requiredRestApiHeaders; + Http::HttpClientHelper::HttpRequestHeaders searchHeaders = m_requiredRestApiHeaders; do { if (!continuationToken.empty()) @@ -98,7 +110,7 @@ namespace AppInstaller::Repository::Rest::Schema::V1_0 } std::move(currentResult.Matches.begin(), std::next(currentResult.Matches.begin(), insertElements), std::inserter(results.Matches, results.Matches.end())); - ct = RestHelper::GetContinuationToken(jsonObject.value()).value_or(L""); + ct = GetContinuationToken(jsonObject.value()).value_or(L""); } continuationToken = ct; @@ -208,7 +220,7 @@ namespace AppInstaller::Repository::Rest::Schema::V1_0 std::vector<Manifest::Manifest> results; utility::string_t continuationToken; - HttpClientHelper::HttpRequestHeaders searchHeaders = m_requiredRestApiHeaders; + Http::HttpClientHelper::HttpRequestHeaders searchHeaders = m_requiredRestApiHeaders; std::optional<web::json::value> jsonObject = m_httpClientHelper.HandleGet(GetManifestByVersionEndpoint(m_restApiUri, packageId, validatedParams), searchHeaders, GetAuthHeaders()); if (!jsonObject) @@ -267,7 +279,7 @@ namespace AppInstaller::Repository::Rest::Schema::V1_0 return manifestParser.Deserialize(manifestsResponseObject); } - HttpClientHelper::HttpRequestHeaders Interface::GetAuthHeaders() const + Http::HttpClientHelper::HttpRequestHeaders Interface::GetAuthHeaders() const { return {}; } diff --git a/src/AppInstallerRepositoryCore/Rest/Schema/1_1/Interface.h b/src/AppInstallerRepositoryCore/Rest/Schema/1_1/Interface.h @@ -8,7 +8,7 @@ namespace AppInstaller::Repository::Rest::Schema::V1_1 // Interface to this schema version exposed through IRestClient. struct Interface : public V1_0::Interface { - Interface(const std::string& restApi, const Schema::HttpClientHelper& helper, IRestClient::Information information, const HttpClientHelper::HttpRequestHeaders& additionalHeaders = {}); + Interface(const std::string& restApi, const Http::HttpClientHelper& helper, IRestClient::Information information, const Http::HttpClientHelper::HttpRequestHeaders& additionalHeaders = {}); Interface(const Interface&) = delete; Interface& operator=(const Interface&) = delete; diff --git a/src/AppInstallerRepositoryCore/Rest/Schema/1_1/RestInterface_1_1.cpp b/src/AppInstallerRepositoryCore/Rest/Schema/1_1/RestInterface_1_1.cpp @@ -2,11 +2,10 @@ // Licensed under the MIT License. #include "pch.h" #include "Rest/Schema/1_1/Interface.h" +#include "Rest/Schema/CommonRestConstants.h" #include "Rest/Schema/IRestClient.h" -#include "Rest/Schema/HttpClientHelper.h" +#include <winget/HttpClientHelper.h> #include <winget/JsonUtil.h> -#include "Rest/Schema/RestHelper.h" -#include "Rest/Schema/CommonRestConstants.h" using namespace std::string_view_literals; @@ -26,9 +25,9 @@ namespace AppInstaller::Repository::Rest::Schema::V1_1 Interface::Interface( const std::string& restApi, - const HttpClientHelper& httpClientHelper, + const Http::HttpClientHelper& httpClientHelper, IRestClient::Information information, - const HttpClientHelper::HttpRequestHeaders& additionalHeaders) : V1_0::Interface(restApi, httpClientHelper), m_information(std::move(information)) + const Http::HttpClientHelper::HttpRequestHeaders& additionalHeaders) : V1_0::Interface(restApi, httpClientHelper), m_information(std::move(information)) { m_requiredRestApiHeaders[JSON::GetUtilityString(ContractVersion)] = JSON::GetUtilityString(Version_1_1_0.ToString()); diff --git a/src/AppInstallerRepositoryCore/Rest/Schema/1_4/Interface.h b/src/AppInstallerRepositoryCore/Rest/Schema/1_4/Interface.h @@ -8,7 +8,7 @@ namespace AppInstaller::Repository::Rest::Schema::V1_4 // Interface to this schema version exposed through IRestClient. struct Interface : public V1_1::Interface { - Interface(const std::string& restApi, const Schema::HttpClientHelper& helper, IRestClient::Information information, const HttpClientHelper::HttpRequestHeaders& additionalHeaders = {}); + Interface(const std::string& restApi, const Http::HttpClientHelper& helper, IRestClient::Information information, const Http::HttpClientHelper::HttpRequestHeaders& additionalHeaders = {}); Interface(const Interface&) = delete; Interface& operator=(const Interface&) = delete; diff --git a/src/AppInstallerRepositoryCore/Rest/Schema/1_4/Json/SearchResponseDeserializer_1_4.cpp b/src/AppInstallerRepositoryCore/Rest/Schema/1_4/Json/SearchResponseDeserializer_1_4.cpp @@ -2,8 +2,8 @@ // Licensed under the MIT License. #include "pch.h" #include "SearchResponseDeserializer.h" -#include "Rest/Schema/RestHelper.h" #include <winget/JsonUtil.h> +#include <winget/Rest.h> namespace AppInstaller::Repository::Rest::Schema::V1_4::Json { @@ -19,8 +19,8 @@ namespace AppInstaller::Repository::Rest::Schema::V1_4::Json auto result = V1_0::Json::SearchResponseDeserializer::DeserializeVersionInfo(versionInfoJsonObject); if (result.has_value()) { - result->UpgradeCodes = RestHelper::GetUniqueItems(JSON::GetRawStringArrayFromJsonNode(versionInfoJsonObject, JSON::GetUtilityString(UpgradeCodes))); - auto arpVersions = RestHelper::GetUniqueItems(JSON::GetRawStringArrayFromJsonNode(versionInfoJsonObject, JSON::GetUtilityString(AppsAndFeaturesEntryVersions))); + result->UpgradeCodes = AppInstaller::Rest::GetUniqueItems(JSON::GetRawStringArrayFromJsonNode(versionInfoJsonObject, JSON::GetUtilityString(UpgradeCodes))); + auto arpVersions = AppInstaller::Rest::GetUniqueItems(JSON::GetRawStringArrayFromJsonNode(versionInfoJsonObject, JSON::GetUtilityString(AppsAndFeaturesEntryVersions))); for (auto const& version : arpVersions) { result->ArpVersions.emplace_back(Utility::Version{ version }); diff --git a/src/AppInstallerRepositoryCore/Rest/Schema/1_4/RestInterface_1_4.cpp b/src/AppInstallerRepositoryCore/Rest/Schema/1_4/RestInterface_1_4.cpp @@ -2,18 +2,18 @@ // Licensed under the MIT License. #include "pch.h" #include "Rest/Schema/1_4/Interface.h" -#include "Rest/Schema/IRestClient.h" -#include "Rest/Schema/HttpClientHelper.h" #include "Rest/Schema/CommonRestConstants.h" +#include "Rest/Schema/IRestClient.h" +#include <winget/HttpClientHelper.h> #include <winget/JsonUtil.h> namespace AppInstaller::Repository::Rest::Schema::V1_4 { Interface::Interface( const std::string& restApi, - const HttpClientHelper& httpClientHelper, + const Http::HttpClientHelper& httpClientHelper, IRestClient::Information information, - const HttpClientHelper::HttpRequestHeaders& additionalHeaders) : V1_1::Interface(restApi, httpClientHelper, std::move(information), additionalHeaders) + const Http::HttpClientHelper::HttpRequestHeaders& additionalHeaders) : V1_1::Interface(restApi, httpClientHelper, std::move(information), additionalHeaders) { m_requiredRestApiHeaders[JSON::GetUtilityString(ContractVersion)] = JSON::GetUtilityString(Version_1_4_0.ToString()); } diff --git a/src/AppInstallerRepositoryCore/Rest/Schema/1_5/Interface.h b/src/AppInstallerRepositoryCore/Rest/Schema/1_5/Interface.h @@ -8,7 +8,7 @@ namespace AppInstaller::Repository::Rest::Schema::V1_5 // Interface to this schema version exposed through IRestClient. struct Interface : public V1_4::Interface { - Interface(const std::string& restApi, const Schema::HttpClientHelper& helper, IRestClient::Information information, const HttpClientHelper::HttpRequestHeaders& additionalHeaders = {}); + Interface(const std::string& restApi, const Http::HttpClientHelper& helper, IRestClient::Information information, const Http::HttpClientHelper::HttpRequestHeaders& additionalHeaders = {}); Interface(const Interface&) = delete; Interface& operator=(const Interface&) = delete; diff --git a/src/AppInstallerRepositoryCore/Rest/Schema/1_5/RestInterface_1_5.cpp b/src/AppInstallerRepositoryCore/Rest/Schema/1_5/RestInterface_1_5.cpp @@ -2,18 +2,18 @@ // Licensed under the MIT License. #include "pch.h" #include "Rest/Schema/1_5/Interface.h" -#include "Rest/Schema/IRestClient.h" -#include "Rest/Schema/HttpClientHelper.h" #include "Rest/Schema/CommonRestConstants.h" +#include "Rest/Schema/IRestClient.h" +#include <winget/HttpClientHelper.h> #include <winget/JsonUtil.h> namespace AppInstaller::Repository::Rest::Schema::V1_5 { Interface::Interface( const std::string& restApi, - const HttpClientHelper& httpClientHelper, + const Http::HttpClientHelper& httpClientHelper, IRestClient::Information information, - const HttpClientHelper::HttpRequestHeaders& additionalHeaders) : V1_4::Interface(restApi, httpClientHelper, std::move(information), additionalHeaders) + const Http::HttpClientHelper::HttpRequestHeaders& additionalHeaders) : V1_4::Interface(restApi, httpClientHelper, std::move(information), additionalHeaders) { m_requiredRestApiHeaders[JSON::GetUtilityString(ContractVersion)] = JSON::GetUtilityString(Version_1_5_0.ToString()); } diff --git a/src/AppInstallerRepositoryCore/Rest/Schema/1_6/Interface.h b/src/AppInstallerRepositoryCore/Rest/Schema/1_6/Interface.h @@ -8,7 +8,7 @@ namespace AppInstaller::Repository::Rest::Schema::V1_6 // Interface to this schema version exposed through IRestClient. struct Interface : public V1_5::Interface { - Interface(const std::string& restApi, const Schema::HttpClientHelper& helper, IRestClient::Information information, const HttpClientHelper::HttpRequestHeaders& additionalHeaders = {}); + Interface(const std::string& restApi, const Http::HttpClientHelper& helper, IRestClient::Information information, const Http::HttpClientHelper::HttpRequestHeaders& additionalHeaders = {}); Interface(const Interface&) = delete; Interface& operator=(const Interface&) = delete; diff --git a/src/AppInstallerRepositoryCore/Rest/Schema/1_6/RestInterface_1_6.cpp b/src/AppInstallerRepositoryCore/Rest/Schema/1_6/RestInterface_1_6.cpp @@ -2,18 +2,18 @@ // Licensed under the MIT License. #include "pch.h" #include "Rest/Schema/1_6/Interface.h" -#include "Rest/Schema/IRestClient.h" -#include "Rest/Schema/HttpClientHelper.h" #include "Rest/Schema/CommonRestConstants.h" +#include "Rest/Schema/IRestClient.h" +#include <winget/HttpClientHelper.h> #include <winget/JsonUtil.h> namespace AppInstaller::Repository::Rest::Schema::V1_6 { Interface::Interface( const std::string& restApi, - const HttpClientHelper& httpClientHelper, + const Http::HttpClientHelper& httpClientHelper, IRestClient::Information information, - const HttpClientHelper::HttpRequestHeaders& additionalHeaders) : V1_5::Interface(restApi, httpClientHelper, std::move(information), additionalHeaders) + const Http::HttpClientHelper::HttpRequestHeaders& additionalHeaders) : V1_5::Interface(restApi, httpClientHelper, std::move(information), additionalHeaders) { m_requiredRestApiHeaders[JSON::GetUtilityString(ContractVersion)] = JSON::GetUtilityString(Version_1_6_0.ToString()); } diff --git a/src/AppInstallerRepositoryCore/Rest/Schema/1_7/Interface.h b/src/AppInstallerRepositoryCore/Rest/Schema/1_7/Interface.h @@ -8,7 +8,7 @@ namespace AppInstaller::Repository::Rest::Schema::V1_7 // Interface to this schema version exposed through IRestClient. struct Interface : public V1_6::Interface { - Interface(const std::string& restApi, const Schema::HttpClientHelper& helper, IRestClient::Information information, const HttpClientHelper::HttpRequestHeaders& additionalHeaders = {}, Authentication::AuthenticationArguments authArgs = {}); + Interface(const std::string& restApi, const Http::HttpClientHelper& helper, IRestClient::Information information, const Http::HttpClientHelper::HttpRequestHeaders& additionalHeaders = {}, Authentication::AuthenticationArguments authArgs = {}); Interface(const Interface&) = delete; Interface& operator=(const Interface&) = delete; @@ -18,7 +18,7 @@ namespace AppInstaller::Repository::Rest::Schema::V1_7 Utility::Version GetVersion() const override; - HttpClientHelper::HttpRequestHeaders GetAuthHeaders() const override; + Http::HttpClientHelper::HttpRequestHeaders GetAuthHeaders() const override; protected: std::unique_ptr<Authentication::Authenticator> m_authenticator; diff --git a/src/AppInstallerRepositoryCore/Rest/Schema/1_7/RestInterface_1_7.cpp b/src/AppInstallerRepositoryCore/Rest/Schema/1_7/RestInterface_1_7.cpp @@ -2,18 +2,18 @@ // Licensed under the MIT License. #include "pch.h" #include "Rest/Schema/1_7/Interface.h" -#include "Rest/Schema/IRestClient.h" -#include "Rest/Schema/HttpClientHelper.h" #include "Rest/Schema/CommonRestConstants.h" +#include "Rest/Schema/IRestClient.h" +#include <winget/HttpClientHelper.h> #include <winget/JsonUtil.h> namespace AppInstaller::Repository::Rest::Schema::V1_7 { Interface::Interface( const std::string& restApi, - const HttpClientHelper& httpClientHelper, + const Http::HttpClientHelper& httpClientHelper, IRestClient::Information information, - const HttpClientHelper::HttpRequestHeaders& additionalHeaders, + const Http::HttpClientHelper::HttpRequestHeaders& additionalHeaders, Authentication::AuthenticationArguments authArgs) : V1_6::Interface(restApi, httpClientHelper, std::move(information), additionalHeaders), m_authArgs(std::move(authArgs)) { m_requiredRestApiHeaders[JSON::GetUtilityString(ContractVersion)] = JSON::GetUtilityString(Version_1_7_0.ToString()); @@ -35,9 +35,9 @@ namespace AppInstaller::Repository::Rest::Schema::V1_7 return Version_1_7_0; } - HttpClientHelper::HttpRequestHeaders Interface::GetAuthHeaders() const + Http::HttpClientHelper::HttpRequestHeaders Interface::GetAuthHeaders() const { - HttpClientHelper::HttpRequestHeaders result; + Http::HttpClientHelper::HttpRequestHeaders result; if (m_information.Authentication.Type == Authentication::AuthenticationType::MicrosoftEntraId) { diff --git a/src/AppInstallerRepositoryCore/Rest/Schema/HttpClientHelper.cpp b/src/AppInstallerRepositoryCore/Rest/Schema/HttpClientHelper.cpp @@ -1,213 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -#include "pch.h" -#include "HttpClientHelper.h" -#include <winget/NetworkSettings.h> - -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); - } - } - - void NativeHandleServerCertificateValidation(web::http::client::native_handle handle, const Certificates::PinningConfiguration& pinningConfiguration) - { - HINTERNET requestHandle = reinterpret_cast<HINTERNET>(handle); - - // Get certificate and pass along to pinning config - wil::unique_cert_context certContext; - DWORD bufferSize = sizeof(&certContext); - THROW_IF_WIN32_BOOL_FALSE(WinHttpQueryOption(requestHandle, WINHTTP_OPTION_SERVER_CERT_CONTEXT, &certContext, &bufferSize)); - - THROW_HR_IF(APPINSTALLER_CLI_ERROR_PINNED_CERTIFICATE_MISMATCH, !pinningConfiguration.Validate(certContext.get())); - } - - std::chrono::seconds GetRetryAfter(const web::http::http_headers& headers) - { - auto retryAfterHeader = headers.find(web::http::header_names::retry_after); - if (retryAfterHeader != headers.end()) - { - return AppInstaller::Utility::GetRetryAfter(retryAfterHeader->second.c_str()); - } - - return 0s; - } - } - - HttpClientHelper::HttpClientHelper(std::shared_ptr<web::http::http_pipeline_stage> stage) - : m_defaultRequestHandlerStage(std::move(stage)) - { - const auto& proxyUri = Settings::Network().GetProxyUri(); - if (proxyUri) - { - AICLI_LOG(Repo, Info, << "Setting proxy for REST HTTP Client helper to " << proxyUri.value()); - m_clientConfig.set_proxy(web::web_proxy{ Utility::ConvertToUTF16(proxyUri.value()) }); - } - else - { - AICLI_LOG(Repo, Info, << "REST HTTP Client helper does not use proxy"); - } - } - - pplx::task<web::http::http_response> HttpClientHelper::Post( - const utility::string_t& uri, - const web::json::value& body, - const HttpClientHelper::HttpRequestHeaders& headers, - const HttpClientHelper::HttpRequestHeaders& authHeaders) const - { - AICLI_LOG(Repo, Info, << "Sending http POST request to: " << utility::conversions::to_utf8string(uri)); - web::http::client::http_client client = GetClient(uri); - web::http::http_request request{ web::http::methods::POST }; - request.headers().set_content_type(web::http::details::mime_types::application_json); - request.set_body(body.serialize()); - - // Add headers - for (auto& pair : headers) - { - 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())); - - // Add auth headers after logging - for (auto& pair : authHeaders) - { - request.headers().add(pair.first, pair.second); - } - - return client.request(request); - } - - std::optional<web::json::value> HttpClientHelper::HandlePost( - const utility::string_t& uri, - const web::json::value& body, - const HttpClientHelper::HttpRequestHeaders& headers, - const HttpClientHelper::HttpRequestHeaders& authHeaders) const - { - web::http::http_response httpResponse; - HttpClientHelper::Post(uri, body, headers, authHeaders).then([&httpResponse](const web::http::http_response& response) - { - httpResponse = response; - }).wait(); - - return ValidateAndExtractResponse(httpResponse); - } - - pplx::task<web::http::http_response> HttpClientHelper::Get( - const utility::string_t& uri, - const HttpClientHelper::HttpRequestHeaders& headers, - const HttpClientHelper::HttpRequestHeaders& authHeaders) const - { - AICLI_LOG(Repo, Info, << "Sending http GET request to: " << utility::conversions::to_utf8string(uri)); - web::http::client::http_client client = GetClient(uri); - web::http::http_request request{ web::http::methods::GET }; - request.headers().set_content_type(web::http::details::mime_types::application_json); - - // Add headers - for (auto& pair : headers) - { - 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())); - - // Add auth headers after logging - for (auto& pair : authHeaders) - { - request.headers().add(pair.first, pair.second); - } - - return client.request(request); - } - - std::optional<web::json::value> HttpClientHelper::HandleGet( - const utility::string_t& uri, - const HttpClientHelper::HttpRequestHeaders& headers, - const HttpClientHelper::HttpRequestHeaders& authHeaders) const - { - web::http::http_response httpResponse; - Get(uri, headers, authHeaders).then([&httpResponse](const web::http::http_response& response) - { - httpResponse = response; - }).wait(); - - return ValidateAndExtractResponse(httpResponse); - } - - void HttpClientHelper::SetPinningConfiguration(const Certificates::PinningConfiguration& configuration) - { - m_clientConfig.set_nativehandle_servercertificate_validation([pinConfig = configuration](web::http::client::native_handle handle) - { - NativeHandleServerCertificateValidation(handle, pinConfig); - }); - } - - web::http::client::http_client HttpClientHelper::GetClient(const utility::string_t& uri) const - { - web::http::client::http_client client{ uri, m_clientConfig }; - - // Add default custom handlers if any. - if (m_defaultRequestHandlerStage) - { - client.add_handler(m_defaultRequestHandlerStage); - } - - return client; - } - - std::optional<web::json::value> HttpClientHelper::ValidateAndExtractResponse(const web::http::http_response& response) const - { - AICLI_LOG(Repo, Info, << "Response status: " << response.status_code()); - // Ensure that we wait for the content to be ready before we log it; otherwise it will be truncated. - AICLI_LOG_LARGE_STRING(Repo, Verbose, << "Response details:", - response.content_ready().then([&](const web::http::http_response&) { return utility::conversions::to_utf8string(response.to_string()); }).get()); - - std::optional<web::json::value> result; - switch (response.status_code()) - { - case web::http::status_codes::OK: - result = ExtractJsonResponse(response); - break; - - case web::http::status_codes::NotFound: - THROW_HR(APPINSTALLER_CLI_ERROR_RESTSOURCE_ENDPOINT_NOT_FOUND); - - case web::http::status_codes::NoContent: - result = {}; - break; - - case web::http::status_codes::BadRequest: - THROW_HR(APPINSTALLER_CLI_ERROR_RESTSOURCE_INTERNAL_ERROR); - - case web::http::status_codes::TooManyRequests: - case web::http::status_codes::ServiceUnavailable: - THROW_EXCEPTION(AppInstaller::Utility::ServiceUnavailableException(GetRetryAfter(response.headers()))); - - default: - THROW_HR(MAKE_HRESULT(SEVERITY_ERROR, FACILITY_HTTP, response.status_code())); - } - - return result; - } - - std::optional<web::json::value> HttpClientHelper::ExtractJsonResponse(const web::http::http_response& response) const - { - utility::string_t contentType = response.headers().content_type(); - - THROW_HR_IF(APPINSTALLER_CLI_ERROR_RESTSOURCE_UNSUPPORTED_MIME_TYPE, - !contentType._Starts_with(web::http::details::mime_types::application_json)); - - return response.extract_json().get(); - } -} diff --git a/src/AppInstallerRepositoryCore/Rest/Schema/HttpClientHelper.h b/src/AppInstallerRepositoryCore/Rest/Schema/HttpClientHelper.h @@ -1,41 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -#pragma once -#include <winget/Certificates.h> - -#include <cpprest/http_client.h> -#include <cpprest/json.h> - -#include <optional> -#include <vector> - -namespace AppInstaller::Repository::Rest::Schema -{ - struct HttpClientHelper - { - using HttpRequestHeaders = std::unordered_map<utility::string_t, utility::string_t>; - - HttpClientHelper(std::shared_ptr<web::http::http_pipeline_stage> = {}); - - pplx::task<web::http::http_response> Post(const utility::string_t& uri, const web::json::value& body, const HttpRequestHeaders& headers = {}, const HttpRequestHeaders& authHeaders = {}) const; - - std::optional<web::json::value> HandlePost(const utility::string_t& uri, const web::json::value& body, const HttpRequestHeaders& headers = {}, const HttpRequestHeaders& authHeaders = {}) const; - - pplx::task<web::http::http_response> Get(const utility::string_t& uri, const HttpRequestHeaders& headers = {}, const HttpRequestHeaders& authHeaders = {}) const; - - std::optional<web::json::value> HandleGet(const utility::string_t& uri, const HttpRequestHeaders& headers = {}, const HttpRequestHeaders& authHeaders = {}) const; - - void SetPinningConfiguration(const Certificates::PinningConfiguration& configuration); - - protected: - std::optional<web::json::value> ValidateAndExtractResponse(const web::http::http_response& response) const; - - std::optional<web::json::value> ExtractJsonResponse(const web::http::http_response& response) const; - - private: - web::http::client::http_client GetClient(const utility::string_t& uri) const; - - std::shared_ptr<web::http::http_pipeline_stage> m_defaultRequestHandlerStage; - web::http::client::http_client_config m_clientConfig; - }; -} diff --git a/src/AppInstallerRepositoryCore/Rest/Schema/InformationResponseDeserializer.h b/src/AppInstallerRepositoryCore/Rest/Schema/InformationResponseDeserializer.h @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once -#include <cpprest/json.h> #include "Rest/Schema/IRestClient.h" namespace AppInstaller::Repository::Rest::Schema diff --git a/src/AppInstallerRepositoryCore/Rest/Schema/RestHelper.cpp b/src/AppInstallerRepositoryCore/Rest/Schema/RestHelper.cpp @@ -1,86 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -#include "pch.h" -#include "RestHelper.h" -#include <winget/JsonUtil.h> -#include "Rest/Schema/CommonRestConstants.h" - -using namespace AppInstaller::Repository::Rest::Schema; - -namespace AppInstaller::Repository::Rest::Schema -{ - utility::string_t RestHelper::GetRestAPIBaseUri(std::string restApiUri) - { - // Trim - std::string uri = restApiUri; - if (!uri.empty()) - { - uri = AppInstaller::Utility::Trim(uri); - - // Remove trailing forward slash - if (uri.back() == '/') - { - uri.pop_back(); - } - } - - // Encode the Uri - return web::uri::encode_uri(JSON::GetUtilityString(uri)); - } - - bool RestHelper::IsValidUri(const utility::string_t& restApiUri) - { - return web::uri::validate(restApiUri); - } - - utility::string_t RestHelper::AppendPathToUri(const utility::string_t& restApiUri, const utility::string_t& path) - { - web::uri_builder builder(restApiUri); - builder.append_path(path, true); - return builder.to_string(); - } - - utility::string_t RestHelper::MakeQueryParam(std::string_view queryName, const std::string& queryValue) - { - std::string queryParam; - queryParam.append(queryName).append("=").append(queryValue); - - return utility::conversions::to_string_t(queryParam); - } - - utility::string_t RestHelper::AppendQueryParamsToUri(const utility::string_t& uri, const std::map<std::string_view, std::string>& queryParameters) - { - web::http::uri_builder builder{ uri }; - - for (auto& pair : queryParameters) - { - builder.append_query(RestHelper::MakeQueryParam(pair.first, pair.second), true); - } - - return builder.to_string(); - } - - std::optional<utility::string_t> RestHelper::GetContinuationToken(const web::json::value& jsonObject) - { - std::optional<std::string> continuationToken = JSON::GetRawStringValueFromJsonNode(jsonObject, JSON::GetUtilityString(ContinuationToken)); - - if (continuationToken) - { - return utility::conversions::to_string_t(continuationToken.value()); - } - - return {}; - } - - std::vector<std::string> RestHelper::GetUniqueItems(const std::vector<std::string>& list) - { - std::set<std::string> set; - for (const auto& item : list) - { - set.emplace(item); - } - - std::vector<std::string> result{ set.begin(), set.end() }; - return result; - } -} diff --git a/src/AppInstallerRepositoryCore/Rest/Schema/RestHelper.h b/src/AppInstallerRepositoryCore/Rest/Schema/RestHelper.h @@ -1,25 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -#pragma once -#include <cpprest/json.h> - -namespace AppInstaller::Repository::Rest::Schema -{ - // Rest source helper. - struct RestHelper - { - static utility::string_t GetRestAPIBaseUri(std::string restApiUri); - - static bool IsValidUri(const utility::string_t& restApiUri); - - static utility::string_t AppendPathToUri(const utility::string_t& restApiUri, const utility::string_t& path); - - static utility::string_t MakeQueryParam(std::string_view queryName, const std::string& queryValue); - - static utility::string_t AppendQueryParamsToUri(const utility::string_t& uri, const std::map<std::string_view, std::string>& queryParameters); - - static std::optional<utility::string_t> GetContinuationToken(const web::json::value& jsonObject); - - static std::vector<std::string> GetUniqueItems(const std::vector<std::string>& list); - }; -} diff --git a/src/AppInstallerRepositoryCore/pch.h b/src/AppInstallerRepositoryCore/pch.h @@ -1,12 +1,11 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once #define NOMINMAX #include <windows.h> #include <urlmon.h> -#include <appmodel.h> -#include <winhttp.h> +#include <appmodel.h> #include <Shlwapi.h> #include <Shlobj.h> #include <msi.h> @@ -54,15 +53,15 @@ #include <unordered_set> #include <utility> -#include <json/json.h> - +#include <json/json.h> + #pragma warning( push ) #pragma warning ( disable : 26495 26439 ) #include <cpprest/http_client.h> #include <cpprest/json.h> #include <cpprest/uri_builder.h> -#pragma warning( pop ) - +#pragma warning( pop ) + #include <AppInstallerDateTime.h> #include <AppInstallerDownloader.h> #include <AppInstallerErrors.h> @@ -78,4 +77,4 @@ #include <winget/Locale.h> #include <winget/Settings.h> #include <winget/UserSettings.h> -#include <winget/Yaml.h>- \ No newline at end of file +#include <winget/Yaml.h>