go项目脚手架
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
sorbet/pkg/rsp/accept.go

172 lines
4.2 KiB

package rsp
import (
"fmt"
"sort"
"strconv"
"strings"
)
var errInvalidTypeSubtype = "accept: Invalid type '%s'."
// headerAccept represents a parsed headerAccept(-Charset|-Encoding|-Language) header.
type headerAccept struct {
Type, Subtype string
Q float64
Extensions map[string]string
}
// AcceptSlice is a slice of headerAccept.
type acceptSlice []headerAccept
func Accepts(header, expect string) bool {
_, typeSubtype, err := parseMediaRange(expect)
if err != nil {
return false
}
return accepts(parse(header), typeSubtype[0], typeSubtype[1])
}
func accepts(slice acceptSlice, typ, sub string) bool {
for _, a := range slice {
if a.Type != typ {
continue
}
if a.Subtype == "*" || a.Subtype == sub {
return true
}
}
return false
}
// parses a HTTP headerAccept(-Charset|-Encoding|-Language) header and returns
// AcceptSlice, sorted in decreasing order of preference. If the header lists
// multiple types that have the same level of preference (same specificity of
// type and subtype, same qvalue, and same number of extensions), the type
// that was listed in the header first comes first in the returned value.
//
// See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14 for more information.
func parse(header string) acceptSlice {
mediaRanges := strings.Split(header, ",")
accepted := make(acceptSlice, 0, len(mediaRanges))
for _, mediaRange := range mediaRanges {
rangeParams, typeSubtype, err := parseMediaRange(mediaRange)
if err != nil {
continue
}
accept := headerAccept{
Type: typeSubtype[0],
Subtype: typeSubtype[1],
Q: 1.0,
Extensions: make(map[string]string),
}
// If there is only one rangeParams, we can stop here.
if len(rangeParams) == 1 {
accepted = append(accepted, accept)
continue
}
// Validate the rangeParams.
validParams := true
for _, v := range rangeParams[1:] {
nameVal := strings.SplitN(v, "=", 2)
if len(nameVal) != 2 {
validParams = false
break
}
nameVal[1] = strings.TrimSpace(nameVal[1])
if name := strings.TrimSpace(nameVal[0]); name == "q" {
qval, err := strconv.ParseFloat(nameVal[1], 64)
if err != nil || qval < 0 {
validParams = false
break
}
if qval > 1.0 {
qval = 1.0
}
accept.Q = qval
} else {
accept.Extensions[name] = nameVal[1]
}
}
if validParams {
accepted = append(accepted, accept)
}
}
sort.Sort(accepted)
return accepted
}
// Len implements the Len() method of the Sort interface.
func (a acceptSlice) Len() int {
return len(a)
}
// Less implements the Less() method of the Sort interface. Elements are
// sorted in order of decreasing preference.
func (a acceptSlice) Less(i, j int) bool {
// Higher qvalues come first.
if a[i].Q > a[j].Q {
return true
} else if a[i].Q < a[j].Q {
return false
}
// Specific types come before wildcard types.
if a[i].Type != "*" && a[j].Type == "*" {
return true
} else if a[i].Type == "*" && a[j].Type != "*" {
return false
}
// Specific subtypes come before wildcard subtypes.
if a[i].Subtype != "*" && a[j].Subtype == "*" {
return true
} else if a[i].Subtype == "*" && a[j].Subtype != "*" {
return false
}
// A lot of extensions comes before not a lot of extensions.
if len(a[i].Extensions) > len(a[j].Extensions) {
return true
}
return false
}
// Swap implements the Swap() method of the Sort interface.
func (a acceptSlice) Swap(i, j int) {
a[i], a[j] = a[j], a[i]
}
// parseMediaRange parses the provided media range, and on success returns the
// parsed range params and type/subtype pair.
func parseMediaRange(mediaRange string) (rangeParams, typeSubtype []string, err error) {
rangeParams = strings.Split(mediaRange, ";")
typeSubtype = strings.Split(rangeParams[0], "/")
// typeSubtype should have a length of exactly two.
if len(typeSubtype) > 2 {
err = fmt.Errorf(errInvalidTypeSubtype, rangeParams[0])
return
} else {
typeSubtype = append(typeSubtype, "*")
}
// Sanitize typeSubtype.
typeSubtype[0] = strings.TrimSpace(typeSubtype[0])
typeSubtype[1] = strings.TrimSpace(typeSubtype[1])
if typeSubtype[0] == "" {
typeSubtype[0] = "*"
}
if typeSubtype[1] == "" {
typeSubtype[1] = "*"
}
return
}