1 // Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information. 2 3 using System.Collections.Generic; 4 using System.Globalization; 5 using System.IO; 6 using System.Reflection; 7 using System.Threading; 8 using System.Web.Caching; 9 using System.Web.Hosting; 10 using Microsoft.Web.Infrastructure; 11 12 namespace System.Web.WebPages 13 { 14 public abstract class ApplicationStartPage : WebPageExecutingBase 15 { 16 private static readonly Action<Action> _safeExecuteStartPageThunk = GetSafeExecuteStartPageThunk(); 17 public static readonly string StartPageVirtualPath = "~/_appstart."; 18 public static readonly string CacheKeyPrefix = "__AppStartPage__"; 19 20 public HttpApplication Application { get; internal set; } 21 22 public override HttpContextBase Context 23 { 24 get { return new HttpContextWrapper(Application.Context); } 25 } 26 27 public static HtmlString Markup { get; private set; } 28 29 internal static Exception Exception { get; private set; } 30 31 public TextWriter Output { get; internal set; } 32 33 public override string VirtualPath 34 { 35 get { return StartPageVirtualPath; } 36 set 37 { 38 // The virtual path for the start page is fixed for now. 39 throw new NotSupportedException(); 40 } 41 } 42 ExecuteInternal()43 internal void ExecuteInternal() 44 { 45 // See comments in GetSafeExecuteStartPageThunk(). 46 _safeExecuteStartPageThunk(() => 47 { 48 Output = new StringWriter(CultureInfo.InvariantCulture); 49 Execute(); 50 Markup = new HtmlString(Output.ToString()); 51 }); 52 } 53 ExecuteStartPage(HttpApplication application)54 internal static void ExecuteStartPage(HttpApplication application) 55 { 56 ExecuteStartPage(application, 57 vpath => MonitorFile(vpath), 58 VirtualPathFactoryManager.Instance, 59 WebPageHttpHandler.GetRegisteredExtensions()); 60 } 61 ExecuteStartPage(HttpApplication application, Action<string> monitorFile, IVirtualPathFactory virtualPathFactory, IEnumerable<string> supportedExtensions)62 internal static void ExecuteStartPage(HttpApplication application, Action<string> monitorFile, IVirtualPathFactory virtualPathFactory, IEnumerable<string> supportedExtensions) 63 { 64 try 65 { 66 ExecuteStartPageInternal(application, monitorFile, virtualPathFactory, supportedExtensions); 67 } 68 catch (Exception e) 69 { 70 // Throw it as a HttpException so as to 71 // display the original stack trace information. 72 Exception = e; 73 throw new HttpException(null, e); 74 } 75 } 76 ExecuteStartPageInternal(HttpApplication application, Action<string> monitorFile, IVirtualPathFactory virtualPathFactory, IEnumerable<string> supportedExtensions)77 internal static void ExecuteStartPageInternal(HttpApplication application, Action<string> monitorFile, IVirtualPathFactory virtualPathFactory, IEnumerable<string> supportedExtensions) 78 { 79 ApplicationStartPage startPage = null; 80 81 foreach (var extension in supportedExtensions) 82 { 83 var vpath = StartPageVirtualPath + extension; 84 85 // We need to monitor regardless of existence because the user could add/remove the 86 // file at any time. 87 monitorFile(vpath); 88 if (!virtualPathFactory.Exists(vpath)) 89 { 90 continue; 91 } 92 93 if (startPage == null) 94 { 95 startPage = virtualPathFactory.CreateInstance<ApplicationStartPage>(vpath); 96 startPage.Application = application; 97 startPage.VirtualPathFactory = virtualPathFactory; 98 startPage.ExecuteInternal(); 99 } 100 } 101 } 102 GetSafeExecuteStartPageThunk()103 private static Action<Action> GetSafeExecuteStartPageThunk() 104 { 105 // Programmatically detect if this version of System.Web.dll suffers from a bug in 106 // which HttpUtility.HtmlEncode can't be called from Application_Start, and if so 107 // set the current HttpContext to null to work around it. 108 // 109 // See Dev10 #906296 and Dev10 #898600 for more information. 110 111 if (typeof(HttpResponse).GetProperty("DisableCustomHttpEncoder", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly) != null) 112 { 113 // this version suffers from the bug 114 return HttpContextHelper.ExecuteInNullContext; 115 } 116 else 117 { 118 // this version does not suffer from the bug 119 return action => action(); 120 } 121 } 122 InitiateShutdown(string key, object value, CacheItemRemovedReason reason)123 private static void InitiateShutdown(string key, object value, CacheItemRemovedReason reason) 124 { 125 // Only handle case when the dependency has changed. 126 if (reason != CacheItemRemovedReason.DependencyChanged) 127 { 128 return; 129 } 130 131 ThreadPool.QueueUserWorkItem(new WaitCallback(ShutdownCallBack)); 132 } 133 MonitorFile(string virtualPath)134 private static void MonitorFile(string virtualPath) 135 { 136 var virtualPathDependencies = new List<string>(); 137 virtualPathDependencies.Add(virtualPath); 138 CacheDependency cacheDependency = HostingEnvironment.VirtualPathProvider.GetCacheDependency( 139 virtualPath, virtualPathDependencies, DateTime.UtcNow); 140 var key = CacheKeyPrefix + virtualPath; 141 142 HttpRuntime.Cache.Insert(key, virtualPath, cacheDependency, 143 Cache.NoAbsoluteExpiration, Cache.NoSlidingExpiration, 144 CacheItemPriority.NotRemovable, new CacheItemRemovedCallback(InitiateShutdown)); 145 } 146 ShutdownCallBack(object state)147 private static void ShutdownCallBack(object state) 148 { 149 InfrastructureHelper.UnloadAppDomain(); 150 } 151 Write(HelperResult result)152 public override void Write(HelperResult result) 153 { 154 if (result != null) 155 { 156 result.WriteTo(Output); 157 } 158 } 159 WriteLiteral(object value)160 public override void WriteLiteral(object value) 161 { 162 Output.Write(value); 163 } 164 Write(object value)165 public override void Write(object value) 166 { 167 Output.Write(HttpUtility.HtmlEncode(value)); 168 } 169 GetOutputWriter()170 protected internal override TextWriter GetOutputWriter() 171 { 172 return Output; 173 } 174 } 175 } 176