In this short post I would like to present you am interesting fact about app_offline.htm. Let’s start with a small puzzle. Imagine you have 2 files in your IIS application folder: web.config and app_offline.htm. Web.config contains following lines:
<?xml version="1.0"?> <configuration> </configuration
and app_offline.htm:
We'll be back in a moment.
Notice that in the web.config file the configuration tag is not closed. Now the question is: what will you see if you try to access your application from the browser?
I was a bit surprised when I saw HTTP Error 500.19 – Internal Server Error:
instead of the content of my app_offline.htm file. From the image we can also see that it’s IIS Web Core that generates this error. To understand what happened I needed to find a method in ASP.NET framework that renders this special file. It didn’t take long till I stumbled upon a CheckApplicationEnabled method in the System.Web.HttpRuntime class (decompiled with dotPeek):
internal static void CheckApplicationEnabled() { string str = Path.Combine(HttpRuntime._theRuntime._appDomainAppPath, "App_Offline.htm"); bool flag = false; HttpRuntime._theRuntime._fcm.StartMonitoringFile(str, new FileChangeEventHandler(HttpRuntime._theRuntime.OnAppOfflineFileChange)); try { if (System.IO.File.Exists(str)) { using (FileStream fileStream = new FileStream(str, FileMode.Open, FileAccess.Read, FileShare.Read)) { if (fileStream.Length <= 1048576L) { int count = (int) fileStream.Length; if (count > 0) { byte[] buffer = new byte[count]; if (fileStream.Read(buffer, 0, count) == count) { HttpRuntime._theRuntime._appOfflineMessage = buffer; flag = true; } } else { flag = true; HttpRuntime._theRuntime._appOfflineMessage = new byte[0]; } } } } } catch { } if (flag) throw new HttpException(503, string.Empty); if (!RuntimeConfig.GetAppConfig().HttpRuntime.Enable) throw new HttpException(404, string.Empty); }
This method is called either on HttpRuntime initialization or when a special application pool is created.
To summarize, it’s HttpRuntime that generates 503 response code and renders a friendly error message. So if anything happens while HttpRuntime is initializing (missing dlls, invalid configuration file etc.) you won’t see this message but less friendly 500 Internal Error. With IIS 7 and above IIS Web Core module will stop request execution even before it reaches the managed runtime.
Simple but useful 🙂