|
//------------------------------------------------------------------------------
// <copyright file="AssemblyResourceLoader.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
namespace System.Web.Handlers {
using System;
using System.Collections;
using System.Collections.Specialized;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.Compilation;
using System.Web.Configuration;
using System.Web.Hosting;
using System.Web.Management;
using System.Web.RegularExpressions;
using System.Web.Security.Cryptography;
using System.Web.UI;
using System.Web.Util;
/// <devdoc>
/// Provides a way to load client-side resources from assemblies
/// </devdoc>
public sealed class AssemblyResourceLoader : IHttpHandler {
private const string _webResourceUrl = "WebResource.axd";
private readonly static Regex webResourceRegex = new WebResourceRegex();
private static IDictionary _urlCache = Hashtable.Synchronized(new Hashtable());
private static IDictionary _assemblyInfoCache = Hashtable.Synchronized(new Hashtable());
private static IDictionary _webResourceCache = Hashtable.Synchronized(new Hashtable());
private static IDictionary _typeAssemblyCache = Hashtable.Synchronized(new Hashtable());
// This group of fields is used for backwards compatibility. In v1.x you could
// technically customize the files in the /aspnet_client/ folder whereas in v2.x
// we serve those files using WebResource.axd. These fields are used to check
// if there is a customized version of the file and use that instead of the resource.
private static bool _webFormsScriptChecked;
private static VirtualPath _webFormsScriptLocation;
private static bool _webUIValidationScriptChecked;
private static VirtualPath _webUIValidationScriptLocation;
private static bool _smartNavScriptChecked;
private static VirtualPath _smartNavScriptLocation;
private static bool _smartNavPageChecked;
private static VirtualPath _smartNavPageLocation;
private static bool _handlerExistenceChecked;
private static bool _handlerExists;
// set by unit tests to avoid dependency on httpruntime.
internal static string _applicationRootPath;
private static bool DebugMode {
get {
return HttpContext.Current.IsDebuggingEnabled;
}
}
/// <devdoc>
/// Create a cache key for the UrlCache.
///
/// requirement: If assembly1 and assembly2 represent the same assembly,
/// then they must be the same object; otherwise this method will fail to generate
/// a unique cache key.
/// </devdoc>
private static int CreateWebResourceUrlCacheKey(Assembly assembly, string resourceName,
bool htmlEncoded, bool forSubstitution, bool enableCdn, bool debuggingEnabled, bool secureConnection) {
int hashCode = HashCodeCombiner.CombineHashCodes(
assembly.GetHashCode(),
resourceName.GetHashCode(),
htmlEncoded.GetHashCode(),
forSubstitution.GetHashCode(),
enableCdn.GetHashCode());
return HashCodeCombiner.CombineHashCodes(hashCode,
debuggingEnabled.GetHashCode(),
secureConnection.GetHashCode());
}
/// <devdoc>
/// Validates that the WebResource.axd handler is registered in config and actually
/// points to the correct handler type.
/// </devdoc>
private static void EnsureHandlerExistenceChecked() {
// First we have to check that the handler is registered:
// <add path="WebResource.axd" verb="GET" type="System.Web.Handlers.AssemblyResourceLoader" validate="True" />
if (!_handlerExistenceChecked) {
HttpContext context = HttpContext.Current;
IIS7WorkerRequest iis7WorkerRequest = (context != null) ? context.WorkerRequest as IIS7WorkerRequest : null;
string webResourcePath = UrlPath.Combine(HttpRuntime.AppDomainAppVirtualPathString, _webResourceUrl);
if (iis7WorkerRequest != null) {
// check the IIS <handlers> section by mapping the handler
string handlerTypeString = iis7WorkerRequest.MapHandlerAndGetHandlerTypeString(method: "GET",
path: UrlPath.Combine(HttpRuntime.AppDomainAppVirtualPathString, _webResourceUrl),
convertNativeStaticFileModule: false, ignoreWildcardMappings: true);
if (!String.IsNullOrEmpty(handlerTypeString)) {
_handlerExists = (typeof(AssemblyResourceLoader) == BuildManager.GetType(handlerTypeString, true /*throwOnFail*/, false /*ignoreCase*/));
}
}
else {
// check the <httpHandlers> section
HttpHandlerAction httpHandler = RuntimeConfig.GetConfig(VirtualPath.Create(webResourcePath)).HttpHandlers.FindMapping("GET", VirtualPath.Create(_webResourceUrl));
_handlerExists = (httpHandler != null) && (httpHandler.TypeInternal == typeof(AssemblyResourceLoader));
}
_handlerExistenceChecked = true;
}
}
/// <devdoc>
/// Performs the actual putting together of the resource reference URL.
/// </devdoc>
private static string FormatWebResourceUrl(string assemblyName, string resourceName, long assemblyDate, bool htmlEncoded) {
string encryptedData = Page.EncryptString(assemblyName + "|" + resourceName, Purpose.AssemblyResourceLoader_WebResourceUrl);
if (htmlEncoded) {
return String.Format(CultureInfo.InvariantCulture, _webResourceUrl + "?d={0}&t={1}",
encryptedData,
assemblyDate);
}
else {
return String.Format(CultureInfo.InvariantCulture, _webResourceUrl + "?d={0}&t={1}",
encryptedData,
assemblyDate);
}
}
internal static Assembly GetAssemblyFromType(Type type) {
Assembly assembly = (Assembly)_typeAssemblyCache[type];
if (assembly == null) {
assembly = type.Assembly;
_typeAssemblyCache[type] = assembly;
}
return assembly;
}
private static Pair GetAssemblyInfo(Assembly assembly) {
Pair assemblyInfo = _assemblyInfoCache[assembly] as Pair;
if (assemblyInfo == null) {
assemblyInfo = GetAssemblyInfoWithAssertInternal(assembly);
_assemblyInfoCache[assembly] = assemblyInfo;
}
Debug.Assert(assemblyInfo != null, "Assembly info should not be null");
return assemblyInfo;
}
[FileIOPermission(SecurityAction.Assert, Unrestricted = true)]
private static Pair GetAssemblyInfoWithAssertInternal(Assembly assembly) {
AssemblyName assemblyName = assembly.GetName();
long assemblyDate = File.GetLastWriteTime(new Uri(assemblyName.CodeBase).LocalPath).Ticks;
Pair assemblyInfo = new Pair(assemblyName, assemblyDate);
return assemblyInfo;
}
/// <devdoc>
/// Gets the virtual path of a physical resource file. Null is
/// returned if the resource does not exist.
/// We assert full FileIOPermission so that we can map paths.
/// </devdoc>
[FileIOPermission(SecurityAction.Assert, Unrestricted = true)]
private static VirtualPath GetDiskResourcePath(string resourceName) {
VirtualPath clientScriptsLocation = Util.GetScriptLocation();
VirtualPath resourceVirtualPath = clientScriptsLocation.SimpleCombine(resourceName);
string resourcePhysicalPath = resourceVirtualPath.MapPath();
if (File.Exists(resourcePhysicalPath)) {
return resourceVirtualPath;
}
else {
return null;
}
}
internal static string GetWebResourceUrl(Type type, string resourceName) {
return GetWebResourceUrl(type, resourceName, false, null);
}
internal static string GetWebResourceUrl(Type type, string resourceName, bool htmlEncoded) {
return GetWebResourceUrl(type, resourceName, htmlEncoded, null);
}
internal static string GetWebResourceUrl(Type type, string resourceName, bool htmlEncoded, IScriptManager scriptManager) {
bool enableCdn = (scriptManager != null && scriptManager.EnableCdn);
return GetWebResourceUrl(type, resourceName, htmlEncoded, scriptManager, enableCdn: enableCdn);
}
/// <devdoc>
/// Gets a URL resource reference to a client-side resource
/// </devdoc>
internal static string GetWebResourceUrl(Type type, string resourceName, bool htmlEncoded, IScriptManager scriptManager, bool enableCdn) {
Assembly assembly = GetAssemblyFromType(type);
Debug.Assert(assembly != null, "Type.Assembly should never be null.");
// If the resource request is for System.Web.dll and more specifically
// it is for a file that we shipped in v1.x, we have to check if a
// customized copy of the file exists. See notes at the top of the file
// regarding this.
if (assembly == typeof(AssemblyResourceLoader).Assembly) {
if (String.Equals(resourceName, "WebForms.js", StringComparison.Ordinal)) {
if (!_webFormsScriptChecked) {
_webFormsScriptLocation = GetDiskResourcePath(resourceName);
_webFormsScriptChecked = true;
}
if (_webFormsScriptLocation != null) {
return _webFormsScriptLocation.VirtualPathString;
}
}
else if (String.Equals(resourceName, "WebUIValidation.js", StringComparison.Ordinal)) {
if (!_webUIValidationScriptChecked) {
_webUIValidationScriptLocation = GetDiskResourcePath(resourceName);
_webUIValidationScriptChecked = true;
}
if (_webUIValidationScriptLocation != null) {
return _webUIValidationScriptLocation.VirtualPathString;
}
}
else if (String.Equals(resourceName, "SmartNav.htm", StringComparison.Ordinal)) {
if (!_smartNavPageChecked) {
_smartNavPageLocation = GetDiskResourcePath(resourceName);
_smartNavPageChecked = true;
}
if (_smartNavPageLocation != null) {
return _smartNavPageLocation.VirtualPathString;
}
}
else if (String.Equals(resourceName, "SmartNav.js", StringComparison.Ordinal)) {
if (!_smartNavScriptChecked) {
_smartNavScriptLocation = GetDiskResourcePath(resourceName);
_smartNavScriptChecked = true;
}
if (_smartNavScriptLocation != null) {
return _smartNavScriptLocation.VirtualPathString;
}
}
}
return GetWebResourceUrlInternal(assembly, resourceName, htmlEncoded, false, scriptManager, enableCdn);
}
private static WebResourceAttribute FindWebResourceAttribute(Assembly assembly, string resourceName) {
object[] attrs = assembly.GetCustomAttributes(false);
for (int i = 0; i < attrs.Length; i++) {
WebResourceAttribute wra = attrs[i] as WebResourceAttribute;
if ((wra != null) && String.Equals(wra.WebResource, resourceName, StringComparison.Ordinal)) {
return wra;
}
}
return null;
}
internal static string FormatCdnUrl(Assembly assembly, string cdnPath) {
// {0} = Short Assembly Name
// {1} = Assembly Version
// {2} = Assembly File Version
// use a new AssemblyName because assembly.GetName() doesn't work in medium trust.
AssemblyName assemblyName = new AssemblyName(assembly.FullName);
return String.Format(CultureInfo.InvariantCulture,
cdnPath,
HttpUtility.UrlEncode(assemblyName.Name),
HttpUtility.UrlEncode(assemblyName.Version.ToString(4)),
HttpUtility.UrlEncode(AssemblyUtil.GetAssemblyFileVersion(assembly)));
}
private static string GetCdnPath(string resourceName, Assembly assembly, bool secureConnection) {
string cdnPath = null;
WebResourceAttribute wra = FindWebResourceAttribute(assembly, resourceName);
if (wra != null) {
cdnPath = secureConnection ? wra.CdnPathSecureConnection : wra.CdnPath;
if (!String.IsNullOrEmpty(cdnPath)) {
cdnPath = FormatCdnUrl(assembly, cdnPath);
}
}
return cdnPath;
}
internal static string GetWebResourceUrlInternal(Assembly assembly, string resourceName,
bool htmlEncoded, bool forSubstitution, IScriptManager scriptManager) {
bool enableCdn = (scriptManager != null && scriptManager.EnableCdn);
return GetWebResourceUrlInternal(assembly, resourceName, htmlEncoded, forSubstitution, scriptManager, enableCdn: enableCdn);
}
internal static string GetWebResourceUrlInternal(Assembly assembly, string resourceName,
bool htmlEncoded, bool forSubstitution, IScriptManager scriptManager, bool enableCdn) {
// When this url is being inserted as a substitution in another resource,
// it should just be "WebResource.axd?d=..." since the resource is already coming
// from the app root (i.e. no need for a full absolute /app/WebResource.axd).
// Otherwise we must return a path that is absolute (starts with '/') or
// a full absolute uri (http://..) as in the case of a CDN Path.
EnsureHandlerExistenceChecked();
if (!_handlerExists) {
throw new InvalidOperationException(SR.GetString(SR.AssemblyResourceLoader_HandlerNotRegistered));
}
Assembly effectiveAssembly = assembly;
string effectiveResourceName = resourceName;
bool debuggingEnabled = false;
bool secureConnection;
if (scriptManager != null) {
debuggingEnabled = scriptManager.IsDebuggingEnabled;
secureConnection = scriptManager.IsSecureConnection;
}
else {
secureConnection = ((HttpContext.Current != null) && (HttpContext.Current.Request != null) &&
HttpContext.Current.Request.IsSecureConnection);
debuggingEnabled = (HttpContext.Current != null) && HttpContext.Current.IsDebuggingEnabled;
}
int urlCacheKey = CreateWebResourceUrlCacheKey(assembly, resourceName, htmlEncoded,
forSubstitution, enableCdn, debuggingEnabled, secureConnection);
string url = (string)_urlCache[urlCacheKey];
if (url == null) {
IScriptResourceDefinition definition = null;
if (ClientScriptManager._scriptResourceMapping != null) {
definition = ClientScriptManager._scriptResourceMapping.GetDefinition(resourceName, assembly);
if (definition != null) {
if (!String.IsNullOrEmpty(definition.ResourceName)) {
effectiveResourceName = definition.ResourceName;
}
if (definition.ResourceAssembly != null) {
effectiveAssembly = definition.ResourceAssembly;
}
}
}
string path = null;
// if a resource mapping exists, take it's settings into consideration
// it might supply a path or a cdnpath.
if (definition != null) {
if (enableCdn) {
// Winner is first path defined, falling back on the effectiveResourceName/Assembly
// Debug Mode : d.CdnDebugPath, d.DebugPath, *wra.CdnPath, d.Path
// Release Mode: d.CdnPath , *wra.CdnPath, d.Path
// * the WebResourceAttribute corresponding to the resource defined in the definition, not the
// the original resource.
// Also, if the definition has a CdnPath but it cannot be converted to a secure one during https,
// the WRA's CdnPath is not considered.
if (debuggingEnabled) {
path = secureConnection ? definition.CdnDebugPathSecureConnection : definition.CdnDebugPath;
if (String.IsNullOrEmpty(path)) {
path = definition.DebugPath;
if (String.IsNullOrEmpty(path)) {
// Get CDN Path from the redirected resource name/assembly, not the original one,
// but not if this is a secure connection and the only reason we didn't use the definition
// cdn path is because it doesnt support secure connections.
if (!secureConnection || String.IsNullOrEmpty(definition.CdnDebugPath)) {
path = GetCdnPath(effectiveResourceName, effectiveAssembly, secureConnection);
}
if (String.IsNullOrEmpty(path)) {
path = definition.Path;
}
}
}
}
else {
path = secureConnection ? definition.CdnPathSecureConnection : definition.CdnPath;
if (String.IsNullOrEmpty(path)) {
// Get CDN Path from the redirected resource name/assembly, not the original one
// but not if this is a secure connection and the only reason we didn't use the definition
// cdn path is because it doesnt support secure connections.
if (!secureConnection || String.IsNullOrEmpty(definition.CdnPath)) {
path = GetCdnPath(effectiveResourceName, effectiveAssembly, secureConnection);
}
if (String.IsNullOrEmpty(path)) {
path = definition.Path;
}
}
}
} // cdn
else {
// Winner is first path defined, falling back on the effectiveResourceName/Assembly
// Debug Mode : d.DebugPath, d.Path
// Release Mode: d.Path
if (debuggingEnabled) {
path = definition.DebugPath;
if (String.IsNullOrEmpty(path)) {
path = definition.Path;
}
}
else {
path = definition.Path;
}
}
} // does not have definition
else if (enableCdn) {
path = GetCdnPath(effectiveResourceName, effectiveAssembly, secureConnection);
}
if (!String.IsNullOrEmpty(path)) {
// assembly based resource has been overridden by a path,
// whether that be a CDN Path or a definition.Path or DebugPath.
// We must return a path that is absolute (starts with '/') or
// a full absolute uri (http://..) as in the case of a CDN Path.
// An overridden Path that is not a CDN Path is required to be absolute
// or app relative.
if (UrlPath.IsAppRelativePath(path)) {
// expand ~/. If it is rooted (/) or an absolute uri, no conversion needed
if (_applicationRootPath == null) {
url = VirtualPathUtility.ToAbsolute(path);
}
else {
url = VirtualPathUtility.ToAbsolute(path, _applicationRootPath);
}
}
else {
// must be a full uri or already rooted.
url = path;
}
if (htmlEncoded) {
url = HttpUtility.HtmlEncode(url);
}
}
else {
string urlAssemblyName;
Pair assemblyInfo = GetAssemblyInfo(effectiveAssembly);
AssemblyName assemblyName = (AssemblyName)assemblyInfo.First;
long assemblyDate = (long)assemblyInfo.Second;
string assemblyVersion = assemblyName.Version.ToString();
if (effectiveAssembly.GlobalAssemblyCache) {
// If the assembly is in the GAC, we need to store a full name to load the assembly later
if (effectiveAssembly == HttpContext.SystemWebAssembly) {
urlAssemblyName = "s";
}
else {
// Pack the necessary values into a more compact format than FullName
StringBuilder builder = new StringBuilder();
builder.Append('f');
builder.Append(assemblyName.Name);
builder.Append(',');
builder.Append(assemblyVersion);
builder.Append(',');
if (assemblyName.CultureInfo != null) {
builder.Append(assemblyName.CultureInfo.ToString());
}
builder.Append(',');
byte[] token = assemblyName.GetPublicKeyToken();
for (int i = 0; i < token.Length; i++) {
builder.Append(token[i].ToString("x2", CultureInfo.InvariantCulture));
}
urlAssemblyName = builder.ToString();
}
}
else {
// Otherwise, we can just use a partial name
urlAssemblyName = "p" + assemblyName.Name;
}
url = FormatWebResourceUrl(urlAssemblyName, effectiveResourceName, assemblyDate, htmlEncoded);
if (!forSubstitution && (HttpRuntime.AppDomainAppVirtualPathString != null)) {
// When this url is being inserted as a substitution in another resource,
// it should just be "WebResource.axd?d=..." since the resource is already coming
// from the app root (i.e. no need for a full absolute /app/WebResource.axd).
url = UrlPath.Combine(HttpRuntime.AppDomainAppVirtualPathString, url);
}
}
_urlCache[urlCacheKey] = url;
}
return url;
}
internal static bool IsValidWebResourceRequest(HttpContext context) {
EnsureHandlerExistenceChecked();
if (!_handlerExists) {
// If the handler isn't properly registered, it can't
// possibly be a valid web resource request.
return false;
}
string webResourceHandlerUrl = UrlPath.Combine(HttpRuntime.AppDomainAppVirtualPathString, _webResourceUrl);
string requestPath = context.Request.Path;
if (String.Equals(requestPath, webResourceHandlerUrl, StringComparison.OrdinalIgnoreCase)) {
return true;
}
return false;
}
internal static void LogWebResourceFailure(string decryptedData, Exception exception) {
string errorMessage = null;
if (decryptedData != null) {
errorMessage = SR.GetString(SR.Webevent_msg_RuntimeErrorWebResourceFailure_ResourceMissing, decryptedData);
}
else {
errorMessage = SR.GetString(SR.Webevent_msg_RuntimeErrorWebResourceFailure_DecryptionError);
}
WebBaseEvent.RaiseSystemEvent(message: errorMessage,
source: null,
eventCode: WebEventCodes.RuntimeErrorWebResourceFailure,
eventDetailCode: WebEventCodes.UndefinedEventDetailCode,
exception: exception);
}
/// <internalonly/>
bool IHttpHandler.IsReusable {
get {
return true;
}
}
/// <internalonly/>
void IHttpHandler.ProcessRequest(HttpContext context) {
// Make sure we don't get any extra content in this handler (like Application.BeginRequest stuff);
context.Response.Clear();
Stream resourceStream = null;
string decryptedData = null;
bool resourceIdentifierPresent = false;
Exception exception = null;
try {
NameValueCollection queryString = context.Request.QueryString;
string encryptedData = queryString["d"];
if (String.IsNullOrEmpty(encryptedData)) {
throw new HttpException(404, SR.GetString(SR.AssemblyResourceLoader_InvalidRequest));
}
resourceIdentifierPresent = true;
decryptedData = Page.DecryptString(encryptedData, Purpose.AssemblyResourceLoader_WebResourceUrl);
int separatorIndex = decryptedData.IndexOf('|');
Debug.Assert(separatorIndex != -1, "The decrypted data must contain a separator.");
string assemblyName = decryptedData.Substring(0, separatorIndex);
if (String.IsNullOrEmpty(assemblyName)) {
throw new HttpException(404, SR.GetString(SR.AssemblyResourceLoader_AssemblyNotFound, assemblyName));
}
string resourceName = decryptedData.Substring(separatorIndex + 1);
if (String.IsNullOrEmpty(resourceName)) {
throw new HttpException(404, SR.GetString(SR.AssemblyResourceLoader_ResourceNotFound, resourceName));
}
char nameType = assemblyName[0];
assemblyName = assemblyName.Substring(1);
Assembly assembly = null;
// If it was a full name, create an AssemblyName and load from that
if (nameType == 'f') {
string[] parts = assemblyName.Split(',');
if (parts.Length != 4) {
throw new HttpException(404, SR.GetString(SR.AssemblyResourceLoader_InvalidRequest));
}
AssemblyName realName = new AssemblyName();
realName.Name = parts[0];
realName.Version = new Version(parts[1]);
string cultureString = parts[2];
// Try to determine the culture, using the invariant culture if there wasn't one (doesn't work without it)
if (cultureString.Length > 0) {
realName.CultureInfo = new CultureInfo(cultureString);
}
else {
realName.CultureInfo = CultureInfo.InvariantCulture;
}
// Parse up the public key token which is represented as hex bytes in a string
string token = parts[3];
byte[] tokenBytes = new byte[token.Length / 2];
for (int i = 0; i < tokenBytes.Length; i++) {
tokenBytes[i] = Byte.Parse(token.Substring(i * 2, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture);
}
realName.SetPublicKeyToken(tokenBytes);
assembly = Assembly.Load(realName);
}
// System.Web special case
else if (nameType == 's') {
assembly = typeof(AssemblyResourceLoader).Assembly;
}
// If was a partial name, just try to load it
else if (nameType == 'p') {
assembly = Assembly.Load(assemblyName);
}
else {
throw new HttpException(404, SR.GetString(SR.AssemblyResourceLoader_InvalidRequest));
}
// Dev10 Bugs 602949: Throw 404 if resource not found rather than do nothing.
// This is done before creating the cache entry, since it could be that the assembly is loaded
// later on without the app restarting.
if (assembly == null) {
throw new HttpException(404, SR.GetString(SR.AssemblyResourceLoader_InvalidRequest));
}
bool performSubstitution = false;
bool validResource = false;
string contentType = String.Empty;
// Check the validation cache to see if the resource has already been validated
int cacheKey = HashCodeCombiner.CombineHashCodes(assembly.GetHashCode(), resourceName.GetHashCode());
Triplet resourceTriplet = (Triplet)_webResourceCache[cacheKey];
if (resourceTriplet != null) {
validResource = (bool)resourceTriplet.First;
contentType = (string)resourceTriplet.Second;
performSubstitution = (bool)resourceTriplet.Third;
}
else {
// Validation cache is empty, find out if it's valid and add it to the cache
WebResourceAttribute wra = FindWebResourceAttribute(assembly, resourceName);
if (wra != null) {
resourceName = wra.WebResource;
validResource = true;
contentType = wra.ContentType;
performSubstitution = wra.PerformSubstitution;
}
// Cache the result so we don't have to do this again
try {
if (validResource) {
// a WebResourceAttribute was found, but does the resource really exist?
validResource = false;
resourceStream = assembly.GetManifestResourceStream(resourceName);
validResource = (resourceStream != null);
}
}
finally {
// Cache the results, even if there was an exception getting the stream,
// so we don't have to do this again
Triplet triplet = new Triplet();
triplet.First = validResource;
triplet.Second = contentType;
triplet.Third = performSubstitution;
_webResourceCache[cacheKey] = triplet;
}
}
if (validResource) {
// Cache the resource so we don't keep processing the same requests
HttpCachePolicy cachePolicy = context.Response.Cache;
cachePolicy.SetCacheability(HttpCacheability.Public);
cachePolicy.VaryByParams["d"] = true;
cachePolicy.SetOmitVaryStar(true);
cachePolicy.SetExpires(DateTime.Now + TimeSpan.FromDays(365));
cachePolicy.SetValidUntilExpires(true);
Pair assemblyInfo = GetAssemblyInfo(assembly);
cachePolicy.SetLastModified(new DateTime((long)assemblyInfo.Second));
StreamReader reader = null;
try {
if (resourceStream == null) {
// null in the case that _webResourceCache had the item
resourceStream = assembly.GetManifestResourceStream(resourceName);
}
if (resourceStream != null) {
context.Response.ContentType = contentType;
if (performSubstitution) {
//
reader = new StreamReader(resourceStream, true);
string content = reader.ReadToEnd();
// Looking for something of the form: WebResource("resourcename")
MatchCollection matches = webResourceRegex.Matches(content);
int startIndex = 0;
StringBuilder newContent = new StringBuilder();
foreach (Match match in matches) {
newContent.Append(content.Substring(startIndex, match.Index - startIndex));
Group group = match.Groups["resourceName"];
if (group != null) {
string embeddedResourceName = group.ToString();
if (embeddedResourceName.Length > 0) {
//
if (String.Equals(embeddedResourceName, resourceName, StringComparison.Ordinal)) {
throw new HttpException(404, SR.GetString(SR.AssemblyResourceLoader_NoCircularReferences, resourceName));
}
newContent.Append(GetWebResourceUrlInternal(assembly, embeddedResourceName, htmlEncoded: false, forSubstitution: true, scriptManager: null));
}
}
startIndex = match.Index + match.Length;
}
newContent.Append(content.Substring(startIndex, content.Length - startIndex));
StreamWriter writer = new StreamWriter(context.Response.OutputStream, reader.CurrentEncoding);
writer.Write(newContent.ToString());
writer.Flush();
}
else {
byte[] buffer = new byte[1024];
Stream outputStream = context.Response.OutputStream;
int count = 1;
while (count > 0) {
count = resourceStream.Read(buffer, 0, 1024);
outputStream.Write(buffer, 0, count);
}
outputStream.Flush();
}
}
}
finally {
if (reader != null)
reader.Close();
if (resourceStream != null)
resourceStream.Close();
}
}
}
catch(Exception e) {
exception = e;
// MSRC 10405: ---- all errors in the event of failure. In particular, we don't want to
// bubble the inner exceptions up in the YSOD, as they might contain sensitive cryptographic
// information. Setting 'resourceStream' to null will cause an appropriate exception to
// be thrown.
resourceStream = null;
}
// Dev10 Bugs 602949: 404 if the assembly is not found or if the resource does not exist
if (resourceStream == null) {
if (resourceIdentifierPresent) {
LogWebResourceFailure(decryptedData, exception);
}
throw new HttpException(404, SR.GetString(SR.AssemblyResourceLoader_InvalidRequest));
}
context.Response.IgnoreFurtherWrites();
}
}
}
|