Same thing as in HEAD.
[mono.git] / mcs / class / System.Web / System.Web / HttpRuntime.cs
1 // 
2 // System.Web.HttpRuntime
3 //
4 // Authors:
5 //      Patrik Torstensson (ptorsten@hotmail.com)
6 //      Gaurav Vaish (gvaish@iitk.ac.in)
7 //      Gonzalo Paniagua Javier (gonzalo@ximian.com)
8 //
9
10 //
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
18 // 
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 // 
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 //
30 using System;
31 using System.Collections;
32 using System.IO;
33 using System.Text;
34 using System.Security;
35 using System.Security.Permissions;
36 using System.Threading;
37 using System.Web.Configuration;
38 using System.Web.UI;
39 using System.Web.Util;
40 using System.Web.Caching;
41
42 namespace System.Web {
43
44         public sealed class HttpRuntime {
45
46                 // Security permission helper objects
47                 private static IStackWalk appPathDiscoveryStackWalk;
48                 private static IStackWalk ctrlPrincipalStackWalk;
49                 private static IStackWalk sensitiveInfoStackWalk;
50                 private static IStackWalk unmgdCodeStackWalk;
51                 private static IStackWalk unrestrictedStackWalk;
52                 private static IStackWalk reflectionStackWalk;
53
54                 private static HttpRuntime _runtime;
55                 private static string appDomainAppId;
56                 private static string appDomainId;
57                 private static string appDomainAppPath;
58                 private static string appDomainAppVirtualPath;
59                 private Cache _cache;
60
61                 private int _activeRequests;
62                 private HttpWorkerRequest.EndOfSendNotification _endOfSendCallback;
63                 private AsyncCallback _handlerCallback;
64                 private WaitCallback _appDomainCallback;
65
66                 private bool _firstRequestStarted;
67                 private bool _firstRequestExecuted;
68                 private DateTime _firstRequestStartTime;
69
70                 private Exception _initError;
71                 private TimeoutManager timeoutManager;
72                 private QueueManager queueManager;
73                 private TraceManager traceManager;
74                 private WaitCallback doRequestCallback;
75                 private int pendingCallbacks;
76
77                 static HttpRuntime ()
78                 {
79                         appPathDiscoveryStackWalk = null;
80                         ctrlPrincipalStackWalk    = null;
81                         sensitiveInfoStackWalk    = null;
82                         unmgdCodeStackWalk        = null;
83                         unrestrictedStackWalk     = null;
84          
85                         _runtime = new HttpRuntime ();
86                         _runtime.Init();
87                 }
88
89                 public HttpRuntime ()
90                 {
91                         doRequestCallback = new WaitCallback (DoRequest);
92                 }
93
94                 static internal object CreateInternalObject(Type type) {
95                         return Activator.CreateInstance(type, true);
96                 }
97
98                 private void Init ()
99                 {
100                         try {
101                                 _cache = new Cache ();
102                                 timeoutManager = new TimeoutManager ();
103
104                                 _endOfSendCallback = new HttpWorkerRequest.EndOfSendNotification(OnEndOfSend);
105                                 _handlerCallback = new AsyncCallback(OnHandlerReady);
106                                 _appDomainCallback = new WaitCallback(OnAppDomainUnload);
107                         } 
108                         catch (Exception error) {
109                                 _initError = error;
110                         }
111                 }
112
113                 private void OnFirstRequestStart(HttpContext context) {
114                         if (_initError != null)
115                                 throw _initError;
116
117                         try {
118                                 WebConfigurationSettings.Init (context);
119                                 traceManager = new TraceManager ();
120                                 queueManager = new QueueManager ();
121                         } catch (Exception e) {
122                                 _initError = e;
123                         }
124
125                         // If we got an error during init, throw to client now..
126                         if (null != _initError)
127                                 throw _initError;
128                 }
129
130                 private void OnFirstRequestEnd() {
131                 }
132
133                 private void OnHandlerReady(IAsyncResult ar) {
134                         HttpContext context = (HttpContext) ar.AsyncState;
135                         try {
136                                 IHttpAsyncHandler handler = context.AsyncHandler;
137
138                                 try {
139                                         handler.EndProcessRequest(ar);
140                                 }
141                                 catch (Exception error) {
142                                         context.AddError(error);
143                                 }
144                         }
145                         finally {
146                                 context.AsyncHandler = null;
147                         }
148
149                         FinishRequest(context, context.Error);
150                 }
151
152                 private void OnEndOfSend(HttpWorkerRequest request, object data) {
153                         HttpContext context = (HttpContext) data;
154
155                         context.Request.Dispose();
156                         context.Response.Dispose();
157                 }
158
159                 internal void FinishRequest(HttpContext context, Exception error) {
160                         if (error == null) {
161                                 try {
162                                         context.Response.FlushAtEndOfRequest();
163                                 } catch (Exception obj) {
164                                         error = obj;
165                                 }
166                         }
167
168                         HttpWorkerRequest request = context.WorkerRequest;
169                         if (null != error) {
170                                 WebTrace.WriteLine (error.ToString ());
171
172                                 context.Response.Clear ();
173                                 context.Response.ClearHeaders ();
174
175                                 if (!(error is HttpException)) {
176                                         error = new HttpException (String.Empty, error);
177                                         context.Response.StatusCode = 500;
178                                 } else {
179                                         context.Response.StatusCode = ((HttpException) error).GetHttpCode ();
180                                 }
181
182                                 if (!RedirectCustomError (context))
183                                         context.Response.Write (((HttpException) error).GetHtmlErrorMessage ());
184
185                                 context.Response.FinalFlush ();
186                         }
187
188                         if (!_firstRequestExecuted) {
189                                 lock (this) {
190                                         if (!_firstRequestExecuted) {
191                                                 _firstRequestExecuted = true;
192                                                 OnFirstRequestEnd();
193                                         }
194                                 }
195                         }
196
197                         Interlocked.Decrement(ref _activeRequests);
198
199                         if (null != request)
200                                 request.EndOfRequest();
201
202                         TryExecuteQueuedRequests ();
203                 }
204
205                 bool RedirectCustomError (HttpContext context)
206                 {
207                         if (!context.IsCustomErrorEnabled)
208                                 return false;
209
210                         CustomErrorsConfig config = null;
211                         try {
212                                 config = (CustomErrorsConfig) context.GetConfig ("system.web/customErrors");
213                         } catch { }
214
215                         if (config == null) {
216                                 if (context.ErrorPage != null)
217                                         return context.Response.RedirectCustomError (context.ErrorPage);
218
219                                 return false;
220                         }
221
222                         string redirect =  config [context.Response.StatusCode];
223                         if (redirect == null) {
224                                 redirect = context.ErrorPage;
225                                 if (redirect == null)
226                                         redirect = config.DefaultRedirect;
227                         }
228
229                         if (redirect == null)
230                                 return false;
231
232                         return context.Response.RedirectCustomError (redirect);
233                 }
234
235                 internal static void FinishUnavailable (HttpWorkerRequest wr)
236                 {
237                         HttpContext context = new HttpContext (wr);
238                         HttpException exception = new HttpException (503, "Service unavailable");
239                         Interlocked.Increment (ref _runtime._activeRequests);
240                         _runtime.FinishRequest (context, exception);
241                 }
242
243                 private void OnAppDomainUnload(object state) {
244                         Dispose();
245                 }
246
247                 internal void Dispose() {
248                         WaitForRequests(5000);
249                         queueManager.Dispose (); // Send a 503 to all queued requests
250                         queueManager = null;
251                         
252                         _cache = null;
253                         HttpApplicationFactory.EndApplication();
254                 }
255
256                 internal void WaitForRequests(int ms) {
257                         DateTime timeout = DateTime.Now.AddMilliseconds(ms);
258
259                         do {
260                                 if (Interlocked.CompareExchange (ref _activeRequests, 0, 0) == 0)
261                                         return;
262
263                                 Thread.Sleep (100);
264                         } while (timeout > DateTime.Now);
265                 }
266
267                 internal void InternalExecuteRequest (HttpWorkerRequest request)
268                 {
269                         IHttpHandler handler;
270                         IHttpAsyncHandler async_handler;
271
272                         HttpContext context = new HttpContext(request);
273
274                         request.SetEndOfSendNotification(_endOfSendCallback, context);
275                         
276                         Interlocked.Increment(ref _activeRequests);
277
278                         try {
279                                 if (!_firstRequestStarted) {
280                                         lock (this) {
281                                                 if (!_firstRequestStarted) {
282                                                         _firstRequestStarted = true;
283                                                         _firstRequestStartTime = DateTime.Now;
284                                                         OnFirstRequestStart(context);
285                                                 }                                               
286                                         }
287                                 }
288
289                                 // This *must* be done after the configuration is initialized.
290                                 context.Response.InitializeWriter ();
291                                 handler = HttpApplicationFactory.GetInstance(context);
292                                 if (null == handler)
293                                         throw new HttpException(FormatResourceString("unable_to_create_app"));
294
295                                 if (handler is IHttpAsyncHandler) {
296                                         async_handler = (IHttpAsyncHandler) handler;
297
298                                         context.AsyncHandler = async_handler;
299                                         async_handler.BeginProcessRequest(context, _handlerCallback, context);
300                                 } else {
301                                         handler.ProcessRequest(context);
302                                         FinishRequest(context, null);
303                                 }
304                         }
305                         catch (Exception error) {
306                                 context.Response.InitializeWriter ();
307                                 FinishRequest(context, error);
308                         }
309                 }
310
311                 void DoRequest (object o)
312                 {
313                         Interlocked.Decrement (ref pendingCallbacks);
314                         InternalExecuteRequest ((HttpWorkerRequest) o);
315                 }
316                 
317                 void TryExecuteQueuedRequests ()
318                 {
319                         // Wait for pending jobs to start
320                         if (Interlocked.CompareExchange (ref pendingCallbacks, 3, 3) == 3) {
321                                 return;
322                         }
323
324                         if (queueManager == null)
325                                 return;
326
327                         if (!queueManager.CanExecuteRequest (false)) {
328                                 return;
329                         }
330
331                         HttpWorkerRequest wr = queueManager.Dequeue ();
332                         if (wr == null) {
333                                 return;
334                         }
335
336                         Interlocked.Increment (ref pendingCallbacks);
337                         ThreadPool.QueueUserWorkItem (doRequestCallback, wr);
338                         TryExecuteQueuedRequests ();
339                 }
340
341                 public static void ProcessRequest (HttpWorkerRequest Request)
342                 {
343                         if (Request == null)
344                                 throw new ArgumentNullException ("Request");
345
346                         if (!_runtime._firstRequestExecuted || _runtime.queueManager.CanExecuteRequest (false)) {
347                                 _runtime.InternalExecuteRequest (Request);
348                         } else {
349                                 _runtime.queueManager.Queue (Request);
350                         }
351                 }
352
353 #if NET_1_1
354                 [MonoTODO]
355                 public void UnloadAppDomain ()
356                 {
357                         throw new NotImplementedException ();
358                 }
359 #endif
360                 public static Cache Cache {
361                         get {
362                                 return _runtime._cache;
363                         }
364                 }      
365
366                 public static string AppDomainAppId {
367                         get {
368                                 if (appDomainAppId == null)
369                                         appDomainAppId = (string) AppDomain.CurrentDomain.GetData (".appId");
370
371                                 return appDomainAppId;
372                         }
373                 }
374
375                 public static string AppDomainAppPath {
376                         get {
377                                 if (appDomainAppPath == null)
378                                         appDomainAppPath = (string) AppDomain.CurrentDomain.GetData (".appPath");
379
380                                 return appDomainAppPath;
381                         }
382                 }
383
384                 public static string AppDomainAppVirtualPath {
385                         get {
386                                 if (appDomainAppVirtualPath == null)
387                                         appDomainAppVirtualPath = (string) AppDomain.CurrentDomain.GetData (".appVPath");
388
389                                 return appDomainAppVirtualPath;
390                         }
391                 }
392
393                 public static string AppDomainId {
394                         get {
395                                 if (appDomainId == null)
396                                         appDomainId = (string) AppDomain.CurrentDomain.GetData (".domainId");
397
398                                 return appDomainId;
399                         }
400                 }
401
402                 public static string AspInstallDirectory {
403                         get {
404                                 return ICalls.GetMachineInstallDirectory ();
405                         }
406                 }
407
408                 public static string BinDirectory {
409                         get {
410                                 return Path.Combine (AppDomainAppPath, "bin");
411                         }
412                 }
413
414                 public static string ClrInstallDirectory {
415                         get {
416                                 return ICalls.GetMachineInstallDirectory ();
417                         }
418                 }
419
420                 public static string CodegenDir {
421                         get {
422                                 return AppDomain.CurrentDomain.SetupInformation.DynamicBase;
423                         }
424                 }
425
426                 public static bool IsOnUNCShare {
427                         get {
428                                 // IsUnc broken under unix?
429                                 return (!((int) Environment.OSVersion.Platform == 128) &&
430                                         new Uri ("file://" + ClrInstallDirectory).IsUnc);
431                         }
432                 }
433
434                 public static string MachineConfigurationDirectory {
435                         get {
436                                 return Path.GetDirectoryName (WebConfigurationSettings.MachineConfigPath);
437                         }
438                 }
439
440                 internal static TimeoutManager TimeoutManager {
441                         get {
442                                 return HttpRuntime._runtime.timeoutManager;
443                         }
444                 }
445
446                 internal static TraceManager TraceManager {
447                         get {
448                                 return HttpRuntime._runtime.traceManager;
449                         }
450                 }
451
452                 public static void Close ()
453                 {
454                         _runtime.Dispose();
455                 }
456
457                 internal static string FormatResourceString (string key)
458                 {
459                         return GetResourceString (key);
460                 }
461
462                 internal static string FormatResourceString (string key, string arg0)
463                 {
464                         /*string format = GetResourceString (key);
465
466                         if (format == null)
467                                 return null;
468                         
469                         return String.Format (format, arg0);
470                         */
471                         return String.Format ("{0}: {1}", key, arg0);
472                 }
473
474                 [MonoTODO ("FormatResourceString (string, string, string)")]
475                 internal static string FormatResourceString (string key, string arg0, string type) {
476                         return String.Format ("{0}: {1} {2}", key, arg0, type);
477                 }
478
479                 [MonoTODO ("FormatResourceString (string, string, string, string)")]
480                 internal static string FormatResourceString (string key, string arg0,
481                                                              string arg1, string arg2)
482                 {
483                         return String.Format ("{0}: {1} {2} {3}", key, arg0, arg1, arg2);
484                 }
485
486                 [MonoTODO ("FormatResourceString (string, string[]")]
487                 internal static string FormatResourceString (string key, string[] args)
488                 {
489                         //StringBuilder sb = new StringBuilder ();
490                         /*sb.AppendFormat ("{0}: ", key);
491                         foreach (string s in args)
492                                 sb.AppendFormat ("{0} ", s);
493
494                         if (sb.Length > 0)
495                                 sb.Length--;
496                         return sb.ToString ();*/
497                         string s = key + ": ";
498                         if (args != null)
499                                 foreach (string k in args)
500                                         s += k + " ";
501                         return s;
502                 }
503
504                 private static string GetResourceString (string key) {
505                         return _runtime.GetResourceStringFromResourceManager (key);
506                 }
507
508                 [MonoTODO ("GetResourceStringFromResourceManager (string)")]
509                 private string GetResourceStringFromResourceManager (string key) {
510                         return "String returned by HttpRuntime.GetResourceStringFromResourceManager";
511                 }
512
513                 #region Security Internal Methods (not impl)
514                 [MonoTODO ("Get Application path from the appdomain object")]
515                 internal static IStackWalk AppPathDiscovery {
516                         get {
517                                 if (appPathDiscoveryStackWalk == null) {
518                                         appPathDiscoveryStackWalk = new FileIOPermission (
519                                                 FileIOPermissionAccess.PathDiscovery, "<apppath>");
520                                 }
521                                 return appPathDiscoveryStackWalk;
522                         }
523                 }
524
525                 internal static IStackWalk ControlPrincipal {
526                         get {
527                                 if (ctrlPrincipalStackWalk == null) {
528                                         ctrlPrincipalStackWalk = new SecurityPermission (
529                                                 SecurityPermissionFlag.ControlPrincipal);
530                                 }
531                                 return ctrlPrincipalStackWalk;
532                         }
533                 }
534
535                 internal static IStackWalk Reflection {
536                         get {
537                                 if (reflectionStackWalk == null) {
538                                         reflectionStackWalk = new ReflectionPermission (
539                                                 ReflectionPermissionFlag.TypeInformation |
540                                                 ReflectionPermissionFlag.MemberAccess);
541                                 }
542                                 return reflectionStackWalk;
543                         }
544                 }
545
546                 internal static IStackWalk SensitiveInformation {
547                         get {
548                                 if (sensitiveInfoStackWalk == null) {
549                                         sensitiveInfoStackWalk = new EnvironmentPermission (
550                                                 PermissionState.Unrestricted);
551                                 }
552                                 return sensitiveInfoStackWalk;
553                         }
554                 }
555
556                 internal static IStackWalk UnmanagedCode {
557                         get {
558                                 if (unmgdCodeStackWalk == null) {
559                                         unmgdCodeStackWalk = new SecurityPermission (
560                                                 SecurityPermissionFlag.UnmanagedCode);
561                                 }
562                                 return unmgdCodeStackWalk;
563                         }
564                 }
565
566                 internal static IStackWalk Unrestricted {
567                         get {
568                                 if (unrestrictedStackWalk == null) {
569                                         unrestrictedStackWalk = new PermissionSet (
570                                                 PermissionState.Unrestricted);
571                                 }
572                                 return unrestrictedStackWalk;
573                         }
574                 }
575
576                 internal static IStackWalk FileReadAccess (string file)
577                 {
578                         return new FileIOPermission (FileIOPermissionAccess.Read, file);
579                 }
580
581                 internal static IStackWalk PathDiscoveryAccess (string path)
582                 {
583                         return new FileIOPermission (FileIOPermissionAccess.PathDiscovery, path);
584                 }
585                 #endregion
586         }
587 }