< Summary

Information
Class: System.Net.Http.RedirectHandler
Assembly: System.Net.Http
File(s): D:\runner\runtime\src\libraries\System.Net.Http\src\System\Net\Http\SocketsHttpHandler\RedirectHandler.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 106
Coverable lines: 106
Total lines: 189
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 48
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.ctor(...)100%110%
SendAsync()0%18180%
GetUriForRedirect(...)0%22220%
RequestRequiresForceGet(...)0%660%
Dispose(...)0%220%
Trace(...)100%110%
TraceError(...)100%110%

File(s)

D:\runner\runtime\src\libraries\System.Net.Http\src\System\Net\Http\SocketsHttpHandler\RedirectHandler.cs

#LineLine coverage
 1// Licensed to the .NET Foundation under one or more agreements.
 2// The .NET Foundation licenses this file to you under the MIT license.
 3
 4using System.Diagnostics;
 5using System.Runtime.CompilerServices;
 6using System.Threading;
 7using System.Threading.Tasks;
 8
 9namespace System.Net.Http
 10{
 11    internal sealed class RedirectHandler : HttpMessageHandlerStage
 12    {
 13        private readonly HttpMessageHandlerStage _innerHandler;
 14        private readonly int _maxAutomaticRedirections;
 15        private readonly bool _disableAuthOnRedirect;
 16
 017        public RedirectHandler(int maxAutomaticRedirections, HttpMessageHandlerStage innerHandler, bool disableAuthOnRed
 018        {
 019            Debug.Assert(innerHandler != null);
 020            Debug.Assert(maxAutomaticRedirections > 0);
 21
 022            _maxAutomaticRedirections = maxAutomaticRedirections;
 023            _innerHandler = innerHandler;
 024            _disableAuthOnRedirect = disableAuthOnRedirect;
 025        }
 26
 27        internal override async ValueTask<HttpResponseMessage> SendAsync(HttpRequestMessage request, bool async, Cancell
 028        {
 029            HttpResponseMessage response = await _innerHandler.SendAsync(request, async, cancellationToken).ConfigureAwa
 30
 031            uint redirectCount = 0;
 32            Uri? redirectUri;
 033            Debug.Assert(request.RequestUri != null);
 034            while ((redirectUri = GetUriForRedirect(request.RequestUri, response)) != null)
 035            {
 036                redirectCount++;
 37
 038                if (redirectCount > _maxAutomaticRedirections)
 039                {
 40                    // If we exceed the maximum number of redirects
 41                    // then just return the 3xx response.
 042                    if (NetEventSource.Log.IsEnabled())
 043                    {
 044                        TraceError($"Exceeded max number of redirects. Redirect from {request.RequestUri} to {redirectUr
 045                    }
 46
 047                    break;
 48                }
 49
 050                response.Dispose();
 51
 52                // Clear the authorization header.
 053                request.Headers.Authorization = null;
 54
 055                if (HttpTelemetry.Log.IsEnabled())
 056                {
 057                    HttpTelemetry.Log.Redirect(redirectUri);
 058                }
 059                if (NetEventSource.Log.IsEnabled())
 060                {
 061                    Trace($"Redirecting from {request.RequestUri} to {redirectUri} in response to status code {(int)resp
 062                }
 63
 64                // Set up for the redirect
 065                request.RequestUri = redirectUri;
 066                if (RequestRequiresForceGet(response.StatusCode, request.Method))
 067                {
 068                    if (NetEventSource.Log.IsEnabled())
 069                    {
 070                        Trace($"Modified request from {request.Method} to {HttpMethod.Get} in response to status code {(
 071                    }
 72
 073                    request.Method = HttpMethod.Get;
 074                    request.Content = null;
 075                    if (request.Headers.TransferEncodingChunked == true)
 076                    {
 077                        request.Headers.TransferEncodingChunked = false;
 078                    }
 079                }
 80
 081                if (_disableAuthOnRedirect)
 082                {
 083                    request.DisableAuth();
 084                }
 85
 86                // Issue the redirected request.
 087                response = await _innerHandler.SendAsync(request, async, cancellationToken).ConfigureAwait(false);
 088            }
 89
 090            return response;
 091        }
 92
 93        private Uri? GetUriForRedirect(Uri requestUri, HttpResponseMessage response)
 094        {
 095            switch (response.StatusCode)
 96            {
 97                case HttpStatusCode.Moved:
 98                case HttpStatusCode.Found:
 99                case HttpStatusCode.SeeOther:
 100                case HttpStatusCode.TemporaryRedirect:
 101                case HttpStatusCode.MultipleChoices:
 102                case HttpStatusCode.PermanentRedirect:
 0103                    break;
 104
 105                default:
 0106                    return null;
 107            }
 108
 0109            Uri? location = response.Headers.Location;
 0110            if (location == null)
 0111            {
 0112                return null;
 113            }
 114
 115            // Ensure the redirect location is an absolute URI.
 0116            if (!location.IsAbsoluteUri)
 0117            {
 0118                location = new Uri(requestUri, location);
 0119            }
 120
 121            // Per https://tools.ietf.org/html/rfc7231#section-7.1.2, a redirect location without a
 122            // fragment should inherit the fragment from the original URI.
 0123            string requestFragment = requestUri.Fragment;
 0124            if (!string.IsNullOrEmpty(requestFragment))
 0125            {
 0126                string redirectFragment = location.Fragment;
 0127                if (string.IsNullOrEmpty(redirectFragment))
 0128                {
 0129                    location = new UriBuilder(location) { Fragment = requestFragment }.Uri;
 0130                }
 0131            }
 132
 133            // Disallow automatic redirection from secure to non-secure schemes
 0134            if (HttpUtilities.IsSupportedSecureScheme(requestUri.Scheme) && !HttpUtilities.IsSupportedSecureScheme(locat
 0135            {
 0136                if (NetEventSource.Log.IsEnabled())
 0137                {
 0138                    TraceError($"Insecure https to http redirect from '{requestUri}' to '{location}' blocked.", response
 0139                }
 140
 0141                return null;
 142            }
 143
 144            // Disallow automatic redirection to unsupported schemes
 0145            if (!HttpUtilities.IsSupportedScheme(location.Scheme))
 0146            {
 0147                if (NetEventSource.Log.IsEnabled())
 0148                {
 0149                    TraceError($"Redirect from '{requestUri}' to '{location}' blocked due to unsupported scheme '{locati
 0150                }
 151
 0152                return null;
 153            }
 154
 0155            return location;
 0156        }
 157
 158        private static bool RequestRequiresForceGet(HttpStatusCode statusCode, HttpMethod requestMethod)
 0159        {
 0160            switch (statusCode)
 161            {
 162                case HttpStatusCode.Moved:
 163                case HttpStatusCode.Found:
 164                case HttpStatusCode.MultipleChoices:
 0165                    return requestMethod == HttpMethod.Post;
 166                case HttpStatusCode.SeeOther:
 0167                    return requestMethod != HttpMethod.Get && requestMethod != HttpMethod.Head;
 168                default:
 0169                    return false;
 170            }
 0171        }
 172
 173        protected override void Dispose(bool disposing)
 0174        {
 0175            if (disposing)
 0176            {
 0177                _innerHandler.Dispose();
 0178            }
 179
 0180            base.Dispose(disposing);
 0181        }
 182
 183        internal void Trace(string message, int requestId, [CallerMemberName] string? memberName = null) =>
 0184            NetEventSource.Log.HandlerMessage(0, 0, requestId, memberName, ToString() + ": " + message);
 185
 186        internal void TraceError(string message, int requestId, [CallerMemberName] string? memberName = null) =>
 0187            NetEventSource.Log.HandlerMessageError(0, 0, requestId, memberName, ToString() + ": " + message);
 188    }
 189}