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;

            while (WinHttpResponseParser.GetResponseHeader(
                       requestHandle, Interop.WinHttp.WINHTTP_QUERY_SET_COOKIE, ref buffer, ref index, out cookieHeader))
            {
                try
                {
                    cookieContainer.SetCookies(request.RequestUri, cookieHeader);
                    if (NetEventSource.Log.IsEnabled())
                    {
                        NetEventSource.Info(cookieContainer, $"Added cookie: {cookieHeader}");
                    }
                }
                catch (CookieException)
                {
                    // We ignore malformed cookies in the response.
                    if (NetEventSource.Log.IsEnabled())
                    {
                        NetEventSource.Error(cookieContainer, $"Ignoring invalid cookie: {cookieHeader}");
                    }
                }
            }
        }
        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.
            List <string> cookieHeaders =
                WinHttpResponseParser.GetResponseHeaders(requestHandle, Interop.WinHttp.WINHTTP_QUERY_SET_COOKIE);

            WinHttpTraceHelper.Trace("WINHTTP_QUERY_SET_COOKIE");

            foreach (string cookieHeader in cookieHeaders)
            {
                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);
                }
            }
        }
Esempio n. 3
0
        private void ReadResponseTrailers()
        {
            // Only load response trailers if:
            // 1. WINHTTP_QUERY_FLAG_TRAILERS is supported by the OS
            // 2. HTTP/2 or later (WINHTTP_QUERY_FLAG_TRAILERS does not work with HTTP/1.1)
            // 3. Response trailers not already loaded
            if (!WinHttpTrailersHelper.OsSupportsTrailers || _responseMessage.Version < WinHttpHandler.HttpVersion20 || _readTrailingHeaders)
            {
                return;
            }

            _readTrailingHeaders = true;

            var bufferLength = WinHttpResponseParser.GetResponseHeaderCharBufferLength(_requestHandle, isTrailingHeaders: true);

            if (bufferLength != 0)
            {
                char[] trailersBuffer = ArrayPool <char> .Shared.Rent(bufferLength);

                try
                {
                    WinHttpResponseParser.ParseResponseTrailers(_requestHandle, _responseMessage, trailersBuffer);
                }
                finally
                {
                    ArrayPool <char> .Shared.Return(trailersBuffer);
                }
            }
        }
Esempio n. 4
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))
                {
                    WinHttpException.ThrowExceptionUsingLastError();
                }

                // 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)
                {
                    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))
                {
                    WinHttpException.ThrowExceptionUsingLastError();
                }

                // 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;
            }
        }
