|
//------------------------------------------------------------------------------
// <copyright file="HttpApplication.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
namespace System.Web {
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.ExceptionServices;
using System.Runtime.InteropServices;
using System.Runtime.Remoting.Messaging;
using System.Runtime.Serialization.Formatters;
using System.Security;
using System.Security.Permissions;
using System.Security.Principal;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using System.Web.Compilation;
using System.Web.Configuration;
using System.Web.Configuration.Common;
using System.Web.Hosting;
using System.Web.Management;
using System.Web.Security;
using System.Web.SessionState;
using System.Web.UI;
using System.Web.Util;
using IIS = System.Web.Hosting.UnsafeIISMethods;
//
// Async EventHandler support
//
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public delegate IAsyncResult BeginEventHandler(object sender, EventArgs e, AsyncCallback cb, object extraData);
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public delegate void EndEventHandler(IAsyncResult ar);
// Represents an event handler using TAP (Task Asynchronous Pattern).
public delegate Task TaskEventHandler(object sender, EventArgs e);
/// <devdoc>
/// <para>
/// The HttpApplication class defines the methods, properties and events common to all
/// HttpApplication objects within the ASP.NET Framework.
/// </para>
/// </devdoc>
[
ToolboxItem(false)
]
public class HttpApplication : IComponent, IHttpAsyncHandler, IRequestCompletedNotifier, ISyncContext {
// application state dictionary
private HttpApplicationState _state;
// context during init for config lookups
private HttpContext _initContext;
// async support
private HttpAsyncResult _ar; // currently pending async result for call into application
// list of modules
private static readonly DynamicModuleRegistry _dynamicModuleRegistry = new DynamicModuleRegistry();
private HttpModuleCollection _moduleCollection;
// event handlers
private static readonly object EventDisposed = new object();
private static readonly object EventErrorRecorded = new object();
private static readonly object EventRequestCompleted = new object();
private static readonly object EventPreSendRequestHeaders = new object();
private static readonly object EventPreSendRequestContent = new object();
private static readonly object EventBeginRequest = new object();
private static readonly object EventAuthenticateRequest = new object();
private static readonly object EventDefaultAuthentication = new object();
private static readonly object EventPostAuthenticateRequest = new object();
private static readonly object EventAuthorizeRequest = new object();
private static readonly object EventPostAuthorizeRequest = new object();
private static readonly object EventResolveRequestCache = new object();
private static readonly object EventPostResolveRequestCache = new object();
private static readonly object EventMapRequestHandler = new object();
private static readonly object EventPostMapRequestHandler = new object();
private static readonly object EventAcquireRequestState = new object();
private static readonly object EventPostAcquireRequestState = new object();
private static readonly object EventPreRequestHandlerExecute = new object();
private static readonly object EventPostRequestHandlerExecute = new object();
private static readonly object EventReleaseRequestState = new object();
private static readonly object EventPostReleaseRequestState = new object();
private static readonly object EventUpdateRequestCache = new object();
private static readonly object EventPostUpdateRequestCache = new object();
private static readonly object EventLogRequest = new object();
private static readonly object EventPostLogRequest = new object();
private static readonly object EventEndRequest = new object();
internal static readonly string AutoCulture = "auto";
private EventHandlerList _events;
private AsyncAppEventHandlersTable _asyncEvents;
// execution steps
private StepManager _stepManager;
// callback for Application ResumeSteps
#pragma warning disable 0649
private WaitCallback _resumeStepsWaitCallback;
#pragma warning restore 0649
// event passed to modules
private EventArgs _appEvent;
// list of handler mappings
private Hashtable _handlerFactories = new Hashtable();
// list of handler/factory pairs to be recycled
private ArrayList _handlerRecycleList;
// flag to hide request and response intrinsics
private bool _hideRequestResponse;
// application execution variables
private HttpContext _context;
private Exception _lastError; // placeholder for the error when context not avail
private bool _timeoutManagerInitialized;
// session (supplied by session-on-end outside of context)
private HttpSessionState _session;
// culture (needs to be set per thread)
private CultureInfo _appLevelCulture;
private CultureInfo _appLevelUICulture;
private CultureInfo _savedAppLevelCulture;
private CultureInfo _savedAppLevelUICulture;
private bool _appLevelAutoCulture;
private bool _appLevelAutoUICulture;
// pipeline event mappings
private Dictionary<string, RequestNotification> _pipelineEventMasks;
// IComponent support
private ISite _site;
// IIS7 specific fields
internal const string MANAGED_PRECONDITION = "managedHandler";
internal const string IMPLICIT_FILTER_MODULE = "AspNetFilterModule";
internal const string IMPLICIT_HANDLER = "ManagedPipelineHandler";
// map modules to their index
private static Hashtable _moduleIndexMap = new Hashtable();
private static bool _initSpecialCompleted;
private bool _initInternalCompleted;
private RequestNotification _appRequestNotifications;
private RequestNotification _appPostNotifications;
// Set the current module init key to the global.asax module to enable
// the custom global.asax derivation constructor to register event handlers
private string _currentModuleCollectionKey = HttpApplicationFactory.applicationFileName;
// module config is read once per app domain and used to initialize the per-instance _moduleContainers array
private static List<ModuleConfigurationInfo> _moduleConfigInfo;
// this is the per instance list that contains the events for each module
private PipelineModuleStepContainer[] _moduleContainers;
// Byte array to be used by HttpRequest.GetEntireRawContent. Windows OS Bug 1632921
private byte[] _entityBuffer;
// Counts the number of code paths consuming this HttpApplication instance. When the counter hits zero,
// it is safe to release this HttpApplication instance back into the HttpApplication pool.
// This counter can be null if we're not using the new Task-friendly code paths.
internal CountdownTask ApplicationInstanceConsumersCounter;
private IAllocatorProvider _allocator;
//
// Public Application properties
//
/// <devdoc>
/// <para>
/// HTTPRuntime provided context object that provides access to additional
/// pipeline-module exposed objects.
/// </para>
/// </devdoc>
[
Browsable(false),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)
]
public HttpContext Context {
get {
return(_context != null) ? _context : _initContext;
}
}
private bool IsContainerInitalizationAllowed {
get {
if (HttpRuntime.UseIntegratedPipeline && _initSpecialCompleted && !_initInternalCompleted) {
// return true if
// i) this is integrated pipeline mode,
// ii) InitSpecial has been called at least once in this AppDomain to register events with IIS,
// iii) InitInternal has not been invoked yet or is currently executing
return true;
}
return false;
}
}
private void ThrowIfEventBindingDisallowed() {
if (HttpRuntime.UseIntegratedPipeline && _initSpecialCompleted && _initInternalCompleted) {
// throw if we're using the integrated pipeline and both InitSpecial and InitInternal have completed.
throw new InvalidOperationException(SR.GetString(SR.Event_Binding_Disallowed));
}
}
private PipelineModuleStepContainer[] ModuleContainers {
get {
if (_moduleContainers == null) {
Debug.Assert(_moduleIndexMap != null && _moduleIndexMap.Count > 0, "_moduleIndexMap != null && _moduleIndexMap.Count > 0");
// At this point, all modules have been registered with IIS via RegisterIntegratedEvent.
// Now we need to create a container for each module and add execution steps.
// The number of containers is the same as the number of modules that have been
// registered (_moduleIndexMap.Count).
_moduleContainers = new PipelineModuleStepContainer[_moduleIndexMap.Count];
for (int i = 0; i < _moduleContainers.Length; i++) {
_moduleContainers[i] = new PipelineModuleStepContainer();
}
}
return _moduleContainers;
}
}
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public event EventHandler Disposed {
add {
Events.AddHandler(EventDisposed, value);
}
remove {
Events.RemoveHandler(EventDisposed, value);
}
}
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
protected EventHandlerList Events {
get {
if (_events == null) {
_events = new EventHandlerList();
}
return _events;
}
}
internal IExecutionStep CreateImplicitAsyncPreloadExecutionStep() {
ImplicitAsyncPreloadModule implicitAsyncPreloadModule = new ImplicitAsyncPreloadModule();
BeginEventHandler beginHandler = null;
EndEventHandler endHandler = null;
implicitAsyncPreloadModule.GetEventHandlers(out beginHandler, out endHandler);
return new AsyncEventExecutionStep(this, beginHandler, endHandler, null);
}
private AsyncAppEventHandlersTable AsyncEvents {
get {
if (_asyncEvents == null)
_asyncEvents = new AsyncAppEventHandlersTable();
return _asyncEvents;
}
}
// Last error during the processing of the current request.
internal Exception LastError {
get {
// only temporaraly public (will be internal and not related context)
return (_context != null) ? _context.Error : _lastError;
}
}
// Used by HttpRequest.GetEntireRawContent. Windows OS Bug 1632921
internal byte[] EntityBuffer
{
get
{
if (_entityBuffer == null)
{
_entityBuffer = new byte[8 * 1024];
}
return _entityBuffer;
}
}
// Provides fixed size reusable buffers per request
// Benefit:
// 1) Eliminates global locks - access to HttpApplication instance is lock free and no concurrent access is expected (by design).
// 36+ cores show really bad spin lock characteristics for short locks.
// 2) Better lifetime dynamics - Buffers increase/decrease as HttpApplication instances grow/shrink on demand.
internal IAllocatorProvider AllocatorProvider {
get {
if (_allocator == null) {
AllocatorProvider alloc = new AllocatorProvider();
alloc.CharBufferAllocator = new SimpleBufferAllocator<char>(BufferingParams.CHAR_BUFFER_SIZE);
alloc.IntBufferAllocator = new SimpleBufferAllocator<int>(BufferingParams.INT_BUFFER_SIZE);
alloc.IntPtrBufferAllocator = new SimpleBufferAllocator<IntPtr>(BufferingParams.INTPTR_BUFFER_SIZE);
Interlocked.CompareExchange(ref _allocator, alloc, null);
}
return _allocator;
}
}
internal void ClearError() {
_lastError = null;
}
/// <devdoc>
/// <para>HTTPRuntime provided request intrinsic object that provides access to incoming HTTP
/// request data.</para>
/// </devdoc>
[
Browsable(false),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)
]
public HttpRequest Request {
get {
HttpRequest request = null;
if (_context != null && !_hideRequestResponse)
request = _context.Request;
if (request == null)
throw new HttpException(SR.GetString(SR.Request_not_available));
return request;
}
}
/// <devdoc>
/// <para>HTTPRuntime provided
/// response intrinsic object that allows transmission of HTTP response data to a
/// client.</para>
/// </devdoc>
[
Browsable(false),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)
]
public HttpResponse Response {
get {
HttpResponse response = null;
if (_context != null && !_hideRequestResponse)
response = _context.Response;
if (response == null)
throw new HttpException(SR.GetString(SR.Response_not_available));
return response;
}
}
/// <devdoc>
/// <para>
/// HTTPRuntime provided session intrinsic.
/// </para>
/// </devdoc>
[
Browsable(false),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)
]
public HttpSessionState Session {
get {
HttpSessionState session = null;
if (_session != null)
session = _session;
else if (_context != null)
session = _context.Session;
if (session == null)
throw new HttpException(SR.GetString(SR.Session_not_available));
return session;
}
}
/// <devdoc>
/// <para>
/// Returns
/// a reference to an HTTPApplication state bag instance.
/// </para>
/// </devdoc>
[
Browsable(false),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)
]
public HttpApplicationState Application {
get {
Debug.Assert(_state != null); // app state always available
return _state;
}
}
/// <devdoc>
/// <para>Provides the web server Intrinsic object.</para>
/// </devdoc>
[
Browsable(false),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)
]
public HttpServerUtility Server {
get {
if (_context != null)
return _context.Server;
else
return new HttpServerUtility(this); // special Server for application only
}
}
/// <devdoc>
/// <para>Provides the User Intrinsic object.</para>
/// </devdoc>
[
Browsable(false),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)
]
public IPrincipal User {
get {
if (_context == null)
throw new HttpException(SR.GetString(SR.User_not_available));
return _context.User;
}
}
/// <devdoc>
/// <para>
/// Collection
/// of all IHTTPModules configured for the current application.
/// </para>
/// </devdoc>
[
Browsable(false),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)
]
public HttpModuleCollection Modules {
[AspNetHostingPermission(SecurityAction.Demand, Level=AspNetHostingPermissionLevel.High)]
get {
if (_moduleCollection == null)
_moduleCollection = new HttpModuleCollection();
return _moduleCollection;
}
}
// event passed to all modules
internal EventArgs AppEvent {
get {
if (_appEvent == null)
_appEvent = EventArgs.Empty;
return _appEvent;
}
set {
_appEvent = null;
}
}
private ISessionStateModule FindISessionStateModule() {
if (!HttpRuntime.UseIntegratedPipeline)
return null;
if (_moduleCollection != null) {
for (int i = 0; i < _moduleCollection.Count; i++) {
ISessionStateModule module = _moduleCollection.Get(i) as ISessionStateModule;
if (module != null) {
return module;
}
}
}
return null;
}
// DevDiv Bugs 151914: Release session state before executing child request
internal void EnsureReleaseState() {
ISessionStateModule module = FindISessionStateModule();
if (module != null) {
module.ReleaseSessionState(Context);
}
}
internal Task EnsureReleaseStateAsync() {
ISessionStateModule module = FindISessionStateModule();
if (module != null) {
return module.ReleaseSessionStateAsync(Context);
}
return TaskAsyncHelper.CompletedTask;
}
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public void CompleteRequest() {
//
// Request completion (force skipping all steps until RequestEnd
//
_stepManager.CompleteRequest();
}
internal bool IsRequestCompleted {
get {
if (null == _stepManager) {
return false;
}
return _stepManager.IsCompleted;
}
}
bool IRequestCompletedNotifier.IsRequestCompleted {
get {
return IsRequestCompleted;
}
}
// Dev10 745301: Asynchronous pipeline steps can start a new thread that triggers
// a SendResponse notification. E.g., it might call Flush when a module is registered
// for PreSendRequestHeaders/Content. If the async pipeline step returns from ExecuteStep
// while the SendResponse notification is executing, the NotificationContext can
// be corrupted. To fix this, a lock is now taken to prevent multi-threaded access when
// the async pipeline step sets the NotificationContext.PendingAsyncCompletion field.
// The SendResponse notification also acquires the lock when it enters managed code and
// releases the lock when it leaves.
internal void AcquireNotifcationContextLock(ref bool locked) {
Debug.Assert(HttpRuntime.UseIntegratedPipeline, "HttpRuntime.UseIntegratedPipeline");
Monitor.Enter(_stepManager, ref locked);
}
internal void ReleaseNotifcationContextLock() {
Debug.Assert(HttpRuntime.UseIntegratedPipeline, "HttpRuntime.UseIntegratedPipeline");
Monitor.Exit(_stepManager);
}
// Some frameworks built on top of the integrated pipeline call Flush() on background thread which will trigger nested
// RQ_SEND_RESPONSE notification and replace the old context.NotificationContext with the new context.NotificationContext.
// In order to maintain proper synchronization logic at the time when the completion callback is called we need to make sure
// we access the original context.NotificationContext (and don't touch the nested one).
// It will make sure that we read the correct NotificationContext
[MethodImpl(MethodImplOptions.NoInlining)]
private void GetNotifcationContextPropertiesUnderLock(ref bool isReentry, ref int eventCount) {
bool locked = false;
try {
AcquireNotifcationContextLock(ref locked);
isReentry = Context.NotificationContext.IsReEntry;
eventCount = CurrentModuleContainer.GetEventCount(Context.CurrentNotification, Context.IsPostNotification) - 1;
}
finally {
if (locked) {
ReleaseNotifcationContextLock();
}
}
}
[MethodImpl(MethodImplOptions.NoInlining)] // Iniling this causes throughtput regression in ResumeStep
private void GetNotifcationContextProperties(ref bool isReentry, ref int eventCount) {
// Read optimistically (without lock)
var nc = Context.NotificationContext;
isReentry = nc.IsReEntry;
// We can continue optimistic read only if this is not reentry
if (!isReentry) {
eventCount = ModuleContainers[nc.CurrentModuleIndex].GetEventCount(nc.CurrentNotification, nc.IsPostNotification) - 1;
// Check if the optimistic read was consistent
if (object.ReferenceEquals(nc, Context.NotificationContext)) {
return;
}
}
GetNotifcationContextPropertiesUnderLock(ref isReentry, ref eventCount);
}
private void RaiseOnError() {
EventHandler handler = (EventHandler)Events[EventErrorRecorded];
if (handler != null) {
try {
handler(this, AppEvent);
}
catch (Exception e) {
if (_context != null) {
_context.AddError(e);
}
}
}
}
private void RaiseOnRequestCompleted() {
EventHandler handler = (EventHandler)Events[EventRequestCompleted];
if (handler != null) {
try {
handler(this, AppEvent);
}
catch (Exception e) {
WebBaseEvent.RaiseRuntimeError(e, this);
}
}
}
internal void RaiseOnPreSendRequestHeaders() {
EventHandler handler = (EventHandler)Events[EventPreSendRequestHeaders];
if (handler != null) {
try {
handler(this, AppEvent);
}
catch (Exception e) {
RecordError(e);
}
}
}
internal void RaiseOnPreSendRequestContent() {
EventHandler handler = (EventHandler)Events[EventPreSendRequestContent];
if (handler != null) {
try {
handler(this, AppEvent);
}
catch (Exception e) {
RecordError(e);
}
}
}
internal HttpAsyncResult AsyncResult {
get {
if (HttpRuntime.UseIntegratedPipeline) {
return (_context.NotificationContext != null) ? _context.NotificationContext.AsyncResult : null;
}
else {
return _ar;
}
}
set {
if (HttpRuntime.UseIntegratedPipeline) {
_context.NotificationContext.AsyncResult = value;
}
else {
_ar = value;
}
}
}
internal void AddSyncEventHookup(object key, Delegate handler, RequestNotification notification) {
AddSyncEventHookup(key, handler, notification, false);
}
private PipelineModuleStepContainer CurrentModuleContainer { get { return ModuleContainers[_context.CurrentModuleIndex]; } }
private PipelineModuleStepContainer GetModuleContainer(string moduleName) {
object value = _moduleIndexMap[moduleName];
if (value == null) {
return null;
}
int moduleIndex = (int)value;
#if DBG
Debug.Trace("PipelineRuntime", "GetModuleContainer: moduleName=" + moduleName + ", index=" + moduleIndex.ToString(CultureInfo.InvariantCulture) + "\r\n");
Debug.Assert(moduleIndex >= 0 && moduleIndex < ModuleContainers.Length, "moduleIndex >= 0 && moduleIndex < ModuleContainers.Length");
#endif
PipelineModuleStepContainer container = ModuleContainers[moduleIndex];
Debug.Assert(container != null, "container != null");
return container;
}
private void AddSyncEventHookup(object key, Delegate handler, RequestNotification notification, bool isPostNotification) {
ThrowIfEventBindingDisallowed();
// add the event to the delegate invocation list
// this keeps non-pipeline ASP.NET hosts working
Events.AddHandler(key, handler);
// For integrated pipeline mode, add events to the IExecutionStep containers only if
// InitSpecial has completed and InitInternal has not completed.
if (IsContainerInitalizationAllowed) {
// lookup the module index and add this notification
PipelineModuleStepContainer container = GetModuleContainer(CurrentModuleCollectionKey);
//WOS 1985878: HttpModule unsubscribing an event handler causes AV in Integrated Mode
if (container != null) {
#if DBG
container.DebugModuleName = CurrentModuleCollectionKey;
#endif
SyncEventExecutionStep step = new SyncEventExecutionStep(this, (EventHandler)handler);
container.AddEvent(notification, isPostNotification, step);
}
}
}
internal void RemoveSyncEventHookup(object key, Delegate handler, RequestNotification notification) {
RemoveSyncEventHookup(key, handler, notification, false);
}
internal void RemoveSyncEventHookup(object key, Delegate handler, RequestNotification notification, bool isPostNotification) {
ThrowIfEventBindingDisallowed();
Events.RemoveHandler(key, handler);
if (IsContainerInitalizationAllowed) {
PipelineModuleStepContainer container = GetModuleContainer(CurrentModuleCollectionKey);
//WOS 1985878: HttpModule unsubscribing an event handler causes AV in Integrated Mode
if (container != null) {
container.RemoveEvent(notification, isPostNotification, handler);
}
}
}
private void AddSendResponseEventHookup(object key, Delegate handler) {
ThrowIfEventBindingDisallowed();
// add the event to the delegate invocation list
// this keeps non-pipeline ASP.NET hosts working
Events.AddHandler(key, handler);
// For integrated pipeline mode, add events to the IExecutionStep containers only if
// InitSpecial has completed and InitInternal has not completed.
if (IsContainerInitalizationAllowed) {
// lookup the module index and add this notification
PipelineModuleStepContainer container = GetModuleContainer(CurrentModuleCollectionKey);
//WOS 1985878: HttpModule unsubscribing an event handler causes AV in Integrated Mode
if (container != null) {
#if DBG
container.DebugModuleName = CurrentModuleCollectionKey;
#endif
bool isHeaders = (key == EventPreSendRequestHeaders);
SendResponseExecutionStep step = new SendResponseExecutionStep(this, (EventHandler)handler, isHeaders);
container.AddEvent(RequestNotification.SendResponse, false /*isPostNotification*/, step);
}
}
}
private void RemoveSendResponseEventHookup(object key, Delegate handler) {
ThrowIfEventBindingDisallowed();
Events.RemoveHandler(key, handler);
if (IsContainerInitalizationAllowed) {
PipelineModuleStepContainer container = GetModuleContainer(CurrentModuleCollectionKey);
//WOS 1985878: HttpModule unsubscribing an event handler causes AV in Integrated Mode
if (container != null) {
container.RemoveEvent(RequestNotification.SendResponse, false /*isPostNotification*/, handler);
}
}
}
//
// Sync event hookup
//
/// <devdoc><para>[To be supplied.]</para></devdoc>
public event EventHandler BeginRequest {
add { AddSyncEventHookup(EventBeginRequest, value, RequestNotification.BeginRequest); }
remove { RemoveSyncEventHookup(EventBeginRequest, value, RequestNotification.BeginRequest); }
}
/// <devdoc><para>[To be supplied.]</para></devdoc>
public event EventHandler AuthenticateRequest {
add { AddSyncEventHookup(EventAuthenticateRequest, value, RequestNotification.AuthenticateRequest); }
remove { RemoveSyncEventHookup(EventAuthenticateRequest, value, RequestNotification.AuthenticateRequest); }
}
// internal - for back-stop module only
internal event EventHandler DefaultAuthentication {
add { AddSyncEventHookup(EventDefaultAuthentication, value, RequestNotification.AuthenticateRequest); }
remove { RemoveSyncEventHookup(EventDefaultAuthentication, value, RequestNotification.AuthenticateRequest); }
}
/// <devdoc><para>[To be supplied.]</para></devdoc>
public event EventHandler PostAuthenticateRequest {
add { AddSyncEventHookup(EventPostAuthenticateRequest, value, RequestNotification.AuthenticateRequest, true); }
remove { RemoveSyncEventHookup(EventPostAuthenticateRequest, value, RequestNotification.AuthenticateRequest, true); }
}
/// <devdoc><para>[To be supplied.]</para></devdoc>
public event EventHandler AuthorizeRequest {
add { AddSyncEventHookup(EventAuthorizeRequest, value, RequestNotification.AuthorizeRequest); }
remove { RemoveSyncEventHookup(EventAuthorizeRequest, value, RequestNotification.AuthorizeRequest); }
}
/// <devdoc><para>[To be supplied.]</para></devdoc>
public event EventHandler PostAuthorizeRequest {
add { AddSyncEventHookup(EventPostAuthorizeRequest, value, RequestNotification.AuthorizeRequest, true); }
remove { RemoveSyncEventHookup(EventPostAuthorizeRequest, value, RequestNotification.AuthorizeRequest, true); }
}
/// <devdoc><para>[To be supplied.]</para></devdoc>
public event EventHandler ResolveRequestCache {
add { AddSyncEventHookup(EventResolveRequestCache, value, RequestNotification.ResolveRequestCache); }
remove { RemoveSyncEventHookup(EventResolveRequestCache, value, RequestNotification.ResolveRequestCache); }
}
/// <devdoc><para>[To be supplied.]</para></devdoc>
public event EventHandler PostResolveRequestCache {
add { AddSyncEventHookup(EventPostResolveRequestCache, value, RequestNotification.ResolveRequestCache, true); }
remove { RemoveSyncEventHookup(EventPostResolveRequestCache, value, RequestNotification.ResolveRequestCache, true); }
}
public event EventHandler MapRequestHandler {
add {
if (!HttpRuntime.UseIntegratedPipeline) {
throw new PlatformNotSupportedException(SR.GetString(SR.Requires_Iis_Integrated_Mode));
}
AddSyncEventHookup(EventMapRequestHandler, value, RequestNotification.MapRequestHandler);
}
remove {
if (!HttpRuntime.UseIntegratedPipeline) {
throw new PlatformNotSupportedException(SR.GetString(SR.Requires_Iis_Integrated_Mode));
}
RemoveSyncEventHookup(EventMapRequestHandler, value, RequestNotification.MapRequestHandler);
}
}
/// <devdoc><para>[To be supplied.]</para></devdoc>
public event EventHandler PostMapRequestHandler {
add { AddSyncEventHookup(EventPostMapRequestHandler, value, RequestNotification.MapRequestHandler, true); }
remove { RemoveSyncEventHookup(EventPostMapRequestHandler, value, RequestNotification.MapRequestHandler); }
}
/// <devdoc><para>[To be supplied.]</para></devdoc>
public event EventHandler AcquireRequestState {
add { AddSyncEventHookup(EventAcquireRequestState, value, RequestNotification.AcquireRequestState); }
remove { RemoveSyncEventHookup(EventAcquireRequestState, value, RequestNotification.AcquireRequestState); }
}
/// <devdoc><para>[To be supplied.]</para></devdoc>
public event EventHandler PostAcquireRequestState {
add { AddSyncEventHookup(EventPostAcquireRequestState, value, RequestNotification.AcquireRequestState, true); }
remove { RemoveSyncEventHookup(EventPostAcquireRequestState, value, RequestNotification.AcquireRequestState, true); }
}
/// <devdoc><para>[To be supplied.]</para></devdoc>
public event EventHandler PreRequestHandlerExecute {
add { AddSyncEventHookup(EventPreRequestHandlerExecute, value, RequestNotification.PreExecuteRequestHandler); }
remove { RemoveSyncEventHookup(EventPreRequestHandlerExecute, value, RequestNotification.PreExecuteRequestHandler); }
}
/// <devdoc><para>[To be supplied.]</para></devdoc>
public event EventHandler PostRequestHandlerExecute {
add { AddSyncEventHookup(EventPostRequestHandlerExecute, value, RequestNotification.ExecuteRequestHandler, true); }
remove { RemoveSyncEventHookup(EventPostRequestHandlerExecute, value, RequestNotification.ExecuteRequestHandler, true); }
}
/// <devdoc><para>[To be supplied.]</para></devdoc>
public event EventHandler ReleaseRequestState {
add { AddSyncEventHookup(EventReleaseRequestState, value, RequestNotification.ReleaseRequestState ); }
remove { RemoveSyncEventHookup(EventReleaseRequestState, value, RequestNotification.ReleaseRequestState); }
}
/// <devdoc><para>[To be supplied.]</para></devdoc>
public event EventHandler PostReleaseRequestState {
add { AddSyncEventHookup(EventPostReleaseRequestState, value, RequestNotification.ReleaseRequestState, true); }
remove { RemoveSyncEventHookup(EventPostReleaseRequestState, value, RequestNotification.ReleaseRequestState, true); }
}
/// <devdoc><para>[To be supplied.]</para></devdoc>
public event EventHandler UpdateRequestCache {
add { AddSyncEventHookup(EventUpdateRequestCache, value, RequestNotification.UpdateRequestCache); }
remove { RemoveSyncEventHookup(EventUpdateRequestCache, value, RequestNotification.UpdateRequestCache); }
}
/// <devdoc><para>[To be supplied.]</para></devdoc>
public event EventHandler PostUpdateRequestCache {
add { AddSyncEventHookup(EventPostUpdateRequestCache, value, RequestNotification.UpdateRequestCache, true); }
remove { RemoveSyncEventHookup(EventPostUpdateRequestCache, value, RequestNotification.UpdateRequestCache, true); }
}
public event EventHandler LogRequest {
add {
if (!HttpRuntime.UseIntegratedPipeline) {
throw new PlatformNotSupportedException(SR.GetString(SR.Requires_Iis_Integrated_Mode));
}
AddSyncEventHookup(EventLogRequest, value, RequestNotification.LogRequest);
}
remove {
if (!HttpRuntime.UseIntegratedPipeline) {
throw new PlatformNotSupportedException(SR.GetString(SR.Requires_Iis_Integrated_Mode));
}
RemoveSyncEventHookup(EventLogRequest, value, RequestNotification.LogRequest);
}
}
public event EventHandler PostLogRequest {
add {
if (!HttpRuntime.UseIntegratedPipeline) {
throw new PlatformNotSupportedException(SR.GetString(SR.Requires_Iis_Integrated_Mode));
}
AddSyncEventHookup(EventPostLogRequest, value, RequestNotification.LogRequest, true);
}
remove {
if (!HttpRuntime.UseIntegratedPipeline) {
throw new PlatformNotSupportedException(SR.GetString(SR.Requires_Iis_Integrated_Mode));
}
RemoveSyncEventHookup(EventPostLogRequest, value, RequestNotification.LogRequest, true);
}
}
/// <devdoc><para>[To be supplied.]</para></devdoc>
public event EventHandler EndRequest {
add { AddSyncEventHookup(EventEndRequest, value, RequestNotification.EndRequest); }
remove { RemoveSyncEventHookup(EventEndRequest, value, RequestNotification.EndRequest); }
}
/// <devdoc><para>[To be supplied.]</para></devdoc>
public event EventHandler Error {
add { Events.AddHandler(EventErrorRecorded, value); }
remove { Events.RemoveHandler(EventErrorRecorded, value); }
}
// Dev10 902404: a new HttpApplication.RequestCompleted event raised when the managed objects associated with
// the request are being released. It allows modules to cleanup resources after all managed modules and handlers
// have executed. This may occur before the native processing of the request has completed; for example, before
// the final response bytes have been sent to the client. The HttpContext is not available during this event
// because it has already been released.
public event EventHandler RequestCompleted {
add { Events.AddHandler(EventRequestCompleted, value); }
remove { Events.RemoveHandler(EventRequestCompleted, value); }
}
/// <devdoc><para>[To be supplied.]</para></devdoc>
public event EventHandler PreSendRequestHeaders {
add { AddSendResponseEventHookup(EventPreSendRequestHeaders, value); }
remove { RemoveSendResponseEventHookup(EventPreSendRequestHeaders, value); }
}
/// <devdoc><para>[To be supplied.]</para></devdoc>
public event EventHandler PreSendRequestContent {
add { AddSendResponseEventHookup(EventPreSendRequestContent, value); }
remove { RemoveSendResponseEventHookup(EventPreSendRequestContent, value); }
}
//
// Async event hookup
//
public void AddOnBeginRequestAsync(BeginEventHandler bh, EndEventHandler eh) {
AddOnBeginRequestAsync(bh, eh, null);
}
public void AddOnBeginRequestAsync(BeginEventHandler beginHandler, EndEventHandler endHandler, Object state) {
AsyncEvents.AddHandler(EventBeginRequest, beginHandler, endHandler, state, RequestNotification.BeginRequest, false, this);
}
public void AddOnAuthenticateRequestAsync(BeginEventHandler bh, EndEventHandler eh) {
AddOnAuthenticateRequestAsync(bh, eh, null);
}
public void AddOnAuthenticateRequestAsync(BeginEventHandler beginHandler, EndEventHandler endHandler, Object state) {
AsyncEvents.AddHandler(EventAuthenticateRequest, beginHandler, endHandler, state,
RequestNotification.AuthenticateRequest, false, this);
}
public void AddOnPostAuthenticateRequestAsync(BeginEventHandler bh, EndEventHandler eh) {
AddOnPostAuthenticateRequestAsync(bh, eh, null);
}
public void AddOnPostAuthenticateRequestAsync(BeginEventHandler beginHandler, EndEventHandler endHandler, Object state) {
AsyncEvents.AddHandler(EventPostAuthenticateRequest, beginHandler, endHandler, state,
RequestNotification.AuthenticateRequest, true, this);
}
public void AddOnAuthorizeRequestAsync(BeginEventHandler bh, EndEventHandler eh) {
AddOnAuthorizeRequestAsync(bh, eh, null);
}
public void AddOnAuthorizeRequestAsync(BeginEventHandler beginHandler, EndEventHandler endHandler, Object state) {
AsyncEvents.AddHandler(EventAuthorizeRequest, beginHandler, endHandler, state,
RequestNotification.AuthorizeRequest, false, this);
}
public void AddOnPostAuthorizeRequestAsync(BeginEventHandler bh, EndEventHandler eh) {
AddOnPostAuthorizeRequestAsync(bh, eh, null);
}
public void AddOnPostAuthorizeRequestAsync(BeginEventHandler beginHandler, EndEventHandler endHandler, Object state) {
AsyncEvents.AddHandler(EventPostAuthorizeRequest, beginHandler, endHandler, state,
RequestNotification.AuthorizeRequest, true, this);
}
public void AddOnResolveRequestCacheAsync(BeginEventHandler bh, EndEventHandler eh) {
AddOnResolveRequestCacheAsync(bh, eh, null);
}
public void AddOnResolveRequestCacheAsync(BeginEventHandler beginHandler, EndEventHandler endHandler, Object state) {
AsyncEvents.AddHandler(EventResolveRequestCache, beginHandler, endHandler, state,
RequestNotification.ResolveRequestCache, false, this);
}
public void AddOnPostResolveRequestCacheAsync(BeginEventHandler bh, EndEventHandler eh) {
AddOnPostResolveRequestCacheAsync(bh, eh, null);
}
public void AddOnPostResolveRequestCacheAsync(BeginEventHandler beginHandler, EndEventHandler endHandler, Object state) {
AsyncEvents.AddHandler(EventPostResolveRequestCache, beginHandler, endHandler, state,
RequestNotification.ResolveRequestCache, true, this);
}
public void AddOnMapRequestHandlerAsync(BeginEventHandler bh, EndEventHandler eh) {
if (!HttpRuntime.UseIntegratedPipeline) {
throw new PlatformNotSupportedException(SR.GetString(SR.Requires_Iis_Integrated_Mode));
}
AddOnMapRequestHandlerAsync(bh, eh, null);
}
public void AddOnMapRequestHandlerAsync(BeginEventHandler beginHandler, EndEventHandler endHandler, Object state) {
if (!HttpRuntime.UseIntegratedPipeline) {
throw new PlatformNotSupportedException(SR.GetString(SR.Requires_Iis_Integrated_Mode));
}
AsyncEvents.AddHandler(EventMapRequestHandler, beginHandler, endHandler, state,
RequestNotification.MapRequestHandler, false, this);
}
public void AddOnPostMapRequestHandlerAsync(BeginEventHandler bh, EndEventHandler eh) {
AddOnPostMapRequestHandlerAsync(bh, eh, null);
}
public void AddOnPostMapRequestHandlerAsync(BeginEventHandler beginHandler, EndEventHandler endHandler, Object state) {
AsyncEvents.AddHandler(EventPostMapRequestHandler, beginHandler, endHandler, state,
RequestNotification.MapRequestHandler, true, this);
}
public void AddOnAcquireRequestStateAsync(BeginEventHandler bh, EndEventHandler eh) {
AddOnAcquireRequestStateAsync(bh, eh, null);
}
public void AddOnAcquireRequestStateAsync(BeginEventHandler beginHandler, EndEventHandler endHandler, Object state) {
AsyncEvents.AddHandler(EventAcquireRequestState, beginHandler, endHandler, state,
RequestNotification.AcquireRequestState, false, this);
}
public void AddOnPostAcquireRequestStateAsync(BeginEventHandler bh, EndEventHandler eh) {
AddOnPostAcquireRequestStateAsync(bh, eh, null);
}
public void AddOnPostAcquireRequestStateAsync(BeginEventHandler beginHandler, EndEventHandler endHandler, Object state) {
AsyncEvents.AddHandler(EventPostAcquireRequestState, beginHandler, endHandler, state,
RequestNotification.AcquireRequestState, true, this);
}
public void AddOnPreRequestHandlerExecuteAsync(BeginEventHandler bh, EndEventHandler eh) {
AddOnPreRequestHandlerExecuteAsync(bh, eh, null);
}
public void AddOnPreRequestHandlerExecuteAsync(BeginEventHandler beginHandler, EndEventHandler endHandler, Object state) {
AsyncEvents.AddHandler(EventPreRequestHandlerExecute, beginHandler, endHandler, state,
RequestNotification.PreExecuteRequestHandler, false, this);
}
public void AddOnPostRequestHandlerExecuteAsync(BeginEventHandler bh, EndEventHandler eh) {
AddOnPostRequestHandlerExecuteAsync(bh, eh, null);
}
public void AddOnPostRequestHandlerExecuteAsync(BeginEventHandler beginHandler, EndEventHandler endHandler, Object state) {
AsyncEvents.AddHandler(EventPostRequestHandlerExecute, beginHandler, endHandler, state,
RequestNotification.ExecuteRequestHandler, true, this);
}
public void AddOnReleaseRequestStateAsync(BeginEventHandler bh, EndEventHandler eh) {
AddOnReleaseRequestStateAsync(bh, eh, null);
}
public void AddOnReleaseRequestStateAsync(BeginEventHandler beginHandler, EndEventHandler endHandler, Object state) {
AsyncEvents.AddHandler(EventReleaseRequestState, beginHandler, endHandler, state,
RequestNotification.ReleaseRequestState, false, this);
}
public void AddOnPostReleaseRequestStateAsync(BeginEventHandler bh, EndEventHandler eh) {
AddOnPostReleaseRequestStateAsync(bh, eh, null);
}
public void AddOnPostReleaseRequestStateAsync(BeginEventHandler beginHandler, EndEventHandler endHandler, Object state) {
AsyncEvents.AddHandler(EventPostReleaseRequestState, beginHandler, endHandler, state,
RequestNotification.ReleaseRequestState, true, this);
}
public void AddOnUpdateRequestCacheAsync(BeginEventHandler bh, EndEventHandler eh) {
AddOnUpdateRequestCacheAsync(bh, eh, null);
}
public void AddOnUpdateRequestCacheAsync(BeginEventHandler beginHandler, EndEventHandler endHandler, Object state) {
AsyncEvents.AddHandler(EventUpdateRequestCache, beginHandler, endHandler, state,
RequestNotification.UpdateRequestCache , false, this);
}
public void AddOnPostUpdateRequestCacheAsync(BeginEventHandler bh, EndEventHandler eh) {
AddOnPostUpdateRequestCacheAsync(bh, eh, null);
}
public void AddOnPostUpdateRequestCacheAsync(BeginEventHandler beginHandler, EndEventHandler endHandler, Object state) {
AsyncEvents.AddHandler(EventPostUpdateRequestCache, beginHandler, endHandler, state,
RequestNotification.UpdateRequestCache , true, this);
}
public void AddOnLogRequestAsync(BeginEventHandler bh, EndEventHandler eh) {
if (!HttpRuntime.UseIntegratedPipeline) {
throw new PlatformNotSupportedException(SR.GetString(SR.Requires_Iis_Integrated_Mode));
}
AddOnLogRequestAsync(bh, eh, null);
}
public void AddOnLogRequestAsync(BeginEventHandler beginHandler, EndEventHandler endHandler, Object state) {
if (!HttpRuntime.UseIntegratedPipeline) {
throw new PlatformNotSupportedException(SR.GetString(SR.Requires_Iis_Integrated_Mode));
}
AsyncEvents.AddHandler(EventLogRequest, beginHandler, endHandler, state,
RequestNotification.LogRequest, false, this);
}
public void AddOnPostLogRequestAsync(BeginEventHandler bh, EndEventHandler eh) {
if (!HttpRuntime.UseIntegratedPipeline) {
throw new PlatformNotSupportedException(SR.GetString(SR.Requires_Iis_Integrated_Mode));
}
AddOnPostLogRequestAsync(bh, eh, null);
}
public void AddOnPostLogRequestAsync(BeginEventHandler beginHandler, EndEventHandler endHandler, Object state) {
if (!HttpRuntime.UseIntegratedPipeline) {
throw new PlatformNotSupportedException(SR.GetString(SR.Requires_Iis_Integrated_Mode));
}
AsyncEvents.AddHandler(EventPostLogRequest, beginHandler, endHandler, state,
RequestNotification.LogRequest, true, this);
}
public void AddOnEndRequestAsync(BeginEventHandler bh, EndEventHandler eh) {
AddOnEndRequestAsync(bh, eh, null);
}
public void AddOnEndRequestAsync(BeginEventHandler beginHandler, EndEventHandler endHandler, Object state) {
AsyncEvents.AddHandler(EventEndRequest, beginHandler, endHandler, state,
RequestNotification.EndRequest, false, this);
}
//
// Public Application virtual methods
//
/// <devdoc>
/// <para>
/// Used
/// to initialize a HttpModule?s instance variables, and to wireup event handlers to
/// the hosting HttpApplication.
/// </para>
/// </devdoc>
public virtual void Init() {
// derived class implements this
}
/// <devdoc>
/// <para>
/// Used
/// to clean up an HttpModule?s instance variables
/// </para>
/// </devdoc>
public virtual void Dispose() {
// also part of IComponent
// derived class implements this
_site = null;
if (_events != null) {
try {
EventHandler handler = (EventHandler)_events[EventDisposed];
if (handler != null)
handler(this, EventArgs.Empty);
}
finally {
_events.Dispose();
}
}
}
[SecurityPermission(SecurityAction.Assert, ControlPrincipal = true)]
internal static void SetCurrentPrincipalWithAssert(IPrincipal user) {
Thread.CurrentPrincipal = user;
}
[SecurityPermission(SecurityAction.Assert, ControlPrincipal = true)]
internal static WindowsIdentity GetCurrentWindowsIdentityWithAssert() {
return WindowsIdentity.GetCurrent();
}
private HttpHandlerAction GetHandlerMapping(HttpContext context, String requestType, VirtualPath path, bool useAppConfig) {
CachedPathData pathData = null;
HandlerMappingMemo memo = null;
HttpHandlerAction mapping = null;
// Check if cached handler could be used
if (!useAppConfig) {
// Grab mapping from cache - verify that the verb matches exactly
pathData = context.GetPathData(path);
memo = pathData.CachedHandler;
// Invalidate cache on missmatch
if (memo != null && !memo.IsMatch(requestType, path)) {
memo = null;
}
}
// Get new mapping
if (memo == null) {
// Load from config
HttpHandlersSection map = useAppConfig ? RuntimeConfig.GetAppConfig().HttpHandlers
: RuntimeConfig.GetConfig(context).HttpHandlers;
mapping = map.FindMapping(requestType, path);
// Add cache entry
if (!useAppConfig) {
memo = new HandlerMappingMemo(mapping, requestType, path);
pathData.CachedHandler = memo;
}
}
else {
// Get mapping from the cache
mapping = memo.Mapping;
}
return mapping;
}
internal IHttpHandler MapIntegratedHttpHandler(HttpContext context, String requestType, VirtualPath path, String pathTranslated, bool useAppConfig, bool convertNativeStaticFileModule) {
IHttpHandler handler = null;
using (new ApplicationImpersonationContext()) {
string type;
// vpath is a non-relative virtual path
string vpath = path.VirtualPathString;
// If we're using app config, modify vpath by appending the path after the last slash
// to the app's virtual path. This will force IIS IHttpContext::MapHandler to use app configuration.
if (useAppConfig) {
int index = vpath.LastIndexOf('/');
index++;
if (index != 0 && index < vpath.Length) {
vpath = UrlPath.SimpleCombine(HttpRuntime.AppDomainAppVirtualPathString, vpath.Substring(index));
}
else {
vpath = HttpRuntime.AppDomainAppVirtualPathString;
}
}
IIS7WorkerRequest wr = context.WorkerRequest as IIS7WorkerRequest;
type = wr.MapHandlerAndGetHandlerTypeString(method: requestType, path: vpath, convertNativeStaticFileModule: convertNativeStaticFileModule, ignoreWildcardMappings: false);
// If a page developer has removed the default mappings with <handlers><clear>
// without replacing them then we need to give a more descriptive error than
// a null parameter exception.
if (type == null) {
PerfCounters.IncrementCounter(AppPerfCounter.REQUESTS_NOT_FOUND);
PerfCounters.IncrementCounter(AppPerfCounter.REQUESTS_FAILED);
throw new HttpException(SR.GetString(SR.Http_handler_not_found_for_request_type, requestType));
}
// if it's a native type, don't go any further
if(String.IsNullOrEmpty(type)) {
return handler;
}
// Get factory from the mapping
IHttpHandlerFactory factory = GetFactory(type);
try {
handler = factory.GetHandler(context, requestType, path.VirtualPathString, pathTranslated);
}
catch (FileNotFoundException e) {
if (HttpRuntime.HasPathDiscoveryPermission(pathTranslated))
throw new HttpException(404, null, e);
else
throw new HttpException(404, null);
}
catch (DirectoryNotFoundException e) {
if (HttpRuntime.HasPathDiscoveryPermission(pathTranslated))
throw new HttpException(404, null, e);
else
throw new HttpException(404, null);
}
catch (PathTooLongException e) {
if (HttpRuntime.HasPathDiscoveryPermission(pathTranslated))
throw new HttpException(414, null, e);
else
throw new HttpException(414, null);
}
// Remember for recycling
if (_handlerRecycleList == null)
_handlerRecycleList = new ArrayList();
_handlerRecycleList.Add(new HandlerWithFactory(handler, factory));
}
return handler;
}
internal IHttpHandler MapHttpHandler(HttpContext context, String requestType, VirtualPath path, String pathTranslated, bool useAppConfig) {
// Don't use remap handler when HttpServerUtility.Execute called
IHttpHandler handler = (context.ServerExecuteDepth == 0) ? context.RemapHandlerInstance : null;
using (new ApplicationImpersonationContext()) {
// Use remap handler if possible
if (handler != null){
return handler;
}
// Map new handler
HttpHandlerAction mapping = GetHandlerMapping(context, requestType, path, useAppConfig);
// If a page developer has removed the default mappings with <httpHandlers><clear>
// without replacing them then we need to give a more descriptive error than
// a null parameter exception.
if (mapping == null) {
PerfCounters.IncrementCounter(AppPerfCounter.REQUESTS_NOT_FOUND);
PerfCounters.IncrementCounter(AppPerfCounter.REQUESTS_FAILED);
throw new HttpException(SR.GetString(SR.Http_handler_not_found_for_request_type, requestType));
}
// Get factory from the mapping
IHttpHandlerFactory factory = GetFactory(mapping);
// Get factory from the mapping
try {
// Check if it supports the more efficient GetHandler call that can avoid
// a VirtualPath object creation.
IHttpHandlerFactory2 factory2 = factory as IHttpHandlerFactory2;
if (factory2 != null) {
handler = factory2.GetHandler(context, requestType, path, pathTranslated);
}
else {
handler = factory.GetHandler(context, requestType, path.VirtualPathString, pathTranslated);
}
}
catch (FileNotFoundException e) {
if (HttpRuntime.HasPathDiscoveryPermission(pathTranslated))
throw new HttpException(404, null, e);
else
throw new HttpException(404, null);
}
catch (DirectoryNotFoundException e) {
if (HttpRuntime.HasPathDiscoveryPermission(pathTranslated))
throw new HttpException(404, null, e);
else
throw new HttpException(404, null);
}
catch (PathTooLongException e) {
if (HttpRuntime.HasPathDiscoveryPermission(pathTranslated))
throw new HttpException(414, null, e);
else
throw new HttpException(414, null);
}
// Remember for recycling
if (_handlerRecycleList == null)
_handlerRecycleList = new ArrayList();
_handlerRecycleList.Add(new HandlerWithFactory(handler, factory));
}
return handler;
}
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public virtual string GetVaryByCustomString(HttpContext context, string custom) {
if (StringUtil.EqualsIgnoreCase(custom, "browser")) {
return context.Request.Browser.Type;
}
return null;
}
public virtual string GetOutputCacheProviderName(HttpContext context) {
// default implementation
return System.Web.Caching.OutputCache.DefaultProviderName;
}
//
// IComponent implementation
//
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[
Browsable(false),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)
]
public ISite Site {
get { return _site;}
set { _site = value;}
}
//
// IHttpAsyncHandler implementation
//
/// <internalonly/>
IAsyncResult IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, Object extraData) {
HttpAsyncResult result;
// Setup the asynchronous stuff and application variables
_context = context;
_context.ApplicationInstance = this;
_stepManager.InitRequest();
// Make sure the context stays rooted (including all async operations)
_context.Root();
// Create the async result
result = new HttpAsyncResult(cb, extraData);
// Remember the async result for use in async completions
AsyncResult = result;
if (_context.TraceIsEnabled)
HttpRuntime.Profile.StartRequest(_context);
// Start the application
ResumeSteps(null);
// Return the async result
return result;
}
/// <internalonly/>
void IHttpAsyncHandler.EndProcessRequest(IAsyncResult result) {
// throw error caught during execution
HttpAsyncResult ar = (HttpAsyncResult)result;
if (ar.Error != null)
throw ar.Error;
}
//
// IHttpHandler implementation
//
/// <internalonly/>
void IHttpHandler.ProcessRequest(HttpContext context) {
throw new HttpException(SR.GetString(SR.Sync_not_supported));
}
/// <internalonly/>
bool IHttpHandler.IsReusable {
get { return true; }
}
//
// Support for external calls into the application like app_onStart
//
[ReflectionPermission(SecurityAction.Assert, Flags=ReflectionPermissionFlag.RestrictedMemberAccess)]
private void InvokeMethodWithAssert(MethodInfo method, int paramCount, object eventSource, EventArgs eventArgs) {
if (paramCount == 0) {
method.Invoke(this, new Object[0]);
}
else {
Debug.Assert(paramCount == 2);
method.Invoke(this, new Object[2] { eventSource, eventArgs });
}
}
internal void ProcessSpecialRequest(HttpContext context,
MethodInfo method,
int paramCount,
Object eventSource,
EventArgs eventArgs,
HttpSessionState session) {
_context = context;
if (HttpRuntime.UseIntegratedPipeline && _context != null) {
_context.HideRequestResponse = true;
}
_hideRequestResponse = true;
_session = session;
_lastError = null;
using (new DisposableHttpContextWrapper(context)) {
using (new ApplicationImpersonationContext()) {
try {
// set culture on the current thread
SetAppLevelCulture();
InvokeMethodWithAssert(method, paramCount, eventSource, eventArgs);
}
catch (Exception e) {
// dereference reflection invocation exceptions
Exception eActual;
if (e is TargetInvocationException)
eActual = e.InnerException;
else
eActual = e;
RecordError(eActual);
if (context == null) {
try {
WebBaseEvent.RaiseRuntimeError(eActual, this);
}
catch {
}
}
}
finally {
// this thread should not be locking app state
if (_state != null)
_state.EnsureUnLock();
// restore culture
RestoreAppLevelCulture();
if (HttpRuntime.UseIntegratedPipeline && _context != null) {
_context.HideRequestResponse = false;
}
_hideRequestResponse = false;
_context = null;
_session = null;
_lastError = null;
_appEvent = null;
}
}
}
}
//
// Report context-less error
//
internal void RaiseErrorWithoutContext(Exception error) {
try {
try {
SetAppLevelCulture();
_lastError = error;
RaiseOnError();
}
finally {
// this thread should not be locking app state
if (_state != null)
_state.EnsureUnLock();
RestoreAppLevelCulture();
_lastError = null;
_appEvent = null;
}
}
catch { // Protect against exception filters
throw;
}
}
//
//
//
internal void InitInternal(HttpContext context, HttpApplicationState state, MethodInfo[] handlers) {
Debug.Assert(context != null, "context != null");
// Remember state
_state = state;
PerfCounters.IncrementCounter(AppPerfCounter.PIPELINES);
try {
try {
// Remember context for config lookups
_initContext = context;
_initContext.ApplicationInstance = this;
// Set config path to be application path for the application initialization
context.ConfigurationPath = context.Request.ApplicationPathObject;
// keep HttpContext.Current working while running user code
using (new DisposableHttpContextWrapper(context)) {
// Build module list from config
if (HttpRuntime.UseIntegratedPipeline) {
Debug.Assert(_moduleConfigInfo != null, "_moduleConfigInfo != null");
Debug.Assert(_moduleConfigInfo.Count >= 0, "_moduleConfigInfo.Count >= 0");
try {
context.HideRequestResponse = true;
_hideRequestResponse = true;
InitIntegratedModules();
}
finally {
context.HideRequestResponse = false;
_hideRequestResponse = false;
}
}
else {
InitModules();
// this is used exclusively for integrated mode
Debug.Assert(null == _moduleContainers, "null == _moduleContainers");
}
// Hookup event handlers via reflection
if (handlers != null)
HookupEventHandlersForApplicationAndModules(handlers);
// Initialization of the derived class
_context = context;
if (HttpRuntime.UseIntegratedPipeline && _context != null) {
_context.HideRequestResponse = true;
}
_hideRequestResponse = true;
try {
Init();
}
catch (Exception e) {
RecordError(e);
}
}
if (HttpRuntime.UseIntegratedPipeline && _context != null) {
_context.HideRequestResponse = false;
}
_hideRequestResponse = false;
_context = null;
_resumeStepsWaitCallback= new WaitCallback(this.ResumeStepsWaitCallback);
// Construct the execution steps array
if (HttpRuntime.UseIntegratedPipeline) {
_stepManager = new PipelineStepManager(this);
}
else {
_stepManager = new ApplicationStepManager(this);
}
_stepManager.BuildSteps(_resumeStepsWaitCallback);
}
finally {
_initInternalCompleted = true;
// Reset config path
context.ConfigurationPath = null;
// don't hold on to the context
_initContext.ApplicationInstance = null;
_initContext = null;
}
}
catch { // Protect against exception filters
throw;
}
}
// helper to expand an event handler into application steps
private void CreateEventExecutionSteps(Object eventIndex, ArrayList steps) {
// async
AsyncAppEventHandler asyncHandler = AsyncEvents[eventIndex];
if (asyncHandler != null) {
asyncHandler.CreateExecutionSteps(this, steps);
}
// sync
EventHandler handler = (EventHandler)Events[eventIndex];
if (handler != null) {
Delegate[] handlers = handler.GetInvocationList();
for (int i = 0; i < handlers.Length; i++) {
steps.Add(new SyncEventExecutionStep(this, (EventHandler)handlers[i]));
}
}
}
internal void InitSpecial(HttpApplicationState state, MethodInfo[] handlers, IntPtr appContext, HttpContext context) {
// Remember state
_state = state;
try {
// Remember the context for the initialization
if (context != null) {
_initContext = context;
_initContext.ApplicationInstance = this;
}
// if we're doing integrated pipeline wireup, then appContext is non-null and we need to init modules and register event subscriptions with IIS
if (appContext != IntPtr.Zero) {
// 1694356: app_offline.htm and <httpRuntime enabled=/> require that we make this check here for integrated mode
using (new ApplicationImpersonationContext()) {
HttpRuntime.CheckApplicationEnabled();
}
// retrieve app level culture
InitAppLevelCulture();
Debug.Trace("PipelineRuntime", "InitSpecial for " + appContext.ToString() + "\n");
RegisterEventSubscriptionsWithIIS(appContext, context, handlers);
}
else {
// retrieve app level culture
InitAppLevelCulture();
// Hookup event handlers via reflection
if (handlers != null) {
HookupEventHandlersForApplicationAndModules(handlers);
}
}
// if we're doing integrated pipeline wireup, then appContext is non-null and we need to register the application (global.asax) event handlers
if (appContext != IntPtr.Zero) {
if (_appPostNotifications != 0 || _appRequestNotifications != 0) {
RegisterIntegratedEvent(appContext,
HttpApplicationFactory.applicationFileName,
_appRequestNotifications,
_appPostNotifications,
this.GetType().FullName,
MANAGED_PRECONDITION,
false);
}
}
}
finally {
_initSpecialCompleted = true;
// Do not hold on to the context
if (_initContext != null) {
_initContext.ApplicationInstance = null;
_initContext = null;
}
}
}
internal void DisposeInternal() {
PerfCounters.DecrementCounter(AppPerfCounter.PIPELINES);
// call derived class
try {
Dispose();
}
catch (Exception e) {
RecordError(e);
}
// dispose modules
if (_moduleCollection != null) {
int numModules = _moduleCollection.Count;
for (int i = 0; i < numModules; i++) {
try {
// set the init key during Dispose for modules
// that try to unregister events
if (HttpRuntime.UseIntegratedPipeline) {
_currentModuleCollectionKey = _moduleCollection.GetKey(i);
}
_moduleCollection[i].Dispose();
}
catch {
}
}
_moduleCollection = null;
}
// Release buffers
if (_allocator != null) {
_allocator.TrimMemory();
}
}
private void BuildEventMaskDictionary(Dictionary<string, RequestNotification> eventMask) {
eventMask["BeginRequest"] = RequestNotification.BeginRequest;
eventMask["AuthenticateRequest"] = RequestNotification.AuthenticateRequest;
eventMask["PostAuthenticateRequest"] = RequestNotification.AuthenticateRequest;
eventMask["AuthorizeRequest"] = RequestNotification.AuthorizeRequest;
eventMask["PostAuthorizeRequest"] = RequestNotification.AuthorizeRequest;
eventMask["ResolveRequestCache"] = RequestNotification.ResolveRequestCache;
eventMask["PostResolveRequestCache"] = RequestNotification.ResolveRequestCache;
eventMask["MapRequestHandler"] = RequestNotification.MapRequestHandler;
eventMask["PostMapRequestHandler"] = RequestNotification.MapRequestHandler;
eventMask["AcquireRequestState"] = RequestNotification.AcquireRequestState;
eventMask["PostAcquireRequestState"] = RequestNotification.AcquireRequestState;
eventMask["PreRequestHandlerExecute"] = RequestNotification.PreExecuteRequestHandler;
eventMask["PostRequestHandlerExecute"] = RequestNotification.ExecuteRequestHandler;
eventMask["ReleaseRequestState"] = RequestNotification.ReleaseRequestState;
eventMask["PostReleaseRequestState"] = RequestNotification.ReleaseRequestState;
eventMask["UpdateRequestCache"] = RequestNotification.UpdateRequestCache;
eventMask["PostUpdateRequestCache"] = RequestNotification.UpdateRequestCache;
eventMask["LogRequest"] = RequestNotification.LogRequest;
eventMask["PostLogRequest"] = RequestNotification.LogRequest;
eventMask["EndRequest"] = RequestNotification.EndRequest;
eventMask["PreSendRequestHeaders"] = RequestNotification.SendResponse;
eventMask["PreSendRequestContent"] = RequestNotification.SendResponse;
}
private void HookupEventHandlersForApplicationAndModules(MethodInfo[] handlers) {
_currentModuleCollectionKey = HttpApplicationFactory.applicationFileName;
if(null == _pipelineEventMasks) {
Dictionary<string, RequestNotification> dict = new Dictionary<string, RequestNotification>();
BuildEventMaskDictionary(dict);
if(null == _pipelineEventMasks) {
_pipelineEventMasks = dict;
}
}
for (int i = 0; i < handlers.Length; i++) {
MethodInfo appMethod = handlers[i];
String appMethodName = appMethod.Name;
int namePosIndex = appMethodName.IndexOf('_');
String targetName = appMethodName.Substring(0, namePosIndex);
// Find target for method
Object target = null;
if (StringUtil.EqualsIgnoreCase(targetName, "Application"))
target = this;
else if (_moduleCollection != null)
target = _moduleCollection[targetName];
if (target == null)
continue;
// Find event on the module type
Type targetType = target.GetType();
EventDescriptorCollection events = TypeDescriptor.GetEvents(targetType);
string eventName = appMethodName.Substring(namePosIndex+1);
EventDescriptor foundEvent = events.Find(eventName, true);
if (foundEvent == null
&& StringUtil.EqualsIgnoreCase(eventName.Substring(0, 2), "on")) {
eventName = eventName.Substring(2);
foundEvent = events.Find(eventName, true);
}
MethodInfo addMethod = null;
if (foundEvent != null) {
EventInfo reflectionEvent = targetType.GetEvent(foundEvent.Name);
Debug.Assert(reflectionEvent != null);
if (reflectionEvent != null) {
addMethod = reflectionEvent.GetAddMethod();
}
}
if (addMethod == null)
continue;
ParameterInfo[] addMethodParams = addMethod.GetParameters();
if (addMethodParams.Length != 1)
continue;
// Create the delegate from app method to pass to AddXXX(handler) method
Delegate handlerDelegate = null;
ParameterInfo[] appMethodParams = appMethod.GetParameters();
if (appMethodParams.Length == 0) {
// If the app method doesn't have arguments --
// -- hookup via intermidiate handler
// only can do it for EventHandler, not strongly typed
if (addMethodParams[0].ParameterType != typeof(System.EventHandler))
continue;
ArglessEventHandlerProxy proxy = new ArglessEventHandlerProxy(this, appMethod);
handlerDelegate = proxy.Handler;
}
else {
// Hookup directly to the app methods hoping all types match
try {
handlerDelegate = Delegate.CreateDelegate(addMethodParams[0].ParameterType, this, appMethodName);
}
catch {
// some type mismatch
continue;
}
}
// Call the AddXXX() to hook up the delegate
try {
addMethod.Invoke(target, new Object[1]{handlerDelegate});
}
catch {
if (HttpRuntime.UseIntegratedPipeline) {
throw;
}
}
if (eventName != null) {
if (_pipelineEventMasks.ContainsKey(eventName)) {
if (!StringUtil.StringStartsWith(eventName, "Post")) {
_appRequestNotifications |= _pipelineEventMasks[eventName];
}
else {
_appPostNotifications |= _pipelineEventMasks[eventName];
}
}
}
}
}
private void RegisterIntegratedEvent(IntPtr appContext,
string moduleName,
RequestNotification requestNotifications,
RequestNotification postRequestNotifications,
string moduleType,
string modulePrecondition,
bool useHighPriority) {
// lookup the modules event index, if it already exists
// use it, otherwise, bump the global count
// the module is used for event dispatch
int moduleIndex;
if (_moduleIndexMap.ContainsKey(moduleName)) {
moduleIndex = (int) _moduleIndexMap[moduleName];
}
else {
moduleIndex = _moduleIndexMap.Count;
_moduleIndexMap[moduleName] = moduleIndex;
}
#if DBG
Debug.Assert(moduleIndex >= 0, "moduleIndex >= 0");
Debug.Trace("PipelineRuntime", "RegisterIntegratedEvent:"
+ " module=" + moduleName
+ ", index=" + moduleIndex.ToString(CultureInfo.InvariantCulture)
+ ", rq_notify=" + requestNotifications
+ ", post_rq_notify=" + postRequestNotifications
+ ", preconditon=" + modulePrecondition + "\r\n");
#endif
int result = UnsafeIISMethods.MgdRegisterEventSubscription(appContext,
moduleName,
requestNotifications,
postRequestNotifications,
moduleType,
modulePrecondition,
new IntPtr(moduleIndex),
useHighPriority);
if(result < 0) {
throw new HttpException(SR.GetString(SR.Failed_Pipeline_Subscription, moduleName));
}
}
private void SetAppLevelCulture() {
CultureInfo culture = null;
CultureInfo uiculture = null;
CultureInfo browserCulture = null;
//get the language from the browser
//DevDivBugs 2001091: Request object is not available in integrated mode during Application_Start,
//so don't try to access it if it is hidden
if((_appLevelAutoCulture || _appLevelAutoUICulture) && _context != null && _context.HideRequestResponse == false) {
string[] userLanguages = _context.UserLanguagesFromContext();
if (userLanguages != null) {
try { browserCulture = CultureUtil.CreateReadOnlyCulture(userLanguages, requireSpecific: true); }
catch { }
}
}
culture = _appLevelCulture;
uiculture = _appLevelUICulture;
if(browserCulture != null) {
if(_appLevelAutoCulture) {
culture = browserCulture;
}
if(_appLevelAutoUICulture) {
uiculture = browserCulture;
}
}
_savedAppLevelCulture = Thread.CurrentThread.CurrentCulture;
_savedAppLevelUICulture = Thread.CurrentThread.CurrentUICulture;
if (culture != null && culture != Thread.CurrentThread.CurrentCulture) {
HttpRuntime.SetCurrentThreadCultureWithAssert(culture);
}
if (uiculture != null && uiculture != Thread.CurrentThread.CurrentUICulture) {
Thread.CurrentThread.CurrentUICulture = uiculture;
}
}
private void RestoreAppLevelCulture() {
CultureInfo currentCulture = Thread.CurrentThread.CurrentCulture;
CultureInfo currentUICulture = Thread.CurrentThread.CurrentUICulture;
if (_savedAppLevelCulture != null) {
// Avoid the cost of the Demand when setting the culture by comparing the cultures first
if (currentCulture != _savedAppLevelCulture) {
HttpRuntime.SetCurrentThreadCultureWithAssert(_savedAppLevelCulture);
}
_savedAppLevelCulture = null;
}
if (_savedAppLevelUICulture != null) {
// Avoid the cost of the Demand when setting the culture by comparing the cultures first
if (currentUICulture != _savedAppLevelUICulture) {
Thread.CurrentThread.CurrentUICulture = _savedAppLevelUICulture;
}
_savedAppLevelUICulture = null;
}
}
// Initializes the thread on entry to the managed pipeline. A ThreadContext is returned, on
// which the caller must call Leave. The IIS7 integrated pipeline uses setImpersonationContext
// to prevent it from being set until after the authentication notification.
// OnThreadEnterPrivate returns ThreadContext.
// ThreadContext.Enter sets variables that are stored on the thread,
// and saves anything currently on the thread so it can be restored
// during the call to ThreadContext.Leave. All variables that are
// modified on the thread should be stored in ThreadContext so they
// can be restored later. ThreadContext.Enter should only be called
// when holding a lock on the HttpApplication instance.
// ThreadContext.Leave is also normally called under the lock, but
// the Integrated Pipeline may delay this call until after the call to
// IndicateCompletion returns. When IndicateCompletion is called,
// IIS7 will execute the remaining notifications for the request on
// the current thread. As a performance improvement, we do not call
// Leave before calling IndicateCompletion, and we do not call Enter/Leave
// for the notifications executed while we are in the call to
// IndicateCompletion. But when IndicateCompletion returns, we do not
// have a lock on the HttpApplication instance and therefore cannot
// modify request state, such as the HttpContext or HttpApplication.
// The only thing we can do is restore the state of the thread.
// There's one problem, the Culture/UICulture may be changed by
// user code that directly updates the values on the current thread, so
// before leaving the pipeline we call ThreadContext.Synchronize to
// synchronize the values that are stored on the HttpContext with what
// is on the thread. Because of this, the next notification will end up using
// the Culture/UICulture set by user-code, just as it did on IIS6.
private ThreadContext OnThreadEnterPrivate(bool setImpersonationContext) {
ThreadContext threadContext = new ThreadContext(_context);
threadContext.AssociateWithCurrentThread(setImpersonationContext);
// An entry is added to the request timeout manager once per request
// and removed in ReleaseAppInstance.
if (!_timeoutManagerInitialized) {
// ensure Timeout is set (see ASURT 148698)
// to avoid ---- getting config later (ASURT 127388)
_context.EnsureTimeout();
HttpRuntime.RequestTimeoutManager.Add(_context);
_timeoutManagerInitialized = true;
}
return threadContext;
}
// consumed by AppVerifier when it is enabled
HttpContext ISyncContext.HttpContext {
get {
return _context;
}
}
// consumed by AspNetSynchronizationContext
ISyncContextLock ISyncContext.Enter() {
return OnThreadEnter();
}
internal ThreadContext OnThreadEnter() {
return OnThreadEnterPrivate(true /* setImpersonationContext */);
}
internal ThreadContext OnThreadEnter(bool setImpersonationContext) {
return OnThreadEnterPrivate(setImpersonationContext);
}
private StepInvoker _stepInvoker;
public void OnExecuteRequestStep(Action<HttpContextBase, Action> callback) {
if (callback == null) {
throw new ArgumentNullException("callback");
}
if (!HttpRuntime.UseIntegratedPipeline) {
throw new PlatformNotSupportedException(SR.GetString(SR.Requires_Iis_Integrated_Mode));
}
if (_initSpecialCompleted && _initInternalCompleted) {
//throw if both InitSpecial and InitInternal have completed.
throw new InvalidOperationException(SR.GetString(SR.OnExecuteRequestStep_Cannot_Be_Called));
}
if (_stepInvoker == null) {
_stepInvoker = new StepInvoker();
}
var nextStep = _stepInvoker;
_stepInvoker = new StepInvoker((nextStepAction) => callback(new HttpContextWrapper(_context), nextStepAction), nextStep);
}
private void ExecuteStepImpl(IExecutionStep step) {
if(_stepInvoker != null) {
bool stepCalled = false;
_stepInvoker.Invoke(() => {
if (!stepCalled) {
stepCalled = true;
step.Execute();
}
});
if (!stepCalled) {
step.Execute();
}
} else {
step.Execute();
}
}
/*
* Execute single step catching exceptions in a fancy way (see below)
*/
internal Exception ExecuteStep(IExecutionStep step, ref bool completedSynchronously) {
Exception error = null;
try {
try {
if (step.IsCancellable) {
_context.BeginCancellablePeriod(); // request can be cancelled from this point
try {
ExecuteStepImpl(step);
}
finally {
_context.EndCancellablePeriod(); // request can be cancelled until this point
}
_context.WaitForExceptionIfCancelled(); // wait outside of finally
}
else {
ExecuteStepImpl(step);
}
if (!step.CompletedSynchronously) {
completedSynchronously = false;
return null;
}
}
catch (Exception e) {
error = e;
// Since we will leave the context later, we need to remember if we are impersonating
// before we lose that info - VSWhidbey 494476
if (ImpersonationContext.CurrentThreadTokenExists) {
e.Data[System.Web.Management.WebThreadInformation.IsImpersonatingKey] = String.Empty;
}
// This might force ThreadAbortException to be thrown
// automatically, because we consumed an exception that was
// hiding ThreadAbortException behind it
if (e is ThreadAbortException &&
((Thread.CurrentThread.ThreadState & ThreadState.AbortRequested) == 0)) {
// Response.End from a COM+ component that re-throws ThreadAbortException
// It is not a real ThreadAbort
// VSWhidbey 178556
error = null;
_stepManager.CompleteRequest();
}
}
#pragma warning disable 1058
catch {
// ignore non-Exception objects that could be thrown
}
#pragma warning restore 1058
}
catch (ThreadAbortException e) {
// ThreadAbortException could be masked as another one
// the try-catch above consumes all exceptions, only
// ThreadAbortException can filter up here because it gets
// auto rethrown if no other exception is thrown on catch
if (e.ExceptionState != null && e.ExceptionState is CancelModuleException) {
// one of ours (Response.End or timeout) -- cancel abort
CancelModuleException cancelException = (CancelModuleException)e.ExceptionState;
if (cancelException.Timeout) {
// Timed out
error = new HttpException(SR.GetString(SR.Request_timed_out),
null, WebEventCodes.RuntimeErrorRequestAbort);
PerfCounters.IncrementCounter(AppPerfCounter.REQUESTS_TIMED_OUT);
}
else {
// Response.End
error = null;
_stepManager.CompleteRequest();
}
Thread.ResetAbort();
}
}
completedSynchronously = true;
return error;
}
/*
* Resume execution of the app steps
*/
private void ResumeStepsFromThreadPoolThread(Exception error) {
if (Thread.CurrentThread.IsThreadPoolThread) {
// if on thread pool thread, use the current thread
ResumeSteps(error);
}
else {
// if on a non-threadpool thread, requeue
ThreadPool.QueueUserWorkItem(_resumeStepsWaitCallback, error);
}
}
private void ResumeStepsWaitCallback(Object error) {
ResumeSteps(error as Exception);
}
private void ResumeSteps(Exception error) {
_stepManager.ResumeSteps(error);
}
/*
* Add error to the context fire OnError on first error
*/
private void RecordError(Exception error) {
bool firstError = true;
if (_context != null) {
if (_context.Error != null)
firstError = false;
_context.AddError(error);
}
else {
if (_lastError != null)
firstError = false;
_lastError = error;
}
if (firstError)
RaiseOnError();
}
//
// Init module list
//
private void InitModulesCommon() {
int n = _moduleCollection.Count;
for (int i = 0; i < n; i++) {
// remember the module being inited for event subscriptions
// we'll later use this for routing
_currentModuleCollectionKey = _moduleCollection.GetKey(i);
_moduleCollection[i].Init(this);
}
_currentModuleCollectionKey = null;
InitAppLevelCulture();
}
private void InitIntegratedModules() {
Debug.Assert(null != _moduleConfigInfo, "null != _moduleConfigInfo");
_moduleCollection = BuildIntegratedModuleCollection(_moduleConfigInfo);
InitModulesCommon();
}
private void InitModules() {
HttpModulesSection pconfig = RuntimeConfig.GetAppConfig().HttpModules;
// get the static list, then add the dynamic members
HttpModuleCollection moduleCollection = pconfig.CreateModules();
HttpModuleCollection dynamicModules = CreateDynamicModules();
moduleCollection.AppendCollection(dynamicModules);
_moduleCollection = moduleCollection; // don't assign until all ops have succeeded
InitModulesCommon();
}
// instantiates modules that have been added to the dynamic registry (classic pipeline)
private HttpModuleCollection CreateDynamicModules() {
HttpModuleCollection moduleCollection = new HttpModuleCollection();
foreach (DynamicModuleRegistryEntry entry in _dynamicModuleRegistry.LockAndFetchList()) {
HttpModuleAction modAction = new HttpModuleAction(entry.Name, entry.Type);
moduleCollection.AddModule(modAction.Entry.ModuleName, modAction.Entry.Create());
}
return moduleCollection;
}
internal string CurrentModuleCollectionKey {
get {
return (null == _currentModuleCollectionKey) ? "UnknownModule" : _currentModuleCollectionKey;
}
}
internal static void RegisterModuleInternal(Type moduleType) {
_dynamicModuleRegistry.Add(moduleType);
}
public static void RegisterModule(Type moduleType) {
RuntimeConfig config = RuntimeConfig.GetAppConfig();
HttpRuntimeSection runtimeSection = config.HttpRuntime;
if (runtimeSection.AllowDynamicModuleRegistration) {
RegisterModuleInternal(moduleType);
}
else {
throw new InvalidOperationException(SR.GetString(SR.DynamicModuleRegistrationOff));
}
}
private void RegisterEventSubscriptionsWithIIS(IntPtr appContext, HttpContext context, MethodInfo[] handlers) {
RequestNotification requestNotifications;
RequestNotification postRequestNotifications;
// register an implicit filter module
RegisterIntegratedEvent(appContext,
IMPLICIT_FILTER_MODULE,
RequestNotification.UpdateRequestCache| RequestNotification.LogRequest /*requestNotifications*/,
0 /*postRequestNotifications*/,
String.Empty /*type*/,
String.Empty /*precondition*/,
true /*useHighPriority*/);
// integrated pipeline will always use serverModules instead of <httpModules>
_moduleCollection = GetModuleCollection(appContext);
if (handlers != null) {
HookupEventHandlersForApplicationAndModules(handlers);
}
// 1643363: Breaking Change: ASP.Net v2.0: Application_OnStart is called after Module.Init (Integarted mode)
HttpApplicationFactory.EnsureAppStartCalledForIntegratedMode(context, this);
// Call Init on HttpApplication derived class ("global.asax")
// and process event subscriptions before processing other modules.
// Doing this now prevents clearing any events that may
// have been added to event handlers during instantiation of this instance.
// NOTE: If "global.asax" has a constructor which hooks up event handlers,
// then they were added to the event handler lists but have not been registered with IIS yet,
// so we MUST call ProcessEventSubscriptions on it first, before the other modules.
_currentModuleCollectionKey = HttpApplicationFactory.applicationFileName;
try {
_hideRequestResponse = true;
context.HideRequestResponse = true;
_context = context;
Init();
}
catch (Exception e) {
RecordError(e);
Exception error = context.Error;
if (error != null) {
throw error;
}
}
finally {
_context = null;
context.HideRequestResponse = false;
_hideRequestResponse = false;
}
ProcessEventSubscriptions(out requestNotifications, out postRequestNotifications);
// Save the notification subscriptions so we can register them with IIS later, after
// we call HookupEventHandlersForApplicationAndModules and process global.asax event handlers.
_appRequestNotifications |= requestNotifications;
_appPostNotifications |= postRequestNotifications;
for (int i = 0; i < _moduleCollection.Count; i++) {
_currentModuleCollectionKey = _moduleCollection.GetKey(i);
IHttpModule httpModule = _moduleCollection.Get(i);
ModuleConfigurationInfo moduleInfo = _moduleConfigInfo[i];
#if DBG
Debug.Trace("PipelineRuntime", "RegisterEventSubscriptionsWithIIS: name=" + CurrentModuleCollectionKey
+ ", type=" + httpModule.GetType().FullName + "\n");
// make sure collections are in sync
Debug.Assert(moduleInfo.Name == _currentModuleCollectionKey, "moduleInfo.Name == _currentModuleCollectionKey");
#endif
httpModule.Init(this);
ProcessEventSubscriptions(out requestNotifications, out postRequestNotifications);
// are any events wired up?
if (requestNotifications != 0 || postRequestNotifications != 0) {
RegisterIntegratedEvent(appContext,
moduleInfo.Name,
requestNotifications,
postRequestNotifications,
moduleInfo.Type,
moduleInfo.Precondition,
false /*useHighPriority*/);
}
}
// WOS 1728067: RewritePath does not remap the handler when rewriting from a non-ASP.NET request
// register a default implicit handler
RegisterIntegratedEvent(appContext,
IMPLICIT_HANDLER,
RequestNotification.ExecuteRequestHandler | RequestNotification.MapRequestHandler /*requestNotifications*/,
RequestNotification.EndRequest /*postRequestNotifications*/,
String.Empty /*type*/,
String.Empty /*precondition*/,
false /*useHighPriority*/);
}
private void ProcessEventSubscriptions(out RequestNotification requestNotifications,
out RequestNotification postRequestNotifications) {
requestNotifications = 0;
postRequestNotifications = 0;
// Begin
if(HasEventSubscription(EventBeginRequest)) {
requestNotifications |= RequestNotification.BeginRequest;
}
// Authenticate
if(HasEventSubscription(EventAuthenticateRequest)) {
requestNotifications |= RequestNotification.AuthenticateRequest;
}
if(HasEventSubscription(EventPostAuthenticateRequest)) {
postRequestNotifications |= RequestNotification.AuthenticateRequest;
}
// Authorize
if(HasEventSubscription(EventAuthorizeRequest)) {
requestNotifications |= RequestNotification.AuthorizeRequest;
}
if(HasEventSubscription(EventPostAuthorizeRequest)) {
postRequestNotifications |= RequestNotification.AuthorizeRequest;
}
// ResolveRequestCache
if(HasEventSubscription(EventResolveRequestCache)) {
requestNotifications |= RequestNotification.ResolveRequestCache;
}
if(HasEventSubscription(EventPostResolveRequestCache)) {
postRequestNotifications |= RequestNotification.ResolveRequestCache;
}
// MapRequestHandler
if(HasEventSubscription(EventMapRequestHandler)) {
requestNotifications |= RequestNotification.MapRequestHandler;
}
if(HasEventSubscription(EventPostMapRequestHandler)) {
postRequestNotifications |= RequestNotification.MapRequestHandler;
}
// AcquireRequestState
if(HasEventSubscription(EventAcquireRequestState)) {
requestNotifications |= RequestNotification.AcquireRequestState;
}
if(HasEventSubscription(EventPostAcquireRequestState)) {
postRequestNotifications |= RequestNotification.AcquireRequestState;
}
// PreExecuteRequestHandler
if(HasEventSubscription(EventPreRequestHandlerExecute)) {
requestNotifications |= RequestNotification.PreExecuteRequestHandler;
}
// PostRequestHandlerExecute
if (HasEventSubscription(EventPostRequestHandlerExecute)) {
postRequestNotifications |= RequestNotification.ExecuteRequestHandler;
}
// ReleaseRequestState
if(HasEventSubscription(EventReleaseRequestState)) {
requestNotifications |= RequestNotification.ReleaseRequestState;
}
if(HasEventSubscription(EventPostReleaseRequestState)) {
postRequestNotifications |= RequestNotification.ReleaseRequestState;
}
// UpdateRequestCache
if(HasEventSubscription(EventUpdateRequestCache)) {
requestNotifications |= RequestNotification.UpdateRequestCache;
}
if(HasEventSubscription(EventPostUpdateRequestCache)) {
postRequestNotifications |= RequestNotification.UpdateRequestCache;
}
// LogRequest
if(HasEventSubscription(EventLogRequest)) {
requestNotifications |= RequestNotification.LogRequest;
}
if(HasEventSubscription(EventPostLogRequest)) {
postRequestNotifications |= RequestNotification.LogRequest;
}
// EndRequest
if(HasEventSubscription(EventEndRequest)) {
requestNotifications |= RequestNotification.EndRequest;
}
// PreSendRequestHeaders
if(HasEventSubscription(EventPreSendRequestHeaders)) {
requestNotifications |= RequestNotification.SendResponse;
}
// PreSendRequestContent
if(HasEventSubscription(EventPreSendRequestContent)) {
requestNotifications |= RequestNotification.SendResponse;
}
}
// check if an event has subscribers
// and *reset* them if so
// this is used only for special app instances
// and not for processing requests
private bool HasEventSubscription(Object eventIndex) {
bool hasEvents = false;
// async
AsyncAppEventHandler asyncHandler = AsyncEvents[eventIndex];
if (asyncHandler != null && asyncHandler.Count > 0) {
asyncHandler.Reset();
hasEvents = true;
}
// sync
EventHandler handler = (EventHandler)Events[eventIndex];
if (handler != null) {
Delegate[] handlers = handler.GetInvocationList();
if( handlers.Length > 0 ) {
hasEvents = true;
}
foreach(Delegate d in handlers) {
Events.RemoveHandler(eventIndex, d);
}
}
return hasEvents;
}
//
// Get app-level culture info (needed to context-less 'global' methods)
//
private void InitAppLevelCulture() {
GlobalizationSection globConfig = RuntimeConfig.GetAppConfig().Globalization;
string culture = globConfig.Culture;
string uiCulture = globConfig.UICulture;
if (!String.IsNullOrEmpty(culture)) {
if (StringUtil.StringStartsWithIgnoreCase(culture, AutoCulture)) {
_appLevelAutoCulture = true;
string appLevelCulture = GetFallbackCulture(culture);
if(appLevelCulture != null) {
_appLevelCulture = HttpServerUtility.CreateReadOnlyCultureInfo(culture.Substring(5));
}
}
else {
_appLevelAutoCulture = false;
_appLevelCulture = HttpServerUtility.CreateReadOnlyCultureInfo(globConfig.Culture);
}
}
if (!String.IsNullOrEmpty(uiCulture)) {
if (StringUtil.StringStartsWithIgnoreCase(uiCulture, AutoCulture))
{
_appLevelAutoUICulture = true;
string appLevelUICulture = GetFallbackCulture(uiCulture);
if(appLevelUICulture != null) {
_appLevelUICulture = HttpServerUtility.CreateReadOnlyCultureInfo(uiCulture.Substring(5));
}
}
else {
_appLevelAutoUICulture = false;
_appLevelUICulture = HttpServerUtility.CreateReadOnlyCultureInfo(globConfig.UICulture);
}
}
}
internal static string GetFallbackCulture(string culture) {
if((culture.Length > 5) && (culture.IndexOf(':') == 4)) {
return culture.Substring(5);
}
return null;
}
//
// Request mappings management functions
//
private IHttpHandlerFactory GetFactory(HttpHandlerAction mapping) {
HandlerFactoryCache entry = (HandlerFactoryCache)_handlerFactories[mapping.Type];
if (entry == null) {
entry = new HandlerFactoryCache(mapping);
_handlerFactories[mapping.Type] = entry;
}
return entry.Factory;
}
private IHttpHandlerFactory GetFactory(string type) {
HandlerFactoryCache entry = (HandlerFactoryCache)_handlerFactories[type];
if (entry == null) {
entry = new HandlerFactoryCache(type);
_handlerFactories[type] = entry;
}
return entry.Factory;
}
/*
* Recycle all handlers mapped during the request processing
*/
private void RecycleHandlers() {
if (_handlerRecycleList != null) {
int numHandlers = _handlerRecycleList.Count;
for (int i = 0; i < numHandlers; i++)
((HandlerWithFactory)_handlerRecycleList[i]).Recycle();
_handlerRecycleList = null;
}
}
/*
* Special exception to cancel module execution (not really an exception)
* used in Response.End and when cancelling requests
*/
internal class CancelModuleException {
private bool _timeout;
internal CancelModuleException(bool timeout) {
_timeout = timeout;
}
internal bool Timeout { get { return _timeout;}}
}
// Setup the asynchronous stuff and application variables
// context for the entire deal is already rooted for native handler
internal void AssignContext(HttpContext context) {
Debug.Assert(HttpRuntime.UseIntegratedPipeline, "HttpRuntime.UseIntegratedPipeline");
if (null == _context) {
_stepManager.InitRequest();
_context = context;
_context.ApplicationInstance = this;
if (_context.TraceIsEnabled)
HttpRuntime.Profile.StartRequest(_context);
// this will throw if config is invalid, so we do it after HttpContext.ApplicationInstance is set
_context.SetImpersonationEnabled();
}
}
internal IAsyncResult BeginProcessRequestNotification(HttpContext context, AsyncCallback cb) {
Debug.Trace("PipelineRuntime", "BeginProcessRequestNotification");
HttpAsyncResult result;
if (_context == null) {
//
AssignContext(context);
}
//
// everytime initialization
//
context.CurrentModuleEventIndex = -1;
// Create the async result
result = new HttpAsyncResult(cb, context);
context.NotificationContext.AsyncResult = result;
// enter notification execution loop
ResumeSteps(null);
return result;
}
internal RequestNotificationStatus EndProcessRequestNotification(IAsyncResult result) {
HttpAsyncResult ar = (HttpAsyncResult)result;
if (ar.Error != null)
throw ar.Error;
return ar.Status;
}
internal void ReleaseAppInstance() {
if (_context != null)
{
if (_context.TraceIsEnabled) {
HttpRuntime.Profile.EndRequest(_context);
}
_context.ClearReferences();
if (_timeoutManagerInitialized) {
HttpRuntime.RequestTimeoutManager.Remove(_context);
_timeoutManagerInitialized = false;
}
if(HttpRuntime.EnablePrefetchOptimization &&
HttpRuntime.InitializationException == null &&
_context.FirstRequest &&
_context.Error == null) {
UnsafeNativeMethods.EndPrefetchActivity((uint)StringUtil.GetNonRandomizedHashCode(HttpRuntime.AppDomainAppId));
}
}
RecycleHandlers();
if (AsyncResult != null) {
AsyncResult = null;
}
_context = null;
RaiseOnRequestCompleted();
AppEvent = null;
if (ApplicationInstanceConsumersCounter != null) {
ApplicationInstanceConsumersCounter.MarkOperationCompleted(); // ReleaseAppInstance call complete
}
else {
HttpApplicationFactory.RecycleApplicationInstance(this);
}
}
private void AddEventMapping(string moduleName,
RequestNotification requestNotification,
bool isPostNotification,
IExecutionStep step) {
ThrowIfEventBindingDisallowed();
// Add events to the IExecutionStep containers only if
// InitSpecial has completed and InitInternal has not completed.
if (!IsContainerInitalizationAllowed) {
return;
}
Debug.Assert(!String.IsNullOrEmpty(moduleName), "!String.IsNullOrEmpty(moduleName)");
Debug.Trace("PipelineRuntime", "AddEventMapping: for " + moduleName +
" for " + requestNotification + "\r\n" );
PipelineModuleStepContainer container = GetModuleContainer(moduleName);
//WOS 1985878: HttpModule unsubscribing an event handler causes AV in Integrated Mode
if (container != null) {
#if DBG
container.DebugModuleName = moduleName;
#endif
container.AddEvent(requestNotification, isPostNotification, step);
}
}
static internal List<ModuleConfigurationInfo> IntegratedModuleList {
get {
return _moduleConfigInfo;
}
}
private HttpModuleCollection GetModuleCollection(IntPtr appContext) {
if (_moduleConfigInfo != null) {
return BuildIntegratedModuleCollection(_moduleConfigInfo);
}
List<ModuleConfigurationInfo> moduleList = null;
IntPtr pModuleCollection = IntPtr.Zero;
IntPtr pBstrModuleName = IntPtr.Zero;
int cBstrModuleName = 0;
IntPtr pBstrModuleType = IntPtr.Zero;
int cBstrModuleType = 0;
IntPtr pBstrModulePrecondition = IntPtr.Zero;
int cBstrModulePrecondition = 0;
try {
int count = 0;
int result = UnsafeIISMethods.MgdGetModuleCollection(IntPtr.Zero, appContext, out pModuleCollection, out count);
if (result < 0) {
throw new HttpException(SR.GetString(SR.Cant_Read_Native_Modules, result.ToString("X8", CultureInfo.InvariantCulture)));
}
moduleList = new List<ModuleConfigurationInfo>(count);
for (uint index = 0; index < count; index++) {
result = UnsafeIISMethods.MgdGetNextModule(pModuleCollection, ref index,
out pBstrModuleName, out cBstrModuleName,
out pBstrModuleType, out cBstrModuleType,
out pBstrModulePrecondition, out cBstrModulePrecondition);
if (result < 0) {
throw new HttpException(SR.GetString(SR.Cant_Read_Native_Modules, result.ToString("X8", CultureInfo.InvariantCulture)));
}
string moduleName = (cBstrModuleName > 0) ? StringUtil.StringFromWCharPtr(pBstrModuleName, cBstrModuleName) : null;
string moduleType = (cBstrModuleType > 0) ? StringUtil.StringFromWCharPtr(pBstrModuleType, cBstrModuleType) : null;
string modulePrecondition = (cBstrModulePrecondition > 0) ? StringUtil.StringFromWCharPtr(pBstrModulePrecondition, cBstrModulePrecondition) : String.Empty;
Marshal.FreeBSTR(pBstrModuleName);
pBstrModuleName = IntPtr.Zero;
cBstrModuleName = 0;
Marshal.FreeBSTR(pBstrModuleType);
pBstrModuleType = IntPtr.Zero;
cBstrModuleType = 0;
Marshal.FreeBSTR(pBstrModulePrecondition);
pBstrModulePrecondition = IntPtr.Zero;
cBstrModulePrecondition = 0;
if (!String.IsNullOrEmpty(moduleName) && !String.IsNullOrEmpty(moduleType)) {
moduleList.Add(new ModuleConfigurationInfo(moduleName, moduleType, modulePrecondition));
}
}
}
finally {
if (pModuleCollection != IntPtr.Zero) {
Marshal.Release(pModuleCollection);
pModuleCollection = IntPtr.Zero;
}
if (pBstrModuleName != IntPtr.Zero) {
Marshal.FreeBSTR(pBstrModuleName);
pBstrModuleName = IntPtr.Zero;
}
if (pBstrModuleType != IntPtr.Zero) {
Marshal.FreeBSTR(pBstrModuleType);
pBstrModuleType = IntPtr.Zero;
}
if (pBstrModulePrecondition != IntPtr.Zero) {
Marshal.FreeBSTR(pBstrModulePrecondition);
pBstrModulePrecondition = IntPtr.Zero;
}
}
// now that the static list has been processed, add in the dynamic module list
moduleList.AddRange(GetConfigInfoForDynamicModules());
_moduleConfigInfo = moduleList;
return BuildIntegratedModuleCollection(_moduleConfigInfo);
}
// gets configuration for modules that have been added to the dynamic registry (integrated pipeline)
private IEnumerable<ModuleConfigurationInfo> GetConfigInfoForDynamicModules() {
return from entry in _dynamicModuleRegistry.LockAndFetchList()
select new ModuleConfigurationInfo(entry.Name, entry.Type, "managedHandler" /* condition */);
}
HttpModuleCollection BuildIntegratedModuleCollection(List<ModuleConfigurationInfo> moduleList) {
HttpModuleCollection modules = new HttpModuleCollection();
foreach(ModuleConfigurationInfo mod in moduleList) {
#if DBG
Debug.Trace("NativeConfig", "Runtime module: " + mod.Name + " of type " + mod.Type + "\n");
#endif
ModulesEntry currentModule = new ModulesEntry(mod.Name, mod.Type, "type", null);
modules.AddModule(currentModule.ModuleName, currentModule.Create());
}
return modules;
}
//
// Internal classes to support [asynchronous] app execution logic
//
internal class AsyncAppEventHandler {
int _count;
ArrayList _beginHandlers;
ArrayList _endHandlers;
ArrayList _stateObjects;
internal AsyncAppEventHandler() {
_count = 0;
_beginHandlers = new ArrayList();
_endHandlers = new ArrayList();
_stateObjects = new ArrayList();
}
internal void Reset() {
_count = 0;
_beginHandlers.Clear();
_endHandlers.Clear();
_stateObjects.Clear();
}
internal int Count {
get {
return _count;
}
}
internal void Add(BeginEventHandler beginHandler, EndEventHandler endHandler, Object state) {
_beginHandlers.Add(beginHandler);
_endHandlers.Add(endHandler);
_stateObjects.Add(state);
_count++;
}
internal void CreateExecutionSteps(HttpApplication app, ArrayList steps) {
for (int i = 0; i < _count; i++) {
steps.Add(new AsyncEventExecutionStep(
app,
(BeginEventHandler)_beginHandlers[i],
(EndEventHandler)_endHandlers[i],
_stateObjects[i]));
}
}
}
internal class AsyncAppEventHandlersTable {
private Hashtable _table;
internal void AddHandler(Object eventId, BeginEventHandler beginHandler,
EndEventHandler endHandler, Object state,
RequestNotification requestNotification,
bool isPost, HttpApplication app) {
if (_table == null)
_table = new Hashtable();
AsyncAppEventHandler asyncHandler = (AsyncAppEventHandler)_table[eventId];
if (asyncHandler == null) {
asyncHandler = new AsyncAppEventHandler();
_table[eventId] = asyncHandler;
}
asyncHandler.Add(beginHandler, endHandler, state);
if (HttpRuntime.UseIntegratedPipeline) {
AsyncEventExecutionStep step =
new AsyncEventExecutionStep(app,
beginHandler,
endHandler,
state);
app.AddEventMapping(app.CurrentModuleCollectionKey, requestNotification, isPost, step);
}
}
internal AsyncAppEventHandler this[Object eventId] {
get {
if (_table == null)
return null;
return (AsyncAppEventHandler)_table[eventId];
}
}
}
// interface to represent one execution step
internal interface IExecutionStep {
void Execute();
bool CompletedSynchronously { get;}
bool IsCancellable { get; }
}
// execution step -- stub
internal class NoopExecutionStep : IExecutionStep {
internal NoopExecutionStep() {
}
void IExecutionStep.Execute() {
}
bool IExecutionStep.CompletedSynchronously {
get { return true;}
}
bool IExecutionStep.IsCancellable {
get { return false; }
}
}
// execution step -- call synchronous event
internal class SyncEventExecutionStep : IExecutionStep {
private HttpApplication _application;
private EventHandler _handler;
internal SyncEventExecutionStep(HttpApplication app, EventHandler handler) {
_application = app;
_handler = handler;
}
internal EventHandler Handler {
get {
return _handler;
}
}
void IExecutionStep.Execute() {
string targetTypeStr = null;
if (_handler != null) {
if (EtwTrace.IsTraceEnabled(EtwTraceLevel.Verbose, EtwTraceFlags.Module)) {
targetTypeStr = _handler.Method.ReflectedType.ToString();
EtwTrace.Trace(EtwTraceType.ETW_TYPE_PIPELINE_ENTER, _application.Context.WorkerRequest, targetTypeStr);
}
_handler(_application, _application.AppEvent);
if (EtwTrace.IsTraceEnabled(EtwTraceLevel.Verbose, EtwTraceFlags.Module)) EtwTrace.Trace(EtwTraceType.ETW_TYPE_PIPELINE_LEAVE, _application.Context.WorkerRequest, targetTypeStr);
}
}
bool IExecutionStep.CompletedSynchronously {
get { return true;}
}
bool IExecutionStep.IsCancellable {
get { return true; }
}
}
// execution step -- call asynchronous event
internal class AsyncEventExecutionStep : IExecutionStep {
private HttpApplication _application;
private BeginEventHandler _beginHandler;
private EndEventHandler _endHandler;
private Object _state;
private AsyncCallback _completionCallback;
private AsyncStepCompletionInfo _asyncStepCompletionInfo; // per call
private bool _sync; // per call
private string _targetTypeStr;
internal AsyncEventExecutionStep(HttpApplication app, BeginEventHandler beginHandler, EndEventHandler endHandler, Object state)
:this(app, beginHandler, endHandler, state, HttpRuntime.UseIntegratedPipeline)
{
}
internal AsyncEventExecutionStep(HttpApplication app, BeginEventHandler beginHandler, EndEventHandler endHandler, Object state, bool useIntegratedPipeline) {
_application = app;
// Instrument the beginHandler method if AppVerifier is enabled.
// If AppVerifier not enabled, we just get back the original delegate to beginHandler uninstrumented.
_beginHandler = AppVerifier.WrapBeginMethod(_application, beginHandler);
_endHandler = endHandler;
_state = state;
_completionCallback = new AsyncCallback(this.OnAsyncEventCompletion);
}
private void OnAsyncEventCompletion(IAsyncResult ar) {
if (ar.CompletedSynchronously) {
// Synchronous completions will be handled by IExecutionStep.Execute.
return;
}
// This IAsyncResult may actually have completed synchronously (we might be on the same thread
// which called IExecutionStep.Execute) even if CompletedSynchronously = false. Regardless,
// we should invoke the End* method on the same thread that invoked this callback, as some
// applications use TLS instead of the IAsyncResult object itself to convey state information.
Debug.Trace("PipelineRuntime", "AsyncStep.OnAsyncEventCompletion");
HttpContext context = _application.Context;
Exception error = null;
// The asynchronous step has completed, so we should disallow further
// async operations until the next step.
context.SyncContext.ProhibitVoidAsyncOperations();
try {
InvokeEndHandler(ar);
} catch (Exception e) {
error = e;
}
bool shouldCallResumeSteps = _asyncStepCompletionInfo.RegisterAsyncCompletion(error);
if (!shouldCallResumeSteps) {
return;
}
if (EtwTrace.IsTraceEnabled(EtwTraceLevel.Verbose, EtwTraceFlags.Module)) EtwTrace.Trace(EtwTraceType.ETW_TYPE_PIPELINE_LEAVE, context.WorkerRequest, _targetTypeStr);
// re-set start time after an async completion (see VSWhidbey 231010)
context.SetStartTime();
// Assert to disregard the user code up the stack
if (HttpRuntime.IsLegacyCas) {
ResumeStepsWithAssert(error);
}
else {
ResumeSteps(error);
}
}
private void InvokeEndHandler(IAsyncResult ar) {
if (_application._stepInvoker != null) {
bool stepCalled = false;
_application._stepInvoker.Invoke(() => {
if (!stepCalled) {
stepCalled = true;
_endHandler(ar);
}
});
if (!stepCalled) {
_endHandler(ar);
}
} else {
_endHandler(ar);
}
}
[PermissionSet(SecurityAction.Assert, Unrestricted = true)]
void ResumeStepsWithAssert(Exception error) {
ResumeSteps(error);
}
void ResumeSteps(Exception error) {
_application.ResumeStepsFromThreadPoolThread(error);
}
void IExecutionStep.Execute() {
_sync = false;
if (EtwTrace.IsTraceEnabled(EtwTraceLevel.Verbose, EtwTraceFlags.Module)) {
_targetTypeStr = _beginHandler.Method.ReflectedType.ToString();
EtwTrace.Trace(EtwTraceType.ETW_TYPE_PIPELINE_ENTER, _application.Context.WorkerRequest, _targetTypeStr);
}
HttpContext context = _application.Context;
_asyncStepCompletionInfo.Reset();
context.SyncContext.AllowVoidAsyncOperations();
IAsyncResult ar;
try {
ar = _beginHandler(_application, _application.AppEvent, _completionCallback, _state);
}
catch {
// The asynchronous step has completed, so we should disallow further
// async operations until the next step.
context.SyncContext.ProhibitVoidAsyncOperations();
throw;
}
bool operationCompleted;
bool mustCallEndHandler;
_asyncStepCompletionInfo.RegisterBeginUnwound(ar, out operationCompleted, out mustCallEndHandler);
if (operationCompleted) {
_sync = true;
if (mustCallEndHandler) {
// The asynchronous step has completed, so we should disallow further
// async operations until the next step.
context.SyncContext.ProhibitVoidAsyncOperations();
_endHandler(ar);
}
_asyncStepCompletionInfo.ReportError();
if (EtwTrace.IsTraceEnabled(EtwTraceLevel.Verbose, EtwTraceFlags.Module)) EtwTrace.Trace(EtwTraceType.ETW_TYPE_PIPELINE_LEAVE, _application.Context.WorkerRequest, _targetTypeStr);
}
}
bool IExecutionStep.CompletedSynchronously {
get { return _sync;}
}
bool IExecutionStep.IsCancellable {
get { return false; }
}
}
// execution step -- validate the path for canonicalization issues
internal class ValidatePathExecutionStep : IExecutionStep {
private HttpApplication _application;
internal ValidatePathExecutionStep(HttpApplication app) {
_application = app;
}
void IExecutionStep.Execute() {
_application.Context.ValidatePath();
}
bool IExecutionStep.CompletedSynchronously {
get { return true; }
}
bool IExecutionStep.IsCancellable {
get { return false; }
}
}
// execution step -- validate request (virtual path, query string, entity body, etc)
internal class ValidateRequestExecutionStep : IExecutionStep {
private HttpApplication _application;
internal ValidateRequestExecutionStep(HttpApplication app) {
_application = app;
}
void IExecutionStep.Execute() {
_application.Context.Request.ValidateInputIfRequiredByConfig();
}
bool IExecutionStep.CompletedSynchronously {
get { return true; }
}
bool IExecutionStep.IsCancellable {
get { return false; }
}
}
// materialize handler for integrated pipeline
// this does not map handler, rather that's done by the core
// this does instantiate the managed type so that things that need to
// look at it can
internal class MaterializeHandlerExecutionStep : IExecutionStep {
private HttpApplication _application;
internal MaterializeHandlerExecutionStep(HttpApplication app) {
_application = app;
}
void IExecutionStep.Execute() {
HttpContext context = _application.Context;
HttpRequest request = context.Request;
IHttpHandler handler = null;
string configType = null;
if (EtwTrace.IsTraceEnabled(EtwTraceLevel.Verbose, EtwTraceFlags.Infrastructure)) EtwTrace.Trace(EtwTraceType.ETW_TYPE_MAPHANDLER_ENTER, context.WorkerRequest);
IIS7WorkerRequest wr = context.WorkerRequest as IIS7WorkerRequest;
// Get handler
if (context.RemapHandlerInstance != null){
//RemapHandler overrides all
wr.SetScriptMapForRemapHandler();
context.Handler = context.RemapHandlerInstance;
}
else if (request.RewrittenUrl != null) {
// RewritePath, we need to re-map the handler
bool handlerExists;
configType = wr.ReMapHandlerAndGetHandlerTypeString(context, request.Path, out handlerExists);
if (!handlerExists) {
// WOS 1973590: When RewritePath is used with missing handler in Integrated Mode,an empty response 200 is returned instead of 404
throw new HttpException(404, SR.GetString(SR.Http_handler_not_found_for_request_type, request.RequestType));
}
}
else {
configType = wr.GetManagedHandlerType();
}
if (!String.IsNullOrEmpty(configType)) {
IHttpHandlerFactory factory = _application.GetFactory(configType);
string pathTranslated = request.PhysicalPathInternal;
try {
handler = factory.GetHandler(context, request.RequestType, request.FilePath, pathTranslated);
}
catch (FileNotFoundException e) {
if (HttpRuntime.HasPathDiscoveryPermission(pathTranslated))
throw new HttpException(404, null, e);
else
throw new HttpException(404, null);
}
catch (DirectoryNotFoundException e) {
if (HttpRuntime.HasPathDiscoveryPermission(pathTranslated))
throw new HttpException(404, null, e);
else
throw new HttpException(404, null);
}
catch (PathTooLongException e) {
if (HttpRuntime.HasPathDiscoveryPermission(pathTranslated))
throw new HttpException(414, null, e);
else
throw new HttpException(414, null);
}
context.Handler = handler;
// Remember for recycling
if (_application._handlerRecycleList == null)
_application._handlerRecycleList = new ArrayList();
_application._handlerRecycleList.Add(new HandlerWithFactory(handler, factory));
}
if (EtwTrace.IsTraceEnabled(EtwTraceLevel.Verbose, EtwTraceFlags.Infrastructure)) EtwTrace.Trace(EtwTraceType.ETW_TYPE_MAPHANDLER_LEAVE, context.WorkerRequest);
}
bool IExecutionStep.CompletedSynchronously {
get { return true;}
}
bool IExecutionStep.IsCancellable {
get { return false; }
}
}
// execution step -- map HTTP handler (used to be a separate module)
internal class MapHandlerExecutionStep : IExecutionStep {
private HttpApplication _application;
internal MapHandlerExecutionStep(HttpApplication app) {
_application = app;
}
void IExecutionStep.Execute() {
HttpContext context = _application.Context;
HttpRequest request = context.Request;
if (EtwTrace.IsTraceEnabled(EtwTraceLevel.Verbose, EtwTraceFlags.Infrastructure)) EtwTrace.Trace(EtwTraceType.ETW_TYPE_MAPHANDLER_ENTER, context.WorkerRequest);
context.Handler = _application.MapHttpHandler(
context,
request.RequestType,
request.FilePathObject,
request.PhysicalPathInternal,
false /*useAppConfig*/);
Debug.Assert(context.ConfigurationPath == context.Request.FilePathObject, "context.ConfigurationPath (" +
context.ConfigurationPath + ") != context.Request.FilePath (" + context.Request.FilePath + ")");
if (EtwTrace.IsTraceEnabled(EtwTraceLevel.Verbose, EtwTraceFlags.Infrastructure)) EtwTrace.Trace(EtwTraceType.ETW_TYPE_MAPHANDLER_LEAVE, context.WorkerRequest);
}
bool IExecutionStep.CompletedSynchronously {
get { return true;}
}
bool IExecutionStep.IsCancellable {
get { return false; }
}
}
// execution step -- call HTTP handler (used to be a separate module)
internal class CallHandlerExecutionStep : IExecutionStep {
private HttpApplication _application;
private AsyncCallback _completionCallback;
private IHttpAsyncHandler _handler; // per call
private AsyncStepCompletionInfo _asyncStepCompletionInfo; // per call
private bool _sync; // per call
internal CallHandlerExecutionStep(HttpApplication app) {
_application = app;
_completionCallback = new AsyncCallback(this.OnAsyncHandlerCompletion);
}
private void OnAsyncHandlerCompletion(IAsyncResult ar) {
if (ar.CompletedSynchronously) {
// Synchronous completions will be handled by IExecutionStep.Execute.
return;
}
// This IAsyncResult may actually have completed synchronously (we might be on the same thread
// which called IExecutionStep.Execute) even if CompletedSynchronously = false. Regardless,
// we should invoke the End* method on the same thread that invoked this callback, as some
// applications use TLS instead of the IAsyncResult object itself to convey state information.
HttpContext context = _application.Context;
Exception error = null;
// The asynchronous step has completed, so we should disallow further
// async operations until the next step.
context.SyncContext.ProhibitVoidAsyncOperations();
try {
try {
InvokeEndHandler(ar);
} finally {
SuppressPostEndRequestIfNecessary(context);
// In Integrated mode, generate the necessary response headers
// after the ASP.NET handler runs. If EndProcessRequest throws,
// the headers will be generated by ReportRuntimeError
context.Response.GenerateResponseHeadersForHandler();
}
}
catch (Exception e) {
if (e is ThreadAbortException || e.InnerException != null && e.InnerException is ThreadAbortException) {
// Response.End happened during async operation
_application.CompleteRequest();
}
else {
error = e;
}
}
bool shouldCallResumeSteps = _asyncStepCompletionInfo.RegisterAsyncCompletion(error);
if (!shouldCallResumeSteps) {
return;
}
if (EtwTrace.IsTraceEnabled(EtwTraceLevel.Information, EtwTraceFlags.Page)) EtwTrace.Trace(EtwTraceType.ETW_TYPE_HTTPHANDLER_LEAVE, context.WorkerRequest);
_handler = null; // not to remember
// re-set start time after an async completion (see VSWhidbey 231010)
context.SetStartTime();
// Assert to disregard the user code up the stack
if (HttpRuntime.IsLegacyCas) {
ResumeStepsWithAssert(error);
}
else {
ResumeSteps(error);
}
}
private void InvokeEndHandler(IAsyncResult ar) {
if (_application._stepInvoker != null) {
bool stepCalled = false;
_application._stepInvoker.Invoke(() => {
if (!stepCalled) {
stepCalled = true;
_handler.EndProcessRequest(ar);
}
});
if (!stepCalled) {
_handler.EndProcessRequest(ar);
}
} else {
_handler.EndProcessRequest(ar);
}
}
[PermissionSet(SecurityAction.Assert, Unrestricted = true)]
void ResumeStepsWithAssert(Exception error) {
ResumeSteps(error);
}
void ResumeSteps(Exception error) {
_application.ResumeStepsFromThreadPoolThread(error);
}
private static void SuppressPostEndRequestIfNecessary(HttpContext context) {
// DevDiv #245124 - ASP.NET now hooks PostEndRequest in order to kick off the WebSocket pipeline.
// If this is not a WebSocket request or the handshake was not completed, then we can suppress
// this pipeline event. This allows us to send the appropriate cache headers to the client,
// and it also gives a small perf boost.
if (!context.IsWebSocketRequestUpgrading) {
IIS7WorkerRequest wr = context.WorkerRequest as IIS7WorkerRequest;
if (wr != null) {
wr.DisableNotifications(notifications: 0, postNotifications: RequestNotification.EndRequest);
}
}
}
void IExecutionStep.Execute() {
HttpContext context = _application.Context;
IHttpHandler handler = context.Handler;
if (EtwTrace.IsTraceEnabled(EtwTraceLevel.Information, EtwTraceFlags.Page)) EtwTrace.Trace(EtwTraceType.ETW_TYPE_HTTPHANDLER_ENTER, context.WorkerRequest);
if (handler != null && HttpRuntime.UseIntegratedPipeline) {
IIS7WorkerRequest wr = context.WorkerRequest as IIS7WorkerRequest;
if (wr != null && wr.IsHandlerExecutionDenied()) {
_sync = true;
HttpException error = new HttpException(403, SR.GetString(SR.Handler_access_denied));
error.SetFormatter(new PageForbiddenErrorFormatter(context.Request.Path, SR.GetString(SR.Handler_access_denied)));
throw error;
}
}
if (handler == null) {
_sync = true;
}
else if (handler is IHttpAsyncHandler) {
// asynchronous handler
IHttpAsyncHandler asyncHandler = (IHttpAsyncHandler)handler;
_sync = false;
_handler = asyncHandler;
// Instrument the BeginProcessRequest method if AppVerifier is enabled.
// If AppVerifier not enabled, we just get back the original delegate to BeginProcessRequest uninstrumented.
var beginProcessRequestDelegate = AppVerifier.WrapBeginMethod<HttpContext>(_application, asyncHandler.BeginProcessRequest);
_asyncStepCompletionInfo.Reset();
context.SyncContext.AllowVoidAsyncOperations();
IAsyncResult ar;
try {
ar = beginProcessRequestDelegate(context, _completionCallback, null);
}
catch {
// The asynchronous step has completed, so we should disallow further
// async operations until the next step.
context.SyncContext.ProhibitVoidAsyncOperations();
throw;
}
bool operationCompleted;
bool mustCallEndHandler;
_asyncStepCompletionInfo.RegisterBeginUnwound(ar, out operationCompleted, out mustCallEndHandler);
if (operationCompleted) {
_sync = true;
_handler = null; // not to remember
// The asynchronous step has completed, so we should disallow further
// async operations until the next step.
context.SyncContext.ProhibitVoidAsyncOperations();
try {
if (mustCallEndHandler) {
asyncHandler.EndProcessRequest(ar);
}
_asyncStepCompletionInfo.ReportError();
}
finally {
SuppressPostEndRequestIfNecessary(context);
// In Integrated mode, generate the necessary response headers
// after the ASP.NET handler runs
context.Response.GenerateResponseHeadersForHandler();
}
if (EtwTrace.IsTraceEnabled(EtwTraceLevel.Information, EtwTraceFlags.Page)) EtwTrace.Trace(EtwTraceType.ETW_TYPE_HTTPHANDLER_LEAVE, context.WorkerRequest);
}
}
else {
// synchronous handler
_sync = true;
// disable async operations
//_application.SyncContext.Disable();
// VSWhidbey 268772 - If a synchronous handler internally kicks off an asynchronous operation and waits (blocking) for that
// operation to complete, the handler will deadlock since the asynchronous operation can't come back to the appropriate
// thread to perform the completion. The solution below was only meant to be temporary but was accidentally left in the product
// for v2.0 RTM, so it's now legacy behavior and cannot be changed.
context.SyncContext.SetSyncCaller();
try {
handler.ProcessRequest(context);
}
finally {
context.SyncContext.ResetSyncCaller();
if (EtwTrace.IsTraceEnabled(EtwTraceLevel.Information, EtwTraceFlags.Page)) EtwTrace.Trace(EtwTraceType.ETW_TYPE_HTTPHANDLER_LEAVE, context.WorkerRequest);
SuppressPostEndRequestIfNecessary(context);
// In Integrated mode, generate the necessary response headers
// after the ASP.NET handler runs
context.Response.GenerateResponseHeadersForHandler();
}
}
}
bool IExecutionStep.CompletedSynchronously {
get { return _sync;}
}
bool IExecutionStep.IsCancellable {
// launching of async handler should not be cancellable
get { return (_application.Context.Handler is IHttpAsyncHandler) ? false : true; }
}
}
// execution step -- initiate the transition to a WebSocket request
internal class TransitionToWebSocketsExecutionStep : IExecutionStep {
private readonly HttpApplication _application;
internal TransitionToWebSocketsExecutionStep(HttpApplication app) {
_application = app;
}
void IExecutionStep.Execute() {
HttpContext context = _application.Context;
if (context.RootedObjects == null
|| context.RootedObjects.WebSocketPipeline == null
|| context.Response.StatusCode != (int)HttpStatusCode.SwitchingProtocols) {
// If this isn't a WebSocket request or something has caused the status code
// not to be HTTP 101 (such as an error, redirect, or something else), no-op.
CompletedSynchronously = true;
}
else {
// DevDiv #273639: Let the HttpRequest instance maintain a reference to the response
// cookie collection, as the HttpResponse instance won't be available after the transition.
context.Request.StoreReferenceToResponseCookies(context.Response.GetCookiesNoCreate());
// If this is a WebSocket request, mark as transitioned so that asynchronous events (like SendRequest)
// don't execute. We also need to mark ourselves as not having completed synchronously so that the
// pipeline unwinds back to ProcessRequestNotification. That method special-cases WebSocket handlers
// and cleans up the HttpContext / HttpApplication eagerly.
// transition: AcceptWebSocketRequestCalled -> TransitionStarted
context.TransitionToWebSocketState(WebSocketTransitionState.TransitionStarted);
CompletedSynchronously = false;
}
}
public bool CompletedSynchronously {
get;
private set;
}
public bool IsCancellable {
// launching of async operation should not be cancellable
get { return false; }
}
}
// execution step -- call response filter
internal class CallFilterExecutionStep : IExecutionStep {
private HttpApplication _application;
internal CallFilterExecutionStep(HttpApplication app) {
_application = app;
}
void IExecutionStep.Execute() {
try {
_application.Context.Response.FilterOutput();
}
finally {
// if this is the UpdateCache notification, then disable the LogRequest notification (which handles the error case)
if (HttpRuntime.UseIntegratedPipeline && (_application.Context.CurrentNotification == RequestNotification.UpdateRequestCache)) {
_application.Context.DisableNotifications(RequestNotification.LogRequest, 0 /*postNotifications*/);
}
}
}
bool IExecutionStep.CompletedSynchronously {
get { return true;}
}
bool IExecutionStep.IsCancellable {
get { return true; }
}
}
// integrated pipeline execution step for RaiseOnPreSendRequestHeaders and RaiseOnPreSendRequestContent
internal class SendResponseExecutionStep : IExecutionStep {
private HttpApplication _application;
private EventHandler _handler;
private bool _isHeaders;
internal SendResponseExecutionStep(HttpApplication app, EventHandler handler, bool isHeaders) {
_application = app;
_handler = handler;
_isHeaders = isHeaders;
}
void IExecutionStep.Execute() {
// IIS only has a SendResponse notification, so we check the flags
// to determine whether this notification is for headers or content.
// The step uses _isHeaders to keep track of whether this is for headers or content.
if (_application.Context.IsSendResponseHeaders && _isHeaders
|| !_isHeaders) {
string targetTypeStr = null;
if (_handler != null) {
if (EtwTrace.IsTraceEnabled(EtwTraceLevel.Verbose, EtwTraceFlags.Module)) {
targetTypeStr = _handler.Method.ReflectedType.ToString();
EtwTrace.Trace(EtwTraceType.ETW_TYPE_PIPELINE_ENTER, _application.Context.WorkerRequest, targetTypeStr);
}
_handler(_application, _application.AppEvent);
if (EtwTrace.IsTraceEnabled(EtwTraceLevel.Verbose, EtwTraceFlags.Module)) EtwTrace.Trace(EtwTraceType.ETW_TYPE_PIPELINE_LEAVE, _application.Context.WorkerRequest, targetTypeStr);
}
}
}
bool IExecutionStep.CompletedSynchronously {
get { return true;}
}
bool IExecutionStep.IsCancellable {
get { return true; }
}
}
internal class UrlMappingsExecutionStep : IExecutionStep {
private HttpApplication _application;
internal UrlMappingsExecutionStep(HttpApplication app) {
_application = app;
}
void IExecutionStep.Execute() {
HttpContext context = _application.Context;
UrlMappingsModule.UrlMappingRewritePath(context);
}
bool IExecutionStep.CompletedSynchronously {
get { return true;}
}
bool IExecutionStep.IsCancellable {
get { return false; }
}
}
internal abstract class StepManager {
protected HttpApplication _application;
protected bool _requestCompleted;
internal StepManager(HttpApplication application) {
_application = application;
}
internal bool IsCompleted { get { return _requestCompleted; } }
internal abstract void BuildSteps(WaitCallback stepCallback);
internal void CompleteRequest() {
_requestCompleted = true;
if (HttpRuntime.UseIntegratedPipeline) {
HttpContext context = _application.Context;
if (context != null && context.NotificationContext != null) {
context.NotificationContext.RequestCompleted = true;
}
}
}
internal abstract void InitRequest();
internal abstract void ResumeSteps(Exception error);
}
internal class ApplicationStepManager : StepManager {
private IExecutionStep[] _execSteps;
private WaitCallback _resumeStepsWaitCallback;
private int _currentStepIndex;
private int _numStepCalls;
private int _numSyncStepCalls;
private int _endRequestStepIndex;
internal ApplicationStepManager(HttpApplication app): base(app) {
}
internal override void BuildSteps(WaitCallback stepCallback ) {
ArrayList steps = new ArrayList();
HttpApplication app = _application;
bool urlMappingsEnabled = false;
UrlMappingsSection urlMappings = RuntimeConfig.GetConfig().UrlMappings;
urlMappingsEnabled = urlMappings.IsEnabled && ( urlMappings.UrlMappings.Count > 0 );
steps.Add(new ValidateRequestExecutionStep(app));
steps.Add(new ValidatePathExecutionStep(app));
if (urlMappingsEnabled)
steps.Add(new UrlMappingsExecutionStep(app)); // url mappings
app.CreateEventExecutionSteps(HttpApplication.EventBeginRequest, steps);
app.CreateEventExecutionSteps(HttpApplication.EventAuthenticateRequest, steps);
app.CreateEventExecutionSteps(HttpApplication.EventDefaultAuthentication, steps);
app.CreateEventExecutionSteps(HttpApplication.EventPostAuthenticateRequest, steps);
app.CreateEventExecutionSteps(HttpApplication.EventAuthorizeRequest, steps);
app.CreateEventExecutionSteps(HttpApplication.EventPostAuthorizeRequest, steps);
app.CreateEventExecutionSteps(HttpApplication.EventResolveRequestCache, steps);
app.CreateEventExecutionSteps(HttpApplication.EventPostResolveRequestCache, steps);
steps.Add(new MapHandlerExecutionStep(app)); // map handler
app.CreateEventExecutionSteps(HttpApplication.EventPostMapRequestHandler, steps);
app.CreateEventExecutionSteps(HttpApplication.EventAcquireRequestState, steps);
app.CreateEventExecutionSteps(HttpApplication.EventPostAcquireRequestState, steps);
app.CreateEventExecutionSteps(HttpApplication.EventPreRequestHandlerExecute, steps);
steps.Add(app.CreateImplicitAsyncPreloadExecutionStep()); // implict async preload step
steps.Add(new CallHandlerExecutionStep(app)); // execute handler
app.CreateEventExecutionSteps(HttpApplication.EventPostRequestHandlerExecute, steps);
app.CreateEventExecutionSteps(HttpApplication.EventReleaseRequestState, steps);
app.CreateEventExecutionSteps(HttpApplication.EventPostReleaseRequestState, steps);
steps.Add(new CallFilterExecutionStep(app)); // filtering
app.CreateEventExecutionSteps(HttpApplication.EventUpdateRequestCache, steps);
app.CreateEventExecutionSteps(HttpApplication.EventPostUpdateRequestCache, steps);
_endRequestStepIndex = steps.Count;
app.CreateEventExecutionSteps(HttpApplication.EventEndRequest, steps);
steps.Add(new NoopExecutionStep()); // the last is always there
_execSteps = new IExecutionStep[steps.Count];
steps.CopyTo(_execSteps);
// callback for async completion when reposting to threadpool thread
_resumeStepsWaitCallback = stepCallback;
}
internal override void InitRequest() {
_currentStepIndex = -1;
_numStepCalls = 0;
_numSyncStepCalls = 0;
_requestCompleted = false;
}
// This attribute prevents undesirable 'just-my-code' debugging behavior (VSWhidbey 404406/VSWhidbey 609188)
[System.Diagnostics.DebuggerStepperBoundaryAttribute]
internal override void ResumeSteps(Exception error) {
bool appCompleted = false;
bool stepCompletedSynchronously = true;
HttpApplication app = _application;
CountdownTask appInstanceConsumersCounter = app.ApplicationInstanceConsumersCounter;
HttpContext context = app.Context;
ThreadContext threadContext = null;
AspNetSynchronizationContextBase syncContext = context.SyncContext;
Debug.Trace("Async", "HttpApplication.ResumeSteps");
try {
if (appInstanceConsumersCounter != null) {
appInstanceConsumersCounter.MarkOperationPending(); // ResumeSteps call started
}
using (syncContext.AcquireThreadLock()) {
// avoid ---- between the app code and fast async completion from a module
try {
threadContext = app.OnThreadEnter();
}
catch (Exception e) {
if (error == null)
error = e;
}
try {
try {
for (; ; ) {
// record error
if (syncContext.Error != null) {
error = syncContext.Error;
syncContext.ClearError();
}
if (error != null) {
app.RecordError(error);
error = null;
}
// check for any outstanding async operations
if (syncContext.PendingCompletion(_resumeStepsWaitCallback)) {
// wait until all pending async operations complete
break;
}
// advance to next step
if (_currentStepIndex < _endRequestStepIndex && (context.Error != null || _requestCompleted)) {
// end request
context.Response.FilterOutput();
_currentStepIndex = _endRequestStepIndex;
}
else {
_currentStepIndex++;
}
if (_currentStepIndex >= _execSteps.Length) {
appCompleted = true;
break;
}
// execute the current step
_numStepCalls++; // count all calls
// enable launching async operations before each new step
syncContext.Enable();
// call to execute current step catching thread abort exception
error = app.ExecuteStep(_execSteps[_currentStepIndex], ref stepCompletedSynchronously);
// unwind the stack in the async case
if (!stepCompletedSynchronously)
break;
_numSyncStepCalls++; // count synchronous calls
}
}
finally {
if (appCompleted) {
// need to raise OnRequestCompleted while within the ThreadContext so that things like User, CurrentCulture, etc. are available
context.RaiseOnRequestCompleted();
}
if (threadContext != null) {
try {
threadContext.DisassociateFromCurrentThread();
}
catch {
}
}
}
}
catch { // Protect against exception filters
throw;
}
} // using
if (appCompleted) {
// need to raise OnPipelineCompleted outside of the ThreadContext so that HttpContext.Current, User, etc. are unavailable
context.RaiseOnPipelineCompleted();
// unroot context (async app operations ended)
context.Unroot();
// async completion
app.AsyncResult.Complete((_numStepCalls == _numSyncStepCalls), null, null);
app.ReleaseAppInstance();
}
}
finally {
if (appInstanceConsumersCounter != null) {
appInstanceConsumersCounter.MarkOperationCompleted(); // ResumeSteps call complete
}
}
}
}
internal class PipelineStepManager : StepManager {
WaitCallback _resumeStepsWaitCallback;
bool _validatePathCalled;
bool _validateInputCalled;
internal PipelineStepManager(HttpApplication app): base(app) {
}
internal override void BuildSteps(WaitCallback stepCallback) {
Debug.Trace("PipelineRuntime", "BuildSteps");
//ArrayList steps = new ArrayList();
HttpApplication app = _application;
// add special steps that don't currently
// correspond to a configured handler
IExecutionStep materializeStep = new MaterializeHandlerExecutionStep(app);
// implicit map step
app.AddEventMapping(
HttpApplication.IMPLICIT_HANDLER,
RequestNotification.MapRequestHandler,
false, materializeStep);
// implicit async preload step
app.AddEventMapping(
HttpApplication.IMPLICIT_HANDLER,
RequestNotification.ExecuteRequestHandler,
false, app.CreateImplicitAsyncPreloadExecutionStep());
// implicit handler routing step
IExecutionStep handlerStep = new CallHandlerExecutionStep(app);
app.AddEventMapping(
HttpApplication.IMPLICIT_HANDLER,
RequestNotification.ExecuteRequestHandler,
false, handlerStep);
// implicit handler WebSockets step
IExecutionStep webSocketsStep = new TransitionToWebSocketsExecutionStep(app);
app.AddEventMapping(
HttpApplication.IMPLICIT_HANDLER,
RequestNotification.EndRequest,
true /* isPostNotification */, webSocketsStep);
// add implicit request filtering step
IExecutionStep filterStep = new CallFilterExecutionStep(app);
// normally, this executes during UpdateRequestCache as a high priority module
app.AddEventMapping(
HttpApplication.IMPLICIT_FILTER_MODULE,
RequestNotification.UpdateRequestCache,
false, filterStep);
// for error conditions, this executes during LogRequest as a high priority module
app.AddEventMapping(
HttpApplication.IMPLICIT_FILTER_MODULE,
RequestNotification.LogRequest,
false, filterStep);
_resumeStepsWaitCallback = stepCallback;
}
internal override void InitRequest() {
_requestCompleted = false;
_validatePathCalled = false;
_validateInputCalled = false;
}
// PipelineStepManager::ResumeSteps
// called from IIS7 (on IIS thread) via BeginProcessRequestNotification
// or from an async completion (on CLR thread) via HttpApplication::ResumeStepsFromThreadPoolThread
// This attribute prevents undesirable 'just-my-code' debugging behavior (VSWhidbey 404406/VSWhidbey 609188)
[System.Diagnostics.DebuggerStepperBoundaryAttribute]
internal override void ResumeSteps(Exception error) {
HttpContext context = _application.Context;
IIS7WorkerRequest wr = context.WorkerRequest as IIS7WorkerRequest;
AspNetSynchronizationContextBase syncContext = context.SyncContext;
RequestNotificationStatus status = RequestNotificationStatus.Continue;
ThreadContext threadContext = null;
bool needToDisassociateThreadContext = false;
bool isSynchronousCompletion = false;
bool needToComplete = false;
bool stepCompletedSynchronously = false;
bool isReEntry = false;
int currentModuleLastEventIndex = -1;
_application.GetNotifcationContextProperties(ref isReEntry, ref currentModuleLastEventIndex);
CountdownTask appInstanceConsumersCounter = _application.ApplicationInstanceConsumersCounter;
using (context.RootedObjects.WithinTraceBlock()) {
// DevDiv Bugs 187441: IIS7 Integrated Mode: Problem flushing Response from background threads in IIS7 integrated mode
if (!isReEntry) // currently we only re-enter for SendResponse
{
syncContext.AssociateWithCurrentThread();
}
try {
if (appInstanceConsumersCounter != null) {
appInstanceConsumersCounter.MarkOperationPending(); // ResumeSteps call started
}
bool locked = false;
try {
// As a performance optimization, ASP.NET uses the IIS IHttpContext::IndicateCompletion function to continue executing notifications
// on a thread that is associated with the AppDomain. This is done by calling IndicateCompletion from within the AppDomain, instead
// of returning to native code. This technique can only be used for notifications that complete synchronously.
// There are two cases where notifications happen on a thread that has an initialized ThreadContext, and therefore does not need
// to call ThreadContext.OnThreadEnter. These include SendResponse notifications and notifications that occur within a call to
// IndicateCompletion. Note that SendResponse notifications occur on-demand, i.e., they happen when another notification triggers
// a SendResponse, at which point it blocks until the SendResponse notification completes.
if (!isReEntry) { // currently we only re-enter for SendResponse
// DevDiv 482614 (Sharepoint Bug 3137123)
// Async completion or SendResponse can happen on a background thread while the thread that called IndicateCompletion has not unwound yet
// Therefore (InIndicateCompletion == true) is not a sufficient evidence that we can use the ThreadContext stored in IndicateCompletionContext
// To avoid using other thread's ThreadContext we use IndicateCompletionContext only if ThreadInsideIndicateCompletion is indeed our thread
if (context.InIndicateCompletion && context.ThreadInsideIndicateCompletion == Thread.CurrentThread) {
// we already have a ThreadContext
threadContext = context.IndicateCompletionContext;
if (context.UsesImpersonation) {
// UsesImpersonation is set to true after RQ_AUTHENTICATE_REQUEST
threadContext.SetImpersonationContext();
}
}
else {
// we need to create a new ThreadContext
threadContext = _application.OnThreadEnter(context.UsesImpersonation);
// keep track if we need to disassociate it later
needToDisassociateThreadContext = true;
}
}
for (; ; ) {
#if DBG
Debug.Trace("PipelineRuntime", "ResumeSteps: CurrentModuleEventIndex=" + context.CurrentModuleEventIndex);
#endif
// check and record errors into the HttpContext
if (syncContext.Error != null) {
error = syncContext.Error;
syncContext.ClearError();
}
if (error != null) {
// the error can be cleared by the user
_application.RecordError(error);
error = null;
}
if (!_validateInputCalled || !_validatePathCalled) {
error = ValidateHelper(context);
if (error != null) {
continue;
}
}
// check for any outstanding async operations
// DevDiv 1020085: User code may leave pending async completions on the synchronization context
// while processing nested (isReEntry == true) and not nested (isReEntry == false) notifications.
// In both cases only the non nested notification which has proper synchronization should handle it.
if (!isReEntry && syncContext.PendingCompletion(_resumeStepsWaitCallback)) {
// Background flushes may trigger RQ_SEND_RESPONSE notifications which will set new context.NotificationContext
// Synchronize access to context.NotificationContext to make sure we update the correct NotificationContext instance
_application.AcquireNotifcationContextLock(ref locked);
// Since the step completed asynchronously, this thread must return RequestNotificationStatus.Pending to IIS,
// and the async completion of this step must call IIS7WorkerRequest::PostCompletion. The async completion of
// this step will call ResumeSteps again.
context.NotificationContext.PendingAsyncCompletion = true;
break;
}
// LogRequest and EndRequest never report errors, and never return a status of FinishRequest.
bool needToFinishRequest = (context.NotificationContext.Error != null || context.NotificationContext.RequestCompleted)
&& context.CurrentNotification != RequestNotification.LogRequest
&& context.CurrentNotification != RequestNotification.EndRequest;
if (needToFinishRequest || context.CurrentModuleEventIndex == currentModuleLastEventIndex) {
// if an error occured or someone completed the request, set the status to FinishRequest
status = needToFinishRequest ? RequestNotificationStatus.FinishRequest : RequestNotificationStatus.Continue;
// async case
if (context.NotificationContext.PendingAsyncCompletion) {
context.Response.SyncStatusIntegrated();
context.NotificationContext.PendingAsyncCompletion = false;
isSynchronousCompletion = false;
needToComplete = true;
break;
}
// sync case (we might be able to stay in managed code and execute another notification)
if (needToFinishRequest || UnsafeIISMethods.MgdGetNextNotification(wr.RequestContext, RequestNotificationStatus.Continue) != 1) {
isSynchronousCompletion = true;
needToComplete = true;
break;
}
int currentModuleIndex = 0;
bool isPostNotification = false;
int currentNotification = 0;
UnsafeIISMethods.MgdGetCurrentNotificationInfo(wr.RequestContext, out currentModuleIndex, out isPostNotification, out currentNotification);
// setup the HttpContext for this event/module combo
context.CurrentModuleIndex = currentModuleIndex;
context.IsPostNotification = isPostNotification;
context.CurrentNotification = (RequestNotification)currentNotification;
context.CurrentModuleEventIndex = -1;
currentModuleLastEventIndex = _application.CurrentModuleContainer.GetEventCount(context.CurrentNotification, context.IsPostNotification) - 1;
}
context.CurrentModuleEventIndex++;
IExecutionStep step = _application.CurrentModuleContainer.GetNextEvent(context.CurrentNotification, context.IsPostNotification,
context.CurrentModuleEventIndex);
// enable launching async operations before each new step
context.SyncContext.Enable();
stepCompletedSynchronously = false;
error = _application.ExecuteStep(step, ref stepCompletedSynchronously);
#if DBG
Debug.Trace("PipelineRuntime", "ResumeSteps: notification=" + context.CurrentNotification.ToString()
+ ", isPost=" + context.IsPostNotification
+ ", step=" + step.GetType().FullName
+ ", completedSync=" + stepCompletedSynchronously
+ ", moduleName=" + _application.CurrentModuleContainer.DebugModuleName
+ ", moduleIndex=" + context.CurrentModuleIndex
+ ", eventIndex=" + context.CurrentModuleEventIndex);
#endif
if (!stepCompletedSynchronously) {
// Since the step completed asynchronously, this thread must return RequestNotificationStatus.Pending to IIS,
// and the async completion of this step must call IIS7WorkerRequest::PostCompletion. The async completion of
// this step will call ResumeSteps again.
//context.AcquireNotifcationContextLockBeforeUnwind();
_application.AcquireNotifcationContextLock(ref locked);
context.NotificationContext.PendingAsyncCompletion = true;
break;
}
else {
context.Response.SyncStatusIntegrated();
}
}
}
finally {
if (locked) {
_application.ReleaseNotifcationContextLock();
}
if (threadContext != null) {
if (context.InIndicateCompletion) {
if (isSynchronousCompletion) {
// this is a sync completion on an IIS thread
threadContext.Synchronize();
// Note for DevDiv 482614 fix:
// If this threadContext is from IndicateCompletionContext (e.g. this thread called IndicateCompletion)
// then we continue reusing this thread and only undo impersonation before unwinding back to IIS.
//
// If this threadContext was created while another thread was and still is in IndicateCompletion call
// (e.g. sync or async flush on a background thread from native code, not managed since isReEnty==false)
// then we can not reuse this thread and this threadContext will be cleaned before we leave ResumeSteps
// (because needToDisassociateThreadContext was set to true when we created this threadContext)
//always undo impersonation so that the token is removed before returning to IIS (DDB 156421)
threadContext.UndoImpersonationContext();
}
else {
// We're returning pending on an IIS thread in a call to IndicateCompletion.
// Leave the thread context now while we're still under the lock so that the
// async completion does not corrupt the state of HttpContext or IndicateCompletionContext.
if (!threadContext.HasBeenDisassociatedFromThread) {
lock (threadContext) {
if (!threadContext.HasBeenDisassociatedFromThread) {
threadContext.DisassociateFromCurrentThread();
// remember to not disassociate again
needToDisassociateThreadContext = false;
// DevDiv 482614:
// Async steps or completions may happen while another thread is inside IndicateCompletion
// We do not clear IndicateCompletionContext if it belongs to another thread
// (otherwise future notifications on the thread that called IndicateCompletion won't have
// context.IndicateCompletionContext pointing to their not yet disassociated ThreadContext)
if (context.ThreadInsideIndicateCompletion == Thread.CurrentThread) {
context.IndicateCompletionContext = null;
}
}
}
}
}
}
else if (isSynchronousCompletion) {
Debug.Assert(needToDisassociateThreadContext == true, "needToDisassociateThreadContext MUST BE true");
// this is a sync completion on an IIS thread
threadContext.Synchronize();
// get ready to call IndicateCompletion
context.IndicateCompletionContext = threadContext;
// Note for DevDiv 482614 fix:
// This thread created a new ThreadContext if it did not call IndicateCompletion yet or if there was
// another thread already in IndicateCompletion (a background flush from native code or a completion
// on another thread). In either case if currently there is no thread in IndicateCompletion
// then we can reuse this thread and its threadContext and call IndicateCompletion on the current thread.
// In this case we will not disassociate this threadContext now
needToDisassociateThreadContext = false;
//always undo impersonation so that the token is removed before returning to IIS (DDB 156421)
threadContext.UndoImpersonationContext();
}
else {
Debug.Assert(needToDisassociateThreadContext == true, "needToDisassociateThreadContext MUST BE true");
// We're not in a call to IndicateCompletion. We're either returning pending or
// we're in an async completion, and therefore we must clean-up the thread state. Impersonation is reverted
threadContext.DisassociateFromCurrentThread();
// remember to not disassociate again
needToDisassociateThreadContext = false;
}
// Cleanup the thread state unless we prepared to call IndicateCompletion or already cleaned up
if (needToDisassociateThreadContext) {
threadContext.DisassociateFromCurrentThread();
}
}
}
// WOS #1703315: we cannot complete until after OnThreadLeave is called.
if (needToComplete) {
// call HttpRuntime::OnRequestNotificationCompletion
_application.AsyncResult.Complete(isSynchronousCompletion, null /*result*/, null /*error*/, status);
}
} // end of try statement that begins after AssociateWithCurrentThread
finally {
if (!isReEntry) {
syncContext.DisassociateFromCurrentThread();
}
if (appInstanceConsumersCounter != null) {
appInstanceConsumersCounter.MarkOperationCompleted(); // ResumeSteps call completed
}
}
}
}
private Exception ValidateHelper(HttpContext context) {
if (!_validateInputCalled) {
_validateInputCalled = true;
try {
context.Request.ValidateInputIfRequiredByConfig();
}
catch(Exception e) {
return e;
}
}
if (!_validatePathCalled) {
_validatePathCalled = true;
try {
context.ValidatePath();
}
catch(Exception e) {
return e;
}
}
return null;
}
}
// WARNING: Mutable struct for performance reasons; exercise caution when using this type.
private struct AsyncStepCompletionInfo {
#pragma warning disable 420 // volatile passed by reference; our uses are safe
// state for async execution steps
private const int ASYNC_STATE_NONE = 0;
private const int ASYNC_STATE_BEGIN_UNWOUND = 1;
private const int ASYNC_STATE_CALLBACK_COMPLETED = 2;
private volatile int _asyncState;
private ExceptionDispatchInfo _error;
// Invoked from the callback to signal that the End* method has run to completion.
// Returns 'true' if the current thread should call ResumeSteps, 'false' if not.
public bool RegisterAsyncCompletion(Exception error) {
// Before the call to Exchange below, the _asyncCompletionInfo field will have the value
// ASYNC_STATE_NONE or ASYNC_STATE_BEGIN_UNWOUND. If it's the former, then the Begin* method
// hasn't yet returned control to IExecutionStep.Execute. From this step's point of view,
// this can be treated as a synchronous completion, which will allow us to call ResumeSteps
// on the original thread and save the cost of destroying the existing ThreadContext and
// creating a new one. If the original value is instead ASYNC_STATE_BEGIN_UNWOUND, then
// the Begin* method already returned control to IExecutionStep.Execute and this step was
// marked as having an asynchronous completion. The original thread will tear down the
// ThreadContext, so the current thread should call back into ResumeSteps to resurrect it.
//
// If there was an error, we'll use the _error field to store it so that IExecutionStep.Execute
// can rethrow it as it's unwinding.
// Interlocked performs a volatile write; all processors will see the write to _error as being
// no later than the write to _asyncState.
_error = (error != null) ? ExceptionDispatchInfo.Capture(error) : null;
int originalState = Interlocked.Exchange(ref _asyncState, ASYNC_STATE_CALLBACK_COMPLETED);
if (originalState == ASYNC_STATE_NONE) {
return false; // IExecutionStep.Execute should call ResumeSteps
}
Debug.Assert(originalState == ASYNC_STATE_BEGIN_UNWOUND, "Unexpected state.");
_error = null; // to prevent long-lived exception object; write doesn't need to be volatile since nobody reads this field anyway in this case
return true; // this thread should call ResumeSteps
}
public void RegisterBeginUnwound(IAsyncResult asyncResult, out bool operationCompleted, out bool mustCallEndHandler) {
operationCompleted = false;
mustCallEndHandler = false;
int originalState = Interlocked.Exchange(ref _asyncState, ASYNC_STATE_BEGIN_UNWOUND);
if (originalState == ASYNC_STATE_NONE) {
if (asyncResult.CompletedSynchronously) {
// Synchronous completion; the callback either wasn't called or was a no-op.
// In either case, we should call the End* method from this thread.
operationCompleted = true;
mustCallEndHandler = true;
}
// Otherwise, this is an asynchronous completion, and the callback hasn't yet been invoked or hasn't fully completed.
// We'll let the thread that invokes the callback call the End* method.
}
else {
Debug.Assert(originalState == ASYNC_STATE_CALLBACK_COMPLETED, "Unexpected state.");
// The operation completed, and the callback already invoked the End* method.
// The only thing we need to do is to report to our caller that the operation completed synchronously
// (so that ResumeSteps runs on this thread) and to observe any exceptions that occurred.
operationCompleted = true;
}
// Interlocked performs a volatile read; if RethrowExceptionIfNecessary() is called after RegisterBeginUnwound(),
// the thread will see the correct value for the _error field.
}
public void ReportError() {
// Using ExceptionDispatchInfo preserves the Exception's stack trace when rethrowing.
ExceptionDispatchInfo error = _error;
if (error != null) {
_error = null; // prevent long-lived Exception objects on the heap
error.Throw();
}
}
public void Reset() {
// All processors see the _error field write as being no later than the _asyncState field write.
_error = null;
_asyncState = ASYNC_STATE_NONE;
}
#pragma warning restore 420 // volatile passed by reference
}
private class StepInvoker {
private Action<Action> _action;
private StepInvoker _nextStep;
public StepInvoker() { }
public StepInvoker(Action<Action> action, StepInvoker step) {
_action = action;
_nextStep = step;
}
public void Invoke(Action executionStep) {
Debug.Assert(executionStep != null);
// Call the chained nextStep.
// The ExecutionStep is garuanteed to execute in HttpApplication,
// since ExecutionStep could cause reEntry to the pipeline and
// there is only one StepInvoker instance per HttpApplication instance.
if (_action != null) {
_action(() => _nextStep.Invoke(executionStep));
} else {
executionStep();
}
}
}
}
}
|