From 85f4475f620c2a485b4ec1e8a0efc918e735adda Mon Sep 17 00:00:00 2001 From: Gyu-Ho Lee Date: Fri, 25 Sep 2015 08:06:26 -0700 Subject: [PATCH] httptypes/errors: HTTPError.WriteTo returns error Squashing all commits into this one (from https://github.com/coreos/etcd/pull/357). Thanks, --- etcdserver/etcdhttp/capability.go | 8 ++- etcdserver/etcdhttp/client.go | 36 +++++----- etcdserver/etcdhttp/client_auth.go | 74 ++++++++++---------- etcdserver/etcdhttp/http.go | 14 ++-- etcdserver/etcdhttp/http_test.go | 5 +- etcdserver/etcdhttp/httptypes/errors.go | 8 ++- etcdserver/etcdhttp/httptypes/errors_test.go | 4 +- proxy/reverse.go | 43 +++++++----- 8 files changed, 109 insertions(+), 83 deletions(-) diff --git a/etcdserver/etcdhttp/capability.go b/etcdserver/etcdhttp/capability.go index 54e673088..d69bea032 100644 --- a/etcdserver/etcdhttp/capability.go +++ b/etcdserver/etcdhttp/capability.go @@ -84,14 +84,16 @@ func isCapabilityEnabled(c capability) bool { func capabilityHandler(c capability, fn func(http.ResponseWriter, *http.Request)) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { if !isCapabilityEnabled(c) { - notCapable(w, c) + notCapable(w, r, c) return } fn(w, r) } } -func notCapable(w http.ResponseWriter, c capability) { +func notCapable(w http.ResponseWriter, r *http.Request, c capability) { herr := httptypes.NewHTTPError(http.StatusInternalServerError, fmt.Sprintf("Not capable of accessing %s feature during rolling upgrades.", c)) - herr.WriteTo(w) + if err := herr.WriteTo(w); err != nil { + plog.Debugf("error writing HTTPError (%v) to %s", err, r.RemoteAddr) + } } diff --git a/etcdserver/etcdhttp/client.go b/etcdserver/etcdhttp/client.go index 64963798f..a6acee029 100644 --- a/etcdserver/etcdhttp/client.go +++ b/etcdserver/etcdhttp/client.go @@ -186,7 +186,7 @@ func (h *membersHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { return } if !hasWriteRootAccess(h.sec, r) { - writeNoAuth(w) + writeNoAuth(w, r) return } w.Header().Set("X-Etcd-Cluster-ID", h.cluster.ID().String()) @@ -206,7 +206,7 @@ func (h *membersHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { case "leader": id := h.server.Leader() if id == 0 { - writeError(w, httptypes.NewHTTPError(http.StatusServiceUnavailable, "During election")) + writeError(w, r, httptypes.NewHTTPError(http.StatusServiceUnavailable, "During election")) return } m := newMember(h.cluster.Member(id)) @@ -215,7 +215,7 @@ func (h *membersHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { plog.Warningf("failed to encode members response (%v)", err) } default: - writeError(w, httptypes.NewHTTPError(http.StatusNotFound, "Not found")) + writeError(w, r, httptypes.NewHTTPError(http.StatusNotFound, "Not found")) } case "POST": req := httptypes.MemberCreateRequest{} @@ -227,11 +227,11 @@ func (h *membersHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { err := h.server.AddMember(ctx, *m) switch { case err == etcdserver.ErrIDExists || err == etcdserver.ErrPeerURLexists: - writeError(w, httptypes.NewHTTPError(http.StatusConflict, err.Error())) + writeError(w, r, httptypes.NewHTTPError(http.StatusConflict, err.Error())) return case err != nil: plog.Errorf("error adding member %s (%v)", m.ID, err) - writeError(w, err) + writeError(w, r, err) return } res := newMember(m) @@ -248,12 +248,12 @@ func (h *membersHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { err := h.server.RemoveMember(ctx, uint64(id)) switch { case err == etcdserver.ErrIDRemoved: - writeError(w, httptypes.NewHTTPError(http.StatusGone, fmt.Sprintf("Member permanently removed: %s", id))) + writeError(w, r, httptypes.NewHTTPError(http.StatusGone, fmt.Sprintf("Member permanently removed: %s", id))) case err == etcdserver.ErrIDNotFound: - writeError(w, httptypes.NewHTTPError(http.StatusNotFound, fmt.Sprintf("No such member: %s", id))) + writeError(w, r, httptypes.NewHTTPError(http.StatusNotFound, fmt.Sprintf("No such member: %s", id))) case err != nil: plog.Errorf("error removing member %s (%v)", id, err) - writeError(w, err) + writeError(w, r, err) default: w.WriteHeader(http.StatusNoContent) } @@ -273,12 +273,12 @@ func (h *membersHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { err := h.server.UpdateMember(ctx, m) switch { case err == etcdserver.ErrPeerURLexists: - writeError(w, httptypes.NewHTTPError(http.StatusConflict, err.Error())) + writeError(w, r, httptypes.NewHTTPError(http.StatusConflict, err.Error())) case err == etcdserver.ErrIDNotFound: - writeError(w, httptypes.NewHTTPError(http.StatusNotFound, fmt.Sprintf("No such member: %s", id))) + writeError(w, r, httptypes.NewHTTPError(http.StatusNotFound, fmt.Sprintf("No such member: %s", id))) case err != nil: plog.Errorf("error updating member %s (%v)", m.ID, err) - writeError(w, err) + writeError(w, r, err) default: w.WriteHeader(http.StatusNoContent) } @@ -311,7 +311,7 @@ func (h *statsHandler) serveLeader(w http.ResponseWriter, r *http.Request) { } stats := h.stats.LeaderStats() if stats == nil { - writeError(w, httptypes.NewHTTPError(http.StatusForbidden, "not current leader")) + writeError(w, r, httptypes.NewHTTPError(http.StatusForbidden, "not current leader")) return } w.Header().Set("Content-Type", "application/json") @@ -397,13 +397,13 @@ func logHandleFunc(w http.ResponseWriter, r *http.Request) { d := json.NewDecoder(r.Body) if err := d.Decode(&in); err != nil { - writeError(w, httptypes.NewHTTPError(http.StatusBadRequest, "Invalid json body")) + writeError(w, r, httptypes.NewHTTPError(http.StatusBadRequest, "Invalid json body")) return } logl, err := capnslog.ParseLevel(strings.ToUpper(in.Level)) if err != nil { - writeError(w, httptypes.NewHTTPError(http.StatusBadRequest, "Invalid log level "+in.Level)) + writeError(w, r, httptypes.NewHTTPError(http.StatusBadRequest, "Invalid log level "+in.Level)) return } @@ -683,16 +683,16 @@ func trimErrorPrefix(err error, prefix string) error { func unmarshalRequest(r *http.Request, req json.Unmarshaler, w http.ResponseWriter) bool { ctype := r.Header.Get("Content-Type") if ctype != "application/json" { - writeError(w, httptypes.NewHTTPError(http.StatusUnsupportedMediaType, fmt.Sprintf("Bad Content-Type %s, accept application/json", ctype))) + writeError(w, r, httptypes.NewHTTPError(http.StatusUnsupportedMediaType, fmt.Sprintf("Bad Content-Type %s, accept application/json", ctype))) return false } b, err := ioutil.ReadAll(r.Body) if err != nil { - writeError(w, httptypes.NewHTTPError(http.StatusBadRequest, err.Error())) + writeError(w, r, httptypes.NewHTTPError(http.StatusBadRequest, err.Error())) return false } if err := req.UnmarshalJSON(b); err != nil { - writeError(w, httptypes.NewHTTPError(http.StatusBadRequest, err.Error())) + writeError(w, r, httptypes.NewHTTPError(http.StatusBadRequest, err.Error())) return false } return true @@ -706,7 +706,7 @@ func getID(p string, w http.ResponseWriter) (types.ID, bool) { } id, err := types.IDFromString(idStr) if err != nil { - writeError(w, httptypes.NewHTTPError(http.StatusNotFound, fmt.Sprintf("No such member: %s", idStr))) + writeError(w, nil, httptypes.NewHTTPError(http.StatusNotFound, fmt.Sprintf("No such member: %s", idStr))) return 0, false } return id, true diff --git a/etcdserver/etcdhttp/client_auth.go b/etcdserver/etcdhttp/client_auth.go index aa9def0d2..70686138c 100644 --- a/etcdserver/etcdhttp/client_auth.go +++ b/etcdserver/etcdhttp/client_auth.go @@ -126,9 +126,11 @@ func hasGuestAccess(sec auth.Store, r *http.Request, key string) bool { return false } -func writeNoAuth(w http.ResponseWriter) { +func writeNoAuth(w http.ResponseWriter, r *http.Request) { herr := httptypes.NewHTTPError(http.StatusUnauthorized, "Insufficient credentials") - herr.WriteTo(w) + if err := herr.WriteTo(w); err != nil { + plog.Debugf("error writing HTTPError (%v) to %s", err, r.RemoteAddr) + } } func handleAuth(mux *http.ServeMux, sh *authHandler) { @@ -144,7 +146,7 @@ func (sh *authHandler) baseRoles(w http.ResponseWriter, r *http.Request) { return } if !hasRootAccess(sh.sec, r) { - writeNoAuth(w) + writeNoAuth(w, r) return } @@ -153,7 +155,7 @@ func (sh *authHandler) baseRoles(w http.ResponseWriter, r *http.Request) { roles, err := sh.sec.AllRoles() if err != nil { - writeError(w, err) + writeError(w, r, err) return } if roles == nil { @@ -162,7 +164,7 @@ func (sh *authHandler) baseRoles(w http.ResponseWriter, r *http.Request) { err = r.ParseForm() if err != nil { - writeError(w, err) + writeError(w, r, err) return } @@ -173,7 +175,7 @@ func (sh *authHandler) baseRoles(w http.ResponseWriter, r *http.Request) { var role auth.Role role, err = sh.sec.GetRole(roleName) if err != nil { - writeError(w, err) + writeError(w, r, err) return } rolesCollections.Roles = append(rolesCollections.Roles, role) @@ -182,7 +184,7 @@ func (sh *authHandler) baseRoles(w http.ResponseWriter, r *http.Request) { if err != nil { plog.Warningf("baseRoles error encoding on %s", r.URL) - writeError(w, err) + writeError(w, r, err) return } } @@ -197,7 +199,7 @@ func (sh *authHandler) handleRoles(w http.ResponseWriter, r *http.Request) { return } if len(pieces) != 3 { - writeError(w, httptypes.NewHTTPError(http.StatusBadRequest, "Invalid path")) + writeError(w, r, httptypes.NewHTTPError(http.StatusBadRequest, "Invalid path")) return } sh.forRole(w, r, pieces[2]) @@ -208,7 +210,7 @@ func (sh *authHandler) forRole(w http.ResponseWriter, r *http.Request, role stri return } if !hasRootAccess(sh.sec, r) { - writeNoAuth(w) + writeNoAuth(w, r) return } w.Header().Set("X-Etcd-Cluster-ID", sh.cluster.ID().String()) @@ -218,7 +220,7 @@ func (sh *authHandler) forRole(w http.ResponseWriter, r *http.Request, role stri case "GET": data, err := sh.sec.GetRole(role) if err != nil { - writeError(w, err) + writeError(w, r, err) return } err = json.NewEncoder(w).Encode(data) @@ -231,11 +233,11 @@ func (sh *authHandler) forRole(w http.ResponseWriter, r *http.Request, role stri var in auth.Role err := json.NewDecoder(r.Body).Decode(&in) if err != nil { - writeError(w, httptypes.NewHTTPError(http.StatusBadRequest, "Invalid JSON in request body.")) + writeError(w, r, httptypes.NewHTTPError(http.StatusBadRequest, "Invalid JSON in request body.")) return } if in.Role != role { - writeError(w, httptypes.NewHTTPError(http.StatusBadRequest, "Role JSON name does not match the name in the URL")) + writeError(w, r, httptypes.NewHTTPError(http.StatusBadRequest, "Role JSON name does not match the name in the URL")) return } @@ -245,19 +247,19 @@ func (sh *authHandler) forRole(w http.ResponseWriter, r *http.Request, role stri if in.Grant.IsEmpty() && in.Revoke.IsEmpty() { err = sh.sec.CreateRole(in) if err != nil { - writeError(w, err) + writeError(w, r, err) return } w.WriteHeader(http.StatusCreated) out = in } else { if !in.Permissions.IsEmpty() { - writeError(w, httptypes.NewHTTPError(http.StatusBadRequest, "Role JSON contains both permissions and grant/revoke")) + writeError(w, r, httptypes.NewHTTPError(http.StatusBadRequest, "Role JSON contains both permissions and grant/revoke")) return } out, err = sh.sec.UpdateRole(in) if err != nil { - writeError(w, err) + writeError(w, r, err) return } w.WriteHeader(http.StatusOK) @@ -272,7 +274,7 @@ func (sh *authHandler) forRole(w http.ResponseWriter, r *http.Request, role stri case "DELETE": err := sh.sec.DeleteRole(role) if err != nil { - writeError(w, err) + writeError(w, r, err) return } } @@ -288,7 +290,7 @@ func (sh *authHandler) baseUsers(w http.ResponseWriter, r *http.Request) { return } if !hasRootAccess(sh.sec, r) { - writeNoAuth(w) + writeNoAuth(w, r) return } w.Header().Set("X-Etcd-Cluster-ID", sh.cluster.ID().String()) @@ -296,7 +298,7 @@ func (sh *authHandler) baseUsers(w http.ResponseWriter, r *http.Request) { users, err := sh.sec.AllUsers() if err != nil { - writeError(w, err) + writeError(w, r, err) return } if users == nil { @@ -305,7 +307,7 @@ func (sh *authHandler) baseUsers(w http.ResponseWriter, r *http.Request) { err = r.ParseForm() if err != nil { - writeError(w, err) + writeError(w, r, err) return } @@ -316,7 +318,7 @@ func (sh *authHandler) baseUsers(w http.ResponseWriter, r *http.Request) { var user auth.User user, err = sh.sec.GetUser(userName) if err != nil { - writeError(w, err) + writeError(w, r, err) return } @@ -325,7 +327,7 @@ func (sh *authHandler) baseUsers(w http.ResponseWriter, r *http.Request) { var role auth.Role role, err = sh.sec.GetRole(roleName) if err != nil { - writeError(w, err) + writeError(w, r, err) return } uwr.Roles = append(uwr.Roles, role) @@ -337,7 +339,7 @@ func (sh *authHandler) baseUsers(w http.ResponseWriter, r *http.Request) { if err != nil { plog.Warningf("baseUsers error encoding on %s", r.URL) - writeError(w, err) + writeError(w, r, err) return } } @@ -352,7 +354,7 @@ func (sh *authHandler) handleUsers(w http.ResponseWriter, r *http.Request) { return } if len(pieces) != 3 { - writeError(w, httptypes.NewHTTPError(http.StatusBadRequest, "Invalid path")) + writeError(w, r, httptypes.NewHTTPError(http.StatusBadRequest, "Invalid path")) return } sh.forUser(w, r, pieces[2]) @@ -363,7 +365,7 @@ func (sh *authHandler) forUser(w http.ResponseWriter, r *http.Request, user stri return } if !hasRootAccess(sh.sec, r) { - writeNoAuth(w) + writeNoAuth(w, r) return } w.Header().Set("X-Etcd-Cluster-ID", sh.cluster.ID().String()) @@ -373,13 +375,13 @@ func (sh *authHandler) forUser(w http.ResponseWriter, r *http.Request, user stri case "GET": u, err := sh.sec.GetUser(user) if err != nil { - writeError(w, err) + writeError(w, r, err) return } err = r.ParseForm() if err != nil { - writeError(w, err) + writeError(w, r, err) return } @@ -388,7 +390,7 @@ func (sh *authHandler) forUser(w http.ResponseWriter, r *http.Request, user stri var role auth.Role role, err = sh.sec.GetRole(roleName) if err != nil { - writeError(w, err) + writeError(w, r, err) return } uwr.Roles = append(uwr.Roles, role) @@ -404,11 +406,11 @@ func (sh *authHandler) forUser(w http.ResponseWriter, r *http.Request, user stri var u auth.User err := json.NewDecoder(r.Body).Decode(&u) if err != nil { - writeError(w, httptypes.NewHTTPError(http.StatusBadRequest, "Invalid JSON in request body.")) + writeError(w, r, httptypes.NewHTTPError(http.StatusBadRequest, "Invalid JSON in request body.")) return } if u.User != user { - writeError(w, httptypes.NewHTTPError(http.StatusBadRequest, "User JSON name does not match the name in the URL")) + writeError(w, r, httptypes.NewHTTPError(http.StatusBadRequest, "User JSON name does not match the name in the URL")) return } @@ -428,18 +430,18 @@ func (sh *authHandler) forUser(w http.ResponseWriter, r *http.Request, user stri } if err != nil { - writeError(w, err) + writeError(w, r, err) return } } else { // update case if len(u.Roles) != 0 { - writeError(w, httptypes.NewHTTPError(http.StatusBadRequest, "User JSON contains both roles and grant/revoke")) + writeError(w, r, httptypes.NewHTTPError(http.StatusBadRequest, "User JSON contains both roles and grant/revoke")) return } out, err = sh.sec.UpdateUser(u) if err != nil { - writeError(w, err) + writeError(w, r, err) return } } @@ -461,7 +463,7 @@ func (sh *authHandler) forUser(w http.ResponseWriter, r *http.Request, user stri case "DELETE": err := sh.sec.DeleteUser(user) if err != nil { - writeError(w, err) + writeError(w, r, err) return } } @@ -476,7 +478,7 @@ func (sh *authHandler) enableDisable(w http.ResponseWriter, r *http.Request) { return } if !hasWriteRootAccess(sh.sec, r) { - writeNoAuth(w) + writeNoAuth(w, r) return } w.Header().Set("X-Etcd-Cluster-ID", sh.cluster.ID().String()) @@ -492,13 +494,13 @@ func (sh *authHandler) enableDisable(w http.ResponseWriter, r *http.Request) { case "PUT": err := sh.sec.EnableAuth() if err != nil { - writeError(w, err) + writeError(w, r, err) return } case "DELETE": err := sh.sec.DisableAuth() if err != nil { - writeError(w, err) + writeError(w, r, err) return } } diff --git a/etcdserver/etcdhttp/http.go b/etcdserver/etcdhttp/http.go index 4350a8eba..eb82fc4a0 100644 --- a/etcdserver/etcdhttp/http.go +++ b/etcdserver/etcdhttp/http.go @@ -41,7 +41,7 @@ var ( // writeError logs and writes the given Error to the ResponseWriter // If Error is an etcdErr, it is rendered to the ResponseWriter // Otherwise, it is assumed to be an InternalServerError -func writeError(w http.ResponseWriter, err error) { +func writeError(w http.ResponseWriter, r *http.Request, err error) { if err == nil { return } @@ -49,10 +49,14 @@ func writeError(w http.ResponseWriter, err error) { case *etcdErr.Error: e.WriteTo(w) case *httptypes.HTTPError: - e.WriteTo(w) + if et := e.WriteTo(w); et != nil { + plog.Debugf("error writing HTTPError (%v) to %s", et, r.RemoteAddr) + } case auth.Error: herr := httptypes.NewHTTPError(e.HTTPStatus(), e.Error()) - herr.WriteTo(w) + if et := herr.WriteTo(w); et != nil { + plog.Debugf("error writing HTTPError (%v) to %s", et, r.RemoteAddr) + } default: switch err { case etcdserver.ErrTimeoutDueToLeaderFail, etcdserver.ErrTimeoutDueToConnectionLost: @@ -61,7 +65,9 @@ func writeError(w http.ResponseWriter, err error) { plog.Errorf("got unexpected response error (%v)", err) } herr := httptypes.NewHTTPError(http.StatusInternalServerError, "Internal Server Error") - herr.WriteTo(w) + if et := herr.WriteTo(w); et != nil { + plog.Debugf("error writing HTTPError (%v) to %s", et, r.RemoteAddr) + } } } diff --git a/etcdserver/etcdhttp/http_test.go b/etcdserver/etcdhttp/http_test.go index f6d760c43..bd2b096f1 100644 --- a/etcdserver/etcdhttp/http_test.go +++ b/etcdserver/etcdhttp/http_test.go @@ -81,7 +81,8 @@ func (fs *errServer) ClusterVersion() *semver.Version { return nil } func TestWriteError(t *testing.T) { // nil error should not panic rec := httptest.NewRecorder() - writeError(rec, nil) + r := new(http.Request) + writeError(rec, r, nil) h := rec.Header() if len(h) > 0 { t.Fatalf("unexpected non-empty headers: %#v", h) @@ -114,7 +115,7 @@ func TestWriteError(t *testing.T) { for i, tt := range tests { rw := httptest.NewRecorder() - writeError(rw, tt.err) + writeError(rw, r, tt.err) if code := rw.Code; code != tt.wcode { t.Errorf("#%d: code=%d, want %d", i, code, tt.wcode) } diff --git a/etcdserver/etcdhttp/httptypes/errors.go b/etcdserver/etcdhttp/httptypes/errors.go index 06434bf73..15d16ff67 100644 --- a/etcdserver/etcdhttp/httptypes/errors.go +++ b/etcdserver/etcdhttp/httptypes/errors.go @@ -35,15 +35,17 @@ func (e HTTPError) Error() string { return e.Message } -// TODO(xiangli): handle http write errors -func (e HTTPError) WriteTo(w http.ResponseWriter) { +func (e HTTPError) WriteTo(w http.ResponseWriter) error { w.Header().Set("Content-Type", "application/json") w.WriteHeader(e.Code) b, err := json.Marshal(e) if err != nil { plog.Panicf("marshal HTTPError should never fail (%v)", err) } - w.Write(b) + if _, err := w.Write(b); err != nil { + return err + } + return nil } func NewHTTPError(code int, m string) *HTTPError { diff --git a/etcdserver/etcdhttp/httptypes/errors_test.go b/etcdserver/etcdhttp/httptypes/errors_test.go index 1f23c334b..6ef1b3edd 100644 --- a/etcdserver/etcdhttp/httptypes/errors_test.go +++ b/etcdserver/etcdhttp/httptypes/errors_test.go @@ -24,7 +24,9 @@ import ( func TestHTTPErrorWriteTo(t *testing.T) { err := NewHTTPError(http.StatusBadRequest, "what a bad request you made!") rr := httptest.NewRecorder() - err.WriteTo(rr) + if e := err.WriteTo(rr); e != nil { + t.Fatalf("HTTPError.WriteTo error (%v)", e) + } wcode := http.StatusBadRequest wheader := http.Header(map[string][]string{ diff --git a/proxy/reverse.go b/proxy/reverse.go index 2ecbf1fc3..51f3f79fe 100644 --- a/proxy/reverse.go +++ b/proxy/reverse.go @@ -28,23 +28,28 @@ import ( "time" + "github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/pkg/capnslog" "github.com/coreos/etcd/etcdserver/etcdhttp/httptypes" "github.com/coreos/etcd/pkg/httputil" ) -// Hop-by-hop headers. These are removed when sent to the backend. -// http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html -// This list of headers borrowed from stdlib httputil.ReverseProxy -var singleHopHeaders = []string{ - "Connection", - "Keep-Alive", - "Proxy-Authenticate", - "Proxy-Authorization", - "Te", // canonicalized version of "TE" - "Trailers", - "Transfer-Encoding", - "Upgrade", -} +var ( + plog = capnslog.NewPackageLogger("github.com/coreos/etcd", "proxy") + + // Hop-by-hop headers. These are removed when sent to the backend. + // http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html + // This list of headers borrowed from stdlib httputil.ReverseProxy + singleHopHeaders = []string{ + "Connection", + "Keep-Alive", + "Proxy-Authenticate", + "Proxy-Authorization", + "Te", // canonicalized version of "TE" + "Trailers", + "Transfer-Encoding", + "Upgrade", + } +) func removeSingleHopHeaders(hdrs *http.Header) { for _, h := range singleHopHeaders { @@ -72,7 +77,9 @@ func (p *reverseProxy) ServeHTTP(rw http.ResponseWriter, clientreq *http.Request if err != nil { msg := fmt.Sprintf("proxy: failed to read request body: %v", err) e := httptypes.NewHTTPError(http.StatusInternalServerError, msg) - e.WriteTo(rw) + if we := e.WriteTo(rw); we != nil { + plog.Debugf("error writing HTTPError (%v) to %s", we, clientreq.RemoteAddr) + } return } } @@ -93,7 +100,9 @@ func (p *reverseProxy) ServeHTTP(rw http.ResponseWriter, clientreq *http.Request // TODO: limit the rate of the error logging. log.Printf(msg) e := httptypes.NewHTTPError(http.StatusServiceUnavailable, msg) - e.WriteTo(rw) + if we := e.WriteTo(rw); we != nil { + plog.Debugf("error writing HTTPError (%v) to %s", we, clientreq.RemoteAddr) + } return } @@ -145,7 +154,9 @@ func (p *reverseProxy) ServeHTTP(rw http.ResponseWriter, clientreq *http.Request reportRequestDropped(clientreq, failedGettingResponse) log.Printf(msg) e := httptypes.NewHTTPError(http.StatusBadGateway, msg) - e.WriteTo(rw) + if we := e.WriteTo(rw); we != nil { + plog.Debugf("error writing HTTPError (%v) to %s", we, clientreq.RemoteAddr) + } return }