ScriptResourceHandler.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / xsp / System / Extensions / Handlers / ScriptResourceHandler.cs / 1305376 / ScriptResourceHandler.cs

                            //------------------------------------------------------------------------------ 
// 
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//----------------------------------------------------------------------------- 

namespace System.Web.Handlers { 
    using System; 
    using System.Collections;
    using System.Collections.Generic; 
    using System.Collections.Specialized;
    using System.Globalization;
    using System.IO;
    using System.IO.Compression; 
    using System.Reflection;
    using System.Resources; 
    using System.Security; 
    using System.Security.Cryptography;
    using System.Security.Permissions; 
    using System.Security.Policy;
    using System.Text;
    using System.Web;
    using System.Web.Configuration; 
    using System.Web.Hosting;
    using System.Web.Resources; 
    using System.Web.UI; 
    using System.Web.Util;
    using Debug = System.Diagnostics.Debug; 
    using Tuple = System.Web.Util.Tuple;

    public class ScriptResourceHandler : IHttpHandler {
 
        private const string _scriptResourceUrl = "~/ScriptResource.axd";
        private static readonly IDictionary _assemblyInfoCache = Hashtable.Synchronized(new Hashtable()); 
        private static readonly IDictionary _cultureCache = Hashtable.Synchronized(new Hashtable()); 
        private static readonly Object _getMethodLock = new Object();
        private static IScriptResourceHandler _scriptResourceHandler = new RuntimeScriptResourceHandler(); 
        private static string _scriptResourceAbsolutePath;
        // _bypassVirtualPathResolution set by unit tests to avoid resolving ~/ paths from unit tests.
        private static bool _bypassVirtualPathResolution = false;
        private static int _maximumResourceUrlLength = 2048; 

        private static string ScriptResourceAbsolutePath { 
            get { 
                if (_scriptResourceAbsolutePath == null) {
                    _scriptResourceAbsolutePath = VirtualPathUtility.ToAbsolute(_scriptResourceUrl); 
                }
                return _scriptResourceAbsolutePath;
            }
        } 

        private static Exception Create404(Exception innerException) { 
            return new HttpException(404, AtlasWeb.ScriptResourceHandler_InvalidRequest, innerException); 
        }
 
        private static string DecryptParameter(NameValueCollection queryString) {
            string encryptedData = queryString["d"];
            if (String.IsNullOrEmpty(encryptedData)) {
                Throw404(); 
            }
 
            try { 
                return Page.DecryptString(encryptedData);
            } 
            catch (CryptographicException ex) {
                throw Create404(ex);
            }
        } 

        internal static CultureInfo DetermineNearestAvailableCulture( 
            Assembly assembly, 
            string scriptResourceName,
            CultureInfo culture) { 

            if (String.IsNullOrEmpty(scriptResourceName)) return CultureInfo.InvariantCulture;

            Tuple cacheKey = new Tuple(assembly, scriptResourceName, culture); 
            CultureInfo cachedCulture = (CultureInfo)_cultureCache[cacheKey];
            if (cachedCulture == null) { 
 
                string releaseResourceName =
                    scriptResourceName.EndsWith(".debug.js", StringComparison.OrdinalIgnoreCase) ? 
                    scriptResourceName.Substring(0, scriptResourceName.Length - 9) + ".js" :
                    null;

                ScriptResourceInfo resourceInfo = ScriptResourceInfo.GetInstance(assembly, scriptResourceName); 
                ScriptResourceInfo releaseResourceInfo = (releaseResourceName != null) ?
                    ScriptResourceInfo.GetInstance(assembly, releaseResourceName) : null; 
 
                if (!String.IsNullOrEmpty(resourceInfo.ScriptResourceName) ||
                    ((releaseResourceInfo != null) && !String.IsNullOrEmpty(releaseResourceInfo.ScriptResourceName))) { 

                    ResourceManager resourceManager =
                        ScriptResourceAttribute.GetResourceManager(resourceInfo.ScriptResourceName, assembly);
                    ResourceManager releaseResourceManager = (releaseResourceInfo != null) ? 
                        ScriptResourceAttribute.GetResourceManager(releaseResourceInfo.ScriptResourceName, assembly) : null;
 
                    ResourceSet localizedSet = null; 
                    ResourceSet releaseSet = null;
                    if (resourceManager != null) { 
                        resourceManager.GetResourceSet(CultureInfo.InvariantCulture, true, true);
                        // Look for the explicitly localized version of the resources that is nearest the culture.
                        localizedSet = resourceManager.GetResourceSet(culture, true, false);
                    } 
                    if (releaseResourceManager != null) {
                        releaseResourceManager.GetResourceSet(CultureInfo.InvariantCulture, true, true); 
                        // Look for the explicitly localized version of the resources that is nearest the culture. 
                        releaseSet = releaseResourceManager.GetResourceSet(culture, true, false);
                    } 
                    if ((resourceManager != null) || (releaseResourceManager != null)) {
                        while ((localizedSet == null) && (releaseSet == null)) {
                            culture = culture.Parent;
                            if (culture.Equals(CultureInfo.InvariantCulture)) break; 
                            localizedSet = resourceManager.GetResourceSet(culture, true, false);
                            releaseSet = (releaseResourceManager != null) ? 
                                releaseResourceManager.GetResourceSet(culture, true, false) : null; 
                        }
                    } 
                    else {
                        culture = CultureInfo.InvariantCulture;
                    }
                } 
                else {
                    culture = CultureInfo.InvariantCulture; 
                } 
                // Neutral assembly culture falls back on invariant
                CultureInfo neutralCulture = GetAssemblyNeutralCulture(assembly); 
                if ((neutralCulture != null) && neutralCulture.Equals(culture)) {
                    culture = CultureInfo.InvariantCulture;
                }
                cachedCulture = culture; 
                _cultureCache[cacheKey] = cachedCulture;
            } 
            return cachedCulture; 
        }
 
        private static void EnsureScriptResourceRequest(string path) {
            if (!IsScriptResourceRequest(path)) {
                Throw404();
            } 
        }
 
        private static Assembly GetAssembly(string assemblyName) { 
            Debug.Assert(!String.IsNullOrEmpty(assemblyName));
            string[] parts = assemblyName.Split(','); 

            if ((parts.Length != 1) && (parts.Length != 4)) {
                Throw404();
            } 

            AssemblyName realName = new AssemblyName(); 
            realName.Name = parts[0]; 
            if (parts.Length == 4) {
                realName.Version = new Version(parts[1]); 
                string cultureString = parts[2];
                realName.CultureInfo = (cultureString.Length > 0) ?
                    new CultureInfo(cultureString) :
                    CultureInfo.InvariantCulture; 
                realName.SetPublicKeyToken(HexParser.Parse(parts[3]));
            } 
            Assembly assembly = null; 
            try {
                assembly = Assembly.Load(realName); 
            }
            catch (FileNotFoundException fnf) {
                Throw404(fnf);
            } 
            catch (FileLoadException fl) {
                Throw404(fl); 
            } 
            catch (BadImageFormatException badImage) {
                Throw404(badImage); 
            }

            return assembly;
        } 

        private static Pair GetAssemblyInfo(Assembly assembly) { 
            Pair assemblyInfo = 
                (Pair)_assemblyInfoCache[assembly];
            if (assemblyInfo == null) { 
                assemblyInfo = GetAssemblyInfoInternal(assembly);
                _assemblyInfoCache[assembly] = assemblyInfo;
            }
            Debug.Assert(assemblyInfo != null, "Assembly info should not be null"); 
            return assemblyInfo;
        } 
 
