1 //------------------------------------------------------------------------------
2 // <copyright file="AssemblyResourceLoader.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 //------------------------------------------------------------------------------
6 namespace System.Web.Handlers {
8 using System.Collections;
9 using System.Collections.Specialized;
10 using System.Globalization;
12 using System.Reflection;
13 using System.Security.Permissions;
15 using System.Text.RegularExpressions;
17 using System.Web.Compilation;
18 using System.Web.Configuration;
19 using System.Web.Hosting;
20 using System.Web.Management;
21 using System.Web.RegularExpressions;
22 using System.Web.Security.Cryptography;
24 using System.Web.Util;
28 /// Provides a way to load client-side resources from assemblies
30 public sealed class AssemblyResourceLoader : IHttpHandler {
31 private const string _webResourceUrl = "WebResource.axd";
33 private readonly static Regex webResourceRegex = new WebResourceRegex();
35 private static IDictionary _urlCache = Hashtable.Synchronized(new Hashtable());
36 private static IDictionary _assemblyInfoCache = Hashtable.Synchronized(new Hashtable());
37 private static IDictionary _webResourceCache = Hashtable.Synchronized(new Hashtable());
38 private static IDictionary _typeAssemblyCache = Hashtable.Synchronized(new Hashtable());
40 // This group of fields is used for backwards compatibility. In v1.x you could
41 // technically customize the files in the /aspnet_client/ folder whereas in v2.x
42 // we serve those files using WebResource.axd. These fields are used to check
43 // if there is a customized version of the file and use that instead of the resource.
44 private static bool _webFormsScriptChecked;
45 private static VirtualPath _webFormsScriptLocation;
46 private static bool _webUIValidationScriptChecked;
47 private static VirtualPath _webUIValidationScriptLocation;
48 private static bool _smartNavScriptChecked;
49 private static VirtualPath _smartNavScriptLocation;
50 private static bool _smartNavPageChecked;
51 private static VirtualPath _smartNavPageLocation;
53 private static bool _handlerExistenceChecked;
54 private static bool _handlerExists;
55 // set by unit tests to avoid dependency on httpruntime.
56 internal static string _applicationRootPath;
58 private static bool DebugMode {
60 return HttpContext.Current.IsDebuggingEnabled;
65 /// Create a cache key for the UrlCache.
67 /// requirement: If assembly1 and assembly2 represent the same assembly,
68 /// then they must be the same object; otherwise this method will fail to generate
69 /// a unique cache key.
71 private static int CreateWebResourceUrlCacheKey(Assembly assembly, string resourceName,
72 bool htmlEncoded, bool forSubstitution, bool enableCdn, bool debuggingEnabled, bool secureConnection) {
73 int hashCode = HashCodeCombiner.CombineHashCodes(
74 assembly.GetHashCode(),
75 resourceName.GetHashCode(),
76 htmlEncoded.GetHashCode(),
77 forSubstitution.GetHashCode(),
78 enableCdn.GetHashCode());
79 return HashCodeCombiner.CombineHashCodes(hashCode,
80 debuggingEnabled.GetHashCode(),
81 secureConnection.GetHashCode());
85 /// Validates that the WebResource.axd handler is registered in config and actually
86 /// points to the correct handler type.
88 private static void EnsureHandlerExistenceChecked() {
89 // First we have to check that the handler is registered:
90 // <add path="WebResource.axd" verb="GET" type="System.Web.Handlers.AssemblyResourceLoader" validate="True" />
91 if (!_handlerExistenceChecked) {
92 HttpContext context = HttpContext.Current;
93 IIS7WorkerRequest iis7WorkerRequest = (context != null) ? context.WorkerRequest as IIS7WorkerRequest : null;
94 string webResourcePath = UrlPath.Combine(HttpRuntime.AppDomainAppVirtualPathString, _webResourceUrl);
95 if (iis7WorkerRequest != null) {
96 // check the IIS <handlers> section by mapping the handler
97 string handlerTypeString = iis7WorkerRequest.MapHandlerAndGetHandlerTypeString(method: "GET",
98 path: UrlPath.Combine(HttpRuntime.AppDomainAppVirtualPathString, _webResourceUrl),
99 convertNativeStaticFileModule: false, ignoreWildcardMappings: true);
100 if (!String.IsNullOrEmpty(handlerTypeString)) {
101 _handlerExists = (typeof(AssemblyResourceLoader) == BuildManager.GetType(handlerTypeString, true /*throwOnFail*/, false /*ignoreCase*/));
105 // check the <httpHandlers> section
106 HttpHandlerAction httpHandler = RuntimeConfig.GetConfig(VirtualPath.Create(webResourcePath)).HttpHandlers.FindMapping("GET", VirtualPath.Create(_webResourceUrl));
107 _handlerExists = (httpHandler != null) && (httpHandler.TypeInternal == typeof(AssemblyResourceLoader));
109 _handlerExistenceChecked = true;
114 /// Performs the actual putting together of the resource reference URL.
116 private static string FormatWebResourceUrl(string assemblyName, string resourceName, long assemblyDate, bool htmlEncoded) {
117 string encryptedData = Page.EncryptString(assemblyName + "|" + resourceName, Purpose.AssemblyResourceLoader_WebResourceUrl);
119 return String.Format(CultureInfo.InvariantCulture, _webResourceUrl + "?d={0}&t={1}",
124 return String.Format(CultureInfo.InvariantCulture, _webResourceUrl + "?d={0}&t={1}",
130 internal static Assembly GetAssemblyFromType(Type type) {
131 Assembly assembly = (Assembly)_typeAssemblyCache[type];
132 if (assembly == null) {
133 assembly = type.Assembly;
134 _typeAssemblyCache[type] = assembly;
139 private static Pair GetAssemblyInfo(Assembly assembly) {
140 Pair assemblyInfo = _assemblyInfoCache[assembly] as Pair;
141 if (assemblyInfo == null) {
142 assemblyInfo = GetAssemblyInfoWithAssertInternal(assembly);
143 _assemblyInfoCache[assembly] = assemblyInfo;
145 Debug.Assert(assemblyInfo != null, "Assembly info should not be null");
149 [FileIOPermission(SecurityAction.Assert, Unrestricted = true)]
150 private static Pair GetAssemblyInfoWithAssertInternal(Assembly assembly) {
151 AssemblyName assemblyName = assembly.GetName();
152 long assemblyDate = File.GetLastWriteTime(new Uri(assemblyName.CodeBase).LocalPath).Ticks;
153 Pair assemblyInfo = new Pair(assemblyName, assemblyDate);
158 /// Gets the virtual path of a physical resource file. Null is
159 /// returned if the resource does not exist.
160 /// We assert full FileIOPermission so that we can map paths.
162 [FileIOPermission(SecurityAction.Assert, Unrestricted = true)]
163 private static VirtualPath GetDiskResourcePath(string resourceName) {
164 VirtualPath clientScriptsLocation = Util.GetScriptLocation();
165 VirtualPath resourceVirtualPath = clientScriptsLocation.SimpleCombine(resourceName);
166 string resourcePhysicalPath = resourceVirtualPath.MapPath();
167 if (File.Exists(resourcePhysicalPath)) {
168 return resourceVirtualPath;
175 internal static string GetWebResourceUrl(Type type, string resourceName) {
176 return GetWebResourceUrl(type, resourceName, false, null);
179 internal static string GetWebResourceUrl(Type type, string resourceName, bool htmlEncoded) {
180 return GetWebResourceUrl(type, resourceName, htmlEncoded, null);
183 internal static string GetWebResourceUrl(Type type, string resourceName, bool htmlEncoded, IScriptManager scriptManager) {
184 bool enableCdn = (scriptManager != null && scriptManager.EnableCdn);
185 return GetWebResourceUrl(type, resourceName, htmlEncoded, scriptManager, enableCdn: enableCdn);
189 /// Gets a URL resource reference to a client-side resource
191 internal static string GetWebResourceUrl(Type type, string resourceName, bool htmlEncoded, IScriptManager scriptManager, bool enableCdn) {
192 Assembly assembly = GetAssemblyFromType(type);
193 Debug.Assert(assembly != null, "Type.Assembly should never be null.");
195 // If the resource request is for System.Web.dll and more specifically
196 // it is for a file that we shipped in v1.x, we have to check if a
197 // customized copy of the file exists. See notes at the top of the file
199 if (assembly == typeof(AssemblyResourceLoader).Assembly) {
200 if (String.Equals(resourceName, "WebForms.js", StringComparison.Ordinal)) {
201 if (!_webFormsScriptChecked) {
202 _webFormsScriptLocation = GetDiskResourcePath(resourceName);
203 _webFormsScriptChecked = true;
205 if (_webFormsScriptLocation != null) {
206 return _webFormsScriptLocation.VirtualPathString;
209 else if (String.Equals(resourceName, "WebUIValidation.js", StringComparison.Ordinal)) {
210 if (!_webUIValidationScriptChecked) {
211 _webUIValidationScriptLocation = GetDiskResourcePath(resourceName);
212 _webUIValidationScriptChecked = true;
214 if (_webUIValidationScriptLocation != null) {
215 return _webUIValidationScriptLocation.VirtualPathString;
218 else if (String.Equals(resourceName, "SmartNav.htm", StringComparison.Ordinal)) {
219 if (!_smartNavPageChecked) {
220 _smartNavPageLocation = GetDiskResourcePath(resourceName);
221 _smartNavPageChecked = true;
223 if (_smartNavPageLocation != null) {
224 return _smartNavPageLocation.VirtualPathString;
227 else if (String.Equals(resourceName, "SmartNav.js", StringComparison.Ordinal)) {
228 if (!_smartNavScriptChecked) {
229 _smartNavScriptLocation = GetDiskResourcePath(resourceName);
230 _smartNavScriptChecked = true;
232 if (_smartNavScriptLocation != null) {
233 return _smartNavScriptLocation.VirtualPathString;
238 return GetWebResourceUrlInternal(assembly, resourceName, htmlEncoded, false, scriptManager, enableCdn);
241 private static WebResourceAttribute FindWebResourceAttribute(Assembly assembly, string resourceName) {
242 object[] attrs = assembly.GetCustomAttributes(false);
243 for (int i = 0; i < attrs.Length; i++) {
244 WebResourceAttribute wra = attrs[i] as WebResourceAttribute;
245 if ((wra != null) && String.Equals(wra.WebResource, resourceName, StringComparison.Ordinal)) {
252 internal static string FormatCdnUrl(Assembly assembly, string cdnPath) {
253 // {0} = Short Assembly Name
254 // {1} = Assembly Version
255 // {2} = Assembly File Version
256 // use a new AssemblyName because assembly.GetName() doesn't work in medium trust.
257 AssemblyName assemblyName = new AssemblyName(assembly.FullName);
258 return String.Format(CultureInfo.InvariantCulture,
260 HttpUtility.UrlEncode(assemblyName.Name),
261 HttpUtility.UrlEncode(assemblyName.Version.ToString(4)),
262 HttpUtility.UrlEncode(AssemblyUtil.GetAssemblyFileVersion(assembly)));
265 private static string GetCdnPath(string resourceName, Assembly assembly, bool secureConnection) {
266 string cdnPath = null;
267 WebResourceAttribute wra = FindWebResourceAttribute(assembly, resourceName);
269 cdnPath = secureConnection ? wra.CdnPathSecureConnection : wra.CdnPath;
270 if (!String.IsNullOrEmpty(cdnPath)) {
271 cdnPath = FormatCdnUrl(assembly, cdnPath);
277 internal static string GetWebResourceUrlInternal(Assembly assembly, string resourceName,
278 bool htmlEncoded, bool forSubstitution, IScriptManager scriptManager) {
280 bool enableCdn = (scriptManager != null && scriptManager.EnableCdn);
281 return GetWebResourceUrlInternal(assembly, resourceName, htmlEncoded, forSubstitution, scriptManager, enableCdn: enableCdn);
284 internal static string GetWebResourceUrlInternal(Assembly assembly, string resourceName,
285 bool htmlEncoded, bool forSubstitution, IScriptManager scriptManager, bool enableCdn) {
286 // When this url is being inserted as a substitution in another resource,
287 // it should just be "WebResource.axd?d=..." since the resource is already coming
288 // from the app root (i.e. no need for a full absolute /app/WebResource.axd).
289 // Otherwise we must return a path that is absolute (starts with '/') or
290 // a full absolute uri (http://..) as in the case of a CDN Path.
292 EnsureHandlerExistenceChecked();
293 if (!_handlerExists) {
294 throw new InvalidOperationException(SR.GetString(SR.AssemblyResourceLoader_HandlerNotRegistered));
296 Assembly effectiveAssembly = assembly;
297 string effectiveResourceName = resourceName;
299 bool debuggingEnabled = false;
300 bool secureConnection;
301 if (scriptManager != null) {
302 debuggingEnabled = scriptManager.IsDebuggingEnabled;
303 secureConnection = scriptManager.IsSecureConnection;
306 secureConnection = ((HttpContext.Current != null) && (HttpContext.Current.Request != null) &&
307 HttpContext.Current.Request.IsSecureConnection);
308 debuggingEnabled = (HttpContext.Current != null) && HttpContext.Current.IsDebuggingEnabled;
310 int urlCacheKey = CreateWebResourceUrlCacheKey(assembly, resourceName, htmlEncoded,
311 forSubstitution, enableCdn, debuggingEnabled, secureConnection);
313 string url = (string)_urlCache[urlCacheKey];
316 IScriptResourceDefinition definition = null;
317 if (ClientScriptManager._scriptResourceMapping != null) {
318 definition = ClientScriptManager._scriptResourceMapping.GetDefinition(resourceName, assembly);
319 if (definition != null) {
320 if (!String.IsNullOrEmpty(definition.ResourceName)) {
321 effectiveResourceName = definition.ResourceName;
323 if (definition.ResourceAssembly != null) {
324 effectiveAssembly = definition.ResourceAssembly;
329 // if a resource mapping exists, take it's settings into consideration
330 // it might supply a path or a cdnpath.
331 if (definition != null) {
333 // Winner is first path defined, falling back on the effectiveResourceName/Assembly
334 // Debug Mode : d.CdnDebugPath, d.DebugPath, *wra.CdnPath, d.Path
335 // Release Mode: d.CdnPath , *wra.CdnPath, d.Path
336 // * the WebResourceAttribute corresponding to the resource defined in the definition, not the
337 // the original resource.
338 // Also, if the definition has a CdnPath but it cannot be converted to a secure one during https,
339 // the WRA's CdnPath is not considered.
340 if (debuggingEnabled) {
341 path = secureConnection ? definition.CdnDebugPathSecureConnection : definition.CdnDebugPath;
342 if (String.IsNullOrEmpty(path)) {
343 path = definition.DebugPath;
344 if (String.IsNullOrEmpty(path)) {
345 // Get CDN Path from the redirected resource name/assembly, not the original one,
346 // but not if this is a secure connection and the only reason we didn't use the definition
347 // cdn path is because it doesnt support secure connections.
348 if (!secureConnection || String.IsNullOrEmpty(definition.CdnDebugPath)) {
349 path = GetCdnPath(effectiveResourceName, effectiveAssembly, secureConnection);
351 if (String.IsNullOrEmpty(path)) {
352 path = definition.Path;
358 path = secureConnection ? definition.CdnPathSecureConnection : definition.CdnPath;
359 if (String.IsNullOrEmpty(path)) {
360 // Get CDN Path from the redirected resource name/assembly, not the original one
361 // but not if this is a secure connection and the only reason we didn't use the definition
362 // cdn path is because it doesnt support secure connections.
363 if (!secureConnection || String.IsNullOrEmpty(definition.CdnPath)) {
364 path = GetCdnPath(effectiveResourceName, effectiveAssembly, secureConnection);
366 if (String.IsNullOrEmpty(path)) {
367 path = definition.Path;
373 // Winner is first path defined, falling back on the effectiveResourceName/Assembly
374 // Debug Mode : d.DebugPath, d.Path
375 // Release Mode: d.Path
376 if (debuggingEnabled) {
377 path = definition.DebugPath;
378 if (String.IsNullOrEmpty(path)) {
379 path = definition.Path;
383 path = definition.Path;
386 } // does not have definition
387 else if (enableCdn) {
388 path = GetCdnPath(effectiveResourceName, effectiveAssembly, secureConnection);
391 if (!String.IsNullOrEmpty(path)) {
392 // assembly based resource has been overridden by a path,
393 // whether that be a CDN Path or a definition.Path or DebugPath.
394 // We must return a path that is absolute (starts with '/') or
395 // a full absolute uri (http://..) as in the case of a CDN Path.
396 // An overridden Path that is not a CDN Path is required to be absolute
398 if (UrlPath.IsAppRelativePath(path)) {
399 // expand ~/. If it is rooted (/) or an absolute uri, no conversion needed
400 if (_applicationRootPath == null) {
401 url = VirtualPathUtility.ToAbsolute(path);
404 url = VirtualPathUtility.ToAbsolute(path, _applicationRootPath);
408 // must be a full uri or already rooted.
412 url = HttpUtility.HtmlEncode(url);
416 string urlAssemblyName;
417 Pair assemblyInfo = GetAssemblyInfo(effectiveAssembly);
418 AssemblyName assemblyName = (AssemblyName)assemblyInfo.First;
419 long assemblyDate = (long)assemblyInfo.Second;
420 string assemblyVersion = assemblyName.Version.ToString();
422 if (effectiveAssembly.GlobalAssemblyCache) {
423 // If the assembly is in the GAC, we need to store a full name to load the assembly later
424 if (effectiveAssembly == HttpContext.SystemWebAssembly) {
425 urlAssemblyName = "s";
428 // Pack the necessary values into a more compact format than FullName
429 StringBuilder builder = new StringBuilder();
431 builder.Append(assemblyName.Name);
433 builder.Append(assemblyVersion);
435 if (assemblyName.CultureInfo != null) {
436 builder.Append(assemblyName.CultureInfo.ToString());
439 byte[] token = assemblyName.GetPublicKeyToken();
440 for (int i = 0; i < token.Length; i++) {
441 builder.Append(token[i].ToString("x2", CultureInfo.InvariantCulture));
443 urlAssemblyName = builder.ToString();
447 // Otherwise, we can just use a partial name
448 urlAssemblyName = "p" + assemblyName.Name;
450 url = FormatWebResourceUrl(urlAssemblyName, effectiveResourceName, assemblyDate, htmlEncoded);
451 if (!forSubstitution && (HttpRuntime.AppDomainAppVirtualPathString != null)) {
452 // When this url is being inserted as a substitution in another resource,
453 // it should just be "WebResource.axd?d=..." since the resource is already coming
454 // from the app root (i.e. no need for a full absolute /app/WebResource.axd).
455 url = UrlPath.Combine(HttpRuntime.AppDomainAppVirtualPathString, url);
458 _urlCache[urlCacheKey] = url;
463 internal static bool IsValidWebResourceRequest(HttpContext context) {
464 EnsureHandlerExistenceChecked();
465 if (!_handlerExists) {
466 // If the handler isn't properly registered, it can't
467 // possibly be a valid web resource request.
471 string webResourceHandlerUrl = UrlPath.Combine(HttpRuntime.AppDomainAppVirtualPathString, _webResourceUrl);
472 string requestPath = context.Request.Path;
473 if (String.Equals(requestPath, webResourceHandlerUrl, StringComparison.OrdinalIgnoreCase)) {
480 internal static void LogWebResourceFailure(string decryptedData, Exception exception) {
481 string errorMessage = null;
482 if (decryptedData != null) {
483 errorMessage = SR.GetString(SR.Webevent_msg_RuntimeErrorWebResourceFailure_ResourceMissing, decryptedData);
486 errorMessage = SR.GetString(SR.Webevent_msg_RuntimeErrorWebResourceFailure_DecryptionError);
488 WebBaseEvent.RaiseSystemEvent(message: errorMessage,
490 eventCode: WebEventCodes.RuntimeErrorWebResourceFailure,
491 eventDetailCode: WebEventCodes.UndefinedEventDetailCode,
492 exception: exception);
496 bool IHttpHandler.IsReusable {
504 void IHttpHandler.ProcessRequest(HttpContext context) {
505 // Make sure we don't get any extra content in this handler (like Application.BeginRequest stuff);
506 context.Response.Clear();
508 Stream resourceStream = null;
509 string decryptedData = null;
510 bool resourceIdentifierPresent = false;
512 Exception exception = null;
514 NameValueCollection queryString = context.Request.QueryString;
516 string encryptedData = queryString["d"];
517 if (String.IsNullOrEmpty(encryptedData)) {
518 throw new HttpException(404, SR.GetString(SR.AssemblyResourceLoader_InvalidRequest));
520 resourceIdentifierPresent = true;
522 decryptedData = Page.DecryptString(encryptedData, Purpose.AssemblyResourceLoader_WebResourceUrl);
524 int separatorIndex = decryptedData.IndexOf('|');
525 Debug.Assert(separatorIndex != -1, "The decrypted data must contain a separator.");
527 string assemblyName = decryptedData.Substring(0, separatorIndex);
528 if (String.IsNullOrEmpty(assemblyName)) {
529 throw new HttpException(404, SR.GetString(SR.AssemblyResourceLoader_AssemblyNotFound, assemblyName));
532 string resourceName = decryptedData.Substring(separatorIndex + 1);
533 if (String.IsNullOrEmpty(resourceName)) {
534 throw new HttpException(404, SR.GetString(SR.AssemblyResourceLoader_ResourceNotFound, resourceName));
537 char nameType = assemblyName[0];
538 assemblyName = assemblyName.Substring(1);
540 Assembly assembly = null;
542 // If it was a full name, create an AssemblyName and load from that
543 if (nameType == 'f') {
544 string[] parts = assemblyName.Split(',');
546 if (parts.Length != 4) {
547 throw new HttpException(404, SR.GetString(SR.AssemblyResourceLoader_InvalidRequest));
550 AssemblyName realName = new AssemblyName();
551 realName.Name = parts[0];
552 realName.Version = new Version(parts[1]);
553 string cultureString = parts[2];
555 // Try to determine the culture, using the invariant culture if there wasn't one (doesn't work without it)
556 if (cultureString.Length > 0) {
557 realName.CultureInfo = new CultureInfo(cultureString);
560 realName.CultureInfo = CultureInfo.InvariantCulture;
563 // Parse up the public key token which is represented as hex bytes in a string
564 string token = parts[3];
565 byte[] tokenBytes = new byte[token.Length / 2];
566 for (int i = 0; i < tokenBytes.Length; i++) {
567 tokenBytes[i] = Byte.Parse(token.Substring(i * 2, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture);
569 realName.SetPublicKeyToken(tokenBytes);
571 assembly = Assembly.Load(realName);
573 // System.Web special case
574 else if (nameType == 's') {
575 assembly = typeof(AssemblyResourceLoader).Assembly;
577 // If was a partial name, just try to load it
578 else if (nameType == 'p') {
579 assembly = Assembly.Load(assemblyName);
582 throw new HttpException(404, SR.GetString(SR.AssemblyResourceLoader_InvalidRequest));
585 // Dev10 Bugs 602949: Throw 404 if resource not found rather than do nothing.
586 // This is done before creating the cache entry, since it could be that the assembly is loaded
587 // later on without the app restarting.
588 if (assembly == null) {
589 throw new HttpException(404, SR.GetString(SR.AssemblyResourceLoader_InvalidRequest));
592 bool performSubstitution = false;
593 bool validResource = false;
594 string contentType = String.Empty;
596 // Check the validation cache to see if the resource has already been validated
597 int cacheKey = HashCodeCombiner.CombineHashCodes(assembly.GetHashCode(), resourceName.GetHashCode());
598 Triplet resourceTriplet = (Triplet)_webResourceCache[cacheKey];
599 if (resourceTriplet != null) {
600 validResource = (bool)resourceTriplet.First;
601 contentType = (string)resourceTriplet.Second;
602 performSubstitution = (bool)resourceTriplet.Third;
605 // Validation cache is empty, find out if it's valid and add it to the cache
606 WebResourceAttribute wra = FindWebResourceAttribute(assembly, resourceName);
608 resourceName = wra.WebResource;
609 validResource = true;
610 contentType = wra.ContentType;
611 performSubstitution = wra.PerformSubstitution;
614 // Cache the result so we don't have to do this again
617 // a WebResourceAttribute was found, but does the resource really exist?
618 validResource = false;
619 resourceStream = assembly.GetManifestResourceStream(resourceName);
620 validResource = (resourceStream != null);
624 // Cache the results, even if there was an exception getting the stream,
625 // so we don't have to do this again
626 Triplet triplet = new Triplet();
627 triplet.First = validResource;
628 triplet.Second = contentType;
629 triplet.Third = performSubstitution;
630 _webResourceCache[cacheKey] = triplet;
635 // Cache the resource so we don't keep processing the same requests
636 HttpCachePolicy cachePolicy = context.Response.Cache;
637 cachePolicy.SetCacheability(HttpCacheability.Public);
638 cachePolicy.VaryByParams["d"] = true;
639 cachePolicy.SetOmitVaryStar(true);
640 cachePolicy.SetExpires(DateTime.Now + TimeSpan.FromDays(365));
641 cachePolicy.SetValidUntilExpires(true);
642 Pair assemblyInfo = GetAssemblyInfo(assembly);
643 cachePolicy.SetLastModified(new DateTime((long)assemblyInfo.Second));
645 StreamReader reader = null;
647 if (resourceStream == null) {
648 // null in the case that _webResourceCache had the item
649 resourceStream = assembly.GetManifestResourceStream(resourceName);
651 if (resourceStream != null) {
652 context.Response.ContentType = contentType;
654 if (performSubstitution) {
656 reader = new StreamReader(resourceStream, true);
658 string content = reader.ReadToEnd();
660 // Looking for something of the form: WebResource("resourcename")
661 MatchCollection matches = webResourceRegex.Matches(content);
663 StringBuilder newContent = new StringBuilder();
664 foreach (Match match in matches) {
665 newContent.Append(content.Substring(startIndex, match.Index - startIndex));
667 Group group = match.Groups["resourceName"];
669 string embeddedResourceName = group.ToString();
670 if (embeddedResourceName.Length > 0) {
672 if (String.Equals(embeddedResourceName, resourceName, StringComparison.Ordinal)) {
673 throw new HttpException(404, SR.GetString(SR.AssemblyResourceLoader_NoCircularReferences, resourceName));
675 newContent.Append(GetWebResourceUrlInternal(assembly, embeddedResourceName, htmlEncoded: false, forSubstitution: true, scriptManager: null));
679 startIndex = match.Index + match.Length;
682 newContent.Append(content.Substring(startIndex, content.Length - startIndex));
684 StreamWriter writer = new StreamWriter(context.Response.OutputStream, reader.CurrentEncoding);
685 writer.Write(newContent.ToString());
689 byte[] buffer = new byte[1024];
690 Stream outputStream = context.Response.OutputStream;
693 count = resourceStream.Read(buffer, 0, 1024);
694 outputStream.Write(buffer, 0, count);
696 outputStream.Flush();
703 if (resourceStream != null)
704 resourceStream.Close();
710 // MSRC 10405: ---- all errors in the event of failure. In particular, we don't want to
711 // bubble the inner exceptions up in the YSOD, as they might contain sensitive cryptographic
712 // information. Setting 'resourceStream' to null will cause an appropriate exception to
714 resourceStream = null;
717 // Dev10 Bugs 602949: 404 if the assembly is not found or if the resource does not exist
718 if (resourceStream == null) {
719 if (resourceIdentifierPresent) {
720 LogWebResourceFailure(decryptedData, exception);
722 throw new HttpException(404, SR.GetString(SR.AssemblyResourceLoader_InvalidRequest));
725 context.Response.IgnoreFurtherWrites();