Vendor main dependencies.

This commit is contained in:
Timo Reimann 2017-02-07 22:33:23 +01:00
parent 49a09ab7dd
commit dd5e3fba01
2738 changed files with 1045689 additions and 0 deletions

202
vendor/github.com/vulcand/route/LICENSE generated vendored Normal file
View file

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

88
vendor/github.com/vulcand/route/iter.go generated vendored Normal file
View file

@ -0,0 +1,88 @@
package route
import (
"fmt"
)
// charPos stores the position in the iterator
type charPos struct {
i int
si int
}
// charIter is a iterator over sequence of strings, returns byte-by-byte characters in string by string
type charIter struct {
i int // position in the current string
si int // position in the array of strings
seq []string // sequence of strings, e.g. ["GET", "/path"]
sep []byte // every string in the sequence has an associated separator used for trie matching, e.g. path uses '/' for separator
// so sequence ["a.host", "/path "]has acoompanying separators ['.', '/']
}
func newIter(seq []string, sep []byte) *charIter {
return &charIter{
i: 0,
si: 0,
seq: seq,
sep: sep,
}
}
func (r *charIter) level() int {
return r.si
}
func (r *charIter) String() string {
if r.isEnd() {
return "<end>"
}
return fmt.Sprintf("<%d:%v>", r.i, r.seq[r.si])
}
func (r *charIter) isEnd() bool {
return len(r.seq) == 0 || // no data at all
(r.si >= len(r.seq)-1 && r.i >= len(r.seq[r.si])) || // we are at the last char of last seq
(len(r.seq[r.si]) == 0) // empty input
}
func (r *charIter) position() charPos {
return charPos{i: r.i, si: r.si}
}
func (r *charIter) setPosition(p charPos) {
r.i = p.i
r.si = p.si
}
func (r *charIter) pushBack() {
if r.i == 0 && r.si == 0 { // this is start
return
} else if r.i == 0 && r.si != 0 { // this is start of the next string
r.si--
r.i = len(r.seq[r.si]) - 1
return
}
r.i--
}
// next returns current byte in the sequence, separator corresponding to that byte, and boolean idicator of whether it's the end of the sequence
func (r *charIter) next() (byte, byte, bool) {
// we have reached the last string in the index, end
if r.isEnd() {
return 0, 0, false
}
b := r.seq[r.si][r.i]
sep := r.sep[r.si]
r.i++
// current string index exceeded the last char of the current string
// move to the next string if it's present
if r.i >= len(r.seq[r.si]) && r.si < len(r.seq)-1 {
r.si++
r.i = 0
}
return b, sep, true
}

186
vendor/github.com/vulcand/route/mapper.go generated vendored Normal file
View file