        private static Pair GetAssemblyInfoInternal(Assembly assembly) {
            AssemblyName assemblyName = new AssemblyName(assembly.FullName); 
            string hash = Convert.ToBase64String(new Hash(assembly).SHA1);
            return new Pair(assemblyName, hash);
        }
 
        private static CultureInfo GetAssemblyNeutralCulture(Assembly assembly) {
            CultureInfo neutralCulture = (CultureInfo)_cultureCache[assembly]; 
            if (neutralCulture == null) { 
                object[] nrlas = assembly.GetCustomAttributes(typeof(NeutralResourcesLanguageAttribute), false);
                if ((nrlas != null) && (nrlas.Length != 0)) { 
                    neutralCulture = CultureInfo.GetCultureInfo(
                        ((NeutralResourcesLanguageAttribute)nrlas[0]).CultureName);
                    _cultureCache[assembly] = neutralCulture;
                } 
            }
            return neutralCulture; 
        } 

        internal static string GetEmptyPageUrl(string title) { 
            return GetScriptResourceHandler().GetEmptyPageUrl(title);
        }

        private static IScriptResourceHandler GetScriptResourceHandler() { 
            if (_scriptResourceHandler == null) {
                _scriptResourceHandler = new RuntimeScriptResourceHandler(); 
            } 
            return _scriptResourceHandler;
        } 

        internal static string GetScriptResourceUrl(
            Assembly assembly,
            string resourceName, 
            CultureInfo culture,
            bool zip) { 
 
            return GetScriptResourceHandler()
                .GetScriptResourceUrl(assembly, resourceName, culture, zip); 
        }

        internal static string GetScriptResourceUrl(
            List>>> assemblyResourceLists, 
            bool zip) {
 
            return GetScriptResourceHandler().GetScriptResourceUrl(assemblyResourceLists, zip); 
        }
 
        protected virtual bool IsReusable {
            get {
                return true;
            } 
        }
 
        internal delegate string VirtualFileReader(string virtualPath, out Encoding encoding); 

        private static bool IsCompressionEnabled(HttpContext context) { 
            return ScriptingScriptResourceHandlerSection.ApplicationSettings.EnableCompression &&
                ((context == null) ||
                !context.Request.Browser.IsBrowser("IE") ||
                (context.Request.Browser.MajorVersion > 6)); 
        }
 
        internal static bool IsScriptResourceRequest(string path) { 
            return !String.IsNullOrEmpty(path) &&
                String.Equals(path, ScriptResourceAbsolutePath, StringComparison.OrdinalIgnoreCase); 
        }

        private static void OutputEmptyPage(HttpResponse response, string title) {
            PrepareResponseCache(response); 
            response.Write(@"
" + 
                           HttpUtility.HtmlEncode(title) + 
                           @"");
        } 

        private static void PrepareResponseCache(HttpResponse response) {
            HttpCachePolicy cachePolicy = response.Cache;
            DateTime now = DateTime.Now; 
            cachePolicy.SetCacheability(HttpCacheability.Public);
            cachePolicy.VaryByParams["d"] = true; 
            cachePolicy.SetOmitVaryStar(true); 
            cachePolicy.SetExpires(now + TimeSpan.FromDays(365));
            cachePolicy.SetValidUntilExpires(true); 
            cachePolicy.SetLastModified(now);
        }

        private static void PrepareResponseNoCache(HttpResponse response) { 
            HttpCachePolicy cachePolicy = response.Cache;
            DateTime now = DateTime.Now; 
            cachePolicy.SetCacheability(HttpCacheability.Public); 
            cachePolicy.SetExpires(now + TimeSpan.FromDays(365));
            cachePolicy.SetValidUntilExpires(true); 
            cachePolicy.SetLastModified(now);
            cachePolicy.SetNoServerCaching();
        }
 
        [SecuritySafeCritical]
        protected virtual void ProcessRequest(HttpContext context) { 
            HttpResponse response = context.Response; 
            response.Clear();
 
            // Checking that the handler is not being called from a different path.
            EnsureScriptResourceRequest(context.Request.Path);

            ProcessRequestInternal(response, context.Request.QueryString, 
                new VirtualFileReader(delegate(string virtualPath, out Encoding encoding) {
                VirtualPathProvider vpp = HostingEnvironment.VirtualPathProvider; 
                if (!vpp.FileExists(virtualPath)) { 
                    Throw404();
                } 
                VirtualFile file = vpp.GetFile(virtualPath);
                using (Stream stream = file.Open()) {
                    using (StreamReader reader = new StreamReader(stream, true)) {
                        encoding = reader.CurrentEncoding; 
                        return reader.ReadToEnd();
                    } 
                } 
            })
            ); 
        }

