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.

<?xml version="1.0" encoding="UTF-8"?>
<error xmlns="DAV:"><no-uid-conflict
xmlns="urn:ietf:params:xml:ns:carddav"></no-uid-conflict></error>
```

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.
This commit is contained in:
Conrad Hoffmann 2022-03-10 15:35:39 +01:00 committed by Simon Ser
parent 6d59672ed4
commit 85d2b222bb
3 changed files with 43 additions and 5 deletions

View File

@ -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) { func (b *backend) Move(r *http.Request, dest *internal.Href, overwrite bool) (created bool, err error) {
panic("TODO") 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,
}
}

View File

@ -99,3 +99,14 @@ func (err *HTTPError) Error() string {
return s 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
}

View File

@ -10,6 +10,12 @@ import (
) )
func ServeError(w http.ResponseWriter, err error) { 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 code := http.StatusInternalServerError
if httpErr, ok := err.(*HTTPError); ok { if httpErr, ok := err.(*HTTPError); ok {
code = httpErr.Code code = httpErr.Code
@ -102,11 +108,7 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
} }
if err != nil { if err != nil {
code := http.StatusInternalServerError ServeError(w, err)
if httpErr, ok := err.(*HTTPError); ok {
code = httpErr.Code
}
http.Error(w, err.Error(), code)
} }
} }