in Release/src/http/client/http_client_winhttp.cpp [962:1286]
void send_request(_In_ const std::shared_ptr<request_context>& request)
{
// First see if we need to be opened.
unsigned long error = open();
if (error != 0)
{
// DO NOT TOUCH the this pointer after completing the request
// This object could be freed along with the request as it could
// be the last reference to this object
request->report_error(error, _XPLATSTR("Open failed"));
return;
}
http_request& msg = request->m_request;
http_headers& headers = msg.headers();
std::shared_ptr<winhttp_request_context> winhttp_context =
std::static_pointer_cast<winhttp_request_context>(request);
std::weak_ptr<winhttp_request_context> weak_winhttp_context = winhttp_context;
proxy_info info;
bool proxy_info_required = false;
const auto& method = msg.method();
// stop injection of headers via method
// resource should be ok, since it's been encoded
// and host won't resolve
if (!::web::http::details::validate_method(method))
{
request->report_exception(http_exception("The method string is invalid."));
return;
}
if (m_proxy_auto_config)
{
WINHTTP_AUTOPROXY_OPTIONS autoproxy_options {};
if (m_proxy_auto_config_url.empty())
{
autoproxy_options.dwFlags = WINHTTP_AUTOPROXY_AUTO_DETECT;
autoproxy_options.dwAutoDetectFlags = WINHTTP_AUTO_DETECT_TYPE_DHCP | WINHTTP_AUTO_DETECT_TYPE_DNS_A;
}
else
{
autoproxy_options.dwFlags = WINHTTP_AUTOPROXY_CONFIG_URL;
autoproxy_options.lpszAutoConfigUrl = m_proxy_auto_config_url.c_str();
}
autoproxy_options.fAutoLogonIfChallenged = TRUE;
auto result = WinHttpGetProxyForUrl(m_hSession, m_uri.to_string().c_str(), &autoproxy_options, &info);
if (result)
{
proxy_info_required = true;
}
else
{
// Failure to download the auto-configuration script is not fatal. Fall back to the default proxy.
}
}
// Need to form uri path, query, and fragment for this request.
// Make sure to keep any path that was specified with the uri when the http_client was created.
const utility::string_t encoded_resource =
http::uri_builder(m_uri).append(msg.relative_uri()).to_uri().resource().to_string();
// Open the request.
winhttp_context->m_request_handle_context = new std::weak_ptr<winhttp_request_context>(winhttp_context);
winhttp_context->m_request_handle =
WinHttpOpenRequest(m_hConnection,
msg.method().c_str(),
encoded_resource.c_str(),
nullptr,
WINHTTP_NO_REFERER,
WINHTTP_DEFAULT_ACCEPT_TYPES,
WINHTTP_FLAG_ESCAPE_DISABLE | (m_secure ? WINHTTP_FLAG_SECURE : 0));
if (winhttp_context->m_request_handle == nullptr)
{
auto errorCode = GetLastError();
delete winhttp_context->m_request_handle_context;
winhttp_context->m_request_handle_context = 0;
request->report_error(errorCode, build_error_msg(errorCode, "WinHttpOpenRequest"));
return;
}
if (!WinHttpSetOption(winhttp_context->m_request_handle,
WINHTTP_OPTION_CONTEXT_VALUE,
&winhttp_context->m_request_handle_context,
sizeof(void*)))
{
auto errorCode = GetLastError();
delete winhttp_context->m_request_handle_context;
winhttp_context->m_request_handle_context = 0;
request->report_error(errorCode, build_error_msg(errorCode, "WinHttpSetOption request context"));
return;
}
if (proxy_info_required)
{
auto result = WinHttpSetOption(
winhttp_context->m_request_handle, WINHTTP_OPTION_PROXY, &info, sizeof(WINHTTP_PROXY_INFO));
if (!result)
{
auto errorCode = GetLastError();
request->report_error(errorCode, build_error_msg(errorCode, "Setting proxy options"));
return;
}
}
// If credentials are specified, use autologon policy: WINHTTP_AUTOLOGON_SECURITY_LEVEL_HIGH
// => default credentials are not used.
// Else, the default autologon policy WINHTTP_AUTOLOGON_SECURITY_LEVEL_MEDIUM will be used.
if (client_config().credentials().is_set())
{
DWORD data = WINHTTP_AUTOLOGON_SECURITY_LEVEL_HIGH;
auto result = WinHttpSetOption(
winhttp_context->m_request_handle, WINHTTP_OPTION_AUTOLOGON_POLICY, &data, sizeof(data));
if (!result)
{
auto errorCode = GetLastError();
request->report_error(
errorCode,
build_error_msg(errorCode, "Setting autologon policy to WINHTTP_AUTOLOGON_SECURITY_LEVEL_HIGH"));
return;
}
}
// Check to turn off server certificate verification.
DWORD ignoredCertificateValidationSteps = 0;
if (client_config().validate_certificates())
{
// if we are validating certificates, also turn on revocation checking
DWORD dwEnableSSLRevocationOpt = WINHTTP_ENABLE_SSL_REVOCATION;
if (!WinHttpSetOption(winhttp_context->m_request_handle,
WINHTTP_OPTION_ENABLE_FEATURE,
&dwEnableSSLRevocationOpt,
sizeof(dwEnableSSLRevocationOpt)))
{
auto errorCode = GetLastError();
request->report_error(errorCode, build_error_msg(errorCode, "Error enabling SSL revocation check"));
return;
}
// check if the user has overridden the desired Common Name with the host header
const auto hostHeader = headers.find(_XPLATSTR("Host"));
if (hostHeader != headers.end())
{
const auto& requestHost = hostHeader->second;
if (!utility::details::str_iequal(requestHost, m_uri.host()))
{
winhttp_context->install_custom_cn_check(requestHost);
ignoredCertificateValidationSteps = SECURITY_FLAG_IGNORE_CERT_CN_INVALID;
}
}
}
else
{
ignoredCertificateValidationSteps =
SECURITY_FLAG_IGNORE_UNKNOWN_CA | SECURITY_FLAG_IGNORE_CERT_DATE_INVALID |
SECURITY_FLAG_IGNORE_CERT_CN_INVALID | SECURITY_FLAG_IGNORE_CERT_WRONG_USAGE;
}
if (ignoredCertificateValidationSteps && !WinHttpSetOption(winhttp_context->m_request_handle,
WINHTTP_OPTION_SECURITY_FLAGS,
&ignoredCertificateValidationSteps,
sizeof(ignoredCertificateValidationSteps)))
{
auto errorCode = GetLastError();
request->report_error(errorCode,
build_error_msg(errorCode, "Setting ignore server certificate verification"));
return;
}
// WinHttpPAL does not currently provide these options
// See https://github.com/microsoft/WinHttpPAL/issues/1
#if !defined(CPPREST_FORCE_HTTP_CLIENT_WINHTTPPAL)
if (client_config().max_redirects() == 0)
{
// Disable auto redirects.
DWORD redirectPolicy = WINHTTP_OPTION_REDIRECT_POLICY_NEVER;
if (!WinHttpSetOption(winhttp_context->m_request_handle,
WINHTTP_OPTION_REDIRECT_POLICY,
&redirectPolicy,
sizeof(redirectPolicy)))
{
auto errorCode = GetLastError();
request->report_error(errorCode, build_error_msg(errorCode, "Setting redirect policy"));
return;
}
// Note, using WINHTTP_OPTION_DISABLE_FEATURE with WINHTTP_DISABLE_REDIRECTS here doesn't seem to work.
}
else
{
// Set max auto redirects.
// Add 1 to config value because WinHttp option counts the original request.
// And another 1 to enable the response (headers) of the rejected automatic redirect to be returned
// rather than reporting an error "WinHttpReceiveResponse: 12156: The HTTP redirect request failed".
DWORD maxRedirects = client_config().max_redirects() < MAXDWORD - 2
? static_cast<DWORD>(client_config().max_redirects() + 2)
: MAXDWORD;
// Therefore, effective max redirects
winhttp_context->m_remaining_redirects = maxRedirects - 2;
if (!WinHttpSetOption(winhttp_context->m_request_handle,
WINHTTP_OPTION_MAX_HTTP_AUTOMATIC_REDIRECTS,
&maxRedirects,
sizeof(maxRedirects)))
{
auto errorCode = GetLastError();
request->report_error(errorCode, build_error_msg(errorCode, "Setting max automatic redirects"));
return;
}
// (Dis)allow HTTPS to HTTP redirects.
DWORD redirectPolicy = client_config().https_to_http_redirects()
? WINHTTP_OPTION_REDIRECT_POLICY_ALWAYS
: WINHTTP_OPTION_REDIRECT_POLICY_DISALLOW_HTTPS_TO_HTTP;
if (!WinHttpSetOption(winhttp_context->m_request_handle,
WINHTTP_OPTION_REDIRECT_POLICY,
&redirectPolicy,
sizeof(redirectPolicy)))
{
auto errorCode = GetLastError();
request->report_error(errorCode, build_error_msg(errorCode, "Setting redirect policy"));
return;
}
}
#endif
size_t content_length;
try
{
content_length = msg._get_impl()->_get_content_length_and_set_compression();
}
catch (...)
{
request->report_exception(std::current_exception());
return;
}
if (content_length > 0)
{
if (msg.method() == http::methods::GET || msg.method() == http::methods::HEAD)
{
request->report_exception(http_exception(get_with_body_err_msg));
return;
}
// There is a request body that needs to be transferred.
if (content_length == (std::numeric_limits<size_t>::max)())
{
// The content length is not set and the application set a stream. This is an
// indication that we will use transfer encoding chunked. We still want to
// know that stream's effective length if possible for memory efficiency.
winhttp_context->m_bodyType = transfer_encoding_chunked;
winhttp_context->m_remaining_to_write = msg._get_impl()->_get_stream_length();
}
else
{
// While we won't be transfer-encoding the data, we will write it in portions.
winhttp_context->m_bodyType = content_length_chunked;
winhttp_context->m_remaining_to_write = content_length;
}
}
utility::string_t flattened_headers = web::http::details::flatten_http_headers(headers);
if (winhttp_context->m_request.method() == http::methods::GET)
{
// Prepare to request a compressed response from the server if necessary.
flattened_headers += winhttp_context->get_compression_header();
}
// Add headers.
if (!flattened_headers.empty())
{
if (!WinHttpAddRequestHeaders(winhttp_context->m_request_handle,
flattened_headers.c_str(),
static_cast<DWORD>(flattened_headers.length()),
WINHTTP_ADDREQ_FLAG_ADD))
{
auto errorCode = GetLastError();
request->report_error(errorCode, build_error_msg(errorCode, "WinHttpAddRequestHeaders"));
return;
}
}
// Register for notification on cancellation to abort this request.
if (msg._cancellation_token() != pplx::cancellation_token::none())
{
// cancellation callback is unregistered when request is completed.
winhttp_context->m_cancellationRegistration =
msg._cancellation_token().register_callback([weak_winhttp_context]() {
// Call the WinHttpSendRequest API after WinHttpCloseHandle will give invalid handle error and we
// throw this exception. Call the cleanup to make the m_request_handle as nullptr, otherwise,
// Application Verifier will give AV exception on m_request_handle.
auto lock = weak_winhttp_context.lock();
if (!lock) return;
lock->cleanup();
});
}
// Call the callback function of user customized options.
try
{
client_config().invoke_nativehandle_options(winhttp_context->m_request_handle);
}
catch (...)
{
request->report_exception(std::current_exception());
return;
}
// Only need to cache the request body if user specified and the request stream doesn't support seeking.
if (winhttp_context->m_bodyType != no_body && client_config().buffer_request() &&
!winhttp_context->_get_readbuffer().can_seek())
{
winhttp_context->m_readBufferCopy =
::utility::details::make_unique<::concurrency::streams::container_buffer<std::vector<uint8_t>>>();
}
_start_request_send(winhttp_context, content_length);
return;
}