engine/prettyerror.go
Rama main · réplica de lectura del repositorio original.
package engine
import (
"bytes"
"html"
"html/template"
"net"
"net/http"
"path/filepath"
"strconv"
"strings"
)
const (
// Highlight of errors in the code
preHighlight = "<font style='color: red !important'>"
postHighlight = "</font>"
// HTML template for the error page
htmlTemplate = `<!doctype html>
<html>
<head>
<title>{{.Title}}</title>
<style>
body {
background-color: #f0f0f0;
color: #0b0b0b;
font-family: Lato,'Trebuchet MS',Helvetica,sans-serif;
font-weight: 300;
margin: 3.5em;
font-size: 1.3em;
}
h1 {
color: #101010;
}
div {
margin-bottom: 35pt;
}
#right {
text-align: right;
}
#wrap {
white-space: pre-wrap;
}
</style>
</head>
<body>
<div style="font-size: 3em; font-weight: bold;">{{.Title}}</div>
Contents of {{.Filename}}:
<div>
<pre><code>{{.Code}}</code></pre>
</div>
Error message:
<div>
<pre id="wrap"><code style="color: #A00000;">{{.ErrorMessage}}</code></pre>
</div>
<div id="right">{{.VersionString}}</div>
</body>
</html>`
)
// Given a lowercase string for the language, return an appropriate error page title
func errorPageTitle(lang string) string {
switch lang {
case "":
return "Error"
case "css":
return "CSS Error"
case "gcss":
return "GCSS Error"
case "html":
return "HTML Error"
case "jsx":
return "JSX Error"
case "lua":
return "Lua Error"
case "teal":
return "Teal Error"
default:
return lang + " Error"
}
}
// isLoopback returns true if the request originates from a loopback address
func isLoopback(req *http.Request) bool {
host, _, err := net.SplitHostPort(req.RemoteAddr)
if err != nil {
host = req.RemoteAddr
}
ip := net.ParseIP(host)
return ip != nil && ip.IsLoopback()
}
// PrettyError serves an informative error page to the user
func (ac *Config) PrettyError(w http.ResponseWriter, req *http.Request, filename string, filebytes []byte, errormessage, lang string) {
// Only show detailed error pages to loopback clients
if !isLoopback(req) {
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
// Prevent caching of error pages
w.Header().Set("Cache-Control", "no-store")
// HTTP status
w.WriteHeader(http.StatusOK)
// HTTP content type
w.Header().Add(contentType, htmlUTF8)
var (
code string
err error
)
// The line that the error refers to, for the case of Lua
linenr := -1
if len(filebytes) > 0 {
if lang == "lua" {
fields := strings.SplitN(errormessage, ":", 3)
if len(fields) > 2 {
numberfield := fields[1]
if strings.Contains(numberfield, "(") {
numberfield = strings.Split(numberfield, "(")[0]
}
linenr, err = strconv.Atoi(numberfield)
linenr--
if err != nil {
linenr = -1
}
}
} else if lang == "amber" {
if strings.Contains(errormessage, "- Line: ") {
fields := strings.SplitN(errormessage, "- Line: ", 2)
if strings.Contains(fields[1], ",") {
numberfields := strings.SplitN(fields[1], ",", 2)
linenr, err = strconv.Atoi(strings.TrimSpace(numberfields[0]))
linenr--
if err != nil {
linenr = -1
}
}
}
}
lines := strings.Split(string(filebytes), "\n")
for i, line := range lines {
lines[i] = html.EscapeString(line)
}
if (linenr >= 0) && (linenr < len(lines)) {
lines[linenr] = preHighlight + lines[linenr] + postHighlight
}
code = strings.Join(lines, "\n")
}
title := errorPageTitle(lang)
data := struct {
Title string
Filename string
Code template.HTML
ErrorMessage string
VersionString string
}{
Title: title,
Filename: filepath.Base(filename),
Code: template.HTML(code),
ErrorMessage: strings.TrimSpace(errormessage),
VersionString: ac.versionString,
}
tmpl, err := template.New("errorPage").Parse(htmlTemplate)
if err != nil {
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
if ac.autoRefresh {
var htmlbuf bytes.Buffer
if err := tmpl.Execute(&htmlbuf, data); err != nil {
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
w.Write(ac.InsertAutoRefresh(req, htmlbuf.Bytes()))
return
}
if err := tmpl.Execute(w, data); err != nil {
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
}