mirror of
https://github.com/adnanh/webhook.git
synced 2025-05-12 08:34:43 +00:00
Update go-chi dependency to v5
This commit is contained in:
parent
dbc6565c35
commit
1d00387284
55 changed files with 2126 additions and 966 deletions
3
go.mod
3
go.mod
|
@ -7,11 +7,10 @@ require (
|
||||||
github.com/dustin/go-humanize v1.0.0
|
github.com/dustin/go-humanize v1.0.0
|
||||||
github.com/fsnotify/fsnotify v1.4.7 // indirect
|
github.com/fsnotify/fsnotify v1.4.7 // indirect
|
||||||
github.com/ghodss/yaml v1.0.0
|
github.com/ghodss/yaml v1.0.0
|
||||||
github.com/go-chi/chi v4.0.2+incompatible
|
github.com/go-chi/chi/v5 v5.0.12
|
||||||
github.com/gofrs/uuid v3.2.0+incompatible
|
github.com/gofrs/uuid v3.2.0+incompatible
|
||||||
github.com/gorilla/mux v1.7.3
|
github.com/gorilla/mux v1.7.3
|
||||||
github.com/kr/pretty v0.1.0 // indirect
|
github.com/kr/pretty v0.1.0 // indirect
|
||||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 // indirect
|
|
||||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8
|
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
|
||||||
gopkg.in/fsnotify.v1 v1.4.2
|
gopkg.in/fsnotify.v1 v1.4.2
|
||||||
|
|
11
go.sum
11
go.sum
|
@ -6,8 +6,8 @@ github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
||||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||||
github.com/go-chi/chi v4.0.2+incompatible h1:maB6vn6FqCxrpz4FqWdh4+lwpyZIQS7YEAUcHlgXVRs=
|
github.com/go-chi/chi/v5 v5.0.12 h1:9euLV5sTrTNTRUU9POmDUvfxyj6LAABLUcEWO+JJb4s=
|
||||||
github.com/go-chi/chi v4.0.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ=
|
github.com/go-chi/chi/v5 v5.0.12/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||||
github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE=
|
github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE=
|
||||||
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||||
github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw=
|
github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw=
|
||||||
|
@ -17,15 +17,8 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
|
||||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 h1:efeOvDhwQ29Dj3SdAV/MJf8oukgn+8D8WgaCaRMchF8=
|
|
||||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
|
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8 h1:JA8d3MPx/IToSyXZG/RhwYEtfrKO1Fxrqe8KrkiLXKM=
|
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8 h1:JA8d3MPx/IToSyXZG/RhwYEtfrKO1Fxrqe8KrkiLXKM=
|
||||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/fsnotify.v1 v1.4.2 h1:AwZiD/bIUttYJ+n/k1UwlSUsM+VSE6id7UAnSKqQ+Tc=
|
gopkg.in/fsnotify.v1 v1.4.2 h1:AwZiD/bIUttYJ+n/k1UwlSUsM+VSE6id7UAnSKqQ+Tc=
|
||||||
|
|
|
@ -8,7 +8,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/dustin/go-humanize"
|
"github.com/dustin/go-humanize"
|
||||||
"github.com/go-chi/chi/middleware"
|
"github.com/go-chi/chi/v5/middleware"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Logger is a middleware that logs useful data about each HTTP request.
|
// Logger is a middleware that logs useful data about each HTTP request.
|
||||||
|
@ -39,7 +39,7 @@ type LogEntry struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write constructs and writes the final log entry.
|
// Write constructs and writes the final log entry.
|
||||||
func (l *LogEntry) Write(status, totalBytes int, elapsed time.Duration) {
|
func (l *LogEntry) Write(status, totalBytes int, header http.Header, elapsed time.Duration, extra interface{}) {
|
||||||
rid := GetReqID(l.req.Context())
|
rid := GetReqID(l.req.Context())
|
||||||
if rid != "" {
|
if rid != "" {
|
||||||
fmt.Fprintf(l.buf, "[%s] ", rid)
|
fmt.Fprintf(l.buf, "[%s] ", rid)
|
||||||
|
|
18
vendor/github.com/go-chi/chi/.travis.yml
generated
vendored
18
vendor/github.com/go-chi/chi/.travis.yml
generated
vendored
|
@ -1,18 +0,0 @@
|
||||||
language: go
|
|
||||||
|
|
||||||
go:
|
|
||||||
- 1.10.x
|
|
||||||
- 1.11.x
|
|
||||||
- 1.12.x
|
|
||||||
|
|
||||||
script:
|
|
||||||
- go get -d -t ./...
|
|
||||||
- go vet ./...
|
|
||||||
- go test ./...
|
|
||||||
- >
|
|
||||||
go_version=$(go version);
|
|
||||||
if [ ${go_version:13:4} = "1.12" ]; then
|
|
||||||
go get -u golang.org/x/tools/cmd/goimports;
|
|
||||||
goimports -d -e ./ | grep '.*' && { echo; echo "Aborting due to non-empty goimports output."; exit 1; } || :;
|
|
||||||
fi
|
|
||||||
|
|
139
vendor/github.com/go-chi/chi/CHANGELOG.md
generated
vendored
139
vendor/github.com/go-chi/chi/CHANGELOG.md
generated
vendored
|
@ -1,139 +0,0 @@
|
||||||
# Changelog
|
|
||||||
|
|
||||||
## 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 targetting 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 middlwares.
|
|
||||||
- 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")`
|
|
275
vendor/github.com/go-chi/chi/middleware/compress.go
generated
vendored
275
vendor/github.com/go-chi/chi/middleware/compress.go
generated
vendored
|
@ -1,275 +0,0 @@
|
||||||
package middleware
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"compress/flate"
|
|
||||||
"compress/gzip"
|
|
||||||
"errors"
|
|
||||||
"io"
|
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
var encoders = map[string]EncoderFunc{}
|
|
||||||
|
|
||||||
var encodingPrecedence = []string{"br", "gzip", "deflate"}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
// TODO:
|
|
||||||
// lzma: Opera.
|
|
||||||
// sdch: Chrome, Android. Gzip output + dictionary header.
|
|
||||||
// br: Brotli, see https://github.com/go-chi/chi/pull/326
|
|
||||||
|
|
||||||
// TODO: Exception for old MSIE browsers that can't handle non-HTML?
|
|
||||||
// https://zoompf.com/blog/2012/02/lose-the-wait-http-compression
|
|
||||||
SetEncoder("gzip", encoderGzip)
|
|
||||||
|
|
||||||
// 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 gzip.
|
|
||||||
SetEncoder("deflate", encoderDeflate)
|
|
||||||
|
|
||||||
// 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.
|
|
||||||
}
|
|
||||||
|
|
||||||
// An EncoderFunc is a function that wraps the provided ResponseWriter with a
|
|
||||||
// streaming compression algorithm and returns it.
|
|
||||||
//
|
|
||||||
// In case of failure, the function should return nil.
|
|
||||||
type EncoderFunc func(w http.ResponseWriter, level int) io.Writer
|
|
||||||
|
|
||||||
// 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 algortithm:
|
|
||||||
//
|
|
||||||
// import brotli_enc "gopkg.in/kothar/brotli-go.v0/enc"
|
|
||||||
//
|
|
||||||
// middleware.SetEncoder("br", func(w http.ResponseWriter, level int) io.Writer {
|
|
||||||
// params := brotli_enc.NewBrotliParams()
|
|
||||||
// params.SetQuality(level)
|
|
||||||
// return brotli_enc.NewBrotliWriter(params, w)
|
|
||||||
// })
|
|
||||||
func 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")
|
|
||||||
}
|
|
||||||
encoders[encoding] = fn
|
|
||||||
|
|
||||||
var e string
|
|
||||||
for _, v := range encodingPrecedence {
|
|
||||||
if v == encoding {
|
|
||||||
e = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if e == "" {
|
|
||||||
encodingPrecedence = append([]string{e}, encodingPrecedence...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var defaultContentTypes = map[string]struct{}{
|
|
||||||
"text/html": {},
|
|
||||||
"text/css": {},
|
|
||||||
"text/plain": {},
|
|
||||||
"text/javascript": {},
|
|
||||||
"application/javascript": {},
|
|
||||||
"application/x-javascript": {},
|
|
||||||
"application/json": {},
|
|
||||||
"application/atom+xml": {},
|
|
||||||
"application/rss+xml": {},
|
|
||||||
"image/svg+xml": {},
|
|
||||||
}
|
|
||||||
|
|
||||||
// DefaultCompress is a middleware that compresses response
|
|
||||||
// body of predefined content types to a data format based
|
|
||||||
// on Accept-Encoding request header. It uses a default
|
|
||||||
// compression level.
|
|
||||||
func DefaultCompress(next http.Handler) http.Handler {
|
|
||||||
return Compress(flate.DefaultCompression)(next)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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.
|
|
||||||
func Compress(level int, types ...string) func(next http.Handler) http.Handler {
|
|
||||||
contentTypes := defaultContentTypes
|
|
||||||
if len(types) > 0 {
|
|
||||||
contentTypes = make(map[string]struct{}, len(types))
|
|
||||||
for _, t := range types {
|
|
||||||
contentTypes[t] = struct{}{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return func(next http.Handler) http.Handler {
|
|
||||||
fn := func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
encoder, encoding := selectEncoder(r.Header)
|
|
||||||
|
|
||||||
cw := &compressResponseWriter{
|
|
||||||
ResponseWriter: w,
|
|
||||||
w: w,
|
|
||||||
contentTypes: contentTypes,
|
|
||||||
encoder: encoder,
|
|
||||||
encoding: encoding,
|
|
||||||
level: level,
|
|
||||||
}
|
|
||||||
defer cw.Close()
|
|
||||||
|
|
||||||
next.ServeHTTP(cw, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
return http.HandlerFunc(fn)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func selectEncoder(h http.Header) (EncoderFunc, string) {
|
|
||||||
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 encodingPrecedence {
|
|
||||||
if fn, ok := encoders[name]; ok && matchAcceptEncoding(accepted, name) {
|
|
||||||
return fn, name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// No encoder found to match the accepted encoding
|
|
||||||
return nil, ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func matchAcceptEncoding(accepted []string, encoding string) bool {
|
|
||||||
for _, v := range accepted {
|
|
||||||
if strings.Index(v, encoding) >= 0 {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
type compressResponseWriter struct {
|
|
||||||
http.ResponseWriter
|
|
||||||
w io.Writer
|
|
||||||
encoder EncoderFunc
|
|
||||||
encoding string
|
|
||||||
contentTypes map[string]struct{}
|
|
||||||
level int
|
|
||||||
wroteHeader bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cw *compressResponseWriter) WriteHeader(code int) {
|
|
||||||
if cw.wroteHeader {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
cw.wroteHeader = true
|
|
||||||
defer cw.ResponseWriter.WriteHeader(code)
|
|
||||||
|
|
||||||
// Already compressed data?
|
|
||||||
if cw.Header().Get("Content-Encoding") != "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse the first part of the Content-Type response header.
|
|
||||||
contentType := ""
|
|
||||||
parts := strings.Split(cw.Header().Get("Content-Type"), ";")
|
|
||||||
if len(parts) > 0 {
|
|
||||||
contentType = parts[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Is the content type compressable?
|
|
||||||
if _, ok := cw.contentTypes[contentType]; !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if cw.encoder != nil && cw.encoding != "" {
|
|
||||||
if wr := cw.encoder(cw.ResponseWriter, cw.level); wr != nil {
|
|
||||||
cw.w = wr
|
|
||||||
cw.Header().Set("Content-Encoding", cw.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.w.Write(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cw *compressResponseWriter) Flush() {
|
|
||||||
if f, ok := cw.w.(http.Flusher); ok {
|
|
||||||
f.Flush()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cw *compressResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
|
||||||
if hj, ok := cw.w.(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.w.(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.w.(io.WriteCloser); ok {
|
|
||||||
return c.Close()
|
|
||||||
}
|
|
||||||
return errors.New("chi/middleware: io.WriteCloser is unavailable on the writer")
|
|
||||||
}
|
|
||||||
|
|
||||||
func encoderGzip(w http.ResponseWriter, level int) io.Writer {
|
|
||||||
gw, err := gzip.NewWriterLevel(w, level)
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return gw
|
|
||||||
}
|
|
||||||
|
|
||||||
func encoderDeflate(w http.ResponseWriter, level int) io.Writer {
|
|
||||||
dw, err := flate.NewWriter(w, level)
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return dw
|
|
||||||
}
|
|
55
vendor/github.com/go-chi/chi/middleware/profiler.go
generated
vendored
55
vendor/github.com/go-chi/chi/middleware/profiler.go
generated
vendored
|
@ -1,55 +0,0 @@
|
||||||
package middleware
|
|
||||||
|
|
||||||
import (
|
|
||||||
"expvar"
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"net/http/pprof"
|
|
||||||
|
|
||||||
"github.com/go-chi/chi"
|
|
||||||
)
|
|
||||||
|
|
||||||
// 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/", 301)
|
|
||||||
})
|
|
||||||
r.HandleFunc("/pprof", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
http.Redirect(w, r, r.RequestURI+"/", 301)
|
|
||||||
})
|
|
||||||
|
|
||||||
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.HandleFunc("/vars", expVars)
|
|
||||||
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
// Replicated from expvar.go as not public.
|
|
||||||
func expVars(w http.ResponseWriter, r *http.Request) {
|
|
||||||
first := true
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
|
||||||
fmt.Fprintf(w, "{\n")
|
|
||||||
expvar.Do(func(kv expvar.KeyValue) {
|
|
||||||
if !first {
|
|
||||||
fmt.Fprintf(w, ",\n")
|
|
||||||
}
|
|
||||||
first = false
|
|
||||||
fmt.Fprintf(w, "%q: %s", kv.Key, kv.Value)
|
|
||||||
})
|
|
||||||
fmt.Fprintf(w, "\n}\n")
|
|
||||||
}
|
|
39
vendor/github.com/go-chi/chi/middleware/recoverer.go
generated
vendored
39
vendor/github.com/go-chi/chi/middleware/recoverer.go
generated
vendored
|
@ -1,39 +0,0 @@
|
||||||
package middleware
|
|
||||||
|
|
||||||
// The original work was derived from Goji's middleware, source:
|
|
||||||
// https://github.com/zenazn/goji/tree/master/web/middleware
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
"runtime/debug"
|
|
||||||
)
|
|
||||||
|
|
||||||
// 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/pressly/lg middleware pkgs.
|
|
||||||
func Recoverer(next http.Handler) http.Handler {
|
|
||||||
fn := func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
defer func() {
|
|
||||||
if rvr := recover(); rvr != nil {
|
|
||||||
|
|
||||||
logEntry := GetLogEntry(r)
|
|
||||||
if logEntry != nil {
|
|
||||||
logEntry.Panic(rvr, debug.Stack())
|
|
||||||
} else {
|
|
||||||
fmt.Fprintf(os.Stderr, "Panic: %+v\n", rvr)
|
|
||||||
debug.PrintStack()
|
|
||||||
}
|
|
||||||
|
|
||||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
next.ServeHTTP(w, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
return http.HandlerFunc(fn)
|
|
||||||
}
|
|
101
vendor/github.com/go-chi/chi/middleware/throttle.go
generated
vendored
101
vendor/github.com/go-chi/chi/middleware/throttle.go
generated
vendored
|
@ -1,101 +0,0 @@
|
||||||
package middleware
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
"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
|
|
||||||
)
|
|
||||||
|
|
||||||
// Throttle is a middleware that limits number of currently processed requests
|
|
||||||
// at a time.
|
|
||||||
func Throttle(limit int) func(http.Handler) http.Handler {
|
|
||||||
return ThrottleBacklog(limit, 0, 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 int, backlogLimit int, backlogTimeout time.Duration) func(http.Handler) http.Handler {
|
|
||||||
if limit < 1 {
|
|
||||||
panic("chi/middleware: Throttle expects limit > 0")
|
|
||||||
}
|
|
||||||
|
|
||||||
if backlogLimit < 0 {
|
|
||||||
panic("chi/middleware: Throttle expects backlogLimit to be positive")
|
|
||||||
}
|
|
||||||
|
|
||||||
t := throttler{
|
|
||||||
tokens: make(chan token, limit),
|
|
||||||
backlogTokens: make(chan token, limit+backlogLimit),
|
|
||||||
backlogTimeout: backlogTimeout,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filling tokens.
|
|
||||||
for i := 0; i < limit+backlogLimit; i++ {
|
|
||||||
if i < limit {
|
|
||||||
t.tokens <- token{}
|
|
||||||
}
|
|
||||||
t.backlogTokens <- token{}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn := func(h http.Handler) http.Handler {
|
|
||||||
t.h = h
|
|
||||||
return &t
|
|
||||||
}
|
|
||||||
|
|
||||||
return 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 {
|
|
||||||
h http.Handler
|
|
||||||
tokens chan token
|
|
||||||
backlogTokens chan token
|
|
||||||
backlogTimeout time.Duration
|
|
||||||
}
|
|
||||||
|
|
||||||
// ServeHTTP is the primary throttler request handler
|
|
||||||
func (t *throttler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
||||||
ctx := r.Context()
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
http.Error(w, errContextCanceled, http.StatusServiceUnavailable)
|
|
||||||
return
|
|
||||||
case btok := <-t.backlogTokens:
|
|
||||||
timer := time.NewTimer(t.backlogTimeout)
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
t.backlogTokens <- btok
|
|
||||||
}()
|
|
||||||
|
|
||||||
select {
|
|
||||||
case <-timer.C:
|
|
||||||
http.Error(w, errTimedOut, http.StatusServiceUnavailable)
|
|
||||||
return
|
|
||||||
case <-ctx.Done():
|
|
||||||
http.Error(w, errContextCanceled, http.StatusServiceUnavailable)
|
|
||||||
return
|
|
||||||
case tok := <-t.tokens:
|
|
||||||
defer func() {
|
|
||||||
t.tokens <- tok
|
|
||||||
}()
|
|
||||||
t.h.ServeHTTP(w, r)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
default:
|
|
||||||
http.Error(w, errCapacityExceeded, http.StatusServiceUnavailable)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
0
vendor/github.com/go-chi/chi/.gitignore → vendor/github.com/go-chi/chi/v5/.gitignore
generated
vendored
0
vendor/github.com/go-chi/chi/.gitignore → vendor/github.com/go-chi/chi/v5/.gitignore
generated
vendored
341
vendor/github.com/go-chi/chi/v5/CHANGELOG.md
generated
vendored
Normal file
341
vendor/github.com/go-chi/chi/v5/CHANGELOG.md
generated
vendored
Normal file
|
@ -0,0 +1,341 @@
|
||||||
|
# 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")`
|
0
vendor/github.com/go-chi/chi/LICENSE → vendor/github.com/go-chi/chi/v5/LICENSE
generated
vendored
0
vendor/github.com/go-chi/chi/LICENSE → vendor/github.com/go-chi/chi/v5/LICENSE
generated
vendored
22
vendor/github.com/go-chi/chi/v5/Makefile
generated
vendored
Normal file
22
vendor/github.com/go-chi/chi/v5/Makefile
generated
vendored
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
.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
|
204
vendor/github.com/go-chi/chi/README.md → vendor/github.com/go-chi/chi/v5/README.md
generated
vendored
204
vendor/github.com/go-chi/chi/README.md → vendor/github.com/go-chi/chi/v5/README.md
generated
vendored
|
@ -1,7 +1,7 @@
|
||||||
# <img alt="chi" src="https://cdn.rawgit.com/go-chi/chi/master/_examples/chi.svg" width="220" />
|
# <img alt="chi" src="https://cdn.rawgit.com/go-chi/chi/master/_examples/chi.svg" width="220" />
|
||||||
|
|
||||||
|
|
||||||
[![GoDoc Widget]][GoDoc] [![Travis Widget]][Travis]
|
[![GoDoc Widget]][GoDoc]
|
||||||
|
|
||||||
`chi` is a lightweight, idiomatic and composable router for building Go HTTP services. It's
|
`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
|
especially good at helping you write large REST API services that are kept maintainable as your
|
||||||
|
@ -15,11 +15,12 @@ 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
|
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
|
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
|
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!
|
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
|
## Install
|
||||||
|
|
||||||
`go get -u github.com/go-chi/chi`
|
`go get -u github.com/go-chi/chi/v5`
|
||||||
|
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
@ -27,10 +28,11 @@ included some useful/optional subpackages: [middleware](/middleware), [render](h
|
||||||
* **Lightweight** - cloc'd in ~1000 LOC for the chi router
|
* **Lightweight** - cloc'd in ~1000 LOC for the chi router
|
||||||
* **Fast** - yes, see [benchmarks](#benchmarks)
|
* **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`
|
* **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 subrouter mounting
|
* **Designed for modular/composable APIs** - middlewares, inline middlewares, route groups and sub-router mounting
|
||||||
* **Context control** - built on new `context` package, providing value chaining, cancelations and timeouts
|
* **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))
|
* **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
|
* **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
|
* **No external dependencies** - plain ol' Go stdlib + net/http
|
||||||
|
|
||||||
|
|
||||||
|
@ -46,11 +48,14 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"github.com/go-chi/chi"
|
|
||||||
|
"github.com/go-chi/chi/v5"
|
||||||
|
"github.com/go-chi/chi/v5/middleware"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
r := chi.NewRouter()
|
r := chi.NewRouter()
|
||||||
|
r.Use(middleware.Logger)
|
||||||
r.Get("/", func(w http.ResponseWriter, r *http.Request) {
|
r.Get("/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Write([]byte("welcome"))
|
w.Write([]byte("welcome"))
|
||||||
})
|
})
|
||||||
|
@ -71,8 +76,8 @@ above, they will show you all the features of chi and serve as a good form of do
|
||||||
import (
|
import (
|
||||||
//...
|
//...
|
||||||
"context"
|
"context"
|
||||||
"github.com/go-chi/chi"
|
"github.com/go-chi/chi/v5"
|
||||||
"github.com/go-chi/chi/middleware"
|
"github.com/go-chi/chi/v5/middleware"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
@ -165,7 +170,7 @@ func AdminOnly(next http.Handler) http.Handler {
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
## Router design
|
## Router interface
|
||||||
|
|
||||||
chi's router is based on a kind of [Patricia Radix trie](https://en.wikipedia.org/wiki/Radix_tree).
|
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`.
|
The router is fully compatible with `net/http`.
|
||||||
|
@ -179,7 +184,7 @@ type Router interface {
|
||||||
http.Handler
|
http.Handler
|
||||||
Routes
|
Routes
|
||||||
|
|
||||||
// Use appends one of more middlewares onto the Router stack.
|
// Use appends one or more middlewares onto the Router stack.
|
||||||
Use(middlewares ...func(http.Handler) http.Handler)
|
Use(middlewares ...func(http.Handler) http.Handler)
|
||||||
|
|
||||||
// With adds inline middlewares for an endpoint handler.
|
// With adds inline middlewares for an endpoint handler.
|
||||||
|
@ -254,15 +259,24 @@ about them, which means the router and all the tooling is designed to be compati
|
||||||
friendly with any middleware in the community. This offers much better extensibility and reuse
|
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.
|
of packages and is at the heart of chi's purpose.
|
||||||
|
|
||||||
Here is an example of a standard net/http middleware handler using the new request context
|
Here is an example of a standard net/http middleware where we assign a context key `"user"`
|
||||||
available in Go. This middleware sets a hypothetical user identifier on the request
|
the value of `"123"`. This middleware sets a hypothetical user identifier on the request
|
||||||
context and calls the next handler in the chain.
|
context and calls the next handler in the chain.
|
||||||
|
|
||||||
```go
|
```go
|
||||||
// HTTP middleware setting a value on the request context
|
// HTTP middleware setting a value on the request context
|
||||||
func MyMiddleware(next http.Handler) http.Handler {
|
func MyMiddleware(next http.Handler) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
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")
|
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))
|
next.ServeHTTP(w, r.WithContext(ctx))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -278,7 +292,11 @@ the user sending an authenticated request, validated+set by a previous middlewar
|
||||||
```go
|
```go
|
||||||
// HTTP handler accessing data from the request context.
|
// HTTP handler accessing data from the request context.
|
||||||
func MyRequestHandler(w http.ResponseWriter, r *http.Request) {
|
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)
|
user := r.Context().Value("user").(string)
|
||||||
|
|
||||||
|
// respond to the client
|
||||||
w.Write([]byte(fmt.Sprintf("hi %s", user)))
|
w.Write([]byte(fmt.Sprintf("hi %s", user)))
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
@ -293,11 +311,15 @@ are able to access the same information.
|
||||||
```go
|
```go
|
||||||
// HTTP handler accessing the url routing parameters.
|
// HTTP handler accessing the url routing parameters.
|
||||||
func MyRequestHandler(w http.ResponseWriter, r *http.Request) {
|
func MyRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
userID := chi.URLParam(r, "userID") // from a route like /users/{userID}
|
// 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()
|
ctx := r.Context()
|
||||||
key := ctx.Value("key").(string)
|
key := ctx.Value("key").(string)
|
||||||
|
|
||||||
|
// respond to the client
|
||||||
w.Write([]byte(fmt.Sprintf("hi %v, %v", userID, key)))
|
w.Write([]byte(fmt.Sprintf("hi %v, %v", userID, key)))
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
@ -311,29 +333,74 @@ with `net/http` can be used with chi's mux.
|
||||||
|
|
||||||
### Core middlewares
|
### Core middlewares
|
||||||
|
|
||||||
-----------------------------------------------------------------------------------------------------------
|
----------------------------------------------------------------------------------------------------
|
||||||
| chi/middleware Handler | description |
|
| chi/middleware Handler | description |
|
||||||
|:----------------------|:---------------------------------------------------------------------------------
|
| :--------------------- | :---------------------------------------------------------------------- |
|
||||||
| AllowContentType | Explicit whitelist of accepted request Content-Types |
|
| [AllowContentEncoding] | Enforces a whitelist of request Content-Encoding headers |
|
||||||
| Compress | Gzip compression for clients that accept compressed responses |
|
| [AllowContentType] | Explicit whitelist of accepted request Content-Types |
|
||||||
| GetHead | Automatically route undefined HEAD requests to GET handlers |
|
| [BasicAuth] | Basic HTTP authentication |
|
||||||
| Heartbeat | Monitoring endpoint to check the servers pulse |
|
| [Compress] | Gzip compression for clients that accept compressed responses |
|
||||||
| Logger | Logs the start and end of each request with the elapsed processing time |
|
| [ContentCharset] | Ensure charset for Content-Type request headers |
|
||||||
| NoCache | Sets response headers to prevent clients from caching |
|
| [CleanPath] | Clean double slashes from request path |
|
||||||
| Profiler | Easily attach net/http/pprof to your routers |
|
| [GetHead] | Automatically route undefined HEAD requests to GET handlers |
|
||||||
| RealIP | Sets a http.Request's RemoteAddr to either X-Forwarded-For or X-Real-IP |
|
| [Heartbeat] | Monitoring endpoint to check the servers pulse |
|
||||||
| Recoverer | Gracefully absorb panics and prints the stack trace |
|
| [Logger] | Logs the start and end of each request with the elapsed processing time |
|
||||||
| RequestID | Injects a request ID into the context of each request |
|
| [NoCache] | Sets response headers to prevent clients from caching |
|
||||||
| RedirectSlashes | Redirect slashes on routing paths |
|
| [Profiler] | Easily attach net/http/pprof to your routers |
|
||||||
| SetHeader | Short-hand middleware to set a response header key/value |
|
| [RealIP] | Sets a http.Request's RemoteAddr to either X-Real-IP or X-Forwarded-For |
|
||||||
| StripSlashes | Strip slashes on routing paths |
|
| [Recoverer] | Gracefully absorb panics and prints the stack trace |
|
||||||
| Throttle | Puts a ceiling on the number of concurrent requests |
|
| [RequestID] | Injects a request ID into the context of each request |
|
||||||
| Timeout | Signals to the request context when the timeout deadline is reached |
|
| [RedirectSlashes] | Redirect slashes on routing paths |
|
||||||
| URLFormat | Parse extension from url and put it on request context |
|
| [RouteHeaders] | Route handling for request headers |
|
||||||
| WithValue | Short-hand middleware to set a key/value on the request context |
|
| [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 |
|
||||||
|
----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
### Auxiliary middlewares & packages
|
[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.
|
Please see https://github.com/go-chi for additional packages.
|
||||||
|
|
||||||
|
@ -344,13 +411,13 @@ Please see https://github.com/go-chi for additional packages.
|
||||||
| [docgen](https://github.com/go-chi/docgen) | Print chi.Router routes at runtime |
|
| [docgen](https://github.com/go-chi/docgen) | Print chi.Router routes at runtime |
|
||||||
| [jwtauth](https://github.com/go-chi/jwtauth) | JWT authentication |
|
| [jwtauth](https://github.com/go-chi/jwtauth) | JWT authentication |
|
||||||
| [hostrouter](https://github.com/go-chi/hostrouter) | Domain/host based request routing |
|
| [hostrouter](https://github.com/go-chi/hostrouter) | Domain/host based request routing |
|
||||||
| [httpcoala](https://github.com/go-chi/httpcoala) | HTTP request coalescer |
|
| [httplog](https://github.com/go-chi/httplog) | Small but powerful structured HTTP request logging |
|
||||||
| [chi-authz](https://github.com/casbin/chi-authz) | Request ACL via https://github.com/hsluoyz/casbin |
|
| [httprate](https://github.com/go-chi/httprate) | HTTP request rate limiter |
|
||||||
| [phi](https://github.com/fate-lovely/phi) | Port chi to [fasthttp](https://github.com/valyala/fasthttp) |
|
| [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 |
|
||||||
--------------------------------------------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
please [submit a PR](./CONTRIBUTING.md) if you'd like to include a link to a chi-compatible middleware
|
|
||||||
|
|
||||||
|
|
||||||
## context?
|
## context?
|
||||||
|
|
||||||
|
@ -369,25 +436,25 @@ and..
|
||||||
|
|
||||||
The benchmark suite: https://github.com/pkieltyka/go-http-routing-benchmark
|
The benchmark suite: https://github.com/pkieltyka/go-http-routing-benchmark
|
||||||
|
|
||||||
Results as of Jan 9, 2019 with Go 1.11.4 on Linux X1 Carbon laptop
|
Results as of Nov 29, 2020 with Go 1.15.5 on Linux AMD 3950x
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
BenchmarkChi_Param 3000000 475 ns/op 432 B/op 3 allocs/op
|
BenchmarkChi_Param 3075895 384 ns/op 400 B/op 2 allocs/op
|
||||||
BenchmarkChi_Param5 2000000 696 ns/op 432 B/op 3 allocs/op
|
BenchmarkChi_Param5 2116603 566 ns/op 400 B/op 2 allocs/op
|
||||||
BenchmarkChi_Param20 1000000 1275 ns/op 432 B/op 3 allocs/op
|
BenchmarkChi_Param20 964117 1227 ns/op 400 B/op 2 allocs/op
|
||||||
BenchmarkChi_ParamWrite 3000000 505 ns/op 432 B/op 3 allocs/op
|
BenchmarkChi_ParamWrite 2863413 420 ns/op 400 B/op 2 allocs/op
|
||||||
BenchmarkChi_GithubStatic 3000000 508 ns/op 432 B/op 3 allocs/op
|
BenchmarkChi_GithubStatic 3045488 395 ns/op 400 B/op 2 allocs/op
|
||||||
BenchmarkChi_GithubParam 2000000 669 ns/op 432 B/op 3 allocs/op
|
BenchmarkChi_GithubParam 2204115 540 ns/op 400 B/op 2 allocs/op
|
||||||
BenchmarkChi_GithubAll 10000 134627 ns/op 87699 B/op 609 allocs/op
|
BenchmarkChi_GithubAll 10000 113811 ns/op 81203 B/op 406 allocs/op
|
||||||
BenchmarkChi_GPlusStatic 3000000 402 ns/op 432 B/op 3 allocs/op
|
BenchmarkChi_GPlusStatic 3337485 359 ns/op 400 B/op 2 allocs/op
|
||||||
BenchmarkChi_GPlusParam 3000000 500 ns/op 432 B/op 3 allocs/op
|
BenchmarkChi_GPlusParam 2825853 423 ns/op 400 B/op 2 allocs/op
|
||||||
BenchmarkChi_GPlus2Params 3000000 586 ns/op 432 B/op 3 allocs/op
|
BenchmarkChi_GPlus2Params 2471697 483 ns/op 400 B/op 2 allocs/op
|
||||||
BenchmarkChi_GPlusAll 200000 7237 ns/op 5616 B/op 39 allocs/op
|
BenchmarkChi_GPlusAll 194220 5950 ns/op 5200 B/op 26 allocs/op
|
||||||
BenchmarkChi_ParseStatic 3000000 408 ns/op 432 B/op 3 allocs/op
|
BenchmarkChi_ParseStatic 3365324 356 ns/op 400 B/op 2 allocs/op
|
||||||
BenchmarkChi_ParseParam 3000000 488 ns/op 432 B/op 3 allocs/op
|
BenchmarkChi_ParseParam 2976614 404 ns/op 400 B/op 2 allocs/op
|
||||||
BenchmarkChi_Parse2Params 3000000 551 ns/op 432 B/op 3 allocs/op
|
BenchmarkChi_Parse2Params 2638084 439 ns/op 400 B/op 2 allocs/op
|
||||||
BenchmarkChi_ParseAll 100000 13508 ns/op 11232 B/op 78 allocs/op
|
BenchmarkChi_ParseAll 109567 11295 ns/op 10400 B/op 52 allocs/op
|
||||||
BenchmarkChi_StaticAll 20000 81933 ns/op 67826 B/op 471 allocs/op
|
BenchmarkChi_StaticAll 16846 71308 ns/op 62802 B/op 314 allocs/op
|
||||||
```
|
```
|
||||||
|
|
||||||
Comparison with other routers: https://gist.github.com/pkieltyka/123032f12052520aaccab752bd3e78cc
|
Comparison with other routers: https://gist.github.com/pkieltyka/123032f12052520aaccab752bd3e78cc
|
||||||
|
@ -412,18 +479,15 @@ We'll be more than happy to see [your contributions](./CONTRIBUTING.md)!
|
||||||
## Beyond REST
|
## Beyond REST
|
||||||
|
|
||||||
chi is just a http router that lets you decompose request handling into many smaller layers.
|
chi is just a http router that lets you decompose request handling into many smaller layers.
|
||||||
Many companies including Pressly.com (of course) use chi to write REST services for their public
|
Many companies use chi to write REST services for their public APIs. But, REST is just a convention
|
||||||
APIs. But, REST is just a convention for managing state via HTTP, and there's a lot of other pieces
|
for managing state via HTTP, and there's a lot of other pieces required to write a complete client-server
|
||||||
required to write a complete client-server system or network of microservices.
|
system or network of microservices.
|
||||||
|
|
||||||
Looking ahead beyond REST, I also recommend some newer works in the field coming from
|
Looking beyond REST, I also recommend some newer works in the field:
|
||||||
[gRPC](https://github.com/grpc/grpc-go), [NATS](https://nats.io), [go-kit](https://github.com/go-kit/kit)
|
* [webrpc](https://github.com/webrpc/webrpc) - Web-focused RPC client+server framework with code-gen
|
||||||
and even [graphql](https://github.com/graphql-go/graphql). They're all pretty cool with their
|
* [gRPC](https://github.com/grpc/grpc-go) - Google's RPC framework via protobufs
|
||||||
own unique approaches and benefits. Specifically, I'd look at gRPC since it makes client-server
|
* [graphql](https://github.com/99designs/gqlgen) - Declarative query language
|
||||||
communication feel like a single program on a single computer, no need to hand-write a client library
|
* [NATS](https://nats.io) - lightweight pub-sub
|
||||||
and the request/response payloads are typed contracts. NATS is pretty amazing too as a super
|
|
||||||
fast and lightweight pub-sub transport that can speak protobufs, with nice service discovery -
|
|
||||||
an excellent combination with gRPC.
|
|
||||||
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
@ -432,7 +496,7 @@ Copyright (c) 2015-present [Peter Kieltyka](https://github.com/pkieltyka)
|
||||||
|
|
||||||
Licensed under [MIT License](./LICENSE)
|
Licensed under [MIT License](./LICENSE)
|
||||||
|
|
||||||
[GoDoc]: https://godoc.org/github.com/go-chi/chi
|
[GoDoc]: https://pkg.go.dev/github.com/go-chi/chi/v5
|
||||||
[GoDoc Widget]: https://godoc.org/github.com/go-chi/chi?status.svg
|
[GoDoc Widget]: https://godoc.org/github.com/go-chi/chi?status.svg
|
||||||
[Travis]: https://travis-ci.org/go-chi/chi
|
[Travis]: https://travis-ci.org/go-chi/chi
|
||||||
[Travis Widget]: https://travis-ci.org/go-chi/chi.svg?branch=master
|
[Travis Widget]: https://travis-ci.org/go-chi/chi.svg?branch=master
|
5
vendor/github.com/go-chi/chi/v5/SECURITY.md
generated
vendored
Normal file
5
vendor/github.com/go-chi/chi/v5/SECURITY.md
generated
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
# 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.
|
6
vendor/github.com/go-chi/chi/chain.go → vendor/github.com/go-chi/chi/v5/chain.go
generated
vendored
6
vendor/github.com/go-chi/chi/chain.go → vendor/github.com/go-chi/chi/v5/chain.go
generated
vendored
|
@ -10,21 +10,21 @@ func Chain(middlewares ...func(http.Handler) http.Handler) Middlewares {
|
||||||
// Handler builds and returns a http.Handler from the chain of middlewares,
|
// Handler builds and returns a http.Handler from the chain of middlewares,
|
||||||
// with `h http.Handler` as the final handler.
|
// with `h http.Handler` as the final handler.
|
||||||
func (mws Middlewares) Handler(h http.Handler) http.Handler {
|
func (mws Middlewares) Handler(h http.Handler) http.Handler {
|
||||||
return &ChainHandler{mws, h, chain(mws, h)}
|
return &ChainHandler{h, chain(mws, h), mws}
|
||||||
}
|
}
|
||||||
|
|
||||||
// HandlerFunc builds and returns a http.Handler from the chain of middlewares,
|
// HandlerFunc builds and returns a http.Handler from the chain of middlewares,
|
||||||
// with `h http.Handler` as the final handler.
|
// with `h http.Handler` as the final handler.
|
||||||
func (mws Middlewares) HandlerFunc(h http.HandlerFunc) http.Handler {
|
func (mws Middlewares) HandlerFunc(h http.HandlerFunc) http.Handler {
|
||||||
return &ChainHandler{mws, h, chain(mws, h)}
|
return &ChainHandler{h, chain(mws, h), mws}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ChainHandler is a http.Handler with support for handler composition and
|
// ChainHandler is a http.Handler with support for handler composition and
|
||||||
// execution.
|
// execution.
|
||||||
type ChainHandler struct {
|
type ChainHandler struct {
|
||||||
Middlewares Middlewares
|
|
||||||
Endpoint http.Handler
|
Endpoint http.Handler
|
||||||
chain http.Handler
|
chain http.Handler
|
||||||
|
Middlewares Middlewares
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ChainHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
func (c *ChainHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
46
vendor/github.com/go-chi/chi/chi.go → vendor/github.com/go-chi/chi/v5/chi.go
generated
vendored
46
vendor/github.com/go-chi/chi/chi.go → vendor/github.com/go-chi/chi/v5/chi.go
generated
vendored
|
@ -1,29 +1,29 @@
|
||||||
//
|
|
||||||
// Package chi is a small, idiomatic and composable router for building HTTP services.
|
// Package chi is a small, idiomatic and composable router for building HTTP services.
|
||||||
//
|
//
|
||||||
// chi requires Go 1.7 or newer.
|
// chi requires Go 1.14 or newer.
|
||||||
//
|
//
|
||||||
// Example:
|
// Example:
|
||||||
// package main
|
|
||||||
//
|
//
|
||||||
// import (
|
// package main
|
||||||
// "net/http"
|
|
||||||
//
|
//
|
||||||
// "github.com/go-chi/chi"
|
// import (
|
||||||
// "github.com/go-chi/chi/middleware"
|
// "net/http"
|
||||||
// )
|
|
||||||
//
|
//
|
||||||
// func main() {
|
// "github.com/go-chi/chi/v5"
|
||||||
// r := chi.NewRouter()
|
// "github.com/go-chi/chi/v5/middleware"
|
||||||
// r.Use(middleware.Logger)
|
// )
|
||||||
// r.Use(middleware.Recoverer)
|
|
||||||
//
|
//
|
||||||
// r.Get("/", func(w http.ResponseWriter, r *http.Request) {
|
// func main() {
|
||||||
// w.Write([]byte("root."))
|
// r := chi.NewRouter()
|
||||||
// })
|
// r.Use(middleware.Logger)
|
||||||
|
// r.Use(middleware.Recoverer)
|
||||||
//
|
//
|
||||||
// http.ListenAndServe(":3333", r)
|
// 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.
|
// See github.com/go-chi/chi/_examples/ for more in-depth examples.
|
||||||
//
|
//
|
||||||
|
@ -47,12 +47,12 @@
|
||||||
// placeholder which will match / characters.
|
// placeholder which will match / characters.
|
||||||
//
|
//
|
||||||
// Examples:
|
// 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/*/index" also matches "/page/intro/latest"
|
|
||||||
// "/date/{yyyy:\\d\\d\\d\\d}/{mm:\\d\\d}/{dd:\\d\\d}" matches "/date/2017/04/01"
|
|
||||||
//
|
//
|
||||||
|
// "/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}/index" also matches "/page/intro/latest"
|
||||||
|
// "/date/{yyyy:\\d\\d\\d\\d}/{mm:\\d\\d}/{dd:\\d\\d}" matches "/date/2017/04/01"
|
||||||
package chi
|
package chi
|
||||||
|
|
||||||
import "net/http"
|
import "net/http"
|
||||||
|
@ -68,7 +68,7 @@ type Router interface {
|
||||||
http.Handler
|
http.Handler
|
||||||
Routes
|
Routes
|
||||||
|
|
||||||
// Use appends one of more middlewares onto the Router stack.
|
// Use appends one or more middlewares onto the Router stack.
|
||||||
Use(middlewares ...func(http.Handler) http.Handler)
|
Use(middlewares ...func(http.Handler) http.Handler)
|
||||||
|
|
||||||
// With adds inline middlewares for an endpoint handler.
|
// With adds inline middlewares for an endpoint handler.
|
126
vendor/github.com/go-chi/chi/context.go → vendor/github.com/go-chi/chi/v5/context.go
generated
vendored
126
vendor/github.com/go-chi/chi/context.go → vendor/github.com/go-chi/chi/v5/context.go
generated
vendored
|
@ -2,11 +2,38 @@ package chi
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"net"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"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 (
|
var (
|
||||||
// RouteCtxKey is the context.Context key to store the request context.
|
// RouteCtxKey is the context.Context key to store the request context.
|
||||||
RouteCtxKey = &contextKey{"RouteContext"}
|
RouteCtxKey = &contextKey{"RouteContext"}
|
||||||
|
@ -18,37 +45,38 @@ var (
|
||||||
type Context struct {
|
type Context struct {
|
||||||
Routes Routes
|
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.
|
// Routing path/method override used during the route search.
|
||||||
// See Mux#routeHTTP method.
|
// See Mux#routeHTTP method.
|
||||||
RoutePath string
|
RoutePath string
|
||||||
RouteMethod string
|
RouteMethod 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
|
|
||||||
|
|
||||||
// URLParams are the stack of routeParams captured during the
|
// URLParams are the stack of routeParams captured during the
|
||||||
// routing lifecycle across a stack of sub-routers.
|
// routing lifecycle across a stack of sub-routers.
|
||||||
URLParams RouteParams
|
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
|
// The endpoint routing pattern that matched the request URI path
|
||||||
// or `RoutePath` of the current sub-router. This value will update
|
// or `RoutePath` of the current sub-router. This value will update
|
||||||
// during the lifecycle of a request passing through a stack of
|
// during the lifecycle of a request passing through a stack of
|
||||||
// sub-routers.
|
// sub-routers.
|
||||||
routePattern string
|
routePattern string
|
||||||
|
|
||||||
// Route parameters matched for the current sub-router. It is
|
// Routing pattern stack throughout the lifecycle of the request,
|
||||||
// intentionally unexported so it cant be tampered.
|
// across all connected routers. It is a record of all matching
|
||||||
routeParams RouteParams
|
// patterns across a stack of sub-routers.
|
||||||
|
RoutePatterns []string
|
||||||
|
|
||||||
// methodNotAllowed hint
|
// methodNotAllowed hint
|
||||||
methodNotAllowed bool
|
methodNotAllowed bool
|
||||||
}
|
methodsAllowed []methodTyp // allowed methods in case of a 405
|
||||||
|
|
||||||
// NewRouteContext returns a new routing Context object.
|
|
||||||
func NewRouteContext() *Context {
|
|
||||||
return &Context{}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset a routing context to its initial state.
|
// Reset a routing context to its initial state.
|
||||||
|
@ -64,6 +92,8 @@ func (x *Context) Reset() {
|
||||||
x.routeParams.Keys = x.routeParams.Keys[:0]
|
x.routeParams.Keys = x.routeParams.Keys[:0]
|
||||||
x.routeParams.Values = x.routeParams.Values[:0]
|
x.routeParams.Values = x.routeParams.Values[:0]
|
||||||
x.methodNotAllowed = false
|
x.methodNotAllowed = false
|
||||||
|
x.methodsAllowed = x.methodsAllowed[:0]
|
||||||
|
x.parentCtx = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// URLParam returns the corresponding URL parameter value from the request
|
// URLParam returns the corresponding URL parameter value from the request
|
||||||
|
@ -84,38 +114,30 @@ func (x *Context) URLParam(key string) string {
|
||||||
//
|
//
|
||||||
// For example,
|
// For example,
|
||||||
//
|
//
|
||||||
// func Instrument(next http.Handler) http.Handler {
|
// func Instrument(next http.Handler) http.Handler {
|
||||||
// return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
// return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
// next.ServeHTTP(w, r)
|
// next.ServeHTTP(w, r)
|
||||||
// routePattern := chi.RouteContext(r.Context()).RoutePattern()
|
// routePattern := chi.RouteContext(r.Context()).RoutePattern()
|
||||||
// measure(w, r, routePattern)
|
// measure(w, r, routePattern)
|
||||||
// })
|
// })
|
||||||
// }
|
// }
|
||||||
func (x *Context) RoutePattern() string {
|
func (x *Context) RoutePattern() string {
|
||||||
routePattern := strings.Join(x.RoutePatterns, "")
|
routePattern := strings.Join(x.RoutePatterns, "")
|
||||||
return strings.Replace(routePattern, "/*/", "/", -1)
|
routePattern = replaceWildcards(routePattern)
|
||||||
}
|
if routePattern != "/" {
|
||||||
|
routePattern = strings.TrimSuffix(routePattern, "//")
|
||||||
// RouteContext returns chi's routing Context object from a
|
routePattern = strings.TrimSuffix(routePattern, "/")
|
||||||
// http.Request Context.
|
|
||||||
func RouteContext(ctx context.Context) *Context {
|
|
||||||
return ctx.Value(RouteCtxKey).(*Context)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 ""
|
return routePattern
|
||||||
}
|
}
|
||||||
|
|
||||||
// URLParamFromCtx returns the url parameter from a http.Request Context.
|
// replaceWildcards takes a route pattern and recursively replaces all
|
||||||
func URLParamFromCtx(ctx context.Context, key string) string {
|
// occurrences of "/*/" to "/".
|
||||||
if rctx := RouteContext(ctx); rctx != nil {
|
func replaceWildcards(p string) string {
|
||||||
return rctx.URLParam(key)
|
if strings.Contains(p, "/*/") {
|
||||||
|
return replaceWildcards(strings.Replace(p, "/*/", "/", -1))
|
||||||
}
|
}
|
||||||
return ""
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
// RouteParams is a structure to track URL routing parameters efficiently.
|
// RouteParams is a structure to track URL routing parameters efficiently.
|
||||||
|
@ -125,28 +147,8 @@ type RouteParams struct {
|
||||||
|
|
||||||
// Add will append a URL parameter to the end of the route param
|
// Add will append a URL parameter to the end of the route param
|
||||||
func (s *RouteParams) Add(key, value string) {
|
func (s *RouteParams) Add(key, value string) {
|
||||||
(*s).Keys = append((*s).Keys, key)
|
s.Keys = append(s.Keys, key)
|
||||||
(*s).Values = append((*s).Values, value)
|
s.Values = append(s.Values, value)
|
||||||
}
|
|
||||||
|
|
||||||
// ServerBaseContext wraps an http.Handler to set the request context to the
|
|
||||||
// `baseCtx`.
|
|
||||||
func ServerBaseContext(baseCtx context.Context, h http.Handler) http.Handler {
|
|
||||||
fn := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
ctx := r.Context()
|
|
||||||
baseCtx := baseCtx
|
|
||||||
|
|
||||||
// Copy over default net/http server context keys
|
|
||||||
if v, ok := ctx.Value(http.ServerContextKey).(*http.Server); ok {
|
|
||||||
baseCtx = context.WithValue(baseCtx, http.ServerContextKey, v)
|
|
||||||
}
|
|
||||||
if v, ok := ctx.Value(http.LocalAddrContextKey).(net.Addr); ok {
|
|
||||||
baseCtx = context.WithValue(baseCtx, http.LocalAddrContextKey, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
h.ServeHTTP(w, r.WithContext(baseCtx))
|
|
||||||
})
|
|
||||||
return fn
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// contextKey is a value for use with context.WithValue. It's used as
|
// contextKey is a value for use with context.WithValue. It's used as
|
3
vendor/github.com/go-chi/chi/v5/go.mod
generated
vendored
Normal file
3
vendor/github.com/go-chi/chi/v5/go.mod
generated
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
module github.com/go-chi/chi/v5
|
||||||
|
|
||||||
|
go 1.14
|
33
vendor/github.com/go-chi/chi/v5/middleware/basic_auth.go
generated
vendored
Normal file
33
vendor/github.com/go-chi/chi/v5/middleware/basic_auth.go
generated
vendored
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
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)
|
||||||
|
}
|
28
vendor/github.com/go-chi/chi/v5/middleware/clean_path.go
generated
vendored
Normal file
28
vendor/github.com/go-chi/chi/v5/middleware/clean_path.go
generated
vendored
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
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)
|
||||||
|
})
|
||||||
|
}
|
398
vendor/github.com/go-chi/chi/v5/middleware/compress.go
generated
vendored
Normal file
398
vendor/github.com/go-chi/chi/v5/middleware/compress.go
generated
vendored
Normal file
|
@ -0,0 +1,398 @@
|
||||||
|
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
|
||||||
|
}
|
34
vendor/github.com/go-chi/chi/v5/middleware/content_encoding.go
generated
vendored
Normal file
34
vendor/github.com/go-chi/chi/v5/middleware/content_encoding.go
generated
vendored
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,9 +19,9 @@ func SetHeader(key, value string) func(next http.Handler) http.Handler {
|
||||||
// AllowContentType enforces a whitelist of request Content-Types otherwise responds
|
// AllowContentType enforces a whitelist of request Content-Types otherwise responds
|
||||||
// with a 415 Unsupported Media Type status.
|
// with a 415 Unsupported Media Type status.
|
||||||
func AllowContentType(contentTypes ...string) func(next http.Handler) http.Handler {
|
func AllowContentType(contentTypes ...string) func(next http.Handler) http.Handler {
|
||||||
cT := []string{}
|
allowedContentTypes := make(map[string]struct{}, len(contentTypes))
|
||||||
for _, t := range contentTypes {
|
for _, ctype := range contentTypes {
|
||||||
cT = append(cT, strings.ToLower(t))
|
allowedContentTypes[strings.TrimSpace(strings.ToLower(ctype))] = struct{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
return func(next http.Handler) http.Handler {
|
return func(next http.Handler) http.Handler {
|
||||||
|
@ -37,11 +37,9 @@ func AllowContentType(contentTypes ...string) func(next http.Handler) http.Handl
|
||||||
s = s[0:i]
|
s = s[0:i]
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, t := range cT {
|
if _, ok := allowedContentTypes[s]; ok {
|
||||||
if t == s {
|
next.ServeHTTP(w, r)
|
||||||
next.ServeHTTP(w, r)
|
return
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
w.WriteHeader(http.StatusUnsupportedMediaType)
|
w.WriteHeader(http.StatusUnsupportedMediaType)
|
|
@ -3,7 +3,7 @@ package middleware
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/go-chi/chi"
|
"github.com/go-chi/chi/v5"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetHead automatically route undefined HEAD requests to GET handlers.
|
// GetHead automatically route undefined HEAD requests to GET handlers.
|
|
@ -12,7 +12,7 @@ import (
|
||||||
func Heartbeat(endpoint string) func(http.Handler) http.Handler {
|
func Heartbeat(endpoint string) func(http.Handler) http.Handler {
|
||||||
f := func(h http.Handler) http.Handler {
|
f := func(h http.Handler) http.Handler {
|
||||||
fn := func(w http.ResponseWriter, r *http.Request) {
|
fn := func(w http.ResponseWriter, r *http.Request) {
|
||||||
if r.Method == "GET" && strings.EqualFold(r.URL.Path, endpoint) {
|
if (r.Method == "GET" || r.Method == "HEAD") && strings.EqualFold(r.URL.Path, endpoint) {
|
||||||
w.Header().Set("Content-Type", "text/plain")
|
w.Header().Set("Content-Type", "text/plain")
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
w.Write([]byte("."))
|
w.Write([]byte("."))
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"runtime"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -16,7 +17,7 @@ var (
|
||||||
// DefaultLogger is called by the Logger middleware handler to log each request.
|
// 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
|
// Its made a package-level variable so that it can be reconfigured for custom
|
||||||
// logging configurations.
|
// logging configurations.
|
||||||
DefaultLogger = RequestLogger(&DefaultLogFormatter{Logger: log.New(os.Stdout, "", log.LstdFlags), NoColor: false})
|
DefaultLogger func(next http.Handler) http.Handler
|
||||||
)
|
)
|
||||||
|
|
||||||
// Logger is a middleware that logs the start and end of each request, along
|
// Logger is a middleware that logs the start and end of each request, along
|
||||||
|
@ -25,8 +26,16 @@ var (
|
||||||
// print in color, otherwise it will print in black and white. Logger prints a
|
// print in color, otherwise it will print in black and white. Logger prints a
|
||||||
// request ID if one is provided.
|
// request ID if one is provided.
|
||||||
//
|
//
|
||||||
// Alternatively, look at https://github.com/pressly/lg and the `lg.RequestLogger`
|
// Alternatively, look at https://github.com/goware/httplog for a more in-depth
|
||||||
// middleware pkg.
|
// 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 {
|
func Logger(next http.Handler) http.Handler {
|
||||||
return DefaultLogger(next)
|
return DefaultLogger(next)
|
||||||
}
|
}
|
||||||
|
@ -40,7 +49,7 @@ func RequestLogger(f LogFormatter) func(next http.Handler) http.Handler {
|
||||||
|
|
||||||
t1 := time.Now()
|
t1 := time.Now()
|
||||||
defer func() {
|
defer func() {
|
||||||
entry.Write(ww.Status(), ww.BytesWritten(), time.Since(t1))
|
entry.Write(ww.Status(), ww.BytesWritten(), ww.Header(), time.Since(t1), nil)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
next.ServeHTTP(ww, WithLogEntry(r, entry))
|
next.ServeHTTP(ww, WithLogEntry(r, entry))
|
||||||
|
@ -58,7 +67,7 @@ type LogFormatter interface {
|
||||||
// LogEntry records the final log when a request completes.
|
// LogEntry records the final log when a request completes.
|
||||||
// See defaultLogEntry for an example implementation.
|
// See defaultLogEntry for an example implementation.
|
||||||
type LogEntry interface {
|
type LogEntry interface {
|
||||||
Write(status, bytes int, elapsed time.Duration)
|
Write(status, bytes int, header http.Header, elapsed time.Duration, extra interface{})
|
||||||
Panic(v interface{}, stack []byte)
|
Panic(v interface{}, stack []byte)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,7 +131,7 @@ type defaultLogEntry struct {
|
||||||
useColor bool
|
useColor bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *defaultLogEntry) Write(status, bytes int, elapsed time.Duration) {
|
func (l *defaultLogEntry) Write(status, bytes int, header http.Header, elapsed time.Duration, extra interface{}) {
|
||||||
switch {
|
switch {
|
||||||
case status < 200:
|
case status < 200:
|
||||||
cW(l.buf, l.useColor, bBlue, "%03d", status)
|
cW(l.buf, l.useColor, bBlue, "%03d", status)
|
||||||
|
@ -151,8 +160,13 @@ func (l *defaultLogEntry) Write(status, bytes int, elapsed time.Duration) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *defaultLogEntry) Panic(v interface{}, stack []byte) {
|
func (l *defaultLogEntry) Panic(v interface{}, stack []byte) {
|
||||||
panicEntry := l.NewLogEntry(l.request).(*defaultLogEntry)
|
PrintPrettyStack(v)
|
||||||
cW(panicEntry.buf, l.useColor, bRed, "panic: %+v", v)
|
}
|
||||||
l.Logger.Print(panicEntry.buf.String())
|
|
||||||
l.Logger.Print(string(stack))
|
func init() {
|
||||||
|
color := true
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
color = false
|
||||||
|
}
|
||||||
|
DefaultLogger = RequestLogger(&DefaultLogFormatter{Logger: log.New(os.Stdout, "", log.LstdFlags), NoColor: !color})
|
||||||
}
|
}
|
18
vendor/github.com/go-chi/chi/v5/middleware/maybe.go
generated
vendored
Normal file
18
vendor/github.com/go-chi/chi/v5/middleware/maybe.go
generated
vendored
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,16 @@
|
||||||
package middleware
|
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
|
// 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
|
// 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.
|
// for defining context keys was copied from Go 1.7's new use of context in net/http.
|
|
@ -9,7 +9,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Unix epoch time
|
// Unix epoch time
|
||||||
var epoch = time.Unix(0, 0).Format(time.RFC1123)
|
var epoch = time.Unix(0, 0).UTC().Format(http.TimeFormat)
|
||||||
|
|
||||||
// Taken from https://github.com/mytrile/nocache
|
// Taken from https://github.com/mytrile/nocache
|
||||||
var noCacheHeaders = map[string]string{
|
var noCacheHeaders = map[string]string{
|
||||||
|
@ -32,10 +32,11 @@ var etagHeaders = []string{
|
||||||
// a router (or subrouter) from being cached by an upstream proxy and/or client.
|
// a router (or subrouter) from being cached by an upstream proxy and/or client.
|
||||||
//
|
//
|
||||||
// As per http://wiki.nginx.org/HttpProxyModule - NoCache sets:
|
// 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
|
// Expires: Thu, 01 Jan 1970 00:00:00 UTC
|
||||||
// X-Accel-Expires: 0
|
// Cache-Control: no-cache, private, max-age=0
|
||||||
// Pragma: no-cache (for HTTP/1.0 proxies/clients)
|
// X-Accel-Expires: 0
|
||||||
|
// Pragma: no-cache (for HTTP/1.0 proxies/clients)
|
||||||
func NoCache(h http.Handler) http.Handler {
|
func NoCache(h http.Handler) http.Handler {
|
||||||
fn := func(w http.ResponseWriter, r *http.Request) {
|
fn := func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
20
vendor/github.com/go-chi/chi/v5/middleware/page_route.go
generated
vendored
Normal file
20
vendor/github.com/go-chi/chi/v5/middleware/page_route.go
generated
vendored
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
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)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
16
vendor/github.com/go-chi/chi/v5/middleware/path_rewrite.go
generated
vendored
Normal file
16
vendor/github.com/go-chi/chi/v5/middleware/path_rewrite.go
generated
vendored
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
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)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
46
vendor/github.com/go-chi/chi/v5/middleware/profiler.go
generated
vendored
Normal file
46
vendor/github.com/go-chi/chi/v5/middleware/profiler.go
generated
vendored
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
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
|
||||||
|
}
|
|
@ -4,16 +4,18 @@ package middleware
|
||||||
// https://github.com/zenazn/goji/tree/master/web/middleware
|
// https://github.com/zenazn/goji/tree/master/web/middleware
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var trueClientIP = http.CanonicalHeaderKey("True-Client-IP")
|
||||||
var xForwardedFor = http.CanonicalHeaderKey("X-Forwarded-For")
|
var xForwardedFor = http.CanonicalHeaderKey("X-Forwarded-For")
|
||||||
var xRealIP = http.CanonicalHeaderKey("X-Real-IP")
|
var xRealIP = http.CanonicalHeaderKey("X-Real-IP")
|
||||||
|
|
||||||
// RealIP is a middleware that sets a http.Request's RemoteAddr to the results
|
// RealIP is a middleware that sets a http.Request's RemoteAddr to the results
|
||||||
// of parsing either the X-Forwarded-For header or the X-Real-IP header (in that
|
// of parsing either the True-Client-IP, X-Real-IP or the X-Forwarded-For headers
|
||||||
// order).
|
// (in that order).
|
||||||
//
|
//
|
||||||
// This middleware should be inserted fairly early in the middleware stack to
|
// This middleware should be inserted fairly early in the middleware stack to
|
||||||
// ensure that subsequent layers (e.g., request loggers) which examine the
|
// ensure that subsequent layers (e.g., request loggers) which examine the
|
||||||
|
@ -40,15 +42,19 @@ func RealIP(h http.Handler) http.Handler {
|
||||||
func realIP(r *http.Request) string {
|
func realIP(r *http.Request) string {
|
||||||
var ip string
|
var ip string
|
||||||
|
|
||||||
if xff := r.Header.Get(xForwardedFor); xff != "" {
|
if tcip := r.Header.Get(trueClientIP); tcip != "" {
|
||||||
i := strings.Index(xff, ", ")
|
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 {
|
if i == -1 {
|
||||||
i = len(xff)
|
i = len(xff)
|
||||||
}
|
}
|
||||||
ip = xff[:i]
|
ip = xff[:i]
|
||||||
} else if xrip := r.Header.Get(xRealIP); xrip != "" {
|
|
||||||
ip = xrip
|
|
||||||
}
|
}
|
||||||
|
if ip == "" || net.ParseIP(ip) == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
return ip
|
return ip
|
||||||
}
|
}
|
203
vendor/github.com/go-chi/chi/v5/middleware/recoverer.go
generated
vendored
Normal file
203
vendor/github.com/go-chi/chi/v5/middleware/recoverer.go
generated
vendored
Normal file
|
@ -0,0 +1,203 @@
|
||||||
|
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
|
||||||
|
}
|
|
@ -20,6 +20,10 @@ type ctxKeyRequestID int
|
||||||
// RequestIDKey is the key that holds the unique request ID in a request context.
|
// RequestIDKey is the key that holds the unique request ID in a request context.
|
||||||
const RequestIDKey ctxKeyRequestID = 0
|
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 prefix string
|
||||||
var reqid uint64
|
var reqid uint64
|
||||||
|
|
||||||
|
@ -63,7 +67,7 @@ func init() {
|
||||||
func RequestID(next http.Handler) http.Handler {
|
func RequestID(next http.Handler) http.Handler {
|
||||||
fn := func(w http.ResponseWriter, r *http.Request) {
|
fn := func(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
requestID := r.Header.Get("X-Request-Id")
|
requestID := r.Header.Get(RequestIDHeader)
|
||||||
if requestID == "" {
|
if requestID == "" {
|
||||||
myid := atomic.AddUint64(&reqid, 1)
|
myid := atomic.AddUint64(&reqid, 1)
|
||||||
requestID = fmt.Sprintf("%s-%06d", prefix, myid)
|
requestID = fmt.Sprintf("%s-%06d", prefix, myid)
|
18
vendor/github.com/go-chi/chi/v5/middleware/request_size.go
generated
vendored
Normal file
18
vendor/github.com/go-chi/chi/v5/middleware/request_size.go
generated
vendored
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
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
|
||||||
|
}
|
151
vendor/github.com/go-chi/chi/v5/middleware/route_headers.go
generated
vendored
Normal file
151
vendor/github.com/go-chi/chi/v5/middleware/route_headers.go
generated
vendored
Normal file
|
@ -0,0 +1,151 @@
|
||||||
|
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)
|
||||||
|
}
|
|
@ -4,7 +4,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/go-chi/chi"
|
"github.com/go-chi/chi/v5"
|
||||||
)
|
)
|
||||||
|
|
||||||
// StripSlashes is a middleware that will match request paths with a trailing
|
// StripSlashes is a middleware that will match request paths with a trailing
|
||||||
|
@ -14,13 +14,18 @@ func StripSlashes(next http.Handler) http.Handler {
|
||||||
fn := func(w http.ResponseWriter, r *http.Request) {
|
fn := func(w http.ResponseWriter, r *http.Request) {
|
||||||
var path string
|
var path string
|
||||||
rctx := chi.RouteContext(r.Context())
|
rctx := chi.RouteContext(r.Context())
|
||||||
if rctx.RoutePath != "" {
|
if rctx != nil && rctx.RoutePath != "" {
|
||||||
path = rctx.RoutePath
|
path = rctx.RoutePath
|
||||||
} else {
|
} else {
|
||||||
path = r.URL.Path
|
path = r.URL.Path
|
||||||
}
|
}
|
||||||
if len(path) > 1 && path[len(path)-1] == '/' {
|
if len(path) > 1 && path[len(path)-1] == '/' {
|
||||||
rctx.RoutePath = path[:len(path)-1]
|
newPath := path[:len(path)-1]
|
||||||
|
if rctx == nil {
|
||||||
|
r.URL.Path = newPath
|
||||||
|
} else {
|
||||||
|
rctx.RoutePath = newPath
|
||||||
|
}
|
||||||
}
|
}
|
||||||
next.ServeHTTP(w, r)
|
next.ServeHTTP(w, r)
|
||||||
}
|
}
|
||||||
|
@ -36,7 +41,7 @@ func RedirectSlashes(next http.Handler) http.Handler {
|
||||||
fn := func(w http.ResponseWriter, r *http.Request) {
|
fn := func(w http.ResponseWriter, r *http.Request) {
|
||||||
var path string
|
var path string
|
||||||
rctx := chi.RouteContext(r.Context())
|
rctx := chi.RouteContext(r.Context())
|
||||||
if rctx.RoutePath != "" {
|
if rctx != nil && rctx.RoutePath != "" {
|
||||||
path = rctx.RoutePath
|
path = rctx.RoutePath
|
||||||
} else {
|
} else {
|
||||||
path = r.URL.Path
|
path = r.URL.Path
|
||||||
|
@ -47,7 +52,8 @@ func RedirectSlashes(next http.Handler) http.Handler {
|
||||||
} else {
|
} else {
|
||||||
path = path[:len(path)-1]
|
path = path[:len(path)-1]
|
||||||
}
|
}
|
||||||
http.Redirect(w, r, path, 301)
|
redirectURL := fmt.Sprintf("//%s%s", r.Host, path)
|
||||||
|
http.Redirect(w, r, redirectURL, 301)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
next.ServeHTTP(w, r)
|
next.ServeHTTP(w, r)
|
25
vendor/github.com/go-chi/chi/v5/middleware/sunset.go
generated
vendored
Normal file
25
vendor/github.com/go-chi/chi/v5/middleware/sunset.go
generated
vendored
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
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)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
27
vendor/github.com/go-chi/chi/v5/middleware/supress_notfound.go
generated
vendored
Normal file
27
vendor/github.com/go-chi/chi/v5/middleware/supress_notfound.go
generated
vendored
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
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)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -32,7 +32,7 @@ var (
|
||||||
reset = []byte{'\033', '[', '0', 'm'}
|
reset = []byte{'\033', '[', '0', 'm'}
|
||||||
)
|
)
|
||||||
|
|
||||||
var isTTY bool
|
var IsTTY bool
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
// This is sort of cheating: if stdout is a character device, we assume
|
// This is sort of cheating: if stdout is a character device, we assume
|
||||||
|
@ -47,17 +47,17 @@ func init() {
|
||||||
fi, err := os.Stdout.Stat()
|
fi, err := os.Stdout.Stat()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
m := os.ModeDevice | os.ModeCharDevice
|
m := os.ModeDevice | os.ModeCharDevice
|
||||||
isTTY = fi.Mode()&m == m
|
IsTTY = fi.Mode()&m == m
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// colorWrite
|
// colorWrite
|
||||||
func cW(w io.Writer, useColor bool, color []byte, s string, args ...interface{}) {
|
func cW(w io.Writer, useColor bool, color []byte, s string, args ...interface{}) {
|
||||||
if isTTY && useColor {
|
if IsTTY && useColor {
|
||||||
w.Write(color)
|
w.Write(color)
|
||||||
}
|
}
|
||||||
fmt.Fprintf(w, s, args...)
|
fmt.Fprintf(w, s, args...)
|
||||||
if isTTY && useColor {
|
if IsTTY && useColor {
|
||||||
w.Write(reset)
|
w.Write(reset)
|
||||||
}
|
}
|
||||||
}
|
}
|
132
vendor/github.com/go-chi/chi/v5/middleware/throttle.go
generated
vendored
Normal file
132
vendor/github.com/go-chi/chi/v5/middleware/throttle.go
generated
vendored
Normal file
|
@ -0,0 +1,132 @@
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 currently 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")
|
||||||
|
}
|
||||||
|
|
||||||
|
t := throttler{
|
||||||
|
tokens: make(chan token, opts.Limit),
|
||||||
|
backlogTokens: make(chan token, opts.Limit+opts.BacklogLimit),
|
||||||
|
backlogTimeout: opts.BacklogTimeout,
|
||||||
|
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, http.StatusTooManyRequests)
|
||||||
|
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, http.StatusTooManyRequests)
|
||||||
|
return
|
||||||
|
case <-ctx.Done():
|
||||||
|
timer.Stop()
|
||||||
|
t.setRetryAfterHeaderIfNeeded(w, true)
|
||||||
|
http.Error(w, errContextCanceled, http.StatusTooManyRequests)
|
||||||
|
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, http.StatusTooManyRequests)
|
||||||
|
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
|
||||||
|
retryAfterFn func(ctxDone bool) time.Duration
|
||||||
|
backlogTimeout 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())))
|
||||||
|
}
|
|
@ -15,21 +15,20 @@ import (
|
||||||
//
|
//
|
||||||
// ie. a route/handler may look like:
|
// ie. a route/handler may look like:
|
||||||
//
|
//
|
||||||
// r.Get("/long", func(w http.ResponseWriter, r *http.Request) {
|
// r.Get("/long", func(w http.ResponseWriter, r *http.Request) {
|
||||||
// ctx := r.Context()
|
// ctx := r.Context()
|
||||||
// processTime := time.Duration(rand.Intn(4)+1) * time.Second
|
// processTime := time.Duration(rand.Intn(4)+1) * time.Second
|
||||||
//
|
//
|
||||||
// select {
|
// select {
|
||||||
// case <-ctx.Done():
|
// case <-ctx.Done():
|
||||||
// return
|
// return
|
||||||
//
|
//
|
||||||
// case <-time.After(processTime):
|
// case <-time.After(processTime):
|
||||||
// // The above channel simulates some hard work.
|
// // The above channel simulates some hard work.
|
||||||
// }
|
// }
|
||||||
//
|
|
||||||
// w.Write([]byte("done"))
|
|
||||||
// })
|
|
||||||
//
|
//
|
||||||
|
// w.Write([]byte("done"))
|
||||||
|
// })
|
||||||
func Timeout(timeout time.Duration) func(next http.Handler) http.Handler {
|
func Timeout(timeout time.Duration) func(next http.Handler) http.Handler {
|
||||||
return func(next http.Handler) http.Handler {
|
return func(next http.Handler) http.Handler {
|
||||||
fn := func(w http.ResponseWriter, r *http.Request) {
|
fn := func(w http.ResponseWriter, r *http.Request) {
|
|
@ -5,7 +5,7 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/go-chi/chi"
|
"github.com/go-chi/chi/v5"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -20,30 +20,29 @@ var (
|
||||||
//
|
//
|
||||||
// Routers should not include a url parameter for the suffix when using this middleware.
|
// 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`
|
// Sample usage for url paths `/articles/1`, `/articles/1.json` and `/articles/1.xml`:
|
||||||
//
|
//
|
||||||
// func routes() http.Handler {
|
// func routes() http.Handler {
|
||||||
// r := chi.NewRouter()
|
// r := chi.NewRouter()
|
||||||
// r.Use(middleware.URLFormat)
|
// r.Use(middleware.URLFormat)
|
||||||
//
|
//
|
||||||
// r.Get("/articles/{id}", ListArticles)
|
// r.Get("/articles/{id}", ListArticles)
|
||||||
//
|
//
|
||||||
// return r
|
// return r
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// func ListArticles(w http.ResponseWriter, r *http.Request) {
|
// func ListArticles(w http.ResponseWriter, r *http.Request) {
|
||||||
// urlFormat, _ := r.Context().Value(middleware.URLFormatCtxKey).(string)
|
// 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)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
//
|
||||||
|
// 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 {
|
func URLFormat(next http.Handler) http.Handler {
|
||||||
fn := func(w http.ResponseWriter, r *http.Request) {
|
fn := func(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
|
@ -51,15 +50,19 @@ func URLFormat(next http.Handler) http.Handler {
|
||||||
var format string
|
var format string
|
||||||
path := r.URL.Path
|
path := r.URL.Path
|
||||||
|
|
||||||
|
rctx := chi.RouteContext(r.Context())
|
||||||
|
if rctx != nil && rctx.RoutePath != "" {
|
||||||
|
path = rctx.RoutePath
|
||||||
|
}
|
||||||
|
|
||||||
if strings.Index(path, ".") > 0 {
|
if strings.Index(path, ".") > 0 {
|
||||||
base := strings.LastIndex(path, "/")
|
base := strings.LastIndex(path, "/")
|
||||||
idx := strings.Index(path[base:], ".")
|
idx := strings.LastIndex(path[base:], ".")
|
||||||
|
|
||||||
if idx > 0 {
|
if idx > 0 {
|
||||||
idx += base
|
idx += base
|
||||||
format = path[idx+1:]
|
format = path[idx+1:]
|
||||||
|
|
||||||
rctx := chi.RouteContext(r.Context())
|
|
||||||
rctx.RoutePath = path[:idx]
|
rctx.RoutePath = path[:idx]
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -6,7 +6,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// WithValue is a middleware that sets a given key/value in a context chain.
|
// WithValue is a middleware that sets a given key/value in a context chain.
|
||||||
func WithValue(key interface{}, val interface{}) func(next http.Handler) http.Handler {
|
func WithValue(key, val interface{}) func(next http.Handler) http.Handler {
|
||||||
return func(next http.Handler) http.Handler {
|
return func(next http.Handler) http.Handler {
|
||||||
fn := func(w http.ResponseWriter, r *http.Request) {
|
fn := func(w http.ResponseWriter, r *http.Request) {
|
||||||
r = r.WithContext(context.WithValue(r.Context(), key, val))
|
r = r.WithContext(context.WithValue(r.Context(), key, val))
|
|
@ -28,7 +28,14 @@ func NewWrapResponseWriter(w http.ResponseWriter, protoMajor int) WrapResponseWr
|
||||||
if fl && hj && rf {
|
if fl && hj && rf {
|
||||||
return &httpFancyWriter{bw}
|
return &httpFancyWriter{bw}
|
||||||
}
|
}
|
||||||
|
if fl && hj {
|
||||||
|
return &flushHijackWriter{bw}
|
||||||
|
}
|
||||||
|
if hj {
|
||||||
|
return &hijackWriter{bw}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if fl {
|
if fl {
|
||||||
return &flushWriter{bw}
|
return &flushWriter{bw}
|
||||||
}
|
}
|
||||||
|
@ -75,7 +82,7 @@ func (b *basicWriter) WriteHeader(code int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *basicWriter) Write(buf []byte) (int, error) {
|
func (b *basicWriter) Write(buf []byte) (int, error) {
|
||||||
b.WriteHeader(http.StatusOK)
|
b.maybeWriteHeader()
|
||||||
n, err := b.ResponseWriter.Write(buf)
|
n, err := b.ResponseWriter.Write(buf)
|
||||||
if b.tee != nil {
|
if b.tee != nil {
|
||||||
_, err2 := b.tee.Write(buf[:n])
|
_, err2 := b.tee.Write(buf[:n])
|
||||||
|
@ -110,19 +117,50 @@ func (b *basicWriter) Unwrap() http.ResponseWriter {
|
||||||
return b.ResponseWriter
|
return b.ResponseWriter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// flushWriter ...
|
||||||
type flushWriter struct {
|
type flushWriter struct {
|
||||||
basicWriter
|
basicWriter
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *flushWriter) Flush() {
|
func (f *flushWriter) Flush() {
|
||||||
f.wroteHeader = true
|
f.wroteHeader = true
|
||||||
|
|
||||||
fl := f.basicWriter.ResponseWriter.(http.Flusher)
|
fl := f.basicWriter.ResponseWriter.(http.Flusher)
|
||||||
fl.Flush()
|
fl.Flush()
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ http.Flusher = &flushWriter{}
|
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
|
// httpFancyWriter is a HTTP writer that additionally satisfies
|
||||||
// http.Flusher, http.Hijacker, and io.ReaderFrom. It exists for the common case
|
// 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
|
// of wrapping the http.ResponseWriter that package http gives you, in order to
|
||||||
|
@ -133,7 +171,6 @@ type httpFancyWriter struct {
|
||||||
|
|
||||||
func (f *httpFancyWriter) Flush() {
|
func (f *httpFancyWriter) Flush() {
|
||||||
f.wroteHeader = true
|
f.wroteHeader = true
|
||||||
|
|
||||||
fl := f.basicWriter.ResponseWriter.(http.Flusher)
|
fl := f.basicWriter.ResponseWriter.(http.Flusher)
|
||||||
fl.Flush()
|
fl.Flush()
|
||||||
}
|
}
|
||||||
|
@ -175,7 +212,6 @@ type http2FancyWriter struct {
|
||||||
|
|
||||||
func (f *http2FancyWriter) Flush() {
|
func (f *http2FancyWriter) Flush() {
|
||||||
f.wroteHeader = true
|
f.wroteHeader = true
|
||||||
|
|
||||||
fl := f.basicWriter.ResponseWriter.(http.Flusher)
|
fl := f.basicWriter.ResponseWriter.(http.Flusher)
|
||||||
fl.Flush()
|
fl.Flush()
|
||||||
}
|
}
|
138
vendor/github.com/go-chi/chi/mux.go → vendor/github.com/go-chi/chi/v5/mux.go
generated
vendored
138
vendor/github.com/go-chi/chi/mux.go → vendor/github.com/go-chi/chi/v5/mux.go
generated
vendored
|
@ -19,29 +19,32 @@ var _ Router = &Mux{}
|
||||||
// particularly useful for writing large REST API services that break a handler
|
// particularly useful for writing large REST API services that break a handler
|
||||||
// into many smaller parts composed of middlewares and end handlers.
|
// into many smaller parts composed of middlewares and end handlers.
|
||||||
type Mux struct {
|
type Mux struct {
|
||||||
// The radix trie router
|
|
||||||
tree *node
|
|
||||||
|
|
||||||
// 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
|
|
||||||
parent *Mux
|
|
||||||
|
|
||||||
// The computed mux handler made of the chained middleware stack and
|
// The computed mux handler made of the chained middleware stack and
|
||||||
// the tree router
|
// the tree router
|
||||||
handler http.Handler
|
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
|
// Routing context pool
|
||||||
pool *sync.Pool
|
pool *sync.Pool
|
||||||
|
|
||||||
// Custom route not found handler
|
// Custom route not found handler
|
||||||
notFoundHandler http.HandlerFunc
|
notFoundHandler http.HandlerFunc
|
||||||
|
|
||||||
// Custom method not allowed handler
|
// The middleware stack
|
||||||
methodNotAllowedHandler http.HandlerFunc
|
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
|
// NewMux returns a newly initialized Mux object that implements the Router
|
||||||
|
@ -78,7 +81,12 @@ func (mx *Mux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
rctx = mx.pool.Get().(*Context)
|
rctx = mx.pool.Get().(*Context)
|
||||||
rctx.Reset()
|
rctx.Reset()
|
||||||
rctx.Routes = mx
|
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))
|
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.handler.ServeHTTP(w, r)
|
||||||
mx.pool.Put(rctx)
|
mx.pool.Put(rctx)
|
||||||
}
|
}
|
||||||
|
@ -99,12 +107,24 @@ func (mx *Mux) Use(middlewares ...func(http.Handler) http.Handler) {
|
||||||
// Handle adds the route `pattern` that matches any http method to
|
// Handle adds the route `pattern` that matches any http method to
|
||||||
// execute the `handler` http.Handler.
|
// execute the `handler` http.Handler.
|
||||||
func (mx *Mux) Handle(pattern string, 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)
|
mx.handle(mALL, pattern, handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HandleFunc adds the route `pattern` that matches any http method to
|
// HandleFunc adds the route `pattern` that matches any http method to
|
||||||
// execute the `handlerFn` http.HandlerFunc.
|
// execute the `handlerFn` http.HandlerFunc.
|
||||||
func (mx *Mux) HandleFunc(pattern string, 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)
|
mx.handle(mALL, pattern, handlerFn)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,7 +168,7 @@ func (mx *Mux) Head(pattern string, handlerFn http.HandlerFunc) {
|
||||||
mx.handle(mHEAD, pattern, handlerFn)
|
mx.handle(mHEAD, pattern, handlerFn)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Options adds the route `pattern` that matches a OPTIONS http method to
|
// Options adds the route `pattern` that matches an OPTIONS http method to
|
||||||
// execute the `handlerFn` http.HandlerFunc.
|
// execute the `handlerFn` http.HandlerFunc.
|
||||||
func (mx *Mux) Options(pattern string, handlerFn http.HandlerFunc) {
|
func (mx *Mux) Options(pattern string, handlerFn http.HandlerFunc) {
|
||||||
mx.handle(mOPTIONS, pattern, handlerFn)
|
mx.handle(mOPTIONS, pattern, handlerFn)
|
||||||
|
@ -220,10 +240,10 @@ func (mx *Mux) MethodNotAllowed(handlerFn http.HandlerFunc) {
|
||||||
|
|
||||||
// With adds inline middlewares for an endpoint handler.
|
// With adds inline middlewares for an endpoint handler.
|
||||||
func (mx *Mux) With(middlewares ...func(http.Handler) http.Handler) Router {
|
func (mx *Mux) With(middlewares ...func(http.Handler) http.Handler) Router {
|
||||||
// Similarly as in handle(), we must build the mux handler once further
|
// Similarly as in handle(), we must build the mux handler once additional
|
||||||
// middleware registration isn't allowed for this stack, like now.
|
// middleware registration isn't allowed for this stack, like now.
|
||||||
if !mx.inline && mx.handler == nil {
|
if !mx.inline && mx.handler == nil {
|
||||||
mx.buildRouteHandler()
|
mx.updateRouteHandler()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy middlewares from parent inline muxs
|
// Copy middlewares from parent inline muxs
|
||||||
|
@ -234,30 +254,33 @@ func (mx *Mux) With(middlewares ...func(http.Handler) http.Handler) Router {
|
||||||
}
|
}
|
||||||
mws = append(mws, middlewares...)
|
mws = append(mws, middlewares...)
|
||||||
|
|
||||||
im := &Mux{pool: mx.pool, inline: true, parent: mx, tree: mx.tree, middlewares: mws}
|
im := &Mux{
|
||||||
|
pool: mx.pool, inline: true, parent: mx, tree: mx.tree, middlewares: mws,
|
||||||
|
notFoundHandler: mx.notFoundHandler, methodNotAllowedHandler: mx.methodNotAllowedHandler,
|
||||||
|
}
|
||||||
|
|
||||||
return im
|
return im
|
||||||
}
|
}
|
||||||
|
|
||||||
// Group creates a new inline-Mux with a fresh middleware stack. It's useful
|
// 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
|
// for a group of handlers along the same routing path that use an additional
|
||||||
// set of middlewares. See _examples/.
|
// set of middlewares. See _examples/.
|
||||||
func (mx *Mux) Group(fn func(r Router)) Router {
|
func (mx *Mux) Group(fn func(r Router)) Router {
|
||||||
im := mx.With().(*Mux)
|
im := mx.With()
|
||||||
if fn != nil {
|
if fn != nil {
|
||||||
fn(im)
|
fn(im)
|
||||||
}
|
}
|
||||||
return im
|
return im
|
||||||
}
|
}
|
||||||
|
|
||||||
// Route creates a new Mux with a fresh middleware stack and mounts it
|
// Route creates a new Mux and mounts it along the `pattern` as a subrouter.
|
||||||
// along the `pattern` as a subrouter. Effectively, this is a short-hand
|
// Effectively, this is a short-hand call to Mount. See _examples/.
|
||||||
// call to Mount. See _examples/.
|
|
||||||
func (mx *Mux) Route(pattern string, fn func(r Router)) Router {
|
func (mx *Mux) Route(pattern string, fn func(r Router)) Router {
|
||||||
subRouter := NewRouter()
|
if fn == nil {
|
||||||
if fn != nil {
|
panic(fmt.Sprintf("chi: attempting to Route() a nil subrouter on '%s'", pattern))
|
||||||
fn(subRouter)
|
|
||||||
}
|
}
|
||||||
|
subRouter := NewRouter()
|
||||||
|
fn(subRouter)
|
||||||
mx.Mount(pattern, subRouter)
|
mx.Mount(pattern, subRouter)
|
||||||
return subRouter
|
return subRouter
|
||||||
}
|
}
|
||||||
|
@ -270,6 +293,10 @@ func (mx *Mux) Route(pattern string, fn func(r Router)) Router {
|
||||||
// routing at the `handler`, which in most cases is another chi.Router. As a result,
|
// 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.
|
// if you define two Mount() routes on the exact same pattern the mount will panic.
|
||||||
func (mx *Mux) Mount(pattern string, handler http.Handler) {
|
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
|
// Provide runtime safety for ensuring a pattern isn't mounted on an existing
|
||||||
// routing pattern.
|
// routing pattern.
|
||||||
if mx.tree.findPattern(pattern+"*") || mx.tree.findPattern(pattern+"/*") {
|
if mx.tree.findPattern(pattern+"*") || mx.tree.findPattern(pattern+"/*") {
|
||||||
|
@ -285,10 +312,18 @@ func (mx *Mux) Mount(pattern string, handler http.Handler) {
|
||||||
subr.MethodNotAllowed(mx.methodNotAllowedHandler)
|
subr.MethodNotAllowed(mx.methodNotAllowedHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wrap the sub-router in a handlerFunc to scope the request path for routing.
|
|
||||||
mountHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
mountHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
rctx := RouteContext(r.Context())
|
rctx := RouteContext(r.Context())
|
||||||
|
|
||||||
|
// shift the url path past the previous subrouter
|
||||||
rctx.RoutePath = mx.nextRoutePath(rctx)
|
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)
|
handler.ServeHTTP(w, r)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -354,19 +389,11 @@ func (mx *Mux) NotFoundHandler() http.HandlerFunc {
|
||||||
|
|
||||||
// MethodNotAllowedHandler returns the default Mux 405 responder whenever
|
// MethodNotAllowedHandler returns the default Mux 405 responder whenever
|
||||||
// a method cannot be resolved for a route.
|
// a method cannot be resolved for a route.
|
||||||
func (mx *Mux) MethodNotAllowedHandler() http.HandlerFunc {
|
func (mx *Mux) MethodNotAllowedHandler(methodsAllowed ...methodTyp) http.HandlerFunc {
|
||||||
if mx.methodNotAllowedHandler != nil {
|
if mx.methodNotAllowedHandler != nil {
|
||||||
return mx.methodNotAllowedHandler
|
return mx.methodNotAllowedHandler
|
||||||
}
|
}
|
||||||
return methodNotAllowedHandler
|
return methodNotAllowedHandler(methodsAllowed...)
|
||||||
}
|
|
||||||
|
|
||||||
// buildRouteHandler 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) buildRouteHandler() {
|
|
||||||
mx.handler = chain(mx.middlewares, http.HandlerFunc(mx.routeHTTP))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// handle registers a http.Handler in the routing tree for a particular http method
|
// handle registers a http.Handler in the routing tree for a particular http method
|
||||||
|
@ -376,9 +403,9 @@ func (mx *Mux) handle(method methodTyp, pattern string, handler http.Handler) *n
|
||||||
panic(fmt.Sprintf("chi: routing pattern must begin with '/' in '%s'", pattern))
|
panic(fmt.Sprintf("chi: routing pattern must begin with '/' in '%s'", pattern))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build the final routing handler for this Mux.
|
// Build the computed routing handler for this routing pattern.
|
||||||
if !mx.inline && mx.handler == nil {
|
if !mx.inline && mx.handler == nil {
|
||||||
mx.buildRouteHandler()
|
mx.updateRouteHandler()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build endpoint handler with inline middlewares for the route
|
// Build endpoint handler with inline middlewares for the route
|
||||||
|
@ -408,6 +435,9 @@ func (mx *Mux) routeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
} else {
|
} else {
|
||||||
routePath = r.URL.Path
|
routePath = r.URL.Path
|
||||||
}
|
}
|
||||||
|
if routePath == "" {
|
||||||
|
routePath = "/"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if method is supported by chi
|
// Check if method is supported by chi
|
||||||
|
@ -422,11 +452,15 @@ func (mx *Mux) routeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
// Find the route
|
// Find the route
|
||||||
if _, _, h := mx.tree.FindRoute(rctx, method, routePath); h != nil {
|
if _, _, h := mx.tree.FindRoute(rctx, method, routePath); h != nil {
|
||||||
|
if supportsPathValue {
|
||||||
|
setPathValue(rctx, r)
|
||||||
|
}
|
||||||
|
|
||||||
h.ServeHTTP(w, r)
|
h.ServeHTTP(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if rctx.methodNotAllowed {
|
if rctx.methodNotAllowed {
|
||||||
mx.MethodNotAllowedHandler().ServeHTTP(w, r)
|
mx.MethodNotAllowedHandler(rctx.methodsAllowed...).ServeHTTP(w, r)
|
||||||
} else {
|
} else {
|
||||||
mx.NotFoundHandler().ServeHTTP(w, r)
|
mx.NotFoundHandler().ServeHTTP(w, r)
|
||||||
}
|
}
|
||||||
|
@ -436,7 +470,7 @@ func (mx *Mux) nextRoutePath(rctx *Context) string {
|
||||||
routePath := "/"
|
routePath := "/"
|
||||||
nx := len(rctx.routeParams.Keys) - 1 // index of last param in list
|
nx := len(rctx.routeParams.Keys) - 1 // index of last param in list
|
||||||
if nx >= 0 && rctx.routeParams.Keys[nx] == "*" && len(rctx.routeParams.Values) > nx {
|
if nx >= 0 && rctx.routeParams.Keys[nx] == "*" && len(rctx.routeParams.Values) > nx {
|
||||||
routePath += rctx.routeParams.Values[nx]
|
routePath = "/" + rctx.routeParams.Values[nx]
|
||||||
}
|
}
|
||||||
return routePath
|
return routePath
|
||||||
}
|
}
|
||||||
|
@ -452,9 +486,23 @@ func (mx *Mux) updateSubRoutes(fn func(subMux *Mux)) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// methodNotAllowedHandler is a helper function to respond with a 405,
|
// updateRouteHandler builds the single mux handler that is a chain of the middleware
|
||||||
// method not allowed.
|
// stack, as defined by calls to Use(), and the tree router (Mux) itself. After this
|
||||||
func methodNotAllowedHandler(w http.ResponseWriter, r *http.Request) {
|
// point, no other middlewares can be registered on this Mux's stack. But you can still
|
||||||
w.WriteHeader(405)
|
// compose additional middlewares via Group()'s or using a chained middleware handler.
|
||||||
w.Write(nil)
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
20
vendor/github.com/go-chi/chi/v5/path_value.go
generated
vendored
Normal file
20
vendor/github.com/go-chi/chi/v5/path_value.go
generated
vendored
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
//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)
|
||||||
|
}
|
||||||
|
}
|
19
vendor/github.com/go-chi/chi/v5/path_value_fallback.go
generated
vendored
Normal file
19
vendor/github.com/go-chi/chi/v5/path_value_fallback.go
generated
vendored
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
//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) {
|
||||||
|
}
|
118
vendor/github.com/go-chi/chi/tree.go → vendor/github.com/go-chi/chi/v5/tree.go
generated
vendored
118
vendor/github.com/go-chi/chi/tree.go → vendor/github.com/go-chi/chi/v5/tree.go
generated
vendored
|
@ -6,7 +6,6 @@ package chi
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"regexp"
|
"regexp"
|
||||||
"sort"
|
"sort"
|
||||||
|
@ -14,7 +13,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type methodTyp int
|
type methodTyp uint
|
||||||
|
|
||||||
const (
|
const (
|
||||||
mSTUB methodTyp = 1 << iota
|
mSTUB methodTyp = 1 << iota
|
||||||
|
@ -44,6 +43,18 @@ var methodMap = map[string]methodTyp{
|
||||||
http.MethodTrace: mTRACE,
|
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
|
// RegisterMethod adds support for custom HTTP method handlers, available
|
||||||
// via Router#Method and Router#MethodFunc
|
// via Router#Method and Router#MethodFunc
|
||||||
func RegisterMethod(method string) {
|
func RegisterMethod(method string) {
|
||||||
|
@ -55,10 +66,10 @@ func RegisterMethod(method string) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
n := len(methodMap)
|
n := len(methodMap)
|
||||||
if n > strconv.IntSize {
|
if n > strconv.IntSize-2 {
|
||||||
panic(fmt.Sprintf("chi: max number of methods reached (%d)", strconv.IntSize))
|
panic(fmt.Sprintf("chi: max number of methods reached (%d)", strconv.IntSize))
|
||||||
}
|
}
|
||||||
mt := methodTyp(math.Exp2(float64(n)))
|
mt := methodTyp(2 << n)
|
||||||
methodMap[method] = mt
|
methodMap[method] = mt
|
||||||
mALL |= mt
|
mALL |= mt
|
||||||
}
|
}
|
||||||
|
@ -73,17 +84,8 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
type node struct {
|
type node struct {
|
||||||
// node type: static, regexp, param, catchAll
|
// subroutes on the leaf node
|
||||||
typ nodeTyp
|
subroutes Routes
|
||||||
|
|
||||||
// first byte of the prefix
|
|
||||||
label byte
|
|
||||||
|
|
||||||
// first byte of the child prefix
|
|
||||||
tail byte
|
|
||||||
|
|
||||||
// prefix is the common prefix we ignore
|
|
||||||
prefix string
|
|
||||||
|
|
||||||
// regexp matcher for regexp nodes
|
// regexp matcher for regexp nodes
|
||||||
rex *regexp.Regexp
|
rex *regexp.Regexp
|
||||||
|
@ -91,12 +93,21 @@ type node struct {
|
||||||
// HTTP handler endpoints on the leaf node
|
// HTTP handler endpoints on the leaf node
|
||||||
endpoints endpoints
|
endpoints endpoints
|
||||||
|
|
||||||
// subroutes on the leaf node
|
// prefix is the common prefix we ignore
|
||||||
subroutes Routes
|
prefix string
|
||||||
|
|
||||||
// child nodes should be stored in-order for iteration,
|
// child nodes should be stored in-order for iteration,
|
||||||
// in groups of the node type.
|
// in groups of the node type.
|
||||||
children [ntCatchAll + 1]nodes
|
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
|
// endpoints is a mapping of http method constants to handlers
|
||||||
|
@ -331,7 +342,7 @@ func (n *node) getEdge(ntyp nodeTyp, label, tail byte, prefix string) *node {
|
||||||
func (n *node) setEndpoint(method methodTyp, handler http.Handler, pattern string) {
|
func (n *node) setEndpoint(method methodTyp, handler http.Handler, pattern string) {
|
||||||
// Set the handler for the method type on the node
|
// Set the handler for the method type on the node
|
||||||
if n.endpoints == nil {
|
if n.endpoints == nil {
|
||||||
n.endpoints = make(endpoints, 0)
|
n.endpoints = make(endpoints)
|
||||||
}
|
}
|
||||||
|
|
||||||
paramKeys := patParamKeys(pattern)
|
paramKeys := patParamKeys(pattern)
|
||||||
|
@ -430,10 +441,12 @@ func (n *node) findRoute(rctx *Context, method methodTyp, path string) *node {
|
||||||
} else {
|
} else {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
} else if ntyp == ntRegexp && p == 0 {
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if ntyp == ntRegexp && xn.rex != nil {
|
if ntyp == ntRegexp && xn.rex != nil {
|
||||||
if xn.rex.Match([]byte(xsearch[:p])) == false {
|
if !xn.rex.MatchString(xsearch[:p]) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
} else if strings.IndexByte(xsearch[:p], '/') != -1 {
|
} else if strings.IndexByte(xsearch[:p], '/') != -1 {
|
||||||
|
@ -441,11 +454,44 @@ func (n *node) findRoute(rctx *Context, method methodTyp, path string) *node {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
prevlen := len(rctx.routeParams.Values)
|
||||||
rctx.routeParams.Values = append(rctx.routeParams.Values, xsearch[:p])
|
rctx.routeParams.Values = append(rctx.routeParams.Values, xsearch[:p])
|
||||||
xsearch = xsearch[p:]
|
xsearch = xsearch[p:]
|
||||||
break
|
|
||||||
|
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:
|
default:
|
||||||
// catch-all nodes
|
// catch-all nodes
|
||||||
rctx.routeParams.Values = append(rctx.routeParams.Values, search)
|
rctx.routeParams.Values = append(rctx.routeParams.Values, search)
|
||||||
|
@ -460,12 +506,19 @@ func (n *node) findRoute(rctx *Context, method methodTyp, path string) *node {
|
||||||
// did we find it yet?
|
// did we find it yet?
|
||||||
if len(xsearch) == 0 {
|
if len(xsearch) == 0 {
|
||||||
if xn.isLeaf() {
|
if xn.isLeaf() {
|
||||||
h, _ := xn.endpoints[method]
|
h := xn.endpoints[method]
|
||||||
if h != nil && h.handler != nil {
|
if h != nil && h.handler != nil {
|
||||||
rctx.routeParams.Keys = append(rctx.routeParams.Keys, h.paramKeys...)
|
rctx.routeParams.Keys = append(rctx.routeParams.Keys, h.paramKeys...)
|
||||||
return xn
|
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
|
// flag that the routing context found a route, but not a corresponding
|
||||||
// supported method
|
// supported method
|
||||||
rctx.methodNotAllowed = true
|
rctx.methodNotAllowed = true
|
||||||
|
@ -518,15 +571,6 @@ func (n *node) findEdge(ntyp nodeTyp, label byte) *node {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *node) isEmpty() bool {
|
|
||||||
for _, nds := range n.children {
|
|
||||||
if len(nds) > 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *node) isLeaf() bool {
|
func (n *node) isLeaf() bool {
|
||||||
return n.endpoints != nil
|
return n.endpoints != nil
|
||||||
}
|
}
|
||||||
|
@ -582,7 +626,7 @@ func (n *node) routes() []Route {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Group methodHandlers by unique patterns
|
// Group methodHandlers by unique patterns
|
||||||
pats := make(map[string]endpoints, 0)
|
pats := make(map[string]endpoints)
|
||||||
|
|
||||||
for mt, h := range eps {
|
for mt, h := range eps {
|
||||||
if h.pattern == "" {
|
if h.pattern == "" {
|
||||||
|
@ -597,7 +641,7 @@ func (n *node) routes() []Route {
|
||||||
}
|
}
|
||||||
|
|
||||||
for p, mh := range pats {
|
for p, mh := range pats {
|
||||||
hs := make(map[string]http.Handler, 0)
|
hs := make(map[string]http.Handler)
|
||||||
if mh[mALL] != nil && mh[mALL].handler != nil {
|
if mh[mALL] != nil && mh[mALL].handler != nil {
|
||||||
hs["*"] = mh[mALL].handler
|
hs["*"] = mh[mALL].handler
|
||||||
}
|
}
|
||||||
|
@ -613,7 +657,7 @@ func (n *node) routes() []Route {
|
||||||
hs[m] = h.handler
|
hs[m] = h.handler
|
||||||
}
|
}
|
||||||
|
|
||||||
rt := Route{p, hs, subroutes}
|
rt := Route{subroutes, hs, p}
|
||||||
rts = append(rts, rt)
|
rts = append(rts, rt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -698,7 +742,7 @@ func patNextSegment(pattern string) (nodeTyp, string, string, byte, int, int) {
|
||||||
rexpat = "^" + rexpat
|
rexpat = "^" + rexpat
|
||||||
}
|
}
|
||||||
if rexpat[len(rexpat)-1] != '$' {
|
if rexpat[len(rexpat)-1] != '$' {
|
||||||
rexpat = rexpat + "$"
|
rexpat += "$"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -795,10 +839,11 @@ func (ns nodes) findEdge(label byte) *node {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Route describes the details of a routing handler.
|
// Route describes the details of a routing handler.
|
||||||
|
// Handlers map key is an HTTP method
|
||||||
type Route struct {
|
type Route struct {
|
||||||
Pattern string
|
|
||||||
Handlers map[string]http.Handler
|
|
||||||
SubRoutes Routes
|
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.
|
// WalkFunc is the type of the function called for each method and route visited by Walk.
|
||||||
|
@ -829,6 +874,7 @@ func walk(r Routes, walkFn WalkFunc, parentRoute string, parentMw ...func(http.H
|
||||||
}
|
}
|
||||||
|
|
||||||
fullRoute := parentRoute + route.Pattern
|
fullRoute := parentRoute + route.Pattern
|
||||||
|
fullRoute = strings.Replace(fullRoute, "/*/", "/", -1)
|
||||||
|
|
||||||
if chain, ok := handler.(*ChainHandler); ok {
|
if chain, ok := handler.(*ChainHandler); ok {
|
||||||
if err := walkFn(method, fullRoute, chain.Endpoint, append(mws, chain.Middlewares...)...); err != nil {
|
if err := walkFn(method, fullRoute, chain.Endpoint, append(mws, chain.Middlewares...)...); err != nil {
|
8
vendor/modules.txt
vendored
8
vendor/modules.txt
vendored
|
@ -9,10 +9,10 @@ github.com/dustin/go-humanize
|
||||||
# github.com/ghodss/yaml v1.0.0
|
# github.com/ghodss/yaml v1.0.0
|
||||||
## explicit
|
## explicit
|
||||||
github.com/ghodss/yaml
|
github.com/ghodss/yaml
|
||||||
# github.com/go-chi/chi v4.0.2+incompatible
|
# github.com/go-chi/chi/v5 v5.0.12
|
||||||
## explicit
|
## explicit
|
||||||
github.com/go-chi/chi
|
github.com/go-chi/chi/v5
|
||||||
github.com/go-chi/chi/middleware
|
github.com/go-chi/chi/v5/middleware
|
||||||
# github.com/gofrs/uuid v3.2.0+incompatible
|
# github.com/gofrs/uuid v3.2.0+incompatible
|
||||||
## explicit
|
## explicit
|
||||||
github.com/gofrs/uuid
|
github.com/gofrs/uuid
|
||||||
|
@ -21,8 +21,6 @@ github.com/gofrs/uuid
|
||||||
github.com/gorilla/mux
|
github.com/gorilla/mux
|
||||||
# github.com/kr/pretty v0.1.0
|
# github.com/kr/pretty v0.1.0
|
||||||
## explicit
|
## explicit
|
||||||
# golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553
|
|
||||||
## explicit
|
|
||||||
# golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8
|
# golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8
|
||||||
## explicit
|
## explicit
|
||||||
golang.org/x/sys/unix
|
golang.org/x/sys/unix
|
||||||
|
|
|
@ -19,7 +19,7 @@ import (
|
||||||
"github.com/adnanh/webhook/internal/middleware"
|
"github.com/adnanh/webhook/internal/middleware"
|
||||||
"github.com/adnanh/webhook/internal/pidfile"
|
"github.com/adnanh/webhook/internal/pidfile"
|
||||||
|
|
||||||
chimiddleware "github.com/go-chi/chi/middleware"
|
chimiddleware "github.com/go-chi/chi/v5/middleware"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
fsnotify "gopkg.in/fsnotify.v1"
|
fsnotify "gopkg.in/fsnotify.v1"
|
||||||
)
|
)
|
||||||
|
|
Loading…
Add table
Reference in a new issue