PageRenderTime 38ms CodeModel.GetById 29ms app.highlight 6ms RepoModel.GetById 1ms app.codeStats 0ms

/app/backend.go

https://code.google.com/p/go-ray/
Go | 152 lines | 100 code | 26 blank | 26 comment | 19 complexity | c46dd773d8749ade4e329e5088d761c2 MD5 | raw file
  1// Copyright 2011 The Go Authors. All rights reserved.
  2// Use of this source code is governed by a BSD-style
  3// license that can be found in the LICENSE file.
  4
  5package app
  6
  7import (
  8	"appengine"
  9	"appengine/channel"
 10	"appengine/taskqueue"
 11	"bytes"
 12	"crypto/sha1"
 13	"fmt"
 14	"goray/job"
 15	"goray/std/yamlscene"
 16	"http"
 17	"io"
 18	"os"
 19	"url"
 20
 21	_ "goray/std/all"
 22)
 23
 24const bucket = "http://appengine-go-ray.commondatastorage.googleapis.com/"
 25
 26func init() {
 27	http.HandleFunc("/task/render", renderTask)
 28	http.HandleFunc("/task/send", sendTask)
 29}
 30
 31func renderTask(w http.ResponseWriter, r *http.Request) {
 32	var (
 33		c        = appengine.NewContext(r)
 34		scene    = r.FormValue("scene")
 35		clientid = r.FormValue("clientid")
 36	)
 37
 38	// send is a helper closure to make sending messages more concise.
 39	send := func(msg string) {
 40		c.Infof("renderTask: %s", msg) // log the message to dashboard
 41		sendMessage(c, clientid, msg)
 42	}
 43
 44	// Recover from panics.
 45	// If programmer error causes this task to crash,
 46	// we should notify the end user.
 47	defer func() {
 48		if e := recover(); e != nil {
 49			c.Criticalf("panic: %v", e)
 50			send("Error: render failed.")
 51		}
 52	}()
 53
 54	send("Rendering...")
 55
 56	// Set up the goray job.
 57	j := job.New("job", bytes.NewBufferString(scene), yamlscene.Params{
 58		"OutputFormat": job.FormatMap["png"],
 59	})
 60
 61	// Render the image.
 62	var image bytes.Buffer
 63	if err := j.Render(&image); err != nil {
 64		send(fmt.Sprintf("Error: %v", err))
 65		c.Errorf("renderTask: %v", err)
 66		return
 67	}
 68
 69	send("Render complete.")
 70
 71	send("Uploading...")
 72
 73	// Use a hash of the scene data as the image filename.
 74	h := sha1.New()
 75	io.WriteString(h, scene)
 76	imageURL := fmt.Sprintf("%s%x.png", bucket, h.Sum())
 77
 78	if err := putImage(c, imageURL, &image); err != nil {
 79		send(fmt.Sprintf("Error: %v", err))
 80		c.Errorf("renderTask: putImage: %v", err)
 81		return
 82	}
 83
 84	send("Upload complete.")
 85
 86	// The front-end looks for a message beginning with "Image: " and
 87	// interprets it as the final image URL.
 88	send("Image: " + imageURL)
 89}
 90
 91// putImage reads image data from r and uploads it to url using authentication
 92// data stored in the datastore.
 93func putImage(c appengine.Context, url string, r io.Reader) os.Error {
 94	rt := oauth2Transport(c)
 95	if rt == nil {
 96		return os.NewError("couldn't load OAuth2 credentials.")
 97	}
 98
 99	req, err := http.NewRequest("PUT", url, r)
100	if err != nil {
101		return err
102	}
103	req.Header.Set("x-goog-acl", "public-read")
104
105	resp, err := rt.RoundTrip(req)
106	if err != nil {
107		return err
108	}
109	if resp.StatusCode != 200 {
110		return os.NewError("uploading image: " + resp.Status)
111	}
112
113	return nil
114}
115
116// sendMessage sends msg to the channel identified by clientid.
117func sendMessage(c appengine.Context, clientid, msg string) {
118	// FIXME: This is a work-around for broken Channel API on back-ends.
119	// Really we should just be doing the channel.Send here,
120	// but instead we must create a task to send the message
121	// from the front-end.
122	// http://code.google.com/p/googleappengine/issues/detail?id=5123
123
124	// Create send task.
125	t := taskqueue.NewPOSTTask("/task/send", url.Values{
126		"id": {clientid}, "msg": {msg},
127	})
128
129	// Send the task to a normal app instance, as the renderTask
130	// should be executed on the backend. By default, new tasks go
131	// run on the instance that creates them.
132	if !appengine.IsDevAppServer() {
133		t.Header.Set("Host", appengine.DefaultVersionHostname(c))
134	}
135
136	// Add task to the queue.
137	if _, err := taskqueue.Add(c, t, ""); err != nil {
138		c.Errorf("renderTask: %v", err)
139	}
140}
141
142// sendTask is a task that sends msg to a channel identified by id.
143func sendTask(w http.ResponseWriter, r *http.Request) {
144	var (
145		c   = appengine.NewContext(r)
146		id  = r.FormValue("id")
147		msg = r.FormValue("msg")
148	)
149	if err := channel.Send(c, id, msg); err != nil {
150		c.Errorf("sending to %q: %v", id, err)
151	}
152}