Ctrl K
ComunidadGuías…

engine/prettyerror.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
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
	}
}