Esempio n. 5
0
        private async void StartRequest(object obj)
        {
            WinHttpRequestState state           = (WinHttpRequestState)obj;
            bool secureConnection               = false;
            HttpResponseMessage responseMessage = null;
            Exception           savedException  = null;
            SafeWinHttpHandle   connectHandle   = null;

            if (state.CancellationToken.IsCancellationRequested)
            {
                state.Tcs.TrySetCanceled(state.CancellationToken);
                return;
            }

            try
            {
                EnsureSessionHandleExists(state);

                SetSessionHandleOptions();

                // Specify an HTTP server.
                connectHandle = Interop.WinHttp.WinHttpConnect(
                    _sessionHandle,
                    state.RequestMessage.RequestUri.Host,
                    (ushort)state.RequestMessage.RequestUri.Port,
                    0);
                ThrowOnInvalidHandle(connectHandle);
                connectHandle.SetParentHandle(_sessionHandle);

                if (state.RequestMessage.RequestUri.Scheme == UriScheme.Https)
                {
                    secureConnection = true;
                }
                else
                {
                    secureConnection = false;
                }

                // Create an HTTP request handle.
                state.RequestHandle = Interop.WinHttp.WinHttpOpenRequest(
                    connectHandle,
                    state.RequestMessage.Method.Method,
                    state.RequestMessage.RequestUri.PathAndQuery,
                    null,
                    Interop.WinHttp.WINHTTP_NO_REFERER,
                    Interop.WinHttp.WINHTTP_DEFAULT_ACCEPT_TYPES,
                    secureConnection ? Interop.WinHttp.WINHTTP_FLAG_SECURE : 0);
                ThrowOnInvalidHandle(state.RequestHandle);
                state.RequestHandle.SetParentHandle(connectHandle);

                // Set callback function.
                SetStatusCallback(state.RequestHandle, WinHttpRequestCallback.StaticCallbackDelegate);

                // Set needed options on the request handle.
                SetRequestHandleOptions(state);

                bool chunkedModeForSend = IsChunkedModeForSend(state.RequestMessage);

                AddRequestHeaders(
                    state.RequestHandle,
                    state.RequestMessage,
                    _cookieUsePolicy == CookieUsePolicy.UseSpecifiedCookieContainer ? _cookieContainer : null);

                uint proxyAuthScheme  = 0;
                uint serverAuthScheme = 0;
                state.RetryRequest = false;

                // The only way to abort pending async operations in WinHTTP is to close the WinHTTP handle.
                // We will detect a cancellation request on the cancellation token by registering a callback.
                // If the callback is invoked, then we begin the abort process by disposing the handle. This
                // will have the side-effect of WinHTTP cancelling any pending I/O and accelerating its callbacks
                // on the handle and thus releasing the awaiting tasks in the loop below. This helps to provide
                // a more timely, cooperative, cancellation pattern.
                using (state.CancellationToken.Register(s => ((WinHttpRequestState)s).RequestHandle.Dispose(), state))
                {
                    do
                    {
                        _authHelper.PreAuthenticateRequest(state, proxyAuthScheme);

                        await InternalSendRequestAsync(state).ConfigureAwait(false);

                        if (state.RequestMessage.Content != null)
                        {
                            await InternalSendRequestBodyAsync(state, chunkedModeForSend).ConfigureAwait(false);
                        }

                        bool receivedResponse = await InternalReceiveResponseHeadersAsync(state).ConfigureAwait(false);

                        if (receivedResponse)
                        {
                            _authHelper.CheckResponseForAuthentication(
                                state,
                                ref proxyAuthScheme,
                                ref serverAuthScheme);
                        }
                    } while (state.RetryRequest);
                }

                state.CancellationToken.ThrowIfCancellationRequested();

                responseMessage = WinHttpResponseParser.CreateResponseMessage(state, _doManualDecompressionCheck);

                // Since the headers have been read, set the "receive" timeout to be based on each read
                // call of the response body data. WINHTTP_OPTION_RECEIVE_TIMEOUT sets a timeout on each
                // lower layer winsock read.
                uint optionData = (uint)_receiveDataTimeout.TotalMilliseconds;
                SetWinHttpOption(state.RequestHandle, Interop.WinHttp.WINHTTP_OPTION_RECEIVE_TIMEOUT, ref optionData);
            }
            catch (Exception ex)
            {
                if (state.SavedException != null)
                {
                    savedException = state.SavedException;
                }
                else
                {
                    savedException = ex;
                }
            }
            finally
            {
                SafeWinHttpHandle.DisposeAndClearHandle(ref connectHandle);
            }

            // Move the main task to a terminal state. This releases any callers of SendAsync() that are awaiting.
            if (responseMessage != null)
            {
                state.Tcs.TrySetResult(responseMessage);
            }
            else
            {
                HandleAsyncException(state, savedException);
            }
        }
        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, state.RequestMessage.RequestUri, state.ServerCredentials);
                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 407.
                    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,
                    // https://github.com/dotnet/runtime/issues/16737. If Proxy==null, we're using the system proxy which is possibly
                    // discovered/calculated with a PAC file. So, we can't determine the actual proxy uri at
                    // this point since it is calculated internally in WinHTTP. For now, pass in null for the uri.
                    state.Proxy?.GetProxy(state.RequestMessage.RequestUri),
                    proxyCreds);
                state.RetryRequest = true;
                break;

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