virtual void send_request()

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