This repository has been archived by the owner on Nov 25, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 104
/
ServerInfo.cs
329 lines (282 loc) · 14.1 KB
/
ServerInfo.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
// 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
{
/// <summary>
/// Provides various info about ASP.NET server.
/// </summary>
public static class ServerInfo
{
/// <remarks>
/// todo: figure out right place for this
/// </remarks>
private const string Style =
"<style type=\"text/css\">" +
" div.server-info { text-align: center; }" +
" table.server-info { border-collapse:collapse; text-align:center; margin: auto; width:600px; direction: ltr; }" +
" table.server-info tbody tr:nth-child(even){ background-color: #EEE; }" +
" table.server-info, table.server-info th, table.server-info td { border:1px solid black; }" +
" table.server-info th, table.server-info td " +
" { text-align:left; padding:2px; font-family:Tahoma, Arial, sans-serif; font-size:0.75em; }" +
" h1.server-info { font-family:Tahoma, Arial, sans-serif; font-size:150%; text-align:center; }" +
" table.server-info h2 { font-family:Tahoma, Arial, sans-serif; font-size:125%; text-align:center; }" +
" p.server-info { text-align:center; font-family:Tahoma, Arial, sans-serif; font-size:0.75em; }" +
" .ital { font-style: italic; } " +
" .warn { color: #F00; } " +
"</style>";
internal static IDictionary<string, string> EnvironmentVariables()
{
// todo: extract well defined subset for special use?
// use a case-insensitive dictionary since environment variables are case-insensitive.
IDictionary<string, string> environmentVariablesResult = new SortedDictionary<string, string>(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<string, string> ServerVariables()
{
var httpContext = HttpContext.Current;
return ServerVariables(httpContext != null ? new HttpContextWrapper(httpContext) : null);
}
internal static IDictionary<string, string> ServerVariables(HttpContextBase context)
{
// todo: extract well defined subset for special use?
IDictionary<string, string> serverVariablesResult = new SortedDictionary<string, string>();
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<string, string> Configuration()
{
IDictionary<string, string> info = new Dictionary<string, string>();
// 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<string, string> HttpRuntimeInfo()
{
IDictionary<string, string> info = new Dictionary<string, string>();
// 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<string, string> LegacyCAS()
{
return LegacyCAS(AppDomain.CurrentDomain);
}
internal static IDictionary<string, string> LegacyCAS(AppDomain appDomain)
{
IDictionary<string, string> info = new Dictionary<string, string>();
try
{
bool legacyCasModeEnabled = !appDomain.IsHomogenous;
if (legacyCasModeEnabled)
{
info[HelpersResources.ServerInfo_LegacyCAS] = HelpersResources.ServerInfo_LegacyCasHelpInfo;
}
}
catch (SecurityException)
{
return info;
}
return info;
}
/// <summary>
/// Generates HTML required to display server information.
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
[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, "<h1 class=\"server-info\">{0}</h1>",
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, "<p class=\"server-info\">{0}</p>",
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());
}
/// <summary>
/// Renders a table section printing out rows and columns.
/// </summary>
private static void PrintInfoSection(StringBuilder builder, string sectionTitle, IDictionary<string, string> entries)
{
builder.AppendLine("<div class=\"server-info\">");
builder.AppendLine("<table class=\"server-info\" dir=\"ltr\">");
if (!String.IsNullOrEmpty(sectionTitle))
{
builder.AppendLine("<caption>");
builder.AppendFormat(CultureInfo.InvariantCulture, "<h2>{0}</h2>", HttpUtility.HtmlEncode(sectionTitle)).AppendLine();
builder.AppendLine("</caption>");
}
builder.AppendLine("<colgroup><col style=\"width:30%;\" /> <col style=\"width:70%;\" /></colgroup>");
builder.AppendLine("<tbody>");
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("<tr>");
builder.AppendFormat(CultureInfo.InvariantCulture, "<th scope=\"row\">{0}</th>", HttpUtility.HtmlEncode(entry.Key));
builder.AppendFormat(CultureInfo.InvariantCulture, "<td{0}>{1}</td>", css, HttpUtility.HtmlEncode(value));
builder.AppendLine("</tr>");
}
builder.AppendLine("</tbody>");
builder.AppendLine("</table>");
builder.AppendLine("</div>");
}
/// <summary>
/// Inserts spaces after ',' and ';' so table can be rendered properly.
/// </summary>
private static string InsertWhiteSpace(string s)
{
return s.Replace(",", ", ").Replace(";", "; ");
}
}
}