void send_request()

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;
    }