HttpRuntime.cs 15 KB

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