Merge pull request #1510 from BrzVlad/fix-file-map
[mono.git] / mcs / class / System.Web.Extensions / System.Web.Handlers / ScriptResourceHandler.cs
index 373d9661fb40cb9fc440bf409ce4fc4632ceaf0a..721efc8657eb3d8303b0363a37de9937790a3239 100644 (file)
@@ -1,10 +1,12 @@
 //\r
 // ScriptResourceHandler.cs\r
 //\r
-// Author:\r
+// Authors:\r
 //   Igor Zelmanovich <igorz@mainsoft.com>\r
+//   Marek Habersack <grendel@twistedcode.net>\r
 //\r
 // (C) 2007 Mainsoft, Inc.  http://www.mainsoft.com\r
+// (C) 2011 Novell, Inc.  http://novell.com\r
 //\r
 //\r
 // Permission is hereby granted, free of charge, to any person obtaining\r
 //\r
 \r
 using System;\r
+using System.Collections;\r
 using System.Collections.Generic;\r
+using System.IO;\r
+using System.Security.Cryptography;\r
+using System.Reflection;\r
+using System.Resources;\r
 using System.Text;\r
+using System.Threading;\r
+using System.Web.Configuration;\r
+using System.Web.Hosting;\r
+using System.Web.UI;\r
+using System.Web.Util;\r
 \r
 namespace System.Web.Handlers\r
 {\r
        public partial class ScriptResourceHandler : IHttpHandler\r
-       {\r
+       {               \r
                protected virtual bool IsReusable {\r
                        get { return true; }\r
                }\r
@@ -50,7 +62,200 @@ namespace System.Web.Handlers
                }\r
 \r
                #endregion\r
+               void AppendResourceScriptContents (StringWriter sw, CompositeEntry entry)\r
+               {\r
+                       if (entry.Assembly == null || entry.Attribute == null || String.IsNullOrEmpty (entry.NameOrPath))\r
+                               return;\r
+\r
+                       using (Stream s = entry.Assembly.GetManifestResourceStream (entry.NameOrPath)) {\r
+                               if (s == null)\r
+                                       throw new HttpException (404, "Resource '" + entry.NameOrPath + "' not found");\r
+\r
+                               if (entry.Attribute.PerformSubstitution) {\r
+                                       using (var r = new StreamReader (s)) {\r
+                                               new PerformSubstitutionHelper (entry.Assembly).PerformSubstitution (r, sw);\r
+                                       }\r
+                               } else {\r
+                                       using (var r = new StreamReader (s)) {\r
+                                               string line = r.ReadLine ();\r
+                                               while (line != null) {\r
+                                                       sw.WriteLine (line);\r
+                                                       line = r.ReadLine ();\r
+                                               }\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+\r
+               void AppendFileScriptContents (StringWriter sw, CompositeEntry entry)\r
+               {\r
+                       // FIXME: should we limit the script size in any way?\r
+                       if (String.IsNullOrEmpty (entry.NameOrPath))\r
+                               return;\r
+\r
+                       string mappedPath;\r
+                       if (!HostingEnvironment.HaveCustomVPP) {\r
+                               // We'll take a shortcut here by bypassing the default VPP layers\r
+                               mappedPath = HostingEnvironment.MapPath (entry.NameOrPath);\r
+                               if (!File.Exists (mappedPath))\r
+                                       return;\r
+                               sw.Write (File.ReadAllText (mappedPath));\r
+                               return;\r
+                       }\r
+\r
+                       VirtualPathProvider vpp = HostingEnvironment.VirtualPathProvider;\r
+                       if (!vpp.FileExists (entry.NameOrPath))\r
+                               return;\r
+                       VirtualFile file = vpp.GetFile (entry.NameOrPath);\r
+                       if (file == null)\r
+                               return;\r
+                       using (Stream s = file.Open ()) {\r
+                               using (var r = new StreamReader (s)) {\r
+                                       string line = r.ReadLine ();\r
+                                       while (line != null) {\r
+                                               sw.WriteLine (line);\r
+                                               line = r.ReadLine ();\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+               \r
+               void AppendScriptContents (StringWriter sw, CompositeEntry entry)\r
+               {\r
+                       if (entry.Assembly != null)\r
+                               AppendResourceScriptContents (sw, entry);\r
+                       else\r
+                               AppendFileScriptContents (sw, entry);\r
+               }\r
+               \r
+               void SendCompositeScript (HttpContext context, HttpRequest request, bool notifyScriptLoaded, List <CompositeEntry> entries)\r
+               {\r
+                       if (entries.Count == 0)\r
+                               throw new HttpException (404, "Resource not found");\r
+\r
+                       long atime;\r
+                       DateTime modifiedSince;\r
+                       bool hasIfModifiedSince = HasIfModifiedSince (context.Request, out modifiedSince);\r
+                       \r
+                       if (hasIfModifiedSince) {\r
+                               bool notModified = true;\r
+                       \r
+                               foreach (CompositeEntry entry in entries) {\r
+                                       if (entry == null)\r
+                                               continue;\r
+                                       if (notModified) {\r
+                                               if (hasIfModifiedSince && entry.IsModifiedSince (modifiedSince))\r
+                                                       notModified = false;\r
+                                       }\r
+                               }\r
+\r
+                               if (notModified) {\r
+                                       RespondWithNotModified (context);\r
+                                       return;\r
+                               }\r
+                       }\r
+                       \r
+                       StringBuilder contents = new StringBuilder ();\r
+                       using (var sw = new StringWriter (contents)) {\r
+                               foreach (CompositeEntry entry in entries) {\r
+                                       if (entry == null)\r
+                                               continue;\r
+                                       AppendScriptContents (sw, entry);\r
+                               }\r
+                       }\r
+                       if (contents.Length == 0)\r
+                               throw new HttpException (404, "Resource not found");\r
+\r
+                       HttpResponse response = context.Response;\r
+                       DateTime utcnow = DateTime.UtcNow;\r
 \r
+                       response.ContentType = "text/javascript";\r
+                       response.Headers.Add ("Last-Modified", utcnow.ToString ("r"));\r
+                       response.ExpiresAbsolute = utcnow.AddYears (1);\r
+                       response.CacheControl = "public";\r
+\r
+                       response.Output.Write (contents.ToString ());\r
+                       if (notifyScriptLoaded)\r
+                               OutputScriptLoadedNotification (response.Output);\r
+               }\r
+               void OutputScriptLoadedNotification (TextWriter writer)\r
+               {\r
+                       writer.WriteLine ();\r
+                       writer.WriteLine ("if(typeof(Sys)!=='undefined')Sys.Application.notifyScriptLoaded();");\r
+               }\r
+               \r
+               protected virtual void ProcessRequest (HttpContext context)\r
+               {\r
+                       HttpRequest request = context.Request;\r
+                       bool notifyScriptLoaded = request.QueryString ["n"] == "t";\r
+                       List <CompositeEntry> compositeEntries = CompositeScriptReference.GetCompositeScriptEntries (request.RawUrl);\r
+                       if (compositeEntries != null) {\r
+                               SendCompositeScript (context, request, notifyScriptLoaded, compositeEntries);\r
+                               return;\r
+                       }\r
+                       EmbeddedResource res;\r
+                       Assembly assembly;                      \r
+                       SendEmbeddedResource (context, out res, out assembly);\r
+\r
+                       HttpResponse response = context.Response;\r
+                       TextWriter writer = response.Output;\r
+                       foreach (ScriptResourceAttribute sra in assembly.GetCustomAttributes (typeof (ScriptResourceAttribute), false)) {\r
+                               if (String.Compare (sra.ScriptName, res.Name, StringComparison.Ordinal) == 0) {\r
+                                       string scriptResourceName = sra.ScriptResourceName;\r
+                                       ResourceSet rset = null;\r
+                                       try {\r
+                                               rset = new ResourceManager (scriptResourceName, assembly).GetResourceSet (Threading.Thread.CurrentThread.CurrentUICulture, true, true);\r
+                                       }\r
+                                       catch (MissingManifestResourceException) {\r
+                                               if (scriptResourceName.EndsWith (".resources", RuntimeHelpers.StringComparison)) {\r
+                                                       scriptResourceName = scriptResourceName.Substring (0, scriptResourceName.Length - 10);\r
+                                                       rset = new ResourceManager (scriptResourceName, assembly).GetResourceSet (Threading.Thread.CurrentThread.CurrentUICulture, true, true);\r
+                                               }\r
+                                               else\r
+                                                       throw;\r
+                                       }\r
+                                       if (rset == null)\r
+                                               break;\r
+                                       writer.WriteLine ();\r
+                                       string ns = sra.TypeName;\r
+                                       int indx = ns.LastIndexOf ('.');\r
+                                       if (indx > 0)\r
+                                               writer.WriteLine ("Type.registerNamespace('" + ns.Substring (0, indx) + "')");\r
+                                       writer.Write ("{0}={{", sra.TypeName);\r
+                                       bool first = true;\r
+                                       foreach (DictionaryEntry de in rset) {\r
+                                               string value = de.Value as string;\r
+                                               if (value != null) {\r
+                                                       if (first)\r
+                                                               first = false;\r
+                                                       else\r
+                                                               writer.Write (',');\r
+                                                       writer.WriteLine ();\r
+                                                       writer.Write ("{0}:{1}", GetScriptStringLiteral ((string) de.Key), GetScriptStringLiteral (value));\r
+                                               }\r
+                                       }\r
+                                       writer.WriteLine ();\r
+                                       writer.WriteLine ("};");\r
+                                       break;\r
+                               }\r
+                       }\r
+                       \r
+                       if (notifyScriptLoaded)\r
+                               OutputScriptLoadedNotification (writer);\r
+               }\r
+               static void CheckIfResourceIsCompositeScript (string resourceName, ref bool includeTimeStamp)\r
+               {\r
+                       bool isCompositeScript = resourceName.StartsWith (CompositeScriptReference.COMPOSITE_SCRIPT_REFERENCE_PREFIX, StringComparison.Ordinal);\r
+                       if (!isCompositeScript)\r
+                               return;\r
+                       \r
+                       includeTimeStamp = false;\r
+               }\r
+\r
+               bool HandleCompositeScriptRequest (HttpContext context, HttpRequest request, string d)\r
+               {\r
+                       return false;\r
+               }\r
                // TODO: add value cache?\r
                static string GetScriptStringLiteral (string value)\r
                {\r
@@ -90,28 +295,5 @@ namespace System.Web.Handlers
                        \r
                        return sb.ToString ();\r
                }\r
-\r
-               class ResourceKey\r
-               {\r
-                       readonly string _resourceName;\r
-                       readonly bool _notifyScriptLoaded;\r
-\r
-                       public ResourceKey (string resourceName, bool notifyScriptLoaded) {\r
-                               _resourceName = resourceName;\r
-                               _notifyScriptLoaded = notifyScriptLoaded;\r
-                       }\r
-\r
-                       public override bool Equals (object obj) {\r
-                               if (!(obj is ResourceKey))\r
-                                       return base.Equals (obj);\r
-\r
-                               ResourceKey resKey = (ResourceKey) obj;\r
-                               return resKey._resourceName == this._resourceName && resKey._notifyScriptLoaded == _notifyScriptLoaded;\r
-                       }\r
-\r
-                       public override int GetHashCode () {\r
-                               return _resourceName.GetHashCode () ^ _notifyScriptLoaded.GetHashCode ();\r
-                       }\r
-               }\r
        }\r
 }\r