        private static void ProcessRequestInternal(
            HttpResponse response, 
            NameValueCollection queryString,
            VirtualFileReader fileReader) { 
 
            string decryptedString = DecryptParameter(queryString);
            if (String.IsNullOrEmpty(decryptedString)) { 
                Throw404();
            }
            bool zip;
            bool singleAssemblyReference; 
            // See GetScriptResourceUrl comment below for first character meanings.
            switch (decryptedString[0]) { 
                case 'Z': 
                case 'z':
                    singleAssemblyReference = true; 
                    zip = true;
                    break;
                case 'U':
                case 'u': 
                    singleAssemblyReference = true;
                    zip = false; 
                    break; 
                case 'Q':
                case 'q': 
                    singleAssemblyReference = false;
                    zip = true;
                    break;
                case 'R': 
                case 'r':
                    singleAssemblyReference = false; 
                    zip = false; 
                    break;
                case 'T': 
                    OutputEmptyPage(response, decryptedString.Substring(1));
                    return;
                default:
                    Throw404(); 
                    return;
            } 
 
            decryptedString = decryptedString.Substring(1);
            if (String.IsNullOrEmpty(decryptedString)) { 
                Throw404();
            }
            string[] decryptedData = decryptedString.Split('|');
 
            if (singleAssemblyReference) {
                // expected: ||[|#|] 
                if (decryptedData.Length != 3 && decryptedData.Length != 5) { 
                    // The decrypted data must have 3 parts plus an optional 2 part hash code separated by pipes.
                    Throw404(); 
                }
            }
            else {
                // expected: |,,,,...||,,,,...|#| 
                if (decryptedData.Length % 2 != 0) {
                    // The decrypted data must have an even number of parts separated by pipes. 
                    Throw404(); 
                }
            } 

            StringBuilder script = new StringBuilder();

            string firstContentType = null; 

            if (singleAssemblyReference) { 
                // single assembly reference, format is 
                // ||
                string assemblyName = decryptedData[0]; 
                string resourceName = decryptedData[1];
                string cultureName = decryptedData[2];

                Assembly assembly = GetAssembly(assemblyName); 
                if (assembly == null) {
                    Throw404(); 
                } 

                script.Append(ScriptResourceAttribute.GetScriptFromWebResourceInternal( 
                    assembly,
                    resourceName,
                    String.IsNullOrEmpty(cultureName) ? CultureInfo.InvariantCulture : new CultureInfo(cultureName),
                    zip, 
                    out firstContentType
                )); 
            } 
            else {
                // composite script reference, format is: 
                // |,,,,...||,,,,...
                // Assembly is empty for path based scripts, and their resource/culture list is ,,...

                // If an assembly starts with "#", the segment is ignored (expected that this includes a hash to ensure 
                // url uniqueness when resources are changed). Also, for forward compatibility '#' segments may contain
                // other data. 
 
                bool needsNewline = false;
 
                for (int i = 0; i < decryptedData.Length; i += 2) {
                    string assemblyName = decryptedData[i];
                    bool hasAssembly = !String.IsNullOrEmpty(assemblyName);
                    if (hasAssembly && assemblyName[0] == '#') { 
                        // hash segments are ignored, it contains a hash code for url uniqueness
                        continue; 
                    } 
                    Debug.Assert(!String.IsNullOrEmpty(decryptedData[i + 1]));
                    string[] resourcesAndCultures = decryptedData[i + 1].Split(','); 

                    if (resourcesAndCultures.Length == 0) {
                        Throw404();
                    } 

                    Assembly assembly = hasAssembly ? GetAssembly(assemblyName) : null; 
 
                    if (assembly == null) {
                        // The scripts are path-based 
                        if (firstContentType == null) {
                            firstContentType = "text/javascript";
                        }
                        for (int j = 0; j < resourcesAndCultures.Length; j++) { 
                            Encoding encoding;
                            // DevDiv Bugs 197242 
                            // path will either be absolute, as in "/app/foo/bar.js" or app relative, as in "~/foo/bar.js" 
                            // ToAbsolute() ensures it is in the form /app/foo/bar.js
                            // This conversion was not done when the url was created to conserve url length. 
                            string path = _bypassVirtualPathResolution ?
                                resourcesAndCultures[j] :
                                VirtualPathUtility.ToAbsolute(resourcesAndCultures[j]);
                            string fileContents = fileReader(path, out encoding); 

                            if (needsNewline) { 
                                // Output an additional newline between resources but not for the last one 
                                script.Append('\n');
                            } 
                            needsNewline = true;

                            script.Append(fileContents);
                        } 
                    }
                    else { 
                        Debug.Assert(resourcesAndCultures.Length % 2 == 0, "The list of resource names and cultures must have an even number of parts separated by commas."); 

                        for (int j = 0; j < resourcesAndCultures.Length; j += 2) { 
                            try {
                                string contentType;
                                string resourceName = resourcesAndCultures[j];
                                string cultureName = resourcesAndCultures[j + 1]; 

                                if (needsNewline) { 
                                    // Output an additional newline between resources but not for the last one 
                                    script.Append('\n');
                                } 
                                needsNewline = true;

                                script.Append(ScriptResourceAttribute.GetScriptFromWebResourceInternal(
                                    assembly, 
                                    resourceName,
                                    String.IsNullOrEmpty(cultureName) ? CultureInfo.InvariantCulture : new CultureInfo(cultureName), 
                                    zip, 
                                    out contentType
                                )); 

                                if (firstContentType == null) {
                                    firstContentType = contentType;
                                } 
                            }
                            catch (MissingManifestResourceException ex) { 
                                throw Create404(ex); 
                            }
                            catch (HttpException ex) { 
                                throw Create404(ex);
                            }
                        }
                    } 
                }
            } 
 
            if (ScriptingScriptResourceHandlerSection.ApplicationSettings.EnableCaching) {
                PrepareResponseCache(response); 
            }
            else {
                PrepareResponseNoCache(response);
            } 

            response.ContentType = firstContentType; 
 
            if (zip) {
                using (MemoryStream zipped = new MemoryStream()) { 
                    using (Stream outputStream = new GZipStream(zipped, CompressionMode.Compress)) {
                        // The choice of an encoding matters little here.
                        // Input streams being of potentially different encodings, UTF-8 is the better
                        // choice as it's the natural encoding for JavaScript. 
                        using (StreamWriter writer = new StreamWriter(outputStream, Encoding.UTF8)) {
                            writer.Write(script.ToString()); 
                        } 
                    }
                    byte[] zippedBytes = zipped.ToArray(); 
                    response.AddHeader("Content-encoding", "gzip");
                    response.OutputStream.Write(zippedBytes, 0, zippedBytes.Length);
                }
            } 
            else {
                // Bug DevDiv #175061, we don't want to force any encoding here and let the default 
                // encoding apply no matter what the incoming scripts might have been encoded with. 
                response.Write(script.ToString());
            } 
        }

        internal static void SetScriptResourceHandler(IScriptResourceHandler scriptResourceHandler) {
            _scriptResourceHandler = scriptResourceHandler; 
        }
 
        private static void Throw404() { 
            throw Create404(null);
        } 

        private static void Throw404(Exception innerException) {
            throw Create404(innerException);
        } 

        #region IHttpHandler implementation 
        void IHttpHandler.ProcessRequest(HttpContext context) { 
            ProcessRequest(context);
        } 

        bool IHttpHandler.IsReusable {
            get {
                return IsReusable; 
            }
        } 
        #endregion 

        private class RuntimeScriptResourceHandler : IScriptResourceHandler { 

            private static readonly IDictionary _urlCache = Hashtable.Synchronized(new Hashtable());
            private static readonly IDictionary _cultureCache = Hashtable.Synchronized(new Hashtable());
            private static string _absoluteScriptResourceUrl; 

            string IScriptResourceHandler.GetScriptResourceUrl( 
                Assembly assembly, string resourceName, CultureInfo culture, bool zip) { 

                return ((IScriptResourceHandler)this).GetScriptResourceUrl( 
                    new List>>>() {
                        new Pair>>(
                            assembly,
                            new List>() { 
                                new Pair(resourceName, culture)
                            } 
                        ) 
                    }, zip);
            } 

            string IScriptResourceHandler.GetScriptResourceUrl(
                List>>> assemblyResourceLists,
                bool zip) { 

                if (!IsCompressionEnabled(HttpContext.Current)) { 
                    zip = false; 
                }
 
                bool allAssemblyResources = true;
                foreach (Pair>> assemblyData in assemblyResourceLists) {
                    if (assemblyData.First == null) {
                        allAssemblyResources = false; 
                        break;
                    } 
                } 

                // If all the scripts are assembly resources, we can cache the generated ScriptResource URL, since 
                // the appdomain will reset if any of the assemblies are changed.  We cannot cache the URL if any
                // scripts are path-based, since the cache entry will not be removed if a path-based script is changed.
                if (allAssemblyResources) {
                    List cacheKeys = new List(); 

                    foreach (Pair>> assemblyData in assemblyResourceLists) { 
                        cacheKeys.Add(assemblyData.First); 
                        foreach (Pair resourceAndCulture in assemblyData.Second) {
                            cacheKeys.Add(resourceAndCulture.First); 
                            cacheKeys.Add(resourceAndCulture.Second);
                        }
                    }
 
                    cacheKeys.Add(zip);
 
                    Tuple cacheKey = new Tuple(cacheKeys.ToArray()); 
                    string url = (string)_urlCache[cacheKey];
 
                    if (url == null) {
                        url = GetScriptResourceUrlImpl(assemblyResourceLists, zip);
                        _urlCache[cacheKey] = url;
                    } 

                    return url; 
                } 
                else {
                    return GetScriptResourceUrlImpl(assemblyResourceLists, zip); 
                }
            }

