2 // System.Web.HttpRuntime
5 // Patrik Torstensson (ptorsten@hotmail.com)
6 // Gaurav Vaish (gvaish@iitk.ac.in)
7 // Gonzalo Paniagua Javier (gonzalo@ximian.com)
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:
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
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.
31 using System.Collections;
34 using System.Security;
35 using System.Security.Permissions;
36 using System.Threading;
37 using System.Web.Configuration;
39 using System.Web.Util;
40 using System.Web.Caching;
42 namespace System.Web {
44 public sealed class HttpRuntime {
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;
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;
61 private int _activeRequests;
62 private HttpWorkerRequest.EndOfSendNotification _endOfSendCallback;
63 private AsyncCallback _handlerCallback;
64 private WaitCallback _appDomainCallback;
66 private bool _firstRequestStarted;
67 private bool _firstRequestExecuted;
68 private DateTime _firstRequestStartTime;
70 private Exception _initError;
71 private TimeoutManager timeoutManager;
72 private QueueManager queueManager;
73 private TraceManager traceManager;
74 private WaitCallback doRequestCallback;
75 private int pendingCallbacks;
79 appPathDiscoveryStackWalk = null;
80 ctrlPrincipalStackWalk = null;
81 sensitiveInfoStackWalk = null;
82 unmgdCodeStackWalk = null;
83 unrestrictedStackWalk = null;
85 _runtime = new HttpRuntime ();
91 doRequestCallback = new WaitCallback (DoRequest);
94 static internal object CreateInternalObject(Type type) {
95 return Activator.CreateInstance(type, true);
101 _cache = new Cache ();
102 timeoutManager = new TimeoutManager ();
104 _endOfSendCallback = new HttpWorkerRequest.EndOfSendNotification(OnEndOfSend);
105 _handlerCallback = new AsyncCallback(OnHandlerReady);
106 _appDomainCallback = new WaitCallback(OnAppDomainUnload);
108 catch (Exception error) {
113 private void OnFirstRequestStart(HttpContext context) {
114 if (_initError != null)
118 WebConfigurationSettings.Init (context);
119 traceManager = new TraceManager ();
120 queueManager = new QueueManager ();
121 } catch (Exception e) {
125 // If we got an error during init, throw to client now..
126 if (null != _initError)
130 private void OnFirstRequestEnd() {
133 private void OnHandlerReady(IAsyncResult ar) {
134 HttpContext context = (HttpContext) ar.AsyncState;
136 IHttpAsyncHandler handler = context.AsyncHandler;
139 handler.EndProcessRequest(ar);
141 catch (Exception error) {
142 context.AddError(error);
146 context.AsyncHandler = null;
149 FinishRequest(context, context.Error);
152 private void OnEndOfSend(HttpWorkerRequest request, object data) {
153 HttpContext context = (HttpContext) data;
155 context.Request.Dispose();
156 context.Response.Dispose();
159 internal void FinishRequest(HttpContext context, Exception error) {
162 context.Response.FlushAtEndOfRequest();
163 } catch (Exception obj) {
168 HttpWorkerRequest request = context.WorkerRequest;
170 WebTrace.WriteLine (error.ToString ());
172 context.Response.Clear ();
173 context.Response.ClearHeaders ();
175 if (!(error is HttpException)) {
176 error = new HttpException (String.Empty, error);
177 context.Response.StatusCode = 500;
179 context.Response.StatusCode = ((HttpException) error).GetHttpCode ();
182 if (!RedirectCustomError (context))
183 context.Response.Write (((HttpException) error).GetHtmlErrorMessage ());
185 context.Response.FinalFlush ();
188 if (!_firstRequestExecuted) {
190 if (!_firstRequestExecuted) {
191 _firstRequestExecuted = true;
197 Interlocked.Decrement(ref _activeRequests);
200 request.EndOfRequest();
202 TryExecuteQueuedRequests ();
205 bool RedirectCustomError (HttpContext context)
207 if (!context.IsCustomErrorEnabled)
210 CustomErrorsConfig config = null;
212 config = (CustomErrorsConfig) context.GetConfig ("system.web/customErrors");
215 if (config == null) {
216 if (context.ErrorPage != null)
217 return context.Response.RedirectCustomError (context.ErrorPage);
222 string redirect = config [context.Response.StatusCode];
223 if (redirect == null) {
224 redirect = context.ErrorPage;
225 if (redirect == null)
226 redirect = config.DefaultRedirect;
229 if (redirect == null)
232 return context.Response.RedirectCustomError (redirect);
235 internal static void FinishUnavailable (HttpWorkerRequest wr)
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);
243 private void OnAppDomainUnload(object state) {
247 internal void Dispose() {
248 WaitForRequests(5000);
249 queueManager.Dispose (); // Send a 503 to all queued requests
253 HttpApplicationFactory.EndApplication();
256 internal void WaitForRequests(int ms) {
257 DateTime timeout = DateTime.Now.AddMilliseconds(ms);
260 if (Interlocked.CompareExchange (ref _activeRequests, 0, 0) == 0)
264 } while (timeout > DateTime.Now);
267 internal void InternalExecuteRequest (HttpWorkerRequest request)
269 IHttpHandler handler;
270 IHttpAsyncHandler async_handler;
272 HttpContext context = new HttpContext(request);
274 request.SetEndOfSendNotification(_endOfSendCallback, context);
276 Interlocked.Increment(ref _activeRequests);
279 if (!_firstRequestStarted) {
281 if (!_firstRequestStarted) {
282 _firstRequestStarted = true;
283 _firstRequestStartTime = DateTime.Now;
284 OnFirstRequestStart(context);
289 // This *must* be done after the configuration is initialized.
290 context.Response.InitializeWriter ();
291 handler = HttpApplicationFactory.GetInstance(context);
293 throw new HttpException(FormatResourceString("unable_to_create_app"));
295 if (handler is IHttpAsyncHandler) {
296 async_handler = (IHttpAsyncHandler) handler;
298 context.AsyncHandler = async_handler;
299 async_handler.BeginProcessRequest(context, _handlerCallback, context);
301 handler.ProcessRequest(context);
302 FinishRequest(context, null);
305 catch (Exception error) {
306 context.Response.InitializeWriter ();
307 FinishRequest(context, error);
311 void DoRequest (object o)
313 Interlocked.Decrement (ref pendingCallbacks);
314 InternalExecuteRequest ((HttpWorkerRequest) o);
317 void TryExecuteQueuedRequests ()
319 // Wait for pending jobs to start
320 if (Interlocked.CompareExchange (ref pendingCallbacks, 3, 3) == 3) {
324 if (queueManager == null)
327 if (!queueManager.CanExecuteRequest (false)) {
331 HttpWorkerRequest wr = queueManager.Dequeue ();
336 Interlocked.Increment (ref pendingCallbacks);
337 ThreadPool.QueueUserWorkItem (doRequestCallback, wr);
338 TryExecuteQueuedRequests ();
341 public static void ProcessRequest (HttpWorkerRequest Request)
344 throw new ArgumentNullException ("Request");
346 if (!_runtime._firstRequestExecuted || _runtime.queueManager.CanExecuteRequest (false)) {
347 _runtime.InternalExecuteRequest (Request);
349 _runtime.queueManager.Queue (Request);
355 public void UnloadAppDomain ()
357 throw new NotImplementedException ();
360 public static Cache Cache {
362 return _runtime._cache;
366 public static string AppDomainAppId {
368 if (appDomainAppId == null)
369 appDomainAppId = (string) AppDomain.CurrentDomain.GetData (".appId");
371 return appDomainAppId;
375 public static string AppDomainAppPath {
377 if (appDomainAppPath == null)
378 appDomainAppPath = (string) AppDomain.CurrentDomain.GetData (".appPath");
380 return appDomainAppPath;
384 public static string AppDomainAppVirtualPath {
386 if (appDomainAppVirtualPath == null)
387 appDomainAppVirtualPath = (string) AppDomain.CurrentDomain.GetData (".appVPath");
389 return appDomainAppVirtualPath;
393 public static string AppDomainId {
395 if (appDomainId == null)
396 appDomainId = (string) AppDomain.CurrentDomain.GetData (".domainId");
402 public static string AspInstallDirectory {
404 return ICalls.GetMachineInstallDirectory ();
408 public static string BinDirectory {
410 return Path.Combine (AppDomainAppPath, "bin");
414 public static string ClrInstallDirectory {
416 return ICalls.GetMachineInstallDirectory ();
420 public static string CodegenDir {
422 return AppDomain.CurrentDomain.SetupInformation.DynamicBase;
426 public static bool IsOnUNCShare {
428 // IsUnc broken under unix?
429 return (!((int) Environment.OSVersion.Platform == 128) &&
430 new Uri ("file://" + ClrInstallDirectory).IsUnc);
434 public static string MachineConfigurationDirectory {
436 return Path.GetDirectoryName (WebConfigurationSettings.MachineConfigPath);
440 internal static TimeoutManager TimeoutManager {
442 return HttpRuntime._runtime.timeoutManager;
446 internal static TraceManager TraceManager {
448 return HttpRuntime._runtime.traceManager;
452 public static void Close ()
457 internal static string FormatResourceString (string key)
459 return GetResourceString (key);
462 internal static string FormatResourceString (string key, string arg0)
464 /*string format = GetResourceString (key);
469 return String.Format (format, arg0);
471 return String.Format ("{0}: {1}", key, arg0);
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);
479 [MonoTODO ("FormatResourceString (string, string, string, string)")]
480 internal static string FormatResourceString (string key, string arg0,
481 string arg1, string arg2)
483 return String.Format ("{0}: {1} {2} {3}", key, arg0, arg1, arg2);
486 [MonoTODO ("FormatResourceString (string, string[]")]
487 internal static string FormatResourceString (string key, string[] args)
489 //StringBuilder sb = new StringBuilder ();
490 /*sb.AppendFormat ("{0}: ", key);
491 foreach (string s in args)
492 sb.AppendFormat ("{0} ", s);
496 return sb.ToString ();*/
497 string s = key + ": ";
499 foreach (string k in args)
504 private static string GetResourceString (string key) {
505 return _runtime.GetResourceStringFromResourceManager (key);
508 [MonoTODO ("GetResourceStringFromResourceManager (string)")]
509 private string GetResourceStringFromResourceManager (string key) {
510 return "String returned by HttpRuntime.GetResourceStringFromResourceManager";
513 #region Security Internal Methods (not impl)
514 [MonoTODO ("Get Application path from the appdomain object")]
515 internal static IStackWalk AppPathDiscovery {
517 if (appPathDiscoveryStackWalk == null) {
518 appPathDiscoveryStackWalk = new FileIOPermission (
519 FileIOPermissionAccess.PathDiscovery, "<apppath>");
521 return appPathDiscoveryStackWalk;
525 internal static IStackWalk ControlPrincipal {
527 if (ctrlPrincipalStackWalk == null) {
528 ctrlPrincipalStackWalk = new SecurityPermission (
529 SecurityPermissionFlag.ControlPrincipal);
531 return ctrlPrincipalStackWalk;
535 internal static IStackWalk Reflection {
537 if (reflectionStackWalk == null) {
538 reflectionStackWalk = new ReflectionPermission (
539 ReflectionPermissionFlag.TypeInformation |
540 ReflectionPermissionFlag.MemberAccess);
542 return reflectionStackWalk;
546 internal static IStackWalk SensitiveInformation {
548 if (sensitiveInfoStackWalk == null) {
549 sensitiveInfoStackWalk = new EnvironmentPermission (
550 PermissionState.Unrestricted);
552 return sensitiveInfoStackWalk;
556 internal static IStackWalk UnmanagedCode {
558 if (unmgdCodeStackWalk == null) {
559 unmgdCodeStackWalk = new SecurityPermission (
560 SecurityPermissionFlag.UnmanagedCode);
562 return unmgdCodeStackWalk;
566 internal static IStackWalk Unrestricted {
568 if (unrestrictedStackWalk == null) {
569 unrestrictedStackWalk = new PermissionSet (
570 PermissionState.Unrestricted);
572 return unrestrictedStackWalk;
576 internal static IStackWalk FileReadAccess (string file)
578 return new FileIOPermission (FileIOPermissionAccess.Read, file);
581 internal static IStackWalk PathDiscoveryAccess (string path)
583 return new FileIOPermission (FileIOPermissionAccess.PathDiscovery, path);