Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(3)

Unified Diff: experimental/webtry/webtry.go

Issue 688713002: delete webtry from main skia repo; it's been moved to buildbots (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « experimental/webtry/templates/workspace.html ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: experimental/webtry/webtry.go
diff --git a/experimental/webtry/webtry.go b/experimental/webtry/webtry.go
deleted file mode 100644
index 9b148343f3261851b3b74e14b3e4749dee7d81af..0000000000000000000000000000000000000000
--- a/experimental/webtry/webtry.go
+++ /dev/null
@@ -1,987 +0,0 @@
-package main
-
-import (
- "bytes"
- "crypto/md5"
- "database/sql"
- "encoding/base64"
- "encoding/binary"
- "encoding/json"
- "flag"
- "fmt"
- htemplate "html/template"
- "image"
- _ "image/gif"
- _ "image/jpeg"
- "image/png"
- "io/ioutil"
- "math/rand"
- "net"
- "net/http"
- "os"
- "os/exec"
- "path/filepath"
- "regexp"
- "strconv"
- "strings"
- "text/template"
- "time"
-)
-
-import (
- "github.com/fiorix/go-web/autogzip"
- _ "github.com/go-sql-driver/mysql"
- "github.com/golang/glog"
- _ "github.com/mattn/go-sqlite3"
- "github.com/rcrowley/go-metrics"
-)
-
-const (
- DEFAULT_SAMPLE = `void draw(SkCanvas* canvas) {
- SkPaint p;
- p.setColor(SK_ColorRED);
- p.setAntiAlias(true);
- p.setStyle(SkPaint::kStroke_Style);
- p.setStrokeWidth(10);
-
- canvas->drawLine(20, 20, 100, 100, p);
-}`
- // Don't increase above 2^16 w/o altering the db tables to accept something bigger than TEXT.
- MAX_TRY_SIZE = 64000
-)
-
-var (
- // codeTemplate is the cpp code template the user's code is copied into.
- codeTemplate *template.Template = nil
-
- // gypTemplate is the GYP file to build the executable containing the user's code.
- gypTemplate *template.Template = nil
-
- // indexTemplate is the main index.html page we serve.
- indexTemplate *htemplate.Template = nil
-
- // iframeTemplate is the main index.html page we serve.
- iframeTemplate *htemplate.Template = nil
-
- // recentTemplate is a list of recent images.
- recentTemplate *htemplate.Template = nil
-
- // workspaceTemplate is the page for workspaces, a series of webtrys.
- workspaceTemplate *htemplate.Template = nil
-
- // db is the database, nil if we don't have an SQL database to store data into.
- db *sql.DB = nil
-
- // directLink is the regex that matches URLs paths that are direct links.
- directLink = regexp.MustCompile("^/c/([a-f0-9]+)$")
-
- // iframeLink is the regex that matches URLs paths that are links to iframes.
- iframeLink = regexp.MustCompile("^/iframe/([a-f0-9]+)$")
-
- // imageLink is the regex that matches URLs paths that are direct links to PNGs.
- imageLink = regexp.MustCompile("^/i/([a-z0-9-_]+.png)$")
-
- // tryInfoLink is the regex that matches URLs paths that are direct links to data about a single try.
- tryInfoLink = regexp.MustCompile("^/json/([a-f0-9]+)$")
-
- // workspaceLink is the regex that matches URLs paths for workspaces.
- workspaceLink = regexp.MustCompile("^/w/([a-z0-9-]+)$")
-
- // errorRE is ther regex that matches compiler errors and extracts the line / column information.
- errorRE = regexp.MustCompile("^.*.cpp:(\\d+):(\\d+):\\s*(.*)")
-
- // workspaceNameAdj is a list of adjectives for building workspace names.
- workspaceNameAdj = []string{
- "autumn", "hidden", "bitter", "misty", "silent", "empty", "dry", "dark",
- "summer", "icy", "delicate", "quiet", "white", "cool", "spring", "winter",
- "patient", "twilight", "dawn", "crimson", "wispy", "weathered", "blue",
- "billowing", "broken", "cold", "damp", "falling", "frosty", "green",
- "long", "late", "lingering", "bold", "little", "morning", "muddy", "old",
- "red", "rough", "still", "small", "sparkling", "throbbing", "shy",
- "wandering", "withered", "wild", "black", "young", "holy", "solitary",
- "fragrant", "aged", "snowy", "proud", "floral", "restless", "divine",
- "polished", "ancient", "purple", "lively", "nameless",
- }
-
- // workspaceNameNoun is a list of nouns for building workspace names.
- workspaceNameNoun = []string{
- "waterfall", "river", "breeze", "moon", "rain", "wind", "sea", "morning",
- "snow", "lake", "sunset", "pine", "shadow", "leaf", "dawn", "glitter",
- "forest", "hill", "cloud", "meadow", "sun", "glade", "bird", "brook",
- "butterfly", "bush", "dew", "dust", "field", "fire", "flower", "firefly",
- "feather", "grass", "haze", "mountain", "night", "pond", "darkness",
- "snowflake", "silence", "sound", "sky", "shape", "surf", "thunder",
- "violet", "water", "wildflower", "wave", "water", "resonance", "sun",
- "wood", "dream", "cherry", "tree", "fog", "frost", "voice", "paper",
- "frog", "smoke", "star",
- }
-
- gitHash = ""
- gitInfo = ""
-
- requestsCounter = metrics.NewRegisteredCounter("requests", metrics.DefaultRegistry)
-)
-
-// flags
-var (
- useChroot = flag.Bool("use_chroot", false, "Run the compiled code in the schroot jail.")
- port = flag.String("port", ":8000", "HTTP service address (e.g., ':8000')")
-)
-
-// lineNumbers adds #line numbering to the user's code.
-func LineNumbers(c string) string {
- lines := strings.Split(c, "\n")
- ret := []string{}
- for i, line := range lines {
- ret = append(ret, fmt.Sprintf("#line %d", i+1))
- ret = append(ret, line)
- }
- return strings.Join(ret, "\n")
-}
-
-func Init() {
- rand.Seed(time.Now().UnixNano())
-
- // Change the current working directory to the directory of the executable.
- cwd, err := filepath.Abs(filepath.Dir(os.Args[0]))
- if err != nil {
- glog.Fatal(err)
- }
- if err := os.Chdir(cwd); err != nil {
- glog.Fatal(err)
- }
-
- codeTemplate = template.Must(template.ParseFiles(filepath.Join(cwd, "templates/template.cpp")))
- gypTemplate = template.Must(template.ParseFiles(filepath.Join(cwd, "templates/template.gyp")))
- indexTemplate = htemplate.Must(htemplate.ParseFiles(
- filepath.Join(cwd, "templates/index.html"),
- filepath.Join(cwd, "templates/titlebar.html"),
- filepath.Join(cwd, "templates/sidebar.html"),
- filepath.Join(cwd, "templates/content.html"),
- filepath.Join(cwd, "templates/headercommon.html"),
- filepath.Join(cwd, "templates/footercommon.html"),
- ))
- iframeTemplate = htemplate.Must(htemplate.ParseFiles(
- filepath.Join(cwd, "templates/iframe.html"),
- filepath.Join(cwd, "templates/content.html"),
- filepath.Join(cwd, "templates/headercommon.html"),
- filepath.Join(cwd, "templates/footercommon.html"),
- ))
- recentTemplate = htemplate.Must(htemplate.ParseFiles(
- filepath.Join(cwd, "templates/recent.html"),
- filepath.Join(cwd, "templates/titlebar.html"),
- filepath.Join(cwd, "templates/sidebar.html"),
- filepath.Join(cwd, "templates/headercommon.html"),
- filepath.Join(cwd, "templates/footercommon.html"),
- ))
- workspaceTemplate = htemplate.Must(htemplate.ParseFiles(
- filepath.Join(cwd, "templates/workspace.html"),
- filepath.Join(cwd, "templates/titlebar.html"),
- filepath.Join(cwd, "templates/sidebar.html"),
- filepath.Join(cwd, "templates/content.html"),
- filepath.Join(cwd, "templates/headercommon.html"),
- filepath.Join(cwd, "templates/footercommon.html"),
- ))
-
- // The git command returns output of the format:
- //
- // f672cead70404080a991ebfb86c38316a4589b23 2014-04-27 19:21:51 +0000
- //
- logOutput, err := doCmd(`git log --format=%H%x20%ai HEAD^..HEAD`)
- if err != nil {
- panic(err)
- }
- logInfo := strings.Split(logOutput, " ")
- gitHash = logInfo[0]
- gitInfo = logInfo[1] + " " + logInfo[2] + " " + logInfo[0][0:6]
-
- // Connect to MySQL server. First, get the password from the metadata server.
- // See https://developers.google.com/compute/docs/metadata#custom.
- req, err := http.NewRequest("GET", "http://metadata/computeMetadata/v1/instance/attributes/password", nil)
- if err != nil {
- panic(err)
- }
- client := http.Client{}
- req.Header.Add("X-Google-Metadata-Request", "True")
- if resp, err := client.Do(req); err == nil {
- password, err := ioutil.ReadAll(resp.Body)
- if err != nil {
- glog.Errorf("Failed to read password from metadata server: %q\n", err)
- panic(err)
- }
- // The IP address of the database is found here:
- // https://console.developers.google.com/project/31977622648/sql/instances/webtry/overview
- // And 3306 is the default port for MySQL.
- db, err = sql.Open("mysql", fmt.Sprintf("webtry:%s@tcp(173.194.83.52:3306)/webtry?parseTime=true", password))
- if err != nil {
- glog.Errorf("ERROR: Failed to open connection to SQL server: %q\n", err)
- panic(err)
- }
- } else {
- glog.Infof("Failed to find metadata, unable to connect to MySQL server (Expected when running locally): %q\n", err)
- // Fallback to sqlite for local use.
- db, err = sql.Open("sqlite3", "./webtry.db")
- if err != nil {
- glog.Errorf("Failed to open: %q\n", err)
- panic(err)
- }
- sql := `CREATE TABLE IF NOT EXISTS source_images (
- id INTEGER PRIMARY KEY NOT NULL,
- image MEDIUMBLOB DEFAULT '' NOT NULL, -- formatted as a PNG.
- width INTEGER DEFAULT 0 NOT NULL,
- height INTEGER DEFAULT 0 NOT NULL,
- create_ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
- hidden INTEGER DEFAULT 0 NOT NULL
- )`
- _, err = db.Exec(sql)
- if err != nil {
- glog.Errorf("Creating source_images table failed: %s", err)
- }
-
- sql = `CREATE TABLE IF NOT EXISTS webtry (
- code TEXT DEFAULT '' NOT NULL,
- create_ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
- hash CHAR(64) DEFAULT '' NOT NULL,
- width INTEGER DEFAULT 256 NOT NULL,
- height INTEGER DEFAULT 256 NOT NULL,
- source_image_id INTEGER DEFAULT 0 NOT NULL,
-
- PRIMARY KEY(hash)
- )`
- _, err = db.Exec(sql)
- if err != nil {
- glog.Errorf("Creating webtry table failed: %s", err)
- }
-
- sql = `CREATE TABLE IF NOT EXISTS workspace (
- name CHAR(64) DEFAULT '' NOT NULL,
- create_ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
- PRIMARY KEY(name)
- )`
- _, err = db.Exec(sql)
- if err != nil {
- glog.Errorf("Creating workspace table failed: %s", err)
- }
-
- sql = `CREATE TABLE IF NOT EXISTS workspacetry (
- name CHAR(64) DEFAULT '' NOT NULL,
- create_ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
- hash CHAR(64) DEFAULT '' NOT NULL,
- width INTEGER DEFAULT 256 NOT NULL,
- height INTEGER DEFAULT 256 NOT NULL,
- hidden INTEGER DEFAULT 0 NOT NULL,
- source_image_id INTEGER DEFAULT 0 NOT NULL,
-
- FOREIGN KEY (name) REFERENCES workspace(name)
- )`
- _, err = db.Exec(sql)
- if err != nil {
- glog.Errorf("Creating workspacetry table failed: %s", err)
- }
- }
-
- // Ping the database to keep the connection fresh.
- go func() {
- c := time.Tick(1 * time.Minute)
- for _ = range c {
- if err := db.Ping(); err != nil {
- glog.Errorf("Database failed to respond: %q\n", err)
- }
- }
- }()
-
- metrics.RegisterRuntimeMemStats(metrics.DefaultRegistry)
- go metrics.CaptureRuntimeMemStats(metrics.DefaultRegistry, 1*time.Minute)
-
- // Start reporting metrics.
- // TODO(jcgregorio) We need a centrialized config server for storing things
- // like the IP address of the Graphite monitor.
- addr, _ := net.ResolveTCPAddr("tcp", "skia-monitoring-b:2003")
- go metrics.Graphite(metrics.DefaultRegistry, 1*time.Minute, "webtry", addr)
-
- writeOutAllSourceImages()
-}
-
-func writeOutAllSourceImages() {
- // Pull all the source images from the db and write them out to inout.
- rows, err := db.Query("SELECT id, image, create_ts FROM source_images ORDER BY create_ts DESC")
- if err != nil {
- glog.Errorf("Failed to open connection to SQL server: %q\n", err)
- panic(err)
- }
- defer rows.Close()
- for rows.Next() {
- var id int
- var image []byte
- var create_ts time.Time
- if err := rows.Scan(&id, &image, &create_ts); err != nil {
- glog.Errorf("failed to fetch from database: %q", err)
- continue
- }
- filename := fmt.Sprintf("../../../inout/image-%d.png", id)
- if _, err := os.Stat(filename); os.IsExist(err) {
- glog.Infof("Skipping write since file exists: %q", filename)
- continue
- }
- if err := ioutil.WriteFile(filename, image, 0666); err != nil {
- glog.Errorf("failed to write image file: %q", err)
- }
- }
-}
-
-// Titlebar is used in titlebar template expansion.
-type Titlebar struct {
- GitHash string
- GitInfo string
-}
-
-// userCode is used in template expansion.
-type userCode struct {
- Code string
- Hash string
- Width int
- Height int
- Source int
- Titlebar Titlebar
-}
-
-// writeTemplate creates a given output file and writes the template
-// result there.
-func writeTemplate(filename string, t *template.Template, context interface{}) error {
- f, err := os.Create(filename)
- if err != nil {
- return err
- }
- defer f.Close()
- return t.Execute(f, context)
-}
-
-// expandToFile expands the template and writes the result to the file.
-func expandToFile(filename string, code string, t *template.Template) error {
- return writeTemplate(filename, t, userCode{
- Code: code,
- Titlebar: Titlebar{GitHash: gitHash, GitInfo: gitInfo},
- })
-}
-
-// expandCode expands the template into a file and calculates the MD5 hash.
-// We include the width and height here so that a single hash can capture
-// both the code and the supplied width/height parameters.
-func expandCode(code string, source int, width, height int) (string, error) {
- // in order to support fonts in the chroot jail, we need to make sure
- // we're using portable typefaces.
- // TODO(humper): Make this more robust, supporting things like setTypeface
-
- inputCodeLines := strings.Split(code, "\n")
- outputCodeLines := []string{
- "DECLARE_bool(portableFonts);",
- fmt.Sprintf("// WxH: %d, %d", width, height),
- }
- for _, line := range inputCodeLines {
- outputCodeLines = append(outputCodeLines, line)
- if strings.HasPrefix(strings.TrimSpace(line), "SkPaint p") {
- outputCodeLines = append(outputCodeLines, "FLAGS_portableFonts = true;")
- outputCodeLines = append(outputCodeLines, "sk_tool_utils::set_portable_typeface(&p, \"Helvetica\", SkTypeface::kNormal);")
- }
- }
-
- fontFriendlyCode := strings.Join(outputCodeLines, "\n")
-
- h := md5.New()
- h.Write([]byte(fontFriendlyCode))
- binary.Write(h, binary.LittleEndian, int64(source))
- hash := fmt.Sprintf("%x", h.Sum(nil))
- // At this point we are running in skia/experimental/webtry, making cache a
- // peer directory to skia.
- // TODO(jcgregorio) Make all relative directories into flags.
- err := expandToFile(fmt.Sprintf("../../../cache/src/%s.cpp", hash), fontFriendlyCode, codeTemplate)
- return hash, err
-}
-
-// expandGyp produces the GYP file needed to build the code
-func expandGyp(hash string) error {
- return writeTemplate(fmt.Sprintf("../../../cache/%s.gyp", hash), gypTemplate, struct{ Hash string }{hash})
-}
-
-// response is serialized to JSON as a response to POSTs.
-type response struct {
- Message string `json:"message"`
- CompileErrors []compileError `json:"compileErrors"`
- RasterImg string `json:"rasterImg"`
- GPUImg string `json:"gpuImg"`
- Hash string `json:"hash"`
-}
-
-// doCmd executes the given command line string; the command being
-// run is expected to not care what its current working directory is.
-// Returns the stdout and stderr.
-func doCmd(commandLine string) (string, error) {
- glog.Infof("Command: %q\n", commandLine)
- programAndArgs := strings.SplitN(commandLine, " ", 2)
- program := programAndArgs[0]
- args := []string{}
- if len(programAndArgs) > 1 {
- args = strings.Split(programAndArgs[1], " ")
- }
- cmd := exec.Command(program, args...)
- message, err := cmd.CombinedOutput()
- glog.Infof("StdOut + StdErr: %s\n", string(message))
- if err != nil {
- glog.Errorf("Exit status: %s\n", err)
- return string(message), fmt.Errorf("Failed to run command.")
- }
- return string(message), nil
-}
-
-// reportError formats an HTTP error response and also logs the detailed error message.
-func reportError(w http.ResponseWriter, r *http.Request, err error, message string) {
- glog.Errorf("%s\n%s", message, err)
- w.Header().Set("Content-Type", "text/plain")
- http.Error(w, message, 500)
-}
-
-// reportTryError formats an HTTP error response in JSON and also logs the detailed error message.
-func reportTryError(w http.ResponseWriter, r *http.Request, err error, message, hash string) {
- m := response{
- Message: message,
- Hash: hash,
- }
- glog.Errorf("%s\n%s", message, err)
- resp, err := json.Marshal(m)
-
- if err != nil {
- http.Error(w, "Failed to serialize a response", 500)
- return
- }
- w.Header().Set("Content-Type", "text/plain")
- w.Write(resp)
-}
-
-func reportCompileError(w http.ResponseWriter, r *http.Request, compileErrors []compileError, hash string) {
- m := response{
- CompileErrors: compileErrors,
- Hash: hash,
- }
-
- resp, err := json.Marshal(m)
-
- if err != nil {
- http.Error(w, "Failed to serialize a response", 500)
- return
- }
- w.Header().Set("Content-Type", "text/plain")
- w.Write(resp)
-}
-
-func writeToDatabase(hash string, code string, workspaceName string, source int, width, height int) {
- if db == nil {
- return
- }
- if _, err := db.Exec("INSERT INTO webtry (code, hash, width, height, source_image_id) VALUES(?, ?, ?, ?, ?)", code, hash, width, height, source); err != nil {
- glog.Errorf("Failed to insert code into database: %q\n", err)
- }
- if workspaceName != "" {
- if _, err := db.Exec("INSERT INTO workspacetry (name, hash, width, height, source_image_id) VALUES(?, ?, ?, ?, ?)", workspaceName, hash, width, height, source); err != nil {
- glog.Errorf("Failed to insert into workspacetry table: %q\n", err)
- }
- }
-}
-
-type Sources struct {
- Id int `json:"id"`
-}
-
-// sourcesHandler serves up the PNG of a specific try.
-func sourcesHandler(w http.ResponseWriter, r *http.Request) {
- glog.Infof("Sources Handler: %q\n", r.URL.Path)
- if r.Method == "GET" {
- rows, err := db.Query("SELECT id, create_ts FROM source_images WHERE hidden=0 ORDER BY create_ts DESC")
- if err != nil {
- http.Error(w, fmt.Sprintf("Failed to query sources: %s.", err), 500)
- }
- defer rows.Close()
- sources := make([]Sources, 0, 0)
- for rows.Next() {
- var id int
- var create_ts time.Time
- if err := rows.Scan(&id, &create_ts); err != nil {
- glog.Errorf("failed to fetch from database: %q", err)
- continue
- }
- sources = append(sources, Sources{Id: id})
- }
-
- resp, err := json.Marshal(sources)
- if err != nil {
- reportError(w, r, err, "Failed to serialize a response.")
- return
- }
- w.Header().Set("Content-Type", "application/json")
- w.Write(resp)
-
- } else if r.Method == "POST" {
- if err := r.ParseMultipartForm(1000000); err != nil {
- http.Error(w, fmt.Sprintf("Failed to load image: %s.", err), 500)
- return
- }
- if _, ok := r.MultipartForm.File["upload"]; !ok {
- http.Error(w, "Invalid upload.", 500)
- return
- }
- if len(r.MultipartForm.File["upload"]) != 1 {
- http.Error(w, "Wrong number of uploads.", 500)
- return
- }
- f, err := r.MultipartForm.File["upload"][0].Open()
- if err != nil {
- http.Error(w, fmt.Sprintf("Failed to load image: %s.", err), 500)
- return
- }
- defer f.Close()
- m, _, err := image.Decode(f)
- if err != nil {
- http.Error(w, fmt.Sprintf("Failed to decode image: %s.", err), 500)
- return
- }
- var b bytes.Buffer
- png.Encode(&b, m)
- bounds := m.Bounds()
- width := bounds.Max.Y - bounds.Min.Y
- height := bounds.Max.X - bounds.Min.X
- if _, err := db.Exec("INSERT INTO source_images (image, width, height) VALUES(?, ?, ?)", b.Bytes(), width, height); err != nil {
- glog.Errorf("Failed to insert sources into database: %q\n", err)
- http.Error(w, fmt.Sprintf("Failed to store image: %s.", err), 500)
- return
- }
- go writeOutAllSourceImages()
-
- // Now redirect back to where we came from.
- http.Redirect(w, r, r.Referer(), 302)
- } else {
- http.NotFound(w, r)
- return
- }
-}
-
-// imageHandler serves up the PNG of a specific try.
-func imageHandler(w http.ResponseWriter, r *http.Request) {
- glog.Infof("Image Handler: %q\n", r.URL.Path)
- if r.Method != "GET" {
- http.NotFound(w, r)
- return
- }
- match := imageLink.FindStringSubmatch(r.URL.Path)
- if len(match) != 2 {
- http.NotFound(w, r)
- return
- }
- filename := match[1]
- w.Header().Set("Content-Type", "image/png")
- http.ServeFile(w, r, fmt.Sprintf("../../../inout/%s", filename))
-}
-
-type Try struct {
- Hash string `json:"hash"`
- Source int
- CreateTS string `json:"create_ts"`
-}
-
-type Recent struct {
- Tries []Try
- Titlebar Titlebar
-}
-
-// recentHandler shows the last 20 tries.
-func recentHandler(w http.ResponseWriter, r *http.Request) {
- glog.Infof("Recent Handler: %q\n", r.URL.Path)
-
- rows, err := db.Query("SELECT create_ts, hash FROM webtry ORDER BY create_ts DESC LIMIT 20")
- if err != nil {
- http.NotFound(w, r)
- return
- }
- defer rows.Close()
- recent := []Try{}
- for rows.Next() {
- var hash string
- var create_ts time.Time
- if err := rows.Scan(&create_ts, &hash); err != nil {
- glog.Errorf("failed to fetch from database: %q", err)
- continue
- }
- recent = append(recent, Try{Hash: hash, CreateTS: create_ts.Format("2006-02-01")})
- }
- w.Header().Set("Content-Type", "text/html")
- if err := recentTemplate.Execute(w, Recent{Tries: recent, Titlebar: Titlebar{GitHash: gitHash, GitInfo: gitInfo}}); err != nil {
- glog.Errorf("Failed to expand template: %q\n", err)
- }
-}
-
-type Workspace struct {
- Name string
- Code string
- Hash string
- Width int
- Height int
- Source int
- Tries []Try
- Titlebar Titlebar
-}
-
-// newWorkspace generates a new random workspace name and stores it in the database.
-func newWorkspace() (string, error) {
- for i := 0; i < 10; i++ {
- adj := workspaceNameAdj[rand.Intn(len(workspaceNameAdj))]
- noun := workspaceNameNoun[rand.Intn(len(workspaceNameNoun))]
- suffix := rand.Intn(1000)
- name := fmt.Sprintf("%s-%s-%d", adj, noun, suffix)
- if _, err := db.Exec("INSERT INTO workspace (name) VALUES(?)", name); err == nil {
- return name, nil
- } else {
- glog.Errorf("Failed to insert workspace into database: %q\n", err)
- }
- }
- return "", fmt.Errorf("Failed to create a new workspace")
-}
-
-// getCode returns the code for a given hash, or the empty string if not found.
-func getCode(hash string) (string, int, int, int, error) {
- code := ""
- width := 0
- height := 0
- source := 0
- if err := db.QueryRow("SELECT code, width, height, source_image_id FROM webtry WHERE hash=?", hash).Scan(&code, &width, &height, &source); err != nil {
- glog.Errorf("Code for hash is missing: %q\n", err)
- return code, width, height, source, err
- }
- return code, width, height, source, nil
-}
-
-func workspaceHandler(w http.ResponseWriter, r *http.Request) {
- glog.Infof("Workspace Handler: %q\n", r.URL.Path)
- if r.Method == "GET" {
- tries := []Try{}
- match := workspaceLink.FindStringSubmatch(r.URL.Path)
- name := ""
- if len(match) == 2 {
- name = match[1]
- rows, err := db.Query("SELECT create_ts, hash, source_image_id FROM workspacetry WHERE name=? ORDER BY create_ts", name)
- if err != nil {
- reportError(w, r, err, "Failed to select.")
- return
- }
- defer rows.Close()
- for rows.Next() {
- var hash string
- var create_ts time.Time
- var source int
- if err := rows.Scan(&create_ts, &hash, &source); err != nil {
- glog.Errorf("failed to fetch from database: %q", err)
- continue
- }
- tries = append(tries, Try{Hash: hash, Source: source, CreateTS: create_ts.Format("2006-02-01")})
- }
- }
- var code string
- var hash string
- var width int
- var height int
- source := 0
- if len(tries) == 0 {
- code = DEFAULT_SAMPLE
- width = 256
- height = 256
- } else {
- hash = tries[len(tries)-1].Hash
- code, width, height, source, _ = getCode(hash)
- }
- w.Header().Set("Content-Type", "text/html")
- if err := workspaceTemplate.Execute(w, Workspace{Tries: tries, Code: code, Name: name, Hash: hash, Width: width, Height: height, Source: source, Titlebar: Titlebar{GitHash: gitHash, GitInfo: gitInfo}}); err != nil {
- glog.Errorf("Failed to expand template: %q\n", err)
- }
- } else if r.Method == "POST" {
- name, err := newWorkspace()
- if err != nil {
- http.Error(w, "Failed to create a new workspace.", 500)
- return
- }
- http.Redirect(w, r, "/w/"+name, 302)
- }
-}
-
-// hasPreProcessor returns true if any line in the code begins with a # char.
-func hasPreProcessor(code string) bool {
- lines := strings.Split(code, "\n")
- for _, s := range lines {
- if strings.HasPrefix(strings.TrimSpace(s), "#") {
- return true
- }
- }
- return false
-}
-
-type TryRequest struct {
- Code string `json:"code"`
- Width int `json:"width"`
- Height int `json:"height"`
- GPU bool `json:"gpu"`
- Raster bool `json:"raster"`
- PDF bool `json:"pdf"`
- Name string `json:"name"` // Optional name of the workspace the code is in.
- Source int `json:"source"` // ID of the source image, 0 if none.
-}
-
-// iframeHandler handles the GET and POST of the main page.
-func iframeHandler(w http.ResponseWriter, r *http.Request) {
- glog.Infof("IFrame Handler: %q\n", r.URL.Path)
- if r.Method != "GET" {
- http.NotFound(w, r)
- return
- }
- match := iframeLink.FindStringSubmatch(r.URL.Path)
- if len(match) != 2 {
- http.NotFound(w, r)
- return
- }
- hash := match[1]
- if db == nil {
- http.NotFound(w, r)
- return
- }
- var code string
- code, width, height, source, err := getCode(hash)
- if err != nil {
- http.NotFound(w, r)
- return
- }
- // Expand the template.
- w.Header().Set("Content-Type", "text/html")
- if err := iframeTemplate.Execute(w, userCode{Code: code, Width: width, Height: height, Hash: hash, Source: source}); err != nil {
- glog.Errorf("Failed to expand template: %q\n", err)
- }
-}
-
-type TryInfo struct {
- Hash string `json:"hash"`
- Code string `json:"code"`
- Width int `json:"width"`
- Height int `json:"height"`
- Source int `json:"source"`
-}
-
-// tryInfoHandler returns information about a specific try.
-func tryInfoHandler(w http.ResponseWriter, r *http.Request) {
- glog.Infof("Try Info Handler: %q\n", r.URL.Path)
- if r.Method != "GET" {
- http.NotFound(w, r)
- return
- }
- match := tryInfoLink.FindStringSubmatch(r.URL.Path)
- if len(match) != 2 {
- http.NotFound(w, r)
- return
- }
- hash := match[1]
- code, width, height, source, err := getCode(hash)
- if err != nil {
- http.NotFound(w, r)
- return
- }
- m := TryInfo{
- Hash: hash,
- Code: code,
- Width: width,
- Height: height,
- Source: source,
- }
- resp, err := json.Marshal(m)
- if err != nil {
- reportError(w, r, err, "Failed to serialize a response.")
- return
- }
- w.Header().Set("Content-Type", "application/json")
- w.Write(resp)
-}
-
-func cleanCompileOutput(s, hash string) string {
- old := "../../../cache/src/" + hash + ".cpp:"
- glog.Infof("replacing %q\n", old)
- return strings.Replace(s, old, "usercode.cpp:", -1)
-}
-
-type compileError struct {
- Line int `json:"line"`
- Column int `json:"column"`
- Error string `json:"error"`
-}
-
-// mainHandler handles the GET and POST of the main page.
-func mainHandler(w http.ResponseWriter, r *http.Request) {
- glog.Infof("Main Handler: %q\n", r.URL.Path)
- requestsCounter.Inc(1)
- if r.Method == "GET" {
- code := DEFAULT_SAMPLE
- source := 0
- width := 256
- height := 256
- match := directLink.FindStringSubmatch(r.URL.Path)
- var hash string
- if len(match) == 2 && r.URL.Path != "/" {
- hash = match[1]
- if db == nil {
- http.NotFound(w, r)
- return
- }
- // Update 'code' with the code found in the database.
- if err := db.QueryRow("SELECT code, width, height, source_image_id FROM webtry WHERE hash=?", hash).Scan(&code, &width, &height, &source); err != nil {
- http.NotFound(w, r)
- return
- }
- }
- // Expand the template.
- w.Header().Set("Content-Type", "text/html")
- if err := indexTemplate.Execute(w, userCode{Code: code, Hash: hash, Source: source, Width: width, Height: height, Titlebar: Titlebar{GitHash: gitHash, GitInfo: gitInfo}}); err != nil {
- glog.Errorf("Failed to expand template: %q\n", err)
- }
- } else if r.Method == "POST" {
- w.Header().Set("Content-Type", "application/json")
- buf := bytes.NewBuffer(make([]byte, 0, MAX_TRY_SIZE))
- n, err := buf.ReadFrom(r.Body)
- if err != nil {
- reportTryError(w, r, err, "Failed to read a request body.", "")
- return
- }
- if n == MAX_TRY_SIZE {
- err := fmt.Errorf("Code length equal to, or exceeded, %d", MAX_TRY_SIZE)
- reportTryError(w, r, err, "Code too large.", "")
- return
- }
- request := TryRequest{}
- if err := json.Unmarshal(buf.Bytes(), &request); err != nil {
- reportTryError(w, r, err, "Coulnd't decode JSON.", "")
- return
- }
- if !(request.GPU || request.Raster || request.PDF) {
- reportTryError(w, r, nil, "No run configuration supplied...", "")
- return
- }
- if hasPreProcessor(request.Code) {
- err := fmt.Errorf("Found preprocessor macro in code.")
- reportTryError(w, r, err, "Preprocessor macros aren't allowed.", "")
- return
- }
- hash, err := expandCode(LineNumbers(request.Code), request.Source, request.Width, request.Height)
- if err != nil {
- reportTryError(w, r, err, "Failed to write the code to compile.", hash)
- return
- }
- writeToDatabase(hash, request.Code, request.Name, request.Source, request.Width, request.Height)
- err = expandGyp(hash)
- if err != nil {
- reportTryError(w, r, err, "Failed to write the gyp file.", hash)
- return
- }
- cmd := fmt.Sprintf("scripts/fiddle_wrapper %s --width %d --height %d", hash, request.Width, request.Height)
- if request.Raster {
- cmd += " --raster"
- }
- if request.GPU {
- cmd += " --gpu"
- }
- if request.PDF {
- cmd += " --pdf"
- }
- if *useChroot {
- cmd = "schroot -c webtry --directory=/ -- /skia_build/skia/experimental/webtry/" + cmd
- }
- if request.Source > 0 {
- cmd += fmt.Sprintf(" --source image-%d.png", request.Source)
- }
-
- message, err := doCmd(cmd)
-
- outputLines := strings.Split(message, "\n")
- errorLines := []compileError{}
- for _, line := range outputLines {
- match := errorRE.FindStringSubmatch(line)
- if len(match) > 0 {
- lineNumber, parseError := strconv.Atoi(match[1])
- if parseError != nil {
- glog.Errorf("ERROR: Couldn't parse line number from %s\n", match[1])
- continue
- }
- columnNumber, parseError := strconv.Atoi(match[2])
- if parseError != nil {
- glog.Errorf("ERROR: Couldn't parse column number from %s\n", match[2])
- continue
- }
- errorLines = append(errorLines,
- compileError{
- Line: lineNumber,
- Column: columnNumber,
- Error: match[3],
- })
- }
- }
-
- if err != nil {
- if len(errorLines) > 0 {
- reportCompileError(w, r, errorLines, hash)
- } else {
- reportTryError(w, r, err, "Failed to run the code:\n"+message, hash)
- }
- return
- }
-
- m := response{
- Hash: hash,
- }
-
- if request.Raster {
- png, err := ioutil.ReadFile("../../../inout/" + hash + "_raster.png")
- if err != nil {
- reportTryError(w, r, err, "Failed to open the raster-generated PNG.", hash)
- return
- }
-
- m.RasterImg = base64.StdEncoding.EncodeToString([]byte(png))
- }
-
- if request.GPU {
- png, err := ioutil.ReadFile("../../../inout/" + hash + "_gpu.png")
- if err != nil {
- reportTryError(w, r, err, "Failed to open the GPU-generated PNG.", hash)
- return
- }
-
- m.GPUImg = base64.StdEncoding.EncodeToString([]byte(png))
- }
-
- resp, err := json.Marshal(m)
- if err != nil {
- reportTryError(w, r, err, "Failed to serialize a response.", hash)
- return
- }
- w.Header().Set("Content-Type", "application/json")
- w.Write(resp)
- }
-}
-
-func main() {
- flag.Parse()
- Init()
- http.HandleFunc("/i/", autogzip.HandleFunc(imageHandler))
- http.HandleFunc("/w/", autogzip.HandleFunc(workspaceHandler))
- http.HandleFunc("/recent/", autogzip.HandleFunc(recentHandler))
- http.HandleFunc("/iframe/", autogzip.HandleFunc(iframeHandler))
- http.HandleFunc("/json/", autogzip.HandleFunc(tryInfoHandler))
- http.HandleFunc("/sources/", autogzip.HandleFunc(sourcesHandler))
-
- // Resources are served directly
- // TODO add support for caching/etags/gzip
- http.Handle("/res/", autogzip.Handle(http.FileServer(http.Dir("./"))))
-
- // TODO Break out /c/ as it's own handler.
- http.HandleFunc("/", autogzip.HandleFunc(mainHandler))
- glog.Fatal(http.ListenAndServe(*port, nil))
-}
« no previous file with comments | « experimental/webtry/templates/workspace.html ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698