소스 검색

[asp.net] Part of fix for bug #649546. A partial rewrite of the web/script resource handler.

* System.Web/System.Web.Handlers/AssemblyResourceLoader.cs: AssemblyResourceLoader/ScriptResourceHandler must
  always generate the same URL for every assembly+resource pair, or otherwise the browser would re-fetch
  the resource in situations where it is not desired (e.g. during an async request to the server to update
  a panel).
  The generated URLs are not encrypted using real encryption, just hashed.
  In addition, the handler code got modernized a bit.

* System.Web.Extensions/System.Web.UI/ScriptManager.cs: scriptStartupBlock is a 3.5+ thing

* System.Web.Extensions_1.0/Makefile: use the 1.0 Microsoft AJAX library for this compat assembly

* System.Web/System.Web.Util/MachineKeySectionUtils.cs: made GetValidationKey public
Marek Habersack 15 년 전
부모
커밋
a9cb57cc13

+ 4 - 0
mcs/class/System.Web.Extensions/System.Web.UI/ScriptManager.cs

@@ -62,7 +62,9 @@ namespace System.Web.UI
 		const string hiddenField = "hiddenField";
 		const string arrayDeclaration = "arrayDeclaration";
 		const string scriptBlock = "scriptBlock";
+#if NET_3_5
 		const string scriptStartupBlock = "scriptStartupBlock";
+#endif
 		const string expando = "expando";
 		const string onSubmit = "onSubmit";
 		const string asyncPostBackControlIDs = "asyncPostBackControlIDs";
@@ -1418,12 +1420,14 @@ namespace System.Web.UI
 							else
 								WriteCallbackOutput (output, scriptBlock, scriptContentWithTags, SerializeScriptBlock (scriptEntry));
 							break;
+#if NET_3_5
 						case RegisteredScriptType.ClientStartupScript:
 							if (scriptEntry.AddScriptTags)
 								WriteCallbackOutput (output, scriptStartupBlock, scriptContentNoTags, scriptEntry.Script);
 							else
 								WriteCallbackOutput (output, scriptStartupBlock, scriptContentWithTags, SerializeScriptBlock (scriptEntry));
 							break;
+#endif
 						case RegisteredScriptType.ClientScriptInclude:
 							WriteCallbackOutput (output, scriptBlock, scriptPath, scriptEntry.Url);
 							break;

+ 7 - 6
mcs/class/System.Web.Extensions_1.0/Makefile

@@ -8,12 +8,12 @@ LIBRARY_NAME = System.Web.Extensions.dll
 LIBRARY_COMPAT = yes
 
 RESOURCE_FILES= \
-	../MicrosoftAjaxLibrary/System.Web.Extensions/3.5.0.0/3.5.21022.8/MicrosoftAjax.debug.js	\
-	../MicrosoftAjaxLibrary/System.Web.Extensions/3.5.0.0/3.5.21022.8/MicrosoftAjax.js		\
-	../MicrosoftAjaxLibrary/System.Web.Extensions/3.5.0.0/3.5.21022.8/MicrosoftAjaxTimer.debug.js	\
-	../MicrosoftAjaxLibrary/System.Web.Extensions/3.5.0.0/3.5.21022.8/MicrosoftAjaxTimer.js		\
-	../MicrosoftAjaxLibrary/System.Web.Extensions/3.5.0.0/3.5.21022.8/MicrosoftAjaxWebForms.debug.js\
-	../MicrosoftAjaxLibrary/System.Web.Extensions/3.5.0.0/3.5.21022.8/MicrosoftAjaxWebForms.js
+	../MicrosoftAjaxLibrary/System.Web.Extensions/1.0.61025.0/MicrosoftAjax.debug.js	\
+	../MicrosoftAjaxLibrary/System.Web.Extensions/1.0.61025.0/MicrosoftAjax.js		\
+	../MicrosoftAjaxLibrary/System.Web.Extensions/1.0.61025.0/MicrosoftAjaxTimer.debug.js	\
+	../MicrosoftAjaxLibrary/System.Web.Extensions/1.0.61025.0/MicrosoftAjaxTimer.js		\
+	../MicrosoftAjaxLibrary/System.Web.Extensions/1.0.61025.0/MicrosoftAjaxWebForms.debug.js\
+	../MicrosoftAjaxLibrary/System.Web.Extensions/1.0.61025.0/MicrosoftAjaxWebForms.js
 
 RESOURCE_FILES_DIST= \
 	../MicrosoftAjaxLibrary/System.Web.Extensions/3.5.0.0/3.5.21022.8/MicrosoftAjax.debug.js	\
