build(deps): bump github.com/labstack/echo/v4 from 4.6.1 to 4.7.2
Bumps [github.com/labstack/echo/v4](https://github.com/labstack/echo) from 4.6.1 to 4.7.2. - [Release notes](https://github.com/labstack/echo/releases) - [Changelog](https://github.com/labstack/echo/blob/master/CHANGELOG.md) - [Commits](https://github.com/labstack/echo/compare/v4.6.1...v4.7.2) --- updated-dependencies: - dependency-name: github.com/labstack/echo/v4 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Signed-off-by: Ondřej Budai <ondrej@budai.cz>
This commit is contained in:
parent
b5dcb40b05
commit
0688d2180b
39 changed files with 1371 additions and 616 deletions
5
go.mod
5
go.mod
|
|
@ -33,8 +33,8 @@ require (
|
||||||
github.com/jackc/pgx/v4 v4.15.0
|
github.com/jackc/pgx/v4 v4.15.0
|
||||||
github.com/julienschmidt/httprouter v1.3.0
|
github.com/julienschmidt/httprouter v1.3.0
|
||||||
github.com/kolo/xmlrpc v0.0.0-20201022064351-38db28db192b
|
github.com/kolo/xmlrpc v0.0.0-20201022064351-38db28db192b
|
||||||
github.com/labstack/echo/v4 v4.6.1
|
github.com/labstack/echo/v4 v4.7.2
|
||||||
github.com/labstack/gommon v0.3.0
|
github.com/labstack/gommon v0.3.1
|
||||||
github.com/openshift-online/ocm-sdk-go v0.1.214
|
github.com/openshift-online/ocm-sdk-go v0.1.214
|
||||||
github.com/oracle/oci-go-sdk/v54 v54.0.0
|
github.com/oracle/oci-go-sdk/v54 v54.0.0
|
||||||
github.com/prometheus/client_golang v1.12.0
|
github.com/prometheus/client_golang v1.12.0
|
||||||
|
|
@ -51,5 +51,4 @@ require (
|
||||||
google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c
|
google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c
|
||||||
google.golang.org/protobuf v1.27.1
|
google.golang.org/protobuf v1.27.1
|
||||||
gopkg.in/ini.v1 v1.66.4
|
gopkg.in/ini.v1 v1.66.4
|
||||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
|
||||||
)
|
)
|
||||||
|
|
|
||||||
15
go.sum
15
go.sum
|
|
@ -470,10 +470,11 @@ github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
|
||||||
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=
|
||||||
github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg=
|
github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg=
|
||||||
github.com/labstack/echo/v4 v4.6.1 h1:OMVsrnNFzYlGSdaiYGHbgWQnr+JM7NG+B9suCPie14M=
|
github.com/labstack/echo/v4 v4.7.2 h1:Kv2/p8OaQ+M6Ex4eGimg9b9e6icoxA42JSlOR3msKtI=
|
||||||
github.com/labstack/echo/v4 v4.6.1/go.mod h1:RnjgMWNDB9g/HucVWhQYNQP9PvbYf6adqftqryo7s9k=
|
github.com/labstack/echo/v4 v4.7.2/go.mod h1:xkCDAdFCIf8jsFQ5NnbK7oqaF/yU1A1X20Ltm0OvSks=
|
||||||
github.com/labstack/gommon v0.3.0 h1:JEeO0bvc78PKdyHxloTKiF8BD5iGrH8T6MSeGvSgob0=
|
|
||||||
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
|
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
|
||||||
|
github.com/labstack/gommon v0.3.1 h1:OomWaJXm7xR6L1HmEtGyQf26TEn7V6X88mktX9kee9o=
|
||||||
|
github.com/labstack/gommon v0.3.1/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=
|
||||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
|
|
@ -492,8 +493,9 @@ github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcncea
|
||||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||||
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||||
github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||||
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
|
|
||||||
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||||
|
github.com/mattn/go-colorable v0.1.11 h1:nQ+aFkoE2TMGc0b68U2OKSexC+eq46+XwZzWXHRmPYs=
|
||||||
|
github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
||||||
github.com/mattn/go-ieproxy v0.0.1 h1:qiyop7gCflfhwCzGyeT0gro3sF9AIg9HU98JORTkqfI=
|
github.com/mattn/go-ieproxy v0.0.1 h1:qiyop7gCflfhwCzGyeT0gro3sF9AIg9HU98JORTkqfI=
|
||||||
github.com/mattn/go-ieproxy v0.0.1/go.mod h1:pYabZ6IHcRpFh7vIaLfK7rdcWgFEb3SFJ6/gNWuh88E=
|
github.com/mattn/go-ieproxy v0.0.1/go.mod h1:pYabZ6IHcRpFh7vIaLfK7rdcWgFEb3SFJ6/gNWuh88E=
|
||||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||||
|
|
@ -815,7 +817,7 @@ golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT
|
||||||
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20210913180222-943fd674d43e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20211209124913-491a49abca63 h1:iocB37TsdFuN6IBRZ+ry36wrkoV51/tl5vOWqkcPGvY=
|
golang.org/x/net v0.0.0-20211209124913-491a49abca63 h1:iocB37TsdFuN6IBRZ+ry36wrkoV51/tl5vOWqkcPGvY=
|
||||||
golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
|
|
@ -923,8 +925,9 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||||
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210910150752-751e447fb3d0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20210917161153-d61c044b1678/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210917161153-d61c044b1678/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 h1:XfKQ4OlFl8okEOr5UvAqFRVj8pY/4yfcXrddB8qAbU0=
|
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 h1:XfKQ4OlFl8okEOr5UvAqFRVj8pY/4yfcXrddB8qAbU0=
|
||||||
|
|
|
||||||
68
vendor/github.com/labstack/echo/v4/CHANGELOG.md
generated
vendored
68
vendor/github.com/labstack/echo/v4/CHANGELOG.md
generated
vendored
|
|
@ -1,5 +1,73 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## v4.7.2 - 2022-03-16
|
||||||
|
|
||||||
|
**Fixes**
|
||||||
|
|
||||||
|
* Fix nil pointer exception when calling Start again after address binding error [#2131](https://github.com/labstack/echo/pull/2131)
|
||||||
|
* Fix CSRF middleware not being able to extract token from multipart/form-data form [#2136](https://github.com/labstack/echo/pull/2136)
|
||||||
|
* Fix Timeout middleware write race [#2126](https://github.com/labstack/echo/pull/2126)
|
||||||
|
|
||||||
|
**Enhancements**
|
||||||
|
|
||||||
|
* Recover middleware should not log panic for aborted handler [#2134](https://github.com/labstack/echo/pull/2134)
|
||||||
|
|
||||||
|
|
||||||
|
## v4.7.1 - 2022-03-13
|
||||||
|
|
||||||
|
**Fixes**
|
||||||
|
|
||||||
|
* Fix `e.Static`, `.File()`, `c.Attachment()` being picky with paths starting with `./`, `../` and `/` after 4.7.0 introduced echo.Filesystem support (Go1.16+) [#2123](https://github.com/labstack/echo/pull/2123)
|
||||||
|
|
||||||
|
**Enhancements**
|
||||||
|
|
||||||
|
* Remove some unused code [#2116](https://github.com/labstack/echo/pull/2116)
|
||||||
|
|
||||||
|
|
||||||
|
## v4.7.0 - 2022-03-01
|
||||||
|
|
||||||
|
**Enhancements**
|
||||||
|
|
||||||
|
* Add JWT, KeyAuth, CSRF multivalue extractors [#2060](https://github.com/labstack/echo/pull/2060)
|
||||||
|
* Add LogErrorFunc to recover middleware [#2072](https://github.com/labstack/echo/pull/2072)
|
||||||
|
* Add support for HEAD method query params binding [#2027](https://github.com/labstack/echo/pull/2027)
|
||||||
|
* Improve filesystem support with echo.FileFS, echo.StaticFS, group.FileFS, group.StaticFS [#2064](https://github.com/labstack/echo/pull/2064)
|
||||||
|
|
||||||
|
**Fixes**
|
||||||
|
|
||||||
|
* Fix X-Real-IP bug, improve tests [#2007](https://github.com/labstack/echo/pull/2007)
|
||||||
|
* Minor syntax fixes [#1994](https://github.com/labstack/echo/pull/1994), [#2102](https://github.com/labstack/echo/pull/2102), [#2102](https://github.com/labstack/echo/pull/2102)
|
||||||
|
|
||||||
|
**General**
|
||||||
|
|
||||||
|
* Add cache-control and connection headers [#2103](https://github.com/labstack/echo/pull/2103)
|
||||||
|
* Add Retry-After header constant [#2078](https://github.com/labstack/echo/pull/2078)
|
||||||
|
* Upgrade `go` directive in `go.mod` to 1.17 [#2049](https://github.com/labstack/echo/pull/2049)
|
||||||
|
* Add Pagoda [#2077](https://github.com/labstack/echo/pull/2077) and Souin [#2069](https://github.com/labstack/echo/pull/2069) to 3rd-party middlewares in README
|
||||||
|
|
||||||
|
## v4.6.3 - 2022-01-10
|
||||||
|
|
||||||
|
**Fixes**
|
||||||
|
|
||||||
|
* Fixed Echo version number in greeting message which was not incremented to `4.6.2` [#2066](https://github.com/labstack/echo/issues/2066)
|
||||||
|
|
||||||
|
|
||||||
|
## v4.6.2 - 2022-01-08
|
||||||
|
|
||||||
|
**Fixes**
|
||||||
|
|
||||||
|
* Fixed route containing escaped colon should be matchable but is not matched to request path [#2047](https://github.com/labstack/echo/pull/2047)
|
||||||
|
* Fixed a problem that returned wrong content-encoding when the gzip compressed content was empty. [#1921](https://github.com/labstack/echo/pull/1921)
|
||||||
|
* Update (test) dependencies [#2021](https://github.com/labstack/echo/pull/2021)
|
||||||
|
|
||||||
|
|
||||||
|
**Enhancements**
|
||||||
|
|
||||||
|
* Add support for configurable target header for the request_id middleware [#2040](https://github.com/labstack/echo/pull/2040)
|
||||||
|
* Change decompress middleware to use stream decompression instead of buffering [#2018](https://github.com/labstack/echo/pull/2018)
|
||||||
|
* Documentation updates
|
||||||
|
|
||||||
|
|
||||||
## v4.6.1 - 2021-09-26
|
## v4.6.1 - 2021-09-26
|
||||||
|
|
||||||
**Enhancements**
|
**Enhancements**
|
||||||
|
|
|
||||||
18
vendor/github.com/labstack/echo/v4/README.md
generated
vendored
18
vendor/github.com/labstack/echo/v4/README.md
generated
vendored
|
|
@ -5,7 +5,6 @@
|
||||||
[](https://goreportcard.com/report/github.com/labstack/echo)
|
[](https://goreportcard.com/report/github.com/labstack/echo)
|
||||||
[](https://travis-ci.org/labstack/echo)
|
[](https://travis-ci.org/labstack/echo)
|
||||||
[](https://codecov.io/gh/labstack/echo)
|
[](https://codecov.io/gh/labstack/echo)
|
||||||
[](https://gitter.im/labstack/echo)
|
|
||||||
[](https://github.com/labstack/echo/discussions)
|
[](https://github.com/labstack/echo/discussions)
|
||||||
[](https://twitter.com/labstack)
|
[](https://twitter.com/labstack)
|
||||||
[](https://raw.githubusercontent.com/labstack/echo/master/LICENSE)
|
[](https://raw.githubusercontent.com/labstack/echo/master/LICENSE)
|
||||||
|
|
@ -66,9 +65,9 @@ go get github.com/labstack/echo/v4
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
"github.com/labstack/echo/v4/middleware"
|
"github.com/labstack/echo/v4/middleware"
|
||||||
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
@ -92,10 +91,23 @@ func hello(c echo.Context) error {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
# Third-party middlewares
|
||||||
|
|
||||||
|
| Repository | Description |
|
||||||
|
|------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
|
| [github.com/labstack/echo-contrib](https://github.com/labstack/echo-contrib) | (by Echo team) [casbin](https://github.com/casbin/casbin), [gorilla/sessions](https://github.com/gorilla/sessions), [jaegertracing](github.com/uber/jaeger-client-go), [prometheus](https://github.com/prometheus/client_golang/), [pprof](https://pkg.go.dev/net/http/pprof), [zipkin](https://github.com/openzipkin/zipkin-go) middlewares |
|
||||||
|
| [deepmap/oapi-codegen](https://github.com/deepmap/oapi-codegen) | Automatically generate RESTful API documentation with [OpenAPI](https://swagger.io/specification/) Client and Server Code Generator |
|
||||||
|
| [github.com/swaggo/echo-swagger](https://github.com/swaggo/echo-swagger) | Automatically generate RESTful API documentation with [Swagger](https://swagger.io/) 2.0. |
|
||||||
|
| [github.com/ziflex/lecho](https://github.com/ziflex/lecho) | [Zerolog](https://github.com/rs/zerolog) logging library wrapper for Echo logger interface. |
|
||||||
|
| [github.com/brpaz/echozap](https://github.com/brpaz/echozap) | Uber´s [Zap](https://github.com/uber-go/zap) logging library wrapper for Echo logger interface. |
|
||||||
|
| [github.com/darkweak/souin/plugins/echo](https://github.com/darkweak/souin/tree/master/plugins/echo) | HTTP cache system based on [Souin](https://github.com/darkweak/souin) to automatically get your endpoints cached. It supports some distributed and non-distributed storage systems depending your needs. |
|
||||||
|
| [github.com/mikestefanello/pagoda](https://github.com/mikestefanello/pagoda) | Rapid, easy full-stack web development starter kit built with Echo.
|
||||||
|
|
||||||
|
Please send a PR to add your own library here.
|
||||||
|
|
||||||
## Help
|
## Help
|
||||||
|
|
||||||
- [Forum](https://github.com/labstack/echo/discussions)
|
- [Forum](https://github.com/labstack/echo/discussions)
|
||||||
- [Chat](https://gitter.im/labstack/echo)
|
|
||||||
|
|
||||||
## Contribute
|
## Contribute
|
||||||
|
|
||||||
|
|
|
||||||
10
vendor/github.com/labstack/echo/v4/bind.go
generated
vendored
10
vendor/github.com/labstack/echo/v4/bind.go
generated
vendored
|
|
@ -111,11 +111,11 @@ func (b *DefaultBinder) Bind(i interface{}, c Context) (err error) {
|
||||||
if err := b.BindPathParams(c, i); err != nil {
|
if err := b.BindPathParams(c, i); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Issue #1670 - Query params are binded only for GET/DELETE and NOT for usual request with body (POST/PUT/PATCH)
|
// Only bind query parameters for GET/DELETE/HEAD to avoid unexpected behavior with destination struct binding from body.
|
||||||
// Reasoning here is that parameters in query and bind destination struct could have UNEXPECTED matches and results due that.
|
// For example a request URL `&id=1&lang=en` with body `{"id":100,"lang":"de"}` would lead to precedence issues.
|
||||||
// i.e. is `&id=1&lang=en` from URL same as `{"id":100,"lang":"de"}` request body and which one should have priority when binding.
|
// The HTTP method check restores pre-v4.1.11 behavior to avoid these problems (see issue #1670)
|
||||||
// This HTTP method check restores pre v4.1.11 behavior and avoids different problems when query is mixed with body
|
method := c.Request().Method
|
||||||
if c.Request().Method == http.MethodGet || c.Request().Method == http.MethodDelete {
|
if method == http.MethodGet || method == http.MethodDelete || method == http.MethodHead {
|
||||||
if err = b.BindQueryParams(c, i); err != nil {
|
if err = b.BindQueryParams(c, i); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
32
vendor/github.com/labstack/echo/v4/context.go
generated
vendored
32
vendor/github.com/labstack/echo/v4/context.go
generated
vendored
|
|
@ -9,8 +9,6 @@ import (
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
@ -210,6 +208,13 @@ type (
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// ContextKeyHeaderAllow is set by Router for getting value for `Allow` header in later stages of handler call chain.
|
||||||
|
// Allow header is mandatory for status 405 (method not found) and useful for OPTIONS method requests.
|
||||||
|
// It is added to context only when Router does not find matching method handler for request.
|
||||||
|
ContextKeyHeaderAllow = "echo_header_allow"
|
||||||
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
defaultMemory = 32 << 20 // 32 MB
|
defaultMemory = 32 << 20 // 32 MB
|
||||||
indexPage = "index.html"
|
indexPage = "index.html"
|
||||||
|
|
@ -562,29 +567,6 @@ func (c *context) Stream(code int, contentType string, r io.Reader) (err error)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *context) File(file string) (err error) {
|
|
||||||
f, err := os.Open(file)
|
|
||||||
if err != nil {
|
|
||||||
return NotFoundHandler(c)
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
|
|
||||||
fi, _ := f.Stat()
|
|
||||||
if fi.IsDir() {
|
|
||||||
file = filepath.Join(file, indexPage)
|
|
||||||
f, err = os.Open(file)
|
|
||||||
if err != nil {
|
|
||||||
return NotFoundHandler(c)
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
if fi, err = f.Stat(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
http.ServeContent(c.Response(), c.Request(), fi.Name(), fi.ModTime(), f)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *context) Attachment(file, name string) error {
|
func (c *context) Attachment(file, name string) error {
|
||||||
return c.contentDisposition(file, name, "attachment")
|
return c.contentDisposition(file, name, "attachment")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
33
vendor/github.com/labstack/echo/v4/context_fs.go
generated
vendored
Normal file
33
vendor/github.com/labstack/echo/v4/context_fs.go
generated
vendored
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
//go:build !go1.16
|
||||||
|
// +build !go1.16
|
||||||
|
|
||||||
|
package echo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *context) File(file string) (err error) {
|
||||||
|
f, err := os.Open(file)
|
||||||
|
if err != nil {
|
||||||
|
return NotFoundHandler(c)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
fi, _ := f.Stat()
|
||||||
|
if fi.IsDir() {
|
||||||
|
file = filepath.Join(file, indexPage)
|
||||||
|
f, err = os.Open(file)
|
||||||
|
if err != nil {
|
||||||
|
return NotFoundHandler(c)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
if fi, err = f.Stat(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
http.ServeContent(c.Response(), c.Request(), fi.Name(), fi.ModTime(), f)
|
||||||
|
return
|
||||||
|
}
|
||||||
52
vendor/github.com/labstack/echo/v4/context_fs_go1.16.go
generated
vendored
Normal file
52
vendor/github.com/labstack/echo/v4/context_fs_go1.16.go
generated
vendored
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
//go:build go1.16
|
||||||
|
// +build go1.16
|
||||||
|
|
||||||
|
package echo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"io/fs"
|
||||||
|
"net/http"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *context) File(file string) error {
|
||||||
|
return fsFile(c, file, c.echo.Filesystem)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FileFS serves file from given file system.
|
||||||
|
//
|
||||||
|
// When dealing with `embed.FS` use `fs := echo.MustSubFS(fs, "rootDirectory") to create sub fs which uses necessary
|
||||||
|
// prefix for directory path. This is necessary as `//go:embed assets/images` embeds files with paths
|
||||||
|
// including `assets/images` as their prefix.
|
||||||
|
func (c *context) FileFS(file string, filesystem fs.FS) error {
|
||||||
|
return fsFile(c, file, filesystem)
|
||||||
|
}
|
||||||
|
|
||||||
|
func fsFile(c Context, file string, filesystem fs.FS) error {
|
||||||
|
f, err := filesystem.Open(file)
|
||||||
|
if err != nil {
|
||||||
|
return ErrNotFound
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
fi, _ := f.Stat()
|
||||||
|
if fi.IsDir() {
|
||||||
|
file = filepath.ToSlash(filepath.Join(file, indexPage)) // ToSlash is necessary for Windows. fs.Open and os.Open are different in that aspect.
|
||||||
|
f, err = filesystem.Open(file)
|
||||||
|
if err != nil {
|
||||||
|
return ErrNotFound
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
if fi, err = f.Stat(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ff, ok := f.(io.ReadSeeker)
|
||||||
|
if !ok {
|
||||||
|
return errors.New("file does not implement io.ReadSeeker")
|
||||||
|
}
|
||||||
|
http.ServeContent(c.Response(), c.Request(), fi.Name(), fi.ModTime(), ff)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
94
vendor/github.com/labstack/echo/v4/echo.go
generated
vendored
94
vendor/github.com/labstack/echo/v4/echo.go
generated
vendored
|
|
@ -47,9 +47,6 @@ import (
|
||||||
stdLog "log"
|
stdLog "log"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"runtime"
|
"runtime"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
@ -66,6 +63,7 @@ import (
|
||||||
type (
|
type (
|
||||||
// Echo is the top-level framework instance.
|
// Echo is the top-level framework instance.
|
||||||
Echo struct {
|
Echo struct {
|
||||||
|
filesystem
|
||||||
common
|
common
|
||||||
// startupMutex is mutex to lock Echo instance access during server configuration and startup. Useful for to get
|
// startupMutex is mutex to lock Echo instance access during server configuration and startup. Useful for to get
|
||||||
// listener address info (on which interface/port was listener binded) without having data races.
|
// listener address info (on which interface/port was listener binded) without having data races.
|
||||||
|
|
@ -77,7 +75,6 @@ type (
|
||||||
maxParam *int
|
maxParam *int
|
||||||
router *Router
|
router *Router
|
||||||
routers map[string]*Router
|
routers map[string]*Router
|
||||||
notFoundHandler HandlerFunc
|
|
||||||
pool sync.Pool
|
pool sync.Pool
|
||||||
Server *http.Server
|
Server *http.Server
|
||||||
TLSServer *http.Server
|
TLSServer *http.Server
|
||||||
|
|
@ -113,10 +110,10 @@ type (
|
||||||
}
|
}
|
||||||
|
|
||||||
// MiddlewareFunc defines a function to process middleware.
|
// MiddlewareFunc defines a function to process middleware.
|
||||||
MiddlewareFunc func(HandlerFunc) HandlerFunc
|
MiddlewareFunc func(next HandlerFunc) HandlerFunc
|
||||||
|
|
||||||
// HandlerFunc defines a function to serve HTTP requests.
|
// HandlerFunc defines a function to serve HTTP requests.
|
||||||
HandlerFunc func(Context) error
|
HandlerFunc func(c Context) error
|
||||||
|
|
||||||
// HTTPErrorHandler is a centralized HTTP error handler.
|
// HTTPErrorHandler is a centralized HTTP error handler.
|
||||||
HTTPErrorHandler func(error, Context)
|
HTTPErrorHandler func(error, Context)
|
||||||
|
|
@ -190,8 +187,12 @@ const (
|
||||||
|
|
||||||
// Headers
|
// Headers
|
||||||
const (
|
const (
|
||||||
HeaderAccept = "Accept"
|
HeaderAccept = "Accept"
|
||||||
HeaderAcceptEncoding = "Accept-Encoding"
|
HeaderAcceptEncoding = "Accept-Encoding"
|
||||||
|
// HeaderAllow is the name of the "Allow" header field used to list the set of methods
|
||||||
|
// advertised as supported by the target resource. Returning an Allow header is mandatory
|
||||||
|
// for status 405 (method not found) and useful for the OPTIONS method in responses.
|
||||||
|
// See RFC 7231: https://datatracker.ietf.org/doc/html/rfc7231#section-7.4.1
|
||||||
HeaderAllow = "Allow"
|
HeaderAllow = "Allow"
|
||||||
HeaderAuthorization = "Authorization"
|
HeaderAuthorization = "Authorization"
|
||||||
HeaderContentDisposition = "Content-Disposition"
|
HeaderContentDisposition = "Content-Disposition"
|
||||||
|
|
@ -203,6 +204,7 @@ const (
|
||||||
HeaderIfModifiedSince = "If-Modified-Since"
|
HeaderIfModifiedSince = "If-Modified-Since"
|
||||||
HeaderLastModified = "Last-Modified"
|
HeaderLastModified = "Last-Modified"
|
||||||
HeaderLocation = "Location"
|
HeaderLocation = "Location"
|
||||||
|
HeaderRetryAfter = "Retry-After"
|
||||||
HeaderUpgrade = "Upgrade"
|
HeaderUpgrade = "Upgrade"
|
||||||
HeaderVary = "Vary"
|
HeaderVary = "Vary"
|
||||||
HeaderWWWAuthenticate = "WWW-Authenticate"
|
HeaderWWWAuthenticate = "WWW-Authenticate"
|
||||||
|
|
@ -212,11 +214,14 @@ const (
|
||||||
HeaderXForwardedSsl = "X-Forwarded-Ssl"
|
HeaderXForwardedSsl = "X-Forwarded-Ssl"
|
||||||
HeaderXUrlScheme = "X-Url-Scheme"
|
HeaderXUrlScheme = "X-Url-Scheme"
|
||||||
HeaderXHTTPMethodOverride = "X-HTTP-Method-Override"
|
HeaderXHTTPMethodOverride = "X-HTTP-Method-Override"
|
||||||
HeaderXRealIP = "X-Real-IP"
|
HeaderXRealIP = "X-Real-Ip"
|
||||||
HeaderXRequestID = "X-Request-ID"
|
HeaderXRequestID = "X-Request-Id"
|
||||||
|
HeaderXCorrelationID = "X-Correlation-Id"
|
||||||
HeaderXRequestedWith = "X-Requested-With"
|
HeaderXRequestedWith = "X-Requested-With"
|
||||||
HeaderServer = "Server"
|
HeaderServer = "Server"
|
||||||
HeaderOrigin = "Origin"
|
HeaderOrigin = "Origin"
|
||||||
|
HeaderCacheControl = "Cache-Control"
|
||||||
|
HeaderConnection = "Connection"
|
||||||
|
|
||||||
// Access control
|
// Access control
|
||||||
HeaderAccessControlRequestMethod = "Access-Control-Request-Method"
|
HeaderAccessControlRequestMethod = "Access-Control-Request-Method"
|
||||||
|
|
@ -241,7 +246,7 @@ const (
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// Version of Echo
|
// Version of Echo
|
||||||
Version = "4.6.1"
|
Version = "4.7.2"
|
||||||
website = "https://echo.labstack.com"
|
website = "https://echo.labstack.com"
|
||||||
// http://patorjk.com/software/taag/#p=display&f=Small%20Slant&t=Echo
|
// http://patorjk.com/software/taag/#p=display&f=Small%20Slant&t=Echo
|
||||||
banner = `
|
banner = `
|
||||||
|
|
@ -301,6 +306,12 @@ var (
|
||||||
}
|
}
|
||||||
|
|
||||||
MethodNotAllowedHandler = func(c Context) error {
|
MethodNotAllowedHandler = func(c Context) error {
|
||||||
|
// See RFC 7231 section 7.4.1: An origin server MUST generate an Allow field in a 405 (Method Not Allowed)
|
||||||
|
// response and MAY do so in any other response. For disabled resources an empty Allow header may be returned
|
||||||
|
routerAllowMethods, ok := c.Get(ContextKeyHeaderAllow).(string)
|
||||||
|
if ok && routerAllowMethods != "" {
|
||||||
|
c.Response().Header().Set(HeaderAllow, routerAllowMethods)
|
||||||
|
}
|
||||||
return ErrMethodNotAllowed
|
return ErrMethodNotAllowed
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
@ -308,8 +319,9 @@ var (
|
||||||
// New creates an instance of Echo.
|
// New creates an instance of Echo.
|
||||||
func New() (e *Echo) {
|
func New() (e *Echo) {
|
||||||
e = &Echo{
|
e = &Echo{
|
||||||
Server: new(http.Server),
|
filesystem: createFilesystem(),
|
||||||
TLSServer: new(http.Server),
|
Server: new(http.Server),
|
||||||
|
TLSServer: new(http.Server),
|
||||||
AutoTLSManager: autocert.Manager{
|
AutoTLSManager: autocert.Manager{
|
||||||
Prompt: autocert.AcceptTOS,
|
Prompt: autocert.AcceptTOS,
|
||||||
},
|
},
|
||||||
|
|
@ -488,50 +500,6 @@ func (e *Echo) Match(methods []string, path string, handler HandlerFunc, middlew
|
||||||
return routes
|
return routes
|
||||||
}
|
}
|
||||||
|
|
||||||
// Static registers a new route with path prefix to serve static files from the
|
|
||||||
// provided root directory.
|
|
||||||
func (e *Echo) Static(prefix, root string) *Route {
|
|
||||||
if root == "" {
|
|
||||||
root = "." // For security we want to restrict to CWD.
|
|
||||||
}
|
|
||||||
return e.static(prefix, root, e.GET)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (common) static(prefix, root string, get func(string, HandlerFunc, ...MiddlewareFunc) *Route) *Route {
|
|
||||||
h := func(c Context) error {
|
|
||||||
p, err := url.PathUnescape(c.Param("*"))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
name := filepath.Join(root, filepath.Clean("/"+p)) // "/"+ for security
|
|
||||||
fi, err := os.Stat(name)
|
|
||||||
if err != nil {
|
|
||||||
// The access path does not exist
|
|
||||||
return NotFoundHandler(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the request is for a directory and does not end with "/"
|
|
||||||
p = c.Request().URL.Path // path must not be empty.
|
|
||||||
if fi.IsDir() && p[len(p)-1] != '/' {
|
|
||||||
// Redirect to ends with "/"
|
|
||||||
return c.Redirect(http.StatusMovedPermanently, p+"/")
|
|
||||||
}
|
|
||||||
return c.File(name)
|
|
||||||
}
|
|
||||||
// Handle added routes based on trailing slash:
|
|
||||||
// /prefix => exact route "/prefix" + any route "/prefix/*"
|
|
||||||
// /prefix/ => only any route "/prefix/*"
|
|
||||||
if prefix != "" {
|
|
||||||
if prefix[len(prefix)-1] == '/' {
|
|
||||||
// Only add any route for intentional trailing slash
|
|
||||||
return get(prefix+"*", h)
|
|
||||||
}
|
|
||||||
get(prefix, h)
|
|
||||||
}
|
|
||||||
return get(prefix+"/*", h)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (common) file(path, file string, get func(string, HandlerFunc, ...MiddlewareFunc) *Route,
|
func (common) file(path, file string, get func(string, HandlerFunc, ...MiddlewareFunc) *Route,
|
||||||
m ...MiddlewareFunc) *Route {
|
m ...MiddlewareFunc) *Route {
|
||||||
return get(path, func(c Context) error {
|
return get(path, func(c Context) error {
|
||||||
|
|
@ -642,7 +610,7 @@ func (e *Echo) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
// Acquire context
|
// Acquire context
|
||||||
c := e.pool.Get().(*context)
|
c := e.pool.Get().(*context)
|
||||||
c.Reset(r, w)
|
c.Reset(r, w)
|
||||||
h := NotFoundHandler
|
var h func(Context) error
|
||||||
|
|
||||||
if e.premiddleware == nil {
|
if e.premiddleware == nil {
|
||||||
e.findRouter(r.Host).Find(r.Method, GetPath(r), c)
|
e.findRouter(r.Host).Find(r.Method, GetPath(r), c)
|
||||||
|
|
@ -764,7 +732,7 @@ func (e *Echo) StartServer(s *http.Server) (err error) {
|
||||||
return s.Serve(e.Listener)
|
return s.Serve(e.Listener)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Echo) configureServer(s *http.Server) (err error) {
|
func (e *Echo) configureServer(s *http.Server) error {
|
||||||
// Setup
|
// Setup
|
||||||
e.colorer.SetOutput(e.Logger.Output())
|
e.colorer.SetOutput(e.Logger.Output())
|
||||||
s.ErrorLog = e.StdLogger
|
s.ErrorLog = e.StdLogger
|
||||||
|
|
@ -779,10 +747,11 @@ func (e *Echo) configureServer(s *http.Server) (err error) {
|
||||||
|
|
||||||
if s.TLSConfig == nil {
|
if s.TLSConfig == nil {
|
||||||
if e.Listener == nil {
|
if e.Listener == nil {
|
||||||
e.Listener, err = newListener(s.Addr, e.ListenerNetwork)
|
l, err := newListener(s.Addr, e.ListenerNetwork)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
e.Listener = l
|
||||||
}
|
}
|
||||||
if !e.HidePort {
|
if !e.HidePort {
|
||||||
e.colorer.Printf("⇨ http server started on %s\n", e.colorer.Green(e.Listener.Addr()))
|
e.colorer.Printf("⇨ http server started on %s\n", e.colorer.Green(e.Listener.Addr()))
|
||||||
|
|
@ -823,7 +792,7 @@ func (e *Echo) TLSListenerAddr() net.Addr {
|
||||||
}
|
}
|
||||||
|
|
||||||
// StartH2CServer starts a custom http/2 server with h2c (HTTP/2 Cleartext).
|
// StartH2CServer starts a custom http/2 server with h2c (HTTP/2 Cleartext).
|
||||||
func (e *Echo) StartH2CServer(address string, h2s *http2.Server) (err error) {
|
func (e *Echo) StartH2CServer(address string, h2s *http2.Server) error {
|
||||||
e.startupMutex.Lock()
|
e.startupMutex.Lock()
|
||||||
// Setup
|
// Setup
|
||||||
s := e.Server
|
s := e.Server
|
||||||
|
|
@ -840,11 +809,12 @@ func (e *Echo) StartH2CServer(address string, h2s *http2.Server) (err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if e.Listener == nil {
|
if e.Listener == nil {
|
||||||
e.Listener, err = newListener(s.Addr, e.ListenerNetwork)
|
l, err := newListener(s.Addr, e.ListenerNetwork)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
e.startupMutex.Unlock()
|
e.startupMutex.Unlock()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
e.Listener = l
|
||||||
}
|
}
|
||||||
if !e.HidePort {
|
if !e.HidePort {
|
||||||
e.colorer.Printf("⇨ http server started on %s\n", e.colorer.Green(e.Listener.Addr()))
|
e.colorer.Printf("⇨ http server started on %s\n", e.colorer.Green(e.Listener.Addr()))
|
||||||
|
|
|
||||||
62
vendor/github.com/labstack/echo/v4/echo_fs.go
generated
vendored
Normal file
62
vendor/github.com/labstack/echo/v4/echo_fs.go
generated
vendored
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
//go:build !go1.16
|
||||||
|
// +build !go1.16
|
||||||
|
|
||||||
|
package echo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
type filesystem struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func createFilesystem() filesystem {
|
||||||
|
return filesystem{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Static registers a new route with path prefix to serve static files from the
|
||||||
|
// provided root directory.
|
||||||
|
func (e *Echo) Static(prefix, root string) *Route {
|
||||||
|
if root == "" {
|
||||||
|
root = "." // For security we want to restrict to CWD.
|
||||||
|
}
|
||||||
|
return e.static(prefix, root, e.GET)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (common) static(prefix, root string, get func(string, HandlerFunc, ...MiddlewareFunc) *Route) *Route {
|
||||||
|
h := func(c Context) error {
|
||||||
|
p, err := url.PathUnescape(c.Param("*"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
name := filepath.Join(root, filepath.Clean("/"+p)) // "/"+ for security
|
||||||
|
fi, err := os.Stat(name)
|
||||||
|
if err != nil {
|
||||||
|
// The access path does not exist
|
||||||
|
return NotFoundHandler(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the request is for a directory and does not end with "/"
|
||||||
|
p = c.Request().URL.Path // path must not be empty.
|
||||||
|
if fi.IsDir() && p[len(p)-1] != '/' {
|
||||||
|
// Redirect to ends with "/"
|
||||||
|
return c.Redirect(http.StatusMovedPermanently, p+"/")
|
||||||
|
}
|
||||||
|
return c.File(name)
|
||||||
|
}
|
||||||
|
// Handle added routes based on trailing slash:
|
||||||
|
// /prefix => exact route "/prefix" + any route "/prefix/*"
|
||||||
|
// /prefix/ => only any route "/prefix/*"
|
||||||
|
if prefix != "" {
|
||||||
|
if prefix[len(prefix)-1] == '/' {
|
||||||
|
// Only add any route for intentional trailing slash
|
||||||
|
return get(prefix+"*", h)
|
||||||
|
}
|
||||||
|
get(prefix, h)
|
||||||
|
}
|
||||||
|
return get(prefix+"/*", h)
|
||||||
|
}
|
||||||
169
vendor/github.com/labstack/echo/v4/echo_fs_go1.16.go
generated
vendored
Normal file
169
vendor/github.com/labstack/echo/v4/echo_fs_go1.16.go
generated
vendored
Normal file
|
|
@ -0,0 +1,169 @@
|
||||||
|
//go:build go1.16
|
||||||
|
// +build go1.16
|
||||||
|
|
||||||
|
package echo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/fs"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type filesystem struct {
|
||||||
|
// Filesystem is file system used by Static and File handlers to access files.
|
||||||
|
// Defaults to os.DirFS(".")
|
||||||
|
//
|
||||||
|
// When dealing with `embed.FS` use `fs := echo.MustSubFS(fs, "rootDirectory") to create sub fs which uses necessary
|
||||||
|
// prefix for directory path. This is necessary as `//go:embed assets/images` embeds files with paths
|
||||||
|
// including `assets/images` as their prefix.
|
||||||
|
Filesystem fs.FS
|
||||||
|
}
|
||||||
|
|
||||||
|
func createFilesystem() filesystem {
|
||||||
|
return filesystem{
|
||||||
|
Filesystem: newDefaultFS(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Static registers a new route with path prefix to serve static files from the provided root directory.
|
||||||
|
func (e *Echo) Static(pathPrefix, fsRoot string) *Route {
|
||||||
|
subFs := MustSubFS(e.Filesystem, fsRoot)
|
||||||
|
return e.Add(
|
||||||
|
http.MethodGet,
|
||||||
|
pathPrefix+"*",
|
||||||
|
StaticDirectoryHandler(subFs, false),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StaticFS registers a new route with path prefix to serve static files from the provided file system.
|
||||||
|
//
|
||||||
|
// When dealing with `embed.FS` use `fs := echo.MustSubFS(fs, "rootDirectory") to create sub fs which uses necessary
|
||||||
|
// prefix for directory path. This is necessary as `//go:embed assets/images` embeds files with paths
|
||||||
|
// including `assets/images` as their prefix.
|
||||||
|
func (e *Echo) StaticFS(pathPrefix string, filesystem fs.FS) *Route {
|
||||||
|
return e.Add(
|
||||||
|
http.MethodGet,
|
||||||
|
pathPrefix+"*",
|
||||||
|
StaticDirectoryHandler(filesystem, false),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StaticDirectoryHandler creates handler function to serve files from provided file system
|
||||||
|
// When disablePathUnescaping is set then file name from path is not unescaped and is served as is.
|
||||||
|
func StaticDirectoryHandler(fileSystem fs.FS, disablePathUnescaping bool) HandlerFunc {
|
||||||
|
return func(c Context) error {
|
||||||
|
p := c.Param("*")
|
||||||
|
if !disablePathUnescaping { // when router is already unescaping we do not want to do is twice
|
||||||
|
tmpPath, err := url.PathUnescape(p)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to unescape path variable: %w", err)
|
||||||
|
}
|
||||||
|
p = tmpPath
|
||||||
|
}
|
||||||
|
|
||||||
|
// fs.FS.Open() already assumes that file names are relative to FS root path and considers name with prefix `/` as invalid
|
||||||
|
name := filepath.ToSlash(filepath.Clean(strings.TrimPrefix(p, "/")))
|
||||||
|
fi, err := fs.Stat(fileSystem, name)
|
||||||
|
if err != nil {
|
||||||
|
return ErrNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the request is for a directory and does not end with "/"
|
||||||
|
p = c.Request().URL.Path // path must not be empty.
|
||||||
|
if fi.IsDir() && len(p) > 0 && p[len(p)-1] != '/' {
|
||||||
|
// Redirect to ends with "/"
|
||||||
|
return c.Redirect(http.StatusMovedPermanently, p+"/")
|
||||||
|
}
|
||||||
|
return fsFile(c, name, fileSystem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FileFS registers a new route with path to serve file from the provided file system.
|
||||||
|
func (e *Echo) FileFS(path, file string, filesystem fs.FS, m ...MiddlewareFunc) *Route {
|
||||||
|
return e.GET(path, StaticFileHandler(file, filesystem), m...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StaticFileHandler creates handler function to serve file from provided file system
|
||||||
|
func StaticFileHandler(file string, filesystem fs.FS) HandlerFunc {
|
||||||
|
return func(c Context) error {
|
||||||
|
return fsFile(c, file, filesystem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// defaultFS exists to preserve pre v4.7.0 behaviour where files were open by `os.Open`.
|
||||||
|
// v4.7 introduced `echo.Filesystem` field which is Go1.16+ `fs.Fs` interface.
|
||||||
|
// Difference between `os.Open` and `fs.Open` is that FS does not allow opening path that start with `.`, `..` or `/`
|
||||||
|
// etc. For example previously you could have `../images` in your application but `fs := os.DirFS("./")` would not
|
||||||
|
// allow you to use `fs.Open("../images")` and this would break all old applications that rely on being able to
|
||||||
|
// traverse up from current executable run path.
|
||||||
|
// NB: private because you really should use fs.FS implementation instances
|
||||||
|
type defaultFS struct {
|
||||||
|
prefix string
|
||||||
|
fs fs.FS
|
||||||
|
}
|
||||||
|
|
||||||
|
func newDefaultFS() *defaultFS {
|
||||||
|
dir, _ := os.Getwd()
|
||||||
|
return &defaultFS{
|
||||||
|
prefix: dir,
|
||||||
|
fs: nil,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fs defaultFS) Open(name string) (fs.File, error) {
|
||||||
|
if fs.fs == nil {
|
||||||
|
return os.Open(name)
|
||||||
|
}
|
||||||
|
return fs.fs.Open(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func subFS(currentFs fs.FS, root string) (fs.FS, error) {
|
||||||
|
root = filepath.ToSlash(filepath.Clean(root)) // note: fs.FS operates only with slashes. `ToSlash` is necessary for Windows
|
||||||
|
if dFS, ok := currentFs.(*defaultFS); ok {
|
||||||
|
// we need to make exception for `defaultFS` instances as it interprets root prefix differently from fs.FS.
|
||||||
|
// fs.Fs.Open does not like relative paths ("./", "../") and absolute paths at all but prior echo.Filesystem we
|
||||||
|
// were able to use paths like `./myfile.log`, `/etc/hosts` and these would work fine with `os.Open` but not with fs.Fs
|
||||||
|
if isRelativePath(root) {
|
||||||
|
root = filepath.Join(dFS.prefix, root)
|
||||||
|
}
|
||||||
|
return &defaultFS{
|
||||||
|
prefix: root,
|
||||||
|
fs: os.DirFS(root),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
return fs.Sub(currentFs, root)
|
||||||
|
}
|
||||||
|
|
||||||
|
func isRelativePath(path string) bool {
|
||||||
|
if path == "" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if path[0] == '/' {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if runtime.GOOS == "windows" && strings.IndexByte(path, ':') != -1 {
|
||||||
|
// https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file?redirectedfrom=MSDN#file_and_directory_names
|
||||||
|
// https://docs.microsoft.com/en-us/dotnet/standard/io/file-path-formats
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustSubFS creates sub FS from current filesystem or panic on failure.
|
||||||
|
// Panic happens when `fsRoot` contains invalid path according to `fs.ValidPath` rules.
|
||||||
|
//
|
||||||
|
// MustSubFS is helpful when dealing with `embed.FS` because for example `//go:embed assets/images` embeds files with
|
||||||
|
// paths including `assets/images` as their prefix. In that case use `fs := echo.MustSubFS(fs, "rootDirectory") to
|
||||||
|
// create sub fs which uses necessary prefix for directory path.
|
||||||
|
func MustSubFS(currentFs fs.FS, fsRoot string) fs.FS {
|
||||||
|
subFs, err := subFS(currentFs, fsRoot)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("can not create sub FS, invalid root given, err: %w", err))
|
||||||
|
}
|
||||||
|
return subFs
|
||||||
|
}
|
||||||
23
vendor/github.com/labstack/echo/v4/go.mod
generated
vendored
23
vendor/github.com/labstack/echo/v4/go.mod
generated
vendored
|
|
@ -1,17 +1,24 @@
|
||||||
module github.com/labstack/echo/v4
|
module github.com/labstack/echo/v4
|
||||||
|
|
||||||
go 1.15
|
go 1.17
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/golang-jwt/jwt v3.2.2+incompatible
|
github.com/golang-jwt/jwt v3.2.2+incompatible
|
||||||
github.com/labstack/gommon v0.3.0
|
github.com/labstack/gommon v0.3.1
|
||||||
github.com/mattn/go-colorable v0.1.8 // indirect
|
github.com/stretchr/testify v1.7.0
|
||||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
|
||||||
github.com/stretchr/testify v1.4.0
|
|
||||||
github.com/valyala/fasttemplate v1.2.1
|
github.com/valyala/fasttemplate v1.2.1
|
||||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5
|
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5
|
||||||
golang.org/x/net v0.0.0-20210913180222-943fd674d43e
|
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f
|
||||||
golang.org/x/sys v0.0.0-20210910150752-751e447fb3d0 // indirect
|
|
||||||
golang.org/x/text v0.3.7 // indirect
|
|
||||||
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324
|
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324
|
||||||
)
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
|
github.com/mattn/go-colorable v0.1.11 // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
|
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||||
|
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b // indirect
|
||||||
|
golang.org/x/text v0.3.7 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
||||||
|
)
|
||||||
|
|
|
||||||
38
vendor/github.com/labstack/echo/v4/go.sum
generated
vendored
38
vendor/github.com/labstack/echo/v4/go.sum
generated
vendored
|
|
@ -1,42 +1,35 @@
|
||||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
|
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
|
||||||
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
||||||
github.com/labstack/gommon v0.3.0 h1:JEeO0bvc78PKdyHxloTKiF8BD5iGrH8T6MSeGvSgob0=
|
github.com/labstack/gommon v0.3.1 h1:OomWaJXm7xR6L1HmEtGyQf26TEn7V6X88mktX9kee9o=
|
||||||
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
|
github.com/labstack/gommon v0.3.1/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=
|
||||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
github.com/mattn/go-colorable v0.1.11 h1:nQ+aFkoE2TMGc0b68U2OKSexC+eq46+XwZzWXHRmPYs=
|
||||||
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
|
github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
||||||
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
|
||||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
|
||||||
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
|
|
||||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
|
||||||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||||
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
|
|
||||||
github.com/valyala/fasttemplate v1.2.1 h1:TVEnxayobAdVkhQfrfes2IzOB6o+z4roRkPF52WA1u4=
|
github.com/valyala/fasttemplate v1.2.1 h1:TVEnxayobAdVkhQfrfes2IzOB6o+z4roRkPF52WA1u4=
|
||||||
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
||||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ=
|
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ=
|
||||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20210913180222-943fd674d43e h1:+b/22bPvDYt4NPDcy4xAGCmON713ONAWFeY3Z7I3tR8=
|
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f h1:OfiFi4JbukWwe3lzw+xunroH1mnC1e2Gy5cxNJApiSY=
|
||||||
golang.org/x/net v0.0.0-20210913180222-943fd674d43e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210910150752-751e447fb3d0 h1:xrCZDmdtoloIiooiA9q0OQb9r8HejIHYoHGhGCe1pGg=
|
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210910150752-751e447fb3d0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b h1:1VkfZQv42XQlA/jchYumAnv1UPo6RgF9rJFkTgZIxO4=
|
||||||
|
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
|
@ -47,5 +40,6 @@ golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxb
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
|
|
||||||
5
vendor/github.com/labstack/echo/v4/group.go
generated
vendored
5
vendor/github.com/labstack/echo/v4/group.go
generated
vendored
|
|
@ -102,11 +102,6 @@ func (g *Group) Group(prefix string, middleware ...MiddlewareFunc) (sg *Group) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Static implements `Echo#Static()` for sub-routes within the Group.
|
|
||||||
func (g *Group) Static(prefix, root string) {
|
|
||||||
g.static(prefix, root, g.GET)
|
|
||||||
}
|
|
||||||
|
|
||||||
// File implements `Echo#File()` for sub-routes within the Group.
|
// File implements `Echo#File()` for sub-routes within the Group.
|
||||||
func (g *Group) File(path, file string) {
|
func (g *Group) File(path, file string) {
|
||||||
g.file(path, file, g.GET)
|
g.file(path, file, g.GET)
|
||||||
|
|
|
||||||
9
vendor/github.com/labstack/echo/v4/group_fs.go
generated
vendored
Normal file
9
vendor/github.com/labstack/echo/v4/group_fs.go
generated
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
//go:build !go1.16
|
||||||
|
// +build !go1.16
|
||||||
|
|
||||||
|
package echo
|
||||||
|
|
||||||
|
// Static implements `Echo#Static()` for sub-routes within the Group.
|
||||||
|
func (g *Group) Static(prefix, root string) {
|
||||||
|
g.static(prefix, root, g.GET)
|
||||||
|
}
|
||||||
33
vendor/github.com/labstack/echo/v4/group_fs_go1.16.go
generated
vendored
Normal file
33
vendor/github.com/labstack/echo/v4/group_fs_go1.16.go
generated
vendored
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
//go:build go1.16
|
||||||
|
// +build go1.16
|
||||||
|
|
||||||
|
package echo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/fs"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Static implements `Echo#Static()` for sub-routes within the Group.
|
||||||
|
func (g *Group) Static(pathPrefix, fsRoot string) {
|
||||||
|
subFs := MustSubFS(g.echo.Filesystem, fsRoot)
|
||||||
|
g.StaticFS(pathPrefix, subFs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StaticFS implements `Echo#StaticFS()` for sub-routes within the Group.
|
||||||
|
//
|
||||||
|
// When dealing with `embed.FS` use `fs := echo.MustSubFS(fs, "rootDirectory") to create sub fs which uses necessary
|
||||||
|
// prefix for directory path. This is necessary as `//go:embed assets/images` embeds files with paths
|
||||||
|
// including `assets/images` as their prefix.
|
||||||
|
func (g *Group) StaticFS(pathPrefix string, filesystem fs.FS) {
|
||||||
|
g.Add(
|
||||||
|
http.MethodGet,
|
||||||
|
pathPrefix+"*",
|
||||||
|
StaticDirectoryHandler(filesystem, false),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FileFS implements `Echo#FileFS()` for sub-routes within the Group.
|
||||||
|
func (g *Group) FileFS(path, file string, filesystem fs.FS, m ...MiddlewareFunc) *Route {
|
||||||
|
return g.GET(path, StaticFileHandler(file, filesystem), m...)
|
||||||
|
}
|
||||||
142
vendor/github.com/labstack/echo/v4/ip.go
generated
vendored
142
vendor/github.com/labstack/echo/v4/ip.go
generated
vendored
|
|
@ -6,6 +6,130 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
By: https://github.com/tmshn (See: https://github.com/labstack/echo/pull/1478 , https://github.com/labstack/echox/pull/134 )
|
||||||
|
Source: https://echo.labstack.com/guide/ip-address/
|
||||||
|
|
||||||
|
IP address plays fundamental role in HTTP; it's used for access control, auditing, geo-based access analysis and more.
|
||||||
|
Echo provides handy method [`Context#RealIP()`](https://godoc.org/github.com/labstack/echo#Context) for that.
|
||||||
|
|
||||||
|
However, it is not trivial to retrieve the _real_ IP address from requests especially when you put L7 proxies before the application.
|
||||||
|
In such situation, _real_ IP needs to be relayed on HTTP layer from proxies to your app, but you must not trust HTTP headers unconditionally.
|
||||||
|
Otherwise, you might give someone a chance of deceiving you. **A security risk!**
|
||||||
|
|
||||||
|
To retrieve IP address reliably/securely, you must let your application be aware of the entire architecture of your infrastructure.
|
||||||
|
In Echo, this can be done by configuring `Echo#IPExtractor` appropriately.
|
||||||
|
This guides show you why and how.
|
||||||
|
|
||||||
|
> Note: if you dont' set `Echo#IPExtractor` explicitly, Echo fallback to legacy behavior, which is not a good choice.
|
||||||
|
|
||||||
|
Let's start from two questions to know the right direction:
|
||||||
|
|
||||||
|
1. Do you put any HTTP (L7) proxy in front of the application?
|
||||||
|
- It includes both cloud solutions (such as AWS ALB or GCP HTTP LB) and OSS ones (such as Nginx, Envoy or Istio ingress gateway).
|
||||||
|
2. If yes, what HTTP header do your proxies use to pass client IP to the application?
|
||||||
|
|
||||||
|
## Case 1. With no proxy
|
||||||
|
|
||||||
|
If you put no proxy (e.g.: directory facing to the internet), all you need to (and have to) see is IP address from network layer.
|
||||||
|
Any HTTP header is untrustable because the clients have full control what headers to be set.
|
||||||
|
|
||||||
|
In this case, use `echo.ExtractIPDirect()`.
|
||||||
|
|
||||||
|
```go
|
||||||
|
e.IPExtractor = echo.ExtractIPDirect()
|
||||||
|
```
|
||||||
|
|
||||||
|
## Case 2. With proxies using `X-Forwarded-For` header
|
||||||
|
|
||||||
|
[`X-Forwared-For` (XFF)](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For) is the popular header
|
||||||
|
to relay clients' IP addresses.
|
||||||
|
At each hop on the proxies, they append the request IP address at the end of the header.
|
||||||
|
|
||||||
|
Following example diagram illustrates this behavior.
|
||||||
|
|
||||||
|
```text
|
||||||
|
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
|
||||||
|
│ "Origin" │───────────>│ Proxy 1 │───────────>│ Proxy 2 │───────────>│ Your app │
|
||||||
|
│ (IP: a) │ │ (IP: b) │ │ (IP: c) │ │ │
|
||||||
|
└──────────┘ └──────────┘ └──────────┘ └──────────┘
|
||||||
|
|
||||||
|
Case 1.
|
||||||
|
XFF: "" "a" "a, b"
|
||||||
|
~~~~~~
|
||||||
|
Case 2.
|
||||||
|
XFF: "x" "x, a" "x, a, b"
|
||||||
|
~~~~~~~~~
|
||||||
|
↑ What your app will see
|
||||||
|
```
|
||||||
|
|
||||||
|
In this case, use **first _untrustable_ IP reading from right**. Never use first one reading from left, as it is
|
||||||
|
configurable by client. Here "trustable" means "you are sure the IP address belongs to your infrastructre".
|
||||||
|
In above example, if `b` and `c` are trustable, the IP address of the client is `a` for both cases, never be `x`.
|
||||||
|
|
||||||
|
In Echo, use `ExtractIPFromXFFHeader(...TrustOption)`.
|
||||||
|
|
||||||
|
```go
|
||||||
|
e.IPExtractor = echo.ExtractIPFromXFFHeader()
|
||||||
|
```
|
||||||
|
|
||||||
|
By default, it trusts internal IP addresses (loopback, link-local unicast, private-use and unique local address
|
||||||
|
from [RFC6890](https://tools.ietf.org/html/rfc6890), [RFC4291](https://tools.ietf.org/html/rfc4291) and
|
||||||
|
[RFC4193](https://tools.ietf.org/html/rfc4193)).
|
||||||
|
To control this behavior, use [`TrustOption`](https://godoc.org/github.com/labstack/echo#TrustOption)s.
|
||||||
|
|
||||||
|
E.g.:
|
||||||
|
|
||||||
|
```go
|
||||||
|
e.IPExtractor = echo.ExtractIPFromXFFHeader(
|
||||||
|
TrustLinkLocal(false),
|
||||||
|
TrustIPRanges(lbIPRange),
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
- Ref: https://godoc.org/github.com/labstack/echo#TrustOption
|
||||||
|
|
||||||
|
## Case 3. With proxies using `X-Real-IP` header
|
||||||
|
|
||||||
|
`X-Real-IP` is another HTTP header to relay clients' IP addresses, but it carries only one address unlike XFF.
|
||||||
|
|
||||||
|
If your proxies set this header, use `ExtractIPFromRealIPHeader(...TrustOption)`.
|
||||||
|
|
||||||
|
```go
|
||||||
|
e.IPExtractor = echo.ExtractIPFromRealIPHeader()
|
||||||
|
```
|
||||||
|
|
||||||
|
Again, it trusts internal IP addresses by default (loopback, link-local unicast, private-use and unique local address
|
||||||
|
from [RFC6890](https://tools.ietf.org/html/rfc6890), [RFC4291](https://tools.ietf.org/html/rfc4291) and
|
||||||
|
[RFC4193](https://tools.ietf.org/html/rfc4193)).
|
||||||
|
To control this behavior, use [`TrustOption`](https://godoc.org/github.com/labstack/echo#TrustOption)s.
|
||||||
|
|
||||||
|
- Ref: https://godoc.org/github.com/labstack/echo#TrustOption
|
||||||
|
|
||||||
|
> **Never forget** to configure the outermost proxy (i.e.; at the edge of your infrastructure) **not to pass through incoming headers**.
|
||||||
|
> Otherwise there is a chance of fraud, as it is what clients can control.
|
||||||
|
|
||||||
|
## About default behavior
|
||||||
|
|
||||||
|
In default behavior, Echo sees all of first XFF header, X-Real-IP header and IP from network layer.
|
||||||
|
|
||||||
|
As you might already notice, after reading this article, this is not good.
|
||||||
|
Sole reason this is default is just backward compatibility.
|
||||||
|
|
||||||
|
## Private IP ranges
|
||||||
|
|
||||||
|
See: https://en.wikipedia.org/wiki/Private_network
|
||||||
|
|
||||||
|
Private IPv4 address ranges (RFC 1918):
|
||||||
|
* 10.0.0.0 – 10.255.255.255 (24-bit block)
|
||||||
|
* 172.16.0.0 – 172.31.255.255 (20-bit block)
|
||||||
|
* 192.168.0.0 – 192.168.255.255 (16-bit block)
|
||||||
|
|
||||||
|
Private IPv6 address ranges:
|
||||||
|
* fc00::/7 address block = RFC 4193 Unique Local Addresses (ULA)
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
type ipChecker struct {
|
type ipChecker struct {
|
||||||
trustLoopback bool
|
trustLoopback bool
|
||||||
trustLinkLocal bool
|
trustLinkLocal bool
|
||||||
|
|
@ -52,6 +176,7 @@ func newIPChecker(configs []TrustOption) *ipChecker {
|
||||||
return checker
|
return checker
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Go1.16+ added `ip.IsPrivate()` but until that use this implementation
|
||||||
func isPrivateIPRange(ip net.IP) bool {
|
func isPrivateIPRange(ip net.IP) bool {
|
||||||
if ip4 := ip.To4(); ip4 != nil {
|
if ip4 := ip.To4(); ip4 != nil {
|
||||||
return ip4[0] == 10 ||
|
return ip4[0] == 10 ||
|
||||||
|
|
@ -87,10 +212,12 @@ type IPExtractor func(*http.Request) string
|
||||||
// ExtractIPDirect extracts IP address using actual IP address.
|
// ExtractIPDirect extracts IP address using actual IP address.
|
||||||
// Use this if your server faces to internet directory (i.e.: uses no proxy).
|
// Use this if your server faces to internet directory (i.e.: uses no proxy).
|
||||||
func ExtractIPDirect() IPExtractor {
|
func ExtractIPDirect() IPExtractor {
|
||||||
return func(req *http.Request) string {
|
return extractIP
|
||||||
ra, _, _ := net.SplitHostPort(req.RemoteAddr)
|
}
|
||||||
return ra
|
|
||||||
}
|
func extractIP(req *http.Request) string {
|
||||||
|
ra, _, _ := net.SplitHostPort(req.RemoteAddr)
|
||||||
|
return ra
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExtractIPFromRealIPHeader extracts IP address using x-real-ip header.
|
// ExtractIPFromRealIPHeader extracts IP address using x-real-ip header.
|
||||||
|
|
@ -98,14 +225,13 @@ func ExtractIPDirect() IPExtractor {
|
||||||
func ExtractIPFromRealIPHeader(options ...TrustOption) IPExtractor {
|
func ExtractIPFromRealIPHeader(options ...TrustOption) IPExtractor {
|
||||||
checker := newIPChecker(options)
|
checker := newIPChecker(options)
|
||||||
return func(req *http.Request) string {
|
return func(req *http.Request) string {
|
||||||
directIP := ExtractIPDirect()(req)
|
|
||||||
realIP := req.Header.Get(HeaderXRealIP)
|
realIP := req.Header.Get(HeaderXRealIP)
|
||||||
if realIP != "" {
|
if realIP != "" {
|
||||||
if ip := net.ParseIP(directIP); ip != nil && checker.trust(ip) {
|
if ip := net.ParseIP(realIP); ip != nil && checker.trust(ip) {
|
||||||
return realIP
|
return realIP
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return directIP
|
return extractIP(req)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -115,7 +241,7 @@ func ExtractIPFromRealIPHeader(options ...TrustOption) IPExtractor {
|
||||||
func ExtractIPFromXFFHeader(options ...TrustOption) IPExtractor {
|
func ExtractIPFromXFFHeader(options ...TrustOption) IPExtractor {
|
||||||
checker := newIPChecker(options)
|
checker := newIPChecker(options)
|
||||||
return func(req *http.Request) string {
|
return func(req *http.Request) string {
|
||||||
directIP := ExtractIPDirect()(req)
|
directIP := extractIP(req)
|
||||||
xffs := req.Header[HeaderXForwardedFor]
|
xffs := req.Header[HeaderXForwardedFor]
|
||||||
if len(xffs) == 0 {
|
if len(xffs) == 0 {
|
||||||
return directIP
|
return directIP
|
||||||
|
|
|
||||||
9
vendor/github.com/labstack/echo/v4/middleware/compress.go
generated
vendored
9
vendor/github.com/labstack/echo/v4/middleware/compress.go
generated
vendored
|
|
@ -27,6 +27,7 @@ type (
|
||||||
gzipResponseWriter struct {
|
gzipResponseWriter struct {
|
||||||
io.Writer
|
io.Writer
|
||||||
http.ResponseWriter
|
http.ResponseWriter
|
||||||
|
wroteBody bool
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -78,8 +79,9 @@ func GzipWithConfig(config GzipConfig) echo.MiddlewareFunc {
|
||||||
}
|
}
|
||||||
rw := res.Writer
|
rw := res.Writer
|
||||||
w.Reset(rw)
|
w.Reset(rw)
|
||||||
|
grw := &gzipResponseWriter{Writer: w, ResponseWriter: rw}
|
||||||
defer func() {
|
defer func() {
|
||||||
if res.Size == 0 {
|
if !grw.wroteBody {
|
||||||
if res.Header().Get(echo.HeaderContentEncoding) == gzipScheme {
|
if res.Header().Get(echo.HeaderContentEncoding) == gzipScheme {
|
||||||
res.Header().Del(echo.HeaderContentEncoding)
|
res.Header().Del(echo.HeaderContentEncoding)
|
||||||
}
|
}
|
||||||
|
|
@ -92,7 +94,6 @@ func GzipWithConfig(config GzipConfig) echo.MiddlewareFunc {
|
||||||
w.Close()
|
w.Close()
|
||||||
pool.Put(w)
|
pool.Put(w)
|
||||||
}()
|
}()
|
||||||
grw := &gzipResponseWriter{Writer: w, ResponseWriter: rw}
|
|
||||||
res.Writer = grw
|
res.Writer = grw
|
||||||
}
|
}
|
||||||
return next(c)
|
return next(c)
|
||||||
|
|
@ -101,9 +102,6 @@ func GzipWithConfig(config GzipConfig) echo.MiddlewareFunc {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *gzipResponseWriter) WriteHeader(code int) {
|
func (w *gzipResponseWriter) WriteHeader(code int) {
|
||||||
if code == http.StatusNoContent { // Issue #489
|
|
||||||
w.ResponseWriter.Header().Del(echo.HeaderContentEncoding)
|
|
||||||
}
|
|
||||||
w.Header().Del(echo.HeaderContentLength) // Issue #444
|
w.Header().Del(echo.HeaderContentLength) // Issue #444
|
||||||
w.ResponseWriter.WriteHeader(code)
|
w.ResponseWriter.WriteHeader(code)
|
||||||
}
|
}
|
||||||
|
|
@ -112,6 +110,7 @@ func (w *gzipResponseWriter) Write(b []byte) (int, error) {
|
||||||
if w.Header().Get(echo.HeaderContentType) == "" {
|
if w.Header().Get(echo.HeaderContentType) == "" {
|
||||||
w.Header().Set(echo.HeaderContentType, http.DetectContentType(b))
|
w.Header().Set(echo.HeaderContentType, http.DetectContentType(b))
|
||||||
}
|
}
|
||||||
|
w.wroteBody = true
|
||||||
return w.Writer.Write(b)
|
return w.Writer.Write(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
69
vendor/github.com/labstack/echo/v4/middleware/cors.go
generated
vendored
69
vendor/github.com/labstack/echo/v4/middleware/cors.go
generated
vendored
|
|
@ -29,6 +29,8 @@ type (
|
||||||
// AllowMethods defines a list methods allowed when accessing the resource.
|
// AllowMethods defines a list methods allowed when accessing the resource.
|
||||||
// This is used in response to a preflight request.
|
// This is used in response to a preflight request.
|
||||||
// Optional. Default value DefaultCORSConfig.AllowMethods.
|
// Optional. Default value DefaultCORSConfig.AllowMethods.
|
||||||
|
// If `allowMethods` is left empty will fill for preflight request `Access-Control-Allow-Methods` header value
|
||||||
|
// from `Allow` header that echo.Router set into context.
|
||||||
AllowMethods []string `yaml:"allow_methods"`
|
AllowMethods []string `yaml:"allow_methods"`
|
||||||
|
|
||||||
// AllowHeaders defines a list of request headers that can be used when
|
// AllowHeaders defines a list of request headers that can be used when
|
||||||
|
|
@ -41,6 +43,8 @@ type (
|
||||||
// a response to a preflight request, this indicates whether or not the
|
// a response to a preflight request, this indicates whether or not the
|
||||||
// actual request can be made using credentials.
|
// actual request can be made using credentials.
|
||||||
// Optional. Default value false.
|
// Optional. Default value false.
|
||||||
|
// Security: avoid using `AllowCredentials = true` with `AllowOrigins = *`.
|
||||||
|
// See http://blog.portswigger.net/2016/10/exploiting-cors-misconfigurations-for.html
|
||||||
AllowCredentials bool `yaml:"allow_credentials"`
|
AllowCredentials bool `yaml:"allow_credentials"`
|
||||||
|
|
||||||
// ExposeHeaders defines a whitelist headers that clients are allowed to
|
// ExposeHeaders defines a whitelist headers that clients are allowed to
|
||||||
|
|
@ -80,7 +84,9 @@ func CORSWithConfig(config CORSConfig) echo.MiddlewareFunc {
|
||||||
if len(config.AllowOrigins) == 0 {
|
if len(config.AllowOrigins) == 0 {
|
||||||
config.AllowOrigins = DefaultCORSConfig.AllowOrigins
|
config.AllowOrigins = DefaultCORSConfig.AllowOrigins
|
||||||
}
|
}
|
||||||
|
hasCustomAllowMethods := true
|
||||||
if len(config.AllowMethods) == 0 {
|
if len(config.AllowMethods) == 0 {
|
||||||
|
hasCustomAllowMethods = false
|
||||||
config.AllowMethods = DefaultCORSConfig.AllowMethods
|
config.AllowMethods = DefaultCORSConfig.AllowMethods
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -109,10 +115,28 @@ func CORSWithConfig(config CORSConfig) echo.MiddlewareFunc {
|
||||||
origin := req.Header.Get(echo.HeaderOrigin)
|
origin := req.Header.Get(echo.HeaderOrigin)
|
||||||
allowOrigin := ""
|
allowOrigin := ""
|
||||||
|
|
||||||
preflight := req.Method == http.MethodOptions
|
|
||||||
res.Header().Add(echo.HeaderVary, echo.HeaderOrigin)
|
res.Header().Add(echo.HeaderVary, echo.HeaderOrigin)
|
||||||
|
|
||||||
// No Origin provided
|
// Preflight request is an OPTIONS request, using three HTTP request headers: Access-Control-Request-Method,
|
||||||
|
// Access-Control-Request-Headers, and the Origin header. See: https://developer.mozilla.org/en-US/docs/Glossary/Preflight_request
|
||||||
|
// For simplicity we just consider method type and later `Origin` header.
|
||||||
|
preflight := req.Method == http.MethodOptions
|
||||||
|
|
||||||
|
// Although router adds special handler in case of OPTIONS method we avoid calling next for OPTIONS in this middleware
|
||||||
|
// as CORS requests do not have cookies / authentication headers by default, so we could get stuck in auth
|
||||||
|
// middlewares by calling next(c).
|
||||||
|
// But we still want to send `Allow` header as response in case of Non-CORS OPTIONS request as router default
|
||||||
|
// handler does.
|
||||||
|
routerAllowMethods := ""
|
||||||
|
if preflight {
|
||||||
|
tmpAllowMethods, ok := c.Get(echo.ContextKeyHeaderAllow).(string)
|
||||||
|
if ok && tmpAllowMethods != "" {
|
||||||
|
routerAllowMethods = tmpAllowMethods
|
||||||
|
c.Response().Header().Set(echo.HeaderAllow, routerAllowMethods)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// No Origin provided. This is (probably) not request from actual browser - proceed executing middleware chain
|
||||||
if origin == "" {
|
if origin == "" {
|
||||||
if !preflight {
|
if !preflight {
|
||||||
return next(c)
|
return next(c)
|
||||||
|
|
@ -145,19 +169,15 @@ func CORSWithConfig(config CORSConfig) echo.MiddlewareFunc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check allowed origin patterns
|
checkPatterns := false
|
||||||
for _, re := range allowOriginPatterns {
|
if allowOrigin == "" {
|
||||||
if allowOrigin == "" {
|
// to avoid regex cost by invalid (long) domains (253 is domain name max limit)
|
||||||
didx := strings.Index(origin, "://")
|
if len(origin) <= (253+3+5) && strings.Contains(origin, "://") {
|
||||||
if didx == -1 {
|
checkPatterns = true
|
||||||
continue
|
}
|
||||||
}
|
}
|
||||||
domAuth := origin[didx+3:]
|
if checkPatterns {
|
||||||
// to avoid regex cost by invalid long domain
|
for _, re := range allowOriginPatterns {
|
||||||
if len(domAuth) > 253 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
if match, _ := regexp.MatchString(re, origin); match {
|
if match, _ := regexp.MatchString(re, origin); match {
|
||||||
allowOrigin = origin
|
allowOrigin = origin
|
||||||
break
|
break
|
||||||
|
|
@ -174,12 +194,13 @@ func CORSWithConfig(config CORSConfig) echo.MiddlewareFunc {
|
||||||
return c.NoContent(http.StatusNoContent)
|
return c.NoContent(http.StatusNoContent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
res.Header().Set(echo.HeaderAccessControlAllowOrigin, allowOrigin)
|
||||||
|
if config.AllowCredentials {
|
||||||
|
res.Header().Set(echo.HeaderAccessControlAllowCredentials, "true")
|
||||||
|
}
|
||||||
|
|
||||||
// Simple request
|
// Simple request
|
||||||
if !preflight {
|
if !preflight {
|
||||||
res.Header().Set(echo.HeaderAccessControlAllowOrigin, allowOrigin)
|
|
||||||
if config.AllowCredentials {
|
|
||||||
res.Header().Set(echo.HeaderAccessControlAllowCredentials, "true")
|
|
||||||
}
|
|
||||||
if exposeHeaders != "" {
|
if exposeHeaders != "" {
|
||||||
res.Header().Set(echo.HeaderAccessControlExposeHeaders, exposeHeaders)
|
res.Header().Set(echo.HeaderAccessControlExposeHeaders, exposeHeaders)
|
||||||
}
|
}
|
||||||
|
|
@ -189,11 +210,13 @@ func CORSWithConfig(config CORSConfig) echo.MiddlewareFunc {
|
||||||
// Preflight request
|
// Preflight request
|
||||||
res.Header().Add(echo.HeaderVary, echo.HeaderAccessControlRequestMethod)
|
res.Header().Add(echo.HeaderVary, echo.HeaderAccessControlRequestMethod)
|
||||||
res.Header().Add(echo.HeaderVary, echo.HeaderAccessControlRequestHeaders)
|
res.Header().Add(echo.HeaderVary, echo.HeaderAccessControlRequestHeaders)
|
||||||
res.Header().Set(echo.HeaderAccessControlAllowOrigin, allowOrigin)
|
|
||||||
res.Header().Set(echo.HeaderAccessControlAllowMethods, allowMethods)
|
if !hasCustomAllowMethods && routerAllowMethods != "" {
|
||||||
if config.AllowCredentials {
|
res.Header().Set(echo.HeaderAccessControlAllowMethods, routerAllowMethods)
|
||||||
res.Header().Set(echo.HeaderAccessControlAllowCredentials, "true")
|
} else {
|
||||||
|
res.Header().Set(echo.HeaderAccessControlAllowMethods, allowMethods)
|
||||||
}
|
}
|
||||||
|
|
||||||
if allowHeaders != "" {
|
if allowHeaders != "" {
|
||||||
res.Header().Set(echo.HeaderAccessControlAllowHeaders, allowHeaders)
|
res.Header().Set(echo.HeaderAccessControlAllowHeaders, allowHeaders)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
110
vendor/github.com/labstack/echo/v4/middleware/csrf.go
generated
vendored
110
vendor/github.com/labstack/echo/v4/middleware/csrf.go
generated
vendored
|
|
@ -2,9 +2,7 @@ package middleware
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/subtle"
|
"crypto/subtle"
|
||||||
"errors"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
|
|
@ -21,13 +19,15 @@ type (
|
||||||
TokenLength uint8 `yaml:"token_length"`
|
TokenLength uint8 `yaml:"token_length"`
|
||||||
// Optional. Default value 32.
|
// Optional. Default value 32.
|
||||||
|
|
||||||
// TokenLookup is a string in the form of "<source>:<key>" that is used
|
// TokenLookup is a string in the form of "<source>:<name>" or "<source>:<name>,<source>:<name>" that is used
|
||||||
// to extract token from the request.
|
// to extract token from the request.
|
||||||
// Optional. Default value "header:X-CSRF-Token".
|
// Optional. Default value "header:X-CSRF-Token".
|
||||||
// Possible values:
|
// Possible values:
|
||||||
// - "header:<name>"
|
// - "header:<name>" or "header:<name>:<cut-prefix>"
|
||||||
// - "form:<name>"
|
|
||||||
// - "query:<name>"
|
// - "query:<name>"
|
||||||
|
// - "form:<name>"
|
||||||
|
// Multiple sources example:
|
||||||
|
// - "header:X-CSRF-Token,query:csrf"
|
||||||
TokenLookup string `yaml:"token_lookup"`
|
TokenLookup string `yaml:"token_lookup"`
|
||||||
|
|
||||||
// Context key to store generated CSRF token into context.
|
// Context key to store generated CSRF token into context.
|
||||||
|
|
@ -62,12 +62,11 @@ type (
|
||||||
// Optional. Default value SameSiteDefaultMode.
|
// Optional. Default value SameSiteDefaultMode.
|
||||||
CookieSameSite http.SameSite `yaml:"cookie_same_site"`
|
CookieSameSite http.SameSite `yaml:"cookie_same_site"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// csrfTokenExtractor defines a function that takes `echo.Context` and returns
|
|
||||||
// either a token or an error.
|
|
||||||
csrfTokenExtractor func(echo.Context) (string, error)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ErrCSRFInvalid is returned when CSRF check fails
|
||||||
|
var ErrCSRFInvalid = echo.NewHTTPError(http.StatusForbidden, "invalid csrf token")
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// DefaultCSRFConfig is the default CSRF middleware config.
|
// DefaultCSRFConfig is the default CSRF middleware config.
|
||||||
DefaultCSRFConfig = CSRFConfig{
|
DefaultCSRFConfig = CSRFConfig{
|
||||||
|
|
@ -114,14 +113,9 @@ func CSRFWithConfig(config CSRFConfig) echo.MiddlewareFunc {
|
||||||
config.CookieSecure = true
|
config.CookieSecure = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize
|
extractors, err := createExtractors(config.TokenLookup, "")
|
||||||
parts := strings.Split(config.TokenLookup, ":")
|
if err != nil {
|
||||||
extractor := csrfTokenFromHeader(parts[1])
|
panic(err)
|
||||||
switch parts[0] {
|
|
||||||
case "form":
|
|
||||||
extractor = csrfTokenFromForm(parts[1])
|
|
||||||
case "query":
|
|
||||||
extractor = csrfTokenFromQuery(parts[1])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||||
|
|
@ -130,28 +124,50 @@ func CSRFWithConfig(config CSRFConfig) echo.MiddlewareFunc {
|
||||||
return next(c)
|
return next(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
req := c.Request()
|
|
||||||
k, err := c.Cookie(config.CookieName)
|
|
||||||
token := ""
|
token := ""
|
||||||
|
if k, err := c.Cookie(config.CookieName); err != nil {
|
||||||
// Generate token
|
token = random.String(config.TokenLength) // Generate token
|
||||||
if err != nil {
|
|
||||||
token = random.String(config.TokenLength)
|
|
||||||
} else {
|
} else {
|
||||||
// Reuse token
|
token = k.Value // Reuse token
|
||||||
token = k.Value
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch req.Method {
|
switch c.Request().Method {
|
||||||
case http.MethodGet, http.MethodHead, http.MethodOptions, http.MethodTrace:
|
case http.MethodGet, http.MethodHead, http.MethodOptions, http.MethodTrace:
|
||||||
default:
|
default:
|
||||||
// Validate token only for requests which are not defined as 'safe' by RFC7231
|
// Validate token only for requests which are not defined as 'safe' by RFC7231
|
||||||
clientToken, err := extractor(c)
|
var lastExtractorErr error
|
||||||
if err != nil {
|
var lastTokenErr error
|
||||||
return echo.NewHTTPError(http.StatusBadRequest, err.Error())
|
outer:
|
||||||
|
for _, extractor := range extractors {
|
||||||
|
clientTokens, err := extractor(c)
|
||||||
|
if err != nil {
|
||||||
|
lastExtractorErr = err
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, clientToken := range clientTokens {
|
||||||
|
if validateCSRFToken(token, clientToken) {
|
||||||
|
lastTokenErr = nil
|
||||||
|
lastExtractorErr = nil
|
||||||
|
break outer
|
||||||
|
}
|
||||||
|
lastTokenErr = ErrCSRFInvalid
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if !validateCSRFToken(token, clientToken) {
|
if lastTokenErr != nil {
|
||||||
return echo.NewHTTPError(http.StatusForbidden, "invalid csrf token")
|
return lastTokenErr
|
||||||
|
} else if lastExtractorErr != nil {
|
||||||
|
// ugly part to preserve backwards compatible errors. someone could rely on them
|
||||||
|
if lastExtractorErr == errQueryExtractorValueMissing {
|
||||||
|
lastExtractorErr = echo.NewHTTPError(http.StatusBadRequest, "missing csrf token in the query string")
|
||||||
|
} else if lastExtractorErr == errFormExtractorValueMissing {
|
||||||
|
lastExtractorErr = echo.NewHTTPError(http.StatusBadRequest, "missing csrf token in the form parameter")
|
||||||
|
} else if lastExtractorErr == errHeaderExtractorValueMissing {
|
||||||
|
lastExtractorErr = echo.NewHTTPError(http.StatusBadRequest, "missing csrf token in request header")
|
||||||
|
} else {
|
||||||
|
lastExtractorErr = echo.NewHTTPError(http.StatusBadRequest, lastExtractorErr.Error())
|
||||||
|
}
|
||||||
|
return lastExtractorErr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -184,38 +200,6 @@ func CSRFWithConfig(config CSRFConfig) echo.MiddlewareFunc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// csrfTokenFromForm returns a `csrfTokenExtractor` that extracts token from the
|
|
||||||
// provided request header.
|
|
||||||
func csrfTokenFromHeader(header string) csrfTokenExtractor {
|
|
||||||
return func(c echo.Context) (string, error) {
|
|
||||||
return c.Request().Header.Get(header), nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// csrfTokenFromForm returns a `csrfTokenExtractor` that extracts token from the
|
|
||||||
// provided form parameter.
|
|
||||||
func csrfTokenFromForm(param string) csrfTokenExtractor {
|
|
||||||
return func(c echo.Context) (string, error) {
|
|
||||||
token := c.FormValue(param)
|
|
||||||
if token == "" {
|
|
||||||
return "", errors.New("missing csrf token in the form parameter")
|
|
||||||
}
|
|
||||||
return token, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// csrfTokenFromQuery returns a `csrfTokenExtractor` that extracts token from the
|
|
||||||
// provided query parameter.
|
|
||||||
func csrfTokenFromQuery(param string) csrfTokenExtractor {
|
|
||||||
return func(c echo.Context) (string, error) {
|
|
||||||
token := c.QueryParam(param)
|
|
||||||
if token == "" {
|
|
||||||
return "", errors.New("missing csrf token in the query string")
|
|
||||||
}
|
|
||||||
return token, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func validateCSRFToken(token, clientToken string) bool {
|
func validateCSRFToken(token, clientToken string) bool {
|
||||||
return subtle.ConstantTimeCompare([]byte(token), []byte(clientToken)) == 1
|
return subtle.ConstantTimeCompare([]byte(token), []byte(clientToken)) == 1
|
||||||
}
|
}
|
||||||
|
|
|
||||||
75
vendor/github.com/labstack/echo/v4/middleware/decompress.go
generated
vendored
75
vendor/github.com/labstack/echo/v4/middleware/decompress.go
generated
vendored
|
|
@ -1,10 +1,8 @@
|
||||||
package middleware
|
package middleware
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"compress/gzip"
|
"compress/gzip"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
|
@ -43,26 +41,7 @@ type DefaultGzipDecompressPool struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DefaultGzipDecompressPool) gzipDecompressPool() sync.Pool {
|
func (d *DefaultGzipDecompressPool) gzipDecompressPool() sync.Pool {
|
||||||
return sync.Pool{
|
return sync.Pool{New: func() interface{} { return new(gzip.Reader) }}
|
||||||
New: func() interface{} {
|
|
||||||
// create with an empty reader (but with GZIP header)
|
|
||||||
w, err := gzip.NewWriterLevel(ioutil.Discard, gzip.BestSpeed)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
b := new(bytes.Buffer)
|
|
||||||
w.Reset(b)
|
|
||||||
w.Flush()
|
|
||||||
w.Close()
|
|
||||||
|
|
||||||
r, err := gzip.NewReader(bytes.NewReader(b.Bytes()))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return r
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Decompress decompresses request body based if content encoding type is set to "gzip" with default config
|
//Decompress decompresses request body based if content encoding type is set to "gzip" with default config
|
||||||
|
|
@ -82,38 +61,38 @@ func DecompressWithConfig(config DecompressConfig) echo.MiddlewareFunc {
|
||||||
|
|
||||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||||
pool := config.GzipDecompressPool.gzipDecompressPool()
|
pool := config.GzipDecompressPool.gzipDecompressPool()
|
||||||
|
|
||||||
return func(c echo.Context) error {
|
return func(c echo.Context) error {
|
||||||
if config.Skipper(c) {
|
if config.Skipper(c) {
|
||||||
return next(c)
|
return next(c)
|
||||||
}
|
}
|
||||||
switch c.Request().Header.Get(echo.HeaderContentEncoding) {
|
|
||||||
case GZIPEncoding:
|
|
||||||
b := c.Request().Body
|
|
||||||
|
|
||||||
i := pool.Get()
|
if c.Request().Header.Get(echo.HeaderContentEncoding) != GZIPEncoding {
|
||||||
gr, ok := i.(*gzip.Reader)
|
return next(c)
|
||||||
if !ok {
|
|
||||||
return echo.NewHTTPError(http.StatusInternalServerError, i.(error).Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := gr.Reset(b); err != nil {
|
|
||||||
pool.Put(gr)
|
|
||||||
if err == io.EOF { //ignore if body is empty
|
|
||||||
return next(c)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
var buf bytes.Buffer
|
|
||||||
io.Copy(&buf, gr)
|
|
||||||
|
|
||||||
gr.Close()
|
|
||||||
pool.Put(gr)
|
|
||||||
|
|
||||||
b.Close() // http.Request.Body is closed by the Server, but because we are replacing it, it must be closed here
|
|
||||||
|
|
||||||
r := ioutil.NopCloser(&buf)
|
|
||||||
c.Request().Body = r
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
i := pool.Get()
|
||||||
|
gr, ok := i.(*gzip.Reader)
|
||||||
|
if !ok || gr == nil {
|
||||||
|
return echo.NewHTTPError(http.StatusInternalServerError, i.(error).Error())
|
||||||
|
}
|
||||||
|
defer pool.Put(gr)
|
||||||
|
|
||||||
|
b := c.Request().Body
|
||||||
|
defer b.Close()
|
||||||
|
|
||||||
|
if err := gr.Reset(b); err != nil {
|
||||||
|
if err == io.EOF { //ignore if body is empty
|
||||||
|
return next(c)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// only Close gzip reader if it was set to a proper gzip source otherwise it will panic on close.
|
||||||
|
defer gr.Close()
|
||||||
|
|
||||||
|
c.Request().Body = gr
|
||||||
|
|
||||||
return next(c)
|
return next(c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
184
vendor/github.com/labstack/echo/v4/middleware/extractor.go
generated
vendored
Normal file
184
vendor/github.com/labstack/echo/v4/middleware/extractor.go
generated
vendored
Normal file
|
|
@ -0,0 +1,184 @@
|
||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"github.com/labstack/echo/v4"
|
||||||
|
"net/textproto"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// extractorLimit is arbitrary number to limit values extractor can return. this limits possible resource exhaustion
|
||||||
|
// attack vector
|
||||||
|
extractorLimit = 20
|
||||||
|
)
|
||||||
|
|
||||||
|
var errHeaderExtractorValueMissing = errors.New("missing value in request header")
|
||||||
|
var errHeaderExtractorValueInvalid = errors.New("invalid value in request header")
|
||||||
|
var errQueryExtractorValueMissing = errors.New("missing value in the query string")
|
||||||
|
var errParamExtractorValueMissing = errors.New("missing value in path params")
|
||||||
|
var errCookieExtractorValueMissing = errors.New("missing value in cookies")
|
||||||
|
var errFormExtractorValueMissing = errors.New("missing value in the form")
|
||||||
|
|
||||||
|
// ValuesExtractor defines a function for extracting values (keys/tokens) from the given context.
|
||||||
|
type ValuesExtractor func(c echo.Context) ([]string, error)
|
||||||
|
|
||||||
|
func createExtractors(lookups string, authScheme string) ([]ValuesExtractor, error) {
|
||||||
|
if lookups == "" {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
sources := strings.Split(lookups, ",")
|
||||||
|
var extractors = make([]ValuesExtractor, 0)
|
||||||
|
for _, source := range sources {
|
||||||
|
parts := strings.Split(source, ":")
|
||||||
|
if len(parts) < 2 {
|
||||||
|
return nil, fmt.Errorf("extractor source for lookup could not be split into needed parts: %v", source)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch parts[0] {
|
||||||
|
case "query":
|
||||||
|
extractors = append(extractors, valuesFromQuery(parts[1]))
|
||||||
|
case "param":
|
||||||
|
extractors = append(extractors, valuesFromParam(parts[1]))
|
||||||
|
case "cookie":
|
||||||
|
extractors = append(extractors, valuesFromCookie(parts[1]))
|
||||||
|
case "form":
|
||||||
|
extractors = append(extractors, valuesFromForm(parts[1]))
|
||||||
|
case "header":
|
||||||
|
prefix := ""
|
||||||
|
if len(parts) > 2 {
|
||||||
|
prefix = parts[2]
|
||||||
|
} else if authScheme != "" && parts[1] == echo.HeaderAuthorization {
|
||||||
|
// backwards compatibility for JWT and KeyAuth:
|
||||||
|
// * we only apply this fix to Authorization as header we use and uses prefixes like "Bearer <token-value>" etc
|
||||||
|
// * previously header extractor assumed that auth-scheme/prefix had a space as suffix we need to retain that
|
||||||
|
// behaviour for default values and Authorization header.
|
||||||
|
prefix = authScheme
|
||||||
|
if !strings.HasSuffix(prefix, " ") {
|
||||||
|
prefix += " "
|
||||||
|
}
|
||||||
|
}
|
||||||
|
extractors = append(extractors, valuesFromHeader(parts[1], prefix))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return extractors, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// valuesFromHeader returns a functions that extracts values from the request header.
|
||||||
|
// valuePrefix is parameter to remove first part (prefix) of the extracted value. This is useful if header value has static
|
||||||
|
// prefix like `Authorization: <auth-scheme> <authorisation-parameters>` where part that we want to remove is `<auth-scheme> `
|
||||||
|
// note the space at the end. In case of basic authentication `Authorization: Basic <credentials>` prefix we want to remove
|
||||||
|
// is `Basic `. In case of JWT tokens `Authorization: Bearer <token>` prefix is `Bearer `.
|
||||||
|
// If prefix is left empty the whole value is returned.
|
||||||
|
func valuesFromHeader(header string, valuePrefix string) ValuesExtractor {
|
||||||
|
prefixLen := len(valuePrefix)
|
||||||
|
// standard library parses http.Request header keys in canonical form but we may provide something else so fix this
|
||||||
|
header = textproto.CanonicalMIMEHeaderKey(header)
|
||||||
|
return func(c echo.Context) ([]string, error) {
|
||||||
|
values := c.Request().Header.Values(header)
|
||||||
|
if len(values) == 0 {
|
||||||
|
return nil, errHeaderExtractorValueMissing
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make([]string, 0)
|
||||||
|
for i, value := range values {
|
||||||
|
if prefixLen == 0 {
|
||||||
|
result = append(result, value)
|
||||||
|
if i >= extractorLimit-1 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if len(value) > prefixLen && strings.EqualFold(value[:prefixLen], valuePrefix) {
|
||||||
|
result = append(result, value[prefixLen:])
|
||||||
|
if i >= extractorLimit-1 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(result) == 0 {
|
||||||
|
if prefixLen > 0 {
|
||||||
|
return nil, errHeaderExtractorValueInvalid
|
||||||
|
}
|
||||||
|
return nil, errHeaderExtractorValueMissing
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// valuesFromQuery returns a function that extracts values from the query string.
|
||||||
|
func valuesFromQuery(param string) ValuesExtractor {
|
||||||
|
return func(c echo.Context) ([]string, error) {
|
||||||
|
result := c.QueryParams()[param]
|
||||||
|
if len(result) == 0 {
|
||||||
|
return nil, errQueryExtractorValueMissing
|
||||||
|
} else if len(result) > extractorLimit-1 {
|
||||||
|
result = result[:extractorLimit]
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// valuesFromParam returns a function that extracts values from the url param string.
|
||||||
|
func valuesFromParam(param string) ValuesExtractor {
|
||||||
|
return func(c echo.Context) ([]string, error) {
|
||||||
|
result := make([]string, 0)
|
||||||
|
paramVales := c.ParamValues()
|
||||||
|
for i, p := range c.ParamNames() {
|
||||||
|
if param == p {
|
||||||
|
result = append(result, paramVales[i])
|
||||||
|
if i >= extractorLimit-1 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(result) == 0 {
|
||||||
|
return nil, errParamExtractorValueMissing
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// valuesFromCookie returns a function that extracts values from the named cookie.
|
||||||
|
func valuesFromCookie(name string) ValuesExtractor {
|
||||||
|
return func(c echo.Context) ([]string, error) {
|
||||||
|
cookies := c.Cookies()
|
||||||
|
if len(cookies) == 0 {
|
||||||
|
return nil, errCookieExtractorValueMissing
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make([]string, 0)
|
||||||
|
for i, cookie := range cookies {
|
||||||
|
if name == cookie.Name {
|
||||||
|
result = append(result, cookie.Value)
|
||||||
|
if i >= extractorLimit-1 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(result) == 0 {
|
||||||
|
return nil, errCookieExtractorValueMissing
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// valuesFromForm returns a function that extracts values from the form field.
|
||||||
|
func valuesFromForm(name string) ValuesExtractor {
|
||||||
|
return func(c echo.Context) ([]string, error) {
|
||||||
|
if c.Request().Form == nil {
|
||||||
|
_ = c.Request().ParseMultipartForm(32 << 20) // same what `c.Request().FormValue(name)` does
|
||||||
|
}
|
||||||
|
values := c.Request().Form[name]
|
||||||
|
if len(values) == 0 {
|
||||||
|
return nil, errFormExtractorValueMissing
|
||||||
|
}
|
||||||
|
if len(values) > extractorLimit-1 {
|
||||||
|
values = values[:extractorLimit]
|
||||||
|
}
|
||||||
|
result := append([]string{}, values...)
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
211
vendor/github.com/labstack/echo/v4/middleware/jwt.go
generated
vendored
211
vendor/github.com/labstack/echo/v4/middleware/jwt.go
generated
vendored
|
|
@ -1,3 +1,4 @@
|
||||||
|
//go:build go1.15
|
||||||
// +build go1.15
|
// +build go1.15
|
||||||
|
|
||||||
package middleware
|
package middleware
|
||||||
|
|
@ -5,12 +6,10 @@ package middleware
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
|
||||||
"reflect"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/golang-jwt/jwt"
|
"github.com/golang-jwt/jwt"
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
|
"net/http"
|
||||||
|
"reflect"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
|
|
@ -22,7 +21,8 @@ type (
|
||||||
// BeforeFunc defines a function which is executed just before the middleware.
|
// BeforeFunc defines a function which is executed just before the middleware.
|
||||||
BeforeFunc BeforeFunc
|
BeforeFunc BeforeFunc
|
||||||
|
|
||||||
// SuccessHandler defines a function which is executed for a valid token.
|
// SuccessHandler defines a function which is executed for a valid token before middleware chain continues with next
|
||||||
|
// middleware or handler.
|
||||||
SuccessHandler JWTSuccessHandler
|
SuccessHandler JWTSuccessHandler
|
||||||
|
|
||||||
// ErrorHandler defines a function which is executed for an invalid token.
|
// ErrorHandler defines a function which is executed for an invalid token.
|
||||||
|
|
@ -32,6 +32,13 @@ type (
|
||||||
// ErrorHandlerWithContext is almost identical to ErrorHandler, but it's passed the current context.
|
// ErrorHandlerWithContext is almost identical to ErrorHandler, but it's passed the current context.
|
||||||
ErrorHandlerWithContext JWTErrorHandlerWithContext
|
ErrorHandlerWithContext JWTErrorHandlerWithContext
|
||||||
|
|
||||||
|
// ContinueOnIgnoredError allows the next middleware/handler to be called when ErrorHandlerWithContext decides to
|
||||||
|
// ignore the error (by returning `nil`).
|
||||||
|
// This is useful when parts of your site/api allow public access and some authorized routes provide extra functionality.
|
||||||
|
// In that case you can use ErrorHandlerWithContext to set a default public JWT token value in the request context
|
||||||
|
// and continue. Some logic down the remaining execution chain needs to check that (public) token value then.
|
||||||
|
ContinueOnIgnoredError bool
|
||||||
|
|
||||||
// Signing key to validate token.
|
// Signing key to validate token.
|
||||||
// This is one of the three options to provide a token validation key.
|
// This is one of the three options to provide a token validation key.
|
||||||
// The order of precedence is a user-defined KeyFunc, SigningKeys and SigningKey.
|
// The order of precedence is a user-defined KeyFunc, SigningKeys and SigningKey.
|
||||||
|
|
@ -61,16 +68,26 @@ type (
|
||||||
// to extract token from the request.
|
// to extract token from the request.
|
||||||
// Optional. Default value "header:Authorization".
|
// Optional. Default value "header:Authorization".
|
||||||
// Possible values:
|
// Possible values:
|
||||||
// - "header:<name>"
|
// - "header:<name>" or "header:<name>:<cut-prefix>"
|
||||||
|
// `<cut-prefix>` is argument value to cut/trim prefix of the extracted value. This is useful if header
|
||||||
|
// value has static prefix like `Authorization: <auth-scheme> <authorisation-parameters>` where part that we
|
||||||
|
// want to cut is `<auth-scheme> ` note the space at the end.
|
||||||
|
// In case of JWT tokens `Authorization: Bearer <token>` prefix we cut is `Bearer `.
|
||||||
|
// If prefix is left empty the whole value is returned.
|
||||||
// - "query:<name>"
|
// - "query:<name>"
|
||||||
// - "param:<name>"
|
// - "param:<name>"
|
||||||
// - "cookie:<name>"
|
// - "cookie:<name>"
|
||||||
// - "form:<name>"
|
// - "form:<name>"
|
||||||
// Multiply sources example:
|
// Multiple sources example:
|
||||||
// - "header: Authorization,cookie: myowncookie"
|
// - "header:Authorization,cookie:myowncookie"
|
||||||
|
|
||||||
TokenLookup string
|
TokenLookup string
|
||||||
|
|
||||||
|
// TokenLookupFuncs defines a list of user-defined functions that extract JWT token from the given context.
|
||||||
|
// This is one of the two options to provide a token extractor.
|
||||||
|
// The order of precedence is user-defined TokenLookupFuncs, and TokenLookup.
|
||||||
|
// You can also provide both if you want.
|
||||||
|
TokenLookupFuncs []ValuesExtractor
|
||||||
|
|
||||||
// AuthScheme to be used in the Authorization header.
|
// AuthScheme to be used in the Authorization header.
|
||||||
// Optional. Default value "Bearer".
|
// Optional. Default value "Bearer".
|
||||||
AuthScheme string
|
AuthScheme string
|
||||||
|
|
@ -95,15 +112,13 @@ type (
|
||||||
}
|
}
|
||||||
|
|
||||||
// JWTSuccessHandler defines a function which is executed for a valid token.
|
// JWTSuccessHandler defines a function which is executed for a valid token.
|
||||||
JWTSuccessHandler func(echo.Context)
|
JWTSuccessHandler func(c echo.Context)
|
||||||
|
|
||||||
// JWTErrorHandler defines a function which is executed for an invalid token.
|
// JWTErrorHandler defines a function which is executed for an invalid token.
|
||||||
JWTErrorHandler func(error) error
|
JWTErrorHandler func(err error) error
|
||||||
|
|
||||||
// JWTErrorHandlerWithContext is almost identical to JWTErrorHandler, but it's passed the current context.
|
// JWTErrorHandlerWithContext is almost identical to JWTErrorHandler, but it's passed the current context.
|
||||||
JWTErrorHandlerWithContext func(error, echo.Context) error
|
JWTErrorHandlerWithContext func(err error, c echo.Context) error
|
||||||
|
|
||||||
jwtExtractor func(echo.Context) (string, error)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Algorithms
|
// Algorithms
|
||||||
|
|
@ -120,13 +135,14 @@ var (
|
||||||
var (
|
var (
|
||||||
// DefaultJWTConfig is the default JWT auth middleware config.
|
// DefaultJWTConfig is the default JWT auth middleware config.
|
||||||
DefaultJWTConfig = JWTConfig{
|
DefaultJWTConfig = JWTConfig{
|
||||||
Skipper: DefaultSkipper,
|
Skipper: DefaultSkipper,
|
||||||
SigningMethod: AlgorithmHS256,
|
SigningMethod: AlgorithmHS256,
|
||||||
ContextKey: "user",
|
ContextKey: "user",
|
||||||
TokenLookup: "header:" + echo.HeaderAuthorization,
|
TokenLookup: "header:" + echo.HeaderAuthorization,
|
||||||
AuthScheme: "Bearer",
|
TokenLookupFuncs: nil,
|
||||||
Claims: jwt.MapClaims{},
|
AuthScheme: "Bearer",
|
||||||
KeyFunc: nil,
|
Claims: jwt.MapClaims{},
|
||||||
|
KeyFunc: nil,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -163,7 +179,7 @@ func JWTWithConfig(config JWTConfig) echo.MiddlewareFunc {
|
||||||
if config.Claims == nil {
|
if config.Claims == nil {
|
||||||
config.Claims = DefaultJWTConfig.Claims
|
config.Claims = DefaultJWTConfig.Claims
|
||||||
}
|
}
|
||||||
if config.TokenLookup == "" {
|
if config.TokenLookup == "" && len(config.TokenLookupFuncs) == 0 {
|
||||||
config.TokenLookup = DefaultJWTConfig.TokenLookup
|
config.TokenLookup = DefaultJWTConfig.TokenLookup
|
||||||
}
|
}
|
||||||
if config.AuthScheme == "" {
|
if config.AuthScheme == "" {
|
||||||
|
|
@ -176,25 +192,12 @@ func JWTWithConfig(config JWTConfig) echo.MiddlewareFunc {
|
||||||
config.ParseTokenFunc = config.defaultParseToken
|
config.ParseTokenFunc = config.defaultParseToken
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize
|
extractors, err := createExtractors(config.TokenLookup, config.AuthScheme)
|
||||||
// Split sources
|
if err != nil {
|
||||||
sources := strings.Split(config.TokenLookup, ",")
|
panic(err)
|
||||||
var extractors []jwtExtractor
|
}
|
||||||
for _, source := range sources {
|
if len(config.TokenLookupFuncs) > 0 {
|
||||||
parts := strings.Split(source, ":")
|
extractors = append(config.TokenLookupFuncs, extractors...)
|
||||||
|
|
||||||
switch parts[0] {
|
|
||||||
case "query":
|
|
||||||
extractors = append(extractors, jwtFromQuery(parts[1]))
|
|
||||||
case "param":
|
|
||||||
extractors = append(extractors, jwtFromParam(parts[1]))
|
|
||||||
case "cookie":
|
|
||||||
extractors = append(extractors, jwtFromCookie(parts[1]))
|
|
||||||
case "form":
|
|
||||||
extractors = append(extractors, jwtFromForm(parts[1]))
|
|
||||||
case "header":
|
|
||||||
extractors = append(extractors, jwtFromHeader(parts[1], config.AuthScheme))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||||
|
|
@ -206,48 +209,54 @@ func JWTWithConfig(config JWTConfig) echo.MiddlewareFunc {
|
||||||
if config.BeforeFunc != nil {
|
if config.BeforeFunc != nil {
|
||||||
config.BeforeFunc(c)
|
config.BeforeFunc(c)
|
||||||
}
|
}
|
||||||
var auth string
|
|
||||||
var err error
|
var lastExtractorErr error
|
||||||
|
var lastTokenErr error
|
||||||
for _, extractor := range extractors {
|
for _, extractor := range extractors {
|
||||||
// Extract token from extractor, if it's not fail break the loop and
|
auths, err := extractor(c)
|
||||||
// set auth
|
if err != nil {
|
||||||
auth, err = extractor(c)
|
lastExtractorErr = ErrJWTMissing // backwards compatibility: all extraction errors are same (unlike KeyAuth)
|
||||||
if err == nil {
|
continue
|
||||||
break
|
}
|
||||||
|
for _, auth := range auths {
|
||||||
|
token, err := config.ParseTokenFunc(auth, c)
|
||||||
|
if err != nil {
|
||||||
|
lastTokenErr = err
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Store user information from token into context.
|
||||||
|
c.Set(config.ContextKey, token)
|
||||||
|
if config.SuccessHandler != nil {
|
||||||
|
config.SuccessHandler(c)
|
||||||
|
}
|
||||||
|
return next(c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// If none of extractor has a token, handle error
|
// we are here only when we did not successfully extract or parse any of the tokens
|
||||||
if err != nil {
|
err := lastTokenErr
|
||||||
if config.ErrorHandler != nil {
|
if err == nil { // prioritize token errors over extracting errors
|
||||||
return config.ErrorHandler(err)
|
err = lastExtractorErr
|
||||||
}
|
|
||||||
|
|
||||||
if config.ErrorHandlerWithContext != nil {
|
|
||||||
return config.ErrorHandlerWithContext(err, c)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
token, err := config.ParseTokenFunc(auth, c)
|
|
||||||
if err == nil {
|
|
||||||
// Store user information from token into context.
|
|
||||||
c.Set(config.ContextKey, token)
|
|
||||||
if config.SuccessHandler != nil {
|
|
||||||
config.SuccessHandler(c)
|
|
||||||
}
|
|
||||||
return next(c)
|
|
||||||
}
|
}
|
||||||
if config.ErrorHandler != nil {
|
if config.ErrorHandler != nil {
|
||||||
return config.ErrorHandler(err)
|
return config.ErrorHandler(err)
|
||||||
}
|
}
|
||||||
if config.ErrorHandlerWithContext != nil {
|
if config.ErrorHandlerWithContext != nil {
|
||||||
return config.ErrorHandlerWithContext(err, c)
|
tmpErr := config.ErrorHandlerWithContext(err, c)
|
||||||
|
if config.ContinueOnIgnoredError && tmpErr == nil {
|
||||||
|
return next(c)
|
||||||
|
}
|
||||||
|
return tmpErr
|
||||||
}
|
}
|
||||||
return &echo.HTTPError{
|
|
||||||
Code: ErrJWTInvalid.Code,
|
// backwards compatible errors codes
|
||||||
Message: ErrJWTInvalid.Message,
|
if lastTokenErr != nil {
|
||||||
Internal: err,
|
return &echo.HTTPError{
|
||||||
|
Code: ErrJWTInvalid.Code,
|
||||||
|
Message: ErrJWTInvalid.Message,
|
||||||
|
Internal: err,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return err // this is lastExtractorErr value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -289,59 +298,3 @@ func (config *JWTConfig) defaultKeyFunc(t *jwt.Token) (interface{}, error) {
|
||||||
|
|
||||||
return config.SigningKey, nil
|
return config.SigningKey, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// jwtFromHeader returns a `jwtExtractor` that extracts token from the request header.
|
|
||||||
func jwtFromHeader(header string, authScheme string) jwtExtractor {
|
|
||||||
return func(c echo.Context) (string, error) {
|
|
||||||
auth := c.Request().Header.Get(header)
|
|
||||||
l := len(authScheme)
|
|
||||||
if len(auth) > l+1 && strings.EqualFold(auth[:l], authScheme) {
|
|
||||||
return auth[l+1:], nil
|
|
||||||
}
|
|
||||||
return "", ErrJWTMissing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// jwtFromQuery returns a `jwtExtractor` that extracts token from the query string.
|
|
||||||
func jwtFromQuery(param string) jwtExtractor {
|
|
||||||
return func(c echo.Context) (string, error) {
|
|
||||||
token := c.QueryParam(param)
|
|
||||||
if token == "" {
|
|
||||||
return "", ErrJWTMissing
|
|
||||||
}
|
|
||||||
return token, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// jwtFromParam returns a `jwtExtractor` that extracts token from the url param string.
|
|
||||||
func jwtFromParam(param string) jwtExtractor {
|
|
||||||
return func(c echo.Context) (string, error) {
|
|
||||||
token := c.Param(param)
|
|
||||||
if token == "" {
|
|
||||||
return "", ErrJWTMissing
|
|
||||||
}
|
|
||||||
return token, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// jwtFromCookie returns a `jwtExtractor` that extracts token from the named cookie.
|
|
||||||
func jwtFromCookie(name string) jwtExtractor {
|
|
||||||
return func(c echo.Context) (string, error) {
|
|
||||||
cookie, err := c.Cookie(name)
|
|
||||||
if err != nil {
|
|
||||||
return "", ErrJWTMissing
|
|
||||||
}
|
|
||||||
return cookie.Value, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// jwtFromForm returns a `jwtExtractor` that extracts token from the form field.
|
|
||||||
func jwtFromForm(name string) jwtExtractor {
|
|
||||||
return func(c echo.Context) (string, error) {
|
|
||||||
field := c.FormValue(name)
|
|
||||||
if field == "" {
|
|
||||||
return "", ErrJWTMissing
|
|
||||||
}
|
|
||||||
return field, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
173
vendor/github.com/labstack/echo/v4/middleware/key_auth.go
generated
vendored
173
vendor/github.com/labstack/echo/v4/middleware/key_auth.go
generated
vendored
|
|
@ -2,11 +2,8 @@ package middleware
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
|
|
@ -15,15 +12,21 @@ type (
|
||||||
// Skipper defines a function to skip middleware.
|
// Skipper defines a function to skip middleware.
|
||||||
Skipper Skipper
|
Skipper Skipper
|
||||||
|
|
||||||
// KeyLookup is a string in the form of "<source>:<name>" that is used
|
// KeyLookup is a string in the form of "<source>:<name>" or "<source>:<name>,<source>:<name>" that is used
|
||||||
// to extract key from the request.
|
// to extract key from the request.
|
||||||
// Optional. Default value "header:Authorization".
|
// Optional. Default value "header:Authorization".
|
||||||
// Possible values:
|
// Possible values:
|
||||||
// - "header:<name>"
|
// - "header:<name>" or "header:<name>:<cut-prefix>"
|
||||||
|
// `<cut-prefix>` is argument value to cut/trim prefix of the extracted value. This is useful if header
|
||||||
|
// value has static prefix like `Authorization: <auth-scheme> <authorisation-parameters>` where part that we
|
||||||
|
// want to cut is `<auth-scheme> ` note the space at the end.
|
||||||
|
// In case of basic authentication `Authorization: Basic <credentials>` prefix we want to remove is `Basic `.
|
||||||
// - "query:<name>"
|
// - "query:<name>"
|
||||||
// - "form:<name>"
|
// - "form:<name>"
|
||||||
// - "cookie:<name>"
|
// - "cookie:<name>"
|
||||||
KeyLookup string `yaml:"key_lookup"`
|
// Multiple sources example:
|
||||||
|
// - "header:Authorization,header:X-Api-Key"
|
||||||
|
KeyLookup string
|
||||||
|
|
||||||
// AuthScheme to be used in the Authorization header.
|
// AuthScheme to be used in the Authorization header.
|
||||||
// Optional. Default value "Bearer".
|
// Optional. Default value "Bearer".
|
||||||
|
|
@ -36,15 +39,20 @@ type (
|
||||||
// ErrorHandler defines a function which is executed for an invalid key.
|
// ErrorHandler defines a function which is executed for an invalid key.
|
||||||
// It may be used to define a custom error.
|
// It may be used to define a custom error.
|
||||||
ErrorHandler KeyAuthErrorHandler
|
ErrorHandler KeyAuthErrorHandler
|
||||||
|
|
||||||
|
// ContinueOnIgnoredError allows the next middleware/handler to be called when ErrorHandler decides to
|
||||||
|
// ignore the error (by returning `nil`).
|
||||||
|
// This is useful when parts of your site/api allow public access and some authorized routes provide extra functionality.
|
||||||
|
// In that case you can use ErrorHandler to set a default public key auth value in the request context
|
||||||
|
// and continue. Some logic down the remaining execution chain needs to check that (public) key auth value then.
|
||||||
|
ContinueOnIgnoredError bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// KeyAuthValidator defines a function to validate KeyAuth credentials.
|
// KeyAuthValidator defines a function to validate KeyAuth credentials.
|
||||||
KeyAuthValidator func(string, echo.Context) (bool, error)
|
KeyAuthValidator func(auth string, c echo.Context) (bool, error)
|
||||||
|
|
||||||
keyExtractor func(echo.Context) (string, error)
|
|
||||||
|
|
||||||
// KeyAuthErrorHandler defines a function which is executed for an invalid key.
|
// KeyAuthErrorHandler defines a function which is executed for an invalid key.
|
||||||
KeyAuthErrorHandler func(error, echo.Context) error
|
KeyAuthErrorHandler func(err error, c echo.Context) error
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
@ -56,6 +64,21 @@ var (
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ErrKeyAuthMissing is error type when KeyAuth middleware is unable to extract value from lookups
|
||||||
|
type ErrKeyAuthMissing struct {
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error returns errors text
|
||||||
|
func (e *ErrKeyAuthMissing) Error() string {
|
||||||
|
return e.Err.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unwrap unwraps error
|
||||||
|
func (e *ErrKeyAuthMissing) Unwrap() error {
|
||||||
|
return e.Err
|
||||||
|
}
|
||||||
|
|
||||||
// KeyAuth returns an KeyAuth middleware.
|
// KeyAuth returns an KeyAuth middleware.
|
||||||
//
|
//
|
||||||
// For valid key it calls the next handler.
|
// For valid key it calls the next handler.
|
||||||
|
|
@ -85,16 +108,9 @@ func KeyAuthWithConfig(config KeyAuthConfig) echo.MiddlewareFunc {
|
||||||
panic("echo: key-auth middleware requires a validator function")
|
panic("echo: key-auth middleware requires a validator function")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize
|
extractors, err := createExtractors(config.KeyLookup, config.AuthScheme)
|
||||||
parts := strings.Split(config.KeyLookup, ":")
|
if err != nil {
|
||||||
extractor := keyFromHeader(parts[1], config.AuthScheme)
|
panic(err)
|
||||||
switch parts[0] {
|
|
||||||
case "query":
|
|
||||||
extractor = keyFromQuery(parts[1])
|
|
||||||
case "form":
|
|
||||||
extractor = keyFromForm(parts[1])
|
|
||||||
case "cookie":
|
|
||||||
extractor = keyFromCookie(parts[1])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||||
|
|
@ -103,79 +119,62 @@ func KeyAuthWithConfig(config KeyAuthConfig) echo.MiddlewareFunc {
|
||||||
return next(c)
|
return next(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract and verify key
|
var lastExtractorErr error
|
||||||
key, err := extractor(c)
|
var lastValidatorErr error
|
||||||
if err != nil {
|
for _, extractor := range extractors {
|
||||||
if config.ErrorHandler != nil {
|
keys, err := extractor(c)
|
||||||
return config.ErrorHandler(err, c)
|
if err != nil {
|
||||||
|
lastExtractorErr = err
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, key := range keys {
|
||||||
|
valid, err := config.Validator(key, c)
|
||||||
|
if err != nil {
|
||||||
|
lastValidatorErr = err
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if valid {
|
||||||
|
return next(c)
|
||||||
|
}
|
||||||
|
lastValidatorErr = errors.New("invalid key")
|
||||||
}
|
}
|
||||||
return echo.NewHTTPError(http.StatusBadRequest, err.Error())
|
|
||||||
}
|
}
|
||||||
valid, err := config.Validator(key, c)
|
|
||||||
if err != nil {
|
// we are here only when we did not successfully extract and validate any of keys
|
||||||
if config.ErrorHandler != nil {
|
err := lastValidatorErr
|
||||||
return config.ErrorHandler(err, c)
|
if err == nil { // prioritize validator errors over extracting errors
|
||||||
|
// ugly part to preserve backwards compatible errors. someone could rely on them
|
||||||
|
if lastExtractorErr == errQueryExtractorValueMissing {
|
||||||
|
err = errors.New("missing key in the query string")
|
||||||
|
} else if lastExtractorErr == errCookieExtractorValueMissing {
|
||||||
|
err = errors.New("missing key in cookies")
|
||||||
|
} else if lastExtractorErr == errFormExtractorValueMissing {
|
||||||
|
err = errors.New("missing key in the form")
|
||||||
|
} else if lastExtractorErr == errHeaderExtractorValueMissing {
|
||||||
|
err = errors.New("missing key in request header")
|
||||||
|
} else if lastExtractorErr == errHeaderExtractorValueInvalid {
|
||||||
|
err = errors.New("invalid key in the request header")
|
||||||
|
} else {
|
||||||
|
err = lastExtractorErr
|
||||||
}
|
}
|
||||||
|
err = &ErrKeyAuthMissing{Err: err}
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.ErrorHandler != nil {
|
||||||
|
tmpErr := config.ErrorHandler(err, c)
|
||||||
|
if config.ContinueOnIgnoredError && tmpErr == nil {
|
||||||
|
return next(c)
|
||||||
|
}
|
||||||
|
return tmpErr
|
||||||
|
}
|
||||||
|
if lastValidatorErr != nil { // prioritize validator errors over extracting errors
|
||||||
return &echo.HTTPError{
|
return &echo.HTTPError{
|
||||||
Code: http.StatusUnauthorized,
|
Code: http.StatusUnauthorized,
|
||||||
Message: "invalid key",
|
Message: "Unauthorized",
|
||||||
Internal: err,
|
Internal: lastValidatorErr,
|
||||||
}
|
}
|
||||||
} else if valid {
|
|
||||||
return next(c)
|
|
||||||
}
|
}
|
||||||
return echo.ErrUnauthorized
|
return echo.NewHTTPError(http.StatusBadRequest, err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// keyFromHeader returns a `keyExtractor` that extracts key from the request header.
|
|
||||||
func keyFromHeader(header string, authScheme string) keyExtractor {
|
|
||||||
return func(c echo.Context) (string, error) {
|
|
||||||
auth := c.Request().Header.Get(header)
|
|
||||||
if auth == "" {
|
|
||||||
return "", errors.New("missing key in request header")
|
|
||||||
}
|
|
||||||
if header == echo.HeaderAuthorization {
|
|
||||||
l := len(authScheme)
|
|
||||||
if len(auth) > l+1 && auth[:l] == authScheme {
|
|
||||||
return auth[l+1:], nil
|
|
||||||
}
|
|
||||||
return "", errors.New("invalid key in the request header")
|
|
||||||
}
|
|
||||||
return auth, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// keyFromQuery returns a `keyExtractor` that extracts key from the query string.
|
|
||||||
func keyFromQuery(param string) keyExtractor {
|
|
||||||
return func(c echo.Context) (string, error) {
|
|
||||||
key := c.QueryParam(param)
|
|
||||||
if key == "" {
|
|
||||||
return "", errors.New("missing key in the query string")
|
|
||||||
}
|
|
||||||
return key, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// keyFromForm returns a `keyExtractor` that extracts key from the form.
|
|
||||||
func keyFromForm(param string) keyExtractor {
|
|
||||||
return func(c echo.Context) (string, error) {
|
|
||||||
key := c.FormValue(param)
|
|
||||||
if key == "" {
|
|
||||||
return "", errors.New("missing key in the form")
|
|
||||||
}
|
|
||||||
return key, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// keyFromCookie returns a `keyExtractor` that extracts key from the form.
|
|
||||||
func keyFromCookie(cookieName string) keyExtractor {
|
|
||||||
return func(c echo.Context) (string, error) {
|
|
||||||
key, err := c.Cookie(cookieName)
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("missing key in cookies: %w", err)
|
|
||||||
}
|
|
||||||
return key.Value, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
4
vendor/github.com/labstack/echo/v4/middleware/middleware.go
generated
vendored
4
vendor/github.com/labstack/echo/v4/middleware/middleware.go
generated
vendored
|
|
@ -12,10 +12,10 @@ import (
|
||||||
type (
|
type (
|
||||||
// Skipper defines a function to skip middleware. Returning true skips processing
|
// Skipper defines a function to skip middleware. Returning true skips processing
|
||||||
// the middleware.
|
// the middleware.
|
||||||
Skipper func(echo.Context) bool
|
Skipper func(c echo.Context) bool
|
||||||
|
|
||||||
// BeforeFunc defines a function which is executed just before the middleware.
|
// BeforeFunc defines a function which is executed just before the middleware.
|
||||||
BeforeFunc func(echo.Context)
|
BeforeFunc func(c echo.Context)
|
||||||
)
|
)
|
||||||
|
|
||||||
func captureTokens(pattern *regexp.Regexp, input string) *strings.Replacer {
|
func captureTokens(pattern *regexp.Regexp, input string) *strings.Replacer {
|
||||||
|
|
|
||||||
13
vendor/github.com/labstack/echo/v4/middleware/rate_limiter.go
generated
vendored
13
vendor/github.com/labstack/echo/v4/middleware/rate_limiter.go
generated
vendored
|
|
@ -153,9 +153,10 @@ func RateLimiterWithConfig(config RateLimiterConfig) echo.MiddlewareFunc {
|
||||||
type (
|
type (
|
||||||
// RateLimiterMemoryStore is the built-in store implementation for RateLimiter
|
// RateLimiterMemoryStore is the built-in store implementation for RateLimiter
|
||||||
RateLimiterMemoryStore struct {
|
RateLimiterMemoryStore struct {
|
||||||
visitors map[string]*Visitor
|
visitors map[string]*Visitor
|
||||||
mutex sync.Mutex
|
mutex sync.Mutex
|
||||||
rate rate.Limit
|
rate rate.Limit //for more info check out Limiter docs - https://pkg.go.dev/golang.org/x/time/rate#Limit.
|
||||||
|
|
||||||
burst int
|
burst int
|
||||||
expiresIn time.Duration
|
expiresIn time.Duration
|
||||||
lastCleanup time.Time
|
lastCleanup time.Time
|
||||||
|
|
@ -170,6 +171,8 @@ type (
|
||||||
/*
|
/*
|
||||||
NewRateLimiterMemoryStore returns an instance of RateLimiterMemoryStore with
|
NewRateLimiterMemoryStore returns an instance of RateLimiterMemoryStore with
|
||||||
the provided rate (as req/s). The provided rate less than 1 will be treated as zero.
|
the provided rate (as req/s). The provided rate less than 1 will be treated as zero.
|
||||||
|
for more info check out Limiter docs - https://pkg.go.dev/golang.org/x/time/rate#Limit.
|
||||||
|
|
||||||
Burst and ExpiresIn will be set to default values.
|
Burst and ExpiresIn will be set to default values.
|
||||||
|
|
||||||
Example (with 20 requests/sec):
|
Example (with 20 requests/sec):
|
||||||
|
|
@ -199,7 +202,7 @@ Characteristics:
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
limiterStore := middleware.NewRateLimiterMemoryStoreWithConfig(
|
limiterStore := middleware.NewRateLimiterMemoryStoreWithConfig(
|
||||||
middleware.RateLimiterMemoryStoreConfig{Rate: 50, Burst: 200, ExpiresIn: 5 * time.Minutes},
|
middleware.RateLimiterMemoryStoreConfig{Rate: 50, Burst: 200, ExpiresIn: 5 * time.Minute},
|
||||||
)
|
)
|
||||||
*/
|
*/
|
||||||
func NewRateLimiterMemoryStoreWithConfig(config RateLimiterMemoryStoreConfig) (store *RateLimiterMemoryStore) {
|
func NewRateLimiterMemoryStoreWithConfig(config RateLimiterMemoryStoreConfig) (store *RateLimiterMemoryStore) {
|
||||||
|
|
@ -221,7 +224,7 @@ func NewRateLimiterMemoryStoreWithConfig(config RateLimiterMemoryStoreConfig) (s
|
||||||
|
|
||||||
// RateLimiterMemoryStoreConfig represents configuration for RateLimiterMemoryStore
|
// RateLimiterMemoryStoreConfig represents configuration for RateLimiterMemoryStore
|
||||||
type RateLimiterMemoryStoreConfig struct {
|
type RateLimiterMemoryStoreConfig struct {
|
||||||
Rate rate.Limit // Rate of requests allowed to pass as req/s
|
Rate rate.Limit // Rate of requests allowed to pass as req/s. For more info check out Limiter docs - https://pkg.go.dev/golang.org/x/time/rate#Limit.
|
||||||
Burst int // Burst additionally allows a number of requests to pass when rate limit is reached
|
Burst int // Burst additionally allows a number of requests to pass when rate limit is reached
|
||||||
ExpiresIn time.Duration // ExpiresIn is the duration after that a rate limiter is cleaned up
|
ExpiresIn time.Duration // ExpiresIn is the duration after that a rate limiter is cleaned up
|
||||||
}
|
}
|
||||||
|
|
|
||||||
25
vendor/github.com/labstack/echo/v4/middleware/recover.go
generated
vendored
25
vendor/github.com/labstack/echo/v4/middleware/recover.go
generated
vendored
|
|
@ -2,6 +2,7 @@ package middleware
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/http"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
|
|
@ -9,6 +10,9 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
|
// LogErrorFunc defines a function for custom logging in the middleware.
|
||||||
|
LogErrorFunc func(c echo.Context, err error, stack []byte) error
|
||||||
|
|
||||||
// RecoverConfig defines the config for Recover middleware.
|
// RecoverConfig defines the config for Recover middleware.
|
||||||
RecoverConfig struct {
|
RecoverConfig struct {
|
||||||
// Skipper defines a function to skip middleware.
|
// Skipper defines a function to skip middleware.
|
||||||
|
|
@ -30,6 +34,10 @@ type (
|
||||||
// LogLevel is log level to printing stack trace.
|
// LogLevel is log level to printing stack trace.
|
||||||
// Optional. Default value 0 (Print).
|
// Optional. Default value 0 (Print).
|
||||||
LogLevel log.Lvl
|
LogLevel log.Lvl
|
||||||
|
|
||||||
|
// LogErrorFunc defines a function for custom logging in the middleware.
|
||||||
|
// If it's set you don't need to provide LogLevel for config.
|
||||||
|
LogErrorFunc LogErrorFunc
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -41,6 +49,7 @@ var (
|
||||||
DisableStackAll: false,
|
DisableStackAll: false,
|
||||||
DisablePrintStack: false,
|
DisablePrintStack: false,
|
||||||
LogLevel: 0,
|
LogLevel: 0,
|
||||||
|
LogErrorFunc: nil,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -69,13 +78,25 @@ func RecoverWithConfig(config RecoverConfig) echo.MiddlewareFunc {
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
|
if r == http.ErrAbortHandler {
|
||||||
|
panic(r)
|
||||||
|
}
|
||||||
err, ok := r.(error)
|
err, ok := r.(error)
|
||||||
if !ok {
|
if !ok {
|
||||||
err = fmt.Errorf("%v", r)
|
err = fmt.Errorf("%v", r)
|
||||||
}
|
}
|
||||||
stack := make([]byte, config.StackSize)
|
var stack []byte
|
||||||
length := runtime.Stack(stack, !config.DisableStackAll)
|
var length int
|
||||||
|
|
||||||
if !config.DisablePrintStack {
|
if !config.DisablePrintStack {
|
||||||
|
stack = make([]byte, config.StackSize)
|
||||||
|
length = runtime.Stack(stack, !config.DisableStackAll)
|
||||||
|
stack = stack[:length]
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.LogErrorFunc != nil {
|
||||||
|
err = config.LogErrorFunc(c, err, stack)
|
||||||
|
} else if !config.DisablePrintStack {
|
||||||
msg := fmt.Sprintf("[PANIC RECOVER] %v %s\n", err, stack[:length])
|
msg := fmt.Sprintf("[PANIC RECOVER] %v %s\n", err, stack[:length])
|
||||||
switch config.LogLevel {
|
switch config.LogLevel {
|
||||||
case log.DEBUG:
|
case log.DEBUG:
|
||||||
|
|
|
||||||
15
vendor/github.com/labstack/echo/v4/middleware/request_id.go
generated
vendored
15
vendor/github.com/labstack/echo/v4/middleware/request_id.go
generated
vendored
|
|
@ -17,14 +17,18 @@ type (
|
||||||
|
|
||||||
// RequestIDHandler defines a function which is executed for a request id.
|
// RequestIDHandler defines a function which is executed for a request id.
|
||||||
RequestIDHandler func(echo.Context, string)
|
RequestIDHandler func(echo.Context, string)
|
||||||
|
|
||||||
|
// TargetHeader defines what header to look for to populate the id
|
||||||
|
TargetHeader string
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// DefaultRequestIDConfig is the default RequestID middleware config.
|
// DefaultRequestIDConfig is the default RequestID middleware config.
|
||||||
DefaultRequestIDConfig = RequestIDConfig{
|
DefaultRequestIDConfig = RequestIDConfig{
|
||||||
Skipper: DefaultSkipper,
|
Skipper: DefaultSkipper,
|
||||||
Generator: generator,
|
Generator: generator,
|
||||||
|
TargetHeader: echo.HeaderXRequestID,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -42,6 +46,9 @@ func RequestIDWithConfig(config RequestIDConfig) echo.MiddlewareFunc {
|
||||||
if config.Generator == nil {
|
if config.Generator == nil {
|
||||||
config.Generator = generator
|
config.Generator = generator
|
||||||
}
|
}
|
||||||
|
if config.TargetHeader == "" {
|
||||||
|
config.TargetHeader = echo.HeaderXRequestID
|
||||||
|
}
|
||||||
|
|
||||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||||
return func(c echo.Context) error {
|
return func(c echo.Context) error {
|
||||||
|
|
@ -51,11 +58,11 @@ func RequestIDWithConfig(config RequestIDConfig) echo.MiddlewareFunc {
|
||||||
|
|
||||||
req := c.Request()
|
req := c.Request()
|
||||||
res := c.Response()
|
res := c.Response()
|
||||||
rid := req.Header.Get(echo.HeaderXRequestID)
|
rid := req.Header.Get(config.TargetHeader)
|
||||||
if rid == "" {
|
if rid == "" {
|
||||||
rid = config.Generator()
|
rid = config.Generator()
|
||||||
}
|
}
|
||||||
res.Header().Set(echo.HeaderXRequestID, rid)
|
res.Header().Set(config.TargetHeader, rid)
|
||||||
if config.RequestIDHandler != nil {
|
if config.RequestIDHandler != nil {
|
||||||
config.RequestIDHandler(c, rid)
|
config.RequestIDHandler(c, rid)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
126
vendor/github.com/labstack/echo/v4/middleware/timeout.go
generated
vendored
126
vendor/github.com/labstack/echo/v4/middleware/timeout.go
generated
vendored
|
|
@ -2,10 +2,10 @@ package middleware
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"net/http"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
|
"net/http"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------------------------------------------
|
||||||
|
|
@ -55,29 +55,27 @@ import (
|
||||||
// })
|
// })
|
||||||
//
|
//
|
||||||
|
|
||||||
type (
|
// TimeoutConfig defines the config for Timeout middleware.
|
||||||
// TimeoutConfig defines the config for Timeout middleware.
|
type TimeoutConfig struct {
|
||||||
TimeoutConfig struct {
|
// Skipper defines a function to skip middleware.
|
||||||
// Skipper defines a function to skip middleware.
|
Skipper Skipper
|
||||||
Skipper Skipper
|
|
||||||
|
|
||||||
// ErrorMessage is written to response on timeout in addition to http.StatusServiceUnavailable (503) status code
|
// ErrorMessage is written to response on timeout in addition to http.StatusServiceUnavailable (503) status code
|
||||||
// It can be used to define a custom timeout error message
|
// It can be used to define a custom timeout error message
|
||||||
ErrorMessage string
|
ErrorMessage string
|
||||||
|
|
||||||
// OnTimeoutRouteErrorHandler is an error handler that is executed for error that was returned from wrapped route after
|
// OnTimeoutRouteErrorHandler is an error handler that is executed for error that was returned from wrapped route after
|
||||||
// request timeouted and we already had sent the error code (503) and message response to the client.
|
// request timeouted and we already had sent the error code (503) and message response to the client.
|
||||||
// NB: do not write headers/body inside this handler. The response has already been sent to the client and response writer
|
// NB: do not write headers/body inside this handler. The response has already been sent to the client and response writer
|
||||||
// will not accept anything no more. If you want to know what actual route middleware timeouted use `c.Path()`
|
// will not accept anything no more. If you want to know what actual route middleware timeouted use `c.Path()`
|
||||||
OnTimeoutRouteErrorHandler func(err error, c echo.Context)
|
OnTimeoutRouteErrorHandler func(err error, c echo.Context)
|
||||||
|
|
||||||
// Timeout configures a timeout for the middleware, defaults to 0 for no timeout
|
// Timeout configures a timeout for the middleware, defaults to 0 for no timeout
|
||||||
// NOTE: when difference between timeout duration and handler execution time is almost the same (in range of 100microseconds)
|
// NOTE: when difference between timeout duration and handler execution time is almost the same (in range of 100microseconds)
|
||||||
// the result of timeout does not seem to be reliable - could respond timeout, could respond handler output
|
// the result of timeout does not seem to be reliable - could respond timeout, could respond handler output
|
||||||
// difference over 500microseconds (0.5millisecond) response seems to be reliable
|
// difference over 500microseconds (0.5millisecond) response seems to be reliable
|
||||||
Timeout time.Duration
|
Timeout time.Duration
|
||||||
}
|
}
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// DefaultTimeoutConfig is the default Timeout middleware config.
|
// DefaultTimeoutConfig is the default Timeout middleware config.
|
||||||
|
|
@ -94,10 +92,17 @@ func Timeout() echo.MiddlewareFunc {
|
||||||
return TimeoutWithConfig(DefaultTimeoutConfig)
|
return TimeoutWithConfig(DefaultTimeoutConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TimeoutWithConfig returns a Timeout middleware with config.
|
// TimeoutWithConfig returns a Timeout middleware with config or panics on invalid configuration.
|
||||||
// See: `Timeout()`.
|
|
||||||
func TimeoutWithConfig(config TimeoutConfig) echo.MiddlewareFunc {
|
func TimeoutWithConfig(config TimeoutConfig) echo.MiddlewareFunc {
|
||||||
// Defaults
|
mw, err := config.ToMiddleware()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return mw
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToMiddleware converts Config to middleware or returns an error for invalid configuration
|
||||||
|
func (config TimeoutConfig) ToMiddleware() (echo.MiddlewareFunc, error) {
|
||||||
if config.Skipper == nil {
|
if config.Skipper == nil {
|
||||||
config.Skipper = DefaultTimeoutConfig.Skipper
|
config.Skipper = DefaultTimeoutConfig.Skipper
|
||||||
}
|
}
|
||||||
|
|
@ -108,26 +113,29 @@ func TimeoutWithConfig(config TimeoutConfig) echo.MiddlewareFunc {
|
||||||
return next(c)
|
return next(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
errChan := make(chan error, 1)
|
||||||
handlerWrapper := echoHandlerFuncWrapper{
|
handlerWrapper := echoHandlerFuncWrapper{
|
||||||
|
writer: &ignorableWriter{ResponseWriter: c.Response().Writer},
|
||||||
ctx: c,
|
ctx: c,
|
||||||
handler: next,
|
handler: next,
|
||||||
errChan: make(chan error, 1),
|
errChan: errChan,
|
||||||
errHandler: config.OnTimeoutRouteErrorHandler,
|
errHandler: config.OnTimeoutRouteErrorHandler,
|
||||||
}
|
}
|
||||||
handler := http.TimeoutHandler(handlerWrapper, config.Timeout, config.ErrorMessage)
|
handler := http.TimeoutHandler(handlerWrapper, config.Timeout, config.ErrorMessage)
|
||||||
handler.ServeHTTP(c.Response().Writer, c.Request())
|
handler.ServeHTTP(handlerWrapper.writer, c.Request())
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case err := <-handlerWrapper.errChan:
|
case err := <-errChan:
|
||||||
return err
|
return err
|
||||||
default:
|
default:
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type echoHandlerFuncWrapper struct {
|
type echoHandlerFuncWrapper struct {
|
||||||
|
writer *ignorableWriter
|
||||||
ctx echo.Context
|
ctx echo.Context
|
||||||
handler echo.HandlerFunc
|
handler echo.HandlerFunc
|
||||||
errHandler func(err error, c echo.Context)
|
errHandler func(err error, c echo.Context)
|
||||||
|
|
@ -160,23 +168,53 @@ func (t echoHandlerFuncWrapper) ServeHTTP(rw http.ResponseWriter, r *http.Reques
|
||||||
}
|
}
|
||||||
return // on timeout we can not send handler error to client because `http.TimeoutHandler` has already sent headers
|
return // on timeout we can not send handler error to client because `http.TimeoutHandler` has already sent headers
|
||||||
}
|
}
|
||||||
// we restore original writer only for cases we did not timeout. On timeout we have already sent response to client
|
|
||||||
// and should not anymore send additional headers/data
|
|
||||||
// so on timeout writer stays what http.TimeoutHandler uses and prevents writing headers/body
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Error must be written into Writer created in `http.TimeoutHandler` so to get Response into `commited` state.
|
// This is needed as `http.TimeoutHandler` will write status code by itself on error and after that our tries to write
|
||||||
// So call global error handler to write error to the client. This is needed or `http.TimeoutHandler` will send
|
// status code will not work anymore as Echo.Response thinks it has been already "committed" and further writes
|
||||||
// status code by itself and after that our tries to write status code will not work anymore and/or create errors in
|
// create errors in log about `superfluous response.WriteHeader call from`
|
||||||
// log about `superfluous response.WriteHeader call from`
|
t.writer.Ignore(true)
|
||||||
t.ctx.Error(err)
|
t.ctx.Response().Writer = originalWriter // make sure we restore writer before we signal original coroutine about the error
|
||||||
// we pass error from handler to middlewares up in handler chain to act on it if needed. But this means that
|
// we pass error from handler to middlewares up in handler chain to act on it if needed.
|
||||||
// global error handler is probably be called twice as `t.ctx.Error` already does that.
|
|
||||||
|
|
||||||
// NB: later call of the global error handler or middlewares will not take any effect, as echo.Response will be
|
|
||||||
// already marked as `committed` because we called global error handler above.
|
|
||||||
t.ctx.Response().Writer = originalWriter // make sure we restore before we signal original coroutine about the error
|
|
||||||
t.errChan <- err
|
t.errChan <- err
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// we restore original writer only for cases we did not timeout. On timeout we have already sent response to client
|
||||||
|
// and should not anymore send additional headers/data
|
||||||
|
// so on timeout writer stays what http.TimeoutHandler uses and prevents writing headers/body
|
||||||
t.ctx.Response().Writer = originalWriter
|
t.ctx.Response().Writer = originalWriter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ignorableWriter is ResponseWriter implementations that allows us to mark writer to ignore further write calls. This
|
||||||
|
// is handy in cases when you do not have direct control of code being executed (3rd party middleware) but want to make
|
||||||
|
// sure that external code will not be able to write response to the client.
|
||||||
|
// Writer is coroutine safe for writes.
|
||||||
|
type ignorableWriter struct {
|
||||||
|
http.ResponseWriter
|
||||||
|
|
||||||
|
lock sync.Mutex
|
||||||
|
ignoreWrites bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *ignorableWriter) Ignore(ignore bool) {
|
||||||
|
w.lock.Lock()
|
||||||
|
w.ignoreWrites = ignore
|
||||||
|
w.lock.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *ignorableWriter) WriteHeader(code int) {
|
||||||
|
w.lock.Lock()
|
||||||
|
defer w.lock.Unlock()
|
||||||
|
if w.ignoreWrites {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.ResponseWriter.WriteHeader(code)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *ignorableWriter) Write(b []byte) (int, error) {
|
||||||
|
w.lock.Lock()
|
||||||
|
defer w.lock.Unlock()
|
||||||
|
if w.ignoreWrites {
|
||||||
|
return len(b), nil
|
||||||
|
}
|
||||||
|
return w.ResponseWriter.Write(b)
|
||||||
|
}
|
||||||
|
|
|
||||||
98
vendor/github.com/labstack/echo/v4/router.go
generated
vendored
98
vendor/github.com/labstack/echo/v4/router.go
generated
vendored
|
|
@ -1,6 +1,7 @@
|
||||||
package echo
|
package echo
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -31,17 +32,18 @@ type (
|
||||||
kind uint8
|
kind uint8
|
||||||
children []*node
|
children []*node
|
||||||
methodHandler struct {
|
methodHandler struct {
|
||||||
connect HandlerFunc
|
connect HandlerFunc
|
||||||
delete HandlerFunc
|
delete HandlerFunc
|
||||||
get HandlerFunc
|
get HandlerFunc
|
||||||
head HandlerFunc
|
head HandlerFunc
|
||||||
options HandlerFunc
|
options HandlerFunc
|
||||||
patch HandlerFunc
|
patch HandlerFunc
|
||||||
post HandlerFunc
|
post HandlerFunc
|
||||||
propfind HandlerFunc
|
propfind HandlerFunc
|
||||||
put HandlerFunc
|
put HandlerFunc
|
||||||
trace HandlerFunc
|
trace HandlerFunc
|
||||||
report HandlerFunc
|
report HandlerFunc
|
||||||
|
allowHeader string
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -68,6 +70,51 @@ func (m *methodHandler) isHandler() bool {
|
||||||
m.report != nil
|
m.report != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *methodHandler) updateAllowHeader() {
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
buf.WriteString(http.MethodOptions)
|
||||||
|
|
||||||
|
if m.connect != nil {
|
||||||
|
buf.WriteString(", ")
|
||||||
|
buf.WriteString(http.MethodConnect)
|
||||||
|
}
|
||||||
|
if m.delete != nil {
|
||||||
|
buf.WriteString(", ")
|
||||||
|
buf.WriteString(http.MethodDelete)
|
||||||
|
}
|
||||||
|
if m.get != nil {
|
||||||
|
buf.WriteString(", ")
|
||||||
|
buf.WriteString(http.MethodGet)
|
||||||
|
}
|
||||||
|
if m.head != nil {
|
||||||
|
buf.WriteString(", ")
|
||||||
|
buf.WriteString(http.MethodHead)
|
||||||
|
}
|
||||||
|
if m.patch != nil {
|
||||||
|
buf.WriteString(", ")
|
||||||
|
buf.WriteString(http.MethodPatch)
|
||||||
|
}
|
||||||
|
if m.post != nil {
|
||||||
|
buf.WriteString(", ")
|
||||||
|
buf.WriteString(http.MethodPost)
|
||||||
|
}
|
||||||
|
if m.propfind != nil {
|
||||||
|
buf.WriteString(", PROPFIND")
|
||||||
|
}
|
||||||
|
if m.put != nil {
|
||||||
|
buf.WriteString(", ")
|
||||||
|
buf.WriteString(http.MethodPut)
|
||||||
|
}
|
||||||
|
if m.trace != nil {
|
||||||
|
buf.WriteString(", ")
|
||||||
|
buf.WriteString(http.MethodTrace)
|
||||||
|
}
|
||||||
|
if m.report != nil {
|
||||||
|
buf.WriteString(", REPORT")
|
||||||
|
}
|
||||||
|
m.allowHeader = buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
// NewRouter returns a new Router instance.
|
// NewRouter returns a new Router instance.
|
||||||
func NewRouter(e *Echo) *Router {
|
func NewRouter(e *Echo) *Router {
|
||||||
return &Router{
|
return &Router{
|
||||||
|
|
@ -99,6 +146,9 @@ func (r *Router) Add(method, path string, h HandlerFunc) {
|
||||||
for i, lcpIndex := 0, len(path); i < lcpIndex; i++ {
|
for i, lcpIndex := 0, len(path); i < lcpIndex; i++ {
|
||||||
if path[i] == ':' {
|
if path[i] == ':' {
|
||||||
if i > 0 && path[i-1] == '\\' {
|
if i > 0 && path[i-1] == '\\' {
|
||||||
|
path = path[:i-1] + path[i:]
|
||||||
|
i--
|
||||||
|
lcpIndex--
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
j := i + 1
|
j := i + 1
|
||||||
|
|
@ -323,6 +373,7 @@ func (n *node) addHandler(method string, h HandlerFunc) {
|
||||||
n.methodHandler.report = h
|
n.methodHandler.report = h
|
||||||
}
|
}
|
||||||
|
|
||||||
|
n.methodHandler.updateAllowHeader()
|
||||||
if h != nil {
|
if h != nil {
|
||||||
n.isHandler = true
|
n.isHandler = true
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -359,13 +410,14 @@ func (n *node) findHandler(method string) HandlerFunc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *node) checkMethodNotAllowed() HandlerFunc {
|
func optionsMethodHandler(allowMethods string) func(c Context) error {
|
||||||
for _, m := range methods {
|
return func(c Context) error {
|
||||||
if h := n.findHandler(m); h != nil {
|
// Note: we are not handling most of the CORS headers here. CORS is handled by CORS middleware
|
||||||
return MethodNotAllowedHandler
|
// 'OPTIONS' method RFC: https://httpwg.org/specs/rfc7231.html#OPTIONS
|
||||||
}
|
// 'Allow' header RFC: https://datatracker.ietf.org/doc/html/rfc7231#section-7.4.1
|
||||||
|
c.Response().Header().Add(HeaderAllow, allowMethods)
|
||||||
|
return c.NoContent(http.StatusNoContent)
|
||||||
}
|
}
|
||||||
return NotFoundHandler
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find lookup a handler registered for method and path. It also parses URL for path
|
// Find lookup a handler registered for method and path. It also parses URL for path
|
||||||
|
|
@ -560,10 +612,16 @@ func (r *Router) Find(method, path string, c Context) {
|
||||||
// use previous match as basis. although we have no matching handler we have path match.
|
// use previous match as basis. although we have no matching handler we have path match.
|
||||||
// so we can send http.StatusMethodNotAllowed (405) instead of http.StatusNotFound (404)
|
// so we can send http.StatusMethodNotAllowed (405) instead of http.StatusNotFound (404)
|
||||||
currentNode = previousBestMatchNode
|
currentNode = previousBestMatchNode
|
||||||
ctx.handler = currentNode.checkMethodNotAllowed()
|
|
||||||
|
ctx.handler = NotFoundHandler
|
||||||
|
if currentNode.isHandler {
|
||||||
|
ctx.Set(ContextKeyHeaderAllow, currentNode.methodHandler.allowHeader)
|
||||||
|
ctx.handler = MethodNotAllowedHandler
|
||||||
|
if method == http.MethodOptions {
|
||||||
|
ctx.handler = optionsMethodHandler(currentNode.methodHandler.allowHeader)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ctx.path = currentNode.ppath
|
ctx.path = currentNode.ppath
|
||||||
ctx.pnames = currentNode.pnames
|
ctx.pnames = currentNode.pnames
|
||||||
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
15
vendor/github.com/mattn/go-colorable/.travis.yml
generated
vendored
15
vendor/github.com/mattn/go-colorable/.travis.yml
generated
vendored
|
|
@ -1,15 +0,0 @@
|
||||||
language: go
|
|
||||||
sudo: false
|
|
||||||
go:
|
|
||||||
- 1.13.x
|
|
||||||
- tip
|
|
||||||
|
|
||||||
before_install:
|
|
||||||
- go get -t -v ./...
|
|
||||||
|
|
||||||
script:
|
|
||||||
- ./go.test.sh
|
|
||||||
|
|
||||||
after_success:
|
|
||||||
- bash <(curl -s https://codecov.io/bash)
|
|
||||||
|
|
||||||
2
vendor/github.com/mattn/go-colorable/README.md
generated
vendored
2
vendor/github.com/mattn/go-colorable/README.md
generated
vendored
|
|
@ -1,6 +1,6 @@
|
||||||
# go-colorable
|
# go-colorable
|
||||||
|
|
||||||
[](https://travis-ci.org/mattn/go-colorable)
|
[](https://github.com/mattn/go-colorable/actions?query=workflow%3Atest)
|
||||||
[](https://codecov.io/gh/mattn/go-colorable)
|
[](https://codecov.io/gh/mattn/go-colorable)
|
||||||
[](http://godoc.org/github.com/mattn/go-colorable)
|
[](http://godoc.org/github.com/mattn/go-colorable)
|
||||||
[](https://goreportcard.com/report/mattn/go-colorable)
|
[](https://goreportcard.com/report/mattn/go-colorable)
|
||||||
|
|
|
||||||
1
vendor/github.com/mattn/go-colorable/colorable_appengine.go
generated
vendored
1
vendor/github.com/mattn/go-colorable/colorable_appengine.go
generated
vendored
|
|
@ -1,3 +1,4 @@
|
||||||
|
//go:build appengine
|
||||||
// +build appengine
|
// +build appengine
|
||||||
|
|
||||||
package colorable
|
package colorable
|
||||||
|
|
|
||||||
4
vendor/github.com/mattn/go-colorable/colorable_others.go
generated
vendored
4
vendor/github.com/mattn/go-colorable/colorable_others.go
generated
vendored
|
|
@ -1,5 +1,5 @@
|
||||||
// +build !windows
|
//go:build !windows && !appengine
|
||||||
// +build !appengine
|
// +build !windows,!appengine
|
||||||
|
|
||||||
package colorable
|
package colorable
|
||||||
|
|
||||||
|
|
|
||||||
14
vendor/github.com/mattn/go-colorable/colorable_windows.go
generated
vendored
14
vendor/github.com/mattn/go-colorable/colorable_windows.go
generated
vendored
|
|
@ -1,5 +1,5 @@
|
||||||
// +build windows
|
//go:build windows && !appengine
|
||||||
// +build !appengine
|
// +build windows,!appengine
|
||||||
|
|
||||||
package colorable
|
package colorable
|
||||||
|
|
||||||
|
|
@ -452,18 +452,22 @@ func (w *Writer) Write(data []byte) (n int, err error) {
|
||||||
} else {
|
} else {
|
||||||
er = bytes.NewReader(data)
|
er = bytes.NewReader(data)
|
||||||
}
|
}
|
||||||
var bw [1]byte
|
var plaintext bytes.Buffer
|
||||||
loop:
|
loop:
|
||||||
for {
|
for {
|
||||||
c1, err := er.ReadByte()
|
c1, err := er.ReadByte()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
plaintext.WriteTo(w.out)
|
||||||
break loop
|
break loop
|
||||||
}
|
}
|
||||||
if c1 != 0x1b {
|
if c1 != 0x1b {
|
||||||
bw[0] = c1
|
plaintext.WriteByte(c1)
|
||||||
w.out.Write(bw[:])
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
_, err = plaintext.WriteTo(w.out)
|
||||||
|
if err != nil {
|
||||||
|
break loop
|
||||||
|
}
|
||||||
c2, err := er.ReadByte()
|
c2, err := er.ReadByte()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
break loop
|
break loop
|
||||||
|
|
|
||||||
4
vendor/github.com/mattn/go-colorable/go.mod
generated
vendored
4
vendor/github.com/mattn/go-colorable/go.mod
generated
vendored
|
|
@ -1,8 +1,8 @@
|
||||||
module github.com/mattn/go-colorable
|
module github.com/mattn/go-colorable
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/mattn/go-isatty v0.0.12
|
github.com/mattn/go-isatty v0.0.14
|
||||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae // indirect
|
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
go 1.13
|
go 1.13
|
||||||
|
|
|
||||||
10
vendor/github.com/mattn/go-colorable/go.sum
generated
vendored
10
vendor/github.com/mattn/go-colorable/go.sum
generated
vendored
|
|
@ -1,5 +1,5 @@
|
||||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8=
|
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6 h1:foEbQz/B0Oz6YIqu/69kfXPYeFQAuuMYFkjaqXzl5Wo=
|
||||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
|
|
||||||
10
vendor/github.com/mattn/go-colorable/noncolorable.go
generated
vendored
10
vendor/github.com/mattn/go-colorable/noncolorable.go
generated
vendored
|
|
@ -18,18 +18,22 @@ func NewNonColorable(w io.Writer) io.Writer {
|
||||||
// Write writes data on console
|
// Write writes data on console
|
||||||
func (w *NonColorable) Write(data []byte) (n int, err error) {
|
func (w *NonColorable) Write(data []byte) (n int, err error) {
|
||||||
er := bytes.NewReader(data)
|
er := bytes.NewReader(data)
|
||||||
var bw [1]byte
|
var plaintext bytes.Buffer
|
||||||
loop:
|
loop:
|
||||||
for {
|
for {
|
||||||
c1, err := er.ReadByte()
|
c1, err := er.ReadByte()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
plaintext.WriteTo(w.out)
|
||||||
break loop
|
break loop
|
||||||
}
|
}
|
||||||
if c1 != 0x1b {
|
if c1 != 0x1b {
|
||||||
bw[0] = c1
|
plaintext.WriteByte(c1)
|
||||||
w.out.Write(bw[:])
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
_, err = plaintext.WriteTo(w.out)
|
||||||
|
if err != nil {
|
||||||
|
break loop
|
||||||
|
}
|
||||||
c2, err := er.ReadByte()
|
c2, err := er.ReadByte()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
break loop
|
break loop
|
||||||
|
|
|
||||||
7
vendor/modules.txt
vendored
7
vendor/modules.txt
vendored
|
|
@ -320,11 +320,11 @@ github.com/kolo/xmlrpc
|
||||||
github.com/kr/pretty
|
github.com/kr/pretty
|
||||||
# github.com/kr/text v0.1.0
|
# github.com/kr/text v0.1.0
|
||||||
github.com/kr/text
|
github.com/kr/text
|
||||||
# github.com/labstack/echo/v4 v4.6.1
|
# github.com/labstack/echo/v4 v4.7.2
|
||||||
## explicit
|
## explicit
|
||||||
github.com/labstack/echo/v4
|
github.com/labstack/echo/v4
|
||||||
github.com/labstack/echo/v4/middleware
|
github.com/labstack/echo/v4/middleware
|
||||||
# github.com/labstack/gommon v0.3.0
|
# github.com/labstack/gommon v0.3.1
|
||||||
## explicit
|
## explicit
|
||||||
github.com/labstack/gommon/bytes
|
github.com/labstack/gommon/bytes
|
||||||
github.com/labstack/gommon/color
|
github.com/labstack/gommon/color
|
||||||
|
|
@ -334,7 +334,7 @@ github.com/labstack/gommon/random
|
||||||
github.com/mailru/easyjson/buffer
|
github.com/mailru/easyjson/buffer
|
||||||
github.com/mailru/easyjson/jlexer
|
github.com/mailru/easyjson/jlexer
|
||||||
github.com/mailru/easyjson/jwriter
|
github.com/mailru/easyjson/jwriter
|
||||||
# github.com/mattn/go-colorable v0.1.8
|
# github.com/mattn/go-colorable v0.1.11
|
||||||
github.com/mattn/go-colorable
|
github.com/mattn/go-colorable
|
||||||
# github.com/mattn/go-ieproxy v0.0.1
|
# github.com/mattn/go-ieproxy v0.0.1
|
||||||
github.com/mattn/go-ieproxy
|
github.com/mattn/go-ieproxy
|
||||||
|
|
@ -733,5 +733,4 @@ gopkg.in/ini.v1
|
||||||
# gopkg.in/yaml.v2 v2.4.0
|
# gopkg.in/yaml.v2 v2.4.0
|
||||||
gopkg.in/yaml.v2
|
gopkg.in/yaml.v2
|
||||||
# gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
|
# gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
|
||||||
## explicit
|
|
||||||
gopkg.in/yaml.v3
|
gopkg.in/yaml.v3
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue