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 unloadDomainCallback;
66 private bool _firstRequestStarted;
67 //private bool _firstRequestExecuted;
69 private Exception _initError;
70 private TimeoutManager timeoutManager;
71 private QueueManager queueManager;
72 private TraceManager traceManager;
73 private WaitCallback doRequestCallback;
74 private int pendingCallbacks;
78 _runtime = new HttpRuntime ();
84 doRequestCallback = new WaitCallback (DoRequest);
87 static internal object CreateInternalObject(Type type) {
88 return Activator.CreateInstance(type, true);
94 _cache = new Cache ();
95 timeoutManager = new TimeoutManager ();
97 _endOfSendCallback = new HttpWorkerRequest.EndOfSendNotification (OnEndOfSend);
98 _handlerCallback = new AsyncCallback (OnHandlerReady);
99 unloadDomainCallback = new WaitCallback (DoUnload);
100 AppDomain.CurrentDomain.DomainUnload += new EventHandler (OnDomainUnload);
102 catch (Exception error) {
107 private void OnFirstRequestStart(HttpContext context) {
108 if (_initError != null)
112 WebConfigurationSettings.Init (context);
113 traceManager = new TraceManager ();
114 queueManager = new QueueManager ();
115 } catch (Exception e) {
119 // If we got an error during init, throw to client now..
120 if (null != _initError)
125 private void OnFirstRequestEnd() {
129 private void OnHandlerReady(IAsyncResult ar) {
130 HttpContext context = (HttpContext) ar.AsyncState;
132 IHttpAsyncHandler handler = context.AsyncHandler;
135 handler.EndProcessRequest(ar);
137 catch (Exception error) {
138 context.AddError(error);
142 context.AsyncHandler = null;
145 FinishRequest(context, context.Error);
148 private void OnEndOfSend(HttpWorkerRequest request, object data) {
149 HttpContext context = (HttpContext) data;
151 context.Request.Dispose();
152 context.Response.Dispose();
155 internal void FinishRequest(HttpContext context, Exception error) {
158 context.Response.FlushAtEndOfRequest();
159 } catch (Exception obj) {
164 HttpWorkerRequest request = context.WorkerRequest;
166 WebTrace.WriteLine (error.ToString ());
168 // We don't want the 'headers sent error'
169 context.Response.SetHeadersSent (false);
170 context.Response.Clear ();
171 context.Response.ClearHeaders ();
173 if (!(error is HttpException)) {
174 error = new HttpException (String.Empty, error);
175 context.Response.StatusCode = 500;
177 context.Response.StatusCode = ((HttpException) error).GetHttpCode ();
180 if (!RedirectCustomError (context))
181 context.Response.Write (((HttpException) error).GetHtmlErrorMessage ());
183 context.Response.FinalFlush ();
187 * This is not being used. OnFirstRequestEnd is empty.
188 if (!_firstRequestExecuted) {
190 if (!_firstRequestExecuted) {
191 _firstRequestExecuted = true;
198 Interlocked.Decrement(ref _activeRequests);
201 request.EndOfRequest();
203 TryExecuteQueuedRequests ();
206 bool RedirectCustomError (HttpContext context)
208 if (!context.IsCustomErrorEnabled)
211 CustomErrorsConfig config = null;
213 config = (CustomErrorsConfig) context.GetConfig ("system.web/customErrors");
216 if (config == null) {
217 if (context.ErrorPage != null)
218 return context.Response.RedirectCustomError (context.ErrorPage);
223 string redirect = config [context.Response.StatusCode];
224 if (redirect == null) {
225 redirect = context.ErrorPage;
226 if (redirect == null)
227 redirect = config.DefaultRedirect;
230 if (redirect == null)
233 return context.Response.RedirectCustomError (redirect);
236 internal static void FinishUnavailable (HttpWorkerRequest wr)
238 HttpContext context = new HttpContext (wr);
239 HttpException exception = new HttpException (503, "Service unavailable");
240 Interlocked.Increment (ref _runtime._activeRequests);
241 context.Response.InitializeWriter ();
242 _runtime.FinishRequest (context, exception);
245 void DoUnload (object state)
248 AppDomain.Unload (AppDomain.CurrentDomain);
252 internal void Dispose() {
253 WaitForRequests (2000);
254 queueManager.Dispose (); // Send a 503 to all queued requests
258 HttpApplicationFactory.EndApplication ();
261 void OnDomainUnload (object o, EventArgs args)
263 HttpApplicationFactory.EndApplication ();
266 internal void ByeByeDomain ()
268 HttpApplicationFactory.EndApplication ();
269 ThreadPool.QueueUserWorkItem (unloadDomainCallback);
272 internal void WaitForRequests(int ms) {
273 DateTime timeout = DateTime.Now.AddMilliseconds(ms);
276 if (Interlocked.CompareExchange (ref _activeRequests, 0, 0) == 0)
280 } while (timeout > DateTime.Now);
283 internal void InternalExecuteRequest (HttpWorkerRequest request)
285 IHttpHandler handler;
286 IHttpAsyncHandler async_handler;
288 HttpContext context = new HttpContext(request);
290 request.SetEndOfSendNotification(_endOfSendCallback, context);
292 Interlocked.Increment(ref _activeRequests);
295 if (!_firstRequestStarted) {
297 if (!_firstRequestStarted) {
298 OnFirstRequestStart(context);
299 _firstRequestStarted = true;
304 // This *must* be done after the configuration is initialized.
305 context.Response.InitializeWriter ();
306 handler = HttpApplicationFactory.GetInstance(context);
308 throw new HttpException(FormatResourceString("unable_to_create_app"));
310 if (handler is IHttpAsyncHandler) {
311 async_handler = (IHttpAsyncHandler) handler;
313 context.AsyncHandler = async_handler;
314 async_handler.BeginProcessRequest(context, _handlerCallback, context);
316 handler.ProcessRequest(context);
317 FinishRequest(context, null);
320 catch (Exception error) {
321 context.Response.InitializeWriter ();
322 FinishRequest(context, error);
326 void DoRequest (object o)
328 Interlocked.Decrement (ref pendingCallbacks);
329 InternalExecuteRequest ((HttpWorkerRequest) o);
332 void TryExecuteQueuedRequests ()
334 // Wait for pending jobs to start
335 if (Interlocked.CompareExchange (ref pendingCallbacks, 3, 3) >= 3)
338 HttpWorkerRequest wr = queueManager.GetNextRequest (null);
342 Interlocked.Increment (ref pendingCallbacks);
343 ThreadPool.QueueUserWorkItem (doRequestCallback, wr);
344 TryExecuteQueuedRequests ();
347 public static void ProcessRequest (HttpWorkerRequest request)
350 throw new ArgumentNullException ("request");
352 QueueManager mgr = _runtime.queueManager;
353 if (_runtime._firstRequestStarted && mgr != null) {
354 request = mgr.GetNextRequest (request);
355 // We're busy, return immediately
360 _runtime.InternalExecuteRequest (request);
364 public static void UnloadAppDomain ()
366 _runtime.ByeByeDomain ();
369 public static Cache Cache {
371 return _runtime._cache;
375 public static string AppDomainAppId {
377 if (appDomainAppId == null)
378 appDomainAppId = (string) AppDomain.CurrentDomain.GetData (".appId");
380 return appDomainAppId;
384 public static string AppDomainAppPath {
386 if (appDomainAppPath == null)
387 appDomainAppPath = (string) AppDomain.CurrentDomain.GetData (".appPath");
389 return appDomainAppPath;
393 public static string AppDomainAppVirtualPath {
395 if (appDomainAppVirtualPath == null)
396 appDomainAppVirtualPath = (string) AppDomain.CurrentDomain.GetData (".appVPath");
398 return appDomainAppVirtualPath;
402 public static string AppDomainId {
404 if (appDomainId == null)
405 appDomainId = (string) AppDomain.CurrentDomain.GetData (".domainId");
411 public static string AspInstallDirectory {
413 return ICalls.GetMachineInstallDirectory ();
417 public static string BinDirectory {
419 return Path.Combine (AppDomainAppPath, "bin");
423 public static string ClrInstallDirectory {
425 return ICalls.GetMachineInstallDirectory ();
429 public static string CodegenDir {
431 return AppDomain.CurrentDomain.SetupInformation.DynamicBase;
435 public static bool IsOnUNCShare {
437 // IsUnc broken under unix?
439 return (Environment.OSVersion.Platform != PlatformID.Unix &&
441 return (!((int) Environment.OSVersion.Platform == 128) &&
443 new Uri ("file://" + ClrInstallDirectory).IsUnc);
447 public static string MachineConfigurationDirectory {
449 return Path.GetDirectoryName (WebConfigurationSettings.MachineConfigPath);
453 internal static TimeoutManager TimeoutManager {
455 return HttpRuntime._runtime.timeoutManager;
459 internal static TraceManager TraceManager {
461 return HttpRuntime._runtime.traceManager;
465 public static void Close ()
470 internal static string FormatResourceString (string key)
472 return GetResourceString (key);
475 internal static string FormatResourceString (string key, string arg0)
477 /*string format = GetResourceString (key);
482 return String.Format (format, arg0);
484 return String.Format ("{0}: {1}", key, arg0);
487 [MonoTODO ("FormatResourceString (string, string, string)")]
488 internal static string FormatResourceString (string key, string arg0, string type) {
489 return String.Format ("{0}: {1} {2}", key, arg0, type);
492 [MonoTODO ("FormatResourceString (string, string, string, string)")]
493 internal static string FormatResourceString (string key, string arg0,
494 string arg1, string arg2)
496 return String.Format ("{0}: {1} {2} {3}", key, arg0, arg1, arg2);
499 [MonoTODO ("FormatResourceString (string, string[]")]
500 internal static string FormatResourceString (string key, string[] args)
502 //StringBuilder sb = new StringBuilder ();
503 /*sb.AppendFormat ("{0}: ", key);
504 foreach (string s in args)
505 sb.AppendFormat ("{0} ", s);
509 return sb.ToString ();*/
510 string s = key + ": ";
512 foreach (string k in args)
517 private static string GetResourceString (string key) {
518 return _runtime.GetResourceStringFromResourceManager (key);
521 [MonoTODO ("GetResourceStringFromResourceManager (string)")]
522 private string GetResourceStringFromResourceManager (string key) {
526 #region Security Internal Methods (not impl)
527 [MonoTODO ("Get Application path from the appdomain object")]
528 internal static IStackWalk AppPathDiscovery {
530 if (appPathDiscoveryStackWalk == null) {
531 appPathDiscoveryStackWalk = new FileIOPermission (
532 FileIOPermissionAccess.PathDiscovery, "<apppath>");
534 return appPathDiscoveryStackWalk;
538 internal static IStackWalk ControlPrincipal {
540 if (ctrlPrincipalStackWalk == null) {
541 ctrlPrincipalStackWalk = new SecurityPermission (
542 SecurityPermissionFlag.ControlPrincipal);
544 return ctrlPrincipalStackWalk;
548 internal static IStackWalk Reflection {
550 if (reflectionStackWalk == null) {
551 reflectionStackWalk = new ReflectionPermission (
552 ReflectionPermissionFlag.TypeInformation |
553 ReflectionPermissionFlag.MemberAccess);
555 return reflectionStackWalk;
559 internal static IStackWalk SensitiveInformation {
561 if (sensitiveInfoStackWalk == null) {
562 sensitiveInfoStackWalk = new EnvironmentPermission (
563 PermissionState.Unrestricted);
565 return sensitiveInfoStackWalk;
569 internal static IStackWalk UnmanagedCode {
571 if (unmgdCodeStackWalk == null) {
572 unmgdCodeStackWalk = new SecurityPermission (
573 SecurityPermissionFlag.UnmanagedCode);
575 return unmgdCodeStackWalk;
579 internal static IStackWalk Unrestricted {
581 if (unrestrictedStackWalk == null) {
582 unrestrictedStackWalk = new PermissionSet (
583 PermissionState.Unrestricted);
585 return unrestrictedStackWalk;
589 internal static IStackWalk FileReadAccess (string file)
591 return new FileIOPermission (FileIOPermissionAccess.Read, file);
594 internal static IStackWalk PathDiscoveryAccess (string path)
596 return new FileIOPermission (FileIOPermissionAccess.PathDiscovery, path);