[Cleanup] Removed TARGET_JVM
[mono.git] / mcs / class / System.Web.Extensions / System.Web.Handlers / ScriptResourceHandler.cs
1 //\r
2 // ScriptResourceHandler.cs\r
3 //\r
4 // Authors:\r
5 //   Igor Zelmanovich <igorz@mainsoft.com>\r
6 //   Marek Habersack <grendel@twistedcode.net>\r
7 //\r
8 // (C) 2007 Mainsoft, Inc.  http://www.mainsoft.com\r
9 // (C) 2011 Novell, Inc.  http://novell.com\r
10 //\r
11 //\r
12 // Permission is hereby granted, free of charge, to any person obtaining\r
13 // a copy of this software and associated documentation files (the\r
14 // "Software"), to deal in the Software without restriction, including\r
15 // without limitation the rights to use, copy, modify, merge, publish,\r
16 // distribute, sublicense, and/or sell copies of the Software, and to\r
17 // permit persons to whom the Software is furnished to do so, subject to\r
18 // the following conditions:\r
19 // \r
20 // The above copyright notice and this permission notice shall be\r
21 // included in all copies or substantial portions of the Software.\r
22 // \r
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\r
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\r
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\r
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
30 //\r
31 \r
32 using System;\r
33 using System.Collections;\r
34 using System.Collections.Generic;\r
35 using System.IO;\r
36 using System.Security.Cryptography;\r
37 using System.Reflection;\r
38 using System.Resources;\r
39 using System.Text;\r
40 using System.Threading;\r
41 using System.Web.Configuration;\r
42 using System.Web.Hosting;\r
43 using System.Web.UI;\r
44 using System.Web.Util;\r
45 \r
46 namespace System.Web.Handlers\r
47 {\r
48         public partial class ScriptResourceHandler : IHttpHandler\r
49         {               \r
50                 protected virtual bool IsReusable {\r
51                         get { return true; }\r
52                 }\r
53 \r
54                 #region IHttpHandler Members\r
55 \r
56                 bool IHttpHandler.IsReusable {\r
57                         get { return IsReusable; }\r
58                 }\r
59 \r
60                 void IHttpHandler.ProcessRequest (HttpContext context) {\r
61                         ProcessRequest (context);\r
62                 }\r
63 \r
64                 #endregion\r
65 #if NET_3_5\r
66                 void AppendResourceScriptContents (StringWriter sw, CompositeEntry entry)\r
67                 {\r
68                         if (entry.Assembly == null || entry.Attribute == null || String.IsNullOrEmpty (entry.NameOrPath))\r
69                                 return;\r
70 \r
71                         using (Stream s = entry.Assembly.GetManifestResourceStream (entry.NameOrPath)) {\r
72                                 if (s == null)\r
73                                         throw new HttpException (404, "Resource '" + entry.NameOrPath + "' not found");\r
74 \r
75                                 if (entry.Attribute.PerformSubstitution) {\r
76                                         using (var r = new StreamReader (s)) {\r
77                                                 new PerformSubstitutionHelper (entry.Assembly).PerformSubstitution (r, sw);\r
78                                         }\r
79                                 } else {\r
80                                         using (var r = new StreamReader (s)) {\r
81                                                 string line = r.ReadLine ();\r
82                                                 while (line != null) {\r
83                                                         sw.WriteLine (line);\r
84                                                         line = r.ReadLine ();\r
85                                                 }\r
86                                         }\r
87                                 }\r
88                         }\r
89                 }\r
90 \r
91                 void AppendFileScriptContents (StringWriter sw, CompositeEntry entry)\r
92                 {\r
93                         // FIXME: should we limit the script size in any way?\r
94                         if (String.IsNullOrEmpty (entry.NameOrPath))\r
95                                 return;\r
96 \r
97                         string mappedPath;\r
98                         if (!HostingEnvironment.HaveCustomVPP) {\r
99                                 // We'll take a shortcut here by bypassing the default VPP layers\r
100                                 mappedPath = HostingEnvironment.MapPath (entry.NameOrPath);\r
101                                 if (!File.Exists (mappedPath))\r
102                                         return;\r
103                                 sw.Write (File.ReadAllText (mappedPath));\r
104                                 return;\r
105                         }\r
106 \r
107                         VirtualPathProvider vpp = HostingEnvironment.VirtualPathProvider;\r
108                         if (!vpp.FileExists (entry.NameOrPath))\r
109                                 return;\r
110                         VirtualFile file = vpp.GetFile (entry.NameOrPath);\r
111                         if (file == null)\r
112                                 return;\r
113                         using (Stream s = file.Open ()) {\r
114                                 using (var r = new StreamReader (s)) {\r
115                                         string line = r.ReadLine ();\r
116                                         while (line != null) {\r
117                                                 sw.WriteLine (line);\r
118                                                 line = r.ReadLine ();\r
119                                         }\r
120                                 }\r
121                         }\r
122                 }\r
123                 \r
124                 void AppendScriptContents (StringWriter sw, CompositeEntry entry)\r
125                 {\r
126                         if (entry.Assembly != null)\r
127                                 AppendResourceScriptContents (sw, entry);\r
128                         else\r
129                                 AppendFileScriptContents (sw, entry);\r
130                 }\r
131                 \r
132                 void SendCompositeScript (HttpContext context, HttpRequest request, bool notifyScriptLoaded, List <CompositeEntry> entries)\r
133                 {\r
134                         if (entries.Count == 0)\r
135                                 throw new HttpException (404, "Resource not found");\r
136 \r
137                         long atime;\r
138                         DateTime modifiedSince;\r
139                         bool hasIfModifiedSince = HasIfModifiedSince (context.Request, out modifiedSince);\r
140                         \r
141                         if (hasIfModifiedSince) {\r
142                                 bool notModified = true;\r
143                         \r
144                                 foreach (CompositeEntry entry in entries) {\r
145                                         if (entry == null)\r
146                                                 continue;\r
147                                         if (notModified) {\r
148                                                 if (hasIfModifiedSince && entry.IsModifiedSince (modifiedSince))\r
149                                                         notModified = false;\r
150                                         }\r
151                                 }\r
152 \r
153                                 if (notModified) {\r
154                                         RespondWithNotModified (context);\r
155                                         return;\r
156                                 }\r
157                         }\r
158                         \r
159                         StringBuilder contents = new StringBuilder ();\r
160                         using (var sw = new StringWriter (contents)) {\r
161                                 foreach (CompositeEntry entry in entries) {\r
162                                         if (entry == null)\r
163                                                 continue;\r
164                                         AppendScriptContents (sw, entry);\r
165                                 }\r
166                         }\r
167                         if (contents.Length == 0)\r
168                                 throw new HttpException (404, "Resource not found");\r
169 \r
170                         HttpResponse response = context.Response;\r
171                         DateTime utcnow = DateTime.UtcNow;\r
172 \r
173                         response.ContentType = "text/javascript";\r
174                         response.Headers.Add ("Last-Modified", utcnow.ToString ("r"));\r
175                         response.ExpiresAbsolute = utcnow.AddYears (1);\r
176                         response.CacheControl = "public";\r
177 \r
178                         response.Output.Write (contents.ToString ());\r
179                         if (notifyScriptLoaded)\r
180                                 OutputScriptLoadedNotification (response.Output);\r
181                 }\r
182 #endif\r
183                 void OutputScriptLoadedNotification (TextWriter writer)\r
184                 {\r
185                         writer.WriteLine ();\r
186                         writer.WriteLine ("if(typeof(Sys)!=='undefined')Sys.Application.notifyScriptLoaded();");\r
187                 }\r
188                 \r
189                 protected virtual void ProcessRequest (HttpContext context)\r
190                 {\r
191                         HttpRequest request = context.Request;\r
192                         bool notifyScriptLoaded = request.QueryString ["n"] == "t";\r
193 #if NET_3_5\r
194                         List <CompositeEntry> compositeEntries = CompositeScriptReference.GetCompositeScriptEntries (request.RawUrl);\r
195                         if (compositeEntries != null) {\r
196                                 SendCompositeScript (context, request, notifyScriptLoaded, compositeEntries);\r
197                                 return;\r
198                         }\r
199 #endif\r
200                         EmbeddedResource res;\r
201                         Assembly assembly;                      \r
202                         SendEmbeddedResource (context, out res, out assembly);\r
203 \r
204                         HttpResponse response = context.Response;\r
205                         TextWriter writer = response.Output;\r
206                         foreach (ScriptResourceAttribute sra in assembly.GetCustomAttributes (typeof (ScriptResourceAttribute), false)) {\r
207                                 if (String.Compare (sra.ScriptName, res.Name, StringComparison.Ordinal) == 0) {\r
208                                         string scriptResourceName = sra.ScriptResourceName;\r
209                                         ResourceSet rset = null;\r
210                                         try {\r
211                                                 rset = new ResourceManager (scriptResourceName, assembly).GetResourceSet (Threading.Thread.CurrentThread.CurrentUICulture, true, true);\r
212                                         }\r
213                                         catch (MissingManifestResourceException) {\r
214                                                 if (scriptResourceName.EndsWith (".resources", RuntimeHelpers.StringComparison)) {\r
215                                                         scriptResourceName = scriptResourceName.Substring (0, scriptResourceName.Length - 10);\r
216                                                         rset = new ResourceManager (scriptResourceName, assembly).GetResourceSet (Threading.Thread.CurrentThread.CurrentUICulture, true, true);\r
217                                                 }\r
218                                                 else\r
219                                                         throw;\r
220                                         }\r
221                                         if (rset == null)\r
222                                                 break;\r
223                                         writer.WriteLine ();\r
224                                         string ns = sra.TypeName;\r
225                                         int indx = ns.LastIndexOf ('.');\r
226                                         if (indx > 0)\r
227                                                 writer.WriteLine ("Type.registerNamespace('" + ns.Substring (0, indx) + "')");\r
228                                         writer.Write ("{0}={{", sra.TypeName);\r
229                                         bool first = true;\r
230                                         foreach (DictionaryEntry de in rset) {\r
231                                                 string value = de.Value as string;\r
232                                                 if (value != null) {\r
233                                                         if (first)\r
234                                                                 first = false;\r
235                                                         else\r
236                                                                 writer.Write (',');\r
237                                                         writer.WriteLine ();\r
238                                                         writer.Write ("{0}:{1}", GetScriptStringLiteral ((string) de.Key), GetScriptStringLiteral (value));\r
239                                                 }\r
240                                         }\r
241                                         writer.WriteLine ();\r
242                                         writer.WriteLine ("};");\r
243                                         break;\r
244                                 }\r
245                         }\r
246                         \r
247                         if (notifyScriptLoaded)\r
248                                 OutputScriptLoadedNotification (writer);\r
249                 }\r
250 #if NET_3_5\r
251                 static void CheckIfResourceIsCompositeScript (string resourceName, ref bool includeTimeStamp)\r
252                 {\r
253                         bool isCompositeScript = resourceName.StartsWith (CompositeScriptReference.COMPOSITE_SCRIPT_REFERENCE_PREFIX, StringComparison.Ordinal);\r
254                         if (!isCompositeScript)\r
255                                 return;\r
256                         \r
257                         includeTimeStamp = false;\r
258                 }\r
259 \r
260                 bool HandleCompositeScriptRequest (HttpContext context, HttpRequest request, string d)\r
261                 {\r
262                         return false;\r
263                 }\r
264 #endif\r
265                 // TODO: add value cache?\r
266                 static string GetScriptStringLiteral (string value)\r
267                 {\r
268                         if (String.IsNullOrEmpty (value))\r
269                                 return "\"" + value + "\"";\r
270                         \r
271                         var sb = new StringBuilder ("\"");\r
272                         for (int i = 0; i < value.Length; i++) {\r
273                                 char ch = value [i];\r
274                                 switch (ch) {\r
275                                         case '\'':\r
276                                                 sb.Append ("\\u0027");\r
277                                                 break;\r
278 \r
279                                         case '"':\r
280                                                 sb.Append ("\\\"");\r
281                                                 break;\r
282 \r
283                                         case '\\':\r
284                                                 sb.Append ("\\\\");\r
285                                                 break;\r
286 \r
287                                         case '\n':\r
288                                                 sb.Append ("\\n");\r
289                                                 break;\r
290 \r
291                                         case '\r':\r
292                                                 sb.Append ("\\r");\r
293                                                 break;\r
294 \r
295                                         default:\r
296                                                 sb.Append (ch);\r
297                                                 break;\r
298                                 }\r
299                         }\r
300                         sb.Append ("\"");\r
301                         \r
302                         return sb.ToString ();\r
303                 }\r
304         }\r
305 }\r