Beispiel #1
0
        public static void AddResponseCookiesToContainer(WinHttpRequestState state)
        {
            HttpRequestMessage request         = state.RequestMessage;
            SafeWinHttpHandle  requestHandle   = state.RequestHandle;
            CookieContainer    cookieContainer = state.Handler.CookieContainer;

            Debug.Assert(state.Handler.CookieUsePolicy == CookieUsePolicy.UseSpecifiedCookieContainer);
            Debug.Assert(cookieContainer != null);

            // Get 'Set-Cookie' headers from response.
            char[] buffer = null;
            uint   index  = 0;
            string cookieHeader;

            WinHttpTraceHelper.Trace("WINHTTP_QUERY_SET_COOKIE");
            while (WinHttpResponseParser.GetResponseHeader(
                       requestHandle, Interop.WinHttp.WINHTTP_QUERY_SET_COOKIE, ref buffer, ref index, out cookieHeader))
            {
                WinHttpTraceHelper.Trace(cookieHeader);
                try
                {
                    cookieContainer.SetCookies(request.RequestUri, cookieHeader);
                    WinHttpTraceHelper.Trace(cookieHeader);
                }
                catch (CookieException)
                {
                    // We ignore malformed cookies in the response.
                    WinHttpTraceHelper.Trace("Ignoring invalid cookie: {0}", cookieHeader);
                }
            }
        }
        private static void OnRequestReadComplete(WinHttpRequestState state, uint bytesRead)
        {
            Debug.Assert(state != null, "OnRequestReadComplete: state is null");
            Debug.Assert(state.TcsReadFromResponseStream != null, "TcsReadFromResponseStream is null");
            Debug.Assert(!state.TcsReadFromResponseStream.Task.IsCompleted, "TcsReadFromResponseStream.Task is completed");

            state.DisposeCtrReadFromResponseStream();

            // If we read to the end of the stream and we're using 'Content-Length' semantics on the response body,
            // then verify we read at least the number of bytes required.
            if (bytesRead == 0 &&
                state.ExpectedBytesToRead.HasValue &&
                state.CurrentBytesRead < state.ExpectedBytesToRead.Value)
            {
                state.TcsReadFromResponseStream.TrySetException(
                    new IOException(string.Format(
                                        "net_http_io_read_incomplete: {0} - {1}",
                                        state.ExpectedBytesToRead.Value,
                                        state.CurrentBytesRead)));
            }
            else
            {
                state.CurrentBytesRead += (long)bytesRead;
                state.TcsReadFromResponseStream.TrySetResult((int)bytesRead);
            }
        }
        public static void WinHttpCallback(
            IntPtr handle,
            IntPtr context,
            uint internetStatus,
            IntPtr statusInformation,
            uint statusInformationLength)
        {
            WinHttpTraceHelper.TraceCallbackStatus("WinHttpCallback", handle, context, internetStatus);

            if (Environment.HasShutdownStarted)
            {
                WinHttpTraceHelper.Trace("WinHttpCallback: Environment.HasShutdownStarted returned True");
                return;
            }

            if (context == IntPtr.Zero)
            {
                return;
            }

            WinHttpRequestState state = WinHttpRequestState.FromIntPtr(context);

            Debug.Assert(state != null, "WinHttpCallback must have a non-null state object");

            RequestCallback(handle, state, internetStatus, statusInformation, statusInformationLength);
        }
        private static void OnRequestWriteComplete(WinHttpRequestState state)
        {
            Debug.Assert(state != null, "OnRequestWriteComplete: state is null");
            Debug.Assert(state.TcsInternalWriteDataToRequestStream != null, "TcsInternalWriteDataToRequestStream is null");
            Debug.Assert(!state.TcsInternalWriteDataToRequestStream.Task.IsCompleted, "TcsInternalWriteDataToRequestStream.Task is completed");

            state.TcsInternalWriteDataToRequestStream.TrySetResult(true);
        }
        private static void OnRequestReceiveResponseHeadersComplete(WinHttpRequestState state)
        {
            Debug.Assert(state != null, "OnRequestReceiveResponseHeadersComplete: state is null");
            Debug.Assert(state.TcsReceiveResponseHeaders != null, "TcsReceiveResponseHeaders is null");
            Debug.Assert(!state.TcsReceiveResponseHeaders.Task.IsCompleted, "TcsReceiveResponseHeaders.Task is completed");

            state.TcsReceiveResponseHeaders.TrySetResult(true);
        }
        private static void OnRequestSendRequestComplete(WinHttpRequestState state)
        {
            Debug.Assert(state != null, "OnRequestSendRequestComplete: state is null");
            Debug.Assert(state.TcsSendRequest != null, "OnRequestSendRequestComplete: TcsSendRequest is null");
            Debug.Assert(!state.TcsSendRequest.Task.IsCompleted, "OnRequestSendRequestComplete: TcsSendRequest.Task is completed");

            state.TcsSendRequest.TrySetResult(true);
        }
        private static void OnRequestDataAvailable(WinHttpRequestState state, int bytesAvailable)
        {
            Debug.Assert(state != null, "OnRequestDataAvailable: state is null");
            Debug.Assert(state.TcsQueryDataAvailable != null, "TcsQueryDataAvailable is null");
            Debug.Assert(!state.TcsQueryDataAvailable.Task.IsCompleted, "TcsQueryDataAvailable.Task is completed");

            state.TcsQueryDataAvailable.TrySetResult(bytesAvailable);
        }
        private static void OnRequestHandleClosing(WinHttpRequestState state)
        {
            Debug.Assert(state != null, "OnRequestSendRequestComplete: state is null");

            // This is the last notification callback that WinHTTP will send. Therefore, we can
            // now explicitly dispose the state object which will free its corresponding GCHandle.
            // This will then allow the state object to be garbage collected.
            state.Dispose();
        }
