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/json" | 8 "encoding/json" |
9 "flag" | 9 "flag" |
10 "fmt" | 10 "fmt" |
(...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
128 } | 128 } |
129 os.Chdir(cwd) | 129 os.Chdir(cwd) |
130 | 130 |
131 codeTemplate, err = template.ParseFiles(filepath.Join(cwd, "templates/te
mplate.cpp")) | 131 codeTemplate, err = template.ParseFiles(filepath.Join(cwd, "templates/te
mplate.cpp")) |
132 if err != nil { | 132 if err != nil { |
133 panic(err) | 133 panic(err) |
134 } | 134 } |
135 indexTemplate, err = htemplate.ParseFiles( | 135 indexTemplate, err = htemplate.ParseFiles( |
136 filepath.Join(cwd, "templates/index.html"), | 136 filepath.Join(cwd, "templates/index.html"), |
137 filepath.Join(cwd, "templates/titlebar.html"), | 137 filepath.Join(cwd, "templates/titlebar.html"), |
| 138 filepath.Join(cwd, "templates/content.html"), |
138 ) | 139 ) |
139 if err != nil { | 140 if err != nil { |
140 panic(err) | 141 panic(err) |
141 } | 142 } |
142 iframeTemplate, err = htemplate.ParseFiles( | 143 iframeTemplate, err = htemplate.ParseFiles( |
143 filepath.Join(cwd, "templates/iframe.html"), | 144 filepath.Join(cwd, "templates/iframe.html"), |
| 145 filepath.Join(cwd, "templates/content.html"), |
144 ) | 146 ) |
145 if err != nil { | 147 if err != nil { |
146 panic(err) | 148 panic(err) |
147 } | 149 } |
148 recentTemplate, err = htemplate.ParseFiles( | 150 recentTemplate, err = htemplate.ParseFiles( |
149 filepath.Join(cwd, "templates/recent.html"), | 151 filepath.Join(cwd, "templates/recent.html"), |
150 filepath.Join(cwd, "templates/titlebar.html"), | 152 filepath.Join(cwd, "templates/titlebar.html"), |
151 ) | 153 ) |
152 if err != nil { | 154 if err != nil { |
153 panic(err) | 155 panic(err) |
154 } | 156 } |
155 workspaceTemplate, err = htemplate.ParseFiles( | 157 workspaceTemplate, err = htemplate.ParseFiles( |
156 filepath.Join(cwd, "templates/workspace.html"), | 158 filepath.Join(cwd, "templates/workspace.html"), |
157 filepath.Join(cwd, "templates/titlebar.html"), | 159 filepath.Join(cwd, "templates/titlebar.html"), |
| 160 filepath.Join(cwd, "templates/content.html"), |
158 ) | 161 ) |
159 if err != nil { | 162 if err != nil { |
160 panic(err) | 163 panic(err) |
161 } | 164 } |
162 | 165 |
163 // Connect to MySQL server. First, get the password from the metadata se
rver. | 166 // Connect to MySQL server. First, get the password from the metadata se
rver. |
164 // See https://developers.google.com/compute/docs/metadata#custom. | 167 // See https://developers.google.com/compute/docs/metadata#custom. |
165 req, err := http.NewRequest("GET", "http://metadata/computeMetadata/v1/i
nstance/attributes/password", nil) | 168 req, err := http.NewRequest("GET", "http://metadata/computeMetadata/v1/i
nstance/attributes/password", nil) |
166 if err != nil { | 169 if err != nil { |
167 panic(err) | 170 panic(err) |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
213 | 216 |
214 FOREIGN KEY (name) REFERENCES workspace(name) | 217 FOREIGN KEY (name) REFERENCES workspace(name) |
215 )` | 218 )` |
216 _, err = db.Exec(sql) | 219 _, err = db.Exec(sql) |
217 log.Printf("Info: status creating sqlite table for workspace try
: %q\n", err) | 220 log.Printf("Info: status creating sqlite table for workspace try
: %q\n", err) |
218 } | 221 } |
219 } | 222 } |
220 | 223 |
221 // userCode is used in template expansion. | 224 // userCode is used in template expansion. |
222 type userCode struct { | 225 type userCode struct { |
223 » UserCode string | 226 » Code string |
224 » Hash string | 227 » Hash string |
225 } | 228 } |
226 | 229 |
227 // expandToFile expands the template and writes the result to the file. | 230 // expandToFile expands the template and writes the result to the file. |
228 func expandToFile(filename string, code string, t *template.Template) error { | 231 func expandToFile(filename string, code string, t *template.Template) error { |
229 f, err := os.Create(filename) | 232 f, err := os.Create(filename) |
230 if err != nil { | 233 if err != nil { |
231 return err | 234 return err |
232 } | 235 } |
233 defer f.Close() | 236 defer f.Close() |
234 » return t.Execute(f, userCode{UserCode: code}) | 237 » return t.Execute(f, userCode{Code: code}) |
235 } | 238 } |
236 | 239 |
237 // expandCode expands the template into a file and calculate the MD5 hash. | 240 // expandCode expands the template into a file and calculate the MD5 hash. |
238 func expandCode(code string) (string, error) { | 241 func expandCode(code string) (string, error) { |
239 h := md5.New() | 242 h := md5.New() |
240 h.Write([]byte(code)) | 243 h.Write([]byte(code)) |
241 hash := fmt.Sprintf("%x", h.Sum(nil)) | 244 hash := fmt.Sprintf("%x", h.Sum(nil)) |
242 // At this point we are running in skia/experimental/webtry, making cach
e a | 245 // At this point we are running in skia/experimental/webtry, making cach
e a |
243 // peer directory to skia. | 246 // peer directory to skia. |
244 // TODO(jcgregorio) Make all relative directories into flags. | 247 // TODO(jcgregorio) Make all relative directories into flags. |
245 err := expandToFile(fmt.Sprintf("../../../cache/%s.cpp", hash), code, co
deTemplate) | 248 err := expandToFile(fmt.Sprintf("../../../cache/%s.cpp", hash), code, co
deTemplate) |
246 return hash, err | 249 return hash, err |
247 } | 250 } |
248 | 251 |
249 // response is serialized to JSON as a response to POSTs. | 252 // response is serialized to JSON as a response to POSTs. |
250 type response struct { | 253 type response struct { |
251 Message string `json:"message"` | 254 Message string `json:"message"` |
| 255 StdOut string `json:"stdout"` |
252 Img string `json:"img"` | 256 Img string `json:"img"` |
253 Hash string `json:"hash"` | 257 Hash string `json:"hash"` |
254 } | 258 } |
255 | 259 |
256 // doCmd executes the given command line string in either the out/Debug | 260 // doCmd executes the given command line string in either the out/Debug |
257 // directory or the inout directory. Returns the stdout, and stderr in the case | 261 // directory or the inout directory. Returns the stdout, and stderr in the case |
258 // of a non-zero exit code. | 262 // of a non-zero exit code. |
259 func doCmd(commandLine string, moveToDebug bool) (string, error) { | 263 func doCmd(commandLine string, moveToDebug bool) (string, error) { |
260 log.Printf("Command: %q\n", commandLine) | 264 log.Printf("Command: %q\n", commandLine) |
261 programAndArgs := strings.SplitN(commandLine, " ", 2) | 265 programAndArgs := strings.SplitN(commandLine, " ", 2) |
(...skipping 29 matching lines...) Expand all Loading... |
291 log.Printf("Exit status: %s\n", err.Error()) | 295 log.Printf("Exit status: %s\n", err.Error()) |
292 log.Printf("StdErr: %s\n", stdErr.String()) | 296 log.Printf("StdErr: %s\n", stdErr.String()) |
293 message += stdErr.String() | 297 message += stdErr.String() |
294 return message, fmt.Errorf("Failed to run command.") | 298 return message, fmt.Errorf("Failed to run command.") |
295 } | 299 } |
296 return message, nil | 300 return message, nil |
297 } | 301 } |
298 | 302 |
299 // reportError formats an HTTP error response and also logs the detailed error m
essage. | 303 // reportError formats an HTTP error response and also logs the detailed error m
essage. |
300 func reportError(w http.ResponseWriter, r *http.Request, err error, message stri
ng) { | 304 func reportError(w http.ResponseWriter, r *http.Request, err error, message stri
ng) { |
| 305 log.Printf("Error: %s\n%s", message, err.Error()) |
| 306 http.Error(w, message, 500) |
| 307 } |
| 308 |
| 309 // reportTryError formats an HTTP error response in JSON and also logs the detai
led error message. |
| 310 func reportTryError(w http.ResponseWriter, r *http.Request, err error, message,
hash string) { |
301 m := response{ | 311 m := response{ |
302 Message: message, | 312 Message: message, |
| 313 Hash: hash, |
303 } | 314 } |
304 log.Printf("Error: %s\n%s", message, err.Error()) | 315 log.Printf("Error: %s\n%s", message, err.Error()) |
305 resp, err := json.Marshal(m) | 316 resp, err := json.Marshal(m) |
306 if err != nil { | 317 if err != nil { |
307 http.Error(w, "Failed to serialize a response", 500) | 318 http.Error(w, "Failed to serialize a response", 500) |
308 return | 319 return |
309 } | 320 } |
310 w.Write(resp) | 321 w.Write(resp) |
311 } | 322 } |
312 | 323 |
(...skipping 179 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
492 http.NotFound(w, r) | 503 http.NotFound(w, r) |
493 return | 504 return |
494 } | 505 } |
495 var code string | 506 var code string |
496 code, err := getCode(hash) | 507 code, err := getCode(hash) |
497 if err != nil { | 508 if err != nil { |
498 http.NotFound(w, r) | 509 http.NotFound(w, r) |
499 return | 510 return |
500 } | 511 } |
501 // Expand the template. | 512 // Expand the template. |
502 » if err := iframeTemplate.Execute(w, userCode{UserCode: code, Hash: hash}
); err != nil { | 513 » if err := iframeTemplate.Execute(w, userCode{Code: code, Hash: hash}); e
rr != nil { |
503 log.Printf("ERROR: Failed to expand template: %q\n", err) | 514 log.Printf("ERROR: Failed to expand template: %q\n", err) |
504 } | 515 } |
505 } | 516 } |
506 | 517 |
507 type TryInfo struct { | 518 type TryInfo struct { |
508 Hash string `json:"hash"` | 519 Hash string `json:"hash"` |
509 Code string `json:"code"` | 520 Code string `json:"code"` |
510 } | 521 } |
511 | 522 |
512 // tryInfoHandler returns information about a specific try. | 523 // tryInfoHandler returns information about a specific try. |
(...skipping 20 matching lines...) Expand all Loading... |
533 } | 544 } |
534 resp, err := json.Marshal(m) | 545 resp, err := json.Marshal(m) |
535 if err != nil { | 546 if err != nil { |
536 reportError(w, r, err, "Failed to serialize a response.") | 547 reportError(w, r, err, "Failed to serialize a response.") |
537 return | 548 return |
538 } | 549 } |
539 w.Header().Set("Content-Type", "application/json") | 550 w.Header().Set("Content-Type", "application/json") |
540 w.Write(resp) | 551 w.Write(resp) |
541 } | 552 } |
542 | 553 |
| 554 func cleanCompileOutput(s, hash string) string { |
| 555 old := "../../../cache/" + hash + ".cpp:" |
| 556 log.Printf("INFO: replacing %q\n", old) |
| 557 return strings.Replace(s, old, "usercode.cpp:", -1) |
| 558 } |
| 559 |
543 // mainHandler handles the GET and POST of the main page. | 560 // mainHandler handles the GET and POST of the main page. |
544 func mainHandler(w http.ResponseWriter, r *http.Request) { | 561 func mainHandler(w http.ResponseWriter, r *http.Request) { |
545 log.Printf("Main Handler: %q\n", r.URL.Path) | 562 log.Printf("Main Handler: %q\n", r.URL.Path) |
546 if r.Method == "GET" { | 563 if r.Method == "GET" { |
547 code := DEFAULT_SAMPLE | 564 code := DEFAULT_SAMPLE |
548 match := directLink.FindStringSubmatch(r.URL.Path) | 565 match := directLink.FindStringSubmatch(r.URL.Path) |
549 var hash string | 566 var hash string |
550 if len(match) == 2 && r.URL.Path != "/" { | 567 if len(match) == 2 && r.URL.Path != "/" { |
551 hash = match[1] | 568 hash = match[1] |
552 if db == nil { | 569 if db == nil { |
553 http.NotFound(w, r) | 570 http.NotFound(w, r) |
554 return | 571 return |
555 } | 572 } |
556 // Update 'code' with the code found in the database. | 573 // Update 'code' with the code found in the database. |
557 if err := db.QueryRow("SELECT code FROM webtry WHERE has
h=?", hash).Scan(&code); err != nil { | 574 if err := db.QueryRow("SELECT code FROM webtry WHERE has
h=?", hash).Scan(&code); err != nil { |
558 http.NotFound(w, r) | 575 http.NotFound(w, r) |
559 return | 576 return |
560 } | 577 } |
561 } | 578 } |
562 // Expand the template. | 579 // Expand the template. |
563 » » if err := indexTemplate.Execute(w, userCode{UserCode: code, Hash
: hash}); err != nil { | 580 » » if err := indexTemplate.Execute(w, userCode{Code: code, Hash: ha
sh}); err != nil { |
564 log.Printf("ERROR: Failed to expand template: %q\n", err
) | 581 log.Printf("ERROR: Failed to expand template: %q\n", err
) |
565 } | 582 } |
566 } else if r.Method == "POST" { | 583 } else if r.Method == "POST" { |
567 w.Header().Set("Content-Type", "application/json") | 584 w.Header().Set("Content-Type", "application/json") |
568 buf := bytes.NewBuffer(make([]byte, 0, MAX_TRY_SIZE)) | 585 buf := bytes.NewBuffer(make([]byte, 0, MAX_TRY_SIZE)) |
569 n, err := buf.ReadFrom(r.Body) | 586 n, err := buf.ReadFrom(r.Body) |
570 if err != nil { | 587 if err != nil { |
571 » » » reportError(w, r, err, "Failed to read a request body.") | 588 » » » reportTryError(w, r, err, "Failed to read a request body
.", "") |
572 return | 589 return |
573 } | 590 } |
574 if n == MAX_TRY_SIZE { | 591 if n == MAX_TRY_SIZE { |
575 err := fmt.Errorf("Code length equal to, or exceeded, %d
", MAX_TRY_SIZE) | 592 err := fmt.Errorf("Code length equal to, or exceeded, %d
", MAX_TRY_SIZE) |
576 » » » reportError(w, r, err, "Code too large.") | 593 » » » reportTryError(w, r, err, "Code too large.", "") |
577 return | 594 return |
578 } | 595 } |
579 request := TryRequest{} | 596 request := TryRequest{} |
580 if err := json.Unmarshal(buf.Bytes(), &request); err != nil { | 597 if err := json.Unmarshal(buf.Bytes(), &request); err != nil { |
581 » » » reportError(w, r, err, "Coulnd't decode JSON.") | 598 » » » reportTryError(w, r, err, "Coulnd't decode JSON.", "") |
582 return | 599 return |
583 } | 600 } |
584 if hasPreProcessor(request.Code) { | 601 if hasPreProcessor(request.Code) { |
585 err := fmt.Errorf("Found preprocessor macro in code.") | 602 err := fmt.Errorf("Found preprocessor macro in code.") |
586 » » » reportError(w, r, err, "Preprocessor macros aren't allow
ed.") | 603 » » » reportTryError(w, r, err, "Preprocessor macros aren't al
lowed.", "") |
587 return | 604 return |
588 } | 605 } |
589 hash, err := expandCode(LineNumbers(request.Code)) | 606 hash, err := expandCode(LineNumbers(request.Code)) |
590 if err != nil { | 607 if err != nil { |
591 » » » reportError(w, r, err, "Failed to write the code to comp
ile.") | 608 » » » reportTryError(w, r, err, "Failed to write the code to c
ompile.", hash) |
592 return | 609 return |
593 } | 610 } |
594 writeToDatabase(hash, request.Code, request.Name) | 611 writeToDatabase(hash, request.Code, request.Name) |
595 message, err := doCmd(fmt.Sprintf(RESULT_COMPILE, hash, hash), t
rue) | 612 message, err := doCmd(fmt.Sprintf(RESULT_COMPILE, hash, hash), t
rue) |
596 if err != nil { | 613 if err != nil { |
597 » » » reportError(w, r, err, "Failed to compile the code:\n"+m
essage) | 614 » » » message = cleanCompileOutput(message, hash) |
| 615 » » » reportTryError(w, r, err, message, hash) |
598 return | 616 return |
599 } | 617 } |
600 linkMessage, err := doCmd(fmt.Sprintf(LINK, hash, hash), true) | 618 linkMessage, err := doCmd(fmt.Sprintf(LINK, hash, hash), true) |
601 if err != nil { | 619 if err != nil { |
602 » » » reportError(w, r, err, "Failed to link the code:\n"+link
Message) | 620 » » » linkMessage = cleanCompileOutput(linkMessage, hash) |
| 621 » » » reportTryError(w, r, err, linkMessage, hash) |
603 return | 622 return |
604 } | 623 } |
605 message += linkMessage | 624 message += linkMessage |
606 cmd := hash + " --out " + hash + ".png" | 625 cmd := hash + " --out " + hash + ".png" |
607 if *useChroot { | 626 if *useChroot { |
608 cmd = "schroot -c webtry --directory=/inout -- /inout/"
+ cmd | 627 cmd = "schroot -c webtry --directory=/inout -- /inout/"
+ cmd |
609 } else { | 628 } else { |
610 abs, err := filepath.Abs("../../../inout") | 629 abs, err := filepath.Abs("../../../inout") |
611 if err != nil { | 630 if err != nil { |
612 » » » » reportError(w, r, err, "Failed to find executabl
e directory.") | 631 » » » » reportTryError(w, r, err, "Failed to find execut
able directory.", hash) |
613 return | 632 return |
614 } | 633 } |
615 cmd = abs + "/" + cmd | 634 cmd = abs + "/" + cmd |
616 } | 635 } |
617 | 636 |
618 execMessage, err := doCmd(cmd, false) | 637 execMessage, err := doCmd(cmd, false) |
619 if err != nil { | 638 if err != nil { |
620 » » » reportError(w, r, err, "Failed to run the code:\n"+execM
essage) | 639 » » » reportTryError(w, r, err, "Failed to run the code:\n"+ex
ecMessage, hash) |
621 return | 640 return |
622 } | 641 } |
623 png, err := ioutil.ReadFile("../../../inout/" + hash + ".png") | 642 png, err := ioutil.ReadFile("../../../inout/" + hash + ".png") |
624 if err != nil { | 643 if err != nil { |
625 » » » reportError(w, r, err, "Failed to open the generated PNG
.") | 644 » » » reportTryError(w, r, err, "Failed to open the generated
PNG.", hash) |
626 return | 645 return |
627 } | 646 } |
628 | 647 |
629 m := response{ | 648 m := response{ |
630 Message: message, | 649 Message: message, |
| 650 StdOut: execMessage, |
631 Img: base64.StdEncoding.EncodeToString([]byte(png)), | 651 Img: base64.StdEncoding.EncodeToString([]byte(png)), |
632 Hash: hash, | 652 Hash: hash, |
633 } | 653 } |
634 resp, err := json.Marshal(m) | 654 resp, err := json.Marshal(m) |
635 if err != nil { | 655 if err != nil { |
636 » » » reportError(w, r, err, "Failed to serialize a response."
) | 656 » » » reportTryError(w, r, err, "Failed to serialize a respons
e.", hash) |
637 return | 657 return |
638 } | 658 } |
639 w.Write(resp) | 659 w.Write(resp) |
640 } | 660 } |
641 } | 661 } |
642 | 662 |
643 func main() { | 663 func main() { |
644 flag.Parse() | 664 flag.Parse() |
645 http.HandleFunc("/i/", imageHandler) | 665 http.HandleFunc("/i/", imageHandler) |
646 http.HandleFunc("/w/", workspaceHandler) | 666 http.HandleFunc("/w/", workspaceHandler) |
647 http.HandleFunc("/recent/", recentHandler) | 667 http.HandleFunc("/recent/", recentHandler) |
648 http.HandleFunc("/iframe/", iframeHandler) | 668 http.HandleFunc("/iframe/", iframeHandler) |
649 http.HandleFunc("/json/", tryInfoHandler) | 669 http.HandleFunc("/json/", tryInfoHandler) |
650 http.HandleFunc("/css/", cssHandler) | 670 http.HandleFunc("/css/", cssHandler) |
651 http.HandleFunc("/js/", jsHandler) | 671 http.HandleFunc("/js/", jsHandler) |
652 // TODO Break out /c/ as it's own handler. | 672 // TODO Break out /c/ as it's own handler. |
653 http.HandleFunc("/", mainHandler) | 673 http.HandleFunc("/", mainHandler) |
654 log.Fatal(http.ListenAndServe(*port, nil)) | 674 log.Fatal(http.ListenAndServe(*port, nil)) |
655 } | 675 } |
OLD | NEW |