|
//------------------------------------------------------------------------------
// <copyright file="HttpContext.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
/*
* HttpContext class
*
* Copyright (c) 1999 Microsoft Corporation
*/
namespace System.Web {
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Configuration;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Remoting.Messaging;
using System.Security.Permissions;
using System.Security.Principal;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Caching;
using System.Web.Compilation;
using System.Web.Configuration;
using System.Web.Hosting;
using System.Web.Instrumentation;
using System.Web.Management;
using System.Web.Profile;
using System.Web.Security;
using System.Web.SessionState;
using System.Web.UI;
using System.Web.Util;
using System.Web.WebSockets;
/// <devdoc>
/// <para>Encapsulates
/// all HTTP-specific
/// context used by the HTTP server to process Web requests.</para>
/// <para>System.Web.IHttpModules and System.Web.IHttpHandler instances are provided a
/// reference to an appropriate HttpContext object. For example
/// the Request and Response
/// objects.</para>
/// </devdoc>
[SuppressMessage("Microsoft.Usage", "CA2302:FlagServiceProviders", Justification = "The service provider implementation is only for specific types which are not com interop types.")]
public sealed class HttpContext : IServiceProvider, IPrincipalContainer
{
internal static readonly Assembly SystemWebAssembly = typeof(HttpContext).Assembly;
private static volatile bool s_eurlSet;
private static string s_eurl;
private IHttpAsyncHandler _asyncAppHandler; // application as handler (not always HttpApplication)
private AsyncPreloadModeFlags _asyncPreloadModeFlags;
private bool _asyncPreloadModeFlagsSet;
private HttpApplication _appInstance;
private IHttpHandler _handler;
[DoNotReset]
private HttpRequest _request;
private HttpResponse _response;
private HttpServerUtility _server;
private Stack _traceContextStack;
private TraceContext _topTraceContext;
[DoNotReset]
private Hashtable _items;
private ArrayList _errors;
private Exception _tempError;
private bool _errorCleared;
[DoNotReset]
private IPrincipalContainer _principalContainer;
[DoNotReset]
internal ProfileBase _Profile;
[DoNotReset]
private DateTime _utcTimestamp;
[DoNotReset]
private HttpWorkerRequest _wr;
private VirtualPath _configurationPath;
internal bool _skipAuthorization;
[DoNotReset]
private CultureInfo _dynamicCulture;
[DoNotReset]
private CultureInfo _dynamicUICulture;
private int _serverExecuteDepth;
private Stack _handlerStack;
private bool _preventPostback;
private bool _runtimeErrorReported;
private PageInstrumentationService _pageInstrumentationService = null;
private ReadOnlyCollection<string> _webSocketRequestedProtocols;
// timeout support
[DoNotReset]
private CancellationTokenHelper _timeoutCancellationTokenHelper; // used for TimedOutToken
private long _timeoutStartTimeUtcTicks = -1; // should always be accessed atomically; -1 means uninitialized
private long _timeoutTicks = -1; // should always be accessed atomically; -1 means uninitialized
private int _timeoutState; // 0=non-cancelable, 1=cancelable, -1=canceled
private DoubleLink _timeoutLink; // link in the timeout's manager list
private bool _threadAbortOnTimeout = true; // whether we should Thread.Abort() this thread when it times out
private Thread _thread;
// cached configuration
private CachedPathData _configurationPathData; // Cached data if _configurationPath != null
private CachedPathData _filePathData; // Cached data of the file being requested
// Sql Cache Dependency
private string _sqlDependencyCookie;
// Session State
volatile SessionStateModule _sessionStateModule;
volatile bool _delayedSessionState; // Delayed session state item
// non-compiled pages
private TemplateControl _templateControl;
// integrated pipeline state
// For the virtual Disposing / Disposed events
private SubscriptionQueue<Action<HttpContext>> _requestCompletedQueue;
[DoNotReset]
private SubscriptionQueue<IDisposable> _pipelineCompletedQueue;
// keep synchronized with mgdhandler.hxx
private const int FLAG_NONE = 0x0;
private const int FLAG_CHANGE_IN_SERVER_VARIABLES = 0x1;
private const int FLAG_CHANGE_IN_REQUEST_HEADERS = 0x2;
private const int FLAG_CHANGE_IN_RESPONSE_HEADERS = 0x4;
private const int FLAG_CHANGE_IN_USER_OBJECT = 0x8;
private const int FLAG_SEND_RESPONSE_HEADERS = 0x10;
private const int FLAG_RESPONSE_HEADERS_SENT = 0x20;
internal const int FLAG_ETW_PROVIDER_ENABLED = 0x40;
private const int FLAG_CHANGE_IN_RESPONSE_STATUS = 0x80;
private volatile NotificationContext _notificationContext;
private bool _isAppInitialized;
[DoNotReset]
private bool _isIntegratedPipeline;
private bool _finishPipelineRequestCalled;
[DoNotReset]
private bool _impersonationEnabled;
internal bool HideRequestResponse;
internal volatile bool InIndicateCompletion;
internal volatile ThreadContext IndicateCompletionContext = null;
internal volatile Thread ThreadInsideIndicateCompletion = null;
// This field is a surrogate for the HttpContext object itself. Our HostExecutionContextManager
// shouldn't capture a reference to the HttpContext itself since these references could be long-lived,
// e.g. if they're captured by a call to ThreadPool.QueueUserWorkItem or a Timer. This would cause the
// associated HttpContext object graph to be long-lived, which would negatively affect performance.
// Instead we capture a reference to this 'Id' object, which allows the HostExecutionContextManager
// to compare the original captured HttpContext with the current HttpContext without actually
// holding on to the original HttpContext instance.
[DoNotReset]
internal readonly object ThreadContextId = new object();
// synchronization context (for EAP / TAP models)
private AspNetSynchronizationContextBase _syncContext;
// This field doesn't need to be volatile since it will only ever be written to by a single thread, and when that thread
// later reads the field it will be guaranteed non-null. We don't care what other threads see, since it will never be
// equal to Thread.CurrentThread for them regardless of whether those threads are seeing the latest value of this field.
// This field should not be marked [DoNotReset] since we want it to be cleared when WebSocket processing begins.
internal Thread _threadWhichStartedWebSocketTransition;
// WebSocket state
[DoNotReset]
private WebSocketTransitionState _webSocketTransitionState; // see comments in WebSocketTransitionState.cs for detailed info on this enum
[DoNotReset]
private string _webSocketNegotiatedProtocol;
// see comments on WebSocketInitStatus for what all of these codes mean
private WebSocketInitStatus GetWebSocketInitStatus() {
IIS7WorkerRequest iis7wr =_wr as IIS7WorkerRequest;
if (iis7wr == null) {
return WebSocketInitStatus.RequiresIntegratedMode;
}
if (CurrentNotification <= RequestNotification.BeginRequest) {
return WebSocketInitStatus.CannotCallFromBeginRequest;
}
if (!iis7wr.IsWebSocketRequest()) {
if (iis7wr.IsWebSocketModuleActive()) {
return WebSocketInitStatus.NotAWebSocketRequest;
}
else {
return WebSocketInitStatus.NativeModuleNotEnabled;
}
}
if (iis7wr.GetIsChildRequest()) {
return WebSocketInitStatus.CurrentRequestIsChildRequest;
}
return WebSocketInitStatus.Success;
}
// Returns true if the request contained the initial WebSocket handshake
// and IIS's WebSocket module is active.
public bool IsWebSocketRequest {
get {
// If AcceptWebSocketRequest has already been called and run to completion, then this
// is obviously a WebSocket request and we can skip further checks (which might throw).
if (IsWebSocketRequestUpgrading) {
return true;
}
switch (GetWebSocketInitStatus()) {
case WebSocketInitStatus.RequiresIntegratedMode:
throw new PlatformNotSupportedException(SR.GetString(SR.Requires_Iis_Integrated_Mode));
case WebSocketInitStatus.CannotCallFromBeginRequest:
throw new InvalidOperationException(SR.GetString(SR.WebSockets_CannotBeCalledDuringBeginRequest));
case WebSocketInitStatus.Success:
return true;
default:
return false;
}
}
}
// While unwinding an HTTP request this indicates if the developer
// told ASP.NET that they wanted to transition to a websocket request
public bool IsWebSocketRequestUpgrading {
get { return (WebSocketTransitionState >= WebSocketTransitionState.AcceptWebSocketRequestCalled); }
}
internal bool HasWebSocketRequestTransitionStarted {
get { return WebSocketTransitionState >= WebSocketTransitionState.TransitionStarted; }
}
internal bool HasWebSocketRequestTransitionCompleted {
get { return WebSocketTransitionState >= WebSocketTransitionState.TransitionCompleted; }
}
internal WebSocketTransitionState WebSocketTransitionState {
get { return _webSocketTransitionState; }
private set { _webSocketTransitionState = value; }
}
// Returns the ordered list of protocols requested by the client,
// or an empty collection if this wasn't a WebSocket request or there was no list present.
public IList<string> WebSocketRequestedProtocols {
get {
if (IsWebSocketRequest) {
if (_webSocketRequestedProtocols == null) {
string rawHeaderValue = _wr.GetUnknownRequestHeader("Sec-WebSocket-Protocol");
IList<string> requestedProtocols = SubProtocolUtil.ParseHeader(rawHeaderValue); // checks for invalid values
_webSocketRequestedProtocols = new ReadOnlyCollection<string>(requestedProtocols ?? new string[0]);
}
return _webSocketRequestedProtocols;
}
else {
// not a WebSocket request
return null;
}
}
}
// Returns the negotiated protocol (sent from the server to the client) for a
// WebSocket request.
public string WebSocketNegotiatedProtocol {
get { return _webSocketNegotiatedProtocol; }
}
public void AcceptWebSocketRequest(Func<AspNetWebSocketContext, Task> userFunc) {
AcceptWebSocketRequest(userFunc, null);
}
[SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "This is a safe critical method.")]
public void AcceptWebSocketRequest(Func<AspNetWebSocketContext, Task> userFunc, AspNetWebSocketOptions options) {
// Begin argument & state checking
// We throw different error codes depending on the check that failed. Things that are
// server configuration errors (WebSockets not enabled) or developer errors (called this
// method with bad parameters) result in an appropriate exception type. Things that are
// remote errors (e.g. bad parameters from the client) result in an HTTP 4xx.
if (userFunc == null) {
throw new ArgumentNullException("userFunc");
}
if (IsWebSocketRequestUpgrading) {
// this method cannot be called multiple times
throw new InvalidOperationException(SR.GetString(SR.WebSockets_AcceptWebSocketRequestCanOnlyBeCalledOnce));
}
// DevDiv #384514: Task<T> doesn't work correctly using the legacy SynchronizationContext setting. Since
// WebSockets operation requires correct Task<T> behavior, we should forbid using the feature when legacy
// mode is enabled.
SynchronizationContextUtil.ValidateModeForWebSockets();
switch (GetWebSocketInitStatus()) {
case WebSocketInitStatus.RequiresIntegratedMode:
throw new PlatformNotSupportedException(SR.GetString(SR.Requires_Iis_Integrated_Mode));
case WebSocketInitStatus.CannotCallFromBeginRequest:
throw new InvalidOperationException(SR.GetString(SR.WebSockets_CannotBeCalledDuringBeginRequest));
case WebSocketInitStatus.NativeModuleNotEnabled:
throw new PlatformNotSupportedException(SR.GetString(SR.WebSockets_WebSocketModuleNotEnabled));
case WebSocketInitStatus.NotAWebSocketRequest:
throw new HttpException((int)HttpStatusCode.BadRequest, SR.GetString(SR.WebSockets_NotAWebSocketRequest));
case WebSocketInitStatus.CurrentRequestIsChildRequest:
throw new InvalidOperationException(SR.GetString(SR.WebSockets_CannotBeCalledDuringChildExecute));
case WebSocketInitStatus.Success:
break;
default:
// fallback error message - not a WebSocket request
throw new HttpException(SR.GetString(SR.WebSockets_UnknownErrorWhileAccepting));
}
if (CurrentNotification > RequestNotification.ExecuteRequestHandler) {
// it is too late to call this method
throw new InvalidOperationException(SR.GetString(SR.WebSockets_CannotBeCalledAfterHandlerExecute));
}
// End argument & state checking
IIS7WorkerRequest wr = (IIS7WorkerRequest)_wr;
// Begin options checking and parsing
if (options != null && options.RequireSameOrigin) {
if (!WebSocketUtil.IsSameOriginRequest(wr)) {
// use Forbidden (HTTP 403) since it's not an authentication error; it's a usage error
throw new HttpException((int)HttpStatusCode.Forbidden, SR.GetString(SR.WebSockets_OriginCheckFailed));
}
}
string subprotocol = null;
if (options != null && !String.IsNullOrEmpty(options.SubProtocol)) {
// AspNetWebSocketOptions.set_SubProtocol() already checked that the provided value is valid
subprotocol = options.SubProtocol;
}
if (subprotocol != null) {
IList<string> incomingProtocols = WebSocketRequestedProtocols;
if (incomingProtocols == null || !incomingProtocols.Contains(subprotocol, StringComparer.Ordinal)) {
// The caller requested a subprotocol that wasn't in the list of accepted protocols coming from the client.
// This is disallowed by the WebSockets protocol spec, Sec. 5.2.2 (#2).
throw new ArgumentException(SR.GetString(SR.WebSockets_SubProtocolCannotBeNegotiated, subprotocol), "options");
}
}
// End options checking and parsing
wr.AcceptWebSocket();
// transition: Inactive -> AcceptWebSocketRequestCalled
TransitionToWebSocketState(WebSocketTransitionState.AcceptWebSocketRequestCalled);
Response.StatusCode = (int)HttpStatusCode.SwitchingProtocols; // 101
if (subprotocol != null) {
Response.AppendHeader("Sec-WebSocket-Protocol", subprotocol);
_webSocketNegotiatedProtocol = subprotocol;
}
RootedObjects.WebSocketPipeline = new WebSocketPipeline(RootedObjects, this, userFunc, subprotocol);
}
internal void TransitionToWebSocketState(WebSocketTransitionState newState) {
// Make sure the state transition is happening in the correct order
#if DBG
WebSocketTransitionState expectedOldState = checked(newState - 1);
Debug.Assert(WebSocketTransitionState == expectedOldState, String.Format(CultureInfo.InvariantCulture, "Expected WebSocketTransitionState to be '{0}', but it was '{1}'.", expectedOldState, WebSocketTransitionState));
#endif
WebSocketTransitionState = newState;
if (newState == Web.WebSocketTransitionState.TransitionStarted) {
_threadWhichStartedWebSocketTransition = Thread.CurrentThread;
}
}
internal bool DidCurrentThreadStartWebSocketTransition {
get {
return _threadWhichStartedWebSocketTransition == Thread.CurrentThread;
}
}
// helper that throws an exception if we have transitioned the current request to a WebSocket request
internal void EnsureHasNotTransitionedToWebSocket() {
if (HasWebSocketRequestTransitionCompleted) {
throw new NotSupportedException(SR.GetString(SR.WebSockets_MethodNotAvailableDuringWebSocketProcessing));
}
}
internal bool FirstRequest {get; set;}
// session state support
private bool _requiresSessionStateFromHandler;
internal bool RequiresSessionState {
get {
switch (SessionStateBehavior) {
case SessionStateBehavior.Required:
case SessionStateBehavior.ReadOnly:
return true;
case SessionStateBehavior.Disabled:
return false;
case SessionStateBehavior.Default:
default:
return _requiresSessionStateFromHandler;
}
}
}
private bool _readOnlySessionStateFromHandler;
internal bool ReadOnlySessionState {
get {
switch (SessionStateBehavior) {
case SessionStateBehavior.ReadOnly:
return true;
case SessionStateBehavior.Required:
case SessionStateBehavior.Disabled:
return false;
case SessionStateBehavior.Default:
default:
return _readOnlySessionStateFromHandler;
}
}
}
internal bool InAspCompatMode;
private IHttpHandler _remapHandler = null;
/// <include file='doc\HttpContext.uex' path='docs/doc[@for="HttpContext.HttpContext"]/*' />
/// <devdoc>
/// <para>
/// Initializes a new instance of the HttpContext class.
/// </para>
/// </devdoc>
public HttpContext(HttpRequest request, HttpResponse response) {
Init(request, response);
request.Context = this;
response.Context = this;
}
/// <devdoc>
/// <para>
/// Initializes a new instance of the HttpContext class.
/// </para>
/// </devdoc>
public HttpContext(HttpWorkerRequest wr) {
_wr = wr;
Init(new HttpRequest(wr, this), new HttpResponse(wr, this));
_response.InitResponseWriter();
}
// ctor used in HttpRuntime
internal HttpContext(HttpWorkerRequest wr, bool initResponseWriter) {
_wr = wr;
Init(new HttpRequest(wr, this), new HttpResponse(wr, this));
if (initResponseWriter)
_response.InitResponseWriter();
PerfCounters.IncrementCounter(AppPerfCounter.REQUESTS_EXECUTING);
}
private void Init(HttpRequest request, HttpResponse response) {
_request = request;
_response = response;
_utcTimestamp = DateTime.UtcNow;
_principalContainer = this;
if (_wr is IIS7WorkerRequest) {
_isIntegratedPipeline = true;
}
if (!(_wr is System.Web.SessionState.StateHttpWorkerRequest))
CookielessHelper.RemoveCookielessValuesFromPath(); // This ensures that the cookieless-helper is initialized and
// rewrites the path if the URI contains cookieless form-auth ticket, session-id, etc.
Profiler p = HttpRuntime.Profile;
if (p != null && p.IsEnabled)
_topTraceContext = new TraceContext(this);
// rewrite path in order to remove "/eurl.axd/guid", if it was
// added to the URL by aspnet_filter.dll.
string eurl = GetEurl();
if (!String.IsNullOrEmpty(eurl)) {
string path = request.Path;
int idxStartEurl = path.Length - eurl.Length;
bool hasTrailingSlash = (path[path.Length - 1] == '/');
if (hasTrailingSlash) {
idxStartEurl--;
}
if (idxStartEurl >= 0
&& StringUtil.Equals(path, idxStartEurl, eurl, 0, eurl.Length)) {
// restore original URL
int originalUrlLen = idxStartEurl;
if (hasTrailingSlash) {
originalUrlLen++;
}
string originalUrl = path.Substring(0, originalUrlLen);
// Dev10 835901: We don't call HttpContext.RewritePath(path) because the
// original path may contain '?' encoded as %3F, and RewritePath
// would interpret what follows as the query string. So instead, we
// clear ConfigurationPath and call InternalRewritePath directly.
ConfigurationPath = null;
Request.InternalRewritePath(VirtualPath.Create(originalUrl), null, true);
}
}
}
// We have a feature that directs extensionless URLs
// into managed code by appending "/eurl.axd/guid" to the path. On IIS 6.0,
// we restore the URL as soon as we get into managed code. Here we get the
// actual value of "/eurl.axd/guid" and remember it.
private string GetEurl() {
// only used on IIS 6.0
if (!(_wr is ISAPIWorkerRequestInProcForIIS6)
|| (_wr is ISAPIWorkerRequestInProcForIIS7)) {
return null;
}
string eurl = s_eurl;
if (eurl == null && !s_eurlSet) {
try {
IntPtr pBuffer = UnsafeNativeMethods.GetExtensionlessUrlAppendage();
if (pBuffer != IntPtr.Zero) {
eurl = StringUtil.StringFromWCharPtr(pBuffer, UnsafeNativeMethods.lstrlenW(pBuffer));
}
}
catch {} // ignore all exceptions
s_eurl = eurl;
s_eurlSet = true;
}
return eurl;
}
// Current HttpContext off the call context
#if DBG
internal static void SetDebugAssertOnAccessToCurrent(bool doAssert) {
if (doAssert) {
CallContext.SetData("__ContextAssert", String.Empty);
}
else {
CallContext.SetData("__ContextAssert", null);
}
}
private static bool NeedDebugAssertOnAccessToCurrent {
get {
return (CallContext.GetData("__ContextAssert") != null);
}
}
#endif
/// <devdoc>
/// <para>Returns the current HttpContext object.</para>
/// </devdoc>
public static HttpContext Current {
get {
#if DBG
if (NeedDebugAssertOnAccessToCurrent) {
Debug.Assert(ContextBase.Current != null);
}
#endif
return ContextBase.Current as HttpContext;
}
set {
ContextBase.Current = value;
}
}
//
// Root / unroot for the duration of async operation
// These are only used for the classic pipeline. The integrated pipeline uses a different rooting mechanism.
//
private IntPtr _rootedPtr;
[SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "This is a safe critical method.")]
internal void Root() {
_rootedPtr = GCUtil.RootObject(this);
}
internal void Unroot() {
GCUtil.UnrootObject(_rootedPtr);
_rootedPtr = IntPtr.Zero;
}
internal void FinishPipelineRequest() {
if (!_finishPipelineRequestCalled) {
_finishPipelineRequestCalled = true;
HttpRuntime.FinishPipelineRequest(this);
}
}
// This is a virtual event which occurs when the HTTP part of this request is winding down, e.g. after EndRequest
// but before the WebSockets pipeline kicks in. The HttpContext is still available for inspection and is provided
// as a parameter to the supplied callback.
[SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate", Justification = @"The normal event pattern doesn't work between HttpContext and HttpContextBase since the signatures differ.")]
public ISubscriptionToken AddOnRequestCompleted(Action<HttpContext> callback) {
if (callback == null) {
throw new ArgumentNullException("callback");
}
return _requestCompletedQueue.Enqueue(callback);
}
internal void RaiseOnRequestCompleted() {
// The callbacks really shouldn't throw exceptions, but we have a catch block just in case.
// Since there's nobody else that can listen for these errors (the request is unwinding and
// user code will no longer run), we'll just log the error.
try {
_requestCompletedQueue.FireAndComplete(action => action(this));
}
catch (Exception e) {
WebBaseEvent.RaiseRuntimeError(e, this);
}
finally {
// Dispose of TimedOutToken so that nobody tries using it after this point.
DisposeTimedOutToken();
}
}
// Allows an object's Dispose() method to be called when the pipeline part of this request is completed, e.g.
// after both the HTTP part and the WebSockets loop have completed. The HttpContext is not available for
// inspection, and HttpContext.Current will be null.
[SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "This is a safe critical method.")]
public ISubscriptionToken DisposeOnPipelineCompleted(IDisposable target) {
if (target == null) {
throw new ArgumentNullException("target");
}
if (RootedObjects != null) {
// integrated pipeline
return RootedObjects.DisposeOnPipelineCompleted(target);
}
else {
// classic pipeline
return _pipelineCompletedQueue.Enqueue(target);
}
}
internal void RaiseOnPipelineCompleted() {
// The callbacks really shouldn't throw exceptions, but we have a catch block just in case.
// Since there's nobody else that can listen for these errors (the request is unwinding and
// user code will no longer run), we'll just log the error.
try {
_pipelineCompletedQueue.FireAndComplete(disposable => disposable.Dispose());
}
catch (Exception e) {
WebBaseEvent.RaiseRuntimeError(e, null);
}
}
internal void ValidatePath() {
CachedPathData pathData = GetConfigurationPathData();
pathData.ValidatePath(_request.PhysicalPathInternal);
}
// IServiceProvider implementation
/// <internalonly/>
Object IServiceProvider.GetService(Type service) {
Object obj;
if (service == typeof(HttpWorkerRequest)) {
InternalSecurityPermissions.UnmanagedCode.Demand();
obj = _wr;
}
else if (service == typeof(HttpRequest))
obj = Request;
else if (service == typeof(HttpResponse))
obj = Response;
else if (service == typeof(HttpApplication))
obj = ApplicationInstance;
else if (service == typeof(HttpApplicationState))
obj = Application;
else if (service == typeof(HttpSessionState))
obj = Session;
else if (service == typeof(HttpServerUtility))
obj = Server;
else
obj = null;
return obj;
}
//
// Async app handler is remembered for the duration of execution of the
// request when application happens to be IHttpAsyncHandler. It is needed
// for HttpRuntime to remember the object on which to call OnEndRequest.
//
// The assumption is that application is a IHttpAsyncHandler, not always
// HttpApplication.
//
internal IHttpAsyncHandler AsyncAppHandler {
get { return _asyncAppHandler; }
set { _asyncAppHandler = value; }
}
public AsyncPreloadModeFlags AsyncPreloadMode {
get {
if (!_asyncPreloadModeFlagsSet) {
_asyncPreloadModeFlags = RuntimeConfig.GetConfig(this).HttpRuntime.AsyncPreloadMode;
_asyncPreloadModeFlagsSet = true;
}
return _asyncPreloadModeFlags;
}
set {
_asyncPreloadModeFlags = value;
_asyncPreloadModeFlagsSet = true;
}
}
// If this flag is not set, the AspNetSynchronizationContext associated with this request will throw
// exceptions when it detects the application misusing the async API. This can occur if somebody
// tries to call SynchronizationContext.Post / OperationStarted / etc. during a part of the
// pipeline where we weren't expecting asynchronous work to take place, if there is still
// outstanding asynchronous work when an asynchronous module or handler signals completion, etc.
// It is meant as a safety net to let developers know early on when they're writing async code
// which doesn't fit our expected patterns and where that code likely has negative side effects.
//
// This flag is respected only by AspNetSynchronizationContext; it has no effect when the
// legacy sync context is in use.
[EditorBrowsable(EditorBrowsableState.Advanced)]
public bool AllowAsyncDuringSyncStages {
get {
return SyncContext.AllowAsyncDuringSyncStages;
}
set {
SyncContext.AllowAsyncDuringSyncStages = value;
}
}
/// <devdoc>
/// <para>Retrieves a reference to the application object for the current Http request.</para>
/// </devdoc>
public HttpApplication ApplicationInstance {
get {
return _appInstance;
}
set {
// For integrated pipeline, once this is set to a non-null value, it can only be set to null.
// The setter should never have been made public. It probably happened in 1.0, before it was possible
// to have getter and setter with different accessibility.
if (_isIntegratedPipeline && _appInstance != null && value != null) {
throw new InvalidOperationException(SR.GetString(SR.Application_instance_cannot_be_changed));
}
else {
_appInstance = value;
// Use HttpApplication instance custom allocator provider
if (_isIntegratedPipeline) {
// The provider allows null - everyone should fallback to default implementation
IAllocatorProvider allocator = _appInstance != null ? _appInstance.AllocatorProvider : null;
_response.SetAllocatorProvider(allocator);
((IIS7WorkerRequest)_wr).AllocatorProvider = allocator;
}
}
}
}
/// <devdoc>
/// <para>
/// Retrieves a reference to the application object for the current
/// Http request.
/// </para>
/// </devdoc>
public HttpApplicationState Application {
get { return HttpApplicationFactory.ApplicationState; }
}
// flag to suppress use of custom HttpEncoder registered in web.config
// for example, yellow error pages should use the default encoder rather than a custom encoder
internal bool DisableCustomHttpEncoder {
get;
set;
}
/// <devdoc>
/// <para>
/// Retrieves or assigns a reference to the <see cref='System.Web.IHttpHandler'/>
/// object for the current request.
/// </para>
/// </devdoc>
public IHttpHandler Handler {
get { return _handler;}
set {
_handler = value;
_requiresSessionStateFromHandler = false;
_readOnlySessionStateFromHandler = false;
InAspCompatMode = false;
if (_handler != null) {
if (_handler is IRequiresSessionState) {
_requiresSessionStateFromHandler = true;
}
if (_handler is IReadOnlySessionState) {
_readOnlySessionStateFromHandler = true;
}
Page page = _handler as Page;
if (page != null && page.IsInAspCompatMode) {
InAspCompatMode = true;
}
}
}
}
/// <devdoc>
/// <para>
/// Retrieves or assigns a reference to the <see cref='System.Web.IHttpHandler'/>
/// object for the previous handler;
/// </para>
/// </devdoc>
public IHttpHandler PreviousHandler {
get {
if (_handlerStack == null || _handlerStack.Count == 0)
return null;
return (IHttpHandler)_handlerStack.Peek();
}
}
/// <devdoc>
/// <para>
/// Retrieves or assigns a reference to the <see cref='System.Web.IHttpHandler'/>
/// object for the current executing handler;
/// </para>
/// </devdoc>
private IHttpHandler _currentHandler = null;
public IHttpHandler CurrentHandler {
get {
if (_currentHandler == null)
_currentHandler = _handler;
return _currentHandler;
}
}
internal void RestoreCurrentHandler() {
_currentHandler = (IHttpHandler)_handlerStack.Pop();
}
internal void SetCurrentHandler(IHttpHandler newtHandler) {
if (_handlerStack == null) {
_handlerStack = new Stack();
}
_handlerStack.Push(CurrentHandler);
_currentHandler = newtHandler;
}
/// <devdoc>
/// <para>
/// Set custom mapping handler processing the request <see cref='System.Web.IHttpHandler'/>
/// </para>
/// </devdoc>
public void RemapHandler(IHttpHandler handler) {
EnsureHasNotTransitionedToWebSocket();
IIS7WorkerRequest wr = _wr as IIS7WorkerRequest;
if (wr != null) {
// Remap handler not allowed after ResolveRequestCache notification
if (_notificationContext.CurrentNotification >= RequestNotification.MapRequestHandler) {
throw new InvalidOperationException(SR.GetString(SR.Invoke_before_pipeline_event, "HttpContext.RemapHandler", "HttpApplication.MapRequestHandler"));
}
string handlerTypeName = null;
string handlerName = null;
if (handler != null) {
Type handlerType = handler.GetType();
handlerTypeName = handlerType.AssemblyQualifiedName;
handlerName = handlerType.FullName;
}
wr.SetRemapHandler(handlerTypeName, handlerName);
}
_remapHandler = handler;
}
internal IHttpHandler RemapHandlerInstance {
get {
return _remapHandler;
}
}
/// <devdoc>
/// <para>
/// Retrieves a reference to the target <see cref='System.Web.HttpRequest'/>
/// object for the current request.
/// </para>
/// </devdoc>
public HttpRequest Request {
get {
if (HideRequestResponse)
throw new HttpException(SR.GetString(SR.Request_not_available));
return _request;
}
}
/// <devdoc>
/// <para>
/// Retrieves a reference to the <see cref='System.Web.HttpResponse'/>
/// object for the current response.
/// </para>
/// </devdoc>
public HttpResponse Response {
get {
if (HideRequestResponse || HasWebSocketRequestTransitionCompleted)
throw new HttpException(SR.GetString(SR.Response_not_available));
return _response;
}
}
internal IHttpHandler TopHandler {
get {
if (_handlerStack == null) {
return _handler;
}
object[] handlers = _handlerStack.ToArray();
if (handlers == null || handlers.Length == 0) {
return _handler;
}
return (IHttpHandler)handlers[handlers.Length - 1];
}
}
/// <devdoc>
/// <para>Retrieves a reference to the <see cref='System.Web.TraceContext'/> object for the current
/// response.</para>
/// </devdoc>
public TraceContext Trace {
get {
if (_topTraceContext == null)
_topTraceContext = new TraceContext(this);
return _topTraceContext;
}
}
internal bool TraceIsEnabled {
get {
if (_topTraceContext == null)
return false;
return _topTraceContext.IsEnabled;
}
set {
if (value)
_topTraceContext = new TraceContext(this);
}
}
/// <devdoc>
/// <para>
/// Retrieves a key-value collection that can be used to
/// build up and share data between an <see cref='System.Web.IHttpModule'/> and an <see cref='System.Web.IHttpHandler'/>
/// during a
/// request.
/// </para>
/// </devdoc>
public IDictionary Items {
get {
if (_items == null)
_items = new Hashtable();
return _items;
}
}
/// <devdoc>
/// <para>
/// Gets a reference to the <see cref='System.Web.SessionState'/> instance for the current request.
/// </para>
/// </devdoc>
public HttpSessionState Session {
get {
if (HasWebSocketRequestTransitionCompleted) {
// Session is unavailable at this point
return null;
}
if (_delayedSessionState) {
lock (this) {
if (_delayedSessionState) {
Debug.Assert(_sessionStateModule != null, "_sessionStateModule != null");
// If it's not null, it means we have a delayed session state item
_sessionStateModule.InitStateStoreItem(true);
_delayedSessionState = false;
}
}
}
return(HttpSessionState)Items[SessionStateUtility.SESSION_KEY];
}
}
[MethodImpl(MethodImplOptions.NoInlining)]
internal void EnsureSessionStateIfNecessary() {
if (_sessionStateModule == null)
{
// If _sessionStateModule is null, we wouldn't be able to call
// _sessionStateModule.EnsureStateStoreItemLocked(), so we return here.
// _sessionStateModule could be null in the following cases,
// 1. No session state acquired.
// 2. HttpResponse.Flush() happens after session state being released.
// 3. The session state module in use is not System.Web.SessionState.SessionStateModule.
//
// This method is for the in-framework SessionStateModule only.
// OOB SessionStateModule can achieve this by using HttpResponse.AddOnSendingHeaders.
return;
}
HttpSessionState session = (HttpSessionState)Items[SessionStateUtility.SESSION_KEY];
if (session != null && // The session has been initiated
session.Count > 0 && // The session state is used
!string.IsNullOrEmpty(session.SessionID)) { // Ensure the session Id is valid - it will force to create new if didn't exist
_sessionStateModule.EnsureStateStoreItemLocked(); // Lock the item if in use
}
}
internal void AddHttpSessionStateModule(SessionStateModule module, bool delayed) {
if (_sessionStateModule != null && _sessionStateModule != module) {
throw new HttpException(SR.GetString(SR.Cant_have_multiple_session_module));
}
_sessionStateModule = module;
_delayedSessionState = delayed;
}
internal void RemoveHttpSessionStateModule() {
_delayedSessionState = false;
_sessionStateModule = null;
}
/// <devdoc>
/// <para>
/// Gets a reference to the <see cref='System.Web.HttpServerUtility'/>
/// for the current
/// request.
/// </para>
/// </devdoc>
public HttpServerUtility Server {
get {
// create only on demand
if (_server == null)
_server = new HttpServerUtility(this);
return _server;
}
}
// if the context has an error, report it, but only one time
internal void ReportRuntimeErrorIfExists(ref RequestNotificationStatus status) {
Exception e = Error;
if (e == null || _runtimeErrorReported) {
return;
}
// WOS 1921799: custom errors don't work in integrated mode if there's an initialization exception
if (_notificationContext != null && CurrentModuleIndex == -1) {
try {
IIS7WorkerRequest wr = _wr as IIS7WorkerRequest;
if (Request.QueryString["aspxerrorpath"] != null
&& wr != null
&& String.IsNullOrEmpty(wr.GetManagedHandlerType())
&& wr.GetCurrentModuleName() == PipelineRuntime.InitExceptionModuleName) {
status = RequestNotificationStatus.Continue; // allow non-managed handler to execute request
return;
}
}
catch {
}
}
_runtimeErrorReported = true;
if (HttpRuntime.AppOfflineMessage != null) {
try {
// report app offline error
Response.TrySkipIisCustomErrors = true;
HttpRuntime.ReportAppOfflineErrorMessage(Response, HttpRuntime.AppOfflineMessage);
}
catch {
}
}
else {
// report error exception
using (new DisposableHttpContextWrapper(this)) {
// if the custom encoder throws, it might interfere with returning error information
// to the client, so we force use of the default encoder
DisableCustomHttpEncoder = true;
// when application is on UNC share the code below must
// be run while impersonating the token given by IIS
using (new ApplicationImpersonationContext()) {
try {
try {
// try to report error in a way that could possibly throw (a config exception)
Response.ReportRuntimeError(e, true /*canThrow*/, false);
}
catch (Exception eReport) {
// report the config error in a way that would not throw
Response.ReportRuntimeError(eReport, false /*canThrow*/, false);
}
}
catch (Exception) {
}
}
}
}
status = RequestNotificationStatus.FinishRequest;
return;
}
/// <devdoc>
/// <para>
/// Gets the
/// first error (if any) accumulated during request processing.
/// </para>
/// </devdoc>
public Exception Error {
get {
if (_tempError != null)
return _tempError;
if (_errors == null || _errors.Count == 0 || _errorCleared)
return null;
return (Exception)_errors[0];
}
}
//
// Temp error (yet to be caught on app level)
// to be reported as Server.GetLastError() but could be cleared later
//
internal Exception TempError {
get { return _tempError; }
set { _tempError = value; }
}
/// <devdoc>
/// <para>
/// An array (collection) of errors accumulated while processing a
/// request.
/// </para>
/// </devdoc>
public Exception[] AllErrors {
get {
int n = (_errors != null) ? _errors.Count : 0;
if (n == 0)
return null;
Exception[] errors = new Exception[n];
_errors.CopyTo(0, errors, 0, n);
return errors;
}
}
/// <devdoc>
/// <para>
/// Registers an error for the current request.
/// </para>
/// </devdoc>
public void AddError(Exception errorInfo) {
if (_errors == null)
_errors = new ArrayList();
_errors.Add(errorInfo);
if (_isIntegratedPipeline && _notificationContext != null) {
// set the error on the current notification context
_notificationContext.Error = errorInfo;
}
}
/// <devdoc>
/// <para>
/// Clears all errors for the current request.
/// </para>
/// </devdoc>
public void ClearError() {
if (_tempError != null)
_tempError = null;
else
_errorCleared = true;
if (_isIntegratedPipeline && _notificationContext != null) {
// clear the error on the current notification context
_notificationContext.Error = null;
}
}
/// <devdoc>
/// <para>
/// IPrincipal security information.
/// </para>
/// </devdoc>
public IPrincipal User {
get { return _principalContainer.Principal; }
[SecurityPermission(SecurityAction.Demand, ControlPrincipal=true)]
set {
SetPrincipalNoDemand(value);
}
}
IPrincipal IPrincipalContainer.Principal {
get;
set;
}
// route all internals call to the principal (that don't have luring attacks)
// through this method so we can centralize reporting
// Before this, some auth modules were assigning directly to _user
internal void SetPrincipalNoDemand(IPrincipal principal, bool needToSetNativePrincipal) {
_principalContainer.Principal = principal;
// push changes through to native side
if (needToSetNativePrincipal
&& _isIntegratedPipeline
&& _notificationContext.CurrentNotification == RequestNotification.AuthenticateRequest) {
IntPtr pManagedPrincipal = IntPtr.Zero;
IIS7WorkerRequest wr = (IIS7WorkerRequest)_wr;
wr.SetPrincipal(principal);
}
}
internal void SetPrincipalNoDemand(IPrincipal principal) {
SetPrincipalNoDemand(principal, true /*needToSetNativePrincipal*/);
}
[DoNotReset]
internal bool _ProfileDelayLoad = false;
public ProfileBase Profile {
get {
if (_Profile == null && _ProfileDelayLoad)
_Profile = ProfileBase.Create(Request.IsAuthenticated ? User.Identity.Name : Request.AnonymousID, Request.IsAuthenticated);
return _Profile;
}
}
internal SessionStateBehavior SessionStateBehavior { get; set; }
[SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate",
Justification = "An internal property already exists. This method does additional work.")]
public void SetSessionStateBehavior(SessionStateBehavior sessionStateBehavior) {
if (_notificationContext != null && _notificationContext.CurrentNotification >= RequestNotification.AcquireRequestState) {
throw new InvalidOperationException(SR.GetString(SR.Invoke_before_pipeline_event, "HttpContext.SetSessionStateBehavior", "HttpApplication.AcquireRequestState"));
}
SessionStateBehavior = sessionStateBehavior;
}
public bool SkipAuthorization {
get { return _skipAuthorization;}
[SecurityPermission(SecurityAction.Demand, ControlPrincipal=true)]
set {
SetSkipAuthorizationNoDemand(value, false);
}
}
internal void SetSkipAuthorizationNoDemand(bool value, bool managedOnly)
{
if (HttpRuntime.UseIntegratedPipeline
&& !managedOnly
&& value != _skipAuthorization) {
// For integrated mode, persist changes to SkipAuthorization
// in the IS_LOGIN_PAGE server variable. When this server variable exists
// and the value is not "0", IIS skips authorization.
_request.SetSkipAuthorization(value);
}
_skipAuthorization = value;
}
// Pointer to the RootedObjects element, which contains information that needs to be flowed
// between the HttpContext and the WebSocket, such as the current principal.
[DoNotReset]
private RootedObjects _rootedObjects;
internal RootedObjects RootedObjects {
get {
return _rootedObjects;
}
set {
// Sync the Principal between the containers
SwitchPrincipalContainer(value);
_rootedObjects = value;
}
}
private void SwitchPrincipalContainer(IPrincipalContainer newPrincipalContainer) {
if (newPrincipalContainer == null) {
newPrincipalContainer = this;
}
// Ensure new container contains the current principal
IPrincipal currentPrincipal = _principalContainer.Principal;
newPrincipalContainer.Principal = currentPrincipal;
_principalContainer = newPrincipalContainer;
}
/// <devdoc>
/// <para>
/// Is this request in debug mode?
/// </para>
/// </devdoc>
public bool IsDebuggingEnabled {
get {
try {
return CompilationUtil.IsDebuggingEnabled(this);
}
catch {
// in case of config errors don't throw
return false;
}
}
}
/// <devdoc>
/// <para>
/// Is this custom error enabled for this request?
/// </para>
/// </devdoc>
public bool IsCustomErrorEnabled {
get {
return CustomErrorsSection.GetSettings(this).CustomErrorsEnabled(_request);
}
}
internal TemplateControl TemplateControl {
get {
return _templateControl;
}
set {
_templateControl = value;
}
}
/// <devdoc>
/// <para>Gets the initial timestamp of the current request.</para>
/// </devdoc>
public DateTime Timestamp {
get { return _utcTimestamp.ToLocalTime();}
}
internal DateTime UtcTimestamp {
get { return _utcTimestamp;}
}
internal HttpWorkerRequest WorkerRequest {
get { return _wr;}
}
/// <devdoc>
/// <para>
/// Gets a reference to the System.Web.Cache.Cache object for the current request.
/// </para>
/// </devdoc>
public Cache Cache {
get { return HttpRuntime.Cache;}
}
/// <summary>
/// Gets a reference to the System.Web.Instrumentation.PageInstrumentationService instance for this request. Guaranteed not to be null (barring private reflection magic).
/// </summary>
public PageInstrumentationService PageInstrumentation {
get {
if(_pageInstrumentationService == null) {
_pageInstrumentationService = new PageInstrumentationService();
}
return _pageInstrumentationService;
}
}
/*
* The virtual path used to get config settings. This allows the user
* to specify a non default config path, without having to pass it to every
* configuration call.
*/
internal VirtualPath ConfigurationPath {
get {
if (_configurationPath == null)
_configurationPath = _request.FilePathObject;
return _configurationPath;
}
set {
_configurationPath = value;
_configurationPathData = null;
_filePathData = null;
}
}
internal CachedPathData GetFilePathData() {
if (_filePathData == null) {
_filePathData = CachedPathData.GetVirtualPathData(_request.FilePathObject, false);
}
return _filePathData;
}
internal CachedPathData GetConfigurationPathData() {
if (_configurationPath == null) {
return GetFilePathData();
}
//
if (_configurationPathData == null) {
_configurationPathData = CachedPathData.GetVirtualPathData(_configurationPath, true);
}
return _configurationPathData;
}
internal CachedPathData GetPathData(VirtualPath path) {
if (path != null) {
if (path.Equals(_request.FilePathObject)) {
return GetFilePathData();
}
if (_configurationPath != null && path.Equals(_configurationPath)) {
return GetConfigurationPathData();
}
}
return CachedPathData.GetVirtualPathData(path, false);
}
internal void FinishRequestForCachedPathData(int statusCode) {
// Remove the cached path data for a file path if the first request for it
// does not succeed due to a bad request. Otherwise we could be vulnerable
// to a DOS attack.
if (_filePathData != null && !_filePathData.CompletedFirstRequest) {
if (400 <= statusCode && statusCode < 500) {
CachedPathData.RemoveBadPathData(_filePathData);
}
else {
CachedPathData.MarkCompleted(_filePathData);
}
}
}
/*
* Uses the Config system to get the specified configuraiton
*/
[Obsolete("The recommended alternative is System.Web.Configuration.WebConfigurationManager.GetWebApplicationSection in System.Web.dll. http://go.microsoft.com/fwlink/?linkid=14202")]
public static object GetAppConfig(String name) {
return WebConfigurationManager.GetWebApplicationSection(name);
}
[Obsolete("The recommended alternative is System.Web.HttpContext.GetSection in System.Web.dll. http://go.microsoft.com/fwlink/?linkid=14202")]
public object GetConfig(String name) {
return GetSection(name);
}
public object GetSection(String sectionName) {
if (HttpConfigurationSystem.UseHttpConfigurationSystem) {
return GetConfigurationPathData().ConfigRecord.GetSection(sectionName);
}
else {
return ConfigurationManager.GetSection(sectionName);
}
}
internal RuntimeConfig GetRuntimeConfig() {
return GetConfigurationPathData().RuntimeConfig;
}
internal RuntimeConfig GetRuntimeConfig(VirtualPath path) {
return GetPathData(path).RuntimeConfig;
}
public void RewritePath(String path) {
RewritePath(path, true);
}
/*
* Called by the URL rewrite module to modify the path for downstream modules
*/
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public void RewritePath(String path, bool rebaseClientPath) {
if (path == null)
throw new ArgumentNullException("path");
// extract query string
String qs = null;
int iqs = path.IndexOf('?');
if (iqs >= 0) {
qs = (iqs < path.Length-1) ? path.Substring(iqs+1) : String.Empty;
path = path.Substring(0, iqs);
}
// resolve relative path
VirtualPath virtualPath = VirtualPath.Create(path);
virtualPath = Request.FilePathObject.Combine(virtualPath);
// disallow paths outside of app
virtualPath.FailIfNotWithinAppRoot();
// clear things that depend on path
ConfigurationPath = null;
// rewrite path on request
Request.InternalRewritePath(virtualPath, qs, rebaseClientPath);
}
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public void RewritePath(String filePath, String pathInfo, String queryString) {
RewritePath(VirtualPath.CreateAllowNull(filePath), VirtualPath.CreateAllowNull(pathInfo),
queryString, false /*setClientFilePath*/);
}
public void RewritePath(string filePath, string pathInfo, String queryString, bool setClientFilePath)
{
RewritePath(VirtualPath.CreateAllowNull(filePath), VirtualPath.CreateAllowNull(pathInfo), queryString, setClientFilePath);
}
internal void RewritePath(VirtualPath filePath, VirtualPath pathInfo, String queryString, bool setClientFilePath) {
EnsureHasNotTransitionedToWebSocket();
if (filePath == null)
throw new ArgumentNullException("filePath");
// resolve relative path
filePath = Request.FilePathObject.Combine(filePath);
// disallow paths outside of app
filePath.FailIfNotWithinAppRoot();
// clear things that depend on path
ConfigurationPath = null;
// rewrite path on request
Request.InternalRewritePath(filePath, pathInfo, queryString, setClientFilePath);
}
internal CultureInfo DynamicCulture {
get { return _dynamicCulture; }
set { _dynamicCulture = value; }
}
internal CultureInfo DynamicUICulture {
get { return _dynamicUICulture; }
set { _dynamicUICulture = value; }
}
public static object GetGlobalResourceObject(string classKey, string resourceKey) {
return GetGlobalResourceObject(classKey, resourceKey, null);
}
public static object GetGlobalResourceObject(string classKey, string resourceKey, CultureInfo culture) {
return ResourceExpressionBuilder.GetGlobalResourceObject(classKey, resourceKey, null, null, culture);
}
public static object GetLocalResourceObject(string virtualPath, string resourceKey) {
return GetLocalResourceObject(virtualPath, resourceKey, null);
}
public static object GetLocalResourceObject(string virtualPath, string resourceKey, CultureInfo culture) {
IResourceProvider pageProvider = ResourceExpressionBuilder.GetLocalResourceProvider(
VirtualPath.Create(virtualPath));
return ResourceExpressionBuilder.GetResourceObject(pageProvider, resourceKey, culture);
}
internal int ServerExecuteDepth {
get { return _serverExecuteDepth; }
set { _serverExecuteDepth = value; }
}
internal bool PreventPostback {
get { return _preventPostback; }
set { _preventPostback = value; }
}
//
// Timeout support
//
internal Thread CurrentThread {
get {
return _thread;
}
set {
_thread = value;
}
}
// Property is thread-safe since needs to be accessed by RequestTimeoutManager in addition to
// normal request threads.
internal TimeSpan Timeout {
get {
long ticks = EnsureTimeout();
return TimeSpan.FromTicks(ticks);
}
set {
Interlocked.Exchange(ref _timeoutTicks, value.Ticks);
}
}
// Access via HttpRequest.TimedOutToken instead.
internal CancellationToken TimedOutToken {
get {
// If we are the first call site to observe the token, then create it in the non-canceled state.
CancellationTokenHelper helper = LazyInitializer.EnsureInitialized(ref _timeoutCancellationTokenHelper, () => new CancellationTokenHelper(canceled: false));
return helper.Token;
}
}
/// <summary>
/// Determines whether the ASP.NET runtime calls Thread.Abort() on the thread servicing this request when
/// the request times out. Default value is 'true'.
/// </summary>
/// <remarks>
/// Handlers and modules that are using Request.TimedOutToken to implement cooperative cancellation may
/// wish to disable the rude Thread.Abort behavior that ASP.NET has historically performed when a request
/// times out. This can help developers make sure that their g----ful cancellation + cleanup routines
/// will run without interruption by ASP.NET.
///
/// The rules for determining when a thread is aborted are somewhat complicated, so applications shouldn't
/// try to depend on them. Currently, the behavior is:
///
/// - The thread will be aborted at some point after Request.TimedOutToken is canceled. The abort might not
/// occur immediately afterward, as the "should Thread.Abort" timer is separate from the "should signal
/// the CancellationToken" timer.
///
/// - We generally don't abort threads that are processing async modules or handlers. There are some
/// exceptions. E.g., during certain parts of the lifecycle for async WebForms pages, the thread can be
/// a candidate to be aborted when a timeout occurs.
///
/// If a developer sets this property to 'false', ASP.NET will not automatically display a "Request timed
/// out" YSOD when a timeout occurs. If this happens the application is responsible for setting the response
/// content appropriately.
/// </remarks>
public bool ThreadAbortOnTimeout {
get { return Volatile.Read(ref _threadAbortOnTimeout); }
set { Volatile.Write(ref _threadAbortOnTimeout, value); }
}
private void DisposeTimedOutToken() {
// If we are the first call site to observe the token, then create it in the disposed state.
CancellationTokenHelper helper = LazyInitializer.EnsureInitialized(ref _timeoutCancellationTokenHelper, () => CancellationTokenHelper.StaticDisposed);
helper.Dispose();
}
internal long EnsureTimeout() {
// Calls to Volatile.* are atomic, even for 64-bit fields.
long ticks = Volatile.Read(ref _timeoutTicks);
if (ticks == -1) {
// Only go to config if the value hasn't yet been initialized.
HttpRuntimeSection cfg = RuntimeConfig.GetConfig(this).HttpRuntime;
ticks = cfg.ExecutionTimeout.Ticks;
// If another thread already came in and initialized _timeoutTicks,
// return that value instead of the value we just read from config.
long originalTicks = Interlocked.CompareExchange(ref _timeoutTicks, ticks, -1);
if (originalTicks != -1) {
ticks = originalTicks;
}
}
return ticks;
}
internal DoubleLink TimeoutLink {
get { return _timeoutLink;}
set { _timeoutLink = value;}
}
/*
Notes on the following 5 functions:
Execution can be cancelled only during certain periods, when inside the catch
block for ThreadAbortException. These periods are marked with the value of
_timeoutState of 1.
There is potential [rare] race condition when the timeout thread would call
thread.abort but the execution logic in the meantime escapes the catch block.
To avoid such race conditions _timeoutState of -1 (cancelled) is introduced.
The timeout thread sets _timeoutState to -1 before thread abort and the
unwinding logic just waits for the exception in this case. The wait cannot
be done in EndCancellablePeriod because the function is call from inside of
a finally block and thus would wait indefinetely. That's why another function
WaitForExceptionIfCancelled had been added.
Originally _timeoutStartTime was set in BeginCancellablePeriod. However, that means
we'll call UtcNow everytime we call ExecuteStep, which is too expensive. So to save
CPU time we created a new method SetStartTime() which is called by the caller of
ExecuteStep.
*/
internal void BeginCancellablePeriod() {
// It could be caused by an exception in OnThreadStart
if (Volatile.Read(ref _timeoutStartTimeUtcTicks) == -1) {
SetStartTime();
}
Volatile.Write(ref _timeoutState, 1);
}
internal void SetStartTime() {
Interlocked.Exchange(ref _timeoutStartTimeUtcTicks, DateTime.UtcNow.Ticks);
}
internal void EndCancellablePeriod() {
Interlocked.CompareExchange(ref _timeoutState, 0, 1);
}
internal void WaitForExceptionIfCancelled() {
while (Volatile.Read(ref _timeoutState) == -1)
Thread.Sleep(100);
}
internal bool IsInCancellablePeriod {
get { return (Volatile.Read(ref _timeoutState) == 1); }
}
internal Thread MustTimeout(DateTime utcNow) {
// Note: The TimedOutToken is keyed off of the HttpContext creation time, not the most recent async
// completion time (like the Thread.Abort logic later in this method).
if (_utcTimestamp + Timeout < utcNow) {
// If we are the first call site to observe the token, then create it in the canceled state.
CancellationTokenHelper helper = LazyInitializer.EnsureInitialized(ref _timeoutCancellationTokenHelper, () => new CancellationTokenHelper(canceled: true));
helper.Cancel();
}
if (Volatile.Read(ref _timeoutState) == 1 && ThreadAbortOnTimeout) { // fast check
long expirationUtcTicks = Volatile.Read(ref _timeoutStartTimeUtcTicks) + Timeout.Ticks; // don't care about overflow
if (expirationUtcTicks < utcNow.Ticks) {
// don't abort in debug mode
try {
if (CompilationUtil.IsDebuggingEnabled(this) || System.Diagnostics.Debugger.IsAttached)
return null;
}
catch {
// ignore config errors
return null;
}
// abort the thread only if in cancelable state, avoiding race conditions
// the caller MUST timeout if the return is true
if (Interlocked.CompareExchange(ref _timeoutState, -1, 1) == 1) {
if (_wr.IsInReadEntitySync) {
AbortConnection();
}
return _thread;
}
}
}
return null;
}
internal bool HasTimeoutExpired {
get {
// Check if it is allowed to timeout
if (Volatile.Read(ref _timeoutState) != 1 || !ThreadAbortOnTimeout) {
return false;
}
// Check if the timeout has expired
long expirationUtcTicks = Volatile.Read(ref _timeoutStartTimeUtcTicks) + Timeout.Ticks; // don't care about overflow
if (expirationUtcTicks >= DateTime.UtcNow.Ticks) {
return false;
}
// Dont't timeout when in debug
try {
if (CompilationUtil.IsDebuggingEnabled(this) || System.Diagnostics.Debugger.IsAttached) {
return false;
}
}
catch {
// ignore config errors
return false;
}
return true;
}
}
// call a delegate within cancellable period (possibly throwing timeout exception)
internal void InvokeCancellableCallback(WaitCallback callback, Object state) {
if (IsInCancellablePeriod) {
// call directly
callback(state);
return;
}
try {
BeginCancellablePeriod(); // request can be cancelled from this point
try {
callback(state);
}
finally {
EndCancellablePeriod(); // request can be cancelled until this point
}
WaitForExceptionIfCancelled(); // wait outside of finally
}
catch (ThreadAbortException e) {
if (e.ExceptionState != null &&
e.ExceptionState is HttpApplication.CancelModuleException &&
((HttpApplication.CancelModuleException)e.ExceptionState).Timeout) {
Thread.ResetAbort();
PerfCounters.IncrementCounter(AppPerfCounter.REQUESTS_TIMED_OUT);
throw new HttpException(SR.GetString(SR.Request_timed_out),
null, WebEventCodes.RuntimeErrorRequestAbort);
}
}
}
internal void PushTraceContext() {
if (_traceContextStack == null) {
_traceContextStack = new Stack();
}
// push current TraceContext on stack
_traceContextStack.Push(_topTraceContext);
// now make a new one for the top if necessary
if (_topTraceContext != null) {
TraceContext tc = new TraceContext(this);
_topTraceContext.CopySettingsTo(tc);
_topTraceContext = tc;
}
}
internal void PopTraceContext() {
Debug.Assert(_traceContextStack != null);
_topTraceContext = (TraceContext) _traceContextStack.Pop();
}
internal bool RequestRequiresAuthorization() {
#if !FEATURE_PAL // FEATURE_PAL does not enable IIS-based hosting features
// if current user is anonymous, then trivially, this page does not require authorization
if (!User.Identity.IsAuthenticated)
return false;
// Ask each of the authorization modules
return
( FileAuthorizationModule.RequestRequiresAuthorization(this) ||
UrlAuthorizationModule.RequestRequiresAuthorization(this) );
#else // !FEATURE_PAL
return false; // ROTORTODO
#endif // !FEATURE_PAL
}
internal int CallISAPI(UnsafeNativeMethods.CallISAPIFunc iFunction, byte [] bufIn, byte [] bufOut) {
if (_wr == null || !(_wr is System.Web.Hosting.ISAPIWorkerRequest))
throw new HttpException(SR.GetString(SR.Cannot_call_ISAPI_functions));
#if !FEATURE_PAL // FEATURE_PAL does not enable IIS-based hosting features
return ((System.Web.Hosting.ISAPIWorkerRequest) _wr).CallISAPI(iFunction, bufIn, bufOut);
#else // !FEATURE_PAL
throw new NotImplementedException ("ROTORTODO");
#endif // !FEATURE_PAL
}
internal void SendEmptyResponse() {
#if !FEATURE_PAL // FEATURE_PAL does not enable IIS-based hosting features
if (_wr != null && (_wr is System.Web.Hosting.ISAPIWorkerRequest))
((System.Web.Hosting.ISAPIWorkerRequest) _wr).SendEmptyResponse();
#endif // !FEATURE_PAL
}
private CookielessHelperClass _CookielessHelper;
internal CookielessHelperClass CookielessHelper {
get {
if (_CookielessHelper == null)
_CookielessHelper = new CookielessHelperClass(this);
return _CookielessHelper;
}
}
// When a thread enters the pipeline, we may need to set the cookie in the CallContext.
internal void ResetSqlDependencyCookie() {
if (_sqlDependencyCookie != null) {
System.Runtime.Remoting.Messaging.CallContext.LogicalSetData(SqlCacheDependency.SQL9_OUTPUT_CACHE_DEPENDENCY_COOKIE, _sqlDependencyCookie);
}
}
// When a thread leaves the pipeline, we may need to remove the cookie from the CallContext.
internal void RemoveSqlDependencyCookie() {
if (_sqlDependencyCookie != null) {
System.Runtime.Remoting.Messaging.CallContext.LogicalSetData(SqlCacheDependency.SQL9_OUTPUT_CACHE_DEPENDENCY_COOKIE, null);
}
}
internal string SqlDependencyCookie {
get {
return _sqlDependencyCookie;
}
set {
_sqlDependencyCookie = value;
System.Runtime.Remoting.Messaging.CallContext.LogicalSetData(SqlCacheDependency.SQL9_OUTPUT_CACHE_DEPENDENCY_COOKIE, value);
}
}
//
// integrated pipeline related
//
internal NotificationContext NotificationContext {
get { return _notificationContext; }
set { _notificationContext = value; }
}
public RequestNotification CurrentNotification {
get {
EnsureHasNotTransitionedToWebSocket();
if (!HttpRuntime.UseIntegratedPipeline) {
throw new PlatformNotSupportedException(SR.GetString(SR.Requires_Iis_Integrated_Mode));
}
return _notificationContext.CurrentNotification;
}
internal set {
if (!HttpRuntime.UseIntegratedPipeline) {
throw new PlatformNotSupportedException(SR.GetString(SR.Requires_Iis_Integrated_Mode));
}
_notificationContext.CurrentNotification = value;
}
}
internal bool IsChangeInServerVars {
get { return (_notificationContext.CurrentNotificationFlags & FLAG_CHANGE_IN_SERVER_VARIABLES) == FLAG_CHANGE_IN_SERVER_VARIABLES; }
}
internal bool IsChangeInRequestHeaders {
get { return (_notificationContext.CurrentNotificationFlags & FLAG_CHANGE_IN_REQUEST_HEADERS) == FLAG_CHANGE_IN_REQUEST_HEADERS; }
}
internal bool IsChangeInResponseHeaders {
get { return (_notificationContext.CurrentNotificationFlags & FLAG_CHANGE_IN_RESPONSE_HEADERS) == FLAG_CHANGE_IN_RESPONSE_HEADERS; }
}
internal bool IsChangeInResponseStatus {
get { return (_notificationContext.CurrentNotificationFlags & FLAG_CHANGE_IN_RESPONSE_STATUS) == FLAG_CHANGE_IN_RESPONSE_STATUS; }
}
internal bool IsChangeInUserPrincipal {
get { return (_notificationContext.CurrentNotificationFlags & FLAG_CHANGE_IN_USER_OBJECT) == FLAG_CHANGE_IN_USER_OBJECT; }
}
internal bool IsRuntimeErrorReported {
get { return _runtimeErrorReported; }
}
internal bool IsSendResponseHeaders {
get { return (_notificationContext.CurrentNotificationFlags & FLAG_SEND_RESPONSE_HEADERS) == FLAG_SEND_RESPONSE_HEADERS; }
}
internal void SetImpersonationEnabled() {
IdentitySection c = RuntimeConfig.GetConfig(this).Identity;
_impersonationEnabled = (c != null && c.Impersonate);
}
internal bool UsesImpersonation {
get {
// if we're on a UNC share and we have a UNC token, then use impersonation for all notifications
if (HttpRuntime.IsOnUNCShareInternal && HostingEnvironment.ApplicationIdentityToken != IntPtr.Zero) {
return true;
}
// if <identity impersonate=/> is false, then don't use impersonation
if (!_impersonationEnabled) {
return false;
}
// the notification context won't be available after we have completed the transition
if (HasWebSocketRequestTransitionCompleted) {
return true;
}
// if this notification is after AuthenticateRequest and not a SendResponse notification, use impersonation
return (((_notificationContext.CurrentNotification == RequestNotification.AuthenticateRequest && _notificationContext.IsPostNotification)
|| _notificationContext.CurrentNotification > RequestNotification.AuthenticateRequest)
&& _notificationContext.CurrentNotification != RequestNotification.SendResponse);
}
}
internal bool AreResponseHeadersSent {
get { return (_notificationContext.CurrentNotificationFlags & FLAG_RESPONSE_HEADERS_SENT) == FLAG_RESPONSE_HEADERS_SENT; }
}
internal bool NeedToInitializeApp() {
bool needToInit = !_isAppInitialized;
if (needToInit) {
_isAppInitialized = true;
}
return needToInit;
}
// flags passed in on the call to PipelineRuntime::ProcessRequestNotification
internal int CurrentNotificationFlags {
get {
return _notificationContext.CurrentNotificationFlags;
}
set {
_notificationContext.CurrentNotificationFlags = value;
}
}
// index of the current "module" running the request
// into the application module array
internal int CurrentModuleIndex {
get {
return _notificationContext.CurrentModuleIndex;
}
set {
_notificationContext.CurrentModuleIndex = value;
}
}
// Each module has a PipelineModuleStepContainer
// which stores/manages a list of event handlers
// that correspond to each RequestNotification.
// CurrentModuleEventIndex is the index (for the current
// module) of the current event handler.
// This will be greater than one when a single
// module registers multiple delegates for a single event.
// e.g.
// app.BeginRequest += Foo;
// app.BeginRequest += Bar;
internal int CurrentModuleEventIndex {
get {
return _notificationContext.CurrentModuleEventIndex;
}
set {
_notificationContext.CurrentModuleEventIndex = value;
}
}
internal void DisableNotifications(RequestNotification notifications, RequestNotification postNotifications) {
IIS7WorkerRequest wr = _wr as IIS7WorkerRequest;
if (null != wr) {
wr.DisableNotifications(notifications, postNotifications);
}
}
public bool IsPostNotification {
get {
EnsureHasNotTransitionedToWebSocket();
if (!HttpRuntime.UseIntegratedPipeline) {
throw new PlatformNotSupportedException(SR.GetString(SR.Requires_Iis_Integrated_Mode));
}
return _notificationContext.IsPostNotification;
}
internal set {
if (!HttpRuntime.UseIntegratedPipeline) {
throw new PlatformNotSupportedException(SR.GetString(SR.Requires_Iis_Integrated_Mode));
}
_notificationContext.IsPostNotification = value;
}
}
// user token for the request
[SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "This is a safe critical method.")]
internal IntPtr ClientIdentityToken {
get {
if (_wr != null) {
return _wr.GetUserToken();
}
else {
return IntPtr.Zero;
}
}
}
// is configured to impersonate client?
internal bool IsClientImpersonationConfigured {
get {
try {
IdentitySection c = RuntimeConfig.GetConfig(this).Identity;
return (c != null && c.Impersonate && c.ImpersonateToken == IntPtr.Zero);
}
catch {
// this property should not throw as it is used in the error reporting pass
// config errors will be reported elsewhere
return false;
}
}
}
internal IntPtr ImpersonationToken {
get {
// by default use app identity
IntPtr token = HostingEnvironment.ApplicationIdentityToken;
IdentitySection c = RuntimeConfig.GetConfig(this).Identity;
if (c != null) {
if (c.Impersonate) {
token = (c.ImpersonateToken != IntPtr.Zero) ? c.ImpersonateToken : ClientIdentityToken;
}
else {
// for non-UNC case impersonate="false" means "don't impersonate",
// but there is a special case for UNC shares - even if
// impersonate="false" we still impersonate the UNC identity
// (hosting identity). and this is how v1.x works as well
if (!HttpRuntime.IsOnUNCShareInternal) {
token = IntPtr.Zero;
}
}
}
return token;
}
}
internal AspNetSynchronizationContextBase SyncContext {
get {
if (_syncContext == null) {
_syncContext = CreateNewAspNetSynchronizationContext();
}
return _syncContext;
}
set {
_syncContext = value;
}
}
internal AspNetSynchronizationContextBase InstallNewAspNetSynchronizationContext() {
AspNetSynchronizationContextBase syncContext = _syncContext;
if (syncContext != null && syncContext == AsyncOperationManager.SynchronizationContext) {
// using current ASP.NET synchronization context - switch it
_syncContext = CreateNewAspNetSynchronizationContext();
AsyncOperationManager.SynchronizationContext = _syncContext;
return syncContext;
}
return null;
}
private AspNetSynchronizationContextBase CreateNewAspNetSynchronizationContext() {
if (!AppSettings.UseTaskFriendlySynchronizationContext) {
return new LegacyAspNetSynchronizationContext(ApplicationInstance);
}
else {
return new AspNetSynchronizationContext(ApplicationInstance);
}
}
[SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "This is a safe critical method.")]
internal void RestoreSavedAspNetSynchronizationContext(AspNetSynchronizationContextBase syncContext) {
AsyncOperationManager.SynchronizationContext = syncContext;
_syncContext = syncContext;
}
internal string[] UserLanguagesFromContext() {
return (Request != null) ? Request.UserLanguages : null;
}
// References should be nulled a.s.a.p. to reduce working set
internal void ClearReferences() {
_appInstance = null;
_handler = null;
_handlerStack = null;
_currentHandler = null;
_remapHandler = null;
if (_isIntegratedPipeline) {
if (!HasWebSocketRequestTransitionStarted) {
// Items is also used by AspNetWebSocketContext and should only be cleared if we're not transitioning to WebSockets
_items = null;
}
_syncContext = null;
}
}
internal void CompleteTransitionToWebSocket() {
ClearReferencesForWebSocketProcessing();
// transition: TransitionStarted -> TransitionCompleted
TransitionToWebSocketState(WebSocketTransitionState.TransitionCompleted);
}
// This is much stronger than just ClearReferences; it tries to free absolutely as much memory as possible.
// Some necessary items (like _wr, etc.) are preserved. The reason we want to modify this particular instance
// in-place rather than create a new instance is that it is likely that references to this object still exist,
// and we don't want the existence of those references to cause memory leaks.
private void ClearReferencesForWebSocketProcessing() {
HttpResponse response = _response;
// everything not marked [DoNotReset] should be eligible for garbage collection
ReflectionUtil.Reset(this);
// Miscellaneous steps:
_request.ClearReferencesForWebSocketProcessing(); // also clean up the HttpRequest instance
if (response != null) {
// HttpResponse is off-limits, but it is possible that the developer accidentally maintained a reference
// to it, e.g. via a closure. We'll release the HttpResponse's references to all its data to prevent
// this from causing memory problems.
ReflectionUtil.Reset(response);
}
}
internal CultureInfo CultureFromConfig(string configString, bool requireSpecific) {
//auto
if(StringUtil.EqualsIgnoreCase(configString, HttpApplication.AutoCulture)) {
string[] userLanguages = UserLanguagesFromContext();
if (userLanguages != null) {
try {
return CultureUtil.CreateReadOnlyCulture(userLanguages, requireSpecific);
}
catch {
return null;
}
}
else {
return null;
}
}
else if(StringUtil.StringStartsWithIgnoreCase(configString, "auto:")) {
string[] userLanguages = UserLanguagesFromContext();
if (userLanguages != null) {
try {
return CultureUtil.CreateReadOnlyCulture(userLanguages, requireSpecific);
}
catch {
return CultureUtil.CreateReadOnlyCulture(configString.Substring(5 /* "auto:".Length */), requireSpecific);
}
}
else {
return CultureUtil.CreateReadOnlyCulture(configString.Substring(5 /* "auto:".Length */), requireSpecific);
}
}
return CultureUtil.CreateReadOnlyCulture(configString, requireSpecific);
}
private enum WebSocketInitStatus {
Success, // iiswsock.dll is active and has told us that the current request is a WebSocket request
RequiresIntegratedMode, // WebSockets requires integrated mode, and the current server is not Integrated mode
CannotCallFromBeginRequest, // We need to wait for BeginRequest to complete before the module has set the server variables
NativeModuleNotEnabled, // iiswsock.dll isn't active in the pipeline
NotAWebSocketRequest, // iiswsock.dll is active, but the current request is not a WebSocket request
CurrentRequestIsChildRequest, // We are currently inside of a child request (IHttpContext::ExecuteRequest)
}
private void AbortConnection() {
IIS7WorkerRequest wr = _wr as IIS7WorkerRequest;
if (wr != null) {
// Direct API Abort is suported in integrated mode only
wr.AbortConnection();
}
else {
// Close in classic mode acts as Abort (see HSE_REQ_CLOSE_CONNECTION)
// It closes the underlined connection
_wr.CloseConnection();
}
}
}
//
// Helper class to add/remove HttpContext to/from CallContext
//
// using (new DisposableHttpContextWrapper(context)) {
// // this code will have HttpContext.Current working
// }
//
internal class DisposableHttpContextWrapper : IDisposable {
private bool _needToUndo;
private HttpContext _savedContext;
internal static HttpContext SwitchContext(HttpContext context) {
return ContextBase.SwitchContext(context) as HttpContext;
}
internal DisposableHttpContextWrapper(HttpContext context) {
if (context != null) {
_savedContext = SwitchContext(context);
_needToUndo = (_savedContext != context);
}
}
void IDisposable.Dispose() {
if (_needToUndo) {
SwitchContext(_savedContext);
_savedContext = null;
_needToUndo = false;
}
}
}
}
|