@ -0,0 +1,186 @@
package route
import (
"net/http"
"strings"
)
// requestMapper maps the request to string e.g. maps request to it's hostname, or request to header
type requestMapper interface {
// separator returns the separator that makes sense for this request, e.g. / for urls or . for domains
separator() byte
// equals returns the equivalent mapper if the two mappers are equivalent, e.g. map to the same sequence
// mappers are also equivalent if one mapper is subset of another, e.g. combined mapper (host, path) is equivalent of (host) mapper
equivalent(requestMapper) requestMapper
// mapRequest maps request to string, e.g. request to it's URL path
mapRequest(r *http.Request) string
// newIter returns the iterator instead of string for stream matchers
newIter(r *http.Request) *charIter
}
type methodMapper struct {
}
func (m *methodMapper) separator() byte {
return methodSep
}
func (m *methodMapper) equivalent(o requestMapper) requestMapper {
_, ok := o.(*methodMapper)
if ok {
return m
}
return nil
}
func (m *methodMapper) mapRequest(r *http.Request) string {
return r.Method
}
func (m *methodMapper) newIter(r *http.Request) *charIter {
return newIter([]string{m.mapRequest(r)}, []byte{m.separator()})
}
type pathMapper struct {
}
func (m *pathMapper) separator() byte {
return pathSep
}
func (p *pathMapper) equivalent(o requestMapper) requestMapper {
_, ok := o.(*pathMapper)
if ok {
return p
}
return nil
}
func (p *pathMapper) newIter(r *http.Request) *charIter {
return newIter([]string{p.mapRequest(r)}, []byte{p.separator()})
}
func (p *pathMapper) mapRequest(r *http.Request) string {
return rawPath(r)
}
type hostMapper struct {
}
func (p *hostMapper) equivalent(o requestMapper) requestMapper {
_, ok := o.(*hostMapper)
if ok {
return p
}
return nil
}
func (m *hostMapper) separator() byte {
return domainSep
}
func (h *hostMapper) mapRequest(r *http.Request) string {
return strings.Split(strings.ToLower(r.Host), ":")[0]
}
func (p *hostMapper) newIter(r *http.Request) *charIter {
return newIter([]string{p.mapRequest(r)}, []byte{p.separator()})
}
type headerMapper struct {
header string
}
func (h *headerMapper) equivalent(o requestMapper) requestMapper {
hm, ok := o.(*headerMapper)
if ok && hm.header == h.header {
return h
}
return nil
}
func (m *headerMapper) separator() byte {
return headerSep
}
func (h *headerMapper) mapRequest(r *http.Request) string {
return r.Header.Get(h.header)
}
func (h *headerMapper) newIter(r *http.Request) *charIter {
return newIter([]string{h.mapRequest(r)}, []byte{h.separator()})
}
type seqMapper struct {
seq []requestMapper
}
func newSeqMapper(seq ...requestMapper) *seqMapper {
var out []requestMapper
for _, s := range seq {
switch m := s.(type) {
case *seqMapper:
out = append(out, m.seq...)
default:
out = append(out, s)
}
}
return &seqMapper{seq: out}
}
func (s *seqMapper) newIter(r *http.Request) *charIter {
out := make([]string, len(s.seq))
for i := range s.seq {
out[i] = s.seq[i].mapRequest(r)
}
seps := make([]byte, len(s.seq))
for i := range s.seq {
seps[i] = s.seq[i].separator()
}
return newIter(out, seps)
}
func (s *seqMapper) mapRequest(r *http.Request) string {
out := make([]string, len(s.seq))
for i := range s.seq {
out[i] = s.seq[i].mapRequest(r)
}
return strings.Join(out, "")
}
func (s *seqMapper) separator() byte {
return s.seq[0].separator()
}
func (s *seqMapper) equivalent(o requestMapper) requestMapper {
so, ok := o.(*seqMapper)
if !ok {
return nil
}
var longer, shorter *seqMapper
if len(s.seq) > len(so.seq) {
longer = s
shorter = so
} else {
longer = so
shorter = s
}
for i, _ := range longer.seq {
// shorter is subset of longer, return longer sequence mapper
if i >= len(shorter.seq)-1 {
return longer
}
if longer.seq[i].equivalent(shorter.seq[i]) == nil {
return nil
}
}
return longer
}
const (
pathSep = '/'
domainSep = '.'
headerSep = '/'
methodSep = ' '
)

155
vendor/github.com/vulcand/route/matcher.go generated vendored Normal file
View file

