|
1
|
1
|
new file mode 100644
|
|
...
|
...
|
@@ -0,0 +1,40 @@
|
|
|
0
|
+package engine
|
|
|
1
|
+
|
|
|
2
|
+import (
|
|
|
3
|
+ "path"
|
|
|
4
|
+ "net/http"
|
|
|
5
|
+)
|
|
|
6
|
+
|
|
|
7
|
+// ServeHTTP executes a job as specified by the http request `r`, and sends the
|
|
|
8
|
+// result as an http response.
|
|
|
9
|
+// This method allows an Engine instance to be passed as a standard http.Handler interface.
|
|
|
10
|
+//
|
|
|
11
|
+// Note that the protocol used in this methid is a convenience wrapper and is not the canonical
|
|
|
12
|
+// implementation of remote job execution. This is because HTTP/1 does not handle stream multiplexing,
|
|
|
13
|
+// and so cannot differentiate stdout from stderr. Additionally, headers cannot be added to a response
|
|
|
14
|
+// once data has been written to the body, which makes it inconvenient to return metadata such
|
|
|
15
|
+// as the exit status.
|
|
|
16
|
+//
|
|
|
17
|
+func (eng *Engine) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
|
18
|
+ jobName := path.Base(r.URL.Path)
|
|
|
19
|
+ jobArgs, exists := r.URL.Query()["a"]
|
|
|
20
|
+ if !exists {
|
|
|
21
|
+ jobArgs = []string{}
|
|
|
22
|
+ }
|
|
|
23
|
+ w.Header().Set("Job-Name", jobName)
|
|
|
24
|
+ for _, arg := range(jobArgs) {
|
|
|
25
|
+ w.Header().Add("Job-Args", arg)
|
|
|
26
|
+ }
|
|
|
27
|
+ job := eng.Job(jobName, jobArgs...)
|
|
|
28
|
+ job.Stdout.Add(w)
|
|
|
29
|
+ job.Stderr.Add(w)
|
|
|
30
|
+ // FIXME: distinguish job status from engine error in Run()
|
|
|
31
|
+ // The former should be passed as a special header, the former
|
|
|
32
|
+ // should cause a 500 status
|
|
|
33
|
+ w.WriteHeader(http.StatusOK)
|
|
|
34
|
+ // The exit status cannot be sent reliably with HTTP1, because headers
|
|
|
35
|
+ // can only be sent before the body.
|
|
|
36
|
+ // (we could possibly use http footers via chunked encoding, but I couldn't find
|
|
|
37
|
+ // how to use them in net/http)
|
|
|
38
|
+ job.Run()
|
|
|
39
|
+}
|