From e30a452cdb02e217f38d762273d0c3943391fc2d Mon Sep 17 00:00:00 2001 From: Denis Kirillov Date: Wed, 5 Jul 2023 17:02:36 +0300 Subject: [PATCH] [#149] Use chi instead of gorilla mux Signed-off-by: Denis Kirillov --- api/router_filter.go | 133 +++++++++++++++++++++++++++++++++++++++++++ go.mod | 2 +- go.sum | 4 +- 3 files changed, 136 insertions(+), 3 deletions(-) create mode 100644 api/router_filter.go diff --git a/api/router_filter.go b/api/router_filter.go new file mode 100644 index 0000000..e7e6f47 --- /dev/null +++ b/api/router_filter.go @@ -0,0 +1,133 @@ +package api + +import ( + "fmt" + "net/http" +) + +type HandlerFilters struct { + filters []Filter + defaultHandler http.Handler +} + +type Filter struct { + queries []Pair + headers []Pair + h http.Handler +} + +type Pair struct { + Key string + Value string +} + +func NewHandlerFilter() *HandlerFilters { + return &HandlerFilters{} +} + +func NewFilter() *Filter { + return &Filter{} +} + +func (hf *HandlerFilters) Add(filter *Filter) *HandlerFilters { + hf.filters = append(hf.filters, *filter) + return hf +} + +// HeadersMatch adds a matcher for header values. +// It accepts a sequence of key/value pairs. Values may define variables. +// Panics if number of parameters is not even. +// Supports only exact matching. +// If the value is an empty string, it will match any value if the key is set. +func (f *Filter) HeadersMatch(pairs ...string) *Filter { + length := len(pairs) + if length%2 != 0 { + panic(fmt.Errorf("filter headers: number of parameters must be multiple of 2, got %v", pairs)) + } + + for i := 0; i < length; i += 2 { + f.headers = append(f.headers, Pair{ + Key: pairs[i], + Value: pairs[i+1], + }) + } + + return f +} + +// Headers is similar to HeadersMatch but accept only header keys, set value to empty string internally. +func (f *Filter) Headers(headers ...string) *Filter { + for _, header := range headers { + f.headers = append(f.headers, Pair{ + Key: header, + Value: "", + }) + } + + return f +} + +func (f *Filter) Handler(handler http.HandlerFunc) *Filter { + f.h = handler + return f +} + +// QueriesMatch adds a matcher for URL query values. +// It accepts a sequence of key/value pairs. Values may define variables. +// Panics if number of parameters is not even. +// Supports only exact matching. +// If the value is an empty string, it will match any value if the key is set. +func (f *Filter) QueriesMatch(pairs ...string) *Filter { + length := len(pairs) + if length%2 != 0 { + panic(fmt.Errorf("filter headers: number of parameters must be multiple of 2, got %v", pairs)) + } + + for i := 0; i < length; i += 2 { + f.queries = append(f.queries, Pair{ + Key: pairs[i], + Value: pairs[i+1], + }) + } + + return f +} + +// Queries is similar to QueriesMatch but accept only query keys, set value to empty string internally. +func (f *Filter) Queries(queries ...string) *Filter { + for _, query := range queries { + f.queries = append(f.queries, Pair{ + Key: query, + Value: "", + }) + } + + return f +} + +func (hf *HandlerFilters) DefaultHandler(handler http.HandlerFunc) *HandlerFilters { + hf.defaultHandler = handler + return hf +} + +func (hf *HandlerFilters) ServeHTTP(w http.ResponseWriter, r *http.Request) { +LOOP: + for _, filter := range hf.filters { + for _, header := range filter.headers { + hdrVals := r.Header.Values(header.Key) + if len(hdrVals) == 0 || header.Value != "" && header.Value != hdrVals[0] { + continue LOOP + } + } + for _, query := range filter.queries { + queryVal := r.URL.Query().Get(query.Key) + if !r.URL.Query().Has(query.Key) || queryVal != "" && query.Value != queryVal { + continue LOOP + } + } + filter.h.ServeHTTP(w, r) + return + } + + hf.defaultHandler.ServeHTTP(w, r) +} diff --git a/go.mod b/go.mod index 7720b88..208e961 100644 --- a/go.mod +++ b/go.mod @@ -8,8 +8,8 @@ require ( git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20230707115716-fe35373d8f1b github.com/aws/aws-sdk-go v1.44.6 github.com/bluele/gcache v0.0.2 + github.com/go-chi/chi/v5 v5.0.8 github.com/google/uuid v1.3.0 - github.com/gorilla/mux v1.8.0 github.com/minio/sio v0.3.0 github.com/nats-io/nats.go v1.13.1-0.20220121202836-972a071d373d github.com/nspcc-dev/neo-go v0.101.1 diff --git a/go.sum b/go.sum index 42d29e8..450e55b 100644 --- a/go.sum +++ b/go.sum @@ -149,6 +149,8 @@ github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4 github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-chi/chi/v5 v5.0.8 h1:lD+NLqFcAi1ovnVZpsnObHGW4xb4J8lNmoYVfECH1Y0= +github.com/go-chi/chi/v5 v5.0.8/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -243,8 +245,6 @@ github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= -github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= -github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=