Beispiel #9
0
        public void PreAuthenticateRequest(WinHttpRequestState state, uint proxyAuthScheme)
        {
            // Set proxy credentials if we have them.
            // If a proxy authentication challenge was responded to, reset
            // those credentials before each SendRequest, because the proxy
            // may require re-authentication after responding to a 401 or
            // to a redirect. If you don't, you can get into a
            // 407-401-407-401- loop.
            if (proxyAuthScheme != 0)
            {
                ICredentials proxyCredentials;
                Uri          proxyUri;
                if (state.Proxy != null)
                {
                    proxyCredentials = state.Proxy.Credentials;
                    proxyUri         = state.Proxy.GetProxy(state.RequestMessage.RequestUri);
                }
                else
                {
                    proxyCredentials = state.DefaultProxyCredentials;
                    proxyUri         = state.RequestMessage.RequestUri;
                }

                SetWinHttpCredential(
                    state.RequestHandle,
                    proxyCredentials,
                    proxyUri,
                    proxyAuthScheme,
                    Interop.WinHttp.WINHTTP_AUTH_TARGET_PROXY);
            }

            // Apply pre-authentication headers for server authentication?
            if (state.PreAuthenticate)
            {
                uint authScheme;
                NetworkCredential serverCredentials;
                if (GetServerCredentialsFromCache(
                        state.RequestMessage.RequestUri,
                        out authScheme,
                        out serverCredentials))
                {
                    SetWinHttpCredential(
                        state.RequestHandle,
                        serverCredentials,
                        state.RequestMessage.RequestUri,
                        authScheme,
                        Interop.WinHttp.WINHTTP_AUTH_TARGET_SERVER);
                    state.LastStatusCode = HttpStatusCode.Unauthorized; // Remember we already set the creds.
                }

                // No cached credential to use at this time. The request will first go out with no
                // 'Authorization' header. Later, if a 401 occurs, we will be able to cache the credential
                // since we will then know the proper auth scheme to use.
                //
                // TODO: Issue #2165. Adding logging to highlight the 'cache miss'.
            }
        }
        private static void OnRequestRedirect(WinHttpRequestState state, Uri redirectUri)
        {
            Debug.Assert(state != null, "OnRequestRedirect: state is null");
            Debug.Assert(redirectUri != null, "OnRequestRedirect: redirectUri is null");
            Debug.Assert(state.TcsReceiveResponseHeaders != null, "TcsReceiveResponseHeaders is null");
            Debug.Assert(!state.TcsReceiveResponseHeaders.Task.IsCompleted, "TcsReceiveResponseHeaders.Task is completed");

            // If we're manually handling cookies, we need to reset them based on the new URI.
            if (state.Handler.CookieUsePolicy == CookieUsePolicy.UseSpecifiedCookieContainer)
            {
                // Add any cookies that may have arrived with redirect response.
                WinHttpCookieContainerAdapter.AddResponseCookiesToContainer(state);

                // Reset cookie request headers based on redirectUri.
                WinHttpCookieContainerAdapter.ResetCookieRequestHeaders(state, redirectUri);
            }

            state.RequestMessage.RequestUri = redirectUri;

            // Redirection to a new uri may require a new connection through a potentially different proxy.
            // If so, we will need to respond to additional 407 proxy auth demands and re-attach any
            // proxy credentials. The ProcessResponse() method looks at the state.LastStatusCode
            // before attaching proxy credentials and marking the HTTP request to be re-submitted.
            // So we need to reset the LastStatusCode remembered. Otherwise, it will see additional 407
            // responses as an indication that proxy auth failed and won't retry the HTTP request.
            if (state.LastStatusCode == HttpStatusCode.ProxyAuthenticationRequired)
            {
                state.LastStatusCode = 0;
            }

            // For security reasons, we drop the server credential if it is a
            // NetworkCredential.  But we allow credentials in a CredentialCache
            // since they are specifically tied to URI's.
            if (!(state.ServerCredentials is CredentialCache))
            {
                state.ServerCredentials = null;
            }
        }
