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

Side by Side Diff: experimental/webtry/webtry.go

Issue 294903017: Add the ability to select a source image to use in the code. (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: Created 6 years, 6 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 unified diff | Download patch
« no previous file with comments | « experimental/webtry/templates/template.cpp ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 package main 1 package main
2 2
3 import ( 3 import (
4 "bytes" 4 "bytes"
5 "crypto/md5" 5 "crypto/md5"
6 "database/sql" 6 "database/sql"
7 "encoding/base64" 7 "encoding/base64"
8 "encoding/binary"
8 "encoding/json" 9 "encoding/json"
9 "flag" 10 "flag"
10 "fmt" 11 "fmt"
11 htemplate "html/template" 12 htemplate "html/template"
13 "image"
14 _ "image/gif"
15 _ "image/jpeg"
16 "image/png"
12 "io/ioutil" 17 "io/ioutil"
13 "log" 18 "log"
14 "math/rand" 19 "math/rand"
15 "net/http" 20 "net/http"
16 "os" 21 "os"
17 "os/exec" 22 "os/exec"
18 "path/filepath" 23 "path/filepath"
19 "regexp" 24 "regexp"
20 "strings" 25 "strings"
21 "text/template" 26 "text/template"
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
63 // db is the database, nil if we don't have an SQL database to store dat a into. 68 // db is the database, nil if we don't have an SQL database to store dat a into.
64 db *sql.DB = nil 69 db *sql.DB = nil
65 70
66 // directLink is the regex that matches URLs paths that are direct links . 71 // directLink is the regex that matches URLs paths that are direct links .
67 directLink = regexp.MustCompile("^/c/([a-f0-9]+)$") 72 directLink = regexp.MustCompile("^/c/([a-f0-9]+)$")
68 73
69 // iframeLink is the regex that matches URLs paths that are links to ifr ames. 74 // iframeLink is the regex that matches URLs paths that are links to ifr ames.
70 iframeLink = regexp.MustCompile("^/iframe/([a-f0-9]+)$") 75 iframeLink = regexp.MustCompile("^/iframe/([a-f0-9]+)$")
71 76
72 // imageLink is the regex that matches URLs paths that are direct links to PNGs. 77 // imageLink is the regex that matches URLs paths that are direct links to PNGs.
73 » imageLink = regexp.MustCompile("^/i/([a-f0-9]+.png)$") 78 » imageLink = regexp.MustCompile("^/i/([a-z0-9-]+.png)$")
74 79
75 // tryInfoLink is the regex that matches URLs paths that are direct link s to data about a single try. 80 // tryInfoLink is the regex that matches URLs paths that are direct link s to data about a single try.
76 tryInfoLink = regexp.MustCompile("^/json/([a-f0-9]+)$") 81 tryInfoLink = regexp.MustCompile("^/json/([a-f0-9]+)$")
77 82
78 // workspaceLink is the regex that matches URLs paths for workspaces. 83 // workspaceLink is the regex that matches URLs paths for workspaces.
79 workspaceLink = regexp.MustCompile("^/w/([a-z0-9-]+)$") 84 workspaceLink = regexp.MustCompile("^/w/([a-z0-9-]+)$")
80 85
81 // workspaceNameAdj is a list of adjectives for building workspace names . 86 // workspaceNameAdj is a list of adjectives for building workspace names .
82 workspaceNameAdj = []string{ 87 workspaceNameAdj = []string{
83 "autumn", "hidden", "bitter", "misty", "silent", "empty", "dry", "dark", 88 "autumn", "hidden", "bitter", "misty", "silent", "empty", "dry", "dark",
(...skipping 130 matching lines...) Expand 10 before | Expand all | Expand 10 after
214 panic(err) 219 panic(err)
215 } 220 }
216 } else { 221 } else {
217 log.Printf("INFO: Failed to find metadata, unable to connect to MySQL server (Expected when running locally): %q\n", err) 222 log.Printf("INFO: Failed to find metadata, unable to connect to MySQL server (Expected when running locally): %q\n", err)
218 // Fallback to sqlite for local use. 223 // Fallback to sqlite for local use.
219 db, err = sql.Open("sqlite3", "./webtry.db") 224 db, err = sql.Open("sqlite3", "./webtry.db")
220 if err != nil { 225 if err != nil {
221 log.Printf("ERROR: Failed to open: %q\n", err) 226 log.Printf("ERROR: Failed to open: %q\n", err)
222 panic(err) 227 panic(err)
223 } 228 }
224 » » sql := `CREATE TABLE webtry ( 229 » » sql := `CREATE TABLE source_images (
225 code TEXT DEFAULT '' NOT NULL, 230 id INTEGER PRIMARY KEY NOT NULL,
226 create_ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, 231 image MEDIUMBLOB DEFAULT '' NOT NULL, -- forma tted as a PNG.
227 hash CHAR(64) DEFAULT '' NOT NULL, 232 width INTEGER DEFAULT 0 NOT NULL,
233 height INTEGER DEFAULT 0 NOT NULL,
234 create_ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
235 hidden INTEGER DEFAULT 0 NOT NULL
236 )`
237 » » _, err = db.Exec(sql)
238 » » log.Printf("Info: status creating sqlite table for sources: %q\n ", err)
239
240 » » sql = `CREATE TABLE webtry (
241 code TEXT DEFAULT '' NOT NULL,
242 create_ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
243 hash CHAR(64) DEFAULT '' NOT NULL,
244 source_image_id INTEGER DEFAULT 0 NOT NULL,
245
228 PRIMARY KEY(hash) 246 PRIMARY KEY(hash)
229 )` 247 )`
230 _, err = db.Exec(sql) 248 _, err = db.Exec(sql)
231 log.Printf("Info: status creating sqlite table for webtry: %q\n" , err) 249 log.Printf("Info: status creating sqlite table for webtry: %q\n" , err)
250
232 sql = `CREATE TABLE workspace ( 251 sql = `CREATE TABLE workspace (
233 name CHAR(64) DEFAULT '' NOT NULL, 252 name CHAR(64) DEFAULT '' NOT NULL,
234 create_ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, 253 create_ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
235 PRIMARY KEY(name) 254 PRIMARY KEY(name)
236 )` 255 )`
237 _, err = db.Exec(sql) 256 _, err = db.Exec(sql)
238 log.Printf("Info: status creating sqlite table for workspace: %q \n", err) 257 log.Printf("Info: status creating sqlite table for workspace: %q \n", err)
258
239 sql = `CREATE TABLE workspacetry ( 259 sql = `CREATE TABLE workspacetry (
240 name CHAR(64) DEFAULT '' NOT NULL, 260 name CHAR(64) DEFAULT '' NOT NULL,
241 create_ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, 261 create_ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
242 hash CHAR(64) DEFAULT '' NOT NULL, 262 hash CHAR(64) DEFAULT '' NOT NULL,
243 hidden INTEGER DEFAULT 0 NOT NULL, 263 hidden INTEGER DEFAULT 0 NOT NULL,
264 source_image_id INTEGER DEFAULT 0 NOT NULL,
244 265
245 FOREIGN KEY (name) REFERENCES workspace(name) 266 FOREIGN KEY (name) REFERENCES workspace(name)
246 )` 267 )`
247 _, err = db.Exec(sql) 268 _, err = db.Exec(sql)
248 log.Printf("Info: status creating sqlite table for workspace try : %q\n", err) 269 log.Printf("Info: status creating sqlite table for workspace try : %q\n", err)
249 } 270 }
250 271
251 // Ping the database to keep the connection fresh. 272 // Ping the database to keep the connection fresh.
252 go func() { 273 go func() {
253 c := time.Tick(1 * time.Minute) 274 c := time.Tick(1 * time.Minute)
254 for _ = range c { 275 for _ = range c {
255 if err := db.Ping(); err != nil { 276 if err := db.Ping(); err != nil {
256 log.Printf("ERROR: Database failed to respond: % q\n", err) 277 log.Printf("ERROR: Database failed to respond: % q\n", err)
257 } 278 }
258 } 279 }
259 }() 280 }()
260 281
282 writeOutAllSourceImages()
283 }
284
285 func writeOutAllSourceImages() {
286 // Pull all the source images from the db and write them out to inout.
287 rows, err := db.Query("SELECT id, image, create_ts FROM source_images OR DER BY create_ts DESC")
288
289 if err != nil {
290 log.Printf("ERROR: Failed to open connection to SQL server: %q\n ", err)
291 panic(err)
292 }
293 for rows.Next() {
294 var id int
295 var image []byte
296 var create_ts time.Time
297 if err := rows.Scan(&id, &image, &create_ts); err != nil {
298 log.Printf("Error: failed to fetch from database: %q", e rr)
299 continue
300 }
301 filename := fmt.Sprintf("../../../inout/image-%d.png", id)
302 if _, err := os.Stat(filename); os.IsExist(err) {
303 log.Printf("Skipping write since file exists: %q", filen ame)
304 continue
305 }
306 if err := ioutil.WriteFile(filename, image, 0666); err != nil {
307 log.Printf("Error: failed to write image file: %q", err)
308 }
309 }
261 } 310 }
262 311
263 // Titlebar is used in titlebar template expansion. 312 // Titlebar is used in titlebar template expansion.
264 type Titlebar struct { 313 type Titlebar struct {
265 GitHash string 314 GitHash string
266 GitInfo string 315 GitInfo string
267 } 316 }
268 317
269 // userCode is used in template expansion. 318 // userCode is used in template expansion.
270 type userCode struct { 319 type userCode struct {
271 Code string 320 Code string
272 Hash string 321 Hash string
322 Source int
273 Titlebar Titlebar 323 Titlebar Titlebar
274 } 324 }
275 325
276 // expandToFile expands the template and writes the result to the file. 326 // expandToFile expands the template and writes the result to the file.
277 func expandToFile(filename string, code string, t *template.Template) error { 327 func expandToFile(filename string, code string, t *template.Template) error {
278 f, err := os.Create(filename) 328 f, err := os.Create(filename)
279 if err != nil { 329 if err != nil {
280 return err 330 return err
281 } 331 }
282 defer f.Close() 332 defer f.Close()
283 return t.Execute(f, userCode{Code: code, Titlebar: Titlebar{GitHash: git Hash, GitInfo: gitInfo}}) 333 return t.Execute(f, userCode{Code: code, Titlebar: Titlebar{GitHash: git Hash, GitInfo: gitInfo}})
284 } 334 }
285 335
286 // expandCode expands the template into a file and calculate the MD5 hash. 336 // expandCode expands the template into a file and calculates the MD5 hash.
287 func expandCode(code string) (string, error) { 337 func expandCode(code string, source int) (string, error) {
288 h := md5.New() 338 h := md5.New()
289 h.Write([]byte(code)) 339 h.Write([]byte(code))
340 binary.Write(h, binary.LittleEndian, int64(source))
290 hash := fmt.Sprintf("%x", h.Sum(nil)) 341 hash := fmt.Sprintf("%x", h.Sum(nil))
291 // At this point we are running in skia/experimental/webtry, making cach e a 342 // At this point we are running in skia/experimental/webtry, making cach e a
292 // peer directory to skia. 343 // peer directory to skia.
293 // TODO(jcgregorio) Make all relative directories into flags. 344 // TODO(jcgregorio) Make all relative directories into flags.
294 err := expandToFile(fmt.Sprintf("../../../cache/%s.cpp", hash), code, co deTemplate) 345 err := expandToFile(fmt.Sprintf("../../../cache/%s.cpp", hash), code, co deTemplate)
295 return hash, err 346 return hash, err
296 } 347 }
297 348
298 // response is serialized to JSON as a response to POSTs. 349 // response is serialized to JSON as a response to POSTs.
299 type response struct { 350 type response struct {
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
353 log.Printf("Error: %s\n%s", message, err.Error()) 404 log.Printf("Error: %s\n%s", message, err.Error())
354 resp, err := json.Marshal(m) 405 resp, err := json.Marshal(m)
355 if err != nil { 406 if err != nil {
356 http.Error(w, "Failed to serialize a response", 500) 407 http.Error(w, "Failed to serialize a response", 500)
357 return 408 return
358 } 409 }
359 w.Header().Set("Content-Type", "text/plain") 410 w.Header().Set("Content-Type", "text/plain")
360 w.Write(resp) 411 w.Write(resp)
361 } 412 }
362 413
363 func writeToDatabase(hash string, code string, workspaceName string) { 414 func writeToDatabase(hash string, code string, workspaceName string, source int) {
364 if db == nil { 415 if db == nil {
365 return 416 return
366 } 417 }
367 » if _, err := db.Exec("INSERT INTO webtry (code, hash) VALUES(?, ?)", cod e, hash); err != nil { 418 » if _, err := db.Exec("INSERT INTO webtry (code, hash, source_image_id) V ALUES(?, ?, ?)", code, hash, source); err != nil {
368 log.Printf("ERROR: Failed to insert code into database: %q\n", e rr) 419 log.Printf("ERROR: Failed to insert code into database: %q\n", e rr)
369 } 420 }
370 if workspaceName != "" { 421 if workspaceName != "" {
371 » » if _, err := db.Exec("INSERT INTO workspacetry (name, hash) VALU ES(?, ?)", workspaceName, hash); err != nil { 422 » » if _, err := db.Exec("INSERT INTO workspacetry (name, hash, sour ce_image_id) VALUES(?, ?, ?)", workspaceName, hash, source); err != nil {
372 log.Printf("ERROR: Failed to insert into workspacetry ta ble: %q\n", err) 423 log.Printf("ERROR: Failed to insert into workspacetry ta ble: %q\n", err)
373 } 424 }
374 } 425 }
375 } 426 }
376 427
428 type Sources struct {
429 Id int `json:"id"`
430 }
431
432 // sourcesHandler serves up the PNG of a specific try.
433 func sourcesHandler(w http.ResponseWriter, r *http.Request) {
434 log.Printf("Sources Handler: %q\n", r.URL.Path)
435 if r.Method == "GET" {
436 rows, err := db.Query("SELECT id, create_ts FROM source_images W HERE hidden=0 ORDER BY create_ts DESC")
437
438 if err != nil {
439 http.Error(w, fmt.Sprintf("Failed to query sources: %s." , err), 500)
440 }
441 sources := make([]Sources, 0, 0)
442 for rows.Next() {
443 var id int
444 var create_ts time.Time
445 if err := rows.Scan(&id, &create_ts); err != nil {
446 log.Printf("Error: failed to fetch from database : %q", err)
447 continue
448 }
449 sources = append(sources, Sources{Id: id})
450 }
451
452 resp, err := json.Marshal(sources)
453 if err != nil {
454 reportError(w, r, err, "Failed to serialize a response." )
455 return
456 }
457 w.Header().Set("Content-Type", "application/json")
458 w.Write(resp)
459
460 } else if r.Method == "POST" {
461 if err := r.ParseMultipartForm(1000000); err != nil {
462 http.Error(w, fmt.Sprintf("Failed to load image: %s.", e rr), 500)
463 return
464 }
465 if _, ok := r.MultipartForm.File["upload"]; !ok {
466 http.Error(w, "Invalid upload.", 500)
467 return
468 }
469 if len(r.MultipartForm.File["upload"]) != 1 {
470 http.Error(w, "Wrong number of uploads.", 500)
471 return
472 }
473 f, err := r.MultipartForm.File["upload"][0].Open()
474 if err != nil {
475 http.Error(w, fmt.Sprintf("Failed to load image: %s.", e rr), 500)
476 return
477 }
478 defer f.Close()
479 m, _, err := image.Decode(f)
480 if err != nil {
481 http.Error(w, fmt.Sprintf("Failed to decode image: %s.", err), 500)
482 return
483 }
484 var b bytes.Buffer
485 png.Encode(&b, m)
486 bounds := m.Bounds()
487 width := bounds.Max.Y - bounds.Min.Y
488 height := bounds.Max.X - bounds.Min.X
489 if _, err := db.Exec("INSERT INTO source_images (image, width, h eight) VALUES(?, ?, ?)", b.Bytes(), width, height); err != nil {
490 log.Printf("ERROR: Failed to insert sources into databas e: %q\n", err)
491 http.Error(w, fmt.Sprintf("Failed to store image: %s.", err), 500)
492 return
493 }
494 go writeOutAllSourceImages()
495
496 // Now redirect back to where we came from.
497 http.Redirect(w, r, r.Referer(), 302)
498 } else {
499 http.NotFound(w, r)
500 return
501 }
502 }
503
377 // imageHandler serves up the PNG of a specific try. 504 // imageHandler serves up the PNG of a specific try.
378 func imageHandler(w http.ResponseWriter, r *http.Request) { 505 func imageHandler(w http.ResponseWriter, r *http.Request) {
379 log.Printf("Image Handler: %q\n", r.URL.Path) 506 log.Printf("Image Handler: %q\n", r.URL.Path)
380 if r.Method != "GET" { 507 if r.Method != "GET" {
381 http.NotFound(w, r) 508 http.NotFound(w, r)
382 return 509 return
383 } 510 }
384 match := imageLink.FindStringSubmatch(r.URL.Path) 511 match := imageLink.FindStringSubmatch(r.URL.Path)
385 if len(match) != 2 { 512 if len(match) != 2 {
386 http.NotFound(w, r) 513 http.NotFound(w, r)
387 return 514 return
388 } 515 }
389 filename := match[1] 516 filename := match[1]
390 w.Header().Set("Content-Type", "image/png") 517 w.Header().Set("Content-Type", "image/png")
391 http.ServeFile(w, r, fmt.Sprintf("../../../inout/%s", filename)) 518 http.ServeFile(w, r, fmt.Sprintf("../../../inout/%s", filename))
392 } 519 }
393 520
394 type Try struct { 521 type Try struct {
395 Hash string `json:"hash"` 522 Hash string `json:"hash"`
523 Source int
396 CreateTS string `json:"create_ts"` 524 CreateTS string `json:"create_ts"`
397 } 525 }
398 526
399 type Recent struct { 527 type Recent struct {
400 Tries []Try 528 Tries []Try
401 Titlebar Titlebar 529 Titlebar Titlebar
402 } 530 }
403 531
404 // recentHandler shows the last 20 tries. 532 // recentHandler shows the last 20 tries.
405 func recentHandler(w http.ResponseWriter, r *http.Request) { 533 func recentHandler(w http.ResponseWriter, r *http.Request) {
(...skipping 18 matching lines...) Expand all
424 w.Header().Set("Content-Type", "text/html") 552 w.Header().Set("Content-Type", "text/html")
425 if err := recentTemplate.Execute(w, Recent{Tries: recent, Titlebar: Titl ebar{GitHash: gitHash, GitInfo: gitInfo}}); err != nil { 553 if err := recentTemplate.Execute(w, Recent{Tries: recent, Titlebar: Titl ebar{GitHash: gitHash, GitInfo: gitInfo}}); err != nil {
426 log.Printf("ERROR: Failed to expand template: %q\n", err) 554 log.Printf("ERROR: Failed to expand template: %q\n", err)
427 } 555 }
428 } 556 }
429 557
430 type Workspace struct { 558 type Workspace struct {
431 Name string 559 Name string
432 Code string 560 Code string
433 Hash string 561 Hash string
562 Source int
434 Tries []Try 563 Tries []Try
435 Titlebar Titlebar 564 Titlebar Titlebar
436 } 565 }
437 566
438 // newWorkspace generates a new random workspace name and stores it in the datab ase. 567 // newWorkspace generates a new random workspace name and stores it in the datab ase.
439 func newWorkspace() (string, error) { 568 func newWorkspace() (string, error) {
440 for i := 0; i < 10; i++ { 569 for i := 0; i < 10; i++ {
441 adj := workspaceNameAdj[rand.Intn(len(workspaceNameAdj))] 570 adj := workspaceNameAdj[rand.Intn(len(workspaceNameAdj))]
442 noun := workspaceNameNoun[rand.Intn(len(workspaceNameNoun))] 571 noun := workspaceNameNoun[rand.Intn(len(workspaceNameNoun))]
443 suffix := rand.Intn(1000) 572 suffix := rand.Intn(1000)
444 name := fmt.Sprintf("%s-%s-%d", adj, noun, suffix) 573 name := fmt.Sprintf("%s-%s-%d", adj, noun, suffix)
445 if _, err := db.Exec("INSERT INTO workspace (name) VALUES(?)", n ame); err == nil { 574 if _, err := db.Exec("INSERT INTO workspace (name) VALUES(?)", n ame); err == nil {
446 return name, nil 575 return name, nil
447 } else { 576 } else {
448 log.Printf("ERROR: Failed to insert workspace into datab ase: %q\n", err) 577 log.Printf("ERROR: Failed to insert workspace into datab ase: %q\n", err)
449 } 578 }
450 } 579 }
451 return "", fmt.Errorf("Failed to create a new workspace") 580 return "", fmt.Errorf("Failed to create a new workspace")
452 } 581 }
453 582
454 // getCode returns the code for a given hash, or the empty string if not found. 583 // getCode returns the code for a given hash, or the empty string if not found.
455 func getCode(hash string) (string, error) { 584 func getCode(hash string) (string, int, error) {
456 code := "" 585 code := ""
457 » if err := db.QueryRow("SELECT code FROM webtry WHERE hash=?", hash).Scan (&code); err != nil { 586 » source := 0
587 » if err := db.QueryRow("SELECT code, source_image_id FROM webtry WHERE ha sh=?", hash).Scan(&code, &source); err != nil {
458 log.Printf("ERROR: Code for hash is missing: %q\n", err) 588 log.Printf("ERROR: Code for hash is missing: %q\n", err)
459 » » return code, err 589 » » return code, source, err
460 } 590 }
461 » return code, nil 591 » return code, source, nil
462 } 592 }
463 593
464 func workspaceHandler(w http.ResponseWriter, r *http.Request) { 594 func workspaceHandler(w http.ResponseWriter, r *http.Request) {
465 log.Printf("Workspace Handler: %q\n", r.URL.Path) 595 log.Printf("Workspace Handler: %q\n", r.URL.Path)
466 if r.Method == "GET" { 596 if r.Method == "GET" {
467 tries := []Try{} 597 tries := []Try{}
468 match := workspaceLink.FindStringSubmatch(r.URL.Path) 598 match := workspaceLink.FindStringSubmatch(r.URL.Path)
469 name := "" 599 name := ""
470 if len(match) == 2 { 600 if len(match) == 2 {
471 name = match[1] 601 name = match[1]
472 » » » rows, err := db.Query("SELECT create_ts, hash FROM works pacetry WHERE name=? ORDER BY create_ts", name) 602 » » » rows, err := db.Query("SELECT create_ts, hash, source_im age_id FROM workspacetry WHERE name=? ORDER BY create_ts", name)
473 if err != nil { 603 if err != nil {
474 reportError(w, r, err, "Failed to select.") 604 reportError(w, r, err, "Failed to select.")
475 return 605 return
476 } 606 }
477 for rows.Next() { 607 for rows.Next() {
478 var hash string 608 var hash string
479 var create_ts time.Time 609 var create_ts time.Time
480 » » » » if err := rows.Scan(&create_ts, &hash); err != n il { 610 » » » » var source int
611 » » » » if err := rows.Scan(&create_ts, &hash, &source); err != nil {
481 log.Printf("Error: failed to fetch from database: %q", err) 612 log.Printf("Error: failed to fetch from database: %q", err)
482 continue 613 continue
483 } 614 }
484 » » » » tries = append(tries, Try{Hash: hash, CreateTS: create_ts.Format("2006-02-01")}) 615 » » » » tries = append(tries, Try{Hash: hash, Source: so urce, CreateTS: create_ts.Format("2006-02-01")})
485 } 616 }
486 } 617 }
487 var code string 618 var code string
488 var hash string 619 var hash string
620 source := 0
489 if len(tries) == 0 { 621 if len(tries) == 0 {
490 code = DEFAULT_SAMPLE 622 code = DEFAULT_SAMPLE
491 } else { 623 } else {
492 hash = tries[len(tries)-1].Hash 624 hash = tries[len(tries)-1].Hash
493 » » » code, _ = getCode(hash) 625 » » » code, source, _ = getCode(hash)
494 } 626 }
495 w.Header().Set("Content-Type", "text/html") 627 w.Header().Set("Content-Type", "text/html")
496 » » if err := workspaceTemplate.Execute(w, Workspace{Tries: tries, C ode: code, Name: name, Hash: hash, Titlebar: Titlebar{GitHash: gitHash, GitInfo: gitInfo}}); err != nil { 628 » » if err := workspaceTemplate.Execute(w, Workspace{Tries: tries, C ode: code, Name: name, Hash: hash, Source: source, Titlebar: Titlebar{GitHash: g itHash, GitInfo: gitInfo}}); err != nil {
497 log.Printf("ERROR: Failed to expand template: %q\n", err ) 629 log.Printf("ERROR: Failed to expand template: %q\n", err )
498 } 630 }
499 } else if r.Method == "POST" { 631 } else if r.Method == "POST" {
500 name, err := newWorkspace() 632 name, err := newWorkspace()
501 if err != nil { 633 if err != nil {
502 http.Error(w, "Failed to create a new workspace.", 500) 634 http.Error(w, "Failed to create a new workspace.", 500)
503 return 635 return
504 } 636 }
505 http.Redirect(w, r, "/w/"+name, 302) 637 http.Redirect(w, r, "/w/"+name, 302)
506 } 638 }
507 } 639 }
508 640
509 // hasPreProcessor returns true if any line in the code begins with a # char. 641 // hasPreProcessor returns true if any line in the code begins with a # char.
510 func hasPreProcessor(code string) bool { 642 func hasPreProcessor(code string) bool {
511 lines := strings.Split(code, "\n") 643 lines := strings.Split(code, "\n")
512 for _, s := range lines { 644 for _, s := range lines {
513 if strings.HasPrefix(strings.TrimSpace(s), "#") { 645 if strings.HasPrefix(strings.TrimSpace(s), "#") {
514 return true 646 return true
515 } 647 }
516 } 648 }
517 return false 649 return false
518 } 650 }
519 651
520 type TryRequest struct { 652 type TryRequest struct {
521 » Code string `json:"code"` 653 » Code string `json:"code"`
522 » Name string `json:"name"` // Optional name of the workspace the code is in. 654 » Name string `json:"name"` // Optional name of the workspace the code is in.
655 » Source int `json:"source"` // ID of the source image, 0 if none.
523 } 656 }
524 657
525 // iframeHandler handles the GET and POST of the main page. 658 // iframeHandler handles the GET and POST of the main page.
526 func iframeHandler(w http.ResponseWriter, r *http.Request) { 659 func iframeHandler(w http.ResponseWriter, r *http.Request) {
527 log.Printf("IFrame Handler: %q\n", r.URL.Path) 660 log.Printf("IFrame Handler: %q\n", r.URL.Path)
528 if r.Method != "GET" { 661 if r.Method != "GET" {
529 http.NotFound(w, r) 662 http.NotFound(w, r)
530 return 663 return
531 } 664 }
532 match := iframeLink.FindStringSubmatch(r.URL.Path) 665 match := iframeLink.FindStringSubmatch(r.URL.Path)
533 if len(match) != 2 { 666 if len(match) != 2 {
534 http.NotFound(w, r) 667 http.NotFound(w, r)
535 return 668 return
536 } 669 }
537 hash := match[1] 670 hash := match[1]
538 if db == nil { 671 if db == nil {
539 http.NotFound(w, r) 672 http.NotFound(w, r)
540 return 673 return
541 } 674 }
542 var code string 675 var code string
543 » code, err := getCode(hash) 676 » code, source, err := getCode(hash)
544 if err != nil { 677 if err != nil {
545 http.NotFound(w, r) 678 http.NotFound(w, r)
546 return 679 return
547 } 680 }
548 // Expand the template. 681 // Expand the template.
549 w.Header().Set("Content-Type", "text/html") 682 w.Header().Set("Content-Type", "text/html")
550 » if err := iframeTemplate.Execute(w, userCode{Code: code, Hash: hash}); e rr != nil { 683 » if err := iframeTemplate.Execute(w, userCode{Code: code, Hash: hash, Sou rce: source}); err != nil {
551 log.Printf("ERROR: Failed to expand template: %q\n", err) 684 log.Printf("ERROR: Failed to expand template: %q\n", err)
552 } 685 }
553 } 686 }
554 687
555 type TryInfo struct { 688 type TryInfo struct {
556 » Hash string `json:"hash"` 689 » Hash string `json:"hash"`
557 » Code string `json:"code"` 690 » Code string `json:"code"`
691 » Source int `json:"source"`
558 } 692 }
559 693
560 // tryInfoHandler returns information about a specific try. 694 // tryInfoHandler returns information about a specific try.
561 func tryInfoHandler(w http.ResponseWriter, r *http.Request) { 695 func tryInfoHandler(w http.ResponseWriter, r *http.Request) {
562 log.Printf("Try Info Handler: %q\n", r.URL.Path) 696 log.Printf("Try Info Handler: %q\n", r.URL.Path)
563 if r.Method != "GET" { 697 if r.Method != "GET" {
564 http.NotFound(w, r) 698 http.NotFound(w, r)
565 return 699 return
566 } 700 }
567 match := tryInfoLink.FindStringSubmatch(r.URL.Path) 701 match := tryInfoLink.FindStringSubmatch(r.URL.Path)
568 if len(match) != 2 { 702 if len(match) != 2 {
569 http.NotFound(w, r) 703 http.NotFound(w, r)
570 return 704 return
571 } 705 }
572 hash := match[1] 706 hash := match[1]
573 » code, err := getCode(hash) 707 » code, source, err := getCode(hash)
574 if err != nil { 708 if err != nil {
575 http.NotFound(w, r) 709 http.NotFound(w, r)
576 return 710 return
577 } 711 }
578 m := TryInfo{ 712 m := TryInfo{
579 » » Hash: hash, 713 » » Hash: hash,
580 » » Code: code, 714 » » Code: code,
715 » » Source: source,
581 } 716 }
582 resp, err := json.Marshal(m) 717 resp, err := json.Marshal(m)
583 if err != nil { 718 if err != nil {
584 reportError(w, r, err, "Failed to serialize a response.") 719 reportError(w, r, err, "Failed to serialize a response.")
585 return 720 return
586 } 721 }
587 w.Header().Set("Content-Type", "application/json") 722 w.Header().Set("Content-Type", "application/json")
588 w.Write(resp) 723 w.Write(resp)
589 } 724 }
590 725
591 func cleanCompileOutput(s, hash string) string { 726 func cleanCompileOutput(s, hash string) string {
592 old := "../../../cache/" + hash + ".cpp:" 727 old := "../../../cache/" + hash + ".cpp:"
593 log.Printf("INFO: replacing %q\n", old) 728 log.Printf("INFO: replacing %q\n", old)
594 return strings.Replace(s, old, "usercode.cpp:", -1) 729 return strings.Replace(s, old, "usercode.cpp:", -1)
595 } 730 }
596 731
597 // mainHandler handles the GET and POST of the main page. 732 // mainHandler handles the GET and POST of the main page.
598 func mainHandler(w http.ResponseWriter, r *http.Request) { 733 func mainHandler(w http.ResponseWriter, r *http.Request) {
599 log.Printf("Main Handler: %q\n", r.URL.Path) 734 log.Printf("Main Handler: %q\n", r.URL.Path)
600 if r.Method == "GET" { 735 if r.Method == "GET" {
601 code := DEFAULT_SAMPLE 736 code := DEFAULT_SAMPLE
737 source := 0
602 match := directLink.FindStringSubmatch(r.URL.Path) 738 match := directLink.FindStringSubmatch(r.URL.Path)
603 var hash string 739 var hash string
604 if len(match) == 2 && r.URL.Path != "/" { 740 if len(match) == 2 && r.URL.Path != "/" {
605 hash = match[1] 741 hash = match[1]
606 if db == nil { 742 if db == nil {
607 http.NotFound(w, r) 743 http.NotFound(w, r)
608 return 744 return
609 } 745 }
610 // Update 'code' with the code found in the database. 746 // Update 'code' with the code found in the database.
611 » » » if err := db.QueryRow("SELECT code FROM webtry WHERE has h=?", hash).Scan(&code); err != nil { 747 » » » if err := db.QueryRow("SELECT code, source_image_id FROM webtry WHERE hash=?", hash).Scan(&code, &source); err != nil {
612 http.NotFound(w, r) 748 http.NotFound(w, r)
613 return 749 return
614 } 750 }
615 } 751 }
616 // Expand the template. 752 // Expand the template.
617 w.Header().Set("Content-Type", "text/html") 753 w.Header().Set("Content-Type", "text/html")
618 » » if err := indexTemplate.Execute(w, userCode{Code: code, Hash: ha sh, Titlebar: Titlebar{GitHash: gitHash, GitInfo: gitInfo}}); err != nil { 754 » » if err := indexTemplate.Execute(w, userCode{Code: code, Hash: ha sh, Source: source, Titlebar: Titlebar{GitHash: gitHash, GitInfo: gitInfo}}); er r != nil {
619 log.Printf("ERROR: Failed to expand template: %q\n", err ) 755 log.Printf("ERROR: Failed to expand template: %q\n", err )
620 } 756 }
621 } else if r.Method == "POST" { 757 } else if r.Method == "POST" {
622 w.Header().Set("Content-Type", "application/json") 758 w.Header().Set("Content-Type", "application/json")
623 buf := bytes.NewBuffer(make([]byte, 0, MAX_TRY_SIZE)) 759 buf := bytes.NewBuffer(make([]byte, 0, MAX_TRY_SIZE))
624 n, err := buf.ReadFrom(r.Body) 760 n, err := buf.ReadFrom(r.Body)
625 if err != nil { 761 if err != nil {
626 reportTryError(w, r, err, "Failed to read a request body .", "") 762 reportTryError(w, r, err, "Failed to read a request body .", "")
627 return 763 return
628 } 764 }
629 if n == MAX_TRY_SIZE { 765 if n == MAX_TRY_SIZE {
630 err := fmt.Errorf("Code length equal to, or exceeded, %d ", MAX_TRY_SIZE) 766 err := fmt.Errorf("Code length equal to, or exceeded, %d ", MAX_TRY_SIZE)
631 reportTryError(w, r, err, "Code too large.", "") 767 reportTryError(w, r, err, "Code too large.", "")
632 return 768 return
633 } 769 }
634 request := TryRequest{} 770 request := TryRequest{}
635 if err := json.Unmarshal(buf.Bytes(), &request); err != nil { 771 if err := json.Unmarshal(buf.Bytes(), &request); err != nil {
636 reportTryError(w, r, err, "Coulnd't decode JSON.", "") 772 reportTryError(w, r, err, "Coulnd't decode JSON.", "")
637 return 773 return
638 } 774 }
639 if hasPreProcessor(request.Code) { 775 if hasPreProcessor(request.Code) {
640 err := fmt.Errorf("Found preprocessor macro in code.") 776 err := fmt.Errorf("Found preprocessor macro in code.")
641 reportTryError(w, r, err, "Preprocessor macros aren't al lowed.", "") 777 reportTryError(w, r, err, "Preprocessor macros aren't al lowed.", "")
642 return 778 return
643 } 779 }
644 » » hash, err := expandCode(LineNumbers(request.Code)) 780 » » hash, err := expandCode(LineNumbers(request.Code), request.Sourc e)
645 if err != nil { 781 if err != nil {
646 reportTryError(w, r, err, "Failed to write the code to c ompile.", hash) 782 reportTryError(w, r, err, "Failed to write the code to c ompile.", hash)
647 return 783 return
648 } 784 }
649 » » writeToDatabase(hash, request.Code, request.Name) 785 » » writeToDatabase(hash, request.Code, request.Name, request.Source )
650 message, err := doCmd(fmt.Sprintf(RESULT_COMPILE, hash, hash), t rue) 786 message, err := doCmd(fmt.Sprintf(RESULT_COMPILE, hash, hash), t rue)
651 if err != nil { 787 if err != nil {
652 message = cleanCompileOutput(message, hash) 788 message = cleanCompileOutput(message, hash)
653 reportTryError(w, r, err, message, hash) 789 reportTryError(w, r, err, message, hash)
654 return 790 return
655 } 791 }
656 linkMessage, err := doCmd(fmt.Sprintf(LINK, hash, hash), true) 792 linkMessage, err := doCmd(fmt.Sprintf(LINK, hash, hash), true)
657 if err != nil { 793 if err != nil {
658 linkMessage = cleanCompileOutput(linkMessage, hash) 794 linkMessage = cleanCompileOutput(linkMessage, hash)
659 reportTryError(w, r, err, linkMessage, hash) 795 reportTryError(w, r, err, linkMessage, hash)
660 return 796 return
661 } 797 }
662 message += linkMessage 798 message += linkMessage
663 cmd := hash + " --out " + hash + ".png" 799 cmd := hash + " --out " + hash + ".png"
800 if request.Source > 0 {
801 cmd += fmt.Sprintf(" --source image-%d.png", request.So urce)
802 }
664 if *useChroot { 803 if *useChroot {
665 cmd = "schroot -c webtry --directory=/inout -- /inout/" + cmd 804 cmd = "schroot -c webtry --directory=/inout -- /inout/" + cmd
666 } else { 805 } else {
667 abs, err := filepath.Abs("../../../inout") 806 abs, err := filepath.Abs("../../../inout")
668 if err != nil { 807 if err != nil {
669 reportTryError(w, r, err, "Failed to find execut able directory.", hash) 808 reportTryError(w, r, err, "Failed to find execut able directory.", hash)
670 return 809 return
671 } 810 }
672 cmd = abs + "/" + cmd 811 cmd = abs + "/" + cmd
673 } 812 }
(...skipping 25 matching lines...) Expand all
699 } 838 }
700 } 839 }
701 840
702 func main() { 841 func main() {
703 flag.Parse() 842 flag.Parse()
704 http.HandleFunc("/i/", autogzip.HandleFunc(imageHandler)) 843 http.HandleFunc("/i/", autogzip.HandleFunc(imageHandler))
705 http.HandleFunc("/w/", autogzip.HandleFunc(workspaceHandler)) 844 http.HandleFunc("/w/", autogzip.HandleFunc(workspaceHandler))
706 http.HandleFunc("/recent/", autogzip.HandleFunc(recentHandler)) 845 http.HandleFunc("/recent/", autogzip.HandleFunc(recentHandler))
707 http.HandleFunc("/iframe/", autogzip.HandleFunc(iframeHandler)) 846 http.HandleFunc("/iframe/", autogzip.HandleFunc(iframeHandler))
708 http.HandleFunc("/json/", autogzip.HandleFunc(tryInfoHandler)) 847 http.HandleFunc("/json/", autogzip.HandleFunc(tryInfoHandler))
848 http.HandleFunc("/sources/", autogzip.HandleFunc(sourcesHandler))
709 849
710 // Resources are served directly 850 // Resources are served directly
711 // TODO add support for caching/etags/gzip 851 // TODO add support for caching/etags/gzip
712 http.Handle("/res/", autogzip.Handle(http.FileServer(http.Dir("./")))) 852 http.Handle("/res/", autogzip.Handle(http.FileServer(http.Dir("./"))))
713 853
714 // TODO Break out /c/ as it's own handler. 854 // TODO Break out /c/ as it's own handler.
715 http.HandleFunc("/", autogzip.HandleFunc(mainHandler)) 855 http.HandleFunc("/", autogzip.HandleFunc(mainHandler))
716 log.Fatal(http.ListenAndServe(*port, nil)) 856 log.Fatal(http.ListenAndServe(*port, nil))
717 } 857 }
OLDNEW
« no previous file with comments | « experimental/webtry/templates/template.cpp ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698