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