1// Copyright 2017 syzkaller project authors. All rights reserved. 2// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 4package dash 5 6import ( 7 "bytes" 8 "fmt" 9 "html/template" 10 "net/http" 11 "time" 12 13 "github.com/google/syzkaller/dashboard/dashapi" 14 "golang.org/x/net/context" 15 "google.golang.org/appengine" 16 "google.golang.org/appengine/log" 17 "google.golang.org/appengine/user" 18) 19 20// This file contains common middleware for UI handlers (auth, html templates, etc). 21 22type contextHandler func(c context.Context, w http.ResponseWriter, r *http.Request) error 23 24func handlerWrapper(fn contextHandler) http.Handler { 25 return handleContext(handleAuth(fn)) 26} 27 28func handleContext(fn contextHandler) http.Handler { 29 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 30 c := appengine.NewContext(r) 31 if err := fn(c, w, r); err != nil { 32 data := &struct { 33 Header *uiHeader 34 Error string 35 }{ 36 Header: commonHeader(c, r), 37 Error: err.Error(), 38 } 39 if err == ErrAccess { 40 w.WriteHeader(http.StatusForbidden) 41 err1 := templates.ExecuteTemplate(w, "forbidden.html", data) 42 if err1 != nil { 43 http.Error(w, err.Error(), http.StatusInternalServerError) 44 } 45 return 46 } 47 if _, dontlog := err.(ErrDontLog); !dontlog { 48 log.Errorf(c, "%v", err) 49 } 50 w.WriteHeader(http.StatusInternalServerError) 51 if err1 := templates.ExecuteTemplate(w, "error.html", data); err1 != nil { 52 http.Error(w, err.Error(), http.StatusInternalServerError) 53 } 54 } 55 }) 56} 57 58type ErrDontLog error 59 60func handleAuth(fn contextHandler) contextHandler { 61 return func(c context.Context, w http.ResponseWriter, r *http.Request) error { 62 if err := checkAccessLevel(c, r, config.AccessLevel); err != nil { 63 return err 64 } 65 return fn(c, w, r) 66 } 67} 68 69func serveTemplate(w http.ResponseWriter, name string, data interface{}) error { 70 buf := new(bytes.Buffer) 71 if err := templates.ExecuteTemplate(buf, name, data); err != nil { 72 return err 73 } 74 w.Write(buf.Bytes()) 75 return nil 76} 77 78type uiHeader struct { 79 LoginLink string 80 AnalyticsTrackingID string 81} 82 83func commonHeader(c context.Context, r *http.Request) *uiHeader { 84 h := &uiHeader{ 85 AnalyticsTrackingID: config.AnalyticsTrackingID, 86 } 87 if user.Current(c) == nil { 88 h.LoginLink, _ = user.LoginURL(c, r.URL.String()) 89 } 90 return h 91} 92 93func formatTime(t time.Time) string { 94 if t.IsZero() { 95 return "" 96 } 97 return t.Format("2006/01/02 15:04") 98} 99 100func formatClock(t time.Time) string { 101 if t.IsZero() { 102 return "" 103 } 104 return t.Format("15:04") 105} 106 107func formatDuration(d time.Duration) string { 108 if d == 0 { 109 return "" 110 } 111 days := int(d / (24 * time.Hour)) 112 hours := int(d / time.Hour % 24) 113 mins := int(d / time.Minute % 60) 114 if days >= 10 { 115 return fmt.Sprintf("%vd", days) 116 } else if days != 0 { 117 return fmt.Sprintf("%vd%02vh", days, hours) 118 } else if hours != 0 { 119 return fmt.Sprintf("%vh%02vm", hours, mins) 120 } 121 return fmt.Sprintf("%vm", mins) 122} 123 124func formatLateness(now, t time.Time) string { 125 if t.IsZero() { 126 return "never" 127 } 128 d := now.Sub(t) 129 if d < 5*time.Minute { 130 return "now" 131 } 132 return formatDuration(d) 133} 134 135func formatReproLevel(l dashapi.ReproLevel) string { 136 switch l { 137 case ReproLevelSyz: 138 return "syz" 139 case ReproLevelC: 140 return "C" 141 default: 142 return "" 143 } 144} 145 146func formatStat(v int64) string { 147 if v == 0 { 148 return "" 149 } 150 return fmt.Sprint(v) 151} 152 153var ( 154 templates = template.Must(template.New("").Funcs(templateFuncs).ParseGlob("*.html")) 155 156 templateFuncs = template.FuncMap{ 157 "formatTime": formatTime, 158 "formatClock": formatClock, 159 "formatDuration": formatDuration, 160 "formatLateness": formatLateness, 161 "formatReproLevel": formatReproLevel, 162 "formatStat": formatStat, 163 } 164) 165