OLD | NEW |
---|---|
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/binary" |
9 "encoding/json" | 9 "encoding/json" |
10 "flag" | 10 "flag" |
11 "fmt" | 11 "fmt" |
12 htemplate "html/template" | 12 htemplate "html/template" |
13 "image" | 13 "image" |
14 _ "image/gif" | 14 _ "image/gif" |
15 _ "image/jpeg" | 15 _ "image/jpeg" |
16 "image/png" | 16 "image/png" |
17 "io/ioutil" | 17 "io/ioutil" |
18 "log" | 18 "log" |
19 "math/rand" | 19 "math/rand" |
20 "net" | 20 "net" |
21 "net/http" | 21 "net/http" |
22 "os" | 22 "os" |
23 "os/exec" | 23 "os/exec" |
24 "path/filepath" | 24 "path/filepath" |
25 "regexp" | 25 "regexp" |
26 "strconv" | |
26 "strings" | 27 "strings" |
27 "text/template" | 28 "text/template" |
28 "time" | 29 "time" |
29 ) | 30 ) |
30 | 31 |
31 import ( | 32 import ( |
32 "github.com/fiorix/go-web/autogzip" | 33 "github.com/fiorix/go-web/autogzip" |
33 _ "github.com/go-sql-driver/mysql" | 34 _ "github.com/go-sql-driver/mysql" |
34 _ "github.com/mattn/go-sqlite3" | 35 _ "github.com/mattn/go-sqlite3" |
35 "github.com/rcrowley/go-metrics" | 36 "github.com/rcrowley/go-metrics" |
(...skipping 361 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
397 return hash, err | 398 return hash, err |
398 } | 399 } |
399 | 400 |
400 // expandGyp produces the GYP file needed to build the code | 401 // expandGyp produces the GYP file needed to build the code |
401 func expandGyp(hash string) error { | 402 func expandGyp(hash string) error { |
402 return writeTemplate(fmt.Sprintf("../../../cache/%s.gyp", hash), gypTemp late, struct{ Hash string }{hash}) | 403 return writeTemplate(fmt.Sprintf("../../../cache/%s.gyp", hash), gypTemp late, struct{ Hash string }{hash}) |
403 } | 404 } |
404 | 405 |
405 // response is serialized to JSON as a response to POSTs. | 406 // response is serialized to JSON as a response to POSTs. |
406 type response struct { | 407 type response struct { |
407 » Message string `json:"message"` | 408 » Message string `json:"message"` |
408 » StdOut string `json:"stdout"` | 409 » CompileErrors []compileError `json:"compileErrors"` |
409 » Img string `json:"img"` | 410 » Img string `json:"img"` |
410 » Hash string `json:"hash"` | 411 » Hash string `json:"hash"` |
411 } | 412 } |
412 | 413 |
413 // doCmd executes the given command line string; the command being | 414 // doCmd executes the given command line string; the command being |
414 // run is expected to not care what its current working directory is. | 415 // run is expected to not care what its current working directory is. |
415 // Returns the stdout and stderr. | 416 // Returns the stdout and stderr. |
416 func doCmd(commandLine string) (string, error) { | 417 func doCmd(commandLine string) (string, error) { |
417 log.Printf("Command: %q\n", commandLine) | 418 log.Printf("Command: %q\n", commandLine) |
418 programAndArgs := strings.SplitN(commandLine, " ", 2) | 419 programAndArgs := strings.SplitN(commandLine, " ", 2) |
419 program := programAndArgs[0] | 420 program := programAndArgs[0] |
420 args := []string{} | 421 args := []string{} |
(...skipping 18 matching lines...) Expand all Loading... | |
439 } | 440 } |
440 | 441 |
441 // reportTryError formats an HTTP error response in JSON and also logs the detai led error message. | 442 // reportTryError formats an HTTP error response in JSON and also logs the detai led error message. |
442 func reportTryError(w http.ResponseWriter, r *http.Request, err error, message, hash string) { | 443 func reportTryError(w http.ResponseWriter, r *http.Request, err error, message, hash string) { |
443 m := response{ | 444 m := response{ |
444 Message: message, | 445 Message: message, |
445 Hash: hash, | 446 Hash: hash, |
446 } | 447 } |
447 log.Printf("Error: %s\n%s", message, err.Error()) | 448 log.Printf("Error: %s\n%s", message, err.Error()) |
448 resp, err := json.Marshal(m) | 449 resp, err := json.Marshal(m) |
450 | |
449 if err != nil { | 451 if err != nil { |
450 http.Error(w, "Failed to serialize a response", 500) | 452 http.Error(w, "Failed to serialize a response", 500) |
451 return | 453 return |
454 } | |
455 w.Header().Set("Content-Type", "text/plain") | |
456 w.Write(resp) | |
457 } | |
458 | |
459 func reportCompileError(w http.ResponseWriter, r *http.Request, compileErrors [] compileError, hash string) { | |
460 m := response{ | |
461 CompileErrors: compileErrors, | |
462 Hash: hash, | |
463 } | |
464 | |
465 resp, err := json.Marshal(m) | |
466 | |
467 if err != nil { | |
468 http.Error(w, "Failed to serialize a response", 500) | |
469 return | |
452 } | 470 } |
453 w.Header().Set("Content-Type", "text/plain") | 471 w.Header().Set("Content-Type", "text/plain") |
454 w.Write(resp) | 472 w.Write(resp) |
455 } | 473 } |
456 | 474 |
457 func writeToDatabase(hash string, code string, workspaceName string, source int, width, height int, gpu bool) { | 475 func writeToDatabase(hash string, code string, workspaceName string, source int, width, height int, gpu bool) { |
458 if db == nil { | 476 if db == nil { |
459 return | 477 return |
460 } | 478 } |
461 if _, err := db.Exec("INSERT INTO webtry (code, hash, width, height, gpu , source_image_id) VALUES(?, ?, ?, ?, ?, ?)", code, hash, width, height, gpu, so urce); err != nil { | 479 if _, err := db.Exec("INSERT INTO webtry (code, hash, width, height, gpu , source_image_id) VALUES(?, ?, ?, ?, ?, ?)", code, hash, width, height, gpu, so urce); err != nil { |
(...skipping 323 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
785 w.Header().Set("Content-Type", "application/json") | 803 w.Header().Set("Content-Type", "application/json") |
786 w.Write(resp) | 804 w.Write(resp) |
787 } | 805 } |
788 | 806 |
789 func cleanCompileOutput(s, hash string) string { | 807 func cleanCompileOutput(s, hash string) string { |
790 old := "../../../cache/src/" + hash + ".cpp:" | 808 old := "../../../cache/src/" + hash + ".cpp:" |
791 log.Printf("INFO: replacing %q\n", old) | 809 log.Printf("INFO: replacing %q\n", old) |
792 return strings.Replace(s, old, "usercode.cpp:", -1) | 810 return strings.Replace(s, old, "usercode.cpp:", -1) |
793 } | 811 } |
794 | 812 |
813 type compileError struct { | |
814 Line int `json:"line"` | |
815 Column int `json:"column"` | |
816 Error string `json:"error"` | |
817 } | |
818 | |
795 // mainHandler handles the GET and POST of the main page. | 819 // mainHandler handles the GET and POST of the main page. |
796 func mainHandler(w http.ResponseWriter, r *http.Request) { | 820 func mainHandler(w http.ResponseWriter, r *http.Request) { |
797 log.Printf("Main Handler: %q\n", r.URL.Path) | 821 log.Printf("Main Handler: %q\n", r.URL.Path) |
798 requestsCounter.Inc(1) | 822 requestsCounter.Inc(1) |
799 if r.Method == "GET" { | 823 if r.Method == "GET" { |
800 code := DEFAULT_SAMPLE | 824 code := DEFAULT_SAMPLE |
801 source := 0 | 825 source := 0 |
802 width := 256 | 826 width := 256 |
803 height := 256 | 827 height := 256 |
804 gpu := false | 828 gpu := false |
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
860 cmd += " --gpu" | 884 cmd += " --gpu" |
861 } | 885 } |
862 if *useChroot { | 886 if *useChroot { |
863 cmd = "schroot -c webtry --directory=/ -- /skia_build/sk ia/experimental/webtry/" + cmd | 887 cmd = "schroot -c webtry --directory=/ -- /skia_build/sk ia/experimental/webtry/" + cmd |
864 } | 888 } |
865 if request.Source > 0 { | 889 if request.Source > 0 { |
866 cmd += fmt.Sprintf(" --source image-%d.png", request.Sou rce) | 890 cmd += fmt.Sprintf(" --source image-%d.png", request.Sou rce) |
867 } | 891 } |
868 | 892 |
869 message, err := doCmd(cmd) | 893 message, err := doCmd(cmd) |
894 | |
895 outputLines := strings.Split(message, "\n") | |
896 errorLines := []compileError{} | |
897 errorRE := regexp.MustCompile(fmt.Sprintf("^.*%s.cpp:(\\d+):(\\d +):\\s*(.*)", hash)) | |
jcgregorio
2014/10/15 19:59:50
Move to top of file so it's only compiled once.
humper
2014/10/15 21:03:59
Done.
| |
898 for _, line := range outputLines { | |
899 match := errorRE.FindStringSubmatch(line) | |
900 if len(match) > 0 { | |
901 lineNumber, parseError := strconv.Atoi(match[1]) | |
902 if parseError != nil { | |
903 log.Printf("ERROR: Couldn't parse line n umber from %s\n", match[1]) | |
jcgregorio
2014/10/15 19:59:50
Add a TODO to switch from log to glog (http://godo
tfarina
2014/10/15 20:05:21
I will do that. I have a bug filed for that: https
| |
904 continue | |
905 } | |
906 columnNumber, parseError := strconv.Atoi(match[2 ]) | |
907 if parseError != nil { | |
908 log.Printf("ERROR: Couldn't parse column number from %s\n", match[2]) | |
909 continue | |
910 } | |
911 errorLines = append(errorLines, | |
912 compileError{ | |
913 Line: lineNumber, | |
914 Column: columnNumber, | |
915 Error: match[3], | |
916 }) | |
917 } | |
918 } | |
919 | |
870 if err != nil { | 920 if err != nil { |
871 » » » reportTryError(w, r, err, "Failed to run the code:\n"+me ssage, hash) | 921 » » » if len(errorLines) > 0 { |
922 » » » » reportCompileError(w, r, errorLines, hash) | |
923 » » » } else { | |
924 » » » » reportTryError(w, r, err, "Failed to run the cod e:\n"+message, hash) | |
925 » » » } | |
872 return | 926 return |
873 } | 927 } |
928 | |
874 png, err := ioutil.ReadFile("../../../inout/" + hash + ".png") | 929 png, err := ioutil.ReadFile("../../../inout/" + hash + ".png") |
875 if err != nil { | 930 if err != nil { |
876 reportTryError(w, r, err, "Failed to open the generated PNG.", hash) | 931 reportTryError(w, r, err, "Failed to open the generated PNG.", hash) |
877 return | 932 return |
878 } | 933 } |
879 | 934 |
880 m := response{ | 935 m := response{ |
881 Message: message, | 936 Message: message, |
882 Img: base64.StdEncoding.EncodeToString([]byte(png)), | 937 Img: base64.StdEncoding.EncodeToString([]byte(png)), |
883 Hash: hash, | 938 Hash: hash, |
(...skipping 18 matching lines...) Expand all Loading... | |
902 http.HandleFunc("/sources/", autogzip.HandleFunc(sourcesHandler)) | 957 http.HandleFunc("/sources/", autogzip.HandleFunc(sourcesHandler)) |
903 | 958 |
904 // Resources are served directly | 959 // Resources are served directly |
905 // TODO add support for caching/etags/gzip | 960 // TODO add support for caching/etags/gzip |
906 http.Handle("/res/", autogzip.Handle(http.FileServer(http.Dir("./")))) | 961 http.Handle("/res/", autogzip.Handle(http.FileServer(http.Dir("./")))) |
907 | 962 |
908 // TODO Break out /c/ as it's own handler. | 963 // TODO Break out /c/ as it's own handler. |
909 http.HandleFunc("/", autogzip.HandleFunc(mainHandler)) | 964 http.HandleFunc("/", autogzip.HandleFunc(mainHandler)) |
910 log.Fatal(http.ListenAndServe(*port, nil)) | 965 log.Fatal(http.ListenAndServe(*port, nil)) |
911 } | 966 } |
OLD | NEW |