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" | |
19 "math/rand" | 18 "math/rand" |
20 "net" | 19 "net" |
21 "net/http" | 20 "net/http" |
22 "os" | 21 "os" |
23 "os/exec" | 22 "os/exec" |
24 "path/filepath" | 23 "path/filepath" |
25 "regexp" | 24 "regexp" |
26 "strings" | 25 "strings" |
27 "text/template" | 26 "text/template" |
28 "time" | 27 "time" |
29 ) | 28 ) |
30 | 29 |
31 import ( | 30 import ( |
32 "github.com/fiorix/go-web/autogzip" | 31 "github.com/fiorix/go-web/autogzip" |
33 _ "github.com/go-sql-driver/mysql" | 32 _ "github.com/go-sql-driver/mysql" |
| 33 "github.com/golang/glog" |
34 _ "github.com/mattn/go-sqlite3" | 34 _ "github.com/mattn/go-sqlite3" |
35 "github.com/rcrowley/go-metrics" | 35 "github.com/rcrowley/go-metrics" |
36 ) | 36 ) |
37 | 37 |
38 const ( | 38 const ( |
39 DEFAULT_SAMPLE = `void draw(SkCanvas* canvas) { | 39 DEFAULT_SAMPLE = `void draw(SkCanvas* canvas) { |
40 SkPaint p; | 40 SkPaint p; |
41 p.setColor(SK_ColorRED); | 41 p.setColor(SK_ColorRED); |
42 p.setAntiAlias(true); | 42 p.setAntiAlias(true); |
43 p.setStyle(SkPaint::kStroke_Style); | 43 p.setStyle(SkPaint::kStroke_Style); |
(...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
135 return strings.Join(ret, "\n") | 135 return strings.Join(ret, "\n") |
136 } | 136 } |
137 | 137 |
138 func init() { | 138 func init() { |
139 rand.Seed(time.Now().UnixNano()) | 139 rand.Seed(time.Now().UnixNano()) |
140 | 140 |
141 // Change the current working directory to the directory of the executab
le. | 141 // Change the current working directory to the directory of the executab
le. |
142 var err error | 142 var err error |
143 cwd, err := filepath.Abs(filepath.Dir(os.Args[0])) | 143 cwd, err := filepath.Abs(filepath.Dir(os.Args[0])) |
144 if err != nil { | 144 if err != nil { |
145 » » log.Fatal(err) | 145 » » glog.Fatal(err) |
146 } | 146 } |
147 os.Chdir(cwd) | 147 os.Chdir(cwd) |
148 | 148 |
149 codeTemplate = template.Must(template.ParseFiles(filepath.Join(cwd, "tem
plates/template.cpp"))) | 149 codeTemplate = template.Must(template.ParseFiles(filepath.Join(cwd, "tem
plates/template.cpp"))) |
150 gypTemplate = template.Must(template.ParseFiles(filepath.Join(cwd, "temp
lates/template.gyp"))) | 150 gypTemplate = template.Must(template.ParseFiles(filepath.Join(cwd, "temp
lates/template.gyp"))) |
151 indexTemplate = htemplate.Must(htemplate.ParseFiles( | 151 indexTemplate = htemplate.Must(htemplate.ParseFiles( |
152 filepath.Join(cwd, "templates/index.html"), | 152 filepath.Join(cwd, "templates/index.html"), |
153 filepath.Join(cwd, "templates/titlebar.html"), | 153 filepath.Join(cwd, "templates/titlebar.html"), |
154 filepath.Join(cwd, "templates/sidebar.html"), | 154 filepath.Join(cwd, "templates/sidebar.html"), |
155 filepath.Join(cwd, "templates/content.html"), | 155 filepath.Join(cwd, "templates/content.html"), |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
194 // See https://developers.google.com/compute/docs/metadata#custom. | 194 // See https://developers.google.com/compute/docs/metadata#custom. |
195 req, err := http.NewRequest("GET", "http://metadata/computeMetadata/v1/i
nstance/attributes/password", nil) | 195 req, err := http.NewRequest("GET", "http://metadata/computeMetadata/v1/i
nstance/attributes/password", nil) |
196 if err != nil { | 196 if err != nil { |
197 panic(err) | 197 panic(err) |
198 } | 198 } |
199 client := http.Client{} | 199 client := http.Client{} |
200 req.Header.Add("X-Google-Metadata-Request", "True") | 200 req.Header.Add("X-Google-Metadata-Request", "True") |
201 if resp, err := client.Do(req); err == nil { | 201 if resp, err := client.Do(req); err == nil { |
202 password, err := ioutil.ReadAll(resp.Body) | 202 password, err := ioutil.ReadAll(resp.Body) |
203 if err != nil { | 203 if err != nil { |
204 » » » log.Printf("ERROR: Failed to read password from metadata
server: %q\n", err) | 204 » » » glog.Errorf("Failed to read password from metadata serve
r: %q\n", err) |
205 panic(err) | 205 panic(err) |
206 } | 206 } |
207 // The IP address of the database is found here: | 207 // The IP address of the database is found here: |
208 // https://console.developers.google.com/project/31977622648/
sql/instances/webtry/overview | 208 // https://console.developers.google.com/project/31977622648/
sql/instances/webtry/overview |
209 // And 3306 is the default port for MySQL. | 209 // And 3306 is the default port for MySQL. |
210 db, err = sql.Open("mysql", fmt.Sprintf("webtry:%s@tcp(173.194.8
3.52:3306)/webtry?parseTime=true", password)) | 210 db, err = sql.Open("mysql", fmt.Sprintf("webtry:%s@tcp(173.194.8
3.52:3306)/webtry?parseTime=true", password)) |
211 if err != nil { | 211 if err != nil { |
212 » » » log.Printf("ERROR: Failed to open connection to SQL serv
er: %q\n", err) | 212 » » » glog.Errorf("ERROR: Failed to open connection to SQL ser
ver: %q\n", err) |
213 panic(err) | 213 panic(err) |
214 } | 214 } |
215 } else { | 215 } else { |
216 » » log.Printf("INFO: Failed to find metadata, unable to connect to
MySQL server (Expected when running locally): %q\n", err) | 216 » » glog.Infof("Failed to find metadata, unable to connect to MySQL
server (Expected when running locally): %q\n", err) |
217 // Fallback to sqlite for local use. | 217 // Fallback to sqlite for local use. |
218 db, err = sql.Open("sqlite3", "./webtry.db") | 218 db, err = sql.Open("sqlite3", "./webtry.db") |
219 if err != nil { | 219 if err != nil { |
220 » » » log.Printf("ERROR: Failed to open: %q\n", err) | 220 » » » glog.Errorf("Failed to open: %q\n", err) |
221 panic(err) | 221 panic(err) |
222 } | 222 } |
223 sql := `CREATE TABLE IF NOT EXISTS source_images ( | 223 sql := `CREATE TABLE IF NOT EXISTS source_images ( |
224 id INTEGER PRIMARY KEY NOT NULL, | 224 id INTEGER PRIMARY KEY NOT NULL, |
225 image MEDIUMBLOB DEFAULT '' NOT NULL, -- forma
tted as a PNG. | 225 image MEDIUMBLOB DEFAULT '' NOT NULL, -- forma
tted as a PNG. |
226 width INTEGER DEFAULT 0 NOT NULL, | 226 width INTEGER DEFAULT 0 NOT NULL, |
227 height INTEGER DEFAULT 0 NOT NULL, | 227 height INTEGER DEFAULT 0 NOT NULL, |
228 create_ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, | 228 create_ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, |
229 hidden INTEGER DEFAULT 0 NOT NULL | 229 hidden INTEGER DEFAULT 0 NOT NULL |
230 )` | 230 )` |
231 _, err = db.Exec(sql) | 231 _, err = db.Exec(sql) |
232 if err != nil { | 232 if err != nil { |
233 » » » log.Printf("Info: status creating sqlite table for sourc
es: %q\n", err) | 233 » » » glog.Infof("status creating sqlite table for sources: %q
\n", err) |
234 } | 234 } |
235 | 235 |
236 sql = `CREATE TABLE IF NOT EXISTS webtry ( | 236 sql = `CREATE TABLE IF NOT EXISTS webtry ( |
237 code TEXT DEFAULT '' NOT NULL, | 237 code TEXT DEFAULT '' NOT NULL, |
238 create_ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, | 238 create_ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, |
239 hash CHAR(64) DEFAULT '' NOT NULL, | 239 hash CHAR(64) DEFAULT '' NOT NULL, |
240 width INTEGER DEFAULT 256 NOT NULL, | 240 width INTEGER DEFAULT 256 NOT NULL, |
241 height INTEGER DEFAULT 256 NOT NULL, | 241 height INTEGER DEFAULT 256 NOT NULL, |
242 gpu BOOL DEFAULT 0 NOT NULL, | 242 gpu BOOL DEFAULT 0 NOT NULL, |
243 source_image_id INTEGER DEFAULT 0 NOT NULL, | 243 source_image_id INTEGER DEFAULT 0 NOT NULL, |
244 | 244 |
245 PRIMARY KEY(hash) | 245 PRIMARY KEY(hash) |
246 )` | 246 )` |
247 _, err = db.Exec(sql) | 247 _, err = db.Exec(sql) |
248 if err != nil { | 248 if err != nil { |
249 » » » log.Printf("Info: status creating sqlite table for webtr
y: %q\n", err) | 249 » » » glog.Infof("status creating sqlite table for webtry: %q\
n", err) |
250 } | 250 } |
251 | 251 |
252 sql = `CREATE TABLE IF NOT EXISTS workspace ( | 252 sql = `CREATE TABLE IF NOT EXISTS workspace ( |
253 name CHAR(64) DEFAULT '' NOT NULL, | 253 name CHAR(64) DEFAULT '' NOT NULL, |
254 create_ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, | 254 create_ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, |
255 PRIMARY KEY(name) | 255 PRIMARY KEY(name) |
256 )` | 256 )` |
257 _, err = db.Exec(sql) | 257 _, err = db.Exec(sql) |
258 if err != nil { | 258 if err != nil { |
259 » » » log.Printf("Info: status creating sqlite table for works
pace: %q\n", err) | 259 » » » glog.Infof("status creating sqlite table for workspace:
%q\n", err) |
260 } | 260 } |
261 | 261 |
262 sql = `CREATE TABLE IF NOT EXISTS workspacetry ( | 262 sql = `CREATE TABLE IF NOT EXISTS workspacetry ( |
263 name CHAR(64) DEFAULT '' NOT NULL, | 263 name CHAR(64) DEFAULT '' NOT NULL, |
264 create_ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, | 264 create_ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, |
265 hash CHAR(64) DEFAULT '' NOT NULL, | 265 hash CHAR(64) DEFAULT '' NOT NULL, |
266 width INTEGER DEFAULT 256 NOT NULL, | 266 width INTEGER DEFAULT 256 NOT NULL, |
267 height INTEGER DEFAULT 256 NOT NULL, | 267 height INTEGER DEFAULT 256 NOT NULL, |
268 gpu BOOL DEFAULT 0 NOT NULL, | 268 gpu BOOL DEFAULT 0 NOT NULL, |
269 hidden INTEGER DEFAULT 0 NOT NULL, | 269 hidden INTEGER DEFAULT 0 NOT NULL, |
270 source_image_id INTEGER DEFAULT 0 NOT NULL, | 270 source_image_id INTEGER DEFAULT 0 NOT NULL, |
271 | 271 |
272 FOREIGN KEY (name) REFERENCES workspace(name) | 272 FOREIGN KEY (name) REFERENCES workspace(name) |
273 )` | 273 )` |
274 _, err = db.Exec(sql) | 274 _, err = db.Exec(sql) |
275 if err != nil { | 275 if err != nil { |
276 » » » log.Printf("Info: status creating sqlite table for works
pace try: %q\n", err) | 276 » » » glog.Infof("status creating sqlite table for workspace t
ry: %q\n", err) |
277 } | 277 } |
278 } | 278 } |
279 | 279 |
280 // Ping the database to keep the connection fresh. | 280 // Ping the database to keep the connection fresh. |
281 go func() { | 281 go func() { |
282 c := time.Tick(1 * time.Minute) | 282 c := time.Tick(1 * time.Minute) |
283 for _ = range c { | 283 for _ = range c { |
284 if err := db.Ping(); err != nil { | 284 if err := db.Ping(); err != nil { |
285 » » » » log.Printf("ERROR: Database failed to respond: %
q\n", err) | 285 » » » » glog.Errorf("Database failed to respond: %q\n",
err) |
286 } | 286 } |
287 } | 287 } |
288 }() | 288 }() |
289 | 289 |
290 metrics.RegisterRuntimeMemStats(metrics.DefaultRegistry) | 290 metrics.RegisterRuntimeMemStats(metrics.DefaultRegistry) |
291 go metrics.CaptureRuntimeMemStats(metrics.DefaultRegistry, 1*time.Minute
) | 291 go metrics.CaptureRuntimeMemStats(metrics.DefaultRegistry, 1*time.Minute
) |
292 | 292 |
293 // Start reporting metrics. | 293 // Start reporting metrics. |
294 // TODO(jcgregorio) We need a centrialized config server for storing thi
ngs | 294 // TODO(jcgregorio) We need a centrialized config server for storing thi
ngs |
295 // like the IP address of the Graphite monitor. | 295 // like the IP address of the Graphite monitor. |
296 addr, _ := net.ResolveTCPAddr("tcp", "skia-monitoring-b:2003") | 296 addr, _ := net.ResolveTCPAddr("tcp", "skia-monitoring-b:2003") |
297 go metrics.Graphite(metrics.DefaultRegistry, 1*time.Minute, "webtry", ad
dr) | 297 go metrics.Graphite(metrics.DefaultRegistry, 1*time.Minute, "webtry", ad
dr) |
298 | 298 |
299 writeOutAllSourceImages() | 299 writeOutAllSourceImages() |
300 } | 300 } |
301 | 301 |
302 func writeOutAllSourceImages() { | 302 func writeOutAllSourceImages() { |
303 // Pull all the source images from the db and write them out to inout. | 303 // Pull all the source images from the db and write them out to inout. |
304 rows, err := db.Query("SELECT id, image, create_ts FROM source_images OR
DER BY create_ts DESC") | 304 rows, err := db.Query("SELECT id, image, create_ts FROM source_images OR
DER BY create_ts DESC") |
305 | 305 |
306 if err != nil { | 306 if err != nil { |
307 » » log.Printf("ERROR: Failed to open connection to SQL server: %q\n
", err) | 307 » » glog.Errorf("Failed to open connection to SQL server: %q\n", err
) |
308 panic(err) | 308 panic(err) |
309 } | 309 } |
310 for rows.Next() { | 310 for rows.Next() { |
311 var id int | 311 var id int |
312 var image []byte | 312 var image []byte |
313 var create_ts time.Time | 313 var create_ts time.Time |
314 if err := rows.Scan(&id, &image, &create_ts); err != nil { | 314 if err := rows.Scan(&id, &image, &create_ts); err != nil { |
315 » » » log.Printf("Error: failed to fetch from database: %q", e
rr) | 315 » » » glog.Errorf("failed to fetch from database: %q", err) |
316 continue | 316 continue |
317 } | 317 } |
318 filename := fmt.Sprintf("../../../inout/image-%d.png", id) | 318 filename := fmt.Sprintf("../../../inout/image-%d.png", id) |
319 if _, err := os.Stat(filename); os.IsExist(err) { | 319 if _, err := os.Stat(filename); os.IsExist(err) { |
320 » » » log.Printf("Skipping write since file exists: %q", filen
ame) | 320 » » » glog.Infof("Skipping write since file exists: %q", filen
ame) |
321 continue | 321 continue |
322 } | 322 } |
323 if err := ioutil.WriteFile(filename, image, 0666); err != nil { | 323 if err := ioutil.WriteFile(filename, image, 0666); err != nil { |
324 » » » log.Printf("Error: failed to write image file: %q", err) | 324 » » » glog.Errorf("failed to write image file: %q", err) |
325 } | 325 } |
326 } | 326 } |
327 } | 327 } |
328 | 328 |
329 // Titlebar is used in titlebar template expansion. | 329 // Titlebar is used in titlebar template expansion. |
330 type Titlebar struct { | 330 type Titlebar struct { |
331 GitHash string | 331 GitHash string |
332 GitInfo string | 332 GitInfo string |
333 } | 333 } |
334 | 334 |
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
407 Message string `json:"message"` | 407 Message string `json:"message"` |
408 StdOut string `json:"stdout"` | 408 StdOut string `json:"stdout"` |
409 Img string `json:"img"` | 409 Img string `json:"img"` |
410 Hash string `json:"hash"` | 410 Hash string `json:"hash"` |
411 } | 411 } |
412 | 412 |
413 // doCmd executes the given command line string; the command being | 413 // doCmd executes the given command line string; the command being |
414 // run is expected to not care what its current working directory is. | 414 // run is expected to not care what its current working directory is. |
415 // Returns the stdout and stderr. | 415 // Returns the stdout and stderr. |
416 func doCmd(commandLine string) (string, error) { | 416 func doCmd(commandLine string) (string, error) { |
417 » log.Printf("Command: %q\n", commandLine) | 417 » glog.Infof("Command: %q\n", commandLine) |
418 programAndArgs := strings.SplitN(commandLine, " ", 2) | 418 programAndArgs := strings.SplitN(commandLine, " ", 2) |
419 program := programAndArgs[0] | 419 program := programAndArgs[0] |
420 args := []string{} | 420 args := []string{} |
421 if len(programAndArgs) > 1 { | 421 if len(programAndArgs) > 1 { |
422 args = strings.Split(programAndArgs[1], " ") | 422 args = strings.Split(programAndArgs[1], " ") |
423 } | 423 } |
424 cmd := exec.Command(program, args...) | 424 cmd := exec.Command(program, args...) |
425 message, err := cmd.CombinedOutput() | 425 message, err := cmd.CombinedOutput() |
426 » log.Printf("StdOut + StdErr: %s\n", string(message)) | 426 » glog.Infof("StdOut + StdErr: %s\n", string(message)) |
427 if err != nil { | 427 if err != nil { |
428 » » log.Printf("Exit status: %s\n", err.Error()) | 428 » » glog.Errorf("Exit status: %s\n", err) |
429 return string(message), fmt.Errorf("Failed to run command.") | 429 return string(message), fmt.Errorf("Failed to run command.") |
430 } | 430 } |
431 return string(message), nil | 431 return string(message), nil |
432 } | 432 } |
433 | 433 |
434 // reportError formats an HTTP error response and also logs the detailed error m
essage. | 434 // reportError formats an HTTP error response and also logs the detailed error m
essage. |
435 func reportError(w http.ResponseWriter, r *http.Request, err error, message stri
ng) { | 435 func reportError(w http.ResponseWriter, r *http.Request, err error, message stri
ng) { |
436 » log.Printf("Error: %s\n%s", message, err.Error()) | 436 » glog.Errorf("%s\n%s", message, err) |
437 w.Header().Set("Content-Type", "text/plain") | 437 w.Header().Set("Content-Type", "text/plain") |
438 http.Error(w, message, 500) | 438 http.Error(w, message, 500) |
439 } | 439 } |
440 | 440 |
441 // reportTryError formats an HTTP error response in JSON and also logs the detai
led error message. | 441 // 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) { | 442 func reportTryError(w http.ResponseWriter, r *http.Request, err error, message,
hash string) { |
443 m := response{ | 443 m := response{ |
444 Message: message, | 444 Message: message, |
445 Hash: hash, | 445 Hash: hash, |
446 } | 446 } |
447 » log.Printf("Error: %s\n%s", message, err.Error()) | 447 » glog.Errorf("%s\n%s", message, err) |
448 resp, err := json.Marshal(m) | 448 resp, err := json.Marshal(m) |
449 if err != nil { | 449 if err != nil { |
450 http.Error(w, "Failed to serialize a response", 500) | 450 http.Error(w, "Failed to serialize a response", 500) |
451 return | 451 return |
452 } | 452 } |
453 w.Header().Set("Content-Type", "text/plain") | 453 w.Header().Set("Content-Type", "text/plain") |
454 w.Write(resp) | 454 w.Write(resp) |
455 } | 455 } |
456 | 456 |
457 func writeToDatabase(hash string, code string, workspaceName string, source int,
width, height int, gpu bool) { | 457 func writeToDatabase(hash string, code string, workspaceName string, source int,
width, height int, gpu bool) { |
458 if db == nil { | 458 if db == nil { |
459 return | 459 return |
460 } | 460 } |
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 { | 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 { |
462 » » log.Printf("ERROR: Failed to insert code into database: %q\n", e
rr) | 462 » » glog.Errorf("Failed to insert code into database: %q\n", err) |
463 } | 463 } |
464 if workspaceName != "" { | 464 if workspaceName != "" { |
465 if _, err := db.Exec("INSERT INTO workspacetry (name, hash, widt
h, height, gpu, source_image_id) VALUES(?, ?, ?, ?, ?, ?)", workspaceName, hash,
width, height, gpu, source); err != nil { | 465 if _, err := db.Exec("INSERT INTO workspacetry (name, hash, widt
h, height, gpu, source_image_id) VALUES(?, ?, ?, ?, ?, ?)", workspaceName, hash,
width, height, gpu, source); err != nil { |
466 » » » log.Printf("ERROR: Failed to insert into workspacetry ta
ble: %q\n", err) | 466 » » » glog.Errorf("Failed to insert into workspacetry table: %
q\n", err) |
467 } | 467 } |
468 } | 468 } |
469 } | 469 } |
470 | 470 |
471 type Sources struct { | 471 type Sources struct { |
472 Id int `json:"id"` | 472 Id int `json:"id"` |
473 } | 473 } |
474 | 474 |
475 // sourcesHandler serves up the PNG of a specific try. | 475 // sourcesHandler serves up the PNG of a specific try. |
476 func sourcesHandler(w http.ResponseWriter, r *http.Request) { | 476 func sourcesHandler(w http.ResponseWriter, r *http.Request) { |
477 » log.Printf("Sources Handler: %q\n", r.URL.Path) | 477 » glog.Infof("Sources Handler: %q\n", r.URL.Path) |
478 if r.Method == "GET" { | 478 if r.Method == "GET" { |
479 rows, err := db.Query("SELECT id, create_ts FROM source_images W
HERE hidden=0 ORDER BY create_ts DESC") | 479 rows, err := db.Query("SELECT id, create_ts FROM source_images W
HERE hidden=0 ORDER BY create_ts DESC") |
480 | 480 |
481 if err != nil { | 481 if err != nil { |
482 http.Error(w, fmt.Sprintf("Failed to query sources: %s."
, err), 500) | 482 http.Error(w, fmt.Sprintf("Failed to query sources: %s."
, err), 500) |
483 } | 483 } |
484 sources := make([]Sources, 0, 0) | 484 sources := make([]Sources, 0, 0) |
485 for rows.Next() { | 485 for rows.Next() { |
486 var id int | 486 var id int |
487 var create_ts time.Time | 487 var create_ts time.Time |
488 if err := rows.Scan(&id, &create_ts); err != nil { | 488 if err := rows.Scan(&id, &create_ts); err != nil { |
489 » » » » log.Printf("Error: failed to fetch from database
: %q", err) | 489 » » » » glog.Errorf("failed to fetch from database: %q",
err) |
490 continue | 490 continue |
491 } | 491 } |
492 sources = append(sources, Sources{Id: id}) | 492 sources = append(sources, Sources{Id: id}) |
493 } | 493 } |
494 | 494 |
495 resp, err := json.Marshal(sources) | 495 resp, err := json.Marshal(sources) |
496 if err != nil { | 496 if err != nil { |
497 reportError(w, r, err, "Failed to serialize a response."
) | 497 reportError(w, r, err, "Failed to serialize a response."
) |
498 return | 498 return |
499 } | 499 } |
(...skipping 23 matching lines...) Expand all Loading... |
523 if err != nil { | 523 if err != nil { |
524 http.Error(w, fmt.Sprintf("Failed to decode image: %s.",
err), 500) | 524 http.Error(w, fmt.Sprintf("Failed to decode image: %s.",
err), 500) |
525 return | 525 return |
526 } | 526 } |
527 var b bytes.Buffer | 527 var b bytes.Buffer |
528 png.Encode(&b, m) | 528 png.Encode(&b, m) |
529 bounds := m.Bounds() | 529 bounds := m.Bounds() |
530 width := bounds.Max.Y - bounds.Min.Y | 530 width := bounds.Max.Y - bounds.Min.Y |
531 height := bounds.Max.X - bounds.Min.X | 531 height := bounds.Max.X - bounds.Min.X |
532 if _, err := db.Exec("INSERT INTO source_images (image, width, h
eight) VALUES(?, ?, ?)", b.Bytes(), width, height); err != nil { | 532 if _, err := db.Exec("INSERT INTO source_images (image, width, h
eight) VALUES(?, ?, ?)", b.Bytes(), width, height); err != nil { |
533 » » » log.Printf("ERROR: Failed to insert sources into databas
e: %q\n", err) | 533 » » » glog.Errorf("Failed to insert sources into database: %q\
n", err) |
534 http.Error(w, fmt.Sprintf("Failed to store image: %s.",
err), 500) | 534 http.Error(w, fmt.Sprintf("Failed to store image: %s.",
err), 500) |
535 return | 535 return |
536 } | 536 } |
537 go writeOutAllSourceImages() | 537 go writeOutAllSourceImages() |
538 | 538 |
539 // Now redirect back to where we came from. | 539 // Now redirect back to where we came from. |
540 http.Redirect(w, r, r.Referer(), 302) | 540 http.Redirect(w, r, r.Referer(), 302) |
541 } else { | 541 } else { |
542 http.NotFound(w, r) | 542 http.NotFound(w, r) |
543 return | 543 return |
544 } | 544 } |
545 } | 545 } |
546 | 546 |
547 // imageHandler serves up the PNG of a specific try. | 547 // imageHandler serves up the PNG of a specific try. |
548 func imageHandler(w http.ResponseWriter, r *http.Request) { | 548 func imageHandler(w http.ResponseWriter, r *http.Request) { |
549 » log.Printf("Image Handler: %q\n", r.URL.Path) | 549 » glog.Infof("Image Handler: %q\n", r.URL.Path) |
550 if r.Method != "GET" { | 550 if r.Method != "GET" { |
551 http.NotFound(w, r) | 551 http.NotFound(w, r) |
552 return | 552 return |
553 } | 553 } |
554 match := imageLink.FindStringSubmatch(r.URL.Path) | 554 match := imageLink.FindStringSubmatch(r.URL.Path) |
555 if len(match) != 2 { | 555 if len(match) != 2 { |
556 http.NotFound(w, r) | 556 http.NotFound(w, r) |
557 return | 557 return |
558 } | 558 } |
559 filename := match[1] | 559 filename := match[1] |
560 w.Header().Set("Content-Type", "image/png") | 560 w.Header().Set("Content-Type", "image/png") |
561 http.ServeFile(w, r, fmt.Sprintf("../../../inout/%s", filename)) | 561 http.ServeFile(w, r, fmt.Sprintf("../../../inout/%s", filename)) |
562 } | 562 } |
563 | 563 |
564 type Try struct { | 564 type Try struct { |
565 Hash string `json:"hash"` | 565 Hash string `json:"hash"` |
566 Source int | 566 Source int |
567 CreateTS string `json:"create_ts"` | 567 CreateTS string `json:"create_ts"` |
568 } | 568 } |
569 | 569 |
570 type Recent struct { | 570 type Recent struct { |
571 Tries []Try | 571 Tries []Try |
572 Titlebar Titlebar | 572 Titlebar Titlebar |
573 } | 573 } |
574 | 574 |
575 // recentHandler shows the last 20 tries. | 575 // recentHandler shows the last 20 tries. |
576 func recentHandler(w http.ResponseWriter, r *http.Request) { | 576 func recentHandler(w http.ResponseWriter, r *http.Request) { |
577 » log.Printf("Recent Handler: %q\n", r.URL.Path) | 577 » glog.Infof("Recent Handler: %q\n", r.URL.Path) |
578 | 578 |
579 var err error | 579 var err error |
580 rows, err := db.Query("SELECT create_ts, hash FROM webtry ORDER BY creat
e_ts DESC LIMIT 20") | 580 rows, err := db.Query("SELECT create_ts, hash FROM webtry ORDER BY creat
e_ts DESC LIMIT 20") |
581 if err != nil { | 581 if err != nil { |
582 http.NotFound(w, r) | 582 http.NotFound(w, r) |
583 return | 583 return |
584 } | 584 } |
585 recent := []Try{} | 585 recent := []Try{} |
586 for rows.Next() { | 586 for rows.Next() { |
587 var hash string | 587 var hash string |
588 var create_ts time.Time | 588 var create_ts time.Time |
589 if err := rows.Scan(&create_ts, &hash); err != nil { | 589 if err := rows.Scan(&create_ts, &hash); err != nil { |
590 » » » log.Printf("Error: failed to fetch from database: %q", e
rr) | 590 » » » glog.Errorf("failed to fetch from database: %q", err) |
591 continue | 591 continue |
592 } | 592 } |
593 recent = append(recent, Try{Hash: hash, CreateTS: create_ts.Form
at("2006-02-01")}) | 593 recent = append(recent, Try{Hash: hash, CreateTS: create_ts.Form
at("2006-02-01")}) |
594 } | 594 } |
595 w.Header().Set("Content-Type", "text/html") | 595 w.Header().Set("Content-Type", "text/html") |
596 if err := recentTemplate.Execute(w, Recent{Tries: recent, Titlebar: Titl
ebar{GitHash: gitHash, GitInfo: gitInfo}}); err != nil { | 596 if err := recentTemplate.Execute(w, Recent{Tries: recent, Titlebar: Titl
ebar{GitHash: gitHash, GitInfo: gitInfo}}); err != nil { |
597 » » log.Printf("ERROR: Failed to expand template: %q\n", err) | 597 » » glog.Errorf("Failed to expand template: %q\n", err) |
598 } | 598 } |
599 } | 599 } |
600 | 600 |
601 type Workspace struct { | 601 type Workspace struct { |
602 Name string | 602 Name string |
603 Code string | 603 Code string |
604 Hash string | 604 Hash string |
605 Width int | 605 Width int |
606 Height int | 606 Height int |
607 Source int | 607 Source int |
608 GPU bool | 608 GPU bool |
609 Tries []Try | 609 Tries []Try |
610 Titlebar Titlebar | 610 Titlebar Titlebar |
611 } | 611 } |
612 | 612 |
613 // newWorkspace generates a new random workspace name and stores it in the datab
ase. | 613 // newWorkspace generates a new random workspace name and stores it in the datab
ase. |
614 func newWorkspace() (string, error) { | 614 func newWorkspace() (string, error) { |
615 for i := 0; i < 10; i++ { | 615 for i := 0; i < 10; i++ { |
616 adj := workspaceNameAdj[rand.Intn(len(workspaceNameAdj))] | 616 adj := workspaceNameAdj[rand.Intn(len(workspaceNameAdj))] |
617 noun := workspaceNameNoun[rand.Intn(len(workspaceNameNoun))] | 617 noun := workspaceNameNoun[rand.Intn(len(workspaceNameNoun))] |
618 suffix := rand.Intn(1000) | 618 suffix := rand.Intn(1000) |
619 name := fmt.Sprintf("%s-%s-%d", adj, noun, suffix) | 619 name := fmt.Sprintf("%s-%s-%d", adj, noun, suffix) |
620 if _, err := db.Exec("INSERT INTO workspace (name) VALUES(?)", n
ame); err == nil { | 620 if _, err := db.Exec("INSERT INTO workspace (name) VALUES(?)", n
ame); err == nil { |
621 return name, nil | 621 return name, nil |
622 } else { | 622 } else { |
623 » » » log.Printf("ERROR: Failed to insert workspace into datab
ase: %q\n", err) | 623 » » » glog.Errorf("Failed to insert workspace into database: %
q\n", err) |
624 } | 624 } |
625 } | 625 } |
626 return "", fmt.Errorf("Failed to create a new workspace") | 626 return "", fmt.Errorf("Failed to create a new workspace") |
627 } | 627 } |
628 | 628 |
629 // getCode returns the code for a given hash, or the empty string if not found. | 629 // getCode returns the code for a given hash, or the empty string if not found. |
630 func getCode(hash string) (string, int, int, int, bool, error) { | 630 func getCode(hash string) (string, int, int, int, bool, error) { |
631 code := "" | 631 code := "" |
632 width := 0 | 632 width := 0 |
633 height := 0 | 633 height := 0 |
634 source := 0 | 634 source := 0 |
635 gpu := false | 635 gpu := false |
636 if err := db.QueryRow("SELECT code, width, height, gpu, source_image_id
FROM webtry WHERE hash=?", hash).Scan(&code, &width, &height, &gpu, &source); er
r != nil { | 636 if err := db.QueryRow("SELECT code, width, height, gpu, source_image_id
FROM webtry WHERE hash=?", hash).Scan(&code, &width, &height, &gpu, &source); er
r != nil { |
637 » » log.Printf("ERROR: Code for hash is missing: %q\n", err) | 637 » » glog.Errorf("Code for hash is missing: %q\n", err) |
638 return code, width, height, source, gpu, err | 638 return code, width, height, source, gpu, err |
639 } | 639 } |
640 return code, width, height, source, gpu, nil | 640 return code, width, height, source, gpu, nil |
641 } | 641 } |
642 | 642 |
643 func workspaceHandler(w http.ResponseWriter, r *http.Request) { | 643 func workspaceHandler(w http.ResponseWriter, r *http.Request) { |
644 » log.Printf("Workspace Handler: %q\n", r.URL.Path) | 644 » glog.Infof("Workspace Handler: %q\n", r.URL.Path) |
645 if r.Method == "GET" { | 645 if r.Method == "GET" { |
646 tries := []Try{} | 646 tries := []Try{} |
647 match := workspaceLink.FindStringSubmatch(r.URL.Path) | 647 match := workspaceLink.FindStringSubmatch(r.URL.Path) |
648 name := "" | 648 name := "" |
649 if len(match) == 2 { | 649 if len(match) == 2 { |
650 name = match[1] | 650 name = match[1] |
651 rows, err := db.Query("SELECT create_ts, hash, source_im
age_id FROM workspacetry WHERE name=? ORDER BY create_ts", name) | 651 rows, err := db.Query("SELECT create_ts, hash, source_im
age_id FROM workspacetry WHERE name=? ORDER BY create_ts", name) |
652 if err != nil { | 652 if err != nil { |
653 reportError(w, r, err, "Failed to select.") | 653 reportError(w, r, err, "Failed to select.") |
654 return | 654 return |
655 } | 655 } |
656 for rows.Next() { | 656 for rows.Next() { |
657 var hash string | 657 var hash string |
658 var create_ts time.Time | 658 var create_ts time.Time |
659 var source int | 659 var source int |
660 if err := rows.Scan(&create_ts, &hash, &source);
err != nil { | 660 if err := rows.Scan(&create_ts, &hash, &source);
err != nil { |
661 » » » » » log.Printf("Error: failed to fetch from
database: %q", err) | 661 » » » » » glog.Errorf("failed to fetch from databa
se: %q", err) |
662 continue | 662 continue |
663 } | 663 } |
664 tries = append(tries, Try{Hash: hash, Source: so
urce, CreateTS: create_ts.Format("2006-02-01")}) | 664 tries = append(tries, Try{Hash: hash, Source: so
urce, CreateTS: create_ts.Format("2006-02-01")}) |
665 } | 665 } |
666 } | 666 } |
667 var code string | 667 var code string |
668 var hash string | 668 var hash string |
669 var width int | 669 var width int |
670 var height int | 670 var height int |
671 source := 0 | 671 source := 0 |
672 gpu := false | 672 gpu := false |
673 if len(tries) == 0 { | 673 if len(tries) == 0 { |
674 code = DEFAULT_SAMPLE | 674 code = DEFAULT_SAMPLE |
675 width = 256 | 675 width = 256 |
676 height = 256 | 676 height = 256 |
677 } else { | 677 } else { |
678 hash = tries[len(tries)-1].Hash | 678 hash = tries[len(tries)-1].Hash |
679 code, width, height, source, gpu, _ = getCode(hash) | 679 code, width, height, source, gpu, _ = getCode(hash) |
680 } | 680 } |
681 w.Header().Set("Content-Type", "text/html") | 681 w.Header().Set("Content-Type", "text/html") |
682 if err := workspaceTemplate.Execute(w, Workspace{Tries: tries, C
ode: code, Name: name, Hash: hash, Width: width, Height: height, GPU: gpu, Sourc
e: source, Titlebar: Titlebar{GitHash: gitHash, GitInfo: gitInfo}}); err != nil
{ | 682 if err := workspaceTemplate.Execute(w, Workspace{Tries: tries, C
ode: code, Name: name, Hash: hash, Width: width, Height: height, GPU: gpu, Sourc
e: source, Titlebar: Titlebar{GitHash: gitHash, GitInfo: gitInfo}}); err != nil
{ |
683 » » » log.Printf("ERROR: Failed to expand template: %q\n", err
) | 683 » » » glog.Errorf("Failed to expand template: %q\n", err) |
684 } | 684 } |
685 } else if r.Method == "POST" { | 685 } else if r.Method == "POST" { |
686 name, err := newWorkspace() | 686 name, err := newWorkspace() |
687 if err != nil { | 687 if err != nil { |
688 http.Error(w, "Failed to create a new workspace.", 500) | 688 http.Error(w, "Failed to create a new workspace.", 500) |
689 return | 689 return |
690 } | 690 } |
691 http.Redirect(w, r, "/w/"+name, 302) | 691 http.Redirect(w, r, "/w/"+name, 302) |
692 } | 692 } |
693 } | 693 } |
(...skipping 13 matching lines...) Expand all Loading... |
707 Code string `json:"code"` | 707 Code string `json:"code"` |
708 Width int `json:"width"` | 708 Width int `json:"width"` |
709 Height int `json:"height"` | 709 Height int `json:"height"` |
710 GPU bool `json:"gpu"` | 710 GPU bool `json:"gpu"` |
711 Name string `json:"name"` // Optional name of the workspace the code
is in. | 711 Name string `json:"name"` // Optional name of the workspace the code
is in. |
712 Source int `json:"source"` // ID of the source image, 0 if none. | 712 Source int `json:"source"` // ID of the source image, 0 if none. |
713 } | 713 } |
714 | 714 |
715 // iframeHandler handles the GET and POST of the main page. | 715 // iframeHandler handles the GET and POST of the main page. |
716 func iframeHandler(w http.ResponseWriter, r *http.Request) { | 716 func iframeHandler(w http.ResponseWriter, r *http.Request) { |
717 » log.Printf("IFrame Handler: %q\n", r.URL.Path) | 717 » glog.Infof("IFrame Handler: %q\n", r.URL.Path) |
718 if r.Method != "GET" { | 718 if r.Method != "GET" { |
719 http.NotFound(w, r) | 719 http.NotFound(w, r) |
720 return | 720 return |
721 } | 721 } |
722 match := iframeLink.FindStringSubmatch(r.URL.Path) | 722 match := iframeLink.FindStringSubmatch(r.URL.Path) |
723 if len(match) != 2 { | 723 if len(match) != 2 { |
724 http.NotFound(w, r) | 724 http.NotFound(w, r) |
725 return | 725 return |
726 } | 726 } |
727 hash := match[1] | 727 hash := match[1] |
728 if db == nil { | 728 if db == nil { |
729 http.NotFound(w, r) | 729 http.NotFound(w, r) |
730 return | 730 return |
731 } | 731 } |
732 var code string | 732 var code string |
733 code, width, height, source, gpu, err := getCode(hash) | 733 code, width, height, source, gpu, err := getCode(hash) |
734 if err != nil { | 734 if err != nil { |
735 http.NotFound(w, r) | 735 http.NotFound(w, r) |
736 return | 736 return |
737 } | 737 } |
738 // Expand the template. | 738 // Expand the template. |
739 w.Header().Set("Content-Type", "text/html") | 739 w.Header().Set("Content-Type", "text/html") |
740 if err := iframeTemplate.Execute(w, userCode{Code: code, Width: width, H
eight: height, GPU: gpu, Hash: hash, Source: source}); err != nil { | 740 if err := iframeTemplate.Execute(w, userCode{Code: code, Width: width, H
eight: height, GPU: gpu, Hash: hash, Source: source}); err != nil { |
741 » » log.Printf("ERROR: Failed to expand template: %q\n", err) | 741 » » glog.Errorf("Failed to expand template: %q\n", err) |
742 } | 742 } |
743 } | 743 } |
744 | 744 |
745 type TryInfo struct { | 745 type TryInfo struct { |
746 Hash string `json:"hash"` | 746 Hash string `json:"hash"` |
747 Code string `json:"code"` | 747 Code string `json:"code"` |
748 Width int `json:"width"` | 748 Width int `json:"width"` |
749 Height int `json:"height"` | 749 Height int `json:"height"` |
750 GPU bool `json:"gpu"` | 750 GPU bool `json:"gpu"` |
751 Source int `json:"source"` | 751 Source int `json:"source"` |
752 } | 752 } |
753 | 753 |
754 // tryInfoHandler returns information about a specific try. | 754 // tryInfoHandler returns information about a specific try. |
755 func tryInfoHandler(w http.ResponseWriter, r *http.Request) { | 755 func tryInfoHandler(w http.ResponseWriter, r *http.Request) { |
756 » log.Printf("Try Info Handler: %q\n", r.URL.Path) | 756 » glog.Infof("Try Info Handler: %q\n", r.URL.Path) |
757 if r.Method != "GET" { | 757 if r.Method != "GET" { |
758 http.NotFound(w, r) | 758 http.NotFound(w, r) |
759 return | 759 return |
760 } | 760 } |
761 match := tryInfoLink.FindStringSubmatch(r.URL.Path) | 761 match := tryInfoLink.FindStringSubmatch(r.URL.Path) |
762 if len(match) != 2 { | 762 if len(match) != 2 { |
763 http.NotFound(w, r) | 763 http.NotFound(w, r) |
764 return | 764 return |
765 } | 765 } |
766 hash := match[1] | 766 hash := match[1] |
(...skipping 14 matching lines...) Expand all Loading... |
781 if err != nil { | 781 if err != nil { |
782 reportError(w, r, err, "Failed to serialize a response.") | 782 reportError(w, r, err, "Failed to serialize a response.") |
783 return | 783 return |
784 } | 784 } |
785 w.Header().Set("Content-Type", "application/json") | 785 w.Header().Set("Content-Type", "application/json") |
786 w.Write(resp) | 786 w.Write(resp) |
787 } | 787 } |
788 | 788 |
789 func cleanCompileOutput(s, hash string) string { | 789 func cleanCompileOutput(s, hash string) string { |
790 old := "../../../cache/src/" + hash + ".cpp:" | 790 old := "../../../cache/src/" + hash + ".cpp:" |
791 » log.Printf("INFO: replacing %q\n", old) | 791 » glog.Infof("replacing %q\n", old) |
792 return strings.Replace(s, old, "usercode.cpp:", -1) | 792 return strings.Replace(s, old, "usercode.cpp:", -1) |
793 } | 793 } |
794 | 794 |
795 // mainHandler handles the GET and POST of the main page. | 795 // mainHandler handles the GET and POST of the main page. |
796 func mainHandler(w http.ResponseWriter, r *http.Request) { | 796 func mainHandler(w http.ResponseWriter, r *http.Request) { |
797 » log.Printf("Main Handler: %q\n", r.URL.Path) | 797 » glog.Infof("Main Handler: %q\n", r.URL.Path) |
798 requestsCounter.Inc(1) | 798 requestsCounter.Inc(1) |
799 if r.Method == "GET" { | 799 if r.Method == "GET" { |
800 code := DEFAULT_SAMPLE | 800 code := DEFAULT_SAMPLE |
801 source := 0 | 801 source := 0 |
802 width := 256 | 802 width := 256 |
803 height := 256 | 803 height := 256 |
804 gpu := false | 804 gpu := false |
805 match := directLink.FindStringSubmatch(r.URL.Path) | 805 match := directLink.FindStringSubmatch(r.URL.Path) |
806 var hash string | 806 var hash string |
807 if len(match) == 2 && r.URL.Path != "/" { | 807 if len(match) == 2 && r.URL.Path != "/" { |
808 hash = match[1] | 808 hash = match[1] |
809 if db == nil { | 809 if db == nil { |
810 http.NotFound(w, r) | 810 http.NotFound(w, r) |
811 return | 811 return |
812 } | 812 } |
813 // Update 'code' with the code found in the database. | 813 // Update 'code' with the code found in the database. |
814 if err := db.QueryRow("SELECT code, width, height, gpu,
source_image_id FROM webtry WHERE hash=?", hash).Scan(&code, &width, &height, &g
pu, &source); err != nil { | 814 if err := db.QueryRow("SELECT code, width, height, gpu,
source_image_id FROM webtry WHERE hash=?", hash).Scan(&code, &width, &height, &g
pu, &source); err != nil { |
815 http.NotFound(w, r) | 815 http.NotFound(w, r) |
816 return | 816 return |
817 } | 817 } |
818 } | 818 } |
819 // Expand the template. | 819 // Expand the template. |
820 w.Header().Set("Content-Type", "text/html") | 820 w.Header().Set("Content-Type", "text/html") |
821 if err := indexTemplate.Execute(w, userCode{Code: code, Hash: ha
sh, Source: source, Width: width, Height: height, GPU: gpu, Titlebar: Titlebar{G
itHash: gitHash, GitInfo: gitInfo}}); err != nil { | 821 if err := indexTemplate.Execute(w, userCode{Code: code, Hash: ha
sh, Source: source, Width: width, Height: height, GPU: gpu, Titlebar: Titlebar{G
itHash: gitHash, GitInfo: gitInfo}}); err != nil { |
822 » » » log.Printf("ERROR: Failed to expand template: %q\n", err
) | 822 » » » glog.Errorf("Failed to expand template: %q\n", err) |
823 } | 823 } |
824 } else if r.Method == "POST" { | 824 } else if r.Method == "POST" { |
825 w.Header().Set("Content-Type", "application/json") | 825 w.Header().Set("Content-Type", "application/json") |
826 buf := bytes.NewBuffer(make([]byte, 0, MAX_TRY_SIZE)) | 826 buf := bytes.NewBuffer(make([]byte, 0, MAX_TRY_SIZE)) |
827 n, err := buf.ReadFrom(r.Body) | 827 n, err := buf.ReadFrom(r.Body) |
828 if err != nil { | 828 if err != nil { |
829 reportTryError(w, r, err, "Failed to read a request body
.", "") | 829 reportTryError(w, r, err, "Failed to read a request body
.", "") |
830 return | 830 return |
831 } | 831 } |
832 if n == MAX_TRY_SIZE { | 832 if n == MAX_TRY_SIZE { |
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
900 http.HandleFunc("/iframe/", autogzip.HandleFunc(iframeHandler)) | 900 http.HandleFunc("/iframe/", autogzip.HandleFunc(iframeHandler)) |
901 http.HandleFunc("/json/", autogzip.HandleFunc(tryInfoHandler)) | 901 http.HandleFunc("/json/", autogzip.HandleFunc(tryInfoHandler)) |
902 http.HandleFunc("/sources/", autogzip.HandleFunc(sourcesHandler)) | 902 http.HandleFunc("/sources/", autogzip.HandleFunc(sourcesHandler)) |
903 | 903 |
904 // Resources are served directly | 904 // Resources are served directly |
905 // TODO add support for caching/etags/gzip | 905 // TODO add support for caching/etags/gzip |
906 http.Handle("/res/", autogzip.Handle(http.FileServer(http.Dir("./")))) | 906 http.Handle("/res/", autogzip.Handle(http.FileServer(http.Dir("./")))) |
907 | 907 |
908 // TODO Break out /c/ as it's own handler. | 908 // TODO Break out /c/ as it's own handler. |
909 http.HandleFunc("/", autogzip.HandleFunc(mainHandler)) | 909 http.HandleFunc("/", autogzip.HandleFunc(mainHandler)) |
910 » log.Fatal(http.ListenAndServe(*port, nil)) | 910 » glog.Fatal(http.ListenAndServe(*port, nil)) |
911 } | 911 } |
OLD | NEW |