32fc9254af35d071718988e0d3ea2479b40d2438
[mono.git] / mcs / class / System.Web / System.Web / HttpRuntime.cs
1 //
2 // System.Web.HttpRuntime.cs 
3 // 
4 // Author:
5 //      Miguel de Icaza (miguel@novell.com)
6 //
7 //
8 // Copyright (C) 2005 Novell, Inc (http://www.novell.com)
9 //
10 // Permission is hereby granted, free of charge, to any person obtaining
11 // a copy of this software and associated documentation files (the
12 // "Software"), to deal in the Software without restriction, including
13 // without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to
15 // permit persons to whom the Software is furnished to do so, subject to
16 // the following conditions:
17 // 
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
20 // 
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 //
29
30 //
31 // TODO: Call HttpRequest.CloseInputStream when we finish a request, as we are using the IntPtr stream.
32 //
33 using System.IO;
34 using System.Text;
35 using System.Globalization;
36 using System.Collections;
37 using System.Security;
38 using System.Security.Permissions;
39 using System.Web.Caching;
40 using System.Web.Configuration;
41 using System.Web.UI;
42 using System.Threading;
43
44 #if NET_2_0 && !TARGET_JVM
45 using System.CodeDom.Compiler;
46 using System.Web.Compilation;
47 #endif
48
49 namespace System.Web {
50         
51         // CAS - no InheritanceDemand here as the class is sealed
52         [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
53         public sealed class HttpRuntime {
54 #if TARGET_J2EE
55                 static QueueManager queue_manager { get { return _runtime._queue_manager; } }
56                 static TraceManager trace_manager { get { return _runtime._trace_manager; } }
57                 static Cache cache { get { return _runtime._cache; } }
58                 static WaitCallback do_RealProcessRequest;
59
60                 QueueManager _queue_manager;
61                 TraceManager _trace_manager;
62                 Cache _cache;
63
64                 static HttpRuntime ()
65                 {
66                         do_RealProcessRequest = new WaitCallback (RealProcessRequest);
67                 }
68
69                 public HttpRuntime ()
70                 {
71                         _queue_manager = new QueueManager ();
72                         _trace_manager = new TraceManager ();
73                         _cache = new Cache ();
74                 }
75
76                 static private HttpRuntime _runtime {
77                         get {
78                                 HttpRuntime runtime = (HttpRuntime)AppDomain.CurrentDomain.GetData("HttpRuntime");
79                                 if (runtime == null)
80                                         lock (typeof(HttpRuntime)) {
81                                                 runtime = (HttpRuntime)AppDomain.CurrentDomain.GetData("HttpRuntime");
82                                                 if (runtime == null) {
83                                                         runtime = new HttpRuntime();
84                                                         AppDomain.CurrentDomain.SetData("HttpRuntime", runtime);
85                                                 }
86                                         }
87                                 return runtime;
88                         }
89                 }
90 #else
91                 static QueueManager queue_manager;
92                 static TraceManager trace_manager;
93                 static TimeoutManager timeout_manager;
94                 static Cache cache;
95                 static WaitCallback do_RealProcessRequest;
96
97                 static HttpRuntime ()
98                 {
99                         queue_manager = new QueueManager ();
100                         trace_manager = new TraceManager ();
101                         timeout_manager = new TimeoutManager ();
102                         cache = new Cache ();
103                         do_RealProcessRequest = new WaitCallback (RealProcessRequest);
104                 }
105
106 #if ONLY_1_1
107                 [SecurityPermission (SecurityAction.Demand, UnmanagedCode = true)]
108 #endif
109                 public HttpRuntime ()
110                 {
111                 }
112 #endif
113
114 #region AppDomain handling
115                 //
116                 // http://radio.weblogs.com/0105476/stories/2002/07/12/executingAspxPagesWithoutAWebServer.html
117                 //
118                 public static string AppDomainAppId {
119                         [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.High)]
120                         get {
121                                 //
122                                 // This value should not change across invocations
123                                 //
124                                 string dirname = (string) AppDomain.CurrentDomain.GetData (".appId");
125                                 if ((dirname != null) && (dirname.Length > 0) && SecurityManager.SecurityEnabled) {
126                                         new FileIOPermission (FileIOPermissionAccess.PathDiscovery, dirname).Demand ();
127                                 }
128                                 return dirname;
129                         }
130                 }
131
132                 // Physical directory for the application
133                 public static string AppDomainAppPath {
134                         get {
135                                 string dirname = (string) AppDomain.CurrentDomain.GetData (".appPath");
136                                 if (SecurityManager.SecurityEnabled) {
137                                         new FileIOPermission (FileIOPermissionAccess.PathDiscovery, dirname).Demand ();
138                                 }
139                                 return dirname;
140                         }
141                 }
142
143                 public static string AppDomainAppVirtualPath {
144                         get {
145                                 return (string) AppDomain.CurrentDomain.GetData (".appVPath");
146                         }
147                 }
148
149                 public static string AppDomainId {
150                         [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.High)]
151                         get {
152                                 return (string) AppDomain.CurrentDomain.GetData (".domainId");
153                         }
154                 }
155
156                 public static string AspInstallDirectory {
157                         get {
158                                 string dirname = (string) AppDomain.CurrentDomain.GetData (".hostingInstallDir");
159                                 if ((dirname != null) && (dirname.Length > 0) && SecurityManager.SecurityEnabled) {
160                                         new FileIOPermission (FileIOPermissionAccess.PathDiscovery, dirname).Demand ();
161                                 }
162                                 return dirname;
163                         }
164                 }
165 #endregion
166                 
167                 public static string BinDirectory {
168                         get {
169                                 string dirname = Path.Combine (AppDomainAppPath, "bin");
170                                 if (SecurityManager.SecurityEnabled) {
171                                         new FileIOPermission (FileIOPermissionAccess.PathDiscovery, dirname).Demand ();
172                                 }
173                                 return dirname;
174                         }
175                 }
176
177                 public static Cache Cache {
178                         get {
179                                 return cache;
180                         }
181                 }
182
183                 public static string ClrInstallDirectory {
184                         get {
185                                 string dirname = Path.GetDirectoryName (typeof (Object).Assembly.Location);
186                                 if ((dirname != null) && (dirname.Length > 0) && SecurityManager.SecurityEnabled) {
187                                         new FileIOPermission (FileIOPermissionAccess.PathDiscovery, dirname).Demand ();
188                                 }
189                                 return dirname;
190                         }
191                 }
192
193                 public static string CodegenDir {
194                         get {
195                                 string dirname = AppDomain.CurrentDomain.SetupInformation.DynamicBase;
196                                 if (SecurityManager.SecurityEnabled) {
197                                         new FileIOPermission (FileIOPermissionAccess.PathDiscovery, dirname).Demand ();
198                                 }
199                                 return dirname;
200                         }
201                 }
202
203                 [MonoTODO]
204                 public static bool IsOnUNCShare {
205                         [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Low)]
206                         get {
207                                 throw new NotImplementedException ();
208                         }
209                 }
210
211                 public static string MachineConfigurationDirectory {
212                         get {
213 #if NET_2_0
214                                 string dirname = Path.GetDirectoryName (WebConfigurationManager.OpenMachineConfiguration().FilePath);
215 #else
216                                 string dirname = Path.GetDirectoryName (WebConfigurationSettings.MachineConfigPath);
217 #endif
218                                 if ((dirname != null) && (dirname.Length > 0) && SecurityManager.SecurityEnabled) {
219                                         new FileIOPermission (FileIOPermissionAccess.PathDiscovery, dirname).Demand ();
220                                 }
221                                 return dirname;
222                         }
223                 }
224
225                 [SecurityPermission (SecurityAction.Demand, UnmanagedCode = true)]
226                 public static void Close ()
227                 {
228                         // Remove all items from cache.
229                 }
230
231                 static void QueuePendingRequests ()
232                 {
233                         HttpWorkerRequest request = queue_manager.GetNextRequest (null);
234                         if (request == null)
235                                 return;
236                         ThreadPool.QueueUserWorkItem (do_RealProcessRequest, request);
237                 }
238
239                 static void RealProcessRequest (object o)
240                 {
241                         HttpContext context = new HttpContext ((HttpWorkerRequest) o);
242                         HttpContext.Current = context;
243
244                         //
245                         // Get application instance (create or reuse an instance of the correct class)
246                         //
247                         HttpApplication app = null;
248                         bool error = false;
249                         try {
250                                 app = HttpApplicationFactory.GetApplication (context);
251                         } catch (Exception e) {
252                                 FinishWithException ((HttpWorkerRequest) o, new HttpException ("", e));
253                                 error = true;
254                         }
255
256                         if (error) {
257                                 context.Request.ReleaseResources ();
258                                 context.Response.ReleaseResources ();
259                                 HttpContext.Current = null;
260                         } else {
261                                 context.ApplicationInstance = app;
262                         
263 #if NET_2_0 && !TARGET_JVM
264                                 //
265                                 // Compile the local resources, if any
266                                 //
267                                 AppLocalResourcesCompiler alrc = new AppLocalResourcesCompiler();
268                                 alrc.Compile();
269 #endif
270                                 
271                                 //
272                                 // Ask application to service the request
273                                 //
274                                 IHttpAsyncHandler ihah = app;
275
276                                 IAsyncResult appiar = ihah.BeginProcessRequest (context, new AsyncCallback (request_processed), context);
277                                 ihah.EndProcessRequest (appiar);
278
279                                 HttpApplicationFactory.Recycle (app);
280                         }
281                         
282                         QueuePendingRequests ();
283                 }
284                 
285                 //
286                 // ProcessRequest method is executed in the AppDomain of the application
287                 //
288                 // Observations:
289                 //    ProcessRequest does not guarantee that `wr' will be processed synchronously,
290                 //    the request can be queued and processed later.
291                 //
292                 [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Medium)]
293                 public static void ProcessRequest (HttpWorkerRequest wr)
294                 {
295                         if (wr == null)
296                                 throw new ArgumentNullException ("wr");
297                         //
298                         // Queue our request, fetch the next available one from the queue
299                         //
300                         HttpWorkerRequest request = queue_manager.GetNextRequest (wr);
301                         if (request == null)
302                                 return;
303
304                         RealProcessRequest (request);
305                 }
306
307                 //
308                 // Callback to be invoked by IHttpAsyncHandler.BeginProcessRequest
309                 //
310                 static void request_processed (IAsyncResult iar)
311                 {
312                         HttpContext context = (HttpContext) iar.AsyncState;
313
314                         context.Request.ReleaseResources ();
315                         context.Response.ReleaseResources ();
316                 }
317
318                 //
319                 // Called when we are shutting down or we need to reload an application
320                 // that has been modified (touch global.asax) 
321                 //
322                 [SecurityPermission (SecurityAction.Demand, UnmanagedCode = true)]
323                 public static void UnloadAppDomain ()
324                 {
325                         //
326                         // TODO: call ReleaseResources
327                         //
328                         ThreadPool.QueueUserWorkItem (new WaitCallback (ShutdownAppDomain), null);
329                 }
330
331                 //
332                 // Shuts down the AppDomain
333                 //
334                 static void ShutdownAppDomain (object args)
335                 {
336                         queue_manager.Dispose ();
337                         // This will call Session_End if needed.
338                         Cache.InvokePrivateCallbacks ();
339                         // Kill our application.
340                         HttpApplicationFactory.Dispose ();
341                         ThreadPool.QueueUserWorkItem (new WaitCallback (DoUnload), null);
342                 }
343
344 #if TARGET_J2EE // No unload support for appdomains under Grasshopper
345                 static void DoUnload (object state)
346                 {
347                 }
348 #else
349                 static void DoUnload (object state)
350                 {
351                         AppDomain.Unload (AppDomain.CurrentDomain);
352                 }
353 #endif
354
355                 static string content503 = "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n" +
356                         "<html><head>\n<title>503 Server Unavailable</title>\n</head><body>\n" +
357                         "<h1>Server Unavailable</h1>\n" +
358                         "</body></html>\n";
359
360                 static void FinishWithException (HttpWorkerRequest wr, HttpException e)
361                 {
362                         int code = e.GetHttpCode ();
363                         wr.SendStatus (code, HttpWorkerRequest.GetStatusDescription (code));
364                         wr.SendUnknownResponseHeader ("Connection", "close");
365                         wr.SendUnknownResponseHeader ("Date", DateTime.Now.ToUniversalTime ().ToString ("r"));
366                         Encoding enc = Encoding.ASCII;
367                         wr.SendUnknownResponseHeader ("Content-Type", "text/html; charset=" + enc.WebName);
368                         string msg = e.GetHtmlErrorMessage ();
369                         byte [] contentBytes = enc.GetBytes (msg);
370                         wr.SendUnknownResponseHeader ("Content-Length", contentBytes.Length.ToString ());
371                         wr.SendResponseFromMemory (contentBytes, contentBytes.Length);
372                         wr.FlushResponse (true);
373                         wr.CloseConnection ();
374                 }
375
376                 //
377                 // This is called from the QueueManager if a request
378                 // can not be processed (load, no resources, or
379                 // appdomain unload).
380                 //
381                 static internal void FinishUnavailable (HttpWorkerRequest wr)
382                 {
383                         wr.SendStatus (503, "Service unavailable");
384                         wr.SendUnknownResponseHeader ("Connection", "close");
385                         wr.SendUnknownResponseHeader ("Date", DateTime.Now.ToUniversalTime ().ToString ("r"));
386                         Encoding enc = Encoding.ASCII;
387                         wr.SendUnknownResponseHeader ("Content-Type", "text/html; charset=" + enc.WebName);
388                         byte [] contentBytes = enc.GetBytes (content503);
389                         wr.SendUnknownResponseHeader ("Content-Length", contentBytes.Length.ToString ());
390                         wr.SendResponseFromMemory (contentBytes, contentBytes.Length);
391                         wr.FlushResponse (true);
392                         wr.CloseConnection ();
393                 }
394
395                 internal static TraceManager TraceManager {
396                         get {
397                                 return trace_manager;
398                         }
399                 }
400
401 #if !TARGET_JVM
402                 internal static TimeoutManager TimeoutManager {
403                         get {
404                                 return timeout_manager;
405                         }
406                 }
407 #endif
408         }
409 }