using System; using System.Linq; using System.Net; using Microsoft.AspNetCore.Antiforgery; using Microsoft.Extensions.DependencyInjection; namespace Microsoft.AspNetCore.Http { /// /// Extensions for the . /// public static class HttpContextExtensions { // Search these additional headers for a remote client ip address. private static readonly string[] _defaultIpHeaderNames = [ "Cf-Connecting-Ip", // set by Cloudflare "X-Real-IP", // wide-spread alternative to X-Forwarded-For "X-Forwarded-For", // commonly used on all known proxies ]; /// /// Retrieves the antiforgery token. /// /// The current . /// FormName, HeaderName and Value of the antiforgery token. public static (string FormName, string HeaderName, string Value) GetAntiforgeryToken(this HttpContext httpContext) { var antiforgery = httpContext.RequestServices.GetService(); var tokenSet = antiforgery?.GetAndStoreTokens(httpContext); return (tokenSet?.FormFieldName, tokenSet?.HeaderName, tokenSet?.RequestToken); } /// /// Returns the remote ip address. /// /// /// Searches for additional headers in the following order: /// /// Cf-Connecting-Ip /// X-Real-IP /// X-Forwarded-For /// /// /// The current . /// The name of the header to resolve the when behind a proxy. /// The ip address of the client. public static IPAddress GetRemoteIpAddress(this HttpContext httpContext, string ipHeaderName = null) { string forwardedForAddress = null; var headerNames = string.IsNullOrWhiteSpace(ipHeaderName) ? _defaultIpHeaderNames : new[] { ipHeaderName }.Concat(_defaultIpHeaderNames); foreach (string headerName in headerNames) { if (!httpContext.Request.Headers.ContainsKey(headerName)) continue; // X-Forwarded-For can contain multiple comma-separated addresses. forwardedForAddress = httpContext.Request.Headers[headerName].ToString() .Split(',', StringSplitOptions.TrimEntries) .First(); break; } if (!string.IsNullOrWhiteSpace(forwardedForAddress) && IPAddress.TryParse(forwardedForAddress, out var remoteAddress)) { return remoteAddress.IsIPv4MappedToIPv6 ? remoteAddress.MapToIPv4() : remoteAddress; } return httpContext.Connection.RemoteIpAddress.IsIPv4MappedToIPv6 ? httpContext.Connection.RemoteIpAddress.MapToIPv4() : httpContext.Connection.RemoteIpAddress; } /// /// Returns whether the request was made locally. /// /// /// Searches for additional headers in the following order: /// /// Cf-Connecting-Ip /// X-Real-IP /// X-Forwarded-For /// /// /// The current . /// The name of the header to resolve the when behind a proxy. /// public static bool IsLocalRequest(this HttpContext httpContext, string ipHeaderName = null) { var remoteIpAddress = httpContext.GetRemoteIpAddress(ipHeaderName); return httpContext.Connection.LocalIpAddress.Equals(remoteIpAddress); } /// /// Tries to retrieve the return url. /// /// The current . /// public static string GetReturnUrl(this HttpContext httpContext) { if (httpContext.Items.ContainsKey("OriginalRequest")) return httpContext.Items["OriginalRequest"].ToString(); if (httpContext.Request.Query.ContainsKey("ReturnUrl")) return httpContext.Request.Query["ReturnUrl"].ToString(); return null; } /// /// Clears a session when available. /// /// The current . public static void ClearSession(this HttpContext httpContext) => httpContext.Session?.Clear(); } }