diff --git a/vendor/manifest b/vendor/manifest index a271175c..8681132a 100644 --- a/vendor/manifest +++ b/vendor/manifest @@ -92,7 +92,7 @@ { "importpath": "github.com/matrix-org/util", "repository": "https://github.com/matrix-org/util", - "revision": "0bbc3896e02031e7e7338948b73ce891aa73ab2b", + "revision": "4de125c773716ad380f2f80cc6c04789ef4c906a", "branch": "master" }, { diff --git a/vendor/src/github.com/matrix-org/util/error.go b/vendor/src/github.com/matrix-org/util/error.go index 9d40c57b..530a581b 100644 --- a/vendor/src/github.com/matrix-org/util/error.go +++ b/vendor/src/github.com/matrix-org/util/error.go @@ -5,8 +5,14 @@ import "fmt" // HTTPError An HTTP Error response, which may wrap an underlying native Go Error. type HTTPError struct { WrappedError error - Message string - Code int + // A human-readable message to return to the client in a JSON response. This + // is ignored if JSON is supplied. + Message string + // HTTP status code. + Code int + // JSON represents the JSON that should be serialized and sent to the client + // instead of the given Message. + JSON interface{} } func (e HTTPError) Error() string { diff --git a/vendor/src/github.com/matrix-org/util/json.go b/vendor/src/github.com/matrix-org/util/json.go index b310ac92..92604c54 100644 --- a/vendor/src/github.com/matrix-org/util/json.go +++ b/vendor/src/github.com/matrix-org/util/json.go @@ -68,7 +68,10 @@ func Protect(handler http.HandlerFunc) http.HandlerFunc { "Request panicked!\n%s", debug.Stack(), ) jsonErrorResponse( - w, req, &HTTPError{nil, "Internal Server Error", 500}, + w, req, &HTTPError{ + Message: "Internal Server Error", + Code: 500, + }, ) } }() @@ -112,7 +115,10 @@ func MakeJSONAPI(handler JSONRequestHandler) http.HandlerFunc { if !ok { r, err := json.Marshal(res) if err != nil { - jsonErrorResponse(w, req, &HTTPError{nil, "Failed to serialise response as JSON", 500}) + jsonErrorResponse(w, req, &HTTPError{ + Message: "Failed to serialise response as JSON", + Code: 500, + }) return } resBytes = r @@ -135,9 +141,23 @@ func jsonErrorResponse(w http.ResponseWriter, req *http.Request, httpErr *HTTPEr w.WriteHeader(httpErr.Code) // Set response code - r, err := json.Marshal(&JSONError{ - Message: httpErr.Message, - }) + var err error + var r []byte + if httpErr.JSON != nil { + r, err = json.Marshal(httpErr.JSON) + if err != nil { + // failed to marshal the supplied interface. Whine and fallback to the HTTP message. + logger.WithError(err).Error("Failed to marshal HTTPError.JSON") + } + } + + // failed to marshal or no custom JSON was supplied, send message JSON. + if err != nil || httpErr.JSON == nil { + r, err = json.Marshal(&JSONError{ + Message: httpErr.Message, + }) + } + if err != nil { // We should never fail to marshal the JSON error response, but in this event just skip // marshalling altogether diff --git a/vendor/src/github.com/matrix-org/util/json_test.go b/vendor/src/github.com/matrix-org/util/json_test.go index 0b827245..2248ac3f 100644 --- a/vendor/src/github.com/matrix-org/util/json_test.go +++ b/vendor/src/github.com/matrix-org/util/json_test.go @@ -29,12 +29,26 @@ func TestMakeJSONAPI(t *testing.T) { ExpectCode int ExpectJSON string }{ - {nil, &HTTPError{nil, "Everything is broken", 500}, 500, `{"message":"Everything is broken"}`}, // Error return values - {nil, &HTTPError{nil, "Not here", 404}, 404, `{"message":"Not here"}`}, // With different status codes - {&MockResponse{"yep"}, nil, 200, `{"foo":"yep"}`}, // Success return values - {[]MockResponse{{"yep"}, {"narp"}}, nil, 200, `[{"foo":"yep"},{"foo":"narp"}]`}, // Top-level array success values - {[]byte(`actually bytes`), nil, 200, `actually bytes`}, // raw []byte escape hatch - {func(cannotBe, marshalled string) {}, nil, 500, `{"message":"Failed to serialise response as JSON"}`}, // impossible marshal + // Error message return values + {nil, &HTTPError{nil, "Everything is broken", 500, nil}, 500, `{"message":"Everything is broken"}`}, + // Error JSON return values + {nil, &HTTPError{nil, "Everything is broken", 500, struct { + Foo string `json:"foo"` + }{"yep"}}, 500, `{"foo":"yep"}`}, + // Error JSON return values which fail to be marshalled should fallback to text + {nil, &HTTPError{nil, "Everything is broken", 500, struct { + Foo interface{} `json:"foo"` + }{func(cannotBe, marshalled string) {}}}, 500, `{"message":"Everything is broken"}`}, + // With different status codes + {nil, &HTTPError{nil, "Not here", 404, nil}, 404, `{"message":"Not here"}`}, + // Success return values + {&MockResponse{"yep"}, nil, 200, `{"foo":"yep"}`}, + // Top-level array success values + {[]MockResponse{{"yep"}, {"narp"}}, nil, 200, `[{"foo":"yep"},{"foo":"narp"}]`}, + // raw []byte escape hatch + {[]byte(`actually bytes`), nil, 200, `actually bytes`}, + // impossible marshal + {func(cannotBe, marshalled string) {}, nil, 500, `{"message":"Failed to serialise response as JSON"}`}, } for _, tst := range tests { @@ -58,7 +72,7 @@ func TestMakeJSONAPI(t *testing.T) { func TestMakeJSONAPIRedirect(t *testing.T) { log.SetLevel(log.PanicLevel) // suppress logs in test output mock := MockJSONRequestHandler{func(req *http.Request) (interface{}, *HTTPError) { - return nil, &HTTPError{nil, "https://matrix.org", 302} + return nil, &HTTPError{nil, "https://matrix.org", 302, nil} }} mockReq, _ := http.NewRequest("GET", "http://example.com/foo", nil) mockWriter := httptest.NewRecorder()