@ -0,0 +1,155 @@
package route
import (
"fmt"
"net/http"
"regexp"
"strings"
)
type matcher interface {
match(*http.Request) *match
setMatch(match *match)
canMerge(matcher) bool
merge(matcher) (matcher, error)
canChain(matcher) bool
chain(matcher) (matcher, error)
}
func hostTrieMatcher(hostname string) (matcher, error) {
return newTrieMatcher(strings.ToLower(hostname), &hostMapper{}, &match{})
}
func hostRegexpMatcher(hostname string) (matcher, error) {
return newRegexpMatcher(strings.ToLower(hostname), &hostMapper{}, &match{})
}
func methodTrieMatcher(method string) (matcher, error) {
return newTrieMatcher(method, &methodMapper{}, &match{})
}
func methodRegexpMatcher(method string) (matcher, error) {
return newRegexpMatcher(method, &methodMapper{}, &match{})
}
func pathTrieMatcher(path string) (matcher, error) {
return newTrieMatcher(path, &pathMapper{}, &match{})
}
func pathRegexpMatcher(path string) (matcher, error) {
return newRegexpMatcher(path, &pathMapper{}, &match{})
}
func headerTrieMatcher(name, value string) (matcher, error) {
return newTrieMatcher(value, &headerMapper{header: name}, &match{})
}
func headerRegexpMatcher(name, value string) (matcher, error) {
return newRegexpMatcher(value, &headerMapper{header: name}, &match{})
}
type match struct {
val interface{}
}
type andMatcher struct {
a matcher
b matcher
}
func newAndMatcher(a, b matcher) matcher {
if a.canChain(b) {
m, err := a.chain(b)
if err == nil {
return m
}
}
return &andMatcher{
a: a, b: b,
}
}
func (a *andMatcher) canChain(matcher) bool {
return false
}
func (a *andMatcher) chain(matcher) (matcher, error) {
return nil, fmt.Errorf("not supported")
}
func (a *andMatcher) String() string {
return fmt.Sprintf("andMatcher(%v, %v)", a.a, a.b)
}
func (a *andMatcher) setMatch(m *match) {
a.a.setMatch(m)
a.b.setMatch(m)
}
func (a *andMatcher) canMerge(o matcher) bool {
return false
}
func (a *andMatcher) merge(o matcher) (matcher, error) {
return nil, fmt.Errorf("Method not supported")
}
func (a *andMatcher) match(req *http.Request) *match {
result := a.a.match(req)
if result == nil {
return nil
}
return a.b.match(req)
}
// Regular expression matcher, takes a regular expression and requestMapper
type regexpMatcher struct {
// Uses this mapper to extract a string from a request to match against
mapper requestMapper
// Compiled regular expression
expr *regexp.Regexp
// match result
result *match
}
func (r *regexpMatcher) canChain(matcher) bool {
return false
}
func (r *regexpMatcher) chain(matcher) (matcher, error) {
return nil, fmt.Errorf("not supported")
}
func (m *regexpMatcher) String() string {
return fmt.Sprintf("regexpMatcher(%v)", m.expr)
}
func (m *regexpMatcher) setMatch(result *match) {
m.result = result
}
func newRegexpMatcher(expr string, mapper requestMapper, m *match) (matcher, error) {
r, err := regexp.Compile(expr)
if err != nil {
return nil, fmt.Errorf("Bad regular expression: %s %s", expr, err)
}
return &regexpMatcher{expr: r, mapper: mapper, result: m}, nil
}
func (m *regexpMatcher) canMerge(matcher) bool {
return false
}
func (m *regexpMatcher) merge(matcher) (matcher, error) {
return nil, fmt.Errorf("Method not supported")
}
func (m *regexpMatcher) match(req *http.Request) *match {
if m.expr.MatchString(m.mapper.mapRequest(req)) {
return m.result
}
return nil
}

74
vendor/github.com/vulcand/route/mux.go generated vendored Normal file
View file

