Skip to content

Commit e0b619e

Browse files
committed
feat(multipart-ct-decoder): set file decoder as default on binary and added validation on content-type from encoding
1 parent 2baea3d commit e0b619e

File tree

3 files changed

+60
-3
lines changed

3 files changed

+60
-3
lines changed

openapi3filter/req_resp_decoder.go

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1220,6 +1220,14 @@ func isBinary(schema *openapi3.SchemaRef) bool {
12201220
return schema.Value.Type.Is("string") && schema.Value.Format == "binary"
12211221
}
12221222

1223+
func hasEncodingContentType(encFn EncodingFn) bool {
1224+
var enc *openapi3.Encoding
1225+
if encFn != nil {
1226+
enc = encFn("")
1227+
}
1228+
return enc != nil && enc.ContentType != ""
1229+
}
1230+
12231231
// decodeBody returns a decoded body.
12241232
// The function returns ParseError when a body is invalid.
12251233
func decodeBody(body io.Reader, header http.Header, schema *openapi3.SchemaRef, encFn EncodingFn) (
@@ -1235,11 +1243,12 @@ func decodeBody(body io.Reader, header http.Header, schema *openapi3.SchemaRef,
12351243
}
12361244

12371245
mediaType := parseMediaType(contentType)
1238-
decoder, ok := bodyDecoders[mediaType]
1239-
if !ok && isBinary(schema) {
1240-
ok, decoder = true, FileBodyDecoder
1246+
if isBinary(schema) && !hasEncodingContentType(encFn) {
1247+
value, err := FileBodyDecoder(body, header, schema, encFn)
1248+
return mediaType, value, err
12411249
}
12421250

1251+
decoder, ok := bodyDecoders[mediaType]
12431252
if !ok {
12441253
return "", nil, &ParseError{
12451254
Kind: KindUnsupportedFormat,

openapi3filter/req_resp_decoder_test.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1649,6 +1649,18 @@ func TestDecodeBody(t *testing.T) {
16491649
})
16501650
require.NoError(t, err)
16511651

1652+
multipartBinaryEncodingCT, multipartMimeBinaryEncodingCT, err := newTestMultipartForm([]*testFormPart{
1653+
{name: "b", contentType: "application/json", data: strings.NewReader(`{"bar1": "bar1"}`), filename: "b1"},
1654+
{name: "d", contentType: "application/pdf", data: strings.NewReader("doo1"), filename: "d1"},
1655+
{name: "f", contentType: "application/json", data: strings.NewReader(`{"foo1": "foo1"}`), filename: "f1"},
1656+
{name: "f", contentType: "application/pdf", data: strings.NewReader("foo2"), filename: "f2"},
1657+
})
1658+
1659+
multipartBinaryEncodingCTUnsupported, multipartMimeBinaryEncodingCTUnsupported, err := newTestMultipartForm([]*testFormPart{
1660+
{name: "b", contentType: "application/json", data: strings.NewReader(`{"bar1": "bar1"}`), filename: "b1"},
1661+
{name: "d", contentType: "application/pdf", data: strings.NewReader("doo1"), filename: "d1"},
1662+
})
1663+
16521664
testCases := []struct {
16531665
name string
16541666
mime string
@@ -1787,6 +1799,39 @@ func TestDecodeBody(t *testing.T) {
17871799
body: strings.NewReader("foo"),
17881800
want: "foo",
17891801
},
1802+
{
1803+
name: "multipartEncodingCT",
1804+
mime: multipartMimeBinaryEncodingCT,
1805+
body: multipartBinaryEncodingCT,
1806+
schema: openapi3.NewObjectSchema().
1807+
WithProperty("b", openapi3.NewStringSchema().WithFormat("binary")).
1808+
WithProperty("d", openapi3.NewStringSchema().WithFormat("binary")).
1809+
WithProperty("f", openapi3.NewArraySchema().WithItems(
1810+
openapi3.NewStringSchema().WithFormat("binary"),
1811+
)),
1812+
want: map[string]any{"b": `{"bar1": "bar1"}`, "d": "doo1", "f": []any{`{"foo1": "foo1"}`, "foo2"}},
1813+
},
1814+
{
1815+
name: "multipartEncodingCTUnsupported",
1816+
mime: multipartMimeBinaryEncodingCTUnsupported,
1817+
body: multipartBinaryEncodingCTUnsupported,
1818+
schema: openapi3.NewObjectSchema().
1819+
WithProperty("b", openapi3.NewStringSchema().WithFormat("binary")).
1820+
WithProperty("d", openapi3.NewStringSchema().WithFormat("binary")),
1821+
encoding: map[string]*openapi3.Encoding{
1822+
"b": {ContentType: "application/json"},
1823+
"d": {ContentType: "application/pdf"},
1824+
},
1825+
want: map[string]any{"b": map[string]any{"bar1": "bar1"}},
1826+
wantErr: &ParseError{
1827+
Kind: KindOther,
1828+
Cause: &ParseError{
1829+
Kind: KindUnsupportedFormat,
1830+
Reason: fmt.Sprintf("%s %q", prefixUnsupportedCT, "application/pdf"),
1831+
},
1832+
path: []any{"d"},
1833+
},
1834+
},
17901835
}
17911836
for _, tc := range testCases {
17921837
t.Run(tc.name, func(t *testing.T) {

openapi3filter/zip_file_upload_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ paths:
4242
file:
4343
type: string
4444
format: binary
45+
encoding:
46+
file:
47+
contentType: application/zip
4548
responses:
4649
'200':
4750
description: Created

0 commit comments

Comments
 (0)