            [SecuritySafeCritical] 
            private static string GetScriptResourceUrlImpl(
                List>>> assemblyResourceLists, 
                bool zip) { 

                EnsureAbsoluteScriptResourceUrl(); 

                // If there's only a single assembly resource, format is
                //      [Z|U|z|u]||
                // If there are multiple resources, or a single resource that is path based, format is 
                //      [Q|R|q|r]|,,,...||,,,...
                // A path based reference has no assembly (empty). 
                // (the Q/R indicators used in place of Z/U give the handler indiciation that the url is a composite 
                // reference, and allows for System.Web.Extensions SP1 to maintain compatibility with RTM, should a
                // single resource be encrypted with SP1 and decrypted with RTM). 

                bool singleAssemblyResource = false;
                if (assemblyResourceLists.Count == 1) {
                    // only one assembly to pull from... 
                    var reference = assemblyResourceLists[0];
                    if ((reference.First != null) && (reference.Second.Count == 1)) { 
                        // resource is assembly not path, and there's only one resource within it to load 
                        singleAssemblyResource = true;
                    } 
                }

                // Next character of the encoded string is:
                // Format: S = Single Assembly Reference, C = Composite Reference or Single Path Reference 
                // Zip: compress or not (true or false)
                // First    Format  Zip? 
                // ===================== 
                // Z        S       T
                // U        S       F 
                // Q        C       T
                // R        C       F

                string indicator; 
                if (singleAssemblyResource) {
                    indicator = (zip ? "Z" : "U"); 
                } 
                else {
                    indicator = (zip ? "Q" : "R"); 
                }

                StringBuilder url = new StringBuilder(indicator);
 
                HashCodeCombiner hashCombiner = new HashCodeCombiner();
 
                bool firstAssembly = true; 
                foreach (Pair>> assemblyData in assemblyResourceLists) {
 
                    if (!firstAssembly) {
                        url.Append('|');
                    }
                    else { 
                        firstAssembly = false;
                    } 
                    if (assemblyData.First != null) { 
                        Pair assemblyInfo = GetAssemblyInfo(assemblyData.First);
 
                        AssemblyName assemblyName = (AssemblyName)assemblyInfo.First;
                        string assemblyHash = (String)assemblyInfo.Second;
                        hashCombiner.AddObject(assemblyHash);
 
                        if (assemblyData.First.GlobalAssemblyCache) {
                            // If the assembly is in the GAC, we need to store a full name to load the assembly later 
                            // Pack the necessary values into a more compact format than FullName 
                            url.Append(assemblyName.Name);
                            url.Append(','); 
                            url.Append(assemblyName.Version);
                            url.Append(',');
                            if (assemblyName.CultureInfo != null) {
                                url.Append(assemblyName.CultureInfo); 
                            }
                            url.Append(','); 
                            url.Append(HexParser.ToString(assemblyName.GetPublicKeyToken())); 
                        }
                        else { 
                            // Otherwise, we can just use a partial name
                            url.Append(assemblyName.Name);
                        }
                    } 
                    url.Append('|');
 
                    bool firstResource = true; 
                    foreach (Pair resourceAndCulture in assemblyData.Second) {
 
                        if (!firstResource) {
                            url.Append(',');
                        }
 
                        if (assemblyData.First != null) {
                            url.Append(resourceAndCulture.First); 
                            Tuple cacheKey = new Tuple( 
                                assemblyData.First,
                                resourceAndCulture.First, 
                                resourceAndCulture.Second
                            );
                            string cultureName = (string)_cultureCache[cacheKey];
                            if (cultureName == null) { 
                                // Check if the resources exist
                                ScriptResourceInfo resourceInfo = 
                                    ScriptResourceInfo.GetInstance(assemblyData.First, resourceAndCulture.First); 
                                if (resourceInfo == ScriptResourceInfo.Empty) {
                                    ThrowUnknownResource(resourceAndCulture.First); 
                                }
                                Stream scriptStream = assemblyData.First.GetManifestResourceStream(resourceInfo.ScriptName);
                                if (scriptStream == null) {
                                    ThrowUnknownResource(resourceAndCulture.First); 
                                }
                                cultureName = DetermineNearestAvailableCulture( 
                                    assemblyData.First, resourceAndCulture.First, resourceAndCulture.Second).Name; 
                                _cultureCache[cacheKey] = cultureName;
                            } 
                            url.Append(singleAssemblyResource ? "|" : ",");
                            url.Append(cultureName);
                        }
                        else { 
                            Debug.Assert(!singleAssemblyResource, "This should never happen since this is a path reference.");
 
                            if (!_bypassVirtualPathResolution) { 
                                VirtualPathProvider vpp = HostingEnvironment.VirtualPathProvider;
                                if (!vpp.FileExists(resourceAndCulture.First)) { 
                                    ThrowUnknownResource(resourceAndCulture.First);
                                }
                                string hash = vpp.GetFileHash(resourceAndCulture.First, new string[] { resourceAndCulture.First });
                                hashCombiner.AddObject(hash); 
                            }
                            url.Append(resourceAndCulture.First); 
                        } 
                        firstResource = false;
                    } 
                }

                // DevDiv Bugs 186624: The hash code needs to be part of the encrypted blob for composite scripts
                // because we cache the composite script on the server using a VaryByParam["d"]. Otherwise, if a 
                // path based script in the composite changes, only the 't' parameter would change, which would
                // cause a new request to the server, but it would be served via cache since 'd' would be the same. 
                // This isn't a problem for assembly based resources since changing them also restarts the app and 
                // clears the cache. We do not vary by 't' because that makes it possible to flood the server cache
                // with cache entries, since anything could be used for 't'. Putting the hash in 'd' ensures a different 
                // url and different cache entry when a script changes, but without the possibility of flooding
                // the server cache.

                // However, we continue to use the 't' parameter for single assembly references for compatibility. 

                string resourceUrl; 
                if (singleAssemblyResource) { 
                    resourceUrl = _absoluteScriptResourceUrl +
                            Page.EncryptString(url.ToString()) + 
                            "&t=" + hashCombiner.CombinedHashString;
                }
                else {
                    // note that CombinedHashString is hex, it will never include a '|' that would confuse the handler. 
                    url.Append("|#|");
                    url.Append(hashCombiner.CombinedHashString); 
                    resourceUrl = _absoluteScriptResourceUrl + 
                            Page.EncryptString(url.ToString());
                } 

                if (resourceUrl.Length > _maximumResourceUrlLength) {
                    throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, AtlasWeb.ScriptResourceHandler_ResourceUrlTooLong, _maximumResourceUrlLength));
                } 
                return resourceUrl;
            } 
 
            private static void EnsureAbsoluteScriptResourceUrl() {
                if (_absoluteScriptResourceUrl == null) { 
                    _absoluteScriptResourceUrl = _bypassVirtualPathResolution ?
                        _scriptResourceUrl + "?d=" :
                        VirtualPathUtility.ToAbsolute(_scriptResourceUrl) + "?d=";
                } 
            }
 
            string IScriptResourceHandler.GetEmptyPageUrl(string title) { 
                EnsureAbsoluteScriptResourceUrl();
                return _absoluteScriptResourceUrl + 
                        Page.EncryptString('T' + title);
            }

            private static void ThrowUnknownResource(string resourceName) { 
                throw new HttpException(String.Format(CultureInfo.CurrentCulture,
                    AtlasWeb.ScriptResourceHandler_UnknownResource, resourceName)); 
            } 
        }
    } 
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//------------------------------------------------------------------------------ 
// 
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//----------------------------------------------------------------------------- 

