| // 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) |
| } |
| |
| } |