func (h *Handler) streamBuild(w http.ResponseWriter, r *http.Request) { jobId := mux.Vars(r)["jobId"] buildId, err := strconv.Atoi(mux.Vars(r)["buildId"]) must(err) must(r.ParseForm()) streamOffset, err := strconv.Atoi(r.Form.Get("offset")) must(err) streamer, err := h.jobService.Stream(jobId, buildId, int64(streamOffset)) must(err) w.Header().Set("Content-Type", "text/event-stream\n\n") for { bytes, done := streamer.Next() _, err = w.Write([]byte(eventMessage("output", string(helpers.SanitisedHTML(bytes))))) must(err) w.(http.Flusher).Flush() if done { break } } must(streamer.Close()) build, err := h.jobService.FindBuild(jobId, buildId) must(err) w.Write([]byte(eventMessage("end", helpers.Message(build)))) }
func (h *Handler) showBuild(w http.ResponseWriter, r *http.Request) { jobId := mux.Vars(r)["jobId"] buildIdStr := mux.Vars(r)["buildId"] if buildIdStr == "latest" { buildNumber, err := h.jobService.HighestBuild(jobId) must(err) http.Redirect(w, r, fmt.Sprintf("/jobs/%s/builds/%d", jobId, buildNumber), 302) return } buildId, err := strconv.Atoi(buildIdStr) must(err) if build, err := h.jobService.FindBuild(jobId, buildId); err == nil { sanitizedOutput := helpers.SanitisedHTML(build.Output) highestBuildNumber, err := h.jobService.HighestBuild(jobId) if err != nil { h.renderErrPage("finding highest build number for job", err, w, r) return } buildNumbers := []int{} for i := highestBuildNumber; i > 0; i-- { buildNumbers = append(buildNumbers, i) } buildView := struct { Build jobs.Build BuildNumber int Output template.HTML BytesAlreadyReceived int ExitMessage string BuildNumbers []int }{ Build: build, BuildNumber: buildId, Output: sanitizedOutput, BytesAlreadyReceived: len(sanitizedOutput), ExitMessage: helpers.Message(build), BuildNumbers: buildNumbers, } h.renderTemplate("show_build", buildView, w) } else { h.renderErrPage("retrieving job", err, w, r) } }
package helpers_test import ( "html/template" "github.com/craigfurman/woodhouse-ci/jobs" "github.com/craigfurman/woodhouse-ci/web/helpers" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("Build view helpers", func() { Describe("sanitized output", func() { It("replaces newlines with html <br>", func() { Expect(helpers.SanitisedHTML([]byte("some\nlines"))).To(Equal(template.HTML("some<br>lines"))) }) It("still escapes all HTML in output", func() { Expect(helpers.SanitisedHTML([]byte("<script>dangerous</script>"))).To(Equal(template.HTML("<script>dangerous</script>"))) }) }) Describe("exit message", func() { It("returns success when exit status is 0", func() { Expect(helpers.Message(jobs.Build{ Finished: true, ExitStatus: 0, })).To(Equal("Success")) })