[sre] Do we ever try to instantiate a generic instance? Assert that we don't.
[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                 void AppendResourceScriptContents (StringWriter sw, CompositeEntry entry)\r
66                 {\r
67                         if (entry.Assembly == null || entry.Attribute == null || String.IsNullOrEmpty (entry.NameOrPath))\r
68                                 return;\r
69 \r
70                         using (Stream s = entry.Assembly.GetManifestResourceStream (entry.NameOrPath)) {\r
71                                 if (s == null)\r
72                                         throw new HttpException (404, "Resource '" + entry.NameOrPath + "' not found");\r
73 \r
74                                 if (entry.Attribute.PerformSubstitution) {\r
75                                         using (var r = new StreamReader (s)) {\r
76                                                 new PerformSubstitutionHelper (entry.Assembly).PerformSubstitution (r, sw);\r
77                                         }\r
78                                 } else {\r
79                                         using (var r = new StreamReader (s)) {\r
80                                                 string line = r.ReadLine ();\r
81                                                 while (line != null) {\r
82                                                         sw.WriteLine (line);\r
83                                                         line = r.ReadLine ();\r
84                                                 }\r
85                                         }\r
86                                 }\r
87                         }\r
88                 }\r
89 \r
90                 void AppendFileScriptContents (StringWriter sw, CompositeEntry entry)\r
91                 {\r
92                         // FIXME: should we limit the script size in any way?\r
93                         if (String.IsNullOrEmpty (entry.NameOrPath))\r
94                                 return;\r
95 \r
96                         string mappedPath;\r
97                         if (!HostingEnvironment.HaveCustomVPP) {\r
98                                 // We'll take a shortcut here by bypassing the default VPP layers\r
99                                 mappedPath = HostingEnvironment.MapPath (entry.NameOrPath);\r
100                                 if (!File.Exists (mappedPath))\r
101                                         return;\r
102                                 sw.Write (File.ReadAllText (mappedPath));\r
103                                 return;\r
104                         }\r
105 \r
106                         VirtualPathProvider vpp = HostingEnvironment.VirtualPathProvider;\r
107                         if (!vpp.FileExists (entry.NameOrPath))\r
108                                 return;\r
109                         VirtualFile file = vpp.GetFile (entry.NameOrPath);\r
110                         if (file == null)\r
111                                 return;\r
112                         using (Stream s = file.Open ()) {\r
113                                 using (var r = new StreamReader (s)) {\r
114                                         string line = r.ReadLine ();\r
115                                         while (line != null) {\r
116                                                 sw.WriteLine (line);\r
117                                                 line = r.ReadLine ();\r
118                                         }\r
119                                 }\r
120                         }\r
121                 }\r
122                 \r
123                 void AppendScriptContents (StringWriter sw, CompositeEntry entry)\r
124                 {\r
125                         if (entry.Assembly != null)\r
126                                 AppendResourceScriptContents (sw, entry);\r
127                         else\r
128                                 AppendFileScriptContents (sw, entry);\r
129                 }\r
130                 \r
131                 void SendCompositeScript (HttpContext context, HttpRequest request, bool notifyScriptLoaded, List <CompositeEntry> entries)\r
132                 {\r
133                         if (entries.Count == 0)\r
134                                 throw new HttpException (404, "Resource not found");\r
135 \r
136                         long atime;\r
137                         DateTime modifiedSince;\r
138                         bool hasIfModifiedSince = HasIfModifiedSince (context.Request, out modifiedSince);\r
139                         \r
140                         if (hasIfModifiedSince) {\r
141                                 bool notModified = true;\r
142                         \r
143                                 foreach (CompositeEntry entry in entries) {\r
144                                         if (entry == null)\r
145                                                 continue;\r
146                                         if (notModified) {\r
147                                                 if (hasIfModifiedSince && entry.IsModifiedSince (modifiedSince))\r
148                                                         notModified = false;\r
149                                         }\r
150                                 }\r
151 \r
152                                 if (notModified) {\r
153                                         RespondWithNotModified (context);\r
154                                         return;\r
155                                 }\r
156                         }\r
157                         \r
158                         StringBuilder contents = new StringBuilder ();\r
159                         using (var sw = new StringWriter (contents)) {\r
160                                 foreach (CompositeEntry entry in entries) {\r
161                                         if (entry == null)\r
162                                                 continue;\r
163                                         AppendScriptContents (sw, entry);\r
164                                 }\r
165                         }\r
166                         if (contents.Length == 0)\r
167                                 throw new HttpException (404, "Resource not found");\r
168 \r
169                         HttpResponse response = context.Response;\r
170                         DateTime utcnow = DateTime.UtcNow;\r
171 \r
172                         response.ContentType = "text/javascript";\r
173                         response.Headers.Add ("Last-Modified", utcnow.ToString ("r"));\r
174                         response.ExpiresAbsolute = utcnow.AddYears (1);\r
175                         response.CacheControl = "public";\r
176 \r
177                         response.Output.Write (contents.ToString ());\r
178                         if (notifyScriptLoaded)\r
179                                 OutputScriptLoadedNotification (response.Output);\r
180                 }\r
181                 void OutputScriptLoadedNotification (TextWriter writer)\r
182                 {\r
183                         writer.WriteLine ();\r
184                         writer.WriteLine ("if(typeof(Sys)!=='undefined')Sys.Application.notifyScriptLoaded();");\r
185                 }\r
186                 \r
187                 protected virtual void ProcessRequest (HttpContext context)\r
188                 {\r
189                         HttpRequest request = context.Request;\r
190                         bool notifyScriptLoaded = request.QueryString ["n"] == "t";\r
191                         List <CompositeEntry> compositeEntries = CompositeScriptReference.GetCompositeScriptEntries (request.RawUrl);\r
192                         if (compositeEntries != null) {\r
193                                 SendCompositeScript (context, request, notifyScriptLoaded, compositeEntries);\r
194                                 return;\r
195                         }\r
196                         EmbeddedResource res;\r
197                         Assembly assembly;                      \r
198                         SendEmbeddedResource (context, out res, out assembly);\r
199 \r
200                         HttpResponse response = context.Response;\r
201                         TextWriter writer = response.Output;\r
202                         foreach (ScriptResourceAttribute sra in assembly.GetCustomAttributes (typeof (ScriptResourceAttribute), false)) {\r
203                                 if (String.Compare (sra.ScriptName, res.Name, StringComparison.Ordinal) == 0) {\r
204                                         string scriptResourceName = sra.ScriptResourceName;\r
205                                         ResourceSet rset = null;\r
206                                         try {\r
207                                                 rset = new ResourceManager (scriptResourceName, assembly).GetResourceSet (Threading.Thread.CurrentThread.CurrentUICulture, true, true);\r
208                                         }\r
209                                         catch (MissingManifestResourceException) {\r
210                                                 if (scriptResourceName.EndsWith (".resources", RuntimeHelpers.StringComparison)) {\r
211                                                         scriptResourceName = scriptResourceName.Substring (0, scriptResourceName.Length - 10);\r
212                                                         rset = new ResourceManager (scriptResourceName, assembly).GetResourceSet (Threading.Thread.CurrentThread.CurrentUICulture, true, true);\r
213                                                 }\r
214                                                 else\r
215                                                         throw;\r
216                                         }\r
217                                         if (rset == null)\r
218                                                 break;\r
219                                         writer.WriteLine ();\r
220                                         string ns = sra.TypeName;\r
221                                         int indx = ns.LastIndexOf ('.');\r
222                                         if (indx > 0)\r
223                                                 writer.WriteLine ("Type.registerNamespace('" + ns.Substring (0, indx) + "')");\r
224                                         writer.Write ("{0}={{", sra.TypeName);\r
225                                         bool first = true;\r
226                                         foreach (DictionaryEntry de in rset) {\r
227                                                 string value = de.Value as string;\r
228                                                 if (value != null) {\r
229                                                         if (first)\r
230                                                                 first = false;\r
231                                                         else\r
232                                                                 writer.Write (',');\r
233                                                         writer.WriteLine ();\r
234                                                         writer.Write ("{0}:{1}", GetScriptStringLiteral ((string) de.Key), GetScriptStringLiteral (value));\r
235                                                 }\r
236                                         }\r
237                                         writer.WriteLine ();\r
238                                         writer.WriteLine ("};");\r
239                                         break;\r
240                                 }\r
241                         }\r
242                         \r
243                         if (notifyScriptLoaded)\r
244                                 OutputScriptLoadedNotification (writer);\r
245                 }\r
246                 static void CheckIfResourceIsCompositeScript (string resourceName, ref bool includeTimeStamp)\r
247                 {\r
248                         bool isCompositeScript = resourceName.StartsWith (CompositeScriptReference.COMPOSITE_SCRIPT_REFERENCE_PREFIX, StringComparison.Ordinal);\r
249                         if (!isCompositeScript)\r
250                                 return;\r
251                         \r
252                         includeTimeStamp = false;\r
253                 }\r
254 \r
255                 bool HandleCompositeScriptRequest (HttpContext context, HttpRequest request, string d)\r
256                 {\r
257                         return false;\r
258                 }\r
259                 // TODO: add value cache?\r
260                 static string GetScriptStringLiteral (string value)\r
261                 {\r
262                         if (String.IsNullOrEmpty (value))\r
263                                 return "\"" + value + "\"";\r
264                         \r
265                         var sb = new StringBuilder ("\"");\r
266                         for (int i = 0; i < value.Length; i++) {\r
267                                 char ch = value [i];\r
268                                 switch (ch) {\r
269                                         case '\'':\r
270                                                 sb.Append ("\\u0027");\r
271                                                 break;\r
272 \r
273                                         case '"':\r
274                                                 sb.Append ("\\\"");\r
275                                                 break;\r
276 \r
277                                         case '\\':\r
278                                                 sb.Append ("\\\\");\r
279                                                 break;\r
280 \r
281                                         case '\n':\r
282                                                 sb.Append ("\\n");\r
283                                                 break;\r
284 \r
285                                         case '\r':\r
286                                                 sb.Append ("\\r");\r
287                                                 break;\r
288 \r
289                                         default:\r
290                                                 sb.Append (ch);\r
291                                                 break;\r
292                                 }\r
293                         }\r
294                         sb.Append ("\"");\r
295                         \r
296                         return sb.ToString ();\r
297                 }\r
298         }\r
299 }\r