@ -0,0 +1,74 @@
package route
import (
"fmt"
"net/http"
)
// Mux implements router compatible with http.Handler
type Mux struct {
// NotFound sets handler for routes that are not found
notFound http.Handler
router Router
}
// NewMux returns new Mux router
func NewMux() *Mux {
return &Mux{
router: New(),
notFound: &notFound{},
}
}
// Handle adds http handler for route expression
func (m *Mux) Handle(expr string, handler http.Handler) error {
return m.router.UpsertRoute(expr, handler)
}
// Handle adds http handler function for route expression
func (m *Mux) HandleFunc(expr string, handler func(http.ResponseWriter, *http.Request)) error {
return m.Handle(expr, http.HandlerFunc(handler))
}
func (m *Mux) Remove(expr string) error {
return m.router.RemoveRoute(expr)
}
// ServeHTTP routes the request and passes it to handler
func (m *Mux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
h, err := m.router.Route(r)
if err != nil || h == nil {
m.notFound.ServeHTTP(w, r)
return
}
h.(http.Handler).ServeHTTP(w, r)
}
func (m *Mux) SetNotFound(n http.Handler) error {
if n == nil {
return fmt.Errorf("Not Found handler cannot be nil. Operation rejected.")
}
m.notFound = n
return nil
}
func (m *Mux) GetNotFound() http.Handler {
return m.notFound
}
func (m *Mux) IsValid(expr string) bool {
return IsValid(expr)
}
// NotFound is a generic http.Handler for request
type notFound struct {
}
// ServeHTTP returns a simple 404 Not found response
func (notFound) ServeHTTP(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/plain")
w.WriteHeader(http.StatusNotFound)
fmt.Fprint(w, "Not found")
}

47
vendor/github.com/vulcand/route/parse.go generated vendored Normal file
View file

@ -0,0 +1,47 @@
package route
import (
"fmt"
"github.com/vulcand/predicate"
)
// IsValid checks whether expression is valid
func IsValid(expr string) bool {
_, err := parse(expr, &match{})
return err == nil
}
func parse(expression string, result *match) (matcher, error) {
p, err := predicate.NewParser(predicate.Def{
Functions: map[string]interface{}{
"Host": hostTrieMatcher,
"HostRegexp": hostRegexpMatcher,
"Path": pathTrieMatcher,
"PathRegexp": pathRegexpMatcher,
"Method": methodTrieMatcher,
"MethodRegexp": methodRegexpMatcher,
"Header": headerTrieMatcher,
"HeaderRegexp": headerRegexpMatcher,
},
Operators: predicate.Operators{
AND: newAndMatcher,
},
})
if err != nil {
return nil, err
}
out, err := p.Parse(expression)
if err != nil {
return nil, err
}
m, ok := out.(matcher)
if !ok {
return nil, fmt.Errorf("unknown result type: %T", out)
}
m.setMatch(result)
return m, nil
}

195
vendor/github.com/vulcand/route/router.go generated vendored Normal file
View file

