package httpd import ( "encoding/json" "io" "net/http" ) // ResponseWriter is an interface for writing a response. type ResponseWriter interface { // WriteResponse writes a response. WriteResponse(resp Response) (int, error) http.ResponseWriter } // NewResponseWriter creates a new ResponseWriter based on the Accept header // in the request that wraps the ResponseWriter. func NewResponseWriter(w http.ResponseWriter, r *http.Request) ResponseWriter { pretty := r.URL.Query().Get("pretty") == "true" rw := &responseWriter{ResponseWriter: w} switch r.Header.Get("Accept") { case "application/json": fallthrough default: w.Header().Add("Content-Type", "application/json") rw.formatter = &jsonFormatter{Pretty: pretty, Writer: w} } return rw } // WriteError is a convenience function for writing an error response to the ResponseWriter. func WriteError(w ResponseWriter, err error) (int, error) { return w.WriteResponse(Response{Err: err}) } // responseWriter is an implementation of ResponseWriter. type responseWriter struct { formatter interface { WriteResponse(resp Response) (int, error) } http.ResponseWriter } // WriteResponse writes the response using the formatter. func (w *responseWriter) WriteResponse(resp Response) (int, error) { return w.formatter.WriteResponse(resp) } // Flush flushes the ResponseWriter if it has a Flush() method. func (w *responseWriter) Flush() { if w, ok := w.ResponseWriter.(http.Flusher); ok { w.Flush() } } // CloseNotify calls CloseNotify on the underlying http.ResponseWriter if it // exists. Otherwise, it returns a nil channel that will never notify. func (w *responseWriter) CloseNotify() <-chan bool { if notifier, ok := w.ResponseWriter.(http.CloseNotifier); ok { return notifier.CloseNotify() } return nil } type jsonFormatter struct { io.Writer Pretty bool } func (w *jsonFormatter) WriteResponse(resp Response) (n int, err error) { var b []byte if w.Pretty { b, err = json.MarshalIndent(resp, "", " ") } else { b, err = json.Marshal(resp) } if err != nil { n, err = io.WriteString(w, err.Error()) } else { n, err = w.Write(b) } w.Write([]byte("\n")) n++ return n, err }