diff --git a/src/AppInstallerCLI/AppInstallerCLI.vcxproj b/src/AppInstallerCLI/AppInstallerCLI.vcxproj index d818f0e343..52e9f4ab96 100644 --- a/src/AppInstallerCLI/AppInstallerCLI.vcxproj +++ b/src/AppInstallerCLI/AppInstallerCLI.vcxproj @@ -1,6 +1,6 @@ - + true true @@ -247,13 +247,13 @@ - + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - + + - + \ No newline at end of file diff --git a/src/AppInstallerCLI/packages.config b/src/AppInstallerCLI/packages.config index f32f48b009..0495acb040 100644 --- a/src/AppInstallerCLI/packages.config +++ b/src/AppInstallerCLI/packages.config @@ -1,4 +1,4 @@  - + \ No newline at end of file diff --git a/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj b/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj index 9d92771cc6..d14e4c5cac 100644 --- a/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj +++ b/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj @@ -1,6 +1,6 @@ - + true true @@ -480,15 +480,14 @@ - + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - + + - - + \ No newline at end of file diff --git a/src/AppInstallerCLICore/packages.config b/src/AppInstallerCLICore/packages.config index 3a8e0698a3..132d149574 100644 --- a/src/AppInstallerCLICore/packages.config +++ b/src/AppInstallerCLICore/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj index ba0ced5853..4bc7051f23 100644 --- a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj +++ b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj @@ -1,6 +1,6 @@ - + true true @@ -1137,14 +1137,14 @@ - + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - + + - + \ No newline at end of file diff --git a/src/AppInstallerCLITests/packages.config b/src/AppInstallerCLITests/packages.config index 3a8e0698a3..132d149574 100644 --- a/src/AppInstallerCLITests/packages.config +++ b/src/AppInstallerCLITests/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj b/src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj index 302805976d..d63e68789a 100644 --- a/src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj +++ b/src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj @@ -1,6 +1,6 @@ - + true true @@ -466,14 +466,14 @@ - + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - + + - + \ No newline at end of file diff --git a/src/AppInstallerCommonCore/HttpStream/HttpClientWrapper.cpp b/src/AppInstallerCommonCore/HttpStream/HttpClientWrapper.cpp index 1da48efa8c..780fce2247 100644 --- a/src/AppInstallerCommonCore/HttpStream/HttpClientWrapper.cpp +++ b/src/AppInstallerCommonCore/HttpStream/HttpClientWrapper.cpp @@ -1,190 +1,185 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "pch.h" -#include "Public/AppInstallerStrings.h" -#include "HttpClientWrapper.h" -#include "Public/AppInstallerRuntime.h" -#include "Public/AppInstallerDownloader.h" - -using namespace winrt::Windows::Foundation; -using namespace winrt::Windows::Security::Cryptography; -using namespace winrt::Windows::Storage; -using namespace winrt::Windows::Storage::Streams; -using namespace winrt::Windows::Web::Http; -using namespace winrt::Windows::Web::Http::Headers; -using namespace winrt::Windows::Web::Http::Filters; - -// Note: this class is used by the HttpRandomAccessStream which is passed to the AppxPackaging COM API -// All exceptions thrown across dll boundaries should be WinRT exception not custom exceptions. -// The HRESULTs will be mapped to UI error code by the appropriate component -namespace AppInstaller::Utility::HttpStream -{ - std::future> HttpClientWrapper::CreateAsync(const Uri& uri) - { - // TODO: Use proxy info. HttpClient does not support using a custom proxy, only using the system-wide one. - std::shared_ptr instance = std::make_shared(); - - // Use an HTTP filter to disable the default caching behavior and use the Most Recent caching behavior instead - // so we don't use a stale cached resource. Note: this wrapper object is used in the custom HTTP stream implementation - // so this affects the parsing of HTTP-based packages/bundles. - HttpBaseProtocolFilter filter; - filter.CacheControl().ReadBehavior(HttpCacheReadBehavior::MostRecent); - instance->m_httpClient = HttpClient(filter); - instance->m_requestUri = uri; - - instance->m_httpClient.DefaultRequestHeaders().Connection().Clear(); - instance->m_httpClient.DefaultRequestHeaders().Append(L"Connection", L"Keep-Alive"); - instance->m_httpClient.DefaultRequestHeaders().UserAgent().ParseAdd(Utility::ConvertToUTF16(Runtime::GetDefaultUserAgent().get())); - - co_await instance->PopulateInfoAsync(); - - co_return instance; - } - - // this function will issue a HEAD request to determine the size of the file and the redirect URI - std::future HttpClientWrapper::PopulateInfoAsync() - { - HttpRequestMessage request(HttpMethod::Head(), m_requestUri); - - HttpResponseMessage response = co_await m_httpClient.SendRequestAsync(request, HttpCompletionOption::ResponseHeadersRead); - - switch (response.StatusCode()) - { - case HttpStatusCode::Ok: - // All good - break; - case HttpStatusCode::TooManyRequests: - case HttpStatusCode::ServiceUnavailable: - { - THROW_EXCEPTION(ServiceUnavailableException(GetRetryAfter(response))); - } - default: - THROW_HR(MAKE_HRESULT(SEVERITY_ERROR, FACILITY_HTTP, response.StatusCode())); - } - - // Get the length from the response - if (response.Content().Headers().HasKey(L"Content-Length")) - { - std::wstring contentLength(response.Content().Headers().Lookup(L"Content-Length")); - m_sizeInBytes = std::stoll(contentLength); - } - else - { - m_sizeInBytes = 0; - } - - // Get the extension from the redirect URI - m_redirectUri = response.RequestMessage().RequestUri(); - - m_contentType = response.Content().Headers().HasKey(L"Content-Type") ? - response.Content().Headers().Lookup(L"Content-Type") - : L""; - - // If the size wasn't resolved try with a GET 0-0 request - if (m_sizeInBytes == 0) - { - co_await SendHttpRequestAsync(0, 1); - } - } - -#ifdef WINGET_DISABLE_FOR_FUZZING -#pragma warning( push ) -#pragma warning( disable : 4714) // HRESULT_FROM_WIN32 marked as forceinline not inlined -#endif - - std::future HttpClientWrapper::SendHttpRequestAsync( - _In_ ULONG64 startPosition, - _In_ UINT32 requestedSizeInBytes) - { - unsigned long long endPosition = 0; - - winrt::check_hresult(ULong64Add(startPosition, requestedSizeInBytes, &endPosition)); - - // Subtracting one should be safe, as the consumer of the stream should not request - // an empty range, so this number can't go negative. - endPosition -= 1; - - std::wstring rangeHeaderValue = L"bytes=" + std::to_wstring(startPosition) + L"-" + std::to_wstring(endPosition); - - HttpRequestMessage request(HttpMethod::Get(), m_requestUri); - request.Headers().Append(L"Range", rangeHeaderValue); - - if (!Utility::IsEmptyOrWhitespace(m_etagHeader)) - { - request.Headers().Append(L"If-Match", m_etagHeader); - } - - if (!Utility::IsEmptyOrWhitespace(m_lastModifiedHeader)) - { - request.Headers().Append(L"If-Unmodified-Since", m_lastModifiedHeader); - } - - HttpResponseMessage response = co_await m_httpClient.SendRequestAsync(request, HttpCompletionOption::ResponseHeadersRead); - HttpContentHeaderCollection contentHeaders = response.Content().Headers(); - - switch (response.StatusCode()) - { - case HttpStatusCode::Ok: - case HttpStatusCode::PartialContent: - // All good - break; - case HttpStatusCode::TooManyRequests: - case HttpStatusCode::ServiceUnavailable: - { - THROW_EXCEPTION(ServiceUnavailableException(GetRetryAfter(response))); - } - default: - THROW_HR(MAKE_HRESULT(SEVERITY_ERROR, FACILITY_HTTP, response.StatusCode())); - } - - if (response.StatusCode() != HttpStatusCode::PartialContent && startPosition != 0) - { - // throw HRESULT used for range-request error - THROW_HR(HRESULT_FROM_WIN32(ERROR_NO_RANGES_PROCESSED)); - } - - if (response.Headers().HasKey(L"Accept-Ranges") && - Utility::ToLower(std::wstring(response.Headers().Lookup(L"Accept-Ranges"))) == L"none") - { - // throw HRESULT used for range-request error - THROW_HR(HRESULT_FROM_WIN32(ERROR_NO_RANGES_PROCESSED)); - } - - if (Utility::IsEmptyOrWhitespace(m_etagHeader) && response.Headers().HasKey(L"ETag")) - { - m_etagHeader = response.Headers().Lookup(L"ETag"); - } - - if (Utility::IsEmptyOrWhitespace(m_lastModifiedHeader) && contentHeaders.HasKey(L"Last-Modified")) - { - m_lastModifiedHeader = contentHeaders.Lookup(L"Last-Modified"); - } - - // If we don't know the size, parse it from the Content-Range field. - if (m_sizeInBytes == 0 && contentHeaders.HasKey(L"Content-Range")) - { - // format: a-b/x where x is either a number or * - std::wstring contentRange(contentHeaders.Lookup(L"Content-Range")); - std::wstring length = contentRange.substr(contentRange.find(L"/") + 1); - m_sizeInBytes = (length == L"*") ? 0 : std::stoll(length); - } - - co_return co_await response.Content().ReadAsBufferAsync(); - } - -#ifdef WINGET_DISABLE_FOR_FUZZING -#pragma warning( pop ) -#endif - - std::future HttpClientWrapper::DownloadRangeAsync( - const ULONG64 startPosition, - const UINT32 requestedSizeInBytes, - const InputStreamOptions& options) - { - std::vector byteArray(requestedSizeInBytes); - IBuffer buffer = CryptographicBuffer::CreateFromByteArray(byteArray); - - co_return co_await SendHttpRequestAsync(startPosition, requestedSizeInBytes); - } -} \ No newline at end of file +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "pch.h" +#include "Public/AppInstallerStrings.h" +#include "HttpClientWrapper.h" +#include "Public/AppInstallerRuntime.h" +#include "Public/AppInstallerDownloader.h" + +using namespace winrt::Windows::Foundation; +using namespace winrt::Windows::Security::Cryptography; +using namespace winrt::Windows::Storage; +using namespace winrt::Windows::Storage::Streams; +using namespace winrt::Windows::Web::Http; +using namespace winrt::Windows::Web::Http::Headers; +using namespace winrt::Windows::Web::Http::Filters; + +// Note: this class is used by the HttpRandomAccessStream which is passed to the AppxPackaging COM API +// All exceptions thrown across dll boundaries should be WinRT exception not custom exceptions. +// The HRESULTs will be mapped to UI error code by the appropriate component +namespace AppInstaller::Utility::HttpStream +{ + std::shared_ptr HttpClientWrapper::Create(const Uri& uri) + { + // TODO: Use proxy info. HttpClient does not support using a custom proxy, only using the system-wide one. + std::shared_ptr instance = std::make_shared(); + + // Use an HTTP filter to disable the default caching behavior and use the Most Recent caching behavior instead + // so we don't use a stale cached resource. Note: this wrapper object is used in the custom HTTP stream implementation + // so this affects the parsing of HTTP-based packages/bundles. + HttpBaseProtocolFilter filter; + filter.CacheControl().ReadBehavior(HttpCacheReadBehavior::MostRecent); + instance->m_httpClient = HttpClient(filter); + instance->m_requestUri = uri; + + instance->m_httpClient.DefaultRequestHeaders().Connection().Clear(); + instance->m_httpClient.DefaultRequestHeaders().Append(L"Connection", L"Keep-Alive"); + instance->m_httpClient.DefaultRequestHeaders().UserAgent().ParseAdd(Utility::ConvertToUTF16(Runtime::GetDefaultUserAgent().get())); + + return instance; + } + + // this function will issue a HEAD request to determine the size of the file and the redirect URI + IAsyncAction HttpClientWrapper::PopulateInfoAsync() + { + HttpRequestMessage request(HttpMethod::Head(), m_requestUri); + + HttpResponseMessage response = co_await m_httpClient.SendRequestAsync(request, HttpCompletionOption::ResponseHeadersRead); + + switch (response.StatusCode()) + { + case HttpStatusCode::Ok: + // All good + break; + case HttpStatusCode::TooManyRequests: + case HttpStatusCode::ServiceUnavailable: + { + THROW_EXCEPTION(ServiceUnavailableException(GetRetryAfter(response))); + } + default: + THROW_HR(MAKE_HRESULT(SEVERITY_ERROR, FACILITY_HTTP, response.StatusCode())); + } + + // Get the length from the response + if (response.Content().Headers().HasKey(L"Content-Length")) + { + std::wstring contentLength(response.Content().Headers().Lookup(L"Content-Length")); + m_sizeInBytes = std::stoll(contentLength); + } + else + { + m_sizeInBytes = 0; + } + + // Get the extension from the redirect URI + m_redirectUri = response.RequestMessage().RequestUri(); + + m_contentType = response.Content().Headers().HasKey(L"Content-Type") ? + response.Content().Headers().Lookup(L"Content-Type") + : L""; + + // If the size wasn't resolved try with a GET 0-0 request + if (m_sizeInBytes == 0) + { + co_await SendHttpRequestAsync(0, 1); + } + } + +#ifdef WINGET_DISABLE_FOR_FUZZING +#pragma warning( push ) +#pragma warning( disable : 4714) // HRESULT_FROM_WIN32 marked as forceinline not inlined +#endif + + IAsyncOperation HttpClientWrapper::SendHttpRequestAsync( + _In_ ULONG64 startPosition, + _In_ UINT32 requestedSizeInBytes) + { + unsigned long long endPosition = 0; + + winrt::check_hresult(ULong64Add(startPosition, requestedSizeInBytes, &endPosition)); + + // Subtracting one should be safe, as the consumer of the stream should not request + // an empty range, so this number can't go negative. + endPosition -= 1; + + std::wstring rangeHeaderValue = L"bytes=" + std::to_wstring(startPosition) + L"-" + std::to_wstring(endPosition); + + HttpRequestMessage request(HttpMethod::Get(), m_requestUri); + request.Headers().Append(L"Range", rangeHeaderValue); + + if (!Utility::IsEmptyOrWhitespace(m_etagHeader)) + { + request.Headers().Append(L"If-Match", m_etagHeader); + } + + if (!Utility::IsEmptyOrWhitespace(m_lastModifiedHeader)) + { + request.Headers().Append(L"If-Unmodified-Since", m_lastModifiedHeader); + } + + HttpResponseMessage response = co_await m_httpClient.SendRequestAsync(request, HttpCompletionOption::ResponseHeadersRead); + HttpContentHeaderCollection contentHeaders = response.Content().Headers(); + + switch (response.StatusCode()) + { + case HttpStatusCode::Ok: + case HttpStatusCode::PartialContent: + // All good + break; + case HttpStatusCode::TooManyRequests: + case HttpStatusCode::ServiceUnavailable: + { + THROW_EXCEPTION(ServiceUnavailableException(GetRetryAfter(response))); + } + default: + THROW_HR(MAKE_HRESULT(SEVERITY_ERROR, FACILITY_HTTP, response.StatusCode())); + } + + if (response.StatusCode() != HttpStatusCode::PartialContent && startPosition != 0) + { + // throw HRESULT used for range-request error + THROW_HR(HRESULT_FROM_WIN32(ERROR_NO_RANGES_PROCESSED)); + } + + if (response.Headers().HasKey(L"Accept-Ranges") && + Utility::ToLower(std::wstring(response.Headers().Lookup(L"Accept-Ranges"))) == L"none") + { + // throw HRESULT used for range-request error + THROW_HR(HRESULT_FROM_WIN32(ERROR_NO_RANGES_PROCESSED)); + } + + if (Utility::IsEmptyOrWhitespace(m_etagHeader) && response.Headers().HasKey(L"ETag")) + { + m_etagHeader = response.Headers().Lookup(L"ETag"); + } + + if (Utility::IsEmptyOrWhitespace(m_lastModifiedHeader) && contentHeaders.HasKey(L"Last-Modified")) + { + m_lastModifiedHeader = contentHeaders.Lookup(L"Last-Modified"); + } + + // If we don't know the size, parse it from the Content-Range field. + if (m_sizeInBytes == 0 && contentHeaders.HasKey(L"Content-Range")) + { + // format: a-b/x where x is either a number or * + std::wstring contentRange(contentHeaders.Lookup(L"Content-Range")); + std::wstring length = contentRange.substr(contentRange.find(L"/") + 1); + m_sizeInBytes = (length == L"*") ? 0 : std::stoll(length); + } + + co_return co_await response.Content().ReadAsBufferAsync(); + } + +#ifdef WINGET_DISABLE_FOR_FUZZING +#pragma warning( pop ) +#endif + + IAsyncOperation HttpClientWrapper::DownloadRangeAsync( + const ULONG64 startPosition, + const UINT32 requestedSizeInBytes, + const InputStreamOptions&) + { + return SendHttpRequestAsync(startPosition, requestedSizeInBytes); + } +} diff --git a/src/AppInstallerCommonCore/HttpStream/HttpClientWrapper.h b/src/AppInstallerCommonCore/HttpStream/HttpClientWrapper.h index 9924c48e19..c9d3f92684 100644 --- a/src/AppInstallerCommonCore/HttpStream/HttpClientWrapper.h +++ b/src/AppInstallerCommonCore/HttpStream/HttpClientWrapper.h @@ -1,51 +1,52 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -#pragma once -#include -#include - -namespace AppInstaller::Utility::HttpStream -{ - // Wrapper around HTTP client. When created, an object of this class will send a HTTP - // head request to determine the size of the data source. - class HttpClientWrapper - { - public: - static std::future> CreateAsync(const winrt::Windows::Foundation::Uri& uri); - - std::future DownloadRangeAsync( - const ULONG64 startPosition, - const UINT32 requestedSizeInBytes, - const winrt::Windows::Storage::Streams::InputStreamOptions& options); - - unsigned long long GetFullFileSize() - { - return m_sizeInBytes; - } - - winrt::Windows::Foundation::Uri GetRedirectUri() - { - return m_redirectUri; - } - - std::wstring GetContentType() - { - return m_contentType; - } - - private: - winrt::Windows::Web::Http::HttpClient m_httpClient; - winrt::Windows::Foundation::Uri m_requestUri = nullptr; - winrt::Windows::Foundation::Uri m_redirectUri = nullptr; - std::wstring m_contentType; - unsigned long long m_sizeInBytes = 0; - std::wstring m_etagHeader; - std::wstring m_lastModifiedHeader; - - std::future PopulateInfoAsync(); - - std::future SendHttpRequestAsync( - _In_ ULONG64 startPosition, - _In_ UINT32 requestedSizeInBytes); - }; +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include +#include + +namespace AppInstaller::Utility::HttpStream +{ + // Wrapper around HTTP client. When created, an object of this class will send a HTTP + // head request to determine the size of the data source. + class HttpClientWrapper + { + public: + // Create returns an uninitialized wrapper. Callers must co_await PopulateInfoAsync() before use. + static std::shared_ptr Create(const winrt::Windows::Foundation::Uri& uri); + + winrt::Windows::Foundation::IAsyncAction PopulateInfoAsync(); + + winrt::Windows::Foundation::IAsyncOperation DownloadRangeAsync( + const ULONG64 startPosition, + const UINT32 requestedSizeInBytes, + const winrt::Windows::Storage::Streams::InputStreamOptions& options); + + unsigned long long GetFullFileSize() + { + return m_sizeInBytes; + } + + winrt::Windows::Foundation::Uri GetRedirectUri() + { + return m_redirectUri; + } + + std::wstring GetContentType() + { + return m_contentType; + } + + private: + winrt::Windows::Web::Http::HttpClient m_httpClient; + winrt::Windows::Foundation::Uri m_requestUri = nullptr; + winrt::Windows::Foundation::Uri m_redirectUri = nullptr; + std::wstring m_contentType; + unsigned long long m_sizeInBytes = 0; + std::wstring m_etagHeader; + std::wstring m_lastModifiedHeader; + + winrt::Windows::Foundation::IAsyncOperation SendHttpRequestAsync( + _In_ ULONG64 startPosition, + _In_ UINT32 requestedSizeInBytes); + }; } \ No newline at end of file diff --git a/src/AppInstallerCommonCore/HttpStream/HttpLocalCache.cpp b/src/AppInstallerCommonCore/HttpStream/HttpLocalCache.cpp index c8fb2fba63..9b87e4e42f 100644 --- a/src/AppInstallerCommonCore/HttpStream/HttpLocalCache.cpp +++ b/src/AppInstallerCommonCore/HttpStream/HttpLocalCache.cpp @@ -1,246 +1,246 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "pch.h" -#include "HttpLocalCache.h" - -using namespace Windows::Storage::Streams; -using namespace winrt::Windows::Storage::Streams; -using namespace winrt::Windows::Security::Cryptography; - -// Note: this class is used by the HttpRandomAccessStream which is passed to the AppxPackaging COM API -// All exceptions thrown across dll boundaries should be WinRT exception not custom exceptions. -// The HRESULTs will be mapped to UI error code by the appropriate component -namespace AppInstaller::Utility::HttpStream -{ - std::future HttpLocalCache::ReadFromCacheAndDownloadIfNecessaryAsync( - const ULONG64 requestedPosition, - const UINT32 requestedSize, - HttpClientWrapper* httpClientWrapper, - InputStreamOptions httpInputStreamOptions) - { - // Increment cache access counter user for implementing LRU replacement - m_accessCounter++; - - // Find all the pages for the given request, and the pages that are missing - std::vector allPages; - std::vector unsatisfiablePages; - FindCachePages(requestedPosition, requestedSize, allPages, unsatisfiablePages); - - // download the missing pages - co_await DownloadAndSaveToCacheAsync( - unsatisfiablePages, - httpClientWrapper, - httpInputStreamOptions); - - // At this point, everything should be in the cache - IBuffer constructedBuffer = {}; - - for (UINT32 i = 0; i < allPages.size(); i++) - { - UINT64 pageOffset = allPages[i]; - IBuffer cachedPageBuffer = ReadPageFromCache(pageOffset); - constructedBuffer = ConcatenateBuffers(constructedBuffer, cachedPageBuffer); - } - - // trim buffer to match requested range - IBuffer requestedBuffer = TrimBufferToSatisfyRequest( - constructedBuffer, - requestedPosition, - requestedSize, - allPages); - - VacateStaleEntriesFromCache(); - - co_return requestedBuffer; - } - - void HttpLocalCache::FindCachePages( - ULONG64 requestedPosition, - UINT32 requestedSize, - std::vector& allPages, - std::vector& unsatisfiablePages) - { - ULONG64 requestedEndPosition; - ULONG64 currentPageOffset; - winrt::check_hresult(ULong64Add(requestedPosition, requestedSize, &requestedEndPosition)); - winrt::check_hresult(ULong64Mult((requestedPosition / PAGE_SIZE), PAGE_SIZE, ¤tPageOffset)); - - // There's always at least one page for the range - do - { - allPages.push_back(currentPageOffset); - - if (m_localCache.find(currentPageOffset) == m_localCache.end()) - { - unsatisfiablePages.push_back(currentPageOffset); - } - - winrt::check_hresult(ULong64Add(currentPageOffset, PAGE_SIZE, ¤tPageOffset)); - - } while (currentPageOffset < requestedEndPosition); - } - - // Breaks the provided buffer into smaller buffers and saves them to the cache at the corresponding - // page offset position, starting at firstPageOffset. The smaller buffers are all PAGE_SIZE bytes, - // except for the one corresponding to the last page in the file - void HttpLocalCache::SaveBufferToCache(const IBuffer& buffer, const ULONG64 firstPageOffset) - { - UINT32 remainingBufferSize = buffer.Length(); - UINT32 currentBufferIndex = 0; - ULONG64 currentPageOffset = firstPageOffset; - - while (remainingBufferSize > 0) - { - // Extract the sub-buffer - UINT32 currentPageSize = std::min(remainingBufferSize, PAGE_SIZE); - IBuffer currentPageBuffer = CreateTrimmedBuffer(buffer, currentBufferIndex, currentPageSize); - - // Add it to the cache - CachedPage currentPage; - currentPage.lastAccessCounter = m_accessCounter; - currentPage.buffer = currentPageBuffer; - m_localCache[currentPageOffset] = currentPage; - - // update loop vars - winrt::check_hresult(UInt32Sub(remainingBufferSize, currentPageSize, &remainingBufferSize)); - winrt::check_hresult(UInt32Add(currentBufferIndex, currentPageSize, ¤tBufferIndex)); - winrt::check_hresult(ULong64Add(currentPageOffset, PAGE_SIZE, ¤tPageOffset)); - } - } - - IBuffer HttpLocalCache::ReadPageFromCache(const ULONG64 pageOffset) - { - if (!(m_localCache.find(pageOffset) != m_localCache.end())) - { - THROW_HR(E_INVALIDARG); - } - - CachedPage& page = m_localCache[pageOffset]; - page.lastAccessCounter = m_accessCounter; - - return page.buffer; - } - - // Trims a buffer that was constructed (by fetching pages from cache and downloading missing pages) - // in order to satisfy a request and return the exact buffer the consumer asked for. - IBuffer HttpLocalCache::TrimBufferToSatisfyRequest( - const IBuffer& constructedBuffer, - const ULONG64 requestedPosition, - const UINT32 requestedSize, - const std::vector allPages) - { - ULONG64 fullBufferStartOffset = allPages[0]; - - ULONG64 trimmedBufferStartRelativeIndex; - winrt::check_hresult(ULong64Sub(requestedPosition, fullBufferStartOffset, &trimmedBufferStartRelativeIndex)); - - IBuffer requestedBuffer = CreateTrimmedBuffer( - constructedBuffer, - (UINT32)trimmedBufferStartRelativeIndex, // Conversion is safe as buffer size is a UINT32. - requestedSize); - - return requestedBuffer; - } - - // Downloads a chunk of the file, saves it to the cache, and returns the corresponding buffer - // If the requested size is 0, this method returns an empty buffer without making HTTP calls - std::future HttpLocalCache::DownloadAndSaveToCacheAsync( - const std::vector unsatisfiablePages, - HttpClientWrapper* httpClientWrapper, - InputStreamOptions httpInputStreamOptions) - { - // Determine the download job - // To make things easy, we will download the contiguous range that includes all the unsatisfiable ranges. - // Note that in theory, this may include cached pages. However, this situation is rarely expected to happen, - // if at all. The package reader usually reads things in chunks of 64 KB or less, so, we should expect to - // always have up to two satisfiable and unsatisfiable pages in total. - UINT64 fileSize = httpClientWrapper->GetFullFileSize(); - ULONG64 downloadJobStartPosition = 0U; - ULONG64 downloadJobEndPosition = 0U; - ULONG64 downloadJobSize = 0U; - if (unsatisfiablePages.size() > 0U) - { - downloadJobStartPosition = unsatisfiablePages[0]; - ULONG64 lastUnsatisfiableJob = unsatisfiablePages[unsatisfiablePages.size() - 1]; - winrt::check_hresult(ULong64Add(lastUnsatisfiableJob, PAGE_SIZE, &downloadJobEndPosition)); - - // make sure to not overflow file size - downloadJobEndPosition = std::min(downloadJobEndPosition, fileSize); - winrt::check_hresult(ULong64Sub(downloadJobEndPosition, downloadJobStartPosition, &downloadJobSize)); - } - - if (downloadJobSize != 0U) - { - // start download job - IBuffer downloadedBuffer = co_await httpClientWrapper->DownloadRangeAsync( - downloadJobStartPosition, - (UINT32)downloadJobSize, - httpInputStreamOptions); - - SaveBufferToCache(downloadedBuffer, downloadJobStartPosition); - } - } - - void HttpLocalCache::VacateStaleEntriesFromCache() - { - // Copy page offsets into vector and sort by the access counter - std::vector> orderedPageOffsets; - for (auto pageIter = m_localCache.begin(); pageIter != m_localCache.end(); pageIter++) - { - orderedPageOffsets.push_back(std::pair(pageIter->first, pageIter->second.lastAccessCounter)); - } - - // Compare function to sort by access counter - auto cmp = [](std::pair const & a, std::pair const & b) - { - return a.second != b.second ? a.second < b.second : a.first < b.first; - }; - - std::sort(orderedPageOffsets.begin(), orderedPageOffsets.end(), cmp); - - for (auto pageIter = orderedPageOffsets.begin(); pageIter != orderedPageOffsets.end(); pageIter++) - { - if (m_localCache.size() > MAX_PAGES) - { - m_localCache.erase(pageIter->first); - } - else - { - break; - } - } - } - - IBuffer HttpLocalCache::CreateTrimmedBuffer( - const IBuffer& originalBuffer, - UINT32 trimStartIndex, - UINT32 size) - { - uint32_t bufferLength = originalBuffer.Length(); - THROW_HR_IF(E_INVALIDARG, trimStartIndex > bufferLength); - - originalBuffer.as<::IInspectable>(); - - // Get the byte array from the IBuffer object - Microsoft::WRL::ComPtr bufferByteAccess; - ::IInspectable* bufferAbi = (::IInspectable*)winrt::get_abi(originalBuffer); - bufferAbi->QueryInterface(IID_PPV_ARGS(&bufferByteAccess)); - byte* byteBuffer = nullptr; - bufferByteAccess->Buffer(&byteBuffer); - - // Create the array of bytes holding the trimmed bytes - IBuffer trimmedBuffer = CryptographicBuffer::CreateFromByteArray( - { byteBuffer + trimStartIndex, std::min(size, bufferLength - trimStartIndex) }); - - return trimmedBuffer; - } - - IBuffer HttpLocalCache::ConcatenateBuffers(const IBuffer& buffer1, const IBuffer& buffer2) - { - DataWriter writer; - writer.WriteBuffer(buffer1); - writer.WriteBuffer(buffer2); - return writer.DetachBuffer(); - } -} \ No newline at end of file +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "pch.h" +#include "HttpLocalCache.h" + +using namespace Windows::Storage::Streams; +using namespace winrt::Windows::Storage::Streams; +using namespace winrt::Windows::Security::Cryptography; + +// Note: this class is used by the HttpRandomAccessStream which is passed to the AppxPackaging COM API +// All exceptions thrown across dll boundaries should be WinRT exception not custom exceptions. +// The HRESULTs will be mapped to UI error code by the appropriate component +namespace AppInstaller::Utility::HttpStream +{ + winrt::Windows::Foundation::IAsyncOperation HttpLocalCache::ReadFromCacheAndDownloadIfNecessaryAsync( + const ULONG64 requestedPosition, + const UINT32 requestedSize, + HttpClientWrapper* httpClientWrapper, + InputStreamOptions httpInputStreamOptions) + { + // Increment cache access counter user for implementing LRU replacement + m_accessCounter++; + + // Find all the pages for the given request, and the pages that are missing + std::vector allPages; + std::vector unsatisfiablePages; + FindCachePages(requestedPosition, requestedSize, allPages, unsatisfiablePages); + + // download the missing pages + co_await DownloadAndSaveToCacheAsync( + unsatisfiablePages, + httpClientWrapper, + httpInputStreamOptions); + + // At this point, everything should be in the cache + IBuffer constructedBuffer = {}; + + for (UINT32 i = 0; i < allPages.size(); i++) + { + UINT64 pageOffset = allPages[i]; + IBuffer cachedPageBuffer = ReadPageFromCache(pageOffset); + constructedBuffer = ConcatenateBuffers(constructedBuffer, cachedPageBuffer); + } + + // trim buffer to match requested range + IBuffer requestedBuffer = TrimBufferToSatisfyRequest( + constructedBuffer, + requestedPosition, + requestedSize, + allPages); + + VacateStaleEntriesFromCache(); + + co_return requestedBuffer; + } + + void HttpLocalCache::FindCachePages( + ULONG64 requestedPosition, + UINT32 requestedSize, + std::vector& allPages, + std::vector& unsatisfiablePages) + { + ULONG64 requestedEndPosition; + ULONG64 currentPageOffset; + winrt::check_hresult(ULong64Add(requestedPosition, requestedSize, &requestedEndPosition)); + winrt::check_hresult(ULong64Mult((requestedPosition / PAGE_SIZE), PAGE_SIZE, ¤tPageOffset)); + + // There's always at least one page for the range + do + { + allPages.push_back(currentPageOffset); + + if (m_localCache.find(currentPageOffset) == m_localCache.end()) + { + unsatisfiablePages.push_back(currentPageOffset); + } + + winrt::check_hresult(ULong64Add(currentPageOffset, PAGE_SIZE, ¤tPageOffset)); + + } while (currentPageOffset < requestedEndPosition); + } + + // Breaks the provided buffer into smaller buffers and saves them to the cache at the corresponding + // page offset position, starting at firstPageOffset. The smaller buffers are all PAGE_SIZE bytes, + // except for the one corresponding to the last page in the file + void HttpLocalCache::SaveBufferToCache(const IBuffer& buffer, const ULONG64 firstPageOffset) + { + UINT32 remainingBufferSize = buffer.Length(); + UINT32 currentBufferIndex = 0; + ULONG64 currentPageOffset = firstPageOffset; + + while (remainingBufferSize > 0) + { + // Extract the sub-buffer + UINT32 currentPageSize = std::min(remainingBufferSize, PAGE_SIZE); + IBuffer currentPageBuffer = CreateTrimmedBuffer(buffer, currentBufferIndex, currentPageSize); + + // Add it to the cache + CachedPage currentPage; + currentPage.lastAccessCounter = m_accessCounter; + currentPage.buffer = currentPageBuffer; + m_localCache[currentPageOffset] = currentPage; + + // update loop vars + winrt::check_hresult(UInt32Sub(remainingBufferSize, currentPageSize, &remainingBufferSize)); + winrt::check_hresult(UInt32Add(currentBufferIndex, currentPageSize, ¤tBufferIndex)); + winrt::check_hresult(ULong64Add(currentPageOffset, PAGE_SIZE, ¤tPageOffset)); + } + } + + IBuffer HttpLocalCache::ReadPageFromCache(const ULONG64 pageOffset) + { + if (!(m_localCache.find(pageOffset) != m_localCache.end())) + { + THROW_HR(E_INVALIDARG); + } + + CachedPage& page = m_localCache[pageOffset]; + page.lastAccessCounter = m_accessCounter; + + return page.buffer; + } + + // Trims a buffer that was constructed (by fetching pages from cache and downloading missing pages) + // in order to satisfy a request and return the exact buffer the consumer asked for. + IBuffer HttpLocalCache::TrimBufferToSatisfyRequest( + const IBuffer& constructedBuffer, + const ULONG64 requestedPosition, + const UINT32 requestedSize, + const std::vector allPages) + { + ULONG64 fullBufferStartOffset = allPages[0]; + + ULONG64 trimmedBufferStartRelativeIndex; + winrt::check_hresult(ULong64Sub(requestedPosition, fullBufferStartOffset, &trimmedBufferStartRelativeIndex)); + + IBuffer requestedBuffer = CreateTrimmedBuffer( + constructedBuffer, + (UINT32)trimmedBufferStartRelativeIndex, // Conversion is safe as buffer size is a UINT32. + requestedSize); + + return requestedBuffer; + } + + // Downloads a chunk of the file, saves it to the cache, and returns the corresponding buffer + // If the requested size is 0, this method returns an empty buffer without making HTTP calls + winrt::Windows::Foundation::IAsyncAction HttpLocalCache::DownloadAndSaveToCacheAsync( + const std::vector unsatisfiablePages, + HttpClientWrapper* httpClientWrapper, + InputStreamOptions httpInputStreamOptions) + { + // Determine the download job + // To make things easy, we will download the contiguous range that includes all the unsatisfiable ranges. + // Note that in theory, this may include cached pages. However, this situation is rarely expected to happen, + // if at all. The package reader usually reads things in chunks of 64 KB or less, so, we should expect to + // always have up to two satisfiable and unsatisfiable pages in total. + UINT64 fileSize = httpClientWrapper->GetFullFileSize(); + ULONG64 downloadJobStartPosition = 0U; + ULONG64 downloadJobEndPosition = 0U; + ULONG64 downloadJobSize = 0U; + if (unsatisfiablePages.size() > 0U) + { + downloadJobStartPosition = unsatisfiablePages[0]; + ULONG64 lastUnsatisfiableJob = unsatisfiablePages[unsatisfiablePages.size() - 1]; + winrt::check_hresult(ULong64Add(lastUnsatisfiableJob, PAGE_SIZE, &downloadJobEndPosition)); + + // make sure to not overflow file size + downloadJobEndPosition = std::min(downloadJobEndPosition, fileSize); + winrt::check_hresult(ULong64Sub(downloadJobEndPosition, downloadJobStartPosition, &downloadJobSize)); + } + + if (downloadJobSize != 0U) + { + // start download job + IBuffer downloadedBuffer = co_await httpClientWrapper->DownloadRangeAsync( + downloadJobStartPosition, + (UINT32)downloadJobSize, + httpInputStreamOptions); + + SaveBufferToCache(downloadedBuffer, downloadJobStartPosition); + } + } + + void HttpLocalCache::VacateStaleEntriesFromCache() + { + // Copy page offsets into vector and sort by the access counter + std::vector> orderedPageOffsets; + for (auto pageIter = m_localCache.begin(); pageIter != m_localCache.end(); pageIter++) + { + orderedPageOffsets.push_back(std::pair(pageIter->first, pageIter->second.lastAccessCounter)); + } + + // Compare function to sort by access counter + auto cmp = [](std::pair const & a, std::pair const & b) + { + return a.second != b.second ? a.second < b.second : a.first < b.first; + }; + + std::sort(orderedPageOffsets.begin(), orderedPageOffsets.end(), cmp); + + for (auto pageIter = orderedPageOffsets.begin(); pageIter != orderedPageOffsets.end(); pageIter++) + { + if (m_localCache.size() > MAX_PAGES) + { + m_localCache.erase(pageIter->first); + } + else + { + break; + } + } + } + + IBuffer HttpLocalCache::CreateTrimmedBuffer( + const IBuffer& originalBuffer, + UINT32 trimStartIndex, + UINT32 size) + { + uint32_t bufferLength = originalBuffer.Length(); + THROW_HR_IF(E_INVALIDARG, trimStartIndex > bufferLength); + + originalBuffer.as<::IInspectable>(); + + // Get the byte array from the IBuffer object + Microsoft::WRL::ComPtr bufferByteAccess; + ::IInspectable* bufferAbi = (::IInspectable*)winrt::get_abi(originalBuffer); + bufferAbi->QueryInterface(IID_PPV_ARGS(&bufferByteAccess)); + byte* byteBuffer = nullptr; + bufferByteAccess->Buffer(&byteBuffer); + + // Create the array of bytes holding the trimmed bytes + IBuffer trimmedBuffer = CryptographicBuffer::CreateFromByteArray( + { byteBuffer + trimStartIndex, std::min(size, bufferLength - trimStartIndex) }); + + return trimmedBuffer; + } + + IBuffer HttpLocalCache::ConcatenateBuffers(const IBuffer& buffer1, const IBuffer& buffer2) + { + DataWriter writer; + writer.WriteBuffer(buffer1); + writer.WriteBuffer(buffer2); + return writer.DetachBuffer(); + } +} diff --git a/src/AppInstallerCommonCore/HttpStream/HttpLocalCache.h b/src/AppInstallerCommonCore/HttpStream/HttpLocalCache.h index 97b04ff8e5..6338a86661 100644 --- a/src/AppInstallerCommonCore/HttpStream/HttpLocalCache.h +++ b/src/AppInstallerCommonCore/HttpStream/HttpLocalCache.h @@ -1,70 +1,71 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -#include "HttpClientWrapper.h" - -namespace AppInstaller::Utility::HttpStream -{ - // Represents an entry in the cache. - struct CachedPage - { - int lastAccessCounter = 0; - winrt::Windows::Storage::Streams::IBuffer buffer; - }; - - // A cache used internally by the custom HttpRandomAccessStream to reduce round-trips - class HttpLocalCache - { - public: - static constexpr UINT32 PAGE_SIZE = 2 << 16; // each entry in the cache is 64 KB - static constexpr UINT32 MAX_PAGES = 200; // cache size capped at 12.5 MB (200 * 64KB) - - // Returns a buffer matching the requested range by reading the parts of the range that are cached - // and downloading the rest using the provided httpClientWrapper object - std::future ReadFromCacheAndDownloadIfNecessaryAsync( - const ULONG64 requestedPosition, - const UINT32 requestedSize, - HttpClientWrapper* httpClientWrapper, - winrt::Windows::Storage::Streams::InputStreamOptions httpInputStreamOptions); - - private: - std::map m_localCache; - UINT32 m_accessCounter = 0U; - - // Returns a vector of all pages corresponding to a range, and another (subset) - // vector of the pages missing from the cache. - void FindCachePages( - const ULONG64 requestedPosition, - const UINT32 requestedSize, - std::vector& allPages, - std::vector& unsatisfiablePages); - - void SaveBufferToCache(const winrt::Windows::Storage::Streams::IBuffer& buffer, const ULONG64 firstPageOffset); - - winrt::Windows::Storage::Streams::IBuffer ReadPageFromCache(const ULONG64 pageOffset); - - void VacateStaleEntriesFromCache(); - - std::future DownloadAndSaveToCacheAsync( - const std::vector unsatisfiablePages, - HttpClientWrapper* httpClientWrapper, - const winrt::Windows::Storage::Streams::InputStreamOptions httpInputStreamOptions); - - winrt::Windows::Storage::Streams::IBuffer TrimBufferToSatisfyRequest( - const winrt::Windows::Storage::Streams::IBuffer& constructedBuffer, - const ULONG64 requestedPosition, - const UINT32 requestedSize, - const std::vector allPages); - - winrt::Windows::Storage::Streams::IBuffer CreateTrimmedBuffer( - const winrt::Windows::Storage::Streams::IBuffer& originalBuffer, - UINT32 trimStartIndex, - UINT32 size); - - winrt::Windows::Storage::Streams::IBuffer ConcatenateBuffers( - const winrt::Windows::Storage::Streams::IBuffer& buffer1, - const winrt::Windows::Storage::Streams::IBuffer& buffer2); - }; +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#pragma once + +#include +#include "HttpClientWrapper.h" + +namespace AppInstaller::Utility::HttpStream +{ + // Represents an entry in the cache. + struct CachedPage + { + int lastAccessCounter = 0; + winrt::Windows::Storage::Streams::IBuffer buffer; + }; + + // A cache used internally by the custom HttpRandomAccessStream to reduce round-trips + class HttpLocalCache + { + public: + static constexpr UINT32 PAGE_SIZE = 2 << 16; // each entry in the cache is 64 KB + static constexpr UINT32 MAX_PAGES = 200; // cache size capped at 12.5 MB (200 * 64KB) + + // Returns a buffer matching the requested range by reading the parts of the range that are cached + // and downloading the rest using the provided httpClientWrapper object + winrt::Windows::Foundation::IAsyncOperation ReadFromCacheAndDownloadIfNecessaryAsync( + const ULONG64 requestedPosition, + const UINT32 requestedSize, + HttpClientWrapper* httpClientWrapper, + winrt::Windows::Storage::Streams::InputStreamOptions httpInputStreamOptions); + + private: + std::map m_localCache; + UINT32 m_accessCounter = 0U; + + // Returns a vector of all pages corresponding to a range, and another (subset) + // vector of the pages missing from the cache. + void FindCachePages( + const ULONG64 requestedPosition, + const UINT32 requestedSize, + std::vector& allPages, + std::vector& unsatisfiablePages); + + void SaveBufferToCache(const winrt::Windows::Storage::Streams::IBuffer& buffer, const ULONG64 firstPageOffset); + + winrt::Windows::Storage::Streams::IBuffer ReadPageFromCache(const ULONG64 pageOffset); + + void VacateStaleEntriesFromCache(); + + winrt::Windows::Foundation::IAsyncAction DownloadAndSaveToCacheAsync( + const std::vector unsatisfiablePages, + HttpClientWrapper* httpClientWrapper, + const winrt::Windows::Storage::Streams::InputStreamOptions httpInputStreamOptions); + + winrt::Windows::Storage::Streams::IBuffer TrimBufferToSatisfyRequest( + const winrt::Windows::Storage::Streams::IBuffer& constructedBuffer, + const ULONG64 requestedPosition, + const UINT32 requestedSize, + const std::vector allPages); + + winrt::Windows::Storage::Streams::IBuffer CreateTrimmedBuffer( + const winrt::Windows::Storage::Streams::IBuffer& originalBuffer, + UINT32 trimStartIndex, + UINT32 size); + + winrt::Windows::Storage::Streams::IBuffer ConcatenateBuffers( + const winrt::Windows::Storage::Streams::IBuffer& buffer1, + const winrt::Windows::Storage::Streams::IBuffer& buffer2); + }; } \ No newline at end of file diff --git a/src/AppInstallerCommonCore/HttpStream/HttpRandomAccessStream.cpp b/src/AppInstallerCommonCore/HttpStream/HttpRandomAccessStream.cpp index 1e4264996f..f352618e2e 100644 --- a/src/AppInstallerCommonCore/HttpStream/HttpRandomAccessStream.cpp +++ b/src/AppInstallerCommonCore/HttpStream/HttpRandomAccessStream.cpp @@ -1,102 +1,104 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "pch.h" -#include "HttpRandomAccessStream.h" -#include "Public/AppInstallerDownloader.h" - -using namespace winrt::Windows::Foundation; -using namespace winrt::Windows::Storage::Streams; - -// Note: the HttpRandomAccessStream is passed to the AppxPackaging COM API -// All exceptions thrown across dll boundaries should be WinRT exception not custom exceptions. -// The HRESULTs will be mapped to UI error code by the appropriate component -namespace AppInstaller::Utility::HttpStream -{ - IAsyncOperation HttpRandomAccessStream::InitializeAsync(const Uri& uri) - { - auto strong_this{ get_strong() }; - - try - { - strong_this->m_httpHelper = co_await HttpClientWrapper::CreateAsync(uri); - strong_this->m_size = strong_this->m_httpHelper->GetFullFileSize(); - strong_this->m_httpLocalCache = std::make_unique(); - } - catch (const ServiceUnavailableException& e) - { - strong_this->m_retryAfter = e.RetryAfter(); - throw; - } - - co_return strong_this.as(); - } - - uint64_t HttpRandomAccessStream::Size() const - { - return m_size; - } - - void HttpRandomAccessStream::Size(uint64_t value) - { - UNREFERENCED_PARAMETER(value); - THROW_HR(E_NOTIMPL); - } - - uint64_t HttpRandomAccessStream::Position() const - { - return m_requestedPosition; - } - - bool HttpRandomAccessStream::CanRead() const - { - return true; - } - - bool HttpRandomAccessStream::CanWrite() const - { - return false; - } - - IInputStream HttpRandomAccessStream::GetInputStreamAt(uint64_t position) const - { - UNREFERENCED_PARAMETER(position); - THROW_HR(E_NOTIMPL); - } - - IOutputStream HttpRandomAccessStream::GetOutputStreamAt(uint64_t position) const - { - UNREFERENCED_PARAMETER(position); - THROW_HR(E_NOTIMPL); - } - - IRandomAccessStream HttpRandomAccessStream::CloneStream() const - { - THROW_HR(E_NOTIMPL); - } - - void HttpRandomAccessStream::Seek(uint64_t position) - { - m_requestedPosition = position; - } - - IAsyncOperationWithProgress HttpRandomAccessStream::ReadAsync( - IBuffer buffer, - uint32_t count, - InputStreamOptions options) - { - IBuffer result = co_await m_httpLocalCache->ReadFromCacheAndDownloadIfNecessaryAsync( - m_requestedPosition, - count, - m_httpHelper.get(), - options); - winrt::check_hresult(ULong64Add(m_requestedPosition, result.Length(), &m_requestedPosition)); - - co_return result; - } - - std::chrono::seconds HttpRandomAccessStream::RetryAfter() const - { - return m_retryAfter; - } +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "pch.h" +#include "HttpRandomAccessStream.h" +#include "Public/AppInstallerDownloader.h" + +using namespace winrt::Windows::Foundation; +using namespace winrt::Windows::Storage::Streams; + +// Note: the HttpRandomAccessStream is passed to the AppxPackaging COM API +// All exceptions thrown across dll boundaries should be WinRT exception not custom exceptions. +// The HRESULTs will be mapped to UI error code by the appropriate component +namespace AppInstaller::Utility::HttpStream +{ + IAsyncOperation HttpRandomAccessStream::InitializeAsync(const Uri& uri) + { + auto strong_this{ get_strong() }; + + try + { + auto httpHelper = HttpClientWrapper::Create(uri); + co_await httpHelper->PopulateInfoAsync(); + strong_this->m_httpHelper = std::move(httpHelper); + strong_this->m_size = strong_this->m_httpHelper->GetFullFileSize(); + strong_this->m_httpLocalCache = std::make_unique(); + } + catch (const ServiceUnavailableException& e) + { + strong_this->m_retryAfter = e.RetryAfter(); + throw; + } + + co_return strong_this.as(); + } + + uint64_t HttpRandomAccessStream::Size() const + { + return m_size; + } + + void HttpRandomAccessStream::Size(uint64_t value) + { + UNREFERENCED_PARAMETER(value); + THROW_HR(E_NOTIMPL); + } + + uint64_t HttpRandomAccessStream::Position() const + { + return m_requestedPosition; + } + + bool HttpRandomAccessStream::CanRead() const + { + return true; + } + + bool HttpRandomAccessStream::CanWrite() const + { + return false; + } + + IInputStream HttpRandomAccessStream::GetInputStreamAt(uint64_t position) const + { + UNREFERENCED_PARAMETER(position); + THROW_HR(E_NOTIMPL); + } + + IOutputStream HttpRandomAccessStream::GetOutputStreamAt(uint64_t position) const + { + UNREFERENCED_PARAMETER(position); + THROW_HR(E_NOTIMPL); + } + + IRandomAccessStream HttpRandomAccessStream::CloneStream() const + { + THROW_HR(E_NOTIMPL); + } + + void HttpRandomAccessStream::Seek(uint64_t position) + { + m_requestedPosition = position; + } + + IAsyncOperationWithProgress HttpRandomAccessStream::ReadAsync( + IBuffer buffer, + uint32_t count, + InputStreamOptions options) + { + IBuffer result = co_await m_httpLocalCache->ReadFromCacheAndDownloadIfNecessaryAsync( + m_requestedPosition, + count, + m_httpHelper.get(), + options); + winrt::check_hresult(ULong64Add(m_requestedPosition, result.Length(), &m_requestedPosition)); + + co_return result; + } + + std::chrono::seconds HttpRandomAccessStream::RetryAfter() const + { + return m_retryAfter; + } } \ No newline at end of file diff --git a/src/AppInstallerCommonCore/packages.config b/src/AppInstallerCommonCore/packages.config index 3a8e0698a3..132d149574 100644 --- a/src/AppInstallerCommonCore/packages.config +++ b/src/AppInstallerCommonCore/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj b/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj index 34cc155a46..1d58afec51 100644 --- a/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj +++ b/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj @@ -1,6 +1,6 @@ - + true true @@ -514,14 +514,14 @@ - + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - + + \ No newline at end of file diff --git a/src/AppInstallerRepositoryCore/packages.config b/src/AppInstallerRepositoryCore/packages.config index 3a8e0698a3..132d149574 100644 --- a/src/AppInstallerRepositoryCore/packages.config +++ b/src/AppInstallerRepositoryCore/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/src/AppInstallerSharedLib/AppInstallerSharedLib.vcxproj b/src/AppInstallerSharedLib/AppInstallerSharedLib.vcxproj index adf3c4c6c6..22329e730e 100644 --- a/src/AppInstallerSharedLib/AppInstallerSharedLib.vcxproj +++ b/src/AppInstallerSharedLib/AppInstallerSharedLib.vcxproj @@ -1,6 +1,6 @@ - + true true @@ -410,15 +410,14 @@ - + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - + + - - + \ No newline at end of file diff --git a/src/AppInstallerSharedLib/AppInstallerSharedLib.vcxproj.filters b/src/AppInstallerSharedLib/AppInstallerSharedLib.vcxproj.filters index 61457db664..142e046444 100644 --- a/src/AppInstallerSharedLib/AppInstallerSharedLib.vcxproj.filters +++ b/src/AppInstallerSharedLib/AppInstallerSharedLib.vcxproj.filters @@ -248,5 +248,6 @@ + \ No newline at end of file diff --git a/src/AppInstallerSharedLib/packages.config b/src/AppInstallerSharedLib/packages.config index 3a8e0698a3..132d149574 100644 --- a/src/AppInstallerSharedLib/packages.config +++ b/src/AppInstallerSharedLib/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/src/ComInprocTestbed/ComInprocTestbed.vcxproj b/src/ComInprocTestbed/ComInprocTestbed.vcxproj index b95fad10ff..91e232c838 100644 --- a/src/ComInprocTestbed/ComInprocTestbed.vcxproj +++ b/src/ComInprocTestbed/ComInprocTestbed.vcxproj @@ -1,6 +1,6 @@ - + 15.0 {E5BCFF58-7D0C-4770-ABB9-AECE1027CD94} @@ -169,9 +169,6 @@ - - - {9ac3c6a4-1875-4d3e-bf9c-c31e81eff6b4} @@ -209,15 +206,18 @@ + + + - + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - + + \ No newline at end of file diff --git a/src/ComInprocTestbed/ComInprocTestbed.vcxproj.filters b/src/ComInprocTestbed/ComInprocTestbed.vcxproj.filters index fab38937e9..3fba5fb97e 100644 --- a/src/ComInprocTestbed/ComInprocTestbed.vcxproj.filters +++ b/src/ComInprocTestbed/ComInprocTestbed.vcxproj.filters @@ -28,9 +28,6 @@ Source Files - - - @@ -48,4 +45,7 @@ Header Files + + + \ No newline at end of file diff --git a/src/ComInprocTestbed/packages.config b/src/ComInprocTestbed/packages.config index f32f48b009..0495acb040 100644 --- a/src/ComInprocTestbed/packages.config +++ b/src/ComInprocTestbed/packages.config @@ -1,4 +1,4 @@  - + \ No newline at end of file diff --git a/src/Microsoft.Management.Configuration.OutOfProc/Microsoft.Management.Configuration.OutOfProc.vcxproj b/src/Microsoft.Management.Configuration.OutOfProc/Microsoft.Management.Configuration.OutOfProc.vcxproj index d52da7bcf5..6c7e1404f1 100644 --- a/src/Microsoft.Management.Configuration.OutOfProc/Microsoft.Management.Configuration.OutOfProc.vcxproj +++ b/src/Microsoft.Management.Configuration.OutOfProc/Microsoft.Management.Configuration.OutOfProc.vcxproj @@ -1,6 +1,6 @@ - + true true @@ -413,15 +413,14 @@ - + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - + + - - + \ No newline at end of file diff --git a/src/Microsoft.Management.Configuration.OutOfProc/Microsoft.Management.Configuration.OutOfProc.vcxproj.filters b/src/Microsoft.Management.Configuration.OutOfProc/Microsoft.Management.Configuration.OutOfProc.vcxproj.filters index 15c52c8a46..20e2b09d55 100644 --- a/src/Microsoft.Management.Configuration.OutOfProc/Microsoft.Management.Configuration.OutOfProc.vcxproj.filters +++ b/src/Microsoft.Management.Configuration.OutOfProc/Microsoft.Management.Configuration.OutOfProc.vcxproj.filters @@ -56,5 +56,6 @@ + \ No newline at end of file diff --git a/src/Microsoft.Management.Configuration.OutOfProc/packages.config b/src/Microsoft.Management.Configuration.OutOfProc/packages.config index 3a8e0698a3..132d149574 100644 --- a/src/Microsoft.Management.Configuration.OutOfProc/packages.config +++ b/src/Microsoft.Management.Configuration.OutOfProc/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/src/Microsoft.Management.Configuration/Microsoft.Management.Configuration.vcxproj b/src/Microsoft.Management.Configuration/Microsoft.Management.Configuration.vcxproj index 53dfe11d66..b96262fccf 100644 --- a/src/Microsoft.Management.Configuration/Microsoft.Management.Configuration.vcxproj +++ b/src/Microsoft.Management.Configuration/Microsoft.Management.Configuration.vcxproj @@ -1,6 +1,6 @@ - + true true @@ -312,15 +312,15 @@ - + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - + + - + \ No newline at end of file diff --git a/src/Microsoft.Management.Configuration/Microsoft.Management.Configuration.vcxproj.filters b/src/Microsoft.Management.Configuration/Microsoft.Management.Configuration.vcxproj.filters index 3901ba8f1b..a20087bd56 100644 --- a/src/Microsoft.Management.Configuration/Microsoft.Management.Configuration.vcxproj.filters +++ b/src/Microsoft.Management.Configuration/Microsoft.Management.Configuration.vcxproj.filters @@ -382,5 +382,6 @@ + \ No newline at end of file diff --git a/src/Microsoft.Management.Configuration/packages.config b/src/Microsoft.Management.Configuration/packages.config index 3a8e0698a3..132d149574 100644 --- a/src/Microsoft.Management.Configuration/packages.config +++ b/src/Microsoft.Management.Configuration/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/src/Microsoft.Management.Deployment.InProc/Microsoft.Management.Deployment.InProc.vcxproj b/src/Microsoft.Management.Deployment.InProc/Microsoft.Management.Deployment.InProc.vcxproj index 25367bc4b5..da95005d3f 100644 --- a/src/Microsoft.Management.Deployment.InProc/Microsoft.Management.Deployment.InProc.vcxproj +++ b/src/Microsoft.Management.Deployment.InProc/Microsoft.Management.Deployment.InProc.vcxproj @@ -1,6 +1,6 @@ - + true true @@ -370,19 +370,18 @@ - + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - + + - - + \ No newline at end of file diff --git a/src/Microsoft.Management.Deployment.InProc/Microsoft.Management.Deployment.InProc.vcxproj.filters b/src/Microsoft.Management.Deployment.InProc/Microsoft.Management.Deployment.InProc.vcxproj.filters index 94cb824253..d9b8b2a279 100644 --- a/src/Microsoft.Management.Deployment.InProc/Microsoft.Management.Deployment.InProc.vcxproj.filters +++ b/src/Microsoft.Management.Deployment.InProc/Microsoft.Management.Deployment.InProc.vcxproj.filters @@ -39,5 +39,6 @@ + \ No newline at end of file diff --git a/src/Microsoft.Management.Deployment.InProc/packages.config b/src/Microsoft.Management.Deployment.InProc/packages.config index 3a8e0698a3..132d149574 100644 --- a/src/Microsoft.Management.Deployment.InProc/packages.config +++ b/src/Microsoft.Management.Deployment.InProc/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/src/Microsoft.Management.Deployment.OutOfProc/Microsoft.Management.Deployment.OutOfProc.vcxproj b/src/Microsoft.Management.Deployment.OutOfProc/Microsoft.Management.Deployment.OutOfProc.vcxproj index 292dc22273..6043186d32 100644 --- a/src/Microsoft.Management.Deployment.OutOfProc/Microsoft.Management.Deployment.OutOfProc.vcxproj +++ b/src/Microsoft.Management.Deployment.OutOfProc/Microsoft.Management.Deployment.OutOfProc.vcxproj @@ -1,6 +1,6 @@ - + true true @@ -411,15 +411,14 @@ - + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - + + - - + \ No newline at end of file diff --git a/src/Microsoft.Management.Deployment.OutOfProc/Microsoft.Management.Deployment.OutOfProc.vcxproj.filters b/src/Microsoft.Management.Deployment.OutOfProc/Microsoft.Management.Deployment.OutOfProc.vcxproj.filters index 0a8e916016..ad773990ff 100644 --- a/src/Microsoft.Management.Deployment.OutOfProc/Microsoft.Management.Deployment.OutOfProc.vcxproj.filters +++ b/src/Microsoft.Management.Deployment.OutOfProc/Microsoft.Management.Deployment.OutOfProc.vcxproj.filters @@ -54,5 +54,6 @@ + \ No newline at end of file diff --git a/src/Microsoft.Management.Deployment.OutOfProc/packages.config b/src/Microsoft.Management.Deployment.OutOfProc/packages.config index 3a8e0698a3..132d149574 100644 --- a/src/Microsoft.Management.Deployment.OutOfProc/packages.config +++ b/src/Microsoft.Management.Deployment.OutOfProc/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/src/Microsoft.Management.Deployment/Microsoft.Management.Deployment.vcxproj b/src/Microsoft.Management.Deployment/Microsoft.Management.Deployment.vcxproj index 5eb84d2782..2894e42bac 100644 --- a/src/Microsoft.Management.Deployment/Microsoft.Management.Deployment.vcxproj +++ b/src/Microsoft.Management.Deployment/Microsoft.Management.Deployment.vcxproj @@ -1,6 +1,6 @@ - + true true @@ -272,15 +272,15 @@ - + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - + + \ No newline at end of file diff --git a/src/Microsoft.Management.Deployment/packages.config b/src/Microsoft.Management.Deployment/packages.config index 3a8e0698a3..132d149574 100644 --- a/src/Microsoft.Management.Deployment/packages.config +++ b/src/Microsoft.Management.Deployment/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/src/WinGetUtil/WinGetUtil.vcxproj b/src/WinGetUtil/WinGetUtil.vcxproj index 12066a2a3b..4e4d707f75 100644 --- a/src/WinGetUtil/WinGetUtil.vcxproj +++ b/src/WinGetUtil/WinGetUtil.vcxproj @@ -1,6 +1,6 @@ - + true true @@ -269,15 +269,14 @@ - + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - + + - - + \ No newline at end of file diff --git a/src/WinGetUtil/packages.config b/src/WinGetUtil/packages.config index 3a8e0698a3..132d149574 100644 --- a/src/WinGetUtil/packages.config +++ b/src/WinGetUtil/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/src/WinGetYamlFuzzing/WinGetYamlFuzzing.vcxproj b/src/WinGetYamlFuzzing/WinGetYamlFuzzing.vcxproj index f47a72e910..30a25c1a78 100644 --- a/src/WinGetYamlFuzzing/WinGetYamlFuzzing.vcxproj +++ b/src/WinGetYamlFuzzing/WinGetYamlFuzzing.vcxproj @@ -1,6 +1,6 @@ - + Fuzzing @@ -98,14 +98,14 @@ - + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - + + - + \ No newline at end of file diff --git a/src/WinGetYamlFuzzing/packages.config b/src/WinGetYamlFuzzing/packages.config index f83207db21..132d149574 100644 --- a/src/WinGetYamlFuzzing/packages.config +++ b/src/WinGetYamlFuzzing/packages.config @@ -1,5 +1,5 @@  + - \ No newline at end of file diff --git a/src/WindowsPackageManager/ConfigurationStaticFunctions.cpp b/src/WindowsPackageManager/ConfigurationStaticFunctions.cpp index 6322b83dad..e904c39101 100644 --- a/src/WindowsPackageManager/ConfigurationStaticFunctions.cpp +++ b/src/WindowsPackageManager/ConfigurationStaticFunctions.cpp @@ -1,324 +1,324 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace AppInstaller::SelfManagement; -using namespace winrt::Microsoft::Management::Deployment; -using namespace winrt::Windows::Foundation::Collections; - -namespace ConfigurationShim -{ - namespace - { - static std::atomic_bool s_canBeCreated{ true }; - - auto GetInternalStatics() - { - return winrt::Microsoft::Management::Configuration::ConfigurationStaticFunctions().as(); - } - - void BlockNewWorkForShutdown(AppInstaller::CancelReason) - { - GetInternalStatics()->BlockNewWorkForShutdown(); - } - - void BeginShutdown(AppInstaller::CancelReason) - { - GetInternalStatics()->BeginShutdown(); - } - - void WaitForShutdown() - { - GetInternalStatics()->WaitForShutdown(); - } - - void RegisterForShutdownSynchronization() - { - static std::once_flag registerComponentOnceFlag; - std::call_once(registerComponentOnceFlag, - [&]() - { - using namespace AppInstaller::ShutdownMonitoring; - - ServerShutdownSynchronization::ComponentSystem component; - component.BlockNewWork = BlockNewWorkForShutdown; - component.BeginShutdown = BeginShutdown; - component.Wait = WaitForShutdown; - - ServerShutdownSynchronization::AddComponent(component); - }); - } - } - - CLSID CLSID_ConfigurationObjectLifetimeWatcher = { 0x89a8f1d4,0x1e24,0x46a4,{0x9f,0x6c,0x65,0x78,0xb0,0x47,0xf2,0xf7} }; - - struct - DECLSPEC_UUID("89a8f1d4-1e24-46a4-9f6c-6578b047f2f7") - ConfigurationObjectLifetimeWatcher : winrt::implements - { - }; - - struct - DECLSPEC_UUID(WINGET_OUTOFPROC_COM_CLSID_ConfigurationStaticFunctions) - ConfigurationStaticFunctionsShim : winrt::implements - { - ConfigurationStaticFunctionsShim() - { - auto threadGlobalsRestore = m_threadGlobals.SetForCurrentThread(); - auto& diagnosticsLogger = m_threadGlobals.GetDiagnosticLogger(); - diagnosticsLogger.SetEnabledChannels(AppInstaller::Logging::Channel::All); - diagnosticsLogger.SetLevel(AppInstaller::Logging::Level::Verbose); - diagnosticsLogger.AddLogger(std::make_unique("WinGetCFG"sv)); - - if (IsConfigurationAvailable()) - { - m_statics = winrt::Microsoft::Management::Configuration::ConfigurationStaticFunctions().as(); - RegisterForShutdownSynchronization(); - } - } - - winrt::Microsoft::Management::Configuration::ConfigurationUnit CreateConfigurationUnit() - { - THROW_HR_IF(CO_E_CLASS_DISABLED, !s_canBeCreated); - - if (!m_statics) - { - THROW_HR(APPINSTALLER_CLI_ERROR_PACKAGE_IS_STUB); - } - - auto result = m_statics.CreateConfigurationUnit(); - result.as()->SetLifetimeWatcher(CreateLifetimeWatcher()); - return result; - } - - winrt::Microsoft::Management::Configuration::ConfigurationSet CreateConfigurationSet() - { - THROW_HR_IF(CO_E_CLASS_DISABLED, !s_canBeCreated); - - if (!m_statics) - { - THROW_HR(APPINSTALLER_CLI_ERROR_PACKAGE_IS_STUB); - } - - auto result = m_statics.CreateConfigurationSet(); - result.as()->SetLifetimeWatcher(CreateLifetimeWatcher()); - return result; - } - - winrt::Windows::Foundation::IAsyncOperation CreateConfigurationSetProcessorFactoryAsync(winrt::hstring const& handler) - { - THROW_HR_IF(CO_E_CLASS_DISABLED, !s_canBeCreated); - - auto strong_this{ get_strong() }; - std::wstring lowerHandler = AppInstaller::Utility::ToLower(handler); - - co_await winrt::resume_background(); - - auto threadGlobalsRestore = m_threadGlobals.SetForCurrentThread(); - winrt::Microsoft::Management::Configuration::IConfigurationSetProcessorFactory result; - - if (lowerHandler == AppInstaller::Configuration::PowerShellHandlerIdentifier) - { - result = AppInstaller::CLI::ConfigurationRemoting::CreateOutOfProcessFactory(AppInstaller::CLI::ConfigurationRemoting::ProcessorEngine::PowerShell); - } - else if (lowerHandler == AppInstaller::Configuration::DynamicRuntimeHandlerIdentifier) - { - result = AppInstaller::CLI::ConfigurationRemoting::CreateDynamicRuntimeFactory(AppInstaller::CLI::ConfigurationRemoting::ProcessorEngine::PowerShell); - } - else if (lowerHandler == AppInstaller::Configuration::DSCv3HandlerIdentifier) - { - result = AppInstaller::CLI::ConfigurationRemoting::CreateOutOfProcessFactory(AppInstaller::CLI::ConfigurationRemoting::ProcessorEngine::DSCv3); - } - else if (lowerHandler == AppInstaller::Configuration::DSCv3DynamicRuntimeHandlerIdentifier) - { - result = AppInstaller::CLI::ConfigurationRemoting::CreateDynamicRuntimeFactory(AppInstaller::CLI::ConfigurationRemoting::ProcessorEngine::DSCv3); - } - - if (result) - { - // Objects returned here *must* implement ILifetimeWatcher for now. - // If we create OOP objects implemented elsewhere in the future, decide then how to exempt those while still ensuring we - // don't accidentally create a lifetime bug by basing it solely off the QI result. - result.as()->SetLifetimeWatcher(CreateLifetimeWatcher()); - co_return result; - } - - AICLI_LOG(Config, Error, << "Unknown handler in CreateConfigurationSetProcessorFactory: " << AppInstaller::Utility::ConvertToUTF8(handler)); - THROW_HR(E_NOT_SET); - } - - winrt::Microsoft::Management::Configuration::ConfigurationProcessor CreateConfigurationProcessor(winrt::Microsoft::Management::Configuration::IConfigurationSetProcessorFactory const& factory) - { - THROW_HR_IF(CO_E_CLASS_DISABLED, !s_canBeCreated); - - if (!m_statics) - { - THROW_HR(APPINSTALLER_CLI_ERROR_PACKAGE_IS_STUB); - } - - auto result = m_statics.CreateConfigurationProcessor(factory); - result.as()->SetLifetimeWatcher(CreateLifetimeWatcher()); - return result; - } - - bool IsConfigurationAvailable() - { - return !IsStubPackage(); - } - - winrt::Windows::Foundation::IAsyncActionWithProgress EnsureConfigurationAvailableAsync() - { - THROW_HR_IF(CO_E_CLASS_DISABLED, !s_canBeCreated); - - if (IsConfigurationAvailable()) - { - return; - } - - auto strong_this{ get_strong() }; - co_await winrt::resume_background(); - - SetStubPreferred(false); - - PackageManager packageManager; - PackageCatalogReference catalogRef{ packageManager.GetPredefinedPackageCatalog(PredefinedPackageCatalog::MicrosoftStore) }; - THROW_HR_IF(APPINSTALLER_CLI_ERROR_INTERNAL_ERROR, !catalogRef); - - ConnectResult connectResult = catalogRef.Connect(); - THROW_HR_IF(APPINSTALLER_CLI_ERROR_SOURCE_OPEN_FAILED, connectResult.Status() != ConnectResultStatus::Ok); - - PackageCatalog catalog = connectResult.PackageCatalog(); - - FindPackagesOptions findPackagesOptions; - PackageMatchFilter filter; - filter.Field(PackageMatchField::Id); - filter.Option(PackageFieldMatchOption::Equals); - filter.Value(AppInstaller::MSStore::s_AppInstallerProductId); - findPackagesOptions.Filters().Append(filter); - - FindPackagesResult findPackagesResult{ catalog.FindPackages(findPackagesOptions) }; - - auto matches = findPackagesResult.Matches(); - THROW_HR_IF(APPINSTALLER_CLI_ERROR_MISSING_PACKAGE, matches.Size() == 0); - auto catalogPackage = matches.GetAt(0).CatalogPackage(); - - InstallOptions installOptions; - installOptions.AcceptPackageAgreements(true); - installOptions.AllowUpgradeToUnknownVersion(true); - installOptions.Force(true); - - auto progress = co_await winrt::get_progress_token(); - - // Don't use UpgradePackageAsync, we don't support upgrade for packages from the msstore - // it has to be install and internally we know is an update. - auto installTask = packageManager.InstallPackageAsync(catalogPackage, installOptions); - installTask.Progress([progress](auto const&, InstallProgress installProgress) - { - if (installProgress.State == PackageInstallProgressState::Downloading && installProgress.BytesRequired != 0) - { - progress((uint32_t)(installProgress.DownloadProgress * 80)); - } - else if (installProgress.State == PackageInstallProgressState::Installing) - { - progress(((uint32_t)installProgress.InstallationProgress * 20) + 80); - } - }); - - co_await installTask; - s_canBeCreated = false; - } - - winrt::Microsoft::Management::Configuration::ConfigurationParameter CreateConfigurationParameter() - { - THROW_HR_IF(CO_E_CLASS_DISABLED, !s_canBeCreated); - - if (!m_statics) - { - THROW_HR(APPINSTALLER_CLI_ERROR_PACKAGE_IS_STUB); - } - - auto result = m_statics.CreateConfigurationParameter(); - result.as()->SetLifetimeWatcher(CreateLifetimeWatcher()); - return result; - } - - winrt::Microsoft::Management::Configuration::FindUnitProcessorsOptions CreateFindUnitProcessorsOptions() - { - THROW_HR_IF(CO_E_CLASS_DISABLED, !s_canBeCreated); - - if (!m_statics) - { - THROW_HR(APPINSTALLER_CLI_ERROR_PACKAGE_IS_STUB); - } - - auto result = m_statics.CreateFindUnitProcessorsOptions(); - result.as()->SetLifetimeWatcher(CreateLifetimeWatcher()); - return result; - } - - private: - // Returns a lifetime watcher object that is currently *unowned*. - IUnknown* CreateLifetimeWatcher() - { - ::Microsoft::WRL::ComPtr factory; - THROW_IF_FAILED(::Microsoft::WRL::Module<::Microsoft::WRL::ModuleType::OutOfProc>::GetModule().GetClassObject(CLSID_ConfigurationObjectLifetimeWatcher, IID_PPV_ARGS(&factory))); - winrt::com_ptr out; - THROW_IF_FAILED(factory->CreateInstance(nullptr, __uuidof(IUnknown), out.put_void())); - return out.detach(); - } - - winrt::Microsoft::Management::Configuration::IConfigurationStatics3 m_statics = nullptr; - AppInstaller::ThreadLocalStorage::WingetThreadGlobals m_threadGlobals; - }; - - // Enable custom code to run before creating any object through the factory. - template - class ConfigurationFactory : public ::wil::wrl_factory_for_winrt_com_class - { - public: - IFACEMETHODIMP CreateInstance(_In_opt_::IUnknown* unknownOuter, REFIID riid, _COM_Outptr_ void** object) noexcept try - { - *object = nullptr; - RETURN_HR_IF(APPINSTALLER_CLI_ERROR_BLOCKED_BY_POLICY, !::AppInstaller::Settings::GroupPolicies().IsEnabled(::AppInstaller::Settings::TogglePolicy::Policy::WinGet)); - RETURN_HR_IF(APPINSTALLER_CLI_ERROR_BLOCKED_BY_POLICY, !::AppInstaller::Settings::GroupPolicies().IsEnabled(::AppInstaller::Settings::TogglePolicy::Policy::Configuration)); - RETURN_HR_IF(E_ACCESSDENIED, !::AppInstaller::Security::IsCOMCallerSameUserAndIntegrityLevel()); - - RETURN_HR_IF(CO_E_CLASS_DISABLED, !s_canBeCreated); - - return ::wil::wrl_factory_for_winrt_com_class::CreateInstance(unknownOuter, riid, object); - } - CATCH_RETURN() - }; - -#define CoCreatableMicrosoftManagementConfigurationClass(className) \ - CoCreatableClassWithFactory(className, ::ConfigurationShim::ConfigurationFactory) - - // Disable 6388 as it seems to be falsely warning -#pragma warning(push) -#pragma warning(disable : 6388) - CoCreatableCppWinRtClass(ConfigurationObjectLifetimeWatcher); - CoCreatableMicrosoftManagementConfigurationClass(ConfigurationStaticFunctionsShim); -#pragma warning(pop) -} +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace AppInstaller::SelfManagement; +using namespace winrt::Microsoft::Management::Deployment; +using namespace winrt::Windows::Foundation::Collections; + +namespace ConfigurationShim +{ + namespace + { + static std::atomic_bool s_canBeCreated{ true }; + + auto GetInternalStatics() + { + return winrt::Microsoft::Management::Configuration::ConfigurationStaticFunctions().as(); + } + + void BlockNewWorkForShutdown(AppInstaller::CancelReason) + { + GetInternalStatics()->BlockNewWorkForShutdown(); + } + + void BeginShutdown(AppInstaller::CancelReason) + { + GetInternalStatics()->BeginShutdown(); + } + + void WaitForShutdown() + { + GetInternalStatics()->WaitForShutdown(); + } + + void RegisterForShutdownSynchronization() + { + static std::once_flag registerComponentOnceFlag; + std::call_once(registerComponentOnceFlag, + [&]() + { + using namespace AppInstaller::ShutdownMonitoring; + + ServerShutdownSynchronization::ComponentSystem component; + component.BlockNewWork = BlockNewWorkForShutdown; + component.BeginShutdown = BeginShutdown; + component.Wait = WaitForShutdown; + + ServerShutdownSynchronization::AddComponent(component); + }); + } + } + + CLSID CLSID_ConfigurationObjectLifetimeWatcher = { 0x89a8f1d4,0x1e24,0x46a4,{0x9f,0x6c,0x65,0x78,0xb0,0x47,0xf2,0xf7} }; + + struct + DECLSPEC_UUID("89a8f1d4-1e24-46a4-9f6c-6578b047f2f7") + ConfigurationObjectLifetimeWatcher : winrt::implements + { + }; + + struct + DECLSPEC_UUID(WINGET_OUTOFPROC_COM_CLSID_ConfigurationStaticFunctions) + ConfigurationStaticFunctionsShim : winrt::implements + { + ConfigurationStaticFunctionsShim() + { + auto threadGlobalsRestore = m_threadGlobals.SetForCurrentThread(); + auto& diagnosticsLogger = m_threadGlobals.GetDiagnosticLogger(); + diagnosticsLogger.SetEnabledChannels(AppInstaller::Logging::Channel::All); + diagnosticsLogger.SetLevel(AppInstaller::Logging::Level::Verbose); + diagnosticsLogger.AddLogger(std::make_unique("WinGetCFG"sv)); + + if (IsConfigurationAvailable()) + { + m_statics = winrt::Microsoft::Management::Configuration::ConfigurationStaticFunctions().as(); + RegisterForShutdownSynchronization(); + } + } + + winrt::Microsoft::Management::Configuration::ConfigurationUnit CreateConfigurationUnit() + { + THROW_HR_IF(CO_E_CLASS_DISABLED, !s_canBeCreated); + + if (!m_statics) + { + THROW_HR(APPINSTALLER_CLI_ERROR_PACKAGE_IS_STUB); + } + + auto result = m_statics.CreateConfigurationUnit(); + result.as()->SetLifetimeWatcher(CreateLifetimeWatcher()); + return result; + } + + winrt::Microsoft::Management::Configuration::ConfigurationSet CreateConfigurationSet() + { + THROW_HR_IF(CO_E_CLASS_DISABLED, !s_canBeCreated); + + if (!m_statics) + { + THROW_HR(APPINSTALLER_CLI_ERROR_PACKAGE_IS_STUB); + } + + auto result = m_statics.CreateConfigurationSet(); + result.as()->SetLifetimeWatcher(CreateLifetimeWatcher()); + return result; + } + + winrt::Windows::Foundation::IAsyncOperation CreateConfigurationSetProcessorFactoryAsync(winrt::hstring const& handler) + { + THROW_HR_IF(CO_E_CLASS_DISABLED, !s_canBeCreated); + + auto strong_this{ get_strong() }; + std::wstring lowerHandler = AppInstaller::Utility::ToLower(handler); + + co_await winrt::resume_background(); + + auto threadGlobalsRestore = m_threadGlobals.SetForCurrentThread(); + winrt::Microsoft::Management::Configuration::IConfigurationSetProcessorFactory result; + + if (lowerHandler == AppInstaller::Configuration::PowerShellHandlerIdentifier) + { + result = AppInstaller::CLI::ConfigurationRemoting::CreateOutOfProcessFactory(AppInstaller::CLI::ConfigurationRemoting::ProcessorEngine::PowerShell); + } + else if (lowerHandler == AppInstaller::Configuration::DynamicRuntimeHandlerIdentifier) + { + result = AppInstaller::CLI::ConfigurationRemoting::CreateDynamicRuntimeFactory(AppInstaller::CLI::ConfigurationRemoting::ProcessorEngine::PowerShell); + } + else if (lowerHandler == AppInstaller::Configuration::DSCv3HandlerIdentifier) + { + result = AppInstaller::CLI::ConfigurationRemoting::CreateOutOfProcessFactory(AppInstaller::CLI::ConfigurationRemoting::ProcessorEngine::DSCv3); + } + else if (lowerHandler == AppInstaller::Configuration::DSCv3DynamicRuntimeHandlerIdentifier) + { + result = AppInstaller::CLI::ConfigurationRemoting::CreateDynamicRuntimeFactory(AppInstaller::CLI::ConfigurationRemoting::ProcessorEngine::DSCv3); + } + + if (result) + { + // Objects returned here *must* implement ILifetimeWatcher for now. + // If we create OOP objects implemented elsewhere in the future, decide then how to exempt those while still ensuring we + // don't accidentally create a lifetime bug by basing it solely off the QI result. + result.as()->SetLifetimeWatcher(CreateLifetimeWatcher()); + co_return result; + } + + AICLI_LOG(Config, Error, << "Unknown handler in CreateConfigurationSetProcessorFactory: " << AppInstaller::Utility::ConvertToUTF8(handler)); + THROW_HR(E_NOT_SET); + } + + winrt::Microsoft::Management::Configuration::ConfigurationProcessor CreateConfigurationProcessor(winrt::Microsoft::Management::Configuration::IConfigurationSetProcessorFactory const& factory) + { + THROW_HR_IF(CO_E_CLASS_DISABLED, !s_canBeCreated); + + if (!m_statics) + { + THROW_HR(APPINSTALLER_CLI_ERROR_PACKAGE_IS_STUB); + } + + auto result = m_statics.CreateConfigurationProcessor(factory); + result.as()->SetLifetimeWatcher(CreateLifetimeWatcher()); + return result; + } + + bool IsConfigurationAvailable() + { + return !IsStubPackage(); + } + + winrt::Windows::Foundation::IAsyncActionWithProgress EnsureConfigurationAvailableAsync() + { + THROW_HR_IF(CO_E_CLASS_DISABLED, !s_canBeCreated); + + if (IsConfigurationAvailable()) + { + co_return; + } + + auto strong_this{ get_strong() }; + co_await winrt::resume_background(); + + SetStubPreferred(false); + + PackageManager packageManager; + PackageCatalogReference catalogRef{ packageManager.GetPredefinedPackageCatalog(PredefinedPackageCatalog::MicrosoftStore) }; + THROW_HR_IF(APPINSTALLER_CLI_ERROR_INTERNAL_ERROR, !catalogRef); + + ConnectResult connectResult = catalogRef.Connect(); + THROW_HR_IF(APPINSTALLER_CLI_ERROR_SOURCE_OPEN_FAILED, connectResult.Status() != ConnectResultStatus::Ok); + + PackageCatalog catalog = connectResult.PackageCatalog(); + + FindPackagesOptions findPackagesOptions; + PackageMatchFilter filter; + filter.Field(PackageMatchField::Id); + filter.Option(PackageFieldMatchOption::Equals); + filter.Value(AppInstaller::MSStore::s_AppInstallerProductId); + findPackagesOptions.Filters().Append(filter); + + FindPackagesResult findPackagesResult{ catalog.FindPackages(findPackagesOptions) }; + + auto matches = findPackagesResult.Matches(); + THROW_HR_IF(APPINSTALLER_CLI_ERROR_MISSING_PACKAGE, matches.Size() == 0); + auto catalogPackage = matches.GetAt(0).CatalogPackage(); + + InstallOptions installOptions; + installOptions.AcceptPackageAgreements(true); + installOptions.AllowUpgradeToUnknownVersion(true); + installOptions.Force(true); + + auto progress = co_await winrt::get_progress_token(); + + // Don't use UpgradePackageAsync, we don't support upgrade for packages from the msstore + // it has to be install and internally we know is an update. + auto installTask = packageManager.InstallPackageAsync(catalogPackage, installOptions); + installTask.Progress([progress](auto const&, InstallProgress installProgress) + { + if (installProgress.State == PackageInstallProgressState::Downloading && installProgress.BytesRequired != 0) + { + progress((uint32_t)(installProgress.DownloadProgress * 80)); + } + else if (installProgress.State == PackageInstallProgressState::Installing) + { + progress(((uint32_t)installProgress.InstallationProgress * 20) + 80); + } + }); + + co_await installTask; + s_canBeCreated = false; + } + + winrt::Microsoft::Management::Configuration::ConfigurationParameter CreateConfigurationParameter() + { + THROW_HR_IF(CO_E_CLASS_DISABLED, !s_canBeCreated); + + if (!m_statics) + { + THROW_HR(APPINSTALLER_CLI_ERROR_PACKAGE_IS_STUB); + } + + auto result = m_statics.CreateConfigurationParameter(); + result.as()->SetLifetimeWatcher(CreateLifetimeWatcher()); + return result; + } + + winrt::Microsoft::Management::Configuration::FindUnitProcessorsOptions CreateFindUnitProcessorsOptions() + { + THROW_HR_IF(CO_E_CLASS_DISABLED, !s_canBeCreated); + + if (!m_statics) + { + THROW_HR(APPINSTALLER_CLI_ERROR_PACKAGE_IS_STUB); + } + + auto result = m_statics.CreateFindUnitProcessorsOptions(); + result.as()->SetLifetimeWatcher(CreateLifetimeWatcher()); + return result; + } + + private: + // Returns a lifetime watcher object that is currently *unowned*. + IUnknown* CreateLifetimeWatcher() + { + ::Microsoft::WRL::ComPtr factory; + THROW_IF_FAILED(::Microsoft::WRL::Module<::Microsoft::WRL::ModuleType::OutOfProc>::GetModule().GetClassObject(CLSID_ConfigurationObjectLifetimeWatcher, IID_PPV_ARGS(&factory))); + winrt::com_ptr out; + THROW_IF_FAILED(factory->CreateInstance(nullptr, __uuidof(IUnknown), out.put_void())); + return out.detach(); + } + + winrt::Microsoft::Management::Configuration::IConfigurationStatics3 m_statics = nullptr; + AppInstaller::ThreadLocalStorage::WingetThreadGlobals m_threadGlobals; + }; + + // Enable custom code to run before creating any object through the factory. + template + class ConfigurationFactory : public ::wil::wrl_factory_for_winrt_com_class + { + public: + IFACEMETHODIMP CreateInstance(_In_opt_::IUnknown* unknownOuter, REFIID riid, _COM_Outptr_ void** object) noexcept try + { + *object = nullptr; + RETURN_HR_IF(APPINSTALLER_CLI_ERROR_BLOCKED_BY_POLICY, !::AppInstaller::Settings::GroupPolicies().IsEnabled(::AppInstaller::Settings::TogglePolicy::Policy::WinGet)); + RETURN_HR_IF(APPINSTALLER_CLI_ERROR_BLOCKED_BY_POLICY, !::AppInstaller::Settings::GroupPolicies().IsEnabled(::AppInstaller::Settings::TogglePolicy::Policy::Configuration)); + RETURN_HR_IF(E_ACCESSDENIED, !::AppInstaller::Security::IsCOMCallerSameUserAndIntegrityLevel()); + + RETURN_HR_IF(CO_E_CLASS_DISABLED, !s_canBeCreated); + + return ::wil::wrl_factory_for_winrt_com_class::CreateInstance(unknownOuter, riid, object); + } + CATCH_RETURN() + }; + +#define CoCreatableMicrosoftManagementConfigurationClass(className) \ + CoCreatableClassWithFactory(className, ::ConfigurationShim::ConfigurationFactory) + + // Disable 6388 as it seems to be falsely warning +#pragma warning(push) +#pragma warning(disable : 6388) + CoCreatableCppWinRtClass(ConfigurationObjectLifetimeWatcher); + CoCreatableMicrosoftManagementConfigurationClass(ConfigurationStaticFunctionsShim); +#pragma warning(pop) +} diff --git a/src/WindowsPackageManager/WindowsPackageManager.vcxproj b/src/WindowsPackageManager/WindowsPackageManager.vcxproj index f8ab04be8e..ad65971af0 100644 --- a/src/WindowsPackageManager/WindowsPackageManager.vcxproj +++ b/src/WindowsPackageManager/WindowsPackageManager.vcxproj @@ -1,6 +1,6 @@ - + true true @@ -431,15 +431,14 @@ - + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - + + - - + \ No newline at end of file diff --git a/src/WindowsPackageManager/packages.config b/src/WindowsPackageManager/packages.config index 3a8e0698a3..132d149574 100644 --- a/src/WindowsPackageManager/packages.config +++ b/src/WindowsPackageManager/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file