mirror of https://github.com/gofiber/fiber.git
* Fix #2383, accepts mimeType * Fix #2383, accepts mimeType * Fix #2383, accepts mimeTypepull/2391/head
parent
c6e86ac906
commit
28d9abb71b
59
ctx.go
59
ctx.go
|
@ -197,73 +197,22 @@ func (app *App) ReleaseCtx(c *Ctx) {
|
||||||
|
|
||||||
// Accepts checks if the specified extensions or content types are acceptable.
|
// Accepts checks if the specified extensions or content types are acceptable.
|
||||||
func (c *Ctx) Accepts(offers ...string) string {
|
func (c *Ctx) Accepts(offers ...string) string {
|
||||||
if len(offers) == 0 {
|
return getOffer(c.Get(HeaderAccept), acceptsOfferType, offers...)
|
||||||
return ""
|
|
||||||
}
|
|
||||||
header := c.Get(HeaderAccept)
|
|
||||||
if header == "" {
|
|
||||||
return offers[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
spec, commaPos := "", 0
|
|
||||||
for len(header) > 0 && commaPos != -1 {
|
|
||||||
commaPos = strings.IndexByte(header, ',')
|
|
||||||
if commaPos != -1 {
|
|
||||||
spec = utils.Trim(header[:commaPos], ' ')
|
|
||||||
} else {
|
|
||||||
spec = utils.TrimLeft(header, ' ')
|
|
||||||
}
|
|
||||||
if factorSign := strings.IndexByte(spec, ';'); factorSign != -1 {
|
|
||||||
spec = spec[:factorSign]
|
|
||||||
}
|
|
||||||
|
|
||||||
var mimetype string
|
|
||||||
for _, offer := range offers {
|
|
||||||
if len(offer) == 0 {
|
|
||||||
continue
|
|
||||||
// Accept: */*
|
|
||||||
} else if spec == "*/*" {
|
|
||||||
return offer
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.IndexByte(offer, '/') != -1 {
|
|
||||||
mimetype = offer // MIME type
|
|
||||||
} else {
|
|
||||||
mimetype = utils.GetMIME(offer) // extension
|
|
||||||
}
|
|
||||||
|
|
||||||
if spec == mimetype {
|
|
||||||
// Accept: <MIME_type>/<MIME_subtype>
|
|
||||||
return offer
|
|
||||||
}
|
|
||||||
|
|
||||||
s := strings.IndexByte(mimetype, '/')
|
|
||||||
// Accept: <MIME_type>/*
|
|
||||||
if strings.HasPrefix(spec, mimetype[:s]) && (spec[s:] == "/*" || mimetype[s:] == "/*") {
|
|
||||||
return offer
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if commaPos != -1 {
|
|
||||||
header = header[commaPos+1:]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ""
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// AcceptsCharsets checks if the specified charset is acceptable.
|
// AcceptsCharsets checks if the specified charset is acceptable.
|
||||||
func (c *Ctx) AcceptsCharsets(offers ...string) string {
|
func (c *Ctx) AcceptsCharsets(offers ...string) string {
|
||||||
return getOffer(c.Get(HeaderAcceptCharset), offers...)
|
return getOffer(c.Get(HeaderAcceptCharset), acceptsOffer, offers...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AcceptsEncodings checks if the specified encoding is acceptable.
|
// AcceptsEncodings checks if the specified encoding is acceptable.
|
||||||
func (c *Ctx) AcceptsEncodings(offers ...string) string {
|
func (c *Ctx) AcceptsEncodings(offers ...string) string {
|
||||||
return getOffer(c.Get(HeaderAcceptEncoding), offers...)
|
return getOffer(c.Get(HeaderAcceptEncoding), acceptsOffer, offers...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AcceptsLanguages checks if the specified language is acceptable.
|
// AcceptsLanguages checks if the specified language is acceptable.
|
||||||
func (c *Ctx) AcceptsLanguages(offers ...string) string {
|
func (c *Ctx) AcceptsLanguages(offers ...string) string {
|
||||||
return getOffer(c.Get(HeaderAcceptLanguage), offers...)
|
return getOffer(c.Get(HeaderAcceptLanguage), acceptsOffer, offers...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// App returns the *App reference to the instance of the Fiber application
|
// App returns the *App reference to the instance of the Fiber application
|
||||||
|
|
27
ctx_test.go
27
ctx_test.go
|
@ -66,14 +66,27 @@ func Benchmark_Ctx_Accepts(b *testing.B) {
|
||||||
app := New()
|
app := New()
|
||||||
c := app.AcquireCtx(&fasthttp.RequestCtx{})
|
c := app.AcquireCtx(&fasthttp.RequestCtx{})
|
||||||
defer app.ReleaseCtx(c)
|
defer app.ReleaseCtx(c)
|
||||||
c.Request().Header.Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9")
|
acceptHeader := "text/html,application/xhtml+xml,application/xml;q=0.9"
|
||||||
var res string
|
c.Request().Header.Set("Accept", acceptHeader)
|
||||||
b.ReportAllocs()
|
acceptValues := [][]string{
|
||||||
b.ResetTimer()
|
{".xml"},
|
||||||
for n := 0; n < b.N; n++ {
|
{"json", "xml"},
|
||||||
res = c.Accepts(".xml")
|
{"application/json", "application/xml"},
|
||||||
|
}
|
||||||
|
expectedResults := []string{".xml", "xml", "application/xml"}
|
||||||
|
|
||||||
|
for i := 0; i < len(acceptValues); i++ {
|
||||||
|
b.Run(fmt.Sprintf("run-%#v", acceptValues[i]), func(bb *testing.B) {
|
||||||
|
var res string
|
||||||
|
bb.ReportAllocs()
|
||||||
|
bb.ResetTimer()
|
||||||
|
|
||||||
|
for n := 0; n < bb.N; n++ {
|
||||||
|
res = c.Accepts(acceptValues[i]...)
|
||||||
|
}
|
||||||
|
utils.AssertEqual(bb, expectedResults[i], res)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
utils.AssertEqual(b, ".xml", res)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// go test -run Test_Ctx_Accepts_EmptyAccept
|
// go test -run Test_Ctx_Accepts_EmptyAccept
|
||||||
|
|
86
helpers.go
86
helpers.go
|
@ -215,36 +215,82 @@ func getGroupPath(prefix, path string) string {
|
||||||
return utils.TrimRight(prefix, '/') + path
|
return utils.TrimRight(prefix, '/') + path
|
||||||
}
|
}
|
||||||
|
|
||||||
// return valid offer for header negotiation
|
// acceptsOffer This function determines if an offer matches a given specification.
|
||||||
func getOffer(header string, offers ...string) string {
|
// It checks if the specification ends with a '*' or if the offer has the prefix of the specification.
|
||||||
|
// Returns true if the offer matches the specification, false otherwise.
|
||||||
|
func acceptsOffer(spec, offer string) bool {
|
||||||
|
if len(spec) >= 1 && spec[len(spec)-1] == '*' {
|
||||||
|
return true
|
||||||
|
} else if strings.HasPrefix(spec, offer) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// acceptsOfferType This function determines if an offer type matches a given specification.
|
||||||
|
// It checks if the specification is equal to */* (i.e., all types are accepted).
|
||||||
|
// It gets the MIME type of the offer (either from the offer itself or by its file extension).
|
||||||
|
// It checks if the offer MIME type matches the specification MIME type or if the specification is of the form <MIME_type>/* and the offer MIME type has the same MIME type.
|
||||||
|
// Returns true if the offer type matches the specification, false otherwise.
|
||||||
|
func acceptsOfferType(spec, offerType string) bool {
|
||||||
|
// Accept: */*
|
||||||
|
if spec == "*/*" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
var mimetype string
|
||||||
|
if strings.IndexByte(offerType, '/') != -1 {
|
||||||
|
mimetype = offerType // MIME type
|
||||||
|
} else {
|
||||||
|
mimetype = utils.GetMIME(offerType) // extension
|
||||||
|
}
|
||||||
|
|
||||||
|
if spec == mimetype {
|
||||||
|
// Accept: <MIME_type>/<MIME_subtype>
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
s := strings.IndexByte(mimetype, '/')
|
||||||
|
// Accept: <MIME_type>/*
|
||||||
|
if strings.HasPrefix(spec, mimetype[:s]) && (spec[s:] == "/*" || mimetype[s:] == "/*") {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// getOffer return valid offer for header negotiation
|
||||||
|
func getOffer(header string, isAccepted func(spec, offer string) bool, offers ...string) string {
|
||||||
if len(offers) == 0 {
|
if len(offers) == 0 {
|
||||||
return ""
|
return ""
|
||||||
} else if header == "" {
|
} else if header == "" {
|
||||||
return offers[0]
|
return offers[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
spec, commaPos := "", 0
|
for _, offer := range offers {
|
||||||
for len(header) > 0 && commaPos != -1 {
|
if len(offer) == 0 {
|
||||||
commaPos = strings.IndexByte(header, ',')
|
continue
|
||||||
if commaPos != -1 {
|
|
||||||
spec = utils.Trim(header[:commaPos], ' ')
|
|
||||||
} else {
|
|
||||||
spec = header
|
|
||||||
}
|
|
||||||
if factorSign := strings.IndexByte(spec, ';'); factorSign != -1 {
|
|
||||||
spec = spec[:factorSign]
|
|
||||||
}
|
}
|
||||||
|
spec, commaPos := "", 0
|
||||||
|
for len(header) > 0 && commaPos != -1 {
|
||||||
|
commaPos = strings.IndexByte(header, ',')
|
||||||
|
if commaPos != -1 {
|
||||||
|
spec = utils.Trim(header[:commaPos], ' ')
|
||||||
|
} else {
|
||||||
|
spec = utils.TrimLeft(header, ' ')
|
||||||
|
}
|
||||||
|
if factorSign := strings.IndexByte(spec, ';'); factorSign != -1 {
|
||||||
|
spec = spec[:factorSign]
|
||||||
|
}
|
||||||
|
|
||||||
for _, offer := range offers {
|
// isAccepted if the current offer is accepted
|
||||||
// has star prefix
|
if isAccepted(spec, offer) {
|
||||||
if len(spec) >= 1 && spec[len(spec)-1] == '*' {
|
|
||||||
return offer
|
|
||||||
} else if strings.HasPrefix(spec, offer) {
|
|
||||||
return offer
|
return offer
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if commaPos != -1 {
|
if commaPos != -1 {
|
||||||
header = header[commaPos+1:]
|
header = header[commaPos+1:]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -223,9 +223,9 @@ func Test_Utils_Parse_Address(t *testing.T) {
|
||||||
|
|
||||||
func Test_Utils_GetOffset(t *testing.T) {
|
func Test_Utils_GetOffset(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
utils.AssertEqual(t, "", getOffer("hello"))
|
utils.AssertEqual(t, "", getOffer("hello", acceptsOffer))
|
||||||
utils.AssertEqual(t, "1", getOffer("", "1"))
|
utils.AssertEqual(t, "1", getOffer("", acceptsOffer, "1"))
|
||||||
utils.AssertEqual(t, "", getOffer("2", "1"))
|
utils.AssertEqual(t, "", getOffer("2", acceptsOffer, "1"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_Utils_TestConn_Deadline(t *testing.T) {
|
func Test_Utils_TestConn_Deadline(t *testing.T) {
|
||||||
|
|
Loading…
Reference in New Issue