2009-06-04 Marek Habersack <mhabersack@novell.com>
[mono.git] / mcs / class / System.Web / System.Web / HttpException.cs
index 46c67029e4d7eb0575848bd8e7e55af09e35b820..6385ffe7cb325273c38e77e17cc1d297dcaada43 100644 (file)
@@ -36,6 +36,7 @@ using System.Security.Permissions;
 using System.Text;
 using System.Web.Util;
 using System.Web.Compilation;
+using System.Collections.Specialized;
 
 namespace System.Web
 {
@@ -48,7 +49,6 @@ namespace System.Web
        public class HttpException : ExternalException
        {
                const string DEFAULT_DESCRIPTION_TEXT = "Error processing request.";
-               const string BOTTOM_EXCEPTION_FORMAT = "<!--\r\n[{0}]: {1}\r\n-->\r\n";
                const string ERROR_404_DESCRIPTION = "The resource you are looking for (or one of its dependencies) could have been removed, had its name changed, or is temporarily unavailable.  Please review the following URL and make sure that it is spelled correctly.";
                
                int http_code = 500;
@@ -87,12 +87,17 @@ namespace System.Web
                }
                
 #if NET_2_0
-               protected HttpException (SerializationInfo info, StreamingContext context)
+               protected
+#else
+               internal
+#endif
+               HttpException (SerializationInfo info, StreamingContext context)
                        : base (info, context)
                {
                        http_code = info.GetInt32 ("_httpCode");
                }
 
+#if NET_2_0
                [SecurityPermission (SecurityAction.Demand, SerializationFormatter = true)]
                public override void GetObjectData (SerializationInfo info, StreamingContext context)
                {
@@ -118,7 +123,7 @@ namespace System.Web
                        http_code = httpCode;
                }
 
-               public HttpException (int httpCode, string message, Exception innerException, string resourceName)
+               internal HttpException (int httpCode, string message, Exception innerException, string resourceName)
                        : this (httpCode, message, innerException)
                {
                        resource_name = resourceName;
@@ -127,14 +132,21 @@ namespace System.Web
                public string GetHtmlErrorMessage ()
                {
                        try {
-                               if (HttpContext.Current.IsCustomErrorEnabled)
-                                       return GetCustomErrorDefaultMessage ();
+                               HttpContext ctx = HttpContext.Current;
+                               if (ctx != null && ctx.IsCustomErrorEnabled) {
+                                       if (http_code != 404 && http_code != 403)
+                                               return GetCustomErrorDefaultMessage ();
+                                       else
+                                               return GetDefaultErrorMessage (false);
+                               }
                                
                                if (!(this.InnerException is HtmlizedException))
-                                       return GetDefaultErrorMessage ();
+                                       return GetDefaultErrorMessage (true);
                                
                                return GetHtmlizedErrorMessage ();
-                       } catch {
+                       } catch (Exception ex) {
+                               Console.WriteLine (ex);
+                               
                                // we need the try/catch block in case the
                                // problem was with MapPath, which will cause
                                // IsCustomErrorEnabled to throw an exception
@@ -160,6 +172,23 @@ namespace System.Web
 
                void WriteFileTop (StringBuilder builder, string title)
                {
+#if TARGET_J2EE
+                       builder.AppendFormat ("<html><head><title>{0}</title><style type=\"text/css\">", title);
+                       builder.AppendFormat (
+                               @"body {{font-family:{0};font-weight:normal;font-size: 9pt;color:black;background-color: white}}
+p {{font-family:{0};font-weight:normal;color:black;margin-top: -5px}}
+b {{font-family:{0};font-weight:bold;color:black;margin-top: -5px}}
+h1 {{ font-family:{0};font-weight:normal;font-size:18pt;color:red }}
+h2 {{ font-family:{0};font-weight:normal;font-size:14pt;color:maroon }}
+pre {{font-family:""Lucida Console"",""DejaVu Sans Mono"",monospace;font-size: 10pt}}
+div.bodyText {{font-family: {0}}}
+table.sampleCode {{width: 100%; background-color: #ffffcc; }}
+.errorText {{color: red; font-weight: bold}}
+.marker {{font-weight: bold; color: black;text-decoration: none;}}
+.version {{color: gray;}}
+.error {{margin-bottom: 10px;}}
+.expandable {{ text-decoration:underline; font-weight:bold; color:navy; cursor:pointer; }}", errorStyleFonts);
+#else
                        builder.Append ("<?xml version=\"1.0\" ?>\n");
                        builder.Append ("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">");
                        builder.AppendFormat ("<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\"><head><title>{0}</title><style type=\"text/css\">", title);
@@ -169,28 +198,52 @@ p {{font-family:{0};font-weight:normal;color:black;margin-top: -5px}}
 b {{font-family:{0};font-weight:bold;color:black;margin-top: -5px}}
 h1 {{ font-family:{0};font-weight:normal;font-size:18pt;color:red }}
 h2 {{ font-family:{0};font-weight:normal;font-size:14pt;color:maroon }}
-pre {{font-family:""Lucida Console"",""DejaVu Sans Mono"",     monospace;font-size: 1.2em}}
+pre,code {{font-family:""Lucida Console"",""DejaVu Sans Mono"",monospace;font-size: 0.9em,white-space: pre-line}}
 div.bodyText {{font-family: {0}}}
 table.sampleCode {{width: 100%; background-color: #ffffcc; }}
 .errorText {{color: red; font-weight: bold}}
 .marker {{font-weight: bold; color: black;text-decoration: none;}}
 .version {{color: gray;}}
 .error {{margin-bottom: 10px;}}
-.expandable {{ text-decoration:underline; font-weight:bold; color:navy; cursor:hand; }}", errorStyleFonts);
+.expandable {{ text-decoration:underline; font-weight:bold; color:navy; cursor:pointer; }}", errorStyleFonts);
+#endif
 
                        builder.AppendFormat (
                                "</style></head><body><h1>Server Error in '{0}' Application</h1><hr style=\"color: silver\"/>",
                                HtmlEncode (HttpRuntime.AppDomainAppVirtualPath));
                }
                
-               void WriteFileBottom (StringBuilder builder, Exception ex1, Exception ex2)
+               void WriteFileBottom (StringBuilder builder, bool showTrace)
                {
-                       builder.Append ("<hr style=\"color: silver\"/>");
-                       builder.AppendFormat ("<strong>Version information: </strong> Mono Version: {0}; ASP.NET Version: {0}</body></html>\r\n", Environment.Version);
-                       if (ex1 != null)
-                               builder.AppendFormat (BOTTOM_EXCEPTION_FORMAT, ex1.GetType (), ex1.ToString ());
-                       if (ex2 != null)
-                               builder.AppendFormat (BOTTOM_EXCEPTION_FORMAT, ex2.GetType (), ex2.ToString ());
+                       if (showTrace) {
+                               builder.Append ("<hr style=\"color: silver\"/>");
+                               builder.AppendFormat ("<strong>Version information: </strong> Mono Version: {0}; ASP.NET Version: {0}</body></html>\r\n", Environment.Version);
+                       
+                               string trace, message;
+                               bool haveTrace;
+                               Exception ex = this;
+
+                               builder.Append ("\r\n<!--");
+                               while (ex != null) {
+                                       trace = ex.StackTrace;
+                                       message = ex.Message;
+                                       haveTrace = (trace != null && trace.Length > 0);
+                               
+                                       if (!haveTrace && (message == null || message.Length == 0)) {
+                                               ex = ex.InnerException;
+                                               continue;
+                                       }
+
+                                       builder.Append ("\r\n[" + ex.GetType () + "]: " + HtmlEncode (message) + "\r\n");
+                                       if (haveTrace)
+                                               builder.Append (ex.StackTrace);
+                               
+                                       ex = ex.InnerException;
+                               }
+                       
+                               builder.Append ("\r\n-->");
+                       } else
+                               builder.Append ("</body></html>\r\n");
                }
 
                string GetCustomErrorDefaultMessage ()
@@ -241,11 +294,11 @@ table.sampleCode {{width: 100%; background-color: #ffffcc; }}
 
     &lt;/system.web&gt;
 &lt;/configuration&gt;</pre></td></tr></table>");
-                       WriteFileBottom (builder, null, null);
+                       WriteFileBottom (builder, false);
                        return builder.ToString ();
                }
                
-               string GetDefaultErrorMessage ()
+               string GetDefaultErrorMessage (bool showTrace)
                {
                        Exception ex, baseEx;
                        ex = baseEx = GetBaseException ();
@@ -258,7 +311,7 @@ table.sampleCode {{width: 100%; background-color: #ffffcc; }}
                        if (http_code == 404)
                                builder.Append ("The resource cannot be found.");
                        else
-                               builder.AppendFormat (HtmlEncode (ex.Message));
+                               builder.Append (HtmlEncode (ex.Message));
                        builder.Append ("</em></h2>\r\n<p><strong>Description: </strong>");
                        
                        if (http_code != 0)
@@ -267,15 +320,15 @@ table.sampleCode {{width: 100%; background-color: #ffffcc; }}
                        builder.Append ("</p>\r\n");
 
                        if (resource_name != null && resource_name.Length > 0)
-                               builder.AppendFormat ("<p><strong>Resource URL: </strong>{0}</p>\r\n", resource_name);
+                               builder.AppendFormat ("<p><strong>Requested URL: </strong>{0}</p>\r\n", resource_name);
                        
-                       if (baseEx != null && http_code != 404) {
+                       if (showTrace && baseEx != null && http_code != 404 && http_code != 403) {
                                builder.Append ("<p><strong>Stack Trace: </strong></p>");
                                builder.Append ("<table summary=\"Stack Trace\" class=\"sampleCode\">\r\n<tr><td>");
                                WriteTextAsCode (builder, baseEx.ToString ());
                                builder.Append ("</td></tr>\r\n</table>\r\n");
                        }
-                       WriteFileBottom (builder, ex, null);
+                       WriteFileBottom (builder, showTrace);
                        
                        return builder.ToString ();
                }
@@ -293,73 +346,148 @@ table.sampleCode {{width: 100%; background-color: #ffffcc; }}
                {
                        StringBuilder builder = new StringBuilder ();
                        HtmlizedException exc = (HtmlizedException) this.InnerException;
+#if TARGET_J2EE
+                       bool isParseException = false;
+                       bool isCompileException = false;
+#else
+                       bool isParseException = exc is ParseException;
+                       bool isCompileException = (!isParseException && exc is CompilationException);
+#endif
+                       
                        WriteFileTop (builder, exc.Title);
                        builder.AppendFormat ("<h2><em>{0}</em></h2>\r\n", exc.Title);
                        builder.AppendFormat ("<p><strong>Description: </strong>{0}\r\n</p>\r\n", HtmlEncode (exc.Description));
-                       string errorMessage = HtmlEncode (exc.ErrorMessage).Replace ("\n", "<br/>");
-                       builder.AppendFormat ("<p><strong>Error message: </strong></p><p>{0}</p>", errorMessage);
-
-                       if (exc.FileName != null)
-                               builder.AppendFormat ("<p><strong>File name: </strong> {0}</p>", HtmlEncode (exc.FileName));
+                       string errorMessage = HtmlEncode (exc.ErrorMessage);
+                       
+                       builder.Append ("<p><strong>");
+                       if (isParseException)
+                               builder.Append ("Parser ");
+                       else if (isCompileException)
+                               builder.Append ("Compiler ");
+                       
+                       builder.Append ("Error Message: </strong>");
+#if NET_2_0
+                       builder.AppendFormat ("<code>{0}</code></p>", errorMessage);
+#else
+                       builder.AppendFormat ("<blockquote><pre>{0}</pre></blockquote></p>", errorMessage);
+#endif
 
+                       StringBuilder longCodeVersion = null;
+                       
                        if (exc.FileText != null) {
-                               if (exc.SourceFile != exc.FileName)
-                                       builder.AppendFormat ("<p><strong>Source File: </strong>{0}</p>", exc.SourceFile);
-
-                               if (exc is ParseException) {
-                                       builder.Append ("<p>&nbsp;&nbsp;&nbsp;&nbsp;<strong>Line: </strong>");
-                                       builder.Append (exc.ErrorLines [0]);
-                                       builder.Append ("</p>");
-                               }
-
-                               if (exc is ParseException) {
-                                       builder.Append ("<strong>Source Error: </strong>\r\n");
+                               if (isParseException || isCompileException) {
+                                       builder.Append ("<p><strong>Source Error: </strong></p>\r\n");
                                        builder.Append ("<table summary=\"Source error\" class=\"sampleCode\">\r\n<tr><td>");
-                                       WriteSource (builder, exc);
-                                       builder.Append ("</td></tr>\r\n</table>\r\n");
+
+                                       if (isCompileException)
+                                               longCodeVersion = new StringBuilder ();
+                                       WriteSource (builder, longCodeVersion, exc);
+                                       builder.Append ("</pre></code></td></tr>\r\n</table>\r\n");
                                } else {
                                        builder.Append ("<table summary=\"Source file\" class=\"sampleCode\">\r\n<tr><td>");
-                                       WriteSource (builder, exc);
-                                       builder.Append ("</td></tr>\r\n</table>\r\n");
+                                       WriteSource (builder, null, exc);
+                                       builder.Append ("</pre></code></td></tr>\r\n</table>\r\n");
                                }
-                       }                       
 
-                       WriteFileBottom (
-                               builder,
-                               exc,
-                               null
-                       );
+                               builder.Append ("<br/><p><strong>Source File: </strong>");
+                               if (exc.SourceFile != exc.FileName)
+                                       builder.Append (exc.SourceFile);
+                               else
+                                       builder.Append (exc.FileName);
+
+                               if (isParseException || isCompileException) {
+                                       int[] errorLines = exc.ErrorLines;
+                                       int numErrors = errorLines != null ? errorLines.Length : 0;
+                                       if (numErrors > 0) {
+                                               builder.AppendFormat ("&nbsp;&nbsp;<strong>Line{0}: </strong>", numErrors > 1 ? "s" : String.Empty);
+                                               for (int i = 0; i < numErrors; i++) {
+                                                       if (i > 0)
+                                                               builder.Append (", ");
+                                                       builder.Append (exc.ErrorLines [i]);
+                                               }
+                                       }
+                               }
+                               builder.Append ("</p>");
+                       } else if (exc.FileName != null)
+                               builder.AppendFormat ("{0}</p>", HtmlEncode (exc.FileName));
+
+                       bool needToggleJS = false;
+                       
+#if !TARGET_J2EE
+                       if (isCompileException) {
+                               CompilationException cex = exc as CompilationException;
+                               StringCollection output = cex.CompilerOutput;
+
+                               if (output != null && output.Count > 0) {
+                                       needToggleJS = true;
+                                       StringBuilder sb = new StringBuilder ();
+                                       foreach (string s in output)
+                                               sb.Append (s + "\r\n");
+                                       WriteExpandableBlock (builder, "compilerOutput", "Show Detailed Compiler Output", sb.ToString ());
+                               }
+                       }
+#endif
+                       
+                       if (longCodeVersion != null && longCodeVersion.Length > 0) {
+                               WriteExpandableBlock (builder, "fullCode", "Show Complete Compilation Source", longCodeVersion.ToString ());
+                               needToggleJS = true;
+                       }
+
+                       if (needToggleJS)
+                               builder.Append ("<script type=\"text/javascript\">\r\n" +
+                                               "function ToggleVisible (id)\r\n" +
+                                               "{\r\n" +
+                                               "\tvar e = document.getElementById (id);\r\n" +
+                                               "\tif (e.style.display == 'none')\r\n" +
+                                               "\t{\r\n" +
+                                               "\t\te.style.display = '';\r\n" +
+                                               "\t} else {\r\n" +
+                                               "\t\te.style.display = 'none';\r\n" +
+                                               "\t}\r\n" +
+                                               "}\r\n" +
+                                               "</script>\r\n");
+                       
+                       WriteFileBottom (builder, true);
                        
                        return builder.ToString ();
                }
 
+               static void WriteExpandableBlock (StringBuilder builder, string id, string title, string contents)
+               {
+                       builder.AppendFormat ("<br><div class=\"expandable\" onclick=\"ToggleVisible ('{1}')\">{0}:</div><br/>" +
+                                             "<div id=\"{1}\" style=\"display: none\"><table summary=\"Details\" class=\"sampleCode\"><tr><td>" +
+                                             "<code><pre>\r\n", title, id);
+                       builder.Append (contents);
+                       builder.Append ("</pre></code></td></tr></table></div>");
+               }
+               
                static void WriteTextAsCode (StringBuilder builder, string text)
                {
                        builder.AppendFormat ("<pre>{0}</pre>", HtmlEncode (text));
                }
 
 #if TARGET_J2EE
-               static void WriteSource (StringBuilder builder, HtmlizedException e)
+               static void WriteSource (StringBuilder builder, StringBuilder longVersion, HtmlizedException e)
                {
-                       builder.Append ("<pre>");
+                       builder.Append ("<code><pre>");
                        WritePageSource (builder, e);
-                       builder.Append ("</pre>\r\n");
+                       builder.Append ("</code></pre>\r\n");
                }
 
 #else
-               static void WriteSource (StringBuilder builder, HtmlizedException e)
+               static void WriteSource (StringBuilder builder, StringBuilder longVersion, HtmlizedException e)
                {
-                       builder.Append ("<pre>");
+                       builder.Append ("<code><pre>");
                        if (e is CompilationException)
-                               WriteCompilationSource (builder, e);
+                               WriteCompilationSource (builder, longVersion, e);
                        else
                                WritePageSource (builder, e);
 
-                       builder.Append ("</pre>\r\n");
+                       builder.Append ("<code></pre>\r\n");
                }
 #endif
                
-               static void WriteCompilationSource (StringBuilder builder, HtmlizedException e)
+               static void WriteCompilationSource (StringBuilder builder, StringBuilder longVersion, HtmlizedException e)
                {
                        int [] a = e.ErrorLines;
                        string s;
@@ -369,21 +497,42 @@ table.sampleCode {{width: 100%; background-color: #ffffcc; }}
 
                        if (a != null && a.Length > 0)
                                errline = a [0];
-                       
-                       TextReader reader = new StringReader (e.FileText);
-                       while ((s = reader.ReadLine ()) != null) {
-                               line++;
 
-                               if (errline == line)
-                                       builder.Append ("<span style=\"color: red\">");
-
-                               builder.AppendFormat ("Line {0}: {1}\r\n", line, HtmlEncode (s));
-
-                               if (line == errline) {
-                                       builder.Append ("</span>");
-                                       errline = (++index < a.Length) ? a [index] : 0;
+                       int begin = errline - 2;
+                       int end = errline + 2;
+
+                       if (begin < 0)
+                               begin = 0;
+
+                       string tmp;                     
+                       using (TextReader reader = new StringReader (e.FileText)) {
+                               while ((s = reader.ReadLine ()) != null) {
+                                       line++;
+                                       if (line < begin || line > end) {
+                                               if (longVersion != null)
+                                                       longVersion.AppendFormat ("Line {0}: {1}\r\n", line, HtmlEncode (s));
+                                               continue;
+                                       }
+                               
+                                       if (errline == line) {
+                                               if (longVersion != null)
+                                                       longVersion.Append ("<span style=\"color: red\">");
+                                               builder.Append ("<span style=\"color: red\">");
+                                       }
+                                       
+                                       tmp = String.Format ("Line {0}: {1}\r\n", line, HtmlEncode (s));
+                                       builder.Append (tmp);
+                                       if (longVersion != null)
+                                               longVersion.Append (tmp);
+                                       
+                                       if (line == errline) {
+                                               builder.Append ("</span>");
+                                               if (longVersion != null)
+                                                       longVersion.Append ("</span>");
+                                               errline = (++index < a.Length) ? a [index] : 0;
+                                       }
                                }
-                       }
+                       }                       
                }
 
                static void WritePageSource (StringBuilder builder, HtmlizedException e)
@@ -392,8 +541,8 @@ table.sampleCode {{width: 100%; background-color: #ffffcc; }}
                        int line = 0;
                        int beginerror = e.ErrorLines [0];
                        int enderror = e.ErrorLines [1];
-                       int begin = beginerror - 3;
-                       int end = enderror + 3;
+                       int begin = beginerror - 2;
+                       int end = enderror + 2;
                        if (begin <= 0)
                                begin = 1;
                        
@@ -409,7 +558,7 @@ table.sampleCode {{width: 100%; background-color: #ffffcc; }}
                                if (beginerror == line)
                                        builder.Append ("<span style=\"color: red\">");
 
-                               builder.AppendFormat ("{0}\r\n", HtmlEncode (s));
+                               builder.AppendFormat ("Line {0}: {1}\r\n", line, HtmlEncode (s));
 
                                if (enderror <= line) {
                                        builder.Append ("</span>");