util/http: add trie case insesitive matching
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
This commit is contained in:
parent
15c020fac5
commit
e3fee6f8a6
@ -6,6 +6,21 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TrieOptions contains search options
|
||||||
|
type TrieOptions struct {
|
||||||
|
IgnoreCase bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// TrieOption func signature
|
||||||
|
type TrieOption func(*TrieOptions)
|
||||||
|
|
||||||
|
// IgnoreCase says that search must be case insensitive
|
||||||
|
func IgnoreCase(b bool) TrieOption {
|
||||||
|
return func(o *TrieOptions) {
|
||||||
|
o.IgnoreCase = b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Tree is a trie tree.
|
// Tree is a trie tree.
|
||||||
type Trie struct {
|
type Trie struct {
|
||||||
node *node
|
node *node
|
||||||
@ -79,19 +94,35 @@ func (t *Trie) Insert(methods []string, path string, handler interface{}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Search searches a path from a tree.
|
// Search searches a path from a tree.
|
||||||
func (t *Trie) Search(method string, path string) (interface{}, map[string]string, bool) {
|
func (t *Trie) Search(method string, path string, opts ...TrieOption) (interface{}, map[string]string, bool) {
|
||||||
params := make(map[string]string)
|
params := make(map[string]string)
|
||||||
|
|
||||||
|
options := TrieOptions{}
|
||||||
|
for _, o := range opts {
|
||||||
|
o(&options)
|
||||||
|
}
|
||||||
|
|
||||||
curNode := t.node
|
curNode := t.node
|
||||||
|
|
||||||
|
nodeLoop:
|
||||||
for _, p := range splitPath(path) {
|
for _, p := range splitPath(path) {
|
||||||
nextNode, ok := curNode.children[p]
|
nextNode, ok := curNode.children[p]
|
||||||
if ok {
|
if ok {
|
||||||
curNode = nextNode
|
curNode = nextNode
|
||||||
continue
|
continue nodeLoop
|
||||||
|
}
|
||||||
|
if options.IgnoreCase {
|
||||||
|
// additional loop for case insensitive matching
|
||||||
|
for k, v := range curNode.children {
|
||||||
|
if literalEqual(k, p, true) {
|
||||||
|
curNode = v
|
||||||
|
continue nodeLoop
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if len(curNode.children) == 0 {
|
if len(curNode.children) == 0 {
|
||||||
if curNode.label != p {
|
if !literalEqual(curNode.label, p, options.IgnoreCase) {
|
||||||
// no matching path was found.
|
// no matching path was found
|
||||||
return nil, nil, false
|
return nil, nil, false
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
@ -199,3 +230,10 @@ func splitPath(path string) []string {
|
|||||||
}
|
}
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func literalEqual(component, literal string, ignoreCase bool) bool {
|
||||||
|
if ignoreCase {
|
||||||
|
return strings.EqualFold(component, literal)
|
||||||
|
}
|
||||||
|
return component == literal
|
||||||
|
}
|
||||||
|
@ -5,6 +5,19 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestTrieIgnoreCase(t *testing.T) {
|
||||||
|
type handler struct {
|
||||||
|
name string
|
||||||
|
}
|
||||||
|
tr := NewTrie()
|
||||||
|
tr.Insert([]string{http.MethodPut}, "/v1/create/{id}", &handler{name: "test"})
|
||||||
|
|
||||||
|
_, _, ok := tr.Search(http.MethodPut, "/v1/CREATE/12", IgnoreCase(true))
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("unexpected error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestTrieContentType(t *testing.T) {
|
func TestTrieContentType(t *testing.T) {
|
||||||
type handler struct {
|
type handler struct {
|
||||||
name string
|
name string
|
||||||
|
Loading…
Reference in New Issue
Block a user