// WriteEvent does the actual work of formatting an SSE compliant message // sending it over the provided ResponseWriter and flushing. func WriteEvent(ctx context.Context, w http.ResponseWriter, e Event) { if e.Error != nil { fmt.Fprint(w, "event: err\n") fmt.Fprintf(w, "data: %s\n\n", e.Error.Error()) w.(http.Flusher).Flush() log.Error(ctx, e.Error) return } // TODO: add tests to ensure retry get's properly rendered if e.Retry != 0 { fmt.Fprintf(w, "retry: %d\n", e.Retry) } if e.ID != "" { fmt.Fprintf(w, "id: %s\n", e.ID) } if e.Event != "" { fmt.Fprintf(w, "event: %s\n", e.Event) } fmt.Fprintf(w, "data: %s\n\n", getJSON(e.Data)) w.(http.Flusher).Flush() }
func render(ctx context.Context, w http.ResponseWriter, p P) { Inflate(ctx, &p) w.Header().Set("Content-Type", "application/problem+json") js, err := json.MarshalIndent(p, "", " ") if err != nil { log.Error(ctx, err) http.Error(w, "error rendering problem", http.StatusInternalServerError) return } w.WriteHeader(p.Status) w.Write(js) }
// Render writes a http response to `w`, compliant with the "Problem // Details for HTTP APIs" RFC: // https://tools.ietf.org/html/draft-ietf-appsawg-http-problem-00 // // `p` is the problem, which may be either a concrete P struct, an implementor // of the `HasProblem` interface, or an error. Any other value for `p` will // panic. func Render(ctx context.Context, w http.ResponseWriter, p interface{}) { switch p := p.(type) { case P: render(ctx, w, p) case *P: render(ctx, w, *p) case error: pp, ok := errToProblemMap[p] if !ok { log.Error(ctx, p) pp = ServerError } render(ctx, w, pp) default: panic(fmt.Sprintf("Invalid problem: %v+", p)) } }