2005-09-06 Gonzalo Paniagua Javier <gonzalo@ximian.com>
[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.Web.Caching;
38 using System.Web.Configuration;
39 using System.Web.UI;
40 using System.Threading;
41
42 namespace System.Web {
43         
44         public sealed class HttpRuntime {
45 #if TARGET_J2EE
46                 static QueueManager queue_manager { get { return _runtime._queue_manager; } }
47                 static TraceManager trace_manager { get { return _runtime._trace_manager; } }
48                 static Cache cache { get { return _runtime._cache; } }
49                 static WaitCallback do_RealProcessRequest;
50
51                 QueueManager _queue_manager;
52                 TraceManager _trace_manager;
53                 Cache _cache;
54
55                 static HttpRuntime ()
56                 {
57                         do_RealProcessRequest = new WaitCallback (RealProcessRequest);
58                 }
59
60                 public HttpRuntime ()
61                 {
62                         _queue_manager = new QueueManager ();
63                         _trace_manager = new TraceManager ();
64                         _cache = new Cache ();
65                 }
66
67                 static private HttpRuntime _runtime {
68                         get {
69                                 HttpRuntime runtime = (HttpRuntime)AppDomain.CurrentDomain.GetData("HttpRuntime");
70                                 if (runtime == null)
71                                         lock (typeof(HttpRuntime)) {
72                                                 runtime = (HttpRuntime)AppDomain.CurrentDomain.GetData("HttpRuntime");
73                                                 if (runtime == null) {
74                                                         runtime = new HttpRuntime();
75                                                         AppDomain.CurrentDomain.SetData("HttpRuntime", runtime);
76                                                 }
77                                         }
78                                 return runtime;
79                         }
80                 }
81 #else
82                 static QueueManager queue_manager;
83                 static TraceManager trace_manager;
84                 static TimeoutManager timeout_manager;
85                 static Cache cache;
86                 static WaitCallback do_RealProcessRequest;
87
88                 static HttpRuntime ()
89                 {
90                         Console.WriteLine ("Jelou");
91                         queue_manager = new QueueManager ();
92                         trace_manager = new TraceManager ();
93                         timeout_manager = new TimeoutManager ();
94                         cache = new Cache ();
95                         do_RealProcessRequest = new WaitCallback (RealProcessRequest);
96                 }
97
98                 public HttpRuntime ()
99                 {
100                 }
101 #endif
102
103 #region AppDomain handling
104                 //
105                 // http://radio.weblogs.com/0105476/stories/2002/07/12/executingAspxPagesWithoutAWebServer.html
106                 //
107                 public static string AppDomainAppId {
108                         get {
109                                 //
110                                 // This value should not change across invocations
111                                 //
112                                
113                                 return (string) AppDomain.CurrentDomain.GetData (".appId");
114                         }
115                 }
116
117                 // Physical directory for the application
118                 public static string AppDomainAppPath {
119                         get {
120                                 return (string) AppDomain.CurrentDomain.GetData (".appPath");
121                         }
122                 }
123
124                 public static string AppDomainAppVirtualPath {
125                         get {
126                                 return (string) AppDomain.CurrentDomain.GetData (".appVPath");
127                         }
128                 }
129
130                 public static string AppDomainId {
131                         get {
132                                 return (string) AppDomain.CurrentDomain.GetData (".domainId");
133                         }
134                 }
135
136                 public static string AspInstallDirectory {
137                         get {
138                                 return (string) AppDomain.CurrentDomain.GetData (".hostingInstallDir");
139                         }
140                 }
141 #endregion
142                 
143                 public static string BinDirectory {
144                         get {
145                                 return Path.Combine (AppDomainAppPath, "bin");
146                         }
147                 }
148
149                 public static Cache Cache {
150                         get {
151                                 return cache;
152                         }
153                 }
154
155                 public static string ClrInstallDirectory {
156                         get {
157                                 return Path.GetDirectoryName (typeof (Object).Assembly.CodeBase);
158                         }
159                 }
160
161                 public static string CodegenDir {
162                         get {
163                                 return AppDomain.CurrentDomain.SetupInformation.DynamicBase;
164                         }
165                 }
166
167                 public static bool IsOnUNCShare {
168                         get {
169                                 throw new NotImplementedException ();
170                         }
171                 }
172
173                 public static string MachineConfigurationDirectory {
174                         get {
175                                 return Path.GetDirectoryName (WebConfigurationSettings.MachineConfigPath);
176                         }
177                 }
178
179                 public static void Close ()
180                 {
181                         // Remove all items from cache.
182                 }
183
184                 static void QueuePendingRequests ()
185                 {
186                         HttpWorkerRequest request = queue_manager.GetNextRequest (null);
187                         if (request == null)
188                                 return;
189                         ThreadPool.QueueUserWorkItem (do_RealProcessRequest, request);
190                 }
191
192                 static void RealProcessRequest (object o)
193                 {
194                         HttpContext context = new HttpContext ((HttpWorkerRequest) o);
195                         HttpContext.Current = context;
196
197                         //
198                         // Get application instance (create or reuse an instance of the correct class)
199                         //
200                         HttpApplication app = HttpApplicationFactory.GetApplication (context);
201
202                         context.ApplicationInstance = app;
203                         
204                         //
205                         // Initialize, load modules specific on the config file.
206                         //
207
208                         //
209                         // Ask application to service the request
210                         //
211                         IHttpAsyncHandler ihah = app;
212
213                         IAsyncResult appiar = ihah.BeginProcessRequest (context, new AsyncCallback (request_processed), context);
214                         ihah.EndProcessRequest (appiar);
215
216                         HttpApplicationFactory.Recycle (app);
217                         
218                         QueuePendingRequests ();
219                 }
220                 
221                 //
222                 // ProcessRequest method is executed in the AppDomain of the application
223                 //
224                 // Observations:
225                 //    ProcessRequest does not guarantee that `wr' will be processed synchronously,
226                 //    the request can be queued and processed later.
227                 //
228                 public static void ProcessRequest (HttpWorkerRequest wr)
229                 {
230                         HttpWorkerRequest request;
231
232                         //
233                         // Queue our request, fetch the next available one from the queue
234                         //
235                         request = queue_manager.GetNextRequest (wr);
236                         if (request == null)
237                                 return;
238
239                         RealProcessRequest (request);
240                 }
241
242                 //
243                 // Callback to be invoked by IHttpAsyncHandler.BeginProcessRequest
244                 //
245                 static void request_processed (IAsyncResult iar)
246                 {
247                         HttpContext context = (HttpContext) iar.AsyncState;
248
249                         context.Request.ReleaseResources ();
250                         context.Response.ReleaseResources ();
251                 }
252
253                 //
254                 // Called when we are shutting down or we need to reload an application
255                 // that has been modified (touch global.asax) 
256                 //
257                 public static void UnloadAppDomain ()
258                 {
259                         //
260                         // TODO: call ReleaseResources
261                         //
262                         ThreadPool.QueueUserWorkItem (new WaitCallback (ShutdownAppDomain), null);
263                 }
264
265                 //
266                 // Shuts down the AppDomain
267                 //
268                 static void ShutdownAppDomain (object args)
269                 {
270                         // Kill our application.
271                         HttpApplicationFactory.Dispose ();
272                         ThreadPool.QueueUserWorkItem (new WaitCallback (DoUnload), null);
273                 }
274
275                 static void DoUnload (object state)
276                 {
277                         AppDomain.Unload (AppDomain.CurrentDomain);
278                 }
279
280                 static string content503 = "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n" +
281                         "<html><head>\n<title>503 Server Unavailable</title>\n</head><body>\n" +
282                         "<h1>Server Unavailable</h1>\n" +
283                         "</body></html>\n";
284
285                 //
286                 // This is called from the QueueManager if a request
287                 // can not be processed (load, no resources, or
288                 // appdomain unload).
289                 //
290                 static internal void FinishUnavailable (HttpWorkerRequest wr)
291                 {
292                         wr.SendStatus (503, "Service unavailable");
293                         wr.SendUnknownResponseHeader ("Connection", "close");
294                         wr.SendUnknownResponseHeader ("Date", DateTime.Now.ToUniversalTime ().ToString ("r"));
295                         Encoding enc = Encoding.ASCII;
296                         wr.SendUnknownResponseHeader ("Content-Type", "text/html; charset=" + enc.WebName);
297                         byte [] contentBytes = enc.GetBytes (content503);
298                         wr.SendUnknownResponseHeader ("Content-Length", contentBytes.Length.ToString ());
299                         wr.SendResponseFromMemory (contentBytes, contentBytes.Length);
300                         wr.FlushResponse (true);
301                         wr.CloseConnection ();
302                 }
303
304                 internal static TraceManager TraceManager {
305                         get {
306                                 return trace_manager;
307                         }
308                 }
309
310 #if !TARGET_JVM
311                 internal static TimeoutManager TimeoutManager {
312                         get {
313                                 return timeout_manager;
314                         }
315                 }
316 #endif
317
318 #if NET_2_0
319                 static ApplicationShutdownReason shutdown_reason = ApplicationShutdownReason.None;
320
321                 [MonoTODO]
322                 public static ApplicationShutdownReason ShutdownReason {
323                         get {
324                                 //
325                                 // Unlike previously believed by Gonzalo and
326                                 // myself HttpRuntime.UnloadAppDomain is not
327                                 // something that happens right away, UnloadAppDomain
328                                 // mereley "queues" the domain for destruction, but
329                                 // the application continues to execute until it
330                                 // is time to terminate.  Only at that point is
331                                 // the domain unloaded.
332                                 //
333                                 // This means that we should probably not use the
334                                 // QueueUserWorkItem above to shutdown the appdomain
335                                 // in a separate thread, but rather just flag this
336                                 // app for termination, and then unload the domain
337                                 //
338                                 // The user can continue executing for a long time
339                                 //
340                                 // See the sample in the docs for ShutdownReason.
341                                 //
342                                 
343                                 return shutdown_reason;
344                         }
345                 }
346 #endif
347
348         }
349 }