File: AspNetHostExecutionContextManager.cs

package info (click to toggle)
mono 6.8.0.105%2Bdfsg-3.3
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 1,284,512 kB
  • sloc: cs: 11,172,132; xml: 2,850,069; ansic: 671,653; cpp: 122,091; perl: 59,366; javascript: 30,841; asm: 22,168; makefile: 20,093; sh: 15,020; python: 4,827; pascal: 925; sql: 859; sed: 16; php: 1
file content (134 lines) | stat: -rw-r--r-- 6,953 bytes parent folder | download | duplicates (7)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
namespace System.Web.Hosting {
    using System;
    using System.Security.Permissions;
    using System.Threading;

    // This HostExecutionContextManager can provide both setup and cleanup logic that
    // is invoked during a call to ExecutionContext.Run. This may be necessary when
    // using the Task-based APIs, as the 'await' language feature generally causes
    // this stack to be generated:
    //
    // { state machine callback }
    // ExecutionContext.Run
    // Task.SomeInternalCallback
    // AspNetSynchronizationContext.PostCallbackLogic
    //
    // The callback logic invoked by our AspNetSynchronizationContext.Post method puts
    // HttpContext-related information in the current ExecutionContext, but the
    // subsequent call to ExecutionContext.Run overwrites that information. So we have
    // logic in AspNetHostExecutionContextManager that can detect if a ThreadContext is
    // associated with the current thread (it will have been set by the Post callback),
    // and if so it should restore HttpContext.Current and other ExecutionContext-related
    // items on the current thread.

    [SecurityPermission(SecurityAction.LinkDemand, Unrestricted = true)]
    internal sealed class AspNetHostExecutionContextManager : HostExecutionContextManager {

        // Used as the return value from SetHostExecutionContext when our logic is active.
        // We use RevertAction instead of Action since it's unambiguous in the event that
        // base.SetHostExecutionContext itself ever changes to return Action.
        private delegate void RevertAction();

        public override HostExecutionContext Capture() {
            ThreadContext currentContext = ThreadContext.Current;
            if (currentContext != null) {
                // We need to capture a reference to the current HttpContext's ThreadContextId
                // so that we can properly restore this instance on the call to SetHostExecutionContext.
                // See comment on HttpContext.ThreadContextId for more information.
                return new AspNetHostExecutionContext(
                    baseContext: base.Capture(),
                    httpContextThreadContextId: currentContext.HttpContext.ThreadContextId);
            }
            else {
                // There is no ThreadContext associated with this thread, hence there is no special
                // setup we need to do to restore things like HttpContext.Current. We can just
                // delegate to the base implementation.
                return base.Capture();
            }
        }

        public override void Revert(object previousState) {
            RevertAction revertAction = previousState as RevertAction;
            if (revertAction != null) {
                // Our revert logic should run. It will eventually call base.Revert.
                revertAction();
            }
            else {
                // We have no revert logic, so just call the base implementation.
                base.Revert(previousState);
            }
        }

        public override object SetHostExecutionContext(HostExecutionContext hostExecutionContext) {
            AspNetHostExecutionContext castHostExecutionContext = hostExecutionContext as AspNetHostExecutionContext;
            if (castHostExecutionContext != null) {
                // Call base.SetHostExecutionContext before calling our own logic.
                object baseRevertParameter = null;
                if (castHostExecutionContext.BaseContext != null) {
                    baseRevertParameter = base.SetHostExecutionContext(castHostExecutionContext.BaseContext);
                }

                ThreadContext currentContext = ThreadContext.Current;
                if (currentContext != null && currentContext.HttpContext.ThreadContextId == castHostExecutionContext.HttpContextThreadContextId) {
                    // If we reached this point, then 'castHostExecutionContext' was captured for the HttpContext
                    // that is associated with the ThreadContext that is assigned to the current thread. We can
                    // safely restore it.
                    Action threadContextCleanupAction = currentContext.EnterExecutionContext();

                    // Perform cleanup in the opposite order from initialization.
                    return (RevertAction)(() => {
                        threadContextCleanupAction();
                        if (baseRevertParameter != null) {
                            base.Revert(baseRevertParameter);
                        }
                    });
                }
                else {
                    // If we reached this point, then 'castHostExecutionContext' was captured by us
                    // but is not applicable to the current thread. This can happen if the developer
                    // called ThreadPool.QueueUserWorkItem, for example. We don't restore HttpContext
                    // on such threads since they're not under the control of ASP.NET. In this case,
                    // we have already called base.SetHostExecutionContext, so we just need to return
                    // the result of that function directly to our caller.
                    return baseRevertParameter;
                }
            }
            else {
                // If we reached this point, then 'hostExecutionContext' was generated by our
                // base class instead of by us, so just delegate to the base implementation.
                return base.SetHostExecutionContext(hostExecutionContext);
            }
        }

        private sealed class AspNetHostExecutionContext : HostExecutionContext {
            public readonly HostExecutionContext BaseContext;
            public readonly object HttpContextThreadContextId;

            internal AspNetHostExecutionContext(HostExecutionContext baseContext, object httpContextThreadContextId) {
                BaseContext = baseContext;
                HttpContextThreadContextId = httpContextThreadContextId;
            }

            // copy ctor
            private AspNetHostExecutionContext(AspNetHostExecutionContext original)
                : this(CreateCopyHelper(original.BaseContext), original.HttpContextThreadContextId) {
            }

            public override HostExecutionContext CreateCopy() {
                return new AspNetHostExecutionContext(this);
            }

            private static HostExecutionContext CreateCopyHelper(HostExecutionContext hostExecutionContext) {
                // creating a copy of a null context should just itself return null
                return (hostExecutionContext != null) ? hostExecutionContext.CreateCopy() : null;
            }

            public override void Dispose(bool disposing) {
                if (disposing && BaseContext != null) {
                    BaseContext.Dispose();
                }
            }
        }

    }
}