namespace System.Web.Handlers { 
    using System; 
    using System.Collections;
    using System.Collections.Generic; 
    using System.Collections.Specialized;
    using System.Globalization;
    using System.IO;
    using System.IO.Compression; 
    using System.Reflection;
    using System.Resources; 
    using System.Security; 
    using System.Security.Cryptography;
    using System.Security.Permissions; 
    using System.Security.Policy;
    using System.Text;
    using System.Web;
    using System.Web.Configuration; 
    using System.Web.Hosting;
    using System.Web.Resources; 
    using System.Web.UI; 
    using System.Web.Util;
    using Debug = System.Diagnostics.Debug; 
    using Tuple = System.Web.Util.Tuple;

    public class ScriptResourceHandler : IHttpHandler {
 
        private const string _scriptResourceUrl = "~/ScriptResource.axd";
        private static readonly IDictionary _assemblyInfoCache = Hashtable.Synchronized(new Hashtable()); 
        private static readonly IDictionary _cultureCache = Hashtable.Synchronized(new Hashtable()); 
        private static readonly Object _getMethodLock = new Object();
        private static IScriptResourceHandler _scriptResourceHandler = new RuntimeScriptResourceHandler(); 
        private static string _scriptResourceAbsolutePath;
        // _bypassVirtualPathResolution set by unit tests to avoid resolving ~/ paths from unit tests.
        private static bool _bypassVirtualPathResolution = false;
        private static int _maximumResourceUrlLength = 2048; 

        private static string ScriptResourceAbsolutePath { 
            get { 
                if (_scriptResourceAbsolutePath == null) {
                    _scriptResourceAbsolutePath = VirtualPathUtility.ToAbsolute(_scriptResourceUrl); 
                }
                return _scriptResourceAbsolutePath;
            }
        } 

        private static Exception Create404(Exception innerException) { 
            return new HttpException(404, AtlasWeb.ScriptResourceHandler_InvalidRequest, innerException); 
        }
 
        private static string DecryptParameter(NameValueCollection queryString) {
            string encryptedData = queryString["d"];
            if (String.IsNullOrEmpty(encryptedData)) {
                Throw404(); 
            }
 
            try { 
                return Page.DecryptString(encryptedData);
            } 
            catch (CryptographicException ex) {
                throw Create404(ex);
            }
        } 

        internal static CultureInfo DetermineNearestAvailableCulture( 
            Assembly assembly, 
            string scriptResourceName,
            CultureInfo culture) { 

            if (String.IsNullOrEmpty(scriptResourceName)) return CultureInfo.InvariantCulture;

            Tuple cacheKey = new Tuple(assembly, scriptResourceName, culture); 
            CultureInfo cachedCulture = (CultureInfo)_cultureCache[cacheKey];
            if (cachedCulture == null) { 
 
                string releaseResourceName =
                    scriptResourceName.EndsWith(".debug.js", StringComparison.OrdinalIgnoreCase) ? 
                    scriptResourceName.Substring(0, scriptResourceName.Length - 9) + ".js" :
                    null;

                ScriptResourceInfo resourceInfo = ScriptResourceInfo.GetInstance(assembly, scriptResourceName); 
                ScriptResourceInfo releaseResourceInfo = (releaseResourceName != null) ?
                    ScriptResourceInfo.GetInstance(assembly, releaseResourceName) : null; 
 
                if (!String.IsNullOrEmpty(resourceInfo.ScriptResourceName) ||
                    ((releaseResourceInfo != null) && !String.IsNullOrEmpty(releaseResourceInfo.ScriptResourceName))) { 

                    ResourceManager resourceManager =
                        ScriptResourceAttribute.GetResourceManager(resourceInfo.ScriptResourceName, assembly);
                    ResourceManager releaseResourceManager = (releaseResourceInfo != null) ? 
                        ScriptResourceAttribute.GetResourceManager(releaseResourceInfo.ScriptResourceName, assembly) : null;
 
                    ResourceSet localizedSet = null; 
                    ResourceSet releaseSet = null;
                    if (resourceManager != null) { 
                        resourceManager.GetResourceSet(CultureInfo.InvariantCulture, true, true);
                        // Look for the explicitly localized version of the resources that is nearest the culture.
                        localizedSet = resourceManager.GetResourceSet(culture, true, false);
                    } 
                    if (releaseResourceManager != null) {
                        releaseResourceManager.GetResourceSet(CultureInfo.InvariantCulture, true, true); 
                        // Look for the explicitly localized version of the resources that is nearest the culture. 
                        releaseSet = releaseResourceManager.GetResourceSet(culture, true, false);
                    } 
                    if ((resourceManager != null) || (releaseResourceManager != null)) {
                        while ((localizedSet == null) && (releaseSet == null)) {
                            culture = culture.Parent;
                            if (culture.Equals(CultureInfo.InvariantCulture)) break; 
                            localizedSet = resourceManager.GetResourceSet(culture, true, false);
                            releaseSet = (releaseResourceManager != null) ? 
                                releaseResourceManager.GetResourceSet(culture, true, false) : null; 
                        }
                    } 
                    else {
                        culture = CultureInfo.InvariantCulture;
                    }
                } 
                else {
                    culture = CultureInfo.InvariantCulture; 
                } 
                // Neutral assembly culture falls back on invariant
                CultureInfo neutralCulture = GetAssemblyNeutralCulture(assembly); 
                if ((neutralCulture != null) && neutralCulture.Equals(culture)) {
                    culture = CultureInfo.InvariantCulture;
                }
                cachedCulture = culture; 
                _cultureCache[cacheKey] = cachedCulture;
            } 
            return cachedCulture; 
        }
 
        private static void EnsureScriptResourceRequest(string path) {
            if (!IsScriptResourceRequest(path)) {
                Throw404();
            } 
        }
 
        private static Assembly GetAssembly(string assemblyName) { 
            Debug.Assert(!String.IsNullOrEmpty(assemblyName));
            string[] parts = assemblyName.Split(','); 

            if ((parts.Length != 1) && (parts.Length != 4)) {
                Throw404();
            } 

            AssemblyName realName = new AssemblyName(); 
            realName.Name = parts[0]; 
            if (parts.Length == 4) {
                realName.Version = new Version(parts[1]); 
                string cultureString = parts[2];
                realName.CultureInfo = (cultureString.Length > 0) ?
                    new CultureInfo(cultureString) :
                    CultureInfo.InvariantCulture; 
                realName.SetPublicKeyToken(HexParser.Parse(parts[3]));
            } 
            Assembly assembly = null; 
            try {
                assembly = Assembly.Load(realName); 
            }
            catch (FileNotFoundException fnf) {
                Throw404(fnf);
            } 
            catch (FileLoadException fl) {
                Throw404(fl); 
            } 
            catch (BadImageFormatException badImage) {
                Throw404(badImage); 
            }

            return assembly;
        } 

        private static Pair GetAssemblyInfo(Assembly assembly) { 
            Pair assemblyInfo = 
                (Pair)_assemblyInfoCache[assembly];
            if (assemblyInfo == null) { 
                assemblyInfo = GetAssemblyInfoInternal(assembly);
                _assemblyInfoCache[assembly] = assemblyInfo;
            }
            Debug.Assert(assemblyInfo != null, "Assembly info should not be null"); 
            return assemblyInfo;
        } 
 
