in Release/src/http/client/http_client_winhttp.cpp [1800:1944]
static bool handle_authentication_failure(HINTERNET hRequestHandle,
const std::shared_ptr<winhttp_request_context>& p_request_context,
_In_ DWORD error = 0)
{
http_request& request = p_request_context->m_request;
_ASSERTE(p_request_context->m_response.status_code() == status_codes::Unauthorized ||
p_request_context->m_response.status_code() == status_codes::ProxyAuthRequired ||
error == ERROR_WINHTTP_RESEND_REQUEST);
// Check if the saved read position is valid
auto rdpos = p_request_context->m_startingPosition;
if (rdpos != static_cast<std::char_traits<uint8_t>::pos_type>(std::char_traits<uint8_t>::eof()))
{
// Try to seek back to the saved read position
auto rbuf = p_request_context->_get_readbuffer();
if (rbuf.seekpos(rdpos, std::ios::ios_base::in) != rdpos)
{
return false;
}
// We successfully seeked back; now reset the compression state, if any, to match
if (p_request_context->m_request.compressor())
{
try
{
p_request_context->m_request.compressor()->reset();
}
catch (...)
{
return false;
}
}
}
p_request_context->m_compression_state = winhttp_request_context::compression_state();
// If we got ERROR_WINHTTP_RESEND_REQUEST, the response header is not available,
// we cannot call WinHttpQueryAuthSchemes and WinHttpSetCredentials.
if (error != ERROR_WINHTTP_RESEND_REQUEST)
{
// Obtain the supported and preferred schemes.
DWORD dwSupportedSchemes;
DWORD dwFirstScheme;
DWORD dwAuthTarget;
if (!WinHttpQueryAuthSchemes(hRequestHandle, &dwSupportedSchemes, &dwFirstScheme, &dwAuthTarget))
{
// This will return the authentication failure to the user, without reporting fatal errors
return false;
}
DWORD dwSelectedScheme = ChooseAuthScheme(dwSupportedSchemes);
if (dwSelectedScheme == 0)
{
// This will return the authentication failure to the user, without reporting fatal errors
return false;
}
credentials cred;
if (dwAuthTarget == WINHTTP_AUTH_TARGET_SERVER && !p_request_context->m_server_authentication_tried)
{
cred = p_request_context->m_http_client->client_config().credentials();
p_request_context->m_server_authentication_tried = true;
}
else if (dwAuthTarget == WINHTTP_AUTH_TARGET_PROXY)
{
bool is_redirect = false;
try
{
web::uri current_uri(get_request_url(hRequestHandle));
is_redirect = p_request_context->m_request.absolute_uri().to_string() != current_uri.to_string();
}
catch (const std::exception&)
{
}
// If we have been redirected, then WinHttp needs the proxy credentials again to make the next request
// leg (which may be on a different server)
if (is_redirect || !p_request_context->m_proxy_authentication_tried)
{
cred = p_request_context->m_http_client->client_config().proxy().credentials();
p_request_context->m_proxy_authentication_tried = true;
}
}
// No credentials found so can't resend.
if (!cred.is_set())
{
return false;
}
// New scope to ensure plaintext password is cleared as soon as possible.
{
auto password = cred._internal_decrypt();
if (!WinHttpSetCredentials(hRequestHandle,
dwAuthTarget,
dwSelectedScheme,
cred.username().c_str(),
password->c_str(),
nullptr))
{
return false;
}
}
}
// Reset the request body type since it might have already started sending.
size_t content_length;
try
{
content_length = request._get_impl()->_get_content_length_and_set_compression();
}
catch (...)
{
return false;
}
if (content_length > 0)
{
// There is a request body that needs to be transferred.
if (content_length == (std::numeric_limits<size_t>::max)())
{
// The content length is unknown and the application set a stream. This is an
// indication that we will need to chunk the data.
p_request_context->m_bodyType = transfer_encoding_chunked;
p_request_context->m_remaining_to_write = request._get_impl()->_get_stream_length();
}
else
{
// While we won't be transfer-encoding the data, we will write it in portions.
p_request_context->m_bodyType = content_length_chunked;
p_request_context->m_remaining_to_write = content_length;
}
}
else
{
p_request_context->m_bodyType = no_body;
}
// We're good.
winhttp_client* winclnt = reinterpret_cast<winhttp_client*>(p_request_context->m_http_client.get());
winclnt->_start_request_send(p_request_context, content_length);
// We will not complete the request. Instead wait for the response to the request that was resent
return true;
}