* HttpContext.jvm.cs, HttpRuntime.cs: cached instance of HttpRuntime in context in...
[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.Reflection;
38 using System.Security;
39 using System.Security.Permissions;
40 using System.Web.Caching;
41 using System.Web.Configuration;
42 using System.Web.UI;
43 using System.Threading;
44
45 #if NET_2_0 && !TARGET_JVM
46 using System.CodeDom.Compiler;
47 using System.Web.Compilation;
48 #endif
49
50 namespace System.Web {
51         
52         // CAS - no InheritanceDemand here as the class is sealed
53         [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
54         public sealed class HttpRuntime {
55 #if TARGET_J2EE
56                 static QueueManager queue_manager { get { return _runtime._queue_manager; } }
57                 static TraceManager trace_manager { get { return _runtime._trace_manager; } }
58                 static Cache cache { get { return _runtime._cache; } }
59                 static WaitCallback do_RealProcessRequest;
60                 
61                 QueueManager _queue_manager;
62                 TraceManager _trace_manager;
63                 Cache _cache;
64
65                 static HttpRuntime ()
66                 {
67                         do_RealProcessRequest = new WaitCallback (RealProcessRequest);
68                 }
69
70                 public HttpRuntime ()
71                 {
72                         WebConfigurationManager.Init ();
73                         _queue_manager = new QueueManager ();
74                         _trace_manager = new TraceManager ();
75                         _cache = new Cache ();
76                 }
77
78                 static private HttpRuntime _runtimeInstance {
79                         get {
80                                 HttpRuntime runtime = (HttpRuntime) AppDomain.CurrentDomain.GetData ("HttpRuntime");
81                                 if (runtime == null)
82                                         lock (typeof (HttpRuntime)) {
83                                                 runtime = (HttpRuntime) AppDomain.CurrentDomain.GetData ("HttpRuntime");
84                                                 if (runtime == null) {
85                                                         runtime = new HttpRuntime ();
86                                                         AppDomain.CurrentDomain.SetData ("HttpRuntime", runtime);
87                                                 }
88                                         }
89                                 return runtime;
90                         }
91                 }
92                 static private HttpRuntime _runtime
93                 {
94                         get
95                         {
96                                 if (HttpContext.Current != null)
97                                         return HttpContext.Current.HttpRuntimeInstance;
98                                 else
99                                         return _runtimeInstance;
100                         }
101                 }
102 #else
103                 static QueueManager queue_manager;
104                 static TraceManager trace_manager;
105                 static TimeoutManager timeout_manager;
106                 static Cache cache;
107                 static WaitCallback do_RealProcessRequest;
108
109 #if NET_2_0
110                 static bool assemblyMappingEnabled;
111                 static object assemblyMappingLock = new object ();
112 #endif
113                 
114                 static HttpRuntime ()
115                 {
116 #if NET_2_0
117                         WebConfigurationManager.Init ();
118 #endif
119                         queue_manager = new QueueManager ();
120                         trace_manager = new TraceManager ();
121                         timeout_manager = new TimeoutManager ();
122                         cache = new Cache ();
123                         do_RealProcessRequest = new WaitCallback (RealProcessRequest);
124                 }
125
126 #if ONLY_1_1
127                 [SecurityPermission (SecurityAction.Demand, UnmanagedCode = true)]
128 #endif
129                 public HttpRuntime ()
130                 {
131                 }
132 #endif
133
134 #region AppDomain handling
135                 //
136                 // http://radio.weblogs.com/0105476/stories/2002/07/12/executingAspxPagesWithoutAWebServer.html
137                 //
138                 public static string AppDomainAppId {
139                         [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.High)]
140                         get {
141                                 //
142                                 // This value should not change across invocations
143                                 //
144                                 string dirname = (string) AppDomain.CurrentDomain.GetData (".appId");
145                                 if ((dirname != null) && (dirname.Length > 0) && SecurityManager.SecurityEnabled) {
146                                         new FileIOPermission (FileIOPermissionAccess.PathDiscovery, dirname).Demand ();
147                                 }
148                                 return dirname;
149                         }
150                 }
151
152                 // Physical directory for the application
153                 public static string AppDomainAppPath {
154                         get {
155                                 string dirname = (string) AppDomain.CurrentDomain.GetData (".appPath");
156                                 if (SecurityManager.SecurityEnabled) {
157                                         new FileIOPermission (FileIOPermissionAccess.PathDiscovery, dirname).Demand ();
158                                 }
159                                 return dirname;
160                         }
161                 }
162
163                 public static string AppDomainAppVirtualPath {
164                         get {
165                                 return (string) AppDomain.CurrentDomain.GetData (".appVPath");
166                         }
167                 }
168
169                 public static string AppDomainId {
170                         [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.High)]
171                         get {
172                                 return (string) AppDomain.CurrentDomain.GetData (".domainId");
173                         }
174                 }
175
176                 public static string AspInstallDirectory {
177                         get {
178                                 string dirname = (string) AppDomain.CurrentDomain.GetData (".hostingInstallDir");
179                                 if ((dirname != null) && (dirname.Length > 0) && SecurityManager.SecurityEnabled) {
180                                         new FileIOPermission (FileIOPermissionAccess.PathDiscovery, dirname).Demand ();
181                                 }
182                                 return dirname;
183                         }
184                 }
185 #endregion
186                 
187                 public static string BinDirectory {
188                         get {
189                                 string dirname = AppDomain.CurrentDomain.SetupInformation.PrivateBinPath;
190                                 if (SecurityManager.SecurityEnabled) {
191                                         new FileIOPermission (FileIOPermissionAccess.PathDiscovery, dirname).Demand ();
192                                 }
193                                 return dirname;
194                         }
195                 }
196
197                 public static Cache Cache {
198                         get {
199                                 return cache;
200                         }
201                 }
202
203                 public static string ClrInstallDirectory {
204                         get {
205                                 string dirname = Path.GetDirectoryName (typeof (Object).Assembly.Location);
206                                 if ((dirname != null) && (dirname.Length > 0) && SecurityManager.SecurityEnabled) {
207                                         new FileIOPermission (FileIOPermissionAccess.PathDiscovery, dirname).Demand ();
208                                 }
209                                 return dirname;
210                         }
211                 }
212
213                 public static string CodegenDir {
214                         get {
215                                 string dirname = AppDomain.CurrentDomain.SetupInformation.DynamicBase;
216                                 if (SecurityManager.SecurityEnabled) {
217                                         new FileIOPermission (FileIOPermissionAccess.PathDiscovery, dirname).Demand ();
218                                 }
219                                 return dirname;
220                         }
221                 }
222
223                 [MonoTODO]
224                 public static bool IsOnUNCShare {
225                         [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Low)]
226                         get {
227                                 throw new NotImplementedException ();
228                         }
229                 }
230
231                 public static string MachineConfigurationDirectory {
232                         get {
233 #if NET_2_0
234                                 string dirname = Path.GetDirectoryName (WebConfigurationManager.OpenMachineConfiguration().FilePath);
235 #else
236                                 string dirname = Path.GetDirectoryName (WebConfigurationSettings.MachineConfigPath);
237 #endif
238                                 if ((dirname != null) && (dirname.Length > 0) && SecurityManager.SecurityEnabled) {
239                                         new FileIOPermission (FileIOPermissionAccess.PathDiscovery, dirname).Demand ();
240                                 }
241                                 return dirname;
242                         }
243                 }
244
245                 [SecurityPermission (SecurityAction.Demand, UnmanagedCode = true)]
246                 public static void Close ()
247                 {
248                         // Remove all items from cache.
249                 }
250
251                 static void QueuePendingRequests ()
252                 {
253                         HttpWorkerRequest request = queue_manager.GetNextRequest (null);
254                         if (request == null)
255                                 return;
256                         ThreadPool.QueueUserWorkItem (do_RealProcessRequest, request);
257                 }
258
259                 static void RealProcessRequest (object o)
260                 {
261                         HttpContext context = new HttpContext ((HttpWorkerRequest) o);
262                         HttpContext.Current = context;
263
264                         //
265                         // Get application instance (create or reuse an instance of the correct class)
266                         //
267                         HttpApplication app = null;
268                         bool error = false;
269                         try {
270                                 app = HttpApplicationFactory.GetApplication (context);
271                         } catch (Exception e) {
272                                 FinishWithException ((HttpWorkerRequest) o, new HttpException ("", e));
273                                 error = true;
274                         }
275
276                         if (error) {
277                                 context.Request.ReleaseResources ();
278                                 context.Response.ReleaseResources ();
279                                 HttpContext.Current = null;
280                         } else {
281                                 context.ApplicationInstance = app;
282                                 
283                                 //
284                                 // Ask application to service the request
285                                 //
286                                 IHttpAsyncHandler ihah = app;
287
288                                 IAsyncResult appiar = ihah.BeginProcessRequest (context, new AsyncCallback (request_processed), context);
289                                 ihah.EndProcessRequest (appiar);
290
291                                 HttpApplicationFactory.Recycle (app);
292                         }
293                         
294                         QueuePendingRequests ();
295                 }
296                 
297                 //
298                 // ProcessRequest method is executed in the AppDomain of the application
299                 //
300                 // Observations:
301                 //    ProcessRequest does not guarantee that `wr' will be processed synchronously,
302                 //    the request can be queued and processed later.
303                 //
304                 [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Medium)]
305                 public static void ProcessRequest (HttpWorkerRequest wr)
306                 {
307                         if (wr == null)
308                                 throw new ArgumentNullException ("wr");
309                         //
310                         // Queue our request, fetch the next available one from the queue
311                         //
312                         HttpWorkerRequest request = queue_manager.GetNextRequest (wr);
313                         if (request == null)
314                                 return;
315
316                         RealProcessRequest (request);
317                 }
318
319                 //
320                 // Callback to be invoked by IHttpAsyncHandler.BeginProcessRequest
321                 //
322                 static void request_processed (IAsyncResult iar)
323                 {
324                         HttpContext context = (HttpContext) iar.AsyncState;
325
326                         context.Request.ReleaseResources ();
327                         context.Response.ReleaseResources ();
328                 }
329
330                 //
331                 // Called when we are shutting down or we need to reload an application
332                 // that has been modified (touch global.asax) 
333                 //
334                 [SecurityPermission (SecurityAction.Demand, UnmanagedCode = true)]
335                 public static void UnloadAppDomain ()
336                 {
337                         //
338                         // TODO: call ReleaseResources
339                         //
340                         ThreadPool.QueueUserWorkItem (new WaitCallback (ShutdownAppDomain), null);
341                 }
342
343                 //
344                 // Shuts down the AppDomain
345                 //
346                 static void ShutdownAppDomain (object args)
347                 {
348                         queue_manager.Dispose ();
349                         // This will call Session_End if needed.
350                         Cache.InvokePrivateCallbacks ();
351                         // Kill our application.
352                         HttpApplicationFactory.Dispose ();
353                         ThreadPool.QueueUserWorkItem (new WaitCallback (DoUnload), null);
354                 }
355
356 #if TARGET_J2EE // No unload support for appdomains under Grasshopper
357                 static void DoUnload (object state)
358                 {
359                 }
360 #else
361                 static void DoUnload (object state)
362                 {
363                         AppDomain.Unload (AppDomain.CurrentDomain);
364                 }
365 #endif
366
367                 static string content503 = "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n" +
368                         "<html><head>\n<title>503 Server Unavailable</title>\n</head><body>\n" +
369                         "<h1>Server Unavailable</h1>\n" +
370                         "</body></html>\n";
371
372                 static void FinishWithException (HttpWorkerRequest wr, HttpException e)
373                 {
374                         int code = e.GetHttpCode ();
375                         wr.SendStatus (code, HttpWorkerRequest.GetStatusDescription (code));
376                         wr.SendUnknownResponseHeader ("Connection", "close");
377                         wr.SendUnknownResponseHeader ("Date", DateTime.Now.ToUniversalTime ().ToString ("r"));
378                         Encoding enc = Encoding.ASCII;
379                         wr.SendUnknownResponseHeader ("Content-Type", "text/html; charset=" + enc.WebName);
380                         string msg = e.GetHtmlErrorMessage ();
381                         byte [] contentBytes = enc.GetBytes (msg);
382                         wr.SendUnknownResponseHeader ("Content-Length", contentBytes.Length.ToString ());
383                         wr.SendResponseFromMemory (contentBytes, contentBytes.Length);
384                         wr.FlushResponse (true);
385                         wr.CloseConnection ();
386                 }
387
388                 //
389                 // This is called from the QueueManager if a request
390                 // can not be processed (load, no resources, or
391                 // appdomain unload).
392                 //
393                 static internal void FinishUnavailable (HttpWorkerRequest wr)
394                 {
395                         wr.SendStatus (503, "Service unavailable");
396                         wr.SendUnknownResponseHeader ("Connection", "close");
397                         wr.SendUnknownResponseHeader ("Date", DateTime.Now.ToUniversalTime ().ToString ("r"));
398                         Encoding enc = Encoding.ASCII;
399                         wr.SendUnknownResponseHeader ("Content-Type", "text/html; charset=" + enc.WebName);
400                         byte [] contentBytes = enc.GetBytes (content503);
401                         wr.SendUnknownResponseHeader ("Content-Length", contentBytes.Length.ToString ());
402                         wr.SendResponseFromMemory (contentBytes, contentBytes.Length);
403                         wr.FlushResponse (true);
404                         wr.CloseConnection ();
405                 }
406
407 #if NET_2_0 && !TARGET_J2EE
408                 static internal void WritePreservationFile (Assembly asm, string genericNameBase)
409                 {
410                         if (asm == null)
411                                 throw new ArgumentNullException ("asm");
412                         if (String.IsNullOrEmpty (genericNameBase))
413                                 throw new ArgumentNullException ("genericNameBase");
414
415                         string compiled = Path.Combine (AppDomain.CurrentDomain.SetupInformation.DynamicBase,
416                                                         genericNameBase + ".compiled");
417                         PreservationFile pf = new PreservationFile ();
418                         try {
419                                 pf.VirtualPath = String.Format ("/{0}/", genericNameBase);
420
421                                 AssemblyName an = asm.GetName ();
422                                 pf.Assembly = an.Name;
423                                 pf.ResultType = BuildResultTypeCode.TopLevelAssembly;
424                                 pf.Save (compiled);
425                         } catch (Exception ex) {
426                                 throw new HttpException (
427                                         String.Format ("Failed to write preservation file {0}", genericNameBase + ".compiled"),
428                                         ex);
429                         }
430                 }
431                 
432                 static Assembly ResolveAssemblyHandler(object sender, ResolveEventArgs e)
433                 {
434                         AssemblyName an = new AssemblyName (e.Name);
435                         string dynamic_base = AppDomain.CurrentDomain.SetupInformation.DynamicBase;
436                         string compiled = Path.Combine (dynamic_base, an.Name + ".compiled");
437
438                         if (!File.Exists (compiled))
439                                 return null;
440
441                         PreservationFile pf;
442                         try {
443                                 pf = new PreservationFile (compiled);
444                         } catch (Exception ex) {
445                                 throw new HttpException (
446                                         String.Format ("Failed to read preservation file {0}", an.Name + ".compiled"),
447                                         ex);
448                         }
449                         
450                         Assembly ret = null;
451                         try {
452                                 string asmPath = Path.Combine (dynamic_base, pf.Assembly + ".dll");
453                                 ret = Assembly.LoadFrom (asmPath);
454                         } catch (Exception) {
455                                 // ignore
456                         }
457                         
458                         return ret;
459                 }
460                 
461                 internal static void EnableAssemblyMapping (bool enable)
462                 {
463                         lock (assemblyMappingLock) {
464                                 if (assemblyMappingEnabled == enable)
465                                         return;
466                                 if (enable)
467                                         AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler (ResolveAssemblyHandler);
468                                 else
469                                         AppDomain.CurrentDomain.AssemblyResolve -= new ResolveEventHandler (ResolveAssemblyHandler);
470                         }
471                 }
472 #endif
473                 
474                 internal static TraceManager TraceManager {
475                         get {
476                                 return trace_manager;
477                         }
478                 }
479
480 #if !TARGET_JVM
481                 internal static TimeoutManager TimeoutManager {
482                         get {
483                                 return timeout_manager;
484                         }
485                 }
486 #endif
487         }
488 }