<%-- Copyright (c) 2008 Gustavo G. Duarte (http://duartes.org/gustavo) This file is licensed under the terms of the MIT/X11 open source license. See copying.txt in the root directory of this project (above /src) or http://en.wikipedia.org/wiki/MIT_License for the terms of copyright. --%> <%@ Control Language="C#" AutoEventWireup="true" Inherits="System.Web.UI.UserControl" %> <%@ Import namespace="System.Collections.Generic"%> <%@ Import namespace="System.Security.Principal"%> <%@ Import namespace="System.Threading"%> <%@ Import namespace="System.Globalization"%> <%@ Import namespace="System.Diagnostics"%> <%@ Import namespace="System.Reflection"%>

HttpRequest information

HttpRequest.LogonUserIdentity and Process

When troubleshooting ASP.NET problems you often ask two questions: which process is the code running in?; and which Windows user is it running as? The default setups are thus:

Windows + IIS Worker Process ASP.NET threads
Vista + IIS 7.0 w3wp.exe running as NETWORK SERVICE, one per application pool IUSR, well-known account with SID S-1-5-17, see this post
Windows 2003 + IIS 6.0 w3wp.exe running as NETWORK SERVICE, one per application pool IUSR_MACHINE_NAME, per-machine anonymous IIS user
Windows 2000/XP + IIS 5.0 aspnet_wp.exe, one for all ASP.NET applications ASPNET, per-machine ASP.NET worker account

These defaults however are subject to many factors, so often you need to find the answers yourself. The cheat sheet below shows you the relevant classes and properties to use. ProcessMonitor is a great tool for troubleshooting Windows runtime problems, especially permission and access denied issues. If you wish to read up on security and .NET, see Keith Brown's superb online book, The .NET Developer's Guide to Windows Security.

<%= StartTable("HttpRequest.LogonUserIdentity And Process") %> <% Add(Environment.GetCommandLineArgs()[0], "Process.GetCurrentProcess().MainModule.FileName", "GetCurrentProcess() has security demands and may not be available in certain situations. One work-around " + "is to use Environment.GetCommandLineArgs()[0] to at least get the file name"); Add(Process.GetCurrentProcess().Id, "Process.GetCurrentProcess().Id", "The Windows process ID for the process running your code. This is the process you attach to for debugging, " + "watch for perf monitoring, and kill to restart your app 'cold'."); Add(Process.GetCurrentProcess().MainModule.FileVersionInfo, "Process.GetCurrentProcess().MainModule.FileVersionInfo"); Add(Process.GetCurrentProcess().StartTime, "Process.GetCurrentProcess().StartTime"); Add(this.Request.LogonUserIdentity.AuthenticationType, "this.Request.LogonUserIdentity.AuthenticationType"); Add(this.Request.LogonUserIdentity.Groups, "this.Request.LogonUserIdentity.Groups", "These are the Windows groups which contain the user running your ASP.NET code."); Add(this.Request.LogonUserIdentity.Name, "this.Request.LogonUserIdentity.Name", "This is the Windows user running your ASP.NET code. This user should be granted the permissions needed " + "by your application."); Add(this.Request.LogonUserIdentity.ImpersonationLevel, "this.Request.LogonUserIdentity.ImpersonationLevel", "This is 'Impersonation' if the thread running your code is impersonating a Windows user, otherwise it's 'None'. " + "In IIS 6 and 7, this is normally 'Impersonation' because the ASP.NET threads run under a different user than the " + "IIS worker process."); Add(this.Request.LogonUserIdentity.IsAnonymous, "this.Request.LogonUserIdentity.IsAnonymous"); Add(this.Request.LogonUserIdentity.IsSystem, "this.Request.LogonUserIdentity.IsSystem", "True if your code is running as Local System. This should never be the case."); Add(this.Request.LogonUserIdentity.User.Value, "this.Request.LogonUserIdentity.User.Value", "The SID for the account."); %> <%= EndTable() %>

Paths

Path names in ASP.NET may be confusing, but the concepts are simple:

There are two useful ASP.NET tools to manipulate paths. The HttpServerUtility.MapPath method converts a virtual path to a physical path. The VirtualPathUtility class has several methods for manipulating virtual paths.

<%= StartTable("HttpRequest and Page Paths") %> <% Add(this.Request.ApplicationPath, "this.Request.ApplicationPath", "Gets the IIS root virtual path for " + "your ASP.net application. ASP.NET transforms ~/ into this value when making an absolute virtual path."); Add(this.Request.AppRelativeCurrentExecutionFilePath, "this.Request.AppRelativeCurrentExecutionFilePath", "Returns the app-relative virtual path for the " + "HttpHandler currently executing, normally your page. " + EquivalentTo("this.Page.AppRelativeVirtualPath")); Add(this.Request.CurrentExecutionFilePath, "this.Request.CurrentExecutionFilePath", "Returns the absolute virtual path for the " + "HttpHandler currently executing, normally your page."); Add(this.Request.PhysicalApplicationPath, "this.Request.PhysicalApplicationPath", "The root of your application in the server's file system."); Add(this.Request.PhysicalPath, "this.Request.PhysicalPath", "The file system location for the " + " " + "HttpHandler currently executing, normally your page."); Add(this.Page.TemplateSourceDirectory, "this.Page.TemplateSourceDirectory"); %> <%= EndTable() %>

Click here for an example where the ApplicationPath is not /.

Request.Url

<%= StartTable() %> <% Add(this.Request.Url.AbsolutePath, "this.Request.Url.AbsolutePath"); Add(this.Request.Url.AbsoluteUri, "this.Request.Url.AbsoluteUri"); Add(this.Request.Url.Authority, "this.Request.Url.Authority"); Add(this.Request.Url.DnsSafeHost, "this.Request.Url.DnsSafeHost"); Add(this.Request.Url.Fragment, "this.Request.Url.Fragment"); Add(this.Request.Url.Host, "this.Request.Url.Host"); Add(this.Request.Url.HostNameType, "this.Request.Url.HostNameType"); Add(this.Request.Url.LocalPath, "this.Request.Url.LocalPath"); Add(this.Request.Url.OriginalString, "this.Request.Url.OriginalString"); Add(this.Request.Url.PathAndQuery, "this.Request.Url.PathAndQuery"); Add(this.Request.Url.Port, "this.Request.Url.Port"); Add(this.Request.Url.Query, "this.Request.Url.Query"); Add(this.Request.Url.Scheme, "this.Request.Url.Scheme"); Add(this.Request.Url.Segments, "this.Request.Url.Segments"); Add(this.Request.Url.UserInfo, "this.Request.Url.UserInfo"); %> <%= EndTable() %>

Information from the HTTP request headers

<%= StartTable() %> <% Add(this.Request.AnonymousID, "this.Request.AnonymousID", "Used with ASP.NET anonymous profiles."); Add(this.Request.ContentLength, "this.Request.ContentLength", "Length in bytes for the BODY in the client's request. For a GET request, " + "this is normally zero as there is no body. For a POST request it depends on how much data is sent to the server."); Add(this.Request.TotalBytes, "this.Request.TotalBytes", "This is usually equal to ContentLength, but it could be slightly different " + "depending on how the client encoded its HTTP request."); Add(this.Request.UrlReferrer, "this.Request.UrlReferrer", "The URL " + "that sent the user your way. Bless them."); Add(this.Request.UserAgent, "this.Request.UserAgent", "The client's browser."); Add(this.Request.UserHostAddress, "this.Request.UserHostAddress"); Add(this.Request.UserHostName, "this.Request.UserHostName"); Add(this.Request.UserLanguages, "this.Request.UserLanguages", "User-configured language preferences " + "sent by the browser. You might tune your content based on this."); %> <%= EndTable() %>

Environment

<%= StartTable() %> <% Add(Environment.OSVersion, "Environment.OSVersion"); Add(Environment.WorkingSet, "Environment.WorkingSet", "Amount of physical memory used by the ASP.net worker process"); Add(Thread.CurrentThread.CurrentCulture, "Thread.CurrentThread.CurrentCulture", "Used by string formatting and other culture-sensitive methods."); Add(Thread.CurrentThread.CurrentUICulture, "Thread.CurrentThread.CurrentUICulture", "Used by the ResourceManager to load culture-specific resources like strings and images ."); Add(Environment.UserDomainName, "Environment.UserDomainName"); Add(Environment.UserName, "Environment.UserName", "Do not trust this value. Stick to HttpRequest.LogonUserIdentity"); Add(Environment.Version, "Environment.Version", ".NET Framework version. This returns '2.0' even " + "for assemblies compiled as .NET 3.5."); %> <%= EndTable() %>

HttpContext

<%= StartTable() %> <% Add(HttpContext.Current.SkipAuthorization, "HttpContext.Current.SkipAuthorization"); Add(HttpContext.Current.User.Identity.AuthenticationType, "HttpContext.Current.User.Identity.AuthenticationType"); Add(HttpContext.Current.User.Identity.IsAuthenticated, "HttpContext.Current.User.Identity.IsAuthenticated"); Add(HttpContext.Current.User.Identity.Name, "HttpContext.Current.User.Identity.Name"); Add(HttpContext.Current.Timestamp, "HttpContext.Current.Timestamp"); %> <%= EndTable() %>

HttpRuntime and AppDomain

Your ASP.NET code runs inside a .NET AppDomain, which in turn runs inside a Windows process. The IIS version and configurations determine where your AppDomain will run. IIS 6 and 7 by default run AppDomains in a Worker Process called w3wp.exe. There is one worker process for each IIS application pool. Inside the Worker Process, there is one AppDomain for each application in the pool. Here are some useful tidbits on the current AppDomain and process:

<%= StartTable() %> <% Add(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile, "AppDomain.CurrentDomain.SetupInformation.ConfigurationFile", "Configuration file for the current AppDomain. For ASP.net this is the web.config file."); Add(HttpRuntime.AppDomainAppId, "HttpRuntime.AppDomainAppId", "The /LM/W3SVC part is fixed for a given IIS instance. The following number is the Site ID within IIS. " + "The last piece is the " + "IIS root virtual path for the application (or 'ROOT' for /)."); Add(HttpRuntime.AppDomainAppPath, "HttpRuntime.AppDomainAppPath", "The filesystem folder containing your application. " + EquivalentTo("AppDomain.CurrentDomain.BaseDirectory")); Add(HttpRuntime.AppDomainAppVirtualPath, "HttpRuntime.AppDomainAppVirtualPath"); Add(HttpRuntime.AppDomainId, "HttpRuntime.AppDomainId", EquivalentTo("AppDomain.CurrentDomain.FriendlyName")); Add(HttpRuntime.AspClientScriptPhysicalPath, "HttpRuntime.AspClientScriptPhysicalPath"); Add(HttpRuntime.AspClientScriptVirtualPath, "HttpRuntime.AspClientScriptVirtualPath"); Add(HttpRuntime.AspInstallDirectory, "HttpRuntime.AspInstallDirectory"); Add(HttpRuntime.BinDirectory, "HttpRuntime.BinDirectory", EquivalentTo("AppDomain.CurrentDomain.RelativeSearchPath")); Add(HttpRuntime.ClrInstallDirectory, "HttpRuntime.ClrInstallDirectory"); Add(HttpRuntime.CodegenDir, "HttpRuntime.CodegenDir", "This is where ASP.NET stores generated source files for your pages, controls, etc. Watch this folder to see " + "what a processed page becomes. Pages are compiled to DLLs which are also stored here. " + EquivalentTo("AppDomain.CurrentDomain.DynamicDirectory")); Add(HttpRuntime.MachineConfigurationDirectory, "HttpRuntime.MachineConfigurationDirectory"); %> <%= EndTable() %>

Loaded Assemblies

.NET code is packaged into .NET assemblies. It is often important to know which assemblies are loaded into your ASP.NET AppDomain because many problems boil down to missing or incorrect assemblies. The method <%= MsdnLinkFromSnippet("AppDomain.CurrentDomain.GetAssemblies")%> returns a list of assemblies loaded in your AppDomain (it requires full trust). Here's the output for the current domain:

Count of loaded assemblies: <%= AppDomain.CurrentDomain.GetAssemblies().Length %>

<% foreach (Assembly a in AppDomain.CurrentDomain.GetAssemblies()) {%> <% } %>
Assemblies

<%= HttpUtility.HtmlEncode(a.ManifestModule.FullyQualifiedName)%>

<%= HttpUtility.HtmlEncode(a.FullName) %>