From 5b09b25dcb2de07fe9bb2abb88d6903e2d66654e Mon Sep 17 00:00:00 2001 From: Dmitry Verkhoturov Date: Mon, 21 Apr 2025 19:33:26 +0100 Subject: [PATCH] get rid of chi and tollbooth --- .github/workflows/ci.yml | 2 +- backend/go.mod | 12 +- backend/go.sum | 21 +- backend/main.go | 1 - backend/rest/server.go | 91 +- backend/rest/server_test.go | 2 +- .../vendor/github.com/ajg/form/.travis.yml | 25 - backend/vendor/github.com/ajg/form/LICENSE | 27 - backend/vendor/github.com/ajg/form/README.md | 247 ----- backend/vendor/github.com/ajg/form/TODO.md | 4 - backend/vendor/github.com/ajg/form/decode.go | 370 -------- backend/vendor/github.com/ajg/form/encode.go | 388 -------- backend/vendor/github.com/ajg/form/form.go | 14 - backend/vendor/github.com/ajg/form/node.go | 152 --- .../vendor/github.com/ajg/form/pre-commit.sh | 18 - .../github.com/didip/tollbooth/v7/.gitignore | 3 - .../didip/tollbooth/v7/.golangci.yml | 33 - .../github.com/didip/tollbooth/v7/LICENSE | 21 - .../github.com/didip/tollbooth/v7/README.md | 186 ---- .../didip/tollbooth/v7/errors/errors.go | 15 - .../didip/tollbooth/v7/internal/time/AUTHORS | 3 - .../tollbooth/v7/internal/time/CONTRIBUTORS | 3 - .../didip/tollbooth/v7/internal/time/LICENSE | 27 - .../didip/tollbooth/v7/internal/time/PATENTS | 22 - .../tollbooth/v7/internal/time/rate/rate.go | 396 -------- .../didip/tollbooth/v7/libstring/libstring.go | 101 -- .../didip/tollbooth/v7/limiter/limiter.go | 610 ------------ .../tollbooth/v7/limiter/limiter_options.go | 14 - .../didip/tollbooth/v7/tollbooth.go | 349 ------- .../github.com/didip/tollbooth_chi/README.md | 33 - .../didip/tollbooth_chi/tollbooth_chi.go | 45 - .../github.com/go-chi/chi/v5/.gitignore | 3 - .../github.com/go-chi/chi/v5/CHANGELOG.md | 341 ------- .../github.com/go-chi/chi/v5/CONTRIBUTING.md | 31 - .../vendor/github.com/go-chi/chi/v5/LICENSE | 20 - .../vendor/github.com/go-chi/chi/v5/Makefile | 22 - .../vendor/github.com/go-chi/chi/v5/README.md | 503 ---------- .../github.com/go-chi/chi/v5/SECURITY.md | 5 - .../vendor/github.com/go-chi/chi/v5/chain.go | 49 - .../vendor/github.com/go-chi/chi/v5/chi.go | 138 --- .../github.com/go-chi/chi/v5/context.go | 165 ---- .../go-chi/chi/v5/middleware/basic_auth.go | 33 - .../go-chi/chi/v5/middleware/clean_path.go | 28 - .../go-chi/chi/v5/middleware/compress.go | 398 -------- .../chi/v5/middleware/content_charset.go | 51 - .../chi/v5/middleware/content_encoding.go | 34 - .../go-chi/chi/v5/middleware/content_type.go | 45 - .../go-chi/chi/v5/middleware/get_head.go | 39 - .../go-chi/chi/v5/middleware/heartbeat.go | 26 - .../go-chi/chi/v5/middleware/logger.go | 172 ---- .../go-chi/chi/v5/middleware/maybe.go | 18 - .../go-chi/chi/v5/middleware/middleware.go | 23 - .../go-chi/chi/v5/middleware/nocache.go | 59 -- .../go-chi/chi/v5/middleware/page_route.go | 20 - .../go-chi/chi/v5/middleware/path_rewrite.go | 16 - .../go-chi/chi/v5/middleware/profiler.go | 46 - .../go-chi/chi/v5/middleware/realip.go | 60 -- .../go-chi/chi/v5/middleware/recoverer.go | 203 ---- .../go-chi/chi/v5/middleware/request_id.go | 96 -- .../go-chi/chi/v5/middleware/request_size.go | 18 - .../go-chi/chi/v5/middleware/route_headers.go | 151 --- .../go-chi/chi/v5/middleware/strip.go | 70 -- .../go-chi/chi/v5/middleware/sunset.go | 25 - .../chi/v5/middleware/supress_notfound.go | 27 - .../go-chi/chi/v5/middleware/terminal.go | 63 -- .../go-chi/chi/v5/middleware/throttle.go | 140 --- .../go-chi/chi/v5/middleware/timeout.go | 48 - .../go-chi/chi/v5/middleware/url_format.go | 75 -- .../go-chi/chi/v5/middleware/value.go | 17 - .../go-chi/chi/v5/middleware/wrap_writer.go | 242 ----- .../vendor/github.com/go-chi/chi/v5/mux.go | 529 ----------- .../github.com/go-chi/chi/v5/path_value.go | 20 - .../go-chi/chi/v5/path_value_fallback.go | 19 - .../vendor/github.com/go-chi/chi/v5/tree.go | 892 ------------------ .../github.com/go-chi/render/.gitignore | 1 - .../vendor/github.com/go-chi/render/LICENSE | 20 - .../vendor/github.com/go-chi/render/README.md | 27 - .../github.com/go-chi/render/content_type.go | 84 -- .../github.com/go-chi/render/decoder.go | 57 -- .../vendor/github.com/go-chi/render/render.go | 143 --- .../github.com/go-chi/render/responder.go | 234 ----- .../go-pkgz/expirable-cache/v3/cache.go | 371 -------- .../go-pkgz/expirable-cache/v3/options.go | 36 - .../go-pkgz/routegroup/.golangci.yml | 59 ++ .../v3 => routegroup}/LICENSE | 3 +- .../github.com/go-pkgz/routegroup/README.md | 263 ++++++ .../github.com/go-pkgz/routegroup/group.go | 188 ++++ backend/vendor/modules.txt | 26 +- 88 files changed, 559 insertions(+), 9168 deletions(-) delete mode 100644 backend/vendor/github.com/ajg/form/.travis.yml delete mode 100644 backend/vendor/github.com/ajg/form/LICENSE delete mode 100644 backend/vendor/github.com/ajg/form/README.md delete mode 100644 backend/vendor/github.com/ajg/form/TODO.md delete mode 100644 backend/vendor/github.com/ajg/form/decode.go delete mode 100644 backend/vendor/github.com/ajg/form/encode.go delete mode 100644 backend/vendor/github.com/ajg/form/form.go delete mode 100644 backend/vendor/github.com/ajg/form/node.go delete mode 100644 backend/vendor/github.com/ajg/form/pre-commit.sh delete mode 100644 backend/vendor/github.com/didip/tollbooth/v7/.gitignore delete mode 100644 backend/vendor/github.com/didip/tollbooth/v7/.golangci.yml delete mode 100644 backend/vendor/github.com/didip/tollbooth/v7/LICENSE delete mode 100644 backend/vendor/github.com/didip/tollbooth/v7/README.md delete mode 100644 backend/vendor/github.com/didip/tollbooth/v7/errors/errors.go delete mode 100644 backend/vendor/github.com/didip/tollbooth/v7/internal/time/AUTHORS delete mode 100644 backend/vendor/github.com/didip/tollbooth/v7/internal/time/CONTRIBUTORS delete mode 100644 backend/vendor/github.com/didip/tollbooth/v7/internal/time/LICENSE delete mode 100644 backend/vendor/github.com/didip/tollbooth/v7/internal/time/PATENTS delete mode 100644 backend/vendor/github.com/didip/tollbooth/v7/internal/time/rate/rate.go delete mode 100644 backend/vendor/github.com/didip/tollbooth/v7/libstring/libstring.go delete mode 100644 backend/vendor/github.com/didip/tollbooth/v7/limiter/limiter.go delete mode 100644 backend/vendor/github.com/didip/tollbooth/v7/limiter/limiter_options.go delete mode 100644 backend/vendor/github.com/didip/tollbooth/v7/tollbooth.go delete mode 100644 backend/vendor/github.com/didip/tollbooth_chi/README.md delete mode 100644 backend/vendor/github.com/didip/tollbooth_chi/tollbooth_chi.go delete mode 100644 backend/vendor/github.com/go-chi/chi/v5/.gitignore delete mode 100644 backend/vendor/github.com/go-chi/chi/v5/CHANGELOG.md delete mode 100644 backend/vendor/github.com/go-chi/chi/v5/CONTRIBUTING.md delete mode 100644 backend/vendor/github.com/go-chi/chi/v5/LICENSE delete mode 100644 backend/vendor/github.com/go-chi/chi/v5/Makefile delete mode 100644 backend/vendor/github.com/go-chi/chi/v5/README.md delete mode 100644 backend/vendor/github.com/go-chi/chi/v5/SECURITY.md delete mode 100644 backend/vendor/github.com/go-chi/chi/v5/chain.go delete mode 100644 backend/vendor/github.com/go-chi/chi/v5/chi.go delete mode 100644 backend/vendor/github.com/go-chi/chi/v5/context.go delete mode 100644 backend/vendor/github.com/go-chi/chi/v5/middleware/basic_auth.go delete mode 100644 backend/vendor/github.com/go-chi/chi/v5/middleware/clean_path.go delete mode 100644 backend/vendor/github.com/go-chi/chi/v5/middleware/compress.go delete mode 100644 backend/vendor/github.com/go-chi/chi/v5/middleware/content_charset.go delete mode 100644 backend/vendor/github.com/go-chi/chi/v5/middleware/content_encoding.go delete mode 100644 backend/vendor/github.com/go-chi/chi/v5/middleware/content_type.go delete mode 100644 backend/vendor/github.com/go-chi/chi/v5/middleware/get_head.go delete mode 100644 backend/vendor/github.com/go-chi/chi/v5/middleware/heartbeat.go delete mode 100644 backend/vendor/github.com/go-chi/chi/v5/middleware/logger.go delete mode 100644 backend/vendor/github.com/go-chi/chi/v5/middleware/maybe.go delete mode 100644 backend/vendor/github.com/go-chi/chi/v5/middleware/middleware.go delete mode 100644 backend/vendor/github.com/go-chi/chi/v5/middleware/nocache.go delete mode 100644 backend/vendor/github.com/go-chi/chi/v5/middleware/page_route.go delete mode 100644 backend/vendor/github.com/go-chi/chi/v5/middleware/path_rewrite.go delete mode 100644 backend/vendor/github.com/go-chi/chi/v5/middleware/profiler.go delete mode 100644 backend/vendor/github.com/go-chi/chi/v5/middleware/realip.go delete mode 100644 backend/vendor/github.com/go-chi/chi/v5/middleware/recoverer.go delete mode 100644 backend/vendor/github.com/go-chi/chi/v5/middleware/request_id.go delete mode 100644 backend/vendor/github.com/go-chi/chi/v5/middleware/request_size.go delete mode 100644 backend/vendor/github.com/go-chi/chi/v5/middleware/route_headers.go delete mode 100644 backend/vendor/github.com/go-chi/chi/v5/middleware/strip.go delete mode 100644 backend/vendor/github.com/go-chi/chi/v5/middleware/sunset.go delete mode 100644 backend/vendor/github.com/go-chi/chi/v5/middleware/supress_notfound.go delete mode 100644 backend/vendor/github.com/go-chi/chi/v5/middleware/terminal.go delete mode 100644 backend/vendor/github.com/go-chi/chi/v5/middleware/throttle.go delete mode 100644 backend/vendor/github.com/go-chi/chi/v5/middleware/timeout.go delete mode 100644 backend/vendor/github.com/go-chi/chi/v5/middleware/url_format.go delete mode 100644 backend/vendor/github.com/go-chi/chi/v5/middleware/value.go delete mode 100644 backend/vendor/github.com/go-chi/chi/v5/middleware/wrap_writer.go delete mode 100644 backend/vendor/github.com/go-chi/chi/v5/mux.go delete mode 100644 backend/vendor/github.com/go-chi/chi/v5/path_value.go delete mode 100644 backend/vendor/github.com/go-chi/chi/v5/path_value_fallback.go delete mode 100644 backend/vendor/github.com/go-chi/chi/v5/tree.go delete mode 100644 backend/vendor/github.com/go-chi/render/.gitignore delete mode 100644 backend/vendor/github.com/go-chi/render/LICENSE delete mode 100644 backend/vendor/github.com/go-chi/render/README.md delete mode 100644 backend/vendor/github.com/go-chi/render/content_type.go delete mode 100644 backend/vendor/github.com/go-chi/render/decoder.go delete mode 100644 backend/vendor/github.com/go-chi/render/render.go delete mode 100644 backend/vendor/github.com/go-chi/render/responder.go delete mode 100644 backend/vendor/github.com/go-pkgz/expirable-cache/v3/cache.go delete mode 100644 backend/vendor/github.com/go-pkgz/expirable-cache/v3/options.go create mode 100644 backend/vendor/github.com/go-pkgz/routegroup/.golangci.yml rename backend/vendor/github.com/go-pkgz/{expirable-cache/v3 => routegroup}/LICENSE (94%) create mode 100644 backend/vendor/github.com/go-pkgz/routegroup/README.md create mode 100644 backend/vendor/github.com/go-pkgz/routegroup/group.go diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 642b494f..317a30a0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,7 +15,7 @@ jobs: uses: actions/setup-go@v5 with: cache-dependency-path: backend/ - go-version: "1.22" + go-version: "1.23" id: go - name: launch mongodb diff --git a/backend/go.mod b/backend/go.mod index 69c0a2f4..c166c080 100644 --- a/backend/go.mod +++ b/backend/go.mod @@ -1,16 +1,14 @@ module github.com/ukeeper/ukeeper-redabilty/backend -go 1.22 -toolchain go1.24.1 +go 1.23.0 + +toolchain go1.24.2 require ( github.com/PuerkitoBio/goquery v1.10.2 - github.com/didip/tollbooth/v7 v7.0.2 - github.com/didip/tollbooth_chi v0.0.0-20220719025231-d662a7f6928f - github.com/go-chi/chi/v5 v5.2.1 - github.com/go-chi/render v1.0.3 github.com/go-pkgz/lgr v0.12.0 github.com/go-pkgz/rest v1.20.3 + github.com/go-pkgz/routegroup v1.3.1 github.com/jessevdk/go-flags v1.6.1 github.com/kennygrant/sanitize v1.2.4 github.com/mauidude/go-readability v0.0.0-20220221173116-a9b3620098b7 @@ -20,10 +18,8 @@ require ( ) require ( - github.com/ajg/form v1.5.1 // indirect github.com/andybalholm/cascadia v1.3.3 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/go-pkgz/expirable-cache/v3 v3.0.0 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/klauspost/compress v1.17.8 // indirect github.com/montanaflynn/stats v0.7.1 // indirect diff --git a/backend/go.sum b/backend/go.sum index c26d3397..6c0a23bf 100644 --- a/backend/go.sum +++ b/backend/go.sum @@ -17,8 +17,6 @@ github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAE github.com/PuerkitoBio/goquery v1.6.0/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc= github.com/PuerkitoBio/goquery v1.10.2 h1:7fh2BdHcG6VFZsK7toXBT/Bh1z5Wmy8Q9MV9HqT2AM8= github.com/PuerkitoBio/goquery v1.10.2/go.mod h1:0guWGjcLu9AYC7C1GHnpysHy056u9aEkUHwhdnePMCU= -github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= -github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y= @@ -45,29 +43,19 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/didip/tollbooth/v7 v7.0.0/go.mod h1:VZhDSGl5bDSPj4wPsih3PFa4Uh9Ghv8hgacaTm5PRT4= -github.com/didip/tollbooth/v7 v7.0.2 h1:WYEfusYI6g64cN0qbZgekDrYfuYBZjUZd5+RlWi69p4= -github.com/didip/tollbooth/v7 v7.0.2/go.mod h1:RtRYfEmFGX70+ike5kSndSvLtQ3+F2EAmTI4Un/VXNc= -github.com/didip/tollbooth_chi v0.0.0-20220719025231-d662a7f6928f h1:jtKwihcLmUC9BAhoJ9adCUqdSSZcOdH2KL7mPTUm2aw= -github.com/didip/tollbooth_chi v0.0.0-20220719025231-d662a7f6928f/go.mod h1:q9C80dnsuVRP2dAskjnXRNWdUJqtGgwG9wNrzt0019s= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-chi/chi/v5 v5.2.1 h1:KOIHODQj58PmL80G2Eak4WdvUzjSJSm0vG72crDCqb8= -github.com/go-chi/chi/v5 v5.2.1/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops= -github.com/go-chi/render v1.0.3 h1:AsXqd2a1/INaIfUSKq3G5uA8weYx20FOsM7uSoCyyt4= -github.com/go-chi/render v1.0.3/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-pkgz/expirable-cache v0.1.0/go.mod h1:GTrEl0X+q0mPNqN6dtcQXksACnzCBQ5k/k1SwXJsZKs= -github.com/go-pkgz/expirable-cache/v3 v3.0.0 h1:u3/gcu3sabLYiTCevoRKv+WzjIn5oo7P8XtiXBeRDLw= -github.com/go-pkgz/expirable-cache/v3 v3.0.0/go.mod h1:2OQiDyEGQalYecLWmXprm3maPXeVb5/6/X7yRPYTzec= github.com/go-pkgz/lgr v0.12.0 h1:uoSCLdiMocZDa+L66DavHG5UIkOJvWKOVqt6sNQllw0= github.com/go-pkgz/lgr v0.12.0/go.mod h1:A4AxjOthFVFK6jRnVYMeusno5SeDAxcLVHd0kI/lN/Y= github.com/go-pkgz/rest v1.20.3 h1:oGGfM8XTnvwek29q1OAhcI1nkKKOpurRFApBiYH44Fk= github.com/go-pkgz/rest v1.20.3/go.mod h1:NC2xNN/y1rIs0PY13FowKoH8rk9RhJNJ0tTbkBg8Yks= +github.com/go-pkgz/routegroup v1.3.1 h1:XAVWskX8Iup6HoQD9zv+gJx4DOJC2DSkKBHCMeeW8/s= +github.com/go-pkgz/routegroup v1.3.1/go.mod h1:kDDPDRLRiRY1vnENrZJw1jQAzQX7fvsbsHGRQFNQfKc= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= @@ -112,10 +100,7 @@ github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= -github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= @@ -201,7 +186,6 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= @@ -409,7 +393,6 @@ gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bl gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/backend/main.go b/backend/main.go index b4a38109..c4195d8a 100644 --- a/backend/main.go +++ b/backend/main.go @@ -34,7 +34,6 @@ func main() { if _, err := flags.Parse(&opts); err != nil { os.Exit(1) } - // Setup logging var options []log.Option if opts.Debug { options = []log.Option{log.Debug, log.CallerFile} diff --git a/backend/rest/server.go b/backend/rest/server.go index 70b29e8b..b3b19748 100644 --- a/backend/rest/server.go +++ b/backend/rest/server.go @@ -12,14 +12,10 @@ import ( "strings" "time" - "github.com/didip/tollbooth/v7" - "github.com/didip/tollbooth_chi" - "github.com/go-chi/chi/v5" - "github.com/go-chi/chi/v5/middleware" - "github.com/go-chi/render" log "github.com/go-pkgz/lgr" "github.com/go-pkgz/rest" "github.com/go-pkgz/rest/logger" + "github.com/go-pkgz/routegroup" "go.mongodb.org/mongo-driver/bson/primitive" "github.com/ukeeper/ukeeper-redabilty/backend/datastore" @@ -67,42 +63,36 @@ func (s *Server) Run(ctx context.Context, address string, port int, frontendDir log.Printf("[WARN] http server terminated, %s", httpServer.ListenAndServe()) } -func (s *Server) routes(frontendDir string) chi.Router { - router := chi.NewRouter() +func (s *Server) routes(frontendDir string) http.Handler { + router := routegroup.New(http.NewServeMux()) - router.Use(middleware.RequestID, middleware.RealIP, rest.Recoverer(log.Default())) - router.Use(middleware.Throttle(1000), middleware.Timeout(60*time.Second)) + router.Use(rest.Recoverer(log.Default())) + router.Use(rest.RealIP) router.Use(rest.AppInfo("ureadability", "Umputun", s.Version), rest.Ping) - router.Use(tollbooth_chi.LimitHandler(tollbooth.NewLimiter(50, nil))) - + router.Use(rest.Throttle(50)) router.Use(logger.New(logger.Log(log.Default()), logger.WithBody, logger.Prefix("[INFO]")).Handler) - router.Route("/api", func(r chi.Router) { - r.Get("/content/v1/parser", s.extractArticleEmulateReadability) - r.Post("/extract", s.extractArticle) - r.Post("/auth", s.authFake) - - r.Group(func(protected chi.Router) { - protected.Use(basicAuth("ureadability", s.Credentials)) - protected.Post("/rule", s.saveRule) - protected.Post("/toggle-rule/{id}", s.toggleRule) - protected.Post("/preview", s.handlePreview) + router.Route(func(api *routegroup.Bundle) { + api.Mount("/api").Route(func(api *routegroup.Bundle) { + api.HandleFunc("GET /content/v1/parser", s.extractArticleEmulateReadability) + api.HandleFunc("POST /extract", s.extractArticle) + api.HandleFunc("POST /auth", s.authFake) + + // add protected group with its own set of middlewares + protectedGroup := api.Group() + protectedGroup.Use(basicAuth("ureadability", s.Credentials)) + protectedGroup.HandleFunc("POST /rule", s.saveRule) + protectedGroup.HandleFunc("POST /toggle-rule/{id}", s.toggleRule) + protectedGroup.HandleFunc("POST /preview", s.handlePreview) }) }) - router.Get("/", s.handleIndex) - router.Get("/add/", s.handleAdd) - router.Get("/edit/{id}", s.handleEdit) + router.HandleFunc("GET /", s.handleIndex) + router.HandleFunc("GET /add/", s.handleAdd) + router.HandleFunc("GET /edit/{id}", s.handleEdit) _ = os.Mkdir(filepath.Join(frontendDir, "static"), 0o700) - fs, err := rest.NewFileServer("/", filepath.Join(frontendDir, "static"), rest.FsOptSPA) - if err != nil { - log.Printf("[ERROR] unable to create file server, %v", err) - return nil - } - router.Get("/*", func(w http.ResponseWriter, r *http.Request) { - fs.ServeHTTP(w, r) - }) + router.HandleFiles("/", http.Dir(filepath.Join(frontendDir, "static"))) return router } @@ -140,7 +130,7 @@ func (s *Server) handleAdd(w http.ResponseWriter, _ *http.Request) { } func (s *Server) handleEdit(w http.ResponseWriter, r *http.Request) { - id := getBid(chi.URLParam(r, "id")) + id := getBid(r.PathValue("id")) rule, found := s.Readability.Rules.GetByID(r.Context(), id) if !found { http.Error(w, "Rule not found", http.StatusNotFound) @@ -163,26 +153,23 @@ func (s *Server) handleEdit(w http.ResponseWriter, r *http.Request) { func (s *Server) extractArticle(w http.ResponseWriter, r *http.Request) { artRequest := extractor.Response{} - if err := render.DecodeJSON(r.Body, &artRequest); err != nil { - render.Status(r, http.StatusInternalServerError) - render.JSON(w, r, JSON{"error": err.Error()}) + if err := rest.DecodeJSON(r, &artRequest); err != nil { + rest.SendErrorJSON(w, r, log.Default(), http.StatusInternalServerError, err, "can't parse request") return } if artRequest.URL == "" { - render.Status(r, http.StatusBadRequest) - render.JSON(w, r, JSON{"error": "url parameter is required"}) + rest.SendErrorJSON(w, r, log.Default(), http.StatusBadRequest, nil, "url parameter is required") return } res, err := s.Readability.Extract(r.Context(), artRequest.URL) if err != nil { - render.Status(r, http.StatusBadRequest) - render.JSON(w, r, JSON{"error": err.Error()}) + rest.SendErrorJSON(w, r, log.Default(), http.StatusBadRequest, err, "can't extract content") return } - render.JSON(w, r, &res) + rest.RenderJSON(w, &res) } // extractArticleEmulateReadability emulates readability API parse - https://www.readability.com/api/content/v1/parser?token=%s&url=%s @@ -190,32 +177,28 @@ func (s *Server) extractArticle(w http.ResponseWriter, r *http.Request) { func (s *Server) extractArticleEmulateReadability(w http.ResponseWriter, r *http.Request) { token := r.URL.Query().Get("token") if s.Token != "" && token == "" { - render.Status(r, http.StatusExpectationFailed) - render.JSON(w, r, JSON{"error": "no token passed"}) + rest.SendErrorJSON(w, r, log.Default(), http.StatusExpectationFailed, nil, "no token passed") return } if s.Token != "" && s.Token != token { - render.Status(r, http.StatusUnauthorized) - render.JSON(w, r, JSON{"error": "wrong token passed"}) + rest.SendErrorJSON(w, r, log.Default(), http.StatusUnauthorized, nil, "wrong token passed") return } extractURL := r.URL.Query().Get("url") if extractURL == "" { - render.Status(r, http.StatusExpectationFailed) - render.JSON(w, r, JSON{"error": "no url passed"}) + rest.SendErrorJSON(w, r, log.Default(), http.StatusExpectationFailed, nil, "no url passed") return } res, err := s.Readability.Extract(r.Context(), extractURL) if err != nil { - render.Status(r, http.StatusBadRequest) - render.JSON(w, r, JSON{"error": err.Error()}) + rest.SendErrorJSON(w, r, log.Default(), http.StatusBadRequest, err, "can't extract content") return } - render.JSON(w, r, &res) + rest.RenderJSON(w, &res) } // generates previews for the provided test URLs @@ -322,11 +305,11 @@ func (s *Server) saveRule(w http.ResponseWriter, r *http.Request) { } w.Header().Set("HX-Redirect", "/") - render.JSON(w, r, &srule) + rest.RenderJSON(w, &srule) } func (s *Server) toggleRule(w http.ResponseWriter, r *http.Request) { - id := getBid(chi.URLParam(r, "id")) + id := getBid(r.PathValue("id")) rule, found := s.Readability.Rules.GetByID(r.Context(), id) if !found { log.Printf("[WARN] rule not found for id: %s", id.Hex()) @@ -356,9 +339,9 @@ func (s *Server) toggleRule(w http.ResponseWriter, r *http.Request) { } // authFake just a dummy post request used for external check for protected resource -func (s *Server) authFake(w http.ResponseWriter, r *http.Request) { +func (s *Server) authFake(w http.ResponseWriter, _ *http.Request) { t := time.Now() - render.JSON(w, r, JSON{"pong": t.Format("20060102150405")}) + rest.RenderJSON(w, JSON{"pong": t.Format("20060102150405")}) } func getBid(id string) primitive.ObjectID { diff --git a/backend/rest/server_test.go b/backend/rest/server_test.go index 3bbc353d..f98b069d 100644 --- a/backend/rest/server_test.go +++ b/backend/rest/server_test.go @@ -312,7 +312,7 @@ func TestServer_RuleUnhappyFlow(t *testing.T) { // get supposed to fail _, code := get(t, ts.URL+"/api/rule") - assert.Equal(t, http.StatusMethodNotAllowed, code) + assert.Equal(t, http.StatusNotFound, code) // get rule by non-existent ID _, code = get(t, ts.URL+"/api/rule/nonexistent") diff --git a/backend/vendor/github.com/ajg/form/.travis.yml b/backend/vendor/github.com/ajg/form/.travis.yml deleted file mode 100644 index 14608c76..00000000 --- a/backend/vendor/github.com/ajg/form/.travis.yml +++ /dev/null @@ -1,25 +0,0 @@ -## Copyright 2014 Alvaro J. Genial. All rights reserved. -## Use of this source code is governed by a BSD-style -## license that can be found in the LICENSE file. - -language: go - -go: - - tip - - 1.6 - - 1.5 - - 1.4 - - 1.3 - # 1.2 - -before_install: - # - go get -v golang.org/x/tools/cmd/cover - # - go get -v golang.org/x/tools/cmd/vet - # - go get -v golang.org/x/lint/golint - - export PATH=$PATH:/home/travis/gopath/bin - -script: - - go build -v ./... - - go test -v -cover ./... - - go vet ./... - # - golint . diff --git a/backend/vendor/github.com/ajg/form/LICENSE b/backend/vendor/github.com/ajg/form/LICENSE deleted file mode 100644 index 9190b165..00000000 --- a/backend/vendor/github.com/ajg/form/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2014 Alvaro J. Genial. All rights reserved. - -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. - -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. diff --git a/backend/vendor/github.com/ajg/form/README.md b/backend/vendor/github.com/ajg/form/README.md deleted file mode 100644 index ad99be4b..00000000 --- a/backend/vendor/github.com/ajg/form/README.md +++ /dev/null @@ -1,247 +0,0 @@ -form -==== - -A Form Encoding & Decoding Package for Go, written by [Alvaro J. Genial](http://alva.ro). - -[![Build Status](https://travis-ci.org/ajg/form.png?branch=master)](https://travis-ci.org/ajg/form) -[![GoDoc](https://godoc.org/github.com/ajg/form?status.png)](https://godoc.org/github.com/ajg/form) - -Synopsis --------- - -This library is designed to allow seamless, high-fidelity encoding and decoding of arbitrary data in `application/x-www-form-urlencoded` format and as [`url.Values`](http://golang.org/pkg/net/url/#Values). It is intended to be useful primarily in dealing with web forms and URI query strings, both of which natively employ said format. - -Unsurprisingly, `form` is modeled after other Go [`encoding`](http://golang.org/pkg/encoding/) packages, in particular [`encoding/json`](http://golang.org/pkg/encoding/json/), and follows the same conventions (see below for more.) It aims to automatically handle any kind of concrete Go [data value](#values) (i.e., not functions, channels, etc.) while providing mechanisms for custom behavior. - -Status ------- - -The implementation is in usable shape and is fairly well tested with its accompanying test suite. The API is unlikely to change much, but still may. Lastly, the code has not yet undergone a security review to ensure it is free of vulnerabilities. Please file an issue or send a pull request for fixes & improvements. - -Dependencies ------------- - -The only requirement is [Go 1.2](http://golang.org/doc/go1.2) or later. - -Usage ------ - -```go -import "github.com/ajg/form" -// or: "gopkg.in/ajg/form.v1" -``` - -Given a type like the following... - -```go -type User struct { - Name string `form:"name"` - Email string `form:"email"` - Joined time.Time `form:"joined,omitempty"` - Posts []int `form:"posts"` - Preferences map[string]string `form:"prefs"` - Avatar []byte `form:"avatar"` - PasswordHash int64 `form:"-"` -} -``` - -...it is easy to encode data of that type... - - -```go -func PostUser(url string, u User) error { - var c http.Client - _, err := c.PostForm(url, form.EncodeToValues(u)) - return err -} -``` - -...as well as decode it... - - -```go -func Handler(w http.ResponseWriter, r *http.Request) { - var u User - - d := form.NewDecoder(r.Body) - if err := d.Decode(&u); err != nil { - http.Error(w, "Form could not be decoded", http.StatusBadRequest) - return - } - - fmt.Fprintf(w, "Decoded: %#v", u) -} -``` - -...without having to do any grunt work. - -Field Tags ----------- - -Like other encoding packages, `form` supports the following options for fields: - - - `` `form:"-"` ``: Causes the field to be ignored during encoding and decoding. - - `` `form:""` ``: Overrides the field's name; useful especially when dealing with external identifiers in camelCase, as are commonly found on the web. - - `` `form:",omitempty"` ``: Elides the field during encoding if it is empty (typically meaning equal to the type's zero value.) - - `` `form:",omitempty"` ``: The way to combine the two options above. - -Values ------- - -### Simple Values - -Values of the following types are all considered simple: - - - `bool` - - `int`, `int8`, `int16`, `int32`, `int64`, `rune` - - `uint`, `uint8`, `uint16`, `uint32`, `uint64`, `byte` - - `float32`, `float64` - - `complex64`, `complex128` - - `string` - - `[]byte` (see note) - - [`time.Time`](http://golang.org/pkg/time/#Time) - - [`url.URL`](http://golang.org/pkg/net/url/#URL) - - An alias of any of the above - - A pointer to any of the above - -### Composite Values - -A composite value is one that can contain other values. Values of the following kinds... - - - Maps - - Slices; except `[]byte` (see note) - - Structs; except [`time.Time`](http://golang.org/pkg/time/#Time) and [`url.URL`](http://golang.org/pkg/net/url/#URL) - - Arrays - - An alias of any of the above - - A pointer to any of the above - -...are considered composites in general, unless they implement custom marshaling/unmarshaling. Composite values are encoded as a flat mapping of paths to values, where the paths are constructed by joining the parent and child paths with a period (`.`). - -(Note: a byte slice is treated as a `string` by default because it's more efficient, but can also be decoded as a slice—i.e., with indexes.) - -### Untyped Values - -While encouraged, it is not necessary to define a type (e.g. a `struct`) in order to use `form`, since it is able to encode and decode untyped data generically using the following rules: - - - Simple values will be treated as a `string`. - - Composite values will be treated as a `map[string]interface{}`, itself able to contain nested values (both scalar and compound) ad infinitum. - - However, if there is a value (of any supported type) already present in a map for a given key, then it will be used when possible, rather than being replaced with a generic value as specified above; this makes it possible to handle partially typed, dynamic or schema-less values. - -### Zero Values - -By default, and without custom marshaling, zero values (also known as empty/default values) are encoded as the empty string. To disable this behavior, meaning to keep zero values in their literal form (e.g. `0` for integral types), `Encoder` offers a `KeepZeros` setter method, which will do just that when set to `true`. - -### Unsupported Values - -Values of the following kinds aren't supported and, if present, must be ignored. - - - Channel - - Function - - Unsafe pointer - - An alias of any of the above - - A pointer to any of the above - -Custom Marshaling ------------------ - -There is a default (generally lossless) marshaling & unmarshaling scheme for any concrete data value in Go, which is good enough in most cases. However, it is possible to override it and use a custom scheme. For instance, a "binary" field could be marshaled more efficiently using [base64](http://golang.org/pkg/encoding/base64/) to prevent it from being percent-escaped during serialization to `application/x-www-form-urlencoded` format. - -Because `form` provides support for [`encoding.TextMarshaler`](http://golang.org/pkg/encoding/#TextMarshaler) and [`encoding.TextUnmarshaler`](http://golang.org/pkg/encoding/#TextUnmarshaler) it is easy to do that; for instance, like this: - -```go -import "encoding" - -type Binary []byte - -var ( - _ encoding.TextMarshaler = &Binary{} - _ encoding.TextUnmarshaler = &Binary{} -) - -func (b Binary) MarshalText() ([]byte, error) { - return []byte(base64.URLEncoding.EncodeToString([]byte(b))), nil -} - -func (b *Binary) UnmarshalText(text []byte) error { - bs, err := base64.URLEncoding.DecodeString(string(text)) - if err == nil { - *b = Binary(bs) - } - return err -} -``` - -Now any value with type `Binary` will automatically be encoded using the [URL](http://golang.org/pkg/encoding/base64/#URLEncoding) variant of base64. It is left as an exercise to the reader to improve upon this scheme by eliminating the need for padding (which, besides being superfluous, uses `=`, a character that will end up percent-escaped.) - -Keys ----- - -In theory any value can be a key as long as it has a string representation. However, by default, periods have special meaning to `form`, and thus, under the hood (i.e. in encoded form) they are transparently escaped using a preceding backslash (`\`). Backslashes within keys, themselves, are also escaped in this manner (e.g. as `\\`) in order to permit representing `\.` itself (as `\\\.`). - -(Note: it is normally unnecessary to deal with this issue unless keys are being constructed manually—e.g. literally embedded in HTML or in a URI.) - -The default delimiter and escape characters used for encoding and decoding composite keys can be changed using the `DelimitWith` and `EscapeWith` setter methods of `Encoder` and `Decoder`, respectively. For example... - -```go -package main - -import ( - "os" - - "github.com/ajg/form" -) - -func main() { - type B struct { - Qux string `form:"qux"` - } - type A struct { - FooBar B `form:"foo.bar"` - } - a := A{FooBar: B{"XYZ"}} - os.Stdout.WriteString("Default: ") - form.NewEncoder(os.Stdout).Encode(a) - os.Stdout.WriteString("\nCustom: ") - form.NewEncoder(os.Stdout).DelimitWith('/').Encode(a) - os.Stdout.WriteString("\n") -} - -``` - -...will produce... - -``` -Default: foo%5C.bar.qux=XYZ -Custom: foo.bar%2Fqux=XYZ -``` - -(`%5C` and `%2F` represent `\` and `/`, respectively.) - -Limitations ------------ - - - Circular (self-referential) values are untested. - -Future Work ------------ - -The following items would be nice to have in the future—though they are not being worked on yet: - - - An option to treat all values as if they had been tagged with `omitempty`. - - An option to automatically treat all field names in `camelCase` or `underscore_case`. - - Built-in support for the types in [`math/big`](http://golang.org/pkg/math/big/). - - Built-in support for the types in [`image/color`](http://golang.org/pkg/image/color/). - - Improve encoding/decoding by reading/writing directly from/to the `io.Reader`/`io.Writer` when possible, rather than going through an intermediate representation (i.e. `node`) which requires more memory. - -(Feel free to implement any of these and then send a pull request.) - -Related Work ------------- - - - Package [gorilla/schema](https://github.com/gorilla/schema), which only implements decoding. - - Package [google/go-querystring](https://github.com/google/go-querystring), which only implements encoding. - -License -------- - -This library is distributed under a BSD-style [LICENSE](./LICENSE). diff --git a/backend/vendor/github.com/ajg/form/TODO.md b/backend/vendor/github.com/ajg/form/TODO.md deleted file mode 100644 index d3447279..00000000 --- a/backend/vendor/github.com/ajg/form/TODO.md +++ /dev/null @@ -1,4 +0,0 @@ -TODO -==== - - - Document IgnoreCase and IgnoreUnknownKeys in README. diff --git a/backend/vendor/github.com/ajg/form/decode.go b/backend/vendor/github.com/ajg/form/decode.go deleted file mode 100644 index dd8bd4f2..00000000 --- a/backend/vendor/github.com/ajg/form/decode.go +++ /dev/null @@ -1,370 +0,0 @@ -// Copyright 2014 Alvaro J. Genial. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package form - -import ( - "fmt" - "io" - "io/ioutil" - "net/url" - "reflect" - "strconv" - "time" -) - -// NewDecoder returns a new form Decoder. -func NewDecoder(r io.Reader) *Decoder { - return &Decoder{r, defaultDelimiter, defaultEscape, false, false} -} - -// Decoder decodes data from a form (application/x-www-form-urlencoded). -type Decoder struct { - r io.Reader - d rune - e rune - ignoreUnknown bool - ignoreCase bool -} - -// DelimitWith sets r as the delimiter used for composite keys by Decoder d and returns the latter; it is '.' by default. -func (d *Decoder) DelimitWith(r rune) *Decoder { - d.d = r - return d -} - -// EscapeWith sets r as the escape used for delimiters (and to escape itself) by Decoder d and returns the latter; it is '\\' by default. -func (d *Decoder) EscapeWith(r rune) *Decoder { - d.e = r - return d -} - -// Decode reads in and decodes form-encoded data into dst. -func (d Decoder) Decode(dst interface{}) error { - bs, err := ioutil.ReadAll(d.r) - if err != nil { - return err - } - vs, err := url.ParseQuery(string(bs)) - if err != nil { - return err - } - v := reflect.ValueOf(dst) - return d.decodeNode(v, parseValues(d.d, d.e, vs, canIndexOrdinally(v))) -} - -// IgnoreUnknownKeys if set to true it will make the Decoder ignore values -// that are not found in the destination object instead of returning an error. -func (d *Decoder) IgnoreUnknownKeys(ignoreUnknown bool) { - d.ignoreUnknown = ignoreUnknown -} - -// IgnoreCase if set to true it will make the Decoder try to set values in the -// destination object even if the case does not match. -func (d *Decoder) IgnoreCase(ignoreCase bool) { - d.ignoreCase = ignoreCase -} - -// DecodeString decodes src into dst. -func (d Decoder) DecodeString(dst interface{}, src string) error { - vs, err := url.ParseQuery(src) - if err != nil { - return err - } - v := reflect.ValueOf(dst) - return d.decodeNode(v, parseValues(d.d, d.e, vs, canIndexOrdinally(v))) -} - -// DecodeValues decodes vs into dst. -func (d Decoder) DecodeValues(dst interface{}, vs url.Values) error { - v := reflect.ValueOf(dst) - return d.decodeNode(v, parseValues(d.d, d.e, vs, canIndexOrdinally(v))) -} - -// DecodeString decodes src into dst. -func DecodeString(dst interface{}, src string) error { - return NewDecoder(nil).DecodeString(dst, src) -} - -// DecodeValues decodes vs into dst. -func DecodeValues(dst interface{}, vs url.Values) error { - return NewDecoder(nil).DecodeValues(dst, vs) -} - -func (d Decoder) decodeNode(v reflect.Value, n node) (err error) { - defer func() { - if e := recover(); e != nil { - err = fmt.Errorf("%v", e) - } - }() - - if v.Kind() == reflect.Slice { - return fmt.Errorf("could not decode directly into slice; use pointer to slice") - } - d.decodeValue(v, n) - return nil -} - -func (d Decoder) decodeValue(v reflect.Value, x interface{}) { - t := v.Type() - k := v.Kind() - - if k == reflect.Ptr && v.IsNil() { - v.Set(reflect.New(t.Elem())) - } - - if unmarshalValue(v, x) { - return - } - - empty := isEmpty(x) - - switch k { - case reflect.Ptr: - d.decodeValue(v.Elem(), x) - return - case reflect.Interface: - if !v.IsNil() { - d.decodeValue(v.Elem(), x) - return - - } else if empty { - return // Allow nil interfaces only if empty. - } else { - panic("form: cannot decode non-empty value into into nil interface") - } - } - - if empty { - v.Set(reflect.Zero(t)) // Treat the empty string as the zero value. - return - } - - switch k { - case reflect.Struct: - if t.ConvertibleTo(timeType) { - d.decodeTime(v, x) - } else if t.ConvertibleTo(urlType) { - d.decodeURL(v, x) - } else { - d.decodeStruct(v, x) - } - case reflect.Slice: - d.decodeSlice(v, x) - case reflect.Array: - d.decodeArray(v, x) - case reflect.Map: - d.decodeMap(v, x) - case reflect.Invalid, reflect.Uintptr, reflect.UnsafePointer, reflect.Chan, reflect.Func: - panic(t.String() + " has unsupported kind " + k.String()) - default: - d.decodeBasic(v, x) - } -} - -func (d Decoder) decodeStruct(v reflect.Value, x interface{}) { - t := v.Type() - for k, c := range getNode(x) { - if f, ok := findField(v, k, d.ignoreCase); !ok && k == "" { - panic(getString(x) + " cannot be decoded as " + t.String()) - } else if !ok { - if !d.ignoreUnknown { - panic(k + " doesn't exist in " + t.String()) - } - } else if !f.CanSet() { - panic(k + " cannot be set in " + t.String()) - } else { - d.decodeValue(f, c) - } - } -} - -func (d Decoder) decodeMap(v reflect.Value, x interface{}) { - t := v.Type() - if v.IsNil() { - v.Set(reflect.MakeMap(t)) - } - for k, c := range getNode(x) { - i := reflect.New(t.Key()).Elem() - d.decodeValue(i, k) - - w := v.MapIndex(i) - if w.IsValid() { // We have an actual element value to decode into. - if w.Kind() == reflect.Interface { - w = w.Elem() - } - w = reflect.New(w.Type()).Elem() - } else if t.Elem().Kind() != reflect.Interface { // The map's element type is concrete. - w = reflect.New(t.Elem()).Elem() - } else { - // The best we can do here is to decode as either a string (for scalars) or a map[string]interface {} (for the rest). - // We could try to guess the type based on the string (e.g. true/false => bool) but that'll get ugly fast, - // especially if we have to guess the kind (slice vs. array vs. map) and index type (e.g. string, int, etc.) - switch c.(type) { - case node: - w = reflect.MakeMap(stringMapType) - case string: - w = reflect.New(stringType).Elem() - default: - panic("value is neither node nor string") - } - } - - d.decodeValue(w, c) - v.SetMapIndex(i, w) - } -} - -func (d Decoder) decodeArray(v reflect.Value, x interface{}) { - t := v.Type() - for k, c := range getNode(x) { - i, err := strconv.Atoi(k) - if err != nil { - panic(k + " is not a valid index for type " + t.String()) - } - if l := v.Len(); i >= l { - panic("index is above array size") - } - d.decodeValue(v.Index(i), c) - } -} - -func (d Decoder) decodeSlice(v reflect.Value, x interface{}) { - t := v.Type() - if t.Elem().Kind() == reflect.Uint8 { - // Allow, but don't require, byte slices to be encoded as a single string. - if s, ok := x.(string); ok { - v.SetBytes([]byte(s)) - return - } - } - - // NOTE: Implicit indexing is currently done at the parseValues level, - // so if if an implicitKey reaches here it will always replace the last. - implicit := 0 - for k, c := range getNode(x) { - var i int - if k == implicitKey { - i = implicit - implicit++ - } else { - explicit, err := strconv.Atoi(k) - if err != nil { - panic(k + " is not a valid index for type " + t.String()) - } - i = explicit - implicit = explicit + 1 - } - // "Extend" the slice if it's too short. - if l := v.Len(); i >= l { - delta := i - l + 1 - v.Set(reflect.AppendSlice(v, reflect.MakeSlice(t, delta, delta))) - } - d.decodeValue(v.Index(i), c) - } -} - -func (d Decoder) decodeBasic(v reflect.Value, x interface{}) { - t := v.Type() - switch k, s := t.Kind(), getString(x); k { - case reflect.Bool: - if b, e := strconv.ParseBool(s); e == nil { - v.SetBool(b) - } else { - panic("could not parse bool from " + strconv.Quote(s)) - } - case reflect.Int, - reflect.Int8, - reflect.Int16, - reflect.Int32, - reflect.Int64: - if i, e := strconv.ParseInt(s, 10, 64); e == nil { - v.SetInt(i) - } else { - panic("could not parse int from " + strconv.Quote(s)) - } - case reflect.Uint, - reflect.Uint8, - reflect.Uint16, - reflect.Uint32, - reflect.Uint64: - if u, e := strconv.ParseUint(s, 10, 64); e == nil { - v.SetUint(u) - } else { - panic("could not parse uint from " + strconv.Quote(s)) - } - case reflect.Float32, - reflect.Float64: - if f, e := strconv.ParseFloat(s, 64); e == nil { - v.SetFloat(f) - } else { - panic("could not parse float from " + strconv.Quote(s)) - } - case reflect.Complex64, - reflect.Complex128: - var c complex128 - if n, err := fmt.Sscanf(s, "%g", &c); n == 1 && err == nil { - v.SetComplex(c) - } else { - panic("could not parse complex from " + strconv.Quote(s)) - } - case reflect.String: - v.SetString(s) - default: - panic(t.String() + " has unsupported kind " + k.String()) - } -} - -func (d Decoder) decodeTime(v reflect.Value, x interface{}) { - t := v.Type() - s := getString(x) - // TODO: Find a more efficient way to do this. - for _, f := range allowedTimeFormats { - if p, err := time.Parse(f, s); err == nil { - v.Set(reflect.ValueOf(p).Convert(v.Type())) - return - } - } - panic("cannot decode string `" + s + "` as " + t.String()) -} - -func (d Decoder) decodeURL(v reflect.Value, x interface{}) { - t := v.Type() - s := getString(x) - if u, err := url.Parse(s); err == nil { - v.Set(reflect.ValueOf(*u).Convert(v.Type())) - return - } - panic("cannot decode string `" + s + "` as " + t.String()) -} - -var allowedTimeFormats = []string{ - "2006-01-02T15:04:05.999999999Z07:00", - "2006-01-02T15:04:05.999999999Z07", - "2006-01-02T15:04:05.999999999Z", - "2006-01-02T15:04:05.999999999", - "2006-01-02T15:04:05Z07:00", - "2006-01-02T15:04:05Z07", - "2006-01-02T15:04:05Z", - "2006-01-02T15:04:05", - "2006-01-02T15:04Z", - "2006-01-02T15:04", - "2006-01-02T15Z", - "2006-01-02T15", - "2006-01-02", - "2006-01", - "2006", - "15:04:05.999999999Z07:00", - "15:04:05.999999999Z07", - "15:04:05.999999999Z", - "15:04:05.999999999", - "15:04:05Z07:00", - "15:04:05Z07", - "15:04:05Z", - "15:04:05", - "15:04Z", - "15:04", - "15Z", - "15", -} diff --git a/backend/vendor/github.com/ajg/form/encode.go b/backend/vendor/github.com/ajg/form/encode.go deleted file mode 100644 index 57a0d0a5..00000000 --- a/backend/vendor/github.com/ajg/form/encode.go +++ /dev/null @@ -1,388 +0,0 @@ -// Copyright 2014 Alvaro J. Genial. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package form - -import ( - "encoding" - "errors" - "fmt" - "io" - "net/url" - "reflect" - "strconv" - "strings" - "time" -) - -// NewEncoder returns a new form Encoder. -func NewEncoder(w io.Writer) *Encoder { - return &Encoder{w, defaultDelimiter, defaultEscape, false} -} - -// Encoder provides a way to encode to a Writer. -type Encoder struct { - w io.Writer - d rune - e rune - z bool -} - -// DelimitWith sets r as the delimiter used for composite keys by Encoder e and returns the latter; it is '.' by default. -func (e *Encoder) DelimitWith(r rune) *Encoder { - e.d = r - return e -} - -// EscapeWith sets r as the escape used for delimiters (and to escape itself) by Encoder e and returns the latter; it is '\\' by default. -func (e *Encoder) EscapeWith(r rune) *Encoder { - e.e = r - return e -} - -// KeepZeros sets whether Encoder e should keep zero (default) values in their literal form when encoding, and returns the former; by default zero values are not kept, but are rather encoded as the empty string. -func (e *Encoder) KeepZeros(z bool) *Encoder { - e.z = z - return e -} - -// Encode encodes dst as form and writes it out using the Encoder's Writer. -func (e Encoder) Encode(dst interface{}) error { - v := reflect.ValueOf(dst) - n, err := encodeToNode(v, e.z) - if err != nil { - return err - } - s := n.values(e.d, e.e).Encode() - l, err := io.WriteString(e.w, s) - switch { - case err != nil: - return err - case l != len(s): - return errors.New("could not write data completely") - } - return nil -} - -// EncodeToString encodes dst as a form and returns it as a string. -func EncodeToString(dst interface{}) (string, error) { - v := reflect.ValueOf(dst) - n, err := encodeToNode(v, false) - if err != nil { - return "", err - } - vs := n.values(defaultDelimiter, defaultEscape) - return vs.Encode(), nil -} - -// EncodeToValues encodes dst as a form and returns it as Values. -func EncodeToValues(dst interface{}) (url.Values, error) { - v := reflect.ValueOf(dst) - n, err := encodeToNode(v, false) - if err != nil { - return nil, err - } - vs := n.values(defaultDelimiter, defaultEscape) - return vs, nil -} - -func encodeToNode(v reflect.Value, z bool) (n node, err error) { - defer func() { - if e := recover(); e != nil { - err = fmt.Errorf("%v", e) - } - }() - return getNode(encodeValue(v, z)), nil -} - -func encodeValue(v reflect.Value, z bool) interface{} { - t := v.Type() - k := v.Kind() - - if s, ok := marshalValue(v); ok { - return s - } else if !z && isEmptyValue(v) { - return "" // Treat the zero value as the empty string. - } - - switch k { - case reflect.Ptr, reflect.Interface: - return encodeValue(v.Elem(), z) - case reflect.Struct: - if t.ConvertibleTo(timeType) { - return encodeTime(v) - } else if t.ConvertibleTo(urlType) { - return encodeURL(v) - } - return encodeStruct(v, z) - case reflect.Slice: - return encodeSlice(v, z) - case reflect.Array: - return encodeArray(v, z) - case reflect.Map: - return encodeMap(v, z) - case reflect.Invalid, reflect.Uintptr, reflect.UnsafePointer, reflect.Chan, reflect.Func: - panic(t.String() + " has unsupported kind " + t.Kind().String()) - default: - return encodeBasic(v) - } -} - -func encodeStruct(v reflect.Value, z bool) interface{} { - t := v.Type() - n := node{} - for i := 0; i < t.NumField(); i++ { - f := t.Field(i) - k, oe := fieldInfo(f) - - if k == "-" { - continue - } else if fv := v.Field(i); oe && isEmptyValue(fv) { - delete(n, k) - } else { - n[k] = encodeValue(fv, z) - } - } - return n -} - -func encodeMap(v reflect.Value, z bool) interface{} { - n := node{} - for _, i := range v.MapKeys() { - k := getString(encodeValue(i, z)) - n[k] = encodeValue(v.MapIndex(i), z) - } - return n -} - -func encodeArray(v reflect.Value, z bool) interface{} { - n := node{} - for i := 0; i < v.Len(); i++ { - n[strconv.Itoa(i)] = encodeValue(v.Index(i), z) - } - return n -} - -func encodeSlice(v reflect.Value, z bool) interface{} { - t := v.Type() - if t.Elem().Kind() == reflect.Uint8 { - return string(v.Bytes()) // Encode byte slices as a single string by default. - } - n := node{} - for i := 0; i < v.Len(); i++ { - n[strconv.Itoa(i)] = encodeValue(v.Index(i), z) - } - return n -} - -func encodeTime(v reflect.Value) string { - t := v.Convert(timeType).Interface().(time.Time) - if t.Year() == 0 && (t.Month() == 0 || t.Month() == 1) && (t.Day() == 0 || t.Day() == 1) { - return t.Format("15:04:05.999999999Z07:00") - } else if t.Hour() == 0 && t.Minute() == 0 && t.Second() == 0 && t.Nanosecond() == 0 { - return t.Format("2006-01-02") - } - return t.Format("2006-01-02T15:04:05.999999999Z07:00") -} - -func encodeURL(v reflect.Value) string { - u := v.Convert(urlType).Interface().(url.URL) - return u.String() -} - -func encodeBasic(v reflect.Value) string { - t := v.Type() - switch k := t.Kind(); k { - case reflect.Bool: - return strconv.FormatBool(v.Bool()) - case reflect.Int, - reflect.Int8, - reflect.Int16, - reflect.Int32, - reflect.Int64: - return strconv.FormatInt(v.Int(), 10) - case reflect.Uint, - reflect.Uint8, - reflect.Uint16, - reflect.Uint32, - reflect.Uint64: - return strconv.FormatUint(v.Uint(), 10) - case reflect.Float32: - return strconv.FormatFloat(v.Float(), 'g', -1, 32) - case reflect.Float64: - return strconv.FormatFloat(v.Float(), 'g', -1, 64) - case reflect.Complex64, reflect.Complex128: - s := fmt.Sprintf("%g", v.Complex()) - return strings.TrimSuffix(strings.TrimPrefix(s, "("), ")") - case reflect.String: - return v.String() - } - panic(t.String() + " has unsupported kind " + t.Kind().String()) -} - -func isEmptyValue(v reflect.Value) bool { - switch t := v.Type(); v.Kind() { - case reflect.Array, reflect.Map, reflect.Slice, reflect.String: - return v.Len() == 0 - case reflect.Bool: - return !v.Bool() - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return v.Int() == 0 - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - return v.Uint() == 0 - case reflect.Float32, reflect.Float64: - return v.Float() == 0 - case reflect.Complex64, reflect.Complex128: - return v.Complex() == 0 - case reflect.Interface, reflect.Ptr: - return v.IsNil() - case reflect.Struct: - if t.ConvertibleTo(timeType) { - return v.Convert(timeType).Interface().(time.Time).IsZero() - } - return reflect.DeepEqual(v, reflect.Zero(t)) - } - return false -} - -// canIndexOrdinally returns whether a value contains an ordered sequence of elements. -func canIndexOrdinally(v reflect.Value) bool { - if !v.IsValid() { - return false - } - switch t := v.Type(); t.Kind() { - case reflect.Ptr, reflect.Interface: - return canIndexOrdinally(v.Elem()) - case reflect.Slice, reflect.Array: - return true - } - return false -} - -func fieldInfo(f reflect.StructField) (k string, oe bool) { - if f.PkgPath != "" { // Skip private fields. - return omittedKey, oe - } - - k = f.Name - tag := f.Tag.Get("form") - if tag == "" { - return k, oe - } - - ps := strings.SplitN(tag, ",", 2) - if ps[0] != "" { - k = ps[0] - } - if len(ps) == 2 { - oe = ps[1] == "omitempty" - } - return k, oe -} - -func findField(v reflect.Value, n string, ignoreCase bool) (reflect.Value, bool) { - t := v.Type() - l := v.NumField() - - var lowerN string - caseInsensitiveMatch := -1 - if ignoreCase { - lowerN = strings.ToLower(n) - } - - // First try named fields. - for i := 0; i < l; i++ { - f := t.Field(i) - k, _ := fieldInfo(f) - if k == omittedKey { - continue - } else if n == k { - return v.Field(i), true - } else if ignoreCase && lowerN == strings.ToLower(k) { - caseInsensitiveMatch = i - } - } - - // If no exact match was found try case insensitive match. - if caseInsensitiveMatch != -1 { - return v.Field(caseInsensitiveMatch), true - } - - // Then try anonymous (embedded) fields. - for i := 0; i < l; i++ { - f := t.Field(i) - k, _ := fieldInfo(f) - if k == omittedKey || !f.Anonymous { // || k != "" ? - continue - } - fv := v.Field(i) - fk := fv.Kind() - for fk == reflect.Ptr || fk == reflect.Interface { - fv = fv.Elem() - fk = fv.Kind() - } - - if fk != reflect.Struct { - continue - } - if ev, ok := findField(fv, n, ignoreCase); ok { - return ev, true - } - } - - return reflect.Value{}, false -} - -var ( - stringType = reflect.TypeOf(string("")) - stringMapType = reflect.TypeOf(map[string]interface{}{}) - timeType = reflect.TypeOf(time.Time{}) - timePtrType = reflect.TypeOf(&time.Time{}) - urlType = reflect.TypeOf(url.URL{}) -) - -func skipTextMarshalling(t reflect.Type) bool { - /*// Skip time.Time because its text unmarshaling is overly rigid: - return t == timeType || t == timePtrType*/ - // Skip time.Time & convertibles because its text unmarshaling is overly rigid: - return t.ConvertibleTo(timeType) || t.ConvertibleTo(timePtrType) -} - -func unmarshalValue(v reflect.Value, x interface{}) bool { - if skipTextMarshalling(v.Type()) { - return false - } - - tu, ok := v.Interface().(encoding.TextUnmarshaler) - if !ok && !v.CanAddr() { - return false - } else if !ok { - return unmarshalValue(v.Addr(), x) - } - - s := getString(x) - if err := tu.UnmarshalText([]byte(s)); err != nil { - panic(err) - } - return true -} - -func marshalValue(v reflect.Value) (string, bool) { - if skipTextMarshalling(v.Type()) { - return "", false - } - - tm, ok := v.Interface().(encoding.TextMarshaler) - if !ok && !v.CanAddr() { - return "", false - } else if !ok { - return marshalValue(v.Addr()) - } - - bs, err := tm.MarshalText() - if err != nil { - panic(err) - } - return string(bs), true -} diff --git a/backend/vendor/github.com/ajg/form/form.go b/backend/vendor/github.com/ajg/form/form.go deleted file mode 100644 index 4052369c..00000000 --- a/backend/vendor/github.com/ajg/form/form.go +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2014 Alvaro J. Genial. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package form implements encoding and decoding of application/x-www-form-urlencoded data. -package form - -const ( - implicitKey = "_" - omittedKey = "-" - - defaultDelimiter = '.' - defaultEscape = '\\' -) diff --git a/backend/vendor/github.com/ajg/form/node.go b/backend/vendor/github.com/ajg/form/node.go deleted file mode 100644 index 567aaafd..00000000 --- a/backend/vendor/github.com/ajg/form/node.go +++ /dev/null @@ -1,152 +0,0 @@ -// Copyright 2014 Alvaro J. Genial. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package form - -import ( - "net/url" - "strconv" - "strings" -) - -type node map[string]interface{} - -func (n node) values(d, e rune) url.Values { - vs := url.Values{} - n.merge(d, e, "", &vs) - return vs -} - -func (n node) merge(d, e rune, p string, vs *url.Values) { - for k, x := range n { - switch y := x.(type) { - case string: - vs.Add(p+escape(d, e, k), y) - case node: - y.merge(d, e, p+escape(d, e, k)+string(d), vs) - default: - panic("value is neither string nor node") - } - } -} - -// TODO: Add tests for implicit indexing. -func parseValues(d, e rune, vs url.Values, canIndexFirstLevelOrdinally bool) node { - // NOTE: Because of the flattening of potentially multiple strings to one key, implicit indexing works: - // i. At the first level; e.g. Foo.Bar=A&Foo.Bar=B becomes 0.Foo.Bar=A&1.Foo.Bar=B - // ii. At the last level; e.g. Foo.Bar._=A&Foo.Bar._=B becomes Foo.Bar.0=A&Foo.Bar.1=B - // TODO: At in-between levels; e.g. Foo._.Bar=A&Foo._.Bar=B becomes Foo.0.Bar=A&Foo.1.Bar=B - // (This last one requires that there only be one placeholder in order for it to be unambiguous.) - - m := map[string]string{} - for k, ss := range vs { - indexLastLevelOrdinally := strings.HasSuffix(k, string(d)+implicitKey) - - for i, s := range ss { - if canIndexFirstLevelOrdinally { - k = strconv.Itoa(i) + string(d) + k - } else if indexLastLevelOrdinally { - k = strings.TrimSuffix(k, implicitKey) + strconv.Itoa(i) - } - - m[k] = s - } - } - - n := node{} - for k, s := range m { - n = n.split(d, e, k, s) - } - return n -} - -func splitPath(d, e rune, path string) (k, rest string) { - esc := false - for i, r := range path { - switch { - case !esc && r == e: - esc = true - case !esc && r == d: - return unescape(d, e, path[:i]), path[i+1:] - default: - esc = false - } - } - return unescape(d, e, path), "" -} - -func (n node) split(d, e rune, path, s string) node { - k, rest := splitPath(d, e, path) - if rest == "" { - return add(n, k, s) - } - if _, ok := n[k]; !ok { - n[k] = node{} - } - - c := getNode(n[k]) - n[k] = c.split(d, e, rest, s) - return n -} - -func add(n node, k, s string) node { - if n == nil { - return node{k: s} - } - - if _, ok := n[k]; ok { - panic("key " + k + " already set") - } - - n[k] = s - return n -} - -func isEmpty(x interface{}) bool { - switch y := x.(type) { - case string: - return y == "" - case node: - if s, ok := y[""].(string); ok { - return s == "" - } - return false - } - panic("value is neither string nor node") -} - -func getNode(x interface{}) node { - switch y := x.(type) { - case string: - return node{"": y} - case node: - return y - } - panic("value is neither string nor node") -} - -func getString(x interface{}) string { - switch y := x.(type) { - case string: - return y - case node: - if s, ok := y[""].(string); ok { - return s - } - return "" - } - panic("value is neither string nor node") -} - -func escape(d, e rune, s string) string { - s = strings.Replace(s, string(e), string(e)+string(e), -1) // Escape the escape (\ => \\) - s = strings.Replace(s, string(d), string(e)+string(d), -1) // Escape the delimiter (. => \.) - return s -} - -func unescape(d, e rune, s string) string { - s = strings.Replace(s, string(e)+string(d), string(d), -1) // Unescape the delimiter (\. => .) - s = strings.Replace(s, string(e)+string(e), string(e), -1) // Unescape the escape (\\ => \) - return s -} diff --git a/backend/vendor/github.com/ajg/form/pre-commit.sh b/backend/vendor/github.com/ajg/form/pre-commit.sh deleted file mode 100644 index 29ce311e..00000000 --- a/backend/vendor/github.com/ajg/form/pre-commit.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash -eu - -# TODO: Only colorize messages given a suitable terminal. -# FIXME: Handle case in which no stash entry is created due to no changes. - -printf "\e[30m=== PRE-COMMIT STARTING ===\e[m\n" -git stash save --quiet --keep-index --include-untracked - -if go build -v ./... && go test -v -cover ./... && go vet ./... && golint . && travis-lint; then - result=$? - printf "\e[32m=== PRE-COMMIT SUCCEEDED ===\e[m\n" -else - result=$? - printf "\e[31m=== PRE-COMMIT FAILED ===\e[m\n" -fi - -git stash pop --quiet -exit $result diff --git a/backend/vendor/github.com/didip/tollbooth/v7/.gitignore b/backend/vendor/github.com/didip/tollbooth/v7/.gitignore deleted file mode 100644 index 91ea4d71..00000000 --- a/backend/vendor/github.com/didip/tollbooth/v7/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/debug -/.vscode -/.idea \ No newline at end of file diff --git a/backend/vendor/github.com/didip/tollbooth/v7/.golangci.yml b/backend/vendor/github.com/didip/tollbooth/v7/.golangci.yml deleted file mode 100644 index 5d0a4b64..00000000 --- a/backend/vendor/github.com/didip/tollbooth/v7/.golangci.yml +++ /dev/null @@ -1,33 +0,0 @@ -linters: - enable: - - revive - - govet - - unconvert - - megacheck - - gas - - gocyclo - - dupl - - misspell - - unparam - - unused - - typecheck - - ineffassign - - stylecheck - - gochecknoinits - - exportloopref - - gocritic - - nakedret - - gosimple - - prealloc - fast: false - disable-all: true - -issues: - exclude-rules: - - path: _test\.go - linters: - - dupl - - text: "Errors unhandled" - linters: - - gosec - exclude-use-default: false diff --git a/backend/vendor/github.com/didip/tollbooth/v7/LICENSE b/backend/vendor/github.com/didip/tollbooth/v7/LICENSE deleted file mode 100644 index 349ee1c2..00000000 --- a/backend/vendor/github.com/didip/tollbooth/v7/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2015 Didip Kerabat - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/backend/vendor/github.com/didip/tollbooth/v7/README.md b/backend/vendor/github.com/didip/tollbooth/v7/README.md deleted file mode 100644 index f96ac2f1..00000000 --- a/backend/vendor/github.com/didip/tollbooth/v7/README.md +++ /dev/null @@ -1,186 +0,0 @@ -[![GoDoc](https://godoc.org/github.com/didip/tollbooth?status.svg)](http://godoc.org/github.com/didip/tollbooth) -[![license](http://img.shields.io/badge/license-MIT-red.svg?style=flat)](https://raw.githubusercontent.com/didip/tollbooth/master/LICENSE) - -## Tollbooth - -This is a generic middleware to rate-limit HTTP requests. - -**NOTE 1:** This library is considered finished. - -**NOTE 2:** Major version changes are backward-incompatible. `v2.0.0` streamlines the ugliness of the old API. - -## Versions - -**v1.0.0:** This version maintains the old API but all the thirdparty modules are moved to their own repo. - -**v2.x.x:** Brand-new API for the sake of code cleanup, thread safety, & auto-expiring data structures. - -**v3.x.x:** Apparently we have been using golang.org/x/time/rate incorrectly. See issue #48. It always limits X number per 1 second. The time duration is not changeable, so it does not make sense to pass TTL to tollbooth. - -**v4.x.x:** Float64 for max requests per second - -**v5.x.x:** go.mod and go.sum - -**v6.x.x:** Replaced `go-cache` with `github.com/go-pkgz/expirable-cache` because `go-cache` leaks goroutines. - -**v7.x.x:** Replaced `time/rate` with `embedded time/rate` so that we can support more rate limit headers. - -## Five Minute Tutorial - -```go -package main - -import ( - "net/http" - - "github.com/didip/tollbooth/v7" -) - -func HelloHandler(w http.ResponseWriter, req *http.Request) { - w.Write([]byte("Hello, World!")) -} - -func main() { - // Create a request limiter per handler. - http.Handle("/", tollbooth.LimitFuncHandler(tollbooth.NewLimiter(1, nil), HelloHandler)) - http.ListenAndServe(":12345", nil) -} -``` - -## Features - -1. Rate-limit by request's remote IP, path, methods, custom headers, & basic auth usernames. - ```go - import ( - "time" - - "github.com/didip/tollbooth/v7" - "github.com/didip/tollbooth/v7/limiter" - ) - - lmt := tollbooth.NewLimiter(1, nil) - - // or create a limiter with expirable token buckets - // This setting means: - // create a 1 request/second limiter and - // every token bucket in it will expire 1 hour after it was initially set. - lmt = tollbooth.NewLimiter(1, &limiter.ExpirableOptions{DefaultExpirationTTL: time.Hour}) - - // Configure list of places to look for IP address. - // By default it's: "RemoteAddr", "X-Forwarded-For", "X-Real-IP" - // If your application is behind a proxy, set "X-Forwarded-For" first. - lmt.SetIPLookups([]string{"RemoteAddr", "X-Forwarded-For", "X-Real-IP"}) - - // Limit only GET and POST requests. - lmt.SetMethods([]string{"GET", "POST"}) - - // Limit based on basic auth usernames. - // You add them on-load, or later as you handle requests. - lmt.SetBasicAuthUsers([]string{"bob", "jane", "didip", "vip"}) - // You can remove them later as well. - lmt.RemoveBasicAuthUsers([]string{"vip"}) - - // Limit request headers containing certain values. - // You add them on-load, or later as you handle requests. - lmt.SetHeader("X-Access-Token", []string{"abc123", "xyz098"}) - // You can remove all entries at once. - lmt.RemoveHeader("X-Access-Token") - // Or remove specific ones. - lmt.RemoveHeaderEntries("X-Access-Token", []string{"limitless-token"}) - - // By the way, the setters are chainable. Example: - lmt.SetIPLookups([]string{"RemoteAddr", "X-Forwarded-For", "X-Real-IP"}). - SetMethods([]string{"GET", "POST"}). - SetBasicAuthUsers([]string{"sansa"}). - SetBasicAuthUsers([]string{"tyrion"}) - ``` - -2. Compose your own middleware by using `LimitByKeys()`. - -3. Header entries and basic auth users can expire over time (to conserve memory). - - ```go - import "time" - - lmt := tollbooth.NewLimiter(1, nil) - - // Set a custom expiration TTL for token bucket. - lmt.SetTokenBucketExpirationTTL(time.Hour) - - // Set a custom expiration TTL for basic auth users. - lmt.SetBasicAuthExpirationTTL(time.Hour) - - // Set a custom expiration TTL for header entries. - lmt.SetHeaderEntryExpirationTTL(time.Hour) - ``` - -4. Upon rejection, the following HTTP response headers are available to users: - - * `X-Rate-Limit-Limit` The maximum request limit. - - * `X-Rate-Limit-Duration` The rate-limiter duration. - - * `X-Rate-Limit-Request-Forwarded-For` The rejected request `X-Forwarded-For`. - - * `X-Rate-Limit-Request-Remote-Addr` The rejected request `RemoteAddr`. - - Upon both success and rejection [RateLimit](https://datatracker.ietf.org/doc/html/draft-ietf-httpapi-ratelimit-headers) headers are sent: - - * `RateLimit-Limit` The maximum request limit within the time window (1s). - - * `RateLimit-Reset` The rate-limiter time window duration in seconds (always 1s). - - * `RateLimit-Remaining` The remaining tokens. - -5. Customize your own message or function when limit is reached. - - ```go - lmt := tollbooth.NewLimiter(1, nil) - - // Set a custom message. - lmt.SetMessage("You have reached maximum request limit.") - - // Set a custom content-type. - lmt.SetMessageContentType("text/plain; charset=utf-8") - - // Set a custom function for rejection. - lmt.SetOnLimitReached(func(w http.ResponseWriter, r *http.Request) { fmt.Println("A request was rejected") }) - ``` - -6. Tollbooth does not require external storage since it uses an algorithm called [Token Bucket](http://en.wikipedia.org/wiki/Token_bucket) [(Go library: golang.org/x/time/rate)](https://godoc.org/golang.org/x/time/rate). - -## Other Web Frameworks - -Sometimes, other frameworks require a little bit of shim to use Tollbooth. These shims below are contributed by the community, so I make no promises on how well they work. The one I am familiar with are: Chi, Gin, and Negroni. - -* [Chi](https://github.com/didip/tollbooth_chi) - -* [Echo](https://github.com/didip/tollbooth_echo) - -* [FastHTTP](https://github.com/didip/tollbooth_fasthttp) - -* [Gin](https://github.com/didip/tollbooth_gin) - -* [GoRestful](https://github.com/didip/tollbooth_gorestful) - -* [HTTPRouter](https://github.com/didip/tollbooth_httprouter) - -* [Iris](https://github.com/didip/tollbooth_iris) - -* [Negroni](https://github.com/didip/tollbooth_negroni) - -## My other Go libraries - -* [ErrStack](https://github.com/didip/errstack): A small library to combine errors and also display filename and line number. - -* [Stopwatch](https://github.com/didip/stopwatch): A small library to measure latency of things. Useful if you want to report latency data to Graphite. - -* [LaborUnion](https://github.com/didip/laborunion): A dynamic worker pool library. - -* [Gomet](https://github.com/didip/gomet): Simple HTTP client & server long poll library for Go. Useful for receiving live updates without needing Websocket. - -## Contributions - -Before sending a PR with code changes, please make sure altered code is covered with tests which are passing, and that golangci-lint shows no errors. - -To check the linter output, [install it](https://golangci-lint.run/usage/install/#local-installation) and then run `golangci-lint run` in the root directory of the repository. diff --git a/backend/vendor/github.com/didip/tollbooth/v7/errors/errors.go b/backend/vendor/github.com/didip/tollbooth/v7/errors/errors.go deleted file mode 100644 index 149bc5a1..00000000 --- a/backend/vendor/github.com/didip/tollbooth/v7/errors/errors.go +++ /dev/null @@ -1,15 +0,0 @@ -// Package errors provide data structure for errors. -package errors - -import "fmt" - -// HTTPError is an error struct that returns both message and status code. -type HTTPError struct { - Message string - StatusCode int -} - -// Error returns error message. -func (httperror *HTTPError) Error() string { - return fmt.Sprintf("%v: %v", httperror.StatusCode, httperror.Message) -} diff --git a/backend/vendor/github.com/didip/tollbooth/v7/internal/time/AUTHORS b/backend/vendor/github.com/didip/tollbooth/v7/internal/time/AUTHORS deleted file mode 100644 index 15167cd7..00000000 --- a/backend/vendor/github.com/didip/tollbooth/v7/internal/time/AUTHORS +++ /dev/null @@ -1,3 +0,0 @@ -# This source code refers to The Go Authors for copyright purposes. -# The master list of authors is in the main Go distribution, -# visible at http://tip.golang.org/AUTHORS. diff --git a/backend/vendor/github.com/didip/tollbooth/v7/internal/time/CONTRIBUTORS b/backend/vendor/github.com/didip/tollbooth/v7/internal/time/CONTRIBUTORS deleted file mode 100644 index 1c4577e9..00000000 --- a/backend/vendor/github.com/didip/tollbooth/v7/internal/time/CONTRIBUTORS +++ /dev/null @@ -1,3 +0,0 @@ -# This source code was written by the Go contributors. -# The master list of contributors is in the main Go distribution, -# visible at http://tip.golang.org/CONTRIBUTORS. diff --git a/backend/vendor/github.com/didip/tollbooth/v7/internal/time/LICENSE b/backend/vendor/github.com/didip/tollbooth/v7/internal/time/LICENSE deleted file mode 100644 index 6a66aea5..00000000 --- a/backend/vendor/github.com/didip/tollbooth/v7/internal/time/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2009 The Go Authors. All rights reserved. - -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. - -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. diff --git a/backend/vendor/github.com/didip/tollbooth/v7/internal/time/PATENTS b/backend/vendor/github.com/didip/tollbooth/v7/internal/time/PATENTS deleted file mode 100644 index 73309904..00000000 --- a/backend/vendor/github.com/didip/tollbooth/v7/internal/time/PATENTS +++ /dev/null @@ -1,22 +0,0 @@ -Additional IP Rights Grant (Patents) - -"This implementation" means the copyrightable works distributed by -Google as part of the Go project. - -Google hereby grants to You a perpetual, worldwide, non-exclusive, -no-charge, royalty-free, irrevocable (except as stated in this section) -patent license to make, have made, use, offer to sell, sell, import, -transfer and otherwise run, modify and propagate the contents of this -implementation of Go, where such license applies only to those patent -claims, both currently owned or controlled by Google and acquired in -the future, licensable by Google that are necessarily infringed by this -implementation of Go. This grant does not include claims that would be -infringed only as a consequence of further modification of this -implementation. If you or your agent or exclusive licensee institute or -order or agree to the institution of patent litigation against any -entity (including a cross-claim or counterclaim in a lawsuit) alleging -that this implementation of Go or any code incorporated within this -implementation of Go constitutes direct or contributory patent -infringement, or inducement of patent infringement, then any patent -rights granted to you under this License for this implementation of Go -shall terminate as of the date such litigation is filed. diff --git a/backend/vendor/github.com/didip/tollbooth/v7/internal/time/rate/rate.go b/backend/vendor/github.com/didip/tollbooth/v7/internal/time/rate/rate.go deleted file mode 100644 index 6c3b442d..00000000 --- a/backend/vendor/github.com/didip/tollbooth/v7/internal/time/rate/rate.go +++ /dev/null @@ -1,396 +0,0 @@ -// Copyright 2015 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package rate provides a rate limiter. -package rate - -import ( - "context" - "fmt" - "math" - "sync" - "time" -) - -// Limit defines the maximum frequency of some events. -// Limit is represented as number of events per second. -// A zero Limit allows no events. -type Limit float64 - -// Inf is the infinite rate limit; it allows all events (even if burst is zero). -const Inf = Limit(math.MaxFloat64) - -// Every converts a minimum time interval between events to a Limit. -func Every(interval time.Duration) Limit { - if interval <= 0 { - return Inf - } - return 1 / Limit(interval.Seconds()) -} - -// A Limiter controls how frequently events are allowed to happen. -// It implements a "token bucket" of size b, initially full and refilled -// at rate r tokens per second. -// Informally, in any large enough time interval, the Limiter limits the -// rate to r tokens per second, with a maximum burst size of b events. -// As a special case, if r == Inf (the infinite rate), b is ignored. -// See https://en.wikipedia.org/wiki/Token_bucket for more about token buckets. -// -// The zero value is a valid Limiter, but it will reject all events. -// Use NewLimiter to create non-zero Limiters. -// -// Limiter has three main methods, Allow, Reserve, and Wait. -// Most callers should use Wait. -// -// Each of the three methods consumes a single token. -// They differ in their behavior when no token is available. -// If no token is available, Allow returns false. -// If no token is available, Reserve returns a reservation for a future token -// and the amount of time the caller must wait before using it. -// If no token is available, Wait blocks until one can be obtained -// or its associated context.Context is canceled. -// -// The methods AllowN, ReserveN, and WaitN consume n tokens. -type Limiter struct { - mu sync.Mutex - limit Limit - burst int - tokens float64 - // last is the last time the limiter's tokens field was updated - last time.Time - // lastEvent is the latest time of a rate-limited event (past or future) - lastEvent time.Time -} - -// Limit returns the maximum overall event rate. -func (lim *Limiter) Limit() Limit { - lim.mu.Lock() - defer lim.mu.Unlock() - return lim.limit -} - -// Burst returns the maximum burst size. Burst is the maximum number of tokens -// that can be consumed in a single call to Allow, Reserve, or Wait, so higher -// Burst values allow more events to happen at once. -// A zero Burst allows no events, unless limit == Inf. -func (lim *Limiter) Burst() int { - lim.mu.Lock() - defer lim.mu.Unlock() - return lim.burst -} - -// NewLimiter returns a new Limiter that allows events up to rate r and permits -// bursts of at most b tokens. -func NewLimiter(r Limit, b int) *Limiter { - return &Limiter{ - limit: r, - burst: b, - } -} - -// Allow is shorthand for AllowN(time.Now(), 1). -func (lim *Limiter) Allow() bool { - return lim.AllowN(time.Now(), 1) -} - -// TokensAt returns the number of tokens available for the given time. -func (lim *Limiter) TokensAt(t time.Time) float64 { - lim.mu.Lock() - _, _, tokens := lim.advance(t) // does not mutate lim - lim.mu.Unlock() - return tokens -} - -// AllowN reports whether n events may happen at time now. -// Use this method if you intend to drop / skip events that exceed the rate limit. -// Otherwise use Reserve or Wait. -func (lim *Limiter) AllowN(now time.Time, n int) bool { - return lim.reserveN(now, n, 0).ok -} - -// A Reservation holds information about events that are permitted by a Limiter to happen after a delay. -// A Reservation may be canceled, which may enable the Limiter to permit additional events. -type Reservation struct { - ok bool - lim *Limiter - tokens int - timeToAct time.Time - // This is the Limit at reservation time, it can change later. - limit Limit -} - -// OK returns whether the limiter can provide the requested number of tokens -// within the maximum wait time. If OK is false, Delay returns InfDuration, and -// Cancel does nothing. -func (r *Reservation) OK() bool { - return r.ok -} - -// Delay is shorthand for DelayFrom(time.Now()). -func (r *Reservation) Delay() time.Duration { - return r.DelayFrom(time.Now()) -} - -// InfDuration is the duration returned by Delay when a Reservation is not OK. -const InfDuration = time.Duration(1<<63 - 1) - -// DelayFrom returns the duration for which the reservation holder must wait -// before taking the reserved action. Zero duration means act immediately. -// InfDuration means the limiter cannot grant the tokens requested in this -// Reservation within the maximum wait time. -func (r *Reservation) DelayFrom(now time.Time) time.Duration { - if !r.ok { - return InfDuration - } - delay := r.timeToAct.Sub(now) - if delay < 0 { - return 0 - } - return delay -} - -// Cancel is shorthand for CancelAt(time.Now()). -func (r *Reservation) Cancel() { - r.CancelAt(time.Now()) -} - -// CancelAt indicates that the reservation holder will not perform the reserved action -// and reverses the effects of this Reservation on the rate limit as much as possible, -// considering that other reservations may have already been made. -func (r *Reservation) CancelAt(now time.Time) { - if !r.ok { - return - } - - r.lim.mu.Lock() - defer r.lim.mu.Unlock() - - if r.lim.limit == Inf || r.tokens == 0 || r.timeToAct.Before(now) { - return - } - - // calculate tokens to restore - // The duration between lim.lastEvent and r.timeToAct tells us how many tokens were reserved - // after r was obtained. These tokens should not be restored. - restoreTokens := float64(r.tokens) - r.limit.tokensFromDuration(r.lim.lastEvent.Sub(r.timeToAct)) - if restoreTokens <= 0 { - return - } - // advance time to now - now, _, tokens := r.lim.advance(now) - // calculate new number of tokens - tokens += restoreTokens - if burst := float64(r.lim.burst); tokens > burst { - tokens = burst - } - // update state - r.lim.last = now - r.lim.tokens = tokens - if r.timeToAct == r.lim.lastEvent { - prevEvent := r.timeToAct.Add(r.limit.durationFromTokens(float64(-r.tokens))) - if !prevEvent.Before(now) { - r.lim.lastEvent = prevEvent - } - } -} - -// Reserve is shorthand for ReserveN(time.Now(), 1). -func (lim *Limiter) Reserve() *Reservation { - return lim.ReserveN(time.Now(), 1) -} - -// ReserveN returns a Reservation that indicates how long the caller must wait before n events happen. -// The Limiter takes this Reservation into account when allowing future events. -// The returned Reservation’s OK() method returns false if n exceeds the Limiter's burst size. -// Usage example: -// r := lim.ReserveN(time.Now(), 1) -// if !r.OK() { -// // Not allowed to act! Did you remember to set lim.burst to be > 0 ? -// return -// } -// time.Sleep(r.Delay()) -// Act() -// Use this method if you wish to wait and slow down in accordance with the rate limit without dropping events. -// If you need to respect a deadline or cancel the delay, use Wait instead. -// To drop or skip events exceeding rate limit, use Allow instead. -func (lim *Limiter) ReserveN(now time.Time, n int) *Reservation { - r := lim.reserveN(now, n, InfDuration) - return &r -} - -// Wait is shorthand for WaitN(ctx, 1). -func (lim *Limiter) Wait(ctx context.Context) (err error) { - return lim.WaitN(ctx, 1) -} - -// WaitN blocks until lim permits n events to happen. -// It returns an error if n exceeds the Limiter's burst size, the Context is -// canceled, or the expected wait time exceeds the Context's Deadline. -// The burst limit is ignored if the rate limit is Inf. -func (lim *Limiter) WaitN(ctx context.Context, n int) (err error) { - lim.mu.Lock() - burst := lim.burst - limit := lim.limit - lim.mu.Unlock() - - if n > burst && limit != Inf { - return fmt.Errorf("rate: Wait(n=%d) exceeds limiter's burst %d", n, burst) - } - // Check if ctx is already cancelled - select { - case <-ctx.Done(): - return ctx.Err() - default: - } - // Determine wait limit - now := time.Now() - waitLimit := InfDuration - if deadline, ok := ctx.Deadline(); ok { - waitLimit = deadline.Sub(now) - } - // Reserve - r := lim.reserveN(now, n, waitLimit) - if !r.ok { - return fmt.Errorf("rate: Wait(n=%d) would exceed context deadline", n) - } - // Wait if necessary - delay := r.DelayFrom(now) - if delay == 0 { - return nil - } - t := time.NewTimer(delay) - defer t.Stop() - select { - case <-t.C: - // We can proceed. - return nil - case <-ctx.Done(): - // Context was canceled before we could proceed. Cancel the - // reservation, which may permit other events to proceed sooner. - r.Cancel() - return ctx.Err() - } -} - -// SetLimit is shorthand for SetLimitAt(time.Now(), newLimit). -func (lim *Limiter) SetLimit(newLimit Limit) { - lim.SetLimitAt(time.Now(), newLimit) -} - -// SetLimitAt sets a new Limit for the limiter. The new Limit, and Burst, may be violated -// or underutilized by those which reserved (using Reserve or Wait) but did not yet act -// before SetLimitAt was called. -func (lim *Limiter) SetLimitAt(now time.Time, newLimit Limit) { - lim.mu.Lock() - defer lim.mu.Unlock() - - now, _, tokens := lim.advance(now) - - lim.last = now - lim.tokens = tokens - lim.limit = newLimit -} - -// SetBurst is shorthand for SetBurstAt(time.Now(), newBurst). -func (lim *Limiter) SetBurst(newBurst int) { - lim.SetBurstAt(time.Now(), newBurst) -} - -// SetBurstAt sets a new burst size for the limiter. -func (lim *Limiter) SetBurstAt(now time.Time, newBurst int) { - lim.mu.Lock() - defer lim.mu.Unlock() - - now, _, tokens := lim.advance(now) - - lim.last = now - lim.tokens = tokens - lim.burst = newBurst -} - -// reserveN is a helper method for AllowN, ReserveN, and WaitN. -// maxFutureReserve specifies the maximum reservation wait duration allowed. -// reserveN returns Reservation, not *Reservation, to avoid allocation in AllowN and WaitN. -func (lim *Limiter) reserveN(now time.Time, n int, maxFutureReserve time.Duration) Reservation { - lim.mu.Lock() - - if lim.limit == Inf { - lim.mu.Unlock() - return Reservation{ - ok: true, - lim: lim, - tokens: n, - timeToAct: now, - } - } - - now, last, tokens := lim.advance(now) - - // Calculate the remaining number of tokens resulting from the request. - tokens -= float64(n) - - // Calculate the wait duration - var waitDuration time.Duration - if tokens < 0 { - waitDuration = lim.limit.durationFromTokens(-tokens) - } - - // Decide result - ok := n <= lim.burst && waitDuration <= maxFutureReserve - - // Prepare reservation - r := Reservation{ - ok: ok, - lim: lim, - limit: lim.limit, - } - if ok { - r.tokens = n - r.timeToAct = now.Add(waitDuration) - } - - // Update state - if ok { - lim.last = now - lim.tokens = tokens - lim.lastEvent = r.timeToAct - } else { - lim.last = last - } - - lim.mu.Unlock() - return r -} - -// advance calculates and returns an updated state for lim resulting from the passage of time. -// lim is not changed. -// advance requires that lim.mu is held. -func (lim *Limiter) advance(now time.Time) (newNow time.Time, newLast time.Time, newTokens float64) { - last := lim.last - if now.Before(last) { - last = now - } - - // Calculate the new number of tokens, due to time that passed. - elapsed := now.Sub(last) - delta := lim.limit.tokensFromDuration(elapsed) - tokens := lim.tokens + delta - if burst := float64(lim.burst); tokens > burst { - tokens = burst - } - return now, last, tokens -} - -// durationFromTokens is a unit conversion function from the number of tokens to the duration -// of time it takes to accumulate them at a rate of limit tokens per second. -func (limit Limit) durationFromTokens(tokens float64) time.Duration { - seconds := tokens / float64(limit) - return time.Duration(float64(time.Second) * seconds) -} - -// tokensFromDuration is a unit conversion function from a time duration to the number of tokens -// which could be accumulated during that duration at a rate of limit tokens per second. -func (limit Limit) tokensFromDuration(d time.Duration) float64 { - return d.Seconds() * float64(limit) -} diff --git a/backend/vendor/github.com/didip/tollbooth/v7/libstring/libstring.go b/backend/vendor/github.com/didip/tollbooth/v7/libstring/libstring.go deleted file mode 100644 index 730b6549..00000000 --- a/backend/vendor/github.com/didip/tollbooth/v7/libstring/libstring.go +++ /dev/null @@ -1,101 +0,0 @@ -// Package libstring provides various string related functions. -package libstring - -import ( - "net" - "net/http" - "strings" -) - -// StringInSlice finds needle in a slice of strings. -func StringInSlice(sliceString []string, needle string) bool { - for _, b := range sliceString { - if b == needle { - return true - } - } - return false -} - -// RemoteIP finds IP Address given http.Request struct. -func RemoteIP(ipLookups []string, forwardedForIndexFromBehind int, r *http.Request) string { - realIP := r.Header.Get("X-Real-IP") - forwardedFor := r.Header.Get("X-Forwarded-For") - - for _, lookup := range ipLookups { - if lookup == "RemoteAddr" { - // 1. Cover the basic use cases for both ipv4 and ipv6 - ip, _, err := net.SplitHostPort(r.RemoteAddr) - if err != nil { - // 2. Upon error, just return the remote addr. - return r.RemoteAddr - } - return ip - } - if lookup == "X-Forwarded-For" && forwardedFor != "" { - // X-Forwarded-For is potentially a list of addresses separated with "," - parts := strings.Split(forwardedFor, ",") - for i, p := range parts { - parts[i] = strings.TrimSpace(p) - } - - partIndex := len(parts) - 1 - forwardedForIndexFromBehind - if partIndex < 0 { - partIndex = 0 - } - - return parts[partIndex] - } - if lookup == "X-Real-IP" && realIP != "" { - return realIP - } - } - - return "" -} - -// CanonicalizeIP returns a form of ip suitable for comparison to other IPs. -// For IPv4 addresses, this is simply the whole string. -// For IPv6 addresses, this is the /64 prefix. -func CanonicalizeIP(ip string) string { - isIPv6 := false - // This is how net.ParseIP decides if an address is IPv6 - // https://cs.opensource.google/go/go/+/refs/tags/go1.17.7:src/net/ip.go;l=704 - for i := 0; !isIPv6 && i < len(ip); i++ { - switch ip[i] { - case '.': - // IPv4 - return ip - case ':': - // IPv6 - isIPv6 = true - } - } - if !isIPv6 { - // Not an IP address at all - return ip - } - - // By default, the string representation of a net.IPNet (masked IP address) is just - // "full_address/mask_bits". But using that will result in different addresses with - // the same /64 prefix comparing differently. So we need to zero out the last 64 bits - // so that all IPs in the same prefix will be the same. - // - // Note: When 1.18 is the minimum Go version, this can be written more cleanly like: - // netip.PrefixFrom(netip.MustParseAddr(ipv6), 64).Masked().Addr().String() - // (With appropriate error checking.) - - ipv6 := net.ParseIP(ip) - if ipv6 == nil { - return ip - } - - const bytesToZero = (128 - 64) / 8 - for i := len(ipv6) - bytesToZero; i < len(ipv6); i++ { - ipv6[i] = 0 - } - - // Note that this doesn't have the "/64" suffix customary with a CIDR representation, - // but those three bytes add nothing for us. - return ipv6.String() -} diff --git a/backend/vendor/github.com/didip/tollbooth/v7/limiter/limiter.go b/backend/vendor/github.com/didip/tollbooth/v7/limiter/limiter.go deleted file mode 100644 index 0153dc80..00000000 --- a/backend/vendor/github.com/didip/tollbooth/v7/limiter/limiter.go +++ /dev/null @@ -1,610 +0,0 @@ -// Package limiter provides data structure to configure rate-limiter. -package limiter - -import ( - "net/http" - "sync" - "time" - - cache "github.com/go-pkgz/expirable-cache/v3" - - "github.com/didip/tollbooth/v7/internal/time/rate" -) - -// New is a constructor for Limiter. -func New(generalExpirableOptions *ExpirableOptions) *Limiter { - lmt := &Limiter{} - - lmt.SetMessageContentType("text/plain; charset=utf-8"). - SetMessage("You have reached maximum request limit."). - SetStatusCode(429). - SetOnLimitReached(nil). - SetIPLookups([]string{"RemoteAddr", "X-Forwarded-For", "X-Real-IP"}). - SetForwardedForIndexFromBehind(0). - SetHeaders(make(map[string][]string)). - SetContextValues(make(map[string][]string)). - SetIgnoreURL(false) - - if generalExpirableOptions != nil { - lmt.generalExpirableOptions = generalExpirableOptions - } else { - lmt.generalExpirableOptions = &ExpirableOptions{} - } - - // Default for DefaultExpirationTTL is 10 years. - if lmt.generalExpirableOptions.DefaultExpirationTTL <= 0 { - lmt.generalExpirableOptions.DefaultExpirationTTL = 87600 * time.Hour - } - - lmt.tokenBuckets = cache.NewCache[string, *rate.Limiter]().WithTTL(lmt.generalExpirableOptions.DefaultExpirationTTL) - - lmt.basicAuthUsers = cache.NewCache[string, bool]().WithTTL(lmt.generalExpirableOptions.DefaultExpirationTTL) - - return lmt -} - -// Limiter is a config struct to limit a particular request handler. -type Limiter struct { - // Maximum number of requests to limit per second. - max float64 - - // Limiter burst size - burst int - - // HTTP message when limit is reached. - message string - - // Content-Type for Message - messageContentType string - - // HTTP status code when limit is reached. - statusCode int - - // A function to call when a request is rejected. - onLimitReached func(w http.ResponseWriter, r *http.Request) - - // An option to write back what you want upon reaching a limit. - overrideDefaultResponseWriter bool - - // List of places to look up IP address. - // Default is "RemoteAddr", "X-Forwarded-For", "X-Real-IP". - // You can rearrange the order as you like. - ipLookups []string - - forwardedForIndex int - - // List of HTTP Methods to limit (GET, POST, PUT, etc.). - // Empty means limit all methods. - methods []string - - // Able to configure token bucket expirations. - generalExpirableOptions *ExpirableOptions - - // List of basic auth usernames to limit. - basicAuthUsers cache.Cache[string, bool] - - // Map of HTTP headers to limit. - // Empty means skip headers checking. - headers map[string]cache.Cache[string, bool] - - // Map of Context values to limit. - contextValues map[string]cache.Cache[string, bool] - - // Map of limiters with TTL - tokenBuckets cache.Cache[string, *rate.Limiter] - - // Ignore URL on the rate limiter keys - ignoreURL bool - - tokenBucketExpirationTTL time.Duration - basicAuthExpirationTTL time.Duration - headerEntryExpirationTTL time.Duration - contextEntryExpirationTTL time.Duration - - sync.RWMutex -} - -// SetTokenBucketExpirationTTL is thread-safe way of setting custom token bucket expiration TTL. -func (l *Limiter) SetTokenBucketExpirationTTL(ttl time.Duration) *Limiter { - l.Lock() - l.tokenBucketExpirationTTL = ttl - l.Unlock() - - return l -} - -// GetTokenBucketExpirationTTL is thread-safe way of getting custom token bucket expiration TTL. -func (l *Limiter) GetTokenBucketExpirationTTL() time.Duration { - l.RLock() - defer l.RUnlock() - return l.tokenBucketExpirationTTL -} - -// SetBasicAuthExpirationTTL is thread-safe way of setting custom basic auth expiration TTL. -func (l *Limiter) SetBasicAuthExpirationTTL(ttl time.Duration) *Limiter { - l.Lock() - l.basicAuthExpirationTTL = ttl - l.Unlock() - - return l -} - -// GetBasicAuthExpirationTTL is thread-safe way of getting custom basic auth expiration TTL. -func (l *Limiter) GetBasicAuthExpirationTTL() time.Duration { - l.RLock() - defer l.RUnlock() - return l.basicAuthExpirationTTL -} - -// SetHeaderEntryExpirationTTL is thread-safe way of setting custom basic auth expiration TTL. -func (l *Limiter) SetHeaderEntryExpirationTTL(ttl time.Duration) *Limiter { - l.Lock() - l.headerEntryExpirationTTL = ttl - l.Unlock() - - return l -} - -// GetHeaderEntryExpirationTTL is thread-safe way of getting custom basic auth expiration TTL. -func (l *Limiter) GetHeaderEntryExpirationTTL() time.Duration { - l.RLock() - defer l.RUnlock() - return l.headerEntryExpirationTTL -} - -// SetContextValueEntryExpirationTTL is thread-safe way of setting custom Context value expiration TTL. -func (l *Limiter) SetContextValueEntryExpirationTTL(ttl time.Duration) *Limiter { - l.Lock() - l.contextEntryExpirationTTL = ttl - l.Unlock() - - return l -} - -// GetContextValueEntryExpirationTTL is thread-safe way of getting custom Context value expiration TTL. -func (l *Limiter) GetContextValueEntryExpirationTTL() time.Duration { - l.RLock() - defer l.RUnlock() - return l.contextEntryExpirationTTL -} - -// SetMax is thread-safe way of setting maximum number of requests to limit per second. -func (l *Limiter) SetMax(max float64) *Limiter { - l.Lock() - l.max = max - l.Unlock() - - return l -} - -// GetMax is thread-safe way of getting maximum number of requests to limit per second. -func (l *Limiter) GetMax() float64 { - l.RLock() - defer l.RUnlock() - return l.max -} - -// SetBurst is thread-safe way of setting maximum burst size. -func (l *Limiter) SetBurst(burst int) *Limiter { - l.Lock() - l.burst = burst - l.Unlock() - - return l -} - -// GetBurst is thread-safe way of setting maximum burst size. -func (l *Limiter) GetBurst() int { - l.RLock() - defer l.RUnlock() - - return l.burst -} - -// SetMessage is thread-safe way of setting HTTP message when limit is reached. -func (l *Limiter) SetMessage(msg string) *Limiter { - l.Lock() - l.message = msg - l.Unlock() - - return l -} - -// GetMessage is thread-safe way of getting HTTP message when limit is reached. -func (l *Limiter) GetMessage() string { - l.RLock() - defer l.RUnlock() - return l.message -} - -// SetMessageContentType is thread-safe way of setting HTTP message Content-Type when limit is reached. -func (l *Limiter) SetMessageContentType(contentType string) *Limiter { - l.Lock() - l.messageContentType = contentType - l.Unlock() - - return l -} - -// GetMessageContentType is thread-safe way of getting HTTP message Content-Type when limit is reached. -func (l *Limiter) GetMessageContentType() string { - l.RLock() - defer l.RUnlock() - return l.messageContentType -} - -// SetStatusCode is thread-safe way of setting HTTP status code when limit is reached. -func (l *Limiter) SetStatusCode(statusCode int) *Limiter { - l.Lock() - l.statusCode = statusCode - l.Unlock() - - return l -} - -// GetStatusCode is thread-safe way of getting HTTP status code when limit is reached. -func (l *Limiter) GetStatusCode() int { - l.RLock() - defer l.RUnlock() - return l.statusCode -} - -// SetOnLimitReached is thread-safe way of setting after-rejection function when limit is reached. -func (l *Limiter) SetOnLimitReached(fn func(w http.ResponseWriter, r *http.Request)) *Limiter { - l.Lock() - l.onLimitReached = fn - l.Unlock() - - return l -} - -// ExecOnLimitReached is thread-safe way of executing after-rejection function when limit is reached. -func (l *Limiter) ExecOnLimitReached(w http.ResponseWriter, r *http.Request) { - l.RLock() - fn := l.onLimitReached - l.RUnlock() - - if fn != nil { - fn(w, r) - } -} - -// SetOverrideDefaultResponseWriter is a thread-safe way of setting the response writer override variable. -func (l *Limiter) SetOverrideDefaultResponseWriter(override bool) { - l.Lock() - l.overrideDefaultResponseWriter = override - l.Unlock() -} - -// GetOverrideDefaultResponseWriter is a thread-safe way of getting the response writer override variable. -func (l *Limiter) GetOverrideDefaultResponseWriter() bool { - l.RLock() - defer l.RUnlock() - return l.overrideDefaultResponseWriter -} - -// SetIPLookups is thread-safe way of setting list of places to look up IP address. -func (l *Limiter) SetIPLookups(ipLookups []string) *Limiter { - l.Lock() - l.ipLookups = ipLookups - l.Unlock() - - return l -} - -// GetIPLookups is thread-safe way of getting list of places to look up IP address. -func (l *Limiter) GetIPLookups() []string { - l.RLock() - defer l.RUnlock() - return l.ipLookups -} - -// SetIgnoreURL is thread-safe way of setting whenever ignore the URL on rate limit keys -func (l *Limiter) SetIgnoreURL(enabled bool) *Limiter { - l.Lock() - l.ignoreURL = enabled - l.Unlock() - - return l -} - -// GetIgnoreURL returns whether the URL is ignored in the rate limit key set -func (l *Limiter) GetIgnoreURL() bool { - l.RLock() - defer l.RUnlock() - return l.ignoreURL -} - -// SetForwardedForIndexFromBehind is thread-safe way of setting which X-Forwarded-For index to choose. -func (l *Limiter) SetForwardedForIndexFromBehind(forwardedForIndex int) *Limiter { - l.Lock() - l.forwardedForIndex = forwardedForIndex - l.Unlock() - - return l -} - -// GetForwardedForIndexFromBehind is thread-safe way of getting which X-Forwarded-For index to choose. -func (l *Limiter) GetForwardedForIndexFromBehind() int { - l.RLock() - defer l.RUnlock() - return l.forwardedForIndex -} - -// SetMethods is thread-safe way of setting list of HTTP Methods to limit (GET, POST, PUT, etc.). -func (l *Limiter) SetMethods(methods []string) *Limiter { - l.Lock() - l.methods = methods - l.Unlock() - - return l -} - -// GetMethods is thread-safe way of getting list of HTTP Methods to limit (GET, POST, PUT, etc.). -func (l *Limiter) GetMethods() []string { - l.RLock() - defer l.RUnlock() - return l.methods -} - -// SetBasicAuthUsers is thread-safe way of setting list of basic auth usernames to limit. -func (l *Limiter) SetBasicAuthUsers(basicAuthUsers []string) *Limiter { - ttl := l.GetBasicAuthExpirationTTL() - if ttl <= 0 { - ttl = l.generalExpirableOptions.DefaultExpirationTTL - } - - for _, basicAuthUser := range basicAuthUsers { - l.basicAuthUsers.Set(basicAuthUser, true, ttl) - } - - return l -} - -// GetBasicAuthUsers is thread-safe way of getting list of basic auth usernames to limit. -func (l *Limiter) GetBasicAuthUsers() []string { - return l.basicAuthUsers.Keys() -} - -// RemoveBasicAuthUsers is thread-safe way of removing basic auth usernames from existing list. -func (l *Limiter) RemoveBasicAuthUsers(basicAuthUsers []string) *Limiter { - for _, toBeRemoved := range basicAuthUsers { - l.basicAuthUsers.Invalidate(toBeRemoved) - } - - return l -} - -// DeleteExpiredTokenBuckets is thread-safe way of deleting expired token buckets -func (l *Limiter) DeleteExpiredTokenBuckets() { - l.tokenBuckets.DeleteExpired() -} - -// SetHeaders is thread-safe way of setting map of HTTP headers to limit. -func (l *Limiter) SetHeaders(headers map[string][]string) *Limiter { - if l.headers == nil { - l.headers = make(map[string]cache.Cache[string, bool]) - } - - for header, entries := range headers { - l.SetHeader(header, entries) - } - - return l -} - -// GetHeaders is thread-safe way of getting map of HTTP headers to limit. -func (l *Limiter) GetHeaders() map[string][]string { - results := make(map[string][]string) - - l.RLock() - defer l.RUnlock() - - for header, entriesAsGoCache := range l.headers { - results[header] = entriesAsGoCache.Keys() - } - - return results -} - -// SetHeader is thread-safe way of setting entries of 1 HTTP header. -func (l *Limiter) SetHeader(header string, entries []string) *Limiter { - l.RLock() - existing, found := l.headers[header] - l.RUnlock() - - ttl := l.GetHeaderEntryExpirationTTL() - if ttl <= 0 { - ttl = l.generalExpirableOptions.DefaultExpirationTTL - } - - if !found { - existing = cache.NewCache[string, bool]().WithTTL(ttl) - } - - for _, entry := range entries { - existing.Set(entry, true, ttl) - } - - l.Lock() - l.headers[header] = existing - l.Unlock() - - return l -} - -// GetHeader is thread-safe way of getting entries of 1 HTTP header. -func (l *Limiter) GetHeader(header string) []string { - l.RLock() - entriesAsGoCache := l.headers[header] - l.RUnlock() - - return entriesAsGoCache.Keys() -} - -// RemoveHeader is thread-safe way of removing entries of 1 HTTP header. -func (l *Limiter) RemoveHeader(header string) *Limiter { - ttl := l.GetHeaderEntryExpirationTTL() - if ttl <= 0 { - ttl = l.generalExpirableOptions.DefaultExpirationTTL - } - - l.Lock() - l.headers[header] = cache.NewCache[string, bool]().WithTTL(ttl) - l.Unlock() - - return l -} - -// RemoveHeaderEntries is thread-safe way of removing new entries to 1 HTTP header rule. -func (l *Limiter) RemoveHeaderEntries(header string, entriesForRemoval []string) *Limiter { - l.RLock() - entries, found := l.headers[header] - l.RUnlock() - - if !found { - return l - } - - for _, toBeRemoved := range entriesForRemoval { - entries.Invalidate(toBeRemoved) - } - - return l -} - -// SetContextValues is thread-safe way of setting map of HTTP headers to limit. -func (l *Limiter) SetContextValues(contextValues map[string][]string) *Limiter { - if l.contextValues == nil { - l.contextValues = make(map[string]cache.Cache[string, bool]) - } - - for contextValue, entries := range contextValues { - l.SetContextValue(contextValue, entries) - } - - return l -} - -// GetContextValues is thread-safe way of getting a map of Context values to limit. -func (l *Limiter) GetContextValues() map[string][]string { - results := make(map[string][]string) - - l.RLock() - defer l.RUnlock() - - for contextValue, entriesAsGoCache := range l.contextValues { - results[contextValue] = entriesAsGoCache.Keys() - } - - return results -} - -// SetContextValue is thread-safe way of setting entries of 1 Context value. -func (l *Limiter) SetContextValue(contextValue string, entries []string) *Limiter { - l.RLock() - existing, found := l.contextValues[contextValue] - l.RUnlock() - - ttl := l.GetContextValueEntryExpirationTTL() - if ttl <= 0 { - ttl = l.generalExpirableOptions.DefaultExpirationTTL - } - - if !found { - existing = cache.NewCache[string, bool]().WithTTL(ttl) - } - - for _, entry := range entries { - existing.Set(entry, true, ttl) - } - - l.Lock() - l.contextValues[contextValue] = existing - l.Unlock() - - return l -} - -// GetContextValue is thread-safe way of getting 1 Context value entry. -func (l *Limiter) GetContextValue(contextValue string) []string { - l.RLock() - entriesAsGoCache := l.contextValues[contextValue] - l.RUnlock() - - return entriesAsGoCache.Keys() -} - -// RemoveContextValue is thread-safe way of removing entries of 1 Context value. -func (l *Limiter) RemoveContextValue(contextValue string) *Limiter { - ttl := l.GetContextValueEntryExpirationTTL() - if ttl <= 0 { - ttl = l.generalExpirableOptions.DefaultExpirationTTL - } - - l.Lock() - l.contextValues[contextValue] = cache.NewCache[string, bool]().WithTTL(ttl) - l.Unlock() - - return l -} - -// RemoveContextValuesEntries is thread-safe way of removing entries to a ContextValue. -func (l *Limiter) RemoveContextValuesEntries(contextValue string, entriesForRemoval []string) *Limiter { - l.RLock() - entries, found := l.contextValues[contextValue] - l.RUnlock() - - if !found { - return l - } - - for _, toBeRemoved := range entriesForRemoval { - entries.Invalidate(toBeRemoved) - } - - return l -} - -func (l *Limiter) limitReachedWithTokenBucketTTL(key string, tokenBucketTTL time.Duration) bool { - lmtMax := l.GetMax() - lmtBurst := l.GetBurst() - l.Lock() - defer l.Unlock() - - if _, found := l.tokenBuckets.Get(key); !found { - l.tokenBuckets.Set( - key, - rate.NewLimiter(rate.Limit(lmtMax), lmtBurst), - tokenBucketTTL, - ) - } - - expiringMap, found := l.tokenBuckets.Get(key) - if !found { - return false - } - - return !expiringMap.Allow() -} - -// LimitReached returns a bool indicating if the Bucket identified by key ran out of tokens. -func (l *Limiter) LimitReached(key string) bool { - ttl := l.GetTokenBucketExpirationTTL() - - if ttl <= 0 { - ttl = l.generalExpirableOptions.DefaultExpirationTTL - } - - return l.limitReachedWithTokenBucketTTL(key, ttl) -} - -// Tokens returns current amount of tokens left in the Bucket identified by key. -func (l *Limiter) Tokens(key string) int { - expiringMap, found := l.tokenBuckets.Get(key) - if !found { - return 0 - } - - return int(expiringMap.TokensAt(time.Now())) -} diff --git a/backend/vendor/github.com/didip/tollbooth/v7/limiter/limiter_options.go b/backend/vendor/github.com/didip/tollbooth/v7/limiter/limiter_options.go deleted file mode 100644 index e5f537b5..00000000 --- a/backend/vendor/github.com/didip/tollbooth/v7/limiter/limiter_options.go +++ /dev/null @@ -1,14 +0,0 @@ -package limiter - -import ( - "time" -) - -// ExpirableOptions are options used for new limiter creation -type ExpirableOptions struct { - DefaultExpirationTTL time.Duration - - // How frequently expire job triggers - // Deprecated: not used anymore - ExpireJobInterval time.Duration -} diff --git a/backend/vendor/github.com/didip/tollbooth/v7/tollbooth.go b/backend/vendor/github.com/didip/tollbooth/v7/tollbooth.go deleted file mode 100644 index 0dcf82e0..00000000 --- a/backend/vendor/github.com/didip/tollbooth/v7/tollbooth.go +++ /dev/null @@ -1,349 +0,0 @@ -// Package tollbooth provides rate-limiting logic to HTTP request handler. -package tollbooth - -import ( - "fmt" - "math" - "net/http" - "strings" - - "github.com/didip/tollbooth/v7/errors" - "github.com/didip/tollbooth/v7/libstring" - "github.com/didip/tollbooth/v7/limiter" -) - -// setResponseHeaders configures X-Rate-Limit-Limit and X-Rate-Limit-Duration -func setResponseHeaders(lmt *limiter.Limiter, w http.ResponseWriter, r *http.Request) { - w.Header().Add("X-Rate-Limit-Limit", fmt.Sprintf("%.2f", lmt.GetMax())) - w.Header().Add("X-Rate-Limit-Duration", "1") - - xForwardedFor := r.Header.Get("X-Forwarded-For") - if strings.TrimSpace(xForwardedFor) != "" { - w.Header().Add("X-Rate-Limit-Request-Forwarded-For", xForwardedFor) - } - - w.Header().Add("X-Rate-Limit-Request-Remote-Addr", r.RemoteAddr) -} - -// setRateLimitResponseHeaders configures RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset -// as seen at https://datatracker.ietf.org/doc/html/draft-ietf-httpapi-ratelimit-headers -func setRateLimitResponseHeaders(lmt *limiter.Limiter, w http.ResponseWriter, tokensLeft int) { - w.Header().Add("RateLimit-Limit", fmt.Sprintf("%d", int(math.Round(lmt.GetMax())))) - w.Header().Add("RateLimit-Reset", "1") - w.Header().Add("RateLimit-Remaining", fmt.Sprintf("%d", tokensLeft)) -} - -// NewLimiter is a convenience function to limiter.New. -func NewLimiter(max float64, tbOptions *limiter.ExpirableOptions) *limiter.Limiter { - return limiter.New(tbOptions). - SetMax(max). - SetBurst(int(math.Max(1, max))). - SetIPLookups([]string{"X-Forwarded-For", "X-Real-IP", "RemoteAddr"}) -} - -// LimitByKeys keeps track number of request made by keys separated by pipe. -// It returns HTTPError when limit is exceeded. -func LimitByKeys(lmt *limiter.Limiter, keys []string) *errors.HTTPError { - err, _ := LimitByKeysAndReturn(lmt, keys) - return err -} - -// LimitByKeysAndReturn keeps track number of request made by keys separated by pipe. -// It returns HTTPError when limit is exceeded, and also returns the current limit value. -func LimitByKeysAndReturn(lmt *limiter.Limiter, keys []string) (*errors.HTTPError, int) { - if lmt.LimitReached(strings.Join(keys, "|")) { - return &errors.HTTPError{Message: lmt.GetMessage(), StatusCode: lmt.GetStatusCode()}, 0 - } - - return nil, lmt.Tokens(strings.Join(keys, "|")) -} - -// ShouldSkipLimiter is a series of filter that decides if request should be limited or not. -func ShouldSkipLimiter(lmt *limiter.Limiter, r *http.Request) bool { - // --------------------------------- - // Filter by remote ip - // If we are unable to find remoteIP, skip limiter - remoteIP := libstring.RemoteIP(lmt.GetIPLookups(), lmt.GetForwardedForIndexFromBehind(), r) - remoteIP = libstring.CanonicalizeIP(remoteIP) - if remoteIP == "" { - return true - } - - // --------------------------------- - // Filter by request method - lmtMethods := lmt.GetMethods() - lmtMethodsIsSet := len(lmtMethods) > 0 - - if lmtMethodsIsSet { - // If request does not contain all of the methods in limiter, - // skip limiter - requestMethodDefinedInLimiter := libstring.StringInSlice(lmtMethods, r.Method) - - if !requestMethodDefinedInLimiter { - return true - } - } - - // --------------------------------- - // Filter by request headers - lmtHeaders := lmt.GetHeaders() - lmtHeadersIsSet := len(lmtHeaders) > 0 - - if lmtHeadersIsSet { - // If request does not contain all of the headers in limiter, - // skip limiter - requestHeadersDefinedInLimiter := false - - for headerKey := range lmtHeaders { - reqHeaderValue := r.Header.Get(headerKey) - if reqHeaderValue != "" { - requestHeadersDefinedInLimiter = true - break - } - } - - if !requestHeadersDefinedInLimiter { - return true - } - - // ------------------------------ - // If request contains the header key but not the values, - // skip limiter - requestHeadersDefinedInLimiter = false - - for headerKey, headerValues := range lmtHeaders { - if len(headerValues) == 0 { - requestHeadersDefinedInLimiter = true - continue - } - for _, headerValue := range headerValues { - if r.Header.Get(headerKey) == headerValue { - requestHeadersDefinedInLimiter = true - break - } - } - } - - if !requestHeadersDefinedInLimiter { - return true - } - } - - // --------------------------------- - // Filter by context values - lmtContextValues := lmt.GetContextValues() - lmtContextValuesIsSet := len(lmtContextValues) > 0 - - if lmtContextValuesIsSet { - // If request does not contain all of the contexts in limiter, - // skip limiter - requestContextValuesDefinedInLimiter := false - - for contextKey := range lmtContextValues { - reqContextValue := fmt.Sprintf("%v", r.Context().Value(contextKey)) - if reqContextValue != "" { - requestContextValuesDefinedInLimiter = true - break - } - } - - if !requestContextValuesDefinedInLimiter { - return true - } - - // ------------------------------ - // If request contains the context key but not the values, - // skip limiter - requestContextValuesDefinedInLimiter = false - - for contextKey, contextValues := range lmtContextValues { - for _, contextValue := range contextValues { - if r.Header.Get(contextKey) == contextValue { - requestContextValuesDefinedInLimiter = true - break - } - } - } - - if !requestContextValuesDefinedInLimiter { - return true - } - } - - // --------------------------------- - // Filter by basic auth usernames - lmtBasicAuthUsers := lmt.GetBasicAuthUsers() - lmtBasicAuthUsersIsSet := len(lmtBasicAuthUsers) > 0 - - if lmtBasicAuthUsersIsSet { - // If request does not contain all of the basic auth users in limiter, - // skip limiter - requestAuthUsernameDefinedInLimiter := false - - username, _, ok := r.BasicAuth() - if ok && libstring.StringInSlice(lmtBasicAuthUsers, username) { - requestAuthUsernameDefinedInLimiter = true - } - - if !requestAuthUsernameDefinedInLimiter { - return true - } - } - - return false -} - -// BuildKeys generates a slice of keys to rate-limit by given limiter and request structs. -func BuildKeys(lmt *limiter.Limiter, r *http.Request) [][]string { - remoteIP := libstring.RemoteIP(lmt.GetIPLookups(), lmt.GetForwardedForIndexFromBehind(), r) - remoteIP = libstring.CanonicalizeIP(remoteIP) - path := r.URL.Path - sliceKeys := make([][]string, 0) - - lmtMethods := lmt.GetMethods() - lmtHeaders := lmt.GetHeaders() - lmtContextValues := lmt.GetContextValues() - lmtBasicAuthUsers := lmt.GetBasicAuthUsers() - lmtIgnoreURL := lmt.GetIgnoreURL() - - lmtHeadersIsSet := len(lmtHeaders) > 0 - lmtContextValuesIsSet := len(lmtContextValues) > 0 - lmtBasicAuthUsersIsSet := len(lmtBasicAuthUsers) > 0 - - usernameToLimit := "" - if lmtBasicAuthUsersIsSet { - username, _, ok := r.BasicAuth() - if ok && libstring.StringInSlice(lmtBasicAuthUsers, username) { - usernameToLimit = username - } - } - - headerValuesToLimit := [][]string{} - if lmtHeadersIsSet { - for headerKey, headerValues := range lmtHeaders { - reqHeaderValue := r.Header.Get(headerKey) - if reqHeaderValue == "" { - continue - } - - if len(headerValues) == 0 { - // If header values are empty, rate-limit all request containing headerKey. - headerValuesToLimit = append(headerValuesToLimit, []string{headerKey, reqHeaderValue}) - - } else { - // If header values are not empty, rate-limit all request with headerKey and headerValues. - for _, headerValue := range headerValues { - if r.Header.Get(headerKey) == headerValue { - headerValuesToLimit = append(headerValuesToLimit, []string{headerKey, headerValue}) - break - } - } - } - } - } - - contextValuesToLimit := [][]string{} - if lmtContextValuesIsSet { - for contextKey, contextValues := range lmtContextValues { - reqContextValue := fmt.Sprintf("%v", r.Context().Value(contextKey)) - if reqContextValue == "" { - continue - } - - if len(contextValues) == 0 { - // If context values are empty, rate-limit all request containing contextKey. - contextValuesToLimit = append(contextValuesToLimit, []string{contextKey, reqContextValue}) - - } else { - // If context values are not empty, rate-limit all request with contextKey and contextValues. - for _, contextValue := range contextValues { - if reqContextValue == contextValue { - contextValuesToLimit = append(contextValuesToLimit, []string{contextKey, contextValue}) - break - } - } - } - } - } - - sliceKey := []string{remoteIP} - if !lmtIgnoreURL { - sliceKey = append(sliceKey, path) - } - - sliceKey = append(sliceKey, lmtMethods...) - - for _, header := range headerValuesToLimit { - sliceKey = append(sliceKey, header[0], header[1]) - } - - for _, contextValue := range contextValuesToLimit { - sliceKey = append(sliceKey, contextValue[0], contextValue[1]) - } - - sliceKey = append(sliceKey, usernameToLimit) - - sliceKeys = append(sliceKeys, sliceKey) - - return sliceKeys -} - -// LimitByRequest builds keys based on http.Request struct, -// loops through all the keys, and check if any one of them returns HTTPError. -func LimitByRequest(lmt *limiter.Limiter, w http.ResponseWriter, r *http.Request) *errors.HTTPError { - setResponseHeaders(lmt, w, r) - - shouldSkip := ShouldSkipLimiter(lmt, r) - if shouldSkip { - return nil - } - - sliceKeys := BuildKeys(lmt, r) - - // Get the lowest value over all keys to return in headers. - // Start with high arbitrary number so that any limit returned would be lower and would - // overwrite the value we start with. - var tokensLeft = math.MaxInt32 - - // Loop sliceKeys and check if one of them has error. - for _, keys := range sliceKeys { - httpError, keysLimit := LimitByKeysAndReturn(lmt, keys) - if tokensLeft > keysLimit { - tokensLeft = keysLimit - } - if httpError != nil { - setRateLimitResponseHeaders(lmt, w, tokensLeft) - return httpError - } - } - - setRateLimitResponseHeaders(lmt, w, tokensLeft) - return nil -} - -// LimitHandler is a middleware that performs rate-limiting given http.Handler struct. -func LimitHandler(lmt *limiter.Limiter, next http.Handler) http.Handler { - middle := func(w http.ResponseWriter, r *http.Request) { - httpError := LimitByRequest(lmt, w, r) - if httpError != nil { - lmt.ExecOnLimitReached(w, r) - if lmt.GetOverrideDefaultResponseWriter() { - return - } - w.Header().Add("Content-Type", lmt.GetMessageContentType()) - w.WriteHeader(httpError.StatusCode) - w.Write([]byte(httpError.Message)) - return - } - - // There's no rate-limit error, serve the next handler. - next.ServeHTTP(w, r) - } - - return http.HandlerFunc(middle) -} - -// LimitFuncHandler is a middleware that performs rate-limiting given request handler function. -func LimitFuncHandler(lmt *limiter.Limiter, nextFunc func(http.ResponseWriter, *http.Request)) http.Handler { - return LimitHandler(lmt, http.HandlerFunc(nextFunc)) -} diff --git a/backend/vendor/github.com/didip/tollbooth_chi/README.md b/backend/vendor/github.com/didip/tollbooth_chi/README.md deleted file mode 100644 index d1ba02b1..00000000 --- a/backend/vendor/github.com/didip/tollbooth_chi/README.md +++ /dev/null @@ -1,33 +0,0 @@ -## tollbooth_chi - -[Chi](https://github.com/pressly/chi) middleware for rate limiting HTTP requests. - - -## Five Minutes Tutorial - -``` -package main - -import ( - "github.com/didip/tollbooth" - "github.com/didip/tollbooth_chi" - "github.com/pressly/chi" - "net/http" - "time" -) - -func main() { - // Create a limiter struct. - limiter := tollbooth.NewLimiter(1, nil) - - r := chi.NewRouter() - - r.Use(tollbooth_chi.LimitHandler(limiter)) - - r.Get("/", func(w http.ResponseWriter, r *http.Request) { - w.Write([]byte("Hello, world!")) - }) - - http.ListenAndServe(":12345", r) -} -``` diff --git a/backend/vendor/github.com/didip/tollbooth_chi/tollbooth_chi.go b/backend/vendor/github.com/didip/tollbooth_chi/tollbooth_chi.go deleted file mode 100644 index 7e45c9a6..00000000 --- a/backend/vendor/github.com/didip/tollbooth_chi/tollbooth_chi.go +++ /dev/null @@ -1,45 +0,0 @@ -package tollbooth_chi - -import ( - "net/http" - - "github.com/didip/tollbooth/v7" - "github.com/didip/tollbooth/v7/limiter" -) - -func LimitHandler(lmt *limiter.Limiter) func(http.Handler) http.Handler { - return func(handler http.Handler) http.Handler { - wrapper := &limiterWrapper{ - lmt: lmt, - } - - wrapper.handler = handler - return wrapper - } -} - -type limiterWrapper struct { - lmt *limiter.Limiter - handler http.Handler -} - -func (l *limiterWrapper) ServeHTTP(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - select { - case <-ctx.Done(): - http.Error(w, "Context was canceled", http.StatusServiceUnavailable) - return - - default: - httpError := tollbooth.LimitByRequest(l.lmt, w, r) - if httpError != nil { - l.lmt.ExecOnLimitReached(w, r) - w.Header().Add("Content-Type", l.lmt.GetMessageContentType()) - w.WriteHeader(httpError.StatusCode) - w.Write([]byte(httpError.Message)) - return - } - - l.handler.ServeHTTP(w, r) - } -} diff --git a/backend/vendor/github.com/go-chi/chi/v5/.gitignore b/backend/vendor/github.com/go-chi/chi/v5/.gitignore deleted file mode 100644 index ba22c99a..00000000 --- a/backend/vendor/github.com/go-chi/chi/v5/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -.idea -*.sw? -.vscode diff --git a/backend/vendor/github.com/go-chi/chi/v5/CHANGELOG.md b/backend/vendor/github.com/go-chi/chi/v5/CHANGELOG.md deleted file mode 100644 index 25b45b97..00000000 --- a/backend/vendor/github.com/go-chi/chi/v5/CHANGELOG.md +++ /dev/null @@ -1,341 +0,0 @@ -# Changelog - -## v5.0.12 (2024-02-16) - -- History of changes: see https://github.com/go-chi/chi/compare/v5.0.11...v5.0.12 - - -## v5.0.11 (2023-12-19) - -- History of changes: see https://github.com/go-chi/chi/compare/v5.0.10...v5.0.11 - - -## v5.0.10 (2023-07-13) - -- Fixed small edge case in tests of v5.0.9 for older Go versions -- History of changes: see https://github.com/go-chi/chi/compare/v5.0.9...v5.0.10 - - -## v5.0.9 (2023-07-13) - -- History of changes: see https://github.com/go-chi/chi/compare/v5.0.8...v5.0.9 - - -## v5.0.8 (2022-12-07) - -- History of changes: see https://github.com/go-chi/chi/compare/v5.0.7...v5.0.8 - - -## v5.0.7 (2021-11-18) - -- History of changes: see https://github.com/go-chi/chi/compare/v5.0.6...v5.0.7 - - -## v5.0.6 (2021-11-15) - -- History of changes: see https://github.com/go-chi/chi/compare/v5.0.5...v5.0.6 - - -## v5.0.5 (2021-10-27) - -- History of changes: see https://github.com/go-chi/chi/compare/v5.0.4...v5.0.5 - - -## v5.0.4 (2021-08-29) - -- History of changes: see https://github.com/go-chi/chi/compare/v5.0.3...v5.0.4 - - -## v5.0.3 (2021-04-29) - -- History of changes: see https://github.com/go-chi/chi/compare/v5.0.2...v5.0.3 - - -## v5.0.2 (2021-03-25) - -- History of changes: see https://github.com/go-chi/chi/compare/v5.0.1...v5.0.2 - - -## v5.0.1 (2021-03-10) - -- Small improvements -- History of changes: see https://github.com/go-chi/chi/compare/v5.0.0...v5.0.1 - - -## v5.0.0 (2021-02-27) - -- chi v5, `github.com/go-chi/chi/v5` introduces the adoption of Go's SIV to adhere to the current state-of-the-tools in Go. -- chi v1.5.x did not work out as planned, as the Go tooling is too powerful and chi's adoption is too wide. - The most responsible thing to do for everyone's benefit is to just release v5 with SIV, so I present to you all, - chi v5 at `github.com/go-chi/chi/v5`. I hope someday the developer experience and ergonomics I've been seeking - will still come to fruition in some form, see https://github.com/golang/go/issues/44550 -- History of changes: see https://github.com/go-chi/chi/compare/v1.5.4...v5.0.0 - - -## v1.5.4 (2021-02-27) - -- Undo prior retraction in v1.5.3 as we prepare for v5.0.0 release -- History of changes: see https://github.com/go-chi/chi/compare/v1.5.3...v1.5.4 - - -## v1.5.3 (2021-02-21) - -- Update go.mod to go 1.16 with new retract directive marking all versions without prior go.mod support -- History of changes: see https://github.com/go-chi/chi/compare/v1.5.2...v1.5.3 - - -## v1.5.2 (2021-02-10) - -- Reverting allocation optimization as a precaution as go test -race fails. -- Minor improvements, see history below -- History of changes: see https://github.com/go-chi/chi/compare/v1.5.1...v1.5.2 - - -## v1.5.1 (2020-12-06) - -- Performance improvement: removing 1 allocation by foregoing context.WithValue, thank you @bouk for - your contribution (https://github.com/go-chi/chi/pull/555). Note: new benchmarks posted in README. -- `middleware.CleanPath`: new middleware that clean's request path of double slashes -- deprecate & remove `chi.ServerBaseContext` in favour of stdlib `http.Server#BaseContext` -- plus other tiny improvements, see full commit history below -- History of changes: see https://github.com/go-chi/chi/compare/v4.1.2...v1.5.1 - - -## v1.5.0 (2020-11-12) - now with go.mod support - -`chi` dates back to 2016 with it's original implementation as one of the first routers to adopt the newly introduced -context.Context api to the stdlib -- set out to design a router that is faster, more modular and simpler than anything -else out there -- while not introducing any custom handler types or dependencies. Today, `chi` still has zero dependencies, -and in many ways is future proofed from changes, given it's minimal nature. Between versions, chi's iterations have been very -incremental, with the architecture and api being the same today as it was originally designed in 2016. For this reason it -makes chi a pretty easy project to maintain, as well thanks to the many amazing community contributions over the years -to who all help make chi better (total of 86 contributors to date -- thanks all!). - -Chi has been a labour of love, art and engineering, with the goals to offer beautiful ergonomics, flexibility, performance -and simplicity when building HTTP services with Go. I've strived to keep the router very minimal in surface area / code size, -and always improving the code wherever possible -- and as of today the `chi` package is just 1082 lines of code (not counting -middlewares, which are all optional). As well, I don't have the exact metrics, but from my analysis and email exchanges from -companies and developers, chi is used by thousands of projects around the world -- thank you all as there is no better form of -joy for me than to have art I had started be helpful and enjoyed by others. And of course I use chi in all of my own projects too :) - -For me, the aesthetics of chi's code and usage are very important. With the introduction of Go's module support -(which I'm a big fan of), chi's past versioning scheme choice to v2, v3 and v4 would mean I'd require the import path -of "github.com/go-chi/chi/v4", leading to the lengthy discussion at https://github.com/go-chi/chi/issues/462. -Haha, to some, you may be scratching your head why I've spent > 1 year stalling to adopt "/vXX" convention in the import -path -- which isn't horrible in general -- but for chi, I'm unable to accept it as I strive for perfection in it's API design, -aesthetics and simplicity. It just doesn't feel good to me given chi's simple nature -- I do not foresee a "v5" or "v6", -and upgrading between versions in the future will also be just incremental. - -I do understand versioning is a part of the API design as well, which is why the solution for a while has been to "do nothing", -as Go supports both old and new import paths with/out go.mod. However, now that Go module support has had time to iron out kinks and -is adopted everywhere, it's time for chi to get with the times. Luckily, I've discovered a path forward that will make me happy, -while also not breaking anyone's app who adopted a prior versioning from tags in v2/v3/v4. I've made an experimental release of -v1.5.0 with go.mod silently, and tested it with new and old projects, to ensure the developer experience is preserved, and it's -largely unnoticed. Fortunately, Go's toolchain will check the tags of a repo and consider the "latest" tag the one with go.mod. -However, you can still request a specific older tag such as v4.1.2, and everything will "just work". But new users can just -`go get github.com/go-chi/chi` or `go get github.com/go-chi/chi@latest` and they will get the latest version which contains -go.mod support, which is v1.5.0+. `chi` will not change very much over the years, just like it hasn't changed much from 4 years ago. -Therefore, we will stay on v1.x from here on, starting from v1.5.0. Any breaking changes will bump a "minor" release and -backwards-compatible improvements/fixes will bump a "tiny" release. - -For existing projects who want to upgrade to the latest go.mod version, run: `go get -u github.com/go-chi/chi@v1.5.0`, -which will get you on the go.mod version line (as Go's mod cache may still remember v4.x). Brand new systems can run -`go get -u github.com/go-chi/chi` or `go get -u github.com/go-chi/chi@latest` to install chi, which will install v1.5.0+ -built with go.mod support. - -My apologies to the developers who will disagree with the decisions above, but, hope you'll try it and see it's a very -minor request which is backwards compatible and won't break your existing installations. - -Cheers all, happy coding! - - ---- - - -## v4.1.2 (2020-06-02) - -- fix that handles MethodNotAllowed with path variables, thank you @caseyhadden for your contribution -- fix to replace nested wildcards correctly in RoutePattern, thank you @@unmultimedio for your contribution -- History of changes: see https://github.com/go-chi/chi/compare/v4.1.1...v4.1.2 - - -## v4.1.1 (2020-04-16) - -- fix for issue https://github.com/go-chi/chi/issues/411 which allows for overlapping regexp - route to the correct handler through a recursive tree search, thanks to @Jahaja for the PR/fix! -- new middleware.RouteHeaders as a simple router for request headers with wildcard support -- History of changes: see https://github.com/go-chi/chi/compare/v4.1.0...v4.1.1 - - -## v4.1.0 (2020-04-1) - -- middleware.LogEntry: Write method on interface now passes the response header - and an extra interface type useful for custom logger implementations. -- middleware.WrapResponseWriter: minor fix -- middleware.Recoverer: a bit prettier -- History of changes: see https://github.com/go-chi/chi/compare/v4.0.4...v4.1.0 - -## v4.0.4 (2020-03-24) - -- middleware.Recoverer: new pretty stack trace printing (https://github.com/go-chi/chi/pull/496) -- a few minor improvements and fixes -- History of changes: see https://github.com/go-chi/chi/compare/v4.0.3...v4.0.4 - - -## v4.0.3 (2020-01-09) - -- core: fix regexp routing to include default value when param is not matched -- middleware: rewrite of middleware.Compress -- middleware: suppress http.ErrAbortHandler in middleware.Recoverer -- History of changes: see https://github.com/go-chi/chi/compare/v4.0.2...v4.0.3 - - -## v4.0.2 (2019-02-26) - -- Minor fixes -- History of changes: see https://github.com/go-chi/chi/compare/v4.0.1...v4.0.2 - - -## v4.0.1 (2019-01-21) - -- Fixes issue with compress middleware: #382 #385 -- History of changes: see https://github.com/go-chi/chi/compare/v4.0.0...v4.0.1 - - -## v4.0.0 (2019-01-10) - -- chi v4 requires Go 1.10.3+ (or Go 1.9.7+) - we have deprecated support for Go 1.7 and 1.8 -- router: respond with 404 on router with no routes (#362) -- router: additional check to ensure wildcard is at the end of a url pattern (#333) -- middleware: deprecate use of http.CloseNotifier (#347) -- middleware: fix RedirectSlashes to include query params on redirect (#334) -- History of changes: see https://github.com/go-chi/chi/compare/v3.3.4...v4.0.0 - - -## v3.3.4 (2019-01-07) - -- Minor middleware improvements. No changes to core library/router. Moving v3 into its -- own branch as a version of chi for Go 1.7, 1.8, 1.9, 1.10, 1.11 -- History of changes: see https://github.com/go-chi/chi/compare/v3.3.3...v3.3.4 - - -## v3.3.3 (2018-08-27) - -- Minor release -- See https://github.com/go-chi/chi/compare/v3.3.2...v3.3.3 - - -## v3.3.2 (2017-12-22) - -- Support to route trailing slashes on mounted sub-routers (#281) -- middleware: new `ContentCharset` to check matching charsets. Thank you - @csucu for your community contribution! - - -## v3.3.1 (2017-11-20) - -- middleware: new `AllowContentType` handler for explicit whitelist of accepted request Content-Types -- middleware: new `SetHeader` handler for short-hand middleware to set a response header key/value -- Minor bug fixes - - -## v3.3.0 (2017-10-10) - -- New chi.RegisterMethod(method) to add support for custom HTTP methods, see _examples/custom-method for usage -- Deprecated LINK and UNLINK methods from the default list, please use `chi.RegisterMethod("LINK")` and `chi.RegisterMethod("UNLINK")` in an `init()` function - - -## v3.2.1 (2017-08-31) - -- Add new `Match(rctx *Context, method, path string) bool` method to `Routes` interface - and `Mux`. Match searches the mux's routing tree for a handler that matches the method/path -- Add new `RouteMethod` to `*Context` -- Add new `Routes` pointer to `*Context` -- Add new `middleware.GetHead` to route missing HEAD requests to GET handler -- Updated benchmarks (see README) - - -## v3.1.5 (2017-08-02) - -- Setup golint and go vet for the project -- As per golint, we've redefined `func ServerBaseContext(h http.Handler, baseCtx context.Context) http.Handler` - to `func ServerBaseContext(baseCtx context.Context, h http.Handler) http.Handler` - - -## v3.1.0 (2017-07-10) - -- Fix a few minor issues after v3 release -- Move `docgen` sub-pkg to https://github.com/go-chi/docgen -- Move `render` sub-pkg to https://github.com/go-chi/render -- Add new `URLFormat` handler to chi/middleware sub-pkg to make working with url mime - suffixes easier, ie. parsing `/articles/1.json` and `/articles/1.xml`. See comments in - https://github.com/go-chi/chi/blob/master/middleware/url_format.go for example usage. - - -## v3.0.0 (2017-06-21) - -- Major update to chi library with many exciting updates, but also some *breaking changes* -- URL parameter syntax changed from `/:id` to `/{id}` for even more flexible routing, such as - `/articles/{month}-{day}-{year}-{slug}`, `/articles/{id}`, and `/articles/{id}.{ext}` on the - same router -- Support for regexp for routing patterns, in the form of `/{paramKey:regExp}` for example: - `r.Get("/articles/{name:[a-z]+}", h)` and `chi.URLParam(r, "name")` -- Add `Method` and `MethodFunc` to `chi.Router` to allow routing definitions such as - `r.Method("GET", "/", h)` which provides a cleaner interface for custom handlers like - in `_examples/custom-handler` -- Deprecating `mux#FileServer` helper function. Instead, we encourage users to create their - own using file handler with the stdlib, see `_examples/fileserver` for an example -- Add support for LINK/UNLINK http methods via `r.Method()` and `r.MethodFunc()` -- Moved the chi project to its own organization, to allow chi-related community packages to - be easily discovered and supported, at: https://github.com/go-chi -- *NOTE:* please update your import paths to `"github.com/go-chi/chi"` -- *NOTE:* chi v2 is still available at https://github.com/go-chi/chi/tree/v2 - - -## v2.1.0 (2017-03-30) - -- Minor improvements and update to the chi core library -- Introduced a brand new `chi/render` sub-package to complete the story of building - APIs to offer a pattern for managing well-defined request / response payloads. Please - check out the updated `_examples/rest` example for how it works. -- Added `MethodNotAllowed(h http.HandlerFunc)` to chi.Router interface - - -## v2.0.0 (2017-01-06) - -- After many months of v2 being in an RC state with many companies and users running it in - production, the inclusion of some improvements to the middlewares, we are very pleased to - announce v2.0.0 of chi. - - -## v2.0.0-rc1 (2016-07-26) - -- Huge update! chi v2 is a large refactor targeting Go 1.7+. As of Go 1.7, the popular - community `"net/context"` package has been included in the standard library as `"context"` and - utilized by `"net/http"` and `http.Request` to managing deadlines, cancelation signals and other - request-scoped values. We're very excited about the new context addition and are proud to - introduce chi v2, a minimal and powerful routing package for building large HTTP services, - with zero external dependencies. Chi focuses on idiomatic design and encourages the use of - stdlib HTTP handlers and middlewares. -- chi v2 deprecates its `chi.Handler` interface and requires `http.Handler` or `http.HandlerFunc` -- chi v2 stores URL routing parameters and patterns in the standard request context: `r.Context()` -- chi v2 lower-level routing context is accessible by `chi.RouteContext(r.Context()) *chi.Context`, - which provides direct access to URL routing parameters, the routing path and the matching - routing patterns. -- Users upgrading from chi v1 to v2, need to: - 1. Update the old chi.Handler signature, `func(ctx context.Context, w http.ResponseWriter, r *http.Request)` to - the standard http.Handler: `func(w http.ResponseWriter, r *http.Request)` - 2. Use `chi.URLParam(r *http.Request, paramKey string) string` - or `URLParamFromCtx(ctx context.Context, paramKey string) string` to access a url parameter value - - -## v1.0.0 (2016-07-01) - -- Released chi v1 stable https://github.com/go-chi/chi/tree/v1.0.0 for Go 1.6 and older. - - -## v0.9.0 (2016-03-31) - -- Reuse context objects via sync.Pool for zero-allocation routing [#33](https://github.com/go-chi/chi/pull/33) -- BREAKING NOTE: due to subtle API changes, previously `chi.URLParams(ctx)["id"]` used to access url parameters - has changed to: `chi.URLParam(ctx, "id")` diff --git a/backend/vendor/github.com/go-chi/chi/v5/CONTRIBUTING.md b/backend/vendor/github.com/go-chi/chi/v5/CONTRIBUTING.md deleted file mode 100644 index b4a6268d..00000000 --- a/backend/vendor/github.com/go-chi/chi/v5/CONTRIBUTING.md +++ /dev/null @@ -1,31 +0,0 @@ -# Contributing - -## Prerequisites - -1. [Install Go][go-install]. -2. Download the sources and switch the working directory: - - ```bash - go get -u -d github.com/go-chi/chi - cd $GOPATH/src/github.com/go-chi/chi - ``` - -## Submitting a Pull Request - -A typical workflow is: - -1. [Fork the repository.][fork] -2. [Create a topic branch.][branch] -3. Add tests for your change. -4. Run `go test`. If your tests pass, return to the step 3. -5. Implement the change and ensure the steps from the previous step pass. -6. Run `goimports -w .`, to ensure the new code conforms to Go formatting guideline. -7. [Add, commit and push your changes.][git-help] -8. [Submit a pull request.][pull-req] - -[go-install]: https://golang.org/doc/install -[fork]: https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/fork-a-repo -[branch]: https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-branches -[git-help]: https://docs.github.com/en -[pull-req]: https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests - diff --git a/backend/vendor/github.com/go-chi/chi/v5/LICENSE b/backend/vendor/github.com/go-chi/chi/v5/LICENSE deleted file mode 100644 index d99f02ff..00000000 --- a/backend/vendor/github.com/go-chi/chi/v5/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -Copyright (c) 2015-present Peter Kieltyka (https://github.com/pkieltyka), Google Inc. - -MIT License - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/backend/vendor/github.com/go-chi/chi/v5/Makefile b/backend/vendor/github.com/go-chi/chi/v5/Makefile deleted file mode 100644 index e0f18c7d..00000000 --- a/backend/vendor/github.com/go-chi/chi/v5/Makefile +++ /dev/null @@ -1,22 +0,0 @@ -.PHONY: all -all: - @echo "**********************************************************" - @echo "** chi build tool **" - @echo "**********************************************************" - - -.PHONY: test -test: - go clean -testcache && $(MAKE) test-router && $(MAKE) test-middleware - -.PHONY: test-router -test-router: - go test -race -v . - -.PHONY: test-middleware -test-middleware: - go test -race -v ./middleware - -.PHONY: docs -docs: - npx docsify-cli serve ./docs diff --git a/backend/vendor/github.com/go-chi/chi/v5/README.md b/backend/vendor/github.com/go-chi/chi/v5/README.md deleted file mode 100644 index 7f662ab4..00000000 --- a/backend/vendor/github.com/go-chi/chi/v5/README.md +++ /dev/null @@ -1,503 +0,0 @@ -# chi - - -[![GoDoc Widget]][GoDoc] - -`chi` is a lightweight, idiomatic and composable router for building Go HTTP services. It's -especially good at helping you write large REST API services that are kept maintainable as your -project grows and changes. `chi` is built on the new `context` package introduced in Go 1.7 to -handle signaling, cancelation and request-scoped values across a handler chain. - -The focus of the project has been to seek out an elegant and comfortable design for writing -REST API servers, written during the development of the Pressly API service that powers our -public API service, which in turn powers all of our client-side applications. - -The key considerations of chi's design are: project structure, maintainability, standard http -handlers (stdlib-only), developer productivity, and deconstructing a large system into many small -parts. The core router `github.com/go-chi/chi` is quite small (less than 1000 LOC), but we've also -included some useful/optional subpackages: [middleware](/middleware), [render](https://github.com/go-chi/render) -and [docgen](https://github.com/go-chi/docgen). We hope you enjoy it too! - -## Install - -`go get -u github.com/go-chi/chi/v5` - - -## Features - -* **Lightweight** - cloc'd in ~1000 LOC for the chi router -* **Fast** - yes, see [benchmarks](#benchmarks) -* **100% compatible with net/http** - use any http or middleware pkg in the ecosystem that is also compatible with `net/http` -* **Designed for modular/composable APIs** - middlewares, inline middlewares, route groups and sub-router mounting -* **Context control** - built on new `context` package, providing value chaining, cancellations and timeouts -* **Robust** - in production at Pressly, Cloudflare, Heroku, 99Designs, and many others (see [discussion](https://github.com/go-chi/chi/issues/91)) -* **Doc generation** - `docgen` auto-generates routing documentation from your source to JSON or Markdown -* **Go.mod support** - as of v5, go.mod support (see [CHANGELOG](https://github.com/go-chi/chi/blob/master/CHANGELOG.md)) -* **No external dependencies** - plain ol' Go stdlib + net/http - - -## Examples - -See [_examples/](https://github.com/go-chi/chi/blob/master/_examples/) for a variety of examples. - - -**As easy as:** - -```go -package main - -import ( - "net/http" - - "github.com/go-chi/chi/v5" - "github.com/go-chi/chi/v5/middleware" -) - -func main() { - r := chi.NewRouter() - r.Use(middleware.Logger) - r.Get("/", func(w http.ResponseWriter, r *http.Request) { - w.Write([]byte("welcome")) - }) - http.ListenAndServe(":3000", r) -} -``` - -**REST Preview:** - -Here is a little preview of what routing looks like with chi. Also take a look at the generated routing docs -in JSON ([routes.json](https://github.com/go-chi/chi/blob/master/_examples/rest/routes.json)) and in -Markdown ([routes.md](https://github.com/go-chi/chi/blob/master/_examples/rest/routes.md)). - -I highly recommend reading the source of the [examples](https://github.com/go-chi/chi/blob/master/_examples/) listed -above, they will show you all the features of chi and serve as a good form of documentation. - -```go -import ( - //... - "context" - "github.com/go-chi/chi/v5" - "github.com/go-chi/chi/v5/middleware" -) - -func main() { - r := chi.NewRouter() - - // A good base middleware stack - r.Use(middleware.RequestID) - r.Use(middleware.RealIP) - r.Use(middleware.Logger) - r.Use(middleware.Recoverer) - - // Set a timeout value on the request context (ctx), that will signal - // through ctx.Done() that the request has timed out and further - // processing should be stopped. - r.Use(middleware.Timeout(60 * time.Second)) - - r.Get("/", func(w http.ResponseWriter, r *http.Request) { - w.Write([]byte("hi")) - }) - - // RESTy routes for "articles" resource - r.Route("/articles", func(r chi.Router) { - r.With(paginate).Get("/", listArticles) // GET /articles - r.With(paginate).Get("/{month}-{day}-{year}", listArticlesByDate) // GET /articles/01-16-2017 - - r.Post("/", createArticle) // POST /articles - r.Get("/search", searchArticles) // GET /articles/search - - // Regexp url parameters: - r.Get("/{articleSlug:[a-z-]+}", getArticleBySlug) // GET /articles/home-is-toronto - - // Subrouters: - r.Route("/{articleID}", func(r chi.Router) { - r.Use(ArticleCtx) - r.Get("/", getArticle) // GET /articles/123 - r.Put("/", updateArticle) // PUT /articles/123 - r.Delete("/", deleteArticle) // DELETE /articles/123 - }) - }) - - // Mount the admin sub-router - r.Mount("/admin", adminRouter()) - - http.ListenAndServe(":3333", r) -} - -func ArticleCtx(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - articleID := chi.URLParam(r, "articleID") - article, err := dbGetArticle(articleID) - if err != nil { - http.Error(w, http.StatusText(404), 404) - return - } - ctx := context.WithValue(r.Context(), "article", article) - next.ServeHTTP(w, r.WithContext(ctx)) - }) -} - -func getArticle(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - article, ok := ctx.Value("article").(*Article) - if !ok { - http.Error(w, http.StatusText(422), 422) - return - } - w.Write([]byte(fmt.Sprintf("title:%s", article.Title))) -} - -// A completely separate router for administrator routes -func adminRouter() http.Handler { - r := chi.NewRouter() - r.Use(AdminOnly) - r.Get("/", adminIndex) - r.Get("/accounts", adminListAccounts) - return r -} - -func AdminOnly(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - perm, ok := ctx.Value("acl.permission").(YourPermissionType) - if !ok || !perm.IsAdmin() { - http.Error(w, http.StatusText(403), 403) - return - } - next.ServeHTTP(w, r) - }) -} -``` - - -## Router interface - -chi's router is based on a kind of [Patricia Radix trie](https://en.wikipedia.org/wiki/Radix_tree). -The router is fully compatible with `net/http`. - -Built on top of the tree is the `Router` interface: - -```go -// Router consisting of the core routing methods used by chi's Mux, -// using only the standard net/http. -type Router interface { - http.Handler - Routes - - // Use appends one or more middlewares onto the Router stack. - Use(middlewares ...func(http.Handler) http.Handler) - - // With adds inline middlewares for an endpoint handler. - With(middlewares ...func(http.Handler) http.Handler) Router - - // Group adds a new inline-Router along the current routing - // path, with a fresh middleware stack for the inline-Router. - Group(fn func(r Router)) Router - - // Route mounts a sub-Router along a `pattern`` string. - Route(pattern string, fn func(r Router)) Router - - // Mount attaches another http.Handler along ./pattern/* - Mount(pattern string, h http.Handler) - - // Handle and HandleFunc adds routes for `pattern` that matches - // all HTTP methods. - Handle(pattern string, h http.Handler) - HandleFunc(pattern string, h http.HandlerFunc) - - // Method and MethodFunc adds routes for `pattern` that matches - // the `method` HTTP method. - Method(method, pattern string, h http.Handler) - MethodFunc(method, pattern string, h http.HandlerFunc) - - // HTTP-method routing along `pattern` - Connect(pattern string, h http.HandlerFunc) - Delete(pattern string, h http.HandlerFunc) - Get(pattern string, h http.HandlerFunc) - Head(pattern string, h http.HandlerFunc) - Options(pattern string, h http.HandlerFunc) - Patch(pattern string, h http.HandlerFunc) - Post(pattern string, h http.HandlerFunc) - Put(pattern string, h http.HandlerFunc) - Trace(pattern string, h http.HandlerFunc) - - // NotFound defines a handler to respond whenever a route could - // not be found. - NotFound(h http.HandlerFunc) - - // MethodNotAllowed defines a handler to respond whenever a method is - // not allowed. - MethodNotAllowed(h http.HandlerFunc) -} - -// Routes interface adds two methods for router traversal, which is also -// used by the github.com/go-chi/docgen package to generate documentation for Routers. -type Routes interface { - // Routes returns the routing tree in an easily traversable structure. - Routes() []Route - - // Middlewares returns the list of middlewares in use by the router. - Middlewares() Middlewares - - // Match searches the routing tree for a handler that matches - // the method/path - similar to routing a http request, but without - // executing the handler thereafter. - Match(rctx *Context, method, path string) bool -} -``` - -Each routing method accepts a URL `pattern` and chain of `handlers`. The URL pattern -supports named params (ie. `/users/{userID}`) and wildcards (ie. `/admin/*`). URL parameters -can be fetched at runtime by calling `chi.URLParam(r, "userID")` for named parameters -and `chi.URLParam(r, "*")` for a wildcard parameter. - - -### Middleware handlers - -chi's middlewares are just stdlib net/http middleware handlers. There is nothing special -about them, which means the router and all the tooling is designed to be compatible and -friendly with any middleware in the community. This offers much better extensibility and reuse -of packages and is at the heart of chi's purpose. - -Here is an example of a standard net/http middleware where we assign a context key `"user"` -the value of `"123"`. This middleware sets a hypothetical user identifier on the request -context and calls the next handler in the chain. - -```go -// HTTP middleware setting a value on the request context -func MyMiddleware(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - // create new context from `r` request context, and assign key `"user"` - // to value of `"123"` - ctx := context.WithValue(r.Context(), "user", "123") - - // call the next handler in the chain, passing the response writer and - // the updated request object with the new context value. - // - // note: context.Context values are nested, so any previously set - // values will be accessible as well, and the new `"user"` key - // will be accessible from this point forward. - next.ServeHTTP(w, r.WithContext(ctx)) - }) -} -``` - - -### Request handlers - -chi uses standard net/http request handlers. This little snippet is an example of a http.Handler -func that reads a user identifier from the request context - hypothetically, identifying -the user sending an authenticated request, validated+set by a previous middleware handler. - -```go -// HTTP handler accessing data from the request context. -func MyRequestHandler(w http.ResponseWriter, r *http.Request) { - // here we read from the request context and fetch out `"user"` key set in - // the MyMiddleware example above. - user := r.Context().Value("user").(string) - - // respond to the client - w.Write([]byte(fmt.Sprintf("hi %s", user))) -} -``` - - -### URL parameters - -chi's router parses and stores URL parameters right onto the request context. Here is -an example of how to access URL params in your net/http handlers. And of course, middlewares -are able to access the same information. - -```go -// HTTP handler accessing the url routing parameters. -func MyRequestHandler(w http.ResponseWriter, r *http.Request) { - // fetch the url parameter `"userID"` from the request of a matching - // routing pattern. An example routing pattern could be: /users/{userID} - userID := chi.URLParam(r, "userID") - - // fetch `"key"` from the request context - ctx := r.Context() - key := ctx.Value("key").(string) - - // respond to the client - w.Write([]byte(fmt.Sprintf("hi %v, %v", userID, key))) -} -``` - - -## Middlewares - -chi comes equipped with an optional `middleware` package, providing a suite of standard -`net/http` middlewares. Please note, any middleware in the ecosystem that is also compatible -with `net/http` can be used with chi's mux. - -### Core middlewares - ----------------------------------------------------------------------------------------------------- -| chi/middleware Handler | description | -| :--------------------- | :---------------------------------------------------------------------- | -| [AllowContentEncoding] | Enforces a whitelist of request Content-Encoding headers | -| [AllowContentType] | Explicit whitelist of accepted request Content-Types | -| [BasicAuth] | Basic HTTP authentication | -| [Compress] | Gzip compression for clients that accept compressed responses | -| [ContentCharset] | Ensure charset for Content-Type request headers | -| [CleanPath] | Clean double slashes from request path | -| [GetHead] | Automatically route undefined HEAD requests to GET handlers | -| [Heartbeat] | Monitoring endpoint to check the servers pulse | -| [Logger] | Logs the start and end of each request with the elapsed processing time | -| [NoCache] | Sets response headers to prevent clients from caching | -| [Profiler] | Easily attach net/http/pprof to your routers | -| [RealIP] | Sets a http.Request's RemoteAddr to either X-Real-IP or X-Forwarded-For | -| [Recoverer] | Gracefully absorb panics and prints the stack trace | -| [RequestID] | Injects a request ID into the context of each request | -| [RedirectSlashes] | Redirect slashes on routing paths | -| [RouteHeaders] | Route handling for request headers | -| [SetHeader] | Short-hand middleware to set a response header key/value | -| [StripSlashes] | Strip slashes on routing paths | -| [Sunset] | Sunset set Deprecation/Sunset header to response | -| [Throttle] | Puts a ceiling on the number of concurrent requests | -| [Timeout] | Signals to the request context when the timeout deadline is reached | -| [URLFormat] | Parse extension from url and put it on request context | -| [WithValue] | Short-hand middleware to set a key/value on the request context | ----------------------------------------------------------------------------------------------------- - -[AllowContentEncoding]: https://pkg.go.dev/github.com/go-chi/chi/middleware#AllowContentEncoding -[AllowContentType]: https://pkg.go.dev/github.com/go-chi/chi/middleware#AllowContentType -[BasicAuth]: https://pkg.go.dev/github.com/go-chi/chi/middleware#BasicAuth -[Compress]: https://pkg.go.dev/github.com/go-chi/chi/middleware#Compress -[ContentCharset]: https://pkg.go.dev/github.com/go-chi/chi/middleware#ContentCharset -[CleanPath]: https://pkg.go.dev/github.com/go-chi/chi/middleware#CleanPath -[GetHead]: https://pkg.go.dev/github.com/go-chi/chi/middleware#GetHead -[GetReqID]: https://pkg.go.dev/github.com/go-chi/chi/middleware#GetReqID -[Heartbeat]: https://pkg.go.dev/github.com/go-chi/chi/middleware#Heartbeat -[Logger]: https://pkg.go.dev/github.com/go-chi/chi/middleware#Logger -[NoCache]: https://pkg.go.dev/github.com/go-chi/chi/middleware#NoCache -[Profiler]: https://pkg.go.dev/github.com/go-chi/chi/middleware#Profiler -[RealIP]: https://pkg.go.dev/github.com/go-chi/chi/middleware#RealIP -[Recoverer]: https://pkg.go.dev/github.com/go-chi/chi/middleware#Recoverer -[RedirectSlashes]: https://pkg.go.dev/github.com/go-chi/chi/middleware#RedirectSlashes -[RequestLogger]: https://pkg.go.dev/github.com/go-chi/chi/middleware#RequestLogger -[RequestID]: https://pkg.go.dev/github.com/go-chi/chi/middleware#RequestID -[RouteHeaders]: https://pkg.go.dev/github.com/go-chi/chi/middleware#RouteHeaders -[SetHeader]: https://pkg.go.dev/github.com/go-chi/chi/middleware#SetHeader -[StripSlashes]: https://pkg.go.dev/github.com/go-chi/chi/middleware#StripSlashes -[Sunset]: https://pkg.go.dev/github.com/go-chi/chi/v5/middleware#Sunset -[Throttle]: https://pkg.go.dev/github.com/go-chi/chi/middleware#Throttle -[ThrottleBacklog]: https://pkg.go.dev/github.com/go-chi/chi/middleware#ThrottleBacklog -[ThrottleWithOpts]: https://pkg.go.dev/github.com/go-chi/chi/middleware#ThrottleWithOpts -[Timeout]: https://pkg.go.dev/github.com/go-chi/chi/middleware#Timeout -[URLFormat]: https://pkg.go.dev/github.com/go-chi/chi/middleware#URLFormat -[WithLogEntry]: https://pkg.go.dev/github.com/go-chi/chi/middleware#WithLogEntry -[WithValue]: https://pkg.go.dev/github.com/go-chi/chi/middleware#WithValue -[Compressor]: https://pkg.go.dev/github.com/go-chi/chi/middleware#Compressor -[DefaultLogFormatter]: https://pkg.go.dev/github.com/go-chi/chi/middleware#DefaultLogFormatter -[EncoderFunc]: https://pkg.go.dev/github.com/go-chi/chi/middleware#EncoderFunc -[HeaderRoute]: https://pkg.go.dev/github.com/go-chi/chi/middleware#HeaderRoute -[HeaderRouter]: https://pkg.go.dev/github.com/go-chi/chi/middleware#HeaderRouter -[LogEntry]: https://pkg.go.dev/github.com/go-chi/chi/middleware#LogEntry -[LogFormatter]: https://pkg.go.dev/github.com/go-chi/chi/middleware#LogFormatter -[LoggerInterface]: https://pkg.go.dev/github.com/go-chi/chi/middleware#LoggerInterface -[ThrottleOpts]: https://pkg.go.dev/github.com/go-chi/chi/middleware#ThrottleOpts -[WrapResponseWriter]: https://pkg.go.dev/github.com/go-chi/chi/middleware#WrapResponseWriter - -### Extra middlewares & packages - -Please see https://github.com/go-chi for additional packages. - --------------------------------------------------------------------------------------------------------------------- -| package | description | -|:---------------------------------------------------|:------------------------------------------------------------- -| [cors](https://github.com/go-chi/cors) | Cross-origin resource sharing (CORS) | -| [docgen](https://github.com/go-chi/docgen) | Print chi.Router routes at runtime | -| [jwtauth](https://github.com/go-chi/jwtauth) | JWT authentication | -| [hostrouter](https://github.com/go-chi/hostrouter) | Domain/host based request routing | -| [httplog](https://github.com/go-chi/httplog) | Small but powerful structured HTTP request logging | -| [httprate](https://github.com/go-chi/httprate) | HTTP request rate limiter | -| [httptracer](https://github.com/go-chi/httptracer) | HTTP request performance tracing library | -| [httpvcr](https://github.com/go-chi/httpvcr) | Write deterministic tests for external sources | -| [stampede](https://github.com/go-chi/stampede) | HTTP request coalescer | --------------------------------------------------------------------------------------------------------------------- - - -## context? - -`context` is a tiny pkg that provides simple interface to signal context across call stacks -and goroutines. It was originally written by [Sameer Ajmani](https://github.com/Sajmani) -and is available in stdlib since go1.7. - -Learn more at https://blog.golang.org/context - -and.. -* Docs: https://golang.org/pkg/context -* Source: https://github.com/golang/go/tree/master/src/context - - -## Benchmarks - -The benchmark suite: https://github.com/pkieltyka/go-http-routing-benchmark - -Results as of Nov 29, 2020 with Go 1.15.5 on Linux AMD 3950x - -```shell -BenchmarkChi_Param 3075895 384 ns/op 400 B/op 2 allocs/op -BenchmarkChi_Param5 2116603 566 ns/op 400 B/op 2 allocs/op -BenchmarkChi_Param20 964117 1227 ns/op 400 B/op 2 allocs/op -BenchmarkChi_ParamWrite 2863413 420 ns/op 400 B/op 2 allocs/op -BenchmarkChi_GithubStatic 3045488 395 ns/op 400 B/op 2 allocs/op -BenchmarkChi_GithubParam 2204115 540 ns/op 400 B/op 2 allocs/op -BenchmarkChi_GithubAll 10000 113811 ns/op 81203 B/op 406 allocs/op -BenchmarkChi_GPlusStatic 3337485 359 ns/op 400 B/op 2 allocs/op -BenchmarkChi_GPlusParam 2825853 423 ns/op 400 B/op 2 allocs/op -BenchmarkChi_GPlus2Params 2471697 483 ns/op 400 B/op 2 allocs/op -BenchmarkChi_GPlusAll 194220 5950 ns/op 5200 B/op 26 allocs/op -BenchmarkChi_ParseStatic 3365324 356 ns/op 400 B/op 2 allocs/op -BenchmarkChi_ParseParam 2976614 404 ns/op 400 B/op 2 allocs/op -BenchmarkChi_Parse2Params 2638084 439 ns/op 400 B/op 2 allocs/op -BenchmarkChi_ParseAll 109567 11295 ns/op 10400 B/op 52 allocs/op -BenchmarkChi_StaticAll 16846 71308 ns/op 62802 B/op 314 allocs/op -``` - -Comparison with other routers: https://gist.github.com/pkieltyka/123032f12052520aaccab752bd3e78cc - -NOTE: the allocs in the benchmark above are from the calls to http.Request's -`WithContext(context.Context)` method that clones the http.Request, sets the `Context()` -on the duplicated (alloc'd) request and returns it the new request object. This is just -how setting context on a request in Go works. - - -## Credits - -* Carl Jackson for https://github.com/zenazn/goji - * Parts of chi's thinking comes from goji, and chi's middleware package - sources from [goji](https://github.com/zenazn/goji/tree/master/web/middleware). - * Please see goji's [LICENSE](https://github.com/zenazn/goji/blob/master/LICENSE) (MIT) -* Armon Dadgar for https://github.com/armon/go-radix -* Contributions: [@VojtechVitek](https://github.com/VojtechVitek) - -We'll be more than happy to see [your contributions](./CONTRIBUTING.md)! - - -## Beyond REST - -chi is just a http router that lets you decompose request handling into many smaller layers. -Many companies use chi to write REST services for their public APIs. But, REST is just a convention -for managing state via HTTP, and there's a lot of other pieces required to write a complete client-server -system or network of microservices. - -Looking beyond REST, I also recommend some newer works in the field: -* [webrpc](https://github.com/webrpc/webrpc) - Web-focused RPC client+server framework with code-gen -* [gRPC](https://github.com/grpc/grpc-go) - Google's RPC framework via protobufs -* [graphql](https://github.com/99designs/gqlgen) - Declarative query language -* [NATS](https://nats.io) - lightweight pub-sub - - -## License - -Copyright (c) 2015-present [Peter Kieltyka](https://github.com/pkieltyka) - -Licensed under [MIT License](./LICENSE) - -[GoDoc]: https://pkg.go.dev/github.com/go-chi/chi/v5 -[GoDoc Widget]: https://godoc.org/github.com/go-chi/chi?status.svg -[Travis]: https://travis-ci.org/go-chi/chi -[Travis Widget]: https://travis-ci.org/go-chi/chi.svg?branch=master diff --git a/backend/vendor/github.com/go-chi/chi/v5/SECURITY.md b/backend/vendor/github.com/go-chi/chi/v5/SECURITY.md deleted file mode 100644 index 7e937f87..00000000 --- a/backend/vendor/github.com/go-chi/chi/v5/SECURITY.md +++ /dev/null @@ -1,5 +0,0 @@ -# Reporting Security Issues - -We appreciate your efforts to responsibly disclose your findings, and will make every effort to acknowledge your contributions. - -To report a security issue, please use the GitHub Security Advisory ["Report a Vulnerability"](https://github.com/go-chi/chi/security/advisories/new) tab. diff --git a/backend/vendor/github.com/go-chi/chi/v5/chain.go b/backend/vendor/github.com/go-chi/chi/v5/chain.go deleted file mode 100644 index a2278414..00000000 --- a/backend/vendor/github.com/go-chi/chi/v5/chain.go +++ /dev/null @@ -1,49 +0,0 @@ -package chi - -import "net/http" - -// Chain returns a Middlewares type from a slice of middleware handlers. -func Chain(middlewares ...func(http.Handler) http.Handler) Middlewares { - return Middlewares(middlewares) -} - -// Handler builds and returns a http.Handler from the chain of middlewares, -// with `h http.Handler` as the final handler. -func (mws Middlewares) Handler(h http.Handler) http.Handler { - return &ChainHandler{h, chain(mws, h), mws} -} - -// HandlerFunc builds and returns a http.Handler from the chain of middlewares, -// with `h http.Handler` as the final handler. -func (mws Middlewares) HandlerFunc(h http.HandlerFunc) http.Handler { - return &ChainHandler{h, chain(mws, h), mws} -} - -// ChainHandler is a http.Handler with support for handler composition and -// execution. -type ChainHandler struct { - Endpoint http.Handler - chain http.Handler - Middlewares Middlewares -} - -func (c *ChainHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { - c.chain.ServeHTTP(w, r) -} - -// chain builds a http.Handler composed of an inline middleware stack and endpoint -// handler in the order they are passed. -func chain(middlewares []func(http.Handler) http.Handler, endpoint http.Handler) http.Handler { - // Return ahead of time if there aren't any middlewares for the chain - if len(middlewares) == 0 { - return endpoint - } - - // Wrap the end handler with the middleware chain - h := middlewares[len(middlewares)-1](endpoint) - for i := len(middlewares) - 2; i >= 0; i-- { - h = middlewares[i](h) - } - - return h -} diff --git a/backend/vendor/github.com/go-chi/chi/v5/chi.go b/backend/vendor/github.com/go-chi/chi/v5/chi.go deleted file mode 100644 index fc32c4ef..00000000 --- a/backend/vendor/github.com/go-chi/chi/v5/chi.go +++ /dev/null @@ -1,138 +0,0 @@ -// Package chi is a small, idiomatic and composable router for building HTTP services. -// -// chi requires Go 1.14 or newer. -// -// Example: -// -// package main -// -// import ( -// "net/http" -// -// "github.com/go-chi/chi/v5" -// "github.com/go-chi/chi/v5/middleware" -// ) -// -// func main() { -// r := chi.NewRouter() -// r.Use(middleware.Logger) -// r.Use(middleware.Recoverer) -// -// r.Get("/", func(w http.ResponseWriter, r *http.Request) { -// w.Write([]byte("root.")) -// }) -// -// http.ListenAndServe(":3333", r) -// } -// -// See github.com/go-chi/chi/_examples/ for more in-depth examples. -// -// URL patterns allow for easy matching of path components in HTTP -// requests. The matching components can then be accessed using -// chi.URLParam(). All patterns must begin with a slash. -// -// A simple named placeholder {name} matches any sequence of characters -// up to the next / or the end of the URL. Trailing slashes on paths must -// be handled explicitly. -// -// A placeholder with a name followed by a colon allows a regular -// expression match, for example {number:\\d+}. The regular expression -// syntax is Go's normal regexp RE2 syntax, except that regular expressions -// including { or } are not supported, and / will never be -// matched. An anonymous regexp pattern is allowed, using an empty string -// before the colon in the placeholder, such as {:\\d+} -// -// The special placeholder of asterisk matches the rest of the requested -// URL. Any trailing characters in the pattern are ignored. This is the only -// placeholder which will match / characters. -// -// Examples: -// -// "/user/{name}" matches "/user/jsmith" but not "/user/jsmith/info" or "/user/jsmith/" -// "/user/{name}/info" matches "/user/jsmith/info" -// "/page/*" matches "/page/intro/latest" -// "/page/{other}/latest" also matches "/page/intro/latest" -// "/date/{yyyy:\\d\\d\\d\\d}/{mm:\\d\\d}/{dd:\\d\\d}" matches "/date/2017/04/01" -package chi - -import "net/http" - -// NewRouter returns a new Mux object that implements the Router interface. -func NewRouter() *Mux { - return NewMux() -} - -// Router consisting of the core routing methods used by chi's Mux, -// using only the standard net/http. -type Router interface { - http.Handler - Routes - - // Use appends one or more middlewares onto the Router stack. - Use(middlewares ...func(http.Handler) http.Handler) - - // With adds inline middlewares for an endpoint handler. - With(middlewares ...func(http.Handler) http.Handler) Router - - // Group adds a new inline-Router along the current routing - // path, with a fresh middleware stack for the inline-Router. - Group(fn func(r Router)) Router - - // Route mounts a sub-Router along a `pattern`` string. - Route(pattern string, fn func(r Router)) Router - - // Mount attaches another http.Handler along ./pattern/* - Mount(pattern string, h http.Handler) - - // Handle and HandleFunc adds routes for `pattern` that matches - // all HTTP methods. - Handle(pattern string, h http.Handler) - HandleFunc(pattern string, h http.HandlerFunc) - - // Method and MethodFunc adds routes for `pattern` that matches - // the `method` HTTP method. - Method(method, pattern string, h http.Handler) - MethodFunc(method, pattern string, h http.HandlerFunc) - - // HTTP-method routing along `pattern` - Connect(pattern string, h http.HandlerFunc) - Delete(pattern string, h http.HandlerFunc) - Get(pattern string, h http.HandlerFunc) - Head(pattern string, h http.HandlerFunc) - Options(pattern string, h http.HandlerFunc) - Patch(pattern string, h http.HandlerFunc) - Post(pattern string, h http.HandlerFunc) - Put(pattern string, h http.HandlerFunc) - Trace(pattern string, h http.HandlerFunc) - - // NotFound defines a handler to respond whenever a route could - // not be found. - NotFound(h http.HandlerFunc) - - // MethodNotAllowed defines a handler to respond whenever a method is - // not allowed. - MethodNotAllowed(h http.HandlerFunc) -} - -// Routes interface adds two methods for router traversal, which is also -// used by the `docgen` subpackage to generation documentation for Routers. -type Routes interface { - // Routes returns the routing tree in an easily traversable structure. - Routes() []Route - - // Middlewares returns the list of middlewares in use by the router. - Middlewares() Middlewares - - // Match searches the routing tree for a handler that matches - // the method/path - similar to routing a http request, but without - // executing the handler thereafter. - Match(rctx *Context, method, path string) bool - - // Find searches the routing tree for the pattern that matches - // the method/path. - Find(rctx *Context, method, path string) string -} - -// Middlewares type is a slice of standard middleware handlers with methods -// to compose middleware chains and http.Handler's. -type Middlewares []func(http.Handler) http.Handler diff --git a/backend/vendor/github.com/go-chi/chi/v5/context.go b/backend/vendor/github.com/go-chi/chi/v5/context.go deleted file mode 100644 index aacf6eff..00000000 --- a/backend/vendor/github.com/go-chi/chi/v5/context.go +++ /dev/null @@ -1,165 +0,0 @@ -package chi - -import ( - "context" - "net/http" - "strings" -) - -// URLParam returns the url parameter from a http.Request object. -func URLParam(r *http.Request, key string) string { - if rctx := RouteContext(r.Context()); rctx != nil { - return rctx.URLParam(key) - } - return "" -} - -// URLParamFromCtx returns the url parameter from a http.Request Context. -func URLParamFromCtx(ctx context.Context, key string) string { - if rctx := RouteContext(ctx); rctx != nil { - return rctx.URLParam(key) - } - return "" -} - -// RouteContext returns chi's routing Context object from a -// http.Request Context. -func RouteContext(ctx context.Context) *Context { - val, _ := ctx.Value(RouteCtxKey).(*Context) - return val -} - -// NewRouteContext returns a new routing Context object. -func NewRouteContext() *Context { - return &Context{} -} - -var ( - // RouteCtxKey is the context.Context key to store the request context. - RouteCtxKey = &contextKey{"RouteContext"} -) - -// Context is the default routing context set on the root node of a -// request context to track route patterns, URL parameters and -// an optional routing path. -type Context struct { - Routes Routes - - // parentCtx is the parent of this one, for using Context as a - // context.Context directly. This is an optimization that saves - // 1 allocation. - parentCtx context.Context - - // Routing path/method override used during the route search. - // See Mux#routeHTTP method. - RoutePath string - RouteMethod string - - // URLParams are the stack of routeParams captured during the - // routing lifecycle across a stack of sub-routers. - URLParams RouteParams - - // Route parameters matched for the current sub-router. It is - // intentionally unexported so it can't be tampered. - routeParams RouteParams - - // The endpoint routing pattern that matched the request URI path - // or `RoutePath` of the current sub-router. This value will update - // during the lifecycle of a request passing through a stack of - // sub-routers. - routePattern string - - // Routing pattern stack throughout the lifecycle of the request, - // across all connected routers. It is a record of all matching - // patterns across a stack of sub-routers. - RoutePatterns []string - - methodsAllowed []methodTyp // allowed methods in case of a 405 - methodNotAllowed bool -} - -// Reset a routing context to its initial state. -func (x *Context) Reset() { - x.Routes = nil - x.RoutePath = "" - x.RouteMethod = "" - x.RoutePatterns = x.RoutePatterns[:0] - x.URLParams.Keys = x.URLParams.Keys[:0] - x.URLParams.Values = x.URLParams.Values[:0] - - x.routePattern = "" - x.routeParams.Keys = x.routeParams.Keys[:0] - x.routeParams.Values = x.routeParams.Values[:0] - x.methodNotAllowed = false - x.methodsAllowed = x.methodsAllowed[:0] - x.parentCtx = nil -} - -// URLParam returns the corresponding URL parameter value from the request -// routing context. -func (x *Context) URLParam(key string) string { - for k := len(x.URLParams.Keys) - 1; k >= 0; k-- { - if x.URLParams.Keys[k] == key { - return x.URLParams.Values[k] - } - } - return "" -} - -// RoutePattern builds the routing pattern string for the particular -// request, at the particular point during routing. This means, the value -// will change throughout the execution of a request in a router. That is -// why it's advised to only use this value after calling the next handler. -// -// For example, -// -// func Instrument(next http.Handler) http.Handler { -// return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { -// next.ServeHTTP(w, r) -// routePattern := chi.RouteContext(r.Context()).RoutePattern() -// measure(w, r, routePattern) -// }) -// } -func (x *Context) RoutePattern() string { - if x == nil { - return "" - } - routePattern := strings.Join(x.RoutePatterns, "") - routePattern = replaceWildcards(routePattern) - if routePattern != "/" { - routePattern = strings.TrimSuffix(routePattern, "//") - routePattern = strings.TrimSuffix(routePattern, "/") - } - return routePattern -} - -// replaceWildcards takes a route pattern and recursively replaces all -// occurrences of "/*/" to "/". -func replaceWildcards(p string) string { - if strings.Contains(p, "/*/") { - return replaceWildcards(strings.Replace(p, "/*/", "/", -1)) - } - return p -} - -// RouteParams is a structure to track URL routing parameters efficiently. -type RouteParams struct { - Keys, Values []string -} - -// Add will append a URL parameter to the end of the route param -func (s *RouteParams) Add(key, value string) { - s.Keys = append(s.Keys, key) - s.Values = append(s.Values, value) -} - -// contextKey is a value for use with context.WithValue. It's used as -// a pointer so it fits in an interface{} without allocation. This technique -// for defining context keys was copied from Go 1.7's new use of context in net/http. -type contextKey struct { - name string -} - -func (k *contextKey) String() string { - return "chi context value " + k.name -} diff --git a/backend/vendor/github.com/go-chi/chi/v5/middleware/basic_auth.go b/backend/vendor/github.com/go-chi/chi/v5/middleware/basic_auth.go deleted file mode 100644 index a546c9e9..00000000 --- a/backend/vendor/github.com/go-chi/chi/v5/middleware/basic_auth.go +++ /dev/null @@ -1,33 +0,0 @@ -package middleware - -import ( - "crypto/subtle" - "fmt" - "net/http" -) - -// BasicAuth implements a simple middleware handler for adding basic http auth to a route. -func BasicAuth(realm string, creds map[string]string) func(next http.Handler) http.Handler { - return func(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - user, pass, ok := r.BasicAuth() - if !ok { - basicAuthFailed(w, realm) - return - } - - credPass, credUserOk := creds[user] - if !credUserOk || subtle.ConstantTimeCompare([]byte(pass), []byte(credPass)) != 1 { - basicAuthFailed(w, realm) - return - } - - next.ServeHTTP(w, r) - }) - } -} - -func basicAuthFailed(w http.ResponseWriter, realm string) { - w.Header().Add("WWW-Authenticate", fmt.Sprintf(`Basic realm="%s"`, realm)) - w.WriteHeader(http.StatusUnauthorized) -} diff --git a/backend/vendor/github.com/go-chi/chi/v5/middleware/clean_path.go b/backend/vendor/github.com/go-chi/chi/v5/middleware/clean_path.go deleted file mode 100644 index adeba429..00000000 --- a/backend/vendor/github.com/go-chi/chi/v5/middleware/clean_path.go +++ /dev/null @@ -1,28 +0,0 @@ -package middleware - -import ( - "net/http" - "path" - - "github.com/go-chi/chi/v5" -) - -// CleanPath middleware will clean out double slash mistakes from a user's request path. -// For example, if a user requests /users//1 or //users////1 will both be treated as: /users/1 -func CleanPath(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - rctx := chi.RouteContext(r.Context()) - - routePath := rctx.RoutePath - if routePath == "" { - if r.URL.RawPath != "" { - routePath = r.URL.RawPath - } else { - routePath = r.URL.Path - } - rctx.RoutePath = path.Clean(routePath) - } - - next.ServeHTTP(w, r) - }) -} diff --git a/backend/vendor/github.com/go-chi/chi/v5/middleware/compress.go b/backend/vendor/github.com/go-chi/chi/v5/middleware/compress.go deleted file mode 100644 index 28240c4b..00000000 --- a/backend/vendor/github.com/go-chi/chi/v5/middleware/compress.go +++ /dev/null @@ -1,398 +0,0 @@ -package middleware - -import ( - "bufio" - "compress/flate" - "compress/gzip" - "errors" - "fmt" - "io" - "io/ioutil" - "net" - "net/http" - "strings" - "sync" -) - -var defaultCompressibleContentTypes = []string{ - "text/html", - "text/css", - "text/plain", - "text/javascript", - "application/javascript", - "application/x-javascript", - "application/json", - "application/atom+xml", - "application/rss+xml", - "image/svg+xml", -} - -// Compress is a middleware that compresses response -// body of a given content types to a data format based -// on Accept-Encoding request header. It uses a given -// compression level. -// -// NOTE: make sure to set the Content-Type header on your response -// otherwise this middleware will not compress the response body. For ex, in -// your handler you should set w.Header().Set("Content-Type", http.DetectContentType(yourBody)) -// or set it manually. -// -// Passing a compression level of 5 is sensible value -func Compress(level int, types ...string) func(next http.Handler) http.Handler { - compressor := NewCompressor(level, types...) - return compressor.Handler -} - -// Compressor represents a set of encoding configurations. -type Compressor struct { - // The mapping of encoder names to encoder functions. - encoders map[string]EncoderFunc - // The mapping of pooled encoders to pools. - pooledEncoders map[string]*sync.Pool - // The set of content types allowed to be compressed. - allowedTypes map[string]struct{} - allowedWildcards map[string]struct{} - // The list of encoders in order of decreasing precedence. - encodingPrecedence []string - level int // The compression level. -} - -// NewCompressor creates a new Compressor that will handle encoding responses. -// -// The level should be one of the ones defined in the flate package. -// The types are the content types that are allowed to be compressed. -func NewCompressor(level int, types ...string) *Compressor { - // If types are provided, set those as the allowed types. If none are - // provided, use the default list. - allowedTypes := make(map[string]struct{}) - allowedWildcards := make(map[string]struct{}) - if len(types) > 0 { - for _, t := range types { - if strings.Contains(strings.TrimSuffix(t, "/*"), "*") { - panic(fmt.Sprintf("middleware/compress: Unsupported content-type wildcard pattern '%s'. Only '/*' supported", t)) - } - if strings.HasSuffix(t, "/*") { - allowedWildcards[strings.TrimSuffix(t, "/*")] = struct{}{} - } else { - allowedTypes[t] = struct{}{} - } - } - } else { - for _, t := range defaultCompressibleContentTypes { - allowedTypes[t] = struct{}{} - } - } - - c := &Compressor{ - level: level, - encoders: make(map[string]EncoderFunc), - pooledEncoders: make(map[string]*sync.Pool), - allowedTypes: allowedTypes, - allowedWildcards: allowedWildcards, - } - - // Set the default encoders. The precedence order uses the reverse - // ordering that the encoders were added. This means adding new encoders - // will move them to the front of the order. - // - // TODO: - // lzma: Opera. - // sdch: Chrome, Android. Gzip output + dictionary header. - // br: Brotli, see https://github.com/go-chi/chi/pull/326 - - // HTTP 1.1 "deflate" (RFC 2616) stands for DEFLATE data (RFC 1951) - // wrapped with zlib (RFC 1950). The zlib wrapper uses Adler-32 - // checksum compared to CRC-32 used in "gzip" and thus is faster. - // - // But.. some old browsers (MSIE, Safari 5.1) incorrectly expect - // raw DEFLATE data only, without the mentioned zlib wrapper. - // Because of this major confusion, most modern browsers try it - // both ways, first looking for zlib headers. - // Quote by Mark Adler: http://stackoverflow.com/a/9186091/385548 - // - // The list of browsers having problems is quite big, see: - // http://zoompf.com/blog/2012/02/lose-the-wait-http-compression - // https://web.archive.org/web/20120321182910/http://www.vervestudios.co/projects/compression-tests/results - // - // That's why we prefer gzip over deflate. It's just more reliable - // and not significantly slower than deflate. - c.SetEncoder("deflate", encoderDeflate) - - // TODO: Exception for old MSIE browsers that can't handle non-HTML? - // https://zoompf.com/blog/2012/02/lose-the-wait-http-compression - c.SetEncoder("gzip", encoderGzip) - - // NOTE: Not implemented, intentionally: - // case "compress": // LZW. Deprecated. - // case "bzip2": // Too slow on-the-fly. - // case "zopfli": // Too slow on-the-fly. - // case "xz": // Too slow on-the-fly. - return c -} - -// SetEncoder can be used to set the implementation of a compression algorithm. -// -// The encoding should be a standardised identifier. See: -// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Encoding -// -// For example, add the Brotli algorithm: -// -// import brotli_enc "gopkg.in/kothar/brotli-go.v0/enc" -// -// compressor := middleware.NewCompressor(5, "text/html") -// compressor.SetEncoder("br", func(w io.Writer, level int) io.Writer { -// params := brotli_enc.NewBrotliParams() -// params.SetQuality(level) -// return brotli_enc.NewBrotliWriter(params, w) -// }) -func (c *Compressor) SetEncoder(encoding string, fn EncoderFunc) { - encoding = strings.ToLower(encoding) - if encoding == "" { - panic("the encoding can not be empty") - } - if fn == nil { - panic("attempted to set a nil encoder function") - } - - // If we are adding a new encoder that is already registered, we have to - // clear that one out first. - delete(c.pooledEncoders, encoding) - delete(c.encoders, encoding) - - // If the encoder supports Resetting (IoReseterWriter), then it can be pooled. - encoder := fn(ioutil.Discard, c.level) - if encoder != nil { - if _, ok := encoder.(ioResetterWriter); ok { - pool := &sync.Pool{ - New: func() interface{} { - return fn(ioutil.Discard, c.level) - }, - } - c.pooledEncoders[encoding] = pool - } - } - // If the encoder is not in the pooledEncoders, add it to the normal encoders. - if _, ok := c.pooledEncoders[encoding]; !ok { - c.encoders[encoding] = fn - } - - for i, v := range c.encodingPrecedence { - if v == encoding { - c.encodingPrecedence = append(c.encodingPrecedence[:i], c.encodingPrecedence[i+1:]...) - } - } - - c.encodingPrecedence = append([]string{encoding}, c.encodingPrecedence...) -} - -// Handler returns a new middleware that will compress the response based on the -// current Compressor. -func (c *Compressor) Handler(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - encoder, encoding, cleanup := c.selectEncoder(r.Header, w) - - cw := &compressResponseWriter{ - ResponseWriter: w, - w: w, - contentTypes: c.allowedTypes, - contentWildcards: c.allowedWildcards, - encoding: encoding, - compressible: false, // determined in post-handler - } - if encoder != nil { - cw.w = encoder - } - // Re-add the encoder to the pool if applicable. - defer cleanup() - defer cw.Close() - - next.ServeHTTP(cw, r) - }) -} - -// selectEncoder returns the encoder, the name of the encoder, and a closer function. -func (c *Compressor) selectEncoder(h http.Header, w io.Writer) (io.Writer, string, func()) { - header := h.Get("Accept-Encoding") - - // Parse the names of all accepted algorithms from the header. - accepted := strings.Split(strings.ToLower(header), ",") - - // Find supported encoder by accepted list by precedence - for _, name := range c.encodingPrecedence { - if matchAcceptEncoding(accepted, name) { - if pool, ok := c.pooledEncoders[name]; ok { - encoder := pool.Get().(ioResetterWriter) - cleanup := func() { - pool.Put(encoder) - } - encoder.Reset(w) - return encoder, name, cleanup - - } - if fn, ok := c.encoders[name]; ok { - return fn(w, c.level), name, func() {} - } - } - - } - - // No encoder found to match the accepted encoding - return nil, "", func() {} -} - -func matchAcceptEncoding(accepted []string, encoding string) bool { - for _, v := range accepted { - if strings.Contains(v, encoding) { - return true - } - } - return false -} - -// An EncoderFunc is a function that wraps the provided io.Writer with a -// streaming compression algorithm and returns it. -// -// In case of failure, the function should return nil. -type EncoderFunc func(w io.Writer, level int) io.Writer - -// Interface for types that allow resetting io.Writers. -type ioResetterWriter interface { - io.Writer - Reset(w io.Writer) -} - -type compressResponseWriter struct { - http.ResponseWriter - - // The streaming encoder writer to be used if there is one. Otherwise, - // this is just the normal writer. - w io.Writer - contentTypes map[string]struct{} - contentWildcards map[string]struct{} - encoding string - wroteHeader bool - compressible bool -} - -func (cw *compressResponseWriter) isCompressible() bool { - // Parse the first part of the Content-Type response header. - contentType := cw.Header().Get("Content-Type") - if idx := strings.Index(contentType, ";"); idx >= 0 { - contentType = contentType[0:idx] - } - - // Is the content type compressible? - if _, ok := cw.contentTypes[contentType]; ok { - return true - } - if idx := strings.Index(contentType, "/"); idx > 0 { - contentType = contentType[0:idx] - _, ok := cw.contentWildcards[contentType] - return ok - } - return false -} - -func (cw *compressResponseWriter) WriteHeader(code int) { - if cw.wroteHeader { - cw.ResponseWriter.WriteHeader(code) // Allow multiple calls to propagate. - return - } - cw.wroteHeader = true - defer cw.ResponseWriter.WriteHeader(code) - - // Already compressed data? - if cw.Header().Get("Content-Encoding") != "" { - return - } - - if !cw.isCompressible() { - cw.compressible = false - return - } - - if cw.encoding != "" { - cw.compressible = true - cw.Header().Set("Content-Encoding", cw.encoding) - cw.Header().Add("Vary", "Accept-Encoding") - - // The content-length after compression is unknown - cw.Header().Del("Content-Length") - } -} - -func (cw *compressResponseWriter) Write(p []byte) (int, error) { - if !cw.wroteHeader { - cw.WriteHeader(http.StatusOK) - } - - return cw.writer().Write(p) -} - -func (cw *compressResponseWriter) writer() io.Writer { - if cw.compressible { - return cw.w - } - return cw.ResponseWriter -} - -type compressFlusher interface { - Flush() error -} - -func (cw *compressResponseWriter) Flush() { - if f, ok := cw.writer().(http.Flusher); ok { - f.Flush() - } - // If the underlying writer has a compression flush signature, - // call this Flush() method instead - if f, ok := cw.writer().(compressFlusher); ok { - f.Flush() - - // Also flush the underlying response writer - if f, ok := cw.ResponseWriter.(http.Flusher); ok { - f.Flush() - } - } -} - -func (cw *compressResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { - if hj, ok := cw.writer().(http.Hijacker); ok { - return hj.Hijack() - } - return nil, nil, errors.New("chi/middleware: http.Hijacker is unavailable on the writer") -} - -func (cw *compressResponseWriter) Push(target string, opts *http.PushOptions) error { - if ps, ok := cw.writer().(http.Pusher); ok { - return ps.Push(target, opts) - } - return errors.New("chi/middleware: http.Pusher is unavailable on the writer") -} - -func (cw *compressResponseWriter) Close() error { - if c, ok := cw.writer().(io.WriteCloser); ok { - return c.Close() - } - return errors.New("chi/middleware: io.WriteCloser is unavailable on the writer") -} - -func (cw *compressResponseWriter) Unwrap() http.ResponseWriter { - return cw.ResponseWriter -} - -func encoderGzip(w io.Writer, level int) io.Writer { - gw, err := gzip.NewWriterLevel(w, level) - if err != nil { - return nil - } - return gw -} - -func encoderDeflate(w io.Writer, level int) io.Writer { - dw, err := flate.NewWriter(w, level) - if err != nil { - return nil - } - return dw -} diff --git a/backend/vendor/github.com/go-chi/chi/v5/middleware/content_charset.go b/backend/vendor/github.com/go-chi/chi/v5/middleware/content_charset.go deleted file mode 100644 index 07b5ce6f..00000000 --- a/backend/vendor/github.com/go-chi/chi/v5/middleware/content_charset.go +++ /dev/null @@ -1,51 +0,0 @@ -package middleware - -import ( - "net/http" - "strings" -) - -// ContentCharset generates a handler that writes a 415 Unsupported Media Type response if none of the charsets match. -// An empty charset will allow requests with no Content-Type header or no specified charset. -func ContentCharset(charsets ...string) func(next http.Handler) http.Handler { - for i, c := range charsets { - charsets[i] = strings.ToLower(c) - } - - return func(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if !contentEncoding(r.Header.Get("Content-Type"), charsets...) { - w.WriteHeader(http.StatusUnsupportedMediaType) - return - } - - next.ServeHTTP(w, r) - }) - } -} - -// Check the content encoding against a list of acceptable values. -func contentEncoding(ce string, charsets ...string) bool { - _, ce = split(strings.ToLower(ce), ";") - _, ce = split(ce, "charset=") - ce, _ = split(ce, ";") - for _, c := range charsets { - if ce == c { - return true - } - } - - return false -} - -// Split a string in two parts, cleaning any whitespace. -func split(str, sep string) (string, string) { - var a, b string - var parts = strings.SplitN(str, sep, 2) - a = strings.TrimSpace(parts[0]) - if len(parts) == 2 { - b = strings.TrimSpace(parts[1]) - } - - return a, b -} diff --git a/backend/vendor/github.com/go-chi/chi/v5/middleware/content_encoding.go b/backend/vendor/github.com/go-chi/chi/v5/middleware/content_encoding.go deleted file mode 100644 index e0b9ccc0..00000000 --- a/backend/vendor/github.com/go-chi/chi/v5/middleware/content_encoding.go +++ /dev/null @@ -1,34 +0,0 @@ -package middleware - -import ( - "net/http" - "strings" -) - -// AllowContentEncoding enforces a whitelist of request Content-Encoding otherwise responds -// with a 415 Unsupported Media Type status. -func AllowContentEncoding(contentEncoding ...string) func(next http.Handler) http.Handler { - allowedEncodings := make(map[string]struct{}, len(contentEncoding)) - for _, encoding := range contentEncoding { - allowedEncodings[strings.TrimSpace(strings.ToLower(encoding))] = struct{}{} - } - return func(next http.Handler) http.Handler { - fn := func(w http.ResponseWriter, r *http.Request) { - requestEncodings := r.Header["Content-Encoding"] - // skip check for empty content body or no Content-Encoding - if r.ContentLength == 0 { - next.ServeHTTP(w, r) - return - } - // All encodings in the request must be allowed - for _, encoding := range requestEncodings { - if _, ok := allowedEncodings[strings.TrimSpace(strings.ToLower(encoding))]; !ok { - w.WriteHeader(http.StatusUnsupportedMediaType) - return - } - } - next.ServeHTTP(w, r) - } - return http.HandlerFunc(fn) - } -} diff --git a/backend/vendor/github.com/go-chi/chi/v5/middleware/content_type.go b/backend/vendor/github.com/go-chi/chi/v5/middleware/content_type.go deleted file mode 100644 index e61ff264..00000000 --- a/backend/vendor/github.com/go-chi/chi/v5/middleware/content_type.go +++ /dev/null @@ -1,45 +0,0 @@ -package middleware - -import ( - "net/http" - "strings" -) - -// SetHeader is a convenience handler to set a response header key/value -func SetHeader(key, value string) func(http.Handler) http.Handler { - return func(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Header().Set(key, value) - next.ServeHTTP(w, r) - }) - } -} - -// AllowContentType enforces a whitelist of request Content-Types otherwise responds -// with a 415 Unsupported Media Type status. -func AllowContentType(contentTypes ...string) func(http.Handler) http.Handler { - allowedContentTypes := make(map[string]struct{}, len(contentTypes)) - for _, ctype := range contentTypes { - allowedContentTypes[strings.TrimSpace(strings.ToLower(ctype))] = struct{}{} - } - - return func(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if r.ContentLength == 0 { - // Skip check for empty content body - next.ServeHTTP(w, r) - return - } - - s := strings.ToLower(strings.TrimSpace(strings.Split(r.Header.Get("Content-Type"), ";")[0])) - - if _, ok := allowedContentTypes[s]; ok { - next.ServeHTTP(w, r) - return - } - - w.WriteHeader(http.StatusUnsupportedMediaType) - }) - } -} - diff --git a/backend/vendor/github.com/go-chi/chi/v5/middleware/get_head.go b/backend/vendor/github.com/go-chi/chi/v5/middleware/get_head.go deleted file mode 100644 index d4606d8b..00000000 --- a/backend/vendor/github.com/go-chi/chi/v5/middleware/get_head.go +++ /dev/null @@ -1,39 +0,0 @@ -package middleware - -import ( - "net/http" - - "github.com/go-chi/chi/v5" -) - -// GetHead automatically route undefined HEAD requests to GET handlers. -func GetHead(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if r.Method == "HEAD" { - rctx := chi.RouteContext(r.Context()) - routePath := rctx.RoutePath - if routePath == "" { - if r.URL.RawPath != "" { - routePath = r.URL.RawPath - } else { - routePath = r.URL.Path - } - } - - // Temporary routing context to look-ahead before routing the request - tctx := chi.NewRouteContext() - - // Attempt to find a HEAD handler for the routing path, if not found, traverse - // the router as through its a GET route, but proceed with the request - // with the HEAD method. - if !rctx.Routes.Match(tctx, "HEAD", routePath) { - rctx.RouteMethod = "GET" - rctx.RoutePath = routePath - next.ServeHTTP(w, r) - return - } - } - - next.ServeHTTP(w, r) - }) -} diff --git a/backend/vendor/github.com/go-chi/chi/v5/middleware/heartbeat.go b/backend/vendor/github.com/go-chi/chi/v5/middleware/heartbeat.go deleted file mode 100644 index f36e8ccf..00000000 --- a/backend/vendor/github.com/go-chi/chi/v5/middleware/heartbeat.go +++ /dev/null @@ -1,26 +0,0 @@ -package middleware - -import ( - "net/http" - "strings" -) - -// Heartbeat endpoint middleware useful to setting up a path like -// `/ping` that load balancers or uptime testing external services -// can make a request before hitting any routes. It's also convenient -// to place this above ACL middlewares as well. -func Heartbeat(endpoint string) func(http.Handler) http.Handler { - f := func(h http.Handler) http.Handler { - fn := func(w http.ResponseWriter, r *http.Request) { - if (r.Method == "GET" || r.Method == "HEAD") && strings.EqualFold(r.URL.Path, endpoint) { - w.Header().Set("Content-Type", "text/plain") - w.WriteHeader(http.StatusOK) - w.Write([]byte(".")) - return - } - h.ServeHTTP(w, r) - } - return http.HandlerFunc(fn) - } - return f -} diff --git a/backend/vendor/github.com/go-chi/chi/v5/middleware/logger.go b/backend/vendor/github.com/go-chi/chi/v5/middleware/logger.go deleted file mode 100644 index cff9bd20..00000000 --- a/backend/vendor/github.com/go-chi/chi/v5/middleware/logger.go +++ /dev/null @@ -1,172 +0,0 @@ -package middleware - -import ( - "bytes" - "context" - "log" - "net/http" - "os" - "runtime" - "time" -) - -var ( - // LogEntryCtxKey is the context.Context key to store the request log entry. - LogEntryCtxKey = &contextKey{"LogEntry"} - - // DefaultLogger is called by the Logger middleware handler to log each request. - // Its made a package-level variable so that it can be reconfigured for custom - // logging configurations. - DefaultLogger func(next http.Handler) http.Handler -) - -// Logger is a middleware that logs the start and end of each request, along -// with some useful data about what was requested, what the response status was, -// and how long it took to return. When standard output is a TTY, Logger will -// print in color, otherwise it will print in black and white. Logger prints a -// request ID if one is provided. -// -// Alternatively, look at https://github.com/goware/httplog for a more in-depth -// http logger with structured logging support. -// -// IMPORTANT NOTE: Logger should go before any other middleware that may change -// the response, such as middleware.Recoverer. Example: -// -// r := chi.NewRouter() -// r.Use(middleware.Logger) // <--<< Logger should come before Recoverer -// r.Use(middleware.Recoverer) -// r.Get("/", handler) -func Logger(next http.Handler) http.Handler { - return DefaultLogger(next) -} - -// RequestLogger returns a logger handler using a custom LogFormatter. -func RequestLogger(f LogFormatter) func(next http.Handler) http.Handler { - return func(next http.Handler) http.Handler { - fn := func(w http.ResponseWriter, r *http.Request) { - entry := f.NewLogEntry(r) - ww := NewWrapResponseWriter(w, r.ProtoMajor) - - t1 := time.Now() - defer func() { - entry.Write(ww.Status(), ww.BytesWritten(), ww.Header(), time.Since(t1), nil) - }() - - next.ServeHTTP(ww, WithLogEntry(r, entry)) - } - return http.HandlerFunc(fn) - } -} - -// LogFormatter initiates the beginning of a new LogEntry per request. -// See DefaultLogFormatter for an example implementation. -type LogFormatter interface { - NewLogEntry(r *http.Request) LogEntry -} - -// LogEntry records the final log when a request completes. -// See defaultLogEntry for an example implementation. -type LogEntry interface { - Write(status, bytes int, header http.Header, elapsed time.Duration, extra interface{}) - Panic(v interface{}, stack []byte) -} - -// GetLogEntry returns the in-context LogEntry for a request. -func GetLogEntry(r *http.Request) LogEntry { - entry, _ := r.Context().Value(LogEntryCtxKey).(LogEntry) - return entry -} - -// WithLogEntry sets the in-context LogEntry for a request. -func WithLogEntry(r *http.Request, entry LogEntry) *http.Request { - r = r.WithContext(context.WithValue(r.Context(), LogEntryCtxKey, entry)) - return r -} - -// LoggerInterface accepts printing to stdlib logger or compatible logger. -type LoggerInterface interface { - Print(v ...interface{}) -} - -// DefaultLogFormatter is a simple logger that implements a LogFormatter. -type DefaultLogFormatter struct { - Logger LoggerInterface - NoColor bool -} - -// NewLogEntry creates a new LogEntry for the request. -func (l *DefaultLogFormatter) NewLogEntry(r *http.Request) LogEntry { - useColor := !l.NoColor - entry := &defaultLogEntry{ - DefaultLogFormatter: l, - request: r, - buf: &bytes.Buffer{}, - useColor: useColor, - } - - reqID := GetReqID(r.Context()) - if reqID != "" { - cW(entry.buf, useColor, nYellow, "[%s] ", reqID) - } - cW(entry.buf, useColor, nCyan, "\"") - cW(entry.buf, useColor, bMagenta, "%s ", r.Method) - - scheme := "http" - if r.TLS != nil { - scheme = "https" - } - cW(entry.buf, useColor, nCyan, "%s://%s%s %s\" ", scheme, r.Host, r.RequestURI, r.Proto) - - entry.buf.WriteString("from ") - entry.buf.WriteString(r.RemoteAddr) - entry.buf.WriteString(" - ") - - return entry -} - -type defaultLogEntry struct { - *DefaultLogFormatter - request *http.Request - buf *bytes.Buffer - useColor bool -} - -func (l *defaultLogEntry) Write(status, bytes int, header http.Header, elapsed time.Duration, extra interface{}) { - switch { - case status < 200: - cW(l.buf, l.useColor, bBlue, "%03d", status) - case status < 300: - cW(l.buf, l.useColor, bGreen, "%03d", status) - case status < 400: - cW(l.buf, l.useColor, bCyan, "%03d", status) - case status < 500: - cW(l.buf, l.useColor, bYellow, "%03d", status) - default: - cW(l.buf, l.useColor, bRed, "%03d", status) - } - - cW(l.buf, l.useColor, bBlue, " %dB", bytes) - - l.buf.WriteString(" in ") - if elapsed < 500*time.Millisecond { - cW(l.buf, l.useColor, nGreen, "%s", elapsed) - } else if elapsed < 5*time.Second { - cW(l.buf, l.useColor, nYellow, "%s", elapsed) - } else { - cW(l.buf, l.useColor, nRed, "%s", elapsed) - } - - l.Logger.Print(l.buf.String()) -} - -func (l *defaultLogEntry) Panic(v interface{}, stack []byte) { - PrintPrettyStack(v) -} - -func init() { - color := true - if runtime.GOOS == "windows" { - color = false - } - DefaultLogger = RequestLogger(&DefaultLogFormatter{Logger: log.New(os.Stdout, "", log.LstdFlags), NoColor: !color}) -} diff --git a/backend/vendor/github.com/go-chi/chi/v5/middleware/maybe.go b/backend/vendor/github.com/go-chi/chi/v5/middleware/maybe.go deleted file mode 100644 index eabca005..00000000 --- a/backend/vendor/github.com/go-chi/chi/v5/middleware/maybe.go +++ /dev/null @@ -1,18 +0,0 @@ -package middleware - -import "net/http" - -// Maybe middleware will allow you to change the flow of the middleware stack execution depending on return -// value of maybeFn(request). This is useful for example if you'd like to skip a middleware handler if -// a request does not satisfy the maybeFn logic. -func Maybe(mw func(http.Handler) http.Handler, maybeFn func(r *http.Request) bool) func(http.Handler) http.Handler { - return func(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if maybeFn(r) { - mw(next).ServeHTTP(w, r) - } else { - next.ServeHTTP(w, r) - } - }) - } -} diff --git a/backend/vendor/github.com/go-chi/chi/v5/middleware/middleware.go b/backend/vendor/github.com/go-chi/chi/v5/middleware/middleware.go deleted file mode 100644 index cc371e00..00000000 --- a/backend/vendor/github.com/go-chi/chi/v5/middleware/middleware.go +++ /dev/null @@ -1,23 +0,0 @@ -package middleware - -import "net/http" - -// New will create a new middleware handler from a http.Handler. -func New(h http.Handler) func(next http.Handler) http.Handler { - return func(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - h.ServeHTTP(w, r) - }) - } -} - -// contextKey is a value for use with context.WithValue. It's used as -// a pointer so it fits in an interface{} without allocation. This technique -// for defining context keys was copied from Go 1.7's new use of context in net/http. -type contextKey struct { - name string -} - -func (k *contextKey) String() string { - return "chi/middleware context value " + k.name -} diff --git a/backend/vendor/github.com/go-chi/chi/v5/middleware/nocache.go b/backend/vendor/github.com/go-chi/chi/v5/middleware/nocache.go deleted file mode 100644 index 9308d40d..00000000 --- a/backend/vendor/github.com/go-chi/chi/v5/middleware/nocache.go +++ /dev/null @@ -1,59 +0,0 @@ -package middleware - -// Ported from Goji's middleware, source: -// https://github.com/zenazn/goji/tree/master/web/middleware - -import ( - "net/http" - "time" -) - -// Unix epoch time -var epoch = time.Unix(0, 0).UTC().Format(http.TimeFormat) - -// Taken from https://github.com/mytrile/nocache -var noCacheHeaders = map[string]string{ - "Expires": epoch, - "Cache-Control": "no-cache, no-store, no-transform, must-revalidate, private, max-age=0", - "Pragma": "no-cache", - "X-Accel-Expires": "0", -} - -var etagHeaders = []string{ - "ETag", - "If-Modified-Since", - "If-Match", - "If-None-Match", - "If-Range", - "If-Unmodified-Since", -} - -// NoCache is a simple piece of middleware that sets a number of HTTP headers to prevent -// a router (or subrouter) from being cached by an upstream proxy and/or client. -// -// As per http://wiki.nginx.org/HttpProxyModule - NoCache sets: -// -// Expires: Thu, 01 Jan 1970 00:00:00 UTC -// Cache-Control: no-cache, private, max-age=0 -// X-Accel-Expires: 0 -// Pragma: no-cache (for HTTP/1.0 proxies/clients) -func NoCache(h http.Handler) http.Handler { - fn := func(w http.ResponseWriter, r *http.Request) { - - // Delete any ETag headers that may have been set - for _, v := range etagHeaders { - if r.Header.Get(v) != "" { - r.Header.Del(v) - } - } - - // Set our NoCache headers - for k, v := range noCacheHeaders { - w.Header().Set(k, v) - } - - h.ServeHTTP(w, r) - } - - return http.HandlerFunc(fn) -} diff --git a/backend/vendor/github.com/go-chi/chi/v5/middleware/page_route.go b/backend/vendor/github.com/go-chi/chi/v5/middleware/page_route.go deleted file mode 100644 index 32871b7e..00000000 --- a/backend/vendor/github.com/go-chi/chi/v5/middleware/page_route.go +++ /dev/null @@ -1,20 +0,0 @@ -package middleware - -import ( - "net/http" - "strings" -) - -// PageRoute is a simple middleware which allows you to route a static GET request -// at the middleware stack level. -func PageRoute(path string, handler http.Handler) func(http.Handler) http.Handler { - return func(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if r.Method == "GET" && strings.EqualFold(r.URL.Path, path) { - handler.ServeHTTP(w, r) - return - } - next.ServeHTTP(w, r) - }) - } -} diff --git a/backend/vendor/github.com/go-chi/chi/v5/middleware/path_rewrite.go b/backend/vendor/github.com/go-chi/chi/v5/middleware/path_rewrite.go deleted file mode 100644 index 99af62c0..00000000 --- a/backend/vendor/github.com/go-chi/chi/v5/middleware/path_rewrite.go +++ /dev/null @@ -1,16 +0,0 @@ -package middleware - -import ( - "net/http" - "strings" -) - -// PathRewrite is a simple middleware which allows you to rewrite the request URL path. -func PathRewrite(old, new string) func(http.Handler) http.Handler { - return func(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - r.URL.Path = strings.Replace(r.URL.Path, old, new, 1) - next.ServeHTTP(w, r) - }) - } -} diff --git a/backend/vendor/github.com/go-chi/chi/v5/middleware/profiler.go b/backend/vendor/github.com/go-chi/chi/v5/middleware/profiler.go deleted file mode 100644 index ebd81ee4..00000000 --- a/backend/vendor/github.com/go-chi/chi/v5/middleware/profiler.go +++ /dev/null @@ -1,46 +0,0 @@ -package middleware - -import ( - "expvar" - "net/http" - "net/http/pprof" - - "github.com/go-chi/chi/v5" -) - -// Profiler is a convenient subrouter used for mounting net/http/pprof. ie. -// -// func MyService() http.Handler { -// r := chi.NewRouter() -// // ..middlewares -// r.Mount("/debug", middleware.Profiler()) -// // ..routes -// return r -// } -func Profiler() http.Handler { - r := chi.NewRouter() - r.Use(NoCache) - - r.Get("/", func(w http.ResponseWriter, r *http.Request) { - http.Redirect(w, r, r.RequestURI+"/pprof/", http.StatusMovedPermanently) - }) - r.HandleFunc("/pprof", func(w http.ResponseWriter, r *http.Request) { - http.Redirect(w, r, r.RequestURI+"/", http.StatusMovedPermanently) - }) - - r.HandleFunc("/pprof/*", pprof.Index) - r.HandleFunc("/pprof/cmdline", pprof.Cmdline) - r.HandleFunc("/pprof/profile", pprof.Profile) - r.HandleFunc("/pprof/symbol", pprof.Symbol) - r.HandleFunc("/pprof/trace", pprof.Trace) - r.Handle("/vars", expvar.Handler()) - - r.Handle("/pprof/goroutine", pprof.Handler("goroutine")) - r.Handle("/pprof/threadcreate", pprof.Handler("threadcreate")) - r.Handle("/pprof/mutex", pprof.Handler("mutex")) - r.Handle("/pprof/heap", pprof.Handler("heap")) - r.Handle("/pprof/block", pprof.Handler("block")) - r.Handle("/pprof/allocs", pprof.Handler("allocs")) - - return r -} diff --git a/backend/vendor/github.com/go-chi/chi/v5/middleware/realip.go b/backend/vendor/github.com/go-chi/chi/v5/middleware/realip.go deleted file mode 100644 index 55c95a89..00000000 --- a/backend/vendor/github.com/go-chi/chi/v5/middleware/realip.go +++ /dev/null @@ -1,60 +0,0 @@ -package middleware - -// Ported from Goji's middleware, source: -// https://github.com/zenazn/goji/tree/master/web/middleware - -import ( - "net" - "net/http" - "strings" -) - -var trueClientIP = http.CanonicalHeaderKey("True-Client-IP") -var xForwardedFor = http.CanonicalHeaderKey("X-Forwarded-For") -var xRealIP = http.CanonicalHeaderKey("X-Real-IP") - -// RealIP is a middleware that sets a http.Request's RemoteAddr to the results -// of parsing either the True-Client-IP, X-Real-IP or the X-Forwarded-For headers -// (in that order). -// -// This middleware should be inserted fairly early in the middleware stack to -// ensure that subsequent layers (e.g., request loggers) which examine the -// RemoteAddr will see the intended value. -// -// You should only use this middleware if you can trust the headers passed to -// you (in particular, the three headers this middleware uses), for example -// because you have placed a reverse proxy like HAProxy or nginx in front of -// chi. If your reverse proxies are configured to pass along arbitrary header -// values from the client, or if you use this middleware without a reverse -// proxy, malicious clients will be able to make you very sad (or, depending on -// how you're using RemoteAddr, vulnerable to an attack of some sort). -func RealIP(h http.Handler) http.Handler { - fn := func(w http.ResponseWriter, r *http.Request) { - if rip := realIP(r); rip != "" { - r.RemoteAddr = rip - } - h.ServeHTTP(w, r) - } - - return http.HandlerFunc(fn) -} - -func realIP(r *http.Request) string { - var ip string - - if tcip := r.Header.Get(trueClientIP); tcip != "" { - ip = tcip - } else if xrip := r.Header.Get(xRealIP); xrip != "" { - ip = xrip - } else if xff := r.Header.Get(xForwardedFor); xff != "" { - i := strings.Index(xff, ",") - if i == -1 { - i = len(xff) - } - ip = xff[:i] - } - if ip == "" || net.ParseIP(ip) == nil { - return "" - } - return ip -} diff --git a/backend/vendor/github.com/go-chi/chi/v5/middleware/recoverer.go b/backend/vendor/github.com/go-chi/chi/v5/middleware/recoverer.go deleted file mode 100644 index 81342dfa..00000000 --- a/backend/vendor/github.com/go-chi/chi/v5/middleware/recoverer.go +++ /dev/null @@ -1,203 +0,0 @@ -package middleware - -// The original work was derived from Goji's middleware, source: -// https://github.com/zenazn/goji/tree/master/web/middleware - -import ( - "bytes" - "errors" - "fmt" - "io" - "net/http" - "os" - "runtime/debug" - "strings" -) - -// Recoverer is a middleware that recovers from panics, logs the panic (and a -// backtrace), and returns a HTTP 500 (Internal Server Error) status if -// possible. Recoverer prints a request ID if one is provided. -// -// Alternatively, look at https://github.com/go-chi/httplog middleware pkgs. -func Recoverer(next http.Handler) http.Handler { - fn := func(w http.ResponseWriter, r *http.Request) { - defer func() { - if rvr := recover(); rvr != nil { - if rvr == http.ErrAbortHandler { - // we don't recover http.ErrAbortHandler so the response - // to the client is aborted, this should not be logged - panic(rvr) - } - - logEntry := GetLogEntry(r) - if logEntry != nil { - logEntry.Panic(rvr, debug.Stack()) - } else { - PrintPrettyStack(rvr) - } - - if r.Header.Get("Connection") != "Upgrade" { - w.WriteHeader(http.StatusInternalServerError) - } - } - }() - - next.ServeHTTP(w, r) - } - - return http.HandlerFunc(fn) -} - -// for ability to test the PrintPrettyStack function -var recovererErrorWriter io.Writer = os.Stderr - -func PrintPrettyStack(rvr interface{}) { - debugStack := debug.Stack() - s := prettyStack{} - out, err := s.parse(debugStack, rvr) - if err == nil { - recovererErrorWriter.Write(out) - } else { - // print stdlib output as a fallback - os.Stderr.Write(debugStack) - } -} - -type prettyStack struct { -} - -func (s prettyStack) parse(debugStack []byte, rvr interface{}) ([]byte, error) { - var err error - useColor := true - buf := &bytes.Buffer{} - - cW(buf, false, bRed, "\n") - cW(buf, useColor, bCyan, " panic: ") - cW(buf, useColor, bBlue, "%v", rvr) - cW(buf, false, bWhite, "\n \n") - - // process debug stack info - stack := strings.Split(string(debugStack), "\n") - lines := []string{} - - // locate panic line, as we may have nested panics - for i := len(stack) - 1; i > 0; i-- { - lines = append(lines, stack[i]) - if strings.HasPrefix(stack[i], "panic(") { - lines = lines[0 : len(lines)-2] // remove boilerplate - break - } - } - - // reverse - for i := len(lines)/2 - 1; i >= 0; i-- { - opp := len(lines) - 1 - i - lines[i], lines[opp] = lines[opp], lines[i] - } - - // decorate - for i, line := range lines { - lines[i], err = s.decorateLine(line, useColor, i) - if err != nil { - return nil, err - } - } - - for _, l := range lines { - fmt.Fprintf(buf, "%s", l) - } - return buf.Bytes(), nil -} - -func (s prettyStack) decorateLine(line string, useColor bool, num int) (string, error) { - line = strings.TrimSpace(line) - if strings.HasPrefix(line, "\t") || strings.Contains(line, ".go:") { - return s.decorateSourceLine(line, useColor, num) - } - if strings.HasSuffix(line, ")") { - return s.decorateFuncCallLine(line, useColor, num) - } - if strings.HasPrefix(line, "\t") { - return strings.Replace(line, "\t", " ", 1), nil - } - return fmt.Sprintf(" %s\n", line), nil -} - -func (s prettyStack) decorateFuncCallLine(line string, useColor bool, num int) (string, error) { - idx := strings.LastIndex(line, "(") - if idx < 0 { - return "", errors.New("not a func call line") - } - - buf := &bytes.Buffer{} - pkg := line[0:idx] - // addr := line[idx:] - method := "" - - if idx := strings.LastIndex(pkg, string(os.PathSeparator)); idx < 0 { - if idx := strings.Index(pkg, "."); idx > 0 { - method = pkg[idx:] - pkg = pkg[0:idx] - } - } else { - method = pkg[idx+1:] - pkg = pkg[0 : idx+1] - if idx := strings.Index(method, "."); idx > 0 { - pkg += method[0:idx] - method = method[idx:] - } - } - pkgColor := nYellow - methodColor := bGreen - - if num == 0 { - cW(buf, useColor, bRed, " -> ") - pkgColor = bMagenta - methodColor = bRed - } else { - cW(buf, useColor, bWhite, " ") - } - cW(buf, useColor, pkgColor, "%s", pkg) - cW(buf, useColor, methodColor, "%s\n", method) - // cW(buf, useColor, nBlack, "%s", addr) - return buf.String(), nil -} - -func (s prettyStack) decorateSourceLine(line string, useColor bool, num int) (string, error) { - idx := strings.LastIndex(line, ".go:") - if idx < 0 { - return "", errors.New("not a source line") - } - - buf := &bytes.Buffer{} - path := line[0 : idx+3] - lineno := line[idx+3:] - - idx = strings.LastIndex(path, string(os.PathSeparator)) - dir := path[0 : idx+1] - file := path[idx+1:] - - idx = strings.Index(lineno, " ") - if idx > 0 { - lineno = lineno[0:idx] - } - fileColor := bCyan - lineColor := bGreen - - if num == 1 { - cW(buf, useColor, bRed, " -> ") - fileColor = bRed - lineColor = bMagenta - } else { - cW(buf, false, bWhite, " ") - } - cW(buf, useColor, bWhite, "%s", dir) - cW(buf, useColor, fileColor, "%s", file) - cW(buf, useColor, lineColor, "%s", lineno) - if num == 1 { - cW(buf, false, bWhite, "\n") - } - cW(buf, false, bWhite, "\n") - - return buf.String(), nil -} diff --git a/backend/vendor/github.com/go-chi/chi/v5/middleware/request_id.go b/backend/vendor/github.com/go-chi/chi/v5/middleware/request_id.go deleted file mode 100644 index 4903ecc2..00000000 --- a/backend/vendor/github.com/go-chi/chi/v5/middleware/request_id.go +++ /dev/null @@ -1,96 +0,0 @@ -package middleware - -// Ported from Goji's middleware, source: -// https://github.com/zenazn/goji/tree/master/web/middleware - -import ( - "context" - "crypto/rand" - "encoding/base64" - "fmt" - "net/http" - "os" - "strings" - "sync/atomic" -) - -// Key to use when setting the request ID. -type ctxKeyRequestID int - -// RequestIDKey is the key that holds the unique request ID in a request context. -const RequestIDKey ctxKeyRequestID = 0 - -// RequestIDHeader is the name of the HTTP Header which contains the request id. -// Exported so that it can be changed by developers -var RequestIDHeader = "X-Request-Id" - -var prefix string -var reqid uint64 - -// A quick note on the statistics here: we're trying to calculate the chance that -// two randomly generated base62 prefixes will collide. We use the formula from -// http://en.wikipedia.org/wiki/Birthday_problem -// -// P[m, n] \approx 1 - e^{-m^2/2n} -// -// We ballpark an upper bound for $m$ by imagining (for whatever reason) a server -// that restarts every second over 10 years, for $m = 86400 * 365 * 10 = 315360000$ -// -// For a $k$ character base-62 identifier, we have $n(k) = 62^k$ -// -// Plugging this in, we find $P[m, n(10)] \approx 5.75%$, which is good enough for -// our purposes, and is surely more than anyone would ever need in practice -- a -// process that is rebooted a handful of times a day for a hundred years has less -// than a millionth of a percent chance of generating two colliding IDs. - -func init() { - hostname, err := os.Hostname() - if hostname == "" || err != nil { - hostname = "localhost" - } - var buf [12]byte - var b64 string - for len(b64) < 10 { - rand.Read(buf[:]) - b64 = base64.StdEncoding.EncodeToString(buf[:]) - b64 = strings.NewReplacer("+", "", "/", "").Replace(b64) - } - - prefix = fmt.Sprintf("%s/%s", hostname, b64[0:10]) -} - -// RequestID is a middleware that injects a request ID into the context of each -// request. A request ID is a string of the form "host.example.com/random-0001", -// where "random" is a base62 random string that uniquely identifies this go -// process, and where the last number is an atomically incremented request -// counter. -func RequestID(next http.Handler) http.Handler { - fn := func(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - requestID := r.Header.Get(RequestIDHeader) - if requestID == "" { - myid := atomic.AddUint64(&reqid, 1) - requestID = fmt.Sprintf("%s-%06d", prefix, myid) - } - ctx = context.WithValue(ctx, RequestIDKey, requestID) - next.ServeHTTP(w, r.WithContext(ctx)) - } - return http.HandlerFunc(fn) -} - -// GetReqID returns a request ID from the given context if one is present. -// Returns the empty string if a request ID cannot be found. -func GetReqID(ctx context.Context) string { - if ctx == nil { - return "" - } - if reqID, ok := ctx.Value(RequestIDKey).(string); ok { - return reqID - } - return "" -} - -// NextRequestID generates the next request ID in the sequence. -func NextRequestID() uint64 { - return atomic.AddUint64(&reqid, 1) -} diff --git a/backend/vendor/github.com/go-chi/chi/v5/middleware/request_size.go b/backend/vendor/github.com/go-chi/chi/v5/middleware/request_size.go deleted file mode 100644 index 678248c4..00000000 --- a/backend/vendor/github.com/go-chi/chi/v5/middleware/request_size.go +++ /dev/null @@ -1,18 +0,0 @@ -package middleware - -import ( - "net/http" -) - -// RequestSize is a middleware that will limit request sizes to a specified -// number of bytes. It uses MaxBytesReader to do so. -func RequestSize(bytes int64) func(http.Handler) http.Handler { - f := func(h http.Handler) http.Handler { - fn := func(w http.ResponseWriter, r *http.Request) { - r.Body = http.MaxBytesReader(w, r.Body, bytes) - h.ServeHTTP(w, r) - } - return http.HandlerFunc(fn) - } - return f -} diff --git a/backend/vendor/github.com/go-chi/chi/v5/middleware/route_headers.go b/backend/vendor/github.com/go-chi/chi/v5/middleware/route_headers.go deleted file mode 100644 index 997bed56..00000000 --- a/backend/vendor/github.com/go-chi/chi/v5/middleware/route_headers.go +++ /dev/null @@ -1,151 +0,0 @@ -package middleware - -import ( - "net/http" - "strings" -) - -// RouteHeaders is a neat little header-based router that allows you to direct -// the flow of a request through a middleware stack based on a request header. -// -// For example, lets say you'd like to setup multiple routers depending on the -// request Host header, you could then do something as so: -// -// r := chi.NewRouter() -// rSubdomain := chi.NewRouter() -// r.Use(middleware.RouteHeaders(). -// Route("Host", "example.com", middleware.New(r)). -// Route("Host", "*.example.com", middleware.New(rSubdomain)). -// Handler) -// r.Get("/", h) -// rSubdomain.Get("/", h2) -// -// Another example, imagine you want to setup multiple CORS handlers, where for -// your origin servers you allow authorized requests, but for third-party public -// requests, authorization is disabled. -// -// r := chi.NewRouter() -// r.Use(middleware.RouteHeaders(). -// Route("Origin", "https://app.skyweaver.net", cors.Handler(cors.Options{ -// AllowedOrigins: []string{"https://api.skyweaver.net"}, -// AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"}, -// AllowedHeaders: []string{"Accept", "Authorization", "Content-Type"}, -// AllowCredentials: true, // <----------<<< allow credentials -// })). -// Route("Origin", "*", cors.Handler(cors.Options{ -// AllowedOrigins: []string{"*"}, -// AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"}, -// AllowedHeaders: []string{"Accept", "Content-Type"}, -// AllowCredentials: false, // <----------<<< do not allow credentials -// })). -// Handler) -func RouteHeaders() HeaderRouter { - return HeaderRouter{} -} - -type HeaderRouter map[string][]HeaderRoute - -func (hr HeaderRouter) Route(header, match string, middlewareHandler func(next http.Handler) http.Handler) HeaderRouter { - header = strings.ToLower(header) - k := hr[header] - if k == nil { - hr[header] = []HeaderRoute{} - } - hr[header] = append(hr[header], HeaderRoute{MatchOne: NewPattern(match), Middleware: middlewareHandler}) - return hr -} - -func (hr HeaderRouter) RouteAny(header string, match []string, middlewareHandler func(next http.Handler) http.Handler) HeaderRouter { - header = strings.ToLower(header) - k := hr[header] - if k == nil { - hr[header] = []HeaderRoute{} - } - patterns := []Pattern{} - for _, m := range match { - patterns = append(patterns, NewPattern(m)) - } - hr[header] = append(hr[header], HeaderRoute{MatchAny: patterns, Middleware: middlewareHandler}) - return hr -} - -func (hr HeaderRouter) RouteDefault(handler func(next http.Handler) http.Handler) HeaderRouter { - hr["*"] = []HeaderRoute{{Middleware: handler}} - return hr -} - -func (hr HeaderRouter) Handler(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if len(hr) == 0 { - // skip if no routes set - next.ServeHTTP(w, r) - } - - // find first matching header route, and continue - for header, matchers := range hr { - headerValue := r.Header.Get(header) - if headerValue == "" { - continue - } - headerValue = strings.ToLower(headerValue) - for _, matcher := range matchers { - if matcher.IsMatch(headerValue) { - matcher.Middleware(next).ServeHTTP(w, r) - return - } - } - } - - // if no match, check for "*" default route - matcher, ok := hr["*"] - if !ok || matcher[0].Middleware == nil { - next.ServeHTTP(w, r) - return - } - matcher[0].Middleware(next).ServeHTTP(w, r) - }) -} - -type HeaderRoute struct { - Middleware func(next http.Handler) http.Handler - MatchOne Pattern - MatchAny []Pattern -} - -func (r HeaderRoute) IsMatch(value string) bool { - if len(r.MatchAny) > 0 { - for _, m := range r.MatchAny { - if m.Match(value) { - return true - } - } - } else if r.MatchOne.Match(value) { - return true - } - return false -} - -type Pattern struct { - prefix string - suffix string - wildcard bool -} - -func NewPattern(value string) Pattern { - p := Pattern{} - if i := strings.IndexByte(value, '*'); i >= 0 { - p.wildcard = true - p.prefix = value[0:i] - p.suffix = value[i+1:] - } else { - p.prefix = value - } - return p -} - -func (p Pattern) Match(v string) bool { - if !p.wildcard { - return p.prefix == v - } - return len(v) >= len(p.prefix+p.suffix) && strings.HasPrefix(v, p.prefix) && strings.HasSuffix(v, p.suffix) -} diff --git a/backend/vendor/github.com/go-chi/chi/v5/middleware/strip.go b/backend/vendor/github.com/go-chi/chi/v5/middleware/strip.go deleted file mode 100644 index 1368fa7a..00000000 --- a/backend/vendor/github.com/go-chi/chi/v5/middleware/strip.go +++ /dev/null @@ -1,70 +0,0 @@ -package middleware - -import ( - "fmt" - "net/http" - - "github.com/go-chi/chi/v5" -) - -// StripSlashes is a middleware that will match request paths with a trailing -// slash, strip it from the path and continue routing through the mux, if a route -// matches, then it will serve the handler. -func StripSlashes(next http.Handler) http.Handler { - fn := func(w http.ResponseWriter, r *http.Request) { - var path string - rctx := chi.RouteContext(r.Context()) - if rctx != nil && rctx.RoutePath != "" { - path = rctx.RoutePath - } else { - path = r.URL.Path - } - if len(path) > 1 && path[len(path)-1] == '/' { - newPath := path[:len(path)-1] - if rctx == nil { - r.URL.Path = newPath - } else { - rctx.RoutePath = newPath - } - } - next.ServeHTTP(w, r) - } - return http.HandlerFunc(fn) -} - -// RedirectSlashes is a middleware that will match request paths with a trailing -// slash and redirect to the same path, less the trailing slash. -// -// NOTE: RedirectSlashes middleware is *incompatible* with http.FileServer, -// see https://github.com/go-chi/chi/issues/343 -func RedirectSlashes(next http.Handler) http.Handler { - fn := func(w http.ResponseWriter, r *http.Request) { - var path string - rctx := chi.RouteContext(r.Context()) - if rctx != nil && rctx.RoutePath != "" { - path = rctx.RoutePath - } else { - path = r.URL.Path - } - if len(path) > 1 && path[len(path)-1] == '/' { - if r.URL.RawQuery != "" { - path = fmt.Sprintf("%s?%s", path[:len(path)-1], r.URL.RawQuery) - } else { - path = path[:len(path)-1] - } - redirectURL := fmt.Sprintf("//%s%s", r.Host, path) - http.Redirect(w, r, redirectURL, 301) - return - } - next.ServeHTTP(w, r) - } - return http.HandlerFunc(fn) -} - -// StripPrefix is a middleware that will strip the provided prefix from the -// request path before handing the request over to the next handler. -func StripPrefix(prefix string) func(http.Handler) http.Handler { - return func(next http.Handler) http.Handler { - return http.StripPrefix(prefix, next) - } -} diff --git a/backend/vendor/github.com/go-chi/chi/v5/middleware/sunset.go b/backend/vendor/github.com/go-chi/chi/v5/middleware/sunset.go deleted file mode 100644 index 18815d58..00000000 --- a/backend/vendor/github.com/go-chi/chi/v5/middleware/sunset.go +++ /dev/null @@ -1,25 +0,0 @@ -package middleware - -import ( - "net/http" - "time" -) - -// Sunset set Deprecation/Sunset header to response -// This can be used to enable Sunset in a route or a route group -// For more: https://www.rfc-editor.org/rfc/rfc8594.html -func Sunset(sunsetAt time.Time, links ...string) func(http.Handler) http.Handler { - return func(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if !sunsetAt.IsZero() { - w.Header().Set("Sunset", sunsetAt.Format(http.TimeFormat)) - w.Header().Set("Deprecation", sunsetAt.Format(http.TimeFormat)) - - for _, link := range links { - w.Header().Add("Link", link) - } - } - next.ServeHTTP(w, r) - }) - } -} diff --git a/backend/vendor/github.com/go-chi/chi/v5/middleware/supress_notfound.go b/backend/vendor/github.com/go-chi/chi/v5/middleware/supress_notfound.go deleted file mode 100644 index 83a8a872..00000000 --- a/backend/vendor/github.com/go-chi/chi/v5/middleware/supress_notfound.go +++ /dev/null @@ -1,27 +0,0 @@ -package middleware - -import ( - "net/http" - - "github.com/go-chi/chi/v5" -) - -// SupressNotFound will quickly respond with a 404 if the route is not found -// and will not continue to the next middleware handler. -// -// This is handy to put at the top of your middleware stack to avoid unnecessary -// processing of requests that are not going to match any routes anyway. For -// example its super annoying to see a bunch of 404's in your logs from bots. -func SupressNotFound(router *chi.Mux) func(next http.Handler) http.Handler { - return func(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - rctx := chi.RouteContext(r.Context()) - match := rctx.Routes.Match(rctx, r.Method, r.URL.Path) - if !match { - router.NotFoundHandler().ServeHTTP(w, r) - return - } - next.ServeHTTP(w, r) - }) - } -} diff --git a/backend/vendor/github.com/go-chi/chi/v5/middleware/terminal.go b/backend/vendor/github.com/go-chi/chi/v5/middleware/terminal.go deleted file mode 100644 index 5ead7b92..00000000 --- a/backend/vendor/github.com/go-chi/chi/v5/middleware/terminal.go +++ /dev/null @@ -1,63 +0,0 @@ -package middleware - -// Ported from Goji's middleware, source: -// https://github.com/zenazn/goji/tree/master/web/middleware - -import ( - "fmt" - "io" - "os" -) - -var ( - // Normal colors - nBlack = []byte{'\033', '[', '3', '0', 'm'} - nRed = []byte{'\033', '[', '3', '1', 'm'} - nGreen = []byte{'\033', '[', '3', '2', 'm'} - nYellow = []byte{'\033', '[', '3', '3', 'm'} - nBlue = []byte{'\033', '[', '3', '4', 'm'} - nMagenta = []byte{'\033', '[', '3', '5', 'm'} - nCyan = []byte{'\033', '[', '3', '6', 'm'} - nWhite = []byte{'\033', '[', '3', '7', 'm'} - // Bright colors - bBlack = []byte{'\033', '[', '3', '0', ';', '1', 'm'} - bRed = []byte{'\033', '[', '3', '1', ';', '1', 'm'} - bGreen = []byte{'\033', '[', '3', '2', ';', '1', 'm'} - bYellow = []byte{'\033', '[', '3', '3', ';', '1', 'm'} - bBlue = []byte{'\033', '[', '3', '4', ';', '1', 'm'} - bMagenta = []byte{'\033', '[', '3', '5', ';', '1', 'm'} - bCyan = []byte{'\033', '[', '3', '6', ';', '1', 'm'} - bWhite = []byte{'\033', '[', '3', '7', ';', '1', 'm'} - - reset = []byte{'\033', '[', '0', 'm'} -) - -var IsTTY bool - -func init() { - // This is sort of cheating: if stdout is a character device, we assume - // that means it's a TTY. Unfortunately, there are many non-TTY - // character devices, but fortunately stdout is rarely set to any of - // them. - // - // We could solve this properly by pulling in a dependency on - // code.google.com/p/go.crypto/ssh/terminal, for instance, but as a - // heuristic for whether to print in color or in black-and-white, I'd - // really rather not. - fi, err := os.Stdout.Stat() - if err == nil { - m := os.ModeDevice | os.ModeCharDevice - IsTTY = fi.Mode()&m == m - } -} - -// colorWrite -func cW(w io.Writer, useColor bool, color []byte, s string, args ...interface{}) { - if IsTTY && useColor { - w.Write(color) - } - fmt.Fprintf(w, s, args...) - if IsTTY && useColor { - w.Write(reset) - } -} diff --git a/backend/vendor/github.com/go-chi/chi/v5/middleware/throttle.go b/backend/vendor/github.com/go-chi/chi/v5/middleware/throttle.go deleted file mode 100644 index 9a870d88..00000000 --- a/backend/vendor/github.com/go-chi/chi/v5/middleware/throttle.go +++ /dev/null @@ -1,140 +0,0 @@ -package middleware - -import ( - "net/http" - "strconv" - "time" -) - -const ( - errCapacityExceeded = "Server capacity exceeded." - errTimedOut = "Timed out while waiting for a pending request to complete." - errContextCanceled = "Context was canceled." -) - -var ( - defaultBacklogTimeout = time.Second * 60 -) - -// ThrottleOpts represents a set of throttling options. -type ThrottleOpts struct { - RetryAfterFn func(ctxDone bool) time.Duration - Limit int - BacklogLimit int - BacklogTimeout time.Duration - StatusCode int -} - -// Throttle is a middleware that limits number of currently processed requests -// at a time across all users. Note: Throttle is not a rate-limiter per user, -// instead it just puts a ceiling on the number of current in-flight requests -// being processed from the point from where the Throttle middleware is mounted. -func Throttle(limit int) func(http.Handler) http.Handler { - return ThrottleWithOpts(ThrottleOpts{Limit: limit, BacklogTimeout: defaultBacklogTimeout}) -} - -// ThrottleBacklog is a middleware that limits number of currently processed -// requests at a time and provides a backlog for holding a finite number of -// pending requests. -func ThrottleBacklog(limit, backlogLimit int, backlogTimeout time.Duration) func(http.Handler) http.Handler { - return ThrottleWithOpts(ThrottleOpts{Limit: limit, BacklogLimit: backlogLimit, BacklogTimeout: backlogTimeout}) -} - -// ThrottleWithOpts is a middleware that limits number of currently processed requests using passed ThrottleOpts. -func ThrottleWithOpts(opts ThrottleOpts) func(http.Handler) http.Handler { - if opts.Limit < 1 { - panic("chi/middleware: Throttle expects limit > 0") - } - - if opts.BacklogLimit < 0 { - panic("chi/middleware: Throttle expects backlogLimit to be positive") - } - - statusCode := opts.StatusCode - if statusCode == 0 { - statusCode = http.StatusTooManyRequests - } - - t := throttler{ - tokens: make(chan token, opts.Limit), - backlogTokens: make(chan token, opts.Limit+opts.BacklogLimit), - backlogTimeout: opts.BacklogTimeout, - statusCode: statusCode, - retryAfterFn: opts.RetryAfterFn, - } - - // Filling tokens. - for i := 0; i < opts.Limit+opts.BacklogLimit; i++ { - if i < opts.Limit { - t.tokens <- token{} - } - t.backlogTokens <- token{} - } - - return func(next http.Handler) http.Handler { - fn := func(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - - select { - - case <-ctx.Done(): - t.setRetryAfterHeaderIfNeeded(w, true) - http.Error(w, errContextCanceled, t.statusCode) - return - - case btok := <-t.backlogTokens: - timer := time.NewTimer(t.backlogTimeout) - - defer func() { - t.backlogTokens <- btok - }() - - select { - case <-timer.C: - t.setRetryAfterHeaderIfNeeded(w, false) - http.Error(w, errTimedOut, t.statusCode) - return - case <-ctx.Done(): - timer.Stop() - t.setRetryAfterHeaderIfNeeded(w, true) - http.Error(w, errContextCanceled, t.statusCode) - return - case tok := <-t.tokens: - defer func() { - timer.Stop() - t.tokens <- tok - }() - next.ServeHTTP(w, r) - } - return - - default: - t.setRetryAfterHeaderIfNeeded(w, false) - http.Error(w, errCapacityExceeded, t.statusCode) - return - } - } - - return http.HandlerFunc(fn) - } -} - -// token represents a request that is being processed. -type token struct{} - -// throttler limits number of currently processed requests at a time. -type throttler struct { - tokens chan token - backlogTokens chan token - backlogTimeout time.Duration - statusCode int - retryAfterFn func(ctxDone bool) time.Duration -} - -// setRetryAfterHeaderIfNeeded sets Retry-After HTTP header if corresponding retryAfterFn option of throttler is initialized. -func (t throttler) setRetryAfterHeaderIfNeeded(w http.ResponseWriter, ctxDone bool) { - if t.retryAfterFn == nil { - return - } - w.Header().Set("Retry-After", strconv.Itoa(int(t.retryAfterFn(ctxDone).Seconds()))) -} diff --git a/backend/vendor/github.com/go-chi/chi/v5/middleware/timeout.go b/backend/vendor/github.com/go-chi/chi/v5/middleware/timeout.go deleted file mode 100644 index add596d6..00000000 --- a/backend/vendor/github.com/go-chi/chi/v5/middleware/timeout.go +++ /dev/null @@ -1,48 +0,0 @@ -package middleware - -import ( - "context" - "net/http" - "time" -) - -// Timeout is a middleware that cancels ctx after a given timeout and return -// a 504 Gateway Timeout error to the client. -// -// It's required that you select the ctx.Done() channel to check for the signal -// if the context has reached its deadline and return, otherwise the timeout -// signal will be just ignored. -// -// ie. a route/handler may look like: -// -// r.Get("/long", func(w http.ResponseWriter, r *http.Request) { -// ctx := r.Context() -// processTime := time.Duration(rand.Intn(4)+1) * time.Second -// -// select { -// case <-ctx.Done(): -// return -// -// case <-time.After(processTime): -// // The above channel simulates some hard work. -// } -// -// w.Write([]byte("done")) -// }) -func Timeout(timeout time.Duration) func(next http.Handler) http.Handler { - return func(next http.Handler) http.Handler { - fn := func(w http.ResponseWriter, r *http.Request) { - ctx, cancel := context.WithTimeout(r.Context(), timeout) - defer func() { - cancel() - if ctx.Err() == context.DeadlineExceeded { - w.WriteHeader(http.StatusGatewayTimeout) - } - }() - - r = r.WithContext(ctx) - next.ServeHTTP(w, r) - } - return http.HandlerFunc(fn) - } -} diff --git a/backend/vendor/github.com/go-chi/chi/v5/middleware/url_format.go b/backend/vendor/github.com/go-chi/chi/v5/middleware/url_format.go deleted file mode 100644 index d8a651e4..00000000 --- a/backend/vendor/github.com/go-chi/chi/v5/middleware/url_format.go +++ /dev/null @@ -1,75 +0,0 @@ -package middleware - -import ( - "context" - "net/http" - "strings" - - "github.com/go-chi/chi/v5" -) - -var ( - // URLFormatCtxKey is the context.Context key to store the URL format data - // for a request. - URLFormatCtxKey = &contextKey{"URLFormat"} -) - -// URLFormat is a middleware that parses the url extension from a request path and stores it -// on the context as a string under the key `middleware.URLFormatCtxKey`. The middleware will -// trim the suffix from the routing path and continue routing. -// -// Routers should not include a url parameter for the suffix when using this middleware. -// -// Sample usage for url paths `/articles/1`, `/articles/1.json` and `/articles/1.xml`: -// -// func routes() http.Handler { -// r := chi.NewRouter() -// r.Use(middleware.URLFormat) -// -// r.Get("/articles/{id}", ListArticles) -// -// return r -// } -// -// func ListArticles(w http.ResponseWriter, r *http.Request) { -// urlFormat, _ := r.Context().Value(middleware.URLFormatCtxKey).(string) -// -// switch urlFormat { -// case "json": -// render.JSON(w, r, articles) -// case "xml:" -// render.XML(w, r, articles) -// default: -// render.JSON(w, r, articles) -// } -// } -func URLFormat(next http.Handler) http.Handler { - fn := func(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - - var format string - path := r.URL.Path - - rctx := chi.RouteContext(r.Context()) - if rctx != nil && rctx.RoutePath != "" { - path = rctx.RoutePath - } - - if strings.Index(path, ".") > 0 { - base := strings.LastIndex(path, "/") - idx := strings.LastIndex(path[base:], ".") - - if idx > 0 { - idx += base - format = path[idx+1:] - - rctx.RoutePath = path[:idx] - } - } - - r = r.WithContext(context.WithValue(ctx, URLFormatCtxKey, format)) - - next.ServeHTTP(w, r) - } - return http.HandlerFunc(fn) -} diff --git a/backend/vendor/github.com/go-chi/chi/v5/middleware/value.go b/backend/vendor/github.com/go-chi/chi/v5/middleware/value.go deleted file mode 100644 index a9dfd434..00000000 --- a/backend/vendor/github.com/go-chi/chi/v5/middleware/value.go +++ /dev/null @@ -1,17 +0,0 @@ -package middleware - -import ( - "context" - "net/http" -) - -// WithValue is a middleware that sets a given key/value in a context chain. -func WithValue(key, val interface{}) func(next http.Handler) http.Handler { - return func(next http.Handler) http.Handler { - fn := func(w http.ResponseWriter, r *http.Request) { - r = r.WithContext(context.WithValue(r.Context(), key, val)) - next.ServeHTTP(w, r) - } - return http.HandlerFunc(fn) - } -} diff --git a/backend/vendor/github.com/go-chi/chi/v5/middleware/wrap_writer.go b/backend/vendor/github.com/go-chi/chi/v5/middleware/wrap_writer.go deleted file mode 100644 index 12d4faf0..00000000 --- a/backend/vendor/github.com/go-chi/chi/v5/middleware/wrap_writer.go +++ /dev/null @@ -1,242 +0,0 @@ -package middleware - -// The original work was derived from Goji's middleware, source: -// https://github.com/zenazn/goji/tree/master/web/middleware - -import ( - "bufio" - "io" - "io/ioutil" - "net" - "net/http" -) - -// NewWrapResponseWriter wraps an http.ResponseWriter, returning a proxy that allows you to -// hook into various parts of the response process. -func NewWrapResponseWriter(w http.ResponseWriter, protoMajor int) WrapResponseWriter { - _, fl := w.(http.Flusher) - - bw := basicWriter{ResponseWriter: w} - - if protoMajor == 2 { - _, ps := w.(http.Pusher) - if fl && ps { - return &http2FancyWriter{bw} - } - } else { - _, hj := w.(http.Hijacker) - _, rf := w.(io.ReaderFrom) - if fl && hj && rf { - return &httpFancyWriter{bw} - } - if fl && hj { - return &flushHijackWriter{bw} - } - if hj { - return &hijackWriter{bw} - } - } - - if fl { - return &flushWriter{bw} - } - - return &bw -} - -// WrapResponseWriter is a proxy around an http.ResponseWriter that allows you to hook -// into various parts of the response process. -type WrapResponseWriter interface { - http.ResponseWriter - // Status returns the HTTP status of the request, or 0 if one has not - // yet been sent. - Status() int - // BytesWritten returns the total number of bytes sent to the client. - BytesWritten() int - // Tee causes the response body to be written to the given io.Writer in - // addition to proxying the writes through. Only one io.Writer can be - // tee'd to at once: setting a second one will overwrite the first. - // Writes will be sent to the proxy before being written to this - // io.Writer. It is illegal for the tee'd writer to be modified - // concurrently with writes. - Tee(io.Writer) - // Unwrap returns the original proxied target. - Unwrap() http.ResponseWriter - // Discard causes all writes to the original ResponseWriter be discarded, - // instead writing only to the tee'd writer if it's set. - // The caller is responsible for calling WriteHeader and Write on the - // original ResponseWriter once the processing is done. - Discard() -} - -// basicWriter wraps a http.ResponseWriter that implements the minimal -// http.ResponseWriter interface. -type basicWriter struct { - http.ResponseWriter - wroteHeader bool - code int - bytes int - tee io.Writer - discard bool -} - -func (b *basicWriter) WriteHeader(code int) { - if code >= 100 && code <= 199 && code != http.StatusSwitchingProtocols { - if !b.discard { - b.ResponseWriter.WriteHeader(code) - } - } else if !b.wroteHeader { - b.code = code - b.wroteHeader = true - if !b.discard { - b.ResponseWriter.WriteHeader(code) - } - } -} - -func (b *basicWriter) Write(buf []byte) (n int, err error) { - b.maybeWriteHeader() - if !b.discard { - n, err = b.ResponseWriter.Write(buf) - if b.tee != nil { - _, err2 := b.tee.Write(buf[:n]) - // Prefer errors generated by the proxied writer. - if err == nil { - err = err2 - } - } - } else if b.tee != nil { - n, err = b.tee.Write(buf) - } else { - n, err = ioutil.Discard.Write(buf) - } - b.bytes += n - return n, err -} - -func (b *basicWriter) maybeWriteHeader() { - if !b.wroteHeader { - b.WriteHeader(http.StatusOK) - } -} - -func (b *basicWriter) Status() int { - return b.code -} - -func (b *basicWriter) BytesWritten() int { - return b.bytes -} - -func (b *basicWriter) Tee(w io.Writer) { - b.tee = w -} - -func (b *basicWriter) Unwrap() http.ResponseWriter { - return b.ResponseWriter -} - -func (b *basicWriter) Discard() { - b.discard = true -} - -// flushWriter ... -type flushWriter struct { - basicWriter -} - -func (f *flushWriter) Flush() { - f.wroteHeader = true - fl := f.basicWriter.ResponseWriter.(http.Flusher) - fl.Flush() -} - -var _ http.Flusher = &flushWriter{} - -// hijackWriter ... -type hijackWriter struct { - basicWriter -} - -func (f *hijackWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { - hj := f.basicWriter.ResponseWriter.(http.Hijacker) - return hj.Hijack() -} - -var _ http.Hijacker = &hijackWriter{} - -// flushHijackWriter ... -type flushHijackWriter struct { - basicWriter -} - -func (f *flushHijackWriter) Flush() { - f.wroteHeader = true - fl := f.basicWriter.ResponseWriter.(http.Flusher) - fl.Flush() -} - -func (f *flushHijackWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { - hj := f.basicWriter.ResponseWriter.(http.Hijacker) - return hj.Hijack() -} - -var _ http.Flusher = &flushHijackWriter{} -var _ http.Hijacker = &flushHijackWriter{} - -// httpFancyWriter is a HTTP writer that additionally satisfies -// http.Flusher, http.Hijacker, and io.ReaderFrom. It exists for the common case -// of wrapping the http.ResponseWriter that package http gives you, in order to -// make the proxied object support the full method set of the proxied object. -type httpFancyWriter struct { - basicWriter -} - -func (f *httpFancyWriter) Flush() { - f.wroteHeader = true - fl := f.basicWriter.ResponseWriter.(http.Flusher) - fl.Flush() -} - -func (f *httpFancyWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { - hj := f.basicWriter.ResponseWriter.(http.Hijacker) - return hj.Hijack() -} - -func (f *http2FancyWriter) Push(target string, opts *http.PushOptions) error { - return f.basicWriter.ResponseWriter.(http.Pusher).Push(target, opts) -} - -func (f *httpFancyWriter) ReadFrom(r io.Reader) (int64, error) { - if f.basicWriter.tee != nil { - n, err := io.Copy(&f.basicWriter, r) - f.basicWriter.bytes += int(n) - return n, err - } - rf := f.basicWriter.ResponseWriter.(io.ReaderFrom) - f.basicWriter.maybeWriteHeader() - n, err := rf.ReadFrom(r) - f.basicWriter.bytes += int(n) - return n, err -} - -var _ http.Flusher = &httpFancyWriter{} -var _ http.Hijacker = &httpFancyWriter{} -var _ http.Pusher = &http2FancyWriter{} -var _ io.ReaderFrom = &httpFancyWriter{} - -// http2FancyWriter is a HTTP2 writer that additionally satisfies -// http.Flusher, and io.ReaderFrom. It exists for the common case -// of wrapping the http.ResponseWriter that package http gives you, in order to -// make the proxied object support the full method set of the proxied object. -type http2FancyWriter struct { - basicWriter -} - -func (f *http2FancyWriter) Flush() { - f.wroteHeader = true - fl := f.basicWriter.ResponseWriter.(http.Flusher) - fl.Flush() -} - -var _ http.Flusher = &http2FancyWriter{} diff --git a/backend/vendor/github.com/go-chi/chi/v5/mux.go b/backend/vendor/github.com/go-chi/chi/v5/mux.go deleted file mode 100644 index 91daf691..00000000 --- a/backend/vendor/github.com/go-chi/chi/v5/mux.go +++ /dev/null @@ -1,529 +0,0 @@ -package chi - -import ( - "context" - "fmt" - "net/http" - "strings" - "sync" -) - -var _ Router = &Mux{} - -// Mux is a simple HTTP route multiplexer that parses a request path, -// records any URL params, and executes an end handler. It implements -// the http.Handler interface and is friendly with the standard library. -// -// Mux is designed to be fast, minimal and offer a powerful API for building -// modular and composable HTTP services with a large set of handlers. It's -// particularly useful for writing large REST API services that break a handler -// into many smaller parts composed of middlewares and end handlers. -type Mux struct { - // The computed mux handler made of the chained middleware stack and - // the tree router - handler http.Handler - - // The radix trie router - tree *node - - // Custom method not allowed handler - methodNotAllowedHandler http.HandlerFunc - - // A reference to the parent mux used by subrouters when mounting - // to a parent mux - parent *Mux - - // Routing context pool - pool *sync.Pool - - // Custom route not found handler - notFoundHandler http.HandlerFunc - - // The middleware stack - middlewares []func(http.Handler) http.Handler - - // Controls the behaviour of middleware chain generation when a mux - // is registered as an inline group inside another mux. - inline bool -} - -// NewMux returns a newly initialized Mux object that implements the Router -// interface. -func NewMux() *Mux { - mux := &Mux{tree: &node{}, pool: &sync.Pool{}} - mux.pool.New = func() interface{} { - return NewRouteContext() - } - return mux -} - -// ServeHTTP is the single method of the http.Handler interface that makes -// Mux interoperable with the standard library. It uses a sync.Pool to get and -// reuse routing contexts for each request. -func (mx *Mux) ServeHTTP(w http.ResponseWriter, r *http.Request) { - // Ensure the mux has some routes defined on the mux - if mx.handler == nil { - mx.NotFoundHandler().ServeHTTP(w, r) - return - } - - // Check if a routing context already exists from a parent router. - rctx, _ := r.Context().Value(RouteCtxKey).(*Context) - if rctx != nil { - mx.handler.ServeHTTP(w, r) - return - } - - // Fetch a RouteContext object from the sync pool, and call the computed - // mx.handler that is comprised of mx.middlewares + mx.routeHTTP. - // Once the request is finished, reset the routing context and put it back - // into the pool for reuse from another request. - rctx = mx.pool.Get().(*Context) - rctx.Reset() - rctx.Routes = mx - rctx.parentCtx = r.Context() - - // NOTE: r.WithContext() causes 2 allocations and context.WithValue() causes 1 allocation - r = r.WithContext(context.WithValue(r.Context(), RouteCtxKey, rctx)) - - // Serve the request and once its done, put the request context back in the sync pool - mx.handler.ServeHTTP(w, r) - mx.pool.Put(rctx) -} - -// Use appends a middleware handler to the Mux middleware stack. -// -// The middleware stack for any Mux will execute before searching for a matching -// route to a specific handler, which provides opportunity to respond early, -// change the course of the request execution, or set request-scoped values for -// the next http.Handler. -func (mx *Mux) Use(middlewares ...func(http.Handler) http.Handler) { - if mx.handler != nil { - panic("chi: all middlewares must be defined before routes on a mux") - } - mx.middlewares = append(mx.middlewares, middlewares...) -} - -// Handle adds the route `pattern` that matches any http method to -// execute the `handler` http.Handler. -func (mx *Mux) Handle(pattern string, handler http.Handler) { - parts := strings.SplitN(pattern, " ", 2) - if len(parts) == 2 { - mx.Method(parts[0], parts[1], handler) - return - } - - mx.handle(mALL, pattern, handler) -} - -// HandleFunc adds the route `pattern` that matches any http method to -// execute the `handlerFn` http.HandlerFunc. -func (mx *Mux) HandleFunc(pattern string, handlerFn http.HandlerFunc) { - parts := strings.SplitN(pattern, " ", 2) - if len(parts) == 2 { - mx.Method(parts[0], parts[1], handlerFn) - return - } - - mx.handle(mALL, pattern, handlerFn) -} - -// Method adds the route `pattern` that matches `method` http method to -// execute the `handler` http.Handler. -func (mx *Mux) Method(method, pattern string, handler http.Handler) { - m, ok := methodMap[strings.ToUpper(method)] - if !ok { - panic(fmt.Sprintf("chi: '%s' http method is not supported.", method)) - } - mx.handle(m, pattern, handler) -} - -// MethodFunc adds the route `pattern` that matches `method` http method to -// execute the `handlerFn` http.HandlerFunc. -func (mx *Mux) MethodFunc(method, pattern string, handlerFn http.HandlerFunc) { - mx.Method(method, pattern, handlerFn) -} - -// Connect adds the route `pattern` that matches a CONNECT http method to -// execute the `handlerFn` http.HandlerFunc. -func (mx *Mux) Connect(pattern string, handlerFn http.HandlerFunc) { - mx.handle(mCONNECT, pattern, handlerFn) -} - -// Delete adds the route `pattern` that matches a DELETE http method to -// execute the `handlerFn` http.HandlerFunc. -func (mx *Mux) Delete(pattern string, handlerFn http.HandlerFunc) { - mx.handle(mDELETE, pattern, handlerFn) -} - -// Get adds the route `pattern` that matches a GET http method to -// execute the `handlerFn` http.HandlerFunc. -func (mx *Mux) Get(pattern string, handlerFn http.HandlerFunc) { - mx.handle(mGET, pattern, handlerFn) -} - -// Head adds the route `pattern` that matches a HEAD http method to -// execute the `handlerFn` http.HandlerFunc. -func (mx *Mux) Head(pattern string, handlerFn http.HandlerFunc) { - mx.handle(mHEAD, pattern, handlerFn) -} - -// Options adds the route `pattern` that matches an OPTIONS http method to -// execute the `handlerFn` http.HandlerFunc. -func (mx *Mux) Options(pattern string, handlerFn http.HandlerFunc) { - mx.handle(mOPTIONS, pattern, handlerFn) -} - -// Patch adds the route `pattern` that matches a PATCH http method to -// execute the `handlerFn` http.HandlerFunc. -func (mx *Mux) Patch(pattern string, handlerFn http.HandlerFunc) { - mx.handle(mPATCH, pattern, handlerFn) -} - -// Post adds the route `pattern` that matches a POST http method to -// execute the `handlerFn` http.HandlerFunc. -func (mx *Mux) Post(pattern string, handlerFn http.HandlerFunc) { - mx.handle(mPOST, pattern, handlerFn) -} - -// Put adds the route `pattern` that matches a PUT http method to -// execute the `handlerFn` http.HandlerFunc. -func (mx *Mux) Put(pattern string, handlerFn http.HandlerFunc) { - mx.handle(mPUT, pattern, handlerFn) -} - -// Trace adds the route `pattern` that matches a TRACE http method to -// execute the `handlerFn` http.HandlerFunc. -func (mx *Mux) Trace(pattern string, handlerFn http.HandlerFunc) { - mx.handle(mTRACE, pattern, handlerFn) -} - -// NotFound sets a custom http.HandlerFunc for routing paths that could -// not be found. The default 404 handler is `http.NotFound`. -func (mx *Mux) NotFound(handlerFn http.HandlerFunc) { - // Build NotFound handler chain - m := mx - hFn := handlerFn - if mx.inline && mx.parent != nil { - m = mx.parent - hFn = Chain(mx.middlewares...).HandlerFunc(hFn).ServeHTTP - } - - // Update the notFoundHandler from this point forward - m.notFoundHandler = hFn - m.updateSubRoutes(func(subMux *Mux) { - if subMux.notFoundHandler == nil { - subMux.NotFound(hFn) - } - }) -} - -// MethodNotAllowed sets a custom http.HandlerFunc for routing paths where the -// method is unresolved. The default handler returns a 405 with an empty body. -func (mx *Mux) MethodNotAllowed(handlerFn http.HandlerFunc) { - // Build MethodNotAllowed handler chain - m := mx - hFn := handlerFn - if mx.inline && mx.parent != nil { - m = mx.parent - hFn = Chain(mx.middlewares...).HandlerFunc(hFn).ServeHTTP - } - - // Update the methodNotAllowedHandler from this point forward - m.methodNotAllowedHandler = hFn - m.updateSubRoutes(func(subMux *Mux) { - if subMux.methodNotAllowedHandler == nil { - subMux.MethodNotAllowed(hFn) - } - }) -} - -// With adds inline middlewares for an endpoint handler. -func (mx *Mux) With(middlewares ...func(http.Handler) http.Handler) Router { - // Similarly as in handle(), we must build the mux handler once additional - // middleware registration isn't allowed for this stack, like now. - if !mx.inline && mx.handler == nil { - mx.updateRouteHandler() - } - - // Copy middlewares from parent inline muxs - var mws Middlewares - if mx.inline { - mws = make(Middlewares, len(mx.middlewares)) - copy(mws, mx.middlewares) - } - mws = append(mws, middlewares...) - - im := &Mux{ - pool: mx.pool, inline: true, parent: mx, tree: mx.tree, middlewares: mws, - notFoundHandler: mx.notFoundHandler, methodNotAllowedHandler: mx.methodNotAllowedHandler, - } - - return im -} - -// Group creates a new inline-Mux with a copy of middleware stack. It's useful -// for a group of handlers along the same routing path that use an additional -// set of middlewares. See _examples/. -func (mx *Mux) Group(fn func(r Router)) Router { - im := mx.With() - if fn != nil { - fn(im) - } - return im -} - -// Route creates a new Mux and mounts it along the `pattern` as a subrouter. -// Effectively, this is a short-hand call to Mount. See _examples/. -func (mx *Mux) Route(pattern string, fn func(r Router)) Router { - if fn == nil { - panic(fmt.Sprintf("chi: attempting to Route() a nil subrouter on '%s'", pattern)) - } - subRouter := NewRouter() - fn(subRouter) - mx.Mount(pattern, subRouter) - return subRouter -} - -// Mount attaches another http.Handler or chi Router as a subrouter along a routing -// path. It's very useful to split up a large API as many independent routers and -// compose them as a single service using Mount. See _examples/. -// -// Note that Mount() simply sets a wildcard along the `pattern` that will continue -// routing at the `handler`, which in most cases is another chi.Router. As a result, -// if you define two Mount() routes on the exact same pattern the mount will panic. -func (mx *Mux) Mount(pattern string, handler http.Handler) { - if handler == nil { - panic(fmt.Sprintf("chi: attempting to Mount() a nil handler on '%s'", pattern)) - } - - // Provide runtime safety for ensuring a pattern isn't mounted on an existing - // routing pattern. - if mx.tree.findPattern(pattern+"*") || mx.tree.findPattern(pattern+"/*") { - panic(fmt.Sprintf("chi: attempting to Mount() a handler on an existing path, '%s'", pattern)) - } - - // Assign sub-Router's with the parent not found & method not allowed handler if not specified. - subr, ok := handler.(*Mux) - if ok && subr.notFoundHandler == nil && mx.notFoundHandler != nil { - subr.NotFound(mx.notFoundHandler) - } - if ok && subr.methodNotAllowedHandler == nil && mx.methodNotAllowedHandler != nil { - subr.MethodNotAllowed(mx.methodNotAllowedHandler) - } - - mountHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - rctx := RouteContext(r.Context()) - - // shift the url path past the previous subrouter - rctx.RoutePath = mx.nextRoutePath(rctx) - - // reset the wildcard URLParam which connects the subrouter - n := len(rctx.URLParams.Keys) - 1 - if n >= 0 && rctx.URLParams.Keys[n] == "*" && len(rctx.URLParams.Values) > n { - rctx.URLParams.Values[n] = "" - } - - handler.ServeHTTP(w, r) - }) - - if pattern == "" || pattern[len(pattern)-1] != '/' { - mx.handle(mALL|mSTUB, pattern, mountHandler) - mx.handle(mALL|mSTUB, pattern+"/", mountHandler) - pattern += "/" - } - - method := mALL - subroutes, _ := handler.(Routes) - if subroutes != nil { - method |= mSTUB - } - n := mx.handle(method, pattern+"*", mountHandler) - - if subroutes != nil { - n.subroutes = subroutes - } -} - -// Routes returns a slice of routing information from the tree, -// useful for traversing available routes of a router. -func (mx *Mux) Routes() []Route { - return mx.tree.routes() -} - -// Middlewares returns a slice of middleware handler functions. -func (mx *Mux) Middlewares() Middlewares { - return mx.middlewares -} - -// Match searches the routing tree for a handler that matches the method/path. -// It's similar to routing a http request, but without executing the handler -// thereafter. -// -// Note: the *Context state is updated during execution, so manage -// the state carefully or make a NewRouteContext(). -func (mx *Mux) Match(rctx *Context, method, path string) bool { - return mx.Find(rctx, method, path) != "" -} - -// Find searches the routing tree for the pattern that matches -// the method/path. -// -// Note: the *Context state is updated during execution, so manage -// the state carefully or make a NewRouteContext(). -func (mx *Mux) Find(rctx *Context, method, path string) string { - m, ok := methodMap[method] - if !ok { - return "" - } - - node, _, _ := mx.tree.FindRoute(rctx, m, path) - pattern := rctx.routePattern - - if node != nil { - if node.subroutes == nil { - e := node.endpoints[m] - return e.pattern - } - - rctx.RoutePath = mx.nextRoutePath(rctx) - subPattern := node.subroutes.Find(rctx, method, rctx.RoutePath) - if subPattern == "" { - return "" - } - - pattern = strings.TrimSuffix(pattern, "/*") - pattern += subPattern - } - - return pattern -} - -// NotFoundHandler returns the default Mux 404 responder whenever a route -// cannot be found. -func (mx *Mux) NotFoundHandler() http.HandlerFunc { - if mx.notFoundHandler != nil { - return mx.notFoundHandler - } - return http.NotFound -} - -// MethodNotAllowedHandler returns the default Mux 405 responder whenever -// a method cannot be resolved for a route. -func (mx *Mux) MethodNotAllowedHandler(methodsAllowed ...methodTyp) http.HandlerFunc { - if mx.methodNotAllowedHandler != nil { - return mx.methodNotAllowedHandler - } - return methodNotAllowedHandler(methodsAllowed...) -} - -// handle registers a http.Handler in the routing tree for a particular http method -// and routing pattern. -func (mx *Mux) handle(method methodTyp, pattern string, handler http.Handler) *node { - if len(pattern) == 0 || pattern[0] != '/' { - panic(fmt.Sprintf("chi: routing pattern must begin with '/' in '%s'", pattern)) - } - - // Build the computed routing handler for this routing pattern. - if !mx.inline && mx.handler == nil { - mx.updateRouteHandler() - } - - // Build endpoint handler with inline middlewares for the route - var h http.Handler - if mx.inline { - mx.handler = http.HandlerFunc(mx.routeHTTP) - h = Chain(mx.middlewares...).Handler(handler) - } else { - h = handler - } - - // Add the endpoint to the tree and return the node - return mx.tree.InsertRoute(method, pattern, h) -} - -// routeHTTP routes a http.Request through the Mux routing tree to serve -// the matching handler for a particular http method. -func (mx *Mux) routeHTTP(w http.ResponseWriter, r *http.Request) { - // Grab the route context object - rctx := r.Context().Value(RouteCtxKey).(*Context) - - // The request routing path - routePath := rctx.RoutePath - if routePath == "" { - if r.URL.RawPath != "" { - routePath = r.URL.RawPath - } else { - routePath = r.URL.Path - } - if routePath == "" { - routePath = "/" - } - } - - // Check if method is supported by chi - if rctx.RouteMethod == "" { - rctx.RouteMethod = r.Method - } - method, ok := methodMap[rctx.RouteMethod] - if !ok { - mx.MethodNotAllowedHandler().ServeHTTP(w, r) - return - } - - // Find the route - if _, _, h := mx.tree.FindRoute(rctx, method, routePath); h != nil { - if supportsPathValue { - setPathValue(rctx, r) - } - - h.ServeHTTP(w, r) - return - } - if rctx.methodNotAllowed { - mx.MethodNotAllowedHandler(rctx.methodsAllowed...).ServeHTTP(w, r) - } else { - mx.NotFoundHandler().ServeHTTP(w, r) - } -} - -func (mx *Mux) nextRoutePath(rctx *Context) string { - routePath := "/" - nx := len(rctx.routeParams.Keys) - 1 // index of last param in list - if nx >= 0 && rctx.routeParams.Keys[nx] == "*" && len(rctx.routeParams.Values) > nx { - routePath = "/" + rctx.routeParams.Values[nx] - } - return routePath -} - -// Recursively update data on child routers. -func (mx *Mux) updateSubRoutes(fn func(subMux *Mux)) { - for _, r := range mx.tree.routes() { - subMux, ok := r.SubRoutes.(*Mux) - if !ok { - continue - } - fn(subMux) - } -} - -// updateRouteHandler builds the single mux handler that is a chain of the middleware -// stack, as defined by calls to Use(), and the tree router (Mux) itself. After this -// point, no other middlewares can be registered on this Mux's stack. But you can still -// compose additional middlewares via Group()'s or using a chained middleware handler. -func (mx *Mux) updateRouteHandler() { - mx.handler = chain(mx.middlewares, http.HandlerFunc(mx.routeHTTP)) -} - -// methodNotAllowedHandler is a helper function to respond with a 405, -// method not allowed. It sets the Allow header with the list of allowed -// methods for the route. -func methodNotAllowedHandler(methodsAllowed ...methodTyp) func(w http.ResponseWriter, r *http.Request) { - return func(w http.ResponseWriter, r *http.Request) { - for _, m := range methodsAllowed { - w.Header().Add("Allow", reverseMethodMap[m]) - } - w.WriteHeader(405) - w.Write(nil) - } -} diff --git a/backend/vendor/github.com/go-chi/chi/v5/path_value.go b/backend/vendor/github.com/go-chi/chi/v5/path_value.go deleted file mode 100644 index 7e78171e..00000000 --- a/backend/vendor/github.com/go-chi/chi/v5/path_value.go +++ /dev/null @@ -1,20 +0,0 @@ -//go:build go1.22 -// +build go1.22 - -package chi - -import "net/http" - -// supportsPathValue is true if the Go version is 1.22 and above. -// -// If this is true, `net/http.Request` has methods `SetPathValue` and `PathValue`. -const supportsPathValue = true - -// setPathValue sets the path values in the Request value -// based on the provided request context. -func setPathValue(rctx *Context, r *http.Request) { - for i, key := range rctx.URLParams.Keys { - value := rctx.URLParams.Values[i] - r.SetPathValue(key, value) - } -} diff --git a/backend/vendor/github.com/go-chi/chi/v5/path_value_fallback.go b/backend/vendor/github.com/go-chi/chi/v5/path_value_fallback.go deleted file mode 100644 index f551781a..00000000 --- a/backend/vendor/github.com/go-chi/chi/v5/path_value_fallback.go +++ /dev/null @@ -1,19 +0,0 @@ -//go:build !go1.22 -// +build !go1.22 - -package chi - -import "net/http" - -// supportsPathValue is true if the Go version is 1.22 and above. -// -// If this is true, `net/http.Request` has methods `SetPathValue` and `PathValue`. -const supportsPathValue = false - -// setPathValue sets the path values in the Request value -// based on the provided request context. -// -// setPathValue is only supported in Go 1.22 and above so -// this is just a blank function so that it compiles. -func setPathValue(rctx *Context, r *http.Request) { -} diff --git a/backend/vendor/github.com/go-chi/chi/v5/tree.go b/backend/vendor/github.com/go-chi/chi/v5/tree.go deleted file mode 100644 index c7d3bc57..00000000 --- a/backend/vendor/github.com/go-chi/chi/v5/tree.go +++ /dev/null @@ -1,892 +0,0 @@ -package chi - -// Radix tree implementation below is a based on the original work by -// Armon Dadgar in https://github.com/armon/go-radix/blob/master/radix.go -// (MIT licensed). It's been heavily modified for use as a HTTP routing tree. - -import ( - "fmt" - "net/http" - "regexp" - "sort" - "strconv" - "strings" -) - -type methodTyp uint - -const ( - mSTUB methodTyp = 1 << iota - mCONNECT - mDELETE - mGET - mHEAD - mOPTIONS - mPATCH - mPOST - mPUT - mTRACE -) - -var mALL = mCONNECT | mDELETE | mGET | mHEAD | - mOPTIONS | mPATCH | mPOST | mPUT | mTRACE - -var methodMap = map[string]methodTyp{ - http.MethodConnect: mCONNECT, - http.MethodDelete: mDELETE, - http.MethodGet: mGET, - http.MethodHead: mHEAD, - http.MethodOptions: mOPTIONS, - http.MethodPatch: mPATCH, - http.MethodPost: mPOST, - http.MethodPut: mPUT, - http.MethodTrace: mTRACE, -} - -var reverseMethodMap = map[methodTyp]string{ - mCONNECT: http.MethodConnect, - mDELETE: http.MethodDelete, - mGET: http.MethodGet, - mHEAD: http.MethodHead, - mOPTIONS: http.MethodOptions, - mPATCH: http.MethodPatch, - mPOST: http.MethodPost, - mPUT: http.MethodPut, - mTRACE: http.MethodTrace, -} - -// RegisterMethod adds support for custom HTTP method handlers, available -// via Router#Method and Router#MethodFunc -func RegisterMethod(method string) { - if method == "" { - return - } - method = strings.ToUpper(method) - if _, ok := methodMap[method]; ok { - return - } - n := len(methodMap) - if n > strconv.IntSize-2 { - panic(fmt.Sprintf("chi: max number of methods reached (%d)", strconv.IntSize)) - } - mt := methodTyp(2 << n) - methodMap[method] = mt - mALL |= mt -} - -type nodeTyp uint8 - -const ( - ntStatic nodeTyp = iota // /home - ntRegexp // /{id:[0-9]+} - ntParam // /{user} - ntCatchAll // /api/v1/* -) - -type node struct { - // subroutes on the leaf node - subroutes Routes - - // regexp matcher for regexp nodes - rex *regexp.Regexp - - // HTTP handler endpoints on the leaf node - endpoints endpoints - - // prefix is the common prefix we ignore - prefix string - - // child nodes should be stored in-order for iteration, - // in groups of the node type. - children [ntCatchAll + 1]nodes - - // first byte of the child prefix - tail byte - - // node type: static, regexp, param, catchAll - typ nodeTyp - - // first byte of the prefix - label byte -} - -// endpoints is a mapping of http method constants to handlers -// for a given route. -type endpoints map[methodTyp]*endpoint - -type endpoint struct { - // endpoint handler - handler http.Handler - - // pattern is the routing pattern for handler nodes - pattern string - - // parameter keys recorded on handler nodes - paramKeys []string -} - -func (s endpoints) Value(method methodTyp) *endpoint { - mh, ok := s[method] - if !ok { - mh = &endpoint{} - s[method] = mh - } - return mh -} - -func (n *node) InsertRoute(method methodTyp, pattern string, handler http.Handler) *node { - var parent *node - search := pattern - - for { - // Handle key exhaustion - if len(search) == 0 { - // Insert or update the node's leaf handler - n.setEndpoint(method, handler, pattern) - return n - } - - // We're going to be searching for a wild node next, - // in this case, we need to get the tail - var label = search[0] - var segTail byte - var segEndIdx int - var segTyp nodeTyp - var segRexpat string - if label == '{' || label == '*' { - segTyp, _, segRexpat, segTail, _, segEndIdx = patNextSegment(search) - } - - var prefix string - if segTyp == ntRegexp { - prefix = segRexpat - } - - // Look for the edge to attach to - parent = n - n = n.getEdge(segTyp, label, segTail, prefix) - - // No edge, create one - if n == nil { - child := &node{label: label, tail: segTail, prefix: search} - hn := parent.addChild(child, search) - hn.setEndpoint(method, handler, pattern) - - return hn - } - - // Found an edge to match the pattern - - if n.typ > ntStatic { - // We found a param node, trim the param from the search path and continue. - // This param/wild pattern segment would already be on the tree from a previous - // call to addChild when creating a new node. - search = search[segEndIdx:] - continue - } - - // Static nodes fall below here. - // Determine longest prefix of the search key on match. - commonPrefix := longestPrefix(search, n.prefix) - if commonPrefix == len(n.prefix) { - // the common prefix is as long as the current node's prefix we're attempting to insert. - // keep the search going. - search = search[commonPrefix:] - continue - } - - // Split the node - child := &node{ - typ: ntStatic, - prefix: search[:commonPrefix], - } - parent.replaceChild(search[0], segTail, child) - - // Restore the existing node - n.label = n.prefix[commonPrefix] - n.prefix = n.prefix[commonPrefix:] - child.addChild(n, n.prefix) - - // If the new key is a subset, set the method/handler on this node and finish. - search = search[commonPrefix:] - if len(search) == 0 { - child.setEndpoint(method, handler, pattern) - return child - } - - // Create a new edge for the node - subchild := &node{ - typ: ntStatic, - label: search[0], - prefix: search, - } - hn := child.addChild(subchild, search) - hn.setEndpoint(method, handler, pattern) - return hn - } -} - -// addChild appends the new `child` node to the tree using the `pattern` as the trie key. -// For a URL router like chi's, we split the static, param, regexp and wildcard segments -// into different nodes. In addition, addChild will recursively call itself until every -// pattern segment is added to the url pattern tree as individual nodes, depending on type. -func (n *node) addChild(child *node, prefix string) *node { - search := prefix - - // handler leaf node added to the tree is the child. - // this may be overridden later down the flow - hn := child - - // Parse next segment - segTyp, _, segRexpat, segTail, segStartIdx, segEndIdx := patNextSegment(search) - - // Add child depending on next up segment - switch segTyp { - - case ntStatic: - // Search prefix is all static (that is, has no params in path) - // noop - - default: - // Search prefix contains a param, regexp or wildcard - - if segTyp == ntRegexp { - rex, err := regexp.Compile(segRexpat) - if err != nil { - panic(fmt.Sprintf("chi: invalid regexp pattern '%s' in route param", segRexpat)) - } - child.prefix = segRexpat - child.rex = rex - } - - if segStartIdx == 0 { - // Route starts with a param - child.typ = segTyp - - if segTyp == ntCatchAll { - segStartIdx = -1 - } else { - segStartIdx = segEndIdx - } - if segStartIdx < 0 { - segStartIdx = len(search) - } - child.tail = segTail // for params, we set the tail - - if segStartIdx != len(search) { - // add static edge for the remaining part, split the end. - // its not possible to have adjacent param nodes, so its certainly - // going to be a static node next. - - search = search[segStartIdx:] // advance search position - - nn := &node{ - typ: ntStatic, - label: search[0], - prefix: search, - } - hn = child.addChild(nn, search) - } - - } else if segStartIdx > 0 { - // Route has some param - - // starts with a static segment - child.typ = ntStatic - child.prefix = search[:segStartIdx] - child.rex = nil - - // add the param edge node - search = search[segStartIdx:] - - nn := &node{ - typ: segTyp, - label: search[0], - tail: segTail, - } - hn = child.addChild(nn, search) - - } - } - - n.children[child.typ] = append(n.children[child.typ], child) - n.children[child.typ].Sort() - return hn -} - -func (n *node) replaceChild(label, tail byte, child *node) { - for i := 0; i < len(n.children[child.typ]); i++ { - if n.children[child.typ][i].label == label && n.children[child.typ][i].tail == tail { - n.children[child.typ][i] = child - n.children[child.typ][i].label = label - n.children[child.typ][i].tail = tail - return - } - } - panic("chi: replacing missing child") -} - -func (n *node) getEdge(ntyp nodeTyp, label, tail byte, prefix string) *node { - nds := n.children[ntyp] - for i := 0; i < len(nds); i++ { - if nds[i].label == label && nds[i].tail == tail { - if ntyp == ntRegexp && nds[i].prefix != prefix { - continue - } - return nds[i] - } - } - return nil -} - -func (n *node) setEndpoint(method methodTyp, handler http.Handler, pattern string) { - // Set the handler for the method type on the node - if n.endpoints == nil { - n.endpoints = make(endpoints) - } - - paramKeys := patParamKeys(pattern) - - if method&mSTUB == mSTUB { - n.endpoints.Value(mSTUB).handler = handler - } - if method&mALL == mALL { - h := n.endpoints.Value(mALL) - h.handler = handler - h.pattern = pattern - h.paramKeys = paramKeys - for _, m := range methodMap { - h := n.endpoints.Value(m) - h.handler = handler - h.pattern = pattern - h.paramKeys = paramKeys - } - } else { - h := n.endpoints.Value(method) - h.handler = handler - h.pattern = pattern - h.paramKeys = paramKeys - } -} - -func (n *node) FindRoute(rctx *Context, method methodTyp, path string) (*node, endpoints, http.Handler) { - // Reset the context routing pattern and params - rctx.routePattern = "" - rctx.routeParams.Keys = rctx.routeParams.Keys[:0] - rctx.routeParams.Values = rctx.routeParams.Values[:0] - - // Find the routing handlers for the path - rn := n.findRoute(rctx, method, path) - if rn == nil { - return nil, nil, nil - } - - // Record the routing params in the request lifecycle - rctx.URLParams.Keys = append(rctx.URLParams.Keys, rctx.routeParams.Keys...) - rctx.URLParams.Values = append(rctx.URLParams.Values, rctx.routeParams.Values...) - - // Record the routing pattern in the request lifecycle - if rn.endpoints[method].pattern != "" { - rctx.routePattern = rn.endpoints[method].pattern - rctx.RoutePatterns = append(rctx.RoutePatterns, rctx.routePattern) - } - - return rn, rn.endpoints, rn.endpoints[method].handler -} - -// Recursive edge traversal by checking all nodeTyp groups along the way. -// It's like searching through a multi-dimensional radix trie. -func (n *node) findRoute(rctx *Context, method methodTyp, path string) *node { - nn := n - search := path - - for t, nds := range nn.children { - ntyp := nodeTyp(t) - if len(nds) == 0 { - continue - } - - var xn *node - xsearch := search - - var label byte - if search != "" { - label = search[0] - } - - switch ntyp { - case ntStatic: - xn = nds.findEdge(label) - if xn == nil || !strings.HasPrefix(xsearch, xn.prefix) { - continue - } - xsearch = xsearch[len(xn.prefix):] - - case ntParam, ntRegexp: - // short-circuit and return no matching route for empty param values - if xsearch == "" { - continue - } - - // serially loop through each node grouped by the tail delimiter - for idx := 0; idx < len(nds); idx++ { - xn = nds[idx] - - // label for param nodes is the delimiter byte - p := strings.IndexByte(xsearch, xn.tail) - - if p < 0 { - if xn.tail == '/' { - p = len(xsearch) - } else { - continue - } - } else if ntyp == ntRegexp && p == 0 { - continue - } - - if ntyp == ntRegexp && xn.rex != nil { - if !xn.rex.MatchString(xsearch[:p]) { - continue - } - } else if strings.IndexByte(xsearch[:p], '/') != -1 { - // avoid a match across path segments - continue - } - - prevlen := len(rctx.routeParams.Values) - rctx.routeParams.Values = append(rctx.routeParams.Values, xsearch[:p]) - xsearch = xsearch[p:] - - if len(xsearch) == 0 { - if xn.isLeaf() { - h := xn.endpoints[method] - if h != nil && h.handler != nil { - rctx.routeParams.Keys = append(rctx.routeParams.Keys, h.paramKeys...) - return xn - } - - for endpoints := range xn.endpoints { - if endpoints == mALL || endpoints == mSTUB { - continue - } - rctx.methodsAllowed = append(rctx.methodsAllowed, endpoints) - } - - // flag that the routing context found a route, but not a corresponding - // supported method - rctx.methodNotAllowed = true - } - } - - // recursively find the next node on this branch - fin := xn.findRoute(rctx, method, xsearch) - if fin != nil { - return fin - } - - // not found on this branch, reset vars - rctx.routeParams.Values = rctx.routeParams.Values[:prevlen] - xsearch = search - } - - rctx.routeParams.Values = append(rctx.routeParams.Values, "") - - default: - // catch-all nodes - rctx.routeParams.Values = append(rctx.routeParams.Values, search) - xn = nds[0] - xsearch = "" - } - - if xn == nil { - continue - } - - // did we find it yet? - if len(xsearch) == 0 { - if xn.isLeaf() { - h := xn.endpoints[method] - if h != nil && h.handler != nil { - rctx.routeParams.Keys = append(rctx.routeParams.Keys, h.paramKeys...) - return xn - } - - for endpoints := range xn.endpoints { - if endpoints == mALL || endpoints == mSTUB { - continue - } - rctx.methodsAllowed = append(rctx.methodsAllowed, endpoints) - } - - // flag that the routing context found a route, but not a corresponding - // supported method - rctx.methodNotAllowed = true - } - } - - // recursively find the next node.. - fin := xn.findRoute(rctx, method, xsearch) - if fin != nil { - return fin - } - - // Did not find final handler, let's remove the param here if it was set - if xn.typ > ntStatic { - if len(rctx.routeParams.Values) > 0 { - rctx.routeParams.Values = rctx.routeParams.Values[:len(rctx.routeParams.Values)-1] - } - } - - } - - return nil -} - -func (n *node) findEdge(ntyp nodeTyp, label byte) *node { - nds := n.children[ntyp] - num := len(nds) - idx := 0 - - switch ntyp { - case ntStatic, ntParam, ntRegexp: - i, j := 0, num-1 - for i <= j { - idx = i + (j-i)/2 - if label > nds[idx].label { - i = idx + 1 - } else if label < nds[idx].label { - j = idx - 1 - } else { - i = num // breaks cond - } - } - if nds[idx].label != label { - return nil - } - return nds[idx] - - default: // catch all - return nds[idx] - } -} - -func (n *node) isLeaf() bool { - return n.endpoints != nil -} - -func (n *node) findPattern(pattern string) bool { - nn := n - for _, nds := range nn.children { - if len(nds) == 0 { - continue - } - - n = nn.findEdge(nds[0].typ, pattern[0]) - if n == nil { - continue - } - - var idx int - var xpattern string - - switch n.typ { - case ntStatic: - idx = longestPrefix(pattern, n.prefix) - if idx < len(n.prefix) { - continue - } - - case ntParam, ntRegexp: - idx = strings.IndexByte(pattern, '}') + 1 - - case ntCatchAll: - idx = longestPrefix(pattern, "*") - - default: - panic("chi: unknown node type") - } - - xpattern = pattern[idx:] - if len(xpattern) == 0 { - return true - } - - return n.findPattern(xpattern) - } - return false -} - -func (n *node) routes() []Route { - rts := []Route{} - - n.walk(func(eps endpoints, subroutes Routes) bool { - if eps[mSTUB] != nil && eps[mSTUB].handler != nil && subroutes == nil { - return false - } - - // Group methodHandlers by unique patterns - pats := make(map[string]endpoints) - - for mt, h := range eps { - if h.pattern == "" { - continue - } - p, ok := pats[h.pattern] - if !ok { - p = endpoints{} - pats[h.pattern] = p - } - p[mt] = h - } - - for p, mh := range pats { - hs := make(map[string]http.Handler) - if mh[mALL] != nil && mh[mALL].handler != nil { - hs["*"] = mh[mALL].handler - } - - for mt, h := range mh { - if h.handler == nil { - continue - } - m := methodTypString(mt) - if m == "" { - continue - } - hs[m] = h.handler - } - - rt := Route{subroutes, hs, p} - rts = append(rts, rt) - } - - return false - }) - - return rts -} - -func (n *node) walk(fn func(eps endpoints, subroutes Routes) bool) bool { - // Visit the leaf values if any - if (n.endpoints != nil || n.subroutes != nil) && fn(n.endpoints, n.subroutes) { - return true - } - - // Recurse on the children - for _, ns := range n.children { - for _, cn := range ns { - if cn.walk(fn) { - return true - } - } - } - return false -} - -// patNextSegment returns the next segment details from a pattern: -// node type, param key, regexp string, param tail byte, param starting index, param ending index -func patNextSegment(pattern string) (nodeTyp, string, string, byte, int, int) { - ps := strings.Index(pattern, "{") - ws := strings.Index(pattern, "*") - - if ps < 0 && ws < 0 { - return ntStatic, "", "", 0, 0, len(pattern) // we return the entire thing - } - - // Sanity check - if ps >= 0 && ws >= 0 && ws < ps { - panic("chi: wildcard '*' must be the last pattern in a route, otherwise use a '{param}'") - } - - var tail byte = '/' // Default endpoint tail to / byte - - if ps >= 0 { - // Param/Regexp pattern is next - nt := ntParam - - // Read to closing } taking into account opens and closes in curl count (cc) - cc := 0 - pe := ps - for i, c := range pattern[ps:] { - if c == '{' { - cc++ - } else if c == '}' { - cc-- - if cc == 0 { - pe = ps + i - break - } - } - } - if pe == ps { - panic("chi: route param closing delimiter '}' is missing") - } - - key := pattern[ps+1 : pe] - pe++ // set end to next position - - if pe < len(pattern) { - tail = pattern[pe] - } - - var rexpat string - if idx := strings.Index(key, ":"); idx >= 0 { - nt = ntRegexp - rexpat = key[idx+1:] - key = key[:idx] - } - - if len(rexpat) > 0 { - if rexpat[0] != '^' { - rexpat = "^" + rexpat - } - if rexpat[len(rexpat)-1] != '$' { - rexpat += "$" - } - } - - return nt, key, rexpat, tail, ps, pe - } - - // Wildcard pattern as finale - if ws < len(pattern)-1 { - panic("chi: wildcard '*' must be the last value in a route. trim trailing text or use a '{param}' instead") - } - return ntCatchAll, "*", "", 0, ws, len(pattern) -} - -func patParamKeys(pattern string) []string { - pat := pattern - paramKeys := []string{} - for { - ptyp, paramKey, _, _, _, e := patNextSegment(pat) - if ptyp == ntStatic { - return paramKeys - } - for i := 0; i < len(paramKeys); i++ { - if paramKeys[i] == paramKey { - panic(fmt.Sprintf("chi: routing pattern '%s' contains duplicate param key, '%s'", pattern, paramKey)) - } - } - paramKeys = append(paramKeys, paramKey) - pat = pat[e:] - } -} - -// longestPrefix finds the length of the shared prefix -// of two strings -func longestPrefix(k1, k2 string) int { - max := len(k1) - if l := len(k2); l < max { - max = l - } - var i int - for i = 0; i < max; i++ { - if k1[i] != k2[i] { - break - } - } - return i -} - -func methodTypString(method methodTyp) string { - for s, t := range methodMap { - if method == t { - return s - } - } - return "" -} - -type nodes []*node - -// Sort the list of nodes by label -func (ns nodes) Sort() { sort.Sort(ns); ns.tailSort() } -func (ns nodes) Len() int { return len(ns) } -func (ns nodes) Swap(i, j int) { ns[i], ns[j] = ns[j], ns[i] } -func (ns nodes) Less(i, j int) bool { return ns[i].label < ns[j].label } - -// tailSort pushes nodes with '/' as the tail to the end of the list for param nodes. -// The list order determines the traversal order. -func (ns nodes) tailSort() { - for i := len(ns) - 1; i >= 0; i-- { - if ns[i].typ > ntStatic && ns[i].tail == '/' { - ns.Swap(i, len(ns)-1) - return - } - } -} - -func (ns nodes) findEdge(label byte) *node { - num := len(ns) - idx := 0 - i, j := 0, num-1 - for i <= j { - idx = i + (j-i)/2 - if label > ns[idx].label { - i = idx + 1 - } else if label < ns[idx].label { - j = idx - 1 - } else { - i = num // breaks cond - } - } - if ns[idx].label != label { - return nil - } - return ns[idx] -} - -// Route describes the details of a routing handler. -// Handlers map key is an HTTP method -type Route struct { - SubRoutes Routes - Handlers map[string]http.Handler - Pattern string -} - -// WalkFunc is the type of the function called for each method and route visited by Walk. -type WalkFunc func(method string, route string, handler http.Handler, middlewares ...func(http.Handler) http.Handler) error - -// Walk walks any router tree that implements Routes interface. -func Walk(r Routes, walkFn WalkFunc) error { - return walk(r, walkFn, "") -} - -func walk(r Routes, walkFn WalkFunc, parentRoute string, parentMw ...func(http.Handler) http.Handler) error { - for _, route := range r.Routes() { - mws := make([]func(http.Handler) http.Handler, len(parentMw)) - copy(mws, parentMw) - mws = append(mws, r.Middlewares()...) - - if route.SubRoutes != nil { - if err := walk(route.SubRoutes, walkFn, parentRoute+route.Pattern, mws...); err != nil { - return err - } - continue - } - - for method, handler := range route.Handlers { - if method == "*" { - // Ignore a "catchAll" method, since we pass down all the specific methods for each route. - continue - } - - fullRoute := parentRoute + route.Pattern - fullRoute = strings.Replace(fullRoute, "/*/", "/", -1) - - if chain, ok := handler.(*ChainHandler); ok { - if err := walkFn(method, fullRoute, chain.Endpoint, append(mws, chain.Middlewares...)...); err != nil { - return err - } - } else { - if err := walkFn(method, fullRoute, handler, mws...); err != nil { - return err - } - } - } - } - - return nil -} diff --git a/backend/vendor/github.com/go-chi/render/.gitignore b/backend/vendor/github.com/go-chi/render/.gitignore deleted file mode 100644 index 22d0d82f..00000000 --- a/backend/vendor/github.com/go-chi/render/.gitignore +++ /dev/null @@ -1 +0,0 @@ -vendor diff --git a/backend/vendor/github.com/go-chi/render/LICENSE b/backend/vendor/github.com/go-chi/render/LICENSE deleted file mode 100644 index 4344db78..00000000 --- a/backend/vendor/github.com/go-chi/render/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -Copyright (c) 2016-Present https://github.com/go-chi authors - -MIT License - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/backend/vendor/github.com/go-chi/render/README.md b/backend/vendor/github.com/go-chi/render/README.md deleted file mode 100644 index 4d371538..00000000 --- a/backend/vendor/github.com/go-chi/render/README.md +++ /dev/null @@ -1,27 +0,0 @@ -# render - -![tests](https://github.com/go-chi/render/actions/workflows/test.yml/badge.svg) -[![Go Report Card](https://goreportcard.com/badge/github.com/go-chi/render)](https://goreportcard.com/report/github.com/go-chi/render) -[![Go Reference](https://pkg.go.dev/badge/github.com/go-chi/render.svg)](https://pkg.go.dev/github.com/go-chi/render) - -The `render` package helps manage HTTP request / response payloads. - -Every well-designed, robust and maintainable Web Service / REST API also needs -well-*defined* request and response payloads. Together with the endpoint handlers, -the request and response payloads make up the contract between your server and the -clients calling on it. - -Typically in a REST API application, you will have your data models (objects/structs) -that hold lower-level runtime application state, and at times you need to assemble, -decorate, hide or transform the representation before responding to a client. That -server output (response payload) structure, is also likely the input structure to -another handler on the server. - -This is where `render` comes in - offering a few simple helpers and interfaces to -provide a simple pattern for managing payload encoding and decoding. - -We've also combined it with some helpers for responding to content types and parsing -request bodies. Please have a look at the [rest](https://github.com/go-chi/chi/blob/master/_examples/rest/main.go) -example which uses the latest chi/render sub-pkg. - -All feedback is welcome, thank you! diff --git a/backend/vendor/github.com/go-chi/render/content_type.go b/backend/vendor/github.com/go-chi/render/content_type.go deleted file mode 100644 index b11a680c..00000000 --- a/backend/vendor/github.com/go-chi/render/content_type.go +++ /dev/null @@ -1,84 +0,0 @@ -package render - -import ( - "context" - "net/http" - "strings" -) - -var ( - ContentTypeCtxKey = &contextKey{"ContentType"} -) - -// ContentType is an enumeration of common HTTP content types. -type ContentType int - -// ContentTypes handled by this package. -const ( - ContentTypeUnknown ContentType = iota - ContentTypePlainText - ContentTypeHTML - ContentTypeJSON - ContentTypeXML - ContentTypeForm - ContentTypeEventStream -) - -func GetContentType(s string) ContentType { - s = strings.TrimSpace(strings.Split(s, ";")[0]) - switch s { - case "text/plain": - return ContentTypePlainText - case "text/html", "application/xhtml+xml": - return ContentTypeHTML - case "application/json", "text/javascript": - return ContentTypeJSON - case "text/xml", "application/xml": - return ContentTypeXML - case "application/x-www-form-urlencoded": - return ContentTypeForm - case "text/event-stream": - return ContentTypeEventStream - default: - return ContentTypeUnknown - } -} - -// SetContentType is a middleware that forces response Content-Type. -func SetContentType(contentType ContentType) func(next http.Handler) http.Handler { - return func(next http.Handler) http.Handler { - fn := func(w http.ResponseWriter, r *http.Request) { - r = r.WithContext(context.WithValue(r.Context(), ContentTypeCtxKey, contentType)) - next.ServeHTTP(w, r) - } - return http.HandlerFunc(fn) - } -} - -// GetRequestContentType is a helper function that returns ContentType based on -// context or request headers. -func GetRequestContentType(r *http.Request) ContentType { - if contentType, ok := r.Context().Value(ContentTypeCtxKey).(ContentType); ok { - return contentType - } - return GetContentType(r.Header.Get("Content-Type")) -} - -func GetAcceptedContentType(r *http.Request) ContentType { - if contentType, ok := r.Context().Value(ContentTypeCtxKey).(ContentType); ok { - return contentType - } - - var contentType ContentType - - // Parse request Accept header. - fields := strings.Split(r.Header.Get("Accept"), ",") - if len(fields) > 0 { - contentType = GetContentType(strings.TrimSpace(fields[0])) - } - - if contentType == ContentTypeUnknown { - contentType = ContentTypePlainText - } - return contentType -} diff --git a/backend/vendor/github.com/go-chi/render/decoder.go b/backend/vendor/github.com/go-chi/render/decoder.go deleted file mode 100644 index 883b2f84..00000000 --- a/backend/vendor/github.com/go-chi/render/decoder.go +++ /dev/null @@ -1,57 +0,0 @@ -package render - -import ( - "encoding/json" - "encoding/xml" - "errors" - "io" - "io/ioutil" - "net/http" - - "github.com/ajg/form" -) - -// Decode is a package-level variable set to our default Decoder. We do this -// because it allows you to set render.Decode to another function with the -// same function signature, while also utilizing the render.Decoder() function -// itself. Effectively, allowing you to easily add your own logic to the package -// defaults. For example, maybe you want to impose a limit on the number of -// bytes allowed to be read from the request body. -var Decode = DefaultDecoder - -// DefaultDecoder detects the correct decoder for use on an HTTP request and -// marshals into a given interface. -func DefaultDecoder(r *http.Request, v interface{}) error { - var err error - - switch GetRequestContentType(r) { - case ContentTypeJSON: - err = DecodeJSON(r.Body, v) - case ContentTypeXML: - err = DecodeXML(r.Body, v) - case ContentTypeForm: - err = DecodeForm(r.Body, v) - default: - err = errors.New("render: unable to automatically decode the request content type") - } - - return err -} - -// DecodeJSON decodes a given reader into an interface using the json decoder. -func DecodeJSON(r io.Reader, v interface{}) error { - defer io.Copy(ioutil.Discard, r) //nolint:errcheck - return json.NewDecoder(r).Decode(v) -} - -// DecodeXML decodes a given reader into an interface using the xml decoder. -func DecodeXML(r io.Reader, v interface{}) error { - defer io.Copy(ioutil.Discard, r) //nolint:errcheck - return xml.NewDecoder(r).Decode(v) -} - -// DecodeForm decodes a given reader into an interface using the form decoder. -func DecodeForm(r io.Reader, v interface{}) error { - decoder := form.NewDecoder(r) //nolint:errcheck - return decoder.Decode(v) -} diff --git a/backend/vendor/github.com/go-chi/render/render.go b/backend/vendor/github.com/go-chi/render/render.go deleted file mode 100644 index 75a90e23..00000000 --- a/backend/vendor/github.com/go-chi/render/render.go +++ /dev/null @@ -1,143 +0,0 @@ -package render - -import ( - "net/http" - "reflect" -) - -// Renderer interface for managing response payloads. -type Renderer interface { - Render(w http.ResponseWriter, r *http.Request) error -} - -// Binder interface for managing request payloads. -type Binder interface { - Bind(r *http.Request) error -} - -// Bind decodes a request body and executes the Binder method of the -// payload structure. -func Bind(r *http.Request, v Binder) error { - if err := Decode(r, v); err != nil { - return err - } - return binder(r, v) -} - -// Render renders a single payload and respond to the client request. -func Render(w http.ResponseWriter, r *http.Request, v Renderer) error { - if err := renderer(w, r, v); err != nil { - return err - } - Respond(w, r, v) - return nil -} - -// RenderList renders a slice of payloads and responds to the client request. -func RenderList(w http.ResponseWriter, r *http.Request, l []Renderer) error { - for _, v := range l { - if err := renderer(w, r, v); err != nil { - return err - } - } - Respond(w, r, l) - return nil -} - -func isNil(f reflect.Value) bool { - switch f.Kind() { - case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice: - return f.IsNil() - default: - return false - } -} - -// Executed top-down -func renderer(w http.ResponseWriter, r *http.Request, v Renderer) error { - rv := reflect.ValueOf(v) - if rv.Kind() == reflect.Ptr { - rv = rv.Elem() - } - - // We call it top-down. - if err := v.Render(w, r); err != nil { - return err - } - - // We're done if the Renderer isn't a struct object - if rv.Kind() != reflect.Struct { - return nil - } - - // For structs, we call Render on each field that implements Renderer - for i := 0; i < rv.NumField(); i++ { - f := rv.Field(i) - if f.Type().Implements(rendererType) { - - if isNil(f) { - continue - } - - fv := f.Interface().(Renderer) - if err := renderer(w, r, fv); err != nil { - return err - } - - } - } - - return nil -} - -// Executed bottom-up -func binder(r *http.Request, v Binder) error { - rv := reflect.ValueOf(v) - if rv.Kind() == reflect.Ptr { - rv = rv.Elem() - } - - // Call Binder on non-struct types right away - if rv.Kind() != reflect.Struct { - return v.Bind(r) - } - - // For structs, we call Bind on each field that implements Binder - for i := 0; i < rv.NumField(); i++ { - f := rv.Field(i) - if f.Type().Implements(binderType) { - - if isNil(f) { - continue - } - - fv := f.Interface().(Binder) - if err := binder(r, fv); err != nil { - return err - } - } - } - - // We call it bottom-up - if err := v.Bind(r); err != nil { - return err - } - - return nil -} - -var ( - rendererType = reflect.TypeOf(new(Renderer)).Elem() - binderType = reflect.TypeOf(new(Binder)).Elem() -) - -// contextKey is a value for use with context.WithValue. It's used as -// a pointer so it fits in an interface{} without allocation. This technique -// for defining context keys was copied from Go 1.7's new use of context in net/http. -type contextKey struct { - name string -} - -func (k *contextKey) String() string { - return "chi render context value " + k.name -} diff --git a/backend/vendor/github.com/go-chi/render/responder.go b/backend/vendor/github.com/go-chi/render/responder.go deleted file mode 100644 index f38807d1..00000000 --- a/backend/vendor/github.com/go-chi/render/responder.go +++ /dev/null @@ -1,234 +0,0 @@ -package render - -import ( - "bytes" - "context" - "encoding/json" - "encoding/xml" - "fmt" - "net/http" - "reflect" -) - -// M is a convenience alias for quickly building a map structure that is going -// out to a responder. Just a short-hand. -type M map[string]interface{} - -// Respond is a package-level variable set to our default Responder. We do this -// because it allows you to set render.Respond to another function with the -// same function signature, while also utilizing the render.Responder() function -// itself. Effectively, allowing you to easily add your own logic to the package -// defaults. For example, maybe you want to test if v is an error and respond -// differently, or log something before you respond. -var Respond = DefaultResponder - -// StatusCtxKey is a context key to record a future HTTP response status code. -var StatusCtxKey = &contextKey{"Status"} - -// Status sets a HTTP response status code hint into request context at any point -// during the request life-cycle. Before the Responder sends its response header -// it will check the StatusCtxKey -func Status(r *http.Request, status int) { - *r = *r.WithContext(context.WithValue(r.Context(), StatusCtxKey, status)) -} - -// Respond handles streaming JSON and XML responses, automatically setting the -// Content-Type based on request headers. It will default to a JSON response. -func DefaultResponder(w http.ResponseWriter, r *http.Request, v interface{}) { - if v != nil { - switch reflect.TypeOf(v).Kind() { - case reflect.Chan: - switch GetAcceptedContentType(r) { - case ContentTypeEventStream: - channelEventStream(w, r, v) - return - default: - v = channelIntoSlice(w, r, v) - } - } - } - - // Format response based on request Accept header. - switch GetAcceptedContentType(r) { - case ContentTypeJSON: - JSON(w, r, v) - case ContentTypeXML: - XML(w, r, v) - default: - JSON(w, r, v) - } -} - -// PlainText writes a string to the response, setting the Content-Type as -// text/plain. -func PlainText(w http.ResponseWriter, r *http.Request, v string) { - w.Header().Set("Content-Type", "text/plain; charset=utf-8") - if status, ok := r.Context().Value(StatusCtxKey).(int); ok { - w.WriteHeader(status) - } - w.Write([]byte(v)) //nolint:errcheck -} - -// Data writes raw bytes to the response, setting the Content-Type as -// application/octet-stream. -func Data(w http.ResponseWriter, r *http.Request, v []byte) { - w.Header().Set("Content-Type", "application/octet-stream") - if status, ok := r.Context().Value(StatusCtxKey).(int); ok { - w.WriteHeader(status) - } - w.Write(v) //nolint:errcheck -} - -// HTML writes a string to the response, setting the Content-Type as text/html. -func HTML(w http.ResponseWriter, r *http.Request, v string) { - w.Header().Set("Content-Type", "text/html; charset=utf-8") - if status, ok := r.Context().Value(StatusCtxKey).(int); ok { - w.WriteHeader(status) - } - w.Write([]byte(v)) //nolint:errcheck -} - -// JSON marshals 'v' to JSON, automatically escaping HTML and setting the -// Content-Type as application/json. -func JSON(w http.ResponseWriter, r *http.Request, v interface{}) { - buf := &bytes.Buffer{} - enc := json.NewEncoder(buf) - enc.SetEscapeHTML(true) - if err := enc.Encode(v); err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - w.Header().Set("Content-Type", "application/json") - if status, ok := r.Context().Value(StatusCtxKey).(int); ok { - w.WriteHeader(status) - } - w.Write(buf.Bytes()) //nolint:errcheck -} - -// XML marshals 'v' to XML, setting the Content-Type as application/xml. It -// will automatically prepend a generic XML header (see encoding/xml.Header) if -// one is not found in the first 100 bytes of 'v'. -func XML(w http.ResponseWriter, r *http.Request, v interface{}) { - b, err := xml.Marshal(v) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - w.Header().Set("Content-Type", "application/xml; charset=utf-8") - if status, ok := r.Context().Value(StatusCtxKey).(int); ok { - w.WriteHeader(status) - } - - // Try to find 100 { - findHeaderUntil = 100 - } - if !bytes.Contains(b[:findHeaderUntil], []byte(" 0 && len(c.items) > c.maxKeys - // Verify size not exceeded - if evict { - c.removeOldest() - } - return evict -} - -// Get returns the key value if it's not expired -func (c *cacheImpl[K, V]) Get(key K) (V, bool) { - def := *new(V) - c.Lock() - defer c.Unlock() - if ent, ok := c.items[key]; ok { - // Expired item check - if time.Now().After(ent.Value.(*cacheItem[K, V]).expiresAt) { - c.stat.Misses++ - return ent.Value.(*cacheItem[K, V]).value, false - } - if c.isLRU { - c.evictList.MoveToFront(ent) - } - c.stat.Hits++ - return ent.Value.(*cacheItem[K, V]).value, true - } - c.stat.Misses++ - return def, false -} - -// Contains checks if a key is in the cache, without updating the recent-ness -// or deleting it for being stale. -func (c *cacheImpl[K, V]) Contains(key K) (ok bool) { - c.Lock() - defer c.Unlock() - _, ok = c.items[key] - return ok -} - -// Peek returns the key value (or undefined if not found) without updating the "recently used"-ness of the key. -// Works exactly the same as Get in case of LRC mode (default one). -func (c *cacheImpl[K, V]) Peek(key K) (V, bool) { - def := *new(V) - c.Lock() - defer c.Unlock() - if ent, ok := c.items[key]; ok { - // Expired item check - if time.Now().After(ent.Value.(*cacheItem[K, V]).expiresAt) { - c.stat.Misses++ - return ent.Value.(*cacheItem[K, V]).value, false - } - c.stat.Hits++ - return ent.Value.(*cacheItem[K, V]).value, true - } - c.stat.Misses++ - return def, false -} - -// GetExpiration returns the expiration time of the key. Non-existing key returns zero time. -func (c *cacheImpl[K, V]) GetExpiration(key K) (time.Time, bool) { - c.Lock() - defer c.Unlock() - if ent, ok := c.items[key]; ok { - return ent.Value.(*cacheItem[K, V]).expiresAt, true - } - return time.Time{}, false -} - -// Keys returns a slice of the keys in the cache, from oldest to newest. -func (c *cacheImpl[K, V]) Keys() []K { - c.Lock() - defer c.Unlock() - return c.keys() -} - -// Values returns a slice of the values in the cache, from oldest to newest. -// Expired entries are filtered out. -func (c *cacheImpl[K, V]) Values() []V { - c.Lock() - defer c.Unlock() - values := make([]V, 0, len(c.items)) - now := time.Now() - for ent := c.evictList.Back(); ent != nil; ent = ent.Prev() { - if now.After(ent.Value.(*cacheItem[K, V]).expiresAt) { - continue - } - values = append(values, ent.Value.(*cacheItem[K, V]).value) - } - return values -} - -// Len return count of items in cache, including expired -func (c *cacheImpl[K, V]) Len() int { - c.Lock() - defer c.Unlock() - return c.evictList.Len() -} - -// Resize changes the cache size. Size of 0 means unlimited. -func (c *cacheImpl[K, V]) Resize(size int) int { - c.Lock() - defer c.Unlock() - if size <= 0 { - c.maxKeys = 0 - return 0 - } - diff := c.evictList.Len() - size - if diff < 0 { - diff = 0 - } - for i := 0; i < diff; i++ { - c.removeOldest() - } - c.maxKeys = size - return diff -} - -// Invalidate key (item) from the cache -func (c *cacheImpl[K, V]) Invalidate(key K) { - c.Lock() - defer c.Unlock() - if ent, ok := c.items[key]; ok { - c.removeElement(ent) - } -} - -// InvalidateFn deletes multiple keys if predicate is true -func (c *cacheImpl[K, V]) InvalidateFn(fn func(key K) bool) { - c.Lock() - defer c.Unlock() - for key, ent := range c.items { - if fn(key) { - c.removeElement(ent) - } - } -} - -// Remove removes the provided key from the cache, returning if the -// key was contained. -func (c *cacheImpl[K, V]) Remove(key K) bool { - c.Lock() - defer c.Unlock() - if ent, ok := c.items[key]; ok { - c.removeElement(ent) - return true - } - return false -} - -// RemoveOldest remove the oldest element in the cache -func (c *cacheImpl[K, V]) RemoveOldest() (key K, value V, ok bool) { - c.Lock() - defer c.Unlock() - if ent := c.evictList.Back(); ent != nil { - c.removeElement(ent) - return ent.Value.(*cacheItem[K, V]).key, ent.Value.(*cacheItem[K, V]).value, true - } - return -} - -// GetOldest returns the oldest entry -func (c *cacheImpl[K, V]) GetOldest() (key K, value V, ok bool) { - c.Lock() - defer c.Unlock() - if ent := c.evictList.Back(); ent != nil { - return ent.Value.(*cacheItem[K, V]).key, ent.Value.(*cacheItem[K, V]).value, true - } - return -} - -// DeleteExpired clears cache of expired items -func (c *cacheImpl[K, V]) DeleteExpired() { - c.Lock() - defer c.Unlock() - for _, key := range c.keys() { - if time.Now().After(c.items[key].Value.(*cacheItem[K, V]).expiresAt) { - c.removeElement(c.items[key]) - } - } -} - -// Purge clears the cache completely. -func (c *cacheImpl[K, V]) Purge() { - c.Lock() - defer c.Unlock() - for k, v := range c.items { - delete(c.items, k) - c.stat.Evicted++ - if c.onEvicted != nil { - c.onEvicted(k, v.Value.(*cacheItem[K, V]).value) - } - } - c.evictList.Init() -} - -// Stat gets the current stats for cache -func (c *cacheImpl[K, V]) Stat() Stats { - c.Lock() - defer c.Unlock() - return c.stat -} - -func (c *cacheImpl[K, V]) String() string { - stats := c.Stat() - size := c.Len() - return fmt.Sprintf("Size: %d, Stats: %+v (%0.1f%%)", size, stats, 100*float64(stats.Hits)/float64(stats.Hits+stats.Misses)) -} - -// Keys returns a slice of the keys in the cache, from oldest to newest. Has to be called with lock! -func (c *cacheImpl[K, V]) keys() []K { - keys := make([]K, 0, len(c.items)) - for ent := c.evictList.Back(); ent != nil; ent = ent.Prev() { - keys = append(keys, ent.Value.(*cacheItem[K, V]).key) - } - return keys -} - -// removeOldest removes the oldest item from the cache. Has to be called with lock! -func (c *cacheImpl[K, V]) removeOldest() { - ent := c.evictList.Back() - if ent != nil { - c.removeElement(ent) - } -} - -// removeOldest removes the oldest item from the cache in case it's already expired. Has to be called with lock! -func (c *cacheImpl[K, V]) removeOldestIfExpired() { - ent := c.evictList.Back() - if ent != nil && time.Now().After(ent.Value.(*cacheItem[K, V]).expiresAt) { - c.removeElement(ent) - } -} - -// removeElement is used to remove a given list element from the cache. Has to be called with lock! -func (c *cacheImpl[K, V]) removeElement(e *list.Element) { - c.evictList.Remove(e) - kv := e.Value.(*cacheItem[K, V]) - delete(c.items, kv.key) - c.stat.Evicted++ - if c.onEvicted != nil { - c.onEvicted(kv.key, kv.value) - } -} - -// cacheItem is used to hold a value in the evictList -type cacheItem[K comparable, V any] struct { - expiresAt time.Time - key K - value V -} diff --git a/backend/vendor/github.com/go-pkgz/expirable-cache/v3/options.go b/backend/vendor/github.com/go-pkgz/expirable-cache/v3/options.go deleted file mode 100644 index b5fe65d8..00000000 --- a/backend/vendor/github.com/go-pkgz/expirable-cache/v3/options.go +++ /dev/null @@ -1,36 +0,0 @@ -package cache - -import "time" - -type options[K comparable, V any] interface { - WithTTL(ttl time.Duration) Cache[K, V] - WithMaxKeys(maxKeys int) Cache[K, V] - WithLRU() Cache[K, V] - WithOnEvicted(fn func(key K, value V)) Cache[K, V] -} - -// WithTTL functional option defines TTL for all cache entries. -// By default, it is set to 10 years, sane option for expirable cache might be 5 minutes. -func (c *cacheImpl[K, V]) WithTTL(ttl time.Duration) Cache[K, V] { - c.ttl = ttl - return c -} - -// WithMaxKeys functional option defines how many keys to keep. -// By default, it is 0, which means unlimited. -func (c *cacheImpl[K, V]) WithMaxKeys(maxKeys int) Cache[K, V] { - c.maxKeys = maxKeys - return c -} - -// WithLRU sets cache to LRU (Least Recently Used) eviction mode. -func (c *cacheImpl[K, V]) WithLRU() Cache[K, V] { - c.isLRU = true - return c -} - -// WithOnEvicted defined function which would be called automatically for automatically and manually deleted entries -func (c *cacheImpl[K, V]) WithOnEvicted(fn func(key K, value V)) Cache[K, V] { - c.onEvicted = fn - return c -} diff --git a/backend/vendor/github.com/go-pkgz/routegroup/.golangci.yml b/backend/vendor/github.com/go-pkgz/routegroup/.golangci.yml new file mode 100644 index 00000000..b740be7f --- /dev/null +++ b/backend/vendor/github.com/go-pkgz/routegroup/.golangci.yml @@ -0,0 +1,59 @@ +linters-settings: + govet: + shadow: true + gocyclo: + min-complexity: 15 + maligned: + suggest-new: true + goconst: + min-len: 2 + min-occurrences: 2 + misspell: + locale: US + lll: + line-length: 140 + gocritic: + enabled-tags: + - performance + - style + - experimental + disabled-checks: + - wrapperFunc + +linters: + enable: + - staticcheck + - revive + - govet + - unconvert + - gosec + - misspell + - unparam + - typecheck + - ineffassign + - stylecheck + - gochecknoinits + - copyloopvar + - gocritic + - nakedret + - gosimple + - prealloc + - unused + fast: false + disable-all: true + + +run: + concurrency: 4 + +issues: + exclude-rules: + - text: "G114: Use of net/http serve function that has no support for setting timeouts" + linters: + - gosec + - linters: + - unparam + - revive + path: _test\.go$ + text: "unused-parameter" + exclude-use-default: false diff --git a/backend/vendor/github.com/go-pkgz/expirable-cache/v3/LICENSE b/backend/vendor/github.com/go-pkgz/routegroup/LICENSE similarity index 94% rename from backend/vendor/github.com/go-pkgz/expirable-cache/v3/LICENSE rename to backend/vendor/github.com/go-pkgz/routegroup/LICENSE index d4771b09..e47db563 100644 --- a/backend/vendor/github.com/go-pkgz/expirable-cache/v3/LICENSE +++ b/backend/vendor/github.com/go-pkgz/routegroup/LICENSE @@ -1,7 +1,6 @@ MIT License -Copyright (c) 2020 Umputun -Copyright (c) 2020 Dmitry Verhoturov +Copyright (c) 2024 Umputun Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/backend/vendor/github.com/go-pkgz/routegroup/README.md b/backend/vendor/github.com/go-pkgz/routegroup/README.md new file mode 100644 index 00000000..375e9dd6 --- /dev/null +++ b/backend/vendor/github.com/go-pkgz/routegroup/README.md @@ -0,0 +1,263 @@ +## routegroup [![Build Status](https://github.com/go-pkgz/routegroup/workflows/build/badge.svg)](https://github.com/go-pkgz/routegroup/actions) [![Go Report Card](https://goreportcard.com/badge/github.com/go-pkgz/routegroup)](https://goreportcard.com/report/github.com/go-pkgz/routegroup) [![Coverage Status](https://coveralls.io/repos/github/go-pkgz/routegroup/badge.svg?branch=master)](https://coveralls.io/github/go-pkgz/routegroup?branch=master) [![godoc](https://godoc.org/github.com/go-pkgz/routegroup?status.svg)](https://godoc.org/github.com/go-pkgz/routegroup) + + +`routegroup` is a tiny Go package providing a lightweight wrapper for efficient route grouping and middleware integration with the standard `http.ServeMux`. + +## Features + +- Simple and intuitive API for route grouping and route mounting. +- Lightweight, just about 100 LOC +- Easy middleware integration for individual routes or groups of routes. +- Seamless integration with Go's standard `http.ServeMux`. +- Fully compatible with the `http.Handler` interface and can be used as a drop-in replacement for `http.ServeMux`. +- No external dependencies. + +## Install and update + +`go get -u github.com/go-pkgz/routegroup` + +## Usage + +**Creating a New Route Group** + +To start, create a new route group without a base path: + +```go +func main() { + mux := http.NewServeMux() + group := routegroup.New(mux) +} +``` + +**Adding Routes with Middleware** + +Add routes to your group, optionally with middleware: + +```go + group.Use(loggingMiddleware, corsMiddleware) + group.Handle("/hello", helloHandler) + group.Handle("/bye", byeHandler) +``` +**Creating a Nested Route Group** + +For routes under a specific path prefix `Mount` method can be used to create a nested group: + +```go + apiGroup := routegroup.Mount(mux, "/api") + apiGroup.Use(loggingMiddleware, corsMiddleware) + apiGroup.Handle("/v1", apiV1Handler) + apiGroup.Handle("/v2", apiV2Handler) + +``` + +**Complete Example** + +Here's a complete example demonstrating route grouping and middleware usage: + +```go +package main + +import ( + "net/http" + + "github.com/go-pkgz/routegroup" +) + +func main() { + router := routegroup.New(http.NewServeMux()) + router.Use(loggingMiddleware) + + // handle the /hello route + router.Handle("GET /hello", helloHandler) + + // create a new group for the /api path + apiRouter := router.Mount("/api") + // add middleware + apiRouter.Use(loggingMiddleware, corsMiddleware) + + // route handling + apiRouter.HandleFunc("GET /hello", func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte("Hello, API!")) + }) + + // add another group with its own set of middlewares + protectedGroup := router.Group() + protectedGroup.Use(authMiddleware) + protectedGroup.HandleFunc("GET /protected", func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte("Protected API!")) + }) + + http.ListenAndServe(":8080", router) +} +``` + +**Applying Middleware to Specific Routes** + +You can also apply middleware to specific routes inside the group without modifying the group's middleware stack: + +```go +apiGroup.With(corsMiddleware, apiMiddleware).Handle("GET /hello", helloHandler) +``` + +**Alternative Usage with `Route`** + +You can also use the `Route` method to add routes and middleware in a single function call: + +```go +router := routegroup.New(http.NewServeMux()) +router.Group().Route(func(b *routegroup.Bundle) { + b.Use(loggingMiddleware, corsMiddleware) + b.Handle("GET /hello", helloHandler) + b.Handle("GET /bye", byeHandler) +}) +http.ListenAndServe(":8080", group) +``` + +Important: The `Route` method does not create a new group by itself; it merely applies middleware and routes to the current group in a functional style. In the example provided, this is technically equivalent to sequentially calling the `Use` and `Handle` methods for the caller's group. While this may not seem intuitive, it is crucial to understand, as using the `Route` method might mistakenly appear to be a way to create a new (sub)group, which it is not. In 99% of cases, `Route` should be called after the creation of a sub-group, either by the `Mount` or `Group` methods. + +For example, using `Route` in this manner is likely a mistake, as it will apply middleware to the root group, not to the newly created sub-group. + +```go +group := routegroup.New(http.NewServeMux()) +group.Route(func(b *routegroup.Bundle) { + b.Use(loggingMiddleware, corsMiddleware) + b.Route(func(sub *routegroup.Bundle) { + sub.Handle("GET /hello", helloHandler) + }) +}) +``` + +**Setting optional `NotFoundHandler`** + +It is possible to set a custom `NotFoundHandler` for the group. This handler will be called when no other route matches the request: + +```go +group.NotFoundHandler(func(w http.ResponseWriter, _ *http.Request) { + http.Error(w, "404 page not found, something is wrong!", http.StatusNotFound) +} +``` + +If a custom `NotFoundHandler` is not configured, `routegroup` will default to using a handler from the standard library (`http.NotFoundHandler()`). It is important to note that the `NotFoundHandler` serves as a catch-all route, which influences "Method Not Allowed" (405) responses. Consequently, if an incorrect method is called, the response will be 404 (or the custom status specified by the `NotFoundHandler`) rather than 405. This behavior aligns with the standard `http.ServeMux` and [may be improved](https://github.com/golang/go/issues/65648) in future versions of Go. + + +### Using derived groups + +In some instances, it's practical to create an initial group that includes a set of middlewares, and then derive all other groups from it. This approach guarantees that every group incorporates a common set of middlewares as a foundation, allowing each to add its specific middlewares. To facilitate this scenario, `routegroup` offers both `Bundle.Group` and `Bundle.Mount` methods, and it also implements the `http.Handler` interface. The following example illustrates how to use derived groups: + +```go +// create a new bundle with a base set of middlewares +// note: the bundle is also http.Handler and can be passed to http.ListenAndServe +router := routegroup.New(http.NewServeMux()) +router.Use(loggingMiddleware, corsMiddleware) + +// add a new, derived group with its own set of middlewares +// this group will inherit the middlewares from the base group +apiGroup := router.Group() +apiGroup.Use(apiMiddleware) +apiGroup.Handle("GET /hello", helloHandler) +apiGroup.Handle("GET /bye", byeHandler) + +// mount another group for the /admin path with its own set of middlewares, +// using `Route` method to show the alternative usage. +// this group will inherit the middlewares from the base group as well +router.Mount("/admin").Route(func(b *routegroup.Bundle) { + b.Use(adminMiddleware) + b.Handle("POST /do", doHandler) +}) + +// start the server, passing the wrapped mux as the handler +http.ListenAndServe(":8080", router) +``` +### Wrap Function + +Sometimes route's group is not necessary, and all you need is to apply middleware(s) directly to a single route. In this case, `routegroup` provides a `Wrap` function that can be used to wrap a single `http.Handler` with one or more middlewares. Here's an example: + +```go +mux := http.NewServeMux() +mux.HandleFunc("/hello", routegroup.Wrap(helloHandler, loggingMiddleware, corsMiddleware)) +http.ListenAndServe(":8080", mux) +``` + +### Automatic registration of `NotFoundHandler` as catch-all route + +`routegroup` automatically registers a `NotFoundHandler` as a catch-all route, which is invoked when no other route matches the request. This handler is wrapped with all the middlewares that are associated with the group. This functionality is beneficial for applying middleware to all routes, including those that are unknown. It practically enables the use of middlewares that should operate across all routes, such as logging. + +In case you want to disable this behavior, you can use the `DisableNotFoundHandler()` function. + +### HandleFiles helper + +`routegroup` provides a helper function `HandleFiles` that can be used to serve static files from a directory. The function is a thin wrapper around the standard `http.FileServer` and can be used to serve files from a specific directory. Here's an example: + +```go +// serve static files from the "assets/static" directory +router.HandleFiles("/static/", http.Dir("assets/static")) +``` + +## Real-world example + +Here's an example of how `routegroup` can be used in a real-world application. The following code snippet is taken from a web service that provides a set of routes for user authentication, session management, and user management. The service also serves static files from the "assets/static" embedded file system. + +```go + +// Routes returns http.Handler that handles all the routes for the Service. +// It also serves static files from the "assets/static" directory. +// The rootURL option sets prefix for the routes. +func (s *Service) Routes() http.Handler { + router := routegroup.Mount(http.NewServeMux(), s.rootURL) // make a bundle with the rootURL base path + // add common middlewares + router.Use(rest.Maybe(handlers.CompressHandler, func(*http.Request) bool { return !s.skipGZ })) + router.Use(rest.Throttle(s.limitActiveReqs)) + router.Use(s.middleware.securityHeaders(s.skipSecurityHeaders)) + + // prepare csrf middleware + csrfMiddleware := s.middleware.csrf(s.skipCSRFCheck) + + // add open routes + router.HandleFunc("GET /login", s.loginPageHandler) + router.HandleFunc("POST /login", s.loginCheckHandler) + router.HandleFunc("GET /logout", s.logoutHandler) + + // add routes with auth middleware + router.Group().Route(func(auth *routegroup.Bundle) { + auth.Use(s.middleware.Auth()) + auth.HandleFunc("GET /update", s.pwdUpdateHandler) + auth.With(csrfMiddleware).HandleFunc("PUT /update", s.pwdUpdateHandler) + }) + + // add admin routes + router.Mount("/admin").Route(func(admin *routegroup.Bundle) { + admin.Use(s.middleware.Auth("admin")) + admin.Use(s.middleware.AdminOnly) + admin.HandleFunc("GET /", s.admin.renderHandler) + admin.With(csrfMiddleware).Route(func(csrf *routegroup.Bundle) { + csrf.HandleFunc("DELETE /sessions", s.admin.deleteSessionsHandler) + csrf.HandleFunc("POST /user", s.admin.addUserHandler) + csrf.HandleFunc("DELETE /user", s.admin.deleteUserHandler) + }) + }) + + router.HandleFunc("GET /static/*", s.fileServerHandlerFunc()) // serve static files + return router +} + +// fileServerHandlerFunc returns http.HandlerFunc that serves static files from the "assets/static" directory. +// prefix is set by the rootURL option. +func (s *Service) fileServerHandlerFunc() http.HandlerFunc { + staticFS, err := fs.Sub(assets, "assets/static") // error is always nil + if err != nil { + panic(err) // should never happen we load from embedded FS + } + return func(w http.ResponseWriter, r *http.Request) { + webFS := http.StripPrefix(s.rootURL+"/static/", http.FileServer(http.FS(staticFS))) + webFS.ServeHTTP(w, r) + } +} +``` + +## Contributing + +Contributions to `routegroup` are welcome! Please submit a pull request or open an issue for any bugs or feature requests. + +## License + +`routegroup` is available under the MIT license. See the [LICENSE](https://github.com/go-pkgz/routegroup/blob/master/LICENSE) file for more info. diff --git a/backend/vendor/github.com/go-pkgz/routegroup/group.go b/backend/vendor/github.com/go-pkgz/routegroup/group.go new file mode 100644 index 00000000..484ac6f2 --- /dev/null +++ b/backend/vendor/github.com/go-pkgz/routegroup/group.go @@ -0,0 +1,188 @@ +// Package routegroup provides a way to group routes and applies middleware to them. +// Works with the standard library's http.ServeMux. +package routegroup + +import ( + "net/http" + "regexp" + "strings" + "sync" +) + +// Bundle represents a group of routes with associated middleware. +type Bundle struct { + mux *http.ServeMux // the underlying mux to register the routes to + basePath string // base path for the group + middlewares []func(http.Handler) http.Handler // middlewares stack + rootRegistered struct { + once sync.Once // used to register a not found handler for the root path if no / route is registered + disableRootNotFoundHandler bool // if true, the not found handler for the root path is not registered automatically + notFound http.HandlerFunc + } +} + +// New creates a new Group. +func New(mux *http.ServeMux) *Bundle { + return &Bundle{mux: mux} +} + +// Mount creates a new group with a specified base path. +func Mount(mux *http.ServeMux, basePath string) *Bundle { + return &Bundle{mux: mux, basePath: basePath} +} + +// ServeHTTP implements the http.Handler interface +func (b *Bundle) ServeHTTP(w http.ResponseWriter, r *http.Request) { + b.rootRegistered.once.Do(func() { + if !b.rootRegistered.disableRootNotFoundHandler { + // register a not found handler for the root path unless it's disabled + // this is needed to be able to use middleware on all routes, for example logging + notFoundHandler := http.NotFoundHandler() + if b.rootRegistered.notFound != nil { + notFoundHandler = b.rootRegistered.notFound + } + b.mux.HandleFunc("/", b.wrapMiddleware(notFoundHandler).ServeHTTP) + } + }) + b.mux.ServeHTTP(w, r) +} + +// Group creates a new group with the same middleware stack as the original on top of the existing bundle. +func (b *Bundle) Group() *Bundle { + return b.clone() // copy the middlewares to avoid modifying the original +} + +// Mount creates a new group with a specified base path on top of the existing bundle. +func (b *Bundle) Mount(basePath string) *Bundle { + g := b.clone() // copy the middlewares to avoid modifying the original + g.basePath += basePath + return g +} + +// Use adds middleware(s) to the Group. +func (b *Bundle) Use(middleware func(http.Handler) http.Handler, more ...func(http.Handler) http.Handler) { + b.middlewares = append(b.middlewares, middleware) + b.middlewares = append(b.middlewares, more...) +} + +// With adds new middleware(s) to the Group and returns a new Group with the updated middleware stack. +// The With method is similar to Use, but instead of modifying the current Group, +// it returns a new Group instance with the added middleware(s). +// This allows for creating chain of middleware without affecting the original Group. +func (b *Bundle) With(middleware func(http.Handler) http.Handler, more ...func(http.Handler) http.Handler) *Bundle { + newMiddlewares := make([]func(http.Handler) http.Handler, len(b.middlewares), len(b.middlewares)+len(more)+1) + copy(newMiddlewares, b.middlewares) + newMiddlewares = append(newMiddlewares, middleware) + newMiddlewares = append(newMiddlewares, more...) + return &Bundle{mux: b.mux, basePath: b.basePath, middlewares: newMiddlewares} +} + +// Handle adds a new route to the Group's mux, applying all middlewares to the handler. +func (b *Bundle) Handle(pattern string, handler http.Handler) { + // for file server paths (ending with /), preserve the pattern and disable root not-found handler + if strings.HasSuffix(pattern, "/") { + if pattern == "/" { + b.rootRegistered.disableRootNotFoundHandler = true + } + fullPath := b.basePath + pattern + b.mux.Handle(fullPath, b.wrapMiddleware(handler)) + return + } + b.register(pattern, handler.ServeHTTP) +} + +// HandleFiles is a helper to serve static files from a directory +func (b *Bundle) HandleFiles(pattern string, root http.FileSystem) { + // normalize pattern to always have trailing slash + if !strings.HasSuffix(pattern, "/") { + pattern += "/" + } + + // build the full path for registration + fullPath := b.basePath + pattern + + if pattern == "/" && b.basePath == "" { + // root case - serve directly without stripping + b.rootRegistered.disableRootNotFoundHandler = true + b.mux.Handle("/", b.wrapMiddleware(http.FileServer(root))) + return + } + + // for both mounted groups and prefixed paths, strip the fullPath + handler := http.StripPrefix(strings.TrimSuffix(fullPath, "/"), http.FileServer(root)) + b.mux.Handle(fullPath, b.wrapMiddleware(handler)) +} + +// HandleFunc registers the handler function for the given pattern to the Group's mux. +// The handler is wrapped with the Group's middlewares. +func (b *Bundle) HandleFunc(pattern string, handler http.HandlerFunc) { + b.register(pattern, handler) +} + +// Handler returns the handler and the pattern that matches the request. +// It always returns a non-nil handler, see http.ServeMux.Handler documentation for details. +func (b *Bundle) Handler(r *http.Request) (h http.Handler, pattern string) { + return b.mux.Handler(r) +} + +// DisableNotFoundHandler disables the automatic registration of a not found handler for the root path. +func (b *Bundle) DisableNotFoundHandler() { b.rootRegistered.disableRootNotFoundHandler = true } + +// NotFoundHandler sets a custom handler for the root path if no / route is registered. +func (b *Bundle) NotFoundHandler(handler http.HandlerFunc) { + b.rootRegistered.notFound = handler +} + +// Matches non-space characters, spaces, then anything, i.e. "GET /path/to/resource" +var reGo122 = regexp.MustCompile(`^(\S+)\s+(.+)$`) + +func (b *Bundle) register(pattern string, handler http.HandlerFunc) { + matches := reGo122.FindStringSubmatch(pattern) + var path, method string + if len(matches) > 2 { // path in the form "GET /path/to/resource" + method = matches[1] + path = matches[2] + pattern = method + " " + b.basePath + path + } else { // path is just "/path/to/resource" + path = pattern + pattern = b.basePath + pattern + // method is not set intentionally here, the request pattern had no method part + } + + // if the pattern is the root path on / change it to /{$} + // this is needed to be able to keep / as a catch-all route and apply middleware to it. + // at the same time, it keeps handling the root request + if pattern == "/" || path == "/" { + if method != "" { // preserve the method part if it was set + pattern = method + " " + b.basePath + "/{$}" + } else { + pattern = b.basePath + "/{$}" // no method part, just the path + } + } + b.mux.HandleFunc(pattern, b.wrapMiddleware(handler).ServeHTTP) +} + +// Route allows for configuring the Group inside the configureFn function. +func (b *Bundle) Route(configureFn func(*Bundle)) { configureFn(b) } + +// wrapMiddleware applies the registered middlewares to a handler. +func (b *Bundle) wrapMiddleware(handler http.Handler) http.Handler { + for i := len(b.middlewares) - 1; i >= 0; i-- { + handler = b.middlewares[i](handler) + } + return handler +} + +func (b *Bundle) clone() *Bundle { + middlewares := make([]func(http.Handler) http.Handler, len(b.middlewares)) + copy(middlewares, b.middlewares) + return &Bundle{mux: b.mux, basePath: b.basePath, middlewares: middlewares} +} + +// Wrap directly wraps the handler with the provided middleware(s). +func Wrap(handler http.Handler, mw1 func(http.Handler) http.Handler, mws ...func(http.Handler) http.Handler) http.Handler { + for i := len(mws) - 1; i >= 0; i-- { + handler = mws[i](handler) + } + return mw1(handler) // apply the first middleware +} diff --git a/backend/vendor/modules.txt b/backend/vendor/modules.txt index 945bf924..a8d2178e 100644 --- a/backend/vendor/modules.txt +++ b/backend/vendor/modules.txt @@ -1,35 +1,12 @@ # github.com/PuerkitoBio/goquery v1.10.2 ## explicit; go 1.23 github.com/PuerkitoBio/goquery -# github.com/ajg/form v1.5.1 -## explicit -github.com/ajg/form # github.com/andybalholm/cascadia v1.3.3 ## explicit; go 1.16 github.com/andybalholm/cascadia # github.com/davecgh/go-spew v1.1.1 ## explicit github.com/davecgh/go-spew/spew -# github.com/didip/tollbooth/v7 v7.0.2 -## explicit; go 1.19 -github.com/didip/tollbooth/v7 -github.com/didip/tollbooth/v7/errors -github.com/didip/tollbooth/v7/internal/time/rate -github.com/didip/tollbooth/v7/libstring -github.com/didip/tollbooth/v7/limiter -# github.com/didip/tollbooth_chi v0.0.0-20220719025231-d662a7f6928f -## explicit; go 1.14 -github.com/didip/tollbooth_chi -# github.com/go-chi/chi/v5 v5.2.1 -## explicit; go 1.20 -github.com/go-chi/chi/v5 -github.com/go-chi/chi/v5/middleware -# github.com/go-chi/render v1.0.3 -## explicit; go 1.16 -github.com/go-chi/render -# github.com/go-pkgz/expirable-cache/v3 v3.0.0 -## explicit; go 1.20 -github.com/go-pkgz/expirable-cache/v3 # github.com/go-pkgz/lgr v0.12.0 ## explicit; go 1.21 github.com/go-pkgz/lgr @@ -38,6 +15,9 @@ github.com/go-pkgz/lgr github.com/go-pkgz/rest github.com/go-pkgz/rest/logger github.com/go-pkgz/rest/realip +# github.com/go-pkgz/routegroup v1.3.1 +## explicit; go 1.22 +github.com/go-pkgz/routegroup # github.com/golang/snappy v0.0.4 ## explicit github.com/golang/snappy