1
39
40 package goautoneg
41
42 import (
43 "sort"
44 "strconv"
45 "strings"
46 )
47
48
49 type Accept struct {
50 Type, SubType string
51 Q float64
52 Params map[string]string
53 }
54
55
56 type acceptSlice []Accept
57
58 func (slice acceptSlice) Len() int {
59 return len(slice)
60 }
61
62 func (slice acceptSlice) Less(i, j int) bool {
63 ai, aj := slice[i], slice[j]
64 if ai.Q > aj.Q {
65 return true
66 }
67 if ai.Type != "*" && aj.Type == "*" {
68 return true
69 }
70 if ai.SubType != "*" && aj.SubType == "*" {
71 return true
72 }
73 return false
74 }
75
76 func (slice acceptSlice) Swap(i, j int) {
77 slice[i], slice[j] = slice[j], slice[i]
78 }
79
80 func stringTrimSpaceCutset(r rune) bool {
81 return r == ' '
82 }
83
84 func nextSplitElement(s, sep string) (item string, remaining string) {
85 if index := strings.Index(s, sep); index != -1 {
86 return s[:index], s[index+1:]
87 }
88 return s, ""
89 }
90
91
92
93 func ParseAccept(header string) acceptSlice {
94 partsCount := 0
95 remaining := header
96 for len(remaining) > 0 {
97 partsCount++
98 _, remaining = nextSplitElement(remaining, ",")
99 }
100 accept := make(acceptSlice, 0, partsCount)
101
102 remaining = header
103 var part string
104 for len(remaining) > 0 {
105 part, remaining = nextSplitElement(remaining, ",")
106 part = strings.TrimFunc(part, stringTrimSpaceCutset)
107
108 a := Accept{
109 Q: 1.0,
110 }
111
112 sp, remainingPart := nextSplitElement(part, ";")
113
114 sp0, spRemaining := nextSplitElement(sp, "/")
115 a.Type = strings.TrimFunc(sp0, stringTrimSpaceCutset)
116
117 switch {
118 case len(spRemaining) == 0:
119 if a.Type == "*" {
120 a.SubType = "*"
121 } else {
122 continue
123 }
124 default:
125 var sp1 string
126 sp1, spRemaining = nextSplitElement(spRemaining, "/")
127 if len(spRemaining) > 0 {
128 continue
129 }
130 a.SubType = strings.TrimFunc(sp1, stringTrimSpaceCutset)
131 }
132
133 if len(remainingPart) == 0 {
134 accept = append(accept, a)
135 continue
136 }
137
138 a.Params = make(map[string]string)
139 for len(remainingPart) > 0 {
140 sp, remainingPart = nextSplitElement(remainingPart, ";")
141 sp0, spRemaining = nextSplitElement(sp, "=")
142 if len(spRemaining) == 0 {
143 continue
144 }
145 var sp1 string
146 sp1, spRemaining = nextSplitElement(spRemaining, "=")
147 if len(spRemaining) != 0 {
148 continue
149 }
150 token := strings.TrimFunc(sp0, stringTrimSpaceCutset)
151 if token == "q" {
152 a.Q, _ = strconv.ParseFloat(sp1, 32)
153 } else {
154 a.Params[token] = strings.TrimFunc(sp1, stringTrimSpaceCutset)
155 }
156 }
157
158 accept = append(accept, a)
159 }
160
161 sort.Sort(accept)
162 return accept
163 }
164
165
166
167 func Negotiate(header string, alternatives []string) (content_type string) {
168 asp := make([][]string, 0, len(alternatives))
169 for _, ctype := range alternatives {
170 asp = append(asp, strings.SplitN(ctype, "/", 2))
171 }
172 for _, clause := range ParseAccept(header) {
173 for i, ctsp := range asp {
174 if clause.Type == ctsp[0] && clause.SubType == ctsp[1] {
175 content_type = alternatives[i]
176 return
177 }
178 if clause.Type == ctsp[0] && clause.SubType == "*" {
179 content_type = alternatives[i]
180 return
181 }
182 if clause.Type == "*" && clause.SubType == "*" {
183 content_type = alternatives[i]
184 return
185 }
186 }
187 }
188 return
189 }
190
View as plain text