Skip to content

Commit 2259b02

Browse files
committed
Attempt to be less hacky with the weak_ptr
1 parent 7ab08d4 commit 2259b02

File tree

3 files changed

+152
-82
lines changed

3 files changed

+152
-82
lines changed

src/AppInstallerCommonCore/HttpClientHelper.cpp

Lines changed: 121 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -5,33 +5,9 @@
55
#include <AppInstallerRuntime.h>
66
#include <winget/HttpClientHelper.h>
77
#include <winget/NetworkSettings.h>
8-
#include <winhttp.h>
98

109
namespace AppInstaller::Http
1110
{
12-
namespace details
13-
{
14-
void InvocationContext::CaptureRequestContext(web::http::client::native_handle handle)
15-
{
16-
HINTERNET requestHandle = reinterpret_cast<HINTERNET>(handle);
17-
std::weak_ptr<void>* contextValue = nullptr;
18-
DWORD valueSize = sizeof(contextValue);
19-
20-
if (LOG_IF_WIN32_BOOL_FALSE(WinHttpQueryOption(requestHandle, WINHTTP_OPTION_CONTEXT_VALUE, &contextValue, &valueSize)))
21-
{
22-
if (contextValue && valueSize == sizeof(contextValue))
23-
{
24-
std::shared_ptr<void> lockedContextValue = contextValue->lock();
25-
if (lockedContextValue)
26-
{
27-
RequestHandle = requestHandle;
28-
RequestContextLifetime = std::move(lockedContextValue);
29-
}
30-
}
31-
}
32-
}
33-
}
34-
3511
namespace
3612
{
3713
// If the caller does not pass in a user agent header, put the default one on the request.
@@ -68,33 +44,6 @@ namespace AppInstaller::Http
6844

6945
return 0s;
7046
}
71-
72-
details::InvocationContext GetClient(
73-
const utility::string_t& uri,
74-
web::http::client::http_client_config clientConfig,
75-
const std::shared_ptr<web::http::http_pipeline_stage>& defaultRequestHandlerStage,
76-
bool captureRequestContext)
77-
{
78-
details::InvocationContext result;
79-
80-
if (captureRequestContext)
81-
{
82-
clientConfig.set_nativehandle_options([&result](web::http::client::native_handle handle)
83-
{
84-
result.CaptureRequestContext(handle);
85-
});
86-
}
87-
88-
result.HttpClient = std::make_unique<web::http::client::http_client>(uri, clientConfig);
89-
90-
// Add default custom handlers if any.
91-
if (defaultRequestHandlerStage)
92-
{
93-
result.HttpClient->add_handler(defaultRequestHandlerStage);
94-
}
95-
96-
return result;
97-
}
9847
}
9948

10049
HttpClientHelper::HttpClientHelper(std::shared_ptr<web::http::http_pipeline_stage> stage)
@@ -112,14 +61,121 @@ namespace AppInstaller::Http
11261
}
11362
}
11463

