]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/github.com/fsouza/go-dockerclient/external/github.com/gorilla/mux/regexp.go
provider: Ensured Go 1.11 in TravisCI and README
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / fsouza / go-dockerclient / external / github.com / gorilla / mux / regexp.go
1 // Copyright 2012 The Gorilla Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 package mux
6
7 import (
8 "bytes"
9 "fmt"
10 "net/http"
11 "net/url"
12 "regexp"
13 "strconv"
14 "strings"
15 )
16
17 // newRouteRegexp parses a route template and returns a routeRegexp,
18 // used to match a host, a path or a query string.
19 //
20 // It will extract named variables, assemble a regexp to be matched, create
21 // a "reverse" template to build URLs and compile regexps to validate variable
22 // values used in URL building.
23 //
24 // Previously we accepted only Python-like identifiers for variable
25 // names ([a-zA-Z_][a-zA-Z0-9_]*), but currently the only restriction is that
26 // name and pattern can't be empty, and names can't contain a colon.
27 func newRouteRegexp(tpl string, matchHost, matchPrefix, matchQuery, strictSlash bool) (*routeRegexp, error) {
28 // Check if it is well-formed.
29 idxs, errBraces := braceIndices(tpl)
30 if errBraces != nil {
31 return nil, errBraces
32 }
33 // Backup the original.
34 template := tpl
35 // Now let's parse it.
36 defaultPattern := "[^/]+"
37 if matchQuery {
38 defaultPattern = "[^?&]*"
39 } else if matchHost {
40 defaultPattern = "[^.]+"
41 matchPrefix = false
42 }
43 // Only match strict slash if not matching
44 if matchPrefix || matchHost || matchQuery {
45 strictSlash = false
46 }
47 // Set a flag for strictSlash.
48 endSlash := false
49 if strictSlash && strings.HasSuffix(tpl, "/") {
50 tpl = tpl[:len(tpl)-1]
51 endSlash = true
52 }
53 varsN := make([]string, len(idxs)/2)
54 varsR := make([]*regexp.Regexp, len(idxs)/2)
55 pattern := bytes.NewBufferString("")
56 pattern.WriteByte('^')
57 reverse := bytes.NewBufferString("")
58 var end int
59 var err error
60 for i := 0; i < len(idxs); i += 2 {
61 // Set all values we are interested in.
62 raw := tpl[end:idxs[i]]
63 end = idxs[i+1]
64 parts := strings.SplitN(tpl[idxs[i]+1:end-1], ":", 2)
65 name := parts[0]
66 patt := defaultPattern
67 if len(parts) == 2 {
68 patt = parts[1]
69 }
70 // Name or pattern can't be empty.
71 if name == "" || patt == "" {
72 return nil, fmt.Errorf("mux: missing name or pattern in %q",
73 tpl[idxs[i]:end])
74 }
75 // Build the regexp pattern.
76 varIdx := i / 2
77 fmt.Fprintf(pattern, "%s(?P<%s>%s)", regexp.QuoteMeta(raw), varGroupName(varIdx), patt)
78 // Build the reverse template.
79 fmt.Fprintf(reverse, "%s%%s", raw)
80
81 // Append variable name and compiled pattern.
82 varsN[varIdx] = name
83 varsR[varIdx], err = regexp.Compile(fmt.Sprintf("^%s$", patt))
84 if err != nil {
85 return nil, err
86 }
87 }
88 // Add the remaining.
89 raw := tpl[end:]
90 pattern.WriteString(regexp.QuoteMeta(raw))
91 if strictSlash {
92 pattern.WriteString("[/]?")
93 }
94 if matchQuery {
95 // Add the default pattern if the query value is empty
96 if queryVal := strings.SplitN(template, "=", 2)[1]; queryVal == "" {
97 pattern.WriteString(defaultPattern)
98 }
99 }
100 if !matchPrefix {
101 pattern.WriteByte('$')
102 }
103 reverse.WriteString(raw)
104 if endSlash {
105 reverse.WriteByte('/')
106 }
107 // Compile full regexp.
108 reg, errCompile := regexp.Compile(pattern.String())
109 if errCompile != nil {
110 return nil, errCompile
111 }
112 // Done!
113 return &routeRegexp{
114 template: template,
115 matchHost: matchHost,
116 matchQuery: matchQuery,
117 strictSlash: strictSlash,
118 regexp: reg,
119 reverse: reverse.String(),
120 varsN: varsN,
121 varsR: varsR,
122 }, nil
123 }
124
125 // routeRegexp stores a regexp to match a host or path and information to
126 // collect and validate route variables.
127 type routeRegexp struct {
128 // The unmodified template.
129 template string
130 // True for host match, false for path or query string match.
131 matchHost bool
132 // True for query string match, false for path and host match.
133 matchQuery bool
134 // The strictSlash value defined on the route, but disabled if PathPrefix was used.
135 strictSlash bool
136 // Expanded regexp.
137 regexp *regexp.Regexp
138 // Reverse template.
139 reverse string
140 // Variable names.
141 varsN []string
142 // Variable regexps (validators).
143 varsR []*regexp.Regexp
144 }
145
146 // Match matches the regexp against the URL host or path.
147 func (r *routeRegexp) Match(req *http.Request, match *RouteMatch) bool {
148 if !r.matchHost {
149 if r.matchQuery {
150 return r.matchQueryString(req)
151 } else {
152 return r.regexp.MatchString(req.URL.Path)
153 }
154 }
155 return r.regexp.MatchString(getHost(req))
156 }
157
158 // url builds a URL part using the given values.
159 func (r *routeRegexp) url(values map[string]string) (string, error) {
160 urlValues := make([]interface{}, len(r.varsN))
161 for k, v := range r.varsN {
162 value, ok := values[v]
163 if !ok {
164 return "", fmt.Errorf("mux: missing route variable %q", v)
165 }
166 urlValues[k] = value
167 }
168 rv := fmt.Sprintf(r.reverse, urlValues...)
169 if !r.regexp.MatchString(rv) {
170 // The URL is checked against the full regexp, instead of checking
171 // individual variables. This is faster but to provide a good error
172 // message, we check individual regexps if the URL doesn't match.
173 for k, v := range r.varsN {
174 if !r.varsR[k].MatchString(values[v]) {
175 return "", fmt.Errorf(
176 "mux: variable %q doesn't match, expected %q", values[v],
177 r.varsR[k].String())
178 }
179 }
180 }
181 return rv, nil
182 }
183
184 // getUrlQuery returns a single query parameter from a request URL.
185 // For a URL with foo=bar&baz=ding, we return only the relevant key
186 // value pair for the routeRegexp.
187 func (r *routeRegexp) getUrlQuery(req *http.Request) string {
188 if !r.matchQuery {
189 return ""
190 }
191 templateKey := strings.SplitN(r.template, "=", 2)[0]
192 for key, vals := range req.URL.Query() {
193 if key == templateKey && len(vals) > 0 {
194 return key + "=" + vals[0]
195 }
196 }
197 return ""
198 }
199
200 func (r *routeRegexp) matchQueryString(req *http.Request) bool {
201 return r.regexp.MatchString(r.getUrlQuery(req))
202 }
203
204 // braceIndices returns the first level curly brace indices from a string.
205 // It returns an error in case of unbalanced braces.
206 func braceIndices(s string) ([]int, error) {
207 var level, idx int
208 idxs := make([]int, 0)
209 for i := 0; i < len(s); i++ {
210 switch s[i] {
211 case '{':
212 if level++; level == 1 {
213 idx = i
214 }
215 case '}':
216 if level--; level == 0 {
217 idxs = append(idxs, idx, i+1)
218 } else if level < 0 {
219 return nil, fmt.Errorf("mux: unbalanced braces in %q", s)
220 }
221 }
222 }
223 if level != 0 {
224 return nil, fmt.Errorf("mux: unbalanced braces in %q", s)
225 }
226 return idxs, nil
227 }
228
229 // varGroupName builds a capturing group name for the indexed variable.
230 func varGroupName(idx int) string {
231 return "v" + strconv.Itoa(idx)
232 }
233
234 // ----------------------------------------------------------------------------
235 // routeRegexpGroup
236 // ----------------------------------------------------------------------------
237
238 // routeRegexpGroup groups the route matchers that carry variables.
239 type routeRegexpGroup struct {
240 host *routeRegexp
241 path *routeRegexp
242 queries []*routeRegexp
243 }
244
245 // setMatch extracts the variables from the URL once a route matches.
246 func (v *routeRegexpGroup) setMatch(req *http.Request, m *RouteMatch, r *Route) {
247 // Store host variables.
248 if v.host != nil {
249 hostVars := v.host.regexp.FindStringSubmatch(getHost(req))
250 if hostVars != nil {
251 subexpNames := v.host.regexp.SubexpNames()
252 varName := 0
253 for i, name := range subexpNames[1:] {
254 if name != "" && name == varGroupName(varName) {
255 m.Vars[v.host.varsN[varName]] = hostVars[i+1]
256 varName++
257 }
258 }
259 }
260 }
261 // Store path variables.
262 if v.path != nil {
263 pathVars := v.path.regexp.FindStringSubmatch(req.URL.Path)
264 if pathVars != nil {
265 subexpNames := v.path.regexp.SubexpNames()
266 varName := 0
267 for i, name := range subexpNames[1:] {
268 if name != "" && name == varGroupName(varName) {
269 m.Vars[v.path.varsN[varName]] = pathVars[i+1]
270 varName++
271 }
272 }
273 // Check if we should redirect.
274 if v.path.strictSlash {
275 p1 := strings.HasSuffix(req.URL.Path, "/")
276 p2 := strings.HasSuffix(v.path.template, "/")
277 if p1 != p2 {
278 u, _ := url.Parse(req.URL.String())
279 if p1 {
280 u.Path = u.Path[:len(u.Path)-1]
281 } else {
282 u.Path += "/"
283 }
284 m.Handler = http.RedirectHandler(u.String(), 301)
285 }
286 }
287 }
288 }
289 // Store query string variables.
290 for _, q := range v.queries {
291 queryVars := q.regexp.FindStringSubmatch(q.getUrlQuery(req))
292 if queryVars != nil {
293 subexpNames := q.regexp.SubexpNames()
294 varName := 0
295 for i, name := range subexpNames[1:] {
296 if name != "" && name == varGroupName(varName) {
297 m.Vars[q.varsN[varName]] = queryVars[i+1]
298 varName++
299 }
300 }
301 }
302 }
303 }
304
305 // getHost tries its best to return the request host.
306 func getHost(r *http.Request) string {
307 if r.URL.IsAbs() {
308 return r.URL.Host
309 }
310 host := r.Host
311 // Slice off any port information.
312 if i := strings.Index(host, ":"); i != -1 {
313 host = host[:i]
314 }
315 return host
316
317 }