@@ -34,6 +34,7 @@ LIB_MCS_FLAGS = \
 	-define:SYSTEM_WEB_EXTENSIONS	\
 	-r:$(corlib)	 		\
 	-r:System.dll 			\
+	-r:System.Core.dll		\
 	-r:System.Drawing.dll 		\
 	-r:System.Data.dll		\
 	-r:System.Xml.dll		\

+ 195 - 125
mcs/class/System.Web/System.Web.Handlers/AssemblyResourceLoader.cs

@@ -3,9 +3,10 @@
 //
 // Authors:
 //	Ben Maurer ([email protected])
+//	Marek Habersack <[email protected]>
 //
 // (C) 2003 Ben Maurer
-//
+// (C) 2010 Novell, Inc (http://novell.com/)
 
 //
 // Permission is hereby granted, free of charge, to any person obtaining
@@ -34,12 +35,16 @@ using System.Reflection;
 using System.IO;
 using System.Resources;
 using System.Collections;
+using System.Collections.Generic;
+using System.Security.Cryptography;
 using System.Text;
 using System.Text.RegularExpressions;
+using System.Threading;
 using System.Web.Configuration;
 using System.Web.Util;
 
-namespace System.Web.Handlers {
+namespace System.Web.Handlers
+{
 #if SYSTEM_WEB_EXTENSIONS
 	partial class ScriptResourceHandler
 	{
@@ -51,31 +56,68 @@ namespace System.Web.Handlers {
 	#else
 	internal // since this is in the .config file, we need to support it, since we dont have versoned support.
 	#endif
-	class AssemblyResourceLoader : IHttpHandler {		
+	class AssemblyResourceLoader : IHttpHandler
+	{
 		const string HandlerFileName = "WebResource.axd";
 		static Assembly currAsm = typeof (AssemblyResourceLoader).Assembly;
 #endif
 		const char QueryParamSeparator = '&';
 
-		static readonly Hashtable _embeddedResources = Hashtable.Synchronized (new Hashtable ());
+		static readonly Dictionary <string, AssemblyEmbeddedResources> _embeddedResources = new Dictionary <string, AssemblyEmbeddedResources> (StringComparer.Ordinal);
+		static readonly ReaderWriterLockSlim _embeddedResourcesLock = new ReaderWriterLockSlim ();
 
-		static void InitEmbeddedResourcesUrls (Assembly assembly, Hashtable hashtable)
+		static string GetStringHash (KeyedHashAlgorithm kha, string str)
+		{
+			if (String.IsNullOrEmpty (str))
+				return String.Empty;
+			
+			return Convert.ToBase64String (kha.ComputeHash (Encoding.UTF8.GetBytes (str)));
+		}
+		
+		static void InitEmbeddedResourcesUrls (KeyedHashAlgorithm kha, Assembly assembly, string assemblyName, string assemblyHash, AssemblyEmbeddedResources entry)
 		{
 			WebResourceAttribute [] attrs = (WebResourceAttribute []) assembly.GetCustomAttributes (typeof (WebResourceAttribute), false);
+			WebResourceAttribute attr;
+			string apath = assembly.Location;
 			for (int i = 0; i < attrs.Length; i++) {
-				string resourceName = attrs [i].WebResource;
-				if (resourceName != null && resourceName.Length > 0) {
+				attr = attrs [i];
+				string resourceName = attr.WebResource;
+				if (!String.IsNullOrEmpty (resourceName)) {
+					string resourceNameHash = GetStringHash (kha, resourceName);
 #if SYSTEM_WEB_EXTENSIONS
-					ResourceKey rkNoNotify = new ResourceKey (resourceName, false);
-					ResourceKey rkNotify = new ResourceKey (resourceName, true);
-
-					if (!hashtable.Contains (rkNoNotify))
-						hashtable.Add (rkNoNotify, CreateResourceUrl (assembly, resourceName, false));
-					if (!hashtable.Contains (rkNotify))
-						hashtable.Add (rkNotify, CreateResourceUrl (assembly, resourceName, true));
+					bool debug = resourceName.EndsWith (".debug.js", StringComparison.OrdinalIgnoreCase);
+					string dbgTail = debug ? "d" : String.Empty;
+					string rkNoNotify = resourceNameHash + "f" + dbgTail;
+					string rkNotify = resourceNameHash + "t" + dbgTail;
+
+					if (!entry.Resources.ContainsKey (rkNoNotify)) {
+						var er = new EmbeddedResource () {
+							Name = resourceName,
+							Attribute = attr, 
+							Url = CreateResourceUrl (kha, assemblyName, assemblyHash, apath, rkNoNotify, debug, false)
+						};
+						
+						entry.Resources.Add (rkNoNotify, er);
+					}
+					
+					if (!entry.Resources.ContainsKey (rkNotify)) {
+						var er = new EmbeddedResource () {
+							Name = resourceName,
+							Attribute = attr, 
+							Url = CreateResourceUrl (kha, assemblyName, assemblyHash, apath, rkNotify, debug, true)
+						};
+						
+						entry.Resources.Add (rkNotify, er);
+					}
 #else
-					if (!hashtable.Contains (resourceName))
-						hashtable.Add (resourceName, CreateResourceUrl (assembly, resourceName, false));
+					if (!entry.Resources.ContainsKey (resourceNameHash)) {
+						var er = new EmbeddedResource () {
+							Name = resourceName,
+							Attribute = attr, 
+							Url = CreateResourceUrl (kha, assemblyName, assemblyHash, apath, resourceNameHash, false, false)
+						};
+						entry.Resources.Add (resourceNameHash, er);
+					}
 #endif
 				}
 			}
@@ -87,78 +129,135 @@ namespace System.Web.Handlers {
 			return GetResourceUrl (type.Assembly, resourceName, false);
 		}
 #endif
-
-		static string EncryptAssemblyResource (string asmName, string resName)
+		static EmbeddedResource DecryptAssemblyResource (string val, out AssemblyEmbeddedResources entry)
 		{
-			byte[] bytes = Encoding.UTF8.GetBytes (String.Concat (asmName, ";", resName));
-			bytes = MachineKeySectionUtils.Encrypt (MachineKeySection.Config, bytes);
-			return Convert.ToBase64String (bytes);
+			entry = null;
+			
+			string[] parts = val.Split ('_');
+			if (parts.Length != 3)
+				return null;
+
+			Encoding enc = Encoding.UTF8;
+			string asmNameHash = parts [0];
+			string resNameHash = parts [1];
+			bool debug = parts [2] == "t";
+			
+			try {
+				_embeddedResourcesLock.EnterReadLock ();
+				if (!_embeddedResources.TryGetValue (asmNameHash, out entry) || entry == null)
+					return null;
+				
+				EmbeddedResource res;
+				if (!entry.Resources.TryGetValue (resNameHash, out res) || res == null) {
+#if SYSTEM_WEB_EXTENSIONS
+					if (!debug)
+						return null;
+
+					if (!entry.Resources.TryGetValue (resNameHash.Substring (0, resNameHash.Length - 1), out res))
+						return null;
+#else
+					return null;
+#endif
+				}
+				
+				return res;
+			} finally {
+				_embeddedResourcesLock.ExitReadLock ();
+			}
 		}
 
-		static void DecryptAssemblyResource (string val, out string asmName, out string resName)
+		internal static string GetResourceUrl (Assembly assembly, string resourceName, bool notifyScriptLoaded)
 		{
-			byte[] bytes = Convert.FromBase64String (val);
-
-			asmName = null;
-			resName = null;			
-
-			byte[] result = MachineKeySectionUtils.Decrypt (MachineKeySection.Config, bytes);
-			bytes = null;
-			// null will be returned if, for any reason, decryption fails
-			if (result == null)
-				return;
-
-			string data = Encoding.UTF8.GetString (result);
-			result = null;
-
-			string[] parts = data.Split (';');
-			if (parts.Length != 2)
-				return;
+			if (assembly == null)
+				return String.Empty;
 			
-			asmName = parts [0];
-			resName = parts [1];
+			MachineKeySection mks = MachineKeySection.Config;
+			using (KeyedHashAlgorithm kha = MachineKeySectionUtils.GetValidationAlgorithm (mks)) {
+				kha.Key = MachineKeySectionUtils.GetValidationKey (mks);
+				return GetResourceUrl (kha, assembly, resourceName, notifyScriptLoaded);
+			}
 		}
 
-		internal static string GetResourceUrl (Assembly assembly, string resourceName, bool notifyScriptLoaded)
+		static string GetResourceUrl (KeyedHashAlgorithm kha, Assembly assembly, string resourceName, bool notifyScriptLoaded)
 		{
-			Hashtable hashtable = (Hashtable)_embeddedResources [assembly];
-			if (hashtable == null) {
-				hashtable = new Hashtable ();
-				InitEmbeddedResourcesUrls (assembly, hashtable);
-				_embeddedResources [assembly] = hashtable;
-			}
+			string assemblyName = assembly == currAsm ? "s" : assembly.GetName ().FullName;
+			string assemblyNameHash = GetStringHash (kha, assemblyName);
+			string resourceNameHash = GetStringHash (kha, resourceName);
+			bool debug = false;
+			string url;
+			AssemblyEmbeddedResources entry;
+
+			try {
+				_embeddedResourcesLock.EnterUpgradeableReadLock ();
+				if (!_embeddedResources.TryGetValue (assemblyNameHash, out entry) || entry == null) {
+					try {
+						_embeddedResourcesLock.EnterWriteLock ();
+						entry = new AssemblyEmbeddedResources () {
+							AssemblyName = assemblyName
+						};
+						InitEmbeddedResourcesUrls (kha, assembly, assemblyName, assemblyNameHash, entry);
+						_embeddedResources.Add (assemblyNameHash, entry);
+					} finally {
+						_embeddedResourcesLock.ExitWriteLock ();
+					}
+				}
+				string lookupKey;
 #if SYSTEM_WEB_EXTENSIONS
-			string url = (string) hashtable [new ResourceKey (resourceName, notifyScriptLoaded)];
+				debug = resourceName.EndsWith (".debug.js", StringComparison.OrdinalIgnoreCase);
+				string dbgTail = debug ? "d" : String.Empty;
+				lookupKey = resourceNameHash + (notifyScriptLoaded ? "t" : "f") + dbgTail;
 #else
-			string url = (string) hashtable [resourceName];
+				lookupKey = resourceNameHash;
 #endif
+				EmbeddedResource res;
+				if (entry.Resources.TryGetValue (lookupKey, out res) && res != null)
+					url = res.Url;
+				else {
+#if SYSTEM_WEB_EXTENSIONS
+					if (debug) {
+						resourceNameHash = GetStringHash (kha, resourceName.Substring (0, resourceName.Length - 9) + ".js");
+						lookupKey = resourceNameHash + (notifyScriptLoaded ? "t" : "f");
+					
+						if (entry.Resources.TryGetValue (lookupKey, out res) && res != null)
+							url = res.Url;
+						else
+							url = null;
+					} else
+#endif
+						url = null;
+				}
+			} finally {
+				_embeddedResourcesLock.ExitUpgradeableReadLock ();
+			}
+
 			if (url == null)
-				url = CreateResourceUrl (assembly, resourceName, notifyScriptLoaded);
+				url = CreateResourceUrl (kha, assemblyName, assemblyNameHash, assembly.Location, resourceNameHash, debug, notifyScriptLoaded);
+			
 			return url;
 		}
 		
-		static string CreateResourceUrl (Assembly assembly, string resourceName, bool notifyScriptLoaded)
+		static string CreateResourceUrl (KeyedHashAlgorithm kha, string assemblyName, string assemblyNameHash, string assemblyPath, string resourceNameHash, bool debug, bool notifyScriptLoaded)
 		{
-
-			string aname = assembly == currAsm ? "s" : assembly.GetName ().FullName;
-			string apath = assembly.Location;
 			string atime = String.Empty;
 			string extra = String.Empty;
 #if SYSTEM_WEB_EXTENSIONS
-			extra = String.Concat (QueryParamSeparator, "n=", notifyScriptLoaded ? "t" : "f");
+			extra = QueryParamSeparator + "n=" + (notifyScriptLoaded ? "t" : "f");
 #endif
 
 #if TARGET_JVM
-			atime = String.Format ("{0}t={1}", QueryParamSeparator, assembly.GetHashCode ());
+			atime = QueryParamSeparator + "t=" + assemblyName.GetHashCode ();
 #else
-			if (apath != String.Empty)
-				atime = String.Concat (QueryParamSeparator, "t=", File.GetLastWriteTimeUtc (apath).Ticks);
+			if (!String.IsNullOrEmpty (assemblyPath) && File.Exists (assemblyPath))
+				atime = QueryParamSeparator + "t=" + File.GetLastWriteTimeUtc (assemblyPath).Ticks;
+			else
+				atime = QueryParamSeparator + "t=" + DateTime.UtcNow.Ticks;
 #endif
-			string href = HandlerFileName + "?d=" + EncryptAssemblyResource (aname, resourceName) + atime + extra;
-
+			string d = assemblyNameHash + "_" + resourceNameHash +  (debug ? "_t" : "_f");
+			string href = HandlerFileName + "?d=" + d + atime + extra;
 			HttpContext ctx = HttpContext.Current;
-			if (ctx != null && ctx.Request != null) {
-				string appPath = VirtualPathUtility.AppendTrailingSlash (ctx.Request.ApplicationPath);
+			HttpRequest req = ctx != null ? ctx.Request : null;
+			if (req != null) {
+				string appPath = VirtualPathUtility.AppendTrailingSlash (req.ApplicationPath);
 				href = appPath + href;
 			}
 			
@@ -168,65 +267,33 @@ namespace System.Web.Handlers {
 #if SYSTEM_WEB_EXTENSIONS
 		protected virtual void ProcessRequest (HttpContext context)
 #else
-		[MonoTODO ("Substitution not implemented")]
 		void System.Web.IHttpHandler.ProcessRequest (HttpContext context)
 #endif
 		{
 			HttpRequest request = context.Request;
-			HttpResponse response = context.Response;
-			string resourceName;
-			string asmName;
-			Assembly assembly;
-
-			// val is URL-decoded, which means every + has been replaced with ' ', we
+			// val is URL-encoded, which means every + has been replaced with ' ', we
 			// need to revert that or the base64 conversion will fail.
 			string d = request.QueryString ["d"];
 			if (!String.IsNullOrEmpty (d))
 				d = d.Replace (' ', '+');
 
-			DecryptAssemblyResource (d, out asmName, out resourceName);
-			if (resourceName == null)
-				throw new HttpException (404, "No resource name given");
-
-			if (asmName == null || asmName == "s")
+			AssemblyEmbeddedResources entry;
+			EmbeddedResource res = DecryptAssemblyResource (d, out entry);
+			WebResourceAttribute wra = res != null ? res.Attribute : null;
+			if (wra == null)
+				throw new HttpException (404, "Resource not found");
+			
+			Assembly assembly;
+			if (entry.AssemblyName == "s")
 				assembly = currAsm;
 			else
-				assembly = Assembly.Load (asmName);
-
-			WebResourceAttribute wra = null;
-			WebResourceAttribute [] attrs = (WebResourceAttribute []) assembly.GetCustomAttributes (typeof (WebResourceAttribute), false);
-			for (int i = 0; i < attrs.Length; i++) {
-				if (attrs [i].WebResource == resourceName) {
-					wra = attrs [i];
-					break;
-				}
-			}
-#if SYSTEM_WEB_EXTENSIONS
-			if (wra == null && resourceName.Length > 9 && resourceName.EndsWith (".debug.js", StringComparison.OrdinalIgnoreCase)) {
-				resourceName = String.Concat (resourceName.Substring (0, resourceName.Length - 9), ".js");
-				for (int i = 0; i < attrs.Length; i++) {
-					if (attrs [i].WebResource == resourceName) {
-						wra = attrs [i];
-						break;
-					}
-				}
-			}
-#endif
-			if (wra == null)
-				throw new HttpException (404, String.Concat ("Resource ", resourceName, " not found"));
+				assembly = Assembly.Load (entry.AssemblyName);
 			
+			HttpResponse response = context.Response;
 			string req_cache = request.Headers ["Cache-Control"];
-			if (req_cache == "max-age=0") {
+			if (String.Compare (req_cache, "max-age=0", StringComparison.Ordinal) == 0) {
 				long atime;
-#if NET_2_0
 				if (Int64.TryParse (request.QueryString ["t"], out atime)) {
-#else
-				atime = -1;
-				try {
-					atime = Int64.Parse (request.QueryString ["t"]);
-				} catch {}
-				if (atime > -1) {
-#endif
 					if (atime == File.GetLastWriteTimeUtc (assembly.Location).Ticks) {
 						response.Clear ();
 						response.StatusCode = 304;
@@ -238,18 +305,10 @@ namespace System.Web.Handlers {
 				}
 			}
 			string modif_since = request.Headers ["If-Modified-Since"];
-			if (modif_since != null && modif_since != "") {
+			if (!String.IsNullOrEmpty (modif_since)) {
 				try {
 					DateTime modif;
-#if NET_2_0
-					if (DateTime.TryParseExact (modif_since, "r", null, 0, out modif))
-#else
-					modif = DateTime.MinValue;
-					try {
-						modif = DateTime.ParseExact (modif_since, "r", null, 0);
-					} catch { }
-					if (modif != DateTime.MinValue)
-#endif
+					if (DateTime.TryParseExact (modif_since, "r", null, 0, out modif)) {
 						if (File.GetLastWriteTimeUtc (assembly.Location) <= modif) {
 							response.Clear ();
 							response.StatusCode = 304;
@@ -258,6 +317,7 @@ namespace System.Web.Handlers {
 							context.ApplicationInstance.CompleteRequest ();
 							return;
 						}
+					}
 				} catch {}
 			}
 
@@ -268,23 +328,21 @@ namespace System.Web.Handlers {
 			response.ExpiresAbsolute = utcnow.AddYears (1);
 			response.CacheControl = "public";
 
-			Stream s = assembly.GetManifestResourceStream (resourceName);
+			Stream s = assembly.GetManifestResourceStream (res.Name);
 			if (s == null)
-				throw new HttpException (404, String.Concat ("Resource ", resourceName, " not found"));
+				throw new HttpException (404, "Resource " + res.Name + " not found");
 
 			if (wra.PerformSubstitution) {
 				using (StreamReader r = new StreamReader (s)) {
 					TextWriter w = response.Output;
 					new PerformSubstitutionHelper (assembly).PerformSubstitution (r, w);
 				}
-#if NET_2_0
 			} else if (response.OutputStream is HttpResponseStream) {
 				UnmanagedMemoryStream st = (UnmanagedMemoryStream) s;
 				HttpResponseStream hstream = (HttpResponseStream) response.OutputStream;
 				unsafe {
 					hstream.WritePtr (new IntPtr (st.PositionPointer), (int) st.Length);
 				}
-#endif
 			} else {
 				byte [] buf = new byte [1024];
 				Stream output = response.OutputStream;
@@ -297,7 +355,7 @@ namespace System.Web.Handlers {
 #if SYSTEM_WEB_EXTENSIONS
 			TextWriter writer = response.Output;
 			foreach (ScriptResourceAttribute sra in assembly.GetCustomAttributes (typeof (ScriptResourceAttribute), false)) {
-				if (sra.ScriptName == resourceName) {
+				if (String.Compare (sra.ScriptName, res.Name, StringComparison.Ordinal) == 0) {
 					string scriptResourceName = sra.ScriptResourceName;
 					ResourceSet rset = null;
 					try {
@@ -326,15 +384,15 @@ namespace System.Web.Handlers {
 						writer.WriteLine ("Type.registerNamespace('" + ns.Substring (0, indx) + "')");
 					writer.Write ("{0}={{", sra.TypeName);
 					bool first = true;
-					foreach (DictionaryEntry entry in rset) {
-						string value = entry.Value as string;
+					foreach (DictionaryEntry de in rset) {
+						string value = de.Value as string;
 						if (value != null) {
 							if (first)
 								first = false;
 							else
 								writer.Write (',');
 							writer.WriteLine ();
-							writer.Write ("{0}:{1}", GetScriptStringLiteral ((string) entry.Key), GetScriptStringLiteral (value));
+							writer.Write ("{0}:{1}", GetScriptStringLiteral ((string) de.Key), GetScriptStringLiteral (value));
 						}
 					}
 					writer.WriteLine ();
@@ -383,6 +441,18 @@ namespace System.Web.Handlers {
 #if !SYSTEM_WEB_EXTENSIONS
 		bool System.Web.IHttpHandler.IsReusable { get { return true; } }
 #endif
+		sealed class EmbeddedResource
+		{
+			public string Name;
+			public string Url;
+			public WebResourceAttribute Attribute;
+		}
+		
+		sealed class AssemblyEmbeddedResources
+		{
+			public string AssemblyName = String.Empty;
+			public Dictionary <string, EmbeddedResource> Resources = new Dictionary <string, EmbeddedResource> (StringComparer.Ordinal);
+		}
 	}
 }
 

+ 1 - 1
mcs/class/System.Web/System.Web.Util/MachineKeySectionUtils.cs

@@ -182,7 +182,7 @@ namespace System.Web.Util {
 			return section.GetDecryptionKey ();
 		}
 
-		static byte [] GetValidationKey (MachineKeySection section)
+		public static byte [] GetValidationKey (MachineKeySection section)
 		{
 			return section.GetValidationKey ();
 		}