        private static Pair GetAssemblyInfoInternal(Assembly assembly) {
            AssemblyName assemblyName = new AssemblyName(assembly.FullName); 
            string hash = Convert.ToBase64String(new Hash(assembly).SHA1);
            return new Pair(assemblyName, hash);
        }
 
        private static CultureInfo GetAssemblyNeutralCulture(Assembly assembly) {
            CultureInfo neutralCulture = (CultureInfo)_cultureCache[assembly]; 
            if (neutralCulture == null) { 
                object[] nrlas = assembly.GetCustomAttributes(typeof(NeutralResourcesLanguageAttribute), false);
                if ((nrlas != null) && (nrlas.Length != 0)) { 
                    neutralCulture = CultureInfo.GetCultureInfo(
                        ((NeutralResourcesLanguageAttribute)nrlas[0]).CultureName);
                    _cultureCache[assembly] = neutralCulture;
                } 
            }
            return neutralCulture; 
        } 

        internal static string GetEmptyPageUrl(string title) { 
            return GetScriptResourceHandler().GetEmptyPageUrl(title);
        }

        private static IScriptResourceHandler GetScriptResourceHandler() { 
            if (_scriptResourceHandler == null) {
                _scriptResourceHandler = new RuntimeScriptResourceHandler(); 
            } 
            return _scriptResourceHandler;
        } 

        internal static string GetScriptResourceUrl(
            Assembly assembly,
            string resourceName, 
            CultureInfo culture,
            bool zip) { 
 
            return GetScriptResourceHandler()
                .GetScriptResourceUrl(assembly, resourceName, culture, zip); 
        }

        internal static string GetScriptResourceUrl(
            List>>> assemblyResourceLists, 
            bool zip) {
 
            return GetScriptResourceHandler().GetScriptResourceUrl(assemblyResourceLists, zip); 
        }
 
        protected virtual bool IsReusable {
            get {
                return true;
            } 
        }
 
        internal delegate string VirtualFileReader(string virtualPath, out Encoding encoding); 

        private static bool IsCompressionEnabled(HttpContext context) { 
            return ScriptingScriptResourceHandlerSection.ApplicationSettings.EnableCompression &&
                ((context == null) ||
                !context.Request.Browser.IsBrowser("IE") ||
                (context.Request.Browser.MajorVersion > 6)); 
        }
 
        internal static bool IsScriptResourceRequest(string path) { 
            return !String.IsNullOrEmpty(path) &&
                String.Equals(path, ScriptResourceAbsolutePath, StringComparison.OrdinalIgnoreCase); 
        }

        private static void OutputEmptyPage(HttpResponse response, string title) {
            PrepareResponseCache(response); 
            response.Write(@"
" + 
                           HttpUtility.HtmlEncode(title) + 
                           @"");
        } 

        private static void PrepareResponseCache(HttpResponse response) {
            HttpCachePolicy cachePolicy = response.Cache;
            DateTime now = DateTime.Now; 
            cachePolicy.SetCacheability(HttpCacheability.Public);
            cachePolicy.VaryByParams["d"] = true; 
            cachePolicy.SetOmitVaryStar(true); 
            cachePolicy.SetExpires(now + TimeSpan.FromDays(365));
            cachePolicy.SetValidUntilExpires(true); 
            cachePolicy.SetLastModified(now);
        }

        private static void PrepareResponseNoCache(HttpResponse response) { 
            HttpCachePolicy cachePolicy = response.Cache;
            DateTime now = DateTime.Now; 
            cachePolicy.SetCacheability(HttpCacheability.Public); 
            cachePolicy.SetExpires(now + TimeSpan.FromDays(365));
            cachePolicy.SetValidUntilExpires(true); 
            cachePolicy.SetLastModified(now);
            cachePolicy.SetNoServerCaching();
        }
 
        [SecuritySafeCritical]
        protected virtual void ProcessRequest(HttpContext context) { 
            HttpResponse response = context.Response; 
            response.Clear();
 
            // Checking that the handler is not being called from a different path.
            EnsureScriptResourceRequest(context.Request.Path);

            ProcessRequestInternal(response, context.Request.QueryString, 
                new VirtualFileReader(delegate(string virtualPath, out Encoding encoding) {
                VirtualPathProvider vpp = HostingEnvironment.VirtualPathProvider; 
                if (!vpp.FileExists(virtualPath)) { 
                    Throw404();
                } 
                VirtualFile file = vpp.GetFile(virtualPath);
                using (Stream stream = file.Open()) {
                    using (StreamReader reader = new StreamReader(stream, true)) {
                        encoding = reader.CurrentEncoding; 
                        return reader.ReadToEnd();
                    } 
                } 
            })
            ); 
        }

