blob: 7a637725d13626dfbf9e817229983df6f9629316 [file] [log] [blame]
// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package internal
import (
"fmt"
"net/http"
"path"
"strings"
"time"
"v.io/x/ref/services/ben"
)
// NewHTTPHandler returns a handler that provides web interface for browsing
// benchmark results in store.
func NewHTTPHandler(assets *Assets, store Store) http.Handler {
return &handler{assets, store}
}
type handler struct {
assets *Assets
store Store
}
func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if file := path.Base(r.URL.Path); strings.HasSuffix(file, ".css") || strings.HasSuffix(file, ".js") {
data, err := h.assets.File(file)
if err != nil {
w.WriteHeader(http.StatusNotFound)
fmt.Fprintln(w, "ERROR:", err)
return
}
if strings.HasSuffix(file, ".css") {
w.Header().Set("Content-Type", "text/css")
} else if strings.HasSuffix(file, ".js") {
w.Header().Set("Content-Type", "application/javascript")
}
w.Write(data)
return
}
if id := strings.TrimSpace(r.FormValue("id")); len(id) > 0 {
bm, itr := h.store.Runs(id)
defer itr.Close()
h.runs(w, bm, itr)
return
}
if qstr := strings.TrimSpace(r.FormValue("q")); len(qstr) > 0 {
query, err := ParseQuery(qstr)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
args := struct {
Query string
Error error
}{qstr, err}
h.executeTemplate(w, tmplBadQuery, args)
return
}
h.handleQuery(w, query)
return
}
if src := strings.TrimSpace(r.FormValue("s")); len(src) > 0 {
h.describeSource(w, src)
return
}
h.executeTemplate(w, tmplHome, nil)
}
func (h *handler) handleQuery(w http.ResponseWriter, query *Query) {
bmarks := h.store.Benchmarks(query)
defer bmarks.Close()
if !bmarks.Advance() {
h.executeTemplate(w, tmplNoBenchmarks, query)
return
}
bm := bmarks.Value()
// Advance once more, if there are more benchmarks (different names,
// different scenarios, different uploaders) for the same query, then
// present a list to choose from.
if !bmarks.Advance() {
itr := bmarks.Runs()
defer itr.Close()
h.runs(w, bm, itr)
return
}
h.benchmarks(w, query, bm, bmarks)
}
func (h *handler) benchmarks(w http.ResponseWriter, query *Query, first Benchmark, itr BenchmarkIterator) {
var (
cancel = make(chan struct{})
items = make(chan Benchmark, 2)
errs = make(chan error, 1)
)
defer close(cancel)
items <- first
items <- itr.Value()
go func() {
defer close(errs)
defer close(items)
for itr.Advance() {
select {
case items <- itr.Value():
case <-cancel:
return
}
if err := itr.Err(); err != nil {
errs <- err
}
}
}()
args := struct {
Query *Query
Items <-chan Benchmark
Err <-chan error
}{
Query: query,
Items: items,
Err: errs,
}
h.executeTemplate(w, tmplBenchmarks, args)
}
func (h *handler) runs(w http.ResponseWriter, bm Benchmark, itr RunIterator) {
type item struct {
Run ben.Run
SourceCodeID string
UploadTime time.Time
Index int
}
var (
cancel = make(chan struct{})
items = make(chan item)
errs = make(chan error, 1)
)
defer close(cancel)
go func() {
defer close(errs)
defer close(items)
idx := 0
for itr.Advance() {
idx++
r, s, t := itr.Value()
select {
case items <- item{r, s, t, idx}:
case <-cancel:
return
}
}
if err := itr.Err(); err != nil {
errs <- err
}
}()
args := struct {
Benchmark Benchmark
Items <-chan item
Err <-chan error
}{
Benchmark: bm,
Items: items,
Err: errs,
}
h.executeTemplate(w, tmplRuns, args)
return
}
func (h *handler) describeSource(w http.ResponseWriter, src string) {
w.Header().Set("Content-Type", "text/plain")
code, err := h.store.DescribeSource(src)
if err != nil {
fmt.Fprintf(w, "ERROR:%v", err)
return
}
fmt.Fprintln(w, code)
}
func (h *handler) executeTemplate(w http.ResponseWriter, tmpl string, args interface{}) {
t, err := h.assets.Template(tmpl)
if err != nil {
fmt.Fprintln(w, "ERROR:Failed to create template:", err)
return
}
if err := t.Execute(w, args); err != nil {
fmt.Fprintf(w, "ERROR:%v", err)
}
}