static bool handle_authentication_failure()

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