// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Security;
using System.Text;
using System.Web.Helpers.Resources;
using System.Web.WebPages;
namespace System.Web.Helpers
{
///
/// Provides various info about ASP.NET server.
///
public static class ServerInfo
{
///
/// todo: figure out right place for this
///
private const string Style =
"";
internal static IDictionary EnvironmentVariables()
{
// todo: extract well defined subset for special use?
// use a case-insensitive dictionary since environment variables are case-insensitive.
IDictionary environmentVariablesResult = new SortedDictionary(StringComparer.OrdinalIgnoreCase);
IDictionary environmentVariables;
// todo: better way to deal with security; query config for trust level?
try
{
environmentVariables = Environment.GetEnvironmentVariables();
}
catch (SecurityException)
{
return environmentVariablesResult;
}
foreach (DictionaryEntry entry in environmentVariables)
{
environmentVariablesResult.Add(entry.Key.ToString(), InsertWhiteSpace(entry.Value.ToString()));
}
return environmentVariablesResult;
}
internal static IDictionary ServerVariables()
{
var httpContext = HttpContext.Current;
return ServerVariables(httpContext != null ? new HttpContextWrapper(httpContext) : null);
}
internal static IDictionary ServerVariables(HttpContextBase context)
{
// todo: extract well defined subset for special use?
IDictionary serverVariablesResult = new SortedDictionary();
NameValueCollection serverVariables;
// todo: better way to deal with security; query config for trust level?
try
{
if ((context != null) && (context.Request != null))
{
serverVariables = context.Request.ServerVariables;
}
else
{
// Just return empty collection when there is no context available.
return serverVariablesResult;
}
}
catch (SecurityException)
{
return serverVariablesResult;
}
foreach (string key in serverVariables.AllKeys)
{
// todo: these values contains very long strings with no spaces that distorts table layout - figure out how to deal with it
if (key.Equals("ALL_HTTP", StringComparison.OrdinalIgnoreCase) ||
key.Equals("ALL_RAW", StringComparison.OrdinalIgnoreCase) ||
key.Equals("HTTP_AUTHORIZATION", StringComparison.OrdinalIgnoreCase) ||
key.Equals("HTTP_COOKIE", StringComparison.OrdinalIgnoreCase))
{
continue;
}
serverVariablesResult.Add(key, InsertWhiteSpace(serverVariables[key]));
}
return serverVariablesResult;
}
internal static IDictionary Configuration()
{
IDictionary info = new Dictionary();
// todo: do we need to localize these strings or would that be confusing
// (Since we just display API names that are all in English)
info.Add("Current Local Time", DateTime.Now.ToString(CultureInfo.CurrentCulture));
info.Add("Current UTC Time", DateTime.UtcNow.ToString(CultureInfo.CurrentCulture));
info.Add("Current Culture", CultureInfo.CurrentCulture.DisplayName);
info.Add("Machine Name", Environment.MachineName);
info.Add("OS Version", Environment.OSVersion.ToString());
info.Add("ASP.NET Version", Environment.Version.ToString());
info.Add("ASP.NET Web Pages Version", new AssemblyName(typeof(WebPage).Assembly.FullName).Version.ToString());
info.Add("User Name", Environment.UserName);
info.Add("User Interactive", Environment.UserInteractive.ToString());
info.Add("Processor Count", Environment.ProcessorCount.ToString(CultureInfo.InvariantCulture));
info.Add("Tick Count", Environment.TickCount.ToString(CultureInfo.InvariantCulture));
// Calls bellow require full trust.
try
{
info.Add("Current Directory", Environment.CurrentDirectory);
}
catch (SecurityException)
{
return info;
}
info.Add("System Directory", Environment.SystemDirectory);
info.Add("User Domain Name", Environment.UserDomainName);
info.Add("Working Set", Environment.WorkingSet.ToString(CultureInfo.InvariantCulture) + " bytes");
return info;
}
internal static IDictionary HttpRuntimeInfo()
{
IDictionary info = new Dictionary();
// todo: better way to deal with security; query config for trust level?
try
{
info.Add("CLR Install Directory", HttpRuntime.ClrInstallDirectory);
}
catch (SecurityException)
{
return info;
}
try
{
info.Add("Codegen Directory", HttpRuntime.CodegenDir);
info.Add("Bin Directory", HttpRuntime.BinDirectory);
info.Add("AppDomain Application Path", HttpRuntime.AppDomainAppPath);
}
catch (ArgumentException)
{
// do nothing
// These APIs don't check if path is set before setting security demands, which causes exception.
// So far this happens only when running from unit tests.
}
info.Add("Asp Install Directory", HttpRuntime.AspInstallDirectory);
info.Add("Machine Configuration Directory", HttpRuntime.MachineConfigurationDirectory);
info.Add("AppDomain Id", HttpRuntime.AppDomainId);
info.Add("AppDomain Application Id", HttpRuntime.AppDomainAppId);
info.Add("AppDomain Application Virtual Path", HttpRuntime.AppDomainAppVirtualPath);
info.Add("Asp Client Script Physical Path", HttpRuntime.AspClientScriptPhysicalPath);
info.Add("Asp Client Script Virtual Path", HttpRuntime.AspClientScriptVirtualPath);
info.Add("Cache Size", HttpRuntime.Cache.Count.ToString(CultureInfo.InvariantCulture));
info.Add("Cache Effective Percentage Physical Memory Limit", HttpRuntime.Cache.EffectivePercentagePhysicalMemoryLimit.ToString(CultureInfo.InvariantCulture));
info.Add("Cache Effective Private Bytes Limit", HttpRuntime.Cache.EffectivePrivateBytesLimit.ToString(CultureInfo.InvariantCulture));
info.Add("On UNC Share", HttpRuntime.IsOnUNCShare.ToString());
return info;
}
internal static IDictionary LegacyCAS()
{
return LegacyCAS(AppDomain.CurrentDomain);
}
internal static IDictionary LegacyCAS(AppDomain appDomain)
{
IDictionary info = new Dictionary();
try
{
bool legacyCasModeEnabled = !appDomain.IsHomogenous;
if (legacyCasModeEnabled)
{
info[HelpersResources.ServerInfo_LegacyCAS] = HelpersResources.ServerInfo_LegacyCasHelpInfo;
}
}
catch (SecurityException)
{
return info;
}
return info;
}
///
/// Generates HTML required to display server information.
///
///
/// HTML generated is XHTML 1.0 compliant but not XHTML 1.1 or HTML5 compliant. The reason is that we
/// generate <style> tag inside <body> tag, which is not allowed. This is by design for now since ServerInfo
/// is debugging aid and should not be used as a permanent part of any web page.
///
[SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate",
Justification = "This could be time consuming operation that does not just retrieve a field.")]
public static HtmlString GetHtml()
{
StringBuilder sb = new StringBuilder(Style);
sb.AppendLine(String.Format(CultureInfo.InvariantCulture, "{0}
",
HttpUtility.HtmlEncode(HelpersResources.ServerInfo_Header)));
var configuration = Configuration();
Debug.Assert((configuration != null) && (configuration.Count > 0));
PrintInfoSection(sb, HttpUtility.HtmlEncode(HelpersResources.ServerInfo_ServerConfigTable), configuration);
var serverVariables = ServerVariables();
Debug.Assert((serverVariables != null));
PrintInfoSection(sb, HelpersResources.ServerInfo_ServerVars, serverVariables);
var legacyCAS = LegacyCAS();
if (legacyCAS.Any())
{
PrintInfoSection(sb, HelpersResources.ServerInfo_LegacyCAS, legacyCAS);
}
// Info below is not available in medium trust.
var httpRuntimeInfo = HttpRuntimeInfo();
Debug.Assert(httpRuntimeInfo != null);
if (!httpRuntimeInfo.Any())
{
sb.AppendLine(String.Format(CultureInfo.InvariantCulture, "{0}
",
HttpUtility.HtmlEncode(HelpersResources.ServerInfo_AdditionalInfo)));
return new HtmlString(sb.ToString());
}
else
{
PrintInfoSection(sb, HelpersResources.ServerInfo_HttpRuntime, httpRuntimeInfo);
var envVariables = EnvironmentVariables();
Debug.Assert(envVariables != null);
PrintInfoSection(sb, HelpersResources.ServerInfo_EnvVars, envVariables);
}
return new HtmlString(sb.ToString());
}
///
/// Renders a table section printing out rows and columns.
///
private static void PrintInfoSection(StringBuilder builder, string sectionTitle, IDictionary entries)
{
builder.AppendLine("");
builder.AppendLine("
");
if (!String.IsNullOrEmpty(sectionTitle))
{
builder.AppendLine("");
builder.AppendFormat(CultureInfo.InvariantCulture, "{0}
", HttpUtility.HtmlEncode(sectionTitle)).AppendLine();
builder.AppendLine("");
}
builder.AppendLine(" ");
builder.AppendLine("");
foreach (var entry in entries)
{
var css = String.Empty;
string value = entry.Value;
if (entry.Key == HelpersResources.ServerInfo_LegacyCAS)
{
// TODO: suboptimal solution, but its easier to do this than come up with something that works better
css = "warn";
}
else if (String.IsNullOrEmpty(entry.Value))
{
css = "ital";
value = HelpersResources.ServerInfo_NoValue;
}
if (css.Any())
{
css = " class=\"" + css + "\"";
}
builder.Append("");
builder.AppendFormat(CultureInfo.InvariantCulture, "{0} | ", HttpUtility.HtmlEncode(entry.Key));
builder.AppendFormat(CultureInfo.InvariantCulture, "{1} | ", css, HttpUtility.HtmlEncode(value));
builder.AppendLine("
");
}
builder.AppendLine("");
builder.AppendLine("
");
builder.AppendLine("
");
}
///
/// Inserts spaces after ',' and ';' so table can be rendered properly.
///
private static string InsertWhiteSpace(string s)
{
return s.Replace(",", ", ").Replace(";", "; ");
}
}
}