Beispiel #11
0
        public static void ResetCookieRequestHeaders(WinHttpRequestState state, Uri redirectUri)
        {
            SafeWinHttpHandle requestHandle = state.RequestHandle;

            Debug.Assert(state.Handler.CookieUsePolicy == CookieUsePolicy.UseSpecifiedCookieContainer);

            // Clear cookies.
            if (!Interop.WinHttp.WinHttpAddRequestHeaders(
                    requestHandle,
                    CookieHeaderNameWithColon,
                    (uint)CookieHeaderNameWithColon.Length,
                    Interop.WinHttp.WINHTTP_ADDREQ_FLAG_REPLACE))
            {
                int lastError = Marshal.GetLastWin32Error();
                if (lastError != Interop.WinHttp.ERROR_WINHTTP_HEADER_NOT_FOUND)
                {
                    throw WinHttpException.CreateExceptionUsingError(lastError);
                }
            }

            // Re-add cookies. The GetCookieHeader() method will return the correct set of
            // cookies based on the redirectUri.
            string cookieHeader = GetCookieHeader(redirectUri, state.Handler.CookieContainer);

            if (!string.IsNullOrEmpty(cookieHeader))
            {
                if (!Interop.WinHttp.WinHttpAddRequestHeaders(
                        requestHandle,
                        cookieHeader,
                        (uint)cookieHeader.Length,
                        Interop.WinHttp.WINHTTP_ADDREQ_FLAG_ADD))
                {
                    WinHttpException.ThrowExceptionUsingLastError();
                }
            }
        }
        private static void RequestCallback(
            IntPtr handle,
            WinHttpRequestState state,
            uint internetStatus,
            IntPtr statusInformation,
            uint statusInformationLength)
        {
            try
            {
                switch (internetStatus)
                {
                case Interop.WinHttp.WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING:
                    OnRequestHandleClosing(state);
                    return;

                case Interop.WinHttp.WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE:
                    OnRequestSendRequestComplete(state);
                    return;

                case Interop.WinHttp.WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE:
                    Debug.Assert(statusInformationLength == Marshal.SizeOf <int>());
                    int bytesAvailable = Marshal.ReadInt32(statusInformation);
                    OnRequestDataAvailable(state, bytesAvailable);
                    return;

                case Interop.WinHttp.WINHTTP_CALLBACK_STATUS_READ_COMPLETE:
                    OnRequestReadComplete(state, statusInformationLength);
                    return;

                case Interop.WinHttp.WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE:
                    OnRequestWriteComplete(state);
                    return;

                case Interop.WinHttp.WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE:
                    OnRequestReceiveResponseHeadersComplete(state);
                    return;

                case Interop.WinHttp.WINHTTP_CALLBACK_STATUS_REDIRECT:
                    string redirectUriString = Marshal.PtrToStringUni(statusInformation);
                    var    redirectUri       = new Uri(redirectUriString);
                    OnRequestRedirect(state, redirectUri);
                    return;

                case Interop.WinHttp.WINHTTP_CALLBACK_STATUS_SENDING_REQUEST:
                    OnRequestSendingRequest(state);
                    return;

                case Interop.WinHttp.WINHTTP_CALLBACK_STATUS_REQUEST_ERROR:
                    Debug.Assert(
                        statusInformationLength == Marshal.SizeOf <Interop.WinHttp.WINHTTP_ASYNC_RESULT>(),
                        "RequestCallback: statusInformationLength=" + statusInformationLength +
                        " must be sizeof(WINHTTP_ASYNC_RESULT)=" + Marshal.SizeOf <Interop.WinHttp.WINHTTP_ASYNC_RESULT>());

                    var asyncResult = Marshal.PtrToStructure <Interop.WinHttp.WINHTTP_ASYNC_RESULT>(statusInformation);
                    OnRequestError(state, asyncResult);
                    return;

                default:
                    return;
                }
            }
            catch (Exception ex)
            {
                Interop.WinHttp.WinHttpCloseHandle(handle);
                state.SavedException = ex;
            }
        }
        private static void OnRequestError(WinHttpRequestState state, Interop.WinHttp.WINHTTP_ASYNC_RESULT asyncResult)
        {
            WinHttpTraceHelper.TraceAsyncError("OnRequestError", asyncResult);

            Debug.Assert(state != null, "OnRequestError: state is null");

            Exception innerException = WinHttpException.CreateExceptionUsingError(unchecked ((int)asyncResult.dwError));

            switch (unchecked ((uint)asyncResult.dwResult.ToInt32()))
            {
            case Interop.WinHttp.API_SEND_REQUEST:
                state.TcsSendRequest.TrySetException(innerException);
                break;

            case Interop.WinHttp.API_RECEIVE_RESPONSE:
                if (asyncResult.dwError == Interop.WinHttp.ERROR_WINHTTP_RESEND_REQUEST)
                {
                    state.RetryRequest = true;
                    state.TcsReceiveResponseHeaders.TrySetResult(false);
                }
                else if (asyncResult.dwError == Interop.WinHttp.ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED)
                {
                    // WinHttp will automatically drop any client SSL certificates that we
                    // have pre-set into the request handle including the NULL certificate
                    // (which means we have no certs to send). For security reasons, we don't
                    // allow the certificate to be re-applied. But we need to tell WinHttp
                    // explicitly that we don't have any certificate to send.
                    Debug.Assert(state.RequestHandle != null, "OnRequestError: state.RequestHandle is null");
                    WinHttpHandler.SetNoClientCertificate(state.RequestHandle);
                    state.RetryRequest = true;
                    state.TcsReceiveResponseHeaders.TrySetResult(false);
                }
                else if (asyncResult.dwError == Interop.WinHttp.ERROR_WINHTTP_OPERATION_CANCELLED)
                {
                    state.TcsReceiveResponseHeaders.TrySetCanceled(state.CancellationToken);
                }
                else
                {
                    state.TcsReceiveResponseHeaders.TrySetException(innerException);
                }
                break;

            case Interop.WinHttp.API_QUERY_DATA_AVAILABLE:
                if (asyncResult.dwError == Interop.WinHttp.ERROR_WINHTTP_OPERATION_CANCELLED)
                {
                    // TODO: Issue #2165. We need to pass in the cancellation token from the
                    // user's ReadAsync() call into the TrySetCanceled().
                    Debug.WriteLine("RequestCallback: QUERY_DATA_AVAILABLE - ERROR_WINHTTP_OPERATION_CANCELLED");
                    state.TcsQueryDataAvailable.TrySetCanceled();
                }
                else
                {
                    state.TcsQueryDataAvailable.TrySetException(
                        new IOException("net_http_io_read", innerException));
                }
                break;

            case Interop.WinHttp.API_READ_DATA:
                state.DisposeCtrReadFromResponseStream();

                if (asyncResult.dwError == Interop.WinHttp.ERROR_WINHTTP_OPERATION_CANCELLED)
                {
                    // TODO: Issue #2165. We need to pass in the cancellation token from the
                    // user's ReadAsync() call into the TrySetCanceled().
                    Debug.WriteLine("RequestCallback: API_READ_DATA - ERROR_WINHTTP_OPERATION_CANCELLED");
                    state.TcsReadFromResponseStream.TrySetCanceled();
                }
                else
                {
                    state.TcsReadFromResponseStream.TrySetException(
                        new IOException("net_http_io_read", innerException));
                }
                break;

            case Interop.WinHttp.API_WRITE_DATA:
                if (asyncResult.dwError == Interop.WinHttp.ERROR_WINHTTP_OPERATION_CANCELLED)
                {
                    // TODO: Issue #2165. We need to pass in the cancellation token from the
                    // user's WriteAsync() call into the TrySetCanceled().
                    Debug.WriteLine("RequestCallback: API_WRITE_DATA - ERROR_WINHTTP_OPERATION_CANCELLED");
                    state.TcsInternalWriteDataToRequestStream.TrySetCanceled();
                }
                else
                {
                    state.TcsInternalWriteDataToRequestStream.TrySetException(
                        new IOException("net_http_io_write", innerException));
                }
                break;

            default:
                Debug.Fail(
                    "OnRequestError: Result (" + asyncResult.dwResult + ") is not expected.",
                    "Error code: " + asyncResult.dwError + " (" + innerException.Message + ")");
                break;
            }
        }
