You've already forked linux-packaging-mono
Imported Upstream version 4.6.0.125
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
This commit is contained in:
parent
a569aebcfd
commit
e79aa3c0ed
359
mcs/class/referencesource/System.Web/ThreadContext.cs
Normal file
359
mcs/class/referencesource/System.Web/ThreadContext.cs
Normal file
@ -0,0 +1,359 @@
|
||||
namespace System.Web {
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using System.Runtime.Remoting.Messaging;
|
||||
using System.Security.Principal;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Web.Configuration;
|
||||
using System.Web.UI;
|
||||
using System.Web.Util;
|
||||
|
||||
// Contains information about any modifications ASP.NET has made to the current
|
||||
// thread and how to undo them. See also the comments on
|
||||
// HttpApplication.OnThreadEnterPrivate.
|
||||
|
||||
internal sealed class ThreadContext : ISyncContextLock {
|
||||
|
||||
// This is a marker holding the current ThreadContext for the current
|
||||
// thread. Uses TLS so that it's not wiped away by ExecutionContext.Run.
|
||||
[ThreadStatic]
|
||||
private static ThreadContext _currentThreadContext;
|
||||
|
||||
private ImpersonationContext _newImpersonationContext;
|
||||
private HttpContext _originalHttpContext;
|
||||
private SynchronizationContext _originalSynchronizationContext;
|
||||
private ThreadContext _originalThreadContextCurrent;
|
||||
private CultureInfo _originalThreadCurrentCulture;
|
||||
private CultureInfo _originalThreadCurrentUICulture;
|
||||
private IPrincipal _originalThreadPrincipal;
|
||||
private bool _setCurrentThreadOnHttpContext;
|
||||
|
||||
internal ThreadContext(HttpContext httpContext) {
|
||||
HttpContext = httpContext;
|
||||
}
|
||||
|
||||
internal static ThreadContext Current {
|
||||
get { return _currentThreadContext; }
|
||||
private set { _currentThreadContext = value; }
|
||||
}
|
||||
|
||||
internal bool HasBeenDisassociatedFromThread {
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
internal HttpContext HttpContext {
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
// Associates this ThreadContext with the current thread. This restores certain
|
||||
// ambient values associated with the current HttpContext, such as the current
|
||||
// user and cultures. It also sets HttpContext.Current.
|
||||
internal void AssociateWithCurrentThread(bool setImpersonationContext) {
|
||||
Debug.Assert(HttpContext != null); // only to be used when context is available
|
||||
Debug.Assert(Current != this, "This ThreadContext is already associated with this thread.");
|
||||
Debug.Assert(!HasBeenDisassociatedFromThread, "This ThreadContext has already been disassociated from a thread.");
|
||||
|
||||
Debug.Trace("OnThread", GetTraceMessage("Enter1"));
|
||||
|
||||
/*
|
||||
* !! IMPORTANT !!
|
||||
* Keep this logic in [....] with DisassociateFromCurrentThread and EnterExecutionContext.
|
||||
*/
|
||||
|
||||
// attach http context to the call context
|
||||
_originalHttpContext = DisposableHttpContextWrapper.SwitchContext(HttpContext);
|
||||
|
||||
// set impersonation on the current thread
|
||||
if (setImpersonationContext) {
|
||||
SetImpersonationContext();
|
||||
}
|
||||
|
||||
// set synchronization context for the current thread to support the async pattern
|
||||
_originalSynchronizationContext = AsyncOperationManager.SynchronizationContext;
|
||||
AspNetSynchronizationContextBase aspNetSynchronizationContext = HttpContext.SyncContext;
|
||||
AsyncOperationManager.SynchronizationContext = aspNetSynchronizationContext;
|
||||
|
||||
// set ETW trace ID
|
||||
Guid g = HttpContext.WorkerRequest.RequestTraceIdentifier;
|
||||
if (g != Guid.Empty) {
|
||||
CallContext.LogicalSetData("E2ETrace.ActivityID", g);
|
||||
}
|
||||
|
||||
// set SqlDependecyCookie
|
||||
HttpContext.ResetSqlDependencyCookie();
|
||||
|
||||
// set principal on the current thread
|
||||
_originalThreadPrincipal = Thread.CurrentPrincipal;
|
||||
HttpApplication.SetCurrentPrincipalWithAssert(HttpContext.User);
|
||||
|
||||
// only set culture on the current thread if it is not initialized
|
||||
SetRequestLevelCulture();
|
||||
|
||||
// DevDivBugs 75042
|
||||
// set current thread in context if there is not there
|
||||
// the timeout manager uses this to abort the correct thread
|
||||
if (HttpContext.CurrentThread == null) {
|
||||
_setCurrentThreadOnHttpContext = true;
|
||||
HttpContext.CurrentThread = Thread.CurrentThread;
|
||||
}
|
||||
|
||||
// Store a reference to the original ThreadContext.Current. It is possible that a parent
|
||||
// ThreadContext might already be associated with the current thread, e.g. if the current
|
||||
// stack contains a call to MgdIndicateCompletion (via
|
||||
// PipelineRuntime.ProcessRequestNotificationHelper). If this is the case, the child
|
||||
// ThreadContext will temporarily take over.
|
||||
_originalThreadContextCurrent = Current;
|
||||
Current = this;
|
||||
|
||||
Debug.Trace("OnThread", GetTraceMessage("Enter2"));
|
||||
}
|
||||
|
||||
private ClientImpersonationContext CreateNewClientImpersonationContext() {
|
||||
// impersonation is set in the ClientImpersonationContext ctor
|
||||
return new ClientImpersonationContext(HttpContext);
|
||||
}
|
||||
|
||||
// Disassociates this ThreadContext from the current thread. Any ambient values (e.g., culture)
|
||||
// associated with the current request are stored in the HttpContext object so that they
|
||||
// can be restored the next time a ThreadContext associated with this HttpContext is active.
|
||||
// Impersonation and other similar modifications to the current thread are undone.
|
||||
internal void DisassociateFromCurrentThread() {
|
||||
Debug.Trace("OnThread", GetTraceMessage("Leave1"));
|
||||
Debug.Assert(Current == this, "This ThreadContext isn't associated with current thread.");
|
||||
Debug.Assert(!HasBeenDisassociatedFromThread, "This ThreadContext has already been disassociated from a thread.");
|
||||
|
||||
/*
|
||||
* !! IMPORTANT !!
|
||||
* Keep this logic in [....] with AssociateWithCurrentThread and EnterExecutionContext.
|
||||
*/
|
||||
|
||||
Current = _originalThreadContextCurrent;
|
||||
HasBeenDisassociatedFromThread = true;
|
||||
|
||||
// remove thread if set
|
||||
if (_setCurrentThreadOnHttpContext) {
|
||||
HttpContext.CurrentThread = null;
|
||||
}
|
||||
|
||||
// this thread should not be locking app state
|
||||
HttpApplicationFactory.ApplicationState.EnsureUnLock();
|
||||
|
||||
// stop impersonation
|
||||
UndoImpersonationContext();
|
||||
|
||||
// restore culture
|
||||
RestoreRequestLevelCulture();
|
||||
|
||||
// restrore synchronization context
|
||||
AsyncOperationManager.SynchronizationContext = _originalSynchronizationContext;
|
||||
|
||||
// restore thread principal
|
||||
HttpApplication.SetCurrentPrincipalWithAssert(_originalThreadPrincipal);
|
||||
|
||||
// Remove SqlCacheDependency cookie from call context if necessary
|
||||
HttpContext.RemoveSqlDependencyCookie();
|
||||
|
||||
// remove http context from the call context
|
||||
DisposableHttpContextWrapper.SwitchContext(_originalHttpContext);
|
||||
_originalHttpContext = null;
|
||||
|
||||
Debug.Trace("OnThread", GetTraceMessage("Leave2"));
|
||||
}
|
||||
|
||||
// Called by AspNetHostExecutionContextManager to signal that ExecutionContext.Run
|
||||
// is being called on a thread currently associated with our ThreadContext. Since
|
||||
// ExecutionContext.Run destroys some of our ambient state (HttpContext.Current, etc.),
|
||||
// we need to restore it. This method returns an Action which should be called when
|
||||
// the call to ExecutionContext.Run is concluding.
|
||||
internal Action EnterExecutionContext() {
|
||||
Debug.Trace("OnThread", GetTraceMessage("EnterExecutionContext1"));
|
||||
Debug.Assert(Current == this, "This ThreadContext isn't associated with current thread.");
|
||||
Debug.Assert(!HasBeenDisassociatedFromThread, "This ThreadContext has already been disassociated from a thread.");
|
||||
|
||||
/*
|
||||
* !! IMPORTANT !!
|
||||
* Keep this logic in [....] with AssociateWithCurrentThread and DisassociateFromCurrentThread.
|
||||
*/
|
||||
|
||||
// ExecutionContext.Run replaces the current impersonation token, so we need to impersonate
|
||||
// if AssociateWithCurrentThread also did so.
|
||||
|
||||
ClientImpersonationContext executionContextClientImpersonationContext = null;
|
||||
if (_newImpersonationContext != null) {
|
||||
executionContextClientImpersonationContext = CreateNewClientImpersonationContext();
|
||||
}
|
||||
|
||||
// ExecutionContext.Run resets the LogicalCallContext / IllogicalCallContext (which contains HttpContext.Current),
|
||||
// so we need to restore both of them.
|
||||
|
||||
DisposableHttpContextWrapper.SwitchContext(HttpContext);
|
||||
|
||||
Guid g = HttpContext.WorkerRequest.RequestTraceIdentifier;
|
||||
if (g != Guid.Empty) {
|
||||
CallContext.LogicalSetData("E2ETrace.ActivityID", g);
|
||||
}
|
||||
|
||||
HttpContext.ResetSqlDependencyCookie();
|
||||
|
||||
// ExecutionContext.Run resets the thread's CurrentPrincipal, so we need to restore it.
|
||||
|
||||
HttpApplication.SetCurrentPrincipalWithAssert(HttpContext.User);
|
||||
|
||||
// Other items like [ThreadStatic] fields, culture, etc. are untouched by ExecutionContext.Run,
|
||||
// so we don't need to worry about them.
|
||||
|
||||
Debug.Trace("OnThread", GetTraceMessage("EnterExecutionContext2"));
|
||||
|
||||
// This delegate is the cleanup routine.
|
||||
return () => {
|
||||
Debug.Trace("OnThread", GetTraceMessage("LeaveExecutionContext1"));
|
||||
|
||||
// Undo any impersonation that we performed.
|
||||
if (executionContextClientImpersonationContext != null) {
|
||||
executionContextClientImpersonationContext.Undo();
|
||||
}
|
||||
|
||||
// Other things, e.g. changes to the logical/illogical call contexts, changes
|
||||
// to CurrentPrincipal, etc., will automatically be reverted anyway when
|
||||
// the call to ExecutionContext.Run concludes, so we don't need to clean up
|
||||
// here.
|
||||
|
||||
Debug.Trace("OnThread", GetTraceMessage("LeaveExecutionContext2"));
|
||||
};
|
||||
}
|
||||
|
||||
private static string GetTraceMessage(string tag) {
|
||||
#if DBG
|
||||
StringBuilder sb = new StringBuilder(256);
|
||||
sb.Append(tag);
|
||||
sb.AppendFormat(" Thread={0}", SafeNativeMethods.GetCurrentThreadId().ToString(CultureInfo.InvariantCulture));
|
||||
sb.AppendFormat(" Context={0}", (HttpContext.Current != null) ? HttpContext.Current.GetHashCode().ToString(CultureInfo.InvariantCulture) : "NULL_CTX");
|
||||
sb.AppendFormat(" Principal={0}", (Thread.CurrentPrincipal != null) ? Thread.CurrentPrincipal.GetHashCode().ToString(CultureInfo.InvariantCulture) : "NULL_PRIN");
|
||||
sb.AppendFormat(" Culture={0}", Thread.CurrentThread.CurrentCulture.LCID.ToString(CultureInfo.InvariantCulture));
|
||||
sb.AppendFormat(" UICulture={0}", Thread.CurrentThread.CurrentUICulture.LCID.ToString(CultureInfo.InvariantCulture));
|
||||
sb.AppendFormat(" ActivityID={0}", CallContext.LogicalGetData("E2ETrace.ActivityID"));
|
||||
return sb.ToString();
|
||||
#else
|
||||
// This method should never be called in release mode.
|
||||
throw new NotImplementedException();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// Restores the thread's CurrentCulture and CurrentUICulture back to what
|
||||
// they were before this ThreadContext was associated with the thread. If
|
||||
// any culture has changed from its original value, we squirrel the new
|
||||
// culture away in HttpContext so that we can restore it the next time any
|
||||
// ThreadContext associated with this HttpContext is active.
|
||||
private void RestoreRequestLevelCulture() {
|
||||
CultureInfo currentCulture = Thread.CurrentThread.CurrentCulture;
|
||||
CultureInfo currentUICulture = Thread.CurrentThread.CurrentUICulture;
|
||||
|
||||
if (_originalThreadCurrentCulture != null) {
|
||||
// Avoid the cost of the Demand when setting the culture by comparing the cultures first
|
||||
if (currentCulture != _originalThreadCurrentCulture) {
|
||||
HttpRuntime.SetCurrentThreadCultureWithAssert(_originalThreadCurrentCulture);
|
||||
if (HttpContext != null) {
|
||||
// remember changed culture for the rest of the request
|
||||
HttpContext.DynamicCulture = currentCulture;
|
||||
}
|
||||
}
|
||||
|
||||
_originalThreadCurrentCulture = null;
|
||||
}
|
||||
|
||||
if (_originalThreadCurrentUICulture != null) {
|
||||
// Avoid the cost of the Demand when setting the culture by comparing the cultures first
|
||||
if (currentUICulture != _originalThreadCurrentUICulture) {
|
||||
Thread.CurrentThread.CurrentUICulture = _originalThreadCurrentUICulture;
|
||||
if (HttpContext != null) {
|
||||
// remember changed culture for the rest of the request
|
||||
HttpContext.DynamicUICulture = currentUICulture;
|
||||
}
|
||||
}
|
||||
|
||||
_originalThreadCurrentUICulture = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Sets impersonation on the current thread.
|
||||
internal void SetImpersonationContext() {
|
||||
if (_newImpersonationContext == null) {
|
||||
_newImpersonationContext = CreateNewClientImpersonationContext();
|
||||
}
|
||||
}
|
||||
|
||||
// Sets the thread's CurrentCulture and CurrentUICulture to those associated
|
||||
// with the current HttpContext. We do this since the culture of a request can
|
||||
// change over its lifetime and isn't necessarily the default for the AppDomain,
|
||||
// e.g. if the culture was read from the request headers.
|
||||
private void SetRequestLevelCulture() {
|
||||
CultureInfo culture = null;
|
||||
CultureInfo uiculture = null;
|
||||
|
||||
GlobalizationSection globConfig = RuntimeConfig.GetConfig(HttpContext).Globalization;
|
||||
if (!String.IsNullOrEmpty(globConfig.Culture))
|
||||
culture = HttpContext.CultureFromConfig(globConfig.Culture, true);
|
||||
|
||||
if (!String.IsNullOrEmpty(globConfig.UICulture))
|
||||
uiculture = HttpContext.CultureFromConfig(globConfig.UICulture, false);
|
||||
|
||||
if (HttpContext.DynamicCulture != null)
|
||||
culture = HttpContext.DynamicCulture;
|
||||
|
||||
if (HttpContext.DynamicUICulture != null)
|
||||
uiculture = HttpContext.DynamicUICulture;
|
||||
|
||||
// Page also could have its own culture settings
|
||||
Page page = HttpContext.CurrentHandler as Page;
|
||||
|
||||
if (page != null) {
|
||||
if (page.DynamicCulture != null)
|
||||
culture = page.DynamicCulture;
|
||||
|
||||
if (page.DynamicUICulture != null)
|
||||
uiculture = page.DynamicUICulture;
|
||||
}
|
||||
|
||||
_originalThreadCurrentCulture = Thread.CurrentThread.CurrentCulture;
|
||||
_originalThreadCurrentUICulture = Thread.CurrentThread.CurrentUICulture;
|
||||
|
||||
if (culture != null && culture != Thread.CurrentThread.CurrentCulture) {
|
||||
HttpRuntime.SetCurrentThreadCultureWithAssert(culture);
|
||||
}
|
||||
|
||||
if (uiculture != null && uiculture != Thread.CurrentThread.CurrentUICulture) {
|
||||
Thread.CurrentThread.CurrentUICulture = uiculture;
|
||||
}
|
||||
}
|
||||
|
||||
// Use of IndicateCompletion requires that we synchronize the cultures
|
||||
// with what may have been set by user code during execution of the
|
||||
// notification.
|
||||
internal void Synchronize() {
|
||||
HttpContext.DynamicCulture = Thread.CurrentThread.CurrentCulture;
|
||||
HttpContext.DynamicUICulture = Thread.CurrentThread.CurrentUICulture;
|
||||
}
|
||||
|
||||
// Undoes any impersonation that we did when associating this ThreadContext
|
||||
// with the current thread.
|
||||
internal void UndoImpersonationContext() {
|
||||
// remove impersonation on the current thread
|
||||
if (_newImpersonationContext != null) {
|
||||
_newImpersonationContext.Undo();
|
||||
_newImpersonationContext = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Called by AspNetSynchronizationContext to signal that it is finished
|
||||
// processing on the current thread.
|
||||
void ISyncContextLock.Leave() {
|
||||
DisassociateFromCurrentThread();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user