        private static void ProcessRequestInternal(
            HttpResponse response, 
            NameValueCollection queryString,
            VirtualFileReader fileReader) { 
 
            string decryptedString = DecryptParameter(queryString);
            if (String.IsNullOrEmpty(decryptedString)) { 
                Throw404();
            }
            bool zip;
            bool singleAssemblyReference; 
            // See GetScriptResourceUrl comment below for first character meanings.
            switch (decryptedString[0]) { 
                case 'Z': 
                case 'z':
                    singleAssemblyReference = true; 
                    zip = true;
                    break;
                case 'U':
                case 'u': 
                    singleAssemblyReference = true;
                    zip = false; 
                    break; 
                case 'Q':
                case 'q': 
                    singleAssemblyReference = false;
                    zip = true;
                    break;
                case 'R': 
                case 'r':
                    singleAssemblyReference = false; 
                    zip = false; 
                    break;
                case 'T': 
                    OutputEmptyPage(response, decryptedString.Substring(1));
                    return;
                default:
                    Throw404(); 
                    return;
            } 
 
            decryptedString = decryptedString.Substring(1);
            if (String.IsNullOrEmpty(decryptedString)) { 
                Throw404();
            }
            string[] decryptedData = decryptedString.Split('|');
 
            if (singleAssemblyReference) {
                // expected: ||[|#|] 
                if (decryptedData.Length != 3 && decryptedData.Length != 5) { 
                    // The decrypted data must have 3 parts plus an optional 2 part hash code separated by pipes.
                    Throw404(); 
                }
            }
            else {
                // expected: |,,,,...||,,,,...|#| 
                if (decryptedData.Length % 2 != 0) {
                    // The decrypted data must have an even number of parts separated by pipes. 
                    Throw404(); 
                }
            } 

            StringBuilder script = new StringBuilder();

            string firstContentType = null; 

            if (singleAssemblyReference) { 
                // single assembly reference, format is 
                // ||
                string assemblyName = decryptedData[0]; 
                string resourceName = decryptedData[1];
                string cultureName = decryptedData[2];

                Assembly assembly = GetAssembly(assemblyName); 
                if (assembly == null) {
                    Throw404(); 
                } 

                script.Append(ScriptResourceAttribute.GetScriptFromWebResourceInternal( 
                    assembly,
                    resourceName,
                    String.IsNullOrEmpty(cultureName) ? CultureInfo.InvariantCulture : new CultureInfo(cultureName),
                    zip, 
                    out firstContentType
                )); 
            } 
            else {
                // composite script reference, format is: 
                // |,,,,...||,,,,...
                // Assembly is empty for path based scripts, and their resource/culture list is ,,...

                // If an assembly starts with "#", the segment is ignored (expected that this includes a hash to ensure 
                // url uniqueness when resources are changed). Also, for forward compatibility '#' segments may contain
                // other data. 
 
                bool needsNewline = false;
 
                for (int i = 0; i < decryptedData.Length; i += 2) {
                    string assemblyName = decryptedData[i];
                    bool hasAssembly = !String.IsNullOrEmpty(assemblyName);
                    if (hasAssembly && assemblyName[0] == '#') { 
                        // hash segments are ignored, it contains a hash code for url uniqueness
                        continue; 
                    } 
                    Debug.Assert(!String.IsNullOrEmpty(decryptedData[i + 1]));
                    string[] resourcesAndCultures = decryptedData[i + 1].Split(','); 

                    if (resourcesAndCultures.Length == 0) {
                        Throw404();
                    } 

                    Assembly assembly = hasAssembly ? GetAssembly(assemblyName) : null; 
 
                    if (assembly == null) {
                        // The scripts are path-based 
                        if (firstContentType == null) {
                            firstContentType = "text/javascript";
                        }
                        for (int j = 0; j < resourcesAndCultures.Length; j++) { 
                            Encoding encoding;
                            // DevDiv Bugs 197242 
                            // path will either be absolute, as in "/app/foo/bar.js" or app relative, as in "~/foo/bar.js" 
                            // ToAbsolute() ensures it is in the form /app/foo/bar.js
                            // This conversion was not done when the url was created to conserve url length. 
                            string path = _bypassVirtualPathResolution ?
                                resourcesAndCultures[j] :
                                VirtualPathUtility.ToAbsolute(resourcesAndCultures[j]);
                            string fileContents = fileReader(path, out encoding); 

                            if (needsNewline) { 
                                // Output an additional newline between resources but not for the last one 
                                script.Append('\n');
                            } 
                            needsNewline = true;

                            script.Append(fileContents);
                        } 
                    }
                    else { 
                        Debug.Assert(resourcesAndCultures.Length % 2 == 0, "The list of resource names and cultures must have an even number of parts separated by commas."); 

                        for (int j = 0; j < resourcesAndCultures.Length; j += 2) { 
                            try {
                                string contentType;
                                string resourceName = resourcesAndCultures[j];
                                string cultureName = resourcesAndCultures[j + 1]; 

                                if (needsNewline) { 
                                    // Output an additional newline between resources but not for the last one 
                                    script.Append('\n');
                                } 
                                needsNewline = true;

                                script.Append(ScriptResourceAttribute.GetScriptFromWebResourceInternal(
                                    assembly, 
                                    resourceName,
                                    String.IsNullOrEmpty(cultureName) ? CultureInfo.InvariantCulture : new CultureInfo(cultureName), 
                                    zip, 
                                    out contentType
                                )); 

                                if (firstContentType == null) {
                                    firstContentType = contentType;
                                } 
                            }
                            catch (MissingManifestResourceException ex) { 
                                throw Create404(ex); 
                            }
                            catch (HttpException ex) { 
                                throw Create404(ex);
                            }
                        }
                    } 
                }
            } 
 
            if (ScriptingScriptResourceHandlerSection.ApplicationSettings.EnableCaching) {
                PrepareResponseCache(response); 
            }
            else {
                PrepareResponseNoCache(response);
            } 

            response.ContentType = firstContentType; 
 
            if (zip) {
                using (MemoryStream zipped = new MemoryStream()) { 
                    using (Stream outputStream = new GZipStream(zipped, CompressionMode.Compress)) {
                        // The choice of an encoding matters little here.
                        // Input streams being of potentially different encodings, UTF-8 is the better
                        // choice as it's the natural encoding for JavaScript. 
                        using (StreamWriter writer = new StreamWriter(outputStream, Encoding.UTF8)) {
                            writer.Write(script.ToString()); 
                        } 
                    }
                    byte[] zippedBytes = zipped.ToArray(); 
                    response.AddHeader("Content-encoding", "gzip");
                    response.OutputStream.Write(zippedBytes, 0, zippedBytes.Length);
                }
            } 
            else {
                // Bug DevDiv #175061, we don't want to force any encoding here and let the default 
                // encoding apply no matter what the incoming scripts might have been encoded with. 
                response.Write(script.ToString());
            } 
        }

        internal static void SetScriptResourceHandler(IScriptResourceHandler scriptResourceHandler) {
            _scriptResourceHandler = scriptResourceHandler; 
        }
 
        private static void Throw404() { 
            throw Create404(null);
        } 

        private static void Throw404(Exception innerException) {
            throw Create404(innerException);
        } 

        #region IHttpHandler implementation 
        void IHttpHandler.ProcessRequest(HttpContext context) { 
            ProcessRequest(context);
        } 

        bool IHttpHandler.IsReusable {
            get {
                return IsReusable; 
            }
        } 
        #endregion 

        private class RuntimeScriptResourceHandler : IScriptResourceHandler { 

            private static readonly IDictionary _urlCache = Hashtable.Synchronized(new Hashtable());
            private static readonly IDictionary _cultureCache = Hashtable.Synchronized(new Hashtable());
            private static string _absoluteScriptResourceUrl; 

            string IScriptResourceHandler.GetScriptResourceUrl( 
                Assembly assembly, string resourceName, CultureInfo culture, bool zip) { 

                return ((IScriptResourceHandler)this).GetScriptResourceUrl( 
                    new List>>>() {
                        new Pair>>(
                            assembly,
                            new List>() { 
                                new Pair(resourceName, culture)
                            } 
                        ) 
                    }, zip);
            } 

            string IScriptResourceHandler.GetScriptResourceUrl(
                List>>> assemblyResourceLists,
                bool zip) { 

                if (!IsCompressionEnabled(HttpContext.Current)) { 
                    zip = false; 
                }
 
                bool allAssemblyResources = true;
                foreach (Pair>> assemblyData in assemblyResourceLists) {
                    if (assemblyData.First == null) {
                        allAssemblyResources = false; 
                        break;
                    } 
                } 

                // If all the scripts are assembly resources, we can cache the generated ScriptResource URL, since 
                // the appdomain will reset if any of the assemblies are changed.  We cannot cache the URL if any
                // scripts are path-based, since the cache entry will not be removed if a path-based script is changed.
                if (allAssemblyResources) {
                    List cacheKeys = new List(); 

                    foreach (Pair>> assemblyData in assemblyResourceLists) { 
                        cacheKeys.Add(assemblyData.First); 
                        foreach (Pair resourceAndCulture in assemblyData.Second) {
                            cacheKeys.Add(resourceAndCulture.First); 
                            cacheKeys.Add(resourceAndCulture.Second);
                        }
                    }
 
                    cacheKeys.Add(zip);
 
                    Tuple cacheKey = new Tuple(cacheKeys.ToArray()); 
                    string url = (string)_urlCache[cacheKey];
 
                    if (url == null) {
                        url = GetScriptResourceUrlImpl(assemblyResourceLists, zip);
                        _urlCache[cacheKey] = url;
                    } 

                    return url; 
                } 
                else {
                    return GetScriptResourceUrlImpl(assemblyResourceLists, zip); 
                }
            }