115-
details::InvocationContext HttpClientHelper::Post(
64+
void HttpClientHelper::InvocationContext::CaptureSessionHandle(web::http::client::native_handle sessionHandle)
65+
{
66+
SessionHandle = sessionHandle;
67+
}
68+
69+
void HttpClientHelper::InvocationContext::InstallStatusCallback(web::http::client::native_handle handle)
70+
{
71+
// Get current context
72+
HINTERNET requestHandle = reinterpret_cast<HINTERNET>(handle);
73+
DWORD_PTR contextValue = 0;
74+
DWORD valueSize = sizeof(contextValue);
75+
76+
THROW_IF_WIN32_BOOL_FALSE(WinHttpQueryOption(requestHandle, WINHTTP_OPTION_CONTEXT_VALUE, &contextValue, &valueSize));
77+
THROW_HR_IF(E_UNEXPECTED, valueSize != sizeof(contextValue));
78+
79+
// Install our callback
80+
WINHTTP_STATUS_CALLBACK previousCallback = WinHttpSetStatusCallback(
81+
SessionHandle,
82+
ValidatePinningConfigurationCallback,
83+
WINHTTP_CALLBACK_FLAG_ALL_COMPLETIONS | WINHTTP_CALLBACK_FLAG_HANDLES |
84+
WINHTTP_CALLBACK_FLAG_SECURE_FAILURE | WINHTTP_CALLBACK_FLAG_SEND_REQUEST |
85+
WINHTTP_CALLBACK_STATUS_REDIRECT,
86+
0);
87+
THROW_LAST_ERROR_IF(previousCallback == WINHTTP_INVALID_STATUS_CALLBACK);
88+
// We expect a cpprestsdk callback to be in place at this point
89+
THROW_HR_IF(E_UNEXPECTED, !previousCallback);
90+
91+
// Install our context
92+
THROW_IF_WIN32_BOOL_FALSE(WinHttpSetOption(requestHandle, WINHTTP_OPTION_CONTEXT_VALUE, Self.get(), sizeof(void*)));
93+
94+
PreviousCallback = previousCallback;
95+
PreviousContext = contextValue;
96+
}
97+
98+
void _stdcall HttpClientHelper::InvocationContext::ValidatePinningConfigurationCallback(
99+
IN HINTERNET hInternet,
100+
IN DWORD_PTR dwContext,
101+
IN DWORD dwInternetStatus,
102+
IN LPVOID lpvStatusInformation OPTIONAL,
103+
IN DWORD dwStatusInformationLength)
104+
{
105+
std::weak_ptr<InvocationContext>* weakContext = reinterpret_cast<std::weak_ptr<InvocationContext>*>(dwContext);
106+
if (!weakContext)
107+
{
108+
return;
109+
}
110+
111+
std::shared_ptr<InvocationContext> context = weakContext->lock();
112+
if (!context)
113+
{
114+
return;
115+
}
116+
117+
if (!context->PreviousCallback)
118+
{
119+
return;
120+
}
121+
122+
if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_SENDING_REQUEST)
123+
{
124+
try
125+
{
126+
NativeHandleServerCertificateValidation(hInternet, context->PinningConfiguration);
127+
}
128+
catch (...)
129+
{
130+
context->PinningValidationException = std::current_exception();
131+
WinHttpCloseHandle(hInternet);
132+
return;
133+
}
134+
}
135+
136+
reinterpret_cast<WINHTTP_STATUS_CALLBACK>(context->PreviousCallback)(hInternet, context->PreviousContext, dwInternetStatus, lpvStatusInformation, dwStatusInformationLength);
137+
}
138+
139+
std::shared_ptr<HttpClientHelper::InvocationContext> HttpClientHelper::GetClient(const utility::string_t& uri) const
140+
{
141+
std::shared_ptr<InvocationContext> result = std::make_shared<InvocationContext>();
142+
result->Self = std::make_unique<std::weak_ptr<InvocationContext>>(result);
143+
web::http::client::http_client_config clientConfig = m_clientConfig;
144+
145+
if (m_pinningConfiguration)
146+
{
147+
result->PinningConfiguration = m_pinningConfiguration.value();
148+
149+
clientConfig.set_nativesessionhandle_options([&result](web::http::client::native_handle handle)
150+
{
151+
result->CaptureSessionHandle(handle);
152+
});
153+
154+
clientConfig.set_nativehandle_options([&result](web::http::client::native_handle handle)
155+
{
156+
result->InstallStatusCallback(handle);
157+
});
158+
}
159+
160+
result->HttpClient = std::make_unique<web::http::client::http_client>(uri, clientConfig);
161+
162+
// Add default custom handlers if any.
163+
if (m_defaultRequestHandlerStage)
164+
{
165+
result->HttpClient->add_handler(m_defaultRequestHandlerStage);
166+
}
167+
168+
return result;
169+
}
170+
171+
std::shared_ptr<HttpClientHelper::InvocationContext> HttpClientHelper::Post(
116172
const utility::string_t& uri,
117173
const web::json::value& body,
118174
const HttpClientHelper::HttpRequestHeaders& headers,
119175
const HttpClientHelper::HttpRequestHeaders& authHeaders) const
120176
{
121177
AICLI_LOG(Repo, Info, << "Sending http POST request to: " << utility::conversions::to_utf8string(uri));
122-
details::InvocationContext result = GetClient(uri, m_clientConfig, m_defaultRequestHandlerStage, m_pinningConfiguration.has_value());
178+
std::shared_ptr<InvocationContext> result = GetClient(uri);
123179
web::http::http_request request{ web::http::methods::POST };
124180
request.headers().set_content_type(web::http::details::mime_types::application_json);
125181
request.set_body(body.serialize());
@@ -139,7 +195,7 @@ namespace AppInstaller::Http
139195
request.headers().add(pair.first, pair.second);
140196
}
141197

