Browse Source

Allow ".,;" as string pattern "break chars"

This allows you to match "/a/cat.gif" with patterns like "/a/:b.:c".

Thanks to @Minecrell for an early patch implementing this functionality.

Fixes #75.
Fixes #48.
Carl Jackson 11 years ago
parent
commit
1a390aba1c
2 changed files with 36 additions and 2 deletions
  1. +18
    -0
      web/pattern_test.go
  2. +18
    -2
      web/string_pattern.go

+ 18
- 0
web/pattern_test.go View File

@ -93,6 +93,9 @@ var patternTests = []struct {
pt("/hello/world", true, map[string]string{ pt("/hello/world", true, map[string]string{
"name": "world", "name": "world",
}), }),
pt("/hello/my.world;wow", true, map[string]string{
"name": "my.world;wow",
}),
pt("/hell", false, nil), pt("/hell", false, nil),
pt("/hello/", false, nil), pt("/hello/", false, nil),
pt("/hello/my/love", false, nil), pt("/hello/my/love", false, nil),
@ -107,6 +110,21 @@ var patternTests = []struct {
pt("/a//b/", false, nil), pt("/a//b/", false, nil),
pt("/a/1/b/2/3", false, nil), pt("/a/1/b/2/3", false, nil),
}}, }},
{parseStringPattern("/a/:b.:c"),
"/a/", []patternTest{
pt("/a/cat.gif", true, map[string]string{
"b": "cat",
"c": "gif",
}),
pt("/a/cat.tar.gz", true, map[string]string{
"b": "cat",
"c": "tar.gz",
}),
pt("/a", false, nil),
pt("/a/cat", false, nil),
pt("/a/cat/gif", false, nil),
pt("/a/cat.", false, nil),
}},
// String prefix tests // String prefix tests
{parseStringPattern("/user/:user*"), {parseStringPattern("/user/:user*"),


+ 18
- 2
web/string_pattern.go View File

@ -7,9 +7,11 @@ import (
"strings" "strings"
) )
// stringPattern is a struct describing
type stringPattern struct { type stringPattern struct {
raw string raw string
pats []string pats []string
breaks []byte
literals []string literals []string
isPrefix bool isPrefix bool
} }
@ -37,8 +39,9 @@ func (s stringPattern) match(r *http.Request, c *C, dryrun bool) bool {
path = path[len(sli):] path = path[len(sli):]
m := 0 m := 0
bc := s.breaks[i]
for ; m < len(path); m++ { for ; m < len(path); m++ {
if path[m] == '/' {
if path[m] == bc {
break break
} }
} }
@ -81,7 +84,13 @@ func (s stringPattern) String() string {
return fmt.Sprintf("stringPattern(%q, %v)", s.raw, s.isPrefix) return fmt.Sprintf("stringPattern(%q, %v)", s.raw, s.isPrefix)
} }
var patternRe = regexp.MustCompile(`/:([^/]+)`)
// "Break characters" are characters that can end patterns. They are not allowed
// to appear in pattern names. "/" was chosen because it is the standard path
// separator, and "." was chosen because it often delimits file extensions. ";"
// and "," were chosen because Section 3.3 of RFC 3986 suggests their use.
const bc = "/.;,"
var patternRe = regexp.MustCompile(`[` + bc + `]:([^` + bc + `]+)`)
func parseStringPattern(s string) stringPattern { func parseStringPattern(s string) stringPattern {
var isPrefix bool var isPrefix bool
@ -93,18 +102,25 @@ func parseStringPattern(s string) stringPattern {
matches := patternRe.FindAllStringSubmatchIndex(s, -1) matches := patternRe.FindAllStringSubmatchIndex(s, -1)
pats := make([]string, len(matches)) pats := make([]string, len(matches))
breaks := make([]byte, len(matches))
literals := make([]string, len(matches)+1) literals := make([]string, len(matches)+1)
n := 0 n := 0
for i, match := range matches { for i, match := range matches {
a, b := match[2], match[3] a, b := match[2], match[3]
literals[i] = s[n : a-1] // Need to leave off the colon literals[i] = s[n : a-1] // Need to leave off the colon
pats[i] = s[a:b] pats[i] = s[a:b]
if b == len(s) {
breaks[i] = '/'
} else {
breaks[i] = s[b]
}
n = b n = b
} }
literals[len(matches)] = s[n:] literals[len(matches)] = s[n:]
return stringPattern{ return stringPattern{
raw: s, raw: s,
pats: pats, pats: pats,
breaks: breaks,
literals: literals, literals: literals,
isPrefix: isPrefix, isPrefix: isPrefix,
} }


Loading…
Cancel
Save