blob: 4d28dcca1fff9a316152c136d3fea44eefa8b22d [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 main
import (
"bytes"
"encoding/base64"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"path/filepath"
"strings"
"text/template"
"v.io/jiri"
"v.io/x/lib/cmdline"
)
var (
portFlag int
)
const (
defaultRefreshMs = "5000"
)
var tmpl = template.Must(template.New("screenshot").Parse(`
<!DOCTYPE html>
<html>
<head>
<style>
body {
margin: 0px !important;
overflow: hidden;
}
img {
width: 100%;
height: 100%;
}
</style>
<script>
function loadScreenshot() {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange=function() {
if (xhr.readyState == 4 && xhr.status == 200) {
var data = JSON.parse(xhr.responseText);
var ele = document.getElementById('screenshot');
if (ele) {
ele.src = 'data:image/png;base64,' + data.Data;
}
}
}
xhr.open("GET", "/data?n={{ .ScreenshotName }}", true);
xhr.send();
}
function pageLoaded() {
loadScreenshot();
setInterval(loadScreenshot, {{ .RefreshMs }});
}
</script>
</head>
<body onload="pageLoaded()">
<img id="screenshot"></img>
</body>
</html>
`))
func init() {
cmdServe.Flags.IntVar(&portFlag, "port", 8000, "Port for the server.")
}
// cmdServe represents the 'serve' command of the vkiosk tool.
var cmdServe = &cmdline.Command{
Name: "serve",
Short: "Serve screenshots from local file system or Google Storage",
Long: "Serve screenshots from local file system or Google Storage.",
Runner: cmdline.RunnerFunc(runServe),
}
func runServe(env *cmdline.Env, _ []string) (e error) {
jirix, err := jiri.NewX(env)
if err != nil {
return err
}
// Start server.
http.HandleFunc("/data", func(w http.ResponseWriter, r *http.Request) {
dataHandler(jirix, w, r)
})
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
indexHandler(jirix, w, r)
})
if err := http.ListenAndServe(fmt.Sprintf(":%d", portFlag), nil); err != nil {
return fmt.Errorf("ListenAndServe(%d) failed: %v", portFlag, err)
}
return nil
}
// indexHandler handles requests for index.html.
func indexHandler(jirix *jiri.X, w http.ResponseWriter, r *http.Request) {
r.ParseForm()
// Parameter "n" specifies the name of the screenshot file.
name := r.Form.Get("n")
if name == "" {
respondWithError(jirix, fmt.Errorf("parameter 'n' not found"), w)
return
}
// Parameter "r" specifies the refresh interval.
refreshMs := r.Form.Get("r")
if refreshMs == "" {
refreshMs = defaultRefreshMs
}
data := struct {
ScreenshotName string
RefreshMs string
}{
ScreenshotName: name,
RefreshMs: refreshMs,
}
if err := tmpl.Execute(w, data); err != nil {
respondWithError(jirix, fmt.Errorf("Execute() failed: %v", err), w)
return
}
}
// dataHandler handles requests for /data.
// It reads screenshots from local file system or Google Storage, and returns
// base64 encoded string as JSON.
func dataHandler(jirix *jiri.X, w http.ResponseWriter, r *http.Request) {
r.ParseForm()
name := r.Form.Get("n")
if name == "" {
respondWithError(jirix, fmt.Errorf("parameter 'n' not found"), w)
return
}
bytes, err := readScreenshot(jirix, name)
if err != nil {
respondWithError(jirix, fmt.Errorf("%v", err), w)
return
}
encoded := base64.StdEncoding.EncodeToString(bytes)
jsonData := struct{ Data string }{
Data: encoded,
}
bytes, err = json.Marshal(&jsonData)
if err != nil {
respondWithError(jirix, fmt.Errorf("Marshal() failed: %v", err), w)
return
}
w.Header().Set("Content-Type", "application/json")
w.Write(bytes)
}
func readScreenshot(jirix *jiri.X, name string) ([]byte, error) {
if strings.HasPrefix(exportDirFlag, "gs://") {
args := []string{
"-q",
"cat",
exportDirFlag + "/" + name,
}
var output bytes.Buffer
if err := jirix.NewSeq().Capture(&output, &output).Last("gsutil", args...); err != nil {
return nil, err
}
return output.Bytes(), nil
}
return ioutil.ReadFile(filepath.Join(exportDirFlag, name))
}
func respondWithError(jirix *jiri.X, err error, w http.ResponseWriter) {
fmt.Fprintf(jirix.Stderr(), "%v\n", err)
http.Error(w, "500 internal server error", http.StatusInternalServerError)
}