55#include < AppInstallerRuntime.h>
66#include < winget/HttpClientHelper.h>
77#include < winget/NetworkSettings.h>
8- #include < winhttp.h>
98
109namespace 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)
0 commit comments