Beispiel #14
0
 internal WinHttpRequestStream(WinHttpRequestState state, bool chunkedMode)
 {
     _state       = state;
     _chunkedMode = chunkedMode;
 }
        private static void OnRequestSendingRequest(WinHttpRequestState state)
        {
            Debug.Assert(state != null, "OnRequestSendingRequest: state is null");
            Debug.Assert(state.RequestHandle != null, "OnRequestSendingRequest: state.RequestHandle is null");

            if (state.RequestMessage.RequestUri.Scheme != UriScheme.Https)
            {
                // Not SSL/TLS.
                return;
            }

            // Grab the channel binding token (CBT) information from the request handle and put it into
            // the TransportContext object.
            state.TransportContext.SetChannelBinding(state.RequestHandle);

            if (state.ServerCertificateValidationCallback != null)
            {
                IntPtr certHandle     = IntPtr.Zero;
                uint   certHandleSize = (uint)IntPtr.Size;

                if (!Interop.WinHttp.WinHttpQueryOption(
                        state.RequestHandle,
                        Interop.WinHttp.WINHTTP_OPTION_SERVER_CERT_CONTEXT,
                        ref certHandle,
                        ref certHandleSize))
                {
                    int lastError = Marshal.GetLastWin32Error();
                    throw WinHttpException.CreateExceptionUsingError(lastError);
                }

                // Create a managed wrapper around the certificate handle. Since this results in duplicating
                // the handle, we will close the original handle after creating the wrapper.
                var serverCertificate = new X509Certificate2(certHandle);
                Interop.Crypt32.CertFreeCertificateContext(certHandle);

                X509Chain       chain = null;
                SslPolicyErrors sslPolicyErrors;

                try
                {
                    WinHttpCertificateHelper.BuildChain(
                        serverCertificate,
                        state.RequestMessage.RequestUri.Host,
                        state.CheckCertificateRevocationList,
                        out chain,
                        out sslPolicyErrors);

                    bool result = state.ServerCertificateValidationCallback(
                        state.RequestMessage,
                        serverCertificate,
                        chain,
                        sslPolicyErrors);
                    if (!result)
                    {
                        throw WinHttpException.CreateExceptionUsingError(
                                  (int)Interop.WinHttp.ERROR_WINHTTP_SECURE_FAILURE);
                    }
                }
                finally
                {
                    if (chain != null)
                    {
                        chain.Dispose();
                    }

                    serverCertificate.Dispose();
                }
            }
        }
