File: CustomLoaderHelper.cs

package info (click to toggle)
mono 6.14.1%2Bds2-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,282,732 kB
  • sloc: cs: 11,182,461; xml: 2,850,281; ansic: 699,123; cpp: 122,919; perl: 58,604; javascript: 30,841; asm: 21,845; makefile: 19,602; sh: 10,973; python: 4,772; pascal: 925; sql: 859; sed: 16; php: 1
file content (148 lines) | stat: -rw-r--r-- 7,532 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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
//------------------------------------------------------------------------------
// <copyright file="CustomLoaderHelper.cs" company="Microsoft">
//    Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//------------------------------------------------------------------------------

namespace System.Web.Hosting {
    using System;
    using System.Globalization;
    using System.Reflection;
    using System.Runtime.InteropServices;
    using System.Runtime.Remoting;
    using System.Runtime.Versioning;

    // Used to locate a custom loader implementation within a bin-deployed assembly.

    internal sealed class CustomLoaderHelper : MarshalByRefObject {

        // the first framework version where the custom loader feature was implemented
        private static readonly string _customLoaderTargetFrameworkName = new FrameworkName(".NETFramework", new Version(4, 5, 1)).ToString();

        private static readonly string _customLoaderAssemblyName = typeof(CustomLoaderHelper).Assembly.FullName;
        private static readonly string _customLoaderTypeName = typeof(CustomLoaderHelper).FullName;
        private static readonly Guid IID_ICustomLoader = new Guid("50A3CE65-2F9F-44E9-9094-32C6C928F966");

        // Instances of this type should only ever be created via reflection (see call to CreateObjectAndUnwrap
        // in GetCustomLoader).
        private CustomLoaderHelper() { }

        internal static IObjectHandle GetCustomLoader(ICustomLoaderHelperFunctions helperFunctions, string appConfigMetabasePath, string configFilePath, string customLoaderPhysicalPath, out AppDomain newlyCreatedAppDomain) {
            // Step 1: Does the host allow custom loaders?

            bool? customLoaderIsEnabled = helperFunctions.CustomLoaderIsEnabled;
            if (customLoaderIsEnabled.HasValue) {
                if ((bool)customLoaderIsEnabled) {
                    // The custom loader is enabled; move on to the next step.
                }
                else {
                    // The custom loader is disabled, fail.
                    throw new NotSupportedException(ApplicationServicesStrings.CustomLoader_ForbiddenByHost);
                }
            }
            else {
                // The host hasn't set a policy, so we'll fall back to our default logic of checking the application's trust level.
                if (!IsFullyTrusted(helperFunctions, appConfigMetabasePath)) {
                    throw new NotSupportedException(ApplicationServicesStrings.CustomLoader_NotInFullTrust);
                }
            }

            // Step 2: Create the new AD

            string binFolderPhysicalPath = helperFunctions.MapPath("/bin/");

            AppDomainSetup setup = new AppDomainSetup() {
                PrivateBinPathProbe = "*",  // disable loading from app base
                PrivateBinPath = binFolderPhysicalPath,
                ApplicationBase = helperFunctions.AppPhysicalPath,
                TargetFrameworkName = _customLoaderTargetFrameworkName
            };

            if (configFilePath != null) {
                setup.ConfigurationFile = configFilePath;
            }

            AppDomain newAppDomainForCustomLoader = AppDomain.CreateDomain("aspnet-custom-loader-" + Guid.NewGuid(), null, setup);
            try {
                // Step 3: Instantiate helper in new AD so that we can get a reference to the loader
                CustomLoaderHelper helper = (CustomLoaderHelper)newAppDomainForCustomLoader.CreateInstanceAndUnwrap(_customLoaderAssemblyName, _customLoaderTypeName,
                    ignoreCase: false,
                    bindingAttr: BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.CreateInstance | BindingFlags.Instance,
                    binder: null,
                    args: null,
                    culture: null,
                    activationAttributes: null);
                ObjectHandle ohCustomLoader = helper.GetCustomLoaderImpl(customLoaderPhysicalPath);

                // If we got this far, success!
                newlyCreatedAppDomain = newAppDomainForCustomLoader;
                return ohCustomLoader;
            }
            catch {
                // If something went wrong, kill the new AD.
                AppDomain.Unload(newAppDomainForCustomLoader);
                throw;
            }
        }

        private ObjectHandle GetCustomLoaderImpl(string customLoaderPhysicalPath) {
            // Step 4: Find the implementation in the custom loader assembly

            // Since we have set the private bin path, we can use this call to Assembly.Load
            // to avoid the load-from context, which has weird behaviors.
            AssemblyName customLoaderAssemblyName = AssemblyName.GetAssemblyName(customLoaderPhysicalPath);
            Assembly customLoaderAssembly = Assembly.Load(customLoaderAssemblyName);
            CustomLoaderAttribute customLoaderAttribute = customLoaderAssembly.GetCustomAttribute<CustomLoaderAttribute>();

            if (customLoaderAttribute == null) {
                throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, ApplicationServicesStrings.CustomLoader_NoAttributeFound, customLoaderAssemblyName));
            }

            // Step 5: Instantiate the custom loader and return a reference back to native code

            object customLoader = Activator.CreateInstance(customLoaderAttribute.CustomLoaderType);

            // This check isn't strictly necessary since the unmanaged layer will handle QueryInterface failures
            // appropriately, but we have an opportunity to provide a better error message at this layer.
            if (!ObjectImplementsComInterface(customLoader, IID_ICustomLoader)) {
                throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, ApplicationServicesStrings.CustomLoader_MustImplementICustomLoader, customLoader.GetType()));
            }

            return new ObjectHandle(customLoader);
        }

        private static bool IsFullyTrusted(ICustomLoaderHelperFunctions helperFunctions, string appConfigMetabasePath) {
            // The managed configuration system hasn't yet been instantiated but the IIS native config system understands
            // ASP.NET configuration and honors hierarchy and section locking.

            try {
                // Must exactly match <trust level="Full" />, as this is what ApplicationManager expects.
                string trustLevel = helperFunctions.GetTrustLevel(appConfigMetabasePath);
                return String.Equals("Full", trustLevel, StringComparison.Ordinal);
            }
            catch {
                // If any of the sections are locked or there is a config error, bail.
                return false;
            }
        }

        private static bool ObjectImplementsComInterface(object o, Guid iid) {
            IntPtr pUnknown = IntPtr.Zero;
            IntPtr pInterface = IntPtr.Zero;

            try {
                pUnknown = Marshal.GetIUnknownForObject(o); // AddRef
                int hr = Marshal.QueryInterface(pUnknown, ref iid, out pInterface); // AddRef
                return (hr == 0 && pInterface != IntPtr.Zero);
            }
            finally {
                if (pUnknown != IntPtr.Zero) {
                    Marshal.Release(pUnknown);
                }
                if (pInterface != IntPtr.Zero) {
                    Marshal.Release(pInterface);
                }
            }
        }
    }
}