Fix for the X-Format header (#51)

This commit is contained in:
Paramtamtam 2022-01-28 12:53:35 +05:00 committed by GitHub
parent 0efbccbb18
commit 13e7a72790
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 74 additions and 101 deletions

View File

@ -12,8 +12,6 @@ import (
) )
func TestNewOSSignals(t *testing.T) { func TestNewOSSignals(t *testing.T) {
t.Parallel()
oss := breaker.NewOSSignals(context.Background()) oss := breaker.NewOSSignals(context.Background())
gotSignal := make(chan os.Signal, 1) gotSignal := make(chan os.Signal, 1)
@ -35,8 +33,6 @@ func TestNewOSSignals(t *testing.T) {
} }
func TestNewOSSignalCtxCancel(t *testing.T) { func TestNewOSSignalCtxCancel(t *testing.T) {
t.Parallel()
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
oss := breaker.NewOSSignals(ctx) oss := breaker.NewOSSignals(ctx)

View File

@ -16,8 +16,6 @@ type httpClientFunc func(*http.Request) (*http.Response, error)
func (f httpClientFunc) Do(req *http.Request) (*http.Response, error) { return f(req) } func (f httpClientFunc) Do(req *http.Request) (*http.Response, error) { return f(req) }
func TestHealthChecker_CheckSuccess(t *testing.T) { func TestHealthChecker_CheckSuccess(t *testing.T) {
t.Parallel()
var httpMock httpClientFunc = func(req *http.Request) (*http.Response, error) { var httpMock httpClientFunc = func(req *http.Request) (*http.Response, error) {
assert.Equal(t, http.MethodGet, req.Method) assert.Equal(t, http.MethodGet, req.Method)
assert.Equal(t, "http://127.0.0.1:123/healthz", req.URL.String()) assert.Equal(t, "http://127.0.0.1:123/healthz", req.URL.String())
@ -35,8 +33,6 @@ func TestHealthChecker_CheckSuccess(t *testing.T) {
} }
func TestHealthChecker_CheckFail(t *testing.T) { func TestHealthChecker_CheckFail(t *testing.T) {
t.Parallel()
var httpMock httpClientFunc = func(req *http.Request) (*http.Response, error) { var httpMock httpClientFunc = func(req *http.Request) (*http.Response, error) {
return &http.Response{ return &http.Response{
Body: ioutil.NopCloser(bytes.NewReader([]byte{})), Body: ioutil.NopCloser(bytes.NewReader([]byte{})),

View File

@ -8,7 +8,5 @@ import (
) )
func TestLiveChecker_Check(t *testing.T) { func TestLiveChecker_Check(t *testing.T) {
t.Parallel()
assert.NoError(t, checkers.NewLiveChecker().Check()) assert.NoError(t, checkers.NewLiveChecker().Check())
} }

View File

@ -3,7 +3,5 @@ package build_test
import "testing" import "testing"
func TestNothing(t *testing.T) { func TestNothing(t *testing.T) {
t.Parallel()
t.Skip("tests for this package have not been implemented yet") t.Skip("tests for this package have not been implemented yet")
} }

View File

@ -16,8 +16,6 @@ type fakeChecker struct{ err error }
func (c *fakeChecker) Check(port uint16) error { return c.err } func (c *fakeChecker) Check(port uint16) error { return c.err }
func TestProperties(t *testing.T) { func TestProperties(t *testing.T) {
t.Parallel()
cmd := healthcheck.NewCommand(&fakeChecker{err: nil}) cmd := healthcheck.NewCommand(&fakeChecker{err: nil})
assert.Equal(t, "healthcheck", cmd.Use) assert.Equal(t, "healthcheck", cmd.Use)

View File

@ -9,8 +9,6 @@ import (
) )
func TestSubcommands(t *testing.T) { func TestSubcommands(t *testing.T) {
t.Parallel()
cmd := cli.NewCommand("unit test") cmd := cli.NewCommand("unit test")
cases := []struct { cases := []struct {
@ -31,8 +29,6 @@ func TestSubcommands(t *testing.T) {
for _, tt := range cases { for _, tt := range cases {
tt := tt tt := tt
t.Run(tt.giveName, func(t *testing.T) { t.Run(tt.giveName, func(t *testing.T) {
t.Parallel()
if _, exists := subcommands[tt.giveName]; !exists { if _, exists := subcommands[tt.giveName]; !exists {
assert.Failf(t, "command not found", "command [%s] was not found", tt.giveName) assert.Failf(t, "command not found", "command [%s] was not found", tt.giveName)
} }
@ -41,8 +37,6 @@ func TestSubcommands(t *testing.T) {
} }
func TestFlags(t *testing.T) { func TestFlags(t *testing.T) {
t.Parallel()
cmd := cli.NewCommand("unit test") cmd := cli.NewCommand("unit test")
cases := []struct { cases := []struct {
@ -59,8 +53,6 @@ func TestFlags(t *testing.T) {
for _, tt := range cases { for _, tt := range cases {
tt := tt tt := tt
t.Run(tt.giveName, func(t *testing.T) { t.Run(tt.giveName, func(t *testing.T) {
t.Parallel()
flag := cmd.Flag(tt.giveName) flag := cmd.Flag(tt.giveName)
if flag == nil { if flag == nil {
@ -76,8 +68,6 @@ func TestFlags(t *testing.T) {
} }
func TestExecuting(t *testing.T) { func TestExecuting(t *testing.T) {
t.Parallel()
cmd := cli.NewCommand("unit test") cmd := cli.NewCommand("unit test")
cmd.SetArgs([]string{}) cmd.SetArgs([]string{})

View File

@ -3,7 +3,5 @@ package serve_test
import "testing" import "testing"
func TestNothing(t *testing.T) { func TestNothing(t *testing.T) {
t.Parallel()
t.Skip("tests for this package have not been implemented yet") t.Skip("tests for this package have not been implemented yet")
} }

View File

@ -10,8 +10,6 @@ import (
) )
func TestProperties(t *testing.T) { func TestProperties(t *testing.T) {
t.Parallel()
cmd := version.NewCommand("") cmd := version.NewCommand("")
assert.Equal(t, "version", cmd.Use) assert.Equal(t, "version", cmd.Use)
@ -20,8 +18,6 @@ func TestProperties(t *testing.T) {
} }
func TestCommandRun(t *testing.T) { func TestCommandRun(t *testing.T) {
t.Parallel()
cmd := version.NewCommand("1.2.3@foobar") cmd := version.NewCommand("1.2.3@foobar")
cmd.SetArgs([]string{}) cmd.SetArgs([]string{})

View File

@ -9,8 +9,6 @@ import (
) )
func TestFromYaml(t *testing.T) { func TestFromYaml(t *testing.T) {
t.Parallel()
var cases = map[string]struct { //nolint:maligned var cases = map[string]struct { //nolint:maligned
giveYaml []byte giveYaml []byte
giveEnv map[string]string giveEnv map[string]string
@ -111,8 +109,6 @@ pages:
for name, tt := range cases { for name, tt := range cases {
t.Run(name, func(t *testing.T) { t.Run(name, func(t *testing.T) {
t.Parallel()
if tt.giveEnv != nil { if tt.giveEnv != nil {
for key, value := range tt.giveEnv { for key, value := range tt.giveEnv {
assert.NoError(t, os.Setenv(key, value)) assert.NoError(t, os.Setenv(key, value))

View File

@ -8,8 +8,6 @@ import (
) )
func TestConstants(t *testing.T) { func TestConstants(t *testing.T) {
t.Parallel()
assert.Equal(t, "LISTEN_ADDR", string(ListenAddr)) assert.Equal(t, "LISTEN_ADDR", string(ListenAddr))
assert.Equal(t, "LISTEN_PORT", string(ListenPort)) assert.Equal(t, "LISTEN_PORT", string(ListenPort))
assert.Equal(t, "TEMPLATE_NAME", string(TemplateName)) assert.Equal(t, "TEMPLATE_NAME", string(TemplateName))

View File

@ -3,7 +3,5 @@ package common_test
import "testing" import "testing"
func TestNothing2(t *testing.T) { func TestNothing2(t *testing.T) {
t.Parallel()
t.Skip("tests for this package have not been implemented yet") t.Skip("tests for this package have not been implemented yet")
} }

View File

@ -2,6 +2,8 @@ package core
import ( import (
"bytes" "bytes"
"sort"
"strconv"
"github.com/valyala/fasthttp" "github.com/valyala/fasthttp"
) )
@ -17,28 +19,66 @@ const (
) )
func ClientWantFormat(ctx *fasthttp.RequestCtx) ContentType { func ClientWantFormat(ctx *fasthttp.RequestCtx) ContentType {
var ( // parse "Content-Type" header (e.g.: `application/json;charset=UTF-8`)
ct = bytes.ToLower(ctx.Request.Header.ContentType()) if ct := bytes.ToLower(ctx.Request.Header.ContentType()); len(ct) > 4 { //nolint:gomnd
f = bytes.ToLower(ctx.Request.Header.Peek(FormatHeader)) // for the Ingress support return mimeTypeToContentType(ct)
) }
// parse `X-Format` header (aka `Accept`) for the Ingress support
// e.g.: `text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8`
if h := bytes.ToLower(bytes.TrimSpace(ctx.Request.Header.Peek(FormatHeader))); len(h) > 2 { //nolint:gomnd,nestif
type format struct {
mimeType []byte
weight float32
}
var formats = make([]format, 0, 8) //nolint:gomnd
for _, b := range bytes.FieldsFunc(h, func(r rune) bool { return r == ',' }) {
if idx := bytes.Index(b, []byte(";q=")); idx > 0 && idx < len(b) {
f := format{b[0:idx], 0}
if len(b) > idx+3 {
if weight, err := strconv.ParseFloat(string(b[idx+3:]), 32); err == nil { //nolint:gomnd
f.weight = float32(weight)
}
}
formats = append(formats, f)
} else {
formats = append(formats, format{b, 1})
}
}
switch l := len(formats); {
case l == 0:
return UnknownContentType
case l == 1:
return mimeTypeToContentType(formats[0].mimeType)
default:
sort.SliceStable(formats, func(i, j int) bool { return formats[i].weight > formats[j].weight })
return mimeTypeToContentType(formats[0].mimeType)
}
}
return UnknownContentType
}
func mimeTypeToContentType(mimeType []byte) ContentType {
switch { switch {
case bytes.Contains(f, []byte("json")), case bytes.Contains(mimeType, []byte("application/json")), bytes.Contains(mimeType, []byte("text/json")):
bytes.Contains(ct, []byte("application/json")),
bytes.Contains(ct, []byte("text/json")):
return JSONContentType return JSONContentType
case bytes.Contains(f, []byte("xml")), case bytes.Contains(mimeType, []byte("application/xml")), bytes.Contains(mimeType, []byte("text/xml")):
bytes.Contains(ct, []byte("application/xml")),
bytes.Contains(ct, []byte("text/xml")):
return XMLContentType return XMLContentType
case bytes.Contains(f, []byte("html")), case bytes.Contains(mimeType, []byte("text/html")):
bytes.Contains(ct, []byte("text/html")):
return HTMLContentType return HTMLContentType
case bytes.Contains(f, []byte("plain")), case bytes.Contains(mimeType, []byte("text/plain")):
bytes.Contains(ct, []byte("text/plain")):
return PlainTextContentType return PlainTextContentType
} }

View File

@ -9,14 +9,26 @@ import (
) )
func TestClientWantFormat(t *testing.T) { func TestClientWantFormat(t *testing.T) {
t.Parallel()
for name, tt := range map[string]struct { for name, tt := range map[string]struct {
giveContentTypeHeader string giveContentTypeHeader string
giveFormatHeader string giveFormatHeader string
giveReqCtx func() *fasthttp.RequestCtx giveReqCtx func() *fasthttp.RequestCtx
wantFormat core.ContentType wantFormat core.ContentType
}{ }{
"priority": {
giveFormatHeader: "application/xml",
giveContentTypeHeader: "text/plain",
wantFormat: core.PlainTextContentType,
},
"format respects weight": {
giveFormatHeader: "text/html;q=0.5,application/xhtml+xml;q=0.9,application/xml;q=1,*/*;q=0.8",
wantFormat: core.XMLContentType,
},
"wrong format value": {
giveFormatHeader: ";q=foobar,bar/baz;;;;;application/xml",
wantFormat: core.UnknownContentType,
},
"content type - application/json": { "content type - application/json": {
giveContentTypeHeader: "application/jsoN; charset=utf-8", wantFormat: core.JSONContentType, giveContentTypeHeader: "application/jsoN; charset=utf-8", wantFormat: core.JSONContentType,
}, },
@ -24,7 +36,7 @@ func TestClientWantFormat(t *testing.T) {
giveContentTypeHeader: "text/Json; charset=utf-8", wantFormat: core.JSONContentType, giveContentTypeHeader: "text/Json; charset=utf-8", wantFormat: core.JSONContentType,
}, },
"format - json": { "format - json": {
giveFormatHeader: "jsOn", wantFormat: core.JSONContentType, giveFormatHeader: "application/jsoN,*/*;q=0.8", wantFormat: core.JSONContentType,
}, },
"content type - application/xml": { "content type - application/xml": {
@ -34,21 +46,22 @@ func TestClientWantFormat(t *testing.T) {
giveContentTypeHeader: "text/Xml; charset=utf-8", wantFormat: core.XMLContentType, giveContentTypeHeader: "text/Xml; charset=utf-8", wantFormat: core.XMLContentType,
}, },
"format - xml": { "format - xml": {
giveFormatHeader: "xMl", wantFormat: core.XMLContentType, giveFormatHeader: "text/Xml", wantFormat: core.XMLContentType,
}, },
"content type - text/html": { "content type - text/html": {
giveContentTypeHeader: "text/htMl; charset=utf-8", wantFormat: core.HTMLContentType, giveContentTypeHeader: "text/htMl; charset=utf-8", wantFormat: core.HTMLContentType,
}, },
"format - html": { "format - html": {
giveFormatHeader: "HtmL", wantFormat: core.HTMLContentType, giveFormatHeader: "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
wantFormat: core.HTMLContentType,
}, },
"content type - text/plain": { "content type - text/plain": {
giveContentTypeHeader: "text/plaiN; charset=utf-8", wantFormat: core.PlainTextContentType, giveContentTypeHeader: "text/plaiN; charset=utf-8", wantFormat: core.PlainTextContentType,
}, },
"format - plain": { "format - plain": {
giveFormatHeader: "PLAIN", wantFormat: core.PlainTextContentType, giveFormatHeader: "text/plaiN,text/html,application/xml;q=0.9,,,*/*;q=0.8", wantFormat: core.PlainTextContentType,
}, },
"unknown on empty": { "unknown on empty": {
@ -61,8 +74,6 @@ func TestClientWantFormat(t *testing.T) {
}, },
} { } {
t.Run(name, func(t *testing.T) { t.Run(name, func(t *testing.T) {
t.Parallel()
h := &fasthttp.RequestHeader{} h := &fasthttp.RequestHeader{}
h.Set(fasthttp.HeaderContentType, tt.giveContentTypeHeader) h.Set(fasthttp.HeaderContentType, tt.giveContentTypeHeader)
h.Set(core.FormatHeader, tt.giveFormatHeader) h.Set(core.FormatHeader, tt.giveFormatHeader)
@ -79,8 +90,6 @@ func TestClientWantFormat(t *testing.T) {
} }
func TestSetClientFormat(t *testing.T) { func TestSetClientFormat(t *testing.T) {
t.Parallel()
for name, tt := range map[string]struct { for name, tt := range map[string]struct {
giveContentType core.ContentType giveContentType core.ContentType
wantHeaderValue string wantHeaderValue string
@ -92,8 +101,6 @@ func TestSetClientFormat(t *testing.T) {
"plain": {giveContentType: core.PlainTextContentType, wantHeaderValue: "text/plain; charset=utf-8"}, "plain": {giveContentType: core.PlainTextContentType, wantHeaderValue: "text/plain; charset=utf-8"},
} { } {
t.Run(name, func(t *testing.T) { t.Run(name, func(t *testing.T) {
t.Parallel()
ctx := &fasthttp.RequestCtx{ ctx := &fasthttp.RequestCtx{
Response: fasthttp.Response{ Response: fasthttp.Response{
Header: fasthttp.ResponseHeader{}, Header: fasthttp.ResponseHeader{},

View File

@ -3,7 +3,5 @@ package errorpage_test
import "testing" import "testing"
func TestNothing(t *testing.T) { func TestNothing(t *testing.T) {
t.Parallel()
t.Skip("tests for this package have not been implemented yet") t.Skip("tests for this package have not been implemented yet")
} }

View File

@ -3,7 +3,5 @@ package healthz_test
import "testing" import "testing"
func TestNothing(t *testing.T) { func TestNothing(t *testing.T) {
t.Parallel()
t.Skip("tests for this package have not been implemented yet") t.Skip("tests for this package have not been implemented yet")
} }

View File

@ -3,7 +3,5 @@ package index_test
import "testing" import "testing"
func TestNothing(t *testing.T) { func TestNothing(t *testing.T) {
t.Parallel()
t.Skip("tests for this package have not been implemented yet") t.Skip("tests for this package have not been implemented yet")
} }

View File

@ -3,7 +3,5 @@ package notfound_test
import "testing" import "testing"
func TestNothing(t *testing.T) { func TestNothing(t *testing.T) {
t.Parallel()
t.Skip("tests for this package have not been implemented yet") t.Skip("tests for this package have not been implemented yet")
} }

View File

@ -3,7 +3,5 @@ package version_test
import "testing" import "testing"
func TestNothing(t *testing.T) { func TestNothing(t *testing.T) {
t.Parallel()
t.Skip("tests for this package have not been implemented yet") t.Skip("tests for this package have not been implemented yet")
} }

View File

@ -3,7 +3,5 @@ package http
import "testing" import "testing"
func TestNothing(t *testing.T) { func TestNothing(t *testing.T) {
t.Parallel()
t.Skip("tests for this package have not been implemented yet") t.Skip("tests for this package have not been implemented yet")
} }

View File

@ -8,8 +8,6 @@ import (
) )
func TestPicker_NextIndex_First(t *testing.T) { func TestPicker_NextIndex_First(t *testing.T) {
t.Parallel()
for i := uint32(0); i < 100; i++ { for i := uint32(0); i < 100; i++ {
p := pick.NewPicker(i, pick.First) p := pick.NewPicker(i, pick.First)
@ -20,8 +18,6 @@ func TestPicker_NextIndex_First(t *testing.T) {
} }
func TestPicker_NextIndex_RandomOnce(t *testing.T) { func TestPicker_NextIndex_RandomOnce(t *testing.T) {
t.Parallel()
for i := uint8(0); i < 10; i++ { for i := uint8(0); i < 10; i++ {
assert.Equal(t, uint32(0), pick.NewPicker(0, pick.RandomOnce).NextIndex()) assert.Equal(t, uint32(0), pick.NewPicker(0, pick.RandomOnce).NextIndex())
} }
@ -39,8 +35,6 @@ func TestPicker_NextIndex_RandomOnce(t *testing.T) {
} }
func TestPicker_NextIndex_RandomEveryTime(t *testing.T) { func TestPicker_NextIndex_RandomEveryTime(t *testing.T) {
t.Parallel()
for i := uint8(0); i < 10; i++ { for i := uint8(0); i < 10; i++ {
assert.Equal(t, uint32(0), pick.NewPicker(0, pick.RandomEveryTime).NextIndex()) assert.Equal(t, uint32(0), pick.NewPicker(0, pick.RandomEveryTime).NextIndex())
} }
@ -59,7 +53,5 @@ func TestPicker_NextIndex_RandomEveryTime(t *testing.T) {
} }
func TestPicker_NextIndex_Unsupported(t *testing.T) { func TestPicker_NextIndex_Unsupported(t *testing.T) {
t.Parallel()
assert.Panics(t, func() { pick.NewPicker(1, 255).NextIndex() }) assert.Panics(t, func() { pick.NewPicker(1, 255).NextIndex() })
} }

View File

@ -8,11 +8,7 @@ import (
) )
func TestStringsSlice_Pick(t *testing.T) { func TestStringsSlice_Pick(t *testing.T) {
t.Parallel()
t.Run("first", func(t *testing.T) { t.Run("first", func(t *testing.T) {
t.Parallel()
for i := uint8(0); i < 100; i++ { for i := uint8(0); i < 100; i++ {
assert.Equal(t, "", pick.NewStringsSlice([]string{}, pick.First).Pick()) assert.Equal(t, "", pick.NewStringsSlice([]string{}, pick.First).Pick())
} }
@ -25,8 +21,6 @@ func TestStringsSlice_Pick(t *testing.T) {
}) })
t.Run("random once", func(t *testing.T) { t.Run("random once", func(t *testing.T) {
t.Parallel()
for i := uint8(0); i < 100; i++ { for i := uint8(0); i < 100; i++ {
assert.Equal(t, "", pick.NewStringsSlice([]string{}, pick.RandomOnce).Pick()) assert.Equal(t, "", pick.NewStringsSlice([]string{}, pick.RandomOnce).Pick())
} }
@ -42,8 +36,6 @@ func TestStringsSlice_Pick(t *testing.T) {
}) })
t.Run("random every time", func(t *testing.T) { t.Run("random every time", func(t *testing.T) {
t.Parallel()
for i := uint8(0); i < 100; i++ { for i := uint8(0); i < 100; i++ {
assert.Equal(t, "", pick.NewStringsSlice([]string{}, pick.RandomEveryTime).Pick()) assert.Equal(t, "", pick.NewStringsSlice([]string{}, pick.RandomEveryTime).Pick())
} }

View File

@ -8,8 +8,6 @@ import (
) )
func TestProperties_Replaces(t *testing.T) { func TestProperties_Replaces(t *testing.T) {
t.Parallel()
props := tpl.Properties{ props := tpl.Properties{
Code: "foo", Code: "foo",
Message: "bar", Message: "bar",

View File

@ -8,8 +8,6 @@ import (
) )
func Test_Render(t *testing.T) { func Test_Render(t *testing.T) {
t.Parallel()
for name, tt := range map[string]struct { for name, tt := range map[string]struct {
giveContent string giveContent string
giveProps tpl.Properties giveProps tpl.Properties
@ -54,8 +52,6 @@ func Test_Render(t *testing.T) {
}, },
} { } {
t.Run(name, func(t *testing.T) { t.Run(name, func(t *testing.T) {
t.Parallel()
content, err := tpl.Render([]byte(tt.giveContent), tt.giveProps) content, err := tpl.Render([]byte(tt.giveContent), tt.giveProps)
if tt.wantError == true { if tt.wantError == true {

View File

@ -5,8 +5,6 @@ import (
) )
func TestVersion(t *testing.T) { func TestVersion(t *testing.T) {
t.Parallel()
for give, want := range map[string]string{ for give, want := range map[string]string{
// without changes // without changes
"vvv": "vvv", "vvv": "vvv",

View File

@ -13,7 +13,7 @@ body contains "Gone"
# X-Code with X-Format # X-Code with X-Format
GET http://{{ host }}:{{ port }}/ GET http://{{ host }}:{{ port }}/
X-Code: 410 X-Code: 410
X-Format: text/json X-Format: text/html;q=0.9,application/xhtml+xml;q=0.9,application/json,image/avif,image/webp,*/*;q=0.8
HTTP/* 410 HTTP/* 410