blob: 34a178281dce76bf8af774a47b3a69a244c33989 [file] [log] [blame]
// Copyright 2017 syzkaller project authors. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
package dash
import (
"bytes"
"fmt"
"html/template"
"net/http"
"time"
"github.com/google/syzkaller/dashboard/dashapi"
"golang.org/x/net/context"
"google.golang.org/appengine"
"google.golang.org/appengine/log"
"google.golang.org/appengine/user"
)
// This file contains common middleware for UI handlers (auth, html templates, etc).
type contextHandler func(c context.Context, w http.ResponseWriter, r *http.Request) error
func handlerWrapper(fn contextHandler) http.Handler {
return handleContext(handleAuth(fn))
}
func handleContext(fn contextHandler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
c := appengine.NewContext(r)
if err := fn(c, w, r); err != nil {
data := &struct {
Header *uiHeader
Error string
}{
Header: commonHeader(c, r),
Error: err.Error(),
}
if err == ErrAccess {
w.WriteHeader(http.StatusForbidden)
err1 := templates.ExecuteTemplate(w, "forbidden.html", data)
if err1 != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
return
}
if _, dontlog := err.(ErrDontLog); !dontlog {
log.Errorf(c, "%v", err)
}
w.WriteHeader(http.StatusInternalServerError)
if err1 := templates.ExecuteTemplate(w, "error.html", data); err1 != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
})
}
type ErrDontLog error
func handleAuth(fn contextHandler) contextHandler {
return func(c context.Context, w http.ResponseWriter, r *http.Request) error {
if err := checkAccessLevel(c, r, config.AccessLevel); err != nil {
return err
}
return fn(c, w, r)
}
}
func serveTemplate(w http.ResponseWriter, name string, data interface{}) error {
buf := new(bytes.Buffer)
if err := templates.ExecuteTemplate(buf, name, data); err != nil {
return err
}
w.Write(buf.Bytes())
return nil
}
type uiHeader struct {
LoginLink string
AnalyticsTrackingID string
}
func commonHeader(c context.Context, r *http.Request) *uiHeader {
h := &uiHeader{
AnalyticsTrackingID: config.AnalyticsTrackingID,
}
if user.Current(c) == nil {
h.LoginLink, _ = user.LoginURL(c, r.URL.String())
}
return h
}
func formatTime(t time.Time) string {
if t.IsZero() {
return ""
}
return t.Format("2006/01/02 15:04")
}
func formatClock(t time.Time) string {
if t.IsZero() {
return ""
}
return t.Format("15:04")
}
func formatDuration(d time.Duration) string {
if d == 0 {
return ""
}
days := int(d / (24 * time.Hour))
hours := int(d / time.Hour % 24)
mins := int(d / time.Minute % 60)
if days >= 10 {
return fmt.Sprintf("%vd", days)
} else if days != 0 {
return fmt.Sprintf("%vd%02vh", days, hours)
} else if hours != 0 {
return fmt.Sprintf("%vh%02vm", hours, mins)
}
return fmt.Sprintf("%vm", mins)
}
func formatLateness(now, t time.Time) string {
if t.IsZero() {
return "never"
}
d := now.Sub(t)
if d < 5*time.Minute {
return "now"
}
return formatDuration(d)
}
func formatReproLevel(l dashapi.ReproLevel) string {
switch l {
case ReproLevelSyz:
return "syz"
case ReproLevelC:
return "C"
default:
return ""
}
}
func formatStat(v int64) string {
if v == 0 {
return ""
}
return fmt.Sprint(v)
}
var (
templates = template.Must(template.New("").Funcs(templateFuncs).ParseGlob("*.html"))
templateFuncs = template.FuncMap{
"formatTime": formatTime,
"formatClock": formatClock,
"formatDuration": formatDuration,
"formatLateness": formatLateness,
"formatReproLevel": formatReproLevel,
"formatStat": formatStat,
}
)