@ -0,0 +1,195 @@
/*
package route provides http package-compatible routing library. It can route http requests by by hostname, method, path and headers.
Route defines simple language for matching requests based on Go syntax. Route provides series of matchers that follow the syntax:
Matcher("value") // matches value using trie
Matcher("<string>.value") // uses trie-based matching for a.value and b.value
MatcherRegexp(".*value") // uses regexp-based matching
Host matcher:
Host("<subdomain>.localhost") // trie-based matcher for a.localhost, b.localhost, etc.
HostRegexp(".*localhost") // regexp based matcher
Path matcher:
Path("/hello/<value>") // trie-based matcher for raw request path
PathRegexp("/hello/.*") // regexp-based matcher for raw request path
Method matcher:
Method("GET") // trie-based matcher for request method
MethodRegexp("POST|PUT") // regexp based matcher for request method
Header matcher:
Header("Content-Type", "application/<subtype>") // trie-based matcher for headers
HeaderRegexp("Content-Type", "application/.*") // regexp based matcher for headers
Matchers can be combined using && operator:
Host("localhost") && Method("POST") && Path("/v1")
Route library will join the trie-based matchers into one trie matcher when possible, for example:
Host("localhost") && Method("POST") && Path("/v1")
Host("localhost") && Method("GET") && Path("/v2")
Will be combined into one trie for performance. If you add a third route:
Host("localhost") && Method("GET") && PathRegexp("/v2/.*")
It wont be joined ito the trie, and would be matched separatedly instead.
*/
package route
import (
"fmt"
"net/http"
"sort"
"sync"
)
// Router implements http request routing and operations. It is a generic router not conforming to http.Handler interface, to get a handler
// conforming to http.Handler interface, use Mux router instead.
type Router interface {
// GetRoute returns a route by a given expression, returns nil if expresison is not found
GetRoute(string) interface{}
// AddRoute adds a route to match by expression, returns error if the expression already defined, or route expression is incorrect
AddRoute(string, interface{}) error
// RemoveRoute removes a route for a given expression
RemoveRoute(string) error
// UpsertRoute updates an existing route or adds a new route by given expression
UpsertRoute(string, interface{}) error
// Route takes a request and matches it against requests, returns matched route in case if found, nil if there's no matching route or error in case of internal error.
Route(*http.Request) (interface{}, error)
}
type router struct {
mutex *sync.RWMutex
matchers []matcher
routes map[string]*match
}
// New creates a new Router instance
func New() Router {
return &router{
mutex: &sync.RWMutex{},
routes: make(map[string]*match),
}
}
func (e *router) GetRoute(expr string) interface{} {
e.mutex.RLock()
defer e.mutex.RUnlock()
res, ok := e.routes[expr]
if ok {
return res.val
}
return nil
}
func (e *router) AddRoute(expr string, val interface{}) error {
e.mutex.Lock()
defer e.mutex.Unlock()
if _, ok := e.routes[expr]; ok {
return fmt.Errorf("Expression '%s' already exists", expr)
}
result := &match{val: val}
if _, err := parse(expr, result); err != nil {
return err
}
e.routes[expr] = result
if err := e.compile(); err != nil {
delete(e.routes, expr)
return err
}
return nil
}
func (e *router) UpsertRoute(expr string, val interface{}) error {
e.mutex.Lock()
defer e.mutex.Unlock()
result := &match{val: val}
if _, err := parse(expr, result); err != nil {
return err
}
prev, existed := e.routes[expr]
e.routes[expr] = result
if err := e.compile(); err != nil {
if existed {
e.routes[expr] = prev
} else {
delete(e.routes, expr)
}
return err
}
return nil
}
func (e *router) compile() error {
var exprs = []string{}
for expr, _ := range e.routes {
exprs = append(exprs, expr)
}
sort.Sort(sort.Reverse(sort.StringSlice(exprs)))
matchers := []matcher{}
i := 0
for _, expr := range exprs {
result := e.routes[expr]
matcher, err := parse(expr, result)
if err != nil {
return err
}
// Merge the previous and new matcher if that's possible
if i > 0 && matchers[i-1].canMerge(matcher) {
m, err := matchers[i-1].merge(matcher)
if err != nil {
return err
}
matchers[i-1] = m
} else {
matchers = append(matchers, matcher)
i += 1
}
}
e.matchers = matchers
return nil
}
func (e *router) RemoveRoute(expr string) error {
e.mutex.Lock()
defer e.mutex.Unlock()
delete(e.routes, expr)
return e.compile()
}
func (e *router) Route(req *http.Request) (interface{}, error) {
e.mutex.RLock()
defer e.mutex.RUnlock()
if len(e.matchers) == 0 {
return nil, nil
}
for _, m := range e.matchers {
if l := m.match(req); l != nil {
return l.val, nil
}
}
return nil, nil
}

476
vendor/github.com/vulcand/route/trie.go generated vendored Normal file
View file

