From b312bc5be0942993aee6c6ec268d1d36078c3d1a Mon Sep 17 00:00:00 2001 From: Marten Seemann Date: Fri, 15 Nov 2024 10:24:32 +0100 Subject: [PATCH] don't require the presense of the Capsule protocol header (#78) The header is optional. We only need to verify the value if the header is present. --- request.go | 40 +++++++++++++++++++--------------------- request_test.go | 25 +++++++++++++------------ 2 files changed, 32 insertions(+), 33 deletions(-) diff --git a/request.go b/request.go index 8f04e15..0b93b5a 100644 --- a/request.go +++ b/request.go @@ -71,29 +71,27 @@ func ParseRequest(r *http.Request, template *uritemplate.Template) (*Request, er Err: fmt.Errorf("host in :authority (%s) does not match template host (%s)", r.Host, u.Host), } } + // The capsule protocol header is optional, but if it's present, + // we need to validate its value. capsuleHeaderValues, ok := r.Header[http3.CapsuleProtocolHeader] - if !ok { - return nil, &RequestParseError{ - HTTPStatus: http.StatusBadRequest, - Err: fmt.Errorf("missing Capsule-Protocol header"), - } - } - item, err := httpsfv.UnmarshalItem(capsuleHeaderValues) - if err != nil { - return nil, &RequestParseError{ - HTTPStatus: http.StatusBadRequest, - Err: fmt.Errorf("invalid capsule header value: %s", capsuleHeaderValues), + if ok { + item, err := httpsfv.UnmarshalItem(capsuleHeaderValues) + if err != nil { + return nil, &RequestParseError{ + HTTPStatus: http.StatusBadRequest, + Err: fmt.Errorf("invalid capsule header value: %s", capsuleHeaderValues), + } } - } - if v, ok := item.Value.(bool); !ok { - return nil, &RequestParseError{ - HTTPStatus: http.StatusBadRequest, - Err: fmt.Errorf("incorrect capsule header value type: %s", reflect.TypeOf(item.Value)), - } - } else if !v { - return nil, &RequestParseError{ - HTTPStatus: http.StatusBadRequest, - Err: fmt.Errorf("incorrect capsule header value: %t", item.Value), + if v, ok := item.Value.(bool); !ok { + return nil, &RequestParseError{ + HTTPStatus: http.StatusBadRequest, + Err: fmt.Errorf("incorrect capsule header value type: %s", reflect.TypeOf(item.Value)), + } + } else if !v { + return nil, &RequestParseError{ + HTTPStatus: http.StatusBadRequest, + Err: fmt.Errorf("incorrect capsule header value: %t", item.Value), + } } } diff --git a/request_test.go b/request_test.go index 0a8afa8..e3a426a 100644 --- a/request_test.go +++ b/request_test.go @@ -6,8 +6,10 @@ import ( "testing" "github.com/dunglas/httpsfv" - "github.com/stretchr/testify/require" + "github.com/quic-go/quic-go/http3" "github.com/yosida95/uritemplate/v3" + + "github.com/stretchr/testify/require" ) func TestRequestParsing(t *testing.T) { @@ -34,6 +36,13 @@ func TestRequestParsing(t *testing.T) { require.Equal(t, r.Target, "[::1]:1234") }) + t.Run("valid request, without the Capsule-Protocol header", func(t *testing.T) { + req := newRequest("https://localhost:1234/masque?h=localhost&p=1337") + req.Header.Del(http3.CapsuleProtocolHeader) + _, err := ParseRequest(req, template) + require.NoError(t, err) + }) + t.Run("wrong request method", func(t *testing.T) { req := newRequest("https://localhost:1234/masque") req.Method = http.MethodHead @@ -57,17 +66,9 @@ func TestRequestParsing(t *testing.T) { require.Equal(t, http.StatusBadRequest, err.(*RequestParseError).HTTPStatus) }) - t.Run("missing Capsule-Protocol header", func(t *testing.T) { - req := newRequest("https://localhost:1234/masque") - req.Header.Del("Capsule-Protocol") - _, err := ParseRequest(req, template) - require.EqualError(t, err, "missing Capsule-Protocol header") - require.Equal(t, http.StatusBadRequest, err.(*RequestParseError).HTTPStatus) - }) - t.Run("invalid Capsule-Protocol header", func(t *testing.T) { req := newRequest("https://localhost:1234/masque") - req.Header.Set("Capsule-Protocol", "🤡") + req.Header.Set(http3.CapsuleProtocolHeader, "🤡") _, err := ParseRequest(req, template) require.EqualError(t, err, "invalid capsule header value: [🤡]") require.Equal(t, http.StatusBadRequest, err.(*RequestParseError).HTTPStatus) @@ -75,7 +76,7 @@ func TestRequestParsing(t *testing.T) { t.Run("invalid Capsule-Protocol header value type", func(t *testing.T) { req := newRequest("https://localhost:1234/masque") - req.Header.Set("Capsule-Protocol", "1") + req.Header.Set(http3.CapsuleProtocolHeader, "1") _, err := ParseRequest(req, template) require.EqualError(t, err, "incorrect capsule header value type: int64") require.Equal(t, http.StatusBadRequest, err.(*RequestParseError).HTTPStatus) @@ -85,7 +86,7 @@ func TestRequestParsing(t *testing.T) { req := newRequest("https://localhost:1234/masque") v, err := httpsfv.Marshal(httpsfv.NewItem(false)) require.NoError(t, err) - req.Header.Set("Capsule-Protocol", v) + req.Header.Set(http3.CapsuleProtocolHeader, v) _, err = ParseRequest(req, template) require.EqualError(t, err, "incorrect capsule header value: false") require.Equal(t, http.StatusBadRequest, err.(*RequestParseError).HTTPStatus)