I'm trying to detect if the website is running in local to display the stack trace instead of a nice server error page. I've been happily using Request.IsLocal in the Global.asax.cs file locally and on internal environments, but when it gets deployed to an Azure app it behaves as if the request was indeed local.
According to the documentation this actually checks if the originating IP was 127.0.0.1 but I don't understand how can that be. Is this just some weird Azure underlying problem?
I didn't see same problem. But, I think, you can try doing IsLocal with the following code.
public static class HttpRequestExtensions
{
public static bool IsLocal(this HttpRequest req)
{
var connection = req.HttpContext.Connection;
if (connection.RemoteIpAddress != null)
{
if (connection.LocalIpAddress != null)
{
return connection.RemoteIpAddress.Equals(connection.LocalIpAddress);
}
return IPAddress.IsLoopback(connection.RemoteIpAddress);
}
if (connection.RemoteIpAddress == null && connection.LocalIpAddress == null)
{
return true;
}
return false;
}
}
Related
In the regular ASP.NET you could do this in a view to determine if the current request was from localhost:
HttpContext.Current.Request.IsLocal
But I can't find something similar in ASP.NET 6/Core/whatever it is meant to be called.
UPDATE: ASP.NET Core 2.0 has a method called Url.IsLocalUrl (see this Microsoft Docs).
I think this code will work, but I haven't been able to test it completely
var callingUrl = Request.Headers["Referer"].ToString();
var isLocal = Url.IsLocalUrl(callingUrl);
But see Will Dean's comment below about this approach:
Anyone thinking about using the 'updated' version which checks the Referrer header should bear in mind that headers are extremely easy to spoof, to a degree that doesn't apply to loopback IP addresses.
Original solution
I came across this looking for a solution to knowing if a request is local. Unfortunately ASP.NET version 1.1.0 does not have a IsLocal method on a connection. I found one solution on a web site called Strathweb but that is out of date too.
I have created my own IsLocal extension, and it seems to work, but I can't say I have tested it in all circumstances, but you are welcome to try it.
public static class IsLocalExtension
{
private const string NullIpAddress = "::1";
public static bool IsLocal(this HttpRequest req)
{
var connection = req.HttpContext.Connection;
if (connection.RemoteIpAddress.IsSet())
{
//We have a remote address set up
return connection.LocalIpAddress.IsSet()
//Is local is same as remote, then we are local
? connection.RemoteIpAddress.Equals(connection.LocalIpAddress)
//else we are remote if the remote IP address is not a loopback address
: IPAddress.IsLoopback(connection.RemoteIpAddress);
}
return true;
}
private static bool IsSet(this IPAddress address)
{
return address != null && address.ToString() != NullIpAddress;
}
}
You call it in a controller action from using the Request property, i.e.
public IActionResult YourAction()
{
var isLocal = Request.IsLocal();
//... your code here
}
I hope that helps someone.
At the time of writing HttpContext.Connection.IsLocal is now missing from .NET Core.
Other working solution checks only for a first loopback address (::1 or 127.0.0.1) which might not be adequate.
I find the solution below useful:
using Microsoft.AspNetCore.Http;
using System.Net;
namespace ApiHelpers.Filters
{
public static class HttpContextFilters
{
public static bool IsLocalRequest(HttpContext context)
{
if (context.Connection.RemoteIpAddress.Equals(context.Connection.LocalIpAddress))
{
return true;
}
if (IPAddress.IsLoopback(context.Connection.RemoteIpAddress))
{
return true;
}
return false;
}
}
}
And the example use case:
app.UseWhen(HttpContextFilters.IsLocalRequest, configuration => configuration.UseElmPage());
None of the above worked for me.
Url.IsLocalUrl works very different and I find it a bit useless:
For example, the following URLs are considered local:
/Views/Default/Index.html
~/Index.html
The following URLs are non-local:
../Index.html
http://www.contoso.com/
http://localhost/Index.html
HttpContext.Connection.IsLocal doesn't exist in .Net Core 2.2
Comparing ControllerContext.HttpContext.Connection.RemoteIpAddress and ControllerContext.HttpContext.Connection.LocalIpAddress also doesn't work in my test because I get "::1" for remote ip and "127.0.0.1" for local ip.
Finally, I used this piece:
IPAddress addr = System.Net.IPAddress.Parse( HttpContext.Connection.RemoteIpAddress.ToString() );
if (System.Net.IPAddress.IsLoopback(addr) )
{
//do something
}
Late to the party, but if I want to check IsLocal in razor views in .Net core 2.2+, I just do this:
#if (Context.Request.Host.Value.StartsWith("localhost"))
{
//do local stuff
}
UPDATE for ASP.NET Core 3.1
You can use this:
if (Request.Host.Host == "localhost") {// do something }
I would also mention that it may be useful to add the below clause to the end of your custom IsLocal() check
if (connection.RemoteIpAddress == null && connection.LocalIpAddress == null)
{
return true;
}
This would account for the scenario where the site is being ran using the Microsoft.AspNetCore.TestHost and the site is being ran entirely locally in memory without an actual TCP/IP connection.
now its
HttpContext.Connection.IsLocal
and if you need to check that outside of a controller then you take a dependency on IHttpContextAccessor to get access to it.
Update based on comment:
HttpContext is intrinsically available in Views
#if (Context.Connection.IsLocal)
{
}
Why System.Net.ServicePoint.ConnectionLimit uses '7FFFFFFF' (Int32.MaxValue/2147483647) when a client connects to a service on 'localhost', whereas it decide to use '2' as default if the service is running on remote machine?
Initially I thought it will be ServicePointManager.DefaultConnectionLimit if servicepoint.connectionlimit is not set. However, I just realized (once I got an issue from customer), that its Int32.MaxValue/2147483647.
I have done some research (for details please refer to below links), however I couldn't find out why it uses to int32.maxvalue. I can kind of conjecture its probably for better performance as the input requests and response messages are not going across boundary.
My Question(s):
Why Int32.MaxValue if the service is running on 'localhost'?
(any explanation in English ;) of the code snippet I copied from reflector is also great - as I kind of conjectured the intentions - but didn't understand the code totally :))
I understand its for perf - but from '2' (default) to 'int32.maxvalue' sounds stretch to me. In other words why is it ok to open as many TCP connections as long as the requests are not going across network.
(in other words - why default to int32.maxvalue - doesn't it have side affects)
Some useful links related to this:
How and where the TCP connection has been created in httpwebrequest, and how is it related to servicepoint?
http://blogs.microsoft.co.il/idof/2011/06/20/servicepointmanagerdefaultconnectionlimit-2-depends/
http://msdn.microsoft.com/en-us/library/system.net.servicepoint.connectionlimit(v=vs.110).aspx
http://arnosoftwaredev.blogspot.com/2006/09/net-20-httpwebrequestkeepalive-and.html
Code Snippet from Reflector
public int ConnectionLimit
{
get
{
if ((!this.m_UserChangedLimit && (this.m_IPAddressInfoList == null)) && (this.m_HostLoopbackGuess == TriState.Unspecified))
{
lock (this)
{
if ((!this.m_UserChangedLimit && (this.m_IPAddressInfoList == null)) && (this.m_HostLoopbackGuess == TriState.Unspecified))
{
IPAddress address = null;
if (IPAddress.TryParse(this.m_Host, out address))
{
this.m_HostLoopbackGuess = IsAddressListLoopback(new IPAddress[] { address }) ? TriState.True : TriState.False;
}
else
{
this.m_HostLoopbackGuess = NclUtilities.GuessWhetherHostIsLoopback(this.m_Host) ? TriState.True : TriState.False;
}
}
}
}
if (!this.m_UserChangedLimit && !((this.m_IPAddressInfoList == null) ? (this.m_HostLoopbackGuess != TriState.True) : !this.m_IPAddressesAreLoopback))
{
return 0x7fffffff;
}
return this.m_ConnectionLimit;
}
set
{
if (value <= 0)
{
throw new ArgumentOutOfRangeException("value");
}
if (!this.m_UserChangedLimit || (this.m_ConnectionLimit != value))
{
lock (this)
{
if (!this.m_UserChangedLimit || (this.m_ConnectionLimit != value))
{
this.m_ConnectionLimit = value;
this.m_UserChangedLimit = true;
this.ResolveConnectionLimit();
}
}
}
}
}
Regards,
Int32.maxvalue is just a placeholder for no limit. You should be able to create as many connections to yourself as you need to.
The code you pasted basically just checks whether you are connecting to the loopback address or not, and if you are, returns maxint, if you are not, returns the value of servicepoint.connectionlimit (2 by default, but you can change it)
I have a service whose LogOnAs is not Local System.It is a different user (say test with administrative privilege).Using Normal code :doesnt work it always throws exception.
public bool IsServiceInstalled(String serviceName)
{
bool IsInstalled = false;
// get list of Windows services
ServiceController[] services = ServiceController.GetServices();
// try to find service name
foreach (ServiceController service in services)
{
if (service.ServiceName == serviceName)
{
IsInstalled = true;
break;
}
}
return IsInstalled;
}
Any help will be greatly appreciated...
To begin with, you can simplify your logic with:
public bool IsServiceInstalled(String aServiceName)
{
ServiceController sc = ServiceController.GetServices()
.FirstOrDefault(s => s.ServiceName == aServiceName);
return (sc != null) ;
}
This uses linq to get the first matching service with the passed name (I've changed the parameter to aServiceName, the a stands for argument).
It won't solve your problem, but will be easier to read and maintain.
Does this work for you when you're logged on normally?
I have a code that I want to run in global.asax in ASP.NET. I want this code to run only on localhost and when the code on an EC2 instance, I want it to run another code (the second code should run when I deploy my project on Amazon Web Service EC2 server), how can I do that without using the DEBUG functionality?
To check for whether or not the request is from the local machine, do this:
bool isLocal = HttpContext.Current.Request.IsLocal;
if(isLocal)
{
// Do things that should only be done when request is local here
}
Note: Read HttpRequest.IsLocal documentation for more information.
I guess uyou can pick few environment variables frm these:
Environment Variables (type ENV)
EC2_AMI_ID
EC2_AKI_ID
EC2_ARI_ID
EC2_AMI_MANIFEST_PATH
EC2_PLACEMENT_AVAILABILITY_ZONE
EC2_HOSTNAME
EC2_INSTANCE_ID
EC2_INSTANCE_TYPE
EC2_LOCAL_HOSTNAME
EC2_PUBLIC_HOSTNAME
EC2_PUBLIC_IPV4
EC2_RESERVATION_ID
EC2_SECURITY_GROUPS
RS_EIP
RS_SERVER
RS_SKETCHY
RS_SYSLOG
RS_TOKEN
RS_SERVER_NAME
List taken from here: https://support.rightscale.com/09-Clouds/AWS/FAQs/FAQ_0013_-_What_EC2_environment_variables_are_available_in_RightScripts%3F
I think, that checking for availability of EC2_AMI_ID and EC2_INSTANCE_ID would be enough to answer your questions.
If you are using ASP.NET dev server (VS 2012 and earlier) use this method:
public static bool IsDebugWebServer()
{
if (HttpContext.Current != null && HttpContext.Current.Request != null)
{
return HttpContext.Current.Request.ServerVariables["SERVER_SOFTWARE"] == null || HttpContext.Current.Request.ServerVariables["SERVER_SOFTWARE"] == string.Empty;
}
else
{
return false;
}
}
And if you are using local IIS Express use this:
public static bool IsDebugWebServer()
{
return String.Compare(Process.GetCurrentProcess().ProcessName, "iisexpress") == 0;
}
I picked up the following code from Stackoverflow->a blog re handling custom 404 in Sitecore (which acutally does a 302 redirect to 404 page with status 200 which gets picked up by google as soft 404).
While this works totally fine in our local test servers, the moment we drop it in production the site goes haywire and takes AGES e.g. 8-9 minutes to load and stuff.
public class ExecuteRequest : Sitecore.Pipelines.HttpRequest.ExecuteRequest
{
protected override void RedirectOnItemNotFound(string url)
{
var context = System.Web.HttpContext.Current;
try
{
// Request the NotFound page
var domain = context.Request.Url.GetComponents(
UriComponents.Scheme | UriComponents.Host,
UriFormat.Unescaped);
var content = WebUtil.ExecuteWebPage(
string.Concat(domain, url));
// The line below is required for IIS 7.5 hosted
// sites or else IIS is gonna display default 404 page
context.Response.TrySkipIisCustomErrors = true;
context.Response.StatusCode = 404;
context.Response.Write(content);
}
catch (Exception ex)
{
Log.Error(string.Format("Falling back to default redirection behavior. Reason for error {0}", ex), ex);
// Fall back to default behavior on exceptions
base.RedirectOnItemNotFound(url);
}
context.Response.End();
}
}
P.S: I then replaced ExecuteRequest with my custom one in web.config.
If you have experienced similar thing or know of any issue re this please do shed some light.
Thanks in advance
There is a setting in Sitecore, with which you can get rid of the 302 redirect:
<setting name="RequestErrors.UseServerSideRedirect" value="true" />
With this settings, the url stays the same and the status code is 404. If you want to have some additional logic (like showing a Sitecore item as error page), there is a Shared Source module called Error Manager on the Sitecore Market Place.
Hope that helps.
Check if the server is able to access the hostname of your website.
Servers often do not have access to a DNS and therefore are unable to resolve hostnames. In order for your 404 handler to work, the application needs to be able to access its own hostname to request the 404 page.
To be sure this works, edit the hosts file of the server and add an entry for your hostname there, pointing it to 127.0.0.1
You can resolve it with creating new resolver. It is good solution when you want to give to an user error page in right language. But there some differences in IIS 7.0 and 7.5.
Add processor to your sitecore configuration:
<processor type="Sitecore.Pipelines.HttpRequest.ItemResolver, Sitecore.Kernel"/>
<processor type="Project.Error404Resolver, Project" />
Processor resolving it:
For IIS 7.0:
public class Error404Resolver : Sitecore.Pipelines.HttpRequest.HttpRequestProcessor
{
public override void Process(Sitecore.Pipelines.HttpRequest.HttpRequestArgs args)
{
if(Sitecore.Context.Item == null && !args.Context.Request.Url.AbsolutePath.StartsWith("/sitecore")
{
args.Context.Response.Clear();
SiteContext site = Sitecore.Context.Site;
if(site != null)
{
Item item404Page = Sitecore.Context.Database.GetItem(site.RootPath + "website/error/404");
if(item404Page != null)
{
Sitecore.Context.Item = item404Page;
args.Context.Response.StatusCode = (int) System.Net.HttpStatusCode.NotFound;
}
}
}
}
}
For IIS 7.5:
public class Error404Resolver : Sitecore.Pipelines.HttpRequest.HttpRequestProcessor
{
public override void Process(Sitecore.Pipelines.HttpRequest.HttpRequestArgs args)
{
if(Sitecore.Context.Item == null && !args.Context.Request.Url.AbsolutePath.StartsWith("/sitecore")
{
args.Context.Response.Clear();
SiteContext site = Sitecore.Context.Site;
if(site != null)
{
Item item404Page = Sitecore.Context.Database.GetItem(site.RootPath + "website/error/404");
if(item404Page != null)
{
WebClient webClient = new WebClient();
webClient.Encoding = args.Context.Request.ContentEncoding;
webClient.Headers.Add("User-Agent", args.Context.Request.UserAgent);
string page = webClient.DownloadString(LinkManager.GetItemUrl(item404Page));
args.Context.Response.StatusCode = (int) System.Net.HttpStatusCode.NotFound;
args.Context.Response.Write(page);
args.Context.Response.TrySkipIisCustomErrors = true;
args.Context.Response.End();
}
}
}
}
}
Whit this you will render error page in current page without redirect and returns to a browser code 404.
I have the same issue at a customer I currently work at (looks like the code was pasted) and actually the reason is pretty obvious: If you execute this call with a url that is not registered in the Sitecore sites config (but accessible via IIS), you will also run through this code. Unfortunately, the WebUtil.ExecuteWebPage call is executed with the wrong url as well, hence you end up stuck in a loop.
Actually you should see a lot of these messages in your log: Falling back to default redirection behavior. Reason for error {0}, probably with timeouts.
If you really want to use your custom handler, you should check if you are in the right site context before calling WebUtil.ExecuteWebPage.