142-
result.ResponseTask = result.HttpClient->request(request);
198+
result->ResponseTask = result->HttpClient->request(request);
143199
return result;
144200
}
145201

@@ -151,15 +207,15 @@ namespace AppInstaller::Http
151207
const HttpResponseHandler& customHandler) const try
152208
{
153209
web::http::http_response httpResponse;
154-
details::InvocationContext context = Post(uri, body, headers, authHeaders);
155-
context.ResponseTask.then([&httpResponse](const web::http::http_response& response)
210+
std::shared_ptr<InvocationContext> context = Post(uri, body, headers, authHeaders);
211+
context->ResponseTask.then([&httpResponse](const web::http::http_response& response)
156212
{
157213
httpResponse = response;
158214
}).wait();
159215

160-
if (m_pinningConfiguration)
216+
if (context->PinningValidationException)
161217
{
162-
NativeHandleServerCertificateValidation(context.RequestHandle, m_pinningConfiguration.value());
218+
std::rethrow_exception(context->PinningValidationException);
163219
}
164220

165221
if (customHandler)
@@ -178,13 +234,13 @@ namespace AppInstaller::Http
178234
RethrowAsWilException(exception);
179235
}
180236

181-
details::InvocationContext HttpClientHelper::Get(
237+
std::shared_ptr<HttpClientHelper::InvocationContext> HttpClientHelper::Get(
182238
const utility::string_t& uri,
183239
const HttpClientHelper::HttpRequestHeaders& headers,
184240
const HttpClientHelper::HttpRequestHeaders& authHeaders) const
185241
{
186242
AICLI_LOG(Repo, Info, << "Sending http GET request to: " << utility::conversions::to_utf8string(uri));
187-
details::InvocationContext result = GetClient(uri, m_clientConfig, m_defaultRequestHandlerStage, m_pinningConfiguration.has_value());
243+
std::shared_ptr<InvocationContext> result = GetClient(uri);
188244
web::http::http_request request{ web::http::methods::GET };
189245
request.headers().set_content_type(web::http::details::mime_types::application_json);
190246

@@ -203,7 +259,7 @@ namespace AppInstaller::Http
203259
request.headers().add(pair.first, pair.second);
204260
}
205261

206-
result.ResponseTask = result.HttpClient->request(request);
262+
result->ResponseTask = result->HttpClient->request(request);
207263
return result;
208264
}
209265

@@ -214,15 +270,15 @@ namespace AppInstaller::Http
214270
const HttpResponseHandler& customHandler) const try
215271
{
216272
web::http::http_response httpResponse;
217-
details::InvocationContext context = Get(uri, headers, authHeaders);
218-
context.ResponseTask.then([&httpResponse](const web::http::http_response& response)
273+
std::shared_ptr<InvocationContext> context = Get(uri, headers, authHeaders);
274+
context->ResponseTask.then([&httpResponse](const web::http::http_response& response)
219275
{
220276
httpResponse = response;
221277
}).wait();
222278

223-
if (m_pinningConfiguration)
279+
if (context->PinningValidationException)
224280
{
225-
NativeHandleServerCertificateValidation(context.RequestHandle, m_pinningConfiguration.value());
281+
std::rethrow_exception(context->PinningValidationException);
226282
}
227283

228284
if (customHandler)

src/AppInstallerCommonCore/Public/winget/HttpClientHelper.h

Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,6 @@
1010

1111
namespace AppInstaller::Http
1212
{
13-
namespace details
14-
{
15-
struct InvocationContext
16-
{
17-
std::unique_ptr<web::http::client::http_client> HttpClient;
18-
pplx::task<web::http::http_response> ResponseTask;
19-
web::http::client::native_handle RequestHandle = INVALID_HANDLE_VALUE;
20-
std::shared_ptr<void> RequestContextLifetime;
21-
22-
void CaptureRequestContext(web::http::client::native_handle handle);
23-
};
24-
}
25-
2613
struct HttpClientHelper
2714
{
2815
using HttpRequestHeaders = std::unordered_map<utility::string_t, utility::string_t>;
@@ -40,17 +27,43 @@ namespace AppInstaller::Http
4027

4128
HttpClientHelper(std::shared_ptr<web::http::http_pipeline_stage> = {});
4229

43-
details::InvocationContext Post(const utility::string_t& uri, const web::json::value& body, const HttpRequestHeaders& headers = {}, const HttpRequestHeaders& authHeaders = {}) const;
44-
4530
std::optional<web::json::value> HandlePost(const utility::string_t& uri, const web::json::value& body, const HttpRequestHeaders& headers = {}, const HttpRequestHeaders& authHeaders = {}, const HttpResponseHandler& customHandler = {}) const;
4631

47-
details::InvocationContext Get(const utility::string_t& uri, const HttpRequestHeaders& headers = {}, const HttpRequestHeaders& authHeaders = {}) const;
48-
4932
std::optional<web::json::value> HandleGet(const utility::string_t& uri, const HttpRequestHeaders& headers = {}, const HttpRequestHeaders& authHeaders = {}, const HttpResponseHandler& customHandler = {}) const;
5033

5134
void SetPinningConfiguration(const Certificates::PinningConfiguration& configuration);
5235

5336
protected:
37+
struct InvocationContext : public std::enable_shared_from_this<InvocationContext>
38+
{
39+
std::unique_ptr<std::weak_ptr<InvocationContext>> Self;
40+
std::unique_ptr<web::http::client::http_client> HttpClient;
41+
pplx::task<web::http::http_response> ResponseTask;
42+
43+
web::http::client::native_handle SessionHandle = INVALID_HANDLE_VALUE;
44+
void* PreviousCallback = nullptr;
45+
DWORD_PTR PreviousContext = 0;
46+
47+
Certificates::PinningConfiguration PinningConfiguration;
48+
std::exception_ptr PinningValidationException = nullptr;
49+
50+
void CaptureSessionHandle(web::http::client::native_handle sessionHandle);
51+
void InstallStatusCallback(web::http::client::native_handle requestHandle);
52+
53+
static void _stdcall ValidatePinningConfigurationCallback(
54+
IN LPVOID hInternet,
55+
IN DWORD_PTR dwContext,
56+
IN DWORD dwInternetStatus,
57+
IN LPVOID lpvStatusInformation OPTIONAL,
58+
IN DWORD dwStatusInformationLength);
59+
};
60+
61+
std::shared_ptr<InvocationContext> GetClient(const utility::string_t& uri) const;
62+
63+
std::shared_ptr<InvocationContext> Post(const utility::string_t& uri, const web::json::value& body, const HttpRequestHeaders& headers = {}, const HttpRequestHeaders& authHeaders = {}) const;
64+
65+
std::shared_ptr<InvocationContext> Get(const utility::string_t& uri, const HttpRequestHeaders& headers = {}, const HttpRequestHeaders& authHeaders = {}) const;
66+
5467
std::optional<web::json::value> ValidateAndExtractResponse(const web::http::http_response& response) const;
5568

5669
std::optional<web::json::value> ExtractJsonResponse(const web::http::http_response& response) const;

src/AppInstallerCommonCore/pch.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include <wincrypt.h>
1919
#include <deliveryoptimization.h>
2020
#include <deliveryoptimizationerrors.h>
21+
#include <winhttp.h>
2122

2223
#include "TraceLogging.h"
2324

0 commit comments

Comments
 (0)