blob: 19eeb4c501d5a88e67a465a6d133af70a5b59240 [file] [log] [blame]
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +00001package main
2
3import (
4 "bytes"
5 "crypto/md5"
commit-bot@chromium.org282333f2014-04-14 14:54:07 +00006 "database/sql"
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +00007 "encoding/base64"
commit-bot@chromium.org0a7e5b72014-05-29 15:58:00 +00008 "encoding/binary"
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +00009 "encoding/json"
10 "flag"
11 "fmt"
commit-bot@chromium.orgc81d1c42014-04-14 18:53:10 +000012 htemplate "html/template"
commit-bot@chromium.org0a7e5b72014-05-29 15:58:00 +000013 "image"
14 _ "image/gif"
15 _ "image/jpeg"
16 "image/png"
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +000017 "io/ioutil"
18 "log"
commit-bot@chromium.orgd04e1dd2014-04-19 13:55:50 +000019 "math/rand"
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +000020 "net/http"
21 "os"
22 "os/exec"
23 "path/filepath"
commit-bot@chromium.orgc81d1c42014-04-14 18:53:10 +000024 "regexp"
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +000025 "strings"
26 "text/template"
commit-bot@chromium.org06aca012014-04-14 20:12:08 +000027 "time"
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +000028)
29
commit-bot@chromium.orga4c36772014-05-19 17:38:52 +000030import (
31 "github.com/fiorix/go-web/autogzip"
32 _ "github.com/go-sql-driver/mysql"
33 _ "github.com/mattn/go-sqlite3"
34)
35
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +000036const (
commit-bot@chromium.org89126a62014-04-30 19:38:51 +000037 RESULT_COMPILE = `../../experimental/webtry/safec++ -DSK_GAMMA_SRGB -DSK_GAMMA_APPLY_TO_A8 -DSK_SCALAR_TO_FLOAT_EXCLUDED -DSK_ALLOW_STATIC_GLOBAL_INITIALIZERS=1 -DSK_SUPPORT_GPU=0 -DSK_SUPPORT_OPENCL=0 -DSK_FORCE_DISTANCEFIELD_FONTS=0 -DSK_SCALAR_IS_FLOAT -DSK_CAN_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/pathops -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-unused-parameter -m64 -fno-rtti -Wnon-virtual-dtor -c ../../../cache/%s.cpp -o ../../../cache/%s.o`
38 LINK = `../../experimental/webtry/safec++ -m64 -lstdc++ -lm -o ../../../inout/%s -Wl,--start-group ../../../cache/%s.o obj/experimental/webtry/webtry.main.o obj/gyp/libflags.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 libskia_ports.a libskia_sfnt.a -Wl,--end-group -lpng -lz -lgif -lpthread -lfontconfig -ldl -lfreetype`
fmalita@google.com950306c2014-05-01 15:14:56 +000039 DEFAULT_SAMPLE = `void draw(SkCanvas* canvas) {
40 SkPaint p;
41 p.setColor(SK_ColorRED);
42 p.setAntiAlias(true);
43 p.setStyle(SkPaint::kStroke_Style);
44 p.setStrokeWidth(10);
commit-bot@chromium.orgc81d1c42014-04-14 18:53:10 +000045
fmalita@google.com950306c2014-05-01 15:14:56 +000046 canvas->drawLine(20, 20, 100, 100, p);
47}`
commit-bot@chromium.org4bd8fdc2014-04-15 00:43:51 +000048 // Don't increase above 2^16 w/o altering the db tables to accept something bigger than TEXT.
49 MAX_TRY_SIZE = 64000
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +000050)
51
52var (
53 // codeTemplate is the cpp code template the user's code is copied into.
54 codeTemplate *template.Template = nil
55
commit-bot@chromium.org06aca012014-04-14 20:12:08 +000056 // indexTemplate is the main index.html page we serve.
57 indexTemplate *htemplate.Template = nil
58
commit-bot@chromium.org2dceeda2014-04-19 14:50:23 +000059 // iframeTemplate is the main index.html page we serve.
60 iframeTemplate *htemplate.Template = nil
61
commit-bot@chromium.orgd04e1dd2014-04-19 13:55:50 +000062 // recentTemplate is a list of recent images.
commit-bot@chromium.org06aca012014-04-14 20:12:08 +000063 recentTemplate *htemplate.Template = nil
commit-bot@chromium.org282333f2014-04-14 14:54:07 +000064
commit-bot@chromium.orgd04e1dd2014-04-19 13:55:50 +000065 // workspaceTemplate is the page for workspaces, a series of webtrys.
66 workspaceTemplate *htemplate.Template = nil
67
commit-bot@chromium.org282333f2014-04-14 14:54:07 +000068 // db is the database, nil if we don't have an SQL database to store data into.
69 db *sql.DB = nil
commit-bot@chromium.orgc81d1c42014-04-14 18:53:10 +000070
71 // directLink is the regex that matches URLs paths that are direct links.
commit-bot@chromium.org06aca012014-04-14 20:12:08 +000072 directLink = regexp.MustCompile("^/c/([a-f0-9]+)$")
73
commit-bot@chromium.org2dceeda2014-04-19 14:50:23 +000074 // iframeLink is the regex that matches URLs paths that are links to iframes.
75 iframeLink = regexp.MustCompile("^/iframe/([a-f0-9]+)$")
76
commit-bot@chromium.org06aca012014-04-14 20:12:08 +000077 // imageLink is the regex that matches URLs paths that are direct links to PNGs.
commit-bot@chromium.org0a7e5b72014-05-29 15:58:00 +000078 imageLink = regexp.MustCompile("^/i/([a-z0-9-]+.png)$")
commit-bot@chromium.orgd04e1dd2014-04-19 13:55:50 +000079
commit-bot@chromium.org35ffc442014-04-22 19:32:06 +000080 // tryInfoLink is the regex that matches URLs paths that are direct links to data about a single try.
81 tryInfoLink = regexp.MustCompile("^/json/([a-f0-9]+)$")
82
commit-bot@chromium.orgd04e1dd2014-04-19 13:55:50 +000083 // workspaceLink is the regex that matches URLs paths for workspaces.
84 workspaceLink = regexp.MustCompile("^/w/([a-z0-9-]+)$")
85
86 // workspaceNameAdj is a list of adjectives for building workspace names.
87 workspaceNameAdj = []string{
88 "autumn", "hidden", "bitter", "misty", "silent", "empty", "dry", "dark",
89 "summer", "icy", "delicate", "quiet", "white", "cool", "spring", "winter",
90 "patient", "twilight", "dawn", "crimson", "wispy", "weathered", "blue",
91 "billowing", "broken", "cold", "damp", "falling", "frosty", "green",
92 "long", "late", "lingering", "bold", "little", "morning", "muddy", "old",
93 "red", "rough", "still", "small", "sparkling", "throbbing", "shy",
94 "wandering", "withered", "wild", "black", "young", "holy", "solitary",
95 "fragrant", "aged", "snowy", "proud", "floral", "restless", "divine",
96 "polished", "ancient", "purple", "lively", "nameless",
97 }
98
99 // workspaceNameNoun is a list of nouns for building workspace names.
100 workspaceNameNoun = []string{
101 "waterfall", "river", "breeze", "moon", "rain", "wind", "sea", "morning",
102 "snow", "lake", "sunset", "pine", "shadow", "leaf", "dawn", "glitter",
103 "forest", "hill", "cloud", "meadow", "sun", "glade", "bird", "brook",
104 "butterfly", "bush", "dew", "dust", "field", "fire", "flower", "firefly",
105 "feather", "grass", "haze", "mountain", "night", "pond", "darkness",
106 "snowflake", "silence", "sound", "sky", "shape", "surf", "thunder",
107 "violet", "water", "wildflower", "wave", "water", "resonance", "sun",
108 "wood", "dream", "cherry", "tree", "fog", "frost", "voice", "paper",
109 "frog", "smoke", "star",
110 }
commit-bot@chromium.org472f8302014-04-28 15:33:31 +0000111
112 gitHash = ""
113 gitInfo = ""
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +0000114)
115
116// flags
117var (
118 useChroot = flag.Bool("use_chroot", false, "Run the compiled code in the schroot jail.")
commit-bot@chromium.org282333f2014-04-14 14:54:07 +0000119 port = flag.String("port", ":8000", "HTTP service address (e.g., ':8000')")
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +0000120)
121
122// lineNumbers adds #line numbering to the user's code.
123func LineNumbers(c string) string {
124 lines := strings.Split(c, "\n")
125 ret := []string{}
126 for i, line := range lines {
127 ret = append(ret, fmt.Sprintf("#line %d", i+1))
128 ret = append(ret, line)
129 }
130 return strings.Join(ret, "\n")
131}
132
133func init() {
commit-bot@chromium.org2dceeda2014-04-19 14:50:23 +0000134 rand.Seed(time.Now().UnixNano())
commit-bot@chromium.orgc81d1c42014-04-14 18:53:10 +0000135
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +0000136 // Change the current working directory to the directory of the executable.
137 var err error
138 cwd, err := filepath.Abs(filepath.Dir(os.Args[0]))
139 if err != nil {
140 log.Fatal(err)
141 }
142 os.Chdir(cwd)
143
144 codeTemplate, err = template.ParseFiles(filepath.Join(cwd, "templates/template.cpp"))
145 if err != nil {
146 panic(err)
147 }
commit-bot@chromium.orgd04e1dd2014-04-19 13:55:50 +0000148 indexTemplate, err = htemplate.ParseFiles(
149 filepath.Join(cwd, "templates/index.html"),
150 filepath.Join(cwd, "templates/titlebar.html"),
commit-bot@chromium.org90041922014-04-22 21:13:45 +0000151 filepath.Join(cwd, "templates/content.html"),
commit-bot@chromium.org015c08e2014-05-19 16:40:08 +0000152 filepath.Join(cwd, "templates/headercommon.html"),
153 filepath.Join(cwd, "templates/footercommon.html"),
commit-bot@chromium.orgd04e1dd2014-04-19 13:55:50 +0000154 )
commit-bot@chromium.org06aca012014-04-14 20:12:08 +0000155 if err != nil {
156 panic(err)
157 }
commit-bot@chromium.org2dceeda2014-04-19 14:50:23 +0000158 iframeTemplate, err = htemplate.ParseFiles(
159 filepath.Join(cwd, "templates/iframe.html"),
commit-bot@chromium.org90041922014-04-22 21:13:45 +0000160 filepath.Join(cwd, "templates/content.html"),
commit-bot@chromium.org015c08e2014-05-19 16:40:08 +0000161 filepath.Join(cwd, "templates/headercommon.html"),
162 filepath.Join(cwd, "templates/footercommon.html"),
commit-bot@chromium.org2dceeda2014-04-19 14:50:23 +0000163 )
164 if err != nil {
165 panic(err)
166 }
commit-bot@chromium.orgd04e1dd2014-04-19 13:55:50 +0000167 recentTemplate, err = htemplate.ParseFiles(
168 filepath.Join(cwd, "templates/recent.html"),
169 filepath.Join(cwd, "templates/titlebar.html"),
commit-bot@chromium.org015c08e2014-05-19 16:40:08 +0000170 filepath.Join(cwd, "templates/headercommon.html"),
171 filepath.Join(cwd, "templates/footercommon.html"),
commit-bot@chromium.orgd04e1dd2014-04-19 13:55:50 +0000172 )
173 if err != nil {
174 panic(err)
175 }
176 workspaceTemplate, err = htemplate.ParseFiles(
177 filepath.Join(cwd, "templates/workspace.html"),
178 filepath.Join(cwd, "templates/titlebar.html"),
commit-bot@chromium.org90041922014-04-22 21:13:45 +0000179 filepath.Join(cwd, "templates/content.html"),
commit-bot@chromium.org015c08e2014-05-19 16:40:08 +0000180 filepath.Join(cwd, "templates/headercommon.html"),
181 filepath.Join(cwd, "templates/footercommon.html"),
commit-bot@chromium.orgd04e1dd2014-04-19 13:55:50 +0000182 )
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +0000183 if err != nil {
184 panic(err)
185 }
commit-bot@chromium.org282333f2014-04-14 14:54:07 +0000186
commit-bot@chromium.org472f8302014-04-28 15:33:31 +0000187 // The git command returns output of the format:
188 //
189 // f672cead70404080a991ebfb86c38316a4589b23 2014-04-27 19:21:51 +0000
190 //
191 logOutput, err := doCmd(`git log --format=%H%x20%ai HEAD^..HEAD`, true)
192 if err != nil {
193 panic(err)
194 }
195 logInfo := strings.Split(logOutput, " ")
196 gitHash = logInfo[0]
197 gitInfo = logInfo[1] + " " + logInfo[2] + " " + logInfo[0][0:6]
198
commit-bot@chromium.org282333f2014-04-14 14:54:07 +0000199 // Connect to MySQL server. First, get the password from the metadata server.
200 // See https://developers.google.com/compute/docs/metadata#custom.
201 req, err := http.NewRequest("GET", "http://metadata/computeMetadata/v1/instance/attributes/password", nil)
202 if err != nil {
203 panic(err)
204 }
205 client := http.Client{}
206 req.Header.Add("X-Google-Metadata-Request", "True")
207 if resp, err := client.Do(req); err == nil {
208 password, err := ioutil.ReadAll(resp.Body)
209 if err != nil {
210 log.Printf("ERROR: Failed to read password from metadata server: %q\n", err)
211 panic(err)
212 }
213 // The IP address of the database is found here:
214 // https://console.developers.google.com/project/31977622648/sql/instances/webtry/overview
215 // And 3306 is the default port for MySQL.
commit-bot@chromium.org4bd8fdc2014-04-15 00:43:51 +0000216 db, err = sql.Open("mysql", fmt.Sprintf("webtry:%s@tcp(173.194.83.52:3306)/webtry?parseTime=true", password))
commit-bot@chromium.org282333f2014-04-14 14:54:07 +0000217 if err != nil {
218 log.Printf("ERROR: Failed to open connection to SQL server: %q\n", err)
219 panic(err)
220 }
221 } else {
commit-bot@chromium.orgd04e1dd2014-04-19 13:55:50 +0000222 log.Printf("INFO: Failed to find metadata, unable to connect to MySQL server (Expected when running locally): %q\n", err)
commit-bot@chromium.orgc81d1c42014-04-14 18:53:10 +0000223 // Fallback to sqlite for local use.
224 db, err = sql.Open("sqlite3", "./webtry.db")
225 if err != nil {
226 log.Printf("ERROR: Failed to open: %q\n", err)
227 panic(err)
228 }
commit-bot@chromium.org0a7e5b72014-05-29 15:58:00 +0000229 sql := `CREATE TABLE source_images (
230 id INTEGER PRIMARY KEY NOT NULL,
231 image MEDIUMBLOB DEFAULT '' NOT NULL, -- formatted as a PNG.
232 width INTEGER DEFAULT 0 NOT NULL,
233 height INTEGER DEFAULT 0 NOT NULL,
234 create_ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
235 hidden INTEGER DEFAULT 0 NOT NULL
236 )`
237 _, err = db.Exec(sql)
238 log.Printf("Info: status creating sqlite table for sources: %q\n", err)
239
240 sql = `CREATE TABLE webtry (
241 code TEXT DEFAULT '' NOT NULL,
242 create_ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
243 hash CHAR(64) DEFAULT '' NOT NULL,
244 source_image_id INTEGER DEFAULT 0 NOT NULL,
245
commit-bot@chromium.orgc81d1c42014-04-14 18:53:10 +0000246 PRIMARY KEY(hash)
247 )`
commit-bot@chromium.orgd04e1dd2014-04-19 13:55:50 +0000248 _, err = db.Exec(sql)
249 log.Printf("Info: status creating sqlite table for webtry: %q\n", err)
commit-bot@chromium.org0a7e5b72014-05-29 15:58:00 +0000250
commit-bot@chromium.orgd04e1dd2014-04-19 13:55:50 +0000251 sql = `CREATE TABLE workspace (
252 name CHAR(64) DEFAULT '' NOT NULL,
253 create_ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
254 PRIMARY KEY(name)
255 )`
256 _, err = db.Exec(sql)
257 log.Printf("Info: status creating sqlite table for workspace: %q\n", err)
commit-bot@chromium.orgd04e1dd2014-04-19 13:55:50 +0000258
commit-bot@chromium.org0a7e5b72014-05-29 15:58:00 +0000259 sql = `CREATE TABLE workspacetry (
260 name CHAR(64) DEFAULT '' NOT NULL,
261 create_ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
262 hash CHAR(64) DEFAULT '' NOT NULL,
263 hidden INTEGER DEFAULT 0 NOT NULL,
264 source_image_id INTEGER DEFAULT 0 NOT NULL,
265
266 FOREIGN KEY (name) REFERENCES workspace(name)
commit-bot@chromium.orgd04e1dd2014-04-19 13:55:50 +0000267 )`
268 _, err = db.Exec(sql)
269 log.Printf("Info: status creating sqlite table for workspace try: %q\n", err)
commit-bot@chromium.org282333f2014-04-14 14:54:07 +0000270 }
commit-bot@chromium.org9f3b9252014-05-14 12:55:34 +0000271
272 // Ping the database to keep the connection fresh.
273 go func() {
274 c := time.Tick(1 * time.Minute)
275 for _ = range c {
276 if err := db.Ping(); err != nil {
277 log.Printf("ERROR: Database failed to respond: %q\n", err)
278 }
279 }
280 }()
281
commit-bot@chromium.org0a7e5b72014-05-29 15:58:00 +0000282 writeOutAllSourceImages()
283}
284
285func writeOutAllSourceImages() {
286 // Pull all the source images from the db and write them out to inout.
287 rows, err := db.Query("SELECT id, image, create_ts FROM source_images ORDER BY create_ts DESC")
288
289 if err != nil {
290 log.Printf("ERROR: Failed to open connection to SQL server: %q\n", err)
291 panic(err)
292 }
293 for rows.Next() {
294 var id int
295 var image []byte
296 var create_ts time.Time
297 if err := rows.Scan(&id, &image, &create_ts); err != nil {
298 log.Printf("Error: failed to fetch from database: %q", err)
299 continue
300 }
301 filename := fmt.Sprintf("../../../inout/image-%d.png", id)
302 if _, err := os.Stat(filename); os.IsExist(err) {
303 log.Printf("Skipping write since file exists: %q", filename)
304 continue
305 }
306 if err := ioutil.WriteFile(filename, image, 0666); err != nil {
307 log.Printf("Error: failed to write image file: %q", err)
308 }
309 }
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +0000310}
311
commit-bot@chromium.org472f8302014-04-28 15:33:31 +0000312// Titlebar is used in titlebar template expansion.
313type Titlebar struct {
314 GitHash string
315 GitInfo string
316}
317
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +0000318// userCode is used in template expansion.
319type userCode struct {
commit-bot@chromium.org472f8302014-04-28 15:33:31 +0000320 Code string
321 Hash string
commit-bot@chromium.org0a7e5b72014-05-29 15:58:00 +0000322 Source int
commit-bot@chromium.org472f8302014-04-28 15:33:31 +0000323 Titlebar Titlebar
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +0000324}
325
326// expandToFile expands the template and writes the result to the file.
327func expandToFile(filename string, code string, t *template.Template) error {
328 f, err := os.Create(filename)
329 if err != nil {
330 return err
331 }
332 defer f.Close()
commit-bot@chromium.org472f8302014-04-28 15:33:31 +0000333 return t.Execute(f, userCode{Code: code, Titlebar: Titlebar{GitHash: gitHash, GitInfo: gitInfo}})
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +0000334}
335
commit-bot@chromium.org0a7e5b72014-05-29 15:58:00 +0000336// expandCode expands the template into a file and calculates the MD5 hash.
337func expandCode(code string, source int) (string, error) {
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +0000338 h := md5.New()
339 h.Write([]byte(code))
commit-bot@chromium.org0a7e5b72014-05-29 15:58:00 +0000340 binary.Write(h, binary.LittleEndian, int64(source))
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +0000341 hash := fmt.Sprintf("%x", h.Sum(nil))
342 // At this point we are running in skia/experimental/webtry, making cache a
343 // peer directory to skia.
344 // TODO(jcgregorio) Make all relative directories into flags.
345 err := expandToFile(fmt.Sprintf("../../../cache/%s.cpp", hash), code, codeTemplate)
346 return hash, err
347}
348
349// response is serialized to JSON as a response to POSTs.
350type response struct {
351 Message string `json:"message"`
commit-bot@chromium.org90041922014-04-22 21:13:45 +0000352 StdOut string `json:"stdout"`
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +0000353 Img string `json:"img"`
commit-bot@chromium.orgc81d1c42014-04-14 18:53:10 +0000354 Hash string `json:"hash"`
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +0000355}
356
357// doCmd executes the given command line string in either the out/Debug
commit-bot@chromium.org15b29812014-04-28 14:56:32 +0000358// directory or the inout directory. Returns the stdout and stderr.
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +0000359func doCmd(commandLine string, moveToDebug bool) (string, error) {
360 log.Printf("Command: %q\n", commandLine)
361 programAndArgs := strings.SplitN(commandLine, " ", 2)
362 program := programAndArgs[0]
363 args := []string{}
364 if len(programAndArgs) > 1 {
365 args = strings.Split(programAndArgs[1], " ")
366 }
367 cmd := exec.Command(program, args...)
368 abs, err := filepath.Abs("../../out/Debug")
369 if err != nil {
370 return "", fmt.Errorf("Failed to find absolute path to Debug directory.")
371 }
372 if moveToDebug {
373 cmd.Dir = abs
374 } else if !*useChroot { // Don't set cmd.Dir when using chroot.
375 abs, err := filepath.Abs("../../../inout")
376 if err != nil {
377 return "", fmt.Errorf("Failed to find absolute path to inout directory.")
378 }
379 cmd.Dir = abs
380 }
381 log.Printf("Run in directory: %q\n", cmd.Dir)
commit-bot@chromium.org15b29812014-04-28 14:56:32 +0000382 message, err := cmd.CombinedOutput()
383 log.Printf("StdOut + StdErr: %s\n", string(message))
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +0000384 if err != nil {
385 log.Printf("Exit status: %s\n", err.Error())
commit-bot@chromium.org15b29812014-04-28 14:56:32 +0000386 return string(message), fmt.Errorf("Failed to run command.")
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +0000387 }
commit-bot@chromium.org15b29812014-04-28 14:56:32 +0000388 return string(message), nil
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +0000389}
390
391// reportError formats an HTTP error response and also logs the detailed error message.
392func reportError(w http.ResponseWriter, r *http.Request, err error, message string) {
commit-bot@chromium.org90041922014-04-22 21:13:45 +0000393 log.Printf("Error: %s\n%s", message, err.Error())
commit-bot@chromium.orga4c36772014-05-19 17:38:52 +0000394 w.Header().Set("Content-Type", "text/plain")
commit-bot@chromium.org90041922014-04-22 21:13:45 +0000395 http.Error(w, message, 500)
396}
397
398// reportTryError formats an HTTP error response in JSON and also logs the detailed error message.
399func reportTryError(w http.ResponseWriter, r *http.Request, err error, message, hash string) {
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +0000400 m := response{
401 Message: message,
commit-bot@chromium.org90041922014-04-22 21:13:45 +0000402 Hash: hash,
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +0000403 }
404 log.Printf("Error: %s\n%s", message, err.Error())
405 resp, err := json.Marshal(m)
406 if err != nil {
407 http.Error(w, "Failed to serialize a response", 500)
408 return
409 }
commit-bot@chromium.orga4c36772014-05-19 17:38:52 +0000410 w.Header().Set("Content-Type", "text/plain")
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +0000411 w.Write(resp)
412}
413
commit-bot@chromium.org0a7e5b72014-05-29 15:58:00 +0000414func writeToDatabase(hash string, code string, workspaceName string, source int) {
commit-bot@chromium.org282333f2014-04-14 14:54:07 +0000415 if db == nil {
416 return
417 }
commit-bot@chromium.org0a7e5b72014-05-29 15:58:00 +0000418 if _, err := db.Exec("INSERT INTO webtry (code, hash, source_image_id) VALUES(?, ?, ?)", code, hash, source); err != nil {
commit-bot@chromium.org282333f2014-04-14 14:54:07 +0000419 log.Printf("ERROR: Failed to insert code into database: %q\n", err)
420 }
commit-bot@chromium.orgd04e1dd2014-04-19 13:55:50 +0000421 if workspaceName != "" {
commit-bot@chromium.org0a7e5b72014-05-29 15:58:00 +0000422 if _, err := db.Exec("INSERT INTO workspacetry (name, hash, source_image_id) VALUES(?, ?, ?)", workspaceName, hash, source); err != nil {
commit-bot@chromium.orgd04e1dd2014-04-19 13:55:50 +0000423 log.Printf("ERROR: Failed to insert into workspacetry table: %q\n", err)
424 }
425 }
commit-bot@chromium.org282333f2014-04-14 14:54:07 +0000426}
427
commit-bot@chromium.org0a7e5b72014-05-29 15:58:00 +0000428type Sources struct {
429 Id int `json:"id"`
430}
431
432// sourcesHandler serves up the PNG of a specific try.
433func sourcesHandler(w http.ResponseWriter, r *http.Request) {
434 log.Printf("Sources Handler: %q\n", r.URL.Path)
435 if r.Method == "GET" {
436 rows, err := db.Query("SELECT id, create_ts FROM source_images WHERE hidden=0 ORDER BY create_ts DESC")
437
438 if err != nil {
439 http.Error(w, fmt.Sprintf("Failed to query sources: %s.", err), 500)
440 }
441 sources := make([]Sources, 0, 0)
442 for rows.Next() {
443 var id int
444 var create_ts time.Time
445 if err := rows.Scan(&id, &create_ts); err != nil {
446 log.Printf("Error: failed to fetch from database: %q", err)
447 continue
448 }
449 sources = append(sources, Sources{Id: id})
450 }
451
452 resp, err := json.Marshal(sources)
453 if err != nil {
454 reportError(w, r, err, "Failed to serialize a response.")
455 return
456 }
457 w.Header().Set("Content-Type", "application/json")
458 w.Write(resp)
459
460 } else if r.Method == "POST" {
461 if err := r.ParseMultipartForm(1000000); err != nil {
462 http.Error(w, fmt.Sprintf("Failed to load image: %s.", err), 500)
463 return
464 }
465 if _, ok := r.MultipartForm.File["upload"]; !ok {
466 http.Error(w, "Invalid upload.", 500)
467 return
468 }
469 if len(r.MultipartForm.File["upload"]) != 1 {
470 http.Error(w, "Wrong number of uploads.", 500)
471 return
472 }
473 f, err := r.MultipartForm.File["upload"][0].Open()
474 if err != nil {
475 http.Error(w, fmt.Sprintf("Failed to load image: %s.", err), 500)
476 return
477 }
478 defer f.Close()
479 m, _, err := image.Decode(f)
480 if err != nil {
481 http.Error(w, fmt.Sprintf("Failed to decode image: %s.", err), 500)
482 return
483 }
484 var b bytes.Buffer
485 png.Encode(&b, m)
486 bounds := m.Bounds()
487 width := bounds.Max.Y - bounds.Min.Y
488 height := bounds.Max.X - bounds.Min.X
489 if _, err := db.Exec("INSERT INTO source_images (image, width, height) VALUES(?, ?, ?)", b.Bytes(), width, height); err != nil {
490 log.Printf("ERROR: Failed to insert sources into database: %q\n", err)
491 http.Error(w, fmt.Sprintf("Failed to store image: %s.", err), 500)
492 return
493 }
494 go writeOutAllSourceImages()
495
496 // Now redirect back to where we came from.
497 http.Redirect(w, r, r.Referer(), 302)
498 } else {
499 http.NotFound(w, r)
500 return
501 }
502}
503
commit-bot@chromium.org06aca012014-04-14 20:12:08 +0000504// imageHandler serves up the PNG of a specific try.
505func imageHandler(w http.ResponseWriter, r *http.Request) {
506 log.Printf("Image Handler: %q\n", r.URL.Path)
507 if r.Method != "GET" {
508 http.NotFound(w, r)
509 return
510 }
511 match := imageLink.FindStringSubmatch(r.URL.Path)
512 if len(match) != 2 {
513 http.NotFound(w, r)
514 return
515 }
516 filename := match[1]
commit-bot@chromium.orga4c36772014-05-19 17:38:52 +0000517 w.Header().Set("Content-Type", "image/png")
commit-bot@chromium.org06aca012014-04-14 20:12:08 +0000518 http.ServeFile(w, r, fmt.Sprintf("../../../inout/%s", filename))
519}
520
521type Try struct {
commit-bot@chromium.orgc3b738a2014-04-21 17:36:44 +0000522 Hash string `json:"hash"`
commit-bot@chromium.org0a7e5b72014-05-29 15:58:00 +0000523 Source int
commit-bot@chromium.orgc3b738a2014-04-21 17:36:44 +0000524 CreateTS string `json:"create_ts"`
commit-bot@chromium.org06aca012014-04-14 20:12:08 +0000525}
526
527type Recent struct {
commit-bot@chromium.org472f8302014-04-28 15:33:31 +0000528 Tries []Try
529 Titlebar Titlebar
commit-bot@chromium.org06aca012014-04-14 20:12:08 +0000530}
531
532// recentHandler shows the last 20 tries.
533func recentHandler(w http.ResponseWriter, r *http.Request) {
534 log.Printf("Recent Handler: %q\n", r.URL.Path)
535
536 var err error
537 rows, err := db.Query("SELECT create_ts, hash FROM webtry ORDER BY create_ts DESC LIMIT 20")
538 if err != nil {
539 http.NotFound(w, r)
540 return
541 }
542 recent := []Try{}
543 for rows.Next() {
544 var hash string
545 var create_ts time.Time
546 if err := rows.Scan(&create_ts, &hash); err != nil {
547 log.Printf("Error: failed to fetch from database: %q", err)
548 continue
549 }
550 recent = append(recent, Try{Hash: hash, CreateTS: create_ts.Format("2006-02-01")})
551 }
commit-bot@chromium.orga4c36772014-05-19 17:38:52 +0000552 w.Header().Set("Content-Type", "text/html")
commit-bot@chromium.org472f8302014-04-28 15:33:31 +0000553 if err := recentTemplate.Execute(w, Recent{Tries: recent, Titlebar: Titlebar{GitHash: gitHash, GitInfo: gitInfo}}); err != nil {
commit-bot@chromium.org06aca012014-04-14 20:12:08 +0000554 log.Printf("ERROR: Failed to expand template: %q\n", err)
555 }
556}
557
commit-bot@chromium.orgd04e1dd2014-04-19 13:55:50 +0000558type Workspace struct {
commit-bot@chromium.org472f8302014-04-28 15:33:31 +0000559 Name string
560 Code string
561 Hash string
commit-bot@chromium.org0a7e5b72014-05-29 15:58:00 +0000562 Source int
commit-bot@chromium.org472f8302014-04-28 15:33:31 +0000563 Tries []Try
564 Titlebar Titlebar
commit-bot@chromium.orgd04e1dd2014-04-19 13:55:50 +0000565}
566
567// newWorkspace generates a new random workspace name and stores it in the database.
568func newWorkspace() (string, error) {
569 for i := 0; i < 10; i++ {
570 adj := workspaceNameAdj[rand.Intn(len(workspaceNameAdj))]
571 noun := workspaceNameNoun[rand.Intn(len(workspaceNameNoun))]
572 suffix := rand.Intn(1000)
573 name := fmt.Sprintf("%s-%s-%d", adj, noun, suffix)
574 if _, err := db.Exec("INSERT INTO workspace (name) VALUES(?)", name); err == nil {
575 return name, nil
576 } else {
577 log.Printf("ERROR: Failed to insert workspace into database: %q\n", err)
578 }
579 }
580 return "", fmt.Errorf("Failed to create a new workspace")
581}
582
583// getCode returns the code for a given hash, or the empty string if not found.
commit-bot@chromium.org0a7e5b72014-05-29 15:58:00 +0000584func getCode(hash string) (string, int, error) {
commit-bot@chromium.orgd04e1dd2014-04-19 13:55:50 +0000585 code := ""
commit-bot@chromium.org0a7e5b72014-05-29 15:58:00 +0000586 source := 0
587 if err := db.QueryRow("SELECT code, source_image_id FROM webtry WHERE hash=?", hash).Scan(&code, &source); err != nil {
commit-bot@chromium.orgd04e1dd2014-04-19 13:55:50 +0000588 log.Printf("ERROR: Code for hash is missing: %q\n", err)
commit-bot@chromium.org0a7e5b72014-05-29 15:58:00 +0000589 return code, source, err
commit-bot@chromium.orgd04e1dd2014-04-19 13:55:50 +0000590 }
commit-bot@chromium.org0a7e5b72014-05-29 15:58:00 +0000591 return code, source, nil
commit-bot@chromium.orgd04e1dd2014-04-19 13:55:50 +0000592}
593
594func workspaceHandler(w http.ResponseWriter, r *http.Request) {
595 log.Printf("Workspace Handler: %q\n", r.URL.Path)
596 if r.Method == "GET" {
597 tries := []Try{}
598 match := workspaceLink.FindStringSubmatch(r.URL.Path)
599 name := ""
600 if len(match) == 2 {
601 name = match[1]
commit-bot@chromium.org0a7e5b72014-05-29 15:58:00 +0000602 rows, err := db.Query("SELECT create_ts, hash, source_image_id FROM workspacetry WHERE name=? ORDER BY create_ts", name)
commit-bot@chromium.orgd04e1dd2014-04-19 13:55:50 +0000603 if err != nil {
604 reportError(w, r, err, "Failed to select.")
605 return
606 }
607 for rows.Next() {
608 var hash string
609 var create_ts time.Time
commit-bot@chromium.org0a7e5b72014-05-29 15:58:00 +0000610 var source int
611 if err := rows.Scan(&create_ts, &hash, &source); err != nil {
commit-bot@chromium.orgd04e1dd2014-04-19 13:55:50 +0000612 log.Printf("Error: failed to fetch from database: %q", err)
613 continue
614 }
commit-bot@chromium.org0a7e5b72014-05-29 15:58:00 +0000615 tries = append(tries, Try{Hash: hash, Source: source, CreateTS: create_ts.Format("2006-02-01")})
commit-bot@chromium.orgd04e1dd2014-04-19 13:55:50 +0000616 }
617 }
618 var code string
commit-bot@chromium.org35ffc442014-04-22 19:32:06 +0000619 var hash string
commit-bot@chromium.org0a7e5b72014-05-29 15:58:00 +0000620 source := 0
commit-bot@chromium.orgd04e1dd2014-04-19 13:55:50 +0000621 if len(tries) == 0 {
622 code = DEFAULT_SAMPLE
623 } else {
commit-bot@chromium.org35ffc442014-04-22 19:32:06 +0000624 hash = tries[len(tries)-1].Hash
commit-bot@chromium.org0a7e5b72014-05-29 15:58:00 +0000625 code, source, _ = getCode(hash)
commit-bot@chromium.orgd04e1dd2014-04-19 13:55:50 +0000626 }
commit-bot@chromium.orga4c36772014-05-19 17:38:52 +0000627 w.Header().Set("Content-Type", "text/html")
commit-bot@chromium.org0a7e5b72014-05-29 15:58:00 +0000628 if err := workspaceTemplate.Execute(w, Workspace{Tries: tries, Code: code, Name: name, Hash: hash, Source: source, Titlebar: Titlebar{GitHash: gitHash, GitInfo: gitInfo}}); err != nil {
commit-bot@chromium.orgd04e1dd2014-04-19 13:55:50 +0000629 log.Printf("ERROR: Failed to expand template: %q\n", err)
630 }
631 } else if r.Method == "POST" {
632 name, err := newWorkspace()
633 if err != nil {
634 http.Error(w, "Failed to create a new workspace.", 500)
635 return
636 }
637 http.Redirect(w, r, "/w/"+name, 302)
638 }
639}
640
commit-bot@chromium.org4bd8fdc2014-04-15 00:43:51 +0000641// hasPreProcessor returns true if any line in the code begins with a # char.
642func hasPreProcessor(code string) bool {
643 lines := strings.Split(code, "\n")
644 for _, s := range lines {
645 if strings.HasPrefix(strings.TrimSpace(s), "#") {
646 return true
647 }
648 }
649 return false
650}
651
commit-bot@chromium.orgd04e1dd2014-04-19 13:55:50 +0000652type TryRequest struct {
commit-bot@chromium.org0a7e5b72014-05-29 15:58:00 +0000653 Code string `json:"code"`
654 Name string `json:"name"` // Optional name of the workspace the code is in.
655 Source int `json:"source"` // ID of the source image, 0 if none.
commit-bot@chromium.orgd04e1dd2014-04-19 13:55:50 +0000656}
657
commit-bot@chromium.org2dceeda2014-04-19 14:50:23 +0000658// iframeHandler handles the GET and POST of the main page.
659func iframeHandler(w http.ResponseWriter, r *http.Request) {
660 log.Printf("IFrame Handler: %q\n", r.URL.Path)
661 if r.Method != "GET" {
662 http.NotFound(w, r)
663 return
664 }
665 match := iframeLink.FindStringSubmatch(r.URL.Path)
666 if len(match) != 2 {
667 http.NotFound(w, r)
668 return
669 }
670 hash := match[1]
671 if db == nil {
672 http.NotFound(w, r)
673 return
674 }
675 var code string
commit-bot@chromium.org0a7e5b72014-05-29 15:58:00 +0000676 code, source, err := getCode(hash)
commit-bot@chromium.org35ffc442014-04-22 19:32:06 +0000677 if err != nil {
commit-bot@chromium.org2dceeda2014-04-19 14:50:23 +0000678 http.NotFound(w, r)
679 return
680 }
681 // Expand the template.
commit-bot@chromium.orga4c36772014-05-19 17:38:52 +0000682 w.Header().Set("Content-Type", "text/html")
commit-bot@chromium.org0a7e5b72014-05-29 15:58:00 +0000683 if err := iframeTemplate.Execute(w, userCode{Code: code, Hash: hash, Source: source}); err != nil {
commit-bot@chromium.org2dceeda2014-04-19 14:50:23 +0000684 log.Printf("ERROR: Failed to expand template: %q\n", err)
685 }
686}
687
commit-bot@chromium.org35ffc442014-04-22 19:32:06 +0000688type TryInfo struct {
commit-bot@chromium.org0a7e5b72014-05-29 15:58:00 +0000689 Hash string `json:"hash"`
690 Code string `json:"code"`
691 Source int `json:"source"`
commit-bot@chromium.org35ffc442014-04-22 19:32:06 +0000692}
693
694// tryInfoHandler returns information about a specific try.
695func tryInfoHandler(w http.ResponseWriter, r *http.Request) {
696 log.Printf("Try Info Handler: %q\n", r.URL.Path)
697 if r.Method != "GET" {
698 http.NotFound(w, r)
699 return
700 }
701 match := tryInfoLink.FindStringSubmatch(r.URL.Path)
702 if len(match) != 2 {
703 http.NotFound(w, r)
704 return
705 }
706 hash := match[1]
commit-bot@chromium.org0a7e5b72014-05-29 15:58:00 +0000707 code, source, err := getCode(hash)
commit-bot@chromium.org35ffc442014-04-22 19:32:06 +0000708 if err != nil {
709 http.NotFound(w, r)
710 return
711 }
712 m := TryInfo{
commit-bot@chromium.org0a7e5b72014-05-29 15:58:00 +0000713 Hash: hash,
714 Code: code,
715 Source: source,
commit-bot@chromium.org35ffc442014-04-22 19:32:06 +0000716 }
717 resp, err := json.Marshal(m)
718 if err != nil {
719 reportError(w, r, err, "Failed to serialize a response.")
720 return
721 }
722 w.Header().Set("Content-Type", "application/json")
723 w.Write(resp)
724}
725
commit-bot@chromium.org90041922014-04-22 21:13:45 +0000726func cleanCompileOutput(s, hash string) string {
727 old := "../../../cache/" + hash + ".cpp:"
728 log.Printf("INFO: replacing %q\n", old)
729 return strings.Replace(s, old, "usercode.cpp:", -1)
730}
731
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +0000732// mainHandler handles the GET and POST of the main page.
733func mainHandler(w http.ResponseWriter, r *http.Request) {
commit-bot@chromium.org06aca012014-04-14 20:12:08 +0000734 log.Printf("Main Handler: %q\n", r.URL.Path)
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +0000735 if r.Method == "GET" {
commit-bot@chromium.orgc81d1c42014-04-14 18:53:10 +0000736 code := DEFAULT_SAMPLE
commit-bot@chromium.org0a7e5b72014-05-29 15:58:00 +0000737 source := 0
commit-bot@chromium.orgc81d1c42014-04-14 18:53:10 +0000738 match := directLink.FindStringSubmatch(r.URL.Path)
commit-bot@chromium.org35ffc442014-04-22 19:32:06 +0000739 var hash string
commit-bot@chromium.org06aca012014-04-14 20:12:08 +0000740 if len(match) == 2 && r.URL.Path != "/" {
commit-bot@chromium.org35ffc442014-04-22 19:32:06 +0000741 hash = match[1]
commit-bot@chromium.orgc81d1c42014-04-14 18:53:10 +0000742 if db == nil {
743 http.NotFound(w, r)
744 return
745 }
746 // Update 'code' with the code found in the database.
commit-bot@chromium.org0a7e5b72014-05-29 15:58:00 +0000747 if err := db.QueryRow("SELECT code, source_image_id FROM webtry WHERE hash=?", hash).Scan(&code, &source); err != nil {
commit-bot@chromium.orgc81d1c42014-04-14 18:53:10 +0000748 http.NotFound(w, r)
749 return
750 }
751 }
752 // Expand the template.
commit-bot@chromium.orga4c36772014-05-19 17:38:52 +0000753 w.Header().Set("Content-Type", "text/html")
commit-bot@chromium.org0a7e5b72014-05-29 15:58:00 +0000754 if err := indexTemplate.Execute(w, userCode{Code: code, Hash: hash, Source: source, Titlebar: Titlebar{GitHash: gitHash, GitInfo: gitInfo}}); err != nil {
commit-bot@chromium.orgc81d1c42014-04-14 18:53:10 +0000755 log.Printf("ERROR: Failed to expand template: %q\n", err)
756 }
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +0000757 } else if r.Method == "POST" {
758 w.Header().Set("Content-Type", "application/json")
commit-bot@chromium.org4bd8fdc2014-04-15 00:43:51 +0000759 buf := bytes.NewBuffer(make([]byte, 0, MAX_TRY_SIZE))
760 n, err := buf.ReadFrom(r.Body)
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +0000761 if err != nil {
commit-bot@chromium.org90041922014-04-22 21:13:45 +0000762 reportTryError(w, r, err, "Failed to read a request body.", "")
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +0000763 return
764 }
commit-bot@chromium.org4bd8fdc2014-04-15 00:43:51 +0000765 if n == MAX_TRY_SIZE {
766 err := fmt.Errorf("Code length equal to, or exceeded, %d", MAX_TRY_SIZE)
commit-bot@chromium.org90041922014-04-22 21:13:45 +0000767 reportTryError(w, r, err, "Code too large.", "")
commit-bot@chromium.org4bd8fdc2014-04-15 00:43:51 +0000768 return
769 }
commit-bot@chromium.orgd04e1dd2014-04-19 13:55:50 +0000770 request := TryRequest{}
771 if err := json.Unmarshal(buf.Bytes(), &request); err != nil {
commit-bot@chromium.org90041922014-04-22 21:13:45 +0000772 reportTryError(w, r, err, "Coulnd't decode JSON.", "")
commit-bot@chromium.orgd04e1dd2014-04-19 13:55:50 +0000773 return
774 }
775 if hasPreProcessor(request.Code) {
commit-bot@chromium.org4bd8fdc2014-04-15 00:43:51 +0000776 err := fmt.Errorf("Found preprocessor macro in code.")
commit-bot@chromium.org90041922014-04-22 21:13:45 +0000777 reportTryError(w, r, err, "Preprocessor macros aren't allowed.", "")
commit-bot@chromium.org4bd8fdc2014-04-15 00:43:51 +0000778 return
779 }
commit-bot@chromium.org0a7e5b72014-05-29 15:58:00 +0000780 hash, err := expandCode(LineNumbers(request.Code), request.Source)
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +0000781 if err != nil {
commit-bot@chromium.org90041922014-04-22 21:13:45 +0000782 reportTryError(w, r, err, "Failed to write the code to compile.", hash)
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +0000783 return
784 }
commit-bot@chromium.org0a7e5b72014-05-29 15:58:00 +0000785 writeToDatabase(hash, request.Code, request.Name, request.Source)
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +0000786 message, err := doCmd(fmt.Sprintf(RESULT_COMPILE, hash, hash), true)
787 if err != nil {
commit-bot@chromium.org90041922014-04-22 21:13:45 +0000788 message = cleanCompileOutput(message, hash)
789 reportTryError(w, r, err, message, hash)
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +0000790 return
791 }
792 linkMessage, err := doCmd(fmt.Sprintf(LINK, hash, hash), true)
793 if err != nil {
commit-bot@chromium.org90041922014-04-22 21:13:45 +0000794 linkMessage = cleanCompileOutput(linkMessage, hash)
795 reportTryError(w, r, err, linkMessage, hash)
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +0000796 return
797 }
798 message += linkMessage
799 cmd := hash + " --out " + hash + ".png"
commit-bot@chromium.org0a7e5b72014-05-29 15:58:00 +0000800 if request.Source > 0 {
801 cmd += fmt.Sprintf(" --source image-%d.png", request.Source)
802 }
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +0000803 if *useChroot {
804 cmd = "schroot -c webtry --directory=/inout -- /inout/" + cmd
805 } else {
806 abs, err := filepath.Abs("../../../inout")
807 if err != nil {
commit-bot@chromium.org90041922014-04-22 21:13:45 +0000808 reportTryError(w, r, err, "Failed to find executable directory.", hash)
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +0000809 return
810 }
811 cmd = abs + "/" + cmd
812 }
813
814 execMessage, err := doCmd(cmd, false)
815 if err != nil {
commit-bot@chromium.org90041922014-04-22 21:13:45 +0000816 reportTryError(w, r, err, "Failed to run the code:\n"+execMessage, hash)
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +0000817 return
818 }
819 png, err := ioutil.ReadFile("../../../inout/" + hash + ".png")
820 if err != nil {
commit-bot@chromium.org90041922014-04-22 21:13:45 +0000821 reportTryError(w, r, err, "Failed to open the generated PNG.", hash)
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +0000822 return
823 }
824
825 m := response{
826 Message: message,
commit-bot@chromium.org90041922014-04-22 21:13:45 +0000827 StdOut: execMessage,
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +0000828 Img: base64.StdEncoding.EncodeToString([]byte(png)),
commit-bot@chromium.orgc81d1c42014-04-14 18:53:10 +0000829 Hash: hash,
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +0000830 }
831 resp, err := json.Marshal(m)
832 if err != nil {
commit-bot@chromium.org90041922014-04-22 21:13:45 +0000833 reportTryError(w, r, err, "Failed to serialize a response.", hash)
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +0000834 return
835 }
commit-bot@chromium.orga4c36772014-05-19 17:38:52 +0000836 w.Header().Set("Content-Type", "application/json")
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +0000837 w.Write(resp)
838 }
839}
840
841func main() {
842 flag.Parse()
commit-bot@chromium.orga4c36772014-05-19 17:38:52 +0000843 http.HandleFunc("/i/", autogzip.HandleFunc(imageHandler))
844 http.HandleFunc("/w/", autogzip.HandleFunc(workspaceHandler))
845 http.HandleFunc("/recent/", autogzip.HandleFunc(recentHandler))
846 http.HandleFunc("/iframe/", autogzip.HandleFunc(iframeHandler))
847 http.HandleFunc("/json/", autogzip.HandleFunc(tryInfoHandler))
commit-bot@chromium.org0a7e5b72014-05-29 15:58:00 +0000848 http.HandleFunc("/sources/", autogzip.HandleFunc(sourcesHandler))
fmalita@google.com950306c2014-05-01 15:14:56 +0000849
850 // Resources are served directly
851 // TODO add support for caching/etags/gzip
commit-bot@chromium.orga4c36772014-05-19 17:38:52 +0000852 http.Handle("/res/", autogzip.Handle(http.FileServer(http.Dir("./"))))
fmalita@google.com950306c2014-05-01 15:14:56 +0000853
commit-bot@chromium.org35ffc442014-04-22 19:32:06 +0000854 // TODO Break out /c/ as it's own handler.
commit-bot@chromium.orga4c36772014-05-19 17:38:52 +0000855 http.HandleFunc("/", autogzip.HandleFunc(mainHandler))
commit-bot@chromium.org282333f2014-04-14 14:54:07 +0000856 log.Fatal(http.ListenAndServe(*port, nil))
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +0000857}