From d07530f46e1eec4e40346e24af34dcc6750ad39f Mon Sep 17 00:00:00 2001 From: Matt Silverlock Date: Sat, 12 Sep 2020 12:20:56 -0700 Subject: [PATCH 01/25] build: CircleCI 2.1 + build matrix (#595) * build: CircleCI 2.1 + build matrix * build: drop Go 1.9 & Go 1.10 * build: remove erroneous version --- .circleci/config.yml | 103 ++++++++++++++++++------------------------- 1 file changed, 43 insertions(+), 60 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 536bc119..ead3e1d4 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,87 +1,70 @@ -version: 2.0 +version: 2.1 jobs: - # Base test configuration for Go library tests Each distinct version should - # inherit this base, and override (at least) the container image used. - "test": &test + "test": + parameters: + version: + type: string + default: "latest" + golint: + type: boolean + default: true + modules: + type: boolean + default: true + goproxy: + type: string + default: "" docker: - - image: circleci/golang:latest + - image: "circleci/golang:<< parameters.version >>" working_directory: /go/src/github.com/gorilla/mux - steps: &steps - # Our build steps: we checkout the repo, fetch our deps, lint, and finally - # run "go test" on the package. + environment: + GO111MODULE: "on" + GOPROXY: "<< parameters.goproxy >>" + steps: - checkout - # Logs the version in our build logs, for posterity - - run: go version + - run: + name: "Print the Go version" + command: > + go version - run: name: "Fetch dependencies" command: > - go get -t -v ./... + if [[ << parameters.modules >> = true ]]; then + go mod download + export GO111MODULE=on + else + go get -v ./... + fi # Only run gofmt, vet & lint against the latest Go version - run: name: "Run golint" command: > - if [ "${LATEST}" = true ] && [ -z "${SKIP_GOLINT}" ]; then + if [ << parameters.version >> = "latest" ] && [ << parameters.golint >> = true ]; then go get -u golang.org/x/lint/golint golint ./... fi - run: name: "Run gofmt" command: > - if [[ "${LATEST}" = true ]]; then + if [[ << parameters.version >> = "latest" ]]; then diff -u <(echo -n) <(gofmt -d -e .) fi - run: name: "Run go vet" - command: > - if [[ "${LATEST}" = true ]]; then + command: > + if [[ << parameters.version >> = "latest" ]]; then go vet -v ./... fi - - run: go test -v -race ./... - - "latest": - <<: *test - environment: - LATEST: true - - "1.12": - <<: *test - docker: - - image: circleci/golang:1.12 - - "1.11": - <<: *test - docker: - - image: circleci/golang:1.11 - - "1.10": - <<: *test - docker: - - image: circleci/golang:1.10 - - "1.9": - <<: *test - docker: - - image: circleci/golang:1.9 - - "1.8": - <<: *test - docker: - - image: circleci/golang:1.8 - - "1.7": - <<: *test - docker: - - image: circleci/golang:1.7 + - run: + name: "Run go test (+ race detector)" + command: > + go test -v -race ./... workflows: - version: 2 - build: + tests: jobs: - - "latest" - - "1.12" - - "1.11" - - "1.10" - - "1.9" - - "1.8" - - "1.7" + - test: + matrix: + parameters: + version: ["latest", "1.15", "1.14", "1.13", "1.12", "1.11"] From 3cf0d013e53d62a96c096366d300c84489c26dd5 Mon Sep 17 00:00:00 2001 From: Jille Timmermans Date: Tue, 14 Sep 2021 14:12:19 +0200 Subject: [PATCH 02/25] Include "404" and "405" in the docs (#602) --- mux.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mux.go b/mux.go index 782a34b2..f126a602 100644 --- a/mux.go +++ b/mux.go @@ -46,9 +46,11 @@ func NewRouter() *Router { // This will send all incoming requests to the router. type Router struct { // Configurable Handler to be used when no route matches. + // This can be used to render your own 404 Not Found errors. NotFoundHandler http.Handler // Configurable Handler to be used when the request method does not match the route. + // This can be used to render your own 405 Method Not Allowed errors. MethodNotAllowedHandler http.Handler // Routes to be matched, in order. From 91708ff8e35bafc8612f690a25f5dd0be6f16864 Mon Sep 17 00:00:00 2001 From: Matt Silverlock Date: Sun, 12 Dec 2021 08:29:04 -0800 Subject: [PATCH 03/25] docs: update README w.r.t new maintainer ask (#660) * Update README.md * build: gofmt on latest --- README.md | 6 +++++- mux_httpserver_test.go | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 35eea9f1..c7e6872b 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,11 @@ ![Gorilla Logo](https://cloud-cdn.questionable.services/gorilla-icon-64.png) -https://www.gorillatoolkit.org/pkg/mux +--- + +⚠️ **[The Gorilla Toolkit is looking for a new maintainer](https://github.com/gorilla/mux/issues/659)** + +--- Package `gorilla/mux` implements a request router and dispatcher for matching incoming requests to their respective handler. diff --git a/mux_httpserver_test.go b/mux_httpserver_test.go index 5d2f4d3a..907ab91d 100644 --- a/mux_httpserver_test.go +++ b/mux_httpserver_test.go @@ -1,3 +1,4 @@ +//go:build go1.9 // +build go1.9 package mux From c889844abd3601217c96aabc4b2dd89f6d904c01 Mon Sep 17 00:00:00 2001 From: Michael Grigoryan <56165400+michaelgrigoryan25@users.noreply.github.com> Date: Sun, 26 Jun 2022 15:46:01 +0400 Subject: [PATCH 04/25] regexp: use iota instead of hardcoded values for regexType* (#679) * regexp: use iota instead of hardcoded values --- regexp.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/regexp.go b/regexp.go index 0144842b..37c11edc 100644 --- a/regexp.go +++ b/regexp.go @@ -22,10 +22,10 @@ type routeRegexpOptions struct { type regexpType int const ( - regexpTypePath regexpType = 0 - regexpTypeHost regexpType = 1 - regexpTypePrefix regexpType = 2 - regexpTypeQuery regexpType = 3 + regexpTypePath regexpType = iota + regexpTypeHost + regexpTypePrefix + regexpTypeQuery ) // newRouteRegexp parses a route template and returns a routeRegexp, From 07eedffb4388b4ed26b86c67aedca1e513e7553b Mon Sep 17 00:00:00 2001 From: Mustaque Ahmed Date: Thu, 18 Aug 2022 02:19:02 +0530 Subject: [PATCH 05/25] [docs] `authenticationMiddleware` initialization in the `README.md` file (#693) * initial commit * fix `README.md` - authenticationMiddleware initialization --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c7e6872b..f09a5b35 100644 --- a/README.md +++ b/README.md @@ -576,7 +576,7 @@ func (amw *authenticationMiddleware) Middleware(next http.Handler) http.Handler r := mux.NewRouter() r.HandleFunc("/", handler) -amw := authenticationMiddleware{} +amw := authenticationMiddleware{tokenUsers: make(map[string]string)} amw.Populate() r.Use(amw.Middleware) From 5e1e8c8d45ad101414d06762b0f7f6200babc929 Mon Sep 17 00:00:00 2001 From: Matt Silverlock Date: Fri, 9 Dec 2022 10:56:37 -0500 Subject: [PATCH 06/25] archive mode --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f09a5b35..35aa29b6 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ --- -⚠️ **[The Gorilla Toolkit is looking for a new maintainer](https://github.com/gorilla/mux/issues/659)** +The Gorilla project has been archived, and is no longer under active maintainenance. You can read more here: https://github.com/gorilla#gorilla-toolkit --- From eb99d7a67714bbab6db27f896a8c8c947b51b610 Mon Sep 17 00:00:00 2001 From: Matt Silverlock Date: Fri, 9 Dec 2022 10:56:57 -0500 Subject: [PATCH 07/25] archive mode --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 35aa29b6..64a20e7d 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ --- -The Gorilla project has been archived, and is no longer under active maintainenance. You can read more here: https://github.com/gorilla#gorilla-toolkit +**The Gorilla project has been archived, and is no longer under active maintainenance. You can read more here: https://github.com/gorilla#gorilla-toolkit** --- From 7456b4e9ae7b67e27ffaa2e497941e77d39bc884 Mon Sep 17 00:00:00 2001 From: Corey Daley Date: Sat, 15 Jul 2023 10:48:51 -0400 Subject: [PATCH 08/25] Update README.md Signed-off-by: Corey Daley --- README.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/README.md b/README.md index 64a20e7d..f836a4e7 100644 --- a/README.md +++ b/README.md @@ -6,12 +6,6 @@ ![Gorilla Logo](https://cloud-cdn.questionable.services/gorilla-icon-64.png) ---- - -**The Gorilla project has been archived, and is no longer under active maintainenance. You can read more here: https://github.com/gorilla#gorilla-toolkit** - ---- - Package `gorilla/mux` implements a request router and dispatcher for matching incoming requests to their respective handler. From 1fa2ee87bf61940bfac616e98513448af97a1813 Mon Sep 17 00:00:00 2001 From: Apoorva Jagtap <35304110+apoorvajagtap@users.noreply.github.com> Date: Mon, 24 Jul 2023 01:45:04 +0530 Subject: [PATCH 09/25] [GPT-95] Update go version, add tools for verification and testing (#718) Fixes # https://gorilla-web-toolkit.atlassian.net/browse/GPT-95 **Summary of Changes** Added `.github/workflows/test.yml` that runs golangci-lint & go tests on any PR created or each push to main branch. --------- Signed-off-by: Corey Daley Signed-off-by: Apoorva Jagtap <35304110+apoorvajagtap@users.noreply.github.com> Co-authored-by: Corey Daley --- .circleci/config.yml | 70 -------------------------- .editorconfig | 20 ++++++++ .github/workflows/issues.yml | 20 ++++++++ .github/workflows/test.yml | 55 +++++++++++++++++++++ .gitignore | 1 + Makefile | 34 +++++++++++++ README.md | 8 +-- go.mod | 2 +- middleware_test.go | 95 ++++++++++++++++++++++++++++-------- mux_httpserver_test.go | 14 ++++-- mux_test.go | 20 ++++++-- regexp.go | 2 +- regexp_test.go | 4 +- route.go | 80 +++++++++++++++--------------- 14 files changed, 280 insertions(+), 145 deletions(-) delete mode 100644 .circleci/config.yml create mode 100644 .editorconfig create mode 100644 .github/workflows/issues.yml create mode 100644 .github/workflows/test.yml create mode 100644 .gitignore create mode 100644 Makefile diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index ead3e1d4..00000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,70 +0,0 @@ -version: 2.1 - -jobs: - "test": - parameters: - version: - type: string - default: "latest" - golint: - type: boolean - default: true - modules: - type: boolean - default: true - goproxy: - type: string - default: "" - docker: - - image: "circleci/golang:<< parameters.version >>" - working_directory: /go/src/github.com/gorilla/mux - environment: - GO111MODULE: "on" - GOPROXY: "<< parameters.goproxy >>" - steps: - - checkout - - run: - name: "Print the Go version" - command: > - go version - - run: - name: "Fetch dependencies" - command: > - if [[ << parameters.modules >> = true ]]; then - go mod download - export GO111MODULE=on - else - go get -v ./... - fi - # Only run gofmt, vet & lint against the latest Go version - - run: - name: "Run golint" - command: > - if [ << parameters.version >> = "latest" ] && [ << parameters.golint >> = true ]; then - go get -u golang.org/x/lint/golint - golint ./... - fi - - run: - name: "Run gofmt" - command: > - if [[ << parameters.version >> = "latest" ]]; then - diff -u <(echo -n) <(gofmt -d -e .) - fi - - run: - name: "Run go vet" - command: > - if [[ << parameters.version >> = "latest" ]]; then - go vet -v ./... - fi - - run: - name: "Run go test (+ race detector)" - command: > - go test -v -race ./... - -workflows: - tests: - jobs: - - test: - matrix: - parameters: - version: ["latest", "1.15", "1.14", "1.13", "1.12", "1.11"] diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..c6b74c3e --- /dev/null +++ b/.editorconfig @@ -0,0 +1,20 @@ +; https://editorconfig.org/ + +root = true + +[*] +insert_final_newline = true +charset = utf-8 +trim_trailing_whitespace = true +indent_style = space +indent_size = 2 + +[{Makefile,go.mod,go.sum,*.go,.gitmodules}] +indent_style = tab +indent_size = 4 + +[*.md] +indent_size = 4 +trim_trailing_whitespace = false + +eclint_indent_style = unset \ No newline at end of file diff --git a/.github/workflows/issues.yml b/.github/workflows/issues.yml new file mode 100644 index 00000000..055ca822 --- /dev/null +++ b/.github/workflows/issues.yml @@ -0,0 +1,20 @@ +# Add issues or pull-requests created to the project. +name: Add issue or pull request to Project + +on: + issues: + types: + - opened + pull_request: + types: + - opened + +jobs: + add-to-project: + runs-on: ubuntu-latest + steps: + - name: Add issue to project + uses: actions/add-to-project@v0.5.0 + with: + project-url: https://github.com/orgs/gorilla/projects/4 + github-token: ${{ secrets.ADD_TO_PROJECT_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 00000000..af48d228 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,55 @@ +name: CI +on: + push: + branches: + - main + pull_request: + branches: + - main + +permissions: + contents: read + +jobs: + verify-and-test: + strategy: + matrix: + go: ['1.19','1.20'] + os: [ubuntu-latest, macos-latest, windows-latest] + fail-fast: true + runs-on: ${{ matrix.os }} + steps: + - name: Checkout Code + uses: actions/checkout@v3 + + - name: Setup Go ${{ matrix.go }} + uses: actions/setup-go@v4 + with: + go-version: ${{ matrix.go }} + cache: false + + - name: Run GolangCI-Lint + uses: golangci/golangci-lint-action@v3 + with: + version: v1.53 + args: --timeout=5m + + - name: Run GoSec + if: matrix.os == 'ubuntu-latest' + uses: securego/gosec@master + with: + args: ./... + + - name: Run GoVulnCheck + uses: golang/govulncheck-action@v1 + with: + go-version-input: ${{ matrix.go }} + go-package: ./... + + - name: Run Tests + run: go test -race -cover -coverprofile=coverage -covermode=atomic -v ./... + + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v3 + with: + files: ./coverage \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..84039fec --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +coverage.coverprofile diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..98f5ab75 --- /dev/null +++ b/Makefile @@ -0,0 +1,34 @@ +GO_LINT=$(shell which golangci-lint 2> /dev/null || echo '') +GO_LINT_URI=github.com/golangci/golangci-lint/cmd/golangci-lint@latest + +GO_SEC=$(shell which gosec 2> /dev/null || echo '') +GO_SEC_URI=github.com/securego/gosec/v2/cmd/gosec@latest + +GO_VULNCHECK=$(shell which govulncheck 2> /dev/null || echo '') +GO_VULNCHECK_URI=golang.org/x/vuln/cmd/govulncheck@latest + +.PHONY: golangci-lint +golangci-lint: + $(if $(GO_LINT), ,go install $(GO_LINT_URI)) + @echo "##### Running golangci-lint" + golangci-lint run -v + +.PHONY: gosec +gosec: + $(if $(GO_SEC), ,go install $(GO_SEC_URI)) + @echo "##### Running gosec" + gosec ./... + +.PHONY: govulncheck +govulncheck: + $(if $(GO_VULNCHECK), ,go install $(GO_VULNCHECK_URI)) + @echo "##### Running govulncheck" + govulncheck ./... + +.PHONY: verify +verify: golangci-lint gosec govulncheck + +.PHONY: test +test: + @echo "##### Running tests" + go test -race -cover -coverprofile=coverage.coverprofile -covermode=atomic -v ./... \ No newline at end of file diff --git a/README.md b/README.md index f836a4e7..551f3c67 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,10 @@ # gorilla/mux -[![GoDoc](https://godoc.org/github.com/gorilla/mux?status.svg)](https://godoc.org/github.com/gorilla/mux) -[![CircleCI](https://circleci.com/gh/gorilla/mux.svg?style=svg)](https://circleci.com/gh/gorilla/mux) -[![Sourcegraph](https://sourcegraph.com/github.com/gorilla/mux/-/badge.svg)](https://sourcegraph.com/github.com/gorilla/mux?badge) +![testing](https://github.com/gorilla/mux/actions/workflows/test.yml/badge.svg) +[![codecov](https://codecov.io/github/gorilla/mux/branch/main/graph/badge.svg)](https://codecov.io/github/gorilla/mux) +[![godoc](https://godoc.org/github.com/gorilla/mux?status.svg)](https://godoc.org/github.com/gorilla/mux) +[![sourcegraph](https://sourcegraph.com/github.com/gorilla/mux/-/badge.svg)](https://sourcegraph.com/github.com/gorilla/mux?badge) + ![Gorilla Logo](https://cloud-cdn.questionable.services/gorilla-icon-64.png) diff --git a/go.mod b/go.mod index df170a39..7bcfa026 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,3 @@ module github.com/gorilla/mux -go 1.12 +go 1.19 diff --git a/middleware_test.go b/middleware_test.go index e9f0ef55..4963b66f 100644 --- a/middleware_test.go +++ b/middleware_test.go @@ -158,7 +158,10 @@ func TestMiddlewareExecution(t *testing.T) { router := NewRouter() router.HandleFunc("/", func(w http.ResponseWriter, e *http.Request) { - w.Write(handlerStr) + _, err := w.Write(handlerStr) + if err != nil { + t.Fatalf("Failed writing HTTP response: %v", err) + } }) t.Run("responds normally without middleware", func(t *testing.T) { @@ -178,7 +181,10 @@ func TestMiddlewareExecution(t *testing.T) { router.Use(func(h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Write(mwStr) + _, err := w.Write(mwStr) + if err != nil { + t.Fatalf("Failed writing HTTP response: %v", err) + } h.ServeHTTP(w, r) }) }) @@ -196,11 +202,17 @@ func TestMiddlewareNotFound(t *testing.T) { router := NewRouter() router.HandleFunc("/", func(w http.ResponseWriter, e *http.Request) { - w.Write(handlerStr) + _, err := w.Write(handlerStr) + if err != nil { + t.Fatalf("Failed writing HTTP response: %v", err) + } }) router.Use(func(h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Write(mwStr) + _, err := w.Write(mwStr) + if err != nil { + t.Fatalf("Failed writing HTTP response: %v", err) + } h.ServeHTTP(w, r) }) }) @@ -221,7 +233,10 @@ func TestMiddlewareNotFound(t *testing.T) { req := newRequest("GET", "/notfound") router.NotFoundHandler = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { - rw.Write([]byte("Custom 404 handler")) + _, err := rw.Write([]byte("Custom 404 handler")) + if err != nil { + t.Fatalf("Failed writing HTTP response: %v", err) + } }) router.ServeHTTP(rw, req) @@ -237,12 +252,18 @@ func TestMiddlewareMethodMismatch(t *testing.T) { router := NewRouter() router.HandleFunc("/", func(w http.ResponseWriter, e *http.Request) { - w.Write(handlerStr) + _, err := w.Write(handlerStr) + if err != nil { + t.Fatalf("Failed writing HTTP response: %v", err) + } }).Methods("GET") router.Use(func(h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Write(mwStr) + _, err := w.Write(mwStr) + if err != nil { + t.Fatalf("Failed writing HTTP response: %v", err) + } h.ServeHTTP(w, r) }) }) @@ -262,7 +283,10 @@ func TestMiddlewareMethodMismatch(t *testing.T) { req := newRequest("POST", "/") router.MethodNotAllowedHandler = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { - rw.Write([]byte("Method not allowed")) + _, err := rw.Write([]byte("Method not allowed")) + if err != nil { + t.Fatalf("Failed writing HTTP response: %v", err) + } }) router.ServeHTTP(rw, req) @@ -278,17 +302,26 @@ func TestMiddlewareNotFoundSubrouter(t *testing.T) { router := NewRouter() router.HandleFunc("/", func(w http.ResponseWriter, e *http.Request) { - w.Write(handlerStr) + _, err := w.Write(handlerStr) + if err != nil { + t.Fatalf("Failed writing HTTP response: %v", err) + } }) subrouter := router.PathPrefix("/sub/").Subrouter() subrouter.HandleFunc("/", func(w http.ResponseWriter, e *http.Request) { - w.Write(handlerStr) + _, err := w.Write(handlerStr) + if err != nil { + t.Fatalf("Failed writing HTTP response: %v", err) + } }) router.Use(func(h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Write(mwStr) + _, err := w.Write(mwStr) + if err != nil { + t.Fatalf("Failed writing HTTP response: %v", err) + } h.ServeHTTP(w, r) }) }) @@ -308,7 +341,10 @@ func TestMiddlewareNotFoundSubrouter(t *testing.T) { req := newRequest("GET", "/sub/notfound") subrouter.NotFoundHandler = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { - rw.Write([]byte("Custom 404 handler")) + _, err := rw.Write([]byte("Custom 404 handler")) + if err != nil { + t.Fatalf("Failed writing HTTP response: %v", err) + } }) router.ServeHTTP(rw, req) @@ -324,17 +360,26 @@ func TestMiddlewareMethodMismatchSubrouter(t *testing.T) { router := NewRouter() router.HandleFunc("/", func(w http.ResponseWriter, e *http.Request) { - w.Write(handlerStr) + _, err := w.Write(handlerStr) + if err != nil { + t.Fatalf("Failed writing HTTP response: %v", err) + } }) subrouter := router.PathPrefix("/sub/").Subrouter() subrouter.HandleFunc("/", func(w http.ResponseWriter, e *http.Request) { - w.Write(handlerStr) + _, err := w.Write(handlerStr) + if err != nil { + t.Fatalf("Failed writing HTTP response: %v", err) + } }).Methods("GET") router.Use(func(h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Write(mwStr) + _, err := w.Write(mwStr) + if err != nil { + t.Fatalf("Failed writing HTTP response: %v", err) + } h.ServeHTTP(w, r) }) }) @@ -354,7 +399,10 @@ func TestMiddlewareMethodMismatchSubrouter(t *testing.T) { req := newRequest("POST", "/sub/") router.MethodNotAllowedHandler = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { - rw.Write([]byte("Method not allowed")) + _, err := rw.Write([]byte("Method not allowed")) + if err != nil { + t.Fatalf("Failed writing HTTP response: %v", err) + } }) router.ServeHTTP(rw, req) @@ -508,7 +556,10 @@ func TestMiddlewareOnMultiSubrouter(t *testing.T) { secondSubRouter := router.PathPrefix("/").Subrouter() router.NotFoundHandler = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { - rw.Write([]byte(notFound)) + _, err := rw.Write([]byte(notFound)) + if err != nil { + t.Fatalf("Failed writing HTTP response: %v", err) + } }) firstSubRouter.HandleFunc("/first", func(w http.ResponseWriter, r *http.Request) { @@ -521,14 +572,20 @@ func TestMiddlewareOnMultiSubrouter(t *testing.T) { firstSubRouter.Use(func(h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Write([]byte(first)) + _, err := w.Write([]byte(first)) + if err != nil { + t.Fatalf("Failed writing HTTP response: %v", err) + } h.ServeHTTP(w, r) }) }) secondSubRouter.Use(func(h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Write([]byte(second)) + _, err := w.Write([]byte(second)) + if err != nil { + t.Fatalf("Failed writing HTTP response: %v", err) + } h.ServeHTTP(w, r) }) }) diff --git a/mux_httpserver_test.go b/mux_httpserver_test.go index 907ab91d..f55a2de3 100644 --- a/mux_httpserver_test.go +++ b/mux_httpserver_test.go @@ -5,7 +5,7 @@ package mux import ( "bytes" - "io/ioutil" + "io" "net/http" "net/http/httptest" "testing" @@ -14,10 +14,16 @@ import ( func TestSchemeMatchers(t *testing.T) { router := NewRouter() router.HandleFunc("/", func(rw http.ResponseWriter, r *http.Request) { - rw.Write([]byte("hello http world")) + _, err := rw.Write([]byte("hello http world")) + if err != nil { + t.Fatalf("Failed writing HTTP response: %v", err) + } }).Schemes("http") router.HandleFunc("/", func(rw http.ResponseWriter, r *http.Request) { - rw.Write([]byte("hello https world")) + _, err := rw.Write([]byte("hello https world")) + if err != nil { + t.Fatalf("Failed writing HTTP response: %v", err) + } }).Schemes("https") assertResponseBody := func(t *testing.T, s *httptest.Server, expectedBody string) { @@ -28,7 +34,7 @@ func TestSchemeMatchers(t *testing.T) { if resp.StatusCode != 200 { t.Fatalf("expected a status code of 200, got %v", resp.StatusCode) } - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) if err != nil { t.Fatalf("unexpected error reading body: %v", err) } diff --git a/mux_test.go b/mux_test.go index 2d8d2b3e..4345254b 100644 --- a/mux_test.go +++ b/mux_test.go @@ -10,7 +10,8 @@ import ( "context" "errors" "fmt" - "io/ioutil" + "io" + "log" "net/http" "net/http/httptest" "net/url" @@ -2136,7 +2137,10 @@ type methodsSubrouterTest struct { // methodHandler writes the method string in response. func methodHandler(method string) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - w.Write([]byte(method)) + _, err := w.Write([]byte(method)) + if err != nil { + log.Printf("Failed writing HTTP response: %v", err) + } } } @@ -2778,7 +2782,7 @@ func TestSubrouterCustomMethodNotAllowed(t *testing.T) { tt.Errorf("Expected status code 405 (got %d)", w.Code) } - b, err := ioutil.ReadAll(w.Body) + b, err := io.ReadAll(w.Body) if err != nil { tt.Errorf("failed to read body: %v", err) } @@ -2859,7 +2863,10 @@ func stringMapEqual(m1, m2 map[string]string) bool { // http.ResponseWriter. func stringHandler(s string) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - w.Write([]byte(s)) + _, err := w.Write([]byte(s)) + if err != nil { + log.Printf("Failed writing HTTP response: %v", err) + } } } @@ -2892,7 +2899,10 @@ func newRequest(method, url string) *http.Request { // Simulate writing to wire var buff bytes.Buffer - req.Write(&buff) + err = req.Write(&buff) + if err != nil { + log.Printf("Failed writing HTTP request: %v", err) + } ioreader := bufio.NewReader(&buff) // Parse request off of 'wire' diff --git a/regexp.go b/regexp.go index 37c11edc..5d05cfa0 100644 --- a/regexp.go +++ b/regexp.go @@ -195,7 +195,7 @@ func (r *routeRegexp) Match(req *http.Request, match *RouteMatch) bool { // url builds a URL part using the given values. func (r *routeRegexp) url(values map[string]string) (string, error) { - urlValues := make([]interface{}, len(r.varsN), len(r.varsN)) + urlValues := make([]interface{}, len(r.varsN)) for k, v := range r.varsN { value, ok := values[v] if !ok { diff --git a/regexp_test.go b/regexp_test.go index 0d80e6a5..d7518f3e 100644 --- a/regexp_test.go +++ b/regexp_test.go @@ -54,7 +54,7 @@ func Benchmark_findQueryKey(b *testing.B) { b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { - for key, _ := range all { + for key := range all { _, _ = findFirstQueryKey(query, key) } } @@ -79,7 +79,7 @@ func Benchmark_findQueryKeyGoLib(b *testing.B) { b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { - for key, _ := range all { + for key := range all { v := u.Query()[key] if len(v) > 0 { _ = v[0] diff --git a/route.go b/route.go index 750afe57..ce1c9bfe 100644 --- a/route.go +++ b/route.go @@ -64,7 +64,7 @@ func (r *Route) Match(req *http.Request, match *RouteMatch) bool { match.MatchErr = nil } - matchErr = nil + matchErr = nil // nolint:ineffassign return false } } @@ -230,9 +230,9 @@ func (m headerMatcher) Match(r *http.Request, match *RouteMatch) bool { // Headers adds a matcher for request header values. // It accepts a sequence of key/value pairs to be matched. For example: // -// r := mux.NewRouter() -// r.Headers("Content-Type", "application/json", -// "X-Requested-With", "XMLHttpRequest") +// r := mux.NewRouter() +// r.Headers("Content-Type", "application/json", +// "X-Requested-With", "XMLHttpRequest") // // The above route will only match if both request header values match. // If the value is an empty string, it will match any value if the key is set. @@ -255,9 +255,9 @@ func (m headerRegexMatcher) Match(r *http.Request, match *RouteMatch) bool { // HeadersRegexp accepts a sequence of key/value pairs, where the value has regex // support. For example: // -// r := mux.NewRouter() -// r.HeadersRegexp("Content-Type", "application/(text|json)", -// "X-Requested-With", "XMLHttpRequest") +// r := mux.NewRouter() +// r.HeadersRegexp("Content-Type", "application/(text|json)", +// "X-Requested-With", "XMLHttpRequest") // // The above route will only match if both the request header matches both regular expressions. // If the value is an empty string, it will match any value if the key is set. @@ -283,10 +283,10 @@ func (r *Route) HeadersRegexp(pairs ...string) *Route { // // For example: // -// r := mux.NewRouter() -// r.Host("www.example.com") -// r.Host("{subdomain}.domain.com") -// r.Host("{subdomain:[a-z]+}.domain.com") +// r := mux.NewRouter() +// r.Host("www.example.com") +// r.Host("{subdomain}.domain.com") +// r.Host("{subdomain:[a-z]+}.domain.com") // // Variable names must be unique in a given route. They can be retrieved // calling mux.Vars(request). @@ -342,11 +342,11 @@ func (r *Route) Methods(methods ...string) *Route { // // For example: // -// r := mux.NewRouter() -// r.Path("/products/").Handler(ProductsHandler) -// r.Path("/products/{key}").Handler(ProductsHandler) -// r.Path("/articles/{category}/{id:[0-9]+}"). -// Handler(ArticleHandler) +// r := mux.NewRouter() +// r.Path("/products/").Handler(ProductsHandler) +// r.Path("/products/{key}").Handler(ProductsHandler) +// r.Path("/articles/{category}/{id:[0-9]+}"). +// Handler(ArticleHandler) // // Variable names must be unique in a given route. They can be retrieved // calling mux.Vars(request). @@ -377,8 +377,8 @@ func (r *Route) PathPrefix(tpl string) *Route { // It accepts a sequence of key/value pairs. Values may define variables. // For example: // -// r := mux.NewRouter() -// r.Queries("foo", "bar", "id", "{id:[0-9]+}") +// r := mux.NewRouter() +// r.Queries("foo", "bar", "id", "{id:[0-9]+}") // // The above route will only match if the URL contains the defined queries // values, e.g.: ?foo=bar&id=42. @@ -473,11 +473,11 @@ func (r *Route) BuildVarsFunc(f BuildVarsFunc) *Route { // // It will test the inner routes only if the parent route matched. For example: // -// r := mux.NewRouter() -// s := r.Host("www.example.com").Subrouter() -// s.HandleFunc("/products/", ProductsHandler) -// s.HandleFunc("/products/{key}", ProductHandler) -// s.HandleFunc("/articles/{category}/{id:[0-9]+}"), ArticleHandler) +// r := mux.NewRouter() +// s := r.Host("www.example.com").Subrouter() +// s.HandleFunc("/products/", ProductsHandler) +// s.HandleFunc("/products/{key}", ProductHandler) +// s.HandleFunc("/articles/{category}/{id:[0-9]+}"), ArticleHandler) // // Here, the routes registered in the subrouter won't be tested if the host // doesn't match. @@ -497,36 +497,36 @@ func (r *Route) Subrouter() *Router { // It accepts a sequence of key/value pairs for the route variables. For // example, given this route: // -// r := mux.NewRouter() -// r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler). -// Name("article") +// r := mux.NewRouter() +// r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler). +// Name("article") // // ...a URL for it can be built using: // -// url, err := r.Get("article").URL("category", "technology", "id", "42") +// url, err := r.Get("article").URL("category", "technology", "id", "42") // // ...which will return an url.URL with the following path: // -// "/articles/technology/42" +// "/articles/technology/42" // // This also works for host variables: // -// r := mux.NewRouter() -// r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler). -// Host("{subdomain}.domain.com"). -// Name("article") +// r := mux.NewRouter() +// r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler). +// Host("{subdomain}.domain.com"). +// Name("article") // -// // url.String() will be "http://news.domain.com/articles/technology/42" -// url, err := r.Get("article").URL("subdomain", "news", -// "category", "technology", -// "id", "42") +// // url.String() will be "http://news.domain.com/articles/technology/42" +// url, err := r.Get("article").URL("subdomain", "news", +// "category", "technology", +// "id", "42") // // The scheme of the resulting url will be the first argument that was passed to Schemes: // -// // url.String() will be "https://example.com" -// r := mux.NewRouter() -// url, err := r.Host("example.com") -// .Schemes("https", "http").URL() +// // url.String() will be "https://example.com" +// r := mux.NewRouter() +// url, err := r.Host("example.com") +// .Schemes("https", "http").URL() // // All variables defined in the route are required, and their values must // conform to the corresponding patterns. From 96847b8b06f990d7b53c8a5c647ee73d3e70582a Mon Sep 17 00:00:00 2001 From: Corey Daley Date: Sun, 23 Jul 2023 16:27:23 -0400 Subject: [PATCH 10/25] Delete release-drafter.yml (#719) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What type of PR is this? (check all applicable) - [ ] Refactor - [ ] Feature - [ ] Bug Fix - [ ] Optimization - [ ] Documentation Update ## Description ## Related Tickets & Documents - Related Issue # - Closes # ## Added/updated tests? - [ ] Yes - [ ] No, and this is why: _please replace this line with details on why tests have not been included_ - [ ] I need help with writing tests ## Run verifications and test - [ ] `make verify` is passing - [ ] `make test` is passing Signed-off-by: Corey Daley --- .github/release-drafter.yml | 8 -------- 1 file changed, 8 deletions(-) delete mode 100644 .github/release-drafter.yml diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml deleted file mode 100644 index 2db2e139..00000000 --- a/.github/release-drafter.yml +++ /dev/null @@ -1,8 +0,0 @@ -# Config for https://github.com/apps/release-drafter -template: | - - - - ## CHANGELOG - - $CHANGES From 81b48a39f012296f51d582d3cefaaba1c2f5a01f Mon Sep 17 00:00:00 2001 From: Corey Daley Date: Sun, 23 Jul 2023 16:28:38 -0400 Subject: [PATCH 11/25] Delete stale.yml (#720) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What type of PR is this? (check all applicable) - [ ] Refactor - [ ] Feature - [ ] Bug Fix - [ ] Optimization - [ ] Documentation Update ## Description ## Related Tickets & Documents - Related Issue # - Closes # ## Added/updated tests? - [ ] Yes - [ ] No, and this is why: _please replace this line with details on why tests have not been included_ - [ ] I need help with writing tests ## Run verifications and test - [ ] `make verify` is passing - [ ] `make test` is passing Signed-off-by: Corey Daley --- .github/stale.yml | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 .github/stale.yml diff --git a/.github/stale.yml b/.github/stale.yml deleted file mode 100644 index f4b12d30..00000000 --- a/.github/stale.yml +++ /dev/null @@ -1,12 +0,0 @@ -daysUntilStale: 75 -daysUntilClose: 14 -# Issues with these labels will never be considered stale -exemptLabels: - - proposal - - needs review - - build system -staleLabel: stale -markComment: > - This issue has been automatically marked as stale because it hasn't seen - a recent update. It'll be automatically closed in a few days. -closeComment: false From 2392d7d19e8c3b02af216a404709415813467766 Mon Sep 17 00:00:00 2001 From: Corey Daley Date: Sun, 23 Jul 2023 16:29:12 -0400 Subject: [PATCH 12/25] Delete AUTHORS (#721) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What type of PR is this? (check all applicable) - [ ] Refactor - [ ] Feature - [ ] Bug Fix - [ ] Optimization - [ ] Documentation Update ## Description ## Related Tickets & Documents - Related Issue # - Closes # ## Added/updated tests? - [ ] Yes - [ ] No, and this is why: _please replace this line with details on why tests have not been included_ - [ ] I need help with writing tests ## Run verifications and test - [ ] `make verify` is passing - [ ] `make test` is passing Signed-off-by: Corey Daley --- AUTHORS | 8 -------- 1 file changed, 8 deletions(-) delete mode 100644 AUTHORS diff --git a/AUTHORS b/AUTHORS deleted file mode 100644 index b722392e..00000000 --- a/AUTHORS +++ /dev/null @@ -1,8 +0,0 @@ -# This is the official list of gorilla/mux authors for copyright purposes. -# -# Please keep the list sorted. - -Google LLC (https://opensource.google.com/) -Kamil Kisielk -Matt Silverlock -Rodrigo Moraes (https://github.com/moraes) From ff63d0e2fd6b3634b25a63c85c27a3dc80a3b8c8 Mon Sep 17 00:00:00 2001 From: Corey Daley Date: Sun, 23 Jul 2023 16:32:33 -0400 Subject: [PATCH 13/25] Update LICENSE (#722) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What type of PR is this? (check all applicable) - [ ] Refactor - [ ] Feature - [ ] Bug Fix - [ ] Optimization - [ ] Documentation Update ## Description ## Related Tickets & Documents - Related Issue # - Closes # ## Added/updated tests? - [ ] Yes - [ ] No, and this is why: _please replace this line with details on why tests have not been included_ - [ ] I need help with writing tests ## Run verifications and test - [ ] `make verify` is passing - [ ] `make test` is passing Signed-off-by: Corey Daley --- LICENSE | 34 ++++++++++------------------------ 1 file changed, 10 insertions(+), 24 deletions(-) diff --git a/LICENSE b/LICENSE index 6903df63..1cedc459 100644 --- a/LICENSE +++ b/LICENSE @@ -1,27 +1,13 @@ -Copyright (c) 2012-2018 The Gorilla Authors. All rights reserved. +BSD 3-Clause License -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: +Copyright 2023 Gorilla web toolkit contributors - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS β€œAS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. From 753457f3059d3db9a96f9f4c67d7f3724e97478d Mon Sep 17 00:00:00 2001 From: Sham Karthik S <53367916+shamkarthik@users.noreply.github.com> Date: Tue, 25 Jul 2023 22:05:16 +0530 Subject: [PATCH 14/25] Updated the logo in README.md (#724) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What type of PR is this? (check all applicable) - [ ] Refactor - [ ] Feature - [ ] Bug Fix - [ ] Optimization - [x] Documentation Update ## Description ## Related Tickets & Documents - Related Issue #710 - Closes #710 ## Added/updated tests? - [ ] Yes - [ ] No, and this is why: _please replace this line with details on why tests have not been included_ - [ ] I need help with writing tests ## Run verifications and test - [ ] `make verify` is passing - [ ] `make test` is passing Signed-off-by: Sham Karthik S <53367916+shamkarthik@users.noreply.github.com> --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 551f3c67..4553c1fe 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ [![sourcegraph](https://sourcegraph.com/github.com/gorilla/mux/-/badge.svg)](https://sourcegraph.com/github.com/gorilla/mux?badge) -![Gorilla Logo](https://cloud-cdn.questionable.services/gorilla-icon-64.png) +![Gorilla Logo](https://github.com/gorilla/.github/assets/53367916/d92caabf-98e0-473e-bfbf-ab554ba435e5) Package `gorilla/mux` implements a request router and dispatcher for matching incoming requests to their respective handler. From 809d12850fc170caa4229eb2da58a4701c9837b8 Mon Sep 17 00:00:00 2001 From: Corey Daley Date: Tue, 25 Jul 2023 13:19:44 -0400 Subject: [PATCH 15/25] Update LICENSE (#723) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What type of PR is this? (check all applicable) - [ ] Refactor - [ ] Feature - [ ] Bug Fix - [ ] Optimization - [ ] Documentation Update ## Description ## Related Tickets & Documents - Related Issue # - Closes # ## Added/updated tests? - [ ] Yes - [ ] No, and this is why: _please replace this line with details on why tests have not been included_ - [ ] I need help with writing tests ## Run verifications and test - [ ] `make verify` is passing - [ ] `make test` is passing Signed-off-by: Corey Daley --- LICENSE | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/LICENSE b/LICENSE index 1cedc459..bb9d80bc 100644 --- a/LICENSE +++ b/LICENSE @@ -1,13 +1,27 @@ -BSD 3-Clause License +Copyright (c) 2023 The Gorilla Authors. All rights reserved. -Copyright 2023 Gorilla web toolkit contributors +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: -Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. -1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - -3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS β€œAS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. From cfc696d6d239ff68ceb71ee35c9a4e4ef3f30ed9 Mon Sep 17 00:00:00 2001 From: Corey Daley Date: Sun, 30 Jul 2023 12:23:57 -0400 Subject: [PATCH 16/25] Update issues.yml (#726) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What type of PR is this? (check all applicable) - [ ] Refactor - [ ] Feature - [ ] Bug Fix - [ ] Optimization - [ ] Documentation Update ## Description ## Related Tickets & Documents - Related Issue # - Closes # ## Added/updated tests? - [ ] Yes - [ ] No, and this is why: _please replace this line with details on why tests have not been included_ - [ ] I need help with writing tests ## Run verifications and test - [ ] `make verify` is passing - [ ] `make test` is passing Signed-off-by: Corey Daley --- .github/workflows/issues.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/issues.yml b/.github/workflows/issues.yml index 055ca822..ff9e3183 100644 --- a/.github/workflows/issues.yml +++ b/.github/workflows/issues.yml @@ -5,7 +5,7 @@ on: issues: types: - opened - pull_request: + pull_request_target: types: - opened @@ -17,4 +17,4 @@ jobs: uses: actions/add-to-project@v0.5.0 with: project-url: https://github.com/orgs/gorilla/projects/4 - github-token: ${{ secrets.ADD_TO_PROJECT_TOKEN }} \ No newline at end of file + github-token: ${{ secrets.ADD_TO_PROJECT_TOKEN }} From 651928c42f7b60c7942e46d5f651dd1ece444246 Mon Sep 17 00:00:00 2001 From: Corey Daley Date: Sun, 30 Jul 2023 12:37:42 -0400 Subject: [PATCH 17/25] Update issues.yml (#727) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What type of PR is this? (check all applicable) - [ ] Refactor - [ ] Feature - [ ] Bug Fix - [ ] Optimization - [ ] Documentation Update ## Description ## Related Tickets & Documents - Related Issue # - Closes # ## Added/updated tests? - [ ] Yes - [ ] No, and this is why: _please replace this line with details on why tests have not been included_ - [ ] I need help with writing tests ## Run verifications and test - [ ] `make verify` is passing - [ ] `make test` is passing Signed-off-by: Corey Daley --- .github/workflows/issues.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/issues.yml b/.github/workflows/issues.yml index ff9e3183..8be6cedb 100644 --- a/.github/workflows/issues.yml +++ b/.github/workflows/issues.yml @@ -8,6 +8,7 @@ on: pull_request_target: types: - opened + - reopened jobs: add-to-project: From 546dd0cc9f3ecdef8b065ac6336b4c6ed99887d4 Mon Sep 17 00:00:00 2001 From: ICHINOSE Shogo Date: Mon, 31 Jul 2023 10:44:28 +0900 Subject: [PATCH 18/25] run go fmt with Go 1.20 (#725) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What type of PR is this? (check all applicable) - [ ] Refactor - [ ] Feature - [ ] Bug Fix - [ ] Optimization - [x] Documentation Update ## Description `go fmt` now formats godoc comments. I apply it. ## Related Tickets & Documents - Related Issue # - Closes # ## Added/updated tests? - [ ] Yes - [x] No, and this is why: this pull request updates only comments - [ ] I need help with writing tests ## Run verifications and test - [x] `make verify` is passing - [x] `make test` is passing Co-authored-by: Corey Daley --- doc.go | 25 ++++++++++++------------- mux.go | 14 +++++++------- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/doc.go b/doc.go index bd5a38b5..80601351 100644 --- a/doc.go +++ b/doc.go @@ -10,18 +10,18 @@ http.ServeMux, mux.Router matches incoming requests against a list of registered routes and calls a handler for the route that matches the URL or other conditions. The main features are: - * Requests can be matched based on URL host, path, path prefix, schemes, - header and query values, HTTP methods or using custom matchers. - * URL hosts, paths and query values can have variables with an optional - regular expression. - * Registered URLs can be built, or "reversed", which helps maintaining - references to resources. - * Routes can be used as subrouters: nested routes are only tested if the - parent route matches. This is useful to define groups of routes that - share common conditions like a host, a path prefix or other repeated - attributes. As a bonus, this optimizes request matching. - * It implements the http.Handler interface so it is compatible with the - standard http.ServeMux. + - Requests can be matched based on URL host, path, path prefix, schemes, + header and query values, HTTP methods or using custom matchers. + - URL hosts, paths and query values can have variables with an optional + regular expression. + - Registered URLs can be built, or "reversed", which helps maintaining + references to resources. + - Routes can be used as subrouters: nested routes are only tested if the + parent route matches. This is useful to define groups of routes that + share common conditions like a host, a path prefix or other repeated + attributes. As a bonus, this optimizes request matching. + - It implements the http.Handler interface so it is compatible with the + standard http.ServeMux. Let's start registering a couple of URL paths and handlers: @@ -301,6 +301,5 @@ A more complex authentication middleware, which maps session token to users, cou r.Use(amw.Middleware) Note: The handler chain will be stopped if your middleware doesn't call `next.ServeHTTP()` with the corresponding parameters. This can be used to abort a request if the middleware writer wants to. - */ package mux diff --git a/mux.go b/mux.go index f126a602..1e089906 100644 --- a/mux.go +++ b/mux.go @@ -31,17 +31,17 @@ func NewRouter() *Router { // It implements the http.Handler interface, so it can be registered to serve // requests: // -// var router = mux.NewRouter() +// var router = mux.NewRouter() // -// func main() { -// http.Handle("/", router) -// } +// func main() { +// http.Handle("/", router) +// } // // Or, for Google App Engine, register it in a init() function: // -// func init() { -// http.Handle("/", router) -// } +// func init() { +// http.Handle("/", router) +// } // // This will send all incoming requests to the router. type Router struct { From 24c3e7f499efd8b1429cfe789c7e6a3631357045 Mon Sep 17 00:00:00 2001 From: Mustaque Ahmed Date: Wed, 16 Aug 2023 07:38:21 +0530 Subject: [PATCH 19/25] Fix `Single Page Application` example in `README.md` file (#678) Fixes #588 **Summary of Changes** 1. Add test case to validate proposed fix (both negative and positive test case). 2. Update `README.md` file. PS: If you want to verify how I'm able to reproduce the issue and tried my fix. Here is a link of my local PR where I have configured `Github Action` to execute same test cases on all the platforms i.e. `ubuntu`, `macos` and `windows`. PR link: https://github.com/amustaque97/mux/pull/1 > PS: Make sure your PR includes/updates tests! If you need help with this part, just ask! --------- Co-authored-by: Corey Daley --- README.md | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 4553c1fe..f3cdc9c4 100644 --- a/README.md +++ b/README.md @@ -247,20 +247,11 @@ type spaHandler struct { // file located at the index path on the SPA handler will be served. This // is suitable behavior for serving an SPA (single page application). func (h spaHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { - // get the absolute path to prevent directory traversal - path, err := filepath.Abs(r.URL.Path) - if err != nil { - // if we failed to get the absolute path respond with a 400 bad request - // and stop - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - - // prepend the path with the path to the static directory - path = filepath.Join(h.staticPath, path) + // Join internally call path.Clean to prevent directory traversal + path := filepath.Join(h.staticPath, path) // check whether a file exists at the given path - _, err = os.Stat(path) + _, err := os.Stat(path) if os.IsNotExist(err) { // file does not exist, serve index.html http.ServeFile(w, r, filepath.Join(h.staticPath, h.indexPath)) From 395ad81d0ebffa0c5a36f3e5a2e720a7d5870d92 Mon Sep 17 00:00:00 2001 From: Soheil Rahmat Date: Thu, 17 Aug 2023 04:34:43 +0300 Subject: [PATCH 20/25] [BUG] Inconsistent HTTP status code on query mismatch (#712) The logical behavior of a router should return an HTTP status code of 404 when a request fails to satisfy route validation logic. Previously, MUX was returning a 405 HTTP status code in some rare scenarios, which was not valid in its case. For more info, See: https://github.com/gorilla/mux/issues/704 Fixes #704 **Summary of Changes** 1. Clear the mismatch error of the previous validations on method match. 2. Added related tests > PS: Make sure your PR includes/updates tests! If you need help with this part, just ask! Co-authored-by: Corey Daley --- mux_test.go | 47 +++++++++++++++++++++++++++++++++++++++++++++++ route.go | 10 ++++++++++ 2 files changed, 57 insertions(+) diff --git a/mux_test.go b/mux_test.go index 4345254b..bd97d33b 100644 --- a/mux_test.go +++ b/mux_test.go @@ -2069,6 +2069,53 @@ func TestNoMatchMethodErrorHandler(t *testing.T) { } } +func TestMultipleDefinitionOfSamePathWithDifferentMethods(t *testing.T) { + emptyHandler := func(w http.ResponseWriter, r *http.Request) {} + + r := NewRouter() + r.HandleFunc("/api", emptyHandler).Methods("POST") + r.HandleFunc("/api", emptyHandler).Queries("time", "{time:[0-9]+}").Methods("GET") + + t.Run("Post Method should be matched properly", func(t *testing.T) { + req, _ := http.NewRequest("POST", "http://localhost/api", nil) + match := new(RouteMatch) + matched := r.Match(req, match) + if !matched { + t.Error("Should have matched route for methods") + } + if match.MatchErr != nil { + t.Error("Should not have any matching error. Found:", match.MatchErr) + } + }) + + t.Run("Get Method with invalid query value should not match", func(t *testing.T) { + req, _ := http.NewRequest("GET", "http://localhost/api?time=-4", nil) + match := new(RouteMatch) + matched := r.Match(req, match) + if matched { + t.Error("Should not have matched route for methods") + } + if match.MatchErr != ErrNotFound { + t.Error("Should have ErrNotFound error. Found:", match.MatchErr) + } + }) + + t.Run("A mismach method of a valid path should return ErrMethodMismatch", func(t *testing.T) { + r := NewRouter() + r.HandleFunc("/api2", emptyHandler).Methods("POST") + req, _ := http.NewRequest("GET", "http://localhost/api2", nil) + match := new(RouteMatch) + matched := r.Match(req, match) + if matched { + t.Error("Should not have matched route for methods") + } + if match.MatchErr != ErrMethodMismatch { + t.Error("Should have ErrMethodMismatch error. Found:", match.MatchErr) + } + }) + +} + func TestErrMatchNotFound(t *testing.T) { emptyHandler := func(w http.ResponseWriter, r *http.Request) {} diff --git a/route.go b/route.go index ce1c9bfe..abea99be 100644 --- a/route.go +++ b/route.go @@ -66,6 +66,16 @@ func (r *Route) Match(req *http.Request, match *RouteMatch) bool { matchErr = nil // nolint:ineffassign return false + } else { + // Multiple routes may share the same path but use different HTTP methods. For instance: + // Route 1: POST "/users/{id}". + // Route 2: GET "/users/{id}", parameters: "id": "[0-9]+". + // + // The router must handle these cases correctly. For a GET request to "/users/abc" with "id" as "-2", + // The router should return a "Not Found" error as no route fully matches this request. + if match.MatchErr == ErrMethodMismatch { + match.MatchErr = nil + } } } From 79f2f457ca5017e08de9f86ada03939a82dbf714 Mon Sep 17 00:00:00 2001 From: Andrew Brown <8261769+andrew-werdna@users.noreply.github.com> Date: Wed, 16 Aug 2023 23:48:59 -0500 Subject: [PATCH 21/25] Clarify documentation examples of Route methods (#672) Fixes #666 **Summary of Changes** 1. Update `Route` method documentation comments where the example in the comments showed a `Router` before. Updated method names include: * `Headers` * `HeadersRegexp` * `Host` * `Path` * `Queries` * `Subrouter` * `URL` Notes: * This includes what PR #667 did plus some changes requested by a maintainer in the comments * I was a little torn about changing the example in `Subrouter` since `(*Router).Host()` (like several `(*Router)` methods) just calls `(*Router).NewRoute().Host()` so I understand if maintainers are ambivalent about that example or want it to remain the same. Signed-off-by: Corey Daley Co-authored-by: Andrew Brown Co-authored-by: Corey Daley --- route.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/route.go b/route.go index abea99be..cd85f4b3 100644 --- a/route.go +++ b/route.go @@ -240,7 +240,7 @@ func (m headerMatcher) Match(r *http.Request, match *RouteMatch) bool { // Headers adds a matcher for request header values. // It accepts a sequence of key/value pairs to be matched. For example: // -// r := mux.NewRouter() +// r := mux.NewRouter().NewRoute() // r.Headers("Content-Type", "application/json", // "X-Requested-With", "XMLHttpRequest") // @@ -265,7 +265,7 @@ func (m headerRegexMatcher) Match(r *http.Request, match *RouteMatch) bool { // HeadersRegexp accepts a sequence of key/value pairs, where the value has regex // support. For example: // -// r := mux.NewRouter() +// r := mux.NewRouter().NewRoute() // r.HeadersRegexp("Content-Type", "application/(text|json)", // "X-Requested-With", "XMLHttpRequest") // @@ -293,7 +293,7 @@ func (r *Route) HeadersRegexp(pairs ...string) *Route { // // For example: // -// r := mux.NewRouter() +// r := mux.NewRouter().NewRoute() // r.Host("www.example.com") // r.Host("{subdomain}.domain.com") // r.Host("{subdomain:[a-z]+}.domain.com") @@ -352,7 +352,7 @@ func (r *Route) Methods(methods ...string) *Route { // // For example: // -// r := mux.NewRouter() +// r := mux.NewRouter().NewRoute() // r.Path("/products/").Handler(ProductsHandler) // r.Path("/products/{key}").Handler(ProductsHandler) // r.Path("/articles/{category}/{id:[0-9]+}"). @@ -387,7 +387,7 @@ func (r *Route) PathPrefix(tpl string) *Route { // It accepts a sequence of key/value pairs. Values may define variables. // For example: // -// r := mux.NewRouter() +// r := mux.NewRouter().NewRoute() // r.Queries("foo", "bar", "id", "{id:[0-9]+}") // // The above route will only match if the URL contains the defined queries @@ -483,7 +483,7 @@ func (r *Route) BuildVarsFunc(f BuildVarsFunc) *Route { // // It will test the inner routes only if the parent route matched. For example: // -// r := mux.NewRouter() +// r := mux.NewRouter().NewRoute() // s := r.Host("www.example.com").Subrouter() // s.HandleFunc("/products/", ProductsHandler) // s.HandleFunc("/products/{key}", ProductHandler) @@ -534,7 +534,7 @@ func (r *Route) Subrouter() *Router { // The scheme of the resulting url will be the first argument that was passed to Schemes: // // // url.String() will be "https://example.com" -// r := mux.NewRouter() +// r := mux.NewRouter().NewRoute() // url, err := r.Host("example.com") // .Schemes("https", "http").URL() // From 85123bf20e069b156415b871dea10517f6a8938a Mon Sep 17 00:00:00 2001 From: Suman Paik Date: Thu, 17 Aug 2023 20:59:52 +0530 Subject: [PATCH 22/25] changed the routeVariables text content. (#708) Before changing the context there is a blind spot, and for this reason, decided to add a line break. Co-authored-by: Corey Daley --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f3cdc9c4..9e49b24b 100644 --- a/README.md +++ b/README.md @@ -749,7 +749,8 @@ func TestMetricsHandler(t *testing.T) { rr := httptest.NewRecorder() - // Need to create a router that we can pass the request through so that the vars will be added to the context + // To add the varsΒ to the context, + // we need to createΒ a router through which we canΒ pass the request. router := mux.NewRouter() router.HandleFunc("/metrics/{type}", MetricsHandler) router.ServeHTTP(rr, req) From 4a671cbc5162efa2ecb1b353e6a704a62737d66c Mon Sep 17 00:00:00 2001 From: eh-steve <16373174+eh-steve@users.noreply.github.com> Date: Thu, 24 Aug 2023 20:57:51 +0100 Subject: [PATCH 23/25] Add GetVarNames() (#676) **Summary of Changes** 1. Added `r.GetVarNames()` function to list all vars a route might need in order to call `r.URL()` --------- Co-authored-by: Anonymous Co-authored-by: Corey Daley --- README.md | 13 +++++++++++++ example_route_vars_test.go | 35 +++++++++++++++++++++++++++++++++++ mux_test.go | 34 ++++++++++++++++++++++++++++++++++ route.go | 19 +++++++++++++++++++ 4 files changed, 101 insertions(+) create mode 100644 example_route_vars_test.go diff --git a/README.md b/README.md index 9e49b24b..87ada8ec 100644 --- a/README.md +++ b/README.md @@ -366,6 +366,19 @@ url, err := r.Get("article").URL("subdomain", "news", "id", "42") ``` +To find all the required variables for a given route when calling `URL()`, the method `GetVarNames()` is available: +```go +r := mux.NewRouter() +r.Host("{domain}"). + Path("/{group}/{item_id}"). + Queries("some_data1", "{some_data1}"). + Queries("some_data2", "{some_data2}"). + Name("article") + +// Will print [domain group item_id some_data1 some_data2] +fmt.Println(r.Get("article").GetVarNames()) + +``` ### Walking Routes The `Walk` function on `mux.Router` can be used to visit all of the routes that are registered on a router. For example, diff --git a/example_route_vars_test.go b/example_route_vars_test.go new file mode 100644 index 00000000..428c3b6c --- /dev/null +++ b/example_route_vars_test.go @@ -0,0 +1,35 @@ +package mux_test + +import ( + "fmt" + "github.com/gorilla/mux" +) + +// This example demonstrates building a dynamic URL using +// required vars and values retrieve from another source +func ExampleRoute_GetVarNames() { + r := mux.NewRouter() + + route := r.Host("{domain}"). + Path("/{group}/{item_id}"). + Queries("some_data1", "{some_data1}"). + Queries("some_data2_and_3", "{some_data2}.{some_data3}") + + dataSource := func(key string) string { + return "my_value_for_" + key + } + + varNames, _ := route.GetVarNames() + + pairs := make([]string, 0, len(varNames)*2) + + for _, varName := range varNames { + pairs = append(pairs, varName, dataSource(varName)) + } + + url, err := route.URL(pairs...) + if err != nil { + panic(err) + } + fmt.Println(url.String()) +} diff --git a/mux_test.go b/mux_test.go index bd97d33b..5898688b 100644 --- a/mux_test.go +++ b/mux_test.go @@ -2879,6 +2879,40 @@ func TestContextMiddleware(t *testing.T) { r.ServeHTTP(rec, req) } +func TestGetVarNames(t *testing.T) { + r := NewRouter() + + route := r.Host("{domain}"). + Path("/{group}/{item_id}"). + Queries("some_data1", "{some_data1}"). + Queries("some_data2_and_3", "{some_data2}.{some_data3}") + + // Order of vars in the slice is not guaranteed, so just check for existence + expected := map[string]bool{ + "domain": true, + "group": true, + "item_id": true, + "some_data1": true, + "some_data2": true, + "some_data3": true, + } + + varNames, err := route.GetVarNames() + if err != nil { + t.Fatal(err) + } + + if len(varNames) != len(expected) { + t.Fatalf("expected %d names, got %d", len(expected), len(varNames)) + } + + for _, varName := range varNames { + if !expected[varName] { + t.Fatalf("got unexpected %s", varName) + } + } +} + // mapToPairs converts a string map to a slice of string pairs func mapToPairs(m map[string]string) []string { var i int diff --git a/route.go b/route.go index cd85f4b3..e8f11df2 100644 --- a/route.go +++ b/route.go @@ -728,6 +728,25 @@ func (r *Route) GetHostTemplate() (string, error) { return r.regexp.host.template, nil } +// GetVarNames returns the names of all variables added by regexp matchers +// These can be used to know which route variables should be passed into r.URL() +func (r *Route) GetVarNames() ([]string, error) { + if r.err != nil { + return nil, r.err + } + var varNames []string + if r.regexp.host != nil { + varNames = append(varNames, r.regexp.host.varsN...) + } + if r.regexp.path != nil { + varNames = append(varNames, r.regexp.path.varsN...) + } + for _, regx := range r.regexp.queries { + varNames = append(varNames, regx.varsN...) + } + return varNames, nil +} + // prepareVars converts the route variable pairs into a map. If the route has a // BuildVarsFunc, it is invoked. func (r *Route) prepareVars(pairs ...string) (map[string]string, error) { From 3401478badc7aa55470bc806ee8cd185a16f9298 Mon Sep 17 00:00:00 2001 From: Steffen Wentzel Date: Thu, 21 Sep 2023 05:06:08 +0200 Subject: [PATCH 24/25] fix SPA handler in README.md (#733) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What type of PR is this? (check all applicable) - [ ] Refactor - [ ] Feature - [ ] Bug Fix - [ ] Optimization - [X] Documentation Update - [ ] Go Version Update - [ ] Dependency Update ## Description Changed the SPA handler example in README.md in two areas. First, made sure to actually include the requested path in the call to `filepath.Join`. Secondly, if the requested path hits a directory, I think it would be beneficial to also serve the `indexPath` file, and not list the directory contents. I also edited the comments in the `README.md` file accordingly. ## Related Tickets & Documents - Related Issue # - Closes # ## Added/updated tests? - [ ] Yes - [X] No, and this is why: I only changed the `README.md`, if any tests are necessary please let me know - [ ] I need help with writing tests ## Run verifications and test - [ ] `make verify` is passing - [ ] `make test` is passing --- README.md | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 87ada8ec..382513d5 100644 --- a/README.md +++ b/README.md @@ -247,23 +247,25 @@ type spaHandler struct { // file located at the index path on the SPA handler will be served. This // is suitable behavior for serving an SPA (single page application). func (h spaHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { - // Join internally call path.Clean to prevent directory traversal - path := filepath.Join(h.staticPath, path) + // Join internally call path.Clean to prevent directory traversal + path := filepath.Join(h.staticPath, r.URL.Path) - // check whether a file exists at the given path - _, err := os.Stat(path) - if os.IsNotExist(err) { - // file does not exist, serve index.html + // check whether a file exists or is a directory at the given path + fi, err := os.Stat(path) + if os.IsNotExist(err) || fi.IsDir() { + // file does not exist or path is a directory, serve index.html http.ServeFile(w, r, filepath.Join(h.staticPath, h.indexPath)) return - } else if err != nil { - // if we got an error (that wasn't that the file doesn't exist) stating the - // file, return a 500 internal server error and stop + } + + if err != nil { + // if we got an error (that wasn't that the file doesn't exist) stating the + // file, return a 500 internal server error and stop http.Error(w, err.Error(), http.StatusInternalServerError) - return + return } - // otherwise, use http.FileServer to serve the static dir + // otherwise, use http.FileServer to serve the static file http.FileServer(http.Dir(h.staticPath)).ServeHTTP(w, r) } From b4617d0b9670ad14039b2739167fd35a60f557c5 Mon Sep 17 00:00:00 2001 From: Corey Daley Date: Wed, 18 Oct 2023 07:23:00 -0400 Subject: [PATCH 25/25] update GitHub workflows (#734) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What type of PR is this? (check all applicable) - [x] Refactor - [ ] Feature - [ ] Bug Fix - [ ] Optimization - [ ] Documentation Update - [ ] Go Version Update - [ ] Dependency Update ## Description ## Related Tickets & Documents - Related Issue # - Closes # ## Added/updated tests? - [ ] Yes - [ ] No, and this is why: _please replace this line with details on why tests have not been included_ - [ ] I need help with writing tests ## Run verifications and test - [x] `make verify` is passing - [x] `make test` is passing --- .github/workflows/issues.yml | 2 +- .github/workflows/security.yml | 37 ++++++++++++++++++++++++++++++++++ .github/workflows/test.yml | 28 ++++--------------------- .github/workflows/verify.yml | 32 +++++++++++++++++++++++++++++ go.mod | 2 +- 5 files changed, 75 insertions(+), 26 deletions(-) create mode 100644 .github/workflows/security.yml create mode 100644 .github/workflows/verify.yml diff --git a/.github/workflows/issues.yml b/.github/workflows/issues.yml index 8be6cedb..768b05b3 100644 --- a/.github/workflows/issues.yml +++ b/.github/workflows/issues.yml @@ -1,4 +1,4 @@ -# Add issues or pull-requests created to the project. +# Add all the issues created to the project. name: Add issue or pull request to Project on: diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml new file mode 100644 index 00000000..ff4a613b --- /dev/null +++ b/.github/workflows/security.yml @@ -0,0 +1,37 @@ +name: Security +on: + push: + branches: + - main + pull_request: + branches: + - main +permissions: + contents: read +jobs: + scan: + strategy: + matrix: + go: ['1.20','1.21'] + fail-fast: true + runs-on: ubuntu-latest + steps: + - name: Checkout Code + uses: actions/checkout@v3 + + - name: Setup Go ${{ matrix.go }} + uses: actions/setup-go@v4 + with: + go-version: ${{ matrix.go }} + cache: false + + - name: Run GoSec + uses: securego/gosec@master + with: + args: -exclude-dir examples ./... + + - name: Run GoVulnCheck + uses: golang/govulncheck-action@v1 + with: + go-version-input: ${{ matrix.go }} + go-package: ./... diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index af48d228..50a3946a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,4 +1,4 @@ -name: CI +name: Test on: push: branches: @@ -6,15 +6,13 @@ on: pull_request: branches: - main - permissions: contents: read - jobs: - verify-and-test: + unit: strategy: matrix: - go: ['1.19','1.20'] + go: ['1.20','1.21'] os: [ubuntu-latest, macos-latest, windows-latest] fail-fast: true runs-on: ${{ matrix.os }} @@ -28,28 +26,10 @@ jobs: go-version: ${{ matrix.go }} cache: false - - name: Run GolangCI-Lint - uses: golangci/golangci-lint-action@v3 - with: - version: v1.53 - args: --timeout=5m - - - name: Run GoSec - if: matrix.os == 'ubuntu-latest' - uses: securego/gosec@master - with: - args: ./... - - - name: Run GoVulnCheck - uses: golang/govulncheck-action@v1 - with: - go-version-input: ${{ matrix.go }} - go-package: ./... - - name: Run Tests run: go test -race -cover -coverprofile=coverage -covermode=atomic -v ./... - name: Upload coverage to Codecov uses: codecov/codecov-action@v3 with: - files: ./coverage \ No newline at end of file + files: ./coverage diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml new file mode 100644 index 00000000..a3eb74b3 --- /dev/null +++ b/.github/workflows/verify.yml @@ -0,0 +1,32 @@ +name: Verify +on: + push: + branches: + - main + pull_request: + branches: + - main +permissions: + contents: read +jobs: + lint: + strategy: + matrix: + go: ['1.20','1.21'] + fail-fast: true + runs-on: ubuntu-latest + steps: + - name: Checkout Code + uses: actions/checkout@v3 + + - name: Setup Go ${{ matrix.go }} + uses: actions/setup-go@v4 + with: + go-version: ${{ matrix.go }} + cache: false + + - name: Run GolangCI-Lint + uses: golangci/golangci-lint-action@v3 + with: + version: v1.53 + args: --timeout=5m diff --git a/go.mod b/go.mod index 7bcfa026..7c6f375b 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,3 @@ module github.com/gorilla/mux -go 1.19 +go 1.20