| 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         "math/rand" |   18         "math/rand" | 
|   19         "net" |   19         "net" | 
|   20         "net/http" |   20         "net/http" | 
|   21         "os" |   21         "os" | 
|   22         "os/exec" |   22         "os/exec" | 
|   23         "path/filepath" |   23         "path/filepath" | 
|   24         "regexp" |   24         "regexp" | 
 |   25         "strconv" | 
|   25         "strings" |   26         "strings" | 
|   26         "text/template" |   27         "text/template" | 
|   27         "time" |   28         "time" | 
|   28 ) |   29 ) | 
|   29  |   30  | 
|   30 import ( |   31 import ( | 
|   31         "github.com/fiorix/go-web/autogzip" |   32         "github.com/fiorix/go-web/autogzip" | 
|   32         _ "github.com/go-sql-driver/mysql" |   33         _ "github.com/go-sql-driver/mysql" | 
|   33         "github.com/golang/glog" |   34         "github.com/golang/glog" | 
|   34         _ "github.com/mattn/go-sqlite3" |   35         _ "github.com/mattn/go-sqlite3" | 
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
|   79  |   80  | 
|   80         // imageLink is the regex that matches URLs paths that are direct links 
     to PNGs. |   81         // imageLink is the regex that matches URLs paths that are direct links 
     to PNGs. | 
|   81         imageLink = regexp.MustCompile("^/i/([a-z0-9-]+.png)$") |   82         imageLink = regexp.MustCompile("^/i/([a-z0-9-]+.png)$") | 
|   82  |   83  | 
|   83         // tryInfoLink is the regex that matches URLs paths that are direct link
     s to data about a single try. |   84         // tryInfoLink is the regex that matches URLs paths that are direct link
     s to data about a single try. | 
|   84         tryInfoLink = regexp.MustCompile("^/json/([a-f0-9]+)$") |   85         tryInfoLink = regexp.MustCompile("^/json/([a-f0-9]+)$") | 
|   85  |   86  | 
|   86         // workspaceLink is the regex that matches URLs paths for workspaces. |   87         // workspaceLink is the regex that matches URLs paths for workspaces. | 
|   87         workspaceLink = regexp.MustCompile("^/w/([a-z0-9-]+)$") |   88         workspaceLink = regexp.MustCompile("^/w/([a-z0-9-]+)$") | 
|   88  |   89  | 
 |   90         // errorRE is ther regex that matches compiler errors and extracts the l
     ine / column information. | 
 |   91         errorRE = regexp.MustCompile("^.*.cpp:(\\d+):(\\d+):\\s*(.*)") | 
 |   92  | 
|   89         // workspaceNameAdj is a list of adjectives for building workspace names
     . |   93         // workspaceNameAdj is a list of adjectives for building workspace names
     . | 
|   90         workspaceNameAdj = []string{ |   94         workspaceNameAdj = []string{ | 
|   91                 "autumn", "hidden", "bitter", "misty", "silent", "empty", "dry",
      "dark", |   95                 "autumn", "hidden", "bitter", "misty", "silent", "empty", "dry",
      "dark", | 
|   92                 "summer", "icy", "delicate", "quiet", "white", "cool", "spring",
      "winter", |   96                 "summer", "icy", "delicate", "quiet", "white", "cool", "spring",
      "winter", | 
|   93                 "patient", "twilight", "dawn", "crimson", "wispy", "weathered", 
     "blue", |   97                 "patient", "twilight", "dawn", "crimson", "wispy", "weathered", 
     "blue", | 
|   94                 "billowing", "broken", "cold", "damp", "falling", "frosty", "gre
     en", |   98                 "billowing", "broken", "cold", "damp", "falling", "frosty", "gre
     en", | 
|   95                 "long", "late", "lingering", "bold", "little", "morning", "muddy
     ", "old", |   99                 "long", "late", "lingering", "bold", "little", "morning", "muddy
     ", "old", | 
|   96                 "red", "rough", "still", "small", "sparkling", "throbbing", "shy
     ", |  100                 "red", "rough", "still", "small", "sparkling", "throbbing", "shy
     ", | 
|   97                 "wandering", "withered", "wild", "black", "young", "holy", "soli
     tary", |  101                 "wandering", "withered", "wild", "black", "young", "holy", "soli
     tary", | 
|   98                 "fragrant", "aged", "snowy", "proud", "floral", "restless", "div
     ine", |  102                 "fragrant", "aged", "snowy", "proud", "floral", "restless", "div
     ine", | 
| (...skipping 299 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
|  398         return hash, err |  402         return hash, err | 
|  399 } |  403 } | 
|  400  |  404  | 
|  401 // expandGyp produces the GYP file needed to build the code |  405 // expandGyp produces the GYP file needed to build the code | 
|  402 func expandGyp(hash string) error { |  406 func expandGyp(hash string) error { | 
|  403         return writeTemplate(fmt.Sprintf("../../../cache/%s.gyp", hash), gypTemp
     late, struct{ Hash string }{hash}) |  407         return writeTemplate(fmt.Sprintf("../../../cache/%s.gyp", hash), gypTemp
     late, struct{ Hash string }{hash}) | 
|  404 } |  408 } | 
|  405  |  409  | 
|  406 // response is serialized to JSON as a response to POSTs. |  410 // response is serialized to JSON as a response to POSTs. | 
|  407 type response struct { |  411 type response struct { | 
|  408 »       Message string `json:"message"` |  412 »       Message       string         `json:"message"` | 
|  409 »       StdOut  string `json:"stdout"` |  413 »       CompileErrors []compileError `json:"compileErrors"` | 
|  410 »       Img     string `json:"img"` |  414 »       Img           string         `json:"img"` | 
|  411 »       Hash    string `json:"hash"` |  415 »       Hash          string         `json:"hash"` | 
|  412 } |  416 } | 
|  413  |  417  | 
|  414 // doCmd executes the given command line string; the command being |  418 // doCmd executes the given command line string; the command being | 
|  415 // run is expected to not care what its current working directory is. |  419 // run is expected to not care what its current working directory is. | 
|  416 // Returns the stdout and stderr. |  420 // Returns the stdout and stderr. | 
|  417 func doCmd(commandLine string) (string, error) { |  421 func doCmd(commandLine string) (string, error) { | 
|  418         glog.Infof("Command: %q\n", commandLine) |  422         glog.Infof("Command: %q\n", commandLine) | 
|  419         programAndArgs := strings.SplitN(commandLine, " ", 2) |  423         programAndArgs := strings.SplitN(commandLine, " ", 2) | 
|  420         program := programAndArgs[0] |  424         program := programAndArgs[0] | 
|  421         args := []string{} |  425         args := []string{} | 
| (...skipping 18 matching lines...) Expand all  Loading... | 
|  440 } |  444 } | 
|  441  |  445  | 
|  442 // reportTryError formats an HTTP error response in JSON and also logs the detai
     led error message. |  446 // reportTryError formats an HTTP error response in JSON and also logs the detai
     led error message. | 
|  443 func reportTryError(w http.ResponseWriter, r *http.Request, err error, message, 
     hash string) { |  447 func reportTryError(w http.ResponseWriter, r *http.Request, err error, message, 
     hash string) { | 
|  444         m := response{ |  448         m := response{ | 
|  445                 Message: message, |  449                 Message: message, | 
|  446                 Hash:    hash, |  450                 Hash:    hash, | 
|  447         } |  451         } | 
|  448         glog.Errorf("%s\n%s", message, err) |  452         glog.Errorf("%s\n%s", message, err) | 
|  449         resp, err := json.Marshal(m) |  453         resp, err := json.Marshal(m) | 
 |  454  | 
|  450         if err != nil { |  455         if err != nil { | 
|  451                 http.Error(w, "Failed to serialize a response", 500) |  456                 http.Error(w, "Failed to serialize a response", 500) | 
|  452                 return |  457                 return | 
 |  458         } | 
 |  459         w.Header().Set("Content-Type", "text/plain") | 
 |  460         w.Write(resp) | 
 |  461 } | 
 |  462  | 
 |  463 func reportCompileError(w http.ResponseWriter, r *http.Request, compileErrors []
     compileError, hash string) { | 
 |  464         m := response{ | 
 |  465                 CompileErrors: compileErrors, | 
 |  466                 Hash:          hash, | 
 |  467         } | 
 |  468  | 
 |  469         resp, err := json.Marshal(m) | 
 |  470  | 
 |  471         if err != nil { | 
 |  472                 http.Error(w, "Failed to serialize a response", 500) | 
 |  473                 return | 
|  453         } |  474         } | 
|  454         w.Header().Set("Content-Type", "text/plain") |  475         w.Header().Set("Content-Type", "text/plain") | 
|  455         w.Write(resp) |  476         w.Write(resp) | 
|  456 } |  477 } | 
|  457  |  478  | 
|  458 func writeToDatabase(hash string, code string, workspaceName string, source int,
      width, height int, gpu bool) { |  479 func writeToDatabase(hash string, code string, workspaceName string, source int,
      width, height int, gpu bool) { | 
|  459         if db == nil { |  480         if db == nil { | 
|  460                 return |  481                 return | 
|  461         } |  482         } | 
|  462         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 { |  483         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 324 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
|  787         w.Header().Set("Content-Type", "application/json") |  808         w.Header().Set("Content-Type", "application/json") | 
|  788         w.Write(resp) |  809         w.Write(resp) | 
|  789 } |  810 } | 
|  790  |  811  | 
|  791 func cleanCompileOutput(s, hash string) string { |  812 func cleanCompileOutput(s, hash string) string { | 
|  792         old := "../../../cache/src/" + hash + ".cpp:" |  813         old := "../../../cache/src/" + hash + ".cpp:" | 
|  793         glog.Infof("replacing %q\n", old) |  814         glog.Infof("replacing %q\n", old) | 
|  794         return strings.Replace(s, old, "usercode.cpp:", -1) |  815         return strings.Replace(s, old, "usercode.cpp:", -1) | 
|  795 } |  816 } | 
|  796  |  817  | 
 |  818 type compileError struct { | 
 |  819         Line   int    `json:"line"` | 
 |  820         Column int    `json:"column"` | 
 |  821         Error  string `json:"error"` | 
 |  822 } | 
 |  823  | 
|  797 // mainHandler handles the GET and POST of the main page. |  824 // mainHandler handles the GET and POST of the main page. | 
|  798 func mainHandler(w http.ResponseWriter, r *http.Request) { |  825 func mainHandler(w http.ResponseWriter, r *http.Request) { | 
|  799         glog.Infof("Main Handler: %q\n", r.URL.Path) |  826         glog.Infof("Main Handler: %q\n", r.URL.Path) | 
|  800         requestsCounter.Inc(1) |  827         requestsCounter.Inc(1) | 
|  801         if r.Method == "GET" { |  828         if r.Method == "GET" { | 
|  802                 code := DEFAULT_SAMPLE |  829                 code := DEFAULT_SAMPLE | 
|  803                 source := 0 |  830                 source := 0 | 
|  804                 width := 256 |  831                 width := 256 | 
|  805                 height := 256 |  832                 height := 256 | 
|  806                 gpu := false |  833                 gpu := false | 
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
|  862                         cmd += " --gpu" |  889                         cmd += " --gpu" | 
|  863                 } |  890                 } | 
|  864                 if *useChroot { |  891                 if *useChroot { | 
|  865                         cmd = "schroot -c webtry --directory=/ -- /skia_build/sk
     ia/experimental/webtry/" + cmd |  892                         cmd = "schroot -c webtry --directory=/ -- /skia_build/sk
     ia/experimental/webtry/" + cmd | 
|  866                 } |  893                 } | 
|  867                 if request.Source > 0 { |  894                 if request.Source > 0 { | 
|  868                         cmd += fmt.Sprintf(" --source image-%d.png", request.Sou
     rce) |  895                         cmd += fmt.Sprintf(" --source image-%d.png", request.Sou
     rce) | 
|  869                 } |  896                 } | 
|  870  |  897  | 
|  871                 message, err := doCmd(cmd) |  898                 message, err := doCmd(cmd) | 
 |  899  | 
 |  900                 outputLines := strings.Split(message, "\n") | 
 |  901                 errorLines := []compileError{} | 
 |  902                 for _, line := range outputLines { | 
 |  903                         match := errorRE.FindStringSubmatch(line) | 
 |  904                         if len(match) > 0 { | 
 |  905                                 lineNumber, parseError := strconv.Atoi(match[1]) | 
 |  906                                 if parseError != nil { | 
 |  907                                         glog.Errorf("ERROR: Couldn't parse line 
     number from %s\n", match[1]) | 
 |  908                                         continue | 
 |  909                                 } | 
 |  910                                 columnNumber, parseError := strconv.Atoi(match[2
     ]) | 
 |  911                                 if parseError != nil { | 
 |  912                                         glog.Errorf("ERROR: Couldn't parse colum
     n number from %s\n", match[2]) | 
 |  913                                         continue | 
 |  914                                 } | 
 |  915                                 errorLines = append(errorLines, | 
 |  916                                         compileError{ | 
 |  917                                                 Line:   lineNumber, | 
 |  918                                                 Column: columnNumber, | 
 |  919                                                 Error:  match[3], | 
 |  920                                         }) | 
 |  921                         } | 
 |  922                 } | 
 |  923  | 
|  872                 if err != nil { |  924                 if err != nil { | 
|  873 »       »       »       reportTryError(w, r, err, "Failed to run the code:\n"+me
     ssage, hash) |  925 »       »       »       if len(errorLines) > 0 { | 
 |  926 »       »       »       »       reportCompileError(w, r, errorLines, hash) | 
 |  927 »       »       »       } else { | 
 |  928 »       »       »       »       reportTryError(w, r, err, "Failed to run the cod
     e:\n"+message, hash) | 
 |  929 »       »       »       } | 
|  874                         return |  930                         return | 
|  875                 } |  931                 } | 
 |  932  | 
|  876                 png, err := ioutil.ReadFile("../../../inout/" + hash + ".png") |  933                 png, err := ioutil.ReadFile("../../../inout/" + hash + ".png") | 
|  877                 if err != nil { |  934                 if err != nil { | 
|  878                         reportTryError(w, r, err, "Failed to open the generated 
     PNG.", hash) |  935                         reportTryError(w, r, err, "Failed to open the generated 
     PNG.", hash) | 
|  879                         return |  936                         return | 
|  880                 } |  937                 } | 
|  881  |  938  | 
|  882                 m := response{ |  939                 m := response{ | 
|  883                         Message: message, |  940                         Message: message, | 
|  884                         Img:     base64.StdEncoding.EncodeToString([]byte(png)), |  941                         Img:     base64.StdEncoding.EncodeToString([]byte(png)), | 
|  885                         Hash:    hash, |  942                         Hash:    hash, | 
| (...skipping 18 matching lines...) Expand all  Loading... | 
|  904         http.HandleFunc("/sources/", autogzip.HandleFunc(sourcesHandler)) |  961         http.HandleFunc("/sources/", autogzip.HandleFunc(sourcesHandler)) | 
|  905  |  962  | 
|  906         // Resources are served directly |  963         // Resources are served directly | 
|  907         // TODO add support for caching/etags/gzip |  964         // TODO add support for caching/etags/gzip | 
|  908         http.Handle("/res/", autogzip.Handle(http.FileServer(http.Dir("./")))) |  965         http.Handle("/res/", autogzip.Handle(http.FileServer(http.Dir("./")))) | 
|  909  |  966  | 
|  910         // TODO Break out /c/ as it's own handler. |  967         // TODO Break out /c/ as it's own handler. | 
|  911         http.HandleFunc("/", autogzip.HandleFunc(mainHandler)) |  968         http.HandleFunc("/", autogzip.HandleFunc(mainHandler)) | 
|  912         glog.Fatal(http.ListenAndServe(*port, nil)) |  969         glog.Fatal(http.ListenAndServe(*port, nil)) | 
|  913 } |  970 } | 
| OLD | NEW |