-
Notifications
You must be signed in to change notification settings - Fork 31
/
web.go
139 lines (121 loc) · 3.18 KB
/
web.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
//go:generate go-bindata-assetfs assets/...
package main
import (
"bytes"
"encoding/json"
"fmt"
"github.com/elazarl/go-bindata-assetfs"
"html/template"
"net/http"
"os"
"os/exec"
"reflect"
"runtime"
"golang.org/x/net/websocket"
)
// indexTmpl is a html template for index page.
var indexTmpl *template.Template
func init() {
indexTmpl = prepareTemplate()
}
// StartServer starts http-server and servers frontend code
// for benchmark results display.
func StartServer(bind string, ch chan interface{}, info *Info) error {
// Handle static files
var fs http.FileSystem
if DevMode() {
fs = http.Dir("assets")
} else {
fs = &assetfs.AssetFS{Asset: Asset, AssetDir: AssetDir, AssetInfo: AssetInfo, Prefix: "assets"}
}
http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(fs)))
// Index page handler
http.HandleFunc("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
handler(w, r, info)
}))
// handle pool of websocket channels
pool := make(WSPool)
go func() {
for {
val := <-ch
for _, conn := range pool {
conn.ch <- val
}
}
}()
// Websocket handler
http.Handle("/ws", websocket.Handler(func(ws *websocket.Conn) {
wshandler(ws, &pool)
}))
go StartBrowser("http://localhost" + bind)
return http.ListenAndServe(bind, nil)
}
// handler handles index page.
func handler(w http.ResponseWriter, r *http.Request, info *Info) {
err := indexTmpl.Execute(w, info)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
fmt.Println("[ERROR] failed to render template:", err)
return
}
}
// StartBrowser tries to open the URL in a browser
// and reports whether it succeeds.
//
// Orig. code: golang.org/x/tools/cmd/cover/html.go
func StartBrowser(url string) bool {
// try to start the browser
var args []string
switch runtime.GOOS {
case "darwin":
args = []string{"open"}
case "windows":
args = []string{"cmd", "/c", "start"}
default:
args = []string{"xdg-open"}
}
cmd := exec.Command(args[0], append(args[1:], url)...)
fmt.Println("If browser window didn't appear, please go to this url:", url)
return cmd.Start() == nil
}
// funcs for index template
var funcs = template.FuncMap{
"last": func(a interface{}) interface{} {
v := reflect.ValueOf(a)
switch v.Kind() {
case reflect.Slice, reflect.Array:
return v.Index(v.Len() - 1).Interface()
default:
return nil
}
},
"json_stripped": func(a interface{}) template.JS {
data, err := json.Marshal(a)
if err != nil {
fmt.Printf("[ERROR] failed to encode series: %v\n", err)
return ""
}
data = bytes.TrimPrefix(data, []byte("{"))
data = bytes.TrimSuffix(data, []byte("}"))
js := template.JS(string(data))
return js
},
}
// DevMode returns true if app is running in development mode.
func DevMode() bool {
devMode := os.Getenv("GOBENCHUI_DEV")
return devMode != ""
}
// prepareTemplate prepares and parses template.
func prepareTemplate() *template.Template {
t := template.New("index.html").Funcs(funcs)
// read from local filesystem for development
if DevMode() {
return template.Must(t.ParseFiles("assets/index.html"))
}
data, err := Asset("assets/index.html")
if err != nil {
panic(err)
}
return template.Must(t.Parse(string(data)))
}