in sandbox/sandbox.go [524:635]
func runHandler(w http.ResponseWriter, r *http.Request) {
t0 := time.Now()
tlast := t0
var logmu sync.Mutex
logf := func(format string, args ...interface{}) {
if !*dev {
return
}
logmu.Lock()
defer logmu.Unlock()
t := time.Now()
d := t.Sub(tlast)
d0 := t.Sub(t0)
tlast = t
log.Print(fmt.Sprintf("+%10v +%10v ", d0, d) + fmt.Sprintf(format, args...))
}
logf("/run")
if r.Method != "POST" {
http.Error(w, "expected a POST", http.StatusBadRequest)
return
}
// Bound the number of requests being processed at once.
// (Before we slurp the binary into memory)
select {
case runSem <- struct{}{}:
case <-r.Context().Done():
return
}
defer func() { <-runSem }()
bin, err := ioutil.ReadAll(http.MaxBytesReader(w, r.Body, maxBinarySize))
if err != nil {
log.Printf("failed to read request body: %v", err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
logf("read %d bytes", len(bin))
c, err := getContainer(r.Context())
if err != nil {
if cerr := r.Context().Err(); cerr != nil {
log.Printf("getContainer, client side cancellation: %v", cerr)
return
}
http.Error(w, "failed to get container", http.StatusInternalServerError)
log.Printf("failed to get container: %v", err)
return
}
logf("got container %s", c.name)
ctx, cancel := context.WithTimeout(context.Background(), runTimeout)
closed := make(chan struct{})
defer func() {
logf("leaving handler; about to close container")
cancel()
<-closed
}()
go func() {
<-ctx.Done()
if ctx.Err() == context.DeadlineExceeded {
logf("timeout")
}
c.Close()
close(closed)
}()
var meta processMeta
meta.Args = r.Header["X-Argument"]
metaJSON, _ := json.Marshal(&meta)
metaJSON = append(metaJSON, '\n')
if _, err := c.stdin.Write(metaJSON); err != nil {
log.Printf("failed to write meta to child: %v", err)
http.Error(w, "unknown error during docker run", http.StatusInternalServerError)
return
}
if _, err := c.stdin.Write(bin); err != nil {
log.Printf("failed to write binary to child: %v", err)
http.Error(w, "unknown error during docker run", http.StatusInternalServerError)
return
}
c.stdin.Close()
logf("wrote+closed")
err = c.Wait()
select {
case <-ctx.Done():
// Timed out or canceled before or exactly as Wait returned.
// Either way, treat it as a timeout.
sendError(w, "timeout running program")
return
default:
logf("finished running; about to close container")
cancel()
}
res := &sandboxtypes.Response{}
if err != nil {
if c.stderr.n < 0 || c.stdout.n < 0 {
// Do not send truncated output, just send the error.
sendError(w, errTooMuchOutput.Error())
return
}
var ee *exec.ExitError
if !errors.As(err, &ee) {
http.Error(w, "unknown error during docker run", http.StatusInternalServerError)
return
}
res.ExitCode = ee.ExitCode()
}
res.Stdout = c.stdout.dst.Bytes()
res.Stderr = cleanStderr(c.stderr.dst.Bytes())
sendResponse(w, res)
}