From e3fee6f8a63865a3625da47dfae9a0ba5424995a Mon Sep 17 00:00:00 2001 From: Vasiliy Tolstov Date: Wed, 29 Sep 2021 13:41:47 +0300 Subject: [PATCH] util/http: add trie case insesitive matching Signed-off-by: Vasiliy Tolstov --- util/http/trie.go | 46 ++++++++++++++++++++++++++++++++++++++---- util/http/trie_test.go | 13 ++++++++++++ 2 files changed, 55 insertions(+), 4 deletions(-) diff --git a/util/http/trie.go b/util/http/trie.go index d5523bca..4a431270 100644 --- a/util/http/trie.go +++ b/util/http/trie.go @@ -6,6 +6,21 @@ import ( "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. type Trie struct { node *node @@ -79,19 +94,35 @@ func (t *Trie) Insert(methods []string, path string, handler interface{}) { } // 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) + options := TrieOptions{} + for _, o := range opts { + o(&options) + } + curNode := t.node + +nodeLoop: for _, p := range splitPath(path) { nextNode, ok := curNode.children[p] if ok { 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 curNode.label != p { - // no matching path was found. + if !literalEqual(curNode.label, p, options.IgnoreCase) { + // no matching path was found return nil, nil, false } break @@ -199,3 +230,10 @@ func splitPath(path string) []string { } return r } + +func literalEqual(component, literal string, ignoreCase bool) bool { + if ignoreCase { + return strings.EqualFold(component, literal) + } + return component == literal +} diff --git a/util/http/trie_test.go b/util/http/trie_test.go index be48fd78..8e51df76 100644 --- a/util/http/trie_test.go +++ b/util/http/trie_test.go @@ -5,6 +5,19 @@ import ( "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) { type handler struct { name string