-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathaccept.go
More file actions
134 lines (119 loc) · 3.51 KB
/
accept.go
File metadata and controls
134 lines (119 loc) · 3.51 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
// Copyright 2013 Ryan Rogers. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package accept allows for easy handling of HTTP Accept headers.
// Accept-Ranges is currently not handled.
package accept
import (
"sort"
"strconv"
"strings"
)
// Accept represents a parsed Accept(-Charset|-Encoding|-Language) header.
type Accept struct {
Type, Subtype string
Q float64
Extensions map[string]string
}
// AcceptSlice is a slice of Accept.
type AcceptSlice []Accept
// Parse parses a HTTP Accept(-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 := Accept{
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
}
// Negotiate returns a type that is accepted by both the header declaration,
// and the list of types provided. If no common types are found, an empty
// string is returned.
func Negotiate(header string, ctypes ...string) (string, error) {
a := Parse(header)
return a.Negotiate(ctypes...)
}
// Negotiate returns a type that is accepted by both the AcceptSlice, and the
// list of types provided. If no common types are found, an empty string is
// returned.
func (accept AcceptSlice) Negotiate(ctypes ...string) (string, error) {
if len(ctypes) == 0 {
return "", nil
}
typeSubtypes := make([][]string, 0, len(ctypes))
for _, v := range ctypes {
_, ts, err := parseMediaRange(v)
if err != nil {
return "", err
}
if ts[0] == "*" && ts[1] == "*" {
return v, nil
}
typeSubtypes = append(typeSubtypes, ts)
}
for _, a := range accept {
for i, ts := range typeSubtypes {
if ((a.Type == ts[0] || a.Type == "*") && (a.Subtype == ts[1] || a.Subtype == "*")) ||
(ts[0] == "*" && ts[1] == a.Subtype) ||
(ts[0] == a.Type && ts[1] == "*") {
return ctypes[i], nil
}
}
}
return "", nil
}
// Accepts returns true if the provided type is accepted.
func (accept AcceptSlice) Accepts(ctype string) bool {
t, err := accept.Negotiate(ctype)
if t == "" || err != nil {
return false
}
return true
}