Skip to content

Commit

Permalink
internal: non-strict RFC etag support
Browse files Browse the repository at this point in the history
For some caldav providers, it wasn't possible to unmarshal unquoted text for etag.
https://datatracker.ietf.org/doc/html/rfc2616#section-3.11 points to wrap text with quotes, but in reality it's sometimes ignored.

internal: test for ETag UnmarshalText, ETag String, ETag unmarshal and marshal
  • Loading branch information
Aleksandr Borisov committed Oct 27, 2024
1 parent 7f8c17a commit fb85cd0
Show file tree
Hide file tree
Showing 2 changed files with 159 additions and 1 deletion.
10 changes: 9 additions & 1 deletion internal/elements.go
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,15 @@ type GetETag struct {
type ETag string

func (etag *ETag) UnmarshalText(b []byte) error {
s, err := strconv.Unquote(string(b))
s := string(b)
// support for providers with non-strict https://datatracker.ietf.org/doc/html/rfc2616#section-3.11 implementation
if !strings.HasPrefix(s, "\"") {
*etag = ETag(s)
return nil
}

var err error
s, err = strconv.Unquote(s)
if err != nil {
return fmt.Errorf("webdav: failed to unquote ETag: %v", err)
}
Expand Down
150 changes: 150 additions & 0 deletions internal/elements_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,153 @@ func TestTimeRoundTrip(t *testing.T) {
t.Fatalf("invalid round-trip:\ngot= %s\nwant=%s", got, want)
}
}

func TestETag_UnmarshalText(t *testing.T) {
type args struct {
b []byte
}
tests := []struct {
name string
etag ETag
args args
wantErr bool
wantETag ETag
}{
{
name: "double quoted string",
etag: "",
args: args{
b: []byte("\"1692394723948\""),
},
wantErr: false,
wantETag: "1692394723948",
},
{
name: "empty double quoted string",
etag: "",
args: args{
b: []byte("\"\""),
},
wantErr: false,
wantETag: "",
},
{
name: "unquoted string",
etag: "",
args: args{
b: []byte("1692394723948"),
},
wantErr: false,
wantETag: "1692394723948",
},
{
name: "empty string",
etag: "",
args: args{
b: []byte(""),
},
wantErr: false,
wantETag: "",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := tt.etag.UnmarshalText(tt.args.b); (err != nil) != tt.wantErr {
t.Errorf("UnmarshalText() error = %v, wantErr %v", err, tt.wantErr)
}

if tt.etag != tt.wantETag {
t.Errorf("UnmarshalText() want %s, got %s", tt.wantETag, tt.etag)
}
})
}
}

func TestETag_String(t *testing.T) {
tests := []struct {
name string
etag ETag
want string
}{
{
name: "string with double-quote",
etag: "162392347123",
want: "\"162392347123\"",
},
{
name: "empty string with double-quote",
etag: "",
want: "\"\"",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := tt.etag.String(); got != tt.want {
t.Errorf("String() = %v, want %v", got, tt.want)
}
})
}
}

func TestETag_UnmarshalAndMarshalText(t *testing.T) {
tests := []struct {
name string
// initial ETag
etag ETag
// first value for ETag UnmarshalText
unmarshalValue []byte
// expected MarshalText() result after UnmarshalText()
wantMarshalled []byte
// expected UnmarshalText() result after MarshalText() with unmarshalValue
wantUnmarshalledETag ETag
}{
{
name: "string",
etag: "",
unmarshalValue: []byte("162392347123"),
wantMarshalled: []byte("\"162392347123\""),
wantUnmarshalledETag: "162392347123",
},
{
name: "double-quoted",
etag: "",
unmarshalValue: []byte("\"162392347123\""),
wantMarshalled: []byte("\"162392347123\""),
wantUnmarshalledETag: "162392347123",
},
{
name: "empty string",
etag: "",
unmarshalValue: []byte(""),
wantMarshalled: []byte("\"\""),
wantUnmarshalledETag: "",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := tt.etag.UnmarshalText(tt.unmarshalValue); err != nil {
t.Errorf("UnmarshalText() = %v", err)
}

m, err := tt.etag.MarshalText()
if err != nil {
t.Errorf("MarshalText() = %v", err)
}

if !bytes.Equal(m, tt.wantMarshalled) {
t.Errorf("MarshalText() want %s, got %s", tt.wantMarshalled, m)
}

if err := tt.etag.UnmarshalText(m); err != nil {
t.Errorf("UnmarshalText() got error after MarshalText() = %v", err)
}

if tt.etag != tt.wantUnmarshalledETag {
t.Errorf("UnmarshalText() after MarshalText() want %s, got %s", tt.wantUnmarshalledETag, tt.etag)
}
})
}
}

0 comments on commit fb85cd0

Please sign in to comment.