            [SecuritySafeCritical] 
            private static string GetScriptResourceUrlImpl(
                List>>> assemblyResourceLists, 
                bool zip) { 

                EnsureAbsoluteScriptResourceUrl(); 

                // If there's only a single assembly resource, format is
                //      [Z|U|z|u]||
                // If there are multiple resources, or a single resource that is path based, format is 
                //      [Q|R|q|r]|,,,...||,,,...
                // A path based reference has no assembly (empty). 
                // (the Q/R indicators used in place of Z/U give the handler indiciation that the url is a composite 
                // reference, and allows for System.Web.Extensions SP1 to maintain compatibility with RTM, should a
                // single resource be encrypted with SP1 and decrypted with RTM). 

                bool singleAssemblyResource = false;
                if (assemblyResourceLists.Count == 1) {
                    // only one assembly to pull from... 
                    var reference = assemblyResourceLists[0];
                    if ((reference.First != null) && (reference.Second.Count == 1)) { 
                        // resource is assembly not path, and there's only one resource within it to load 
                        singleAssemblyResource = true;
                    } 
                }

                // Next character of the encoded string is:
                // Format: S = Single Assembly Reference, C = Composite Reference or Single Path Reference 
                // Zip: compress or not (true or false)
                // First    Format  Zip? 
                // ===================== 
                // Z        S       T
                // U        S       F 
                // Q        C       T
                // R        C       F

                string indicator; 
                if (singleAssemblyResource) {
                    indicator = (zip ? "Z" : "U"); 
                } 
                else {
                    indicator = (zip ? "Q" : "R"); 
                }

                StringBuilder url = new StringBuilder(indicator);
 
                HashCodeCombiner hashCombiner = new HashCodeCombiner();
 
                bool firstAssembly = true; 
                foreach (Pair>> assemblyData in assemblyResourceLists) {
 
                    if (!firstAssembly) {
                        url.Append('|');
                    }
                    else { 
                        firstAssembly = false;
                    } 
                    if (assemblyData.First != null) { 
                        Pair assemblyInfo = GetAssemblyInfo(assemblyData.First);
 
                        AssemblyName assemblyName = (AssemblyName)assemblyInfo.First;
                        string assemblyHash = (String)assemblyInfo.Second;
                        hashCombiner.AddObject(assemblyHash);
 
                        if (assemblyData.First.GlobalAssemblyCache) {
                            // If the assembly is in the GAC, we need to store a full name to load the assembly later 
                            // Pack the necessary values into a more compact format than FullName 
                            url.Append(assemblyName.Name);
                            url.Append(','); 
                            url.Append(assemblyName.Version);
                            url.Append(',');
                            if (assemblyName.CultureInfo != null) {
                                url.Append(assemblyName.CultureInfo); 
                            }
                            url.Append(','); 
                            url.Append(HexParser.ToString(assemblyName.GetPublicKeyToken())); 
                        }
                        else { 
                            // Otherwise, we can just use a partial name
                            url.Append(assemblyName.Name);
                        }
                    } 
                    url.Append('|');
 
                    bool firstResource = true; 
                    foreach (Pair resourceAndCulture in assemblyData.Second) {
 
                        if (!firstResource) {
                            url.Append(',');
                        }
 
                        if (assemblyData.First != null) {
                            url.Append(resourceAndCulture.First); 
                            Tuple cacheKey = new Tuple( 
                                assemblyData.First,
                                resourceAndCulture.First, 
                                resourceAndCulture.Second
                            );
                            string cultureName = (string)_cultureCache[cacheKey];
                            if (cultureName == null) { 
                                // Check if the resources exist
                                ScriptResourceInfo resourceInfo = 
                                    ScriptResourceInfo.GetInstance(assemblyData.First, resourceAndCulture.First); 
                                if (resourceInfo == ScriptResourceInfo.Empty) {
                                    ThrowUnknownResource(resourceAndCulture.First); 
                                }
                                Stream scriptStream = assemblyData.First.GetManifestResourceStream(resourceInfo.ScriptName);
                                if (scriptStream == null) {
                                    ThrowUnknownResource(resourceAndCulture.First); 
                                }
                                cultureName = DetermineNearestAvailableCulture( 
                                    assemblyData.First, resourceAndCulture.First, resourceAndCulture.Second).Name; 
                                _cultureCache[cacheKey] = cultureName;
                            } 
                            url.Append(singleAssemblyResource ? "|" : ",");
                            url.Append(cultureName);
                        }
                        else { 
                            Debug.Assert(!singleAssemblyResource, "This should never happen since this is a path reference.");
 
                            if (!_bypassVirtualPathResolution) { 
                                VirtualPathProvider vpp = HostingEnvironment.VirtualPathProvider;
                                if (!vpp.FileExists(resourceAndCulture.First)) { 
                                    ThrowUnknownResource(resourceAndCulture.First);
                                }
                                string hash = vpp.GetFileHash(resourceAndCulture.First, new string[] { resourceAndCulture.First });
                                hashCombiner.AddObject(hash); 
                            }
                            url.Append(resourceAndCulture.First); 
                        } 
                        firstResource = false;
                    } 
                }

                // DevDiv Bugs 186624: The hash code needs to be part of the encrypted blob for composite scripts
                // because we cache the composite script on the server using a VaryByParam["d"]. Otherwise, if a 
                // path based script in the composite changes, only the 't' parameter would change, which would
                // cause a new request to the server, but it would be served via cache since 'd' would be the same. 
                // This isn't a problem for assembly based resources since changing them also restarts the app and 
                // clears the cache. We do not vary by 't' because that makes it possible to flood the server cache
                // with cache entries, since anything could be used for 't'. Putting the hash in 'd' ensures a different 
                // url and different cache entry when a script changes, but without the possibility of flooding
                // the server cache.

                // However, we continue to use the 't' parameter for single assembly references for compatibility. 

                string resourceUrl; 
                if (singleAssemblyResource) { 
                    resourceUrl = _absoluteScriptResourceUrl +
                            Page.EncryptString(url.ToString()) + 
                            "&t=" + hashCombiner.CombinedHashString;
                }
                else {
                    // note that CombinedHashString is hex, it will never include a '|' that would confuse the handler. 
                    url.Append("|#|");
                    url.Append(hashCombiner.CombinedHashString); 
                    resourceUrl = _absoluteScriptResourceUrl + 
                            Page.EncryptString(url.ToString());
                } 

                if (resourceUrl.Length > _maximumResourceUrlLength) {
                    throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, AtlasWeb.ScriptResourceHandler_ResourceUrlTooLong, _maximumResourceUrlLength));
                } 
                return resourceUrl;
            } 
 
            private static void EnsureAbsoluteScriptResourceUrl() {
                if (_absoluteScriptResourceUrl == null) { 
                    _absoluteScriptResourceUrl = _bypassVirtualPathResolution ?
                        _scriptResourceUrl + "?d=" :
                        VirtualPathUtility.ToAbsolute(_scriptResourceUrl) + "?d=";
                } 
            }
 
            string IScriptResourceHandler.GetEmptyPageUrl(string title) { 
                EnsureAbsoluteScriptResourceUrl();
                return _absoluteScriptResourceUrl + 
                        Page.EncryptString('T' + title);
            }

            private static void ThrowUnknownResource(string resourceName) { 
                throw new HttpException(String.Format(CultureInfo.CurrentCulture,
                    AtlasWeb.ScriptResourceHandler_UnknownResource, resourceName)); 
            } 
        }
    } 
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.

                        

                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK