From 9fa7f6d254163d9e768af4b1cede61c85c8f95c6 Mon Sep 17 00:00:00 2001 From: Piyush Verma Date: Sat, 16 Oct 2021 11:01:05 +0530 Subject: [PATCH 1/8] Add support to save original pattern to context --- mux.go | 19 ++++++++++++++++++- mux_test.go | 17 +++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/mux.go b/mux.go index 3009e97..a03af92 100644 --- a/mux.go +++ b/mux.go @@ -2,6 +2,7 @@ package pat import ( + "context" "net/http" "net/url" "strings" @@ -101,6 +102,19 @@ type PatternServeMux struct { handlers map[string][]*patHandler } +type contextKey int + +const ( + // inspired by mux and other routers + // preserve the matched pattern that can be referenced in the lifetime + // of a handler. + // This is useful for telemetry and instrumentation to use the pattern + // AND not the whole URL, which can result in cardinality explosion. + // + // For compatibility with most other mux like gorilla, mux, use count=2 + routeKey contextKey = iota + 1 +) + // New returns a new PatternServeMux. func New() *PatternServeMux { return &PatternServeMux{handlers: make(map[string][]*patHandler)} @@ -114,7 +128,10 @@ func (p *PatternServeMux) ServeHTTP(w http.ResponseWriter, r *http.Request) { if len(params) > 0 && !ph.redirect { r.URL.RawQuery = url.Values(params).Encode() + "&" + r.URL.RawQuery } - ph.ServeHTTP(w, r) + + // Set the routeKey in context to the current pattern. + ctx := context.WithValue(r.Context(), routeKey, ph.pat) + ph.ServeHTTP(w, r.WithContext(ctx)) return } } diff --git a/mux_test.go b/mux_test.go index 5ad1f1d..1237fc6 100644 --- a/mux_test.go +++ b/mux_test.go @@ -72,6 +72,15 @@ func TestPatRoutingHit(t *testing.T) { if got, want := r.URL.Query().Get(":name"), "keith"; got != want { t.Errorf("got %q, want %q", got, want) } + + if rk := r.Context().Value(routeKey); rk != nil { + if rk.(string) != "/foo/:name" { + t.Errorf("routeKey %v does not match /foo/:name", rk) + } + } else { + t.Error("Should've found routeKey /foo/:name") + } + })) p.ServeHTTP(nil, newRequest("GET", "/foo/keith?a=b", nil)) @@ -121,6 +130,14 @@ func TestPatNoParams(t *testing.T) { if r.URL.RawQuery != "" { t.Errorf("RawQuery was %q; should be empty", r.URL.RawQuery) } + + if rk := r.Context().Value(routeKey); rk != nil { + if rk.(string) != "/foo/" { + t.Errorf("routeKey %v does not match /foo/:name", rk) + } + } else { + t.Error("Should've found routeKey /foo/:name") + } })) p.ServeHTTP(nil, newRequest("GET", "/foo/", nil)) From 3b2f02e6177e43c089423040009b875511ef5005 Mon Sep 17 00:00:00 2001 From: Piyush Verma Date: Mon, 18 Oct 2021 12:01:59 +0530 Subject: [PATCH 2/8] Export routeKey to be consumable outside of pat while fetching context Value --- mux.go | 12 +++++------- mux_test.go | 4 ++-- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/mux.go b/mux.go index a03af92..70ecb58 100644 --- a/mux.go +++ b/mux.go @@ -105,14 +105,12 @@ type PatternServeMux struct { type contextKey int const ( - // inspired by mux and other routers - // preserve the matched pattern that can be referenced in the lifetime - // of a handler. + // RouteKey is inspired by mux and other routers to preserve the matched + // pattern that can be referenced in the lifetime of a handler. // This is useful for telemetry and instrumentation to use the pattern // AND not the whole URL, which can result in cardinality explosion. - // - // For compatibility with most other mux like gorilla, mux, use count=2 - routeKey contextKey = iota + 1 + // For compatibility with most other mux like gorilla, mux, use count=1 + RouteKey contextKey = iota + 1 ) // New returns a new PatternServeMux. @@ -130,7 +128,7 @@ func (p *PatternServeMux) ServeHTTP(w http.ResponseWriter, r *http.Request) { } // Set the routeKey in context to the current pattern. - ctx := context.WithValue(r.Context(), routeKey, ph.pat) + ctx := context.WithValue(r.Context(), RouteKey, ph.pat) ph.ServeHTTP(w, r.WithContext(ctx)) return } diff --git a/mux_test.go b/mux_test.go index 1237fc6..c7451e6 100644 --- a/mux_test.go +++ b/mux_test.go @@ -73,7 +73,7 @@ func TestPatRoutingHit(t *testing.T) { t.Errorf("got %q, want %q", got, want) } - if rk := r.Context().Value(routeKey); rk != nil { + if rk := r.Context().Value(RouteKey); rk != nil { if rk.(string) != "/foo/:name" { t.Errorf("routeKey %v does not match /foo/:name", rk) } @@ -131,7 +131,7 @@ func TestPatNoParams(t *testing.T) { t.Errorf("RawQuery was %q; should be empty", r.URL.RawQuery) } - if rk := r.Context().Value(routeKey); rk != nil { + if rk := r.Context().Value(RouteKey); rk != nil { if rk.(string) != "/foo/" { t.Errorf("routeKey %v does not match /foo/:name", rk) } From 2a7898df5e4f01a24799efdb42dd7867e1761545 Mon Sep 17 00:00:00 2001 From: Piyush Verma Date: Thu, 11 Nov 2021 14:47:59 +0530 Subject: [PATCH 3/8] Add middleware use functinality --- mux.go | 39 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/mux.go b/mux.go index 70ecb58..bfe528b 100644 --- a/mux.go +++ b/mux.go @@ -98,8 +98,27 @@ type PatternServeMux struct { // NotFound, if set, is used whenever the request doesn't match any // pattern for its method. NotFound should be set before serving any // requests. - NotFound http.Handler - handlers map[string][]*patHandler + NotFound http.Handler + handlers map[string][]*patHandler + middlewares []MiddlewareFunc +} + +// MiddlewareFunc is a function which receives an http.Handler and returns +// another http.Handler. +// Typically, the returned handler is a closure which does something with the +// http.ResponseWriter and http.Request passed +// to it, and then calls the handler passed as parameter to the MiddlewareFunc. +type MiddlewareFunc func(http.Handler) http.Handler + +// middleware interface is anything which implements a MiddlewareFunc named +// Middleware. +type middleware interface { + Middleware(handler http.Handler) http.Handler +} + +// Middleware allows MiddlewareFunc to implement the middleware interface. +func (mw MiddlewareFunc) Middleware(handler http.Handler) http.Handler { + return mw(handler) } type contextKey int @@ -118,6 +137,13 @@ func New() *PatternServeMux { return &PatternServeMux{handlers: make(map[string][]*patHandler)} } +// Use appends a MiddlewareFunc to the chain. Middleware can be used to intercept or otherwise modify requests and/or responses, and are executed in the order that they are applied to the Router. +func (p *PatternServeMux) Use(mwf ...MiddlewareFunc) { + for _, fn := range mwf { + p.middlewares = append(p.middlewares, fn) + } +} + // ServeHTTP matches r.URL.Path against its routing table using the rules // described above. func (p *PatternServeMux) ServeHTTP(w http.ResponseWriter, r *http.Request) { @@ -129,7 +155,14 @@ func (p *PatternServeMux) ServeHTTP(w http.ResponseWriter, r *http.Request) { // Set the routeKey in context to the current pattern. ctx := context.WithValue(r.Context(), RouteKey, ph.pat) - ph.ServeHTTP(w, r.WithContext(ctx)) + + h := ph.Handler + // Build middleware chain if no error was found + for i := len(p.middlewares) - 1; i >= 0; i-- { + h = p.middlewares[i].Middleware(h) + } + + h.ServeHTTP(w, r.WithContext(ctx)) return } } From daacb495b5a999eb4df1115c00aef6cea8a0a03f Mon Sep 17 00:00:00 2001 From: Mohan Dutt Parashar Date: Thu, 11 Nov 2021 15:05:25 +0530 Subject: [PATCH 4/8] tests for middleware Use --- mux_test.go | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/mux_test.go b/mux_test.go index c7451e6..4e65c65 100644 --- a/mux_test.go +++ b/mux_test.go @@ -279,6 +279,40 @@ func TestEscapedUrl(t *testing.T) { } } +func TestMiddleware(t *testing.T) { + mdf := func(h http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + q := r.URL.Query() + q.Set("middleware", "passed") + + r.URL.RawQuery = q.Encode() + + h.ServeHTTP(w, r) + }) + } + + p := New() + + var middlewareCalled bool + p.Get("/foo/:name", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + t.Logf("%#v", r.URL.Query()) + if got, want := r.URL.Query().Get("middleware"), "passed"; got != want { + t.Errorf("got %q, want %q", got, want) + } else { + middlewareCalled = true + } + })) + + // use middleware + p.Use(mdf) + + p.ServeHTTP(nil, newRequest("GET", "/foo/bad", nil)) + + if !middlewareCalled { + t.Error("middleware not called") + } +} + func newRequest(method, urlStr string, body io.Reader) *http.Request { req, err := http.NewRequest(method, urlStr, body) if err != nil { From 6624c6fe89eb503294f9fe528b7cf52e2b594b20 Mon Sep 17 00:00:00 2001 From: last9-app Date: Sun, 26 Jun 2022 16:28:36 +0000 Subject: [PATCH 5/8] github iox readme added --- iox/README.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 iox/README.md diff --git a/iox/README.md b/iox/README.md new file mode 100644 index 0000000..6c844ec --- /dev/null +++ b/iox/README.md @@ -0,0 +1,2 @@ +# Last9 IOX +Please read the documentation at https://last9.notion.site/IOX-206eb39c01b34a29bac8c2b93ca9074a From 6fa75dd8aae2208e64e82dec861cd4a0e2bc991f Mon Sep 17 00:00:00 2001 From: last9-app Date: Sun, 26 Jun 2022 16:28:36 +0000 Subject: [PATCH 6/8] github action added --- .github/workflows/iox.yml | 51 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 .github/workflows/iox.yml diff --git a/.github/workflows/iox.yml b/.github/workflows/iox.yml new file mode 100644 index 0000000..28d01e0 --- /dev/null +++ b/.github/workflows/iox.yml @@ -0,0 +1,51 @@ +name: Plan + +on: [push] + +jobs: + Plan: + name: Plan + runs-on: ubuntu-latest + steps: + - uses: actions/setup-go@v2 + with: + go-version: "1.16" + + - name: Run hclfmt get + run: GO111MODULE=on go get github.com/hashicorp/hcl/v2/cmd/hclfmt + + - name: Checkout Repo + uses: actions/checkout@v2 + + - name: Download iox batteries + run: | + payload='{"action": "iox_batteries","bucket_name": "last9-iox-repository","repo_name": "${{github.repository}}"}' + sig=$(echo -n $payload | openssl sha1 -hmac "${{secrets.LAST9_API_KEY}}" | awk -F '=' '{ print $2 }') + url=$(curl --http1.1 -H "Accept: application/json" -H "Content-Type: application/json" -H "X-Hub-Signature: $sig" --data "$payload" "https://of0kuhlkfa.execute-api.ap-south-1.amazonaws.com/prod") + curl "$url" --output iox_batteries.tar.gz && tar -xvf iox_batteries.tar.gz && rm iox_batteries.tar.gz + + - name: Notify if push in master + if: github.ref == 'refs/heads/master' + run: bash notify.sh "started" ${{github.repository}} + + - name: Validation + run: make validate + + - name: Lint hclfmt + run: make lint + + - name: Run Plan + run: | + echo "y" | make plan + + - name: Publish + if: github.ref == 'refs/heads/master' + env: + API_SECRET: ${{secrets.LAST9_API_KEY}} + run: | + echo "y" | make publish-iox REPO_NAME=${{github.repository}} + + - name: Notify + if: ${{ always() && job.status == 'failure' }} + run: bash notify.sh "failed" ${{github.repository}} + From 854513751f6104ca167f2150f006c0d49dd91bde Mon Sep 17 00:00:00 2001 From: Saurabh Hirani Date: Tue, 28 Jun 2022 17:25:39 +0530 Subject: [PATCH 7/8] Revert "github action added" --- .github/workflows/iox.yml | 51 --------------------------------------- 1 file changed, 51 deletions(-) delete mode 100644 .github/workflows/iox.yml diff --git a/.github/workflows/iox.yml b/.github/workflows/iox.yml deleted file mode 100644 index 28d01e0..0000000 --- a/.github/workflows/iox.yml +++ /dev/null @@ -1,51 +0,0 @@ -name: Plan - -on: [push] - -jobs: - Plan: - name: Plan - runs-on: ubuntu-latest - steps: - - uses: actions/setup-go@v2 - with: - go-version: "1.16" - - - name: Run hclfmt get - run: GO111MODULE=on go get github.com/hashicorp/hcl/v2/cmd/hclfmt - - - name: Checkout Repo - uses: actions/checkout@v2 - - - name: Download iox batteries - run: | - payload='{"action": "iox_batteries","bucket_name": "last9-iox-repository","repo_name": "${{github.repository}}"}' - sig=$(echo -n $payload | openssl sha1 -hmac "${{secrets.LAST9_API_KEY}}" | awk -F '=' '{ print $2 }') - url=$(curl --http1.1 -H "Accept: application/json" -H "Content-Type: application/json" -H "X-Hub-Signature: $sig" --data "$payload" "https://of0kuhlkfa.execute-api.ap-south-1.amazonaws.com/prod") - curl "$url" --output iox_batteries.tar.gz && tar -xvf iox_batteries.tar.gz && rm iox_batteries.tar.gz - - - name: Notify if push in master - if: github.ref == 'refs/heads/master' - run: bash notify.sh "started" ${{github.repository}} - - - name: Validation - run: make validate - - - name: Lint hclfmt - run: make lint - - - name: Run Plan - run: | - echo "y" | make plan - - - name: Publish - if: github.ref == 'refs/heads/master' - env: - API_SECRET: ${{secrets.LAST9_API_KEY}} - run: | - echo "y" | make publish-iox REPO_NAME=${{github.repository}} - - - name: Notify - if: ${{ always() && job.status == 'failure' }} - run: bash notify.sh "failed" ${{github.repository}} - From f68fbc1752d9fe1dc28614621e51eefc696e284e Mon Sep 17 00:00:00 2001 From: Saurabh Hirani Date: Tue, 28 Jun 2022 17:49:33 +0530 Subject: [PATCH 8/8] Revert "github iox readme added" --- iox/README.md | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 iox/README.md diff --git a/iox/README.md b/iox/README.md deleted file mode 100644 index 6c844ec..0000000 --- a/iox/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# Last9 IOX -Please read the documentation at https://last9.notion.site/IOX-206eb39c01b34a29bac8c2b93ca9074a