Beispiel #16
0
        public static HttpResponseMessage CreateResponseMessage(
            WinHttpRequestState state,
            bool doManualDecompressionCheck)
        {
            HttpRequestMessage request         = state.RequestMessage;
            SafeWinHttpHandle  requestHandle   = state.RequestHandle;
            CookieUsePolicy    cookieUsePolicy = state.Handler.CookieUsePolicy;
            CookieContainer    cookieContainer = state.Handler.CookieContainer;
            var  response             = new HttpResponseMessage();
            bool stripEncodingHeaders = false;

            // Create a single buffer to use for all subsequent WinHttpQueryHeaders string interop calls.
            // This buffer is the length needed for WINHTTP_QUERY_RAW_HEADERS_CRLF, which includes the status line
            // and all headers separated by CRLF, so it should be large enough for any individual status line or header queries.
            int bufferLength = GetResponseHeaderCharBufferLength(requestHandle, Interop.WinHttp.WINHTTP_QUERY_RAW_HEADERS_CRLF);

            char[] buffer = new char[bufferLength];

            // Get HTTP version, status code, reason phrase from the response headers.

            int versionLength = GetResponseHeader(requestHandle, Interop.WinHttp.WINHTTP_QUERY_VERSION, buffer);

            response.Version =
                CharArrayHelpers.EqualsOrdinalAsciiIgnoreCase("HTTP/1.1", buffer, 0, versionLength) ? HttpVersion.Version11 :
                CharArrayHelpers.EqualsOrdinalAsciiIgnoreCase("HTTP/1.0", buffer, 0, versionLength) ? HttpVersion.Version10 :
                HttpVersion.Unknown;

            response.StatusCode = (HttpStatusCode)GetResponseHeaderNumberInfo(
                requestHandle,
                Interop.WinHttp.WINHTTP_QUERY_STATUS_CODE);

            int reasonPhraseLength = GetResponseHeader(requestHandle, Interop.WinHttp.WINHTTP_QUERY_STATUS_TEXT, buffer);

            response.ReasonPhrase = reasonPhraseLength > 0 ?
                                    GetReasonPhrase(response.StatusCode, buffer, reasonPhraseLength) :
                                    string.Empty;

            // Create response stream and wrap it in a StreamContent object.
            var responseStream = new WinHttpResponseStream(requestHandle, state);

            state.RequestHandle = null; // ownership successfully transfered to WinHttpResponseStram.
            Stream decompressedStream = responseStream;

            if (doManualDecompressionCheck)
            {
                int contentEncodingStartIndex = 0;
                int contentEncodingLength     = GetResponseHeader(
                    requestHandle,
                    Interop.WinHttp.WINHTTP_QUERY_CONTENT_ENCODING,
                    buffer);

                CharArrayHelpers.Trim(buffer, ref contentEncodingStartIndex, ref contentEncodingLength);

                if (contentEncodingLength > 0)
                {
                    if (CharArrayHelpers.EqualsOrdinalAsciiIgnoreCase(
                            EncodingNameGzip, buffer, contentEncodingStartIndex, contentEncodingLength))
                    {
                        decompressedStream   = new GZipStream(responseStream, CompressionMode.Decompress);
                        stripEncodingHeaders = true;
                    }
                    else if (CharArrayHelpers.EqualsOrdinalAsciiIgnoreCase(
                                 EncodingNameDeflate, buffer, contentEncodingStartIndex, contentEncodingLength))
                    {
                        decompressedStream   = new DeflateStream(responseStream, CompressionMode.Decompress);
                        stripEncodingHeaders = true;
                    }
                }
            }

            var content = new StreamContent(decompressedStream);

            response.Content        = content;
            response.RequestMessage = request;

            // Parse raw response headers and place them into response message.
            ParseResponseHeaders(requestHandle, response, buffer, stripEncodingHeaders);

            if (response.RequestMessage.Method != HttpMethod.Head)
            {
                state.ExpectedBytesToRead = response.Content.Headers.ContentLength;
            }

            return(response);
        }
