Example #1
0
 bool request::validate()
 {
   return
     validate_method()
     && validate_length()
     && find_and_validate_format();
 }
Example #2
0
    // Start sending request.
    void send_request(_In_ const std::shared_ptr<request_context> &request)
    {
        http_request &msg = request->m_request;
        auto winrt_context = std::static_pointer_cast<winrt_request_context>(request);

        if (!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.decrypt();
                password = password_plaintext->c_str();
            }
            if (proxy_cred.is_set())
            {
                proxy_username = proxy_cred.username();
                proxy_password_plaintext = proxy_cred.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;
        }

        const auto timeout = config.timeout();
        const int secs = static_cast<int>(timeout.count());
        hr = winrt_context->m_hRequest->SetProperty(XHR_PROP_TIMEOUT, secs * 1000);
        if (FAILED(hr))
        {
            request->report_error(hr, L"Failure to set HTTP request properties");
            return;
        }

        // 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>(request).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.call_user_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));
                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();
            });
        }
    }