From 85d2b222bbcd68a1a0fce1ecbf2d859bbe238aeb Mon Sep 17 00:00:00 2001 From: Conrad Hoffmann Date: Thu, 10 Mar 2022 15:35:39 +0100 Subject: [PATCH] Add error type representing DAV/XML errors Backends will need some way to signal that a precondition error occurred (and specifying which one) without causing the server to return a 500. This commit adds an exported function to create a specific error for this. The existing error handling routine is slightly adapted to handle this error in such a way that it returns the desired result. Usage would be something like: ``` return "", carddav.NewPreconditionError(carddav.PreconditionNoUIDConflict) ``` which triggers the following HTTP response: ``` HTTP/1.1 409 Conflict. Content-Type: text/xml; charset=utf-8. Date: Thu, 10 Mar 2022 10:28:56 GMT. Content-Length: 141. Connection: close. ``` This response gets correctly recognized by e.g. Evolution (though it's handling is not great). The added error type is generic enough to be used for other stuff also. As it is not exported (internal package), new functions for creating such errors would have to be added. --- carddav/server.go | 25 +++++++++++++++++++++++++ internal/internal.go | 11 +++++++++++ internal/server.go | 12 +++++++----- 3 files changed, 43 insertions(+), 5 deletions(-) diff --git a/carddav/server.go b/carddav/server.go index d4cf876..5aeb714 100644 --- a/carddav/server.go +++ b/carddav/server.go @@ -439,3 +439,28 @@ func (b *backend) Copy(r *http.Request, dest *internal.Href, recursive, overwrit func (b *backend) Move(r *http.Request, dest *internal.Href, overwrite bool) (created bool, err error) { panic("TODO") } + +// https://tools.ietf.org/rfcmarkup?doc=6352#section-6.3.2.1 +type PreconditionType string + +const ( + PreconditionNoUIDConflict PreconditionType = "no-uid-conflict" + PreconditionSupportedAddressData PreconditionType = "supported-address-data" + PreconditionValidAddressData PreconditionType = "valid-address-data" + PreconditionMaxResourceSize PreconditionType = "max-resource-size" +) + +func NewPreconditionError(err PreconditionType) error { + name := xml.Name{"urn:ietf:params:xml:ns:carddav", string(err)} + elem := internal.NewRawXMLElement(name, nil, nil) + e := internal.Error{ + Raw: []internal.RawXMLValue{ + *elem, + }, + } + return &internal.DAVError{ + Code: 409, + Msg: fmt.Sprintf("precondition not met: %s", string(err)), + Err: e, + } +} diff --git a/internal/internal.go b/internal/internal.go index 08c2ef8..065e66f 100644 --- a/internal/internal.go +++ b/internal/internal.go @@ -99,3 +99,14 @@ func (err *HTTPError) Error() string { return s } } + +// DAVError is a XML error with HTTP status and a human readable message +type DAVError struct { + Code int + Msg string + Err Error +} + +func (err *DAVError) Error() string { + return err.Msg +} diff --git a/internal/server.go b/internal/server.go index 5a51aed..66f4964 100644 --- a/internal/server.go +++ b/internal/server.go @@ -10,6 +10,12 @@ import ( ) func ServeError(w http.ResponseWriter, err error) { + if davErr, ok := err.(*DAVError); ok { + w.WriteHeader(davErr.Code) + ServeXML(w).Encode(davErr.Err) + return + } + code := http.StatusInternalServerError if httpErr, ok := err.(*HTTPError); ok { code = httpErr.Code @@ -102,11 +108,7 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { } if err != nil { - code := http.StatusInternalServerError - if httpErr, ok := err.(*HTTPError); ok { - code = httpErr.Code - } - http.Error(w, err.Error(), code) + ServeError(w, err) } }