2003-10-06 Gonzalo Paniagua Javier <gonzalo@ximian.com>
[mono.git] / mcs / class / System.Web / System.Web / HttpRuntime.cs
1 // 
2 // System.Web.HttpRuntime
3 //
4 // Author:
5 //   Patrik Torstensson (ptorsten@hotmail.com)
6 //   Gaurav Vaish (gvaish@iitk.ac.in)
7 //
8 using System;
9 using System.Collections;
10 using System.Text;
11 using System.Security;
12 using System.Security.Permissions;
13 using System.Threading;
14 using System.Web.UI;
15 using System.Web.Util;
16 using System.Web.Caching;
17
18 namespace System.Web {
19
20         public sealed class HttpRuntime {
21
22                 // Security permission helper objects
23                 private static IStackWalk appPathDiscoveryStackWalk;
24                 private static IStackWalk ctrlPrincipalStackWalk;
25                 private static IStackWalk sensitiveInfoStackWalk;
26                 private static IStackWalk unmgdCodeStackWalk;
27                 private static IStackWalk unrestrictedStackWalk;
28                 private static IStackWalk reflectionStackWalk;
29
30                 private static HttpRuntime _runtime;
31                 private static string appDomainAppId;
32                 private static string appDomainId;
33                 private static string appDomainAppPath;
34                 private static string appDomainAppVirtualPath;
35                 private Cache _cache;
36
37                 private int _activeRequests;
38                 private HttpWorkerRequest.EndOfSendNotification _endOfSendCallback;
39                 private AsyncCallback _handlerCallback;
40                 private WaitCallback _appDomainCallback;
41
42                 private bool _firstRequestStarted;
43                 private bool _firstRequestExecuted;
44                 private DateTime _firstRequestStartTime;
45
46                 private Exception _initError;
47
48                 static HttpRuntime ()
49                 {
50                         appPathDiscoveryStackWalk = null;
51                         ctrlPrincipalStackWalk    = null;
52                         sensitiveInfoStackWalk    = null;
53                         unmgdCodeStackWalk        = null;
54                         unrestrictedStackWalk     = null;
55          
56                         _runtime = new HttpRuntime ();
57                         _runtime.Init();
58                 }
59
60                 public HttpRuntime ()
61                 {       
62                 }
63
64                 static internal object CreateInternalObject(Type type) {
65                         return Activator.CreateInstance(type, true);
66                 }
67
68                 [MonoTODO()]
69                 private void Init ()
70                 {
71                         try {
72                                 _cache = new Cache ();
73
74                                 // TODO: timeout manager
75                                 // TODO: Load all app domain data
76                                 // TODO: Trace manager
77                                 _endOfSendCallback = new HttpWorkerRequest.EndOfSendNotification(OnEndOfSend);
78                                 _handlerCallback = new AsyncCallback(OnHandlerReady);
79                                 _appDomainCallback = new WaitCallback(OnAppDomainUnload);
80                         } 
81                         catch (Exception error) {
82                                 _initError = error;
83                         }
84                 }
85
86                 private void OnFirstRequestStart() {
87                         if (null == _initError) {
88                                 // TODO: First request initialize (config, trace, request queue)
89                         }
90
91                         // If we got an error during init, throw to client now..
92                         if (null != _initError)
93                                 throw _initError;
94                 }
95
96                 private void OnFirstRequestEnd() {
97                 }
98
99                 private void OnHandlerReady(IAsyncResult ar) {
100                         HttpContext context = (HttpContext) ar.AsyncState;
101                         try {
102                                 IHttpAsyncHandler handler = context.AsyncHandler;
103
104                                 try {
105                                         handler.EndProcessRequest(ar);
106                                 }
107                                 catch (Exception error) {
108                                         context.AddError(error);
109                                 }
110                         }
111                         finally {
112                                 context.AsyncHandler = null;
113                         }
114
115                         FinishRequest(context, context.Error);
116                 }
117
118                 private void OnEndOfSend(HttpWorkerRequest request, object data) {
119                         HttpContext context = (HttpContext) data;
120
121                         context.Request.Dispose();
122                         context.Response.Dispose();
123                 }
124
125                 internal void FinishRequest(HttpContext context, Exception error) {
126                         if (error == null) {
127                                 try {
128                                         context.Response.FlushAtEndOfRequest();
129                                 } catch (Exception obj) {
130                                         error = obj;
131                                 }
132                         }
133
134                         HttpWorkerRequest request = context.WorkerRequest;
135                         if (null != error) {
136                                 WebTrace.WriteLine (error.ToString ());
137
138                                 context.Response.Clear ();
139                                 context.Response.ClearHeaders ();
140                                 if (!(error is HttpException)) {
141                                         error = new HttpException (String.Empty, error);
142                                         context.Response.StatusCode = 500;
143                                 } else {
144                                         context.Response.StatusCode = ((HttpException) error).GetHttpCode ();
145                                 }
146                                 context.Response.Write (((HttpException) error).GetHtmlErrorMessage ());
147                                 context.Response.FinalFlush ();
148                         }
149
150                         if (!_firstRequestExecuted) {
151                                 lock (this) {
152                                         if (!_firstRequestExecuted) {
153                                                 OnFirstRequestEnd();
154                                                 _firstRequestExecuted = true;
155                                         }
156                                 }
157                         }
158
159                         Interlocked.Decrement(ref _activeRequests);
160
161                         if (null != request)
162                                 request.EndOfRequest();
163
164                         // TODO: Schedule more work in request queue
165                 }
166
167                 private void OnAppDomainUnload(object state) {
168                         Dispose();
169                 }
170
171                 [MonoTODO]
172                 internal void Dispose() {
173                         // TODO: Drain Request queue
174                         // TODO: Stop request queue
175                         // TODO: Move timeout value to config
176                         WaitForRequests(5000);
177                         
178                         _cache = null;
179                         HttpApplicationFactory.EndApplication();
180                 }
181
182                 [MonoTODO]
183                 internal void WaitForRequests(int ms) {
184                         DateTime timeout = DateTime.Now.AddMilliseconds(ms);
185
186                         do {
187                                 // TODO: We should check the request queue here also
188                                 if (_activeRequests == 0)
189                                         return;
190
191                                 Thread.Sleep(100);
192                         } while (timeout > DateTime.Now);
193                 }
194
195                 internal void InternalExecuteRequest(HttpWorkerRequest request)
196                 {
197                         if (request == null)
198                                 throw new ArgumentNullException ("request");
199                         
200                         IHttpHandler handler;
201                         IHttpAsyncHandler async_handler;
202
203                         HttpContext context = new HttpContext(request);
204
205                         request.SetEndOfSendNotification(_endOfSendCallback, context);
206                         
207                         Interlocked.Increment(ref _activeRequests);
208
209                         try {
210                                 if (!_firstRequestStarted) {
211                                         lock (this) {
212                                                 if (!_firstRequestStarted) {
213                                                         _firstRequestStartTime = DateTime.Now;
214
215                                                         OnFirstRequestStart();
216                                                         _firstRequestStarted = true;
217                                                 }                                               
218                                         }
219                                 }
220
221                                 handler = HttpApplicationFactory.GetInstance(context);
222                                 if (null == handler)
223                                         throw new HttpException(FormatResourceString("unable_to_create_app"));
224
225                                 if (handler is IHttpAsyncHandler) {
226                                         async_handler = (IHttpAsyncHandler) handler;
227
228                                         context.AsyncHandler = async_handler;
229                                         async_handler.BeginProcessRequest(context, _handlerCallback, context);
230                                 } else {
231                                         handler.ProcessRequest(context);
232                                         FinishRequest(context, null);
233                                 }
234                         }
235                         catch (Exception error) {
236                                 FinishRequest(context, error);
237                         }
238                 }
239
240                 public static void ProcessRequest (HttpWorkerRequest Request)
241                 {
242                         // TODO: Request queue
243                         _runtime.InternalExecuteRequest(Request);
244                 }
245
246                 public static Cache Cache {
247                         get {
248                                 return _runtime._cache;
249                         }
250                 }      
251
252                 public static string AppDomainAppId {
253                         get {
254                                 if (appDomainAppId == null)
255                                         appDomainAppId = (string) AppDomain.CurrentDomain.GetData (".appId");
256
257                                 return appDomainAppId;
258                         }
259                 }
260
261                 public static string AppDomainAppPath {
262                         get {
263                                 if (appDomainAppPath == null)
264                                         appDomainAppPath = (string) AppDomain.CurrentDomain.GetData (".appPath");
265
266                                 return appDomainAppPath;
267                         }
268                 }
269
270                 public static string AppDomainAppVirtualPath {
271                         get {
272                                 if (appDomainAppVirtualPath == null)
273                                         appDomainAppVirtualPath = (string) AppDomain.CurrentDomain.GetData (".appVPath");
274
275                                 return appDomainAppVirtualPath;
276                         }
277                 }
278
279                 public static string AppDomainId {
280                         get {
281                                 if (appDomainId == null)
282                                         appDomainId = (string) AppDomain.CurrentDomain.GetData (".domainId");
283
284                                 return appDomainId;
285                         }
286                 }
287
288                 [MonoTODO]
289                 public static string AspInstallDirectory {
290                         get {
291                                 throw new NotImplementedException ();
292                         }
293                 }
294
295                 [MonoTODO]
296                 public static string BinDirectory {
297                         get {
298                                 throw new NotImplementedException ();
299                         }
300                 }
301
302                 [MonoTODO]
303                 public static string ClrInstallDirectory {
304                         get {
305                                 throw new NotImplementedException ();
306                         }
307                 }
308
309                 [MonoTODO]
310                 public static string CodegenDir {
311                         get {
312                                 throw new NotImplementedException ();
313                         }
314                 }
315
316                 [MonoTODO]
317                 public static bool IsOnUNCShare {
318                         get {
319                                 throw new NotImplementedException ();
320                         }
321                 }
322
323                 [MonoTODO]
324                 public static string MachineConfigurationDirectory {
325                         get {
326                                 throw new NotImplementedException ();
327                         }
328                 }
329
330                 public static void Close ()
331                 {
332                         _runtime.Dispose();
333                 }
334
335                 internal static string FormatResourceString (string key)
336                 {
337                         return GetResourceString (key);
338                 }
339
340                 internal static string FormatResourceString (string key, string arg0)
341                 {
342                         /*string format = GetResourceString (key);
343
344                         if (format == null)
345                                 return null;
346                         
347                         return String.Format (format, arg0);
348                         */
349                         return String.Format ("{0}: {1}", key, arg0);
350                 }
351
352                 [MonoTODO ("FormatResourceString (string, string, string)")]
353                 internal static string FormatResourceString (string key, string arg0, string type) {
354                         return String.Format ("{0}: {1} {2}", key, arg0, type);
355                 }
356
357                 [MonoTODO ("FormatResourceString (string, string, string, string)")]
358                 internal static string FormatResourceString (string key, string arg0,
359                                                              string arg1, string arg2)
360                 {
361                         return String.Format ("{0}: {1} {2} {3}", key, arg0, arg1, arg2);
362                 }
363
364                 [MonoTODO ("FormatResourceString (string, string[]")]
365                 internal static string FormatResourceString (string key, string[] args)
366                 {
367                         //StringBuilder sb = new StringBuilder ();
368                         /*sb.AppendFormat ("{0}: ", key);
369                         foreach (string s in args)
370                                 sb.AppendFormat ("{0} ", s);
371
372                         if (sb.Length > 0)
373                                 sb.Length--;
374                         return sb.ToString ();*/
375                         string s = key + ": ";
376                         if (args != null)
377                                 foreach (string k in args)
378                                         s += k + " ";
379                         return s;
380                 }
381
382                 private static string GetResourceString (string key) {
383                         return _runtime.GetResourceStringFromResourceManager (key);
384                 }
385
386                 [MonoTODO ("GetResourceStringFromResourceManager (string)")]
387                 private string GetResourceStringFromResourceManager (string key) {
388                         return "String returned by HttpRuntime.GetResourceStringFromResourceManager";
389                 }
390
391                 #region Security Internal Methods (not impl)
392                 [MonoTODO ("Get Application path from the appdomain object")]
393                 internal static IStackWalk AppPathDiscovery {
394                         get {
395                                 if (appPathDiscoveryStackWalk == null) {
396                                         appPathDiscoveryStackWalk = new FileIOPermission (
397                                                 FileIOPermissionAccess.PathDiscovery, "<apppath>");
398                                 }
399                                 return appPathDiscoveryStackWalk;
400                         }
401                 }
402
403                 internal static IStackWalk ControlPrincipal {
404                         get {
405                                 if (ctrlPrincipalStackWalk == null) {
406                                         ctrlPrincipalStackWalk = new SecurityPermission (
407                                                 SecurityPermissionFlag.ControlPrincipal);
408                                 }
409                                 return ctrlPrincipalStackWalk;
410                         }
411                 }
412
413                 internal static IStackWalk Reflection {
414                         get {
415                                 if (reflectionStackWalk == null) {
416                                         reflectionStackWalk = new ReflectionPermission (
417                                                 ReflectionPermissionFlag.TypeInformation |
418                                                 ReflectionPermissionFlag.MemberAccess);
419                                 }
420                                 return reflectionStackWalk;
421                         }
422                 }
423
424                 internal static IStackWalk SensitiveInformation {
425                         get {
426                                 if (sensitiveInfoStackWalk == null) {
427                                         sensitiveInfoStackWalk = new EnvironmentPermission (
428                                                 PermissionState.Unrestricted);
429                                 }
430                                 return sensitiveInfoStackWalk;
431                         }
432                 }
433
434                 internal static IStackWalk UnmanagedCode {
435                         get {
436                                 if (unmgdCodeStackWalk == null) {
437                                         unmgdCodeStackWalk = new SecurityPermission (
438                                                 SecurityPermissionFlag.UnmanagedCode);
439                                 }
440                                 return unmgdCodeStackWalk;
441                         }
442                 }
443
444                 internal static IStackWalk Unrestricted {
445                         get {
446                                 if (unrestrictedStackWalk == null) {
447                                         unrestrictedStackWalk = new PermissionSet (
448                                                 PermissionState.Unrestricted);
449                                 }
450                                 return unrestrictedStackWalk;
451                         }
452                 }
453
454                 internal static IStackWalk FileReadAccess (string file)
455                 {
456                         return new FileIOPermission (FileIOPermissionAccess.Read, file);
457                 }
458
459                 internal static IStackWalk PathDiscoveryAccess (string path)
460                 {
461                         return new FileIOPermission (FileIOPermissionAccess.PathDiscovery, path);
462                 }
463                 #endregion
464         }
465 }