From 93e9ad812d26f97d92858670e73b9858d59ac9d6 Mon Sep 17 00:00:00 2001 From: mpl Date: Wed, 17 Jul 2013 00:43:53 +0200 Subject: [PATCH] buildbot: properly answer if-modified-since requests http://camlistore.org/issue/119 Change-Id: If5b486bf95748267e33f354e0a8fed9c79f220c0 --- misc/buildbot/bot.go | 58 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 52 insertions(+), 6 deletions(-) diff --git a/misc/buildbot/bot.go b/misc/buildbot/bot.go index 6c4281042..f3206ea80 100644 --- a/misc/buildbot/bot.go +++ b/misc/buildbot/bot.go @@ -81,6 +81,10 @@ var ( historylk sync.Mutex currentTestSuite *testSuite history History + + // For "If-Modified-Since" requests on the status page. + // Updated every time a new test suite starts or ends. + lastModified time.Time ) var NameToCmd = map[string]string{ @@ -418,6 +422,8 @@ func addRun(tsk *task, tskErr error) { if tskErr != nil && currentTestSuite.Err == nil { currentTestSuite.Err = tskErr currentTestSuite.failedTask = len(currentTestSuite.Run) + } else { + currentTestSuite.failedTask = -1 } currentTestSuite.Run = append(currentTestSuite.Run, tsk) } @@ -790,6 +796,7 @@ func camputMany() error { } func handleErr(err error, proc *os.Process) { + lastModified = time.Now() lastErr = err dbg.Printf("%v", err) if proc != nil { @@ -824,6 +831,7 @@ func main() { } if doBuildGo || doBuildCamli0 || doBuildCamli1 || lastErr != nil { for _, isTip := range [2]bool{false, true} { + lastModified = time.Now() restorePATH() currentTestSuite = &testSuite{ Run: make([]*task, 0, 1), @@ -878,6 +886,7 @@ func main() { dbg.Println("All good.") killCamli(proc) addTestSuite() + lastModified = time.Now() } tryCount++ } @@ -925,15 +934,40 @@ type progressData struct { Current string } +// modtime is the modification time of the resource to be served, or IsZero(). +// return value is whether this request is now complete. +func checkLastModified(w http.ResponseWriter, r *http.Request, modtime time.Time) bool { + if modtime.IsZero() { + return false + } + + // The Date-Modified header truncates sub-second precision, so + // use mtime < t+1s instead of mtime <= t to check for unmodified. + if t, err := time.Parse(http.TimeFormat, r.Header.Get("If-Modified-Since")); err == nil && modtime.Before(t.Add(1*time.Second)) { + h := w.Header() + delete(h, "Content-Type") + delete(h, "Content-Length") + w.WriteHeader(http.StatusNotModified) + return true + } + w.Header().Set("Last-Modified", modtime.UTC().Format(http.TimeFormat)) + return false +} + func okHandler(w http.ResponseWriter, r *http.Request) { t := strings.Replace(r.URL.Path, okPrefix, "", -1) historylk.Lock() defer historylk.Unlock() ts, err := getPastTestSuite(t) - if err != nil { + if err != nil || len(ts.Run) == 0 { http.NotFound(w, r) return } + lastTask := ts.Run[len(ts.Run)-1] + lastModTime := lastTask.start.Add(lastTask.Duration) + if checkLastModified(w, r, lastModTime) { + return + } dat := &progressData{ Ts: ts, } @@ -946,9 +980,13 @@ func okHandler(w http.ResponseWriter, r *http.Request) { func progressHandler(w http.ResponseWriter, r *http.Request) { historylk.Lock() defer historylk.Unlock() + currentTask := getCurrentTask() + if checkLastModified(w, r, currentTask.start) { + return + } dat := &progressData{ Ts: currentTestSuite, - Current: getCurrentTask().Cmd, + Current: currentTask.Cmd, } err := testSuiteTpl.Execute(w, dat) if err != nil { @@ -966,11 +1004,16 @@ func failHandler(w http.ResponseWriter, r *http.Request) { historylk.Lock() defer historylk.Unlock() ts, err := getPastTestSuite(t) - if err != nil { + if err != nil || len(ts.Run) == 0 || ts.failedTask == -1 { http.NotFound(w, r) return } - taskReport := &TaskReport{Cmd: ts.Run[ts.failedTask].Cmd, Err: ts.Err} + failedTask := ts.Run[ts.failedTask] + lastModTime := failedTask.start.Add(failedTask.Duration) + if checkLastModified(w, r, lastModTime) { + return + } + taskReport := &TaskReport{Cmd: failedTask.Cmd, Err: ts.Err} err = taskTpl.Execute(w, taskReport) if err != nil { log.Printf("fail template: %v\n", err) @@ -983,8 +1026,6 @@ type status struct { } func invertedHistory() (inverted History) { - historylk.Lock() - defer historylk.Unlock() inverted = make(History, len(history)) endpos := len(history) - 1 for k, v := range history { @@ -994,10 +1035,15 @@ func invertedHistory() (inverted History) { } func statusHandler(w http.ResponseWriter, r *http.Request) { + historylk.Lock() + defer historylk.Unlock() stat := &status{ Hs: invertedHistory(), Ts: currentTestSuite, } + if checkLastModified(w, r, lastModified) { + return + } err := statusTpl.Execute(w, stat) if err != nil { log.Printf("status template: %v\n", err)