Dedicated type for conditional match header fields

The `If-Match` and `If-None-Match` conditional headers can have either a
wildcard or a (quoted) ETag as value. However, the ETag _could_ be a
literal `*`, so care must be taken to allow these cases to be
distinguished. The values of these headers have to be handled by the
backend, so export a type that facilitates working with these values.
This commit is contained in:
Conrad Hoffmann 2022-10-31 12:05:27 +01:00 committed by Simon Ser
parent d4d56c2707
commit ac9af45270
3 changed files with 33 additions and 14 deletions

View File

@ -23,10 +23,10 @@ import (
type PutCalendarObjectOptions struct {
// IfNoneMatch indicates that the client does not want to overwrite
// an existing resource.
IfNoneMatch bool
IfNoneMatch webdav.ConditionalMatch
// IfMatch provides the ETag of the resource that the client intends
// to overwrite, can be ""
IfMatch string
IfMatch webdav.ConditionalMatch
}
// Backend is a CalDAV server backend.
@ -642,13 +642,12 @@ func (b *backend) PropPatch(r *http.Request, update *internal.PropertyUpdate) (*
}
func (b *backend) Put(r *http.Request) (*internal.Href, error) {
if inm := r.Header.Get("If-None-Match"); inm != "" && inm != "*" {
return nil, internal.HTTPErrorf(http.StatusBadRequest, "invalid value for If-None-Match header")
}
ifNoneMatch := webdav.ConditionalMatch(r.Header.Get("If-None-Match"))
ifMatch := webdav.ConditionalMatch(r.Header.Get("If-Match"))
opts := PutCalendarObjectOptions{
IfNoneMatch: r.Header.Get("If-None-Match") == "*",
IfMatch: r.Header.Get("If-Match"),
IfNoneMatch: ifNoneMatch,
IfMatch: ifMatch,
}
t, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))

View File

@ -21,10 +21,10 @@ import (
type PutAddressObjectOptions struct {
// IfNoneMatch indicates that the client does not want to overwrite
// an existing resource.
IfNoneMatch bool
IfNoneMatch webdav.ConditionalMatch
// IfMatch provides the ETag of the resource that the client intends
// to overwrite, can be ""
IfMatch string
IfMatch webdav.ConditionalMatch
}
// Backend is a CardDAV server backend.
@ -631,13 +631,12 @@ func (b *backend) PropPatch(r *http.Request, update *internal.PropertyUpdate) (*
}
func (b *backend) Put(r *http.Request) (*internal.Href, error) {
if inm := r.Header.Get("If-None-Match"); inm != "" && inm != "*" {
return nil, internal.HTTPErrorf(http.StatusBadRequest, "invalid value for If-None-Match header")
}
ifNoneMatch := webdav.ConditionalMatch(r.Header.Get("If-None-Match"))
ifMatch := webdav.ConditionalMatch(r.Header.Get("If-Match"))
opts := PutAddressObjectOptions{
IfNoneMatch: r.Header.Get("If-None-Match") == "*",
IfMatch: r.Header.Get("If-Match"),
IfNoneMatch: ifNoneMatch,
IfMatch: ifMatch,
}
t, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))

View File

@ -30,3 +30,24 @@ type groupMembership struct {
XMLName xml.Name `xml:"DAV: group-membership"`
Hrefs []internal.Href `xml:"href"`
}
// ConditionalMatch represents the value of a conditional header
// according to RFC 2068 section 14.25 and RFC 2068 section 14.26
// The (optional) value can either be a wildcard or an ETag.
type ConditionalMatch string
func (val ConditionalMatch) IsSet() bool {
return val != ""
}
func (val ConditionalMatch) IsWildcard() bool {
return val == "*"
}
func (val ConditionalMatch) ETag() (string, error) {
var e internal.ETag
if err := e.UnmarshalText([]byte(val)); err != nil {
return "", err
}
return string(e), nil
}