From e2c362fcb95e5f6e780c9f2517656388a332e7cb Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Thu, 7 Sep 2023 15:48:18 +1000 Subject: [PATCH] feat: switch ContentType from options pattern to fluent --- http/constants.go | 55 +++++++++++----------- http/constants_test.go | 12 ++--- http/parse.go | 5 +- http/parse_test.go | 104 ++++++++++++++++++++--------------------- 4 files changed, 88 insertions(+), 88 deletions(-) diff --git a/http/constants.go b/http/constants.go index 8af4f72..2105f46 100644 --- a/http/constants.go +++ b/http/constants.go @@ -24,8 +24,11 @@ var ( ResponseChunkDelimeter = []byte("0\r\n") // An http/1.1 chunk delimeter, used for specifying an early end to the response ) +// ContentType represents a Content-Type descriptor for use with the response +// Content-Type header or the request Accept header specifically for +// Trustless Gateway requests and responses. type ContentType struct { - Mime string + MimeType string Order ContentTypeOrder Duplicates bool Quality float32 @@ -33,7 +36,7 @@ type ContentType struct { func (ct ContentType) String() string { sb := strings.Builder{} - sb.WriteString(ct.Mime) + sb.WriteString(ct.MimeType) sb.WriteString(";version=") sb.WriteString(MimeTypeCarVersion) sb.WriteString(";order=") @@ -51,55 +54,53 @@ func (ct ContentType) String() string { return sb.String() } -type ContentTypeOption func(ct *ContentType) +// WithOrder returns a new ContentType with the specified order. +func (ct ContentType) WithOrder(order ContentTypeOrder) ContentType { + ct.Order = order + return ct +} -func WithContentTypeOrder(order ContentTypeOrder) ContentTypeOption { - return func(ct *ContentType) { - ct.Order = order - } +// WithDuplicates returns a new ContentType with the specified duplicates. +func (ct ContentType) WithDuplicates(duplicates bool) ContentType { + ct.Duplicates = duplicates + return ct } -func WithContentTypeDuplicates(duplicates bool) ContentTypeOption { - return func(ct *ContentType) { - ct.Duplicates = duplicates - } +// WithMime returns a new ContentType with the specified mime type. +func (ct ContentType) WithMimeType(mime string) ContentType { + ct.MimeType = mime + return ct } -func WithContentTypeQuality(quality float32) ContentTypeOption { - return func(ct *ContentType) { - ct.Quality = quality - } +// WithQuality returns a new ContentType with the specified quality. +func (ct ContentType) WithQuality(quality float32) ContentType { + ct.Quality = quality + return ct } -func NewContentType(opt ...ContentTypeOption) ContentType { - ct := ContentType{ - Mime: MimeTypeCar, +func DefaultContentType() ContentType { + return ContentType{ + MimeType: MimeTypeCar, Order: DefaultOrder, Duplicates: DefaultIncludeDupes, Quality: 1, } - for _, o := range opt { - o(&ct) - } - return ct } // ResponseContentTypeHeader returns the value for the Content-Type header for a // Trustless Gateway response which will vary depending on whether duplicates // are included or not. Otherwise, the header is the same for all responses. // -// Deprecated: Use NewContentType().String() instead. +// Deprecated: Use DefaultContentType().WithDuplicates(duplicates).String() instead. func ResponseContentTypeHeader(duplicates bool) string { - ct := NewContentType() - ct.Duplicates = duplicates - return ct.String() + return DefaultContentType().WithDuplicates(duplicates).String() } // RequestAcceptHeader returns the value for the Accept header for a Trustless // Gateway request which will vary depending on whether duplicates are included // or not. Otherwise, the header is the same for all requests. // -// Deprecated: Use NewContentType().String() instead. +// Deprecated: Use DefaultContentType().WithDuplicates(duplicates).String() instead. func RequestAcceptHeader(duplicates bool) string { return ResponseContentTypeHeader(duplicates) } diff --git a/http/constants_test.go b/http/constants_test.go index 037c92e..bfa1b93 100644 --- a/http/constants_test.go +++ b/http/constants_test.go @@ -15,10 +15,10 @@ func TestContentType(t *testing.T) { req.Equal("application/vnd.ipld.car;version=1;order=dfs;dups=n", trustlesshttp.ResponseContentTypeHeader(false)) req.Equal("application/vnd.ipld.car;version=1;order=dfs;dups=n", trustlesshttp.RequestAcceptHeader(false)) - req.Equal("application/vnd.ipld.car;version=1;order=dfs;dups=y", trustlesshttp.NewContentType().String()) - req.Equal("application/vnd.ipld.car;version=1;order=dfs;dups=y;q=0.8", trustlesshttp.NewContentType(trustlesshttp.WithContentTypeQuality(0.8)).String()) - req.Equal("application/vnd.ipld.car;version=1;order=dfs;dups=y;q=0.333", trustlesshttp.NewContentType(trustlesshttp.WithContentTypeQuality(1.0/3.0)).String()) - req.Equal("application/vnd.ipld.car;version=1;order=dfs;dups=y", trustlesshttp.NewContentType(trustlesshttp.WithContentTypeQuality(-1.0)).String()) - req.Equal("application/vnd.ipld.car;version=1;order=dfs;dups=n", trustlesshttp.NewContentType(trustlesshttp.WithContentTypeDuplicates(false)).String()) - req.Equal("application/vnd.ipld.car;version=1;order=unk;dups=n", trustlesshttp.NewContentType(trustlesshttp.WithContentTypeDuplicates(false), trustlesshttp.WithContentTypeOrder(trustlesshttp.ContentTypeOrderUnk)).String()) + req.Equal("application/vnd.ipld.car;version=1;order=dfs;dups=y", trustlesshttp.DefaultContentType().String()) + req.Equal("application/vnd.ipld.car;version=1;order=dfs;dups=y;q=0.8", trustlesshttp.DefaultContentType().WithQuality(0.8).String()) + req.Equal("application/vnd.ipld.car;version=1;order=dfs;dups=y;q=0.333", trustlesshttp.DefaultContentType().WithQuality(1.0/3.0).String()) + req.Equal("application/vnd.ipld.car;version=1;order=dfs;dups=y", trustlesshttp.DefaultContentType().WithQuality(-1.0).String()) + req.Equal("application/vnd.ipld.car;version=1;order=dfs;dups=n", trustlesshttp.DefaultContentType().WithDuplicates(false).String()) + req.Equal("application/vnd.ipld.car;version=1;order=unk;dups=n", trustlesshttp.DefaultContentType().WithDuplicates(false).WithOrder(trustlesshttp.ContentTypeOrderUnk).String()) } diff --git a/http/parse.go b/http/parse.go index e26ba24..c6674a5 100644 --- a/http/parse.go +++ b/http/parse.go @@ -97,7 +97,7 @@ func CheckFormat(req *http.Request) (ContentType, error) { } if validFormat { - return NewContentType(), nil // default is acceptable in this case (no accept but format=car) + return DefaultContentType(), nil // default is acceptable in this case (no accept but format=car) } return ContentType{}, fmt.Errorf("neither a valid Accept header nor format parameter were provided") @@ -140,8 +140,7 @@ func parseContentType(header string, strictType bool) (ContentType, bool) { typeParts := strings.Split(header, ";") mime := strings.TrimSpace(typeParts[0]) if mime == MimeTypeCar || (!strictType && (mime == "*/*" || mime == "application/*")) { - contentType := NewContentType() - contentType.Mime = mime + contentType := DefaultContentType().WithMimeType(mime) // parse additional car attributes outlined in IPIP-412 // https://specs.ipfs.tech/http-gateways/trustless-gateway/ for _, nextPart := range typeParts[1:] { diff --git a/http/parse_test.go b/http/parse_test.go index 20aa6ed..b0c7cef 100644 --- a/http/parse_test.go +++ b/http/parse_test.go @@ -113,15 +113,15 @@ func TestCheckFormat(t *testing.T) { err string }{ {"empty (err)", "", "", trustlesshttp.ContentType{}, "neither a valid Accept header nor format parameter were provided"}, - {"format=bop (err)", "", "format=bop", trustlesshttp.NewContentType(), "invalid format parameter; unsupported: \"bop\""}, - {"format=car", "", "format=car", trustlesshttp.NewContentType(), ""}, - {"plain accept", "application/vnd.ipld.car", "", trustlesshttp.NewContentType(), ""}, - {"accept dups", "application/vnd.ipld.car; dups=y", "", trustlesshttp.NewContentType(), ""}, - {"accept no dups", "application/vnd.ipld.car; dups=n", "", trustlesshttp.NewContentType(trustlesshttp.WithContentTypeDuplicates(false)), ""}, - {"accept no dups and cruft", "application/vnd.ipld.car; dups=n; bip; bop", "", trustlesshttp.NewContentType(trustlesshttp.WithContentTypeDuplicates(false)), ""}, - {"valid accept but format=bop (err)", "application/vnd.ipld.car; dups=y", "format=bop", trustlesshttp.NewContentType(), "invalid format parameter; unsupported: \"bop\""}, - {"valid accept but format=car", "application/vnd.ipld.car; dups=y", "format=car", trustlesshttp.NewContentType(), ""}, - {"invalid accept but format=car", "application/vnd.ipld.car; dups=YES!", "format=car", trustlesshttp.NewContentType(trustlesshttp.WithContentTypeDuplicates(false)), "invalid Accept header; unsupported"}, + {"format=bop (err)", "", "format=bop", trustlesshttp.DefaultContentType(), "invalid format parameter; unsupported: \"bop\""}, + {"format=car", "", "format=car", trustlesshttp.DefaultContentType(), ""}, + {"plain accept", "application/vnd.ipld.car", "", trustlesshttp.DefaultContentType(), ""}, + {"accept dups", "application/vnd.ipld.car; dups=y", "", trustlesshttp.DefaultContentType(), ""}, + {"accept no dups", "application/vnd.ipld.car; dups=n", "", trustlesshttp.DefaultContentType().WithDuplicates(false), ""}, + {"accept no dups and cruft", "application/vnd.ipld.car; dups=n; bip; bop", "", trustlesshttp.DefaultContentType().WithDuplicates(false), ""}, + {"valid accept but format=bop (err)", "application/vnd.ipld.car; dups=y", "format=bop", trustlesshttp.DefaultContentType(), "invalid format parameter; unsupported: \"bop\""}, + {"valid accept but format=car", "application/vnd.ipld.car; dups=y", "format=car", trustlesshttp.DefaultContentType(), ""}, + {"invalid accept but format=car", "application/vnd.ipld.car; dups=YES!", "format=car", trustlesshttp.DefaultContentType().WithDuplicates(false), "invalid Accept header; unsupported"}, } { t.Run(tc.name, func(t *testing.T) { req := &http.Request{} @@ -143,32 +143,32 @@ func TestCheckFormat(t *testing.T) { func TestParseContentType(t *testing.T) { for _, tc := range []struct { - name string - accept string - expectValidContentType bool - expectDups bool + name string + accept string + expectValid bool + expectContentType trustlesshttp.ContentType }{ - {"empty (err)", "", false, false}, - {"plain", "application/vnd.ipld.car", true, true}, - {"*/*", "*/*", false, false}, - {"application/*", "application/*", false, false}, - {"dups", "application/vnd.ipld.car; dups=y", true, true}, - {"no dups", "application/vnd.ipld.car; dups=n", true, false}, - {"no dups and cruft", "application/vnd.ipld.car; dups=n; bip; bop", true, false}, - {"version=1", "application/vnd.ipld.car; version=1; dups=n", true, false}, - {"version=2", "application/vnd.ipld.car; version=2; dups=n", false, false}, - {"order=dfs", "application/vnd.ipld.car; order=dfs; dups=n", true, false}, - {"order=unk", "application/vnd.ipld.car; order=unk; dups=n", true, false}, - {"order=bork", "application/vnd.ipld.car; order=bork; dups=y", false, false}, - {"complete", "application/vnd.ipld.car; order=dfs; dups=y; version=1", true, true}, - {"complete (squish)", "application/vnd.ipld.car;order=dfs;dups=y;version=1", true, true}, - {"complete (shuffle)", "application/vnd.ipld.car;version=1;dups=y;order=dfs;", true, true}, - {"complete (cruft)", "application/vnd.ipld.car;;version=1; bip ; dups=n ;bop;order=dfs;--", true, false}, + {"empty (err)", "", false, trustlesshttp.ContentType{}}, + {"plain", "application/vnd.ipld.car", true, trustlesshttp.DefaultContentType()}, + {"*/*", "*/*", false, trustlesshttp.ContentType{}}, + {"application/*", "application/*", false, trustlesshttp.ContentType{}}, + {"dups", "application/vnd.ipld.car; dups=y", true, trustlesshttp.DefaultContentType()}, + {"no dups", "application/vnd.ipld.car; dups=n", true, trustlesshttp.DefaultContentType().WithDuplicates(false)}, + {"no dups and cruft", "application/vnd.ipld.car; dups=n; bip; bop", true, trustlesshttp.DefaultContentType().WithDuplicates(false)}, + {"version=1", "application/vnd.ipld.car; version=1; dups=n", true, trustlesshttp.DefaultContentType().WithDuplicates(false)}, + {"version=2", "application/vnd.ipld.car; version=2; dups=n", false, trustlesshttp.ContentType{}}, + {"order=dfs", "application/vnd.ipld.car; order=dfs; dups=n", true, trustlesshttp.DefaultContentType().WithDuplicates(false)}, + {"order=unk", "application/vnd.ipld.car; order=unk; dups=n", true, trustlesshttp.DefaultContentType().WithDuplicates(false).WithOrder(trustlesshttp.ContentTypeOrderUnk)}, + {"order=bork", "application/vnd.ipld.car; order=bork; dups=y", false, trustlesshttp.ContentType{}}, + {"complete", "application/vnd.ipld.car; order=dfs; dups=y; version=1", true, trustlesshttp.DefaultContentType()}, + {"complete (squish)", "application/vnd.ipld.car;order=dfs;dups=y;version=1", true, trustlesshttp.DefaultContentType()}, + {"complete (shuffle)", "application/vnd.ipld.car;version=1;dups=y;order=dfs;", true, trustlesshttp.DefaultContentType()}, + {"complete (cruft)", "application/vnd.ipld.car;;version=1; bip ; dups=n ;bop;order=dfs;--", true, trustlesshttp.DefaultContentType().WithDuplicates(false)}, } { t.Run(tc.name, func(t *testing.T) { - valid, dups := trustlesshttp.ParseContentType(tc.accept) - require.Equal(t, tc.expectValidContentType, valid) - require.Equal(t, tc.expectDups, dups) + ct, valid := trustlesshttp.ParseContentType(tc.accept) + require.Equal(t, tc.expectValid, valid) + require.Equal(t, tc.expectContentType, ct) }) } } @@ -180,22 +180,22 @@ func TestParseAccept(t *testing.T) { expected []trustlesshttp.ContentType }{ {"empty (err)", "", []trustlesshttp.ContentType{}}, - {"plain", "application/vnd.ipld.car", []trustlesshttp.ContentType{{Mime: trustlesshttp.MimeTypeCar, Duplicates: true, Order: trustlesshttp.ContentTypeOrderDfs, Quality: 1.0}}}, - {"*/*", "*/*", []trustlesshttp.ContentType{{Mime: "*/*", Duplicates: true, Order: trustlesshttp.ContentTypeOrderDfs, Quality: 1.0}}}, - {"application/*", "application/*", []trustlesshttp.ContentType{{Mime: "application/*", Duplicates: true, Order: trustlesshttp.ContentTypeOrderDfs, Quality: 1.0}}}, - {"dups", "application/vnd.ipld.car; dups=y", []trustlesshttp.ContentType{{Mime: trustlesshttp.MimeTypeCar, Duplicates: true, Order: trustlesshttp.ContentTypeOrderDfs, Quality: 1.0}}}, - {"no dups", "application/vnd.ipld.car; dups=n", []trustlesshttp.ContentType{{Mime: trustlesshttp.MimeTypeCar, Duplicates: false, Order: trustlesshttp.ContentTypeOrderDfs, Quality: 1.0}}}, - {"no dups and cruft", "application/vnd.ipld.car; dups=n; bip; bop", []trustlesshttp.ContentType{{Mime: trustlesshttp.MimeTypeCar, Duplicates: false, Order: trustlesshttp.ContentTypeOrderDfs, Quality: 1.0}}}, - {"version=1", "application/vnd.ipld.car; version=1; dups=n", []trustlesshttp.ContentType{{Mime: trustlesshttp.MimeTypeCar, Duplicates: false, Order: trustlesshttp.ContentTypeOrderDfs, Quality: 1.0}}}, + {"plain", "application/vnd.ipld.car", []trustlesshttp.ContentType{{MimeType: trustlesshttp.MimeTypeCar, Duplicates: true, Order: trustlesshttp.ContentTypeOrderDfs, Quality: 1.0}}}, + {"*/*", "*/*", []trustlesshttp.ContentType{{MimeType: "*/*", Duplicates: true, Order: trustlesshttp.ContentTypeOrderDfs, Quality: 1.0}}}, + {"application/*", "application/*", []trustlesshttp.ContentType{{MimeType: "application/*", Duplicates: true, Order: trustlesshttp.ContentTypeOrderDfs, Quality: 1.0}}}, + {"dups", "application/vnd.ipld.car; dups=y", []trustlesshttp.ContentType{{MimeType: trustlesshttp.MimeTypeCar, Duplicates: true, Order: trustlesshttp.ContentTypeOrderDfs, Quality: 1.0}}}, + {"no dups", "application/vnd.ipld.car; dups=n", []trustlesshttp.ContentType{{MimeType: trustlesshttp.MimeTypeCar, Duplicates: false, Order: trustlesshttp.ContentTypeOrderDfs, Quality: 1.0}}}, + {"no dups and cruft", "application/vnd.ipld.car; dups=n; bip; bop", []trustlesshttp.ContentType{{MimeType: trustlesshttp.MimeTypeCar, Duplicates: false, Order: trustlesshttp.ContentTypeOrderDfs, Quality: 1.0}}}, + {"version=1", "application/vnd.ipld.car; version=1; dups=n", []trustlesshttp.ContentType{{MimeType: trustlesshttp.MimeTypeCar, Duplicates: false, Order: trustlesshttp.ContentTypeOrderDfs, Quality: 1.0}}}, {"version=2", "application/vnd.ipld.car; version=2; dups=n", []trustlesshttp.ContentType{}}, - {"order=dfs", "application/vnd.ipld.car; order=dfs; dups=n", []trustlesshttp.ContentType{{Mime: trustlesshttp.MimeTypeCar, Duplicates: false, Order: trustlesshttp.ContentTypeOrderDfs, Quality: 1.0}}}, - {"order=unk", "application/vnd.ipld.car; order=unk; dups=n", []trustlesshttp.ContentType{{Mime: trustlesshttp.MimeTypeCar, Duplicates: false, Order: trustlesshttp.ContentTypeOrderUnk, Quality: 1.0}}}, + {"order=dfs", "application/vnd.ipld.car; order=dfs; dups=n", []trustlesshttp.ContentType{{MimeType: trustlesshttp.MimeTypeCar, Duplicates: false, Order: trustlesshttp.ContentTypeOrderDfs, Quality: 1.0}}}, + {"order=unk", "application/vnd.ipld.car; order=unk; dups=n", []trustlesshttp.ContentType{{MimeType: trustlesshttp.MimeTypeCar, Duplicates: false, Order: trustlesshttp.ContentTypeOrderUnk, Quality: 1.0}}}, {"order=bork", "application/vnd.ipld.car; order=bork; dups=y", []trustlesshttp.ContentType{}}, - {"complete", "application/vnd.ipld.car; order=dfs; dups=y; version=1", []trustlesshttp.ContentType{{Mime: trustlesshttp.MimeTypeCar, Duplicates: true, Order: trustlesshttp.ContentTypeOrderDfs, Quality: 1.0}}}, - {"complete (squish)", "application/vnd.ipld.car;order=dfs;dups=y;version=1", []trustlesshttp.ContentType{{Mime: trustlesshttp.MimeTypeCar, Duplicates: true, Order: trustlesshttp.ContentTypeOrderDfs, Quality: 1.0}}}, - {"complete (shuffle)", "application/vnd.ipld.car;version=1;dups=y;order=dfs;", []trustlesshttp.ContentType{{Mime: trustlesshttp.MimeTypeCar, Duplicates: true, Order: trustlesshttp.ContentTypeOrderDfs, Quality: 1.0}}}, - {"complete (cruft)", "application/vnd.ipld.car;;version=1; bip ; dups=n ;bop;order=dfs;--", []trustlesshttp.ContentType{{Mime: trustlesshttp.MimeTypeCar, Duplicates: false, Order: trustlesshttp.ContentTypeOrderDfs, Quality: 1.0}}}, - {"q", "application/vnd.ipld.car; order=dfs; q=0.77; dups=n", []trustlesshttp.ContentType{{Mime: trustlesshttp.MimeTypeCar, Duplicates: false, Order: trustlesshttp.ContentTypeOrderDfs, Quality: 0.77}}}, + {"complete", "application/vnd.ipld.car; order=dfs; dups=y; version=1", []trustlesshttp.ContentType{{MimeType: trustlesshttp.MimeTypeCar, Duplicates: true, Order: trustlesshttp.ContentTypeOrderDfs, Quality: 1.0}}}, + {"complete (squish)", "application/vnd.ipld.car;order=dfs;dups=y;version=1", []trustlesshttp.ContentType{{MimeType: trustlesshttp.MimeTypeCar, Duplicates: true, Order: trustlesshttp.ContentTypeOrderDfs, Quality: 1.0}}}, + {"complete (shuffle)", "application/vnd.ipld.car;version=1;dups=y;order=dfs;", []trustlesshttp.ContentType{{MimeType: trustlesshttp.MimeTypeCar, Duplicates: true, Order: trustlesshttp.ContentTypeOrderDfs, Quality: 1.0}}}, + {"complete (cruft)", "application/vnd.ipld.car;;version=1; bip ; dups=n ;bop;order=dfs;--", []trustlesshttp.ContentType{{MimeType: trustlesshttp.MimeTypeCar, Duplicates: false, Order: trustlesshttp.ContentTypeOrderDfs, Quality: 1.0}}}, + {"q", "application/vnd.ipld.car; order=dfs; q=0.77; dups=n", []trustlesshttp.ContentType{{MimeType: trustlesshttp.MimeTypeCar, Duplicates: false, Order: trustlesshttp.ContentTypeOrderDfs, Quality: 0.77}}}, {"q=bork", "application/vnd.ipld.car; order=dfs; q=bork; dups=n", []trustlesshttp.ContentType{}}, {"q=-1", "application/vnd.ipld.car; order=dfs; q=-0.1; dups=n", []trustlesshttp.ContentType{}}, @@ -203,12 +203,12 @@ func TestParseAccept(t *testing.T) { "ordered", "application/vnd.ipld.car;dups=n;order=unk;q=0.8, text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.1, application/vnd.ipld.car;dups=y;order=dfs;q=0.9 , application/vnd.ipld.car, application/vnd.ipld.car;dups=y;order=unk;q=0.7, application/vnd.ipld.car;dups=y;order=dfs;q=0.7", []trustlesshttp.ContentType{ - {Mime: trustlesshttp.MimeTypeCar, Duplicates: true, Order: trustlesshttp.ContentTypeOrderDfs, Quality: 1.0}, - {Mime: trustlesshttp.MimeTypeCar, Duplicates: true, Order: trustlesshttp.ContentTypeOrderDfs, Quality: 0.9}, - {Mime: trustlesshttp.MimeTypeCar, Duplicates: false, Order: trustlesshttp.ContentTypeOrderUnk, Quality: 0.8}, - {Mime: trustlesshttp.MimeTypeCar, Duplicates: true, Order: trustlesshttp.ContentTypeOrderUnk, Quality: 0.7}, - {Mime: trustlesshttp.MimeTypeCar, Duplicates: true, Order: trustlesshttp.ContentTypeOrderDfs, Quality: 0.7}, - {Mime: "*/*", Duplicates: true, Order: trustlesshttp.ContentTypeOrderDfs, Quality: 0.1}, + {MimeType: trustlesshttp.MimeTypeCar, Duplicates: true, Order: trustlesshttp.ContentTypeOrderDfs, Quality: 1.0}, + {MimeType: trustlesshttp.MimeTypeCar, Duplicates: true, Order: trustlesshttp.ContentTypeOrderDfs, Quality: 0.9}, + {MimeType: trustlesshttp.MimeTypeCar, Duplicates: false, Order: trustlesshttp.ContentTypeOrderUnk, Quality: 0.8}, + {MimeType: trustlesshttp.MimeTypeCar, Duplicates: true, Order: trustlesshttp.ContentTypeOrderUnk, Quality: 0.7}, + {MimeType: trustlesshttp.MimeTypeCar, Duplicates: true, Order: trustlesshttp.ContentTypeOrderDfs, Quality: 0.7}, + {MimeType: "*/*", Duplicates: true, Order: trustlesshttp.ContentTypeOrderDfs, Quality: 0.1}, }, }, } {