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" |
11 _ "github.com/go-sql-driver/mysql" | 11 _ "github.com/go-sql-driver/mysql" |
12 _ "github.com/mattn/go-sqlite3" | 12 _ "github.com/mattn/go-sqlite3" |
13 htemplate "html/template" | 13 htemplate "html/template" |
14 "io/ioutil" | 14 "io/ioutil" |
15 "log" | 15 "log" |
16 "net/http" | 16 "net/http" |
17 "os" | 17 "os" |
18 "os/exec" | 18 "os/exec" |
19 "path/filepath" | 19 "path/filepath" |
20 "regexp" | 20 "regexp" |
21 "strings" | 21 "strings" |
22 "text/template" | 22 "text/template" |
23 "time" | |
23 ) | 24 ) |
24 | 25 |
25 const ( | 26 const ( |
26 RESULT_COMPILE = `c++ -DSK_GAMMA_SRGB -DSK_GAMMA_APPLY_TO_A8 -DSK_SCALAR _TO_FLOAT_EXCLUDED -DSK_ALLOW_STATIC_GLOBAL_INITIALIZERS=1 -DSK_SUPPORT_GPU=0 -D SK_SUPPORT_OPENCL=0 -DSK_FORCE_DISTANCEFIELD_FONTS=0 -DSK_SCALAR_IS_FLOAT -DSK_C AN_USE_FLOAT -DSK_SAMPLES_FOR_X -DSK_BUILD_FOR_UNIX -DSK_USE_POSIX_THREADS -DSK_ SYSTEM_ZLIB=1 -DSK_DEBUG -DSK_DEVELOPER=1 -I../../src/core -I../../src/images -I ../../tools/flags -I../../include/config -I../../include/core -I../../include/pa thops -I../../include/pipe -I../../include/effects -I../../include/ports -I../.. /src/sfnt -I../../include/utils -I../../src/utils -I../../include/images -g -fno -exceptions -fstrict-aliasing -Wall -Wextra -Winit-self -Wpointer-arith -Wno-unu sed-parameter -Wno-c++11-extensions -Werror -m64 -fno-rtti -Wnon-virtual-dtor -c ../../../cache/%s.cpp -o ../../../cache/%s.o` | 27 RESULT_COMPILE = `c++ -DSK_GAMMA_SRGB -DSK_GAMMA_APPLY_TO_A8 -DSK_SCALAR _TO_FLOAT_EXCLUDED -DSK_ALLOW_STATIC_GLOBAL_INITIALIZERS=1 -DSK_SUPPORT_GPU=0 -D SK_SUPPORT_OPENCL=0 -DSK_FORCE_DISTANCEFIELD_FONTS=0 -DSK_SCALAR_IS_FLOAT -DSK_C AN_USE_FLOAT -DSK_SAMPLES_FOR_X -DSK_BUILD_FOR_UNIX -DSK_USE_POSIX_THREADS -DSK_ SYSTEM_ZLIB=1 -DSK_DEBUG -DSK_DEVELOPER=1 -I../../src/core -I../../src/images -I ../../tools/flags -I../../include/config -I../../include/core -I../../include/pa thops -I../../include/pipe -I../../include/effects -I../../include/ports -I../.. /src/sfnt -I../../include/utils -I../../src/utils -I../../include/images -g -fno -exceptions -fstrict-aliasing -Wall -Wextra -Winit-self -Wpointer-arith -Wno-unu sed-parameter -Wno-c++11-extensions -Werror -m64 -fno-rtti -Wnon-virtual-dtor -c ../../../cache/%s.cpp -o ../../../cache/%s.o` |
27 LINK = `c++ -m64 -lstdc++ -lm -o ../../../inout/%s -Wl,--start -group ../../../cache/%s.o obj/experimental/webtry/webtry.main.o obj/gyp/libflag s.a libskia_images.a libskia_core.a libskia_effects.a obj/gyp/libjpeg.a obj/gyp/ libwebp_dec.a obj/gyp/libwebp_demux.a obj/gyp/libwebp_dsp.a obj/gyp/libwebp_enc. a obj/gyp/libwebp_utils.a libskia_utils.a libskia_opts.a libskia_opts_ssse3.a li bskia_ports.a libskia_sfnt.a -Wl,--end-group -lpng -lz -lgif -lpthread -lfontcon fig -ldl -lfreetype` | 28 LINK = `c++ -m64 -lstdc++ -lm -o ../../../inout/%s -Wl,--start -group ../../../cache/%s.o obj/experimental/webtry/webtry.main.o obj/gyp/libflag s.a libskia_images.a libskia_core.a libskia_effects.a obj/gyp/libjpeg.a obj/gyp/ libwebp_dec.a obj/gyp/libwebp_demux.a obj/gyp/libwebp_dsp.a obj/gyp/libwebp_enc. a obj/gyp/libwebp_utils.a libskia_utils.a libskia_opts.a libskia_opts_ssse3.a li bskia_ports.a libskia_sfnt.a -Wl,--end-group -lpng -lz -lgif -lpthread -lfontcon fig -ldl -lfreetype` |
28 DEFAULT_SAMPLE = `SkPaint p; | 29 DEFAULT_SAMPLE = `SkPaint p; |
29 p.setColor(SK_ColorRED); | 30 p.setColor(SK_ColorRED); |
30 p.setAntiAlias(true); | 31 p.setAntiAlias(true); |
31 p.setStyle(SkPaint::kStroke_Style); | 32 p.setStyle(SkPaint::kStroke_Style); |
32 p.setStrokeWidth(10); | 33 p.setStrokeWidth(10); |
33 | 34 |
34 canvas->drawLine(20, 20, 100, 100, p); | 35 canvas->drawLine(20, 20, 100, 100, p); |
35 ` | 36 ` |
36 ) | 37 ) |
37 | 38 |
38 var ( | 39 var ( |
39 // codeTemplate is the cpp code template the user's code is copied into. | 40 // codeTemplate is the cpp code template the user's code is copied into. |
40 codeTemplate *template.Template = nil | 41 codeTemplate *template.Template = nil |
41 | 42 |
42 » // index is the main index.html page we serve. | 43 » // indexTemplate is the main index.html page we serve. |
43 » index *htemplate.Template = nil | 44 » indexTemplate *htemplate.Template = nil |
45 | |
46 » // recentTemplate is a list of recent images. | |
47 » recentTemplate *htemplate.Template = nil | |
44 | 48 |
45 // db is the database, nil if we don't have an SQL database to store dat a into. | 49 // db is the database, nil if we don't have an SQL database to store dat a into. |
46 db *sql.DB = nil | 50 db *sql.DB = nil |
47 | 51 |
48 // directLink is the regex that matches URLs paths that are direct links . | 52 // directLink is the regex that matches URLs paths that are direct links . |
49 » directLink = regexp.MustCompile("^c/([a-a0-9]+)$") | 53 » directLink = regexp.MustCompile("^/c/([a-f0-9]+)$") |
54 | |
55 » // imageLink is the regex that matches URLs paths that are direct links to PNGs. | |
56 » imageLink = regexp.MustCompile("^/i/([a-f0-9]+.png)$") | |
50 ) | 57 ) |
51 | 58 |
52 // flags | 59 // flags |
53 var ( | 60 var ( |
54 useChroot = flag.Bool("use_chroot", false, "Run the compiled code in the schroot jail.") | 61 useChroot = flag.Bool("use_chroot", false, "Run the compiled code in the schroot jail.") |
55 port = flag.String("port", ":8000", "HTTP service address (e.g., ': 8000')") | 62 port = flag.String("port", ":8000", "HTTP service address (e.g., ': 8000')") |
56 ) | 63 ) |
57 | 64 |
58 // lineNumbers adds #line numbering to the user's code. | 65 // lineNumbers adds #line numbering to the user's code. |
59 func LineNumbers(c string) string { | 66 func LineNumbers(c string) string { |
(...skipping 14 matching lines...) Expand all Loading... | |
74 if err != nil { | 81 if err != nil { |
75 log.Fatal(err) | 82 log.Fatal(err) |
76 } | 83 } |
77 os.Chdir(cwd) | 84 os.Chdir(cwd) |
78 | 85 |
79 codeTemplate, err = template.ParseFiles(filepath.Join(cwd, "templates/te mplate.cpp")) | 86 codeTemplate, err = template.ParseFiles(filepath.Join(cwd, "templates/te mplate.cpp")) |
80 if err != nil { | 87 if err != nil { |
81 panic(err) | 88 panic(err) |
82 } | 89 } |
83 // Convert index.html into a template, which is expanded with the code. | 90 // Convert index.html into a template, which is expanded with the code. |
84 » index, err = htemplate.ParseFiles(filepath.Join(cwd, "templates/index.ht ml")) | 91 » indexTemplate, err = htemplate.ParseFiles(filepath.Join(cwd, "templates/ index.html")) |
85 if err != nil { | 92 if err != nil { |
86 panic(err) | 93 panic(err) |
87 } | 94 } |
95 | |
96 recentTemplate, err = htemplate.ParseFiles(filepath.Join(cwd, "templates /recent.html")) | |
97 if err != nil { | |
98 panic(err) | |
99 } | |
88 | 100 |
89 // Connect to MySQL server. First, get the password from the metadata se rver. | 101 // Connect to MySQL server. First, get the password from the metadata se rver. |
90 // See https://developers.google.com/compute/docs/metadata#custom. | 102 // See https://developers.google.com/compute/docs/metadata#custom. |
91 req, err := http.NewRequest("GET", "http://metadata/computeMetadata/v1/i nstance/attributes/password", nil) | 103 req, err := http.NewRequest("GET", "http://metadata/computeMetadata/v1/i nstance/attributes/password", nil) |
92 if err != nil { | 104 if err != nil { |
93 panic(err) | 105 panic(err) |
94 } | 106 } |
95 client := http.Client{} | 107 client := http.Client{} |
96 req.Header.Add("X-Google-Metadata-Request", "True") | 108 req.Header.Add("X-Google-Metadata-Request", "True") |
97 if resp, err := client.Do(req); err == nil { | 109 if resp, err := client.Do(req); err == nil { |
(...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
219 | 231 |
220 func writeToDatabase(hash string, code string) { | 232 func writeToDatabase(hash string, code string) { |
221 if db == nil { | 233 if db == nil { |
222 return | 234 return |
223 } | 235 } |
224 if _, err := db.Exec("INSERT INTO webtry (code, hash) VALUES(?, ?)", cod e, hash); err != nil { | 236 if _, err := db.Exec("INSERT INTO webtry (code, hash) VALUES(?, ?)", cod e, hash); err != nil { |
225 log.Printf("ERROR: Failed to insert code into database: %q\n", e rr) | 237 log.Printf("ERROR: Failed to insert code into database: %q\n", e rr) |
226 } | 238 } |
227 } | 239 } |
228 | 240 |
241 func cssHandler(w http.ResponseWriter, r *http.Request) { | |
242 http.ServeFile(w, r, "css/webtry.css") | |
243 } | |
244 | |
245 // imageHandler serves up the PNG of a specific try. | |
246 func imageHandler(w http.ResponseWriter, r *http.Request) { | |
247 log.Printf("Image Handler: %q\n", r.URL.Path) | |
248 if r.Method != "GET" { | |
249 http.NotFound(w, r) | |
250 return | |
251 } | |
252 match := imageLink.FindStringSubmatch(r.URL.Path) | |
253 if len(match) != 2 { | |
254 http.NotFound(w, r) | |
255 return | |
256 } | |
257 filename := match[1] | |
258 http.ServeFile(w, r, fmt.Sprintf("../../../inout/%s", filename)) | |
mtklein
2014/04/14 18:27:53
Doesn't this make you sort of wish the images were
jcgregorio
2014/04/14 18:43:46
Yeah yeah, I'm coming around to that idea :-)
| |
259 } | |
260 | |
261 type Try struct { | |
262 Hash string | |
263 CreateTS string | |
264 } | |
265 | |
266 type Recent struct { | |
267 Tries []Try | |
268 } | |
269 | |
270 // recentHandler shows the last 20 tries. | |
271 func recentHandler(w http.ResponseWriter, r *http.Request) { | |
272 log.Printf("Recent Handler: %q\n", r.URL.Path) | |
273 | |
274 var err error | |
275 rows, err := db.Query("SELECT create_ts, hash FROM webtry ORDER BY creat e_ts DESC LIMIT 20") | |
276 if err != nil { | |
277 http.NotFound(w, r) | |
278 return | |
279 } | |
280 recent := []Try{} | |
281 for rows.Next() { | |
282 var hash string | |
283 var create_ts time.Time | |
284 if err := rows.Scan(&create_ts, &hash); err != nil { | |
285 log.Printf("Error: failed to fetch from database: %q", e rr) | |
286 continue | |
287 } | |
288 recent = append(recent, Try{Hash: hash, CreateTS: create_ts.Form at("2006-02-01")}) | |
mtklein
2014/04/14 18:27:53
I've never seen this .Format method before. Cute!
jcgregorio
2014/04/14 18:43:46
Yeah, makes for very readable code.
On 2014/04/14
| |
289 } | |
290 if err := recentTemplate.Execute(w, Recent{Tries: recent}); err != nil { | |
291 log.Printf("ERROR: Failed to expand template: %q\n", err) | |
292 } | |
293 } | |
294 | |
229 // mainHandler handles the GET and POST of the main page. | 295 // mainHandler handles the GET and POST of the main page. |
230 func mainHandler(w http.ResponseWriter, r *http.Request) { | 296 func mainHandler(w http.ResponseWriter, r *http.Request) { |
297 log.Printf("Main Handler: %q\n", r.URL.Path) | |
mtklein
2014/04/14 18:27:53
Leftover?
jcgregorio
2014/04/14 18:43:46
Actually useful to have in the logs.
On 2014/04/1
| |
231 if r.Method == "GET" { | 298 if r.Method == "GET" { |
232 code := DEFAULT_SAMPLE | 299 code := DEFAULT_SAMPLE |
233 directLink := regexp.MustCompile("^/c/([a-f0-9]+)$") | |
234 match := directLink.FindStringSubmatch(r.URL.Path) | 300 match := directLink.FindStringSubmatch(r.URL.Path) |
235 » » if len(match) == 2 { | 301 » » if len(match) == 2 && r.URL.Path != "/" { |
236 hash := match[1] | 302 hash := match[1] |
237 if db == nil { | 303 if db == nil { |
238 http.NotFound(w, r) | 304 http.NotFound(w, r) |
239 return | 305 return |
240 } | 306 } |
241 // Update 'code' with the code found in the database. | 307 // Update 'code' with the code found in the database. |
242 if err := db.QueryRow("SELECT code FROM webtry WHERE has h=?", hash).Scan(&code); err != nil { | 308 if err := db.QueryRow("SELECT code FROM webtry WHERE has h=?", hash).Scan(&code); err != nil { |
243 http.NotFound(w, r) | 309 http.NotFound(w, r) |
244 return | 310 return |
245 } | 311 } |
246 } | 312 } |
247 // Expand the template. | 313 // Expand the template. |
248 » » if err := index.Execute(w, userCode{UserCode: code}); err != nil { | 314 » » if err := indexTemplate.Execute(w, userCode{UserCode: code}); er r != nil { |
249 log.Printf("ERROR: Failed to expand template: %q\n", err ) | 315 log.Printf("ERROR: Failed to expand template: %q\n", err ) |
250 } | 316 } |
251 } else if r.Method == "POST" { | 317 } else if r.Method == "POST" { |
252 w.Header().Set("Content-Type", "application/json") | 318 w.Header().Set("Content-Type", "application/json") |
253 b, err := ioutil.ReadAll(r.Body) | 319 b, err := ioutil.ReadAll(r.Body) |
254 if err != nil { | 320 if err != nil { |
255 reportError(w, r, err, "Failed to read a request body.") | 321 reportError(w, r, err, "Failed to read a request body.") |
256 return | 322 return |
257 } | 323 } |
258 code := string(b) | 324 code := string(b) |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
305 if err != nil { | 371 if err != nil { |
306 reportError(w, r, err, "Failed to serialize a response." ) | 372 reportError(w, r, err, "Failed to serialize a response." ) |
307 return | 373 return |
308 } | 374 } |
309 w.Write(resp) | 375 w.Write(resp) |
310 } | 376 } |
311 } | 377 } |
312 | 378 |
313 func main() { | 379 func main() { |
314 flag.Parse() | 380 flag.Parse() |
315 | 381 » http.HandleFunc("/i/", imageHandler) |
382 » http.HandleFunc("/recent/", recentHandler) | |
383 » http.HandleFunc("/css/", cssHandler) | |
316 http.HandleFunc("/", mainHandler) | 384 http.HandleFunc("/", mainHandler) |
317 log.Fatal(http.ListenAndServe(*port, nil)) | 385 log.Fatal(http.ListenAndServe(*port, nil)) |
318 } | 386 } |
OLD | NEW |