in Release/src/http/client/http_client_winrt.cpp [387:556]
virtual void send_request(_In_ const std::shared_ptr<request_context>& request) override
{
http_request& msg = request->m_request;
auto winrt_context = std::static_pointer_cast<winrt_request_context>(request);
if (!web::http::details::validate_method(msg.method()))
{
request->report_exception(http_exception(L"The method string is invalid."));
return;
}
if (msg.method() == http::methods::TRCE)
{
// Not supported by WinInet. Generate a more specific exception than what WinInet does.
request->report_exception(http_exception(L"TRACE is not supported"));
return;
}
const size_t content_length = msg._get_impl()->_get_content_length();
if (content_length == (std::numeric_limits<size_t>::max)())
{
// IXHR2 does not allow transfer encoding chunked. So the user is expected to set the content length
request->report_exception(http_exception(L"Content length is not specified in the http headers"));
return;
}
// Start sending HTTP request.
HRESULT hr = CoCreateInstance(__uuidof(FreeThreadedXMLHTTP60),
nullptr,
CLSCTX_INPROC,
__uuidof(IXMLHTTPRequest2),
reinterpret_cast<void**>(winrt_context->m_hRequest.GetAddressOf()));
if (FAILED(hr))
{
request->report_error(hr, L"Failure to create IXMLHTTPRequest2 instance");
return;
}
utility::string_t encoded_resource = http::uri_builder(m_uri).append(msg.relative_uri()).to_string();
const auto& config = client_config();
const auto& client_cred = config.credentials();
const auto& proxy = config.proxy();
const auto& proxy_cred = proxy.credentials();
if (!proxy.is_default())
{
request->report_exception(http_exception(L"Only a default proxy server is supported"));
return;
}
// New scope to ensure plain text password is cleared as soon as possible.
{
utility::string_t username, proxy_username;
const utility::char_t* password = nullptr;
const utility::char_t* proxy_password = nullptr;
::web::details::plaintext_string password_plaintext, proxy_password_plaintext;
if (client_cred.is_set())
{
username = client_cred.username();
password_plaintext = client_cred._internal_decrypt();
password = password_plaintext->c_str();
}
if (proxy_cred.is_set())
{
proxy_username = proxy_cred.username();
proxy_password_plaintext = proxy_cred._internal_decrypt();
proxy_password = proxy_password_plaintext->c_str();
}
hr = winrt_context->m_hRequest->Open(msg.method().c_str(),
encoded_resource.c_str(),
Make<HttpRequestCallback>(winrt_context).Get(),
username.c_str(),
password,
proxy_username.c_str(),
proxy_password);
}
if (FAILED(hr))
{
request->report_error(hr, L"Failure to open HTTP request");
return;
}
// Suppress automatic prompts for user credentials, since they are already provided.
hr = winrt_context->m_hRequest->SetProperty(XHR_PROP_NO_CRED_PROMPT, TRUE);
if (FAILED(hr))
{
request->report_error(hr, L"Failure to set no credentials prompt property");
return;
}
// Set timeout.
ULONGLONG timeout = static_cast<ULONGLONG>(config.timeout<std::chrono::milliseconds>().count());
timeout = (std::max<decltype(timeout)>)(timeout, (std::numeric_limits<decltype(timeout)>::min)() + 1);
hr = winrt_context->m_hRequest->SetProperty(XHR_PROP_TIMEOUT, timeout);
if (FAILED(hr))
{
request->report_error(hr, L"Failure to set HTTP request properties");
return;
}
// If XHR_PROP_ONDATA_NEVER is defined in ixmlhttprequest.h or msxml6.h utilize it.
// Specifies never to call OnDataAvailable improving performance and we
// already don't use OnDataAvaliable anyway.
#ifdef XHR_PROP_ONDATA_NEVER
hr = winrt_context->m_hRequest->SetProperty(XHR_PROP_ONDATA_THRESHOLD, XHR_PROP_ONDATA_NEVER);
if (FAILED(hr))
{
request->report_error(hr, L"Failure to turn off on data threshold");
}
#endif
// Add headers.
for (const auto& hdr : msg.headers())
{
winrt_context->m_hRequest->SetRequestHeader(hdr.first.c_str(), hdr.second.c_str());
}
// Set response stream.
hr = winrt_context->m_hRequest->SetCustomResponseStream(Make<IResponseStream>(winrt_context).Get());
if (FAILED(hr))
{
request->report_error(hr, L"Failure to set HTTP response stream");
return;
}
// Call the callback function of user customized options
try
{
config.invoke_nativehandle_options(winrt_context->m_hRequest.Get());
}
catch (...)
{
request->report_exception(std::current_exception());
return;
}
if (content_length == 0)
{
hr = winrt_context->m_hRequest->Send(nullptr, 0);
}
else
{
if (msg.method() == http::methods::GET || msg.method() == http::methods::HEAD)
{
request->report_exception(http_exception(get_with_body_err_msg));
return;
}
hr = winrt_context->m_hRequest->Send(Make<IRequestStream>(winrt_context, content_length).Get(),
content_length);
}
if (FAILED(hr))
{
request->report_error(hr, L"Failure to send HTTP request");
return;
}
// Register for notification on cancellation to abort this request.
if (msg._cancellation_token() != pplx::cancellation_token::none())
{
auto requestHandle = winrt_context->m_hRequest;
// cancellation callback is unregistered when request is completed.
winrt_context->m_cancellationRegistration =
msg._cancellation_token().register_callback([requestHandle]() { requestHandle->Abort(); });
}
}