@ -0,0 +1,476 @@
package route
import (
"bytes"
"fmt"
"net/http"
"regexp"
"strings"
"unicode"
)
// Regular expression to match url parameters
var reParam *regexp.Regexp
func init() {
reParam = regexp.MustCompile("^<([^>]+)>")
}
// Trie http://en.wikipedia.org/wiki/Trie for url matching with support of named parameters
type trie struct {
root *trieNode
// mapper takes the request and returns sequence that can be matched
mapper requestMapper
}
func (t *trie) canChain(o matcher) bool {
_, ok := o.(*trie)
return ok
}
func (t *trie) chain(o matcher) (matcher, error) {
to, ok := o.(*trie)
if !ok {
return nil, fmt.Errorf("can chain only with other trie")
}
m := t.root.findMatchNode()
m.matches = nil
m.children = []*trieNode{to.root}
t.root.setLevel(-1)
return &trie{
root: t.root,
mapper: newSeqMapper(t.mapper, to.mapper),
}, nil
}
func (t *trie) String() string {
return fmt.Sprintf("trieMatcher()")
}
// Takes the expression with url and the node that corresponds to this expression and returns parsed trie
func newTrieMatcher(expression string, mapper requestMapper, result *match) (*trie, error) {
t := &trie{
mapper: mapper,
}
t.root = &trieNode{trie: t}
if len(expression) == 0 {
return nil, fmt.Errorf("Empty URL expression")
}
err := t.root.parseExpression(-1, expression, result)
if err != nil {
return nil, err
}
return t, nil
}
func (t *trie) setMatch(result *match) {
t.root.setMatch(result)
}
// Tries can merge with other tries
func (t *trie) canMerge(m matcher) bool {
ot, ok := m.(*trie)
return ok && t.mapper.equivalent(ot.mapper) != nil
}
// Merge takes the other trie and modifies itself to match the passed trie as well.
// Note that trie passed as a parameter can be only simple trie without multiple branches per node, e.g. a->b->c->
// Trie on the left is "accumulating" trie that grows.
func (p *trie) merge(m matcher) (matcher, error) {
other, ok := m.(*trie)
if !ok {
return nil, fmt.Errorf("Can't merge %T and %T", p, m)
}
mapper := p.mapper.equivalent(other.mapper)
if mapper == nil {
return nil, fmt.Errorf("Can't merge %T and %T", p, m)
}
root, err := p.root.merge(other.root)
if err != nil {
return nil, err
}
return &trie{root: root, mapper: mapper}, nil
}
// Takes the request and returns the location if the request path matches any of it's paths
// returns nil if none of the requests matches
func (p *trie) match(r *http.Request) *match {
if p.root == nil {
return nil
}
return p.root.match(p.mapper.newIter(r))
}
type trieNode struct {
trie *trie
// Matching character, can be empty in case if it's a root node
// or node with a pattern matcher
char byte
// Optional children of this node, can be empty if it's a leaf node
children []*trieNode
// If present, means that this node is a pattern matcher
patternMatcher patternMatcher
// If present it means this node contains potential match for a request, and this is a leaf node.
matches []*match
// For chained tries matching different parts of the request levels would increase for next chained trie nodes
level int
}
func (e *trieNode) setMatch(m *match) {
n := e.findMatchNode()
n.matches = []*match{m}
}
func (e *trieNode) setLevel(level int) {
if e.isRoot() {
level++
}
e.level = level
if len(e.matches) != 0 {
return
}
// Check for the match in child nodes
for _, c := range e.children {
c.setLevel(level)
}
}
func (e *trieNode) findMatchNode() *trieNode {
if len(e.matches) != 0 {
return e
}
// Check for the match in child nodes
for _, c := range e.children {
if n := c.findMatchNode(); n != nil {
return n
}
}
return nil
}
func (e *trieNode) isMatching() bool {
return len(e.matches) != 0
}
func (e *trieNode) isRoot() bool {
return e.char == byte(0) && e.patternMatcher == nil
}
func (e *trieNode) isPatternMatcher() bool {
return e.patternMatcher != nil
}
func (e *trieNode) isCharMatcher() bool {
return e.char != 0
}
func (e *trieNode) String() string {
self := ""
if e.patternMatcher != nil {
self = e.patternMatcher.String()
} else {
self = fmt.Sprintf("%c", e.char)
}
if e.isMatching() {
return fmt.Sprintf("match(%d:%s)", e.level, self)
} else if e.isRoot() {
return fmt.Sprintf("root(%d)", e.level)
} else {
return fmt.Sprintf("node(%d:%s)", e.level, self)
}
}
func (e *trieNode) equals(o *trieNode) bool {
return (e.level == o.level) && // we can merge nodes that are on the same level to avoid merges for different subtrie parts
(e.char == o.char) && // chars are equal
(e.patternMatcher == nil && o.patternMatcher == nil) || // both nodes have no matchers
((e.patternMatcher != nil && o.patternMatcher != nil) && e.patternMatcher.equals(o.patternMatcher)) // both nodes have equal matchers
}
func (e *trieNode) merge(o *trieNode) (*trieNode, error) {
children := make([]*trieNode, 0, len(e.children))
merged := make(map[*trieNode]bool)
// First, find the nodes with similar keys and merge them
for _, c := range e.children {
for _, c2 := range o.children {
// The nodes are equivalent, so we can merge them
if c.equals(c2) {
m, err := c.merge(c2)
if err != nil {
return nil, err
}
merged[c] = true
merged[c2] = true
children = append(children, m)
}
}
}
// Next, append the keys that haven't been merged
for _, c := range e.children {
if !merged[c] {
children = append(children, c)
}
}
for _, c := range o.children {
if !merged[c] {
children = append(children, c)
}
}
return &trieNode{
level: e.level,
trie: e.trie,
char: e.char,
children: children,
patternMatcher: e.patternMatcher,
matches: append(e.matches, o.matches...),
}, nil
}
func (p *trieNode) parseExpression(offset int, pattern string, m *match) error {
// We are the last element, so we are the matching node
if offset >= len(pattern)-1 {
p.matches = []*match{m}
return nil
}
// There's a next character that exists
patternMatcher, newOffset, err := parsePatternMatcher(offset+1, pattern)
// We have found the matcher, but the syntax or parameters are wrong
if err != nil {
return err
}
// Matcher was found
if patternMatcher != nil {
node := &trieNode{patternMatcher: patternMatcher, trie: p.trie}
p.children = []*trieNode{node}
return node.parseExpression(newOffset-1, pattern, m)
} else {
// Matcher was not found, next node is just a character
node := &trieNode{char: pattern[offset+1], trie: p.trie}
p.children = []*trieNode{node}
return node.parseExpression(offset+1, pattern, m)
}
}
func parsePatternMatcher(offset int, pattern string) (patternMatcher, int, error) {
if pattern[offset] != '<' {
return nil, -1, nil
}
rest := pattern[offset:]
match := reParam.FindStringSubmatchIndex(rest)
if len(match) == 0 {
return nil, -1, nil
}
// Split parsed matcher parameters separated by :
values := strings.Split(rest[match[2]:match[3]], ":")
// The common syntax is <matcherType:matcherArg1:matcherArg2>
matcherType := values[0]
matcherArgs := values[1:]
// In case if there's only one <param> is implicitly converted to <string:param>
if len(values) == 1 {
matcherType = "string"
matcherArgs = values
}
matcher, err := makeMatcher(matcherType, matcherArgs)
if err != nil {
return nil, offset, err
}
return matcher, offset + match[1], nil
}
type matchResult struct {
matcher patternMatcher
value interface{}
}
type patternMatcher interface {
getName() string
match(i *charIter) bool
equals(other patternMatcher) bool
String() string
}
func makeMatcher(matcherType string, matcherArgs []string) (patternMatcher, error) {
switch matcherType {
case "string":
return newStringMatcher(matcherArgs)
case "int":
return newIntMatcher(matcherArgs)
}
return nil, fmt.Errorf("unsupported matcher: %s", matcherType)
}
func newStringMatcher(args []string) (patternMatcher, error) {
if len(args) != 1 {
return nil, fmt.Errorf("expected only one parameter - variable name, got: %s", args)
}
return &stringMatcher{name: args[0]}, nil
}
type stringMatcher struct {
name string
}
func (s *stringMatcher) String() string {
return fmt.Sprintf("<string:%s>", s.name)
}
func (s *stringMatcher) getName() string {
return s.name
}
func (s *stringMatcher) match(i *charIter) bool {
s.grabValue(i)
return true
}
func (s *stringMatcher) equals(other patternMatcher) bool {
_, ok := other.(*stringMatcher)
return ok && other.getName() == s.getName()
}
func (s *stringMatcher) grabValue(i *charIter) {
for {
c, sep, ok := i.next()
if !ok {
return
}
if c == sep {
i.pushBack()
return
}
}
}
func newIntMatcher(args []string) (patternMatcher, error) {
if len(args) != 1 {
return nil, fmt.Errorf("expected only one parameter - variable name, got: %s", args)
}
return &intMatcher{name: args[0]}, nil
}
type intMatcher struct {
name string
}
func (s *intMatcher) String() string {
return fmt.Sprintf("<int:%s>", s.name)
}
func (s *intMatcher) getName() string {
return s.name
}
func (s *intMatcher) match(iter *charIter) bool {
// count stores amount of consumed characters so we know how many push
// backs to do in case there is no match
var count int
for {
c, sep, ok := iter.next()
count++
// if the current character is not a number:
// - it's either a separator that means it's a match
// - it's some other character that means it's not a match
if !unicode.IsDigit(rune(c)) {
if c == sep {
iter.pushBack()
return true
} else {
for i := 0; i < count; i++ {
iter.pushBack()
}
return false
}
}
// if it's the end of the string, it's a match
if !ok {
return true
}
}
}
func (s *intMatcher) equals(other patternMatcher) bool {
_, ok := other.(*intMatcher)
return ok && other.getName() == s.getName()
}
func (e *trieNode) matchNode(i *charIter) bool {
if i.level() != e.level {
return false
}
if e.isRoot() {
return true
}
if e.isPatternMatcher() {
return e.patternMatcher.match(i)
}
c, _, ok := i.next()
if !ok {
// we have reached the end
return false
}
if c != e.char {
// no match, so don't consume the character
i.pushBack()
return false
}
return true
}
func (e *trieNode) match(i *charIter) *match {
if !e.matchNode(i) {
return nil
}
// This is a leaf node and we are at the last character of the pattern
if len(e.matches) != 0 && i.isEnd() {
return e.matches[0]
}
// Check for the match in child nodes
for _, c := range e.children {
p := i.position()
if match := c.match(i); match != nil {
return match
}
i.setPosition(p)
}
// Child nodes did not match and we at the boundary
if len(e.matches) != 0 && i.level() > e.level {
return e.matches[0]
}
return nil
}
// printTrie is useful for debugging and test purposes, it outputs the formatted
// represenation of the trie
func printTrie(t *trie) string {
return printTrieNode(t.root)
}
func printTrieNode(e *trieNode) string {
out := &bytes.Buffer{}
printTrieNodeInner(out, e, 0)
return out.String()
}
func printTrieNodeInner(b *bytes.Buffer, e *trieNode, offset int) {
if offset == 0 {
fmt.Fprintf(b, "\n")
}
padding := strings.Repeat(" ", offset)
fmt.Fprintf(b, "%s%s\n", padding, e.String())
if len(e.children) != 0 {
for _, c := range e.children {
printTrieNodeInner(b, c, offset+1)
}
}
}

33
vendor/github.com/vulcand/route/utils.go generated vendored Normal file
View file

@ -0,0 +1,33 @@
package route
import (
"net/http"
"strings"
)
// RawPath returns escaped url path section
func rawPath(r *http.Request) string {
// If there are no escape symbols, don't extract raw path
if !strings.ContainsRune(r.RequestURI, '%') {
if len(r.URL.Path) == 0 {
return "/"
}
return r.URL.Path
}
path := r.RequestURI
if path == "" {
path = "/"
}
// This is absolute URI, split host and port
if strings.Contains(path, "://") {
vals := strings.SplitN(path, r.URL.Host, 2)
if len(vals) == 2 {
path = vals[1]
}
}
idx := strings.IndexRune(path, '?')
if idx == -1 {
return path
}
return path[:idx]
}