Beispiel #17
0
 internal WinHttpResponseStream(SafeWinHttpHandle requestHandle, WinHttpRequestState state)
 {
     _state         = state;
     _requestHandle = requestHandle;
 }
Beispiel #18
0
        public void CheckResponseForAuthentication(
            WinHttpRequestState state,
            ref uint proxyAuthScheme,
            ref uint serverAuthScheme)
        {
            uint supportedSchemes   = 0;
            uint firstSchemeIgnored = 0;
            uint authTarget         = 0;
            Uri  uri = state.RequestMessage.RequestUri;

            state.RetryRequest = false;

            // Check the status code and retry the request applying credentials if needed.
            var statusCode = (HttpStatusCode)WinHttpResponseParser.GetResponseHeaderNumberInfo(
                state.RequestHandle,
                Interop.WinHttp.WINHTTP_QUERY_STATUS_CODE);

            switch (statusCode)
            {
            case HttpStatusCode.Unauthorized:
                if (state.ServerCredentials == null || state.LastStatusCode == HttpStatusCode.Unauthorized)
                {
                    // Either we don't have server credentials or we already tried
                    // to set the credentials and it failed before.
                    // So we will let the 401 be the final status code returned.
                    break;
                }

                state.LastStatusCode = statusCode;

                // Determine authorization scheme to use. We ignore the firstScheme
                // parameter which is included in the supportedSchemes flags already.
                // We pass the schemes to ChooseAuthScheme which will pick the scheme
                // based on most secure scheme to least secure scheme ordering.
                if (!Interop.WinHttp.WinHttpQueryAuthSchemes(
                        state.RequestHandle,
                        out supportedSchemes,
                        out firstSchemeIgnored,
                        out authTarget))
                {
                    // WinHTTP returns an error for schemes it doesn't handle.
                    // So, we need to ignore the error and just let it stay at 401.
                    break;
                }

                // WinHTTP returns the proper authTarget based on the status code (401, 407).
                // But we can validate with assert.
                Debug.Assert(authTarget == Interop.WinHttp.WINHTTP_AUTH_TARGET_SERVER);

                serverAuthScheme = ChooseAuthScheme(supportedSchemes);
                if (serverAuthScheme != 0)
                {
                    if (SetWinHttpCredential(
                            state.RequestHandle,
                            state.ServerCredentials,
                            uri,
                            serverAuthScheme,
                            authTarget))
                    {
                        state.RetryRequest = true;
                    }
                }

                break;

            case HttpStatusCode.ProxyAuthenticationRequired:
                if (state.LastStatusCode == HttpStatusCode.ProxyAuthenticationRequired)
                {
                    // We tried already to set the credentials.
                    break;
                }

                state.LastStatusCode = statusCode;

                // If we don't have any proxy credentials to try, then we end up with 407.
                ICredentials proxyCreds = state.Proxy == null ?
                                          state.DefaultProxyCredentials :
                                          state.Proxy.Credentials;
                if (proxyCreds == null)
                {
                    break;
                }

                // Determine authorization scheme to use. We ignore the firstScheme
                // parameter which is included in the supportedSchemes flags already.
                // We pass the schemes to ChooseAuthScheme which will pick the scheme
                // based on most secure scheme to least secure scheme ordering.
                if (!Interop.WinHttp.WinHttpQueryAuthSchemes(
                        state.RequestHandle,
                        out supportedSchemes,
                        out firstSchemeIgnored,
                        out authTarget))
                {
                    // WinHTTP returns an error for schemes it doesn't handle.
                    // So, we need to ignore the error and just let it stay at 401.
                    break;
                }

                // WinHTTP returns the proper authTarget based on the status code (401, 407).
                // But we can validate with assert.
                Debug.Assert(authTarget == Interop.WinHttp.WINHTTP_AUTH_TARGET_PROXY);

                proxyAuthScheme    = ChooseAuthScheme(supportedSchemes);
                state.RetryRequest = true;
                break;

            default:
                if (state.PreAuthenticate && serverAuthScheme != 0)
                {
                    SaveServerCredentialsToCache(uri, serverAuthScheme, state.ServerCredentials);
                }
                break;
            }
        }