< 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: 107
Coverable lines: 107
Total lines: 190
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 headers.
 053                request.Headers.Authorization = null;
 054                request.Headers.ProxyAuthorization = null;
 55
 056                if (HttpTelemetry.Log.IsEnabled())
 057                {
 058                    HttpTelemetry.Log.Redirect(redirectUri);
 059                }
 060                if (NetEventSource.Log.IsEnabled())
 061                {
 062                    Trace($"Redirecting from {request.RequestUri} to {redirectUri} in response to status code {(int)resp
 063                }
 64
 65                // Set up for the redirect
 066                request.RequestUri = redirectUri;
 067                if (RequestRequiresForceGet(response.StatusCode, request.Method))
 068                {
 069                    if (NetEventSource.Log.IsEnabled())
 070                    {
 071                        Trace($"Modified request from {request.Method} to {HttpMethod.Get} in response to status code {(
 072                    }
 73
 074                    request.Method = HttpMethod.Get;
 075                    request.Content = null;
 076                    if (request.Headers.TransferEncodingChunked == true)
 077                    {
 078                        request.Headers.TransferEncodingChunked = false;
 079                    }
 080                }
 81
 082                if (_disableAuthOnRedirect)
 083                {
 084                    request.DisableAuth();
 085                }
 86
 87                // Issue the redirected request.
 088                response = await _innerHandler.SendAsync(request, async, cancellationToken).ConfigureAwait(false);
 089            }
 90
 091            return response;
 092        }
 93
 94        private Uri? GetUriForRedirect(Uri requestUri, HttpResponseMessage response)
 095        {
 096            switch (response.StatusCode)
 97            {
 98                case HttpStatusCode.Moved:
 99                case HttpStatusCode.Found:
 100                case HttpStatusCode.SeeOther:
 101                case HttpStatusCode.TemporaryRedirect:
 102                case HttpStatusCode.MultipleChoices:
 103                case HttpStatusCode.PermanentRedirect:
 0104                    break;
 105
 106                default:
 0107                    return null;
 108            }
 109
 0110            Uri? location = response.Headers.Location;
 0111            if (location == null)
 0112            {
 0113                return null;
 114            }
 115
 116            // Ensure the redirect location is an absolute URI.
 0117            if (!location.IsAbsoluteUri)
 0118            {
 0119                location = new Uri(requestUri, location);
 0120            }
 121
 122            // Per https://tools.ietf.org/html/rfc7231#section-7.1.2, a redirect location without a
 123            // fragment should inherit the fragment from the original URI.
 0124            string requestFragment = requestUri.Fragment;
 0125            if (!string.IsNullOrEmpty(requestFragment))
 0126            {
 0127                string redirectFragment = location.Fragment;
 0128                if (string.IsNullOrEmpty(redirectFragment))
 0129                {
 0130                    location = new UriBuilder(location) { Fragment = requestFragment }.Uri;
 0131                }
 0132            }
 133
 134            // Disallow automatic redirection from secure to non-secure schemes
 0135            if (HttpUtilities.IsSupportedSecureScheme(requestUri.Scheme) && !HttpUtilities.IsSupportedSecureScheme(locat
 0136            {
 0137                if (NetEventSource.Log.IsEnabled())
 0138                {
 0139                    TraceError($"Insecure https to http redirect from '{requestUri}' to '{location}' blocked.", response
 0140                }
 141
 0142                return null;
 143            }
 144
 145            // Disallow automatic redirection to unsupported schemes
 0146            if (!HttpUtilities.IsSupportedScheme(location.Scheme))
 0147            {
 0148                if (NetEventSource.Log.IsEnabled())
 0149                {
 0150                    TraceError($"Redirect from '{requestUri}' to '{location}' blocked due to unsupported scheme '{locati
 0151                }
 152
 0153                return null;
 154            }
 155
 0156            return location;
 0157        }
 158
 159        private static bool RequestRequiresForceGet(HttpStatusCode statusCode, HttpMethod requestMethod)
 0160        {
 0161            switch (statusCode)
 162            {
 163                case HttpStatusCode.Moved:
 164                case HttpStatusCode.Found:
 165                case HttpStatusCode.MultipleChoices:
 0166                    return requestMethod == HttpMethod.Post;
 167                case HttpStatusCode.SeeOther:
 0168                    return requestMethod != HttpMethod.Get && requestMethod != HttpMethod.Head;
 169                default:
 0170                    return false;
 171            }
 0172        }
 173
 174        protected override void Dispose(bool disposing)
 0175        {
 0176            if (disposing)
 0177            {
 0178                _innerHandler.Dispose();
 0179            }
 180
 0181            base.Dispose(disposing);
 0182        }
 183
 184        internal void Trace(string message, int requestId, [CallerMemberName] string? memberName = null) =>
 0185            NetEventSource.Log.HandlerMessage(0, 0, requestId, memberName, ToString() + ": " + message);
 186
 187        internal void TraceError(string message, int requestId, [CallerMemberName] string? memberName = null) =>
 0188            NetEventSource.Log.HandlerMessageError(0, 0, requestId, memberName, ToString() + ": " + message);
 189    }
 190}