blob: 43598fe09286191a817494d7d664ee0a86c08a20 [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`
jcgregorio92bfb092014-06-03 09:04:44 -070038 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/libetc1.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`
39
fmalita@google.com950306c2014-05-01 15:14:56 +000040 DEFAULT_SAMPLE = `void draw(SkCanvas* canvas) {
41 SkPaint p;
42 p.setColor(SK_ColorRED);
43 p.setAntiAlias(true);
44 p.setStyle(SkPaint::kStroke_Style);
45 p.setStrokeWidth(10);
commit-bot@chromium.orgc81d1c42014-04-14 18:53:10 +000046
fmalita@google.com950306c2014-05-01 15:14:56 +000047 canvas->drawLine(20, 20, 100, 100, p);
48}`
commit-bot@chromium.org4bd8fdc2014-04-15 00:43:51 +000049 // Don't increase above 2^16 w/o altering the db tables to accept something bigger than TEXT.
50 MAX_TRY_SIZE = 64000
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +000051)
52
53var (
54 // codeTemplate is the cpp code template the user's code is copied into.
55 codeTemplate *template.Template = nil
56
commit-bot@chromium.org06aca012014-04-14 20:12:08 +000057 // indexTemplate is the main index.html page we serve.
58 indexTemplate *htemplate.Template = nil
59
commit-bot@chromium.org2dceeda2014-04-19 14:50:23 +000060 // iframeTemplate is the main index.html page we serve.
61 iframeTemplate *htemplate.Template = nil
62
commit-bot@chromium.orgd04e1dd2014-04-19 13:55:50 +000063 // recentTemplate is a list of recent images.
commit-bot@chromium.org06aca012014-04-14 20:12:08 +000064 recentTemplate *htemplate.Template = nil
commit-bot@chromium.org282333f2014-04-14 14:54:07 +000065
commit-bot@chromium.orgd04e1dd2014-04-19 13:55:50 +000066 // workspaceTemplate is the page for workspaces, a series of webtrys.
67 workspaceTemplate *htemplate.Template = nil
68
commit-bot@chromium.org282333f2014-04-14 14:54:07 +000069 // db is the database, nil if we don't have an SQL database to store data into.
70 db *sql.DB = nil
commit-bot@chromium.orgc81d1c42014-04-14 18:53:10 +000071
72 // directLink is the regex that matches URLs paths that are direct links.
commit-bot@chromium.org06aca012014-04-14 20:12:08 +000073 directLink = regexp.MustCompile("^/c/([a-f0-9]+)$")
74
commit-bot@chromium.org2dceeda2014-04-19 14:50:23 +000075 // iframeLink is the regex that matches URLs paths that are links to iframes.
76 iframeLink = regexp.MustCompile("^/iframe/([a-f0-9]+)$")
77
commit-bot@chromium.org06aca012014-04-14 20:12:08 +000078 // imageLink is the regex that matches URLs paths that are direct links to PNGs.
commit-bot@chromium.org0a7e5b72014-05-29 15:58:00 +000079 imageLink = regexp.MustCompile("^/i/([a-z0-9-]+.png)$")
commit-bot@chromium.orgd04e1dd2014-04-19 13:55:50 +000080
commit-bot@chromium.org35ffc442014-04-22 19:32:06 +000081 // tryInfoLink is the regex that matches URLs paths that are direct links to data about a single try.
82 tryInfoLink = regexp.MustCompile("^/json/([a-f0-9]+)$")
83
commit-bot@chromium.orgd04e1dd2014-04-19 13:55:50 +000084 // workspaceLink is the regex that matches URLs paths for workspaces.
85 workspaceLink = regexp.MustCompile("^/w/([a-z0-9-]+)$")
86
87 // workspaceNameAdj is a list of adjectives for building workspace names.
88 workspaceNameAdj = []string{
89 "autumn", "hidden", "bitter", "misty", "silent", "empty", "dry", "dark",
90 "summer", "icy", "delicate", "quiet", "white", "cool", "spring", "winter",
91 "patient", "twilight", "dawn", "crimson", "wispy", "weathered", "blue",
92 "billowing", "broken", "cold", "damp", "falling", "frosty", "green",
93 "long", "late", "lingering", "bold", "little", "morning", "muddy", "old",
94 "red", "rough", "still", "small", "sparkling", "throbbing", "shy",
95 "wandering", "withered", "wild", "black", "young", "holy", "solitary",
96 "fragrant", "aged", "snowy", "proud", "floral", "restless", "divine",
97 "polished", "ancient", "purple", "lively", "nameless",
98 }
99
100 // workspaceNameNoun is a list of nouns for building workspace names.
101 workspaceNameNoun = []string{
102 "waterfall", "river", "breeze", "moon", "rain", "wind", "sea", "morning",
103 "snow", "lake", "sunset", "pine", "shadow", "leaf", "dawn", "glitter",
104 "forest", "hill", "cloud", "meadow", "sun", "glade", "bird", "brook",
105 "butterfly", "bush", "dew", "dust", "field", "fire", "flower", "firefly",
106 "feather", "grass", "haze", "mountain", "night", "pond", "darkness",
107 "snowflake", "silence", "sound", "sky", "shape", "surf", "thunder",
108 "violet", "water", "wildflower", "wave", "water", "resonance", "sun",
109 "wood", "dream", "cherry", "tree", "fog", "frost", "voice", "paper",
110 "frog", "smoke", "star",
111 }
commit-bot@chromium.org472f8302014-04-28 15:33:31 +0000112
113 gitHash = ""
114 gitInfo = ""
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +0000115)
116
117// flags
118var (
119 useChroot = flag.Bool("use_chroot", false, "Run the compiled code in the schroot jail.")
commit-bot@chromium.org282333f2014-04-14 14:54:07 +0000120 port = flag.String("port", ":8000", "HTTP service address (e.g., ':8000')")
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +0000121)
122
123// lineNumbers adds #line numbering to the user's code.
124func LineNumbers(c string) string {
125 lines := strings.Split(c, "\n")
126 ret := []string{}
127 for i, line := range lines {
128 ret = append(ret, fmt.Sprintf("#line %d", i+1))
129 ret = append(ret, line)
130 }
131 return strings.Join(ret, "\n")
132}
133
134func init() {
commit-bot@chromium.org2dceeda2014-04-19 14:50:23 +0000135 rand.Seed(time.Now().UnixNano())
commit-bot@chromium.orgc81d1c42014-04-14 18:53:10 +0000136
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +0000137 // Change the current working directory to the directory of the executable.
138 var err error
139 cwd, err := filepath.Abs(filepath.Dir(os.Args[0]))
140 if err != nil {
141 log.Fatal(err)
142 }
143 os.Chdir(cwd)
144
145 codeTemplate, err = template.ParseFiles(filepath.Join(cwd, "templates/template.cpp"))
146 if err != nil {
147 panic(err)
148 }
commit-bot@chromium.orgd04e1dd2014-04-19 13:55:50 +0000149 indexTemplate, err = htemplate.ParseFiles(
150 filepath.Join(cwd, "templates/index.html"),
151 filepath.Join(cwd, "templates/titlebar.html"),
commit-bot@chromium.org90041922014-04-22 21:13:45 +0000152 filepath.Join(cwd, "templates/content.html"),
commit-bot@chromium.org015c08e2014-05-19 16:40:08 +0000153 filepath.Join(cwd, "templates/headercommon.html"),
154 filepath.Join(cwd, "templates/footercommon.html"),
commit-bot@chromium.orgd04e1dd2014-04-19 13:55:50 +0000155 )
commit-bot@chromium.org06aca012014-04-14 20:12:08 +0000156 if err != nil {
157 panic(err)
158 }
commit-bot@chromium.org2dceeda2014-04-19 14:50:23 +0000159 iframeTemplate, err = htemplate.ParseFiles(
160 filepath.Join(cwd, "templates/iframe.html"),
commit-bot@chromium.org90041922014-04-22 21:13:45 +0000161 filepath.Join(cwd, "templates/content.html"),
commit-bot@chromium.org015c08e2014-05-19 16:40:08 +0000162 filepath.Join(cwd, "templates/headercommon.html"),
163 filepath.Join(cwd, "templates/footercommon.html"),
commit-bot@chromium.org2dceeda2014-04-19 14:50:23 +0000164 )
165 if err != nil {
166 panic(err)
167 }
commit-bot@chromium.orgd04e1dd2014-04-19 13:55:50 +0000168 recentTemplate, err = htemplate.ParseFiles(
169 filepath.Join(cwd, "templates/recent.html"),
170 filepath.Join(cwd, "templates/titlebar.html"),
commit-bot@chromium.org015c08e2014-05-19 16:40:08 +0000171 filepath.Join(cwd, "templates/headercommon.html"),
172 filepath.Join(cwd, "templates/footercommon.html"),
commit-bot@chromium.orgd04e1dd2014-04-19 13:55:50 +0000173 )
174 if err != nil {
175 panic(err)
176 }
177 workspaceTemplate, err = htemplate.ParseFiles(
178 filepath.Join(cwd, "templates/workspace.html"),
179 filepath.Join(cwd, "templates/titlebar.html"),
commit-bot@chromium.org90041922014-04-22 21:13:45 +0000180 filepath.Join(cwd, "templates/content.html"),
commit-bot@chromium.org015c08e2014-05-19 16:40:08 +0000181 filepath.Join(cwd, "templates/headercommon.html"),
182 filepath.Join(cwd, "templates/footercommon.html"),
commit-bot@chromium.orgd04e1dd2014-04-19 13:55:50 +0000183 )
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +0000184 if err != nil {
185 panic(err)
186 }
commit-bot@chromium.org282333f2014-04-14 14:54:07 +0000187
commit-bot@chromium.org472f8302014-04-28 15:33:31 +0000188 // The git command returns output of the format:
189 //
190 // f672cead70404080a991ebfb86c38316a4589b23 2014-04-27 19:21:51 +0000
191 //
192 logOutput, err := doCmd(`git log --format=%H%x20%ai HEAD^..HEAD`, true)
193 if err != nil {
194 panic(err)
195 }
196 logInfo := strings.Split(logOutput, " ")
197 gitHash = logInfo[0]
198 gitInfo = logInfo[1] + " " + logInfo[2] + " " + logInfo[0][0:6]
199
commit-bot@chromium.org282333f2014-04-14 14:54:07 +0000200 // Connect to MySQL server. First, get the password from the metadata server.
201 // See https://developers.google.com/compute/docs/metadata#custom.
202 req, err := http.NewRequest("GET", "http://metadata/computeMetadata/v1/instance/attributes/password", nil)
203 if err != nil {
204 panic(err)
205 }
206 client := http.Client{}
207 req.Header.Add("X-Google-Metadata-Request", "True")
208 if resp, err := client.Do(req); err == nil {
209 password, err := ioutil.ReadAll(resp.Body)
210 if err != nil {
211 log.Printf("ERROR: Failed to read password from metadata server: %q\n", err)
212 panic(err)
213 }
214 // The IP address of the database is found here:
215 // https://console.developers.google.com/project/31977622648/sql/instances/webtry/overview
216 // And 3306 is the default port for MySQL.
commit-bot@chromium.org4bd8fdc2014-04-15 00:43:51 +0000217 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 +0000218 if err != nil {
219 log.Printf("ERROR: Failed to open connection to SQL server: %q\n", err)
220 panic(err)
221 }
222 } else {
commit-bot@chromium.orgd04e1dd2014-04-19 13:55:50 +0000223 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 +0000224 // Fallback to sqlite for local use.
225 db, err = sql.Open("sqlite3", "./webtry.db")
226 if err != nil {
227 log.Printf("ERROR: Failed to open: %q\n", err)
228 panic(err)
229 }
commit-bot@chromium.org0a7e5b72014-05-29 15:58:00 +0000230 sql := `CREATE TABLE source_images (
231 id INTEGER PRIMARY KEY NOT NULL,
232 image MEDIUMBLOB DEFAULT '' NOT NULL, -- formatted as a PNG.
233 width INTEGER DEFAULT 0 NOT NULL,
234 height INTEGER DEFAULT 0 NOT NULL,
235 create_ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
236 hidden INTEGER DEFAULT 0 NOT NULL
237 )`
238 _, err = db.Exec(sql)
239 log.Printf("Info: status creating sqlite table for sources: %q\n", err)
240
241 sql = `CREATE TABLE webtry (
242 code TEXT DEFAULT '' NOT NULL,
243 create_ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
244 hash CHAR(64) DEFAULT '' NOT NULL,
245 source_image_id INTEGER DEFAULT 0 NOT NULL,
246
commit-bot@chromium.orgc81d1c42014-04-14 18:53:10 +0000247 PRIMARY KEY(hash)
248 )`
commit-bot@chromium.orgd04e1dd2014-04-19 13:55:50 +0000249 _, err = db.Exec(sql)
250 log.Printf("Info: status creating sqlite table for webtry: %q\n", err)
commit-bot@chromium.org0a7e5b72014-05-29 15:58:00 +0000251
commit-bot@chromium.orgd04e1dd2014-04-19 13:55:50 +0000252 sql = `CREATE TABLE workspace (
253 name CHAR(64) DEFAULT '' NOT NULL,
254 create_ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
255 PRIMARY KEY(name)
256 )`
257 _, err = db.Exec(sql)
258 log.Printf("Info: status creating sqlite table for workspace: %q\n", err)
commit-bot@chromium.orgd04e1dd2014-04-19 13:55:50 +0000259
commit-bot@chromium.org0a7e5b72014-05-29 15:58:00 +0000260 sql = `CREATE TABLE workspacetry (
261 name CHAR(64) DEFAULT '' NOT NULL,
262 create_ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
263 hash CHAR(64) DEFAULT '' NOT NULL,
264 hidden INTEGER DEFAULT 0 NOT NULL,
265 source_image_id INTEGER DEFAULT 0 NOT NULL,
266
267 FOREIGN KEY (name) REFERENCES workspace(name)
commit-bot@chromium.orgd04e1dd2014-04-19 13:55:50 +0000268 )`
269 _, err = db.Exec(sql)
270 log.Printf("Info: status creating sqlite table for workspace try: %q\n", err)
commit-bot@chromium.org282333f2014-04-14 14:54:07 +0000271 }
commit-bot@chromium.org9f3b9252014-05-14 12:55:34 +0000272
273 // Ping the database to keep the connection fresh.
274 go func() {
275 c := time.Tick(1 * time.Minute)
276 for _ = range c {
277 if err := db.Ping(); err != nil {
278 log.Printf("ERROR: Database failed to respond: %q\n", err)
279 }
280 }
281 }()
282
commit-bot@chromium.org0a7e5b72014-05-29 15:58:00 +0000283 writeOutAllSourceImages()
284}
285
286func writeOutAllSourceImages() {
287 // Pull all the source images from the db and write them out to inout.
288 rows, err := db.Query("SELECT id, image, create_ts FROM source_images ORDER BY create_ts DESC")
289
290 if err != nil {
291 log.Printf("ERROR: Failed to open connection to SQL server: %q\n", err)
292 panic(err)
293 }
294 for rows.Next() {
295 var id int
296 var image []byte
297 var create_ts time.Time
298 if err := rows.Scan(&id, &image, &create_ts); err != nil {
299 log.Printf("Error: failed to fetch from database: %q", err)
300 continue
301 }
302 filename := fmt.Sprintf("../../../inout/image-%d.png", id)
303 if _, err := os.Stat(filename); os.IsExist(err) {
304 log.Printf("Skipping write since file exists: %q", filename)
305 continue
306 }
307 if err := ioutil.WriteFile(filename, image, 0666); err != nil {
308 log.Printf("Error: failed to write image file: %q", err)
309 }
310 }
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +0000311}
312
commit-bot@chromium.org472f8302014-04-28 15:33:31 +0000313// Titlebar is used in titlebar template expansion.
314type Titlebar struct {
315 GitHash string
316 GitInfo string
317}
318
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +0000319// userCode is used in template expansion.
320type userCode struct {
commit-bot@chromium.org472f8302014-04-28 15:33:31 +0000321 Code string
322 Hash string
commit-bot@chromium.org0a7e5b72014-05-29 15:58:00 +0000323 Source int
commit-bot@chromium.org472f8302014-04-28 15:33:31 +0000324 Titlebar Titlebar
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +0000325}
326
327// expandToFile expands the template and writes the result to the file.
328func expandToFile(filename string, code string, t *template.Template) error {
329 f, err := os.Create(filename)
330 if err != nil {
331 return err
332 }
333 defer f.Close()
commit-bot@chromium.org472f8302014-04-28 15:33:31 +0000334 return t.Execute(f, userCode{Code: code, Titlebar: Titlebar{GitHash: gitHash, GitInfo: gitInfo}})
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +0000335}
336
commit-bot@chromium.org0a7e5b72014-05-29 15:58:00 +0000337// expandCode expands the template into a file and calculates the MD5 hash.
338func expandCode(code string, source int) (string, error) {
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +0000339 h := md5.New()
340 h.Write([]byte(code))
commit-bot@chromium.org0a7e5b72014-05-29 15:58:00 +0000341 binary.Write(h, binary.LittleEndian, int64(source))
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +0000342 hash := fmt.Sprintf("%x", h.Sum(nil))
343 // At this point we are running in skia/experimental/webtry, making cache a
344 // peer directory to skia.
345 // TODO(jcgregorio) Make all relative directories into flags.
346 err := expandToFile(fmt.Sprintf("../../../cache/%s.cpp", hash), code, codeTemplate)
347 return hash, err
348}
349
350// response is serialized to JSON as a response to POSTs.
351type response struct {
352 Message string `json:"message"`
commit-bot@chromium.org90041922014-04-22 21:13:45 +0000353 StdOut string `json:"stdout"`
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +0000354 Img string `json:"img"`
commit-bot@chromium.orgc81d1c42014-04-14 18:53:10 +0000355 Hash string `json:"hash"`
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +0000356}
357
358// doCmd executes the given command line string in either the out/Debug
commit-bot@chromium.org15b29812014-04-28 14:56:32 +0000359// directory or the inout directory. Returns the stdout and stderr.
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +0000360func doCmd(commandLine string, moveToDebug bool) (string, error) {
361 log.Printf("Command: %q\n", commandLine)
362 programAndArgs := strings.SplitN(commandLine, " ", 2)
363 program := programAndArgs[0]
364 args := []string{}
365 if len(programAndArgs) > 1 {
366 args = strings.Split(programAndArgs[1], " ")
367 }
368 cmd := exec.Command(program, args...)
369 abs, err := filepath.Abs("../../out/Debug")
370 if err != nil {
371 return "", fmt.Errorf("Failed to find absolute path to Debug directory.")
372 }
373 if moveToDebug {
374 cmd.Dir = abs
375 } else if !*useChroot { // Don't set cmd.Dir when using chroot.
376 abs, err := filepath.Abs("../../../inout")
377 if err != nil {
378 return "", fmt.Errorf("Failed to find absolute path to inout directory.")
379 }
380 cmd.Dir = abs
381 }
382 log.Printf("Run in directory: %q\n", cmd.Dir)
commit-bot@chromium.org15b29812014-04-28 14:56:32 +0000383 message, err := cmd.CombinedOutput()
384 log.Printf("StdOut + StdErr: %s\n", string(message))
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +0000385 if err != nil {
386 log.Printf("Exit status: %s\n", err.Error())
commit-bot@chromium.org15b29812014-04-28 14:56:32 +0000387 return string(message), fmt.Errorf("Failed to run command.")
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +0000388 }
commit-bot@chromium.org15b29812014-04-28 14:56:32 +0000389 return string(message), nil
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +0000390}
391
392// reportError formats an HTTP error response and also logs the detailed error message.
393func reportError(w http.ResponseWriter, r *http.Request, err error, message string) {
commit-bot@chromium.org90041922014-04-22 21:13:45 +0000394 log.Printf("Error: %s\n%s", message, err.Error())
commit-bot@chromium.orga4c36772014-05-19 17:38:52 +0000395 w.Header().Set("Content-Type", "text/plain")
commit-bot@chromium.org90041922014-04-22 21:13:45 +0000396 http.Error(w, message, 500)
397}
398
399// reportTryError formats an HTTP error response in JSON and also logs the detailed error message.
400func reportTryError(w http.ResponseWriter, r *http.Request, err error, message, hash string) {
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +0000401 m := response{
402 Message: message,
commit-bot@chromium.org90041922014-04-22 21:13:45 +0000403 Hash: hash,
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +0000404 }
405 log.Printf("Error: %s\n%s", message, err.Error())
406 resp, err := json.Marshal(m)
407 if err != nil {
408 http.Error(w, "Failed to serialize a response", 500)
409 return
410 }
commit-bot@chromium.orga4c36772014-05-19 17:38:52 +0000411 w.Header().Set("Content-Type", "text/plain")
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +0000412 w.Write(resp)
413}
414
commit-bot@chromium.org0a7e5b72014-05-29 15:58:00 +0000415func writeToDatabase(hash string, code string, workspaceName string, source int) {
commit-bot@chromium.org282333f2014-04-14 14:54:07 +0000416 if db == nil {
417 return
418 }
commit-bot@chromium.org0a7e5b72014-05-29 15:58:00 +0000419 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 +0000420 log.Printf("ERROR: Failed to insert code into database: %q\n", err)
421 }
commit-bot@chromium.orgd04e1dd2014-04-19 13:55:50 +0000422 if workspaceName != "" {
commit-bot@chromium.org0a7e5b72014-05-29 15:58:00 +0000423 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 +0000424 log.Printf("ERROR: Failed to insert into workspacetry table: %q\n", err)
425 }
426 }
commit-bot@chromium.org282333f2014-04-14 14:54:07 +0000427}
428
commit-bot@chromium.org0a7e5b72014-05-29 15:58:00 +0000429type Sources struct {
430 Id int `json:"id"`
431}
432
433// sourcesHandler serves up the PNG of a specific try.
434func sourcesHandler(w http.ResponseWriter, r *http.Request) {
435 log.Printf("Sources Handler: %q\n", r.URL.Path)
436 if r.Method == "GET" {
437 rows, err := db.Query("SELECT id, create_ts FROM source_images WHERE hidden=0 ORDER BY create_ts DESC")
438
439 if err != nil {
440 http.Error(w, fmt.Sprintf("Failed to query sources: %s.", err), 500)
441 }
442 sources := make([]Sources, 0, 0)
443 for rows.Next() {
444 var id int
445 var create_ts time.Time
446 if err := rows.Scan(&id, &create_ts); err != nil {
447 log.Printf("Error: failed to fetch from database: %q", err)
448 continue
449 }
450 sources = append(sources, Sources{Id: id})
451 }
452
453 resp, err := json.Marshal(sources)
454 if err != nil {
455 reportError(w, r, err, "Failed to serialize a response.")
456 return
457 }
458 w.Header().Set("Content-Type", "application/json")
459 w.Write(resp)
460
461 } else if r.Method == "POST" {
462 if err := r.ParseMultipartForm(1000000); err != nil {
463 http.Error(w, fmt.Sprintf("Failed to load image: %s.", err), 500)
464 return
465 }
466 if _, ok := r.MultipartForm.File["upload"]; !ok {
467 http.Error(w, "Invalid upload.", 500)
468 return
469 }
470 if len(r.MultipartForm.File["upload"]) != 1 {
471 http.Error(w, "Wrong number of uploads.", 500)
472 return
473 }
474 f, err := r.MultipartForm.File["upload"][0].Open()
475 if err != nil {
476 http.Error(w, fmt.Sprintf("Failed to load image: %s.", err), 500)
477 return
478 }
479 defer f.Close()
480 m, _, err := image.Decode(f)
481 if err != nil {
482 http.Error(w, fmt.Sprintf("Failed to decode image: %s.", err), 500)
483 return
484 }
485 var b bytes.Buffer
486 png.Encode(&b, m)
487 bounds := m.Bounds()
488 width := bounds.Max.Y - bounds.Min.Y
489 height := bounds.Max.X - bounds.Min.X
490 if _, err := db.Exec("INSERT INTO source_images (image, width, height) VALUES(?, ?, ?)", b.Bytes(), width, height); err != nil {
491 log.Printf("ERROR: Failed to insert sources into database: %q\n", err)
492 http.Error(w, fmt.Sprintf("Failed to store image: %s.", err), 500)
493 return
494 }
495 go writeOutAllSourceImages()
496
497 // Now redirect back to where we came from.
498 http.Redirect(w, r, r.Referer(), 302)
499 } else {
500 http.NotFound(w, r)
501 return
502 }
503}
504
commit-bot@chromium.org06aca012014-04-14 20:12:08 +0000505// imageHandler serves up the PNG of a specific try.
506func imageHandler(w http.ResponseWriter, r *http.Request) {
507 log.Printf("Image Handler: %q\n", r.URL.Path)
508 if r.Method != "GET" {
509 http.NotFound(w, r)
510 return
511 }
512 match := imageLink.FindStringSubmatch(r.URL.Path)
513 if len(match) != 2 {
514 http.NotFound(w, r)
515 return
516 }
517 filename := match[1]
commit-bot@chromium.orga4c36772014-05-19 17:38:52 +0000518 w.Header().Set("Content-Type", "image/png")
commit-bot@chromium.org06aca012014-04-14 20:12:08 +0000519 http.ServeFile(w, r, fmt.Sprintf("../../../inout/%s", filename))
520}
521
522type Try struct {
commit-bot@chromium.orgc3b738a2014-04-21 17:36:44 +0000523 Hash string `json:"hash"`
commit-bot@chromium.org0a7e5b72014-05-29 15:58:00 +0000524 Source int
commit-bot@chromium.orgc3b738a2014-04-21 17:36:44 +0000525 CreateTS string `json:"create_ts"`
commit-bot@chromium.org06aca012014-04-14 20:12:08 +0000526}
527
528type Recent struct {
commit-bot@chromium.org472f8302014-04-28 15:33:31 +0000529 Tries []Try
530 Titlebar Titlebar
commit-bot@chromium.org06aca012014-04-14 20:12:08 +0000531}
532
533// recentHandler shows the last 20 tries.
534func recentHandler(w http.ResponseWriter, r *http.Request) {
535 log.Printf("Recent Handler: %q\n", r.URL.Path)
536
537 var err error
538 rows, err := db.Query("SELECT create_ts, hash FROM webtry ORDER BY create_ts DESC LIMIT 20")
539 if err != nil {
540 http.NotFound(w, r)
541 return
542 }
543 recent := []Try{}
544 for rows.Next() {
545 var hash string
546 var create_ts time.Time
547 if err := rows.Scan(&create_ts, &hash); err != nil {
548 log.Printf("Error: failed to fetch from database: %q", err)
549 continue
550 }
551 recent = append(recent, Try{Hash: hash, CreateTS: create_ts.Format("2006-02-01")})
552 }
commit-bot@chromium.orga4c36772014-05-19 17:38:52 +0000553 w.Header().Set("Content-Type", "text/html")
commit-bot@chromium.org472f8302014-04-28 15:33:31 +0000554 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 +0000555 log.Printf("ERROR: Failed to expand template: %q\n", err)
556 }
557}
558
commit-bot@chromium.orgd04e1dd2014-04-19 13:55:50 +0000559type Workspace struct {
commit-bot@chromium.org472f8302014-04-28 15:33:31 +0000560 Name string
561 Code string
562 Hash string
commit-bot@chromium.org0a7e5b72014-05-29 15:58:00 +0000563 Source int
commit-bot@chromium.org472f8302014-04-28 15:33:31 +0000564 Tries []Try
565 Titlebar Titlebar
commit-bot@chromium.orgd04e1dd2014-04-19 13:55:50 +0000566}
567
568// newWorkspace generates a new random workspace name and stores it in the database.
569func newWorkspace() (string, error) {
570 for i := 0; i < 10; i++ {
571 adj := workspaceNameAdj[rand.Intn(len(workspaceNameAdj))]
572 noun := workspaceNameNoun[rand.Intn(len(workspaceNameNoun))]
573 suffix := rand.Intn(1000)
574 name := fmt.Sprintf("%s-%s-%d", adj, noun, suffix)
575 if _, err := db.Exec("INSERT INTO workspace (name) VALUES(?)", name); err == nil {
576 return name, nil
577 } else {
578 log.Printf("ERROR: Failed to insert workspace into database: %q\n", err)
579 }
580 }
581 return "", fmt.Errorf("Failed to create a new workspace")
582}
583
584// getCode returns the code for a given hash, or the empty string if not found.
commit-bot@chromium.org0a7e5b72014-05-29 15:58:00 +0000585func getCode(hash string) (string, int, error) {
commit-bot@chromium.orgd04e1dd2014-04-19 13:55:50 +0000586 code := ""
commit-bot@chromium.org0a7e5b72014-05-29 15:58:00 +0000587 source := 0
588 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 +0000589 log.Printf("ERROR: Code for hash is missing: %q\n", err)
commit-bot@chromium.org0a7e5b72014-05-29 15:58:00 +0000590 return code, source, err
commit-bot@chromium.orgd04e1dd2014-04-19 13:55:50 +0000591 }
commit-bot@chromium.org0a7e5b72014-05-29 15:58:00 +0000592 return code, source, nil
commit-bot@chromium.orgd04e1dd2014-04-19 13:55:50 +0000593}
594
595func workspaceHandler(w http.ResponseWriter, r *http.Request) {
596 log.Printf("Workspace Handler: %q\n", r.URL.Path)
597 if r.Method == "GET" {
598 tries := []Try{}
599 match := workspaceLink.FindStringSubmatch(r.URL.Path)
600 name := ""
601 if len(match) == 2 {
602 name = match[1]
commit-bot@chromium.org0a7e5b72014-05-29 15:58:00 +0000603 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 +0000604 if err != nil {
605 reportError(w, r, err, "Failed to select.")
606 return
607 }
608 for rows.Next() {
609 var hash string
610 var create_ts time.Time
commit-bot@chromium.org0a7e5b72014-05-29 15:58:00 +0000611 var source int
612 if err := rows.Scan(&create_ts, &hash, &source); err != nil {
commit-bot@chromium.orgd04e1dd2014-04-19 13:55:50 +0000613 log.Printf("Error: failed to fetch from database: %q", err)
614 continue
615 }
commit-bot@chromium.org0a7e5b72014-05-29 15:58:00 +0000616 tries = append(tries, Try{Hash: hash, Source: source, CreateTS: create_ts.Format("2006-02-01")})
commit-bot@chromium.orgd04e1dd2014-04-19 13:55:50 +0000617 }
618 }
619 var code string
commit-bot@chromium.org35ffc442014-04-22 19:32:06 +0000620 var hash string
commit-bot@chromium.org0a7e5b72014-05-29 15:58:00 +0000621 source := 0
commit-bot@chromium.orgd04e1dd2014-04-19 13:55:50 +0000622 if len(tries) == 0 {
623 code = DEFAULT_SAMPLE
624 } else {
commit-bot@chromium.org35ffc442014-04-22 19:32:06 +0000625 hash = tries[len(tries)-1].Hash
commit-bot@chromium.org0a7e5b72014-05-29 15:58:00 +0000626 code, source, _ = getCode(hash)
commit-bot@chromium.orgd04e1dd2014-04-19 13:55:50 +0000627 }
commit-bot@chromium.orga4c36772014-05-19 17:38:52 +0000628 w.Header().Set("Content-Type", "text/html")
commit-bot@chromium.org0a7e5b72014-05-29 15:58:00 +0000629 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 +0000630 log.Printf("ERROR: Failed to expand template: %q\n", err)
631 }
632 } else if r.Method == "POST" {
633 name, err := newWorkspace()
634 if err != nil {
635 http.Error(w, "Failed to create a new workspace.", 500)
636 return
637 }
638 http.Redirect(w, r, "/w/"+name, 302)
639 }
640}
641
commit-bot@chromium.org4bd8fdc2014-04-15 00:43:51 +0000642// hasPreProcessor returns true if any line in the code begins with a # char.
643func hasPreProcessor(code string) bool {
644 lines := strings.Split(code, "\n")
645 for _, s := range lines {
646 if strings.HasPrefix(strings.TrimSpace(s), "#") {
647 return true
648 }
649 }
650 return false
651}
652
commit-bot@chromium.orgd04e1dd2014-04-19 13:55:50 +0000653type TryRequest struct {
commit-bot@chromium.org0a7e5b72014-05-29 15:58:00 +0000654 Code string `json:"code"`
655 Name string `json:"name"` // Optional name of the workspace the code is in.
656 Source int `json:"source"` // ID of the source image, 0 if none.
commit-bot@chromium.orgd04e1dd2014-04-19 13:55:50 +0000657}
658
commit-bot@chromium.org2dceeda2014-04-19 14:50:23 +0000659// iframeHandler handles the GET and POST of the main page.
660func iframeHandler(w http.ResponseWriter, r *http.Request) {
661 log.Printf("IFrame Handler: %q\n", r.URL.Path)
662 if r.Method != "GET" {
663 http.NotFound(w, r)
664 return
665 }
666 match := iframeLink.FindStringSubmatch(r.URL.Path)
667 if len(match) != 2 {
668 http.NotFound(w, r)
669 return
670 }
671 hash := match[1]
672 if db == nil {
673 http.NotFound(w, r)
674 return
675 }
676 var code string
commit-bot@chromium.org0a7e5b72014-05-29 15:58:00 +0000677 code, source, err := getCode(hash)
commit-bot@chromium.org35ffc442014-04-22 19:32:06 +0000678 if err != nil {
commit-bot@chromium.org2dceeda2014-04-19 14:50:23 +0000679 http.NotFound(w, r)
680 return
681 }
682 // Expand the template.
commit-bot@chromium.orga4c36772014-05-19 17:38:52 +0000683 w.Header().Set("Content-Type", "text/html")
commit-bot@chromium.org0a7e5b72014-05-29 15:58:00 +0000684 if err := iframeTemplate.Execute(w, userCode{Code: code, Hash: hash, Source: source}); err != nil {
commit-bot@chromium.org2dceeda2014-04-19 14:50:23 +0000685 log.Printf("ERROR: Failed to expand template: %q\n", err)
686 }
687}
688
commit-bot@chromium.org35ffc442014-04-22 19:32:06 +0000689type TryInfo struct {
commit-bot@chromium.org0a7e5b72014-05-29 15:58:00 +0000690 Hash string `json:"hash"`
691 Code string `json:"code"`
692 Source int `json:"source"`
commit-bot@chromium.org35ffc442014-04-22 19:32:06 +0000693}
694
695// tryInfoHandler returns information about a specific try.
696func tryInfoHandler(w http.ResponseWriter, r *http.Request) {
697 log.Printf("Try Info Handler: %q\n", r.URL.Path)
698 if r.Method != "GET" {
699 http.NotFound(w, r)
700 return
701 }
702 match := tryInfoLink.FindStringSubmatch(r.URL.Path)
703 if len(match) != 2 {
704 http.NotFound(w, r)
705 return
706 }
707 hash := match[1]
commit-bot@chromium.org0a7e5b72014-05-29 15:58:00 +0000708 code, source, err := getCode(hash)
commit-bot@chromium.org35ffc442014-04-22 19:32:06 +0000709 if err != nil {
710 http.NotFound(w, r)
711 return
712 }
713 m := TryInfo{
commit-bot@chromium.org0a7e5b72014-05-29 15:58:00 +0000714 Hash: hash,
715 Code: code,
716 Source: source,
commit-bot@chromium.org35ffc442014-04-22 19:32:06 +0000717 }
718 resp, err := json.Marshal(m)
719 if err != nil {
720 reportError(w, r, err, "Failed to serialize a response.")
721 return
722 }
723 w.Header().Set("Content-Type", "application/json")
724 w.Write(resp)
725}
726
commit-bot@chromium.org90041922014-04-22 21:13:45 +0000727func cleanCompileOutput(s, hash string) string {
728 old := "../../../cache/" + hash + ".cpp:"
729 log.Printf("INFO: replacing %q\n", old)
730 return strings.Replace(s, old, "usercode.cpp:", -1)
731}
732
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +0000733// mainHandler handles the GET and POST of the main page.
734func mainHandler(w http.ResponseWriter, r *http.Request) {
commit-bot@chromium.org06aca012014-04-14 20:12:08 +0000735 log.Printf("Main Handler: %q\n", r.URL.Path)
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +0000736 if r.Method == "GET" {
commit-bot@chromium.orgc81d1c42014-04-14 18:53:10 +0000737 code := DEFAULT_SAMPLE
commit-bot@chromium.org0a7e5b72014-05-29 15:58:00 +0000738 source := 0
commit-bot@chromium.orgc81d1c42014-04-14 18:53:10 +0000739 match := directLink.FindStringSubmatch(r.URL.Path)
commit-bot@chromium.org35ffc442014-04-22 19:32:06 +0000740 var hash string
commit-bot@chromium.org06aca012014-04-14 20:12:08 +0000741 if len(match) == 2 && r.URL.Path != "/" {
commit-bot@chromium.org35ffc442014-04-22 19:32:06 +0000742 hash = match[1]
commit-bot@chromium.orgc81d1c42014-04-14 18:53:10 +0000743 if db == nil {
744 http.NotFound(w, r)
745 return
746 }
747 // Update 'code' with the code found in the database.
commit-bot@chromium.org0a7e5b72014-05-29 15:58:00 +0000748 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 +0000749 http.NotFound(w, r)
750 return
751 }
752 }
753 // Expand the template.
commit-bot@chromium.orga4c36772014-05-19 17:38:52 +0000754 w.Header().Set("Content-Type", "text/html")
commit-bot@chromium.org0a7e5b72014-05-29 15:58:00 +0000755 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 +0000756 log.Printf("ERROR: Failed to expand template: %q\n", err)
757 }
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +0000758 } else if r.Method == "POST" {
759 w.Header().Set("Content-Type", "application/json")
commit-bot@chromium.org4bd8fdc2014-04-15 00:43:51 +0000760 buf := bytes.NewBuffer(make([]byte, 0, MAX_TRY_SIZE))
761 n, err := buf.ReadFrom(r.Body)
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +0000762 if err != nil {
commit-bot@chromium.org90041922014-04-22 21:13:45 +0000763 reportTryError(w, r, err, "Failed to read a request body.", "")
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +0000764 return
765 }
commit-bot@chromium.org4bd8fdc2014-04-15 00:43:51 +0000766 if n == MAX_TRY_SIZE {
767 err := fmt.Errorf("Code length equal to, or exceeded, %d", MAX_TRY_SIZE)
commit-bot@chromium.org90041922014-04-22 21:13:45 +0000768 reportTryError(w, r, err, "Code too large.", "")
commit-bot@chromium.org4bd8fdc2014-04-15 00:43:51 +0000769 return
770 }
commit-bot@chromium.orgd04e1dd2014-04-19 13:55:50 +0000771 request := TryRequest{}
772 if err := json.Unmarshal(buf.Bytes(), &request); err != nil {
commit-bot@chromium.org90041922014-04-22 21:13:45 +0000773 reportTryError(w, r, err, "Coulnd't decode JSON.", "")
commit-bot@chromium.orgd04e1dd2014-04-19 13:55:50 +0000774 return
775 }
776 if hasPreProcessor(request.Code) {
commit-bot@chromium.org4bd8fdc2014-04-15 00:43:51 +0000777 err := fmt.Errorf("Found preprocessor macro in code.")
commit-bot@chromium.org90041922014-04-22 21:13:45 +0000778 reportTryError(w, r, err, "Preprocessor macros aren't allowed.", "")
commit-bot@chromium.org4bd8fdc2014-04-15 00:43:51 +0000779 return
780 }
commit-bot@chromium.org0a7e5b72014-05-29 15:58:00 +0000781 hash, err := expandCode(LineNumbers(request.Code), request.Source)
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +0000782 if err != nil {
commit-bot@chromium.org90041922014-04-22 21:13:45 +0000783 reportTryError(w, r, err, "Failed to write the code to compile.", hash)
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +0000784 return
785 }
commit-bot@chromium.org0a7e5b72014-05-29 15:58:00 +0000786 writeToDatabase(hash, request.Code, request.Name, request.Source)
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +0000787 message, err := doCmd(fmt.Sprintf(RESULT_COMPILE, hash, hash), true)
788 if err != nil {
commit-bot@chromium.org90041922014-04-22 21:13:45 +0000789 message = cleanCompileOutput(message, hash)
790 reportTryError(w, r, err, message, hash)
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +0000791 return
792 }
793 linkMessage, err := doCmd(fmt.Sprintf(LINK, hash, hash), true)
794 if err != nil {
commit-bot@chromium.org90041922014-04-22 21:13:45 +0000795 linkMessage = cleanCompileOutput(linkMessage, hash)
796 reportTryError(w, r, err, linkMessage, hash)
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +0000797 return
798 }
799 message += linkMessage
800 cmd := hash + " --out " + hash + ".png"
commit-bot@chromium.org0a7e5b72014-05-29 15:58:00 +0000801 if request.Source > 0 {
802 cmd += fmt.Sprintf(" --source image-%d.png", request.Source)
803 }
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +0000804 if *useChroot {
805 cmd = "schroot -c webtry --directory=/inout -- /inout/" + cmd
806 } else {
807 abs, err := filepath.Abs("../../../inout")
808 if err != nil {
commit-bot@chromium.org90041922014-04-22 21:13:45 +0000809 reportTryError(w, r, err, "Failed to find executable directory.", hash)
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +0000810 return
811 }
812 cmd = abs + "/" + cmd
813 }
814
815 execMessage, err := doCmd(cmd, false)
816 if err != nil {
commit-bot@chromium.org90041922014-04-22 21:13:45 +0000817 reportTryError(w, r, err, "Failed to run the code:\n"+execMessage, hash)
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +0000818 return
819 }
820 png, err := ioutil.ReadFile("../../../inout/" + hash + ".png")
821 if err != nil {
commit-bot@chromium.org90041922014-04-22 21:13:45 +0000822 reportTryError(w, r, err, "Failed to open the generated PNG.", hash)
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +0000823 return
824 }
825
826 m := response{
827 Message: message,
commit-bot@chromium.org90041922014-04-22 21:13:45 +0000828 StdOut: execMessage,
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +0000829 Img: base64.StdEncoding.EncodeToString([]byte(png)),
commit-bot@chromium.orgc81d1c42014-04-14 18:53:10 +0000830 Hash: hash,
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +0000831 }
832 resp, err := json.Marshal(m)
833 if err != nil {
commit-bot@chromium.org90041922014-04-22 21:13:45 +0000834 reportTryError(w, r, err, "Failed to serialize a response.", hash)
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +0000835 return
836 }
commit-bot@chromium.orga4c36772014-05-19 17:38:52 +0000837 w.Header().Set("Content-Type", "application/json")
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +0000838 w.Write(resp)
839 }
840}
841
842func main() {
843 flag.Parse()
commit-bot@chromium.orga4c36772014-05-19 17:38:52 +0000844 http.HandleFunc("/i/", autogzip.HandleFunc(imageHandler))
845 http.HandleFunc("/w/", autogzip.HandleFunc(workspaceHandler))
846 http.HandleFunc("/recent/", autogzip.HandleFunc(recentHandler))
847 http.HandleFunc("/iframe/", autogzip.HandleFunc(iframeHandler))
848 http.HandleFunc("/json/", autogzip.HandleFunc(tryInfoHandler))
commit-bot@chromium.org0a7e5b72014-05-29 15:58:00 +0000849 http.HandleFunc("/sources/", autogzip.HandleFunc(sourcesHandler))
fmalita@google.com950306c2014-05-01 15:14:56 +0000850
851 // Resources are served directly
852 // TODO add support for caching/etags/gzip
commit-bot@chromium.orga4c36772014-05-19 17:38:52 +0000853 http.Handle("/res/", autogzip.Handle(http.FileServer(http.Dir("./"))))
fmalita@google.com950306c2014-05-01 15:14:56 +0000854
commit-bot@chromium.org35ffc442014-04-22 19:32:06 +0000855 // TODO Break out /c/ as it's own handler.
commit-bot@chromium.orga4c36772014-05-19 17:38:52 +0000856 http.HandleFunc("/", autogzip.HandleFunc(mainHandler))
commit-bot@chromium.org282333f2014-04-14 14:54:07 +0000857 log.Fatal(http.ListenAndServe(*port, nil))
commit-bot@chromium.org6d036c22014-04-09 18:59:44 +0000858}